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);
+ }
+}