View Javadoc
1   package org.opentrafficsim.draw.lane;
2   
3   import java.awt.BasicStroke;
4   import java.awt.Color;
5   import java.awt.Graphics2D;
6   import java.awt.geom.Path2D;
7   import java.awt.image.ImageObserver;
8   import java.lang.reflect.Field;
9   import java.rmi.RemoteException;
10  import java.util.LinkedHashSet;
11  import java.util.Set;
12  
13  import javax.naming.NamingException;
14  
15  import org.djutils.exceptions.Try;
16  import org.opentrafficsim.core.geometry.OTSLine3D;
17  import org.opentrafficsim.core.geometry.OTSPoint3D;
18  import org.opentrafficsim.core.gtu.GTU;
19  import org.opentrafficsim.road.gtu.lane.perception.LaneStructureRecord;
20  import org.opentrafficsim.road.gtu.lane.perception.RollingLaneStructure;
21  import org.opentrafficsim.road.gtu.lane.perception.RollingLaneStructureRecord;
22  import org.opentrafficsim.road.gtu.lane.perception.RollingLaneStructureRecord.RecordLink;
23  
24  import nl.tudelft.simulation.dsol.animation.D2.Renderable2D;
25  import nl.tudelft.simulation.language.d3.DirectedPoint;
26  
27  /**
28   * LaneStructureAnimation.java. <br>
29   * <br>
30   * Copyright (c) 2003-2019 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
31   * for project information <a href="https://www.simulation.tudelft.nl/" target="_blank">www.simulation.tudelft.nl</a>. The
32   * source code and binary code of this software is proprietary information of Delft University of Technology.
33   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
34   */
35  public class LaneStructureAnimation extends Renderable2D<LaneStructureLocatable>
36  {
37      /** Destroyed. */
38      private boolean isDestroyed = false;
39  
40      /**
41       * @param source LaneStructureLocatable; dummy locatable
42       * @throws NamingException on naming exception
43       * @throws RemoteException on remote exception
44       */
45      LaneStructureAnimation(final LaneStructureLocatable source) throws NamingException, RemoteException
46      {
47          super(source, source.getGtu().getSimulator());
48          this.setFlip(false);
49          this.setRotate(false);
50      }
51  
52      /** {@inheritDoc} */
53      @Override
54      public void paint(final Graphics2D graphics, final ImageObserver observer) throws RemoteException
55      {
56          if (!this.isDestroyed)
57          {
58              if (getSource().getGtu().isDestroyed())
59              {
60                  this.isDestroyed = true;
61                  Try.execute(() -> destroy(), "Exception during deletion of LaneStructureAnimation.");
62                  return;
63              }
64              else
65              {
66                  LaneStructureRecord rt = getSource().getRollingLaneStructure().getRootRecord();
67                  if (rt != null)
68                  {
69                      paintRecord(rt, graphics);
70                  }
71              }
72          }
73      }
74  
75      /**
76       * @param lsr LaneStructureRecord; record
77       * @param graphics Graphics2D; graphics
78       */
79      @SuppressWarnings({"unchecked"})
80      private void paintRecord(final LaneStructureRecord lsr, final Graphics2D graphics)
81      {
82          // line
83          DirectedPoint loc = Try.assign(() -> getSource().getLocation(), "Unable to return location.");
84          graphics.setStroke(new BasicStroke(
85                  getSource().getRollingLaneStructure().animationAccess.getCrossSectionRecords().containsValue(lsr) ? 1.0f
86                          : 0.5f));
87          graphics.setColor(
88                  getSource().getRollingLaneStructure().animationAccess.getCrossSectionRecords().containsValue(lsr) ? Color.PINK
89                          : getSource().getRollingLaneStructure().animationAccess.getUpstreamEdge().contains(lsr) ? Color.MAGENTA
90                                  : getSource().getRollingLaneStructure().animationAccess.getDownstreamEdge().contains(lsr)
91                                          ? Color.GREEN : Color.CYAN);
92          OTSLine3D line = Try.assign(() -> lsr.getLane().getCenterLine().extractFractional(0.1, 0.9),
93                  "Exception while painting LaneStructures");
94          Path2D.Double path = new Path2D.Double();
95          boolean start = true;
96          for (OTSPoint3D point : line.getPoints())
97          {
98              if (start)
99              {
100                 path.moveTo(point.x - loc.x, -(point.y - loc.y));
101                 start = false;
102             }
103             else
104             {
105                 path.lineTo(point.x - loc.x, -(point.y - loc.y));
106             }
107         }
108         graphics.draw(path);
109         // connection
110         Field sourceField = Try.assign(() -> RollingLaneStructureRecord.class.getDeclaredField("source"),
111                 "Exception while painting LaneStructure");
112         sourceField.setAccessible(true);
113         LaneStructureRecord src =
114                 Try.assign(() -> (LaneStructureRecord) sourceField.get(lsr), "Exception while painting LaneStructure");
115         if (src != null)
116         {
117             Field sourceLinkField = Try.assign(() -> RollingLaneStructureRecord.class.getDeclaredField("sourceLink"),
118                     "Exception while painting LaneStructure");
119             sourceLinkField.setAccessible(true);
120             RecordLink link = (RecordLink) Try.assign(() -> sourceLinkField.get(lsr), "Exception while painting LaneStructure");
121             float f1 = link.equals(RecordLink.DOWN) ? 0.9f : link.equals(RecordLink.UP) ? 0.1f : 0.5f;
122             float f2 = link.equals(RecordLink.DOWN) ? 0.0f : link.equals(RecordLink.UP) ? 1.0f : 0.5f;
123             f1 = src.getDirection().isPlus() ? f1 : 1.0f - f1;
124             f2 = lsr.getDirection().isPlus() ? f2 : 1.0f - f2;
125             float f3 = f1;
126             float f4 = f2;
127             DirectedPoint p1 = Try.assign(() -> src.getLane().getCenterLine().getLocationFraction(f3),
128                     "Exception while painting LaneStructure");
129             DirectedPoint p2 = Try.assign(() -> line.getLocationFraction(f4), "Exception while painting LaneStructure");
130             path = new Path2D.Double();
131             path.moveTo(p1.x - loc.x, -(p1.y - loc.y));
132             path.lineTo(p2.x - loc.x, -(p2.y - loc.y));
133             graphics.setStroke(
134                     new BasicStroke(0.15f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[] {.3f, 1.2f}, 0f));
135             graphics.setColor(Color.DARK_GRAY);
136             graphics.draw(path);
137         }
138         // left/right
139         paintLateralConnection(lsr, lsr.getLeft(), Color.RED, graphics, loc);
140         paintLateralConnection(lsr, lsr.getRight(), Color.BLUE, graphics, loc);
141         // recursion to depending records
142         Field dependentField = Try.assign(() -> RollingLaneStructureRecord.class.getDeclaredField("dependentRecords"),
143                 "Exception while painting LaneStructure");
144         dependentField.setAccessible(true);
145         Set<LaneStructureRecord> dependables =
146                 (Set<LaneStructureRecord>) Try.assign(() -> dependentField.get(lsr), "Exception while painting LaneStructure");
147         if (dependables != null)
148         {
149             for (LaneStructureRecord dependable : new LinkedHashSet<>(dependables)) // concurrency
150             {
151                 paintRecord(dependable, graphics);
152             }
153         }
154     }
155 
156     /**
157      * Paint the connection to a lateral record.
158      * @param main LaneStructureRecord; main record
159      * @param adj LaneStructureRecord; adjacent record, can be {@code null}
160      * @param color Color; color
161      * @param graphics Graphics2D; graphics
162      * @param loc DirectedPoint; location
163      */
164     private void paintLateralConnection(final LaneStructureRecord main, final LaneStructureRecord adj, final Color color,
165             final Graphics2D graphics, final DirectedPoint loc)
166     {
167         if (adj == null)
168         {
169             return;
170         }
171         float f1 = main.getDirection().isPlus() ? 0.45f : 0.55f;
172         float f2 = adj.getDirection().isPlus() ? 0.55f : 0.45f;
173         DirectedPoint p1 = Try.assign(() -> main.getLane().getCenterLine().getLocationFraction(f1),
174                 "Exception while painting LaneStructure");
175         DirectedPoint p2 = Try.assign(() -> adj.getLane().getCenterLine().getLocationFraction(f2),
176                 "Exception while painting LaneStructure");
177         Path2D.Double path = new Path2D.Double();
178         path.moveTo(p1.x - loc.x, -(p1.y - loc.y));
179         path.lineTo(p2.x - loc.x, -(p2.y - loc.y));
180         graphics.setStroke(
181                 new BasicStroke(0.05f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[] {.15f, 0.6f}, 0f));
182         graphics.setColor(color);
183         graphics.draw(path);
184     }
185 
186     /**
187      * Enables visualization of this lane structure. This is purely for debugging purposes.
188      * @param rollingLaneStructure RollingLaneStructure; the lane structure to visualize
189      * @param gtu GTU; GTU to animate the LaneStructure of
190      */
191     public static final void visualize(final RollingLaneStructure rollingLaneStructure, final GTU gtu)
192     {
193         Try.execute(() -> new LaneStructureAnimation(new LaneStructureLocatable(rollingLaneStructure, gtu)),
194                 "Could not create animation.");
195     }
196 
197 }