Contents [0/28] |
Today [1/28] |
Exam [2/28] |
Midterm Practice: Music Example [3/28] |
Midterm Practice: Filters [4/28] |
Midterm Practice: What A Pain [5/28] |
Midterm Practice: Composition/Map [6/28] |
PSP: SEI [7/28] |
PSP: Motivation [8/28] |
PSP: Planning [9/28] |
PSP: Measurement [10/28] |
PSP: What is needed? [11/28] |
PSP: Introducing Personal Disciplines [12/28] |
PSP: The PSP [13/28] |
Project [14/28] |
Observer: Cyclic Dependencies [15/28] |
Observer: 1 [16/28] |
Observer: 2 [17/28] |
Observer: 3 [18/28] |
Observer: 4 [19/28] |
Observer: 5 [20/28] |
Observer: 6 [21/28] |
Observer: 7 [22/28] |
Observer: 8 [23/28] |
Observer: UI Driven [24/28] |
Animation: Overview [25/28] |
Animation: Model [26/28] |
Animation: Text output [27/28] |
Animation: Swing output [28/28] |
Today [1/28] |
Exam Review (practice questions 5, 6, 11, 12)
Recording your productivity.
Observer
Project (First project iteration due week after exam)
Exam [2/28] |
See here.
Midterm Practice: Music Example [3/28] |
For questions see the homework.
file:music/Music.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
package music; class Music { private static double pitch = 440; /** play note at the current pitch for the given duration in milliseconds (the initial pitch is A = 440 Hz) */ public static void play(int duration) { System.out.println("play for " + duration/1000.0 + ": " + pitch ); } /** rest for given duration */ public static void rest(int duration) { System.out.println("rest for " + duration/1000.0); } /** multiply the pitch frequency by the given factor (a factor less than one will lower the pitch) */ public static void scalePitch(double factor) { pitch *= factor; } /** reset the pitch to note A = 440 Hz */ public static void reset() { pitch = 440; } }
file:music/Event.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package music; interface Event { public void play(); } class Note implements Event { int d; double f; public Note(int duration, double factor) { this.d = duration; this.f = factor; } public void play() { Music.scalePitch(f); Music.play(d); Music.scalePitch(1.0/f); } } class Rest implements Event { int d; public Rest(int duration) { d = duration; } public void play() { Music.rest(d); } }
file:music/EventGroup.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package music; import java.util.List; import java.util.LinkedList; class EventGroup implements Event { List<Event> events = new LinkedList<Event>(); public void add(Event e) { events.add(e); } public void play() { for (Event e : events) { e.play(); } } }
file:music/Transpose.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package music; class Transpose implements Event { Event e; double f; public Transpose(Event e, double factor) { this.e = e; this.f = factor; } public void play() { Music.scalePitch(f); e.play(); Music.scalePitch(1.0/f); } }
file:music/Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
package music; class Main { public static void main(String[] args) { EventGroup eg1 = new EventGroup(); eg1.add(new Note(250, 2.0)); eg1.add(new Rest(250)); eg1.add(new Note(500, 1.0)); eg1.add(new Rest(500)); eg1.add(new Note(1000, 0.5)); EventGroup eg2 = new EventGroup(); eg2.add(eg1); eg1.add(new Rest(1000)); eg2.add(new Transpose(eg1, 2.0)); eg2.play(); } }
Midterm Practice: Filters [4/28] |
For questions see the homework.
file:functions/two/Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package functions.two; interface Stream { int next(); } class IntStream implements Stream { private int i = -1; public int next() { return ++i; } } interface Predicate{ boolean evaluate(int j); } class FilteredStream implements Stream{ private Stream it; private Predicate p; public FilteredStream(Stream it, Predicate p) { this.it = it; this.p =p; } public int next() { int i; do { i = it.next(); } while (!p.evaluate(i)); return i; } } class IsEven implements Predicate { public boolean evaluate(int j){ return (j%2)== 0; } } class Main { public static void main (String[] args) { IntStream I = new IntStream(); FilteredStream F = new FilteredStream(I, new IsEven()); System.out.println(F.next()); // prints 0 on the screen System.out.println(F.next()); // prints 2 on the screen System.out.println(F.next()); // prints 4 on the screen System.out.println(F.next()); // prints 6 on the screen System.out.println(F.next()); // prints 8 on the screen IntStream J = new IntStream(); J.next(); // move forward one item in J FilteredStream G = new FilteredStream(J, new IsEven()); System.out.println(G.next()); // prints 2 on the screen System.out.println(G.next()); // prints 4 on the screen System.out.println(G.next()); // prints 6 on the screen System.out.println(G.next()); // prints 8 on the screen IntStream K = new IntStream(); class Div3 implements Predicate { public boolean evaluate(int n) { return (n%3) == 0; } } FilteredStream H = new FilteredStream(K, new Div3()); System.out.println(H.next()); // prints 0 on the screen System.out.println(H.next()); // prints 3 on the screen System.out.println(H.next()); // prints 6 on the screen System.out.println(H.next()); // prints 9 on the screen } }
Midterm Practice: What A Pain [5/28] |
For questions see the homework.
file:functions/two/Main2.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package functions.two; class NotDivn implements Predicate { final private int n; NotDivn(int n) { this.n = n; } public boolean evaluate(int m) { return (m%n) != 0; } } class WhatAPain implements Stream{ private Stream it; public WhatAPain(Stream it) { this.it = it; } public int next() { final int n = it.next(); final Predicate d = new NotDivn(n); Stream newit = new FilteredStream(it, d); it = newit; return (n); } } class Main2 { public static void main(String[] args) { IntStream I = new IntStream(); System.out.println(I.next()); // prints out 0 on the screen System.out.println(I.next()); // prints out 1 on the screen WhatAPain w = new WhatAPain(I); System.out.println(w.next()); System.out.println(w.next()); System.out.println(w.next()); System.out.println(w.next()); System.out.println(w.next()); System.out.println(w.next()); System.out.println(w.next()); System.out.println(w.next()); System.out.println(w.next()); System.out.println(w.next()); } }
Links
Midterm Practice: Composition/Map [6/28] |
For questions see the homework.
file:functions/one/Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package functions.one; interface ArrFun { public int[] exec(int[] x); } interface IntFun { public int exec(int x); } class Abs implements IntFun { public int exec(int x) { //SAME AS: if (x < 0) return -x; else return x; return (x < 0) ? -x : x; } } class Cube implements IntFun { public int exec(int x) { return x*x*x; } } class Map implements ArrFun { IntFun f; public Map(IntFun f) { this.f = f; } public int[] exec(int[] x) { int[] answer = new int[x.length]; for (int i=0; i<x.length; i++) { answer[i] = f.exec(x[i]); } return answer; } } class Main { public static void print(int[] x) { System.out.print("[ "); for (int i=0; i<x.length; i++) System.out.print(x[i]+ " "); System.out.println("]"); } public static void main(String[] argv) { IntFun f = new Abs(); System.out.println(f.exec(-5)); int[] a = new int[10]; for (int i=0; i<a.length; i++) a[i] = -i*10; ArrFun mabs = new Map(new Comp(new Abs(), new Cube())); print(mabs.exec(a)); } } class Comp implements IntFun { IntFun f, g; public Comp(IntFun f, IntFun g) { this.f = f; this.g = g; } public int exec(int x) { return g.exec(f.exec(x)); } }
PSP: SEI [7/28] |
PSP is the Personal Software Process developed by Humphries at CMU's Software Engineering Institute: SEI.
Interest is in process. What makes a project successful? What make a project fail?
PSP concentrates on the individual programmer. The Team Software Process (TSP) concentrates on Teams.
Goal is to distill knowledge and transmit it to others. SEI has done a very good job with this.
SE472 is a PSP course in the style advocated by SEI. This class is not.
The rest of these notes are copied from a talk given by Watts Humphries at DePaul in 2003.
PSP: Motivation [8/28] |
Watts Humphrey: Our job is to transform poorly understood
and rapidly changing needs into precise machine instructions.
Experimentation is necessary to determine what we are supposed to build, hence iterative development.
But software production is also a business, and
businesses run on schedules and commitments
.
So we need to measure what we do so that we know how to make plans and commitments.
PSP: Planning [9/28] |
Quoting Watts Humphrey:
PSP: Measurement [10/28] |
Quoting Watts Humphrey:
PSP: What is needed? [11/28] |
Quoting Watts Humphrey:
PSP: Introducing Personal Disciplines [12/28] |
Quoting Watts Humphrey:
PSP: The PSP [13/28] |
We will only be doing a bit of measurement in this course.
I will also ask you to try to predict how much time you will spend. You can see how accurate your predictions are. With experience, you will get better at predicting.
Later stages of the PSP concentrate on:
Project [14/28] |
The project will be a traffic simulation. Detailed instructions are For details see here: file:notes-final-project.
Observer: Cyclic Dependencies [15/28] |
Example of a GUI element (a slider) which controls a program
variable x
.
Program creates Slider
.
Slider
controls program.
Observer reverse the dependency of Slider on program.
Program can use slider code, but slider decides when to call (back) to the client!
javax.swing.JSlider
javax.swing.event.ChangeListener
java.awt.Button
java.awt.event.ActionListener
Observer: 1 [16/28] |
file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package observer.one; public class Main { public static void main(String[] argv) { Int c = new Int(); Runnable r1 = new M(c); Runnable r2 = new N(c); for (int i=0; i<10000; i++) { r1.run(); r2.run(); } } } class Int { private int v; public int get() { return v; } public void inc() { v++; } public void dec() { v--; } } class M implements Runnable { private Int c; public M(Int c) { this.c = c; } public void run() { c.inc(); c.inc(); c.dec(); } } class N implements Runnable { private Int c; public N(Int c) { this.c = c; } public void run() { for (int i=0; i<50; i++) { if (i%3==0) { c.dec(); } else { c.inc(); } } } }
What if we have to print the number of operations performed on the Int?
Observer: 2 [17/28] |
file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package observer.two; public class Main { public static void main(String[] argv) { Int c = new Int(); Runnable r1 = new M(c); Runnable r2 = new N(c); for (int i=0; i<10000; i++) { r1.run(); r2.run(); } } } class Int { private int v; public int get() { return v; } public void inc() { v++; } public void dec() { v--; } } class M implements Runnable { private int numOps; private Int c; public M(Int c) { this.c = c; } public void run() { c.inc(); System.out.println (++numOps); c.inc(); System.out.println (++numOps); c.dec(); System.out.println (++numOps); } } class N implements Runnable { private Int c; public N(Int c) { this.c = c; } public void run() { for (int i=0; i<50; i++) { if (i%3==0) { c.dec(); /*??*/ } else { c.inc(); /*??*/ } } } }
Who remembers the numOps?
Observer: 3 [18/28] |
file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package observer.three; public class Main { public static void main(String[] argv) { Int c = new Int(); Runnable r1 = new M(c); Runnable r2 = new N(c); for (int i=0; i<10000; i++) { r1.run(); r2.run(); } } } class Int { private int v; private int numOps; public int get() { return v; } public void inc() { v++; System.out.println (++numOps); } public void dec() { v--; System.out.println (++numOps); } } class M implements Runnable { private Int c; public M(Int c) { this.c = c; } public void run() { c.inc(); c.inc(); c.dec(); } } class N implements Runnable { private Int c; public N(Int c) { this.c = c; } public void run() { for (int i=0; i<50; i++) { if (i%3==0) { c.dec(); } else { c.inc(); } } } }
Is this Int cohesive?
Let's use an observer!
Observer: 4 [19/28] |
file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package observer.four; public class Main { public static void main(String[] argv) { Int c = new Int(); IntObserver o = new IntObserver(c); Runnable r1 = new M(c); Runnable r2 = new N(c); for (int i=0; i<10000; i++) { r1.run(); r2.run(); } } } class IntObserver { private int numOps; public IntObserver (Int c) { /* ?? */ } public void update() { System.out.println (++numOps); } } class Int { private int v; public int get() { return v; } public void inc() { v++; } public void dec() { v--; } } class M implements Runnable { private Int c; public M(Int c) { this.c = c; } public void run() { c.inc(); c.inc(); c.dec(); } } class N implements Runnable { private Int c; public N(Int c) { this.c = c; } public void run() { for (int i=0; i<50; i++) { if (i%3==0) { c.dec(); } else { c.inc(); } } } }
How do we tell the observer what to observe?
Observer: 5 [20/28] |
file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package observer.five; public class Main { public static void main(String[] argv) { IntObserver o = new IntObserver(); Int c = new Int(o); Runnable r1 = new M(c); Runnable r2 = new N(c); for (int i=0; i<10000; i++) { r1.run(); r2.run(); } } } class IntObserver { private int numOps; public void update() { System.out.println (++numOps); } } class Int { private IntObserver o; public Int(IntObserver o) { this.o = o; } private int v; public int get() { return v; } public void inc() { v++; o.update(); } public void dec() { v--; o.update(); } } class M implements Runnable { private Int c; public M(Int c) { this.c = c; } public void run() { c.inc(); c.inc(); c.dec(); } } class N implements Runnable { private Int c; public N(Int c) { this.c = c; } public void run() { for (int i=0; i<50; i++) { if (i%3==0) { c.dec(); } else { c.inc(); } } } }
Observers are lazy! Subscriber is a better name.
Does Int need to know the actual class of the subscriber?
Could there be more than one subscriber?
Observer: 6 [21/28] |
file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package observer.six; import java.util.List; import java.util.ArrayList; public class Main { public static void main(String[] argv) { Int c = new Int(); c.addObserver(new IntObserver()); Runnable r1 = new M(c); Runnable r2 = new N(c); for (int i=0; i<10000; i++) { r1.run(); r2.run(); } } } interface Observer { public void update(); } class IntObserver implements Observer { private int numOps; public void update() { System.out.println (++numOps); } } class Int { private List<Observer> observers = new ArrayList<Observer>(); private boolean changed = false; public void addObserver(Observer o) { observers.add(o); } private void setChanged() { changed = true; } private void notifyObservers() { if (changed) { for (Observer o : observers) { o.update(); } changed = false; } } private int v; public int get() { return v; } public void inc() { v++; setChanged(); notifyObservers(); } public void dec() { v--; setChanged(); notifyObservers(); } } class M implements Runnable { private Int c; public M(Int c) { this.c = c; } public void run() { c.inc(); c.inc(); c.dec(); } } class N implements Runnable { private Int c; public N(Int c) { this.c = c; } public void run() { for (int i=0; i<50; i++) { if (i%3==0) { c.dec(); } else { c.inc(); } } } }
Observer: 7 [22/28] |
file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package observer.seven; import java.util.Observer; import java.util.Observable; public class Main { public static void main(String[] argv) { Int c = new Int(); c.addObserver(new IntObserver()); Runnable r1 = new M(c); Runnable r2 = new N(c); for (int i=0; i<10000; i++) { r1.run(); r2.run(); } } } class IntObserver implements Observer { private int numOps; public void update(Observable o, Object arg) { System.out.println (++numOps); } } class Int extends Observable { private int v; public int get() { return v; } public void inc() { v++; setChanged(); notifyObservers(); } public void dec() { v--; setChanged(); notifyObservers(); } } class M implements Runnable { private Int c; public M(Int c) { this.c = c; } public void run() { c.inc(); c.inc(); c.dec(); } } class N implements Runnable { private Int c; public N(Int c) { this.c = c; } public void run() { for (int i=0; i<50; i++) { if (i%3==0) { c.dec(); } else { c.inc(); } } } }
java.util.Observable
java.util.Observer
Is it worth giving IntObserver a name?
Observer: 8 [23/28] |
file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package observer.eight; import java.util.Observer; import java.util.Observable; public class Main { public static void main(String[] argv) { Int c = new Int(); c.addObserver(new Observer() { private int numOps; public void update(Observable o, Object arg) { System.out.println (++numOps); } }); Runnable r1 = new M(c); Runnable r2 = new N(c); for (int i=0; i<10000; i++) { r1.run(); r2.run(); } } } class Int extends Observable { private int v; public int get() { return v; } public void inc() { v++; setChanged(); notifyObservers(); } public void dec() { v--; setChanged(); notifyObservers(); } } class M implements Runnable { private Int c; public M(Int c) { this.c = c; } public void run() { c.inc(); c.inc(); c.dec(); } } class N implements Runnable { private Int c; public N(Int c) { this.c = c; } public void run() { for (int i=0; i<50; i++) { if (i%3==0) { c.dec(); } else { c.inc(); } } } }
Observer: UI Driven [24/28] |
file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package observer.slider; import javax.swing.JSlider; import javax.swing.SwingConstants; public class Main { public static void main(String[] args) { new M().run(); } } class M implements Runnable { static int MIN = 0; static int MAX = 100; static int INIT = 50; int x = INIT; public void run () { JSlider s = new JSlider(SwingConstants.VERTICAL, M.MIN, M.MAX, M.INIT); /* ... */ s.addChangeListener(e -> { JSlider source = (JSlider)e.getSource(); if (!source.getValueIsAdjusting()) { // the next two lines are redundant x = source.getValue(); M.this.x = source.getValue(); } }); } }
Animation: Overview [25/28] |
The code presents a rough framework for animation/visualization. I have written the code to handle geometry problems similar to those of the project. You should be able to use the animator package unchanged. You will need to make some (small) changes to the visualizer. Of course, you will need to replace the model and main packages entirely.
Note that this code does not solve the problem put forth by the project. This is just a bunch of lines with bouncing dots...
To get the code, download file:myproject.zip. Unzip it, and put it in eclipse. You can run
project.main.Main
file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package myproject.main; import myproject.model.Model; import myproject.model.swing.SwingAnimatorBuilder; import myproject.model.text.TextAnimatorBuilder; /** * A static class to demonstrate the visualization aspect of * simulation. */ public class Main { private Main() {} public static void main(String[] args) { { Model m = new Model(new TextAnimatorBuilder(), 0, 1); m.run(1000); m.dispose(); } { Model m = new Model(new SwingAnimatorBuilder(), 0, 1); m.run(1000); m.dispose(); } { Model m = new Model(new TextAnimatorBuilder(), 1, 1); m.run(10); m.dispose(); } { Model m = new Model(new SwingAnimatorBuilder(), 1, 1); m.run(10); m.dispose(); } { Model m = new Model(new TextAnimatorBuilder(), 2, 3); m.run(500); m.run(500); m.dispose(); } System.exit(0); } }
Animation: Model [26/28] |
Package documentation: myproject.model [private] myproject.util [private]
file:MP.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package myproject.model; /** * Static class for model parameters. */ public class MP { private MP() {} /** Length of cars, in meters */ public static double carLength = 10; /** Length of roads, in meters */ public static double roadLength = 200; /** Maximum car velocity, in meters/second */ public static double maxVelocity = 6; }
file:Model.java [source] [doc-public] [doc-private]
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package myproject.model; import java.util.List; import java.util.ArrayList; import java.util.Observable; import myproject.util.Animator; /** * An example to model for a simple visualization. * The model contains roads organized in a matrix. * See {@link #Model(AnimatorBuilder, int, int)}. */ public class Model extends Observable { private List<Agent> agents; private Animator animator; private boolean disposed; private double time; /** Creates a model to be visualized using the <code>builder</code>. * If the builder is null, no visualization is performed. * The number of <code>rows</code> and <code>columns</code> * indicate the number of {@link Light}s, organized as a 2D * matrix. These are separated and surrounded by horizontal and * vertical {@link Road}s. For example, calling the constructor with 1 * row and 2 columns generates a model of the form: * <pre> * | | * --@--@-- * | | * </pre> * where <code>@</code> is a {@link Light}, <code>|</code> is a * vertical {@link Road} and <code>--</code> is a horizontal {@link Road}. * Each road has one {@link Car}. * * <p> * The {@link AnimatorBuilder} is used to set up an {@link * Animator}. * {@link AnimatorBuilder#getAnimator()} is registered as * an observer of this model. * <p> */ public Model(AnimatorBuilder builder, int rows, int columns) { if (rows < 0 || columns < 0 || (rows == 0 && columns == 0)) { throw new IllegalArgumentException(); } if (builder == null) { builder = new NullAnimatorBuilder(); } this.agents = new ArrayList<Agent>(); setup(builder, rows, columns); this.animator = builder.getAnimator(); super.addObserver(animator); } /** * Run the simulation for <code>duration</code> model seconds. */ public void run(double duration) { if (disposed) throw new IllegalStateException(); for (int i=0; i<duration; i++) { time++; // iterate through a copy because agents may change during iteration... for (Agent a : agents.toArray(new Agent[0])) { a.run(time); } super.setChanged(); super.notifyObservers(); } } /** * Throw away this model. */ public void dispose() { animator.dispose(); disposed = true; } /** * Construct the model, establishing correspondences with the visualizer. */ private void setup(AnimatorBuilder builder, int rows, int columns) { List<Road> roads = new ArrayList<Road>(); Light[][] intersections = new Light[rows][columns]; // Add Lights for (int i=0; i<rows; i++) { for (int j=0; j<columns; j++) { intersections[i][j] = new Light(); builder.addLight(intersections[i][j], i, j); agents.add(intersections[i][j]); } } // Add Horizontal Roads boolean eastToWest = false; for (int i=0; i<rows; i++) { for (int j=0; j<=columns; j++) { Road l = new Road(); builder.addHorizontalRoad(l, i, j, eastToWest); roads.add(l); } eastToWest = !eastToWest; } // Add Vertical Roads boolean southToNorth = false; for (int j=0; j<columns; j++) { for (int i=0; i<=rows; i++) { Road l = new Road(); builder.addVerticalRoad(l, i, j, southToNorth); roads.add(l); } southToNorth = !southToNorth; } // Add Cars for (Road l : roads) { Car car = new Car(); agents.add(car); l.accept(car); } } }
file:Road.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
package myproject.model; import java.util.List; import java.util.ArrayList; /** * A road holds cars. */ public class Road { Road() { } // Created only by this package private List<Car> cars = new ArrayList<Car>(); public void accept(Car d) { if (d == null) { throw new IllegalArgumentException(); } cars.add(d); } public List<Car> getCars() { return cars; } }
file:Agent.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
package myproject.model; /** * Interface for active model objects. */ public interface Agent { public void run(double time); }
file:Car.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package myproject.model; /** * A car remembers its position from the beginning of its road. * Cars have random velocity and random movement pattern: * when reaching the end of a road, the dot either resets its position * to the beginning of the road, or reverses its direction. */ public class Car implements Agent { Car() { } // Created only by this package private boolean backAndForth = Math.round(Math.random())==1 ? true : false; private double position = (MP.roadLength-MP.carLength) * Math.random (); private double velocity = (int) Math.ceil(Math.random() * MP.maxVelocity); private java.awt.Color color = new java.awt.Color((int)Math.ceil(128 + Math.random()*127),(int)Math.ceil(128 + Math.random()*127),(int)Math.ceil(Math.random()*255)); public double getPosition() { return position; } public java.awt.Color getColor() { return color; } public void run(double time) { if (backAndForth) { if (((position + velocity) < 0) || ((position + velocity) > (MP.roadLength-MP.carLength))) velocity *= -1; } else { if ((position + velocity) > (MP.roadLength-MP.carLength)) position = 0; } position += velocity; } }
file:Light.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
package myproject.model; /** * A light has a boolean state. */ public class Light implements Agent { Light() { } // Created only by this package private boolean state; public boolean getState() { return state; } public void run(double time) { if (time%40==0) { state = !state; } } }
file:Animator.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
package myproject.util; import java.util.Observer; /** * An interface for displaying simulations. */ public interface Animator extends Observer { public void dispose(); }
file:AnimatorBuilder.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package myproject.model; import myproject.util.Animator; /** * An interface for building a {@link Animator} for this {@link Model}. */ public interface AnimatorBuilder { /** * Returns the {@link Animator}. * This method may be called only once; subsequent calls throw an * {@link IllegalStateException}. */ public Animator getAnimator(); /** * Add the {@link Light} to the display at position <code>i,j</code>. */ public void addLight(Light d, int i, int j); /** * Add the horizontal {@link Road} to the display, west of position <code>i,j</code>. * If <code>eastToWest</code> is true, then road position 0 is the eastmost position. * If <code>eastToWest</code> is false, then road position 0 is the westmost position. */ public void addHorizontalRoad(Road l, int i, int j, boolean eastToWest); /** * Add the vertical {@link Road} to the display, north of position <code>i,j</code>. * If <code>southToNorth</code> is true, then road position 0 is the southmost position. * If <code>southToNorth</code> is false, then road position 0 is the northmost position. */ public void addVerticalRoad(Road l, int i, int j, boolean southToNorth); } /** * Null object for {@link AnimatorBuilder}. */ class NullAnimatorBuilder implements AnimatorBuilder { public Animator getAnimator() { return new NullAnimator(); } public void addLight(Light d, int i, int j) { } public void addHorizontalRoad(Road l, int i, int j, boolean eastToWest) { } public void addVerticalRoad(Road l, int i, int j, boolean southToNorth) { } } /** * Null object for {@link Animator}. */ class NullAnimator implements Animator { public void update(java.util.Observable o, Object arg) { } public void dispose() { } }
Animation: Text output [27/28] |
Package documentation: myproject.model.text [private] myproject.util [private]
file:TextAnimatorBuilder.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package myproject.model.text; import java.util.ArrayList; import java.util.List; import java.util.Observable; import myproject.model.AnimatorBuilder; import myproject.model.Car; import myproject.model.Light; import myproject.model.Road; import myproject.util.Animator; /** * A class for building Animators. */ public class TextAnimatorBuilder implements AnimatorBuilder { TextAnimator animator; public TextAnimatorBuilder() { this.animator = new TextAnimator(); } public Animator getAnimator() { if (animator == null) { throw new IllegalStateException(); } Animator returnValue = animator; animator = null; return returnValue; } public void addLight(Light d, int i, int j) { animator.addLight(d,i,j); } public void addHorizontalRoad(Road l, int i, int j, boolean eastToWest) { animator.addRoad(l,i,j); } public void addVerticalRoad(Road l, int i, int j, boolean southToNorth) { animator.addRoad(l,i,j); } /** Class for drawing the Model. */ private static class TextAnimator implements Animator { /** Triple of a model element <code>x</code> and coordinates. */ private static class Element<T> { T x; int i; int j; Element(T x, int i, int j) { this.x = x; this.i = i; this.j = j; } } private List<Element<Road>> roadElements; private List<Element<Light>> lightElements; TextAnimator() { roadElements = new ArrayList<Element<Road>>(); lightElements = new ArrayList<Element<Light>>(); } void addLight(Light x, int i, int j) { lightElements.add(new Element<Light>(x,i,j)); } void addRoad(Road x, int i, int j) { roadElements.add(new Element<Road>(x,i,j)); } public void dispose() { } public void update(Observable o, Object arg) { for (Element<Light> e : lightElements) { System.out.print("Light at (" + e.i + "," + e.j + "): "); if (e.x.getState()) { System.out.println("Blue"); } else { System.out.println("Yellow"); } } for (Element<Road> e : roadElements) { for (Car d : e.x.getCars()) { System.out.print("Road at (" + e.i + "," + e.j + "): "); System.out.println("Car at " + d.getPosition()); } } } } }
Animation: Swing output [28/28] |
Package documentation: myproject.model.swing [private] myproject.util [private]
file:Animator.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
package myproject.util; import java.util.Observer; /** * An interface for displaying simulations. */ public interface Animator extends Observer { public void dispose(); }
file:SwingAnimator.java [source] [doc-public] [doc-private]
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
package myproject.util; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.util.Observable; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.WindowConstants; /** * A swing implementation of {@link Animator}, using a {@link JFrame} * to display the animation. The {@link JFrame} is created and * displayed by the constructor. * * Calls to <code>update()</code> result in a call to * <code>painter.paint()</code>. This is executed in the swing * thread while the main thread is paused for <code>delay</code> * milliseconds. */ public class SwingAnimator implements Animator { // The following fields are manipulated by the main program thread private int delay; // The following fields are manipulated by the swing thread private JFrame frame; // Swing representation of an OS window private ContentPane content; // A paintable component private boolean disposed = false; // If true, then die /** * Creates and displays a {@link JFrame} for the animation. * @param name The name to be displayed on the graphical window. * @param width The width of the display, in pixels. * @param height The height of the display, in pixels. * @param delay Time to pause after an update, in milliseconds. */ public SwingAnimator(final SwingAnimatorPainter painter, final String name, final int width, final int height, int delay) { this.delay = delay; // Create a graphics window and display it SwingUtilities.invokeLater(() -> { content = new ContentPane(painter, width, height); // A paintable component for content frame = new JFrame(); // An OS window frame.setTitle(name); // The title of the Frame frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); // End program if Frame is closed frame.setContentPane(content); // Associate the content with the Frame frame.pack(); // Fix the layout of the Frame frame.setVisible(true); // Display the Frame }); } /** * Throw away this visualization. */ public void dispose() { SwingUtilities.invokeLater(() -> { frame.dispose(); disposed = true; }); } /** * Calls to <code>update</code> are executed in the swing thread, * while the main thread is paused for <code>delay</code> * milliseconds. */ public void update(final Observable model, Object ignored) { if (disposed) throw new IllegalStateException(); // Redraw the window // content.repaint() causes a call to content.paint(g) // where g is an appropriate graphics argument. SwingUtilities.invokeLater(() -> content.repaint()); // Delay the main thread try { Thread.sleep(delay); } catch (InterruptedException e) {} } /** * A component for painting. * All code is executed in the swing thread. */ private static class ContentPane extends JPanel { private static final long serialVersionUID = 2008L; private int width; private int height; private SwingAnimatorPainter painter; ContentPane(SwingAnimatorPainter painter, int width, int height) { this.painter = painter; this.width = width; this.height = height; setPreferredSize(new Dimension(width, height)); setDoubleBuffered(true); setOpaque(true); setBackground(Color.WHITE); } public void paint(Graphics g) { // This test is necessary because the swing thread may call this // method before the simulation calls SwingAnimator.update() if (painter != null ) { // The clearRect is necessary, since JPanel is lightweight g.clearRect(0, 0, width, height); painter.paint(g); } } } }
file:SwingAnimatorPainter.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
package myproject.util; import java.awt.Graphics; /** * Callback interface for {@link SwingAnimator}. * @see SwingAnimator */ public interface SwingAnimatorPainter { public void paint(Graphics arg); }
file:VP.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
package myproject.model.swing; import myproject.model.MP; /** * Static class for visualization parameters. */ class VP { private VP() {} /** Width of model elements, in meters */ static double elementWidth = MP.carLength; /** Gap between model elements, in meters */ static double gap = 1; /** Width of the displayed graphics window, in pixels */ static int displayWidth = 1000; /** Height of the displayed graphics window, in pixels */ static int displayHeight = 1000; /** Delay introduced into each graphics update, in milliseconds */ static int displayDelay = 50; /** Scalefactor relating model space to pixels, in pixels/meter */ static double scaleFactor = 1; }
file:SwingAnimatorBuilder.java [source] [doc-public] [doc-private]
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
package myproject.model.swing; import java.awt.Color; import java.awt.Graphics; import java.util.ArrayList; import java.util.List; import myproject.model.AnimatorBuilder; import myproject.model.Car; import myproject.model.Light; import myproject.model.MP; import myproject.model.Road; import myproject.util.Animator; import myproject.util.SwingAnimator; import myproject.util.SwingAnimatorPainter; /** * A class for building Animators. */ public class SwingAnimatorBuilder implements AnimatorBuilder { MyPainter painter; public SwingAnimatorBuilder() { painter = new MyPainter(); } public Animator getAnimator() { if (painter == null) { throw new IllegalStateException(); } Animator returnValue = new SwingAnimator(painter, "Traffic Simulator", VP.displayWidth, VP.displayHeight, VP.displayDelay); painter = null; return returnValue; } private static double skipInit = VP.gap; private static double skipRoad = VP.gap + MP.roadLength; private static double skipCar = VP.gap + VP.elementWidth; private static double skipRoadCar = skipRoad + skipCar; public void addLight(Light d, int i, int j) { double x = skipInit + skipRoad + j*skipRoadCar; double y = skipInit + skipRoad + i*skipRoadCar; Translator t = new TranslatorWE(x, y, MP.carLength, VP.elementWidth, VP.scaleFactor); painter.addLight(d,t); } public void addHorizontalRoad(Road l, int i, int j, boolean eastToWest) { double x = skipInit + j*skipRoadCar; double y = skipInit + skipRoad + i*skipRoadCar; Translator t = eastToWest ? new TranslatorEW(x, y, MP.roadLength, VP.elementWidth, VP.scaleFactor) : new TranslatorWE(x, y, MP.roadLength, VP.elementWidth, VP.scaleFactor); painter.addRoad(l,t); } public void addVerticalRoad(Road l, int i, int j, boolean southToNorth) { double x = skipInit + skipRoad + j*skipRoadCar; double y = skipInit + i*skipRoadCar; Translator t = southToNorth ? new TranslatorSN(x, y, MP.roadLength, VP.elementWidth, VP.scaleFactor) : new TranslatorNS(x, y, MP.roadLength, VP.elementWidth, VP.scaleFactor); painter.addRoad(l,t); } /** Class for drawing the Model. */ private static class MyPainter implements SwingAnimatorPainter { /** Pair of a model element <code>x</code> and a translator <code>t</code>. */ private static class Element<T> { T x; Translator t; Element(T x, Translator t) { this.x = x; this.t = t; } } private List<Element<Road>> roadElements; private List<Element<Light>> lightElements; MyPainter() { roadElements = new ArrayList<Element<Road>>(); lightElements = new ArrayList<Element<Light>>(); } void addLight(Light x, Translator t) { lightElements.add(new Element<Light>(x,t)); } void addRoad(Road x, Translator t) { roadElements.add(new Element<Road>(x,t)); } public void paint(Graphics g) { // This method is called by the swing thread, so may be called // at any time during execution... // First draw the background elements for (Element<Light> e : lightElements) { if (e.x.getState()) { g.setColor(Color.BLUE); } else { g.setColor(Color.YELLOW); } XGraphics.fillOval(g, e.t, 0, 0, MP.carLength, VP.elementWidth); } g.setColor(Color.BLACK); for (Element<Road> e : roadElements) { XGraphics.fillRect(g, e.t, 0, 0, MP.roadLength, VP.elementWidth); } // Then draw the foreground elements for (Element<Road> e : roadElements) { // iterate through a copy because e.x.getCars() may change during iteration... for (Car d : e.x.getCars().toArray(new Car[0])) { g.setColor(d.getColor()); XGraphics.fillOval(g, e.t, d.getPosition(), 0, MP.carLength, VP.elementWidth); } } } } }
file:XGraphics.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package myproject.model.swing; import java.awt.Graphics; /** * Static class for drawing using a Translation. */ class XGraphics { private XGraphics() {} static void clearRect(Graphics g, Translator t, double x, double y, double w, double h) { g.clearRect(t.getX(x,y,w,h), t.getY(x,y,w,h), t.getWidth(w,h), t.getHeight(w,h)); } static void drawOval(Graphics g, Translator t, double x, double y, double w, double h) { g.drawOval(t.getX(x,y,w,h), t.getY(x,y,w,h), t.getWidth(w,h), t.getHeight(w,h)); } static void drawRect(Graphics g, Translator t, double x, double y, double w, double h) { g.drawRect(t.getX(x,y,w,h), t.getY(x,y,w,h), t.getWidth(w,h), t.getHeight(w,h)); } static void fillOval(Graphics g, Translator t, double x, double y, double w, double h) { g.fillOval(t.getX(x,y,w,h), t.getY(x,y,w,h), t.getWidth(w,h), t.getHeight(w,h)); } static void fillRect(Graphics g, Translator t, double x, double y, double w, double h) { g.fillRect(t.getX(x,y,w,h), t.getY(x,y,w,h), t.getWidth(w,h), t.getHeight(w,h)); } static void drawString(Graphics g, Translator t, String str, double x, double y) { g.drawString(str, t.getX(x,y,0,0), t.getY(x,y,0,0)); } }
file:Translator.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package myproject.model.swing; /** * A translator from relative model space to screen graphics. */ abstract class Translator { double tX; double tY; double tWidth; double tHeight; double tScaleFactor; Translator(double tX, double tY, double tWidth, double tHeight, double tScaleFactor) { this.tX = tX; this.tY = tY; this.tWidth = tWidth; this.tHeight = tHeight; this.tScaleFactor = tScaleFactor; } int scale(double arg) { return (int) Math.ceil(arg * tScaleFactor); } abstract int getX(double x, double y, double width, double height); abstract int getY(double x, double y, double width, double height); abstract int getWidth(double width, double height); abstract int getHeight(double width, double height); } class TranslatorWE extends Translator { TranslatorWE(double tX, double tY, double tWidth, double tHeight, double tScaleFactor) { super(tX, tY, tWidth, tHeight, tScaleFactor); } int getX(double x, double y, double width, double height) { return scale(tX+x); } int getY(double x, double y, double width, double height) { return scale(tY+y); } int getWidth(double width, double height) { return scale(width); } int getHeight(double width, double height) { return scale(height); } } class TranslatorEW extends Translator { TranslatorEW(double tX, double tY, double tWidth, double tHeight, double tScaleFactor) { super(tX, tY, tWidth, tHeight, tScaleFactor); } int getX(double x, double y, double width, double height) { return scale(tX+tWidth-x-width); } int getY(double x, double y, double width, double height) { return scale(tY+tHeight-y-height); } int getWidth(double width, double height) { return scale(width); } int getHeight(double width, double height) { return scale(height); } } class TranslatorNS extends Translator { TranslatorNS(double tX, double tY, double tWidth, double tHeight, double tScaleFactor) { super(tX, tY, tWidth, tHeight, tScaleFactor); } int getX(double x, double y, double width, double height) { return scale(tX+y); } int getY(double x, double y, double width, double height) { return scale(tY+x); } int getWidth(double width, double height) { return scale(height); } int getHeight(double width, double height) { return scale(width); } } class TranslatorSN extends Translator { TranslatorSN(double tX, double tY, double tWidth, double tHeight, double tScaleFactor) { super(tX, tY, tWidth, tHeight, tScaleFactor); } int getX(double x, double y, double width, double height) { return scale(tX+tHeight-y-height); } int getY(double x, double y, double width, double height) { return scale(tY+tWidth-x-width); } int getWidth(double width, double height) { return scale(height); } int getHeight(double width, double height) { return scale(width); } }
Revised: 2008/02/19 17:28