Bug 1245242 - Normalize to unit vector for DOMMatrix.rotateAxisAngleSelf. r=roc

This commit is contained in:
William Chen
2016-02-04 00:30:00 -08:00
parent aae5b9dda8
commit 72b3be9de2
4 changed files with 58 additions and 66 deletions

View File

@@ -552,22 +552,10 @@ DOMMatrix::RotateAxisAngleSelf(double aX, double aY,
}
aAngle *= radPerDegree;
// sin(aAngle / 2) * cos(aAngle / 2)
double sc = sin(aAngle) / 2;
// pow(sin(aAngle / 2), 2)
double sq = (1 - cos(aAngle)) / 2;
Ensure3DMatrix();
gfx::Matrix4x4 m;
m._11 = 1 - 2 * (aY * aY + aZ * aZ) * sq;
m._12 = 2 * (aX * aY * sq + aZ * sc);
m._13 = 2 * (aX * aZ * sq - aY * sc);
m._21 = 2 * (aX * aY * sq - aZ * sc);
m._22 = 1 - 2 * (aX * aX + aZ * aZ) * sq;
m._23 = 2 * (aY * aZ * sq + aX * sc);
m._31 = 2 * (aX * aZ * sq + aY * sc);
m._32 = 2 * (aY * aZ * sq - aX * sc);
m._33 = 1 - 2 * (aX * aX + aY * aY) * sq;
m.SetRotateAxisAngle(aX, aY, aZ, aAngle);
*mMatrix3D = m * *mMatrix3D;

View File

@@ -323,4 +323,14 @@ test(function() {
assert_throws("SyntaxError", function() { new WebKitCSSMatrix("initial"); }, "initial is not a valid constructor argument.")
assert_throws("SyntaxError", function() { new WebKitCSSMatrix("inherit"); }, "inherit is not a valid constructor arugment.")
}, "Test invalid constructor arguments.");
test(function() {
var m1 = new WebKitCSSMatrix();
m1 = m1.rotateAxisAngle(0, 0, 1, 45);
var m2 = new WebKitCSSMatrix();
m2 = m2.rotateAxisAngle(0, 0, 3, 45);
assert_true(RoughCompareMatrix(m1, m2), "rotateAxisAngle should normalize vector to unit vector.");
}, "Test normalization of vector for rotateAxisAngle");
</script>

View File

@@ -1433,6 +1433,48 @@ public:
_24 = -sinTheta * temp + cosTheta * _24;
}
// Sets this matrix to a rotation matrix about a
// vector [x,y,z] by angle theta. The vector is normalized
// to a unit vector.
// https://www.w3.org/TR/css3-3d-transforms/#Rotate3dDefined
void SetRotateAxisAngle(double aX, double aY, double aZ, double aTheta)
{
Point3D vector(aX, aY, aZ);
if (!vector.Length()) {
return;
}
vector.Normalize();
double x = vector.x;
double y = vector.y;
double z = vector.z;
double cosTheta = FlushToZero(cos(aTheta));
double sinTheta = FlushToZero(sin(aTheta));
// sin(aTheta / 2) * cos(aTheta / 2)
double sc = sinTheta / 2;
// pow(sin(aTheta / 2), 2)
double sq = (1 - cosTheta) / 2;
_11 = 1 - 2 * (y * y + z * z) * sq;
_12 = 2 * (x * y * sq + z * sc);
_13 = 2 * (x * z * sq - y * sc);
_14 = 0.0f;
_21 = 2 * (x * y * sq - z * sc);
_22 = 1 - 2 * (x * x + z * z) * sq;
_23 = 2 * (y * z * sq + x * sc);
_24 = 0.0f;
_31 = 2 * (x * z * sq + y * sc);
_32 = 2 * (y * z * sq - x * sc);
_33 = 1 - 2 * (x * x + y * y) * sq;
_34 = 0.0f;
_41 = 0.0f;
_42 = 0.0f;
_43 = 0.0f;
_44 = 1.0f;
}
void Perspective(float aDepth)
{
MOZ_ASSERT(aDepth > 0.0f, "Perspective must be positive!");

View File

@@ -134,17 +134,6 @@ TransformReferenceBox::Init(const nsSize& aDimensions)
mIsCached = true;
}
/* Force small values to zero. We do this to avoid having sin(360deg)
* evaluate to a tiny but nonzero value.
*/
static double FlushToZero(double aVal)
{
if (-FLT_EPSILON < aVal && aVal < FLT_EPSILON)
return 0.0f;
else
return aVal;
}
float
ProcessTranslatePart(const nsCSSValue& aValue,
nsStyleContext* aContext,
@@ -543,50 +532,13 @@ ProcessRotate3D(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
{
NS_PRECONDITION(aData->Count() == 5, "Invalid array!");
/* We want our matrix to look like this:
* | 1 + (1-cos(angle))*(x*x-1) -z*sin(angle)+(1-cos(angle))*x*y y*sin(angle)+(1-cos(angle))*x*z 0 |
* | z*sin(angle)+(1-cos(angle))*x*y 1 + (1-cos(angle))*(y*y-1) -x*sin(angle)+(1-cos(angle))*y*z 0 |
* | -y*sin(angle)+(1-cos(angle))*x*z x*sin(angle)+(1-cos(angle))*y*z 1 + (1-cos(angle))*(z*z-1) 0 |
* | 0 0 0 1 |
* (see http://www.w3.org/TR/css3-3d-transforms/#transform-functions)
*/
/* The current spec specifies a matrix that rotates in the wrong direction. For now we just negate
* the angle provided to get the correct rotation direction until the spec is updated.
* See bug 704468.
*/
double theta = -aData->Item(4).GetAngleValueInRadians();
float cosTheta = FlushToZero(cos(theta));
float sinTheta = FlushToZero(sin(theta));
Point3D vector(aData->Item(1).GetFloatValue(),
aData->Item(2).GetFloatValue(),
aData->Item(3).GetFloatValue());
if (!vector.Length()) {
return;
}
vector.Normalize();
double theta = aData->Item(4).GetAngleValueInRadians();
float x = aData->Item(1).GetFloatValue();
float y = aData->Item(2).GetFloatValue();
float z = aData->Item(3).GetFloatValue();
Matrix4x4 temp;
/* Create our matrix */
temp._11 = 1 + (1 - cosTheta) * (vector.x * vector.x - 1);
temp._12 = -vector.z * sinTheta + (1 - cosTheta) * vector.x * vector.y;
temp._13 = vector.y * sinTheta + (1 - cosTheta) * vector.x * vector.z;
temp._14 = 0.0f;
temp._21 = vector.z * sinTheta + (1 - cosTheta) * vector.x * vector.y;
temp._22 = 1 + (1 - cosTheta) * (vector.y * vector.y - 1);
temp._23 = -vector.x * sinTheta + (1 - cosTheta) * vector.y * vector.z;
temp._24 = 0.0f;
temp._31 = -vector.y * sinTheta + (1 - cosTheta) * vector.x * vector.z;
temp._32 = vector.x * sinTheta + (1 - cosTheta) * vector.y * vector.z;
temp._33 = 1 + (1 - cosTheta) * (vector.z * vector.z - 1);
temp._34 = 0.0f;
temp._41 = 0.0f;
temp._42 = 0.0f;
temp._43 = 0.0f;
temp._44 = 1.0f;
temp.SetRotateAxisAngle(x, y, z, theta);
aMatrix = temp * aMatrix;
}