SE450: 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); } }