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/05/22 14:18:37 UTC

[commons-geometry] branch master updated: GEOMETRY-2: Cleaning up API to remove necessity of internal casting of Points and Vectors; creating separate Point and Vector types to better reflect the mathematical domain

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


The following commit(s) were added to refs/heads/master by this push:
     new 09cc0fc  GEOMETRY-2: Cleaning up API to remove necessity of internal casting of Points and Vectors; creating separate Point and Vector types to better reflect the mathematical domain
     new 47bfec9  Merge branch 'GEOMETRY-2__matt'
09cc0fc is described below

commit 09cc0fc375464ac4839e6cbc1444b738abbf1c28
Author: Matt Juntunen <ma...@hotmail.com>
AuthorDate: Mon May 21 00:38:40 2018 -0400

    GEOMETRY-2: Cleaning up API to remove necessity of internal casting of Points and Vectors; creating separate Point and Vector types to better reflect the mathematical domain
---
 .../apache/commons/geometry/core/AffinePoint.java  |   45 +
 .../org/apache/commons/geometry/core/Geometry.java |   10 +-
 .../org/apache/commons/geometry/core/Point.java    |   37 +-
 .../geometry/core/{Space.java => Spatial.java}     |   26 +-
 .../org/apache/commons/geometry/core/Vector.java   |  104 +-
 .../geometry/core/partitioning/AbstractRegion.java |  158 +-
 .../core/partitioning/AbstractSubHyperplane.java   |   70 +-
 .../geometry/core/partitioning/BSPTree.java        |  107 +-
 .../geometry/core/partitioning/BSPTreeVisitor.java |   12 +-
 .../core/partitioning/BoundaryAttribute.java       |   27 +-
 .../core/partitioning/BoundaryBuilder.java         |   28 +-
 .../core/partitioning/BoundaryProjection.java      |   18 +-
 .../core/partitioning/BoundaryProjector.java       |   59 +-
 .../core/partitioning/BoundarySizeVisitor.java     |   16 +-
 .../core/partitioning/Characterization.java        |   42 +-
 .../geometry/core/partitioning/Embedding.java      |   15 +-
 .../geometry/core/partitioning/Hyperplane.java     |   19 +-
 .../geometry/core/partitioning/InsideFinder.java   |   16 +-
 .../geometry/core/partitioning/NodesSet.java       |   18 +-
 .../commons/geometry/core/partitioning/Region.java |   25 +-
 .../geometry/core/partitioning/RegionFactory.java  |  123 +-
 .../geometry/core/partitioning/SubHyperplane.java  |   16 +-
 .../geometry/core/partitioning/Transform.java      |   13 +-
 .../geometry/core/partitioning/TreeBuilder.java    |   17 +-
 .../geometry/core/partitioning/TreeDumper.java     |   21 +-
 .../geometry/core/partitioning/TreePrinter.java    |   26 +-
 .../commons/geometry/enclosing/Encloser.java       |    5 +-
 .../commons/geometry/enclosing/EnclosingBall.java  |    3 +-
 .../geometry/enclosing/SupportBallGenerator.java   |    5 +-
 .../commons/geometry/enclosing/WelzlEncloser.java  |   27 +-
 .../threed/enclosing/SphereGenerator.java          |   28 +-
 .../euclidean/twod/enclosing/DiskGenerator.java    |   21 +-
 .../geometry/enclosing/WelzlEncloser2DTest.java    |   59 +-
 .../geometry/enclosing/WelzlEncloser3DTest.java    |  121 +-
 .../threed/enclosing/SphereGeneratorTest.java      |  115 +-
 .../twod/enclosing/DiskGeneratorTest.java          |   69 +-
 .../commons/geometry/euclidean/EuclideanPoint.java |   43 +
 .../{twod/Vector2D.java => EuclideanVector.java}   |   26 +-
 .../geometry/euclidean/oned/Cartesian1D.java       |  349 +---
 .../geometry/euclidean/oned/Euclidean1D.java       |   80 -
 .../geometry/euclidean/oned/IntervalsSet.java      |  101 +-
 .../geometry/euclidean/oned/OrientedPoint.java     |   28 +-
 .../commons/geometry/euclidean/oned/Point1D.java   |  267 +++
 .../geometry/euclidean/oned/SubOrientedPoint.java  |   18 +-
 .../commons/geometry/euclidean/oned/Vector1D.java  |  305 +++-
 .../{oned/Vector1D.java => package-info.java}      |   20 +-
 .../geometry/euclidean/threed/Cartesian3D.java     |  583 +------
 .../geometry/euclidean/threed/Euclidean3D.java     |   75 -
 .../commons/geometry/euclidean/threed/Line.java    |   95 +-
 .../euclidean/threed/OutlineExtractor.java         |   76 +-
 .../commons/geometry/euclidean/threed/Plane.java   |  144 +-
 .../commons/geometry/euclidean/threed/Point3D.java |  291 ++++
 .../geometry/euclidean/threed/PolyhedronsSet.java  |  191 ++-
 .../geometry/euclidean/threed/Rotation.java        |  150 +-
 .../geometry/euclidean/threed/RotationOrder.java   |   38 +-
 .../commons/geometry/euclidean/threed/Segment.java |   10 +-
 .../commons/geometry/euclidean/threed/SubLine.java |   23 +-
 .../geometry/euclidean/threed/SubPlane.java        |   43 +-
 .../geometry/euclidean/threed/Vector3D.java        |  526 +++++-
 .../geometry/euclidean/twod/Cartesian2D.java       |  459 +-----
 .../geometry/euclidean/twod/Euclidean2D.java       |   75 -
 .../commons/geometry/euclidean/twod/Line.java      |  136 +-
 .../geometry/euclidean/twod/NestedLoops.java       |   27 +-
 .../commons/geometry/euclidean/twod/Point2D.java   |  276 ++++
 .../geometry/euclidean/twod/PolygonsSet.java       |  166 +-
 .../commons/geometry/euclidean/twod/Segment.java   |   22 +-
 .../commons/geometry/euclidean/twod/SubLine.java   |   56 +-
 .../commons/geometry/euclidean/twod/Vector2D.java  |  433 ++++-
 .../core/partitioning/CharacterizationTest.java    |  190 ++-
 .../geometry/euclidean/EuclideanTestUtils.java     |   76 +-
 .../geometry/euclidean/oned/Cartesian1DTest.java   |  359 +---
 .../geometry/euclidean/oned/IntervalsSetTest.java  |  128 +-
 .../geometry/euclidean/oned/OrientedPointTest.java |   96 +-
 .../geometry/euclidean/oned/Point1DTest.java       |  262 +++
 .../euclidean/oned/SubOrientedPointTest.java       |   38 +-
 .../geometry/euclidean/oned/Vector1DTest.java      |  370 +++++
 .../geometry/euclidean/threed/Cartesian3DTest.java |   83 +
 .../geometry/euclidean/threed/Euclidean3DTest.java |   44 -
 .../geometry/euclidean/threed/LineTest.java        |   76 +-
 .../geometry/euclidean/threed/OBJWriter.java       |   51 +-
 .../geometry/euclidean/threed/PLYParser.java       |    8 +-
 .../geometry/euclidean/threed/PlaneTest.java       |   88 +-
 .../geometry/euclidean/threed/Point3DTest.java     |  287 ++++
 .../euclidean/threed/PolyhedronsSetTest.java       |  980 +++++------
 .../geometry/euclidean/threed/RotationTest.java    |  248 +--
 .../geometry/euclidean/threed/SubLineTest.java     |   68 +-
 .../geometry/euclidean/threed/Vector3DTest.java    |  862 +++++++---
 .../geometry/euclidean/twod/Cartesian2DTest.java   |  242 +--
 .../geometry/euclidean/twod/Euclidean2DTest.java   |   44 -
 .../commons/geometry/euclidean/twod/LineTest.java  |   70 +-
 .../geometry/euclidean/twod/NestedLoopsTest.java   |   16 +-
 .../geometry/euclidean/twod/Point2DTest.java       |  262 +++
 .../geometry/euclidean/twod/PolygonsSetTest.java   | 1716 ++++++++++----------
 .../geometry/euclidean/twod/SegmentTest.java       |   12 +-
 .../geometry/euclidean/twod/SubLineTest.java       |   68 +-
 .../geometry/euclidean/twod/Vector2DTest.java      |  526 ++++++
 .../twod/hull/AbstractConvexHullGenerator2D.java   |   10 +-
 .../euclidean/twod/hull/AklToussaintHeuristic.java |   44 +-
 .../geometry/euclidean/twod/hull/ConvexHull2D.java |   38 +-
 .../euclidean/twod/hull/ConvexHullGenerator2D.java |    7 +-
 .../euclidean/twod/hull/MonotoneChain.java         |   28 +-
 .../apache/commons/geometry/hull/ConvexHull.java   |    5 +-
 .../commons/geometry/hull/ConvexHullGenerator.java |    5 +-
 .../twod/hull/AklToussaintHeuristicTest.java       |    4 +-
 .../hull/ConvexHullGenerator2DAbstractTest.java    |  232 +--
 .../euclidean/twod/hull/MonotoneChainTest.java     |   22 +-
 commons-geometry-spherical/pom.xml                 |   16 +
 .../geometry/spherical/SphericalCoordinates.java   |   12 +-
 .../commons/geometry/spherical/oned/ArcsSet.java   |   95 +-
 .../geometry/spherical/oned/LimitAngle.java        |   11 +-
 .../commons/geometry/spherical/oned/S1Point.java   |   34 +-
 .../commons/geometry/spherical/oned/Sphere1D.java  |   86 -
 .../geometry/spherical/oned/SubLimitAngle.java     |   12 +-
 .../commons/geometry/spherical/twod/Circle.java    |   69 +-
 .../commons/geometry/spherical/twod/Edge.java      |    4 +-
 .../geometry/spherical/twod/EdgesBuilder.java      |   34 +-
 .../spherical/twod/PropertiesComputer.java         |   41 +-
 .../commons/geometry/spherical/twod/S2Point.java   |   50 +-
 .../commons/geometry/spherical/twod/Sphere2D.java  |   81 -
 .../spherical/twod/SphericalPolygonsSet.java       |   84 +-
 .../commons/geometry/spherical/twod/SubCircle.java |   18 +-
 .../spherical/SphericalCoordinatesTest.java        |   26 +-
 .../geometry/spherical/SphericalTestUtils.java     |   19 +-
 .../commons/geometry/spherical/oned/ArcTest.java   |   79 +
 .../geometry/spherical/oned/ArcsSetTest.java       |  590 +++++++
 .../geometry/spherical/oned/LimitAngleTest.java    |   31 +-
 .../geometry/spherical/oned/S1PointTest.java       |   60 +
 .../geometry/spherical/twod/CircleTest.java        |  188 +++
 .../geometry/spherical/twod/S2PointTest.java       |   77 +
 .../spherical/twod/SphericalPolygonsSetTest.java   |  550 +++++++
 .../geometry/spherical/twod/SubCircleTest.java     |  138 ++
 131 files changed, 10117 insertions(+), 6606 deletions(-)

diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/AffinePoint.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/AffinePoint.java
new file mode 100644
index 0000000..80d7b22
--- /dev/null
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/AffinePoint.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+/** Interface that adds affine space operations to the base {@link Point}
+ * interface. Affine spaces consist of points and displacement vectors
+ * representing translations between points. Since this interface extends
+ * {@link Point}, the represented space is both affine and metric.
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Affine_space">Affine space</a>
+ * @see <a href="https://en.wikipedia.org/wiki/Metric_space">Metric space</a>
+ * @see Point
+ *
+ * @param <P> Point implementation type
+ * @param <V> Vector implementation type
+ */
+public interface AffinePoint<P extends AffinePoint<P, V>, V extends Vector<V>> extends Point<P> {
+
+    /** Returns the displacement vector from this point to p.
+     * @param p second point
+     * @return The displacement vector from this point to p.
+     */
+    V subtract(P p);
+
+    /** Returns the point resulting from adding the given displacement
+     * vector to this point.
+     * @param v displacement vector
+     * @return point resulting from displacing this point by v
+     */
+    P add(V v);
+}
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 adef1ab..d74d312 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,10 +23,18 @@ public class Geometry {
     /** Alias for {@link Math#PI}, placed here for completeness. */
     public static final double PI = Math.PI;
 
-    /** Constant representing {@code 2*pi}.
+    /** Constant value for {@code 2*pi}.
      */
     public static final double TWO_PI = 2.0 * Math.PI;
 
+    /** Constant value for {@code pi / 2}.
+     */
+    public static final double HALF_PI = 0.5 * Math.PI;
+
+    /** Constant value for {@code - pi / 2}.
+     */
+    public static final double MINUS_HALF_PI = - 0.5 * Math.PI;
+
     /** Private constructor */
     private Geometry() {}
 }
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Point.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Point.java
index 9a9b2f4..191088f 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Point.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Point.java
@@ -16,30 +16,23 @@
  */
 package org.apache.commons.geometry.core;
 
-import java.io.Serializable;
-
-/** This interface represents a generic geometrical point.
- * @param <S> Type of the space.
- * @see Space
- * @see Vector
+/** Interface representing a point in a mathematical space.
+ * Implementations of this interface are sufficient to define a
+ * space since they define both the structure of the points making up
+ * the space and the operations permitted on them. The only mathematical
+ * requirement at this level is that the represented space have a defined
+ * distance metric, meaning an operation that can compute the distance
+ * between two points (ie, the space must be a metric space).
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Metric_space">Metric space</a>
+ *
+ * @param <P> Point implementation type
  */
-public interface Point<S extends Space> extends Serializable {
-
-    /** Get the space to which the point belongs.
-     * @return containing space
-     */
-    Space getSpace();
-
-    /**
-     * Returns true if any coordinate of this point is NaN; false otherwise
-     * @return  true if any coordinate of this point is NaN; false otherwise
-     */
-    boolean isNaN();
+public interface Point<P extends Point<P>> extends Spatial {
 
-    /** Compute the distance between the instance and another point.
+    /** Compute the distance between this point and another point.
      * @param p second point
-     * @return the distance between the instance and p
+     * @return the distance between this point and p
      */
-    double distance(Point<S> p);
-
+    double distance(P p);
 }
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Space.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Spatial.java
similarity index 57%
rename from commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Space.java
rename to commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Spatial.java
index a932550..ad72eb7 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Space.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Spatial.java
@@ -18,22 +18,26 @@ package org.apache.commons.geometry.core;
 
 import java.io.Serializable;
 
-/** This interface represents a generic space, with affine and vectorial counterparts.
- * @see Vector
+/** Interface representing a generic element in a mathematical space.
  */
-public interface Space extends Serializable {
+public interface Spatial extends Serializable {
 
-    /** Get the dimension of the space.
-     * @return dimension of the space
+    /** Returns the number of dimensions in the space that this element
+     * belongs to.
+     * @return the number of dimensions in the element's space
      */
     int getDimension();
 
-    /** Get the n-1 dimension subspace of this space.
-     * @return n-1 dimension sub-space of this space
-     * @see #getDimension()
-     * @exception UnsupportedOperationException for dimension-1 spaces
-     * which do not have sub-spaces
+    /** Returns true if any value in this element is NaN; otherwise
+     * returns false.
+     * @return true if any value in this element is NaN
      */
-    Space getSubSpace() throws UnsupportedOperationException;
+    boolean isNaN();
 
+    /** Returns true if any value in this element is infinite and none
+     * are NaN; otherwise, returns false.
+     * @return true if any value in this element is infinite and none
+     *      are NaN
+     */
+    boolean isInfinite();
 }
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java
index e13799c..1d0269d 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java
@@ -16,41 +16,50 @@
  */
 package org.apache.commons.geometry.core;
 
-import java.text.NumberFormat;
-
-/** This interface represents a generic vector in a vectorial space or a point in an affine space.
- * @param <S> Type of the space.
- * @see Space
- * @see Point
+/** Interface representing a vector in a vector space. The most common
+ * use of this interface is to represent displacement vectors in an affine
+ * space.
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Vector_space">Vector space</a>
+ * @see <a href="https://en.wikipedia.org/wiki/Affine_space">Affine space</a>
+ *
+ * @see AffinePoint
+ *
+ * @param <V> Vector implementation type
  */
-public interface Vector<S extends Space> {
+public interface Vector<V extends Vector<V>> extends Spatial {
 
-    /** Get the space to which the point belongs.
-     * @return containing space
+    /** Get the zero (null) vector of the space.
+     * @return zero vector of the space
      */
-    Space getSpace();
+    V getZero();
 
-    /** Get the null vector of the vectorial space or origin point of the affine space.
-     * @return null vector of the vectorial space or origin point of the affine space
-     */
-    Vector<S> getZero();
-
-    /** Get the L<sub>1</sub> norm for the vector.
+    /** Get the L<sub>1</sub> norm for the vector. This is defined as the
+     * sum of the absolute values of all vector components.
+     *
+     * @see <a href="http://mathworld.wolfram.com/L1-Norm.html">L1 Norm</a>
      * @return L<sub>1</sub> norm for the vector
      */
     double getNorm1();
 
-    /** Get the L<sub>2</sub> norm for the vector.
+    /** Get the L<sub>2</sub> norm (commonly known as the Euclidean norm) for the vector.
+     * This corresponds to the common notion of vector magnitude or length.
+     * This is defined as the square root of the sum of the squares of all vector components.
+     * @see <a href="http://mathworld.wolfram.com/L2-Norm.html">L2 Norm</a>
      * @return Euclidean norm for the vector
      */
     double getNorm();
 
-    /** Get the square of the norm for the vector.
+    /** Get the square of the L<sub>2</sub> norm (also known as the Euclidean norm)
+     * for the vector. This is equal to the sum of the squares of all vector components.
+     * @see #getNorm()
      * @return square of the Euclidean norm for the vector
      */
     double getNormSq();
 
-    /** Get the L<sub>&infin;</sub> norm for the vector.
+    /** Get the L<sub>&infin;</sub> norm for the vector. This is defined as the
+     * maximum of the absolute values of all vector components.
+     * @see <a href="http://mathworld.wolfram.com/L-Infinity-Norm.html">L<sub>&infin;</sub> Norm</a>
      * @return L<sub>&infin;</sub> norm for the vector
      */
     double getNormInf();
@@ -59,102 +68,85 @@ public interface Vector<S extends Space> {
      * @param v vector to add
      * @return a new vector
      */
-    Vector<S> add(Vector<S> v);
+    V add(V v);
 
     /** Add a scaled vector to the instance.
      * @param factor scale factor to apply to v before adding it
      * @param v vector to add
      * @return a new vector
      */
-    Vector<S> add(double factor, Vector<S> v);
+    V add(double factor, V v);
 
     /** Subtract a vector from the instance.
      * @param v vector to subtract
      * @return a new vector
      */
-    Vector<S> subtract(Vector<S> v);
+    V subtract(V v);
 
     /** Subtract a scaled vector from the instance.
      * @param factor scale factor to apply to v before subtracting it
      * @param v vector to subtract
      * @return a new vector
      */
-    Vector<S> subtract(double factor, Vector<S> v);
+    V subtract(double factor, V v);
 
-    /** Get the opposite of the instance.
-     * @return a new vector which is opposite to the instance
+    /** Get the negation of the instance.
+     * @return a new vector which is the negation of the instance
      */
-    Vector<S> negate();
+    V negate();
 
-    /** Get a normalized vector aligned with the instance.
+    /** Get a normalized vector aligned with the instance. The returned
+     * vector has a magnitude of 1.
      * @return a new normalized vector
      * @exception IllegalStateException if the norm is zero
      */
-    Vector<S> normalize() throws IllegalStateException;
+    V normalize() throws IllegalStateException;
 
     /** Multiply the instance by a scalar.
      * @param a scalar
      * @return a new vector
      */
-    Vector<S> scalarMultiply(double a);
-
-    /**
-     * Returns true if any coordinate of this point is NaN; false otherwise
-     * @return  true if any coordinate of this point is NaN; false otherwise
-     */
-    boolean isNaN();
-
-    /**
-     * Returns true if any coordinate of this vector is infinite and none are NaN;
-     * false otherwise
-     * @return  true if any coordinate of this vector is infinite and none are NaN;
-     * false otherwise
-     */
-    boolean isInfinite();
+    V scalarMultiply(double a);
 
     /** Compute the distance between the instance and another vector according to the L<sub>1</sub> norm.
      * <p>Calling this method is equivalent to calling:
      * <code>q.subtract(p).getNorm1()</code> except that no intermediate
      * vector is built</p>
+     * @see #getNorm1()
      * @param v second vector
      * @return the distance between the instance and p according to the L<sub>1</sub> norm
      */
-    double distance1(Vector<S> v);
+    double distance1(V v);
 
     /** Compute the distance between the instance and another vector.
      * @param v second vector
      * @return the distance between the instance and v
      */
-    double distance(Vector<S> v);
+    double distance(V v);
 
     /** Compute the distance between the instance and another vector according to the L<sub>&infin;</sub> norm.
      * <p>Calling this method is equivalent to calling:
      * <code>q.subtract(p).getNormInf()</code> except that no intermediate
      * vector is built</p>
+     * @see #getNormInf()
      * @param v second vector
      * @return the distance between the instance and p according to the L<sub>&infin;</sub> norm
      */
-    double distanceInf(Vector<S> v);
+    double distanceInf(V v);
 
     /** Compute the square of the distance between the instance and another vector.
      * <p>Calling this method is equivalent to calling:
      * <code>q.subtract(p).getNormSq()</code> except that no intermediate
      * vector is built</p>
+     * @see #getNormSq()
      * @param v second vector
      * @return the square of the distance between the instance and p
      */
-    double distanceSq(Vector<S> v);
+    double distanceSq(V v);
 
     /** Compute the dot-product of the instance and another vector.
      * @param v second vector
-     * @return the dot product this.v
+     * @return the dot product this &middot; v
      */
-    double dotProduct(Vector<S> v);
-
-    /** Get a string representation of this vector.
-     * @param format the custom format for components
-     * @return a string representation of this vector
-     */
-    String toString(final NumberFormat format);
-
+    double dotProduct(V v);
 }
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractRegion.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractRegion.java
index bc23114..ff923d6 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractRegion.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractRegion.java
@@ -25,18 +25,16 @@ import java.util.Map;
 import java.util.TreeSet;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
-import org.apache.commons.geometry.core.Vector;
 
-/** Abstract class for all regions, independently of geometry type or dimension.
+/** Abstract class for all regions, independent of geometry type or dimension.
 
- * @param <S> Type of the space.
- * @param <T> Type of the sub-space.
+ * @param <P> Point type defining the space
+ * @param <S> Point type defining the sub-space
  */
-public abstract class AbstractRegion<S extends Space, T extends Space> implements Region<S> {
+public abstract class AbstractRegion<P extends Point<P>, S extends Point<S>> implements Region<P> {
 
     /** Inside/Outside BSP tree. */
-    private BSPTree<S> tree;
+    private BSPTree<P> tree;
 
     /** Tolerance below which points are considered to belong to hyperplanes. */
     private final double tolerance;
@@ -45,7 +43,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
     private double size;
 
     /** Barycenter. */
-    private Point<S> barycenter;
+    private P barycenter;
 
     /** Build a region representing the whole space.
      * @param tolerance tolerance below which points are considered identical.
@@ -68,7 +66,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
      * @param tree inside/outside BSP tree representing the region
      * @param tolerance tolerance below which points are considered identical.
      */
-    protected AbstractRegion(final BSPTree<S> tree, final double tolerance) {
+    protected AbstractRegion(final BSPTree<P> tree, final double tolerance) {
         this.tree      = tree;
         this.tolerance = tolerance;
     }
@@ -93,7 +91,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
      * collection of {@link SubHyperplane SubHyperplane} objects
      * @param tolerance tolerance below which points are considered identical.
      */
-    protected AbstractRegion(final Collection<SubHyperplane<S>> boundary, final double tolerance) {
+    protected AbstractRegion(final Collection<SubHyperplane<P>> boundary, final double tolerance) {
 
         this.tolerance = tolerance;
 
@@ -107,10 +105,10 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
             // sort the boundary elements in decreasing size order
             // (we don't want equal size elements to be removed, so
             // we use a trick to fool the TreeSet)
-            final TreeSet<SubHyperplane<S>> ordered = new TreeSet<>(new Comparator<SubHyperplane<S>>() {
+            final TreeSet<SubHyperplane<P>> ordered = new TreeSet<>(new Comparator<SubHyperplane<P>>() {
                 /** {@inheritDoc} */
                 @Override
-                public int compare(final SubHyperplane<S> o1, final SubHyperplane<S> o2) {
+                public int compare(final SubHyperplane<P> o1, final SubHyperplane<P> o2) {
                     final double size1 = o1.getSize();
                     final double size2 = o2.getSize();
                     return (size2 < size1) ? -1 : ((o1 == o2) ? 0 : +1);
@@ -123,22 +121,22 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
             insertCuts(tree, ordered);
 
             // set up the inside/outside flags
-            tree.visit(new BSPTreeVisitor<S>() {
+            tree.visit(new BSPTreeVisitor<P>() {
 
                 /** {@inheritDoc} */
                 @Override
-                public Order visitOrder(final BSPTree<S> node) {
+                public Order visitOrder(final BSPTree<P> node) {
                     return Order.PLUS_SUB_MINUS;
                 }
 
                 /** {@inheritDoc} */
                 @Override
-                public void visitInternalNode(final BSPTree<S> node) {
+                public void visitInternalNode(final BSPTree<P> node) {
                 }
 
                 /** {@inheritDoc} */
                 @Override
-                public void visitLeafNode(final BSPTree<S> node) {
+                public void visitLeafNode(final BSPTree<P> node) {
                     if (node.getParent() == null || node == node.getParent().getMinus()) {
                         node.setAttribute(Boolean.TRUE);
                     } else {
@@ -156,7 +154,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
      * empty region will be built)
      * @param tolerance tolerance below which points are considered identical.
      */
-    public AbstractRegion(final Hyperplane<S>[] hyperplanes, final double tolerance) {
+    public AbstractRegion(final Hyperplane<P>[] hyperplanes, final double tolerance) {
         this.tolerance = tolerance;
         if ((hyperplanes == null) || (hyperplanes.length == 0)) {
             tree = new BSPTree<>(Boolean.FALSE);
@@ -166,9 +164,9 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
             tree = hyperplanes[0].wholeSpace().getTree(false);
 
             // chop off parts of the space
-            BSPTree<S> node = tree;
+            BSPTree<P> node = tree;
             node.setAttribute(Boolean.TRUE);
-            for (final Hyperplane<S> hyperplane : hyperplanes) {
+            for (final Hyperplane<P> hyperplane : hyperplanes) {
                 if (node.insertCut(hyperplane)) {
                     node.setAttribute(null);
                     node.getPlus().setAttribute(Boolean.FALSE);
@@ -183,7 +181,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
 
     /** {@inheritDoc} */
     @Override
-    public abstract AbstractRegion<S, T> buildNew(BSPTree<S> newTree);
+    public abstract AbstractRegion<P, S> buildNew(BSPTree<P> newTree);
 
     /** Get the tolerance below which points are considered to belong to hyperplanes.
      * @return tolerance below which points are considered to belong to hyperplanes
@@ -198,12 +196,12 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
      * @param boundary collection of edges belonging to the cell defined
      * by the node
      */
-    private void insertCuts(final BSPTree<S> node, final Collection<SubHyperplane<S>> boundary) {
+    private void insertCuts(final BSPTree<P> node, final Collection<SubHyperplane<P>> boundary) {
 
-        final Iterator<SubHyperplane<S>> iterator = boundary.iterator();
+        final Iterator<SubHyperplane<P>> iterator = boundary.iterator();
 
         // build the current level
-        Hyperplane<S> inserted = null;
+        Hyperplane<P> inserted = null;
         while ((inserted == null) && iterator.hasNext()) {
             inserted = iterator.next().getHyperplane();
             if (!node.insertCut(inserted.copySelf())) {
@@ -216,11 +214,11 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
         }
 
         // distribute the remaining edges in the two sub-trees
-        final ArrayList<SubHyperplane<S>> plusList  = new ArrayList<>();
-        final ArrayList<SubHyperplane<S>> minusList = new ArrayList<>();
+        final ArrayList<SubHyperplane<P>> plusList  = new ArrayList<>();
+        final ArrayList<SubHyperplane<P>> minusList = new ArrayList<>();
         while (iterator.hasNext()) {
-            final SubHyperplane<S> other = iterator.next();
-            final SubHyperplane.SplitSubHyperplane<S> split = other.split(inserted);
+            final SubHyperplane<P> other = iterator.next();
+            final SubHyperplane.SplitSubHyperplane<P> split = other.split(inserted);
             switch (split.getSide()) {
             case PLUS:
                 plusList.add(other);
@@ -245,7 +243,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
 
     /** {@inheritDoc} */
     @Override
-    public AbstractRegion<S, T> copySelf() {
+    public AbstractRegion<P, S> copySelf() {
         return buildNew(tree.copySelf());
     }
 
@@ -257,7 +255,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
 
     /** {@inheritDoc} */
     @Override
-    public boolean isEmpty(final BSPTree<S> node) {
+    public boolean isEmpty(final BSPTree<P> node) {
 
         // we use a recursive function rather than the BSPTreeVisitor
         // interface because we can stop visiting the tree as soon as we
@@ -281,7 +279,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
 
     /** {@inheritDoc} */
     @Override
-    public boolean isFull(final BSPTree<S> node) {
+    public boolean isFull(final BSPTree<P> node) {
 
         // we use a recursive function rather than the BSPTreeVisitor
         // interface because we can stop visiting the tree as soon as we
@@ -299,32 +297,22 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
 
     /** {@inheritDoc} */
     @Override
-    public boolean contains(final Region<S> region) {
-        return new RegionFactory<S>().difference(region, this).isEmpty();
+    public boolean contains(final Region<P> region) {
+        return new RegionFactory<P>().difference(region, this).isEmpty();
     }
 
     /** {@inheritDoc}
      */
     @Override
-    public BoundaryProjection<S> projectToBoundary(final Point<S> point) {
-        final BoundaryProjector<S, T> projector = new BoundaryProjector<>(point);
+    public BoundaryProjection<P> projectToBoundary(final P point) {
+        final BoundaryProjector<P, S> projector = new BoundaryProjector<>(point);
         getTree(true).visit(projector);
         return projector.getProjection();
     }
 
-    /** Check a point with respect to the region.
-     * @param point point to check
-     * @return a code representing the point status: either {@link
-     * Region.Location#INSIDE}, {@link Region.Location#OUTSIDE} or
-     * {@link Region.Location#BOUNDARY}
-     */
-//    public Location checkPoint(final Vector<S> point) {
-//        return checkPoint((Point<S>) point);
-//    }
-
     /** {@inheritDoc} */
     @Override
-    public Location checkPoint(final Point<S> point) {
+    public Location checkPoint(final P point) {
         return checkPoint(tree, point);
     }
 
@@ -335,19 +323,8 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
      * Region.Location#INSIDE INSIDE}, {@link Region.Location#OUTSIDE
      * OUTSIDE} or {@link Region.Location#BOUNDARY BOUNDARY}
      */
-    protected Location checkPoint(final BSPTree<S> node, final Vector<S> point) {
-        return checkPoint(node, (Point<S>) point);
-    }
-
-    /** Check a point with respect to the region starting at a given node.
-     * @param node root node of the region
-     * @param point point to check
-     * @return a code representing the point status: either {@link
-     * Region.Location#INSIDE INSIDE}, {@link Region.Location#OUTSIDE
-     * OUTSIDE} or {@link Region.Location#BOUNDARY BOUNDARY}
-     */
-    protected Location checkPoint(final BSPTree<S> node, final Point<S> point) {
-        final BSPTree<S> cell = node.getCell(point, tolerance);
+    protected Location checkPoint(final BSPTree<P> node, final P point) {
+        final BSPTree<P> cell = node.getCell(point, tolerance);
         if (cell.getCut() == null) {
             // the point is in the interior of a cell, just check the attribute
             return ((Boolean) cell.getAttribute()) ? Location.INSIDE : Location.OUTSIDE;
@@ -362,10 +339,10 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
 
     /** {@inheritDoc} */
     @Override
-    public BSPTree<S> getTree(final boolean includeBoundaryAttributes) {
+    public BSPTree<P> getTree(final boolean includeBoundaryAttributes) {
         if (includeBoundaryAttributes && (tree.getCut() != null) && (tree.getAttribute() == null)) {
             // compute the boundary attributes
-            tree.visit(new BoundaryBuilder<S>());
+            tree.visit(new BoundaryBuilder<P>());
         }
         return tree;
     }
@@ -373,7 +350,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
     /** {@inheritDoc} */
     @Override
     public double getBoundarySize() {
-        final BoundarySizeVisitor<S> visitor = new BoundarySizeVisitor<>();
+        final BoundarySizeVisitor<P> visitor = new BoundarySizeVisitor<>();
         getTree(true).visit(visitor);
         return visitor.getSize();
     }
@@ -396,7 +373,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
 
     /** {@inheritDoc} */
     @Override
-    public Point<S> getBarycenter() {
+    public P getBarycenter() {
         if (barycenter == null) {
             computeGeometricalProperties();
         }
@@ -406,14 +383,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
     /** Set the barycenter of the instance.
      * @param barycenter barycenter of the instance
      */
-    protected void setBarycenter(final Vector<S> barycenter) {
-        setBarycenter((Point<S>) barycenter);
-    }
-
-    /** Set the barycenter of the instance.
-     * @param barycenter barycenter of the instance
-     */
-    protected void setBarycenter(final Point<S> barycenter) {
+    protected void setBarycenter(final P barycenter) {
         this.barycenter = barycenter;
     }
 
@@ -424,7 +394,7 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
 
     /** {@inheritDoc} */
     @Override
-    public SubHyperplane<S> intersection(final SubHyperplane<S> sub) {
+    public SubHyperplane<P> intersection(final SubHyperplane<P> sub) {
         return recurseIntersection(tree, sub);
     }
 
@@ -434,19 +404,19 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
      * @param sub sub-hyperplane traversing the region
      * @return filtered sub-hyperplane
      */
-    private SubHyperplane<S> recurseIntersection(final BSPTree<S> node, final SubHyperplane<S> sub) {
+    private SubHyperplane<P> recurseIntersection(final BSPTree<P> node, final SubHyperplane<P> sub) {
 
         if (node.getCut() == null) {
             return (Boolean) node.getAttribute() ? sub.copySelf() : null;
         }
 
-        final Hyperplane<S> hyperplane = node.getCut().getHyperplane();
-        final SubHyperplane.SplitSubHyperplane<S> split = sub.split(hyperplane);
+        final Hyperplane<P> hyperplane = node.getCut().getHyperplane();
+        final SubHyperplane.SplitSubHyperplane<P> split = sub.split(hyperplane);
         if (split.getPlus() != null) {
             if (split.getMinus() != null) {
                 // both sides
-                final SubHyperplane<S> plus  = recurseIntersection(node.getPlus(),  split.getPlus());
-                final SubHyperplane<S> minus = recurseIntersection(node.getMinus(), split.getMinus());
+                final SubHyperplane<P> plus  = recurseIntersection(node.getPlus(),  split.getPlus());
+                final SubHyperplane<P> minus = recurseIntersection(node.getMinus(), split.getMinus());
                 if (plus == null) {
                     return minus;
                 } else if (minus == null) {
@@ -479,21 +449,21 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
      * @return a new region, resulting from the application of the
      * transform to the instance
      */
-    public AbstractRegion<S, T> applyTransform(final Transform<S, T> transform) {
+    public AbstractRegion<P, S> applyTransform(final Transform<P, S> transform) {
 
         // transform the tree, except for boundary attribute splitters
-        final Map<BSPTree<S>, BSPTree<S>> map = new HashMap<>();
-        final BSPTree<S> transformedTree = recurseTransform(getTree(false), transform, map);
+        final Map<BSPTree<P>, BSPTree<P>> map = new HashMap<>();
+        final BSPTree<P> transformedTree = recurseTransform(getTree(false), transform, map);
 
         // set up the boundary attributes splitters
-        for (final Map.Entry<BSPTree<S>, BSPTree<S>> entry : map.entrySet()) {
+        for (final Map.Entry<BSPTree<P>, BSPTree<P>> entry : map.entrySet()) {
             if (entry.getKey().getCut() != null) {
                 @SuppressWarnings("unchecked")
-                BoundaryAttribute<S> original = (BoundaryAttribute<S>) entry.getKey().getAttribute();
+                BoundaryAttribute<P> original = (BoundaryAttribute<P>) entry.getKey().getAttribute();
                 if (original != null) {
                     @SuppressWarnings("unchecked")
-                    BoundaryAttribute<S> transformed = (BoundaryAttribute<S>) entry.getValue().getAttribute();
-                    for (final BSPTree<S> splitter : original.getSplitters()) {
+                    BoundaryAttribute<P> transformed = (BoundaryAttribute<P>) entry.getValue().getAttribute();
+                    for (final BSPTree<P> splitter : original.getSplitters()) {
                         transformed.getSplitters().add(map.get(splitter));
                     }
                 }
@@ -511,24 +481,24 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
      * @return a new tree
      */
     @SuppressWarnings("unchecked")
-    private BSPTree<S> recurseTransform(final BSPTree<S> node, final Transform<S, T> transform,
-                                        final Map<BSPTree<S>, BSPTree<S>> map) {
+    private BSPTree<P> recurseTransform(final BSPTree<P> node, final Transform<P, S> transform,
+                                        final Map<BSPTree<P>, BSPTree<P>> map) {
 
-        final BSPTree<S> transformedNode;
+        final BSPTree<P> transformedNode;
         if (node.getCut() == null) {
             transformedNode = new BSPTree<>(node.getAttribute());
         } else {
 
-            final SubHyperplane<S>  sub = node.getCut();
-            final SubHyperplane<S> tSub = ((AbstractSubHyperplane<S, T>) sub).applyTransform(transform);
-            BoundaryAttribute<S> attribute = (BoundaryAttribute<S>) node.getAttribute();
+            final SubHyperplane<P>  sub = node.getCut();
+            final SubHyperplane<P> tSub = ((AbstractSubHyperplane<P, S>) sub).applyTransform(transform);
+            BoundaryAttribute<P> attribute = (BoundaryAttribute<P>) node.getAttribute();
             if (attribute != null) {
-                final SubHyperplane<S> tPO = (attribute.getPlusOutside() == null) ?
-                    null : ((AbstractSubHyperplane<S, T>) attribute.getPlusOutside()).applyTransform(transform);
-                final SubHyperplane<S> tPI = (attribute.getPlusInside()  == null) ?
-                    null  : ((AbstractSubHyperplane<S, T>) attribute.getPlusInside()).applyTransform(transform);
+                final SubHyperplane<P> tPO = (attribute.getPlusOutside() == null) ?
+                    null : ((AbstractSubHyperplane<P, S>) attribute.getPlusOutside()).applyTransform(transform);
+                final SubHyperplane<P> tPI = (attribute.getPlusInside()  == null) ?
+                    null  : ((AbstractSubHyperplane<P, S>) attribute.getPlusInside()).applyTransform(transform);
                 // we start with an empty list of splitters, it will be filled in out of recursion
-                attribute = new BoundaryAttribute<>(tPO, tPI, new NodesSet<S>());
+                attribute = new BoundaryAttribute<>(tPO, tPI, new NodesSet<P>());
             }
 
             transformedNode = new BSPTree<>(tSub,
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractSubHyperplane.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractSubHyperplane.java
index 08d885e..6c07722 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractSubHyperplane.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/AbstractSubHyperplane.java
@@ -19,7 +19,7 @@ package org.apache.commons.geometry.core.partitioning;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.commons.geometry.core.Space;
+import org.apache.commons.geometry.core.Point;
 
 /** This class implements the dimension-independent parts of {@link SubHyperplane}.
 
@@ -30,24 +30,24 @@ import org.apache.commons.geometry.core.Space;
  * hyperplane with the convex region which it splits, the chopping
  * hyperplanes are the cut hyperplanes closer to the tree root.</p>
 
- * @param <S> Type of the embedding space.
- * @param <T> Type of the embedded sub-space.
+ * @param <P> Point type defining the space
+ * @param <S> Point type defining the sub-space
  */
-public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
-    implements SubHyperplane<S> {
+public abstract class AbstractSubHyperplane<P extends Point<P>, S extends Point<S>>
+    implements SubHyperplane<P> {
 
     /** Underlying hyperplane. */
-    private final Hyperplane<S> hyperplane;
+    private final Hyperplane<P> hyperplane;
 
     /** Remaining region of the hyperplane. */
-    private final Region<T> remainingRegion;
+    private final Region<S> remainingRegion;
 
     /** Build a sub-hyperplane from an hyperplane and a region.
      * @param hyperplane underlying hyperplane
      * @param remainingRegion remaining region of the hyperplane
      */
-    protected AbstractSubHyperplane(final Hyperplane<S> hyperplane,
-                                    final Region<T> remainingRegion) {
+    protected AbstractSubHyperplane(final Hyperplane<P> hyperplane,
+                                    final Region<S> remainingRegion) {
         this.hyperplane      = hyperplane;
         this.remainingRegion = remainingRegion;
     }
@@ -57,12 +57,12 @@ public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
      * @param remaining remaining region of the hyperplane
      * @return a new sub-hyperplane
      */
-    protected abstract AbstractSubHyperplane<S, T> buildNew(final Hyperplane<S> hyper,
-                                                            final Region<T> remaining);
+    protected abstract AbstractSubHyperplane<P, S> buildNew(final Hyperplane<P> hyper,
+                                                            final Region<S> remaining);
 
     /** {@inheritDoc} */
     @Override
-    public AbstractSubHyperplane<S, T> copySelf() {
+    public AbstractSubHyperplane<P, S> copySelf() {
         return buildNew(hyperplane.copySelf(), remainingRegion);
     }
 
@@ -70,7 +70,7 @@ public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
      * @return underlying hyperplane
      */
     @Override
-    public Hyperplane<S> getHyperplane() {
+    public Hyperplane<P> getHyperplane() {
         return hyperplane;
     }
 
@@ -81,7 +81,7 @@ public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
      * corresponding region is a convex 2D polygon.</p>
      * @return remaining region of the hyperplane
      */
-    public Region<T> getRemainingRegion() {
+    public Region<S> getRemainingRegion() {
         return remainingRegion;
     }
 
@@ -93,11 +93,11 @@ public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
 
     /** {@inheritDoc} */
     @Override
-    public AbstractSubHyperplane<S, T> reunite(final SubHyperplane<S> other) {
+    public AbstractSubHyperplane<P, S> reunite(final SubHyperplane<P> other) {
         @SuppressWarnings("unchecked")
-        AbstractSubHyperplane<S, T> o = (AbstractSubHyperplane<S, T>) other;
+        AbstractSubHyperplane<P, S> o = (AbstractSubHyperplane<P, S>) other;
         return buildNew(hyperplane,
-                        new RegionFactory<T>().union(remainingRegion, o.remainingRegion));
+                        new RegionFactory<S>().union(remainingRegion, o.remainingRegion));
     }
 
     /** Apply a transform to the instance.
@@ -110,23 +110,23 @@ public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
      * @param transform D-dimension transform to apply
      * @return the transformed instance
      */
-    public AbstractSubHyperplane<S, T> applyTransform(final Transform<S, T> transform) {
-        final Hyperplane<S> tHyperplane = transform.apply(hyperplane);
+    public AbstractSubHyperplane<P, S> applyTransform(final Transform<P, S> transform) {
+        final Hyperplane<P> tHyperplane = transform.apply(hyperplane);
 
         // transform the tree, except for boundary attribute splitters
-        final Map<BSPTree<T>, BSPTree<T>> map = new HashMap<>();
-        final BSPTree<T> tTree =
+        final Map<BSPTree<S>, BSPTree<S>> map = new HashMap<>();
+        final BSPTree<S> tTree =
             recurseTransform(remainingRegion.getTree(false), tHyperplane, transform, map);
 
         // set up the boundary attributes splitters
-        for (final Map.Entry<BSPTree<T>, BSPTree<T>> entry : map.entrySet()) {
+        for (final Map.Entry<BSPTree<S>, BSPTree<S>> entry : map.entrySet()) {
             if (entry.getKey().getCut() != null) {
                 @SuppressWarnings("unchecked")
-                BoundaryAttribute<T> original = (BoundaryAttribute<T>) entry.getKey().getAttribute();
+                BoundaryAttribute<S> original = (BoundaryAttribute<S>) entry.getKey().getAttribute();
                 if (original != null) {
                     @SuppressWarnings("unchecked")
-                    BoundaryAttribute<T> transformed = (BoundaryAttribute<T>) entry.getValue().getAttribute();
-                    for (final BSPTree<T> splitter : original.getSplitters()) {
+                    BoundaryAttribute<S> transformed = (BoundaryAttribute<S>) entry.getValue().getAttribute();
+                    for (final BSPTree<S> splitter : original.getSplitters()) {
                         transformed.getSplitters().add(map.get(splitter));
                     }
                 }
@@ -144,25 +144,25 @@ public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
      * @param map transformed nodes map
      * @return a new tree
      */
-    private BSPTree<T> recurseTransform(final BSPTree<T> node,
-                                        final Hyperplane<S> transformed,
-                                        final Transform<S, T> transform,
-                                        final Map<BSPTree<T>, BSPTree<T>> map) {
+    private BSPTree<S> recurseTransform(final BSPTree<S> node,
+                                        final Hyperplane<P> transformed,
+                                        final Transform<P, S> transform,
+                                        final Map<BSPTree<S>, BSPTree<S>> map) {
 
-        final BSPTree<T> transformedNode;
+        final BSPTree<S> transformedNode;
         if (node.getCut() == null) {
             transformedNode = new BSPTree<>(node.getAttribute());
         } else {
 
             @SuppressWarnings("unchecked")
-            BoundaryAttribute<T> attribute = (BoundaryAttribute<T>) node.getAttribute();
+            BoundaryAttribute<S> attribute = (BoundaryAttribute<S>) node.getAttribute();
             if (attribute != null) {
-                final SubHyperplane<T> tPO = (attribute.getPlusOutside() == null) ?
+                final SubHyperplane<S> tPO = (attribute.getPlusOutside() == null) ?
                     null : transform.apply(attribute.getPlusOutside(), hyperplane, transformed);
-                final SubHyperplane<T> tPI = (attribute.getPlusInside() == null) ?
+                final SubHyperplane<S> tPI = (attribute.getPlusInside() == null) ?
                     null : transform.apply(attribute.getPlusInside(), hyperplane, transformed);
                 // we start with an empty list of splitters, it will be filled in out of recursion
-                attribute = new BoundaryAttribute<>(tPO, tPI, new NodesSet<T>());
+                attribute = new BoundaryAttribute<>(tPO, tPI, new NodesSet<S>());
             }
 
             transformedNode = new BSPTree<>(transform.apply(node.getCut(), hyperplane, transformed),
@@ -178,7 +178,7 @@ public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
 
     /** {@inheritDoc} */
     @Override
-    public abstract SplitSubHyperplane<S> split(Hyperplane<S> hyper);
+    public abstract SplitSubHyperplane<P> split(Hyperplane<P> hyper);
 
     /** {@inheritDoc} */
     @Override
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BSPTree.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BSPTree.java
index cd57774..4a6a407 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BSPTree.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BSPTree.java
@@ -20,7 +20,6 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 
 /** This class represent a Binary Space Partition tree.
 
@@ -56,21 +55,21 @@ import org.apache.commons.geometry.core.Space;
  * Computer Graphics 24(4), August 1990, pp 115-124, published by the
  * Association for Computing Machinery (ACM).</p>
 
- * @param <S> Type of the space.
+ * @param <P> Point type defining the space
  */
-public class BSPTree<S extends Space> {
+public class BSPTree<P extends Point<P>> {
 
     /** Cut sub-hyperplane. */
-    private SubHyperplane<S> cut;
+    private SubHyperplane<P> cut;
 
     /** Tree at the plus side of the cut hyperplane. */
-    private BSPTree<S> plus;
+    private BSPTree<P> plus;
 
     /** Tree at the minus side of the cut hyperplane. */
-    private BSPTree<S> minus;
+    private BSPTree<P> minus;
 
     /** Parent tree. */
-    private BSPTree<S> parent;
+    private BSPTree<P> parent;
 
     /** Application-defined attribute. */
     private Object attribute;
@@ -109,7 +108,7 @@ public class BSPTree<S extends Space> {
      * @param attribute attribute associated with the node (may be null)
      * @see #insertCut
      */
-    public BSPTree(final SubHyperplane<S> cut, final BSPTree<S> plus, final BSPTree<S> minus,
+    public BSPTree(final SubHyperplane<P> cut, final BSPTree<P> plus, final BSPTree<P> minus,
                    final Object attribute) {
         this.cut       = cut;
         this.plus      = plus;
@@ -143,14 +142,14 @@ public class BSPTree<S extends Space> {
      * the cell now has two leaf child nodes)
      * @see #BSPTree(SubHyperplane, BSPTree, BSPTree, Object)
      */
-    public boolean insertCut(final Hyperplane<S> hyperplane) {
+    public boolean insertCut(final Hyperplane<P> hyperplane) {
 
         if (cut != null) {
             plus.parent  = null;
             minus.parent = null;
         }
 
-        final SubHyperplane<S> chopped = fitToCell(hyperplane.wholeHyperplane());
+        final SubHyperplane<P> chopped = fitToCell(hyperplane.wholeHyperplane());
         if (chopped == null || chopped.isEmpty()) {
             cut          = null;
             plus         = null;
@@ -174,7 +173,7 @@ public class BSPTree<S extends Space> {
      * objects).</p>
      * @return a new tree, copy of the instance
      */
-    public BSPTree<S> copySelf() {
+    public BSPTree<P> copySelf() {
 
         if (cut == null) {
             return new BSPTree<>(attribute);
@@ -188,7 +187,7 @@ public class BSPTree<S extends Space> {
     /** Get the cut sub-hyperplane.
      * @return cut sub-hyperplane, null if this is a leaf tree
      */
-    public SubHyperplane<S> getCut() {
+    public SubHyperplane<P> getCut() {
         return cut;
     }
 
@@ -196,7 +195,7 @@ public class BSPTree<S extends Space> {
      * @return tree on the plus side of the cut hyperplane, null if this
      * is a leaf tree
      */
-    public BSPTree<S> getPlus() {
+    public BSPTree<P> getPlus() {
         return plus;
     }
 
@@ -204,14 +203,14 @@ public class BSPTree<S extends Space> {
      * @return tree on the minus side of the cut hyperplane, null if this
      * is a leaf tree
      */
-    public BSPTree<S> getMinus() {
+    public BSPTree<P> getMinus() {
         return minus;
     }
 
     /** Get the parent node.
      * @return parent node, null if the node has no parents
      */
-    public BSPTree<S> getParent() {
+    public BSPTree<P> getParent() {
         return parent;
     }
 
@@ -236,7 +235,7 @@ public class BSPTree<S extends Space> {
     /** Visit the BSP tree nodes.
      * @param visitor object visiting the tree nodes
      */
-    public void visit(final BSPTreeVisitor<S> visitor) {
+    public void visit(final BSPTreeVisitor<P> visitor) {
         if (cut == null) {
             visitor.visitLeafNode(this);
         } else {
@@ -284,9 +283,9 @@ public class BSPTree<S extends Space> {
      * @return a new sub-hyperplane, guaranteed to have no part outside
      * of the instance cell
      */
-    private SubHyperplane<S> fitToCell(final SubHyperplane<S> sub) {
-        SubHyperplane<S> s = sub;
-        for (BSPTree<S> tree = this; tree.parent != null && s != null; tree = tree.parent) {
+    private SubHyperplane<P> fitToCell(final SubHyperplane<P> sub) {
+        SubHyperplane<P> s = sub;
+        for (BSPTree<P> tree = this; tree.parent != null && s != null; tree = tree.parent) {
             if (tree == tree.parent.plus) {
                 s = s.split(tree.parent.cut.getHyperplane()).getPlus();
             } else {
@@ -305,7 +304,7 @@ public class BSPTree<S extends Space> {
      * are considered to belong to the hyperplane itself
      * @return the tree cell to which the point belongs
      */
-    public BSPTree<S> getCell(final Point<S> point, final double tolerance) {
+    public BSPTree<P> getCell(final P point, final double tolerance) {
 
         if (cut == null) {
             return this;
@@ -333,8 +332,8 @@ public class BSPTree<S extends Space> {
      * @return close cells (may be empty if all cut sub-hyperplanes are farther
      * than maxOffset from the point)
      */
-    public List<BSPTree<S>> getCloseCuts(final Point<S> point, final double maxOffset) {
-        final List<BSPTree<S>> close = new ArrayList<>();
+    public List<BSPTree<P>> getCloseCuts(final P point, final double maxOffset) {
+        final List<BSPTree<P>> close = new ArrayList<>();
         recurseCloseCuts(point, maxOffset, close);
         return close;
     }
@@ -345,8 +344,8 @@ public class BSPTree<S extends Space> {
      * close to the point (in absolute value)
      * @param close list to fill
      */
-    private void recurseCloseCuts(final Point<S> point, final double maxOffset,
-                                  final List<BSPTree<S>> close) {
+    private void recurseCloseCuts(final P point, final double maxOffset,
+                                  final List<BSPTree<P>> close) {
         if (cut != null) {
 
             // position of the point with respect to the cut hyperplane
@@ -401,7 +400,7 @@ public class BSPTree<S extends Space> {
      * tree</code>, this value can be ignored if parentTree is not null
      * since all connections have already been established
      */
-    public BSPTree<S> merge(final BSPTree<S> tree, final LeafMerger<S> leafMerger) {
+    public BSPTree<P> merge(final BSPTree<P> tree, final LeafMerger<P> leafMerger) {
         return merge(tree, leafMerger, null, false);
     }
 
@@ -420,8 +419,8 @@ public class BSPTree<S extends Space> {
      * tree</code>, this value can be ignored if parentTree is not null
      * since all connections have already been established
      */
-    private BSPTree<S> merge(final BSPTree<S> tree, final LeafMerger<S> leafMerger,
-                             final BSPTree<S> parentTree, final boolean isPlusChild) {
+    private BSPTree<P> merge(final BSPTree<P> tree, final LeafMerger<P> leafMerger,
+                             final BSPTree<P> parentTree, final boolean isPlusChild) {
         if (cut == null) {
             // cell/tree operation
             return leafMerger.merge(this, tree, parentTree, isPlusChild, true);
@@ -430,7 +429,7 @@ public class BSPTree<S extends Space> {
             return leafMerger.merge(tree, this, parentTree, isPlusChild, false);
         } else {
             // tree/tree operation
-            final BSPTree<S> merged = tree.split(cut);
+            final BSPTree<P> merged = tree.split(cut);
             if (parentTree != null) {
                 merged.parent = parentTree;
                 if (isPlusChild) {
@@ -469,7 +468,7 @@ public class BSPTree<S extends Space> {
      * difference and symmetric difference (exclusive or).</p>
      * @param <S> Type of the space.
      */
-    public interface LeafMerger<S extends Space> {
+    public interface LeafMerger<S extends Point<S>> {
 
         /** Merge a leaf node and a tree node.
          * <p>This method is called at the end of a recursive merging
@@ -516,7 +515,7 @@ public class BSPTree<S extends Space> {
      * </p>
      * @param <S> Type of the space.
      */
-    public interface VanishingCutHandler<S extends Space> {
+    public interface VanishingCutHandler<S extends Point<S>> {
 
         /** Fix a node with both vanished cut and children.
          * @param node node to fix
@@ -544,19 +543,19 @@ public class BSPTree<S extends Space> {
      * sub-hyperplane, the two parts of the split instance as its two
      * sub-trees and a null parent
      */
-    public BSPTree<S> split(final SubHyperplane<S> sub) {
+    public BSPTree<P> split(final SubHyperplane<P> sub) {
 
         if (cut == null) {
-            return new BSPTree<>(sub, copySelf(), new BSPTree<S>(attribute), null);
+            return new BSPTree<>(sub, copySelf(), new BSPTree<P>(attribute), null);
         }
 
-        final Hyperplane<S> cHyperplane = cut.getHyperplane();
-        final Hyperplane<S> sHyperplane = sub.getHyperplane();
-        final SubHyperplane.SplitSubHyperplane<S> subParts = sub.split(cHyperplane);
+        final Hyperplane<P> cHyperplane = cut.getHyperplane();
+        final Hyperplane<P> sHyperplane = sub.getHyperplane();
+        final SubHyperplane.SplitSubHyperplane<P> subParts = sub.split(cHyperplane);
         switch (subParts.getSide()) {
         case PLUS :
         { // the partitioning sub-hyperplane is entirely in the plus sub-tree
-            final BSPTree<S> split = plus.split(sub);
+            final BSPTree<P> split = plus.split(sub);
             if (cut.split(sHyperplane).getSide() == Side.PLUS) {
                 split.plus =
                     new BSPTree<>(cut.copySelf(), split.plus, minus.copySelf(), attribute);
@@ -572,7 +571,7 @@ public class BSPTree<S extends Space> {
         }
         case MINUS :
         { // the partitioning sub-hyperplane is entirely in the minus sub-tree
-            final BSPTree<S> split = minus.split(sub);
+            final BSPTree<P> split = minus.split(sub);
             if (cut.split(sHyperplane).getSide() == Side.PLUS) {
                 split.plus =
                     new BSPTree<>(cut.copySelf(), plus.copySelf(), split.plus, attribute);
@@ -588,13 +587,13 @@ public class BSPTree<S extends Space> {
         }
         case BOTH :
         {
-            final SubHyperplane.SplitSubHyperplane<S> cutParts = cut.split(sHyperplane);
-            final BSPTree<S> split =
+            final SubHyperplane.SplitSubHyperplane<P> cutParts = cut.split(sHyperplane);
+            final BSPTree<P> split =
                 new BSPTree<>(sub, plus.split(subParts.getPlus()), minus.split(subParts.getMinus()),
                                null);
             split.plus.cut          = cutParts.getPlus();
             split.minus.cut         = cutParts.getMinus();
-            final BSPTree<S> tmp    = split.plus.minus;
+            final BSPTree<P> tmp    = split.plus.minus;
             split.plus.minus        = split.minus.plus;
             split.plus.minus.parent = split.plus;
             split.minus.plus        = tmp;
@@ -622,8 +621,8 @@ public class BSPTree<S extends Space> {
      * cases of vanishing cut sub-hyperplanes in internal nodes during merging
      * @see LeafMerger
      */
-    public void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild,
-                             final VanishingCutHandler<S> vanishingHandler) {
+    public void insertInTree(final BSPTree<P> parentTree, final boolean isPlusChild,
+                             final VanishingCutHandler<P> vanishingHandler) {
 
         // set up parent/child links
         parent = parentTree;
@@ -639,10 +638,10 @@ public class BSPTree<S extends Space> {
         if (cut != null) {
 
             // explore the parent nodes from here towards tree root
-            for (BSPTree<S> tree = this; tree.parent != null; tree = tree.parent) {
+            for (BSPTree<P> tree = this; tree.parent != null; tree = tree.parent) {
 
                 // this is an hyperplane of some parent node
-                final Hyperplane<S> hyperplane = tree.parent.cut.getHyperplane();
+                final Hyperplane<P> hyperplane = tree.parent.cut.getHyperplane();
 
                 // chop off the parts of the inserted tree that extend
                 // on the wrong side of this parent hyperplane
@@ -658,7 +657,7 @@ public class BSPTree<S extends Space> {
 
                 if (cut == null) {
                     // the cut sub-hyperplane has vanished
-                    final BSPTree<S> fixed = vanishingHandler.fixNode(this);
+                    final BSPTree<P> fixed = vanishingHandler.fixNode(this);
                     cut       = fixed.cut;
                     plus      = fixed.plus;
                     minus     = fixed.minus;
@@ -696,17 +695,17 @@ public class BSPTree<S extends Space> {
      * a single branch with the cell as a leaf node, and other leaf nodes
      * as the remnants of the pruned branches
      */
-    public BSPTree<S> pruneAroundConvexCell(final Object cellAttribute,
+    public BSPTree<P> pruneAroundConvexCell(final Object cellAttribute,
                                             final Object otherLeafsAttributes,
                                             final Object internalAttributes) {
 
         // build the current cell leaf
-        BSPTree<S> tree = new BSPTree<>(cellAttribute);
+        BSPTree<P> tree = new BSPTree<>(cellAttribute);
 
         // build the pruned tree bottom-up
-        for (BSPTree<S> current = this; current.parent != null; current = current.parent) {
-            final SubHyperplane<S> parentCut = current.parent.cut.copySelf();
-            final BSPTree<S>       sibling   = new BSPTree<>(otherLeafsAttributes);
+        for (BSPTree<P> current = this; current.parent != null; current = current.parent) {
+            final SubHyperplane<P> parentCut = current.parent.cut.copySelf();
+            final BSPTree<P>       sibling   = new BSPTree<>(otherLeafsAttributes);
             if (current == current.parent.plus) {
                 tree = new BSPTree<>(parentCut, tree, sibling, internalAttributes);
             } else {
@@ -726,7 +725,7 @@ public class BSPTree<S extends Space> {
      * @param vanishingHandler handler to use for handling very rare corner
      * cases of vanishing cut sub-hyperplanes in internal nodes during merging
      */
-    private void chopOffMinus(final Hyperplane<S> hyperplane, final VanishingCutHandler<S> vanishingHandler) {
+    private void chopOffMinus(final Hyperplane<P> hyperplane, final VanishingCutHandler<P> vanishingHandler) {
         if (cut != null) {
 
             cut = cut.split(hyperplane).getPlus();
@@ -735,7 +734,7 @@ public class BSPTree<S extends Space> {
 
             if (cut == null) {
                 // the cut sub-hyperplane has vanished
-                final BSPTree<S> fixed = vanishingHandler.fixNode(this);
+                final BSPTree<P> fixed = vanishingHandler.fixNode(this);
                 cut       = fixed.cut;
                 plus      = fixed.plus;
                 minus     = fixed.minus;
@@ -753,7 +752,7 @@ public class BSPTree<S extends Space> {
      * @param vanishingHandler handler to use for handling very rare corner
      * cases of vanishing cut sub-hyperplanes in internal nodes during merging
      */
-    private void chopOffPlus(final Hyperplane<S> hyperplane, final VanishingCutHandler<S> vanishingHandler) {
+    private void chopOffPlus(final Hyperplane<P> hyperplane, final VanishingCutHandler<P> vanishingHandler) {
         if (cut != null) {
 
             cut = cut.split(hyperplane).getMinus();
@@ -762,7 +761,7 @@ public class BSPTree<S extends Space> {
 
             if (cut == null) {
                 // the cut sub-hyperplane has vanished
-                final BSPTree<S> fixed = vanishingHandler.fixNode(this);
+                final BSPTree<P> fixed = vanishingHandler.fixNode(this);
                 cut       = fixed.cut;
                 plus      = fixed.plus;
                 minus     = fixed.minus;
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BSPTreeVisitor.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BSPTreeVisitor.java
index f7bbdbb..52d0eee 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BSPTreeVisitor.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BSPTreeVisitor.java
@@ -16,7 +16,7 @@
  */
 package org.apache.commons.geometry.core.partitioning;
 
-import org.apache.commons.geometry.core.Space;
+import org.apache.commons.geometry.core.Point;
 
 /** This interface is used to visit {@link BSPTree BSP tree} nodes.
 
@@ -40,12 +40,12 @@ import org.apache.commons.geometry.core.Space;
  *   </li>
  * </ul>
 
- * @param <S> Type of the space.
+ * @param <P> Point type defining the space
 
  * @see BSPTree
  * @see SubHyperplane
  */
-public interface BSPTreeVisitor<S extends Space> {
+public interface BSPTreeVisitor<P extends Point<P>> {
 
     /** Enumerate for visit order with respect to plus sub-tree, minus sub-tree and cut sub-hyperplane. */
     enum Order {
@@ -92,7 +92,7 @@ public interface BSPTreeVisitor<S extends Space> {
      * {@link Order#MINUS_PLUS_SUB}, {@link Order#MINUS_SUB_PLUS},
      * {@link Order#SUB_PLUS_MINUS}, {@link Order#SUB_MINUS_PLUS}
      */
-    Order visitOrder(BSPTree<S> node);
+    Order visitOrder(BSPTree<P> node);
 
     /** Visit a BSP tree node node having a non-null sub-hyperplane.
      * <p>It is guaranteed that this method will be called after {@link
@@ -101,12 +101,12 @@ public interface BSPTreeVisitor<S extends Space> {
      * @param node BSP node guaranteed to have a non null cut sub-hyperplane
      * @see #visitLeafNode
      */
-    void visitInternalNode(BSPTree<S> node);
+    void visitInternalNode(BSPTree<P> node);
 
     /** Visit a leaf BSP tree node node having a null sub-hyperplane.
      * @param node leaf BSP node having a null sub-hyperplane
      * @see #visitInternalNode
      */
-    void visitLeafNode(BSPTree<S> node);
+    void visitLeafNode(BSPTree<P> node);
 
 }
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryAttribute.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryAttribute.java
index ad6a365..0476c34 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryAttribute.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryAttribute.java
@@ -16,36 +16,39 @@
  */
 package org.apache.commons.geometry.core.partitioning;
 
-import org.apache.commons.geometry.core.Space;
+import org.apache.commons.geometry.core.Point;
 
 /** Class holding boundary attributes.
+ *
  * <p>This class is used for the attributes associated with the
  * nodes of region boundary shell trees returned by the {@link
  * Region#getTree(boolean) Region.getTree(includeBoundaryAttributes)}
  * when the boolean {@code includeBoundaryAttributes} parameter is
  * set to {@code true}. It contains the parts of the node cut
  * sub-hyperplane that belong to the boundary.</p>
+ *
  * <p>This class is a simple placeholder, it does not provide any
  * processing methods.</p>
- * @param <S> Type of the space.
+ *
+ * @param <P> Point type defining the space
  * @see Region#getTree
  */
-public class BoundaryAttribute<S extends Space> {
+public class BoundaryAttribute<P extends Point<P>> {
 
     /** Part of the node cut sub-hyperplane that belongs to the
      * boundary and has the outside of the region on the plus side of
      * its underlying hyperplane (may be null).
      */
-    private final SubHyperplane<S> plusOutside;
+    private final SubHyperplane<P> plusOutside;
 
     /** Part of the node cut sub-hyperplane that belongs to the
      * boundary and has the inside of the region on the plus side of
      * its underlying hyperplane (may be null).
      */
-    private final SubHyperplane<S> plusInside;
+    private final SubHyperplane<P> plusInside;
 
     /** Sub-hyperplanes that were used to split the boundary part. */
-    private final NodesSet<S> splitters;
+    private final NodesSet<P> splitters;
 
     /** Simple constructor.
      * @param plusOutside part of the node cut sub-hyperplane that
@@ -57,9 +60,9 @@ public class BoundaryAttribute<S extends Space> {
      * @param splitters sub-hyperplanes that were used to
      * split the boundary part (may be null)
      */
-    BoundaryAttribute(final SubHyperplane<S> plusOutside,
-                      final SubHyperplane<S> plusInside,
-                      final NodesSet<S> splitters) {
+    BoundaryAttribute(final SubHyperplane<P> plusOutside,
+                      final SubHyperplane<P> plusInside,
+                      final NodesSet<P> splitters) {
         this.plusOutside = plusOutside;
         this.plusInside  = plusInside;
         this.splitters   = splitters;
@@ -72,7 +75,7 @@ public class BoundaryAttribute<S extends Space> {
      * boundary and has the outside of the region on the plus side of
      * its underlying hyperplane
      */
-    public SubHyperplane<S> getPlusOutside() {
+    public SubHyperplane<P> getPlusOutside() {
         return plusOutside;
     }
 
@@ -83,14 +86,14 @@ public class BoundaryAttribute<S extends Space> {
      * boundary and has the inside of the region on the plus side of
      * its underlying hyperplane
      */
-    public SubHyperplane<S> getPlusInside() {
+    public SubHyperplane<P> getPlusInside() {
         return plusInside;
     }
 
     /** Get the sub-hyperplanes that were used to split the boundary part.
      * @return sub-hyperplanes that were used to split the boundary part
      */
-    public NodesSet<S> getSplitters() {
+    public NodesSet<P> getSplitters() {
         return splitters;
     }
 
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryBuilder.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryBuilder.java
index 816d3c2..63d19b8 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryBuilder.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryBuilder.java
@@ -16,40 +16,42 @@
  */
 package org.apache.commons.geometry.core.partitioning;
 
-import org.apache.commons.geometry.core.Space;
+import org.apache.commons.geometry.core.Point;
 
 /** Visitor building boundary shell tree.
+ *
  * <p>
  * The boundary shell is represented as {@link BoundaryAttribute boundary attributes}
  * at each internal node.
  * </p>
- * @param <S> Type of the space.
+ *
+ * @param <P> Point type defining the space.
  */
-class BoundaryBuilder<S extends Space> implements BSPTreeVisitor<S> {
+class BoundaryBuilder<P extends Point<P>> implements BSPTreeVisitor<P> {
 
     /** {@inheritDoc} */
     @Override
-    public Order visitOrder(BSPTree<S> node) {
+    public Order visitOrder(BSPTree<P> node) {
         return Order.PLUS_MINUS_SUB;
     }
 
     /** {@inheritDoc} */
     @Override
-    public void visitInternalNode(BSPTree<S> node) {
+    public void visitInternalNode(BSPTree<P> node) {
 
-        SubHyperplane<S> plusOutside = null;
-        SubHyperplane<S> plusInside  = null;
-        NodesSet<S>      splitters   = null;
+        SubHyperplane<P> plusOutside = null;
+        SubHyperplane<P> plusInside  = null;
+        NodesSet<P>      splitters   = null;
 
         // characterize the cut sub-hyperplane,
         // first with respect to the plus sub-tree
-        final Characterization<S> plusChar = new Characterization<>(node.getPlus(), node.getCut().copySelf());
+        final Characterization<P> plusChar = new Characterization<>(node.getPlus(), node.getCut().copySelf());
 
         if (plusChar.touchOutside()) {
             // plusChar.outsideTouching() corresponds to a subset of the cut sub-hyperplane
             // known to have outside cells on its plus side, we want to check if parts
             // of this subset do have inside cells on their minus side
-            final Characterization<S> minusChar = new Characterization<>(node.getMinus(), plusChar.outsideTouching());
+            final Characterization<P> minusChar = new Characterization<>(node.getMinus(), plusChar.outsideTouching());
             if (minusChar.touchInside()) {
                 // this part belongs to the boundary,
                 // it has the outside on its plus side and the inside on its minus side
@@ -64,7 +66,7 @@ class BoundaryBuilder<S extends Space> implements BSPTreeVisitor<S> {
             // plusChar.insideTouching() corresponds to a subset of the cut sub-hyperplane
             // known to have inside cells on its plus side, we want to check if parts
             // of this subset do have outside cells on their minus side
-            final Characterization<S> minusChar = new Characterization<>(node.getMinus(), plusChar.insideTouching());
+            final Characterization<P> minusChar = new Characterization<>(node.getMinus(), plusChar.insideTouching());
             if (minusChar.touchOutside()) {
                 // this part belongs to the boundary,
                 // it has the inside on its plus side and the outside on its minus side
@@ -79,7 +81,7 @@ class BoundaryBuilder<S extends Space> implements BSPTreeVisitor<S> {
 
         if (splitters != null) {
             // the parent nodes are natural splitters for boundary sub-hyperplanes
-            for (BSPTree<S> up = node.getParent(); up != null; up = up.getParent()) {
+            for (BSPTree<P> up = node.getParent(); up != null; up = up.getParent()) {
                 splitters.add(up);
             }
         }
@@ -91,7 +93,7 @@ class BoundaryBuilder<S extends Space> implements BSPTreeVisitor<S> {
 
     /** {@inheritDoc} */
     @Override
-    public void visitLeafNode(BSPTree<S> node) {
+    public void visitLeafNode(BSPTree<P> node) {
     }
 
 }
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryProjection.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryProjection.java
index 1d5254d..27709c2 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryProjection.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryProjection.java
@@ -17,22 +17,24 @@
 package org.apache.commons.geometry.core.partitioning;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 
 /** Class holding the result of point projection on region boundary.
+ *
  * <p>This class is a simple placeholder, it does not provide any
  * processing methods.</p>
+ *
  * <p>Instances of this class are guaranteed to be immutable</p>
- * @param <S> Type of the space.
+ *
+ * @param <P> Point type defining the space
  * @see AbstractRegion#projectToBoundary(Point)
  */
-public class BoundaryProjection<S extends Space> {
+public class BoundaryProjection<P extends Point<P>> {
 
     /** Original point. */
-    private final Point<S> original;
+    private final P original;
 
     /** Projected point. */
-    private final Point<S> projected;
+    private final P projected;
 
     /** Offset of the point with respect to the boundary it is projected on. */
     private final double offset;
@@ -42,7 +44,7 @@ public class BoundaryProjection<S extends Space> {
      * @param projected projected point
      * @param offset offset of the point with respect to the boundary it is projected on
      */
-    public BoundaryProjection(final Point<S> original, final Point<S> projected, final double offset) {
+    public BoundaryProjection(final P original, final P projected, final double offset) {
         this.original  = original;
         this.projected = projected;
         this.offset    = offset;
@@ -51,14 +53,14 @@ public class BoundaryProjection<S extends Space> {
     /** Get the original point.
      * @return original point
      */
-    public Point<S> getOriginal() {
+    public P getOriginal() {
         return original;
     }
 
     /** Projected point.
      * @return projected point, or null if there are no boundary
      */
-    public Point<S> getProjected() {
+    public P getProjected() {
         return projected;
     }
 
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryProjector.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryProjector.java
index 390695c..f694a89 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryProjector.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundaryProjector.java
@@ -20,23 +20,22 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 import org.apache.commons.geometry.core.partitioning.Region.Location;
 
 /** Local tree visitor to compute projection on boundary.
- * @param <S> Type of the space.
- * @param <T> Type of the sub-space.
+ * @param <P> Point type defining the space
+ * @param <S> Point type defining the sub-space
  */
-class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisitor<S> {
+class BoundaryProjector<P extends Point<P>, S extends Point<S>> implements BSPTreeVisitor<P> {
 
     /** Original point. */
-    private final Point<S> original;
+    private final P original;
 
     /** Current best projected point. */
-    private Point<S> projected;
+    private P projected;
 
     /** Leaf node closest to the test point. */
-    private BSPTree<S> leaf;
+    private BSPTree<P> leaf;
 
     /** Current offset. */
     private double offset;
@@ -44,7 +43,7 @@ class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisi
     /** Simple constructor.
      * @param original original point
      */
-    BoundaryProjector(final Point<S> original) {
+    BoundaryProjector(final P original) {
         this.original  = original;
         this.projected = null;
         this.leaf      = null;
@@ -53,7 +52,7 @@ class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisi
 
     /** {@inheritDoc} */
     @Override
-    public Order visitOrder(final BSPTree<S> node) {
+    public Order visitOrder(final BSPTree<P> node) {
         // we want to visit the tree so that the first encountered
         // leaf is the one closest to the test point
         if (node.getCut().getHyperplane().getOffset(original) <= 0) {
@@ -65,22 +64,22 @@ class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisi
 
     /** {@inheritDoc} */
     @Override
-    public void visitInternalNode(final BSPTree<S> node) {
+    public void visitInternalNode(final BSPTree<P> node) {
 
         // project the point on the cut sub-hyperplane
-        final Hyperplane<S> hyperplane = node.getCut().getHyperplane();
+        final Hyperplane<P> hyperplane = node.getCut().getHyperplane();
         final double signedOffset = hyperplane.getOffset(original);
         if (Math.abs(signedOffset) < offset) {
 
             // project point
-            final Point<S> regular = hyperplane.project(original);
+            final P regular = hyperplane.project(original);
 
             // get boundary parts
-            final List<Region<T>> boundaryParts = boundaryRegions(node);
+            final List<Region<S>> boundaryParts = boundaryRegions(node);
 
             // check if regular projection really belongs to the boundary
             boolean regularFound = false;
-            for (final Region<T> part : boundaryParts) {
+            for (final Region<S> part : boundaryParts) {
                 if (!regularFound && belongsToPart(regular, hyperplane, part)) {
                     // the projected point lies in the boundary
                     projected    = regular;
@@ -93,8 +92,8 @@ class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisi
                 // the regular projected point is not on boundary,
                 // so we have to check further if a singular point
                 // (i.e. a vertex in 2D case) is a possible projection
-                for (final Region<T> part : boundaryParts) {
-                    final Point<S> spI = singularProjection(regular, hyperplane, part);
+                for (final Region<S> part : boundaryParts) {
+                    final P spI = singularProjection(regular, hyperplane, part);
                     if (spI != null) {
                         final double distance = original.distance(spI);
                         if (distance < offset) {
@@ -112,7 +111,7 @@ class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisi
 
     /** {@inheritDoc} */
     @Override
-    public void visitLeafNode(final BSPTree<S> node) {
+    public void visitLeafNode(final BSPTree<P> node) {
         if (leaf == null) {
             // this is the first leaf we visit,
             // it is the closest one to the original point
@@ -123,7 +122,7 @@ class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisi
     /** Get the projection.
      * @return projection
      */
-    public BoundaryProjection<S> getProjection() {
+    public BoundaryProjection<P> getProjection() {
 
         // fix offset sign
         offset = Math.copySign(offset, (Boolean) leaf.getAttribute() ? -1 : +1);
@@ -136,12 +135,12 @@ class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisi
      * @param node internal node
      * @return regions in the node sub-hyperplane
      */
-    private List<Region<T>> boundaryRegions(final BSPTree<S> node) {
+    private List<Region<S>> boundaryRegions(final BSPTree<P> node) {
 
-        final List<Region<T>> regions = new ArrayList<>(2);
+        final List<Region<S>> regions = new ArrayList<>(2);
 
         @SuppressWarnings("unchecked")
-        final BoundaryAttribute<S> ba = (BoundaryAttribute<S>) node.getAttribute();
+        final BoundaryAttribute<P> ba = (BoundaryAttribute<P>) node.getAttribute();
         addRegion(ba.getPlusInside(),  regions);
         addRegion(ba.getPlusOutside(), regions);
 
@@ -153,10 +152,10 @@ class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisi
      * @param sub sub-hyperplane defining the region
      * @param list to fill up
      */
-    private void addRegion(final SubHyperplane<S> sub, final List<Region<T>> list) {
+    private void addRegion(final SubHyperplane<P> sub, final List<Region<S>> list) {
         if (sub != null) {
             @SuppressWarnings("unchecked")
-            final Region<T> region = ((AbstractSubHyperplane<S, T>) sub).getRemainingRegion();
+            final Region<S> region = ((AbstractSubHyperplane<P, S>) sub).getRemainingRegion();
             if (region != null) {
                 list.add(region);
             }
@@ -169,12 +168,12 @@ class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisi
      * @param part boundary part
      * @return true if point lies on the boundary part
      */
-    private boolean belongsToPart(final Point<S> point, final Hyperplane<S> hyperplane,
-                                  final Region<T> part) {
+    private boolean belongsToPart(final P point, final Hyperplane<P> hyperplane,
+                                  final Region<S> part) {
 
         // there is a non-null sub-space, we can dive into smaller dimensions
         @SuppressWarnings("unchecked")
-        final Embedding<S, T> embedding = (Embedding<S, T>) hyperplane;
+        final Embedding<P, S> embedding = (Embedding<P, S>) hyperplane;
         return part.checkPoint(embedding.toSubSpace(point)) != Location.OUTSIDE;
 
     }
@@ -185,13 +184,13 @@ class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisi
      * @param part boundary part
      * @return projection to a singular point of boundary part (may be null)
      */
-    private Point<S> singularProjection(final Point<S> point, final Hyperplane<S> hyperplane,
-                                        final Region<T> part) {
+    private P singularProjection(final P point, final Hyperplane<P> hyperplane,
+                                        final Region<S> part) {
 
         // there is a non-null sub-space, we can dive into smaller dimensions
         @SuppressWarnings("unchecked")
-        final Embedding<S, T> embedding = (Embedding<S, T>) hyperplane;
-        final BoundaryProjection<T> bp = part.projectToBoundary(embedding.toSubSpace(point));
+        final Embedding<P, S> embedding = (Embedding<P, S>) hyperplane;
+        final BoundaryProjection<S> bp = part.projectToBoundary(embedding.toSubSpace(point));
 
         // back to initial dimension
         return (bp.getProjected() == null) ? null : embedding.toSpace(bp.getProjected());
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundarySizeVisitor.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundarySizeVisitor.java
index b305a36..e84c70a 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundarySizeVisitor.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/BoundarySizeVisitor.java
@@ -16,12 +16,12 @@
  */
 package org.apache.commons.geometry.core.partitioning;
 
-import org.apache.commons.geometry.core.Space;
+import org.apache.commons.geometry.core.Point;
 
 /** Visitor computing the boundary size.
- * @param <S> Type of the space.
+ * @param <P> Point type defining the space
  */
-class BoundarySizeVisitor<S extends Space> implements BSPTreeVisitor<S> {
+class BoundarySizeVisitor<P extends Point<P>> implements BSPTreeVisitor<P> {
 
     /** Size of the boundary. */
     private double boundarySize;
@@ -34,16 +34,16 @@ class BoundarySizeVisitor<S extends Space> implements BSPTreeVisitor<S> {
 
     /** {@inheritDoc}*/
     @Override
-    public Order visitOrder(final BSPTree<S> node) {
+    public Order visitOrder(final BSPTree<P> node) {
         return Order.MINUS_SUB_PLUS;
     }
 
     /** {@inheritDoc}*/
     @Override
-    public void visitInternalNode(final BSPTree<S> node) {
+    public void visitInternalNode(final BSPTree<P> node) {
         @SuppressWarnings("unchecked")
-        final BoundaryAttribute<S> attribute =
-            (BoundaryAttribute<S>) node.getAttribute();
+        final BoundaryAttribute<P> attribute =
+            (BoundaryAttribute<P>) node.getAttribute();
         if (attribute.getPlusOutside() != null) {
             boundarySize += attribute.getPlusOutside().getSize();
         }
@@ -54,7 +54,7 @@ class BoundarySizeVisitor<S extends Space> implements BSPTreeVisitor<S> {
 
     /** {@inheritDoc}*/
     @Override
-    public void visitLeafNode(final BSPTree<S> node) {
+    public void visitLeafNode(final BSPTree<P> node) {
     }
 
     /** Get the size of the boundary.
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Characterization.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Characterization.java
index 7184c96..d9ec6e7 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Characterization.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Characterization.java
@@ -19,25 +19,25 @@ package org.apache.commons.geometry.core.partitioning;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.commons.geometry.core.Space;
+import org.apache.commons.geometry.core.Point;
 
 /** Cut sub-hyperplanes characterization with respect to inside/outside cells.
  * @see BoundaryBuilder
- * @param <S> Type of the space.
+ * @param <P> Point type defining the space
  */
-class Characterization<S extends Space> {
+class Characterization<P extends Point<P>> {
 
     /** Part of the cut sub-hyperplane that touch outside cells. */
-    private SubHyperplane<S> outsideTouching;
+    private SubHyperplane<P> outsideTouching;
 
     /** Part of the cut sub-hyperplane that touch inside cells. */
-    private SubHyperplane<S> insideTouching;
+    private SubHyperplane<P> insideTouching;
 
     /** Nodes that were used to split the outside touching part. */
-    private final NodesSet<S> outsideSplitters;
+    private final NodesSet<P> outsideSplitters;
 
     /** Nodes that were used to split the outside touching part. */
-    private final NodesSet<S> insideSplitters;
+    private final NodesSet<P> insideSplitters;
 
     /** Simple constructor.
      * <p>Characterization consists in splitting the specified
@@ -51,12 +51,12 @@ class Characterization<S extends Space> {
      * @param node current BSP tree node
      * @param sub sub-hyperplane to characterize
      */
-    Characterization(final BSPTree<S> node, final SubHyperplane<S> sub) {
+    Characterization(final BSPTree<P> node, final SubHyperplane<P> sub) {
         outsideTouching  = null;
         insideTouching   = null;
         outsideSplitters = new NodesSet<>();
         insideSplitters  = new NodesSet<>();
-        characterize(node, sub, new ArrayList<BSPTree<S>>());
+        characterize(node, sub, new ArrayList<BSPTree<P>>());
     }
 
     /** Filter the parts of an hyperplane belonging to the boundary.
@@ -72,8 +72,8 @@ class Characterization<S extends Space> {
      * @param sub sub-hyperplane to characterize
      * @param splitters nodes that did split the current one
      */
-    private void characterize(final BSPTree<S> node, final SubHyperplane<S> sub,
-                              final List<BSPTree<S>> splitters) {
+    private void characterize(final BSPTree<P> node, final SubHyperplane<P> sub,
+                              final List<BSPTree<P>> splitters) {
         if (node.getCut() == null) {
             // we have reached a leaf node
             final boolean inside = (Boolean) node.getAttribute();
@@ -83,8 +83,8 @@ class Characterization<S extends Space> {
                 addOutsideTouching(sub, splitters);
             }
         } else {
-            final Hyperplane<S> hyperplane = node.getCut().getHyperplane();
-            final SubHyperplane.SplitSubHyperplane<S> split = sub.split(hyperplane);
+            final Hyperplane<P> hyperplane = node.getCut().getHyperplane();
+            final SubHyperplane.SplitSubHyperplane<P> split = sub.split(hyperplane);
             switch (split.getSide()) {
             case PLUS:
                 characterize(node.getPlus(),  sub, splitters);
@@ -117,8 +117,8 @@ class Characterization<S extends Space> {
      * @param sub part of the cut sub-hyperplane known to touch an outside cell
      * @param splitters sub-hyperplanes that did split the current one
      */
-    private void addOutsideTouching(final SubHyperplane<S> sub,
-                                    final List<BSPTree<S>> splitters) {
+    private void addOutsideTouching(final SubHyperplane<P> sub,
+                                    final List<BSPTree<P>> splitters) {
         if (outsideTouching == null) {
             outsideTouching = sub;
         } else {
@@ -131,8 +131,8 @@ class Characterization<S extends Space> {
      * @param sub part of the cut sub-hyperplane known to touch an inside cell
      * @param splitters sub-hyperplanes that did split the current one
      */
-    private void addInsideTouching(final SubHyperplane<S> sub,
-                                   final List<BSPTree<S>> splitters) {
+    private void addInsideTouching(final SubHyperplane<P> sub,
+                                   final List<BSPTree<P>> splitters) {
         if (insideTouching == null) {
             insideTouching = sub;
         } else {
@@ -152,7 +152,7 @@ class Characterization<S extends Space> {
      * @return parts of the cut sub-hyperplane known to touch outside cells
      * (may be null or empty)
      */
-    public SubHyperplane<S> outsideTouching() {
+    public SubHyperplane<P> outsideTouching() {
         return outsideTouching;
     }
 
@@ -163,7 +163,7 @@ class Characterization<S extends Space> {
      * </p>
      * @return nodes that were used to split the outside touching part
      */
-    public NodesSet<S> getOutsideSplitters() {
+    public NodesSet<P> getOutsideSplitters() {
         return outsideSplitters;
     }
 
@@ -178,7 +178,7 @@ class Characterization<S extends Space> {
      * @return parts of the cut sub-hyperplane known to touch inside cells
      * (may be null or empty)
      */
-    public SubHyperplane<S> insideTouching() {
+    public SubHyperplane<P> insideTouching() {
         return insideTouching;
     }
 
@@ -189,7 +189,7 @@ class Characterization<S extends Space> {
      * </p>
      * @return nodes that were used to split the inside touching part
      */
-    public NodesSet<S> getInsideSplitters() {
+    public NodesSet<P> getInsideSplitters() {
         return insideSplitters;
     }
 
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Embedding.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Embedding.java
index 7ed9ef5..f4ebd50 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Embedding.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Embedding.java
@@ -17,7 +17,6 @@
 package org.apache.commons.geometry.core.partitioning;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 
 /** This interface defines mappers between a space and one of its sub-spaces.
 
@@ -27,8 +26,8 @@ import org.apache.commons.geometry.core.Space;
  * of the dimensions differences. As an example, {@link
  * org.apache.commons.geometry.euclidean.threed.Line Line} in 3D
  * implements Embedding&lt;{@link
- * org.apache.commons.geometry.euclidean.threed.Cartesian3D Cartesian3D}, {@link
- * org.apache.commons.geometry.euclidean.oned.Cartesian1D Cartesian1D}&gt;, i.e. it
+ * org.apache.commons.geometry.euclidean.threed.Point3D Point3D}, {@link
+ * org.apache.commons.geometry.euclidean.oned.Point1D Point1D}&gt;, i.e. it
  * maps directly dimensions 3 and 1.</p>
 
  * <p>In the 3D euclidean space, hyperplanes are 2D planes, and the 1D
@@ -41,12 +40,12 @@ import org.apache.commons.geometry.core.Space;
  * versions, which breaks compatibility for external implementations.
  * </p>
 
- * @param <S> Type of the embedding space.
- * @param <T> Type of the embedded sub-space.
+ * @param <P> Point type defining the embedding space.
+ * @param <S> Point type defining the embedded sub-space.
 
  * @see Hyperplane
  */
-public interface Embedding<S extends Space, T extends Space> {
+public interface Embedding<P extends Point<P>, S extends Point<S>> {
 
     /** Transform a space point into a sub-space point.
      * @param point n-dimension point of the space
@@ -54,7 +53,7 @@ public interface Embedding<S extends Space, T extends Space> {
      * the specified space point
      * @see #toSpace
      */
-    Point<T> toSubSpace(Point<S> point);
+    S toSubSpace(P point);
 
     /** Transform a sub-space point into a space point.
      * @param point (n-1)-dimension point of the sub-space
@@ -62,6 +61,6 @@ public interface Embedding<S extends Space, T extends Space> {
      * specified sub-space point
      * @see #toSubSpace
      */
-    Point<S> toSpace(Point<T> point);
+    P toSpace(S point);
 
 }
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Hyperplane.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Hyperplane.java
index 8041658..9a29432 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Hyperplane.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Hyperplane.java
@@ -17,7 +17,6 @@
 package org.apache.commons.geometry.core.partitioning;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 
 /** This interface represents an hyperplane of a space.
 
@@ -32,14 +31,14 @@ import org.apache.commons.geometry.core.Space;
 
  * <p>
  * Note that this interface is <em>not</em> intended to be implemented
- * by Apache Commons Math users, it is only intended to be implemented
+ * by Apache Commons Geometry users, it is only intended to be implemented
  * within the library itself. New methods may be added even for minor
  * versions, which breaks compatibility for external implementations.
  * </p>
 
- * @param <S> Type of the space.
+ * @param <P> Point type defining the space
  */
-public interface Hyperplane<S extends Space> {
+public interface Hyperplane<P extends Point<P>> {
 
     /** Copy the instance.
      * <p>The instance created is completely independant of the original
@@ -47,7 +46,7 @@ public interface Hyperplane<S extends Space> {
      * shared (except for immutable objects).</p>
      * @return a new hyperplane, copy of the instance
      */
-    Hyperplane<S> copySelf();
+    Hyperplane<P> copySelf();
 
     /** Get the offset (oriented distance) of a point.
      * <p>The offset is 0 if the point is on the underlying hyperplane,
@@ -57,13 +56,13 @@ public interface Hyperplane<S extends Space> {
      * @param point point to check
      * @return offset of the point
      */
-    double getOffset(Point<S> point);
+    double getOffset(P point);
 
     /** Project a point to the hyperplane.
      * @param point point to project
      * @return projected point
      */
-    Point<S> project(Point<S> point);
+    P project(P point);
 
     /** Get the tolerance below which points are considered to belong to the hyperplane.
      * @return tolerance below which points are considered to belong to the hyperplane
@@ -79,16 +78,16 @@ public interface Hyperplane<S extends Space> {
      * @return true if the instance and the other hyperplane have
      * the same orientation
      */
-    boolean sameOrientationAs(Hyperplane<S> other);
+    boolean sameOrientationAs(Hyperplane<P> other);
 
     /** Build a sub-hyperplane covering the whole hyperplane.
      * @return a sub-hyperplane covering the whole hyperplane
      */
-    SubHyperplane<S> wholeHyperplane();
+    SubHyperplane<P> wholeHyperplane();
 
     /** Build a region covering the whole space.
      * @return a region containing the instance
      */
-    Region<S> wholeSpace();
+    Region<P> wholeSpace();
 
 }
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/InsideFinder.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/InsideFinder.java
index aec8a41..cb384d9 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/InsideFinder.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/InsideFinder.java
@@ -16,16 +16,16 @@
  */
 package org.apache.commons.geometry.core.partitioning;
 
-import org.apache.commons.geometry.core.Space;
+import org.apache.commons.geometry.core.Point;
 
 /** Utility class checking if inside nodes can be found
  * on the plus and minus sides of an hyperplane.
- * @param <S> Type of the space.
+ * @param <P> Point type defining the space
  */
-class InsideFinder<S extends Space> {
+class InsideFinder<P extends Point<P>> {
 
     /** Region on which to operate. */
-    private final Region<S> region;
+    private final Region<P> region;
 
     /** Indicator of inside leaf nodes found on the plus side. */
     private boolean plusFound;
@@ -36,7 +36,7 @@ class InsideFinder<S extends Space> {
     /** Simple constructor.
      * @param region region on which to operate
      */
-    InsideFinder(final Region<S> region) {
+    InsideFinder(final Region<P> region) {
         this.region = region;
         plusFound  = false;
         minusFound = false;
@@ -56,7 +56,7 @@ class InsideFinder<S extends Space> {
      * @param node current BSP tree node
      * @param sub sub-hyperplane
      */
-    public void recurseSides(final BSPTree<S> node, final SubHyperplane<S> sub) {
+    public void recurseSides(final BSPTree<P> node, final SubHyperplane<P> sub) {
 
         if (node.getCut() == null) {
             if ((Boolean) node.getAttribute()) {
@@ -67,8 +67,8 @@ class InsideFinder<S extends Space> {
             return;
         }
 
-        final Hyperplane<S> hyperplane = node.getCut().getHyperplane();
-        final SubHyperplane.SplitSubHyperplane<S> split = sub.split(hyperplane);
+        final Hyperplane<P> hyperplane = node.getCut().getHyperplane();
+        final SubHyperplane.SplitSubHyperplane<P> split = sub.split(hyperplane);
         switch (split.getSide()) {
         case PLUS :
             // the sub-hyperplane is entirely in the plus sub-tree
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/NodesSet.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/NodesSet.java
index 20ef6b7..54e0c3d 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/NodesSet.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/NodesSet.java
@@ -20,16 +20,16 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
-import org.apache.commons.geometry.core.Space;
+import org.apache.commons.geometry.core.Point;
 
 /** Set of {@link BSPTree BSP tree} nodes.
  * @see BoundaryAttribute
- * @param <S> Type of the space.
+ * @param <P> Point type defining the space
  */
-public class NodesSet<S extends Space> implements Iterable<BSPTree<S>> {
+public class NodesSet<P extends Point<P>> implements Iterable<BSPTree<P>> {
 
     /** List of sub-hyperplanes. */
-    private final List<BSPTree<S>> list;
+    private final List<BSPTree<P>> list;
 
     /** Simple constructor.
      */
@@ -40,9 +40,9 @@ public class NodesSet<S extends Space> implements Iterable<BSPTree<S>> {
     /** Add a node if not already known.
      * @param node node to add
      */
-    public void add(final BSPTree<S> node) {
+    public void add(final BSPTree<P> node) {
 
-        for (final BSPTree<S> existing : list) {
+        for (final BSPTree<P> existing : list) {
             if (node == existing) {
                 // the node is already known, don't add it
                 return;
@@ -57,15 +57,15 @@ public class NodesSet<S extends Space> implements Iterable<BSPTree<S>> {
     /** Add nodes if they are not already known.
      * @param iterator nodes iterator
      */
-    public void addAll(final Iterable<BSPTree<S>> iterator) {
-        for (final BSPTree<S> node : iterator) {
+    public void addAll(final Iterable<BSPTree<P>> iterator) {
+        for (final BSPTree<P> node : iterator) {
             add(node);
         }
     }
 
     /** {@inheritDoc} */
     @Override
-    public Iterator<BSPTree<S>> iterator() {
+    public Iterator<BSPTree<P>> iterator() {
         return list.iterator();
     }
 
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Region.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Region.java
index 63155a5..4fef7e3 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Region.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Region.java
@@ -17,7 +17,6 @@
 package org.apache.commons.geometry.core.partitioning;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 
 /** This interface represents a region of a space as a partition.
 
@@ -46,9 +45,9 @@ import org.apache.commons.geometry.core.Space;
  * versions, which breaks compatibility for external implementations.
  * </p>
 
- * @param <S> Type of the space.
+ * @param <P> Point type defining the space
  */
-public interface Region<S extends Space> {
+public interface Region<P extends Point<P>> {
 
     /** Enumerate for the location of a point with respect to the region. */
     enum Location {
@@ -78,7 +77,7 @@ public interface Region<S extends Space> {
      * @param newTree inside/outside BSP tree representing the new region
      * @return the built region
      */
-    Region<S> buildNew(BSPTree<S> newTree);
+    Region<P> buildNew(BSPTree<P> newTree);
 
     /** Copy the instance.
      * <p>The instance created is completely independant of the original
@@ -87,7 +86,7 @@ public interface Region<S extends Space> {
      * attributes and immutable objects).</p>
      * @return a new region, copy of the instance
      */
-    Region<S> copySelf();
+    Region<P> copySelf();
 
     /** Check if the instance is empty.
      * @return true if the instance is empty
@@ -101,7 +100,7 @@ public interface Region<S extends Space> {
      * property)
      * @return true if the sub-tree starting at the given node is empty
      */
-    boolean isEmpty(final BSPTree<S> node);
+    boolean isEmpty(final BSPTree<P> node);
 
     /** Check if the instance covers the full space.
      * @return true if the instance covers the full space
@@ -115,26 +114,26 @@ public interface Region<S extends Space> {
      * property)
      * @return true if the sub-tree starting at the given node covers the full space
      */
-    boolean isFull(final BSPTree<S> node);
+    boolean isFull(final BSPTree<P> node);
 
     /** Check if the instance entirely contains another region.
      * @param region region to check against the instance
      * @return true if the instance contains the specified tree
      */
-    boolean contains(final Region<S> region);
+    boolean contains(final Region<P> region);
 
     /** Check a point with respect to the region.
      * @param point point to check
      * @return a code representing the point status: either {@link
      * Location#INSIDE}, {@link Location#OUTSIDE} or {@link Location#BOUNDARY}
      */
-    Location checkPoint(final Point<S> point);
+    Location checkPoint(final P point);
 
     /** Project a point on the boundary of the region.
      * @param point point to check
      * @return projection of the point on the boundary
      */
-    BoundaryProjection<S> projectToBoundary(final Point<S> point);
+    BoundaryProjection<P> projectToBoundary(final P point);
 
     /** Get the underlying BSP tree.
 
@@ -175,7 +174,7 @@ public interface Region<S extends Space> {
      * @return underlying BSP tree
      * @see BoundaryAttribute
      */
-    BSPTree<S> getTree(final boolean includeBoundaryAttributes);
+    BSPTree<P> getTree(final boolean includeBoundaryAttributes);
 
     /** Get the size of the boundary.
      * @return the size of the boundary (this is 0 in 1D, a length in
@@ -192,7 +191,7 @@ public interface Region<S extends Space> {
     /** Get the barycenter of the instance.
      * @return an object representing the barycenter
      */
-    Point<S> getBarycenter();
+    P getBarycenter();
 
     /** Get the parts of a sub-hyperplane that are contained in the region.
      * <p>The parts of the sub-hyperplane that belong to the boundary are
@@ -200,6 +199,6 @@ public interface Region<S extends Space> {
      * @param sub sub-hyperplane traversing the region
      * @return filtered sub-hyperplane
      */
-    SubHyperplane<S> intersection(final SubHyperplane<S> sub);
+    SubHyperplane<P> intersection(final SubHyperplane<P> sub);
 
 }
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/RegionFactory.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/RegionFactory.java
index 61a888a..c15676c 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/RegionFactory.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/RegionFactory.java
@@ -20,16 +20,15 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 import org.apache.commons.geometry.core.partitioning.BSPTree.VanishingCutHandler;
 import org.apache.commons.geometry.core.partitioning.Region.Location;
 import org.apache.commons.geometry.core.partitioning.SubHyperplane.SplitSubHyperplane;
 
 /** This class is a factory for {@link Region}.
 
- * @param <S> Type of the space.
+ * @param <P> Point type defining the space
  */
-public class RegionFactory<S extends Space> {
+public class RegionFactory<P extends Point<P>> {
 
     /** Visitor removing internal nodes attributes. */
     private final NodesCleaner nodeCleaner;
@@ -45,18 +44,18 @@ public class RegionFactory<S extends Space> {
      * @return a new convex region, or null if the collection is empty
      */
     @SafeVarargs
-    public final Region<S> buildConvex(final Hyperplane<S> ... hyperplanes) {
+    public final Region<P> buildConvex(final Hyperplane<P> ... hyperplanes) {
         if ((hyperplanes == null) || (hyperplanes.length == 0)) {
             return null;
         }
 
         // use the first hyperplane to build the right class
-        final Region<S> region = hyperplanes[0].wholeSpace();
+        final Region<P> region = hyperplanes[0].wholeSpace();
 
         // chop off parts of the space
-        BSPTree<S> node = region.getTree(false);
+        BSPTree<P> node = region.getTree(false);
         node.setAttribute(Boolean.TRUE);
-        for (final Hyperplane<S> hyperplane : hyperplanes) {
+        for (final Hyperplane<P> hyperplane : hyperplanes) {
             if (node.insertCut(hyperplane)) {
                 node.setAttribute(null);
                 node.getPlus().setAttribute(Boolean.FALSE);
@@ -66,10 +65,10 @@ public class RegionFactory<S extends Space> {
                 // the hyperplane could not be inserted in the current leaf node
                 // either it is completely outside (which means the input hyperplanes
                 // are wrong), or it is parallel to a previous hyperplane
-                SubHyperplane<S> s = hyperplane.wholeHyperplane();
-                for (BSPTree<S> tree = node; tree.getParent() != null && s != null; tree = tree.getParent()) {
-                    final Hyperplane<S>         other = tree.getParent().getCut().getHyperplane();
-                    final SplitSubHyperplane<S> split = s.split(other);
+                SubHyperplane<P> s = hyperplane.wholeHyperplane();
+                for (BSPTree<P> tree = node; tree.getParent() != null && s != null; tree = tree.getParent()) {
+                    final Hyperplane<P>         other = tree.getParent().getCut().getHyperplane();
+                    final SplitSubHyperplane<P> split = s.split(other);
                     switch (split.getSide()) {
                         case HYPER :
                             // the hyperplane is parallel to a previous hyperplane
@@ -102,8 +101,8 @@ public class RegionFactory<S extends Space> {
      * parts of it will be reused in the new region)
      * @return a new region, result of {@code region1 union region2}
      */
-    public Region<S> union(final Region<S> region1, final Region<S> region2) {
-        final BSPTree<S> tree =
+    public Region<P> union(final Region<P> region1, final Region<P> region2) {
+        final BSPTree<P> tree =
             region1.getTree(false).merge(region2.getTree(false), new UnionMerger());
         tree.visit(nodeCleaner);
         return region1.buildNew(tree);
@@ -116,8 +115,8 @@ public class RegionFactory<S extends Space> {
      * parts of it will be reused in the new region)
      * @return a new region, result of {@code region1 intersection region2}
      */
-    public Region<S> intersection(final Region<S> region1, final Region<S> region2) {
-        final BSPTree<S> tree =
+    public Region<P> intersection(final Region<P> region1, final Region<P> region2) {
+        final BSPTree<P> tree =
             region1.getTree(false).merge(region2.getTree(false), new IntersectionMerger());
         tree.visit(nodeCleaner);
         return region1.buildNew(tree);
@@ -130,8 +129,8 @@ public class RegionFactory<S extends Space> {
      * parts of it will be reused in the new region)
      * @return a new region, result of {@code region1 xor region2}
      */
-    public Region<S> xor(final Region<S> region1, final Region<S> region2) {
-        final BSPTree<S> tree =
+    public Region<P> xor(final Region<P> region1, final Region<P> region2) {
+        final BSPTree<P> tree =
             region1.getTree(false).merge(region2.getTree(false), new XorMerger());
         tree.visit(nodeCleaner);
         return region1.buildNew(tree);
@@ -144,8 +143,8 @@ public class RegionFactory<S extends Space> {
      * parts of it will be reused in the new region)
      * @return a new region, result of {@code region1 minus region2}
      */
-    public Region<S> difference(final Region<S> region1, final Region<S> region2) {
-        final BSPTree<S> tree =
+    public Region<P> difference(final Region<P> region1, final Region<P> region2) {
+        final BSPTree<P> tree =
             region1.getTree(false).merge(region2.getTree(false), new DifferenceMerger(region1, region2));
         tree.visit(nodeCleaner);
         return region1.buildNew(tree);
@@ -161,7 +160,7 @@ public class RegionFactory<S extends Space> {
      * region independent region will be built
      * @return a new region, complement of the specified one
      */
-    public Region<S> getComplement(final Region<S> region) {
+    public Region<P> getComplement(final Region<P> region) {
         return region.buildNew(recurseComplement(region.getTree(false)));
     }
 
@@ -169,21 +168,21 @@ public class RegionFactory<S extends Space> {
      * @param node current node of the original tree
      * @return new tree, complement of the node
      */
-    private BSPTree<S> recurseComplement(final BSPTree<S> node) {
+    private BSPTree<P> recurseComplement(final BSPTree<P> node) {
 
         // transform the tree, except for boundary attribute splitters
-        final Map<BSPTree<S>, BSPTree<S>> map = new HashMap<>();
-        final BSPTree<S> transformedTree = recurseComplement(node, map);
+        final Map<BSPTree<P>, BSPTree<P>> map = new HashMap<>();
+        final BSPTree<P> transformedTree = recurseComplement(node, map);
 
         // set up the boundary attributes splitters
-        for (final Map.Entry<BSPTree<S>, BSPTree<S>> entry : map.entrySet()) {
+        for (final Map.Entry<BSPTree<P>, BSPTree<P>> entry : map.entrySet()) {
             if (entry.getKey().getCut() != null) {
                 @SuppressWarnings("unchecked")
-                BoundaryAttribute<S> original = (BoundaryAttribute<S>) entry.getKey().getAttribute();
+                BoundaryAttribute<P> original = (BoundaryAttribute<P>) entry.getKey().getAttribute();
                 if (original != null) {
                     @SuppressWarnings("unchecked")
-                    BoundaryAttribute<S> transformed = (BoundaryAttribute<S>) entry.getValue().getAttribute();
-                    for (final BSPTree<S> splitter : original.getSplitters()) {
+                    BoundaryAttribute<P> transformed = (BoundaryAttribute<P>) entry.getValue().getAttribute();
+                    for (final BSPTree<P> splitter : original.getSplitters()) {
                         transformed.getSplitters().add(map.get(splitter));
                     }
                 }
@@ -199,23 +198,23 @@ public class RegionFactory<S extends Space> {
      * @param map transformed nodes map
      * @return new tree, complement of the node
      */
-    private BSPTree<S> recurseComplement(final BSPTree<S> node,
-                                         final Map<BSPTree<S>, BSPTree<S>> map) {
+    private BSPTree<P> recurseComplement(final BSPTree<P> node,
+                                         final Map<BSPTree<P>, BSPTree<P>> map) {
 
-        final BSPTree<S> transformedNode;
+        final BSPTree<P> transformedNode;
         if (node.getCut() == null) {
             transformedNode = new BSPTree<>(((Boolean) node.getAttribute()) ? Boolean.FALSE : Boolean.TRUE);
         } else {
 
             @SuppressWarnings("unchecked")
-            BoundaryAttribute<S> attribute = (BoundaryAttribute<S>) node.getAttribute();
+            BoundaryAttribute<P> attribute = (BoundaryAttribute<P>) node.getAttribute();
             if (attribute != null) {
-                final SubHyperplane<S> plusOutside =
+                final SubHyperplane<P> plusOutside =
                         (attribute.getPlusInside() == null) ? null : attribute.getPlusInside().copySelf();
-                final SubHyperplane<S> plusInside  =
+                final SubHyperplane<P> plusInside  =
                         (attribute.getPlusOutside() == null) ? null : attribute.getPlusOutside().copySelf();
                 // we start with an empty list of splitters, it will be filled in out of recursion
-                attribute = new BoundaryAttribute<>(plusOutside, plusInside, new NodesSet<S>());
+                attribute = new BoundaryAttribute<>(plusOutside, plusInside, new NodesSet<P>());
             }
 
             transformedNode = new BSPTree<>(node.getCut().copySelf(),
@@ -230,11 +229,11 @@ public class RegionFactory<S extends Space> {
     }
 
     /** BSP tree leaf merger computing union of two regions. */
-    private class UnionMerger implements BSPTree.LeafMerger<S> {
+    private class UnionMerger implements BSPTree.LeafMerger<P> {
         /** {@inheritDoc} */
         @Override
-        public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree,
-                                final BSPTree<S> parentTree,
+        public BSPTree<P> merge(final BSPTree<P> leaf, final BSPTree<P> tree,
+                                final BSPTree<P> parentTree,
                                 final boolean isPlusChild, final boolean leafFromInstance) {
             if ((Boolean) leaf.getAttribute()) {
                 // the leaf node represents an inside cell
@@ -248,11 +247,11 @@ public class RegionFactory<S extends Space> {
     }
 
     /** BSP tree leaf merger computing intersection of two regions. */
-    private class IntersectionMerger implements BSPTree.LeafMerger<S> {
+    private class IntersectionMerger implements BSPTree.LeafMerger<P> {
         /** {@inheritDoc} */
         @Override
-        public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree,
-                                final BSPTree<S> parentTree,
+        public BSPTree<P> merge(final BSPTree<P> leaf, final BSPTree<P> tree,
+                                final BSPTree<P> parentTree,
                                 final boolean isPlusChild, final boolean leafFromInstance) {
             if ((Boolean) leaf.getAttribute()) {
                 // the leaf node represents an inside cell
@@ -266,13 +265,13 @@ public class RegionFactory<S extends Space> {
     }
 
     /** BSP tree leaf merger computing symmetric difference (exclusive or) of two regions. */
-    private class XorMerger implements BSPTree.LeafMerger<S> {
+    private class XorMerger implements BSPTree.LeafMerger<P> {
         /** {@inheritDoc} */
         @Override
-        public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree,
-                                final BSPTree<S> parentTree, final boolean isPlusChild,
+        public BSPTree<P> merge(final BSPTree<P> leaf, final BSPTree<P> tree,
+                                final BSPTree<P> parentTree, final boolean isPlusChild,
                                 final boolean leafFromInstance) {
-            BSPTree<S> t = tree;
+            BSPTree<P> t = tree;
             if ((Boolean) leaf.getAttribute()) {
                 // the leaf node represents an inside cell
                 t = recurseComplement(t);
@@ -283,37 +282,37 @@ public class RegionFactory<S extends Space> {
     }
 
     /** BSP tree leaf merger computing difference of two regions. */
-    private class DifferenceMerger implements BSPTree.LeafMerger<S>, VanishingCutHandler<S> {
+    private class DifferenceMerger implements BSPTree.LeafMerger<P>, VanishingCutHandler<P> {
 
         /** Region to subtract from. */
-        private final Region<S> region1;
+        private final Region<P> region1;
 
         /** Region to subtract. */
-        private final Region<S> region2;
+        private final Region<P> region2;
 
         /** Simple constructor.
          * @param region1 region to subtract from
          * @param region2 region to subtract
          */
-        DifferenceMerger(final Region<S> region1, final Region<S> region2) {
+        DifferenceMerger(final Region<P> region1, final Region<P> region2) {
             this.region1 = region1.copySelf();
             this.region2 = region2.copySelf();
         }
 
         /** {@inheritDoc} */
         @Override
-        public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree,
-                                final BSPTree<S> parentTree, final boolean isPlusChild,
+        public BSPTree<P> merge(final BSPTree<P> leaf, final BSPTree<P> tree,
+                                final BSPTree<P> parentTree, final boolean isPlusChild,
                                 final boolean leafFromInstance) {
             if ((Boolean) leaf.getAttribute()) {
                 // the leaf node represents an inside cell
-                final BSPTree<S> argTree =
+                final BSPTree<P> argTree =
                     recurseComplement(leafFromInstance ? tree : leaf);
                 argTree.insertInTree(parentTree, isPlusChild, this);
                 return argTree;
             }
             // the leaf node represents an outside cell
-            final BSPTree<S> instanceTree =
+            final BSPTree<P> instanceTree =
                 leafFromInstance ? leaf : tree;
             instanceTree.insertInTree(parentTree, isPlusChild, this);
             return instanceTree;
@@ -321,11 +320,11 @@ public class RegionFactory<S extends Space> {
 
         /** {@inheritDoc} */
         @Override
-        public BSPTree<S> fixNode(final BSPTree<S> node) {
+        public BSPTree<P> fixNode(final BSPTree<P> node) {
             // get a representative point in the degenerate cell
-            final BSPTree<S> cell = node.pruneAroundConvexCell(Boolean.TRUE, Boolean.FALSE, null);
-            final Region<S> r = region1.buildNew(cell);
-            final Point<S> p = r.getBarycenter();
+            final BSPTree<P> cell = node.pruneAroundConvexCell(Boolean.TRUE, Boolean.FALSE, null);
+            final Region<P> r = region1.buildNew(cell);
+            final P p = r.getBarycenter();
             return new BSPTree<>(region1.checkPoint(p) == Location.INSIDE &&
                                   region2.checkPoint(p) == Location.OUTSIDE);
         }
@@ -333,29 +332,29 @@ public class RegionFactory<S extends Space> {
     }
 
     /** Visitor removing internal nodes attributes. */
-    private class NodesCleaner implements  BSPTreeVisitor<S> {
+    private class NodesCleaner implements  BSPTreeVisitor<P> {
 
         /** {@inheritDoc} */
         @Override
-        public Order visitOrder(final BSPTree<S> node) {
+        public Order visitOrder(final BSPTree<P> node) {
             return Order.PLUS_SUB_MINUS;
         }
 
         /** {@inheritDoc} */
         @Override
-        public void visitInternalNode(final BSPTree<S> node) {
+        public void visitInternalNode(final BSPTree<P> node) {
             node.setAttribute(null);
         }
 
         /** {@inheritDoc} */
         @Override
-        public void visitLeafNode(final BSPTree<S> node) {
+        public void visitLeafNode(final BSPTree<P> node) {
         }
 
     }
 
     /** Handler replacing nodes with vanishing cuts with leaf nodes. */
-    private class VanishingToLeaf implements VanishingCutHandler<S> {
+    private class VanishingToLeaf implements VanishingCutHandler<P> {
 
         /** Inside/outside indocator to use for ambiguous nodes. */
         private final boolean inside;
@@ -369,7 +368,7 @@ public class RegionFactory<S extends Space> {
 
         /** {@inheritDoc} */
         @Override
-        public BSPTree<S> fixNode(final BSPTree<S> node) {
+        public BSPTree<P> fixNode(final BSPTree<P> node) {
             if (node.getPlus().getAttribute().equals(node.getMinus().getAttribute())) {
                 // no ambiguity
                 return new BSPTree<>(node.getPlus().getAttribute());
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/SubHyperplane.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/SubHyperplane.java
index da8b24d..7237bb7 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/SubHyperplane.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/SubHyperplane.java
@@ -16,7 +16,7 @@
  */
 package org.apache.commons.geometry.core.partitioning;
 
-import org.apache.commons.geometry.core.Space;
+import org.apache.commons.geometry.core.Point;
 
 /** This interface represents the remaining parts of an hyperplane after
  * other parts have been chopped off.
@@ -35,9 +35,9 @@ import org.apache.commons.geometry.core.Space;
  * versions, which breaks compatibility for external implementations.
  * </p>
 
- * @param <S> Type of the embedding space.
+ * @param <P> Point type defining the embedding space.
  */
-public interface SubHyperplane<S extends Space> {
+public interface SubHyperplane<P extends Point<P>> {
 
     /** Copy the instance.
      * <p>The instance created is completely independent of the original
@@ -46,12 +46,12 @@ public interface SubHyperplane<S extends Space> {
      * objects).</p>
      * @return a new sub-hyperplane, copy of the instance
      */
-    SubHyperplane<S> copySelf();
+    SubHyperplane<P> copySelf();
 
     /** Get the underlying hyperplane.
      * @return underlying hyperplane
      */
-    Hyperplane<S> getHyperplane();
+    Hyperplane<P> getHyperplane();
 
     /** Check if the instance is empty.
      * @return true if the instance is empty
@@ -70,19 +70,19 @@ public interface SubHyperplane<S extends Space> {
      * on the plus side of the hyperplane and the part of the
      * instance on the minus side of the hyperplane
      */
-    SplitSubHyperplane<S> split(Hyperplane<S> hyperplane);
+    SplitSubHyperplane<P> split(Hyperplane<P> hyperplane);
 
     /** Compute the union of the instance and another sub-hyperplane.
      * @param other other sub-hyperplane to union (<em>must</em> be in the
      * same hyperplane as the instance)
      * @return a new sub-hyperplane, union of the instance and other
      */
-    SubHyperplane<S> reunite(SubHyperplane<S> other);
+    SubHyperplane<P> reunite(SubHyperplane<P> other);
 
     /** Class holding the results of the {@link #split split} method.
      * @param <U> Type of the embedding space.
      */
-    class SplitSubHyperplane<U extends Space> {
+    class SplitSubHyperplane<U extends Point<U>> {
 
         /** Part of the sub-hyperplane on the plus side of the splitting hyperplane. */
         private final SubHyperplane<U> plus;
diff --git a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Transform.java b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Transform.java
index a034d6c..53cb056 100644
--- a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Transform.java
+++ b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/partitioning/Transform.java
@@ -17,7 +17,6 @@
 package org.apache.commons.geometry.core.partitioning;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 
 
 /** This interface represents an inversible affine transform in a space.
@@ -46,22 +45,22 @@ import org.apache.commons.geometry.core.Space;
  *   </li>
  * </ul>
 
- * @param <S> Type of the embedding space.
- * @param <T> Type of the embedded sub-space.
+ * @param <P> Point type defining the embedding space.
+ * @param <S> Point type defining the embedded sub-space.
  */
-public interface Transform<S extends Space, T extends Space> {
+public interface Transform<P extends Point<P>, S extends Point<S>> {
 
     /** Transform a point of a space.
      * @param point point to transform
      * @return a new object representing the transformed point
      */
-    Point<S> apply(Point<S> point);
+    P apply(P point);
 
     /** Transform an hyperplane of a space.
      * @param hyperplane hyperplane to transform
      * @return a new object representing the transformed hyperplane
      */
-    Hyperplane<S> apply(Hyperplane<S> hyperplane);
+    Hyperplane<P> apply(Hyperplane<P> hyperplane);
 
     /** Transform a sub-hyperplane embedded in an hyperplane.
      * @param sub sub-hyperplane to transform
@@ -73,6 +72,6 @@ public interface Transform<S extends Space, T extends Space> {
      * <em>has</em> been applied to it)
      * @return a new object representing the transformed sub-hyperplane
      */
-    SubHyperplane<T> apply(SubHyperplane<T> sub, Hyperplane<S> original, Hyperplane<S> transformed);
+    SubHyperplane<S> apply(SubHyperplane<S> sub, Hyperplane<P> original, Hyperplane<P> transformed);
 
 }
diff --git a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreeBuilder.java b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreeBuilder.java
index 364d702..3ad8e8e 100644
--- a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreeBuilder.java
+++ b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreeBuilder.java
@@ -20,15 +20,12 @@ import java.io.IOException;
 import java.text.ParseException;
 import java.util.StringTokenizer;
 
-import org.apache.commons.geometry.core.Space;
-import org.apache.commons.geometry.core.partitioning.AbstractRegion;
-import org.apache.commons.geometry.core.partitioning.BSPTree;
-import org.apache.commons.geometry.core.partitioning.Hyperplane;
+import org.apache.commons.geometry.core.Point;
 
 /** Local class for building an {@link AbstractRegion} tree.
- * @param <S> Type of the space.
+ * @param <P> Point type defining the space
  */
-public abstract class TreeBuilder<S extends Space> {
+public abstract class TreeBuilder<P extends Point<P>> {
 
     /** Keyword for tolerance. */
     private static final String TOLERANCE = "tolerance";
@@ -52,7 +49,7 @@ public abstract class TreeBuilder<S extends Space> {
     private static final String FALSE     = "false";
 
     /** Tree root. */
-    private BSPTree<S> root;
+    private BSPTree<P> root;
 
     /** Tolerance. */
     private final double tolerance;
@@ -86,7 +83,7 @@ public abstract class TreeBuilder<S extends Space> {
      * @exception IOException if the string cannot be read
      * @exception ParseException if the string cannot be parsed
      */
-    private void parseTree(final BSPTree<S> node)
+    private void parseTree(final BSPTree<P> node)
         throws IOException, ParseException {
         if (INTERNAL.equals(getWord(INTERNAL, LEAF))) {
             // this is an internal node, it has a cut sub-hyperplane (stored as a whole hyperplane)
@@ -142,7 +139,7 @@ public abstract class TreeBuilder<S extends Space> {
     /** Get the built tree.
      * @return built tree
      */
-    public BSPTree<S> getTree() {
+    public BSPTree<P> getTree() {
         return root;
     }
 
@@ -158,7 +155,7 @@ public abstract class TreeBuilder<S extends Space> {
      * @exception IOException if the string cannot be read
      * @exception ParseException if the string cannot be parsed
      */
-    protected abstract Hyperplane<S> parseHyperplane()
+    protected abstract Hyperplane<P> parseHyperplane()
         throws IOException, ParseException;
 
 }
\ No newline at end of file
diff --git a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreeDumper.java b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreeDumper.java
index 532b9f9..6c1285f 100644
--- a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreeDumper.java
+++ b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreeDumper.java
@@ -19,15 +19,12 @@ package org.apache.commons.geometry.core.partitioning;
 import java.util.Formatter;
 import java.util.Locale;
 
-import org.apache.commons.geometry.core.Space;
-import org.apache.commons.geometry.core.partitioning.BSPTree;
-import org.apache.commons.geometry.core.partitioning.BSPTreeVisitor;
-import org.apache.commons.geometry.core.partitioning.Hyperplane;
+import org.apache.commons.geometry.core.Point;
 
 /** Dumping visitor.
- * @param <S> Type of the space.
+ * @param <P> Point type defining the space
  */
-public abstract class TreeDumper<S extends Space> implements BSPTreeVisitor<S> {
+public abstract class TreeDumper<P extends Point<P>> implements BSPTreeVisitor<P> {
     /** Builder for the string representation of the dumped tree. */
     private final StringBuilder dump;
 
@@ -66,17 +63,17 @@ public abstract class TreeDumper<S extends Space> implements BSPTreeVisitor<S> {
     /** Format a string representation of the hyperplane underlying a cut sub-hyperplane.
      * @param hyperplane hyperplane to format
      */
-    protected abstract void formatHyperplane(Hyperplane<S> hyperplane);
+    protected abstract void formatHyperplane(Hyperplane<P> hyperplane);
 
     /** {@inheritDoc} */
     @Override
-    public Order visitOrder(final BSPTree<S> node) {
+    public Order visitOrder(final BSPTree<P> node) {
         return Order.SUB_MINUS_PLUS;
     }
 
     /** {@inheritDoc} */
     @Override
-    public void visitInternalNode(final BSPTree<S> node) {
+    public void visitInternalNode(final BSPTree<P> node) {
         formatter.format("%s %s internal ", prefix, type(node));
         formatHyperplane(node.getCut().getHyperplane());
         formatter.format("%n");
@@ -85,10 +82,10 @@ public abstract class TreeDumper<S extends Space> implements BSPTreeVisitor<S> {
 
     /** {@inheritDoc} */
     @Override
-    public void visitLeafNode(final BSPTree<S> node) {
+    public void visitLeafNode(final BSPTree<P> node) {
         formatter.format("%s %s leaf %s%n",
                          prefix, type(node), node.getAttribute());
-        for (BSPTree<S> n = node;
+        for (BSPTree<P> n = node;
              n.getParent() != null && n == n.getParent().getPlus();
              n = n.getParent()) {
             prefix = prefix.substring(0, prefix.length() - 2);
@@ -100,7 +97,7 @@ public abstract class TreeDumper<S extends Space> implements BSPTreeVisitor<S> {
      * @return "plus " or "minus" depending on the node being the plus or minus
      * child of its parent ("plus " is arbitrarily returned for the root node)
      */
-    private String type(final BSPTree<S> node) {
+    private String type(final BSPTree<P> node) {
         return (node.getParent() != null && node == node.getParent().getMinus()) ? "minus" : "plus ";
     }
 }
diff --git a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreePrinter.java b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreePrinter.java
index 6f79537..75acd92 100644
--- a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreePrinter.java
+++ b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/partitioning/TreePrinter.java
@@ -18,14 +18,12 @@ package org.apache.commons.geometry.core.partitioning;
 
 import java.util.Objects;
 
-import org.apache.commons.geometry.core.Space;
-import org.apache.commons.geometry.core.partitioning.BSPTree;
-import org.apache.commons.geometry.core.partitioning.BSPTreeVisitor;
+import org.apache.commons.geometry.core.Point;
 
 /** Base for classes that create string representations of {@link BSPTree}s.
- * @param <S>
+ * @param <P> Point type defining the space
  */
-public abstract class TreePrinter<S extends Space> implements BSPTreeVisitor<S> {
+public abstract class TreePrinter<P extends Point<P>> implements BSPTreeVisitor<P> {
 
     /** Indent per tree level */
     protected static final String INDENT = "    ";
@@ -40,7 +38,7 @@ public abstract class TreePrinter<S extends Space> implements BSPTreeVisitor<S>
      * @param tree
      * @return
      */
-    public String writeAsString(BSPTree<S> tree) {
+    public String writeAsString(BSPTree<P> tree) {
         output.delete(0, output.length());
 
         tree.visit(this);
@@ -50,13 +48,13 @@ public abstract class TreePrinter<S extends Space> implements BSPTreeVisitor<S>
 
     /** {@inheritDoc} */
     @Override
-    public Order visitOrder(BSPTree<S> node) {
+    public Order visitOrder(BSPTree<P> node) {
         return Order.SUB_MINUS_PLUS;
     }
 
     /** {@inheritDoc} */
     @Override
-    public void visitInternalNode(BSPTree<S> node) {
+    public void visitInternalNode(BSPTree<P> node) {
         writeLinePrefix(node);
         writeInternalNode(node);
 
@@ -67,13 +65,13 @@ public abstract class TreePrinter<S extends Space> implements BSPTreeVisitor<S>
 
     /** {@inheritDoc} */
     @Override
-    public void visitLeafNode(BSPTree<S> node) {
+    public void visitLeafNode(BSPTree<P> node) {
         writeLinePrefix(node);
         writeLeafNode(node);
 
         write("\n");
 
-        BSPTree<S> cur = node;
+        BSPTree<P> cur = node;
         while (cur.getParent() != null && cur.getParent().getPlus() == cur) {
             --depth;
             cur = cur.getParent();
@@ -85,7 +83,7 @@ public abstract class TreePrinter<S extends Space> implements BSPTreeVisitor<S>
      * for the node itself.
      * @param node
      */
-    protected void writeLinePrefix(BSPTree<S> node) {
+    protected void writeLinePrefix(BSPTree<P> node) {
         for (int i=0; i<depth; ++i) {
             write(INDENT);
         }
@@ -106,7 +104,7 @@ public abstract class TreePrinter<S extends Space> implements BSPTreeVisitor<S>
      * @param node
      * @return
      */
-    protected String nodeIdString(BSPTree<S> node) {
+    protected String nodeIdString(BSPTree<P> node) {
         String str = Objects.toString(node);
         int idx = str.lastIndexOf('.');
         if (idx > -1) {
@@ -125,13 +123,13 @@ public abstract class TreePrinter<S extends Space> implements BSPTreeVisitor<S>
     /** Method for subclasses to provide their own string representation
      * of the given internal node.
      */
-    protected abstract void writeInternalNode(BSPTree<S> node);
+    protected abstract void writeInternalNode(BSPTree<P> node);
 
     /** Writes a leaf node. The default implementation here simply writes
      * the node attribute as a string.
      * @param node
      */
-    protected void writeLeafNode(BSPTree<S> node) {
+    protected void writeLeafNode(BSPTree<P> node) {
         write(String.valueOf(node.getAttribute()));
     }
 }
\ No newline at end of file
diff --git a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/Encloser.java b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/Encloser.java
index a99023a..7fd1f18 100644
--- a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/Encloser.java
+++ b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/Encloser.java
@@ -17,19 +17,18 @@
 package org.apache.commons.geometry.enclosing;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 
 /** Interface for algorithms computing enclosing balls.
  * @param <S> Space type.
  * @param <P> Point type.
  * @see EnclosingBall
  */
-public interface Encloser<S extends Space, P extends Point<S>> {
+public interface Encloser<P extends Point<P>> {
 
     /** Find a ball enclosing a list of points.
      * @param points points to enclose
      * @return enclosing ball
      */
-    EnclosingBall<S, P> enclose(Iterable<P> points);
+    EnclosingBall<P> enclose(Iterable<P> points);
 
 }
diff --git a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/EnclosingBall.java b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/EnclosingBall.java
index b269747..7ed537b 100644
--- a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/EnclosingBall.java
+++ b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/EnclosingBall.java
@@ -19,7 +19,6 @@ package org.apache.commons.geometry.enclosing;
 import java.io.Serializable;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 
 /** This class represents a ball enclosing some points.
  * @param <S> Space type.
@@ -28,7 +27,7 @@ import org.apache.commons.geometry.core.Space;
  * @see Point
  * @see Encloser
  */
-public class EnclosingBall<S extends Space, P extends Point<S>> implements Serializable {
+public class EnclosingBall<P extends Point<P>> implements Serializable {
 
     /** Serializable UID. */
     private static final long serialVersionUID = 20140126L;
diff --git a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/SupportBallGenerator.java b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/SupportBallGenerator.java
index 53a9229..6d0e603 100644
--- a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/SupportBallGenerator.java
+++ b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/SupportBallGenerator.java
@@ -19,7 +19,6 @@ package org.apache.commons.geometry.enclosing;
 import java.util.List;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 
 /** Interface for generating balls based on support points.
  * <p>
@@ -30,12 +29,12 @@ import org.apache.commons.geometry.core.Space;
  * @param <P> Point type.
  * @see EnclosingBall
  */
-public interface SupportBallGenerator<S extends Space, P extends Point<S>> {
+public interface SupportBallGenerator<P extends Point<P>> {
 
     /** Create a ball whose boundary lies on prescribed support points.
      * @param support support points (may be empty)
      * @return ball whose boundary lies on the prescribed support points
      */
-    EnclosingBall<S, P> ballOnSupport(List<P> support);
+    EnclosingBall<P> ballOnSupport(List<P> support);
 
 }
diff --git a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/WelzlEncloser.java b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/WelzlEncloser.java
index c20c706..f825ade 100644
--- a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/WelzlEncloser.java
+++ b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/enclosing/WelzlEncloser.java
@@ -20,7 +20,6 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
 
 /** Class implementing Emo Welzl algorithm to find the smallest enclosing ball in linear time.
  * <p>
@@ -38,26 +37,26 @@ import org.apache.commons.geometry.core.Space;
  * @param <S> Space type.
  * @param <P> Point type.
  */
-public class WelzlEncloser<S extends Space, P extends Point<S>> implements Encloser<S, P> {
+public class WelzlEncloser<P extends Point<P>> implements Encloser<P> {
 
     /** Tolerance below which points are consider to be identical. */
     private final double tolerance;
 
     /** Generator for balls on support. */
-    private final SupportBallGenerator<S, P> generator;
+    private final SupportBallGenerator<P> generator;
 
     /** Simple constructor.
      * @param tolerance below which points are consider to be identical
      * @param generator generator for balls on support
      */
-    public WelzlEncloser(final double tolerance, final SupportBallGenerator<S, P> generator) {
+    public WelzlEncloser(final double tolerance, final SupportBallGenerator<P> generator) {
         this.tolerance = tolerance;
         this.generator = generator;
     }
 
     /** {@inheritDoc} */
     @Override
-    public EnclosingBall<S, P> enclose(final Iterable<P> points) {
+    public EnclosingBall<P> enclose(final Iterable<P> points) {
 
         if (points == null || !points.iterator().hasNext()) {
             // return an empty ball
@@ -73,15 +72,15 @@ public class WelzlEncloser<S extends Space, P extends Point<S>> implements Enclo
      * @param points points to be enclosed
      * @return enclosing ball
      */
-    private EnclosingBall<S, P> pivotingBall(final Iterable<P> points) {
+    private EnclosingBall<P> pivotingBall(final Iterable<P> points) {
 
         final P first = points.iterator().next();
-        final List<P> extreme = new ArrayList<>(first.getSpace().getDimension() + 1);
-        final List<P> support = new ArrayList<>(first.getSpace().getDimension() + 1);
+        final List<P> extreme = new ArrayList<>(first.getDimension() + 1);
+        final List<P> support = new ArrayList<>(first.getDimension() + 1);
 
         // start with only first point selected as a candidate support
         extreme.add(first);
-        EnclosingBall<S, P> ball = moveToFrontBall(extreme, extreme.size(), support);
+        EnclosingBall<P> ball = moveToFrontBall(extreme, extreme.size(), support);
 
         while (true) {
 
@@ -96,7 +95,7 @@ public class WelzlEncloser<S extends Space, P extends Point<S>> implements Enclo
             // recurse search, restricted to the small subset containing support and farthest point
             support.clear();
             support.add(farthest);
-            EnclosingBall<S, P> savedBall = ball;
+            EnclosingBall<P> savedBall = ball;
             ball = moveToFrontBall(extreme, extreme.size(), support);
             if (ball.getRadius() < savedBall.getRadius()) {
                 // this should never happen
@@ -120,13 +119,13 @@ public class WelzlEncloser<S extends Space, P extends Point<S>> implements Enclo
      * @param support points that must belong to the ball support
      * @return enclosing ball, for the extreme subset only
      */
-    private EnclosingBall<S, P> moveToFrontBall(final List<P> extreme, final int nbExtreme,
+    private EnclosingBall<P> moveToFrontBall(final List<P> extreme, final int nbExtreme,
                                                 final List<P> support) {
 
         // create a new ball on the prescribed support
-        EnclosingBall<S, P> ball = generator.ballOnSupport(support);
+        EnclosingBall<P> ball = generator.ballOnSupport(support);
 
-        if (ball.getSupportSize() <= ball.getCenter().getSpace().getDimension()) {
+        if (ball.getSupportSize() <= ball.getCenter().getDimension()) {
 
             for (int i = 0; i < nbExtreme; ++i) {
                 final P pi = extreme.get(i);
@@ -159,7 +158,7 @@ public class WelzlEncloser<S extends Space, P extends Point<S>> implements Enclo
      * @param ball current ball
      * @return farthest point
      */
-    public P selectFarthest(final Iterable<P> points, final EnclosingBall<S, P> ball) {
+    public P selectFarthest(final Iterable<P> points, final EnclosingBall<P> ball) {
 
         final P center = ball.getCenter();
         P farthest   = null;
diff --git a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/euclidean/threed/enclosing/SphereGenerator.java b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/euclidean/threed/enclosing/SphereGenerator.java
index 5bdc69c..a124cf3 100644
--- a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/euclidean/threed/enclosing/SphereGenerator.java
+++ b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/euclidean/threed/enclosing/SphereGenerator.java
@@ -21,42 +21,40 @@ import java.util.List;
 
 import org.apache.commons.geometry.enclosing.EnclosingBall;
 import org.apache.commons.geometry.enclosing.SupportBallGenerator;
-import org.apache.commons.geometry.euclidean.threed.Cartesian3D;
-import org.apache.commons.geometry.euclidean.threed.Euclidean3D;
 import org.apache.commons.geometry.euclidean.threed.Plane;
-import org.apache.commons.geometry.euclidean.twod.Cartesian2D;
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.geometry.euclidean.threed.Point3D;
+import org.apache.commons.geometry.euclidean.twod.Point2D;
 import org.apache.commons.geometry.euclidean.twod.enclosing.DiskGenerator;
 import org.apache.commons.numbers.fraction.BigFraction;
 
 /** Class generating an enclosing ball from its support points.
  */
-public class SphereGenerator implements SupportBallGenerator<Euclidean3D, Cartesian3D> {
+public class SphereGenerator implements SupportBallGenerator<Point3D> {
 
     /** {@inheritDoc} */
     @Override
-    public EnclosingBall<Euclidean3D, Cartesian3D> ballOnSupport(final List<Cartesian3D> support) {
+    public EnclosingBall<Point3D> ballOnSupport(final List<Point3D> support) {
 
         if (support.size() < 1) {
-            return new EnclosingBall<>(Cartesian3D.ZERO, Double.NEGATIVE_INFINITY);
+            return new EnclosingBall<>(Point3D.ZERO, Double.NEGATIVE_INFINITY);
         } else {
-            final Cartesian3D vA = support.get(0);
+            final Point3D vA = support.get(0);
             if (support.size() < 2) {
                 return new EnclosingBall<>(vA, 0, vA);
             } else {
-                final Cartesian3D vB = support.get(1);
+                final Point3D vB = support.get(1);
                 if (support.size() < 3) {
-                    return new EnclosingBall<>(new Cartesian3D(0.5, vA, 0.5, vB),
+                    return new EnclosingBall<>(Point3D.vectorCombination(0.5, vA, 0.5, vB),
                                                                     0.5 * vA.distance(vB),
                                                                     vA, vB);
                 } else {
-                    final Cartesian3D vC = support.get(2);
+                    final Point3D vC = support.get(2);
                     if (support.size() < 4) {
 
                         // delegate to 2D disk generator
                         final Plane p = new Plane(vA, vB, vC,
-                                                  1.0e-10 * (vA.getNorm1() + vB.getNorm1() + vC.getNorm1()));
-                        final EnclosingBall<Euclidean2D, Cartesian2D> disk =
+                                                  1.0e-10 * (vA.asVector().getNorm1() + vB.asVector().getNorm1() + vC.asVector().getNorm1()));
+                        final EnclosingBall<Point2D> disk =
                                 new DiskGenerator().ballOnSupport(Arrays.asList(p.toSubSpace(vA),
                                                                                 p.toSubSpace(vB),
                                                                                 p.toSubSpace(vC)));
@@ -66,7 +64,7 @@ public class SphereGenerator implements SupportBallGenerator<Euclidean3D, Cartes
                                                                         disk.getRadius(), vA, vB, vC);
 
                     } else {
-                        final Cartesian3D vD = support.get(3);
+                        final Point3D vD = support.get(3);
                         // a sphere is 3D can be defined as:
                         // (1)   (x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = r^2
                         // which can be written:
@@ -119,7 +117,7 @@ public class SphereGenerator implements SupportBallGenerator<Euclidean3D, Cartes
                         final BigFraction dy      = c3[0].subtract(centerY);
                         final BigFraction dz      = c4[0].subtract(centerZ);
                         final BigFraction r2      = dx.multiply(dx).add(dy.multiply(dy)).add(dz.multiply(dz));
-                        return new EnclosingBall<>(new Cartesian3D(centerX.doubleValue(),
+                        return new EnclosingBall<>(new Point3D(centerX.doubleValue(),
                                                                                      centerY.doubleValue(),
                                                                                      centerZ.doubleValue()),
                                                                         Math.sqrt(r2.doubleValue()),
diff --git a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/euclidean/twod/enclosing/DiskGenerator.java b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/euclidean/twod/enclosing/DiskGenerator.java
index 52b5626..c2b9acc 100644
--- a/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/euclidean/twod/enclosing/DiskGenerator.java
+++ b/commons-geometry-enclosing/src/main/java/org/apache/commons/geometry/euclidean/twod/enclosing/DiskGenerator.java
@@ -18,34 +18,33 @@ package org.apache.commons.geometry.euclidean.twod.enclosing;
 
 import java.util.List;
 
-import org.apache.commons.numbers.fraction.BigFraction;
 import org.apache.commons.geometry.enclosing.EnclosingBall;
 import org.apache.commons.geometry.enclosing.SupportBallGenerator;
-import org.apache.commons.geometry.euclidean.twod.Cartesian2D;
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.geometry.euclidean.twod.Point2D;
+import org.apache.commons.numbers.fraction.BigFraction;
 
 /** Class generating an enclosing ball from its support points.
  */
-public class DiskGenerator implements SupportBallGenerator<Euclidean2D, Cartesian2D> {
+public class DiskGenerator implements SupportBallGenerator<Point2D> {
 
     /** {@inheritDoc} */
     @Override
-    public EnclosingBall<Euclidean2D, Cartesian2D> ballOnSupport(final List<Cartesian2D> support) {
+    public EnclosingBall<Point2D> ballOnSupport(final List<Point2D> support) {
 
         if (support.size() < 1) {
-            return new EnclosingBall<>(Cartesian2D.ZERO, Double.NEGATIVE_INFINITY);
+            return new EnclosingBall<>(Point2D.ZERO, Double.NEGATIVE_INFINITY);
         } else {
-            final Cartesian2D vA = support.get(0);
+            final Point2D vA = support.get(0);
             if (support.size() < 2) {
                 return new EnclosingBall<>(vA, 0, vA);
             } else {
-                final Cartesian2D vB = support.get(1);
+                final Point2D vB = support.get(1);
                 if (support.size() < 3) {
-                    return new EnclosingBall<>(new Cartesian2D(0.5, vA, 0.5, vB),
+                    return new EnclosingBall<>(Point2D.vectorCombination(0.5, vA, 0.5, vB),
                                                                     0.5 * vA.distance(vB),
                                                                     vA, vB);
                 } else {
-                    final Cartesian2D vC = support.get(2);
+                    final Point2D vC = support.get(2);
                     // a disk is 2D can be defined as:
                     // (1)   (x - x_0)^2 + (y - y_0)^2 = r^2
                     // which can be written:
@@ -86,7 +85,7 @@ public class DiskGenerator implements SupportBallGenerator<Euclidean2D, Cartesia
                     final BigFraction dx      = c2[0].subtract(centerX);
                     final BigFraction dy      = c3[0].subtract(centerY);
                     final BigFraction r2      = dx.multiply(dx).add(dy.multiply(dy));
-                    return new EnclosingBall<>(new Cartesian2D(centerX.doubleValue(),
+                    return new EnclosingBall<>(new Point2D(centerX.doubleValue(),
                                                                                  centerY.doubleValue()),
                                                                     Math.sqrt(r2.doubleValue()),
                                                                     vA, vB, vC);
diff --git a/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/enclosing/WelzlEncloser2DTest.java b/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/enclosing/WelzlEncloser2DTest.java
index 21d9cd6..8a975e1 100644
--- a/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/enclosing/WelzlEncloser2DTest.java
+++ b/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/enclosing/WelzlEncloser2DTest.java
@@ -20,11 +20,8 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import org.apache.commons.geometry.enclosing.EnclosingBall;
-import org.apache.commons.geometry.enclosing.WelzlEncloser;
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.geometry.euclidean.twod.Point2D;
 import org.apache.commons.geometry.euclidean.twod.enclosing.DiskGenerator;
-import org.apache.commons.geometry.euclidean.twod.Cartesian2D;
 import org.apache.commons.rng.UniformRandomProvider;
 import org.apache.commons.rng.simple.RandomSource;
 import org.junit.Assert;
@@ -36,36 +33,36 @@ public class WelzlEncloser2DTest {
     @Test
     public void testNullList() {
         DiskGenerator generator = new DiskGenerator();
-        WelzlEncloser<Euclidean2D, Cartesian2D> encloser =
+        WelzlEncloser<Point2D> encloser =
                 new WelzlEncloser<>(1.0e-10, generator);
-        EnclosingBall<Euclidean2D, Cartesian2D> ball = encloser.enclose(null);
+        EnclosingBall<Point2D> ball = encloser.enclose(null);
         Assert.assertTrue(ball.getRadius() < 0);
     }
 
     @Test
     public void testNoPoints() {
         DiskGenerator generator = new DiskGenerator();
-        WelzlEncloser<Euclidean2D, Cartesian2D> encloser =
+        WelzlEncloser<Point2D> encloser =
                 new WelzlEncloser<>(1.0e-10, generator);
-        EnclosingBall<Euclidean2D, Cartesian2D> ball = encloser.enclose(new ArrayList<Cartesian2D>());
+        EnclosingBall<Point2D> ball = encloser.enclose(new ArrayList<Point2D>());
         Assert.assertTrue(ball.getRadius() < 0);
     }
 
     @Test
     public void testRegularPoints() {
-        List<Cartesian2D> list = buildList(22, 26, 30, 38, 64, 28,  8, 54, 11, 15);
+        List<Point2D> list = buildList(22, 26, 30, 38, 64, 28,  8, 54, 11, 15);
         checkDisk(list, Arrays.asList(list.get(2), list.get(3), list.get(4)));
     }
 
     @Test
     public void testSolutionOnDiameter() {
-        List<Cartesian2D> list = buildList(22, 26, 30, 38, 64, 28,  8, 54);
+        List<Point2D> list = buildList(22, 26, 30, 38, 64, 28,  8, 54);
         checkDisk(list, Arrays.asList(list.get(2), list.get(3)));
     }
 
     @Test
     public void testReducingBall1() {
-        List<Cartesian2D> list = buildList(0.05380958511396061, 0.57332359658700000,
+        List<Point2D> list = buildList(0.05380958511396061, 0.57332359658700000,
                                         0.99348810731127870, 0.02056421361521466,
                                         0.01203950647796437, 0.99779675042261860,
                                         0.00810189987706078, 0.00589246003827815,
@@ -75,7 +72,7 @@ public class WelzlEncloser2DTest {
 
     @Test
     public void testReducingBall2() {
-        List<Cartesian2D> list = buildList(0.016930586154703, 0.333955448537779,
+        List<Point2D> list = buildList(0.016930586154703, 0.333955448537779,
                                         0.987189104892331, 0.969778855274507,
                                         0.983696889599935, 0.012904580013266,
                                         0.013114499572905, 0.034740156356895);
@@ -87,39 +84,39 @@ public class WelzlEncloser2DTest {
         UniformRandomProvider random = RandomSource.create(RandomSource.WELL_1024_A, 0xa2a63cad12c01fb2l);
         for (int k = 0; k < 100; ++k) {
             int nbPoints = random.nextInt(10000);
-            List<Cartesian2D> points = new ArrayList<>();
+            List<Point2D> points = new ArrayList<>();
             for (int i = 0; i < nbPoints; ++i) {
                 double x = random.nextDouble();
                 double y = random.nextDouble();
-                points.add(new Cartesian2D(x, y));
+                points.add(new Point2D(x, y));
             }
             checkDisk(points);
         }
     }
 
-    private List<Cartesian2D> buildList(final double ... coordinates) {
-        List<Cartesian2D> list = new ArrayList<>(coordinates.length / 2);
+    private List<Point2D> buildList(final double ... coordinates) {
+        List<Point2D> list = new ArrayList<>(coordinates.length / 2);
         for (int i = 0; i < coordinates.length; i += 2) {
-            list.add(new Cartesian2D(coordinates[i], coordinates[i + 1]));
+            list.add(new Point2D(coordinates[i], coordinates[i + 1]));
         }
         return list;
     }
 
-    private void checkDisk(List<Cartesian2D> points, List<Cartesian2D> refSupport) {
+    private void checkDisk(List<Point2D> points, List<Point2D> refSupport) {
 
-        EnclosingBall<Euclidean2D, Cartesian2D> disk = checkDisk(points);
+        EnclosingBall<Point2D> disk = checkDisk(points);
 
         // compare computed disk with expected disk
         DiskGenerator generator = new DiskGenerator();
-        EnclosingBall<Euclidean2D, Cartesian2D> expected = generator.ballOnSupport(refSupport);
+        EnclosingBall<Point2D> expected = generator.ballOnSupport(refSupport);
         Assert.assertEquals(refSupport.size(), disk.getSupportSize());
         Assert.assertEquals(expected.getRadius(),        disk.getRadius(),        1.0e-10);
         Assert.assertEquals(expected.getCenter().getX(), disk.getCenter().getX(), 1.0e-10);
         Assert.assertEquals(expected.getCenter().getY(), disk.getCenter().getY(), 1.0e-10);
 
-        for (Cartesian2D s : disk.getSupport()) {
+        for (Point2D s : disk.getSupport()) {
             boolean found = false;
-            for (Cartesian2D rs : refSupport) {
+            for (Point2D rs : refSupport) {
                 if (s == rs) {
                     found = true;
                 }
@@ -129,14 +126,14 @@ public class WelzlEncloser2DTest {
 
         // check removing any point of the support disk fails to enclose the point
         for (int i = 0; i < disk.getSupportSize(); ++i) {
-            List<Cartesian2D> reducedSupport = new ArrayList<>();
+            List<Point2D> reducedSupport = new ArrayList<>();
             int count = 0;
-            for (Cartesian2D s : disk.getSupport()) {
+            for (Point2D s : disk.getSupport()) {
                 if (count++ != i) {
                     reducedSupport.add(s);
                 }
             }
-            EnclosingBall<Euclidean2D, Cartesian2D> reducedDisk = generator.ballOnSupport(reducedSupport);
+            EnclosingBall<Point2D> reducedDisk = generator.ballOnSupport(reducedSupport);
             boolean foundOutside = false;
             for (int j = 0; j < points.size() && !foundOutside; ++j) {
                 if (!reducedDisk.contains(points.get(j), 1.0e-10)) {
@@ -148,20 +145,20 @@ public class WelzlEncloser2DTest {
 
     }
 
-    private EnclosingBall<Euclidean2D, Cartesian2D> checkDisk(List<Cartesian2D> points) {
+    private EnclosingBall<Point2D> checkDisk(List<Point2D> points) {
 
-        WelzlEncloser<Euclidean2D, Cartesian2D> encloser =
+        WelzlEncloser<Point2D> encloser =
                 new WelzlEncloser<>(1.0e-10, new DiskGenerator());
-        EnclosingBall<Euclidean2D, Cartesian2D> disk = encloser.enclose(points);
+        EnclosingBall<Point2D> disk = encloser.enclose(points);
 
         // all points are enclosed
-        for (Cartesian2D v : points) {
+        for (Point2D v : points) {
             Assert.assertTrue(disk.contains(v, 1.0e-10));
         }
 
-        for (Cartesian2D v : points) {
+        for (Point2D v : points) {
             boolean inSupport = false;
-            for (Cartesian2D s : disk.getSupport()) {
+            for (Point2D s : disk.getSupport()) {
                 if (v == s) {
                     inSupport = true;
                 }
diff --git a/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/enclosing/WelzlEncloser3DTest.java b/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/enclosing/WelzlEncloser3DTest.java
index 0466b48..e874ca7 100644
--- a/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/enclosing/WelzlEncloser3DTest.java
+++ b/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/enclosing/WelzlEncloser3DTest.java
@@ -21,16 +21,13 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import org.junit.Assert;
-import org.junit.Test;
+import org.apache.commons.geometry.euclidean.threed.Point3D;
+import org.apache.commons.geometry.euclidean.threed.enclosing.SphereGenerator;
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.simple.RandomSource;
 import org.apache.commons.rng.sampling.UnitSphereSampler;
-import org.apache.commons.geometry.enclosing.EnclosingBall;
-import org.apache.commons.geometry.enclosing.WelzlEncloser;
-import org.apache.commons.geometry.euclidean.threed.Euclidean3D;
-import org.apache.commons.geometry.euclidean.threed.enclosing.SphereGenerator;
-import org.apache.commons.geometry.euclidean.threed.Cartesian3D;
+import org.apache.commons.rng.simple.RandomSource;
+import org.junit.Assert;
+import org.junit.Test;
 
 
 public class WelzlEncloser3DTest {
@@ -38,67 +35,67 @@ public class WelzlEncloser3DTest {
     @Test
     public void testNullList() {
         SphereGenerator generator = new SphereGenerator();
-        WelzlEncloser<Euclidean3D, Cartesian3D> encloser =
+        WelzlEncloser<Point3D> encloser =
                 new WelzlEncloser<>(1.0e-10, generator);
-        EnclosingBall<Euclidean3D, Cartesian3D> ball = encloser.enclose(null);
+        EnclosingBall<Point3D> ball = encloser.enclose(null);
         Assert.assertTrue(ball.getRadius() < 0);
     }
 
     @Test
     public void testNoPoints() {
         SphereGenerator generator = new SphereGenerator();
-        WelzlEncloser<Euclidean3D, Cartesian3D> encloser =
+        WelzlEncloser<Point3D> encloser =
                 new WelzlEncloser<>(1.0e-10, generator);
-        EnclosingBall<Euclidean3D, Cartesian3D> ball = encloser.enclose(new ArrayList<Cartesian3D>());
+        EnclosingBall<Point3D> ball = encloser.enclose(new ArrayList<Point3D>());
         Assert.assertTrue(ball.getRadius() < 0);
     }
 
     @Test
     public void testReducingBall() {
-        List<Cartesian3D> list =
-                Arrays.asList(new Cartesian3D(-7.140397329568118, -16.571661242582177,  11.714458961735405),
-                              new Cartesian3D(-7.137986707455888, -16.570767323375720,  11.708602108715928),
-                              new Cartesian3D(-7.139185068549035, -16.570891204702250,  11.715554057357394),
-                              new Cartesian3D(-7.142682716997507, -16.571609818234290,  11.710787934580328),
-                              new Cartesian3D(-7.139018392423351, -16.574405614157020,  11.710518716711425),
-                              new Cartesian3D(-7.140870659936730, -16.567993074240455,  11.710914678204503),
-                              new Cartesian3D(-7.136350173659562, -16.570498228820930,  11.713965225900928),
-                              new Cartesian3D(-7.141675762759172, -16.572852471407028,  11.714033471449508),
-                              new Cartesian3D(-7.140453077221105, -16.570212820780647,  11.708624578004980),
-                              new Cartesian3D(-7.140322188726825, -16.574152894557717,  11.710305611121410),
-                              new Cartesian3D(-7.141116131477088, -16.574061164624560,  11.712938509321699));
-        WelzlEncloser<Euclidean3D, Cartesian3D> encloser =
+        List<Point3D> list =
+                Arrays.asList(new Point3D(-7.140397329568118, -16.571661242582177,  11.714458961735405),
+                              new Point3D(-7.137986707455888, -16.570767323375720,  11.708602108715928),
+                              new Point3D(-7.139185068549035, -16.570891204702250,  11.715554057357394),
+                              new Point3D(-7.142682716997507, -16.571609818234290,  11.710787934580328),
+                              new Point3D(-7.139018392423351, -16.574405614157020,  11.710518716711425),
+                              new Point3D(-7.140870659936730, -16.567993074240455,  11.710914678204503),
+                              new Point3D(-7.136350173659562, -16.570498228820930,  11.713965225900928),
+                              new Point3D(-7.141675762759172, -16.572852471407028,  11.714033471449508),
+                              new Point3D(-7.140453077221105, -16.570212820780647,  11.708624578004980),
+                              new Point3D(-7.140322188726825, -16.574152894557717,  11.710305611121410),
+                              new Point3D(-7.141116131477088, -16.574061164624560,  11.712938509321699));
+        WelzlEncloser<Point3D> encloser =
                 new WelzlEncloser<>(1.0e-10, new SphereGenerator());
-        EnclosingBall<Euclidean3D, Cartesian3D> ball = encloser.enclose(list);
+        EnclosingBall<Point3D> ball = encloser.enclose(list);
         Assert.assertTrue(ball.getRadius() > 0);
     }
 
     @Test
     public void testInfiniteLoop() {
         // this test used to generate an infinite loop
-        List<Cartesian3D> list =
-                Arrays.asList(new Cartesian3D( -0.89227075512164380,  -2.89317694645713900,  14.84572323743355500),
-                              new Cartesian3D( -0.92099498940693580,  -2.31086108263908940,  12.92071026467688300),
-                              new Cartesian3D( -0.85227999411005200,  -3.06314731441320730,  15.40163831651287000),
-                              new Cartesian3D( -1.77399413020785970,  -3.65630391378114260,  14.13190097751873400),
-                              new Cartesian3D(  0.33157833272465354,  -2.22813591757792160,  14.21225234159008200),
-                              new Cartesian3D( -1.53065579165484400,  -1.65692084770139570,  14.61483055714788500),
-                              new Cartesian3D( -1.08457093941217140,  -1.96100325935602980,  13.09265170575555000),
-                              new Cartesian3D(  0.30029469589708850,  -3.05470831395667370,  14.56352400426342600),
-                              new Cartesian3D( -0.95007443938638460,  -1.86810946486118360,  15.14491234340057000),
-                              new Cartesian3D( -1.89661503804130830,  -2.17004080885185860,  14.81235128513927000),
-                              new Cartesian3D( -0.72193328761607530,  -1.44513142833618270,  14.52355724218561800),
-                              new Cartesian3D( -0.26895980939606550,  -3.69512371522084140,  14.72272846327652000),
-                              new Cartesian3D( -1.53501693431786170,  -3.25055166611021900,  15.15509062584274800),
-                              new Cartesian3D( -0.71727553535519410,  -3.62284279460799100,  13.26256700929380700),
-                              new Cartesian3D( -0.30220950676137365,  -3.25410412500779070,  13.13682612771606000),
-                              new Cartesian3D( -0.04543996608267075,  -1.93081853923797750,  14.79497997883171400),
-                              new Cartesian3D( -1.53348892951571640,  -3.66688919703524900,  14.73095600812074200),
-                              new Cartesian3D( -0.98034899533935820,  -3.34004481162763960,  13.03245014017556800));
-
-        WelzlEncloser<Euclidean3D, Cartesian3D> encloser =
+        List<Point3D> list =
+                Arrays.asList(new Point3D( -0.89227075512164380,  -2.89317694645713900,  14.84572323743355500),
+                              new Point3D( -0.92099498940693580,  -2.31086108263908940,  12.92071026467688300),
+                              new Point3D( -0.85227999411005200,  -3.06314731441320730,  15.40163831651287000),
+                              new Point3D( -1.77399413020785970,  -3.65630391378114260,  14.13190097751873400),
+                              new Point3D(  0.33157833272465354,  -2.22813591757792160,  14.21225234159008200),
+                              new Point3D( -1.53065579165484400,  -1.65692084770139570,  14.61483055714788500),
+                              new Point3D( -1.08457093941217140,  -1.96100325935602980,  13.09265170575555000),
+                              new Point3D(  0.30029469589708850,  -3.05470831395667370,  14.56352400426342600),
+                              new Point3D( -0.95007443938638460,  -1.86810946486118360,  15.14491234340057000),
+                              new Point3D( -1.89661503804130830,  -2.17004080885185860,  14.81235128513927000),
+                              new Point3D( -0.72193328761607530,  -1.44513142833618270,  14.52355724218561800),
+                              new Point3D( -0.26895980939606550,  -3.69512371522084140,  14.72272846327652000),
+                              new Point3D( -1.53501693431786170,  -3.25055166611021900,  15.15509062584274800),
+                              new Point3D( -0.71727553535519410,  -3.62284279460799100,  13.26256700929380700),
+                              new Point3D( -0.30220950676137365,  -3.25410412500779070,  13.13682612771606000),
+                              new Point3D( -0.04543996608267075,  -1.93081853923797750,  14.79497997883171400),
+                              new Point3D( -1.53348892951571640,  -3.66688919703524900,  14.73095600812074200),
+                              new Point3D( -0.98034899533935820,  -3.34004481162763960,  13.03245014017556800));
+
+        WelzlEncloser<Point3D> encloser =
                 new WelzlEncloser<>(1.0e-10, new SphereGenerator());
-        EnclosingBall<Euclidean3D, Cartesian3D> ball = encloser.enclose(list);
+        EnclosingBall<Point3D> ball = encloser.enclose(list);
         Assert.assertTrue(ball.getRadius() > 0);
     }
 
@@ -112,13 +109,13 @@ public class WelzlEncloser3DTest {
             // define the reference sphere we want to compute
             double d = 25 * random.nextDouble();
             double refRadius = 10 * random.nextDouble();
-            Cartesian3D refCenter = new Cartesian3D(d, new Cartesian3D(sr.nextVector()));
+            Point3D refCenter = Point3D.vectorCombination(d, Point3D.of(sr.nextVector()));
             // set up a large sample inside the reference sphere
             int nbPoints = random.nextInt(1000);
-            List<Cartesian3D> points = new ArrayList<>();
+            List<Point3D> points = new ArrayList<>();
             for (int i = 0; i < nbPoints; ++i) {
                 double r = refRadius * random.nextDouble();
-                points.add(new Cartesian3D(1.0, refCenter, r, new Cartesian3D(sr.nextVector())));
+                points.add(Point3D.vectorCombination(1.0, refCenter, r, Point3D.of(sr.nextVector())));
             }
 
             // test we find a sphere at most as large as the one used for random drawings
@@ -127,23 +124,23 @@ public class WelzlEncloser3DTest {
         }
     }
 
-    private void checkSphere(List<Cartesian3D> points, double refRadius) {
+    private void checkSphere(List<Point3D> points, double refRadius) {
 
-        EnclosingBall<Euclidean3D, Cartesian3D> sphere = checkSphere(points);
+        EnclosingBall<Point3D> sphere = checkSphere(points);
 
         // compare computed sphere with bounding sphere
         Assert.assertTrue(sphere.getRadius() <= refRadius);
 
         // check removing any point of the support Sphere fails to enclose the point
         for (int i = 0; i < sphere.getSupportSize(); ++i) {
-            List<Cartesian3D> reducedSupport = new ArrayList<>();
+            List<Point3D> reducedSupport = new ArrayList<>();
             int count = 0;
-            for (Cartesian3D s : sphere.getSupport()) {
+            for (Point3D s : sphere.getSupport()) {
                 if (count++ != i) {
                     reducedSupport.add(s);
                 }
             }
-            EnclosingBall<Euclidean3D, Cartesian3D> reducedSphere =
+            EnclosingBall<Point3D> reducedSphere =
                     new SphereGenerator().ballOnSupport(reducedSupport);
             boolean foundOutside = false;
             for (int j = 0; j < points.size() && !foundOutside; ++j) {
@@ -156,20 +153,20 @@ public class WelzlEncloser3DTest {
 
     }
 
-    private EnclosingBall<Euclidean3D, Cartesian3D> checkSphere(List<Cartesian3D> points) {
+    private EnclosingBall<Point3D> checkSphere(List<Point3D> points) {
 
-        WelzlEncloser<Euclidean3D, Cartesian3D> encloser =
+        WelzlEncloser<Point3D> encloser =
                 new WelzlEncloser<>(1.0e-10, new SphereGenerator());
-        EnclosingBall<Euclidean3D, Cartesian3D> Sphere = encloser.enclose(points);
+        EnclosingBall<Point3D> Sphere = encloser.enclose(points);
 
         // all points are enclosed
-        for (Cartesian3D v : points) {
+        for (Point3D v : points) {
             Assert.assertTrue(Sphere.contains(v, 1.0e-10));
         }
 
-        for (Cartesian3D v : points) {
+        for (Point3D v : points) {
             boolean inSupport = false;
-            for (Cartesian3D s : Sphere.getSupport()) {
+            for (Point3D s : Sphere.getSupport()) {
                 if (v == s) {
                     inSupport = true;
                 }
diff --git a/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/euclidean/threed/enclosing/SphereGeneratorTest.java b/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/euclidean/threed/enclosing/SphereGeneratorTest.java
index 1adbaa5..a1bf60b 100644
--- a/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/euclidean/threed/enclosing/SphereGeneratorTest.java
+++ b/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/euclidean/threed/enclosing/SphereGeneratorTest.java
@@ -21,8 +21,7 @@ import java.util.Arrays;
 import java.util.List;
 
 import org.apache.commons.geometry.enclosing.EnclosingBall;
-import org.apache.commons.geometry.euclidean.threed.Cartesian3D;
-import org.apache.commons.geometry.euclidean.threed.Euclidean3D;
+import org.apache.commons.geometry.euclidean.threed.Point3D;
 import org.apache.commons.rng.UniformRandomProvider;
 import org.apache.commons.rng.sampling.UnitSphereSampler;
 import org.apache.commons.rng.simple.RandomSource;
@@ -33,8 +32,8 @@ public class SphereGeneratorTest {
 
     @Test
     public void testSupport0Point() {
-        List<Cartesian3D> support = Arrays.asList(new Cartesian3D[0]);
-        EnclosingBall<Euclidean3D, Cartesian3D> sphere = new SphereGenerator().ballOnSupport(support);
+        List<Point3D> support = Arrays.asList(new Point3D[0]);
+        EnclosingBall<Point3D> sphere = new SphereGenerator().ballOnSupport(support);
         Assert.assertTrue(sphere.getRadius() < 0);
         Assert.assertEquals(0, sphere.getSupportSize());
         Assert.assertEquals(0, sphere.getSupport().length);
@@ -42,16 +41,16 @@ public class SphereGeneratorTest {
 
     @Test
     public void testSupport1Point() {
-        List<Cartesian3D> support = Arrays.asList(new Cartesian3D(1, 2, 3));
-        EnclosingBall<Euclidean3D, Cartesian3D> sphere = new SphereGenerator().ballOnSupport(support);
+        List<Point3D> support = Arrays.asList(new Point3D(1, 2, 3));
+        EnclosingBall<Point3D> sphere = new SphereGenerator().ballOnSupport(support);
         Assert.assertEquals(0.0, sphere.getRadius(), 1.0e-10);
         Assert.assertTrue(sphere.contains(support.get(0)));
         Assert.assertTrue(sphere.contains(support.get(0), 0.5));
-        Assert.assertFalse(sphere.contains(new Cartesian3D(support.get(0).getX() + 0.1,
+        Assert.assertFalse(sphere.contains(new Point3D(support.get(0).getX() + 0.1,
                                                         support.get(0).getY() + 0.1,
                                                         support.get(0).getZ() + 0.1),
                                            0.001));
-        Assert.assertTrue(sphere.contains(new Cartesian3D(support.get(0).getX() + 0.1,
+        Assert.assertTrue(sphere.contains(new Point3D(support.get(0).getX() + 0.1,
                                                        support.get(0).getY() + 0.1,
                                                        support.get(0).getZ() + 0.1),
                                           0.5));
@@ -62,73 +61,73 @@ public class SphereGeneratorTest {
 
     @Test
     public void testSupport2Points() {
-        List<Cartesian3D> support = Arrays.asList(new Cartesian3D(1, 0, 0),
-                                               new Cartesian3D(3, 0, 0));
-        EnclosingBall<Euclidean3D, Cartesian3D> sphere = new SphereGenerator().ballOnSupport(support);
+        List<Point3D> support = Arrays.asList(new Point3D(1, 0, 0),
+                                               new Point3D(3, 0, 0));
+        EnclosingBall<Point3D> sphere = new SphereGenerator().ballOnSupport(support);
         Assert.assertEquals(1.0, sphere.getRadius(), 1.0e-10);
         int i = 0;
-        for (Cartesian3D v : support) {
+        for (Point3D v : support) {
             Assert.assertTrue(sphere.contains(v));
             Assert.assertEquals(1.0, v.distance(sphere.getCenter()), 1.0e-10);
             Assert.assertTrue(v == sphere.getSupport()[i++]);
         }
-        Assert.assertTrue(sphere.contains(new Cartesian3D(2, 0.9, 0)));
-        Assert.assertFalse(sphere.contains(Cartesian3D.ZERO));
-        Assert.assertEquals(0.0, new Cartesian3D(2, 0, 0).distance(sphere.getCenter()), 1.0e-10);
+        Assert.assertTrue(sphere.contains(new Point3D(2, 0.9, 0)));
+        Assert.assertFalse(sphere.contains(Point3D.ZERO));
+        Assert.assertEquals(0.0, new Point3D(2, 0, 0).distance(sphere.getCenter()), 1.0e-10);
         Assert.assertEquals(2, sphere.getSupportSize());
     }
 
     @Test
     public void testSupport3Points() {
-        List<Cartesian3D> support = Arrays.asList(new Cartesian3D(1, 0, 0),
-                                               new Cartesian3D(3, 0, 0),
-                                               new Cartesian3D(2, 2, 0));
-        EnclosingBall<Euclidean3D, Cartesian3D> sphere = new SphereGenerator().ballOnSupport(support);
+        List<Point3D> support = Arrays.asList(new Point3D(1, 0, 0),
+                                               new Point3D(3, 0, 0),
+                                               new Point3D(2, 2, 0));
+        EnclosingBall<Point3D> sphere = new SphereGenerator().ballOnSupport(support);
         Assert.assertEquals(5.0 / 4.0, sphere.getRadius(), 1.0e-10);
         int i = 0;
-        for (Cartesian3D v : support) {
+        for (Point3D v : support) {
             Assert.assertTrue(sphere.contains(v));
             Assert.assertEquals(5.0 / 4.0, v.distance(sphere.getCenter()), 1.0e-10);
             Assert.assertTrue(v == sphere.getSupport()[i++]);
         }
-        Assert.assertTrue(sphere.contains(new Cartesian3D(2, 0.9, 0)));
-        Assert.assertFalse(sphere.contains(new Cartesian3D(0.9,  0, 0)));
-        Assert.assertFalse(sphere.contains(new Cartesian3D(3.1,  0, 0)));
-        Assert.assertTrue(sphere.contains(new Cartesian3D(2.0, -0.499, 0)));
-        Assert.assertFalse(sphere.contains(new Cartesian3D(2.0, -0.501, 0)));
-        Assert.assertTrue(sphere.contains(new Cartesian3D(2.0, 3.0 / 4.0, -1.249)));
-        Assert.assertFalse(sphere.contains(new Cartesian3D(2.0, 3.0 / 4.0, -1.251)));
-        Assert.assertEquals(0.0, new Cartesian3D(2.0, 3.0 / 4.0, 0).distance(sphere.getCenter()), 1.0e-10);
+        Assert.assertTrue(sphere.contains(new Point3D(2, 0.9, 0)));
+        Assert.assertFalse(sphere.contains(new Point3D(0.9,  0, 0)));
+        Assert.assertFalse(sphere.contains(new Point3D(3.1,  0, 0)));
+        Assert.assertTrue(sphere.contains(new Point3D(2.0, -0.499, 0)));
+        Assert.assertFalse(sphere.contains(new Point3D(2.0, -0.501, 0)));
+        Assert.assertTrue(sphere.contains(new Point3D(2.0, 3.0 / 4.0, -1.249)));
+        Assert.assertFalse(sphere.contains(new Point3D(2.0, 3.0 / 4.0, -1.251)));
+        Assert.assertEquals(0.0, new Point3D(2.0, 3.0 / 4.0, 0).distance(sphere.getCenter()), 1.0e-10);
         Assert.assertEquals(3, sphere.getSupportSize());
     }
 
     @Test
     public void testSupport4Points() {
-        List<Cartesian3D> support = Arrays.asList(new Cartesian3D(17, 14,  18),
-                                               new Cartesian3D(11, 14,  22),
-                                               new Cartesian3D( 2, 22,  17),
-                                               new Cartesian3D(22, 11, -10));
-        EnclosingBall<Euclidean3D, Cartesian3D> sphere = new SphereGenerator().ballOnSupport(support);
+        List<Point3D> support = Arrays.asList(new Point3D(17, 14,  18),
+                                               new Point3D(11, 14,  22),
+                                               new Point3D( 2, 22,  17),
+                                               new Point3D(22, 11, -10));
+        EnclosingBall<Point3D> sphere = new SphereGenerator().ballOnSupport(support);
         Assert.assertEquals(25.0, sphere.getRadius(), 1.0e-10);
         int i = 0;
-        for (Cartesian3D v : support) {
+        for (Point3D v : support) {
             Assert.assertTrue(sphere.contains(v));
             Assert.assertEquals(25.0, v.distance(sphere.getCenter()), 1.0e-10);
             Assert.assertTrue(v == sphere.getSupport()[i++]);
         }
-        Assert.assertTrue(sphere.contains (new Cartesian3D(-22.999, 2, 2)));
-        Assert.assertFalse(sphere.contains(new Cartesian3D(-23.001, 2, 2)));
-        Assert.assertTrue(sphere.contains (new Cartesian3D( 26.999, 2, 2)));
-        Assert.assertFalse(sphere.contains(new Cartesian3D( 27.001, 2, 2)));
-        Assert.assertTrue(sphere.contains (new Cartesian3D(2, -22.999, 2)));
-        Assert.assertFalse(sphere.contains(new Cartesian3D(2, -23.001, 2)));
-        Assert.assertTrue(sphere.contains (new Cartesian3D(2,  26.999, 2)));
-        Assert.assertFalse(sphere.contains(new Cartesian3D(2,  27.001, 2)));
-        Assert.assertTrue(sphere.contains (new Cartesian3D(2, 2, -22.999)));
-        Assert.assertFalse(sphere.contains(new Cartesian3D(2, 2, -23.001)));
-        Assert.assertTrue(sphere.contains (new Cartesian3D(2, 2,  26.999)));
-        Assert.assertFalse(sphere.contains(new Cartesian3D(2, 2,  27.001)));
-        Assert.assertEquals(0.0, new Cartesian3D(2.0, 2.0, 2.0).distance(sphere.getCenter()), 1.0e-10);
+        Assert.assertTrue(sphere.contains (new Point3D(-22.999, 2, 2)));
+        Assert.assertFalse(sphere.contains(new Point3D(-23.001, 2, 2)));
+        Assert.assertTrue(sphere.contains (new Point3D( 26.999, 2, 2)));
+        Assert.assertFalse(sphere.contains(new Point3D( 27.001, 2, 2)));
+        Assert.assertTrue(sphere.contains (new Point3D(2, -22.999, 2)));
+        Assert.assertFalse(sphere.contains(new Point3D(2, -23.001, 2)));
+        Assert.assertTrue(sphere.contains (new Point3D(2,  26.999, 2)));
+        Assert.assertFalse(sphere.contains(new Point3D(2,  27.001, 2)));
+        Assert.assertTrue(sphere.contains (new Point3D(2, 2, -22.999)));
+        Assert.assertFalse(sphere.contains(new Point3D(2, 2, -23.001)));
+        Assert.assertTrue(sphere.contains (new Point3D(2, 2,  26.999)));
+        Assert.assertFalse(sphere.contains(new Point3D(2, 2,  27.001)));
+        Assert.assertEquals(0.0, new Point3D(2.0, 2.0, 2.0).distance(sphere.getCenter()), 1.0e-10);
         Assert.assertEquals(4, sphere.getSupportSize());
     }
 
@@ -140,12 +139,12 @@ public class SphereGeneratorTest {
         for (int i = 0; i < 100; ++i) {
             double d = 25 * random.nextDouble();
             double refRadius = 10 * random.nextDouble();
-            Cartesian3D refCenter = new Cartesian3D(d, new Cartesian3D(sr.nextVector()));
-            List<Cartesian3D> support = new ArrayList<>();
+            Point3D refCenter = Point3D.vectorCombination(d, Point3D.of(sr.nextVector()));
+            List<Point3D> support = new ArrayList<>();
             for (int j = 0; j < 5; ++j) {
-                support.add(new Cartesian3D(1.0, refCenter, refRadius, new Cartesian3D(sr.nextVector())));
+                support.add(Point3D.vectorCombination(1.0, refCenter, refRadius, Point3D.of(sr.nextVector())));
             }
-            EnclosingBall<Euclidean3D, Cartesian3D> sphere = new SphereGenerator().ballOnSupport(support);
+            EnclosingBall<Point3D> sphere = new SphereGenerator().ballOnSupport(support);
             Assert.assertEquals(0.0, refCenter.distance(sphere.getCenter()), 4e-7 * refRadius);
             Assert.assertEquals(refRadius, sphere.getRadius(), 1e-7 * refRadius);
         }
@@ -153,20 +152,20 @@ public class SphereGeneratorTest {
 
     @Test
     public void testDegeneratedCase() {
-       final List<Cartesian3D> support =
-               Arrays.asList(new Cartesian3D(Math.scalb(-8039905610797991.0, -50),   //   -7.140870659936730
+       final List<Point3D> support =
+               Arrays.asList(new Point3D(Math.scalb(-8039905610797991.0, -50),   //   -7.140870659936730
                                           Math.scalb(-4663475464714142.0, -48),   //  -16.567993074240455
                                           Math.scalb( 6592658872616184.0, -49)),  //   11.710914678204503
-                             new Cartesian3D(Math.scalb(-8036658568968473.0, -50),   //   -7.137986707455888
+                             new Point3D(Math.scalb(-8036658568968473.0, -50),   //   -7.137986707455888
                                           Math.scalb(-4664256346424880.0, -48),   //  -16.570767323375720
                                           Math.scalb( 6591357011730307.0, -49)),  //  11.708602108715928)
-                             new Cartesian3D(Math.scalb(-8037820142977230.0, -50),   //   -7.139018392423351
+                             new Point3D(Math.scalb(-8037820142977230.0, -50),   //   -7.139018392423351
                                           Math.scalb(-4665280434237813.0, -48),   //  -16.574405614157020
                                           Math.scalb( 6592435966112099.0, -49)),  //   11.710518716711425
-                             new Cartesian3D(Math.scalb(-8038007803611611.0, -50),   //   -7.139185068549035
+                             new Point3D(Math.scalb(-8038007803611611.0, -50),   //   -7.139185068549035
                                           Math.scalb(-4664291215918380.0, -48),   //  -16.570891204702250
                                           Math.scalb( 6595270610894208.0, -49))); //   11.715554057357394
-        EnclosingBall<Euclidean3D, Cartesian3D> sphere = new SphereGenerator().ballOnSupport(support);
+        EnclosingBall<Point3D> sphere = new SphereGenerator().ballOnSupport(support);
 
         // the following values have been computed using Emacs calc with exact arithmetic from the
         // rational representation corresponding to the scalb calls (i.e. -8039905610797991/2^50, ...)
@@ -177,7 +176,7 @@ public class SphereGeneratorTest {
         Assert.assertEquals(-16.571096474251747245361467833760, sphere.getCenter().getY(), 1.0e-20);
         Assert.assertEquals( 11.711945804096960876521111630800, sphere.getCenter().getZ(), 1.0e-20);
 
-        for (Cartesian3D v : support) {
+        for (Point3D v : support) {
             Assert.assertTrue(sphere.contains(v, 1.0e-14));
         }
 
diff --git a/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/euclidean/twod/enclosing/DiskGeneratorTest.java b/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/euclidean/twod/enclosing/DiskGeneratorTest.java
index 4fbe657..fcafaa4 100644
--- a/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/euclidean/twod/enclosing/DiskGeneratorTest.java
+++ b/commons-geometry-enclosing/src/test/java/org/apache/commons/geometry/euclidean/twod/enclosing/DiskGeneratorTest.java
@@ -19,23 +19,22 @@ package org.apache.commons.geometry.euclidean.twod.enclosing;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import org.junit.Assert;
-import org.junit.Test;
+
+import org.apache.commons.geometry.enclosing.EnclosingBall;
+import org.apache.commons.geometry.euclidean.twod.Point2D;
 import org.apache.commons.rng.UniformRandomProvider;
-import org.apache.commons.rng.simple.RandomSource;
 import org.apache.commons.rng.sampling.UnitSphereSampler;
-import org.apache.commons.geometry.enclosing.EnclosingBall;
-import org.apache.commons.geometry.euclidean.twod.enclosing.DiskGenerator;
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
-import org.apache.commons.geometry.euclidean.twod.Cartesian2D;
+import org.apache.commons.rng.simple.RandomSource;
+import org.junit.Assert;
+import org.junit.Test;
 
 
 public class DiskGeneratorTest {
 
     @Test
     public void testSupport0Point() {
-        List<Cartesian2D> support = Arrays.asList(new Cartesian2D[0]);
-        EnclosingBall<Euclidean2D, Cartesian2D> disk = new DiskGenerator().ballOnSupport(support);
+        List<Point2D> support = Arrays.asList(new Point2D[0]);
+        EnclosingBall<Point2D> disk = new DiskGenerator().ballOnSupport(support);
         Assert.assertTrue(disk.getRadius() < 0);
         Assert.assertEquals(0, disk.getSupportSize());
         Assert.assertEquals(0, disk.getSupport().length);
@@ -43,15 +42,15 @@ public class DiskGeneratorTest {
 
     @Test
     public void testSupport1Point() {
-        List<Cartesian2D> support = Arrays.asList(new Cartesian2D(1, 2));
-        EnclosingBall<Euclidean2D, Cartesian2D> disk = new DiskGenerator().ballOnSupport(support);
+        List<Point2D> support = Arrays.asList(new Point2D(1, 2));
+        EnclosingBall<Point2D> disk = new DiskGenerator().ballOnSupport(support);
         Assert.assertEquals(0.0, disk.getRadius(), 1.0e-10);
         Assert.assertTrue(disk.contains(support.get(0)));
         Assert.assertTrue(disk.contains(support.get(0), 0.5));
-        Assert.assertFalse(disk.contains(new Cartesian2D(support.get(0).getX() + 0.1,
+        Assert.assertFalse(disk.contains(new Point2D(support.get(0).getX() + 0.1,
                                                       support.get(0).getY() - 0.1),
                                          0.001));
-        Assert.assertTrue(disk.contains(new Cartesian2D(support.get(0).getX() + 0.1,
+        Assert.assertTrue(disk.contains(new Point2D(support.get(0).getX() + 0.1,
                                                      support.get(0).getY() - 0.1),
                                         0.5));
         Assert.assertEquals(0, support.get(0).distance(disk.getCenter()), 1.0e-10);
@@ -61,41 +60,41 @@ public class DiskGeneratorTest {
 
     @Test
     public void testSupport2Points() {
-        List<Cartesian2D> support = Arrays.asList(new Cartesian2D(1, 0),
-                                               new Cartesian2D(3, 0));
-        EnclosingBall<Euclidean2D, Cartesian2D> disk = new DiskGenerator().ballOnSupport(support);
+        List<Point2D> support = Arrays.asList(new Point2D(1, 0),
+                                               new Point2D(3, 0));
+        EnclosingBall<Point2D> disk = new DiskGenerator().ballOnSupport(support);
         Assert.assertEquals(1.0, disk.getRadius(), 1.0e-10);
         int i = 0;
-        for (Cartesian2D v : support) {
+        for (Point2D v : support) {
             Assert.assertTrue(disk.contains(v));
             Assert.assertEquals(1.0, v.distance(disk.getCenter()), 1.0e-10);
             Assert.assertTrue(v == disk.getSupport()[i++]);
         }
-        Assert.assertTrue(disk.contains(new Cartesian2D(2, 0.9)));
-        Assert.assertFalse(disk.contains(Cartesian2D.ZERO));
-        Assert.assertEquals(0.0, new Cartesian2D(2, 0).distance(disk.getCenter()), 1.0e-10);
+        Assert.assertTrue(disk.contains(new Point2D(2, 0.9)));
+        Assert.assertFalse(disk.contains(Point2D.ZERO));
+        Assert.assertEquals(0.0, new Point2D(2, 0).distance(disk.getCenter()), 1.0e-10);
         Assert.assertEquals(2, disk.getSupportSize());
     }
 
     @Test
     public void testSupport3Points() {
-        List<Cartesian2D> support = Arrays.asList(new Cartesian2D(1, 0),
-                                               new Cartesian2D(3, 0),
-                                               new Cartesian2D(2, 2));
-        EnclosingBall<Euclidean2D, Cartesian2D> disk = new DiskGenerator().ballOnSupport(support);
+        List<Point2D> support = Arrays.asList(new Point2D(1, 0),
+                                               new Point2D(3, 0),
+                                               new Point2D(2, 2));
+        EnclosingBall<Point2D> disk = new DiskGenerator().ballOnSupport(support);
         Assert.assertEquals(5.0 / 4.0, disk.getRadius(), 1.0e-10);
         int i = 0;
-        for (Cartesian2D v : support) {
+        for (Point2D v : support) {
             Assert.assertTrue(disk.contains(v));
             Assert.assertEquals(5.0 / 4.0, v.distance(disk.getCenter()), 1.0e-10);
             Assert.assertTrue(v == disk.getSupport()[i++]);
         }
-        Assert.assertTrue(disk.contains(new Cartesian2D(2, 0.9)));
-        Assert.assertFalse(disk.contains(new Cartesian2D(0.9,  0)));
-        Assert.assertFalse(disk.contains(new Cartesian2D(3.1,  0)));
-        Assert.assertTrue(disk.contains(new Cartesian2D(2.0, -0.499)));
-        Assert.assertFalse(disk.contains(new Cartesian2D(2.0, -0.501)));
-        Assert.assertEquals(0.0, new Cartesian2D(2.0, 3.0 / 4.0).distance(disk.getCenter()), 1.0e-10);
+        Assert.assertTrue(disk.contains(new Point2D(2, 0.9)));
+        Assert.assertFalse(disk.contains(new Point2D(0.9,  0)));
+        Assert.assertFalse(disk.contains(new Point2D(3.1,  0)));
+        Assert.assertTrue(disk.contains(new Point2D(2.0, -0.499)));
+        Assert.assertFalse(disk.contains(new Point2D(2.0, -0.501)));
+        Assert.assertEquals(0.0, new Point2D(2.0, 3.0 / 4.0).distance(disk.getCenter()), 1.0e-10);
         Assert.assertEquals(3, disk.getSupportSize());
     }
 
@@ -107,12 +106,12 @@ public class DiskGeneratorTest {
         for (int i = 0; i < 500; ++i) {
             double d = 25 * random.nextDouble();
             double refRadius = 10 * random.nextDouble();
-            Cartesian2D refCenter = new Cartesian2D(d, new Cartesian2D(sr.nextVector()));
-            List<Cartesian2D> support = new ArrayList<>();
+            Point2D refCenter = Point2D.vectorCombination(d, Point2D.of(sr.nextVector()));
+            List<Point2D> support = new ArrayList<>();
             for (int j = 0; j < 3; ++j) {
-                support.add(new Cartesian2D(1.0, refCenter, refRadius, new Cartesian2D(sr.nextVector())));
+                support.add(Point2D.vectorCombination(1.0, refCenter, refRadius, Point2D.of(sr.nextVector())));
             }
-            EnclosingBall<Euclidean2D, Cartesian2D> disk = new DiskGenerator().ballOnSupport(support);
+            EnclosingBall<Point2D> disk = new DiskGenerator().ballOnSupport(support);
             Assert.assertEquals(0.0, refCenter.distance(disk.getCenter()), 3e-9 * refRadius);
             Assert.assertEquals(refRadius, disk.getRadius(), 7e-10 * refRadius);
         }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanPoint.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanPoint.java
new file mode 100644
index 0000000..033b6bd
--- /dev/null
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanPoint.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import org.apache.commons.geometry.core.AffinePoint;
+
+/** Represents a point in a Euclidean space of any dimension.
+ *
+ * @param <P> Point implementation type
+ * @param <V> Vector implementation type
+ */
+public interface EuclideanPoint<P extends EuclideanPoint<P, V>, V extends EuclideanVector<P, V>> extends AffinePoint<P, V> {
+
+    /** Returns a vector with the same coordinates as this point.
+     * This is equivalent to the expression {@code v = P - Z} where
+     * {@code P} is this point, {@code Z} is the zero point. and
+     * {@code v} is the returned vector.
+     * @return vector with same coordinate values as this point
+     */
+    V asVector();
+
+    /** Returns the vector representing the displacement from this point
+     * to the given point. This is exactly equivalent to {@code p.subtract(thisPoint)}
+     * but with a method name that is much easier to visualize.
+     * @param p the point the returned vector will be directed toward
+     * @return vector representing the displacement <em>from</em> this point <em>to</em> the given point
+     */
+    V vectorTo(P p);
+}
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/EuclideanVector.java
similarity index 57%
copy from commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
copy to commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java
index 42fd4b7..0622c89 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/EuclideanVector.java
@@ -14,24 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.geometry.euclidean.twod;
+package org.apache.commons.geometry.euclidean;
 
 import org.apache.commons.geometry.core.Vector;
 
-/** This class represents a 2D vector.
+/** Represents a vector in a Euclidean space of any dimension.
+ *
+ * @param <P> Point implementation type
+ * @param <V> Vector implementation type
  */
-public abstract class Vector2D implements Vector<Euclidean2D> {
-
-    /** Get the abscissa of the vector.
-     * @return abscissa of the vector
-     * @see Cartesian2D#Cartesian2D(double, double)
-     */
-    public abstract double getX();
+public interface EuclideanVector<P extends EuclideanPoint<P, V>, V extends EuclideanVector<P, V>> extends Vector<V> {
 
-    /** Get the ordinate of the vector.
-     * @return ordinate of the vector
-     * @see Cartesian2D#Cartesian2D(double, double)
+    /** Returns a point with the same coordinates as this vector.
+     * This is equivalent to the expression {@code P = Z + v}, where
+     * {@code v} is this vector, {@code Z} is the zero point, and
+     * {@code P} is the returned point.
+     * @return point with the same coordinates as this vector
      */
-    public abstract double getY();
-
+    P asPoint();
 }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Cartesian1D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Cartesian1D.java
index 4517616..7cfbf9d 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Cartesian1D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Cartesian1D.java
@@ -16,201 +16,39 @@
  */
 package org.apache.commons.geometry.euclidean.oned;
 
-import java.text.NumberFormat;
+import org.apache.commons.geometry.core.Spatial;
 
-import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
-import org.apache.commons.geometry.core.Vector;
-
-/** This class represents a 1D point or a 1D vector.
- * <p>An instance of Cartesian1D represents the point with the corresponding
- * Cartesian coordinates.</p>
- * <p>An instance of Cartesian1D also represents the vector which begins at
- * the origin and ends at the point corresponding to the coordinates.</p>
- * <p>Instances of this class are guaranteed to be immutable.</p>
+/** This class represents a Cartesian coordinate value in
+ * one-dimensional Euclidean space.
  */
-public class Cartesian1D extends Vector1D implements Point<Euclidean1D> {
-
-    /** Origin (coordinates: 0). */
-    public static final Cartesian1D ZERO = new Cartesian1D(0.0);
-
-    /** Unit (coordinates: 1). */
-    public static final Cartesian1D ONE  = new Cartesian1D(1.0);
-
-    // CHECKSTYLE: stop ConstantName
-    /** A vector with all coordinates set to NaN. */
-    public static final Cartesian1D NaN = new Cartesian1D(Double.NaN);
-    // CHECKSTYLE: resume ConstantName
-
-    /** A vector with all coordinates set to positive infinity. */
-    public static final Cartesian1D POSITIVE_INFINITY =
-        new Cartesian1D(Double.POSITIVE_INFINITY);
-
-    /** A vector with all coordinates set to negative infinity. */
-    public static final Cartesian1D NEGATIVE_INFINITY =
-        new Cartesian1D(Double.NEGATIVE_INFINITY);
+public abstract class Cartesian1D implements Spatial {
 
     /** Serializable UID. */
-    private static final long serialVersionUID = 7556674948671647925L;
+    private static final long serialVersionUID = -1178039568877797126L;
 
-    /** Abscissa. */
-    private final double x;
+    /** Abscissa (coordinate value). */
+    protected final double x;
 
-    /** Simple constructor.
-     * Build a vector from its coordinates
-     * @param x abscissa
-     * @see #getX()
+    /**
+     * Simple constructor.
+     * @param x abscissa (coordinate value)
      */
-    public Cartesian1D(double x) {
+    protected Cartesian1D(double x) {
         this.x = x;
     }
 
-    /** Multiplicative constructor
-     * Build a vector from another one and a scale factor.
-     * The vector built will be a * u
-     * @param a scale factor
-     * @param u base (unscaled) vector
-     */
-    public Cartesian1D(double a, Cartesian1D u) {
-        this.x = a * u.x;
-    }
-
-    /** Linear constructor
-     * Build a vector from two other ones and corresponding scale factors.
-     * The vector built will be a1 * u1 + a2 * u2
-     * @param a1 first scale factor
-     * @param u1 first base (unscaled) vector
-     * @param a2 second scale factor
-     * @param u2 second base (unscaled) vector
-     */
-    public Cartesian1D(double a1, Cartesian1D u1, double a2, Cartesian1D u2) {
-        this.x = a1 * u1.x + a2 * u2.x;
-    }
-
-    /** Linear constructor
-     * Build a vector from three other ones and corresponding scale factors.
-     * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
-     * @param a1 first scale factor
-     * @param u1 first base (unscaled) vector
-     * @param a2 second scale factor
-     * @param u2 second base (unscaled) vector
-     * @param a3 third scale factor
-     * @param u3 third base (unscaled) vector
-     */
-    public Cartesian1D(double a1, Cartesian1D u1, double a2, Cartesian1D u2,
-                   double a3, Cartesian1D u3) {
-        this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x;
-    }
-
-    /** Linear constructor
-     * Build a vector from four other ones and corresponding scale factors.
-     * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
-     * @param a1 first scale factor
-     * @param u1 first base (unscaled) vector
-     * @param a2 second scale factor
-     * @param u2 second base (unscaled) vector
-     * @param a3 third scale factor
-     * @param u3 third base (unscaled) vector
-     * @param a4 fourth scale factor
-     * @param u4 fourth base (unscaled) vector
-     */
-    public Cartesian1D(double a1, Cartesian1D u1, double a2, Cartesian1D u2,
-                   double a3, Cartesian1D u3, double a4, Cartesian1D u4) {
-        this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x;
-    }
-
-    /** Get the abscissa of the vector.
-     * @return abscissa of the vector
-     * @see #Cartesian1D(double)
+    /**
+     * Returns the abscissa (coordinate value) of the instance.
+     * @return the abscissa value
      */
-    @Override
     public double getX() {
         return x;
     }
 
     /** {@inheritDoc} */
     @Override
-    public Space getSpace() {
-        return Euclidean1D.getInstance();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian1D getZero() {
-        return ZERO;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNorm1() {
-        return Math.abs(x);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNorm() {
-        return Math.abs(x);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNormSq() {
-        return x * x;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNormInf() {
-        return Math.abs(x);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian1D add(Vector<Euclidean1D> v) {
-        Cartesian1D v1 = (Cartesian1D) v;
-        return new Cartesian1D(x + v1.getX());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian1D add(double factor, Vector<Euclidean1D> v) {
-        Cartesian1D v1 = (Cartesian1D) v;
-        return new Cartesian1D(x + factor * v1.getX());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian1D subtract(Vector<Euclidean1D> p) {
-        Cartesian1D p3 = (Cartesian1D) p;
-        return new Cartesian1D(x - p3.x);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian1D subtract(double factor, Vector<Euclidean1D> v) {
-        Cartesian1D v1 = (Cartesian1D) v;
-        return new Cartesian1D(x - factor * v1.getX());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian1D normalize() throws IllegalStateException {
-        double s = getNorm();
-        if (s == 0) {
-            throw new IllegalStateException("Norm is zero");
-        }
-        return scalarMultiply(1 / s);
-    }
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian1D negate() {
-        return new Cartesian1D(-x);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian1D scalarMultiply(double a) {
-        return new Cartesian1D(a * x);
+    public int getDimension() {
+        return 1;
     }
 
     /** {@inheritDoc} */
@@ -224,159 +62,4 @@ public class Cartesian1D extends Vector1D implements Point<Euclidean1D> {
     public boolean isInfinite() {
         return !isNaN() && Double.isInfinite(x);
     }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distance1(Vector<Euclidean1D> p) {
-        Cartesian1D p1 = (Cartesian1D) p;
-        final double dx = Math.abs(p1.x - x);
-        return dx;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distance(Point<Euclidean1D> p) {
-        return distance((Cartesian1D) p);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distance(Vector<Euclidean1D> v) {
-        return distance((Cartesian1D) v);
-    }
-
-    /** Compute the distance between the instance and other coordinates.
-     * @param c other coordinates
-     * @return the distance between the instance and c
-     */
-    public double distance(Cartesian1D c) {
-        final double dx = c.x - x;
-        return Math.abs(dx);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distanceInf(Vector<Euclidean1D> p) {
-        Cartesian1D p1 = (Cartesian1D) p;
-        final double dx = Math.abs(p1.x - x);
-        return dx;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distanceSq(Vector<Euclidean1D> p) {
-        Cartesian1D p1 = (Cartesian1D) p;
-        final double dx = p1.x - x;
-        return dx * dx;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double dotProduct(final Vector<Euclidean1D> v) {
-        final Cartesian1D v1 = (Cartesian1D) v;
-        return x * v1.x;
-    }
-
-    /** Compute the distance between two points according to the L<sub>2</sub> norm.
-     * <p>Calling this method is equivalent to calling:
-     * <code>p1.subtract(p2).getNorm()</code> except that no intermediate
-     * vector is built</p>
-     * @param p1 first vector
-     * @param p2 second vector
-     * @return the distance between p1 and p2 according to the L<sub>2</sub> norm
-     */
-    public static double distance(Cartesian1D p1, Cartesian1D p2) {
-        return p1.distance(p2);
-    }
-
-    /** Compute the distance between two points according to the L<sub>&infin;</sub> norm.
-     * <p>Calling this method is equivalent to calling:
-     * <code>p1.subtract(p2).getNormInf()</code> except that no intermediate
-     * vector is built</p>
-     * @param p1 first vector
-     * @param p2 second vector
-     * @return the distance between p1 and p2 according to the L<sub>&infin;</sub> norm
-     */
-    public static double distanceInf(Cartesian1D p1, Cartesian1D p2) {
-        return p1.distanceInf(p2);
-    }
-
-    /** Compute the square of the distance between two points.
-     * <p>Calling this method is equivalent to calling:
-     * <code>p1.subtract(p2).getNormSq()</code> except that no intermediate
-     * vector is built</p>
-     * @param p1 first vector
-     * @param p2 second vector
-     * @return the square of the distance between p1 and p2
-     */
-    public static double distanceSq(Cartesian1D p1, Cartesian1D p2) {
-        return p1.distanceSq(p2);
-    }
-
-    /**
-     * Test for the equality of two 1D vectors.
-     * <p>
-     * If all coordinates of two 1D vectors are exactly the same, and none are
-     * <code>Double.NaN</code>, the two 1D vectors are considered to be equal.
-     * </p>
-     * <p>
-     * <code>NaN</code> coordinates are considered to affect globally the vector
-     * and be equals to each other - i.e, if either (or all) coordinates of the
-     * 1D vector are equal to <code>Double.NaN</code>, the 1D vector is equal to
-     * {@link #NaN}.
-     * </p>
-     *
-     * @param other Object to test for equality to this
-     * @return true if two 1D vector objects are equal, false if
-     *         object is null, not an instance of Cartesian1D, or
-     *         not equal to this Cartesian1D instance
-     *
-     */
-    @Override
-    public boolean equals(Object other) {
-
-        if (this == other) {
-            return true;
-        }
-
-        if (other instanceof Cartesian1D) {
-            final Cartesian1D rhs = (Cartesian1D)other;
-            if (rhs.isNaN()) {
-                return this.isNaN();
-            }
-
-            return x == rhs.x;
-        }
-        return false;
-    }
-
-    /**
-     * Get a hashCode for the 1D vector.
-     * <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 7785;
-        }
-        return 997 * Double.hashCode(x);
-    }
-
-    /** Get a string representation of this vector.
-     * @return a string representation of this vector
-     */
-    @Override
-    public String toString() {
-        return toString(NumberFormat.getInstance());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public String toString(final NumberFormat format) {
-        return "{" + format.format(x) + "}";
-    }
-
 }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Euclidean1D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Euclidean1D.java
deleted file mode 100644
index 394e317..0000000
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Euclidean1D.java
+++ /dev/null
@@ -1,80 +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.oned;
-
-import java.io.Serializable;
-
-import org.apache.commons.geometry.core.Space;
-
-/**
- * This class implements a one-dimensional space.
- */
-public class Euclidean1D implements Serializable, Space {
-
-    /** Serializable version identifier. */
-    private static final long serialVersionUID = -1178039568877797126L;
-
-    /** Private constructor for the singleton.
-     */
-    private Euclidean1D() {
-    }
-
-    /** Get the unique instance.
-     * @return the unique instance
-     */
-    public static Euclidean1D getInstance() {
-        return LazyHolder.INSTANCE;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public int getDimension() {
-        return 1;
-    }
-
-    /** {@inheritDoc}
-     * <p>
-     * As the 1-dimension Euclidean space does not have proper sub-spaces,
-     * this method always throws a {@link UnsupportedOperationException}
-     * </p>
-     * @return nothing
-     * @throws UnsupportedOperationException in all cases
-     */
-    @Override
-    public Space getSubSpace() throws UnsupportedOperationException {
-        throw new UnsupportedOperationException("One-dimensional space does not have a subspace");
-    }
-
-    // CHECKSTYLE: stop HideUtilityClassConstructor
-    /** Holder for the instance.
-     * <p>We use here the Initialization On Demand Holder Idiom.</p>
-     */
-    private static class LazyHolder {
-        /** Cached field instance. */
-        private static final Euclidean1D INSTANCE = new Euclidean1D();
-    }
-    // CHECKSTYLE: resume HideUtilityClassConstructor
-
-    /** Handle deserialization of the singleton.
-     * @return the singleton instance
-     */
-    private Object readResolve() {
-        // return the singleton instance
-        return LazyHolder.INSTANCE;
-    }
-}
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/IntervalsSet.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/IntervalsSet.java
index 3bdcd17..05e7798 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/IntervalsSet.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/IntervalsSet.java
@@ -22,7 +22,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
 
-import org.apache.commons.geometry.core.Point;
 import org.apache.commons.geometry.core.partitioning.AbstractRegion;
 import org.apache.commons.geometry.core.partitioning.BSPTree;
 import org.apache.commons.geometry.core.partitioning.BoundaryProjection;
@@ -30,7 +29,7 @@ import org.apache.commons.geometry.core.partitioning.SubHyperplane;
 
 /** This class represents a 1D region: a set of intervals.
  */
-public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> implements Iterable<double[]> {
+public class IntervalsSet extends AbstractRegion<Point1D, Point1D> implements Iterable<double[]> {
 
     /** Build an intervals set representing the whole real line.
      * @param tolerance tolerance below which points are considered identical.
@@ -60,7 +59,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param tree inside/outside BSP tree representing the intervals set
      * @param tolerance tolerance below which points are considered identical.
      */
-    public IntervalsSet(final BSPTree<Euclidean1D> tree, final double tolerance) {
+    public IntervalsSet(final BSPTree<Point1D> tree, final double tolerance) {
         super(tree, tolerance);
     }
 
@@ -84,7 +83,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param boundary collection of boundary elements
      * @param tolerance tolerance below which points are considered identical.
      */
-    public IntervalsSet(final Collection<SubHyperplane<Euclidean1D>> boundary,
+    public IntervalsSet(final Collection<SubHyperplane<Point1D>> boundary,
                         final double tolerance) {
         super(boundary, tolerance);
     }
@@ -97,7 +96,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param tolerance tolerance below which points are considered identical.
      * @return the built tree
      */
-    private static BSPTree<Euclidean1D> buildTree(final double lower, final double upper,
+    private static BSPTree<Point1D> buildTree(final double lower, final double upper,
                                                   final double tolerance) {
         if (Double.isInfinite(lower) && (lower < 0)) {
             if (Double.isInfinite(upper) && (upper > 0)) {
@@ -105,31 +104,31 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
                 return new BSPTree<>(Boolean.TRUE);
             }
             // the tree must be open on the negative infinity side
-            final SubHyperplane<Euclidean1D> upperCut =
-                new OrientedPoint(new Cartesian1D(upper), true, tolerance).wholeHyperplane();
+            final SubHyperplane<Point1D> upperCut =
+                new OrientedPoint(new Point1D(upper), true, tolerance).wholeHyperplane();
             return new BSPTree<>(upperCut,
-                               new BSPTree<Euclidean1D>(Boolean.FALSE),
-                               new BSPTree<Euclidean1D>(Boolean.TRUE),
+                               new BSPTree<Point1D>(Boolean.FALSE),
+                               new BSPTree<Point1D>(Boolean.TRUE),
                                null);
         }
-        final SubHyperplane<Euclidean1D> lowerCut =
-            new OrientedPoint(new Cartesian1D(lower), false, tolerance).wholeHyperplane();
+        final SubHyperplane<Point1D> lowerCut =
+            new OrientedPoint(new Point1D(lower), false, tolerance).wholeHyperplane();
         if (Double.isInfinite(upper) && (upper > 0)) {
             // the tree must be open on the positive infinity side
             return new BSPTree<>(lowerCut,
-                                            new BSPTree<Euclidean1D>(Boolean.FALSE),
-                                            new BSPTree<Euclidean1D>(Boolean.TRUE),
+                                            new BSPTree<Point1D>(Boolean.FALSE),
+                                            new BSPTree<Point1D>(Boolean.TRUE),
                                             null);
         }
 
         // the tree must be bounded on the two sides
-        final SubHyperplane<Euclidean1D> upperCut =
-            new OrientedPoint(new Cartesian1D(upper), true, tolerance).wholeHyperplane();
+        final SubHyperplane<Point1D> upperCut =
+            new OrientedPoint(new Point1D(upper), true, tolerance).wholeHyperplane();
         return new BSPTree<>(lowerCut,
-                                        new BSPTree<Euclidean1D>(Boolean.FALSE),
+                                        new BSPTree<Point1D>(Boolean.FALSE),
                                         new BSPTree<>(upperCut,
-                                                                 new BSPTree<Euclidean1D>(Boolean.FALSE),
-                                                                 new BSPTree<Euclidean1D>(Boolean.TRUE),
+                                                                 new BSPTree<Point1D>(Boolean.FALSE),
+                                                                 new BSPTree<Point1D>(Boolean.TRUE),
                                                                  null),
                                         null);
 
@@ -137,7 +136,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
 
     /** {@inheritDoc} */
     @Override
-    public IntervalsSet buildNew(final BSPTree<Euclidean1D> tree) {
+    public IntervalsSet buildNew(final BSPTree<Point1D> tree) {
         return new IntervalsSet(tree, getTolerance());
     }
 
@@ -145,7 +144,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
     @Override
     protected void computeGeometricalProperties() {
         if (getTree(false).getCut() == null) {
-            setBarycenter((Point<Euclidean1D>) Cartesian1D.NaN);
+            setBarycenter(Point1D.NaN);
             setSize(((Boolean) getTree(false).getAttribute()) ? Double.POSITIVE_INFINITY : 0);
         } else {
             double size = 0.0;
@@ -156,11 +155,11 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
             }
             setSize(size);
             if (Double.isInfinite(size)) {
-                setBarycenter((Point<Euclidean1D>) Cartesian1D.NaN);
+                setBarycenter(Point1D.NaN);
             } else if (size > 0) {
-                setBarycenter((Point<Euclidean1D>) new Cartesian1D(sum / size));
+                setBarycenter(new Point1D(sum / size));
             } else {
-                setBarycenter((Point<Euclidean1D>) ((OrientedPoint) getTree(false).getCut().getHyperplane()).getLocation());
+                setBarycenter(((OrientedPoint) getTree(false).getCut().getHyperplane()).getLocation());
             }
         }
     }
@@ -172,7 +171,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * instance is empty)
      */
     public double getInf() {
-        BSPTree<Euclidean1D> node = getTree(false);
+        BSPTree<Point1D> node = getTree(false);
         double  inf  = Double.POSITIVE_INFINITY;
         while (node.getCut() != null) {
             final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane();
@@ -189,7 +188,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * instance is empty)
      */
     public double getSup() {
-        BSPTree<Euclidean1D> node = getTree(false);
+        BSPTree<Point1D> node = getTree(false);
         double  sup  = Double.NEGATIVE_INFINITY;
         while (node.getCut() != null) {
             final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane();
@@ -202,10 +201,10 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
     /** {@inheritDoc}
      */
     @Override
-    public BoundaryProjection<Euclidean1D> projectToBoundary(final Point<Euclidean1D> point) {
+    public BoundaryProjection<Point1D> projectToBoundary(final Point1D point) {
 
         // get position of test point
-        final double x = ((Cartesian1D) point).getX();
+        final double x = point.getX();
 
         double previous = Double.NEGATIVE_INFINITY;
         for (final double[] a : this) {
@@ -242,8 +241,8 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param x abscissa of the point
      * @return a new point for finite abscissa, null otherwise
      */
-    private Cartesian1D finiteOrNullPoint(final double x) {
-        return Double.isInfinite(x) ? null : new Cartesian1D(x);
+    private Point1D finiteOrNullPoint(final double x) {
+        return Double.isInfinite(x) ? null : new Point1D(x);
     }
 
     /** Build an ordered list of intervals representing the instance.
@@ -271,15 +270,15 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param root tree root
      * @return first leaf node
      */
-    private BSPTree<Euclidean1D> getFirstLeaf(final BSPTree<Euclidean1D> root) {
+    private BSPTree<Point1D> getFirstLeaf(final BSPTree<Point1D> root) {
 
         if (root.getCut() == null) {
             return root;
         }
 
         // find the smallest internal node
-        BSPTree<Euclidean1D> smallest = null;
-        for (BSPTree<Euclidean1D> n = root; n != null; n = previousInternalNode(n)) {
+        BSPTree<Point1D> smallest = null;
+        for (BSPTree<Point1D> n = root; n != null; n = previousInternalNode(n)) {
             smallest = n;
         }
 
@@ -291,10 +290,10 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @return smallest internal node,
      * or null if there are no internal nodes (i.e. the set is either empty or covers the real line)
      */
-    private BSPTree<Euclidean1D> getFirstIntervalBoundary() {
+    private BSPTree<Point1D> getFirstIntervalBoundary() {
 
         // start search at the tree root
-        BSPTree<Euclidean1D> node = getTree(false);
+        BSPTree<Point1D> node = getTree(false);
         if (node.getCut() == null) {
             return null;
         }
@@ -315,7 +314,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param node internal node to check
      * @return true if the node corresponds to the start abscissa of an interval
      */
-    private boolean isIntervalStart(final BSPTree<Euclidean1D> node) {
+    private boolean isIntervalStart(final BSPTree<Point1D> node) {
 
         if ((Boolean) leafBefore(node).getAttribute()) {
             // it has an inside cell before it, it may end an interval but not start it
@@ -337,7 +336,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param node internal node to check
      * @return true if the node corresponds to the end abscissa of an interval
      */
-    private boolean isIntervalEnd(final BSPTree<Euclidean1D> node) {
+    private boolean isIntervalEnd(final BSPTree<Point1D> node) {
 
         if (!(Boolean) leafBefore(node).getAttribute()) {
             // it has an outside cell before it, it may start an interval but not end it
@@ -360,7 +359,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @return next internal node in ascending order, or null
      * if this is the last internal node
      */
-    private BSPTree<Euclidean1D> nextInternalNode(BSPTree<Euclidean1D> node) {
+    private BSPTree<Point1D> nextInternalNode(BSPTree<Point1D> node) {
 
         if (childAfter(node).getCut() != null) {
             // the next node is in the sub-tree
@@ -380,7 +379,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @return previous internal node in ascending order, or null
      * if this is the first internal node
      */
-    private BSPTree<Euclidean1D> previousInternalNode(BSPTree<Euclidean1D> node) {
+    private BSPTree<Point1D> previousInternalNode(BSPTree<Point1D> node) {
 
         if (childBefore(node).getCut() != null) {
             // the next node is in the sub-tree
@@ -399,7 +398,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param node internal node at which the sub-tree starts
      * @return leaf node just before the internal node
      */
-    private BSPTree<Euclidean1D> leafBefore(BSPTree<Euclidean1D> node) {
+    private BSPTree<Point1D> leafBefore(BSPTree<Point1D> node) {
 
         node = childBefore(node);
         while (node.getCut() != null) {
@@ -414,7 +413,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param node internal node at which the sub-tree starts
      * @return leaf node just after the internal node
      */
-    private BSPTree<Euclidean1D> leafAfter(BSPTree<Euclidean1D> node) {
+    private BSPTree<Point1D> leafAfter(BSPTree<Point1D> node) {
 
         node = childAfter(node);
         while (node.getCut() != null) {
@@ -429,8 +428,8 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param node child node considered
      * @return true is the node has a parent end is before it in ascending order
      */
-    private boolean isBeforeParent(final BSPTree<Euclidean1D> node) {
-        final BSPTree<Euclidean1D> parent = node.getParent();
+    private boolean isBeforeParent(final BSPTree<Point1D> node) {
+        final BSPTree<Point1D> parent = node.getParent();
         if (parent == null) {
             return false;
         } else {
@@ -442,8 +441,8 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param node child node considered
      * @return true is the node has a parent end is after it in ascending order
      */
-    private boolean isAfterParent(final BSPTree<Euclidean1D> node) {
-        final BSPTree<Euclidean1D> parent = node.getParent();
+    private boolean isAfterParent(final BSPTree<Point1D> node) {
+        final BSPTree<Point1D> parent = node.getParent();
         if (parent == null) {
             return false;
         } else {
@@ -455,7 +454,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param node internal node at which the sub-tree starts
      * @return child node just before the internal node
      */
-    private BSPTree<Euclidean1D> childBefore(BSPTree<Euclidean1D> node) {
+    private BSPTree<Point1D> childBefore(BSPTree<Point1D> node) {
         if (isDirect(node)) {
             // smaller abscissas are on minus side, larger abscissas are on plus side
             return node.getMinus();
@@ -469,7 +468,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param node internal node at which the sub-tree starts
      * @return child node just after the internal node
      */
-    private BSPTree<Euclidean1D> childAfter(BSPTree<Euclidean1D> node) {
+    private BSPTree<Point1D> childAfter(BSPTree<Point1D> node) {
         if (isDirect(node)) {
             // smaller abscissas are on minus side, larger abscissas are on plus side
             return node.getPlus();
@@ -483,7 +482,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param node internal node to check
      * @return true if the oriented point is direct
      */
-    private boolean isDirect(final BSPTree<Euclidean1D> node) {
+    private boolean isDirect(final BSPTree<Point1D> node) {
         return ((OrientedPoint) node.getCut().getHyperplane()).isDirect();
     }
 
@@ -491,7 +490,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
      * @param node internal node to check
      * @return abscissa
      */
-    private double getAngle(final BSPTree<Euclidean1D> node) {
+    private double getAngle(final BSPTree<Point1D> node) {
         return ((OrientedPoint) node.getCut().getHyperplane()).getLocation().getX();
     }
 
@@ -512,7 +511,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
     private class SubIntervalsIterator implements Iterator<double[]> {
 
         /** Current node. */
-        private BSPTree<Euclidean1D> current;
+        private BSPTree<Point1D> current;
 
         /** Sub-interval no yet returned. */
         private double[] pending;
@@ -549,7 +548,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
         private void selectPending() {
 
             // look for the start of the interval
-            BSPTree<Euclidean1D> start = current;
+            BSPTree<Point1D> start = current;
             while (start != null && !isIntervalStart(start)) {
                 start = nextInternalNode(start);
             }
@@ -562,7 +561,7 @@ public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> imple
             }
 
             // look for the end of the interval
-            BSPTree<Euclidean1D> end = start;
+            BSPTree<Point1D> end = start;
             while (end != null && !isIntervalEnd(end)) {
                 end = nextInternalNode(end);
             }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/OrientedPoint.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/OrientedPoint.java
index b1d0d89..e03edbb 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/OrientedPoint.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/OrientedPoint.java
@@ -16,8 +16,6 @@
  */
 package org.apache.commons.geometry.euclidean.oned;
 
-import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Vector;
 import org.apache.commons.geometry.core.partitioning.Hyperplane;
 
 /** This class represents a 1D oriented hyperplane.
@@ -25,10 +23,10 @@ import org.apache.commons.geometry.core.partitioning.Hyperplane;
  * boolean.</p>
  * <p>Instances of this class are guaranteed to be immutable.</p>
  */
-public class OrientedPoint implements Hyperplane<Euclidean1D> {
+public class OrientedPoint implements Hyperplane<Point1D> {
 
-    /** Vector location. */
-    private final Cartesian1D location;
+    /** Point location. */
+    private final Point1D location;
 
     /** Orientation. */
     private boolean direct;
@@ -42,7 +40,7 @@ public class OrientedPoint implements Hyperplane<Euclidean1D> {
      * abscissas greater than {@code location}
      * @param tolerance tolerance below which points are considered to belong to the hyperplane
      */
-    public OrientedPoint(final Cartesian1D location, final boolean direct, final double tolerance) {
+    public OrientedPoint(final Point1D location, final boolean direct, final double tolerance) {
         this.location  = location;
         this.direct    = direct;
         this.tolerance = tolerance;
@@ -58,18 +56,10 @@ public class OrientedPoint implements Hyperplane<Euclidean1D> {
         return this;
     }
 
-    /** Get the offset (oriented distance) of a vector.
-     * @param vector vector to check
-     * @return offset of the vector
-     */
-    public double getOffset(Vector<Euclidean1D> vector) {
-        return getOffset((Point<Euclidean1D>) vector);
-    }
-
     /** {@inheritDoc} */
     @Override
-    public double getOffset(final Point<Euclidean1D> point) {
-        final double delta = ((Cartesian1D) point).getX() - location.getX();
+    public double getOffset(final Point1D point) {
+        final double delta = point.getX() - location.getX();
         return direct ? delta : -delta;
     }
 
@@ -100,13 +90,13 @@ public class OrientedPoint implements Hyperplane<Euclidean1D> {
 
     /** {@inheritDoc} */
     @Override
-    public boolean sameOrientationAs(final Hyperplane<Euclidean1D> other) {
+    public boolean sameOrientationAs(final Hyperplane<Point1D> other) {
         return !(direct ^ ((OrientedPoint) other).direct);
     }
 
     /** {@inheritDoc} */
     @Override
-    public Point<Euclidean1D> project(Point<Euclidean1D> point) {
+    public Point1D project(Point1D point) {
         return location;
     }
 
@@ -119,7 +109,7 @@ public class OrientedPoint implements Hyperplane<Euclidean1D> {
     /** Get the hyperplane location on the real line.
      * @return the hyperplane location
      */
-    public Cartesian1D getLocation() {
+    public Point1D getLocation() {
         return location;
     }
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Point1D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Point1D.java
new file mode 100644
index 0000000..8983598
--- /dev/null
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Point1D.java
@@ -0,0 +1,267 @@
+/*
+ * 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 org.apache.commons.geometry.euclidean.EuclideanPoint;
+import org.apache.commons.numbers.arrays.LinearCombination;
+
+/** This class representing a point in one-dimensional Euclidean space.
+ * Instances of this class are guaranteed to be immutable.
+ */
+public final class Point1D extends Cartesian1D implements EuclideanPoint<Point1D, Vector1D> {
+
+    /** Origin (coordinates: 0). */
+    public static final Point1D ZERO = new Point1D(0.0);
+
+    /** Unit (coordinates: 1). */
+    public static final Point1D ONE  = new Point1D(1.0);
+
+    // CHECKSTYLE: stop ConstantName
+    /** A vector with all coordinates set to NaN. */
+    public static final Point1D NaN = new Point1D(Double.NaN);
+    // CHECKSTYLE: resume ConstantName
+
+    /** A point with all coordinates set to positive infinity. */
+    public static final Point1D POSITIVE_INFINITY =
+        new Point1D(Double.POSITIVE_INFINITY);
+
+    /** A point with all coordinates set to negative infinity. */
+    public static final Point1D NEGATIVE_INFINITY =
+        new Point1D(Double.NEGATIVE_INFINITY);
+
+    /** Serializable UID. */
+    private static final long serialVersionUID = 7556674948671647925L;
+
+    /** Simple constructor.
+     * @param x abscissa (coordinate value)
+     * @see #getX()
+     */
+    public Point1D(double x) {
+        super(x);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector1D asVector() {
+        return Vector1D.of(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distance(Point1D p) {
+        return Math.abs(p.x - x);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector1D subtract(Point1D p) {
+        return Vector1D.of(x - p.x);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector1D vectorTo(Point1D p) {
+        return p.subtract(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Point1D add(Vector1D v) {
+        return new Point1D(x + v.x);
+    }
+
+    /**
+     * Get a hashCode for this point.
+     * <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 7785;
+        }
+        return 997 * Double.hashCode(x);
+    }
+
+    /**
+     * Test for the equality of two points.
+     * <p>
+     * If all coordinates of two points are exactly the same, and none are
+     * <code>Double.NaN</code>, the two points are considered to be equal.
+     * </p>
+     * <p>
+     * <code>NaN</code> coordinates are considered to globally affect the point
+     * and be equal to each other - i.e, if either (or all) coordinates of the
+     * point are equal to <code>Double.NaN</code>, the point is equal to
+     * {@link #NaN}.
+     * </p>
+     *
+     * @param other Object to test for equality to this
+     * @return true if the two point objects are equal, false if
+     *         object is null, not an instance of Point1D, or
+     *         not equal to this Point1D instance
+     *
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (other instanceof Point1D) {
+            final Point1D rhs = (Point1D) other;
+            if (rhs.isNaN()) {
+                return this.isNaN();
+            }
+
+            return x == rhs.x;
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "(" + x + ")";
+    }
+
+    /** Returns a point with the given coordinate value.
+     * @param x point coordinate
+     * @return point instance
+     */
+    public static Point1D of(double x) {
+        return new Point1D(x);
+    }
+
+    /** Returns a point instance with the given coordinate value.
+     * @param value point coordinate
+     * @return point instance
+     */
+    public static Point1D of(Cartesian1D value) {
+        return new Point1D(value.x);
+    }
+
+    /** Returns a point with coordinates calculated by multiplying each input coordinate
+     * with its corresponding factor and adding the results.
+     *
+     * <p>This is equivalent
+     * to converting all input coordinates to vectors, scaling and adding the
+     * vectors (a linear combination), and adding the result to the zero point.
+     * This method, however, does not create any intermediate objects.
+     * </p>
+     * <p>
+     * The name of this method was chosen to emphasize the fact that the operation
+     * should be viewed as occurring in vector space, since addition and scalar
+     * multiplication are not defined directly for points.
+     * </p>
+     *
+     * @param a scale factor for first coordinate
+     * @param c first coordinate
+     * @return point with coordinates calculated by {@code a * c}
+     * @see {@link Vector1D#linearCombination(double, Vector1D)}
+     */
+    public static Point1D vectorCombination(double a, Cartesian1D c) {
+        return new Point1D(a * c.x);
+    }
+
+    /** Returns a point with coordinates calculated by multiplying each input coordinate
+     * with its corresponding factor and adding the results.
+     *
+     * <p>This is equivalent
+     * to converting all input coordinates to vectors, scaling and adding the
+     * vectors (a linear combination), and adding the result to the zero point.
+     * This method, however, does not create any intermediate objects.
+     * </p>
+     * <p>
+     * The name of this method was chosen to emphasize the fact that the operation
+     * should be viewed as occurring in vector space, since addition and scalar
+     * multiplication are not defined directly for points.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @return point with coordinates calculated by {@code (a1 * c1) + (a2 * c2)}
+     * @see {@link Vector1D#linearCombination(double, Cartesian1D, double, Cartesian1D)}
+     */
+    public static Point1D vectorCombination(double a1, Cartesian1D c1, double a2, Cartesian1D c2) {
+        return new Point1D(
+                LinearCombination.value(a1, c1.x, a2, c2.x));
+    }
+
+    /** Returns a point with coordinates calculated by multiplying each input coordinate
+     * with its corresponding factor and adding the results.
+     *
+     * <p>This is equivalent
+     * to converting all input coordinates to vectors, scaling and adding the
+     * vectors (a linear combination), and adding the result to the zero point.
+     * This method, however, does not create any intermediate objects.
+     * </p>
+     * <p>
+     * The name of this method was chosen to emphasize the fact that the operation
+     * should be viewed as occurring in vector space, since addition and scalar
+     * multiplication are not defined directly for points.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @param a3 scale factor for third coordinate
+     * @param c3 third coordinate
+     * @return point with coordinates calculated by {@code (a1 * c1) + (a2 * c2) + (a3 * c3)}
+     * @see {@link Vector1D#linearCombination(double, Cartesian1D, double, Cartesian1D, double, Cartesian1D)}
+     */
+    public static Point1D vectorCombination(double a1, Cartesian1D c1, double a2, Cartesian1D c2,
+            double a3, Cartesian1D c3) {
+        return new Point1D(
+                LinearCombination.value(a1, c1.x, a2, c2.x, a3, c3.x));
+    }
+
+    /** Returns a point with coordinates calculated by multiplying each input coordinate
+     * with its corresponding factor and adding the results.
+     *
+     * <p>This is equivalent
+     * to converting all input coordinates to vectors, scaling and adding the
+     * vectors (a linear combination), and adding the result to the zero point.
+     * This method, however, does not create any intermediate objects.
+     * </p>
+     * <p>
+     * The name of this method was chosen to emphasize the fact that the operation
+     * should be viewed as occurring in vector space, since addition and scalar
+     * multiplication are not defined directly for points.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @param a3 scale factor for third coordinate
+     * @param c3 third coordinate
+     * @param a4 scale factor for fourth coordinate
+     * @param c4 fourth coordinate
+     * @return point with coordinates calculated by {@code (a1 * c1) + (a2 * c2) + (a3 * c3) + (a4 * c4)}
+     * @see {@link Vector1D#linearCombination(double, Cartesian1D, double, Cartesian1D, double, Cartesian1D, double, Cartesian1D)}
+     */
+    public static Point1D vectorCombination(double a1, Cartesian1D c1, double a2, Cartesian1D c2,
+            double a3, Cartesian1D c3, double a4, Cartesian1D c4) {
+        return new Point1D(
+                LinearCombination.value(a1, c1.x, a2, c2.x, a3, c3.x, a4, c4.x));
+    }
+}
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/SubOrientedPoint.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/SubOrientedPoint.java
index 8e05e5a..de6881b 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/SubOrientedPoint.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/SubOrientedPoint.java
@@ -25,14 +25,14 @@ import org.apache.commons.geometry.core.partitioning.Region;
  * boolean.</p>
  * <p>Instances of this class are guaranteed to be immutable.</p>
  */
-public class SubOrientedPoint extends AbstractSubHyperplane<Euclidean1D, Euclidean1D> {
+public class SubOrientedPoint extends AbstractSubHyperplane<Point1D, Point1D> {
 
     /** Simple constructor.
      * @param hyperplane underlying hyperplane
      * @param remainingRegion remaining region of the hyperplane
      */
-    public SubOrientedPoint(final Hyperplane<Euclidean1D> hyperplane,
-                            final Region<Euclidean1D> remainingRegion) {
+    public SubOrientedPoint(final Hyperplane<Point1D> hyperplane,
+                            final Region<Point1D> remainingRegion) {
         super(hyperplane, remainingRegion);
     }
 
@@ -50,14 +50,14 @@ public class SubOrientedPoint extends AbstractSubHyperplane<Euclidean1D, Euclide
 
     /** {@inheritDoc} */
     @Override
-    protected AbstractSubHyperplane<Euclidean1D, Euclidean1D> buildNew(final Hyperplane<Euclidean1D> hyperplane,
-                                                                       final Region<Euclidean1D> remainingRegion) {
+    protected AbstractSubHyperplane<Point1D, Point1D> buildNew(final Hyperplane<Point1D> hyperplane,
+                                                                       final Region<Point1D> remainingRegion) {
         return new SubOrientedPoint(hyperplane, remainingRegion);
     }
 
     /** {@inheritDoc} */
     @Override
-    public SplitSubHyperplane<Euclidean1D> split(final Hyperplane<Euclidean1D> hyperplane) {
+    public SplitSubHyperplane<Point1D> split(final Hyperplane<Point1D> hyperplane) {
         final OrientedPoint thisHyperplane = (OrientedPoint) getHyperplane();
         final double global = hyperplane.getOffset(thisHyperplane.getLocation());
 
@@ -65,11 +65,11 @@ public class SubOrientedPoint extends AbstractSubHyperplane<Euclidean1D, Euclide
         final double tolerance = thisHyperplane.getTolerance();
 
         if (global < -tolerance) {
-            return new SplitSubHyperplane<Euclidean1D>(null, this);
+            return new SplitSubHyperplane<Point1D>(null, this);
         } else if (global > tolerance) {
-            return new SplitSubHyperplane<Euclidean1D>(this, null);
+            return new SplitSubHyperplane<Point1D>(this, null);
         } else {
-            return new SplitSubHyperplane<Euclidean1D>(null, null);
+            return new SplitSubHyperplane<Point1D>(null, null);
         }
     }
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
index e9fdaf1..f2adbad 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
@@ -16,16 +16,307 @@
  */
 package org.apache.commons.geometry.euclidean.oned;
 
-import org.apache.commons.geometry.core.Vector;
+import org.apache.commons.geometry.euclidean.EuclideanVector;
+import org.apache.commons.numbers.arrays.LinearCombination;
 
-/** This class represents a 1D vector.
+/** This class represents a vector in one-dimensional Euclidean space.
+ * Instances of this class are guaranteed to be immutable.
  */
-public abstract class Vector1D implements Vector<Euclidean1D> {
+public final class Vector1D extends Cartesian1D implements EuclideanVector<Point1D, Vector1D> {
 
-    /** Get the abscissa of the vector.
-     * @return abscissa of the vector
-     * @see Cartesian1D#Cartesian1D(double)
+    /** Zero vector (coordinates: 0). */
+    public static final Vector1D ZERO = new Vector1D(0.0);
+
+    /** Unit vector (coordinates: 1). */
+    public static final Vector1D ONE  = new Vector1D(1.0);
+
+    // CHECKSTYLE: stop ConstantName
+    /** A vector with all coordinates set to NaN. */
+    public static final Vector1D NaN = new Vector1D(Double.NaN);
+    // CHECKSTYLE: resume ConstantName
+
+    /** A vector with all coordinates set to positive infinity. */
+    public static final Vector1D POSITIVE_INFINITY =
+        new Vector1D(Double.POSITIVE_INFINITY);
+
+    /** A vector with all coordinates set to negative infinity. */
+    public static final Vector1D NEGATIVE_INFINITY =
+        new Vector1D(Double.NEGATIVE_INFINITY);
+
+    /** Serializable UID. */
+    private static final long serialVersionUID = 1582116020164328846L;
+
+    /** Simple constructor.
+     * @param x abscissa (coordinate value)
      */
-    public abstract double getX();
+    public Vector1D(double x) {
+        super(x);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Point1D asPoint() {
+        return Point1D.of(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector1D getZero() {
+        return ZERO;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double getNorm1() {
+        return getNorm();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double getNorm() {
+        return Math.abs(x);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double getNormSq() {
+        return x * x;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double getNormInf() {
+        return getNorm();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector1D add(Vector1D v) {
+        return new Vector1D(x + v.x);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector1D add(double factor, Vector1D v) {
+        return new Vector1D(x + (factor * v.x));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector1D subtract(Vector1D v) {
+        return new Vector1D(x - v.x);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector1D subtract(double factor, Vector1D v) {
+        return new Vector1D(x - (factor * v.x));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector1D negate() {
+        return new Vector1D(-x);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector1D normalize() throws IllegalStateException {
+        double s = getNorm();
+        if (s == 0) {
+            throw new IllegalStateException("Norm is zero");
+        }
+        return scalarMultiply(1.0 / s);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector1D scalarMultiply(double a) {
+        return new Vector1D(a * x);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distance1(Vector1D v) {
+        return distance(v);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distance(Vector1D v) {
+        return Math.abs(v.x - x);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distanceInf(Vector1D v) {
+        return distance(v);
+    }
 
+    /** {@inheritDoc} */
+    @Override
+    public double distanceSq(Vector1D v) {
+        final double dx = v.x - x;
+        return dx * dx;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double dotProduct(Vector1D v) {
+        return x * v.x;
+    }
+
+    /**
+     * Get a hashCode for the vector.
+     * <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 857;
+        }
+        return 403 * Double.hashCode(x);
+    }
+
+    /**
+     * Test for the equality of two vectors.
+     * <p>
+     * If all coordinates of two vectors are exactly the same, and none are
+     * <code>Double.NaN</code>, the two vectors are considered to be equal.
+     * </p>
+     * <p>
+     * <code>NaN</code> coordinates are considered to globally affect the vector
+     * and be equal to each other - i.e, if either (or all) coordinates of the
+     * vector are equal to <code>Double.NaN</code>, the vector is equal to
+     * {@link #NaN}.
+     * </p>
+     *
+     * @param other Object to test for equality to this
+     * @return true if two vector objects are equal, false if
+     *         object is null, not an instance of Vector1D, or
+     *         not equal to this Vector1D instance
+     *
+     */
+    @Override
+    public boolean equals(Object other) {
+
+        if (this == other) {
+            return true;
+        }
+
+        if (other instanceof Vector1D) {
+            final Vector1D rhs = (Vector1D) other;
+            if (rhs.isNaN()) {
+                return this.isNaN();
+            }
+
+            return x == rhs.x;
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "{" + x + "}";
+    }
+
+    /** Returns a vector with the given coordinate value.
+     * @param x vector coordinate
+     * @return vector instance
+     */
+    public static Vector1D of(double x) {
+        return new Vector1D(x);
+    }
+
+    /** Returns a vector instance with the given coordinate value.
+     * @param value vector coordinate
+     * @return vector instance
+     */
+    public static Vector1D of(Cartesian1D value) {
+        return new Vector1D(value.x);
+    }
+
+    /** Returns a vector consisting of the linear combination of the inputs.
+     * <p>
+     * A linear combination is the sum of all of the inputs multiplied by their
+     * corresponding scale factors. All inputs are interpreted as vectors. If points
+     * are to be passed, they should be viewed as representing the vector from the
+     * zero point to the given point.
+     * </p>
+     *
+     * @param a scale factor for first coordinate
+     * @param c first coordinate
+     * @return vector with coordinates calculated by {@code a * c}
+     */
+    public static Vector1D linearCombination(double a, Cartesian1D c) {
+        return new Vector1D(a * c.x);
+    }
+
+    /** Returns a vector consisting of the linear combination of the inputs.
+     * <p>
+     * A linear combination is the sum of all of the inputs multiplied by their
+     * corresponding scale factors. All inputs are interpreted as vectors. If points
+     * are to be passed, they should be viewed as representing the vector from the
+     * zero point to the given point.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @return vector with coordinates calculated by {@code (a1 * c1) + (a2 * c2)}
+     */
+    public static Vector1D linearCombination(double a1, Cartesian1D c1, double a2, Cartesian1D c2) {
+        return new Vector1D(
+                LinearCombination.value(a1, c1.x, a2, c2.x));
+    }
+
+    /** Returns a vector consisting of the linear combination of the inputs.
+     * <p>
+     * A linear combination is the sum of all of the inputs multiplied by their
+     * corresponding scale factors. All inputs are interpreted as vectors. If points
+     * are to be passed, they should be viewed as representing the vector from the
+     * zero point to the given point.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @param a3 scale factor for third coordinate
+     * @param c3 third coordinate
+     * @return vector with coordinates calculated by {@code (a1 * c1) + (a2 * c2) + (a3 * c3)}
+     */
+    public static Vector1D linearCombination(double a1, Cartesian1D c1, double a2, Cartesian1D c2,
+            double a3, Cartesian1D c3) {
+        return new Vector1D(
+                LinearCombination.value(a1, c1.x, a2, c2.x, a3, c3.x));
+    }
+
+    /** Returns a vector consisting of the linear combination of the inputs.
+     * <p>
+     * A linear combination is the sum of all of the inputs multiplied by their
+     * corresponding scale factors. All inputs are interpreted as vectors. If points
+     * are to be passed, they should be viewed as representing the vector from the
+     * zero point to the given point.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @param a3 scale factor for third coordinate
+     * @param c3 third coordinate
+     * @param a4 scale factor for fourth coordinate
+     * @param c4 fourth coordinate
+     * @return point with coordinates calculated by {@code (a1 * c1) + (a2 * c2) + (a3 * c3) + (a4 * c4)}
+     */
+    public static Vector1D linearCombination(double a1, Cartesian1D c1, double a2, Cartesian1D c2,
+            double a3, Cartesian1D c3, double a4, Cartesian1D c4) {
+        return new Vector1D(
+                LinearCombination.value(a1, c1.x, a2, c2.x, a3, c3.x, a4, c4.x));
+    }
 }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/package-info.java
similarity index 68%
copy from commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
copy to commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/package-info.java
index e9fdaf1..2ed2a4c 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/package-info.java
@@ -14,18 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.geometry.euclidean.oned;
-
-import org.apache.commons.geometry.core.Vector;
-
-/** This class represents a 1D vector.
+/**
+ *
+ * <p>
+ * This package provides basic interfaces for Euclidean components.
+ * </p>
  */
-public abstract class Vector1D implements Vector<Euclidean1D> {
-
-    /** Get the abscissa of the vector.
-     * @return abscissa of the vector
-     * @see Cartesian1D#Cartesian1D(double)
-     */
-    public abstract double getX();
-
-}
+package org.apache.commons.geometry.euclidean;
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 79b975f..b57c108 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
@@ -17,210 +17,59 @@
 
 package org.apache.commons.geometry.euclidean.threed;
 
-import java.io.Serializable;
-import java.text.NumberFormat;
+import org.apache.commons.geometry.core.Spatial;
 
-import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
-import org.apache.commons.geometry.core.Vector;
-import org.apache.commons.numbers.arrays.LinearCombination;
-
-/**
- * This class represents points or vectors in a three-dimensional space.
- * <p>An instance of Cartesian3D represents the point with the corresponding
- * coordinates.</p>
- * <p>An instance of Cartesian3D also represents the vector which begins at
- * the origin and ends at the point corresponding to the coordinates.</p>
- * <p>Instance of this class are guaranteed to be immutable.</p>
+/** This class represents a Cartesian coordinate value in
+ * three-dimensional Euclidean space.
  */
-public class Cartesian3D extends Vector3D implements Serializable, Point<Euclidean3D> {
-
-    /** Null vector (coordinates: 0, 0, 0). */
-    public static final Cartesian3D ZERO   = new Cartesian3D(0, 0, 0);
-
-    /** First canonical vector (coordinates: 1, 0, 0). */
-    public static final Cartesian3D PLUS_I = new Cartesian3D(1, 0, 0);
-
-    /** Opposite of the first canonical vector (coordinates: -1, 0, 0). */
-    public static final Cartesian3D MINUS_I = new Cartesian3D(-1, 0, 0);
-
-    /** Second canonical vector (coordinates: 0, 1, 0). */
-    public static final Cartesian3D PLUS_J = new Cartesian3D(0, 1, 0);
-
-    /** Opposite of the second canonical vector (coordinates: 0, -1, 0). */
-    public static final Cartesian3D MINUS_J = new Cartesian3D(0, -1, 0);
-
-    /** Third canonical vector (coordinates: 0, 0, 1). */
-    public static final Cartesian3D PLUS_K = new Cartesian3D(0, 0, 1);
-
-    /** Opposite of the third canonical vector (coordinates: 0, 0, -1).  */
-    public static final Cartesian3D MINUS_K = new Cartesian3D(0, 0, -1);
-
-    // CHECKSTYLE: stop ConstantName
-    /** A vector with all coordinates set to NaN. */
-    public static final Cartesian3D NaN = new Cartesian3D(Double.NaN, Double.NaN, Double.NaN);
-    // CHECKSTYLE: resume ConstantName
-
-    /** A vector with all coordinates set to positive infinity. */
-    public static final Cartesian3D POSITIVE_INFINITY =
-        new Cartesian3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
-
-    /** A vector with all coordinates set to negative infinity. */
-    public static final Cartesian3D NEGATIVE_INFINITY =
-        new Cartesian3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+public abstract class Cartesian3D implements Spatial {
 
-    /** Serializable version identifier. */
-    private static final long serialVersionUID = 1313493323784566947L;
+    /** Serializable UID. */
+    private static final long serialVersionUID = 6249091865814886817L;
 
-    /** Error message when norms are zero. */
-    private static final String ZERO_NORM_MSG = "Norm is zero";
+    /** Abscissa (first coordinate value) */
+    protected final double x;
 
-    /** Abscissa. */
-    private final double x;
+    /** Ordinate (second coordinate value) */
+    protected final double y;
 
-    /** Ordinate. */
-    private final double y;
-
-    /** Height. */
-    private final double z;
+    /** Height (third coordinate value)*/
+    protected final double z;
 
     /** Simple constructor.
-     * Build a vector from its coordinates
-     * @param x abscissa
-     * @param y ordinate
-     * @param z height
-     * @see #getX()
-     * @see #getY()
-     * @see #getZ()
+     * @param x abscissa (first coordinate value)
+     * @param y ordinate (second coordinate value)
+     * @param z height (third coordinate value)
      */
-    public Cartesian3D(double x, double y, double z) {
+    protected Cartesian3D(double x, double y, double z) {
         this.x = x;
         this.y = y;
         this.z = z;
     }
 
-    /** Simple constructor.
-     * Build a vector from its coordinates
-     * @param v coordinates array
-     * @exception DimensionMismatchException if array does not have 3 elements
-     * @see #toArray()
-     */
-    public Cartesian3D(double[] v) throws IllegalArgumentException {
-        if (v.length != 3) {
-            throw new IllegalArgumentException("Dimension mismatch: " + v.length + " != 3");
-        }
-        this.x = v[0];
-        this.y = v[1];
-        this.z = v[2];
-    }
-
-    /** Simple constructor.
-     * Build 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()
+    /** Returns the abscissa (first coordinate) value of the instance.
+     * @return the abscisaa
      */
-    public Cartesian3D(double alpha, double delta) {
-        double cosDelta = Math.cos(delta);
-        this.x = Math.cos(alpha) * cosDelta;
-        this.y = Math.sin(alpha) * cosDelta;
-        this.z = Math.sin(delta);
-    }
-
-    /** Multiplicative constructor
-     * Build a vector from another one and a scale factor.
-     * The vector built will be a * u
-     * @param a scale factor
-     * @param u base (unscaled) vector
-     */
-    public Cartesian3D(double a, Cartesian3D u) {
-        this.x = a * u.x;
-        this.y = a * u.y;
-        this.z = a * u.z;
-    }
-
-    /** Linear constructor
-     * Build a vector from two other ones and corresponding scale factors.
-     * The vector built will be a1 * u1 + a2 * u2
-     * @param a1 first scale factor
-     * @param u1 first base (unscaled) vector
-     * @param a2 second scale factor
-     * @param u2 second base (unscaled) vector
-     */
-    public Cartesian3D(double a1, Cartesian3D u1, double a2, Cartesian3D u2) {
-        this.x = LinearCombination.value(a1, u1.x, a2, u2.x);
-        this.y = LinearCombination.value(a1, u1.y, a2, u2.y);
-        this.z = LinearCombination.value(a1, u1.z, a2, u2.z);
-    }
-
-    /** Linear constructor
-     * Build a vector from three other ones and corresponding scale factors.
-     * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
-     * @param a1 first scale factor
-     * @param u1 first base (unscaled) vector
-     * @param a2 second scale factor
-     * @param u2 second base (unscaled) vector
-     * @param a3 third scale factor
-     * @param u3 third base (unscaled) vector
-     */
-    public Cartesian3D(double a1, Cartesian3D u1, double a2, Cartesian3D u2,
-                    double a3, Cartesian3D u3) {
-        this.x = LinearCombination.value(a1, u1.x, a2, u2.x, a3, u3.x);
-        this.y = LinearCombination.value(a1, u1.y, a2, u2.y, a3, u3.y);
-        this.z = LinearCombination.value(a1, u1.z, a2, u2.z, a3, u3.z);
-    }
-
-    /** Linear constructor
-     * Build a vector from four other ones and corresponding scale factors.
-     * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
-     * @param a1 first scale factor
-     * @param u1 first base (unscaled) vector
-     * @param a2 second scale factor
-     * @param u2 second base (unscaled) vector
-     * @param a3 third scale factor
-     * @param u3 third base (unscaled) vector
-     * @param a4 fourth scale factor
-     * @param u4 fourth base (unscaled) vector
-     */
-    public Cartesian3D(double a1, Cartesian3D u1, double a2, Cartesian3D u2,
-                    double a3, Cartesian3D u3, double a4, Cartesian3D u4) {
-        this.x = LinearCombination.value(a1, u1.x, a2, u2.x, a3, u3.x, a4, u4.x);
-        this.y = LinearCombination.value(a1, u1.y, a2, u2.y, a3, u3.y, a4, u4.y);
-        this.z = LinearCombination.value(a1, u1.z, a2, u2.z, a3, u3.z, a4, u4.z);
-    }
-
-    /** Get the abscissa of the vector.
-     * @return abscissa of the vector
-     * @see #Cartesian3D(double, double, double)
-     */
-    @Override
     public double getX() {
         return x;
     }
 
-    /** Get the ordinate of the vector.
-     * @return ordinate of the vector
-     * @see #Cartesian3D(double, double, double)
+    /** Returns the ordinate (second coordinate) value of the instance.
+     * @return the ordinate
      */
-    @Override
     public double getY() {
         return y;
     }
 
-    /** Get the height of the vector.
-     * @return height of the vector
-     * @see #Cartesian3D(double, double, double)
+    /** Returns the height (third coordinate) value of the instance.
+     * @return the height
      */
-    @Override
     public double getZ() {
         return z;
     }
 
-    /** Get the vector coordinates as a dimension 3 array.
-     * @return vector coordinates
-     * @see #Cartesian3D(double[])
+    /** Get the coordinates for this instance as a dimension 3 array.
+     * @return the coordinates for this instance
      */
     public double[] toArray() {
         return new double[] { x, y, z };
@@ -228,172 +77,8 @@ public class Cartesian3D extends Vector3D implements Serializable, Point<Euclide
 
     /** {@inheritDoc} */
     @Override
-    public Space getSpace() {
-        return Euclidean3D.getInstance();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian3D getZero() {
-        return ZERO;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNorm1() {
-        return Math.abs(x) + Math.abs(y) + Math.abs(z);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNorm() {
-        // there are no cancellation problems here, so we use the straightforward formula
-        return Math.sqrt (x * x + y * y + z * z);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNormSq() {
-        // there are no cancellation problems here, so we use the straightforward formula
-        return x * x + y * y + z * z;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNormInf() {
-        return Math.max(Math.max(Math.abs(x), Math.abs(y)), Math.abs(z));
-    }
-
-    /** Get the azimuth of the vector.
-     * @return azimuth (&alpha;) of the vector, between -&pi; and +&pi;
-     * @see #Cartesian3D(double, double)
-     */
-    public double getAlpha() {
-        return Math.atan2(y, x);
-    }
-
-    /** Get the elevation of the vector.
-     * @return elevation (&delta;) of the vector, between -&pi;/2 and +&pi;/2
-     * @see #Cartesian3D(double, double)
-     */
-    public double getDelta() {
-        return Math.asin(z / getNorm());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian3D add(final Vector<Euclidean3D> v) {
-        final Cartesian3D v3 = (Cartesian3D) v;
-        return new Cartesian3D(x + v3.x, y + v3.y, z + v3.z);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian3D add(double factor, final Vector<Euclidean3D> v) {
-        return new Cartesian3D(1, this, factor, (Cartesian3D) v);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian3D subtract(final Vector<Euclidean3D> v) {
-        final Cartesian3D v3 = (Cartesian3D) v;
-        return new Cartesian3D(x - v3.x, y - v3.y, z - v3.z);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian3D subtract(final double factor, final Vector<Euclidean3D> v) {
-        return new Cartesian3D(1, this, -factor, (Cartesian3D) v);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian3D normalize() throws IllegalStateException {
-        double s = getNorm();
-        if (s == 0) {
-            throw new IllegalStateException(ZERO_NORM_MSG);
-        }
-        return scalarMultiply(1 / s);
-    }
-
-    /** Get a vector orthogonal to the instance.
-     * <p>There are an infinite number of normalized vectors orthogonal
-     * to the instance. This method picks up one of them almost
-     * arbitrarily. It is useful when one needs to compute a reference
-     * frame with one of the axes in a predefined direction. The
-     * following example shows how to build a frame having the k axis
-     * aligned with the known vector u :
-     * <pre><code>
-     *   Cartesian3D k = u.normalize();
-     *   Cartesian3D i = k.orthogonal();
-     *   Cartesian3D j = Cartesian3D.crossProduct(k, i);
-     * </code></pre>
-     * @return a new normalized vector orthogonal to the instance
-     * @exception IllegalStateException if the norm of the instance is zero
-     */
-    public Cartesian3D orthogonal() throws IllegalStateException {
-
-        double threshold = 0.6 * getNorm();
-        if (threshold == 0) {
-            throw new IllegalStateException(ZERO_NORM_MSG);
-        }
-
-        if (Math.abs(x) <= threshold) {
-            double inverse  = 1 / Math.sqrt(y * y + z * z);
-            return new Cartesian3D(0, inverse * z, -inverse * y);
-        } else if (Math.abs(y) <= threshold) {
-            double inverse  = 1 / Math.sqrt(x * x + z * z);
-            return new Cartesian3D(-inverse * z, 0, inverse * x);
-        }
-        double inverse  = 1 / Math.sqrt(x * x + y * y);
-        return new Cartesian3D(inverse * y, -inverse * x, 0);
-
-    }
-
-    /** Compute the angular separation between two vectors.
-     * <p>This method computes the angular separation between two
-     * vectors using the dot product for well separated vectors and the
-     * cross product for almost aligned vectors. This allows to have a
-     * good accuracy in all cases, even for vectors very close to each
-     * other.</p>
-     * @param v1 first vector
-     * @param v2 second vector
-     * @return angular separation between v1 and v2
-     * @exception IllegalArgumentException if either vector has a zero norm
-     */
-    public static double angle(Cartesian3D v1, Cartesian3D v2) throws IllegalArgumentException {
-
-        double normProduct = v1.getNorm() * v2.getNorm();
-        if (normProduct == 0) {
-            throw new IllegalArgumentException(ZERO_NORM_MSG);
-        }
-
-        double dot = v1.dotProduct(v2);
-        double threshold = normProduct * 0.9999;
-        if ((dot < -threshold) || (dot > threshold)) {
-            // the vectors are almost aligned, compute using the sine
-            Cartesian3D v3 = crossProduct(v1, v2);
-            if (dot >= 0) {
-                return Math.asin(v3.getNorm() / normProduct);
-            }
-            return Math.PI - Math.asin(v3.getNorm() / normProduct);
-        }
-
-        // the vectors are sufficiently separated to use the cosine
-        return Math.acos(dot / normProduct);
-
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian3D negate() {
-        return new Cartesian3D(-x, -y, -z);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian3D scalarMultiply(double a) {
-        return new Cartesian3D(a * x, a * y, a * z);
+    public int getDimension() {
+        return 3;
     }
 
     /** {@inheritDoc} */
@@ -408,214 +93,16 @@ public class Cartesian3D extends Vector3D implements Serializable, Point<Euclide
         return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z));
     }
 
-    /**
-     * Test for the equality of two 3D vectors.
-     * <p>
-     * If all coordinates of two 3D vectors are exactly the same, and none are
-     * <code>Double.NaN</code>, the two 3D vectors are considered to be equal.
-     * </p>
-     * <p>
-     * <code>NaN</code> coordinates are considered to affect globally the vector
-     * and be equals to each other - i.e, if either (or all) coordinates of the
-     * 3D vector are equal to <code>Double.NaN</code>, the 3D vector is equal to
-     * {@link #NaN}.
-     * </p>
-     *
-     * @param other Object to test for equality to this
-     * @return true if two 3D vector objects are equal, false if
-     *         object is null, not an instance of Cartesian3D, or
-     *         not equal to this Cartesian3D instance
-     *
-     */
-    @Override
-    public boolean equals(Object other) {
-
-        if (this == other) {
-            return true;
-        }
-
-        if (other instanceof Cartesian3D) {
-            final Cartesian3D rhs = (Cartesian3D)other;
-            if (rhs.isNaN()) {
-                return this.isNaN();
-            }
-
-            return (x == rhs.x) && (y == rhs.y) && (z == rhs.z);
-        }
-        return false;
-    }
-
-    /**
-     * Get a hashCode for the 3D vector.
-     * <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 642;
-        }
-        return 643 * (164 * Double.hashCode(x) +  3 * Double.hashCode(y) +  Double.hashCode(z));
-    }
-
-    /** {@inheritDoc}
-     * <p>
-     * The implementation uses specific multiplication and addition
-     * algorithms to preserve accuracy and reduce cancellation effects.
-     * It should be very accurate even for nearly orthogonal vectors.
-     * </p>
-     * @see LinearCombination#value(double, double, double, double, double, double)
-     */
-    @Override
-    public double dotProduct(final Vector<Euclidean3D> v) {
-        final Cartesian3D v3 = (Cartesian3D) v;
-        return LinearCombination.value(x, v3.x, y, v3.y, z, v3.z);
-    }
-
-    /** Compute the cross-product of the instance with another vector.
-     * @param v other vector
-     * @return the cross product this ^ v as a new Cartesian3D
-     */
-    public Cartesian3D crossProduct(final Vector<Euclidean3D> v) {
-        final Cartesian3D v3 = (Cartesian3D) v;
-        return new Cartesian3D(LinearCombination.value(y, v3.z, -z, v3.y),
-                            LinearCombination.value(z, v3.x, -x, v3.z),
-                            LinearCombination.value(x, v3.y, -y, v3.x));
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distance1(Vector<Euclidean3D> v) {
-        final Cartesian3D v3 = (Cartesian3D) v;
-        final double dx = Math.abs(v3.x - x);
-        final double dy = Math.abs(v3.y - y);
-        final double dz = Math.abs(v3.z - z);
-        return dx + dy + dz;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distance(Point<Euclidean3D> p) {
-        return distance((Cartesian3D) p);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distance(Vector<Euclidean3D> v) {
-        return distance((Cartesian3D) v);
-    }
-
-    /** Compute the distance between the instance and other coordinates.
-     * @param c other coordinates
-     * @return the distance between the instance and c
-     */
-    public double distance(Cartesian3D c) {
-        final double dx = c.x - x;
-        final double dy = c.y - y;
-        final double dz = c.z - z;
-        return Math.sqrt(dx * dx + dy * dy + dz * dz);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distanceInf(Vector<Euclidean3D> v) {
-        final Cartesian3D v3 = (Cartesian3D) v;
-        final double dx = Math.abs(v3.x - x);
-        final double dy = Math.abs(v3.y - y);
-        final double dz = Math.abs(v3.z - z);
-        return Math.max(Math.max(dx, dy), dz);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distanceSq(Vector<Euclidean3D> v) {
-        final Cartesian3D v3 = (Cartesian3D) v;
-        final double dx = v3.x - x;
-        final double dy = v3.y - y;
-        final double dz = v3.z - z;
-        return dx * dx + dy * dy + dz * dz;
-    }
-
-    /** Compute the dot-product of two vectors.
-     * @param v1 first vector
-     * @param v2 second vector
-     * @return the dot product v1.v2
-     */
-    public static double dotProduct(Cartesian3D v1, Cartesian3D v2) {
-        return v1.dotProduct(v2);
-    }
-
-    /** Compute the cross-product of two vectors.
-     * @param v1 first vector
-     * @param v2 second vector
-     * @return the cross product v1 ^ v2 as a new Vector
+    /** Returns the Euclidean distance from this set of coordinates to the given coordinates.
+     * @param other coordinates to compute the distance to.
+     * @return Euclidean distance value
      */
-    public static Cartesian3D crossProduct(final Cartesian3D v1, final Cartesian3D v2) {
-        return v1.crossProduct(v2);
-    }
-
-    /** Compute the distance between two vectors according to the L<sub>1</sub> norm.
-     * <p>Calling this method is equivalent to calling:
-     * <code>v1.subtract(v2).getNorm1()</code> except that no intermediate
-     * vector is built</p>
-     * @param v1 first vector
-     * @param v2 second vector
-     * @return the distance between v1 and v2 according to the L<sub>1</sub> norm
-     */
-    public static double distance1(Cartesian3D v1, Cartesian3D v2) {
-        return v1.distance1(v2);
-    }
-
-    /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
-     * <p>Calling this method is equivalent to calling:
-     * <code>v1.subtract(v2).getNorm()</code> except that no intermediate
-     * vector is built</p>
-     * @param v1 first vector
-     * @param v2 second vector
-     * @return the distance between v1 and v2 according to the L<sub>2</sub> norm
-     */
-    public static double distance(Cartesian3D v1, Cartesian3D v2) {
-        return v1.distance(v2);
-    }
-
-    /** Compute the distance between two vectors according to the L<sub>&infin;</sub> norm.
-     * <p>Calling this method is equivalent to calling:
-     * <code>v1.subtract(v2).getNormInf()</code> except that no intermediate
-     * vector is built</p>
-     * @param v1 first vector
-     * @param v2 second vector
-     * @return the distance between v1 and v2 according to the L<sub>&infin;</sub> norm
-     */
-    public static double distanceInf(Cartesian3D v1, Cartesian3D v2) {
-        return v1.distanceInf(v2);
-    }
-
-    /** Compute the square of the distance between two vectors.
-     * <p>Calling this method is equivalent to calling:
-     * <code>v1.subtract(v2).getNormSq()</code> except that no intermediate
-     * vector is built</p>
-     * @param v1 first vector
-     * @param v2 second vector
-     * @return the square of the distance between v1 and v2
-     */
-    public static double distanceSq(Cartesian3D v1, Cartesian3D v2) {
-        return v1.distanceSq(v2);
-    }
-
-    /** Get a string representation of this vector.
-     * @return a string representation of this vector
-     */
-    @Override
-    public String toString() {
-        return toString(NumberFormat.getInstance());
-    }
+    protected double euclideanDistance(Cartesian3D other) {
+        // there are no cancellation problems here, so we use the straightforward formula
+        final double dx = x - other.x;
+        final double dy = y - other.y;
+        final double dz = z - other.z;
 
-    /** {@inheritDoc} */
-    @Override
-    public String toString(final NumberFormat format) {
-        return "{" + format.format(x) + "; " + format.format(y) + "; " + format.format(z) + "}";
+        return Math.sqrt((dx * dx) + (dy * dy) + (dz * dz));
     }
-
 }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Euclidean3D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Euclidean3D.java
deleted file mode 100644
index 4988476..0000000
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Euclidean3D.java
+++ /dev/null
@@ -1,75 +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 java.io.Serializable;
-
-import org.apache.commons.geometry.core.Space;
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
-
-/**
- * This class implements a three-dimensional space.
- */
-public class Euclidean3D implements Serializable, Space {
-
-    /** Serializable version identifier. */
-    private static final long serialVersionUID = 6249091865814886817L;
-
-    /** Private constructor for the singleton.
-     */
-    private Euclidean3D() {
-    }
-
-    /** Get the unique instance.
-     * @return the unique instance
-     */
-    public static Euclidean3D getInstance() {
-        return LazyHolder.INSTANCE;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public int getDimension() {
-        return 3;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Euclidean2D getSubSpace() {
-        return Euclidean2D.getInstance();
-    }
-
-    // CHECKSTYLE: stop HideUtilityClassConstructor
-    /** Holder for the instance.
-     * <p>We use here the Initialization On Demand Holder Idiom.</p>
-     */
-    private static class LazyHolder {
-        /** Cached field instance. */
-        private static final Euclidean3D INSTANCE = new Euclidean3D();
-    }
-    // CHECKSTYLE: resume HideUtilityClassConstructor
-
-    /** Handle deserialization of the singleton.
-     * @return the singleton instance
-     */
-    private Object readResolve() {
-        // return the singleton instance
-        return LazyHolder.INSTANCE;
-    }
-
-}
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Line.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Line.java
index 2024032..68512a8 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Line.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Line.java
@@ -16,12 +16,9 @@
  */
 package org.apache.commons.geometry.euclidean.threed;
 
-import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Vector;
 import org.apache.commons.geometry.core.partitioning.Embedding;
-import org.apache.commons.geometry.euclidean.oned.Cartesian1D;
-import org.apache.commons.geometry.euclidean.oned.Euclidean1D;
 import org.apache.commons.geometry.euclidean.oned.IntervalsSet;
+import org.apache.commons.geometry.euclidean.oned.Point1D;
 import org.apache.commons.numbers.core.Precision;
 
 /** The class represent lines in a three dimensional space.
@@ -33,13 +30,13 @@ import org.apache.commons.numbers.core.Precision;
  * which is closest to the origin. Abscissa increases in the line
  * direction.</p>0
  */
-public class Line implements Embedding<Euclidean3D, Euclidean1D> {
+public class Line implements Embedding<Point3D, Point1D> {
 
     /** Line direction. */
-    private Cartesian3D direction;
+    private Vector3D direction;
 
     /** Line point closest to the origin. */
-    private Cartesian3D zero;
+    private Point3D zero;
 
     /** Tolerance below which points are considered identical. */
     private final double tolerance;
@@ -50,7 +47,7 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      * @param tolerance tolerance below which points are considered identical
      * @exception IllegalArgumentException if the points are equal
      */
-    public Line(final Cartesian3D p1, final Cartesian3D p2, final double tolerance)
+    public Line(final Point3D p1, final Point3D p2, final double tolerance)
         throws IllegalArgumentException {
         reset(p1, p2);
         this.tolerance = tolerance;
@@ -72,14 +69,14 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      * @param p2 second point belonging to the line (this can be any point, different from p1)
      * @exception IllegalArgumentException if the points are equal
      */
-    public void reset(final Cartesian3D p1, final Cartesian3D p2) throws IllegalStateException {
-        final Cartesian3D delta = p2.subtract(p1);
+    public void reset(final Point3D p1, final Point3D p2) throws IllegalArgumentException {
+        final Vector3D delta = p2.subtract(p1);
         final double norm2 = delta.getNormSq();
         if (norm2 == 0.0) {
             throw new IllegalArgumentException("Points are equal");
         }
-        this.direction = new Cartesian3D(1.0 / Math.sqrt(norm2), delta);
-        zero = new Cartesian3D(1.0, p1, -p1.dotProduct(delta) / norm2, delta);
+        this.direction = Vector3D.linearCombination(1.0 / Math.sqrt(norm2), delta);
+        this.zero = Point3D.vectorCombination(1.0, p1, -p1.asVector().dotProduct(delta) / norm2, delta);
     }
 
     /** Get the tolerance below which points are considered identical.
@@ -101,14 +98,14 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
     /** Get the normalized direction vector.
      * @return normalized direction vector
      */
-    public Cartesian3D getDirection() {
+    public Vector3D getDirection() {
         return direction;
     }
 
     /** Get the line point closest to the origin.
      * @return line point closest to the origin
      */
-    public Cartesian3D getOrigin() {
+    public Point3D getOrigin() {
         return zero;
     }
 
@@ -119,7 +116,7 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      * @param point point to check
      * @return abscissa of the point
      */
-    public double getAbscissa(final Cartesian3D point) {
+    public double getAbscissa(final Point3D point) {
         return point.subtract(zero).dotProduct(direction);
     }
 
@@ -127,42 +124,8 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      * @param abscissa desired abscissa for the point
      * @return one point belonging to the line, at specified abscissa
      */
-    public Cartesian3D pointAt(final double abscissa) {
-        return new Cartesian3D(1.0, zero, abscissa, direction);
-    }
-
-    /** Transform a space point into a sub-space point.
-     * @param vector n-dimension point of the space
-     * @return (n-1)-dimension point of the sub-space corresponding to
-     * the specified space point
-     */
-    public Cartesian1D toSubSpace(Vector<Euclidean3D> vector) {
-        return toSubSpace((Point<Euclidean3D>) vector);
-    }
-
-    /** Transform a sub-space point into a space point.
-     * @param vector (n-1)-dimension point of the sub-space
-     * @return n-dimension point of the space corresponding to the
-     * specified sub-space point
-     */
-    public Cartesian3D toSpace(Vector<Euclidean1D> vector) {
-        return toSpace((Point<Euclidean1D>) vector);
-    }
-
-    /** {@inheritDoc}
-     * @see #getAbscissa(Cartesian3D)
-     */
-    @Override
-    public Cartesian1D toSubSpace(final Point<Euclidean3D> point) {
-        return toSubSpace((Cartesian3D) point);
-    }
-
-    /** {@inheritDoc}
-     * @see #pointAt(double)
-     */
-    @Override
-    public Cartesian3D toSpace(final Point<Euclidean1D> point) {
-        return toSpace((Cartesian1D) point);
+    public Point3D pointAt(final double abscissa) {
+        return Point3D.vectorCombination(1.0, zero, abscissa, direction);
     }
 
     /** Transform a space point into a sub-space point.
@@ -170,8 +133,9 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      * @return (n-1)-dimension point of the sub-space corresponding to
      * the specified space point
      */
-    public Cartesian1D toSubSpace(final Cartesian3D point) {
-        return new Cartesian1D(getAbscissa(point));
+    @Override
+    public Point1D toSubSpace(final Point3D point) {
+        return new Point1D(getAbscissa(point));
     }
 
     /** Transform a sub-space point into a space point.
@@ -179,7 +143,8 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      * @return n-dimension point of the space corresponding to the
      * specified sub-space point
      */
-    public Cartesian3D toSpace(final Cartesian1D point) {
+    @Override
+    public Point3D toSpace(final Point1D point) {
         return pointAt(point.getX());
     }
 
@@ -191,7 +156,7 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      * @return true if the lines are similar
      */
     public boolean isSimilarTo(final Line line) {
-        final double angle = Cartesian3D.angle(direction, line.direction);
+        final double angle = direction.angle(line.direction);
         return ((angle < tolerance) || (angle > (Math.PI - tolerance))) && contains(line.zero);
     }
 
@@ -199,7 +164,7 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      * @param p point to check
      * @return true if p belongs to the line
      */
-    public boolean contains(final Cartesian3D p) {
+    public boolean contains(final Point3D p) {
         return distance(p) < tolerance;
     }
 
@@ -207,9 +172,9 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      * @param p to check
      * @return distance between the instance and the point
      */
-    public double distance(final Cartesian3D p) {
-        final Cartesian3D d = p.subtract(zero);
-        final Cartesian3D n = new Cartesian3D(1.0, d, -d.dotProduct(direction), direction);
+    public double distance(final Point3D p) {
+        final Vector3D d = p.subtract(zero);
+        final Vector3D n = Vector3D.linearCombination(1.0, d, -d.dotProduct(direction), direction);
         return n.getNorm();
     }
 
@@ -219,7 +184,7 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      */
     public double distance(final Line line) {
 
-        final Cartesian3D normal = Cartesian3D.crossProduct(direction, line.direction);
+        final Vector3D normal = direction.crossProduct(line.direction);
         final double n = normal.getNorm();
         if (n < Precision.SAFE_MIN) {
             // lines are parallel
@@ -237,7 +202,7 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      * @param line line to check against the instance
      * @return point of the instance closest to another line
      */
-    public Cartesian3D closestPoint(final Line line) {
+    public Point3D closestPoint(final Line line) {
 
         final double cos = direction.dotProduct(line.direction);
         final double n = 1 - cos * cos;
@@ -246,11 +211,11 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
             return zero;
         }
 
-        final Cartesian3D delta0 = line.zero.subtract(zero);
+        final Vector3D delta0 = line.zero.subtract(zero);
         final double a        = delta0.dotProduct(direction);
         final double b        = delta0.dotProduct(line.direction);
 
-        return new Cartesian3D(1, zero, (a - b * cos) / n, direction);
+        return Point3D.vectorCombination(1, zero, (a - b * cos) / n, direction);
 
     }
 
@@ -259,8 +224,8 @@ public class Line implements Embedding<Euclidean3D, Euclidean1D> {
      * @return intersection point of the instance and the other line
      * or null if there are no intersection points
      */
-    public Cartesian3D intersection(final Line line) {
-        final Cartesian3D closest = closestPoint(line);
+    public Point3D intersection(final Line line) {
+        final Point3D closest = closestPoint(line);
         return line.contains(closest) ? closest : null;
     }
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/OutlineExtractor.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/OutlineExtractor.java
index 3f25191..830d8bf 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/OutlineExtractor.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/OutlineExtractor.java
@@ -18,16 +18,14 @@ package org.apache.commons.geometry.euclidean.threed;
 
 import java.util.ArrayList;
 
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
-import org.apache.commons.geometry.euclidean.twod.PolygonsSet;
-import org.apache.commons.geometry.core.Point;
 import org.apache.commons.geometry.core.partitioning.AbstractSubHyperplane;
 import org.apache.commons.geometry.core.partitioning.BSPTree;
 import org.apache.commons.geometry.core.partitioning.BSPTreeVisitor;
 import org.apache.commons.geometry.core.partitioning.BoundaryAttribute;
 import org.apache.commons.geometry.core.partitioning.RegionFactory;
 import org.apache.commons.geometry.core.partitioning.SubHyperplane;
-import org.apache.commons.geometry.euclidean.twod.Cartesian2D;
+import org.apache.commons.geometry.euclidean.twod.Point2D;
+import org.apache.commons.geometry.euclidean.twod.PolygonsSet;
 
 /** Extractor for {@link PolygonsSet polyhedrons sets} outlines.
  * <p>This class extracts the 2D outlines from {{@link PolygonsSet
@@ -36,29 +34,29 @@ import org.apache.commons.geometry.euclidean.twod.Cartesian2D;
 public class OutlineExtractor {
 
     /** Abscissa axis of the projection plane. */
-    private final Cartesian3D u;
+    private final Vector3D u;
 
     /** Ordinate axis of the projection plane. */
-    private final Cartesian3D v;
+    private final Vector3D v;
 
     /** Normal of the projection plane (viewing direction). */
-    private final Cartesian3D w;
+    private final Vector3D w;
 
     /** Build an extractor for a specific projection plane.
      * @param u abscissa axis of the projection point
      * @param v ordinate axis of the projection point
      */
-    public OutlineExtractor(final Cartesian3D u, final Cartesian3D v) {
+    public OutlineExtractor(final Vector3D u, final Vector3D v) {
         this.u = u;
         this.v = v;
-        w = Cartesian3D.crossProduct(u, v);
+        this.w = u.crossProduct(v);
     }
 
     /** Extract the outline of a polyhedrons set.
      * @param polyhedronsSet polyhedrons set whose outline must be extracted
      * @return an outline, as an array of loops.
      */
-    public Cartesian2D[][] getOutline(final PolyhedronsSet polyhedronsSet) {
+    public Point2D[][] getOutline(final PolyhedronsSet polyhedronsSet) {
 
         // project all boundary facets into one polygons set
         final BoundaryProjector projector = new BoundaryProjector(polyhedronsSet.getTolerance());
@@ -66,9 +64,9 @@ public class OutlineExtractor {
         final PolygonsSet projected = projector.getProjected();
 
         // Remove the spurious intermediate vertices from the outline
-        final Cartesian2D[][] outline = projected.getVertices();
+        final Point2D[][] outline = projected.getVertices();
         for (int i = 0; i < outline.length; ++i) {
-            final Cartesian2D[] rawLoop = outline[i];
+            final Point2D[] rawLoop = outline[i];
             int end = rawLoop.length;
             int j = 0;
             while (j < end) {
@@ -85,7 +83,7 @@ public class OutlineExtractor {
             }
             if (end != rawLoop.length) {
                 // resize the array
-                outline[i] = new Cartesian2D[end];
+                outline[i] = new Point2D[end];
                 System.arraycopy(rawLoop, 0, outline[i], 0, end);
             }
         }
@@ -102,10 +100,10 @@ public class OutlineExtractor {
      * @param i index of the point to check (must be between 0 and n-1)
      * @return true if the point is exactly between its neighbors
      */
-    private boolean pointIsBetween(final Cartesian2D[] loop, final int n, final int i) {
-        final Cartesian2D previous = loop[(i + n - 1) % n];
-        final Cartesian2D current  = loop[i];
-        final Cartesian2D next     = loop[(i + 1) % n];
+    private boolean pointIsBetween(final Point2D[] loop, final int n, final int i) {
+        final Point2D previous = loop[(i + n - 1) % n];
+        final Point2D current  = loop[i];
+        final Point2D next     = loop[(i + 1) % n];
         final double dx1       = current.getX() - previous.getX();
         final double dy1       = current.getY() - previous.getY();
         final double dx2       = next.getX()    - current.getX();
@@ -117,7 +115,7 @@ public class OutlineExtractor {
     }
 
     /** Visitor projecting the boundary facets on a plane. */
-    private class BoundaryProjector implements BSPTreeVisitor<Euclidean3D> {
+    private class BoundaryProjector implements BSPTreeVisitor<Point3D> {
 
         /** Projection of the polyhedrons set on the plane. */
         private PolygonsSet projected;
@@ -129,22 +127,22 @@ public class OutlineExtractor {
          * @param tolerance tolerance below which points are considered identical
          */
         BoundaryProjector(final double tolerance) {
-            this.projected = new PolygonsSet(new BSPTree<Euclidean2D>(Boolean.FALSE), tolerance);
+            this.projected = new PolygonsSet(new BSPTree<Point2D>(Boolean.FALSE), tolerance);
             this.tolerance = tolerance;
         }
 
         /** {@inheritDoc} */
         @Override
-        public Order visitOrder(final BSPTree<Euclidean3D> node) {
+        public Order visitOrder(final BSPTree<Point3D> node) {
             return Order.MINUS_SUB_PLUS;
         }
 
         /** {@inheritDoc} */
         @Override
-        public void visitInternalNode(final BSPTree<Euclidean3D> node) {
+        public void visitInternalNode(final BSPTree<Point3D> node) {
             @SuppressWarnings("unchecked")
-            final BoundaryAttribute<Euclidean3D> attribute =
-                (BoundaryAttribute<Euclidean3D>) node.getAttribute();
+            final BoundaryAttribute<Point3D> attribute =
+                (BoundaryAttribute<Point3D>) node.getAttribute();
             if (attribute.getPlusOutside() != null) {
                 addContribution(attribute.getPlusOutside(), false);
             }
@@ -155,33 +153,33 @@ public class OutlineExtractor {
 
         /** {@inheritDoc} */
         @Override
-        public void visitLeafNode(final BSPTree<Euclidean3D> node) {
+        public void visitLeafNode(final BSPTree<Point3D> node) {
         }
 
         /** Add he contribution of a boundary facet.
          * @param facet boundary facet
          * @param reversed if true, the facet has the inside on its plus side
          */
-        private void addContribution(final SubHyperplane<Euclidean3D> facet, final boolean reversed) {
+        private void addContribution(final SubHyperplane<Point3D> facet, final boolean reversed) {
 
             // extract the vertices of the facet
             @SuppressWarnings("unchecked")
-            final AbstractSubHyperplane<Euclidean3D, Euclidean2D> absFacet =
-                (AbstractSubHyperplane<Euclidean3D, Euclidean2D>) facet;
+            final AbstractSubHyperplane<Point3D, Point2D> absFacet =
+                (AbstractSubHyperplane<Point3D, Point2D>) facet;
             final Plane plane    = (Plane) facet.getHyperplane();
 
             final double scal = plane.getNormal().dotProduct(w);
             if (Math.abs(scal) > 1.0e-3) {
-                Cartesian2D[][] vertices =
+                Point2D[][] vertices =
                     ((PolygonsSet) absFacet.getRemainingRegion()).getVertices();
 
                 if ((scal < 0) ^ reversed) {
                     // the facet is seen from the inside,
                     // we need to invert its boundary orientation
-                    final Cartesian2D[][] newVertices = new Cartesian2D[vertices.length][];
+                    final Point2D[][] newVertices = new Point2D[vertices.length][];
                     for (int i = 0; i < vertices.length; ++i) {
-                        final Cartesian2D[] loop = vertices[i];
-                        final Cartesian2D[] newLoop = new Cartesian2D[loop.length];
+                        final Point2D[] loop = vertices[i];
+                        final Point2D[] newLoop = new Point2D[loop.length];
                         if (loop[0] == null) {
                             newLoop[0] = null;
                             for (int j = 1; j < loop.length; ++j) {
@@ -201,22 +199,22 @@ public class OutlineExtractor {
                 }
 
                 // compute the projection of the facet in the outline plane
-                final ArrayList<SubHyperplane<Euclidean2D>> edges = new ArrayList<>();
-                for (Cartesian2D[] loop : vertices) {
+                final ArrayList<SubHyperplane<Point2D>> edges = new ArrayList<>();
+                for (Point2D[] loop : vertices) {
                     final boolean closed = loop[0] != null;
                     int previous         = closed ? (loop.length - 1) : 1;
-                    Cartesian3D previous3D  = plane.toSpace(loop[previous]);
+                    Vector3D previous3D  = plane.toSpace(loop[previous]).asVector();
                     int current          = (previous + 1) % loop.length;
-                    Cartesian2D pPoint       = new Cartesian2D(previous3D.dotProduct(u),
+                    Point2D pPoint       = new Point2D(previous3D.dotProduct(u),
                                                          previous3D.dotProduct(v));
                     while (current < loop.length) {
 
-                        final Cartesian3D current3D = plane.toSpace((Point<Euclidean2D>) loop[current]);
-                        final Cartesian2D  cPoint    = new Cartesian2D(current3D.dotProduct(u),
+                        final Vector3D current3D = plane.toSpace(loop[current]).asVector();
+                        final Point2D  cPoint    = new Point2D(current3D.dotProduct(u),
                                                                  current3D.dotProduct(v));
                         final org.apache.commons.geometry.euclidean.twod.Line line =
                             new org.apache.commons.geometry.euclidean.twod.Line(pPoint, cPoint, tolerance);
-                        SubHyperplane<Euclidean2D> edge = line.wholeHyperplane();
+                        SubHyperplane<Point2D> edge = line.wholeHyperplane();
 
                         if (closed || (previous != 1)) {
                             // the previous point is a real vertex
@@ -247,7 +245,7 @@ public class OutlineExtractor {
                 final PolygonsSet projectedFacet = new PolygonsSet(edges, tolerance);
 
                 // add the contribution of the facet to the global outline
-                projected = (PolygonsSet) new RegionFactory<Euclidean2D>().union(projected, projectedFacet);
+                projected = (PolygonsSet) new RegionFactory<Point2D>().union(projected, projectedFacet);
 
             }
         }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Plane.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Plane.java
index 8a54620..5ac48dc 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Plane.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Plane.java
@@ -16,33 +16,30 @@
  */
 package org.apache.commons.geometry.euclidean.threed;
 
-import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Vector;
 import org.apache.commons.geometry.core.partitioning.Embedding;
 import org.apache.commons.geometry.core.partitioning.Hyperplane;
-import org.apache.commons.geometry.euclidean.oned.Cartesian1D;
-import org.apache.commons.geometry.euclidean.twod.Cartesian2D;
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.geometry.euclidean.oned.Point1D;
+import org.apache.commons.geometry.euclidean.twod.Point2D;
 import org.apache.commons.geometry.euclidean.twod.PolygonsSet;
 
 /** The class represent planes in a three dimensional space.
  */
-public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Euclidean2D> {
+public class Plane implements Hyperplane<Point3D>, Embedding<Point3D, Point2D> {
 
     /** Offset of the origin with respect to the plane. */
     private double originOffset;
 
     /** Origin of the plane frame. */
-    private Cartesian3D origin;
+    private Point3D origin;
 
     /** First vector of the plane frame (in plane). */
-    private Cartesian3D u;
+    private Vector3D u;
 
     /** Second vector of the plane frame (in plane). */
-    private Cartesian3D v;
+    private Vector3D v;
 
     /** Third vector of the plane frame (plane normal). */
-    private Cartesian3D w;
+    private Vector3D w;
 
     /** Tolerance below which points are considered identical. */
     private final double tolerance;
@@ -52,7 +49,7 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @param tolerance tolerance below which points are considered identical
      * @exception IllegalArgumentException if the normal norm is too small
      */
-    public Plane(final Cartesian3D normal, final double tolerance)
+    public Plane(final Vector3D normal, final double tolerance)
         throws IllegalArgumentException {
         setNormal(normal);
         this.tolerance = tolerance;
@@ -66,11 +63,11 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @param tolerance tolerance below which points are considered identical
      * @exception IllegalArgumentException if the normal norm is too small
      */
-    public Plane(final Cartesian3D p, final Cartesian3D normal, final double tolerance)
+    public Plane(final Point3D p, final Vector3D normal, final double tolerance)
         throws IllegalArgumentException {
         setNormal(normal);
         this.tolerance = tolerance;
-        originOffset = -p.dotProduct(w);
+        this.originOffset = -p.asVector().dotProduct(w);
         setFrame();
     }
 
@@ -83,7 +80,7 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @param tolerance tolerance below which points are considered identical
      * @exception IllegalArgumentException if the points do not constitute a plane
      */
-    public Plane(final Cartesian3D p1, final Cartesian3D p2, final Cartesian3D p3, final double tolerance)
+    public Plane(final Point3D p1, final Point3D p2, final Point3D p3, final double tolerance)
         throws IllegalArgumentException {
         this(p1, p2.subtract(p1).crossProduct(p3.subtract(p1)), tolerance);
     }
@@ -119,9 +116,9 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @param normal normal direction to the plane
      * @exception IllegalArgumentException if the normal norm is too small
      */
-    public void reset(final Cartesian3D p, final Cartesian3D normal) throws IllegalArgumentException {
+    public void reset(final Point3D p, final Vector3D normal) throws IllegalArgumentException {
         setNormal(normal);
-        originOffset = -p.dotProduct(w);
+        originOffset = -p.asVector().dotProduct(w);
         setFrame();
     }
 
@@ -143,20 +140,20 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @param normal normal direction to the plane (will be copied)
      * @exception IllegalArgumentException if the normal norm is too close to zero
      */
-    private void setNormal(final Cartesian3D normal) throws IllegalArgumentException {
+    private void setNormal(final Vector3D normal) throws IllegalArgumentException {
         final double norm = normal.getNorm();
         if (norm < 1.0e-10) {
             throw new IllegalArgumentException("Norm is zero");
         }
-        w = new Cartesian3D(1.0 / norm, normal);
+        w = Vector3D.linearCombination(1.0 / norm, normal);
     }
 
     /** Reset the plane frame.
      */
     private void setFrame() {
-        origin = new Cartesian3D(-originOffset, w);
+        origin = Vector3D.linearCombination(-originOffset, w).asPoint();
         u = w.orthogonal();
-        v = Cartesian3D.crossProduct(w, u);
+        v = w.crossProduct(u);
     }
 
     /** Get the origin point of the plane frame.
@@ -165,7 +162,7 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @return the origin point of the plane frame (point closest to the
      * 3D-space origin)
      */
-    public Cartesian3D getOrigin() {
+    public Point3D getOrigin() {
         return origin;
     }
 
@@ -177,7 +174,7 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @see #getU
      * @see #getV
      */
-    public Cartesian3D getNormal() {
+    public Vector3D getNormal() {
         return w;
     }
 
@@ -189,7 +186,7 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @see #getV
      * @see #getNormal
      */
-    public Cartesian3D getU() {
+    public Vector3D getU() {
         return u;
     }
 
@@ -201,13 +198,13 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @see #getU
      * @see #getNormal
      */
-    public Cartesian3D getV() {
+    public Vector3D getV() {
         return v;
     }
 
     /** {@inheritDoc} */
     @Override
-    public Point<Euclidean3D> project(Point<Euclidean3D> point) {
+    public Point3D project(Point3D point) {
         return toSpace(toSubSpace(point));
     }
 
@@ -229,67 +226,32 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * reversed.</p>
      */
     public void revertSelf() {
-        final Cartesian3D tmp = u;
+        final Vector3D tmp = u;
         u = v;
         v = tmp;
         w = w.negate();
         originOffset = -originOffset;
     }
 
-    /** Transform a space vector into a sub-space vector.
-     * @param vector n-dimension vector of the space
-     * @return (n-1)-dimension vector of the sub-space corresponding to
-     * the specified space vector
-     */
-    public Cartesian2D toSubSpace(Vector<Euclidean3D> vector) {
-        return toSubSpace((Cartesian3D) vector);
-    }
-
-    /** Transform a sub-space point into a space point.
-     * @param vector (n-1)-dimension point of the sub-space
-     * @return n-dimension point of the space corresponding to the
-     * specified sub-space point
-     */
-    public Cartesian3D toSpace(Vector<Euclidean2D> vector) {
-        return toSpace((Cartesian2D) vector);
-    }
-
     /** Transform a 3D space point into an in-plane point.
-     * @param point point of the space (must be a {@link Cartesian3D} instance)
+     * @param point point of the space (must be a {@link Point3D} instance)
      * @return in-plane point
      * @see #toSpace
      */
     @Override
-    public Cartesian2D toSubSpace(final Point<Euclidean3D> point) {
-        return toSubSpace((Cartesian3D) point);
+    public Point2D toSubSpace(final Point3D point) {
+        Vector3D vec = point.asVector();
+        return new Point2D(vec.dotProduct(u), vec.dotProduct(v));
     }
 
     /** Transform an in-plane point into a 3D space point.
-     * @param point in-plane point (must be a {@link Cartesian2D} instance)
+     * @param point in-plane point (must be a {@link Point2D} instance)
      * @return 3D space point
      * @see #toSubSpace
      */
     @Override
-    public Cartesian3D toSpace(final Point<Euclidean2D> point) {
-        return toSpace((Cartesian2D) point);
-    }
-
-    /** Transform a 3D space point into an in-plane point.
-     * @param point point of the space
-     * @return in-plane point
-     * @see #toSpace
-     */
-    public Cartesian2D toSubSpace(final Cartesian3D point) {
-        return new Cartesian2D(point.dotProduct(u), point.dotProduct(v));
-    }
-
-    /** Transform an in-plane point into a 3D space point.
-     * @param point in-plane point
-     * @return 3D space point
-     * @see #toSubSpace
-     */
-    public Cartesian3D toSpace(final Cartesian2D point) {
-        return new Cartesian3D(point.getX(), u, point.getY(), v, -originOffset, w);
+    public Point3D toSpace(final Point2D point) {
+        return Point3D.vectorCombination(point.getX(), u, point.getY(), v, -originOffset, w);
     }
 
     /** Get one point from the 3D-space.
@@ -299,8 +261,8 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @return one point in the 3D-space, with given coordinates and offset
      * relative to the plane
      */
-    public Cartesian3D getPointAt(final Cartesian2D inPlane, final double offset) {
-        return new Cartesian3D(inPlane.getX(), u, inPlane.getY(), v, offset - originOffset, w);
+    public Point3D getPointAt(final Point2D inPlane, final double offset) {
+        return Point3D.vectorCombination(inPlane.getX(), u, inPlane.getY(), v, offset - originOffset, w);
     }
 
     /** Check if the instance is similar to another plane.
@@ -311,7 +273,7 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @return true if the planes are similar
      */
     public boolean isSimilarTo(final Plane plane) {
-        final double angle = Cartesian3D.angle(w, plane.w);
+        final double angle = w.angle(plane.w);
         return ((angle < 1.0e-10) && (Math.abs(originOffset - plane.originOffset) < tolerance)) ||
                ((angle > (Math.PI - 1.0e-10)) && (Math.abs(originOffset + plane.originOffset) < tolerance));
     }
@@ -322,9 +284,9 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @param rotation vectorial rotation operator
      * @return a new plane
      */
-    public Plane rotate(final Cartesian3D center, final Rotation rotation) {
+    public Plane rotate(final Point3D center, final Rotation rotation) {
 
-        final Cartesian3D delta = origin.subtract(center);
+        final Vector3D delta = origin.subtract(center);
         final Plane plane = new Plane(center.add(rotation.applyTo(delta)),
                                       rotation.applyTo(w), tolerance);
 
@@ -341,7 +303,7 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @param translation translation to apply
      * @return a new plane
      */
-    public Plane translate(final Cartesian3D translation) {
+    public Plane translate(final Vector3D translation) {
 
         final Plane plane = new Plane(origin.add(translation), w, tolerance);
 
@@ -358,15 +320,15 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @return intersection point between between the line and the
      * instance (null if the line is parallel to the instance)
      */
-    public Cartesian3D intersection(final Line line) {
-        final Cartesian3D direction = line.getDirection();
+    public Point3D intersection(final Line line) {
+        final Vector3D direction = line.getDirection();
         final double   dot       = w.dotProduct(direction);
         if (Math.abs(dot) < 1.0e-10) {
             return null;
         }
-        final Cartesian3D point = line.toSpace(Cartesian1D.ZERO);
-        final double   k     = -(originOffset + w.dotProduct(point)) / dot;
-        return new Cartesian3D(1.0, point, k, direction);
+        final Point3D point = line.toSpace(Point1D.ZERO);
+        final double   k     = -(originOffset + w.dotProduct(point.asVector())) / dot;
+        return Point3D.vectorCombination(1.0, point, k, direction);
     }
 
     /** Build the line shared by the instance and another plane.
@@ -375,11 +337,11 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * other plane (really a {@link Line Line} instance)
      */
     public Line intersection(final Plane other) {
-        final Cartesian3D direction = Cartesian3D.crossProduct(w, other.w);
+        final Vector3D direction = w.crossProduct(other.w);
         if (direction.getNorm() < tolerance) {
             return null;
         }
-        final Cartesian3D point = intersection(this, other, new Plane(direction, tolerance));
+        final Point3D point = intersection(this, other, new Plane(direction, tolerance));
         return new Line(point, point.add(direction), tolerance);
     }
 
@@ -389,7 +351,7 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @param plane3 third plane2
      * @return intersection point of three planes, null if some planes are parallel
      */
-    public static Cartesian3D intersection(final Plane plane1, final Plane plane2, final Plane plane3) {
+    public static Point3D intersection(final Plane plane1, final Plane plane2, final Plane plane3) {
 
         // coefficients of the three planes linear equations
         final double a1 = plane1.w.getX();
@@ -418,7 +380,7 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
         }
 
         final double r = 1.0 / determinant;
-        return new Cartesian3D(
+        return new Point3D(
                             (-a23 * d1 - (c1 * b3 - c3 * b1) * d2 - (c2 * b1 - c1 * b2) * d3) * r,
                             (-b23 * d1 - (c3 * a1 - c1 * a3) * d2 - (c1 * a2 - c2 * a1) * d3) * r,
                             (-c23 * d1 - (b1 * a3 - b3 * a1) * d2 - (b2 * a1 - b1 * a2) * d3) * r);
@@ -446,7 +408,7 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @param p point to check
      * @return true if p belongs to the plane
      */
-    public boolean contains(final Cartesian3D p) {
+    public boolean contains(final Point3D p) {
         return Math.abs(getOffset(p)) < tolerance;
     }
 
@@ -464,14 +426,6 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
         return originOffset + (sameOrientationAs(plane) ? -plane.originOffset : plane.originOffset);
     }
 
-    /** Get the offset (oriented distance) of a vector.
-     * @param vector vector to check
-     * @return offset of the vector
-     */
-//    public double getOffset(Vector<Euclidean3D> vector) {
-//        return getOffset((Point<Euclidean3D>) vector);
-//    }
-
     /** Get the offset (oriented distance) of a point.
      * <p>The offset is 0 if the point is on the underlying hyperplane,
      * it is positive if the point is on one particular side of the
@@ -481,8 +435,8 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * @return offset of the point
      */
     @Override
-    public double getOffset(final Point<Euclidean3D> point) {
-        return ((Cartesian3D) point).dotProduct(w) + originOffset;
+    public double getOffset(final Point3D point) {
+        return point.asVector().dotProduct(w) + originOffset;
     }
 
     /** Check if the instance has the same orientation as another hyperplane.
@@ -491,7 +445,7 @@ public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Eu
      * the same orientation
      */
     @Override
-    public boolean sameOrientationAs(final Hyperplane<Euclidean3D> other) {
+    public boolean sameOrientationAs(final Hyperplane<Point3D> other) {
         return (((Plane) other).w).dotProduct(w) > 0.0;
     }
 
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
new file mode 100644
index 0000000..a91f03e
--- /dev/null
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
@@ -0,0 +1,291 @@
+/*
+ * 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.EuclideanPoint;
+import org.apache.commons.numbers.arrays.LinearCombination;
+
+/** This class represents a point in three-dimensional Euclidean space.
+ * Instances of this class are guaranteed to be immutable.
+ */
+public final class Point3D extends Cartesian3D implements EuclideanPoint<Point3D, Vector3D> {
+
+    /** Zero point (coordinates: 0, 0, 0). */
+    public static final Point3D ZERO   = new Point3D(0, 0, 0);
+
+    // CHECKSTYLE: stop ConstantName
+    /** A point with all coordinates set to NaN. */
+    public static final Point3D NaN = new Point3D(Double.NaN, Double.NaN, Double.NaN);
+    // CHECKSTYLE: resume ConstantName
+
+    /** A point with all coordinates set to positive infinity. */
+    public static final Point3D POSITIVE_INFINITY =
+        new Point3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+
+    /** A point with all coordinates set to negative infinity. */
+    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 = 1313493323784566947L;
+
+    /** Simple constructor.
+     * Build a point from its coordinates
+     * @param x abscissa
+     * @param y ordinate
+     * @param z height
+     */
+    public Point3D(double x, double y, double z) {
+        super(x, y, z);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector3D asVector() {
+        return Vector3D.of(x, y, z);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distance(Point3D p) {
+        return euclideanDistance(p);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector3D subtract(Point3D p) {
+        return new Vector3D(
+                    x - p.x,
+                    y - p.y,
+                    z - p.z
+                );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector3D vectorTo(Point3D p) {
+        return p.subtract(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Point3D add(Vector3D v) {
+        return new Point3D(
+                    x + v.x,
+                    y + v.y,
+                    z + v.z
+                );
+    }
+
+    /**
+     * Get a hashCode for the point.
+     * <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 642;
+        }
+        return 643 * (164 * Double.hashCode(x) +  3 * Double.hashCode(y) +  Double.hashCode(z));
+    }
+
+    /** Test for the equality of two points.
+     * <p>
+     * If all coordinates of two points are exactly the same, and none are
+     * <code>Double.NaN</code>, the two points are considered to be equal.
+     * </p>
+     * <p>
+     * <code>NaN</code> coordinates are considered to globally affect the point
+     * and be equal to each other - i.e, if either (or all) coordinates of the
+     * point are equal to <code>Double.NaN</code>, the point is equal to
+     * {@link #NaN}.
+     * </p>
+     *
+     * @param other Object to test for equality to this
+     * @return true if two Point3D objects are equal, false if
+     *         object is null, not an instance of Point3D, or
+     *         not equal to this Point3D instance
+     *
+     */
+    @Override
+    public boolean equals(Object other) {
+
+        if (this == other) {
+            return true;
+        }
+
+        if (other instanceof Point3D) {
+            final Point3D rhs = (Point3D) other;
+            if (rhs.isNaN()) {
+                return this.isNaN();
+            }
+
+            return (x == rhs.x) && (y == rhs.y) && (z == rhs.z);
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "(" + x + "; " + y + "; " + z + ")";
+    }
+
+    /** Returns a point with the given coordinate values
+     * @param x abscissa (first coordinate value)
+     * @param y ordinate (second coordinate value)
+     * @param z height (third coordinate value)
+     * @return point instance
+     */
+    public static Point3D of(double x, double y, double z) {
+        return new Point3D(x, y, z);
+    }
+
+    /** Returns a point with the given coordinates.
+     * @param value coordinate values
+     * @return point instance
+     */
+    public static Point3D of(Cartesian3D value) {
+        return new Point3D(value.x, value.y, value.z);
+    }
+
+    /** Creates a point from the coordinates in the given 3-element array.
+     * @param p coordinates array
+     * @return new point
+     * @exception IllegalArgumentException if the array does not have 3 elements
+     */
+    public static Point3D of(double[] p) {
+        if (p.length != 3) {
+            throw new IllegalArgumentException("Dimension mismatch: " + p.length + " != 3");
+        }
+        return new Point3D(p[0], p[1], p[2]);
+    }
+
+    /** Returns a point with coordinates calculated by multiplying each input coordinate
+     * with its corresponding factor and adding the results.
+     *
+     * <p>This is equivalent
+     * to converting all input coordinates to vectors, scaling and adding the
+     * vectors (a linear combination), and adding the result to the zero point.
+     * This method, however, does not create any intermediate objects.
+     * </p>
+     * <p>
+     * The name of this method was chosen to emphasize the fact that the operation
+     * should be viewed as occurring in vector space, since addition and scalar
+     * multiplication are not defined directly for points.
+     * </p>
+     *
+     * @param a scale factor for first coordinate
+     * @param c first coordinate
+     * @return point with coordinates calculated by {@code a * c}
+     */
+    public static Point3D vectorCombination(double a, Cartesian3D c) {
+        return new Point3D(a * c.x, a * c.y, a * c.z);
+    }
+
+    /** Returns a point with coordinates calculated by multiplying each input coordinate
+     * with its corresponding factor and adding the results.
+     *
+     * <p>This is equivalent
+     * to converting all input coordinates to vectors, scaling and adding the
+     * vectors (a linear combination), and adding the result to the zero point.
+     * This method, however, does not create any intermediate objects.
+     * </p>
+     * <p>
+     * The name of this method was chosen to emphasize the fact that the operation
+     * should be viewed as occurring in vector space, since addition and scalar
+     * multiplication are not defined directly for points.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @return point with coordinates calculated by {@code (a1 * c1) + (a2 * c2)}
+     */
+    public static Point3D vectorCombination(double a1, Cartesian3D c1, double a2, Cartesian3D c2) {
+        return new Point3D(
+                LinearCombination.value(a1, c1.x, a2, c2.x),
+                LinearCombination.value(a1, c1.y, a2, c2.y),
+                LinearCombination.value(a1, c1.z, a2, c2.z));
+    }
+
+    /** Returns a point with coordinates calculated by multiplying each input coordinate
+     * with its corresponding factor and adding the results.
+     *
+     * <p>This is equivalent
+     * to converting all input coordinates to vectors, scaling and adding the
+     * vectors (a linear combination), and adding the result to the zero point.
+     * This method, however, does not create any intermediate objects.
+     * </p>
+     * <p>
+     * The name of this method was chosen to emphasize the fact that the operation
+     * should be viewed as occurring in vector space, since addition and scalar
+     * multiplication are not defined directly for points.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @param a3 scale factor for third coordinate
+     * @param c3 third coordinate
+     * @return point with coordinates calculated by {@code (a1 * c1) + (a2 * c2) + (a3 * c3)}
+     */
+    public static Point3D vectorCombination(double a1, Cartesian3D c1, double a2, Cartesian3D c2,
+            double a3, Cartesian3D c3) {
+        return new Point3D(
+                LinearCombination.value(a1, c1.x, a2, c2.x, a3, c3.x),
+                LinearCombination.value(a1, c1.y, a2, c2.y, a3, c3.y),
+                LinearCombination.value(a1, c1.z, a2, c2.z, a3, c3.z));
+    }
+
+    /** Returns a point with coordinates calculated by multiplying each input coordinate
+     * with its corresponding factor and adding the results.
+     *
+     * <p>This is equivalent
+     * to converting all input coordinates to vectors, scaling and adding the
+     * vectors (a linear combination), and adding the result to the zero point.
+     * This method, however, does not create any intermediate objects.
+     * </p>
+     * <p>
+     * The name of this method was chosen to emphasize the fact that the operation
+     * should be viewed as occurring in vector space, since addition and scalar
+     * multiplication are not defined directly for points.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @param a3 scale factor for third coordinate
+     * @param c3 third coordinate
+     * @param a4 scale factor for fourth coordinate
+     * @param c4 fourth coordinate
+     * @return point with coordinates calculated by {@code (a1 * c1) + (a2 * c2) + (a3 * c3) + (a4 * c4)}
+     */
+    public static Point3D vectorCombination(double a1, Cartesian3D c1, double a2, Cartesian3D c2,
+            double a3, Cartesian3D c3, double a4, Cartesian3D c4) {
+        return new Point3D(
+                LinearCombination.value(a1, c1.x, a2, c2.x, a3, c3.x, a4, c4.x),
+                LinearCombination.value(a1, c1.y, a2, c2.y, a3, c3.y, a4, c4.y),
+                LinearCombination.value(a1, c1.z, a2, c2.z, a3, c3.z, a4, c4.z));
+    }
+}
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSet.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSet.java
index 6cb2771..d70efba 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSet.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSet.java
@@ -31,15 +31,14 @@ import org.apache.commons.geometry.core.partitioning.Region;
 import org.apache.commons.geometry.core.partitioning.RegionFactory;
 import org.apache.commons.geometry.core.partitioning.SubHyperplane;
 import org.apache.commons.geometry.core.partitioning.Transform;
-import org.apache.commons.geometry.euclidean.oned.Euclidean1D;
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.geometry.euclidean.oned.Point1D;
+import org.apache.commons.geometry.euclidean.twod.Point2D;
 import org.apache.commons.geometry.euclidean.twod.PolygonsSet;
 import org.apache.commons.geometry.euclidean.twod.SubLine;
-import org.apache.commons.geometry.euclidean.twod.Cartesian2D;
 
 /** This class represents a 3D region: a set of polyhedrons.
  */
-public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
+public class PolyhedronsSet extends AbstractRegion<Point3D, Point2D> {
 
     /** Build a polyhedrons set representing the whole real line.
      * @param tolerance tolerance below which points are considered identical
@@ -68,7 +67,7 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * @param tree inside/outside BSP tree representing the region
      * @param tolerance tolerance below which points are considered identical
      */
-    public PolyhedronsSet(final BSPTree<Euclidean3D> tree, final double tolerance) {
+    public PolyhedronsSet(final BSPTree<Point3D> tree, final double tolerance) {
         super(tree, tolerance);
     }
 
@@ -92,7 +91,7 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * collection of {@link SubHyperplane SubHyperplane} objects
      * @param tolerance tolerance below which points are considered identical
      */
-    public PolyhedronsSet(final Collection<SubHyperplane<Euclidean3D>> boundary,
+    public PolyhedronsSet(final Collection<SubHyperplane<Point3D>> boundary,
                           final double tolerance) {
         super(boundary, tolerance);
     }
@@ -114,7 +113,7 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * @param tolerance tolerance below which points are considered identical
      * @exception IllegalArgumentException if some basic sanity checks fail
      */
-    public PolyhedronsSet(final List<Cartesian3D> vertices, final List<int[]> facets,
+    public PolyhedronsSet(final List<Point3D> vertices, final List<int[]> facets,
                           final double tolerance) {
         super(buildBoundary(vertices, facets, tolerance), tolerance);
     }
@@ -145,7 +144,7 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * @param tolerance tolerance below which points are considered identical
      * @return boundary tree
      */
-    private static BSPTree<Euclidean3D> buildBoundary(final double xMin, final double xMax,
+    private static BSPTree<Point3D> buildBoundary(final double xMin, final double xMax,
                                                       final double yMin, final double yMax,
                                                       final double zMin, final double zMax,
                                                       final double tolerance) {
@@ -153,14 +152,14 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
             // too thin box, build an empty polygons set
             return new BSPTree<>(Boolean.FALSE);
         }
-        final Plane pxMin = new Plane(new Cartesian3D(xMin, 0,    0),   Cartesian3D.MINUS_I, tolerance);
-        final Plane pxMax = new Plane(new Cartesian3D(xMax, 0,    0),   Cartesian3D.PLUS_I,  tolerance);
-        final Plane pyMin = new Plane(new Cartesian3D(0,    yMin, 0),   Cartesian3D.MINUS_J, tolerance);
-        final Plane pyMax = new Plane(new Cartesian3D(0,    yMax, 0),   Cartesian3D.PLUS_J,  tolerance);
-        final Plane pzMin = new Plane(new Cartesian3D(0,    0,   zMin), Cartesian3D.MINUS_K, tolerance);
-        final Plane pzMax = new Plane(new Cartesian3D(0,    0,   zMax), Cartesian3D.PLUS_K,  tolerance);
-        final Region<Euclidean3D> boundary =
-        new RegionFactory<Euclidean3D>().buildConvex(pxMin, pxMax, pyMin, pyMax, pzMin, pzMax);
+        final Plane pxMin = new Plane(new Point3D(xMin, 0,    0),   Vector3D.MINUS_X, tolerance);
+        final Plane pxMax = new Plane(new Point3D(xMax, 0,    0),   Vector3D.PLUS_X,  tolerance);
+        final Plane pyMin = new Plane(new Point3D(0,    yMin, 0),   Vector3D.MINUS_Y, tolerance);
+        final Plane pyMax = new Plane(new Point3D(0,    yMax, 0),   Vector3D.PLUS_Y,  tolerance);
+        final Plane pzMin = new Plane(new Point3D(0,    0,   zMin), Vector3D.MINUS_Z, tolerance);
+        final Plane pzMax = new Plane(new Point3D(0,    0,   zMax), Vector3D.PLUS_Z,  tolerance);
+        final Region<Point3D> boundary =
+        new RegionFactory<Point3D>().buildConvex(pxMin, pxMax, pyMin, pyMax, pzMin, pzMax);
         return boundary.getTree(false);
     }
 
@@ -171,15 +170,15 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * @return boundary as a list of sub-hyperplanes
      * @exception IllegalArgumentException if some basic sanity checks fail
      */
-    private static List<SubHyperplane<Euclidean3D>> buildBoundary(final List<Cartesian3D> vertices,
+    private static List<SubHyperplane<Point3D>> buildBoundary(final List<Point3D> vertices,
                                                                   final List<int[]> facets,
                                                                   final double tolerance) {
 
         // check vertices distances
         for (int i = 0; i < vertices.size() - 1; ++i) {
-            final Cartesian3D vi = vertices.get(i);
+            final Point3D vi = vertices.get(i);
             for (int j = i + 1; j < vertices.size(); ++j) {
-                if (Cartesian3D.distance(vi, vertices.get(j)) <= tolerance) {
+                if (vi.distance(vertices.get(j)) <= tolerance) {
                     throw new IllegalArgumentException("Vertices are too close near point " + vi);
                 }
             }
@@ -203,15 +202,15 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
                         found = found || (v == vA);
                     }
                     if (!found) {
-                        final Cartesian3D start = vertices.get(vA);
-                        final Cartesian3D end   = vertices.get(vB);
+                        final Point3D start = vertices.get(vA);
+                        final Point3D end   = vertices.get(vB);
                         throw new IllegalArgumentException("Edge joining points " + start + " and " + end + " is connected to one facet only");
                     }
                 }
             }
         }
 
-        final List<SubHyperplane<Euclidean3D>> boundary = new ArrayList<>();
+        final List<SubHyperplane<Point3D>> boundary = new ArrayList<>();
 
         for (final int[] facet : facets) {
 
@@ -220,9 +219,9 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
                                     tolerance);
 
             // check all points are in the plane
-            final Cartesian2D[] two2Points = new Cartesian2D[facet.length];
+            final Point2D[] two2Points = new Point2D[facet.length];
             for (int i = 0 ; i < facet.length; ++i) {
-                final Cartesian3D v = vertices.get(facet[i]);
+                final Point3D v = vertices.get(facet[i]);
                 if (!plane.contains(v)) {
                     throw new IllegalArgumentException("Point " + v + " is out of plane");
                 }
@@ -244,7 +243,7 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * @return references array such that r[v][k] = f for some k if facet f contains vertex v
      * @exception IllegalArgumentException if some facets have fewer than 3 vertices
      */
-    private static int[][] findReferences(final List<Cartesian3D> vertices, final List<int[]> facets) {
+    private static int[][] findReferences(final List<Point3D> vertices, final List<int[]> facets) {
 
         // find the maximum number of facets a vertex belongs to
         final int[] nbFacets = new int[vertices.size()];
@@ -288,7 +287,7 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * once in the successors list (which means one facet orientation is wrong)
 
      */
-    private static int[][] successors(final List<Cartesian3D> vertices, final List<int[]> facets,
+    private static int[][] successors(final List<Point3D> vertices, final List<int[]> facets,
                                       final int[][] references) {
 
         // create an array large enough
@@ -311,8 +310,8 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
                 successors[v][k] = facet[(i + 1) % facet.length];
                 for (int l = 0; l < k; ++l) {
                     if (successors[v][l] == successors[v][k]) {
-                        final Cartesian3D start = vertices.get(v);
-                        final Cartesian3D end   = vertices.get(successors[v][k]);
+                        final Point3D start = vertices.get(v);
+                        final Point3D end   = vertices.get(successors[v][k]);
                         throw new IllegalArgumentException("Facet orientation mismatch around edge joining points " + start + " and " + end);
                     }
                 }
@@ -326,7 +325,7 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
 
     /** {@inheritDoc} */
     @Override
-    public PolyhedronsSet buildNew(final BSPTree<Euclidean3D> tree) {
+    public PolyhedronsSet buildNew(final BSPTree<Point3D> tree) {
         return new PolyhedronsSet(tree, getTolerance());
     }
 
@@ -336,11 +335,11 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
         // check simple cases first
         if (isEmpty()) {
             setSize(0.0);
-            setBarycenter((Point<Euclidean3D>) Cartesian3D.NaN);
+            setBarycenter(Point3D.NaN);
         }
         else if (isFull()) {
             setSize(Double.POSITIVE_INFINITY);
-            setBarycenter((Point<Euclidean3D>) Cartesian3D.NaN);
+            setBarycenter(Point3D.NaN);
         }
         else {
             // not empty or full; compute the contribution of all boundary facets
@@ -348,16 +347,16 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
             getTree(true).visit(contributionVisitor);
 
             final double size = contributionVisitor.getSize();
-            final Cartesian3D barycenter = contributionVisitor.getBarycenter();
+            final Point3D barycenter = contributionVisitor.getBarycenter();
 
             if (size < 0) {
                 // the polyhedrons set is a finite outside surrounded by an infinite inside
                 setSize(Double.POSITIVE_INFINITY);
-                setBarycenter((Point<Euclidean3D>) Cartesian3D.NaN);
+                setBarycenter(Point3D.NaN);
             } else {
                 // the polyhedrons set is finite
                 setSize(size);
-                setBarycenter((Point<Euclidean3D>) barycenter);
+                setBarycenter(barycenter);
             }
         }
     }
@@ -376,13 +375,13 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      *  line from the apex to the base. The polyhedron barycenter then becomes
      *  the volume-weighted average of these pyramid centers.
      */
-    private static class FacetsContributionVisitor implements BSPTreeVisitor<Euclidean3D> {
+    private static class FacetsContributionVisitor implements BSPTreeVisitor<Point3D> {
 
         /** Accumulator for facet volume contributions. */
         private double volumeSum;
 
         /** Accumulator for barycenter contributions. */
-        private Cartesian3D barycenterSum = Cartesian3D.ZERO;
+        private Point3D barycenterSum = Point3D.ZERO;
 
         /** Returns the total computed size (ie, volume) of the polyhedron.
          * This value will be negative if the polyhedron is "inside-out", meaning
@@ -399,25 +398,25 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
          * region is infinite.
          * @return the barycenter.
          */
-        public Cartesian3D getBarycenter() {
+        public Point3D getBarycenter() {
             // Since the volume we used when adding together the facet contributions
             // was 3x the actual pyramid size, we'll multiply by 1/4 here instead
             // of 3/4 to adjust for the actual barycenter position in each pyramid.
-            return new Cartesian3D(1.0 / (4 * getSize()), barycenterSum);
+            return Point3D.vectorCombination(1.0 / (4 * getSize()), barycenterSum);
         }
 
         /** {@inheritDoc} */
         @Override
-        public Order visitOrder(final BSPTree<Euclidean3D> node) {
+        public Order visitOrder(final BSPTree<Point3D> node) {
             return Order.MINUS_SUB_PLUS;
         }
 
         /** {@inheritDoc} */
         @Override
-        public void visitInternalNode(final BSPTree<Euclidean3D> node) {
+        public void visitInternalNode(final BSPTree<Point3D> node) {
             @SuppressWarnings("unchecked")
-            final BoundaryAttribute<Euclidean3D> attribute =
-                (BoundaryAttribute<Euclidean3D>) node.getAttribute();
+            final BoundaryAttribute<Point3D> attribute =
+                (BoundaryAttribute<Point3D>) node.getAttribute();
             if (attribute.getPlusOutside() != null) {
                 addContribution(attribute.getPlusOutside(), false);
             }
@@ -428,34 +427,34 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
 
         /** {@inheritDoc} */
         @Override
-        public void visitLeafNode(final BSPTree<Euclidean3D> node) {
+        public void visitLeafNode(final BSPTree<Point3D> node) {
         }
 
         /** Add the contribution of a boundary facet.
          * @param facet boundary facet
          * @param reversed if true, the facet has the inside on its plus side
          */
-        private void addContribution(final SubHyperplane<Euclidean3D> facet, final boolean reversed) {
+        private void addContribution(final SubHyperplane<Point3D> facet, final boolean reversed) {
 
-            final Region<Euclidean2D> polygon = ((SubPlane) facet).getRemainingRegion();
+            final Region<Point2D> polygon = ((SubPlane) facet).getRemainingRegion();
             final double area = polygon.getSize();
 
             if (Double.isInfinite(area)) {
                 volumeSum = Double.POSITIVE_INFINITY;
-                barycenterSum = Cartesian3D.NaN;
+                barycenterSum = Point3D.NaN;
             } else {
                 final Plane plane = (Plane) facet.getHyperplane();
-                final Cartesian3D facetBarycenter = plane.toSpace(polygon.getBarycenter());
+                final Point3D facetBarycenter = plane.toSpace(polygon.getBarycenter());
 
                 // the volume here is actually 3x the actual pyramid volume; we'll apply
                 // the final scaling all at once at the end
-                double scaledVolume = area * facetBarycenter.dotProduct(plane.getNormal());
+                double scaledVolume = area * facetBarycenter.asVector().dotProduct(plane.getNormal());
                 if (reversed) {
                     scaledVolume = -scaledVolume;
                 }
 
                 volumeSum += scaledVolume;
-                barycenterSum = new Cartesian3D(1.0, barycenterSum, scaledVolume, facetBarycenter);
+                barycenterSum = Point3D.vectorCombination(1.0, barycenterSum, scaledVolume, facetBarycenter);
             }
         }
     }
@@ -467,7 +466,7 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * given point, or null if the line does not intersect any
      * sub-hyperplane
      */
-    public SubHyperplane<Euclidean3D> firstIntersection(final Cartesian3D point, final Line line) {
+    public SubHyperplane<Point3D> firstIntersection(final Point3D point, final Line line) {
         return recurseFirstIntersection(getTree(true), point, line);
     }
 
@@ -479,23 +478,23 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * given point, or null if the line does not intersect any
      * sub-hyperplane
      */
-    private SubHyperplane<Euclidean3D> recurseFirstIntersection(final BSPTree<Euclidean3D> node,
-                                                                final Cartesian3D point,
+    private SubHyperplane<Point3D> recurseFirstIntersection(final BSPTree<Point3D> node,
+                                                                final Point3D point,
                                                                 final Line line) {
 
-        final SubHyperplane<Euclidean3D> cut = node.getCut();
+        final SubHyperplane<Point3D> cut = node.getCut();
         if (cut == null) {
             return null;
         }
-        final BSPTree<Euclidean3D> minus = node.getMinus();
-        final BSPTree<Euclidean3D> plus  = node.getPlus();
+        final BSPTree<Point3D> minus = node.getMinus();
+        final BSPTree<Point3D> plus  = node.getPlus();
         final Plane                plane = (Plane) cut.getHyperplane();
 
         // establish search order
         final double offset = plane.getOffset(point);
         final boolean in    = Math.abs(offset) < getTolerance();
-        final BSPTree<Euclidean3D> near;
-        final BSPTree<Euclidean3D> far;
+        final BSPTree<Point3D> near;
+        final BSPTree<Point3D> far;
         if (offset < 0) {
             near = minus;
             far  = plus;
@@ -506,23 +505,23 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
 
         if (in) {
             // search in the cut hyperplane
-            final SubHyperplane<Euclidean3D> facet = boundaryFacet(point, node);
+            final SubHyperplane<Point3D> facet = boundaryFacet(point, node);
             if (facet != null) {
                 return facet;
             }
         }
 
         // search in the near branch
-        final SubHyperplane<Euclidean3D> crossed = recurseFirstIntersection(near, point, line);
+        final SubHyperplane<Point3D> crossed = recurseFirstIntersection(near, point, line);
         if (crossed != null) {
             return crossed;
         }
 
         if (!in) {
             // search in the cut hyperplane
-            final Cartesian3D hit3D = plane.intersection(line);
+            final Point3D hit3D = plane.intersection(line);
             if (hit3D != null && line.getAbscissa(hit3D) > line.getAbscissa(point)) {
-                final SubHyperplane<Euclidean3D> facet = boundaryFacet(hit3D, node);
+                final SubHyperplane<Point3D> facet = boundaryFacet(hit3D, node);
                 if (facet != null) {
                     return facet;
                 }
@@ -540,12 +539,12 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * @return the boundary facet this points belongs to (or null if it
      * does not belong to any boundary facet)
      */
-    private SubHyperplane<Euclidean3D> boundaryFacet(final Cartesian3D point,
-                                                     final BSPTree<Euclidean3D> node) {
-        final Cartesian2D point2D = ((Plane) node.getCut().getHyperplane()).toSubSpace(point);
+    private SubHyperplane<Point3D> boundaryFacet(final Point3D point,
+                                                     final BSPTree<Point3D> node) {
+        final Point2D point2D = ((Plane) node.getCut().getHyperplane()).toSubSpace(point);
         @SuppressWarnings("unchecked")
-        final BoundaryAttribute<Euclidean3D> attribute =
-            (BoundaryAttribute<Euclidean3D>) node.getAttribute();
+        final BoundaryAttribute<Point3D> attribute =
+            (BoundaryAttribute<Point3D>) node.getAttribute();
         if ((attribute.getPlusOutside() != null) &&
             (((SubPlane) attribute.getPlusOutside()).getRemainingRegion().checkPoint(point2D) == Location.INSIDE)) {
             return attribute.getPlusOutside();
@@ -563,15 +562,15 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * @param rotation vectorial rotation operator
      * @return a new instance representing the rotated region
      */
-    public PolyhedronsSet rotate(final Cartesian3D center, final Rotation rotation) {
+    public PolyhedronsSet rotate(final Point3D center, final Rotation rotation) {
         return (PolyhedronsSet) applyTransform(new RotationTransform(center, rotation));
     }
 
     /** 3D rotation as a Transform. */
-    private static class RotationTransform implements Transform<Euclidean3D, Euclidean2D> {
+    private static class RotationTransform implements Transform<Point3D, Point2D> {
 
         /** Center point of the rotation. */
-        private final Cartesian3D   center;
+        private final Point3D   center;
 
         /** Vectorial rotation. */
         private final Rotation   rotation;
@@ -580,46 +579,46 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
         private Plane cachedOriginal;
 
         /** Cached 2D transform valid inside the cached original hyperplane. */
-        private Transform<Euclidean2D, Euclidean1D>  cachedTransform;
+        private Transform<Point2D, Point1D>  cachedTransform;
 
         /** Build a rotation transform.
          * @param center center point of the rotation
          * @param rotation vectorial rotation
          */
-        RotationTransform(final Cartesian3D center, final Rotation rotation) {
+        RotationTransform(final Point3D center, final Rotation rotation) {
             this.center   = center;
             this.rotation = rotation;
         }
 
         /** {@inheritDoc} */
         @Override
-        public Cartesian3D apply(final Point<Euclidean3D> point) {
-            final Cartesian3D delta = ((Cartesian3D) point).subtract(center);
-            return new Cartesian3D(1.0, center, 1.0, rotation.applyTo(delta));
+        public Point3D apply(final Point3D point) {
+            final Vector3D delta = point.subtract(center);
+            return Point3D.vectorCombination(1.0, center, 1.0, rotation.applyTo(delta));
         }
 
         /** {@inheritDoc} */
         @Override
-        public Plane apply(final Hyperplane<Euclidean3D> hyperplane) {
+        public Plane apply(final Hyperplane<Point3D> hyperplane) {
             return ((Plane) hyperplane).rotate(center, rotation);
         }
 
         /** {@inheritDoc} */
         @Override
-        public SubHyperplane<Euclidean2D> apply(final SubHyperplane<Euclidean2D> sub,
-                                                final Hyperplane<Euclidean3D> original,
-                                                final Hyperplane<Euclidean3D> transformed) {
+        public SubHyperplane<Point2D> apply(final SubHyperplane<Point2D> sub,
+                                                final Hyperplane<Point3D> original,
+                                                final Hyperplane<Point3D> transformed) {
             if (original != cachedOriginal) {
                 // we have changed hyperplane, reset the in-hyperplane transform
 
                 final Plane    oPlane = (Plane) original;
                 final Plane    tPlane = (Plane) transformed;
-                final Cartesian3D p00    = oPlane.getOrigin();
-                final Cartesian3D p10    = oPlane.toSpace(new Cartesian2D(1.0, 0.0));
-                final Cartesian3D p01    = oPlane.toSpace(new Cartesian2D(0.0, 1.0));
-                final Cartesian2D tP00   = tPlane.toSubSpace(apply(p00));
-                final Cartesian2D tP10   = tPlane.toSubSpace(apply(p10));
-                final Cartesian2D tP01   = tPlane.toSubSpace(apply(p01));
+                final Point3D p00    = oPlane.getOrigin();
+                final Point3D p10    = oPlane.toSpace(new Point2D(1.0, 0.0));
+                final Point3D p01    = oPlane.toSpace(new Point2D(0.0, 1.0));
+                final Point2D tP00   = tPlane.toSubSpace(apply(p00));
+                final Point2D tP10   = tPlane.toSubSpace(apply(p10));
+                final Point2D tP01   = tPlane.toSubSpace(apply(p01));
 
                 cachedOriginal  = (Plane) original;
                 cachedTransform =
@@ -641,52 +640,52 @@ public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
      * @param translation translation to apply
      * @return a new instance representing the translated region
      */
-    public PolyhedronsSet translate(final Cartesian3D translation) {
+    public PolyhedronsSet translate(final Vector3D translation) {
         return (PolyhedronsSet) applyTransform(new TranslationTransform(translation));
     }
 
     /** 3D translation as a transform. */
-    private static class TranslationTransform implements Transform<Euclidean3D, Euclidean2D> {
+    private static class TranslationTransform implements Transform<Point3D, Point2D> {
 
         /** Translation vector. */
-        private final Cartesian3D   translation;
+        private final Vector3D   translation;
 
         /** Cached original hyperplane. */
         private Plane cachedOriginal;
 
         /** Cached 2D transform valid inside the cached original hyperplane. */
-        private Transform<Euclidean2D, Euclidean1D>  cachedTransform;
+        private Transform<Point2D, Point1D>  cachedTransform;
 
         /** Build a translation transform.
          * @param translation translation vector
          */
-        TranslationTransform(final Cartesian3D translation) {
+        TranslationTransform(final Vector3D translation) {
             this.translation = translation;
         }
 
         /** {@inheritDoc} */
         @Override
-        public Cartesian3D apply(final Point<Euclidean3D> point) {
-            return new Cartesian3D(1.0, (Cartesian3D) point, 1.0, translation);
+        public Point3D apply(final Point3D point) {
+            return Point3D.vectorCombination(1.0, point, 1.0, translation);
         }
 
         /** {@inheritDoc} */
         @Override
-        public Plane apply(final Hyperplane<Euclidean3D> hyperplane) {
+        public Plane apply(final Hyperplane<Point3D> hyperplane) {
             return ((Plane) hyperplane).translate(translation);
         }
 
         /** {@inheritDoc} */
         @Override
-        public SubHyperplane<Euclidean2D> apply(final SubHyperplane<Euclidean2D> sub,
-                                                final Hyperplane<Euclidean3D> original,
-                                                final Hyperplane<Euclidean3D> transformed) {
+        public SubHyperplane<Point2D> apply(final SubHyperplane<Point2D> sub,
+                                                final Hyperplane<Point3D> original,
+                                                final Hyperplane<Point3D> transformed) {
             if (original != cachedOriginal) {
                 // we have changed hyperplane, reset the in-hyperplane transform
 
                 final Plane   oPlane = (Plane) original;
                 final Plane   tPlane = (Plane) transformed;
-                final Cartesian2D shift  = tPlane.toSubSpace(apply(oPlane.getOrigin()));
+                final Point2D shift  = tPlane.toSubSpace(apply(oPlane.getOrigin()));
 
                 cachedOriginal  = (Plane) original;
                 cachedTransform =
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java
index eac61f1..7261fd1 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java
@@ -44,8 +44,8 @@ import org.apache.commons.numbers.arrays.LinearCombination;
  * <p>Focus is oriented on what a rotation <em>do</em> rather than on its
  * underlying representation. Once it has been built, and regardless of its
  * internal representation, a rotation is an <em>operator</em> which basically
- * transforms three dimensional {@link Cartesian3D vectors} into other three
- * dimensional {@link Cartesian3D vectors}. Depending on the application, the
+ * transforms three dimensional {@link Vector3D vectors} into other three
+ * dimensional {@link Vector3D vectors}. Depending on the application, the
  * meaning of these vectors may vary and the semantics of the rotation also.</p>
  * <p>For example in an spacecraft attitude simulation tool, users will often
  * consider the vectors are fixed (say the Earth direction for example) and the
@@ -68,8 +68,8 @@ import org.apache.commons.numbers.arrays.LinearCombination;
  * class does not push the user towards one specific definition and hence does not
  * provide methods like <code>projectVectorIntoDestinationFrame</code> or
  * <code>computeTransformedDirection</code>. It provides simpler and more generic
- * methods: {@link #applyTo(Cartesian3D) applyTo(Cartesian3D)} and {@link
- * #applyInverseTo(Cartesian3D) applyInverseTo(Cartesian3D)}.</p>
+ * methods: {@link #applyTo(Vector3D) applyTo(Cartesian3D)} and {@link
+ * #applyInverseTo(Vector3D) applyInverseTo(Cartesian3D)}.</p>
  *
  * <p>Since a rotation is basically a vectorial operator, several rotations can be
  * composed together and the composite operation <code>r = r<sub>1</sub> o
@@ -84,7 +84,7 @@ import org.apache.commons.numbers.arrays.LinearCombination;
  *
  * <p>Rotations are guaranteed to be immutable objects.</p>
  *
- * @see Cartesian3D
+ * @see Vector3D
  * @see RotationOrder
  */
 
@@ -154,16 +154,16 @@ public class Rotation implements Serializable {
   /** Build a rotation from an axis and an angle.
    * <p>
    * Calling this constructor is equivalent to call
-   * {@link #Rotation(Cartesian3D, double, RotationConvention)
+   * {@link #Rotation(Vector3D, double, RotationConvention)
    * new Rotation(axis, angle, RotationConvention.VECTOR_OPERATOR)}
    * </p>
    * @param axis axis around which to rotate
    * @param angle rotation angle.
    * @exception IllegalArgumentException if the axis norm is zero
-   * @deprecated as of 3.6, replaced with {@link #Rotation(Cartesian3D, double, RotationConvention)}
+   * @deprecated as of 3.6, replaced with {@link #Rotation(Vector3D, double, RotationConvention)}
    */
   @Deprecated
-  public Rotation(Cartesian3D axis, double angle) throws IllegalArgumentException {
+  public Rotation(Vector3D axis, double angle) throws IllegalArgumentException {
       this(axis, angle, RotationConvention.VECTOR_OPERATOR);
   }
 
@@ -173,7 +173,7 @@ public class Rotation implements Serializable {
    * @param convention convention to use for the semantics of the angle
    * @exception IllegalArgumentException if the axis norm is zero
    */
-  public Rotation(final Cartesian3D axis, final double angle, final RotationConvention convention)
+  public Rotation(final Vector3D axis, final double angle, final RotationConvention convention)
       throws IllegalArgumentException {
 
     double norm = axis.getNorm();
@@ -267,19 +267,19 @@ public class Rotation implements Serializable {
    * @exception IllegalArgumentException if the norm of one of the vectors is zero,
    * or if one of the pair is degenerated (i.e. the vectors of the pair are collinear)
    */
-  public Rotation(Cartesian3D u1, Cartesian3D u2, Cartesian3D v1, Cartesian3D v2)
+  public Rotation(Vector3D u1, Vector3D u2, Vector3D v1, Vector3D v2)
       throws IllegalArgumentException {
 
       try {
           // build orthonormalized base from u1, u2
           // this fails when vectors are null or collinear, which is forbidden to define a rotation
-          final Cartesian3D u3 = u1.crossProduct(u2).normalize();
+          final Vector3D u3 = u1.crossProduct(u2).normalize();
           u2 = u3.crossProduct(u1).normalize();
           u1 = u1.normalize();
 
           // build an orthonormalized base from v1, v2
           // this fails when vectors are null or collinear, which is forbidden to define a rotation
-          final Cartesian3D v3 = v1.crossProduct(v2).normalize();
+          final Vector3D v3 = v1.crossProduct(v2).normalize();
           v2 = v3.crossProduct(v1).normalize();
           v1 = v1.normalize();
 
@@ -327,7 +327,7 @@ public class Rotation implements Serializable {
    * @param v desired image of u by the rotation
    * @exception IllegalArgumentException if the norm of one of the vectors is zero
    */
-  public Rotation(Cartesian3D u, Cartesian3D v) throws IllegalArgumentException {
+  public Rotation(Vector3D u, Vector3D v) throws IllegalArgumentException {
 
     double normProduct = u.getNorm() * v.getNorm();
     if (normProduct == 0) {
@@ -339,7 +339,7 @@ public class Rotation implements Serializable {
     if (dot < ((2.0e-15 - 1.0) * normProduct)) {
       // special case u = -v: we select a PI angle rotation around
       // an arbitrary vector orthogonal to u
-      Cartesian3D w = u.orthogonal();
+      Vector3D w = u.orthogonal();
       q0 = 0.0;
       q1 = -w.getX();
       q2 = -w.getY();
@@ -349,7 +349,7 @@ public class Rotation implements Serializable {
       // the shortest possible rotation: axis orthogonal to this plane
       q0 = Math.sqrt(0.5 * (1.0 + dot / normProduct));
       double coeff = 1.0 / (2.0 * q0 * normProduct);
-      Cartesian3D q = v.crossProduct(u);
+      Vector3D q = v.crossProduct(u);
       q1 = coeff * q.getX();
       q2 = coeff * q.getY();
       q3 = coeff * q.getZ();
@@ -517,11 +517,11 @@ public class Rotation implements Serializable {
    * {@link #getAxis(RotationConvention) getAxis(RotationConvention.VECTOR_OPERATOR)}
    * </p>
    * @return normalized axis of the rotation
-   * @see #Rotation(Cartesian3D, double, RotationConvention)
+   * @see #Rotation(Vector3D, double, RotationConvention)
    * @deprecated as of 3.6, replaced with {@link #getAxis(RotationConvention)}
    */
   @Deprecated
-  public Cartesian3D getAxis() {
+  public Vector3D getAxis() {
     return getAxis(RotationConvention.VECTOR_OPERATOR);
   }
 
@@ -533,26 +533,26 @@ public class Rotation implements Serializable {
    * </p>
    * @param convention convention to use for the semantics of the angle
    * @return normalized axis of the rotation
-   * @see #Rotation(Cartesian3D, double, RotationConvention)
+   * @see #Rotation(Vector3D, double, RotationConvention)
    */
-  public Cartesian3D getAxis(final RotationConvention convention) {
+  public Vector3D getAxis(final RotationConvention convention) {
     final double squaredSine = q1 * q1 + q2 * q2 + q3 * q3;
     if (squaredSine == 0) {
-      return convention == RotationConvention.VECTOR_OPERATOR ? Cartesian3D.PLUS_I : Cartesian3D.MINUS_I;
+      return convention == RotationConvention.VECTOR_OPERATOR ? Vector3D.PLUS_X : Vector3D.MINUS_X;
     } else {
         final double sgn = convention == RotationConvention.VECTOR_OPERATOR ? +1 : -1;
         if (q0 < 0) {
             final double inverse = sgn / Math.sqrt(squaredSine);
-            return new Cartesian3D(q1 * inverse, q2 * inverse, q3 * inverse);
+            return new Vector3D(q1 * inverse, q2 * inverse, q3 * inverse);
         }
         final double inverse = -sgn / Math.sqrt(squaredSine);
-        return new Cartesian3D(q1 * inverse, q2 * inverse, q3 * inverse);
+        return new Vector3D(q1 * inverse, q2 * inverse, q3 * inverse);
     }
   }
 
   /** Get the angle of the rotation.
    * @return angle of the rotation (between 0 and &pi;)
-   * @see #Rotation(Cartesian3D, double)
+   * @see #Rotation(Vector3D, double)
    */
   public double getAngle() {
     if ((q0 < -0.1) || (q0 > 0.1)) {
@@ -630,8 +630,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusI) coordinates are :
               // cos (psi) cos (theta), -sin (psi) cos (theta), sin (theta)
               // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_K);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_I);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Z);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_X);
               if  ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -648,8 +648,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusI) coordinates are :
               // cos (theta) cos (psi), -sin (psi), sin (theta) cos (psi)
               // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_J);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_I);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Y);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_X);
               if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -666,8 +666,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusJ) coordinates are :
               // sin (psi) cos (phi), cos (psi) cos (phi), -sin (phi)
               // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_K);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_J);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Z);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y);
               if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -684,8 +684,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusJ) coordinates are :
               // sin (psi), cos (phi) cos (psi), -sin (phi) cos (psi)
               // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_I);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_J);
+              Vector3D v1 = applyTo(Vector3D.PLUS_X);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y);
               if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -702,8 +702,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusK) coordinates are :
               // -sin (theta) cos (phi), sin (phi), cos (theta) cos (phi)
               // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_J);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_K);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Y);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z);
               if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -720,8 +720,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusK) coordinates are :
               // -sin (theta), sin (phi) cos (theta), cos (phi) cos (theta)
               // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_I);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_K);
+              Vector3D v1 = applyTo(Vector3D.PLUS_X);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z);
               if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -738,8 +738,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusI) coordinates are :
               // cos (theta), sin (theta) sin (phi2), sin (theta) cos (phi2)
               // and we can choose to have theta in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_I);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_I);
+              Vector3D v1 = applyTo(Vector3D.PLUS_X);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_X);
               if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -756,8 +756,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusI) coordinates are :
               // cos (psi), -sin (psi) cos (phi2), sin (psi) sin (phi2)
               // and we can choose to have psi in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_I);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_I);
+              Vector3D v1 = applyTo(Vector3D.PLUS_X);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_X);
               if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -774,8 +774,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusJ) coordinates are :
               // sin (phi) sin (theta2), cos (phi), -sin (phi) cos (theta2)
               // and we can choose to have phi in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_J);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_J);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Y);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y);
               if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -792,8 +792,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusJ) coordinates are :
               // sin (psi) cos (theta2), cos (psi), sin (psi) sin (theta2)
               // and we can choose to have psi in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_J);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_J);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Y);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y);
               if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -810,8 +810,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusK) coordinates are :
               // sin (phi) sin (psi2), sin (phi) cos (psi2), cos (phi)
               // and we can choose to have phi in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_K);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_K);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Z);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z);
               if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -828,8 +828,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusK) coordinates are :
               // -sin (theta) cos (psi2), sin (theta) sin (psi2), cos (theta)
               // and we can choose to have theta in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_K);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_K);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Z);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z);
               if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -848,8 +848,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusK) coordinates are :
               // sin (theta), -sin (phi) cos (theta), cos (phi) cos (theta)
               // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_I);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_K);
+              Vector3D v1 = applyTo(Vector3D.PLUS_X);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z);
               if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -866,8 +866,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusJ) coordinates are :
               // -sin (psi), cos (phi) cos (psi), sin (phi) cos (psi)
               // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_I);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_J);
+              Vector3D v1 = applyTo(Vector3D.PLUS_X);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y);
               if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -884,8 +884,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusK) coordinates are :
               // sin (theta) cos (phi), -sin (phi), cos (theta) cos (phi)
               // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_J);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_K);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Y);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z);
               if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -902,8 +902,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusI) coordinates are :
               // cos (theta) cos (psi), sin (psi), -sin (theta) cos (psi)
               // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_J);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_I);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Y);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_X);
               if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -920,8 +920,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusJ) coordinates are :
               // -sin (psi) cos (phi), cos (psi) cos (phi), sin (phi)
               // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_K);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_J);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Z);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y);
               if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -938,8 +938,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusI) coordinates are :
               // cos (psi) cos (theta), sin (psi) cos (theta), -sin (theta)
               // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_K);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_I);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Z);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_X);
               if  ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
                   throw new IllegalStateException(CARDAN_SINGULARITY_MSG);
               }
@@ -956,8 +956,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusI) coordinates are :
               // cos (theta), sin (theta) sin (phi1), -sin (theta) cos (phi1)
               // and we can choose to have theta in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_I);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_I);
+              Vector3D v1 = applyTo(Vector3D.PLUS_X);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_X);
               if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -974,8 +974,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusI) coordinates are :
               // cos (psi), sin (psi) cos (phi1), sin (psi) sin (phi1)
               // and we can choose to have psi in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_I);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_I);
+              Vector3D v1 = applyTo(Vector3D.PLUS_X);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_X);
               if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -992,8 +992,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusJ) coordinates are :
               //  sin (theta1) sin (phi), cos (phi), cos (theta1) sin (phi)
               // and we can choose to have phi in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_J);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_J);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Y);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y);
               if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -1010,8 +1010,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusJ) coordinates are :
               //  -cos (theta1) sin (psi), cos (psi), sin (theta1) sin (psi)
               // and we can choose to have psi in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_J);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_J);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Y);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Y);
               if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -1028,8 +1028,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusK) coordinates are :
               //  sin (psi1) sin (phi), -cos (psi1) sin (phi), cos (phi)
               // and we can choose to have phi in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_K);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_K);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Z);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z);
               if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -1046,8 +1046,8 @@ public class Rotation implements Serializable {
               // (-r) (Cartesian3D.plusK) coordinates are :
               //  cos (psi1) sin (theta), sin (psi1) sin (theta), cos (theta)
               // and we can choose to have theta in the interval [0 ; PI]
-              Cartesian3D v1 = applyTo(Cartesian3D.PLUS_K);
-              Cartesian3D v2 = applyInverseTo(Cartesian3D.PLUS_K);
+              Vector3D v1 = applyTo(Vector3D.PLUS_Z);
+              Vector3D v2 = applyInverseTo(Vector3D.PLUS_Z);
               if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
                   throw new IllegalStateException(EULER_SINGULARITY_MSG);
               }
@@ -1105,7 +1105,7 @@ public class Rotation implements Serializable {
    * @param u vector to apply the rotation to
    * @return a new vector which is the image of u by the rotation
    */
-  public Cartesian3D applyTo(Cartesian3D u) {
+  public Vector3D applyTo(Vector3D u) {
 
     double x = u.getX();
     double y = u.getY();
@@ -1113,7 +1113,7 @@ public class Rotation implements Serializable {
 
     double s = q1 * x + q2 * y + q3 * z;
 
-    return new Cartesian3D(2 * (q0 * (x * q0 - (q2 * z - q3 * y)) + s * q1) - x,
+    return new Vector3D(2 * (q0 * (x * q0 - (q2 * z - q3 * y)) + s * q1) - x,
                         2 * (q0 * (y * q0 - (q3 * x - q1 * z)) + s * q2) - y,
                         2 * (q0 * (z * q0 - (q1 * y - q2 * x)) + s * q3) - z);
 
@@ -1142,7 +1142,7 @@ public class Rotation implements Serializable {
    * @param u vector to apply the inverse of the rotation to
    * @return a new vector which such that u is its image by the rotation
    */
-  public Cartesian3D applyInverseTo(Cartesian3D u) {
+  public Vector3D applyInverseTo(Vector3D u) {
 
     double x = u.getX();
     double y = u.getY();
@@ -1151,7 +1151,7 @@ public class Rotation implements Serializable {
     double s = q1 * x + q2 * y + q3 * z;
     double m0 = -q0;
 
-    return new Cartesian3D(2 * (m0 * (x * m0 - (q2 * z - q3 * y)) + s * q1) - x,
+    return new Vector3D(2 * (m0 * (x * m0 - (q2 * z - q3 * y)) + s * q1) - x,
                         2 * (m0 * (y * m0 - (q3 * x - q1 * z)) + s * q2) - y,
                         2 * (m0 * (z * m0 - (q1 * y - q2 * x)) + s * q3) - z);
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/RotationOrder.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/RotationOrder.java
index 7e0a042..0d0e440 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/RotationOrder.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/RotationOrder.java
@@ -33,96 +33,96 @@ public final class RotationOrder {
      * around Z
      */
     public static final RotationOrder XYZ =
-      new RotationOrder("XYZ", Cartesian3D.PLUS_I, Cartesian3D.PLUS_J, Cartesian3D.PLUS_K);
+      new RotationOrder("XYZ", Vector3D.PLUS_X, Vector3D.PLUS_Y, Vector3D.PLUS_Z);
 
     /** Set of Cardan angles.
      * this ordered set of rotations is around X, then around Z, then
      * around Y
      */
     public static final RotationOrder XZY =
-      new RotationOrder("XZY", Cartesian3D.PLUS_I, Cartesian3D.PLUS_K, Cartesian3D.PLUS_J);
+      new RotationOrder("XZY", Vector3D.PLUS_X, Vector3D.PLUS_Z, Vector3D.PLUS_Y);
 
     /** Set of Cardan angles.
      * this ordered set of rotations is around Y, then around X, then
      * around Z
      */
     public static final RotationOrder YXZ =
-      new RotationOrder("YXZ", Cartesian3D.PLUS_J, Cartesian3D.PLUS_I, Cartesian3D.PLUS_K);
+      new RotationOrder("YXZ", Vector3D.PLUS_Y, Vector3D.PLUS_X, Vector3D.PLUS_Z);
 
     /** Set of Cardan angles.
      * this ordered set of rotations is around Y, then around Z, then
      * around X
      */
     public static final RotationOrder YZX =
-      new RotationOrder("YZX", Cartesian3D.PLUS_J, Cartesian3D.PLUS_K, Cartesian3D.PLUS_I);
+      new RotationOrder("YZX", Vector3D.PLUS_Y, Vector3D.PLUS_Z, Vector3D.PLUS_X);
 
     /** Set of Cardan angles.
      * this ordered set of rotations is around Z, then around X, then
      * around Y
      */
     public static final RotationOrder ZXY =
-      new RotationOrder("ZXY", Cartesian3D.PLUS_K, Cartesian3D.PLUS_I, Cartesian3D.PLUS_J);
+      new RotationOrder("ZXY", Vector3D.PLUS_Z, Vector3D.PLUS_X, Vector3D.PLUS_Y);
 
     /** Set of Cardan angles.
      * this ordered set of rotations is around Z, then around Y, then
      * around X
      */
     public static final RotationOrder ZYX =
-      new RotationOrder("ZYX", Cartesian3D.PLUS_K, Cartesian3D.PLUS_J, Cartesian3D.PLUS_I);
+      new RotationOrder("ZYX", Vector3D.PLUS_Z, Vector3D.PLUS_Y, Vector3D.PLUS_X);
 
     /** Set of Euler angles.
      * this ordered set of rotations is around X, then around Y, then
      * around X
      */
     public static final RotationOrder XYX =
-      new RotationOrder("XYX", Cartesian3D.PLUS_I, Cartesian3D.PLUS_J, Cartesian3D.PLUS_I);
+      new RotationOrder("XYX", Vector3D.PLUS_X, Vector3D.PLUS_Y, Vector3D.PLUS_X);
 
     /** Set of Euler angles.
      * this ordered set of rotations is around X, then around Z, then
      * around X
      */
     public static final RotationOrder XZX =
-      new RotationOrder("XZX", Cartesian3D.PLUS_I, Cartesian3D.PLUS_K, Cartesian3D.PLUS_I);
+      new RotationOrder("XZX", Vector3D.PLUS_X, Vector3D.PLUS_Z, Vector3D.PLUS_X);
 
     /** Set of Euler angles.
      * this ordered set of rotations is around Y, then around X, then
      * around Y
      */
     public static final RotationOrder YXY =
-      new RotationOrder("YXY", Cartesian3D.PLUS_J, Cartesian3D.PLUS_I, Cartesian3D.PLUS_J);
+      new RotationOrder("YXY", Vector3D.PLUS_Y, Vector3D.PLUS_X, Vector3D.PLUS_Y);
 
     /** Set of Euler angles.
      * this ordered set of rotations is around Y, then around Z, then
      * around Y
      */
     public static final RotationOrder YZY =
-      new RotationOrder("YZY", Cartesian3D.PLUS_J, Cartesian3D.PLUS_K, Cartesian3D.PLUS_J);
+      new RotationOrder("YZY", Vector3D.PLUS_Y, Vector3D.PLUS_Z, Vector3D.PLUS_Y);
 
     /** Set of Euler angles.
      * this ordered set of rotations is around Z, then around X, then
      * around Z
      */
     public static final RotationOrder ZXZ =
-      new RotationOrder("ZXZ", Cartesian3D.PLUS_K, Cartesian3D.PLUS_I, Cartesian3D.PLUS_K);
+      new RotationOrder("ZXZ", Vector3D.PLUS_Z, Vector3D.PLUS_X, Vector3D.PLUS_Z);
 
     /** Set of Euler angles.
      * this ordered set of rotations is around Z, then around Y, then
      * around Z
      */
     public static final RotationOrder ZYZ =
-      new RotationOrder("ZYZ", Cartesian3D.PLUS_K, Cartesian3D.PLUS_J, Cartesian3D.PLUS_K);
+      new RotationOrder("ZYZ", Vector3D.PLUS_Z, Vector3D.PLUS_Y, Vector3D.PLUS_Z);
 
     /** Name of the rotations order. */
     private final String name;
 
     /** Axis of the first rotation. */
-    private final Cartesian3D a1;
+    private final Vector3D a1;
 
     /** Axis of the second rotation. */
-    private final Cartesian3D a2;
+    private final Vector3D a2;
 
     /** Axis of the third rotation. */
-    private final Cartesian3D a3;
+    private final Vector3D a3;
 
     /** Private constructor.
      * This is a utility class that cannot be instantiated by the user,
@@ -133,7 +133,7 @@ public final class RotationOrder {
      * @param a3 axis of the third rotation
      */
     private RotationOrder(final String name,
-                          final Cartesian3D a1, final Cartesian3D a2, final Cartesian3D a3) {
+                          final Vector3D a1, final Vector3D a2, final Vector3D a3) {
         this.name = name;
         this.a1   = a1;
         this.a2   = a2;
@@ -151,21 +151,21 @@ public final class RotationOrder {
     /** Get the axis of the first rotation.
      * @return axis of the first rotation
      */
-    public Cartesian3D getA1() {
+    public Vector3D getA1() {
         return a1;
     }
 
     /** Get the axis of the second rotation.
      * @return axis of the second rotation
      */
-    public Cartesian3D getA2() {
+    public Vector3D getA2() {
         return a2;
     }
 
     /** Get the axis of the second rotation.
      * @return axis of the second rotation
      */
-    public Cartesian3D getA3() {
+    public Vector3D getA3() {
         return a3;
     }
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Segment.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Segment.java
index e9681a1..93825d5 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Segment.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Segment.java
@@ -22,10 +22,10 @@ package org.apache.commons.geometry.euclidean.threed;
 public class Segment {
 
     /** Start point of the segment. */
-    private final Cartesian3D start;
+    private final Point3D start;
 
     /** End point of the segments. */
-    private final Cartesian3D end;
+    private final Point3D end;
 
     /** Line containing the segment. */
     private final Line     line;
@@ -35,7 +35,7 @@ public class Segment {
      * @param end end point of the segment
      * @param line line containing the segment
      */
-    public Segment(final Cartesian3D start, final Cartesian3D end, final Line line) {
+    public Segment(final Point3D start, final Point3D end, final Line line) {
         this.start  = start;
         this.end    = end;
         this.line   = line;
@@ -44,14 +44,14 @@ public class Segment {
     /** Get the start point of the segment.
      * @return start point of the segment
      */
-    public Cartesian3D getStart() {
+    public Point3D getStart() {
         return start;
     }
 
     /** Get the end point of the segment.
      * @return end point of the segment
      */
-    public Cartesian3D getEnd() {
+    public Point3D getEnd() {
         return end;
     }
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubLine.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubLine.java
index 2be477f..fdf511b 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubLine.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubLine.java
@@ -19,11 +19,10 @@ package org.apache.commons.geometry.euclidean.threed;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.commons.geometry.core.Point;
 import org.apache.commons.geometry.core.partitioning.Region.Location;
-import org.apache.commons.geometry.euclidean.oned.Cartesian1D;
 import org.apache.commons.geometry.euclidean.oned.Interval;
 import org.apache.commons.geometry.euclidean.oned.IntervalsSet;
+import org.apache.commons.geometry.euclidean.oned.Point1D;
 
 /** This class represents a subset of a {@link Line}.
  */
@@ -50,7 +49,7 @@ public class SubLine {
      * @param tolerance tolerance below which points are considered identical
      * @exception IllegalArgumentException if the points are equal
      */
-    public SubLine(final Cartesian3D start, final Cartesian3D end, final double tolerance)
+    public SubLine(final Point3D start, final Point3D end, final double tolerance)
         throws IllegalArgumentException {
         this(new Line(start, end, tolerance), buildIntervalSet(start, end, tolerance));
     }
@@ -84,8 +83,8 @@ public class SubLine {
         final List<Segment> segments = new ArrayList<>(list.size());
 
         for (final Interval interval : list) {
-            final Cartesian3D start = line.toSpace(new Cartesian1D(interval.getInf()));
-            final Cartesian3D end   = line.toSpace(new Cartesian1D(interval.getSup()));
+            final Point3D start = line.toSpace(new Point1D(interval.getInf()));
+            final Point3D end   = line.toSpace(new Point1D(interval.getSup()));
             segments.add(new Segment(start, end, line));
         }
 
@@ -107,19 +106,19 @@ public class SubLine {
      * occurring on endpoints lead to null being returned
      * @return the intersection point if there is one, null if the sub-lines don't intersect
      */
-    public Cartesian3D intersection(final SubLine subLine, final boolean includeEndPoints) {
+    public Point3D intersection(final SubLine subLine, final boolean includeEndPoints) {
 
         // compute the intersection on infinite line
-        Cartesian3D v1D = line.intersection(subLine.line);
+        Point3D v1D = line.intersection(subLine.line);
         if (v1D == null) {
             return null;
         }
 
         // check location of point with respect to first sub-line
-        Location loc1 = remainingRegion.checkPoint(line.toSubSpace((Point<Euclidean3D>) v1D));
+        Location loc1 = remainingRegion.checkPoint(line.toSubSpace(v1D));
 
         // check location of point with respect to second sub-line
-        Location loc2 = subLine.remainingRegion.checkPoint(subLine.line.toSubSpace((Point<Euclidean3D>) v1D));
+        Location loc2 = subLine.remainingRegion.checkPoint(subLine.line.toSubSpace(v1D));
 
         if (includeEndPoints) {
             return ((loc1 != Location.OUTSIDE) && (loc2 != Location.OUTSIDE)) ? v1D : null;
@@ -136,11 +135,11 @@ public class SubLine {
      * @param tolerance tolerance below which points are considered identical
      * @exception IllegalArgumentException if the points are equal
      */
-    private static IntervalsSet buildIntervalSet(final Cartesian3D start, final Cartesian3D end, final double tolerance)
+    private static IntervalsSet buildIntervalSet(final Point3D start, final Point3D end, final double tolerance)
         throws IllegalArgumentException {
         final Line line = new Line(start, end, tolerance);
-        return new IntervalsSet(line.toSubSpace((Point<Euclidean3D>) start).getX(),
-                                line.toSubSpace((Point<Euclidean3D>) end).getX(),
+        return new IntervalsSet(line.toSubSpace(start).getX(),
+                                line.toSubSpace(end).getX(),
                                 tolerance);
     }
 
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubPlane.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubPlane.java
index 7b38e64..c2848b2 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubPlane.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/SubPlane.java
@@ -21,28 +21,27 @@ import org.apache.commons.geometry.core.partitioning.BSPTree;
 import org.apache.commons.geometry.core.partitioning.Hyperplane;
 import org.apache.commons.geometry.core.partitioning.Region;
 import org.apache.commons.geometry.core.partitioning.SubHyperplane;
-import org.apache.commons.geometry.euclidean.oned.Cartesian1D;
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.geometry.euclidean.oned.Point1D;
+import org.apache.commons.geometry.euclidean.twod.Point2D;
 import org.apache.commons.geometry.euclidean.twod.PolygonsSet;
-import org.apache.commons.geometry.euclidean.twod.Cartesian2D;
 
 /** This class represents a sub-hyperplane for {@link Plane}.
  */
-public class SubPlane extends AbstractSubHyperplane<Euclidean3D, Euclidean2D> {
+public class SubPlane extends AbstractSubHyperplane<Point3D, Point2D> {
 
     /** Simple constructor.
      * @param hyperplane underlying hyperplane
      * @param remainingRegion remaining region of the hyperplane
      */
-    public SubPlane(final Hyperplane<Euclidean3D> hyperplane,
-                    final Region<Euclidean2D> remainingRegion) {
+    public SubPlane(final Hyperplane<Point3D> hyperplane,
+                    final Region<Point2D> remainingRegion) {
         super(hyperplane, remainingRegion);
     }
 
     /** {@inheritDoc} */
     @Override
-    protected AbstractSubHyperplane<Euclidean3D, Euclidean2D> buildNew(final Hyperplane<Euclidean3D> hyperplane,
-                                                                       final Region<Euclidean2D> remainingRegion) {
+    protected AbstractSubHyperplane<Point3D, Point2D> buildNew(final Hyperplane<Point3D> hyperplane,
+                                                                       final Region<Point2D> remainingRegion) {
         return new SubPlane(hyperplane, remainingRegion);
     }
 
@@ -53,7 +52,7 @@ public class SubPlane extends AbstractSubHyperplane<Euclidean3D, Euclidean2D> {
      * instance on the minus side of the instance
      */
     @Override
-    public SplitSubHyperplane<Euclidean3D> split(Hyperplane<Euclidean3D> hyperplane) {
+    public SplitSubHyperplane<Point3D> split(Hyperplane<Point3D> hyperplane) {
 
         final Plane otherPlane = (Plane) hyperplane;
         final Plane thisPlane  = (Plane) getHyperplane();
@@ -73,28 +72,28 @@ public class SubPlane extends AbstractSubHyperplane<Euclidean3D, Euclidean2D> {
         }
 
         // the hyperplanes do intersect
-        Cartesian2D p = thisPlane.toSubSpace(inter.toSpace(Cartesian1D.ZERO));
-        Cartesian2D q = thisPlane.toSubSpace(inter.toSpace(Cartesian1D.ONE));
-        Cartesian3D crossP = Cartesian3D.crossProduct(inter.getDirection(), thisPlane.getNormal());
+        Point2D p = thisPlane.toSubSpace(inter.toSpace(Point1D.ZERO));
+        Point2D q = thisPlane.toSubSpace(inter.toSpace(Point1D.ONE));
+        Vector3D crossP = inter.getDirection().crossProduct(thisPlane.getNormal());
         if (crossP.dotProduct(otherPlane.getNormal()) < 0) {
-            final Cartesian2D tmp = p;
+            final Point2D tmp = p;
             p           = q;
             q           = tmp;
         }
-        final SubHyperplane<Euclidean2D> l2DMinus =
+        final SubHyperplane<Point2D> l2DMinus =
             new org.apache.commons.geometry.euclidean.twod.Line(p, q, tolerance).wholeHyperplane();
-        final SubHyperplane<Euclidean2D> l2DPlus =
+        final SubHyperplane<Point2D> l2DPlus =
             new org.apache.commons.geometry.euclidean.twod.Line(q, p, tolerance).wholeHyperplane();
 
-        final BSPTree<Euclidean2D> splitTree = getRemainingRegion().getTree(false).split(l2DMinus);
-        final BSPTree<Euclidean2D> plusTree  = getRemainingRegion().isEmpty(splitTree.getPlus()) ?
-                                               new BSPTree<Euclidean2D>(Boolean.FALSE) :
-                                               new BSPTree<>(l2DPlus, new BSPTree<Euclidean2D>(Boolean.FALSE),
+        final BSPTree<Point2D> splitTree = getRemainingRegion().getTree(false).split(l2DMinus);
+        final BSPTree<Point2D> plusTree  = getRemainingRegion().isEmpty(splitTree.getPlus()) ?
+                                               new BSPTree<Point2D>(Boolean.FALSE) :
+                                               new BSPTree<>(l2DPlus, new BSPTree<Point2D>(Boolean.FALSE),
                                                                         splitTree.getPlus(), null);
 
-        final BSPTree<Euclidean2D> minusTree = getRemainingRegion().isEmpty(splitTree.getMinus()) ?
-                                               new BSPTree<Euclidean2D>(Boolean.FALSE) :
-                                                   new BSPTree<>(l2DMinus, new BSPTree<Euclidean2D>(Boolean.FALSE),
+        final BSPTree<Point2D> minusTree = getRemainingRegion().isEmpty(splitTree.getMinus()) ?
+                                               new BSPTree<Point2D>(Boolean.FALSE) :
+                                                   new BSPTree<>(l2DMinus, new BSPTree<Point2D>(Boolean.FALSE),
                                                                             splitTree.getMinus(), null);
 
         return new SplitSubHyperplane<>(new SubPlane(thisPlane.copySelf(), new PolygonsSet(plusTree, tolerance)),
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 03efa55..9e9d39d 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
@@ -14,32 +14,524 @@
  * 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.Vector;
+import org.apache.commons.geometry.euclidean.EuclideanVector;
+import org.apache.commons.numbers.arrays.LinearCombination;
 
-/**
- * This class implements vectors in a three-dimensional space.
+/** This class represents a vector in three-dimensional Euclidean space.
+ * Instances of this class are guaranteed to be immutable.
  */
-public abstract class Vector3D implements Vector<Euclidean3D> {
+public class Vector3D extends Cartesian3D implements EuclideanVector<Point3D, Vector3D> {
+
+    /** Zero (null) vector (coordinates: 0, 0, 0). */
+    public static final Vector3D ZERO   = new Vector3D(0, 0, 0);
+
+    /** First canonical vector (coordinates: 1, 0, 0). */
+    public static final Vector3D PLUS_X = new Vector3D(1, 0, 0);
+
+    /** Opposite of the first canonical vector (coordinates: -1, 0, 0). */
+    public static final Vector3D MINUS_X = new Vector3D(-1, 0, 0);
+
+    /** Second canonical vector (coordinates: 0, 1, 0). */
+    public static final Vector3D PLUS_Y = new Vector3D(0, 1, 0);
+
+    /** Opposite of the second canonical vector (coordinates: 0, -1, 0). */
+    public static final Vector3D MINUS_Y = new Vector3D(0, -1, 0);
+
+    /** Third canonical vector (coordinates: 0, 0, 1). */
+    public static final Vector3D PLUS_Z = new Vector3D(0, 0, 1);
+
+    /** Opposite of the third canonical vector (coordinates: 0, 0, -1).  */
+    public static final Vector3D MINUS_Z = new Vector3D(0, 0, -1);
+
+ // 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
+
+    /** A vector with all coordinates set to positive infinity. */
+    public static final Vector3D POSITIVE_INFINITY =
+        new Vector3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
 
-    /** Get the abscissa of the vector.
-     * @return abscissa of the vector
-     * @see Cartesian3D#Cartesian3D(double, double, double)
+    /** A vector with all coordinates set to negative infinity. */
+    public static final Vector3D NEGATIVE_INFINITY =
+        new Vector3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+
+    /** Serializable UID */
+    private static final long serialVersionUID = 3695385854431542858L;
+
+    /** 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
+     * @param y ordinate
+     * @param z height
      */
-    public abstract double getX();
+    public Vector3D(double x, double y, double z) {
+        super(x, y, z);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector3D getZero() {
+        return ZERO;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Point3D asPoint() {
+        return Point3D.of(x, y, z);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double getNorm1() {
+        return Math.abs(x) + Math.abs(y) + Math.abs(z);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double getNorm() {
+        // there are no cancellation problems here, so we use the straightforward formula
+        return Math.sqrt ((x * x) + (y * y) + (z * z));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double getNormSq() {
+        // there are no cancellation problems here, so we use the straightforward formula
+        return (x * x) + (y * y) + (z * z);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double getNormInf() {
+        return Math.max(Math.max(Math.abs(x), Math.abs(y)), Math.abs(z));
+    }
 
-    /** Get the ordinate of the vector.
-     * @return ordinate of the vector
-     * @see Cartesian3D#Cartesian3D(double, double, double)
+    /** Get the azimuth of the vector.
+     * @return azimuth (&alpha;) of the vector, between -&pi; and +&pi;
      */
-    public abstract double getY();
+    public double getAlpha() {
+        return Math.atan2(y, x);
+    }
 
-    /** Get the height of the vector.
-     * @return height of the vector
-     * @see Cartesian3D#Cartesian3D(double, double, double)
+    /** Get the elevation of the vector.
+     * @return elevation (&delta;) of the vector, between -&pi;/2 and +&pi;/2
      */
-    public abstract double getZ();
+    public double getDelta() {
+        return Math.asin(z / getNorm());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector3D add(Vector3D v) {
+        return new Vector3D(
+                    x + v.x,
+                    y + v.y,
+                    z + v.z
+                );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector3D add(double factor, Vector3D v) {
+        return new Vector3D(
+                    x + (factor * v.x),
+                    y + (factor * v.y),
+                    z + (factor * v.z)
+                );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector3D subtract(Vector3D v) {
+        return new Vector3D(
+                    x - v.x,
+                    y - v.y,
+                    z - v.z
+                );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector3D subtract(double factor, Vector3D v) {
+        return new Vector3D(
+                    x - (factor * v.x),
+                    y - (factor * v.y),
+                    z - (factor * v.z)
+                );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector3D negate() {
+        return new Vector3D(-x, -y, -z);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector3D normalize() throws IllegalStateException {
+        double s = getNorm();
+        if (s == 0) {
+            throw new IllegalStateException(ZERO_NORM_MSG);
+        }
+        return scalarMultiply(1 / s);
+    }
+
+    /** Get a vector orthogonal to the instance.
+     * <p>There are an infinite number of normalized vectors orthogonal
+     * to the instance. This method picks up one of them almost
+     * arbitrarily. It is useful when one needs to compute a reference
+     * frame with one of the axes in a predefined direction. The
+     * following example shows how to build a frame having the k axis
+     * aligned with the known vector u :
+     * <pre><code>
+     *   Vector3D k = u.normalize();
+     *   Vector3D i = k.orthogonal();
+     *   Vector3D j = Vector3D.crossProduct(k, i);
+     * </code></pre>
+     * @return a new normalized vector orthogonal to the instance
+     * @exception IllegalStateException if the norm of the instance is zero
+     */
+    public Vector3D orthogonal() throws IllegalStateException {
+        double threshold = 0.6 * getNorm();
+        if (threshold == 0) {
+            throw new IllegalStateException(ZERO_NORM_MSG);
+        }
+
+        if (Math.abs(x) <= threshold) {
+            double inverse  = 1 / Math.sqrt(y * y + z * z);
+            return new Vector3D(0, inverse * z, -inverse * y);
+        } else if (Math.abs(y) <= threshold) {
+            double inverse  = 1 / Math.sqrt(x * x + z * z);
+            return new Vector3D(-inverse * z, 0, inverse * x);
+        }
+        double inverse  = 1 / Math.sqrt(x * x + y * y);
+        return new Vector3D(inverse * y, -inverse * x, 0);
+    }
+
+    /** Compute the angular separation between two vectors.
+     * <p>This method computes the angular separation between two
+     * vectors using the dot product for well separated vectors and the
+     * cross product for almost aligned vectors. This allows to have a
+     * good accuracy in all cases, even for vectors very close to each
+     * other.</p>
+     * @param v other vector
+     * @return angular separation between this instance and v
+     * @exception IllegalStateException if either vector has a zero norm
+     */
+    public double angle(Vector3D v) throws IllegalStateException {
+        double normProduct = getNorm() * v.getNorm();
+        if (normProduct == 0) {
+            throw new IllegalStateException(ZERO_NORM_MSG);
+        }
+
+        double dot = dotProduct(v);
+        double threshold = normProduct * 0.9999;
+        if ((dot < -threshold) || (dot > threshold)) {
+            // the vectors are almost aligned, compute using the sine
+            Vector3D cross = crossProduct(v);
+            if (dot >= 0) {
+                return Math.asin(cross.getNorm() / normProduct);
+            }
+            return Math.PI - Math.asin(cross.getNorm() / normProduct);
+        }
+
+        // the vectors are sufficiently separated to use the cosine
+        return Math.acos(dot / normProduct);
+    }
+
+    /** Compute the cross-product of the instance with another vector.
+     * @param v other vector
+     * @return the cross product this ^ v as a new Cartesian3D
+     */
+    public Vector3D crossProduct(final Vector3D v) {
+        return new Vector3D(LinearCombination.value(y, v.z, -z, v.y),
+                            LinearCombination.value(z, v.x, -x, v.z),
+                            LinearCombination.value(x, v.y, -y, v.x));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector3D scalarMultiply(double a) {
+        return new Vector3D(a * x, a * y, a * z);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distance1(Vector3D v) {
+        double dx = Math.abs(v.x - x);
+        double dy = Math.abs(v.y - y);
+        double dz = Math.abs(v.z - z);
+
+        return dx + dy + dz;
+    }
 
+    /** {@inheritDoc} */
+    @Override
+    public double distance(Vector3D v) {
+        return euclideanDistance(v);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distanceInf(Vector3D v) {
+        double dx = Math.abs(v.x - x);
+        double dy = Math.abs(v.y - y);
+        double dz = Math.abs(v.z - z);
+
+        return Math.max(Math.max(dx, dy), dz);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distanceSq(Vector3D v) {
+        double dx = v.x - x;
+        double dy = v.y - y;
+        double dz = v.z - z;
+
+        return (dx * dx) + (dy * dy) + (dz * dz);
+    }
+
+    /** {@inheritDoc}
+     * <p>
+     * The implementation uses specific multiplication and addition
+     * algorithms to preserve accuracy and reduce cancellation effects.
+     * It should be very accurate even for nearly orthogonal vectors.
+     * </p>
+     * @see LinearCombination#value(double, double, double, double, double, double)
+     */
+    @Override
+    public double dotProduct(Vector3D v) {
+        return LinearCombination.value(x, v.x, y, v.y, z, v.z);
+    }
+
+    /**
+     * Get a hashCode for the vector.
+     * <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 642;
+        }
+        return 643 * (164 * Double.hashCode(x) +  3 * Double.hashCode(y) +  Double.hashCode(z));
+    }
+
+    /**
+     * Test for the equality of two vector instances.
+     * <p>
+     * If all coordinates of two vectors are exactly the same, and none are
+     * <code>Double.NaN</code>, the two instances are considered to be equal.
+     * </p>
+     * <p>
+     * <code>NaN</code> coordinates are considered to globally affect the vector
+     * and be equal to each other - i.e, if either (or all) coordinates of the
+     * vector are equal to <code>Double.NaN</code>, the vector is equal to
+     * {@link #NaN}.
+     * </p>
+     *
+     * @param other Object to test for equality to this
+     * @return true if two Vector3D objects are equal, false if
+     *         object is null, not an instance of Vector3D, or
+     *         not equal to this Vector3D instance
+     *
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (other instanceof Vector3D) {
+            final Vector3D rhs = (Vector3D) other;
+            if (rhs.isNaN()) {
+                return this.isNaN();
+            }
+
+            return (x == rhs.x) && (y == rhs.y) && (z == rhs.z);
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "{" + x + "; " + y + "; " + z + "}";
+    }
+
+    /** Computes the dot product between to vectors. This method simply
+     * calls {@code v1.dotProduct(v2)}.
+     * @param v1 first vector
+     * @param v2 second vector
+     * @return the dot product
+     * @see {@link #dotProduct(Vector3D)}
+     */
+    public static double dotProduct(Vector3D v1, Vector3D v2) {
+        return v1.dotProduct(v2);
+    }
+
+    /** Computes the angle in radians between two vectors. This method
+     * simply calls {@code v1.angle(v2)}.
+     * @param v1 first vector
+     * @param v2 second vector
+     * @return the angle between the vectors in radians
+     * @see {@link #angle(Vector3D)}
+     */
+    public static double angle(Vector3D v1, Vector3D v2) {
+        return v1.angle(v2);
+    }
+
+    /** Computes the cross product between two vectors. This method simply
+     * calls {@code v1.crossProduct(v2)}.
+     * @param v1 first vector
+     * @param v2 second vector
+     * @return the computed cross product vector
+     * @see {@link #crossProduct(Vector3D)}
+     */
+    public static Vector3D crossProduct(Vector3D v1, Vector3D v2) {
+        return v1.crossProduct(v2);
+    }
+
+    /** Returns a vector with the given coordinate values.
+     * @param x abscissa (first coordinate value)
+     * @param y abscissa (second coordinate value)
+     * @param z height (third coordinate value)
+     * @return vector instance
+     */
+    public static Vector3D of(double x, double y, double z) {
+        return new Vector3D(x, y, z);
+    }
+
+    /** Returns a vector instance with the given coordinate values.
+     * @param value vector coordinates
+     * @return vector instance
+     */
+    public static Vector3D of(Cartesian3D value) {
+        return new Vector3D(value.x, value.y, value.z);
+    }
+
+    /** Creates a vector from the coordinates in the given 3-element array.
+     * @param v coordinates array
+     * @return new vector
+     * @exception IllegalArgumentException if the array does not have 3 elements
+     */
+    public static Vector3D of(double[] v) {
+        if (v.length != 3) {
+            throw new IllegalArgumentException("Dimension mismatch: " + v.length + " != 3");
+        }
+        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()
+     * @param alpha
+     * @param delta
+     * @return new vector instance with the given azimuthal 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);
+    }
+
+    /** Returns a vector consisting of the linear combination of the inputs.
+     * <p>
+     * A linear combination is the sum of all of the inputs multiplied by their
+     * corresponding scale factors. All inputs are interpreted as vectors. If points
+     * are to be passed, they should be viewed as representing the vector from the
+     * zero point to the given point.
+     * </p>
+     *
+     * @param a scale factor for first coordinate
+     * @param c first coordinate
+     * @return vector with coordinates calculated by {@code a * c}
+     */
+    public static Vector3D linearCombination(double a, Cartesian3D c) {
+        return new Vector3D(a * c.x, a * c.y, a * c.z);
+    }
+
+    /** Returns a vector consisting of the linear combination of the inputs.
+     * <p>
+     * A linear combination is the sum of all of the inputs multiplied by their
+     * corresponding scale factors. All inputs are interpreted as vectors. If points
+     * are to be passed, they should be viewed as representing the vector from the
+     * zero point to the given point.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @return vector with coordinates calculated by {@code (a1 * c1) + (a2 * c2)}
+     */
+    public static Vector3D linearCombination(double a1, Cartesian3D c1, double a2, Cartesian3D c2) {
+        return new Vector3D(
+                LinearCombination.value(a1, c1.x, a2, c2.x),
+                LinearCombination.value(a1, c1.y, a2, c2.y),
+                LinearCombination.value(a1, c1.z, a2, c2.z));
+    }
+
+    /** Returns a vector consisting of the linear combination of the inputs.
+     * <p>
+     * A linear combination is the sum of all of the inputs multiplied by their
+     * corresponding scale factors. All inputs are interpreted as vectors. If points
+     * are to be passed, they should be viewed as representing the vector from the
+     * zero point to the given point.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @param a3 scale factor for third coordinate
+     * @param c3 third coordinate
+     * @return vector with coordinates calculated by {@code (a1 * c1) + (a2 * c2) + (a3 * c3)}
+     */
+    public static Vector3D linearCombination(double a1, Cartesian3D c1, double a2, Cartesian3D c2,
+            double a3, Cartesian3D c3) {
+        return new Vector3D(
+                LinearCombination.value(a1, c1.x, a2, c2.x, a3, c3.x),
+                LinearCombination.value(a1, c1.y, a2, c2.y, a3, c3.y),
+                LinearCombination.value(a1, c1.z, a2, c2.z, a3, c3.z));
+    }
+
+    /** Returns a vector consisting of the linear combination of the inputs.
+     * <p>
+     * A linear combination is the sum of all of the inputs multiplied by their
+     * corresponding scale factors. All inputs are interpreted as vectors. If points
+     * are to be passed, they should be viewed as representing the vector from the
+     * zero point to the given point.
+     * </p>
+     *
+     * @param a1 scale factor for first coordinate
+     * @param c1 first coordinate
+     * @param a2 scale factor for second coordinate
+     * @param c2 second coordinate
+     * @param a3 scale factor for third coordinate
+     * @param c3 third coordinate
+     * @param a4 scale factor for fourth coordinate
+     * @param c4 fourth coordinate
+     * @return point with coordinates calculated by {@code (a1 * c1) + (a2 * c2) + (a3 * c3) + (a4 * c4)}
+     */
+    public static Vector3D linearCombination(double a1, Cartesian3D c1, double a2, Cartesian3D c2,
+            double a3, Cartesian3D c3, double a4, Cartesian3D c4) {
+        return new Vector3D(
+                LinearCombination.value(a1, c1.x, a2, c2.x, a3, c3.x, a4, c4.x),
+                LinearCombination.value(a1, c1.y, a2, c2.y, a3, c3.y, a4, c4.y),
+                LinearCombination.value(a1, c1.z, a2, c2.z, a3, c3.z, a4, c4.z));
+    }
 }
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 483d341..29aedcd 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
@@ -14,157 +14,51 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.geometry.euclidean.twod;
 
-import java.text.NumberFormat;
+package org.apache.commons.geometry.euclidean.twod;
 
-import org.apache.commons.geometry.core.Point;
-import org.apache.commons.geometry.core.Space;
-import org.apache.commons.geometry.core.Vector;
-import org.apache.commons.numbers.arrays.LinearCombination;
+import org.apache.commons.geometry.core.Spatial;
 
-/** This class represents a 2D point or a 2D vector.
- * <p>An instance of Cartesian2D represents the point with the corresponding
- * coordinates.</p>
- * <p>An instance of Cartesian2D also represents the vector which begins at
- * the origin and ends at the point corresponding to the coordinates.</p>
- * <p>Instances of this class are guaranteed to be immutable.</p>
+/** This class represents a set of Cartesian coordinates in
+ * two-dimensional Euclidean space.
  */
-public class Cartesian2D extends Vector2D implements Point<Euclidean2D> {
-
-    /** Origin (coordinates: 0, 0). */
-    public static final Cartesian2D ZERO   = new Cartesian2D(0, 0);
-
-    // CHECKSTYLE: stop ConstantName
-    /** A vector with all coordinates set to NaN. */
-    public static final Cartesian2D NaN = new Cartesian2D(Double.NaN, Double.NaN);
-    // CHECKSTYLE: resume ConstantName
-
-    /** A vector with all coordinates set to positive infinity. */
-    public static final Cartesian2D POSITIVE_INFINITY =
-        new Cartesian2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+public abstract class Cartesian2D implements Spatial {
 
-    /** A vector with all coordinates set to negative infinity. */
-    public static final Cartesian2D NEGATIVE_INFINITY =
-        new Cartesian2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+    /** Serializable UID */
+    private static final long serialVersionUID = 2918583078965478552L;
 
-    /** Serializable UID. */
-    private static final long serialVersionUID = 266938651998679754L;
+    /** Abscissa (first coordinate) */
+    protected final double x;
 
-    /** Error message when norms are zero. */
-    private static final String ZERO_NORM_MSG = "Norm is zero";
+    /** Ordinate (second coordinate) */
+    protected final double y;
 
-    /** Abscissa. */
-    private final double x;
-
-    /** Ordinate. */
-    private final double y;
-
-    /** Simple constructor.
-     * Build a vector from its coordinates
-     * @param x abscissa
-     * @param y ordinate
-     * @see #getX()
-     * @see #getY()
+    /**
+     * Simple Cartesian constructor.
+     * @param x abscissa (first coordinate)
+     * @param y ordinate (second coordinate)
      */
-    public Cartesian2D(double x, double y) {
+    protected Cartesian2D(double x, double y) {
         this.x = x;
         this.y = y;
     }
 
-    /** Simple constructor.
-     * Build a vector from its coordinates
-     * @param v coordinates array
-     * @exception DimensionMismatchException if array does not have 2 elements
-     * @see #toArray()
-     */
-    public Cartesian2D(double[] v) throws IllegalArgumentException {
-        if (v.length != 2) {
-            throw new IllegalArgumentException("Dimension mismatch: " + v.length + " != 2");
-        }
-        this.x = v[0];
-        this.y = v[1];
-    }
-
-    /** Multiplicative constructor
-     * Build a vector from another one and a scale factor.
-     * The vector built will be a * u
-     * @param a scale factor
-     * @param u base (unscaled) vector
-     */
-    public Cartesian2D(double a, Cartesian2D u) {
-        this.x = a * u.x;
-        this.y = a * u.y;
-    }
-
-    /** Linear constructor
-     * Build a vector from two other ones and corresponding scale factors.
-     * The vector built will be a1 * u1 + a2 * u2
-     * @param a1 first scale factor
-     * @param u1 first base (unscaled) vector
-     * @param a2 second scale factor
-     * @param u2 second base (unscaled) vector
+    /** Returns the abscissa (first coordinate value) of the instance.
+     * @return the abscissa
      */
-    public Cartesian2D(double a1, Cartesian2D u1, double a2, Cartesian2D u2) {
-        this.x = a1 * u1.x + a2 * u2.x;
-        this.y = a1 * u1.y + a2 * u2.y;
-    }
-
-    /** Linear constructor
-     * Build a vector from three other ones and corresponding scale factors.
-     * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
-     * @param a1 first scale factor
-     * @param u1 first base (unscaled) vector
-     * @param a2 second scale factor
-     * @param u2 second base (unscaled) vector
-     * @param a3 third scale factor
-     * @param u3 third base (unscaled) vector
-     */
-    public Cartesian2D(double a1, Cartesian2D u1, double a2, Cartesian2D u2,
-                   double a3, Cartesian2D u3) {
-        this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x;
-        this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y;
-    }
-
-    /** Linear constructor
-     * Build a vector from four other ones and corresponding scale factors.
-     * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
-     * @param a1 first scale factor
-     * @param u1 first base (unscaled) vector
-     * @param a2 second scale factor
-     * @param u2 second base (unscaled) vector
-     * @param a3 third scale factor
-     * @param u3 third base (unscaled) vector
-     * @param a4 fourth scale factor
-     * @param u4 fourth base (unscaled) vector
-     */
-    public Cartesian2D(double a1, Cartesian2D u1, double a2, Cartesian2D u2,
-                   double a3, Cartesian2D u3, double a4, Cartesian2D u4) {
-        this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x;
-        this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y + a4 * u4.y;
-    }
-
-    /** Get the abscissa of the vector.
-     * @return abscissa of the vector
-     * @see #Cartesian2D(double, double)
-     */
-    @Override
     public double getX() {
         return x;
     }
 
-    /** Get the ordinate of the vector.
-     * @return ordinate of the vector
-     * @see #Cartesian2D(double, double)
+    /** Returns the ordinate (second coordinate value) of the instance.
+     * @return the ordinate
      */
-    @Override
     public double getY() {
         return y;
     }
 
-    /** Get the vector coordinates as a dimension 2 array.
-     * @return vector coordinates
-     * @see #Cartesian2D(double[])
+    /** Get the coordinates for this instance as a dimension 2 array.
+     * @return coordinates for this instance
      */
     public double[] toArray() {
         return new double[] { x, y };
@@ -172,122 +66,8 @@ public class Cartesian2D extends Vector2D implements Point<Euclidean2D> {
 
     /** {@inheritDoc} */
     @Override
-    public Space getSpace() {
-        return Euclidean2D.getInstance();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian2D getZero() {
-        return ZERO;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNorm1() {
-        return Math.abs(x) + Math.abs(y);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNorm() {
-        return Math.sqrt (x * x + y * y);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNormSq() {
-        return x * x + y * y;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double getNormInf() {
-        return Math.max(Math.abs(x), Math.abs(y));
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian2D add(Vector<Euclidean2D> v) {
-        Cartesian2D v2 = (Cartesian2D) v;
-        return new Cartesian2D(x + v2.getX(), y + v2.getY());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian2D add(double factor, Vector<Euclidean2D> v) {
-        Cartesian2D v2 = (Cartesian2D) v;
-        return new Cartesian2D(x + factor * v2.getX(), y + factor * v2.getY());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian2D subtract(Vector<Euclidean2D> p) {
-        Cartesian2D p3 = (Cartesian2D) p;
-        return new Cartesian2D(x - p3.x, y - p3.y);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian2D subtract(double factor, Vector<Euclidean2D> v) {
-        Cartesian2D v2 = (Cartesian2D) v;
-        return new Cartesian2D(x - factor * v2.getX(), y - factor * v2.getY());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian2D normalize() throws IllegalStateException {
-        double s = getNorm();
-        if (s == 0) {
-            throw new IllegalStateException(ZERO_NORM_MSG);
-        }
-        return scalarMultiply(1 / s);
-    }
-
-    /** Compute the angular separation between two vectors.
-     * <p>This method computes the angular separation between two
-     * vectors using the dot product for well separated vectors and the
-     * cross product for almost aligned vectors. This allows to have a
-     * good accuracy in all cases, even for vectors very close to each
-     * other.</p>
-     * @param v1 first vector
-     * @param v2 second vector
-     * @return angular separation between v1 and v2
-     * @exception IllegalArgumentException if either vector has a zero norm
-     */
-    public static double angle(Cartesian2D v1, Cartesian2D v2) throws IllegalArgumentException {
-
-        double normProduct = v1.getNorm() * v2.getNorm();
-        if (normProduct == 0) {
-            throw new IllegalArgumentException(ZERO_NORM_MSG);
-        }
-
-        double dot = v1.dotProduct(v2);
-        double threshold = normProduct * 0.9999;
-        if ((dot < -threshold) || (dot > threshold)) {
-            // the vectors are almost aligned, compute using the sine
-            final double n = Math.abs(LinearCombination.value(v1.x, v2.y, -v1.y, v2.x));
-            if (dot >= 0) {
-                return Math.asin(n / normProduct);
-            }
-            return Math.PI - Math.asin(n / normProduct);
-        }
-
-        // the vectors are sufficiently separated to use the cosine
-        return Math.acos(dot / normProduct);
-
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian2D negate() {
-        return new Cartesian2D(-x, -y);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Cartesian2D scalarMultiply(double a) {
-        return new Cartesian2D(a * x, a * y);
+    public int getDimension() {
+        return 2;
     }
 
     /** {@inheritDoc} */
@@ -302,190 +82,13 @@ public class Cartesian2D extends Vector2D implements Point<Euclidean2D> {
         return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y));
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public double distance1(Vector<Euclidean2D> p) {
-        Cartesian2D p3 = (Cartesian2D) p;
-        final double dx = Math.abs(p3.x - x);
-        final double dy = Math.abs(p3.y - y);
-        return dx + dy;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distance(Point<Euclidean2D> p) {
-        return distance((Cartesian2D) p);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distance(Vector<Euclidean2D> v) {
-        return distance((Cartesian2D) v);
-    }
-
-    /** Compute the distance between the instance and other coordinates.
-     * @param c other coordinates
-     * @return the distance between the instance and c
+    /** Returns the Euclidean distance from this value to the given value.
+     * @param other the set of coordinates to compute the distance to
+     * @return Euclidean distance
      */
-    public double distance(Cartesian2D c) {
-        final double dx = c.x - x;
-        final double dy = c.y - y;
-        return Math.sqrt(dx * dx + dy * dy);
+    protected double euclideanDistance(Cartesian2D other) {
+        double dx = x - other.x;
+        double dy = y - other.y;
+        return Math.sqrt((dx * dx) + (dy * dy));
     }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distanceInf(Vector<Euclidean2D> p) {
-        Cartesian2D p3 = (Cartesian2D) p;
-        final double dx = Math.abs(p3.x - x);
-        final double dy = Math.abs(p3.y - y);
-        return Math.max(dx, dy);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double distanceSq(Vector<Euclidean2D> p) {
-        Cartesian2D p3 = (Cartesian2D) p;
-        final double dx = p3.x - x;
-        final double dy = p3.y - y;
-        return dx * dx + dy * dy;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public double dotProduct(final Vector<Euclidean2D> v) {
-        final Cartesian2D v2 = (Cartesian2D) v;
-        return LinearCombination.value(x, v2.x, y, v2.y);
-    }
-
-    /**
-     * Compute the cross-product of the instance and the given vector.
-     * <p>
-     * The cross product can be used to determine the location of a point
-     * with regard to the line formed by (p1, p2) and is calculated as:
-     * \[
-     *    P = (x_2 - x_1)(y_3 - y_1) - (y_2 - y_1)(x_3 - x_1)
-     * \]
-     * with \(p3 = (x_3, y_3)\) being this instance.
-     * <p>
-     * If the result is 0, the points are collinear, i.e. lie on a single straight line L;
-     * if it is positive, this point lies to the left, otherwise to the right of the line
-     * formed by (p1, p2).
-     *
-     * @param p1 first point of the line
-     * @param p2 second point of the line
-     * @return the cross-product
-     *
-     * @see <a href="http://en.wikipedia.org/wiki/Cross_product">Cross product (Wikipedia)</a>
-     */
-    public double crossProduct(final Cartesian2D p1, final Cartesian2D p2) {
-        final double x1 = p2.getX() - p1.getX();
-        final double y1 = getY() - p1.getY();
-        final double x2 = getX() - p1.getX();
-        final double y2 = p2.getY() - p1.getY();
-        return LinearCombination.value(x1, y1, -x2, y2);
-    }
-
-    /** Compute the distance between two points according to the L<sub>2</sub> norm.
-     * <p>Calling this method is equivalent to calling:
-     * <code>p1.subtract(p2).getNorm()</code> except that no intermediate
-     * vector is built</p>
-     * @param p1 first point
-     * @param p2 second point
-     * @return the distance between p1 and p2 according to the L<sub>2</sub> norm
-     */
-    public static double distance(Cartesian2D p1, Cartesian2D p2) {
-        return p1.distance(p2);
-    }
-
-    /** Compute the distance between two points according to the L<sub>&infin;</sub> norm.
-     * <p>Calling this method is equivalent to calling:
-     * <code>p1.subtract(p2).getNormInf()</code> except that no intermediate
-     * vector is built</p>
-     * @param p1 first point
-     * @param p2 second point
-     * @return the distance between p1 and p2 according to the L<sub>&infin;</sub> norm
-     */
-    public static double distanceInf(Cartesian2D p1, Cartesian2D p2) {
-        return p1.distanceInf(p2);
-    }
-
-    /** Compute the square of the distance between two points.
-     * <p>Calling this method is equivalent to calling:
-     * <code>p1.subtract(p2).getNormSq()</code> except that no intermediate
-     * vector is built</p>
-     * @param p1 first point
-     * @param p2 second point
-     * @return the square of the distance between p1 and p2
-     */
-    public static double distanceSq(Cartesian2D p1, Cartesian2D p2) {
-        return p1.distanceSq(p2);
-    }
-
-    /**
-     * Test for the equality of two 2D instances.
-     * <p>
-     * If all coordinates of two 2D vectors are exactly the same, and none are
-     * <code>Double.NaN</code>, the two 2D instances are considered to be equal.
-     * </p>
-     * <p>
-     * <code>NaN</code> coordinates are considered to affect globally the vector
-     * and be equals to each other - i.e, if either (or all) coordinates of the
-     * 2D vector are equal to <code>Double.NaN</code>, the 2D vector is equal to
-     * {@link #NaN}.
-     * </p>
-     *
-     * @param other Object to test for equality to this
-     * @return true if two 2D Cartesian objects are equal, false if
-     *         object is null, not an instance of Cartesian2D, or
-     *         not equal to this Cartesian2D instance
-     *
-     */
-    @Override
-    public boolean equals(Object other) {
-
-        if (this == other) {
-            return true;
-        }
-
-        if (other instanceof Cartesian2D) {
-            final Cartesian2D rhs = (Cartesian2D)other;
-            if (rhs.isNaN()) {
-                return this.isNaN();
-            }
-
-            return (x == rhs.x) && (y == rhs.y);
-        }
-        return false;
-    }
-
-    /**
-     * Get a hashCode for the 2D 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 542;
-        }
-        return 122 * (76 * Double.hashCode(x) +  Double.hashCode(y));
-    }
-
-    /** Get a string representation of this vector.
-     * @return a string representation of this vector
-     */
-    @Override
-    public String toString() {
-        return toString(NumberFormat.getInstance());
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public String toString(final NumberFormat format) {
-        return "{" + format.format(x) + "; " + format.format(y) + "}";
-    }
-
 }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Euclidean2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Euclidean2D.java
deleted file mode 100644
index 83ec10c..0000000
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Euclidean2D.java
+++ /dev/null
@@ -1,75 +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.
... 17390 lines suppressed ...

-- 
To stop receiving notification emails like this one, please contact
erans@apache.org.