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 0ccee1783e8ae72941f3d79e88b9a8e59548fc89 Author: Matt Juntunen <[email protected]> AuthorDate: Tue Feb 5 23:29:58 2019 -0500 GEOMETRY-41: Adding equals(EuclideanVector, DoublePrecisionContext) and isZero(DoublePrecisionContext) methods to EuclideanVector and subclasses --- .../geometry/euclidean/EuclideanVector.java | 30 +++++++++++ .../commons/geometry/euclidean/oned/Vector1D.java | 7 +++ .../geometry/euclidean/threed/Vector3D.java | 9 ++++ .../commons/geometry/euclidean/twod/Vector2D.java | 8 +++ .../geometry/euclidean/oned/Vector1DTest.java | 44 +++++++++++++++ .../geometry/euclidean/threed/Vector3DTest.java | 62 ++++++++++++++++++++++ .../geometry/euclidean/twod/Vector2DTest.java | 52 ++++++++++++++++++ 7 files changed, 212 insertions(+) diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java index 00edec6..f767f97 100644 --- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java +++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java @@ -21,6 +21,7 @@ import java.io.Serializable; import org.apache.commons.geometry.core.Point; import org.apache.commons.geometry.core.Vector; import org.apache.commons.geometry.core.exception.IllegalNormException; +import org.apache.commons.geometry.core.precision.DoublePrecisionContext; import org.apache.commons.geometry.euclidean.internal.Vectors; /** Abstract base class for Euclidean vectors <em>and</em> points. See @@ -67,6 +68,35 @@ public abstract class EuclideanVector<V extends EuclideanVector<V>> */ public abstract V lerp(V v, double t); + /** Return true if the current instance and given vector are considered equal as evaluated by the + * given precision context. + * + * <p>Equality is determined by comparing each pair of components in turn from the two + * vectors. If all components evaluate as equal, then the vectors are considered equal. If any are + * not equal, then the vectors are not considered equal. Note that this approach means that the + * calculated distance between two "equal" vectors may be as much as <code>√(n * eps<sup>2</sup>)</code>, + * where {@code n} is the number of components in the vector and {@code eps} is the maximum epsilon + * value allowed by the precision context. + * @param v vector to check for equality + * @param precision precision context used to determine floating point equality + * @return true if the current instance is considered equal to the given vector when using + * the given precision context; otherwise false + */ + public abstract boolean equals(V v, DoublePrecisionContext precision); + + /** Return true if the current instance is considered equal to the zero vector as evaluated by the + * given precision context. This is a convenience method equivalent to + * {@code vec.equals(vec.getZero(), precision)}. + * + * @param precision precision context used to determine floating point equality + * @return true if the current instance is considered equal to the zero vector when using + * the given precision context; otherwise false + * @see #equals(EuclideanVector, DoublePrecisionContext) + */ + public boolean isZero(final DoublePrecisionContext precision) { + return equals(getZero(), precision); + } + /** Return the vector norm value, throwing an {@link IllegalNormException} if the value * is not real (ie, NaN or infinite) or zero. * @return the vector norm value, guaranteed to be real and non-zero 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 6556ca0..67a683a 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 @@ -19,6 +19,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.core.precision.DoublePrecisionContext; import org.apache.commons.geometry.euclidean.EuclideanVector; import org.apache.commons.geometry.euclidean.internal.Vectors; import org.apache.commons.numbers.arrays.LinearCombination; @@ -219,6 +220,12 @@ public class Vector1D extends EuclideanVector<Vector1D> { return transform.apply(this); } + /** {@inheritDoc} */ + @Override + public boolean equals(final Vector1D vec, final DoublePrecisionContext precision) { + return precision.areEqual(x, vec.x); + } + /** * Get a hashCode for the vector. * <p>All NaN values have the same hash code.</p> 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 b59793a..e350ef7 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 @@ -20,6 +20,7 @@ package org.apache.commons.geometry.euclidean.threed; import org.apache.commons.geometry.core.exception.IllegalNormException; import org.apache.commons.geometry.core.internal.DoubleFunction3N; import org.apache.commons.geometry.core.internal.SimpleTupleFormat; +import org.apache.commons.geometry.core.precision.DoublePrecisionContext; import org.apache.commons.geometry.euclidean.MultiDimensionalEuclideanVector; import org.apache.commons.geometry.euclidean.internal.Vectors; import org.apache.commons.numbers.arrays.LinearCombination; @@ -370,6 +371,14 @@ public class Vector3D extends MultiDimensionalEuclideanVector<Vector3D> { return transform.apply(this); } + /** {@inheritDoc} */ + @Override + public boolean equals(final Vector3D vec, final DoublePrecisionContext precision) { + return precision.areEqual(x, vec.x) && + precision.areEqual(y, vec.y) && + precision.areEqual(z, vec.z); + } + /** * Get a hashCode for the vector. * <p>All NaN values have the same hash code.</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 08b4f1f..582f66f 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 @@ -19,6 +19,7 @@ package org.apache.commons.geometry.euclidean.twod; import org.apache.commons.geometry.core.exception.IllegalNormException; import org.apache.commons.geometry.core.internal.DoubleFunction2N; import org.apache.commons.geometry.core.internal.SimpleTupleFormat; +import org.apache.commons.geometry.core.precision.DoublePrecisionContext; import org.apache.commons.geometry.euclidean.MultiDimensionalEuclideanVector; import org.apache.commons.geometry.euclidean.internal.Vectors; import org.apache.commons.numbers.arrays.LinearCombination; @@ -319,6 +320,13 @@ public class Vector2D extends MultiDimensionalEuclideanVector<Vector2D> { return transform.apply(this); } + /** {@inheritDoc} */ + @Override + public boolean equals(final Vector2D vec, final DoublePrecisionContext precision) { + return precision.areEqual(x, vec.x) && + precision.areEqual(y, vec.y); + } + /** * Get a hashCode for the 2D coordinates. * <p> 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 8aa06b7..e3b015d 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 @@ -21,6 +21,8 @@ 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.geometry.core.precision.DoublePrecisionContext; +import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext; import org.apache.commons.numbers.core.Precision; import org.junit.Assert; import org.junit.Test; @@ -464,6 +466,48 @@ public class Vector1DTest { } @Test + public void testPrecisionEquals() { + // arrange + DoublePrecisionContext smallEps = new EpsilonDoublePrecisionContext(1e-6); + DoublePrecisionContext largeEps = new EpsilonDoublePrecisionContext(1e-1); + + Vector1D vec = Vector1D.of(1); + + // act/assert + Assert.assertTrue(vec.equals(vec, smallEps)); + Assert.assertTrue(vec.equals(vec, largeEps)); + + Assert.assertTrue(vec.equals(Vector1D.of(1.0000007), smallEps)); + Assert.assertTrue(vec.equals(Vector1D.of(1.0000007), largeEps)); + + Assert.assertFalse(vec.equals(Vector1D.of(1.004), smallEps)); + Assert.assertTrue(vec.equals(Vector1D.of(1.004), largeEps)); + + Assert.assertFalse(vec.equals(Vector1D.of(2), smallEps)); + Assert.assertFalse(vec.equals(Vector1D.of(-2), largeEps)); + } + + @Test + public void testIsZero() { + // arrange + DoublePrecisionContext smallEps = new EpsilonDoublePrecisionContext(1e-6); + DoublePrecisionContext largeEps = new EpsilonDoublePrecisionContext(1e-1); + + // act/assert + Assert.assertTrue(Vector1D.of(0.0).isZero(smallEps)); + Assert.assertTrue(Vector1D.of(-0.0).isZero(largeEps)); + + Assert.assertTrue(Vector1D.of(1e-7).isZero(smallEps)); + Assert.assertTrue(Vector1D.of(-1e-7).isZero(largeEps)); + + Assert.assertFalse(Vector1D.of(1e-2).isZero(smallEps)); + Assert.assertTrue(Vector1D.of(-1e-2).isZero(largeEps)); + + Assert.assertFalse(Vector1D.of(0.2).isZero(smallEps)); + Assert.assertFalse(Vector1D.of(-0.2).isZero(largeEps)); + } + + @Test public void testHashCode() { // arrange Vector1D u = Vector1D.of(1); 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 c6243d5..95f6077 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 @@ -22,6 +22,8 @@ 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.geometry.core.precision.DoublePrecisionContext; +import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext; import org.apache.commons.geometry.euclidean.EuclideanTestUtils; import org.apache.commons.numbers.core.Precision; import org.apache.commons.rng.UniformRandomProvider; @@ -934,6 +936,66 @@ public class Vector3DTest { } @Test + public void testPrecisionEquals() { + // arrange + DoublePrecisionContext smallEps = new EpsilonDoublePrecisionContext(1e-6); + DoublePrecisionContext largeEps = new EpsilonDoublePrecisionContext(1e-1); + + Vector3D vec = Vector3D.of(1, -2, 3); + + // act/assert + Assert.assertTrue(vec.equals(vec, smallEps)); + Assert.assertTrue(vec.equals(vec, largeEps)); + + Assert.assertTrue(vec.equals(Vector3D.of(1.0000007, -2.0000009, 3.0000009), smallEps)); + Assert.assertTrue(vec.equals(Vector3D.of(1.0000007, -2.0000009, 3.0000009), largeEps)); + + Assert.assertFalse(vec.equals(Vector3D.of(1.004, -2, 3), smallEps)); + Assert.assertFalse(vec.equals(Vector3D.of(1, -2.004, 3), smallEps)); + Assert.assertFalse(vec.equals(Vector3D.of(1, -2, 2.999), smallEps)); + Assert.assertTrue(vec.equals(Vector3D.of(1.004, -2.004, 2.999), largeEps)); + + Assert.assertFalse(vec.equals(Vector3D.of(2, -2, 3), smallEps)); + Assert.assertFalse(vec.equals(Vector3D.of(1, -3, 3), smallEps)); + Assert.assertFalse(vec.equals(Vector3D.of(1, -2, 4), smallEps)); + Assert.assertFalse(vec.equals(Vector3D.of(2, -3, 4), smallEps)); + + Assert.assertFalse(vec.equals(Vector3D.of(2, -2, 3), largeEps)); + Assert.assertFalse(vec.equals(Vector3D.of(1, -3, 3), largeEps)); + Assert.assertFalse(vec.equals(Vector3D.of(1, -2, 4), largeEps)); + Assert.assertFalse(vec.equals(Vector3D.of(2, -3, 4), largeEps)); + } + + @Test + public void testIsZero() { + // arrange + DoublePrecisionContext smallEps = new EpsilonDoublePrecisionContext(1e-6); + DoublePrecisionContext largeEps = new EpsilonDoublePrecisionContext(1e-1); + + // act/assert + Assert.assertTrue(Vector3D.of(0.0, -0.0, 0.0).isZero(smallEps)); + Assert.assertTrue(Vector3D.of(-0.0, 0.0, -0.0).isZero(largeEps)); + + Assert.assertTrue(Vector3D.of(-1e-7, 1e-7, -1e-8).isZero(smallEps)); + Assert.assertTrue(Vector3D.of(1e-7, -1e-7, 1e-8).isZero(largeEps)); + + Assert.assertFalse(Vector3D.of(1e-2, 0.0, 0.0).isZero(smallEps)); + Assert.assertFalse(Vector3D.of(0.0, 1e-2, 0.0).isZero(smallEps)); + Assert.assertFalse(Vector3D.of(0.0, 0.0, 1e-2).isZero(smallEps)); + Assert.assertTrue(Vector3D.of(1e-2, -1e-2, 1e-2).isZero(largeEps)); + + Assert.assertFalse(Vector3D.of(0.2, 0.0, 0.0).isZero(smallEps)); + Assert.assertFalse(Vector3D.of(0.0, 0.2, 0.0).isZero(smallEps)); + Assert.assertFalse(Vector3D.of(0.0, 0.0, 0.2).isZero(smallEps)); + Assert.assertFalse(Vector3D.of(0.2, 0.2, 0.2).isZero(smallEps)); + + Assert.assertFalse(Vector3D.of(0.2, 0.0, 0.0).isZero(largeEps)); + Assert.assertFalse(Vector3D.of(0.0, 0.2, 0.0).isZero(largeEps)); + Assert.assertFalse(Vector3D.of(0.0, 0.0, 0.2).isZero(largeEps)); + Assert.assertFalse(Vector3D.of(0.2, 0.2, 0.2).isZero(largeEps)); + } + + @Test public void testHashCode() { // arrange double delta = 10 * Precision.EPSILON; 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 5a4c268..76564b4 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 @@ -21,6 +21,8 @@ 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.geometry.core.precision.DoublePrecisionContext; +import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext; import org.apache.commons.geometry.euclidean.EuclideanTestUtils; import org.apache.commons.numbers.core.Precision; import org.junit.Assert; @@ -741,6 +743,56 @@ public class Vector2DTest { } @Test + public void testPrecisionEquals() { + // arrange + DoublePrecisionContext smallEps = new EpsilonDoublePrecisionContext(1e-6); + DoublePrecisionContext largeEps = new EpsilonDoublePrecisionContext(1e-1); + + Vector2D vec = Vector2D.of(1, -2); + + // act/assert + Assert.assertTrue(vec.equals(vec, smallEps)); + Assert.assertTrue(vec.equals(vec, largeEps)); + + Assert.assertTrue(vec.equals(Vector2D.of(1.0000007, -2.0000009), smallEps)); + Assert.assertTrue(vec.equals(Vector2D.of(1.0000007, -2.0000009), largeEps)); + + Assert.assertFalse(vec.equals(Vector2D.of(1.004, -2), smallEps)); + Assert.assertFalse(vec.equals(Vector2D.of(1, -2.004), smallEps)); + Assert.assertTrue(vec.equals(Vector2D.of(1.004, -2.004), largeEps)); + + Assert.assertFalse(vec.equals(Vector2D.of(1, -3), smallEps)); + Assert.assertFalse(vec.equals(Vector2D.of(2, -2), smallEps)); + Assert.assertFalse(vec.equals(Vector2D.of(1, -3), largeEps)); + Assert.assertFalse(vec.equals(Vector2D.of(2, -2), largeEps)); + } + + @Test + public void testIsZero() { + // arrange + DoublePrecisionContext smallEps = new EpsilonDoublePrecisionContext(1e-6); + DoublePrecisionContext largeEps = new EpsilonDoublePrecisionContext(1e-1); + + // act/assert + Assert.assertTrue(Vector2D.of(0.0, -0.0).isZero(smallEps)); + Assert.assertTrue(Vector2D.of(-0.0, 0.0).isZero(largeEps)); + + Assert.assertTrue(Vector2D.of(-1e-7, 1e-7).isZero(smallEps)); + Assert.assertTrue(Vector2D.of(1e-7, 1e-7).isZero(largeEps)); + + Assert.assertFalse(Vector2D.of(1e-2, 0.0).isZero(smallEps)); + Assert.assertFalse(Vector2D.of(0.0, 1e-2).isZero(smallEps)); + Assert.assertTrue(Vector2D.of(1e-2, -1e-2).isZero(largeEps)); + + Assert.assertFalse(Vector2D.of(0.2, 0.0).isZero(smallEps)); + Assert.assertFalse(Vector2D.of(0.0, 0.2).isZero(smallEps)); + Assert.assertFalse(Vector2D.of(0.2, 0.2).isZero(smallEps)); + Assert.assertFalse(Vector2D.of(-0.2, 0.0).isZero(largeEps)); + Assert.assertFalse(Vector2D.of(0.0, -0.2).isZero(largeEps)); + Assert.assertFalse(Vector2D.of(-0.2, -0.2).isZero(largeEps)); + } + + @Test public void testHashCode() { // arrange Vector2D u = Vector2D.of(1, 1);
