You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2018/07/21 10:07:38 UTC

[commons-geometry] branch master updated (8273d59 -> d7b4a10)

This is an automated email from the ASF dual-hosted git repository.

erans pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/commons-geometry.git.


    from 8273d59  Merge branch 'GEOMETRY-3_GEOMETRY-4__matt'
     new 76086cc  GEOMETRY-7: adding PolarCoordinates class
     new abb1a97  using built-in Math.hypot() method for 2D Euclidean distance calculation
     new 96f32b6  Merge branch 'master' into polar-spherical-working
     new a1e38d2  GEOMETRY-7: adding basic SphericalCoordinates class
     new d2b0c02  GEOMETRY-7: simplifying PolarCoordinate toString/parse representation
     new fefd035  GEOMETRY-7: adding previous spherical gradient and Hessian convertion to new SphericalDerivativeConverter class; removing old SphericalCoordinates class
     new f1a6d4b  GEOMETRY-7: adding toSpherical() and ofSpherical() to Cartesian 3D classes
     new ccd1928  GEOMETRY-7: making PolarCoordinates and SphericalCoordinates final; adding class javadocs
     new 1c54b6c  GEOEMTRY-7: removing spherical coordinate code for working with jacobians and hessians since I'm not sure how it would be used; it can be added back later if needed
     new 2593603  GEOMETRY-7: creating static methods for azimuth and polar angle normalization; using polar and spherical naming conventions and conversion methods in S1Point and S2Point
     new c892bfe  GEOMETRY-7: changing polar/spherical azimuth convention from (-pi, +pi] range to [0, 2pi) range in order to match S1Point and S2Point conventions
     new 9b3f388  GEOMETRY-7: merging polar and spherical code with latest from master
     new 6dc7b9b  GEOMETRY-7: fixing Javadoc and CheckStyle issues
     new 49adafa  License header.
     new d7b4a10  Merge branch 'GEOMETRY-7__matt'

The 15 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/commons/geometry/core/Geometry.java |  20 +-
 .../apache/commons/geometry/core/GeometryTest.java |  66 ++++
 .../geometry/euclidean/threed/Cartesian3D.java     |   7 +
 .../commons/geometry/euclidean/threed/Point3D.java |  21 +-
 .../euclidean/threed/SphericalCoordinates.java     | 312 ++++++++++++++++
 .../geometry/euclidean/threed/Vector3D.java        |  54 +--
 .../geometry/euclidean/twod/Cartesian2D.java       |  13 +-
 .../commons/geometry/euclidean/twod/Point2D.java   |  19 +-
 .../geometry/euclidean/twod/PolarCoordinates.java  | 262 ++++++++++++++
 .../commons/geometry/euclidean/twod/Vector2D.java  |  25 +-
 .../geometry/euclidean/oned/Vector1DTest.java      |  16 +
 .../geometry/euclidean/threed/Cartesian3DTest.java |  45 +++
 .../geometry/euclidean/threed/Point3DTest.java     |  24 +-
 .../euclidean/threed/SphericalCoordinatesTest.java | 399 +++++++++++++++++++++
 .../geometry/euclidean/threed/Vector3DTest.java    |  58 ++-
 .../geometry/euclidean/twod/Cartesian2DTest.java   |  50 ++-
 .../geometry/euclidean/twod/Point2DTest.java       |  31 +-
 .../euclidean/twod/PolarCoordinatesTest.java       | 362 +++++++++++++++++++
 .../geometry/euclidean/twod/Vector2DTest.java      |  47 ++-
 .../geometry/spherical/SphericalCoordinates.java   | 394 --------------------
 .../commons/geometry/spherical/oned/ArcsSet.java   |   4 +-
 .../geometry/spherical/oned/LimitAngle.java        |   2 +-
 .../commons/geometry/spherical/oned/S1Point.java   |  54 ++-
 .../commons/geometry/spherical/package-info.java   |  23 --
 .../commons/geometry/spherical/twod/Circle.java    |   2 +-
 .../commons/geometry/spherical/twod/S2Point.java   | 113 +++---
 .../spherical/SphericalCoordinatesTest.java        |  83 -----
 .../geometry/spherical/SphericalTestUtils.java     |   2 +-
 .../geometry/spherical/oned/LimitAngleTest.java    |   2 +-
 .../geometry/spherical/oned/S1PointTest.java       |   4 +-
 .../geometry/spherical/twod/CircleTest.java        |  10 +-
 .../geometry/spherical/twod/S2PointTest.java       |  20 +-
 .../spherical/twod/SphericalPolygonsSetTest.java   |   4 +-
 33 files changed, 1810 insertions(+), 738 deletions(-)
 create mode 100644 commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTest.java
 create mode 100644 commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
 create mode 100644 commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
 create mode 100644 commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
 create mode 100644 commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
 delete mode 100644 commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/SphericalCoordinates.java
 delete mode 100644 commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/package-info.java
 delete mode 100644 commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/SphericalCoordinatesTest.java


[commons-geometry] 04/15: GEOMETRY-7: adding basic SphericalCoordinates class

Posted by er...@apache.org.
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 a1e38d23c9c7ae015cb81db48114e6bc48afc431
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Sat Jun 30 22:29:31 2018 -0400

    GEOMETRY-7: adding basic SphericalCoordinates class
---
 .../euclidean/threed/SphericalCoordinates.java     | 280 ++++++++++++++++
 .../euclidean/threed/SphericalCoordinates_OLD.java |  18 +-
 .../geometry/euclidean/threed/Vector3D.java        |  14 -
 .../geometry/euclidean/twod/PolarCoordinates.java  |  52 +--
 .../euclidean/threed/SphericalCoordinatesTest.java | 368 +++++++++++++++++++++
 .../threed/SphericalCoordinatesTest_OLD.java       |  33 +-
 .../geometry/euclidean/threed/Vector3DTest.java    |  14 -
 .../commons/geometry/spherical/package-info.java   |  23 --
 8 files changed, 700 insertions(+), 102 deletions(-)

diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
new file mode 100644
index 0000000..609e90b
--- /dev/null
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
@@ -0,0 +1,280 @@
+/*
+ * 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.euclidean.threed;
+
+import java.io.Serializable;
+
+import org.apache.commons.geometry.core.Geometry;
+import org.apache.commons.geometry.core.Spatial;
+import org.apache.commons.geometry.core.util.Coordinates;
+import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.numbers.angle.PlaneAngleRadians;
+
+/** Class representing a set of spherical coordinates in 3 dimensional Euclidean space.
+ */
+public class SphericalCoordinates implements Spatial, Serializable {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 20180623L;
+
+    /** Factory object for delegating instance creation. */
+    private static final Coordinates.Factory3D<SphericalCoordinates> FACTORY = new Coordinates.Factory3D<SphericalCoordinates>() {
+
+        /** {@inheritDoc} */
+        @Override
+        public SphericalCoordinates create(double a1, double a2, double a3) {
+            return new SphericalCoordinates(a1, a2, a3);
+        }
+
+    };
+
+    /** Radius value. */
+    private final double radius;
+
+    /** Azimuth angle in radians. */
+    private final double azimuth;
+
+    /** Polar angle in radians. */
+    private final double polar;
+
+    /** Simple constructor. The given inputs are normalized.
+     * @param radius Radius value.
+     * @param azimuth Azimuth angle in radians.
+     * @param polar Polar angle in radians.
+     */
+    private SphericalCoordinates(double radius, double azimuth, double polar) {
+        if (radius < 0) {
+            // negative radius; flip the angles
+            radius = Math.abs(radius);
+            azimuth += Geometry.PI;
+            polar += Geometry.PI;
+        }
+
+        if (Double.isFinite(azimuth) && (azimuth <= Geometry.MINUS_PI || azimuth > Geometry.PI)) {
+            azimuth = PlaneAngleRadians.normalizeBetweenMinusPiAndPi(azimuth);
+
+            // azimuth is now in the range [-pi, pi] but we want it to be in the range
+            // (-pi, pi] in order to have completely unique coordinates
+            if (azimuth <= -Geometry.PI) {
+                azimuth += Geometry.TWO_PI;
+            }
+        }
+
+        // normalize the polar angle; this is the angle between the polar vector and the point ray
+        // so it is unsigned (unlike the azimuth) and should be in the range [0, pi]
+        if (Double.isFinite(polar)) {
+            polar = Math.abs(PlaneAngleRadians.normalizeBetweenMinusPiAndPi(polar));
+        }
+
+        this.radius = radius;
+        this.azimuth = azimuth;
+        this.polar = polar;
+    }
+
+    /** Return the radius value. The value is in the range {@code [0, +infinity)}.
+     * @return the radius value
+     */
+    public double getRadius() {
+        return radius;
+    }
+
+    /** Return the azimuth angle in radians. This is the angle in the x-y plane measured counter-clockwise from
+     * the positive x axis. The angle is in the range {@code (-pi, pi]}.
+     * @return the azimuth angle in radians
+     */
+    public double getAzimuth() {
+        return azimuth;
+    }
+
+    /** Return the polar angle in radians. This is the angle the coordinate ray makes with the positive z axis.
+     * The angle is in the range {@code [0, pi]}.
+     * @return the polar angle in radians
+     */
+    public double getPolar() {
+        return polar;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getDimension() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isNaN() {
+        return Double.isNaN(radius) || Double.isNaN(azimuth) || Double.isNaN(polar);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isInfinite() {
+        return !isNaN() && (Double.isInfinite(radius) || Double.isInfinite(azimuth) || Double.isInfinite(polar));
+    }
+
+    /** Convert this set of spherical coordinates to Cartesian coordinates.
+     * The Cartesian coordinates are computed and passed to the given
+     * factory instance. The factory's return value is returned.
+     * @param factory Factory instance that will be passed the computed Cartesian coordinates
+     * @return the value returned by the factory when passed Cartesian
+     *      coordinates equivalent to this set of spherical coordinates.
+     */
+    public <T> T toCartesian(final Coordinates.Factory3D<T> factory) {
+        return toCartesian(radius, azimuth, polar, factory);
+    }
+
+    /** Convert this set of spherical coordinates to a 3 dimensional vector.
+     * @return A 3-dimensional vector with an equivalent set of
+     *      coordinates.
+     */
+    public Vector3D toVector() {
+        return toCartesian(Vector3D.getFactory());
+    }
+
+    /** Convert this set of spherical coordinates to a 3 dimensional point.
+    * @return A 3-dimensional point with an equivalent set of
+    *      coordinates.
+    */
+    public Point3D toPoint() {
+        return toCartesian(Point3D.getFactory());
+    }
+
+    /**
+     * Get a hashCode for this set of spherical coordinates.
+     * <p>All NaN values have the same hash code.</p>
+     *
+     * @return a hash code value for this object
+     */
+    @Override
+    public int hashCode() {
+        if (isNaN()) {
+            return 127;
+        }
+        return 449 * (79 * Double.hashCode(radius) + Double.hashCode(azimuth) + Double.hashCode(polar));
+    }
+
+    /** Test for the equality of two sets of spherical coordinates.
+     * <p>
+     * If all values of two sets of coordinates are exactly the same, and none are
+     * <code>Double.NaN</code>, the two sets are considered to be equal.
+     * </p>
+     * <p>
+     * <code>NaN</code> values are considered to globally affect the coordinates
+     * and be equal to each other - i.e, if either (or all) values of the
+     * coordinate set are equal to <code>Double.NaN</code>, the set is equal to
+     * {@link #NaN}.
+     * </p>
+     *
+     * @param other Object to test for equality to this
+     * @return true if two SphericalCoordinates objects are equal, false if
+     *         object is null, not an instance of SphericalCoordinates, or
+     *         not equal to this SphericalCoordinates instance
+     *
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (other instanceof SphericalCoordinates) {
+            final SphericalCoordinates rhs = (SphericalCoordinates) other;
+            if (rhs.isNaN()) {
+                return this.isNaN();
+            }
+
+            return (radius == rhs.radius) && (azimuth == rhs.azimuth) && (polar == rhs.polar);
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return SimpleCoordinateFormat.getPointFormat().format(radius, azimuth, polar);
+    }
+
+    /** Create a {@link SphericalCoordinates} instance from the given values. The values are normalized
+     * so that {@code radius} lies in the range {@code [0, +infinity)}, {@code azimuth} lies in the range
+     * {@code (-pi, +pi]}, and {@code polar} lies in the range {@code [0, +pi]}.
+     * @param radius the length of the line segment from the origin to the coordinate point.
+     * @param azimuth the angle in the x-y plane, measured in radians counter-clockwise
+     *      from the positive x-axis.
+     * @param polar the angle in radians between the positive z-axis and the ray from the origin
+     *      to the coordinate point.
+     * @return a new {@link SphericalCoordinates} instance representing the same point as the given set of
+     *      spherical coordinates.
+     */
+    public static SphericalCoordinates of(final double radius, final double azimuth, final double polar) {
+        return new SphericalCoordinates(radius, azimuth, polar);
+    }
+
+    /** Convert the given set of Cartesian coordinates to spherical coordinates.
+     * @param x X coordinate value
+     * @param y Y coordinate value
+     * @param z Z coordinate value
+     * @return a set of spherical coordinates equivalent to the given Cartesian coordinates
+     */
+    public static SphericalCoordinates ofCartesian(final double x, final double y, final double z) {
+        final double radius = Math.sqrt((x*x) + (y*y) + (z*z));
+        final double azimuth = Math.atan2(y, x);
+
+        // default the polar angle to 0 when the radius is 0
+        final double polar = (radius > 0.0) ? Math.acos(z / radius) : 0.0;
+
+        return new SphericalCoordinates(radius, azimuth, polar);
+    }
+
+    /** Parse the given string and return a new {@link SphericalCoordinates} instance. The parsed
+     * coordinate values are normalized as in the {@link #of(double, double, double)} method.
+     * The expected string format is the same as that returned by {@link #toString()}.
+     * @param input the string to parse
+     * @return new {@link SphericalCoordinates} instance
+     * @throws IllegalArgumentException if the string format is invalid.
+     */
+    public static SphericalCoordinates parse(String input) {
+        return SimpleCoordinateFormat.getPointFormat().parse(input, FACTORY);
+    }
+
+    /** Convert the given set of spherical coordinates to Cartesian coordinates.
+     * The Cartesian coordinates are computed and passed to the given
+     * factory instance. The factory's return value is returned.
+     * @param radius The spherical radius value.
+     * @param azimuth The spherical azimuth angle in radians.
+     * @param polar The spherical polar angle in radians.
+     * @param factory Factory instance that will be passed the
+     * @return the value returned by the factory when passed Cartesian
+     *      coordinates equivalent to the given set of spherical coordinates.
+     */
+    public static <T> T toCartesian(final double radius, final double azimuth, final double polar,
+            Coordinates.Factory3D<T> factory) {
+        final double xyLength = radius * Math.sin(polar);
+
+        final double x = xyLength * Math.cos(azimuth);
+        final double y = xyLength * Math.sin(azimuth);
+        final double z = radius * Math.cos(polar);
+
+        return factory.create(x, y, z);
+    }
+
+    /** Return a factory object for generating new {@link SphericalCoordinates} instances.
+     * @return factory object for generating new instances.
+     */
+    public static Coordinates.Factory3D<SphericalCoordinates> getFactory() {
+        return FACTORY;
+    }
+}
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/SphericalCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates_OLD.java
similarity index 96%
rename from commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/SphericalCoordinates.java
rename to commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates_OLD.java
index d9d959f..ac39a5e 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/SphericalCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates_OLD.java
@@ -14,13 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.geometry.spherical;
+package org.apache.commons.geometry.euclidean.threed;
 
 
 import java.io.Serializable;
 
-import org.apache.commons.geometry.euclidean.threed.Vector3D;
-
 /** This class provides conversions related to <a
  * href="http://mathworld.wolfram.com/SphericalCoordinates.html">spherical coordinates</a>.
  * <p>
@@ -48,7 +46,7 @@ import org.apache.commons.geometry.euclidean.threed.Vector3D;
  * between spherical and Cartesian coordinates.
  * </p>
  */
-public class SphericalCoordinates implements Serializable {
+public class SphericalCoordinates_OLD implements Serializable {
 
     /** Serializable UID. */
     private static final long serialVersionUID = 20130206L;
@@ -80,14 +78,14 @@ public class SphericalCoordinates implements Serializable {
     /** Build a spherical coordinates transformer from Cartesian coordinates.
      * @param v Cartesian coordinates
      */
-    public SphericalCoordinates(final Vector3D v) {
+    public SphericalCoordinates_OLD(final Vector3D v) {
 
         // Cartesian coordinates
         this.v = v;
 
         // remaining spherical coordinates
         this.r     = v.getNorm();
-        this.theta = v.getAlpha();
+        this.theta = 0.0; //v.getAlpha();
         this.phi   = Math.acos(v.getZ() / r);
 
     }
@@ -97,7 +95,7 @@ public class SphericalCoordinates implements Serializable {
      * @param theta azimuthal angle in x-y plane
      * @param phi polar (co-latitude) angle
      */
-    public SphericalCoordinates(final double r, final double theta, final double phi) {
+    public SphericalCoordinates_OLD(final double r, final double theta, final double phi) {
 
         final double cosTheta = Math.cos(theta);
         final double sinTheta = Math.sin(theta);
@@ -382,11 +380,11 @@ public class SphericalCoordinates implements Serializable {
             this.z = z;
         }
 
-        /** Replace the deserialized data transfer object with a {@link SphericalCoordinates}.
-         * @return replacement {@link SphericalCoordinates}
+        /** Replace the deserialized data transfer object with a {@link SphericalCoordinates_OLD}.
+         * @return replacement {@link SphericalCoordinates_OLD}
          */
         private Object readResolve() {
-            return new SphericalCoordinates(Vector3D.of(x, y, z));
+            return new SphericalCoordinates_OLD(Vector3D.of(x, y, z));
         }
 
     }
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 2083fba..7d8c5e8 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
@@ -130,20 +130,6 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point
         return Math.max(Math.max(Math.abs(getX()), Math.abs(getY())), Math.abs(getZ()));
     }
 
-    /** Get the azimuth of the vector.
-     * @return azimuth (&alpha;) of the vector, between -&pi; and +&pi;
-     */
-    public double getAlpha() {
-        return Math.atan2(getY(), getX());
-    }
-
-    /** Get the elevation of the vector.
-     * @return elevation (&delta;) of the vector, between -&pi;/2 and +&pi;/2
-     */
-    public double getDelta() {
-        return Math.asin(getZ() / getNorm());
-    }
-
     /** {@inheritDoc} */
     @Override
     public Vector3D add(Vector3D v) {
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
index de3731c..681f646 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
@@ -26,13 +26,14 @@ import org.apache.commons.geometry.core.util.Coordinates;
 import org.apache.commons.numbers.angle.PlaneAngleRadians;
 
 /** Class representing a set of polar coordinates in 2 dimensional
- * Euclidean space. Coordinates are normalized so that {@code radius >= 0}
- * and {@code -pi < azimuth <= pi}.
+ * Euclidean space. Coordinates are normalized so that {@code radius}
+ * is in the range {@code [0, +infinity)} and {@code azimuth} is in the
+ * range {@code (-pi, pi]}.
  */
 public class PolarCoordinates implements Spatial, Serializable {
 
     /** Serializable version UID */
-    private static final long serialVersionUID = -3122872387910228544L;
+    private static final long serialVersionUID = 20180630L;
 
     /** Shared parser/formatter instance **/
     private static final PolarCoordinatesParser PARSER = new PolarCoordinatesParser();
@@ -43,11 +44,27 @@ public class PolarCoordinates implements Spatial, Serializable {
     /** Azimuth angle in radians. */
     private final double azimuth;
 
-    /** Simple constructor. Input values must already be normalized.
+    /** Simple constructor. Input values are normalized.
      * @param radius Radius value.
      * @param azimuth Azimuth angle in radians.
      */
-    private PolarCoordinates(final double radius, final double azimuth) {
+    private PolarCoordinates(double radius, double azimuth) {
+        if (radius < 0) {
+            // negative radius; flip the angles
+            radius = Math.abs(radius);
+            azimuth += Geometry.PI;
+        }
+
+        if (Double.isFinite(azimuth) && (azimuth <= Geometry.MINUS_PI || azimuth > Geometry.PI)) {
+            azimuth = PlaneAngleRadians.normalizeBetweenMinusPiAndPi(azimuth);
+
+            // azimuth is now in the range [-pi, pi] but we want it to be in the range
+            // (-pi, pi] in order to have completely unique coordinates
+            if (azimuth <= -Geometry.PI) {
+                azimuth += Geometry.TWO_PI;
+            }
+        }
+
         this.radius = radius;
         this.azimuth = azimuth;
     }
@@ -177,21 +194,6 @@ public class PolarCoordinates implements Spatial, Serializable {
      * @return
      */
     public static PolarCoordinates of(double radius, double azimuth) {
-        if (radius < 0) {
-            radius = Math.abs(radius);
-            azimuth += Geometry.PI;
-        }
-
-        if (Double.isFinite(azimuth)) {
-            azimuth = PlaneAngleRadians.normalizeBetweenMinusPiAndPi(azimuth);
-
-            // the above normalizes the azimuth to -pi <= azimuth <= pi
-            // but we want -pi < azimuth <= pi to have completely unique coordinates
-            if (azimuth <= -Geometry.PI) {
-                azimuth += Geometry.TWO_PI;
-            }
-        }
-
         return new PolarCoordinates(radius, azimuth);
     }
 
@@ -212,7 +214,8 @@ public class PolarCoordinates implements Spatial, Serializable {
      * and azimuth is within the range {@code (-pi, pi]}. The expected string
      * format is the same as that returned by {@link #toString()}.
      * @param input the string to parse
-     * @return
+     * @return new {@link PolarCoordinates} instance
+     * @throws IllegalArgumentException if the string format is invalid.
      */
     public static PolarCoordinates parse(String input) {
         return PARSER.parse(input);
@@ -225,8 +228,8 @@ public class PolarCoordinates implements Spatial, Serializable {
      * @param azimuth Azimuth value in radians
      * @param factory Factory instance that will be passed the computed Cartesian coordinates
      * @param <T> Type returned by the factory
-     * @return the value returned by the given factory when passed Cartesian
-     *      coordinates equivalent to given set of polar coordinates.
+     * @return the value returned by the factory when passed Cartesian
+     *      coordinates equivalent to the given set of polar coordinates.
      */
     public static <T> T toCartesian(final double radius, final double azimuth, final Coordinates.Factory2D<T> factory) {
         final double x = radius * Math.cos(azimuth);
@@ -293,8 +296,7 @@ public class PolarCoordinates implements Spatial, Serializable {
             readSuffix(str, pos);
             endParse(str, pos);
 
-            // use the factory method so that the values will be normalized
-            return PolarCoordinates.of(radius, azimuth);
+            return new PolarCoordinates(radius, azimuth);
         }
     }
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
new file mode 100644
index 0000000..8b1f51a
--- /dev/null
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
@@ -0,0 +1,368 @@
+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.util.Coordinates;
+import org.apache.commons.geometry.euclidean.twod.PolarCoordinates;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class SphericalCoordinatesTest {
+
+    private static final double EPS = 1e-10;
+
+    private static final double QUARTER_PI = 0.25 * Geometry.PI;
+    private static final double MINUS_QUARTER_PI = -0.25 * Geometry.PI;
+    private static final double THREE_QUARTER_PI = 0.75 * Geometry.PI;
+    private static final double MINUS_THREE_QUARTER_PI = -0.75 * Geometry.PI;
+
+    @Test
+    public void testOf() {
+        // act/assert
+        checkSpherical(SphericalCoordinates.of(0, 0, 0), 0, 0, 0);
+        checkSpherical(SphericalCoordinates.of(0.1, 0.2, 0.3), 0.1, 0.2, 0.3);
+
+        checkSpherical(SphericalCoordinates.of(1, Geometry.HALF_PI, Geometry.PI),
+                1, Geometry.HALF_PI, Geometry.PI);
+        checkSpherical(SphericalCoordinates.of(1, Geometry.MINUS_HALF_PI, Geometry.HALF_PI),
+                1, Geometry.MINUS_HALF_PI, Geometry.HALF_PI);
+    }
+
+    @Test
+    public void testOf_normalizesAzimuthAngle() {
+        // act/assert
+        checkSpherical(SphericalCoordinates.of(2, Geometry.TWO_PI, 0), 2, 0, 0);
+        checkSpherical(SphericalCoordinates.of(2, Geometry.HALF_PI + Geometry.TWO_PI, 0), 2, Geometry.HALF_PI, 0);
+        checkSpherical(SphericalCoordinates.of(2, -Geometry.PI, 0), 2, Geometry.PI, 0);
+        checkSpherical(SphericalCoordinates.of(2, Geometry.PI * 1.5, 0), 2, Geometry.MINUS_HALF_PI, 0);
+    }
+
+    @Test
+    public void testOf_normalizesPolarAngle() {
+        // act/assert
+        checkSpherical(SphericalCoordinates.of(1, 0, 0), 1, 0, 0);
+
+        checkSpherical(SphericalCoordinates.of(1, 0, QUARTER_PI), 1, 0, QUARTER_PI);
+        checkSpherical(SphericalCoordinates.of(1, 0, MINUS_QUARTER_PI), 1, 0, QUARTER_PI);
+
+        checkSpherical(SphericalCoordinates.of(1, 0, Geometry.HALF_PI), 1, 0, Geometry.HALF_PI);
+        checkSpherical(SphericalCoordinates.of(1, 0, Geometry.MINUS_HALF_PI), 1, 0, Geometry.HALF_PI);
+
+        checkSpherical(SphericalCoordinates.of(1, 0, THREE_QUARTER_PI), 1, 0, THREE_QUARTER_PI);
+        checkSpherical(SphericalCoordinates.of(1, 0, MINUS_THREE_QUARTER_PI), 1, 0, THREE_QUARTER_PI);
+
+        checkSpherical(SphericalCoordinates.of(1, 0, Geometry.TWO_PI), 1, 0, 0);
+        checkSpherical(SphericalCoordinates.of(1, 0, Geometry.MINUS_TWO_PI), 1, 0, 0);
+    }
+
+    @Test
+    public void testOf_angleWrapAround() {
+        // act/assert
+        checkOfWithAngleWrapAround(1, 0, 0);
+        checkOfWithAngleWrapAround(1, QUARTER_PI, QUARTER_PI);
+        checkOfWithAngleWrapAround(1, Geometry.HALF_PI, Geometry.HALF_PI);
+        checkOfWithAngleWrapAround(1, THREE_QUARTER_PI, THREE_QUARTER_PI);
+        checkOfWithAngleWrapAround(1, Geometry.PI, Geometry.PI);
+    }
+
+    private void checkOfWithAngleWrapAround(double radius, double azimuth, double polar) {
+        for (int i=-4; i<=4; ++i) {
+            checkSpherical(
+                    SphericalCoordinates.of(radius, azimuth + (i * Geometry.TWO_PI), polar + (-i * Geometry.TWO_PI)),
+                    radius, azimuth, polar);
+        }
+    }
+
+    @Test
+    public void testOf_negativeRadius() {
+        // act/assert
+        checkSpherical(SphericalCoordinates.of(-2, 0, 0), 2, Geometry.PI, Geometry.PI);
+        checkSpherical(SphericalCoordinates.of(-2, Geometry.PI, Geometry.PI), 2, 0, 0);
+
+        checkSpherical(SphericalCoordinates.of(-3, Geometry.HALF_PI, QUARTER_PI), 3, Geometry.MINUS_HALF_PI, THREE_QUARTER_PI);
+        checkSpherical(SphericalCoordinates.of(-3, Geometry.MINUS_HALF_PI, THREE_QUARTER_PI), 3, Geometry.HALF_PI, QUARTER_PI);
+
+        checkSpherical(SphericalCoordinates.of(-4, QUARTER_PI, Geometry.HALF_PI), 4, MINUS_THREE_QUARTER_PI, Geometry.HALF_PI);
+        checkSpherical(SphericalCoordinates.of(-4, MINUS_THREE_QUARTER_PI, Geometry.HALF_PI), 4, QUARTER_PI, Geometry.HALF_PI);
+    }
+
+    @Test
+    public void testOf_NaNAndInfinite() {
+        // act/assert
+        checkSpherical(SphericalCoordinates.of(Double.NaN, Double.NaN, Double.NaN),
+                Double.NaN, Double.NaN, Double.NaN);
+        checkSpherical(SphericalCoordinates.of(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY),
+                Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+        checkSpherical(SphericalCoordinates.of(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY),
+                Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+    }
+
+    @Test
+    public void testOfCartesian() {
+        // arrange
+        double sqrt3 = Math.sqrt(3);
+
+        // act/assert
+        checkSpherical(SphericalCoordinates.ofCartesian(0, 0, 0), 0, 0, 0);
+
+        checkSpherical(SphericalCoordinates.ofCartesian(0.1, 0, 0), 0.1, 0, Geometry.HALF_PI);
+        checkSpherical(SphericalCoordinates.ofCartesian(-0.1, 0, 0), 0.1, Geometry.PI, Geometry.HALF_PI);
+
+        checkSpherical(SphericalCoordinates.ofCartesian(0, 0.1, 0), 0.1, Geometry.HALF_PI, Geometry.HALF_PI);
+        checkSpherical(SphericalCoordinates.ofCartesian(0, -0.1, 0), 0.1, Geometry.MINUS_HALF_PI, Geometry.HALF_PI);
+
+        checkSpherical(SphericalCoordinates.ofCartesian(0, 0, 0.1), 0.1, 0, 0);
+        checkSpherical(SphericalCoordinates.ofCartesian(0, 0, -0.1), 0.1, 0, Geometry.PI);
+
+        checkSpherical(SphericalCoordinates.ofCartesian(1, 1, 1), sqrt3, QUARTER_PI, Math.acos(1 / sqrt3));
+        checkSpherical(SphericalCoordinates.ofCartesian(-1, -1, -1), sqrt3, MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3));
+    }
+
+    @Test
+    public void testToPoint() {
+        // arrange
+        double sqrt3 = Math.sqrt(3);
+
+        // act/assert
+        checkPoint(SphericalCoordinates.of(0, 0, 0).toPoint(), 0, 0, 0);
+
+        checkPoint(SphericalCoordinates.of(1, 0, Geometry.HALF_PI).toPoint(), 1, 0, 0);
+        checkPoint(SphericalCoordinates.of(1, Geometry.PI, Geometry.HALF_PI).toPoint(), -1, 0, 0);
+
+        checkPoint(SphericalCoordinates.of(2, Geometry.HALF_PI, Geometry.HALF_PI).toPoint(), 0, 2, 0);
+        checkPoint(SphericalCoordinates.of(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI).toPoint(), 0, -2, 0);
+
+        checkPoint(SphericalCoordinates.of(3, 0, 0).toPoint(), 0, 0, 3);
+        checkPoint(SphericalCoordinates.of(3, 0, Geometry.PI).toPoint(), 0, 0, -3);
+
+        checkPoint(SphericalCoordinates.of(Math.sqrt(3), QUARTER_PI, Math.acos(1 / sqrt3)).toPoint(), 1, 1, 1);
+        checkPoint(SphericalCoordinates.of(Math.sqrt(3), MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3)).toPoint(), -1, -1, -1);
+    }
+
+    @Test
+    public void testToVector() {
+        // arrange
+        double sqrt3 = Math.sqrt(3);
+
+        // act/assert
+        checkVector(SphericalCoordinates.of(0, 0, 0).toVector(), 0, 0, 0);
+
+        checkVector(SphericalCoordinates.of(1, 0, Geometry.HALF_PI).toVector(), 1, 0, 0);
+        checkVector(SphericalCoordinates.of(1, Geometry.PI, Geometry.HALF_PI).toVector(), -1, 0, 0);
+
+        checkVector(SphericalCoordinates.of(2, Geometry.HALF_PI, Geometry.HALF_PI).toVector(), 0, 2, 0);
+        checkVector(SphericalCoordinates.of(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI).toVector(), 0, -2, 0);
+
+        checkVector(SphericalCoordinates.of(3, 0, 0).toVector(), 0, 0, 3);
+        checkVector(SphericalCoordinates.of(3, 0, Geometry.PI).toVector(), 0, 0, -3);
+
+        checkVector(SphericalCoordinates.of(Math.sqrt(3), QUARTER_PI, Math.acos(1 / sqrt3)).toVector(), 1, 1, 1);
+        checkVector(SphericalCoordinates.of(Math.sqrt(3), MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3)).toVector(), -1, -1, -1);
+    }
+
+    @Test
+    public void testToCartesian_callback() {
+        // arrange
+        double sqrt3 = Math.sqrt(3);
+        Coordinates.Factory3D<Point3D> factory = Point3D.getFactory();
+
+        // act/assert
+        checkPoint(SphericalCoordinates.of(0, 0, 0).toCartesian(factory), 0, 0, 0);
+
+        checkPoint(SphericalCoordinates.of(1, 0, Geometry.HALF_PI).toCartesian(factory), 1, 0, 0);
+        checkPoint(SphericalCoordinates.of(1, Geometry.PI, Geometry.HALF_PI).toCartesian(factory), -1, 0, 0);
+
+        checkPoint(SphericalCoordinates.of(2, Geometry.HALF_PI, Geometry.HALF_PI).toCartesian(factory), 0, 2, 0);
+        checkPoint(SphericalCoordinates.of(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI).toCartesian(factory), 0, -2, 0);
+
+        checkPoint(SphericalCoordinates.of(3, 0, 0).toCartesian(factory), 0, 0, 3);
+        checkPoint(SphericalCoordinates.of(3, 0, Geometry.PI).toCartesian(factory), 0, 0, -3);
+
+        checkPoint(SphericalCoordinates.of(Math.sqrt(3), QUARTER_PI, Math.acos(1 / sqrt3)).toCartesian(factory), 1, 1, 1);
+        checkPoint(SphericalCoordinates.of(Math.sqrt(3), MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3)).toCartesian(factory), -1, -1, -1);
+    }
+
+    @Test
+    public void testToCartesian_static() {
+        // arrange
+        double sqrt3 = Math.sqrt(3);
+        Coordinates.Factory3D<Point3D> factory = Point3D.getFactory();
+
+        // act/assert
+        checkPoint(SphericalCoordinates.toCartesian(0, 0, 0, factory), 0, 0, 0);
+
+        checkPoint(SphericalCoordinates.toCartesian(1, 0, Geometry.HALF_PI, factory), 1, 0, 0);
+        checkPoint(SphericalCoordinates.toCartesian(1, Geometry.PI, Geometry.HALF_PI, factory), -1, 0, 0);
+
+        checkPoint(SphericalCoordinates.toCartesian(2, Geometry.HALF_PI, Geometry.HALF_PI, factory), 0, 2, 0);
+        checkPoint(SphericalCoordinates.toCartesian(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI, factory), 0, -2, 0);
+
+        checkPoint(SphericalCoordinates.toCartesian(3, 0, 0, factory), 0, 0, 3);
+        checkPoint(SphericalCoordinates.toCartesian(3, 0, Geometry.PI, factory), 0, 0, -3);
+
+        checkPoint(SphericalCoordinates.toCartesian(Math.sqrt(3), QUARTER_PI, Math.acos(1 / sqrt3), factory), 1, 1, 1);
+        checkPoint(SphericalCoordinates.toCartesian(Math.sqrt(3), MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3), factory), -1, -1, -1);
+    }
+
+    @Test
+    public void testGetDimension() {
+        // arrange
+        SphericalCoordinates s = SphericalCoordinates.of(0, 0, 0);
+
+        // act/assert
+        Assert.assertEquals(3, s.getDimension());
+    }
+
+    @Test
+    public void testNaN() {
+        // act/assert
+        Assert.assertTrue(SphericalCoordinates.of(0, 0, Double.NaN).isNaN());
+        Assert.assertTrue(SphericalCoordinates.of(0, Double.NaN, 0).isNaN());
+        Assert.assertTrue(SphericalCoordinates.of(Double.NaN, 0, 0).isNaN());
+
+        Assert.assertFalse(SphericalCoordinates.of(1, 1, 1).isNaN());
+        Assert.assertFalse(SphericalCoordinates.of(1, 1, Double.NEGATIVE_INFINITY).isNaN());
+        Assert.assertFalse(SphericalCoordinates.of(1, Double.POSITIVE_INFINITY, 1).isNaN());
+        Assert.assertFalse(SphericalCoordinates.of(Double.NEGATIVE_INFINITY, 1, 1).isNaN());
+    }
+
+    @Test
+    public void testInfinite() {
+        // act/assert
+        Assert.assertTrue(SphericalCoordinates.of(0, 0, Double.NEGATIVE_INFINITY).isInfinite());
+        Assert.assertTrue(SphericalCoordinates.of(0, Double.NEGATIVE_INFINITY, 0).isInfinite());
+        Assert.assertTrue(SphericalCoordinates.of(Double.NEGATIVE_INFINITY, 0, 0).isInfinite());
+        Assert.assertTrue(SphericalCoordinates.of(0, 0, Double.POSITIVE_INFINITY).isInfinite());
+        Assert.assertTrue(SphericalCoordinates.of(0, Double.POSITIVE_INFINITY, 0).isInfinite());
+        Assert.assertTrue(SphericalCoordinates.of(Double.POSITIVE_INFINITY, 0, 0).isInfinite());
+
+        Assert.assertFalse(SphericalCoordinates.of(1, 1, 1).isInfinite());
+        Assert.assertFalse(SphericalCoordinates.of(0, 0, Double.NaN).isInfinite());
+        Assert.assertFalse(SphericalCoordinates.of(0, Double.NEGATIVE_INFINITY, Double.NaN).isInfinite());
+        Assert.assertFalse(SphericalCoordinates.of(Double.NaN, 0, Double.NEGATIVE_INFINITY).isInfinite());
+        Assert.assertFalse(SphericalCoordinates.of(Double.POSITIVE_INFINITY, Double.NaN, 0).isInfinite());
+        Assert.assertFalse(SphericalCoordinates.of(0, Double.NaN, Double.POSITIVE_INFINITY).isInfinite());
+    }
+
+    @Test
+    public void testHashCode() {
+        // arrange
+        SphericalCoordinates a = SphericalCoordinates.of(1, 2, 3);
+        SphericalCoordinates b = SphericalCoordinates.of(10, 2, 3);
+        SphericalCoordinates c = SphericalCoordinates.of(1, 20, 3);
+        SphericalCoordinates d = SphericalCoordinates.of(1, 2, 30);
+
+        SphericalCoordinates e = SphericalCoordinates.of(1, 2, 3);
+
+        // act/assert
+        Assert.assertEquals(a.hashCode(), a.hashCode());
+        Assert.assertEquals(a.hashCode(), e.hashCode());
+
+        Assert.assertNotEquals(a.hashCode(), b.hashCode());
+        Assert.assertNotEquals(a.hashCode(), c.hashCode());
+        Assert.assertNotEquals(a.hashCode(), d.hashCode());
+    }
+
+    @Test
+    public void testHashCode_NaNInstancesHaveSameHashCode() {
+        // arrange
+        SphericalCoordinates a = SphericalCoordinates.of(1, 2, Double.NaN);
+        SphericalCoordinates b = SphericalCoordinates.of(1, Double.NaN, 3);
+        SphericalCoordinates c = SphericalCoordinates.of(Double.NaN, 2, 3);
+
+        // act/assert
+        Assert.assertEquals(a.hashCode(), b.hashCode());
+        Assert.assertEquals(b.hashCode(), c.hashCode());
+    }
+
+    @Test
+    public void testEquals() {
+        // arrange
+        SphericalCoordinates a = SphericalCoordinates.of(1, 2, 3);
+        SphericalCoordinates b = SphericalCoordinates.of(10, 2, 3);
+        SphericalCoordinates c = SphericalCoordinates.of(1, 20, 3);
+        SphericalCoordinates d = SphericalCoordinates.of(1, 2, 30);
+
+        SphericalCoordinates e = SphericalCoordinates.of(1, 2, 3);
+
+        // act/assert
+        Assert.assertFalse(a.equals(null));
+        Assert.assertFalse(a.equals(new Object()));
+
+        Assert.assertTrue(a.equals(a));
+        Assert.assertTrue(a.equals(e));
+
+        Assert.assertFalse(a.equals(b));
+        Assert.assertFalse(a.equals(c));
+        Assert.assertFalse(a.equals(d));
+    }
+
+    @Test
+    public void testEquals_NaNInstancesEqual() {
+        // arrange
+        SphericalCoordinates a = SphericalCoordinates.of(1, 2, Double.NaN);
+        SphericalCoordinates b = SphericalCoordinates.of(1, Double.NaN, 3);
+        SphericalCoordinates c = SphericalCoordinates.of(Double.NaN, 2, 3);
+
+        // act/assert
+        Assert.assertTrue(a.equals(b));
+        Assert.assertTrue(b.equals(c));
+    }
+
+    @Test
+    public void testToString() {
+        // arrange
+        SphericalCoordinates sph = SphericalCoordinates.of(1, 2, 3);
+        Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}, 3.{0,2}\\)");
+
+        // act
+        String str = sph.toString();;
+
+        // assert
+        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                    pattern.matcher(str).matches());
+    }
+
+    @Test
+    public void testParse() {
+        // act/assert
+        checkSpherical(SphericalCoordinates.parse("(1, 2, -3)"), 1, 2, 3);
+        checkSpherical(SphericalCoordinates.parse("(  2e0 , 5 , -0.000 )"), 2, 5 - Geometry.TWO_PI, 0);
+        checkSpherical(SphericalCoordinates.parse("(NaN,Infinity,-Infinity)"), Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testParse_failure() {
+        // act/assert
+        SphericalCoordinates.parse("abc");
+    }
+
+    @Test
+    public void testGetFactory() {
+        // act
+        Coordinates.Factory3D<SphericalCoordinates> factory = SphericalCoordinates.getFactory();
+
+        // assert
+        checkSpherical(factory.create(2, 0.5 + Geometry.TWO_PI, 0.1 + Geometry.PI), 2, 0.5, Geometry.PI - 0.1);
+    }
+
+    private void checkSpherical(SphericalCoordinates c, double radius, double azimuth, double polar) {
+        Assert.assertEquals(radius, c.getRadius(), EPS);
+        Assert.assertEquals(azimuth, c.getAzimuth(), EPS);
+        Assert.assertEquals(polar, c.getPolar(), EPS);
+    }
+
+    private void checkPoint(Point3D p, double x, double y, double z) {
+        Assert.assertEquals(x, p.getX(), EPS);
+        Assert.assertEquals(y, p.getY(), EPS);
+        Assert.assertEquals(z, p.getZ(), EPS);
+    }
+
+    private void checkVector(Vector3D v, double x, double y, double z) {
+        Assert.assertEquals(x, v.getX(), EPS);
+        Assert.assertEquals(y, v.getY(), EPS);
+        Assert.assertEquals(z, v.getZ(), EPS);
+    }
+}
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/SphericalCoordinatesTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest_OLD.java
similarity index 69%
rename from commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/SphericalCoordinatesTest.java
rename to commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest_OLD.java
index db3b76e..999111c 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/SphericalCoordinatesTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest_OLD.java
@@ -15,56 +15,57 @@
  * limitations under the License.
  */
 
-package org.apache.commons.geometry.spherical;
+package org.apache.commons.geometry.euclidean.threed;
 
+import org.apache.commons.geometry.euclidean.threed.SphericalCoordinates_OLD;
 import org.apache.commons.geometry.euclidean.threed.Vector3D;
 import org.apache.commons.geometry.core.GeometryTestUtils;
 import org.junit.Assert;
 import org.junit.Test;
 
-public class SphericalCoordinatesTest {
+public class SphericalCoordinatesTest_OLD {
 
     @Test
     public void testCoordinatesStoC() {
         double piO2 = 0.5 * Math.PI;
-        SphericalCoordinates sc1 = new SphericalCoordinates(2.0, 0, piO2);
+        SphericalCoordinates_OLD sc1 = new SphericalCoordinates_OLD(2.0, 0, piO2);
         Assert.assertEquals(0, sc1.getCartesian().distance(Vector3D.of(2, 0, 0)), 1.0e-10);
-        SphericalCoordinates sc2 = new SphericalCoordinates(2.0, piO2, piO2);
+        SphericalCoordinates_OLD sc2 = new SphericalCoordinates_OLD(2.0, piO2, piO2);
         Assert.assertEquals(0, sc2.getCartesian().distance(Vector3D.of(0, 2, 0)), 1.0e-10);
-        SphericalCoordinates sc3 = new SphericalCoordinates(2.0, Math.PI, piO2);
+        SphericalCoordinates_OLD sc3 = new SphericalCoordinates_OLD(2.0, Math.PI, piO2);
         Assert.assertEquals(0, sc3.getCartesian().distance(Vector3D.of(-2, 0, 0)), 1.0e-10);
-        SphericalCoordinates sc4 = new SphericalCoordinates(2.0, -piO2, piO2);
+        SphericalCoordinates_OLD sc4 = new SphericalCoordinates_OLD(2.0, -piO2, piO2);
         Assert.assertEquals(0, sc4.getCartesian().distance(Vector3D.of(0, -2, 0)), 1.0e-10);
-        SphericalCoordinates sc5 = new SphericalCoordinates(2.0, 1.23456, 0);
+        SphericalCoordinates_OLD sc5 = new SphericalCoordinates_OLD(2.0, 1.23456, 0);
         Assert.assertEquals(0, sc5.getCartesian().distance(Vector3D.of(0, 0, 2)), 1.0e-10);
-        SphericalCoordinates sc6 = new SphericalCoordinates(2.0, 6.54321, Math.PI);
+        SphericalCoordinates_OLD sc6 = new SphericalCoordinates_OLD(2.0, 6.54321, Math.PI);
         Assert.assertEquals(0, sc6.getCartesian().distance(Vector3D.of(0, 0, -2)), 1.0e-10);
     }
 
     @Test
     public void testCoordinatesCtoS() {
         double piO2 = 0.5 * Math.PI;
-        SphericalCoordinates sc1 = new SphericalCoordinates(Vector3D.of(2, 0, 0));
+        SphericalCoordinates_OLD sc1 = new SphericalCoordinates_OLD(Vector3D.of(2, 0, 0));
         Assert.assertEquals(2,           sc1.getR(),     1.0e-10);
         Assert.assertEquals(0,           sc1.getTheta(), 1.0e-10);
         Assert.assertEquals(piO2,        sc1.getPhi(),   1.0e-10);
-        SphericalCoordinates sc2 = new SphericalCoordinates(Vector3D.of(0, 2, 0));
+        SphericalCoordinates_OLD sc2 = new SphericalCoordinates_OLD(Vector3D.of(0, 2, 0));
         Assert.assertEquals(2,           sc2.getR(),     1.0e-10);
         Assert.assertEquals(piO2,        sc2.getTheta(), 1.0e-10);
         Assert.assertEquals(piO2,        sc2.getPhi(),   1.0e-10);
-        SphericalCoordinates sc3 = new SphericalCoordinates(Vector3D.of(-2, 0, 0));
+        SphericalCoordinates_OLD sc3 = new SphericalCoordinates_OLD(Vector3D.of(-2, 0, 0));
         Assert.assertEquals(2,           sc3.getR(),     1.0e-10);
         Assert.assertEquals(Math.PI, sc3.getTheta(), 1.0e-10);
         Assert.assertEquals(piO2,        sc3.getPhi(),   1.0e-10);
-        SphericalCoordinates sc4 = new SphericalCoordinates(Vector3D.of(0, -2, 0));
+        SphericalCoordinates_OLD sc4 = new SphericalCoordinates_OLD(Vector3D.of(0, -2, 0));
         Assert.assertEquals(2,           sc4.getR(),     1.0e-10);
         Assert.assertEquals(-piO2,       sc4.getTheta(), 1.0e-10);
         Assert.assertEquals(piO2,        sc4.getPhi(),   1.0e-10);
-        SphericalCoordinates sc5 = new SphericalCoordinates(Vector3D.of(0, 0, 2));
+        SphericalCoordinates_OLD sc5 = new SphericalCoordinates_OLD(Vector3D.of(0, 0, 2));
         Assert.assertEquals(2,           sc5.getR(),     1.0e-10);
         //  don't check theta on poles, as it is singular
         Assert.assertEquals(0,           sc5.getPhi(),   1.0e-10);
-        SphericalCoordinates sc6 = new SphericalCoordinates(Vector3D.of(0, 0, -2));
+        SphericalCoordinates_OLD sc6 = new SphericalCoordinates_OLD(Vector3D.of(0, 0, -2));
         Assert.assertEquals(2,           sc6.getR(),     1.0e-10);
         //  don't check theta on poles, as it is singular
         Assert.assertEquals(Math.PI, sc6.getPhi(),   1.0e-10);
@@ -72,8 +73,8 @@ public class SphericalCoordinatesTest {
 
     @Test
     public void testSerialization() {
-        SphericalCoordinates a = new SphericalCoordinates(3, 2, 1);
-        SphericalCoordinates b = (SphericalCoordinates) GeometryTestUtils.serializeAndRecover(a);
+        SphericalCoordinates_OLD a = new SphericalCoordinates_OLD(3, 2, 1);
+        SphericalCoordinates_OLD b = (SphericalCoordinates_OLD) GeometryTestUtils.serializeAndRecover(a);
         Assert.assertEquals(0, a.getCartesian().distance(b.getCartesian()), 1.0e-10);
         Assert.assertEquals(a.getR(),     b.getR(),     1.0e-10);
         Assert.assertEquals(a.getTheta(), b.getTheta(), 1.0e-10);
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 c9b3cf9..86bf0a3 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
@@ -770,20 +770,6 @@ public class Vector3DTest {
                     2, 5, -3);
     }
 
-    @Test
-    public void testAngular() {
-        Assert.assertEquals(0,           Vector3D.PLUS_X.getAlpha(), 1.0e-10);
-        Assert.assertEquals(0,           Vector3D.PLUS_X.getDelta(), 1.0e-10);
-        Assert.assertEquals(Math.PI / 2, Vector3D.PLUS_Y.getAlpha(), 1.0e-10);
-        Assert.assertEquals(0,           Vector3D.PLUS_Y.getDelta(), 1.0e-10);
-        Assert.assertEquals(0,           Vector3D.PLUS_Z.getAlpha(), 1.0e-10);
-        Assert.assertEquals(Math.PI / 2, Vector3D.PLUS_Z.getDelta(), 1.0e-10);
-
-        Vector3D u = Vector3D.of(-1, 1, -1);
-        Assert.assertEquals(3 * Math.PI /4, u.getAlpha(), 1.0e-10);
-        Assert.assertEquals(-1.0 / Math.sqrt(3), Math.sin(u.getDelta()), 1.0e-10);
-    }
-
     private void checkVector(Vector3D v, double x, double y, double z) {
         Assert.assertEquals(x, v.getX(), EPS);
         Assert.assertEquals(y, v.getY(), EPS);
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/package-info.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/package-info.java
deleted file mode 100644
index 020a968..0000000
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/package-info.java
+++ /dev/null
@@ -1,23 +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.
- */
-/**
- *
- * <p>
- * Base package for spherical geometry components.
- * </p>
- */
-package org.apache.commons.geometry.spherical;


[commons-geometry] 10/15: GEOMETRY-7: creating static methods for azimuth and polar angle normalization; using polar and spherical naming conventions and conversion methods in S1Point and S2Point

Posted by er...@apache.org.
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 259360397420c3bea7767d7947383fd0d31789f4
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Tue Jul 10 23:32:19 2018 -0400

    GEOMETRY-7: creating static methods for azimuth and polar angle normalization; using polar and spherical naming conventions and conversion methods in S1Point and S2Point
---
 .../euclidean/threed/SphericalCoordinates.java     |  67 ++++++-----
 .../geometry/euclidean/twod/PolarCoordinates.java  |  50 +++++----
 .../euclidean/threed/SphericalCoordinatesTest.java |  49 ++++++++-
 .../euclidean/twod/PolarCoordinatesTest.java       |  24 ++++
 .../commons/geometry/spherical/oned/ArcsSet.java   |   4 +-
 .../geometry/spherical/oned/LimitAngle.java        |   2 +-
 .../commons/geometry/spherical/oned/S1Point.java   |  54 +++++----
 .../commons/geometry/spherical/twod/Circle.java    |   2 +-
 .../commons/geometry/spherical/twod/S2Point.java   | 122 +++++++++------------
 .../geometry/spherical/SphericalTestUtils.java     |   2 +-
 .../geometry/spherical/oned/LimitAngleTest.java    |   2 +-
 .../geometry/spherical/oned/S1PointTest.java       |   4 +-
 .../geometry/spherical/twod/CircleTest.java        |  10 +-
 .../geometry/spherical/twod/S2PointTest.java       |  10 +-
 .../spherical/twod/SphericalPolygonsSetTest.java   |   4 +-
 15 files changed, 236 insertions(+), 170 deletions(-)

diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
index d3b3a25..37d7ea8 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
@@ -22,11 +22,12 @@ import org.apache.commons.geometry.core.Geometry;
 import org.apache.commons.geometry.core.Spatial;
 import org.apache.commons.geometry.core.util.Coordinates;
 import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.geometry.euclidean.twod.PolarCoordinates;
 import org.apache.commons.numbers.angle.PlaneAngleRadians;
 
-/** Class representing <a href="https://en.wikipedia.org/wiki/Spherical_coordinate_system">spherical coordinates</a> 
+/** Class representing <a href="https://en.wikipedia.org/wiki/Spherical_coordinate_system">spherical coordinates</a>
  * in 3 dimensional Euclidean space.
- * 
+ *
  * <p>Spherical coordinates for a point are defined by three values:
  * <ol>
  * 	<li><em>Radius</em> - The distance from the point to a fixed referenced point.</li>
@@ -36,14 +37,14 @@ import org.apache.commons.numbers.angle.PlaneAngleRadians;
  *direction must be orthogonal to the reference plane.</li>
  * </ol>
  * This class follows the convention of using the origin as the reference point; the positive x-axis as the
- * reference direction for the azimuth angle, measured in the x-y plane with positive angles moving counter-clockwise 
+ * reference direction for the azimuth angle, measured in the x-y plane with positive angles moving counter-clockwise
  * toward the positive y-axis; and the positive z-axis as the zenith direction. Spherical coordinates are
  * related to Cartesian coordinates as follows:
  * <pre>
  * x = r cos(&theta;) sin(&Phi;)
  * y = r sin(&theta;) sin(&Phi;)
  * z = r cos(&Phi;)
- * 
+ *
  * r = &radic;(x<sup>2</sup>+y<sup>2</sup>+z<sup>2</sup>)
  * &theta; = atan2(y, x)
  * &Phi; = acos(z/r)
@@ -51,19 +52,19 @@ import org.apache.commons.numbers.angle.PlaneAngleRadians;
  * where <em>r</em> is the radius, <em>&theta;</em> is the azimuth angle, and <em>&Phi;</em> is the polar angle
  * of the spherical coordinates.
  * </p>
- * 
+ *
  * <p>There are numerous, competing conventions for the symbols used to represent spherical coordinate values. For
  * example, the mathematical convention is to use <em>(r, &theta;, &Phi;)</em> to represent radius, azimuth angle, and
  * polar angle, whereas the physics convention flips the angle values and uses <em>(r, &Phi;, &theta;)</em>. As such,
  * this class avoids the use of these symbols altogether in favor of the less ambiguous formal names of the values,
  * e.g. {@code radius}, {@code azimuth}, and {@code polar}.
  * </p>
- * 
- * <p>In order to ensure the uniqueness of coordinate sets, coordinate values 
- * are normalized so that {@code radius} is in the range {@code [0, +Infinity)}, 
+ *
+ * <p>In order to ensure the uniqueness of coordinate sets, coordinate values
+ * are normalized so that {@code radius} is in the range {@code [0, +Infinity)},
  * {@code azimuth} is in the range {@code (-pi, pi]}, and {@code polar} is in the
  * range {@code [0, pi]}.</p>
- * 
+ *
  * @see <a href="https://en.wikipedia.org/wiki/Spherical_coordinate_system">Spherical Coordinate System</a>
  */
 public final class SphericalCoordinates implements Spatial, Serializable {
@@ -103,25 +104,9 @@ public final class SphericalCoordinates implements Spatial, Serializable {
             polar += Geometry.PI;
         }
 
-        if (Double.isFinite(azimuth) && (azimuth <= Geometry.MINUS_PI || azimuth > Geometry.PI)) {
-            azimuth = PlaneAngleRadians.normalizeBetweenMinusPiAndPi(azimuth);
-
-            // azimuth is now in the range [-pi, pi] but we want it to be in the range
-            // (-pi, pi] in order to have completely unique coordinates
-            if (azimuth <= -Geometry.PI) {
-                azimuth += Geometry.TWO_PI;
-            }
-        }
-
-        // normalize the polar angle; this is the angle between the polar vector and the point ray
-        // so it is unsigned (unlike the azimuth) and should be in the range [0, pi]
-        if (Double.isFinite(polar)) {
-            polar = Math.abs(PlaneAngleRadians.normalizeBetweenMinusPiAndPi(polar));
-        }
-
         this.radius = radius;
-        this.azimuth = azimuth;
-        this.polar = polar;
+        this.azimuth = normalizeAzimuth(azimuth);
+        this.polar = normalizePolar(polar);
     }
 
     /** Return the radius value. The value is in the range {@code [0, +Infinity)}.
@@ -288,6 +273,34 @@ public final class SphericalCoordinates implements Spatial, Serializable {
         return SimpleCoordinateFormat.getPointFormat().parse(input, FACTORY);
     }
 
+    /** Normalize an azimuth value to be within the range {@code (-pi, +pi]}. This
+     * is exactly equivalent to {@link PolarCoordinates#normalizeAzimuth(double)}.
+     * @param azimuth azimuth value in radians
+     * @return equivalent azimuth value in the range {@code (-pi, +pi]}.
+     * @see PolarCoordinates#normalizeAzimuth(double)
+     */
+    public static double normalizeAzimuth(double azimuth) {
+        return PolarCoordinates.normalizeAzimuth(azimuth);
+    }
+
+    /** Normalize a polar value to be within the range {@code [0, +pi]}. Since the
+     * polar angle is the angle between two vectors (the zenith direction and the
+     * point vector), the sign of the angle is not significant as in the azimuth angle.
+     * For example, a polar angle of {@code -pi/2} and one of {@code +pi/2} will both
+     * normalize to {@code pi/2}.
+     * @param polar polar value in radians
+     * @return equalivalent polar value in the range {@code [0, +pi]}
+     */
+    public static double normalizePolar(double polar) {
+        // normalize the polar angle; this is the angle between the polar vector and the point ray
+        // so it is unsigned (unlike the azimuth) and should be in the range [0, pi]
+        if (Double.isFinite(polar)) {
+            polar = Math.abs(PlaneAngleRadians.normalizeBetweenMinusPiAndPi(polar));
+        }
+
+        return polar;
+    }
+
     /** Convert the given set of spherical coordinates to Cartesian coordinates.
      * The Cartesian coordinates are computed and passed to the given
      * factory instance. The factory's return value is returned.
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
index 9bc9c96..3ea6779 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
@@ -24,30 +24,30 @@ import org.apache.commons.geometry.core.util.Coordinates;
 import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
 import org.apache.commons.numbers.angle.PlaneAngleRadians;
 
-/** Class representing <a href="https://en.wikipedia.org/wiki/Polar_coordinate_system">polar coordinates</a> 
- * in 2 dimensional Euclidean space. 
- * 
+/** Class representing <a href="https://en.wikipedia.org/wiki/Polar_coordinate_system">polar coordinates</a>
+ * in 2 dimensional Euclidean space.
+ *
  * <p>Polar coordinates are defined by a distance from a reference point
- * and an angle from a reference direction. The distance value is called 
+ * and an angle from a reference direction. The distance value is called
  * the radial coordinate, or <em>radius</em>, and the angle is called the angular coordinate,
  * or <em>azimuth</em>. This class follows the standard
  * mathematical convention of using the positive x-axis as the reference
- * direction and measuring positive angles counter-clockwise, toward the 
+ * direction and measuring positive angles counter-clockwise, toward the
  * positive y-axis. The origin is used as the reference point. Polar coordinate
  * are related to Cartesian coordinates as follows:
- * <pre> 
+ * <pre>
  * x = r * cos(&theta;)
  * y = r * sin(&theta;)
- * 
+ *
  * r = &radic;(x<sup>2</sup> + y<sup>2</sup>)
  * &theta; = atan2(y, x)
  * </pre>
  * where <em>r</em> is the radius and <em>&theta;</em> is the azimuth of the polar coordinates.
  * </p>
- * <p>In order to ensure the uniqueness of coordinate sets, coordinate values 
- * are normalized so that {@code radius} is in the range {@code [0, +Infinity)} 
+ * <p>In order to ensure the uniqueness of coordinate sets, coordinate values
+ * are normalized so that {@code radius} is in the range {@code [0, +Infinity)}
  * and {@code azimuth} is in the range {@code (-pi, pi]}.</p>
- * 
+ *
  * @see <a href="https://en.wikipedia.org/wiki/Polar_coordinate_system">Polar Coordinate System</a>
  */
 public final class PolarCoordinates implements Spatial, Serializable {
@@ -82,18 +82,8 @@ public final class PolarCoordinates implements Spatial, Serializable {
             azimuth += Geometry.PI;
         }
 
-        if (Double.isFinite(azimuth) && (azimuth <= Geometry.MINUS_PI || azimuth > Geometry.PI)) {
-            azimuth = PlaneAngleRadians.normalizeBetweenMinusPiAndPi(azimuth);
-
-            // azimuth is now in the range [-pi, pi] but we want it to be in the range
-            // (-pi, pi] in order to have completely unique coordinates
-            if (azimuth <= -Geometry.PI) {
-                azimuth += Geometry.TWO_PI;
-            }
-        }
-
         this.radius = radius;
-        this.azimuth = azimuth;
+        this.azimuth = normalizeAzimuth(azimuth);;
     }
 
     /** Return the radius value. The value will be greater than or equal to 0.
@@ -246,6 +236,24 @@ public final class PolarCoordinates implements Spatial, Serializable {
         return SimpleCoordinateFormat.getPointFormat().parse(input, FACTORY);
     }
 
+    /** Normalize an azimuth value to be within the range {@code (-pi, +pi]}.
+     * @param azimuth azimuth value in radians
+     * @return equivalent azimuth value in the range {@code (-pi, +pi]}.
+     */
+    public static double normalizeAzimuth(double azimuth) {
+        if (Double.isFinite(azimuth) && (azimuth <= Geometry.MINUS_PI || azimuth > Geometry.PI)) {
+            azimuth = PlaneAngleRadians.normalizeBetweenMinusPiAndPi(azimuth);
+
+            // azimuth is now in the range [-pi, pi] but we want it to be in the range
+            // (-pi, pi] in order to have completely unique coordinates
+            if (azimuth <= -Geometry.PI) {
+                azimuth += Geometry.TWO_PI;
+            }
+        }
+
+        return azimuth;
+    }
+
     /** Convert the given set of polar coordinates to Cartesian coordinates.
      * The Cartesian coordinates are computed and passed to the given
      * factory instance. The factory's return value is returned.
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
index 1acfde7..241093a 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
@@ -23,7 +23,6 @@ import org.apache.commons.geometry.core.util.Coordinates;
 import org.junit.Assert;
 import org.junit.Test;
 
-
 public class SphericalCoordinatesTest {
 
     private static final double EPS = 1e-10;
@@ -355,6 +354,54 @@ public class SphericalCoordinatesTest {
     }
 
     @Test
+    public void testNormalizeAzimuth() {
+        // act/assert
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(0), 0.0, EPS);
+
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.HALF_PI), Geometry.HALF_PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.PI), Geometry.PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.PI + Geometry.HALF_PI), Geometry.MINUS_HALF_PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.TWO_PI), 0.0, EPS);
+
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.MINUS_HALF_PI), Geometry.MINUS_HALF_PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(-Geometry.PI), Geometry.PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(-Geometry.PI - Geometry.HALF_PI), Geometry.HALF_PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(-Geometry.TWO_PI), 0.0, EPS);
+    }
+
+    @Test
+    public void testNormalizeAzimuth_NaNAndInfinite() {
+        // act/assert
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Double.NaN), Double.NaN, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Double.NEGATIVE_INFINITY), Double.NEGATIVE_INFINITY, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Double.POSITIVE_INFINITY), Double.POSITIVE_INFINITY, EPS);
+    }
+
+    @Test
+    public void testNormalizePolar() {
+        // act/assert
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(0), 0.0, EPS);
+
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(Geometry.HALF_PI), Geometry.HALF_PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(Geometry.PI), Geometry.PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(Geometry.PI + Geometry.HALF_PI), Geometry.HALF_PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(Geometry.TWO_PI), 0.0, EPS);
+
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(Geometry.MINUS_HALF_PI), Geometry.HALF_PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(-Geometry.PI), Geometry.PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(-Geometry.PI - Geometry.HALF_PI), Geometry.HALF_PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(-Geometry.TWO_PI), 0.0, EPS);
+    }
+
+    @Test
+    public void testNormalizePolar_NaNAndInfinite() {
+        // act/assert
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(Double.NaN), Double.NaN, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(Double.NEGATIVE_INFINITY), Double.NEGATIVE_INFINITY, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizePolar(Double.POSITIVE_INFINITY), Double.POSITIVE_INFINITY, EPS);
+    }
+
+    @Test
     public void testGetFactory() {
         // act
         Coordinates.Factory3D<SphericalCoordinates> factory = SphericalCoordinates.getFactory();
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
index 2b28c19..d8636f0 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
@@ -344,6 +344,30 @@ public class PolarCoordinatesTest {
     }
 
     @Test
+    public void testNormalizeAzimuth() {
+        // act/assert
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(0), 0.0, EPS);
+
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.HALF_PI), Geometry.HALF_PI, EPS);
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.PI), Geometry.PI, EPS);
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.PI + Geometry.HALF_PI), Geometry.MINUS_HALF_PI, EPS);
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.TWO_PI), 0.0, EPS);
+
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.MINUS_HALF_PI), Geometry.MINUS_HALF_PI, EPS);
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(-Geometry.PI), Geometry.PI, EPS);
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(-Geometry.PI - Geometry.HALF_PI), Geometry.HALF_PI, EPS);
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(-Geometry.TWO_PI), 0.0, EPS);
+    }
+
+    @Test
+    public void testNormalizeAzimuth_NaNAndInfinite() {
+        // act/assert
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Double.NaN), Double.NaN, EPS);
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Double.NEGATIVE_INFINITY), Double.NEGATIVE_INFINITY, EPS);
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Double.POSITIVE_INFINITY), Double.POSITIVE_INFINITY, EPS);
+    }
+
+    @Test
     public void testGetFactory() {
         // act
         Coordinates.Factory2D<PolarCoordinates> factory = PolarCoordinates.getFactory();
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 813dae9..311950f 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
@@ -435,7 +435,7 @@ public class ArcsSet extends AbstractRegion<S1Point, S1Point> implements Iterabl
      * @return limit angle
      */
     private double getAngle(final BSPTree<S1Point> node) {
-        return ((LimitAngle) node.getCut().getHyperplane()).getLocation().getAlpha();
+        return ((LimitAngle) node.getCut().getHyperplane()).getLocation().getAzimuth();
     }
 
     /** {@inheritDoc} */
@@ -475,7 +475,7 @@ public class ArcsSet extends AbstractRegion<S1Point, S1Point> implements Iterabl
     public BoundaryProjection<S1Point> projectToBoundary(final S1Point point) {
 
         // get position of test point
-        final double alpha = point.getAlpha();
+        final double alpha = point.getAzimuth();
 
         boolean wrapFirst = false;
         double first      = Double.NaN;
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/LimitAngle.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/LimitAngle.java
index bc80a21..605a649 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/LimitAngle.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/LimitAngle.java
@@ -58,7 +58,7 @@ public class LimitAngle implements Hyperplane<S1Point> {
     /** {@inheritDoc} */
     @Override
     public double getOffset(final S1Point point) {
-        final double delta = point.getAlpha() - location.getAlpha();
+        final double delta = point.getAzimuth() - location.getAzimuth();
         return direct ? delta : -delta;
     }
 
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
index fdabb13..4d3f87e 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
@@ -21,6 +21,7 @@ import java.io.Serializable;
 import org.apache.commons.geometry.core.Point;
 import org.apache.commons.geometry.core.util.Coordinates;
 import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.geometry.euclidean.twod.PolarCoordinates;
 import org.apache.commons.geometry.euclidean.twod.Vector2D;
 import org.apache.commons.numbers.angle.PlaneAngleRadians;
 
@@ -29,13 +30,13 @@ import org.apache.commons.numbers.angle.PlaneAngleRadians;
  */
 public final class S1Point implements Point<S1Point>, Serializable {
 
-   // CHECKSTYLE: stop ConstantName
-    /** A vector with all coordinates set to NaN. */
-    public static final S1Point NaN = new S1Point(Double.NaN, Vector2D.NaN);
+    // CHECKSTYLE: stop ConstantName
+    /** A point with all coordinates set to NaN. */
+    public static final S1Point NaN = new S1Point(Double.NaN);
     // CHECKSTYLE: resume ConstantName
 
     /** Serializable UID. */
-    private static final long serialVersionUID = 20131218L;
+    private static final long serialVersionUID = 20180710L;
 
     /** Factory for delegating instance creation. */
     private static Coordinates.Factory1D<S1Point> FACTORY = new Coordinates.Factory1D<S1Point>() {
@@ -43,31 +44,31 @@ public final class S1Point implements Point<S1Point>, Serializable {
         /** {@inheritDoc} */
         @Override
         public S1Point create(double a) {
-            return S1Point.of(a);
+            return new S1Point(a);
         }
     };
 
-    /** Azimuthal angle in radians \( \alpha \). */
-    private final double alpha;
+    /** Azimuthal angle in radians. */
+    private final double azimuth;
 
     /** Corresponding 2D normalized vector. */
     private final Vector2D vector;
 
     /** Build a point from its internal components.
-     * @param alpha azimuthal angle \( \alpha \)
+     * @param azimuth azimuthal angle
      * @param vector corresponding vector
      */
-    private S1Point(final double alpha, final Vector2D vector) {
-        this.alpha  = alpha;
-        this.vector = vector;
+    private S1Point(final double azimuth) {
+        this.azimuth  = PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(azimuth);
+        this.vector = Double.isFinite(azimuth) ? Vector2D.ofPolar(1.0, azimuth) : Vector2D.NaN;
     }
 
-    /** Get the azimuthal angle in radians \( \alpha \).
-     * @return azimuthal angle \( \alpha \)
+    /** Get the azimuthal angle in radians.
+     * @return azimuthal angle
      * @see S1Point#of(double)
      */
-    public double getAlpha() {
-        return alpha;
+    public double getAzimuth() {
+        return azimuth;
     }
 
     /** Get the corresponding normalized vector in the 2D Euclidean space.
@@ -86,13 +87,13 @@ public final class S1Point implements Point<S1Point>, Serializable {
     /** {@inheritDoc} */
     @Override
     public boolean isNaN() {
-        return Double.isNaN(alpha);
+        return Double.isNaN(azimuth);
     }
 
     /** {@inheritDoc} */
     @Override
     public boolean isInfinite() {
-        return !isNaN() && Double.isInfinite(alpha);
+        return !isNaN() && Double.isInfinite(azimuth);
     }
 
     /** {@inheritDoc} */
@@ -131,7 +132,6 @@ public final class S1Point implements Point<S1Point>, Serializable {
      */
     @Override
     public boolean equals(Object other) {
-
         if (this == other) {
             return true;
         }
@@ -142,11 +142,10 @@ public final class S1Point implements Point<S1Point>, Serializable {
                 return this.isNaN();
             }
 
-            return alpha == rhs.alpha;
+            return azimuth == rhs.azimuth;
         }
 
         return false;
-
     }
 
     /**
@@ -161,25 +160,22 @@ public final class S1Point implements Point<S1Point>, Serializable {
         if (isNaN()) {
             return 542;
         }
-        return 1759 * Double.hashCode(alpha);
+        return 1759 * Double.hashCode(azimuth);
     }
 
     /** {@inheritDoc} */
     @Override
     public String toString() {
-        return SimpleCoordinateFormat.getPointFormat().format(getAlpha());
+        return SimpleCoordinateFormat.getPointFormat().format(getAzimuth());
     }
 
     /** Creates a new point instance from the given azimuthal coordinate value.
-     * @param alpha azimuthal angle in radians \( \alpha \)
+     * @param azimuth azimuthal angle in radians
      * @return point instance with the given azimuth coordinate value
-     * @see #getAlpha()
+     * @see #getAzimuth()
      */
-    public static S1Point of(double alpha) {
-        double normalizedAlpha = PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(alpha);
-        Vector2D vector = Vector2D.of(Math.cos(normalizedAlpha), Math.sin(normalizedAlpha));
-
-        return new S1Point(normalizedAlpha, vector);
+    public static S1Point of(double azimuth) {
+        return new S1Point(azimuth);
     }
 
     /** Parses the given string and returns a new point instance. The expected string
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/Circle.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/Circle.java
index 31cc5c9..e0aaba0 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/Circle.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/Circle.java
@@ -168,7 +168,7 @@ public class Circle implements Hyperplane<S2Point>, Embedding<S2Point, S1Point>
      */
     @Override
     public S2Point toSpace(final S1Point point) {
-        return S2Point.of(getPointAt(point.getAlpha()));
+        return S2Point.of(getPointAt(point.getAzimuth()));
     }
 
     /** Get a circle point from its phase around the circle.
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
index 93cec93..1b67205 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
@@ -21,45 +21,39 @@ import java.io.Serializable;
 import org.apache.commons.geometry.core.Point;
 import org.apache.commons.geometry.core.util.Coordinates;
 import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+import org.apache.commons.geometry.euclidean.threed.SphericalCoordinates;
 import org.apache.commons.geometry.euclidean.threed.Vector3D;
 
 /** This class represents a point on the 2-sphere.
- * <p>
- * We use the mathematical convention to use the azimuthal angle \( \theta \)
- * in the x-y plane as the first coordinate, and the polar angle \( \varphi \)
- * as the second coordinate (see <a
- * href="http://mathworld.wolfram.com/SphericalCoordinates.html">Spherical
- * Coordinates</a> in MathWorld).
- * </p>
  * <p>Instances of this class are guaranteed to be immutable.</p>
  */
 public final class S2Point implements Point<S2Point>, Serializable {
 
-    /** +I (coordinates: \( \theta = 0, \varphi = \pi/2 \)). */
+    /** +I (coordinates: ( azimuth = 0, polar = pi/2 )). */
     public static final S2Point PLUS_I = new S2Point(0, 0.5 * Math.PI, Vector3D.PLUS_X);
 
-    /** +J (coordinates: \( \theta = \pi/2, \varphi = \pi/2 \))). */
+    /** +J (coordinates: ( azimuth = pi/2, polar = pi/2 ))). */
     public static final S2Point PLUS_J = new S2Point(0.5 * Math.PI, 0.5 * Math.PI, Vector3D.PLUS_Y);
 
-    /** +K (coordinates: \( \theta = any angle, \varphi = 0 \)). */
+    /** +K (coordinates: ( azimuth = any angle, polar = 0 )). */
     public static final S2Point PLUS_K = new S2Point(0, 0, Vector3D.PLUS_Z);
 
-    /** -I (coordinates: \( \theta = \pi, \varphi = \pi/2 \)). */
+    /** -I (coordinates: ( azimuth = pi, polar = pi/2 )). */
     public static final S2Point MINUS_I = new S2Point(Math.PI, 0.5 * Math.PI, Vector3D.MINUS_X);
 
-    /** -J (coordinates: \( \theta = 3\pi/2, \varphi = \pi/2 \)). */
+    /** -J (coordinates: ( azimuth = 3pi/2, polar = pi/2 )). */
     public static final S2Point MINUS_J = new S2Point(1.5 * Math.PI, 0.5 * Math.PI, Vector3D.MINUS_Y);
 
-    /** -K (coordinates: \( \theta = any angle, \varphi = \pi \)). */
+    /** -K (coordinates: ( azimuth = any angle, polar = pi )). */
     public static final S2Point MINUS_K = new S2Point(0, Math.PI, Vector3D.MINUS_Z);
 
     // CHECKSTYLE: stop ConstantName
-    /** A vector with all coordinates set to NaN. */
+    /** A point with all coordinates set to NaN. */
     public static final S2Point NaN = new S2Point(Double.NaN, Double.NaN, Vector3D.NaN);
     // CHECKSTYLE: resume ConstantName
 
     /** Serializable UID. */
-    private static final long serialVersionUID = 20131218L;
+    private static final long serialVersionUID = 20180710L;
 
     /** Factory for delegating instance creation. */
     private static Coordinates.Factory2D<S2Point> FACTORY = new Coordinates.Factory2D<S2Point>() {
@@ -67,44 +61,46 @@ public final class S2Point implements Point<S2Point>, Serializable {
         /** {@inheritDoc} */
         @Override
         public S2Point create(double a1, double a2) {
+            // use the factory method for full input validation
             return S2Point.of(a1, a2);
         }
     };
 
-    /** Azimuthal angle \( \theta \) in the x-y plane. */
-    private final double theta;
+    /** Azimuthal angle in the x-y plane. */
+    private final double azimuth;
 
-    /** Polar angle \( \varphi \). */
-    private final double phi;
+    /** Polar angle. */
+    private final double polar;
 
     /** Corresponding 3D normalized vector. */
     private final Vector3D vector;
 
     /** Build a point from its internal components.
-     * @param theta azimuthal angle \( \theta \) in the x-y plane
-     * @param phi polar angle \( \varphi \)
-     * @param vector corresponding vector
+     * @param azimuth azimuthal angle in the x-y plane
+     * @param polar polar angle
+     * @param vector corresponding vector; if null, the vector is computed
+     * @throws IllegalArgumentException
      */
-    private S2Point(final double theta, final double phi, final Vector3D vector) {
-        this.theta  = theta;
-        this.phi    = phi;
-        this.vector = vector;
+    private S2Point(final double azimuth, final double polar, final Vector3D vector) {
+        this.azimuth = azimuth;
+        this.polar = polar;
+        this.vector = (vector != null) ? vector : Vector3D.ofSpherical(1.0, azimuth, polar);
     }
 
-    /** Get the azimuthal angle \( \theta \) in the x-y plane.
-     * @return azimuthal angle \( \theta \) in the x-y plane
+    /** Get the azimuthal angle in the x-y plane in radians.
+     * @return azimuthal angle in the x-y plane
      * @see S2Point#of(double, double)
      */
-    public double getTheta() {
-        return theta;
+    public double getAzimuth() {
+        return azimuth;
     }
 
-    /** Get the polar angle \( \varphi \).
-     * @return polar angle \( \varphi \)
+    /** Get the polar angle in radians.
+     * @return polar angle
      * @see S2Point#of(double, double)
      */
-    public double getPhi() {
-        return phi;
+    public double getPolar() {
+        return polar;
     }
 
     /** Get the corresponding normalized vector in the 3D Euclidean space.
@@ -123,20 +119,20 @@ public final class S2Point implements Point<S2Point>, Serializable {
     /** {@inheritDoc} */
     @Override
     public boolean isNaN() {
-        return Double.isNaN(theta) || Double.isNaN(phi);
+        return Double.isNaN(azimuth) || Double.isNaN(polar);
     }
 
     /** {@inheritDoc} */
     @Override
     public boolean isInfinite() {
-        return !isNaN() && (Double.isInfinite(theta) || Double.isInfinite(phi));
+        return !isNaN() && (Double.isInfinite(azimuth) || Double.isInfinite(polar));
     }
 
     /** Get the opposite of the instance.
      * @return a new vector which is opposite to the instance
      */
     public S2Point negate() {
-        return new S2Point(-theta, Math.PI - phi, vector.negate());
+        return new S2Point(-azimuth, Math.PI - polar, vector.negate());
     }
 
     /** {@inheritDoc} */
@@ -175,7 +171,6 @@ public final class S2Point implements Point<S2Point>, Serializable {
      */
     @Override
     public boolean equals(Object other) {
-
         if (this == other) {
             return true;
         }
@@ -186,7 +181,7 @@ public final class S2Point implements Point<S2Point>, Serializable {
                 return this.isNaN();
             }
 
-            return (theta == rhs.theta) && (phi == rhs.phi);
+            return (azimuth == rhs.azimuth) && (polar == rhs.polar);
         }
         return false;
     }
@@ -203,57 +198,40 @@ public final class S2Point implements Point<S2Point>, Serializable {
         if (isNaN()) {
             return 542;
         }
-        return 134 * (37 * Double.hashCode(theta) +  Double.hashCode(phi));
+        return 134 * (37 * Double.hashCode(azimuth) +  Double.hashCode(polar));
     }
 
     /** {@inheritDoc} */
     @Override
     public String toString() {
-        return SimpleCoordinateFormat.getPointFormat().format(getTheta(), getPhi());
+        return SimpleCoordinateFormat.getPointFormat().format(getAzimuth(), getPolar());
     }
 
     /** Build a vector from its spherical coordinates
-     * @param theta azimuthal angle \( \theta \) in the x-y plane
-     * @param phi polar angle \( \varphi \)
+     * @param azimuth azimuthal angle in the x-y plane
+     * @param polar polar angle
      * @return point instance with the given coordinates
-     * @see #getTheta()
-     * @see #getPhi()
-     * @exception IllegalArgumentException if \( \varphi \) is not in the [\( 0; \pi \)] range
+     * @see #getAzimuth()
+     * @see #getPolar()
+     * @exception IllegalArgumentException if polar is not in the {@code [0, +pi]} range
      */
-    public static S2Point of(final double theta, final double phi) {
-        return new S2Point(theta, phi, vector(theta, phi));
+    public static S2Point of(final double azimuth, final double polar) throws IllegalArgumentException {
+        if (polar < 0 || polar > Math.PI) {
+            throw new IllegalArgumentException(polar + " is out of [" + 0 + ", " + Math.PI + "] range");
+        }
+
+        return new S2Point(azimuth, polar, null);
     }
 
     /** Build a point from its underlying 3D vector
      * @param vector 3D vector
      * @return point instance with the coordinates determined by the given 3D vector
-     * @exception IllegalArgumentException if vector norm is zero
+     * @exception IllegalStateException if vector norm is zero
      */
     public static S2Point of(final Vector3D vector) {
-        return new S2Point(Math.atan2(vector.getY(), vector.getX()),
-                Vector3D.PLUS_Z.angle(vector),
-                vector.normalize());
-    }
-
-    /** Build the normalized vector corresponding to spherical coordinates.
-     * @param theta azimuthal angle \( \theta \) in the x-y plane
-     * @param phi polar angle \( \varphi \)
-     * @return normalized vector
-     * @exception IllegalArgumentException if \( \varphi \) is not in the [\( 0; \pi \)] range
-     */
-    private static Vector3D vector(final double theta, final double phi)
-       throws IllegalArgumentException {
-
-        if (phi < 0 || phi > Math.PI) {
-            throw new IllegalArgumentException(phi + " is out of [" + 0 + ", " + Math.PI + "] range");
-        }
-
-        final double cosTheta = Math.cos(theta);
-        final double sinTheta = Math.sin(theta);
-        final double cosPhi   = Math.cos(phi);
-        final double sinPhi   = Math.sin(phi);
+        SphericalCoordinates coords = vector.toSpherical();
 
-        return Vector3D.of(cosTheta * sinPhi, sinTheta * sinPhi, cosPhi);
+        return new S2Point(coords.getAzimuth(), coords.getPolar(), vector.normalize());
     }
 
     /** Parses the given string and returns a new point instance. The expected string
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/SphericalTestUtils.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/SphericalTestUtils.java
index a43bc3d..6baa3b1 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/SphericalTestUtils.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/SphericalTestUtils.java
@@ -46,7 +46,7 @@ public class SphericalTestUtils {
             protected void formatHyperplane(final Hyperplane<S1Point> hyperplane) {
                 final LimitAngle h = (LimitAngle) hyperplane;
                 getFormatter().format("%22.15e %b %22.15e",
-                                      h.getLocation().getAlpha(), h.isDirect(), h.getTolerance());
+                                      h.getLocation().getAzimuth(), h.isDirect(), h.getTolerance());
             }
 
         };
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/LimitAngleTest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/LimitAngleTest.java
index f9a9b3b..629f2b6 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/LimitAngleTest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/LimitAngleTest.java
@@ -26,7 +26,7 @@ public class LimitAngleTest {
     public void testReversedLimit() {
         for (int k = -2; k < 3; ++k) {
             LimitAngle l  = new LimitAngle(S1Point.of(1.0 + k * Geometry.TWO_PI), false, 1.0e-10);
-            Assert.assertEquals(l.getLocation().getAlpha(), l.getReverse().getLocation().getAlpha(), 1.0e-10);
+            Assert.assertEquals(l.getLocation().getAzimuth(), l.getReverse().getLocation().getAzimuth(), 1.0e-10);
             Assert.assertEquals(l.getTolerance(), l.getReverse().getTolerance(), 1.0e-10);
             Assert.assertTrue(l.sameOrientationAs(l));
             Assert.assertFalse(l.sameOrientationAs(l.getReverse()));
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/S1PointTest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/S1PointTest.java
index eac954d..f1c0f40 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/S1PointTest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/S1PointTest.java
@@ -56,7 +56,7 @@ public class S1PointTest {
     @Test
     public void testDistance() {
         S1Point a = S1Point.of(1.0);
-        S1Point b = S1Point.of(a.getAlpha() + 0.5 * Math.PI);
+        S1Point b = S1Point.of(a.getAzimuth() + 0.5 * Math.PI);
         Assert.assertEquals(0.5 * Math.PI, a.distance(b), 1.0e-10);
     }
 
@@ -92,7 +92,7 @@ public class S1PointTest {
     }
 
     private void checkPoint(S1Point p, double alpha) {
-        Assert.assertEquals(alpha, p.getAlpha(), EPS);
+        Assert.assertEquals(alpha, p.getAzimuth(), EPS);
     }
 
 }
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/CircleTest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/CircleTest.java
index 7b9ad53..8e96248 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/CircleTest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/CircleTest.java
@@ -99,10 +99,10 @@ public class CircleTest {
     @Test
     public void testSubSpace() {
         Circle circle = new Circle(S2Point.of(1.2, 2.5), S2Point.of(-4.3, 0), 1.0e-10);
-        Assert.assertEquals(0.0, circle.toSubSpace(S2Point.of(circle.getXAxis())).getAlpha(), 1.0e-10);
-        Assert.assertEquals(0.5 * Math.PI, circle.toSubSpace(S2Point.of(circle.getYAxis())).getAlpha(), 1.0e-10);
+        Assert.assertEquals(0.0, circle.toSubSpace(S2Point.of(circle.getXAxis())).getAzimuth(), 1.0e-10);
+        Assert.assertEquals(0.5 * Math.PI, circle.toSubSpace(S2Point.of(circle.getYAxis())).getAzimuth(), 1.0e-10);
         Vector3D p = Vector3D.of(1, 2, -4);
-        Assert.assertEquals(circle.getPhase(p), circle.toSubSpace(S2Point.of(p)).getAlpha(), 1.0e-10);
+        Assert.assertEquals(circle.getPhase(p), circle.toSubSpace(S2Point.of(p)).getAzimuth(), 1.0e-10);
     }
 
     @Test
@@ -177,9 +177,9 @@ public class CircleTest {
 
             SubLimitAngle  sub = new LimitAngle(S1Point.of(Geometry.TWO_PI * random.nextDouble()),
                                                 random.nextBoolean(), 1.0e-10).wholeHyperplane();
-            Vector3D psub = c.getPointAt(((LimitAngle) sub.getHyperplane()).getLocation().getAlpha());
+            Vector3D psub = c.getPointAt(((LimitAngle) sub.getHyperplane()).getLocation().getAzimuth());
             SubLimitAngle tsub = (SubLimitAngle) t.apply(sub, c, tc);
-            Vector3D ptsub = tc.getPointAt(((LimitAngle) tsub.getHyperplane()).getLocation().getAlpha());
+            Vector3D ptsub = tc.getPointAt(((LimitAngle) tsub.getHyperplane()).getLocation().getAzimuth());
             Assert.assertEquals(0.0, r.applyTo(psub).distance(ptsub), 1.0e-10);
 
         }
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
index 1661921..78f9097 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
@@ -30,8 +30,8 @@ public class S2PointTest {
     public void testS2Point() {
         for (int k = -2; k < 3; ++k) {
             S2Point p = S2Point.of(1.0 + k * Geometry.TWO_PI, 1.4);
-            Assert.assertEquals(1.0 + k * Geometry.TWO_PI, p.getTheta(), EPS);
-            Assert.assertEquals(1.4, p.getPhi(), EPS);
+            Assert.assertEquals(1.0 + k * Geometry.TWO_PI, p.getAzimuth(), EPS);
+            Assert.assertEquals(1.4, p.getPolar(), EPS);
             Assert.assertEquals(Math.cos(1.0) * Math.sin(1.4), p.getVector().getX(), EPS);
             Assert.assertEquals(Math.sin(1.0) * Math.sin(1.4), p.getVector().getY(), EPS);
             Assert.assertEquals(Math.cos(1.4), p.getVector().getZ(), EPS);
@@ -70,7 +70,7 @@ public class S2PointTest {
     @Test
     public void testDistance() {
         S2Point a = S2Point.of(1.0, 0.5 * Math.PI);
-        S2Point b = S2Point.of(a.getTheta() + 0.5 * Math.PI, a.getPhi());
+        S2Point b = S2Point.of(a.getAzimuth() + 0.5 * Math.PI, a.getPolar());
         Assert.assertEquals(0.5 * Math.PI, a.distance(b), 1.0e-10);
         Assert.assertEquals(Math.PI, a.distance(a.negate()), 1.0e-10);
         Assert.assertEquals(0.5 * Math.PI, S2Point.MINUS_I.distance(S2Point.MINUS_K), 1.0e-10);
@@ -108,7 +108,7 @@ public class S2PointTest {
     }
 
     private void checkPoint(S2Point p, double theta, double phi) {
-        Assert.assertEquals(theta, p.getTheta(), EPS);
-        Assert.assertEquals(phi, p.getPhi(), EPS);
+        Assert.assertEquals(theta, p.getAzimuth(), EPS);
+        Assert.assertEquals(phi, p.getPolar(), EPS);
     }
 }
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/SphericalPolygonsSetTest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/SphericalPolygonsSetTest.java
index 0f056e8..ce6b231 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/SphericalPolygonsSetTest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/SphericalPolygonsSetTest.java
@@ -375,7 +375,7 @@ public class SphericalPolygonsSetTest {
         SphericalPolygonsSet hexaWithHole =
                 (SphericalPolygonsSet) new RegionFactory<S2Point>().difference(hexa, hole);
 
-        for (double phi = center.getPhi() - alpha + 0.1; phi < center.getPhi() + alpha - 0.1; phi += 0.07) {
+        for (double phi = center.getPolar() - alpha + 0.1; phi < center.getPolar() + alpha - 0.1; phi += 0.07) {
             Location l = hexaWithHole.checkPoint(S2Point.of(Math.PI / 4, phi));
             if (phi < Math.PI / 6 || phi > Math.PI / 3) {
                 Assert.assertEquals(Location.INSIDE,  l);
@@ -428,7 +428,7 @@ public class SphericalPolygonsSetTest {
                             concentric.getSize(), 1.0e-10);
 
         // we expect lots of sign changes as we traverse all concentric rings
-        double phi = S2Point.of(center).getPhi();
+        double phi = S2Point.of(center).getPolar();
         Assert.assertEquals(+0.207, concentric.projectToBoundary(S2Point.of(-0.60,  phi)).getOffset(), 0.01);
         Assert.assertEquals(-0.048, concentric.projectToBoundary(S2Point.of(-0.21,  phi)).getOffset(), 0.01);
         Assert.assertEquals(+0.027, concentric.projectToBoundary(S2Point.of(-0.10,  phi)).getOffset(), 0.01);


[commons-geometry] 09/15: GEOEMTRY-7: removing spherical coordinate code for working with jacobians and hessians since I'm not sure how it would be used; it can be added back later if needed

Posted by er...@apache.org.
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 1c54b6ca49a920435f5aa742db3cc52b1e6f7d2b
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Tue Jul 10 22:06:01 2018 -0400

    GEOEMTRY-7: removing spherical coordinate code for working with jacobians and hessians since I'm not sure how it would be used; it can be added back later if needed
---
 .../threed/SphericalDerivativeConverter.java       | 241 ---------------------
 .../threed/SphericalDerivativeConverterTest.java   | 187 ----------------
 2 files changed, 428 deletions(-)

diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverter.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverter.java
deleted file mode 100644
index 45b8794..0000000
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverter.java
+++ /dev/null
@@ -1,241 +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.euclidean.threed;
-
-/** Class containing methods for converting gradients and Hessian
- * matrices from spherical to Cartesian coordinates.
- */
-public class SphericalDerivativeConverter {
-
-    /** Spherical coordinates. */
-    private final SphericalCoordinates spherical;
-
-    /** Cartesian vector equivalent to spherical coordinates. */
-    private final Vector3D vector;
-
-    /** Jacobian of (r, &theta; &Phi;). */
-    private double[][] jacobian;
-
-    /** Hessian of radius. */
-    private double[][] rHessian;
-
-    /** Hessian of azimuthal angle in the x-y plane &theta;. */
-    private double[][] thetaHessian;
-
-    /** Hessian of polar (co-latitude) angle &Phi;. */
-    private double[][] phiHessian;
-
-    public SphericalDerivativeConverter(SphericalCoordinates spherical) {
-        this.spherical = spherical;
-        this.vector = spherical.toVector();
-
-        computeJacobian();
-    }
-
-    /** Return the {@link SphericalCoordinates} for this instance.
-     * @return spherical coordinates for this instance
-     */
-    public SphericalCoordinates getSpherical() {
-        return spherical;
-    }
-
-    /** Return the {@link Vector3D} for this instance. This vector is
-     * equivalent to the spherical coordinates.
-     * @return vector for this instance
-     */
-    public Vector3D getVector() {
-        return vector;
-    }
-
-    /** Convert a gradient with respect to spherical coordinates into a gradient
-     * with respect to Cartesian coordinates.
-     * @param sGradient gradient with respect to spherical coordinates
-     * {df/dr, df/d&theta;, df/d&Phi;}
-     * @return gradient with respect to Cartesian coordinates
-     * {df/dx, df/dy, df/dz}
-     */
-    public double[] toCartesianGradient(final double[] sGradient) {
-        // compose derivatives as gradient^T . J
-        // the expressions have been simplified since we know jacobian[1][2] = dTheta/dZ = 0
-        return new double[] {
-            sGradient[0] * jacobian[0][0] + sGradient[1] * jacobian[1][0] + sGradient[2] * jacobian[2][0],
-            sGradient[0] * jacobian[0][1] + sGradient[1] * jacobian[1][1] + sGradient[2] * jacobian[2][1],
-            sGradient[0] * jacobian[0][2]                                 + sGradient[2] * jacobian[2][2]
-        };
-    }
-
-    /** Convert a Hessian with respect to spherical coordinates into a Hessian
-     * with respect to Cartesian coordinates.
-     * <p>
-     * As Hessian are always symmetric, we use only the lower left part of the provided
-     * spherical Hessian, so the upper part may not be initialized. However, we still
-     * do fill up the complete array we create, with guaranteed symmetry.
-     * </p>
-     * @param sHessian Hessian with respect to spherical coordinates
-     * {{d<sup>2</sup>f/dr<sup>2</sup>, d<sup>2</sup>f/drd&theta;, d<sup>2</sup>f/drd&Phi;},
-     *  {d<sup>2</sup>f/drd&theta;, d<sup>2</sup>f/d&theta;<sup>2</sup>, d<sup>2</sup>f/d&theta;d&Phi;},
-     *  {d<sup>2</sup>f/drd&Phi;, d<sup>2</sup>f/d&theta;d&Phi;, d<sup>2</sup>f/d&Phi;<sup>2</sup>}
-     * @param sGradient gradient with respect to spherical coordinates
-     * {df/dr, df/d&theta;, df/d&Phi;}
-     * @return Hessian with respect to Cartesian coordinates
-     * {{d<sup>2</sup>f/dx<sup>2</sup>, d<sup>2</sup>f/dxdy, d<sup>2</sup>f/dxdz},
-     *  {d<sup>2</sup>f/dxdy, d<sup>2</sup>f/dy<sup>2</sup>, d<sup>2</sup>f/dydz},
-     *  {d<sup>2</sup>f/dxdz, d<sup>2</sup>f/dydz, d<sup>2</sup>f/dz<sup>2</sup>}}
-     */
-    public double[][] toCartesianHessian(final double[][] sHessian, final double[] sGradient) {
-        computeHessians();
-
-        // compose derivative as J^T . H_f . J + df/dr H_r + df/dtheta H_theta + df/dphi H_phi
-        // the expressions have been simplified since we know jacobian[1][2] = dTheta/dZ = 0
-        // and H_theta is only a 2x2 matrix as it does not depend on z
-        final double[][] hj = new double[3][3];
-        final double[][] cHessian = new double[3][3];
-
-        // compute H_f . J
-        // beware we use ONLY the lower-left part of sHessian
-        hj[0][0] = sHessian[0][0] * jacobian[0][0] + sHessian[1][0] * jacobian[1][0] + sHessian[2][0] * jacobian[2][0];
-        hj[0][1] = sHessian[0][0] * jacobian[0][1] + sHessian[1][0] * jacobian[1][1] + sHessian[2][0] * jacobian[2][1];
-        hj[0][2] = sHessian[0][0] * jacobian[0][2]                                   + sHessian[2][0] * jacobian[2][2];
-        hj[1][0] = sHessian[1][0] * jacobian[0][0] + sHessian[1][1] * jacobian[1][0] + sHessian[2][1] * jacobian[2][0];
-        hj[1][1] = sHessian[1][0] * jacobian[0][1] + sHessian[1][1] * jacobian[1][1] + sHessian[2][1] * jacobian[2][1];
-        // don't compute hj[1][2] as it is not used below
-        hj[2][0] = sHessian[2][0] * jacobian[0][0] + sHessian[2][1] * jacobian[1][0] + sHessian[2][2] * jacobian[2][0];
-        hj[2][1] = sHessian[2][0] * jacobian[0][1] + sHessian[2][1] * jacobian[1][1] + sHessian[2][2] * jacobian[2][1];
-        hj[2][2] = sHessian[2][0] * jacobian[0][2]                                   + sHessian[2][2] * jacobian[2][2];
-
-        // compute lower-left part of J^T . H_f . J
-        cHessian[0][0] = jacobian[0][0] * hj[0][0] + jacobian[1][0] * hj[1][0] + jacobian[2][0] * hj[2][0];
-        cHessian[1][0] = jacobian[0][1] * hj[0][0] + jacobian[1][1] * hj[1][0] + jacobian[2][1] * hj[2][0];
-        cHessian[2][0] = jacobian[0][2] * hj[0][0]                             + jacobian[2][2] * hj[2][0];
-        cHessian[1][1] = jacobian[0][1] * hj[0][1] + jacobian[1][1] * hj[1][1] + jacobian[2][1] * hj[2][1];
-        cHessian[2][1] = jacobian[0][2] * hj[0][1]                             + jacobian[2][2] * hj[2][1];
-        cHessian[2][2] = jacobian[0][2] * hj[0][2]                             + jacobian[2][2] * hj[2][2];
-
-        // add gradient contribution
-        cHessian[0][0] += sGradient[0] * rHessian[0][0] + sGradient[1] * thetaHessian[0][0] + sGradient[2] * phiHessian[0][0];
-        cHessian[1][0] += sGradient[0] * rHessian[1][0] + sGradient[1] * thetaHessian[1][0] + sGradient[2] * phiHessian[1][0];
-        cHessian[2][0] += sGradient[0] * rHessian[2][0]                                     + sGradient[2] * phiHessian[2][0];
-        cHessian[1][1] += sGradient[0] * rHessian[1][1] + sGradient[1] * thetaHessian[1][1] + sGradient[2] * phiHessian[1][1];
-        cHessian[2][1] += sGradient[0] * rHessian[2][1]                                     + sGradient[2] * phiHessian[2][1];
-        cHessian[2][2] += sGradient[0] * rHessian[2][2]                                     + sGradient[2] * phiHessian[2][2];
-
-        // ensure symmetry
-        cHessian[0][1] = cHessian[1][0];
-        cHessian[0][2] = cHessian[2][0];
-        cHessian[1][2] = cHessian[2][1];
-
-        return cHessian;
-    }
-
-    /** Evaluates (r, &theta;, &phi;) Jacobian. */
-    private void computeJacobian() {
-
-        // intermediate variables
-        final double r    = spherical.getRadius();
-        final double x    = vector.getX();
-        final double y    = vector.getY();
-        final double z    = vector.getZ();
-        final double rho2 = x * x + y * y;
-        final double rho  = Math.sqrt(rho2);
-        final double r2   = rho2 + z * z;
-
-        jacobian = new double[3][3];
-
-        // row representing the gradient of r
-        jacobian[0][0] = x / r;
-        jacobian[0][1] = y / r;
-        jacobian[0][2] = z / r;
-
-        // row representing the gradient of theta
-        jacobian[1][0] = -y / rho2;
-        jacobian[1][1] =  x / rho2;
-        // jacobian[1][2] is already set to 0 at allocation time
-
-        // row representing the gradient of phi
-        jacobian[2][0] = x * z / (rho * r2);
-        jacobian[2][1] = y * z / (rho * r2);
-        jacobian[2][2] = -rho / r2;
-    }
-
-    /** Lazy evaluation of Hessians. */
-    private void computeHessians() {
-        if (rHessian == null) {
-
-            // intermediate variables
-            final double r      = spherical.getRadius();
-            final double x      = vector.getX();
-            final double y      = vector.getY();
-            final double z      = vector.getZ();
-            final double x2     = x * x;
-            final double y2     = y * y;
-            final double z2     = z * z;
-            final double rho2   = x2 + y2;
-            final double rho    = Math.sqrt(rho2);
-            final double r2     = rho2 + z2;
-            final double xOr    = x / r;
-            final double yOr    = y / r;
-            final double zOr    = z / r;
-            final double xOrho2 = x / rho2;
-            final double yOrho2 = y / rho2;
-            final double xOr3   = xOr / r2;
-            final double yOr3   = yOr / r2;
-            final double zOr3   = zOr / r2;
-
-            // lower-left part of Hessian of r
-            rHessian = new double[3][3];
-            rHessian[0][0] = y * yOr3 + z * zOr3;
-            rHessian[1][0] = -x * yOr3;
-            rHessian[2][0] = -z * xOr3;
-            rHessian[1][1] = x * xOr3 + z * zOr3;
-            rHessian[2][1] = -y * zOr3;
-            rHessian[2][2] = x * xOr3 + y * yOr3;
-
-            // upper-right part is symmetric
-            rHessian[0][1] = rHessian[1][0];
-            rHessian[0][2] = rHessian[2][0];
-            rHessian[1][2] = rHessian[2][1];
-
-            // lower-left part of Hessian of azimuthal angle theta
-            thetaHessian = new double[2][2];
-            thetaHessian[0][0] = 2 * xOrho2 * yOrho2;
-            thetaHessian[1][0] = yOrho2 * yOrho2 - xOrho2 * xOrho2;
-            thetaHessian[1][1] = -2 * xOrho2 * yOrho2;
-
-            // upper-right part is symmetric
-            thetaHessian[0][1] = thetaHessian[1][0];
-
-            // lower-left part of Hessian of polar (co-latitude) angle phi
-            final double rhor2       = rho * r2;
-            final double rho2r2      = rho * rhor2;
-            final double rhor4       = rhor2 * r2;
-            final double rho3r4      = rhor4 * rho2;
-            final double r2P2rho2    = 3 * rho2 + z2;
-            phiHessian = new double[3][3];
-            phiHessian[0][0] = z * (rho2r2 - x2 * r2P2rho2) / rho3r4;
-            phiHessian[1][0] = -x * y * z * r2P2rho2 / rho3r4;
-            phiHessian[2][0] = x * (rho2 - z2) / rhor4;
-            phiHessian[1][1] = z * (rho2r2 - y2 * r2P2rho2) / rho3r4;
-            phiHessian[2][1] = y * (rho2 - z2) / rhor4;
-            phiHessian[2][2] = 2 * rho * zOr3 / r;
-
-            // upper-right part is symmetric
-            phiHessian[0][1] = phiHessian[1][0];
-            phiHessian[0][2] = phiHessian[2][0];
-            phiHessian[1][2] = phiHessian[2][1];
-        }
-    }
-}
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverterTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverterTest.java
deleted file mode 100644
index 4b9bcbd..0000000
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverterTest.java
+++ /dev/null
@@ -1,187 +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.euclidean.threed;
-
-import org.apache.commons.geometry.core.Geometry;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SphericalDerivativeConverterTest {
-
-    private static final double EPS = 1e-10;
-
-    @Test
-    public void testConstructor() {
-        // arrange
-        SphericalCoordinates sc = SphericalCoordinates.of(2, Geometry.PI, Geometry.HALF_PI);
-
-        // act
-        SphericalDerivativeConverter conv = new SphericalDerivativeConverter(sc);
-
-        // assert
-        checkSpherical(conv.getSpherical(), 2, Geometry.PI, Geometry.HALF_PI);
-        checkVector(conv.getVector(), -2, 0, 0);
-    }
-
-    @Test
-    public void testToCartesianGradient() {
-        // NOTE: The following set of test data is taken from the original test for this code in commons-math.
-        // The test in that project generated and checked the inputs on the fly using the commons-math differentiation
-        // classes. However, since we don't have the benefit of those here, we're using some selected data points
-        // from that test.
-
-        // act/assert
-        checkToCartesianGradient(
-                SphericalCoordinates.of(0.2, 0.1, 0.1),
-                new double[] { 3.1274095413292105E-4, -1.724542757978006E-6, 1.5102449769881866E-4 },
-                new double[] { 0.0007872851, -0.0000078127, 0.0002357921 });
-
-        checkToCartesianGradient(
-                SphericalCoordinates.of(0.2, 0.1, 1.6),
-                new double[] { -7.825830329191124E-8, 7.798528724837122E-10, -4.027286034178383E-7 },
-                new double[] { -0.0000000197, 0.0000000019, 0.0000020151 });
-
-        checkToCartesianGradient(
-                SphericalCoordinates.of(0.2, 1.6, 0.1),
-                new double[] { -9.075903886546823E-6, -1.5573157416535893E-5, -4.352284221940998E-6 },
-                new double[] { 0.0007802833, 0.0000002252, -0.000006858 });
-
-        checkToCartesianGradient(
-                SphericalCoordinates.of(0.2, 2.4, 2.4),
-                new double[] { 6.045188551967462E-4, 2.944844493772992E-5, 5.207279563401837E-5 },
-                new double[] { -0.0003067696, -0.0000146129, -0.0006216347 });
-
-        checkToCartesianGradient(
-                SphericalCoordinates.of(9.2, 5.5, 2.4),
-                new double[] { 27.09285722408859, 327.829199283976, 422.53939642005736 },
-                new double[] { 26.1884919572, 48.3685006936, -51.0009075025 });
-    }
-
-    private void checkToCartesianGradient(SphericalCoordinates spherical, double[] sGradient, double[] cGradient) {
-        SphericalDerivativeConverter conv = new SphericalDerivativeConverter(spherical);
-
-        double[] result = conv.toCartesianGradient(sGradient);
-
-        Assert.assertArrayEquals(cGradient, result, EPS);
-    }
-
-    @Test
-    public void testToCartesianHessian() {
-        // NOTE: The following set of test data is taken from the original test for this code in commons-math.
-        // The test in that project generated and checked the inputs on the fly using the commons-math differentiation
-        // classes. However, since we don't have the benefit of those here, we're using some selected data points
-        // from that test.
-        //
-        // The NaN values in the input spherical Hessians are only present to ensure that the upper-right
-        // part of the matrix is not used in the calculation.
-
-        // act/assert
-        checkToCartesianHessian(
-                SphericalCoordinates.of(0.2, 0.0, 0.1),
-                new double[] { 3.147028015595093E-4, -1.5708927954007288E-7, 1.5209020574753025E-4 },
-                new double[][] {
-                    { 0.004720542023392639, Double.NaN, Double.NaN },
-                    { -3.927231988501822E-6, -1.5732003526076452E-5, Double.NaN },
-                    { 0.0030418041149506037, -3.0840214797113795E-6, -1.56400962465978E-4 }
-                },
-                new double[][] {
-                    { 0.0, -3.940348984959686E-4, 0.011880399467047453 },
-                    { -3.940348984959686E-4, 7.867570038987733E-6, -1.1860608699245036E-4 },
-                    { 0.011880399467047453, -1.1860608699245036E-4, 0.002384031969540735 }
-                });
-
-        checkToCartesianHessian(
-                SphericalCoordinates.of(0.2, 0.2, 1.7),
-                new double[] { -6.492205616890373E-6, 9.721055406032577E-8, -7.490005649457144E-6 },
-                new double[][] {
-                    { -9.660140526063848E-5, Double.NaN, Double.NaN },
-                    { 2.087263937942704E-6, 3.0135301759512823E-7, Double.NaN },
-                    { -1.4908056742242714E-4, 2.228225255291761E-6, -1.1271700251178201E-4 }
-                },
-                new double[][] {
-                    { 0.0, 8.228328248729827E-7, 1.9536195257978514E-4 },
-                    { 8.228328248729827E-7, -1.568516517220037E-7, -1.862033454396115E-5 },
-                    { 1.9536195257978514E-4, -1.862033454396115E-5, -0.0029473017314775615 }
-                });
-
-        checkToCartesianHessian(
-                SphericalCoordinates.of(0.2, 1.6, 0.1),
-                new double[] { -9.075903886546686E-6, -1.5573157416535897E-5, -4.352284221940931E-6 },
-                new double[][] {
-                    { -1.3557892633841054E-4, Double.NaN, Double.NaN },
-                    { -3.106944464923055E-4, 4.4143436330613375E-7, Double.NaN },
-                    { -8.660889278565699E-5, -1.489922640116937E-4, 5.374400993902801E-6 }
-                },
-                new double[][] {
-                    { 0.0, -3.862868527078941E-4, 0.011763015339492582 },
-                    { -3.862868527078941E-4, -2.229868350965674E-7, 3.395142163599996E-6 },
-                    { 0.011763015339492582, 3.395142163599996E-6, -6.892478835391066E-5 }
-                });
-
-        checkToCartesianHessian(
-                SphericalCoordinates.of(0.2, 2.4, 2.5),
-                new double[] { 6.911538590806891E-4, 3.344602742543664E-5, 3.330643810411849E-5 },
-                new double[][] {
-                    { 0.010200457858547542, Double.NaN, Double.NaN },
-                    { 6.695363800209198E-4, -3.070347513695088E-5, Double.NaN },
-                    { 6.68380906286568E-4, 3.001744637007274E-5, -2.273032055462482E-4 }
-                },
-                new double[][] {
-                    { 0.0, 1.9000713243497378E-4, 0.007402721147059207 },
-                    { 1.9000713243497378E-4, 1.6118798431431763E-5, 3.139960286869248E-4 },
-                    { 0.007402721147059207, 3.139960286869248E-4, 0.008155571186075681 }
-                });
-
-        checkToCartesianHessian(
-                SphericalCoordinates.of(9.2, 5.6, 2.5),
-                new double[] { 41.42645719593436, 859.1407583470807, 939.7112322238082 },
-                new double[][] {
-                    { 11.642163255436742, Double.NaN, Double.NaN },
-                    { 54.8154280776715, 5286.1651942531325, Double.NaN },
-                    { 60.370567966140726, 4700.570567363823, 4929.996883244262 }
-                },
-                new double[][] {
-                    { 0.0, 36.772022140868714, -22.087375306566134 },
-                    { 36.772022140868714, 212.8111723550033, -63.91326828897971 },
-                    { -22.087375306566134, -63.91326828897971, 25.593304575600133 }
-                });
-    }
-
-    private void checkToCartesianHessian(SphericalCoordinates spherical, double[] sGradient,
-            double[][] sHessian, double[][] cHessian) {
-        SphericalDerivativeConverter conv = new SphericalDerivativeConverter(spherical);
-
-        double[][] result = conv.toCartesianHessian(sHessian, sGradient);
-
-        Assert.assertEquals(cHessian.length, result.length);
-        for (int i=0; i<cHessian.length; ++i) {
-            Assert.assertArrayEquals("Hessians differ at row " + i, cHessian[i], result[i], EPS);
-        }
-    }
-
-    private void checkSpherical(SphericalCoordinates c, double radius, double azimuth, double polar) {
-        Assert.assertEquals(radius, c.getRadius(), EPS);
-        Assert.assertEquals(azimuth, c.getAzimuth(), EPS);
-        Assert.assertEquals(polar, c.getPolar(), EPS);
-    }
-
-    private void checkVector(Vector3D v, double x, double y, double z) {
-        Assert.assertEquals(x, v.getX(), EPS);
-        Assert.assertEquals(y, v.getY(), EPS);
-        Assert.assertEquals(z, v.getZ(), EPS);
-    }
-}


[commons-geometry] 14/15: License header.

Posted by er...@apache.org.
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 49adafa76de421ac0debbf9761879243aaa26bdb
Author: Gilles Sadowski <gi...@harfang.homelinux.org>
AuthorDate: Sat Jul 21 11:58:31 2018 +0200

    License header.
---
 .../commons/geometry/euclidean/oned/Vector1DTest.java    | 16 ++++++++++++++++
 .../geometry/euclidean/threed/Cartesian3DTest.java       | 16 ++++++++++++++++
 .../commons/geometry/euclidean/twod/Cartesian2DTest.java | 16 ++++++++++++++++
 .../commons/geometry/euclidean/twod/Vector2DTest.java    | 16 ++++++++++++++++
 4 files changed, 64 insertions(+)

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..9e9119c 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
@@ -1,3 +1,19 @@
+/*
+ * 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.euclidean.oned;
 
 import java.util.regex.Pattern;
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
index 6e075b9..8d47105 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
@@ -1,3 +1,19 @@
+/*
+ * 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.euclidean.threed;
 
 import java.util.regex.Pattern;
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
index 5852616..b7ef382 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
@@ -1,3 +1,19 @@
+/*
+ * 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.euclidean.twod;
 
 import java.util.regex.Pattern;
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 1ef8d3b..1846eac 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
@@ -1,3 +1,19 @@
+/*
+ * 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.euclidean.twod;
 
 import java.util.regex.Pattern;


[commons-geometry] 02/15: using built-in Math.hypot() method for 2D Euclidean distance calculation

Posted by er...@apache.org.
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 abb1a9778ac0b35d048ff7b4e3e522548a009a81
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Thu Jun 21 23:45:49 2018 -0400

    using built-in Math.hypot() method for 2D Euclidean distance calculation
---
 .../org/apache/commons/geometry/euclidean/twod/Cartesian2D.java     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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 8710b3e..a1a9785 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
@@ -96,8 +96,8 @@ public abstract class Cartesian2D implements Spatial, Serializable {
      * @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));
+        final double dx = x - other.x;
+        final double dy = y - other.y;
+        return Math.hypot(dx, dy);
     }
 }


[commons-geometry] 03/15: Merge branch 'master' into polar-spherical-working

Posted by er...@apache.org.
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 96f32b6e81e5816cbde19d10fd04bce7cca0bd7e
Merge: abb1a97 afdd744
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Thu Jun 21 23:56:33 2018 -0400

    Merge branch 'master' into polar-spherical-working

 README.md                            | 105 +++++++++++++++++++++++++++++++++++
 commons-geometry-core/README.md      | 105 +++++++++++++++++++++++++++++++++++
 commons-geometry-enclosing/README.md | 105 +++++++++++++++++++++++++++++++++++
 commons-geometry-euclidean/README.md | 105 +++++++++++++++++++++++++++++++++++
 commons-geometry-hull/README.md      | 105 +++++++++++++++++++++++++++++++++++
 commons-geometry-spherical/README.md | 105 +++++++++++++++++++++++++++++++++++
 pom.xml                              |   2 +-
 7 files changed, 631 insertions(+), 1 deletion(-)


[commons-geometry] 12/15: GEOMETRY-7: merging polar and spherical code with latest from master

Posted by er...@apache.org.
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 9b3f388b8f4f742bde87161438954359dfefb43b
Merge: c892bfe 8273d59
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Fri Jul 20 22:51:27 2018 -0400

    GEOMETRY-7: merging polar and spherical code with latest from master

 .../DoubleFunction1N.java}                         |  20 +-
 .../DoubleFunction2N.java}                         |  21 +-
 .../DoubleFunction3N.java}                         |  22 +-
 .../geometry/core/internal/SimpleTupleFormat.java  | 426 +++++++++++++++++++++
 .../core/{util => internal}/package-info.java      |   5 +-
 .../core/util/AbstractCoordinateParser.java        | 257 -------------
 .../commons/geometry/core/util/Coordinates.java    |  68 ----
 .../geometry/core/util/SimpleCoordinateFormat.java | 202 ----------
 .../SimpleTupleFormatTest.java}                    |  85 ++--
 .../geometry/euclidean/oned/Cartesian1D.java       |   9 +-
 .../commons/geometry/euclidean/oned/Point1D.java   |  27 +-
 .../commons/geometry/euclidean/oned/Vector1D.java  |  27 +-
 .../geometry/euclidean/threed/Cartesian3D.java     |   9 +-
 .../commons/geometry/euclidean/threed/Point3D.java |  29 +-
 .../euclidean/threed/SphericalCoordinates.java     |  46 +--
 .../geometry/euclidean/threed/Vector3D.java        |  29 +-
 .../geometry/euclidean/twod/Cartesian2D.java       |   9 +-
 .../commons/geometry/euclidean/twod/Point2D.java   |  31 +-
 .../geometry/euclidean/twod/PolarCoordinates.java  |  44 +--
 .../commons/geometry/euclidean/twod/Vector2D.java  |  31 +-
 .../geometry/euclidean/oned/Cartesian1DTest.java   |  16 +
 .../geometry/euclidean/oned/Point1DTest.java       |  11 -
 .../geometry/euclidean/oned/Vector1DTest.java      |  23 +-
 .../geometry/euclidean/threed/Cartesian3DTest.java |  16 +
 .../geometry/euclidean/threed/Point3DTest.java     |  13 +-
 .../euclidean/threed/SphericalCoordinatesTest.java |  35 +-
 .../geometry/euclidean/threed/Vector3DTest.java    |  23 +-
 .../geometry/euclidean/twod/Cartesian2DTest.java   |  17 +-
 .../geometry/euclidean/twod/Point2DTest.java       |  11 -
 .../euclidean/twod/PolarCoordinatesTest.java       |  36 +-
 .../geometry/euclidean/twod/Vector2DTest.java      |  21 +-
 .../commons/geometry/spherical/oned/S1Point.java   |  21 +-
 .../commons/geometry/spherical/twod/S2Point.java   |  22 +-
 .../geometry/spherical/oned/S1PointTest.java       |  12 -
 .../geometry/spherical/twod/S2PointTest.java       |  11 -
 35 files changed, 694 insertions(+), 991 deletions(-)

diff --cc commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
index 6e4da3e,7c2b3f7..5ddb722
--- 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
@@@ -44,10 -44,10 +44,10 @@@ public final class Point3D extends Cart
          new Point3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
  
      /** Serializable version identifier. */
-     private static final long serialVersionUID = 1313493323784566947L;
+     private static final long serialVersionUID = 20180710L;
  
--    /** Factory for delegating instance creation. */
-     private static Coordinates.Factory3D<Point3D> FACTORY = new Coordinates.Factory3D<Point3D>() {
 -    private static DoubleFunction3N<Point3D> FACTORY = new DoubleFunction3N<Point3D>() {
++    /** Package private factory for delegating instance creation. */
++    static DoubleFunction3N<Point3D> FACTORY = new DoubleFunction3N<Point3D>() {
  
          /** {@inheritDoc} */
          @Override
diff --cc commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
index 5be957d,0000000..02ace69
mode 100644,000000..100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
@@@ -1,331 -1,0 +1,313 @@@
 +/*
 + * 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.euclidean.threed;
 +
 +import java.io.Serializable;
 +
 +import org.apache.commons.geometry.core.Geometry;
 +import org.apache.commons.geometry.core.Spatial;
- import org.apache.commons.geometry.core.util.Coordinates;
- import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
++import org.apache.commons.geometry.core.internal.DoubleFunction3N;
++import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
 +import org.apache.commons.geometry.euclidean.twod.PolarCoordinates;
 +import org.apache.commons.numbers.angle.PlaneAngleRadians;
 +
 +/** Class representing <a href="https://en.wikipedia.org/wiki/Spherical_coordinate_system">spherical coordinates</a>
 + * in 3 dimensional Euclidean space.
 + *
 + * <p>Spherical coordinates for a point are defined by three values:
 + * <ol>
 + * 	<li><em>Radius</em> - The distance from the point to a fixed referenced point.</li>
 + * 	<li><em>Azimuth angle</em> - The angle measured from a fixed reference direction in a plane to
 + * the orthogonal projection of the point on that plane.</li>
 + *	<li><em>Polar angle</em> - The angle measured from a fixed zenith direction to the point. The zenith
 + *direction must be orthogonal to the reference plane.</li>
 + * </ol>
 + * This class follows the convention of using the origin as the reference point; the positive x-axis as the
 + * reference direction for the azimuth angle, measured in the x-y plane with positive angles moving counter-clockwise
 + * toward the positive y-axis; and the positive z-axis as the zenith direction. Spherical coordinates are
 + * related to Cartesian coordinates as follows:
 + * <pre>
 + * x = r cos(&theta;) sin(&Phi;)
 + * y = r sin(&theta;) sin(&Phi;)
 + * z = r cos(&Phi;)
 + *
 + * r = &radic;(x<sup>2</sup>+y<sup>2</sup>+z<sup>2</sup>)
 + * &theta; = atan2(y, x)
 + * &Phi; = acos(z/r)
 + * </pre>
 + * where <em>r</em> is the radius, <em>&theta;</em> is the azimuth angle, and <em>&Phi;</em> is the polar angle
 + * of the spherical coordinates.
 + * </p>
 + *
 + * <p>There are numerous, competing conventions for the symbols used to represent spherical coordinate values. For
 + * example, the mathematical convention is to use <em>(r, &theta;, &Phi;)</em> to represent radius, azimuth angle, and
 + * polar angle, whereas the physics convention flips the angle values and uses <em>(r, &Phi;, &theta;)</em>. As such,
 + * this class avoids the use of these symbols altogether in favor of the less ambiguous formal names of the values,
 + * e.g. {@code radius}, {@code azimuth}, and {@code polar}.
 + * </p>
 + *
 + * <p>In order to ensure the uniqueness of coordinate sets, coordinate values
 + * are normalized so that {@code radius} is in the range {@code [0, +Infinity)},
 + * {@code azimuth} is in the range {@code [0, 2pi)}, and {@code polar} is in the
 + * range {@code [0, pi]}.</p>
 + *
 + * @see <a href="https://en.wikipedia.org/wiki/Spherical_coordinate_system">Spherical Coordinate System</a>
 + */
 +public final class SphericalCoordinates implements Spatial, Serializable {
 +
 +    /** Serializable version identifier. */
 +    private static final long serialVersionUID = 20180623L;
 +
 +    /** Factory object for delegating instance creation. */
-     private static final Coordinates.Factory3D<SphericalCoordinates> FACTORY = new Coordinates.Factory3D<SphericalCoordinates>() {
++    private static final DoubleFunction3N<SphericalCoordinates> FACTORY = new DoubleFunction3N<SphericalCoordinates>() {
 +
 +        /** {@inheritDoc} */
 +        @Override
-         public SphericalCoordinates create(double a1, double a2, double a3) {
-             return new SphericalCoordinates(a1, a2, a3);
++        public SphericalCoordinates apply(double n1, double n2, double n3) {
++            return new SphericalCoordinates(n1, n2, n3);
 +        }
 +    };
 +
 +    /** Radius value. */
 +    private final double radius;
 +
 +    /** Azimuth angle in radians. */
 +    private final double azimuth;
 +
 +    /** Polar angle in radians. */
 +    private final double polar;
 +
 +    /** Simple constructor. The given inputs are normalized.
 +     * @param radius Radius value.
 +     * @param azimuth Azimuth angle in radians.
 +     * @param polar Polar angle in radians.
 +     */
 +    private SphericalCoordinates(double radius, double azimuth, double polar) {
 +        if (radius < 0) {
 +            // negative radius; flip the angles
 +            radius = Math.abs(radius);
 +            azimuth += Geometry.PI;
 +            polar += Geometry.PI;
 +        }
 +
 +        this.radius = radius;
 +        this.azimuth = normalizeAzimuth(azimuth);
 +        this.polar = normalizePolar(polar);
 +    }
 +
 +    /** Return the radius value. The value is in the range {@code [0, +Infinity)}.
 +     * @return the radius value
 +     */
 +    public double getRadius() {
 +        return radius;
 +    }
 +
 +    /** Return the azimuth angle in radians. This is the angle in the x-y plane measured counter-clockwise from
 +     * the positive x axis. The angle is in the range {@code [0, 2pi)}.
 +     * @return the azimuth angle in radians
 +     */
 +    public double getAzimuth() {
 +        return azimuth;
 +    }
 +
 +    /** Return the polar angle in radians. This is the angle the coordinate ray makes with the positive z axis.
 +     * The angle is in the range {@code [0, pi]}.
 +     * @return the polar angle in radians
 +     */
 +    public double getPolar() {
 +        return polar;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override
 +    public int getDimension() {
 +        return 3;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override
 +    public boolean isNaN() {
 +        return Double.isNaN(radius) || Double.isNaN(azimuth) || Double.isNaN(polar);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override
 +    public boolean isInfinite() {
 +        return !isNaN() && (Double.isInfinite(radius) || Double.isInfinite(azimuth) || Double.isInfinite(polar));
 +    }
 +
-     /** Convert this set of spherical coordinates to Cartesian coordinates.
-      * The Cartesian coordinates are computed and passed to the given
-      * factory instance. The factory's return value is returned.
-      * @param factory Factory instance that will be passed the computed Cartesian coordinates
-      * @return the value returned by the factory when passed Cartesian
-      *      coordinates equivalent to this set of spherical coordinates.
-      */
-     public <T> T toCartesian(final Coordinates.Factory3D<T> factory) {
-         return toCartesian(radius, azimuth, polar, factory);
-     }
- 
 +    /** Convert this set of spherical coordinates to a 3 dimensional vector.
 +     * @return A 3-dimensional vector with an equivalent set of
 +     *      coordinates.
 +     */
 +    public Vector3D toVector() {
-         return toCartesian(Vector3D.getFactory());
++        return toCartesian(radius, azimuth, polar, Vector3D.FACTORY);
 +    }
 +
 +    /** Convert this set of spherical coordinates to a 3 dimensional point.
 +    * @return A 3-dimensional point with an equivalent set of
 +    *      coordinates.
 +    */
 +    public Point3D toPoint() {
-         return toCartesian(Point3D.getFactory());
++        return toCartesian(radius, azimuth, polar, Point3D.FACTORY);
 +    }
 +
 +    /** Get a hashCode for this set of spherical coordinates.
 +     * <p>All NaN values have the same hash code.</p>
 +     *
 +     * @return a hash code value for this object
 +     */
 +    @Override
 +    public int hashCode() {
 +        if (isNaN()) {
 +            return 127;
 +        }
 +        return 449 * (79 * Double.hashCode(radius) + Double.hashCode(azimuth) + Double.hashCode(polar));
 +    }
 +
 +    /** Test for the equality of two sets of spherical coordinates.
 +     * <p>
 +     * If all values of two sets of coordinates are exactly the same, and none are
 +     * <code>Double.NaN</code>, the two sets are considered to be equal.
 +     * </p>
 +     * <p>
 +     * <code>NaN</code> values are considered to globally affect the coordinates
 +     * and be equal to each other - i.e, if either (or all) values of the
 +     * coordinate set are equal to <code>Double.NaN</code>, the set is equal to
 +     * {@link #NaN}.
 +     * </p>
 +     *
 +     * @param other Object to test for equality to this
 +     * @return true if two SphericalCoordinates objects are equal, false if
 +     *         object is null, not an instance of SphericalCoordinates, or
 +     *         not equal to this SphericalCoordinates instance
 +     *
 +     */
 +    @Override
 +    public boolean equals(Object other) {
 +        if (this == other) {
 +            return true;
 +        }
 +
 +        if (other instanceof SphericalCoordinates) {
 +            final SphericalCoordinates rhs = (SphericalCoordinates) other;
 +            if (rhs.isNaN()) {
 +                return this.isNaN();
 +            }
 +
 +            return (radius == rhs.radius) && (azimuth == rhs.azimuth) && (polar == rhs.polar);
 +        }
 +        return false;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override
 +    public String toString() {
-         return SimpleCoordinateFormat.getPointFormat().format(radius, azimuth, polar);
++        return SimpleTupleFormat.getDefault().format(radius, azimuth, polar);
 +    }
 +
 +    /** Return a new instance with the given spherical coordinate values. The values are normalized
 +     * so that {@code radius} lies in the range {@code [0, +Infinity)}, {@code azimuth} lies in the range
 +     * {@code [0, 2pi)}, and {@code polar} lies in the range {@code [0, +pi]}.
 +     * @param radius the length of the line segment from the origin to the coordinate point.
 +     * @param azimuth the angle in the x-y plane, measured in radians counter-clockwise
 +     *      from the positive x-axis.
 +     * @param polar the angle in radians between the positive z-axis and the ray from the origin
 +     *      to the coordinate point.
 +     * @return a new {@link SphericalCoordinates} instance representing the same point as the given set of
 +     *      spherical coordinates.
 +     */
 +    public static SphericalCoordinates of(final double radius, final double azimuth, final double polar) {
 +        return new SphericalCoordinates(radius, azimuth, polar);
 +    }
 +
 +    /** Convert the given set of Cartesian coordinates to spherical coordinates.
 +     * @param x X coordinate value
 +     * @param y Y coordinate value
 +     * @param z Z coordinate value
 +     * @return a set of spherical coordinates equivalent to the given Cartesian coordinates
 +     */
 +    public static SphericalCoordinates ofCartesian(final double x, final double y, final double z) {
 +        final double radius = Math.sqrt((x*x) + (y*y) + (z*z));
 +        final double azimuth = Math.atan2(y, x);
 +
 +        // default the polar angle to 0 when the radius is 0
 +        final double polar = (radius > 0.0) ? Math.acos(z / radius) : 0.0;
 +
 +        return new SphericalCoordinates(radius, azimuth, polar);
 +    }
 +
 +    /** Parse the given string and return a new {@link SphericalCoordinates} instance. The parsed
 +     * coordinate values are normalized as in the {@link #of(double, double, double)} method.
 +     * The expected string format is the same as that returned by {@link #toString()}.
 +     * @param input the string to parse
 +     * @return new {@link SphericalCoordinates} instance
 +     * @throws IllegalArgumentException if the string format is invalid.
 +     */
 +    public static SphericalCoordinates parse(String input) {
-         return SimpleCoordinateFormat.getPointFormat().parse(input, FACTORY);
++        return SimpleTupleFormat.getDefault().parse(input, FACTORY);
 +    }
 +
 +    /** Normalize an azimuth value to be within the range {@code [0, 2pi)}. This
 +     * is exactly equivalent to {@link PolarCoordinates#normalizeAzimuth(double)}.
 +     * @param azimuth azimuth value in radians
 +     * @return equivalent azimuth value in the range {@code [0, 2pi)}.
 +     * @see PolarCoordinates#normalizeAzimuth(double)
 +     */
 +    public static double normalizeAzimuth(double azimuth) {
 +        return PolarCoordinates.normalizeAzimuth(azimuth);
 +    }
 +
 +    /** Normalize a polar value to be within the range {@code [0, +pi]}. Since the
 +     * polar angle is the angle between two vectors (the zenith direction and the
 +     * point vector), the sign of the angle is not significant as in the azimuth angle.
 +     * For example, a polar angle of {@code -pi/2} and one of {@code +pi/2} will both
 +     * normalize to {@code pi/2}.
 +     * @param polar polar value in radians
 +     * @return equalivalent polar value in the range {@code [0, +pi]}
 +     */
 +    public static double normalizePolar(double polar) {
 +        // normalize the polar angle; this is the angle between the polar vector and the point ray
 +        // so it is unsigned (unlike the azimuth) and should be in the range [0, pi]
 +        if (Double.isFinite(polar)) {
 +            polar = Math.abs(PlaneAngleRadians.normalizeBetweenMinusPiAndPi(polar));
 +        }
 +
 +        return polar;
 +    }
 +
-     /** Convert the given set of spherical coordinates to Cartesian coordinates.
-      * The Cartesian coordinates are computed and passed to the given
++    /** Package private method to convert the given set of spherical coordinates to
++     * Cartesian coordinates. The Cartesian coordinates are computed and passed to the given
 +     * factory instance. The factory's return value is returned.
 +     * @param radius The spherical radius value.
 +     * @param azimuth The spherical azimuth angle in radians.
 +     * @param polar The spherical polar angle in radians.
 +     * @param factory Factory instance that will be passed the
 +     * @return the value returned by the factory when passed Cartesian
 +     *      coordinates equivalent to the given set of spherical coordinates.
 +     */
-     public static <T> T toCartesian(final double radius, final double azimuth, final double polar,
-             Coordinates.Factory3D<T> factory) {
++    static <T> T toCartesian(final double radius, final double azimuth, final double polar,
++            DoubleFunction3N<T> factory) {
 +        final double xyLength = radius * Math.sin(polar);
 +
 +        final double x = xyLength * Math.cos(azimuth);
 +        final double y = xyLength * Math.sin(azimuth);
 +        final double z = radius * Math.cos(polar);
 +
-         return factory.create(x, y, z);
-     }
- 
-     /** Return a factory object for generating new {@link SphericalCoordinates} instances.
-      * @return factory object for generating new instances.
-      */
-     public static Coordinates.Factory3D<SphericalCoordinates> getFactory() {
-         return FACTORY;
++        return factory.apply(x, y, z);
 +    }
 +}
diff --cc commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java
index eeeeb72,459b014..c35b2d0
--- 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
@@@ -66,8 -66,8 +66,8 @@@ public final class Vector3D extends Car
      /** Error message when norms are zero. */
      private static final String ZERO_NORM_MSG = "Norm is zero";
  
--    /** Factory for delegating instance creation. */
-     private static Coordinates.Factory3D<Vector3D> FACTORY = new Coordinates.Factory3D<Vector3D>() {
 -    private static DoubleFunction3N<Vector3D> FACTORY = new DoubleFunction3N<Vector3D>() {
++    /** Package private factory for delegating instance creation. */
++    static DoubleFunction3N<Vector3D> FACTORY = new DoubleFunction3N<Vector3D>() {
  
          /** {@inheritDoc} */
          @Override
diff --cc commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
index 6561570,c9fde1f..4009398
--- 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
@@@ -43,10 -43,10 +43,10 @@@ public final class Point2D extends Cart
          new Point2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
  
      /** Serializable UID. */
-     private static final long serialVersionUID = 266938651998679754L;
+     private static final long serialVersionUID = 20180710L;
  
--    /** Factory for delegating instance creation. */
-     private static Coordinates.Factory2D<Point2D> FACTORY = new Coordinates.Factory2D<Point2D>() {
 -    private static DoubleFunction2N<Point2D> FACTORY = new DoubleFunction2N<Point2D>() {
++    /** Package private factory for delegating instance creation. */
++    static DoubleFunction2N<Point2D> FACTORY = new DoubleFunction2N<Point2D>() {
  
          /** {@inheritDoc} */
          @Override
@@@ -178,15 -172,6 +172,15 @@@
          return new Point2D(p[0], p[1]);
      }
  
 +    /**Return a point with coordinates equivalent to the given set of polar coordinates.
 +     * @param radius The polar coordinate radius value.
 +     * @param azimuth The polar coordinate azimuth angle in radians.
 +     * @return point instance with coordinates equivalent to the given polar coordinates.
 +     */
 +    public static Point2D ofPolar(final double radius, final double azimuth) {
-         return PolarCoordinates.toCartesian(radius, azimuth, getFactory());
++        return PolarCoordinates.toCartesian(radius, azimuth, FACTORY);
 +    }
 +
      /** Parses the given string and returns a new point instance. The expected string
       * format is the same as that returned by {@link #toString()}.
       * @param str the string to parse
diff --cc commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
index 9dbbfa1,0000000..d1c6aca
mode 100644,000000..100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
@@@ -1,280 -1,0 +1,262 @@@
 +/*
 + * 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.euclidean.twod;
 +
 +import java.io.Serializable;
 +
 +import org.apache.commons.geometry.core.Geometry;
 +import org.apache.commons.geometry.core.Spatial;
- import org.apache.commons.geometry.core.util.Coordinates;
- import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
++import org.apache.commons.geometry.core.internal.DoubleFunction2N;
++import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
 +import org.apache.commons.numbers.angle.PlaneAngleRadians;
 +
 +/** Class representing <a href="https://en.wikipedia.org/wiki/Polar_coordinate_system">polar coordinates</a>
 + * in 2 dimensional Euclidean space.
 + *
 + * <p>Polar coordinates are defined by a distance from a reference point
 + * and an angle from a reference direction. The distance value is called
 + * the radial coordinate, or <em>radius</em>, and the angle is called the angular coordinate,
 + * or <em>azimuth</em>. This class follows the standard
 + * mathematical convention of using the positive x-axis as the reference
 + * direction and measuring positive angles counter-clockwise, toward the
 + * positive y-axis. The origin is used as the reference point. Polar coordinate
 + * are related to Cartesian coordinates as follows:
 + * <pre>
 + * x = r * cos(&theta;)
 + * y = r * sin(&theta;)
 + *
 + * r = &radic;(x<sup>2</sup> + y<sup>2</sup>)
 + * &theta; = atan2(y, x)
 + * </pre>
 + * where <em>r</em> is the radius and <em>&theta;</em> is the azimuth of the polar coordinates.
 + * </p>
 + * <p>In order to ensure the uniqueness of coordinate sets, coordinate values
 + * are normalized so that {@code radius} is in the range {@code [0, +Infinity)}
 + * and {@code azimuth} is in the range {@code [0, 2pi)}.</p>
 + *
 + * @see <a href="https://en.wikipedia.org/wiki/Polar_coordinate_system">Polar Coordinate System</a>
 + */
 +public final class PolarCoordinates implements Spatial, Serializable {
 +
 +    /** Serializable version UID */
 +    private static final long serialVersionUID = 20180630L;
 +
 +    /** Factory object for delegating instance creation. */
-     private static final Coordinates.Factory2D<PolarCoordinates> FACTORY = new Coordinates.Factory2D<PolarCoordinates>() {
++    private static final DoubleFunction2N<PolarCoordinates> FACTORY = new DoubleFunction2N<PolarCoordinates>() {
 +
 +        /** {@inheritDoc} */
 +        @Override
-         public PolarCoordinates create(double a1, double a2) {
-             return new PolarCoordinates(a1, a2);
++        public PolarCoordinates apply(double n1, double n2) {
++            return new PolarCoordinates(n1, n2);
 +        }
 +    };
 +
 +    /** Radius value */
 +    private final double radius;
 +
 +    /** Azimuth angle in radians */
 +    private final double azimuth;
 +
 +    /** Simple constructor. Input values are normalized.
 +     * @param radius Radius value.
 +     * @param azimuth Azimuth angle in radians.
 +     */
 +    private PolarCoordinates(double radius, double azimuth) {
 +        if (radius < 0) {
 +            // negative radius; flip the angles
 +            radius = Math.abs(radius);
 +            azimuth += Geometry.PI;
 +        }
 +
 +        this.radius = radius;
 +        this.azimuth = normalizeAzimuth(azimuth);;
 +    }
 +
 +    /** Return the radius value. The value will be greater than or equal to 0.
 +     * @return radius value
 +     */
 +    public double getRadius() {
 +        return radius;
 +    }
 +
 +    /** Return the azimuth angle in radians. The value will be
 +     * in the range {@code [0, 2pi)}.
 +     * @return azimuth value in radians.
 +     */
 +    public double getAzimuth() {
 +        return azimuth;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override
 +    public int getDimension() {
 +        return 2;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override
 +    public boolean isNaN() {
 +        return Double.isNaN(radius) || Double.isNaN(azimuth);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override
 +    public boolean isInfinite() {
 +        return !isNaN() && (Double.isInfinite(radius) || Double.isInfinite(azimuth));
 +    }
 +
-     /** Convert this set of polar coordinates to Cartesian coordinates.
-      * The Cartesian coordinates are computed and passed to the given
-      * factory instance. The factory's return value is returned.
-      * @param factory Factory instance that will be passed the computed Cartesian coordinates
-      * @return the value returned by the given factory when passed Cartesian
-      *      coordinates equivalent to this set of polar coordinates.
-      */
-     public <T> T toCartesian(final Coordinates.Factory2D<T> factory) {
-         return toCartesian(radius, azimuth, factory);
-     }
- 
 +    /** Convert this set of polar coordinates to a 2-dimensional
 +     * vector.
 +     * @return A 2-dimensional vector with an equivalent set of
 +     *      coordinates.
 +     */
 +    public Vector2D toVector() {
-         return toCartesian(Vector2D.getFactory());
++        return toCartesian(radius, azimuth, Vector2D.FACTORY);
 +    }
 +
 +    /** Convert this set of polar coordinates to a 2-dimensional
 +     * point.
 +     * @return A 2-dimensional point with an equivalent set of
 +     *      coordinates.
 +     */
 +    public Point2D toPoint() {
-         return toCartesian(Point2D.getFactory());
++        return toCartesian(radius, azimuth, Point2D.FACTORY);
 +    }
 +
 +    /** Get a hashCode for this set of polar coordinates.
 +     * <p>All NaN values have the same hash code.</p>
 +     *
 +     * @return a hash code value for this object
 +     */
 +    @Override
 +    public int hashCode() {
 +        if (isNaN()) {
 +            return 191;
 +        }
 +        return 449 * (76 * Double.hashCode(radius) + Double.hashCode(azimuth));
 +    }
 +
 +    /** Test for the equality of two sets of polar coordinates.
 +     * <p>
 +     * If all values of two sets of coordinates are exactly the same, and none are
 +     * <code>Double.NaN</code>, the two sets are considered to be equal.
 +     * </p>
 +     * <p>
 +     * <code>NaN</code> values are considered to globally affect the coordinates
 +     * and be equal to each other - i.e, if either (or all) values of the
 +     * coordinate set are equal to <code>Double.NaN</code>, the set is equal to
 +     * {@link #NaN}.
 +     * </p>
 +     *
 +     * @param other Object to test for equality to this
 +     * @return true if two PolarCoordinates objects are equal, false if
 +     *         object is null, not an instance of PolarCoordinates, or
 +     *         not equal to this PolarCoordinates instance
 +     *
 +     */
 +    @Override
 +    public boolean equals(Object other) {
 +        if (this == other) {
 +            return true;
 +        }
 +
 +        if (other instanceof PolarCoordinates) {
 +            final PolarCoordinates rhs = (PolarCoordinates) other;
 +            if (rhs.isNaN()) {
 +                return this.isNaN();
 +            }
 +
 +            return (radius == rhs.radius) && (azimuth == rhs.azimuth);
 +        }
 +        return false;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override
 +    public String toString() {
-         return SimpleCoordinateFormat.getPointFormat().format(radius, azimuth);
++        return SimpleTupleFormat.getDefault().format(radius, azimuth);
 +    }
 +
 +    /** Return a new instance with the given polar coordinate values.
 +     * The values are normalized so that {@code radius} lies in the range {@code [0, +Infinity)}
 +     * and {@code azimuth} in the range {@code [0, 2pi)}.
 +     * @param radius Radius value.
 +     * @param azimuth Azimuth angle in radians.
 +     * @return new {@link PolarCoordinates} instance
 +     */
 +    public static PolarCoordinates of(double radius, double azimuth) {
 +        return new PolarCoordinates(radius, azimuth);
 +    }
 +
 +    /** Convert the given Cartesian coordinates to polar form.
 +     * @param x X coordinate value
 +     * @param y Y coordinate value
 +     * @return polar coordinates equivalent to the given Cartesian coordinates
 +     */
 +    public static PolarCoordinates ofCartesian(final double x, final double y) {
 +        final double azimuth = Math.atan2(y, x);
 +        final double radius = Math.hypot(x, y);
 +
 +        return new PolarCoordinates(radius, azimuth);
 +    }
 +
 +    /** Parse the given string and return a new polar coordinates instance. The parsed
 +     * coordinates are normalized as in the {@link #of(double, double)} method. The expected string
 +     * format is the same as that returned by {@link #toString()}.
 +     * @param input the string to parse
 +     * @return new {@link PolarCoordinates} instance
 +     * @throws IllegalArgumentException if the string format is invalid.
 +     */
 +    public static PolarCoordinates parse(String input) {
-         return SimpleCoordinateFormat.getPointFormat().parse(input, FACTORY);
++        return SimpleTupleFormat.getDefault().parse(input, FACTORY);
 +    }
 +
 +    /** Normalize an azimuth value to be within the range {@code [0, 2pi)}.
 +     * @param azimuth azimuth value in radians
 +     * @return equivalent azimuth value in the range {@code [0, 2pi)}.
 +     */
 +    public static double normalizeAzimuth(double azimuth) {
 +        if (Double.isFinite(azimuth) && (azimuth < 0.0 || azimuth >= Geometry.TWO_PI)) {
 +            azimuth = PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(azimuth);
 +
 +            // azimuth is now in the range [0, 2pi] but we want it to be in the range
 +            // [0, 2pi) in order to have completely unique coordinates
 +            if (azimuth >= Geometry.TWO_PI) {
 +                azimuth -= Geometry.TWO_PI;
 +            }
 +        }
 +
 +        return azimuth;
 +    }
 +
-     /** Convert the given set of polar coordinates to Cartesian coordinates.
-      * The Cartesian coordinates are computed and passed to the given
++    /** Package private method to convert the given set of polar coordinates to
++     * Cartesian coordinates. The Cartesian coordinates are computed and passed to the given
 +     * factory instance. The factory's return value is returned.
 +     * @param radius Radius value
 +     * @param azimuth Azimuth value in radians
 +     * @param factory Factory instance that will be passed the computed Cartesian coordinates
 +     * @param <T> Type returned by the factory
 +     * @return the value returned by the factory when passed Cartesian
 +     *      coordinates equivalent to the given set of polar coordinates.
 +     */
-     public static <T> T toCartesian(final double radius, final double azimuth, final Coordinates.Factory2D<T> factory) {
++    static <T> T toCartesian(final double radius, final double azimuth, final DoubleFunction2N<T> factory) {
 +        final double x = radius * Math.cos(azimuth);
 +        final double y = radius * Math.sin(azimuth);
 +
-         return factory.create(x, y);
-     }
- 
-     /** Return a factory object for creating new {@link PolarCoordinates} instances.
-      * @return factory object for creating new instances.
-      */
-     public static Coordinates.Factory2D<PolarCoordinates> getFactory() {
-         return FACTORY;
++        return factory.apply(x, y);
 +    }
 +}
diff --cc commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
index e2e2e56,4b435cc..6d8f10e
--- 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
@@@ -60,8 -60,8 +60,8 @@@ public final class Vector2D extends Car
      /** Error message when norms are zero. */
      private static final String ZERO_NORM_MSG = "Norm is zero";
  
--    /** Factory for delegating instance creation. */
-     private static Coordinates.Factory2D<Vector2D> FACTORY = new Coordinates.Factory2D<Vector2D>() {
 -    private static DoubleFunction2N<Vector2D> FACTORY = new DoubleFunction2N<Vector2D>() {
++    /** Package private factory for delegating instance creation. */
++    static DoubleFunction2N<Vector2D> FACTORY = new DoubleFunction2N<Vector2D>() {
  
          /** {@inheritDoc} */
          @Override
@@@ -377,15 -371,6 +371,15 @@@
          return new Vector2D(v[0], v[1]);
      }
  
 +    /** Return a vector with coordinates equivalent to the given set of polar coordinates.
 +     * @param radius The polar coordinate radius value.
 +     * @param azimuth The polar coordinate azimuth angle in radians.
 +     * @return vector instance with coordinates equivalent to the given polar coordinates.
 +     */
 +    public static Vector2D ofPolar(final double radius, final double azimuth) {
-         return PolarCoordinates.toCartesian(radius, azimuth, getFactory());
++        return PolarCoordinates.toCartesian(radius, azimuth, Vector2D.FACTORY);
 +    }
 +
      /** Parses the given string and returns a new vector instance. The expected string
       * format is the same as that returned by {@link #toString()}.
       * @param str the string to parse
diff --cc commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
index 6326f5e,aadfe73..6e075b9
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
@@@ -1,6 -1,7 +1,8 @@@
  package org.apache.commons.geometry.euclidean.threed;
  
+ import java.util.regex.Pattern;
+ 
 +import org.apache.commons.geometry.core.Geometry;
  import org.junit.Assert;
  import org.junit.Test;
  
@@@ -96,12 -75,20 +98,26 @@@ public class Cartesian3DTest 
          Assert.assertFalse(new StubCartesian3D(0, Double.NaN, Double.POSITIVE_INFINITY).isInfinite());
      }
  
+     @Test
+     public void testToString() {
+         // arrange
+         StubCartesian3D c = new StubCartesian3D(1, 2, 3);
+         Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}, 3.{0,2}\\)");
+ 
+         // act
+         String str = c.toString();
+ 
+         // assert
+         Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                     pattern.matcher(str).matches());
+     }
+ 
 +    private void checkSpherical(SphericalCoordinates c, double radius, double azimuth, double polar) {
 +        Assert.assertEquals(radius, c.getRadius(), TEST_TOLERANCE);
 +        Assert.assertEquals(azimuth, c.getAzimuth(), TEST_TOLERANCE);
 +        Assert.assertEquals(polar, c.getPolar(), TEST_TOLERANCE);
 +    }
 +
      private static class StubCartesian3D extends Cartesian3D {
          private static final long serialVersionUID = 1L;
  
diff --cc commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
index 770cf25,62218a6..b76cbda
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
@@@ -18,8 -18,6 +18,7 @@@ package org.apache.commons.geometry.euc
  
  import java.util.regex.Pattern;
  
 +import org.apache.commons.geometry.core.Geometry;
- import org.apache.commons.geometry.core.util.Coordinates;
  import org.apache.commons.numbers.core.Precision;
  import org.junit.Assert;
  import org.junit.Test;
@@@ -244,37 -242,6 +243,27 @@@ public class Point3DTest 
      }
  
      @Test
 +    public void testOfSpherical() {
-      // arrange
++        // arrange
 +        double sqrt3 = Math.sqrt(3);
 +
 +        // act/assert
 +        checkPoint(Point3D.ofSpherical(0, 0, 0), 0, 0, 0);
 +
 +        checkPoint(Point3D.ofSpherical(1, 0, Geometry.HALF_PI), 1, 0, 0);
 +        checkPoint(Point3D.ofSpherical(1, Geometry.PI, Geometry.HALF_PI), -1, 0, 0);
 +
 +        checkPoint(Point3D.ofSpherical(2, Geometry.HALF_PI, Geometry.HALF_PI), 0, 2, 0);
 +        checkPoint(Point3D.ofSpherical(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI), 0, -2, 0);
 +
 +        checkPoint(Point3D.ofSpherical(3, 0, 0), 0, 0, 3);
 +        checkPoint(Point3D.ofSpherical(3, 0, Geometry.PI), 0, 0, -3);
 +
 +        checkPoint(Point3D.ofSpherical(sqrt3, 0.25 * Geometry.PI, Math.acos(1 / sqrt3)), 1, 1, 1);
 +        checkPoint(Point3D.ofSpherical(sqrt3, -0.75 * Geometry.PI, Math.acos(-1 / sqrt3)), -1, -1, -1);
 +    }
 +
 +    @Test
-     public void testGetFactory() {
-         // act
-         Coordinates.Factory3D<Point3D> factory = Point3D.getFactory();
- 
-         // assert
-         checkPoint(factory.create(1, 2, 3), 1, 2, 3);
-         checkPoint(factory.create(-1, -2, -3), -1, -2, -3);
-     }
- 
-     @Test
      public void testVectorCombination1() {
          // arrange
          Point3D p1 = Point3D.of(1, 2, 3);
diff --cc commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
index 11057ed,0000000..ce4f03f
mode 100644,000000..100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
@@@ -1,430 -1,0 +1,399 @@@
 +/*
 + * 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.euclidean.threed;
 +
 +import java.util.regex.Pattern;
 +
 +import org.apache.commons.geometry.core.Geometry;
- import org.apache.commons.geometry.core.util.Coordinates;
++import org.apache.commons.geometry.core.internal.DoubleFunction3N;
 +import org.junit.Assert;
 +import org.junit.Test;
 +
 +public class SphericalCoordinatesTest {
 +
 +    private static final double EPS = 1e-10;
 +
 +    private static final double QUARTER_PI = 0.25 * Geometry.PI;
 +    private static final double MINUS_QUARTER_PI = -0.25 * Geometry.PI;
 +    private static final double THREE_QUARTER_PI = 0.75 * Geometry.PI;
 +    private static final double MINUS_THREE_QUARTER_PI = -0.75 * Geometry.PI;
 +
 +    @Test
 +    public void testOf() {
 +        // act/assert
 +        checkSpherical(SphericalCoordinates.of(0, 0, 0), 0, 0, 0);
 +        checkSpherical(SphericalCoordinates.of(0.1, 0.2, 0.3), 0.1, 0.2, 0.3);
 +
 +        checkSpherical(SphericalCoordinates.of(1, Geometry.HALF_PI, Geometry.PI),
 +                1, Geometry.HALF_PI, Geometry.PI);
 +        checkSpherical(SphericalCoordinates.of(1, Geometry.MINUS_HALF_PI, Geometry.HALF_PI),
 +                1, Geometry.THREE_HALVES_PI, Geometry.HALF_PI);
 +    }
 +
 +    @Test
 +    public void testOf_normalizesAzimuthAngle() {
 +        // act/assert
 +        checkSpherical(SphericalCoordinates.of(2, Geometry.TWO_PI, 0), 2, 0, 0);
 +        checkSpherical(SphericalCoordinates.of(2, Geometry.HALF_PI + Geometry.TWO_PI, 0), 2, Geometry.HALF_PI, 0);
 +        checkSpherical(SphericalCoordinates.of(2, -Geometry.PI, 0), 2, Geometry.PI, 0);
 +        checkSpherical(SphericalCoordinates.of(2, Geometry.THREE_HALVES_PI, 0), 2, Geometry.THREE_HALVES_PI, 0);
 +    }
 +
 +    @Test
 +    public void testOf_normalizesPolarAngle() {
 +        // act/assert
 +        checkSpherical(SphericalCoordinates.of(1, 0, 0), 1, 0, 0);
 +
 +        checkSpherical(SphericalCoordinates.of(1, 0, QUARTER_PI), 1, 0, QUARTER_PI);
 +        checkSpherical(SphericalCoordinates.of(1, 0, MINUS_QUARTER_PI), 1, 0, QUARTER_PI);
 +
 +        checkSpherical(SphericalCoordinates.of(1, 0, Geometry.HALF_PI), 1, 0, Geometry.HALF_PI);
 +        checkSpherical(SphericalCoordinates.of(1, 0, Geometry.MINUS_HALF_PI), 1, 0, Geometry.HALF_PI);
 +
 +        checkSpherical(SphericalCoordinates.of(1, 0, THREE_QUARTER_PI), 1, 0, THREE_QUARTER_PI);
 +        checkSpherical(SphericalCoordinates.of(1, 0, MINUS_THREE_QUARTER_PI), 1, 0, THREE_QUARTER_PI);
 +
 +        checkSpherical(SphericalCoordinates.of(1, 0, Geometry.TWO_PI), 1, 0, 0);
 +        checkSpherical(SphericalCoordinates.of(1, 0, Geometry.MINUS_TWO_PI), 1, 0, 0);
 +    }
 +
 +    @Test
 +    public void testOf_angleWrapAround() {
 +        // act/assert
 +        checkOfWithAngleWrapAround(1, 0, 0);
 +        checkOfWithAngleWrapAround(1, QUARTER_PI, QUARTER_PI);
 +        checkOfWithAngleWrapAround(1, Geometry.HALF_PI, Geometry.HALF_PI);
 +        checkOfWithAngleWrapAround(1, THREE_QUARTER_PI, THREE_QUARTER_PI);
 +        checkOfWithAngleWrapAround(1, Geometry.PI, Geometry.PI);
 +    }
 +
 +    private void checkOfWithAngleWrapAround(double radius, double azimuth, double polar) {
 +        for (int i=-4; i<=4; ++i) {
 +            checkSpherical(
 +                    SphericalCoordinates.of(radius, azimuth + (i * Geometry.TWO_PI), polar + (-i * Geometry.TWO_PI)),
 +                    radius, azimuth, polar);
 +        }
 +    }
 +
 +    @Test
 +    public void testOf_negativeRadius() {
 +        // act/assert
 +        checkSpherical(SphericalCoordinates.of(-2, 0, 0), 2, Geometry.PI, Geometry.PI);
 +        checkSpherical(SphericalCoordinates.of(-2, Geometry.PI, Geometry.PI), 2, 0, 0);
 +
 +        checkSpherical(SphericalCoordinates.of(-3, Geometry.HALF_PI, QUARTER_PI), 3, Geometry.THREE_HALVES_PI, THREE_QUARTER_PI);
 +        checkSpherical(SphericalCoordinates.of(-3, Geometry.MINUS_HALF_PI, THREE_QUARTER_PI), 3, Geometry.HALF_PI, QUARTER_PI);
 +
 +        checkSpherical(SphericalCoordinates.of(-4, QUARTER_PI, Geometry.HALF_PI), 4, Geometry.PI + QUARTER_PI, Geometry.HALF_PI);
 +        checkSpherical(SphericalCoordinates.of(-4, MINUS_THREE_QUARTER_PI, Geometry.HALF_PI), 4, QUARTER_PI, Geometry.HALF_PI);
 +    }
 +
 +    @Test
 +    public void testOf_NaNAndInfinite() {
 +        // act/assert
 +        checkSpherical(SphericalCoordinates.of(Double.NaN, Double.NaN, Double.NaN),
 +                Double.NaN, Double.NaN, Double.NaN);
 +        checkSpherical(SphericalCoordinates.of(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY),
 +                Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
 +        checkSpherical(SphericalCoordinates.of(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY),
 +                Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
 +    }
 +
 +    @Test
 +    public void testOfCartesian() {
 +        // arrange
 +        double sqrt3 = Math.sqrt(3);
 +
 +        // act/assert
 +        checkSpherical(SphericalCoordinates.ofCartesian(0, 0, 0), 0, 0, 0);
 +
 +        checkSpherical(SphericalCoordinates.ofCartesian(0.1, 0, 0), 0.1, 0, Geometry.HALF_PI);
 +        checkSpherical(SphericalCoordinates.ofCartesian(-0.1, 0, 0), 0.1, Geometry.PI, Geometry.HALF_PI);
 +
 +        checkSpherical(SphericalCoordinates.ofCartesian(0, 0.1, 0), 0.1, Geometry.HALF_PI, Geometry.HALF_PI);
 +        checkSpherical(SphericalCoordinates.ofCartesian(0, -0.1, 0), 0.1, Geometry.THREE_HALVES_PI, Geometry.HALF_PI);
 +
 +        checkSpherical(SphericalCoordinates.ofCartesian(0, 0, 0.1), 0.1, 0, 0);
 +        checkSpherical(SphericalCoordinates.ofCartesian(0, 0, -0.1), 0.1, 0, Geometry.PI);
 +
 +        checkSpherical(SphericalCoordinates.ofCartesian(1, 1, 1), sqrt3, QUARTER_PI, Math.acos(1 / sqrt3));
 +        checkSpherical(SphericalCoordinates.ofCartesian(-1, -1, -1), sqrt3, 1.25 * Geometry.PI, Math.acos(-1 / sqrt3));
 +    }
 +
 +    @Test
 +    public void testToPoint() {
 +        // arrange
 +        double sqrt3 = Math.sqrt(3);
 +
 +        // act/assert
 +        checkPoint(SphericalCoordinates.of(0, 0, 0).toPoint(), 0, 0, 0);
 +
 +        checkPoint(SphericalCoordinates.of(1, 0, Geometry.HALF_PI).toPoint(), 1, 0, 0);
 +        checkPoint(SphericalCoordinates.of(1, Geometry.PI, Geometry.HALF_PI).toPoint(), -1, 0, 0);
 +
 +        checkPoint(SphericalCoordinates.of(2, Geometry.HALF_PI, Geometry.HALF_PI).toPoint(), 0, 2, 0);
 +        checkPoint(SphericalCoordinates.of(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI).toPoint(), 0, -2, 0);
 +
 +        checkPoint(SphericalCoordinates.of(3, 0, 0).toPoint(), 0, 0, 3);
 +        checkPoint(SphericalCoordinates.of(3, 0, Geometry.PI).toPoint(), 0, 0, -3);
 +
 +        checkPoint(SphericalCoordinates.of(Math.sqrt(3), QUARTER_PI, Math.acos(1 / sqrt3)).toPoint(), 1, 1, 1);
 +        checkPoint(SphericalCoordinates.of(Math.sqrt(3), MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3)).toPoint(), -1, -1, -1);
 +    }
 +
 +    @Test
 +    public void testToVector() {
 +        // arrange
 +        double sqrt3 = Math.sqrt(3);
 +
 +        // act/assert
 +        checkVector(SphericalCoordinates.of(0, 0, 0).toVector(), 0, 0, 0);
 +
 +        checkVector(SphericalCoordinates.of(1, 0, Geometry.HALF_PI).toVector(), 1, 0, 0);
 +        checkVector(SphericalCoordinates.of(1, Geometry.PI, Geometry.HALF_PI).toVector(), -1, 0, 0);
 +
 +        checkVector(SphericalCoordinates.of(2, Geometry.HALF_PI, Geometry.HALF_PI).toVector(), 0, 2, 0);
 +        checkVector(SphericalCoordinates.of(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI).toVector(), 0, -2, 0);
 +
 +        checkVector(SphericalCoordinates.of(3, 0, 0).toVector(), 0, 0, 3);
 +        checkVector(SphericalCoordinates.of(3, 0, Geometry.PI).toVector(), 0, 0, -3);
 +
 +        checkVector(SphericalCoordinates.of(sqrt3, QUARTER_PI, Math.acos(1 / sqrt3)).toVector(), 1, 1, 1);
 +        checkVector(SphericalCoordinates.of(sqrt3, MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3)).toVector(), -1, -1, -1);
 +    }
 +
 +    @Test
-     public void testToCartesian_callback() {
-         // arrange
-         double sqrt3 = Math.sqrt(3);
-         Coordinates.Factory3D<Point3D> factory = Point3D.getFactory();
- 
-         // act/assert
-         checkPoint(SphericalCoordinates.of(0, 0, 0).toCartesian(factory), 0, 0, 0);
- 
-         checkPoint(SphericalCoordinates.of(1, 0, Geometry.HALF_PI).toCartesian(factory), 1, 0, 0);
-         checkPoint(SphericalCoordinates.of(1, Geometry.PI, Geometry.HALF_PI).toCartesian(factory), -1, 0, 0);
- 
-         checkPoint(SphericalCoordinates.of(2, Geometry.HALF_PI, Geometry.HALF_PI).toCartesian(factory), 0, 2, 0);
-         checkPoint(SphericalCoordinates.of(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI).toCartesian(factory), 0, -2, 0);
- 
-         checkPoint(SphericalCoordinates.of(3, 0, 0).toCartesian(factory), 0, 0, 3);
-         checkPoint(SphericalCoordinates.of(3, 0, Geometry.PI).toCartesian(factory), 0, 0, -3);
- 
-         checkPoint(SphericalCoordinates.of(Math.sqrt(3), QUARTER_PI, Math.acos(1 / sqrt3)).toCartesian(factory), 1, 1, 1);
-         checkPoint(SphericalCoordinates.of(Math.sqrt(3), MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3)).toCartesian(factory), -1, -1, -1);
-     }
- 
-     @Test
 +    public void testToCartesian_static() {
 +        // arrange
 +        double sqrt3 = Math.sqrt(3);
-         Coordinates.Factory3D<Point3D> factory = Point3D.getFactory();
++        DoubleFunction3N<Point3D> factory = Point3D.FACTORY;
 +
 +        // act/assert
 +        checkPoint(SphericalCoordinates.toCartesian(0, 0, 0, factory), 0, 0, 0);
 +
 +        checkPoint(SphericalCoordinates.toCartesian(1, 0, Geometry.HALF_PI, factory), 1, 0, 0);
 +        checkPoint(SphericalCoordinates.toCartesian(1, Geometry.PI, Geometry.HALF_PI, factory), -1, 0, 0);
 +
 +        checkPoint(SphericalCoordinates.toCartesian(2, Geometry.HALF_PI, Geometry.HALF_PI, factory), 0, 2, 0);
 +        checkPoint(SphericalCoordinates.toCartesian(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI, factory), 0, -2, 0);
 +
 +        checkPoint(SphericalCoordinates.toCartesian(3, 0, 0, factory), 0, 0, 3);
 +        checkPoint(SphericalCoordinates.toCartesian(3, 0, Geometry.PI, factory), 0, 0, -3);
 +
 +        checkPoint(SphericalCoordinates.toCartesian(Math.sqrt(3), QUARTER_PI, Math.acos(1 / sqrt3), factory), 1, 1, 1);
 +        checkPoint(SphericalCoordinates.toCartesian(Math.sqrt(3), MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3), factory), -1, -1, -1);
 +    }
 +
 +    @Test
 +    public void testGetDimension() {
 +        // arrange
 +        SphericalCoordinates s = SphericalCoordinates.of(0, 0, 0);
 +
 +        // act/assert
 +        Assert.assertEquals(3, s.getDimension());
 +    }
 +
 +    @Test
 +    public void testNaN() {
 +        // act/assert
 +        Assert.assertTrue(SphericalCoordinates.of(0, 0, Double.NaN).isNaN());
 +        Assert.assertTrue(SphericalCoordinates.of(0, Double.NaN, 0).isNaN());
 +        Assert.assertTrue(SphericalCoordinates.of(Double.NaN, 0, 0).isNaN());
 +
 +        Assert.assertFalse(SphericalCoordinates.of(1, 1, 1).isNaN());
 +        Assert.assertFalse(SphericalCoordinates.of(1, 1, Double.NEGATIVE_INFINITY).isNaN());
 +        Assert.assertFalse(SphericalCoordinates.of(1, Double.POSITIVE_INFINITY, 1).isNaN());
 +        Assert.assertFalse(SphericalCoordinates.of(Double.NEGATIVE_INFINITY, 1, 1).isNaN());
 +    }
 +
 +    @Test
 +    public void testInfinite() {
 +        // act/assert
 +        Assert.assertTrue(SphericalCoordinates.of(0, 0, Double.NEGATIVE_INFINITY).isInfinite());
 +        Assert.assertTrue(SphericalCoordinates.of(0, Double.NEGATIVE_INFINITY, 0).isInfinite());
 +        Assert.assertTrue(SphericalCoordinates.of(Double.NEGATIVE_INFINITY, 0, 0).isInfinite());
 +        Assert.assertTrue(SphericalCoordinates.of(0, 0, Double.POSITIVE_INFINITY).isInfinite());
 +        Assert.assertTrue(SphericalCoordinates.of(0, Double.POSITIVE_INFINITY, 0).isInfinite());
 +        Assert.assertTrue(SphericalCoordinates.of(Double.POSITIVE_INFINITY, 0, 0).isInfinite());
 +
 +        Assert.assertFalse(SphericalCoordinates.of(1, 1, 1).isInfinite());
 +        Assert.assertFalse(SphericalCoordinates.of(0, 0, Double.NaN).isInfinite());
 +        Assert.assertFalse(SphericalCoordinates.of(0, Double.NEGATIVE_INFINITY, Double.NaN).isInfinite());
 +        Assert.assertFalse(SphericalCoordinates.of(Double.NaN, 0, Double.NEGATIVE_INFINITY).isInfinite());
 +        Assert.assertFalse(SphericalCoordinates.of(Double.POSITIVE_INFINITY, Double.NaN, 0).isInfinite());
 +        Assert.assertFalse(SphericalCoordinates.of(0, Double.NaN, Double.POSITIVE_INFINITY).isInfinite());
 +    }
 +
 +    @Test
 +    public void testHashCode() {
 +        // arrange
 +        SphericalCoordinates a = SphericalCoordinates.of(1, 2, 3);
 +        SphericalCoordinates b = SphericalCoordinates.of(10, 2, 3);
 +        SphericalCoordinates c = SphericalCoordinates.of(1, 20, 3);
 +        SphericalCoordinates d = SphericalCoordinates.of(1, 2, 30);
 +
 +        SphericalCoordinates e = SphericalCoordinates.of(1, 2, 3);
 +
 +        // act/assert
 +        Assert.assertEquals(a.hashCode(), a.hashCode());
 +        Assert.assertEquals(a.hashCode(), e.hashCode());
 +
 +        Assert.assertNotEquals(a.hashCode(), b.hashCode());
 +        Assert.assertNotEquals(a.hashCode(), c.hashCode());
 +        Assert.assertNotEquals(a.hashCode(), d.hashCode());
 +    }
 +
 +    @Test
 +    public void testHashCode_NaNInstancesHaveSameHashCode() {
 +        // arrange
 +        SphericalCoordinates a = SphericalCoordinates.of(1, 2, Double.NaN);
 +        SphericalCoordinates b = SphericalCoordinates.of(1, Double.NaN, 3);
 +        SphericalCoordinates c = SphericalCoordinates.of(Double.NaN, 2, 3);
 +
 +        // act/assert
 +        Assert.assertEquals(a.hashCode(), b.hashCode());
 +        Assert.assertEquals(b.hashCode(), c.hashCode());
 +    }
 +
 +    @Test
 +    public void testEquals() {
 +        // arrange
 +        SphericalCoordinates a = SphericalCoordinates.of(1, 2, 3);
 +        SphericalCoordinates b = SphericalCoordinates.of(10, 2, 3);
 +        SphericalCoordinates c = SphericalCoordinates.of(1, 20, 3);
 +        SphericalCoordinates d = SphericalCoordinates.of(1, 2, 30);
 +
 +        SphericalCoordinates e = SphericalCoordinates.of(1, 2, 3);
 +
 +        // act/assert
 +        Assert.assertFalse(a.equals(null));
 +        Assert.assertFalse(a.equals(new Object()));
 +
 +        Assert.assertTrue(a.equals(a));
 +        Assert.assertTrue(a.equals(e));
 +
 +        Assert.assertFalse(a.equals(b));
 +        Assert.assertFalse(a.equals(c));
 +        Assert.assertFalse(a.equals(d));
 +    }
 +
 +    @Test
 +    public void testEquals_NaNInstancesEqual() {
 +        // arrange
 +        SphericalCoordinates a = SphericalCoordinates.of(1, 2, Double.NaN);
 +        SphericalCoordinates b = SphericalCoordinates.of(1, Double.NaN, 3);
 +        SphericalCoordinates c = SphericalCoordinates.of(Double.NaN, 2, 3);
 +
 +        // act/assert
 +        Assert.assertTrue(a.equals(b));
 +        Assert.assertTrue(b.equals(c));
 +    }
 +
 +    @Test
 +    public void testToString() {
 +        // arrange
 +        SphericalCoordinates sph = SphericalCoordinates.of(1, 2, 3);
 +        Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}, 3.{0,2}\\)");
 +
 +        // act
 +        String str = sph.toString();;
 +
 +        // assert
 +        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
 +                    pattern.matcher(str).matches());
 +    }
 +
 +    @Test
 +    public void testParse() {
 +        // act/assert
 +        checkSpherical(SphericalCoordinates.parse("(1, 2, 3)"), 1, 2, 3);
 +        checkSpherical(SphericalCoordinates.parse("(  -2.0 , 1 , -5e-1)"), 2, 1 + Geometry.PI, Geometry.PI - 0.5);
 +        checkSpherical(SphericalCoordinates.parse("(NaN,Infinity,-Infinity)"), Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
 +    }
 +
 +    @Test(expected = IllegalArgumentException.class)
 +    public void testParse_failure() {
 +        // act/assert
 +        SphericalCoordinates.parse("abc");
 +    }
 +
 +    @Test
 +    public void testNormalizeAzimuth() {
 +        // act/assert
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(0), 0.0, EPS);
 +
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.HALF_PI), Geometry.HALF_PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.PI), Geometry.PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.THREE_HALVES_PI), Geometry.THREE_HALVES_PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.TWO_PI), 0.0, EPS);
 +
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.MINUS_HALF_PI), Geometry.THREE_HALVES_PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(-Geometry.PI), Geometry.PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(-Geometry.PI - Geometry.HALF_PI), Geometry.HALF_PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(-Geometry.TWO_PI), 0.0, EPS);
 +    }
 +
 +    @Test
 +    public void testNormalizeAzimuth_NaNAndInfinite() {
 +        // act/assert
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Double.NaN), Double.NaN, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Double.NEGATIVE_INFINITY), Double.NEGATIVE_INFINITY, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Double.POSITIVE_INFINITY), Double.POSITIVE_INFINITY, EPS);
 +    }
 +
 +    @Test
 +    public void testNormalizePolar() {
 +        // act/assert
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(0), 0.0, EPS);
 +
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(Geometry.HALF_PI), Geometry.HALF_PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(Geometry.PI), Geometry.PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(Geometry.PI + Geometry.HALF_PI), Geometry.HALF_PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(Geometry.TWO_PI), 0.0, EPS);
 +
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(Geometry.MINUS_HALF_PI), Geometry.HALF_PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(-Geometry.PI), Geometry.PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(-Geometry.PI - Geometry.HALF_PI), Geometry.HALF_PI, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(-Geometry.TWO_PI), 0.0, EPS);
 +    }
 +
 +    @Test
 +    public void testNormalizePolar_NaNAndInfinite() {
 +        // act/assert
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(Double.NaN), Double.NaN, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(Double.NEGATIVE_INFINITY), Double.NEGATIVE_INFINITY, EPS);
 +        Assert.assertEquals(SphericalCoordinates.normalizePolar(Double.POSITIVE_INFINITY), Double.POSITIVE_INFINITY, EPS);
 +    }
 +
-     @Test
-     public void testGetFactory() {
-         // act
-         Coordinates.Factory3D<SphericalCoordinates> factory = SphericalCoordinates.getFactory();
- 
-         // assert
-         checkSpherical(factory.create(2, 0.5 + Geometry.TWO_PI, 0.1 + Geometry.PI), 2, 0.5, Geometry.PI - 0.1);
-     }
- 
 +    private void checkSpherical(SphericalCoordinates c, double radius, double azimuth, double polar) {
 +        Assert.assertEquals(radius, c.getRadius(), EPS);
 +        Assert.assertEquals(azimuth, c.getAzimuth(), EPS);
 +        Assert.assertEquals(polar, c.getPolar(), EPS);
 +    }
 +
 +    private void checkPoint(Point3D p, double x, double y, double z) {
 +        Assert.assertEquals(x, p.getX(), EPS);
 +        Assert.assertEquals(y, p.getY(), EPS);
 +        Assert.assertEquals(z, p.getZ(), EPS);
 +    }
 +
 +    private void checkVector(Vector3D v, double x, double y, double z) {
 +        Assert.assertEquals(x, v.getX(), EPS);
 +        Assert.assertEquals(y, v.getY(), EPS);
 +        Assert.assertEquals(z, v.getZ(), EPS);
 +    }
 +}
diff --cc commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
index d52d358,c4017bd..f6a1606
--- 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
@@@ -689,37 -688,6 +688,27 @@@ public class Vector3DTest 
      }
  
      @Test
 +    public void testOfSpherical() {
-      // arrange
++        // arrange
 +        double sqrt3 = Math.sqrt(3);
 +
 +        // act/assert
 +        checkVector(Vector3D.ofSpherical(0, 0, 0), 0, 0, 0);
 +
 +        checkVector(Vector3D.ofSpherical(1, 0, Geometry.HALF_PI), 1, 0, 0);
 +        checkVector(Vector3D.ofSpherical(1, Geometry.PI, Geometry.HALF_PI), -1, 0, 0);
 +
 +        checkVector(Vector3D.ofSpherical(2, Geometry.HALF_PI, Geometry.HALF_PI), 0, 2, 0);
 +        checkVector(Vector3D.ofSpherical(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI), 0, -2, 0);
 +
 +        checkVector(Vector3D.ofSpherical(3, 0, 0), 0, 0, 3);
 +        checkVector(Vector3D.ofSpherical(3, 0, Geometry.PI), 0, 0, -3);
 +
 +        checkVector(Vector3D.ofSpherical(sqrt3, 0.25 * Geometry.PI, Math.acos(1 / sqrt3)), 1, 1, 1);
 +        checkVector(Vector3D.ofSpherical(sqrt3, -0.75 * Geometry.PI, Math.acos(-1 / sqrt3)), -1, -1, -1);
 +    }
 +
 +    @Test
-     public void testGetFactory() {
-         // act
-         Coordinates.Factory3D<Vector3D> factory = Vector3D.getFactory();
- 
-         // assert
-         checkVector(factory.create(1, 2, 3), 1, 2, 3);
-         checkVector(factory.create(-1, -2, -3), -1, -2, -3);
-     }
- 
-     @Test
      public void testLinearCombination1() {
          // arrange
          Vector3D p1 = Vector3D.of(1, 2, 3);
diff --cc commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
index 261ad09,e3d5127..5852616
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
@@@ -1,10 -1,11 +1,11 @@@
  package org.apache.commons.geometry.euclidean.twod;
  
+ import java.util.regex.Pattern;
+ 
 +import org.apache.commons.geometry.core.Geometry;
  import org.junit.Assert;
  import org.junit.Test;
  
--
  public class Cartesian2DTest {
  
      private static final double TEST_TOLERANCE = 1e-15;
@@@ -70,37 -71,19 +71,51 @@@
      }
  
      @Test
 +    public void testToPolar() {
 +        // arrange
 +        double sqrt2 = Math.sqrt(2.0);
 +
 +        // act/assert
 +        checkPolar(new StubCartesian2D(0, 0).toPolar(), 0, 0);
 +
 +        checkPolar(new StubCartesian2D(1, 0).toPolar(), 1, 0);
 +        checkPolar(new StubCartesian2D(-1, 0).toPolar(), 1, Geometry.PI);
 +
 +        checkPolar(new StubCartesian2D(0, 2).toPolar(), 2, Geometry.HALF_PI);
 +        checkPolar(new StubCartesian2D(0, -2).toPolar(), 2, Geometry.THREE_HALVES_PI);
 +
 +        checkPolar(new StubCartesian2D(sqrt2, sqrt2).toPolar(), 2, 0.25 * Geometry.PI);
 +        checkPolar(new StubCartesian2D(-sqrt2, sqrt2).toPolar(), 2, 0.75 * Geometry.PI);
 +        checkPolar(new StubCartesian2D(sqrt2, -sqrt2).toPolar(), 2, 1.75 * Geometry.PI);
 +        checkPolar(new StubCartesian2D(-sqrt2, -sqrt2).toPolar(), 2, 1.25 * Geometry.PI);
 +    }
 +
 +    @Test
 +    public void testToPolar_NaNAndInfinite() {
 +        // act/assert
 +        Assert.assertTrue(new StubCartesian2D(Double.NaN, Double.NaN).toPolar().isNaN());
 +        Assert.assertTrue(new StubCartesian2D(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).toPolar().isInfinite());
 +    }
 +
++    @Test
+     public void testToString() {
+         // arrange
+         StubCartesian2D c = new StubCartesian2D(1, 2);
+         Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}\\)");
+ 
+         // act
+         String str = c.toString();
+ 
+         // assert
+         Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                     pattern.matcher(str).matches());
+     }
+ 
 +    private void checkPolar(PolarCoordinates polar, double radius, double azimuth) {
 +        Assert.assertEquals(radius, polar.getRadius(), TEST_TOLERANCE);
 +        Assert.assertEquals(azimuth, polar.getAzimuth(), TEST_TOLERANCE);
 +    }
 +
      private static class StubCartesian2D extends Cartesian2D {
          private static final long serialVersionUID = 1L;
  
diff --cc commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
index f71ce00,e6cf422..69c3aa3
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
@@@ -18,8 -18,6 +18,7 @@@ package org.apache.commons.geometry.euc
  
  import java.util.regex.Pattern;
  
 +import org.apache.commons.geometry.core.Geometry;
- import org.apache.commons.geometry.core.util.Coordinates;
  import org.apache.commons.numbers.core.Precision;
  import org.junit.Assert;
  import org.junit.Test;
@@@ -221,38 -219,6 +220,28 @@@ public class Point2DTest 
      }
  
      @Test
 +    public void testOfPolar() {
 +        // arrange
 +        double eps = 1e-15;
 +        double sqrt2 = Math.sqrt(2.0);
 +
 +        // act/assert
 +        checkPoint(Point2D.ofPolar(0, 0), 0, 0, eps);
 +        checkPoint(Point2D.ofPolar(1, 0), 1, 0, eps);
 +
 +        checkPoint(Point2D.ofPolar(2, Geometry.PI), -2, 0, eps);
 +        checkPoint(Point2D.ofPolar(-2, Geometry.PI), 2, 0, eps);
 +
 +        checkPoint(Point2D.ofPolar(2, Geometry.HALF_PI), 0, 2, eps);
 +        checkPoint(Point2D.ofPolar(-2, Geometry.HALF_PI), 0, -2, eps);
 +
 +        checkPoint(Point2D.ofPolar(2, 0.25 * Geometry.PI), sqrt2, sqrt2, eps);
 +        checkPoint(Point2D.ofPolar(2, 0.75 * Geometry.PI), -sqrt2, sqrt2, eps);
 +        checkPoint(Point2D.ofPolar(2, -0.25 * Geometry.PI), sqrt2, - sqrt2, eps);
 +        checkPoint(Point2D.ofPolar(2, -0.75 * Geometry.PI), -sqrt2, - sqrt2, eps);
 +    }
 +
 +    @Test
-     public void testGetFactory() {
-         // act
-         Coordinates.Factory2D<Point2D> factory = Point2D.getFactory();
- 
-         // assert
-         checkPoint(factory.create(1, 2), 1, 2);
-         checkPoint(factory.create(-1, -2), -1, -2);
-     }
- 
-     @Test
      public void testVectorCombination1() {
          // arrange
          Point2D p1 = Point2D.of(1, 2);
diff --cc commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
index ce74382,0000000..524e7e0
mode 100644,000000..100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
@@@ -1,392 -1,0 +1,362 @@@
 +/*
 + * 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.euclidean.twod;
 +
 +import java.util.regex.Pattern;
 +
 +import org.apache.commons.geometry.core.Geometry;
- import org.apache.commons.geometry.core.util.Coordinates;
++import org.apache.commons.geometry.core.internal.DoubleFunction2N;
 +import org.junit.Assert;
 +import org.junit.Test;
 +
 +public class PolarCoordinatesTest {
 +
 +    private static final double EPS = 1e-10;
 +
 +    @Test
 +    public void testOf() {
 +        // act/assert
 +        checkPolar(PolarCoordinates.of(0, 0), 0, 0);
 +
 +        checkPolar(PolarCoordinates.of(2, 0), 2, 0);
 +        checkPolar(PolarCoordinates.of(2, Geometry.HALF_PI), 2, Geometry.HALF_PI);
 +        checkPolar(PolarCoordinates.of(2, Geometry.PI), 2, Geometry.PI);
 +        checkPolar(PolarCoordinates.of(2, Geometry.MINUS_HALF_PI), 2, Geometry.THREE_HALVES_PI);
 +    }
 +
 +    @Test
 +    public void testOf_unnormalizedAngles() {
 +        // act/assert
 +        checkPolar(PolarCoordinates.of(2, Geometry.TWO_PI), 2, 0);
 +        checkPolar(PolarCoordinates.of(2, Geometry.HALF_PI + Geometry.TWO_PI), 2, Geometry.HALF_PI);
 +        checkPolar(PolarCoordinates.of(2, -Geometry.PI), 2, Geometry.PI);
 +        checkPolar(PolarCoordinates.of(2, -Geometry.PI * 1.5), 2, Geometry.HALF_PI);
 +    }
 +
 +    @Test
 +    public void testOf_azimuthWrapAround() {
 +        // arrange
 +        double delta = 1e-6;
 +
 +        // act/assert
 +        checkAzimuthWrapAround(2, 0);
 +        checkAzimuthWrapAround(2, delta);
 +        checkAzimuthWrapAround(2, Geometry.PI - delta);
 +        checkAzimuthWrapAround(2, Geometry.PI);
 +
 +        checkAzimuthWrapAround(2, Geometry.THREE_HALVES_PI);
 +        checkAzimuthWrapAround(2, Geometry.TWO_PI - delta);
 +    }
 +
 +    private void checkAzimuthWrapAround(double radius, double azimuth) {
 +        checkPolar(PolarCoordinates.of(radius, azimuth), radius, azimuth);
 +
 +        checkPolar(PolarCoordinates.of(radius, azimuth - Geometry.TWO_PI), radius, azimuth);
 +        checkPolar(PolarCoordinates.of(radius, azimuth - (2 * Geometry.TWO_PI)), radius, azimuth);
 +        checkPolar(PolarCoordinates.of(radius, azimuth - (3 * Geometry.TWO_PI)), radius, azimuth);
 +
 +        checkPolar(PolarCoordinates.of(radius, azimuth + Geometry.TWO_PI), radius, azimuth);
 +        checkPolar(PolarCoordinates.of(radius, azimuth + (2 * Geometry.TWO_PI)), radius, azimuth);
 +        checkPolar(PolarCoordinates.of(radius, azimuth + (3 * Geometry.TWO_PI)), radius, azimuth);
 +    }
 +
 +    @Test
 +    public void testOf_negativeRadius() {
 +        // act/assert
 +        checkPolar(PolarCoordinates.of(-1, 0), 1, Geometry.PI);
 +        checkPolar(PolarCoordinates.of(-1e-6, Geometry.HALF_PI), 1e-6, Geometry.THREE_HALVES_PI);
 +        checkPolar(PolarCoordinates.of(-2, Geometry.PI), 2, 0);
 +        checkPolar(PolarCoordinates.of(-3, Geometry.MINUS_HALF_PI), 3, Geometry.HALF_PI);
 +    }
 +
 +    @Test
 +    public void testOf_NaNAndInfinite() {
 +        // act/assert
 +        checkPolar(PolarCoordinates.of(Double.NaN, 0), Double.NaN, 0);
 +        checkPolar(PolarCoordinates.of(Double.NEGATIVE_INFINITY, 0), Double.POSITIVE_INFINITY, Geometry.PI);
 +        checkPolar(PolarCoordinates.of(Double.POSITIVE_INFINITY, 0), Double.POSITIVE_INFINITY, 0);
 +
 +        checkPolar(PolarCoordinates.of(0, Double.NaN), 0, Double.NaN);
 +        checkPolar(PolarCoordinates.of(0, Double.NEGATIVE_INFINITY), 0, Double.NEGATIVE_INFINITY);
 +        checkPolar(PolarCoordinates.of(0, Double.POSITIVE_INFINITY), 0, Double.POSITIVE_INFINITY);
 +    }
 +
 +    @Test
 +    public void testOfCartesian() {
 +        // arrange
 +        double sqrt2 = Math.sqrt(2);
 +
 +        // act/assert
 +        checkPolar(PolarCoordinates.ofCartesian(0, 0), 0, 0);
 +
 +        checkPolar(PolarCoordinates.ofCartesian(1, 0), 1, 0);
 +        checkPolar(PolarCoordinates.ofCartesian(1, 1), sqrt2, 0.25 * Geometry.PI);
 +        checkPolar(PolarCoordinates.ofCartesian(0, 1), 1, Geometry.HALF_PI);
 +
 +        checkPolar(PolarCoordinates.ofCartesian(-1, 1), sqrt2, 0.75 * Geometry.PI);
 +        checkPolar(PolarCoordinates.ofCartesian(-1, 0), 1, Geometry.PI);
 +        checkPolar(PolarCoordinates.ofCartesian(-1, -1), sqrt2, 1.25 * Geometry.PI);
 +
 +        checkPolar(PolarCoordinates.ofCartesian(0, -1), 1, 1.5 * Geometry.PI);
 +        checkPolar(PolarCoordinates.ofCartesian(1, -1), sqrt2, 1.75 * Geometry.PI);
 +    }
 +
 +    @Test
 +    public void testDimension() {
 +        // arrange
 +        PolarCoordinates p = PolarCoordinates.of(1, 0);
 +
 +        // act/assert
 +        Assert.assertEquals(2, p.getDimension());
 +    }
 +
 +    @Test
 +    public void testIsNaN() {
 +        // act/assert
 +        Assert.assertFalse(PolarCoordinates.of(1, 0).isNaN());
 +        Assert.assertFalse(PolarCoordinates.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).isNaN());
 +
 +        Assert.assertTrue(PolarCoordinates.of(Double.NaN, 0).isNaN());
 +        Assert.assertTrue(PolarCoordinates.of(1, Double.NaN).isNaN());
 +        Assert.assertTrue(PolarCoordinates.of(Double.NaN, Double.NaN).isNaN());
 +    }
 +
 +    @Test
 +    public void testIsInfinite() {
 +        // act/assert
 +        Assert.assertFalse(PolarCoordinates.of(1, 0).isInfinite());
 +        Assert.assertFalse(PolarCoordinates.of(Double.NaN, Double.NaN).isInfinite());
 +
 +        Assert.assertTrue(PolarCoordinates.of(Double.POSITIVE_INFINITY, 0).isInfinite());
 +        Assert.assertTrue(PolarCoordinates.of(Double.NEGATIVE_INFINITY, 0).isInfinite());
 +        Assert.assertFalse(PolarCoordinates.of(Double.NEGATIVE_INFINITY, Double.NaN).isInfinite());
 +
 +        Assert.assertTrue(PolarCoordinates.of(0, Double.POSITIVE_INFINITY).isInfinite());
 +        Assert.assertTrue(PolarCoordinates.of(0, Double.NEGATIVE_INFINITY).isInfinite());
 +        Assert.assertFalse(PolarCoordinates.of(Double.NaN, Double.NEGATIVE_INFINITY).isInfinite());
 +
 +        Assert.assertTrue(PolarCoordinates.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).isInfinite());
 +        Assert.assertTrue(PolarCoordinates.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY).isInfinite());
 +    }
 +
 +    @Test
 +    public void testHashCode() {
 +        // arrange
 +        PolarCoordinates a = PolarCoordinates.of(1, 2);
 +        PolarCoordinates b = PolarCoordinates.of(10, 2);
 +        PolarCoordinates c = PolarCoordinates.of(10, 20);
 +        PolarCoordinates d = PolarCoordinates.of(1, 20);
 +
 +        PolarCoordinates e = PolarCoordinates.of(1, 2);
 +
 +        // act/assert
 +        Assert.assertEquals(a.hashCode(), a.hashCode());
 +        Assert.assertEquals(a.hashCode(), e.hashCode());
 +
 +        Assert.assertNotEquals(a.hashCode(), b.hashCode());
 +        Assert.assertNotEquals(a.hashCode(), c.hashCode());
 +        Assert.assertNotEquals(a.hashCode(), d.hashCode());
 +    }
 +
 +    @Test
 +    public void testHashCode_NaNInstancesHaveSameHashCode() {
 +        // arrange
 +        PolarCoordinates a = PolarCoordinates.of(1, Double.NaN);
 +        PolarCoordinates b = PolarCoordinates.of(Double.NaN, 1);
 +
 +        // act/assert
 +        Assert.assertEquals(a.hashCode(), b.hashCode());
 +    }
 +
 +    @Test
 +    public void testEquals() {
 +        // arrange
 +        PolarCoordinates a = PolarCoordinates.of(1, 2);
 +        PolarCoordinates b = PolarCoordinates.of(10, 2);
 +        PolarCoordinates c = PolarCoordinates.of(10, 20);
 +        PolarCoordinates d = PolarCoordinates.of(1, 20);
 +
 +        PolarCoordinates e = PolarCoordinates.of(1, 2);
 +
 +        // act/assert
 +        Assert.assertFalse(a.equals(null));
 +        Assert.assertFalse(a.equals(new Object()));
 +
 +        Assert.assertTrue(a.equals(a));
 +        Assert.assertTrue(a.equals(e));
 +
 +        Assert.assertFalse(a.equals(b));
 +        Assert.assertFalse(a.equals(c));
 +        Assert.assertFalse(a.equals(d));
 +    }
 +
 +    @Test
 +    public void testEquals_NaNInstancesEqual() {
 +        // arrange
 +        PolarCoordinates a = PolarCoordinates.of(1, Double.NaN);
 +        PolarCoordinates b = PolarCoordinates.of(Double.NaN, 1);
 +
 +        // act/assert
 +        Assert.assertTrue(a.equals(b));
 +    }
 +
 +    @Test
 +    public void testToVector() {
 +        // arrange
 +        double sqrt2 = Math.sqrt(2);
 +
 +        // act/assert
 +        checkVector(PolarCoordinates.of(0, 0).toVector(), 0, 0);
 +
 +        checkVector(PolarCoordinates.of(1, 0).toVector(), 1, 0);
 +        checkVector(PolarCoordinates.of(sqrt2, 0.25 * Geometry.PI).toVector(), 1, 1);
 +        checkVector(PolarCoordinates.of(1, Geometry.HALF_PI).toVector(), 0, 1);
 +
 +        checkVector(PolarCoordinates.of(sqrt2, 0.75 * Geometry.PI).toVector(), -1, 1);
 +        checkVector(PolarCoordinates.of(1, Geometry.PI).toVector(), -1, 0);
 +        checkVector(PolarCoordinates.of(sqrt2, -0.75 * Geometry.PI).toVector(), -1, -1);
 +
 +        checkVector(PolarCoordinates.of(1, Geometry.MINUS_HALF_PI).toVector(), 0, -1);
 +        checkVector(PolarCoordinates.of(sqrt2, -0.25 * Geometry.PI).toVector(), 1, -1);
 +    }
 +
 +    @Test
 +    public void testToPoint() {
 +        // arrange
 +        double sqrt2 = Math.sqrt(2);
 +
 +        // act/assert
 +        checkPoint(PolarCoordinates.of(0, 0).toPoint(), 0, 0);
 +
 +        checkPoint(PolarCoordinates.of(1, 0).toPoint(), 1, 0);
 +        checkPoint(PolarCoordinates.of(sqrt2, 0.25 * Geometry.PI).toPoint(), 1, 1);
 +        checkPoint(PolarCoordinates.of(1, Geometry.HALF_PI).toPoint(), 0, 1);
 +
 +        checkPoint(PolarCoordinates.of(sqrt2, 0.75 * Geometry.PI).toPoint(), -1, 1);
 +        checkPoint(PolarCoordinates.of(1, Geometry.PI).toPoint(), -1, 0);
 +        checkPoint(PolarCoordinates.of(sqrt2, -0.75 * Geometry.PI).toPoint(), -1, -1);
 +
 +        checkPoint(PolarCoordinates.of(1, Geometry.MINUS_HALF_PI).toPoint(), 0, -1);
 +        checkPoint(PolarCoordinates.of(sqrt2, -0.25 * Geometry.PI).toPoint(), 1, -1);
 +    }
 +
 +    @Test
-     public void testToCartesian() {
-         // arrange
-         Coordinates.Factory2D<Point2D> factory = Point2D.getFactory();
-         double sqrt2 = Math.sqrt(2);
- 
-         // act/assert
-         checkPoint(PolarCoordinates.of(0, 0).toCartesian(factory), 0, 0);
- 
-         checkPoint(PolarCoordinates.of(1, 0).toCartesian(factory), 1, 0);
-         checkPoint(PolarCoordinates.of(sqrt2, 0.25 * Geometry.PI).toCartesian(factory), 1, 1);
-         checkPoint(PolarCoordinates.of(1, Geometry.HALF_PI).toCartesian(factory), 0, 1);
- 
-         checkPoint(PolarCoordinates.of(sqrt2, 0.75 * Geometry.PI).toCartesian(factory), -1, 1);
-         checkPoint(PolarCoordinates.of(1, Geometry.PI).toCartesian(factory), -1, 0);
-         checkPoint(PolarCoordinates.of(sqrt2, -0.75 * Geometry.PI).toCartesian(factory), -1, -1);
- 
-         checkPoint(PolarCoordinates.of(1, Geometry.MINUS_HALF_PI).toCartesian(factory), 0, -1);
-         checkPoint(PolarCoordinates.of(sqrt2, -0.25 * Geometry.PI).toCartesian(factory), 1, -1);
-     }
- 
-     @Test
 +    public void testToCartesian_static() {
 +        // arrange
-         Coordinates.Factory2D<Point2D> factory = Point2D.getFactory();
++        DoubleFunction2N<Point2D> factory = Point2D.FACTORY;
 +        double sqrt2 = Math.sqrt(2);
 +
 +        // act/assert
 +        checkPoint(PolarCoordinates.toCartesian(0, 0, factory), 0, 0);
 +
 +        checkPoint(PolarCoordinates.toCartesian(1, 0, factory), 1, 0);
 +        checkPoint(PolarCoordinates.toCartesian(sqrt2, 0.25 * Geometry.PI, factory), 1, 1);
 +        checkPoint(PolarCoordinates.toCartesian(1, Geometry.HALF_PI, factory), 0, 1);
 +
 +        checkPoint(PolarCoordinates.toCartesian(sqrt2, 0.75 * Geometry.PI, factory), -1, 1);
 +        checkPoint(PolarCoordinates.toCartesian(1, Geometry.PI, factory), -1, 0);
 +        checkPoint(PolarCoordinates.toCartesian(sqrt2, -0.75 * Geometry.PI, factory), -1, -1);
 +
 +        checkPoint(PolarCoordinates.toCartesian(1, Geometry.MINUS_HALF_PI, factory), 0, -1);
 +        checkPoint(PolarCoordinates.toCartesian(sqrt2, -0.25 * Geometry.PI, factory), 1, -1);
 +    }
 +
 +    @Test
 +    public void testToCartesian_static_NaNAndInfinite() {
 +        // arrange
-         Coordinates.Factory2D<Point2D> factory = Point2D.getFactory();
++        DoubleFunction2N<Point2D> factory = Point2D.FACTORY;
 +
 +        // act/assert
 +        Assert.assertTrue(PolarCoordinates.toCartesian(Double.NaN, 0, factory).isNaN());
 +        Assert.assertTrue(PolarCoordinates.toCartesian(0, Double.NaN, factory).isNaN());
 +
 +        Assert.assertTrue(PolarCoordinates.toCartesian(Double.POSITIVE_INFINITY, 0, factory).isNaN());
 +        Assert.assertTrue(PolarCoordinates.toCartesian(0, Double.POSITIVE_INFINITY, factory).isNaN());
 +        Assert.assertTrue(PolarCoordinates.toCartesian(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, factory).isNaN());
 +
 +        Assert.assertTrue(PolarCoordinates.toCartesian(Double.NEGATIVE_INFINITY, 0, factory).isNaN());
 +        Assert.assertTrue(PolarCoordinates.toCartesian(0, Double.NEGATIVE_INFINITY, factory).isNaN());
 +        Assert.assertTrue(PolarCoordinates.toCartesian(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, factory).isNaN());
 +    }
 +
 +    @Test
 +    public void testToString() {
 +        // arrange
 +        PolarCoordinates polar = PolarCoordinates.of(1, 2);
 +        Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}\\)");
 +
 +        // act
 +        String str = polar.toString();;
 +
 +        // assert
 +        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
 +                    pattern.matcher(str).matches());
 +    }
 +
 +    @Test
 +    public void testParse() {
 +        // act/assert
 +        checkPolar(PolarCoordinates.parse("(1, 2)"), 1, 2);
 +        checkPolar(PolarCoordinates.parse("( -1 , 0.5 )"), 1, 0.5 + Geometry.PI);
 +        checkPolar(PolarCoordinates.parse("(NaN,-Infinity)"), Double.NaN, Double.NEGATIVE_INFINITY);
 +    }
 +
 +    @Test(expected = IllegalArgumentException.class)
 +    public void testParse_failure() {
 +        // act/assert
 +        PolarCoordinates.parse("abc");
 +    }
 +
 +    @Test
 +    public void testNormalizeAzimuth() {
 +        // act/assert
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(0), 0.0, EPS);
 +
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.HALF_PI), Geometry.HALF_PI, EPS);
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.PI), Geometry.PI, EPS);
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.THREE_HALVES_PI), Geometry.THREE_HALVES_PI, EPS);
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.TWO_PI), 0.0, EPS);
 +
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.MINUS_HALF_PI), Geometry.THREE_HALVES_PI, EPS);
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(-Geometry.PI), Geometry.PI, EPS);
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(-Geometry.PI - Geometry.HALF_PI), Geometry.HALF_PI, EPS);
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(-Geometry.TWO_PI), 0.0, EPS);
 +    }
 +
 +    @Test
 +    public void testNormalizeAzimuth_NaNAndInfinite() {
 +        // act/assert
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Double.NaN), Double.NaN, EPS);
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Double.NEGATIVE_INFINITY), Double.NEGATIVE_INFINITY, EPS);
 +        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Double.POSITIVE_INFINITY), Double.POSITIVE_INFINITY, EPS);
 +    }
 +
-     @Test
-     public void testGetFactory() {
-         // act
-         Coordinates.Factory2D<PolarCoordinates> factory = PolarCoordinates.getFactory();
- 
-         // assert
-         checkPolar(factory.create(-1, Geometry.HALF_PI), 1, Geometry.THREE_HALVES_PI);
-     }
- 
 +    private void checkPolar(PolarCoordinates polar, double radius, double azimuth) {
 +        Assert.assertEquals(radius, polar.getRadius(), EPS);
 +        Assert.assertEquals(azimuth, polar.getAzimuth(), EPS);
 +    }
 +
 +    private void checkVector(Vector2D v, double x, double y) {
 +        Assert.assertEquals(x, v.getX(), EPS);
 +        Assert.assertEquals(y, v.getY(), EPS);
 +    }
 +
 +    private void checkPoint(Point2D p, double x, double y) {
 +        Assert.assertEquals(x, p.getX(), EPS);
 +        Assert.assertEquals(y, p.getY(), EPS);
 +    }
 +}
diff --cc commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java
index dbd2a2a,83d56a1..1ef8d3b
--- 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
@@@ -483,40 -482,7 +482,30 @@@ public class Vector2DTest 
          // act/assert
          Vector2D.of(new double[] {0.0 });
      }
 +
 +    @Test
 +    public void testOfPolar() {
 +        // arrange
 +        double eps = 1e-15;
 +        double sqrt2 = Math.sqrt(2.0);
 +
 +        // act/assert
 +        checkVector(Vector2D.ofPolar(0, 0), 0, 0, eps);
 +        checkVector(Vector2D.ofPolar(1, 0), 1, 0, eps);
 +
 +        checkVector(Vector2D.ofPolar(2, Geometry.PI), -2, 0, eps);
 +        checkVector(Vector2D.ofPolar(-2, Geometry.PI), 2, 0, eps);
 +
 +        checkVector(Vector2D.ofPolar(2, Geometry.HALF_PI), 0, 2, eps);
 +        checkVector(Vector2D.ofPolar(-2, Geometry.HALF_PI), 0, -2, eps);
 +
 +        checkVector(Vector2D.ofPolar(2, 0.25 * Geometry.PI), sqrt2, sqrt2, eps);
 +        checkVector(Vector2D.ofPolar(2, 0.75 * Geometry.PI), -sqrt2, sqrt2, eps);
 +        checkVector(Vector2D.ofPolar(2, -0.25 * Geometry.PI), sqrt2, - sqrt2, eps);
 +        checkVector(Vector2D.ofPolar(2, -0.75 * Geometry.PI), -sqrt2, - sqrt2, eps);
 +    }
 +
      @Test
-     public void testGetFactory() {
-         // act
-         Coordinates.Factory2D<Vector2D> factory = Vector2D.getFactory();
- 
-         // assert
-         checkVector(factory.create(1, 2), 1, 2);
-         checkVector(factory.create(-1, -2), -1, -2);
-     }
- 
-     @Test
      public void testLinearCombination1() {
          // arrange
          Vector2D p1 = Vector2D.of(1, 2);
diff --cc commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
index 20602e7,21139d2..d18b5a9
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
@@@ -19,10 -19,10 +19,10 @@@ package org.apache.commons.geometry.sph
  import java.io.Serializable;
  
  import org.apache.commons.geometry.core.Point;
- import org.apache.commons.geometry.core.util.Coordinates;
- import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+ import org.apache.commons.geometry.core.internal.DoubleFunction1N;
+ import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
 +import org.apache.commons.geometry.euclidean.twod.PolarCoordinates;
  import org.apache.commons.geometry.euclidean.twod.Vector2D;
 -import org.apache.commons.numbers.angle.PlaneAngleRadians;
  
  /** This class represents a point on the 1-sphere.
   * <p>Instances of this class are guaranteed to be immutable.</p>
@@@ -42,8 -42,8 +42,8 @@@ public final class S1Point implements P
  
          /** {@inheritDoc} */
          @Override
-         public S1Point create(double a) {
-             return new S1Point(a);
+         public S1Point apply(double n) {
 -            return S1Point.of(n);
++            return new S1Point(n);
          }
      };
  
@@@ -165,7 -167,7 +165,7 @@@
      /** {@inheritDoc} */
      @Override
      public String toString() {
-         return SimpleCoordinateFormat.getPointFormat().format(getAzimuth());
 -        return SimpleTupleFormat.getDefault().format(getAlpha());
++        return SimpleTupleFormat.getDefault().format(getAzimuth());
      }
  
      /** Creates a new point instance from the given azimuthal coordinate value.
diff --cc commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
index 0b3b191,3fc8795..19cf154
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
@@@ -19,9 -19,8 +19,9 @@@ package org.apache.commons.geometry.sph
  import java.io.Serializable;
  
  import org.apache.commons.geometry.core.Point;
- import org.apache.commons.geometry.core.util.Coordinates;
- import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
+ import org.apache.commons.geometry.core.internal.DoubleFunction2N;
+ import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
 +import org.apache.commons.geometry.euclidean.threed.SphericalCoordinates;
  import org.apache.commons.geometry.euclidean.threed.Vector3D;
  
  /** This class represents a point on the 2-sphere.
@@@ -203,7 -209,7 +202,7 @@@ public final class S2Point implements P
      /** {@inheritDoc} */
      @Override
      public String toString() {
-         return SimpleCoordinateFormat.getPointFormat().format(getAzimuth(), getPolar());
 -        return SimpleTupleFormat.getDefault().format(getTheta(), getPhi());
++        return SimpleTupleFormat.getDefault().format(getAzimuth(), getPolar());
      }
  
      /** Build a vector from its spherical coordinates
diff --cc commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/S1PointTest.java
index f1c0f40,ba81963..f67fc50
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/S1PointTest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/oned/S1PointTest.java
@@@ -80,19 -79,8 +79,8 @@@ public class S1PointTest 
          S1Point.parse("abc");
      }
  
-     @Test
-     public void testGetFactory() {
-         // act
-         Coordinates.Factory1D<S1Point> factory = S1Point.getFactory();
- 
-         // assert
-         checkPoint(factory.create(0), 0);
-         checkPoint(factory.create(1), 1);
-         checkPoint(factory.create(Geometry.TWO_PI), 0);
-     }
- 
      private void checkPoint(S1Point p, double alpha) {
 -        Assert.assertEquals(alpha, p.getAlpha(), EPS);
 +        Assert.assertEquals(alpha, p.getAzimuth(), EPS);
      }
  
  }
diff --cc commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
index 462b0dd,2e15d09..827f107
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
@@@ -87,18 -96,8 +86,8 @@@ public class S2PointTest 
          S2Point.parse("abc");
      }
  
-     @Test
-     public void testGetFactory() {
-         // act
-         Coordinates.Factory2D<S2Point> factory = S2Point.getFactory();
- 
-         // assert
-         checkPoint(factory.create(0, 0), 0, 0);
-         checkPoint(factory.create(1, 2), 1, 2);
-     }
- 
      private void checkPoint(S2Point p, double theta, double phi) {
 -        Assert.assertEquals(theta, p.getTheta(), EPS);
 -        Assert.assertEquals(phi, p.getPhi(), EPS);
 +        Assert.assertEquals(theta, p.getAzimuth(), EPS);
 +        Assert.assertEquals(phi, p.getPolar(), EPS);
      }
  }


[commons-geometry] 06/15: GEOMETRY-7: adding previous spherical gradient and Hessian convertion to new SphericalDerivativeConverter class; removing old SphericalCoordinates class

Posted by er...@apache.org.
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 fefd035e263190d74aa02127fcd1ffcfdb3d227e
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Sun Jul 1 00:09:07 2018 -0400

    GEOMETRY-7: adding previous spherical gradient and Hessian convertion to new SphericalDerivativeConverter class; removing old SphericalCoordinates class
---
 ..._OLD.java => SphericalDerivativeConverter.java} | 253 +++++----------------
 .../euclidean/threed/SphericalCoordinatesTest.java |  16 ++
 .../threed/SphericalCoordinatesTest_OLD.java       |  84 -------
 .../threed/SphericalDerivativeConverterTest.java   | 187 +++++++++++++++
 4 files changed, 254 insertions(+), 286 deletions(-)

diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates_OLD.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverter.java
similarity index 60%
rename from commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates_OLD.java
rename to commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverter.java
index ac39a5e..45b8794 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates_OLD.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverter.java
@@ -16,52 +16,16 @@
  */
 package org.apache.commons.geometry.euclidean.threed;
 
-
-import java.io.Serializable;
-
-/** This class provides conversions related to <a
- * href="http://mathworld.wolfram.com/SphericalCoordinates.html">spherical coordinates</a>.
- * <p>
- * The conventions used here are the mathematical ones, i.e. spherical coordinates are
- * related to Cartesian coordinates as follows:
- * </p>
- * <ul>
- *   <li>x = r cos(&theta;) sin(&Phi;)</li>
- *   <li>y = r sin(&theta;) sin(&Phi;)</li>
- *   <li>z = r cos(&Phi;)</li>
- * </ul>
- * <ul>
- *   <li>r       = &radic;(x<sup>2</sup>+y<sup>2</sup>+z<sup>2</sup>)</li>
- *   <li>&theta; = atan2(y, x)</li>
- *   <li>&Phi;   = acos(z/r)</li>
- * </ul>
- * <p>
- * r is the radius, &theta; is the azimuthal angle in the x-y plane and &Phi; is the polar
- * (co-latitude) angle. These conventions are <em>different</em> from the conventions used
- * in physics (and in particular in spherical harmonics) where the meanings of &theta; and
- * &Phi; are reversed.
- * </p>
- * <p>
- * This class provides conversion of coordinates and also of gradient and Hessian
- * between spherical and Cartesian coordinates.
- * </p>
+/** Class containing methods for converting gradients and Hessian
+ * matrices from spherical to Cartesian coordinates.
  */
-public class SphericalCoordinates_OLD implements Serializable {
-
-    /** Serializable UID. */
-    private static final long serialVersionUID = 20130206L;
-
-    /** Cartesian coordinates. */
-    private final Vector3D v;
+public class SphericalDerivativeConverter {
 
-    /** Radius. */
-    private final double r;
+    /** Spherical coordinates. */
+    private final SphericalCoordinates spherical;
 
-    /** Azimuthal angle in the x-y plane &theta;. */
-    private final double theta;
-
-    /** Polar angle (co-latitude) &Phi;. */
-    private final double phi;
+    /** Cartesian vector equivalent to spherical coordinates. */
+    private final Vector3D vector;
 
     /** Jacobian of (r, &theta; &Phi;). */
     private double[][] jacobian;
@@ -75,77 +39,26 @@ public class SphericalCoordinates_OLD implements Serializable {
     /** Hessian of polar (co-latitude) angle &Phi;. */
     private double[][] phiHessian;
 
-    /** Build a spherical coordinates transformer from Cartesian coordinates.
-     * @param v Cartesian coordinates
-     */
-    public SphericalCoordinates_OLD(final Vector3D v) {
-
-        // Cartesian coordinates
-        this.v = v;
-
-        // remaining spherical coordinates
-        this.r     = v.getNorm();
-        this.theta = 0.0; //v.getAlpha();
-        this.phi   = Math.acos(v.getZ() / r);
-
-    }
-
-    /** Build a spherical coordinates transformer from spherical coordinates.
-     * @param r radius
-     * @param theta azimuthal angle in x-y plane
-     * @param phi polar (co-latitude) angle
-     */
-    public SphericalCoordinates_OLD(final double r, final double theta, final double phi) {
-
-        final double cosTheta = Math.cos(theta);
-        final double sinTheta = Math.sin(theta);
-        final double cosPhi   = Math.cos(phi);
-        final double sinPhi   = Math.sin(phi);
+    public SphericalDerivativeConverter(SphericalCoordinates spherical) {
+        this.spherical = spherical;
+        this.vector = spherical.toVector();
 
-        // spherical coordinates
-        this.r     = r;
-        this.theta = theta;
-        this.phi   = phi;
-
-        // Cartesian coordinates
-        this.v  = Vector3D.of(r * cosTheta * sinPhi,
-                               r * sinTheta * sinPhi,
-                               r * cosPhi);
-
-    }
-
-    /** Get the Cartesian coordinates.
-     * @return Cartesian coordinates
-     */
-    public Vector3D getCartesian() {
-        return v;
-    }
-
-    /** Get the radius.
-     * @return radius r
-     * @see #getTheta()
-     * @see #getPhi()
-     */
-    public double getR() {
-        return r;
+        computeJacobian();
     }
 
-    /** Get the azimuthal angle in x-y plane.
-     * @return azimuthal angle in x-y plane &theta;
-     * @see #getR()
-     * @see #getPhi()
+    /** Return the {@link SphericalCoordinates} for this instance.
+     * @return spherical coordinates for this instance
      */
-    public double getTheta() {
-        return theta;
+    public SphericalCoordinates getSpherical() {
+        return spherical;
     }
 
-    /** Get the polar (co-latitude) angle.
-     * @return polar (co-latitude) angle &Phi;
-     * @see #getR()
-     * @see #getTheta()
+    /** Return the {@link Vector3D} for this instance. This vector is
+     * equivalent to the spherical coordinates.
+     * @return vector for this instance
      */
-    public double getPhi() {
-        return phi;
+    public Vector3D getVector() {
+        return vector;
     }
 
     /** Convert a gradient with respect to spherical coordinates into a gradient
@@ -156,10 +69,6 @@ public class SphericalCoordinates_OLD implements Serializable {
      * {df/dx, df/dy, df/dz}
      */
     public double[] toCartesianGradient(final double[] sGradient) {
-
-        // lazy evaluation of Jacobian
-        computeJacobian();
-
         // compose derivatives as gradient^T . J
         // the expressions have been simplified since we know jacobian[1][2] = dTheta/dZ = 0
         return new double[] {
@@ -167,7 +76,6 @@ public class SphericalCoordinates_OLD implements Serializable {
             sGradient[0] * jacobian[0][1] + sGradient[1] * jacobian[1][1] + sGradient[2] * jacobian[2][1],
             sGradient[0] * jacobian[0][2]                                 + sGradient[2] * jacobian[2][2]
         };
-
     }
 
     /** Convert a Hessian with respect to spherical coordinates into a Hessian
@@ -189,8 +97,6 @@ public class SphericalCoordinates_OLD implements Serializable {
      *  {d<sup>2</sup>f/dxdz, d<sup>2</sup>f/dydz, d<sup>2</sup>f/dz<sup>2</sup>}}
      */
     public double[][] toCartesianHessian(final double[][] sHessian, final double[] sGradient) {
-
-        computeJacobian();
         computeHessians();
 
         // compose derivative as J^T . H_f . J + df/dr H_r + df/dtheta H_theta + df/dphi H_phi
@@ -233,52 +139,47 @@ public class SphericalCoordinates_OLD implements Serializable {
         cHessian[1][2] = cHessian[2][1];
 
         return cHessian;
-
     }
 
-    /** Lazy evaluation of (r, &theta;, &phi;) Jacobian.
-     */
+    /** Evaluates (r, &theta;, &phi;) Jacobian. */
     private void computeJacobian() {
-        if (jacobian == null) {
 
-            // intermediate variables
-            final double x    = v.getX();
-            final double y    = v.getY();
-            final double z    = v.getZ();
-            final double rho2 = x * x + y * y;
-            final double rho  = Math.sqrt(rho2);
-            final double r2   = rho2 + z * z;
-
-            jacobian = new double[3][3];
-
-            // row representing the gradient of r
-            jacobian[0][0] = x / r;
-            jacobian[0][1] = y / r;
-            jacobian[0][2] = z / r;
-
-            // row representing the gradient of theta
-            jacobian[1][0] = -y / rho2;
-            jacobian[1][1] =  x / rho2;
-            // jacobian[1][2] is already set to 0 at allocation time
-
-            // row representing the gradient of phi
-            jacobian[2][0] = x * z / (rho * r2);
-            jacobian[2][1] = y * z / (rho * r2);
-            jacobian[2][2] = -rho / r2;
-
-        }
+        // intermediate variables
+        final double r    = spherical.getRadius();
+        final double x    = vector.getX();
+        final double y    = vector.getY();
+        final double z    = vector.getZ();
+        final double rho2 = x * x + y * y;
+        final double rho  = Math.sqrt(rho2);
+        final double r2   = rho2 + z * z;
+
+        jacobian = new double[3][3];
+
+        // row representing the gradient of r
+        jacobian[0][0] = x / r;
+        jacobian[0][1] = y / r;
+        jacobian[0][2] = z / r;
+
+        // row representing the gradient of theta
+        jacobian[1][0] = -y / rho2;
+        jacobian[1][1] =  x / rho2;
+        // jacobian[1][2] is already set to 0 at allocation time
+
+        // row representing the gradient of phi
+        jacobian[2][0] = x * z / (rho * r2);
+        jacobian[2][1] = y * z / (rho * r2);
+        jacobian[2][2] = -rho / r2;
     }
 
-    /** Lazy evaluation of Hessians.
-     */
+    /** Lazy evaluation of Hessians. */
     private void computeHessians() {
-
         if (rHessian == null) {
 
             // intermediate variables
-            final double x      = v.getX();
-            final double y      = v.getY();
-            final double z      = v.getZ();
+            final double r      = spherical.getRadius();
+            final double x      = vector.getX();
+            final double y      = vector.getY();
+            final double z      = vector.getZ();
             final double x2     = x * x;
             final double y2     = y * y;
             final double z2     = z * z;
@@ -335,58 +236,6 @@ public class SphericalCoordinates_OLD implements Serializable {
             phiHessian[0][1] = phiHessian[1][0];
             phiHessian[0][2] = phiHessian[2][0];
             phiHessian[1][2] = phiHessian[2][1];
-
         }
-
-    }
-
-    /**
-     * Replace the instance with a data transfer object for serialization.
-     * @return data transfer object that will be serialized
-     */
-    private Object writeReplace() {
-        return new DataTransferObject(v.getX(), v.getY(), v.getZ());
     }
-
-    /** Internal class used only for serialization. */
-    private static class DataTransferObject implements Serializable {
-
-        /** Serializable UID. */
-        private static final long serialVersionUID = 20130206L;
-
-        /** Abscissa.
-         * @serial
-         */
-        private final double x;
-
-        /** Ordinate.
-         * @serial
-         */
-        private final double y;
-
-        /** Height.
-         * @serial
-         */
-        private final double z;
-
-        /** Simple constructor.
-         * @param x abscissa
-         * @param y ordinate
-         * @param z height
-         */
-        DataTransferObject(final double x, final double y, final double z) {
-            this.x = x;
-            this.y = y;
-            this.z = z;
-        }
-
-        /** Replace the deserialized data transfer object with a {@link SphericalCoordinates_OLD}.
-         * @return replacement {@link SphericalCoordinates_OLD}
-         */
-        private Object readResolve() {
-            return new SphericalCoordinates_OLD(Vector3D.of(x, y, z));
-        }
-
-    }
-
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
index d1d9f01..37d381d 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
@@ -1,3 +1,19 @@
+/*
+ * 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.euclidean.threed;
 
 import java.util.regex.Pattern;
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest_OLD.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest_OLD.java
deleted file mode 100644
index 999111c..0000000
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest_OLD.java
+++ /dev/null
@@ -1,84 +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.euclidean.threed;
-
-import org.apache.commons.geometry.euclidean.threed.SphericalCoordinates_OLD;
-import org.apache.commons.geometry.euclidean.threed.Vector3D;
-import org.apache.commons.geometry.core.GeometryTestUtils;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SphericalCoordinatesTest_OLD {
-
-    @Test
-    public void testCoordinatesStoC() {
-        double piO2 = 0.5 * Math.PI;
-        SphericalCoordinates_OLD sc1 = new SphericalCoordinates_OLD(2.0, 0, piO2);
-        Assert.assertEquals(0, sc1.getCartesian().distance(Vector3D.of(2, 0, 0)), 1.0e-10);
-        SphericalCoordinates_OLD sc2 = new SphericalCoordinates_OLD(2.0, piO2, piO2);
-        Assert.assertEquals(0, sc2.getCartesian().distance(Vector3D.of(0, 2, 0)), 1.0e-10);
-        SphericalCoordinates_OLD sc3 = new SphericalCoordinates_OLD(2.0, Math.PI, piO2);
-        Assert.assertEquals(0, sc3.getCartesian().distance(Vector3D.of(-2, 0, 0)), 1.0e-10);
-        SphericalCoordinates_OLD sc4 = new SphericalCoordinates_OLD(2.0, -piO2, piO2);
-        Assert.assertEquals(0, sc4.getCartesian().distance(Vector3D.of(0, -2, 0)), 1.0e-10);
-        SphericalCoordinates_OLD sc5 = new SphericalCoordinates_OLD(2.0, 1.23456, 0);
-        Assert.assertEquals(0, sc5.getCartesian().distance(Vector3D.of(0, 0, 2)), 1.0e-10);
-        SphericalCoordinates_OLD sc6 = new SphericalCoordinates_OLD(2.0, 6.54321, Math.PI);
-        Assert.assertEquals(0, sc6.getCartesian().distance(Vector3D.of(0, 0, -2)), 1.0e-10);
-    }
-
-    @Test
-    public void testCoordinatesCtoS() {
-        double piO2 = 0.5 * Math.PI;
-        SphericalCoordinates_OLD sc1 = new SphericalCoordinates_OLD(Vector3D.of(2, 0, 0));
-        Assert.assertEquals(2,           sc1.getR(),     1.0e-10);
-        Assert.assertEquals(0,           sc1.getTheta(), 1.0e-10);
-        Assert.assertEquals(piO2,        sc1.getPhi(),   1.0e-10);
-        SphericalCoordinates_OLD sc2 = new SphericalCoordinates_OLD(Vector3D.of(0, 2, 0));
-        Assert.assertEquals(2,           sc2.getR(),     1.0e-10);
-        Assert.assertEquals(piO2,        sc2.getTheta(), 1.0e-10);
-        Assert.assertEquals(piO2,        sc2.getPhi(),   1.0e-10);
-        SphericalCoordinates_OLD sc3 = new SphericalCoordinates_OLD(Vector3D.of(-2, 0, 0));
-        Assert.assertEquals(2,           sc3.getR(),     1.0e-10);
-        Assert.assertEquals(Math.PI, sc3.getTheta(), 1.0e-10);
-        Assert.assertEquals(piO2,        sc3.getPhi(),   1.0e-10);
-        SphericalCoordinates_OLD sc4 = new SphericalCoordinates_OLD(Vector3D.of(0, -2, 0));
-        Assert.assertEquals(2,           sc4.getR(),     1.0e-10);
-        Assert.assertEquals(-piO2,       sc4.getTheta(), 1.0e-10);
-        Assert.assertEquals(piO2,        sc4.getPhi(),   1.0e-10);
-        SphericalCoordinates_OLD sc5 = new SphericalCoordinates_OLD(Vector3D.of(0, 0, 2));
-        Assert.assertEquals(2,           sc5.getR(),     1.0e-10);
-        //  don't check theta on poles, as it is singular
-        Assert.assertEquals(0,           sc5.getPhi(),   1.0e-10);
-        SphericalCoordinates_OLD sc6 = new SphericalCoordinates_OLD(Vector3D.of(0, 0, -2));
-        Assert.assertEquals(2,           sc6.getR(),     1.0e-10);
-        //  don't check theta on poles, as it is singular
-        Assert.assertEquals(Math.PI, sc6.getPhi(),   1.0e-10);
-    }
-
-    @Test
-    public void testSerialization() {
-        SphericalCoordinates_OLD a = new SphericalCoordinates_OLD(3, 2, 1);
-        SphericalCoordinates_OLD b = (SphericalCoordinates_OLD) GeometryTestUtils.serializeAndRecover(a);
-        Assert.assertEquals(0, a.getCartesian().distance(b.getCartesian()), 1.0e-10);
-        Assert.assertEquals(a.getR(),     b.getR(),     1.0e-10);
-        Assert.assertEquals(a.getTheta(), b.getTheta(), 1.0e-10);
-        Assert.assertEquals(a.getPhi(),   b.getPhi(),   1.0e-10);
-    }
-
-}
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverterTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverterTest.java
new file mode 100644
index 0000000..4b9bcbd
--- /dev/null
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalDerivativeConverterTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.euclidean.threed;
+
+import org.apache.commons.geometry.core.Geometry;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SphericalDerivativeConverterTest {
+
+    private static final double EPS = 1e-10;
+
+    @Test
+    public void testConstructor() {
+        // arrange
+        SphericalCoordinates sc = SphericalCoordinates.of(2, Geometry.PI, Geometry.HALF_PI);
+
+        // act
+        SphericalDerivativeConverter conv = new SphericalDerivativeConverter(sc);
+
+        // assert
+        checkSpherical(conv.getSpherical(), 2, Geometry.PI, Geometry.HALF_PI);
+        checkVector(conv.getVector(), -2, 0, 0);
+    }
+
+    @Test
+    public void testToCartesianGradient() {
+        // NOTE: The following set of test data is taken from the original test for this code in commons-math.
+        // The test in that project generated and checked the inputs on the fly using the commons-math differentiation
+        // classes. However, since we don't have the benefit of those here, we're using some selected data points
+        // from that test.
+
+        // act/assert
+        checkToCartesianGradient(
+                SphericalCoordinates.of(0.2, 0.1, 0.1),
+                new double[] { 3.1274095413292105E-4, -1.724542757978006E-6, 1.5102449769881866E-4 },
+                new double[] { 0.0007872851, -0.0000078127, 0.0002357921 });
+
+        checkToCartesianGradient(
+                SphericalCoordinates.of(0.2, 0.1, 1.6),
+                new double[] { -7.825830329191124E-8, 7.798528724837122E-10, -4.027286034178383E-7 },
+                new double[] { -0.0000000197, 0.0000000019, 0.0000020151 });
+
+        checkToCartesianGradient(
+                SphericalCoordinates.of(0.2, 1.6, 0.1),
+                new double[] { -9.075903886546823E-6, -1.5573157416535893E-5, -4.352284221940998E-6 },
+                new double[] { 0.0007802833, 0.0000002252, -0.000006858 });
+
+        checkToCartesianGradient(
+                SphericalCoordinates.of(0.2, 2.4, 2.4),
+                new double[] { 6.045188551967462E-4, 2.944844493772992E-5, 5.207279563401837E-5 },
+                new double[] { -0.0003067696, -0.0000146129, -0.0006216347 });
+
+        checkToCartesianGradient(
+                SphericalCoordinates.of(9.2, 5.5, 2.4),
+                new double[] { 27.09285722408859, 327.829199283976, 422.53939642005736 },
+                new double[] { 26.1884919572, 48.3685006936, -51.0009075025 });
+    }
+
+    private void checkToCartesianGradient(SphericalCoordinates spherical, double[] sGradient, double[] cGradient) {
+        SphericalDerivativeConverter conv = new SphericalDerivativeConverter(spherical);
+
+        double[] result = conv.toCartesianGradient(sGradient);
+
+        Assert.assertArrayEquals(cGradient, result, EPS);
+    }
+
+    @Test
+    public void testToCartesianHessian() {
+        // NOTE: The following set of test data is taken from the original test for this code in commons-math.
+        // The test in that project generated and checked the inputs on the fly using the commons-math differentiation
+        // classes. However, since we don't have the benefit of those here, we're using some selected data points
+        // from that test.
+        //
+        // The NaN values in the input spherical Hessians are only present to ensure that the upper-right
+        // part of the matrix is not used in the calculation.
+
+        // act/assert
+        checkToCartesianHessian(
+                SphericalCoordinates.of(0.2, 0.0, 0.1),
+                new double[] { 3.147028015595093E-4, -1.5708927954007288E-7, 1.5209020574753025E-4 },
+                new double[][] {
+                    { 0.004720542023392639, Double.NaN, Double.NaN },
+                    { -3.927231988501822E-6, -1.5732003526076452E-5, Double.NaN },
+                    { 0.0030418041149506037, -3.0840214797113795E-6, -1.56400962465978E-4 }
+                },
+                new double[][] {
+                    { 0.0, -3.940348984959686E-4, 0.011880399467047453 },
+                    { -3.940348984959686E-4, 7.867570038987733E-6, -1.1860608699245036E-4 },
+                    { 0.011880399467047453, -1.1860608699245036E-4, 0.002384031969540735 }
+                });
+
+        checkToCartesianHessian(
+                SphericalCoordinates.of(0.2, 0.2, 1.7),
+                new double[] { -6.492205616890373E-6, 9.721055406032577E-8, -7.490005649457144E-6 },
+                new double[][] {
+                    { -9.660140526063848E-5, Double.NaN, Double.NaN },
+                    { 2.087263937942704E-6, 3.0135301759512823E-7, Double.NaN },
+                    { -1.4908056742242714E-4, 2.228225255291761E-6, -1.1271700251178201E-4 }
+                },
+                new double[][] {
+                    { 0.0, 8.228328248729827E-7, 1.9536195257978514E-4 },
+                    { 8.228328248729827E-7, -1.568516517220037E-7, -1.862033454396115E-5 },
+                    { 1.9536195257978514E-4, -1.862033454396115E-5, -0.0029473017314775615 }
+                });
+
+        checkToCartesianHessian(
+                SphericalCoordinates.of(0.2, 1.6, 0.1),
+                new double[] { -9.075903886546686E-6, -1.5573157416535897E-5, -4.352284221940931E-6 },
+                new double[][] {
+                    { -1.3557892633841054E-4, Double.NaN, Double.NaN },
+                    { -3.106944464923055E-4, 4.4143436330613375E-7, Double.NaN },
+                    { -8.660889278565699E-5, -1.489922640116937E-4, 5.374400993902801E-6 }
+                },
+                new double[][] {
+                    { 0.0, -3.862868527078941E-4, 0.011763015339492582 },
+                    { -3.862868527078941E-4, -2.229868350965674E-7, 3.395142163599996E-6 },
+                    { 0.011763015339492582, 3.395142163599996E-6, -6.892478835391066E-5 }
+                });
+
+        checkToCartesianHessian(
+                SphericalCoordinates.of(0.2, 2.4, 2.5),
+                new double[] { 6.911538590806891E-4, 3.344602742543664E-5, 3.330643810411849E-5 },
+                new double[][] {
+                    { 0.010200457858547542, Double.NaN, Double.NaN },
+                    { 6.695363800209198E-4, -3.070347513695088E-5, Double.NaN },
+                    { 6.68380906286568E-4, 3.001744637007274E-5, -2.273032055462482E-4 }
+                },
+                new double[][] {
+                    { 0.0, 1.9000713243497378E-4, 0.007402721147059207 },
+                    { 1.9000713243497378E-4, 1.6118798431431763E-5, 3.139960286869248E-4 },
+                    { 0.007402721147059207, 3.139960286869248E-4, 0.008155571186075681 }
+                });
+
+        checkToCartesianHessian(
+                SphericalCoordinates.of(9.2, 5.6, 2.5),
+                new double[] { 41.42645719593436, 859.1407583470807, 939.7112322238082 },
+                new double[][] {
+                    { 11.642163255436742, Double.NaN, Double.NaN },
+                    { 54.8154280776715, 5286.1651942531325, Double.NaN },
+                    { 60.370567966140726, 4700.570567363823, 4929.996883244262 }
+                },
+                new double[][] {
+                    { 0.0, 36.772022140868714, -22.087375306566134 },
+                    { 36.772022140868714, 212.8111723550033, -63.91326828897971 },
+                    { -22.087375306566134, -63.91326828897971, 25.593304575600133 }
+                });
+    }
+
+    private void checkToCartesianHessian(SphericalCoordinates spherical, double[] sGradient,
+            double[][] sHessian, double[][] cHessian) {
+        SphericalDerivativeConverter conv = new SphericalDerivativeConverter(spherical);
+
+        double[][] result = conv.toCartesianHessian(sHessian, sGradient);
+
+        Assert.assertEquals(cHessian.length, result.length);
+        for (int i=0; i<cHessian.length; ++i) {
+            Assert.assertArrayEquals("Hessians differ at row " + i, cHessian[i], result[i], EPS);
+        }
+    }
+
+    private void checkSpherical(SphericalCoordinates c, double radius, double azimuth, double polar) {
+        Assert.assertEquals(radius, c.getRadius(), EPS);
+        Assert.assertEquals(azimuth, c.getAzimuth(), EPS);
+        Assert.assertEquals(polar, c.getPolar(), EPS);
+    }
+
+    private void checkVector(Vector3D v, double x, double y, double z) {
+        Assert.assertEquals(x, v.getX(), EPS);
+        Assert.assertEquals(y, v.getY(), EPS);
+        Assert.assertEquals(z, v.getZ(), EPS);
+    }
+}


[commons-geometry] 08/15: GEOMETRY-7: making PolarCoordinates and SphericalCoordinates final; adding class javadocs

Posted by er...@apache.org.
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 ccd1928504fdf4332e1da0f0ee62efc0e7512c05
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Mon Jul 9 22:49:05 2018 -0400

    GEOMETRY-7: making PolarCoordinates and SphericalCoordinates final; adding class javadocs
---
 .../euclidean/threed/SphericalCoordinates.java     | 53 +++++++++++++++++++---
 .../geometry/euclidean/twod/PolarCoordinates.java  | 42 ++++++++++++-----
 2 files changed, 77 insertions(+), 18 deletions(-)

diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
index 6b38049..d3b3a25 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
@@ -24,9 +24,49 @@ import org.apache.commons.geometry.core.util.Coordinates;
 import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
 import org.apache.commons.numbers.angle.PlaneAngleRadians;
 
-/** Class representing a set of spherical coordinates in 3 dimensional Euclidean space.
+/** Class representing <a href="https://en.wikipedia.org/wiki/Spherical_coordinate_system">spherical coordinates</a> 
+ * in 3 dimensional Euclidean space.
+ * 
+ * <p>Spherical coordinates for a point are defined by three values:
+ * <ol>
+ * 	<li><em>Radius</em> - The distance from the point to a fixed referenced point.</li>
+ * 	<li><em>Azimuth angle</em> - The angle measured from a fixed reference direction in a plane to
+ * the orthogonal projection of the point on that plane.</li>
+ *	<li><em>Polar angle</em> - The angle measured from a fixed zenith direction to the point. The zenith
+ *direction must be orthogonal to the reference plane.</li>
+ * </ol>
+ * This class follows the convention of using the origin as the reference point; the positive x-axis as the
+ * reference direction for the azimuth angle, measured in the x-y plane with positive angles moving counter-clockwise 
+ * toward the positive y-axis; and the positive z-axis as the zenith direction. Spherical coordinates are
+ * related to Cartesian coordinates as follows:
+ * <pre>
+ * x = r cos(&theta;) sin(&Phi;)
+ * y = r sin(&theta;) sin(&Phi;)
+ * z = r cos(&Phi;)
+ * 
+ * r = &radic;(x<sup>2</sup>+y<sup>2</sup>+z<sup>2</sup>)
+ * &theta; = atan2(y, x)
+ * &Phi; = acos(z/r)
+ * </pre>
+ * where <em>r</em> is the radius, <em>&theta;</em> is the azimuth angle, and <em>&Phi;</em> is the polar angle
+ * of the spherical coordinates.
+ * </p>
+ * 
+ * <p>There are numerous, competing conventions for the symbols used to represent spherical coordinate values. For
+ * example, the mathematical convention is to use <em>(r, &theta;, &Phi;)</em> to represent radius, azimuth angle, and
+ * polar angle, whereas the physics convention flips the angle values and uses <em>(r, &Phi;, &theta;)</em>. As such,
+ * this class avoids the use of these symbols altogether in favor of the less ambiguous formal names of the values,
+ * e.g. {@code radius}, {@code azimuth}, and {@code polar}.
+ * </p>
+ * 
+ * <p>In order to ensure the uniqueness of coordinate sets, coordinate values 
+ * are normalized so that {@code radius} is in the range {@code [0, +Infinity)}, 
+ * {@code azimuth} is in the range {@code (-pi, pi]}, and {@code polar} is in the
+ * range {@code [0, pi]}.</p>
+ * 
+ * @see <a href="https://en.wikipedia.org/wiki/Spherical_coordinate_system">Spherical Coordinate System</a>
  */
-public class SphericalCoordinates implements Spatial, Serializable {
+public final class SphericalCoordinates implements Spatial, Serializable {
 
     /** Serializable version identifier. */
     private static final long serialVersionUID = 20180623L;
@@ -84,7 +124,7 @@ public class SphericalCoordinates implements Spatial, Serializable {
         this.polar = polar;
     }
 
-    /** Return the radius value. The value is in the range {@code [0, +infinity)}.
+    /** Return the radius value. The value is in the range {@code [0, +Infinity)}.
      * @return the radius value
      */
     public double getRadius() {
@@ -152,8 +192,7 @@ public class SphericalCoordinates implements Spatial, Serializable {
         return toCartesian(Point3D.getFactory());
     }
 
-    /**
-     * Get a hashCode for this set of spherical coordinates.
+    /** Get a hashCode for this set of spherical coordinates.
      * <p>All NaN values have the same hash code.</p>
      *
      * @return a hash code value for this object
@@ -207,8 +246,8 @@ public class SphericalCoordinates implements Spatial, Serializable {
         return SimpleCoordinateFormat.getPointFormat().format(radius, azimuth, polar);
     }
 
-    /** Create a {@link SphericalCoordinates} instance from the given values. The values are normalized
-     * so that {@code radius} lies in the range {@code [0, +infinity)}, {@code azimuth} lies in the range
+    /** Return a new instance with the given spherical coordinate values. The values are normalized
+     * so that {@code radius} lies in the range {@code [0, +Infinity)}, {@code azimuth} lies in the range
      * {@code (-pi, +pi]}, and {@code polar} lies in the range {@code [0, +pi]}.
      * @param radius the length of the line segment from the origin to the coordinate point.
      * @param azimuth the angle in the x-y plane, measured in radians counter-clockwise
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
index da80e59..9bc9c96 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
@@ -24,12 +24,33 @@ import org.apache.commons.geometry.core.util.Coordinates;
 import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
 import org.apache.commons.numbers.angle.PlaneAngleRadians;
 
-/** Class representing a set of polar coordinates in 2 dimensional
- * Euclidean space. Coordinates are normalized so that {@code radius}
- * is in the range {@code [0, +infinity)} and {@code azimuth} is in the
- * range {@code (-pi, pi]}.
+/** Class representing <a href="https://en.wikipedia.org/wiki/Polar_coordinate_system">polar coordinates</a> 
+ * in 2 dimensional Euclidean space. 
+ * 
+ * <p>Polar coordinates are defined by a distance from a reference point
+ * and an angle from a reference direction. The distance value is called 
+ * the radial coordinate, or <em>radius</em>, and the angle is called the angular coordinate,
+ * or <em>azimuth</em>. This class follows the standard
+ * mathematical convention of using the positive x-axis as the reference
+ * direction and measuring positive angles counter-clockwise, toward the 
+ * positive y-axis. The origin is used as the reference point. Polar coordinate
+ * are related to Cartesian coordinates as follows:
+ * <pre> 
+ * x = r * cos(&theta;)
+ * y = r * sin(&theta;)
+ * 
+ * r = &radic;(x<sup>2</sup> + y<sup>2</sup>)
+ * &theta; = atan2(y, x)
+ * </pre>
+ * where <em>r</em> is the radius and <em>&theta;</em> is the azimuth of the polar coordinates.
+ * </p>
+ * <p>In order to ensure the uniqueness of coordinate sets, coordinate values 
+ * are normalized so that {@code radius} is in the range {@code [0, +Infinity)} 
+ * and {@code azimuth} is in the range {@code (-pi, pi]}.</p>
+ * 
+ * @see <a href="https://en.wikipedia.org/wiki/Polar_coordinate_system">Polar Coordinate System</a>
  */
-public class PolarCoordinates implements Spatial, Serializable {
+public final class PolarCoordinates implements Spatial, Serializable {
 
     /** Serializable version UID */
     private static final long serialVersionUID = 20180630L;
@@ -47,7 +68,7 @@ public class PolarCoordinates implements Spatial, Serializable {
     /** Radius value */
     private final double radius;
 
-    /** Azimuth angle in radians. */
+    /** Azimuth angle in radians */
     private final double azimuth;
 
     /** Simple constructor. Input values are normalized.
@@ -137,8 +158,7 @@ public class PolarCoordinates implements Spatial, Serializable {
         return toCartesian(Point2D.getFactory());
     }
 
-    /**
-     * Get a hashCode for this set of polar coordinates.
+    /** Get a hashCode for this set of polar coordinates.
      * <p>All NaN values have the same hash code.</p>
      *
      * @return a hash code value for this object
@@ -192,9 +212,9 @@ public class PolarCoordinates implements Spatial, Serializable {
         return SimpleCoordinateFormat.getPointFormat().format(radius, azimuth);
     }
 
-    /** Return a new polar coordinate instance with the given values.
-     * The values are normalized so that radius lies in the range {@code [0, +infinity)}
-     * and azimuth in the range {@code (-pi, pi]}.
+    /** Return a new instance with the given polar coordinate values.
+     * The values are normalized so that {@code radius} lies in the range {@code [0, +infinity)}
+     * and {@code azimuth} in the range {@code (-pi, pi]}.
      * @param radius Radius value.
      * @param azimuth Azimuth angle in radians.
      * @return new {@link PolarCoordinates} instance


[commons-geometry] 01/15: GEOMETRY-7: adding PolarCoordinates class

Posted by er...@apache.org.
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 76086cc615ce129629b2f5a4a91baa1c3c3b3467
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Thu Jun 21 23:39:15 2018 -0400

    GEOMETRY-7: adding PolarCoordinates class
---
 .../org/apache/commons/geometry/core/Geometry.java |  15 +-
 .../apache/commons/geometry/core/GeometryTest.java |  64 ++++
 .../geometry/euclidean/twod/Cartesian2D.java       |   7 +
 .../commons/geometry/euclidean/twod/Point2D.java   |   9 +
 .../geometry/euclidean/twod/PolarCoordinates.java  | 300 +++++++++++++++++
 .../commons/geometry/euclidean/twod/Vector2D.java  |   9 +
 .../geometry/euclidean/twod/Cartesian2DTest.java   |  33 ++
 .../geometry/euclidean/twod/Point2DTest.java       |  31 +-
 .../euclidean/twod/PolarCoordinatesTest.java       | 360 +++++++++++++++++++++
 .../geometry/euclidean/twod/Vector2DTest.java      |  30 +-
 10 files changed, 848 insertions(+), 10 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..efe797d 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
@@ -23,16 +23,19 @@ 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}.
-     */
+    /** Constant value for {@code -pi} */
+    public static final double MINUS_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}.
-     */
+    /** Constant value for {@code -2*pi}. */
+    public static final double MINUS_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}.
-     */
+    /** Constant value for {@code - pi / 2}. */
     public static final double MINUS_HALF_PI = - 0.5 * Math.PI;
 
     /** Private constructor */
diff --git a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTest.java b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTest.java
new file mode 100644
index 0000000..88533eb
--- /dev/null
+++ b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class GeometryTest {
+
+    @Test
+    public void testConstants() {
+        // arrange
+        double eps = 0.0;
+
+        // act/assert
+        Assert.assertEquals(Math.PI, Geometry.PI, eps);
+        Assert.assertEquals(-Math.PI, Geometry.MINUS_PI, eps);
+
+        Assert.assertEquals(2.0 * Math.PI, Geometry.TWO_PI, eps);
+        Assert.assertEquals(-2.0 * Math.PI, Geometry.MINUS_TWO_PI, eps);
+
+        Assert.assertEquals(Math.PI / 2.0, Geometry.HALF_PI, 0.0);
+        Assert.assertEquals(-Math.PI / 2.0, Geometry.MINUS_HALF_PI, eps);
+    }
+
+    @Test
+    public void testConstants_trigEval() {
+        // arrange
+        double eps = 1e-15;
+
+        // act/assert
+        Assert.assertEquals(0.0, Math.sin(Geometry.PI), eps);
+        Assert.assertEquals(-1.0, Math.cos(Geometry.PI), eps);
+
+        Assert.assertEquals(0.0, Math.sin(Geometry.MINUS_PI), eps);
+        Assert.assertEquals(-1.0, Math.cos(Geometry.MINUS_PI), eps);
+
+        Assert.assertEquals(0.0, Math.sin(Geometry.TWO_PI), eps);
+        Assert.assertEquals(1.0, Math.cos(Geometry.TWO_PI), eps);
+
+        Assert.assertEquals(0.0, Math.sin(Geometry.MINUS_TWO_PI), eps);
+        Assert.assertEquals(1.0, Math.cos(Geometry.MINUS_TWO_PI), eps);
+
+        Assert.assertEquals(1.0, Math.sin(Geometry.HALF_PI), eps);
+        Assert.assertEquals(0.0, Math.cos(Geometry.HALF_PI), eps);
+
+        Assert.assertEquals(-1.0, Math.sin(Geometry.MINUS_HALF_PI), eps);
+        Assert.assertEquals(0.0, Math.cos(Geometry.MINUS_HALF_PI), eps);
+    }
+}
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 d4c69b6..8710b3e 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
@@ -59,6 +59,13 @@ public abstract class Cartesian2D implements Spatial, Serializable {
         return y;
     }
 
+    /** Return an equivalent set of coordinates in polar form.
+     * @return An equivalent set of coordinates in polar form.
+     */
+    public PolarCoordinates toPolar() {
+        return PolarCoordinates.ofCartesian(x, y);
+    }
+
     /** Get the coordinates for this instance as a dimension 2 array.
      * @return coordinates for this instance
      */
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 c32777a..6561570 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
@@ -178,6 +178,15 @@ public final class Point2D extends Cartesian2D implements EuclideanPoint<Point2D
         return new Point2D(p[0], p[1]);
     }
 
+    /**Return a point with coordinates equivalent to the given set of polar coordinates.
+     * @param radius The polar coordinate radius value.
+     * @param azimuth The polar coordinate azimuth angle in radians.
+     * @return point instance with coordinates equivalent to the given polar coordinates.
+     */
+    public static Point2D ofPolar(final double radius, final double azimuth) {
+        return PolarCoordinates.toCartesian(radius, azimuth, getFactory());
+    }
+
     /** Parses the given string and returns a new point instance. The expected string
      * format is the same as that returned by {@link #toString()}.
      * @param str the string to parse
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
new file mode 100644
index 0000000..de3731c
--- /dev/null
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
@@ -0,0 +1,300 @@
+/*
+ * 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.euclidean.twod;
+
+import java.io.Serializable;
+import java.text.ParsePosition;
+
+import org.apache.commons.geometry.core.Geometry;
+import org.apache.commons.geometry.core.Spatial;
+import org.apache.commons.geometry.core.util.AbstractCoordinateParser;
+import org.apache.commons.geometry.core.util.Coordinates;
+import org.apache.commons.numbers.angle.PlaneAngleRadians;
+
+/** Class representing a set of polar coordinates in 2 dimensional
+ * Euclidean space. Coordinates are normalized so that {@code radius >= 0}
+ * and {@code -pi < azimuth <= pi}.
+ */
+public class PolarCoordinates implements Spatial, Serializable {
+
+    /** Serializable version UID */
+    private static final long serialVersionUID = -3122872387910228544L;
+
+    /** Shared parser/formatter instance **/
+    private static final PolarCoordinatesParser PARSER = new PolarCoordinatesParser();
+
+    /** Radius value */
+    private final double radius;
+
+    /** Azimuth angle in radians. */
+    private final double azimuth;
+
+    /** Simple constructor. Input values must already be normalized.
+     * @param radius Radius value.
+     * @param azimuth Azimuth angle in radians.
+     */
+    private PolarCoordinates(final double radius, final double azimuth) {
+        this.radius = radius;
+        this.azimuth = azimuth;
+    }
+
+    /** Return the radius value. The value will be greater than or equal to 0.
+     * @return radius value
+     */
+    public double getRadius() {
+        return radius;
+    }
+
+    /** Return the azimuth angle in radians. The value will be
+     * in the range {@code (-pi, pi]}.
+     * @return azimuth value in radians.
+     */
+    public double getAzimuth() {
+        return azimuth;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getDimension() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isNaN() {
+        return Double.isNaN(radius) || Double.isNaN(azimuth);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isInfinite() {
+        return !isNaN() && (Double.isInfinite(radius) || Double.isInfinite(azimuth));
+    }
+
+    /** Convert this set of polar coordinates to Cartesian coordinates.
+     * The Cartesian coordinates are computed and passed to the given
+     * factory instance. The factory's return value is returned.
+     * @param factory Factory instance that will be passed the computed Cartesian coordinates
+     * @return the value returned by the given factory when passed Cartesian
+     *      coordinates equivalent to this set of polar coordinates.
+     */
+    public <T> T toCartesian(final Coordinates.Factory2D<T> factory) {
+        return toCartesian(radius, azimuth, factory);
+    }
+
+    /** Convert this set of polar coordinates to a 2-dimensional
+     * vector.
+     * @return A 2-dimensional vector with an equivalent set of
+     *      coordinates.
+     */
+    public Vector2D toVector() {
+        return toCartesian(Vector2D.getFactory());
+    }
+
+    /** Convert this set of polar coordinates to a 2-dimensional
+     * point.
+     * @return A 2-dimensional point with an equivalent set of
+     *      coordinates.
+     */
+    public Point2D toPoint() {
+        return toCartesian(Point2D.getFactory());
+    }
+
+    /**
+     * Get a hashCode for this set of polar coordinates.
+     * <p>All NaN values have the same hash code.</p>
+     *
+     * @return a hash code value for this object
+     */
+    @Override
+    public int hashCode() {
+        if (isNaN()) {
+            return 191;
+        }
+        return 449 * (76 * Double.hashCode(radius) + Double.hashCode(azimuth));
+    }
+
+    /** Test for the equality of two sets of polar coordinates.
+     * <p>
+     * If all values of two sets of coordinates are exactly the same, and none are
+     * <code>Double.NaN</code>, the two sets are considered to be equal.
+     * </p>
+     * <p>
+     * <code>NaN</code> values are considered to globally affect the coordinates
+     * and be equal to each other - i.e, if either (or all) values of the
+     * coordinate set are equal to <code>Double.NaN</code>, the set is equal to
+     * {@link #NaN}.
+     * </p>
+     *
+     * @param other Object to test for equality to this
+     * @return true if two PolarCoordinates objects are equal, false if
+     *         object is null, not an instance of PolarCoordinates, or
+     *         not equal to this PolarCoordinates instance
+     *
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (other instanceof PolarCoordinates) {
+            final PolarCoordinates rhs = (PolarCoordinates) other;
+            if (rhs.isNaN()) {
+                return this.isNaN();
+            }
+
+            return (radius == rhs.radius) && (azimuth == rhs.azimuth);
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return PARSER.format(this);
+    }
+
+    /** Return a new polar coordinate instance with the given values.
+     * The values are normalized so that radius lies in the range {@code [0, +infinity)}
+     * and azimuth in the range {@code (-pi, pi]}.
+     * @param radius Radius value.
+     * @param azimuth Azimuth angle in radians.
+     * @return
+     */
+    public static PolarCoordinates of(double radius, double azimuth) {
+        if (radius < 0) {
+            radius = Math.abs(radius);
+            azimuth += Geometry.PI;
+        }
+
+        if (Double.isFinite(azimuth)) {
+            azimuth = PlaneAngleRadians.normalizeBetweenMinusPiAndPi(azimuth);
+
+            // the above normalizes the azimuth to -pi <= azimuth <= pi
+            // but we want -pi < azimuth <= pi to have completely unique coordinates
+            if (azimuth <= -Geometry.PI) {
+                azimuth += Geometry.TWO_PI;
+            }
+        }
+
+        return new PolarCoordinates(radius, azimuth);
+    }
+
+    /** Convert the given Cartesian coordinates to polar form.
+     * @param x X coordinate value
+     * @param y Y coordinate value
+     * @return polar coordinates equivalent to the given Cartesian coordinates
+     */
+    public static PolarCoordinates ofCartesian(final double x, final double y) {
+        final double azimuth = Math.atan2(y, x);
+        final double radius = Math.hypot(x, y);
+
+        return new PolarCoordinates(radius, azimuth);
+    }
+
+    /** Parse the given string and return a new polar coordinates instance. The parsed
+     * coordinates are normalized so that radius is within the range {@code [0, +infinity)}
+     * and azimuth is within the range {@code (-pi, pi]}. The expected string
+     * format is the same as that returned by {@link #toString()}.
+     * @param input the string to parse
+     * @return
+     */
+    public static PolarCoordinates parse(String input) {
+        return PARSER.parse(input);
+    }
+
+    /** Convert the given set of polar coordinates to Cartesian coordinates.
+     * The Cartesian coordinates are computed and passed to the given
+     * factory instance. The factory's return value is returned.
+     * @param radius Radius value
+     * @param azimuth Azimuth value in radians
+     * @param factory Factory instance that will be passed the computed Cartesian coordinates
+     * @param <T> Type returned by the factory
+     * @return the value returned by the given factory when passed Cartesian
+     *      coordinates equivalent to given set of polar coordinates.
+     */
+    public static <T> T toCartesian(final double radius, final double azimuth, final Coordinates.Factory2D<T> factory) {
+        final double x = radius * Math.cos(azimuth);
+        final double y = radius * Math.sin(azimuth);
+
+        return factory.create(x, y);
+    }
+
+    /** Parser and formatter class for polar coordinates. */
+    private static class PolarCoordinatesParser extends AbstractCoordinateParser {
+
+        /** String prefix for the radius value. */
+        private static final String RADIUS_PREFIX = "r=";
+
+        /** String prefix for the azimuth value. */
+        private static final String AZIMUTH_PREFIX = "az=";
+
+        /** Simple constructor. */
+        private PolarCoordinatesParser() {
+            super(",", "(", ")");
+        }
+
+        /** Return a standardized string representation of the given set of polar
+         * coordinates.
+         * @param polar coordinates to format
+         * @return a standard string representation of the polar coordinates
+         */
+        public String format(PolarCoordinates polar) {
+            final StringBuilder sb = new StringBuilder();
+
+            sb.append(getPrefix());
+
+            sb.append(RADIUS_PREFIX);
+            sb.append(polar.getRadius());
+
+            sb.append(getSeparator());
+            sb.append(" ");
+
+            sb.append(AZIMUTH_PREFIX);
+            sb.append(polar.getAzimuth());
+
+            sb.append(getSuffix());
+
+            return sb.toString();
+        }
+
+        /** Parse the given string and return a set of standardized polar coordinates.
+         * @param str the string to parse
+         * @return polar coordinates
+         */
+        public PolarCoordinates parse(String str) {
+            final ParsePosition pos = new ParsePosition(0);
+
+            readPrefix(str, pos);
+
+            consumeWhitespace(str, pos);
+            readSequence(str, RADIUS_PREFIX, pos);
+            final double radius = readCoordinateValue(str, pos);
+
+            consumeWhitespace(str, pos);
+            readSequence(str, AZIMUTH_PREFIX, pos);
+            final double azimuth = readCoordinateValue(str, pos);
+
+            readSuffix(str, pos);
+            endParse(str, pos);
+
+            // use the factory method so that the values will be normalized
+            return PolarCoordinates.of(radius, azimuth);
+        }
+    }
+}
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 b6c67af..e2e2e56 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
@@ -377,6 +377,15 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point
         return new Vector2D(v[0], v[1]);
     }
 
+    /** Return a vector with coordinates equivalent to the given set of polar coordinates.
+     * @param radius The polar coordinate radius value.
+     * @param azimuth The polar coordinate azimuth angle in radians.
+     * @return vector instance with coordinates equivalent to the given polar coordinates.
+     */
+    public static Vector2D ofPolar(final double radius, final double azimuth) {
+        return PolarCoordinates.toCartesian(radius, azimuth, getFactory());
+    }
+
     /** Parses the given string and returns a new vector instance. The expected string
      * format is the same as that returned by {@link #toString()}.
      * @param str the string to parse
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
index b8c7238..379269f 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
@@ -1,5 +1,6 @@
 package org.apache.commons.geometry.euclidean.twod;
 
+import org.apache.commons.geometry.core.Geometry;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -68,6 +69,38 @@ public class Cartesian2DTest {
         Assert.assertFalse(new StubCartesian2D(Double.NaN, Double.POSITIVE_INFINITY).isInfinite());
     }
 
+    @Test
+    public void testToPolar() {
+        // arrange
+        double sqrt2 = Math.sqrt(2.0);
+
+        // act/assert
+        checkPolar(new StubCartesian2D(0, 0).toPolar(), 0, 0);
+
+        checkPolar(new StubCartesian2D(1, 0).toPolar(), 1, 0);
+        checkPolar(new StubCartesian2D(-1, 0).toPolar(), 1, Geometry.PI);
+
+        checkPolar(new StubCartesian2D(0, 2).toPolar(), 2, Geometry.HALF_PI);
+        checkPolar(new StubCartesian2D(0, -2).toPolar(), 2, Geometry.MINUS_HALF_PI);
+
+        checkPolar(new StubCartesian2D(sqrt2, sqrt2).toPolar(), 2, 0.25 * Geometry.PI);
+        checkPolar(new StubCartesian2D(-sqrt2, sqrt2).toPolar(), 2, 0.75 * Geometry.PI);
+        checkPolar(new StubCartesian2D(sqrt2, -sqrt2).toPolar(), 2, -0.25 * Geometry.PI);
+        checkPolar(new StubCartesian2D(-sqrt2, -sqrt2).toPolar(), 2, -0.75 * Geometry.PI);
+    }
+
+    @Test
+    public void testToPolar_NaNAndInfinite() {
+        // act/assert
+        Assert.assertTrue(new StubCartesian2D(Double.NaN, Double.NaN).toPolar().isNaN());
+        Assert.assertTrue(new StubCartesian2D(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).toPolar().isInfinite());
+    }
+
+    private void checkPolar(PolarCoordinates polar, double radius, double azimuth) {
+        Assert.assertEquals(radius, polar.getRadius(), TEST_TOLERANCE);
+        Assert.assertEquals(azimuth, polar.getAzimuth(), TEST_TOLERANCE);
+    }
+
     private static class StubCartesian2D extends Cartesian2D {
         private static final long serialVersionUID = 1L;
 
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
index 20d95ad..f71ce00 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
@@ -18,6 +18,7 @@ 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.util.Coordinates;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
@@ -220,6 +221,28 @@ public class Point2DTest {
     }
 
     @Test
+    public void testOfPolar() {
+        // arrange
+        double eps = 1e-15;
+        double sqrt2 = Math.sqrt(2.0);
+
+        // act/assert
+        checkPoint(Point2D.ofPolar(0, 0), 0, 0, eps);
+        checkPoint(Point2D.ofPolar(1, 0), 1, 0, eps);
+
+        checkPoint(Point2D.ofPolar(2, Geometry.PI), -2, 0, eps);
+        checkPoint(Point2D.ofPolar(-2, Geometry.PI), 2, 0, eps);
+
+        checkPoint(Point2D.ofPolar(2, Geometry.HALF_PI), 0, 2, eps);
+        checkPoint(Point2D.ofPolar(-2, Geometry.HALF_PI), 0, -2, eps);
+
+        checkPoint(Point2D.ofPolar(2, 0.25 * Geometry.PI), sqrt2, sqrt2, eps);
+        checkPoint(Point2D.ofPolar(2, 0.75 * Geometry.PI), -sqrt2, sqrt2, eps);
+        checkPoint(Point2D.ofPolar(2, -0.25 * Geometry.PI), sqrt2, - sqrt2, eps);
+        checkPoint(Point2D.ofPolar(2, -0.75 * Geometry.PI), -sqrt2, - sqrt2, eps);
+    }
+
+    @Test
     public void testGetFactory() {
         // act
         Coordinates.Factory2D<Point2D> factory = Point2D.getFactory();
@@ -286,7 +309,11 @@ public class Point2DTest {
     }
 
     private void checkPoint(Point2D p, double x, double y) {
-        Assert.assertEquals(x, p.getX(), EPS);
-        Assert.assertEquals(y, p.getY(), EPS);
+        checkPoint(p, x, y, EPS);
+    }
+
+    private void checkPoint(Point2D p, double x, double y, double eps) {
+        Assert.assertEquals(x, p.getX(), eps);
+        Assert.assertEquals(y, p.getY(), eps);
     }
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
new file mode 100644
index 0000000..db5da3b
--- /dev/null
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
@@ -0,0 +1,360 @@
+/*
+ * 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.euclidean.twod;
+
+import java.util.regex.Pattern;
+
+import org.apache.commons.geometry.core.Geometry;
+import org.apache.commons.geometry.core.util.Coordinates;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PolarCoordinatesTest {
+
+    private static final double EPS = 1e-10;
+
+    @Test
+    public void testOf() {
+        // act/assert
+        checkPolar(PolarCoordinates.of(0, 0), 0, 0);
+
+        checkPolar(PolarCoordinates.of(2, 0), 2, 0);
+        checkPolar(PolarCoordinates.of(2, Geometry.HALF_PI), 2, Geometry.HALF_PI);
+        checkPolar(PolarCoordinates.of(2, Geometry.PI), 2, Geometry.PI);
+        checkPolar(PolarCoordinates.of(2, Geometry.MINUS_HALF_PI), 2, Geometry.MINUS_HALF_PI);
+    }
+
+    @Test
+    public void testOf_unnormalizedAngles() {
+        // act/assert
+        checkPolar(PolarCoordinates.of(2, Geometry.TWO_PI), 2, 0);
+        checkPolar(PolarCoordinates.of(2, Geometry.HALF_PI + Geometry.TWO_PI), 2, Geometry.HALF_PI);
+        checkPolar(PolarCoordinates.of(2, -Geometry.PI), 2, Geometry.PI);
+        checkPolar(PolarCoordinates.of(2, Geometry.PI * 1.5), 2, Geometry.MINUS_HALF_PI);
+    }
+
+    @Test
+    public void testOf_azimuthWrapAround() {
+        // arrange
+        double delta = 1e-6;
+
+        // act/assert
+        checkAzimuthWrapAround(2, 0);
+        checkAzimuthWrapAround(2, delta);
+        checkAzimuthWrapAround(2, Geometry.PI - delta);
+        checkAzimuthWrapAround(2, Geometry.PI);
+
+        checkAzimuthWrapAround(2, 0);
+        checkAzimuthWrapAround(2, -delta);
+        checkAzimuthWrapAround(2, delta - Geometry.PI);
+    }
+
+    private void checkAzimuthWrapAround(double radius, double azimuth) {
+        checkPolar(PolarCoordinates.of(radius, azimuth), radius, azimuth);
+
+        checkPolar(PolarCoordinates.of(radius, azimuth - Geometry.TWO_PI), radius, azimuth);
+        checkPolar(PolarCoordinates.of(radius, azimuth - (2 * Geometry.TWO_PI)), radius, azimuth);
+        checkPolar(PolarCoordinates.of(radius, azimuth - (3 * Geometry.TWO_PI)), radius, azimuth);
+
+        checkPolar(PolarCoordinates.of(radius, azimuth + Geometry.TWO_PI), radius, azimuth);
+        checkPolar(PolarCoordinates.of(radius, azimuth + (2 * Geometry.TWO_PI)), radius, azimuth);
+        checkPolar(PolarCoordinates.of(radius, azimuth + (3 * Geometry.TWO_PI)), radius, azimuth);
+    }
+
+    @Test
+    public void testOf_negativeRadius() {
+        // act/assert
+        checkPolar(PolarCoordinates.of(-1, 0), 1, Geometry.PI);
+        checkPolar(PolarCoordinates.of(-1e-6, Geometry.HALF_PI), 1e-6, Geometry.MINUS_HALF_PI);
+        checkPolar(PolarCoordinates.of(-2, Geometry.PI), 2, 0);
+        checkPolar(PolarCoordinates.of(-3, Geometry.MINUS_HALF_PI), 3, Geometry.HALF_PI);
+    }
+
+    @Test
+    public void testOf_NaNAndInfinite() {
+        // act/assert
+        checkPolar(PolarCoordinates.of(Double.NaN, 0), Double.NaN, 0);
+        checkPolar(PolarCoordinates.of(Double.NEGATIVE_INFINITY, 0), Double.POSITIVE_INFINITY, Geometry.PI);
+        checkPolar(PolarCoordinates.of(Double.POSITIVE_INFINITY, 0), Double.POSITIVE_INFINITY, 0);
+
+        checkPolar(PolarCoordinates.of(0, Double.NaN), 0, Double.NaN);
+        checkPolar(PolarCoordinates.of(0, Double.NEGATIVE_INFINITY), 0, Double.NEGATIVE_INFINITY);
+        checkPolar(PolarCoordinates.of(0, Double.POSITIVE_INFINITY), 0, Double.POSITIVE_INFINITY);
+    }
+
+    @Test
+    public void testOfCartesian() {
+        // arrange
+        double sqrt2 = Math.sqrt(2);
+
+        // act/assert
+        checkPolar(PolarCoordinates.ofCartesian(0, 0), 0, 0);
+
+        checkPolar(PolarCoordinates.ofCartesian(1, 0), 1, 0);
+        checkPolar(PolarCoordinates.ofCartesian(1, 1), sqrt2, 0.25 * Geometry.PI);
+        checkPolar(PolarCoordinates.ofCartesian(0, 1), 1, Geometry.HALF_PI);
+
+        checkPolar(PolarCoordinates.ofCartesian(-1, 1), sqrt2, 0.75 * Geometry.PI);
+        checkPolar(PolarCoordinates.ofCartesian(-1, 0), 1, Geometry.PI);
+        checkPolar(PolarCoordinates.ofCartesian(-1, -1), sqrt2, - 0.75 * Geometry.PI);
+
+        checkPolar(PolarCoordinates.ofCartesian(0, -1), 1, Geometry.MINUS_HALF_PI);
+        checkPolar(PolarCoordinates.ofCartesian(1, -1), sqrt2, -0.25 * Geometry.PI);
+    }
+
+    @Test
+    public void testDimension() {
+        // arrange
+        PolarCoordinates p = PolarCoordinates.of(1, 0);
+
+        // act/assert
+        Assert.assertEquals(2, p.getDimension());
+    }
+
+    @Test
+    public void testIsNaN() {
+        // act/assert
+        Assert.assertFalse(PolarCoordinates.of(1, 0).isNaN());
+        Assert.assertFalse(PolarCoordinates.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).isNaN());
+
+        Assert.assertTrue(PolarCoordinates.of(Double.NaN, 0).isNaN());
+        Assert.assertTrue(PolarCoordinates.of(1, Double.NaN).isNaN());
+        Assert.assertTrue(PolarCoordinates.of(Double.NaN, Double.NaN).isNaN());
+    }
+
+    @Test
+    public void testIsInfinite() {
+        // act/assert
+        Assert.assertFalse(PolarCoordinates.of(1, 0).isInfinite());
+        Assert.assertFalse(PolarCoordinates.of(Double.NaN, Double.NaN).isInfinite());
+
+        Assert.assertTrue(PolarCoordinates.of(Double.POSITIVE_INFINITY, 0).isInfinite());
+        Assert.assertTrue(PolarCoordinates.of(Double.NEGATIVE_INFINITY, 0).isInfinite());
+        Assert.assertFalse(PolarCoordinates.of(Double.NEGATIVE_INFINITY, Double.NaN).isInfinite());
+
+        Assert.assertTrue(PolarCoordinates.of(0, Double.POSITIVE_INFINITY).isInfinite());
+        Assert.assertTrue(PolarCoordinates.of(0, Double.NEGATIVE_INFINITY).isInfinite());
+        Assert.assertFalse(PolarCoordinates.of(Double.NaN, Double.NEGATIVE_INFINITY).isInfinite());
+
+        Assert.assertTrue(PolarCoordinates.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).isInfinite());
+        Assert.assertTrue(PolarCoordinates.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY).isInfinite());
+    }
+
+    @Test
+    public void testHashCode() {
+        // arrange
+        PolarCoordinates a = PolarCoordinates.of(1, 2);
+        PolarCoordinates b = PolarCoordinates.of(10, 2);
+        PolarCoordinates c = PolarCoordinates.of(10, 20);
+        PolarCoordinates d = PolarCoordinates.of(1, 20);
+
+        PolarCoordinates e = PolarCoordinates.of(1, 2);
+
+        // act/assert
+        Assert.assertEquals(a.hashCode(), a.hashCode());
+        Assert.assertEquals(a.hashCode(), e.hashCode());
+
+        Assert.assertNotEquals(a.hashCode(), b.hashCode());
+        Assert.assertNotEquals(a.hashCode(), c.hashCode());
+        Assert.assertNotEquals(a.hashCode(), d.hashCode());
+    }
+
+    @Test
+    public void testHashCode_NaNInstancesHaveSameHashCode() {
+        // arrange
+        PolarCoordinates a = PolarCoordinates.of(1, Double.NaN);
+        PolarCoordinates b = PolarCoordinates.of(Double.NaN, 1);
+
+        // act/assert
+        Assert.assertEquals(a.hashCode(), b.hashCode());
+    }
+
+    @Test
+    public void testEquals() {
+        // arrange
+        PolarCoordinates a = PolarCoordinates.of(1, 2);
+        PolarCoordinates b = PolarCoordinates.of(10, 2);
+        PolarCoordinates c = PolarCoordinates.of(10, 20);
+        PolarCoordinates d = PolarCoordinates.of(1, 20);
+
+        PolarCoordinates e = PolarCoordinates.of(1, 2);
+
+        // act/assert
+        Assert.assertFalse(a.equals(null));
+        Assert.assertFalse(a.equals(new Object()));
+
+        Assert.assertTrue(a.equals(a));
+        Assert.assertTrue(a.equals(e));
+
+        Assert.assertFalse(a.equals(b));
+        Assert.assertFalse(a.equals(c));
+        Assert.assertFalse(a.equals(d));
+    }
+
+    @Test
+    public void testEquals_NaNInstancesEqual() {
+        // arrange
+        PolarCoordinates a = PolarCoordinates.of(1, Double.NaN);
+        PolarCoordinates b = PolarCoordinates.of(Double.NaN, 1);
+
+        // act/assert
+        Assert.assertTrue(a.equals(b));
+    }
+
+    @Test
+    public void testToVector() {
+        // arrange
+        double sqrt2 = Math.sqrt(2);
+
+        // act/assert
+        checkVector(PolarCoordinates.of(0, 0).toVector(), 0, 0);
+
+        checkVector(PolarCoordinates.of(1, 0).toVector(), 1, 0);
+        checkVector(PolarCoordinates.of(sqrt2, 0.25 * Geometry.PI).toVector(), 1, 1);
+        checkVector(PolarCoordinates.of(1, Geometry.HALF_PI).toVector(), 0, 1);
+
+        checkVector(PolarCoordinates.of(sqrt2, 0.75 * Geometry.PI).toVector(), -1, 1);
+        checkVector(PolarCoordinates.of(1, Geometry.PI).toVector(), -1, 0);
+        checkVector(PolarCoordinates.of(sqrt2, -0.75 * Geometry.PI).toVector(), -1, -1);
+
+        checkVector(PolarCoordinates.of(1, Geometry.MINUS_HALF_PI).toVector(), 0, -1);
+        checkVector(PolarCoordinates.of(sqrt2, -0.25 * Geometry.PI).toVector(), 1, -1);
+    }
+
+    @Test
+    public void testToPoint() {
+        // arrange
+        double sqrt2 = Math.sqrt(2);
+
+        // act/assert
+        checkPoint(PolarCoordinates.of(0, 0).toPoint(), 0, 0);
+
+        checkPoint(PolarCoordinates.of(1, 0).toPoint(), 1, 0);
+        checkPoint(PolarCoordinates.of(sqrt2, 0.25 * Geometry.PI).toPoint(), 1, 1);
+        checkPoint(PolarCoordinates.of(1, Geometry.HALF_PI).toPoint(), 0, 1);
+
+        checkPoint(PolarCoordinates.of(sqrt2, 0.75 * Geometry.PI).toPoint(), -1, 1);
+        checkPoint(PolarCoordinates.of(1, Geometry.PI).toPoint(), -1, 0);
+        checkPoint(PolarCoordinates.of(sqrt2, -0.75 * Geometry.PI).toPoint(), -1, -1);
+
+        checkPoint(PolarCoordinates.of(1, Geometry.MINUS_HALF_PI).toPoint(), 0, -1);
+        checkPoint(PolarCoordinates.of(sqrt2, -0.25 * Geometry.PI).toPoint(), 1, -1);
+    }
+
+    @Test
+    public void testToCartesian() {
+        // arrange
+        Coordinates.Factory2D<Point2D> factory = Point2D.getFactory();
+        double sqrt2 = Math.sqrt(2);
+
+        // act/assert
+        checkPoint(PolarCoordinates.of(0, 0).toCartesian(factory), 0, 0);
+
+        checkPoint(PolarCoordinates.of(1, 0).toCartesian(factory), 1, 0);
+        checkPoint(PolarCoordinates.of(sqrt2, 0.25 * Geometry.PI).toCartesian(factory), 1, 1);
+        checkPoint(PolarCoordinates.of(1, Geometry.HALF_PI).toCartesian(factory), 0, 1);
+
+        checkPoint(PolarCoordinates.of(sqrt2, 0.75 * Geometry.PI).toCartesian(factory), -1, 1);
+        checkPoint(PolarCoordinates.of(1, Geometry.PI).toCartesian(factory), -1, 0);
+        checkPoint(PolarCoordinates.of(sqrt2, -0.75 * Geometry.PI).toCartesian(factory), -1, -1);
+
+        checkPoint(PolarCoordinates.of(1, Geometry.MINUS_HALF_PI).toCartesian(factory), 0, -1);
+        checkPoint(PolarCoordinates.of(sqrt2, -0.25 * Geometry.PI).toCartesian(factory), 1, -1);
+    }
+
+    @Test
+    public void testToCartesian_static() {
+        // arrange
+        Coordinates.Factory2D<Point2D> factory = Point2D.getFactory();
+        double sqrt2 = Math.sqrt(2);
+
+        // act/assert
+        checkPoint(PolarCoordinates.toCartesian(0, 0, factory), 0, 0);
+
+        checkPoint(PolarCoordinates.toCartesian(1, 0, factory), 1, 0);
+        checkPoint(PolarCoordinates.toCartesian(sqrt2, 0.25 * Geometry.PI, factory), 1, 1);
+        checkPoint(PolarCoordinates.toCartesian(1, Geometry.HALF_PI, factory), 0, 1);
+
+        checkPoint(PolarCoordinates.toCartesian(sqrt2, 0.75 * Geometry.PI, factory), -1, 1);
+        checkPoint(PolarCoordinates.toCartesian(1, Geometry.PI, factory), -1, 0);
+        checkPoint(PolarCoordinates.toCartesian(sqrt2, -0.75 * Geometry.PI, factory), -1, -1);
+
+        checkPoint(PolarCoordinates.toCartesian(1, Geometry.MINUS_HALF_PI, factory), 0, -1);
+        checkPoint(PolarCoordinates.toCartesian(sqrt2, -0.25 * Geometry.PI, factory), 1, -1);
+    }
+
+    @Test
+    public void testToCartesian_static_NaNAndInfinite() {
+        // arrange
+        Coordinates.Factory2D<Point2D> factory = Point2D.getFactory();
+
+        // act/assert
+        Assert.assertTrue(PolarCoordinates.toCartesian(Double.NaN, 0, factory).isNaN());
+        Assert.assertTrue(PolarCoordinates.toCartesian(0, Double.NaN, factory).isNaN());
+
+        Assert.assertTrue(PolarCoordinates.toCartesian(Double.POSITIVE_INFINITY, 0, factory).isNaN());
+        Assert.assertTrue(PolarCoordinates.toCartesian(0, Double.POSITIVE_INFINITY, factory).isNaN());
+        Assert.assertTrue(PolarCoordinates.toCartesian(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, factory).isNaN());
+
+        Assert.assertTrue(PolarCoordinates.toCartesian(Double.NEGATIVE_INFINITY, 0, factory).isNaN());
+        Assert.assertTrue(PolarCoordinates.toCartesian(0, Double.NEGATIVE_INFINITY, factory).isNaN());
+        Assert.assertTrue(PolarCoordinates.toCartesian(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, factory).isNaN());
+    }
+
+    @Test
+    public void testToString() {
+        // arrange
+        PolarCoordinates polar = PolarCoordinates.of(1, 2);
+        Pattern pattern = Pattern.compile("\\(r=1.{0,2}, az=2.{0,2}\\)");
+
+        // act
+        String str = polar.toString();;
+
+        // assert
+        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                    pattern.matcher(str).matches());
+    }
+
+    @Test
+    public void testParse() {
+        // act/assert
+        checkPolar(PolarCoordinates.parse("(r=1, az=2)"), 1, 2);
+        checkPolar(PolarCoordinates.parse("( r= -1, az= 0.5 )"), 1, 0.5 - Geometry.PI);
+        checkPolar(PolarCoordinates.parse("( r=NaN, az= -Infinity )"), Double.NaN, Double.NEGATIVE_INFINITY);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testParse_failure() {
+        // act/assert
+        PolarCoordinates.parse("abc");
+    }
+
+    private void checkPolar(PolarCoordinates polar, double radius, double azimuth) {
+        Assert.assertEquals(radius, polar.getRadius(), EPS);
+        Assert.assertEquals(azimuth, polar.getAzimuth(), EPS);
+    }
+
+    private void checkVector(Vector2D v, double x, double y) {
+        Assert.assertEquals(x, v.getX(), EPS);
+        Assert.assertEquals(y, v.getY(), EPS);
+    }
+
+    private void checkPoint(Point2D p, double x, double y) {
+        Assert.assertEquals(x, p.getX(), EPS);
+        Assert.assertEquals(y, p.getY(), EPS);
+    }
+}
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 13208c0..dbd2a2a 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
@@ -485,6 +485,28 @@ public class Vector2DTest {
     }
 
     @Test
+    public void testOfPolar() {
+        // arrange
+        double eps = 1e-15;
+        double sqrt2 = Math.sqrt(2.0);
+
+        // act/assert
+        checkVector(Vector2D.ofPolar(0, 0), 0, 0, eps);
+        checkVector(Vector2D.ofPolar(1, 0), 1, 0, eps);
+
+        checkVector(Vector2D.ofPolar(2, Geometry.PI), -2, 0, eps);
+        checkVector(Vector2D.ofPolar(-2, Geometry.PI), 2, 0, eps);
+
+        checkVector(Vector2D.ofPolar(2, Geometry.HALF_PI), 0, 2, eps);
+        checkVector(Vector2D.ofPolar(-2, Geometry.HALF_PI), 0, -2, eps);
+
+        checkVector(Vector2D.ofPolar(2, 0.25 * Geometry.PI), sqrt2, sqrt2, eps);
+        checkVector(Vector2D.ofPolar(2, 0.75 * Geometry.PI), -sqrt2, sqrt2, eps);
+        checkVector(Vector2D.ofPolar(2, -0.25 * Geometry.PI), sqrt2, - sqrt2, eps);
+        checkVector(Vector2D.ofPolar(2, -0.75 * Geometry.PI), -sqrt2, - sqrt2, eps);
+    }
+
+    @Test
     public void testGetFactory() {
         // act
         Coordinates.Factory2D<Vector2D> factory = Vector2D.getFactory();
@@ -546,8 +568,12 @@ public class Vector2DTest {
     }
 
     private void checkVector(Vector2D v, double x, double y) {
-        Assert.assertEquals(x, v.getX(), EPS);
-        Assert.assertEquals(y, v.getY(), EPS);
+        checkVector(v, x, y, EPS);
+    }
+
+    private void checkVector(Vector2D v, double x, double y, double eps) {
+        Assert.assertEquals(x, v.getX(), eps);
+        Assert.assertEquals(y, v.getY(), eps);
     }
 
     private void checkPoint(Point2D p, double x, double y) {


[commons-geometry] 11/15: GEOMETRY-7: changing polar/spherical azimuth convention from (-pi, +pi] range to [0, 2pi) range in order to match S1Point and S2Point conventions

Posted by er...@apache.org.
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 c892bfe0ab781a3cebd80512ee3abbbfe775af7b
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Wed Jul 11 00:04:54 2018 -0400

    GEOMETRY-7: changing polar/spherical azimuth convention from (-pi, +pi] range to [0, 2pi) range in order to match S1Point and S2Point conventions
---
 .../org/apache/commons/geometry/core/Geometry.java |  7 ++++--
 .../apache/commons/geometry/core/GeometryTest.java |  2 ++
 .../euclidean/threed/SphericalCoordinates.java     | 10 ++++-----
 .../geometry/euclidean/twod/PolarCoordinates.java  | 24 ++++++++++-----------
 .../geometry/euclidean/threed/Cartesian3DTest.java |  4 ++--
 .../euclidean/threed/SphericalCoordinatesTest.java | 18 ++++++++--------
 .../geometry/euclidean/twod/Cartesian2DTest.java   |  6 +++---
 .../euclidean/twod/PolarCoordinatesTest.java       | 25 +++++++++++-----------
 .../commons/geometry/spherical/oned/S1Point.java   |  3 +--
 .../commons/geometry/spherical/twod/S2Point.java   | 10 ++-------
 .../geometry/spherical/twod/S2PointTest.java       | 12 +----------
 11 files changed, 54 insertions(+), 67 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 efe797d..d227755 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
@@ -32,12 +32,15 @@ public class Geometry {
     /** Constant value for {@code -2*pi}. */
     public static final double MINUS_TWO_PI = -2.0 * Math.PI;
 
-    /** Constant value for {@code pi / 2}. */
+    /** Constant value for {@code pi/2}. */
     public static final double HALF_PI = 0.5 * Math.PI;
 
-    /** Constant value for {@code - pi / 2}. */
+    /** Constant value for {@code - pi/2}. */
     public static final double MINUS_HALF_PI = - 0.5 * Math.PI;
 
+    /** Constant value for {@code  3*pi/2}. */
+    public static final double THREE_HALVES_PI = 1.5 * Math.PI;
+
     /** Private constructor */
     private Geometry() {}
 }
diff --git a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTest.java b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTest.java
index 88533eb..61fc872 100644
--- a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTest.java
+++ b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTest.java
@@ -35,6 +35,8 @@ public class GeometryTest {
 
         Assert.assertEquals(Math.PI / 2.0, Geometry.HALF_PI, 0.0);
         Assert.assertEquals(-Math.PI / 2.0, Geometry.MINUS_HALF_PI, eps);
+
+        Assert.assertEquals((3.0 * Math.PI) / 2.0, Geometry.THREE_HALVES_PI, eps);
     }
 
     @Test
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
index 37d7ea8..5be957d 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
@@ -62,7 +62,7 @@ import org.apache.commons.numbers.angle.PlaneAngleRadians;
  *
  * <p>In order to ensure the uniqueness of coordinate sets, coordinate values
  * are normalized so that {@code radius} is in the range {@code [0, +Infinity)},
- * {@code azimuth} is in the range {@code (-pi, pi]}, and {@code polar} is in the
+ * {@code azimuth} is in the range {@code [0, 2pi)}, and {@code polar} is in the
  * range {@code [0, pi]}.</p>
  *
  * @see <a href="https://en.wikipedia.org/wiki/Spherical_coordinate_system">Spherical Coordinate System</a>
@@ -117,7 +117,7 @@ public final class SphericalCoordinates implements Spatial, Serializable {
     }
 
     /** Return the azimuth angle in radians. This is the angle in the x-y plane measured counter-clockwise from
-     * the positive x axis. The angle is in the range {@code (-pi, pi]}.
+     * the positive x axis. The angle is in the range {@code [0, 2pi)}.
      * @return the azimuth angle in radians
      */
     public double getAzimuth() {
@@ -233,7 +233,7 @@ public final class SphericalCoordinates implements Spatial, Serializable {
 
     /** Return a new instance with the given spherical coordinate values. The values are normalized
      * so that {@code radius} lies in the range {@code [0, +Infinity)}, {@code azimuth} lies in the range
-     * {@code (-pi, +pi]}, and {@code polar} lies in the range {@code [0, +pi]}.
+     * {@code [0, 2pi)}, and {@code polar} lies in the range {@code [0, +pi]}.
      * @param radius the length of the line segment from the origin to the coordinate point.
      * @param azimuth the angle in the x-y plane, measured in radians counter-clockwise
      *      from the positive x-axis.
@@ -273,10 +273,10 @@ public final class SphericalCoordinates implements Spatial, Serializable {
         return SimpleCoordinateFormat.getPointFormat().parse(input, FACTORY);
     }
 
-    /** Normalize an azimuth value to be within the range {@code (-pi, +pi]}. This
+    /** Normalize an azimuth value to be within the range {@code [0, 2pi)}. This
      * is exactly equivalent to {@link PolarCoordinates#normalizeAzimuth(double)}.
      * @param azimuth azimuth value in radians
-     * @return equivalent azimuth value in the range {@code (-pi, +pi]}.
+     * @return equivalent azimuth value in the range {@code [0, 2pi)}.
      * @see PolarCoordinates#normalizeAzimuth(double)
      */
     public static double normalizeAzimuth(double azimuth) {
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
index 3ea6779..9dbbfa1 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
@@ -46,7 +46,7 @@ import org.apache.commons.numbers.angle.PlaneAngleRadians;
  * </p>
  * <p>In order to ensure the uniqueness of coordinate sets, coordinate values
  * are normalized so that {@code radius} is in the range {@code [0, +Infinity)}
- * and {@code azimuth} is in the range {@code (-pi, pi]}.</p>
+ * and {@code azimuth} is in the range {@code [0, 2pi)}.</p>
  *
  * @see <a href="https://en.wikipedia.org/wiki/Polar_coordinate_system">Polar Coordinate System</a>
  */
@@ -94,7 +94,7 @@ public final class PolarCoordinates implements Spatial, Serializable {
     }
 
     /** Return the azimuth angle in radians. The value will be
-     * in the range {@code (-pi, pi]}.
+     * in the range {@code [0, 2pi)}.
      * @return azimuth value in radians.
      */
     public double getAzimuth() {
@@ -203,8 +203,8 @@ public final class PolarCoordinates implements Spatial, Serializable {
     }
 
     /** Return a new instance with the given polar coordinate values.
-     * The values are normalized so that {@code radius} lies in the range {@code [0, +infinity)}
-     * and {@code azimuth} in the range {@code (-pi, pi]}.
+     * The values are normalized so that {@code radius} lies in the range {@code [0, +Infinity)}
+     * and {@code azimuth} in the range {@code [0, 2pi)}.
      * @param radius Radius value.
      * @param azimuth Azimuth angle in radians.
      * @return new {@link PolarCoordinates} instance
@@ -236,18 +236,18 @@ public final class PolarCoordinates implements Spatial, Serializable {
         return SimpleCoordinateFormat.getPointFormat().parse(input, FACTORY);
     }
 
-    /** Normalize an azimuth value to be within the range {@code (-pi, +pi]}.
+    /** Normalize an azimuth value to be within the range {@code [0, 2pi)}.
      * @param azimuth azimuth value in radians
-     * @return equivalent azimuth value in the range {@code (-pi, +pi]}.
+     * @return equivalent azimuth value in the range {@code [0, 2pi)}.
      */
     public static double normalizeAzimuth(double azimuth) {
-        if (Double.isFinite(azimuth) && (azimuth <= Geometry.MINUS_PI || azimuth > Geometry.PI)) {
-            azimuth = PlaneAngleRadians.normalizeBetweenMinusPiAndPi(azimuth);
+        if (Double.isFinite(azimuth) && (azimuth < 0.0 || azimuth >= Geometry.TWO_PI)) {
+            azimuth = PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(azimuth);
 
-            // azimuth is now in the range [-pi, pi] but we want it to be in the range
-            // (-pi, pi] in order to have completely unique coordinates
-            if (azimuth <= -Geometry.PI) {
-                azimuth += Geometry.TWO_PI;
+            // azimuth is now in the range [0, 2pi] but we want it to be in the range
+            // [0, 2pi) in order to have completely unique coordinates
+            if (azimuth >= Geometry.TWO_PI) {
+                azimuth -= Geometry.TWO_PI;
             }
         }
 
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
index 88547a9..6326f5e 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
@@ -47,13 +47,13 @@ public class Cartesian3DTest {
         checkSpherical(new StubCartesian3D(-0.1, 0, 0).toSpherical(), 0.1, Geometry.PI, Geometry.HALF_PI);
 
         checkSpherical(new StubCartesian3D(0, 0.1, 0).toSpherical(), 0.1, Geometry.HALF_PI, Geometry.HALF_PI);
-        checkSpherical(new StubCartesian3D(0, -0.1, 0).toSpherical(), 0.1, Geometry.MINUS_HALF_PI, Geometry.HALF_PI);
+        checkSpherical(new StubCartesian3D(0, -0.1, 0).toSpherical(), 0.1, Geometry.PI + Geometry.HALF_PI, Geometry.HALF_PI);
 
         checkSpherical(new StubCartesian3D(0, 0, 0.1).toSpherical(), 0.1, 0, 0);
         checkSpherical(new StubCartesian3D(0, 0, -0.1).toSpherical(), 0.1, 0, Geometry.PI);
 
         checkSpherical(new StubCartesian3D(1, 1, 1).toSpherical(), sqrt3, 0.25 * Geometry.PI, Math.acos(1 / sqrt3));
-        checkSpherical(new StubCartesian3D(-1, -1, -1).toSpherical(), sqrt3, -0.75 * Geometry.PI, Math.acos(-1 / sqrt3));
+        checkSpherical(new StubCartesian3D(-1, -1, -1).toSpherical(), sqrt3, 1.25 * Geometry.PI, Math.acos(-1 / sqrt3));
     }
 
     @Test
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
index 241093a..11057ed 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
@@ -41,7 +41,7 @@ public class SphericalCoordinatesTest {
         checkSpherical(SphericalCoordinates.of(1, Geometry.HALF_PI, Geometry.PI),
                 1, Geometry.HALF_PI, Geometry.PI);
         checkSpherical(SphericalCoordinates.of(1, Geometry.MINUS_HALF_PI, Geometry.HALF_PI),
-                1, Geometry.MINUS_HALF_PI, Geometry.HALF_PI);
+                1, Geometry.THREE_HALVES_PI, Geometry.HALF_PI);
     }
 
     @Test
@@ -50,7 +50,7 @@ public class SphericalCoordinatesTest {
         checkSpherical(SphericalCoordinates.of(2, Geometry.TWO_PI, 0), 2, 0, 0);
         checkSpherical(SphericalCoordinates.of(2, Geometry.HALF_PI + Geometry.TWO_PI, 0), 2, Geometry.HALF_PI, 0);
         checkSpherical(SphericalCoordinates.of(2, -Geometry.PI, 0), 2, Geometry.PI, 0);
-        checkSpherical(SphericalCoordinates.of(2, Geometry.PI * 1.5, 0), 2, Geometry.MINUS_HALF_PI, 0);
+        checkSpherical(SphericalCoordinates.of(2, Geometry.THREE_HALVES_PI, 0), 2, Geometry.THREE_HALVES_PI, 0);
     }
 
     @Test
@@ -95,10 +95,10 @@ public class SphericalCoordinatesTest {
         checkSpherical(SphericalCoordinates.of(-2, 0, 0), 2, Geometry.PI, Geometry.PI);
         checkSpherical(SphericalCoordinates.of(-2, Geometry.PI, Geometry.PI), 2, 0, 0);
 
-        checkSpherical(SphericalCoordinates.of(-3, Geometry.HALF_PI, QUARTER_PI), 3, Geometry.MINUS_HALF_PI, THREE_QUARTER_PI);
+        checkSpherical(SphericalCoordinates.of(-3, Geometry.HALF_PI, QUARTER_PI), 3, Geometry.THREE_HALVES_PI, THREE_QUARTER_PI);
         checkSpherical(SphericalCoordinates.of(-3, Geometry.MINUS_HALF_PI, THREE_QUARTER_PI), 3, Geometry.HALF_PI, QUARTER_PI);
 
-        checkSpherical(SphericalCoordinates.of(-4, QUARTER_PI, Geometry.HALF_PI), 4, MINUS_THREE_QUARTER_PI, Geometry.HALF_PI);
+        checkSpherical(SphericalCoordinates.of(-4, QUARTER_PI, Geometry.HALF_PI), 4, Geometry.PI + QUARTER_PI, Geometry.HALF_PI);
         checkSpherical(SphericalCoordinates.of(-4, MINUS_THREE_QUARTER_PI, Geometry.HALF_PI), 4, QUARTER_PI, Geometry.HALF_PI);
     }
 
@@ -125,13 +125,13 @@ public class SphericalCoordinatesTest {
         checkSpherical(SphericalCoordinates.ofCartesian(-0.1, 0, 0), 0.1, Geometry.PI, Geometry.HALF_PI);
 
         checkSpherical(SphericalCoordinates.ofCartesian(0, 0.1, 0), 0.1, Geometry.HALF_PI, Geometry.HALF_PI);
-        checkSpherical(SphericalCoordinates.ofCartesian(0, -0.1, 0), 0.1, Geometry.MINUS_HALF_PI, Geometry.HALF_PI);
+        checkSpherical(SphericalCoordinates.ofCartesian(0, -0.1, 0), 0.1, Geometry.THREE_HALVES_PI, Geometry.HALF_PI);
 
         checkSpherical(SphericalCoordinates.ofCartesian(0, 0, 0.1), 0.1, 0, 0);
         checkSpherical(SphericalCoordinates.ofCartesian(0, 0, -0.1), 0.1, 0, Geometry.PI);
 
         checkSpherical(SphericalCoordinates.ofCartesian(1, 1, 1), sqrt3, QUARTER_PI, Math.acos(1 / sqrt3));
-        checkSpherical(SphericalCoordinates.ofCartesian(-1, -1, -1), sqrt3, MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3));
+        checkSpherical(SphericalCoordinates.ofCartesian(-1, -1, -1), sqrt3, 1.25 * Geometry.PI, Math.acos(-1 / sqrt3));
     }
 
     @Test
@@ -343,7 +343,7 @@ public class SphericalCoordinatesTest {
     public void testParse() {
         // act/assert
         checkSpherical(SphericalCoordinates.parse("(1, 2, 3)"), 1, 2, 3);
-        checkSpherical(SphericalCoordinates.parse("(  -2.0 , 1 , -5e-1)"), 2, 1 - Geometry.PI, Geometry.PI - 0.5);
+        checkSpherical(SphericalCoordinates.parse("(  -2.0 , 1 , -5e-1)"), 2, 1 + Geometry.PI, Geometry.PI - 0.5);
         checkSpherical(SphericalCoordinates.parse("(NaN,Infinity,-Infinity)"), Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
     }
 
@@ -360,10 +360,10 @@ public class SphericalCoordinatesTest {
 
         Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.HALF_PI), Geometry.HALF_PI, EPS);
         Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.PI), Geometry.PI, EPS);
-        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.PI + Geometry.HALF_PI), Geometry.MINUS_HALF_PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.THREE_HALVES_PI), Geometry.THREE_HALVES_PI, EPS);
         Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.TWO_PI), 0.0, EPS);
 
-        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.MINUS_HALF_PI), Geometry.MINUS_HALF_PI, EPS);
+        Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(Geometry.MINUS_HALF_PI), Geometry.THREE_HALVES_PI, EPS);
         Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(-Geometry.PI), Geometry.PI, EPS);
         Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(-Geometry.PI - Geometry.HALF_PI), Geometry.HALF_PI, EPS);
         Assert.assertEquals(SphericalCoordinates.normalizeAzimuth(-Geometry.TWO_PI), 0.0, EPS);
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
index 379269f..261ad09 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
@@ -81,12 +81,12 @@ public class Cartesian2DTest {
         checkPolar(new StubCartesian2D(-1, 0).toPolar(), 1, Geometry.PI);
 
         checkPolar(new StubCartesian2D(0, 2).toPolar(), 2, Geometry.HALF_PI);
-        checkPolar(new StubCartesian2D(0, -2).toPolar(), 2, Geometry.MINUS_HALF_PI);
+        checkPolar(new StubCartesian2D(0, -2).toPolar(), 2, Geometry.THREE_HALVES_PI);
 
         checkPolar(new StubCartesian2D(sqrt2, sqrt2).toPolar(), 2, 0.25 * Geometry.PI);
         checkPolar(new StubCartesian2D(-sqrt2, sqrt2).toPolar(), 2, 0.75 * Geometry.PI);
-        checkPolar(new StubCartesian2D(sqrt2, -sqrt2).toPolar(), 2, -0.25 * Geometry.PI);
-        checkPolar(new StubCartesian2D(-sqrt2, -sqrt2).toPolar(), 2, -0.75 * Geometry.PI);
+        checkPolar(new StubCartesian2D(sqrt2, -sqrt2).toPolar(), 2, 1.75 * Geometry.PI);
+        checkPolar(new StubCartesian2D(-sqrt2, -sqrt2).toPolar(), 2, 1.25 * Geometry.PI);
     }
 
     @Test
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
index d8636f0..ce74382 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
@@ -35,7 +35,7 @@ public class PolarCoordinatesTest {
         checkPolar(PolarCoordinates.of(2, 0), 2, 0);
         checkPolar(PolarCoordinates.of(2, Geometry.HALF_PI), 2, Geometry.HALF_PI);
         checkPolar(PolarCoordinates.of(2, Geometry.PI), 2, Geometry.PI);
-        checkPolar(PolarCoordinates.of(2, Geometry.MINUS_HALF_PI), 2, Geometry.MINUS_HALF_PI);
+        checkPolar(PolarCoordinates.of(2, Geometry.MINUS_HALF_PI), 2, Geometry.THREE_HALVES_PI);
     }
 
     @Test
@@ -44,7 +44,7 @@ public class PolarCoordinatesTest {
         checkPolar(PolarCoordinates.of(2, Geometry.TWO_PI), 2, 0);
         checkPolar(PolarCoordinates.of(2, Geometry.HALF_PI + Geometry.TWO_PI), 2, Geometry.HALF_PI);
         checkPolar(PolarCoordinates.of(2, -Geometry.PI), 2, Geometry.PI);
-        checkPolar(PolarCoordinates.of(2, Geometry.PI * 1.5), 2, Geometry.MINUS_HALF_PI);
+        checkPolar(PolarCoordinates.of(2, -Geometry.PI * 1.5), 2, Geometry.HALF_PI);
     }
 
     @Test
@@ -58,9 +58,8 @@ public class PolarCoordinatesTest {
         checkAzimuthWrapAround(2, Geometry.PI - delta);
         checkAzimuthWrapAround(2, Geometry.PI);
 
-        checkAzimuthWrapAround(2, 0);
-        checkAzimuthWrapAround(2, -delta);
-        checkAzimuthWrapAround(2, delta - Geometry.PI);
+        checkAzimuthWrapAround(2, Geometry.THREE_HALVES_PI);
+        checkAzimuthWrapAround(2, Geometry.TWO_PI - delta);
     }
 
     private void checkAzimuthWrapAround(double radius, double azimuth) {
@@ -79,7 +78,7 @@ public class PolarCoordinatesTest {
     public void testOf_negativeRadius() {
         // act/assert
         checkPolar(PolarCoordinates.of(-1, 0), 1, Geometry.PI);
-        checkPolar(PolarCoordinates.of(-1e-6, Geometry.HALF_PI), 1e-6, Geometry.MINUS_HALF_PI);
+        checkPolar(PolarCoordinates.of(-1e-6, Geometry.HALF_PI), 1e-6, Geometry.THREE_HALVES_PI);
         checkPolar(PolarCoordinates.of(-2, Geometry.PI), 2, 0);
         checkPolar(PolarCoordinates.of(-3, Geometry.MINUS_HALF_PI), 3, Geometry.HALF_PI);
     }
@@ -110,10 +109,10 @@ public class PolarCoordinatesTest {
 
         checkPolar(PolarCoordinates.ofCartesian(-1, 1), sqrt2, 0.75 * Geometry.PI);
         checkPolar(PolarCoordinates.ofCartesian(-1, 0), 1, Geometry.PI);
-        checkPolar(PolarCoordinates.ofCartesian(-1, -1), sqrt2, - 0.75 * Geometry.PI);
+        checkPolar(PolarCoordinates.ofCartesian(-1, -1), sqrt2, 1.25 * Geometry.PI);
 
-        checkPolar(PolarCoordinates.ofCartesian(0, -1), 1, Geometry.MINUS_HALF_PI);
-        checkPolar(PolarCoordinates.ofCartesian(1, -1), sqrt2, -0.25 * Geometry.PI);
+        checkPolar(PolarCoordinates.ofCartesian(0, -1), 1, 1.5 * Geometry.PI);
+        checkPolar(PolarCoordinates.ofCartesian(1, -1), sqrt2, 1.75 * Geometry.PI);
     }
 
     @Test
@@ -333,7 +332,7 @@ public class PolarCoordinatesTest {
     public void testParse() {
         // act/assert
         checkPolar(PolarCoordinates.parse("(1, 2)"), 1, 2);
-        checkPolar(PolarCoordinates.parse("( -1 , 0.5 )"), 1, 0.5 - Geometry.PI);
+        checkPolar(PolarCoordinates.parse("( -1 , 0.5 )"), 1, 0.5 + Geometry.PI);
         checkPolar(PolarCoordinates.parse("(NaN,-Infinity)"), Double.NaN, Double.NEGATIVE_INFINITY);
     }
 
@@ -350,10 +349,10 @@ public class PolarCoordinatesTest {
 
         Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.HALF_PI), Geometry.HALF_PI, EPS);
         Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.PI), Geometry.PI, EPS);
-        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.PI + Geometry.HALF_PI), Geometry.MINUS_HALF_PI, EPS);
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.THREE_HALVES_PI), Geometry.THREE_HALVES_PI, EPS);
         Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.TWO_PI), 0.0, EPS);
 
-        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.MINUS_HALF_PI), Geometry.MINUS_HALF_PI, EPS);
+        Assert.assertEquals(PolarCoordinates.normalizeAzimuth(Geometry.MINUS_HALF_PI), Geometry.THREE_HALVES_PI, EPS);
         Assert.assertEquals(PolarCoordinates.normalizeAzimuth(-Geometry.PI), Geometry.PI, EPS);
         Assert.assertEquals(PolarCoordinates.normalizeAzimuth(-Geometry.PI - Geometry.HALF_PI), Geometry.HALF_PI, EPS);
         Assert.assertEquals(PolarCoordinates.normalizeAzimuth(-Geometry.TWO_PI), 0.0, EPS);
@@ -373,7 +372,7 @@ public class PolarCoordinatesTest {
         Coordinates.Factory2D<PolarCoordinates> factory = PolarCoordinates.getFactory();
 
         // assert
-        checkPolar(factory.create(-1, Geometry.HALF_PI), 1, Geometry.MINUS_HALF_PI);
+        checkPolar(factory.create(-1, Geometry.HALF_PI), 1, Geometry.THREE_HALVES_PI);
     }
 
     private void checkPolar(PolarCoordinates polar, double radius, double azimuth) {
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
index 4d3f87e..20602e7 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
@@ -23,7 +23,6 @@ import org.apache.commons.geometry.core.util.Coordinates;
 import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
 import org.apache.commons.geometry.euclidean.twod.PolarCoordinates;
 import org.apache.commons.geometry.euclidean.twod.Vector2D;
-import org.apache.commons.numbers.angle.PlaneAngleRadians;
 
 /** This class represents a point on the 1-sphere.
  * <p>Instances of this class are guaranteed to be immutable.</p>
@@ -59,7 +58,7 @@ public final class S1Point implements Point<S1Point>, Serializable {
      * @param vector corresponding vector
      */
     private S1Point(final double azimuth) {
-        this.azimuth  = PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(azimuth);
+        this.azimuth  = PolarCoordinates.normalizeAzimuth(azimuth);
         this.vector = Double.isFinite(azimuth) ? Vector2D.ofPolar(1.0, azimuth) : Vector2D.NaN;
     }
 
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
index 1b67205..0b3b191 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
@@ -79,11 +79,10 @@ public final class S2Point implements Point<S2Point>, Serializable {
      * @param azimuth azimuthal angle in the x-y plane
      * @param polar polar angle
      * @param vector corresponding vector; if null, the vector is computed
-     * @throws IllegalArgumentException
      */
     private S2Point(final double azimuth, final double polar, final Vector3D vector) {
-        this.azimuth = azimuth;
-        this.polar = polar;
+        this.azimuth = SphericalCoordinates.normalizeAzimuth(azimuth);
+        this.polar = SphericalCoordinates.normalizePolar(polar);
         this.vector = (vector != null) ? vector : Vector3D.ofSpherical(1.0, azimuth, polar);
     }
 
@@ -213,13 +212,8 @@ public final class S2Point implements Point<S2Point>, Serializable {
      * @return point instance with the given coordinates
      * @see #getAzimuth()
      * @see #getPolar()
-     * @exception IllegalArgumentException if polar is not in the {@code [0, +pi]} range
      */
     public static S2Point of(final double azimuth, final double polar) throws IllegalArgumentException {
-        if (polar < 0 || polar > Math.PI) {
-            throw new IllegalArgumentException(polar + " is out of [" + 0 + ", " + Math.PI + "] range");
-        }
-
         return new S2Point(azimuth, polar, null);
     }
 
diff --git a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
index 78f9097..462b0dd 100644
--- a/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
+++ b/commons-geometry-spherical/src/test/java/org/apache/commons/geometry/spherical/twod/S2PointTest.java
@@ -30,7 +30,7 @@ public class S2PointTest {
     public void testS2Point() {
         for (int k = -2; k < 3; ++k) {
             S2Point p = S2Point.of(1.0 + k * Geometry.TWO_PI, 1.4);
-            Assert.assertEquals(1.0 + k * Geometry.TWO_PI, p.getAzimuth(), EPS);
+            Assert.assertEquals(1.0, p.getAzimuth(), EPS);
             Assert.assertEquals(1.4, p.getPolar(), EPS);
             Assert.assertEquals(Math.cos(1.0) * Math.sin(1.4), p.getVector().getX(), EPS);
             Assert.assertEquals(Math.sin(1.0) * Math.sin(1.4), p.getVector().getY(), EPS);
@@ -39,16 +39,6 @@ public class S2PointTest {
         }
     }
 
-    @Test(expected=IllegalArgumentException.class)
-    public void testNegativePolarAngle() {
-        S2Point.of(1.0, -1.0);
-    }
-
-    @Test(expected=IllegalArgumentException.class)
-    public void testTooLargePolarAngle() {
-        S2Point.of(1.0, 3.5);
-    }
-
     @Test
     public void testNaN() {
         Assert.assertTrue(S2Point.NaN.isNaN());


[commons-geometry] 07/15: GEOMETRY-7: adding toSpherical() and ofSpherical() to Cartesian 3D classes

Posted by er...@apache.org.
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 f1a6d4b05b3cd6443236fe5e02ceb0f94c0a5f91
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Sun Jul 1 00:33:13 2018 -0400

    GEOMETRY-7: adding toSpherical() and ofSpherical() to Cartesian 3D classes
---
 .../geometry/euclidean/threed/Cartesian3D.java     |  7 ++++
 .../commons/geometry/euclidean/threed/Point3D.java | 11 ++++++
 .../geometry/euclidean/threed/Vector3D.java        | 22 ++++-------
 .../geometry/euclidean/threed/Cartesian3DTest.java | 29 ++++++++++++++
 .../geometry/euclidean/threed/Point3DTest.java     | 24 +++++++++++-
 .../euclidean/threed/SphericalCoordinatesTest.java |  4 +-
 .../geometry/euclidean/threed/Vector3DTest.java    | 44 +++++++++++-----------
 7 files changed, 102 insertions(+), 39 deletions(-)

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 6b619ac..af412f0 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
@@ -77,6 +77,13 @@ public abstract class Cartesian3D implements Spatial, Serializable {
         return new double[] { x, y, z };
     }
 
+    /** Return an equivalent set of coordinates in spherical form.
+     * @return an equivalent set of coordinates in spherical form.
+     */
+    public SphericalCoordinates toSpherical() {
+        return SphericalCoordinates.ofCartesian(x, y, z);
+    }
+
     /** {@inheritDoc} */
     @Override
     public int getDimension() {
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 f257425..6e4da3e 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
@@ -190,6 +190,17 @@ public final class Point3D extends Cartesian3D implements EuclideanPoint<Point3D
         return new Point3D(p[0], p[1], p[2]);
     }
 
+    /** Create a point from a set of spherical coordinates.
+     * @param radius the spherical radius value
+     * @param azimuth the angle in the x-y plane measured in radians counter-clockwise from the
+     *      positive x axis.
+     * @param polar the angle with the positive z axis in radians.
+     * @return a point instance with the given set of spherical coordinates
+     */
+    public static Point3D ofSpherical(double radius, double azimuth, double polar) {
+        return SphericalCoordinates.toCartesian(radius, azimuth, polar, FACTORY);
+    }
+
     /** Parses the given string and returns a new point instance. The expected string
      * format is the same as that returned by {@link #toString()}.
      * @param str the string to parse
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 7d8c5e8..eeeeb72 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
@@ -437,21 +437,15 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point
         return new Vector3D(v[0], v[1], v[2]);
     }
 
-    /** Builds a vector from its azimuthal coordinates
-     * @param alpha azimuth (&alpha;) around Z
-     *              (0 is +X, &pi;/2 is +Y, &pi; is -X and 3&pi;/2 is -Y)
-     * @param delta elevation (&delta;) above (XY) plane, from -&pi;/2 to +&pi;/2
-     * @see #getAlpha()
-     * @see #getDelta()
-     * @return new vector instance with the given azimuthal coordinates
+    /** Create a vector from a set of spherical coordinates.
+     * @param radius the spherical radius value
+     * @param azimuth the angle in the x-y plane measured in radians counter-clockwise from the
+     *      positive x axis.
+     * @param polar the angle with the positive z axis in radians.
+     * @return a vector instance with the given set of spherical coordinates
      */
-    public static Vector3D fromSpherical(double alpha, double delta) {
-        double cosDelta = Math.cos(delta);
-        double x = Math.cos(alpha) * cosDelta;
-        double y = Math.sin(alpha) * cosDelta;
-        double z = Math.sin(delta);
-
-        return new Vector3D(x, y, z);
+    public static Vector3D ofSpherical(double radius, double azimuth, double polar) {
+        return SphericalCoordinates.toCartesian(radius, azimuth, polar, FACTORY);
     }
 
     /** Parses the given string and returns a new vector instance. The expected string
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
index 4e6205d..88547a9 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
@@ -1,5 +1,6 @@
 package org.apache.commons.geometry.euclidean.threed;
 
+import org.apache.commons.geometry.core.Geometry;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -33,6 +34,28 @@ public class Cartesian3DTest {
         Assert.assertEquals(3.0, arr[2], TEST_TOLERANCE);
     }
 
+
+    @Test
+    public void testToSpherical() {
+        // arrange
+        double sqrt3 = Math.sqrt(3);
+
+        // act/assert
+        checkSpherical(new StubCartesian3D(0, 0, 0).toSpherical(), 0, 0, 0);
+
+        checkSpherical(new StubCartesian3D(0.1, 0, 0).toSpherical(), 0.1, 0, Geometry.HALF_PI);
+        checkSpherical(new StubCartesian3D(-0.1, 0, 0).toSpherical(), 0.1, Geometry.PI, Geometry.HALF_PI);
+
+        checkSpherical(new StubCartesian3D(0, 0.1, 0).toSpherical(), 0.1, Geometry.HALF_PI, Geometry.HALF_PI);
+        checkSpherical(new StubCartesian3D(0, -0.1, 0).toSpherical(), 0.1, Geometry.MINUS_HALF_PI, Geometry.HALF_PI);
+
+        checkSpherical(new StubCartesian3D(0, 0, 0.1).toSpherical(), 0.1, 0, 0);
+        checkSpherical(new StubCartesian3D(0, 0, -0.1).toSpherical(), 0.1, 0, Geometry.PI);
+
+        checkSpherical(new StubCartesian3D(1, 1, 1).toSpherical(), sqrt3, 0.25 * Geometry.PI, Math.acos(1 / sqrt3));
+        checkSpherical(new StubCartesian3D(-1, -1, -1).toSpherical(), sqrt3, -0.75 * Geometry.PI, Math.acos(-1 / sqrt3));
+    }
+
     @Test
     public void testDimension() {
         // arrange
@@ -73,6 +96,12 @@ public class Cartesian3DTest {
         Assert.assertFalse(new StubCartesian3D(0, Double.NaN, Double.POSITIVE_INFINITY).isInfinite());
     }
 
+    private void checkSpherical(SphericalCoordinates c, double radius, double azimuth, double polar) {
+        Assert.assertEquals(radius, c.getRadius(), TEST_TOLERANCE);
+        Assert.assertEquals(azimuth, c.getAzimuth(), TEST_TOLERANCE);
+        Assert.assertEquals(polar, c.getPolar(), TEST_TOLERANCE);
+    }
+
     private static class StubCartesian3D extends Cartesian3D {
         private static final long serialVersionUID = 1L;
 
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
index d20d918..770cf25 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
@@ -18,6 +18,7 @@ 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.util.Coordinates;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
@@ -25,7 +26,7 @@ import org.junit.Test;
 
 public class Point3DTest {
 
-    private static final double EPS = Math.ulp(1d);
+    private static final double EPS = 1e-15;
 
     @Test
     public void testConstants() {
@@ -243,6 +244,27 @@ public class Point3DTest {
     }
 
     @Test
+    public void testOfSpherical() {
+     // arrange
+        double sqrt3 = Math.sqrt(3);
+
+        // act/assert
+        checkPoint(Point3D.ofSpherical(0, 0, 0), 0, 0, 0);
+
+        checkPoint(Point3D.ofSpherical(1, 0, Geometry.HALF_PI), 1, 0, 0);
+        checkPoint(Point3D.ofSpherical(1, Geometry.PI, Geometry.HALF_PI), -1, 0, 0);
+
+        checkPoint(Point3D.ofSpherical(2, Geometry.HALF_PI, Geometry.HALF_PI), 0, 2, 0);
+        checkPoint(Point3D.ofSpherical(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI), 0, -2, 0);
+
+        checkPoint(Point3D.ofSpherical(3, 0, 0), 0, 0, 3);
+        checkPoint(Point3D.ofSpherical(3, 0, Geometry.PI), 0, 0, -3);
+
+        checkPoint(Point3D.ofSpherical(sqrt3, 0.25 * Geometry.PI, Math.acos(1 / sqrt3)), 1, 1, 1);
+        checkPoint(Point3D.ofSpherical(sqrt3, -0.75 * Geometry.PI, Math.acos(-1 / sqrt3)), -1, -1, -1);
+    }
+
+    @Test
     public void testGetFactory() {
         // act
         Coordinates.Factory3D<Point3D> factory = Point3D.getFactory();
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
index 37d381d..1acfde7 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
@@ -173,8 +173,8 @@ public class SphericalCoordinatesTest {
         checkVector(SphericalCoordinates.of(3, 0, 0).toVector(), 0, 0, 3);
         checkVector(SphericalCoordinates.of(3, 0, Geometry.PI).toVector(), 0, 0, -3);
 
-        checkVector(SphericalCoordinates.of(Math.sqrt(3), QUARTER_PI, Math.acos(1 / sqrt3)).toVector(), 1, 1, 1);
-        checkVector(SphericalCoordinates.of(Math.sqrt(3), MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3)).toVector(), -1, -1, -1);
+        checkVector(SphericalCoordinates.of(sqrt3, QUARTER_PI, Math.acos(1 / sqrt3)).toVector(), 1, 1, 1);
+        checkVector(SphericalCoordinates.of(sqrt3, MINUS_THREE_QUARTER_PI, Math.acos(-1 / sqrt3)).toVector(), -1, -1, -1);
     }
 
     @Test
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 86bf0a3..d52d358 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
@@ -29,7 +29,7 @@ import org.junit.Test;
 
 public class Vector3DTest {
 
-    private static final double EPS = Math.ulp(1d);
+    private static final double EPS = 1e-15;
 
     @Test
     public void testConstants() {
@@ -689,6 +689,27 @@ public class Vector3DTest {
     }
 
     @Test
+    public void testOfSpherical() {
+     // arrange
+        double sqrt3 = Math.sqrt(3);
+
+        // act/assert
+        checkVector(Vector3D.ofSpherical(0, 0, 0), 0, 0, 0);
+
+        checkVector(Vector3D.ofSpherical(1, 0, Geometry.HALF_PI), 1, 0, 0);
+        checkVector(Vector3D.ofSpherical(1, Geometry.PI, Geometry.HALF_PI), -1, 0, 0);
+
+        checkVector(Vector3D.ofSpherical(2, Geometry.HALF_PI, Geometry.HALF_PI), 0, 2, 0);
+        checkVector(Vector3D.ofSpherical(2, Geometry.MINUS_HALF_PI, Geometry.HALF_PI), 0, -2, 0);
+
+        checkVector(Vector3D.ofSpherical(3, 0, 0), 0, 0, 3);
+        checkVector(Vector3D.ofSpherical(3, 0, Geometry.PI), 0, 0, -3);
+
+        checkVector(Vector3D.ofSpherical(sqrt3, 0.25 * Geometry.PI, Math.acos(1 / sqrt3)), 1, 1, 1);
+        checkVector(Vector3D.ofSpherical(sqrt3, -0.75 * Geometry.PI, Math.acos(-1 / sqrt3)), -1, -1, -1);
+    }
+
+    @Test
     public void testGetFactory() {
         // act
         Coordinates.Factory3D<Vector3D> factory = Vector3D.getFactory();
@@ -749,27 +770,6 @@ public class Vector3DTest {
         checkVector(Vector3D.linearCombination(-3, p1, 2, p2, -4, p3, 5, p4), -64, -78, -2);
     }
 
-    @Test
-    public void testConstructors() {
-        double r = Math.sqrt(2) /2;
-        checkVector(Vector3D.linearCombination(2, Vector3D.fromSpherical(Math.PI / 3, -Math.PI / 4)),
-                    r, r * Math.sqrt(3), -2 * r);
-        checkVector(Vector3D.linearCombination(2, Vector3D.PLUS_X,
-                                 -3, Vector3D.MINUS_Z),
-                    2, 0, 3);
-        checkVector(Vector3D.linearCombination(2, Vector3D.PLUS_X,
-                                 5, Vector3D.PLUS_Y,
-                                 -3, Vector3D.MINUS_Z),
-                    2, 5, 3);
-        checkVector(Vector3D.linearCombination(2, Vector3D.PLUS_X,
-                                 5, Vector3D.PLUS_Y,
-                                 5, Vector3D.MINUS_Y,
-                                 -3, Vector3D.MINUS_Z),
-                    2, 0, 3);
-        checkVector(Vector3D.of(new double[] { 2,  5,  -3 }),
-                    2, 5, -3);
-    }
-
     private void checkVector(Vector3D v, double x, double y, double z) {
         Assert.assertEquals(x, v.getX(), EPS);
         Assert.assertEquals(y, v.getY(), EPS);


[commons-geometry] 13/15: GEOMETRY-7: fixing Javadoc and CheckStyle issues

Posted by er...@apache.org.
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 6dc7b9bbea81086358dca033d4faaec4873de6aa
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Fri Jul 20 23:42:11 2018 -0400

    GEOMETRY-7: fixing Javadoc and CheckStyle issues
---
 .../org/apache/commons/geometry/core/Geometry.java    |  2 +-
 .../commons/geometry/euclidean/threed/Point3D.java    |  8 ++++----
 .../euclidean/threed/SphericalCoordinates.java        | 19 +++++++++----------
 .../commons/geometry/euclidean/threed/Vector3D.java   | 16 ++++++++--------
 .../commons/geometry/euclidean/twod/Point2D.java      |  8 ++++----
 .../geometry/euclidean/twod/PolarCoordinates.java     |  8 ++++----
 .../commons/geometry/euclidean/twod/Vector2D.java     | 14 +++++++-------
 .../commons/geometry/spherical/oned/S1Point.java      |  1 -
 .../commons/geometry/spherical/twod/S2Point.java      |  2 +-
 9 files changed, 38 insertions(+), 40 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 d227755..fe319c4 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-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 5ddb722..e099130 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
@@ -43,11 +43,8 @@ public final class Point3D extends Cartesian3D implements EuclideanPoint<Point3D
     public static final Point3D NEGATIVE_INFINITY =
         new Point3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
 
-    /** Serializable version identifier. */
-    private static final long serialVersionUID = 20180710L;
-
     /** Package private factory for delegating instance creation. */
-    static DoubleFunction3N<Point3D> FACTORY = new DoubleFunction3N<Point3D>() {
+    static final DoubleFunction3N<Point3D> FACTORY = new DoubleFunction3N<Point3D>() {
 
         /** {@inheritDoc} */
         @Override
@@ -56,6 +53,9 @@ public final class Point3D extends Cartesian3D implements EuclideanPoint<Point3D
         }
     };
 
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 20180710L;
+
     /** Simple constructor.
      * Build a point from its coordinates
      * @param x abscissa
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
index 02ace69..86b664c 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
@@ -30,10 +30,10 @@ import org.apache.commons.numbers.angle.PlaneAngleRadians;
  *
  * <p>Spherical coordinates for a point are defined by three values:
  * <ol>
- * 	<li><em>Radius</em> - The distance from the point to a fixed referenced point.</li>
- * 	<li><em>Azimuth angle</em> - The angle measured from a fixed reference direction in a plane to
+ *  <li><em>Radius</em> - The distance from the point to a fixed referenced point.</li>
+ *  <li><em>Azimuth angle</em> - The angle measured from a fixed reference direction in a plane to
  * the orthogonal projection of the point on that plane.</li>
- *	<li><em>Polar angle</em> - The angle measured from a fixed zenith direction to the point. The zenith
+ *  <li><em>Polar angle</em> - The angle measured from a fixed zenith direction to the point. The zenith
  *direction must be orthogonal to the reference plane.</li>
  * </ol>
  * This class follows the convention of using the origin as the reference point; the positive x-axis as the
@@ -45,20 +45,18 @@ import org.apache.commons.numbers.angle.PlaneAngleRadians;
  * y = r sin(&theta;) sin(&Phi;)
  * z = r cos(&Phi;)
  *
- * r = &radic;(x<sup>2</sup>+y<sup>2</sup>+z<sup>2</sup>)
+ * r = &radic;(x^2 + y^2 + z^2)
  * &theta; = atan2(y, x)
  * &Phi; = acos(z/r)
  * </pre>
  * where <em>r</em> is the radius, <em>&theta;</em> is the azimuth angle, and <em>&Phi;</em> is the polar angle
  * of the spherical coordinates.
- * </p>
  *
  * <p>There are numerous, competing conventions for the symbols used to represent spherical coordinate values. For
  * example, the mathematical convention is to use <em>(r, &theta;, &Phi;)</em> to represent radius, azimuth angle, and
  * polar angle, whereas the physics convention flips the angle values and uses <em>(r, &Phi;, &theta;)</em>. As such,
  * this class avoids the use of these symbols altogether in favor of the less ambiguous formal names of the values,
- * e.g. {@code radius}, {@code azimuth}, and {@code polar}.
- * </p>
+ * e.g. {@code radius}, {@code azimuth}, and {@code polar}.</p>
  *
  * <p>In order to ensure the uniqueness of coordinate sets, coordinate values
  * are normalized so that {@code radius} is in the range {@code [0, +Infinity)},
@@ -186,9 +184,9 @@ public final class SphericalCoordinates implements Spatial, Serializable {
      * </p>
      * <p>
      * <code>NaN</code> values are considered to globally affect the coordinates
-     * and be equal to each other - i.e, if either (or all) values of the
-     * coordinate set are equal to <code>Double.NaN</code>, the set is equal to
-     * {@link #NaN}.
+     * and be equal to each other - i.e, if any (or all) values of the
+     * coordinate set are equal to <code>Double.NaN</code>, the set as a whole
+     * is considered to equal NaN.
      * </p>
      *
      * @param other Object to test for equality to this
@@ -293,6 +291,7 @@ public final class SphericalCoordinates implements Spatial, Serializable {
     /** Package private method to convert the given set of spherical coordinates to
      * Cartesian coordinates. The Cartesian coordinates are computed and passed to the given
      * factory instance. The factory's return value is returned.
+     * @param <T> Factory return type.
      * @param radius The spherical radius value.
      * @param azimuth The spherical azimuth angle in radians.
      * @param polar The spherical polar angle in radians.
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 c35b2d0..078a5d3 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
@@ -47,7 +47,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
@@ -60,14 +60,8 @@ public final class Vector3D extends Cartesian3D implements EuclideanVector<Point
     public static final Vector3D NEGATIVE_INFINITY =
         new Vector3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
 
-    /** Serializable UID */
-    private static final long serialVersionUID = 20180710L;
-
-    /** Error message when norms are zero. */
-    private static final String ZERO_NORM_MSG = "Norm is zero";
-
     /** Package private factory for delegating instance creation. */
-    static DoubleFunction3N<Vector3D> FACTORY = new DoubleFunction3N<Vector3D>() {
+    static final DoubleFunction3N<Vector3D> FACTORY = new DoubleFunction3N<Vector3D>() {
 
         /** {@inheritDoc} */
         @Override
@@ -76,6 +70,12 @@ 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";
+
     /** Simple constructor.
      * Build a vector from its coordinates
      * @param x abscissa
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 4009398..94c2272 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
@@ -42,11 +42,8 @@ public final class Point2D extends Cartesian2D implements EuclideanPoint<Point2D
     public static final Point2D NEGATIVE_INFINITY =
         new Point2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
 
-    /** Serializable UID. */
-    private static final long serialVersionUID = 20180710L;
-
     /** Package private factory for delegating instance creation. */
-    static DoubleFunction2N<Point2D> FACTORY = new DoubleFunction2N<Point2D>() {
+    static final DoubleFunction2N<Point2D> FACTORY = new DoubleFunction2N<Point2D>() {
 
         /** {@inheritDoc} */
         @Override
@@ -55,6 +52,9 @@ public final class Point2D extends Cartesian2D implements EuclideanPoint<Point2D
         }
     };
 
+    /** Serializable UID. */
+    private static final long serialVersionUID = 20180710L;
+
     /** Simple constructor.
      * Build a point from its coordinates
      * @param x abscissa
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
index d1c6aca..c910629 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
@@ -39,11 +39,11 @@ import org.apache.commons.numbers.angle.PlaneAngleRadians;
  * x = r * cos(&theta;)
  * y = r * sin(&theta;)
  *
- * r = &radic;(x<sup>2</sup> + y<sup>2</sup>)
+ * r = &radic;(x^2 + y^2)
  * &theta; = atan2(y, x)
  * </pre>
  * where <em>r</em> is the radius and <em>&theta;</em> is the azimuth of the polar coordinates.
- * </p>
+ *
  * <p>In order to ensure the uniqueness of coordinate sets, coordinate values
  * are normalized so that {@code radius} is in the range {@code [0, +Infinity)}
  * and {@code azimuth} is in the range {@code [0, 2pi)}.</p>
@@ -158,8 +158,8 @@ public final class PolarCoordinates implements Spatial, Serializable {
      * <p>
      * <code>NaN</code> values are considered to globally affect the coordinates
      * and be equal to each other - i.e, if either (or all) values of the
-     * coordinate set are equal to <code>Double.NaN</code>, the set is equal to
-     * {@link #NaN}.
+     * coordinate set are equal to <code>Double.NaN</code>, the set as a whole is
+     * considered to equal <code>NaN</code>.
      * </p>
      *
      * @param other Object to test for equality to this
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 6d8f10e..36c300e 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
@@ -54,14 +54,8 @@ public final class Vector2D extends Cartesian2D implements EuclideanVector<Point
     public static final Vector2D NEGATIVE_INFINITY =
         new Vector2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
 
-    /** Serializable UID */
-    private static final long serialVersionUID = 20180710L;
-
-    /** Error message when norms are zero. */
-    private static final String ZERO_NORM_MSG = "Norm is zero";
-
     /** Package private factory for delegating instance creation. */
-    static DoubleFunction2N<Vector2D> FACTORY = new DoubleFunction2N<Vector2D>() {
+    static final DoubleFunction2N<Vector2D> FACTORY = new DoubleFunction2N<Vector2D>() {
 
         /** {@inheritDoc} */
         @Override
@@ -70,6 +64,12 @@ 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";
+
     /** Simple constructor.
      * @param x abscissa (first coordinate)
      * @param y ordinate (second coordinate)
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
index d18b5a9..a9df1aa 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/oned/S1Point.java
@@ -55,7 +55,6 @@ public final class S1Point implements Point<S1Point>, Serializable {
 
     /** Build a point from its internal components.
      * @param azimuth azimuthal angle
-     * @param vector corresponding vector
      */
     private S1Point(final double azimuth) {
         this.azimuth  = PolarCoordinates.normalizeAzimuth(azimuth);
diff --git a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
index 19cf154..1031b4b 100644
--- a/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
+++ b/commons-geometry-spherical/src/main/java/org/apache/commons/geometry/spherical/twod/S2Point.java
@@ -212,7 +212,7 @@ public final class S2Point implements Point<S2Point>, Serializable {
      * @see #getAzimuth()
      * @see #getPolar()
      */
-    public static S2Point of(final double azimuth, final double polar) throws IllegalArgumentException {
+    public static S2Point of(final double azimuth, final double polar) {
         return new S2Point(azimuth, polar, null);
     }
 


[commons-geometry] 05/15: GEOMETRY-7: simplifying PolarCoordinate toString/parse representation

Posted by er...@apache.org.
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 d2b0c0219ccbfdd80bfa6b1ad7329b2ee8894c5b
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Sat Jun 30 22:41:19 2018 -0400

    GEOMETRY-7: simplifying PolarCoordinate toString/parse representation
---
 .../euclidean/threed/SphericalCoordinates.java     |  1 -
 .../geometry/euclidean/twod/PolarCoordinates.java  | 88 +++++-----------------
 .../euclidean/threed/SphericalCoordinatesTest.java |  5 +-
 .../euclidean/twod/PolarCoordinatesTest.java       | 17 ++++-
 4 files changed, 34 insertions(+), 77 deletions(-)

diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
index 609e90b..6b38049 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinates.java
@@ -39,7 +39,6 @@ public class SphericalCoordinates implements Spatial, Serializable {
         public SphericalCoordinates create(double a1, double a2, double a3) {
             return new SphericalCoordinates(a1, a2, a3);
         }
-
     };
 
     /** Radius value. */
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
index 681f646..da80e59 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinates.java
@@ -17,12 +17,11 @@
 package org.apache.commons.geometry.euclidean.twod;
 
 import java.io.Serializable;
-import java.text.ParsePosition;
 
 import org.apache.commons.geometry.core.Geometry;
 import org.apache.commons.geometry.core.Spatial;
-import org.apache.commons.geometry.core.util.AbstractCoordinateParser;
 import org.apache.commons.geometry.core.util.Coordinates;
+import org.apache.commons.geometry.core.util.SimpleCoordinateFormat;
 import org.apache.commons.numbers.angle.PlaneAngleRadians;
 
 /** Class representing a set of polar coordinates in 2 dimensional
@@ -35,8 +34,15 @@ public class PolarCoordinates implements Spatial, Serializable {
     /** Serializable version UID */
     private static final long serialVersionUID = 20180630L;
 
-    /** Shared parser/formatter instance **/
-    private static final PolarCoordinatesParser PARSER = new PolarCoordinatesParser();
+    /** Factory object for delegating instance creation. */
+    private static final Coordinates.Factory2D<PolarCoordinates> FACTORY = new Coordinates.Factory2D<PolarCoordinates>() {
+
+        /** {@inheritDoc} */
+        @Override
+        public PolarCoordinates create(double a1, double a2) {
+            return new PolarCoordinates(a1, a2);
+        }
+    };
 
     /** Radius value */
     private final double radius;
@@ -183,7 +189,7 @@ public class PolarCoordinates implements Spatial, Serializable {
     /** {@inheritDoc} */
     @Override
     public String toString() {
-        return PARSER.format(this);
+        return SimpleCoordinateFormat.getPointFormat().format(radius, azimuth);
     }
 
     /** Return a new polar coordinate instance with the given values.
@@ -191,7 +197,7 @@ public class PolarCoordinates implements Spatial, Serializable {
      * and azimuth in the range {@code (-pi, pi]}.
      * @param radius Radius value.
      * @param azimuth Azimuth angle in radians.
-     * @return
+     * @return new {@link PolarCoordinates} instance
      */
     public static PolarCoordinates of(double radius, double azimuth) {
         return new PolarCoordinates(radius, azimuth);
@@ -210,15 +216,14 @@ public class PolarCoordinates implements Spatial, Serializable {
     }
 
     /** Parse the given string and return a new polar coordinates instance. The parsed
-     * coordinates are normalized so that radius is within the range {@code [0, +infinity)}
-     * and azimuth is within the range {@code (-pi, pi]}. The expected string
+     * coordinates are normalized as in the {@link #of(double, double)} method. The expected string
      * format is the same as that returned by {@link #toString()}.
      * @param input the string to parse
      * @return new {@link PolarCoordinates} instance
      * @throws IllegalArgumentException if the string format is invalid.
      */
     public static PolarCoordinates parse(String input) {
-        return PARSER.parse(input);
+        return SimpleCoordinateFormat.getPointFormat().parse(input, FACTORY);
     }
 
     /** Convert the given set of polar coordinates to Cartesian coordinates.
@@ -238,65 +243,10 @@ public class PolarCoordinates implements Spatial, Serializable {
         return factory.create(x, y);
     }
 
-    /** Parser and formatter class for polar coordinates. */
-    private static class PolarCoordinatesParser extends AbstractCoordinateParser {
-
-        /** String prefix for the radius value. */
-        private static final String RADIUS_PREFIX = "r=";
-
-        /** String prefix for the azimuth value. */
-        private static final String AZIMUTH_PREFIX = "az=";
-
-        /** Simple constructor. */
-        private PolarCoordinatesParser() {
-            super(",", "(", ")");
-        }
-
-        /** Return a standardized string representation of the given set of polar
-         * coordinates.
-         * @param polar coordinates to format
-         * @return a standard string representation of the polar coordinates
-         */
-        public String format(PolarCoordinates polar) {
-            final StringBuilder sb = new StringBuilder();
-
-            sb.append(getPrefix());
-
-            sb.append(RADIUS_PREFIX);
-            sb.append(polar.getRadius());
-
-            sb.append(getSeparator());
-            sb.append(" ");
-
-            sb.append(AZIMUTH_PREFIX);
-            sb.append(polar.getAzimuth());
-
-            sb.append(getSuffix());
-
-            return sb.toString();
-        }
-
-        /** Parse the given string and return a set of standardized polar coordinates.
-         * @param str the string to parse
-         * @return polar coordinates
-         */
-        public PolarCoordinates parse(String str) {
-            final ParsePosition pos = new ParsePosition(0);
-
-            readPrefix(str, pos);
-
-            consumeWhitespace(str, pos);
-            readSequence(str, RADIUS_PREFIX, pos);
-            final double radius = readCoordinateValue(str, pos);
-
-            consumeWhitespace(str, pos);
-            readSequence(str, AZIMUTH_PREFIX, pos);
-            final double azimuth = readCoordinateValue(str, pos);
-
-            readSuffix(str, pos);
-            endParse(str, pos);
-
-            return new PolarCoordinates(radius, azimuth);
-        }
+    /** Return a factory object for creating new {@link PolarCoordinates} instances.
+     * @return factory object for creating new instances.
+     */
+    public static Coordinates.Factory2D<PolarCoordinates> getFactory() {
+        return FACTORY;
     }
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
index 8b1f51a..d1d9f01 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SphericalCoordinatesTest.java
@@ -4,7 +4,6 @@ import java.util.regex.Pattern;
 
 import org.apache.commons.geometry.core.Geometry;
 import org.apache.commons.geometry.core.util.Coordinates;
-import org.apache.commons.geometry.euclidean.twod.PolarCoordinates;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -328,8 +327,8 @@ public class SphericalCoordinatesTest {
     @Test
     public void testParse() {
         // act/assert
-        checkSpherical(SphericalCoordinates.parse("(1, 2, -3)"), 1, 2, 3);
-        checkSpherical(SphericalCoordinates.parse("(  2e0 , 5 , -0.000 )"), 2, 5 - Geometry.TWO_PI, 0);
+        checkSpherical(SphericalCoordinates.parse("(1, 2, 3)"), 1, 2, 3);
+        checkSpherical(SphericalCoordinates.parse("(  -2.0 , 1 , -5e-1)"), 2, 1 - Geometry.PI, Geometry.PI - 0.5);
         checkSpherical(SphericalCoordinates.parse("(NaN,Infinity,-Infinity)"), Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
     }
 
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
index db5da3b..2b28c19 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolarCoordinatesTest.java
@@ -319,7 +319,7 @@ public class PolarCoordinatesTest {
     public void testToString() {
         // arrange
         PolarCoordinates polar = PolarCoordinates.of(1, 2);
-        Pattern pattern = Pattern.compile("\\(r=1.{0,2}, az=2.{0,2}\\)");
+        Pattern pattern = Pattern.compile("\\(1.{0,2}, 2.{0,2}\\)");
 
         // act
         String str = polar.toString();;
@@ -332,9 +332,9 @@ public class PolarCoordinatesTest {
     @Test
     public void testParse() {
         // act/assert
-        checkPolar(PolarCoordinates.parse("(r=1, az=2)"), 1, 2);
-        checkPolar(PolarCoordinates.parse("( r= -1, az= 0.5 )"), 1, 0.5 - Geometry.PI);
-        checkPolar(PolarCoordinates.parse("( r=NaN, az= -Infinity )"), Double.NaN, Double.NEGATIVE_INFINITY);
+        checkPolar(PolarCoordinates.parse("(1, 2)"), 1, 2);
+        checkPolar(PolarCoordinates.parse("( -1 , 0.5 )"), 1, 0.5 - Geometry.PI);
+        checkPolar(PolarCoordinates.parse("(NaN,-Infinity)"), Double.NaN, Double.NEGATIVE_INFINITY);
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -343,6 +343,15 @@ public class PolarCoordinatesTest {
         PolarCoordinates.parse("abc");
     }
 
+    @Test
+    public void testGetFactory() {
+        // act
+        Coordinates.Factory2D<PolarCoordinates> factory = PolarCoordinates.getFactory();
+
+        // assert
+        checkPolar(factory.create(-1, Geometry.HALF_PI), 1, Geometry.MINUS_HALF_PI);
+    }
+
     private void checkPolar(PolarCoordinates polar, double radius, double azimuth) {
         Assert.assertEquals(radius, polar.getRadius(), EPS);
         Assert.assertEquals(azimuth, polar.getAzimuth(), EPS);


[commons-geometry] 15/15: Merge branch 'GEOMETRY-7__matt'

Posted by er...@apache.org.
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 d7b4a1016a89cd07552024a1a90fc7e7534e4569
Merge: 8273d59 49adafa
Author: Gilles Sadowski <gi...@harfang.homelinux.org>
AuthorDate: Sat Jul 21 11:59:46 2018 +0200

    Merge branch 'GEOMETRY-7__matt'
    
    Closes #7

 .../org/apache/commons/geometry/core/Geometry.java |  20 +-
 .../apache/commons/geometry/core/GeometryTest.java |  66 ++++
 .../geometry/euclidean/threed/Cartesian3D.java     |   7 +
 .../commons/geometry/euclidean/threed/Point3D.java |  21 +-
 .../euclidean/threed/SphericalCoordinates.java     | 312 ++++++++++++++++
 .../geometry/euclidean/threed/Vector3D.java        |  54 +--
 .../geometry/euclidean/twod/Cartesian2D.java       |  13 +-
 .../commons/geometry/euclidean/twod/Point2D.java   |  19 +-
 .../geometry/euclidean/twod/PolarCoordinates.java  | 262 ++++++++++++++
 .../commons/geometry/euclidean/twod/Vector2D.java  |  25 +-
 .../geometry/euclidean/oned/Vector1DTest.java      |  16 +
 .../geometry/euclidean/threed/Cartesian3DTest.java |  45 +++
 .../geometry/euclidean/threed/Point3DTest.java     |  24 +-
 .../euclidean/threed/SphericalCoordinatesTest.java | 399 +++++++++++++++++++++
 .../geometry/euclidean/threed/Vector3DTest.java    |  58 ++-
 .../geometry/euclidean/twod/Cartesian2DTest.java   |  50 ++-
 .../geometry/euclidean/twod/Point2DTest.java       |  31 +-
 .../euclidean/twod/PolarCoordinatesTest.java       | 362 +++++++++++++++++++
 .../geometry/euclidean/twod/Vector2DTest.java      |  47 ++-
 .../geometry/spherical/SphericalCoordinates.java   | 394 --------------------
 .../commons/geometry/spherical/oned/ArcsSet.java   |   4 +-
 .../geometry/spherical/oned/LimitAngle.java        |   2 +-
 .../commons/geometry/spherical/oned/S1Point.java   |  54 ++-
 .../commons/geometry/spherical/package-info.java   |  23 --
 .../commons/geometry/spherical/twod/Circle.java    |   2 +-
 .../commons/geometry/spherical/twod/S2Point.java   | 113 +++---
 .../spherical/SphericalCoordinatesTest.java        |  83 -----
 .../geometry/spherical/SphericalTestUtils.java     |   2 +-
 .../geometry/spherical/oned/LimitAngleTest.java    |   2 +-
 .../geometry/spherical/oned/S1PointTest.java       |   4 +-
 .../geometry/spherical/twod/CircleTest.java        |  10 +-
 .../geometry/spherical/twod/S2PointTest.java       |  20 +-
 .../spherical/twod/SphericalPolygonsSetTest.java   |   4 +-
 33 files changed, 1810 insertions(+), 738 deletions(-)