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