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