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