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