View Javadoc
1   package org.opentrafficsim.draw.network;
2   
3   import java.awt.Color;
4   import java.awt.Graphics2D;
5   import java.awt.geom.Path2D;
6   import java.awt.image.ImageObserver;
7   import java.util.function.Supplier;
8   
9   import org.djutils.base.Identifiable;
10  import org.djutils.draw.line.PolyLine2d;
11  import org.djutils.draw.point.OrientedPoint2d;
12  import org.djutils.draw.point.Point2d;
13  import org.opentrafficsim.draw.ClickableLineLocatable;
14  import org.opentrafficsim.draw.DrawLevel;
15  import org.opentrafficsim.draw.OtsRenderable;
16  import org.opentrafficsim.draw.PaintLine;
17  import org.opentrafficsim.draw.TextAlignment;
18  import org.opentrafficsim.draw.TextAnimation;
19  import org.opentrafficsim.draw.network.LinkAnimation.LinkData;
20  
21  import nl.tudelft.simulation.naming.context.Contextualized;
22  
23  /**
24   * Draws LinkData.
25   * <p>
26   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
27   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
28   * </p>
29   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
30   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
31   */
32  public class LinkAnimation extends OtsRenderable<LinkData>
33  {
34      /** */
35      private static final long serialVersionUID = 20140000L;
36  
37      /** */
38      private float width;
39  
40      /** the Text object to destroy when the animation is destroyed. */
41      private Text text;
42  
43      /** Drawable path. */
44      private Path2D.Float path;
45  
46      /** Drawable path for start point. */
47      private Path2D.Float startPoint;
48  
49      /** Drawable path for end point. */
50      private Path2D.Float endPoint;
51  
52      /** Color. */
53      private final Color color;
54  
55      /** Design line, also cached in parent, this is to see if it has changed. */
56      private PolyLine2d designLine;
57  
58      /** Whether animation is dynamic. */
59      private boolean dynamic = false;
60  
61      /**
62       * @param link link data.
63       * @param contextualized context provider.
64       * @param width width
65       */
66      public LinkAnimation(final LinkData link, final Contextualized contextualized, final float width)
67      {
68          super(link, contextualized);
69          this.width = width;
70          this.text = new Text(link, link::getId, 0.0f, 1.5f, TextAlignment.CENTER, Color.BLACK, contextualized,
71                  TextAnimation.RENDERWHEN10);
72          setPath();
73          this.color = getSource().isConnector() ? Color.PINK.darker() : Color.BLUE;
74      }
75  
76      /**
77       * Sets the animation as dynamic, obtaining geometry at each draw.
78       * @param dynamic whether it is dynamic {@code false} by default.
79       * @return for method chaining.
80       */
81      public LinkAnimation setDynamic(final boolean dynamic)
82      {
83          this.dynamic = dynamic;
84          return this;
85      }
86  
87      @Override
88      public final void paint(final Graphics2D graphics, final ImageObserver observer)
89      {
90          if (this.dynamic)
91          {
92              PolyLine2d designLine = getSource().getCenterLine();
93              if (!designLine.equals(this.designLine))
94              {
95                  setPath();
96              }
97          }
98          setRendering(graphics);
99          PaintLine.paintLine(graphics, this.color, this.width, this.path);
100         PaintLine.paintLine(graphics, this.color, this.width / 30, this.startPoint);
101         PaintLine.paintLine(graphics, this.color, this.width / 30, this.endPoint);
102         resetRendering(graphics);
103     }
104 
105     /**
106      * Sets drawable paths.
107      */
108     private void setPath()
109     {
110         this.designLine = getSource().getCenterLine();
111         this.path = PaintLine.getPath(getSource().getLocation(), this.designLine);
112         this.startPoint = getEndPoint(this.designLine.getFirst(), this.designLine.get(1));
113         this.endPoint = getEndPoint(this.designLine.getLast(), this.designLine.get(this.designLine.size() - 2));
114     }
115 
116     /**
117      * @param endPoint the end of the design line where a end point must be highlighted
118      * @param nextPoint the point nearest <code>endPoint</code> (needed to figure out the direction of the design line)
119      * @return Path2D.Float; path to draw an end point.
120      */
121     private Path2D.Float getEndPoint(final Point2d endPoint, final Point2d nextPoint)
122     {
123         double dx = nextPoint.x - endPoint.x;
124         double dy = nextPoint.y - endPoint.y;
125         double length = endPoint.distance(nextPoint);
126         // scale dx, dy so that size is this.width
127         dx *= this.width / length;
128         dy *= this.width / length;
129         PolyLine2d line =
130                 new PolyLine2d(new Point2d(endPoint.x - dy, endPoint.y + dx), new Point2d(endPoint.x + dy, endPoint.y - dx));
131         return PaintLine.getPath(getSource().getLocation(), line);
132     }
133 
134     @Override
135     public void destroy(final Contextualized contextProvider)
136     {
137         super.destroy(contextProvider);
138         this.text.destroy(contextProvider);
139     }
140 
141     @Override
142     public final String toString()
143     {
144         return "LinkAnimation [width=" + this.width + ", link=" + super.getSource() + "]";
145     }
146 
147     /**
148      * Text animation for the Link. Separate class to be able to turn it on and off...
149      * <p>
150      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
151      * <br>
152      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
153      * </p>
154      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
155      * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
156      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
157      */
158     public class Text extends TextAnimation<LinkData, Text>
159     {
160         /** */
161         private static final long serialVersionUID = 20161211L;
162 
163         /**
164          * @param source the object for which the text is displayed
165          * @param text the text to display
166          * @param dx the horizontal movement of the text, in meters
167          * @param dy the vertical movement of the text, in meters
168          * @param textPlacement where to place the text
169          * @param color the color of the text
170          * @param contextualized context provider.
171          * @param scaleDependentRendering enables rendering in a scale dependent fashion
172          */
173         public Text(final LinkData source, final Supplier<String> text, final float dx, final float dy,
174                 final TextAlignment textPlacement, final Color color, final Contextualized contextualized,
175                 final ScaleDependentRendering scaleDependentRendering)
176         {
177             super(source, text, dx, dy, textPlacement, color, 2.0f, 12.0f, 50f, contextualized, null, scaleDependentRendering);
178         }
179 
180         @Override
181         public final String toString()
182         {
183             return "LinkAnimation.Text []";
184         }
185     }
186 
187     /**
188      * LinkData provides the information required to draw a link.
189      * <p>
190      * Copyright (c) 2023-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
191      * <br>
192      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
193      * </p>
194      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
195      */
196     public interface LinkData extends ClickableLineLocatable, Identifiable
197     {
198         @Override
199         OrientedPoint2d getLocation();
200 
201         /**
202          * Returns whether this is a connector.
203          * @return whether this is a connector.
204          */
205         boolean isConnector();
206 
207         /**
208          * Returns the center line in world coordinates.
209          * @return the center line in world coordinates.
210          */
211         PolyLine2d getCenterLine();
212 
213         @Override
214         default double getZ()
215         {
216             return DrawLevel.LINK.getZ();
217         }
218     }
219 
220 }