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

jsorel pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new ad1fce0356 feat(Geometry): math Matrix extends geoapi Matrix, cleaning 
Similarity classes
ad1fce0356 is described below

commit ad1fce03567e9af399e4dcb3093c8f2f322d315c
Author: jsorel <[email protected]>
AuthorDate: Thu Mar 5 14:41:27 2026 +0100

    feat(Geometry): math Matrix extends geoapi Matrix, cleaning Similarity 
classes
---
 .../main/org/apache/sis/geometries/Geometries.java |  17 +-
 .../apache/sis/geometries/math/AbstractMatrix.java | 142 +++---
 .../sis/geometries/math/AbstractSimilarity.java    | 294 +++++++++++
 .../org/apache/sis/geometries/math/Affine.java     |   2 +-
 .../org/apache/sis/geometries/math/Affine1D.java   |   4 +-
 .../org/apache/sis/geometries/math/Affine2D.java   |  12 +-
 .../org/apache/sis/geometries/math/Affine3D.java   |  28 +-
 .../org/apache/sis/geometries/math/Affine4D.java   |  46 +-
 .../org/apache/sis/geometries/math/Matrices.java   |  44 +-
 .../org/apache/sis/geometries/math/Matrix.java     |  58 ++-
 .../org/apache/sis/geometries/math/Matrix2D.java   |  28 +-
 .../org/apache/sis/geometries/math/Matrix3D.java   | 149 +++---
 .../org/apache/sis/geometries/math/Matrix4D.java   |  97 ++--
 .../org/apache/sis/geometries/math/MatrixND.java   |  42 +-
 .../org/apache/sis/geometries/math/Quaternion.java |  15 +-
 .../org/apache/sis/geometries/math/Similarity.java |  92 +++-
 .../apache/sis/geometries/math/Similarity3D.java   | 176 +++++++
 .../apache/sis/geometries/math/SimilarityND.java   | 551 ++-------------------
 .../org/apache/sis/geometries/scene/SceneNode.java |   6 +-
 .../apache/sis/geometries/math/QuaternionTest.java |   2 +-
 20 files changed, 918 insertions(+), 887 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java
index 42d95d729a..227d69454e 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Geometries.java
@@ -55,6 +55,7 @@ import org.apache.sis.geometries.math.Array;
 import org.apache.sis.geometries.mesh.MeshPrimitive;
 import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.geometries.internal.shared.ArraySequence;
+import org.apache.sis.geometries.math.Matrix3D;
 import org.apache.sis.geometry.wrapper.jts.JTS;
 import org.apache.sis.measure.Units;
 import org.apache.sis.referencing.CRS;
@@ -63,7 +64,6 @@ import org.apache.sis.referencing.cs.DefaultCartesianCS;
 import org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis;
 import org.apache.sis.referencing.cs.DefaultLinearCS;
 import org.apache.sis.referencing.datum.DefaultEngineeringDatum;
-import org.apache.sis.referencing.operation.matrix.Matrix3;
 import org.apache.sis.referencing.operation.transform.LinearTransform;
 import org.apache.sis.referencing.internal.shared.AxisDirections;
 import org.apache.sis.util.ArgumentChecks;
