diff --git a/src/main/java/pythagoras/d/AbstractPoint.java b/src/main/java/pythagoras/d/AbstractPoint.java index a955791..d1ebe4b 100644 --- a/src/main/java/pythagoras/d/AbstractPoint.java +++ b/src/main/java/pythagoras/d/AbstractPoint.java @@ -32,6 +32,11 @@ public abstract class AbstractPoint implements IPoint return Points.distance(x(), y(), p.x(), p.y()); } + @Override // from interface IPoint + public double direction (IPoint other) { + return Math.atan2(other.y() - y(), other.x() - x()); + } + @Override // from IPoint public Point mult (double s) { return mult(s, new Point()); diff --git a/src/main/java/pythagoras/d/AbstractVector.java b/src/main/java/pythagoras/d/AbstractVector.java index ef61373..9d595e8 100644 --- a/src/main/java/pythagoras/d/AbstractVector.java +++ b/src/main/java/pythagoras/d/AbstractVector.java @@ -37,17 +37,6 @@ public abstract class AbstractVector implements IVector return scale(1f / length(), result); } - @Override // from interface IVector - public double angle (IVector other) { - double cos = dot(other) / (length() * other.length()); - return cos >= 1f ? 0f : Math.acos(cos); - } - - @Override // from interface IVector - public double direction (IVector other) { - return Math.atan2(other.y() - y(), other.x() - x()); - } - @Override // from interface IVector public double length () { return Math.sqrt(lengthSq()); @@ -70,6 +59,17 @@ public abstract class AbstractVector implements IVector return dx*dx + dy*dy; } + @Override // from interface IVector + public double angle () { + return Math.atan2(y(), x()); + } + + @Override // from interface IVector + public double angleBetween (IVector other) { + double cos = dot(other) / (length() * other.length()); + return cos >= 1f ? 0f : Math.acos(cos); + } + @Override // from interface IVector public Vector scale (double v) { return scale(v, new Vector()); diff --git a/src/main/java/pythagoras/d/IPoint.java b/src/main/java/pythagoras/d/IPoint.java index 349ae81..1a9a13d 100644 --- a/src/main/java/pythagoras/d/IPoint.java +++ b/src/main/java/pythagoras/d/IPoint.java @@ -27,6 +27,10 @@ public interface IPoint extends Cloneable /** Returns the Euclidian distance between this point and the supplied point. */ double distance (IPoint p); + /** Returns the angle (in radians) of the vector starting at this point and ending at the + * supplied other point. */ + double direction (IPoint other); + /** Multiplies this point by a scale factor. * @return a new point containing the result. */ Point mult (double s); diff --git a/src/main/java/pythagoras/d/IVector.java b/src/main/java/pythagoras/d/IVector.java index 2c8656b..2b7d40c 100644 --- a/src/main/java/pythagoras/d/IVector.java +++ b/src/main/java/pythagoras/d/IVector.java @@ -34,13 +34,7 @@ public interface IVector * @return a reference to the result, for chaining. */ Vector normalize (Vector result); - /** Returns the angle between this vector and the specified other vector. */ - double angle (IVector other); - - /** Returns the direction of a vector pointing from this point to the specified other point. */ - double direction (IVector other); - - /** Returns the length of this vector. */ + /** Returns the length (magnitude) of this vector. */ double length (); /** Returns the squared length of this vector. */ @@ -52,6 +46,12 @@ public interface IVector /** Returns the squared distance from this vector to the specified other. */ double distanceSq (IVector other); + /** Returns the angle of this vector. */ + double angle (); + + /** Returns the angle between this vector and the specified other vector. */ + double angleBetween (IVector other); + /** Scales this vector uniformly by the specified magnitude. * @return a new vector containing the result. */ Vector scale (double v); diff --git a/src/main/java/pythagoras/d/Vector.java b/src/main/java/pythagoras/d/Vector.java index 5663cd7..b7b3e05 100644 --- a/src/main/java/pythagoras/d/Vector.java +++ b/src/main/java/pythagoras/d/Vector.java @@ -110,6 +110,22 @@ public class Vector extends AbstractVector return this; } + /** + * Sets this vector's angle, preserving its magnitude. + * @return a reference to this vector, for chaining. + */ + public Vector setAngle (double angle) { + double l = length(); + return set(l * Math.cos(angle), l * Math.sin(angle)); + } + + /** + * Sets this vector's magnitude, preserving its angle. + */ + public Vector setLength (double length) { + return normalizeLocal().scaleLocal(length); + } + @Override // from AbstractVector public double x () { return x; diff --git a/src/main/java/pythagoras/d/Vectors.java b/src/main/java/pythagoras/d/Vectors.java index dcc661e..b414314 100644 --- a/src/main/java/pythagoras/d/Vectors.java +++ b/src/main/java/pythagoras/d/Vectors.java @@ -25,6 +25,13 @@ public class Vectors /** A vector containing the maximum doubleing point value for all components. */ public static final IVector MAX_VALUE = new Vector(Float.MAX_VALUE, Float.MAX_VALUE); + /** + * Creates a new vector from polar coordinates. + */ + public static final Vector fromPolar (double magnitude, double angle) { + return new Vector(magnitude * Math.cos(angle), magnitude * Math.sin(angle)); + } + /** * Returns the magnitude of the specified vector. */ diff --git a/src/main/java/pythagoras/f/AbstractPoint.java b/src/main/java/pythagoras/f/AbstractPoint.java index 6f5a8cb..3bd9b1a 100644 --- a/src/main/java/pythagoras/f/AbstractPoint.java +++ b/src/main/java/pythagoras/f/AbstractPoint.java @@ -32,6 +32,11 @@ public abstract class AbstractPoint implements IPoint return Points.distance(x(), y(), p.x(), p.y()); } + @Override // from interface IPoint + public float direction (IPoint other) { + return FloatMath.atan2(other.y() - y(), other.x() - x()); + } + @Override // from IPoint public Point mult (float s) { return mult(s, new Point()); diff --git a/src/main/java/pythagoras/f/AbstractVector.java b/src/main/java/pythagoras/f/AbstractVector.java index 3c49ab6..8dfa8a4 100644 --- a/src/main/java/pythagoras/f/AbstractVector.java +++ b/src/main/java/pythagoras/f/AbstractVector.java @@ -37,17 +37,6 @@ public abstract class AbstractVector implements IVector return scale(1f / length(), result); } - @Override // from interface IVector - public float angleBetween (IVector other) { - float cos = dot(other) / (length() * other.length()); - return cos >= 1f ? 0f : FloatMath.acos(cos); - } - - @Override // from interface IVector - public float direction (IVector other) { - return FloatMath.atan2(other.y() - y(), other.x() - x()); - } - @Override // from interface IVector public float length () { return FloatMath.sqrt(lengthSq()); @@ -70,6 +59,17 @@ public abstract class AbstractVector implements IVector return dx*dx + dy*dy; } + @Override // from interface IVector + public float angle () { + return FloatMath.atan2(y(), x()); + } + + @Override // from interface IVector + public float angleBetween (IVector other) { + float cos = dot(other) / (length() * other.length()); + return cos >= 1f ? 0f : FloatMath.acos(cos); + } + @Override // from interface IVector public Vector scale (float v) { return scale(v, new Vector()); diff --git a/src/main/java/pythagoras/f/IPoint.java b/src/main/java/pythagoras/f/IPoint.java index 7cb81d7..a42342c 100644 --- a/src/main/java/pythagoras/f/IPoint.java +++ b/src/main/java/pythagoras/f/IPoint.java @@ -27,6 +27,10 @@ public interface IPoint extends Cloneable /** Returns the Euclidian distance between this point and the supplied point. */ float distance (IPoint p); + /** Returns the angle (in radians) of the vector starting at this point and ending at the + * supplied other point. */ + float direction (IPoint other); + /** Multiplies this point by a scale factor. * @return a new point containing the result. */ Point mult (float s); diff --git a/src/main/java/pythagoras/f/IVector.java b/src/main/java/pythagoras/f/IVector.java index 9b0e0de..454bb6e 100644 --- a/src/main/java/pythagoras/f/IVector.java +++ b/src/main/java/pythagoras/f/IVector.java @@ -34,13 +34,7 @@ public interface IVector * @return a reference to the result, for chaining. */ Vector normalize (Vector result); - /** Returns the angle between this vector and the specified other vector. */ - float angleBetween (IVector other); - - /** Returns the direction of a vector pointing from this point to the specified other point. */ - float direction (IVector other); - - /** Returns the length of this vector. */ + /** Returns the length (magnitude) of this vector. */ float length (); /** Returns the squared length of this vector. */ @@ -52,6 +46,12 @@ public interface IVector /** Returns the squared distance from this vector to the specified other. */ float distanceSq (IVector other); + /** Returns the angle of this vector. */ + float angle (); + + /** Returns the angle between this vector and the specified other vector. */ + float angleBetween (IVector other); + /** Scales this vector uniformly by the specified magnitude. * @return a new vector containing the result. */ Vector scale (float v); diff --git a/src/main/java/pythagoras/f/Vector.java b/src/main/java/pythagoras/f/Vector.java index 8e07db1..09eec17 100644 --- a/src/main/java/pythagoras/f/Vector.java +++ b/src/main/java/pythagoras/f/Vector.java @@ -110,6 +110,22 @@ public class Vector extends AbstractVector return this; } + /** + * Sets this vector's angle, preserving its magnitude. + * @return a reference to this vector, for chaining. + */ + public Vector setAngle (float angle) { + float l = length(); + return set(l * FloatMath.cos(angle), l * FloatMath.sin(angle)); + } + + /** + * Sets this vector's magnitude, preserving its angle. + */ + public Vector setLength (float length) { + return normalizeLocal().scaleLocal(length); + } + @Override // from AbstractVector public float x () { return x; diff --git a/src/main/java/pythagoras/f/Vectors.java b/src/main/java/pythagoras/f/Vectors.java index 9f7c4e4..550d1a2 100644 --- a/src/main/java/pythagoras/f/Vectors.java +++ b/src/main/java/pythagoras/f/Vectors.java @@ -25,6 +25,13 @@ public class Vectors /** A vector containing the maximum floating point value for all components. */ public static final IVector MAX_VALUE = new Vector(Float.MAX_VALUE, Float.MAX_VALUE); + /** + * Creates a new vector from polar coordinates. + */ + public static final Vector fromPolar (float magnitude, float angle) { + return new Vector(magnitude * FloatMath.cos(angle), magnitude * FloatMath.sin(angle)); + } + /** * Returns the magnitude of the specified vector. */ diff --git a/src/test/java/pythagoras/f/VectorTest.java b/src/test/java/pythagoras/f/VectorTest.java new file mode 100644 index 0000000..d0616b4 --- /dev/null +++ b/src/test/java/pythagoras/f/VectorTest.java @@ -0,0 +1,56 @@ +// +// Pythagoras - a collection of geometry classes +// http://github.com/samskivert/pythagoras + +package pythagoras.f; + +import org.junit.*; +import static org.junit.Assert.*; + +/** + * Tests aspects of the {@link Vector} class. + */ +public class VectorTest +{ + @Test + public void testFromPolar () { + for (float length = 0.05f; length < 5; length += 0.05f) { + // stay away from -PI and PI because the signs might be flipped + for (float theta = -FloatMath.PI + 0.05f; theta < FloatMath.PI; theta += 0.05f) { + Vector v = Vectors.fromPolar(length, theta); + assertEquals(length, v.length(), MathUtil.EPSILON); + assertEquals(theta, v.angle(), MathUtil.EPSILON); + } + } + } + + @Test + public void testSetLength () { + for (float length = 0.05f; length < 5; length += 0.05f) { + // stay away from -PI and PI because the signs might be flipped + for (float theta = -FloatMath.PI + 0.05f; theta < FloatMath.PI; theta += 0.05f) { + Vector v = Vectors.fromPolar(length, theta); + v.setLength(10); + // make sure setting length actually sets the length + assertEquals(10, v.length(), MathUtil.EPSILON); + // make sure setting length doesn't bork angle + assertEquals(theta, v.angle(), MathUtil.EPSILON); + } + } + } + + @Test + public void testSetAngle () { + for (float length = 0.05f; length < 5; length += 0.05f) { + // stay away from -PI and PI because the signs might be flipped + for (float theta = 0.05f; theta < FloatMath.PI; theta += 0.05f) { + Vector v = Vectors.fromPolar(length, theta); + v.setAngle(FloatMath.PI - theta); + // make sure setting angle actually sets the angle + assertEquals(FloatMath.PI - theta, v.angle(), MathUtil.EPSILON); + // make sure setting length doesn't bork length + assertEquals(length, v.length(), MathUtil.EPSILON); + } + } + } +}