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 fc1af80256cb7b1f562aab0841267565695215fd Author: Matt Juntunen <matt.juntu...@hotmail.com> AuthorDate: Fri Sep 14 00:23:28 2018 -0400 GEOMETRY-8: simplifying exception handling to use mix of JDK exceptions (for argument validation and illegal states) and GeometryException subclasses (for geometry-specific situations) --- .../geometry/core/exception/GeometryException.java | 4 +- .../core/exception/GeometryValueException.java | 30 ----- .../core/exception/IllegalNormException.java | 12 +- .../geometry/core/exception/package-info.java | 2 +- .../GeometryInternalError.java} | 22 ++-- .../commons/geometry/enclosing/WelzlEncloser.java | 3 +- .../geometry/euclidean/internal/Vectors.java | 16 ++- .../commons/geometry/euclidean/oned/Vector1D.java | 12 +- .../commons/geometry/euclidean/threed/Plane.java | 7 +- .../geometry/euclidean/threed/Rotation.java | 124 ++++++++++++++------- .../geometry/euclidean/threed/Vector3D.java | 4 +- .../commons/geometry/euclidean/twod/Vector2D.java | 4 +- .../geometry/euclidean/internal/VectorsTest.java | 36 ++++-- .../geometry/euclidean/threed/RotationTest.java | 14 ++- .../commons/geometry/spherical/oned/ArcsSet.java | 44 ++------ .../spherical/twod/PropertiesComputer.java | 3 +- .../geometry/spherical/oned/ArcsSetTest.java | 2 +- 17 files changed, 184 insertions(+), 155 deletions(-) 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 index 48e4b2a..57462be 100644 --- 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 @@ -16,7 +16,9 @@ */ package org.apache.commons.geometry.core.exception; -/** Base class for geometry exceptions. +/** Base class for geometry exceptions. The semantics of this class + * are intended to be similar to {@link java.lang.ArithmeticException} + * but for geometric operations. */ public class GeometryException extends RuntimeException { 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 deleted file mode 100644 index 5d7b0d6..0000000 --- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryValueException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 index b0d80e8..26fc11c 100644 --- 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 @@ -19,16 +19,22 @@ 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 { +public class IllegalNormException extends GeometryException { /** Serializable version identifier */ private static final long serialVersionUID = 20180909L; - /** - * Simple constructor accepting the illegal norm value. + /** Simple constructor accepting the illegal norm value. * @param norm the illegal norm value */ public IllegalNormException(double norm) { super("Illegal norm: " + norm); } + + /** Constructor accepting an error message. + * @param msg the error message + */ + public IllegalNormException(String msg) { + super(msg); + } } 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 index 8180f81..2b7d21b 100644 --- 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 @@ -17,7 +17,7 @@ /** * * <p> - * This package contains exception types used by this library. + * This package contains specialized exception types used by this library. * </p> */ package org.apache.commons.geometry.core.exception; 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/internal/GeometryInternalError.java similarity index 50% rename from commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryStateException.java rename to commons-geometry-core/src/main/java/org/apache/commons/geometry/core/internal/GeometryInternalError.java index 4a8080e..219ccdb 100644 --- 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/internal/GeometryInternalError.java @@ -14,17 +14,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.commons.geometry.core.exception; +package org.apache.commons.geometry.core.internal; -public class GeometryStateException extends GeometryException { +/** Exception thrown when something that should not happen does happen. + * This class is not intended to be part of the public API and should + * never be seen by the user when algorithms are functioning correctly. + */ +public class GeometryInternalError extends IllegalStateException { + + /** Error message used for exceptions of this type. */ + private static final String ERROR_MSG = "An internal geometry error occurred. This most often indicates an " + + "error in the algorithm implementation than in the calling code or data. Please file a bug report " + + "with the developers."; /** Serializable version identifier */ - private static final long serialVersionUID = 20180909L; + private static final long serialVersionUID = 20180913L; - /** Simple constructor with error message. - * @param msg exception message string + /** Simple constructor with a default error message. */ - public GeometryStateException(String msg) { - super(msg); + public GeometryInternalError() { + super(ERROR_MSG); } } diff --git a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/WelzlEncloser.java b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/WelzlEncloser.java index 4ec5ad5..65dbc2a 100644 --- a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/WelzlEncloser.java +++ b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/WelzlEncloser.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; import org.apache.commons.geometry.core.Point; +import org.apache.commons.geometry.core.internal.GeometryInternalError; /** Class implementing Emo Welzl algorithm to find the smallest enclosing ball in linear time. * <p> @@ -98,7 +99,7 @@ public class WelzlEncloser<P extends Point<P>> implements Encloser<P> { ball = moveToFrontBall(extreme, extreme.size(), support); if (ball.getRadius() < savedBall.getRadius()) { // this should never happen - throw new IllegalStateException("Please file a bug report"); + throw new GeometryInternalError(); } // it was an interesting point, move it to the front diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/Vectors.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/Vectors.java index cbb19b2..31ec162 100644 --- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/Vectors.java +++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/Vectors.java @@ -25,17 +25,27 @@ public final class Vectors { /** Private constructor. */ private Vectors() {} + /** Returns true if the given value is finite (ie, not NaN or inifinite) + * and not equal to zero. + * @param value the value to test + * @return true if {@code value} is not NaN, infinite, or zero; otherwise + * false + */ + public static boolean isFiniteNonZero(final double value) { + return Double.isFinite(value) && value != 0.0; + } + /** 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. + * to allow this method to be called inline. * @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) { + public static double ensureFiniteNonZeroNorm(final double norm) throws IllegalNormException { + if (!isFiniteNonZero(norm)) { throw new IllegalNormException(norm); } 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 26df5d1..79a4d35 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 @@ -116,7 +116,7 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public Vector1D withMagnitude(double magnitude) { - Vectors.checkFiniteNonZeroNorm(getNorm()); + Vectors.ensureFiniteNonZeroNorm(getNorm()); return (getX() > 0.0)? new Vector1D(magnitude) : new Vector1D(-magnitude); } @@ -154,7 +154,7 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public Vector1D normalize() { - Vectors.checkFiniteNonZeroNorm(getNorm()); + Vectors.ensureFiniteNonZeroNorm(getNorm()); return (getX() > 0.0) ? ONE : MINUS_ONE; } @@ -200,7 +200,7 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point */ @Override public Vector1D project(final Vector1D base) { - Vectors.checkFiniteNonZeroNorm(base.getNorm()); + Vectors.ensureFiniteNonZeroNorm(base.getNorm()); return this; } @@ -210,7 +210,7 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point */ @Override public Vector1D reject(final Vector1D base) { - Vectors.checkFiniteNonZeroNorm(base.getNorm()); + Vectors.ensureFiniteNonZeroNorm(base.getNorm()); return Vector1D.ZERO; } @@ -221,8 +221,8 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point */ @Override public double angle(final Vector1D v) { - Vectors.checkFiniteNonZeroNorm(getNorm()); - Vectors.checkFiniteNonZeroNorm(v.getNorm()); + Vectors.ensureFiniteNonZeroNorm(getNorm()); + Vectors.ensureFiniteNonZeroNorm(v.getNorm()); final double sig1 = Math.signum(getX()); final double sig2 = Math.signum(v.getX()); diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Plane.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Plane.java index bc3c040..424fbb3 100644 --- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Plane.java +++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Plane.java @@ -18,6 +18,7 @@ package org.apache.commons.geometry.euclidean.threed; import org.apache.commons.geometry.core.partitioning.Embedding; import org.apache.commons.geometry.core.partitioning.Hyperplane; +import org.apache.commons.geometry.euclidean.internal.Vectors; import org.apache.commons.geometry.euclidean.oned.Point1D; import org.apache.commons.geometry.euclidean.twod.Point2D; import org.apache.commons.geometry.euclidean.twod.PolygonsSet; @@ -141,10 +142,8 @@ public class Plane implements Hyperplane<Point3D>, Embedding<Point3D, Point2D> { * @exception IllegalArgumentException if the normal norm is too close to zero */ private void setNormal(final Vector3D normal) { - final double norm = normal.getNorm(); - if (norm < 1.0e-10) { - throw new IllegalArgumentException("Norm is zero"); - } + final double norm = Vectors.ensureFiniteNonZeroNorm(normal.getNorm()); + w = Vector3D.linearCombination(1.0 / norm, normal); } 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 2e9b63d..466aedc 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,7 +19,9 @@ package org.apache.commons.geometry.euclidean.threed; import java.io.Serializable; +import org.apache.commons.geometry.core.exception.GeometryException; import org.apache.commons.geometry.core.exception.IllegalNormException; +import org.apache.commons.geometry.euclidean.internal.Vectors; import org.apache.commons.numbers.arrays.LinearCombination; /** @@ -97,12 +99,6 @@ public class Rotation implements Serializable { /** Serializable version identifier */ private static final long serialVersionUID = 20180903L; - /** Error message for Cardan angle singularities */ - private static final String CARDAN_SINGULARITY_MSG = "Cardan angles singularity"; - - /** Error message for Euler angle singularities */ - private static final String EULER_SINGULARITY_MSG = "Euler angles singularity"; - /** Scalar coordinate of the quaternion. */ private final double q0; @@ -172,14 +168,14 @@ public class Rotation implements Serializable { * @param axis axis around which to rotate * @param angle rotation angle * @param convention convention to use for the semantics of the angle - * @exception IllegalArgumentException if the axis norm is zero + * @exception IllegalNormException if the axis norm is zero, NaN, or infinite */ public Rotation(final Vector3D axis, final double angle, final RotationConvention convention) - throws IllegalArgumentException { + throws IllegalNormException { double norm = axis.getNorm(); - if (norm == 0) { - throw new IllegalArgumentException("Zero norm for rotation axis"); + if (!Vectors.isFiniteNonZero(norm)) { + throw new IllegalNormException("Illegal norm for rotation axis: " + norm); } double halfAngle = convention == RotationConvention.VECTOR_OPERATOR ? -0.5 * angle : +0.5 * angle; @@ -320,13 +316,13 @@ public class Rotation implements Serializable { * @param u origin vector * @param v desired image of u by the rotation - * @exception IllegalArgumentException if the norm of one of the vectors is zero + * @exception IllegalNormException if the norm of one of the vectors is zero, NaN, or infinite */ public Rotation(Vector3D u, Vector3D v) { double normProduct = u.getNorm() * v.getNorm(); - if (normProduct == 0) { - throw new IllegalArgumentException("Zero norm for rotation defining vector"); + if (!Vectors.isFiniteNonZero(normProduct)) { + throw new IllegalNormException("Illegal norm for rotation defining vector: " + normProduct); } double dot = u.dotProduct(v); @@ -568,13 +564,13 @@ public class Rotation implements Serializable { * @param order rotation order to use * @return an array of three angles, in the order specified by the set - * @exception IllegalStateException if the rotation is + * @exception AngleSetSingularityException if the rotation is * singular with respect to the angles set specified * @deprecated as of 3.6, replaced with {@link #getAngles(RotationOrder, RotationConvention)} */ @Deprecated public double[] getAngles(RotationOrder order) - throws IllegalStateException { + throws AngleSetSingularityException { return getAngles(order, RotationConvention.VECTOR_OPERATOR); } @@ -611,11 +607,11 @@ public class Rotation implements Serializable { * @param order rotation order to use * @param convention convention to use for the semantics of the angle * @return an array of three angles, in the order specified by the set - * @exception IllegalStateException if the rotation is - * singular with respect to the angles set specified + * @exception AngleSetSingularityException if the rotation is + * singular with respect to the angle set specified */ public double[] getAngles(RotationOrder order, RotationConvention convention) - throws IllegalStateException { + throws AngleSetSingularityException { if (convention == RotationConvention.VECTOR_OPERATOR) { if (order == RotationOrder.XYZ) { @@ -628,7 +624,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Z); Vector3D v2 = applyInverseTo(Vector3D.PLUS_X); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(-(v1.getY()), v1.getZ()), @@ -646,7 +642,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Y); Vector3D v2 = applyInverseTo(Vector3D.PLUS_X); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(v1.getZ(), v1.getY()), @@ -664,7 +660,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Z); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(v1.getX(), v1.getZ()), @@ -682,7 +678,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_X); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(-(v1.getZ()), v1.getX()), @@ -700,7 +696,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Y); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(-(v1.getX()), v1.getY()), @@ -718,7 +714,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_X); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(v1.getY(), v1.getX()), @@ -736,7 +732,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_X); Vector3D v2 = applyInverseTo(Vector3D.PLUS_X); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v1.getY(), -v1.getZ()), @@ -754,7 +750,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_X); Vector3D v2 = applyInverseTo(Vector3D.PLUS_X); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v1.getZ(), v1.getY()), @@ -772,7 +768,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Y); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v1.getX(), v1.getZ()), @@ -790,7 +786,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Y); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v1.getZ(), -v1.getX()), @@ -808,7 +804,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Z); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v1.getX(), -v1.getY()), @@ -826,7 +822,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Z); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v1.getY(), v1.getX()), @@ -846,7 +842,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_X); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(-v2.getY(), v2.getZ()), @@ -864,7 +860,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_X); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(v2.getZ(), v2.getY()), @@ -882,7 +878,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Y); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(v2.getX(), v2.getZ()), @@ -900,7 +896,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Y); Vector3D v2 = applyInverseTo(Vector3D.PLUS_X); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(-v2.getZ(), v2.getX()), @@ -918,7 +914,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Z); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(-v2.getX(), v2.getY()), @@ -936,7 +932,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Z); Vector3D v2 = applyInverseTo(Vector3D.PLUS_X); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { - throw new IllegalStateException(CARDAN_SINGULARITY_MSG); + throw new CardanSingularityException(); } return new double[] { Math.atan2(v2.getY(), v2.getX()), @@ -954,7 +950,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_X); Vector3D v2 = applyInverseTo(Vector3D.PLUS_X); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v2.getY(), -v2.getZ()), @@ -972,7 +968,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_X); Vector3D v2 = applyInverseTo(Vector3D.PLUS_X); if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v2.getZ(), v2.getY()), @@ -990,7 +986,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Y); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v2.getX(), v2.getZ()), @@ -1008,7 +1004,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Y); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y); if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v2.getZ(), -v2.getX()), @@ -1026,7 +1022,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Z); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v2.getX(), -v2.getY()), @@ -1044,7 +1040,7 @@ public class Rotation implements Serializable { Vector3D v1 = applyTo(Vector3D.PLUS_Z); Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z); if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) { - throw new IllegalStateException(EULER_SINGULARITY_MSG); + throw new EulerSingularityException(); } return new double[] { Math.atan2(v2.getY(), v2.getX()), @@ -1411,4 +1407,48 @@ public class Rotation implements Serializable { return r1.composeInverseInternal(r2).getAngle(); } + /** Exception thrown when an angle set encounters a singularity. + */ + public static class AngleSetSingularityException extends GeometryException { + + /** Serializable version identifier */ + private static final long serialVersionUID = 20180913L; + + /** Simple constructor with an error message. + * @param msg error message + */ + public AngleSetSingularityException(String msg) { + super(msg); + } + } + + /** Exception thrown when a Cardan angles singularity is encountered. + */ + public static class CardanSingularityException extends AngleSetSingularityException { + + /** Serializable version identifier */ + private static final long serialVersionUID = 20180913L; + + /** + * Simple constructor. + */ + public CardanSingularityException() { + super("Cardan angles singularity"); + } + } + + /** Exception thrown when an Euler angles singularity is encountered. + */ + public static class EulerSingularityException extends AngleSetSingularityException { + + /** Serializable version identifier */ + private static final long serialVersionUID = 20180913L; + + /** + * Simple constructor. + */ + public EulerSingularityException() { + super("Euler angles singularity"); + } + } } 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 5c5317c..4b96f43 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 @@ -389,7 +389,7 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point * @throws IllegalNormException if the norm is zero, NaN, or infinite */ private double getFiniteNonZeroNorm() { - return Vectors.checkFiniteNonZeroNorm(getNorm()); + return Vectors.ensureFiniteNonZeroNorm(getNorm()); } /** Returns a component of the current instance relative to the given base @@ -406,7 +406,7 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point private Vector3D getComponent(Vector3D base, boolean reject) { final double aDotB = dotProduct(base); - final double baseMag = Vectors.checkFiniteNonZeroNorm(base.getNorm()); + final double baseMag = Vectors.ensureFiniteNonZeroNorm(base.getNorm()); final double scale = aDotB / (baseMag * baseMag); 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 1d7be1c..36338d0 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 @@ -334,7 +334,7 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point * @throws IllegalNormException if the norm is zero, NaN, or infinite */ private double getFiniteNonZeroNorm() { - return Vectors.checkFiniteNonZeroNorm(getNorm()); + return Vectors.ensureFiniteNonZeroNorm(getNorm()); } /** Returns a component of the current instance relative to the given base @@ -351,7 +351,7 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point private Vector2D getComponent(Vector2D base, boolean reject) { final double aDotB = dotProduct(base); - final double baseMag = Vectors.checkFiniteNonZeroNorm(base.getNorm()); + final double baseMag = Vectors.ensureFiniteNonZeroNorm(base.getNorm()); final double scale = aDotB / (baseMag * baseMag); diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/internal/VectorsTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/internal/VectorsTest.java index 2d38904..2d38b03 100644 --- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/internal/VectorsTest.java +++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/internal/VectorsTest.java @@ -27,23 +27,37 @@ public class VectorsTest { private static final double EPS = Math.ulp(1d); @Test - public void testCheckFiniteNonZeroNorm() { + public void testIsFiniteNonZero() { // 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.assertTrue(Vectors.isFiniteNonZero(1e-20)); + Assert.assertTrue(Vectors.isFiniteNonZero(1e20)); + Assert.assertTrue(Vectors.isFiniteNonZero(-1e-20)); + Assert.assertTrue(Vectors.isFiniteNonZero(-1e20)); + + Assert.assertFalse(Vectors.isFiniteNonZero(0.0)); + Assert.assertFalse(Vectors.isFiniteNonZero(Double.NaN)); + Assert.assertFalse(Vectors.isFiniteNonZero(Double.POSITIVE_INFINITY)); + Assert.assertFalse(Vectors.isFiniteNonZero(Double.NEGATIVE_INFINITY)); + } + + @Test + public void testEnsureFiniteNonZeroNorm() { + // act/assert + Assert.assertEquals(1.0, Vectors.ensureFiniteNonZeroNorm(1.0), EPS); + Assert.assertEquals(23.12, Vectors.ensureFiniteNonZeroNorm(23.12), EPS); + Assert.assertEquals(2e-12, Vectors.ensureFiniteNonZeroNorm(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); + Assert.assertEquals(-1.0, Vectors.ensureFiniteNonZeroNorm(-1.0), EPS); + Assert.assertEquals(-23.12, Vectors.ensureFiniteNonZeroNorm(-23.12), EPS); + Assert.assertEquals(-2e-12, Vectors.ensureFiniteNonZeroNorm(-2e-12), EPS); - GeometryTestUtils.assertThrows(() -> Vectors.checkFiniteNonZeroNorm(0.0), + GeometryTestUtils.assertThrows(() -> Vectors.ensureFiniteNonZeroNorm(0.0), IllegalNormException.class, "Illegal norm: 0.0"); - GeometryTestUtils.assertThrows(() -> Vectors.checkFiniteNonZeroNorm(Double.NaN), + GeometryTestUtils.assertThrows(() -> Vectors.ensureFiniteNonZeroNorm(Double.NaN), IllegalNormException.class, "Illegal norm: NaN"); - GeometryTestUtils.assertThrows(() -> Vectors.checkFiniteNonZeroNorm(Double.POSITIVE_INFINITY), + GeometryTestUtils.assertThrows(() -> Vectors.ensureFiniteNonZeroNorm(Double.POSITIVE_INFINITY), IllegalNormException.class, "Illegal norm: Infinity"); - GeometryTestUtils.assertThrows(() -> Vectors.checkFiniteNonZeroNorm(Double.NEGATIVE_INFINITY), + GeometryTestUtils.assertThrows(() -> Vectors.ensureFiniteNonZeroNorm(Double.NEGATIVE_INFINITY), IllegalNormException.class, "Illegal norm: -Infinity"); } 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 9e5bad0..136a4cb 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 @@ -18,6 +18,8 @@ package org.apache.commons.geometry.euclidean.threed; import org.apache.commons.geometry.core.exception.IllegalNormException; +import org.apache.commons.geometry.euclidean.threed.Rotation.CardanSingularityException; +import org.apache.commons.geometry.euclidean.threed.Rotation.EulerSingularityException; import org.apache.commons.numbers.angle.PlaneAngleRadians; import org.junit.Assert; import org.junit.Test; @@ -63,7 +65,7 @@ public class RotationTest { try { new Rotation(Vector3D.of(0, 0, 0), 2 * Math.PI / 3); Assert.fail("an exception should have been thrown"); - } catch (IllegalArgumentException e) { + } catch (IllegalNormException e) { } r = new Rotation(Vector3D.PLUS_Z, 1.5 * Math.PI); @@ -93,7 +95,7 @@ public class RotationTest { try { new Rotation(Vector3D.of(0, 0, 0), 2 * Math.PI / 3, RotationConvention.VECTOR_OPERATOR); Assert.fail("an exception should have been thrown"); - } catch (IllegalArgumentException e) { + } catch (IllegalNormException e) { } r = new Rotation(Vector3D.PLUS_Z, 1.5 * Math.PI, RotationConvention.VECTOR_OPERATOR); @@ -126,7 +128,7 @@ public class RotationTest { try { new Rotation(Vector3D.of(0, 0, 0), 2 * Math.PI / 3, RotationConvention.FRAME_TRANSFORM); Assert.fail("an exception should have been thrown"); - } catch (IllegalArgumentException e) { + } catch (IllegalNormException e) { } r = new Rotation(Vector3D.PLUS_Z, 1.5 * Math.PI, RotationConvention.FRAME_TRANSFORM); @@ -196,7 +198,7 @@ public class RotationTest { try { new Rotation(u, Vector3D.ZERO); Assert.fail("an exception should have been thrown"); - } catch (IllegalArgumentException e) { + } catch (IllegalNormException e) { // expected behavior } @@ -494,7 +496,7 @@ public class RotationTest { try { r.getAngles(CardanOrders[i], convention); Assert.fail("an exception should have been caught"); - } catch (IllegalStateException cese) { + } catch (CardanSingularityException cese) { // expected behavior } } @@ -512,7 +514,7 @@ public class RotationTest { try { r.getAngles(EulerOrders[i], convention); Assert.fail("an exception should have been caught"); - } catch (IllegalStateException cese) { + } catch (EulerSingularityException cese) { // expected behavior } } diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/ArcsSet.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/ArcsSet.java index 311950f..8ca86ce 100644 --- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/ArcsSet.java +++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/ArcsSet.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.NoSuchElementException; import org.apache.commons.geometry.core.Geometry; +import org.apache.commons.geometry.core.internal.GeometryInternalError; import org.apache.commons.geometry.core.partitioning.AbstractRegion; import org.apache.commons.geometry.core.partitioning.BSPTree; import org.apache.commons.geometry.core.partitioning.BoundaryProjection; @@ -42,9 +43,6 @@ import org.apache.commons.numbers.core.Precision; */ public class ArcsSet extends AbstractRegion<S1Point, S1Point> implements Iterable<double[]> { - /** Message used for internal errors. */ - private static final String INTERNAL_ERROR_MESSAGE = "Please file a bug report"; - /** Build an arcs set representing the whole circle. * @param tolerance tolerance below which close sub-arcs are merged together */ @@ -79,11 +77,11 @@ public class ArcsSet extends AbstractRegion<S1Point, S1Point> implements Iterabl * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p> * @param tree inside/outside BSP tree representing the arcs set * @param tolerance tolerance below which close sub-arcs are merged together - * @exception InconsistentStateAt2PiWrapping if the tree leaf nodes are not + * @exception IllegalArgumentException if the tree leaf nodes are not * consistent across the \( 0, 2 \pi \) crossing */ public ArcsSet(final BSPTree<S1Point> tree, final double tolerance) - throws InconsistentStateAt2PiWrapping { + throws IllegalArgumentException { super(tree, tolerance); check2PiConsistency(); } @@ -107,11 +105,11 @@ public class ArcsSet extends AbstractRegion<S1Point, S1Point> implements Iterabl * space.</p> * @param boundary collection of boundary elements * @param tolerance tolerance below which close sub-arcs are merged together - * @exception InconsistentStateAt2PiWrapping if the tree leaf nodes are not + * @exception IllegalArgumentException if the tree leaf nodes are not * consistent across the \( 0, 2 \pi \) crossing */ public ArcsSet(final Collection<SubHyperplane<S1Point>> boundary, final double tolerance) - throws InconsistentStateAt2PiWrapping { + throws IllegalArgumentException { super(boundary, tolerance); check2PiConsistency(); } @@ -167,10 +165,10 @@ public class ArcsSet extends AbstractRegion<S1Point, S1Point> implements Iterabl } /** Check consistency. - * @exception InconsistentStateAt2PiWrapping if the tree leaf nodes are not + * @exception IllegalArgumentException if the tree leaf nodes are not * consistent across the \( 0, 2 \pi \) crossing */ - private void check2PiConsistency() throws InconsistentStateAt2PiWrapping { + private void check2PiConsistency() throws IllegalArgumentException { // start search at the tree root BSPTree<S1Point> root = getTree(false); @@ -185,7 +183,7 @@ public class ArcsSet extends AbstractRegion<S1Point, S1Point> implements Iterabl final Boolean stateAfter = (Boolean) getLastLeaf(root).getAttribute(); if (stateBefore ^ stateAfter) { - throw new InconsistentStateAt2PiWrapping(); + throw new IllegalArgumentException("Inconsistent state at 2\\u03c0 wrapping"); } } @@ -655,7 +653,7 @@ public class ArcsSet extends AbstractRegion<S1Point, S1Point> implements Iterabl } if (end == null) { // this should never happen - throw new IllegalStateException(INTERNAL_ERROR_MESSAGE); + throw new GeometryInternalError(); } // we have identified the last arc @@ -794,7 +792,7 @@ public class ArcsSet extends AbstractRegion<S1Point, S1Point> implements Iterabl final BSPTree<S1Point> node = tree.getCell(limit.getLocation(), getTolerance()); if (node.getCut() != null) { // this should never happen - throw new IllegalStateException(INTERNAL_ERROR_MESSAGE); + throw new GeometryInternalError(); } node.insertCut(limit); @@ -926,27 +924,5 @@ public class ArcsSet extends AbstractRegion<S1Point, S1Point> implements Iterabl return Side.HYPER; } } - - } - - /** Specialized exception for inconsistent BSP tree state inconsistency. - * <p> - * This exception is thrown at {@link ArcsSet} construction time when the - * {@link org.apache.commons.geometry.core.partitioning.Region.Location inside/outside} - * state is not consistent at the 0, \(2 \pi \) crossing. - * </p> - */ - public static class InconsistentStateAt2PiWrapping extends IllegalArgumentException { - - /** Serializable UID. */ - private static final long serialVersionUID = 20140107L; - - /** Simple constructor. - */ - public InconsistentStateAt2PiWrapping() { - super("Inconsistent state at 2\\u03c0 wrapping"); - } - } - } diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/PropertiesComputer.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/PropertiesComputer.java index 3f6da88..536a2dd 100644 --- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/PropertiesComputer.java +++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/PropertiesComputer.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; import org.apache.commons.geometry.core.Geometry; +import org.apache.commons.geometry.core.internal.GeometryInternalError; import org.apache.commons.geometry.core.partitioning.BSPTree; import org.apache.commons.geometry.core.partitioning.BSPTreeVisitor; import org.apache.commons.geometry.euclidean.threed.Point3D; @@ -79,7 +80,7 @@ class PropertiesComputer implements BSPTreeVisitor<S2Point> { final List<Vertex> boundary = convex.getBoundaryLoops(); if (boundary.size() != 1) { // this should never happen - throw new IllegalStateException("Please file a bug report"); + throw new GeometryInternalError(); } // compute the geometrical properties of the convex cell diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/ArcsSetTest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/ArcsSetTest.java index a6bab1a..8d8c600 100644 --- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/ArcsSetTest.java +++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/ArcsSetTest.java @@ -358,7 +358,7 @@ public class ArcsSetTest { } - @Test(expected=ArcsSet.InconsistentStateAt2PiWrapping.class) + @Test(expected=IllegalArgumentException.class) public void testInconsistentState() { SubLimitAngle l1 = new LimitAngle(S1Point.of(1.0), false, 1.0e-10).wholeHyperplane(); SubLimitAngle l2 = new LimitAngle(S1Point.of(2.0), true, 1.0e-10).wholeHyperplane();