View Javadoc
1   package org.opentrafficsim.animation.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.animation.Colors;
14  import org.opentrafficsim.animation.DrawLevel;
15  import org.opentrafficsim.animation.LineLocatable;
16  import org.opentrafficsim.animation.OtsRenderableLabeled;
17  import org.opentrafficsim.animation.PaintLine;
18  import org.opentrafficsim.animation.RenderableTextSource;
19  import org.opentrafficsim.animation.TextAlignment;
20  import org.opentrafficsim.animation.network.LinkAnimation.LinkData;
21  import org.opentrafficsim.animation.network.LinkAnimation.Text;
22  
23  import nl.tudelft.simulation.naming.context.Contextualized;
24  
25  /**
26   * Draws link data.
27   * <p>
28   * Copyright (c) 2013-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
29   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
30   * </p>
31   * @author Alexander Verbraeck
32   * @author Wouter Schakel
33   */
34  public class LinkAnimation extends OtsRenderableLabeled<LinkData, Text>
35  {
36  
37      /** Width. */
38      private float width;
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      /**
56       * Constructor.
57       * @param link link data
58       * @param contextualized context provider
59       * @param width width
60       */
61      public LinkAnimation(final LinkData link, final Contextualized contextualized, final float width)
62      {
63          super(link, contextualized);
64          this.width = width;
65          setPath();
66          this.color = getSource().isConnector() ? Color.PINK.darker() : Colors.OTS_BLUE;
67      }
68  
69      @Override
70      protected Text createText(final LinkData source, final Contextualized contextualized, final String prefix)
71      {
72          return new Text(source, source::getId, contextualized);
73      }
74  
75      @Override
76      public final void paint(final Graphics2D graphics, final ImageObserver observer)
77      {
78          if (isDynamic())
79          {
80              PolyLine2d line = getSource().getCenterLine();
81              if (!line.equals(this.designLine))
82              {
83                  setPath();
84              }
85          }
86          setRendering(graphics);
87          double scale = Math.sqrt(graphics.getTransform().getDeterminant());
88          double factor = 2.0 / Math.min(scale, 2.0); // do not make smaller when zooming out below scale 3
89          PaintLine.paintLine(graphics, this.color, factor * this.width, this.path);
90          PaintLine.paintLine(graphics, this.color, this.width / 30, this.startPoint);
91          PaintLine.paintLine(graphics, this.color, this.width / 30, this.endPoint);
92          resetRendering(graphics);
93      }
94  
95      /**
96       * Sets drawable paths.
97       */
98      private void setPath()
99      {
100         this.designLine = getSource().getCenterLine();
101         this.path = PaintLine.getPath(getSource().getLocation(), this.designLine);
102         this.startPoint = getEndPoint(this.designLine.getFirst(), this.designLine.get(1));
103         this.endPoint = getEndPoint(this.designLine.getLast(), this.designLine.get(this.designLine.size() - 2));
104     }
105 
106     /**
107      * @param pointEnd the end of the design line where a end point must be highlighted
108      * @param nextPoint the point nearest {@code pointEnd} (needed to figure out the direction of the design line)
109      * @return Path2D.Float; path to draw an end point
110      */
111     private Path2D.Float getEndPoint(final Point2d pointEnd, final Point2d nextPoint)
112     {
113         double dx = nextPoint.x - pointEnd.x;
114         double dy = nextPoint.y - pointEnd.y;
115         double length = pointEnd.distance(nextPoint);
116         // scale dx, dy so that size is this.width
117         dx *= this.width / length;
118         dy *= this.width / length;
119         PolyLine2d line = new PolyLine2d(0.0, new Point2d(pointEnd.x - dy, pointEnd.y + dx),
120                 new Point2d(pointEnd.x + dy, pointEnd.y - dx));
121         return PaintLine.getPath(getSource().getLocation(), line);
122     }
123 
124     @Override
125     public final String toString()
126     {
127         return "LinkAnimation [width=" + this.width + ", link=" + super.getSource() + "]";
128     }
129 
130     /**
131      * Text animation for the link.
132      */
133     public static class Text extends RenderableTextSource<LinkData, Text>
134     {
135         /**
136          * Constructor.
137          * @param source the object for which the text is displayed
138          * @param text the text to display
139          * @param contextualized context provider
140          */
141         public Text(final LinkData source, final Supplier<String> text, final Contextualized contextualized)
142         {
143             super(source, text, 0.0f, 1.5f, TextAlignment.CENTER, Color.BLACK, 2.0f, 12.0f, 50f, contextualized, null,
144                     RenderableTextSource.RENDERWHEN10);
145         }
146 
147         @Override
148         public final String toString()
149         {
150             return "LinkAnimation.Text []";
151         }
152     }
153 
154     /**
155      * Provides the information required to draw a link.
156      */
157     public interface LinkData extends LineLocatable, Identifiable
158     {
159         @Override
160         DirectedPoint2d getLocation();
161 
162         /**
163          * Returns whether this is a connector.
164          * @return whether this is a connector
165          */
166         boolean isConnector();
167 
168         /**
169          * Returns the center line in world coordinates.
170          * @return the center line in world coordinates
171          */
172         PolyLine2d getCenterLine();
173 
174         @Override
175         default double getZ()
176         {
177             return DrawLevel.LINK.getZ();
178         }
179     }
180 
181 }