All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aima.core.util.math.geom.shapes.Ellipse2D Maven / Gradle / Ivy

Go to download

AIMA-Java Core Algorithms from the book Artificial Intelligence a Modern Approach 3rd Ed.

The newest version!
package aima.core.util.math.geom.shapes;

import aima.core.util.Util;

/**
 * This class implements an ellipse in a two-dimensional Cartesian plot.
* As an ellipse can be rotated around its center it is represented by a center {@link Point2D} and two {@link Vector2D} that are the horizontal and vertical radius. * * @author Arno von Borries * @author Jan Phillip Kretzschmar * @author Andreas Walscheid * */ public class Ellipse2D implements IGeometric2D { private final Point2D center; private final Vector2D horizontal; private final double horizontalLength; private final Vector2D vertical; private final double verticalLength; private final double angle; private final TransformMatrix2D transform; private final TransformMatrix2D transformInverse; /** * @param center the center point of the ellipse. * @param rx the radius of the ellipse in X direction. * @param ry the radius of the ellipse in Y direction. */ public Ellipse2D(Point2D center, double rx, double ry) { this.center = center; this.horizontal = new Vector2D(rx,0.0d); this.horizontalLength = rx; this.vertical = new Vector2D(0.0d,ry); this.verticalLength = ry; this.angle = 0.0d; this.transform = TransformMatrix2D.UNITY_MATRIX; this.transformInverse = TransformMatrix2D.UNITY_MATRIX; } /** * @param center the center point of the ellipse. * @param horizontal the horizontal radius of the ellipse as a vector. * @param vertical the vertical radius of the ellipse as a vector. */ public Ellipse2D(Point2D center, Vector2D horizontal, Vector2D vertical) { this.center = center; if(Util.compareDoubles(horizontal.getX(),0.0d) && Util.compareDoubles(vertical.getY(),0.0d)) { this.horizontal = vertical; this.vertical = horizontal; } else { this.horizontal = horizontal; this.vertical = vertical; } this.horizontalLength = this.horizontal.length(); this.verticalLength = this.vertical.length(); if(Util.compareDoubles(this.horizontal.getY(),0.0d) && Util.compareDoubles(this.vertical.getX(),0.0d)) { this.angle = 0.0d; this.transform = TransformMatrix2D.UNITY_MATRIX; this.transformInverse = TransformMatrix2D.UNITY_MATRIX; } else { if(Util.compareDoubles(this.horizontal.getX(),0.0d)) { this.angle = Vector2D.Y_VECTOR.angleTo(this.vertical); } else { this.angle = Vector2D.X_VECTOR.angleTo(this.horizontal); } TransformMatrix2D result = TransformMatrix2D.translate(center.getX(), center.getY()); result = result.multiply(TransformMatrix2D.rotate(-this.angle)); this.transform = result.multiply(TransformMatrix2D.translate(-center.getX(), -center.getY())); this.transformInverse = this.transform.inverse(); } } /** * @return the center point of the ellipse. */ public Point2D getCenter() { return center; } /** * @return the length of the horizontal radius vector of the ellipse. */ public double getHorizontalLength() { return horizontalLength; } /** * @return the length of the vertical radius vector of the ellipse. */ public double getVerticalLength() { return verticalLength; } /** * @return the angle by which the ellipse is rotated. */ public double getAngle() { return angle; } @Override public Point2D randomPoint() { final double x = Util.generateRandomDoubleBetween(-horizontalLength,horizontalLength); final double delta = Math.abs(verticalLength*Math.sin(Math.acos(x/horizontalLength))); final double y = Util.generateRandomDoubleBetween(-delta, delta); return transformInverse.multiply(new Point2D(center.getX()+x,center.getY()+y)); } @Override public boolean isInside(Point2D point) { Point2D transformedPoint = transform.multiply(point); if(center.getX()-horizontalLength < transformedPoint.getX() && center.getX()+horizontalLength > transformedPoint.getX()) { final double delta = Math.abs(verticalLength*Math.sin(Math.acos((transformedPoint.getX() - center.getX())/horizontalLength))); return transformedPoint.getY() < center.getY() + delta && transformedPoint.getY() > center.getY() - delta; } else { return false; } } @Override public boolean isInsideBorder(Point2D point) { Point2D transformedPoint = transform.multiply(point); if((center.getX()-horizontalLength < transformedPoint.getX() || Util.compareDoubles(transformedPoint.getX(), center.getX()-horizontalLength)) && (center.getX()+horizontalLength > transformedPoint.getX() || Util.compareDoubles(transformedPoint.getX(), center.getX()+horizontalLength))) { final double delta = Math.abs(verticalLength*Math.sin(Math.acos((transformedPoint.getX() - center.getX())/horizontalLength))); return (transformedPoint.getY() < center.getY() + delta || Util.compareDoubles(transformedPoint.getY(), center.getY() + delta)) && (transformedPoint.getY() >= center.getY() - delta || Util.compareDoubles(transformedPoint.getY(), center.getY() - delta)); } return false; } @Override public double rayCast(Ray2D ray) { final Ray2D transformedRay = ray.transform(transform); final double squaredHorizontal = horizontalLength*horizontalLength; final double squaredVertical = verticalLength*verticalLength; final double squaredDirectionX = transformedRay.getDirection().getX()*transformedRay.getDirection().getX(); final double divisor = squaredHorizontal*transformedRay.getDirection().getY()*transformedRay.getDirection().getY()+squaredVertical*squaredDirectionX; if(Util.compareDoubles(divisor,0.0d)) return Double.POSITIVE_INFINITY; final double squareRoot = Math.sqrt(squaredHorizontal*squaredVertical*(squaredDirectionX*(squaredVertical-center.getY()*center.getY()+transformedRay.getStart().getY()*(2*center.getY()-transformedRay.getStart().getY()))+transformedRay.getDirection().getY()*(transformedRay.getDirection().getY()*(squaredHorizontal-center.getX()*center.getX()+transformedRay.getStart().getX()*(2*center.getX()-transformedRay.getStart().getX()))+2*transformedRay.getDirection().getX()*(transformedRay.getStart().getX()*(transformedRay.getStart().getY()-center.getY())+center.getX()*(center.getY()-transformedRay.getStart().getY()))))); if(Util.compareDoubles(squareRoot,Double.NaN)) return Double.POSITIVE_INFINITY; final double tmpFactor1 = squaredVertical*transformedRay.getDirection().getX(); final double tmpFactor2 = squaredHorizontal*transformedRay.getDirection().getY(); final double factors = tmpFactor1*center.getX() - tmpFactor1*transformedRay.getStart().getX() + tmpFactor2*center.getY() - tmpFactor2*transformedRay.getStart().getY(); final double result = (factors-Math.abs(squareRoot))/divisor; if(result >= 0) { final Point2D intersection = transformedRay.getStart().add(transformedRay.getDirection().multiply(result)); return transformInverse.multiply(intersection).vec(ray.getStart()).length(); } return Double.POSITIVE_INFINITY; } @Override public Rect2D getBounds() { final double cosAngle = Math.cos(angle); final double sinAngle = Math.sin(angle); final double boundX = Math.sqrt(horizontalLength*horizontalLength*cosAngle*cosAngle+verticalLength*verticalLength*sinAngle*sinAngle); final double boundY = Math.sqrt(horizontalLength*horizontalLength*sinAngle*sinAngle+verticalLength*verticalLength*cosAngle*cosAngle); return new Rect2D(center.getX()-boundX, center.getY()-boundY, center.getX()+boundX, center.getY()+boundY); } @Override public IGeometric2D transform(TransformMatrix2D matrix) { final Point2D centerNew = matrix.multiply(center); final Vector2D horizontalNew = centerNew.vec(matrix.multiply(center.add(horizontal))), verticalNew = centerNew.vec(matrix.multiply(center.add(vertical))); final double radiusHorizontalNew = horizontalNew.length(); final double radiusVerticalNew = verticalNew.length(); if(Util.compareDoubles(radiusHorizontalNew,radiusVerticalNew)) { //Transform let this ellipse become a circle. return new Circle2D(centerNew,radiusHorizontalNew); } return new Ellipse2D(centerNew,horizontalNew,verticalNew); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy