Skip to content

[css-transforms-2] The way to determine the equality of two normalized direction vectors of rotate3d() #9339

Open
@BorisChiou

Description

@BorisChiou

When we do interpolation on two rotate3d functions, the spec in [css-transforms-2] says:

For interpolations with the primitive rotate3d, the direction vectors of the transform functions get normalized first. If the normalized vectors are not equal and both rotation angles are non-zero the transform functions get converted into 4x4 matrices first and interpolated as defined in section Interpolation of Matrices afterwards.

We normalize the direction vectors first, and check if they are equal. However, we are trying to compare two vectors with floating-point numbers, so I'd like to know should they be equal exactly, or within a suitable tolerance?

It seems different browsers use different ways:

In WebKit, it just checks the equality directly.:

fromNormalizedVector == toNormalizedVector

In Blink, it uses the formula, A.B = |A||B|cosθ, and compute the θ, the angle between two vectors, within a epsilon, 1e-4:

double dot = gfx::DotProduct(a.axis, b.axis);
if (dot < 0)
  return false;

double a_squared = a.axis.LengthSquared();
double b_squared = b.axis.LengthSquared();
double error = std::abs(1 - (dot * dot) / (a_squared * b_squared));
if (error > kAngleEpsilon)
  return false;
}

In Gecko, it checks the equality but with a machine epsilon:

fv.approx_eq(&tv)

I noticed this when fixing this test in Gecko:

test_composition({
  property: 'rotate',
  underlying: '1 2 3 40deg',
  addFrom: '2 4 6 10deg',
  addTo: '3 6 9 50deg',
  comparisonFunction: compareRotations
}, [
  {at: -1, expect: '0.27 0.53 0.8 10deg'},
  {at: 0, expect: '0.27 0.53 0.8 50deg'},
  {at: 0.25, expect: '0.27 0.53 0.8 60deg'},
  {at: 0.75, expect: '0.27 0.53 0.8 80deg'},
  {at: 1, expect: '0.27 0.53 0.8 90deg'},
  {at: 2, expect: '0.27 0.53 0.8 130deg'},
]);

In this test, the axis (1, 2, 3) is normalized as (0.26726124, 0.5345225, 0.8017837). However, the axis (3, 6, 9) is normalized as (0.26726124, 0.5345225, 0.80178374). The 3rd component is different, 0.8017837 vs 0.80178374, so Gecko failed this test. This is definitely a potential issue because we are comparing the normalized vectors. It seems Blink uses a smarter way and has a tolerance on the angle. However, Gecko uses a machine epsilon on each vector components.

These different implementation might have behavior-difference in the future. Should we define which way we should use to determine the equality between two normalized direction vectors? Or it doesn't matter?

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