Skip to content

scale factors assumed positive in com.jme3.math #2089

@stephengold

Description

@stephengold

com.jme3.math breaks in many places if scale factors are allowed to be negative. For instance, Matrix4f.toScaleVector(Vector3f) assigns a (positive) square root to each component:

/**
* Determines the scale component of the coordinate transform.
*
* @param store storage for the result (not null, modified)
* @return the scale factors (in {@code store}) for chaining
*/
public Vector3f toScaleVector(Vector3f store) {
float scaleX = (float) Math.sqrt(m00 * m00 + m10 * m10 + m20 * m20);
float scaleY = (float) Math.sqrt(m01 * m01 + m11 * m11 + m21 * m21);
float scaleZ = (float) Math.sqrt(m02 * m02 + m12 * m12 + m22 * m22);
store.set(scaleX, scaleY, scaleZ);
return store;
}

As a consequence of this bug (or undocumented assumption) the zero-argument Matrix4f.oScaleVector() method is also broken, as is Transform.fromTransformMatrix(Matrix4f).

Here is a code fragment that demonstrates those breakages:

        Vector3f transA = new Vector3f(1f, 2f, 3f);
        Quaternion rotA = new Quaternion(0.8f, 0.4f, 0.2f, 0.4f);
        Vector3f scaleA = new Vector3f(4f, -5f, -6f);
        Transform a = new Transform(transA, rotA, scaleA);
        Matrix4f matA = a.toTransformMatrix();
        
        Transform b = new Transform();
        b.fromTransformMatrix(matA);
        System.out.println("a: " + a);
        System.out.println("b: " + b);
        System.out.println();
        
        Vector3f scaleB = matA.toScaleVector();
        System.out.println("scaleA: " + scaleA);
        System.out.println("scaleB: " + scaleB);
        System.out.println();

If this issue were solved, the transforms a and b would be approximately equal, as would the vectors scaleA and scaleB. Instead, we get:

a: Transform[ 1.0, 2.0, 3.0]
[ 0.8, 0.4, 0.2, 0.4]
[ 4.0 , -5.0, -6.0]
b: Transform[ 1.0, 2.0, 3.0]
[ -0.40000004, -0.20000003, 0.40000004, 0.8]
[ 4.0 , 5.0, 5.9999995]

scaleA: (4.0, -5.0, -6.0)
scaleB: (4.0, 5.0, 5.9999995)

There's a similar assumption of positive scale factors in Quaternion.fromRotationMatrix(), which rejects scaling but only if all the scale factors are positive.

Directly or indirectly, JMonkeyEngine uses these methods in many places, notably Transform.invert(), which is similarly broken, as demonstrated by the following code fragment:

        Vector3f transA = new Vector3f(1f, 2f, 3f);
        Quaternion rotA = new Quaternion(0.8f, 0.4f, 0.2f, 0.4f);
        Vector3f scaleA = new Vector3f(4f, -5f, -6f);
        Transform a = new Transform(transA, rotA, scaleA);
        
        Transform aInverse = a.invert();
        Transform c = aInverse.invert();
        System.out.println("a: " + a);
        System.out.println("c: " + c);
        System.out.println();

If this issue were solved, the transforms a and c would be approximately equal. Instead, we get:

a: Transform[ 1.0, 2.0, 3.0]
[ 0.8, 0.4, 0.2, 0.4]
[ 4.0 , -5.0, -6.0]
c: Transform[ 1.74527, 2.0037012, 2.7823966]
[ -0.3705214, -0.15285265, 0.4035354, 0.8135669]
[ 4.5550847 , 5.043545, 4.949222]

If a matrix contains an odd number of negative scale factors, the fact can be detected using determinants. However, I don't know an easy way to detect an even number of negative scale factors.

Metadata

Metadata

Assignees

Labels

DocumentationIssues that affect the Wiki, Javadoc or any other form of documentation

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions