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 517b8edb7e feat(Math): remove several matrix static methods, now part 
of matrix classes
517b8edb7e is described below

commit 517b8edb7e8a970f7ef7c24abdbf93c57f95df9e
Author: jsorel <[email protected]>
AuthorDate: Fri Mar 13 14:34:37 2026 +0100

    feat(Math): remove several matrix static methods, now part of matrix classes
---
 .../apache/sis/geometries/math/AbstractMatrix.java |  13 ++
 .../sis/geometries/math/AbstractSimilarity.java    | 137 +++++++-----
 .../org/apache/sis/geometries/math/Matrices.java   | 247 ---------------------
 .../org/apache/sis/geometries/math/Matrix.java     |  13 ++
 .../org/apache/sis/geometries/math/Matrix3D.java   |  38 +++-
 .../org/apache/sis/geometries/math/MatrixND.java   |  57 ++++-
 .../org/apache/sis/geometries/math/ReadOnly.java   |  14 +-
 .../org/apache/sis/geometries/math/Similarity.java |  62 +++---
 .../apache/sis/geometries/math/Similarity3D.java   |  26 +--
 .../apache/sis/geometries/math/SimilarityND.java   |  54 ++---
 10 files changed, 258 insertions(+), 403 deletions(-)

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 72747bd6c1..a820d69ec0 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
@@ -465,6 +465,19 @@ abstract class AbstractMatrix<T extends AbstractMatrix<T>> 
extends SimplifiedTra
         return set((ReadOnly.Matrix)t);
     }
 
+    @Override
+    public T roundZeros(double epsilon){
+        for (int x=0;x<nbCol;x++){
+            for (int y=0;y<nbRow;y++){
+                double d = get(y, x);
+                if (!(d>epsilon || d<-epsilon)){
+                    set(y,x, 0.0);
+                }
+            }
+        }
+        return (T) this;
+    }
+
     /**
      * invert matrix
      */
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
index 0b995e23e3..676c48f0b1 100644
--- 
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
@@ -74,9 +74,9 @@ public abstract class AbstractSimilarity<T extends 
AbstractSimilarity<T>> extend
         if (dirty){
             dirty = false;
             //update matrix
-            final Vector<?> translation = getTranslation();
-            final Matrix<?> rotation = getRotation();
-            final Vector<?> scale = getScale();
+            final ReadOnly.Vector<?> translation = getTranslation();
+            final ReadOnly.Matrix<?> rotation = getRotation();
+            final ReadOnly.Vector<?> scale = getScale();
             matrix.setToIdentity();
             matrix.set(rotation);
             matrix.scale(scale.extend(1).toArrayDouble());
@@ -136,58 +136,96 @@ public abstract class AbstractSimilarity<T extends 
AbstractSimilarity<T>> extend
         return (T) this;
     }
 
+    /**
+     * Decompose a matrix in rotation, scale and translation.
+     * The matrix is expected to be orthogonal of size 3x3 or 4x4.
+     */
     @Override
     public T setFromMatrix(ReadOnly.Matrix<?> matrix) {
-        Matrices.decomposeMatrix(matrix, getRotation(), getScale(), 
getTranslation());
-        notifyChanged();
-        return (T) this;
+        return update((Matrix<?> rotation, Vector<?> scale, Vector<?> 
translation) -> {
+            final int dimension = matrix.getNumCol()-1;
+            if (dimension == 2){
+                final double scaleX = Math.sqrt(matrix.get(0,0)*matrix.get(0,0)
+                        + matrix.get(1,0)*matrix.get(1,0));
+                final double scaleY = Math.sqrt(matrix.get(0,1)*matrix.get(0,1)
+                        + matrix.get(1,1)*matrix.get(1,1));
+
+                final double[] invertScale = new double[]{1d/scaleX,1d/scaleY};
+                final Matrix<?> rot = matrix.getRange(0, 1, 0, 1);
+                rot.scale(invertScale);
+
+                rotation.set(rot);
+                scale.set(0,scaleX);
+                scale.set(1,scaleY);
+                translation.set(0, matrix.get(0,2));
+                translation.set(1, matrix.get(1,2));
+            } else if (dimension == 3){
+                final double scaleX = Math.sqrt(matrix.get(0,0)*matrix.get(0,0)
+                        + matrix.get(1,0)*matrix.get(1,0)
+                        + matrix.get(2,0)*matrix.get(2,0));
+                final double scaleY = Math.sqrt(matrix.get(0,1)*matrix.get(0,1)
+                        + matrix.get(1,1)*matrix.get(1,1)
+                        + matrix.get(2,1)*matrix.get(2,1));
+                final double scaleZ = Math.sqrt(matrix.get(0,2)*matrix.get(0,2)
+                        + matrix.get(1,2)*matrix.get(1,2)
+                        + matrix.get(2,2)*matrix.get(2,2));
+                final double[] invertScale = new 
double[]{1d/scaleX,1d/scaleY,1d/scaleZ};
+                final Matrix<?> rot = matrix.getRange(0, 2, 0, 2);
+                rot.scale(invertScale);
+
+                rotation.set(rot);
+                scale.set(0, scaleX);
+                scale.set(1, scaleY);
+                scale.set(2, scaleZ);
+                translation.set(0,matrix.get(0,3));
+                translation.set(1,matrix.get(1,3));
+                translation.set(2,matrix.get(2,3));
+
+            } else {
+                throw new UnsupportedOperationException("Only works for 2D and 
3D for now. TODO");
+            }
+            return ALL_UPDATED;
+        });
     }
 
     @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;
+        return update((Matrix<?> rotation, Vector<?> scale, Vector<?> 
translation) -> {
+            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);
+            }
+            return change ? ALL_UPDATED : 0;
+        });
     }
 
     @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;
