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

com.googlecode.blaisemath.plane.PlanarMathUtils Maven / Gradle / Ivy

The newest version!
/**
 * PlanarMathUtils.java
 * Created on Jul 29, 2009
 */
package com.googlecode.blaisemath.plane;

/*
 * #%L
 * BlaiseMath
 * --
 * Copyright (C) 2009 - 2015 Elisha Peterson
 * --
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import java.awt.geom.Point2D;
import static java.lang.Math.*;

/**
 * 

* PlanarMathUtils is a library of static methods for use on elements of the plane. * All methods work with Point2D.Doubles. *

*

* This library is built to handle a "circle" of points at infinity. These are represented by a Double.POSITIVE_INFINITY * x-coordinate, and a y-coordinate representing the infinite angle. *

* * @author Elisha Peterson */ public final class PlanarMathUtils { /** The origin. */ public static final Point2D.Double ZERO = new Point2D.Double(); /** The point at infinity. */ public static final Point2D.Double INFINITY = new Point2D.Double(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); /** A non-existent point. */ public static final Point2D.Double NO_POINT = new Point2D.Double(Double.NaN, Double.NaN); /** Non-instantiable class. */ private PlanarMathUtils() {} //================================================================================================== // // 0. TEST METHODS // /** * Determines whether provided point is infinite. * @param point point to check * @return true if this represents an infinite point. */ public static boolean isInfinite(Point2D.Double point) { return Double.isInfinite(point.x); } /** * Determines whether provided point is valid. * @param point point to check * @return false if the point is valid (either infinite or finite) */ public static boolean isValidPoint(Point2D.Double point) { return ! (Double.isNaN(point.x) || Double.isNaN(point.y) || Double.isInfinite(point.y)); } //================================================================================================== // // 1a. METHODS TO CONSTRUCT POINTS // /** * Creates a point-at-infinity using a specified angle. * @param angle the angle to palce the point at * @return a point at infinity, represented in polar terms */ public static Point2D.Double polarPointAtInfinity(double angle) { return new Point2D.Double(Double.POSITIVE_INFINITY, angle); } //================================================================================================== // // 1b. METHODS TO CONVERT POINTS BETWEEN VARIOUS FORMATS // /** * Creates and returns a new point with specified radius and angle * @param radius the radius (may be negative) * @param angle the angle * @return a new point with specified radius and angle */ public static Point2D.Double toCartesianFromPolar(double radius, double angle) { return new Point2D.Double(radius * Math.cos(angle), radius * Math.sin(angle)); } /** * Converts a Cartesian-specified point to a polar-specified point. * @param point the Cartesian-specified point * @return a point whose x is the radius, y is the angle (from x-axis) of the point */ public static Point2D.Double toPolarFromCartesian(Point2D.Double point) { return new Point2D.Double(point.distance(0,0), angle(point)); } //================================================================================================== // // 2. METHODS OPERATING ON A POINT AND RETURNING THAT POINT // /** * Normalizes the input vector, by dividing by the magnitude, and returns a copy of the vector. * @param vector the input vector * @return normalization of the vector, i.e. unit vector with the same heading as this vector; * returns the vector if magnitude is 0. */ public static Point2D.Double normalize(Point2D.Double vector) { double magn = magnitude(vector); if (magn == 0) return vector; vector.x /= magn; vector.y /= magn; return vector; } /** * Translates first point by amount of the second. Returns the first point. * @param point the first point to translate (value will change) * @param dPoint the amount to translate by * @return */ public static Point2D.Double translate(Point2D.Double point, Point2D.Double dPoint) { point.x += dPoint.x; point.y += dPoint.y; return point; } //================================================================================================== // // 3. METHODS TO CREATE POINTS BASED ON EXISTING POINTS // /** * Adds a collection of points, and returns the result. * @param points the points to add together * @return sum of all the points */ public static Point2D.Double sum(Point2D.Double... points) { Point2D.Double result = new Point2D.Double(); for (int i = 0; i < points.length; i++) { result.x += points[i].x; result.y += points[i].y; } return result; } /** * Returns a point that is a copy of the provided point rotated about the origin by a specified amount. * @param point the input point * @param dAngle the amount of angle change * @return a new point */ public static Point2D.Double rotate(Point2D.Double point, double dAngle) { double newX = cos(dAngle) * point.x - sin(dAngle) * point.y; double newY = sin(dAngle) * point.x + cos(dAngle) * point.y; return new Point2D.Double(newX, newY); } /** * Returns a point that is a copy of the provided point rotated about the specified anchor by a specified amount. * @param anchor the anchor point for the rotation * @param point the input point * @param dAngle the amount of (counter-clockwise) angle change * @return a new point */ public static Point2D.Double rotate(Point2D.Double anchor, Point2D.Double point, double dAngle) { double newX = anchor.x + cos(dAngle) * (point.x - anchor.x) - sin(dAngle) * (point.y - anchor.y); double newY = anchor.y + sin(dAngle) * (point.x - anchor.x) + cos(dAngle) * (point.y - anchor.y); return new Point2D.Double(newX, newY); } //================================================================================================== // // 4a. METHODS THAT RETURN dot/cross product PROPERTIES OF ONE OR MORE POINTS // /** * Computes dot product of two vectors * @param v1 first vector * @param v2 second vector * @return value of dot product */ public static double dotProduct(Point2D.Double v1, Point2D.Double v2) { return v1.x * v2.x + v1.y * v2.y; } /** * Computes magnitude of cross product of two planar vectors. * @param v1 first vector * @param v2 second vector * @return cross product (signed magnitude only) */ public static double crossProduct(Point2D.Double v1, Point2D.Double v2) { return v1.x * v2.y - v1.y * v2.x; } /** * Computes signed magnitude of cross product of planar vectors (p2-p1) and (p3-p1) * @param p1 first point * @param p2 second point * @param p3 third point * @return cross product double */ public static double crossProductOf3Points(Point2D.Double p1, Point2D.Double p2, Point2D.Double p3) { return (p2.x - p1.x) * (p3.y - p2.y) - (p2.y - p1.y) * (p3.x - p2.x); } //================================================================================================== // // 4b. METHODS THAT RETURN angle/magnitude PROPERTIES OF ONE OR MORE POINTS // /** * Computes magnitude of a vector. * @param vec the vector * @return magnitude */ public static double magnitude(Point2D.Double vec) { return vec.distance(0, 0); } /** * Computes "angle" of a vector using the Math.atan method. * @param vec the vector * @return angle in the range of -pi to pi, as computed by Math.atan */ public static double angle(Point2D.Double vec) { if (Double.isInfinite(vec.x)) { return vec.y > PI ? vec.y - 2*PI : vec.y; } return atan2(vec.y, vec.x); } /** * Computes angle formed by vector with the x-axis. * @param x the x coord * @param y the y coord * @return an angle with range between 0 and 2pi */ public static double positiveAngle(double x, double y) { double result = atan2(y, x); return result < 0 ? result + 2*PI : result; } /** * Computes angle made by given vector with the x-axis * @param vec a vector * @return an angle with range between 0 and 2pi */ public static double positiveAngle(Point2D.Double vec) { if (Double.isInfinite(vec.x)) return vec.y < 0 ? vec.y + 2*PI : vec.y; return positiveAngle(vec.x, vec.y); } /** * Computes angle between two vectors, as comptued by the dot product formula * @param vec1 first vector * @param vec2 second vector * @return angle in the range of 0 to pi. */ public static double angleBetween(Point2D.Double vec1, Point2D.Double vec2) { return Math.acos( dotProduct(vec1, vec2) / ( magnitude(vec1) * magnitude(vec2) ) ); } /** * Computes (signed) angle between two vectors, in the range of -pi to pi * @param vec1 first vector * @param vec2 second vector * @return angle of vec2 relative to vec1, in the range of -pi to pi */ public static double signedAngleBetween(Point2D.Double vec1, Point2D.Double vec2) { double angle1 = atan2(vec1.y, vec1.x); double angle2 = atan2(vec2.y, vec2.x); double diff = angle2 - angle1; if (diff > PI) return diff - 2*PI; else if (diff < -PI) return diff + 2*PI; else return diff; } /** * Computes angle between three points. In particular, this is the angle required to rotate * the vector [p2->p1] to the point where it is parallel to the vector [p2->p3]. * Allows for points at infinity of the form (infinity, angle) * @param p1 first point * @param p2 second point * @param p3 third point * @return the rotation angle, between 0 and 2pi; or NaN if any of the points coincide */ public static double positiveAngleBetween3Points(Point2D.Double p1, Point2D.Double p2, Point2D.Double p3) { if (isInfinite(p2)) { return 0; } double angleDiff = (isInfinite(p3) ? p3.y : positiveAngle(p3.x - p2.x, p3.y - p2.y)) - (isInfinite(p1) ? p1.y : positiveAngle(p1.x - p2.x, p1.y - p2.y)); if (angleDiff < 0) { angleDiff += 2*Math.PI; } return angleDiff; } //================================================================================================== // // 5. PROJECTION FORMULAS // /** * Computes scalar projection of first vector onto second vector. * @param projVec vector that will be projected * @param dir vector to project onto * @return length of component of projVec in the direction of dir */ public static double scalarProjection(Point2D.Double projVec, Point2D.Double dir) { return dotProduct(projVec, dir) / magnitude(dir); } /** * Computes projection of first vector onto second vector. * @param projVec vector that will be projected * @param dir vector to project onto * @return vector in the direction of dir */ public static Point2D.Double vectorProjection(Point2D.Double projVec, Point2D.Double dir) { double factor = dotProduct(projVec, dir) / pow(magnitude(dir), 2); return new Point2D.Double(dir.x * factor, dir.y * factor); } /** * Computes component of first vector that is perpendicular to the second vector * @param projVec first vector * @param dir vector to project orthogonally to * @return vector component that is perpendicular to dir */ public static double perpendicularScalarProjection(Point2D.Double projVec, Point2D.Double dir) { return Math.sqrt(projVec.distanceSq(0,0) - pow(scalarProjection(projVec,dir), 2)); } /** * Computes component of first vector that is perpendicular to the second vector * @param projVec first vector * @param dir vector to project orthogonally to * @return vector component that is perpendicular to dir */ public static Point2D.Double perpendicularVectorProjection(Point2D.Double projVec, Point2D.Double dir) { Point2D.Double parallel = vectorProjection(projVec, dir); return new Point2D.Double(projVec.x - parallel.x, projVec.y - parallel.y); } //================================================================================================== // // 6. DISTANCE FORMULAS // private static double t(Point2D.Double original, Point2D.Double endpt1, Point2D.Double endpt2) { return ((endpt1.x - original.x) * (endpt1.x - endpt2.x) + (endpt1.y - original.y) * (endpt1.y - endpt2.y)) / endpt1.distanceSq(endpt2); } /** * Computes and returns point along the line between point1 and point2 which is closest * to itself, aka. the point which makes a perpendicular with the line. * Mathematically this can be computed by minimizing the distanceTo between * (x,y) and p1+t*(p2-p1) as t varies. Mathematically, the result is * * (p1-p).(p1-p2) * t= -------------- * ||p1-p2||^2 * * @param original the point considered * @param endpt1 first endpoint of the line * @param endpt2 second endpoint of the line * @return point along the line that forms a perpendicular with the original point; or if the endpoints coincide a point at an endpoint **/ public static Point2D.Double closestPointOnLine(Point2D.Double original, Point2D.Double endpt1, Point2D.Double endpt2) { if (endpt1.equals(endpt2)) return new Point2D.Double(endpt1.x, endpt1.y); double t = t(original, endpt1, endpt2); return new Point2D.Double(endpt1.x + t * (endpt2.x - endpt1.x), endpt1.y + t * (endpt2.y - endpt1.y)); } /** * Returns point on RAY from endpt1 to endpt2 which is closest to this point. * @param original the point considered * @param endpt1 first endpoint of the ray * @param endpt2 second endpoint of the ray * @return point along the line that forms a perpendicular with the original point, or the nearest endpt to that point; or if the endpoints coincide a point at an endpoint */ public static Point2D.Double closestPointOnRay(Point2D.Double original, Point2D.Double endpt1, Point2D.Double endpt2) { if (endpt1.equals(endpt2)) { return new Point2D.Double(endpt1.x, endpt1.y); } double t = t(original, endpt1, endpt2); t = t < 0 ? 0 : t; return new Point2D.Double(endpt1.x + t * (endpt2.x - endpt1.x), endpt1.y + t * (endpt2.y - endpt1.y)); } /** * Returns point on line SEGMENT between point1 and point2 which is closest to this point. * @param original the point considered * @param endpt1 first endpoint of the line segment * @param endpt2 second endpoint of the line segment * @return point along the line that forms a perpendicular with the original point, or the nearest endpt to that point; or if the endpoints coincide a point at an endpoint */ public static Point2D.Double closestPointOnSegment(Point2D.Double original, Point2D.Double endpt1, Point2D.Double endpt2) { if (endpt1.equals(endpt2)) { return new Point2D.Double(endpt1.x, endpt1.y); } double t = t(original, endpt1, endpt2); t = t < 0 ? 0 : (t > 1 ? 1 : t); return new Point2D.Double(endpt1.x + t * (endpt2.x - endpt1.x), endpt1.y + t * (endpt2.y - endpt1.y)); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy