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 a1fa272d75 feat(Math): add Similarity transform interface and 
Similarity3D
a1fa272d75 is described below

commit a1fa272d758d8761cbe1b7d14730868ee451d89b
Author: jsorel <[email protected]>
AuthorDate: Thu Feb 19 12:03:49 2026 +0100

    feat(Math): add Similarity transform interface and Similarity3D
---
 .../org/apache/sis/geometries/math/Similarity.java |  54 ++++++++
 .../apache/sis/geometries/math/Similarity3D.java   | 139 +++++++++++++++++++++
 2 files changed, 193 insertions(+)

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
new file mode 100644
index 0000000000..05643486ff
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Similarity.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+/**
+ * A similarity is the equivalent of a affine transform but preserving angles 
by avoiding
+ * shearing value not rotations.
+ * 3 different elements are stored.
+ * - rotation matrix
+ * - translation vector
+ * - scale vector
+ * <p>
+ * The purpose of similary is to store elements separately, avoiding 
innacuracy and progressive
+ * distortion when opearations are accumulated.
+ * <p>
+ * A good description of the problem can be found here :
+ * 
https://www.gamedeveloper.com/programming/in-depth-matrices-rotation-scale-and-drifting
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public interface Similarity {
+
+    /**
+     * 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.
+     */
+    boolean isIdentity();
+
+    /**
+     * Set this transformation to identity.
+     */
+    void toIdentity();
+
+}
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..ec6f76d107
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Similarity3D.java
@@ -0,0 +1,139 @@
+/*
+ * 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;
+import org.apache.sis.referencing.operation.matrix.Matrix3;
+import org.apache.sis.referencing.operation.matrix.Matrix4;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.opengis.referencing.operation.MathTransform;
+
+/**
+ * 3D similarity.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class Similarity3D implements Similarity {
+
+    public final Vector3D.Double scale = new Vector3D.Double(1, 1, 1);
+    public final Vector3D.Double translate = new Vector3D.Double(0, 0, 0);
+    public final Matrix3 rotation = new Matrix3(1,0,0, 0,1,0, 0,0,1);
+
+    public Similarity3D(){}
+
+    /**
+     * 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 scale.isAll(1) && translate.isAll(0) && rotation.isIdentity();
+    }
+
+    /**
+     * Set this transformation to identity.
+     */
+    @Override
+    public void toIdentity() {
+        scale.setAll(1.0);
+        translate.setAll(0.0);
+        rotation.m00 = 1.0; rotation.m01 = 0.0; rotation.m02 = 0.0;
+        rotation.m10 = 0.0; rotation.m11 = 1.0; rotation.m12 = 0.0;
+        rotation.m20 = 0.0; rotation.m21 = 0.0; rotation.m22 = 1.0;
+    }
+
+    /**
+     * 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]
+     */
+    public Matrix4 toMatrix() {
+
+        Matrix4 matrix = new Matrix4();
+        //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 = translate.x;
+        matrix.m13 = translate.y;
+        matrix.m23 = translate.z;
+
+        return matrix;
+    }
+
+    /**
+     * Extract scale, translation and rotation from 4x4 matrix.
+     */
+    public void fromMatrix(Matrix4 matrix) {
+        Matrices.decomposeMatrix(matrix, rotation, scale, translate);
+    }
+
+    /**
+     * Copy scale, translation and rotation from transform.
+     */
+    public void fromTransform(Similarity3D trs) {
+        this.rotation.setMatrix(trs.rotation);
+        this.translate.set(trs.translate);
+        this.scale.set(trs.scale);
+    }
+
+    /**
+     * Combine the different elements to obtain a linear transform of 
dimension 3.
+     */
+    public MathTransform toMathTransform() {
+        return MathTransforms.linear(toMatrix());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                this.scale,
+                this.translate,
+                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.translate, other.translate)
+            && Objects.equals(this.rotation, other.rotation);
+    }
+}

Reply via email to