+        return update((Matrix<?> rotation, Vector<?> scale, Vector<?> 
translation) -> {
+            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);
+            }
+            return change ? ALL_UPDATED : 0;
+        });
     }
 
     @Override
@@ -251,10 +289,8 @@ public abstract class AbstractSimilarity<T extends 
AbstractSimilarity<T>> extend
     }
 
     @Override
-    public Matrix<?> toMatrix(Matrix<?> buffer) {
-        if (buffer == null) return toMatrix();
-        buffer.set(viewMatrix());
-        return buffer;
+    public Affine<?> toAffine() {
+        return viewAffine().copy();
     }
 
     protected PropertyChangeSupport getEventManager() {
@@ -280,8 +316,7 @@ public abstract class AbstractSimilarity<T extends 
AbstractSimilarity<T>> extend
      * Flag to indicate the transform parameters has changed.
      * This is used to recalculate the general matrix when needed.
      */
-    @Override
-    public void notifyChanged(){
+    protected void notifyChanged(){
         dirty = true;
         inverseDirty = true;
         affineDirty = true;
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 5ab0b06afe..cba3dd01ad 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
@@ -26,61 +26,6 @@ public final class Matrices {
 
     private Matrices(){}
 
-    /**
-     * Checks if the given matrix is the identity matrix.
-     *
-     * @param m matrix to test.
-     * @return true if matrix is an Identity matrix.
-     */
-    public static boolean isIdentity(final double[][] m){
-        if ( m.length!=m[0].length ) return false; // m must be a square matrix
-        for ( int x=0; x<m[0].length; x++) {
-            for ( int y=0; y<m.length; y++) {
-                if (x==y){
-                    if ( m[y][x] != 1 ) return false;
-                } else {
-                    if ( m[y][x] != 0 ) return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Sets the given matrix to identity matrix.
-     *
-     * @param m a square matrix.
-     * @throws IllegalArgumentException when m is not a square matrix.
-     */
-    public static void setToIdentity( double[][] m ){
-        if (m.length != m[0].length) {
-            throw new IllegalArgumentException("The m matrix must be a square 
matrix.");
-        }
-        for (int x = 0; x < m[0].length; x++) {
-            for (int y = 0; y < m.length; y++) {
-                if (x == y) {
-                    m[y][x] = 1.;
-                } else {
-                    m[y][x] = 0.;
-                }
-            }
-        }
-    }
-
-    /**
-     * Creates a n*n identity matrix.
-     *
-     * @param n number of rows and columns.
-     * @return an n*n identity matrix.
-     */
-    public static double[][] identity(int n) {
-        double[][] identity = new double[n][n];
-        for (int i = 0; i < n; i++) {
-            identity[i][i] = 1.;
-        }
-        return identity;
-    }
-
     /**
      * Adds m1 and m2, the result is stored in m1, returns m1.
      * see {@link Matrices#add(un.science.math.Matrix, un.science.math.Matrix, 
un.science.math.Matrix)}
@@ -387,99 +332,6 @@ public final class Matrices {
         return sum;
     }
 
-    /**
-     * replace valeus close to 0 with zero, removing -0 if present
-     * @param matrix
-     * @param epsilon
-     */
-    public static void roundZeros(double[][] matrix, double epsilon){
-        for (int x=0;x<matrix.length;x++){
-            for (int y=0;y<matrix[0].length;y++){
-                if (!(matrix[x][y]>epsilon || matrix[x][y]<-epsilon)){
-                    matrix[x][y] = 0.0;
-                }
-            }
-        }
-    }
-
-    public static Tuple<?> transformLocal(final double[][] matrix, final 
Tuple<?> vector) {
-        return transform(matrix, vector, vector);
-    }
-
-    public static Tuple<?> transform(final double[][] matrix, final 
ReadOnly.Tuple<?> vector, Tuple<?> buffer) {
-        if (buffer==null) buffer = Vectors.createDouble(matrix.length);
-        final double[] array = new double[matrix.length];
-        transform(matrix, vector.toArrayDouble(), array);
-        buffer.set(array);
-        return buffer;
-    }
-
-    /**
-     * Transform given vector.
-     *
-     * @param matrix input matrix
-     * @param vector vector to transform
-     * @param buffer result vector buffer, can be null
-     * @return the product of matrix and vector.
-     */
-    public static double[] transform(final double[][] matrix, final double[] 
vector, double[] buffer) {
-        if (vector.length != matrix[0].length){
-            throw new IllegalArgumentException("matrix column size and vector 
size differ : "+matrix[0].length+","+vector.length);
-        }
-
-        final int nbRow = matrix.length;
-        final int nbCol = matrix[0].length;
-        final double[] res = new double[nbRow];
-
-        for (int r = 0; r < nbRow; r++) {
-            double s = 0;
-            for (int c = 0; c < nbCol; c++) {
-                s += matrix[r][c] * vector[c];
-            }
-            res[r] = s;
-        }
-
-        if ( buffer == null ) {
-            buffer = new double[matrix.length];
-        }
-        System.arraycopy(res, 0, buffer, 0, res.length);
-
-        return buffer;
-    }
-
-    /**
-     * Transforms given vector.
-     *
-     * @param matrix transformation matrix.
-     * @param vector considered as a column matrix.
-     * @param buffer result vector buffer, can be null
-     * @return the product of matrix and vector.
-     */
-    public static float[] transform(final double[][] matrix, final float[] 
vector, float[] buffer) {
-        if (vector.length != matrix[0].length){
-            throw new IllegalArgumentException("matrix column size and vector 
size differ : "+matrix[0].length+","+vector.length);
-        }
-
-        if (buffer == null) {
-            buffer = new float[matrix.length];
-        }
-
-        final int nbRow = matrix.length;
-        final int nbCol = matrix[0].length;
-        final float[] res = new float[nbRow];
-
-        for (int r = 0; r < nbRow; r++) {
-            double s = 0;
-            for (int c = 0; c < nbCol; c++) {
-                s += matrix[r][c] * vector[c];
-            }
-            res[r] = (float) s;
-        }
-
-        System.arraycopy(res, 0, buffer, 0, res.length);
-        return buffer;
-    }
-
     /**
      * Multiply m1 by m2, result is stored in m1, returns m1.
      * see {@link Matrices#multiply(un.science.math.Matrix, 
un.science.math.Matrix, un.science.math.Matrix)}
@@ -697,58 +549,6 @@ public final class Matrices {
         return buffer;
     }
 
-    /**
-     * Calculate Euler angle of given matrix.
-     * Source :
-     * http://www.soi.city.ac.uk/~sbbh653/publications/euler.pdf
-     * http://jeux.developpez.com/faq/math/?page=transformations#Q37
-     *
-     * @param mat input matrix
-     * @param buffer euler buffer, can be null
-     * @return euler angle in radians (heading/yaw , elevation/pitch , 
bank/roll)
-     */
-    public static double[] toEuler(double[][] mat, double[] buffer){
-
-        if (buffer == null){
-            buffer = new double[3];
-        }
-
-        double angle_x;
-        double angle_y;
-        double angle_z;
-
-        if (mat[2][0] != -1 && mat[2][0] != +1){
-            //first possible solution
-            angle_y = -Math.asin(mat[2][0]);
-            double cosy1 = Math.cos(angle_y);
-            angle_x = Math.atan2(mat[2][1]/cosy1, mat[2][2]/cosy1);
-            angle_z = Math.atan2(mat[1][0]/cosy1, mat[0][0]/cosy1);
-
-            //second possible solution
-            //angle_y = Angles.PI - y1;
-            //double cosy2 = Math.cos(angle_y);
-            //angle_x = Math.atan2(mat[2][1]/cosy2, mat[2][2]/cosy2);
-            //angle_z = Math.atan2(mat[1][0]/cosy2, mat[0][0]/cosy2);
-
-        } else {
-            // Gimbal lock
-            angle_z = 0;
-            if (mat[2][0] == -1){
-                angle_y = Math.PI / 2.0;
-                angle_x = angle_z + Math.atan2(mat[0][1], mat[0][2]);
-            } else {
-                angle_y = -Math.PI / 2.0;
-                angle_x = -angle_z + Math.atan2(-mat[0][1], -mat[0][2]);
-            }
-        }
-
-
-        buffer[0] = angle_z;
-        buffer[1] = angle_y;
-        buffer[2] = angle_x;
-        return buffer;
-    }
-
     /**
      * Create and orbit matrix 4x4 focus on the root point (0,0,0).
      *
@@ -785,51 +585,4 @@ public final class Matrices {
         return result.getValues();
     }
 
-    /**
-     * Decompose a matrix in rotation, scale and translation.
-     * The matrix is expected to be orthogonal of size 3x3 or 4x4.
-     */
-    public static void decomposeMatrix(ReadOnly.Matrix<?> trs, Matrix<?> 
rotation, Tuple<?> scale, Tuple<?> translation){
-        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));
-            final double scaleY = Math.sqrt(trs.get(0,1)*trs.get(0,1)
-                                          + trs.get(1,1)*trs.get(1,1));
-
-            final double[] invertScale = new double[]{1d/scaleX,1d/scaleY};
-            final Matrix<?> rot = trs.getRange(0, 1, 0, 1);
-            rot.scale(invertScale);
-
-            rotation.set(rot);
-            scale.set(0,scaleX);
-            scale.set(1,scaleY);
-            translation.set(0, trs.get(0,2));
-            translation.set(1, trs.get(1,2));
-        } else if (dimension == 3){
-            final double scaleX = Math.sqrt(trs.get(0,0)*trs.get(0,0)
-                                          + trs.get(1,0)*trs.get(1,0)
-                                          + trs.get(2,0)*trs.get(2,0));
-            final double scaleY = Math.sqrt(trs.get(0,1)*trs.get(0,1)
-                                          + trs.get(1,1)*trs.get(1,1)
-                                          + trs.get(2,1)*trs.get(2,1));
-            final double scaleZ = Math.sqrt(trs.get(0,2)*trs.get(0,2)
-                                          + trs.get(1,2)*trs.get(1,2)
-                                          + trs.get(2,2)*trs.get(2,2));
-            final double[] invertScale = new 
double[]{1d/scaleX,1d/scaleY,1d/scaleZ};
-            final Matrix rot = trs.getRange(0, 2, 0, 2);
-            rot.scale(invertScale);
-
-            rotation.set(rot);
-            scale.set(0, scaleX);
-            scale.set(1, scaleY);
-            scale.set(2, scaleZ);
-            translation.set(0,trs.get(0,3));
-            translation.set(1,trs.get(1,3));
-            translation.set(2,trs.get(2,3));
-
-        } else {
-            throw new IllegalArgumentException("Only works for 2D and 3D for 
now. TODO");
-        }
-    }
 }
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 89739a39e0..8026a1c7a7 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
@@ -34,6 +34,11 @@ public interface Matrix<T extends Matrix<T>> extends 
ReadOnly.Matrix<T>, org.ope
         return org.opengis.referencing.operation.Matrix.super.isIdentity();
     }
 
+    @Override
+    default boolean isSquare() {
+        return getNumRow() == getNumCol();
+    }
+
     @Override
     default double getElement(int row, int col) {
         return get(row,col);
@@ -133,6 +138,14 @@ public interface Matrix<T extends Matrix<T>> extends 
ReadOnly.Matrix<T>, org.ope
 
     T transpose();
 
+    /**
+     * replace valeus close to 0 with zero, removing -0 if present
+     *
+     * @param epsilon tolerance
+     * @return this matrix
+     */
+    T roundZeros(double epsilon);
+
     T copy();
 
     @Override
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 2c522a8f40..cc91e57736 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
@@ -294,10 +294,44 @@ public class Matrix3D extends AbstractMatrix<Matrix3D> {
 
     /**
      * Convert to euler angles.
+     * Source :
+     * http://www.soi.city.ac.uk/~sbbh653/publications/euler.pdf
+     * http://jeux.developpez.com/faq/math/?page=transformations#Q37
+     *
      * @return euler angle in radians (heading/yaw , elevation/pitch , 
bank/roll)
      */
-    public Vector<?> toEuler(){
-        return new VectorND.Double(Matrices.toEuler(toArray2Double(ROW_ORDER), 
null));
+    public Vector3D.Double toEuler(){
+
+        double angle_x;
+        double angle_y;
+        double angle_z;
+
+        if (m20 != -1 && m20 != +1){
+            //first possible solution
+            angle_y = -Math.asin(m20);
+            double cosy1 = Math.cos(angle_y);
+            angle_x = Math.atan2(m21/cosy1, m22/cosy1);
+            angle_z = Math.atan2(m10/cosy1, m00/cosy1);
+
+            //second possible solution
+            //angle_y = Angles.PI - y1;
+            //double cosy2 = Math.cos(angle_y);
+            //angle_x = Math.atan2(mat[2][1]/cosy2, mat[2][2]/cosy2);
+            //angle_z = Math.atan2(mat[1][0]/cosy2, mat[0][0]/cosy2);
+
+        } else {
+            // Gimbal lock
+            angle_z = 0;
+            if (m20 == -1){
+                angle_y = Math.PI / 2.0;
+                angle_x = angle_z + Math.atan2(m01, m02);
+            } else {
+                angle_y = -Math.PI / 2.0;
+                angle_x = -angle_z + Math.atan2(-m01, -m02);
+            }
+        }
+
+        return new Vector3D.Double(angle_z, angle_y, angle_x);
     }
 
     /**
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 e7eff9f878..dcfcd0e99d 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
@@ -307,13 +307,20 @@ public class MatrixND extends AbstractMatrix<MatrixND>{
         return this;
     }
 
-    /**
-     * Set Matrix value to identity matrix.
-     * @return this matrix
-     */
     @Override
     public MatrixND setToIdentity(){
-        Matrices.setToIdentity(values);
+        if (values.length != values[0].length) {
+            throw new IllegalArgumentException("The matrix is not a square 
matrix.");
+        }
+        for (int x = 0; x < values[0].length; x++) {
+            for (int y = 0; y < values.length; y++) {
+                if (x == y) {
+                    values[y][x] = 1.;
+                } else {
+                    values[y][x] = 0.;
+                }
+            }
+        }
         return this;
     }
 
@@ -330,8 +337,24 @@ public class MatrixND extends AbstractMatrix<MatrixND>{
     }
 
     @Override
-    public boolean isIdentity(){
-        return Matrices.isIdentity(values);
+    public boolean isIdentity() {
+        if (values.length != values[0].length) {
+            return false; // m must be a square matrix
+        }
+        for (int x = 0; x < values[0].length; x++) {
+            for (int y = 0; y < values.length; y++) {
+                if (x == y) {
+                    if (values[y][x] != 1) {
+                        return false;
+                    }
+                } else {
+                    if (values[y][x] != 0) {
+                        return false;
+                    }
+                }
+            }
+        }
+        return true;
     }
 
     /**
@@ -419,7 +442,7 @@ public class MatrixND extends AbstractMatrix<MatrixND>{
         //TODO improve it, slow and memory greedy
         final double[] v = Arrays.copyOfRange(source, sourceOffset, 
sourceOffset + getInputDimensions());
         final double[] d = new double[getOutputDimensions()];
-        Matrices.transform(values, v, d);
+        internalTransform(v, d);
         System.arraycopy(d,0,dest,destOffset,d.length);
     }
 
@@ -431,8 +454,24 @@ public class MatrixND extends AbstractMatrix<MatrixND>{
             v[i] = source[sourceOffset+i];
         }
         final double[] d = new double[getOutputDimensions()];
-        Matrices.transform(values, v, d);
+        internalTransform(v, d);
         for (int i=0;i<d.length;i++) dest[destOffset+i] = (float) d[i];
     }
 
+    /**
+     * Transform given vector.
+     *
+     * @param vector vector to transform
+     * @param buffer result vector buffer, not null
+     * @return the product of matrix and vector.
+     */
+    private void internalTransform(final double[] vector, double[] buffer) {
+        for (int r = 0; r < nbRow; r++) {
+            double s = 0;
+            for (int c = 0; c < nbCol; c++) {
+                s += values[r][c] * vector[c];
+            }
+            buffer[r] = s;
+        }
+    }
 }
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/ReadOnly.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/ReadOnly.java
index ccbe79890d..94015988ca 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/ReadOnly.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/ReadOnly.java
@@ -509,6 +509,11 @@ public final class ReadOnly {
          */
         boolean isFinite();
 
+        /**
+         * @return true if matrix has the same number of rows and columns.
+         */
+        boolean isSquare();
+
         /**
          * For compatibility with MatrixSIS.
          *
@@ -568,15 +573,6 @@ public final class ReadOnly {
          */
         org.apache.sis.geometries.math.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
-         */
-        org.apache.sis.geometries.math.Matrix<?> 
toMatrix(org.apache.sis.geometries.math.Matrix<?> buffer);
-
         /**
          * Create and affine transform.
          *
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 4fb692b1c8..91c8a282ad 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
@@ -37,33 +37,19 @@ import org.opengis.referencing.operation.MathTransform;
  */
 public interface Similarity<T extends Similarity<T>> extends 
ReadOnly.Similarity<T> {
 
-    /**
-     * Get transform rotation.
-     * Call notifyChanged after if you modified the values.
-     *
-     * @return Matrix
-     */
-    @Override
-    Matrix<?> getRotation();
+    public static final int ROTATION_UPDATED = 1;
+    public static final int SCALE_UPDATED = 1 << 1;
+    public static final int TRANSLATION_UPDATED = 1 << 2;
+    public static final int ALL_UPDATED = ROTATION_UPDATED | SCALE_UPDATED | 
TRANSLATION_UPDATED;
 
     /**
-     * Get transform scale.
-     * Call notifyChanged after if you modified the values.
+     * Update this similarity.
+     * This method will send a change event if values have changed.
      *
-     * @return Vector
-     */
-    @Override
-    Vector<?> getScale();
-
-    /**
-     * Get transform translation.
-     * Call notifyChanged after if you modified the values.
-     *ç&é ,
-     * +
-     * @return Vector
+     * @param updater, gets rotation,scale,translation as input and returns 
the modified elements.
+     * @return this similarity
      */
-    @Override
-    Vector<?> getTranslation();
+    T update(TriFunction<Matrix<?>,Vector<?>,Vector<?>,Integer> updater);
 
     /**
      * Get a general matrix view of size : dimension+1
@@ -85,11 +71,20 @@ public interface Similarity<T extends Similarity<T>> 
extends ReadOnly.Similarity
      */
     ReadOnly.Matrix<?> viewMatrixInverse();
 
+    /**
+     * Get an affine view of size : dimension+1
+     * This affine 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]
+     *
+     * @return Affine, never null
+     */
     ReadOnly.Affine<?> viewAffine();
 
     /**
      * 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
@@ -98,6 +93,7 @@ public interface Similarity<T extends Similarity<T>> extends 
ReadOnly.Similarity
 
     /**
      * Multiply this similarity by given similarity and store the result in 
this similarity.
+     * This method will send a change event if values have changed.
      *
      * @param other multiplying similarity
      * @return this similarity
@@ -106,6 +102,8 @@ public interface Similarity<T extends Similarity<T>> 
extends ReadOnly.Similarity
 
     /**
      * Copy values from given transform.
+     * This method will send a change event if values have changed.
+     *
      * @param trs
      * @return this instance
      */
@@ -114,6 +112,7 @@ public interface Similarity<T extends Similarity<T>> 
extends ReadOnly.Similarity
     /**
      * Set transform from given matrix.
      * Matrix must be orthogonal of size dimension+1.
+     * This method will send a change event if values have changed.
      *
      * @param trs
      * @throws IllegalArgumentException if matrix is not affine
@@ -123,6 +122,7 @@ public interface Similarity<T extends Similarity<T>> 
extends ReadOnly.Similarity
     /**
      * Set transform from given affine.
      * Affine must be of same size.
+     * This method will send a change event if values have changed.
      *
      * @param trs
      */
@@ -137,14 +137,15 @@ public interface Similarity<T extends Similarity<T>> 
extends ReadOnly.Similarity
     /**
      * Set this transform to given translation.
      * This will reset rotation and scale values.
-     *
      * This method will send a change event if values have changed.
+     *
      * @param trs
      */
     T setToTranslation(double[] trs);
 
     /**
      * Inverse this affine transform.
+     * This method will send a change event if values have changed.
      *
      * @return this affine instance
      */
@@ -158,16 +159,15 @@ public interface Similarity<T extends Similarity<T>> 
extends ReadOnly.Similarity
     @Override
     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());
     }
+
+    @FunctionalInterface
+    public static interface TriFunction<T, U, V, R> {
+        R apply(T t, U u, V v);
+    }
 }
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
index c090fc8e1e..c6aaf4e124 100644
--- 
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
@@ -25,9 +25,9 @@ import java.util.Objects;
  */
 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);
+    private final Vector3D.Double scale = new Vector3D.Double(1, 1, 1);
+    private final Vector3D.Double translation = new Vector3D.Double(0, 0, 0);
+    private final Matrix3D rotation = new Matrix3D(1,0,0, 0,1,0, 0,0,1);
 
     public Similarity3D(){
         super(3);
@@ -49,20 +49,27 @@ public final class Similarity3D extends 
AbstractSimilarity<Similarity3D> {
     }
 
     @Override
-    public Matrix3D getRotation() {
+    public ReadOnly.Matrix<?> getRotation() {
         return rotation;
     }
 
     @Override
-    public Vector3D.Double getScale() {
+    public ReadOnly.Vector<?> getScale() {
         return scale;
     }
 
     @Override
-    public Vector3D.Double getTranslation() {
+    public ReadOnly.Vector<?> getTranslation() {
         return translation;
     }
 
+    @Override
+    public Similarity3D update(TriFunction<Matrix<?>, Vector<?>, Vector<?>, 
Integer> updater) {
+        final Integer changed = updater.apply(rotation, scale, translation);
+        if (changed != 0) notifyChanged();
+        return this;
+    }
+
     @Override
     public Similarity3D multiply(ReadOnly.Similarity<?> other) {
         final Vector<?> resTrans = other.getTranslation().copy()
@@ -136,13 +143,6 @@ public final class Similarity3D extends 
AbstractSimilarity<Similarity3D> {
         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);
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 9dcc6c2139..d4919e9c9a 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
@@ -38,9 +38,9 @@ package org.apache.sis.geometries.math;
 public class SimilarityND extends AbstractSimilarity<SimilarityND> {
 
     private final int dimension;
-    public final Matrix<?> rotation;
-    public final Vector<?> scale;
-    public final Vector<?> translation;
+    private final Matrix<?> rotation;
+    private final Vector<?> scale;
+    private final Vector<?> translation;
 
     public static Similarity<?> create(int dimension) {
         return new SimilarityND(dimension);
@@ -115,20 +115,27 @@ public class SimilarityND extends 
AbstractSimilarity<SimilarityND> {
     }
 
     @Override
-    public Matrix<?> getRotation() {
+    public ReadOnly.Matrix<?> getRotation() {
         return rotation;
     }
 
     @Override
-    public Vector<?> getScale() {
+    public ReadOnly.Vector<?> getScale() {
         return scale;
     }
 
     @Override
-    public Vector<?> getTranslation() {
+    public ReadOnly.Vector<?> getTranslation() {
         return translation;
     }
 
+    @Override
+    public SimilarityND update(TriFunction<Matrix<?>, Vector<?>, Vector<?>, 
Integer> updater) {
+        final Integer changed = updater.apply(rotation, scale, translation);
+        if (changed != 0) notifyChanged();
+        return this;
+    }
+
     @Override
     public SimilarityND multiply(ReadOnly.Similarity<?> other) {
         /*
@@ -159,41 +166,6 @@ public class SimilarityND extends 
AbstractSimilarity<SimilarityND> {
         return this;
     }
 
-    @Override
-    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<?> 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 Affine<?> toAffine() {
-        return AffineND.create(dimension).setFromMatrix(toMatrix());
-    }
-
     @Override
     public SimilarityND copy() {
         return new SimilarityND(dimension).set(this);


Reply via email to