LaneStructureAnimation.java

  1. package org.opentrafficsim.animation.lane;

  2. import java.awt.BasicStroke;
  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.lang.reflect.Field;
  8. import java.rmi.RemoteException;
  9. import java.util.LinkedHashSet;
  10. import java.util.Set;

  11. import javax.naming.NamingException;

  12. import org.djutils.draw.point.OrientedPoint2d;
  13. import org.djutils.draw.point.Point2d;
  14. import org.djutils.exceptions.Try;
  15. import org.opentrafficsim.base.geometry.OtsRenderable;
  16. import org.opentrafficsim.core.geometry.OtsLine2d;
  17. import org.opentrafficsim.core.gtu.Gtu;
  18. import org.opentrafficsim.road.gtu.lane.perception.LaneStructureRecord;
  19. import org.opentrafficsim.road.gtu.lane.perception.RollingLaneStructure;
  20. import org.opentrafficsim.road.gtu.lane.perception.RollingLaneStructureRecord;
  21. import org.opentrafficsim.road.gtu.lane.perception.RollingLaneStructureRecord.RecordLink;

  22. /**
  23.  * LaneStructureAnimation.java.
  24.  * <p>
  25.  * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  26.  * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  27.  * </p>
  28.  * @author <a href="https://github.com/averbraeck" target="_blank">Alexander Verbraeck</a>
  29.  */
  30. public class LaneStructureAnimation extends OtsRenderable<LaneStructureLocatable>
  31. {
  32.     /** Destroyed. */
  33.     private boolean isDestroyed = false;

  34.     /**
  35.      * @param source LaneStructureLocatable; dummy locatable
  36.      * @throws NamingException on naming exception
  37.      * @throws RemoteException on remote exception
  38.      */
  39.     LaneStructureAnimation(final LaneStructureLocatable source) throws NamingException, RemoteException
  40.     {
  41.         super(source, source.getGtu().getSimulator());
  42.         this.setFlip(false);
  43.         this.setRotate(false);
  44.     }

  45.     /** {@inheritDoc} */
  46.     @Override
  47.     public void paint(final Graphics2D graphics, final ImageObserver observer)
  48.     {
  49.         if (!this.isDestroyed)
  50.         {
  51.             setRendering(graphics);
  52.             if (getSource().getGtu().isDestroyed())
  53.             {
  54.                 this.isDestroyed = true;
  55.                 Try.execute(() -> destroy(getSource().getGtu().getSimulator()),
  56.                         "Exception during deletion of LaneStructureAnimation.");
  57.                 return;
  58.             }
  59.             else
  60.             {
  61.                 LaneStructureRecord rt = getSource().getRollingLaneStructure().getRootRecord();
  62.                 if (rt != null)
  63.                 {
  64.                     paintRecord(rt, graphics);
  65.                 }
  66.             }
  67.             resetRendering(graphics);
  68.         }
  69.     }

  70.     /**
  71.      * @param lsr LaneStructureRecord; record
  72.      * @param graphics Graphics2D; graphics
  73.      */
  74.     @SuppressWarnings({"unchecked"})
  75.     private void paintRecord(final LaneStructureRecord lsr, final Graphics2D graphics)
  76.     {
  77.         // line
  78.         OrientedPoint2d loc = Try.assign(() -> getSource().getLocation(), "Unable to return location.");
  79.         graphics.setStroke(new BasicStroke(
  80.                 getSource().getRollingLaneStructure().animationAccess.getCrossSectionRecords().containsValue(lsr) ? 1.0f
  81.                         : 0.5f));
  82.         graphics.setColor(
  83.                 getSource().getRollingLaneStructure().animationAccess.getCrossSectionRecords().containsValue(lsr) ? Color.PINK
  84.                         : getSource().getRollingLaneStructure().animationAccess.getUpstreamEdge().contains(lsr) ? Color.MAGENTA
  85.                                 : getSource().getRollingLaneStructure().animationAccess.getDownstreamEdge().contains(lsr)
  86.                                         ? Color.GREEN : Color.CYAN);
  87.         OtsLine2d line = Try.assign(() -> lsr.getLane().getCenterLine().extractFractional(0.1, 0.9),
  88.                 "Exception while painting LaneStructures");
  89.         Path2D.Double path = new Path2D.Double();
  90.         boolean start = true;
  91.         for (Point2d point : line.getPoints())
  92.         {
  93.             if (start)
  94.             {
  95.                 path.moveTo(point.x - loc.x, -(point.y - loc.y));
  96.                 start = false;
  97.             }
  98.             else
  99.             {
  100.                 path.lineTo(point.x - loc.x, -(point.y - loc.y));
  101.             }
  102.         }
  103.         graphics.draw(path);
  104.         // connection
  105.         Field sourceField = Try.assign(() -> RollingLaneStructureRecord.class.getDeclaredField("source"),
  106.                 "Exception while painting LaneStructure");
  107.         sourceField.setAccessible(true);
  108.         LaneStructureRecord src =
  109.                 Try.assign(() -> (LaneStructureRecord) sourceField.get(lsr), "Exception while painting LaneStructure");
  110.         if (src != null)
  111.         {
  112.             Field sourceLinkField = Try.assign(() -> RollingLaneStructureRecord.class.getDeclaredField("sourceLink"),
  113.                     "Exception while painting LaneStructure");
  114.             sourceLinkField.setAccessible(true);
  115.             RecordLink link = (RecordLink) Try.assign(() -> sourceLinkField.get(lsr), "Exception while painting LaneStructure");
  116.             float f1 = link.equals(RecordLink.DOWN) ? 0.9f : link.equals(RecordLink.UP) ? 0.1f : 0.5f;
  117.             float f2 = link.equals(RecordLink.DOWN) ? 0.0f : link.equals(RecordLink.UP) ? 1.0f : 0.5f;
  118.             float f3 = f1;
  119.             float f4 = f2;
  120.             OrientedPoint2d p1 = Try.assign(() -> src.getLane().getCenterLine().getLocationFraction(f3),
  121.                     "Exception while painting LaneStructure");
  122.             OrientedPoint2d p2 = Try.assign(() -> line.getLocationFraction(f4), "Exception while painting LaneStructure");
  123.             path = new Path2D.Double();
  124.             path.moveTo(p1.x - loc.x, -(p1.y - loc.y));
  125.             path.lineTo(p2.x - loc.x, -(p2.y - loc.y));
  126.             graphics.setStroke(
  127.                     new BasicStroke(0.15f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[] {.3f, 1.2f}, 0f));
  128.             graphics.setColor(Color.DARK_GRAY);
  129.             graphics.draw(path);
  130.         }
  131.         // left/right
  132.         paintLateralConnection(lsr, lsr.getLeft(), Color.RED, graphics, loc);
  133.         paintLateralConnection(lsr, lsr.getRight(), Color.BLUE, graphics, loc);
  134.         // recursion to depending records
  135.         Field dependentField = Try.assign(() -> RollingLaneStructureRecord.class.getDeclaredField("dependentRecords"),
  136.                 "Exception while painting LaneStructure");
  137.         dependentField.setAccessible(true);
  138.         Set<LaneStructureRecord> dependables =
  139.                 (Set<LaneStructureRecord>) Try.assign(() -> dependentField.get(lsr), "Exception while painting LaneStructure");
  140.         if (dependables != null)
  141.         {
  142.             for (LaneStructureRecord dependable : new LinkedHashSet<>(dependables)) // concurrency
  143.             {
  144.                 paintRecord(dependable, graphics);
  145.             }
  146.         }
  147.     }

  148.     /**
  149.      * Paint the connection to a lateral record.
  150.      * @param main LaneStructureRecord; main record
  151.      * @param adj LaneStructureRecord; adjacent record, can be {@code null}
  152.      * @param color Color; color
  153.      * @param graphics Graphics2D; graphics
  154.      * @param loc OrientedPoint2d; location
  155.      */
  156.     private void paintLateralConnection(final LaneStructureRecord main, final LaneStructureRecord adj, final Color color,
  157.             final Graphics2D graphics, final OrientedPoint2d loc)
  158.     {
  159.         if (adj == null)
  160.         {
  161.             return;
  162.         }
  163.         float f1 = 0.45f;
  164.         float f2 = 0.55f;
  165.         OrientedPoint2d p1 = Try.assign(() -> main.getLane().getCenterLine().getLocationFraction(f1),
  166.                 "Exception while painting LaneStructure");
  167.         OrientedPoint2d p2 = Try.assign(() -> adj.getLane().getCenterLine().getLocationFraction(f2),
  168.                 "Exception while painting LaneStructure");
  169.         Path2D.Double path = new Path2D.Double();
  170.         path.moveTo(p1.x - loc.x, -(p1.y - loc.y));
  171.         path.lineTo(p2.x - loc.x, -(p2.y - loc.y));
  172.         graphics.setStroke(
  173.                 new BasicStroke(0.05f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[] {.15f, 0.6f}, 0f));
  174.         graphics.setColor(color);
  175.         graphics.draw(path);
  176.     }

  177.     /**
  178.      * Enables visualization of this lane structure. This is purely for debugging purposes.
  179.      * @param rollingLaneStructure RollingLaneStructure; the lane structure to visualize
  180.      * @param gtu Gtu; GTU to animate the LaneStructure of
  181.      */
  182.     public static final void visualize(final RollingLaneStructure rollingLaneStructure, final Gtu gtu)
  183.     {
  184.         Try.execute(() -> new LaneStructureAnimation(new LaneStructureLocatable(rollingLaneStructure, gtu)),
  185.                 "Could not create animation.");
  186.     }

  187. }