DefaultCarAnimation.java

  1. package org.opentrafficsim.draw.gtu;

  2. import java.awt.BasicStroke;
  3. import java.awt.Color;
  4. import java.awt.Graphics2D;
  5. import java.awt.geom.Ellipse2D;
  6. import java.awt.geom.Rectangle2D;
  7. import java.awt.geom.RectangularShape;
  8. import java.awt.image.ImageObserver;
  9. import java.rmi.RemoteException;
  10. import java.util.function.Supplier;

  11. import javax.naming.NamingException;

  12. import org.djunits.value.vdouble.scalar.Length;
  13. import org.djutils.base.Identifiable;
  14. import org.djutils.draw.point.OrientedPoint2d;
  15. import org.opentrafficsim.base.geometry.OtsLocatable;
  16. import org.opentrafficsim.base.geometry.OtsRenderable;
  17. import org.opentrafficsim.draw.DrawLevel;
  18. import org.opentrafficsim.draw.TextAlignment;
  19. import org.opentrafficsim.draw.TextAnimation;
  20. import org.opentrafficsim.draw.gtu.DefaultCarAnimation.GtuData;

  21. import nl.tudelft.simulation.naming.context.Contextualized;

  22. /**
  23.  * Draw a car.
  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">Alexander Verbraeck</a>
  29.  * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  30.  * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  31.  */
  32. public class DefaultCarAnimation extends OtsRenderable<GtuData>
  33. {
  34.     /** */
  35.     private static final long serialVersionUID = 20150000L;

  36.     /** the Text object to destroy when the GTU animation is destroyed. */
  37.     private Text text;

  38.     /** Hashcode. */
  39.     private final int hashCode;

  40.     /** GTU outline. */
  41.     private Rectangle2D.Double rectangle;

  42.     /** Front indicator (white circle). */
  43.     private Ellipse2D.Double frontIndicator;

  44.     /** Left indicator. */
  45.     private Rectangle2D.Double leftIndicator;

  46.     /** Right indicator. */
  47.     private Rectangle2D.Double rightIndicator;

  48.     /** Left brake light. */
  49.     private Rectangle2D.Double leftBrake;

  50.     /** Right brake light. */
  51.     private Rectangle2D.Double rightBrake;

  52.     /** Marker if zoomed out. */
  53.     private RectangularShape marker;

  54.     /**
  55.      * Construct the DefaultCarAnimation for a LaneBasedIndividualCar.
  56.      * @param gtu GtuData; the Car to draw
  57.      * @param contextualized Contextualized; context provider
  58.      * @throws NamingException in case of registration failure of the animation
  59.      * @throws RemoteException on communication failure
  60.      */
  61.     public DefaultCarAnimation(final GtuData gtu, final Contextualized contextualized) throws NamingException, RemoteException
  62.     {
  63.         super(gtu, contextualized);
  64.         this.hashCode = gtu.hashCode();
  65.         this.text = new Text(gtu, gtu::getId, 0.0f, 0.0f, TextAlignment.CENTER, Color.BLACK, contextualized,
  66.                 new TextAnimation.ContrastToBackground()
  67.                 {
  68.                     /** {@inheritDoc} */
  69.                     @Override
  70.                     public Color getBackgroundColor()
  71.                     {
  72.                         return gtu.getColor();
  73.                     }
  74.                 }).setDynamic(true);
  75.     }

  76.     /** {@inheritDoc} */
  77.     @Override
  78.     public final void paint(final Graphics2D graphics, final ImageObserver observer)
  79.     {
  80.         setRendering(graphics);
  81.         final GtuData gtu = getSource();
  82.         if (this.rectangle == null)
  83.         {
  84.             // set shapes, this is done in paint() and not the constructor, as the super constructor binds to context causing
  85.             // paint commands before the shapes are calculated in the constructor
  86.             final double length = gtu.getLength().si;
  87.             final double lFront = gtu.getFront().si;
  88.             final double lRear = gtu.getRear().si;
  89.             final double width = gtu.getWidth().si;
  90.             final double w2 = width / 2;
  91.             final double w4 = width / 4;
  92.             this.rectangle = new Rectangle2D.Double(lRear, -w2, length, width);
  93.             this.frontIndicator = new Ellipse2D.Double(lFront - w2 - w4, -w4, w2, w2);
  94.             this.leftIndicator = new Rectangle2D.Double(lFront - w4, -w2, w4, w4);
  95.             this.rightIndicator = new Rectangle2D.Double(lFront - w4, w2 - w4, w4, w4);
  96.             this.leftBrake = new Rectangle2D.Double(lRear, w2 - w4, w4, w4);
  97.             this.rightBrake = new Rectangle2D.Double(lRear, -w2, w4, w4);
  98.             this.marker = gtu.getMarker();
  99.         }

  100.         double scale = graphics.getTransform().getDeterminant();
  101.         // Math.sqrt(Math.pow(graphics.getTransform()..getScaleX(), 2)
  102.         // Math.pow(graphics.getTransform().getScaleY(), 2));
  103.         if (scale > 1)
  104.         {
  105.             Color color = gtu.getColor();
  106.             graphics.setColor(color);
  107.             BasicStroke saveStroke = (BasicStroke) graphics.getStroke();
  108.             graphics.setStroke(new BasicStroke(0.05f)); // 5 cm
  109.             graphics.fill(this.rectangle);

  110.             graphics.setColor(Color.WHITE);
  111.             graphics.fill(this.frontIndicator);
  112.             // Draw a white disk at the front to indicate which side faces forward
  113.             if (color.equals(Color.WHITE))
  114.             {
  115.                 // Put a black ring around it
  116.                 graphics.setColor(Color.BLACK);
  117.                 graphics.draw(this.frontIndicator);
  118.             }

  119.             // turn indicator lights
  120.             graphics.setColor(Color.YELLOW);
  121.             if (gtu.leftIndicatorOn())
  122.             {
  123.                 graphics.fill(this.leftIndicator);
  124.                 if (color.equals(Color.YELLOW))
  125.                 {
  126.                     graphics.setColor(Color.BLACK);
  127.                     graphics.draw(this.leftIndicator);
  128.                 }
  129.             }
  130.             if (gtu.rightIndicatorOn())
  131.             {
  132.                 graphics.fill(this.rightIndicator);
  133.                 if (color.equals(Color.YELLOW))
  134.                 {
  135.                     graphics.setColor(Color.BLACK);
  136.                     graphics.draw(this.rightIndicator);
  137.                 }
  138.             }

  139.             // braking lights
  140.             if (gtu.isBrakingLightsOn())
  141.             {
  142.                 graphics.setColor(Color.RED);
  143.                 graphics.fill(this.leftBrake);
  144.                 graphics.fill(this.rightBrake);
  145.                 if (color.equals(Color.RED))
  146.                 {
  147.                     graphics.setColor(Color.BLACK);
  148.                     graphics.draw(this.leftBrake);
  149.                     graphics.draw(this.rightBrake);
  150.                 }
  151.             }
  152.             graphics.setStroke(saveStroke);
  153.         }
  154.         else
  155.         {
  156.             // zoomed out, draw as marker with 7px diameter
  157.             graphics.setColor(gtu.getColor());
  158.             double w = 7.0 / Math.sqrt(scale);
  159.             double x = -w / 2.0;
  160.             this.marker.setFrame(x, x, w, w);
  161.             graphics.fill(this.marker);
  162.         }
  163.         resetRendering(graphics);
  164.     }

  165.     /** {@inheritDoc} */
  166.     @Override
  167.     public void destroy(final Contextualized contextProvider)
  168.     {
  169.         super.destroy(contextProvider);
  170.         this.text.destroy(contextProvider);
  171.     }

  172.     /** {@inheritDoc} */
  173.     @Override
  174.     public int hashCode()
  175.     {
  176.         return this.hashCode;
  177.     }

  178.     /** {@inheritDoc} */
  179.     @Override
  180.     public boolean equals(final Object object)
  181.     {
  182.         // only here to prevent a 'hashCode without equals' warning
  183.         return super.equals(object);
  184.     }

  185.     /**
  186.      * Text animation for the Car. Separate class to be able to turn it on and off...
  187.      * <p>
  188.      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  189.      * <br>
  190.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  191.      * </p>
  192.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  193.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  194.      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  195.      */
  196.     public class Text extends TextAnimation<GtuData, Text>
  197.     {
  198.         /** */
  199.         private static final long serialVersionUID = 20161211L;

  200.         /** is the animation destroyed? */
  201.         private boolean isTextDestroyed = false;

  202.         /**
  203.          * @param source GtuData; the object for which the text is displayed
  204.          * @param text Supplier&lt;String&gt;; the text to display
  205.          * @param dx float; the horizontal movement of the text, in meters
  206.          * @param dy float; the vertical movement of the text, in meters
  207.          * @param textAlignment TextAlignment; where to place the text
  208.          * @param color Color; the color of the text
  209.          * @param contextualized Contextualized; context provider
  210.          * @throws NamingException when animation context cannot be created or retrieved
  211.          * @throws RemoteException - when remote context cannot be found
  212.          */
  213.         public Text(final GtuData source, final Supplier<String> text, final float dx, final float dy,
  214.                 final TextAlignment textAlignment, final Color color, final Contextualized contextualized)
  215.                 throws RemoteException, NamingException
  216.         {
  217.             super(source, text, dx, dy, textAlignment, color, 1.0f, 12.0f, 50f, contextualized, TextAnimation.RENDERWHEN1);
  218.         }

  219.         /**
  220.          * @param source GtuData; the object for which the text is displayed
  221.          * @param text Supplier&lt;String&gt;; the text to display
  222.          * @param dx float; the horizontal movement of the text, in meters
  223.          * @param dy float; the vertical movement of the text, in meters
  224.          * @param textAlignment TextAlignment; where to place the text
  225.          * @param color Color; the color of the text
  226.          * @param contextualized Contextualized; context provider
  227.          * @param background TextAnimation.ContrastToBackground; connection to retrieve the current background color
  228.          * @throws NamingException when animation context cannot be created or retrieved
  229.          * @throws RemoteException - when remote context cannot be found
  230.          */
  231.         @SuppressWarnings("parameternumber")
  232.         public Text(final GtuData source, final Supplier<String> text, final float dx, final float dy,
  233.                 final TextAlignment textAlignment, final Color color, final Contextualized contextualized,
  234.                 final TextAnimation.ContrastToBackground background) throws RemoteException, NamingException
  235.         {
  236.             super(source, text, dx, dy, textAlignment, color, 1.0f, 12.0f, 50f, contextualized, background, RENDERWHEN1);
  237.         }

  238.         /** {@inheritDoc} */
  239.         @Override
  240.         public final String toString()
  241.         {
  242.             return "Text [isTextDestroyed=" + this.isTextDestroyed + "]";
  243.         }

  244.     }

  245.     /**
  246.      * GtuData provides the information required to draw a link.
  247.      * <p>
  248.      * Copyright (c) 2023-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
  249.      * <br>
  250.      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  251.      * </p>
  252.      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
  253.      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
  254.      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
  255.      */
  256.     public interface GtuData extends OtsLocatable, Identifiable
  257.     {
  258.         /**
  259.          * Returns the GTU color.
  260.          * @return Color; GTU color.
  261.          */
  262.         Color getColor();

  263.         /**
  264.          * Returns the length.
  265.          * @return Length; length.
  266.          */
  267.         Length getLength();

  268.         /**
  269.          * Returns the width.
  270.          * @return Length; width.
  271.          */
  272.         Length getWidth();

  273.         /**
  274.          * Returns the distance towards the front.
  275.          * @return Length; distance towards the front.
  276.          */
  277.         Length getFront();

  278.         /**
  279.          * Returns the distance towards the rear.
  280.          * @return Length; distance towards the rear.
  281.          */
  282.         Length getRear();

  283.         /**
  284.          * Returns whether the left indicator is on.
  285.          * @return boolean; whether the left indicator is on.
  286.          */
  287.         boolean leftIndicatorOn();

  288.         /**
  289.          * Returns whether the right indicator is on.
  290.          * @return boolean; whether the right indicator is on.
  291.          */
  292.         boolean rightIndicatorOn();

  293.         /**
  294.          * Returns the shape of a marker to show when zoomed out.
  295.          * @return RectangularShape; shape of a marker to show when zoomed out.
  296.          */
  297.         default RectangularShape getMarker()
  298.         {
  299.             return new Ellipse2D.Double(0, 0, 0, 0);
  300.         }

  301.         /**
  302.          * Returns whether the braking lights are on.
  303.          * @return boolean; whether the braking lights are on.
  304.          */
  305.         boolean isBrakingLightsOn();

  306.         /** {@inheritDoc} */
  307.         @Override
  308.         OrientedPoint2d getLocation();

  309.         /** {@inheritDoc} */
  310.         @Override
  311.         default double getZ()
  312.         {
  313.             return DrawLevel.GTU.getZ();
  314.         }
  315.     }

  316. }