LinkAnimation.java
package org.opentrafficsim.animation.network;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.image.ImageObserver;
import java.util.function.Supplier;
import org.djutils.base.Identifiable;
import org.djutils.draw.line.PolyLine2d;
import org.djutils.draw.point.DirectedPoint2d;
import org.djutils.draw.point.Point2d;
import org.opentrafficsim.animation.Colors;
import org.opentrafficsim.animation.DrawLevel;
import org.opentrafficsim.animation.LineLocatable;
import org.opentrafficsim.animation.OtsRenderableLabeled;
import org.opentrafficsim.animation.PaintLine;
import org.opentrafficsim.animation.RenderableTextSource;
import org.opentrafficsim.animation.TextAlignment;
import org.opentrafficsim.animation.network.LinkAnimation.LinkData;
import org.opentrafficsim.animation.network.LinkAnimation.Text;
import nl.tudelft.simulation.naming.context.Contextualized;
/**
* Draws link data.
* <p>
* Copyright (c) 2013-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
* </p>
* @author Alexander Verbraeck
* @author Wouter Schakel
*/
public class LinkAnimation extends OtsRenderableLabeled<LinkData, Text>
{
/** Width. */
private float width;
/** Drawable path. */
private Path2D.Float path;
/** Drawable path for start point. */
private Path2D.Float startPoint;
/** Drawable path for end point. */
private Path2D.Float endPoint;
/** Color. */
private final Color color;
/** Design line, also cached in parent, this is to see if it has changed. */
private PolyLine2d designLine;
/**
* Constructor.
* @param link link data
* @param contextualized context provider
* @param width width
*/
public LinkAnimation(final LinkData link, final Contextualized contextualized, final float width)
{
super(link, contextualized);
this.width = width;
setPath();
this.color = getSource().isConnector() ? Color.PINK.darker() : Colors.OTS_BLUE;
}
@Override
protected Text createText(final LinkData source, final Contextualized contextualized, final String prefix)
{
return new Text(source, source::getId, contextualized);
}
@Override
public final void paint(final Graphics2D graphics, final ImageObserver observer)
{
if (isDynamic())
{
PolyLine2d line = getSource().getCenterLine();
if (!line.equals(this.designLine))
{
setPath();
}
}
setRendering(graphics);
double scale = Math.sqrt(graphics.getTransform().getDeterminant());
double factor = 2.0 / Math.min(scale, 2.0); // do not make smaller when zooming out below scale 3
PaintLine.paintLine(graphics, this.color, factor * this.width, this.path);
PaintLine.paintLine(graphics, this.color, this.width / 30, this.startPoint);
PaintLine.paintLine(graphics, this.color, this.width / 30, this.endPoint);
resetRendering(graphics);
}
/**
* Sets drawable paths.
*/
private void setPath()
{
this.designLine = getSource().getCenterLine();
this.path = PaintLine.getPath(getSource().getLocation(), this.designLine);
this.startPoint = getEndPoint(this.designLine.getFirst(), this.designLine.get(1));
this.endPoint = getEndPoint(this.designLine.getLast(), this.designLine.get(this.designLine.size() - 2));
}
/**
* @param pointEnd the end of the design line where a end point must be highlighted
* @param nextPoint the point nearest {@code pointEnd} (needed to figure out the direction of the design line)
* @return Path2D.Float; path to draw an end point
*/
private Path2D.Float getEndPoint(final Point2d pointEnd, final Point2d nextPoint)
{
double dx = nextPoint.x - pointEnd.x;
double dy = nextPoint.y - pointEnd.y;
double length = pointEnd.distance(nextPoint);
// scale dx, dy so that size is this.width
dx *= this.width / length;
dy *= this.width / length;
PolyLine2d line = new PolyLine2d(0.0, new Point2d(pointEnd.x - dy, pointEnd.y + dx),
new Point2d(pointEnd.x + dy, pointEnd.y - dx));
return PaintLine.getPath(getSource().getLocation(), line);
}
@Override
public final String toString()
{
return "LinkAnimation [width=" + this.width + ", link=" + super.getSource() + "]";
}
/**
* Text animation for the link.
*/
public static class Text extends RenderableTextSource<LinkData, Text>
{
/**
* Constructor.
* @param source the object for which the text is displayed
* @param text the text to display
* @param contextualized context provider
*/
public Text(final LinkData source, final Supplier<String> text, final Contextualized contextualized)
{
super(source, text, 0.0f, 1.5f, TextAlignment.CENTER, Color.BLACK, 2.0f, 12.0f, 50f, contextualized, null,
RenderableTextSource.RENDERWHEN10);
}
@Override
public final String toString()
{
return "LinkAnimation.Text []";
}
}
/**
* Provides the information required to draw a link.
*/
public interface LinkData extends LineLocatable, Identifiable
{
@Override
DirectedPoint2d getLocation();
/**
* Returns whether this is a connector.
* @return whether this is a connector
*/
boolean isConnector();
/**
* Returns the center line in world coordinates.
* @return the center line in world coordinates
*/
PolyLine2d getCenterLine();
@Override
default double getZ()
{
return DrawLevel.LINK.getZ();
}
}
}