/*
* Copyright (C) 2013 Google Inc.
*
* 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.
*/
package interactivespaces.util.geometry;
import interactivespaces.util.math.MathUtils;
import java.util.Collection;
/**
* A 2D vector.
*
* @author Keith M. Hughes
*/
public class Vector2 {
/**
* Linearly interpolate along the line between {@code vector0} and {@code vector1}.
*
* <ul>
* <li>If {@code amount} is {@code 0}, the value will be {@code vector0}.</li>
* <li>If {@code amount} is {@code 1}, the value will be {@code vector1}.</li>
* </ul>
*
* @param vector0
* the origin point
* @param vector1
* the direction point
* @param amount
* the percentage between the origin and the direction
* @param answer
* where to place the answer
*
* @return the answer
*/
public static Vector2 linearInterpolate(Vector2 vector0, Vector2 vector1, double amount, Vector2 answer) {
answer.v0 = MathUtils.linearInterpolate(vector0.v0, vector1.v0, amount);
answer.v1 = MathUtils.linearInterpolate(vector0.v1, vector1.v1, amount);
return answer;
}
/**
* Linearly interpolate along the line between {@code vector0} and {@code vector1}.
*
* <ul>
* <li>If {@code amount} is {@code 0}, the value will be {@code vector0}.</li>
* <li>If {@code amount} is {@code 1}, the value will be {@code vector1}.</li>
* </ul>
*
* @param vector0
* the origin point
* @param vector1
* the direction point
* @param amount
* the percentage between the origin and the direction
*
* @return a new vector containing the answer
*/
public static Vector2 linearInterpolate(Vector2 vector0, Vector2 vector1, double amount) {
return linearInterpolate(vector0, vector1, amount, new Vector2());
}
/**
* Calculate the center point of a collection of vectors.
*
* <p>
* This is calculated as the average of each component.
*
* @param vectors
* the collection of vectors
* @param answer
* the vector in which to place the answer
*
* @return the answer
*/
public static Vector2 calculateVectorsCenter(Collection<Vector2> vectors, Vector2 answer) {
double sumV0 = 0.0;
double sumV1 = 0.0;
for (Vector2 vector : vectors) {
sumV0 += vector.v0;
sumV1 += vector.v1;
}
answer.set(sumV0 / vectors.size(), sumV1 / vectors.size());
return answer;
}
/**
* Find the point on the line nearest to the given point.
*
* @param linePoint0
* one of the points on the line
* @param linePoint1
* the other point on the line
* @param point
* the given point
*
* @return the answer in a newly created vector
*/
public static Vector2 nearestPointOnLine(Vector2 linePoint0, Vector2 linePoint1, Vector2 point) {
return nearestPointOnLine(linePoint0, linePoint1, point, new Vector2());
}
/**
* Find the point on the line nearest to the given point.
*
* @param linePoint0
* one of the points on the line
* @param linePoint1
* the other point on the line
* @param point
* the given point
* @param answer
* where the answer will be stored
*
* @return the answer
*
* @throws ArithmeticException
* the line points are the same, so no line is determined
*/
public static Vector2 nearestPointOnLine(Vector2 linePoint0, Vector2 linePoint1, Vector2 point, Vector2 answer)
throws ArithmeticException {
// The algorithm here is simple. We want the intersection of the line
// perpendicular to the original line which contains the target point of
// interest. This intersection will be the point on the line closest to the
// target point.
// Take the two line points and figure out the
// equation of the form a x + b y + c = 0.
// This is done by taking the two line points, putting them in homogenuous
// coordinates (x, y, 1) and taking their cross product.
double dx = linePoint1.v0 - linePoint0.v0;
double dy = linePoint1.v1 - linePoint0.v1;
if (dx == 0.0 && dy == 0.0) {
throw new ArithmeticException("Line points are the same");
}
// a = -dy
// b = dx
double c = linePoint0.v0 * linePoint1.v1 - linePoint0.v1 * linePoint1.v0;
// Now we need a line that is perpendicular to the above line.
// This line will be -b x + a y + cp = 0, or
// dx x + dy y + cp = 0.
double cp = -dx * point.v0 - dy * point.v1;
// If we have two lines of the form a x + b y + c = 0, we can treat them
// as 3 vectors (a,b,c). If we take their cross product, we will have their
// intersection point.
// The point on the line closest will be the intersection of the original
// line with the perpendicular
// After the cross product we will have
double w = -dx * dx - dy * dy;
answer.v0 = (dx * cp - dy * c) / w;
answer.v1 = (dx * c + dy * cp) / w;
return answer;
}
/**
* Find the point on the line nearest to the given point.
*
* @param line0Point0
* one of the points on the first line
* @param line0Point1
* the other point on the first line
* @param line1Point0
* one of the points on the second line
* @param line1Point1
* the other point on the second line
*
* @return the answer in a newly created vector
*
* @throws ArithmeticException
* the lines don't intersect
*/
public static Vector2 intersectLines(Vector2 line0Point0, Vector2 line0Point1, Vector2 line1Point0,
Vector2 line1Point1) {
return intersectLines(line0Point0, line0Point1, line1Point0, line1Point1, new Vector2());
}
/**
* Find the point on the line nearest to the given point.
*
* @param line0Point0
* one of the points on the first line
* @param line0Point1
* the other point on the first line
* @param line1Point0
* one of the points on the second line
* @param line1Point1
* the other point on the second line
* @param answer
* where the answer will be stored
*
* @return the answer
*
* @throws ArithmeticException
* the lines don't intersect
*/
public static Vector2 intersectLines(Vector2 line0Point0, Vector2 line0Point1, Vector2 line1Point0,
Vector2 line1Point1, Vector2 answer) throws ArithmeticException {
// The algorithm here is simple. Calculate the lines in the form
// a x + b y + c = 0 for both lines.
// Treat this then as a 3 vector (a,b,c).
// Take the cross product of these two vectors.
// The result is the intersection point in homogeneous coordinates.
// Take the two line points and figure out the
// equation of the form a x + b y + c = 0.
// This is done by taking the two line points, putting them in homogenuous
// coordinates (x, y, 1) and taking their cross product.
double a0 = line0Point0.v1 - line0Point1.v1;
double b0 = line0Point1.v0 - line0Point0.v0;
double c0 = line0Point0.v0 * line0Point1.v1 - line0Point0.v1 * line0Point1.v0;
double a1 = line1Point0.v1 - line1Point1.v1;
double b1 = line1Point1.v0 - line1Point0.v0;
double c1 = line1Point0.v0 * line1Point1.v1 - line1Point0.v1 * line1Point1.v0;
double w = a0 * b1 - a1 * b0;
if (w == 0) {
throw new ArithmeticException("The lines are parallel");
}
answer.v0 = (b0 * c1 - b1 * c0) / w;
answer.v1 = (a1 * c0 - a0 * c1) / w;
return answer;
}
/**
* The first component of the vector.
*/
double v0;
/**
* The second component of the vector.
*/
double v1;
/**
* Construct a new vector with components all equal to 0.
*/
public Vector2() {
this(0.0, 0.0);
}
/**
* Construct a new vector with the supplied components.
*
* @param v0
* the first component
* @param v1
* the second component
*/
public Vector2(double v0, double v1) {
this.v0 = v0;
this.v1 = v1;
}
/**
* Construct a vector whose components are the same as the supplied vector.
*
* @param v
* the supplied vector
*/
public Vector2(Vector2 v) {
this.v0 = v.v0;
this.v1 = v.v1;
}
/**
* Get the first component of the vector.
*
* @return first component
*/
public double getV0() {
return v0;
}
/**
* Set the first component of the vector.
*
* @param v
* the new component value
*
* @return this vector
*/
public Vector2 setV0(double v) {
this.v0 = v;
return this;
}
/**
* Get the second component of the vector.
*
* @return the second component
*/
public double getV1() {
return v1;
}
/**
* Set the second component of the vector.
*
* @param v
* the new component value
*
* @return this vector
*/
public Vector2 setV1(double v) {
this.v1 = v;
return this;
}
/**
* Set the components of the vector.
*
* @param v0
* the first component value
* @param v1
* the second component value
*
* @return this vector
*/
public Vector2 set(double v0, double v1) {
this.v0 = v0;
this.v1 = v1;
return this;
}
/**
* Set the components of the vector.
*
* @param v
* the vector to get the components from
*
* @return this vector
*/
public Vector2 set(Vector2 v) {
return set(v.v0, v.v1);
}
/**
* Normalize the current vector.
*
* @return a new vector with the normalized components
*/
public Vector2 normalize() {
return new Vector2(this).normalizeSelf();
}
/**
* Normalize the current vector.
*
* @return this vector with the normalized components
*/
public Vector2 normalizeSelf() {
double length = getLength();
v0 /= length;
v1 /= length;
return this;
}
/**
* Get the length of the vector.
*
* @return the length of the vector
*/
public double getLength() {
return Math.sqrt(v0 * v0 + v1 * v1);
}
/**
* Add the supplied vector to this vector.
*
* @param v
* the supplied vector
*
* @return a new vector with components are the result of the addition
*/
public Vector2 add(Vector2 v) {
return new Vector2(this).addSelf(v);
}
/**
* Add the supplied vector to this vector.
*
* @param v
* the supplied vector
*
* @return this vector with components are the result of the addition
*/
public Vector2 addSelf(Vector2 v) {
v0 += v.v0;
v1 += v.v1;
return this;
}
/**
* Subtract the supplied vector from this vector.
*
* @param v
* the supplied vector
*
* @return a new vector with components are the result of the subtraction
*/
public Vector2 subtract(Vector2 v) {
return new Vector2(this).subtractSelf(v);
}
/**
* Subtract the supplied vector from this vector.
*
* @param v
* the supplied vector
*
* @return this vector with components are the result of the subtraction
*/
public Vector2 subtractSelf(Vector2 v) {
v0 -= v.v0;
v1 -= v.v1;
return this;
}
/**
* Get a vector which has every component multiplied by a factor.
*
* @param factor
* the factor
*
* @return a new vector with components that are scaled
*/
public Vector2 scale(double factor) {
return new Vector2(this).scaleSelf(factor);
}
/**
* Get a vector which has every component multiplied by a factor.
*
* @param factor
* the factor
*
* @return this vector with components that are scaled
*/
public Vector2 scaleSelf(double factor) {
v0 *= factor;
v1 *= factor;
return this;
}
/**
* Get a vector which has every component multiplied by a factor.
*
* @param factor0
* the factor for the first component
* @param factor1
* the factor for the second component
*
* @return a new vector with components that are scaled
*/
public Vector2 scale(double factor0, double factor1) {
return new Vector2(this).scaleSelf(factor0, factor1);
}
/**
* Get a vector which has every component multiplied by a factor.
*
* @param factor0
* the factor for the first component
* @param factor1
* the factor for the second component
*
* @return this vector with components that are scaled
*/
public Vector2 scaleSelf(double factor0, double factor1) {
v0 *= factor0;
v1 *= factor1;
return this;
}
/**
* Divide all components of the current vector by a factor.
*
* @param factor
* the factor by which to divide the components
*
* @return a new vector with components equal to the division
*/
public Vector2 divide(double factor) {
return new Vector2(this).divideSelf(factor);
}
/**
* Divide all components of the current vector by a factor.
*
* @param factor
* the factor by which to divide the components
*
* @return this vector with components equal to the division
*/
public Vector2 divideSelf(double factor) {
v0 /= factor;
v1 /= factor;
return this;
}
/**
* Limit the vector to a given magnitude. If the magnitude is less the vector
* is left alone, if the magnitude is greater, it will be scaled to have a
* length of the given magnitude.
*
* @param magnitude
* the magnitude limit
*
* @return a new vector with components that are limited
*/
public Vector2 limit(double magnitude) {
return new Vector2(this).limitSelf(magnitude);
}
/**
* Limit the vector to a given magnitude. If the magnitude is less the vector
* is left alone, if the magnitude is greater, it will be scaled to have a
* length of the given magnitude.
*
* @param magnitude
* the magnitude limit
*
* @return this vector with components that are limited
*/
public Vector2 limitSelf(double magnitude) {
double length = getLength();
if (length > magnitude) {
double factor = magnitude / length;
v0 *= factor;
v1 *= factor;
}
return this;
}
/**
* Calculate the square of the Euclidean distance between this vector and the
* other using all 3 coordinates.
*
* @param v
* the second vector
*
* @return the Euclidean distance
*/
public double euclideanDistanceSquared(Vector2 v) {
double xDiff = v0 - v.v0;
double yDiff = v1 - v.v1;
return xDiff * xDiff + yDiff * yDiff;
}
/**
* Calculate the Euclidean distance between this vector and the other using
* all 3 coordinates.
*
* @param v
* the second vector
*
* @return the Euclidean distance
*/
public double euclideanDistance(Vector2 v) {
return Math.sqrt(euclideanDistanceSquared(v));
}
/**
* Calculate the square of the Euclidean distance between this vector and the
* other.
*
* @param v
* the second vector
*
* @return the Euclidean distance
*/
public double euclideanDistanceSquared2(Vector2 v) {
double xDiff = v0 - v.v0;
double yDiff = v1 - v.v1;
return xDiff * xDiff + yDiff * yDiff;
}
/**
* Calculate the Euclidean distance between this vector and the second using
* the first 2 coordinates.
*
* @param v
* the second vector
*
* @return the Euclidean distance
*/
public double euclideanDistance2(Vector2 v) {
return Math.sqrt(euclideanDistanceSquared2(v));
}
/**
* Multiply the current vector by the matrix.
*
* @param m
* the matrix to multiply by
*
* @return a new vector whose components are result of the multiplication
*/
public Vector2 multiply(Matrix3 m) {
return new Vector2(this).multiplySelf(m);
}
/**
* Multiple the current vector in homogeneous space, setting the current
* vector to the result.
*
* @param m
* the vector to multiply by
*
* @return this vector whose components are result of the multiplication
*/
public Vector2 multiplySelf(Matrix3 m) {
double tv0 = v0 * m.matrix[0][0] + v1 * m.matrix[0][1] + m.matrix[0][2];
double tv1 = v0 * m.matrix[1][0] + v1 * m.matrix[1][1] + m.matrix[1][2];
double w = v0 * m.matrix[2][0] + v1 * m.matrix[2][1] + m.matrix[2][2];
v0 = tv0 / w;
v1 = tv1 / w;
return this;
}
/**
* Is this vector equal to the other?
*
* @param v
* the second vector
*
* @return {@code true} if each component is equal to the corresponding
* component.
*/
public boolean equal(Vector2 v) {
return v0 == v.v0 && v1 == v.v1;
}
/**
* Is this vectors equal to the other within some tolerance?
*
* @param v
* the second vector
* @param tolerance
* the tolerance for equality
*
* @return {@code true} if each component is equal to the corresponding
* component within the tolerance factor
*/
public boolean equal(Vector2 v, double tolerance) {
return MathUtils.equals(v0, v.v0, tolerance) && MathUtils.equals(v1, v.v1, tolerance);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(v0);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(v1);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Vector2 other = (Vector2) obj;
if (Double.doubleToLongBits(v0) != Double.doubleToLongBits(other.v0)) {
return false;
}
if (Double.doubleToLongBits(v1) != Double.doubleToLongBits(other.v1)) {
return false;
}
return true;
}
@Override
public String toString() {
return "Vector2 [v0=" + v0 + ", v1=" + v1 + "]";
}
}