You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2018/09/17 13:41:55 UTC
[commons-geometry] 03/07: GEOMETRY-10: adding private UnitVector
private classes in Vector2D and Vector1D for normalization optimizations;
adding Point?D#directionTo() method
This is an automated email from the ASF dual-hosted git repository.
erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-geometry.git
commit dd0d3baa4f58cdb5d75759696a5f17be2c1d0708
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Sun Sep 16 23:24:42 2018 -0400
GEOMETRY-10: adding private UnitVector private classes in Vector2D and Vector1D for normalization optimizations; adding Point?D#directionTo() method
---
.../commons/geometry/euclidean/EuclideanPoint.java | 11 ++++
.../commons/geometry/euclidean/oned/Point1D.java | 6 ++
.../commons/geometry/euclidean/oned/Vector1D.java | 51 +++++++++++++--
.../commons/geometry/euclidean/threed/Point3D.java | 2 +
.../geometry/euclidean/threed/Vector3D.java | 8 +--
.../commons/geometry/euclidean/twod/Point2D.java | 9 +++
.../commons/geometry/euclidean/twod/Vector2D.java | 63 ++++++++++++++----
.../geometry/euclidean/oned/Point1DTest.java | 35 ++++++++++
.../geometry/euclidean/oned/Vector1DTest.java | 68 ++++++++++++++++++--
.../geometry/euclidean/threed/Point3DTest.java | 37 +++++++++++
.../geometry/euclidean/threed/Vector3DTest.java | 15 ++++-
.../geometry/euclidean/twod/Point2DTest.java | 37 +++++++++++
.../geometry/euclidean/twod/Vector2DTest.java | 74 +++++++++++++++++++---
13 files changed, 376 insertions(+), 40 deletions(-)
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanPoint.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanPoint.java
index bde9f67..ed662b2 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanPoint.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanPoint.java
@@ -41,6 +41,17 @@ public interface EuclideanPoint<P extends EuclideanPoint<P, V>, V extends Euclid
*/
V vectorTo(P p);
+ /** Returns the unit vector representing the direction of displacement from this
+ * point to the given point. This is exactly equivalent to {@code p.subtract(thisPoint).normalize()}
+ * but without the intermediate vector instance.
+ * @param p the point the returned vector will be directed toward
+ * @return unit vector representing the direction of displacement <em>from</em> this point
+ * <em>to</em> the given point
+ * @throws IllegalNormException if the norm of the vector pointing from this point to {@code p}
+ * is zero, NaN, or infinite
+ */
+ V directionTo(P p);
+
/** Linearly interpolates between this point and the given point using the equation
* {@code P = (1 - t)*A + t*B}, where {@code A} is the current point and {@code B}
* is the given point. This means that if {@code t = 0}, a point equal to the current
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Point1D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Point1D.java
index 4f0fec1..7e977b7 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Point1D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Point1D.java
@@ -83,6 +83,12 @@ public final class Point1D extends Cartesian1D implements EuclideanPoint<Point1D
/** {@inheritDoc} */
@Override
+ public Vector1D directionTo(Point1D p) {
+ return Vector1D.normalize(p.getX() - getX());
+ }
+
+ /** {@inheritDoc} */
+ @Override
public Point1D lerp(Point1D p, double t) {
return vectorCombination(1.0 - t, this, t, p);
}
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
index 79a4d35..b678559 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
@@ -17,6 +17,7 @@
package org.apache.commons.geometry.euclidean.oned;
import org.apache.commons.geometry.core.Geometry;
+import org.apache.commons.geometry.core.exception.IllegalNormException;
import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
import org.apache.commons.geometry.euclidean.EuclideanVector;
import org.apache.commons.geometry.euclidean.internal.Vectors;
@@ -25,16 +26,16 @@ import org.apache.commons.numbers.arrays.LinearCombination;
/** This class represents a vector in one-dimensional Euclidean space.
* Instances of this class are guaranteed to be immutable.
*/
-public final class Vector1D extends Cartesian1D implements EuclideanVector<Point1D, Vector1D> {
+public class Vector1D extends Cartesian1D implements EuclideanVector<Point1D, Vector1D> {
/** Zero vector (coordinates: 0). */
public static final Vector1D ZERO = new Vector1D(0.0);
/** Unit vector (coordinates: 1). */
- public static final Vector1D ONE = new Vector1D(1.0);
+ public static final Vector1D ONE = new UnitVector(1.0);
/** Negation of unit vector (coordinates: -1). */
- public static final Vector1D MINUS_ONE = new Vector1D(-1.0);
+ public static final Vector1D MINUS_ONE = new UnitVector(-1.0);
// CHECKSTYLE: stop ConstantName
/** A vector with all coordinates set to NaN. */
@@ -154,9 +155,7 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point
/** {@inheritDoc} */
@Override
public Vector1D normalize() {
- Vectors.ensureFiniteNonZeroNorm(getNorm());
-
- return (getX() > 0.0) ? ONE : MINUS_ONE;
+ return normalize(getX());
}
/** {@inheritDoc} */
@@ -290,6 +289,17 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point
return new Vector1D(x);
}
+ /** Returns a normalized vector derived from the given value.
+ * @param x abscissa (first coordinate value)
+ * @return normalized vector instance
+ * @throws IllegalNormException if the norm of the given value is zero, NaN, or infinite
+ */
+ public static Vector1D normalize(final double x) {
+ Vectors.ensureFiniteNonZeroNorm(Vectors.norm(x));
+
+ return (x > 0.0) ? ONE : MINUS_ONE;
+ }
+
/** Parses the given string and returns a new vector instance. The expected string
* format is the same as that returned by {@link #toString()}.
* @param str the string to parse
@@ -380,4 +390,33 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point
return new Vector1D(
LinearCombination.value(a1, c1.getX(), a2, c2.getX(), a3, c3.getX(), a4, c4.getX()));
}
+
+ /** Private class used to represent unit vectors. This allows optimizations to be performed for certain
+ * operations.
+ */
+ private static final class UnitVector extends Vector1D {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20180903L;
+
+ /** Simple constructor. Callers are responsible for ensuring that the given
+ * values represent a normalized vector.
+ * @param x abscissa (first coordinate value)
+ */
+ private UnitVector(final double x) {
+ super(x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector1D normalize() {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector1D withMagnitude(final double mag) {
+ return scalarMultiply(mag);
+ }
+ }
}
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
index 9e44bf5..24626ec 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
@@ -87,6 +87,8 @@ public final class Point3D extends Cartesian3D implements EuclideanPoint<Point3D
return p.subtract(this);
}
+ /** {@inheritDoc} */
+ @Override
public Vector3D directionTo(Point3D p) {
return Vector3D.normalize(
p.getX() - getX(),
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java
index cb66b1c..0014d4d 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java
@@ -425,7 +425,7 @@ public class Vector3D extends Cartesian3D implements EuclideanVector<Point3D, Ve
// We need to check the norm value here to ensure that it's legal. However, we don't
// want to incur the cost or floating point error of getting the actual norm and then
// multiplying it again to get the square norm. So, we'll just check the squared norm
- // directly. This will produce the same result as checking the actual norm since
+ // directly. This will produce the same error result as checking the actual norm since
// Math.sqrt(0.0) == 0.0, Math.sqrt(Double.NaN) == Double.NaN and
// Math.sqrt(Double.POSITIVE_INFINITY) == Double.POSITIVE_INFINITY.
final double baseMagSq = Vectors.ensureFiniteNonZeroNorm(base.getNormSq());
@@ -478,10 +478,10 @@ public class Vector3D extends Cartesian3D implements EuclideanVector<Point3D, Ve
/** Returns a normalized vector derived from the given values.
* @param x abscissa (first coordinate value)
- * @param y abscissa (second coordinate value)
+ * @param y ordinate (second coordinate value)
* @param z height (third coordinate value)
* @return normalized vector instance
- * @throws IllegalNormException if the norm of the given values is zero
+ * @throws IllegalNormException if the norm of the given values is zero, NaN, or infinite
*/
public static Vector3D normalize(final double x, final double y, final double z) {
final double norm = Vectors.ensureFiniteNonZeroNorm(Vectors.norm(x, y, z));
@@ -598,7 +598,7 @@ public class Vector3D extends Cartesian3D implements EuclideanVector<Point3D, Ve
/** Simple constructor. Callers are responsible for ensuring that the given
* values represent a normalized vector.
* @param x abscissa (first coordinate value)
- * @param y abscissa (second coordinate value)
+ * @param y ordinate (second coordinate value)
* @param z height (third coordinate value)
*/
private UnitVector(final double x, final double y, final double z) {
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
index ece33f0..ddd707e 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
@@ -80,6 +80,15 @@ public final class Point2D extends Cartesian2D implements EuclideanPoint<Point2D
/** {@inheritDoc} */
@Override
+ public Vector2D directionTo(Point2D p) {
+ return Vector2D.normalize(
+ p.getX() - getX(),
+ p.getY() - getY()
+ );
+ }
+
+ /** {@inheritDoc} */
+ @Override
public Point2D lerp(Point2D p, double t) {
return vectorCombination(1.0 - t, this, t, p);
}
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
index 36338d0..7ae7edf 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
@@ -25,22 +25,22 @@ import org.apache.commons.numbers.arrays.LinearCombination;
/** This class represents a vector in two-dimensional Euclidean space.
* Instances of this class are guaranteed to be immutable.
*/
-public final class Vector2D extends Cartesian2D implements EuclideanVector<Point2D, Vector2D> {
+public class Vector2D extends Cartesian2D implements EuclideanVector<Point2D, Vector2D> {
/** Zero vector (coordinates: 0, 0). */
public static final Vector2D ZERO = new Vector2D(0, 0);
/** Unit vector pointing in the direction of the positive x-axis. */
- public static final Vector2D PLUS_X = new Vector2D(1, 0);
+ public static final Vector2D PLUS_X = new UnitVector(1, 0);
/** Unit vector pointing in the direction of the negative x-axis. */
- public static final Vector2D MINUS_X = new Vector2D(-1, 0);
+ public static final Vector2D MINUS_X = new UnitVector(-1, 0);
/** Unit vector pointing in the direction of the positive y-axis. */
- public static final Vector2D PLUS_Y = new Vector2D(0, 1);
+ public static final Vector2D PLUS_Y = new UnitVector(0, 1);
/** Unit vector pointing in the direction of the negative y-axis. */
- public static final Vector2D MINUS_Y = new Vector2D(0, -1);
+ public static final Vector2D MINUS_Y = new UnitVector(0, -1);
// CHECKSTYLE: stop ConstantName
/** A vector with all coordinates set to NaN. */
@@ -66,14 +66,6 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point
super(x, y);
}
- /** Get the vector coordinates as a dimension 2 array.
- * @return vector coordinates
- */
- @Override
- public double[] toArray() {
- return new double[] { getX(), getY() };
- }
-
/** {@inheritDoc} */
@Override
public Point2D asPoint() {
@@ -172,7 +164,7 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point
/** {@inheritDoc} */
@Override
public Vector2D normalize() {
- return scalarMultiply(1.0 / getFiniteNonZeroNorm());
+ return normalize(getX(), getY());
}
/** {@inheritDoc} */
@@ -395,6 +387,19 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point
return PolarCoordinates.toCartesian(radius, azimuth, Vector2D::new);
}
+ /** Returns a normalized vector derived from the given values.
+ * @param x abscissa (first coordinate value)
+ * @param y ordinate (second coordinate value)
+ * @return normalized vector instance
+ * @throws IllegalNormException if the norm of the given values is zero, NaN, or infinite
+ */
+ public static Vector2D normalize(final double x, final double y) {
+ final double norm = Vectors.ensureFiniteNonZeroNorm(Vectors.norm(x, y));
+ final double invNorm = 1.0 / norm;
+
+ return new UnitVector(x * invNorm, y * invNorm);
+ }
+
/** Parses the given string and returns a new vector instance. The expected string
* format is the same as that returned by {@link #toString()}.
* @param str the string to parse
@@ -488,4 +493,34 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point
LinearCombination.value(a1, c1.getX(), a2, c2.getX(), a3, c3.getX(), a4, c4.getX()),
LinearCombination.value(a1, c1.getY(), a2, c2.getY(), a3, c3.getY(), a4, c4.getY()));
}
+
+ /** Private class used to represent unit vectors. This allows optimizations to be performed for certain
+ * operations.
+ */
+ private static final class UnitVector extends Vector2D {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20180903L;
+
+ /** Simple constructor. Callers are responsible for ensuring that the given
+ * values represent a normalized vector.
+ * @param x abscissa (first coordinate value)
+ * @param y abscissa (second coordinate value)
+ */
+ private UnitVector(final double x, final double y) {
+ super(x, y);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector2D normalize() {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector2D withMagnitude(final double mag) {
+ return scalarMultiply(mag);
+ }
+ }
}
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Point1DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Point1DTest.java
index 25b2fe6..1b40bf2 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Point1DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Point1DTest.java
@@ -19,6 +19,8 @@ package org.apache.commons.geometry.euclidean.oned;
import java.util.regex.Pattern;
+import org.apache.commons.geometry.core.GeometryTestUtils;
+import org.apache.commons.geometry.core.exception.IllegalNormException;
import org.apache.commons.numbers.core.Precision;
import org.junit.Assert;
import org.junit.Test;
@@ -118,6 +120,39 @@ public class Point1DTest {
}
@Test
+ public void testDirectionTo() {
+ // act/assert
+ Point1D p1 = Point1D.of(1);
+ Point1D p2 = Point1D.of(5);
+ Point1D p3 = Point1D.of(-2);
+
+ // act/assert
+ checkVector(p1.directionTo(p2), 1);
+ checkVector(p2.directionTo(p1), -1);
+
+ checkVector(p1.directionTo(p3), -1);
+ checkVector(p3.directionTo(p1), 1);
+ }
+
+ @Test
+ public void testDirectionTo_illegalNorm() {
+ // arrange
+ Point1D p = Point1D.of(2);
+
+ // act/assert
+ GeometryTestUtils.assertThrows(() -> Point1D.ZERO.directionTo(Point1D.ZERO),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> p.directionTo(p),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> p.directionTo(Point1D.NaN),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Point1D.NEGATIVE_INFINITY.directionTo(p),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> p.directionTo(Point1D.POSITIVE_INFINITY),
+ IllegalNormException.class);
+ }
+
+ @Test
public void testLerp() {
// arrange
Point1D p1 = Point1D.of(1);
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
index ee817cc..3c7bd50 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
@@ -41,6 +41,22 @@ public class Vector1DTest {
}
@Test
+ public void testConstants_normalize() {
+ // act/assert
+ GeometryTestUtils.assertThrows(() -> Vector1D.ZERO.normalize(),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector1D.NaN.normalize(),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector1D.POSITIVE_INFINITY.normalize(),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector1D.NEGATIVE_INFINITY.normalize(),
+ IllegalNormException.class);
+
+ Assert.assertSame(Vector1D.ONE.normalize(), Vector1D.ONE);
+ Assert.assertSame(Vector1D.MINUS_ONE.normalize(), Vector1D.MINUS_ONE);
+ }
+
+ @Test
public void testAsPoint() {
// act/assert
checkPoint(Vector1D.of(0.0).asPoint(), 0.0);
@@ -135,6 +151,21 @@ public class Vector1DTest {
}
@Test
+ public void testWithMagnitude_unitVectors() {
+ // arrange
+ Vector1D v = Vector1D.of(2.0).normalize();
+
+ // act/assert
+ checkVector(Vector1D.ONE.withMagnitude(2.5), 2.5);
+ checkVector(Vector1D.MINUS_ONE.withMagnitude(3.14), -3.14);
+
+ for (double mag = -10.0; mag <= 10.0; ++mag)
+ {
+ Assert.assertEquals(Math.abs(mag), v.withMagnitude(mag).getMagnitude(), TEST_TOLERANCE);
+ }
+ }
+
+ @Test
public void testAdd() {
// arrange
Vector1D v1 = Vector1D.of(1);
@@ -208,17 +239,27 @@ public class Vector1DTest {
@Test
public void testNormalize_illegalNorm() {
// act/assert
- GeometryTestUtils.assertThrows(() -> Vector1D.ZERO.normalize(),
+ GeometryTestUtils.assertThrows(() -> Vector1D.of(0.0).normalize(),
IllegalNormException.class);
- GeometryTestUtils.assertThrows(() -> Vector1D.NaN.normalize(),
+ GeometryTestUtils.assertThrows(() -> Vector1D.of(Double.NaN).normalize(),
IllegalNormException.class);
- GeometryTestUtils.assertThrows(() -> Vector1D.POSITIVE_INFINITY.normalize(),
+ GeometryTestUtils.assertThrows(() -> Vector1D.of(Double.POSITIVE_INFINITY).normalize(),
IllegalNormException.class);
- GeometryTestUtils.assertThrows(() -> Vector1D.NEGATIVE_INFINITY.normalize(),
+ GeometryTestUtils.assertThrows(() -> Vector1D.of(Double.NEGATIVE_INFINITY).normalize(),
IllegalNormException.class);
}
@Test
+ public void testNormalize_isIdempotent() {
+ // arrange
+ Vector1D v = Vector1D.of(2).normalize();
+
+ // act/assert
+ Assert.assertSame(v, v.normalize());
+ checkVector(v.normalize(), 1.0);
+ }
+
+ @Test
public void testNegate() {
// act/assert
checkVector(Vector1D.of(0.1).negate(), -0.1);
@@ -541,6 +582,25 @@ public class Vector1DTest {
}
@Test
+ public void testNormalize_static() {
+ // act/assert
+ checkVector(Vector1D.normalize(2.0), 1);
+ checkVector(Vector1D.normalize(-4.0), -1);
+ }
+
+ @Test
+ public void testNormalize_static_illegalNorm() {
+ GeometryTestUtils.assertThrows(() -> Vector1D.normalize(0.0),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector1D.normalize(Double.NaN),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector1D.normalize(Double.NEGATIVE_INFINITY),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector1D.normalize(Double.POSITIVE_INFINITY),
+ IllegalNormException.class);
+ }
+
+ @Test
public void testLinearCombination() {
// act/assert
checkVector(Vector1D.linearCombination(2, Vector1D.of(3)), 6);
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
index 3e312dd..e1cd149 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
@@ -19,6 +19,8 @@ package org.apache.commons.geometry.euclidean.threed;
import java.util.regex.Pattern;
import org.apache.commons.geometry.core.Geometry;
+import org.apache.commons.geometry.core.GeometryTestUtils;
+import org.apache.commons.geometry.core.exception.IllegalNormException;
import org.apache.commons.numbers.core.Precision;
import org.junit.Assert;
import org.junit.Test;
@@ -103,6 +105,41 @@ public class Point3DTest {
}
@Test
+ public void testDirectionTo() {
+ // act/assert
+ double invSqrt3 = 1.0 / Math.sqrt(3);
+
+ Point3D p1 = Point3D.of(1, 1, 1);
+ Point3D p2 = Point3D.of(1, 5, 1);
+ Point3D p3 = Point3D.of(-2, -2, -2);
+
+ // act/assert
+ checkVector(p1.directionTo(p2), 0, 1, 0);
+ checkVector(p2.directionTo(p1), 0, -1, 0);
+
+ checkVector(p1.directionTo(p3), -invSqrt3, -invSqrt3, -invSqrt3);
+ checkVector(p3.directionTo(p1), invSqrt3, invSqrt3, invSqrt3);
+ }
+
+ @Test
+ public void testDirectionTo_illegalNorm() {
+ // arrange
+ Point3D p = Point3D.of(1, 2, 3);
+
+ // act/assert
+ GeometryTestUtils.assertThrows(() -> Point3D.ZERO.directionTo(Point3D.ZERO),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> p.directionTo(p),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> p.directionTo(Point3D.NaN),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Point3D.NEGATIVE_INFINITY.directionTo(p),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> p.directionTo(Point3D.POSITIVE_INFINITY),
+ IllegalNormException.class);
+ }
+
+ @Test
public void testLerp() {
// arrange
Point3D p1 = Point3D.of(1, -5, 2);
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
index 48c4524..8d6fe9e 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
@@ -53,8 +53,17 @@ public class Vector3DTest {
}
@Test
- public void testNonZeroConstants_areUnitVectorInstances() {
+ public void testConstants_normalize() {
// act/assert
+ GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.normalize(),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector3D.NaN.normalize(),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector3D.POSITIVE_INFINITY.normalize(),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector3D.NEGATIVE_INFINITY.normalize(),
+ IllegalNormException.class);
+
Assert.assertSame(Vector3D.PLUS_X.normalize(), Vector3D.PLUS_X);
Assert.assertSame(Vector3D.MINUS_X.normalize(), Vector3D.MINUS_X);
@@ -392,7 +401,9 @@ public class Vector3DTest {
IllegalNormException.class);
GeometryTestUtils.assertThrows(() -> Vector3D.PLUS_X.orthogonal(Vector3D.MINUS_X),
IllegalNormException.class);
- GeometryTestUtils.assertThrows(() -> Vector3D.of(1.0, 1.0, 1.0).orthogonal(Vector3D.of(-2.0, -2.0, -2.0)),
+ GeometryTestUtils.assertThrows(() -> Vector3D.of(1.0, 1.0, 1.0).orthogonal(Vector3D.of(2.0, 2.0, 2.0)),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector3D.of(-1.01, -1.01, -1.01).orthogonal(Vector3D.of(20.1, 20.1, 20.1)),
IllegalNormException.class);
}
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
index d5903de..dcf89d9 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
@@ -19,6 +19,8 @@ package org.apache.commons.geometry.euclidean.twod;
import java.util.regex.Pattern;
import org.apache.commons.geometry.core.Geometry;
+import org.apache.commons.geometry.core.GeometryTestUtils;
+import org.apache.commons.geometry.core.exception.IllegalNormException;
import org.apache.commons.numbers.core.Precision;
import org.junit.Assert;
import org.junit.Test;
@@ -94,6 +96,41 @@ public class Point2DTest {
}
@Test
+ public void testDirectionTo() {
+ // act/assert
+ double invSqrt2 = 1.0 / Math.sqrt(2);
+
+ Point2D p1 = Point2D.of(1, 1);
+ Point2D p2 = Point2D.of(1, 5);
+ Point2D p3 = Point2D.of(-2, -2);
+
+ // act/assert
+ checkVector(p1.directionTo(p2), 0, 1);
+ checkVector(p2.directionTo(p1), 0, -1);
+
+ checkVector(p1.directionTo(p3), -invSqrt2, -invSqrt2);
+ checkVector(p3.directionTo(p1), invSqrt2, invSqrt2);
+ }
+
+ @Test
+ public void testDirectionTo_illegalNorm() {
+ // arrange
+ Point2D p = Point2D.of(1, 2);
+
+ // act/assert
+ GeometryTestUtils.assertThrows(() -> Point2D.ZERO.directionTo(Point2D.ZERO),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> p.directionTo(p),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> p.directionTo(Point2D.NaN),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Point2D.NEGATIVE_INFINITY.directionTo(p),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> p.directionTo(Point2D.POSITIVE_INFINITY),
+ IllegalNormException.class);
+ }
+
+ @Test
public void testLerp() {
// arrange
Point2D p1 = Point2D.of(1, -5);
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java
index 791f1c6..0beb633 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java
@@ -44,17 +44,22 @@ public class Vector2DTest {
}
@Test
- public void testToArray() {
- // arrange
- Vector2D v = Vector2D.of(1, 2);
+ public void testConstants_normalize() {
+ // act/assert
+ GeometryTestUtils.assertThrows(() -> Vector2D.ZERO.normalize(),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector2D.NaN.normalize(),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector2D.POSITIVE_INFINITY.normalize(),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector2D.NEGATIVE_INFINITY.normalize(),
+ IllegalNormException.class);
- // act
- double[] arr = v.toArray();
+ Assert.assertSame(Vector2D.PLUS_X.normalize(), Vector2D.PLUS_X);
+ Assert.assertSame(Vector2D.MINUS_X.normalize(), Vector2D.MINUS_X);
- // assert
- Assert.assertEquals(2, arr.length);
- Assert.assertEquals(1, arr[0], EPS);
- Assert.assertEquals(2, arr[1], EPS);
+ Assert.assertSame(Vector2D.PLUS_Y.normalize(), Vector2D.PLUS_Y);
+ Assert.assertSame(Vector2D.MINUS_Y.normalize(), Vector2D.MINUS_Y);
}
@Test
@@ -175,6 +180,22 @@ public class Vector2DTest {
}
@Test
+ public void testWithMagnitude_unitVectors() {
+ // arrange
+ double eps = 1e-14;
+ Vector2D v = Vector2D.of(2.0, -3.0).normalize();
+
+ // act/assert
+ checkVector(Vector2D.PLUS_X.withMagnitude(2.5), 2.5, 0.0);
+ checkVector(Vector2D.MINUS_Y.withMagnitude(3.14), 0.0, -3.14);
+
+ for (double mag = -10.0; mag <= 10.0; ++mag)
+ {
+ Assert.assertEquals(Math.abs(mag), v.withMagnitude(mag).getMagnitude(), eps);
+ }
+ }
+
+ @Test
public void testAdd() {
// arrange
Vector2D v1 = Vector2D.of(-1, 2);
@@ -249,7 +270,7 @@ public class Vector2DTest {
checkVector(Vector2D.of(-100, 0).normalize(), -1, 0);
checkVector(Vector2D.of(0, 100).normalize(), 0, 1);
checkVector(Vector2D.of(0, -100).normalize(), 0, -1);
- checkVector(Vector2D.of(-1, 2).normalize(), -1.0/Math.sqrt(5), 2.0/Math.sqrt(5));
+ checkVector(Vector2D.of(-1, 2).normalize(), -1.0 / Math.sqrt(5), 2.0 / Math.sqrt(5));
}
@Test
@@ -266,6 +287,17 @@ public class Vector2DTest {
}
@Test
+ public void testNormalize_isIdempotent() {
+ // arrange
+ double invSqrt2 = 1.0 / Math.sqrt(2);
+ Vector2D v = Vector2D.of(2, 2).normalize();
+
+ // act/assert
+ Assert.assertSame(v, v.normalize());
+ checkVector(v.normalize(), invSqrt2, invSqrt2);
+ }
+
+ @Test
public void testNegate() {
// act/assert
checkVector(Vector2D.of(1, 2).negate(), -1, -2);
@@ -703,6 +735,28 @@ public class Vector2DTest {
}
@Test
+ public void testNormalize_static() {
+ // arrange
+ double invSqrt2 = 1.0 / Math.sqrt(2.0);
+
+ // act/assert
+ checkVector(Vector2D.normalize(2.0, -2.0), invSqrt2, -invSqrt2);
+ checkVector(Vector2D.normalize(-4.0, 4.0), -invSqrt2, invSqrt2);
+ }
+
+ @Test
+ public void testNormalize_static_illegalNorm() {
+ GeometryTestUtils.assertThrows(() -> Vector2D.normalize(0.0, 0.0),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector2D.normalize(Double.NaN, 1.0),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector2D.normalize(1.0, Double.NEGATIVE_INFINITY),
+ IllegalNormException.class);
+ GeometryTestUtils.assertThrows(() -> Vector2D.normalize(1.0, Double.POSITIVE_INFINITY),
+ IllegalNormException.class);
+ }
+
+ @Test
public void testLinearCombination1() {
// arrange
Vector2D p1 = Vector2D.of(1, 2);