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 85d2cddc9f2d8d3870b4622d1f96c54ff128cf66 Merge: fb028f0 2e6fc6a Author: Matt Juntunen <matt.juntu...@hotmail.com> AuthorDate: Sat Sep 15 15:52:08 2018 -0400 merging with master .../org/apache/commons/geometry/core/Vector.java | 18 +- .../GeometryException.java} | 23 +- .../IllegalNormException.java} | 29 ++- .../core/{util => exception}/package-info.java | 4 +- .../core/internal/GeometryInternalError.java | 38 +++ .../geometry/core/internal/SimpleTupleFormat.java | 20 +- .../commons/geometry/core/GeometryTestUtils.java | 32 +++ .../commons/geometry/enclosing/WelzlEncloser.java | 3 +- .../geometry/enclosing/WelzlEncloser3DTest.java | 2 +- .../geometry/euclidean/internal}/Vectors.java | 31 ++- .../commons/geometry/euclidean/oned/Point1D.java | 14 +- .../commons/geometry/euclidean/oned/Vector1D.java | 63 ++--- .../commons/geometry/euclidean/threed/Line.java | 2 +- .../commons/geometry/euclidean/threed/Plane.java | 11 +- .../commons/geometry/euclidean/threed/Point3D.java | 16 +- .../geometry/euclidean/threed/Rotation.java | 213 +++++++++------- .../commons/geometry/euclidean/threed/SubLine.java | 2 +- .../geometry/euclidean/threed/Vector3D.java | 139 +++------- .../geometry/euclidean/twod/NestedLoops.java | 4 +- .../commons/geometry/euclidean/twod/Point2D.java | 16 +- .../commons/geometry/euclidean/twod/Vector2D.java | 103 ++------ .../geometry/euclidean/internal}/VectorsTest.java | 39 ++- .../geometry/euclidean/oned/Point1DTest.java | 26 -- .../geometry/euclidean/oned/Vector1DTest.java | 111 ++++---- .../geometry/euclidean/threed/Point3DTest.java | 26 -- .../geometry/euclidean/threed/RotationTest.java | 17 +- .../geometry/euclidean/threed/Vector3DTest.java | 282 ++++++++------------- .../geometry/euclidean/twod/Point2DTest.java | 26 -- .../geometry/euclidean/twod/Vector2DTest.java | 192 +++++--------- .../geometry/euclidean/twod/hull/ConvexHull2D.java | 2 +- .../commons/geometry/spherical/oned/ArcsSet.java | 52 +--- .../commons/geometry/spherical/oned/S1Point.java | 2 +- .../geometry/spherical/twod/EdgesBuilder.java | 6 +- .../spherical/twod/PropertiesComputer.java | 7 +- .../commons/geometry/spherical/twod/S2Point.java | 2 +- .../spherical/twod/SphericalPolygonsSet.java | 8 +- .../commons/geometry/spherical/twod/SubCircle.java | 3 +- .../geometry/spherical/oned/ArcsSetTest.java | 2 +- .../geometry/spherical/twod/CircleTest.java | 32 ++- .../spherical/twod/SphericalPolygonsSetTest.java | 6 +- 40 files changed, 710 insertions(+), 914 deletions(-) diff --cc commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java index 61c73d6,4b96f43..cb66b1c --- 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 @@@ -16,11 -16,10 +16,12 @@@ */ 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.util.Vectors; import org.apache.commons.geometry.euclidean.EuclideanVector; - import org.apache.commons.geometry.euclidean.internal.ZeroNormException; + import org.apache.commons.geometry.euclidean.internal.Vectors; import org.apache.commons.numbers.arrays.LinearCombination; /** This class represents a vector in three-dimensional Euclidean space. @@@ -189,8 -188,8 +190,8 @@@ public class Vector3D extends Cartesian /** {@inheritDoc} */ @Override - public Vector3D normalize() throws IllegalStateException { + public Vector3D normalize() { - return scalarMultiply(1.0 / getFiniteNonZeroNorm()); + return normalize(getX(), getY(), getZ()); } /** Get a vector orthogonal to the instance. @@@ -226,19 -226,6 +228,19 @@@ return new Vector3D(inverse * y, -inverse * x, 0); } + /** Returns a unit vector orthogonal to the current vector and pointing in the direction + * of {@code dir}. This method is equivalent to calling {@code dir.reject(vec).normalize()} + * except that no intermediate vector object is produced. + * @param dir the direction to use for generating the orthogonal vector + * @return unit vector orthogonal to the current vector and pointing in the direction of + * {@code dir} that does not lie along the current vector - * @throws IllegalStateException if the norm of the current vector is zero or the given - * vector is collinear with this vector. ++ * @throws IllegalNormException if either vector norm is zero, NaN or infinite, ++ * or the given vector is collinear with this vector. + */ - public Vector3D orthogonal(Vector3D dir) throws IllegalStateException { ++ public Vector3D orthogonal(Vector3D dir) { + return dir.getComponent(this, true, Vector3D::normalize); + } + /** {@inheritDoc} * <p>This method computes the angular separation between two * vectors using the dot product for well separated vectors and the @@@ -336,14 -323,14 +338,14 @@@ /** {@inheritDoc} */ @Override - public Vector3D project(Vector3D base) throws IllegalStateException { + public Vector3D project(Vector3D base) { - return getComponent(base, false); + return getComponent(base, false, Vector3D::new); } /** {@inheritDoc} */ @Override - public Vector3D reject(Vector3D base) throws IllegalStateException { + public Vector3D reject(Vector3D base) { - return getComponent(base, true); + return getComponent(base, true, Vector3D::new); } /** @@@ -416,87 -399,28 +414,35 @@@ * @param reject If true, the rejection of this instance from {@code base} is * returned. If false, the projection of this instance onto {@code base} * is returned. + * @param factory factory function used to build the final vector * @return The projection or rejection of this instance relative to {@code base}, * depending on the value of {@code reject}. - * @throws IllegalStateException if {@code base} has a zero norm + * @throws IllegalNormException if {@code base} has a zero, NaN, or infinite norm */ - private Vector3D getComponent(Vector3D base, boolean reject, DoubleFunction3N<Vector3D> factory) throws IllegalStateException { - private Vector3D getComponent(Vector3D base, boolean reject) { ++ private Vector3D getComponent(Vector3D base, boolean reject, DoubleFunction3N<Vector3D> factory) { final double aDotB = dotProduct(base); - final double baseMagSq = base.getNormSq(); - if (baseMagSq == 0.0) { - throw new ZeroNormException(ZeroNormException.INVALID_BASE); - } - final double baseMag = Vectors.ensureFiniteNonZeroNorm(base.getNorm()); ++ // 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 ++ // 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()); - final double scale = aDotB / (baseMag * baseMag); + final double scale = aDotB / baseMagSq; final double projX = scale * base.getX(); final double projY = scale * base.getY(); final double projZ = scale * base.getZ(); if (reject) { - return new Vector3D(getX() - projX, getY() - projY, getZ() - projZ); + return factory.apply(getX() - projX, getY() - projY, getZ() - projZ); } - return new Vector3D(projX, projY, projZ); + return factory.apply(projX, projY, projZ); } - /** Computes the dot product between to vectors. This method simply - * calls {@code v1.dotProduct(v2)}. - * @param v1 first vector - * @param v2 second vector - * @return the dot product - * @see #dotProduct(Vector3D) - */ - public static double dotProduct(Vector3D v1, Vector3D v2) { - return v1.dotProduct(v2); - } - - /** Computes the angle in radians between two vectors. This method - * simply calls {@code v1.angle(v2)}. - * @param v1 first vector - * @param v2 second vector - * @return the angle between the vectors in radians - * @see #angle(Vector3D) - */ - public static double angle(Vector3D v1, Vector3D v2) { - return v1.angle(v2); - } - - /** Projects the given vector onto {@code base}. This method simply - * calls {@code v.project(base)}. - * @param v vector to project - * @param base the base vector to project onto - * @return the projected vector - * @see #project(Vector3D) - */ - public static Vector3D project(Vector3D v, Vector3D base) { - return v.project(base); - } - - /** Returns the vector rejection of {@code v} from {@code base}. This - * method simply calls {@code v.reject(base)}. - * @param v vector to reject - * @param base the base vector to reject from - * @return the vector rejection - * @see #reject(Vector3D) - */ - public static Vector3D reject(Vector3D v, Vector3D base) { - return v.reject(base); - } - - /** Computes the cross product between two vectors. This method simply - * calls {@code v1.crossProduct(v2)}. - * @param v1 first vector - * @param v2 second vector - * @return the computed cross product vector - * @see #crossProduct(Vector3D) - */ - public static Vector3D crossProduct(Vector3D v1, Vector3D v2) { - return v1.crossProduct(v2); - } - /** Returns a vector with the given coordinate values. * @param x abscissa (first coordinate value) * @param y abscissa (second coordinate value) @@@ -530,23 -454,6 +476,20 @@@ return SphericalCoordinates.toCartesian(radius, azimuth, polar, Vector3D::new); } + /** Returns a normalized vector derived from the given values. + * @param x abscissa (first coordinate value) + * @param y abscissa (second coordinate value) + * @param z height (third coordinate value) + * @return normalized vector instance - * @throws IllegalStateException if the norm of the given values is zero ++ * @throws IllegalNormException if the norm of the given values is zero + */ - public static Vector3D normalize(final double x, final double y, final double z) throws IllegalStateException { - final double norm = Vectors.norm(x, y, z); - if (norm == 0.0) { - throw new ZeroNormException(); - } ++ public static Vector3D normalize(final double x, final double y, final double z) { ++ final double norm = Vectors.ensureFiniteNonZeroNorm(Vectors.norm(x, y, z)); + final double invNorm = 1.0 / norm; + + return new UnitVector(x * invNorm, y * invNorm, z * 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 diff --cc commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java index e0de1c5,4d34a91..48c4524 --- 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 @@@ -157,20 -144,19 +159,27 @@@ public class Vector3DTest checkVector(Vector3D.of(x, y, z).withMagnitude(0.5), 0.5 * normX, 0.5 * normY, 0.5 * normZ); checkVector(Vector3D.of(x, y, z).withMagnitude(3), 3 * normX, 3 * normY, 3 * normZ); + + checkVector(Vector3D.of(x, y, z).withMagnitude(-0.5), -0.5 * normX, -0.5 * normY, -0.5 * normZ); + checkVector(Vector3D.of(x, y, z).withMagnitude(-3), -3 * normX, -3 * normY, -3 * normZ); + + for (double mag = -10.0; mag <= 10.0; ++mag) + { + Assert.assertEquals(Math.abs(mag), Vector3D.of(x, y, z).withMagnitude(mag).getMagnitude(), EPS); + } } - @Test(expected = IllegalStateException.class) - public void testWithMagnitude_zeroNorm() { + @Test + public void testWithMagnitude_illegalNorm() { // act/assert - Vector3D.ZERO.withMagnitude(1.0); + GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.withMagnitude(2.0), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector3D.NaN.withMagnitude(2.0), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector3D.POSITIVE_INFINITY.withMagnitude(2.0), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector3D.NEGATIVE_INFINITY.withMagnitude(2.0), + IllegalNormException.class); } @Test @@@ -286,13 -256,20 +295,20 @@@ checkVector(Vector3D.of(2, 2, 2).normalize(), invSqrt3, invSqrt3, invSqrt3); checkVector(Vector3D.of(-2, -2, -2).normalize(), -invSqrt3, -invSqrt3, -invSqrt3); - Assert.assertEquals(1.0, Vector3D.of(5, -4, 2).normalize().getNorm(), 1.0e-12); + Assert.assertEquals(1.0, Vector3D.of(5, -4, 2).normalize().getNorm(), EPS); } - @Test(expected = IllegalStateException.class) - public void testNormalize_zeroNorm() { + @Test + public void testNormalize_illegalNorm() { // act/assert - Vector3D.ZERO.normalize(); + 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); } @Test @@@ -328,31 -301,6 +351,52 @@@ } @Test + public void testOrthogonal_givenDirection() { + // arrange + double invSqrt2 = 1.0 / Math.sqrt(2.0); + + // act/assert + checkVector(Vector3D.PLUS_X.orthogonal(Vector3D.of(-1.0, 0.1, 0.0)), 0.0, 1.0, 0.0); + checkVector(Vector3D.PLUS_Y.orthogonal(Vector3D.of(2.0, 2.0, 2.0)), invSqrt2, 0.0, invSqrt2); + checkVector(Vector3D.PLUS_Z.orthogonal(Vector3D.of(3.0, 3.0, -3.0)), invSqrt2, invSqrt2, 0.0); + + checkVector(Vector3D.of(invSqrt2, invSqrt2, 0.0).orthogonal(Vector3D.of(1.0, 1.0, 0.2)), 0.0, 0.0, 1.0); + } + - @Test(expected = IllegalStateException.class) - public void testOrthogonal_givenDirection_zeroNorm() { ++ @Test ++ public void testOrthogonal_givenDirection_illegalNorm() { + // act/assert - Vector3D.ZERO.orthogonal(Vector3D.PLUS_X); ++ GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.orthogonal(Vector3D.PLUS_X), ++ IllegalNormException.class); ++ GeometryTestUtils.assertThrows(() -> Vector3D.NaN.orthogonal(Vector3D.PLUS_X), ++ IllegalNormException.class); ++ GeometryTestUtils.assertThrows(() -> Vector3D.POSITIVE_INFINITY.orthogonal(Vector3D.PLUS_X), ++ IllegalNormException.class); ++ GeometryTestUtils.assertThrows(() -> Vector3D.NEGATIVE_INFINITY.orthogonal(Vector3D.PLUS_X), ++ IllegalNormException.class); ++ ++ GeometryTestUtils.assertThrows(() -> Vector3D.PLUS_X.orthogonal(Vector3D.ZERO), ++ IllegalNormException.class); ++ GeometryTestUtils.assertThrows(() -> Vector3D.PLUS_X.orthogonal(Vector3D.NaN), ++ IllegalNormException.class); ++ GeometryTestUtils.assertThrows(() -> Vector3D.PLUS_X.orthogonal(Vector3D.POSITIVE_INFINITY), ++ IllegalNormException.class); ++ GeometryTestUtils.assertThrows(() -> Vector3D.PLUS_X.orthogonal(Vector3D.NEGATIVE_INFINITY), ++ IllegalNormException.class); + } + - @Test(expected = IllegalStateException.class) ++ @Test + public void testOrthogonal_givenDirection_directionIsCollinear() { + // act/assert - Vector3D.PLUS_X.orthogonal(Vector3D.of(-2.0, 0.0, 0.0)); ++ GeometryTestUtils.assertThrows(() -> Vector3D.PLUS_X.orthogonal(Vector3D.PLUS_X), ++ 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)), ++ IllegalNormException.class); + } + + @Test public void testAngle() { // arrange double tolerance = 1e-10; @@@ -1069,21 -904,6 +1000,28 @@@ } @Test + public void testNormalize_static() { + // arrange + double invSqrt3 = 1.0 / Math.sqrt(3.0); + + // act/assert + checkVector(Vector3D.normalize(2.0, -2.0, 2.0), invSqrt3, -invSqrt3, invSqrt3); + checkVector(Vector3D.normalize(-4.0, 4.0, -4.0), -invSqrt3, invSqrt3, -invSqrt3); + } + - @Test(expected = IllegalStateException.class) - public void testNormalize_static_zeroNorm() { - Vector3D.normalize(0.0, 0.0, 0.0); ++ @Test ++ public void testNormalize_static_illegalNorm() { ++ GeometryTestUtils.assertThrows(() -> Vector3D.normalize(0.0, 0.0, 0.0), ++ IllegalNormException.class); ++ GeometryTestUtils.assertThrows(() -> Vector3D.normalize(Double.NaN, 1.0, 1.0), ++ IllegalNormException.class); ++ GeometryTestUtils.assertThrows(() -> Vector3D.normalize(1.0, Double.NEGATIVE_INFINITY, 1.0), ++ IllegalNormException.class); ++ GeometryTestUtils.assertThrows(() -> Vector3D.normalize(1.0, 1.0, Double.POSITIVE_INFINITY), ++ IllegalNormException.class); + } + + @Test public void testLinearCombination1() { // arrange Vector3D p1 = Vector3D.of(1, 2, 3);