1 package org.opentrafficsim.draw.gtu;
2
3 import java.awt.BasicStroke;
4 import java.awt.Color;
5 import java.awt.Graphics2D;
6 import java.awt.geom.Ellipse2D;
7 import java.awt.geom.Rectangle2D;
8 import java.awt.geom.RectangularShape;
9 import java.awt.image.ImageObserver;
10 import java.io.Serializable;
11 import java.rmi.RemoteException;
12
13 import javax.naming.NamingException;
14
15 import org.opentrafficsim.core.animation.gtu.colorer.DefaultSwitchableGtuColorer;
16 import org.opentrafficsim.core.animation.gtu.colorer.GtuColorer;
17 import org.opentrafficsim.core.animation.gtu.colorer.IdGtuColorer;
18 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
19 import org.opentrafficsim.core.geometry.DirectedPoint;
20 import org.opentrafficsim.draw.core.TextAlignment;
21 import org.opentrafficsim.draw.core.TextAnimation;
22 import org.opentrafficsim.road.gtu.lane.LaneBasedGtu;
23
24 import nl.tudelft.simulation.dsol.animation.Locatable;
25 import nl.tudelft.simulation.dsol.animation.D2.Renderable2D;
26 import nl.tudelft.simulation.dsol.animation.D2.Renderable2DInterface;
27 import nl.tudelft.simulation.language.d2.Angle;
28 import nl.tudelft.simulation.naming.context.Contextualized;
29
30
31
32
33
34
35
36
37
38
39 public class DefaultCarAnimation extends Renderable2D<LaneBasedGtu> implements Renderable2DInterface<LaneBasedGtu>, Serializable
40 {
41
42 private static final long serialVersionUID = 20150000L;
43
44
45 private GtuColorer gtuColorer = new DefaultSwitchableGtuColorer();
46
47
48 private Text text;
49
50
51 private boolean isDestroyed = false;
52
53
54 private final int hashCode;
55
56
57 private Rectangle2D.Double rectangle;
58
59
60 private Ellipse2D.Double frontIndicator;
61
62
63 private Rectangle2D.Double leftIndicator;
64
65
66 private Rectangle2D.Double rightIndicator;
67
68
69 private Rectangle2D.Double leftBrake;
70
71
72 private Rectangle2D.Double rightBrake;
73
74
75 private RectangularShape marker;
76
77
78
79
80
81
82
83
84 public DefaultCarAnimation(final LaneBasedGtu gtu, final OtsSimulatorInterface simulator)
85 throws NamingException, RemoteException
86 {
87 this(gtu, simulator, null);
88 }
89
90
91
92
93
94
95
96
97
98 public DefaultCarAnimation(final LaneBasedGtu gtu, final OtsSimulatorInterface simulator, final GtuColorer gtuColorer)
99 throws NamingException, RemoteException
100 {
101 super(gtu, simulator);
102 this.hashCode = gtu.hashCode();
103 if (null == gtuColorer)
104 {
105 this.gtuColorer = new IdGtuColorer();
106 }
107 else
108 {
109 this.gtuColorer = gtuColorer;
110 }
111 this.text = new Text(gtu, gtu.getId(), 0.0f, 0.0f, TextAlignment.CENTER, Color.BLACK, simulator,
112 new TextAnimation.ContrastToBackground()
113 {
114
115 @Override
116 public Color getBackgroundColor()
117 {
118 return gtuColorer.getColor(gtu);
119 }
120 });
121 }
122
123
124
125
126
127 public final void setGtuColorer(final GtuColorer newGtuColorer)
128 {
129 this.gtuColorer = newGtuColorer;
130 }
131
132
133 @Override
134 public final void paint(final Graphics2D graphics, final ImageObserver observer)
135 {
136 final LaneBasedGtu gtu = getSource();
137 if (this.rectangle == null)
138 {
139
140
141 final double length = gtu.getLength().si;
142 final double lFront = gtu.getFront().getDx().si;
143 final double lRear = gtu.getRear().getDx().si;
144 final double width = gtu.getWidth().si;
145 final double w2 = width / 2;
146 final double w4 = width / 4;
147 this.rectangle = new Rectangle2D.Double(lRear, -w2, length, width);
148 this.frontIndicator = new Ellipse2D.Double(lFront - w2 - w4, -w4, w2, w2);
149 this.leftIndicator = new Rectangle2D.Double(lFront - w4, -w2, w4, w4);
150 this.rightIndicator = new Rectangle2D.Double(lFront - w4, w2 - w4, w4, w4);
151 this.leftBrake = new Rectangle2D.Double(lRear, w2 - w4, w4, w4);
152 this.rightBrake = new Rectangle2D.Double(lRear, -w2, w4, w4);
153 switch (gtu.getType().getMarker())
154 {
155 case CIRCLE:
156 this.marker = new Ellipse2D.Double(0, 0, 0, 0);
157 break;
158 case SQUARE:
159 this.marker = new Rectangle2D.Double(0, 0, 0, 0);
160 break;
161 default:
162 this.marker = new Ellipse2D.Double(0, 0, 0, 0);
163 }
164 }
165
166 double scale = graphics.getTransform().getDeterminant();
167
168
169 if (scale > 1)
170 {
171 Color color = this.gtuColorer.getColor(gtu);
172 graphics.setColor(color);
173 BasicStroke saveStroke = (BasicStroke) graphics.getStroke();
174 graphics.setStroke(new BasicStroke(0.05f));
175 graphics.fill(this.rectangle);
176
177 graphics.setColor(Color.WHITE);
178 graphics.fill(this.frontIndicator);
179
180 if (color.equals(Color.WHITE))
181 {
182
183 graphics.setColor(Color.BLACK);
184 graphics.draw(this.frontIndicator);
185 }
186
187
188 graphics.setColor(Color.YELLOW);
189 if (gtu.getTurnIndicatorStatus() != null && gtu.getTurnIndicatorStatus().isLeftOrBoth())
190 {
191 graphics.fill(this.leftIndicator);
192 if (color.equals(Color.YELLOW))
193 {
194 graphics.setColor(Color.BLACK);
195 graphics.draw(this.leftIndicator);
196 }
197 }
198 if (gtu.getTurnIndicatorStatus() != null && gtu.getTurnIndicatorStatus().isRightOrBoth())
199 {
200 graphics.fill(this.rightIndicator);
201 if (color.equals(Color.YELLOW))
202 {
203 graphics.setColor(Color.BLACK);
204 graphics.draw(this.rightIndicator);
205 }
206 }
207
208
209 if (gtu.isBrakingLightsOn())
210 {
211 graphics.setColor(Color.RED);
212 graphics.fill(this.leftBrake);
213 graphics.fill(this.rightBrake);
214 if (color.equals(Color.RED))
215 {
216 graphics.setColor(Color.BLACK);
217 graphics.draw(this.leftBrake);
218 graphics.draw(this.rightBrake);
219 }
220 }
221 graphics.setStroke(saveStroke);
222 }
223 else
224 {
225
226 graphics.setColor(this.gtuColorer.getColor(gtu));
227 double w = 7.0 / Math.sqrt(scale);
228 double x = -w / 2.0;
229 this.marker.setFrame(x, x, w, w);
230 graphics.fill(this.marker);
231 }
232 }
233
234
235 @Override
236 public void destroy(final Contextualized contextProvider)
237 {
238 super.destroy(contextProvider);
239 this.text.destroy(contextProvider);
240 this.isDestroyed = true;
241 }
242
243
244 @Override
245 public final String toString()
246 {
247 return super.toString();
248 }
249
250
251 @Override
252 public int hashCode()
253 {
254 return this.hashCode;
255 }
256
257
258 @Override
259 public boolean equals(final Object object)
260 {
261
262 return super.equals(object);
263 }
264
265
266
267
268
269
270
271
272
273
274
275
276 public class Text extends TextAnimation
277 {
278
279 private static final long serialVersionUID = 20161211L;
280
281
282 private boolean isTextDestroyed = false;
283
284
285
286
287
288
289
290
291
292
293
294
295 public Text(final Locatable source, final String text, final float dx, final float dy,
296 final TextAlignment textAlignment, final Color color, final OtsSimulatorInterface simulator)
297 throws RemoteException, NamingException
298 {
299 super(source, text, dx, dy, textAlignment, color, 1.0f, 12.0f, 50f, simulator, TextAnimation.RENDERWHEN1);
300 }
301
302
303
304
305
306
307
308
309
310
311
312
313
314 @SuppressWarnings("parameternumber")
315 public Text(final Locatable source, final String text, final float dx, final float dy,
316 final TextAlignment textAlignment, final Color color, final OtsSimulatorInterface simulator,
317 final TextAnimation.ContrastToBackground background) throws RemoteException, NamingException
318 {
319 super(source, text, dx, dy, textAlignment, color, 1.0f, 12.0f, 50f, simulator, background, RENDERWHEN1);
320 }
321
322
323 @Override
324 public final void paint(final Graphics2D graphics, final ImageObserver observer)
325 {
326 final LaneBasedGtu car = (LaneBasedGtu) getSource();
327
328 if (car.isDestroyed())
329 {
330 if (!this.isTextDestroyed)
331 {
332 try
333 {
334 destroy(car.getSimulator());
335 }
336 catch (Exception e)
337 {
338 System.err.println("Error while destroying text animation of GTU " + car.getId());
339 }
340 this.isTextDestroyed = true;
341 }
342 return;
343 }
344
345 super.paint(graphics, observer);
346 }
347
348
349 @Override
350 @SuppressWarnings("checkstyle:designforextension")
351 public DirectedPoint getLocation()
352 {
353
354 DirectedPoint p = ((LaneBasedGtu) getSource()).getLocation();
355 double a = Angle.normalizePi(p.getRotZ());
356 if (a > Math.PI / 2.0 || a < -0.99 * Math.PI / 2.0)
357 {
358 a += Math.PI;
359 }
360 return new DirectedPoint(p.x, p.y, Double.MAX_VALUE, 0.0, 0.0, a);
361 }
362
363
364 @Override
365 public final String toString()
366 {
367 return "Text [isTextDestroyed=" + this.isTextDestroyed + "]";
368 }
369
370 }
371
372 }