1 package org.opentrafficsim.core.animation;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.Font;
6 import java.awt.FontMetrics;
7 import java.awt.Graphics2D;
8 import java.awt.geom.Point2D;
9 import java.awt.geom.Rectangle2D;
10 import java.awt.image.ImageObserver;
11 import java.io.Serializable;
12 import java.rmi.RemoteException;
13
14 import javax.media.j3d.Bounds;
15 import javax.naming.NamingException;
16
17 import org.opentrafficsim.core.logger.SimLogger;
18
19 import nl.tudelft.simulation.dsol.animation.Locatable;
20 import nl.tudelft.simulation.dsol.animation.D2.Renderable2D;
21 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
22 import nl.tudelft.simulation.language.d3.BoundingBox;
23 import nl.tudelft.simulation.language.d3.DirectedPoint;
24
25 /**
26 * Display a text for another Locatable object.
27 * <p>
28 * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
29 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
30 * </p>
31 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
32 * initial version Dec 11, 2016 <br>
33 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
34 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
35 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
36 */
37 public abstract class TextAnimation implements Locatable, Serializable
38 {
39 /** */
40 private static final long serialVersionUID = 20161211L;
41
42 /** The object for which the text is displayed. */
43 private final Locatable source;
44
45 /** The text to display. */
46 private String text;
47
48 /** The horizontal movement of the text, in meters. */
49 private final float dx;
50
51 /** The vertical movement of the text, in meters. */
52 private final float dy;
53
54 /** Whether to center or not. */
55 private final TextAlignment textAlignment;
56
57 /** The color of the text. */
58 private Color color;
59
60 /** FontSize the size of the font; default = 2.0 (meters). */
61 private final float fontSize;
62
63 /** The animation implementation. */
64 private final AnimationImpl animationImpl;
65
66 /** The font. */
67 private Font font;
68
69 /** The font rectangle. */
70 private Rectangle2D fontRectangle = null;
71
72 /**
73 * @param source Locatable; the object for which the text is displayed
74 * @param text String; the text to display
75 * @param dx float; the horizontal movement of the text, in meters
76 * @param dy float; the vertical movement of the text, in meters
77 * @param textAlignment TextAlignment; where to place the text
78 * @param color Color; the color of the text
79 * @param fontSize float; the size of the font; default = 2.0 (meters)
80 * @param simulator SimulatorInterface.TimeDoubleUnit; the simulator
81 * @throws NamingException when animation context cannot be created or retrieved
82 * @throws RemoteException when remote context cannot be found
83 */
84 @SuppressWarnings("checkstyle:parameternumber")
85 public TextAnimation(final Locatable source, final String text, final float dx, final float dy,
86 final TextAlignment textAlignment, final Color color, final float fontSize,
87 final SimulatorInterface.TimeDoubleUnit simulator) throws RemoteException, NamingException
88 {
89 this.source = source;
90 this.text = text;
91 this.dx = dx;
92 this.dy = dy;
93 this.textAlignment = textAlignment;
94 this.color = color;
95 this.fontSize = fontSize;
96
97 this.font = new Font("SansSerif", Font.PLAIN, 2);
98 if (this.fontSize != 2.0f)
99 {
100 this.font = this.font.deriveFont(this.fontSize);
101 }
102
103 this.animationImpl = new AnimationImpl(this, simulator);
104 }
105
106 /**
107 * @param source Locatable; the object for which the text is displayed
108 * @param text String; the text to display
109 * @param dx float; the horizontal movement of the text, in meters
110 * @param dy float; the vertical movement of the text, in meters
111 * @param textAlignment TextAlignment; where to place the text
112 * @param color Color; the color of the text
113 * @param simulator SimulatorInterface.TimeDoubleUnit; the simulator
114 * @throws NamingException when animation context cannot be created or retrieved
115 * @throws RemoteException when remote context cannot be found
116 */
117 public TextAnimation(final Locatable source, final String text, final float dx, final float dy,
118 final TextAlignment textAlignment, final Color color, final SimulatorInterface.TimeDoubleUnit simulator)
119 throws RemoteException, NamingException
120 {
121 this(source, text, dx, dy, textAlignment, color, 2.0f, simulator);
122 }
123
124 /** {@inheritDoc} */
125 @Override
126 @SuppressWarnings("checkstyle:designforextension")
127 public DirectedPoint getLocation() throws RemoteException
128 {
129 // draw always on top.
130 DirectedPoint p = this.source.getLocation();
131 return new DirectedPoint(p.x, p.y, Double.MAX_VALUE, 0.0, 0.0, p.getRotZ());
132 }
133
134 /** {@inheritDoc} */
135 @Override
136 public final Bounds getBounds() throws RemoteException
137 {
138 return new BoundingBox(0.0, 0.0, 0.0);
139 }
140
141 /**
142 * paint() method so it can be overridden or extended.
143 * @param graphics Graphics2D; the graphics object
144 * @param observer ImageObserver; the observer
145 * @throws RemoteException on network exception
146 */
147 @SuppressWarnings("checkstyle:designforextension")
148 public void paint(final Graphics2D graphics, final ImageObserver observer) throws RemoteException
149 {
150 graphics.setFont(this.font);
151 synchronized (this.font)
152 {
153 if (this.fontRectangle == null)
154 {
155 FontMetrics fm = graphics.getFontMetrics();
156 this.fontRectangle = fm.getStringBounds(this.text, graphics);
157 }
158 graphics.setColor(this.color);
159 float dxText =
160 this.textAlignment.equals(TextAlignment.LEFT) ? 0.0f : this.textAlignment.equals(TextAlignment.CENTER)
161 ? (float) -this.fontRectangle.getWidth() / 2.0f : (float) -this.fontRectangle.getWidth();
162 graphics.drawString(this.text, dxText + this.dx, this.fontSize / 2.0f - this.dy);
163 }
164 }
165
166 /**
167 * Destroy the text animation.
168 */
169 public final void destroy()
170 {
171 try
172 {
173 this.animationImpl.destroy();
174 }
175 catch (NamingException exception)
176 {
177 SimLogger.always().warn(exception, "Tried to destroy Text for GTU animation of GTU {}", this.source.toString());
178 }
179 }
180
181 /**
182 * Clone the TextAnimation and return a copy for the new source on the new simulator.
183 * @param newSource Locatable; the new source to link to the text animation
184 * @param newSimulator SimulatorInterface.TimeDoubleUnit; the new simulator to register the animation on
185 * @return TextAnimation; a copy of this TextAnimation
186 * @throws RemoteException when remote animation cannot be reached
187 * @throws NamingException when animation name cannot be found or bound in the Context
188 */
189 public abstract TextAnimation clone(Locatable newSource, SimulatorInterface.TimeDoubleUnit newSimulator)
190 throws RemoteException, NamingException;
191
192 /**
193 * Retrieve the source.
194 * @return Locatable; the source
195 */
196 protected final Locatable getSource()
197 {
198 return this.source;
199 }
200
201 /**
202 * Retrieve dx.
203 * @return float; the value of dx
204 */
205 protected final float getDx()
206 {
207 return this.dx;
208 }
209
210 /**
211 * Retrieve dy.
212 * @return float; the value of dy
213 */
214 protected final float getDy()
215 {
216 return this.dy;
217 }
218
219 /**
220 * Retrieve the text alignment.
221 * @return TextAlignment; the text alignment
222 */
223 protected final TextAlignment getTextAlignment()
224 {
225 return this.textAlignment;
226 }
227
228 /**
229 * Retrieve the font size.
230 * @return float; the font size
231 */
232 protected final float getFontSize()
233 {
234 return this.fontSize;
235 }
236
237 /**
238 * Retrieve the font.
239 * @return Font; the font
240 */
241 protected final Font getFont()
242 {
243 return this.font;
244 }
245
246 /**
247 * Retrieve the current text.
248 * @return String; the current text
249 */
250 protected final String getText()
251 {
252 return this.text;
253 }
254
255 /**
256 * Update the text.
257 * @param text String; the new text
258 */
259 protected final void setText(final String text)
260 {
261 this.text = text;
262 synchronized (this.font)
263 {
264 this.fontRectangle = null;
265 }
266 }
267
268 /**
269 * Retrieve the current color.
270 * @return Color; the current color
271 */
272 protected final Color getColor()
273 {
274 return this.color;
275 }
276
277 /**
278 * Update the color.
279 * @param color Color; the new color
280 */
281 protected final void setColor(final Color color)
282 {
283 this.color = color;
284 }
285
286 /**
287 * Retrieve the current flip status.
288 * @return boolean; the current flip status
289 */
290 public final boolean isFlip()
291 {
292 return this.animationImpl.isFlip();
293 }
294
295 /**
296 * Update the flip status.
297 * @param flip boolean; the new flip status
298 */
299 public final void setFlip(final boolean flip)
300 {
301 this.animationImpl.setFlip(flip);
302 }
303
304 /**
305 * Retrieve the current rotation status.
306 * @return boolean; the current rotation status
307 */
308 public final boolean isRotate()
309 {
310 return this.animationImpl.isRotate();
311 }
312
313 /**
314 * Update the rotation status.
315 * @param rotate boolean; the new rotation status
316 */
317 public final void setRotate(final boolean rotate)
318 {
319 this.animationImpl.setRotate(rotate);
320
321 }
322
323 /**
324 * Retrieve the current scale status.
325 * @return boolean; the current scale status
326 */
327 public final boolean isScale()
328 {
329 return this.animationImpl.isScale();
330 }
331
332 /**
333 * Update the scale status.
334 * @param scale boolean; the new scale status
335 */
336 public final void setScale(final boolean scale)
337 {
338 this.animationImpl.setScale(scale);
339 }
340
341 /**
342 * Retrieve the current translate status.
343 * @return boolean; the current translate status
344 */
345 public final boolean isTranslate()
346 {
347 return this.animationImpl.isTranslate();
348 }
349
350 /**
351 * Update the translate status.
352 * @param translate boolean; the new translate status
353 */
354 public final void setTranslate(final boolean translate)
355 {
356 this.animationImpl.setTranslate(translate);
357 }
358
359 /**
360 * The implementation of the text animation. Cloning will be taken care of by the overarching TextAnimation-derived class.
361 * <p>
362 * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
363 * <br>
364 * BSD-style license. See <a href="http://opentrafficsim.org/docs/current/license.html">OpenTrafficSim License</a>.
365 * </p>
366 * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
367 * initial version Dec 11, 2016 <br>
368 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
369 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
370 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
371 */
372 private static class AnimationImpl extends Renderable2D<Locatable> implements Serializable
373 {
374 /** */
375 private static final long serialVersionUID = 20170400L;
376
377 /**
378 * Construct a new AnimationImpl.
379 * @param source Locatable; the source
380 * @param simulator SimulatorInterface.TimeDoubleUnit; the simulator
381 * @throws NamingException when animation context cannot be created or retrieved
382 * @throws RemoteException when remote context cannot be found
383 */
384 AnimationImpl(final Locatable source, final SimulatorInterface.TimeDoubleUnit simulator)
385 throws NamingException, RemoteException
386 {
387 super(source, simulator);
388 }
389
390 /** {@inheritDoc} */
391 @Override
392 public final void paint(final Graphics2D graphics, final ImageObserver observer) throws RemoteException
393 {
394 TextAnimation ta = ((TextAnimation) getSource());
395 ta.paint(graphics, observer);
396 }
397
398 /** {@inheritDoc} */
399 @Override
400 public boolean contains(final Point2D pointWorldCoordinates, final Rectangle2D extent, final Dimension screen)
401 {
402 return false;
403 }
404
405 /** {@inheritDoc} */
406 @Override
407 public final String toString()
408 {
409 return "TextAnimation.AnimationImpl []";
410 }
411
412 }
413 }