nl.tudelft.simulation.dsol.animation.d2.RenderableScale Maven / Gradle / Ivy
package nl.tudelft.simulation.dsol.animation.d2;
import java.awt.Dimension;
import java.awt.geom.Point2D;
import org.djutils.draw.bounds.Bounds2d;
import org.djutils.draw.point.Point;
import org.djutils.draw.point.Point2d;
import org.djutils.exceptions.Throw;
/**
* A helper class for transforming between screen coordinates and world coordinates. The x-axis and y-axis can be scaled
* independently from each other, causing expansion or compression of the y-axis relative to the x-axis.
*
* Copyright (c) 2020-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
* for project information DSOL Manual. The DSOL
* project is distributed under a three-clause BSD-style license, which can be found at
* DSOL License.
*
* @author Alexander Verbraeck
*/
public class RenderableScale
{
/**
* the Y-scale factor with respect to the X-scale; 1 means X and Y scales are the same; A number larger than 1 means the
* y-axis is compressed.
*/
private final double yScaleRatio;
/**
* the scale factor for the object, e.g., to get 1 pixel unit to equal 1 meter on a map. A scale factor smaller than 1 means
* that the object will be drawn smaller on the screen.
*/
private final double objectScaleFactor;
/**
* Construct a translator between world coordinates and screen coordinates that uses a different scale factor for x and y.
* @param yScaleRatio double; the Y-scale ratio with respect to the X-scale (when yScale < 1 it results in a condensed
* Y-axis, where yScale > 1 results in an expanded Y-axis
* @param objectScaleFactor double; the scale factor for the object, e.g., to get 1 pixel unit to equal 1 meter on a map. A
* scale factor smaller than 1 means that the object will be drawn smaller on the screen.
* @throws IllegalArgumentException when yScale <= 0
*/
public RenderableScale(final double yScaleRatio, final double objectScaleFactor)
{
Throw.when(yScaleRatio <= 0, IllegalArgumentException.class, "yScaleRatio should be larger than 0");
Throw.when(objectScaleFactor <= 0, IllegalArgumentException.class, "objectScaleFactor should be larger than 0");
this.yScaleRatio = yScaleRatio;
this.objectScaleFactor = objectScaleFactor;
}
/**
* Construct a translator between world coordinates and screen coordinates that uses a different scale factor for x and y.
* @param yScaleRatio double; the Y-scale ratio with respect to the X-scale (when yScale < 1 it results in a condensed
* Y-axis, where yScale > 1 results in an expanded Y-axis
* @throws IllegalArgumentException when yScale <= 0
*/
public RenderableScale(final double yScaleRatio)
{
this(yScaleRatio, 1.0);
}
/**
* Construct a translator between world coordinates and screen coordinates that uses an X/Y ratio of 1.
* @throws IllegalArgumentException when yScale <= 0
*/
public RenderableScale()
{
this(1.0, 1.0);
}
/**
* Returns the X-scale of a screen compared to an extent. If the height or the width of the screen are < 0 Double.NaN is
* returned.
* @param extent Rectangle2D; the extent of this animation
* @param screen Dimension; the screen dimensions
* @return double; the scale. Can return Double.NaN
*/
public double getXScale(final Bounds2d extent, final Dimension screen)
{
if (screen.getHeight() <= 0 || screen.getWidth() <= 0)
{
return Double.NaN;
}
return extent.getDeltaX() / screen.getWidth();
}
/**
* Returns the Y-scale of a screen compared to an extent. If the height or the width of the screen are < 0 Double.NaN is
* returned.
* @param extent Rectangle2D; the extent of this animation
* @param screen Dimension; the screen dimensions
* @return double; the scale. Can return Double.NaN
*/
public double getYScale(final Bounds2d extent, final Dimension screen)
{
if (screen.getHeight() <= 0 || screen.getWidth() <= 0)
{
return Double.NaN;
}
return extent.getDeltaY() / screen.getHeight();
}
/**
* computes the visible extent.
* @param extent Rectangle2D; the extent
* @param screen Dimension; the screen
* @return a new extent or null if parameters are null or screen is invalid (width / height <= 0)
*/
public Bounds2d computeVisibleExtent(final Bounds2d extent, final Dimension screen)
{
if (extent == null || screen == null || screen.getHeight() <= 0 || screen.getWidth() <= 0)
{
return null;
}
double xScale = extent.getDeltaX() / screen.getWidth();
double yScale = extent.getDeltaY() / (this.yScaleRatio * screen.getHeight());
Bounds2d result;
if (xScale < yScale)
{
result = new Bounds2d(extent.midPoint().getX() - 0.5 * screen.getWidth() * yScale,
extent.midPoint().getX() + 0.5 * screen.getWidth() * yScale, extent.getMinY(), extent.getMaxY());
}
else
{
result = new Bounds2d(extent.getMinX(), extent.getMaxX(),
extent.midPoint().getY() - 0.5 * screen.getHeight() * xScale * this.yScaleRatio,
extent.midPoint().getY() + 0.5 * screen.getHeight() * xScale * this.yScaleRatio);
}
return result;
}
/**
* returns the frame xy-coordinates of a point in world coordinates. If parameters are invalid (i.e. screen.size <= 0) a
* null value is returned. If parameter combinations (i.e !extent.contains(point)) are invalid a null value is returned.
* @param worldCoordinates Point<?, ?>; the world coordinates
* @param extent Bounds2d; the extent of this animation
* @param screen Dimension; the screen dimentsions
* @return Point2D (x,y) on screen. Can be null.
*/
public Point2D getScreenCoordinates(final Point> worldCoordinates, final Bounds2d extent, final Dimension screen)
{
double x = (worldCoordinates.getX() - extent.getMinX()) * (1.0 / getXScale(extent, screen));
double y = screen.getHeight() - (worldCoordinates.getY() - extent.getMinY()) * (1.0 / getYScale(extent, screen));
return new Point2D.Double(x, y);
}
/**
* returns the frame xy-coordinates of a point in screen coordinates. If parameters are invalid (i.e. screen.size < 0) a
* null value is returned. If parameter combinations (i.e !screen.contains(point)) are invalid a null value is returned.
* @param screenCoordinates Point2D; the screen coordinates
* @param extent Bounds2d; the extent of this animation
* @param screen Dimension; the screen dimensions
* @return Point2d (x,y) in the 2D or 3D world
*/
public Point2d getWorldCoordinates(final Point2D screenCoordinates, final Bounds2d extent, final Dimension screen)
{
double x = (screenCoordinates.getX()) * getXScale(extent, screen) + extent.getMinX();
double y = ((screen.getHeight() - screenCoordinates.getY())) * getYScale(extent, screen) + extent.getMinY();
return new Point2d(x, y);
}
/**
* Return the y-scale ratio. A number larger than 1 means the y-axis is compressed.
* @return double; the y-scale ratio. A number larger than 1 means the y-axis is compressed
*/
public double getYScaleRatio()
{
return this.yScaleRatio;
}
/**
* Return the scale factor for the object, e.g., to get 1 pixel unit to equal 1 meter on a map. A scale factor smaller than
* 1 means that the object will be drawn smaller on the screen.
* @return double; the scale factor for the object, e.g., to get 1 pixel unit to equal 1 meter on a map
*/
public double getObjectScaleFactor()
{
return this.objectScaleFactor;
}
}