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 2019/02/10 10:31:07 UTC
[commons-geometry] 01/03: GEOMETRY-41: Adding
equals(EuclideanVector,
DoublePrecisionContext) and isZero(DoublePrecisionContext) methods to
EuclideanVector and subclasses
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 <ma...@hotmail.com>
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);