Re: [css-transforms-1] SLERP edge case behaviour

On Wed, Jul 8, 2015 at 12:21 PM, Tab Atkins Jr. <jackalmage@gmail.com> wrote:
> On Mon, Jul 6, 2015 at 8:31 PM, Shane Stephens <shans@google.com> wrote:
>> Hi list,
>>
>> Here's how we currently define quaternion SLERPing during interpolation:
>> http://dev.w3.org/csswg/css-transforms/#interpolation-of-decomposed-3d-matrix-values
>>
>> When quatA = [0, 1, 0, 0] and quatB = [0, -1, 0, 0], this has the
>> unfortunate side effect of setting product to -1, which means the definition
>> of 'w' has 0 in the denominator.
>>
>> What do?
>
> Something analogous to the early exit when product is 1, I would
> assume. I don't know how math works, so I'm not sure what the right
> answer is.

This is another example where the "shortest path" rotation[1] would
fix things. Since [0, 1, 0, 0] and [0, -1, 0, 0] represent the same
orientation/rotation, the shortest path is just an identity transform.

Adding an early exit like when the product is 1 will add a
discontinuity under the current interpolation algorithm, because two
quaternions that are *almost* antipodal will do a full lap around
before coming back to nearly the same orientation.

It will have to be special-cased regardless, though, as the slerp path
between quaternions is not unique iff their dot product has an
absolute value of 1. The "correct" long path rotation would be to pick
an arbitrary (quaternion) axis to rotate around, but if that level of
edit needs to occur, it's the perfect chance to add the much simpler
always-take-the-shortest-path change instead!

> (By the way, the clamping code is exactly wrong. It should use min()
> for the upper bound and max() for the lower bound; as written, it
> ensures that product is *outside* the (-1,1) range.)

There are a few odd things in the pseudo code:

- Why is the clamping included at all? They have to be unit
quaternions, so the clamping is just an implementation detail to make
sure numerical issues don't take the dot product outside the domain of
acos and sqrt

- `theta = acos(dot)` should be `theta = acos(product)`

- the expression multiplied with quaternionA's components can also be
pulled out of the loop like `w`, which would make it a bit clearer:
  for (i = 0; i < 4; i++)
    quaternionDst[i] = w1 * quaternionA[i] + w2 * quaternionB[i]

- the code doesn't take the shortest path between quaternions which
really would be way better :) Is there a reason discussion of this
fell off?

Brendan

[1] https://lists.w3.org/Archives/Public/www-style/2013May/0131.html

Received on Thursday, 9 July 2015 02:22:33 UTC