View Javadoc
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 }