OtsRenderable.java
package org.opentrafficsim.base.geometry;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.RenderingHints.Key;
import java.util.Map;
import java.util.WeakHashMap;
import org.djutils.draw.Oriented;
import org.djutils.draw.Transform2d;
import org.djutils.draw.bounds.Bounds2d;
import org.djutils.draw.point.Point2d;
import org.djutils.exceptions.Throw;
import nl.tudelft.simulation.dsol.animation.d2.Renderable2d;
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 OtsLocatable> extends Renderable2d<L>
{
/** */
private static final long serialVersionUID = 20240223L;
/** Standard rendering keys. */
public static Key[] RENDERING_KEYS = new Key[] {RenderingHints.KEY_ANTIALIASING};
/** Standard rendering values. */
public static Object[] RENDERING_VALUES = new Object[] {RenderingHints.VALUE_ANTIALIAS_ON};
/** Stored hints to reset. */
private static Map<Object, Object[]> OLD_RENDERING_HINTS = new WeakHashMap<>();
/**
* Constructs a new Renderable2d.
* @param source T; the source
* @param contextProvider Contextualized; the object that can provide the context to store the animation objects
*/
public OtsRenderable(final L source, final Contextualized contextProvider)
{
super(source, contextProvider);
}
/**
* Set standard rendering hints for this renderable to paint. The graphics should be reset using {@code resetRendering()}
* after painting.
* @param graphics Graphics2D; graphics.
*/
protected void setRendering(final Graphics2D graphics)
{
Object[] old = OLD_RENDERING_HINTS.computeIfAbsent(this, (o) -> new Object[RENDERING_KEYS.length]);
for (int i = 0; i < RENDERING_KEYS.length; i++)
{
old[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 Graphics2D; graphics.
*/
protected void resetRendering(final Graphics2D graphics)
{
Object[] old = OLD_RENDERING_HINTS.computeIfAbsent(this, (o) -> new Object[RENDERING_KEYS.length]);
Throw.when(old == null, IllegalStateException.class,
"Renderable %s resets rendering hints, but it never changed rendering hints with setRendering().", this);
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 (old[i] != null)
// {
graphics.setRenderingHint(RENDERING_KEYS[i], old[i]);
// }
}
}
/** {@inheritDoc} */
@Override
public boolean contains(final Point2d pointWorldCoordinates, final Bounds2d extent)
{
Transform2d transformation = toBoundsTransform(getSource().getLocation());
Point2d pointObjectCoordinates = transformation.transform(pointWorldCoordinates);
return getSource().getBounds().contains(pointObjectCoordinates);
}
/**
* Returns a transformation by which absolute coordinates can be translated and rotated to the frame of the possibly
* oriented location around which bounds are defined.
* @param location Point2d; location (can be an {@code Oriented}).
* @return Transform2d; transformation.
*/
public static Transform2d toBoundsTransform(final Point2d location)
{
Transform2d transformation = new Transform2d();
if (location instanceof Oriented<?>)
{
transformation.rotation(-((Oriented<?>) location).getDirZ());
}
transformation.translate(-location.getX(), -location.getY());
return transformation;
}
}