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 b329f9b0952c9c3537b6bcd0803a482a1001af17 Author: Matt Juntunen <[email protected]> AuthorDate: Fri Jul 20 00:43:59 2018 -0400 GEOMETRY-9: adding getMagnitude, getMagnitudeSq, and withMagnitude methods to Euclidean vectors; creating Vectors utility class to encapsulate vector norm methods --- .../org/apache/commons/geometry/core/Geometry.java | 2 +- .../org/apache/commons/geometry/core/Vector.java | 5 +- .../apache/commons/geometry/core/util/Vectors.java | 163 +++++++++++++++++++ .../core/{Geometry.java => util/package-info.java} | 29 +--- .../commons/geometry/core/util/VectorsTest.java | 178 +++++++++++++++++++++ .../geometry/euclidean/EuclideanVector.java | 23 +++ .../commons/geometry/euclidean/oned/Vector1D.java | 46 +++++- .../geometry/euclidean/threed/Cartesian3D.java | 13 -- .../commons/geometry/euclidean/threed/Point3D.java | 6 +- .../geometry/euclidean/threed/Vector3D.java | 111 +++++++------ .../geometry/euclidean/twod/Cartesian2D.java | 10 -- .../commons/geometry/euclidean/twod/Point2D.java | 3 +- .../commons/geometry/euclidean/twod/Vector2D.java | 79 +++++---- .../geometry/euclidean/oned/Vector1DTest.java | 34 ++++ .../geometry/euclidean/threed/Vector3DTest.java | 51 +++++- .../geometry/euclidean/twod/Vector2DTest.java | 47 +++++- 16 files changed, 662 insertions(+), 138 deletions(-) diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Geometry.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Geometry.java index d74d312..6eb700b 100644 --- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Geometry.java +++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Geometry.java @@ -18,7 +18,7 @@ package org.apache.commons.geometry.core; /** Class containing geometric constants. */ -public class Geometry { +public final class Geometry { /** Alias for {@link Math#PI}, placed here for completeness. */ public static final double PI = Math.PI; 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 1d0269d..03bda8f 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 @@ -36,7 +36,6 @@ public interface Vector<V extends Vector<V>> extends Spatial { /** Get the L<sub>1</sub> norm for the vector. This is defined as the * sum of the absolute values of all vector components. - * * @see <a href="http://mathworld.wolfram.com/L1-Norm.html">L1 Norm</a> * @return L<sub>1</sub> norm for the vector */ @@ -46,14 +45,14 @@ public interface Vector<V extends Vector<V>> extends Spatial { * This corresponds to the common notion of vector magnitude or length. * This is defined as the square root of the sum of the squares of all vector components. * @see <a href="http://mathworld.wolfram.com/L2-Norm.html">L2 Norm</a> - * @return Euclidean norm for the vector + * @return L<sub>2</sub> norm for the vector */ double getNorm(); /** Get the square of the L<sub>2</sub> norm (also known as the Euclidean norm) * for the vector. This is equal to the sum of the squares of all vector components. * @see #getNorm() - * @return square of the Euclidean norm for the vector + * @return square of the L<sub>2</sub> norm for the vector */ double getNormSq(); diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/Vectors.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/Vectors.java new file mode 100644 index 0000000..f066364 --- /dev/null +++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/Vectors.java @@ -0,0 +1,163 @@ +/* + * 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.util; + +/** This class consists exclusively of static vector utility methods. + */ +public final class Vectors { + + /** 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 + * @return L<sub>1</sub> norm for the vector with the given components + * @see <a href="http://mathworld.wolfram.com/L1-Norm.html">L1 Norm</a> + */ + public static double norm1(final double x) { + return Math.abs(x); + } + + /** 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 x1 first vector component + * @param x2 second vector compoent + * @return L<sub>1</sub> norm for the vector with the given components + * @see <a href="http://mathworld.wolfram.com/L1-Norm.html">L1 Norm</a> + */ + public static double norm1(final double x1, final double x2) { + return Math.abs(x1) + Math.abs(x2); + } + + /** 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 x1 first vector component + * @param x2 second vector compoent + * @param x2 third vector component + * @return L<sub>1</sub> norm for the vector with the given components + * @see <a href="http://mathworld.wolfram.com/L1-Norm.html">L1 Norm</a> + */ + public static double norm1(final double x1, final double x2, final double x3) { + return Math.abs(x1) + Math.abs(x2) + Math.abs(x3); + } + + /** Get the L<sub>2</sub> norm (commonly known as the Euclidean norm) for the vector + * with the given components. This corresponds to the common notion of vector magnitude + * or length and is defined as the square root of the sum of the squares of all vector components. + * @param x vector component + * @return L<sub>2</sub> norm for the vector with the given components + * @see <a href="http://mathworld.wolfram.com/L2-Norm.html">L2 Norm</a> + */ + public static double norm(final double x) { + return Math.abs(x); + } + + /** Get the L<sub>2</sub> norm (commonly known as the Euclidean norm) for the vector + * with the given components. This corresponds to the common notion of vector magnitude + * or length and is defined as the square root of the sum of the squares of all vector components. + * @param x1 first vector component + * @param x2 second vector component + * @return L<sub>2</sub> norm for the vector with the given components + * @see <a href="http://mathworld.wolfram.com/L2-Norm.html">L2 Norm</a> + */ + public static double norm(final double x1, final double x2) { + return Math.hypot(x1, x2); + } + + /** Get the L<sub>2</sub> norm (commonly known as the Euclidean norm) for the vector + * with the given components. This corresponds to the common notion of vector magnitude + * or length and is defined as the square root of the sum of the squares of all vector components. + * @param x1 first vector component + * @param x2 second vector component + * @param x3 third vector component + * @return L<sub>2</sub> norm for the vector with the given components + * @see <a href="http://mathworld.wolfram.com/L2-Norm.html">L2 Norm</a> + */ + public static double norm(final double x1, final double x2, final double x3) { + return Math.sqrt(normSq(x1, x2, x3)); + } + + /** Get the square of the L<sub>2</sub> norm (also known as the Euclidean norm) + * for the vector with the given components. This is equal to the sum of the squares of + * all vector components. + * @param x vector component + * @return square of the L<sub>2</sub> norm for the vector with the given components + * @see #norm(double) + */ + public static double normSq(final double x) { + return x * x; + } + + /** Get the square of the L<sub>2</sub> norm (also known as the Euclidean norm) + * for the vector with the given components. This is equal to the sum of the squares of + * all vector components. + * @param x1 first vector component + * @param x2 second vector component + * @return square of the L<sub>2</sub> norm for the vector with the given components + * @see #norm(double, double) + */ + public static double normSq(final double x1, final double x2) { + return (x1 * x1) + (x2 * x2); + } + + /** Get the square of the L<sub>2</sub> norm (also known as the Euclidean norm) + * for the vector with the given components. This is equal to the sum of the squares of + * all vector components. + * @param x1 first vector component + * @param x2 second vector component + * @param x3 third vector component + * @return square of the L<sub>2</sub> norm for the vector with the given components + * @see #norm(double, double, double) + */ + public static double normSq(final double x1, final double x2, final double x3) { + return (x1 * x1) + (x2 * x2) + (x3 * x3); + } + + /** Get the L<sub>∞</sub> norm for the vector with the given components. This is defined + * as the maximum of the absolute values of all vector components. + * @param x vector component + * @return L<sub>∞</sub> norm for the vector with the given components + * @see <a href="http://mathworld.wolfram.com/L-Infinity-Norm.html">L<sub>∞</sub> Norm</a> + */ + public static double normInf(final double x) { + return Math.abs(x); + } + + /** Get the L<sub>∞</sub> norm for the vector with the given components. This is defined + * as the maximum of the absolute values of all vector components. + * @param x1 first vector component + * @param x2 second vector component + * @return L<sub>∞</sub> norm for the vector with the given components + * @see <a href="http://mathworld.wolfram.com/L-Infinity-Norm.html">L<sub>∞</sub> Norm</a> + */ + public static double normInf(final double x1, final double x2) { + return Math.max(Math.abs(x1), Math.abs(x2)); + } + + /** Get the L<sub>∞</sub> norm for the vector with the given components. This is defined + * as the maximum of the absolute values of all vector components. + * @param x1 first vector component + * @param x2 second vector component + * @param x3 third vector component + * @return L<sub>∞</sub> norm for the vector with the given components + * @see <a href="http://mathworld.wolfram.com/L-Infinity-Norm.html">L<sub>∞</sub> Norm</a> + */ + public static double normInf(final double x1, final double x2, final double x3) { + return Math.max(Math.max(Math.abs(x1), Math.abs(x2)), Math.abs(x3)); + } + + /** Private constructor. */ + private Vectors() {} +} diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Geometry.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/package-info.java similarity index 56% copy from commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Geometry.java copy to commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/package-info.java index d74d312..fd6dea3 100644 --- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Geometry.java +++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/util/package-info.java @@ -14,27 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.commons.geometry.core; - -/** Class containing geometric constants. +/** + * + * <p> + * This package contains geometry utility classes. + * </p> */ -public class Geometry { - - /** Alias for {@link Math#PI}, placed here for completeness. */ - public static final double PI = Math.PI; - - /** Constant value for {@code 2*pi}. - */ - public static final double TWO_PI = 2.0 * Math.PI; - - /** Constant value for {@code pi / 2}. - */ - public static final double HALF_PI = 0.5 * Math.PI; - - /** Constant value for {@code - pi / 2}. - */ - public static final double MINUS_HALF_PI = - 0.5 * Math.PI; - - /** Private constructor */ - private Geometry() {} -} +package org.apache.commons.geometry.core.util; diff --git a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/util/VectorsTest.java b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/util/VectorsTest.java new file mode 100644 index 0000000..253192a --- /dev/null +++ b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/util/VectorsTest.java @@ -0,0 +1,178 @@ +/* + * 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.util; + +import org.junit.Assert; +import org.junit.Test; + + +public class VectorsTest { + + private static final double EPS = Math.ulp(1d); + + @Test + public void testNorm1_oneD() { + // act/assert + Assert.assertEquals(0.0, Vectors.norm1(0.0), EPS); + + Assert.assertEquals(2.0, Vectors.norm1(-2.0), EPS); + Assert.assertEquals(1.0, Vectors.norm1(-1.0), EPS); + + Assert.assertEquals(1.0, Vectors.norm1(1.0), EPS); + Assert.assertEquals(2.0, Vectors.norm1(2.0), EPS); + } + + @Test + public void testNorm1_twoD() { + // act/assert + Assert.assertEquals(0.0, Vectors.norm1(0.0, 0.0), EPS); + + Assert.assertEquals(3.0, Vectors.norm1(1.0, 2.0), EPS); + Assert.assertEquals(7.0, Vectors.norm1(3.0, -4.0), EPS); + Assert.assertEquals(11.0, Vectors.norm1(-5.0, 6.0), EPS); + Assert.assertEquals(16.0, Vectors.norm1(-7.0, -9.0), EPS); + } + + @Test + public void testNorm1_threeD() { + // act/assert + Assert.assertEquals(0.0, Vectors.norm1(0.0, 0.0, 0.0), EPS); + + Assert.assertEquals(6.0, Vectors.norm1(1.0, 2.0, 3.0), EPS); + Assert.assertEquals(15.0, Vectors.norm1(4.0, 5.0, -6.0), EPS); + Assert.assertEquals(24.0, Vectors.norm1(7.0, -8.0, 9.0), EPS); + Assert.assertEquals(33.0, Vectors.norm1(10.0, -11.0, -12.0), EPS); + Assert.assertEquals(42.0, Vectors.norm1(-13.0, 14.0, 15.0), EPS); + Assert.assertEquals(51.0, Vectors.norm1(-16.0, 17.0, -18.0), EPS); + Assert.assertEquals(60.0, Vectors.norm1(-19.0, -20.0, 21.0), EPS); + Assert.assertEquals(69.0, Vectors.norm1(-22.0, -23.0, -24.0), EPS); + } + + @Test + public void testNorm_oneD() { + // act/assert + Assert.assertEquals(0.0, Vectors.norm(0.0), EPS); + + Assert.assertEquals(2.0, Vectors.norm(-2.0), EPS); + Assert.assertEquals(1.0, Vectors.norm(-1.0), EPS); + + Assert.assertEquals(1.0, Vectors.norm(1.0), EPS); + Assert.assertEquals(2.0, Vectors.norm(2.0), EPS); + } + + @Test + public void testNorm_twoD() { + // act/assert + Assert.assertEquals(0.0, Vectors.norm(0.0, 0.0), EPS); + + Assert.assertEquals(Math.sqrt(5.0), Vectors.norm(1.0, 2.0), EPS); + Assert.assertEquals(5.0, Vectors.norm(3.0, -4.0), EPS); + Assert.assertEquals(Math.sqrt(61.0), Vectors.norm(-5.0, 6.0), EPS); + Assert.assertEquals(Math.sqrt(130.0), Vectors.norm(-7.0, -9.0), EPS); + } + + @Test + public void testNorm_threeD() { + // act/assert + Assert.assertEquals(0.0, Vectors.norm(0.0, 0.0, 0.0), EPS); + + Assert.assertEquals(Math.sqrt(14.0), Vectors.norm(1.0, 2.0, 3.0), EPS); + Assert.assertEquals(Math.sqrt(77.0), Vectors.norm(4.0, 5.0, -6.0), EPS); + Assert.assertEquals(Math.sqrt(194.0), Vectors.norm(7.0, -8.0, 9.0), EPS); + Assert.assertEquals(Math.sqrt(365.0), Vectors.norm(10.0, -11.0, -12.0), EPS); + Assert.assertEquals(Math.sqrt(590.0), Vectors.norm(-13.0, 14.0, 15.0), EPS); + Assert.assertEquals(Math.sqrt(869.0), Vectors.norm(-16.0, 17.0, -18.0), EPS); + Assert.assertEquals(Math.sqrt(1202.0), Vectors.norm(-19.0, -20.0, 21.0), EPS); + Assert.assertEquals(Math.sqrt(1589.0), Vectors.norm(-22.0, -23.0, -24.0), EPS); + } + + @Test + public void testNormSq_oneD() { + // act/assert + Assert.assertEquals(0.0, Vectors.normSq(0.0), EPS); + + Assert.assertEquals(9.0, Vectors.normSq(-3.0), EPS); + Assert.assertEquals(1.0, Vectors.normSq(-1.0), EPS); + + Assert.assertEquals(1.0, Vectors.normSq(1.0), EPS); + Assert.assertEquals(9.0, Vectors.normSq(3.0), EPS); + } + + @Test + public void testNormSq_twoD() { + // act/assert + Assert.assertEquals(0.0, Vectors.normSq(0.0, 0.0), EPS); + + Assert.assertEquals(5.0, Vectors.normSq(1.0, 2.0), EPS); + Assert.assertEquals(25.0, Vectors.normSq(3.0, -4.0), EPS); + Assert.assertEquals(61.0, Vectors.normSq(-5.0, 6.0), EPS); + Assert.assertEquals(130.0, Vectors.normSq(-7.0, -9.0), EPS); + } + + @Test + public void testNormSq_threeD() { + // act/assert + Assert.assertEquals(0.0, Vectors.normSq(0.0, 0.0, 0.0), EPS); + + Assert.assertEquals(14.0, Vectors.normSq(1.0, 2.0, 3.0), EPS); + Assert.assertEquals(77.0, Vectors.normSq(4.0, 5.0, -6.0), EPS); + Assert.assertEquals(194.0, Vectors.normSq(7.0, -8.0, 9.0), EPS); + Assert.assertEquals(365.0, Vectors.normSq(10.0, -11.0, -12.0), EPS); + Assert.assertEquals(590.0, Vectors.normSq(-13.0, 14.0, 15.0), EPS); + Assert.assertEquals(869.0, Vectors.normSq(-16.0, 17.0, -18.0), EPS); + Assert.assertEquals(1202.0, Vectors.normSq(-19.0, -20.0, 21.0), EPS); + Assert.assertEquals(1589.0, Vectors.normSq(-22.0, -23.0, -24.0), EPS); + } + + @Test + public void testNormInf_oneD() { + // act/assert + Assert.assertEquals(0.0, Vectors.normInf(0.0), EPS); + + Assert.assertEquals(2.0, Vectors.normInf(-2.0), EPS); + Assert.assertEquals(1.0, Vectors.normInf(-1.0), EPS); + + Assert.assertEquals(1.0, Vectors.normInf(1.0), EPS); + Assert.assertEquals(2.0, Vectors.normInf(2.0), EPS); + } + + @Test + public void testNormInf_twoD() { + // act/assert + Assert.assertEquals(0.0, Vectors.normInf(0.0, 0.0), EPS); + + Assert.assertEquals(2.0, Vectors.normInf(2.0, 1.0), EPS); + Assert.assertEquals(4.0, Vectors.normInf(3.0, -4.0), EPS); + Assert.assertEquals(6.0, Vectors.normInf(-6.0, 5.0), EPS); + Assert.assertEquals(9.0, Vectors.normInf(-7.0, -9.0), EPS); + } + + @Test + public void testNormInf_threeD() { + // act/assert + Assert.assertEquals(0.0, Vectors.normInf(0.0, 0.0, 0.0), EPS); + + Assert.assertEquals(3.0, Vectors.normInf(1.0, 3.0, 2.0), EPS); + Assert.assertEquals(6.0, Vectors.normInf(6.0, 5.0, -4.0), EPS); + Assert.assertEquals(9.0, Vectors.normInf(7.0, -9.0, 8.0), EPS); + Assert.assertEquals(12.0, Vectors.normInf(10.0, -11.0, -12.0), EPS); + Assert.assertEquals(15.0, Vectors.normInf(-13.0, 14.0, 15.0), EPS); + Assert.assertEquals(18.0, Vectors.normInf(-16.0, 17.0, -18.0), EPS); + Assert.assertEquals(21.0, Vectors.normInf(-21.0, -19.0, 20.0), EPS); + Assert.assertEquals(24.0, Vectors.normInf(-22.0, -23.0, -24.0), EPS); + } +} diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java index 0622c89..3d8874b 100644 --- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java +++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java @@ -32,4 +32,27 @@ public interface EuclideanVector<P extends EuclideanPoint<P, V>, V extends Eucli * @return point with the same coordinates as this vector */ P asPoint(); + + /** Returns the magnitude (i.e. length) of the vector. This is + * the same value as returned by {@link #getNorm()}. + * @return the magnitude, or length, of the vector + * @see #getNorm() + */ + double getMagnitude(); + + /** Returns the squared magnitude of the vector. This is the + * same value as returned by {@link #getNormSq()}. + * @return the squared magnitude of the vector + * @see #getMagnitude() + * @see #getNormSq() + */ + double getMagnitudeSq(); + + /** Returns a vector with the same direction but with the given + * magnitude. This is equivalent to calling {@code vec.normalize().scalarMultiply(mag)} + * but without the intermediate vector. + * @param magnitude The vector magnitude + * @return a vector with the same direction as the current instance but the given magnitude + */ + V withMagnitude(double magnitude); } 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 1a915f6..11786a5 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 @@ -18,6 +18,7 @@ package org.apache.commons.geometry.euclidean.oned; import org.apache.commons.geometry.core.internal.DoubleFunction1N; import org.apache.commons.geometry.core.internal.SimpleTupleFormat; +import org.apache.commons.geometry.core.util.Vectors; import org.apache.commons.geometry.euclidean.EuclideanVector; import org.apache.commons.numbers.arrays.LinearCombination; @@ -80,29 +81,49 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public double getNorm1() { - return getNorm(); + return Vectors.norm1(getX()); } /** {@inheritDoc} */ @Override public double getNorm() { - return Math.abs(getX()); + return Vectors.norm(getX()); } /** {@inheritDoc} */ @Override public double getNormSq() { - return getX() * getX(); + return Vectors.normSq(getX()); } /** {@inheritDoc} */ @Override public double getNormInf() { + return Vectors.normInf(getX()); + } + + /** {@inheritDoc} */ + @Override + public double getMagnitude() { return getNorm(); } /** {@inheritDoc} */ @Override + public double getMagnitudeSq() { + return getNormSq(); + } + + /** {@inheritDoc} */ + @Override + public Vector1D withMagnitude(double magnitude) { + final double invNorm = 1.0 / nonZeroNorm(); + + return new Vector1D(magnitude * getX() * invNorm); + } + + /** {@inheritDoc} */ + @Override public Vector1D add(Vector1D v) { return new Vector1D(getX() + v.getX()); } @@ -134,11 +155,7 @@ public final class Vector1D extends Cartesian1D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public Vector1D normalize() throws IllegalStateException { - double s = getNorm(); - if (s == 0) { - throw new IllegalStateException("Norm is zero"); - } - return scalarMultiply(1.0 / s); + return scalarMultiply(1.0 / nonZeroNorm()); } /** {@inheritDoc} */ @@ -229,6 +246,19 @@ public final class Vector1D extends Cartesian1D 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 + */ + private double nonZeroNorm() throws IllegalStateException { + final double n = getNorm(); + if (n == 0) { + throw new IllegalStateException("Norm is zero"); + } + + return n; + } + /** Returns a vector with the given coordinate value. * @param x vector coordinate * @return vector instance diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Cartesian3D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Cartesian3D.java index 6dbe753..4cb6ce0 100644 --- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Cartesian3D.java +++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Cartesian3D.java @@ -101,17 +101,4 @@ public abstract class Cartesian3D implements Spatial, Serializable { public String toString() { return SimpleTupleFormat.getDefault().format(getX(), getY(), getZ()); } - - /** Returns the Euclidean distance from this set of coordinates to the given coordinates. - * @param other coordinates to compute the distance to. - * @return Euclidean distance value - */ - protected double euclideanDistance(Cartesian3D other) { - // there are no cancellation problems here, so we use the straightforward formula - final double dx = x - other.x; - final double dy = y - other.y; - final double dz = z - other.z; - - return Math.sqrt((dx * dx) + (dy * dy) + (dz * dz)); - } } 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 7c2b3f7..39ed989 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 @@ -19,6 +19,7 @@ package org.apache.commons.geometry.euclidean.threed; import org.apache.commons.geometry.core.internal.DoubleFunction3N; import org.apache.commons.geometry.core.internal.SimpleTupleFormat; +import org.apache.commons.geometry.core.util.Vectors; import org.apache.commons.geometry.euclidean.EuclideanPoint; import org.apache.commons.numbers.arrays.LinearCombination; @@ -75,7 +76,10 @@ public final class Point3D extends Cartesian3D implements EuclideanPoint<Point3D /** {@inheritDoc} */ @Override public double distance(Point3D p) { - return euclideanDistance(p); + return Vectors.norm( + getX() - p.getX(), + getY() - p.getY(), + getZ() - p.getZ()); } /** {@inheritDoc} */ 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 459b014..41e47e8 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 @@ -18,6 +18,7 @@ package org.apache.commons.geometry.euclidean.threed; import org.apache.commons.geometry.core.internal.DoubleFunction3N; import org.apache.commons.geometry.core.internal.SimpleTupleFormat; +import org.apache.commons.geometry.core.util.Vectors; import org.apache.commons.geometry.euclidean.EuclideanVector; import org.apache.commons.numbers.arrays.LinearCombination; @@ -47,7 +48,7 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point /** Opposite of the third canonical vector (coordinates: 0, 0, -1). */ public static final Vector3D MINUS_Z = new Vector3D(0, 0, -1); - // CHECKSTYLE: stop ConstantName + // CHECKSTYLE: stop ConstantName /** A vector with all coordinates set to NaN. */ public static final Vector3D NaN = new Vector3D(Double.NaN, Double.NaN, Double.NaN); // CHECKSTYLE: resume ConstantName @@ -63,9 +64,6 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point /** Serializable UID */ private static final long serialVersionUID = 20180710L; - /** Error message when norms are zero. */ - private static final String ZERO_NORM_MSG = "Norm is zero"; - /** Factory for delegating instance creation. */ private static DoubleFunction3N<Vector3D> FACTORY = new DoubleFunction3N<Vector3D>() { @@ -101,33 +99,49 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public double getNorm1() { - return Math.abs(getX()) + Math.abs(getY()) + Math.abs(getZ()); + return Vectors.norm1(getX(), getY(), getZ()); } /** {@inheritDoc} */ @Override public double getNorm() { - // there are no cancellation problems here, so we use the straightforward formula - final double x = getX(); - final double y = getY(); - final double z = getZ(); - return Math.sqrt ((x * x) + (y * y) + (z * z)); + return Vectors.norm(getX(), getY(), getZ()); } /** {@inheritDoc} */ @Override public double getNormSq() { - // there are no cancellation problems here, so we use the straightforward formula - final double x = getX(); - final double y = getY(); - final double z = getZ(); - return (x * x) + (y * y) + (z * z); + return Vectors.normSq(getX(), getY(), getZ()); } /** {@inheritDoc} */ @Override public double getNormInf() { - return Math.max(Math.max(Math.abs(getX()), Math.abs(getY())), Math.abs(getZ())); + return Vectors.normInf(getX(), getY(), getZ()); + } + + /** {@inheritDoc} */ + @Override + public double getMagnitude() { + return getNorm(); + } + + /** {@inheritDoc} */ + @Override + public double getMagnitudeSq() { + return getNormSq(); + } + + /** {@inheritDoc} */ + @Override + public Vector3D withMagnitude(double magnitude) { + final double invNorm = 1.0 / nonZeroNorm(); + + return new Vector3D( + magnitude * getX() * invNorm, + magnitude * getY() * invNorm, + magnitude * getZ() * invNorm + ); } /** Get the azimuth of the vector. @@ -193,11 +207,7 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public Vector3D normalize() throws IllegalStateException { - double s = getNorm(); - if (s == 0) { - throw new IllegalStateException(ZERO_NORM_MSG); - } - return scalarMultiply(1 / s); + return scalarMultiply(1 / nonZeroNorm()); } /** Get a vector orthogonal to the instance. @@ -216,10 +226,7 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point * @exception IllegalStateException if the norm of the instance is zero */ public Vector3D orthogonal() throws IllegalStateException { - double threshold = 0.6 * getNorm(); - if (threshold == 0) { - throw new IllegalStateException(ZERO_NORM_MSG); - } + double threshold = 0.6 * nonZeroNorm(); final double x = getX(); final double y = getY(); @@ -247,10 +254,7 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point * @exception IllegalStateException if either vector has a zero norm */ public double angle(Vector3D v) throws IllegalStateException { - double normProduct = getNorm() * v.getNorm(); - if (normProduct == 0) { - throw new IllegalStateException(ZERO_NORM_MSG); - } + double normProduct = nonZeroNorm() * v.nonZeroNorm(); double dot = dotProduct(v); double threshold = normProduct * 0.9999; @@ -286,37 +290,41 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public double distance1(Vector3D v) { - double dx = Math.abs(v.getX() - getX()); - double dy = Math.abs(v.getY() - getY()); - double dz = Math.abs(v.getZ() - getZ()); - - return dx + dy + dz; + return Vectors.norm1( + getX() - v.getX(), + getY() - v.getY(), + getZ() - v.getZ() + ); } /** {@inheritDoc} */ @Override public double distance(Vector3D v) { - return euclideanDistance(v); + return Vectors.norm( + getX() - v.getX(), + getY() - v.getY(), + getZ() - v.getZ() + ); } /** {@inheritDoc} */ @Override public double distanceInf(Vector3D v) { - double dx = Math.abs(v.getX() - getX()); - double dy = Math.abs(v.getY() - getY()); - double dz = Math.abs(v.getZ() - getZ()); - - return Math.max(Math.max(dx, dy), dz); + return Vectors.normInf( + getX() - v.getX(), + getY() - v.getY(), + getZ() - v.getZ() + ); } /** {@inheritDoc} */ @Override public double distanceSq(Vector3D v) { - double dx = v.getX() - getX(); - double dy = v.getY() - getY(); - double dz = v.getZ() - getZ(); - - return (dx * dx) + (dy * dy) + (dz * dz); + return Vectors.normSq( + getX() - v.getX(), + getY() - v.getY(), + getZ() - v.getZ() + ); } /** {@inheritDoc} @@ -382,6 +390,19 @@ 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 + */ + private double nonZeroNorm() throws IllegalStateException { + final double n = getNorm(); + if (n == 0) { + throw new IllegalStateException("Norm is zero"); + } + + return n; + } + /** Computes the dot product between to vectors. This method simply * calls {@code v1.dotProduct(v2)}. * @param v1 first vector diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Cartesian2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Cartesian2D.java index 8d35fed..dfd375d 100644 --- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Cartesian2D.java +++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Cartesian2D.java @@ -90,14 +90,4 @@ public abstract class Cartesian2D implements Spatial, Serializable { public String toString() { return SimpleTupleFormat.getDefault().format(getX(), getY()); } - - /** Returns the Euclidean distance from this value to the given value. - * @param other the set of coordinates to compute the distance to - * @return Euclidean distance - */ - protected double euclideanDistance(Cartesian2D other) { - double dx = x - other.x; - double dy = y - other.y; - return Math.sqrt((dx * dx) + (dy * dy)); - } } 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 c9fde1f..cb213f2 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 @@ -18,6 +18,7 @@ package org.apache.commons.geometry.euclidean.twod; import org.apache.commons.geometry.core.internal.DoubleFunction2N; import org.apache.commons.geometry.core.internal.SimpleTupleFormat; +import org.apache.commons.geometry.core.util.Vectors; import org.apache.commons.geometry.euclidean.EuclideanPoint; import org.apache.commons.numbers.arrays.LinearCombination; @@ -73,7 +74,7 @@ public final class Point2D extends Cartesian2D implements EuclideanPoint<Point2D /** {@inheritDoc} */ @Override public double distance(Point2D p) { - return euclideanDistance(p); + return Vectors.norm(getX() - p.getX(), getY() - p.getY()); } /** {@inheritDoc} */ 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 4b435cc..819ba2b 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 @@ -18,6 +18,7 @@ package org.apache.commons.geometry.euclidean.twod; import org.apache.commons.geometry.core.internal.DoubleFunction2N; import org.apache.commons.geometry.core.internal.SimpleTupleFormat; +import org.apache.commons.geometry.core.util.Vectors; import org.apache.commons.geometry.euclidean.EuclideanVector; import org.apache.commons.numbers.arrays.LinearCombination; @@ -57,9 +58,6 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point /** Serializable UID */ private static final long serialVersionUID = 20180710L; - /** Error message when norms are zero. */ - private static final String ZERO_NORM_MSG = "Norm is zero"; - /** Factory for delegating instance creation. */ private static DoubleFunction2N<Vector2D> FACTORY = new DoubleFunction2N<Vector2D>() { @@ -101,29 +99,48 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public double getNorm1() { - return Math.abs(getX()) + Math.abs(getY()); + return Vectors.norm1(getX(), getY()); } /** {@inheritDoc} */ @Override public double getNorm() { - final double x = getX(); - final double y = getY(); - return Math.sqrt ((x * x) + (y * y)); + return Vectors.norm(getX(), getY()); } /** {@inheritDoc} */ @Override public double getNormSq() { - final double x = getX(); - final double y = getY(); - return (x * x) + (y * y); + return Vectors.normSq(getX(), getY()); } /** {@inheritDoc} */ @Override public double getNormInf() { - return Math.max(Math.abs(getX()), Math.abs(getY())); + return Vectors.normInf(getX(), getY()); + } + + /** {@inheritDoc} */ + @Override + public double getMagnitude() { + return getNorm(); + } + + /** {@inheritDoc} */ + @Override + public double getMagnitudeSq() { + return getNormSq(); + } + + /** {@inheritDoc} */ + @Override + public Vector2D withMagnitude(double magnitude) { + final double invNorm = 1.0 / nonZeroNorm(); + + return new Vector2D( + magnitude * getX() * invNorm, + magnitude * getY() * invNorm + ); } /** {@inheritDoc} */ @@ -159,11 +176,7 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public Vector2D normalize() throws IllegalStateException { - double n = getNorm(); - if (n == 0) { - throw new IllegalStateException(ZERO_NORM_MSG); - } - return scalarMultiply(1 / n); + return scalarMultiply(1.0 / nonZeroNorm()); } /** {@inheritDoc} */ @@ -175,31 +188,25 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point /** {@inheritDoc} */ @Override public double distance1(Vector2D v) { - double dx = Math.abs(getX() - v.getX()); - double dy = Math.abs(getY() - v.getY()); - return dx + dy; + return Vectors.norm1(getX() - v.getX(), getY() - v.getY()); } /** {@inheritDoc} */ @Override public double distance(Vector2D v) { - return euclideanDistance(v); + return Vectors.norm(getX() - v.getX(), getY() - v.getY()); } /** {@inheritDoc} */ @Override public double distanceInf(Vector2D v) { - double dx = Math.abs(getX() - v.getX()); - double dy = Math.abs(getY() - v.getY()); - return Math.max(dx, dy); + return Vectors.normInf(getX() - v.getX(), getY() - v.getY()); } /** {@inheritDoc} */ @Override public double distanceSq(Vector2D v) { - double dx = getX() - v.getX(); - double dy = getY() - v.getY(); - return (dx * dx) + (dy * dy); + return Vectors.normSq(getX() - v.getX(), getY() - v.getY()); } /** {@inheritDoc} */ @@ -218,13 +225,10 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point * * @param v vector to compute the angle with * @return angular separation between this vector and v in radians - * @exception IllegalArgumentException if either vector has a zero norm + * @exception IllegalStateException if either vector has a zero norm */ public double angle(Vector2D v) throws IllegalArgumentException { - double normProduct = getNorm() * v.getNorm(); - if (normProduct == 0) { - throw new IllegalArgumentException(ZERO_NORM_MSG); - } + double normProduct = nonZeroNorm() * v.nonZeroNorm(); double dot = dotProduct(v); double threshold = normProduct * 0.9999; @@ -320,6 +324,19 @@ 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 + */ + private double nonZeroNorm() throws IllegalStateException { + final double n = getNorm(); + if (n == 0) { + throw new IllegalStateException("Norm is zero"); + } + + return n; + } + /** Computes the dot product between to vectors. This method simply * calls {@code v1.dotProduct(v2)}. * @param v1 first vector 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 15c864f..491b0c0 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 @@ -74,6 +74,40 @@ public class Vector1DTest { } @Test + public void testMagnitude() { + // act/assert + Assert.assertEquals(0.0, Vector1D.ZERO.getMagnitude(), TEST_TOLERANCE); + Assert.assertEquals(3.0, Vector1D.of(3).getMagnitude(), TEST_TOLERANCE); + Assert.assertEquals(3.0, Vector1D.of(-3).getMagnitude(), TEST_TOLERANCE); + } + + @Test + public void testMagnitudeSq() { + // act/assert + Assert.assertEquals(0.0, Vector1D.of(0).getMagnitudeSq(), TEST_TOLERANCE); + Assert.assertEquals(9.0, Vector1D.of(3).getMagnitudeSq(), TEST_TOLERANCE); + Assert.assertEquals(9.0, Vector1D.of(-3).getMagnitudeSq(), TEST_TOLERANCE); + } + + @Test + public void testWithMagnitude() { + // act/assert + checkVector(Vector1D.ONE.withMagnitude(0.0), 0.0); + + checkVector(Vector1D.of(0.5).withMagnitude(2.0), 2.0); + checkVector(Vector1D.of(5).withMagnitude(3.0), 3.0); + + checkVector(Vector1D.of(-0.5).withMagnitude(2.0), -2.0); + checkVector(Vector1D.of(-5).withMagnitude(3.0), -3.0); + } + + @Test(expected = IllegalStateException.class) + public void testWithMagnitude_zeroNorm() { + // act/assert + Vector1D.ZERO.withMagnitude(1.0); + } + + @Test public void testAdd() { // arrange Vector1D v1 = Vector1D.of(1); diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java index c4017bd..1b029cb 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 @@ -87,7 +87,7 @@ public class Vector3DTest { @Test public void testNormSq() { // act/assert - Assert.assertEquals(0.0, Vector3D.ZERO.getNorm(), 0); + Assert.assertEquals(0.0, Vector3D.ZERO.getNormSq(), 0); Assert.assertEquals(29, Vector3D.of(2, 3, 4).getNormSq(), EPS); Assert.assertEquals(29, Vector3D.of(-2, -3, -4).getNormSq(), EPS); } @@ -101,6 +101,55 @@ public class Vector3DTest { } @Test + public void testMagnitude() { + // act/assert + Assert.assertEquals(0.0, Vector3D.ZERO.getMagnitude(), 0); + Assert.assertEquals(Math.sqrt(29), Vector3D.of(2, 3, 4).getMagnitude(), EPS); + Assert.assertEquals(Math.sqrt(29), Vector3D.of(-2, -3, -4).getMagnitude(), EPS); + } + + @Test + public void testMagnitudeSq() { + // act/assert + Assert.assertEquals(0.0, Vector3D.ZERO.getMagnitudeSq(), 0); + Assert.assertEquals(29, Vector3D.of(2, 3, 4).getMagnitudeSq(), EPS); + Assert.assertEquals(29, Vector3D.of(-2, -3, -4).getMagnitudeSq(), EPS); + } + + @Test + public void testWithMagnitude() { + // arrange + double x = 2; + double y = 3; + double z = 4; + + double len = Math.sqrt((x * x) + (y * y) + (z * z)); + + double normX = x / len; + double normY = y / len; + double normZ = z / len; + + // act/assert + checkVector(Vector3D.of(x, y, z).withMagnitude(1.0), normX, normY, normZ); + checkVector(Vector3D.of(x, y, -z).withMagnitude(1.0), normX, normY, -normZ); + checkVector(Vector3D.of(x, -y, z).withMagnitude(1.0), normX, -normY, normZ); + checkVector(Vector3D.of(x, -y, -z).withMagnitude(1.0), normX, -normY, -normZ); + checkVector(Vector3D.of(-x, y, z).withMagnitude(1.0), -normX, normY, normZ); + checkVector(Vector3D.of(-x, y, -z).withMagnitude(1.0), -normX, normY, -normZ); + checkVector(Vector3D.of(-x, -y, z).withMagnitude(1.0), -normX, -normY, normZ); + checkVector(Vector3D.of(-x, -y, -z).withMagnitude(1.0), -normX, -normY, -normZ); + + checkVector(Vector3D.of(x, y, z).withMagnitude(0.5), 0.5 * normX, 0.5 * normY, 0.5 * normZ); + checkVector(Vector3D.of(x, y, z).withMagnitude(3), 3 * normX, 3 * normY, 3 * normZ); + } + + @Test(expected = IllegalStateException.class) + public void testWithMagnitude_zeroNorm() { + // act/assert + Vector3D.ZERO.withMagnitude(1.0); + } + + @Test public void testAdd() { // arrange Vector3D v1 = Vector3D.of(1, 2, 3); 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 83d56a1..58f1ee5 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 @@ -104,6 +104,51 @@ public class Vector2DTest { } @Test + public void testMagnitude() { + // act/assert + Assert.assertEquals(0.0, Vector2D.of(0, 0).getMagnitude(), EPS); + + Assert.assertEquals(5.0, Vector2D.of(3, 4).getMagnitude(), EPS); + Assert.assertEquals(5.0, Vector2D.of(3, -4).getMagnitude(), EPS); + Assert.assertEquals(5.0, Vector2D.of(-3, 4).getMagnitude(), EPS); + Assert.assertEquals(5.0, Vector2D.of(-3, -4).getMagnitude(), EPS); + + Assert.assertEquals(Math.sqrt(5.0), Vector2D.of(-1, -2).getMagnitude(), EPS); + } + + @Test + public void testMagnitudeSq() { + // act/assert + Assert.assertEquals(0.0, Vector2D.of(0, 0).getMagnitudeSq(), EPS); + + Assert.assertEquals(25.0, Vector2D.of(3, 4).getMagnitudeSq(), EPS); + Assert.assertEquals(25.0, Vector2D.of(3, -4).getMagnitudeSq(), EPS); + Assert.assertEquals(25.0, Vector2D.of(-3, 4).getMagnitudeSq(), EPS); + Assert.assertEquals(25.0, Vector2D.of(-3, -4).getMagnitudeSq(), EPS); + + Assert.assertEquals(5.0, Vector2D.of(-1, -2).getMagnitudeSq(), EPS); + } + + @Test + public void testWithMagnitude() { + // act/assert + checkVector(Vector2D.of(3, 4).withMagnitude(1.0), 0.6, 0.8); + checkVector(Vector2D.of(4, 3).withMagnitude(1.0), 0.8, 0.6); + + checkVector(Vector2D.of(-3, 4).withMagnitude(0.5), -0.3, 0.4); + checkVector(Vector2D.of(3, -4).withMagnitude(2.0), 1.2, -1.6); + checkVector(Vector2D.of(-3, -4).withMagnitude(3.0), -1.8, 3.0 * Math.sin(Math.atan2(-4, -3))); + + checkVector(Vector2D.of(0.5, 0.5).withMagnitude(2), Math.sqrt(2), Math.sqrt(2)); + } + + @Test(expected = IllegalStateException.class) + public void testWithMagnitude_zeroNorm() { + // act/assert + Vector2D.ZERO.withMagnitude(1.0); + } + + @Test public void testAdd() { // arrange Vector2D v1 = Vector2D.of(-1, 2); @@ -353,7 +398,7 @@ public class Vector2DTest { } - @Test(expected = IllegalArgumentException.class) + @Test(expected = IllegalStateException.class) public void testAngle_zeroNorm() { Vector2D.of(1, 1).angle(Vector2D.ZERO); }
