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