aima.core.util.math.geom.CartesianPlot2D Maven / Gradle / Ivy
package aima.core.util.math.geom;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import aima.core.util.Util;
import aima.core.util.math.geom.shapes.IGeometric2D;
import aima.core.util.math.geom.shapes.Point2D;
import aima.core.util.math.geom.shapes.Ray2D;
import aima.core.util.math.geom.shapes.Rect2D;
/**
* This class is a simple implementation of a Cartesian plot.
* It uses a {@link IGroupParser} that generates a set of {@link IGeometric2D}.
*
* @author Arno von Borries
* @author Jan Phillip Kretzschmar
* @author Andreas Walscheid
*
*/
public class CartesianPlot2D {
private double rayRange;
private IGroupParser parser;
private ArrayList shapes;
private ArrayList boundaries;
/**
* @param parser a file parser which implements {@link IGroupParser}. This parser is used to load a map file.
*/
public CartesianPlot2D(IGroupParser parser) {
this.parser = parser;
}
/**
* Sets the maximum length of a rayCast after which {@code Double.POSITIVE_INFINITY} may be returned.
* @param rayRange the maximum non zero positive length, that the ray can reliably have. This parameter is used to speed up {@code rayCast}.
* Set this to {@code Double.POSITIVE_INFINITY} if you like waiting.
*/
public void setRayRange(double rayRange) {
this.rayRange = rayRange;
}
/**
* Sets the set of shapes of the plot.
* @param shapes the set of shapes to be set.
*/
public void setShapes(ArrayList shapes) {
this.shapes = shapes;
boundaries = new ArrayList(shapes.size());
for(IGeometric2D shape: shapes) {
boundaries.add(shape.getBounds());
}
}
/**
* Loads a map input into this Cartesian plot.
* @param input the stream containing the data.
* @param groupID the identification for the group of elements that will be loaded.
* @throws Exception thrown if the input does not contain the group.
* @throws Exception thrown by the parser when it encounters an error in the input.
*/
public void loadMap(InputStream input,String groupID) throws Exception {
shapes = parser.parse(input, groupID);
boundaries = new ArrayList(shapes.size());
for(IGeometric2D shape: shapes) {
boundaries.add(shape.getBounds());
}
}
/**
* @return an iterator over the shapes.
*/
public Iterator getShapes() {
return shapes.iterator();
}
/**
* @return an iterator over the boundaries of the shapes.
*/
public Iterator getBoundaries() {
return boundaries.iterator();
}
/**
* Checks whether the plot does not contain any elements.
* @return true if the plot contains no element.
*/
public boolean isEmpty() {
return shapes == null ? true : shapes.isEmpty();
}
/**
* Calculates a random point that is somewhere inside one of the shapes of the plot or on a border of a shape.
* @return a point on a shape within the plot.
*/
public Point2D randomPoint() {
IGeometric2D shape = Util.selectRandomlyFromList(shapes);
return shape.randomPoint();
}
/**
* Calculates the length of a ray until it intersects with a shape.
* As this class uses the set maximum ray range to speed up this ray casting,
* this function may return {@code Double.POSITIVE_INFINITY} for a ray that intersects with an obstacle that is further away than the rayRange from the given starting point.
* @param ray the ray to be used for ray casting.
* @return the length of the ray.
*/
public double rayCast(Ray2D ray) {
double result = Double.POSITIVE_INFINITY;
Rect2D rayBounding = new Rect2D(ray.getStart().getX() - rayRange,ray.getStart().getY() - rayRange, ray.getStart().getX() + rayRange, ray.getStart().getY() + rayRange);
for(int i=0;i < shapes.size(); i++) {
Rect2D bounding = boundaries.get(i);
if( rayBounding.isInsideBorder(bounding.getLowerLeft()) ||
rayBounding.isInsideBorder(bounding.getUpperLeft()) ||
rayBounding.isInsideBorder(bounding.getLowerRight()) ||
rayBounding.isInsideBorder(bounding.getUpperRight())) {
final double tmp = shapes.get(i).rayCast(ray);
result = tmp < result ? tmp : result;
}
}
return result;
}
/**
* Checks whether the given point is on any of the shapes of the plot.
* @param point the point to be tested.
* @return true if the point is on any of the shapes.
*/
public boolean isPointInsideBorderShape(Point2D point) {
for(int i=0;i < shapes.size();i++) {
if(boundaries.get(i).isInsideBorder(point)) {
if(shapes.get(i).isInsideBorder(point)) {
return true;
}
}
}
return false;
}
/**
* Checks whether the given point is inside any of the shapes of the plot.
* @param point the point to be tested.
* @return true if the point is in any of the shapes (excluding their borders).
*/
public boolean isPointInsideShape(Point2D point) {
for(int i=0;i < shapes.size();i++) {
if(boundaries.get(i).isInside(point)) {
if(shapes.get(i).isInside(point)) {
return true;
}
}
}
return false;
}
}