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 b7f391ac5c53503239b063f2d9066d5886880a89 Author: Matt Juntunen <matt.juntu...@hotmail.com> AuthorDate: Mon Sep 10 22:21:05 2018 -0400 GEOMETRY-8: adding initial exception hierarchy; using IllegalNormException in Vector classes --- .../org/apache/commons/geometry/core/Vector.java | 18 ++-- .../geometry/core/exception/GeometryException.java | 32 +++++++ .../core/exception/GeometryStateException.java | 30 +++++++ .../core/exception/GeometryValueException.java | 30 +++++++ .../core/exception/IllegalNormException.java | 34 ++++++++ .../geometry/core/exception/package-info.java | 23 +++++ .../commons/geometry/core/GeometryTestUtils.java | 32 +++++++ .../geometry/euclidean}/internal/Vectors.java | 21 ++++- .../commons/geometry/euclidean/oned/Vector1D.java | 50 +++++------ .../commons/geometry/euclidean/threed/Point3D.java | 2 +- .../geometry/euclidean/threed/Rotation.java | 85 +++++++++---------- .../geometry/euclidean/threed/Vector3D.java | 52 +++++------- .../commons/geometry/euclidean/twod/Point2D.java | 2 +- .../commons/geometry/euclidean/twod/Vector2D.java | 45 +++++----- .../geometry/euclidean}/internal/VectorsTest.java | 26 +++++- .../geometry/euclidean/oned/Vector1DTest.java | 89 +++++++++++++++----- .../geometry/euclidean/threed/RotationTest.java | 3 +- .../geometry/euclidean/threed/Vector3DTest.java | 98 ++++++++++++++++++---- .../geometry/euclidean/twod/Vector2DTest.java | 87 +++++++++++++++---- 19 files changed, 558 insertions(+), 201 deletions(-) diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java index d4eb296..b04d644 100644 --- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java +++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java @@ -16,6 +16,8 @@ */ package org.apache.commons.geometry.core; +import org.apache.commons.geometry.core.exception.IllegalNormException; + /** Interface representing a vector in a vector space. The most common * use of this interface is to represent displacement vectors in an affine * space. @@ -120,9 +122,9 @@ public interface Vector<V extends Vector<V>> extends Spatial { /** Get a normalized vector aligned with the instance. The returned * vector has a magnitude of 1. * @return a new normalized vector - * @exception IllegalStateException if the norm is zero + * @exception IllegalNormException if the norm is zero, NaN, or infinite */ - V normalize() throws IllegalStateException; + V normalize() throws IllegalNormException; /** Multiply the instance by a scalar. * @param a scalar @@ -180,10 +182,10 @@ public interface Vector<V extends Vector<V>> extends Spatial { * </code> * @param base base vector * @return the vector projection of the instance onto {@code base} - * @exception IllegalStateException if the norm of the base vector is zero + * @exception IllegalNormException if the norm of the base vector is zero, NaN, or infinite * @see #reject(Vector) */ - V project(V base) throws IllegalStateException; + V project(V base) throws IllegalNormException; /** Get the rejection of the instance from the given base vector. The returned * vector is orthogonal to {@code base}. This operation can be interpreted as @@ -195,15 +197,15 @@ public interface Vector<V extends Vector<V>> extends Spatial { * </code> * @param base base vector * @return the vector rejection of the instance from {@code base} - * @exception IllegalStateException if the norm of the base vector is zero + * @exception IllegalNormException if the norm of the base vector is zero, NaN, or infinite * @see #project(Vector) */ - V reject(V base) throws IllegalStateException; + V reject(V base) throws IllegalNormException; /** Compute the angular separation between two vectors in radians. * @param v other vector * @return angular separation between this instance and v in radians - * @exception IllegalStateException if either vector has a zero norm + * @exception IllegalNormException if either vector has a zero, NaN, or infinite norm */ - double angle(V v) throws IllegalStateException; + double angle(V v) throws IllegalNormException; } diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryException.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryException.java new file mode 100644 index 0000000..48e4b2a --- /dev/null +++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryException.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.geometry.core.exception; + +/** Base class for geometry exceptions. + */ +public class GeometryException extends RuntimeException { + + /** Serializable version identifier */ + private static final long serialVersionUID = 20180909L; + + /** Simple constructor with error message. + * @param msg exception message string + */ + public GeometryException(String msg) { + super(msg); + } +} diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryStateException.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryStateException.java new file mode 100644 index 0000000..4a8080e --- /dev/null +++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryStateException.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.geometry.core.exception; + +public class GeometryStateException extends GeometryException { + + /** Serializable version identifier */ + private static final long serialVersionUID = 20180909L; + + /** Simple constructor with error message. + * @param msg exception message string + */ + public GeometryStateException(String msg) { + super(msg); + } +} diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryValueException.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryValueException.java new file mode 100644 index 0000000..5d7b0d6 --- /dev/null +++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryValueException.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.geometry.core.exception; + +public class GeometryValueException extends GeometryException { + + /** Serializable version identifier */ + private static final long serialVersionUID = 20180909L; + + /** Simple constructor with error message. + * @param msg exception message string + */ + public GeometryValueException(String msg) { + super(msg); + } +} diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/IllegalNormException.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/IllegalNormException.java new file mode 100644 index 0000000..b0d80e8 --- /dev/null +++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/IllegalNormException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.geometry.core.exception; + +/** Exception thrown when an illegal vector norm value is encountered + * in an operation. + */ +public class IllegalNormException extends GeometryValueException { + + /** Serializable version identifier */ + private static final long serialVersionUID = 20180909L; + + /** + * Simple constructor accepting the illegal norm value. + * @param norm the illegal norm value + */ + public IllegalNormException(double norm) { + super("Illegal norm: " + norm); + } +} diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/package-info.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/package-info.java new file mode 100644 index 0000000..8180f81 --- /dev/null +++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * + * <p> + * This package contains exception types used by this library. + * </p> + */ +package org.apache.commons.geometry.core.exception; diff --git a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTestUtils.java b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTestUtils.java index 3dbf58a..60bdeea 100644 --- a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTestUtils.java +++ b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTestUtils.java @@ -45,6 +45,38 @@ public class GeometryTestUtils { Assert.assertTrue(msg, value < 0); } + /** Asserts that the given Runnable throws an exception of the given type. + * @param r the Runnable instance + * @param exceptionType the expected exception type + */ + public static void assertThrows(Runnable r, Class<?> exceptionType) { + assertThrows(r, exceptionType, null); + } + + /** Asserts that the given Runnable throws an exception of the given type. If + * {@code message} is not null, the exception message is asserted to equal the + * given value. + * @param r the Runnable instance + * @param exceptionType the expected exception type + * @param message the expected exception message; ignored if null + */ + public static void assertThrows(Runnable r, Class<?> exceptionType, String message) { + try { + r.run(); + Assert.fail("Operation should have thrown an exception"); + } + catch (Exception exc) { + Class<?> actualType = exc.getClass(); + + Assert.assertTrue("Expected exception of type " + exceptionType.getName() + " but was " + actualType.getName(), + exceptionType.isAssignableFrom(actualType)); + + if (message != null) { + Assert.assertEquals(message, exc.getMessage()); + } + } + } + /** * Serializes and then recovers an object from a byte array. Returns the deserialized object. * diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/internal/Vectors.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/Vectors.java similarity index 91% rename from commons-geometry-core/src/main/java/org/apache/commons/geometry/core/internal/Vectors.java rename to commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/Vectors.java index 673d53b..cbb19b2 100644 --- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/internal/Vectors.java +++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/Vectors.java @@ -14,7 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.commons.geometry.core.internal; +package org.apache.commons.geometry.euclidean.internal; + +import org.apache.commons.geometry.core.exception.IllegalNormException; /** This class consists exclusively of static vector utility methods. */ @@ -23,6 +25,23 @@ public final class Vectors { /** Private constructor. */ private Vectors() {} + + /** Throws an {@link IllegalNormException} if the given norm value + * is not finite (ie, NaN or infinite) or zero. The argument is returned + * to allow this method to be called in-line. + * @param norm vector norm value + * @return the validated norm value + * @throws IllegalNormException if the given norm value is NaN, infinite, + * or zero + */ + public static double checkFiniteNonZeroNorm(final double norm) throws IllegalNormException { + if (!Double.isFinite(norm) || norm == 0.0) { + throw new IllegalNormException(norm); + } + + return norm; + } + /** Get the L<sub>1</sub> norm for the vector with the given components. * This is defined as the sum of the absolute values of all vector components. * @param x vector component 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 df40670..c5d455d 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,10 +17,10 @@ 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.internal.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 one-dimensional Euclidean space. @@ -117,14 +117,9 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public Vector1D withMagnitude(double magnitude) { - final double x = getX(); - if (x > 0.0) { - return new Vector1D(magnitude); - } - else if (x < 0.0) { - return new Vector1D(-magnitude); - } - throw new ZeroNormException(); + Vectors.checkFiniteNonZeroNorm(getNorm()); + + return (getX() > 0.0)? new Vector1D(magnitude) : new Vector1D(-magnitude); } /** {@inheritDoc} */ @@ -159,15 +154,10 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point /** {@inheritDoc} */ @Override - public Vector1D normalize() throws IllegalStateException { - final double x = getX(); - if (x > 0.0) { - return ONE; - } - else if (x < 0.0) { - return MINUS_ONE; - } - throw new ZeroNormException(); + public Vector1D normalize() throws IllegalNormException { + Vectors.checkFiniteNonZeroNorm(getNorm()); + + return (getX() > 0.0) ? ONE : MINUS_ONE; } /** {@inheritDoc} */ @@ -210,10 +200,9 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point * <p>For the one-dimensional case, this method simply returns the current instance.</p> */ @Override - public Vector1D project(final Vector1D base) throws IllegalStateException { - if (base.getX() == 0) { - throw new ZeroNormException(ZeroNormException.INVALID_BASE); - } + public Vector1D project(final Vector1D base) throws IllegalNormException { + Vectors.checkFiniteNonZeroNorm(base.getNorm()); + return this; } @@ -221,10 +210,9 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point * <p>For the one-dimensional case, this method simply returns the zero vector.</p> */ @Override - public Vector1D reject(final Vector1D base) throws IllegalStateException { - if (base.getX() == 0) { - throw new ZeroNormException(ZeroNormException.INVALID_BASE); - } + public Vector1D reject(final Vector1D base) throws IllegalNormException { + Vectors.checkFiniteNonZeroNorm(base.getNorm()); + return Vector1D.ZERO; } @@ -233,13 +221,13 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point * the same sign and {@code pi} if they are opposite.</p> */ @Override - public double angle(final Vector1D v) throws IllegalStateException { + public double angle(final Vector1D v) throws IllegalNormException { + Vectors.checkFiniteNonZeroNorm(getNorm()); + Vectors.checkFiniteNonZeroNorm(v.getNorm()); + final double sig1 = Math.signum(getX()); final double sig2 = Math.signum(v.getX()); - if (sig1 == 0 || sig2 == 0) { - throw new ZeroNormException(); - } // the angle is 0 if the x value signs are the same and pi if not return (sig1 == sig2) ? 0.0 : Geometry.PI; } 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 8e15230..b9a4e3e 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 @@ -18,8 +18,8 @@ package org.apache.commons.geometry.euclidean.threed; import org.apache.commons.geometry.core.internal.SimpleTupleFormat; -import org.apache.commons.geometry.core.internal.Vectors; import org.apache.commons.geometry.euclidean.EuclideanPoint; +import org.apache.commons.geometry.euclidean.internal.Vectors; import org.apache.commons.numbers.arrays.LinearCombination; /** This class represents a point in three-dimensional Euclidean space. diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java index 272d5c0..7ab3ec3 100644 --- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java +++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java @@ -19,6 +19,7 @@ package org.apache.commons.geometry.euclidean.threed; import java.io.Serializable; +import org.apache.commons.geometry.core.exception.IllegalNormException; import org.apache.commons.numbers.arrays.LinearCombination; /** @@ -264,54 +265,48 @@ public class Rotation implements Serializable { * @param u2 second vector of the origin pair * @param v1 desired image of u1 by the rotation * @param v2 desired image of u2 by the rotation - * @exception IllegalArgumentException if the norm of one of the vectors is zero, - * or if one of the pair is degenerated (i.e. the vectors of the pair are collinear) + * @exception IllegalNormException if the norm of one of the vectors is zero, NaN, infinite, + * or if one of the pair is degenerated (i.e. the vectors of the pair are collinear) */ public Rotation(Vector3D u1, Vector3D u2, Vector3D v1, Vector3D v2) - throws IllegalArgumentException { - - try { - // build orthonormalized base from u1, u2 - // this fails when vectors are null or collinear, which is forbidden to define a rotation - final Vector3D u3 = u1.crossProduct(u2).normalize(); - u2 = u3.crossProduct(u1).normalize(); - u1 = u1.normalize(); - - // build an orthonormalized base from v1, v2 - // this fails when vectors are null or collinear, which is forbidden to define a rotation - final Vector3D v3 = v1.crossProduct(v2).normalize(); - v2 = v3.crossProduct(v1).normalize(); - v1 = v1.normalize(); - - // buid a matrix transforming the first base into the second one - final double[][] m = new double[][] { - { - LinearCombination.value(u1.getX(), v1.getX(), u2.getX(), v2.getX(), u3.getX(), v3.getX()), - LinearCombination.value(u1.getY(), v1.getX(), u2.getY(), v2.getX(), u3.getY(), v3.getX()), - LinearCombination.value(u1.getZ(), v1.getX(), u2.getZ(), v2.getX(), u3.getZ(), v3.getX()) - }, - { - LinearCombination.value(u1.getX(), v1.getY(), u2.getX(), v2.getY(), u3.getX(), v3.getY()), - LinearCombination.value(u1.getY(), v1.getY(), u2.getY(), v2.getY(), u3.getY(), v3.getY()), - LinearCombination.value(u1.getZ(), v1.getY(), u2.getZ(), v2.getY(), u3.getZ(), v3.getY()) - }, - { - LinearCombination.value(u1.getX(), v1.getZ(), u2.getX(), v2.getZ(), u3.getX(), v3.getZ()), - LinearCombination.value(u1.getY(), v1.getZ(), u2.getY(), v2.getZ(), u3.getY(), v3.getZ()), - LinearCombination.value(u1.getZ(), v1.getZ(), u2.getZ(), v2.getZ(), u3.getZ(), v3.getZ()) - } - }; - - double[] quat = mat2quat(m); - q0 = quat[0]; - q1 = quat[1]; - q2 = quat[2]; - q3 = quat[3]; - - } catch (IllegalStateException exc) { - throw new IllegalArgumentException("Invalid rotation vector pairs", exc); - } + throws IllegalNormException { + + // build orthonormalized base from u1, u2 + // this fails when vectors are null or collinear, which is forbidden to define a rotation + final Vector3D u3 = u1.crossProduct(u2).normalize(); + u2 = u3.crossProduct(u1).normalize(); + u1 = u1.normalize(); + + // build an orthonormalized base from v1, v2 + // this fails when vectors are null or collinear, which is forbidden to define a rotation + final Vector3D v3 = v1.crossProduct(v2).normalize(); + v2 = v3.crossProduct(v1).normalize(); + v1 = v1.normalize(); + + // buid a matrix transforming the first base into the second one + final double[][] m = new double[][] { + { + LinearCombination.value(u1.getX(), v1.getX(), u2.getX(), v2.getX(), u3.getX(), v3.getX()), + LinearCombination.value(u1.getY(), v1.getX(), u2.getY(), v2.getX(), u3.getY(), v3.getX()), + LinearCombination.value(u1.getZ(), v1.getX(), u2.getZ(), v2.getX(), u3.getZ(), v3.getX()) + }, + { + LinearCombination.value(u1.getX(), v1.getY(), u2.getX(), v2.getY(), u3.getX(), v3.getY()), + LinearCombination.value(u1.getY(), v1.getY(), u2.getY(), v2.getY(), u3.getY(), v3.getY()), + LinearCombination.value(u1.getZ(), v1.getY(), u2.getZ(), v2.getY(), u3.getZ(), v3.getY()) + }, + { + LinearCombination.value(u1.getX(), v1.getZ(), u2.getX(), v2.getZ(), u3.getX(), v3.getZ()), + LinearCombination.value(u1.getY(), v1.getZ(), u2.getY(), v2.getZ(), u3.getY(), v3.getZ()), + LinearCombination.value(u1.getZ(), v1.getZ(), u2.getZ(), v2.getZ(), u3.getZ(), v3.getZ()) + } + }; + double[] quat = mat2quat(m); + q0 = quat[0]; + q1 = quat[1]; + q2 = quat[2]; + q3 = quat[3]; } /** Build one of the rotations that transform one vector into another one. 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 fd52558..d8de801 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 @@ -16,10 +16,10 @@ */ package org.apache.commons.geometry.euclidean.threed; +import org.apache.commons.geometry.core.exception.IllegalNormException; import org.apache.commons.geometry.core.internal.SimpleTupleFormat; -import org.apache.commons.geometry.core.internal.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. @@ -131,7 +131,7 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public Vector3D withMagnitude(double magnitude) { - final double invNorm = 1.0 / getNonZeroNorm(); + final double invNorm = 1.0 / getFiniteNonZeroNorm(); return new Vector3D( magnitude * getX() * invNorm, @@ -188,8 +188,8 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point /** {@inheritDoc} */ @Override - public Vector3D normalize() throws IllegalStateException { - return scalarMultiply(1.0 / getNonZeroNorm()); + public Vector3D normalize() throws IllegalNormException { + return scalarMultiply(1.0 / getFiniteNonZeroNorm()); } /** Get a vector orthogonal to the instance. @@ -205,10 +205,11 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point * Vector3D j = Vector3D.crossProduct(k, i); * </code></pre> * @return a new normalized vector orthogonal to the instance - * @exception IllegalStateException if the norm of the instance is zero + * @exception IllegalNormException if the norm of the instance is zero, NaN, + * or infinite */ - public Vector3D orthogonal() throws IllegalStateException { - double threshold = 0.6 * getNonZeroNorm(); + public Vector3D orthogonal() throws IllegalNormException { + double threshold = 0.6 * getFiniteNonZeroNorm(); final double x = getX(); final double y = getY(); @@ -233,8 +234,8 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point * other.</p> */ @Override - public double angle(Vector3D v) throws IllegalStateException { - double normProduct = getNonZeroNorm() * v.getNonZeroNorm(); + public double angle(Vector3D v) throws IllegalNormException { + double normProduct = getFiniteNonZeroNorm() * v.getFiniteNonZeroNorm(); double dot = dotProduct(v); double threshold = normProduct * 0.9999; @@ -322,13 +323,13 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point /** {@inheritDoc} */ @Override - public Vector3D project(Vector3D base) throws IllegalStateException { + public Vector3D project(Vector3D base) throws IllegalNormException { return getComponent(base, false); } /** {@inheritDoc} */ @Override - public Vector3D reject(Vector3D base) throws IllegalStateException { + public Vector3D reject(Vector3D base) throws IllegalNormException { return getComponent(base, true); } @@ -382,17 +383,13 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point return false; } - /** Returns the vector norm, throwing an IllegalStateException if the norm is zero. - * @return the non-zero norm value - * @throws IllegalStateException if the norm is zero + /** Returns the vector norm, throwing an IllegalNormException if the norm is zero, + * NaN, or infinite. + * @return the finite, non-zero norm value + * @throws IllegalNormException if the norm is zero, NaN, or infinite */ - private double getNonZeroNorm() throws IllegalStateException { - final double n = getNorm(); - if (n == 0) { - throw new ZeroNormException(); - } - - return n; + private double getFiniteNonZeroNorm() throws IllegalNormException { + return Vectors.checkFiniteNonZeroNorm(getNorm()); } /** Returns a component of the current instance relative to the given base @@ -404,17 +401,14 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point * is returned. * @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) throws IllegalStateException { + private Vector3D getComponent(Vector3D base, boolean reject) throws IllegalNormException { final double aDotB = dotProduct(base); - final double baseMagSq = base.getNormSq(); - if (baseMagSq == 0.0) { - throw new ZeroNormException(ZeroNormException.INVALID_BASE); - } + final double baseMag = Vectors.checkFiniteNonZeroNorm(base.getNorm()); - final double scale = aDotB / baseMagSq; + final double scale = aDotB / (baseMag * baseMag); final double projX = scale * base.getX(); final double projY = scale * base.getY(); 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 e7a89cb..323d8e6 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 @@ -17,8 +17,8 @@ package org.apache.commons.geometry.euclidean.twod; import org.apache.commons.geometry.core.internal.SimpleTupleFormat; -import org.apache.commons.geometry.core.internal.Vectors; import org.apache.commons.geometry.euclidean.EuclideanPoint; +import org.apache.commons.geometry.euclidean.internal.Vectors; import org.apache.commons.numbers.arrays.LinearCombination; /** This class represents a point in two-dimensional Euclidean space. 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 fec9764..298d052 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 @@ -16,10 +16,10 @@ */ package org.apache.commons.geometry.euclidean.twod; +import org.apache.commons.geometry.core.exception.IllegalNormException; import org.apache.commons.geometry.core.internal.SimpleTupleFormat; -import org.apache.commons.geometry.core.internal.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 two-dimensional Euclidean space. @@ -131,7 +131,7 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public Vector2D withMagnitude(double magnitude) { - final double invNorm = 1.0 / getNonZeroNorm(); + final double invNorm = 1.0 / getFiniteNonZeroNorm(); return new Vector2D( magnitude * getX() * invNorm, @@ -171,8 +171,8 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point /** {@inheritDoc} */ @Override - public Vector2D normalize() throws IllegalStateException { - return scalarMultiply(1.0 / getNonZeroNorm()); + public Vector2D normalize() throws IllegalNormException { + return scalarMultiply(1.0 / getFiniteNonZeroNorm()); } /** {@inheritDoc} */ @@ -213,13 +213,13 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point /** {@inheritDoc} */ @Override - public Vector2D project(Vector2D base) throws IllegalStateException { + public Vector2D project(Vector2D base) throws IllegalNormException { return getComponent(base, false); } /** {@inheritDoc} */ @Override - public Vector2D reject(Vector2D base) throws IllegalStateException { + public Vector2D reject(Vector2D base) throws IllegalNormException { return getComponent(base, true); } @@ -231,8 +231,8 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point * other.</p> */ @Override - public double angle(Vector2D v) throws IllegalStateException { - double normProduct = getNonZeroNorm() * v.getNonZeroNorm(); + public double angle(Vector2D v) throws IllegalNormException { + double normProduct = getFiniteNonZeroNorm() * v.getFiniteNonZeroNorm(); double dot = dotProduct(v); double threshold = normProduct * 0.9999; @@ -328,17 +328,13 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point return false; } - /** Returns the vector norm, throwing an IllegalStateException if the norm is zero. - * @return the non-zero norm value - * @throws IllegalStateException if the norm is zero + /** Returns the vector norm, throwing an IllegalNormException if the norm is zero, + * NaN, or infinite. + * @return the finite, non-zero norm value + * @throws IllegalNormException if the norm is zero, NaN, or infinite */ - private double getNonZeroNorm() throws IllegalStateException { - final double n = getNorm(); - if (n == 0) { - throw new ZeroNormException(); - } - - return n; + private double getFiniteNonZeroNorm() throws IllegalNormException { + return Vectors.checkFiniteNonZeroNorm(getNorm()); } /** Returns a component of the current instance relative to the given base @@ -350,17 +346,14 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point * is returned. * @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 Vector2D getComponent(Vector2D base, boolean reject) throws IllegalStateException { + private Vector2D getComponent(Vector2D base, boolean reject) throws IllegalNormException { final double aDotB = dotProduct(base); - final double baseMagSq = base.getNormSq(); - if (baseMagSq == 0.0) { - throw new ZeroNormException(ZeroNormException.INVALID_BASE); - } + final double baseMag = Vectors.checkFiniteNonZeroNorm(base.getNorm()); - final double scale = aDotB / baseMagSq; + final double scale = aDotB / (baseMag * baseMag); final double projX = scale * base.getX(); final double projY = scale * base.getY(); diff --git a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/internal/VectorsTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/internal/VectorsTest.java similarity index 83% rename from commons-geometry-core/src/test/java/org/apache/commons/geometry/core/internal/VectorsTest.java rename to commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/internal/VectorsTest.java index 9817b28..2d38904 100644 --- a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/internal/VectorsTest.java +++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/internal/VectorsTest.java @@ -14,9 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.commons.geometry.core.internal; +package org.apache.commons.geometry.euclidean.internal; -import org.apache.commons.geometry.core.internal.Vectors; +import org.apache.commons.geometry.core.GeometryTestUtils; +import org.apache.commons.geometry.core.exception.IllegalNormException; import org.junit.Assert; import org.junit.Test; @@ -26,6 +27,27 @@ public class VectorsTest { private static final double EPS = Math.ulp(1d); @Test + public void testCheckFiniteNonZeroNorm() { + // act/assert + Assert.assertEquals(1.0, Vectors.checkFiniteNonZeroNorm(1.0), EPS); + Assert.assertEquals(23.12, Vectors.checkFiniteNonZeroNorm(23.12), EPS); + Assert.assertEquals(2e-12, Vectors.checkFiniteNonZeroNorm(2e-12), EPS); + + Assert.assertEquals(-1.0, Vectors.checkFiniteNonZeroNorm(-1.0), EPS); + Assert.assertEquals(-23.12, Vectors.checkFiniteNonZeroNorm(-23.12), EPS); + Assert.assertEquals(-2e-12, Vectors.checkFiniteNonZeroNorm(-2e-12), EPS); + + GeometryTestUtils.assertThrows(() -> Vectors.checkFiniteNonZeroNorm(0.0), + IllegalNormException.class, "Illegal norm: 0.0"); + GeometryTestUtils.assertThrows(() -> Vectors.checkFiniteNonZeroNorm(Double.NaN), + IllegalNormException.class, "Illegal norm: NaN"); + GeometryTestUtils.assertThrows(() -> Vectors.checkFiniteNonZeroNorm(Double.POSITIVE_INFINITY), + IllegalNormException.class, "Illegal norm: Infinity"); + GeometryTestUtils.assertThrows(() -> Vectors.checkFiniteNonZeroNorm(Double.NEGATIVE_INFINITY), + IllegalNormException.class, "Illegal norm: -Infinity"); + } + + @Test public void testNorm1_oneD() { // act/assert Assert.assertEquals(0.0, Vectors.norm1(0.0), EPS); 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 e5b580a..ee817cc 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 @@ -19,6 +19,8 @@ package org.apache.commons.geometry.euclidean.oned; 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; @@ -119,10 +121,17 @@ public class Vector1DTest { checkVector(Vector1D.of(-5).withMagnitude(3.0), -3.0); } - @Test(expected = IllegalStateException.class) - public void testWithMagnitude_zeroNorm() { + @Test + public void testWithMagnitude_illegalNorm() { // act/assert - Vector1D.ZERO.withMagnitude(1.0); + GeometryTestUtils.assertThrows(() -> Vector1D.ZERO.withMagnitude(2.0), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.NaN.withMagnitude(2.0), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.POSITIVE_INFINITY.withMagnitude(2.0), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.NEGATIVE_INFINITY.withMagnitude(2.0), + IllegalNormException.class); } @Test @@ -196,10 +205,17 @@ public class Vector1DTest { checkVector(Vector1D.of(-5).normalize(), -1); } - @Test(expected = IllegalStateException.class) - public void testNormalize_zeroNorm() { - // act - Vector1D.ZERO.normalize(); + @Test + public void testNormalize_illegalNorm() { + // 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); } @Test @@ -317,10 +333,17 @@ public class Vector1DTest { checkVector(v3.project(v3), 4); } - @Test(expected = IllegalStateException.class) - public void testProject_baseHasZeroNorm() { + @Test + public void testProject_baseHasIllegalNorm() { // act/assert - Vector1D.of(2.0).project(Vector1D.ZERO); + GeometryTestUtils.assertThrows(() -> Vector1D.of(2.0).project(Vector1D.ZERO), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.of(2.0).project(Vector1D.NaN), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.of(2.0).project(Vector1D.POSITIVE_INFINITY), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.of(2.0).project(Vector1D.NEGATIVE_INFINITY), + IllegalNormException.class); } @Test @@ -340,10 +363,17 @@ public class Vector1DTest { checkVector(v2.reject(v2), 0); } - @Test(expected = IllegalStateException.class) - public void testReject_baseHasZeroNorm() { + @Test + public void testReject_baseHasIllegalNorm() { // act/assert - Vector1D.of(2.0).reject(Vector1D.ZERO); + GeometryTestUtils.assertThrows(() -> Vector1D.of(2.0).reject(Vector1D.ZERO), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.of(2.0).reject(Vector1D.NaN), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.of(2.0).reject(Vector1D.POSITIVE_INFINITY), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.of(2.0).reject(Vector1D.NEGATIVE_INFINITY), + IllegalNormException.class); } @Test @@ -376,16 +406,29 @@ public class Vector1DTest { Assert.assertEquals(0.0, v4.angle(v4), TEST_TOLERANCE); } - @Test(expected = IllegalStateException.class) - public void testAngle_firstVectorZero() { - // act/assert - Vector1D.ZERO.angle(Vector1D.of(1.0)); - } - - @Test(expected = IllegalStateException.class) - public void testAngle_secondVectorZero() { - // act/assert - Vector1D.of(1.0).angle(Vector1D.ZERO); + @Test + public void testAngle_illegalNorm() { + // arrange + Vector1D v = Vector1D.of(1.0); + + // act/assert + GeometryTestUtils.assertThrows(() -> Vector1D.ZERO.angle(v), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.NaN.angle(v), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.POSITIVE_INFINITY.angle(v), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector1D.NEGATIVE_INFINITY.angle(v), + IllegalNormException.class); + + GeometryTestUtils.assertThrows(() -> v.angle(Vector1D.ZERO), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.angle(Vector1D.NaN), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.angle(Vector1D.POSITIVE_INFINITY), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.angle(Vector1D.NEGATIVE_INFINITY), + IllegalNormException.class); } @Test diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java index ca28f44..9e5bad0 100644 --- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java +++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java @@ -17,6 +17,7 @@ package org.apache.commons.geometry.euclidean.threed; +import org.apache.commons.geometry.core.exception.IllegalNormException; import org.apache.commons.numbers.angle.PlaneAngleRadians; import org.junit.Assert; import org.junit.Test; @@ -235,7 +236,7 @@ public class RotationTest { try { new Rotation(u1, u2, Vector3D.ZERO, v2); Assert.fail("an exception should have been thrown"); - } catch (IllegalArgumentException e) { + } catch (IllegalNormException e) { // expected behavior } 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 3aa2dd7..4d34a91 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 @@ -20,6 +20,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.geometry.euclidean.EuclideanTestUtils; import org.apache.commons.numbers.core.Precision; import org.apache.commons.rng.UniformRandomProvider; @@ -144,10 +146,17 @@ public class Vector3DTest { checkVector(Vector3D.of(x, y, z).withMagnitude(3), 3 * normX, 3 * normY, 3 * normZ); } - @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 @@ -250,10 +259,17 @@ public class Vector3DTest { Assert.assertEquals(1.0, Vector3D.of(5, -4, 2).normalize().getNorm(), 1.0e-12); } - @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 @@ -271,10 +287,17 @@ public class Vector3DTest { Assert.assertEquals(0.0, v4.dotProduct(v4.orthogonal()), EPS); } - @Test(expected = IllegalStateException.class) - public void testOrthogonal_zeroNorm() { + @Test + public void testOrthogonal_illegalNorm() { // act/assert - Vector3D.ZERO.orthogonal(); + GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.orthogonal(), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector3D.NaN.orthogonal(), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector3D.POSITIVE_INFINITY.orthogonal(), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector3D.NEGATIVE_INFINITY.orthogonal(), + IllegalNormException.class); } @Test @@ -299,10 +322,29 @@ public class Vector3DTest { Assert.assertEquals(Geometry.HALF_PI, Vector3D.PLUS_X.angle(Vector3D.MINUS_Z), tolerance); } - @Test(expected = IllegalStateException.class) - public void testAngle_zeroNorm() { + @Test + public void testAngle_illegalNorm() { + // arrange + Vector3D v = Vector3D.of(1.0, 1.0, 1.0); + // act/assert - Vector3D.ZERO.angle(Vector3D.PLUS_X); + GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.angle(v), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector3D.NaN.angle(v), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector3D.POSITIVE_INFINITY.angle(v), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector3D.NEGATIVE_INFINITY.angle(v), + IllegalNormException.class); + + GeometryTestUtils.assertThrows(() -> v.angle(Vector3D.ZERO), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.angle(Vector3D.NaN), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.angle(Vector3D.POSITIVE_INFINITY), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.angle(Vector3D.NEGATIVE_INFINITY), + IllegalNormException.class); } @Test @@ -592,10 +634,20 @@ public class Vector3DTest { checkVector(v2.project(Vector3D.of(-1.0, -1.0, -1.0)), -6.0, -6.0, -6.0); } - @Test(expected = IllegalStateException.class) - public void testProject_baseHasZeroNorm() { + @Test + public void testProject_baseHasIllegalNorm() { + // arrange + Vector3D v = Vector3D.of(1.0, 1.0, 1.0); + // act/assert - Vector3D.of(1.0, 1.0, 1.0).project(Vector3D.ZERO); + GeometryTestUtils.assertThrows(() -> v.project(Vector3D.ZERO), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.project(Vector3D.NaN), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.project(Vector3D.POSITIVE_INFINITY), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.project(Vector3D.NEGATIVE_INFINITY), + IllegalNormException.class); } @Test @@ -628,10 +680,20 @@ public class Vector3DTest { checkVector(v2.reject(Vector3D.of(-1.0, -1.0, -1.0)), 1.0, 0.0, -1.0); } - @Test(expected = IllegalStateException.class) - public void testReject_baseHasZeroNorm() { + @Test + public void testReject_baseHasIllegalNorm() { + // arrange + Vector3D v = Vector3D.of(1.0, 1.0, 1.0); + // act/assert - Vector3D.of(1.0, 1.0, 1.0).reject(Vector3D.ZERO); + GeometryTestUtils.assertThrows(() -> v.reject(Vector3D.ZERO), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.reject(Vector3D.NaN), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.reject(Vector3D.POSITIVE_INFINITY), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.reject(Vector3D.NEGATIVE_INFINITY), + IllegalNormException.class); } @Test 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 62e0c8e..791f1c6 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 @@ -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.geometry.euclidean.EuclideanTestUtils; import org.apache.commons.numbers.core.Precision; import org.junit.Assert; @@ -159,10 +161,17 @@ public class Vector2DTest { checkVector(Vector2D.of(0.5, 0.5).withMagnitude(2), Math.sqrt(2), Math.sqrt(2)); } - @Test(expected = IllegalStateException.class) - public void testWithMagnitude_zeroNorm() { + @Test + public void testWithMagnitude_illegalNorm() { // act/assert - Vector2D.ZERO.withMagnitude(1.0); + GeometryTestUtils.assertThrows(() -> Vector2D.ZERO.withMagnitude(2.0), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector2D.NaN.withMagnitude(2.0), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector2D.POSITIVE_INFINITY.withMagnitude(2.0), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector2D.NEGATIVE_INFINITY.withMagnitude(2.0), + IllegalNormException.class); } @Test @@ -243,9 +252,17 @@ public class Vector2DTest { checkVector(Vector2D.of(-1, 2).normalize(), -1.0/Math.sqrt(5), 2.0/Math.sqrt(5)); } - @Test(expected = IllegalStateException.class) - public void testNormalize_zeroNorm() { - Vector2D.ZERO.normalize(); + @Test + public void testNormalize_illegalNorm() { + // 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); } @Test @@ -376,9 +393,29 @@ public class Vector2DTest { } - @Test(expected = IllegalStateException.class) - public void testAngle_zeroNorm() { - Vector2D.of(1, 1).angle(Vector2D.ZERO); + @Test + public void testAngle_illegalNorm() { + // arrange + Vector2D v = Vector2D.of(1.0, 1.0); + + // act/assert + GeometryTestUtils.assertThrows(() -> Vector2D.ZERO.angle(v), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector2D.NaN.angle(v), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector2D.POSITIVE_INFINITY.angle(v), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> Vector2D.NEGATIVE_INFINITY.angle(v), + IllegalNormException.class); + + GeometryTestUtils.assertThrows(() -> v.angle(Vector2D.ZERO), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.angle(Vector2D.NaN), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.angle(Vector2D.POSITIVE_INFINITY), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.angle(Vector2D.NEGATIVE_INFINITY), + IllegalNormException.class); } @Test @@ -418,10 +455,20 @@ public class Vector2DTest { checkVector(v2.project(v1), (19.0 / 25.0) * 3.0, (19.0 / 25.0) * 4.0); } - @Test(expected = IllegalStateException.class) - public void testProject_baseHasZeroNorm() { + @Test + public void testProject_baseHasIllegalNorm() { + // arrange + Vector2D v = Vector2D.of(1.0, 1.0); + // act/assert - Vector2D.of(1.0, 1.0).project(Vector2D.ZERO); + GeometryTestUtils.assertThrows(() -> v.project(Vector2D.ZERO), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.project(Vector2D.NaN), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.project(Vector2D.POSITIVE_INFINITY), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.project(Vector2D.NEGATIVE_INFINITY), + IllegalNormException.class); } @Test @@ -445,10 +492,20 @@ public class Vector2DTest { checkVector(v2.reject(v1), (-32.0 / 25.0), (6.0 / 25.0) * 4.0); } - @Test(expected = IllegalStateException.class) - public void testReject_baseHasZeroNorm() { + @Test + public void testReject_baseHasIllegalNorm() { + // arrange + Vector2D v = Vector2D.of(1.0, 1.0); + // act/assert - Vector2D.of(1.0, 1.0).reject(Vector2D.ZERO); + GeometryTestUtils.assertThrows(() -> v.reject(Vector2D.ZERO), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.reject(Vector2D.NaN), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.reject(Vector2D.POSITIVE_INFINITY), + IllegalNormException.class); + GeometryTestUtils.assertThrows(() -> v.reject(Vector2D.NEGATIVE_INFINITY), + IllegalNormException.class); } @Test