OtsRenderable.java
package org.opentrafficsim.draw;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.RenderingHints.Key;
import java.awt.geom.Point2D;
import org.djutils.draw.Transform2d;
import org.djutils.draw.bounds.Bounds2d;
import org.djutils.draw.point.Point2d;
import org.opentrafficsim.base.geometry.OtsShape;
import nl.tudelft.simulation.dsol.animation.d2.Renderable2d;
import nl.tudelft.simulation.dsol.animation.d2.RenderableScale;
import nl.tudelft.simulation.naming.context.Contextualized;
/**
* Extends {@code Renderable2d} to let the {@code contains} method look at the actual bounds shape, rather than only the box.
* <p>
* Copyright (c) 2024-2024 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 <a href="https://github.com/wjschakel">Wouter Schakel</a>
* @param <L> locatable type
*/
public abstract class OtsRenderable<L extends OtsShape> extends Renderable2d<L>
{
/** Standard rendering keys. */
private static final Key[] RENDERING_KEYS = new Key[] {RenderingHints.KEY_ANTIALIASING};
/** Standard rendering values. */
private static final Object[] RENDERING_VALUES = new Object[] {RenderingHints.VALUE_ANTIALIAS_ON};
/** Stored hints to reset. */
private Object[] oldRenderingHints = new Object[RENDERING_KEYS.length];
/**
* Constructs a new Renderable2d.
* @param source the source
* @param contextProvider the object that can provide the context to store the animation objects
*/
public OtsRenderable(final L source, final Contextualized contextProvider)
{
super(source, contextProvider);
setScaleY(true);
}
/**
* Set standard rendering hints for this renderable to paint. The graphics should be reset using {@code resetRendering()}
* after painting.
* @param graphics graphics.
*/
protected void setRendering(final Graphics2D graphics)
{
for (int i = 0; i < RENDERING_KEYS.length; i++)
{
this.oldRenderingHints[i] = graphics.getRenderingHint(RENDERING_KEYS[i]);
graphics.setRenderingHint(RENDERING_KEYS[i], RENDERING_VALUES[i]);
}
}
/**
* Resets rendering hints that this renderable changed through {@code setRendering()}.
* @param graphics graphics.
*/
protected void resetRendering(final Graphics2D graphics)
{
for (int i = 0; i < RENDERING_KEYS.length; i++)
{
// If ever a null valued hint is used, just check for null values and do not reset the value in that case.
// For now the check is not implemented as no such hint is used.
// if (this.oldRenderingHints[i] != null)
// {
graphics.setRenderingHint(RENDERING_KEYS[i], this.oldRenderingHints[i]);
// }
}
}
@Override
public boolean contains(final Point2D pointScreenCoordinates, final Bounds2d extent, final Dimension screenSize,
final RenderableScale scale, final double worldMargin, final double pixelMargin)
{
// super implementation seems to not handle rotation well, so we apply a different transformation order here
Point2d screenLocation = scale.getScreenCoordinatesAsPoint2d(getSource().getLocation(), extent, screenSize);
double xScale = scale.getXScale(extent, screenSize);
double yScale = scale.getYScale(extent, screenSize);
Transform2d transformation = new Transform2d();
transformation.rotation(-getSource().getDirZ());
transformation.scale(xScale, yScale);
transformation.reflectY();
transformation.translate(screenLocation.neg());
Point2d pointRelativeTo00 =
transformation.transform(new Point2d(pointScreenCoordinates.getX(), pointScreenCoordinates.getY()));
return contains(pointRelativeTo00, scale, worldMargin, pixelMargin, xScale, yScale);
}
@Override
public boolean contains(final Point2d pointRelativeTo00, final RenderableScale scale, final double worldMargin,
final double pixelMargin, final double xScale, final double yScale)
{
return getSource().contains(pointRelativeTo00.x, pointRelativeTo00.y);
}
}