Skip to content

[css-transforms-2] The ratio of quaternionA in Slerp algorithm #9338

Open
@BorisChiou

Description

@BorisChiou

Per spec in [css-transforms-2], in the section of Interpolation of decomposed 3D matrix values:

w = sin(t * theta) / sqrt(1 - product * product)
quaternionA[i] *= cos(t * theta) - product * w
quaternionB[i] *= w
quaternionDst[i] = quaternionA[i] + quaternionB[i]

The ratio of quaternionA is cos(t * theta) - product * w. (note: w is the ratio of quaternionB.)

This is correct In theory. However, its floating-point number calculation may produce some imprecise values.
For example, in this wpt:

test_interpolation({
  property: 'transform',
  from: 'matrix3d(0.571428571428571, -0.625, -0.8333333333333346, -0.66666666666669, 0.5, -0.1875, -0.8125, 0.3125, 0.34375, -1, 0.8333333333333327, 1.34375, -1.34375, 1, -0.9375, 1)',
  to: 'none'
}, [
  ...
  {at: 1, expect: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)'}, // note: this will be serialized as matrix(1, 0, 0, 1, 0, 0)
  ...
]);

At 100%, the interpolated value should be serialized as matrix(1, 0, 0, 1, 0, 0). However, in Gecko, when applying the slerp algorithm, the ratio of quaternionA is not equal to 0 exactly. It's something like -2.220446049250313e-16, which is equal to zero approximately. So the recomposed matrix is:

matrix3d(
  1.0, 9.355591e-17, 2.0598274e-16, 0.0,
  -9.355591e-17, 1.0, 8.233775e-17, 0.0,
  -2.0598274e-16, -8.233775e-17, 1.0, 0.0,
  0.0, 0.0, 0.0, 1.0
)

Therefore, this test is failed in Gecko because the interpolated result can not be converted into a 2d matrix, though these components are equal to zero approximately with some tolerances.

However, I notice Blink and WebKit use another formula.
Blink:

double half_angle = std::acos(cos_half_angle);
double scaleA = std::sin((1 - t) * half_angle) / sin_half_angle; // The ratio of quaternionA
double scaleB = std::sin(t * half_angle) / sin_half_angle;
return (scaleA * from) + (scaleB * to);

WebKit:

double halfAngle = std::acos(cosHalfAngle);
double scale = std::sin((1 - t) * halfAngle) / sinHalfAngle; // The ratio of quaternionA
double invscale = std::sin(t * halfAngle) / sinHalfAngle;
return { copy.x * scale + other.x * invscale, copy.y * scale + other.y * invscale, copy.z * scale + other.z * invscale, copy.w * scale + other.w * invscale };

Obviously, the ratio of quaternionA in Blink and WebKit is:

sin((1 - t) * halfAngle) / sin(halfAngle)

This formula is from https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/index.htm

After I applied this formula in Gecko, and the result of the ratio of quaternionA become 0 exactly in this test case, obviously. So the recomposed matrix can be serialized as 2d matrix successfully (i.e. there is no number approaching zero. They are exactly zeros.)

So perhaps we should update the spec to use this formula to avoid potential different behaviors among browsers and potential imprecision issues from floating-point number calculation?

Updated: No browser use the formula in the spec now.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions