You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by GitBox <gi...@apache.org> on 2018/05/22 14:18:42 UTC

[GitHub] asfgit closed pull request #2: GEOMETRY-2: Points and Vectors API Updates

asfgit closed pull request #2: GEOMETRY-2: Points and Vectors API Updates
URL: https://github.com/apache/commons-geometry/pull/2
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

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 @@
     /** 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 @@
 
 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 @@
      * @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.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 @@
     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 @@ protected AbstractRegion(final double tolerance) {
      * @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 @@ protected AbstractRegion(final BSPTree<S> tree, final double tolerance) {
      * 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 @@ protected AbstractRegion(final Collection<SubHyperplane<S>> boundary, final doub
             // 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 int compare(final SubHyperplane<S> o1, final SubHyperplane<S> o2) {
             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 void visitLeafNode(final BSPTree<S> node) {
      * 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 AbstractRegion(final Hyperplane<S>[] hyperplanes, final double tolerance)
             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 AbstractRegion(final Hyperplane<S>[] hyperplanes, final double tolerance)
 
     /** {@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 double getTolerance() {
      * @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 @@ private void insertCuts(final BSPTree<S> node, final Collection<SubHyperplane<S>
         }
 
         // 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 @@ private void insertCuts(final BSPTree<S> node, final Collection<SubHyperplane<S>
 
     /** {@inheritDoc} */
     @Override
-    public AbstractRegion<S, T> copySelf() {
+    public AbstractRegion<P, S> copySelf() {
         return buildNew(tree.copySelf());
     }
 
@@ -257,7 +255,7 @@ public boolean isEmpty() {
 
     /** {@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 boolean isFull() {
 
     /** {@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 boolean isFull(final BSPTree<S> node) {
 
     /** {@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 Location checkPoint(final Point<S> point) {
      * 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 @@ protected Location checkPoint(final BSPTree<S> node, final Point<S> point) {
 
     /** {@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 @@ protected Location checkPoint(final BSPTree<S> node, final Point<S> point) {
     /** {@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 @@ protected void setSize(final double size) {
 
     /** {@inheritDoc} */
     @Override
-    public Point<S> getBarycenter() {
+    public P getBarycenter() {
         if (barycenter == null) {
             computeGeometricalProperties();
         }
@@ -406,14 +383,7 @@ protected void setSize(final double size) {
     /** 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 @@ protected void setBarycenter(final Point<S> barycenter) {
 
     /** {@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 @@ protected void setBarycenter(final Point<S> barycenter) {
      * @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 @@ protected void setBarycenter(final Point<S> barycenter) {
      * @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 @@ protected void setBarycenter(final Point<S> barycenter) {
      * @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 @@
 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 @@
  * 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 @@ protected AbstractSubHyperplane(final Hyperplane<S> hyperplane,
      * @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 @@ protected AbstractSubHyperplane(final Hyperplane<S> hyperplane,
      * @return underlying hyperplane
      */
     @Override
-    public Hyperplane<S> getHyperplane() {
+    public Hyperplane<P> getHyperplane() {
         return hyperplane;
     }
 
@@ -81,7 +81,7 @@ protected AbstractSubHyperplane(final Hyperplane<S> hyperplane,
      * 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 double getSize() {
 
     /** {@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 double getSize() {
      * @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 double getSize() {
      * @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 double getSize() {
 
     /** {@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.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 @@
  * 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 BSPTree(final Object attribute) {
      * @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 BSPTree(final SubHyperplane<S> cut, final BSPTree<S> plus, final BSPTree<
      * 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 boolean insertCut(final Hyperplane<S> hyperplane) {
      * 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 boolean insertCut(final Hyperplane<S> hyperplane) {
     /** 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 boolean insertCut(final Hyperplane<S> hyperplane) {
      * @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 boolean insertCut(final Hyperplane<S> hyperplane) {
      * @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 Object getAttribute() {
     /** 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 void visit(final BSPTreeVisitor<S> visitor) {
      * @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 void visit(final BSPTreeVisitor<S> visitor) {
      * 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 void visit(final BSPTreeVisitor<S> visitor) {
      * @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 void visit(final BSPTreeVisitor<S> visitor) {
      * 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 @@ private void condense() {
      * 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 @@ private void condense() {
      * 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 @@ private void condense() {
             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 @@ private void condense() {
      * 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 @@ private void condense() {
      * </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 @@ private void condense() {
      * 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 @@ private void condense() {
         }
         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 @@ private void condense() {
         }
         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 @@ private void condense() {
      * 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 void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild,
         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 void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild,
 
                 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 void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild,
      * 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 void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild,
      * @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 @@ private void chopOffMinus(final Hyperplane<S> hyperplane, final VanishingCutHand
 
             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 @@ private void chopOffMinus(final Hyperplane<S> hyperplane, final VanishingCutHand
      * @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 @@ private void chopOffPlus(final Hyperplane<S> hyperplane, final VanishingCutHandl
 
             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 @@
  *   </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 @@
      * {@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 @@
      * @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 @@
      * @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 @@
      * 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 @@
      * 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 @@ public void visitInternalNode(BSPTree<S> node) {
             // 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 @@ public void visitInternalNode(BSPTree<S> node) {
 
         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 @@ public void visitInternalNode(BSPTree<S> node) {
 
     /** {@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 @@
      * @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 BoundaryProjection(final Point<S> original, final Point<S> projected, fin
     /** 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.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 @@
     /** 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 @@
 
     /** {@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 @@ public Order visitOrder(final BSPTree<S> node) {
 
     /** {@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 @@ public void visitInternalNode(final BSPTree<S> node) {
                 // 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 @@ public void visitInternalNode(final BSPTree<S> node) {
 
     /** {@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 @@ public void visitLeafNode(final BSPTree<S> node) {
     /** 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 @@ public void visitLeafNode(final BSPTree<S> node) {
      * @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 @@ public void visitLeafNode(final BSPTree<S> node) {
      * @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 @@ private void addRegion(final SubHyperplane<S> sub, final List<Region<T>> list) {
      * @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 @@ private boolean belongsToPart(final Point<S> point, final Hyperplane<S> hyperpla
      * @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 @@
 
     /** {@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 @@ public void visitInternalNode(final BSPTree<S> node) {
 
     /** {@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 @@
 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 @@
      * @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 @@
      * @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 @@ private void characterize(final BSPTree<S> node, final SubHyperplane<S> sub,
                 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 @@ private void characterize(final BSPTree<S> node, final SubHyperplane<S> sub,
      * @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 @@ private void addOutsideTouching(final SubHyperplane<S> sub,
      * @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 @@ public boolean touchOutside() {
      * @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 @@ public boolean touchOutside() {
      * </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 @@ public boolean touchInside() {
      * @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 @@ public boolean touchInside() {
      * </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 @@
  * 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 @@
  * 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 @@
      * 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 @@
      * 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 @@
 
  * <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 @@
      * 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 @@
      * @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 @@
      * @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 @@
     /** 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 @@
      * @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 @@ public void recurseSides(final BSPTree<S> node, final SubHyperplane<S> sub) {
             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.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 NodesSet() {
     /** 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 void add(final BSPTree<S> node) {
     /** 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 @@
  * 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 @@
      * @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 @@
      * 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 @@
      * 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 @@
      * 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 @@
      * @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 @@
     /** 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 @@
      * @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.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 RegionFactory() {
      * @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 RegionFactory() {
                 // 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 RegionFactory() {
      * 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 RegionFactory() {
      * 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 RegionFactory() {
      * 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 RegionFactory() {
      * 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 RegionFactory() {
      * 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 RegionFactory() {
      * @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 RegionFactory() {
      * @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 RegionFactory() {
     }
 
     /** 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 RegionFactory() {
     }
 
     /** 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 RegionFactory() {
     }
 
     /** 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 RegionFactory() {
     }
 
     /** 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 RegionFactory() {
 
         /** {@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 RegionFactory() {
     }
 
     /** 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 void visitLeafNode(final BSPTree<S> node) {
 
         /** {@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 @@
  * 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 @@
      * 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 @@
      * 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 @@
  *   </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 @@
      * <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.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 @@
     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 TreeBuilder(final String type, final String s)
      * @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 @@ protected boolean getBoolean()
     /** Get the built tree.
      * @return built tree
      */
-    public BSPTree<S> getTree() {
+    public BSPTree<P> getTree() {
         return root;
     }
 
@@ -158,7 +155,7 @@ public double getTolerance() {
      * @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 @@
 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 @@ protected Formatter getFormatter() {
     /** 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 void visitInternalNode(final BSPTree<S> node) {
 
     /** {@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 void visitLeafNode(final BSPTree<S> node) {
      * @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 @@
 
 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 @@
      * @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 String writeAsString(BSPTree<S> tree) {
 
     /** {@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 void visitInternalNode(BSPTree<S> node) {
 
     /** {@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 void visitLeafNode(BSPTree<S> node) {
      * 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 @@ protected void writeLinePrefix(BSPTree<S> node) {
      * @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 @@ protected void write(String str) {
     /** 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 @@
 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 @@
  * @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 @@
 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 @@
  * @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.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 @@
  * @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 WelzlEncloser(final double tolerance, final SupportBallGenerator<S, P> ge
      * @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 WelzlEncloser(final double tolerance, final SupportBallGenerator<S, P> ge
             // 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 WelzlEncloser(final double tolerance, final SupportBallGenerator<S, P> ge
      * @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 WelzlEncloser(final double tolerance, final SupportBallGenerator<S, P> ge
      * @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 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 @@
                                                                         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 @@
                         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 @@
 
 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 @@
                     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.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 @@
     @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 void testReducingBall1() {
 
     @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 void testLargeSamples() {
         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 @@ private void checkDisk(List<Cartesian2D> points, List<Cartesian2D> refSupport) {
 
         // 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 @@ private void checkDisk(List<Cartesian2D> points, List<Cartesian2D> refSupport) {
 
     }
 
-    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.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 @@
     @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 void testLargeSamples() throws IOException {
             // 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 void testLargeSamples() throws IOException {
         }
     }
 
-    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 @@ private void checkSphere(List<Cartesian3D> points, double refRadius) {
 
     }
 
-    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.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 @@
 
     @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 void testSupport0Point() {
 
     @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 void testSupport1Point() {
 
     @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 void testRandom() {
         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 void testRandom() {
 
     @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 void testDegeneratedCase() {
         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 @@
 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 void testSupport0Point() {
 
     @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 void testSupport1Point() {
 
     @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 void testRandom() {
         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/test/java/org/apache/commons/geometry/euclidean/oned/Euclidean1DTest.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java
similarity index 52%
rename from commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Euclidean1DTest.java
rename to commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java
index 924f02c..0622c89 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Euclidean1DTest.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java
@@ -14,30 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.geometry.euclidean.oned;
+package org.apache.commons.geometry.euclidean;
 
-import org.apache.commons.geometry.core.GeometryTestUtils;
-import org.apache.commons.geometry.core.Space;
-import org.junit.Assert;
-import org.junit.Test;
+import org.apache.commons.geometry.core.Vector;
 
-public class Euclidean1DTest {
-
-    @Test
-    public void testDimension() {
-        Assert.assertEquals(1, Euclidean1D.getInstance().getDimension());
-    }
-
-    @Test(expected=UnsupportedOperationException.class)
-    public void testSubSpace() {
-        Euclidean1D.getInstance().getSubSpace();
-    }
-
-    @Test
-    public void testSerialization() {
-        Space e1 = Euclidean1D.getInstance();
-        Space deserialized = (Space) GeometryTestUtils.serializeAndRecover(e1);
-        Assert.assertTrue(e1 == deserialized);
-    }
+/** Represents a vector in a Euclidean space of any dimension.
+ *
+ * @param <P> Point implementation type
+ * @param <V> Vector implementation type
+ */
+public interface EuclideanVector<P extends EuclideanPoint<P, V>, V extends EuclideanVector<P, V>> extends Vector<V> {
 
+    /** 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
+     */
+    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 boolean isNaN() {
     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.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 @@
 
 /** 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 IntervalsSet(final double lower, final double upper, final double toleran
      * @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 IntervalsSet(final BSPTree<Euclidean1D> tree, final double tolerance) {
      * @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 IntervalsSet(final Collection<SubHyperplane<Euclidean1D>> boundary,
      * @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 IntervalsSet(final Collection<SubHyperplane<Euclidean1D>> boundary,
                 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 IntervalsSet(final Collection<SubHyperplane<Euclidean1D>> boundary,
 
     /** {@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 IntervalsSet buildNew(final BSPTree<Euclidean1D> tree) {
     @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 @@ protected void computeGeometricalProperties() {
             }
             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 @@ protected void computeGeometricalProperties() {
      * 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 double getInf() {
      * 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 double getSup() {
     /** {@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 double getSup() {
      * @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 @@ private Cartesian1D finiteOrNullPoint(final double x) {
      * @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 @@ private Cartesian1D finiteOrNullPoint(final double x) {
      * @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 @@ private Cartesian1D finiteOrNullPoint(final double x) {
      * @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 @@ private boolean isIntervalStart(final BSPTree<Euclidean1D> node) {
      * @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 @@ private boolean isIntervalEnd(final BSPTree<Euclidean1D> node) {
      * @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 @@ private boolean isIntervalEnd(final BSPTree<Euclidean1D> node) {
      * @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 @@ private boolean isIntervalEnd(final BSPTree<Euclidean1D> node) {
      * @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 @@ private boolean isIntervalEnd(final BSPTree<Euclidean1D> node) {
      * @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 @@ private boolean isIntervalEnd(final BSPTree<Euclidean1D> node) {
      * @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 @@ private boolean isBeforeParent(final BSPTree<Euclidean1D> node) {
      * @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 @@ private boolean isAfterParent(final BSPTree<Euclidean1D> node) {
      * @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 @@ private boolean isAfterParent(final BSPTree<Euclidean1D> node) {
      * @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 @@ private boolean isAfterParent(final BSPTree<Euclidean1D> node) {
      * @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 @@ private boolean isDirect(final BSPTree<Euclidean1D> node) {
      * @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 @@ private double getAngle(final BSPTree<Euclidean1D> node) {
     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 @@ private double getAngle(final BSPTree<Euclidean1D> node) {
         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 @@ private void selectPending() {
             }
 
             // 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 @@
  * 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 @@
      * 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 OrientedPoint copySelf() {
         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 IntervalsSet wholeSpace() {
 
     /** {@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 double getTolerance() {
     /** 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 @@
  * 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 boolean isEmpty() {
 
     /** {@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 boolean isEmpty() {
         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/package-info.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/package-info.java
new file mode 100644
index 0000000..2ed2a4c
--- /dev/null
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides basic interfaces for Euclidean components.
+ * </p>
+ */
+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 double getZ() {
 
     /** {@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 boolean isInfinite() {
         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 @@
  * 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 @@
      * @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 Line(final Line line) {
      * @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 Line revert() {
     /** 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 Cartesian3D getOrigin() {
      * @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 double getAbscissa(final Cartesian3D point) {
      * @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 Cartesian3D toSpace(final Point<Euclidean1D> point) {
      * @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 Cartesian1D toSubSpace(final Cartesian3D point) {
      * @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 Cartesian3D toSpace(final Cartesian1D point) {
      * @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 boolean isSimilarTo(final Line line) {
      * @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 boolean contains(final Cartesian3D p) {
      * @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 double distance(final Cartesian3D p) {
      */
     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 double distance(final Line line) {
      * @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 Cartesian3D closestPoint(final Line line) {
             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 Cartesian3D closestPoint(final Line line) {
      * @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 @@
 
 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 @@
 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 OutlineExtractor(final Cartesian3D u, final Cartesian3D v) {
         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 OutlineExtractor(final Cartesian3D u, final Cartesian3D v) {
             }
             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 OutlineExtractor(final Cartesian3D u, final Cartesian3D v) {
      * @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 @@ private boolean pointIsBetween(final Cartesian2D[] loop, final int n, final int
     }
 
     /** 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 @@ private boolean pointIsBetween(final Cartesian2D[] loop, final int n, final int
          * @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 void visitInternalNode(final BSPTree<Euclidean3D> node) {
 
         /** {@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 @@ private void addContribution(final SubHyperplane<Euclidean3D> facet, final boole
                 }
 
                 // 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 @@ private void addContribution(final SubHyperplane<Euclidean3D> facet, final boole
                 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 @@
      * @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 Plane(final Cartesian3D normal, final double tolerance)
      * @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 Plane(final Cartesian3D p, final Cartesian3D normal, final double toleran
      * @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 Plane copySelf() {
      * @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 void reset(final Plane original) {
      * @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 @@ private void setFrame() {
      * @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 Cartesian3D getOrigin() {
      * @see #getU
      * @see #getV
      */
-    public Cartesian3D getNormal() {
+    public Vector3D getNormal() {
         return w;
     }
 
@@ -189,7 +186,7 @@ public Cartesian3D getNormal() {
      * @see #getV
      * @see #getNormal
      */
-    public Cartesian3D getU() {
+    public Vector3D getU() {
         return u;
     }
 
@@ -201,13 +198,13 @@ public Cartesian3D getU() {
      * @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 double getTolerance() {
      * 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 Cartesian3D toSpace(final Cartesian2D point) {
      * @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 Cartesian3D getPointAt(final Cartesian2D inPlane, final double offset) {
      * @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 boolean isSimilarTo(final Plane plane) {
      * @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 Plane rotate(final Cartesian3D center, final Rotation rotation) {
      * @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 Plane translate(final Cartesian3D translation) {
      * @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 Cartesian3D intersection(final Line line) {
      * 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 Line intersection(final Plane other) {
      * @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 static Cartesian3D intersection(final Plane plane1, final Plane plane2, f
         }
 
         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 PolyhedronsSet wholeSpace() {
      * @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 double getOffset(final Plane plane) {
         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 double getOffset(final Plane plane) {
      * @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 double getOffset(final Point<Euclidean3D> point) {
      * 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.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 PolyhedronsSet(final double tolerance) {
      * @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 PolyhedronsSet(final BSPTree<Euclidean3D> tree, final double tolerance) {
      * 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 PolyhedronsSet(final Collection<SubHyperplane<Euclidean3D>> boundary,
      * @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 PolyhedronsSet(final double xMin, final double xMax,
      * @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 PolyhedronsSet(final double xMin, final double xMax,
             // 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 PolyhedronsSet(final double xMin, final double xMax,
      * @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 PolyhedronsSet(final double xMin, final double xMax,
                         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 PolyhedronsSet(final double xMin, final double xMax,
                                     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 PolyhedronsSet(final double xMin, final double xMax,
      * @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 PolyhedronsSet(final double xMin, final double xMax,
      * 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 PolyhedronsSet(final double xMin, final double xMax,
                 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 PolyhedronsSet(final double xMin, final double xMax,
 
     /** {@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 @@ protected void computeGeometricalProperties() {
         // 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 @@ else if (isFull()) {
             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 @@ else if (isFull()) {
      *  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 double getSize() {
          * 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 void visitInternalNode(final BSPTree<Euclidean3D> node) {
 
         /** {@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 @@ private void addContribution(final SubHyperplane<Euclidean3D> facet, final boole
      * 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 @@ private void addContribution(final SubHyperplane<Euclidean3D> facet, final boole
      * 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 @@ private void addContribution(final SubHyperplane<Euclidean3D> facet, final boole
 
         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 @@ private void addContribution(final SubHyperplane<Euclidean3D> facet, final boole
      * @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 @@ private void addContribution(final SubHyperplane<Euclidean3D> facet, final boole
      * @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 PolyhedronsSet rotate(final Cartesian3D center, final Rotation rotation)
         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 Plane apply(final Hyperplane<Euclidean3D> hyperplane) {
      * @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 @@
  * <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 @@
  * 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 @@
  *
  * <p>Rotations are guaranteed to be immutable objects.</p>
  *
- * @see Cartesian3D
+ * @see Vector3D
  * @see RotationOrder
  */
 
@@ -154,16 +154,16 @@ public Rotation(double q0, double q1, double q2, double q3,
   /** 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 Rotation(Cartesian3D axis, double angle) throws IllegalArgumentException
    * @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 Rotation(double[][] m, double threshold)
    * @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 Rotation(Cartesian3D u1, Cartesian3D u2, Cartesian3D v1, Cartesian3D v2)
    * @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 Rotation(Cartesian3D u, Cartesian3D v) throws IllegalArgumentException {
     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 Rotation(Cartesian3D u, Cartesian3D v) throws IllegalArgumentException {
       // 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 double getQ3() {
    * {@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 Cartesian3D getAxis() {
    * </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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
               // (-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 double getAngle() {
    * @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 Cartesian3D applyTo(Cartesian3D u) {
 
     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 void applyTo(final double[] in, final double[] out) {
    * @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 Cartesian3D applyInverseTo(Cartesian3D u) {
     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 @@
      * 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 @@
      * @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 String toString() {
     /** 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 @@
 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 @@
      * @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 Segment(final Cartesian3D start, final Cartesian3D end, final Line line)
     /** 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 @@
 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 SubLine(final Line line, final IntervalsSet remainingRegion) {
      * @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 SubLine(final Segment segment) throws IllegalArgumentException {
         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 SubLine(final Segment segment) throws IllegalArgumentException {
      * 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 Cartesian3D intersection(final SubLine subLine, final boolean includeEndP
      * @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.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 SubPlane(final Hyperplane<Euclidean3D> hyperplane,
      * 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 SubPlane(final Hyperplane<Euclidean3D> hyperplane,
         }
 
         // 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 double getY() {
 
     /** {@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 boolean isInfinite() {
         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.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.geometry.euclidean.twod;
-
-import java.io.Serializable;
-
-import org.apache.commons.geometry.core.Space;
-import org.apache.commons.geometry.euclidean.oned.Euclidean1D;
-
-/**
- * This class implements a two-dimensional space.
- */
-public class Euclidean2D implements Serializable, Space {
-
-    /** Serializable version identifier. */
-    private static final long serialVersionUID = 4793432849757649566L;
-
-    /** Private constructor for the singleton.
-     */
-    private Euclidean2D() {
-    }
-
-    /** Get the unique instance.
-     * @return the unique instance
-     */
-    public static Euclidean2D getInstance() {
-        return LazyHolder.INSTANCE;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public int getDimension() {
-        return 2;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public Euclidean1D getSubSpace() {
-        return Euclidean1D.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 Euclidean2D INSTANCE = new Euclidean2D();
-    }
-    // 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/twod/Line.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Line.java
index aadb7ca..cb3c2ea 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Line.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Line.java
@@ -16,18 +16,15 @@
  */
 package org.apache.commons.geometry.euclidean.twod;
 
-import org.apache.commons.numbers.arrays.LinearCombination;
-import org.apache.commons.numbers.angle.PlaneAngleRadians;
-import org.apache.commons.geometry.euclidean.oned.Euclidean1D;
-import org.apache.commons.geometry.euclidean.oned.IntervalsSet;
-import org.apache.commons.geometry.euclidean.oned.OrientedPoint;
-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.core.partitioning.SubHyperplane;
 import org.apache.commons.geometry.core.partitioning.Transform;
-import org.apache.commons.geometry.euclidean.oned.Cartesian1D;
+import org.apache.commons.geometry.euclidean.oned.IntervalsSet;
+import org.apache.commons.geometry.euclidean.oned.OrientedPoint;
+import org.apache.commons.geometry.euclidean.oned.Point1D;
+import org.apache.commons.numbers.angle.PlaneAngleRadians;
+import org.apache.commons.numbers.arrays.LinearCombination;
 
 /** This class represents an oriented line in the 2D plane.
 
@@ -53,7 +50,7 @@
  * left half plane is the set of points with negative offsets and the
  * right half plane is the set of points with positive offsets.</p>
  */
-public class Line implements Hyperplane<Euclidean2D>, Embedding<Euclidean2D, Euclidean1D> {
+public class Line implements Hyperplane<Point2D>, Embedding<Point2D, Point1D> {
     /** Angle with respect to the abscissa axis. */
     private double angle;
 
@@ -78,7 +75,7 @@
      * @param p2 second point
      * @param tolerance tolerance below which points are considered identical
      */
-    public Line(final Cartesian2D p1, final Cartesian2D p2, final double tolerance) {
+    public Line(final Point2D p1, final Point2D p2, final double tolerance) {
         reset(p1, p2);
         this.tolerance = tolerance;
     }
@@ -88,7 +85,7 @@ public Line(final Cartesian2D p1, final Cartesian2D p2, final double tolerance)
      * @param angle angle of the line with respect to abscissa axis
      * @param tolerance tolerance below which points are considered identical
      */
-    public Line(final Cartesian2D p, final double angle, final double tolerance) {
+    public Line(final Point2D p, final double angle, final double tolerance) {
         reset(p, angle);
         this.tolerance = tolerance;
     }
@@ -135,7 +132,7 @@ public Line copySelf() {
      * @param p1 first point
      * @param p2 second point
      */
-    public void reset(final Cartesian2D p1, final Cartesian2D p2) {
+    public void reset(final Point2D p1, final Point2D p2) {
         unlinkReverse();
         final double dx = p2.getX() - p1.getX();
         final double dy = p2.getY() - p1.getY();
@@ -157,7 +154,7 @@ public void reset(final Cartesian2D p1, final Cartesian2D p2) {
      * @param p point belonging to the line
      * @param alpha angle of the line with respect to abscissa axis
      */
-    public void reset(final Cartesian2D p, final double alpha) {
+    public void reset(final Point2D p, final double alpha) {
         unlinkReverse();
         this.angle   = PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(alpha);
         cos          = Math.cos(this.angle);
@@ -193,8 +190,8 @@ private void unlinkReverse() {
      * instance.</p>
      * <p>
      * As long as neither the instance nor its reverse are modified
-     * (i.e. as long as none of the {@link #reset(Cartesian2D, Cartesian2D)},
-     * {@link #reset(Cartesian2D, double)}, {@link #revertSelf()},
+     * (i.e. as long as none of the {@link #reset(Point2D, Point2D)},
+     * {@link #reset(Point2D, double)}, {@link #revertSelf()},
      * {@link #setAngle(double)} or {@link #setOriginOffset(double)}
      * methods are called), then the line and its reverse remain linked
      * together so that {@code line.getReverse().getReverse() == line}.
@@ -212,53 +209,17 @@ public Line getReverse() {
         return reverse;
     }
 
-    /** 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<Euclidean2D> vector) {
-        return toSubSpace((Cartesian2D) 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 Cartesian2D toSpace(Vector<Euclidean1D> vector) {
-        return toSpace((Cartesian1D) vector);
-    }
-
     /** {@inheritDoc} */
     @Override
-    public Cartesian1D toSubSpace(final Point<Euclidean2D> point) {
-        return toSubSpace((Cartesian2D) point);
+    public Point1D toSubSpace(final Point2D point) {
+        return new Point1D(LinearCombination.value(cos, point.getX(), sin, point.getY()));
     }
 
     /** {@inheritDoc} */
     @Override
-    public Cartesian2D toSpace(final Point<Euclidean1D> point) {
-        return toSpace((Cartesian1D) point);
-    }
-
-    /** Transform a space point into a sub-space point.
-     * @param cartesian n-dimension point of the space
-     * @return (n-1)-dimension point of the sub-space corresponding to
-     * the specified space point
-     */
-    public Cartesian1D toSubSpace(final Cartesian2D cartesian) {
-        return new Cartesian1D(LinearCombination.value(cos, cartesian.getX(), sin, cartesian.getY()));
-    }
-
-    /** Transform a sub-space point into a space point.
-     * @param cartesian (n-1)-dimension point of the sub-space
-     * @return n-dimension point of the space corresponding to the
-     * specified sub-space point
-     */
-    public Cartesian2D toSpace(Cartesian1D cartesian) {
-        final double abscissa = cartesian.getX();
-        return new Cartesian2D(LinearCombination.value(abscissa, cos, -originOffset, sin),
+    public Point2D toSpace(final Point1D point) {
+        final double abscissa = point.getX();
+        return new Point2D(LinearCombination.value(abscissa, cos, -originOffset, sin),
                             LinearCombination.value(abscissa, sin,  originOffset, cos));
     }
 
@@ -267,18 +228,18 @@ public Cartesian2D toSpace(Cartesian1D cartesian) {
      * @return intersection point of the instance and the other line
      * or null if there are no intersection points
      */
-    public Cartesian2D intersection(final Line other) {
+    public Point2D intersection(final Line other) {
         final double d = LinearCombination.value(sin, other.cos, -other.sin, cos);
         if (Math.abs(d) < tolerance) {
             return null;
         }
-        return new Cartesian2D(LinearCombination.value(cos, other.originOffset, -other.cos, originOffset) / d,
+        return new Point2D(LinearCombination.value(cos, other.originOffset, -other.cos, originOffset) / d,
                             LinearCombination.value(sin, other.originOffset, -other.sin, originOffset) / d);
     }
 
     /** {@inheritDoc} */
     @Override
-    public Point<Euclidean2D> project(Point<Euclidean2D> point) {
+    public Point2D project(Point2D point) {
         return toSpace(toSubSpace(point));
     }
 
@@ -318,31 +279,15 @@ public double getOffset(final Line line) {
                (LinearCombination.value(cos, line.cos, sin, line.sin) > 0 ? -line.originOffset : line.originOffset);
     }
 
-    /** Get the offset (oriented distance) of a vector.
-     * @param vector vector to check
-     * @return offset of the vector
-     */
-    public double getOffset(Vector<Euclidean2D> vector) {
-        return getOffset((Cartesian2D) vector);
-    }
-
     /** {@inheritDoc} */
     @Override
-    public double getOffset(final Point<Euclidean2D> point) {
-        return getOffset((Cartesian2D) point);
-    }
-
-    /** Get the offset (oriented distance) of a point.
-     * @param cartesian point to check
-     * @return offset of the point
-     */
-    public double getOffset(Cartesian2D cartesian) {
-        return LinearCombination.value(sin, cartesian.getX(), -cos, cartesian.getY(), 1.0, originOffset);
+    public double getOffset(final Point2D point) {
+        return LinearCombination.value(sin, point.getX(), -cos, point.getY(), 1.0, originOffset);
     }
 
     /** {@inheritDoc} */
     @Override
-    public boolean sameOrientationAs(final Hyperplane<Euclidean2D> other) {
+    public boolean sameOrientationAs(final Hyperplane<Point2D> other) {
         final Line otherL = (Line) other;
         return LinearCombination.value(sin, otherL.sin, cos, otherL.cos) >= 0.0;
     }
@@ -353,10 +298,10 @@ public boolean sameOrientationAs(final Hyperplane<Euclidean2D> other) {
      * @return one point in the plane, with given abscissa and offset
      * relative to the line
      */
-    public Cartesian2D getPointAt(final Cartesian1D abscissa, final double offset) {
+    public Point2D getPointAt(final Point1D abscissa, final double offset) {
         final double x       = abscissa.getX();
         final double dOffset = offset - originOffset;
-        return new Cartesian2D(LinearCombination.value(x, cos,  dOffset, sin),
+        return new Point2D(LinearCombination.value(x, cos,  dOffset, sin),
                             LinearCombination.value(x, sin, -dOffset, cos));
     }
 
@@ -364,7 +309,7 @@ public Cartesian2D getPointAt(final Cartesian1D abscissa, final double offset) {
      * @param p point to check
      * @return true if p belongs to the line
      */
-    public boolean contains(final Cartesian2D p) {
+    public boolean contains(final Point2D p) {
         return Math.abs(getOffset(p)) < tolerance;
     }
 
@@ -376,7 +321,7 @@ public boolean contains(final Cartesian2D p) {
      * @param p to check
      * @return distance between the instance and the point
      */
-    public double distance(final Cartesian2D p) {
+    public double distance(final Point2D p) {
         return Math.abs(getOffset(p));
     }
 
@@ -392,7 +337,7 @@ public boolean isParallelTo(final Line line) {
     /** Translate the line to force it passing by a point.
      * @param p point by which the line should pass
      */
-    public void translateToPoint(final Cartesian2D p) {
+    public void translateToPoint(final Point2D p) {
         originOffset = LinearCombination.value(cos, p.getY(), -sin, p.getX());
     }
 
@@ -437,12 +382,12 @@ public void setOriginOffset(final double offset) {
      * @param cX1 transform addendum for output abscissa
      * @param cY1 transform addendum for output ordinate
      * @return a new transform that can be applied to either {@link
-     * Cartesian2D}, {@link Line Line} or {@link
+     * Point2D}, {@link Line Line} or {@link
      * org.apache.commons.geometry.core.partitioning.SubHyperplane
      * SubHyperplane} instances
      * @exception IllegalArgumentException if the transform is non invertible
      */
-    public static Transform<Euclidean2D, Euclidean1D> getTransform(final double cXX,
+    public static Transform<Point2D, Point1D> getTransform(final double cXX,
                                                                    final double cYX,
                                                                    final double cXY,
                                                                    final double cYY,
@@ -459,7 +404,7 @@ public void setOriginOffset(final double offset) {
      * applied to a large number of lines (for example to a large
      * polygon)./<p>
      */
-    private static class LineTransform implements Transform<Euclidean2D, Euclidean1D> {
+    private static class LineTransform implements Transform<Point2D, Point1D> {
 
         /** Transform factor between input abscissa and output abscissa. */
         private final double cXX;
@@ -520,17 +465,16 @@ public void setOriginOffset(final double offset) {
 
         /** {@inheritDoc} */
         @Override
-        public Cartesian2D apply(final Point<Euclidean2D> point) {
-            final Cartesian2D p2D = (Cartesian2D) point;
-            final double  x   = p2D.getX();
-            final double  y   = p2D.getY();
-            return new Cartesian2D(LinearCombination.value(cXX, x, cXY, y, cX1, 1),
+        public Point2D apply(final Point2D point) {
+            final double  x   = point.getX();
+            final double  y   = point.getY();
+            return new Point2D(LinearCombination.value(cXX, x, cXY, y, cX1, 1),
                                 LinearCombination.value(cYX, x, cYY, y, cY1, 1));
         }
 
         /** {@inheritDoc} */
         @Override
-        public Line apply(final Hyperplane<Euclidean2D> hyperplane) {
+        public Line apply(final Hyperplane<Point2D> hyperplane) {
             final Line   line    = (Line) hyperplane;
             final double rOffset = LinearCombination.value(c1X, line.cos, c1Y, line.sin, c11, line.originOffset);
             final double rCos    = LinearCombination.value(cXX, line.cos, cXY, line.sin);
@@ -543,13 +487,13 @@ public Line apply(final Hyperplane<Euclidean2D> hyperplane) {
 
         /** {@inheritDoc} */
         @Override
-        public SubHyperplane<Euclidean1D> apply(final SubHyperplane<Euclidean1D> sub,
-                                                final Hyperplane<Euclidean2D> original,
-                                                final Hyperplane<Euclidean2D> transformed) {
+        public SubHyperplane<Point1D> apply(final SubHyperplane<Point1D> sub,
+                                                final Hyperplane<Point2D> original,
+                                                final Hyperplane<Point2D> transformed) {
             final OrientedPoint op     = (OrientedPoint) sub.getHyperplane();
             final Line originalLine    = (Line) original;
             final Line transformedLine = (Line) transformed;
-            final Cartesian1D newLoc =
+            final Point1D newLoc =
                 transformedLine.toSubSpace(apply(originalLine.toSpace(op.getLocation())));
             return new OrientedPoint(newLoc, op.isDirect(), originalLine.tolerance).wholeHyperplane();
         }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/NestedLoops.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/NestedLoops.java
index 75554f2..a8ec823 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/NestedLoops.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/NestedLoops.java
@@ -20,7 +20,6 @@
 import java.util.Iterator;
 import java.util.List;
 
-import org.apache.commons.geometry.core.Point;
 import org.apache.commons.geometry.core.partitioning.Region;
 import org.apache.commons.geometry.core.partitioning.RegionFactory;
 import org.apache.commons.geometry.core.partitioning.SubHyperplane;
@@ -44,13 +43,13 @@
 class NestedLoops {
 
     /** Boundary loop. */
-    private Cartesian2D[] loop;
+    private Point2D[] loop;
 
     /** Surrounded loops. */
     private List<NestedLoops> surrounded;
 
     /** Polygon enclosing a finite region. */
-    private Region<Euclidean2D> polygon;
+    private Region<Point2D> polygon;
 
     /** Indicator for original loop orientation. */
     private boolean originalIsClockwise;
@@ -76,7 +75,7 @@
      * @param tolerance tolerance below which points are considered identical
      * @exception IllegalArgumentException if an outline has an open boundary loop
      */
-    private NestedLoops(final Cartesian2D[] loop, final double tolerance)
+    private NestedLoops(final Point2D[] loop, final double tolerance)
         throws IllegalArgumentException {
 
         if (loop[0] == null) {
@@ -88,15 +87,15 @@ private NestedLoops(final Cartesian2D[] loop, final double tolerance)
         this.tolerance  = tolerance;
 
         // build the polygon defined by the loop
-        final ArrayList<SubHyperplane<Euclidean2D>> edges = new ArrayList<>();
-        Cartesian2D current = loop[loop.length - 1];
+        final ArrayList<SubHyperplane<Point2D>> edges = new ArrayList<>();
+        Point2D current = loop[loop.length - 1];
         for (int i = 0; i < loop.length; ++i) {
-            final Cartesian2D previous = current;
+            final Point2D previous = current;
             current = loop[i];
             final Line   line   = new Line(previous, current, tolerance);
             final IntervalsSet region =
-                new IntervalsSet(line.toSubSpace((Point<Euclidean2D>) previous).getX(),
-                                 line.toSubSpace((Point<Euclidean2D>) current).getX(),
+                new IntervalsSet(line.toSubSpace(previous).getX(),
+                                 line.toSubSpace(current).getX(),
                                  tolerance);
             edges.add(new SubLine(line, region));
         }
@@ -104,7 +103,7 @@ private NestedLoops(final Cartesian2D[] loop, final double tolerance)
 
         // ensure the polygon encloses a finite region of the plane
         if (Double.isInfinite(polygon.getSize())) {
-            polygon = new RegionFactory<Euclidean2D>().getComplement(polygon);
+            polygon = new RegionFactory<Point2D>().getComplement(polygon);
             originalIsClockwise = false;
         } else {
             originalIsClockwise = true;
@@ -117,7 +116,7 @@ private NestedLoops(final Cartesian2D[] loop, final double tolerance)
      * @exception IllegalArgumentException if an outline has crossing
      * boundary loops or open boundary loops
      */
-    public void add(final Cartesian2D[] bLoop) throws IllegalArgumentException {
+    public void add(final Point2D[] bLoop) throws IllegalArgumentException {
         add(new NestedLoops(bLoop, tolerance));
     }
 
@@ -146,7 +145,7 @@ private void add(final NestedLoops node) throws IllegalArgumentException {
         }
 
         // we should be separate from the remaining children
-        RegionFactory<Euclidean2D> factory = new RegionFactory<>();
+        RegionFactory<Point2D> factory = new RegionFactory<>();
         for (final NestedLoops child : surrounded) {
             if (!factory.intersection(node.polygon, child.polygon).isEmpty()) {
                 throw new IllegalArgumentException("Some outline boundary loops cross each other");
@@ -159,7 +158,7 @@ private void add(final NestedLoops node) throws IllegalArgumentException {
 
     /** Correct the orientation of the loops contained in the tree.
      * <p>This is this method that really inverts the loops that where
-     * provided through the {@link #add(Cartesian2D[]) add} method if
+     * provided through the {@link #add(Point2D[]) add} method if
      * they are mis-oriented</p>
      */
     public void correctOrientation() {
@@ -179,7 +178,7 @@ private void setClockWise(final boolean clockwise) {
             int min = -1;
             int max = loop.length;
             while (++min < --max) {
-                final Cartesian2D tmp = loop[min];
+                final Point2D tmp = loop[min];
                 loop[min] = loop[max];
                 loop[max] = tmp;
             }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
new file mode 100644
index 0000000..2d73d72
--- /dev/null
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.geometry.euclidean.twod;
+
+import org.apache.commons.geometry.euclidean.EuclideanPoint;
+import org.apache.commons.numbers.arrays.LinearCombination;
+
+/** This class represents a point in two-dimensional Euclidean space.
+ * Instances of this class are guaranteed to be immutable.
+ */
+public final class Point2D extends Cartesian2D implements EuclideanPoint<Point2D, Vector2D> {
+
+    /** Origin (coordinates: 0, 0). */
+    public static final Point2D ZERO   = new Point2D(0, 0);
+
+ // CHECKSTYLE: stop ConstantName
+    /** A point with all coordinates set to NaN. */
+    public static final Point2D NaN = new Point2D(Double.NaN, Double.NaN);
+    // CHECKSTYLE: resume ConstantName
+
+    /** A point with all coordinates set to positive infinity. */
+    public static final Point2D POSITIVE_INFINITY =
+        new Point2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+
+    /** A point with all coordinates set to negative infinity. */
+    public static final Point2D NEGATIVE_INFINITY =
+        new Point2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+
+    /** Serializable UID. */
+    private static final long serialVersionUID = 266938651998679754L;
+
+    /** Simple constructor.
+     * Build a point from its coordinates
+     * @param x abscissa
+     * @param y ordinate
+     */
+    public Point2D(double x, double y) {
+        super(x, y);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector2D asVector() {
+        return new Vector2D(x, y);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distance(Point2D p) {
+        return euclideanDistance(p);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector2D subtract(Point2D p) {
+        return Vector2D.of(x - p.x, y - p.y);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector2D vectorTo(Point2D p) {
+        return p.subtract(this);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Point2D add(Vector2D v) {
+        return new Point2D(x + v.x, y + v.y);
+    }
+
+    /**
+     * 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 542;
+        }
+        return 122 * (76 * Double.hashCode(x) +  Double.hashCode(y));
+    }
+
+    /** 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 Point2D objects are equal, false if
+     *         object is null, not an instance of Point2D, or
+     *         not equal to this Point2D instance
+     *
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (other instanceof Point2D) {
+            final Point2D rhs = (Point2D) other;
+            if (rhs.isNaN()) {
+                return this.isNaN();
+            }
+
+            return (x == rhs.x) && (y == rhs.y);
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "(" + x + "; " + y + ")";
+    }
+
+    /** Returns a point with the given coordinate values
+     * @param x abscissa (first coordinate value)
+     * @param y ordinate (second coordinate value)
+     * @return point instance
+     */
+    public static Point2D of(double x, double y) {
+        return new Point2D(x, y);
+    }
+
+    /** Returns a point with the given coordinates.
+     * @param value coordinate values
+     * @return point instance
+     */
+    public static Point2D of(Cartesian2D value) {
+        return new Point2D(value.x, value.y);
+    }
+
+    /** Returns a point with the coordinates from the given 2-element array.
+     * @param p coordinates array
+     * @return new point
+     * @exception IllegalArgumentException if the array does not have 2 elements
+     */
+    public static Point2D of(double[] p) {
+        if (p.length != 2) {
+            throw new IllegalArgumentException("Dimension mismatch: " + p.length + " != 2");
+        }
+        return new Point2D(p[0], p[1]);
+    }
+
+    /** 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 Point2D vectorCombination(double a, Cartesian2D c) {
+        return new Point2D(a * c.x, a * c.y);
+    }
+
+    /** 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 Point2D vectorCombination(double a1, Cartesian2D c1, double a2, Cartesian2D c2) {
+        return new Point2D(
+                LinearCombination.value(a1, c1.x, a2, c2.x),
+                LinearCombination.value(a1, c1.y, a2, c2.y));
+    }
+
+    /** 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 Point2D vectorCombination(double a1, Cartesian2D c1, double a2, Cartesian2D c2,
+            double a3, Cartesian2D c3) {
+        return new Point2D(
+                LinearCombination.value(a1, c1.x, a2, c2.x, a3, c3.x),
+                LinearCombination.value(a1, c1.y, a2, c2.y, a3, c3.y));
+    }
+
+    /** 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 Point2D vectorCombination(double a1, Cartesian2D c1, double a2, Cartesian2D c2,
+            double a3, Cartesian2D c3, double a4, Cartesian2D c4) {
+        return new Point2D(
+                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));
+    }
+}
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolygonsSet.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolygonsSet.java
index 290daca..de79c32 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolygonsSet.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/PolygonsSet.java
@@ -20,10 +20,6 @@
 import java.util.Collection;
 import java.util.List;
 
-import org.apache.commons.geometry.euclidean.oned.Euclidean1D;
-import org.apache.commons.geometry.euclidean.oned.Interval;
-import org.apache.commons.geometry.euclidean.oned.IntervalsSet;
-import org.apache.commons.geometry.core.Point;
 import org.apache.commons.geometry.core.partitioning.AbstractRegion;
 import org.apache.commons.geometry.core.partitioning.AbstractSubHyperplane;
 import org.apache.commons.geometry.core.partitioning.BSPTree;
@@ -32,15 +28,17 @@
 import org.apache.commons.geometry.core.partitioning.Hyperplane;
 import org.apache.commons.geometry.core.partitioning.Side;
 import org.apache.commons.geometry.core.partitioning.SubHyperplane;
-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;
 import org.apache.commons.numbers.core.Precision;
 
 /** This class represents a 2D region: a set of polygons.
  */
-public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
+public class PolygonsSet extends AbstractRegion<Point2D, Point1D> {
 
     /** Vertices organized as boundary loops. */
-    private Cartesian2D[][] vertices;
+    private Point2D[][] vertices;
 
     /** Build a polygons set representing the whole plane.
      * @param tolerance tolerance below which points are considered identical
@@ -69,7 +67,7 @@ public PolygonsSet(final double tolerance) {
      * @param tree inside/outside BSP tree representing the region
      * @param tolerance tolerance below which points are considered identical
      */
-    public PolygonsSet(final BSPTree<Euclidean2D> tree, final double tolerance) {
+    public PolygonsSet(final BSPTree<Point2D> tree, final double tolerance) {
         super(tree, tolerance);
     }
 
@@ -94,7 +92,7 @@ public PolygonsSet(final BSPTree<Euclidean2D> tree, final double tolerance) {
      * collection of {@link SubHyperplane SubHyperplane} objects
      * @param tolerance tolerance below which points are considered identical
      */
-    public PolygonsSet(final Collection<SubHyperplane<Euclidean2D>> boundary, final double tolerance) {
+    public PolygonsSet(final Collection<SubHyperplane<Point2D>> boundary, final double tolerance) {
         super(boundary, tolerance);
     }
 
@@ -141,7 +139,7 @@ public PolygonsSet(final double xMin, final double xMax,
      * belong to the hyperplane (which is therefore more a slab)
      * @param vertices vertices of the simple loop boundary
      */
-    public PolygonsSet(final double hyperplaneThickness, final Cartesian2D ... vertices) {
+    public PolygonsSet(final double hyperplaneThickness, final Point2D ... vertices) {
         super(verticesToTree(hyperplaneThickness, vertices), hyperplaneThickness);
     }
 
@@ -160,10 +158,10 @@ public PolygonsSet(final double hyperplaneThickness, final Cartesian2D ... verti
             // too thin box, build an empty polygons set
             return null;
         }
-        final Cartesian2D minMin = new Cartesian2D(xMin, yMin);
-        final Cartesian2D minMax = new Cartesian2D(xMin, yMax);
-        final Cartesian2D maxMin = new Cartesian2D(xMax, yMin);
-        final Cartesian2D maxMax = new Cartesian2D(xMax, yMax);
+        final Point2D minMin = new Point2D(xMin, yMin);
+        final Point2D minMax = new Point2D(xMin, yMax);
+        final Point2D maxMin = new Point2D(xMax, yMin);
+        final Point2D maxMax = new Point2D(xMax, yMax);
         return new Line[] {
             new Line(minMin, maxMin, tolerance),
             new Line(maxMin, maxMax, tolerance),
@@ -187,8 +185,8 @@ public PolygonsSet(final double hyperplaneThickness, final Cartesian2D ... verti
      * @param vertices vertices of the simple loop boundary
      * @return the BSP tree of the input vertices
      */
-    private static BSPTree<Euclidean2D> verticesToTree(final double hyperplaneThickness,
-                                                       final Cartesian2D ... vertices) {
+    private static BSPTree<Point2D> verticesToTree(final double hyperplaneThickness,
+                                                       final Point2D ... vertices) {
 
         final int n = vertices.length;
         if (n == 0) {
@@ -224,7 +222,7 @@ public PolygonsSet(final double hyperplaneThickness, final Cartesian2D ... verti
             // check if another vertex also happens to be on this line
             for (final Vertex vertex : vArray) {
                 if (vertex != start && vertex != end &&
-                    Math.abs(line.getOffset((Point<Euclidean2D>) vertex.getLocation())) <= hyperplaneThickness) {
+                    Math.abs(line.getOffset(vertex.getLocation())) <= hyperplaneThickness) {
                     vertex.bindWith(line);
                 }
             }
@@ -232,7 +230,7 @@ public PolygonsSet(final double hyperplaneThickness, final Cartesian2D ... verti
         }
 
         // build the tree top-down
-        final BSPTree<Euclidean2D> tree = new BSPTree<>();
+        final BSPTree<Point2D> tree = new BSPTree<>();
         insertEdges(hyperplaneThickness, tree, edges);
 
         return tree;
@@ -248,7 +246,7 @@ public PolygonsSet(final double hyperplaneThickness, final Cartesian2D ... verti
      * (excluding edges not belonging to the cell defined by this node)
      */
     private static void insertEdges(final double hyperplaneThickness,
-                                    final BSPTree<Euclidean2D> node,
+                                    final BSPTree<Point2D> node,
                                     final List<Edge> edges) {
 
         // find an edge with an hyperplane that can be inserted in the node
@@ -270,7 +268,7 @@ private static void insertEdges(final double hyperplaneThickness,
         if (inserted == null) {
             // no suitable edge was found, the node remains a leaf node
             // we need to set its inside/outside boolean indicator
-            final BSPTree<Euclidean2D> parent = node.getParent();
+            final BSPTree<Point2D> parent = node.getParent();
             if (parent == null || node == parent.getMinus()) {
                 node.setAttribute(Boolean.TRUE);
             } else {
@@ -285,8 +283,8 @@ private static void insertEdges(final double hyperplaneThickness,
         final List<Edge> minusList = new ArrayList<>();
         for (final Edge edge : edges) {
             if (edge != inserted) {
-                final double startOffset = inserted.getLine().getOffset((Point<Euclidean2D>) edge.getStart().getLocation());
-                final double endOffset   = inserted.getLine().getOffset((Point<Euclidean2D>) edge.getEnd().getLocation());
+                final double startOffset = inserted.getLine().getOffset(edge.getStart().getLocation());
+                final double endOffset   = inserted.getLine().getOffset(edge.getEnd().getLocation());
                 Side startSide = (Math.abs(startOffset) <= hyperplaneThickness) ?
                                  Side.HYPER : ((startOffset < 0) ? Side.MINUS : Side.PLUS);
                 Side endSide   = (Math.abs(endOffset) <= hyperplaneThickness) ?
@@ -341,7 +339,7 @@ private static void insertEdges(final double hyperplaneThickness,
     private static class Vertex {
 
         /** Vertex location. */
-        private final Cartesian2D location;
+        private final Point2D location;
 
         /** Incoming edge. */
         private Edge incoming;
@@ -355,7 +353,7 @@ private static void insertEdges(final double hyperplaneThickness,
         /** Build a non-processed vertex not owned by any node yet.
          * @param location vertex location
          */
-        Vertex(final Cartesian2D location) {
+        Vertex(final Point2D location) {
             this.location = location;
             this.incoming = null;
             this.outgoing = null;
@@ -365,7 +363,7 @@ private static void insertEdges(final double hyperplaneThickness,
         /** Get Vertex location.
          * @return vertex location
          */
-        public Cartesian2D getLocation() {
+        public Point2D getLocation() {
             return location;
         }
 
@@ -450,7 +448,7 @@ public Edge getOutgoing() {
         private final Line line;
 
         /** Node whose cut hyperplane contains this edge. */
-        private BSPTree<Euclidean2D> node;
+        private BSPTree<Point2D> node;
 
         /** Build an edge not contained in any node yet.
          * @param start start vertex
@@ -494,7 +492,7 @@ public Line getLine() {
         /** Set the node whose cut hyperplane contains this edge.
          * @param node node whose cut hyperplane contains this edge
          */
-        public void setNode(final BSPTree<Euclidean2D> node) {
+        public void setNode(final BSPTree<Point2D> node) {
             this.node = node;
         }
 
@@ -502,7 +500,7 @@ public void setNode(final BSPTree<Euclidean2D> node) {
          * @return node whose cut hyperplane contains this edge
          * (null if edge has not yet been inserted into the BSP tree)
          */
-        public BSPTree<Euclidean2D> getNode() {
+        public BSPTree<Point2D> getNode() {
             return node;
         }
 
@@ -529,7 +527,7 @@ public Vertex split(final Line splitLine) {
 
     /** {@inheritDoc} */
     @Override
-    public PolygonsSet buildNew(final BSPTree<Euclidean2D> tree) {
+    public PolygonsSet buildNew(final BSPTree<Point2D> tree) {
         return new PolygonsSet(tree, getTolerance());
     }
 
@@ -537,22 +535,22 @@ public PolygonsSet buildNew(final BSPTree<Euclidean2D> tree) {
     @Override
     protected void computeGeometricalProperties() {
 
-        final Cartesian2D[][] v = getVertices();
+        final Point2D[][] v = getVertices();
 
         if (v.length == 0) {
-            final BSPTree<Euclidean2D> tree = getTree(false);
+            final BSPTree<Point2D> tree = getTree(false);
             if (tree.getCut() == null && (Boolean) tree.getAttribute()) {
                 // the instance covers the whole space
                 setSize(Double.POSITIVE_INFINITY);
-                setBarycenter((Point<Euclidean2D>) Cartesian2D.NaN);
+                setBarycenter(Point2D.NaN);
             } else {
                 setSize(0);
-                setBarycenter((Point<Euclidean2D>) Cartesian2D.NaN);
+                setBarycenter(Point2D.NaN);
             }
         } else if (v[0][0] == null) {
             // there is at least one open-loop: the polygon is infinite
             setSize(Double.POSITIVE_INFINITY);
-            setBarycenter((Point<Euclidean2D>) Cartesian2D.NaN);
+            setBarycenter(Point2D.NaN);
         } else {
             // all loops are closed, we compute some integrals around the shape
 
@@ -560,10 +558,10 @@ protected void computeGeometricalProperties() {
             double sumX = 0;
             double sumY = 0;
 
-            for (Cartesian2D[] loop : v) {
+            for (Point2D[] loop : v) {
                 double x1 = loop[loop.length - 1].getX();
                 double y1 = loop[loop.length - 1].getY();
-                for (final Cartesian2D point : loop) {
+                for (final Point2D point : loop) {
                     final double x0 = x1;
                     final double y0 = y1;
                     x1 = point.getX();
@@ -578,10 +576,10 @@ protected void computeGeometricalProperties() {
             if (sum < 0) {
                 // the polygon as a finite outside surrounded by an infinite inside
                 setSize(Double.POSITIVE_INFINITY);
-                setBarycenter((Point<Euclidean2D>) Cartesian2D.NaN);
+                setBarycenter(Point2D.NaN);
             } else {
                 setSize(sum / 2);
-                setBarycenter((Point<Euclidean2D>) new Cartesian2D(sumX / (3 * sum), sumY / (3 * sum)));
+                setBarycenter(new Point2D(sumX / (3 * sum), sumY / (3 * sum)));
             }
 
         }
@@ -611,10 +609,10 @@ protected void computeGeometricalProperties() {
      * loops with the open loops first (the returned value is guaranteed
      * to be non-null)
      */
-    public Cartesian2D[][] getVertices() {
+    public Point2D[][] getVertices() {
         if (vertices == null) {
             if (getTree(false).getCut() == null) {
-                vertices = new Cartesian2D[0][];
+                vertices = new Point2D[0][];
             } else {
 
                 // build the unconnected segments
@@ -653,7 +651,7 @@ protected void computeGeometricalProperties() {
                 }
 
                 // transform the loops in an array of arrays of points
-                vertices = new Cartesian2D[loops.size()][];
+                vertices = new Point2D[loops.size()][];
                 int i = 0;
 
                 for (final List<Segment> loop : loops) {
@@ -661,14 +659,14 @@ protected void computeGeometricalProperties() {
                         (loop.size() == 2 && loop.get(0).getStart() == null && loop.get(1).getEnd() == null)) {
                         // single infinite line
                         final Line line = loop.get(0).getLine();
-                        vertices[i++] = new Cartesian2D[] {
+                        vertices[i++] = new Point2D[] {
                             null,
-                            line.toSpace(new Cartesian1D(-Float.MAX_VALUE)),
-                            line.toSpace(new Cartesian1D(+Float.MAX_VALUE))
+                            line.toSpace(new Point1D(-Float.MAX_VALUE)),
+                            line.toSpace(new Point1D(+Float.MAX_VALUE))
                         };
                     } else if (loop.get(0).getStart() == null) {
                         // open loop with at least one real point
-                        final Cartesian2D[] array = new Cartesian2D[loop.size() + 2];
+                        final Point2D[] array = new Point2D[loop.size() + 2];
                         int j = 0;
                         for (Segment segment : loop) {
 
@@ -677,7 +675,7 @@ protected void computeGeometricalProperties() {
                                 double x = segment.getLine().toSubSpace(segment.getEnd()).getX();
                                 x -= Math.max(1.0, Math.abs(x / 2));
                                 array[j++] = null;
-                                array[j++] = segment.getLine().toSpace(new Cartesian1D(x));
+                                array[j++] = segment.getLine().toSpace(new Point1D(x));
                             }
 
                             if (j < (array.length - 1)) {
@@ -687,13 +685,13 @@ protected void computeGeometricalProperties() {
                                 // last dummy point
                                 double x = segment.getLine().toSubSpace(segment.getStart()).getX();
                                 x += Math.max(1.0, Math.abs(x / 2));
-                                array[j++] = segment.getLine().toSpace(new Cartesian1D(x));
+                                array[j++] = segment.getLine().toSpace(new Point1D(x));
                             }
 
                         }
                         vertices[i++] = array;
                     } else {
-                        final Cartesian2D[] array = new Cartesian2D[loop.size()];
+                        final Point2D[] array = new Point2D[loop.size()];
                         int j = 0;
                         for (Segment segment : loop) {
                             array[j++] = segment.getStart();
@@ -717,8 +715,8 @@ private int naturalFollowerConnections(final List<ConnectableSegment> segments)
         int connected = 0;
         for (final ConnectableSegment segment : segments) {
             if (segment.getNext() == null) {
-                final BSPTree<Euclidean2D> node = segment.getNode();
-                final BSPTree<Euclidean2D> end  = segment.getEndNode();
+                final BSPTree<Point2D> node = segment.getNode();
+                final BSPTree<Point2D> end  = segment.getEndNode();
                 for (final ConnectableSegment candidateNext : segments) {
                     if (candidateNext.getPrevious()  == null &&
                         candidateNext.getNode()      == end &&
@@ -743,8 +741,8 @@ private int splitEdgeConnections(final List<ConnectableSegment> segments) {
         int connected = 0;
         for (final ConnectableSegment segment : segments) {
             if (segment.getNext() == null) {
-                final Hyperplane<Euclidean2D> hyperplane = segment.getNode().getCut().getHyperplane();
-                final BSPTree<Euclidean2D> end  = segment.getEndNode();
+                final Hyperplane<Point2D> hyperplane = segment.getNode().getCut().getHyperplane();
+                final BSPTree<Point2D> end  = segment.getEndNode();
                 for (final ConnectableSegment candidateNext : segments) {
                     if (candidateNext.getPrevious()                      == null &&
                         candidateNext.getNode().getCut().getHyperplane() == hyperplane &&
@@ -773,12 +771,12 @@ private int closeVerticesConnections(final List<ConnectableSegment> segments) {
         int connected = 0;
         for (final ConnectableSegment segment : segments) {
             if (segment.getNext() == null && segment.getEnd() != null) {
-                final Cartesian2D end = segment.getEnd();
+                final Point2D end = segment.getEnd();
                 ConnectableSegment selectedNext = null;
                 double min = Double.POSITIVE_INFINITY;
                 for (final ConnectableSegment candidateNext : segments) {
                     if (candidateNext.getPrevious() == null && candidateNext.getStart() != null) {
-                        final double distance = Cartesian2D.distance(end, candidateNext.getStart());
+                        final double distance = end.distance(candidateNext.getStart());
                         if (distance < min) {
                             selectedNext = candidateNext;
                             min          = distance;
@@ -885,13 +883,13 @@ private void filterSpuriousVertices(final List<Segment> loop) {
     private static class ConnectableSegment extends Segment {
 
         /** Node containing segment. */
-        private final BSPTree<Euclidean2D> node;
+        private final BSPTree<Point2D> node;
 
         /** Node whose intersection with current node defines start point. */
-        private final BSPTree<Euclidean2D> startNode;
+        private final BSPTree<Point2D> startNode;
 
         /** Node whose intersection with current node defines end point. */
-        private final BSPTree<Euclidean2D> endNode;
+        private final BSPTree<Point2D> endNode;
 
         /** Previous segment. */
         private ConnectableSegment previous;
@@ -910,10 +908,10 @@ private void filterSpuriousVertices(final List<Segment> loop) {
          * @param startNode node whose intersection with current node defines start point
          * @param endNode node whose intersection with current node defines end point
          */
-        ConnectableSegment(final Cartesian2D start, final Cartesian2D end, final Line line,
-                           final BSPTree<Euclidean2D> node,
-                           final BSPTree<Euclidean2D> startNode,
-                           final BSPTree<Euclidean2D> endNode) {
+        ConnectableSegment(final Point2D start, final Point2D end, final Line line,
+                           final BSPTree<Point2D> node,
+                           final BSPTree<Point2D> startNode,
+                           final BSPTree<Point2D> endNode) {
             super(start, end, line);
             this.node      = node;
             this.startNode = startNode;
@@ -926,21 +924,21 @@ private void filterSpuriousVertices(final List<Segment> loop) {
         /** Get the node containing segment.
          * @return node containing segment
          */
-        public BSPTree<Euclidean2D> getNode() {
+        public BSPTree<Point2D> getNode() {
             return node;
         }
 
         /** Get the node whose intersection with current node defines start point.
          * @return node whose intersection with current node defines start point
          */
-        public BSPTree<Euclidean2D> getStartNode() {
+        public BSPTree<Point2D> getStartNode() {
             return startNode;
         }
 
         /** Get the node whose intersection with current node defines end point.
          * @return node whose intersection with current node defines end point
          */
-        public BSPTree<Euclidean2D> getEndNode() {
+        public BSPTree<Point2D> getEndNode() {
             return endNode;
         }
 
@@ -989,7 +987,7 @@ public boolean isProcessed() {
     }
 
     /** Visitor building segments. */
-    private static class SegmentsBuilder implements BSPTreeVisitor<Euclidean2D> {
+    private static class SegmentsBuilder implements BSPTreeVisitor<Point2D> {
 
         /** Tolerance for close nodes connection. */
         private final double tolerance;
@@ -1007,16 +1005,16 @@ public boolean isProcessed() {
 
         /** {@inheritDoc} */
         @Override
-        public Order visitOrder(final BSPTree<Euclidean2D> node) {
+        public Order visitOrder(final BSPTree<Point2D> node) {
             return Order.MINUS_SUB_PLUS;
         }
 
         /** {@inheritDoc} */
         @Override
-        public void visitInternalNode(final BSPTree<Euclidean2D> node) {
+        public void visitInternalNode(final BSPTree<Point2D> node) {
             @SuppressWarnings("unchecked")
-            final BoundaryAttribute<Euclidean2D> attribute = (BoundaryAttribute<Euclidean2D>) node.getAttribute();
-            final Iterable<BSPTree<Euclidean2D>> splitters = attribute.getSplitters();
+            final BoundaryAttribute<Point2D> attribute = (BoundaryAttribute<Point2D>) node.getAttribute();
+            final Iterable<BSPTree<Point2D>> splitters = attribute.getSplitters();
             if (attribute.getPlusOutside() != null) {
                 addContribution(attribute.getPlusOutside(), node, splitters, false);
             }
@@ -1027,7 +1025,7 @@ public void visitInternalNode(final BSPTree<Euclidean2D> node) {
 
         /** {@inheritDoc} */
         @Override
-        public void visitLeafNode(final BSPTree<Euclidean2D> node) {
+        public void visitLeafNode(final BSPTree<Point2D> node) {
         }
 
         /** Add the contribution of a boundary facet.
@@ -1036,26 +1034,26 @@ public void visitLeafNode(final BSPTree<Euclidean2D> node) {
          * @param splitters splitters for the boundary facet
          * @param reversed if true, the facet has the inside on its plus side
          */
-        private void addContribution(final SubHyperplane<Euclidean2D> sub,
-                                     final BSPTree<Euclidean2D> node,
-                                     final Iterable<BSPTree<Euclidean2D>> splitters,
+        private void addContribution(final SubHyperplane<Point2D> sub,
+                                     final BSPTree<Point2D> node,
+                                     final Iterable<BSPTree<Point2D>> splitters,
                                      final boolean reversed) {
             @SuppressWarnings("unchecked")
-            final AbstractSubHyperplane<Euclidean2D, Euclidean1D> absSub =
-                (AbstractSubHyperplane<Euclidean2D, Euclidean1D>) sub;
+            final AbstractSubHyperplane<Point2D, Point1D> absSub =
+                (AbstractSubHyperplane<Point2D, Point1D>) sub;
             final Line line      = (Line) sub.getHyperplane();
             final List<Interval> intervals = ((IntervalsSet) absSub.getRemainingRegion()).asList();
             for (final Interval i : intervals) {
 
                 // find the 2D points
-                final Cartesian2D startV = Double.isInfinite(i.getInf()) ?
-                                        null : line.toSpace(new Cartesian1D(i.getInf()));
-                final Cartesian2D endV   = Double.isInfinite(i.getSup()) ?
-                                        null : line.toSpace(new Cartesian1D(i.getSup()));
+                final Point2D startV = Double.isInfinite(i.getInf()) ?
+                                        null : line.toSpace(new Point1D(i.getInf()));
+                final Point2D endV   = Double.isInfinite(i.getSup()) ?
+                                        null : line.toSpace(new Point1D(i.getSup()));
 
                 // recover the connectivity information
-                final BSPTree<Euclidean2D> startN = selectClosest(startV, splitters);
-                final BSPTree<Euclidean2D> endN   = selectClosest(endV, splitters);
+                final BSPTree<Point2D> startN = selectClosest(startV, splitters);
+                final BSPTree<Point2D> endN   = selectClosest(endV, splitters);
 
                 if (reversed) {
                     segments.add(new ConnectableSegment(endV, startV, line.getReverse(),
@@ -1073,12 +1071,12 @@ private void addContribution(final SubHyperplane<Euclidean2D> sub,
          * @param candidates candidate nodes
          * @return node closest to point, or null if point is null or no node is closer than tolerance
          */
-        private BSPTree<Euclidean2D> selectClosest(final Cartesian2D point, final Iterable<BSPTree<Euclidean2D>> candidates) {
+        private BSPTree<Point2D> selectClosest(final Point2D point, final Iterable<BSPTree<Point2D>> candidates) {
             if (point != null) {
-                BSPTree<Euclidean2D> selected = null;
+                BSPTree<Point2D> selected = null;
                 double min = Double.POSITIVE_INFINITY;
 
-                for (final BSPTree<Euclidean2D> node : candidates) {
+                for (final BSPTree<Point2D> node : candidates) {
                     final double distance = Math.abs(node.getCut().getHyperplane().getOffset(point));
                     if (distance < min) {
                         selected = node;
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Segment.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Segment.java
index 41646ed..bc8b214 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Segment.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Segment.java
@@ -16,17 +16,15 @@
  */
 package org.apache.commons.geometry.euclidean.twod;
 
-import org.apache.commons.geometry.core.Point;
-
 /** Simple container for a two-points segment.
  */
 public class Segment {
 
     /** Start point of the segment. */
-    private final Cartesian2D start;
+    private final Point2D start;
 
     /** End point of the segment. */
-    private final Cartesian2D end;
+    private final Point2D end;
 
     /** Line containing the segment. */
     private final Line     line;
@@ -36,7 +34,7 @@
      * @param end end point of the segment
      * @param line line containing the segment
      */
-    public Segment(final Cartesian2D start, final Cartesian2D end, final Line line) {
+    public Segment(final Point2D start, final Point2D end, final Line line) {
         this.start  = start;
         this.end    = end;
         this.line   = line;
@@ -45,14 +43,14 @@ public Segment(final Cartesian2D start, final Cartesian2D end, final Line line)
     /** Get the start point of the segment.
      * @return start point of the segment
      */
-    public Cartesian2D getStart() {
+    public Point2D getStart() {
         return start;
     }
 
     /** Get the end point of the segment.
      * @return end point of the segment
      */
-    public Cartesian2D getEnd() {
+    public Point2D getEnd() {
         return end;
     }
 
@@ -77,7 +75,7 @@ public Line getLine() {
      * @param p to check
      * @return distance between the instance and the point
      */
-    public double distance(final Cartesian2D p) {
+    public double distance(final Point2D p) {
         final double deltaX = end.getX() - start.getX();
         final double deltaY = end.getY() - start.getY();
 
@@ -92,8 +90,8 @@ public double distance(final Cartesian2D p) {
 
         // if point isn't on the line segment, just return the shortest distance to the end points
         if (r < 0 || r > 1) {
-            final double dist1 = getStart().distance((Point<Euclidean2D>) p);
-            final double dist2 = getEnd().distance((Point<Euclidean2D>) p);
+            final double dist1 = getStart().distance(p);
+            final double dist2 = getEnd().distance(p);
 
             return Math.min(dist1, dist2);
         }
@@ -102,8 +100,8 @@ public double distance(final Cartesian2D p) {
             final double px = start.getX() + r * deltaX;
             final double py = start.getY() + r * deltaY;
 
-            final Cartesian2D interPt = new Cartesian2D(px, py);
-            return interPt.distance((Point<Euclidean2D>) p);
+            final Point2D interPt = new Point2D(px, py);
+            return interPt.distance(p);
         }
     }
 }
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/SubLine.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/SubLine.java
index 2f71a28..b9e63d6 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/SubLine.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/SubLine.java
@@ -19,29 +19,27 @@
 import java.util.ArrayList;
 import java.util.List;
 
-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.Hyperplane;
 import org.apache.commons.geometry.core.partitioning.Region;
-import org.apache.commons.geometry.core.partitioning.SubHyperplane;
 import org.apache.commons.geometry.core.partitioning.Region.Location;
-import org.apache.commons.geometry.euclidean.oned.Cartesian1D;
-import org.apache.commons.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.geometry.core.partitioning.SubHyperplane;
 import org.apache.commons.geometry.euclidean.oned.Interval;
 import org.apache.commons.geometry.euclidean.oned.IntervalsSet;
 import org.apache.commons.geometry.euclidean.oned.OrientedPoint;
+import org.apache.commons.geometry.euclidean.oned.Point1D;
 
 /** This class represents a sub-hyperplane for {@link Line}.
  */
-public class SubLine extends AbstractSubHyperplane<Euclidean2D, Euclidean1D> {
+public class SubLine extends AbstractSubHyperplane<Point2D, Point1D> {
 
     /** Simple constructor.
      * @param hyperplane underlying hyperplane
      * @param remainingRegion remaining region of the hyperplane
      */
-    public SubLine(final Hyperplane<Euclidean2D> hyperplane,
-                   final Region<Euclidean1D> remainingRegion) {
+    public SubLine(final Hyperplane<Point2D> hyperplane,
+                   final Region<Point1D> remainingRegion) {
         super(hyperplane, remainingRegion);
     }
 
@@ -50,7 +48,7 @@ public SubLine(final Hyperplane<Euclidean2D> hyperplane,
      * @param end end point
      * @param tolerance tolerance below which points are considered identical
      */
-    public SubLine(final Cartesian2D start, final Cartesian2D end, final double tolerance) {
+    public SubLine(final Point2D start, final Point2D end, final double tolerance) {
         super(new Line(start, end, tolerance), buildIntervalSet(start, end, tolerance));
     }
 
@@ -83,8 +81,8 @@ public SubLine(final Segment segment) {
         final List<Segment> segments = new ArrayList<>(list.size());
 
         for (final Interval interval : list) {
-            final Cartesian2D start = line.toSpace(new Cartesian1D(interval.getInf()));
-            final Cartesian2D end   = line.toSpace(new Cartesian1D(interval.getSup()));
+            final Point2D start = line.toSpace(new Point1D(interval.getInf()));
+            final Point2D end   = line.toSpace(new Point1D(interval.getSup()));
             segments.add(new Segment(start, end, line));
         }
 
@@ -106,23 +104,23 @@ public SubLine(final Segment segment) {
      * 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 Cartesian2D intersection(final SubLine subLine, final boolean includeEndPoints) {
+    public Point2D intersection(final SubLine subLine, final boolean includeEndPoints) {
 
         // retrieve the underlying lines
         Line line1 = (Line) getHyperplane();
         Line line2 = (Line) subLine.getHyperplane();
 
         // compute the intersection on infinite line
-        Cartesian2D v2D = line1.intersection(line2);
+        Point2D v2D = line1.intersection(line2);
         if (v2D == null) {
             return null;
         }
 
         // check location of point with respect to first sub-line
-        Location loc1 = getRemainingRegion().checkPoint(line1.toSubSpace((Point<Euclidean2D>) v2D));
+        Location loc1 = getRemainingRegion().checkPoint(line1.toSubSpace(v2D));
 
         // check location of point with respect to second sub-line
-        Location loc2 = subLine.getRemainingRegion().checkPoint(line2.toSubSpace((Point<Euclidean2D>) v2D));
+        Location loc2 = subLine.getRemainingRegion().checkPoint(line2.toSubSpace(v2D));
 
         if (includeEndPoints) {
             return ((loc1 != Location.OUTSIDE) && (loc2 != Location.OUTSIDE)) ? v2D : null;
@@ -138,7 +136,7 @@ public Cartesian2D intersection(final SubLine subLine, final boolean includeEndP
      * @param tolerance tolerance below which points are considered identical
      * @return an interval set
      */
-    private static IntervalsSet buildIntervalSet(final Cartesian2D start, final Cartesian2D end, final double tolerance) {
+    private static IntervalsSet buildIntervalSet(final Point2D start, final Point2D end, final double tolerance) {
         final Line line = new Line(start, end, tolerance);
         return new IntervalsSet(line.toSubSpace(start).getX(),
                                 line.toSubSpace(end).getX(),
@@ -147,18 +145,18 @@ private static IntervalsSet buildIntervalSet(final Cartesian2D start, final Cart
 
     /** {@inheritDoc} */
     @Override
-    protected AbstractSubHyperplane<Euclidean2D, Euclidean1D> buildNew(final Hyperplane<Euclidean2D> hyperplane,
-                                                                       final Region<Euclidean1D> remainingRegion) {
+    protected AbstractSubHyperplane<Point2D, Point1D> buildNew(final Hyperplane<Point2D> hyperplane,
+                                                                       final Region<Point1D> remainingRegion) {
         return new SubLine(hyperplane, remainingRegion);
     }
 
     /** {@inheritDoc} */
     @Override
-    public SplitSubHyperplane<Euclidean2D> split(final Hyperplane<Euclidean2D> hyperplane) {
+    public SplitSubHyperplane<Point2D> split(final Hyperplane<Point2D> hyperplane) {
 
         final Line    thisLine  = (Line) getHyperplane();
         final Line    otherLine = (Line) hyperplane;
-        final Cartesian2D crossing = thisLine.intersection(otherLine);
+        final Point2D crossing = thisLine.intersection(otherLine);
         final double tolerance  = thisLine.getTolerance();
 
         if (crossing == null) {
@@ -175,20 +173,20 @@ private static IntervalsSet buildIntervalSet(final Cartesian2D start, final Cart
 
         // the lines do intersect
         final boolean direct = Math.sin(thisLine.getAngle() - otherLine.getAngle()) < 0;
-        final Cartesian1D x      = thisLine.toSubSpace(crossing);
-        final SubHyperplane<Euclidean1D> subPlus  =
+        final Point1D x      = thisLine.toSubSpace(crossing);
+        final SubHyperplane<Point1D> subPlus  =
                 new OrientedPoint(x, !direct, tolerance).wholeHyperplane();
-        final SubHyperplane<Euclidean1D> subMinus =
+        final SubHyperplane<Point1D> subMinus =
                 new OrientedPoint(x,  direct, tolerance).wholeHyperplane();
 
-        final BSPTree<Euclidean1D> splitTree = getRemainingRegion().getTree(false).split(subMinus);
-        final BSPTree<Euclidean1D> plusTree  = getRemainingRegion().isEmpty(splitTree.getPlus()) ?
-                                               new BSPTree<Euclidean1D>(Boolean.FALSE) :
-                                               new BSPTree<>(subPlus, new BSPTree<Euclidean1D>(Boolean.FALSE),
+        final BSPTree<Point1D> splitTree = getRemainingRegion().getTree(false).split(subMinus);
+        final BSPTree<Point1D> plusTree  = getRemainingRegion().isEmpty(splitTree.getPlus()) ?
+                                               new BSPTree<Point1D>(Boolean.FALSE) :
+                                               new BSPTree<>(subPlus, new BSPTree<Point1D>(Boolean.FALSE),
                                                                         splitTree.getPlus(), null);
-        final BSPTree<Euclidean1D> minusTree = getRemainingRegion().isEmpty(splitTree.getMinus()) ?
-                                               new BSPTree<Euclidean1D>(Boolean.FALSE) :
-                                               new BSPTree<>(subMinus, new BSPTree<Euclidean1D>(Boolean.FALSE),
+        final BSPTree<Point1D> minusTree = getRemainingRegion().isEmpty(splitTree.getMinus()) ?
+                                               new BSPTree<Point1D>(Boolean.FALSE) :
+                                               new BSPTree<>(subMinus, new BSPTree<Point1D>(Boolean.FALSE),
                                                                         splitTree.getMinus(), null);
         return new SplitSubHyperplane<>(new SubLine(thisLine.copySelf(), new IntervalsSet(plusTree, tolerance)),
                                                    new SubLine(thisLine.copySelf(), new IntervalsSet(minusTree, tolerance)));
diff --git a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
index 42fd4b7..1a7e5bd 100644
--- a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
+++ b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
@@ -16,22 +16,433 @@
  */
 package org.apache.commons.geometry.euclidean.twod;
 
-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 2D vector.
+/** This class represents a vector in two-dimensional Euclidean space.
+ * Instances of this class are guaranteed to be immutable.
  */
-public abstract class Vector2D implements Vector<Euclidean2D> {
+public final class Vector2D extends Cartesian2D implements EuclideanVector<Point2D, Vector2D> {
 
-    /** Get the abscissa of the vector.
-     * @return abscissa of the vector
-     * @see Cartesian2D#Cartesian2D(double, double)
+    /** Zero vector (coordinates: 0, 0). */
+    public static final Vector2D ZERO   = new Vector2D(0, 0);
+
+    /** Unit vector pointing in the direction of the positive x-axis. */
+    public static final Vector2D PLUS_X = new Vector2D(1, 0);
+
+    /** Unit vector pointing in the direction of the negative x-axis. */
+    public static final Vector2D MINUS_X = new Vector2D(-1, 0);
+
+    /** Unit vector pointing in the direction of the positive y-axis. */
+    public static final Vector2D PLUS_Y = new Vector2D(0, 1);
+
+    /** Unit vector pointing in the direction of the negative y-axis. */
+    public static final Vector2D MINUS_Y = new Vector2D(0, -1);
+
+    // CHECKSTYLE: stop ConstantName
+    /** A vector with all coordinates set to NaN. */
+    public static final Vector2D NaN = new Vector2D(Double.NaN, Double.NaN);
+    // CHECKSTYLE: resume ConstantName
+
+    /** A vector with all coordinates set to positive infinity. */
+    public static final Vector2D POSITIVE_INFINITY =
+        new Vector2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+
+    /** A vector with all coordinates set to negative infinity. */
+    public static final Vector2D NEGATIVE_INFINITY =
+        new Vector2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+
+    /** Serializable UID */
+    private static final long serialVersionUID = 1746839897232305304L;
+
+    /** Error message when norms are zero. */
+    private static final String ZERO_NORM_MSG = "Norm is zero";
+
+    /** Simple constructor.
+     * @param x abscissa (first coordinate)
+     * @param y ordinate (second coordinate)
+     */
+    public Vector2D(double x, double y) {
+        super(x, y);
+    }
+
+    /** Get the vector coordinates as a dimension 2 array.
+     * @return vector coordinates
+     * @see #Cartesian2D(double[])
+     */
+    @Override
+    public double[] toArray() {
+        return new double[] { x, y };
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Point2D asPoint() {
+        return new Point2D(x, y);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector2D 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 Vector2D add(Vector2D v) {
+        return new Vector2D(x + v.x, y + v.y);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector2D add(double factor, Vector2D v) {
+        return new Vector2D(x + (factor * v.x), y + (factor * v.y));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector2D subtract(Vector2D v) {
+        return new Vector2D(x - v.x, y - v.y);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector2D subtract(double factor, Vector2D v) {
+        return new Vector2D(x - (factor * v.x), y - (factor * v.y));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector2D negate() {
+        return new Vector2D(-x, -y);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector2D normalize() throws IllegalStateException {
+        double n = getNorm();
+        if (n == 0) {
+            throw new IllegalStateException(ZERO_NORM_MSG);
+        }
+        return scalarMultiply(1 / n);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Vector2D scalarMultiply(double a) {
+        return new Vector2D(a * x, a * y);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distance1(Vector2D v) {
+        double dx = Math.abs(x - v.x);
+        double dy = Math.abs(y - v.y);
+        return dx + dy;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distance(Vector2D v) {
+        return euclideanDistance(v);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distanceInf(Vector2D v) {
+        double dx = Math.abs(x - v.x);
+        double dy = Math.abs(y - v.y);
+        return Math.max(dx, dy);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double distanceSq(Vector2D v) {
+        double dx = x - v.x;
+        double dy = y - v.y;
+        return (dx * dx) + (dy * dy);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double dotProduct(Vector2D v) {
+        return LinearCombination.value(x, v.x, y, v.y);
+    }
+
+    /** Compute the angular separation in radians between this vector
+     * and the given vector.
+     * <p>This method computes the angular separation between the 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 vector to compute the angle with
+     * @return angular separation between this vector and v in radians
+     * @exception IllegalArgumentException if either vector has a zero norm
+     */
+    public double angle(Vector2D v) throws IllegalArgumentException {
+        double normProduct = getNorm() * v.getNorm();
+        if (normProduct == 0) {
+            throw new IllegalArgumentException(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
+            final double n = Math.abs(LinearCombination.value(x, v.y, -y, v.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);
+    }
+
+    /**
+     * 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://mathworld.wolfram.com/CrossProduct.html">Cross product (Mathworld)</a>
      */
-    public abstract double getX();
+    public double crossProduct(final Vector2D p1, final Vector2D p2) {
+        final double x1 = p2.x - p1.x;
+        final double y1 = y - p1.y;
+        final double x2 = x - p1.x;
+        final double y2 = p2.y - p1.y;
+        return LinearCombination.value(x1, y1, -x2, y2);
+    }
 
-    /** Get the ordinate of the vector.
-     * @return ordinate of the vector
-     * @see Cartesian2D#Cartesian2D(double, double)
+    /**
+     * 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
      */
-    public abstract double getY();
+    @Override
+    public int hashCode() {
+        if (isNaN()) {
+            return 542;
+        }
+        return 122 * (76 * Double.hashCode(x) +  Double.hashCode(y));
+    }
 
+    /**
+     * 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 Vector2D objects are equal, false if
+     *         object is null, not an instance of Vector2D, or
+     *         not equal to this Vector2D instance
+     *
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (other instanceof Vector2D) {
+            final Vector2D rhs = (Vector2D) other;
+            if (rhs.isNaN()) {
+                return this.isNaN();
+            }
+
+            return (x == rhs.x) && (y == rhs.y);
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "{" + x + "; " + y + "}";
+    }
+
+    /** 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(Vector2D)}
+     */
+    public static double dotProduct(Vector2D v1, Vector2D 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(Vector2D)}
+     */
+    public static double angle(Vector2D v1, Vector2D v2) {
+        return v1.angle(v2);
+    }
+
+    /** Returns a vector with the given coordinate values.
+     * @param x abscissa (first coordinate value)
+     * @param y abscissa (second coordinate value)
+     * @return vector instance
+     */
+    public static Vector2D of(double x, double y) {
+        return new Vector2D(x, y);
+    }
+
+    /** Returns a vector instance with the given coordinate values.
+     * @param value vector coordinates
+     * @return vector instance
+     */
+    public static Vector2D of(Cartesian2D value) {
+        return new Vector2D(value.x, value.y);
+    }
+
+    /** Creates a vector from the coordinates in the given 2-element array.
+     * @param v coordinates array
+     * @return new vector
+     * @exception IllegalArgumentException if the array does not have 2 elements
+     */
+    public static Vector2D of(double[] v) {
+        if (v.length != 2) {
+            throw new IllegalArgumentException("Dimension mismatch: " + v.length + " != 2");
+        }
+        return new Vector2D(v[0], v[1]);
+    }
+
+    /** 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 Vector2D linearCombination(double a, Cartesian2D c) {
+        return new Vector2D(a * c.x, a * c.y);
+    }
+
+    /** 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 Vector2D linearCombination(double a1, Cartesian2D c1, double a2, Cartesian2D c2) {
+        return new Vector2D(
+                LinearCombination.value(a1, c1.x, a2, c2.x),
+                LinearCombination.value(a1, c1.y, a2, c2.y));
+    }
+
+    /** 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 Vector2D linearCombination(double a1, Cartesian2D c1, double a2, Cartesian2D c2,
+            double a3, Cartesian2D c3) {
+        return new Vector2D(
+                LinearCombination.value(a1, c1.x, a2, c2.x, a3, c3.x),
+                LinearCombination.value(a1, c1.y, a2, c2.y, a3, c3.y));
+    }
+
+    /** 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 Vector2D linearCombination(double a1, Cartesian2D c1, double a2, Cartesian2D c2,
+            double a3, Cartesian2D c3, double a4, Cartesian2D c4) {
+        return new Vector2D(
+                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));
+    }
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/core/partitioning/CharacterizationTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/core/partitioning/CharacterizationTest.java
index cbbaff4..e2a94fb 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/core/partitioning/CharacterizationTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/core/partitioning/CharacterizationTest.java
@@ -18,13 +18,9 @@
 
 import java.util.Iterator;
 
-import org.apache.commons.geometry.core.partitioning.BSPTree;
-import org.apache.commons.geometry.core.partitioning.Characterization;
-import org.apache.commons.geometry.core.partitioning.NodesSet;
 import org.apache.commons.geometry.euclidean.oned.IntervalsSet;
-import org.apache.commons.geometry.euclidean.twod.Cartesian2D;
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
 import org.apache.commons.geometry.euclidean.twod.Line;
+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.junit.Assert;
@@ -41,11 +37,11 @@
     @Test
     public void testCharacterize_insideLeaf() {
         // arrange
-        BSPTree<Euclidean2D> tree = new BSPTree<>(Boolean.TRUE);
-        SubLine sub = buildSubLine(new Cartesian2D(0, -1), new Cartesian2D(0, 1));
+        BSPTree<Point2D> tree = new BSPTree<>(Boolean.TRUE);
+        SubLine sub = buildSubLine(new Point2D(0, -1), new Point2D(0, 1));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(true, ch.touchInside());
@@ -60,11 +56,11 @@ public void testCharacterize_insideLeaf() {
     @Test
     public void testCharacterize_outsideLeaf() {
         // arrange
-        BSPTree<Euclidean2D> tree = new BSPTree<>(Boolean.FALSE);
-        SubLine sub = buildSubLine(new Cartesian2D(0, -1), new Cartesian2D(0, 1));
+        BSPTree<Point2D> tree = new BSPTree<>(Boolean.FALSE);
+        SubLine sub = buildSubLine(new Point2D(0, -1), new Point2D(0, 1));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(false, ch.touchInside());
@@ -79,13 +75,13 @@ public void testCharacterize_outsideLeaf() {
     @Test
     public void testCharacterize_onPlusSide() {
         // arrange
-        BSPTree<Euclidean2D> tree = new BSPTree<>(Boolean.TRUE);
-        cut(tree, buildLine(new Cartesian2D(0, 0), new Cartesian2D(1, 0)));
+        BSPTree<Point2D> tree = new BSPTree<>(Boolean.TRUE);
+        cut(tree, buildLine(new Point2D(0, 0), new Point2D(1, 0)));
 
-        SubLine sub = buildSubLine(new Cartesian2D(0, -1), new Cartesian2D(0, -2));
+        SubLine sub = buildSubLine(new Point2D(0, -1), new Point2D(0, -2));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(false, ch.touchInside());
@@ -100,13 +96,13 @@ public void testCharacterize_onPlusSide() {
     @Test
     public void testCharacterize_onMinusSide() {
         // arrange
-        BSPTree<Euclidean2D> tree = new BSPTree<>(Boolean.TRUE);
-        cut(tree, buildLine(new Cartesian2D(0, 0), new Cartesian2D(1, 0)));
+        BSPTree<Point2D> tree = new BSPTree<>(Boolean.TRUE);
+        cut(tree, buildLine(new Point2D(0, 0), new Point2D(1, 0)));
 
-        SubLine sub = buildSubLine(new Cartesian2D(0, 1), new Cartesian2D(0, 2));
+        SubLine sub = buildSubLine(new Point2D(0, 1), new Point2D(0, 2));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(true, ch.touchInside());
@@ -121,13 +117,13 @@ public void testCharacterize_onMinusSide() {
     @Test
     public void testCharacterize_onBothSides() {
         // arrange
-        BSPTree<Euclidean2D> tree = new BSPTree<>(Boolean.TRUE);
-        cut(tree, buildLine(new Cartesian2D(0, 0), new Cartesian2D(1, 0)));
+        BSPTree<Point2D> tree = new BSPTree<>(Boolean.TRUE);
+        cut(tree, buildLine(new Point2D(0, 0), new Point2D(1, 0)));
 
-        SubLine sub = buildSubLine(new Cartesian2D(0, -1), new Cartesian2D(0, 1));
+        SubLine sub = buildSubLine(new Point2D(0, -1), new Point2D(0, 1));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(true, ch.touchInside());
@@ -135,11 +131,11 @@ public void testCharacterize_onBothSides() {
 
         SubLine inside = (SubLine) ch.insideTouching();
         Assert.assertEquals(1, inside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(0, 0), inside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(0, 1), inside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(0, 0), inside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(0, 1), inside.getSegments().get(0).getEnd());
 
         Assert.assertEquals(1, size(ch.getInsideSplitters()));
-        Iterator<BSPTree<Euclidean2D>> insideSplitterIter = ch.getInsideSplitters().iterator();
+        Iterator<BSPTree<Point2D>> insideSplitterIter = ch.getInsideSplitters().iterator();
         Assert.assertSame(tree, insideSplitterIter.next());
 
         Assert.assertEquals(true, ch.touchOutside());
@@ -147,25 +143,25 @@ public void testCharacterize_onBothSides() {
 
         SubLine outside = (SubLine) ch.outsideTouching();
         Assert.assertEquals(1, outside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(0, -1), outside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(0, 0), outside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(0, -1), outside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(0, 0), outside.getSegments().get(0).getEnd());
 
         Assert.assertEquals(1, size(ch.getOutsideSplitters()));
-        Iterator<BSPTree<Euclidean2D>> outsideSplitterIter = ch.getOutsideSplitters().iterator();
+        Iterator<BSPTree<Point2D>> outsideSplitterIter = ch.getOutsideSplitters().iterator();
         Assert.assertSame(tree, outsideSplitterIter.next());
     }
 
     @Test
     public void testCharacterize_multipleSplits_reunitedOnPlusSide() {
         // arrange
-        BSPTree<Euclidean2D> tree = new BSPTree<>(Boolean.TRUE);
-        cut(tree, buildLine(new Cartesian2D(0, 0), new Cartesian2D(1, 0)));
-        cut(tree.getMinus(), buildLine(new Cartesian2D(-1, 0), new Cartesian2D(0, 1)));
+        BSPTree<Point2D> tree = new BSPTree<>(Boolean.TRUE);
+        cut(tree, buildLine(new Point2D(0, 0), new Point2D(1, 0)));
+        cut(tree.getMinus(), buildLine(new Point2D(-1, 0), new Point2D(0, 1)));
 
-        SubLine sub = buildSubLine(new Cartesian2D(0, -2), new Cartesian2D(0, 2));
+        SubLine sub = buildSubLine(new Point2D(0, -2), new Point2D(0, 2));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(true, ch.touchInside());
@@ -173,11 +169,11 @@ public void testCharacterize_multipleSplits_reunitedOnPlusSide() {
 
         SubLine inside = (SubLine) ch.insideTouching();
         Assert.assertEquals(1, inside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(0, 1), inside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(0, 2), inside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(0, 1), inside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(0, 2), inside.getSegments().get(0).getEnd());
 
         Assert.assertEquals(2, size(ch.getInsideSplitters()));
-        Iterator<BSPTree<Euclidean2D>> insideSplitterIter = ch.getInsideSplitters().iterator();
+        Iterator<BSPTree<Point2D>> insideSplitterIter = ch.getInsideSplitters().iterator();
         Assert.assertSame(tree, insideSplitterIter.next());
         Assert.assertSame(tree.getMinus(), insideSplitterIter.next());
 
@@ -186,11 +182,11 @@ public void testCharacterize_multipleSplits_reunitedOnPlusSide() {
 
         SubLine outside = (SubLine) ch.outsideTouching();
         Assert.assertEquals(1, outside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(0, -2), outside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(0, 1), outside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(0, -2), outside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(0, 1), outside.getSegments().get(0).getEnd());
 
         Assert.assertEquals(2, size(ch.getOutsideSplitters()));
-        Iterator<BSPTree<Euclidean2D>> outsideSplitterIter = ch.getOutsideSplitters().iterator();
+        Iterator<BSPTree<Point2D>> outsideSplitterIter = ch.getOutsideSplitters().iterator();
         Assert.assertSame(tree, outsideSplitterIter.next());
         Assert.assertSame(tree.getMinus(), outsideSplitterIter.next());
     }
@@ -198,15 +194,15 @@ public void testCharacterize_multipleSplits_reunitedOnPlusSide() {
     @Test
     public void testCharacterize_multipleSplits_reunitedOnMinusSide() {
         // arrange
-        BSPTree<Euclidean2D> tree = new BSPTree<>(Boolean.TRUE);
-        cut(tree, buildLine(new Cartesian2D(0, 0), new Cartesian2D(1, 0)));
-        cut(tree.getMinus(), buildLine(new Cartesian2D(-1, 0), new Cartesian2D(0, 1)));
-        cut(tree.getMinus().getPlus(), buildLine(new Cartesian2D(-0.5, 0.5), new Cartesian2D(0, 0)));
+        BSPTree<Point2D> tree = new BSPTree<>(Boolean.TRUE);
+        cut(tree, buildLine(new Point2D(0, 0), new Point2D(1, 0)));
+        cut(tree.getMinus(), buildLine(new Point2D(-1, 0), new Point2D(0, 1)));
+        cut(tree.getMinus().getPlus(), buildLine(new Point2D(-0.5, 0.5), new Point2D(0, 0)));
 
-        SubLine sub = buildSubLine(new Cartesian2D(0, -2), new Cartesian2D(0, 2));
+        SubLine sub = buildSubLine(new Point2D(0, -2), new Point2D(0, 2));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(true, ch.touchInside());
@@ -214,11 +210,11 @@ public void testCharacterize_multipleSplits_reunitedOnMinusSide() {
 
         SubLine inside = (SubLine) ch.insideTouching();
         Assert.assertEquals(1, inside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(0, 0), inside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(0, 2), inside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(0, 0), inside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(0, 2), inside.getSegments().get(0).getEnd());
 
         Assert.assertEquals(2, size(ch.getInsideSplitters()));
-        Iterator<BSPTree<Euclidean2D>> insideSplitterIter = ch.getInsideSplitters().iterator();
+        Iterator<BSPTree<Point2D>> insideSplitterIter = ch.getInsideSplitters().iterator();
         Assert.assertSame(tree, insideSplitterIter.next());
         Assert.assertSame(tree.getMinus(), insideSplitterIter.next());
 
@@ -227,24 +223,24 @@ public void testCharacterize_multipleSplits_reunitedOnMinusSide() {
 
         SubLine outside = (SubLine) ch.outsideTouching();
         Assert.assertEquals(1, outside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(0, -2), outside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(0, 0), outside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(0, -2), outside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(0, 0), outside.getSegments().get(0).getEnd());
 
         Assert.assertEquals(1, size(ch.getOutsideSplitters()));
-        Iterator<BSPTree<Euclidean2D>> outsideSplitterIter = ch.getOutsideSplitters().iterator();
+        Iterator<BSPTree<Point2D>> outsideSplitterIter = ch.getOutsideSplitters().iterator();
         Assert.assertSame(tree, outsideSplitterIter.next());
     }
 
     @Test
     public void testCharacterize_onHyperplane_sameOrientation() {
         // arrange
-        BSPTree<Euclidean2D> tree = new BSPTree<>(Boolean.TRUE);
-        cut(tree, buildLine(new Cartesian2D(0, 0), new Cartesian2D(1, 0)));
+        BSPTree<Point2D> tree = new BSPTree<>(Boolean.TRUE);
+        cut(tree, buildLine(new Point2D(0, 0), new Point2D(1, 0)));
 
-        SubLine sub = buildSubLine(new Cartesian2D(0, 0), new Cartesian2D(1, 0));
+        SubLine sub = buildSubLine(new Point2D(0, 0), new Point2D(1, 0));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(true, ch.touchInside());
@@ -259,13 +255,13 @@ public void testCharacterize_onHyperplane_sameOrientation() {
     @Test
     public void testCharacterize_onHyperplane_oppositeOrientation() {
         // arrange
-        BSPTree<Euclidean2D> tree = new BSPTree<>(Boolean.TRUE);
-        cut(tree, buildLine(new Cartesian2D(0, 0), new Cartesian2D(1, 0)));
+        BSPTree<Point2D> tree = new BSPTree<>(Boolean.TRUE);
+        cut(tree, buildLine(new Point2D(0, 0), new Point2D(1, 0)));
 
-        SubLine sub = buildSubLine(new Cartesian2D(1, 0), new Cartesian2D(0, 0));
+        SubLine sub = buildSubLine(new Point2D(1, 0), new Point2D(0, 0));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(true, ch.touchInside());
@@ -280,14 +276,14 @@ public void testCharacterize_onHyperplane_oppositeOrientation() {
     @Test
     public void testCharacterize_onHyperplane_multipleSplits_sameOrientation() {
         // arrange
-        BSPTree<Euclidean2D> tree = new BSPTree<>(Boolean.TRUE);
-        cut(tree, buildLine(new Cartesian2D(0, 0), new Cartesian2D(1, 0)));
-        cut(tree.getMinus(), buildLine(new Cartesian2D(-1, 0), new Cartesian2D(0, 1)));
+        BSPTree<Point2D> tree = new BSPTree<>(Boolean.TRUE);
+        cut(tree, buildLine(new Point2D(0, 0), new Point2D(1, 0)));
+        cut(tree.getMinus(), buildLine(new Point2D(-1, 0), new Point2D(0, 1)));
 
-        SubLine sub = buildSubLine(new Cartesian2D(-2, 0), new Cartesian2D(2, 0));
+        SubLine sub = buildSubLine(new Point2D(-2, 0), new Point2D(2, 0));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(true, ch.touchInside());
@@ -295,11 +291,11 @@ public void testCharacterize_onHyperplane_multipleSplits_sameOrientation() {
 
         SubLine inside = (SubLine) ch.insideTouching();
         Assert.assertEquals(1, inside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(-2, 0), inside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(-1, 0), inside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(-2, 0), inside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(-1, 0), inside.getSegments().get(0).getEnd());
 
         Assert.assertEquals(1, size(ch.getInsideSplitters()));
-        Iterator<BSPTree<Euclidean2D>> insideSplitterIter = ch.getInsideSplitters().iterator();
+        Iterator<BSPTree<Point2D>> insideSplitterIter = ch.getInsideSplitters().iterator();
         Assert.assertSame(tree.getMinus(), insideSplitterIter.next());
 
         Assert.assertEquals(true, ch.touchOutside());
@@ -307,25 +303,25 @@ public void testCharacterize_onHyperplane_multipleSplits_sameOrientation() {
 
         SubLine outside = (SubLine) ch.outsideTouching();
         Assert.assertEquals(1, outside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(-1, 0), outside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(2, 0), outside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(-1, 0), outside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(2, 0), outside.getSegments().get(0).getEnd());
 
         Assert.assertEquals(1, size(ch.getOutsideSplitters()));
-        Iterator<BSPTree<Euclidean2D>> outsideSplitterIter = ch.getOutsideSplitters().iterator();
+        Iterator<BSPTree<Point2D>> outsideSplitterIter = ch.getOutsideSplitters().iterator();
         Assert.assertSame(tree.getMinus(), outsideSplitterIter.next());
     }
 
     @Test
     public void testCharacterize_onHyperplane_multipleSplits_oppositeOrientation() {
         // arrange
-        BSPTree<Euclidean2D> tree = new BSPTree<>(Boolean.TRUE);
-        cut(tree, buildLine(new Cartesian2D(0, 0), new Cartesian2D(1, 0)));
-        cut(tree.getMinus(), buildLine(new Cartesian2D(-1, 0), new Cartesian2D(0, 1)));
+        BSPTree<Point2D> tree = new BSPTree<>(Boolean.TRUE);
+        cut(tree, buildLine(new Point2D(0, 0), new Point2D(1, 0)));
+        cut(tree.getMinus(), buildLine(new Point2D(-1, 0), new Point2D(0, 1)));
 
-        SubLine sub = buildSubLine(new Cartesian2D(2, 0), new Cartesian2D(-2, 0));
+        SubLine sub = buildSubLine(new Point2D(2, 0), new Point2D(-2, 0));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(true, ch.touchInside());
@@ -333,11 +329,11 @@ public void testCharacterize_onHyperplane_multipleSplits_oppositeOrientation() {
 
         SubLine inside = (SubLine) ch.insideTouching();
         Assert.assertEquals(1, inside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(-1, 0), inside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(-2, 0), inside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(-1, 0), inside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(-2, 0), inside.getSegments().get(0).getEnd());
 
         Assert.assertEquals(1, size(ch.getInsideSplitters()));
-        Iterator<BSPTree<Euclidean2D>> insideSplitterIter = ch.getInsideSplitters().iterator();
+        Iterator<BSPTree<Point2D>> insideSplitterIter = ch.getInsideSplitters().iterator();
         Assert.assertSame(tree.getMinus(), insideSplitterIter.next());
 
         Assert.assertEquals(true, ch.touchOutside());
@@ -345,11 +341,11 @@ public void testCharacterize_onHyperplane_multipleSplits_oppositeOrientation() {
 
         SubLine outside = (SubLine) ch.outsideTouching();
         Assert.assertEquals(1, outside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(2, 0), outside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(-1, 0), outside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(2, 0), outside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(-1, 0), outside.getSegments().get(0).getEnd());
 
         Assert.assertEquals(1, size(ch.getOutsideSplitters()));
-        Iterator<BSPTree<Euclidean2D>> outsideSplitterIter = ch.getOutsideSplitters().iterator();
+        Iterator<BSPTree<Point2D>> outsideSplitterIter = ch.getOutsideSplitters().iterator();
         Assert.assertSame(tree.getMinus(), outsideSplitterIter.next());
     }
 
@@ -357,12 +353,12 @@ public void testCharacterize_onHyperplane_multipleSplits_oppositeOrientation() {
     public void testCharacterize_onHyperplane_box() {
         // arrange
         PolygonsSet poly = new PolygonsSet(0, 1, 0, 1, TEST_TOLERANCE);
-        BSPTree<Euclidean2D> tree = poly.getTree(false);
+        BSPTree<Point2D> tree = poly.getTree(false);
 
-        SubLine sub = buildSubLine(new Cartesian2D(2, 0), new Cartesian2D(-2, 0));
+        SubLine sub = buildSubLine(new Point2D(2, 0), new Point2D(-2, 0));
 
         // act
-        Characterization<Euclidean2D> ch = new Characterization<>(tree, sub);
+        Characterization<Point2D> ch = new Characterization<>(tree, sub);
 
         // assert
         Assert.assertEquals(true, ch.touchInside());
@@ -370,8 +366,8 @@ public void testCharacterize_onHyperplane_box() {
 
         SubLine inside = (SubLine) ch.insideTouching();
         Assert.assertEquals(1, inside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(1, 0), inside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(0, 0), inside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(1, 0), inside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(0, 0), inside.getSegments().get(0).getEnd());
 
         Assert.assertEquals(2, size(ch.getInsideSplitters()));
 
@@ -380,15 +376,15 @@ public void testCharacterize_onHyperplane_box() {
 
         SubLine outside = (SubLine) ch.outsideTouching();
         Assert.assertEquals(2, outside.getSegments().size());
-        assertVectorEquals(new Cartesian2D(2, 0), outside.getSegments().get(0).getStart());
-        assertVectorEquals(new Cartesian2D(1, 0), outside.getSegments().get(0).getEnd());
-        assertVectorEquals(new Cartesian2D(0, 0), outside.getSegments().get(1).getStart());
-        assertVectorEquals(new Cartesian2D(-2, 0), outside.getSegments().get(1).getEnd());
+        assertVectorEquals(new Point2D(2, 0), outside.getSegments().get(0).getStart());
+        assertVectorEquals(new Point2D(1, 0), outside.getSegments().get(0).getEnd());
+        assertVectorEquals(new Point2D(0, 0), outside.getSegments().get(1).getStart());
+        assertVectorEquals(new Point2D(-2, 0), outside.getSegments().get(1).getEnd());
 
         Assert.assertEquals(2, size(ch.getOutsideSplitters()));
     }
 
-    private void cut(BSPTree<Euclidean2D> tree, Line line) {
+    private void cut(BSPTree<Point2D> tree, Line line) {
         if (tree.insertCut(line)) {
             tree.setAttribute(null);
             tree.getPlus().setAttribute(Boolean.FALSE);
@@ -396,8 +392,8 @@ private void cut(BSPTree<Euclidean2D> tree, Line line) {
         }
     }
 
-    private int size(NodesSet<Euclidean2D> nodes) {
-        Iterator<BSPTree<Euclidean2D>> it = nodes.iterator();
+    private int size(NodesSet<Point2D> nodes) {
+        Iterator<BSPTree<Point2D>> it = nodes.iterator();
 
         int size = 0;
         while (it.hasNext()) {
@@ -408,18 +404,18 @@ private int size(NodesSet<Euclidean2D> nodes) {
         return size;
     }
 
-    private Line buildLine(Cartesian2D p1, Cartesian2D p2) {
+    private Line buildLine(Point2D p1, Point2D p2) {
         return new Line(p1, p2, TEST_TOLERANCE);
     }
 
-    private SubLine buildSubLine(Cartesian2D start, Cartesian2D end) {
+    private SubLine buildSubLine(Point2D start, Point2D end) {
         Line line = new Line(start, end, TEST_TOLERANCE);
         double lower = (line.toSubSpace(start)).getX();
         double upper = (line.toSubSpace(end)).getX();
         return new SubLine(line, new IntervalsSet(lower, upper, TEST_TOLERANCE));
     }
 
-    private void assertVectorEquals(Cartesian2D expected, Cartesian2D actual) {
+    private void assertVectorEquals(Point2D expected, Point2D actual) {
         String msg = "Expected vector to equal " + expected + " but was " + actual + ";";
         Assert.assertEquals(msg, expected.getX(), actual.getX(), TEST_TOLERANCE);
         Assert.assertEquals(msg, expected.getY(), actual.getY(), TEST_TOLERANCE);
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/EuclideanTestUtils.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/EuclideanTestUtils.java
index e0f4f05..9fb0df1 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/EuclideanTestUtils.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/EuclideanTestUtils.java
@@ -28,23 +28,21 @@
 import org.apache.commons.geometry.core.partitioning.TreeDumper;
 import org.apache.commons.geometry.core.partitioning.TreePrinter;
 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.OrientedPoint;
+import org.apache.commons.geometry.euclidean.oned.Point1D;
 import org.apache.commons.geometry.euclidean.oned.SubOrientedPoint;
-import org.apache.commons.geometry.euclidean.oned.Vector1D;
 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.threed.Point3D;
 import org.apache.commons.geometry.euclidean.threed.PolyhedronsSet;
 import org.apache.commons.geometry.euclidean.threed.SubPlane;
 import org.apache.commons.geometry.euclidean.threed.Vector3D;
 import org.apache.commons.geometry.euclidean.twod.Cartesian2D;
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
 import org.apache.commons.geometry.euclidean.twod.Line;
+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.Vector2D;
 import org.junit.Assert;
 
 /** Class containing various euclidean-related test utilities.
@@ -57,8 +55,8 @@
      * @param actual
      * @param tolerance
      */
-    public static void assertVectorEquals(Vector1D expected, Vector1D actual, double tolerance) {
-        String msg = "Expected vector to equal " + expected + " but was " + actual + ";";
+    public static void assertCoordinatesEqual(Cartesian1D expected, Cartesian1D actual, double tolerance) {
+        String msg = "Expected coordinates to equal " + expected + " but was " + actual + ";";
         Assert.assertEquals(msg, expected.getX(), actual.getX(), tolerance);
     }
 
@@ -68,8 +66,8 @@ public static void assertVectorEquals(Vector1D expected, Vector1D actual, double
      * @param actual
      * @param tolerance
      */
-    public static void assertVectorEquals(Vector2D expected, Vector2D actual, double tolerance) {
-        String msg = "Expected vector to equal " + expected + " but was " + actual + ";";
+    public static void assertCoordinatesEqual(Cartesian2D expected, Cartesian2D actual, double tolerance) {
+        String msg = "Expected coordinates to equal " + expected + " but was " + actual + ";";
         Assert.assertEquals(msg, expected.getX(), actual.getX(), tolerance);
         Assert.assertEquals(msg, expected.getY(), actual.getY(), tolerance);
     }
@@ -80,8 +78,8 @@ public static void assertVectorEquals(Vector2D expected, Vector2D actual, double
      * @param actual
      * @param tolerance
      */
-    public static void assertVectorEquals(Vector3D expected, Vector3D actual, double tolerance) {
-        String msg = "Expected vector to equal " + expected + " but was " + actual + ";";
+    public static void assertCoordinatesEqual(Cartesian3D expected, Cartesian3D actual, double tolerance) {
+        String msg = "Expected coordinates to equal " + expected + " but was " + actual + ";";
         Assert.assertEquals(msg, expected.getX(), actual.getX(), tolerance);
         Assert.assertEquals(msg, expected.getY(), actual.getY(), tolerance);
         Assert.assertEquals(msg, expected.getZ(), actual.getZ(), tolerance);
@@ -110,11 +108,11 @@ public static void assertNegativeInfinity(double value) {
      * @return string representation of the region
      */
     public static String dump(final IntervalsSet intervalsSet) {
-        final TreeDumper<Euclidean1D> visitor = new TreeDumper<Euclidean1D>("IntervalsSet", intervalsSet.getTolerance()) {
+        final TreeDumper<Point1D> visitor = new TreeDumper<Point1D>("IntervalsSet", intervalsSet.getTolerance()) {
 
             /** {@inheritDoc} */
             @Override
-            protected void formatHyperplane(final Hyperplane<Euclidean1D> hyperplane) {
+            protected void formatHyperplane(final Hyperplane<Point1D> hyperplane) {
                 final OrientedPoint h = (OrientedPoint) hyperplane;
                 getFormatter().format("%22.15e %b %22.15e",
                                       h.getLocation().getX(), h.isDirect(), h.getTolerance());
@@ -130,13 +128,13 @@ protected void formatHyperplane(final Hyperplane<Euclidean1D> hyperplane) {
      * @return string representation of the region
      */
     public static String dump(final PolygonsSet polygonsSet) {
-        final TreeDumper<Euclidean2D> visitor = new TreeDumper<Euclidean2D>("PolygonsSet", polygonsSet.getTolerance()) {
+        final TreeDumper<Point2D> visitor = new TreeDumper<Point2D>("PolygonsSet", polygonsSet.getTolerance()) {
 
             /** {@inheritDoc} */
             @Override
-            protected void formatHyperplane(final Hyperplane<Euclidean2D> hyperplane) {
+            protected void formatHyperplane(final Hyperplane<Point2D> hyperplane) {
                 final Line h = (Line) hyperplane;
-                final Cartesian2D p = h.toSpace(Cartesian1D.ZERO);
+                final Point2D p = h.toSpace(Point1D.ZERO);
                 getFormatter().format("%22.15e %22.15e %22.15e %22.15e",
                                       p.getX(), p.getY(), h.getAngle(), h.getTolerance());
             }
@@ -151,13 +149,13 @@ protected void formatHyperplane(final Hyperplane<Euclidean2D> hyperplane) {
      * @return string representation of the region
      */
     public static String dump(final PolyhedronsSet polyhedronsSet) {
-        final TreeDumper<Euclidean3D> visitor = new TreeDumper<Euclidean3D>("PolyhedronsSet", polyhedronsSet.getTolerance()) {
+        final TreeDumper<Point3D> visitor = new TreeDumper<Point3D>("PolyhedronsSet", polyhedronsSet.getTolerance()) {
 
             /** {@inheritDoc} */
             @Override
-            protected void formatHyperplane(final Hyperplane<Euclidean3D> hyperplane) {
+            protected void formatHyperplane(final Hyperplane<Point3D> hyperplane) {
                 final Plane h = (Plane) hyperplane;
-                final Cartesian3D p = h.toSpace(Cartesian2D.ZERO);
+                final Point3D p = h.toSpace(Point2D.ZERO);
                 getFormatter().format("%22.15e %22.15e %22.15e %22.15e %22.15e %22.15e %22.15e",
                                       p.getX(), p.getY(), p.getZ(),
                                       h.getNormal().getX(), h.getNormal().getY(), h.getNormal().getZ(),
@@ -177,13 +175,13 @@ protected void formatHyperplane(final Hyperplane<Euclidean3D> hyperplane) {
      */
     public static IntervalsSet parseIntervalsSet(final String s)
         throws IOException, ParseException {
-        final TreeBuilder<Euclidean1D> builder = new TreeBuilder<Euclidean1D>("IntervalsSet", s) {
+        final TreeBuilder<Point1D> builder = new TreeBuilder<Point1D>("IntervalsSet", s) {
 
             /** {@inheritDoc} */
             @Override
             public OrientedPoint parseHyperplane()
                 throws IOException, ParseException {
-                return new OrientedPoint(new Cartesian1D(getNumber()), getBoolean(), getNumber());
+                return new OrientedPoint(new Point1D(getNumber()), getBoolean(), getNumber());
             }
 
         };
@@ -198,13 +196,13 @@ public OrientedPoint parseHyperplane()
      */
     public static PolygonsSet parsePolygonsSet(final String s)
         throws IOException, ParseException {
-        final TreeBuilder<Euclidean2D> builder = new TreeBuilder<Euclidean2D>("PolygonsSet", s) {
+        final TreeBuilder<Point2D> builder = new TreeBuilder<Point2D>("PolygonsSet", s) {
 
             /** {@inheritDoc} */
             @Override
             public Line parseHyperplane()
                 throws IOException, ParseException {
-                return new Line(new Cartesian2D(getNumber(), getNumber()), getNumber(), getNumber());
+                return new Line(new Point2D(getNumber(), getNumber()), getNumber(), getNumber());
             }
 
         };
@@ -219,14 +217,14 @@ public Line parseHyperplane()
      */
     public static PolyhedronsSet parsePolyhedronsSet(final String s)
         throws IOException, ParseException {
-        final TreeBuilder<Euclidean3D> builder = new TreeBuilder<Euclidean3D>("PolyhedronsSet", s) {
+        final TreeBuilder<Point3D> builder = new TreeBuilder<Point3D>("PolyhedronsSet", s) {
 
             /** {@inheritDoc} */
             @Override
             public Plane parseHyperplane()
                 throws IOException, ParseException {
-                return new Plane(new Cartesian3D(getNumber(), getNumber(), getNumber()),
-                                 new Cartesian3D(getNumber(), getNumber(), getNumber()),
+                return new Plane(new Point3D(getNumber(), getNumber(), getNumber()),
+                                 new Vector3D(getNumber(), getNumber(), getNumber()),
                                  getNumber());
             }
 
@@ -240,7 +238,7 @@ public Plane parseHyperplane()
      * the console. This is intended for quick debugging of small trees.
      * @param tree
      */
-    public static void printTree1D(BSPTree<Euclidean1D> tree) {
+    public static void printTree1D(BSPTree<Point1D> tree) {
         TreePrinter1D printer = new TreePrinter1D();
         System.out.println(printer.writeAsString(tree));
     }
@@ -249,7 +247,7 @@ public static void printTree1D(BSPTree<Euclidean1D> tree) {
      * the console. This is intended for quick debugging of small trees.
      * @param tree
      */
-    public static void printTree2D(BSPTree<Euclidean2D> tree) {
+    public static void printTree2D(BSPTree<Point2D> tree) {
         TreePrinter2D printer = new TreePrinter2D();
         System.out.println(printer.writeAsString(tree));
     }
@@ -258,7 +256,7 @@ public static void printTree2D(BSPTree<Euclidean2D> tree) {
      * the console. This is intended for quick debugging of small trees.
      * @param tree
      */
-    public static void printTree3D(BSPTree<Euclidean3D> tree) {
+    public static void printTree3D(BSPTree<Point3D> tree) {
         TreePrinter3D printer = new TreePrinter3D();
         System.out.println(printer.writeAsString(tree));
     }
@@ -266,11 +264,11 @@ public static void printTree3D(BSPTree<Euclidean3D> tree) {
 
     /** Class for creating string representations of 1D {@link BSPTree}s.
      */
-    public static class TreePrinter1D extends TreePrinter<Euclidean1D> {
+    public static class TreePrinter1D extends TreePrinter<Point1D> {
 
         /** {@inheritDoc} */
         @Override
-        protected void writeInternalNode(BSPTree<Euclidean1D> node) {
+        protected void writeInternalNode(BSPTree<Point1D> node) {
             SubOrientedPoint cut = (SubOrientedPoint) node.getCut();
 
             OrientedPoint hyper = (OrientedPoint) cut.getHyperplane();
@@ -306,16 +304,16 @@ protected void writeInternalNode(BSPTree<Euclidean1D> node) {
 
     /** Class for creating string representations of 2D {@link BSPTree}s.
      */
-    public static class TreePrinter2D extends TreePrinter<Euclidean2D> {
+    public static class TreePrinter2D extends TreePrinter<Point2D> {
 
         /** {@inheritDoc} */
         @Override
-        protected void writeInternalNode(BSPTree<Euclidean2D> node) {
+        protected void writeInternalNode(BSPTree<Point2D> node) {
             SubLine cut = (SubLine) node.getCut();
             Line line = (Line) cut.getHyperplane();
             IntervalsSet remainingRegion = (IntervalsSet) cut.getRemainingRegion();
 
-            write("cut = { angle: " + Math.toDegrees(line.getAngle()) + ", origin: " + line.toSpace(Cartesian1D.ZERO) + "}");
+            write("cut = { angle: " + Math.toDegrees(line.getAngle()) + ", origin: " + line.toSpace(Point1D.ZERO) + "}");
             write(", remainingRegion: [");
 
             boolean isFirst = true;
@@ -335,11 +333,11 @@ protected void writeInternalNode(BSPTree<Euclidean2D> node) {
 
     /** Class for creating string representations of 3D {@link BSPTree}s.
      */
-    public static class TreePrinter3D extends TreePrinter<Euclidean3D> {
+    public static class TreePrinter3D extends TreePrinter<Point3D> {
 
         /** {@inheritDoc} */
         @Override
-        protected void writeInternalNode(BSPTree<Euclidean3D> node) {
+        protected void writeInternalNode(BSPTree<Point3D> node) {
             SubPlane cut = (SubPlane) node.getCut();
             Plane plane = (Plane) cut.getHyperplane();
             PolygonsSet polygon = (PolygonsSet) cut.getRemainingRegion();
@@ -348,10 +346,10 @@ protected void writeInternalNode(BSPTree<Euclidean3D> node) {
             write(", remainingRegion = [");
 
             boolean isFirst = true;
-            for (Cartesian2D[] loop : polygon.getVertices()) {
+            for (Point2D[] loop : polygon.getVertices()) {
                 // convert to 3-space for easier debugging
-                List<Cartesian3D> loop3 = new ArrayList<>();
-                for (Cartesian2D vertex : loop) {
+                List<Point3D> loop3 = new ArrayList<>();
+                for (Point2D vertex : loop) {
                     if (vertex != null) {
                         loop3.add(plane.toSpace(vertex));
                     }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Cartesian1DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Cartesian1DTest.java
index 2db8ded..835a368 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Cartesian1DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Cartesian1DTest.java
@@ -14,18 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.commons.geometry.euclidean.oned;
 
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.text.NumberFormat;
-import java.util.Locale;
-
-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.core.Precision;
+import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -34,352 +25,50 @@
     private static final double TEST_TOLERANCE = 1e-15;
 
     @Test
-    public void testConstants() {
-        // act/assert
-        checkVector(Cartesian1D.ZERO, 0.0);
-        checkVector(Cartesian1D.ONE, 1.0);
-        checkVector(Cartesian1D.NaN, Double.NaN);
-        checkVector(Cartesian1D.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
-        checkVector(Cartesian1D.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
-    }
-
-    @Test
-    public void testConstructor_simple() {
-        // act/assert
-        checkVector(new Cartesian1D(2), 2);
-        checkVector(new Cartesian1D(-2), -2);
-        checkVector(new Cartesian1D(Math.PI), Math.PI);
-    }
-
-    @Test
-    public void testConstructor_multiplicative() {
-        // act/assert
-        checkVector(new Cartesian1D(2, new Cartesian1D(3)), 6);
-        checkVector(new Cartesian1D(-2, new Cartesian1D(3)), -6);
-    }
-
-    @Test
-    public void testConstructor_linear2() {
-        // act/assert
-        checkVector(new Cartesian1D(
-                2, new Cartesian1D(3),
-                5, new Cartesian1D(7)), 41);
-        checkVector(new Cartesian1D(
-                2, new Cartesian1D(3),
-                -5, new Cartesian1D(7)),-29);
-    }
-
-    @Test
-    public void testConstructor_linear3() {
-        // act/assert
-        checkVector(new Cartesian1D(
-                2, new Cartesian1D(3),
-                5, new Cartesian1D(7),
-                11, new Cartesian1D(13)), 184);
-        checkVector(new Cartesian1D(
-                2, new Cartesian1D(3),
-                5, new Cartesian1D(7),
-                -11, new Cartesian1D(13)), -102);
-    }
-
-    @Test
-    public void testConstructor_linear4() {
-        // act/assert
-        checkVector(new Cartesian1D(
-                2, new Cartesian1D(3),
-                5, new Cartesian1D(7),
-                11, new Cartesian1D(13),
-                17, new Cartesian1D(19)), 507);
-        checkVector(new Cartesian1D(
-                2, new Cartesian1D(3),
-                5, new Cartesian1D(7),
-                11, new Cartesian1D(13),
-                -17, new Cartesian1D(19)), -139);
-    }
-
-    @Test
-    public void testSpace() {
-        // act
-        Space space = new Cartesian1D(1).getSpace();
-
-        // assert
-        Assert.assertEquals(1, space.getDimension());
-    }
-
-    @Test
-    public void testZero() {
-        // act
-        Cartesian1D zero = new Cartesian1D(1).getZero();
-
-        // assert
-        Assert.assertEquals(0, zero.getX(), TEST_TOLERANCE);
-    }
-
-    @Test
-    public void testNorm1() {
-        // act/assert
-        Assert.assertEquals(0.0, Cartesian1D.ZERO.getNorm1(), TEST_TOLERANCE);
-        Assert.assertEquals(6.0, new Cartesian1D(6).getNorm1(), TEST_TOLERANCE);
-        Assert.assertEquals(6.0, new Cartesian1D(-6).getNorm1(), TEST_TOLERANCE);
-    }
-
-    @Test
-    public void testNorm() {
-        // act/assert
-        Assert.assertEquals(0.0, Cartesian1D.ZERO.getNorm(), TEST_TOLERANCE);
-        Assert.assertEquals(3.0, new Cartesian1D(3).getNorm(), TEST_TOLERANCE);
-        Assert.assertEquals(3.0, new Cartesian1D(-3).getNorm(), TEST_TOLERANCE);
-    }
-
-    @Test
-    public void testNormSq() {
+    public void testCoordinates() {
         // act/assert
-        Assert.assertEquals(0.0, new Cartesian1D(0).getNormSq(), TEST_TOLERANCE);
-        Assert.assertEquals(9.0, new Cartesian1D(3).getNormSq(), TEST_TOLERANCE);
-        Assert.assertEquals(9.0, new Cartesian1D(-3).getNormSq(), TEST_TOLERANCE);
-    }
+        Assert.assertEquals(0.0, new StubCartesian1D(0.0).getX(), TEST_TOLERANCE);
+        Assert.assertEquals(-1.0, new StubCartesian1D(-1.0).getX(), TEST_TOLERANCE);
+        Assert.assertEquals(1.0, new StubCartesian1D(1.0).getX(), TEST_TOLERANCE);
 
-    @Test
-    public void testNormInf() {
-        // act/assert
-        Assert.assertEquals(0.0, Cartesian1D.ZERO.getNormInf(), TEST_TOLERANCE);
-        Assert.assertEquals(3.0, new Cartesian1D(3).getNormInf(), TEST_TOLERANCE);
-        Assert.assertEquals(3.0, new Cartesian1D(-3).getNormInf(), TEST_TOLERANCE);
+        Assert.assertEquals(Double.NaN, new StubCartesian1D(Double.NaN).getX(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertNegativeInfinity(new StubCartesian1D(Double.NEGATIVE_INFINITY).getX());
+        EuclideanTestUtils.assertPositiveInfinity(new StubCartesian1D(Double.POSITIVE_INFINITY).getX());
     }
 
     @Test
-    public void testAdd() {
+    public void testDimension() {
         // arrange
-        Cartesian1D v1 = new Cartesian1D(1);
-        Cartesian1D v2 = new Cartesian1D(-3);
+        Cartesian1D c = new StubCartesian1D(0.0);
 
         // act/assert
-        v1 = v1.add(v2);
-        checkVector(v1, -2);
-
-        checkVector(v2.add(v1), -5);
-        checkVector(v2.add(3, v1), -9);
-    }
-
-    @Test
-    public void testSubtract() {
-        // arrange
-        Cartesian1D v1 = new Cartesian1D(1);
-        Cartesian1D v2 = new Cartesian1D(-3);
-
-        // act/assert
-        v1 = v1.subtract(v2);
-        checkVector(v1, 4);
-
-        checkVector(v2.subtract(v1), -7);
-        checkVector(v2.subtract(3, v1), -15);
-    }
-
-    @Test
-    public void testNormalize() {
-        // act/assert
-        checkVector(new Cartesian1D(1).normalize(), 1);
-        checkVector(new Cartesian1D(-1).normalize(), -1);
-        checkVector(new Cartesian1D(5).normalize(), 1);
-        checkVector(new Cartesian1D(-5).normalize(), -1);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testNormalize_zeroNorm() {
-        // act
-        Cartesian1D.ZERO.normalize();
-    }
-
-    @Test
-    public void testNegate() {
-        // act/assert
-        checkVector(new Cartesian1D(0.1).negate(), -0.1);
-        checkVector(new Cartesian1D(-0.1).negate(), 0.1);
-    }
-
-    @Test
-    public void testScalarMultiply() {
-        // act/assert
-        checkVector(new Cartesian1D(1).scalarMultiply(3), 3);
-        checkVector(new Cartesian1D(1).scalarMultiply(-3), -3);
-
-        checkVector(new Cartesian1D(1.5).scalarMultiply(7), 10.5);
-        checkVector(new Cartesian1D(-1.5).scalarMultiply(7), -10.5);
+        Assert.assertEquals(1, c.getDimension());
     }
 
     @Test
     public void testNaN() {
         // act/assert
-        Assert.assertTrue(new Cartesian1D(Double.NaN).isNaN());
-        Assert.assertFalse(new Cartesian1D(1).isNaN());
-        Assert.assertFalse(new Cartesian1D(Double.NEGATIVE_INFINITY).isNaN());
-    }
-
-    @Test
-    public void testInfinite() {
-        // act/assert
-        Assert.assertTrue(new Cartesian1D(Double.NEGATIVE_INFINITY).isInfinite());
-        Assert.assertTrue(new Cartesian1D(Double.POSITIVE_INFINITY).isInfinite());
-        Assert.assertFalse(new Cartesian1D(1).isInfinite());
-        Assert.assertFalse(new Cartesian1D(Double.NaN).isInfinite());
-    }
-
-    @Test
-    public void testDistance1() {
-        // arrange
-        Cartesian1D v1 = new Cartesian1D(1);
-        Cartesian1D v2 = new Cartesian1D(-4);
-
-        // act/assert
-        Assert.assertEquals(0.0, v1.distance1(v1), TEST_TOLERANCE);
-
-        Assert.assertEquals(5.0, v1.distance1(v2), TEST_TOLERANCE);
-        Assert.assertEquals(v1.subtract(v2).getNorm1(), v1.distance1(v2), TEST_TOLERANCE);
-
-        Assert.assertEquals(0.0, new Cartesian1D(-1).distance1(new Cartesian1D(-1)), TEST_TOLERANCE);
-    }
-
-    @Test
-    public void testDistance() {
-        // arrange
-        Cartesian1D v1 = new Cartesian1D(1);
-        Cartesian1D v2 = new Cartesian1D(-4);
-
-        // act/assert
-        Assert.assertEquals(0.0, v1.distance(v1), TEST_TOLERANCE);
-
-        Assert.assertEquals(5.0, v1.distance(v2), TEST_TOLERANCE);
-        Assert.assertEquals(5.0, v1.distance((Point<Euclidean1D>) v2), TEST_TOLERANCE);
-        Assert.assertEquals(5.0, v1.distance((Vector<Euclidean1D>) v2), TEST_TOLERANCE);
-        Assert.assertEquals(v1.subtract(v2).getNorm(), v1.distance(v2), TEST_TOLERANCE);
-
-        Assert.assertEquals(0.0, new Cartesian1D(-1).distance(new Cartesian1D(-1)), TEST_TOLERANCE);
-    }
-
-    @Test
-    public void testDistance_static() {
-        // arrange
-        Cartesian1D v1 = new Cartesian1D(1);
-        Cartesian1D v2 = new Cartesian1D(-4);
-
-        // act/assert
-        Assert.assertEquals(0.0, Cartesian1D.distance(v1, v1), TEST_TOLERANCE);
-
-        Assert.assertEquals(5.0, Cartesian1D.distance(v1, v2), TEST_TOLERANCE);
-        Assert.assertEquals(v1.subtract(v2).getNorm(), Cartesian1D.distance(v1, v2), TEST_TOLERANCE);
-
-        Assert.assertEquals(0.0, Cartesian1D.distance(new Cartesian1D(-1), new Cartesian1D(-1)), TEST_TOLERANCE);
-    }
-
-    @Test
-    public void testDistanceInf() {
-        // arrange
-        Cartesian1D v1 = new Cartesian1D(1);
-        Cartesian1D v2 = new Cartesian1D(-4);
-
-        // act/assert
-        Assert.assertEquals(0.0, new Cartesian1D(-1).distanceInf(new Cartesian1D(-1)), TEST_TOLERANCE);
-        Assert.assertEquals(5.0, v1.distanceInf(v2), TEST_TOLERANCE);
-
-        Assert.assertEquals(v1.subtract(v2).getNormInf(), v1.distanceInf(v2), TEST_TOLERANCE);
-    }
-
-    @Test
-    public void testDistanceInf_static() {
-        // arrange
-        Cartesian1D v1 = new Cartesian1D(1);
-        Cartesian1D v2 = new Cartesian1D(-4);
-
-        // act/assert
-        Assert.assertEquals(0.0, Cartesian1D.distanceInf(new Cartesian1D(-1), new Cartesian1D(-1)), TEST_TOLERANCE);
-        Assert.assertEquals(5.0, Cartesian1D.distanceInf(v1, v2), TEST_TOLERANCE);
-
-        Assert.assertEquals(v1.subtract(v2).getNormInf(), Cartesian1D.distanceInf(v1, v2), TEST_TOLERANCE);
-    }
-
-    @Test
-    public void testDistanceSq() {
-        // arrange
-        Cartesian1D v1 = new Cartesian1D(1);
-        Cartesian1D v2 = new Cartesian1D(-4);
-
-        // act/assert
-        Assert.assertEquals(0.0, new Cartesian1D(-1).distanceSq(new Cartesian1D(-1)), TEST_TOLERANCE);
-        Assert.assertEquals(25.0, v1.distanceSq(v2), TEST_TOLERANCE);
+        Assert.assertTrue(new StubCartesian1D(Double.NaN).isNaN());
 
-        Assert.assertEquals(Cartesian1D.distance(v1, v2) * Cartesian1D.distance(v1, v2),
-                            v1.distanceSq(v2), TEST_TOLERANCE);
+        Assert.assertFalse(new StubCartesian1D(1).isNaN());
+        Assert.assertFalse(new StubCartesian1D(Double.NEGATIVE_INFINITY).isNaN());
     }
 
     @Test
-    public void testDistanceSq_static() {
-        // arrange
-        Cartesian1D v1 = new Cartesian1D(1);
-        Cartesian1D v2 = new Cartesian1D(-4);
-
-        // act/assert
-        Assert.assertEquals(0.0, Cartesian1D.distanceSq(new Cartesian1D(-1), new Cartesian1D(-1)), TEST_TOLERANCE);
-        Assert.assertEquals(25.0, Cartesian1D.distanceSq(v1, v2), TEST_TOLERANCE);
-
-        Assert.assertEquals(Cartesian1D.distance(v1, v2) * Cartesian1D.distance(v1, v2),
-                            Cartesian1D.distanceSq(v1, v2), TEST_TOLERANCE);
-    }
-
-    @Test
-    public void testDotProduct() {
-        // act/assert
-        Assert.assertEquals(6.0, new Cartesian1D(2).dotProduct(new Cartesian1D(3)), TEST_TOLERANCE);
-        Assert.assertEquals(-6.0, new Cartesian1D(2).dotProduct(new Cartesian1D(-3)), TEST_TOLERANCE);
-    }
-
-    @Test
-    public void testEquals() {
-        // arrange
-        Cartesian1D u1 = new Cartesian1D(1);
-        Cartesian1D u2 = new Cartesian1D(1);
-
-        // act/assert
-        Assert.assertFalse(u1.equals(null));
-        Assert.assertFalse(u1.equals(new Object()));
-
-        Assert.assertTrue(u1.equals(u1));
-        Assert.assertTrue(u1.equals(u2));
-
-        Assert.assertFalse(u1.equals(new Cartesian1D(-1)));
-        Assert.assertFalse(u1.equals(new Cartesian1D(1 + 10 * Precision.EPSILON)));
-
-        Assert.assertTrue(new Cartesian1D(Double.NaN).equals(new Cartesian1D(Double.NaN)));
-    }
-
-    @Test
-    public void testHash() {
-        // arrange
-        Cartesian1D u = new Cartesian1D(1);
-        Cartesian1D v = new Cartesian1D(1 + 10 * Precision.EPSILON);
-
+    public void testInfinite() {
         // act/assert
-        Assert.assertTrue(u.hashCode() != v.hashCode());
-        Assert.assertEquals(new Cartesian1D(Double.NaN).hashCode(), new Cartesian1D(Double.NaN).hashCode());
-    }
+        Assert.assertTrue(new StubCartesian1D(Double.NEGATIVE_INFINITY).isInfinite());
+        Assert.assertTrue(new StubCartesian1D(Double.POSITIVE_INFINITY).isInfinite());
 
-    @Test
-    public void testToString() {
-        // act/assert
-        Assert.assertEquals("{3}", new Cartesian1D(3).toString());
-        Assert.assertEquals("{-3}", new Cartesian1D(-3).toString());
+        Assert.assertFalse(new StubCartesian1D(1).isInfinite());
+        Assert.assertFalse(new StubCartesian1D(Double.NaN).isInfinite());
     }
 
-    @Test
-    public void testToString_numberFormat() {
-        // arrange
-        NumberFormat format = new DecimalFormat("0.000", new DecimalFormatSymbols(Locale.US));
-
-        // act/assert
-        Assert.assertEquals("{-1.000}", new Cartesian1D(-1).toString(format));
-        Assert.assertEquals("{3.142}", new Cartesian1D(Math.PI).toString(format));
-    }
+    private static class StubCartesian1D extends Cartesian1D {
+        private static final long serialVersionUID = 1L;
 
-    private void checkVector(Cartesian1D v, double x) {
-        Assert.assertEquals(x, v.getX(), TEST_TOLERANCE);
+        public StubCartesian1D(double x) {
+            super(x);
+        }
     }
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/IntervalsSetTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/IntervalsSetTest.java
index 0ead59c..dd70adf 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/IntervalsSetTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/IntervalsSetTest.java
@@ -44,9 +44,9 @@ public void testInterval_wholeNumberLine() {
         EuclideanTestUtils.assertPositiveInfinity(set.getSup());
         EuclideanTestUtils.assertPositiveInfinity(set.getSize());
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian1D.NaN, (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point1D.NaN, set.getBarycenter(), TEST_TOLERANCE);
 
-        BSPTree<Euclidean1D> tree = set.getTree(true);
+        BSPTree<Point1D> tree = set.getTree(true);
         Assert.assertEquals(Boolean.TRUE, tree.getAttribute());
         Assert.assertNull(tree.getCut());
         Assert.assertNull(tree.getMinus());
@@ -72,9 +72,9 @@ public void testInterval_doubleOpenInterval() {
         EuclideanTestUtils.assertPositiveInfinity(set.getSup());
         EuclideanTestUtils.assertPositiveInfinity(set.getSize());
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian1D.NaN, (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point1D.NaN, set.getBarycenter(), TEST_TOLERANCE);
 
-        BSPTree<Euclidean1D> tree = set.getTree(true);
+        BSPTree<Point1D> tree = set.getTree(true);
         Assert.assertEquals(Boolean.TRUE, tree.getAttribute());
         Assert.assertNull(tree.getCut());
         Assert.assertNull(tree.getMinus());
@@ -100,7 +100,7 @@ public void testInterval_openInterval_positive() {
         EuclideanTestUtils.assertPositiveInfinity(set.getSup());
         EuclideanTestUtils.assertPositiveInfinity(set.getSize());
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian1D.NaN, (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point1D.NaN, set.getBarycenter(), TEST_TOLERANCE);
 
         List<Interval> intervals = set.asList();
         Assert.assertEquals(1, intervals.size());
@@ -125,7 +125,7 @@ public void testInterval_openInterval_negative() {
         Assert.assertEquals(9.0, set.getSup(), TEST_TOLERANCE);
         EuclideanTestUtils.assertPositiveInfinity(set.getSize());
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian1D.NaN, (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point1D.NaN, set.getBarycenter(), TEST_TOLERANCE);
 
         List<Interval> intervals = set.asList();
         Assert.assertEquals(1, intervals.size());
@@ -150,7 +150,7 @@ public void testInterval_singleClosedInterval() {
         Assert.assertEquals(9.0, set.getSup(), TEST_TOLERANCE);
         Assert.assertEquals(10.0, set.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian1D(4.0), (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point1D(4.0), set.getBarycenter(), TEST_TOLERANCE);
 
         List<Interval> intervals = set.asList();
         Assert.assertEquals(1, intervals.size());
@@ -176,7 +176,7 @@ public void testInterval_singlePoint() {
         Assert.assertEquals(1.0, set.getSup(), TEST_TOLERANCE);
         Assert.assertEquals(0.0, set.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian1D(1.0), (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point1D(1.0), set.getBarycenter(), TEST_TOLERANCE);
 
         List<Interval> intervals = set.asList();
         Assert.assertEquals(1, intervals.size());
@@ -192,7 +192,7 @@ public void testInterval_singlePoint() {
     @Test
     public void testFromBoundaries_wholeNumberLine() {
         // arrange
-        List<SubHyperplane<Euclidean1D>> boundaries = new ArrayList<>();
+        List<SubHyperplane<Point1D>> boundaries = new ArrayList<>();
 
         // act
         IntervalsSet set = new IntervalsSet(boundaries, TEST_TOLERANCE);
@@ -203,9 +203,9 @@ public void testFromBoundaries_wholeNumberLine() {
         EuclideanTestUtils.assertPositiveInfinity(set.getSup());
         EuclideanTestUtils.assertPositiveInfinity(set.getSize());
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian1D.NaN, (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point1D.NaN, set.getBarycenter(), TEST_TOLERANCE);
 
-        BSPTree<Euclidean1D> tree = set.getTree(true);
+        BSPTree<Point1D> tree = set.getTree(true);
         Assert.assertEquals(Boolean.TRUE, tree.getAttribute());
         Assert.assertNull(tree.getCut());
         Assert.assertNull(tree.getMinus());
@@ -223,7 +223,7 @@ public void testFromBoundaries_wholeNumberLine() {
     @Test
     public void testFromBoundaries_openInterval_positive() {
         // arrange
-        List<SubHyperplane<Euclidean1D>> boundaries = new ArrayList<>();
+        List<SubHyperplane<Point1D>> boundaries = new ArrayList<>();
         boundaries.add(subOrientedPoint(9.0, false));
 
         // act
@@ -235,7 +235,7 @@ public void testFromBoundaries_openInterval_positive() {
         EuclideanTestUtils.assertPositiveInfinity(set.getSup());
         EuclideanTestUtils.assertPositiveInfinity(set.getSize());
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian1D.NaN, (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point1D.NaN, set.getBarycenter(), TEST_TOLERANCE);
 
         List<Interval> intervals = set.asList();
         Assert.assertEquals(1, intervals.size());
@@ -252,7 +252,7 @@ public void testFromBoundaries_openInterval_positive() {
     @Test
     public void testFromBoundaries_openInterval_negative() {
         // arrange
-        List<SubHyperplane<Euclidean1D>> boundaries = new ArrayList<>();
+        List<SubHyperplane<Point1D>> boundaries = new ArrayList<>();
         boundaries.add(subOrientedPoint(9.0, true));
 
         // act
@@ -264,7 +264,7 @@ public void testFromBoundaries_openInterval_negative() {
         Assert.assertEquals(9.0, set.getSup(), TEST_TOLERANCE);
         EuclideanTestUtils.assertPositiveInfinity(set.getSize());
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian1D.NaN, (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point1D.NaN, set.getBarycenter(), TEST_TOLERANCE);
 
         List<Interval> intervals = set.asList();
         Assert.assertEquals(1, intervals.size());
@@ -281,7 +281,7 @@ public void testFromBoundaries_openInterval_negative() {
     @Test
     public void testFromBoundaries_singleClosedInterval() {
         // arrange
-        List<SubHyperplane<Euclidean1D>> boundaries = new ArrayList<>();
+        List<SubHyperplane<Point1D>> boundaries = new ArrayList<>();
         boundaries.add(subOrientedPoint(-1.0, false));
         boundaries.add(subOrientedPoint(9.0, true));
 
@@ -294,7 +294,7 @@ public void testFromBoundaries_singleClosedInterval() {
         Assert.assertEquals(9.0, set.getSup(), TEST_TOLERANCE);
         Assert.assertEquals(10.0, set.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian1D(4.0), (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point1D(4.0), set.getBarycenter(), TEST_TOLERANCE);
 
         List<Interval> intervals = set.asList();
         Assert.assertEquals(1, intervals.size());
@@ -312,7 +312,7 @@ public void testFromBoundaries_singleClosedInterval() {
     @Test
     public void testFromBoundaries_multipleClosedIntervals() {
         // arrange
-        List<SubHyperplane<Euclidean1D>> boundaries = new ArrayList<>();
+        List<SubHyperplane<Point1D>> boundaries = new ArrayList<>();
         boundaries.add(subOrientedPoint(-1.0, false));
         boundaries.add(subOrientedPoint(2.0, true));
         boundaries.add(subOrientedPoint(5.0, false));
@@ -327,7 +327,7 @@ public void testFromBoundaries_multipleClosedIntervals() {
         Assert.assertEquals(9.0, set.getSup(), TEST_TOLERANCE);
         Assert.assertEquals(7.0, set.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian1D(29.5 / 7.0), (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point1D(29.5 / 7.0), set.getBarycenter(), TEST_TOLERANCE);
 
         List<Interval> intervals = set.asList();
         Assert.assertEquals(2, intervals.size());
@@ -348,7 +348,7 @@ public void testFromBoundaries_multipleClosedIntervals() {
     @Test
     public void testFromBoundaries_mixedOpenAndClosedIntervals() {
         // arrange
-        List<SubHyperplane<Euclidean1D>> boundaries = new ArrayList<>();
+        List<SubHyperplane<Point1D>> boundaries = new ArrayList<>();
         boundaries.add(subOrientedPoint(-2.0, true));
         boundaries.add(subOrientedPoint(-1.0, false));
         boundaries.add(subOrientedPoint(2.0, true));
@@ -365,7 +365,7 @@ public void testFromBoundaries_mixedOpenAndClosedIntervals() {
         EuclideanTestUtils.assertPositiveInfinity(set.getSup());
         EuclideanTestUtils.assertPositiveInfinity(set.getSize());
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian1D(Double.NaN), (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point1D(Double.NaN), set.getBarycenter(), TEST_TOLERANCE);
 
         List<Interval> intervals = set.asList();
         Assert.assertEquals(4, intervals.size());
@@ -393,7 +393,7 @@ public void testFromBoundaries_intervalEqualToTolerance_onlyFirstBoundaryUsed()
         double tolerance = 1e-3;
         double first = 1.0;
         double second = 1.0 + tolerance;
-        List<SubHyperplane<Euclidean1D>> boundaries = new ArrayList<>();
+        List<SubHyperplane<Point1D>> boundaries = new ArrayList<>();
         boundaries.add(subOrientedPoint(first, true, tolerance));
         boundaries.add(subOrientedPoint(second, false, tolerance));
 
@@ -406,7 +406,7 @@ public void testFromBoundaries_intervalEqualToTolerance_onlyFirstBoundaryUsed()
         Assert.assertEquals(first, set.getSup(), TEST_TOLERANCE);
         EuclideanTestUtils.assertPositiveInfinity(set.getSize());
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian1D.NaN, (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point1D.NaN, set.getBarycenter(), TEST_TOLERANCE);
 
         List<Interval> intervals = set.asList();
         Assert.assertEquals(1, intervals.size());
@@ -423,7 +423,7 @@ public void testFromBoundaries_intervalSmallerThanTolerance_onlyFirstBoundaryUse
         double tolerance = 1e-3;
         double first = 1.0;
         double second = 1.0 - 1e-4;
-        List<SubHyperplane<Euclidean1D>> boundaries = new ArrayList<>();
+        List<SubHyperplane<Point1D>> boundaries = new ArrayList<>();
         boundaries.add(subOrientedPoint(first, false, tolerance));
         boundaries.add(subOrientedPoint(second, true, tolerance));
 
@@ -436,7 +436,7 @@ public void testFromBoundaries_intervalSmallerThanTolerance_onlyFirstBoundaryUse
         EuclideanTestUtils.assertPositiveInfinity(set.getSup());
         EuclideanTestUtils.assertPositiveInfinity(set.getSize());
         Assert.assertEquals(0.0, set.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian1D.NaN, (Cartesian1D) set.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point1D.NaN, set.getBarycenter(), TEST_TOLERANCE);
 
         List<Interval> intervals = set.asList();
         Assert.assertEquals(1, intervals.size());
@@ -450,7 +450,7 @@ public void testFromBoundaries_intervalSmallerThanTolerance_onlyFirstBoundaryUse
     @Test
     public void testProjectToBoundary() {
         // arrange
-        List<SubHyperplane<Euclidean1D>> boundaries = new ArrayList<>();
+        List<SubHyperplane<Point1D>> boundaries = new ArrayList<>();
         boundaries.add(subOrientedPoint(-2.0, true));
         boundaries.add(subOrientedPoint(-1.0, false));
         boundaries.add(subOrientedPoint(2.0, true));
@@ -461,43 +461,43 @@ public void testProjectToBoundary() {
         IntervalsSet set = new IntervalsSet(boundaries, TEST_TOLERANCE);
 
         // act/assert
-        assertProjection(new Cartesian1D(-2), -1, set, new Cartesian1D(-3));
-        assertProjection(new Cartesian1D(-2), 0, set, new Cartesian1D(-2));
-        assertProjection(new Cartesian1D(-2), 0.1, set, new Cartesian1D(-1.9));
+        assertProjection(new Point1D(-2), -1, set, new Point1D(-3));
+        assertProjection(new Point1D(-2), 0, set, new Point1D(-2));
+        assertProjection(new Point1D(-2), 0.1, set, new Point1D(-1.9));
 
-        assertProjection(new Cartesian1D(-1), 0.5, set, new Cartesian1D(-1.5));
-        assertProjection(new Cartesian1D(-1), 0.1, set, new Cartesian1D(-1.1));
-        assertProjection(new Cartesian1D(-1), 0, set, new Cartesian1D(-1));
-        assertProjection(new Cartesian1D(-1), -1, set, new Cartesian1D(0));
+        assertProjection(new Point1D(-1), 0.5, set, new Point1D(-1.5));
+        assertProjection(new Point1D(-1), 0.1, set, new Point1D(-1.1));
+        assertProjection(new Point1D(-1), 0, set, new Point1D(-1));
+        assertProjection(new Point1D(-1), -1, set, new Point1D(0));
 
-        assertProjection(new Cartesian1D(2), -1, set, new Cartesian1D(1));
-        assertProjection(new Cartesian1D(2), 0, set, new Cartesian1D(2));
-        assertProjection(new Cartesian1D(2), 1, set, new Cartesian1D(3));
+        assertProjection(new Point1D(2), -1, set, new Point1D(1));
+        assertProjection(new Point1D(2), 0, set, new Point1D(2));
+        assertProjection(new Point1D(2), 1, set, new Point1D(3));
 
-        assertProjection(new Cartesian1D(5), 1, set, new Cartesian1D(4));
-        assertProjection(new Cartesian1D(5), 0, set, new Cartesian1D(5));
+        assertProjection(new Point1D(5), 1, set, new Point1D(4));
+        assertProjection(new Point1D(5), 0, set, new Point1D(5));
 
-        assertProjection(new Cartesian1D(5), -1, set, new Cartesian1D(6));
-        assertProjection(new Cartesian1D(5), -2, set, new Cartesian1D(7));
+        assertProjection(new Point1D(5), -1, set, new Point1D(6));
+        assertProjection(new Point1D(5), -2, set, new Point1D(7));
 
-        assertProjection(new Cartesian1D(9), -1, set, new Cartesian1D(8));
-        assertProjection(new Cartesian1D(9), 0, set, new Cartesian1D(9));
-        assertProjection(new Cartesian1D(9), 0.1, set, new Cartesian1D(9.1));
+        assertProjection(new Point1D(9), -1, set, new Point1D(8));
+        assertProjection(new Point1D(9), 0, set, new Point1D(9));
+        assertProjection(new Point1D(9), 0.1, set, new Point1D(9.1));
 
-        assertProjection(new Cartesian1D(10), 0, set, new Cartesian1D(10));
-        assertProjection(new Cartesian1D(10), -1, set, new Cartesian1D(11));
+        assertProjection(new Point1D(10), 0, set, new Point1D(10));
+        assertProjection(new Point1D(10), -1, set, new Point1D(11));
     }
 
     @Test
     public void testInterval() {
         IntervalsSet set = new IntervalsSet(2.3, 5.7, 1.0e-10);
         Assert.assertEquals(3.4, set.getSize(), 1.0e-10);
-        Assert.assertEquals(4.0, ((Cartesian1D) set.getBarycenter()).getX(), 1.0e-10);
-        Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Cartesian1D(2.3)));
-        Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Cartesian1D(5.7)));
-        Assert.assertEquals(Region.Location.OUTSIDE,  set.checkPoint(new Cartesian1D(1.2)));
-        Assert.assertEquals(Region.Location.OUTSIDE,  set.checkPoint(new Cartesian1D(8.7)));
-        Assert.assertEquals(Region.Location.INSIDE,   set.checkPoint(new Cartesian1D(3.0)));
+        Assert.assertEquals(4.0, set.getBarycenter().getX(), 1.0e-10);
+        Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(2.3)));
+        Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(5.7)));
+        Assert.assertEquals(Region.Location.OUTSIDE,  set.checkPoint(new Point1D(1.2)));
+        Assert.assertEquals(Region.Location.OUTSIDE,  set.checkPoint(new Point1D(8.7)));
+        Assert.assertEquals(Region.Location.INSIDE,   set.checkPoint(new Point1D(3.0)));
         Assert.assertEquals(2.3, set.getInf(), 1.0e-10);
         Assert.assertEquals(5.7, set.getSup(), 1.0e-10);
     }
@@ -505,17 +505,17 @@ public void testInterval() {
     @Test
     public void testInfinite() {
         IntervalsSet set = new IntervalsSet(9.0, Double.POSITIVE_INFINITY, 1.0e-10);
-        Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Cartesian1D(9.0)));
-        Assert.assertEquals(Region.Location.OUTSIDE,  set.checkPoint(new Cartesian1D(8.4)));
+        Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new Point1D(9.0)));
+        Assert.assertEquals(Region.Location.OUTSIDE,  set.checkPoint(new Point1D(8.4)));
         for (double e = 1.0; e <= 6.0; e += 1.0) {
             Assert.assertEquals(Region.Location.INSIDE,
-                                set.checkPoint(new Cartesian1D(Math.pow(10.0, e))));
+                                set.checkPoint(new Point1D(Math.pow(10.0, e))));
         }
         Assert.assertTrue(Double.isInfinite(set.getSize()));
         Assert.assertEquals(9.0, set.getInf(), 1.0e-10);
         Assert.assertTrue(Double.isInfinite(set.getSup()));
 
-        set = (IntervalsSet) new RegionFactory<Euclidean1D>().getComplement(set);
+        set = (IntervalsSet) new RegionFactory<Point1D>().getComplement(set);
         Assert.assertEquals(9.0, set.getSup(), 1.0e-10);
         Assert.assertTrue(Double.isInfinite(set.getInf()));
 
@@ -524,7 +524,7 @@ public void testInfinite() {
     @Test
     public void testBooleanOperations() {
         // arrange
-        RegionFactory<Euclidean1D> factory = new RegionFactory<>();
+        RegionFactory<Point1D> factory = new RegionFactory<>();
 
         // act
         IntervalsSet set = (IntervalsSet)
@@ -538,7 +538,7 @@ public void testBooleanOperations() {
         Assert.assertEquals(11.0, set.getSup(), TEST_TOLERANCE);
 
         Assert.assertEquals(5.0, set.getSize(), TEST_TOLERANCE);
-        Assert.assertEquals(5.9, ((Cartesian1D) set.getBarycenter()).getX(), TEST_TOLERANCE);
+        Assert.assertEquals(5.9, set.getBarycenter().getX(), TEST_TOLERANCE);
 
         assertLocation(Region.Location.OUTSIDE, set, 0.0);
         assertLocation(Region.Location.OUTSIDE, set, 4.0);
@@ -558,7 +558,7 @@ public void testBooleanOperations() {
     }
 
     private void assertLocation(Region.Location location, IntervalsSet set, double pt) {
-        Assert.assertEquals(location, set.checkPoint(new Cartesian1D(pt)));
+        Assert.assertEquals(location, set.checkPoint(new Point1D(pt)));
     }
 
     private void assertInterval(double expectedInf, double expectedSup, Interval actual, double tolerance) {
@@ -566,12 +566,12 @@ private void assertInterval(double expectedInf, double expectedSup, Interval act
         Assert.assertEquals(expectedSup, actual.getSup(), tolerance);
     }
 
-    private void assertProjection(Cartesian1D expectedProjection, double expectedOffset,
-            IntervalsSet set, Cartesian1D toProject) {
-        BoundaryProjection<Euclidean1D> proj = set.projectToBoundary(toProject);
+    private void assertProjection(Point1D expectedProjection, double expectedOffset,
+            IntervalsSet set, Point1D toProject) {
+        BoundaryProjection<Point1D> proj = set.projectToBoundary(toProject);
 
-        EuclideanTestUtils.assertVectorEquals(toProject, (Cartesian1D) proj.getOriginal(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(expectedProjection, (Cartesian1D) proj.getProjected(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(toProject, proj.getOriginal(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(expectedProjection, proj.getProjected(), TEST_TOLERANCE);
         Assert.assertEquals(expectedOffset, proj.getOffset(), TEST_TOLERANCE);
     }
 
@@ -581,6 +581,6 @@ private SubOrientedPoint subOrientedPoint(double location, boolean direct) {
 
     private SubOrientedPoint subOrientedPoint(double location, boolean direct, double tolerance) {
         // the remaining region isn't necessary for creating 1D boundaries so we can set it to null here
-        return new SubOrientedPoint(new OrientedPoint(new Cartesian1D(location), direct, tolerance), null);
+        return new SubOrientedPoint(new OrientedPoint(new Point1D(location), direct, tolerance), null);
     }
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/OrientedPointTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/OrientedPointTest.java
index 75ecf3a..a986b91 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/OrientedPointTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/OrientedPointTest.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.euclidean.EuclideanTestUtils;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
@@ -28,7 +26,7 @@
     @Test
     public void testConstructor() {
         // act
-        OrientedPoint pt = new OrientedPoint(new Cartesian1D(2.0), true, 1e-5);
+        OrientedPoint pt = new OrientedPoint(new Point1D(2.0), true, 1e-5);
 
         // assert
         Assert.assertEquals(2.0, pt.getLocation().getX(), Precision.EPSILON);
@@ -39,7 +37,7 @@ public void testConstructor() {
     @Test
     public void testCopySelf() {
         // arrange
-        OrientedPoint orig = new OrientedPoint(new Cartesian1D(2.0), true, 1e-5);
+        OrientedPoint orig = new OrientedPoint(new Point1D(2.0), true, 1e-5);
 
         // act
         OrientedPoint copy = orig.copySelf();
@@ -54,67 +52,37 @@ public void testCopySelf() {
     @Test
     public void testGetOffset_direct_point() {
         // arrange
-        OrientedPoint pt = new OrientedPoint(new Cartesian1D(-1.0), true, 1e-5);
+        OrientedPoint pt = new OrientedPoint(new Point1D(-1.0), true, 1e-5);
 
         // act/assert
-        Assert.assertEquals(-99, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(-100)), Precision.EPSILON);
-        Assert.assertEquals(-1, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(-2)), Precision.EPSILON);
-        Assert.assertEquals(-0.01, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(-1.01)), Precision.EPSILON);
-        Assert.assertEquals(0.0, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(-1.0)), Precision.EPSILON);
-        Assert.assertEquals(0.01, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(-0.99)), Precision.EPSILON);
-        Assert.assertEquals(1, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(0)), Precision.EPSILON);
-        Assert.assertEquals(101, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(100)), Precision.EPSILON);
+        Assert.assertEquals(-99, pt.getOffset(new Point1D(-100)), Precision.EPSILON);
+        Assert.assertEquals(-1, pt.getOffset(new Point1D(-2)), Precision.EPSILON);
+        Assert.assertEquals(-0.01, pt.getOffset(new Point1D(-1.01)), Precision.EPSILON);
+        Assert.assertEquals(0.0, pt.getOffset(new Point1D(-1.0)), Precision.EPSILON);
+        Assert.assertEquals(0.01, pt.getOffset(new Point1D(-0.99)), Precision.EPSILON);
+        Assert.assertEquals(1, pt.getOffset(new Point1D(0)), Precision.EPSILON);
+        Assert.assertEquals(101, pt.getOffset(new Point1D(100)), Precision.EPSILON);
     }
 
     @Test
     public void testGetOffset_notDirect_point() {
         // arrange
-        OrientedPoint pt = new OrientedPoint(new Cartesian1D(-1.0), false, 1e-5);
+        OrientedPoint pt = new OrientedPoint(new Point1D(-1.0), false, 1e-5);
 
         // act/assert
-        Assert.assertEquals(99, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(-100)), Precision.EPSILON);
-        Assert.assertEquals(1, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(-2)), Precision.EPSILON);
-        Assert.assertEquals(0.01, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(-1.01)), Precision.EPSILON);
-        Assert.assertEquals(0.0, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(-1.0)), Precision.EPSILON);
-        Assert.assertEquals(-0.01, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(-0.99)), Precision.EPSILON);
-        Assert.assertEquals(-1, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(0)), Precision.EPSILON);
-        Assert.assertEquals(-101, pt.getOffset((Point<Euclidean1D>) new Cartesian1D(100)), Precision.EPSILON);
-    }
-
-    @Test
-    public void testGetOffset_direct_vector() {
-        // arrange
-        OrientedPoint pt = new OrientedPoint(new Cartesian1D(-1.0), true, 1e-5);
-
-        // act/assert
-        Assert.assertEquals(-99, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(-100)), Precision.EPSILON);
-        Assert.assertEquals(-1, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(-2)), Precision.EPSILON);
-        Assert.assertEquals(-0.01, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(-1.01)), Precision.EPSILON);
-        Assert.assertEquals(-0.0, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(-1.0)), Precision.EPSILON);
-        Assert.assertEquals(0.01, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(-0.99)), Precision.EPSILON);
-        Assert.assertEquals(1, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(0)), Precision.EPSILON);
-        Assert.assertEquals(101, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(100)), Precision.EPSILON);
-    }
-
-    @Test
-    public void testGetOffset_notDirect_vector() {
-        // arrange
-        OrientedPoint pt = new OrientedPoint(new Cartesian1D(-1.0), false, 1e-5);
-
-        // act/assert
-        Assert.assertEquals(99, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(-100)), Precision.EPSILON);
-        Assert.assertEquals(1, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(-2)), Precision.EPSILON);
-        Assert.assertEquals(0.01, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(-1.01)), Precision.EPSILON);
-        Assert.assertEquals(0.0, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(-1.0)), Precision.EPSILON);
-        Assert.assertEquals(-0.01, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(-0.99)), Precision.EPSILON);
-        Assert.assertEquals(-1, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(0)), Precision.EPSILON);
-        Assert.assertEquals(-101, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(100)), Precision.EPSILON);
+        Assert.assertEquals(99, pt.getOffset(new Point1D(-100)), Precision.EPSILON);
+        Assert.assertEquals(1, pt.getOffset(new Point1D(-2)), Precision.EPSILON);
+        Assert.assertEquals(0.01, pt.getOffset(new Point1D(-1.01)), Precision.EPSILON);
+        Assert.assertEquals(0.0, pt.getOffset(new Point1D(-1.0)), Precision.EPSILON);
+        Assert.assertEquals(-0.01, pt.getOffset(new Point1D(-0.99)), Precision.EPSILON);
+        Assert.assertEquals(-1, pt.getOffset(new Point1D(0)), Precision.EPSILON);
+        Assert.assertEquals(-101, pt.getOffset(new Point1D(100)), Precision.EPSILON);
     }
 
     @Test
     public void testWholeHyperplane() {
         // arrange
-        OrientedPoint pt = new OrientedPoint(new Cartesian1D(1.0), false, 1e-5);
+        OrientedPoint pt = new OrientedPoint(new Point1D(1.0), false, 1e-5);
 
         // act
         SubOrientedPoint subPt = pt.wholeHyperplane();
@@ -127,7 +95,7 @@ public void testWholeHyperplane() {
     @Test
     public void testWholeSpace() {
         // arrange
-        OrientedPoint pt = new OrientedPoint(new Cartesian1D(1.0), false, 1e-5);
+        OrientedPoint pt = new OrientedPoint(new Point1D(1.0), false, 1e-5);
 
         // act
         IntervalsSet set = pt.wholeSpace();
@@ -140,10 +108,10 @@ public void testWholeSpace() {
     @Test
     public void testSameOrientationAs() {
         // arrange
-        OrientedPoint notDirect1 = new OrientedPoint(new Cartesian1D(1.0), false, 1e-5);
-        OrientedPoint notDirect2 = new OrientedPoint(new Cartesian1D(1.0), false, 1e-5);
-        OrientedPoint direct1 = new OrientedPoint(new Cartesian1D(1.0), true, 1e-5);
-        OrientedPoint direct2 = new OrientedPoint(new Cartesian1D(1.0), true, 1e-5);
+        OrientedPoint notDirect1 = new OrientedPoint(new Point1D(1.0), false, 1e-5);
+        OrientedPoint notDirect2 = new OrientedPoint(new Point1D(1.0), false, 1e-5);
+        OrientedPoint direct1 = new OrientedPoint(new Point1D(1.0), true, 1e-5);
+        OrientedPoint direct2 = new OrientedPoint(new Point1D(1.0), true, 1e-5);
 
         // act/assert
         Assert.assertTrue(notDirect1.sameOrientationAs(notDirect1));
@@ -161,19 +129,19 @@ public void testSameOrientationAs() {
     @Test
     public void testProject() {
         // arrange
-        OrientedPoint pt = new OrientedPoint(new Cartesian1D(1.0), true, 1e-5);
+        OrientedPoint pt = new OrientedPoint(new Point1D(1.0), true, 1e-5);
 
         // act/assert
-        Assert.assertEquals(1.0, ((Cartesian1D) pt.project(new Cartesian1D(-1.0))).getX(), Precision.EPSILON);
-        Assert.assertEquals(1.0, ((Cartesian1D) pt.project(new Cartesian1D(0.0))).getX(), Precision.EPSILON);
-        Assert.assertEquals(1.0, ((Cartesian1D) pt.project(new Cartesian1D(1.0))).getX(), Precision.EPSILON);
-        Assert.assertEquals(1.0, ((Cartesian1D) pt.project(new Cartesian1D(100.0))).getX(), Precision.EPSILON);
+        Assert.assertEquals(1.0, pt.project(new Point1D(-1.0)).getX(), Precision.EPSILON);
+        Assert.assertEquals(1.0, pt.project(new Point1D(0.0)).getX(), Precision.EPSILON);
+        Assert.assertEquals(1.0, pt.project(new Point1D(1.0)).getX(), Precision.EPSILON);
+        Assert.assertEquals(1.0, pt.project(new Point1D(100.0)).getX(), Precision.EPSILON);
     }
 
     @Test
     public void testRevertSelf() {
         // arrange
-        OrientedPoint pt = new OrientedPoint(new Cartesian1D(2.0), true, 1e-5);
+        OrientedPoint pt = new OrientedPoint(new Point1D(2.0), true, 1e-5);
 
         // act
         pt.revertSelf();
@@ -183,7 +151,7 @@ public void testRevertSelf() {
         Assert.assertFalse(pt.isDirect());
         Assert.assertEquals(1e-5, pt.getTolerance(), Precision.EPSILON);
 
-        Assert.assertEquals(1, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(1.0)), Precision.EPSILON);
-        Assert.assertEquals(-1, pt.getOffset((Vector<Euclidean1D>) new Cartesian1D(3.0)), Precision.EPSILON);
+        Assert.assertEquals(1, pt.getOffset(new Point1D(1.0)), Precision.EPSILON);
+        Assert.assertEquals(-1, pt.getOffset(new Point1D(3.0)), Precision.EPSILON);
     }
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Point1DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Point1DTest.java
new file mode 100644
index 0000000..702abef
--- /dev/null
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Point1DTest.java
@@ -0,0 +1,262 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.geometry.euclidean.oned;
+
+import java.util.regex.Pattern;
+
+import org.apache.commons.numbers.core.Precision;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Point1DTest {
+
+    private static final double TEST_TOLERANCE = 1e-15;
+
+    @Test
+    public void testConstants() {
+        // act/assert
+        checkPoint(Point1D.ZERO, 0.0);
+        checkPoint(Point1D.ONE, 1.0);
+        checkPoint(Point1D.NaN, Double.NaN);
+        checkPoint(Point1D.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+        checkPoint(Point1D.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+    }
+
+    @Test
+    public void testAsVector() {
+        // act/assert
+        checkVector(Point1D.of(0).asVector(), 0.0);
+        checkVector(Point1D.of(1).asVector(), 1.0);
+        checkVector(Point1D.of(-1).asVector(), -1.0);
+        checkVector(Point1D.NaN.asVector(), Double.NaN);
+        checkVector(Point1D.POSITIVE_INFINITY.asVector(), Double.POSITIVE_INFINITY);
+        checkVector(Point1D.NEGATIVE_INFINITY.asVector(), Double.NEGATIVE_INFINITY);
+    }
+
+    @Test
+    public void testDistance() {
+        // arrange
+        Point1D p1 = Point1D.of(1);
+        Point1D p2 = Point1D.of(-4);
+        Point1D p3 = Point1D.of(10);
+
+        // act/assert
+        Assert.assertEquals(0.0, p1.distance(p1), TEST_TOLERANCE);
+        Assert.assertEquals(0.0, p2.distance(p2), TEST_TOLERANCE);
+        Assert.assertEquals(0.0, p3.distance(p3), TEST_TOLERANCE);
+
+        Assert.assertEquals(5.0, p1.distance(p2), TEST_TOLERANCE);
+        Assert.assertEquals(5.0, p2.distance(p1), TEST_TOLERANCE);
+
+        Assert.assertEquals(9.0, p1.distance(p3), TEST_TOLERANCE);
+        Assert.assertEquals(9.0, p3.distance(p1), TEST_TOLERANCE);
+
+        Assert.assertEquals(14.0, p2.distance(p3), TEST_TOLERANCE);
+        Assert.assertEquals(14.0, p3.distance(p2), TEST_TOLERANCE);
+
+        Assert.assertEquals(0.0, Point1D.of(-1).distance(Point1D.of(-1)), TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testSubtract() {
+        // arrange
+        Point1D p1 = Point1D.of(1);
+        Point1D p2 = Point1D.of(-4);
+        Point1D p3 = Point1D.of(10);
+
+        // act/assert
+        checkVector(p1.subtract(p1), 0.0);
+        checkVector(p2.subtract(p2), 0.0);
+        checkVector(p3.subtract(p3), 0.0);
+
+        checkVector(p1.subtract(p2), 5.0);
+        checkVector(p2.subtract(p1), -5.0);
+
+        checkVector(p1.subtract(p3), -9.0);
+        checkVector(p3.subtract(p1), 9.0);
+
+        checkVector(p2.subtract(p3), -14.0);
+        checkVector(p3.subtract(p2), 14.0);
+    }
+
+    @Test
+    public void testVectorTo() {
+        // arrange
+        Point1D p1 = Point1D.of(1);
+        Point1D p2 = Point1D.of(-4);
+        Point1D p3 = Point1D.of(10);
+
+        // act/assert
+        checkVector(p1.vectorTo(p1), 0.0);
+        checkVector(p2.vectorTo(p2), 0.0);
+        checkVector(p3.vectorTo(p3), 0.0);
+
+        checkVector(p1.vectorTo(p2), -5.0);
+        checkVector(p2.vectorTo(p1), 5.0);
+
+        checkVector(p1.vectorTo(p3), 9.0);
+        checkVector(p3.vectorTo(p1), -9.0);
+
+        checkVector(p2.vectorTo(p3), 14.0);
+        checkVector(p3.vectorTo(p2), -14.0);
+    }
+
+    @Test
+    public void testAdd() {
+        // arrange
+        Point1D p1 = Point1D.of(2.0);
+        Point1D p2 = Point1D.of(-2.0);
+
+        // act/assert
+        checkPoint(p1.add(Vector1D.ZERO), 2.0);
+        checkPoint(p1.add(Vector1D.of(1)), 3.0);
+        checkPoint(p1.add(Vector1D.of(-1)), 1.0);
+
+        checkPoint(p2.add(Vector1D.ZERO), -2.0);
+        checkPoint(p2.add(Vector1D.of(1)), -1.0);
+        checkPoint(p2.add(Vector1D.of(-1)), -3.0);
+    }
+
+    @Test
+    public void testHashCode() {
+        // arrange
+        Point1D u = Point1D.of(1);
+        Point1D v = Point1D.of(1 + 10 * Precision.EPSILON);
+        Point1D w = Point1D.of(1);
+
+        // act/assert
+        Assert.assertTrue(u.hashCode() != v.hashCode());
+        Assert.assertEquals(u.hashCode(), w.hashCode());
+
+        Assert.assertEquals(Point1D.of(Double.NaN).hashCode(), Point1D.NaN.hashCode());
+        Assert.assertEquals(Point1D.of(Double.NaN).hashCode(), Point1D.of(Double.NaN).hashCode());
+    }
+
+    @Test
+    public void testEquals() {
+        // arrange
+        Point1D u1 = Point1D.of(1);
+        Point1D u2 = Point1D.of(1);
+
+        // act/assert
+        Assert.assertFalse(u1.equals(null));
+        Assert.assertFalse(u1.equals(new Object()));
+
+        Assert.assertTrue(u1.equals(u1));
+        Assert.assertTrue(u1.equals(u2));
+
+        Assert.assertFalse(u1.equals(Point1D.of(-1)));
+        Assert.assertFalse(u1.equals(Point1D.of(1 + 10 * Precision.EPSILON)));
+
+        Assert.assertTrue(Point1D.of(Double.NaN).equals(Point1D.of(Double.NaN)));
+        Assert.assertTrue(Point1D.of(Double.POSITIVE_INFINITY).equals(Point1D.of(Double.POSITIVE_INFINITY)));
+        Assert.assertTrue(Point1D.of(Double.NEGATIVE_INFINITY).equals(Point1D.of(Double.NEGATIVE_INFINITY)));
+    }
+
+    @Test
+    public void testToString() {
+        // arrange
+        Point1D p = Point1D.of(3);
+        Pattern pattern = Pattern.compile("\\(3.{0,2}\\)");
+
+        // act
+        String str = p.toString();
+
+        // assert
+        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                    pattern.matcher(str).matches());
+    }
+
+    @Test
+    public void testOf() {
+        // act/assert
+        checkPoint(Point1D.of(0), 0.0);
+        checkPoint(Point1D.of(-1), -1.0);
+        checkPoint(Point1D.of(1), 1.0);
+        checkPoint(Point1D.of(Math.PI), Math.PI);
+        checkPoint(Point1D.of(Double.NaN), Double.NaN);
+        checkPoint(Point1D.of(Double.NEGATIVE_INFINITY), Double.NEGATIVE_INFINITY);
+        checkPoint(Point1D.of(Double.POSITIVE_INFINITY), Double.POSITIVE_INFINITY);
+    }
+
+    @Test
+    public void testOf_coordinateArg() {
+        // act/assert
+        checkPoint(Point1D.of(new Vector1D(0)), 0.0);
+        checkPoint(Point1D.of(new Vector1D(-1)), -1.0);
+        checkPoint(Point1D.of(new Vector1D(1)), 1.0);
+        checkPoint(Point1D.of(new Vector1D(Math.PI)), Math.PI);
+        checkPoint(Point1D.of(new Vector1D(Double.NaN)), Double.NaN);
+        checkPoint(Point1D.of(new Vector1D(Double.NEGATIVE_INFINITY)), Double.NEGATIVE_INFINITY);
+        checkPoint(Point1D.of(new Vector1D(Double.POSITIVE_INFINITY)), Double.POSITIVE_INFINITY);
+    }
+
+    @Test
+    public void testVectorCombination() {
+        // act/assert
+        checkPoint(Point1D.vectorCombination(2, Point1D.of(3)), 6);
+        checkPoint(Point1D.vectorCombination(-2, Point1D.of(3)), -6);
+    }
+
+    @Test
+    public void testVectorCombination2() {
+        // act/assert
+        checkPoint(Point1D.vectorCombination(
+                2, Point1D.of(3),
+                5, Point1D.of(7)), 41);
+        checkPoint(Point1D.vectorCombination(
+                2, Point1D.of(3),
+                -5, Point1D.of(7)),-29);
+    }
+
+    @Test
+    public void testVectorCombination3() {
+        // act/assert
+        checkPoint(Point1D.vectorCombination(
+                2, Point1D.of(3),
+                5, Point1D.of(7),
+                11, Point1D.of(13)), 184);
+        checkPoint(Point1D.vectorCombination(
+                2, Point1D.of(3),
+                5, Point1D.of(7),
+                -11, Point1D.of(13)), -102);
+    }
+
+    @Test
+    public void testVectorCombination4() {
+        // act/assert
+        checkPoint(Point1D.vectorCombination(
+                2, Point1D.of(3),
+                5, Point1D.of(7),
+                11, Point1D.of(13),
+                17, Point1D.of(19)), 507);
+        checkPoint(Point1D.vectorCombination(
+                2, Point1D.of(3),
+                5, Point1D.of(7),
+                11, Point1D.of(13),
+                -17, Point1D.of(19)), -139);
+    }
+
+    private void checkPoint(Point1D p, double x) {
+        Assert.assertEquals(x, p.getX(), TEST_TOLERANCE);
+    }
+
+    private void checkVector(Vector1D v, double x) {
+        Assert.assertEquals(x, v.getX(), TEST_TOLERANCE);
+    }
+}
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/SubOrientedPointTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/SubOrientedPointTest.java
index a0210ec..6cf8de7 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/SubOrientedPointTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/SubOrientedPointTest.java
@@ -28,7 +28,7 @@
     @Test
     public void testGetSize() {
         // arrange
-        OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
+        OrientedPoint hyperplane = new OrientedPoint(new Point1D(1), true, TEST_TOLERANCE);
         SubOrientedPoint pt = hyperplane.wholeHyperplane();
 
         // act/assert
@@ -38,7 +38,7 @@ public void testGetSize() {
     @Test
     public void testIsEmpty() {
         // arrange
-        OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
+        OrientedPoint hyperplane = new OrientedPoint(new Point1D(1), true, TEST_TOLERANCE);
         SubOrientedPoint pt = hyperplane.wholeHyperplane();
 
         // act/assert
@@ -48,14 +48,14 @@ public void testIsEmpty() {
     @Test
     public void testBuildNew() {
         // arrange
-        OrientedPoint originalHyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
+        OrientedPoint originalHyperplane = new OrientedPoint(new Point1D(1), true, TEST_TOLERANCE);
         SubOrientedPoint pt = originalHyperplane.wholeHyperplane();
 
-        OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(2), true, TEST_TOLERANCE);
+        OrientedPoint hyperplane = new OrientedPoint(new Point1D(2), true, TEST_TOLERANCE);
         IntervalsSet intervals = new IntervalsSet(2, 3, TEST_TOLERANCE);
 
         // act
-        SubHyperplane<Euclidean1D> result = pt.buildNew(hyperplane, intervals);
+        SubHyperplane<Point1D> result = pt.buildNew(hyperplane, intervals);
 
         // assert
         Assert.assertTrue(result instanceof SubOrientedPoint);
@@ -66,14 +66,14 @@ public void testBuildNew() {
     @Test
     public void testSplit_resultOnMinusSide() {
         // arrange
-        OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
+        OrientedPoint hyperplane = new OrientedPoint(new Point1D(1), true, TEST_TOLERANCE);
         IntervalsSet interval = new IntervalsSet(TEST_TOLERANCE);
         SubOrientedPoint pt = new SubOrientedPoint(hyperplane, interval);
 
-        OrientedPoint splitter = new OrientedPoint(new Cartesian1D(2), true, TEST_TOLERANCE);
+        OrientedPoint splitter = new OrientedPoint(new Point1D(2), true, TEST_TOLERANCE);
 
         // act
-        SplitSubHyperplane<Euclidean1D> split = pt.split(splitter);
+        SplitSubHyperplane<Point1D> split = pt.split(splitter);
 
         // assert
         Assert.assertEquals(Side.MINUS, split.getSide());
@@ -92,14 +92,14 @@ public void testSplit_resultOnMinusSide() {
     @Test
     public void testSplit_resultOnPlusSide() {
         // arrange
-        OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
+        OrientedPoint hyperplane = new OrientedPoint(new Point1D(1), true, TEST_TOLERANCE);
         IntervalsSet interval = new IntervalsSet(TEST_TOLERANCE);
         SubOrientedPoint pt = new SubOrientedPoint(hyperplane, interval);
 
-        OrientedPoint splitter = new OrientedPoint(new Cartesian1D(0), true, TEST_TOLERANCE);
+        OrientedPoint splitter = new OrientedPoint(new Point1D(0), true, TEST_TOLERANCE);
 
         // act
-        SplitSubHyperplane<Euclidean1D> split = pt.split(splitter);
+        SplitSubHyperplane<Point1D> split = pt.split(splitter);
 
         // assert
         Assert.assertEquals(Side.PLUS, split.getSide());
@@ -118,14 +118,14 @@ public void testSplit_resultOnPlusSide() {
     @Test
     public void testSplit_equivalentHyperplanes() {
         // arrange
-        OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
+        OrientedPoint hyperplane = new OrientedPoint(new Point1D(1), true, TEST_TOLERANCE);
         IntervalsSet interval = new IntervalsSet(TEST_TOLERANCE);
         SubOrientedPoint pt = new SubOrientedPoint(hyperplane, interval);
 
-        OrientedPoint splitter = new OrientedPoint(new Cartesian1D(1), true, TEST_TOLERANCE);
+        OrientedPoint splitter = new OrientedPoint(new Point1D(1), true, TEST_TOLERANCE);
 
         // act
-        SplitSubHyperplane<Euclidean1D> split = pt.split(splitter);
+        SplitSubHyperplane<Point1D> split = pt.split(splitter);
 
         // assert
         Assert.assertEquals(Side.HYPER, split.getSide());
@@ -137,23 +137,23 @@ public void testSplit_equivalentHyperplanes() {
     @Test
     public void testSplit_usesToleranceFromParentHyperplane() {
         // arrange
-        OrientedPoint hyperplane = new OrientedPoint(new Cartesian1D(1), true, 0.1);
+        OrientedPoint hyperplane = new OrientedPoint(new Point1D(1), true, 0.1);
         SubOrientedPoint pt = hyperplane.wholeHyperplane();
 
         // act/assert
-        SplitSubHyperplane<Euclidean1D> plusSplit = pt.split(new OrientedPoint(new Cartesian1D(0.899), true, 1e-10));
+        SplitSubHyperplane<Point1D> plusSplit = pt.split(new OrientedPoint(new Point1D(0.899), true, 1e-10));
         Assert.assertNull(plusSplit.getMinus());
         Assert.assertNotNull(plusSplit.getPlus());
 
-        SplitSubHyperplane<Euclidean1D> lowWithinTolerance = pt.split(new OrientedPoint(new Cartesian1D(0.901), true, 1e-10));
+        SplitSubHyperplane<Point1D> lowWithinTolerance = pt.split(new OrientedPoint(new Point1D(0.901), true, 1e-10));
         Assert.assertNull(lowWithinTolerance.getMinus());
         Assert.assertNull(lowWithinTolerance.getPlus());
 
-        SplitSubHyperplane<Euclidean1D> highWithinTolerance = pt.split(new OrientedPoint(new Cartesian1D(1.09), true, 1e-10));
+        SplitSubHyperplane<Point1D> highWithinTolerance = pt.split(new OrientedPoint(new Point1D(1.09), true, 1e-10));
         Assert.assertNull(highWithinTolerance.getMinus());
         Assert.assertNull(highWithinTolerance.getPlus());
 
-        SplitSubHyperplane<Euclidean1D> minusSplit = pt.split(new OrientedPoint(new Cartesian1D(1.101), true, 1e-10));
+        SplitSubHyperplane<Point1D> minusSplit = pt.split(new OrientedPoint(new Point1D(1.101), true, 1e-10));
         Assert.assertNotNull(minusSplit.getMinus());
         Assert.assertNull(minusSplit.getPlus());
     }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
new file mode 100644
index 0000000..fa757ab
--- /dev/null
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
@@ -0,0 +1,370 @@
+package org.apache.commons.geometry.euclidean.oned;
+
+import java.util.regex.Pattern;
+
+import org.apache.commons.numbers.core.Precision;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Vector1DTest {
+
+    private static final double TEST_TOLERANCE = 1e-15;
+
+    @Test
+    public void testConstants() {
+        // act/assert
+        checkVector(Vector1D.ZERO, 0.0);
+        checkVector(Vector1D.ONE, 1.0);
+        checkVector(Vector1D.NaN, Double.NaN);
+        checkVector(Vector1D.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+        checkVector(Vector1D.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+    }
+
+    @Test
+    public void testAsPoint() {
+        // act/assert
+        checkPoint(Vector1D.of(0.0).asPoint(), 0.0);
+        checkPoint(Vector1D.of(1.0).asPoint(), 1.0);
+        checkPoint(Vector1D.of(-1.0).asPoint(), -1.0);
+        checkPoint(Vector1D.of(Double.NaN).asPoint(), Double.NaN);
+        checkPoint(Vector1D.of(Double.NEGATIVE_INFINITY).asPoint(), Double.NEGATIVE_INFINITY);
+        checkPoint(Vector1D.of(Double.POSITIVE_INFINITY).asPoint(), Double.POSITIVE_INFINITY);
+    }
+
+    @Test
+    public void testZero() {
+        // act
+        Vector1D zero = Vector1D.of(1).getZero();
+
+        // assert
+        checkVector(zero, 0.0);
+        checkPoint(Point1D.ONE.add(zero), 1.0);
+    }
+
+    @Test
+    public void testNorm1() {
+        // act/assert
+        Assert.assertEquals(0.0, Vector1D.ZERO.getNorm1(), TEST_TOLERANCE);
+        Assert.assertEquals(6.0, Vector1D.of(6).getNorm1(), TEST_TOLERANCE);
+        Assert.assertEquals(6.0, Vector1D.of(-6).getNorm1(), TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testNorm() {
+        // act/assert
+        Assert.assertEquals(0.0, Vector1D.ZERO.getNorm(), TEST_TOLERANCE);
+        Assert.assertEquals(3.0, Vector1D.of(3).getNorm(), TEST_TOLERANCE);
+        Assert.assertEquals(3.0, Vector1D.of(-3).getNorm(), TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testNormSq() {
+        // act/assert
+        Assert.assertEquals(0.0, Vector1D.of(0).getNormSq(), TEST_TOLERANCE);
+        Assert.assertEquals(9.0, Vector1D.of(3).getNormSq(), TEST_TOLERANCE);
+        Assert.assertEquals(9.0, Vector1D.of(-3).getNormSq(), TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testNormInf() {
+        // act/assert
+        Assert.assertEquals(0.0, Vector1D.ZERO.getNormInf(), TEST_TOLERANCE);
+        Assert.assertEquals(3.0, Vector1D.of(3).getNormInf(), TEST_TOLERANCE);
+        Assert.assertEquals(3.0, Vector1D.of(-3).getNormInf(), TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testAdd() {
+        // arrange
+        Vector1D v1 = Vector1D.of(1);
+        Vector1D v2 = Vector1D.of(-3);
+        Vector1D v3 = Vector1D.of(3);
+
+        // act/assert
+        checkVector(v1.add(v1), 2);
+        checkVector(v1.add(v2), -2);
+        checkVector(v2.add(v1), -2);
+        checkVector(v2.add(v3), 0);
+    }
+
+    @Test
+    public void testAdd_scaled() {
+        // arrange
+        Vector1D v1 = Vector1D.of(1);
+        Vector1D v2 = Vector1D.of(-3);
+        Vector1D v3 = Vector1D.of(3);
+
+        // act/assert
+        checkVector(v1.add(1, v1), 2);
+        checkVector(v1.add(0.5, v1), 1.5);
+        checkVector(v1.add(-1, v1), 0);
+
+        checkVector(v1.add(0, v2), 1);
+        checkVector(v2.add(3, v1), 0);
+        checkVector(v2.add(2, v3), 3);
+    }
+
+    @Test
+    public void testSubtract() {
+        // arrange
+        Vector1D v1 = Vector1D.of(1);
+        Vector1D v2 = Vector1D.of(-3);
+        Vector1D v3 = Vector1D.of(3);
+
+        // act/assert
+        checkVector(v1.subtract(v1), 0);
+        checkVector(v1.subtract(v2), 4);
+        checkVector(v2.subtract(v1), -4);
+        checkVector(v2.subtract(v3), -6);
+    }
+
+    @Test
+    public void testSubtract_scaled() {
+        // arrange
+        Vector1D v1 = Vector1D.of(1);
+        Vector1D v2 = Vector1D.of(-3);
+        Vector1D v3 = Vector1D.of(3);
+
+        // act/assert
+        checkVector(v1.subtract(1, v1), 0);
+        checkVector(v1.subtract(0.5, v1), 0.5);
+        checkVector(v1.subtract(-1, v1), 2);
+
+        checkVector(v1.subtract(0, v2), 1);
+        checkVector(v2.subtract(3, v1), -6);
+        checkVector(v2.subtract(2, v3), -9);
+    }
+
+    @Test
+    public void testNormalize() {
+        // act/assert
+        checkVector(Vector1D.of(1).normalize(), 1);
+        checkVector(Vector1D.of(-1).normalize(), -1);
+        checkVector(Vector1D.of(5).normalize(), 1);
+        checkVector(Vector1D.of(-5).normalize(), -1);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testNormalize_zeroNorm() {
+        // act
+        Vector1D.ZERO.normalize();
+    }
+
+    @Test
+    public void testNegate() {
+        // act/assert
+        checkVector(Vector1D.of(0.1).negate(), -0.1);
+        checkVector(Vector1D.of(-0.1).negate(), 0.1);
+    }
+
+    @Test
+    public void testScalarMultiply() {
+        // act/assert
+        checkVector(Vector1D.of(1).scalarMultiply(3), 3);
+        checkVector(Vector1D.of(1).scalarMultiply(-3), -3);
+
+        checkVector(Vector1D.of(1.5).scalarMultiply(7), 10.5);
+        checkVector(Vector1D.of(-1.5).scalarMultiply(7), -10.5);
+    }
+
+    @Test
+    public void testDistance1() {
+        // arrange
+        Vector1D v1 = Vector1D.of(1);
+        Vector1D v2 = Vector1D.of(-4);
+
+        // act/assert
+        Assert.assertEquals(0.0, v1.distance1(v1), TEST_TOLERANCE);
+
+        Assert.assertEquals(5.0, v1.distance1(v2), TEST_TOLERANCE);
+        Assert.assertEquals(5.0, v2.distance1(v1), TEST_TOLERANCE);
+        Assert.assertEquals(v1.subtract(v2).getNorm1(), v1.distance1(v2), TEST_TOLERANCE);
+
+        Assert.assertEquals(0.0, Vector1D.of(-1).distance1(Vector1D.of(-1)), TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testDistance() {
+        // arrange
+        Vector1D v1 = Vector1D.of(1);
+        Vector1D v2 = Vector1D.of(-4);
+
+        // act/assert
+        Assert.assertEquals(0.0, v1.distance(v1), TEST_TOLERANCE);
+
+        Assert.assertEquals(5.0, v1.distance(v2), TEST_TOLERANCE);
+        Assert.assertEquals(5.0, v2.distance(v1), TEST_TOLERANCE);
+        Assert.assertEquals(v1.subtract(v2).getNorm(), v1.distance(v2), TEST_TOLERANCE);
+
+        Assert.assertEquals(0.0, Vector1D.of(-1).distance(Vector1D.of(-1)), TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testDistanceInf() {
+        // arrange
+        Vector1D v1 = Vector1D.of(1);
+        Vector1D v2 = Vector1D.of(-4);
+
+        // act/assert
+        Assert.assertEquals(0.0, Vector1D.of(-1).distanceInf(Vector1D.of(-1)), TEST_TOLERANCE);
+        Assert.assertEquals(5.0, v1.distanceInf(v2), TEST_TOLERANCE);
+        Assert.assertEquals(5.0, v2.distanceInf(v1), TEST_TOLERANCE);
+
+        Assert.assertEquals(v1.subtract(v2).getNormInf(), v1.distanceInf(v2), TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testDistanceSq() {
+        // arrange
+        Vector1D v1 = Vector1D.of(1);
+        Vector1D v2 = Vector1D.of(-4);
+
+        // act/assert
+        Assert.assertEquals(0.0, Vector1D.of(-1).distanceSq(Vector1D.of(-1)), TEST_TOLERANCE);
+        Assert.assertEquals(25.0, v1.distanceSq(v2), TEST_TOLERANCE);
+        Assert.assertEquals(25.0, v2.distanceSq(v1), TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testDotProduct() {
+        // arrange
+        Vector1D v1 = Vector1D.of(2);
+        Vector1D v2 = Vector1D.of(-3);
+        Vector1D v3 = Vector1D.of(3);
+
+        // act/assert
+        Assert.assertEquals(-6.0, v1.dotProduct(v2), TEST_TOLERANCE);
+        Assert.assertEquals(-6.0, v2.dotProduct(v1), TEST_TOLERANCE);
+
+        Assert.assertEquals(6.0, v1.dotProduct(v3), TEST_TOLERANCE);
+        Assert.assertEquals(6.0, v3.dotProduct(v1), TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testHashCode() {
+        // arrange
+        Vector1D u = Vector1D.of(1);
+        Vector1D v = Vector1D.of(1 + 10 * Precision.EPSILON);
+        Vector1D w = Vector1D.of(1);
+
+        // act/assert
+        Assert.assertTrue(u.hashCode() != v.hashCode());
+        Assert.assertEquals(u.hashCode(), w.hashCode());
+
+        Assert.assertEquals(Vector1D.of(Double.NaN).hashCode(), Vector1D.NaN.hashCode());
+        Assert.assertEquals(Vector1D.of(Double.NaN).hashCode(), Vector1D.of(Double.NaN).hashCode());
+    }
+
+    @Test
+    public void testEquals() {
+        // arrange
+        Vector1D u1 = Vector1D.of(1);
+        Vector1D u2 = Vector1D.of(1);
+
+        // act/assert
+        Assert.assertFalse(u1.equals(null));
+        Assert.assertFalse(u1.equals(new Object()));
+
+        Assert.assertTrue(u1.equals(u1));
+        Assert.assertTrue(u1.equals(u2));
+
+        Assert.assertFalse(u1.equals(Vector1D.of(-1)));
+        Assert.assertFalse(u1.equals(Vector1D.of(1 + 10 * Precision.EPSILON)));
+
+        Assert.assertTrue(Vector1D.of(Double.NaN).equals(Vector1D.of(Double.NaN)));
+        Assert.assertTrue(Vector1D.of(Double.POSITIVE_INFINITY).equals(Vector1D.of(Double.POSITIVE_INFINITY)));
+        Assert.assertTrue(Vector1D.of(Double.NEGATIVE_INFINITY).equals(Vector1D.of(Double.NEGATIVE_INFINITY)));
+    }
+
+    @Test
+    public void testToString() {
+        // arrange
+        Vector1D v = Vector1D.of(3);
+        Pattern pattern = Pattern.compile("\\{3.{0,2}\\}");
+
+        // act
+        String str = v.toString();
+
+        // assert
+        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                    pattern.matcher(str).matches());
+    }
+
+    @Test
+    public void testOf() {
+        // act/assert
+        checkVector(Vector1D.of(0), 0.0);
+        checkVector(Vector1D.of(-1), -1.0);
+        checkVector(Vector1D.of(1), 1.0);
+        checkVector(Vector1D.of(Math.PI), Math.PI);
+        checkVector(Vector1D.of(Double.NaN), Double.NaN);
+        checkVector(Vector1D.of(Double.NEGATIVE_INFINITY), Double.NEGATIVE_INFINITY);
+        checkVector(Vector1D.of(Double.POSITIVE_INFINITY), Double.POSITIVE_INFINITY);
+    }
+
+    @Test
+    public void testOf_coordinateArg() {
+        // act/assert
+        checkVector(Vector1D.of(Vector1D.of(0)), 0.0);
+        checkVector(Vector1D.of(Vector1D.of(-1)), -1.0);
+        checkVector(Vector1D.of(Vector1D.of(1)), 1.0);
+        checkVector(Vector1D.of(Vector1D.of(Math.PI)), Math.PI);
+        checkVector(Vector1D.of(Vector1D.of(Double.NaN)), Double.NaN);
+        checkVector(Vector1D.of(Vector1D.of(Double.NEGATIVE_INFINITY)), Double.NEGATIVE_INFINITY);
+        checkVector(Vector1D.of(Vector1D.of(Double.POSITIVE_INFINITY)), Double.POSITIVE_INFINITY);
+    }
+
+    @Test
+    public void testLinearCombination() {
+        // act/assert
+        checkVector(Vector1D.linearCombination(2, Vector1D.of(3)), 6);
+        checkVector(Vector1D.linearCombination(-2, Vector1D.of(3)), -6);
+    }
+
+    @Test
+    public void testLinearCombination2() {
+        // act/assert
+        checkVector(Vector1D.linearCombination(
+                2, Vector1D.of(3),
+                5, Vector1D.of(7)), 41);
+        checkVector(Vector1D.linearCombination(
+                2, Vector1D.of(3),
+                -5, Vector1D.of(7)),-29);
+    }
+
+    @Test
+    public void testLinearCombination3() {
+        // act/assert
+        checkVector(Vector1D.linearCombination(
+                2, Vector1D.of(3),
+                5, Vector1D.of(7),
+                11, Vector1D.of(13)), 184);
+        checkVector(Vector1D.linearCombination(
+                2, Vector1D.of(3),
+                5, Vector1D.of(7),
+                -11, Vector1D.of(13)), -102);
+    }
+
+    @Test
+    public void testLinearCombination4() {
+        // act/assert
+        checkVector(Vector1D.linearCombination(
+                2, Vector1D.of(3),
+                5, Vector1D.of(7),
+                11, Vector1D.of(13),
+                17, Vector1D.of(19)), 507);
+        checkVector(Vector1D.linearCombination(
+                2, Vector1D.of(3),
+                5, Vector1D.of(7),
+                11, Vector1D.of(13),
+                -17, Vector1D.of(19)), -139);
+    }
+
+    private void checkPoint(Point1D p, double x) {
+        Assert.assertEquals(x, p.getX(), TEST_TOLERANCE);
+    }
+
+    private void checkVector(Vector1D v, double x) {
+        Assert.assertEquals(x, v.getX(), TEST_TOLERANCE);
+    }
+}
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
new file mode 100644
index 0000000..4e6205d
--- /dev/null
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Cartesian3DTest.java
@@ -0,0 +1,83 @@
+package org.apache.commons.geometry.euclidean.threed;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Cartesian3DTest {
+
+    private static final double TEST_TOLERANCE = 1e-15;
+
+    @Test
+    public void testCoordinates() {
+        // arrange
+        Cartesian3D c = new StubCartesian3D(1, 2, 3);
+
+        // act/assert
+        Assert.assertEquals(1.0, c.getX(), TEST_TOLERANCE);
+        Assert.assertEquals(2.0, c.getY(), TEST_TOLERANCE);
+        Assert.assertEquals(3.0, c.getZ(), TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testToArray() {
+        // arrange
+        Cartesian3D c = new StubCartesian3D(1, 2, 3);
+
+        // act
+        double[] arr = c.toArray();
+
+        // assert
+        Assert.assertEquals(3, arr.length);
+        Assert.assertEquals(1.0, arr[0], TEST_TOLERANCE);
+        Assert.assertEquals(2.0, arr[1], TEST_TOLERANCE);
+        Assert.assertEquals(3.0, arr[2], TEST_TOLERANCE);
+    }
+
+    @Test
+    public void testDimension() {
+        // arrange
+        Cartesian3D c = new StubCartesian3D(1, 2, 3);
+
+        // act/assert
+        Assert.assertEquals(3, c.getDimension());
+    }
+
+    @Test
+    public void testNaN() {
+        // act/assert
+        Assert.assertTrue(new StubCartesian3D(0, 0, Double.NaN).isNaN());
+        Assert.assertTrue(new StubCartesian3D(0, Double.NaN, 0).isNaN());
+        Assert.assertTrue(new StubCartesian3D(Double.NaN, 0, 0).isNaN());
+
+        Assert.assertFalse(new StubCartesian3D(1, 1, 1).isNaN());
+        Assert.assertFalse(new StubCartesian3D(1, 1, Double.NEGATIVE_INFINITY).isNaN());
+        Assert.assertFalse(new StubCartesian3D(1, Double.POSITIVE_INFINITY, 1).isNaN());
+        Assert.assertFalse(new StubCartesian3D(Double.NEGATIVE_INFINITY, 1, 1).isNaN());
+    }
+
+    @Test
+    public void testInfinite() {
+        // act/assert
+        Assert.assertTrue(new StubCartesian3D(0, 0, Double.NEGATIVE_INFINITY).isInfinite());
+        Assert.assertTrue(new StubCartesian3D(0, Double.NEGATIVE_INFINITY, 0).isInfinite());
+        Assert.assertTrue(new StubCartesian3D(Double.NEGATIVE_INFINITY, 0, 0).isInfinite());
+        Assert.assertTrue(new StubCartesian3D(0, 0, Double.POSITIVE_INFINITY).isInfinite());
+        Assert.assertTrue(new StubCartesian3D(0, Double.POSITIVE_INFINITY, 0).isInfinite());
+        Assert.assertTrue(new StubCartesian3D(Double.POSITIVE_INFINITY, 0, 0).isInfinite());
+
+        Assert.assertFalse(new StubCartesian3D(1, 1, 1).isInfinite());
+        Assert.assertFalse(new StubCartesian3D(0, 0, Double.NaN).isInfinite());
+        Assert.assertFalse(new StubCartesian3D(0, Double.NEGATIVE_INFINITY, Double.NaN).isInfinite());
+        Assert.assertFalse(new StubCartesian3D(Double.NaN, 0, Double.NEGATIVE_INFINITY).isInfinite());
+        Assert.assertFalse(new StubCartesian3D(Double.POSITIVE_INFINITY, Double.NaN, 0).isInfinite());
+        Assert.assertFalse(new StubCartesian3D(0, Double.NaN, Double.POSITIVE_INFINITY).isInfinite());
+    }
+
+    private static class StubCartesian3D extends Cartesian3D {
+        private static final long serialVersionUID = 1L;
+
+        public StubCartesian3D(double x, double y, double z) {
+            super(x, y, z);
+        }
+    }
+}
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/LineTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/LineTest.java
index 749db4f..6a6a87b 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/LineTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/LineTest.java
@@ -16,8 +16,6 @@
  */
 package org.apache.commons.geometry.euclidean.threed;
 
-import org.apache.commons.geometry.euclidean.threed.Line;
-import org.apache.commons.geometry.euclidean.threed.Cartesian3D;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -25,22 +23,22 @@
 
     @Test
     public void testContains() {
-        Cartesian3D p1 = new Cartesian3D(0, 0, 1);
-        Line l = new Line(p1, new Cartesian3D(0, 0, 2), 1.0e-10);
+        Point3D p1 = new Point3D(0, 0, 1);
+        Line l = new Line(p1, new Point3D(0, 0, 2), 1.0e-10);
         Assert.assertTrue(l.contains(p1));
-        Assert.assertTrue(l.contains(new Cartesian3D(1.0, p1, 0.3, l.getDirection())));
-        Cartesian3D u = l.getDirection().orthogonal();
-        Cartesian3D v = Cartesian3D.crossProduct(l.getDirection(), u);
+        Assert.assertTrue(l.contains(Point3D.vectorCombination(1.0, p1, 0.3, l.getDirection())));
+        Vector3D u = l.getDirection().orthogonal();
+        Vector3D v = l.getDirection().crossProduct(u);
         for (double alpha = 0; alpha < 2 * Math.PI; alpha += 0.3) {
-            Assert.assertTrue(! l.contains(p1.add(new Cartesian3D(Math.cos(alpha), u,
+            Assert.assertTrue(! l.contains(p1.add(Vector3D.linearCombination(Math.cos(alpha), u,
                                                                Math.sin(alpha), v))));
         }
     }
 
     @Test
     public void testSimilar() {
-        Cartesian3D p1  = new Cartesian3D (1.2, 3.4, -5.8);
-        Cartesian3D p2  = new Cartesian3D (3.4, -5.8, 1.2);
+        Point3D p1  = new Point3D (1.2, 3.4, -5.8);
+        Point3D p2  = new Point3D (3.4, -5.8, 1.2);
         Line     lA  = new Line(p1, p2, 1.0e-10);
         Line     lB  = new Line(p2, p1, 1.0e-10);
         Assert.assertTrue(lA.isSimilarTo(lB));
@@ -49,91 +47,91 @@ public void testSimilar() {
 
     @Test
     public void testPointDistance() {
-        Line l = new Line(new Cartesian3D(0, 1, 1), new Cartesian3D(0, 2, 2), 1.0e-10);
-        Assert.assertEquals(Math.sqrt(3.0 / 2.0), l.distance(new Cartesian3D(1, 0, 1)), 1.0e-10);
-        Assert.assertEquals(0, l.distance(new Cartesian3D(0, -4, -4)), 1.0e-10);
+        Line l = new Line(new Point3D(0, 1, 1), new Point3D(0, 2, 2), 1.0e-10);
+        Assert.assertEquals(Math.sqrt(3.0 / 2.0), l.distance(new Point3D(1, 0, 1)), 1.0e-10);
+        Assert.assertEquals(0, l.distance(new Point3D(0, -4, -4)), 1.0e-10);
     }
 
     @Test
     public void testLineDistance() {
-        Line l = new Line(new Cartesian3D(0, 1, 1), new Cartesian3D(0, 2, 2), 1.0e-10);
+        Line l = new Line(new Point3D(0, 1, 1), new Point3D(0, 2, 2), 1.0e-10);
         Assert.assertEquals(1.0,
-                            l.distance(new Line(new Cartesian3D(1, 0, 1), new Cartesian3D(1, 0, 2), 1.0e-10)),
+                            l.distance(new Line(new Point3D(1, 0, 1), new Point3D(1, 0, 2), 1.0e-10)),
                             1.0e-10);
         Assert.assertEquals(0.5,
-                            l.distance(new Line(new Cartesian3D(-0.5, 0, 0), new Cartesian3D(-0.5, -1, -1), 1.0e-10)),
+                            l.distance(new Line(new Point3D(-0.5, 0, 0), new Point3D(-0.5, -1, -1), 1.0e-10)),
                             1.0e-10);
         Assert.assertEquals(0.0,
                             l.distance(l),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            l.distance(new Line(new Cartesian3D(0, -4, -4), new Cartesian3D(0, -5, -5), 1.0e-10)),
+                            l.distance(new Line(new Point3D(0, -4, -4), new Point3D(0, -5, -5), 1.0e-10)),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            l.distance(new Line(new Cartesian3D(0, -4, -4), new Cartesian3D(0, -3, -4), 1.0e-10)),
+                            l.distance(new Line(new Point3D(0, -4, -4), new Point3D(0, -3, -4), 1.0e-10)),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            l.distance(new Line(new Cartesian3D(0, -4, -4), new Cartesian3D(1, -4, -4), 1.0e-10)),
+                            l.distance(new Line(new Point3D(0, -4, -4), new Point3D(1, -4, -4), 1.0e-10)),
                             1.0e-10);
         Assert.assertEquals(Math.sqrt(8),
-                            l.distance(new Line(new Cartesian3D(0, -4, 0), new Cartesian3D(1, -4, 0), 1.0e-10)),
+                            l.distance(new Line(new Point3D(0, -4, 0), new Point3D(1, -4, 0), 1.0e-10)),
                             1.0e-10);
     }
 
     @Test
     public void testClosest() {
-        Line l = new Line(new Cartesian3D(0, 1, 1), new Cartesian3D(0, 2, 2), 1.0e-10);
+        Line l = new Line(new Point3D(0, 1, 1), new Point3D(0, 2, 2), 1.0e-10);
         Assert.assertEquals(0.0,
-                            l.closestPoint(new Line(new Cartesian3D(1, 0, 1), new Cartesian3D(1, 0, 2), 1.0e-10)).distance(new Cartesian3D(0, 0, 0)),
+                            l.closestPoint(new Line(new Point3D(1, 0, 1), new Point3D(1, 0, 2), 1.0e-10)).distance(new Point3D(0, 0, 0)),
                             1.0e-10);
         Assert.assertEquals(0.5,
-                            l.closestPoint(new Line(new Cartesian3D(-0.5, 0, 0), new Cartesian3D(-0.5, -1, -1), 1.0e-10)).distance(new Cartesian3D(-0.5, 0, 0)),
+                            l.closestPoint(new Line(new Point3D(-0.5, 0, 0), new Point3D(-0.5, -1, -1), 1.0e-10)).distance(new Point3D(-0.5, 0, 0)),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            l.closestPoint(l).distance(new Cartesian3D(0, 0, 0)),
+                            l.closestPoint(l).distance(new Point3D(0, 0, 0)),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            l.closestPoint(new Line(new Cartesian3D(0, -4, -4), new Cartesian3D(0, -5, -5), 1.0e-10)).distance(new Cartesian3D(0, 0, 0)),
+                            l.closestPoint(new Line(new Point3D(0, -4, -4), new Point3D(0, -5, -5), 1.0e-10)).distance(new Point3D(0, 0, 0)),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            l.closestPoint(new Line(new Cartesian3D(0, -4, -4), new Cartesian3D(0, -3, -4), 1.0e-10)).distance(new Cartesian3D(0, -4, -4)),
+                            l.closestPoint(new Line(new Point3D(0, -4, -4), new Point3D(0, -3, -4), 1.0e-10)).distance(new Point3D(0, -4, -4)),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            l.closestPoint(new Line(new Cartesian3D(0, -4, -4), new Cartesian3D(1, -4, -4), 1.0e-10)).distance(new Cartesian3D(0, -4, -4)),
+                            l.closestPoint(new Line(new Point3D(0, -4, -4), new Point3D(1, -4, -4), 1.0e-10)).distance(new Point3D(0, -4, -4)),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            l.closestPoint(new Line(new Cartesian3D(0, -4, 0), new Cartesian3D(1, -4, 0), 1.0e-10)).distance(new Cartesian3D(0, -2, -2)),
+                            l.closestPoint(new Line(new Point3D(0, -4, 0), new Point3D(1, -4, 0), 1.0e-10)).distance(new Point3D(0, -2, -2)),
                             1.0e-10);
     }
 
     @Test
     public void testIntersection() {
-        Line l = new Line(new Cartesian3D(0, 1, 1), new Cartesian3D(0, 2, 2), 1.0e-10);
-        Assert.assertNull(l.intersection(new Line(new Cartesian3D(1, 0, 1), new Cartesian3D(1, 0, 2), 1.0e-10)));
-        Assert.assertNull(l.intersection(new Line(new Cartesian3D(-0.5, 0, 0), new Cartesian3D(-0.5, -1, -1), 1.0e-10)));
+        Line l = new Line(new Point3D(0, 1, 1), new Point3D(0, 2, 2), 1.0e-10);
+        Assert.assertNull(l.intersection(new Line(new Point3D(1, 0, 1), new Point3D(1, 0, 2), 1.0e-10)));
+        Assert.assertNull(l.intersection(new Line(new Point3D(-0.5, 0, 0), new Point3D(-0.5, -1, -1), 1.0e-10)));
         Assert.assertEquals(0.0,
-                            l.intersection(l).distance(new Cartesian3D(0, 0, 0)),
+                            l.intersection(l).distance(new Point3D(0, 0, 0)),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            l.intersection(new Line(new Cartesian3D(0, -4, -4), new Cartesian3D(0, -5, -5), 1.0e-10)).distance(new Cartesian3D(0, 0, 0)),
+                            l.intersection(new Line(new Point3D(0, -4, -4), new Point3D(0, -5, -5), 1.0e-10)).distance(new Point3D(0, 0, 0)),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            l.intersection(new Line(new Cartesian3D(0, -4, -4), new Cartesian3D(0, -3, -4), 1.0e-10)).distance(new Cartesian3D(0, -4, -4)),
+                            l.intersection(new Line(new Point3D(0, -4, -4), new Point3D(0, -3, -4), 1.0e-10)).distance(new Point3D(0, -4, -4)),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            l.intersection(new Line(new Cartesian3D(0, -4, -4), new Cartesian3D(1, -4, -4), 1.0e-10)).distance(new Cartesian3D(0, -4, -4)),
+                            l.intersection(new Line(new Point3D(0, -4, -4), new Point3D(1, -4, -4), 1.0e-10)).distance(new Point3D(0, -4, -4)),
                             1.0e-10);
-        Assert.assertNull(l.intersection(new Line(new Cartesian3D(0, -4, 0), new Cartesian3D(1, -4, 0), 1.0e-10)));
+        Assert.assertNull(l.intersection(new Line(new Point3D(0, -4, 0), new Point3D(1, -4, 0), 1.0e-10)));
     }
 
     @Test
     public void testRevert() {
 
         // setup
-        Line line = new Line(new Cartesian3D(1653345.6696423641, 6170370.041579291, 90000),
-                             new Cartesian3D(1650757.5050732433, 6160710.879908984, 0.9),
+        Line line = new Line(new Point3D(1653345.6696423641, 6170370.041579291, 90000),
+                             new Point3D(1650757.5050732433, 6160710.879908984, 0.9),
                              1.0e-10);
-        Cartesian3D expected = line.getDirection().negate();
+        Vector3D expected = line.getDirection().negate();
 
         // action
         Line reverted = line.revert();
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/OBJWriter.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/OBJWriter.java
index 0e71f0c..cb3f861 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/OBJWriter.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/OBJWriter.java
@@ -31,8 +31,7 @@
 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.euclidean.twod.Cartesian2D;
-import org.apache.commons.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.geometry.euclidean.twod.Point2D;
 import org.apache.commons.geometry.euclidean.twod.PolygonsSet;
 
 /** This class creates simple OBJ files from {@link PolyhedronsSet} instances.
@@ -77,10 +76,10 @@ public static void write(File file, PolyhedronsSet poly) throws IOException {
      * @param vertices
      * @throws IOException
      */
-    private static void writeVertices(Writer writer, List<Cartesian3D> vertices) throws IOException {
+    private static void writeVertices(Writer writer, List<Point3D> vertices) throws IOException {
         DecimalFormat df = new DecimalFormat("0.######");
 
-        for (Cartesian3D v : vertices) {
+        for (Point3D v : vertices) {
             writer.write("v ");
             writer.write(df.format(v.getX()));
             writer.write(" ");
@@ -113,7 +112,7 @@ private static void writeFaces(Writer writer, List<int[]> faces) throws IOExcept
      * other, then the vertices are considered equal. This helps to avoid
      * writing duplicate vertices in the OBJ output.
      */
-    private static class VertexComparator implements Comparator<Cartesian3D> {
+    private static class VertexComparator implements Comparator<Point3D> {
 
         /** Geometric tolerance value */
         private double tolerance;
@@ -127,7 +126,7 @@ public VertexComparator(double tolerance) {
 
         /** {@inheritDoc} */
         @Override
-        public int compare(Cartesian3D a, Cartesian3D b) {
+        public int compare(Point3D a, Point3D b) {
             int result = compareDoubles(a.getX(), b.getX());
             if (result == 0) {
                 result = compareDoubles(a.getY(), b.getY());
@@ -160,16 +159,16 @@ else if (diff > tolerance) {
     /** Class for converting a 3D BSPTree into a list of vertices
      * and face vertex indices.
      */
-    private static class MeshBuilder implements BSPTreeVisitor<Euclidean3D> {
+    private static class MeshBuilder implements BSPTreeVisitor<Point3D> {
 
         /** Geometric tolerance */
         private final double tolerance;
 
         /** Map of vertices to their index in the vertices list */
-        private Map<Cartesian3D, Integer> vertexIndexMap;
+        private Map<Point3D, Integer> vertexIndexMap;
 
         /** List of unique vertices in the BSPTree boundary */
-        private List<Cartesian3D> vertices;
+        private List<Point3D> vertices;
 
         /**
          * List of face vertex indices. Each face will have 3 indices. Indices
@@ -190,7 +189,7 @@ public MeshBuilder(double tolerance) {
         /** Returns the list of unique vertices found in the BSPTree.
          * @return
          */
-        public List<Cartesian3D> getVertices() {
+        public List<Point3D> getVertices() {
             return vertices;
         }
 
@@ -204,15 +203,15 @@ public MeshBuilder(double tolerance) {
 
         /** {@inheritDoc} */
         @Override
-        public Order visitOrder(BSPTree<Euclidean3D> node) {
+        public Order visitOrder(BSPTree<Point3D> node) {
             return Order.SUB_MINUS_PLUS;
         }
 
         /** {@inheritDoc} */
         @SuppressWarnings("unchecked")
         @Override
-        public void visitInternalNode(BSPTree<Euclidean3D> node) {
-            BoundaryAttribute<Euclidean3D> attr = (BoundaryAttribute<Euclidean3D>) node.getAttribute();
+        public void visitInternalNode(BSPTree<Point3D> node) {
+            BoundaryAttribute<Point3D> attr = (BoundaryAttribute<Point3D>) node.getAttribute();
 
             if (attr.getPlusOutside() != null) {
                 addBoundary((SubPlane) attr.getPlusOutside());
@@ -224,7 +223,7 @@ else if (attr.getPlusInside() != null) {
 
         /** {@inheritDoc} */
         @Override
-        public void visitLeafNode(BSPTree<Euclidean3D> node) {
+        public void visitLeafNode(BSPTree<Point3D> node) {
             // do nothing
         }
 
@@ -239,8 +238,8 @@ private void addBoundary(SubPlane subplane) {
             TriangleExtractor triExtractor = new TriangleExtractor(tolerance);
             poly.getTree(true).visit(triExtractor);
 
-            Cartesian3D v1, v2, v3;
-            for (Cartesian2D[] tri : triExtractor.getTriangles()) {
+            Point3D v1, v2, v3;
+            for (Point2D[] tri : triExtractor.getTriangles()) {
                 v1 = plane.toSpace(tri[0]);
                 v2 = plane.toSpace(tri[1]);
                 v3 = plane.toSpace(tri[2]);
@@ -259,7 +258,7 @@ private void addBoundary(SubPlane subplane) {
          * @param vertex
          * @return
          */
-        private int getVertexIndex(Cartesian3D vertex) {
+        private int getVertexIndex(Point3D vertex) {
             Integer idx = vertexIndexMap.get(vertex);
             if (idx == null) {
                 idx = vertices.size();
@@ -273,13 +272,13 @@ private int getVertexIndex(Cartesian3D vertex) {
 
     /** Visitor for extracting a collection of triangles from a 2D BSPTree.
      */
-    private static class TriangleExtractor implements BSPTreeVisitor<Euclidean2D> {
+    private static class TriangleExtractor implements BSPTreeVisitor<Point2D> {
 
         /** Geometric tolerance */
         private double tolerance;
 
         /** List of extracted triangles */
-        private List<Cartesian2D[]> triangles = new ArrayList<>();
+        private List<Point2D[]> triangles = new ArrayList<>();
 
         /** Creates a new instance with the given geometric tolerance.
          * @param tolerance
@@ -291,30 +290,30 @@ public TriangleExtractor(double tolerance) {
         /** Returns the list of extracted triangles.
          * @return
          */
-        public List<Cartesian2D[]> getTriangles() {
+        public List<Point2D[]> getTriangles() {
             return triangles;
         }
 
         /** {@inheritDoc} */
         @Override
-        public Order visitOrder(BSPTree<Euclidean2D> node) {
+        public Order visitOrder(BSPTree<Point2D> node) {
             return Order.SUB_MINUS_PLUS;
         }
 
         /** {@inheritDoc} */
         @Override
-        public void visitInternalNode(BSPTree<Euclidean2D> node) {
+        public void visitInternalNode(BSPTree<Point2D> node) {
             // do nothing
         }
 
         /** {@inheritDoc} */
         @Override
-        public void visitLeafNode(BSPTree<Euclidean2D> node) {
+        public void visitLeafNode(BSPTree<Point2D> node) {
             if ((Boolean) node.getAttribute()) {
                 PolygonsSet convexPoly = new PolygonsSet(node.pruneAroundConvexCell(Boolean.TRUE,
                         Boolean.FALSE, null), tolerance);
 
-                for (Cartesian2D[] loop : convexPoly.getVertices()) {
+                for (Point2D[] loop : convexPoly.getVertices()) {
                     if (loop.length > 0 && loop[0] != null) { // skip unclosed loops
                         addTriangles(loop);
                     }
@@ -326,10 +325,10 @@ public void visitLeafNode(BSPTree<Euclidean2D> node) {
          * triangles and adds them to the internal list.
          * @param vertices
          */
-        private void addTriangles(Cartesian2D[] vertices) {
+        private void addTriangles(Point2D[] vertices) {
             // use a triangle fan to add the convex region
             for (int i=2; i<vertices.length; ++i) {
-                triangles.add(new Cartesian2D[] { vertices[0], vertices[i-1], vertices[i] });
+                triangles.add(new Point2D[] { vertices[0], vertices[i-1], vertices[i] });
             }
         }
     }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PLYParser.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PLYParser.java
index f8c3edf..78ebefd 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PLYParser.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PLYParser.java
@@ -39,7 +39,7 @@
 public class PLYParser {
 
     /** Parsed vertices. */
-    private Cartesian3D[] vertices;
+    private Point3D[] vertices;
 
     /** Parsed faces. */
     private int[][] faces;
@@ -166,7 +166,7 @@ public PLYParser(final InputStream stream)
             ++vPropertiesNumber;
 
             // parse vertices
-            vertices = new Cartesian3D[nbVertices];
+            vertices = new Point3D[nbVertices];
             for (int i = 0; i < nbVertices; ++i) {
                 fields = parseNextLine();
                 if (fields.size() != vPropertiesNumber ||
@@ -175,7 +175,7 @@ public PLYParser(final InputStream stream)
                     fields.get(zIndex).getToken() != Token.UNKNOWN) {
                     complain();
                 }
-                vertices[i] = new Cartesian3D(Double.parseDouble(fields.get(xIndex).getValue()),
+                vertices[i] = new Point3D(Double.parseDouble(fields.get(xIndex).getValue()),
                                            Double.parseDouble(fields.get(yIndex).getValue()),
                                            Double.parseDouble(fields.get(zIndex).getValue()));
             }
@@ -228,7 +228,7 @@ private void complain() throws ParseException {
     /** Get the parsed vertices.
      * @return parsed vertices
      */
-    public List<Cartesian3D> getVertices() {
+    public List<Point3D> getVertices() {
         return Arrays.asList(vertices);
     }
 
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PlaneTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PlaneTest.java
index ea4f2f1..f6080a8 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PlaneTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PlaneTest.java
@@ -19,7 +19,7 @@
 import org.apache.commons.geometry.euclidean.threed.Line;
 import org.apache.commons.geometry.euclidean.threed.Plane;
 import org.apache.commons.geometry.euclidean.threed.Rotation;
-import org.apache.commons.geometry.euclidean.threed.Cartesian3D;
+import org.apache.commons.geometry.euclidean.threed.Point3D;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -27,37 +27,37 @@
 
     @Test
     public void testContains() {
-        Plane p = new Plane(new Cartesian3D(0, 0, 1), new Cartesian3D(0, 0, 1), 1.0e-10);
-        Assert.assertTrue(p.contains(new Cartesian3D(0, 0, 1)));
-        Assert.assertTrue(p.contains(new Cartesian3D(17, -32, 1)));
-        Assert.assertTrue(! p.contains(new Cartesian3D(17, -32, 1.001)));
+        Plane p = new Plane(new Point3D(0, 0, 1), new Vector3D(0, 0, 1), 1.0e-10);
+        Assert.assertTrue(p.contains(new Point3D(0, 0, 1)));
+        Assert.assertTrue(p.contains(new Point3D(17, -32, 1)));
+        Assert.assertTrue(! p.contains(new Point3D(17, -32, 1.001)));
     }
 
     @Test
     public void testOffset() {
-        Cartesian3D p1 = new Cartesian3D(1, 1, 1);
-        Plane p = new Plane(p1, new Cartesian3D(0.2, 0, 0), 1.0e-10);
-        Assert.assertEquals(-5.0, p.getOffset(new Cartesian3D(-4, 0, 0)), 1.0e-10);
-        Assert.assertEquals(+5.0, p.getOffset(new Cartesian3D(6, 10, -12)), 1.0e-10);
+        Point3D p1 = new Point3D(1, 1, 1);
+        Plane p = new Plane(p1, new Vector3D(0.2, 0, 0), 1.0e-10);
+        Assert.assertEquals(-5.0, p.getOffset(new Point3D(-4, 0, 0)), 1.0e-10);
+        Assert.assertEquals(+5.0, p.getOffset(new Point3D(6, 10, -12)), 1.0e-10);
         Assert.assertEquals(0.3,
-                            p.getOffset(new Cartesian3D(1.0, p1, 0.3, p.getNormal())),
+                            p.getOffset(Point3D.vectorCombination(1.0, p1, 0.3, p.getNormal())),
                             1.0e-10);
         Assert.assertEquals(-0.3,
-                            p.getOffset(new Cartesian3D(1.0, p1, -0.3, p.getNormal())),
+                            p.getOffset(Point3D.vectorCombination(1.0, p1, -0.3, p.getNormal())),
                             1.0e-10);
     }
 
     @Test
     public void testPoint() {
-        Plane p = new Plane(new Cartesian3D(2, -3, 1), new Cartesian3D(1, 4, 9), 1.0e-10);
+        Plane p = new Plane(new Point3D(2, -3, 1), new Vector3D(1, 4, 9), 1.0e-10);
         Assert.assertTrue(p.contains(p.getOrigin()));
     }
 
     @Test
     public void testThreePoints() {
-        Cartesian3D p1 = new Cartesian3D(1.2, 3.4, -5.8);
-        Cartesian3D p2 = new Cartesian3D(3.4, -5.8, 1.2);
-        Cartesian3D p3 = new Cartesian3D(-2.0, 4.3, 0.7);
+        Point3D p1 = new Point3D(1.2, 3.4, -5.8);
+        Point3D p2 = new Point3D(3.4, -5.8, 1.2);
+        Point3D p3 = new Point3D(-2.0, 4.3, 0.7);
         Plane    p  = new Plane(p1, p2, p3, 1.0e-10);
         Assert.assertTrue(p.contains(p1));
         Assert.assertTrue(p.contains(p2));
@@ -66,11 +66,11 @@ public void testThreePoints() {
 
     @Test
     public void testRotate() {
-        Cartesian3D p1 = new Cartesian3D(1.2, 3.4, -5.8);
-        Cartesian3D p2 = new Cartesian3D(3.4, -5.8, 1.2);
-        Cartesian3D p3 = new Cartesian3D(-2.0, 4.3, 0.7);
+        Point3D p1 = new Point3D(1.2, 3.4, -5.8);
+        Point3D p2 = new Point3D(3.4, -5.8, 1.2);
+        Point3D p3 = new Point3D(-2.0, 4.3, 0.7);
         Plane    p  = new Plane(p1, p2, p3, 1.0e-10);
-        Cartesian3D oldNormal = p.getNormal();
+        Vector3D oldNormal = p.getNormal();
 
         p = p.rotate(p2, new Rotation(p2.subtract(p1), 1.7, RotationConvention.VECTOR_OPERATOR));
         Assert.assertTrue(p.contains(p1));
@@ -91,22 +91,22 @@ public void testRotate() {
 
     @Test
     public void testTranslate() {
-        Cartesian3D p1 = new Cartesian3D(1.2, 3.4, -5.8);
-        Cartesian3D p2 = new Cartesian3D(3.4, -5.8, 1.2);
-        Cartesian3D p3 = new Cartesian3D(-2.0, 4.3, 0.7);
+        Point3D p1 = new Point3D(1.2, 3.4, -5.8);
+        Point3D p2 = new Point3D(3.4, -5.8, 1.2);
+        Point3D p3 = new Point3D(-2.0, 4.3, 0.7);
         Plane    p  = new Plane(p1, p2, p3, 1.0e-10);
 
-        p = p.translate(new Cartesian3D(2.0, p.getU(), -1.5, p.getV()));
+        p = p.translate(Vector3D.linearCombination(2.0, p.getU(), -1.5, p.getV()));
         Assert.assertTrue(p.contains(p1));
         Assert.assertTrue(p.contains(p2));
         Assert.assertTrue(p.contains(p3));
 
-        p = p.translate(new Cartesian3D(-1.2, p.getNormal()));
+        p = p.translate(Vector3D.linearCombination(-1.2, p.getNormal()));
         Assert.assertTrue(! p.contains(p1));
         Assert.assertTrue(! p.contains(p2));
         Assert.assertTrue(! p.contains(p3));
 
-        p = p.translate(new Cartesian3D(+1.2, p.getNormal()));
+        p = p.translate(Vector3D.linearCombination(+1.2, p.getNormal()));
         Assert.assertTrue(p.contains(p1));
         Assert.assertTrue(p.contains(p2));
         Assert.assertTrue(p.contains(p3));
@@ -115,22 +115,22 @@ public void testTranslate() {
 
     @Test
     public void testIntersection() {
-        Plane p = new Plane(new Cartesian3D(1, 2, 3), new Cartesian3D(-4, 1, -5), 1.0e-10);
-        Line  l = new Line(new Cartesian3D(0.2, -3.5, 0.7), new Cartesian3D(1.2, -2.5, -0.3), 1.0e-10);
-        Cartesian3D point = p.intersection(l);
+        Plane p = new Plane(new Point3D(1, 2, 3), new Vector3D(-4, 1, -5), 1.0e-10);
+        Line  l = new Line(new Point3D(0.2, -3.5, 0.7), new Point3D(1.2, -2.5, -0.3), 1.0e-10);
+        Point3D point = p.intersection(l);
         Assert.assertTrue(p.contains(point));
         Assert.assertTrue(l.contains(point));
-        Assert.assertNull(p.intersection(new Line(new Cartesian3D(10, 10, 10),
-                                                  new Cartesian3D(10, 10, 10).add(p.getNormal().orthogonal()),
+        Assert.assertNull(p.intersection(new Line(new Point3D(10, 10, 10),
+                                                  new Point3D(10, 10, 10).add(p.getNormal().orthogonal()),
                                                   1.0e-10)));
     }
 
     @Test
     public void testIntersection2() {
-        Cartesian3D p1  = new Cartesian3D (1.2, 3.4, -5.8);
-        Cartesian3D p2  = new Cartesian3D (3.4, -5.8, 1.2);
-        Plane    pA  = new Plane(p1, p2, new Cartesian3D (-2.0, 4.3, 0.7), 1.0e-10);
-        Plane    pB  = new Plane(p1, new Cartesian3D (11.4, -3.8, 5.1), p2, 1.0e-10);
+        Point3D p1  = new Point3D (1.2, 3.4, -5.8);
+        Point3D p2  = new Point3D (3.4, -5.8, 1.2);
+        Plane    pA  = new Plane(p1, p2, new Point3D (-2.0, 4.3, 0.7), 1.0e-10);
+        Plane    pB  = new Plane(p1, new Point3D (11.4, -3.8, 5.1), p2, 1.0e-10);
         Line     l   = pA.intersection(pB);
         Assert.assertTrue(l.contains(p1));
         Assert.assertTrue(l.contains(p2));
@@ -139,11 +139,11 @@ public void testIntersection2() {
 
     @Test
     public void testIntersection3() {
-        Cartesian3D reference = new Cartesian3D (1.2, 3.4, -5.8);
-        Plane p1 = new Plane(reference, new Cartesian3D(1, 3, 3), 1.0e-10);
-        Plane p2 = new Plane(reference, new Cartesian3D(-2, 4, 0), 1.0e-10);
-        Plane p3 = new Plane(reference, new Cartesian3D(7, 0, -4), 1.0e-10);
-        Cartesian3D p = Plane.intersection(p1, p2, p3);
+        Point3D reference = new Point3D (1.2, 3.4, -5.8);
+        Plane p1 = new Plane(reference, new Vector3D(1, 3, 3), 1.0e-10);
+        Plane p2 = new Plane(reference, new Vector3D(-2, 4, 0), 1.0e-10);
+        Plane p3 = new Plane(reference, new Vector3D(7, 0, -4), 1.0e-10);
+        Point3D p = Plane.intersection(p1, p2, p3);
         Assert.assertEquals(reference.getX(), p.getX(), 1.0e-10);
         Assert.assertEquals(reference.getY(), p.getY(), 1.0e-10);
         Assert.assertEquals(reference.getZ(), p.getZ(), 1.0e-10);
@@ -151,15 +151,15 @@ public void testIntersection3() {
 
     @Test
     public void testSimilar() {
-        Cartesian3D p1  = new Cartesian3D (1.2, 3.4, -5.8);
-        Cartesian3D p2  = new Cartesian3D (3.4, -5.8, 1.2);
-        Cartesian3D p3  = new Cartesian3D (-2.0, 4.3, 0.7);
+        Point3D p1  = new Point3D (1.2, 3.4, -5.8);
+        Point3D p2  = new Point3D (3.4, -5.8, 1.2);
+        Point3D p3  = new Point3D (-2.0, 4.3, 0.7);
         Plane    pA  = new Plane(p1, p2, p3, 1.0e-10);
-        Plane    pB  = new Plane(p1, new Cartesian3D (11.4, -3.8, 5.1), p2, 1.0e-10);
+        Plane    pB  = new Plane(p1, new Point3D (11.4, -3.8, 5.1), p2, 1.0e-10);
         Assert.assertTrue(! pA.isSimilarTo(pB));
         Assert.assertTrue(pA.isSimilarTo(pA));
         Assert.assertTrue(pA.isSimilarTo(new Plane(p1, p3, p2, 1.0e-10)));
-        Cartesian3D shift = new Cartesian3D(0.3, pA.getNormal());
+        Vector3D shift = Vector3D.linearCombination(0.3, pA.getNormal());
         Assert.assertTrue(! pA.isSimilarTo(new Plane(p1.add(shift),
                                                      p3.add(shift),
                                                      p2.add(shift),
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
new file mode 100644
index 0000000..fa4cf3f
--- /dev/null
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Point3DTest.java
@@ -0,0 +1,287 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.geometry.euclidean.threed;
+
+import java.util.regex.Pattern;
+
+import org.apache.commons.numbers.core.Precision;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Point3DTest {
+
+    private static final double EPS = Math.ulp(1d);
+
+    @Test
+    public void testConstants() {
+        // act/assert
+        checkPoint(Point3D.ZERO, 0, 0, 0);
+        checkPoint(Point3D.NaN, Double.NaN, Double.NaN, Double.NaN);
+        checkPoint(Point3D.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+        checkPoint(Point3D.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+    }
+
+    @Test
+    public void testAsVector() {
+        // act/assert
+        checkVector(Point3D.of(1, 2, 3).asVector(), 1, 2, 3);
+        checkVector(Point3D.of(-1, -2, -3).asVector(), -1, -2, -3);
+        checkVector(Point3D.of(Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY).asVector(),
+                Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
+    }
+
+    @Test
+    public void testDistance() {
+        // act/assert
+        Point3D p1 = Point3D.of(1, 2, 3);
+        Point3D p2 = Point3D.of(4, 5, 6);
+        Point3D p3 = Point3D.of(-7, -8, -9);
+
+        // act/assert
+        Assert.assertEquals(0,  p1.distance(p1), EPS);
+        Assert.assertEquals(0,  p2.distance(p2), EPS);
+        Assert.assertEquals(0,  p3.distance(p3), EPS);
+
+        Assert.assertEquals(Math.sqrt(27), p1.distance(p2), EPS);
+        Assert.assertEquals(Math.sqrt(27), p2.distance(p1), EPS);
+
+        Assert.assertEquals(Math.sqrt(308), p1.distance(p3), EPS);
+        Assert.assertEquals(Math.sqrt(308), p3.distance(p1), EPS);
+    }
+
+    @Test
+    public void testSubtract() {
+        // act/assert
+        Point3D p1 = Point3D.of(1, 2, 3);
+        Point3D p2 = Point3D.of(4, 5, 6);
+        Point3D p3 = Point3D.of(-7, -8, -9);
+
+        // act/assert
+        checkVector(p1.subtract(p1), 0, 0, 0);
+        checkVector(p2.subtract(p2), 0, 0, 0);
+        checkVector(p3.subtract(p3), 0, 0, 0);
+
+        checkVector(p1.subtract(p2), -3, -3, -3);
+        checkVector(p2.subtract(p1), 3, 3, 3);
+
+        checkVector(p1.subtract(p3), 8, 10, 12);
+        checkVector(p3.subtract(p1), -8, -10,-12);
+    }
+
+    @Test
+    public void testVectorTo() {
+        // act/assert
+        Point3D p1 = Point3D.of(1, 2, 3);
+        Point3D p2 = Point3D.of(4, 5, 6);
+        Point3D p3 = Point3D.of(-7, -8, -9);
+
+        // act/assert
+        checkVector(p1.vectorTo(p1), 0, 0, 0);
+        checkVector(p2.vectorTo(p2), 0, 0, 0);
+        checkVector(p3.vectorTo(p3), 0, 0, 0);
+
+        checkVector(p1.vectorTo(p2), 3, 3, 3);
+        checkVector(p2.vectorTo(p1), -3, -3, -3);
+
+        checkVector(p1.vectorTo(p3), -8, -10, -12);
+        checkVector(p3.vectorTo(p1), 8, 10, 12);
+    }
+
+    @Test
+    public void testAdd() {
+        // act/assert
+        Point3D p1 = Point3D.of(1, 2, 3);
+        Point3D p2 = Point3D.of(-4, -5, -6);
+
+        // act/assert
+        checkPoint(p1.add(Vector3D.ZERO), 1, 2, 3);
+        checkPoint(p1.add(Vector3D.of(4, 5, 6)), 5, 7, 9);
+        checkPoint(p1.add(Vector3D.of(-4, -5, -6)), -3, -3, -3);
+
+        checkPoint(p2.add(Vector3D.ZERO), -4, -5, -6);
+        checkPoint(p2.add(Vector3D.of(1, 0, 0)), -3, -5, -6);
+        checkPoint(p2.add(Vector3D.of(0, -1, 0)), -4, -6, -6);
+        checkPoint(p2.add(Vector3D.of(0, 0, 1)), -4, -5, -5);
+    }
+
+    @Test
+    public void testHashCode() {
+        // arrange
+        double delta = 10 * Precision.EPSILON;
+
+        Point3D u = Point3D.of(1, 1, 1);
+        Point3D v = Point3D.of(1 + delta, 1 + delta, 1 + delta);
+        Point3D w = Point3D.of(1, 1, 1);
+
+        // act/assert
+        Assert.assertTrue(u.hashCode() != v.hashCode());
+        Assert.assertEquals(u.hashCode(), w.hashCode());
+
+        Assert.assertEquals(Point3D.of(0, 0, Double.NaN).hashCode(), Point3D.NaN.hashCode());
+        Assert.assertEquals(Point3D.of(0, Double.NaN, 0).hashCode(), Point3D.NaN.hashCode());
+        Assert.assertEquals(Point3D.of(Double.NaN, 0, 0).hashCode(), Point3D.NaN.hashCode());
+        Assert.assertEquals(Point3D.of(0, Double.NaN, 0).hashCode(), Point3D.of(Double.NaN, 0, 0).hashCode());
+    }
+
+    @Test
+    public void testEquals() {
+        // arrange
+        double delta = 10 * Precision.EPSILON;
+
+        Point3D u1 = Point3D.of(1, 2, 3);
+        Point3D u2 = Point3D.of(1, 2, 3);
+
+        // act/assert
+        Assert.assertFalse(u1.equals(null));
+        Assert.assertFalse(u1.equals(new Object()));
+
+        Assert.assertTrue(u1.equals(u1));
+        Assert.assertTrue(u1.equals(u2));
+
+        Assert.assertFalse(u1.equals(Point3D.of(-1, -2, -3)));
+        Assert.assertFalse(u1.equals(Point3D.of(1 + delta, 2, 3)));
+        Assert.assertFalse(u1.equals(Point3D.of(1, 2 + delta, 3)));
+        Assert.assertFalse(u1.equals(Point3D.of(1, 2, 3 + delta)));
+
+        Assert.assertTrue(Point3D.of(Double.NaN, 0, 0).equals(Point3D.of(0, Double.NaN, 0)));
+        Assert.assertTrue(Point3D.of(0, 0, Double.NaN).equals(Point3D.of(Double.NaN, 0, 0)));
+
+        Assert.assertTrue(Point3D.of(0, 0, Double.NEGATIVE_INFINITY).equals(Point3D.of(0, 0, Double.NEGATIVE_INFINITY)));
+        Assert.assertFalse(Point3D.of(0, 0, Double.NEGATIVE_INFINITY).equals(Point3D.of(0, Double.NEGATIVE_INFINITY, 0)));
+        Assert.assertFalse(Point3D.of(0, 0, Double.NEGATIVE_INFINITY).equals(Point3D.of(Double.NEGATIVE_INFINITY, 0, 0)));
+
+        Assert.assertTrue(Point3D.of(0, 0, Double.POSITIVE_INFINITY).equals(Point3D.of(0, 0, Double.POSITIVE_INFINITY)));
+        Assert.assertFalse(Point3D.of(0, 0, Double.POSITIVE_INFINITY).equals(Point3D.of(0, Double.POSITIVE_INFINITY, 0)));
+        Assert.assertFalse(Point3D.of(0, 0, Double.POSITIVE_INFINITY).equals(Point3D.of(Double.POSITIVE_INFINITY, 0, 0)));
+    }
+
+    @Test
+    public void testToString() {
+        // arrange
+        Point3D p = Point3D.of(1, 2, 3);
+        Pattern pattern = Pattern.compile("\\(1.{0,2}; 2.{0,2}; 3.{0,2}\\)");
+
+        // act
+        String str = p.toString();
+
+        // assert
+        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                    pattern.matcher(str).matches());
+    }
+
+    @Test
+    public void testOf() {
+        // act/assert
+        checkPoint(Point3D.of(1, 2, 3), 1, 2, 3);
+        checkPoint(Point3D.of(-1, -2, -3), -1, -2, -3);
+        checkPoint(Point3D.of(Math.PI, Double.NaN, Double.POSITIVE_INFINITY),
+                Math.PI, Double.NaN, Double.POSITIVE_INFINITY);
+        checkPoint(Point3D.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E),
+                Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E);
+    }
+
+    @Test
+    public void testOf_coordinateArg() {
+        // act/assert
+        checkPoint(Point3D.of(Vector3D.of(1, 2, 3)), 1, 2, 3);
+        checkPoint(Point3D.of(Vector3D.of(-1, -2, -3)), -1, -2, -3);
+        checkPoint(Point3D.of(Vector3D.of(Math.PI, Double.NaN, Double.POSITIVE_INFINITY)),
+                Math.PI, Double.NaN, Double.POSITIVE_INFINITY);
+        checkPoint(Point3D.of(Vector3D.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E)),
+                   Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E);
+    }
+
+    @Test
+    public void testOf_arrayArg() {
+        // act/assert
+        checkPoint(Point3D.of(new double[] { 1, 2, 3 }), 1, 2, 3);
+        checkPoint(Point3D.of(new double[] { -1, -2, -3 }), -1, -2, -3);
+        checkPoint(Point3D.of(new double[] { Math.PI, Double.NaN, Double.POSITIVE_INFINITY }),
+                Math.PI, Double.NaN, Double.POSITIVE_INFINITY);
+        checkPoint(Point3D.of(new double[] { Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E}),
+                Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testOf_arrayArg_invalidDimensions() {
+        // act/assert
+        Point3D.of(new double[] { 0.0, 0.0 });
+    }
+
+    @Test
+    public void testVectorCombination1() {
+        // arrange
+        Point3D p1 = Point3D.of(1, 2, 3);
+
+        // act/assert
+        checkPoint(Point3D.vectorCombination(0, p1), 0, 0, 0);
+
+        checkPoint(Point3D.vectorCombination(1, p1), 1, 2, 3);
+        checkPoint(Point3D.vectorCombination(-1, p1), -1, -2, -3);
+
+        checkPoint(Point3D.vectorCombination(0.5, p1), 0.5, 1, 1.5);
+        checkPoint(Point3D.vectorCombination(-0.5, p1), -0.5, -1, -1.5);
+    }
+
+    @Test
+    public void testVectorCombination2() {
+        // arrange
+        Point3D p1 = Point3D.of(1, 2, 3);
+        Point3D p2 = Point3D.of(-3, -4, -5);
+
+        // act/assert
+        checkPoint(Point3D.vectorCombination(2, p1, -3, p2), 11, 16, 21);
+        checkPoint(Point3D.vectorCombination(-3, p1, 2, p2), -9, -14, -19);
+    }
+
+    @Test
+    public void testVectorCombination3() {
+        // arrange
+        Point3D p1 = Point3D.of(1, 2, 3);
+        Point3D p2 = Point3D.of(-3, -4, -5);
+        Point3D p3 = Point3D.of(5, 6, 7);
+
+        // act/assert
+        checkPoint(Point3D.vectorCombination(2, p1, -3, p2, 4, p3), 31, 40, 49);
+        checkPoint(Point3D.vectorCombination(-3, p1, 2, p2, -4, p3), -29, -38, -47);
+    }
+
+    @Test
+    public void testVectorCombination4() {
+        // arrange
+        Point3D p1 = Point3D.of(1, 2, 3);
+        Point3D p2 = Point3D.of(-3, -4, -5);
+        Point3D p3 = Point3D.of(5, 6, 7);
+        Point3D p4 = Point3D.of(-7, -8, 9);
+
+        // act/assert
+        checkPoint(Point3D.vectorCombination(2, p1, -3, p2, 4, p3, -5, p4), 66, 80, 4);
+        checkPoint(Point3D.vectorCombination(-3, p1, 2, p2, -4, p3, 5, p4), -64, -78, -2);
+    }
+
+    private void checkVector(Vector3D v, double x, double y, double z) {
+        Assert.assertEquals(x, v.getX(), EPS);
+        Assert.assertEquals(y, v.getY(), EPS);
+        Assert.assertEquals(z, v.getZ(), EPS);
+    }
+
+    private void checkPoint(Point3D p, double x, double y, double z) {
+        Assert.assertEquals(x, p.getX(), EPS);
+        Assert.assertEquals(y, p.getY(), EPS);
+        Assert.assertEquals(z, p.getZ(), EPS);
+    }
+}
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSetTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSetTest.java
index 4ac23be..2e04ec1 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSetTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/PolyhedronsSetTest.java
@@ -33,8 +33,7 @@
 import org.apache.commons.geometry.core.partitioning.RegionFactory;
 import org.apache.commons.geometry.core.partitioning.SubHyperplane;
 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
-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.geometry.euclidean.twod.PolygonsSet;
 import org.apache.commons.geometry.euclidean.twod.SubLine;
 import org.apache.commons.numbers.core.Precision;
@@ -56,44 +55,44 @@ public void testWholeSpace() {
         Assert.assertEquals(TEST_TOLERANCE, polySet.getTolerance(), Precision.EPSILON);
         EuclideanTestUtils.assertPositiveInfinity(polySet.getSize());
         Assert.assertEquals(0.0, polySet.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian3D.NaN, (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point3D.NaN, polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(polySet.isEmpty());
         Assert.assertTrue(polySet.isFull());
 
         checkPoints(Region.Location.INSIDE, polySet,
-                new Cartesian3D(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE),
-                new Cartesian3D(-100, -100, -100),
-                new Cartesian3D(0, 0, 0),
-                new Cartesian3D(100, 100, 100),
-                new Cartesian3D(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE));
+                new Point3D(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE),
+                new Point3D(-100, -100, -100),
+                new Point3D(0, 0, 0),
+                new Point3D(100, 100, 100),
+                new Point3D(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE));
     }
 
     @Test
     public void testEmptyRegion() {
         // act
-        PolyhedronsSet polySet = new PolyhedronsSet(new BSPTree<Euclidean3D>(Boolean.FALSE), TEST_TOLERANCE);
+        PolyhedronsSet polySet = new PolyhedronsSet(new BSPTree<Point3D>(Boolean.FALSE), TEST_TOLERANCE);
 
         // assert
         Assert.assertEquals(TEST_TOLERANCE, polySet.getTolerance(), Precision.EPSILON);
         Assert.assertEquals(0.0, polySet.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(0.0, polySet.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian3D.NaN, (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point3D.NaN, polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertTrue(polySet.isEmpty());
         Assert.assertFalse(polySet.isFull());
 
         checkPoints(Region.Location.OUTSIDE, polySet,
-                new Cartesian3D(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE),
-                new Cartesian3D(-100, -100, -100),
-                new Cartesian3D(0, 0, 0),
-                new Cartesian3D(100, 100, 100),
-                new Cartesian3D(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE));
+                new Point3D(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE),
+                new Point3D(-100, -100, -100),
+                new Point3D(0, 0, 0),
+                new Point3D(100, 100, 100),
+                new Point3D(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE));
     }
 
     @Test
     public void testHalfSpace() {
         // arrange
-        List<SubHyperplane<Euclidean3D>> boundaries = new ArrayList<>();
-        boundaries.add(new SubPlane(new Plane(Cartesian3D.ZERO, Cartesian3D.PLUS_J, TEST_TOLERANCE),
+        List<SubHyperplane<Point3D>> boundaries = new ArrayList<>();
+        boundaries.add(new SubPlane(new Plane(Point3D.ZERO, Vector3D.PLUS_Y, TEST_TOLERANCE),
                 new PolygonsSet(TEST_TOLERANCE)));
 
         // act
@@ -103,49 +102,49 @@ public void testHalfSpace() {
         Assert.assertEquals(TEST_TOLERANCE, polySet.getTolerance(), Precision.EPSILON);
         EuclideanTestUtils.assertPositiveInfinity(polySet.getSize());
         EuclideanTestUtils.assertPositiveInfinity(polySet.getBoundarySize());
-        EuclideanTestUtils.assertVectorEquals(Cartesian3D.NaN, (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point3D.NaN, polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(polySet.isEmpty());
         Assert.assertFalse(polySet.isFull());
 
         checkPoints(Region.Location.INSIDE, polySet,
-                new Cartesian3D(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE),
-                new Cartesian3D(-100, -100, -100));
-        checkPoints(Region.Location.BOUNDARY, polySet, new Cartesian3D(0, 0, 0));
+                new Point3D(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE),
+                new Point3D(-100, -100, -100));
+        checkPoints(Region.Location.BOUNDARY, polySet, new Point3D(0, 0, 0));
         checkPoints(Region.Location.OUTSIDE, polySet,
-                new Cartesian3D(100, 100, 100),
-                new Cartesian3D(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE));
+                new Point3D(100, 100, 100),
+                new Point3D(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE));
     }
 
     @Test
     public void testInvertedRegion() {
         // arrange
-        List<SubHyperplane<Euclidean3D>> boundaries = createBoxBoundaries(Cartesian3D.ZERO, 1.0, TEST_TOLERANCE);
+        List<SubHyperplane<Point3D>> boundaries = createBoxBoundaries(Point3D.ZERO, 1.0, TEST_TOLERANCE);
         PolyhedronsSet box = new PolyhedronsSet(boundaries, TEST_TOLERANCE);;
 
         // act
-        PolyhedronsSet polySet = (PolyhedronsSet) new RegionFactory<Euclidean3D>().getComplement(box);
+        PolyhedronsSet polySet = (PolyhedronsSet) new RegionFactory<Point3D>().getComplement(box);
 
         // assert
         Assert.assertEquals(TEST_TOLERANCE, polySet.getTolerance(), Precision.EPSILON);
         EuclideanTestUtils.assertPositiveInfinity(polySet.getSize());
         Assert.assertEquals(6, polySet.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian3D.NaN, (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point3D.NaN, polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(polySet.isEmpty());
         Assert.assertFalse(polySet.isFull());
 
         checkPoints(Region.Location.INSIDE, polySet,
-                new Cartesian3D(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE),
-                new Cartesian3D(-100, -100, -100),
-                new Cartesian3D(100, 100, 100),
-                new Cartesian3D(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE));
+                new Point3D(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE),
+                new Point3D(-100, -100, -100),
+                new Point3D(100, 100, 100),
+                new Point3D(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE));
         checkPoints(Region.Location.OUTSIDE, polySet,
-                new Cartesian3D(0, 0, 0));
+                new Point3D(0, 0, 0));
     }
 
     @Test
     public void testCreateFromBoundaries_noBoundaries_treeRepresentsWholeSpace() {
         // arrange
-        List<SubHyperplane<Euclidean3D>> boundaries = new ArrayList<>();
+        List<SubHyperplane<Point3D>> boundaries = new ArrayList<>();
 
         // act
         PolyhedronsSet polySet = new PolyhedronsSet(boundaries, TEST_TOLERANCE);
@@ -154,7 +153,7 @@ public void testCreateFromBoundaries_noBoundaries_treeRepresentsWholeSpace() {
         Assert.assertEquals(TEST_TOLERANCE, polySet.getTolerance(), Precision.EPSILON);
         EuclideanTestUtils.assertPositiveInfinity(polySet.getSize());
         Assert.assertEquals(0.0, polySet.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian3D.NaN, (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point3D.NaN, polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(polySet.isEmpty());
         Assert.assertTrue(polySet.isFull());
     }
@@ -162,7 +161,7 @@ public void testCreateFromBoundaries_noBoundaries_treeRepresentsWholeSpace() {
     @Test
     public void testCreateFromBoundaries_unitBox() {
         // arrange
-        List<SubHyperplane<Euclidean3D>> boundaries = createBoxBoundaries(Cartesian3D.ZERO, 1.0, TEST_TOLERANCE);
+        List<SubHyperplane<Point3D>> boundaries = createBoxBoundaries(Point3D.ZERO, 1.0, TEST_TOLERANCE);
 
         // act
         PolyhedronsSet polySet = new PolyhedronsSet(boundaries, TEST_TOLERANCE);
@@ -171,63 +170,63 @@ public void testCreateFromBoundaries_unitBox() {
         Assert.assertEquals(TEST_TOLERANCE, polySet.getTolerance(), Precision.EPSILON);
         Assert.assertEquals(1.0, polySet.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(6.0, polySet.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian3D.ZERO, (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point3D.ZERO, polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(polySet.isEmpty());
         Assert.assertFalse(polySet.isFull());
 
         checkPoints(Region.Location.OUTSIDE, polySet,
-                new Cartesian3D(-1, 0, 0),
-                new Cartesian3D(1, 0, 0),
-                new Cartesian3D(0, -1, 0),
-                new Cartesian3D(0, 1, 0),
-                new Cartesian3D(0, 0, -1),
-                new Cartesian3D(0, 0, 1),
-
-                new Cartesian3D(1, 1, 1),
-                new Cartesian3D(1, 1, -1),
-                new Cartesian3D(1, -1, 1),
-                new Cartesian3D(1, -1, -1),
-                new Cartesian3D(-1, 1, 1),
-                new Cartesian3D(-1, 1, -1),
-                new Cartesian3D(-1, -1, 1),
-                new Cartesian3D(-1, -1, -1));
+                new Point3D(-1, 0, 0),
+                new Point3D(1, 0, 0),
+                new Point3D(0, -1, 0),
+                new Point3D(0, 1, 0),
+                new Point3D(0, 0, -1),
+                new Point3D(0, 0, 1),
+
+                new Point3D(1, 1, 1),
+                new Point3D(1, 1, -1),
+                new Point3D(1, -1, 1),
+                new Point3D(1, -1, -1),
+                new Point3D(-1, 1, 1),
+                new Point3D(-1, 1, -1),
+                new Point3D(-1, -1, 1),
+                new Point3D(-1, -1, -1));
 
         checkPoints(Region.Location.BOUNDARY, polySet,
-                new Cartesian3D(0.5, 0, 0),
-                new Cartesian3D(-0.5, 0, 0),
-                new Cartesian3D(0, 0.5, 0),
-                new Cartesian3D(0, -0.5, 0),
-                new Cartesian3D(0, 0, 0.5),
-                new Cartesian3D(0, 0, -0.5),
-
-                new Cartesian3D(0.5, 0.5, 0.5),
-                new Cartesian3D(0.5, 0.5, -0.5),
-                new Cartesian3D(0.5, -0.5, 0.5),
-                new Cartesian3D(0.5, -0.5, -0.5),
-                new Cartesian3D(-0.5, 0.5, 0.5),
-                new Cartesian3D(-0.5, 0.5, -0.5),
-                new Cartesian3D(-0.5, -0.5, 0.5),
-                new Cartesian3D(-0.5, -0.5, -0.5));
+                new Point3D(0.5, 0, 0),
+                new Point3D(-0.5, 0, 0),
+                new Point3D(0, 0.5, 0),
+                new Point3D(0, -0.5, 0),
+                new Point3D(0, 0, 0.5),
+                new Point3D(0, 0, -0.5),
+
+                new Point3D(0.5, 0.5, 0.5),
+                new Point3D(0.5, 0.5, -0.5),
+                new Point3D(0.5, -0.5, 0.5),
+                new Point3D(0.5, -0.5, -0.5),
+                new Point3D(-0.5, 0.5, 0.5),
+                new Point3D(-0.5, 0.5, -0.5),
+                new Point3D(-0.5, -0.5, 0.5),
+                new Point3D(-0.5, -0.5, -0.5));
 
         checkPoints(Region.Location.INSIDE, polySet,
-                new Cartesian3D(0, 0, 0),
-
-                new Cartesian3D(0.4, 0.4, 0.4),
-                new Cartesian3D(0.4, 0.4, -0.4),
-                new Cartesian3D(0.4, -0.4, 0.4),
-                new Cartesian3D(0.4, -0.4, -0.4),
-                new Cartesian3D(-0.4, 0.4, 0.4),
-                new Cartesian3D(-0.4, 0.4, -0.4),
-                new Cartesian3D(-0.4, -0.4, 0.4),
-                new Cartesian3D(-0.4, -0.4, -0.4));
+                new Point3D(0, 0, 0),
+
+                new Point3D(0.4, 0.4, 0.4),
+                new Point3D(0.4, 0.4, -0.4),
+                new Point3D(0.4, -0.4, 0.4),
+                new Point3D(0.4, -0.4, -0.4),
+                new Point3D(-0.4, 0.4, 0.4),
+                new Point3D(-0.4, 0.4, -0.4),
+                new Point3D(-0.4, -0.4, 0.4),
+                new Point3D(-0.4, -0.4, -0.4));
     }
 
     @Test
     public void testCreateFromBoundaries_twoBoxes_disjoint() {
         // arrange
-        List<SubHyperplane<Euclidean3D>> boundaries = new ArrayList<>();
-        boundaries.addAll(createBoxBoundaries(Cartesian3D.ZERO, 1.0, TEST_TOLERANCE));
-        boundaries.addAll(createBoxBoundaries(new Cartesian3D(2, 0, 0), 1.0, TEST_TOLERANCE));
+        List<SubHyperplane<Point3D>> boundaries = new ArrayList<>();
+        boundaries.addAll(createBoxBoundaries(Point3D.ZERO, 1.0, TEST_TOLERANCE));
+        boundaries.addAll(createBoxBoundaries(new Point3D(2, 0, 0), 1.0, TEST_TOLERANCE));
 
         // act
         PolyhedronsSet polySet = new PolyhedronsSet(boundaries, TEST_TOLERANCE);
@@ -236,26 +235,26 @@ public void testCreateFromBoundaries_twoBoxes_disjoint() {
         Assert.assertEquals(TEST_TOLERANCE, polySet.getTolerance(), Precision.EPSILON);
         Assert.assertEquals(2.0, polySet.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(12.0, polySet.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian3D(1, 0, 0), (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point3D(1, 0, 0), polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(polySet.isEmpty());
         Assert.assertFalse(polySet.isFull());
 
         checkPoints(Region.Location.OUTSIDE, polySet,
-                new Cartesian3D(-1, 0, 0),
-                new Cartesian3D(1, 0, 0),
-                new Cartesian3D(3, 0, 0));
+                new Point3D(-1, 0, 0),
+                new Point3D(1, 0, 0),
+                new Point3D(3, 0, 0));
 
         checkPoints(Region.Location.INSIDE, polySet,
-                new Cartesian3D(0, 0, 0),
-                new Cartesian3D(2, 0, 0));
+                new Point3D(0, 0, 0),
+                new Point3D(2, 0, 0));
     }
 
     @Test
     public void testCreateFromBoundaries_twoBoxes_sharedSide() {
         // arrange
-        List<SubHyperplane<Euclidean3D>> boundaries = new ArrayList<>();
-        boundaries.addAll(createBoxBoundaries(new Cartesian3D(0, 0, 0), 1.0, TEST_TOLERANCE));
-        boundaries.addAll(createBoxBoundaries(new Cartesian3D(1, 0, 0), 1.0, TEST_TOLERANCE));
+        List<SubHyperplane<Point3D>> boundaries = new ArrayList<>();
+        boundaries.addAll(createBoxBoundaries(new Point3D(0, 0, 0), 1.0, TEST_TOLERANCE));
+        boundaries.addAll(createBoxBoundaries(new Point3D(1, 0, 0), 1.0, TEST_TOLERANCE));
 
         // act
         PolyhedronsSet polySet = new PolyhedronsSet(boundaries, TEST_TOLERANCE);
@@ -264,26 +263,26 @@ public void testCreateFromBoundaries_twoBoxes_sharedSide() {
         Assert.assertEquals(TEST_TOLERANCE, polySet.getTolerance(), Precision.EPSILON);
         Assert.assertEquals(2.0, polySet.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(10.0, polySet.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian3D(0.5, 0, 0), (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point3D(0.5, 0, 0), polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(polySet.isEmpty());
         Assert.assertFalse(polySet.isFull());
 
         checkPoints(Region.Location.OUTSIDE, polySet,
-                new Cartesian3D(-1, 0, 0),
-                new Cartesian3D(2, 0, 0));
+                new Point3D(-1, 0, 0),
+                new Point3D(2, 0, 0));
 
         checkPoints(Region.Location.INSIDE, polySet,
-                new Cartesian3D(0, 0, 0),
-                new Cartesian3D(1, 0, 0));
+                new Point3D(0, 0, 0),
+                new Point3D(1, 0, 0));
     }
 
     @Test
     public void testCreateFromBoundaries_twoBoxes_separationLessThanTolerance() {
         // arrange
         double tolerance = 1e-6;
-        List<SubHyperplane<Euclidean3D>> boundaries = new ArrayList<>();
-        boundaries.addAll(createBoxBoundaries(new Cartesian3D(0, 0, 0), 1.0, tolerance));
-        boundaries.addAll(createBoxBoundaries(new Cartesian3D(1 + 1e-7, 0, 0), 1.0, tolerance));
+        List<SubHyperplane<Point3D>> boundaries = new ArrayList<>();
+        boundaries.addAll(createBoxBoundaries(new Point3D(0, 0, 0), 1.0, tolerance));
+        boundaries.addAll(createBoxBoundaries(new Point3D(1 + 1e-7, 0, 0), 1.0, tolerance));
 
         // act
         PolyhedronsSet polySet = new PolyhedronsSet(boundaries, tolerance);
@@ -292,25 +291,25 @@ public void testCreateFromBoundaries_twoBoxes_separationLessThanTolerance() {
         Assert.assertEquals(tolerance, polySet.getTolerance(), Precision.EPSILON);
         Assert.assertEquals(2.0, polySet.getSize(), tolerance);
         Assert.assertEquals(10.0, polySet.getBoundarySize(), tolerance);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian3D(0.5 + 5e-8, 0, 0), (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point3D(0.5 + 5e-8, 0, 0), polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(polySet.isEmpty());
         Assert.assertFalse(polySet.isFull());
 
         checkPoints(Region.Location.OUTSIDE, polySet,
-                new Cartesian3D(-1, 0, 0),
-                new Cartesian3D(2, 0, 0));
+                new Point3D(-1, 0, 0),
+                new Point3D(2, 0, 0));
 
         checkPoints(Region.Location.INSIDE, polySet,
-                new Cartesian3D(0, 0, 0),
-                new Cartesian3D(1, 0, 0));
+                new Point3D(0, 0, 0),
+                new Point3D(1, 0, 0));
     }
 
     @Test
     public void testCreateFromBoundaries_twoBoxes_sharedEdge() {
         // arrange
-        List<SubHyperplane<Euclidean3D>> boundaries = new ArrayList<>();
-        boundaries.addAll(createBoxBoundaries(new Cartesian3D(0, 0, 0), 1.0, TEST_TOLERANCE));
-        boundaries.addAll(createBoxBoundaries(new Cartesian3D(1, 1, 0), 1.0, TEST_TOLERANCE));
+        List<SubHyperplane<Point3D>> boundaries = new ArrayList<>();
+        boundaries.addAll(createBoxBoundaries(new Point3D(0, 0, 0), 1.0, TEST_TOLERANCE));
+        boundaries.addAll(createBoxBoundaries(new Point3D(1, 1, 0), 1.0, TEST_TOLERANCE));
 
         // act
         PolyhedronsSet polySet = new PolyhedronsSet(boundaries, TEST_TOLERANCE);
@@ -319,27 +318,27 @@ public void testCreateFromBoundaries_twoBoxes_sharedEdge() {
         Assert.assertEquals(TEST_TOLERANCE, polySet.getTolerance(), Precision.EPSILON);
         Assert.assertEquals(2.0, polySet.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(12.0, polySet.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian3D(0.5, 0.5, 0), (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point3D(0.5, 0.5, 0), polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(polySet.isEmpty());
         Assert.assertFalse(polySet.isFull());
 
         checkPoints(Region.Location.OUTSIDE, polySet,
-                new Cartesian3D(-1, 0, 0),
-                new Cartesian3D(1, 0, 0),
-                new Cartesian3D(0, 1, 0),
-                new Cartesian3D(2, 1, 0));
+                new Point3D(-1, 0, 0),
+                new Point3D(1, 0, 0),
+                new Point3D(0, 1, 0),
+                new Point3D(2, 1, 0));
 
         checkPoints(Region.Location.INSIDE, polySet,
-                new Cartesian3D(0, 0, 0),
-                new Cartesian3D(1, 1, 0));
+                new Point3D(0, 0, 0),
+                new Point3D(1, 1, 0));
     }
 
     @Test
     public void testCreateFromBoundaries_twoBoxes_sharedPoint() {
         // arrange
-        List<SubHyperplane<Euclidean3D>> boundaries = new ArrayList<>();
-        boundaries.addAll(createBoxBoundaries(new Cartesian3D(0, 0, 0), 1.0, TEST_TOLERANCE));
-        boundaries.addAll(createBoxBoundaries(new Cartesian3D(1, 1, 1), 1.0, TEST_TOLERANCE));
+        List<SubHyperplane<Point3D>> boundaries = new ArrayList<>();
+        boundaries.addAll(createBoxBoundaries(new Point3D(0, 0, 0), 1.0, TEST_TOLERANCE));
+        boundaries.addAll(createBoxBoundaries(new Point3D(1, 1, 1), 1.0, TEST_TOLERANCE));
 
         // act
         PolyhedronsSet polySet = new PolyhedronsSet(boundaries, TEST_TOLERANCE);
@@ -348,19 +347,19 @@ public void testCreateFromBoundaries_twoBoxes_sharedPoint() {
         Assert.assertEquals(TEST_TOLERANCE, polySet.getTolerance(), Precision.EPSILON);
         Assert.assertEquals(2.0, polySet.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(12.0, polySet.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian3D(0.5, 0.5, 0.5), (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point3D(0.5, 0.5, 0.5), polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(polySet.isEmpty());
         Assert.assertFalse(polySet.isFull());
 
         checkPoints(Region.Location.OUTSIDE, polySet,
-                new Cartesian3D(-1, 0, 0),
-                new Cartesian3D(1, 0, 0),
-                new Cartesian3D(0, 1, 1),
-                new Cartesian3D(2, 1, 1));
+                new Point3D(-1, 0, 0),
+                new Point3D(1, 0, 0),
+                new Point3D(0, 1, 1),
+                new Point3D(2, 1, 1));
 
         checkPoints(Region.Location.INSIDE, polySet,
-                new Cartesian3D(0, 0, 0),
-                new Cartesian3D(1, 1, 1));
+                new Point3D(0, 0, 0),
+                new Point3D(1, 1, 1));
     }
 
     @Test
@@ -371,7 +370,7 @@ public void testCreateBox() {
         // assert
         Assert.assertEquals(1.0, tree.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(6.0, tree.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian3D(0.5, 0.5, 0.5), (Cartesian3D) tree.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point3D(0.5, 0.5, 0.5), tree.getBarycenter(), TEST_TOLERANCE);
 
         for (double x = -0.25; x < 1.25; x += 0.1) {
             boolean xOK = (x >= 0.0) && (x <= 1.0);
@@ -381,25 +380,25 @@ public void testCreateBox() {
                     boolean zOK = (z >= 0.0) && (z <= 1.0);
                     Region.Location expected =
                         (xOK && yOK && zOK) ? Region.Location.INSIDE : Region.Location.OUTSIDE;
-                    Assert.assertEquals(expected, tree.checkPoint(new Cartesian3D(x, y, z)));
+                    Assert.assertEquals(expected, tree.checkPoint(new Point3D(x, y, z)));
                 }
             }
         }
-        checkPoints(Region.Location.BOUNDARY, tree, new Cartesian3D[] {
-            new Cartesian3D(0.0, 0.5, 0.5),
-            new Cartesian3D(1.0, 0.5, 0.5),
-            new Cartesian3D(0.5, 0.0, 0.5),
-            new Cartesian3D(0.5, 1.0, 0.5),
-            new Cartesian3D(0.5, 0.5, 0.0),
-            new Cartesian3D(0.5, 0.5, 1.0)
+        checkPoints(Region.Location.BOUNDARY, tree, new Point3D[] {
+            new Point3D(0.0, 0.5, 0.5),
+            new Point3D(1.0, 0.5, 0.5),
+            new Point3D(0.5, 0.0, 0.5),
+            new Point3D(0.5, 1.0, 0.5),
+            new Point3D(0.5, 0.5, 0.0),
+            new Point3D(0.5, 0.5, 1.0)
         });
-        checkPoints(Region.Location.OUTSIDE, tree, new Cartesian3D[] {
-            new Cartesian3D(0.0, 1.2, 1.2),
-            new Cartesian3D(1.0, 1.2, 1.2),
-            new Cartesian3D(1.2, 0.0, 1.2),
-            new Cartesian3D(1.2, 1.0, 1.2),
-            new Cartesian3D(1.2, 1.2, 0.0),
-            new Cartesian3D(1.2, 1.2, 1.0)
+        checkPoints(Region.Location.OUTSIDE, tree, new Point3D[] {
+            new Point3D(0.0, 1.2, 1.2),
+            new Point3D(1.0, 1.2, 1.2),
+            new Point3D(1.2, 0.0, 1.2),
+            new Point3D(1.2, 1.0, 1.2),
+            new Point3D(1.2, 1.2, 0.0),
+            new Point3D(1.2, 1.2, 1.0)
         });
     }
 
@@ -409,13 +408,13 @@ public void testInvertedBox() {
         PolyhedronsSet tree = new PolyhedronsSet(0, 1, 0, 1, 0, 1, 1.0e-10);
 
         // act
-        tree = (PolyhedronsSet) new RegionFactory<Euclidean3D>().getComplement(tree);
+        tree = (PolyhedronsSet) new RegionFactory<Point3D>().getComplement(tree);
 
         // assert
         EuclideanTestUtils.assertPositiveInfinity(tree.getSize());
         Assert.assertEquals(6.0, tree.getBoundarySize(), 1.0e-10);
 
-        Cartesian3D barycenter = (Cartesian3D) tree.getBarycenter();
+        Point3D barycenter = tree.getBarycenter();
         Assert.assertTrue(Double.isNaN(barycenter.getX()));
         Assert.assertTrue(Double.isNaN(barycenter.getY()));
         Assert.assertTrue(Double.isNaN(barycenter.getZ()));
@@ -428,39 +427,39 @@ public void testInvertedBox() {
                     boolean zOK = (z < 0.0) || (z > 1.0);
                     Region.Location expected =
                         (xOK || yOK || zOK) ? Region.Location.INSIDE : Region.Location.OUTSIDE;
-                    Assert.assertEquals(expected, tree.checkPoint(new Cartesian3D(x, y, z)));
+                    Assert.assertEquals(expected, tree.checkPoint(new Point3D(x, y, z)));
                 }
             }
         }
-        checkPoints(Region.Location.BOUNDARY, tree, new Cartesian3D[] {
-            new Cartesian3D(0.0, 0.5, 0.5),
-            new Cartesian3D(1.0, 0.5, 0.5),
-            new Cartesian3D(0.5, 0.0, 0.5),
-            new Cartesian3D(0.5, 1.0, 0.5),
-            new Cartesian3D(0.5, 0.5, 0.0),
-            new Cartesian3D(0.5, 0.5, 1.0)
+        checkPoints(Region.Location.BOUNDARY, tree, new Point3D[] {
+            new Point3D(0.0, 0.5, 0.5),
+            new Point3D(1.0, 0.5, 0.5),
+            new Point3D(0.5, 0.0, 0.5),
+            new Point3D(0.5, 1.0, 0.5),
+            new Point3D(0.5, 0.5, 0.0),
+            new Point3D(0.5, 0.5, 1.0)
         });
-        checkPoints(Region.Location.INSIDE, tree, new Cartesian3D[] {
-            new Cartesian3D(0.0, 1.2, 1.2),
-            new Cartesian3D(1.0, 1.2, 1.2),
-            new Cartesian3D(1.2, 0.0, 1.2),
-            new Cartesian3D(1.2, 1.0, 1.2),
-            new Cartesian3D(1.2, 1.2, 0.0),
-            new Cartesian3D(1.2, 1.2, 1.0)
+        checkPoints(Region.Location.INSIDE, tree, new Point3D[] {
+            new Point3D(0.0, 1.2, 1.2),
+            new Point3D(1.0, 1.2, 1.2),
+            new Point3D(1.2, 0.0, 1.2),
+            new Point3D(1.2, 1.0, 1.2),
+            new Point3D(1.2, 1.2, 0.0),
+            new Point3D(1.2, 1.2, 1.0)
         });
     }
 
     @Test
     public void testTetrahedron() {
         // arrange
-        Cartesian3D vertex1 = new Cartesian3D(1, 2, 3);
-        Cartesian3D vertex2 = new Cartesian3D(2, 2, 4);
-        Cartesian3D vertex3 = new Cartesian3D(2, 3, 3);
-        Cartesian3D vertex4 = new Cartesian3D(1, 3, 4);
+        Point3D vertex1 = new Point3D(1, 2, 3);
+        Point3D vertex2 = new Point3D(2, 2, 4);
+        Point3D vertex3 = new Point3D(2, 3, 3);
+        Point3D vertex4 = new Point3D(1, 3, 4);
 
         // act
         PolyhedronsSet tree =
-            (PolyhedronsSet) new RegionFactory<Euclidean3D>().buildConvex(
+            (PolyhedronsSet) new RegionFactory<Point3D>().buildConvex(
                 new Plane(vertex3, vertex2, vertex1, TEST_TOLERANCE),
                 new Plane(vertex2, vertex3, vertex4, TEST_TOLERANCE),
                 new Plane(vertex4, vertex3, vertex1, TEST_TOLERANCE),
@@ -469,21 +468,21 @@ public void testTetrahedron() {
         // assert
         Assert.assertEquals(1.0 / 3.0, tree.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(2.0 * Math.sqrt(3.0), tree.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian3D(1.5, 2.5, 3.5), (Cartesian3D) tree.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point3D(1.5, 2.5, 3.5), tree.getBarycenter(), TEST_TOLERANCE);
 
         double third = 1.0 / 3.0;
-        checkPoints(Region.Location.BOUNDARY, tree, new Cartesian3D[] {
+        checkPoints(Region.Location.BOUNDARY, tree, new Point3D[] {
             vertex1, vertex2, vertex3, vertex4,
-            new Cartesian3D(third, vertex1, third, vertex2, third, vertex3),
-            new Cartesian3D(third, vertex2, third, vertex3, third, vertex4),
-            new Cartesian3D(third, vertex3, third, vertex4, third, vertex1),
-            new Cartesian3D(third, vertex4, third, vertex1, third, vertex2)
+            Point3D.vectorCombination(third, vertex1, third, vertex2, third, vertex3),
+            Point3D.vectorCombination(third, vertex2, third, vertex3, third, vertex4),
+            Point3D.vectorCombination(third, vertex3, third, vertex4, third, vertex1),
+            Point3D.vectorCombination(third, vertex4, third, vertex1, third, vertex2)
         });
-        checkPoints(Region.Location.OUTSIDE, tree, new Cartesian3D[] {
-            new Cartesian3D(1, 2, 4),
-            new Cartesian3D(2, 2, 3),
-            new Cartesian3D(2, 3, 4),
-            new Cartesian3D(1, 3, 3)
+        checkPoints(Region.Location.OUTSIDE, tree, new Point3D[] {
+            new Point3D(1, 2, 4),
+            new Point3D(2, 2, 3),
+            new Point3D(2, 3, 4),
+            new Point3D(1, 3, 3)
         });
     }
 
@@ -495,93 +494,93 @@ public void testSphere() {
         double radius = 1.0;
 
         // act
-        PolyhedronsSet polySet = createSphere(new Cartesian3D(1, 2, 3), radius, 8, 16);
+        PolyhedronsSet polySet = createSphere(new Point3D(1, 2, 3), radius, 8, 16);
 
         // assert
         Assert.assertEquals(sphereVolume(radius), polySet.getSize(), approximationTolerance);
         Assert.assertEquals(sphereSurface(radius), polySet.getBoundarySize(), approximationTolerance);
-        EuclideanTestUtils.assertVectorEquals(new Cartesian3D(1, 2, 3), (Cartesian3D) polySet.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point3D(1, 2, 3), polySet.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(polySet.isEmpty());
         Assert.assertFalse(polySet.isFull());
 
         checkPoints(Region.Location.OUTSIDE, polySet,
-                new Cartesian3D(-0.1, 2, 3),
-                new Cartesian3D(2.1, 2, 3),
-                new Cartesian3D(1, 0.9, 3),
-                new Cartesian3D(1, 3.1, 3),
-                new Cartesian3D(1, 2, 1.9),
-                new Cartesian3D(1, 2, 4.1),
-                new Cartesian3D(1.6, 2.6, 3.6));
+                new Point3D(-0.1, 2, 3),
+                new Point3D(2.1, 2, 3),
+                new Point3D(1, 0.9, 3),
+                new Point3D(1, 3.1, 3),
+                new Point3D(1, 2, 1.9),
+                new Point3D(1, 2, 4.1),
+                new Point3D(1.6, 2.6, 3.6));
 
         checkPoints(Region.Location.INSIDE, polySet,
-                new Cartesian3D(1, 2, 3),
-                new Cartesian3D(0.1, 2, 3),
-                new Cartesian3D(1.9, 2, 3),
-                new Cartesian3D(1, 2.1, 3),
-                new Cartesian3D(1, 2.9, 3),
-                new Cartesian3D(1, 2, 2.1),
-                new Cartesian3D(1, 2, 3.9),
-                new Cartesian3D(1.5, 2.5, 3.5));
+                new Point3D(1, 2, 3),
+                new Point3D(0.1, 2, 3),
+                new Point3D(1.9, 2, 3),
+                new Point3D(1, 2.1, 3),
+                new Point3D(1, 2.9, 3),
+                new Point3D(1, 2, 2.1),
+                new Point3D(1, 2, 3.9),
+                new Point3D(1.5, 2.5, 3.5));
     }
 
     @Test
     public void testIsometry() {
         // arrange
-        Cartesian3D vertex1 = new Cartesian3D(1.1, 2.2, 3.3);
-        Cartesian3D vertex2 = new Cartesian3D(2.0, 2.4, 4.2);
-        Cartesian3D vertex3 = new Cartesian3D(2.8, 3.3, 3.7);
-        Cartesian3D vertex4 = new Cartesian3D(1.0, 3.6, 4.5);
+        Point3D vertex1 = new Point3D(1.1, 2.2, 3.3);
+        Point3D vertex2 = new Point3D(2.0, 2.4, 4.2);
+        Point3D vertex3 = new Point3D(2.8, 3.3, 3.7);
+        Point3D vertex4 = new Point3D(1.0, 3.6, 4.5);
 
         // act
         PolyhedronsSet tree =
-            (PolyhedronsSet) new RegionFactory<Euclidean3D>().buildConvex(
+            (PolyhedronsSet) new RegionFactory<Point3D>().buildConvex(
                 new Plane(vertex3, vertex2, vertex1, TEST_TOLERANCE),
                 new Plane(vertex2, vertex3, vertex4, TEST_TOLERANCE),
                 new Plane(vertex4, vertex3, vertex1, TEST_TOLERANCE),
                 new Plane(vertex1, vertex2, vertex4, TEST_TOLERANCE));
 
         // assert
-        Cartesian3D barycenter = (Cartesian3D) tree.getBarycenter();
-        Cartesian3D s = new Cartesian3D(10.2, 4.3, -6.7);
-        Cartesian3D c = new Cartesian3D(-0.2, 2.1, -3.2);
-        Rotation r = new Rotation(new Cartesian3D(6.2, -4.4, 2.1), 0.12, RotationConvention.VECTOR_OPERATOR);
+        Point3D barycenter = tree.getBarycenter();
+        Vector3D s = new Vector3D(10.2, 4.3, -6.7);
+        Point3D c = new Point3D(-0.2, 2.1, -3.2);
+        Rotation r = new Rotation(new Vector3D(6.2, -4.4, 2.1), 0.12, RotationConvention.VECTOR_OPERATOR);
 
         tree = tree.rotate(c, r).translate(s);
 
-        Cartesian3D newB =
-            new Cartesian3D(1.0, s,
+        Point3D newB =
+                Point3D.vectorCombination(1.0, s,
                          1.0, c,
                          1.0, r.applyTo(barycenter.subtract(c)));
         Assert.assertEquals(0.0,
-                            newB.subtract((Cartesian3D) tree.getBarycenter()).getNorm(),
+                            newB.subtract(tree.getBarycenter()).getNorm(),
                             TEST_TOLERANCE);
 
-        final Cartesian3D[] expectedV = new Cartesian3D[] {
-            new Cartesian3D(1.0, s,
+        final Point3D[] expectedV = new Point3D[] {
+                Point3D.vectorCombination(1.0, s,
                          1.0, c,
                          1.0, r.applyTo(vertex1.subtract(c))),
-                         new Cartesian3D(1.0, s,
+                            Point3D.vectorCombination(1.0, s,
                                       1.0, c,
                                       1.0, r.applyTo(vertex2.subtract(c))),
-                                      new Cartesian3D(1.0, s,
+                                        Point3D.vectorCombination(1.0, s,
                                                    1.0, c,
                                                    1.0, r.applyTo(vertex3.subtract(c))),
-                                                   new Cartesian3D(1.0, s,
+                                                    Point3D.vectorCombination(1.0, s,
                                                                 1.0, c,
                                                                 1.0, r.applyTo(vertex4.subtract(c)))
         };
-        tree.getTree(true).visit(new BSPTreeVisitor<Euclidean3D>() {
+        tree.getTree(true).visit(new BSPTreeVisitor<Point3D>() {
 
             @Override
-            public Order visitOrder(BSPTree<Euclidean3D> node) {
+            public Order visitOrder(BSPTree<Point3D> node) {
                 return Order.MINUS_SUB_PLUS;
             }
 
             @Override
-            public void visitInternalNode(BSPTree<Euclidean3D> node) {
+            public void visitInternalNode(BSPTree<Point3D> node) {
                 @SuppressWarnings("unchecked")
-                BoundaryAttribute<Euclidean3D> attribute =
-                    (BoundaryAttribute<Euclidean3D>) node.getAttribute();
+                BoundaryAttribute<Point3D> attribute =
+                    (BoundaryAttribute<Point3D>) node.getAttribute();
                 if (attribute.getPlusOutside() != null) {
                     checkFacet((SubPlane) attribute.getPlusOutside());
                 }
@@ -591,16 +590,16 @@ public void visitInternalNode(BSPTree<Euclidean3D> node) {
             }
 
             @Override
-            public void visitLeafNode(BSPTree<Euclidean3D> node) {
+            public void visitLeafNode(BSPTree<Point3D> node) {
             }
 
             private void checkFacet(SubPlane facet) {
                 Plane plane = (Plane) facet.getHyperplane();
-                Cartesian2D[][] vertices =
+                Point2D[][] vertices =
                     ((PolygonsSet) facet.getRemainingRegion()).getVertices();
                 Assert.assertEquals(1, vertices.length);
                 for (int i = 0; i < vertices[0].length; ++i) {
-                    Cartesian3D v = plane.toSpace(vertices[0][i]);
+                    Point3D v = plane.toSpace(vertices[0][i]);
                     double d = Double.POSITIVE_INFINITY;
                     for (int k = 0; k < expectedV.length; ++k) {
                         d = Math.min(d, v.subtract(expectedV[k]).getNorm());
@@ -627,7 +626,7 @@ public void testBuildBox() {
             new PolyhedronsSet(x - l, x + l, y - w, y + w, z - w, z + w, TEST_TOLERANCE);
 
         // assert
-        EuclideanTestUtils.assertVectorEquals(new Cartesian3D(x, y, z), (Cartesian3D) tree.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point3D(x, y, z), tree.getBarycenter(), TEST_TOLERANCE);
         Assert.assertEquals(8 * l * w * w, tree.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(8 * w * (2 * l + w), tree.getBoundarySize(), TEST_TOLERANCE);
     }
@@ -646,13 +645,13 @@ public void testCross() {
             new PolyhedronsSet(x - w, x + w, y - l, y + l, z - w, z + w, TEST_TOLERANCE);
         PolyhedronsSet zBeam =
             new PolyhedronsSet(x - w, x + w, y - w, y + w, z - l, z + l, TEST_TOLERANCE);
-        RegionFactory<Euclidean3D> factory = new RegionFactory<>();
+        RegionFactory<Point3D> factory = new RegionFactory<>();
 
         // act
         PolyhedronsSet tree = (PolyhedronsSet) factory.union(xBeam, factory.union(yBeam, zBeam));
 
         // assert
-        EuclideanTestUtils.assertVectorEquals(new Cartesian3D(x, y, z), (Cartesian3D) tree.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point3D(x, y, z), tree.getBarycenter(), TEST_TOLERANCE);
         Assert.assertEquals(8 * w * w * (3 * l - 2 * w), tree.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(24 * w * (2 * l - w), tree.getBoundarySize(), TEST_TOLERANCE);
     }
@@ -678,19 +677,19 @@ public void testCreateFromBoundaries_handlesSmallBoundariesCreatedDuringConstruc
             1, 5, 6, 1, 6, 2,
             2, 6, 7, 2, 7, 3,
             4, 0, 3, 4, 3, 7};
-        ArrayList<SubHyperplane<Euclidean3D>> subHyperplaneList = new ArrayList<>();
+        ArrayList<SubHyperplane<Point3D>> subHyperplaneList = new ArrayList<>();
         for (int idx = 0; idx < indices.length; idx += 3) {
             int idxA = indices[idx] * 3;
             int idxB = indices[idx + 1] * 3;
             int idxC = indices[idx + 2] * 3;
-            Cartesian3D v_1 = new Cartesian3D(coords[idxA], coords[idxA + 1], coords[idxA + 2]);
-            Cartesian3D v_2 = new Cartesian3D(coords[idxB], coords[idxB + 1], coords[idxB + 2]);
-            Cartesian3D v_3 = new Cartesian3D(coords[idxC], coords[idxC + 1], coords[idxC + 2]);
-            Cartesian3D[] vertices = {v_1, v_2, v_3};
+            Point3D v_1 = new Point3D(coords[idxA], coords[idxA + 1], coords[idxA + 2]);
+            Point3D v_2 = new Point3D(coords[idxB], coords[idxB + 1], coords[idxB + 2]);
+            Point3D v_3 = new Point3D(coords[idxC], coords[idxC + 1], coords[idxC + 2]);
+            Point3D[] vertices = {v_1, v_2, v_3};
             Plane polyPlane = new Plane(v_1, v_2, v_3, TEST_TOLERANCE);
-            ArrayList<SubHyperplane<Euclidean2D>> lines = new ArrayList<>();
+            ArrayList<SubHyperplane<Point2D>> lines = new ArrayList<>();
 
-            Cartesian2D[] projPts = new Cartesian2D[vertices.length];
+            Point2D[] projPts = new Point2D[vertices.length];
             for (int ptIdx = 0; ptIdx < projPts.length; ptIdx++) {
                 projPts[ptIdx] = polyPlane.toSubSpace(vertices[ptIdx]);
             }
@@ -700,7 +699,7 @@ public void testCreateFromBoundaries_handlesSmallBoundariesCreatedDuringConstruc
                 lineInPlane = new SubLine(projPts[ptIdx], projPts[(ptIdx + 1) % projPts.length], TEST_TOLERANCE);
                 lines.add(lineInPlane);
             }
-            Region<Euclidean2D> polyRegion = new PolygonsSet(lines, TEST_TOLERANCE);
+            Region<Point2D> polyRegion = new PolygonsSet(lines, TEST_TOLERANCE);
             SubPlane polygon = new SubPlane(polyPlane, polyRegion);
             subHyperplaneList.add(polygon);
         }
@@ -727,10 +726,10 @@ public void testWrongUsage() {
         // the following is a wrong usage of the constructor.
         // as explained in the javadoc, the failure is NOT detected at construction
         // time but occurs later on
-        PolyhedronsSet ps = new PolyhedronsSet(new BSPTree<Euclidean3D>(), TEST_TOLERANCE);
+        PolyhedronsSet ps = new PolyhedronsSet(new BSPTree<Point3D>(), TEST_TOLERANCE);
         Assert.assertNotNull(ps);
         try {
-            ps.checkPoint(Cartesian3D.ZERO);
+            ps.checkPoint(Point3D.ZERO);
             Assert.fail("an exception should have been thrown");
         } catch (NullPointerException npe) {
             // this is expected
@@ -742,18 +741,18 @@ public void testDumpParse() throws IOException, ParseException {
         // arrange
         double tol=1e-8;
 
-        Cartesian3D[] verts=new Cartesian3D[8];
+        Point3D[] verts=new Point3D[8];
         double xmin=-1,xmax=1;
         double ymin=-1,ymax=1;
         double zmin=-1,zmax=1;
-        verts[0]=new Cartesian3D(xmin,ymin,zmin);
-        verts[1]=new Cartesian3D(xmax,ymin,zmin);
-        verts[2]=new Cartesian3D(xmax,ymax,zmin);
-        verts[3]=new Cartesian3D(xmin,ymax,zmin);
-        verts[4]=new Cartesian3D(xmin,ymin,zmax);
-        verts[5]=new Cartesian3D(xmax,ymin,zmax);
-        verts[6]=new Cartesian3D(xmax,ymax,zmax);
-        verts[7]=new Cartesian3D(xmin,ymax,zmax);
+        verts[0]=new Point3D(xmin,ymin,zmin);
+        verts[1]=new Point3D(xmax,ymin,zmin);
+        verts[2]=new Point3D(xmax,ymax,zmin);
+        verts[3]=new Point3D(xmin,ymax,zmin);
+        verts[4]=new Point3D(xmin,ymin,zmax);
+        verts[5]=new Point3D(xmax,ymin,zmax);
+        verts[6]=new Point3D(xmax,ymax,zmax);
+        verts[7]=new Point3D(xmin,ymax,zmax);
         //
         int[][] faces=new int[12][];
         faces[0]=new int[]{3,1,0};  // bottom (-z)
@@ -781,7 +780,7 @@ public void testDumpParse() throws IOException, ParseException {
 
         Assert.assertEquals(8.0, parsed.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(24.0, parsed.getBoundarySize(), TEST_TOLERANCE);
-        Assert.assertTrue(new RegionFactory<Euclidean3D>().difference(polyset, parsed).isEmpty());
+        Assert.assertTrue(new RegionFactory<Point3D>().difference(polyset, parsed).isEmpty());
     }
 
     @Test
@@ -816,7 +815,7 @@ public void testCreateFromBRep_badOrientation() throws IOException, ParseExcepti
 
     @Test
     public void testCreateFromBRep_wrongNumberOfPoints() throws IOException, ParseException {
-        checkError(Arrays.asList(Cartesian3D.ZERO, Cartesian3D.PLUS_I, Cartesian3D.PLUS_J, Cartesian3D.PLUS_K),
+        checkError(Arrays.asList(Point3D.ZERO, new Point3D(1, 0, 0), new Point3D(0, 1, 0), new Point3D(0, 0, 1)),
                    Arrays.asList(new int[] { 0, 1, 2 }, new int[] {2, 3}),
                    "");
     }
@@ -832,7 +831,7 @@ private void checkError(final String resourceName, final String expected) {
         }
     }
 
-    private void checkError(final List<Cartesian3D> vertices, final List<int[]> facets,
+    private void checkError(final List<Point3D> vertices, final List<int[]> facets,
                             final String expected) {
         try {
             new PolyhedronsSet(vertices, facets, TEST_TOLERANCE);
@@ -847,48 +846,48 @@ private void checkError(final List<Cartesian3D> vertices, final List<int[]> face
     @Test
     public void testFirstIntersection() {
         // arrange
-        List<SubHyperplane<Euclidean3D>> boundaries = createBoxBoundaries(Cartesian3D.ZERO, 2.0, TEST_TOLERANCE);
+        List<SubHyperplane<Point3D>> boundaries = createBoxBoundaries(Point3D.ZERO, 2.0, TEST_TOLERANCE);
         PolyhedronsSet polySet = new PolyhedronsSet(boundaries, TEST_TOLERANCE);
 
-        Line xPlus = new Line(Cartesian3D.ZERO, Cartesian3D.PLUS_I, TEST_TOLERANCE);
-        Line xMinus = new Line(Cartesian3D.ZERO, Cartesian3D.MINUS_I, TEST_TOLERANCE);
+        Line xPlus = new Line(Point3D.ZERO, new Point3D(1, 0, 0), TEST_TOLERANCE);
+        Line xMinus = new Line(Point3D.ZERO, new Point3D(-1, 0, 0), TEST_TOLERANCE);
 
-        Line yPlus = new Line(Cartesian3D.ZERO, Cartesian3D.PLUS_J, TEST_TOLERANCE);
-        Line yMinus = new Line(Cartesian3D.ZERO, Cartesian3D.MINUS_J, TEST_TOLERANCE);
+        Line yPlus = new Line(Point3D.ZERO, new Point3D(0, 1, 0), TEST_TOLERANCE);
+        Line yMinus = new Line(Point3D.ZERO, new Point3D(0, -1, 0), TEST_TOLERANCE);
 
-        Line zPlus = new Line(Cartesian3D.ZERO, Cartesian3D.PLUS_K, TEST_TOLERANCE);
-        Line zMinus = new Line(Cartesian3D.ZERO, Cartesian3D.MINUS_K, TEST_TOLERANCE);
+        Line zPlus = new Line(Point3D.ZERO, new Point3D(0, 0, 1), TEST_TOLERANCE);
+        Line zMinus = new Line(Point3D.ZERO, new Point3D(0, 0, -1), TEST_TOLERANCE);
 
         // act/assert
-        assertSubPlaneNormal(new Cartesian3D(-1, 0, 0), polySet.firstIntersection(new Cartesian3D(-1.1, 0, 0), xPlus));
-        assertSubPlaneNormal(new Cartesian3D(-1, 0, 0), polySet.firstIntersection(new Cartesian3D(-1, 0, 0), xPlus));
-        assertSubPlaneNormal(new Cartesian3D(1, 0, 0), polySet.firstIntersection(new Cartesian3D(-0.9, 0, 0), xPlus));
-        Assert.assertEquals(null, polySet.firstIntersection(new Cartesian3D(1.1, 0, 0), xPlus));
-
-        assertSubPlaneNormal(new Cartesian3D(1, 0, 0), polySet.firstIntersection(new Cartesian3D(1.1, 0, 0), xMinus));
-        assertSubPlaneNormal(new Cartesian3D(1, 0, 0), polySet.firstIntersection(new Cartesian3D(1, 0, 0), xMinus));
-        assertSubPlaneNormal(new Cartesian3D(-1, 0, 0), polySet.firstIntersection(new Cartesian3D(0.9, 0, 0), xMinus));
-        Assert.assertEquals(null, polySet.firstIntersection(new Cartesian3D(-1.1, 0, 0), xMinus));
-
-        assertSubPlaneNormal(new Cartesian3D(0, -1, 0), polySet.firstIntersection(new Cartesian3D(0, -1.1, 0), yPlus));
-        assertSubPlaneNormal(new Cartesian3D(0, -1, 0), polySet.firstIntersection(new Cartesian3D(0, -1, 0), yPlus));
-        assertSubPlaneNormal(new Cartesian3D(0, 1, 0), polySet.firstIntersection(new Cartesian3D(0, -0.9, 0), yPlus));
-        Assert.assertEquals(null, polySet.firstIntersection(new Cartesian3D(0, 1.1, 0), yPlus));
-
-        assertSubPlaneNormal(new Cartesian3D(0, 1, 0), polySet.firstIntersection(new Cartesian3D(0, 1.1, 0), yMinus));
-        assertSubPlaneNormal(new Cartesian3D(0, 1, 0), polySet.firstIntersection(new Cartesian3D(0, 1, 0), yMinus));
-        assertSubPlaneNormal(new Cartesian3D(0, -1, 0), polySet.firstIntersection(new Cartesian3D(0, 0.9, 0), yMinus));
-        Assert.assertEquals(null, polySet.firstIntersection(new Cartesian3D(0, -1.1, 0), yMinus));
-
-        assertSubPlaneNormal(new Cartesian3D(0, 0, -1), polySet.firstIntersection(new Cartesian3D(0, 0, -1.1), zPlus));
-        assertSubPlaneNormal(new Cartesian3D(0, 0, -1), polySet.firstIntersection(new Cartesian3D(0, 0, -1), zPlus));
-        assertSubPlaneNormal(new Cartesian3D(0, 0, 1), polySet.firstIntersection(new Cartesian3D(0, 0, -0.9), zPlus));
-        Assert.assertEquals(null, polySet.firstIntersection(new Cartesian3D(0, 0, 1.1), zPlus));
-
-        assertSubPlaneNormal(new Cartesian3D(0, 0, 1), polySet.firstIntersection(new Cartesian3D(0, 0, 1.1), zMinus));
-        assertSubPlaneNormal(new Cartesian3D(0, 0, 1), polySet.firstIntersection(new Cartesian3D(0, 0, 1), zMinus));
-        assertSubPlaneNormal(new Cartesian3D(0, 0, -1), polySet.firstIntersection(new Cartesian3D(0, 0, 0.9), zMinus));
-        Assert.assertEquals(null, polySet.firstIntersection(new Cartesian3D(0, 0, -1.1), zMinus));
+        assertSubPlaneNormal(new Vector3D(-1, 0, 0), polySet.firstIntersection(new Point3D(-1.1, 0, 0), xPlus));
+        assertSubPlaneNormal(new Vector3D(-1, 0, 0), polySet.firstIntersection(new Point3D(-1, 0, 0), xPlus));
+        assertSubPlaneNormal(new Vector3D(1, 0, 0), polySet.firstIntersection(new Point3D(-0.9, 0, 0), xPlus));
+        Assert.assertEquals(null, polySet.firstIntersection(new Point3D(1.1, 0, 0), xPlus));
+
+        assertSubPlaneNormal(new Vector3D(1, 0, 0), polySet.firstIntersection(new Point3D(1.1, 0, 0), xMinus));
+        assertSubPlaneNormal(new Vector3D(1, 0, 0), polySet.firstIntersection(new Point3D(1, 0, 0), xMinus));
+        assertSubPlaneNormal(new Vector3D(-1, 0, 0), polySet.firstIntersection(new Point3D(0.9, 0, 0), xMinus));
+        Assert.assertEquals(null, polySet.firstIntersection(new Point3D(-1.1, 0, 0), xMinus));
+
+        assertSubPlaneNormal(new Vector3D(0, -1, 0), polySet.firstIntersection(new Point3D(0, -1.1, 0), yPlus));
+        assertSubPlaneNormal(new Vector3D(0, -1, 0), polySet.firstIntersection(new Point3D(0, -1, 0), yPlus));
+        assertSubPlaneNormal(new Vector3D(0, 1, 0), polySet.firstIntersection(new Point3D(0, -0.9, 0), yPlus));
+        Assert.assertEquals(null, polySet.firstIntersection(new Point3D(0, 1.1, 0), yPlus));
+
+        assertSubPlaneNormal(new Vector3D(0, 1, 0), polySet.firstIntersection(new Point3D(0, 1.1, 0), yMinus));
+        assertSubPlaneNormal(new Vector3D(0, 1, 0), polySet.firstIntersection(new Point3D(0, 1, 0), yMinus));
+        assertSubPlaneNormal(new Vector3D(0, -1, 0), polySet.firstIntersection(new Point3D(0, 0.9, 0), yMinus));
+        Assert.assertEquals(null, polySet.firstIntersection(new Point3D(0, -1.1, 0), yMinus));
+
+        assertSubPlaneNormal(new Vector3D(0, 0, -1), polySet.firstIntersection(new Point3D(0, 0, -1.1), zPlus));
+        assertSubPlaneNormal(new Vector3D(0, 0, -1), polySet.firstIntersection(new Point3D(0, 0, -1), zPlus));
+        assertSubPlaneNormal(new Vector3D(0, 0, 1), polySet.firstIntersection(new Point3D(0, 0, -0.9), zPlus));
+        Assert.assertEquals(null, polySet.firstIntersection(new Point3D(0, 0, 1.1), zPlus));
+
+        assertSubPlaneNormal(new Vector3D(0, 0, 1), polySet.firstIntersection(new Point3D(0, 0, 1.1), zMinus));
+        assertSubPlaneNormal(new Vector3D(0, 0, 1), polySet.firstIntersection(new Point3D(0, 0, 1), zMinus));
+        assertSubPlaneNormal(new Vector3D(0, 0, -1), polySet.firstIntersection(new Point3D(0, 0, 0.9), zMinus));
+        Assert.assertEquals(null, polySet.firstIntersection(new Point3D(0, 0, -1.1), zMinus));
     }
 
     // Issue 1211
@@ -902,14 +901,14 @@ public void testFirstIntersection_onlyReturnsPointsInDirectionOfRay() throws IOE
         // act/assert
         int nrays = 1000;
         for (int i = 0; i < nrays; i++) {
-            Cartesian3D origin    = Cartesian3D.ZERO;
-            Cartesian3D direction = new Cartesian3D(2 * random.nextDouble() - 1,
+            Point3D origin    = Point3D.ZERO;
+            Vector3D direction = new Vector3D(2 * random.nextDouble() - 1,
                                               2 * random.nextDouble() - 1,
                                               2 * random.nextDouble() - 1).normalize();
             Line line = new Line(origin, origin.add(direction), polyset.getTolerance());
-            SubHyperplane<Euclidean3D> plane = polyset.firstIntersection(origin, line);
+            SubHyperplane<Point3D> plane = polyset.firstIntersection(origin, line);
             if (plane != null) {
-                Cartesian3D intersectionPoint = ((Plane)plane.getHyperplane()).intersection(line);
+                Point3D intersectionPoint = ((Plane)plane.getHyperplane()).intersection(line);
                 double dotProduct = direction.dotProduct(intersectionPoint.subtract(origin));
                 Assert.assertTrue(dotProduct > 0);
             }
@@ -923,10 +922,10 @@ public void testBoolean_union() throws IOException {
         double size = 1.0;
         double radius = size * 0.5;
         PolyhedronsSet box = new PolyhedronsSet(0, size, 0, size, 0, size, TEST_TOLERANCE);
-        PolyhedronsSet sphere = createSphere(new Cartesian3D(size * 0.5, size * 0.5, size), radius, 8, 16);
+        PolyhedronsSet sphere = createSphere(new Point3D(size * 0.5, size * 0.5, size), radius, 8, 16);
 
         // act
-        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Euclidean3D>().union(box, sphere);
+        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Point3D>().union(box, sphere);
 
         // OBJWriter.write("union.obj", result);
 
@@ -939,20 +938,20 @@ public void testBoolean_union() throws IOException {
         Assert.assertFalse(result.isFull());
 
         checkPoints(Region.Location.OUTSIDE, result,
-                new Cartesian3D(-0.1, 0.5, 0.5),
-                new Cartesian3D(1.1, 0.5, 0.5),
-                new Cartesian3D(0.5, -0.1, 0.5),
-                new Cartesian3D(0.5, 1.1, 0.5),
-                new Cartesian3D(0.5, 0.5, -0.1),
-                new Cartesian3D(0.5, 0.5, 1.6));
+                new Point3D(-0.1, 0.5, 0.5),
+                new Point3D(1.1, 0.5, 0.5),
+                new Point3D(0.5, -0.1, 0.5),
+                new Point3D(0.5, 1.1, 0.5),
+                new Point3D(0.5, 0.5, -0.1),
+                new Point3D(0.5, 0.5, 1.6));
 
         checkPoints(Region.Location.INSIDE, result,
-                new Cartesian3D(0.1, 0.5, 0.5),
-                new Cartesian3D(0.9, 0.5, 0.5),
-                new Cartesian3D(0.5, 0.1, 0.5),
-                new Cartesian3D(0.5, 0.9, 0.5),
-                new Cartesian3D(0.5, 0.5, 0.1),
-                new Cartesian3D(0.5, 0.5, 1.4));
+                new Point3D(0.1, 0.5, 0.5),
+                new Point3D(0.9, 0.5, 0.5),
+                new Point3D(0.5, 0.1, 0.5),
+                new Point3D(0.5, 0.9, 0.5),
+                new Point3D(0.5, 0.5, 0.1),
+                new Point3D(0.5, 0.5, 1.4));
     }
 
     @Test
@@ -961,34 +960,34 @@ public void testUnion_self() {
         double tolerance = 0.2;
         double radius = 1.0;
 
-        PolyhedronsSet sphere = createSphere(Cartesian3D.ZERO, radius, 8, 16);
+        PolyhedronsSet sphere = createSphere(Point3D.ZERO, radius, 8, 16);
 
         // act
-        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Euclidean3D>().union(sphere, sphere.copySelf());
+        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Point3D>().union(sphere, sphere.copySelf());
 
         // assert
         Assert.assertEquals(sphereVolume(radius), result.getSize(), tolerance);
         Assert.assertEquals(sphereSurface(radius), result.getBoundarySize(), tolerance);
-        EuclideanTestUtils.assertVectorEquals(Cartesian3D.ZERO, (Cartesian3D) result.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point3D.ZERO, result.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(result.isEmpty());
         Assert.assertFalse(result.isFull());
 
         checkPoints(Region.Location.OUTSIDE, result,
-                new Cartesian3D(-1.1, 0, 0),
-                new Cartesian3D(1.1, 0, 0),
-                new Cartesian3D(0, -1.1, 0),
-                new Cartesian3D(0, 1.1, 0),
-                new Cartesian3D(0, 0, -1.1),
-                new Cartesian3D(0, 0, 1.1));
+                new Point3D(-1.1, 0, 0),
+                new Point3D(1.1, 0, 0),
+                new Point3D(0, -1.1, 0),
+                new Point3D(0, 1.1, 0),
+                new Point3D(0, 0, -1.1),
+                new Point3D(0, 0, 1.1));
 
         checkPoints(Region.Location.INSIDE, result,
-                new Cartesian3D(-0.9, 0, 0),
-                new Cartesian3D(0.9, 0, 0),
-                new Cartesian3D(0, -0.9, 0),
-                new Cartesian3D(0, 0.9, 0),
-                new Cartesian3D(0, 0, -0.9),
-                new Cartesian3D(0, 0, 0.9),
-                Cartesian3D.ZERO);
+                new Point3D(-0.9, 0, 0),
+                new Point3D(0.9, 0, 0),
+                new Point3D(0, -0.9, 0),
+                new Point3D(0, 0.9, 0),
+                new Point3D(0, 0, -0.9),
+                new Point3D(0, 0, 0.9),
+                Point3D.ZERO);
     }
 
     @Test
@@ -998,10 +997,10 @@ public void testBoolean_intersection() throws IOException {
         double size = 1.0;
         double radius = size * 0.5;
         PolyhedronsSet box = new PolyhedronsSet(0, size, 0, size, 0, size, TEST_TOLERANCE);
-        PolyhedronsSet sphere = createSphere(new Cartesian3D(size * 0.5, size * 0.5, size), radius, 8, 16);
+        PolyhedronsSet sphere = createSphere(new Point3D(size * 0.5, size * 0.5, size), radius, 8, 16);
 
         // act
-        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Euclidean3D>().intersection(box, sphere);
+        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Point3D>().intersection(box, sphere);
 
         // OBJWriter.write("intersection.obj", result);
 
@@ -1013,20 +1012,20 @@ public void testBoolean_intersection() throws IOException {
         Assert.assertFalse(result.isFull());
 
         checkPoints(Region.Location.OUTSIDE, result,
-                new Cartesian3D(-0.1, 0.5, 1.0),
-                new Cartesian3D(1.1, 0.5, 1.0),
-                new Cartesian3D(0.5, -0.1, 1.0),
-                new Cartesian3D(0.5, 1.1, 1.0),
-                new Cartesian3D(0.5, 0.5, 0.4),
-                new Cartesian3D(0.5, 0.5, 1.1));
+                new Point3D(-0.1, 0.5, 1.0),
+                new Point3D(1.1, 0.5, 1.0),
+                new Point3D(0.5, -0.1, 1.0),
+                new Point3D(0.5, 1.1, 1.0),
+                new Point3D(0.5, 0.5, 0.4),
+                new Point3D(0.5, 0.5, 1.1));
 
         checkPoints(Region.Location.INSIDE, result,
-                new Cartesian3D(0.1, 0.5, 0.9),
-                new Cartesian3D(0.9, 0.5, 0.9),
-                new Cartesian3D(0.5, 0.1, 0.9),
-                new Cartesian3D(0.5, 0.9, 0.9),
-                new Cartesian3D(0.5, 0.5, 0.6),
-                new Cartesian3D(0.5, 0.5, 0.9));
+                new Point3D(0.1, 0.5, 0.9),
+                new Point3D(0.9, 0.5, 0.9),
+                new Point3D(0.5, 0.1, 0.9),
+                new Point3D(0.5, 0.9, 0.9),
+                new Point3D(0.5, 0.5, 0.6),
+                new Point3D(0.5, 0.5, 0.9));
     }
 
     @Test
@@ -1035,34 +1034,34 @@ public void testIntersection_self() {
         double tolerance = 0.2;
         double radius = 1.0;
 
-        PolyhedronsSet sphere = createSphere(Cartesian3D.ZERO, radius, 8, 16);
+        PolyhedronsSet sphere = createSphere(Point3D.ZERO, radius, 8, 16);
 
         // act
-        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Euclidean3D>().intersection(sphere, sphere.copySelf());
+        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Point3D>().intersection(sphere, sphere.copySelf());
 
         // assert
         Assert.assertEquals(sphereVolume(radius), result.getSize(), tolerance);
         Assert.assertEquals(sphereSurface(radius), result.getBoundarySize(), tolerance);
-        EuclideanTestUtils.assertVectorEquals(Cartesian3D.ZERO, (Cartesian3D) result.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point3D.ZERO, result.getBarycenter(), TEST_TOLERANCE);
         Assert.assertFalse(result.isEmpty());
         Assert.assertFalse(result.isFull());
 
         checkPoints(Region.Location.OUTSIDE, result,
-                new Cartesian3D(-1.1, 0, 0),
-                new Cartesian3D(1.1, 0, 0),
-                new Cartesian3D(0, -1.1, 0),
-                new Cartesian3D(0, 1.1, 0),
-                new Cartesian3D(0, 0, -1.1),
-                new Cartesian3D(0, 0, 1.1));
+                new Point3D(-1.1, 0, 0),
+                new Point3D(1.1, 0, 0),
+                new Point3D(0, -1.1, 0),
+                new Point3D(0, 1.1, 0),
+                new Point3D(0, 0, -1.1),
+                new Point3D(0, 0, 1.1));
 
         checkPoints(Region.Location.INSIDE, result,
-                new Cartesian3D(-0.9, 0, 0),
-                new Cartesian3D(0.9, 0, 0),
-                new Cartesian3D(0, -0.9, 0),
-                new Cartesian3D(0, 0.9, 0),
-                new Cartesian3D(0, 0, -0.9),
-                new Cartesian3D(0, 0, 0.9),
-                Cartesian3D.ZERO);
+                new Point3D(-0.9, 0, 0),
+                new Point3D(0.9, 0, 0),
+                new Point3D(0, -0.9, 0),
+                new Point3D(0, 0.9, 0),
+                new Point3D(0, 0, -0.9),
+                new Point3D(0, 0, 0.9),
+                Point3D.ZERO);
     }
 
     @Test
@@ -1079,7 +1078,7 @@ public void testBoolean_xor_twoCubes() throws IOException {
                 0.5, size + 0.5, TEST_TOLERANCE);
 
         // act
-        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Euclidean3D>().xor(box1, box2);
+        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Point3D>().xor(box1, box2);
 
         // OBJWriter.write("xor_twoCubes.obj", result);
 
@@ -1091,21 +1090,21 @@ public void testBoolean_xor_twoCubes() throws IOException {
         Assert.assertFalse(result.isFull());
 
         checkPoints(Region.Location.OUTSIDE, result,
-                new Cartesian3D(-0.1, -0.1, -0.1),
-                new Cartesian3D(0.75, 0.75, 0.75),
-                new Cartesian3D(1.6, 1.6, 1.6));
+                new Point3D(-0.1, -0.1, -0.1),
+                new Point3D(0.75, 0.75, 0.75),
+                new Point3D(1.6, 1.6, 1.6));
 
         checkPoints(Region.Location.BOUNDARY, result,
-                new Cartesian3D(0, 0, 0),
-                new Cartesian3D(0.5, 0.5, 0.5),
-                new Cartesian3D(1, 1, 1),
-                new Cartesian3D(1.5, 1.5, 1.5));
+                new Point3D(0, 0, 0),
+                new Point3D(0.5, 0.5, 0.5),
+                new Point3D(1, 1, 1),
+                new Point3D(1.5, 1.5, 1.5));
 
         checkPoints(Region.Location.INSIDE, result,
-                new Cartesian3D(0.1, 0.1, 0.1),
-                new Cartesian3D(0.4, 0.4, 0.4),
-                new Cartesian3D(1.1, 1.1, 1.1),
-                new Cartesian3D(1.4, 1.4, 1.4));
+                new Point3D(0.1, 0.1, 0.1),
+                new Point3D(0.4, 0.4, 0.4),
+                new Point3D(1.1, 1.1, 1.1),
+                new Point3D(1.4, 1.4, 1.4));
     }
 
     @Test
@@ -1115,10 +1114,10 @@ public void testBoolean_xor_cubeAndSphere() throws IOException {
         double size = 1.0;
         double radius = size * 0.5;
         PolyhedronsSet box = new PolyhedronsSet(0, size, 0, size, 0, size, TEST_TOLERANCE);
-        PolyhedronsSet sphere = createSphere(new Cartesian3D(size * 0.5, size * 0.5, size), radius, 8, 16);
+        PolyhedronsSet sphere = createSphere(new Point3D(size * 0.5, size * 0.5, size), radius, 8, 16);
 
         // act
-        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Euclidean3D>().xor(box, sphere);
+        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Point3D>().xor(box, sphere);
 
         // OBJWriter.write("xor_cubeAndSphere.obj", result);
 
@@ -1131,21 +1130,21 @@ public void testBoolean_xor_cubeAndSphere() throws IOException {
         Assert.assertFalse(result.isFull());
 
         checkPoints(Region.Location.OUTSIDE, result,
-                new Cartesian3D(-0.1, 0.5, 0.5),
-                new Cartesian3D(1.1, 0.5, 0.5),
-                new Cartesian3D(0.5, -0.1, 0.5),
-                new Cartesian3D(0.5, 1.1, 0.5),
-                new Cartesian3D(0.5, 0.5, -0.1),
-                new Cartesian3D(0.5, 0.5, 1.6),
-                new Cartesian3D(0.5, 0.5, 0.9));
+                new Point3D(-0.1, 0.5, 0.5),
+                new Point3D(1.1, 0.5, 0.5),
+                new Point3D(0.5, -0.1, 0.5),
+                new Point3D(0.5, 1.1, 0.5),
+                new Point3D(0.5, 0.5, -0.1),
+                new Point3D(0.5, 0.5, 1.6),
+                new Point3D(0.5, 0.5, 0.9));
 
         checkPoints(Region.Location.INSIDE, result,
-                new Cartesian3D(0.1, 0.5, 0.5),
-                new Cartesian3D(0.9, 0.5, 0.5),
-                new Cartesian3D(0.5, 0.1, 0.5),
-                new Cartesian3D(0.5, 0.9, 0.5),
-                new Cartesian3D(0.5, 0.5, 0.1),
-                new Cartesian3D(0.5, 0.5, 1.4));
+                new Point3D(0.1, 0.5, 0.5),
+                new Point3D(0.9, 0.5, 0.5),
+                new Point3D(0.5, 0.1, 0.5),
+                new Point3D(0.5, 0.9, 0.5),
+                new Point3D(0.5, 0.5, 0.1),
+                new Point3D(0.5, 0.5, 1.4));
     }
 
     @Test
@@ -1153,32 +1152,32 @@ public void testXor_self() {
         // arrange
         double radius = 1.0;
 
-        PolyhedronsSet sphere = createSphere(Cartesian3D.ZERO, radius, 8, 16);
+        PolyhedronsSet sphere = createSphere(Point3D.ZERO, radius, 8, 16);
 
         // act
-        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Euclidean3D>().xor(sphere, sphere.copySelf());
+        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Point3D>().xor(sphere, sphere.copySelf());
 
         // assert
         Assert.assertEquals(0.0, result.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(0.0, result.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian3D.NaN, (Cartesian3D) result.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point3D.NaN, result.getBarycenter(), TEST_TOLERANCE);
         Assert.assertTrue(result.isEmpty());
         Assert.assertFalse(result.isFull());
 
         checkPoints(Region.Location.OUTSIDE, result,
-                new Cartesian3D(-1.1, 0, 0),
-                new Cartesian3D(1.1, 0, 0),
-                new Cartesian3D(0, -1.1, 0),
-                new Cartesian3D(0, 1.1, 0),
-                new Cartesian3D(0, 0, -1.1),
-                new Cartesian3D(0, 0, 1.1),
-                new Cartesian3D(-0.9, 0, 0),
-                new Cartesian3D(0.9, 0, 0),
-                new Cartesian3D(0, -0.9, 0),
-                new Cartesian3D(0, 0.9, 0),
-                new Cartesian3D(0, 0, -0.9),
-                new Cartesian3D(0, 0, 0.9),
-                Cartesian3D.ZERO);
+                new Point3D(-1.1, 0, 0),
+                new Point3D(1.1, 0, 0),
+                new Point3D(0, -1.1, 0),
+                new Point3D(0, 1.1, 0),
+                new Point3D(0, 0, -1.1),
+                new Point3D(0, 0, 1.1),
+                new Point3D(-0.9, 0, 0),
+                new Point3D(0.9, 0, 0),
+                new Point3D(0, -0.9, 0),
+                new Point3D(0, 0.9, 0),
+                new Point3D(0, 0, -0.9),
+                new Point3D(0, 0, 0.9),
+                Point3D.ZERO);
     }
 
     @Test
@@ -1188,10 +1187,10 @@ public void testBoolean_difference() throws IOException {
         double size = 1.0;
         double radius = size * 0.5;
         PolyhedronsSet box = new PolyhedronsSet(0, size, 0, size, 0, size, TEST_TOLERANCE);
-        PolyhedronsSet sphere = createSphere(new Cartesian3D(size * 0.5, size * 0.5, size), radius, 8, 16);
+        PolyhedronsSet sphere = createSphere(new Point3D(size * 0.5, size * 0.5, size), radius, 8, 16);
 
         // act
-        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Euclidean3D>().difference(box, sphere);
+        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Point3D>().difference(box, sphere);
 
         // OBJWriter.write("difference.obj", result);
 
@@ -1203,20 +1202,20 @@ public void testBoolean_difference() throws IOException {
         Assert.assertFalse(result.isFull());
 
         checkPoints(Region.Location.OUTSIDE, result,
-                new Cartesian3D(-0.1, 0.5, 1.0),
-                new Cartesian3D(1.1, 0.5, 1.0),
-                new Cartesian3D(0.5, -0.1, 1.0),
-                new Cartesian3D(0.5, 1.1, 1.0),
-                new Cartesian3D(0.5, 0.5, -0.1),
-                new Cartesian3D(0.5, 0.5, 0.6));
+                new Point3D(-0.1, 0.5, 1.0),
+                new Point3D(1.1, 0.5, 1.0),
+                new Point3D(0.5, -0.1, 1.0),
+                new Point3D(0.5, 1.1, 1.0),
+                new Point3D(0.5, 0.5, -0.1),
+                new Point3D(0.5, 0.5, 0.6));
 
         checkPoints(Region.Location.INSIDE, result,
-                new Cartesian3D(0.1, 0.5, 0.4),
-                new Cartesian3D(0.9, 0.5, 0.4),
-                new Cartesian3D(0.5, 0.1, 0.4),
-                new Cartesian3D(0.5, 0.9, 0.4),
-                new Cartesian3D(0.5, 0.5, 0.1),
-                new Cartesian3D(0.5, 0.5, 0.4));
+                new Point3D(0.1, 0.5, 0.4),
+                new Point3D(0.9, 0.5, 0.4),
+                new Point3D(0.5, 0.1, 0.4),
+                new Point3D(0.5, 0.9, 0.4),
+                new Point3D(0.5, 0.5, 0.1),
+                new Point3D(0.5, 0.5, 0.4));
     }
 
     @Test
@@ -1224,32 +1223,32 @@ public void testDifference_self() {
         // arrange
         double radius = 1.0;
 
-        PolyhedronsSet sphere = createSphere(Cartesian3D.ZERO, radius, 8, 16);
+        PolyhedronsSet sphere = createSphere(Point3D.ZERO, radius, 8, 16);
 
         // act
-        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Euclidean3D>().difference(sphere, sphere.copySelf());
+        PolyhedronsSet result = (PolyhedronsSet) new RegionFactory<Point3D>().difference(sphere, sphere.copySelf());
 
         // assert
         Assert.assertEquals(0.0, result.getSize(), TEST_TOLERANCE);
         Assert.assertEquals(0.0, result.getBoundarySize(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(Cartesian3D.NaN, (Cartesian3D) result.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point3D.NaN, result.getBarycenter(), TEST_TOLERANCE);
         Assert.assertTrue(result.isEmpty());
         Assert.assertFalse(result.isFull());
 
         checkPoints(Region.Location.OUTSIDE, result,
-                new Cartesian3D(-1.1, 0, 0),
-                new Cartesian3D(1.1, 0, 0),
-                new Cartesian3D(0, -1.1, 0),
-                new Cartesian3D(0, 1.1, 0),
-                new Cartesian3D(0, 0, -1.1),
-                new Cartesian3D(0, 0, 1.1),
-                new Cartesian3D(-0.9, 0, 0),
-                new Cartesian3D(0.9, 0, 0),
-                new Cartesian3D(0, -0.9, 0),
-                new Cartesian3D(0, 0.9, 0),
-                new Cartesian3D(0, 0, -0.9),
-                new Cartesian3D(0, 0, 0.9),
-                Cartesian3D.ZERO);
+                new Point3D(-1.1, 0, 0),
+                new Point3D(1.1, 0, 0),
+                new Point3D(0, -1.1, 0),
+                new Point3D(0, 1.1, 0),
+                new Point3D(0, 0, -1.1),
+                new Point3D(0, 0, 1.1),
+                new Point3D(-0.9, 0, 0),
+                new Point3D(0.9, 0, 0),
+                new Point3D(0, -0.9, 0),
+                new Point3D(0, 0.9, 0),
+                new Point3D(0, 0, -0.9),
+                new Point3D(0, 0, 0.9),
+                Point3D.ZERO);
     }
 
     @Test
@@ -1259,11 +1258,11 @@ public void testBoolean_multiple() throws IOException {
         double size = 1.0;
         double radius = size * 0.5;
         PolyhedronsSet box = new PolyhedronsSet(0, size, 0, size, 0, size, TEST_TOLERANCE);
-        PolyhedronsSet sphereToAdd = createSphere(new Cartesian3D(size * 0.5, size * 0.5, size), radius, 8, 16);
-        PolyhedronsSet sphereToRemove1 = createSphere(new Cartesian3D(size * 0.5, 0, size * 0.5), radius, 8, 16);
-        PolyhedronsSet sphereToRemove2 = createSphere(new Cartesian3D(size * 0.5, 1, size * 0.5), radius, 8, 16);
+        PolyhedronsSet sphereToAdd = createSphere(new Point3D(size * 0.5, size * 0.5, size), radius, 8, 16);
+        PolyhedronsSet sphereToRemove1 = createSphere(new Point3D(size * 0.5, 0, size * 0.5), radius, 8, 16);
+        PolyhedronsSet sphereToRemove2 = createSphere(new Point3D(size * 0.5, 1, size * 0.5), radius, 8, 16);
 
-        RegionFactory<Euclidean3D> factory = new RegionFactory<Euclidean3D>();
+        RegionFactory<Point3D> factory = new RegionFactory<Point3D>();
 
         // act
         PolyhedronsSet result = (PolyhedronsSet) factory.union(box, sphereToAdd);
@@ -1281,20 +1280,20 @@ public void testBoolean_multiple() throws IOException {
         Assert.assertFalse(result.isFull());
 
         checkPoints(Region.Location.OUTSIDE, result,
-                new Cartesian3D(-0.1, 0.5, 0.5),
-                new Cartesian3D(1.1, 0.5, 0.5),
-                new Cartesian3D(0.5, 0.4, 0.5),
-                new Cartesian3D(0.5, 0.6, 0.5),
-                new Cartesian3D(0.5, 0.5, -0.1),
-                new Cartesian3D(0.5, 0.5, 1.6));
+                new Point3D(-0.1, 0.5, 0.5),
+                new Point3D(1.1, 0.5, 0.5),
+                new Point3D(0.5, 0.4, 0.5),
+                new Point3D(0.5, 0.6, 0.5),
+                new Point3D(0.5, 0.5, -0.1),
+                new Point3D(0.5, 0.5, 1.6));
 
         checkPoints(Region.Location.INSIDE, result,
-                new Cartesian3D(0.1, 0.5, 0.1),
-                new Cartesian3D(0.9, 0.5, 0.1),
-                new Cartesian3D(0.5, 0.4, 0.1),
-                new Cartesian3D(0.5, 0.6, 0.1),
-                new Cartesian3D(0.5, 0.5, 0.1),
-                new Cartesian3D(0.5, 0.5, 1.4));
+                new Point3D(0.1, 0.5, 0.1),
+                new Point3D(0.9, 0.5, 0.1),
+                new Point3D(0.5, 0.4, 0.1),
+                new Point3D(0.5, 0.6, 0.1),
+                new Point3D(0.5, 0.5, 0.1),
+                new Point3D(0.5, 0.5, 1.4));
     }
 
     @Test
@@ -1303,35 +1302,35 @@ public void testProjectToBoundary() {
         PolyhedronsSet polySet = new PolyhedronsSet(0, 1, 0, 1, 0, 1, TEST_TOLERANCE);
 
         // act/assert
-        checkProjectToBoundary(polySet, new Cartesian3D(0.4, 0.5, 0.5),
-                new Cartesian3D(0, 0.5, 0.5), -0.4);
-        checkProjectToBoundary(polySet, new Cartesian3D(1.5, 0.5, 0.5),
-                new Cartesian3D(1, 0.5, 0.5), 0.5);
-        checkProjectToBoundary(polySet, new Cartesian3D(2, 2, 2),
-                new Cartesian3D(1, 1, 1), Math.sqrt(3));
+        checkProjectToBoundary(polySet, new Point3D(0.4, 0.5, 0.5),
+                new Point3D(0, 0.5, 0.5), -0.4);
+        checkProjectToBoundary(polySet, new Point3D(1.5, 0.5, 0.5),
+                new Point3D(1, 0.5, 0.5), 0.5);
+        checkProjectToBoundary(polySet, new Point3D(2, 2, 2),
+                new Point3D(1, 1, 1), Math.sqrt(3));
     }
 
     @Test
     public void testProjectToBoundary_invertedRegion() {
         // arrange
         PolyhedronsSet polySet = new PolyhedronsSet(0, 1, 0, 1, 0, 1, TEST_TOLERANCE);
-        polySet = (PolyhedronsSet) new RegionFactory<Euclidean3D>().getComplement(polySet);
+        polySet = (PolyhedronsSet) new RegionFactory<Point3D>().getComplement(polySet);
 
         // act/assert
-        checkProjectToBoundary(polySet, new Cartesian3D(0.4, 0.5, 0.5),
-                new Cartesian3D(0, 0.5, 0.5), 0.4);
-        checkProjectToBoundary(polySet, new Cartesian3D(1.5, 0.5, 0.5),
-                new Cartesian3D(1, 0.5, 0.5), -0.5);
-        checkProjectToBoundary(polySet, new Cartesian3D(2, 2, 2),
-                new Cartesian3D(1, 1, 1), -Math.sqrt(3));
+        checkProjectToBoundary(polySet, new Point3D(0.4, 0.5, 0.5),
+                new Point3D(0, 0.5, 0.5), 0.4);
+        checkProjectToBoundary(polySet, new Point3D(1.5, 0.5, 0.5),
+                new Point3D(1, 0.5, 0.5), -0.5);
+        checkProjectToBoundary(polySet, new Point3D(2, 2, 2),
+                new Point3D(1, 1, 1), -Math.sqrt(3));
     }
 
-    private void checkProjectToBoundary(PolyhedronsSet poly, Cartesian3D toProject,
-            Cartesian3D expectedPoint, double expectedOffset) {
-        BoundaryProjection<Euclidean3D> proj = poly.projectToBoundary(toProject);
+    private void checkProjectToBoundary(PolyhedronsSet poly, Point3D toProject,
+            Point3D expectedPoint, double expectedOffset) {
+        BoundaryProjection<Point3D> proj = poly.projectToBoundary(toProject);
 
-        EuclideanTestUtils.assertVectorEquals(toProject, (Cartesian3D) proj.getOriginal(), TEST_TOLERANCE);
-        EuclideanTestUtils.assertVectorEquals(expectedPoint, (Cartesian3D) proj.getProjected(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(toProject, proj.getOriginal(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(expectedPoint, proj.getProjected(), TEST_TOLERANCE);
         Assert.assertEquals(expectedOffset, proj.getOffset(), TEST_TOLERANCE);
     }
 
@@ -1346,71 +1345,71 @@ private String loadTestData(final String resourceName)
         }
     }
 
-    private void checkPoints(Region.Location expected, PolyhedronsSet poly, Cartesian3D ... points) {
+    private void checkPoints(Region.Location expected, PolyhedronsSet poly, Point3D ... points) {
         for (int i = 0; i < points.length; ++i) {
             Assert.assertEquals("Incorrect location for " + points[i], expected, poly.checkPoint(points[i]));
         }
     }
 
-    private List<SubHyperplane<Euclidean3D>> createBoxBoundaries(Cartesian3D center, double size, double tolerance) {
-        List<SubHyperplane<Euclidean3D>> boundaries = new ArrayList<>();
+    private List<SubHyperplane<Point3D>> createBoxBoundaries(Point3D center, double size, double tolerance) {
+        List<SubHyperplane<Point3D>> boundaries = new ArrayList<>();
 
         double offset = size * 0.5;
 
-        Plane xMinus = new Plane(center.add(new Cartesian3D(-offset, 0, 0)), Cartesian3D.MINUS_I, tolerance);
-        Plane xPlus = new Plane(center.add(new Cartesian3D(offset, 0, 0)), Cartesian3D.PLUS_I, tolerance);
-        Plane yPlus = new Plane(center.add(new Cartesian3D(0, offset, 0)), Cartesian3D.PLUS_J, tolerance);
-        Plane yMinus = new Plane(center.add(new Cartesian3D(0, -offset, 0)), Cartesian3D.MINUS_J, tolerance);
-        Plane zPlus = new Plane(center.add(new Cartesian3D(0, 0, offset)), Cartesian3D.PLUS_K, tolerance);
-        Plane zMinus = new Plane(center.add(new Cartesian3D(0, 0, -offset)), Cartesian3D.MINUS_K, tolerance);
+        Plane xMinus = new Plane(center.add(new Vector3D(-offset, 0, 0)), Vector3D.MINUS_X, tolerance);
+        Plane xPlus = new Plane(center.add(new Vector3D(offset, 0, 0)), Vector3D.PLUS_X, tolerance);
+        Plane yPlus = new Plane(center.add(new Vector3D(0, offset, 0)), Vector3D.PLUS_Y, tolerance);
+        Plane yMinus = new Plane(center.add(new Vector3D(0, -offset, 0)), Vector3D.MINUS_Y, tolerance);
+        Plane zPlus = new Plane(center.add(new Vector3D(0, 0, offset)), Vector3D.PLUS_Z, tolerance);
+        Plane zMinus = new Plane(center.add(new Vector3D(0, 0, -offset)), Vector3D.MINUS_Z, tolerance);
 
         // +x
         boundaries.add(createSubPlane(xPlus,
-                        center.add(new Cartesian3D(offset, offset, offset)),
-                        center.add(new Cartesian3D(offset, -offset, offset)),
-                        center.add(new Cartesian3D(offset, -offset, -offset)),
-                        center.add(new Cartesian3D(offset, offset, -offset))));
+                        center.add(new Vector3D(offset, offset, offset)),
+                        center.add(new Vector3D(offset, -offset, offset)),
+                        center.add(new Vector3D(offset, -offset, -offset)),
+                        center.add(new Vector3D(offset, offset, -offset))));
 
         // -x
         boundaries.add(createSubPlane(xMinus,
-                        center.add(new Cartesian3D(-offset, -offset, offset)),
-                        center.add(new Cartesian3D(-offset, offset, offset)),
-                        center.add(new Cartesian3D(-offset, offset, -offset)),
-                        center.add(new Cartesian3D(-offset, -offset, -offset))));
+                        center.add(new Vector3D(-offset, -offset, offset)),
+                        center.add(new Vector3D(-offset, offset, offset)),
+                        center.add(new Vector3D(-offset, offset, -offset)),
+                        center.add(new Vector3D(-offset, -offset, -offset))));
 
         // +y
         boundaries.add(createSubPlane(yPlus,
-                        center.add(new Cartesian3D(-offset, offset, offset)),
-                        center.add(new Cartesian3D(offset, offset, offset)),
-                        center.add(new Cartesian3D(offset, offset, -offset)),
-                        center.add(new Cartesian3D(-offset, offset, -offset))));
+                        center.add(new Vector3D(-offset, offset, offset)),
+                        center.add(new Vector3D(offset, offset, offset)),
+                        center.add(new Vector3D(offset, offset, -offset)),
+                        center.add(new Vector3D(-offset, offset, -offset))));
 
         // -y
         boundaries.add(createSubPlane(yMinus,
-                        center.add(new Cartesian3D(-offset, -offset, offset)),
-                        center.add(new Cartesian3D(-offset, -offset, -offset)),
-                        center.add(new Cartesian3D(offset, -offset, -offset)),
-                        center.add(new Cartesian3D(offset, -offset, offset))));
+                        center.add(new Vector3D(-offset, -offset, offset)),
+                        center.add(new Vector3D(-offset, -offset, -offset)),
+                        center.add(new Vector3D(offset, -offset, -offset)),
+                        center.add(new Vector3D(offset, -offset, offset))));
 
         // +z
         boundaries.add(createSubPlane(zPlus,
-                        center.add(new Cartesian3D(-offset, -offset, offset)),
-                        center.add(new Cartesian3D(offset, -offset, offset)),
-                        center.add(new Cartesian3D(offset, offset, offset)),
-                        center.add(new Cartesian3D(-offset, offset, offset))));
+                        center.add(new Vector3D(-offset, -offset, offset)),
+                        center.add(new Vector3D(offset, -offset, offset)),
+                        center.add(new Vector3D(offset, offset, offset)),
+                        center.add(new Vector3D(-offset, offset, offset))));
 
         // -z
         boundaries.add(createSubPlane(zMinus,
-                        center.add(new Cartesian3D(-offset, -offset, -offset)),
-                        center.add(new Cartesian3D(-offset, offset, -offset)),
-                        center.add(new Cartesian3D(offset, offset, -offset)),
-                        center.add(new Cartesian3D(offset, -offset, -offset))));
+                        center.add(new Vector3D(-offset, -offset, -offset)),
+                        center.add(new Vector3D(-offset, offset, -offset)),
+                        center.add(new Vector3D(offset, offset, -offset)),
+                        center.add(new Vector3D(offset, -offset, -offset))));
 
         return boundaries;
     }
 
-    private SubPlane createSubPlane(Plane plane, Cartesian3D...points) {
-        Cartesian2D[] points2d = new Cartesian2D[points.length];
+    private SubPlane createSubPlane(Plane plane, Point3D...points) {
+        Point2D[] points2d = new Point2D[points.length];
         for (int i=0; i<points.length; ++i) {
             points2d[i] = plane.toSubSpace(points[i]);
         }
@@ -1420,15 +1419,15 @@ private SubPlane createSubPlane(Plane plane, Cartesian3D...points) {
         return new SubPlane(plane, polygon);
     }
 
-    private PolyhedronsSet createSphere(Cartesian3D center, double radius, int stacks, int slices) {
+    private PolyhedronsSet createSphere(Point3D center, double radius, int stacks, int slices) {
         List<Plane> planes = new ArrayList<>();
 
         // add top and bottom planes (+/- z)
-        Cartesian3D topZ = new Cartesian3D(center.getX(), center.getY(), center.getZ() + radius);
-        Cartesian3D bottomZ = new Cartesian3D(center.getX(), center.getY(), center.getZ() - radius);
+        Point3D topZ = new Point3D(center.getX(), center.getY(), center.getZ() + radius);
+        Point3D bottomZ = new Point3D(center.getX(), center.getY(), center.getZ() - radius);
 
-        planes.add(new Plane(topZ, Cartesian3D.PLUS_K, TEST_TOLERANCE));
-        planes.add(new Plane(bottomZ, Cartesian3D.MINUS_K, TEST_TOLERANCE));
+        planes.add(new Plane(topZ, Vector3D.PLUS_Z, TEST_TOLERANCE));
+        planes.add(new Plane(bottomZ, Vector3D.MINUS_Z, TEST_TOLERANCE));
 
         // add the side planes
         double vDelta = Math.PI / stacks;
@@ -1441,7 +1440,8 @@ private PolyhedronsSet createSphere(Cartesian3D center, double radius, int stack
         double stackRadius;
         double stackHeight;
         double x, y;
-        Cartesian3D norm, pt;
+        Point3D pt;
+        Vector3D norm;
 
         vAngle = -0.5 * vDelta;
         for (int v=0; v<stacks; ++v) {
@@ -1457,19 +1457,19 @@ private PolyhedronsSet createSphere(Cartesian3D center, double radius, int stack
                 x = Math.cos(hAngle) * stackRadius;
                 y = Math.sin(hAngle) * stackRadius;
 
-                norm = new Cartesian3D(x, y, stackHeight).normalize();
-                pt = norm.scalarMultiply(adjustedRadius).add(center);
+                norm = new Vector3D(x, y, stackHeight).normalize();
+                pt = center.add(norm.scalarMultiply(adjustedRadius));
 
                 planes.add(new Plane(pt, norm, TEST_TOLERANCE));
             }
         }
 
-        return (PolyhedronsSet) new RegionFactory<Euclidean3D>().buildConvex(planes.toArray(new Plane[0]));
+        return (PolyhedronsSet) new RegionFactory<Point3D>().buildConvex(planes.toArray(new Plane[0]));
     }
 
-    private void assertSubPlaneNormal(Cartesian3D expectedNormal, SubHyperplane<Euclidean3D> sub) {
-        Cartesian3D norm = ((Plane) sub.getHyperplane()).getNormal();
-        EuclideanTestUtils.assertVectorEquals(expectedNormal, norm, TEST_TOLERANCE);
+    private void assertSubPlaneNormal(Vector3D expectedNormal, SubHyperplane<Point3D> sub) {
+        Vector3D norm = ((Plane) sub.getHyperplane()).getNormal();
+        EuclideanTestUtils.assertCoordinatesEqual(expectedNormal, norm, TEST_TOLERANCE);
     }
 
     private double cubeVolume(double size) {
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java
index fd6884f..875575b 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java
@@ -28,21 +28,21 @@
   public void testIdentity() {
 
     Rotation r = Rotation.IDENTITY;
-    checkVector(r.applyTo(Cartesian3D.PLUS_I), Cartesian3D.PLUS_I);
-    checkVector(r.applyTo(Cartesian3D.PLUS_J), Cartesian3D.PLUS_J);
-    checkVector(r.applyTo(Cartesian3D.PLUS_K), Cartesian3D.PLUS_K);
+    checkVector(r.applyTo(Vector3D.PLUS_X), Vector3D.PLUS_X);
+    checkVector(r.applyTo(Vector3D.PLUS_Y), Vector3D.PLUS_Y);
+    checkVector(r.applyTo(Vector3D.PLUS_Z), Vector3D.PLUS_Z);
     checkAngle(r.getAngle(), 0);
 
     r = new Rotation(-1, 0, 0, 0, false);
-    checkVector(r.applyTo(Cartesian3D.PLUS_I), Cartesian3D.PLUS_I);
-    checkVector(r.applyTo(Cartesian3D.PLUS_J), Cartesian3D.PLUS_J);
-    checkVector(r.applyTo(Cartesian3D.PLUS_K), Cartesian3D.PLUS_K);
+    checkVector(r.applyTo(Vector3D.PLUS_X), Vector3D.PLUS_X);
+    checkVector(r.applyTo(Vector3D.PLUS_Y), Vector3D.PLUS_Y);
+    checkVector(r.applyTo(Vector3D.PLUS_Z), Vector3D.PLUS_Z);
     checkAngle(r.getAngle(), 0);
 
     r = new Rotation(42, 0, 0, 0, true);
-    checkVector(r.applyTo(Cartesian3D.PLUS_I), Cartesian3D.PLUS_I);
-    checkVector(r.applyTo(Cartesian3D.PLUS_J), Cartesian3D.PLUS_J);
-    checkVector(r.applyTo(Cartesian3D.PLUS_K), Cartesian3D.PLUS_K);
+    checkVector(r.applyTo(Vector3D.PLUS_X), Vector3D.PLUS_X);
+    checkVector(r.applyTo(Vector3D.PLUS_Y), Vector3D.PLUS_Y);
+    checkVector(r.applyTo(Vector3D.PLUS_Z), Vector3D.PLUS_Z);
     checkAngle(r.getAngle(), 0);
 
   }
@@ -51,95 +51,95 @@ public void testIdentity() {
   @Deprecated
   public void testAxisAngleDeprecated() {
 
-    Rotation r = new Rotation(new Cartesian3D(10, 10, 10), 2 * Math.PI / 3);
-    checkVector(r.applyTo(Cartesian3D.PLUS_I), Cartesian3D.PLUS_J);
-    checkVector(r.applyTo(Cartesian3D.PLUS_J), Cartesian3D.PLUS_K);
-    checkVector(r.applyTo(Cartesian3D.PLUS_K), Cartesian3D.PLUS_I);
+    Rotation r = new Rotation(new Vector3D(10, 10, 10), 2 * Math.PI / 3);
+    checkVector(r.applyTo(Vector3D.PLUS_X), Vector3D.PLUS_Y);
+    checkVector(r.applyTo(Vector3D.PLUS_Y), Vector3D.PLUS_Z);
+    checkVector(r.applyTo(Vector3D.PLUS_Z), Vector3D.PLUS_X);
     double s = 1 / Math.sqrt(3);
-    checkVector(r.getAxis(), new Cartesian3D(s, s, s));
+    checkVector(r.getAxis(), new Vector3D(s, s, s));
     checkAngle(r.getAngle(), 2 * Math.PI / 3);
 
     try {
-      new Rotation(new Cartesian3D(0, 0, 0), 2 * Math.PI / 3);
+      new Rotation(new Vector3D(0, 0, 0), 2 * Math.PI / 3);
       Assert.fail("an exception should have been thrown");
     } catch (IllegalArgumentException e) {
     }
 
-    r = new Rotation(Cartesian3D.PLUS_K, 1.5 * Math.PI);
-    checkVector(r.getAxis(), new Cartesian3D(0, 0, -1));
+    r = new Rotation(Vector3D.PLUS_Z, 1.5 * Math.PI);
+    checkVector(r.getAxis(), new Vector3D(0, 0, -1));
     checkAngle(r.getAngle(), 0.5 * Math.PI);
 
-    r = new Rotation(Cartesian3D.PLUS_J, Math.PI);
-    checkVector(r.getAxis(), Cartesian3D.PLUS_J);
+    r = new Rotation(Vector3D.PLUS_Y, Math.PI);
+    checkVector(r.getAxis(), Vector3D.PLUS_Y);
     checkAngle(r.getAngle(), Math.PI);
 
-    checkVector(Rotation.IDENTITY.getAxis(), Cartesian3D.PLUS_I);
+    checkVector(Rotation.IDENTITY.getAxis(), Vector3D.PLUS_X);
 
   }
 
   @Test
   public void testAxisAngleVectorOperator() {
 
-    Rotation r = new Rotation(new Cartesian3D(10, 10, 10), 2 * Math.PI / 3, RotationConvention.VECTOR_OPERATOR);
-    checkVector(r.applyTo(Cartesian3D.PLUS_I), Cartesian3D.PLUS_J);
-    checkVector(r.applyTo(Cartesian3D.PLUS_J), Cartesian3D.PLUS_K);
-    checkVector(r.applyTo(Cartesian3D.PLUS_K), Cartesian3D.PLUS_I);
+    Rotation r = new Rotation(new Vector3D(10, 10, 10), 2 * Math.PI / 3, RotationConvention.VECTOR_OPERATOR);
+    checkVector(r.applyTo(Vector3D.PLUS_X), Vector3D.PLUS_Y);
+    checkVector(r.applyTo(Vector3D.PLUS_Y), Vector3D.PLUS_Z);
+    checkVector(r.applyTo(Vector3D.PLUS_Z), Vector3D.PLUS_X);
     double s = 1 / Math.sqrt(3);
-    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), new Cartesian3D( s,  s,  s));
-    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), new Cartesian3D(-s, -s, -s));
+    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), new Vector3D( s,  s,  s));
+    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), new Vector3D(-s, -s, -s));
     checkAngle(r.getAngle(), 2 * Math.PI / 3);
 
     try {
-      new Rotation(new Cartesian3D(0, 0, 0), 2 * Math.PI / 3, RotationConvention.VECTOR_OPERATOR);
+      new Rotation(new Vector3D(0, 0, 0), 2 * Math.PI / 3, RotationConvention.VECTOR_OPERATOR);
       Assert.fail("an exception should have been thrown");
     } catch (IllegalArgumentException e) {
     }
 
-    r = new Rotation(Cartesian3D.PLUS_K, 1.5 * Math.PI, RotationConvention.VECTOR_OPERATOR);
-    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), new Cartesian3D(0, 0, -1));
-    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), new Cartesian3D(0, 0, +1));
+    r = new Rotation(Vector3D.PLUS_Z, 1.5 * Math.PI, RotationConvention.VECTOR_OPERATOR);
+    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), new Vector3D(0, 0, -1));
+    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), new Vector3D(0, 0, +1));
     checkAngle(r.getAngle(), 0.5 * Math.PI);
 
-    r = new Rotation(Cartesian3D.PLUS_J, Math.PI, RotationConvention.VECTOR_OPERATOR);
-    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), Cartesian3D.PLUS_J);
-    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), Cartesian3D.MINUS_J);
+    r = new Rotation(Vector3D.PLUS_Y, Math.PI, RotationConvention.VECTOR_OPERATOR);
+    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), Vector3D.PLUS_Y);
+    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), Vector3D.MINUS_Y);
     checkAngle(r.getAngle(), Math.PI);
 
-    checkVector(Rotation.IDENTITY.getAxis(RotationConvention.VECTOR_OPERATOR), Cartesian3D.PLUS_I);
-    checkVector(Rotation.IDENTITY.getAxis(RotationConvention.FRAME_TRANSFORM), Cartesian3D.MINUS_I);
+    checkVector(Rotation.IDENTITY.getAxis(RotationConvention.VECTOR_OPERATOR), Vector3D.PLUS_X);
+    checkVector(Rotation.IDENTITY.getAxis(RotationConvention.FRAME_TRANSFORM), Vector3D.MINUS_X);
 
   }
 
   @Test
   public void testAxisAngleFrameTransform() {
 
-    Rotation r = new Rotation(new Cartesian3D(10, 10, 10), 2 * Math.PI / 3, RotationConvention.FRAME_TRANSFORM);
-    checkVector(r.applyTo(Cartesian3D.PLUS_I), Cartesian3D.PLUS_K);
-    checkVector(r.applyTo(Cartesian3D.PLUS_J), Cartesian3D.PLUS_I);
-    checkVector(r.applyTo(Cartesian3D.PLUS_K), Cartesian3D.PLUS_J);
+    Rotation r = new Rotation(new Vector3D(10, 10, 10), 2 * Math.PI / 3, RotationConvention.FRAME_TRANSFORM);
+    checkVector(r.applyTo(Vector3D.PLUS_X), Vector3D.PLUS_Z);
+    checkVector(r.applyTo(Vector3D.PLUS_Y), Vector3D.PLUS_X);
+    checkVector(r.applyTo(Vector3D.PLUS_Z), Vector3D.PLUS_Y);
     double s = 1 / Math.sqrt(3);
-    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), new Cartesian3D( s,  s,  s));
-    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), new Cartesian3D(-s, -s, -s));
+    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), new Vector3D( s,  s,  s));
+    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), new Vector3D(-s, -s, -s));
     checkAngle(r.getAngle(), 2 * Math.PI / 3);
 
     try {
-      new Rotation(new Cartesian3D(0, 0, 0), 2 * Math.PI / 3, RotationConvention.FRAME_TRANSFORM);
+      new Rotation(new Vector3D(0, 0, 0), 2 * Math.PI / 3, RotationConvention.FRAME_TRANSFORM);
       Assert.fail("an exception should have been thrown");
     } catch (IllegalArgumentException e) {
     }
 
-    r = new Rotation(Cartesian3D.PLUS_K, 1.5 * Math.PI, RotationConvention.FRAME_TRANSFORM);
-    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), new Cartesian3D(0, 0, -1));
-    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), new Cartesian3D(0, 0, +1));
+    r = new Rotation(Vector3D.PLUS_Z, 1.5 * Math.PI, RotationConvention.FRAME_TRANSFORM);
+    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), new Vector3D(0, 0, -1));
+    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), new Vector3D(0, 0, +1));
     checkAngle(r.getAngle(), 0.5 * Math.PI);
 
-    r = new Rotation(Cartesian3D.PLUS_J, Math.PI, RotationConvention.FRAME_TRANSFORM);
-    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), Cartesian3D.PLUS_J);
-    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), Cartesian3D.MINUS_J);
+    r = new Rotation(Vector3D.PLUS_Y, Math.PI, RotationConvention.FRAME_TRANSFORM);
+    checkVector(r.getAxis(RotationConvention.FRAME_TRANSFORM), Vector3D.PLUS_Y);
+    checkVector(r.getAxis(RotationConvention.VECTOR_OPERATOR), Vector3D.MINUS_Y);
     checkAngle(r.getAngle(), Math.PI);
 
-    checkVector(Rotation.IDENTITY.getAxis(RotationConvention.FRAME_TRANSFORM), Cartesian3D.MINUS_I);
-    checkVector(Rotation.IDENTITY.getAxis(RotationConvention.VECTOR_OPERATOR), Cartesian3D.PLUS_I);
+    checkVector(Rotation.IDENTITY.getAxis(RotationConvention.FRAME_TRANSFORM), Vector3D.MINUS_X);
+    checkVector(Rotation.IDENTITY.getAxis(RotationConvention.VECTOR_OPERATOR), Vector3D.PLUS_X);
 
   }
 
@@ -151,7 +151,7 @@ public void testRevertDeprecated() {
     checkRotation(reverted.applyTo(r), 1, 0, 0, 0);
     Assert.assertEquals(r.getAngle(), reverted.getAngle(), 1.0e-12);
     Assert.assertEquals(-1,
-                        Cartesian3D.dotProduct(r.getAxis(RotationConvention.VECTOR_OPERATOR),
+                        r.getAxis(RotationConvention.VECTOR_OPERATOR).dotProduct(
                                            reverted.getAxis(RotationConvention.VECTOR_OPERATOR)),
                         1.0e-12);
   }
@@ -164,7 +164,7 @@ public void testRevertVectorOperator() {
     checkRotation(reverted.compose(r, RotationConvention.VECTOR_OPERATOR), 1, 0, 0, 0);
     Assert.assertEquals(r.getAngle(), reverted.getAngle(), 1.0e-12);
     Assert.assertEquals(-1,
-                        Cartesian3D.dotProduct(r.getAxis(RotationConvention.VECTOR_OPERATOR),
+                        r.getAxis(RotationConvention.VECTOR_OPERATOR).dotProduct(
                                            reverted.getAxis(RotationConvention.VECTOR_OPERATOR)),
                         1.0e-12);
   }
@@ -177,7 +177,7 @@ public void testRevertFrameTransform() {
     checkRotation(reverted.compose(r, RotationConvention.FRAME_TRANSFORM), 1, 0, 0, 0);
     Assert.assertEquals(r.getAngle(), reverted.getAngle(), 1.0e-12);
     Assert.assertEquals(-1,
-                        Cartesian3D.dotProduct(r.getAxis(RotationConvention.FRAME_TRANSFORM),
+                        r.getAxis(RotationConvention.FRAME_TRANSFORM).dotProduct(
                                            reverted.getAxis(RotationConvention.FRAME_TRANSFORM)),
                         1.0e-12);
   }
@@ -185,15 +185,15 @@ public void testRevertFrameTransform() {
   @Test
   public void testVectorOnePair() {
 
-    Cartesian3D u = new Cartesian3D(3, 2, 1);
-    Cartesian3D v = new Cartesian3D(-4, 2, 2);
+    Vector3D u = new Vector3D(3, 2, 1);
+    Vector3D v = new Vector3D(-4, 2, 2);
     Rotation r = new Rotation(u, v);
     checkVector(r.applyTo(u.scalarMultiply(v.getNorm())), v.scalarMultiply(u.getNorm()));
 
     checkAngle(new Rotation(u, u.negate()).getAngle(), Math.PI);
 
     try {
-        new Rotation(u, Cartesian3D.ZERO);
+        new Rotation(u, Vector3D.ZERO);
         Assert.fail("an exception should have been thrown");
     } catch (IllegalArgumentException e) {
         // expected behavior
@@ -204,36 +204,36 @@ public void testVectorOnePair() {
   @Test
   public void testVectorTwoPairs() {
 
-    Cartesian3D u1 = new Cartesian3D(3, 0, 0);
-    Cartesian3D u2 = new Cartesian3D(0, 5, 0);
-    Cartesian3D v1 = new Cartesian3D(0, 0, 2);
-    Cartesian3D v2 = new Cartesian3D(-2, 0, 2);
+    Vector3D u1 = new Vector3D(3, 0, 0);
+    Vector3D u2 = new Vector3D(0, 5, 0);
+    Vector3D v1 = new Vector3D(0, 0, 2);
+    Vector3D v2 = new Vector3D(-2, 0, 2);
     Rotation r = new Rotation(u1, u2, v1, v2);
-    checkVector(r.applyTo(Cartesian3D.PLUS_I), Cartesian3D.PLUS_K);
-    checkVector(r.applyTo(Cartesian3D.PLUS_J), Cartesian3D.MINUS_I);
+    checkVector(r.applyTo(Vector3D.PLUS_X), Vector3D.PLUS_Z);
+    checkVector(r.applyTo(Vector3D.PLUS_Y), Vector3D.MINUS_X);
 
     r = new Rotation(u1, u2, u1.negate(), u2.negate());
-    Cartesian3D axis = r.getAxis(RotationConvention.VECTOR_OPERATOR);
-    if (Cartesian3D.dotProduct(axis, Cartesian3D.PLUS_K) > 0) {
-      checkVector(axis, Cartesian3D.PLUS_K);
+    Vector3D axis = r.getAxis(RotationConvention.VECTOR_OPERATOR);
+    if (axis.dotProduct(Vector3D.PLUS_Z) > 0) {
+      checkVector(axis, Vector3D.PLUS_Z);
     } else {
-      checkVector(axis, Cartesian3D.MINUS_K);
+      checkVector(axis, Vector3D.MINUS_Z);
     }
     checkAngle(r.getAngle(), Math.PI);
 
     double sqrt = Math.sqrt(2) / 2;
-    r = new Rotation(Cartesian3D.PLUS_I,  Cartesian3D.PLUS_J,
-                     new Cartesian3D(0.5, 0.5,  sqrt),
-                     new Cartesian3D(0.5, 0.5, -sqrt));
+    r = new Rotation(Vector3D.PLUS_X,  Vector3D.PLUS_Y,
+                     new Vector3D(0.5, 0.5,  sqrt),
+                     new Vector3D(0.5, 0.5, -sqrt));
     checkRotation(r, sqrt, 0.5, 0.5, 0);
 
-    r = new Rotation(u1, u2, u1, Cartesian3D.crossProduct(u1, u2));
+    r = new Rotation(u1, u2, u1, u1.crossProduct(u2));
     checkRotation(r, sqrt, -sqrt, 0, 0);
 
     checkRotation(new Rotation(u1, u2, u1, u2), 1, 0, 0, 0);
 
     try {
-        new Rotation(u1, u2, Cartesian3D.ZERO, v2);
+        new Rotation(u1, u2, Vector3D.ZERO, v2);
         Assert.fail("an exception should have been thrown");
     } catch (IllegalArgumentException e) {
       // expected behavior
@@ -308,9 +308,9 @@ public void testMatrix() {
                       { 0.0, 0.0, 1.0 },
                       { 1.0, 0.0, 0.0 } };
     Rotation r = new Rotation(m1, 1.0e-7);
-    checkVector(r.applyTo(Cartesian3D.PLUS_I), Cartesian3D.PLUS_K);
-    checkVector(r.applyTo(Cartesian3D.PLUS_J), Cartesian3D.PLUS_I);
-    checkVector(r.applyTo(Cartesian3D.PLUS_K), Cartesian3D.PLUS_J);
+    checkVector(r.applyTo(Vector3D.PLUS_X), Vector3D.PLUS_Z);
+    checkVector(r.applyTo(Vector3D.PLUS_Y), Vector3D.PLUS_X);
+    checkVector(r.applyTo(Vector3D.PLUS_Z), Vector3D.PLUS_Y);
 
     double[][] m2 = { { 0.83203, -0.55012, -0.07139 },
                       { 0.48293,  0.78164, -0.39474 },
@@ -361,12 +361,12 @@ public void testMatrix() {
       }
     }
 
-    checkVector(r.applyTo(Cartesian3D.PLUS_I),
-                new Cartesian3D(m3[0][0], m3[1][0], m3[2][0]));
-    checkVector(r.applyTo(Cartesian3D.PLUS_J),
-                new Cartesian3D(m3[0][1], m3[1][1], m3[2][1]));
-    checkVector(r.applyTo(Cartesian3D.PLUS_K),
-                new Cartesian3D(m3[0][2], m3[1][2], m3[2][2]));
+    checkVector(r.applyTo(Vector3D.PLUS_X),
+                new Vector3D(m3[0][0], m3[1][0], m3[2][0]));
+    checkVector(r.applyTo(Vector3D.PLUS_Y),
+                new Vector3D(m3[0][1], m3[1][1], m3[2][1]));
+    checkVector(r.applyTo(Vector3D.PLUS_Z),
+                new Vector3D(m3[0][2], m3[1][2], m3[2][2]));
 
     double[][] m4 = { { 1.0,  0.0,  0.0 },
                       { 0.0, -1.0,  0.0 },
@@ -524,7 +524,7 @@ public void testSingularities() {
   @Test
   public void testQuaternion() {
 
-    Rotation r1 = new Rotation(new Cartesian3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
+    Rotation r1 = new Rotation(new Vector3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
     double n = 23.5;
     Rotation r2 = new Rotation(n * r1.getQ0(), n * r1.getQ1(),
                                n * r1.getQ2(), n * r1.getQ3(),
@@ -532,7 +532,7 @@ public void testQuaternion() {
     for (double x = -0.9; x < 0.9; x += 0.2) {
       for (double y = -0.9; y < 0.9; y += 0.2) {
         for (double z = -0.9; z < 0.9; z += 0.2) {
-          Cartesian3D u = new Cartesian3D(x, y, z);
+          Vector3D u = new Vector3D(x, y, z);
           checkVector(r2.applyTo(u), r1.applyTo(u));
         }
       }
@@ -546,14 +546,14 @@ public void testQuaternion() {
   @Test
   public void testApplyTo() {
 
-    Rotation r1 = new Rotation(new Cartesian3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
-    Rotation r2 = new Rotation(new Cartesian3D(-1, 3, 2), 0.3, RotationConvention.VECTOR_OPERATOR);
+    Rotation r1 = new Rotation(new Vector3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
+    Rotation r2 = new Rotation(new Vector3D(-1, 3, 2), 0.3, RotationConvention.VECTOR_OPERATOR);
     Rotation r3 = r2.applyTo(r1);
 
     for (double x = -0.9; x < 0.9; x += 0.2) {
       for (double y = -0.9; y < 0.9; y += 0.2) {
         for (double z = -0.9; z < 0.9; z += 0.2) {
-          Cartesian3D u = new Cartesian3D(x, y, z);
+          Vector3D u = new Vector3D(x, y, z);
           checkVector(r2.applyTo(r1.applyTo(u)), r3.applyTo(u));
         }
       }
@@ -564,14 +564,14 @@ public void testApplyTo() {
   @Test
   public void testComposeVectorOperator() {
 
-    Rotation r1 = new Rotation(new Cartesian3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
-    Rotation r2 = new Rotation(new Cartesian3D(-1, 3, 2), 0.3, RotationConvention.VECTOR_OPERATOR);
+    Rotation r1 = new Rotation(new Vector3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
+    Rotation r2 = new Rotation(new Vector3D(-1, 3, 2), 0.3, RotationConvention.VECTOR_OPERATOR);
     Rotation r3 = r2.compose(r1, RotationConvention.VECTOR_OPERATOR);
 
     for (double x = -0.9; x < 0.9; x += 0.2) {
       for (double y = -0.9; y < 0.9; y += 0.2) {
         for (double z = -0.9; z < 0.9; z += 0.2) {
-          Cartesian3D u = new Cartesian3D(x, y, z);
+          Vector3D u = new Vector3D(x, y, z);
           checkVector(r2.applyTo(r1.applyTo(u)), r3.applyTo(u));
         }
       }
@@ -582,8 +582,8 @@ public void testComposeVectorOperator() {
   @Test
   public void testComposeFrameTransform() {
 
-    Rotation r1 = new Rotation(new Cartesian3D(2, -3, 5), 1.7, RotationConvention.FRAME_TRANSFORM);
-    Rotation r2 = new Rotation(new Cartesian3D(-1, 3, 2), 0.3, RotationConvention.FRAME_TRANSFORM);
+    Rotation r1 = new Rotation(new Vector3D(2, -3, 5), 1.7, RotationConvention.FRAME_TRANSFORM);
+    Rotation r2 = new Rotation(new Vector3D(-1, 3, 2), 0.3, RotationConvention.FRAME_TRANSFORM);
     Rotation r3 = r2.compose(r1, RotationConvention.FRAME_TRANSFORM);
     Rotation r4 = r1.compose(r2, RotationConvention.VECTOR_OPERATOR);
     Assert.assertEquals(0.0, Rotation.distance(r3, r4), 1.0e-15);
@@ -591,7 +591,7 @@ public void testComposeFrameTransform() {
     for (double x = -0.9; x < 0.9; x += 0.2) {
       for (double y = -0.9; y < 0.9; y += 0.2) {
         for (double z = -0.9; z < 0.9; z += 0.2) {
-          Cartesian3D u = new Cartesian3D(x, y, z);
+          Vector3D u = new Vector3D(x, y, z);
           checkVector(r1.applyTo(r2.applyTo(u)), r3.applyTo(u));
         }
       }
@@ -602,14 +602,14 @@ public void testComposeFrameTransform() {
   @Test
   public void testApplyInverseToRotation() {
 
-    Rotation r1 = new Rotation(new Cartesian3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
-    Rotation r2 = new Rotation(new Cartesian3D(-1, 3, 2), 0.3, RotationConvention.VECTOR_OPERATOR);
+    Rotation r1 = new Rotation(new Vector3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
+    Rotation r2 = new Rotation(new Vector3D(-1, 3, 2), 0.3, RotationConvention.VECTOR_OPERATOR);
     Rotation r3 = r2.applyInverseTo(r1);
 
     for (double x = -0.9; x < 0.9; x += 0.2) {
       for (double y = -0.9; y < 0.9; y += 0.2) {
         for (double z = -0.9; z < 0.9; z += 0.2) {
-          Cartesian3D u = new Cartesian3D(x, y, z);
+          Vector3D u = new Vector3D(x, y, z);
           checkVector(r2.applyInverseTo(r1.applyTo(u)), r3.applyTo(u));
         }
       }
@@ -620,14 +620,14 @@ public void testApplyInverseToRotation() {
   @Test
   public void testComposeInverseVectorOperator() {
 
-    Rotation r1 = new Rotation(new Cartesian3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
-    Rotation r2 = new Rotation(new Cartesian3D(-1, 3, 2), 0.3, RotationConvention.VECTOR_OPERATOR);
+    Rotation r1 = new Rotation(new Vector3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
+    Rotation r2 = new Rotation(new Vector3D(-1, 3, 2), 0.3, RotationConvention.VECTOR_OPERATOR);
     Rotation r3 = r2.composeInverse(r1, RotationConvention.VECTOR_OPERATOR);
 
     for (double x = -0.9; x < 0.9; x += 0.2) {
       for (double y = -0.9; y < 0.9; y += 0.2) {
         for (double z = -0.9; z < 0.9; z += 0.2) {
-          Cartesian3D u = new Cartesian3D(x, y, z);
+          Vector3D u = new Vector3D(x, y, z);
           checkVector(r2.applyInverseTo(r1.applyTo(u)), r3.applyTo(u));
         }
       }
@@ -638,8 +638,8 @@ public void testComposeInverseVectorOperator() {
   @Test
   public void testComposeInverseFrameTransform() {
 
-    Rotation r1 = new Rotation(new Cartesian3D(2, -3, 5), 1.7, RotationConvention.FRAME_TRANSFORM);
-    Rotation r2 = new Rotation(new Cartesian3D(-1, 3, 2), 0.3, RotationConvention.FRAME_TRANSFORM);
+    Rotation r1 = new Rotation(new Vector3D(2, -3, 5), 1.7, RotationConvention.FRAME_TRANSFORM);
+    Rotation r2 = new Rotation(new Vector3D(-1, 3, 2), 0.3, RotationConvention.FRAME_TRANSFORM);
     Rotation r3 = r2.composeInverse(r1, RotationConvention.FRAME_TRANSFORM);
     Rotation r4 = r1.revert().composeInverse(r2.revert(), RotationConvention.VECTOR_OPERATOR);
     Assert.assertEquals(0.0, Rotation.distance(r3, r4), 1.0e-15);
@@ -647,7 +647,7 @@ public void testComposeInverseFrameTransform() {
     for (double x = -0.9; x < 0.9; x += 0.2) {
       for (double y = -0.9; y < 0.9; y += 0.2) {
         for (double z = -0.9; z < 0.9; z += 0.2) {
-          Cartesian3D u = new Cartesian3D(x, y, z);
+          Vector3D u = new Vector3D(x, y, z);
           checkVector(r1.applyTo(r2.applyInverseTo(u)), r3.applyTo(u));
         }
       }
@@ -658,13 +658,13 @@ public void testComposeInverseFrameTransform() {
   @Test
   public void testArray() {
 
-      Rotation r = new Rotation(new Cartesian3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
+      Rotation r = new Rotation(new Vector3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
 
       for (double x = -0.9; x < 0.9; x += 0.2) {
           for (double y = -0.9; y < 0.9; y += 0.2) {
               for (double z = -0.9; z < 0.9; z += 0.2) {
-                  Cartesian3D u = new Cartesian3D(x, y, z);
-                  Cartesian3D v = r.applyTo(u);
+                  Vector3D u = new Vector3D(x, y, z);
+                  Vector3D v = r.applyTo(u);
                   double[] inOut = new double[] { x, y, z };
                   r.applyTo(inOut, inOut);
                   Assert.assertEquals(v.getX(), inOut[0], 1.0e-10);
@@ -683,10 +683,10 @@ public void testArray() {
   @Test
   public void testApplyInverseTo() {
 
-    Rotation r = new Rotation(new Cartesian3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
+    Rotation r = new Rotation(new Vector3D(2, -3, 5), 1.7, RotationConvention.VECTOR_OPERATOR);
     for (double lambda = 0; lambda < 6.2; lambda += 0.2) {
       for (double phi = -1.55; phi < 1.55; phi += 0.2) {
-          Cartesian3D u = new Cartesian3D(Math.cos(lambda) * Math.cos(phi),
+          Vector3D u = new Vector3D(Math.cos(lambda) * Math.cos(phi),
                                     Math.sin(lambda) * Math.cos(phi),
                                     Math.sin(phi));
           r.applyInverseTo(r.applyTo(u));
@@ -698,7 +698,7 @@ public void testApplyInverseTo() {
     r = Rotation.IDENTITY;
     for (double lambda = 0; lambda < 6.2; lambda += 0.2) {
       for (double phi = -1.55; phi < 1.55; phi += 0.2) {
-          Cartesian3D u = new Cartesian3D(Math.cos(lambda) * Math.cos(phi),
+          Vector3D u = new Vector3D(Math.cos(lambda) * Math.cos(phi),
                                     Math.sin(lambda) * Math.cos(phi),
                                     Math.sin(phi));
           checkVector(u, r.applyInverseTo(r.applyTo(u)));
@@ -706,10 +706,10 @@ public void testApplyInverseTo() {
       }
     }
 
-    r = new Rotation(Cartesian3D.PLUS_K, Math.PI, RotationConvention.VECTOR_OPERATOR);
+    r = new Rotation(Vector3D.PLUS_Z, Math.PI, RotationConvention.VECTOR_OPERATOR);
     for (double lambda = 0; lambda < 6.2; lambda += 0.2) {
       for (double phi = -1.55; phi < 1.55; phi += 0.2) {
-          Cartesian3D u = new Cartesian3D(Math.cos(lambda) * Math.cos(phi),
+          Vector3D u = new Vector3D(Math.cos(lambda) * Math.cos(phi),
                                     Math.sin(lambda) * Math.cos(phi),
                                     Math.sin(phi));
           checkVector(u, r.applyInverseTo(r.applyTo(u)));
@@ -721,13 +721,13 @@ public void testApplyInverseTo() {
 
   @Test
   public void testIssue639() {
-      Cartesian3D u1 = new Cartesian3D(-1321008684645961.0 /  268435456.0,
+      Vector3D u1 = new Vector3D(-1321008684645961.0 /  268435456.0,
                                  -5774608829631843.0 /  268435456.0,
                                  -3822921525525679.0 / 4294967296.0);
-      Cartesian3D u2 =new Cartesian3D( -5712344449280879.0 /    2097152.0,
+      Vector3D u2 =new Vector3D( -5712344449280879.0 /    2097152.0,
                                  -2275058564560979.0 /    1048576.0,
                                   4423475992255071.0 /      65536.0);
-      Rotation rot = new Rotation(u1, u2, Cartesian3D.PLUS_I,Cartesian3D.PLUS_K);
+      Rotation rot = new Rotation(u1, u2, Vector3D.PLUS_X,Vector3D.PLUS_Z);
       Assert.assertEquals( 0.6228370359608200639829222, rot.getQ0(), 1.0e-15);
       Assert.assertEquals( 0.0257707621456498790029987, rot.getQ1(), 1.0e-15);
       Assert.assertEquals(-0.0000000002503012255839931, rot.getQ2(), 1.0e-15);
@@ -736,11 +736,11 @@ public void testIssue639() {
 
   @Test
   public void testIssue801() {
-      Cartesian3D u1 = new Cartesian3D(0.9999988431610581, -0.0015210774290851095, 0.0);
-      Cartesian3D u2 = new Cartesian3D(0.0, 0.0, 1.0);
+      Vector3D u1 = new Vector3D(0.9999988431610581, -0.0015210774290851095, 0.0);
+      Vector3D u2 = new Vector3D(0.0, 0.0, 1.0);
 
-      Cartesian3D v1 = new Cartesian3D(0.9999999999999999, 0.0, 0.0);
-      Cartesian3D v2 = new Cartesian3D(0.0, 0.0, -1.0);
+      Vector3D v1 = new Vector3D(0.9999999999999999, 0.0, 0.0);
+      Vector3D v2 = new Vector3D(0.0, 0.0, -1.0);
 
       Rotation quat = new Rotation(u1, u2, v1, v2);
       double q2 = quat.getQ0() * quat.getQ0() +
@@ -748,8 +748,8 @@ public void testIssue801() {
                   quat.getQ2() * quat.getQ2() +
                   quat.getQ3() * quat.getQ3();
       Assert.assertEquals(1.0, q2, 1.0e-14);
-      Assert.assertEquals(0.0, Cartesian3D.angle(v1, quat.applyTo(u1)), 1.0e-14);
-      Assert.assertEquals(0.0, Cartesian3D.angle(v2, quat.applyTo(u2)), 1.0e-14);
+      Assert.assertEquals(0.0, v1.angle(quat.applyTo(u1)), 1.0e-14);
+      Assert.assertEquals(0.0, v2.angle(quat.applyTo(u2)), 1.0e-14);
 
   }
 
@@ -759,13 +759,13 @@ public void testGithubPullRequest22A() {
       final double xRotation = Math.toDegrees(30);
       final double yRotation = Math.toDegrees(20);
       final double zRotation = Math.toDegrees(10);
-      final Cartesian3D startingVector = Cartesian3D.PLUS_I;
-      Cartesian3D appliedIndividually = startingVector;
+      final Vector3D startingVector = Vector3D.PLUS_X;
+      Vector3D appliedIndividually = startingVector;
       appliedIndividually = new Rotation(order, RotationConvention.FRAME_TRANSFORM, zRotation, 0, 0).applyTo(appliedIndividually);
       appliedIndividually = new Rotation(order, RotationConvention.FRAME_TRANSFORM, 0, yRotation, 0).applyTo(appliedIndividually);
       appliedIndividually = new Rotation(order, RotationConvention.FRAME_TRANSFORM, 0, 0, xRotation).applyTo(appliedIndividually);
 
-      final Cartesian3D bad = new Rotation(order, RotationConvention.FRAME_TRANSFORM, zRotation, yRotation, xRotation).applyTo(startingVector);
+      final Vector3D bad = new Rotation(order, RotationConvention.FRAME_TRANSFORM, zRotation, yRotation, xRotation).applyTo(startingVector);
 
       Assert.assertEquals(bad.getX(), appliedIndividually.getX(), 1e-12);
       Assert.assertEquals(bad.getY(), appliedIndividually.getY(), 1e-12);
@@ -778,8 +778,8 @@ public void testGithubPullRequest22B() {
       final double xRotation = Math.toDegrees(30);
       final double yRotation = Math.toDegrees(20);
       final double zRotation = Math.toDegrees(10);
-      final Cartesian3D startingVector = Cartesian3D.PLUS_I;
-      Cartesian3D appliedIndividually = startingVector;
+      final Vector3D startingVector = Vector3D.PLUS_X;
+      Vector3D appliedIndividually = startingVector;
       appliedIndividually = new Rotation(order, RotationConvention.FRAME_TRANSFORM, zRotation, 0, 0).applyTo(appliedIndividually);
       appliedIndividually = new Rotation(order, RotationConvention.FRAME_TRANSFORM, 0, yRotation, 0).applyTo(appliedIndividually);
       appliedIndividually = new Rotation(order, RotationConvention.FRAME_TRANSFORM, 0, 0, xRotation).applyTo(appliedIndividually);
@@ -790,14 +790,14 @@ public void testGithubPullRequest22B() {
       final Rotation composite = r1.compose(r2.compose(r3,
                                                        RotationConvention.FRAME_TRANSFORM),
                                             RotationConvention.FRAME_TRANSFORM);
-      final Cartesian3D good = composite.applyTo(startingVector);
+      final Vector3D good = composite.applyTo(startingVector);
 
       Assert.assertEquals(good.getX(), appliedIndividually.getX(), 1e-12);
       Assert.assertEquals(good.getY(), appliedIndividually.getY(), 1e-12);
       Assert.assertEquals(good.getZ(), appliedIndividually.getZ(), 1e-12);
   }
 
-  private void checkVector(Cartesian3D v1, Cartesian3D v2) {
+  private void checkVector(Vector3D v1, Vector3D v2) {
     Assert.assertTrue(v1.subtract(v2).getNorm() < 1.0e-10);
   }
 
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SubLineTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SubLineTest.java
index 055d86a..c076b9e 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SubLineTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/SubLineTest.java
@@ -19,12 +19,8 @@
 import java.util.List;
 
 import org.apache.commons.geometry.core.partitioning.RegionFactory;
-import org.apache.commons.geometry.euclidean.oned.Euclidean1D;
 import org.apache.commons.geometry.euclidean.oned.IntervalsSet;
-import org.apache.commons.geometry.euclidean.threed.Line;
-import org.apache.commons.geometry.euclidean.threed.Segment;
-import org.apache.commons.geometry.euclidean.threed.SubLine;
-import org.apache.commons.geometry.euclidean.threed.Cartesian3D;
+import org.apache.commons.geometry.euclidean.oned.Point1D;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -32,19 +28,19 @@
 
     @Test
     public void testEndPoints() {
-        Cartesian3D p1 = new Cartesian3D(-1, -7, 2);
-        Cartesian3D p2 = new Cartesian3D(7, -1, 0);
+        Point3D p1 = new Point3D(-1, -7, 2);
+        Point3D p2 = new Point3D(7, -1, 0);
         Segment segment = new Segment(p1, p2, new Line(p1, p2, 1.0e-10));
         SubLine sub = new SubLine(segment);
         List<Segment> segments = sub.getSegments();
         Assert.assertEquals(1, segments.size());
-        Assert.assertEquals(0.0, new Cartesian3D(-1, -7, 2).distance(segments.get(0).getStart()), 1.0e-10);
-        Assert.assertEquals(0.0, new Cartesian3D( 7, -1, 0).distance(segments.get(0).getEnd()), 1.0e-10);
+        Assert.assertEquals(0.0, new Point3D(-1, -7, 2).distance(segments.get(0).getStart()), 1.0e-10);
+        Assert.assertEquals(0.0, new Point3D( 7, -1, 0).distance(segments.get(0).getEnd()), 1.0e-10);
     }
 
     @Test
     public void testNoEndPoints() {
-        SubLine wholeLine = new Line(new Cartesian3D(-1, 7, 2), new Cartesian3D(7, 1, 0), 1.0e-10).wholeLine();
+        SubLine wholeLine = new Line(new Point3D(-1, 7, 2), new Point3D(7, 1, 0), 1.0e-10).wholeLine();
         List<Segment> segments = wholeLine.getSegments();
         Assert.assertEquals(1, segments.size());
         Assert.assertTrue(Double.isInfinite(segments.get(0).getStart().getX()) &&
@@ -63,16 +59,16 @@ public void testNoEndPoints() {
 
     @Test
     public void testNoSegments() {
-        SubLine empty = new SubLine(new Line(new Cartesian3D(-1, -7, 2), new Cartesian3D(7, -1, 0), 1.0e-10),
-                                    (IntervalsSet) new RegionFactory<Euclidean1D>().getComplement(new IntervalsSet(1.0e-10)));
+        SubLine empty = new SubLine(new Line(new Point3D(-1, -7, 2), new Point3D(7, -1, 0), 1.0e-10),
+                                    (IntervalsSet) new RegionFactory<Point1D>().getComplement(new IntervalsSet(1.0e-10)));
         List<Segment> segments = empty.getSegments();
         Assert.assertEquals(0, segments.size());
     }
 
     @Test
     public void testSeveralSegments() {
-        SubLine twoSubs = new SubLine(new Line(new Cartesian3D(-1, -7, 2), new Cartesian3D(7, -1, 0), 1.0e-10),
-                                      (IntervalsSet) new RegionFactory<Euclidean1D>().union(new IntervalsSet(1, 2, 1.0e-10),
+        SubLine twoSubs = new SubLine(new Line(new Point3D(-1, -7, 2), new Point3D(7, -1, 0), 1.0e-10),
+                                      (IntervalsSet) new RegionFactory<Point1D>().union(new IntervalsSet(1, 2, 1.0e-10),
                                                                                             new IntervalsSet(3, 4, 1.0e-10)));
         List<Segment> segments = twoSubs.getSegments();
         Assert.assertEquals(2, segments.size());
@@ -80,7 +76,7 @@ public void testSeveralSegments() {
 
     @Test
     public void testHalfInfiniteNeg() {
-        SubLine empty = new SubLine(new Line(new Cartesian3D(-1, -7, 2), new Cartesian3D(7, -1, -2), 1.0e-10),
+        SubLine empty = new SubLine(new Line(new Point3D(-1, -7, 2), new Point3D(7, -1, -2), 1.0e-10),
                                     new IntervalsSet(Double.NEGATIVE_INFINITY, 0.0, 1.0e-10));
         List<Segment> segments = empty.getSegments();
         Assert.assertEquals(1, segments.size());
@@ -90,16 +86,16 @@ public void testHalfInfiniteNeg() {
                           segments.get(0).getStart().getY() < 0);
         Assert.assertTrue(Double.isInfinite(segments.get(0).getStart().getZ()) &&
                           segments.get(0).getStart().getZ() > 0);
-        Assert.assertEquals(0.0, new Cartesian3D(3, -4, 0).distance(segments.get(0).getEnd()), 1.0e-10);
+        Assert.assertEquals(0.0, new Point3D(3, -4, 0).distance(segments.get(0).getEnd()), 1.0e-10);
     }
 
     @Test
     public void testHalfInfinitePos() {
-        SubLine empty = new SubLine(new Line(new Cartesian3D(-1, -7, 2), new Cartesian3D(7, -1, -2), 1.0e-10),
+        SubLine empty = new SubLine(new Line(new Point3D(-1, -7, 2), new Point3D(7, -1, -2), 1.0e-10),
                                     new IntervalsSet(0.0, Double.POSITIVE_INFINITY, 1.0e-10));
         List<Segment> segments = empty.getSegments();
         Assert.assertEquals(1, segments.size());
-        Assert.assertEquals(0.0, new Cartesian3D(3, -4, 0).distance(segments.get(0).getStart()), 1.0e-10);
+        Assert.assertEquals(0.0, new Point3D(3, -4, 0).distance(segments.get(0).getStart()), 1.0e-10);
         Assert.assertTrue(Double.isInfinite(segments.get(0).getEnd().getX()) &&
                           segments.get(0).getEnd().getX() > 0);
         Assert.assertTrue(Double.isInfinite(segments.get(0).getEnd().getY()) &&
@@ -110,56 +106,56 @@ public void testHalfInfinitePos() {
 
     @Test
     public void testIntersectionInsideInside() {
-        SubLine sub1 = new SubLine(new Cartesian3D(1, 1, 1), new Cartesian3D(3, 1, 1), 1.0e-10);
-        SubLine sub2 = new SubLine(new Cartesian3D(2, 0, 0), new Cartesian3D(2, 2, 2), 1.0e-10);
-        Assert.assertEquals(0.0, new Cartesian3D(2, 1, 1).distance(sub1.intersection(sub2, true)),  1.0e-12);
-        Assert.assertEquals(0.0, new Cartesian3D(2, 1, 1).distance(sub1.intersection(sub2, false)), 1.0e-12);
+        SubLine sub1 = new SubLine(new Point3D(1, 1, 1), new Point3D(3, 1, 1), 1.0e-10);
+        SubLine sub2 = new SubLine(new Point3D(2, 0, 0), new Point3D(2, 2, 2), 1.0e-10);
+        Assert.assertEquals(0.0, new Point3D(2, 1, 1).distance(sub1.intersection(sub2, true)),  1.0e-12);
+        Assert.assertEquals(0.0, new Point3D(2, 1, 1).distance(sub1.intersection(sub2, false)), 1.0e-12);
     }
 
     @Test
     public void testIntersectionInsideBoundary() {
-        SubLine sub1 = new SubLine(new Cartesian3D(1, 1, 1), new Cartesian3D(3, 1, 1), 1.0e-10);
-        SubLine sub2 = new SubLine(new Cartesian3D(2, 0, 0), new Cartesian3D(2, 1, 1), 1.0e-10);
-        Assert.assertEquals(0.0, new Cartesian3D(2, 1, 1).distance(sub1.intersection(sub2, true)),  1.0e-12);
+        SubLine sub1 = new SubLine(new Point3D(1, 1, 1), new Point3D(3, 1, 1), 1.0e-10);
+        SubLine sub2 = new SubLine(new Point3D(2, 0, 0), new Point3D(2, 1, 1), 1.0e-10);
+        Assert.assertEquals(0.0, new Point3D(2, 1, 1).distance(sub1.intersection(sub2, true)),  1.0e-12);
         Assert.assertNull(sub1.intersection(sub2, false));
     }
 
     @Test
     public void testIntersectionInsideOutside() {
-        SubLine sub1 = new SubLine(new Cartesian3D(1, 1, 1), new Cartesian3D(3, 1, 1), 1.0e-10);
-        SubLine sub2 = new SubLine(new Cartesian3D(2, 0, 0), new Cartesian3D(2, 0.5, 0.5), 1.0e-10);
+        SubLine sub1 = new SubLine(new Point3D(1, 1, 1), new Point3D(3, 1, 1), 1.0e-10);
+        SubLine sub2 = new SubLine(new Point3D(2, 0, 0), new Point3D(2, 0.5, 0.5), 1.0e-10);
         Assert.assertNull(sub1.intersection(sub2, true));
         Assert.assertNull(sub1.intersection(sub2, false));
     }
 
     @Test
     public void testIntersectionBoundaryBoundary() {
-        SubLine sub1 = new SubLine(new Cartesian3D(1, 1, 1), new Cartesian3D(2, 1, 1), 1.0e-10);
-        SubLine sub2 = new SubLine(new Cartesian3D(2, 0, 0), new Cartesian3D(2, 1, 1), 1.0e-10);
-        Assert.assertEquals(0.0, new Cartesian3D(2, 1, 1).distance(sub1.intersection(sub2, true)),  1.0e-12);
+        SubLine sub1 = new SubLine(new Point3D(1, 1, 1), new Point3D(2, 1, 1), 1.0e-10);
+        SubLine sub2 = new SubLine(new Point3D(2, 0, 0), new Point3D(2, 1, 1), 1.0e-10);
+        Assert.assertEquals(0.0, new Point3D(2, 1, 1).distance(sub1.intersection(sub2, true)),  1.0e-12);
         Assert.assertNull(sub1.intersection(sub2, false));
     }
 
     @Test
     public void testIntersectionBoundaryOutside() {
-        SubLine sub1 = new SubLine(new Cartesian3D(1, 1, 1), new Cartesian3D(2, 1, 1), 1.0e-10);
-        SubLine sub2 = new SubLine(new Cartesian3D(2, 0, 0), new Cartesian3D(2, 0.5, 0.5), 1.0e-10);
+        SubLine sub1 = new SubLine(new Point3D(1, 1, 1), new Point3D(2, 1, 1), 1.0e-10);
+        SubLine sub2 = new SubLine(new Point3D(2, 0, 0), new Point3D(2, 0.5, 0.5), 1.0e-10);
         Assert.assertNull(sub1.intersection(sub2, true));
         Assert.assertNull(sub1.intersection(sub2, false));
     }
 
     @Test
     public void testIntersectionOutsideOutside() {
-        SubLine sub1 = new SubLine(new Cartesian3D(1, 1, 1), new Cartesian3D(1.5, 1, 1), 1.0e-10);
-        SubLine sub2 = new SubLine(new Cartesian3D(2, 0, 0), new Cartesian3D(2, 0.5, 0.5), 1.0e-10);
+        SubLine sub1 = new SubLine(new Point3D(1, 1, 1), new Point3D(1.5, 1, 1), 1.0e-10);
+        SubLine sub2 = new SubLine(new Point3D(2, 0, 0), new Point3D(2, 0.5, 0.5), 1.0e-10);
         Assert.assertNull(sub1.intersection(sub2, true));
         Assert.assertNull(sub1.intersection(sub2, false));
     }
 
     @Test
     public void testIntersectionNotIntersecting() {
-        SubLine sub1 = new SubLine(new Cartesian3D(1, 1, 1), new Cartesian3D(1.5, 1, 1), 1.0e-10);
-        SubLine sub2 = new SubLine(new Cartesian3D(2, 3, 0), new Cartesian3D(2, 3, 0.5), 1.0e-10);
+        SubLine sub1 = new SubLine(new Point3D(1, 1, 1), new Point3D(1.5, 1, 1), 1.0e-10);
+        SubLine sub2 = new SubLine(new Point3D(2, 3, 0), new Point3D(2, 3, 0.5), 1.0e-10);
         Assert.assertNull(sub1.intersection(sub2, true));
         Assert.assertNull(sub1.intersection(sub2, false));
     }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
index 528f3f0..f2b4fcc 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
@@ -17,12 +17,9 @@
 
 package org.apache.commons.geometry.euclidean.threed;
 
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.text.NumberFormat;
-import java.util.Locale;
+import java.util.regex.Pattern;
 
-import org.apache.commons.geometry.core.Space;
+import org.apache.commons.geometry.core.Geometry;
 import org.apache.commons.numbers.core.Precision;
 import org.apache.commons.rng.UniformRandomProvider;
 import org.apache.commons.rng.simple.RandomSource;
@@ -30,378 +27,741 @@
 import org.junit.Test;
 
 public class Vector3DTest {
-    @Test
-    public void testConstructors() {
-        double r = Math.sqrt(2) /2;
-        checkVector(new Cartesian3D(2, new Cartesian3D(Math.PI / 3, -Math.PI / 4)),
-                    r, r * Math.sqrt(3), -2 * r);
-        checkVector(new Cartesian3D(2, Cartesian3D.PLUS_I,
-                                 -3, Cartesian3D.MINUS_K),
-                    2, 0, 3);
-        checkVector(new Cartesian3D(2, Cartesian3D.PLUS_I,
-                                 5, Cartesian3D.PLUS_J,
-                                 -3, Cartesian3D.MINUS_K),
-                    2, 5, 3);
-        checkVector(new Cartesian3D(2, Cartesian3D.PLUS_I,
-                                 5, Cartesian3D.PLUS_J,
-                                 5, Cartesian3D.MINUS_J,
-                                 -3, Cartesian3D.MINUS_K),
-                    2, 0, 3);
-        checkVector(new Cartesian3D(new double[] { 2,  5,  -3 }),
-                    2, 5, -3);
-    }
+
+    private static final double EPS = Math.ulp(1d);
 
     @Test
-    public void testSpace() {
-        Space space = new Cartesian3D(1, 2, 2).getSpace();
-        Assert.assertEquals(3, space.getDimension());
-        Assert.assertEquals(2, space.getSubSpace().getDimension());
+    public void testConstants() {
+        // act/assert
+        checkVector(Vector3D.ZERO, 0, 0, 0);
+
+        checkVector(Vector3D.PLUS_X, 1, 0, 0);
+        checkVector(Vector3D.MINUS_X, -1, 0, 0);
+
+        checkVector(Vector3D.PLUS_Y, 0, 1, 0);
+        checkVector(Vector3D.MINUS_Y, 0, -1, 0);
+
+        checkVector(Vector3D.PLUS_Z, 0, 0, 1);
+        checkVector(Vector3D.MINUS_Z, 0, 0, -1);
+
+        checkVector(Vector3D.NaN, Double.NaN, Double.NaN, Double.NaN);
+        checkVector(Vector3D.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+        checkVector(Vector3D.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
     }
 
     @Test
     public void testZero() {
-        Assert.assertEquals(0, new Cartesian3D(1, 2, 2).getZero().getNorm(), 1.0e-15);
+        // act
+        Vector3D zero = Vector3D.of(1, 2, 3).getZero();
+
+        // assert
+        checkVector(zero, 0, 0, 0);
+        Assert.assertEquals(0, zero.getNorm(), EPS);
     }
 
     @Test
-    public void testEquals() {
-        Cartesian3D u1 = new Cartesian3D(1, 2, 3);
-        Cartesian3D u2 = new Cartesian3D(1, 2, 3);
-        Assert.assertTrue(u1.equals(u1));
-        Assert.assertTrue(u1.equals(u2));
-        Assert.assertFalse(u1.equals(new Rotation(1, 0, 0, 0, false)));
-        Assert.assertFalse(u1.equals(new Cartesian3D(1, 2, 3 + 10 * Precision.EPSILON)));
-        Assert.assertFalse(u1.equals(new Cartesian3D(1, 2 + 10 * Precision.EPSILON, 3)));
-        Assert.assertFalse(u1.equals(new Cartesian3D(1 + 10 * Precision.EPSILON, 2, 3)));
-        Assert.assertTrue(new Cartesian3D(0, Double.NaN, 0).equals(new Cartesian3D(0, 0, Double.NaN)));
+    public void testAsPoint() {
+        // act/assert
+        checkPoint(Vector3D.of(1, 2, 3).asPoint(), 1, 2, 3);
+        checkPoint(Vector3D.of(-1, -2, -3).asPoint(), -1, -2, -3);
+        checkPoint(Vector3D.of(Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY).asPoint(),
+                Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
     }
 
     @Test
-    public void testHash() {
-        Assert.assertEquals(new Cartesian3D(0, Double.NaN, 0).hashCode(), new Cartesian3D(0, 0, Double.NaN).hashCode());
-        Cartesian3D u = new Cartesian3D(1, 2, 3);
-        Cartesian3D v = new Cartesian3D(1, 2, 3 + 10 * Precision.EPSILON);
-        Assert.assertTrue(u.hashCode() != v.hashCode());
+    public void testNorm1() {
+        // act/assert
+        Assert.assertEquals(0.0, Vector3D.ZERO.getNorm1(), EPS);
+        Assert.assertEquals(9.0, Vector3D.of(2, -3, 4).getNorm1(), EPS);
+        Assert.assertEquals(9.0, Vector3D.of(-2, 3, -4).getNorm1(), EPS);
     }
 
     @Test
-    public void testInfinite() {
-        Assert.assertTrue(new Cartesian3D(1, 1, Double.NEGATIVE_INFINITY).isInfinite());
-        Assert.assertTrue(new Cartesian3D(1, Double.NEGATIVE_INFINITY, 1).isInfinite());
-        Assert.assertTrue(new Cartesian3D(Double.NEGATIVE_INFINITY, 1, 1).isInfinite());
-        Assert.assertFalse(new Cartesian3D(1, 1, 2).isInfinite());
-        Assert.assertFalse(new Cartesian3D(1, Double.NaN, Double.NEGATIVE_INFINITY).isInfinite());
+    public void testNorm() {
+        // act/assert
+        Assert.assertEquals(0.0, Vector3D.ZERO.getNorm(), 0);
+        Assert.assertEquals(Math.sqrt(29), Vector3D.of(2, 3, 4).getNorm(), EPS);
+        Assert.assertEquals(Math.sqrt(29), Vector3D.of(-2, -3, -4).getNorm(), EPS);
     }
 
     @Test
-    public void testNaN() {
-        Assert.assertTrue(new Cartesian3D(1, 1, Double.NaN).isNaN());
-        Assert.assertTrue(new Cartesian3D(1, Double.NaN, 1).isNaN());
-        Assert.assertTrue(new Cartesian3D(Double.NaN, 1, 1).isNaN());
-        Assert.assertFalse(new Cartesian3D(1, 1, 2).isNaN());
-        Assert.assertFalse(new Cartesian3D(1, 1, Double.NEGATIVE_INFINITY).isNaN());
+    public void testNormSq() {
+        // act/assert
+        Assert.assertEquals(0.0, Vector3D.ZERO.getNorm(), 0);
+        Assert.assertEquals(29, Vector3D.of(2, 3, 4).getNormSq(), EPS);
+        Assert.assertEquals(29, Vector3D.of(-2, -3, -4).getNormSq(), EPS);
     }
 
     @Test
-    public void testToString() {
-        Assert.assertEquals("{3; 2; 1}", new Cartesian3D(3, 2, 1).toString());
-        NumberFormat format = new DecimalFormat("0.000", new DecimalFormatSymbols(Locale.US));
-        Assert.assertEquals("{3.000; 2.000; 1.000}", new Cartesian3D(3, 2, 1).toString(format));
+    public void testNormInf() {
+        // act/assert
+        Assert.assertEquals(0.0, Vector3D.ZERO.getNormInf(), 0);
+        Assert.assertEquals(4, Vector3D.of(2, 3, 4).getNormInf(), EPS);
+        Assert.assertEquals(4, Vector3D.of(-2, -3, -4).getNormInf(), EPS);
     }
 
-    @Test(expected = IllegalArgumentException.class)
-    public void testWrongDimension() {
-        new Cartesian3D(new double[] { 2,  5 });
+    @Test
+    public void testAdd() {
+        // arrange
+        Vector3D v1 = Vector3D.of(1, 2, 3);
+        Vector3D v2 = Vector3D.of(-4, -5, -6);
+        Vector3D v3 = Vector3D.of(7, 8, 9);
+
+        // act/assert
+        checkVector(v1.add(v1), 2, 4, 6);
+
+        checkVector(v1.add(v2), -3, -3, -3);
+        checkVector(v2.add(v1), -3, -3, -3);
+
+        checkVector(v1.add(v3), 8, 10, 12);
+        checkVector(v3.add(v1), 8, 10, 12);
     }
 
     @Test
-    public void testCoordinates() {
-        Cartesian3D v = new Cartesian3D(1, 2, 3);
-        Assert.assertTrue(Math.abs(v.getX() - 1) < 1.0e-12);
-        Assert.assertTrue(Math.abs(v.getY() - 2) < 1.0e-12);
-        Assert.assertTrue(Math.abs(v.getZ() - 3) < 1.0e-12);
-        double[] coordinates = v.toArray();
-        Assert.assertTrue(Math.abs(coordinates[0] - 1) < 1.0e-12);
-        Assert.assertTrue(Math.abs(coordinates[1] - 2) < 1.0e-12);
-        Assert.assertTrue(Math.abs(coordinates[2] - 3) < 1.0e-12);
+    public void testAdd_scaled() {
+        // arrange
+        Vector3D v1 = Vector3D.of(1, 2, 3);
+        Vector3D v2 = Vector3D.of(-4, -5, -6);
+        Vector3D v3 = Vector3D.of(7, 8, 9);
+
+        // act/assert
+        checkVector(v1.add(0, v1), 1, 2, 3);
+        checkVector(v1.add(0.5, v1), 1.5, 3, 4.5);
+        checkVector(v1.add(1, v1), 2, 4, 6);
+
+        checkVector(v1.add(2, v2), -7, -8, -9);
+        checkVector(v2.add(2, v1), -2, -1, -0);
+
+        checkVector(v1.add(-2, v3), -13, -14, -15);
+        checkVector(v3.add(-2, v1), 5, 4, 3);
     }
 
     @Test
-    public void testNorm1() {
-        Assert.assertEquals(0.0, Cartesian3D.ZERO.getNorm1(), 0);
-        Assert.assertEquals(6.0, new Cartesian3D(1, -2, 3).getNorm1(), 0);
+    public void testSubtract() {
+        // arrange
+        Vector3D v1 = Vector3D.of(1, 2, 3);
+        Vector3D v2 = Vector3D.of(-4, -5, -6);
+        Vector3D v3 = Vector3D.of(7, 8, 9);
+
+        // act/assert
+        checkVector(v1.subtract(v1), 0, 0, 0);
+
+        checkVector(v1.subtract(v2), 5, 7, 9);
+        checkVector(v2.subtract(v1), -5, -7, -9);
+
+        checkVector(v1.subtract(v3), -6, -6, -6);
+        checkVector(v3.subtract(v1), 6, 6, 6);
     }
 
     @Test
-    public void testNorm() {
-        Assert.assertEquals(0.0, Cartesian3D.ZERO.getNorm(), 0);
-        Assert.assertEquals(Math.sqrt(14), new Cartesian3D(1, 2, 3).getNorm(), 1.0e-12);
+    public void testSubtract_scaled() {
+        // arrange
+        Vector3D v1 = Vector3D.of(1, 2, 3);
+        Vector3D v2 = Vector3D.of(-4, -5, -6);
+        Vector3D v3 = Vector3D.of(7, 8, 9);
+
+        // act/assert
+        checkVector(v1.subtract(0, v1), 1, 2, 3);
+        checkVector(v1.subtract(0.5, v1), 0.5, 1, 1.5);
+        checkVector(v1.subtract(1, v1), 0, 0, 0);
+
+        checkVector(v1.subtract(2, v2), 9, 12, 15);
+        checkVector(v2.subtract(2, v1), -6, -9, -12);
+
+        checkVector(v1.subtract(-2, v3), 15, 18, 21);
+        checkVector(v3.subtract(-2, v1), 9, 12, 15);
     }
 
     @Test
-    public void testNormSq() {
-        Assert.assertEquals(0.0, new Cartesian3D(0, 0, 0).getNormSq(), 0);
-        Assert.assertEquals(14, new Cartesian3D(1, 2, 3).getNormSq(), 1.0e-12);
+    public void testNegate() {
+        // act/assert
+        checkVector(Vector3D.of(0.1, 2.5, 1.3).negate(), -0.1, -2.5, -1.3);
+        checkVector(Vector3D.of(-0.1, -2.5, -1.3).negate(), 0.1, 2.5, 1.3);
     }
 
     @Test
-    public void testNormInf() {
-        Assert.assertEquals(0.0, Cartesian3D.ZERO.getNormInf(), 0);
-        Assert.assertEquals(3.0, new Cartesian3D(1, -2, 3).getNormInf(), 0);
+    public void testNormalize() {
+        // arrange
+        double invSqrt3 = 1 / Math.sqrt(3);
+
+        // act/assert
+        checkVector(Vector3D.of(100, 0, 0).normalize(), 1, 0, 0);
+        checkVector(Vector3D.of(-100, 0, 0).normalize(), -1, 0, 0);
+
+        checkVector(Vector3D.of(0, 100, 0).normalize(), 0, 1, 0);
+        checkVector(Vector3D.of(0, -100, 0).normalize(), 0, -1, 0);
+
+        checkVector(Vector3D.of(0, 0, 100).normalize(), 0, 0, 1);
+        checkVector(Vector3D.of(0, 0, -100).normalize(), 0, 0, -1);
+
+        checkVector(Vector3D.of(2, 2, 2).normalize(), invSqrt3, invSqrt3, invSqrt3);
+        checkVector(Vector3D.of(-2, -2, -2).normalize(), -invSqrt3, -invSqrt3, -invSqrt3);
+
+        Assert.assertEquals(1.0, Vector3D.of(5, -4, 2).normalize().getNorm(), 1.0e-12);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testNormalize_zeroNorm() {
+        // act/assert
+        Vector3D.ZERO.normalize();
     }
 
     @Test
-    public void testDistance1() {
-        Cartesian3D v1 = new Cartesian3D(1, -2, 3);
-        Cartesian3D v2 = new Cartesian3D(-4, 2, 0);
-        Assert.assertEquals(0.0, Cartesian3D.distance1(Cartesian3D.MINUS_I, Cartesian3D.MINUS_I), 0);
-        Assert.assertEquals(12.0, Cartesian3D.distance1(v1, v2), 1.0e-12);
-        Assert.assertEquals(v1.subtract(v2).getNorm1(), Cartesian3D.distance1(v1, v2), 1.0e-12);
+    public void testOrthogonal() {
+        // arrange
+        Vector3D v1 = Vector3D.of(0.1, 2.5, 1.3);
+        Vector3D v2 = Vector3D.of(2.3, -0.003, 7.6);
+        Vector3D v3 = Vector3D.of(-1.7, 1.4, 0.2);
+        Vector3D v4 = Vector3D.of(4.2, 0.1, -1.8);
+
+        // act/assert
+        Assert.assertEquals(0.0, v1.dotProduct(v1.orthogonal()), EPS);
+        Assert.assertEquals(0.0, v2.dotProduct(v2.orthogonal()), EPS);
+        Assert.assertEquals(0.0, v3.dotProduct(v3.orthogonal()), EPS);
+        Assert.assertEquals(0.0, v4.dotProduct(v4.orthogonal()), EPS);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testOrthogonal_zeroNorm() {
+        // act/assert
+        Vector3D.ZERO.orthogonal();
     }
 
     @Test
-    public void testDistance() {
-        Cartesian3D v1 = new Cartesian3D(1, -2, 3);
-        Cartesian3D v2 = new Cartesian3D(-4, 2, 0);
-        Assert.assertEquals(0.0, Cartesian3D.distance(Cartesian3D.MINUS_I, Cartesian3D.MINUS_I), 0);
-        Assert.assertEquals(Math.sqrt(50), Cartesian3D.distance(v1, v2), 1.0e-12);
-        Assert.assertEquals(v1.subtract(v2).getNorm(), Cartesian3D.distance(v1, v2), 1.0e-12);
+    public void testAngle() {
+        // arrange
+        double tolerance = 1e-10;
+
+        Vector3D v1 = Vector3D.of(1, 2, 3);
+        Vector3D v2 = Vector3D.of(4, 5, 6);
+
+        // act/assert
+        Assert.assertEquals(0.22572612855273393616, v1.angle(v2), tolerance);
+        Assert.assertEquals(7.98595620686106654517199e-8, v1.angle(Vector3D.of(2, 4, 6.000001)), tolerance);
+        Assert.assertEquals(3.14159257373023116985197793156, v1.angle(Vector3D.of(-2, -4, -6.000001)), tolerance);
+
+        Assert.assertEquals(0.0, Vector3D.PLUS_X.angle(Vector3D.PLUS_X), tolerance);
+        Assert.assertEquals(Geometry.PI, Vector3D.PLUS_X.angle(Vector3D.MINUS_X), tolerance);
+
+        Assert.assertEquals(Geometry.HALF_PI, Vector3D.PLUS_X.angle(Vector3D.PLUS_Y), tolerance);
+        Assert.assertEquals(Geometry.HALF_PI, Vector3D.PLUS_X.angle(Vector3D.MINUS_Y), tolerance);
+        Assert.assertEquals(Geometry.HALF_PI, Vector3D.PLUS_X.angle(Vector3D.PLUS_Z), tolerance);
+        Assert.assertEquals(Geometry.HALF_PI, Vector3D.PLUS_X.angle(Vector3D.MINUS_Z), tolerance);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testAngle_zeroNorm() {
+        // act/assert
+        Vector3D.ZERO.angle(Vector3D.PLUS_X);
     }
 
     @Test
-    public void testDistanceSq() {
-        Cartesian3D v1 = new Cartesian3D(1, -2, 3);
-        Cartesian3D v2 = new Cartesian3D(-4, 2, 0);
-        Assert.assertEquals(0.0, Cartesian3D.distanceSq(Cartesian3D.MINUS_I, Cartesian3D.MINUS_I), 0);
-        Assert.assertEquals(50.0, Cartesian3D.distanceSq(v1, v2), 1.0e-12);
-        Assert.assertEquals(Cartesian3D.distance(v1, v2) * Cartesian3D.distance(v1, v2),
-                            Cartesian3D.distanceSq(v1, v2), 1.0e-12);
+    public void testAngle_angularSeparation() {
+        // arrange
+        Vector3D v1 = Vector3D.of(2, -1, 4);
+
+        Vector3D  k = v1.normalize();
+        Vector3D  i = k.orthogonal();
+        Vector3D v2 = k.scalarMultiply(Math.cos(1.2)).add(i.scalarMultiply(Math.sin(1.2)));
+
+        // act/assert
+        Assert.assertTrue(Math.abs(v1.angle(v2) - 1.2) < 1.0e-12);
   }
 
     @Test
-    public void testDistanceInf() {
-        Cartesian3D v1 = new Cartesian3D(1, -2, 3);
-        Cartesian3D v2 = new Cartesian3D(-4, 2, 0);
-        Assert.assertEquals(0.0, Cartesian3D.distanceInf(Cartesian3D.MINUS_I, Cartesian3D.MINUS_I), 0);
-        Assert.assertEquals(5.0, Cartesian3D.distanceInf(v1, v2), 1.0e-12);
-        Assert.assertEquals(v1.subtract(v2).getNormInf(), Cartesian3D.distanceInf(v1, v2), 1.0e-12);
+    public void testAngle_static() {
+        // arrange
+        double tolerance = 1e-10;
+
+        Vector3D v1 = Vector3D.of(1, 2, 3);
+        Vector3D v2 = Vector3D.of(4, 5, 6);
+
+        // act/assert
+        Assert.assertEquals(0.22572612855273393616, Vector3D.angle(v1, v2), tolerance);
+        Assert.assertEquals(7.98595620686106654517199e-8, Vector3D.angle(v1, Vector3D.of(2, 4, 6.000001)), tolerance);
+        Assert.assertEquals(3.14159257373023116985197793156, Vector3D.angle(v1, Vector3D.of(-2, -4, -6.000001)), tolerance);
+
+        Assert.assertEquals(0.0, Vector3D.angle(Vector3D.PLUS_X, Vector3D.PLUS_X), tolerance);
+        Assert.assertEquals(Geometry.PI, Vector3D.angle(Vector3D.PLUS_X, Vector3D.MINUS_X), tolerance);
+
+        Assert.assertEquals(Geometry.HALF_PI, Vector3D.angle(Vector3D.PLUS_X, Vector3D.PLUS_Y), tolerance);
+        Assert.assertEquals(Geometry.HALF_PI, Vector3D.angle(Vector3D.PLUS_X, Vector3D.MINUS_Y), tolerance);
+        Assert.assertEquals(Geometry.HALF_PI, Vector3D.angle(Vector3D.PLUS_X, Vector3D.PLUS_Z), tolerance);
+        Assert.assertEquals(Geometry.HALF_PI, Vector3D.angle(Vector3D.PLUS_X, Vector3D.MINUS_Z), tolerance);
     }
 
     @Test
-    public void testSubtract() {
-        Cartesian3D v1 = new Cartesian3D(1, 2, 3);
-        Cartesian3D v2 = new Cartesian3D(-3, -2, -1);
-        v1 = v1.subtract(v2);
-        checkVector(v1, 4, 4, 4);
+    public void testCrossProduct() {
+        // act/assert
+        checkVector(Vector3D.PLUS_X.crossProduct(Vector3D.PLUS_Y), 0, 0, 1);
+        checkVector(Vector3D.PLUS_X.crossProduct(Vector3D.MINUS_Y), 0, 0, -1);
+
+        checkVector(Vector3D.MINUS_X.crossProduct(Vector3D.MINUS_Y), 0, 0, 1);
+        checkVector(Vector3D.MINUS_X.crossProduct(Vector3D.PLUS_Y), 0, 0, -1);
 
-        checkVector(v2.subtract(v1), -7, -6, -5);
-        checkVector(v2.subtract(3, v1), -15, -14, -13);
+        checkVector(Vector3D.of(2, 1, -4).crossProduct(Vector3D.of(3, 1, -1)), 3, -10, -1);
+
+        double invSqrt6 = 1 / Math.sqrt(6);
+        checkVector(Vector3D.of(1, 1, 1).crossProduct(Vector3D.of(-1, 0, 1)).normalize(), invSqrt6, - 2 * invSqrt6, invSqrt6);
     }
 
     @Test
-    public void testAdd() {
-        Cartesian3D v1 = new Cartesian3D(1, 2, 3);
-        Cartesian3D v2 = new Cartesian3D(-3, -2, -1);
-        v1 = v1.add(v2);
-        checkVector(v1, -2, 0, 2);
+    public void testCrossProduct_nearlyAntiParallel() {
+        // the vectors u1 and u2 are nearly but not exactly anti-parallel
+        // (7.31e-16 degrees from 180 degrees) naive cross product (i.e.
+        // computing u1.x * u2.x + u1.y * u2.y + u1.z * u2.z
+        // leads to a result of   [0.0009765, -0.0001220, -0.0039062],
+        // instead of the correct [0.0006913, -0.0001254, -0.0007909]
+
+        // arrange
+        final Vector3D u1 = Vector3D.of(-1321008684645961.0 /   268435456.0,
+                                         -5774608829631843.0 /   268435456.0,
+                                         -7645843051051357.0 /  8589934592.0);
+        final Vector3D u2 = Vector3D.of( 1796571811118507.0 /  2147483648.0,
+                                          7853468008299307.0 /  2147483648.0,
+                                          2599586637357461.0 / 17179869184.0);
+        final Vector3D u3 = Vector3D.of(12753243807587107.0 / 18446744073709551616.0,
+                                         -2313766922703915.0 / 18446744073709551616.0,
+                                          -227970081415313.0 /   288230376151711744.0);
+
+        // act
+        Vector3D cNaive = Vector3D.of(u1.getY() * u2.getZ() - u1.getZ() * u2.getY(),
+                                       u1.getZ() * u2.getX() - u1.getX() * u2.getZ(),
+                                       u1.getX() * u2.getY() - u1.getY() * u2.getX());
+        Vector3D cAccurate = u1.crossProduct(u2);
+
+        // assert
+        Assert.assertTrue(u3.distance(cNaive) > 2.9 * u3.getNorm());
+        Assert.assertEquals(0.0, u3.distance(cAccurate), 1.0e-30 * cAccurate.getNorm());
+    }
+
+    @Test
+    public void testCrossProduct_accuracy() {
+        // we compare accurate versus naive cross product implementations
+        // on regular vectors (i.e. not extreme cases like in the previous test)
+        UniformRandomProvider random = RandomSource.create(RandomSource.WELL_1024_A, 885362227452043215l);
+        for (int i = 0; i < 10000; ++i) {
+            // arrange
+            double ux = 10000 * random.nextDouble();
+            double uy = 10000 * random.nextDouble();
+            double uz = 10000 * random.nextDouble();
+            double vx = 10000 * random.nextDouble();
+            double vy = 10000 * random.nextDouble();
+            double vz = 10000 * random.nextDouble();
+
+            // act
+            Vector3D cNaive = Vector3D.of(uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx);
+            Vector3D cAccurate = Vector3D.of(ux, uy, uz).crossProduct(Vector3D.of(vx, vy, vz));
 
-        checkVector(v2.add(v1), -5, -2, 1);
-        checkVector(v2.add(3, v1), -9, -2, 5);
+            // assert
+            Assert.assertEquals(0.0, cAccurate.distance(cNaive), 6.0e-15 * cAccurate.getNorm());
+        }
     }
 
     @Test
-    public void testScalarProduct() {
-        Cartesian3D v = new Cartesian3D(1, 2, 3);
-        v = v.scalarMultiply(3);
-        checkVector(v, 3, 6, 9);
+    public void testCrossProduct_cancellation() {
+        // act/assert
+        Vector3D v1 = Vector3D.of(9070467121.0, 4535233560.0, 1);
+        Vector3D v2 = Vector3D.of(9070467123.0, 4535233561.0, 1);
+        checkVector(v1.crossProduct(v2), -1, 2, 1);
 
-        checkVector(v.scalarMultiply(0.5), 1.5, 3, 4.5);
+        double scale    = Math.scalb(1.0, 100);
+        Vector3D big1   = Vector3D.linearCombination(scale, v1);
+        Vector3D small2 = Vector3D.linearCombination(1 / scale, v2);
+        checkVector(big1.crossProduct(small2), -1, 2, 1);
     }
 
     @Test
-    public void testVectorialProducts() {
-        Cartesian3D v1 = new Cartesian3D(2, 1, -4);
-        Cartesian3D v2 = new Cartesian3D(3, 1, -1);
+    public void testCrossProduct_static() {
+        // act/assert
+        checkVector(Vector3D.crossProduct(Vector3D.PLUS_X, Vector3D.PLUS_Y), 0, 0, 1);
+        checkVector(Vector3D.crossProduct(Vector3D.PLUS_X, Vector3D.MINUS_Y), 0, 0, -1);
 
-        Assert.assertTrue(Math.abs(Cartesian3D.dotProduct(v1, v2) - 11) < 1.0e-12);
+        checkVector(Vector3D.crossProduct(Vector3D.MINUS_X, Vector3D.MINUS_Y), 0, 0, 1);
+        checkVector(Vector3D.crossProduct(Vector3D.MINUS_X, Vector3D.PLUS_Y), 0, 0, -1);
 
-        Cartesian3D v3 = Cartesian3D.crossProduct(v1, v2);
-        checkVector(v3, 3, -10, -1);
+        checkVector(Vector3D.crossProduct(Vector3D.of(2, 1, -4), Vector3D.of(3, 1, -1)), 3, -10, -1);
 
-        Assert.assertTrue(Math.abs(Cartesian3D.dotProduct(v1, v3)) < 1.0e-12);
-        Assert.assertTrue(Math.abs(Cartesian3D.dotProduct(v2, v3)) < 1.0e-12);
+        double invSqrt6 = 1 / Math.sqrt(6);
+        checkVector(Vector3D.crossProduct(Vector3D.of(1, 1, 1), Vector3D.of(-1, 0, 1)).normalize(), invSqrt6, - 2 * invSqrt6, invSqrt6);
     }
 
     @Test
-    public void testCrossProductCancellation() {
-        Cartesian3D v1 = new Cartesian3D(9070467121.0, 4535233560.0, 1);
-        Cartesian3D v2 = new Cartesian3D(9070467123.0, 4535233561.0, 1);
-        checkVector(Cartesian3D.crossProduct(v1, v2), -1, 2, 1);
+    public void testScalarMultiply() {
+        // arrange
+        Vector3D v1 = Vector3D.of(2, 3, 4);
+        Vector3D v2 = Vector3D.of(-2, -3, -4);
 
-        double scale    = Math.scalb(1.0, 100);
-        Cartesian3D big1   = new Cartesian3D(scale, v1);
-        Cartesian3D small2 = new Cartesian3D(1 / scale, v2);
-        checkVector(Cartesian3D.crossProduct(big1, small2), -1, 2, 1);
+        // act/assert
+        checkVector(v1.scalarMultiply(0), 0, 0, 0);
+        checkVector(v1.scalarMultiply(0.5), 1, 1.5, 2);
+        checkVector(v1.scalarMultiply(1), 2, 3, 4);
+        checkVector(v1.scalarMultiply(2), 4, 6, 8);
+        checkVector(v1.scalarMultiply(-2), -4, -6, -8);
 
+        checkVector(v2.scalarMultiply(0), 0, 0, 0);
+        checkVector(v2.scalarMultiply(0.5), -1, -1.5, -2);
+        checkVector(v2.scalarMultiply(1), -2, -3, -4);
+        checkVector(v2.scalarMultiply(2), -4, -6, -8);
+        checkVector(v2.scalarMultiply(-2), 4, 6, 8);
     }
 
     @Test
-    public void testAngular() {
-        Assert.assertEquals(0,           Cartesian3D.PLUS_I.getAlpha(), 1.0e-10);
-        Assert.assertEquals(0,           Cartesian3D.PLUS_I.getDelta(), 1.0e-10);
-        Assert.assertEquals(Math.PI / 2, Cartesian3D.PLUS_J.getAlpha(), 1.0e-10);
-        Assert.assertEquals(0,           Cartesian3D.PLUS_J.getDelta(), 1.0e-10);
-        Assert.assertEquals(0,           Cartesian3D.PLUS_K.getAlpha(), 1.0e-10);
-        Assert.assertEquals(Math.PI / 2, Cartesian3D.PLUS_K.getDelta(), 1.0e-10);
-
-        Cartesian3D u = new Cartesian3D(-1, 1, -1);
-        Assert.assertEquals(3 * Math.PI /4, u.getAlpha(), 1.0e-10);
-        Assert.assertEquals(-1.0 / Math.sqrt(3), Math.sin(u.getDelta()), 1.0e-10);
+    public void testDistance1() {
+        // arrange
+        Vector3D v1 = Vector3D.of(1, -2, 3);
+        Vector3D v2 = Vector3D.of(-4, 2, 0);
+        Vector3D v3 = Vector3D.of(5, -6, -7);
+
+        // act/assert
+        Assert.assertEquals(0.0, v1.distance1(v1), EPS);
+        Assert.assertEquals(0.0, v2.distance1(v2), EPS);
+
+        Assert.assertEquals(12.0, v1.distance1(v2), EPS);
+        Assert.assertEquals(12.0, v2.distance1(v1), EPS);
+
+        Assert.assertEquals(v1.subtract(v2).getNorm1(), v1.distance1(v2), EPS);
+
+        Assert.assertEquals(18, v1.distance1(v3), EPS);
+        Assert.assertEquals(18, v3.distance1(v1), EPS);
     }
 
     @Test
-    public void testAngularSeparation() {
-        Cartesian3D v1 = new Cartesian3D(2, -1, 4);
+    public void testDistance() {
+        // arrange
+        Vector3D v1 = Vector3D.of(1, -2, 3);
+        Vector3D v2 = Vector3D.of(-4, 2, 0);
+        Vector3D v3 = Vector3D.of(5, -6, -7);
 
-        Cartesian3D  k = v1.normalize();
-        Cartesian3D  i = k.orthogonal();
-        Cartesian3D v2 = k.scalarMultiply(Math.cos(1.2)).add(i.scalarMultiply(Math.sin(1.2)));
+        // act/assert
+        Assert.assertEquals(0.0, v1.distance(v1), EPS);
+        Assert.assertEquals(0.0, v2.distance(v2), EPS);
 
-        Assert.assertTrue(Math.abs(Cartesian3D.angle(v1, v2) - 1.2) < 1.0e-12);
-  }
+        Assert.assertEquals(Math.sqrt(50), v1.distance(v2), EPS);
+        Assert.assertEquals(Math.sqrt(50), v2.distance(v1), EPS);
 
-    @Test
-    public void testNormalize() {
-        Assert.assertEquals(1.0, new Cartesian3D(5, -4, 2).normalize().getNorm(), 1.0e-12);
-        try {
-            Cartesian3D.ZERO.normalize();
-            Assert.fail("an exception should have been thrown");
-        } catch (IllegalStateException e) {
-            // expected behavior
-        }
+        Assert.assertEquals(v1.subtract(v2).getNorm(), v1.distance(v2), EPS);
+
+        Assert.assertEquals(Math.sqrt(132), v1.distance(v3), EPS);
+        Assert.assertEquals(Math.sqrt(132), v3.distance(v1), EPS);
     }
 
     @Test
-    public void testNegate() {
-        checkVector(new Cartesian3D(0.1, 2.5, 1.3).negate(), -0.1, -2.5, -1.3);
-    }
+    public void testDistanceSq() {
+        // arrange
+        Vector3D v1 = Vector3D.of(1, -2, 3);
+        Vector3D v2 = Vector3D.of(-4, 2, 0);
+        Vector3D v3 = Vector3D.of(5, -6, -7);
+
+        // act/assert
+        Assert.assertEquals(0.0, v1.distanceSq(v1), EPS);
+        Assert.assertEquals(0.0, v2.distanceSq(v2), EPS);
+
+        Assert.assertEquals(50, v1.distanceSq(v2), EPS);
+        Assert.assertEquals(50, v2.distanceSq(v1), EPS);
+
+        Assert.assertEquals(v1.subtract(v2).getNormSq(), v1.distanceSq(v2), EPS);
+
+        Assert.assertEquals(132, v1.distanceSq(v3), EPS);
+        Assert.assertEquals(132, v3.distanceSq(v1), EPS);
+  }
 
     @Test
-    public void testOrthogonal() {
-        Cartesian3D v1 = new Cartesian3D(0.1, 2.5, 1.3);
-        Assert.assertEquals(0.0, Cartesian3D.dotProduct(v1, v1.orthogonal()), 1.0e-12);
-        Cartesian3D v2 = new Cartesian3D(2.3, -0.003, 7.6);
-        Assert.assertEquals(0.0, Cartesian3D.dotProduct(v2, v2.orthogonal()), 1.0e-12);
-        Cartesian3D v3 = new Cartesian3D(-1.7, 1.4, 0.2);
-        Assert.assertEquals(0.0, Cartesian3D.dotProduct(v3, v3.orthogonal()), 1.0e-12);
-        Cartesian3D v4 = new Cartesian3D(4.2, 0.1, -1.8);
-        Assert.assertEquals(0.0, Cartesian3D.dotProduct(v4, v4.orthogonal()), 1.0e-12);
-        try {
-            new Cartesian3D(0, 0, 0).orthogonal();
-            Assert.fail("an exception should have been thrown");
-        } catch (IllegalStateException e) {
-            // expected behavior
-        }
+    public void testDistanceInf() {
+        // arrange
+        Vector3D v1 = Vector3D.of(1, -2, 3);
+        Vector3D v2 = Vector3D.of(-4, 2, 0);
+        Vector3D v3 = Vector3D.of(5, -6, -7);
+
+        // act/assert
+        Assert.assertEquals(0.0, v1.distanceInf(v1), EPS);
+        Assert.assertEquals(0.0, v2.distanceInf(v2), EPS);
+
+        Assert.assertEquals(5, v1.distanceInf(v2), EPS);
+        Assert.assertEquals(5, v2.distanceInf(v1), EPS);
+
+        Assert.assertEquals(v1.subtract(v2).getNormInf(), v1.distanceInf(v2), EPS);
+
+        Assert.assertEquals(10, v1.distanceInf(v3), EPS);
+        Assert.assertEquals(10, v3.distanceInf(v1), EPS);
     }
+
     @Test
-    public void testAngle() {
-        Assert.assertEquals(0.22572612855273393616,
-                            Cartesian3D.angle(new Cartesian3D(1, 2, 3), new Cartesian3D(4, 5, 6)),
-                            1.0e-12);
-        Assert.assertEquals(7.98595620686106654517199e-8,
-                            Cartesian3D.angle(new Cartesian3D(1, 2, 3), new Cartesian3D(2, 4, 6.000001)),
-                            1.0e-12);
-        Assert.assertEquals(3.14159257373023116985197793156,
-                            Cartesian3D.angle(new Cartesian3D(1, 2, 3), new Cartesian3D(-2, -4, -6.000001)),
-                            1.0e-12);
-        try {
-            Cartesian3D.angle(Cartesian3D.ZERO, Cartesian3D.PLUS_I);
-            Assert.fail("an exception should have been thrown");
-        } catch (IllegalArgumentException e) {
-            // expected behavior
-        }
+    public void testDotProduct() {
+        // arrange
+        Vector3D v1 = Vector3D.of(1, -2, 3);
+        Vector3D v2 = Vector3D.of(-4, 5, -6);
+        Vector3D v3 = Vector3D.of(7, 8, 9);
+
+        // act/assert
+        Assert.assertEquals(14, v1.dotProduct(v1), EPS);
+
+        Assert.assertEquals(-32, v1.dotProduct(v2), EPS);
+        Assert.assertEquals(-32, v2.dotProduct(v1), EPS);
+
+        Assert.assertEquals(18, v1.dotProduct(v3), EPS);
+        Assert.assertEquals(18, v3.dotProduct(v1), EPS);
     }
 
     @Test
-    public void testAccurateDotProduct() {
+    public void testDotProduct_nearlyOrthogonal() {
         // the following two vectors are nearly but not exactly orthogonal
         // naive dot product (i.e. computing u1.x * u2.x + u1.y * u2.y + u1.z * u2.z
         // leads to a result of 0.0, instead of the correct -1.855129...
-        Cartesian3D u1 = new Cartesian3D(-1321008684645961.0 /  268435456.0,
+
+        // arrange
+        Vector3D u1 = Vector3D.of(-1321008684645961.0 /  268435456.0,
                                    -5774608829631843.0 /  268435456.0,
                                    -7645843051051357.0 / 8589934592.0);
-        Cartesian3D u2 = new Cartesian3D(-5712344449280879.0 /    2097152.0,
+        Vector3D u2 = Vector3D.of(-5712344449280879.0 /    2097152.0,
                                    -4550117129121957.0 /    2097152.0,
                                     8846951984510141.0 /     131072.0);
+
+        // act
         double sNaive = u1.getX() * u2.getX() + u1.getY() * u2.getY() + u1.getZ() * u2.getZ();
         double sAccurate = u1.dotProduct(u2);
+
+        // assert
         Assert.assertEquals(0.0, sNaive, 1.0e-30);
         Assert.assertEquals(-2088690039198397.0 / 1125899906842624.0, sAccurate, 1.0e-15);
     }
 
     @Test
-    public void testDotProduct() {
+    public void testDotProduct_accuracy() {
         // we compare accurate versus naive dot product implementations
         // on regular vectors (i.e. not extreme cases like in the previous test)
         UniformRandomProvider random = RandomSource.create(RandomSource.WELL_1024_A, 553267312521321237l);
         for (int i = 0; i < 10000; ++i) {
+            // arrange
             double ux = 10000 * random.nextDouble();
             double uy = 10000 * random.nextDouble();
             double uz = 10000 * random.nextDouble();
             double vx = 10000 * random.nextDouble();
             double vy = 10000 * random.nextDouble();
             double vz = 10000 * random.nextDouble();
+
+            // act
             double sNaive = ux * vx + uy * vy + uz * vz;
-            double sAccurate = new Cartesian3D(ux, uy, uz).dotProduct(new Cartesian3D(vx, vy, vz));
+            double sAccurate = Vector3D.of(ux, uy, uz).dotProduct(Vector3D.of(vx, vy, vz));
+
+            // assert
             Assert.assertEquals(sNaive, sAccurate, 2.5e-16 * sAccurate);
         }
     }
 
     @Test
-    public void testAccurateCrossProduct() {
-        // the vectors u1 and u2 are nearly but not exactly anti-parallel
-        // (7.31e-16 degrees from 180 degrees) naive cross product (i.e.
-        // computing u1.x * u2.x + u1.y * u2.y + u1.z * u2.z
-        // leads to a result of   [0.0009765, -0.0001220, -0.0039062],
-        // instead of the correct [0.0006913, -0.0001254, -0.0007909]
-        final Cartesian3D u1 = new Cartesian3D(-1321008684645961.0 /   268435456.0,
-                                         -5774608829631843.0 /   268435456.0,
-                                         -7645843051051357.0 /  8589934592.0);
-        final Cartesian3D u2 = new Cartesian3D( 1796571811118507.0 /  2147483648.0,
-                                          7853468008299307.0 /  2147483648.0,
-                                          2599586637357461.0 / 17179869184.0);
-        final Cartesian3D u3 = new Cartesian3D(12753243807587107.0 / 18446744073709551616.0,
-                                         -2313766922703915.0 / 18446744073709551616.0,
-                                          -227970081415313.0 /   288230376151711744.0);
-        Cartesian3D cNaive = new Cartesian3D(u1.getY() * u2.getZ() - u1.getZ() * u2.getY(),
-                                       u1.getZ() * u2.getX() - u1.getX() * u2.getZ(),
-                                       u1.getX() * u2.getY() - u1.getY() * u2.getX());
-        Cartesian3D cAccurate = u1.crossProduct(u2);
-        Assert.assertTrue(u3.distance(cNaive) > 2.9 * u3.getNorm());
-        Assert.assertEquals(0.0, u3.distance(cAccurate), 1.0e-30 * cAccurate.getNorm());
+    public void testDotProduct_static() {
+        // arrange
+        Vector3D v1 = Vector3D.of(1, -2, 3);
+        Vector3D v2 = Vector3D.of(-4, 5, -6);
+        Vector3D v3 = Vector3D.of(7, 8, 9);
+
+        // act/assert
+        Assert.assertEquals(14, Vector3D.dotProduct(v1, v1), EPS);
+
+        Assert.assertEquals(-32, Vector3D.dotProduct(v1, v2), EPS);
+        Assert.assertEquals(-32, Vector3D.dotProduct(v2, v1), EPS);
+
+        Assert.assertEquals(18, Vector3D.dotProduct(v1, v3), EPS);
+        Assert.assertEquals(18, Vector3D.dotProduct(v3, v1), EPS);
     }
 
     @Test
-    public void testCrossProduct() {
-        // we compare accurate versus naive cross product implementations
-        // on regular vectors (i.e. not extreme cases like in the previous test)
-        UniformRandomProvider random = RandomSource.create(RandomSource.WELL_1024_A, 885362227452043215l);
-        for (int i = 0; i < 10000; ++i) {
-            double ux = 10000 * random.nextDouble();
-            double uy = 10000 * random.nextDouble();
-            double uz = 10000 * random.nextDouble();
-            double vx = 10000 * random.nextDouble();
-            double vy = 10000 * random.nextDouble();
-            double vz = 10000 * random.nextDouble();
-            Cartesian3D cNaive = new Cartesian3D(uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx);
-            Cartesian3D cAccurate = new Cartesian3D(ux, uy, uz).crossProduct(new Cartesian3D(vx, vy, vz));
-            Assert.assertEquals(0.0, cAccurate.distance(cNaive), 6.0e-15 * cAccurate.getNorm());
-        }
+    public void testHashCode() {
+        // arrange
+        double delta = 10 * Precision.EPSILON;
+        Vector3D u = Vector3D.of(1, 1, 1);
+        Vector3D v = Vector3D.of(1 + delta, 1 + delta, 1 + delta);
+        Vector3D w = Vector3D.of(1, 1, 1);
+
+        // act/assert
+        Assert.assertTrue(u.hashCode() != v.hashCode());
+        Assert.assertEquals(u.hashCode(), w.hashCode());
+
+        Assert.assertEquals(Vector3D.of(0, 0, Double.NaN).hashCode(), Vector3D.NaN.hashCode());
+        Assert.assertEquals(Vector3D.of(0, Double.NaN, 0).hashCode(), Vector3D.NaN.hashCode());
+        Assert.assertEquals(Vector3D.of(Double.NaN, 0, 0).hashCode(), Vector3D.NaN.hashCode());
+        Assert.assertEquals(Vector3D.of(0, 0, Double.NaN).hashCode(), Vector3D.of(Double.NaN, 0, 0).hashCode());
+    }
+
+    @Test
+    public void testEquals() {
+        // arrange
+        double delta = 10 * Precision.EPSILON;
+        Vector3D u1 = Vector3D.of(1, 2, 3);
+        Vector3D u2 = Vector3D.of(1, 2, 3);
+
+        // act/assert
+        Assert.assertFalse(u1.equals(null));
+        Assert.assertFalse(u1.equals(new Object()));
+
+        Assert.assertTrue(u1.equals(u1));
+        Assert.assertTrue(u1.equals(u2));
+
+        Assert.assertFalse(u1.equals(Vector3D.of(-1, -2, -3)));
+        Assert.assertFalse(u1.equals(Vector3D.of(1 + delta, 2, 3)));
+        Assert.assertFalse(u1.equals(Vector3D.of(1, 2 + delta, 3)));
+        Assert.assertFalse(u1.equals(Vector3D.of(1, 2, 3 + delta)));
+
+        Assert.assertTrue(Vector3D.of(0, Double.NaN, 0).equals(Vector3D.of(Double.NaN, 0, 0)));
+
+        Assert.assertTrue(Vector3D.of(0, 0, Double.POSITIVE_INFINITY).equals(Vector3D.of(0, 0, Double.POSITIVE_INFINITY)));
+        Assert.assertFalse(Vector3D.of(0, Double.POSITIVE_INFINITY, 0).equals(Vector3D.of(0, 0, Double.POSITIVE_INFINITY)));
+        Assert.assertFalse(Vector3D.of(Double.POSITIVE_INFINITY, 0, 0).equals(Vector3D.of(0, 0, Double.POSITIVE_INFINITY)));
+
+        Assert.assertTrue(Vector3D.of(Double.NEGATIVE_INFINITY, 0, 0).equals(Vector3D.of(Double.NEGATIVE_INFINITY, 0, 0)));
+        Assert.assertFalse(Vector3D.of(0, Double.NEGATIVE_INFINITY, 0).equals(Vector3D.of(Double.NEGATIVE_INFINITY, 0, 0)));
+        Assert.assertFalse(Vector3D.of(0, 0, Double.NEGATIVE_INFINITY).equals(Vector3D.of(Double.NEGATIVE_INFINITY, 0, 0)));
+    }
+
+    @Test
+    public void testToString() {
+        // arrange
+        Vector3D v = Vector3D.of(1, 2, 3);
+        Pattern pattern = Pattern.compile("\\{1.{0,2}; 2.{0,2}; 3.{0,2}\\}");
+
+        // act
+        String str = v.toString();
+
+        // assert
+        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                    pattern.matcher(str).matches());
+    }
+
+    @Test
+    public void testOf() {
+        // act/assert
+        checkVector(Vector3D.of(1, 2, 3), 1, 2, 3);
+        checkVector(Vector3D.of(-1, -2, -3), -1, -2, -3);
+        checkVector(Vector3D.of(Math.PI, Double.NaN, Double.POSITIVE_INFINITY),
+                Math.PI, Double.NaN, Double.POSITIVE_INFINITY);
+        checkVector(Vector3D.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E),
+                Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E);
+    }
+
+    @Test
+    public void testOf_coordinateArg() {
+        // act/assert
+        checkVector(Vector3D.of(Point3D.of(1, 2, 3)), 1, 2, 3);
+        checkVector(Vector3D.of(Point3D.of(-1, -2, -3)), -1, -2, -3);
+        checkVector(Vector3D.of(Point3D.of(Math.PI, Double.NaN, Double.POSITIVE_INFINITY)),
+                Math.PI, Double.NaN, Double.POSITIVE_INFINITY);
+        checkVector(Vector3D.of(Point3D.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E)),
+                   Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E);
+    }
+
+    @Test
+    public void testOf_arrayArg() {
+        // act/assert
+        checkVector(Vector3D.of(new double[] { 1, 2, 3 }), 1, 2, 3);
+        checkVector(Vector3D.of(new double[] { -1, -2, -3 }), -1, -2, -3);
+        checkVector(Vector3D.of(new double[] { Math.PI, Double.NaN, Double.POSITIVE_INFINITY }),
+                Math.PI, Double.NaN, Double.POSITIVE_INFINITY);
+        checkVector(Vector3D.of(new double[] { Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E}),
+                Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Math.E);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testOf_arrayArg_invalidDimensions() {
+        // act/assert
+        Vector3D.of(new double[] { 0.0, 0.0 });
+    }
+
+    @Test
+    public void testLinearCombination1() {
+        // arrange
+        Vector3D p1 = Vector3D.of(1, 2, 3);
+
+        // act/assert
+        checkVector(Vector3D.linearCombination(0, p1), 0, 0, 0);
+
+        checkVector(Vector3D.linearCombination(1, p1), 1, 2, 3);
+        checkVector(Vector3D.linearCombination(-1, p1), -1, -2, -3);
+
+        checkVector(Vector3D.linearCombination(0.5, p1), 0.5, 1, 1.5);
+        checkVector(Vector3D.linearCombination(-0.5, p1), -0.5, -1, -1.5);
+    }
+
+    @Test
+    public void testLinearCombination2() {
+        // arrange
+        Vector3D p1 = Vector3D.of(1, 2, 3);
+        Vector3D p2 = Vector3D.of(-3, -4, -5);
+
+        // act/assert
+        checkVector(Vector3D.linearCombination(2, p1, -3, p2), 11, 16, 21);
+        checkVector(Vector3D.linearCombination(-3, p1, 2, p2), -9, -14, -19);
+    }
+
+    @Test
+    public void testLinearCombination3() {
+        // arrange
+        Vector3D p1 = Vector3D.of(1, 2, 3);
+        Vector3D p2 = Vector3D.of(-3, -4, -5);
+        Vector3D p3 = Vector3D.of(5, 6, 7);
+
+        // act/assert
+        checkVector(Vector3D.linearCombination(2, p1, -3, p2, 4, p3), 31, 40, 49);
+        checkVector(Vector3D.linearCombination(-3, p1, 2, p2, -4, p3), -29, -38, -47);
+    }
+
+    @Test
+    public void testLinearCombination4() {
+        // arrange
+        Vector3D p1 = Vector3D.of(1, 2, 3);
+        Vector3D p2 = Vector3D.of(-3, -4, -5);
+        Vector3D p3 = Vector3D.of(5, 6, 7);
+        Vector3D p4 = Vector3D.of(-7, -8, 9);
+
+        // act/assert
+        checkVector(Vector3D.linearCombination(2, p1, -3, p2, 4, p3, -5, p4), 66, 80, 4);
+        checkVector(Vector3D.linearCombination(-3, p1, 2, p2, -4, p3, 5, p4), -64, -78, -2);
+    }
+
+    @Test
+    public void testConstructors() {
+        double r = Math.sqrt(2) /2;
+        checkVector(Vector3D.linearCombination(2, Vector3D.fromSpherical(Math.PI / 3, -Math.PI / 4)),
+                    r, r * Math.sqrt(3), -2 * r);
+        checkVector(Vector3D.linearCombination(2, Vector3D.PLUS_X,
+                                 -3, Vector3D.MINUS_Z),
+                    2, 0, 3);
+        checkVector(Vector3D.linearCombination(2, Vector3D.PLUS_X,
+                                 5, Vector3D.PLUS_Y,
+                                 -3, Vector3D.MINUS_Z),
+                    2, 5, 3);
+        checkVector(Vector3D.linearCombination(2, Vector3D.PLUS_X,
+                                 5, Vector3D.PLUS_Y,
+                                 5, Vector3D.MINUS_Y,
+                                 -3, Vector3D.MINUS_Z),
+                    2, 0, 3);
+        checkVector(Vector3D.of(new double[] { 2,  5,  -3 }),
+                    2, 5, -3);
+    }
+
+    @Test
+    public void testAngular() {
+        Assert.assertEquals(0,           Vector3D.PLUS_X.getAlpha(), 1.0e-10);
+        Assert.assertEquals(0,           Vector3D.PLUS_X.getDelta(), 1.0e-10);
+        Assert.assertEquals(Math.PI / 2, Vector3D.PLUS_Y.getAlpha(), 1.0e-10);
+        Assert.assertEquals(0,           Vector3D.PLUS_Y.getDelta(), 1.0e-10);
+        Assert.assertEquals(0,           Vector3D.PLUS_Z.getAlpha(), 1.0e-10);
+        Assert.assertEquals(Math.PI / 2, Vector3D.PLUS_Z.getDelta(), 1.0e-10);
+
+        Vector3D u = Vector3D.of(-1, 1, -1);
+        Assert.assertEquals(3 * Math.PI /4, u.getAlpha(), 1.0e-10);
+        Assert.assertEquals(-1.0 / Math.sqrt(3), Math.sin(u.getDelta()), 1.0e-10);
+    }
+
+    private void checkVector(Vector3D v, double x, double y, double z) {
+        Assert.assertEquals(x, v.getX(), EPS);
+        Assert.assertEquals(y, v.getY(), EPS);
+        Assert.assertEquals(z, v.getZ(), EPS);
     }
 
-    private void checkVector(Cartesian3D v, double x, double y, double z) {
-        Assert.assertEquals(x, v.getX(), 1.0e-12);
-        Assert.assertEquals(y, v.getY(), 1.0e-12);
-        Assert.assertEquals(z, v.getZ(), 1.0e-12);
+    private void checkPoint(Point3D p, double x, double y, double z) {
+        Assert.assertEquals(x, p.getX(), EPS);
+        Assert.assertEquals(y, p.getY(), EPS);
+        Assert.assertEquals(z, p.getZ(), EPS);
     }
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
index cec6237..b8c7238 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Cartesian2DTest.java
@@ -1,232 +1,78 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 package org.apache.commons.geometry.euclidean.twod;
 
 import org.junit.Assert;
 import org.junit.Test;
 
-public class Cartesian2DTest {
-
-    private static final double EPS = Math.ulp(1d);
-
-    @Test
-    public void testScaledVectorTripleConstructor() {
-        Cartesian2D oneOne = new Cartesian2D(1.0,1.0);
-        Cartesian2D oneTwo = new Cartesian2D(1.0,2.0);
-        Cartesian2D oneThree = new Cartesian2D(1.0,3.0);
 
-        Cartesian2D tripleCombo = new Cartesian2D(3.0, oneOne, 1.0, oneTwo, 2.5, oneThree);
+public class Cartesian2DTest {
 
-        Assert.assertEquals(3.0 * 1 + 1.0 * 1 + 2.5 * 1,tripleCombo.getX(), EPS);
-        Assert.assertEquals(3.0 * 1 + 1.0 * 2 + 2.5 * 3,tripleCombo.getY(), EPS);
-    }
+    private static final double TEST_TOLERANCE = 1e-15;
 
     @Test
-    public void testScaledVectorQuadrupleConstructor() {
-        Cartesian2D oneOne = new Cartesian2D(1.0, 1.0);
-        Cartesian2D oneTwo = new Cartesian2D(1.0, 2.0);
-        Cartesian2D oneThree = new Cartesian2D(1.0, 3.0);
-        Cartesian2D oneFour = new Cartesian2D(1.0, 4.0);
+    public void testCoordinates() {
+        // arrange
+        Cartesian2D c = new StubCartesian2D(1, 2);
 
-        Cartesian2D tripleCombo = new Cartesian2D(3.0, oneOne, 1.0, oneTwo, 2.5, oneThree, 2.0, oneFour);
-
-        Assert.assertEquals(3.0 * 1.0 + 1.0 * 1.0 + 2.5 * 1.0 + 2.0 * 1.0,tripleCombo.getX(), EPS);
-        Assert.assertEquals(3.0 * 1.0 + 1.0 * 2.0 + 2.5 * 3.0 + 2.0 * 4.0,tripleCombo.getY(), EPS);
-    }
-
-    @Test
-    public void testConstructorExceptions() {
-        double[] v = new double[] {0.0, 1.0, 2.0};
-        try {
-            new Cartesian2D(v);
-        }
-        catch (Exception e) {
-            Assert.assertTrue(e instanceof IllegalArgumentException);
-        }
+        // act/assert
+        Assert.assertEquals(1.0, c.getX(), TEST_TOLERANCE);
+        Assert.assertEquals(2.0, c.getY(), TEST_TOLERANCE);
     }
 
     @Test
     public void testToArray() {
-        Cartesian2D oneTwo = new Cartesian2D(1.0, 2.0);
-        double[] array = oneTwo.toArray();
-        Assert.assertEquals(1.0, array[0], EPS);
-        Assert.assertEquals(2.0, array[1], EPS);
-    }
+        // arrange
+        Cartesian2D oneTwo = new StubCartesian2D(1, 2);
 
-    @Test
-    public void testGetZero() {
-        Cartesian2D zero = (new Cartesian2D(1.0, 1.0)).getZero();
-        Assert.assertEquals(0.0, zero.getX(), EPS);
-        Assert.assertEquals(0.0, zero.getY(), EPS);
-    }
+        // act
+        double[] array = oneTwo.toArray();
 
-    @Test
-    public void testNorm1() {
-        Cartesian2D oneTwo = new Cartesian2D(-1.0, 2.0);
-        Assert.assertEquals(3.0, oneTwo.getNorm1(), EPS);
+        // assert
+        Assert.assertEquals(2, array.length);
+        Assert.assertEquals(1.0, array[0], TEST_TOLERANCE);
+        Assert.assertEquals(2.0, array[1], TEST_TOLERANCE);
     }
 
     @Test
-    public void testNormSq() {
-        Cartesian2D oneTwo = new Cartesian2D(-1.0, 2.0);
-        Assert.assertEquals(5.0, oneTwo.getNormSq(), EPS);
-    }
+    public void testDimension() {
+        // arrange
+        Cartesian2D c = new StubCartesian2D(1, 2);
 
-    @Test
-    public void testNormInf() {
-        Cartesian2D oneTwo = new Cartesian2D(-1.0, 2.0);
-        Assert.assertEquals(2.0, oneTwo.getNormInf(), EPS);
+        // act/assert
+        Assert.assertEquals(2, c.getDimension());
     }
 
     @Test
-    public void testVectorAddition() {
-        Cartesian2D minusOneTwo = new Cartesian2D(-1.0,2.0);
-        Cartesian2D threeFive = new Cartesian2D(3.0,5.0);
-        Cartesian2D addition = minusOneTwo.add(threeFive);
-        Assert.assertEquals(2.0, addition.getX(), EPS);
-        Assert.assertEquals(7.0, addition.getY(), EPS);
-    }
+    public void testNaN() {
+        // act/assert
+        Assert.assertTrue(new StubCartesian2D(0, Double.NaN).isNaN());
+        Assert.assertTrue(new StubCartesian2D(Double.NaN, 0).isNaN());
 
-    @Test
-    public void testScaledVectorAddition() {
-        Cartesian2D minusOneTwo = new Cartesian2D(-1.0,2.0);
-        Cartesian2D threeFive = new Cartesian2D(3.0,5.0);
-        Cartesian2D addition = minusOneTwo.add(2.0, threeFive);
-        Assert.assertEquals(5.0, addition.getX(), EPS);
-        Assert.assertEquals(12.0, addition.getY(), EPS);
+        Assert.assertFalse(new StubCartesian2D(1, 1).isNaN());
+        Assert.assertFalse(new StubCartesian2D(1, Double.NEGATIVE_INFINITY).isNaN());
+        Assert.assertFalse(new StubCartesian2D(Double.POSITIVE_INFINITY, 1).isNaN());
     }
 
     @Test
-    public void testVectorSubtraction() {
-        Cartesian2D minusOneTwo = new Cartesian2D(-1.0,2.0);
-        Cartesian2D threeFive = new Cartesian2D(3.0,5.0);
-        Cartesian2D addition = minusOneTwo.subtract(threeFive);
-        Assert.assertEquals(-4.0, addition.getX(), EPS);
-        Assert.assertEquals(-3.0, addition.getY(), EPS);
-    }
+    public void testInfinite() {
+        // act/assert
+        Assert.assertTrue(new StubCartesian2D(0, Double.NEGATIVE_INFINITY).isInfinite());
+        Assert.assertTrue(new StubCartesian2D(Double.NEGATIVE_INFINITY, 0).isInfinite());
+        Assert.assertTrue(new StubCartesian2D(0, Double.POSITIVE_INFINITY).isInfinite());
+        Assert.assertTrue(new StubCartesian2D(Double.POSITIVE_INFINITY, 0).isInfinite());
 
-    @Test
-    public void testScaledVectorSubtraction() {
-        Cartesian2D minusOneTwo = new Cartesian2D(-1.0,2.0);
-        Cartesian2D threeFive = new Cartesian2D(3.0,5.0);
-        Cartesian2D addition = minusOneTwo.subtract(2.0, threeFive);
-        Assert.assertEquals(-7.0, addition.getX(), EPS);
-        Assert.assertEquals(-8.0, addition.getY(), EPS);
+        Assert.assertFalse(new StubCartesian2D(1, 1).isInfinite());
+        Assert.assertFalse(new StubCartesian2D(0, Double.NaN).isInfinite());
+        Assert.assertFalse(new StubCartesian2D(Double.NEGATIVE_INFINITY, Double.NaN).isInfinite());
+        Assert.assertFalse(new StubCartesian2D(Double.NaN, Double.NEGATIVE_INFINITY).isInfinite());
+        Assert.assertFalse(new StubCartesian2D(Double.POSITIVE_INFINITY, Double.NaN).isInfinite());
+        Assert.assertFalse(new StubCartesian2D(Double.NaN, Double.POSITIVE_INFINITY).isInfinite());
     }
 
-    @Test
-    public void testNormalize() {
-        Cartesian2D minusOneTwo = new Cartesian2D(-1.0,2.0);
-        Cartesian2D normalizedMinusOneTwo = minusOneTwo.normalize();
-        Assert.assertEquals(-1.0/Math.sqrt(5), normalizedMinusOneTwo.getX(), EPS);
-        Assert.assertEquals(2.0/Math.sqrt(5), normalizedMinusOneTwo.getY(), EPS);
-        Cartesian2D zero = minusOneTwo.getZero();
-        try {
-            zero.normalize();
-        }
-        catch (Exception e) {
-            Assert.assertTrue(e instanceof IllegalStateException);
-        }
-    }
+    private static class StubCartesian2D extends Cartesian2D {
+        private static final long serialVersionUID = 1L;
 
-    @Test
-    public void testAngle() {
-        Cartesian2D oneOne = new Cartesian2D(1.0, 1.0);
-        try {
-            Cartesian2D.angle(oneOne.getZero(), oneOne.getZero());
+        public StubCartesian2D(double x, double y) {
+            super(x, y);
         }
-        catch (Exception e) {
-            Assert.assertTrue(e instanceof IllegalArgumentException);
-        }
-        Cartesian2D oneZero = new Cartesian2D(1.0,0.0);
-        double angle = Cartesian2D.angle(oneOne, oneZero);
-        Assert.assertEquals(Math.PI/4, angle, EPS);
-        Assert.assertEquals(0.004999958333958323, Cartesian2D.angle(new Cartesian2D(20.0,0.0), new Cartesian2D(20.0,0.1)), EPS);
-    }
-
-    @Test
-    public void testNegate() {
-        Cartesian2D oneOne = new Cartesian2D(1.0,1.0);
-        Cartesian2D negated = oneOne.negate();
-        Assert.assertEquals(-1.0, negated.getX(), EPS);
-        Assert.assertEquals(-1.0, negated.getY(), EPS);
-    }
-
-    @Test
-    public void testIsInfinite() {
-        Cartesian2D oneOne = new Cartesian2D(1.0, 1.0);
-        Cartesian2D infiniteVector = new Cartesian2D(Double.POSITIVE_INFINITY, 0.0);
-        Assert.assertFalse(oneOne.isInfinite());
-        Assert.assertTrue(infiniteVector.isInfinite());
-    }
-
-    @Test
-    public void testDistance1() {
-        Cartesian2D oneOne = new Cartesian2D(1.0,1.0);
-        Cartesian2D fiveEleven = new Cartesian2D(5.0,11.0);
-        double distance1 = oneOne.distance1(fiveEleven);
-        Assert.assertEquals(14.0, distance1, EPS);
-    }
-
-    @Test
-    public void testDistanceInf() {
-        Cartesian2D oneOne = new Cartesian2D(1.0,1.0);
-        Cartesian2D fiveEleven = new Cartesian2D(5.0,11.0);
-        double distanceInf = oneOne.distanceInf(fiveEleven);
-        double staticDistanceInf = Cartesian2D.distanceInf(oneOne, fiveEleven);
-        Assert.assertEquals(10.0, distanceInf, EPS);
-        Assert.assertEquals(distanceInf, staticDistanceInf, EPS);
-    }
-
-    @Test
-    public void testDistanceSq() {
-        Cartesian2D oneFive = new Cartesian2D(1.0, 5.0);
-        Cartesian2D fourOne = new Cartesian2D(4.0, 1.0);
-        double distanceSq = oneFive.distanceSq(fourOne);
-        double staticDistanceSq = Cartesian2D.distanceSq(oneFive, fourOne);
-        Assert.assertEquals(25.0, distanceSq, EPS);
-        Assert.assertEquals(distanceSq, staticDistanceSq, EPS);
-    }
-
-    @Test
-    public void testHashCode() {
-        int hashCode = (new Cartesian2D(1.0,1.0)).hashCode();
-        Assert.assertEquals(887095296, hashCode);
-        Assert.assertEquals(542, (new Cartesian2D(Double.NaN, Double.NaN)).hashCode());
-    }
-
-
-    @Test
-    public void testToString() {
-        Assert.assertEquals("{1; 2}", (new Cartesian2D(1.0,2.0)).toString());
-    }
-
-    @Test
-    public void testCrossProduct() {
-        Cartesian2D p1 = new Cartesian2D(1, 1);
-        Cartesian2D p2 = new Cartesian2D(2, 2);
-
-        Cartesian2D p3 = new Cartesian2D(3, 3);
-        Assert.assertEquals(0.0, p3.crossProduct(p1, p2), EPS);
-
-        Cartesian2D p4 = new Cartesian2D(1, 2);
-        Assert.assertEquals(1.0, p4.crossProduct(p1, p2), EPS);
-
-        Cartesian2D p5 = new Cartesian2D(2, 1);
-        Assert.assertEquals(-1.0, p5.crossProduct(p1, p2), EPS);
     }
 }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Euclidean2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Euclidean2DTest.java
deleted file mode 100644
index eeeff69..0000000
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Euclidean2DTest.java
+++ /dev/null
@@ -1,44 +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.twod;
-
-import org.apache.commons.geometry.core.GeometryTestUtils;
-import org.apache.commons.geometry.core.Space;
-import org.apache.commons.geometry.euclidean.oned.Euclidean1D;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class Euclidean2DTest {
-
-    @Test
-    public void testDimension() {
-        Assert.assertEquals(2, Euclidean2D.getInstance().getDimension());
-    }
-
-    @Test
-    public void testSubSpace() {
-        Assert.assertTrue(Euclidean1D.getInstance() == Euclidean2D.getInstance().getSubSpace());
-    }
-
-    @Test
-    public void testSerialization() {
-        Space e2 = Euclidean2D.getInstance();
-        Space deserialized = (Space) GeometryTestUtils.serializeAndRecover(e2);
-        Assert.assertTrue(e2 == deserialized);
-    }
-
-}
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/LineTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/LineTest.java
index 1cc912d..60ea0ad 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/LineTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/LineTest.java
@@ -16,10 +16,8 @@
  */
 package org.apache.commons.geometry.euclidean.twod;
 
-import org.apache.commons.geometry.core.Point;
 import org.apache.commons.geometry.core.partitioning.Transform;
-import org.apache.commons.geometry.euclidean.oned.Cartesian1D;
-import org.apache.commons.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.geometry.euclidean.oned.Point1D;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -27,54 +25,54 @@
 
     @Test
     public void testContains() {
-        Line l = new Line(new Cartesian2D(0, 1), new Cartesian2D(1, 2), 1.0e-10);
-        Assert.assertTrue(l.contains(new Cartesian2D(0, 1)));
-        Assert.assertTrue(l.contains(new Cartesian2D(1, 2)));
-        Assert.assertTrue(l.contains(new Cartesian2D(7, 8)));
-        Assert.assertTrue(! l.contains(new Cartesian2D(8, 7)));
+        Line l = new Line(new Point2D(0, 1), new Point2D(1, 2), 1.0e-10);
+        Assert.assertTrue(l.contains(new Point2D(0, 1)));
+        Assert.assertTrue(l.contains(new Point2D(1, 2)));
+        Assert.assertTrue(l.contains(new Point2D(7, 8)));
+        Assert.assertTrue(! l.contains(new Point2D(8, 7)));
     }
 
     @Test
     public void testAbscissa() {
-        Line l = new Line(new Cartesian2D(2, 1), new Cartesian2D(-2, -2), 1.0e-10);
+        Line l = new Line(new Point2D(2, 1), new Point2D(-2, -2), 1.0e-10);
         Assert.assertEquals(0.0,
-                            (l.toSubSpace(new Cartesian2D(-3,  4))).getX(),
+                            (l.toSubSpace(new Point2D(-3,  4))).getX(),
                             1.0e-10);
         Assert.assertEquals(0.0,
-                            (l.toSubSpace(new Cartesian2D( 3, -4))).getX(),
+                            (l.toSubSpace(new Point2D( 3, -4))).getX(),
                             1.0e-10);
         Assert.assertEquals(-5.0,
-                            (l.toSubSpace(new Cartesian2D( 7, -1))).getX(),
+                            (l.toSubSpace(new Point2D( 7, -1))).getX(),
                             1.0e-10);
         Assert.assertEquals(5.0,
-                             (l.toSubSpace(new Cartesian2D(-1, -7))).getX(),
+                             (l.toSubSpace(new Point2D(-1, -7))).getX(),
                              1.0e-10);
     }
 
     @Test
     public void testOffset() {
-        Line l = new Line(new Cartesian2D(2, 1), new Cartesian2D(-2, -2), 1.0e-10);
-        Assert.assertEquals(-5.0, l.getOffset(new Cartesian2D(5, -3)), 1.0e-10);
-        Assert.assertEquals(+5.0, l.getOffset(new Cartesian2D(-5, 2)), 1.0e-10);
+        Line l = new Line(new Point2D(2, 1), new Point2D(-2, -2), 1.0e-10);
+        Assert.assertEquals(-5.0, l.getOffset(new Point2D(5, -3)), 1.0e-10);
+        Assert.assertEquals(+5.0, l.getOffset(new Point2D(-5, 2)), 1.0e-10);
     }
 
     @Test
     public void testDistance() {
-        Line l = new Line(new Cartesian2D(2, 1), new Cartesian2D(-2, -2), 1.0e-10);
-        Assert.assertEquals(+5.0, l.distance(new Cartesian2D(5, -3)), 1.0e-10);
-        Assert.assertEquals(+5.0, l.distance(new Cartesian2D(-5, 2)), 1.0e-10);
+        Line l = new Line(new Point2D(2, 1), new Point2D(-2, -2), 1.0e-10);
+        Assert.assertEquals(+5.0, l.distance(new Point2D(5, -3)), 1.0e-10);
+        Assert.assertEquals(+5.0, l.distance(new Point2D(-5, 2)), 1.0e-10);
     }
 
     @Test
     public void testPointAt() {
-        Line l = new Line(new Cartesian2D(2, 1), new Cartesian2D(-2, -2), 1.0e-10);
+        Line l = new Line(new Point2D(2, 1), new Point2D(-2, -2), 1.0e-10);
         for (double a = -2.0; a < 2.0; a += 0.2) {
-            Point<Euclidean1D> pA = new Cartesian1D(a);
-            Point<Euclidean2D> point = l.toSpace(pA);
+            Point1D pA = new Point1D(a);
+            Point2D point = l.toSpace(pA);
             Assert.assertEquals(a, (l.toSubSpace(point)).getX(), 1.0e-10);
             Assert.assertEquals(0.0, l.getOffset(point),   1.0e-10);
             for (double o = -2.0; o < 2.0; o += 0.2) {
-                point = l.getPointAt((Cartesian1D) pA, o);
+                point = l.getPointAt(pA, o);
                 Assert.assertEquals(a, (l.toSubSpace(point)).getX(), 1.0e-10);
                 Assert.assertEquals(o, l.getOffset(point),   1.0e-10);
             }
@@ -83,35 +81,35 @@ public void testPointAt() {
 
     @Test
     public void testOriginOffset() {
-        Line l1 = new Line(new Cartesian2D(0, 1), new Cartesian2D(1, 2), 1.0e-10);
+        Line l1 = new Line(new Point2D(0, 1), new Point2D(1, 2), 1.0e-10);
         Assert.assertEquals(Math.sqrt(0.5), l1.getOriginOffset(), 1.0e-10);
-        Line l2 = new Line(new Cartesian2D(1, 2), new Cartesian2D(0, 1), 1.0e-10);
+        Line l2 = new Line(new Point2D(1, 2), new Point2D(0, 1), 1.0e-10);
         Assert.assertEquals(-Math.sqrt(0.5), l2.getOriginOffset(), 1.0e-10);
     }
 
     @Test
     public void testParallel() {
-        Line l1 = new Line(new Cartesian2D(0, 1), new Cartesian2D(1, 2), 1.0e-10);
-        Line l2 = new Line(new Cartesian2D(2, 2), new Cartesian2D(3, 3), 1.0e-10);
+        Line l1 = new Line(new Point2D(0, 1), new Point2D(1, 2), 1.0e-10);
+        Line l2 = new Line(new Point2D(2, 2), new Point2D(3, 3), 1.0e-10);
         Assert.assertTrue(l1.isParallelTo(l2));
-        Line l3 = new Line(new Cartesian2D(1, 0), new Cartesian2D(0.5, -0.5), 1.0e-10);
+        Line l3 = new Line(new Point2D(1, 0), new Point2D(0.5, -0.5), 1.0e-10);
         Assert.assertTrue(l1.isParallelTo(l3));
-        Line l4 = new Line(new Cartesian2D(1, 0), new Cartesian2D(0.5, -0.51), 1.0e-10);
+        Line l4 = new Line(new Point2D(1, 0), new Point2D(0.5, -0.51), 1.0e-10);
         Assert.assertTrue(! l1.isParallelTo(l4));
     }
 
     @Test
     public void testTransform() {
 
-        Line l1 = new Line(new Cartesian2D(1.0 ,1.0), new Cartesian2D(4.0 ,1.0), 1.0e-10);
-        Transform<Euclidean2D, Euclidean1D> t1 =
+        Line l1 = new Line(new Point2D(1.0 ,1.0), new Point2D(4.0 ,1.0), 1.0e-10);
+        Transform<Point2D, Point1D> t1 =
             Line.getTransform(0.0, 0.5, -1.0, 0.0, 1.0, 1.5);
         Assert.assertEquals(0.5 * Math.PI,
                             ((Line) t1.apply(l1)).getAngle(),
                             1.0e-10);
 
-        Line l2 = new Line(new Cartesian2D(0.0, 0.0), new Cartesian2D(1.0, 1.0), 1.0e-10);
-        Transform<Euclidean2D, Euclidean1D> t2 =
+        Line l2 = new Line(new Point2D(0.0, 0.0), new Point2D(1.0, 1.0), 1.0e-10);
+        Transform<Point2D, Point1D> t2 =
             Line.getTransform(0.0, 0.5, -1.0, 0.0, 1.0, 1.5);
         Assert.assertEquals(Math.atan2(1.0, -2.0),
                             ((Line) t2.apply(l2)).getAngle(),
@@ -121,9 +119,9 @@ public void testTransform() {
 
     @Test
     public void testIntersection() {
-        Line    l1 = new Line(new Cartesian2D( 0, 1), new Cartesian2D(1, 2), 1.0e-10);
-        Line    l2 = new Line(new Cartesian2D(-1, 2), new Cartesian2D(2, 1), 1.0e-10);
-        Cartesian2D p  = l1.intersection(l2);
+        Line    l1 = new Line(new Point2D( 0, 1), new Point2D(1, 2), 1.0e-10);
+        Line    l2 = new Line(new Point2D(-1, 2), new Point2D(2, 1), 1.0e-10);
+        Point2D p  = l1.intersection(l2);
         Assert.assertEquals(0.5, p.getX(), 1.0e-10);
         Assert.assertEquals(1.5, p.getY(), 1.0e-10);
     }
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/NestedLoopsTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/NestedLoopsTest.java
index 7dd4f30..26cec45 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/NestedLoopsTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/NestedLoopsTest.java
@@ -31,13 +31,13 @@
     @SuppressWarnings("unchecked")
     @Test
     public void testNestedLoops() throws Exception {
-        Cartesian2D oneOne = new Cartesian2D(1.0, 1.0);
-        Cartesian2D oneNegativeOne = new Cartesian2D(1.0, -1.0);
-        Cartesian2D negativeOneNegativeOne = new Cartesian2D(-1.0, -1.0);
-        Cartesian2D negativeOneOne = new Cartesian2D(-1.0, 1.0);
-        Cartesian2D origin = new Cartesian2D(0, 0);
+        Point2D oneOne = new Point2D(1.0, 1.0);
+        Point2D oneNegativeOne = new Point2D(1.0, -1.0);
+        Point2D negativeOneNegativeOne = new Point2D(-1.0, -1.0);
+        Point2D negativeOneOne = new Point2D(-1.0, 1.0);
+        Point2D origin = new Point2D(0, 0);
 
-        Cartesian2D [] vertices = new Cartesian2D[]{
+        Point2D [] vertices = new Point2D[]{
                 oneOne,
                 oneNegativeOne,
                 negativeOneNegativeOne,
@@ -54,8 +54,8 @@ public void testNestedLoops() throws Exception {
         surroundedField.setAccessible(Boolean.TRUE);
         loopField.setAccessible(Boolean.TRUE);
         List<NestedLoops> surrounded = (List<NestedLoops>) surroundedField.get(nestedLoops);
-        Cartesian2D[] loop = (Cartesian2D []) loopField.get(surrounded.get(0));
-        Set<Cartesian2D> vertexSet = new HashSet<>(Arrays.asList(loop));
+        Point2D[] loop = (Point2D []) loopField.get(surrounded.get(0));
+        Set<Point2D> vertexSet = new HashSet<>(Arrays.asList(loop));
         Assert.assertTrue(vertexSet.contains(oneOne));
         Assert.assertTrue(vertexSet.contains(oneNegativeOne));
         Assert.assertTrue(vertexSet.contains(negativeOneNegativeOne));
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
new file mode 100644
index 0000000..6892198
--- /dev/null
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Point2DTest.java
@@ -0,0 +1,262 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.geometry.euclidean.twod;
+
+import java.util.regex.Pattern;
+
+import org.apache.commons.numbers.core.Precision;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Point2DTest {
+
+    private static final double EPS = Math.ulp(1d);
+
+    @Test
+    public void testConstants() {
+        // act/assert
+        checkPoint(Point2D.ZERO, 0.0, 0.0);
+        checkPoint(Point2D.NaN, Double.NaN, Double.NaN);
+        checkPoint(Point2D.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+        checkPoint(Point2D.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+    }
+
+    @Test
+    public void testAsVector() {
+        // act/assert
+        checkVector(Point2D.of(1, 2).asVector(), 1, 2);
+        checkVector(Point2D.of(-1, -2).asVector(), -1, -2);
+        checkVector(Point2D.of(Double.NaN, Double.POSITIVE_INFINITY).asVector(), Double.NaN, Double.POSITIVE_INFINITY);
+        checkVector(Point2D.of(Double.NEGATIVE_INFINITY, Double.NaN).asVector(), Double.NEGATIVE_INFINITY, Double.NaN);
+    }
+
+    @Test
+    public void testDistance() {
+        // arrange
+        Point2D p1 = Point2D.of(1, 1);
+        Point2D p2 = Point2D.of(4, 5);
+        Point2D p3 = Point2D.of(-1, 0);
+
+        // act/assert
+        Assert.assertEquals(0, p1.distance(p1), EPS);
+        Assert.assertEquals(5, p1.distance(p2), EPS);
+        Assert.assertEquals(5, p2.distance(p1), EPS);
+
+        Assert.assertEquals(Math.sqrt(5), p1.distance(p3), EPS);
+        Assert.assertEquals(Math.sqrt(5), p3.distance(p1), EPS);
+    }
+
+    @Test
+    public void testSubtract() {
+        // arrange
+        Point2D p1 = Point2D.of(1, 1);
+        Point2D p2 = Point2D.of(4, 5);
+        Point2D p3 = Point2D.of(-1, 0);
+
+        // act/assert
+        checkVector(p1.subtract(p1), 0, 0);
+        checkVector(p1.subtract(p2), -3, -4);
+        checkVector(p2.subtract(p1), 3, 4);
+
+        checkVector(p1.subtract(p3), 2, 1);
+        checkVector(p3.subtract(p1), -2, -1);
+    }
+
+    @Test
+    public void testVectorTo() {
+        // arrange
+        Point2D p1 = Point2D.of(1, 1);
+        Point2D p2 = Point2D.of(4, 5);
+        Point2D p3 = Point2D.of(-1, 0);
+
+        // act/assert
+        checkVector(p1.vectorTo(p1), 0, 0);
+        checkVector(p1.vectorTo(p2), 3, 4);
+        checkVector(p2.vectorTo(p1), -3, -4);
+
+        checkVector(p1.vectorTo(p3), -2, -1);
+        checkVector(p3.vectorTo(p1), 2, 1);
+    }
+
+    @Test
+    public void testAdd() {
+        // arrange
+        Point2D p1 = Point2D.of(1, 1);
+        Point2D p2 = Point2D.of(-4, -5);
+
+        // act/assert
+        checkPoint(p1.add(Vector2D.ZERO), 1, 1);
+        checkPoint(p1.add(Vector2D.of(0, 1)), 1, 2);
+        checkPoint(p1.add(Vector2D.of(1, 0)), 2, 1);
+        checkPoint(p1.add(Vector2D.of(0, -1)), 1, 0);
+        checkPoint(p1.add(Vector2D.of(-1, 0)), 0, 1);
+
+        checkPoint(p2.add(Vector2D.ZERO), -4, -5);
+        checkPoint(p2.add(Vector2D.of(1, 1)), -3, -4);
+        checkPoint(p2.add(Vector2D.of(-1, -1)), -5, -6);
+    }
+
+    @Test
+    public void testHashCode() {
+        // arrange
+        Point2D u = Point2D.of(1, 1);
+        Point2D v = Point2D.of(1 + 10 * Precision.EPSILON, 1 + 10 * Precision.EPSILON);
+        Point2D w = Point2D.of(1, 1);
+
+        // act/assert
+        Assert.assertTrue(u.hashCode() != v.hashCode());
+        Assert.assertEquals(u.hashCode(), w.hashCode());
+
+        Assert.assertEquals(Point2D.of(0, Double.NaN).hashCode(), Point2D.NaN.hashCode());
+        Assert.assertEquals(Point2D.of(Double.NaN, 0).hashCode(), Point2D.NaN.hashCode());
+        Assert.assertEquals(Point2D.of(0, Double.NaN).hashCode(), Point2D.of(Double.NaN, 0).hashCode());
+    }
+
+    @Test
+    public void testEquals() {
+        // arrange
+        Point2D u1 = Point2D.of(1, 2);
+        Point2D u2 = Point2D.of(1, 2);
+
+        // act/assert
+        Assert.assertFalse(u1.equals(null));
+        Assert.assertFalse(u1.equals(new Object()));
+
+        Assert.assertTrue(u1.equals(u1));
+        Assert.assertTrue(u1.equals(u2));
+
+        Assert.assertFalse(u1.equals(Point2D.of(-1, -2)));
+        Assert.assertFalse(u1.equals(Point2D.of(1 + 10 * Precision.EPSILON, 2)));
+        Assert.assertFalse(u1.equals(Point2D.of(1, 2 + 10 * Precision.EPSILON)));
+
+        Assert.assertTrue(Point2D.of(0, Double.NaN).equals(Point2D.of(Double.NaN, 0)));
+
+        Assert.assertTrue(Point2D.of(0, Double.POSITIVE_INFINITY).equals(Point2D.of(0, Double.POSITIVE_INFINITY)));
+        Assert.assertFalse(Point2D.of(Double.POSITIVE_INFINITY, 0).equals(Point2D.of(0, Double.POSITIVE_INFINITY)));
+
+        Assert.assertTrue(Point2D.of(Double.NEGATIVE_INFINITY, 0).equals(Point2D.of(Double.NEGATIVE_INFINITY, 0)));
+        Assert.assertFalse(Point2D.of(0, Double.NEGATIVE_INFINITY).equals(Point2D.of(Double.NEGATIVE_INFINITY, 0)));
+    }
+
+    @Test
+    public void testToString() {
+        // arrange
+        Point2D p = Point2D.of(1, 2);
+        Pattern pattern = Pattern.compile("\\(1.{0,2}; 2.{0,2}\\)");
+
+        // act
+        String str = p.toString();
+
+        // assert
+        Assert.assertTrue("Expected string " + str + " to match regex " + pattern,
+                    pattern.matcher(str).matches());
+    }
+
+    @Test
+    public void testOf() {
+        // act/assert
+        checkPoint(Point2D.of(0, 1), 0, 1);
+        checkPoint(Point2D.of(-1, -2), -1, -2);
+        checkPoint(Point2D.of(Math.PI, Double.NaN), Math.PI, Double.NaN);
+        checkPoint(Point2D.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY), Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
+    }
+
+    @Test
+    public void testOf_coordinateArg() {
+        // act/assert
+        checkPoint(Point2D.of(Vector2D.of(0, 1)), 0, 1);
+        checkPoint(Point2D.of(Vector2D.of(-1, -2)), -1, -2);
+        checkPoint(Point2D.of(Vector2D.of(Math.PI, Double.NaN)), Math.PI, Double.NaN);
+        checkPoint(Point2D.of(Vector2D.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY)), Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
+    }
+
+    @Test
+    public void testOf_arrayArg() {
+        // act/assert
+        checkPoint(Point2D.of(new double[] { 0, 1 }), 0, 1);
+        checkPoint(Point2D.of(new double[] { -1, -2 }), -1, -2);
+        checkPoint(Point2D.of(new double[] { Math.PI, Double.NaN }), Math.PI, Double.NaN);
+        checkPoint(Point2D.of(new double[] { Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY }), Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testOf_arrayArg_invalidDimensions() {
+        // act/assert
+        Point2D.of(new double[] {0.0 });
+    }
+
+    @Test
+    public void testVectorCombination1() {
+        // arrange
+        Point2D p1 = Point2D.of(1, 2);
+
+        // act/assert
+        checkPoint(Point2D.vectorCombination(0, p1), 0, 0);
+
+        checkPoint(Point2D.vectorCombination(1, p1), 1, 2);
+        checkPoint(Point2D.vectorCombination(-1, p1), -1, -2);
+
+        checkPoint(Point2D.vectorCombination(0.5, p1), 0.5, 1);
+        checkPoint(Point2D.vectorCombination(-0.5, p1), -0.5, -1);
+    }
+
+    @Test
+    public void testVectorCombination2() {
+        // arrange
+        Point2D p1 = Point2D.of(1, 2);
+        Point2D p2 = Point2D.of(-3, -4);
+
+        // act/assert
+        checkPoint(Point2D.vectorCombination(2, p1, -3, p2), 11, 16);
+        checkPoint(Point2D.vectorCombination(-3, p1, 2, p2), -9, -14);
+    }
+
+    @Test
+    public void testVectorCombination3() {
+        // arrange
+        Point2D p1 = Point2D.of(1, 2);
+        Point2D p2 = Point2D.of(-3, -4);
+        Point2D p3 = Point2D.of(5, 6);
+
+        // act/assert
+        checkPoint(Point2D.vectorCombination(2, p1, -3, p2, 4, p3), 31, 40);
+        checkPoint(Point2D.vectorCombination(-3, p1, 2, p2, -4, p3), -29, -38);
+    }
+
+    @Test
+    public void testVectorCombination4() {
+        // arrange
+        Point2D p1 = Point2D.of(1, 2);
+        Point2D p2 = Point2D.of(-3, -4);
+        Point2D p3 = Point2D.of(5, 6);
+        Point2D p4 = Point2D.of(-7, -8);
+
+        // act/assert
+        checkPoint(Point2D.vectorCombination(2, p1, -3, p2, 4, p3, -5, p4), 66, 80);
+        checkPoint(Point2D.vectorCombination(-3, p1, 2, p2, -4, p3, 5, p4), -64, -78);
+    }
+
+    private void checkVector(Vector2D v, double x, double y) {
+        Assert.assertEquals(x, v.getX(), EPS);
+        Assert.assertEquals(y, v.getY(), EPS);
+    }
+
+    private void checkPoint(Point2D p, double x, double y) {
+        Assert.assertEquals(x, p.getX(), EPS);
+        Assert.assertEquals(y, p.getY(), EPS);
+    }
+}
diff --git a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolygonsSetTest.java b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolygonsSetTest.java
index 3e4f77b..f565ea6 100644
--- a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolygonsSetTest.java
+++ b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/PolygonsSetTest.java
@@ -25,13 +25,13 @@
 import org.apache.commons.geometry.core.partitioning.BoundaryProjection;
 import org.apache.commons.geometry.core.partitioning.Hyperplane;
 import org.apache.commons.geometry.core.partitioning.Region;
+import org.apache.commons.geometry.core.partitioning.Region.Location;
 import org.apache.commons.geometry.core.partitioning.RegionFactory;
 import org.apache.commons.geometry.core.partitioning.SubHyperplane;
-import org.apache.commons.geometry.core.partitioning.Region.Location;
 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
-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;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
 import org.junit.Test;
@@ -52,16 +52,16 @@ public void testFull() {
         Assert.assertEquals(0, poly.getVertices().length);
         Assert.assertFalse(poly.isEmpty());
         Assert.assertTrue(poly.isFull());
-        EuclideanTestUtils.assertVectorEquals(Cartesian2D.NaN, (Cartesian2D) poly.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point2D.NaN, poly.getBarycenter(), TEST_TOLERANCE);
 
         checkPoints(Region.Location.INSIDE, poly,
-                new Cartesian2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY),
-                Cartesian2D.ZERO,
-                new Cartesian2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
+                new Point2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY),
+                Point2D.ZERO,
+                new Point2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
 
         for (double y = -1; y < 1; y += 0.1) {
             for (double x = -1; x < 1; x += 0.1) {
-                EuclideanTestUtils.assertNegativeInfinity(poly.projectToBoundary(new Cartesian2D(x, y)).getOffset());
+                EuclideanTestUtils.assertNegativeInfinity(poly.projectToBoundary(new Point2D(x, y)).getOffset());
             }
         }
     }
@@ -69,7 +69,7 @@ public void testFull() {
     @Test
     public void testEmpty() {
         // act
-        PolygonsSet poly = (PolygonsSet) new RegionFactory<Euclidean2D>().getComplement(new PolygonsSet(TEST_TOLERANCE));
+        PolygonsSet poly = (PolygonsSet) new RegionFactory<Point2D>().getComplement(new PolygonsSet(TEST_TOLERANCE));
 
         // assert
         Assert.assertEquals(TEST_TOLERANCE, poly.getTolerance(), Precision.EPSILON);
@@ -78,17 +78,17 @@ public void testEmpty() {
         Assert.assertEquals(0, poly.getVertices().length);
         Assert.assertTrue(poly.isEmpty());
         Assert.assertFalse(poly.isFull());
-        EuclideanTestUtils.assertVectorEquals(Cartesian2D.NaN, (Cartesian2D) poly.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point2D.NaN, poly.getBarycenter(), TEST_TOLERANCE);
 
         checkPoints(Region.Location.OUTSIDE, poly,
-                new Cartesian2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY),
-                Cartesian2D.ZERO,
-                new Cartesian2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
+                new Point2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY),
+                Point2D.ZERO,
+                new Point2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
 
 
         for (double y = -1; y < 1; y += 0.1) {
             for (double x = -1; x < 1; x += 0.1) {
-                EuclideanTestUtils.assertPositiveInfinity(poly.projectToBoundary(new Cartesian2D(x, y)).getOffset());
+                EuclideanTestUtils.assertPositiveInfinity(poly.projectToBoundary(new Point2D(x, y)).getOffset());
             }
         }
     }
@@ -96,9 +96,9 @@ public void testEmpty() {
     @Test
     public void testInfiniteLines_single() {
         // arrange
-        Line line = new Line(new Cartesian2D(0, 0), new Cartesian2D(1, 1), TEST_TOLERANCE);
+        Line line = new Line(new Point2D(0, 0), new Point2D(1, 1), TEST_TOLERANCE);
 
-        List<SubHyperplane<Euclidean2D>> boundaries = new ArrayList<SubHyperplane<Euclidean2D>>();
+        List<SubHyperplane<Point2D>> boundaries = new ArrayList<SubHyperplane<Point2D>>();
         boundaries.add(line.wholeHyperplane());
 
         // act
@@ -110,32 +110,32 @@ public void testInfiniteLines_single() {
         EuclideanTestUtils.assertPositiveInfinity(poly.getBoundarySize());
         Assert.assertFalse(poly.isEmpty());
         Assert.assertFalse(poly.isFull());
-        EuclideanTestUtils.assertVectorEquals(Cartesian2D.NaN, (Cartesian2D) poly.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point2D.NaN, poly.getBarycenter(), TEST_TOLERANCE);
 
-        checkVertexLoopsEquivalent(new Cartesian2D[][] {
+        checkVertexLoopsEquivalent(new Point2D[][] {
             {
                 null,
-                line.toSpace(new Cartesian1D(-Float.MAX_VALUE)),
-                line.toSpace(new Cartesian1D(Float.MAX_VALUE))
+                line.toSpace(new Point1D(-Float.MAX_VALUE)),
+                line.toSpace(new Point1D(Float.MAX_VALUE))
             }
         }, poly.getVertices());
 
         checkPoints(Region.Location.OUTSIDE, poly,
-                new Cartesian2D(1, -1),
-                new Cartesian2D(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
+                new Point2D(1, -1),
+                new Point2D(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
         checkPoints(Region.Location.INSIDE, poly,
-                new Cartesian2D(-1, 1),
-                new Cartesian2D(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
-        checkPoints(Region.Location.BOUNDARY, poly, Cartesian2D.ZERO);
+                new Point2D(-1, 1),
+                new Point2D(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
+        checkPoints(Region.Location.BOUNDARY, poly, Point2D.ZERO);
     }
 
     @Test
     public void testInfiniteLines_twoIntersecting() {
         // arrange
-        Line line1 = new Line(new Cartesian2D(0, 0), new Cartesian2D(1, 1), TEST_TOLERANCE);
-        Line line2 = new Line(new Cartesian2D(1, -1), new Cartesian2D(0, 0), TEST_TOLERANCE);
+        Line line1 = new Line(new Point2D(0, 0), new Point2D(1, 1), TEST_TOLERANCE);
+        Line line2 = new Line(new Point2D(1, -1), new Point2D(0, 0), TEST_TOLERANCE);
 
-        List<SubHyperplane<Euclidean2D>> boundaries = new ArrayList<SubHyperplane<Euclidean2D>>();
+        List<SubHyperplane<Point2D>> boundaries = new ArrayList<SubHyperplane<Point2D>>();
         boundaries.add(line1.wholeHyperplane());
         boundaries.add(line2.wholeHyperplane());
 
@@ -148,32 +148,32 @@ public void testInfiniteLines_twoIntersecting() {
         EuclideanTestUtils.assertPositiveInfinity(poly.getBoundarySize());
         Assert.assertFalse(poly.isEmpty());
         Assert.assertFalse(poly.isFull());
-        EuclideanTestUtils.assertVectorEquals(Cartesian2D.NaN, (Cartesian2D) poly.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point2D.NaN, poly.getBarycenter(), TEST_TOLERANCE);
 
-        checkVertexLoopsEquivalent(new Cartesian2D[][] {
+        checkVertexLoopsEquivalent(new Point2D[][] {
             {
                 null,
-                line2.toSpace(new Cartesian1D(-Float.MAX_VALUE)),
-                line2.toSpace(new Cartesian1D(Float.MAX_VALUE))
+                line2.toSpace(new Point1D(-Float.MAX_VALUE)),
+                line2.toSpace(new Point1D(Float.MAX_VALUE))
             }
         }, poly.getVertices());
 
         checkPoints(Region.Location.INSIDE, poly,
-                new Cartesian2D(-1, 0),
-                new Cartesian2D(-Float.MAX_VALUE, Float.MAX_VALUE / 2.0));
+                new Point2D(-1, 0),
+                new Point2D(-Float.MAX_VALUE, Float.MAX_VALUE / 2.0));
         checkPoints(Region.Location.OUTSIDE, poly,
-                new Cartesian2D(1, 0),
-                new Cartesian2D(Float.MAX_VALUE, Float.MAX_VALUE / 2.0));
-        checkPoints(Region.Location.BOUNDARY, poly, Cartesian2D.ZERO);
+                new Point2D(1, 0),
+                new Point2D(Float.MAX_VALUE, Float.MAX_VALUE / 2.0));
+        checkPoints(Region.Location.BOUNDARY, poly, Point2D.ZERO);
     }
 
     @Test
     public void testInfiniteLines_twoParallel_facingIn() {
         // arrange
-        Line line1 = new Line(new Cartesian2D(1, 1), new Cartesian2D(0, 1), TEST_TOLERANCE);
-        Line line2 = new Line(new Cartesian2D(0, -1), new Cartesian2D(1, -1), TEST_TOLERANCE);
+        Line line1 = new Line(new Point2D(1, 1), new Point2D(0, 1), TEST_TOLERANCE);
+        Line line2 = new Line(new Point2D(0, -1), new Point2D(1, -1), TEST_TOLERANCE);
 
-        List<SubHyperplane<Euclidean2D>> boundaries = new ArrayList<SubHyperplane<Euclidean2D>>();
+        List<SubHyperplane<Point2D>> boundaries = new ArrayList<SubHyperplane<Point2D>>();
         boundaries.add(line1.wholeHyperplane());
         boundaries.add(line2.wholeHyperplane());
 
@@ -186,40 +186,40 @@ public void testInfiniteLines_twoParallel_facingIn() {
         EuclideanTestUtils.assertPositiveInfinity(poly.getBoundarySize());
         Assert.assertFalse(poly.isEmpty());
         Assert.assertFalse(poly.isFull());
-        EuclideanTestUtils.assertVectorEquals(Cartesian2D.NaN, (Cartesian2D) poly.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point2D.NaN, poly.getBarycenter(), TEST_TOLERANCE);
 
-        checkVertexLoopsEquivalent(new Cartesian2D[][] {
+        checkVertexLoopsEquivalent(new Point2D[][] {
             {
                 null,
-                line1.toSpace(new Cartesian1D(-Float.MAX_VALUE)),
-                line1.toSpace(new Cartesian1D(Float.MAX_VALUE))
+                line1.toSpace(new Point1D(-Float.MAX_VALUE)),
+                line1.toSpace(new Point1D(Float.MAX_VALUE))
             },
             {
                 null,
-                line2.toSpace(new Cartesian1D(-Float.MAX_VALUE)),
-                line2.toSpace(new Cartesian1D(Float.MAX_VALUE))
+                line2.toSpace(new Point1D(-Float.MAX_VALUE)),
+                line2.toSpace(new Point1D(Float.MAX_VALUE))
             }
         }, poly.getVertices());
 
         checkPoints(Region.Location.INSIDE, poly,
-                new Cartesian2D(0, 0),
-                new Cartesian2D(0, 0.9),
-                new Cartesian2D(0, -0.9));
+                new Point2D(0, 0),
+                new Point2D(0, 0.9),
+                new Point2D(0, -0.9));
         checkPoints(Region.Location.OUTSIDE, poly,
-                new Cartesian2D(0, 1.1),
-                new Cartesian2D(0, -1.1));
+                new Point2D(0, 1.1),
+                new Point2D(0, -1.1));
         checkPoints(Region.Location.BOUNDARY, poly,
-                new Cartesian2D(0, 1),
-                new Cartesian2D(0, -1));
+                new Point2D(0, 1),
+                new Point2D(0, -1));
     }
 
     @Test
     public void testInfiniteLines_twoParallel_facingOut() {
         // arrange
-        Line line1 = new Line(new Cartesian2D(0, 1), new Cartesian2D(1, 1), TEST_TOLERANCE);
-        Line line2 = new Line(new Cartesian2D(1, -1), new Cartesian2D(0, -1), TEST_TOLERANCE);
+        Line line1 = new Line(new Point2D(0, 1), new Point2D(1, 1), TEST_TOLERANCE);
+        Line line2 = new Line(new Point2D(1, -1), new Point2D(0, -1), TEST_TOLERANCE);
 
-        List<SubHyperplane<Euclidean2D>> boundaries = new ArrayList<SubHyperplane<Euclidean2D>>();
+        List<SubHyperplane<Point2D>> boundaries = new ArrayList<SubHyperplane<Point2D>>();
         boundaries.add(line1.wholeHyperplane());
         boundaries.add(line2.wholeHyperplane());
 
@@ -232,43 +232,43 @@ public void testInfiniteLines_twoParallel_facingOut() {
         EuclideanTestUtils.assertPositiveInfinity(poly.getBoundarySize());
         Assert.assertFalse(poly.isEmpty());
         Assert.assertFalse(poly.isFull());
-        EuclideanTestUtils.assertVectorEquals(Cartesian2D.NaN, (Cartesian2D) poly.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point2D.NaN, poly.getBarycenter(), TEST_TOLERANCE);
 
-        checkVertexLoopsEquivalent(new Cartesian2D[][] {
+        checkVertexLoopsEquivalent(new Point2D[][] {
             {
                 null,
-                line1.toSpace(new Cartesian1D(-Float.MAX_VALUE)),
-                line1.toSpace(new Cartesian1D(Float.MAX_VALUE))
+                line1.toSpace(new Point1D(-Float.MAX_VALUE)),
+                line1.toSpace(new Point1D(Float.MAX_VALUE))
             },
             {
                 null,
-                line2.toSpace(new Cartesian1D(-Float.MAX_VALUE)),
-                line2.toSpace(new Cartesian1D(Float.MAX_VALUE))
+                line2.toSpace(new Point1D(-Float.MAX_VALUE)),
+                line2.toSpace(new Point1D(Float.MAX_VALUE))
             }
         }, poly.getVertices());
 
         checkPoints(Region.Location.OUTSIDE, poly,
-                new Cartesian2D(0, 0),
-                new Cartesian2D(0, 0.9),
-                new Cartesian2D(0, -0.9));
+                new Point2D(0, 0),
+                new Point2D(0, 0.9),
+                new Point2D(0, -0.9));
         checkPoints(Region.Location.INSIDE, poly,
-                new Cartesian2D(0, 1.1),
-                new Cartesian2D(0, -1.1));
+                new Point2D(0, 1.1),
+                new Point2D(0, -1.1));
         checkPoints(Region.Location.BOUNDARY, poly,
-                new Cartesian2D(0, 1),
-                new Cartesian2D(0, -1));
+                new Point2D(0, 1),
+                new Point2D(0, -1));
     }
 
     @Test
     public void testMixedFiniteAndInfiniteLines_explicitInfiniteBoundaries() {
         // arrange
-        Line line1 = new Line(new Cartesian2D(3, 3), new Cartesian2D(0, 3), TEST_TOLERANCE);
-        Line line2 = new Line(new Cartesian2D(0, -3), new Cartesian2D(3, -3), TEST_TOLERANCE);
+        Line line1 = new Line(new Point2D(3, 3), new Point2D(0, 3), TEST_TOLERANCE);
+        Line line2 = new Line(new Point2D(0, -3), new Point2D(3, -3), TEST_TOLERANCE);
 
-        List<SubHyperplane<Euclidean2D>> boundaries = new ArrayList<SubHyperplane<Euclidean2D>>();
+        List<SubHyperplane<Point2D>> boundaries = new ArrayList<SubHyperplane<Point2D>>();
         boundaries.add(line1.wholeHyperplane());
         boundaries.add(line2.wholeHyperplane());
-        boundaries.add(buildSegment(new Cartesian2D(0, 3), new Cartesian2D(0, -3)));
+        boundaries.add(buildSegment(new Point2D(0, 3), new Point2D(0, -3)));
 
         // act
         PolygonsSet poly = new PolygonsSet(boundaries, TEST_TOLERANCE);
@@ -279,30 +279,30 @@ public void testMixedFiniteAndInfiniteLines_explicitInfiniteBoundaries() {
         EuclideanTestUtils.assertPositiveInfinity(poly.getBoundarySize());
         Assert.assertFalse(poly.isEmpty());
         Assert.assertFalse(poly.isFull());
-        EuclideanTestUtils.assertVectorEquals(Cartesian2D.NaN, (Cartesian2D) poly.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point2D.NaN, poly.getBarycenter(), TEST_TOLERANCE);
 
-        checkVertexLoopsEquivalent(new Cartesian2D[][] {
+        checkVertexLoopsEquivalent(new Point2D[][] {
             {
                 null,
-                new Cartesian2D(1, 3), // dummy point
-                new Cartesian2D(0, 3),
-                new Cartesian2D(0, -3),
-                new Cartesian2D(1, -3) // dummy point
+                new Point2D(1, 3), // dummy point
+                new Point2D(0, 3),
+                new Point2D(0, -3),
+                new Point2D(1, -3) // dummy point
             }
         }, poly.getVertices());
 
         checkPoints(Region.Location.INSIDE, poly,
-                new Cartesian2D(0.1, 2.9),
-                new Cartesian2D(0.1, 0),
-                new Cartesian2D(0.1, -2.9));
+                new Point2D(0.1, 2.9),
+                new Point2D(0.1, 0),
+                new Point2D(0.1, -2.9));
         checkPoints(Region.Location.OUTSIDE, poly,
-                new Cartesian2D(0, 3.1),
-                new Cartesian2D(-0.5, 0),
-                new Cartesian2D(0, -3.1));
+                new Point2D(0, 3.1),
+                new Point2D(-0.5, 0),
+                new Point2D(0, -3.1));
         checkPoints(Region.Location.BOUNDARY, poly,
-                new Cartesian2D(3, 3),
-                new Cartesian2D(0, 0),
-                new Cartesian2D(3, -3));
+                new Point2D(3, 3),
+                new Point2D(0, 0),
+                new Point2D(3, -3));
     }
 
     // The polygon in this test is created from finite boundaries but the generated
@@ -313,11 +313,11 @@ public void testMixedFiniteAndInfiniteLines_explicitInfiniteBoundaries() {
     @Test
     public void testMixedFiniteAndInfiniteLines_impliedInfiniteBoundaries() {
         // arrange
-        Line line = new Line(new Cartesian2D(3, 0), new Cartesian2D(3, 3), TEST_TOLERANCE);
+        Line line = new Line(new Point2D(3, 0), new Point2D(3, 3), TEST_TOLERANCE);
 
-        List<SubHyperplane<Euclidean2D>> boundaries = new ArrayList<SubHyperplane<Euclidean2D>>();
-        boundaries.add(buildSegment(new Cartesian2D(0, 3), new Cartesian2D(0, 0)));
-        boundaries.add(buildSegment(new Cartesian2D(0, 0), new Cartesian2D(3, 0)));
+        List<SubHyperplane<Point2D>> boundaries = new ArrayList<SubHyperplane<Point2D>>();
+        boundaries.add(buildSegment(new Point2D(0, 3), new Point2D(0, 0)));
+        boundaries.add(buildSegment(new Point2D(0, 0), new Point2D(3, 0)));
         boundaries.add(new SubLine(line, new IntervalsSet(0, Double.POSITIVE_INFINITY, TEST_TOLERANCE)));
 
         // act
@@ -329,34 +329,34 @@ public void testMixedFiniteAndInfiniteLines_impliedInfiniteBoundaries() {
         EuclideanTestUtils.assertPositiveInfinity(poly.getBoundarySize());
         Assert.assertFalse(poly.isEmpty());
         Assert.assertFalse(poly.isFull());
-        EuclideanTestUtils.assertVectorEquals(Cartesian2D.NaN, (Cartesian2D) poly.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point2D.NaN, poly.getBarycenter(), TEST_TOLERANCE);
 
-        checkVertexLoopsEquivalent(new Cartesian2D[][] {
+        checkVertexLoopsEquivalent(new Point2D[][] {
             {
                 null,
-                new Cartesian2D(0, 1), // dummy point
-                new Cartesian2D(0, 0),
-                new Cartesian2D(3, 0),
-                new Cartesian2D(3, 1) // dummy point
+                new Point2D(0, 1), // dummy point
+                new Point2D(0, 0),
+                new Point2D(3, 0),
+                new Point2D(3, 1) // dummy point
             }
         }, poly.getVertices());
 
         checkPoints(Region.Location.INSIDE, poly,
-                new Cartesian2D(0.1, Float.MAX_VALUE),
-                new Cartesian2D(0.1, 0.1),
-                new Cartesian2D(1.5, 0.1),
-                new Cartesian2D(2.9, 0.1),
-                new Cartesian2D(2.9, Float.MAX_VALUE));
+                new Point2D(0.1, Float.MAX_VALUE),
+                new Point2D(0.1, 0.1),
+                new Point2D(1.5, 0.1),
+                new Point2D(2.9, 0.1),
+                new Point2D(2.9, Float.MAX_VALUE));
         checkPoints(Region.Location.OUTSIDE, poly,
-                new Cartesian2D(-0.1, Float.MAX_VALUE),
-                new Cartesian2D(-0.1, 0.1),
-                new Cartesian2D(1.5, -0.1),
-                new Cartesian2D(3.1, 0.1),
-                new Cartesian2D(3.1, Float.MAX_VALUE));
+                new Point2D(-0.1, Float.MAX_VALUE),
+                new Point2D(-0.1, 0.1),
+                new Point2D(1.5, -0.1),
+                new Point2D(3.1, 0.1),
+                new Point2D(3.1, Float.MAX_VALUE));
         checkPoints(Region.Location.BOUNDARY, poly,
-                new Cartesian2D(0, 1),
-                new Cartesian2D(1, 0),
-                new Cartesian2D(3, 1));
+                new Point2D(0, 1),
+                new Point2D(1, 0),
+                new Point2D(3, 1));
     }
 
     @Test
@@ -369,42 +369,42 @@ public void testBox() {
         Assert.assertEquals(8.0, box.getBoundarySize(), TEST_TOLERANCE);
         Assert.assertFalse(box.isEmpty());
         Assert.assertFalse(box.isFull());
-        EuclideanTestUtils.assertVectorEquals(new Cartesian2D(1, 0), (Cartesian2D) box.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(new Point2D(1, 0), box.getBarycenter(), TEST_TOLERANCE);
 
-        checkVertexLoopsEquivalent(new Cartesian2D[][] {
+        checkVertexLoopsEquivalent(new Point2D[][] {
             {
-                new Cartesian2D(2, -1),
-                new Cartesian2D(2, 1),
-                new Cartesian2D(0, 1),
-                new Cartesian2D(0, -1)
+                new Point2D(2, -1),
+                new Point2D(2, 1),
+                new Point2D(0, 1),
+                new Point2D(0, -1)
             }
         }, box.getVertices());
 
         checkPoints(Region.Location.INSIDE, box,
-                new Cartesian2D(0.1, 0),
-                new Cartesian2D(1.9, 0),
-                new Cartesian2D(1, 0.9),
-                new Cartesian2D(1, -0.9));
+                new Point2D(0.1, 0),
+                new Point2D(1.9, 0),
+                new Point2D(1, 0.9),
+                new Point2D(1, -0.9));
         checkPoints(Region.Location.OUTSIDE, box,
-                new Cartesian2D(-0.1, 0),
-                new Cartesian2D(2.1, 0),
-                new Cartesian2D(1, -1.1),
-                new Cartesian2D(1, 1.1));
+                new Point2D(-0.1, 0),
+                new Point2D(2.1, 0),
+                new Point2D(1, -1.1),
+                new Point2D(1, 1.1));
         checkPoints(Region.Location.BOUNDARY, box,
-                new Cartesian2D(0, 0),
-                new Cartesian2D(2, 0),
-                new Cartesian2D(1, 1),
-                new Cartesian2D(1, -1));
+                new Point2D(0, 0),
+                new Point2D(2, 0),
+                new Point2D(1, 1),
+                new Point2D(1, -1));
     }
 
     @Test
     public void testInvertedBox() {
         // arrange
-        List<SubHyperplane<Euclidean2D>> boundaries = new ArrayList<SubHyperplane<Euclidean2D>>();
-        boundaries.add(buildSegment(new Cartesian2D(0, -1), new Cartesian2D(0, 1)));
-        boundaries.add(buildSegment(new Cartesian2D(2, 1), new Cartesian2D(2, -1)));
-        boundaries.add(buildSegment(new Cartesian2D(0, 1), new Cartesian2D(2, 1)));
-        boundaries.add(buildSegment(new Cartesian2D(2, -1), new Cartesian2D(0, -1)));
+        List<SubHyperplane<Point2D>> boundaries = new ArrayList<SubHyperplane<Point2D>>();
+        boundaries.add(buildSegment(new Point2D(0, -1), new Point2D(0, 1)));
+        boundaries.add(buildSegment(new Point2D(2, 1), new Point2D(2, -1)));
+        boundaries.add(buildSegment(new Point2D(0, 1), new Point2D(2, 1)));
+        boundaries.add(buildSegment(new Point2D(2, -1), new Point2D(0, -1)));
 
         // act
         PolygonsSet box = new PolygonsSet(boundaries, TEST_TOLERANCE);
@@ -414,49 +414,49 @@ public void testInvertedBox() {
         Assert.assertEquals(8.0, box.getBoundarySize(), TEST_TOLERANCE);
         Assert.assertFalse(box.isEmpty());
         Assert.assertFalse(box.isFull());
-        EuclideanTestUtils.assertVectorEquals(Cartesian2D.NaN, (Cartesian2D) box.getBarycenter(), TEST_TOLERANCE);
+        EuclideanTestUtils.assertCoordinatesEqual(Point2D.NaN, box.getBarycenter(), TEST_TOLERANCE);
 
-        checkVertexLoopsEquivalent(new Cartesian2D[][] {
+        checkVertexLoopsEquivalent(new Point2D[][] {
             {
-                new Cartesian2D(0, -1),
-                new Cartesian2D(0, 1),
-                new Cartesian2D(2, 1),
-                new Cartesian2D(2, -1)
+                new Point2D(0, -1),
+                new Point2D(0, 1),
+                new Point2D(2, 1),
+                new Point2D(2, -1)
             }
         }, box.getVertices());
 
         checkPoints(Region.Location.OUTSIDE, box,
-                new Cartesian2D(0.1, 0),
-                new Cartesian2D(1.9, 0),
-                new Cartesian2D(1, 0.9),
-                new Cartesian2D(1, -0.9));
+                new Point2D(0.1, 0),
+                new Point2D(1.9, 0),
+                new Point2D(1, 0.9),
+                new Point2D(1, -0.9));
         checkPoints(Region.Location.INSIDE, box,
-                new Cartesian2D(-0.1, 0),
-                new Cartesian2D(2.1, 0),
-                new Cartesian2D(1, -1.1),
-                new Cartesian2D(1, 1.1));
+                new Point2D(-0.1, 0),
+                new Point2D(2.1, 0),
+                new Point2D(1, -1.1),
+                new Point2D(1, 1.1));
         checkPoints(Region.Location.BOUNDARY, box,
-                new Cartesian2D(0, 0),
-                new Cartesian2D(2, 0),
-                new Cartesian2D(1, 1),
-                new Cartesian2D(1, -1));
+                new Point2D(0, 0),
+                new Point2D(2, 0),
+                new Point2D(1, 1),
+                new Point2D(1, -1));
     }
 
     @Test
     public void testSimplyConnected() {
         // arrange
-        Cartesian2D[][] vertices = new Cartesian2D[][] {
-            new Cartesian2D[] {
-                new Cartesian2D(36.0, 22.0),
-                new Cartesian2D(39.0, 32.0),
-                new Cartesian2D(19.0, 32.0),
-                new Cartesian2D( 6.0, 16.0),
-                new Cartesian2D(31.0, 10.0),
-                new Cartesian2D(42.0, 16.0),
-                new Cartesian2D(34.0, 20.0),
-                new Cartesian2D(29.0, 19.0),
-                new Cartesian2D(23.0, 22.0),
-                new Cartesian2D(33.0, 25.0)
+        Point2D[][] vertices = new Point2D[][] {
+            new Point2D[] {
+                new Point2D(36.0, 22.0),
+                new Point2D(39.0, 32.0),
+                new Point2D(19.0, 32.0),
+                new Point2D( 6.0, 16.0),
+                new Point2D(31.0, 10.0),
+                new Point2D(42.0, 16.0),
+                new Point2D(34.0, 20.0),
+                new Point2D(29.0, 19.0),
+                new Point2D(23.0, 22.0),
+                new Point2D(33.0, 25.0)
             }
         };
 
@@ -465,22 +465,22 @@ public void testSimplyConnected() {
 
         // assert
         checkPoints(Region.Location.INSIDE, set,
-            new Cartesian2D(30.0, 15.0),
-            new Cartesian2D(15.0, 20.0),
-            new Cartesian2D(24.0, 25.0),
-            new Cartesian2D(35.0, 30.0),
-            new Cartesian2D(19.0, 17.0));
+            new Point2D(30.0, 15.0),
+            new Point2D(15.0, 20.0),
+            new Point2D(24.0, 25.0),
+            new Point2D(35.0, 30.0),
+            new Point2D(19.0, 17.0));
         checkPoints(Region.Location.OUTSIDE, set,
-            new Cartesian2D(50.0, 30.0),
-            new Cartesian2D(30.0, 35.0),
-            new Cartesian2D(10.0, 25.0),
-            new Cartesian2D(10.0, 10.0),
-            new Cartesian2D(40.0, 10.0),
-            new Cartesian2D(50.0, 15.0),
-            new Cartesian2D(30.0, 22.0));
+            new Point2D(50.0, 30.0),
+            new Point2D(30.0, 35.0),
+            new Point2D(10.0, 25.0),
+            new Point2D(10.0, 10.0),
+            new Point2D(40.0, 10.0),
+            new Point2D(50.0, 15.0),
+            new Point2D(30.0, 22.0));
         checkPoints(Region.Location.BOUNDARY, set,
-            new Cartesian2D(30.0, 32.0),
-            new Cartesian2D(34.0, 20.0));
+            new Point2D(30.0, 32.0),
+            new Point2D(34.0, 20.0));
 
         checkVertexLoopsEquivalent(vertices, set.getVertices());
     }
@@ -488,18 +488,18 @@ public void testSimplyConnected() {
     @Test
     public void testStair() {
         // arrange
-        Cartesian2D[][] vertices = new Cartesian2D[][] {
-            new Cartesian2D[] {
-                new Cartesian2D( 0.0, 0.0),
-                new Cartesian2D( 0.0, 2.0),
-                new Cartesian2D(-0.1, 2.0),
-                new Cartesian2D(-0.1, 1.0),
-                new Cartesian2D(-0.3, 1.0),
-                new Cartesian2D(-0.3, 1.5),
-                new Cartesian2D(-1.3, 1.5),
-                new Cartesian2D(-1.3, 2.0),
-                new Cartesian2D(-1.8, 2.0),
-                new Cartesian2D(-1.8 - 1.0 / Math.sqrt(2.0),
+        Point2D[][] vertices = new Point2D[][] {
+            new Point2D[] {
+                new Point2D( 0.0, 0.0),
+                new Point2D( 0.0, 2.0),
+                new Point2D(-0.1, 2.0),
+                new Point2D(-0.1, 1.0),
+                new Point2D(-0.3, 1.0),
+                new Point2D(-0.3, 1.5),
+                new Point2D(-1.3, 1.5),
+                new Point2D(-1.3, 2.0),
+                new Point2D(-1.8, 2.0),
+                new Point2D(-1.8 - 1.0 / Math.sqrt(2.0),
                             2.0 - 1.0 / Math.sqrt(2.0))
             }

  (This diff was longer than 20,000 lines, and has been truncated...)


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org