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

BorisChiou has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-transforms-2] The way to determine the equality of two normalized direction vectors of rotate3d() ==
When we do interpolation on two rotate3d functions, the [spec in [css-transforms-2]](https://drafts.csswg.org/css-transforms-2/#interpolation-of-transform-functions) 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](https://drafts.csswg.org/css-transforms-2/#matrix-interpolation) 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](https://github.com/WebKit/WebKit/blob/30884546903f1ba774adb0cbef1adc91c6c53c64/Source/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp#L88), it just checks the equality directly.:
```
fromNormalizedVector == toNormalizedVector
```

In [Blink](https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/transforms/rotation.cc;l=98-106;drc=6f4c64436342c818aa41e6a5c55034e74ec9c6b6), 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](https://searchfox.org/mozilla-central/source/servo/components/style/values/animated/transform.rs#1438), it checks the equality but with a machine epsilon:
```
fv.approx_eq(&tv)
```

I noticed this when fixing this [test](https://searchfox.org/mozilla-central/source/testing/web-platform/tests/css/css-transforms/animation/rotate-composition.html#105-118) 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?

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/9339 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Monday, 11 September 2023 22:35:29 UTC