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