@@ -228,21 +228,10 @@ public final class Geometries {
     /**
      * Get rotation matrix from Geometry crs to another.
      */
-    public static Matrix3 createRotation(CoordinateReferenceSystem crs1, 
CoordinateReferenceSystem crs2) throws FactoryException {
+    public static Matrix3D createRotation(CoordinateReferenceSystem crs1, 
CoordinateReferenceSystem crs2) throws FactoryException {
         final MathTransform trs = CRS.findOperation(crs1, crs2, 
null).getMathTransform();
         final LinearTransform lt = (LinearTransform) trs;
-        final Matrix m = lt.getMatrix();
-        final Matrix3 rotation = new Matrix3();
-        rotation.m00 = m.getElement(0, 0);
-        rotation.m01 = m.getElement(0, 1);
-        rotation.m02 = m.getElement(0, 2);
-        rotation.m10 = m.getElement(1, 0);
-        rotation.m11 = m.getElement(1, 1);
-        rotation.m12 = m.getElement(1, 2);
-        rotation.m20 = m.getElement(2, 0);
-        rotation.m21 = m.getElement(2, 1);
-        rotation.m22 = m.getElement(2, 2);
-        return rotation;
+        return new Matrix3D().set(lt.getMatrix());
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/AbstractMatrix.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/AbstractMatrix.java
index 22c38515e4..55110a2b5c 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/AbstractMatrix.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/AbstractMatrix.java
@@ -24,7 +24,7 @@ import java.util.Arrays;
  * @author Johann Sorel
  * @aurhor Bertrand COTE
  */
-abstract class AbstractMatrix<T extends AbstractMatrix<T>> extends 
SimplifiedTransform implements Matrix<T>{
+abstract class AbstractMatrix<T extends AbstractMatrix<T>> extends 
SimplifiedTransform implements Matrix<T> {
 
     protected final int nbRow;
     protected final int nbCol;
@@ -42,12 +42,12 @@ abstract class AbstractMatrix<T extends AbstractMatrix<T>> 
extends SimplifiedTra
     }
 
     @Override
-    public int getNbRow() {
+    public int getNumRow() {
         return nbRow;
     }
 
     @Override
-    public int getNbCol() {
+    public int getNumCol() {
         return nbCol;
     }
 
@@ -61,19 +61,25 @@ abstract class AbstractMatrix<T extends AbstractMatrix<T>> 
extends SimplifiedTra
         return nbRow;
     }
 
-    
////////////////////////////////////////////////////////////////////////////
-    // get/set matrix values 
///////////////////////////////////////////////////
-    
////////////////////////////////////////////////////////////////////////////
-
     @Override
-    public double[][] toArray2DoubleRowOrder() {
-        final double[][] C = new double[nbRow][nbCol];
-        for (int r = 0; r < nbRow; r++) {
-            for (int c = 0; c < nbCol; c++) {
-                C[r][c] = get(r,c);
+    public double[][] toArray2Double(boolean rowOrder) {
+        if (rowOrder) {
+            final double[][] C = new double[nbRow][nbCol];
+            for (int r = 0; r < nbRow; r++) {
+                for (int c = 0; c < nbCol; c++) {
+                    C[r][c] = get(r,c);
+                }
+            }
+            return C;
+        } else {
+            final double[][] C = new double[nbCol][nbRow];
+            for (int r = 0; r < nbRow; r++) {
+                for (int c = 0; c < nbCol; c++) {
+                    C[c][r] = get(r,c);
+                }
             }
+            return C;
         }
-        return C;
     }
 
     @Override
@@ -207,8 +213,8 @@ abstract class AbstractMatrix<T extends AbstractMatrix<T>> 
extends SimplifiedTra
 
     @Override
     public T set(final Matrix<?> toCopy){
-        final int rm = Math.min(this.nbRow, toCopy.getNbRow());
-        final int cm = Math.min(this.nbCol, toCopy.getNbCol());
+        final int rm = Math.min(this.nbRow, toCopy.getNumRow());
+        final int cm = Math.min(this.nbCol, toCopy.getNumCol());
         for (int r=0;r<rm;r++){
             for (int c=0;c<cm;c++){
                 set(r, c, toCopy.get(r, c));
@@ -218,20 +224,36 @@ abstract class AbstractMatrix<T extends 
AbstractMatrix<T>> extends SimplifiedTra
     }
 
     @Override
-    public T set(final double[] values){
-        for (int i=0,r=0;r<nbRow;r++){
-            for (int c=0;c<nbCol;c++,i++){
-                set(r, c, values[i]);
+    public T set(final double[] values, boolean rowOrder){
+        if (rowOrder) {
+            for (int i=0,r=0;r<nbRow;r++){
+                for (int c=0;c<nbCol;c++,i++){
+                    set(r, c, values[i]);
+                }
+            }
+        } else {
+            for (int i=0,c=0;c<nbCol;c++){
+                for (int r=0;r<nbRow;r++,i++){
+                    set(r, c, values[i]);
+                }
             }
         }
         return (T) this;
     }
 
     @Override
-    public T set(final double[][] values){
-        for (int r=0;r<nbRow;r++){
-            for (int c=0;c<nbCol;c++){
-                set(r, c, values[r][c]);
+    public T set(final double[][] values, boolean rowOrder){
+        if (rowOrder) {
+            for (int r=0;r<nbRow;r++){
+                for (int c=0;c<nbCol;c++){
+                    set(r, c, values[r][c]);
+                }
+            }
+        } else {
+            for (int r=0;r<nbRow;r++){
+                for (int c=0;c<nbCol;c++){
+                    set(r, c, values[c][r]);
+                }
             }
         }
         return (T) this;
@@ -345,6 +367,18 @@ abstract class AbstractMatrix<T extends AbstractMatrix<T>> 
extends SimplifiedTra
         return (T) this;
     }
 
+    @Override
+    public T set(org.opengis.referencing.operation.Matrix toCopy) {
+        final int rm = Math.min(this.nbRow, toCopy.getNumRow());
+        final int cm = Math.min(this.nbCol, toCopy.getNumCol());
+        for (int r=0;r<rm;r++){
+            for (int c=0;c<cm;c++){
+                set(r, c, toCopy.getElement(r, c));
+            }
+        }
+        return (T) this;
+
+    }
     /**
      * Set Matrix value to identity matrix.
      * @return this matrix
@@ -415,41 +449,41 @@ abstract class AbstractMatrix<T extends 
AbstractMatrix<T>> extends SimplifiedTra
      */
     @Override
     public T invert(){
-        final double[][] inverse = 
Matrices.localInvert(toArray2DoubleRowOrder());
+        final double[][] inverse = 
Matrices.localInvert(toArray2Double(ROW_ORDER));
         if (inverse == null){
             throw new IllegalArgumentException("Can not inverse");
         }
-        set(inverse);
+        set(inverse, ROW_ORDER);
         return (T) this;
     }
 
     @Override
     public T add(Matrix<?> other){
-        set(Matrices.localAdd(dArray(this), dArray(other)));
+        set(Matrices.localAdd(dArray(this), dArray(other)), ROW_ORDER);
         return (T) this;
     }
 
     @Override
     public T subtract(Matrix<?> other){
-        set(Matrices.localSubtract(dArray(this), dArray(other)));
+        set(Matrices.localSubtract(dArray(this), dArray(other)), ROW_ORDER);
         return (T) this;
     }
 
     @Override
     public T scale(double[] tuple){
-        set(Matrices.localScale(dArray(this), tuple));
+        set(Matrices.localScale(dArray(this), tuple), ROW_ORDER);
         return (T) this;
     }
 
     @Override
     public T scale(double scale){
-        set(Matrices.localScale(dArray(this), scale));
+        set(Matrices.localScale(dArray(this), scale), ROW_ORDER);
         return (T) this;
     }
 
     @Override
     public T multiply(Matrix<?> other){
-        set(Matrices.localMultiply(dArray(this), dArray(other)));
+        set(Matrices.localMultiply(dArray(this), dArray(other)), ROW_ORDER);
         return (T) this;
     }
 
@@ -470,33 +504,20 @@ abstract class AbstractMatrix<T extends 
AbstractMatrix<T>> extends SimplifiedTra
         }
     }
 
-    /**
-     * Matrix as 1D double array, in column order.
-     *
-     * @return 1D double array, in column order.
-     */
     @Override
-    public double[] toArrayDoubleColOrder(){
+    public double[] toArrayDouble(boolean rowOrder){
         final double[] array = new double[nbRow*nbCol];
-        for (int p=0,c=0;c<nbCol;c++){
-            for (int r=0;r<nbRow;r++,p++){
-                array[p] = get(r, c);
+        if (rowOrder) {
+            for (int p=0,r=0;r<nbRow;r++){
+                for (int c=0;c<nbCol;c++,p++){
+                    array[p] = get(r, c);
+                }
             }
-        }
-        return array;
-    }
-
-    /**
-     * Matrix as 1D double array, in row order.
-     *
-     * @return 1D double array, in row order.
-     */
-    @Override
-    public double[] toArrayDoubleRowOrder(){
-        final double[] array = new double[nbRow*nbCol];
-        for (int p=0,r=0;r<nbRow;r++){
-            for (int c=0;c<nbCol;c++,p++){
-                array[p] = get(r, c);
+        } else {
+            for (int p=0,c=0;c<nbCol;c++){
+                for (int r=0;r<nbRow;r++,p++){
+                    array[p] = get(r, c);
+                }
             }
         }
         return array;
@@ -537,21 +558,26 @@ abstract class AbstractMatrix<T extends 
AbstractMatrix<T>> extends SimplifiedTra
             return false;
         }
         final Matrix other = (Matrix) obj;
-        if (this.nbRow != other.getNbRow()) {
+        if (this.nbRow != other.getNumRow()) {
             return false;
         }
-        if (this.nbCol != other.getNbCol()) {
+        if (this.nbCol != other.getNumCol()) {
             return false;
         }
 
         //check values
-        if (!Arrays.equals(toArrayDoubleRowOrder(), 
other.toArrayDoubleRowOrder())){
+        if (!Arrays.equals(toArrayDouble(ROW_ORDER), 
other.toArrayDouble(ROW_ORDER))){
             return false;
         }
         return true;
     }
 
+    @Override
+    public org.opengis.referencing.operation.Matrix clone() {
+        return copy();
+    }
+
     protected static double[][] dArray(Matrix matrix){
-        return matrix instanceof MatrixND ? ((MatrixND) matrix).values : 
matrix.toArray2DoubleRowOrder();
+        return matrix instanceof MatrixND ? ((MatrixND) matrix).values : 
matrix.toArray2Double(ROW_ORDER);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/AbstractSimilarity.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/AbstractSimilarity.java
new file mode 100644
index 0000000000..70df85eec4
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/AbstractSimilarity.java
@@ -0,0 +1,294 @@
+/*
+ * 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.sis.geometries.math;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.Arrays;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public abstract class AbstractSimilarity<T extends AbstractSimilarity<T>> 
extends SimplifiedTransform implements Similarity<T>{
+
+    protected static final String PROPERTY_MATRIX = "matrix";
+
+    //keep track of the matrix state
+    protected final Object lock = new Object();
+    protected PropertyChangeSupport eventManager;
+    protected final Matrix<?> oldMatrix;
+
+    protected boolean dirty = true;
+    protected boolean inverseDirty = true;
+    protected boolean affineDirty = true;
+
+    //store a view of the global matrix of size dimension+1
+    //which group rotation,scale and translation
+    private final Matrix<?> matrix;
+    private final Matrix<?> inverseMatrix;
+    private final Matrix<?> inverseRotation;
+    private final Affine<?> affine;
+    private final Affine<?> inverseAffine;
+
+    public AbstractSimilarity(int dimension) {
+        super(dimension);
+
+        this.inverseRotation = MatrixND.create(dimension, 
dimension).setToIdentity();
+        this.dirty = false;
+        final int msize = dimension + 1;
+        this.matrix = MatrixND.create(msize, msize).setToIdentity();
+        this.oldMatrix = this.matrix.copy();
+        this.inverseMatrix = this.matrix.copy();
+        this.affine = AffineND.create(dimension);
+        this.inverseAffine = AffineND.create(dimension);
+    }
+
+    /**
+     * Get a general matrix view of size : dimension+1
+     * This matrix combine rotation, scale and translation
+     *
+     * [R*S, R*S, R*S, T]
+     * [R*S, R*S, R*S, T]
+     * [R*S, R*S, R*S, T]
+     * [  0,   0,   0, 1]
+     *
+     * @return Matrix, never null
+     */
+    @Override
+    public Matrix<?> viewMatrix(){
+        if (dirty){
+            dirty = false;
+            //update matrix
+            final Vector<?> translation = getTranslation();
+            final Matrix<?> rotation = getRotation();
+            final Vector<?> scale = getScale();
+            matrix.setToIdentity();
+            matrix.set(rotation);
+            matrix.scale(scale.extend(1).toArrayDouble());
+            for (int i = 0, dimension = getDimension(); i < dimension; i++) {
+                matrix.set(i, dimension, translation.get(i));
+            }
+            oldMatrix.set(matrix);
+            if (!matrix.isFinite()){
+                throw new RuntimeException("Matrix is not finite 
:\nRotation\n"+rotation+"Scale "+scale+"\nTranslate "+translation);
+            }
+        }
+        return matrix;
+    }
+
+    /**
+     * Get a general inverse matrix view of size : dimension+1
+     * DO NOT MODIFY THIS MATRIX.
+     *
+     * @return Matrix, never null
+     */
+    @Override
+    public Matrix<?> viewMatrixInverse(){
+        if (dirty){
+            viewMatrix();
+        }
+        if (inverseDirty){
+            inverseDirty = false;
+            inverseRotation.set(getRotation()).invert();
+            inverseMatrix.set(matrix).invert();
+        }
+        return inverseMatrix;
+    }
+
+    @Override
+    public Affine<?> viewAffine() {
+        if (dirty || affineDirty) {
+            affine.setFromMatrix(viewMatrix());
+            inverseAffine.setFromMatrix(viewMatrixInverse());
+            affineDirty = false;
+        }
+        return affine;
+    }
+
+    @Override
+    public Affine<?> viewAffineInverse() {
+        if (dirty || affineDirty) {
+            affine.setFromMatrix(viewMatrix());
+            inverseAffine.setFromMatrix(viewMatrixInverse());
+            affineDirty = false;
+        }
+        return inverseAffine;
+    }
+
+    @Override
+    public T setFromAffine(Affine<?> trs){
+        setFromMatrix(trs.toMatrix());
+        return (T) this;
+    }
+
+    @Override
+    public T setFromMatrix(Matrix<?> matrix) {
+        Matrices.decomposeMatrix(matrix, getRotation(), getScale(), 
getTranslation());
+        notifyChanged();
+        return (T) this;
+    }
+
+    @Override
+    public T setToTranslation(double[] trs){
+        boolean change = false;
+        final Matrix<?> rotation = getRotation();
+        if (!rotation.isIdentity()){
+            change = true;
+            rotation.setToIdentity();
+        }
+        final Vector<?> scale = getScale();
+        if (!scale.isAll(1.0)){
+            change = true;
+            scale.setAll(1.0);
+        }
+        final Vector<?> translation = getTranslation();
+        if (!Arrays.equals(trs, translation.toArrayDouble())){
+            change = true;
+            translation.set(trs);
+        }
+
+        if (change) notifyChanged();
+
+        return (T) this;
+    }
+
+    @Override
+    public T setToIdentity(){
+        boolean change = false;
+        final Matrix<?> rotation = getRotation();
+        if (!rotation.isIdentity()){
+            change = true;
+            rotation.setToIdentity();
+        }
+        final Vector<?> scale = getScale();
+        if (!scale.isAll(1.0)){
+            change = true;
+            scale.setAll(1.0);
+        }
+        final Vector<?> translation = getTranslation();
+        if (!translation.isAll(0.0)){
+            change = true;
+            translation.setAll(0.0);
+        }
+
+        if (change) notifyChanged();
+        return (T) this;
+    }
+
+    @Override
+    public T invert() {
+        return setFromAffine(toAffine().invert());
+    }
+
+    /**
+     * Test if this transform is identity.
+     * <ul>
+     *  <li>Scale must be all at 1</li>
+     *  <li>Translation must be all at 0</li>
+     *  <li>Rotation must be an identity matrix</li>
+     * </ul>
+     *
+     * @return true if transform is identity.
+     */
+    @Override
+    public boolean isIdentity() {
+        return getScale().isAll(1) && getTranslation().isAll(0) && 
getRotation().isIdentity();
+    }
+
+    @Override
+    public Transform createInverse() {
+        return toAffine().invert().copy();
+    }
+
+    @Override
+    public Tuple<?> transform(Tuple<?> source, Tuple<?> out) {
+        return toAffine().transform(source, out);
+    }
+
+    @Override
+    public void transform(double[] in, int sourceOffset, double[] out, int 
destOffset, int nbTuple) {
+        toAffine().transform(in, sourceOffset, out, destOffset, nbTuple);
+    }
+
+    @Override
+    public void transform(float[] in, int sourceOffset, float[] out, int 
destOffset, int nbTuple) {
+        toAffine().transform(in, sourceOffset, out, destOffset, nbTuple);
+    }
+
+    @Override
+    protected void transform1(double[] source, int sourceOffset, double[] 
dest, int destOffset) {
+        toAffine().transform(source, sourceOffset, dest, destOffset, 1);
+    }
+
+    @Override
+    protected void transform1(float[] source, int sourceOffset, float[] dest, 
int destOffset) {
+        toAffine().transform(source, sourceOffset, dest, destOffset, 1);
+    }
+
+    @Override
+    public Tuple<?> inverseTransform(Tuple<?> source, Tuple<?> dest) {
+        return toAffine().invert().transform(source, dest);
+    }
+
+    @Override
+    public Matrix<?> toMatrix() {
+        return viewMatrix().copy();
+    }
+
+    @Override
+    public Matrix<?> toMatrix(Matrix<?> buffer) {
+        if (buffer == null) return toMatrix();
+        buffer.set(viewMatrix());
+        return buffer;
+    }
+
+    protected PropertyChangeSupport getEventManager() {
+        return getEventManager(true);
+    }
+
+    protected PropertyChangeSupport getEventManager(boolean create) {
+        synchronized (lock){
+            if (eventManager==null && create) eventManager = new 
PropertyChangeSupport(this);
+        }
+        return eventManager;
+    }
+
+    public void addEventListener(PropertyChangeListener listener) {
+        getEventManager().addPropertyChangeListener(listener);
+    }
+
+    public void removeEventListener(PropertyChangeListener listener) {
+        getEventManager().removePropertyChangeListener(listener);
+    }
+
+    /**
+     * Flag to indicate the transform parameters has changed.
+     * This is used to recalculate the general matrix when needed.
+     */
+    @Override
+    public void notifyChanged(){
+        dirty = true;
+        inverseDirty = true;
+        affineDirty = true;
+
+        if (eventManager != null && 
eventManager.hasListeners(PROPERTY_MATRIX)) {
+            //we have listeners, we need to recalculate the transform now
+            eventManager.firePropertyChange(PROPERTY_MATRIX, oldMatrix.copy(), 
viewMatrix().copy());
+        }
+    }
+}
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine.java
index 31816bd4a8..5bd4f1f333 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine.java
@@ -155,7 +155,7 @@ public interface Affine<T extends Affine<T>> extends 
Transform {
      * @param buffer to store matrix values in
      * @return matrix
      */
-    Matrix<?> toMatrix(Matrix buffer);
+    Matrix<?> toMatrix(Matrix<?> buffer);
 
     /**
      * Create a copy of this Affine.
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine1D.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine1D.java
index 0a35eba6c6..a9bf2d4c69 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine1D.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine1D.java
@@ -22,8 +22,8 @@ package org.apache.sis.geometries.math;
  */
 public final class Affine1D extends AbstractAffine<Affine1D> implements 
Transform1D {
 
-    private double m00;
-    private double m01;
+    double m00;
+    double m01;
 
     public Affine1D() {
         super(1);
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine2D.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine2D.java
index 9bc0a90fae..e34573ea90 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine2D.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine2D.java
@@ -23,12 +23,12 @@ package org.apache.sis.geometries.math;
  */
 public final class Affine2D extends AbstractAffine<Affine2D> {
 
-    private double m00;
-    private double m01;
-    private double m02;
-    private double m10;
-    private double m11;
-    private double m12;
+    double m00;
+    double m01;
+    double m02;
+    double m10;
+    double m11;
+    double m12;
 
     public Affine2D() {
         super(2);
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine3D.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine3D.java
index 1dbf531125..0a9137e6bc 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine3D.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine3D.java
@@ -22,18 +22,18 @@ package org.apache.sis.geometries.math;
  */
 public final class Affine3D extends AbstractAffine<Affine3D> {
 
-    private double m00;
-    private double m01;
-    private double m02;
-    private double m03;
-    private double m10;
-    private double m11;
-    private double m12;
-    private double m13;
-    private double m20;
-    private double m21;
-    private double m22;
-    private double m23;
+    double m00;
+    double m01;
+    double m02;
+    double m03;
+    double m10;
+    double m11;
+    double m12;
+    double m13;
+    double m20;
+    double m21;
+    double m22;
+    double m23;
 
     public Affine3D() {
         super(3);
@@ -263,7 +263,7 @@ public final class Affine3D extends 
AbstractAffine<Affine3D> {
     }
 
     @Override
-    public Affine3D setFromMatrix(Matrix m) {
+    public Affine3D setFromMatrix(Matrix<?> m) {
         m00 = m.get(0, 0);
         m01 = m.get(0, 1);
         m02 = m.get(0, 2);
@@ -331,7 +331,7 @@ public final class Affine3D extends 
AbstractAffine<Affine3D> {
     }
 
     @Override
-    public Matrix toMatrix(Matrix buffer) {
+    public Matrix<?> toMatrix(Matrix<?> buffer) {
         if (buffer == null) return toMatrix();
         buffer.set(0, 0, m00);
         buffer.set(0, 1, m01);
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine4D.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine4D.java
index cac4150225..f22c3ee952 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine4D.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Affine4D.java
@@ -22,29 +22,29 @@ package org.apache.sis.geometries.math;
  */
 public final class Affine4D extends AbstractAffine<Affine4D> {
 
-    private double m00;
-    private double m01;
-    private double m02;
-    private double m03;
-    private double m04;
-
-    private double m10;
-    private double m11;
-    private double m12;
-    private double m13;
-    private double m14;
-
-    private double m20;
-    private double m21;
-    private double m22;
-    private double m23;
-    private double m24;
-
-    private double m30;
-    private double m31;
-    private double m32;
-    private double m33;
-    private double m34;
+    double m00;
+    double m01;
+    double m02;
+    double m03;
+    double m04;
+
+    double m10;
+    double m11;
+    double m12;
+    double m13;
+    double m14;
+
+    double m20;
+    double m21;
+    double m22;
+    double m23;
+    double m24;
+
+    double m30;
+    double m31;
+    double m32;
+    double m33;
+    double m34;
 
     public Affine4D() {
         super(4);
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrices.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrices.java
index e1a3c737f4..650788c8d0 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrices.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrices.java
@@ -626,27 +626,6 @@ public final class Matrices {
         return buffer;
     }
 
-    /**
-     * Create rotation matrix to move v1 on v2.
-     *
-     * @param v1
-     * @param v2
-     * @return
-     */
-    public static Matrix4D createRotation(Vector<?> v1, Vector<?> v2){
-        v1 = v1.normalize();
-        v2 = v2.normalize();
-        final double angle = Math.acos(v1.dot(v2));
-        if (angle==0){
-            //vectors are colinear
-            Matrix4D identity = new Matrix4D();
-            return identity;
-        }
-        final Vector axis = v1.copy().cross(v2).normalize();
-        double[][] m = Matrices.createRotation4(angle, axis, null);
-        return new Matrix4D(m);
-    }
-
     /**
      * Build rotation matrix from Euler angle.
      * Sources :
@@ -770,27 +749,6 @@ public final class Matrices {
         return buffer;
     }
 
-    public static Matrix3D createFromUpAndRight(Vector<?> v, Vector<?> u) {
-        v = v.copy();
-        u = u.copy();
-        v.normalize();
-        u.normalize();
-
-        //W = Normalized(Cross(V,U))
-        Vector w = v.cross(u);
-        w.normalize();
-
-        //to ensure it is correctly perpendicular
-        //U = Normalized(Cross(W,V))
-        u = w.cross(v);
-        u.normalize();
-
-        return new Matrix3D(
-                u.get(0), w.get(0), v.get(0),
-                u.get(1), w.get(1), v.get(1),
-                u.get(2), w.get(2), v.get(2));
-    }
-
     /**
      * Create and orbit matrix 4x4 focus on the root point (0,0,0).
      *
@@ -832,7 +790,7 @@ public final class Matrices {
      * The matrix is expected to be orthogonal of size 3x3 or 4x4.
      */
     public static void decomposeMatrix(Matrix<?> trs, Matrix<?> rotation, 
Tuple<?> scale, Tuple<?> translation){
-        final int dimension = trs.getNbCol()-1;
+        final int dimension = trs.getNumCol()-1;
         if (dimension == 2){
             final double scaleX = Math.sqrt(trs.get(0,0)*trs.get(0,0)
                                           + trs.get(1,0)*trs.get(1,0));
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix.java
index 07a673cff3..2b32973823 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix.java
@@ -23,11 +23,26 @@ import 
org.apache.sis.referencing.operation.matrix.MatrixSIS;
  * @author Johann Sorel (Geomatys)
  * @todo Remove this class when all elements are merged in MatrixSIS
  */
-public interface Matrix<T extends Matrix<T>> extends Transform {
+public interface Matrix<T extends Matrix<T>> extends Transform, 
org.opengis.referencing.operation.Matrix {
 
-    int getNbRow();
+    public static final boolean ROW_ORDER = true;
+    public static final boolean COL_ORDER = false;
 
-    int getNbCol();
+    @Override
+    int getNumRow();
+
+    @Override
+    int getNumCol();
+
+    @Override
+    default boolean isIdentity() {
+        return org.opengis.referencing.operation.Matrix.super.isIdentity();
+    }
+
+    @Override
+    default double getElement(int row, int col) {
+        return get(row,col);
+    }
 
     double get(int row, int col);
 
@@ -85,13 +100,18 @@ public interface Matrix<T extends Matrix<T>> extends 
Transform {
      */
     Matrix<?> getRange(int[] r, int j0, int j1);
 
+    @Override
+    default void setElement(int row, int col, double value) {
+        set(row, col, value);
+    }
+
     T set(int row, int col, double value);
 
     T set(final Matrix<?> toCopy);
 
-    T set(final double[] values);
+    T set(final double[] values, boolean rowOrder);
 
-    T set(final double[][] values);
+    T set(final double[][] values, boolean rowOrder);
 
     T setRow(int row, double[] values);
 
@@ -142,25 +162,20 @@ public interface Matrix<T extends Matrix<T>> extends 
Transform {
     T setRange(int i0, int i1, int[] c, Matrix<?> X);
 
     /**
-     * Matrix as 2D double array, in row order.
-     *
-     * @return 2D double array, in row order.
-     */
-    double[][] toArray2DoubleRowOrder();
-
-    /**
-     * Matrix as 1D double array, in column order.
+     * Matrix as 2D double array.
      *
-     * @return 1D double array, in column order.
+     * @param rowOrder true for row order, col order otherwise
+     * @return 2D double array
      */
-    double[] toArrayDoubleColOrder();
+    double[][] toArray2Double(boolean rowOrder);
 
     /**
-     * Matrix as 1D double array, in row order.
+     * Matrix as 1D double array.
      *
-     * @return 1D double array, in row order.
+     * @param rowOrder true for row order, col order otherwise
+     * @return 1D double array
      */
-    double[] toArrayDoubleRowOrder();
+    double[] toArrayDouble(boolean rowOrder);
 
     /**
      * Set Matrix value to identity matrix.
@@ -187,6 +202,11 @@ public interface Matrix<T extends Matrix<T>> extends 
Transform {
 
     T copy();
 
+    @Override
+    default org.opengis.referencing.operation.Matrix clone() {
+        return copy();
+    }
+
     /**
      * Test if all cells in the matrix equals given value.
      * @param scalar scalar
@@ -210,4 +230,6 @@ public interface Matrix<T extends Matrix<T>> extends 
Transform {
      * @return sis matrix equivalent
      */
     MatrixSIS toMatrixSIS();
+
+    T set(org.opengis.referencing.operation.Matrix matrix);
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix2D.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix2D.java
index ce498fe702..e1a5c2cdfc 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix2D.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix2D.java
@@ -30,11 +30,16 @@ public class Matrix2D extends AbstractMatrix<Matrix2D> {
     double m00,m01;
     double m10,m11;
 
+    /**
+     * New identity matrix2D
+     */
     public Matrix2D() {
         super(2, 2);
+        m00 = 1;
+        m11 = 1;
     }
 
-    public Matrix2D(Matrix m) {
+    public Matrix2D(Matrix<?> m) {
         super(2, 2);
         m00 = m.get(0, 0);
         m01 = m.get(0, 1);
@@ -81,7 +86,7 @@ public class Matrix2D extends AbstractMatrix<Matrix2D> {
     }
 
     @Override
-    public Matrix2D set(final Matrix toCopy){
+    public Matrix2D set(final Matrix<?> toCopy){
         if (toCopy instanceof Matrix2D){
             Matrix2D o = (Matrix2D) toCopy;
             m00 = o.m00;m01 = o.m01;
@@ -158,7 +163,7 @@ public class Matrix2D extends AbstractMatrix<Matrix2D> {
     }
 
     @Override
-    public Matrix2D multiply(Matrix other) {
+    public Matrix2D multiply(Matrix<?> other) {
         if (other instanceof Matrix2D){
             //usual case
             Matrix2D o = (Matrix2D) other;
@@ -191,7 +196,7 @@ public class Matrix2D extends AbstractMatrix<Matrix2D> {
     }
 
     @Override
-    public Tuple transform(Tuple vector, Tuple buffer) {
+    public Tuple<?> transform(Tuple<?> vector, Tuple<?> buffer) {
         if (buffer == null) buffer = new Vector2D.Double();
 
         if (vector instanceof Vector2D.Double && buffer instanceof 
Vector2D.Double) {
@@ -217,19 +222,4 @@ public class Matrix2D extends AbstractMatrix<Matrix2D> {
         return new Matrix2(m00, m01, m10, m11);
     }
 
-    /**
-     * Create rotation matrix from angle.
-     *
-     * @param angle in radians
-     * @return Matrix2
-     */
-    public static Matrix2D fromAngle(double angle) {
-        final Matrix2D m = new Matrix2D();
-        m.set(0, 0, Math.cos(angle));
-        m.set(0, 1, -Math.sin(angle));
-        m.set(1, 0, Math.sin(angle));
-        m.set(1, 1, Math.cos(angle));
-        return m;
-    }
-
 }
\ No newline at end of file
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix3D.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix3D.java
index b6c6f7ade6..72d1d02b12 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix3D.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix3D.java
@@ -31,8 +31,14 @@ public class Matrix3D extends AbstractMatrix<Matrix3D> {
     double m10,m11,m12;
     double m20,m21,m22;
 
+    /**
+     * New identity matrix3D
+     */
     public Matrix3D() {
         super(3, 3);
+        m00 = 1;
+        m11 = 1;
+        m22 = 1;
     }
 
     public Matrix3D(Matrix<?> m) {
@@ -219,7 +225,7 @@ public class Matrix3D extends AbstractMatrix<Matrix3D> {
     }
 
     @Override
-    public Matrix3D multiply(Matrix other) {
+    public Matrix3D multiply(Matrix<?> other) {
         if (other instanceof Matrix3D o){
             //usual case
             double b00 = this.m00 * o.m00 + this.m01 * o.m10 + this.m02 * 
o.m20;
@@ -260,7 +266,7 @@ public class Matrix3D extends AbstractMatrix<Matrix3D> {
     }
 
     @Override
-    public Tuple transform(Tuple vector, Tuple buffer) {
+    public Tuple<?> transform(Tuple<?> vector, Tuple<?> buffer) {
         if (buffer == null) buffer = new Vector3D.Double();
 
         if (vector instanceof Vector3D.Double v && buffer instanceof 
Vector3D.Double b) {
@@ -291,20 +297,26 @@ public class Matrix3D extends AbstractMatrix<Matrix3D> {
      * @return euler angle in radians (heading/yaw , elevation/pitch , 
bank/roll)
      */
     public Vector<?> toEuler(){
-        return new VectorND.Double(Matrices.toEuler(toArray2DoubleRowOrder(), 
null));
+        return new VectorND.Double(Matrices.toEuler(toArray2Double(ROW_ORDER), 
null));
     }
 
     /**
+     * Build rotation matrix from euler angle.
      *
      * @param euler in radians (heading/yaw , elevation/pitch , bank/roll)
-     * @return
+     * @return this matrix
      */
-    public Matrix3D fromEuler(Tuple<?> euler){
-        set(Matrices.fromEuler(euler.toArrayDouble(), new double[3][3]));
+    public Matrix3D setFromEuler(Tuple<?> euler){
+        set(Matrices.fromEuler(euler.toArrayDouble(), new double[3][3]), 
ROW_ORDER);
         return this;
     }
 
-    public Matrix3D fromAngle(final double angle, final Tuple<?> rotationAxis){
+    /**
+     * @param angle in radians
+     * @param rotationAxis rotation axis
+     * @return this matrix
+     */
+    public Matrix3D setFromAngle(final double angle, final Tuple<?> 
rotationAxis){
         final double fCos = Math.cos(angle);
         final double fSin = Math.sin(angle);
         final double fOneMinusCos = (1.0) - fCos;
@@ -330,97 +342,92 @@ public class Matrix3D extends AbstractMatrix<Matrix3D> {
         return this;
     }
 
-    @Override
-    public Matrix3D copy() {
-        return new Matrix3D(this);
-    }
-
-    @Override
-    public MatrixSIS toMatrixSIS() {
-        return new Matrix3(m00, m01, m02, m10, m11, m12, m20, m21, m22);
+    /**
+     * Create a rotation matrix as the concatenation of rotation on x,y,z axis.
+     *
+     * @param x in radians
+     * @param y in radians
+     * @param z in radians
+     * @return
+     */
+    public Matrix3D setFromAngles(double x, double y, double z){
+        setFromAngle(x, new Vector3D.Double(1, 0, 0));
+        Matrix3D r2 = new Matrix3D().setFromAngle(y, new Vector3D.Double(0, 1, 
0));
+        Matrix3D r3 = new Matrix3D().setFromAngle(z, new Vector3D.Double(0, 0, 
1));
+        this.multiply(r2);
+        this.multiply(r3);
+        return this;
     }
 
     /**
-     * @return determinant
+     * Create rotation matrix from 3 axis.
+     * Each Tuple must be unit length(normalized)
+     *
+     * @param xAxis values are copied in 1th row
+     * @param yAxis values are copied in 2nd row
+     * @param zAxis values are copied in 3rd row
+     * @return rotation matrix
      */
-    public double getDeterminant(){
-        return m00 * (m11 * m22 - m12 * m21) -
-               m01 * (m10 * m22 - m12 * m20) +
-               m02 * (m10 * m21 - m11 * m20);
+    public Matrix3D setFromAxis(final Tuple<?> xAxis, final Tuple<?> yAxis, 
final Tuple<?> zAxis){
+        setToIdentity();
+        this.setRow(0, xAxis.toArrayDouble());
+        this.setRow(1, yAxis.toArrayDouble());
+        this.setRow(2, zAxis.toArrayDouble());
+        return this;
     }
 
     /**
      * Create rotation matrix to move v1 on v2.
      *
-     * @param v1
-     * @param v2
-     * @return
+     * @param v1 moving vector
+     * @param v2 target vector
+     * @return this matrix
      */
-    public static Matrix3D createRotation(Vector<?> v1, Vector<?> v2){
+    public Matrix3D setFromVectors(Vector<?> v1, Vector<?> v2) {
         v1 = v1.normalize();
         v2 = v2.normalize();
         final double angle = Math.acos(v1.dot(v2));
         if (angle == 0){
             //vectors are colinear
-            Matrix3D identity = new Matrix3D().setToIdentity();
-            return identity;
+            return setToIdentity();
         }
         final Vector<?> axis = v1.cross(v2).normalize();
-        return createRotation3(angle, axis);
+        return setFromAngle(angle, axis);
     }
 
-    /**
-     *
-     * @param angle in radians
-     * @param rotateAxis, rotation axis
-     * @return
-     */
-    public static Matrix3D createRotation3(final double angle, final Tuple 
rotateAxis){
-        return new Matrix3D().fromAngle(angle, rotateAxis);
+    public Matrix3D setFromUpAndRight(Vector<?> v, Vector<?> u) {
+        v = v.copy().normalize();
+        u = u.copy().normalize();
+
+        //W = Normalized(Cross(V,U))
+        Vector<?> w = v.cross(u).normalize();
+
+        //to ensure it is correctly perpendicular
+        //U = Normalized(Cross(W,V))
+        u = w.cross(v).normalize();
+        setCol(0, u.toArrayDouble());
+        setCol(1, w.toArrayDouble());
+        setCol(2, v.toArrayDouble());
+        return this;
     }
 
-    /**
-     * Build rotation matrix from euler angle.
-     *
-     * @param euler angles in radians (heading/yaw , elevation/pitch , 
bank/roll)
-     * @return Matrix3
-     */
-    public static Matrix3D createRotationEuler(final Tuple euler){
-        return new Matrix3D().fromEuler(euler);
+    @Override
+    public Matrix3D copy() {
+        return new Matrix3D(this);
     }
 
-    /**
-     * Create rotation matrix from 3 axis.
-     * Each Tuple must be unit length(normalized)
-     *
-     * @param xAxis values are copied in 1th row
-     * @param yAxis values are copied in 2nd row
-     * @param zAxis values are copied in 3rd row
-     * @return rotation matrix
-     */
-    public static Matrix3D createFromAxis(final Tuple xAxis, final Tuple 
yAxis, final Tuple zAxis){
-        final Matrix3D m = new Matrix3D().setToIdentity();
-        m.setRow(0, xAxis.toArrayDouble());
-        m.setRow(1, yAxis.toArrayDouble());
-        m.setRow(2, zAxis.toArrayDouble());
-        return m;
+    @Override
+    public MatrixSIS toMatrixSIS() {
+        return new Matrix3(m00, m01, m02, m10, m11, m12, m20, m21, m22);
     }
 
     /**
-     * Create a rotation matrix as the concatenation of rotation on x,y,z axis.
-     *
-     * @param x in radians
-     * @param y in radians
-     * @param z in radians
-     * @return
+     * @return determinant
      */
-    public static Matrix3D createFromAngles(double x, double y, double z){
-        Matrix3D r1 = Matrix3D.createRotation3(x, new Vector3D.Double(1, 0, 
0));
-        Matrix3D r2 = Matrix3D.createRotation3(y, new Vector3D.Double(0, 1, 
0));
-        Matrix3D r3 = Matrix3D.createRotation3(z, new Vector3D.Double(0, 0, 
1));
-        r1.multiply(r2);
-        r1.multiply(r3);
-        return r1;
+    public double getDeterminant(){
+        return m00 * (m11 * m22 - m12 * m21) -
+               m01 * (m10 * m22 - m12 * m20) +
+               m02 * (m10 * m21 - m11 * m20);
     }
 
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix4D.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix4D.java
index 74f6902313..9c64cccce8 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix4D.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Matrix4D.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.geometries.math;
 
+import static org.apache.sis.geometries.math.Matrix.ROW_ORDER;
 import org.apache.sis.referencing.operation.matrix.Matrix4;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 
@@ -26,16 +27,23 @@ import 
org.apache.sis.referencing.operation.matrix.MatrixSIS;
  */
 public class Matrix4D extends AbstractMatrix<Matrix4D> {
 
-    private double m00,m01,m02,m03;
-    private double m10,m11,m12,m13;
-    private double m20,m21,m22,m23;
-    private double m30,m31,m32,m33;
+    double m00,m01,m02,m03;
+    double m10,m11,m12,m13;
+    double m20,m21,m22,m23;
+    double m30,m31,m32,m33;
 
+    /**
+     * New identity matrix4D
+     */
     public Matrix4D() {
         super(4, 4);
+        m00 = 1;
+        m11 = 1;
+        m22 = 1;
+        m33 = 1;
     }
 
-    public Matrix4D(Matrix m) {
+    public Matrix4D(Matrix<?> m) {
         super(4, 4);
         m00 = m.get(0, 0);m01 = m.get(0, 1);m02 = m.get(0, 2);m03 = m.get(0, 
3);
         m10 = m.get(1, 0);m11 = m.get(1, 1);m12 = m.get(1, 2);m13 = m.get(1, 
3);
@@ -228,9 +236,8 @@ public class Matrix4D extends AbstractMatrix<Matrix4D> {
      * @param euler angles in radians (heading/yaw , elevation/pitch , 
bank/roll)
      * @return Matrix4
      */
-    public Matrix4D fromEuler(Tuple<?> euler){
-        set(Matrices.fromEuler(euler.toArrayDouble(), null));
-        return this;
+    public Matrix4D setFromEuler(Tuple<?> euler){
+        return set(Matrices.fromEuler(euler.toArrayDouble(), null), ROW_ORDER);
     }
 
     @Override
@@ -279,7 +286,7 @@ public class Matrix4D extends AbstractMatrix<Matrix4D> {
 
     @Override
     public Matrix4D transpose(){
-        return set(new Matrix4D(Matrices.transpose(toArray2DoubleRowOrder())));
+        return set(new 
Matrix4D(Matrices.transpose(toArray2Double(ROW_ORDER))));
     }
 
     /**
@@ -321,26 +328,16 @@ public class Matrix4D extends AbstractMatrix<Matrix4D> {
      * @param rotation Matrix[3x3]
      * @param scale Tuple[3]
      * @param translation Tuple[3]
-     * @return Matrix4
+     * @return this matrix
      */
-    public static Matrix4D createFromComponents(final MatrixND rotation, 
Tuple<?> scale, Tuple<?> translation){
-        final Matrix4D matrix = new Matrix4D()
-            .set(rotation)
-            .scale(new VectorND.Double(scale).extend(1).toArrayDouble());
+    public Matrix4D setFromComponents(final MatrixND rotation, Tuple<?> scale, 
Tuple<?> translation){
+        setToIdentity();
+        set(rotation);
+        scale(new VectorND.Double(scale).extend(1).toArrayDouble());
         for (int i=0;i<translation.getDimension();i++){
-            matrix.set(i, 3, translation.get(i));
+            set(i, 3, translation.get(i));
         }
-        return matrix;
-    }
-
-    /**
-     * Build rotation matrix from euler angle.
-     *
-     * @param euler angles in radians (heading/yaw , elevation/pitch , 
bank/roll)
-     * @return Matrix4
-     */
-    public static Matrix4D createRotationEuler(final Tuple<?> euler){
-        return new Matrix4D(Matrices.fromEuler(euler.toArrayDouble(), new 
double[4][4]));
+        return this;
     }
 
     /**
@@ -350,17 +347,16 @@ public class Matrix4D extends AbstractMatrix<Matrix4D> {
      * @param xAxis values are copied in 1th row
      * @param yAxis values are copied in 2nd row
      * @param zAxis values are copied in 3rd row
-     * @return rotation matrix
+     * @return this matrix
      */
-    public static Matrix4D createFromAxis(final Tuple<?> xAxis, final Tuple<?> 
yAxis, final Tuple<?> zAxis){
-        final Matrix4D m = new Matrix4D().setToIdentity();
-        m.setRow(0, xAxis.toArrayDouble());
-        m.setRow(1, yAxis.toArrayDouble());
-        m.setRow(2, zAxis.toArrayDouble());
-        return m;
+    public Matrix4D setFromAxis(final Tuple<?> xAxis, final Tuple<?> yAxis, 
final Tuple<?> zAxis){
+        setToIdentity();
+        setRow(0, xAxis.toArrayDouble());
+        setRow(1, yAxis.toArrayDouble());
+        setRow(2, zAxis.toArrayDouble());
+        return this;
     }
 
-
     /**
      * Create and orbit matrix 4x4 focus on the root point (0,0,0).
      *
@@ -368,18 +364,39 @@ public class Matrix4D extends AbstractMatrix<Matrix4D> {
      * @param yAngle vertical angle
      * @param rollAngle roll angle
      * @param distance distance from base
-     * @return orbit matrix 4x4
+     * @return this matrix, orbit matrix 4x4
      */
-    public static Matrix4D focusedOrbit(final double xAngle,
+    public Matrix4D setFocusedOrbit(final double xAngle,
             final double yAngle, double rollAngle, double distance){
-        return new Matrix4D(Matrices.focusedOrbit(xAngle, yAngle, rollAngle, 
distance));
+        return set(Matrices.focusedOrbit(xAngle, yAngle, rollAngle, distance), 
ROW_ORDER);
+    }
+
+    /**
+     * Create rotation matrix to move v1 on v2.
+     *
+     * @param v1
+     * @param v2
+     * @return
+     */
+    public Matrix4D setFromVectors(Vector<?> v1, Vector<?> v2){
+        v1 = v1.normalize();
+        v2 = v2.normalize();
+        final double angle = Math.acos(v1.dot(v2));
+        if (angle==0){
+            //vectors are colinear
+            Matrix4D identity = new Matrix4D();
+            return identity;
+        }
+        final Vector<?> axis = v1.copy().cross(v2).normalize();
+        double[][] m = Matrices.createRotation4(angle, axis, null);
+        return set(m, ROW_ORDER);
     }
 
-    
///////////////////////////////////////////////////////////////////////////////
+    // 
/////////////////////////////////////////////////////////////////////////////
     // compute cofactor of 3x3 minor matrix without sign
     // input params are 9 elements of the minor matrix
     // NOTE: The caller must know its sign.
-    
///////////////////////////////////////////////////////////////////////////////
+    // 
/////////////////////////////////////////////////////////////////////////////
     private static double getCofactor(
             double m0, double m1, double m2,
             double m3, double m4, double m5,
@@ -414,7 +431,7 @@ public class Matrix4D extends AbstractMatrix<Matrix4D> {
     }
 
     @Override
-    public Tuple transform(Tuple vector, Tuple buffer) {
+    public Tuple<?> transform(Tuple<?> vector, Tuple<?> buffer) {
         if (buffer == null) buffer = new Vector4D.Double();
 
         if (vector instanceof Vector4D.Double && buffer instanceof 
Vector4D.Double) {
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/MatrixND.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/MatrixND.java
index 867ed1220c..b33ad90030 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/MatrixND.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/MatrixND.java
@@ -36,7 +36,7 @@ public class MatrixND extends AbstractMatrix<MatrixND>{
      * @param nbcol
      * @return
      */
-    public static Matrix create(int nbrow, int nbcol){
+    public static Matrix<?> create(int nbrow, int nbcol){
         if (nbrow==nbcol) {
             switch(nbrow){
                 case 2 : return new Matrix2D();
@@ -49,8 +49,8 @@ public class MatrixND extends AbstractMatrix<MatrixND>{
 
     /**
      * Create a new matrix of given size.
-     * It is recommended to use statice create method to benefit from 
specialized
-     * matrices implementations such as Matric2,Matric4,Matrix4.
+     * It is recommended to use static create method to benefit from 
specialized
+     * matrices implementations such as Matrix2D,Matrix3D,Matrix4D.
      *
      * @param nbrow
      * @param nbcol
@@ -60,44 +60,14 @@ public class MatrixND extends AbstractMatrix<MatrixND>{
         values = new double[nbrow][nbcol];
     }
 
-    protected MatrixND(final double m00, final double m01,
-                  final double m10, final double m11){
-        this(new double[][]{
-            {m00,m01},
-            {m10,m11}
-        });
-    }
-
-    protected MatrixND(final double m00, final double m01, final double m02,
-                  final double m10, final double m11, final double m12,
-                  final double m20, final double m21, final double m22){
-        this(new double[][]{
-            {m00,m01,m02},
-            {m10,m11,m12},
-            {m20,m21,m22}
-        });
-    }
-
-    protected MatrixND(final double m00, final double m01, final double m02, 
final double m03,
-                  final double m10, final double m11, final double m12, final 
double m13,
-                  final double m20, final double m21, final double m22, final 
double m23,
-                  final double m30, final double m31, final double m32, final 
double m33){
-        this(new double[][]{
-            {m00,m01,m02,m03},
-            {m10,m11,m12,m13},
-            {m20,m21,m22,m23},
-            {m30,m31,m32,m33}
-        });
-    }
-
     public MatrixND(final double[][] values) {
         super(values.length,values[0].length);
         this.values = values;
     }
 
-    public MatrixND(Matrix m) {
-        super(m.getNbRow(),m.getNbCol());
-        values = m.toArray2DoubleRowOrder();
+    public MatrixND(Matrix<?> m) {
+        super(m.getNumRow(),m.getNumCol());
+        values = m.toArray2Double(ROW_ORDER);
     }
 
     public double[][] getValues() {
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Quaternion.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Quaternion.java
index 1703e7c266..275260b327 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Quaternion.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Quaternion.java
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.geometries.math;
 
+import static org.apache.sis.geometries.math.Matrix.ROW_ORDER;
+
 /**
  * Quaternion object.
  *
@@ -350,7 +352,7 @@ public class Quaternion extends VectorND.Double {
      * @return this Quaternion
      */
     public Quaternion setFromMatrix(final Matrix matrix){
-        final double[][] m = matrix.toArray2DoubleRowOrder();
+        final double[][] m = matrix.toArray2Double(ROW_ORDER);
         final double trace = m[0][0] + m[1][1] + m[2][2] + 1;
 
         final double s,x,y,z,w;
@@ -389,7 +391,7 @@ public class Quaternion extends VectorND.Double {
      * @param angle rotation angle, in radians
      * @return this quaternion
      */
-    public Quaternion setFromAngle(Tuple axis, double angle) {
+    public Quaternion setFromAngle(Tuple<?> axis, double angle) {
         Quaternions.fromAngle(axis, angle, values);
         return this;
     }
@@ -400,9 +402,8 @@ public class Quaternion extends VectorND.Double {
      * @param euler angles in radians (heading/yaw , elevation/pitch , 
bank/roll)
      * @return this quaternion
      */
-    public Quaternion setFromEuler(Vector euler) {
-        final Matrix3D matrix = Matrix3D.createRotationEuler(euler);
-        return setFromMatrix(matrix);
+    public Quaternion setFromEuler(Vector<?> euler) {
+        return setFromMatrix(new Matrix3D().setFromEuler(euler));
     }
 
     /**
@@ -416,7 +417,7 @@ public class Quaternion extends VectorND.Double {
         final double dot = base.dot(target);
         if (dot < -0.999999) {
             //try to find a better value on other axis
-            Vector tmpvec3 = UNIT_X.cross(base);
+            Vector<?> tmpvec3 = UNIT_X.cross(base);
             if (tmpvec3.length() < 0.000001) {
                 tmpvec3 = UNIT_Y.cross(base);
             }
@@ -429,7 +430,7 @@ public class Quaternion extends VectorND.Double {
             values[2] = 0;
             values[3] = 1;
         } else {
-            final Vector cross = base.cross(target);
+            final Vector<?> cross = base.cross(target);
             values[0] = cross.get(0);
             values[1] = cross.get(1);
             values[2] = cross.get(2);
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Similarity.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Similarity.java
index 2a22b7369d..60b371fb7b 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Similarity.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Similarity.java
@@ -16,6 +16,9 @@
  */
 package org.apache.sis.geometries.math;
 
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.opengis.referencing.operation.MathTransform;
+
 /**
  * A similarity is the equivalent of a affine transform but preserving angles 
by avoiding
  * shearing value not rotations.
@@ -32,7 +35,7 @@ package org.apache.sis.geometries.math;
  *
  * @author Johann Sorel (Geomatys)
  */
-public interface Similarity<T extends Similarity<T>> extends Affine<T> {
+public interface Similarity<T extends Similarity<T>> extends Transform {
 
     /**
      * Dimension of the transform.
@@ -46,7 +49,7 @@ public interface Similarity<T extends Similarity<T>> extends 
Affine<T> {
      *
      * @return Matrix
      */
-    Matrix getRotation();
+    Matrix<?> getRotation();
 
     /**
      * Get transform scale.
@@ -59,7 +62,8 @@ public interface Similarity<T extends Similarity<T>> extends 
Affine<T> {
     /**
      * Get transform translation.
      * Call notifyChanged after if you modified the values.
-     *
+     *ç&é ,
+     * +
      * @return Vector
      */
     Vector<?> getTranslation();
@@ -75,26 +79,25 @@ public interface Similarity<T extends Similarity<T>> 
extends Affine<T> {
      *
      * @return Matrix, never null
      */
-    Matrix viewMatrix();
+    Matrix<?> viewMatrix();
 
     /**
      * Get a general inverse matrix view of size : dimension+1
      *
      * @return Matrix, never null
      */
-    Matrix viewMatrixInverse();
+    Matrix<?> viewMatrixInverse();
 
-    /**
-     * {@inheritDoc }
-     */
-    @Override
-    void transform(double[] in, int sourceOffset, double[] out, int 
destOffset, int nbTuple);
+    Affine<?> viewAffine();
 
     /**
-     * {@inheritDoc }
+     * Inverse view of this transform.
+     * The returned affine is no modifiable.
+     * The returned affine reflects any change made to this transform
+     *
+     * @return inverse transform view
      */
-    @Override
-    void transform(float[] in, int sourceOffset, float[] out, int destOffset, 
int nbTuple);
+    Affine<?> viewAffineInverse();
 
     /**
      * Inverse transform a single tuple.
@@ -106,14 +109,11 @@ public interface Similarity<T extends Similarity<T>> 
extends Affine<T> {
     Tuple<?> inverseTransform(Tuple<?> source, Tuple<?> dest);
 
     /**
-     * Inverse view of this transform.
-     * The returned affine is no modifiable.
-     * The returned affine reflects any change made to this transform
+     * Multiply this similarity by given similarity and store the result in 
this similarity.
      *
-     * @return inverse transform view
+     * @param other multiplying similarity
+     * @return this similarity
      */
-    Affine<?> inverse();
-
     T multiply(Similarity<?> other);
 
     /**
@@ -128,24 +128,22 @@ public interface Similarity<T extends Similarity<T>> 
extends Affine<T> {
      * Matrix must be orthogonal of size dimension+1.
      *
      * @param trs
+     * @throws IllegalArgumentException if matrix is not affine
      */
-    @Override
-    T setFromMatrix(Matrix trs);
+    T setFromMatrix(Matrix<?> trs) throws IllegalArgumentException;
 
     /**
-     * Set transform from given matrix.
+     * Set transform from given affine.
      * Affine must be of same size.
      *
      * @param trs
      */
-    @Override
-    T set(Affine<?> trs);
+    T setFromAffine(Affine<?> trs);
 
     /**
      * Set to identity.
      * This method will send a change event if values have changed.
      */
-    @Override
     T setToIdentity();
 
     /**
@@ -157,10 +155,54 @@ public interface Similarity<T extends Similarity<T>> 
extends Affine<T> {
      */
     T setToTranslation(double[] trs);
 
+    /**
+     * Inverse this affine transform.
+     *
+     * @return this affine instance
+     */
+    T invert();
+
+    /**
+     * Create a square matrix of size dimensions+1
+     * The last matrix line will be [0,...,1]
+     *
+     * @return matrix
+     */
+    Matrix<?> toMatrix();
+
+    /**
+     * Create a square matrix of size dimensions+1
+     * The last matrix line will be [0,...,1]
+     *
+     * @param buffer to store matrix values in
+     * @return matrix
+     */
+    Matrix<?> toMatrix(Matrix<?> buffer);
+
+    /**
+     * Create and affine transform.
+     *
+     * @return 
+     */
+    Affine<?> toAffine();
+
+    /**
+     * Create a copy of this Affine.
+     *
+     * @return copy
+     */
+    T copy();
+
     /**
      * Flag to indicate the transform parameters has changed.
      * This is used to recalculate the general matrix when needed.
      */
     void notifyChanged();
 
+    /**
+     * Combine the different elements to obtain a linear transform of 
dimension 3.
+     */
+    default MathTransform toMathTransform() {
+        return MathTransforms.linear(toMatrix().toMatrixSIS());
+    }
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Similarity3D.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Similarity3D.java
new file mode 100644
index 0000000000..bf247b577b
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Similarity3D.java
@@ -0,0 +1,176 @@
+/*
+ * 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.sis.geometries.math;
+
+import java.util.Objects;
+
+/**
+ * 3D similarity.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class Similarity3D extends AbstractSimilarity<Similarity3D> {
+
+    public final Vector3D.Double scale = new Vector3D.Double(1, 1, 1);
+    public final Vector3D.Double translation = new Vector3D.Double(0, 0, 0);
+    public final Matrix3D rotation = new Matrix3D(1,0,0, 0,1,0, 0,0,1);
+
+    public Similarity3D(){
+        super(3);
+    }
+
+    @Override
+    public int getInputDimensions() {
+        return 3;
+    }
+
+    @Override
+    public int getOutputDimensions() {
+        return 3;
+    }
+
+    @Override
+    public int getDimension() {
+        return 3;
+    }
+
+    @Override
+    public Matrix3D getRotation() {
+        return rotation;
+    }
+
+    @Override
+    public Vector3D.Double getScale() {
+        return scale;
+    }
+
+    @Override
+    public Vector3D.Double getTranslation() {
+        return translation;
+    }
+
+    @Override
+    public Similarity3D multiply(Similarity<?> other) {
+        final Vector<?> resTrans = other.getTranslation().copy()
+                .transform(rotation)
+                .multiply(scale)
+                .add(translation);
+
+        rotation.multiply(other.getRotation());
+        scale.multiply(other.getScale());
+        translation.set(resTrans);
+        notifyChanged();
+        return this;
+    }
+
+    @Override
+    public Similarity3D set(Similarity<?> trs) {
+        if (rotation.equals(trs.getRotation()) && scale.equals(trs.getScale()) 
&& translation.equals(trs.getTranslation())){
+            //nothing changes
+            return this;
+        }
+        rotation.set(trs.getRotation());
+        scale.set(trs.getScale());
+        translation.set(trs.getTranslation());
+        notifyChanged();
+        return this;
+    }
+
+    /**
+     * Combine the different elements to obtain a 4x4 matrix.
+     * [R*S, R*S, R*S, T]
+     * [R*S, R*S, R*S, T]
+     * [R*S, R*S, R*S, T]
+     * [  0,   0,   0, 1]
+     */
+    @Override
+    public Matrix4D toMatrix() {
+
+        final Matrix4D matrix = new Matrix4D();
+        //set rotation
+        matrix.m00 = rotation.m00; matrix.m10 = rotation.m10; matrix.m20 = 
rotation.m20;
+        matrix.m01 = rotation.m01; matrix.m11 = rotation.m11; matrix.m21 = 
rotation.m21;
+        matrix.m02 = rotation.m02; matrix.m12 = rotation.m12; matrix.m22 = 
rotation.m22;
+        //scale matrix
+        matrix.m00 *= scale.x; matrix.m01 *= scale.y; matrix.m02 *= scale.z;
+        matrix.m10 *= scale.x; matrix.m11 *= scale.y; matrix.m12 *= scale.z;
+        matrix.m20 *= scale.x; matrix.m21 *= scale.y; matrix.m22 *= scale.z;
+        //add translation
+        matrix.m03 = translation.x;
+        matrix.m13 = translation.y;
+        matrix.m23 = translation.z;
+
+        return matrix;
+    }
+
+    @Override
+    public Affine3D toAffine() {
+        final Affine3D affine = new Affine3D();
+        //set rotation
+        affine.m00 = rotation.m00; affine.m10 = rotation.m10; affine.m20 = 
rotation.m20;
+        affine.m01 = rotation.m01; affine.m11 = rotation.m11; affine.m21 = 
rotation.m21;
+        affine.m02 = rotation.m02; affine.m12 = rotation.m12; affine.m22 = 
rotation.m22;
+        //scale matrix
+        affine.m00 *= scale.x; affine.m01 *= scale.y; affine.m02 *= scale.z;
+        affine.m10 *= scale.x; affine.m11 *= scale.y; affine.m12 *= scale.z;
+        affine.m20 *= scale.x; affine.m21 *= scale.y; affine.m22 *= scale.z;
+        //add translation
+        affine.m03 = translation.x;
+        affine.m13 = translation.y;
+        affine.m23 = translation.z;
+
+        return affine;
+    }
+
+    @Override
+    public Matrix<?> toMatrix(Matrix<?> buffer) {
+        if (buffer == null) return toMatrix();
+        buffer.set(toMatrix());
+        return buffer;
+    }
+
+    @Override
+    public Similarity3D copy() {
+        return new Similarity3D().set(this);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(this.scale,
+                this.translation,
+                this.rotation);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Similarity3D other = (Similarity3D) obj;
+        return Objects.equals(this.scale, other.scale)
+            && Objects.equals(this.translation, other.translation)
+            && Objects.equals(this.rotation, other.rotation);
+    }
+
+}
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/SimilarityND.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/SimilarityND.java
index f63127c667..7fa8ea4da8 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/SimilarityND.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/SimilarityND.java
@@ -16,10 +16,6 @@
  */
 package org.apache.sis.geometries.math;
 
-import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeSupport;
-import java.util.Arrays;
-
 /**
  * A similarity is the equivalent of a affine transform but preserving angles 
by avoiding
  * shearing value not rotations.
@@ -39,36 +35,18 @@ import java.util.Arrays;
  *
  * @author Johann Sorel
  */
-public class SimilarityND extends AbstractAffine<SimilarityND> implements 
Similarity<SimilarityND> {
-
-    private static final String PROPERTY_MATRIX = "matrix";
-
-    //keep track of the matrix state
-    private final Object lock = new Object();
-    private PropertyChangeSupport eventManager;
-    private final Matrix<?> oldMatrix;
+public class SimilarityND extends AbstractSimilarity<SimilarityND> {
 
     private final int dimension;
-    private final Matrix<?> rotation;
-    private final Vector<?> scale;
-    private final Vector<?> translation;
-
-    //store a view of the global matrix of size dimension+1
-    //which group rotation,scale and translation
-    private final Inverse inverseAff = new Inverse();
-    private boolean dirty = true;
-    private boolean inverseDirty = true;
-    private boolean affineDirty = true;
-    private final Matrix<?> matrix;
-    private final Matrix<?> inverseMatrix;
-    private final Matrix<?> inverseRotation;
-    private final Affine<?> affine;
+    public final Matrix<?> rotation;
+    public final Vector<?> scale;
+    public final Vector<?> translation;
 
     public static Similarity<?> create(int dimension) {
         return new SimilarityND(dimension);
     }
 
-    public static Similarity<?> create(Matrix rotation, Vector<?> scale, 
Vector<?> translation) {
+    public static Similarity<?> create(Matrix<?> rotation, Vector<?> scale, 
Vector<?> translation) {
         return new SimilarityND(rotation, scale, translation);
     }
 
@@ -80,45 +58,28 @@ public class SimilarityND extends 
AbstractAffine<SimilarityND> implements Simila
         super(dimension);
         this.dimension = dimension;
         this.rotation = MatrixND.create(dimension, dimension).setToIdentity();
-        this.inverseRotation = rotation.copy();
         this.scale = Vectors.create(dimension, DataType.DOUBLE);
         //set scale to 1 by default
         this.scale.setAll(1d);
         this.translation = Vectors.create(dimension, DataType.DOUBLE);
-        this.dirty = false;
-        final int msize = dimension+1;
-        this.matrix = MatrixND.create(msize, msize).setToIdentity();
-        this.oldMatrix = this.matrix.copy();
-        this.inverseMatrix = MatrixND.create(msize, msize);
-        this.affine = AffineND.create(dimension);
     }
 
-    protected SimilarityND(Matrix rotation, Vector<?> scale, Vector<?> 
translation) {
+    protected SimilarityND(Matrix<?> rotation, Vector<?> scale, Vector<?> 
translation) {
         super(scale.getDimension());
         this.dimension = scale.getDimension();
         this.rotation = rotation;
-        this.inverseRotation = this.rotation.copy();
         this.scale = scale;
         this.translation = translation;
-        final int msize = dimension+1;
-        this.matrix = MatrixND.create(msize, msize).setToIdentity();
-        this.oldMatrix = this.matrix.copy();
-        this.inverseMatrix = MatrixND.create(msize, msize);
-        this.affine = AffineND.create(dimension);
+        dirty = true;
     }
 
     protected SimilarityND(Quaternion rotation, Vector<?> scale, Vector<?> 
translation) {
         super(scale.getDimension());
         this.dimension = scale.getDimension();
         this.rotation = rotation.toMatrix3();
-        this.inverseRotation = this.rotation.copy();
         this.scale = scale;
         this.translation = translation;
-        final int msize = dimension+1;
-        this.matrix = MatrixND.create(msize, msize).setToIdentity();
-        this.oldMatrix = this.matrix.copy();
-        this.inverseMatrix = MatrixND.create(msize, msize);
-        this.affine = AffineND.create(dimension);
+        dirty = true;
     }
 
     /**
@@ -126,7 +87,7 @@ public class SimilarityND extends 
AbstractAffine<SimilarityND> implements Simila
      * @param trs
      */
     @Override
-    public SimilarityND set(Similarity trs){
+    public SimilarityND set(Similarity<?> trs){
         if (rotation.equals(trs.getRotation()) && scale.equals(trs.getScale()) 
&& translation.equals(trs.getTranslation())){
             //nothing changes
             return this;
@@ -138,361 +99,34 @@ public class SimilarityND extends 
AbstractAffine<SimilarityND> implements Simila
         return this;
     }
 
-    /**
-     * Set transform from given matrix.
-     * Matrix must be orthogonal of size dimension+1.
-     * @param trs
-     */
-    @Override
-    public SimilarityND setFromMatrix(Matrix trs){
-        Matrices.decomposeMatrix(trs, rotation, scale, translation);
-        notifyChanged();
-        return this;
-    }
-
-    /**
-     * Set transform from given matrix.
-     * Matrix must be orthogonal of size dimension+1.
-     * @param trs
-     */
-    @Override
-    public SimilarityND set(Affine trs){
-        setFromMatrix(trs.toMatrix());
-        return this;
-    }
-
-    @Override
-    public SimilarityND set(int row, int col, double value) throws 
IllegalArgumentException {
-        throw new UnsupportedOperationException("Not supported.");
-    }
-
-    /**
-     * Set to identity.
-     * This method will send a change event if values have changed.
-     */
-    @Override
-    public SimilarityND setToIdentity(){
-        boolean change = false;
-        if (!rotation.isIdentity()){
-            change = true;
-            rotation.setToIdentity();
-        }
-        if (!scale.isAll(1.0)){
-            change = true;
-            scale.setAll(1.0);
-        }
-        if (!translation.isAll(0.0)){
-            change = true;
-            translation.setAll(0.0);
-        }
-
-        if (change) notifyChanged();
-        return this;
-    }
-
-    /**
-     * Set this transform to given translation.
-     * This will reset rotation and scale values.
-     *
-     * This method will send a change event if values have changed.
-     */
-    @Override
-    public SimilarityND setToTranslation(double[] trs){
-        boolean change = false;
-        if (!rotation.isIdentity()){
-            change = true;
-            rotation.setToIdentity();
-        }
-        if (!scale.isAll(1.0)){
-            change = true;
-            scale.setAll(1.0);
-        }
-        if (!Arrays.equals(trs, translation.toArrayDouble())){
-            change = true;
-            translation.set(trs);
-        }
-
-        if (change) notifyChanged();
-
-        return this;
-    }
-
-    @Override
-    public double get(int row, int col) {
-        return viewMatrix().get(row, col);
-    }
-
-    /**
-     * Dimension of the transform.
-     * @return int
-     */
     @Override
     public int getDimension() {
         return dimension;
     }
 
-    /**
-     * Get transform rotation.
-     * Call notifyChanged after if you modified the values.
-     *
-     * @return Matrix
-     */
-    @Override
-    public Matrix getRotation() {
-        return rotation;
-    }
-
-    /**
-     * Get transform scale.
-     * Call notifyChanged after if you modified the values.
-     *
-     * @return Vector<?>
-     */
-    @Override
-    public Vector<?> getScale() {
-        return scale;
-    }
-
-    /**
-     * Get transform translation.
-     * Call notifyChanged after if you modified the values.
-     *
-     * @return Vector<?>
-     */
-    @Override
-    public Vector<?> getTranslation() {
-        return translation;
-    }
-
-    /**
-     * Flag to indicate the transform parameters has changed.
-     * This is used to recalculate the general matrix when needed.
-     */
-    @Override
-    public void notifyChanged(){
-        dirty=true;
-        inverseDirty=true;
-        affineDirty=true;
-
-        if (eventManager!=null && eventManager.hasListeners(PROPERTY_MATRIX)){
-            //we have listeners, we need to recalculate the transform now
-            eventManager.firePropertyChange(PROPERTY_MATRIX, oldMatrix.copy(), 
viewMatrix().copy());
-        }
-    }
-
-    /**
-     * Get a general matrix view of size : dimension+1
-     * This matrix combine rotation, scale and translation
-     *
-     * [R*S, R*S, R*S, T]
-     * [R*S, R*S, R*S, T]
-     * [R*S, R*S, R*S, T]
-     * [  0,   0,   0, 1]
-     *
-     * @return Matrix, never null
-     */
-    @Override
-    public Matrix<?> viewMatrix(){
-        if (dirty){
-            dirty = false;
-            //update matrix
-            matrix.setToIdentity();
-            matrix.set(rotation);
-            matrix.scale(scale.extend(1).toArrayDouble());
-            for (int i=0;i<dimension;i++){
-                matrix.set(i, dimension, translation.get(i));
-            }
-            oldMatrix.set(matrix);
-            if (!matrix.isFinite()){
-                throw new RuntimeException("Matrix is not finite 
:\nRotation\n"+rotation+"Scale "+scale+"\nTranslate "+translation);
-            }
-        }
-        return matrix;
-    }
-
-    /**
-     * Get a general inverse matrix view of size : dimension+1
-     * DO NOT MODIFY THIS MATRIX.
-     *
-     * @return Matrix, never null
-     */
-    @Override
-    public Matrix<?> viewMatrixInverse(){
-        if (dirty){
-            viewMatrix();
-        }
-        if (inverseDirty){
-            inverseDirty = false;
-            inverseRotation.set(rotation).invert();
-            inverseMatrix.set(matrix).invert();
-        }
-        return inverseMatrix;
-    }
-
-    private Affine<?> asAffine() {
-        if (dirty || affineDirty) {
-            affine.setFromMatrix(viewMatrix());
-            affineDirty = false;
-        }
-        return affine;
-    }
-
-    /**
-     * {@inheritDoc }
-     */
     @Override
     public int getInputDimensions() {
         return dimension;
     }
 
-    /**
-     * {@inheritDoc }
-     */
     @Override
     public int getOutputDimensions() {
         return dimension;
     }
 
     @Override
-    public Tuple<?> transform(Tuple<?> source, Tuple<?> out) {
-        return asAffine().transform(source, out);
-
-        //under code uses less memory but is much slower
-//        if (out == null) out = DefaultVector<?>.create(dimension);
-//
-//        if (out instanceof Vector<?>) {
-//            //scale
-//            out.set(source);
-//            ((Vector<?>) out).localMultiply(scale);
-//            //rotate
-//            rotation.transform(out, out);
-//            //translate
-//            ((Vector<?>) out).localAdd(translation);
-//        } else {
-//            final Vector<?> array = DefaultVector<?>.create(out);
-//            //scale
-//            array.set(source);
-//            array.localMultiply(scale);
-//            //rotate
-//            rotation.transform(array, array);
-//            //translate
-//            array.localAdd(translation);
-//            out.set(array);
-//        }
-//
-//        return out;
-    }
-
-    /**
-     * {@inheritDoc }
-     */
-    @Override
-    public void transform(double[] in, int sourceOffset, double[] out, int 
destOffset, int nbTuple) {
-        asAffine().transform(in, sourceOffset, out, destOffset, nbTuple);
-
-        //we could use this, but it is slower.
-        //asMatrix().transform(in, sourceOffset, out, destOffset, nbTuple);
-
-        //this is also slower
-//        //scale
-//        Vector<?>s.multiplyRegular(in, out, sourceOffset, destOffset, 
scale.toDouble(), nbTuple);
-//        //rotate
-//        rotation.transform(out, destOffset, out, destOffset, nbTuple);
-//        //translate
-//        Vector<?>s.addRegular(out, out, destOffset, destOffset, 
translation.toDouble(), nbTuple);
-//
-//        return out;
-    }
-
-    /**
-     * {@inheritDoc }
-     */
-    @Override
-    public void transform(float[] in, int sourceOffset, float[] out, int 
destOffset, int nbTuple) {
-        asAffine().transform(in, sourceOffset, out, destOffset, nbTuple);
-
-        //we could use this, but it is slower.
-        //asMatrix().transform(source, sourceOffset, dest, destOffset, 
nbTuple);
-
-        //this is also slower
-//        //scale
-//        Vector<?>s.multiplyRegular(in, out, sourceOffset, destOffset, 
scale.toFloat(), nbTuple);
-//        //rotate
-//        rotation.transform(out, destOffset, out, destOffset, nbTuple);
-//        //translate
-//        Vector<?>s.addRegular(out, out, destOffset, destOffset, 
translation.toFloat(), nbTuple);
-//
-//        return out;
-    }
-
-    @Override
-    protected void transform1(double[] source, int sourceOffset, double[] 
dest, int destOffset) {
-        asAffine().transform(source, sourceOffset, dest, destOffset, 1);
-    }
-
-    @Override
-    protected void transform1(float[] source, int sourceOffset, float[] dest, 
int destOffset) {
-        asAffine().transform(source, sourceOffset, dest, destOffset, 1);
-    }
-
-    /**
-     * Inverse transform a single tuple.
-     *
-     * @param source tuple, can not be null.
-     * @param dest tuple, can be null.
-     * @return destination tuple.
-     */
-    public Tuple<?> inverseTransform(Tuple<?> source, Tuple<?> dest){
-        return inverseAff.transform(source, dest);
-    }
-
-    protected PropertyChangeSupport getEventManager() {
-        return getEventManager(true);
-    }
-
-    protected PropertyChangeSupport getEventManager(boolean create) {
-        synchronized (lock){
-            if (eventManager==null && create) eventManager = new 
PropertyChangeSupport(this);
-        }
-        return eventManager;
-    }
-
-    public void addEventListener(PropertyChangeListener listener) {
-        getEventManager().addPropertyChangeListener(listener);
-    }
-
-    public void removeEventListener(PropertyChangeListener listener) {
-        getEventManager().removePropertyChangeListener(listener);
-    }
-
-    /**
-     * Inverse view of this transform.
-     * The returned affine is no modifiable.
-     * The returned affine reflects any change made to this NodeTransform
-     *
-     * @return inverse transform view
-     */
-    @Override
-    public Affine inverse() {
-        return inverseAff;
+    public Matrix<?> getRotation() {
+        return rotation;
     }
 
     @Override
-    public SimilarityND invert() {
-        final Affine<?> affine = AffineND.create(this);
-        affine.invert();
-        set(affine);
-        return this;
+    public Vector<?> getScale() {
+        return scale;
     }
 
-
     @Override
-    public SimilarityND multiply(Affine<?> other) {
-        if (other instanceof Similarity<?> s) {
-            return multiply(s);
-        } else {
-            return multiply(new SimilarityND(dimension).set(other));
-        }
+    public Vector<?> getTranslation() {
+        return translation;
     }
 
     @Override
@@ -526,138 +160,43 @@ public class SimilarityND extends 
AbstractAffine<SimilarityND> implements Simila
     }
 
     @Override
-    public Matrix toMatrix() {
-        return viewMatrix().copy();
+    public Matrix<?> toMatrix() {
+        final Matrix<?> matrix = MatrixND.create(dimension+1, dimension+1);
+        final Vector<?> translation = getTranslation();
+        final Matrix<?> rotation = getRotation();
+        final Vector<?> scale = getScale();
+        matrix.setToIdentity();
+        matrix.set(rotation);
+        matrix.scale(scale.extend(1).toArrayDouble());
+        for (int i = 0, dimension = getDimension(); i < dimension; i++) {
+            matrix.set(i, dimension, translation.get(i));
+        }
+        return matrix;
     }
 
     @Override
-    public Matrix toMatrix(Matrix buffer) {
-        if (buffer==null) return toMatrix();
-        buffer.set(viewMatrix());
-        return buffer;
+    public Matrix<?> toMatrix(Matrix<?> matrix) {
+        if (matrix == null) matrix = MatrixND.create(dimension+1, dimension+1);
+        final Vector<?> translation = getTranslation();
+        final Matrix<?> rotation = getRotation();
+        final Vector<?> scale = getScale();
+        matrix.setToIdentity();
+        matrix.set(rotation);
+        matrix.scale(scale.extend(1).toArrayDouble());
+        for (int i = 0, dimension = getDimension(); i < dimension; i++) {
+            matrix.set(i, dimension, translation.get(i));
+        }
+        return matrix;
     }
 
     @Override
-    public SimilarityND copy() {
-        final SimilarityND aff = new SimilarityND(dimension);
-        aff.set(this);
-        return aff;
+    public Affine<?> toAffine() {
+        return AffineND.create(dimension).setFromMatrix(toMatrix());
     }
 
-    private class Inverse extends AbstractAffine<SimilarityND> {
-
-        public Inverse() {
-            super(dimension);
-        }
-
-        @Override
-        public int getInputDimensions() {
-            return dimension;
-        }
-
-        @Override
-        public int getOutputDimensions() {
-            return dimension;
-        }
-
-        @Override
-        public Tuple<?> transform(Tuple<?> source, Tuple<?> dest) {
-            if (dest == null) dest = Vectors.create(dimension, 
DataType.DOUBLE);
-            viewMatrixInverse();
-            Vector<?> vd = Vectors.castOrWrap(dest);
-            //inverse translate
-            vd.set(source).subtract(translation);
-            //inverse rotate
-            inverseRotation.transform(vd, vd);
-            //invert scale
-            vd.divide(scale);
-            return vd;
-        }
-
-        @Override
-        public void transform(double[] source, int sourceOffset, double[] 
dest, int destOffset, int nbTuple) {
-            viewMatrixInverse();
-            //inverse translate
-            Vectors.subtractRegular(source, dest, sourceOffset, destOffset, 
translation.toArrayDouble(), nbTuple);
-            //inverse rotate
-            inverseRotation.transform(dest, destOffset, dest, destOffset, 
nbTuple);
-            //invert scale
-            Vectors.divideRegular(dest, dest, destOffset, destOffset, 
scale.toArrayDouble(), nbTuple);
-        }
-
-        @Override
-        public void transform(float[] source, int sourceOffset, float[] dest, 
int destOffset, int nbTuple) {
-            viewMatrixInverse();
-            //inverse translate
-            Vectors.subtractRegular(source, dest, sourceOffset, destOffset, 
translation.toArrayFloat(), nbTuple);
-            //inverse rotate
-            inverseRotation.transform(dest, destOffset, dest, destOffset, 
nbTuple);
-            //invert scale
-            Vectors.divideRegular(dest, dest, destOffset, destOffset, 
scale.toArrayFloat(), nbTuple);
-        }
-
-        @Override
-        protected void transform1(double[] source, int sourceOffset, double[] 
dest, int destOffset) {
-            viewMatrixInverse();
-            //inverse translate
-            Vectors.subtractRegular(source, dest, sourceOffset, destOffset, 
translation.toArrayDouble(), 1);
-            //inverse rotate
-            inverseRotation.transform(dest, destOffset, dest, destOffset, 1);
-            //invert scale
-            Vectors.divideRegular(dest, dest, destOffset, destOffset, 
scale.toArrayDouble(), 1);
-        }
-
-        @Override
-        protected void transform1(float[] source, int sourceOffset, float[] 
dest, int destOffset) {
-            viewMatrixInverse();
-            //inverse translate
-            Vectors.subtractRegular(source, dest, sourceOffset, destOffset, 
translation.toArrayFloat(), 1);
-            //inverse rotate
-            inverseRotation.transform(dest, destOffset, dest, destOffset, 1);
-            //invert scale
-            Vectors.divideRegular(dest, dest, destOffset, destOffset, 
scale.toArrayFloat(), 1);
-        }
-
-        @Override
-        public double get(int row, int col) {
-            return viewMatrixInverse().get(row, col);
-        }
-
-        @Override
-        public Matrix toMatrix() {
-            return viewMatrixInverse().copy();
-        }
-
-        @Override
-        public Matrix toMatrix(Matrix buffer) {
-            if (buffer==null) {
-                return viewMatrixInverse().copy();
-            } else {
-                buffer.set(viewMatrixInverse());
-                return buffer;
-            }
-        }
-
-        @Override
-        public SimilarityND set(int row, int col, double value) throws 
IllegalArgumentException {
-            throw new UnsupportedOperationException("Not supported.");
-        }
-
-        @Override
-        public SimilarityND setFromMatrix(Matrix m) throws 
IllegalArgumentException {
-            throw new UnsupportedOperationException("Not supported.");
-        }
-
-        @Override
-        public SimilarityND createInverse() {
-            throw new UnsupportedOperationException("Not supported.");
-        }
-
-        @Override
-        public SimilarityND copy() {
-            final SimilarityND aff = new SimilarityND(dimension);
-            aff.set(Inverse.this);
-            return aff;
-        }
+    @Override
+    public SimilarityND copy() {
+        return new SimilarityND(dimension).set(this);
     }
+
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/SceneNode.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/SceneNode.java
index a106dd98f2..16f8cfb104 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/SceneNode.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/SceneNode.java
@@ -25,7 +25,7 @@ import java.util.Objects;
 import java.util.Optional;
 import org.apache.sis.geometries.Geometries;
 import org.apache.sis.geometries.math.Similarity;
-import org.apache.sis.geometries.math.SimilarityND;
+import org.apache.sis.geometries.math.Similarity3D;
 import org.apache.sis.geometry.Envelopes;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.measure.NumberRange;
@@ -48,7 +48,7 @@ import org.opengis.referencing.operation.TransformException;
  */
 public class SceneNode {
 
-    private final SimilarityND parentToNode = (SimilarityND) 
SimilarityND.create(3);
+    private final Similarity3D parentToNode = new Similarity3D();
     private SceneNode parent = null;
     private final List<SceneNode> children = new 
NotifiedCheckedList<SceneNode>(){
         @Override
@@ -181,7 +181,7 @@ public class SceneNode {
      *
      * @return Transform, never null.
      */
-    public SimilarityND getTransform() {
+    public Similarity3D getTransform() {
         return parentToNode;
     }
 
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/math/QuaternionTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/math/QuaternionTest.java
index 20bdd548bb..1634d74662 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/math/QuaternionTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/math/QuaternionTest.java
@@ -79,7 +79,7 @@ public class QuaternionTest {
         Quaternion q = new Quaternion();
         q.setFromMatrix(m);
 
-        
assertArrayEquals(m.toArrayDoubleColOrder(),q.toMatrix4().toArrayDoubleColOrder(),
 DELTA);
+        
assertArrayEquals(m.toArrayDouble(Matrix.COL_ORDER),q.toMatrix4().toArrayDouble(Matrix.COL_ORDER),
 DELTA);
     }
 
     @Test


Reply via email to