Bug 1272549 - Part 3: Implement rotate3d with different direction axis.
Use quaternion vectors to calculate the distance of two rotate3d
functions.
MozReview-Commit-ID: LizbvRqzcuh
--- a/gfx/thebes/gfxQuaternion.h
+++ b/gfx/thebes/gfxQuaternion.h
@@ -26,16 +26,38 @@ struct gfxQuaternion : public mozilla::g
if(aMatrix[2][1] > aMatrix[1][2])
x = -x;
if(aMatrix[0][2] > aMatrix[2][0])
y = -y;
if(aMatrix[1][0] > aMatrix[0][1])
z = -z;
}
+ // Convert from |direction axis, angle| pair to gfxQuaternion.
+ //
+ // Reference:
+ // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
+ //
+ // if the direction axis is (x, y, z) = xi + yj + zk,
+ // and the angle is |theta|, this formula can be done using
+ // an extension of Euler's formula:
+ // q = cos(theta/2) + (xi + yj + zk)(sin(theta/2))
+ // = cos(theta/2) +
+ // x*sin(theta/2)i + y*sin(theta/2)j + z*sin(theta/2)k
+ // Note: aDirection should be an unit vector and
+ // the unit of aAngle should be Radian.
+ gfxQuaternion(const mozilla::gfx::Point3D &aDirection, gfxFloat aAngle) {
+ MOZ_ASSERT(mozilla::gfx::FuzzyEqual(aDirection.Length(), 1.0f),
+ "aDirection should be an unit vector");
+ x = aDirection.x * sin(aAngle/2.0);
+ y = aDirection.y * sin(aAngle/2.0);
+ z = aDirection.z * sin(aAngle/2.0);
+ w = cos(aAngle/2.0);
+ }
+
gfxQuaternion Slerp(const gfxQuaternion &aOther, gfxFloat aCoeff) {
gfxFloat dot = mozilla::clamped(DotProduct(aOther), -1.0, 1.0);
if (dot == 1.0) {
return *this;
}
gfxFloat theta = acos(dot);
gfxFloat rsintheta = 1/sqrt(1 - dot*dot);
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -810,18 +810,23 @@ ComputeTransformDistance(nsCSSValue::Arr
if (vector1 == vector2) {
// Handle rotate3d with matched (normalized) vectors.
nsCSSValue angle;
AddCSSValueAngle(1.0, a2->Item(4), -1.0, a1->Item(4), angle);
distance = angle.GetAngleValueInRadians() *
angle.GetAngleValueInRadians();
} else {
- // TODO: Convert to quaternion vectors and calculate the angle.
- distance = 0.0;
+ // Use quaternion vectors to get the angle difference. Both q1 and q2
+ // are unit vectors, so we can get their angle difference by
+ // cos(theta/2) = (q1 dot q2) / (|q1| * |q2|) = q1 dot q2.
+ gfxQuaternion q1(vector1, a1->Item(4).GetAngleValueInRadians());
+ gfxQuaternion q2(vector2, a2->Item(4).GetAngleValueInRadians());
+ distance = 2.0 * acos(clamped(q1.DotProduct(q2), -1.0, 1.0));
+ distance = distance * distance;
}
break;
}
case eCSSKeyword_perspective:
// TODO: This will be fixed in the later patch.
distance = 0.0;
break;
case eCSSKeyword_matrix: