OtsLocatable.java
package org.opentrafficsim.base.geometry;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.djutils.draw.Oriented;
import org.djutils.draw.Transform2d;
import org.djutils.draw.bounds.Bounds2d;
import org.djutils.draw.line.PolyLine2d;
import org.djutils.draw.line.Polygon2d;
import org.djutils.draw.point.Point2d;
import nl.tudelft.simulation.dsol.animation.Locatable;
/**
* Locatable that specifies return types.
* <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>
*/
public interface OtsLocatable extends Locatable, SpatialObject
{
@Override
Point2d getLocation();
/**
* Returns the bounds relative to the location.
* @return bounds relative to the location.
*/
@Override
Bounds2d getBounds();
/**
* Returns the shape relative to the location.
* @return the shape relative to the location.
*/
default OtsShape getShape()
{
return new PolygonShape(relativeContour(this));
}
/*
* The following methods that take an OtsLocatable as input are defined not as default but as static, as these methods
* should not be exposed as functions of an OtsLocatable.
*/
/**
* Generates a polygon as contour based on bounds and location of the locatable.
* @param locatable locatable
* @return contour
*/
static Polygon2d boundsAsContour(final OtsLocatable locatable)
{
List<Point2d> points = new ArrayList<>();
Bounds2d bounds = locatable.getBounds();
points.add(new Point2d(bounds.getMaxX(), bounds.getMaxY()));
points.add(new Point2d(bounds.getMinX(), bounds.getMaxY()));
points.add(new Point2d(bounds.getMinX(), bounds.getMinY()));
points.add(new Point2d(bounds.getMaxX(), bounds.getMinY()));
return new Polygon2d(transform(toContourTransform(locatable.getLocation()), points.iterator()));
}
/**
* Generates bounds based on polygon and location of the locatable.
* @param locatable locatable
* @return bounds
*/
static Bounds2d contourAsBounds(final OtsLocatable locatable)
{
return relativeContour(locatable).getBounds();
}
/**
* Returns the contour of the locatable translated and rotated such that it is defined relative to the location.
* @param locatable locatable
* @return contour relative to location
*/
static Polygon2d relativeContour(final OtsLocatable locatable)
{
return transformContour(locatable.getContour(), locatable.getLocation());
}
/**
* Transform the contour from the location to relative coordinates, which may also be an {@code OrientedPoint} for rotation.
* @param contour contour
* @param location location, which may also be an {@code OrientedPoint} for rotation
* @return transformed contour
*/
static Polygon2d transformContour(final Polygon2d contour, final Point2d location)
{
return new Polygon2d(transform(toBoundsTransform(location), contour.getPoints()));
}
/**
* Transform the line by location, which may also be an {@code OrientedPoint} for rotation.
* @param line line
* @param location location, which may also be an {@code OrientedPoint} for rotation
* @return transformed line
*/
static PolyLine2d transformLine(final PolyLine2d line, final Point2d location)
{
return new PolyLine2d(transform(toBoundsTransform(location), line.getPoints()));
}
/**
* Translates and possibly rotates points by location.
* @param transformation transformation
* @param itSource points
* @return translated and possibly rotated contour
*/
private static List<Point2d> transform(final Transform2d transformation, final Iterator<Point2d> itSource)
{
Point2d prev = null;
List<Point2d> points = new ArrayList<>();
while (itSource.hasNext())
{
Point2d next = transformation.transform(itSource.next());
if (!next.equals(prev))
{
points.add(next);
}
prev = next;
}
return points;
}
/**
* 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 location (can be an {@code Oriented}).
* @return transformation.
*/
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;
}
/**
* Returns a transformation by which relative coordinates can be translated and rotated to the frame of the possibly
* oriented location around which bounds are defined.
* @param location location (can be an {@code Oriented}).
* @return transformation.
*/
static Transform2d toContourTransform(final Point2d location)
{
Transform2d transformation = new Transform2d();
transformation.translate(location.getX(), location.getY());
if (location instanceof Oriented<?>)
{
transformation.rotation(((Oriented<?>) location).getDirZ());
}
return transformation;
}
}