[removed] issue with 360deg rotations and matrix c

2019-09-11 10:44发布

问题:

I asked previously about the proper function for perspective (to compute and compose a matrix), now I'm facing an older problem and I wasn't expecting this to be so much of an issue still.

Basically 360deg / 720deg is just zero and I don't know how to hack the following function to fix that.

CSSMatrix.Rotate = function(rx, ry, rz){
 rx *= Math.PI / 180;
 ry *= Math.PI / 180;
 rz *= Math.PI / 180;

 // minus sin() because of right-handed system
 var cosx = Math.cos(rx), sinx = - Math.sin(rx);
 var cosy = Math.cos(ry), siny = - Math.sin(ry);
 var cosz = Math.cos(rz), sinz = - Math.sin(rz);
 var m = new CSSMatrix();

 m.m11 = m.a = cosy * cosz;
 m.m12 = m.b = -cosy * sinz;
 m.m13 = siny;

m.m21 = m.c = sinx * siny * cosz + cosx * sinz;
m.m22 = m.d = cosx * cosz - sinx * siny * sinz;
m.m23 = - sinx * cosy;

 m.m31 = sinx * sinz - cosx * siny * cosz;
 m.m32 = sinx * cosz + cosx * siny * sinz;
 m.m33 = cosx * cosy;

 return m;
};

When using 360deg rotations (on any axis) to compose a matrix, the CSSMatrix.rotate() method is creating a rotation matrix and for each angle value we get angle * Math.PI / 180 then other sinus / cosinus operations, but the matrix result is different from a computed transform of a regular rotateX(360deg).

See my fiddles here where same code doesn't work properly with 360deg angle and working properly with angles different from 360deg.

How can I fix that please?

回答1:

The issue here is the precision supported by the CSSMatrix polyfill's code. It supports upto 6 decimals and truncates any lesser value (positive or negative) to 0 i.e. anything less than 0.000001 will be converted to 0.

In your fiddle, if you just apply the rotateX(360deg) transform, it converts to this matrix3d:

matrix3d(1, 0, 0, 0, 0, 1, -2.44929e-16, 0, 0, 2.44929e-16, 1, 0, 0, 0, 0, 1)

The polyfill converts -2.44929e-16 and 2.44929e-16 to 0 thereby generating this matrix3d:

matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)

Increasing the decimal precision in the polyfill's code fixes this issue. Change line 35 from: CSSMatrix.SMALL_NUMBER = 1e-6;

to

CSSMatrix.SMALL_NUMBER = 1e-20; // 20 decimal point precision

I've fixed that in this fiddle.


Edit: Regarding the question in the comment about different matrices being generated when applying rotate along 2 axes: This is because the compose function used in the fiddle applies rotation along all axes at the same time - which would be equivalent to a single rotate3d(x, y, z) call.

But the transforms applied via CSS in the fiddle rotate on the X and Z axes separately which would be equivalent to applying rotate(x, 0, 0) followed by rotate(0, 0, z).

This can be verified by changing the compose function in the fiddle and comparing the matrix3d generated by the polyfill vs the one generated by the browser.