TransformationMatrix.cpp [plain text]
#include "config.h"
#include "TransformationMatrix.h"
#include "FloatPoint3D.h"
#include "FloatRect.h"
#include "FloatQuad.h"
#include "IntRect.h"
#include <wtf/Assertions.h>
#include <wtf/MathExtras.h>
namespace WebCore {
typedef double Vector4[4];
typedef double Vector3[3];
const double SMALL_NUMBER = 1.e-8;
static double determinant2x2(double a, double b, double c, double d)
{
return a * d - b * c;
}
static double determinant3x3(double a1, double a2, double a3, double b1, double b2, double b3, double c1, double c2, double c3)
{
return a1 * determinant2x2(b2, b3, c2, c3)
- b1 * determinant2x2(a2, a3, c2, c3)
+ c1 * determinant2x2(a2, a3, b2, b3);
}
static double determinant4x4(const TransformationMatrix::Matrix4& m)
{
double a1 = m[0][0];
double b1 = m[0][1];
double c1 = m[0][2];
double d1 = m[0][3];
double a2 = m[1][0];
double b2 = m[1][1];
double c2 = m[1][2];
double d2 = m[1][3];
double a3 = m[2][0];
double b3 = m[2][1];
double c3 = m[2][2];
double d3 = m[2][3];
double a4 = m[3][0];
double b4 = m[3][1];
double c4 = m[3][2];
double d4 = m[3][3];
return a1 * determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4)
- b1 * determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4)
+ c1 * determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4)
- d1 * determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
}
static void adjoint(const TransformationMatrix::Matrix4& matrix, TransformationMatrix::Matrix4& result)
{
double a1 = matrix[0][0];
double b1 = matrix[0][1];
double c1 = matrix[0][2];
double d1 = matrix[0][3];
double a2 = matrix[1][0];
double b2 = matrix[1][1];
double c2 = matrix[1][2];
double d2 = matrix[1][3];
double a3 = matrix[2][0];
double b3 = matrix[2][1];
double c3 = matrix[2][2];
double d3 = matrix[2][3];
double a4 = matrix[3][0];
double b4 = matrix[3][1];
double c4 = matrix[3][2];
double d4 = matrix[3][3];
result[0][0] = determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4);
result[1][0] = - determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4);
result[2][0] = determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4);
result[3][0] = - determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
result[0][1] = - determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4);
result[1][1] = determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4);
result[2][1] = - determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4);
result[3][1] = determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4);
result[0][2] = determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4);
result[1][2] = - determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4);
result[2][2] = determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4);
result[3][2] = - determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4);
result[0][3] = - determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3);
result[1][3] = determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3);
result[2][3] = - determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3);
result[3][3] = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3);
}
static bool inverse(const TransformationMatrix::Matrix4& matrix, TransformationMatrix::Matrix4& result)
{
adjoint(matrix, result);
double det = determinant4x4(matrix);
if (fabs(det) < SMALL_NUMBER)
return false;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
result[i][j] = result[i][j] / det;
return true;
}
static void transposeMatrix4(const TransformationMatrix::Matrix4& a, TransformationMatrix::Matrix4& b)
{
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
b[i][j] = a[j][i];
}
static void v4MulPointByMatrix(const Vector4 p, const TransformationMatrix::Matrix4& m, Vector4 result)
{
result[0] = (p[0] * m[0][0]) + (p[1] * m[1][0]) +
(p[2] * m[2][0]) + (p[3] * m[3][0]);
result[1] = (p[0] * m[0][1]) + (p[1] * m[1][1]) +
(p[2] * m[2][1]) + (p[3] * m[3][1]);
result[2] = (p[0] * m[0][2]) + (p[1] * m[1][2]) +
(p[2] * m[2][2]) + (p[3] * m[3][2]);
result[3] = (p[0] * m[0][3]) + (p[1] * m[1][3]) +
(p[2] * m[2][3]) + (p[3] * m[3][3]);
}
static double v3Length(Vector3 a)
{
return sqrt((a[0] * a[0]) + (a[1] * a[1]) + (a[2] * a[2]));
}
static void v3Scale(Vector3 v, double desiredLength)
{
double len = v3Length(v);
if (len != 0) {
double l = desiredLength / len;
v[0] *= l;
v[1] *= l;
v[2] *= l;
}
}
static double v3Dot(const Vector3 a, const Vector3 b)
{
return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]);
}
static void v3Combine(const Vector3 a, const Vector3 b, Vector3 result, double ascl, double bscl)
{
result[0] = (ascl * a[0]) + (bscl * b[0]);
result[1] = (ascl * a[1]) + (bscl * b[1]);
result[2] = (ascl * a[2]) + (bscl * b[2]);
}
static void v3Cross(const Vector3 a, const Vector3 b, Vector3 result)
{
result[0] = (a[1] * b[2]) - (a[2] * b[1]);
result[1] = (a[2] * b[0]) - (a[0] * b[2]);
result[2] = (a[0] * b[1]) - (a[1] * b[0]);
}
static bool decompose(const TransformationMatrix::Matrix4& mat, TransformationMatrix::DecomposedType& result)
{
TransformationMatrix::Matrix4 localMatrix;
memcpy(localMatrix, mat, sizeof(TransformationMatrix::Matrix4));
if (localMatrix[3][3] == 0)
return false;
int i, j;
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
localMatrix[i][j] /= localMatrix[3][3];
TransformationMatrix::Matrix4 perspectiveMatrix;
memcpy(perspectiveMatrix, localMatrix, sizeof(TransformationMatrix::Matrix4));
for (i = 0; i < 3; i++)
perspectiveMatrix[i][3] = 0;
perspectiveMatrix[3][3] = 1;
if (determinant4x4(perspectiveMatrix) == 0)
return false;
if (localMatrix[0][3] != 0 || localMatrix[1][3] != 0 || localMatrix[2][3] != 0) {
Vector4 rightHandSide;
rightHandSide[0] = localMatrix[0][3];
rightHandSide[1] = localMatrix[1][3];
rightHandSide[2] = localMatrix[2][3];
rightHandSide[3] = localMatrix[3][3];
TransformationMatrix::Matrix4 inversePerspectiveMatrix, transposedInversePerspectiveMatrix;
inverse(perspectiveMatrix, inversePerspectiveMatrix);
transposeMatrix4(inversePerspectiveMatrix, transposedInversePerspectiveMatrix);
Vector4 perspectivePoint;
v4MulPointByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspectivePoint);
result.perspectiveX = perspectivePoint[0];
result.perspectiveY = perspectivePoint[1];
result.perspectiveZ = perspectivePoint[2];
result.perspectiveW = perspectivePoint[3];
localMatrix[0][3] = localMatrix[1][3] = localMatrix[2][3] = 0;
localMatrix[3][3] = 1;
} else {
result.perspectiveX = result.perspectiveY = result.perspectiveZ = 0;
result.perspectiveW = 1;
}
result.translateX = localMatrix[3][0];
localMatrix[3][0] = 0;
result.translateY = localMatrix[3][1];
localMatrix[3][1] = 0;
result.translateZ = localMatrix[3][2];
localMatrix[3][2] = 0;
Vector3 row[3], pdum3;
for (i = 0; i < 3; i++) {
row[i][0] = localMatrix[i][0];
row[i][1] = localMatrix[i][1];
row[i][2] = localMatrix[i][2];
}
result.scaleX = v3Length(row[0]);
v3Scale(row[0], 1.0);
result.skewXY = v3Dot(row[0], row[1]);
v3Combine(row[1], row[0], row[1], 1.0, -result.skewXY);
result.scaleY = v3Length(row[1]);
v3Scale(row[1], 1.0);
result.skewXY /= result.scaleY;
result.skewXZ = v3Dot(row[0], row[2]);
v3Combine(row[2], row[0], row[2], 1.0, -result.skewXZ);
result.skewYZ = v3Dot(row[1], row[2]);
v3Combine(row[2], row[1], row[2], 1.0, -result.skewYZ);
result.scaleZ = v3Length(row[2]);
v3Scale(row[2], 1.0);
result.skewXZ /= result.scaleZ;
result.skewYZ /= result.scaleZ;
v3Cross(row[1], row[2], pdum3);
if (v3Dot(row[0], pdum3) < 0) {
for (i = 0; i < 3; i++) {
result.scaleX *= -1;
row[i][0] *= -1;
row[i][1] *= -1;
row[i][2] *= -1;
}
}
double s, t, x, y, z, w;
t = row[0][0] + row[1][1] + row[2][2] + 1.0;
if (t > 1e-4) {
s = 0.5 / sqrt(t);
w = 0.25 / s;
x = (row[2][1] - row[1][2]) * s;
y = (row[0][2] - row[2][0]) * s;
z = (row[1][0] - row[0][1]) * s;
} else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) {
s = sqrt (1.0 + row[0][0] - row[1][1] - row[2][2]) * 2.0; x = 0.25 * s;
y = (row[0][1] + row[1][0]) / s;
z = (row[0][2] + row[2][0]) / s;
w = (row[2][1] - row[1][2]) / s;
} else if (row[1][1] > row[2][2]) {
s = sqrt (1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0; x = (row[0][1] + row[1][0]) / s;
y = 0.25 * s;
z = (row[1][2] + row[2][1]) / s;
w = (row[0][2] - row[2][0]) / s;
} else {
s = sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0; x = (row[0][2] + row[2][0]) / s;
y = (row[1][2] + row[2][1]) / s;
z = 0.25 * s;
w = (row[1][0] - row[0][1]) / s;
}
result.quaternionX = x;
result.quaternionY = y;
result.quaternionZ = z;
result.quaternionW = w;
return true;
}
static void slerp(double qa[4], const double qb[4], double t)
{
double ax, ay, az, aw;
double bx, by, bz, bw;
double cx, cy, cz, cw;
double angle;
double th, invth, scale, invscale;
ax = qa[0]; ay = qa[1]; az = qa[2]; aw = qa[3];
bx = qb[0]; by = qb[1]; bz = qb[2]; bw = qb[3];
angle = ax * bx + ay * by + az * bz + aw * bw;
if (angle < 0.0) {
ax = -ax; ay = -ay;
az = -az; aw = -aw;
angle = -angle;
}
if (angle + 1.0 > .05) {
if (1.0 - angle >= .05) {
th = acos (angle);
invth = 1.0 / sin (th);
scale = sin (th * (1.0 - t)) * invth;
invscale = sin (th * t) * invth;
} else {
scale = 1.0 - t;
invscale = t;
}
} else {
bx = -ay;
by = ax;
bz = -aw;
bw = az;
scale = sin(piDouble * (.5 - t));
invscale = sin (piDouble * t);
}
cx = ax * scale + bx * invscale;
cy = ay * scale + by * invscale;
cz = az * scale + bz * invscale;
cw = aw * scale + bw * invscale;
qa[0] = cx; qa[1] = cy; qa[2] = cz; qa[3] = cw;
}
TransformationMatrix& TransformationMatrix::scale(double s)
{
return scaleNonUniform(s, s);
}
TransformationMatrix& TransformationMatrix::rotateFromVector(double x, double y)
{
return rotate(rad2deg(atan2(y, x)));
}
TransformationMatrix& TransformationMatrix::flipX()
{
return scaleNonUniform(-1.0f, 1.0f);
}
TransformationMatrix& TransformationMatrix::flipY()
{
return scaleNonUniform(1.0f, -1.0f);
}
FloatPoint TransformationMatrix::projectPoint(const FloatPoint& p) const
{
double x = p.x();
double y = p.y();
double z = -(m13() * x + m23() * y + m43()) / m33();
double outX = x * m11() + y * m21() + z * m31() + m41();
double outY = x * m12() + y * m22() + z * m32() + m42();
double w = x * m14() + y * m24() + z * m34() + m44();
if (w != 1 && w != 0) {
outX /= w;
outY /= w;
}
return FloatPoint(static_cast<float>(outX), static_cast<float>(outY));
}
FloatQuad TransformationMatrix::projectQuad(const FloatQuad& q) const
{
FloatQuad projectedQuad;
projectedQuad.setP1(projectPoint(q.p1()));
projectedQuad.setP2(projectPoint(q.p2()));
projectedQuad.setP3(projectPoint(q.p3()));
projectedQuad.setP4(projectPoint(q.p4()));
return projectedQuad;
}
FloatPoint TransformationMatrix::mapPoint(const FloatPoint& p) const
{
if (isIdentityOrTranslation())
return FloatPoint(p.x() + static_cast<float>(m_matrix[3][0]), p.y() + static_cast<float>(m_matrix[3][1]));
double x, y;
multVecMatrix(p.x(), p.y(), x, y);
return FloatPoint(static_cast<float>(x), static_cast<float>(y));
}
FloatPoint3D TransformationMatrix::mapPoint(const FloatPoint3D& p) const
{
if (isIdentityOrTranslation())
return FloatPoint3D(p.x() + static_cast<float>(m_matrix[3][0]),
p.y() + static_cast<float>(m_matrix[3][1]),
p.z() + static_cast<float>(m_matrix[3][2]));
double x, y, z;
multVecMatrix(p.x(), p.y(), p.z(), x, y, z);
return FloatPoint3D(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z));
}
IntRect TransformationMatrix::mapRect(const IntRect &rect) const
{
return enclosingIntRect(mapRect(FloatRect(rect)));
}
FloatRect TransformationMatrix::mapRect(const FloatRect& r) const
{
if (isIdentityOrTranslation()) {
FloatRect mappedRect(r);
mappedRect.move(static_cast<float>(m_matrix[3][0]), static_cast<float>(m_matrix[3][1]));
return mappedRect;
}
FloatQuad resultQuad = mapQuad(FloatQuad(r));
return resultQuad.boundingBox();
}
FloatQuad TransformationMatrix::mapQuad(const FloatQuad& q) const
{
if (isIdentityOrTranslation()) {
FloatQuad mappedQuad(q);
mappedQuad.move(static_cast<float>(m_matrix[3][0]), static_cast<float>(m_matrix[3][1]));
return mappedQuad;
}
FloatQuad result;
result.setP1(mapPoint(q.p1()));
result.setP2(mapPoint(q.p2()));
result.setP3(mapPoint(q.p3()));
result.setP4(mapPoint(q.p4()));
return result;
}
TransformationMatrix& TransformationMatrix::scaleNonUniform(double sx, double sy)
{
TransformationMatrix mat;
mat.m_matrix[0][0] = sx;
mat.m_matrix[1][1] = sy;
multLeft(mat);
return *this;
}
TransformationMatrix& TransformationMatrix::scale3d(double sx, double sy, double sz)
{
TransformationMatrix mat;
mat.m_matrix[0][0] = sx;
mat.m_matrix[1][1] = sy;
mat.m_matrix[2][2] = sz;
multLeft(mat);
return *this;
}
TransformationMatrix& TransformationMatrix::rotate3d(double x, double y, double z, double angle)
{
angle = deg2rad(angle);
angle /= 2.0f;
double sinA = sin(angle);
double cosA = cos(angle);
double sinA2 = sinA * sinA;
double length = sqrt(x * x + y * y + z * z);
if (length == 0) {
x = 0;
y = 0;
z = 1;
} else if (length != 1) {
x /= length;
y /= length;
z /= length;
}
TransformationMatrix mat;
if (x == 1.0f && y == 0.0f && z == 0.0f) {
mat.m_matrix[0][0] = 1.0f;
mat.m_matrix[0][1] = 0.0f;
mat.m_matrix[0][2] = 0.0f;
mat.m_matrix[1][0] = 0.0f;
mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2;
mat.m_matrix[1][2] = 2.0f * sinA * cosA;
mat.m_matrix[2][0] = 0.0f;
mat.m_matrix[2][1] = -2.0f * sinA * cosA;
mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2;
mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f;
mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f;
mat.m_matrix[3][3] = 1.0f;
} else if (x == 0.0f && y == 1.0f && z == 0.0f) {
mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2;
mat.m_matrix[0][1] = 0.0f;
mat.m_matrix[0][2] = -2.0f * sinA * cosA;
mat.m_matrix[1][0] = 0.0f;
mat.m_matrix[1][1] = 1.0f;
mat.m_matrix[1][2] = 0.0f;
mat.m_matrix[2][0] = 2.0f * sinA * cosA;
mat.m_matrix[2][1] = 0.0f;
mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2;
mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f;
mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f;
mat.m_matrix[3][3] = 1.0f;
} else if (x == 0.0f && y == 0.0f && z == 1.0f) {
mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2;
mat.m_matrix[0][1] = 2.0f * sinA * cosA;
mat.m_matrix[0][2] = 0.0f;
mat.m_matrix[1][0] = -2.0f * sinA * cosA;
mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2;
mat.m_matrix[1][2] = 0.0f;
mat.m_matrix[2][0] = 0.0f;
mat.m_matrix[2][1] = 0.0f;
mat.m_matrix[2][2] = 1.0f;
mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f;
mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f;
mat.m_matrix[3][3] = 1.0f;
} else {
double x2 = x*x;
double y2 = y*y;
double z2 = z*z;
mat.m_matrix[0][0] = 1.0f - 2.0f * (y2 + z2) * sinA2;
mat.m_matrix[0][1] = 2.0f * (x * y * sinA2 + z * sinA * cosA);
mat.m_matrix[0][2] = 2.0f * (x * z * sinA2 - y * sinA * cosA);
mat.m_matrix[1][0] = 2.0f * (y * x * sinA2 - z * sinA * cosA);
mat.m_matrix[1][1] = 1.0f - 2.0f * (z2 + x2) * sinA2;
mat.m_matrix[1][2] = 2.0f * (y * z * sinA2 + x * sinA * cosA);
mat.m_matrix[2][0] = 2.0f * (z * x * sinA2 + y * sinA * cosA);
mat.m_matrix[2][1] = 2.0f * (z * y * sinA2 - x * sinA * cosA);
mat.m_matrix[2][2] = 1.0f - 2.0f * (x2 + y2) * sinA2;
mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f;
mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f;
mat.m_matrix[3][3] = 1.0f;
}
multLeft(mat);
return *this;
}
TransformationMatrix& TransformationMatrix::rotate3d(double rx, double ry, double rz)
{
rx = deg2rad(rx);
ry = deg2rad(ry);
rz = deg2rad(rz);
TransformationMatrix mat;
rz /= 2.0f;
double sinA = sin(rz);
double cosA = cos(rz);
double sinA2 = sinA * sinA;
mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2;
mat.m_matrix[0][1] = 2.0f * sinA * cosA;
mat.m_matrix[0][2] = 0.0f;
mat.m_matrix[1][0] = -2.0f * sinA * cosA;
mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2;
mat.m_matrix[1][2] = 0.0f;
mat.m_matrix[2][0] = 0.0f;
mat.m_matrix[2][1] = 0.0f;
mat.m_matrix[2][2] = 1.0f;
mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f;
mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f;
mat.m_matrix[3][3] = 1.0f;
TransformationMatrix rmat(mat);
ry /= 2.0f;
sinA = sin(ry);
cosA = cos(ry);
sinA2 = sinA * sinA;
mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2;
mat.m_matrix[0][1] = 0.0f;
mat.m_matrix[0][2] = -2.0f * sinA * cosA;
mat.m_matrix[1][0] = 0.0f;
mat.m_matrix[1][1] = 1.0f;
mat.m_matrix[1][2] = 0.0f;
mat.m_matrix[2][0] = 2.0f * sinA * cosA;
mat.m_matrix[2][1] = 0.0f;
mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2;
mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f;
mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f;
mat.m_matrix[3][3] = 1.0f;
rmat.multLeft(mat);
rx /= 2.0f;
sinA = sin(rx);
cosA = cos(rx);
sinA2 = sinA * sinA;
mat.m_matrix[0][0] = 1.0f;
mat.m_matrix[0][1] = 0.0f;
mat.m_matrix[0][2] = 0.0f;
mat.m_matrix[1][0] = 0.0f;
mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2;
mat.m_matrix[1][2] = 2.0f * sinA * cosA;
mat.m_matrix[2][0] = 0.0f;
mat.m_matrix[2][1] = -2.0f * sinA * cosA;
mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2;
mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f;
mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f;
mat.m_matrix[3][3] = 1.0f;
rmat.multLeft(mat);
multLeft(rmat);
return *this;
}
TransformationMatrix& TransformationMatrix::translate(double tx, double ty)
{
m_matrix[3][0] += tx * m_matrix[0][0] + ty * m_matrix[1][0];
m_matrix[3][1] += tx * m_matrix[0][1] + ty * m_matrix[1][1];
m_matrix[3][2] += tx * m_matrix[0][2] + ty * m_matrix[1][2];
m_matrix[3][3] += tx * m_matrix[0][3] + ty * m_matrix[1][3];
return *this;
}
TransformationMatrix& TransformationMatrix::translate3d(double tx, double ty, double tz)
{
m_matrix[3][0] += tx * m_matrix[0][0] + ty * m_matrix[1][0] + tz * m_matrix[2][0];
m_matrix[3][1] += tx * m_matrix[0][1] + ty * m_matrix[1][1] + tz * m_matrix[2][1];
m_matrix[3][2] += tx * m_matrix[0][2] + ty * m_matrix[1][2] + tz * m_matrix[2][2];
m_matrix[3][3] += tx * m_matrix[0][3] + ty * m_matrix[1][3] + tz * m_matrix[2][3];
return *this;
}
TransformationMatrix& TransformationMatrix::translateRight(double tx, double ty)
{
if (tx != 0) {
m_matrix[0][0] += m_matrix[0][3] * tx;
m_matrix[1][0] += m_matrix[1][3] * tx;
m_matrix[2][0] += m_matrix[2][3] * tx;
m_matrix[3][0] += m_matrix[3][3] * tx;
}
if (ty != 0) {
m_matrix[0][1] += m_matrix[0][3] * ty;
m_matrix[1][1] += m_matrix[1][3] * ty;
m_matrix[2][1] += m_matrix[2][3] * ty;
m_matrix[3][1] += m_matrix[3][3] * ty;
}
return *this;
}
TransformationMatrix& TransformationMatrix::translateRight3d(double tx, double ty, double tz)
{
translateRight(tx, ty);
if (tz != 0) {
m_matrix[0][2] += m_matrix[0][3] * tz;
m_matrix[1][2] += m_matrix[1][3] * tz;
m_matrix[2][2] += m_matrix[2][3] * tz;
m_matrix[3][2] += m_matrix[3][3] * tz;
}
return *this;
}
TransformationMatrix& TransformationMatrix::skew(double sx, double sy)
{
sx = deg2rad(sx);
sy = deg2rad(sy);
TransformationMatrix mat;
mat.m_matrix[0][1] = tan(sy); mat.m_matrix[1][0] = tan(sx);
multLeft(mat);
return *this;
}
TransformationMatrix& TransformationMatrix::applyPerspective(double p)
{
TransformationMatrix mat;
if (p != 0)
mat.m_matrix[2][3] = -1/p;
multLeft(mat);
return *this;
}
TransformationMatrix& TransformationMatrix::multLeft(const TransformationMatrix& mat)
{
Matrix4 tmp;
tmp[0][0] = (mat.m_matrix[0][0] * m_matrix[0][0] + mat.m_matrix[0][1] * m_matrix[1][0]
+ mat.m_matrix[0][2] * m_matrix[2][0] + mat.m_matrix[0][3] * m_matrix[3][0]);
tmp[0][1] = (mat.m_matrix[0][0] * m_matrix[0][1] + mat.m_matrix[0][1] * m_matrix[1][1]
+ mat.m_matrix[0][2] * m_matrix[2][1] + mat.m_matrix[0][3] * m_matrix[3][1]);
tmp[0][2] = (mat.m_matrix[0][0] * m_matrix[0][2] + mat.m_matrix[0][1] * m_matrix[1][2]
+ mat.m_matrix[0][2] * m_matrix[2][2] + mat.m_matrix[0][3] * m_matrix[3][2]);
tmp[0][3] = (mat.m_matrix[0][0] * m_matrix[0][3] + mat.m_matrix[0][1] * m_matrix[1][3]
+ mat.m_matrix[0][2] * m_matrix[2][3] + mat.m_matrix[0][3] * m_matrix[3][3]);
tmp[1][0] = (mat.m_matrix[1][0] * m_matrix[0][0] + mat.m_matrix[1][1] * m_matrix[1][0]
+ mat.m_matrix[1][2] * m_matrix[2][0] + mat.m_matrix[1][3] * m_matrix[3][0]);
tmp[1][1] = (mat.m_matrix[1][0] * m_matrix[0][1] + mat.m_matrix[1][1] * m_matrix[1][1]
+ mat.m_matrix[1][2] * m_matrix[2][1] + mat.m_matrix[1][3] * m_matrix[3][1]);
tmp[1][2] = (mat.m_matrix[1][0] * m_matrix[0][2] + mat.m_matrix[1][1] * m_matrix[1][2]
+ mat.m_matrix[1][2] * m_matrix[2][2] + mat.m_matrix[1][3] * m_matrix[3][2]);
tmp[1][3] = (mat.m_matrix[1][0] * m_matrix[0][3] + mat.m_matrix[1][1] * m_matrix[1][3]
+ mat.m_matrix[1][2] * m_matrix[2][3] + mat.m_matrix[1][3] * m_matrix[3][3]);
tmp[2][0] = (mat.m_matrix[2][0] * m_matrix[0][0] + mat.m_matrix[2][1] * m_matrix[1][0]
+ mat.m_matrix[2][2] * m_matrix[2][0] + mat.m_matrix[2][3] * m_matrix[3][0]);
tmp[2][1] = (mat.m_matrix[2][0] * m_matrix[0][1] + mat.m_matrix[2][1] * m_matrix[1][1]
+ mat.m_matrix[2][2] * m_matrix[2][1] + mat.m_matrix[2][3] * m_matrix[3][1]);
tmp[2][2] = (mat.m_matrix[2][0] * m_matrix[0][2] + mat.m_matrix[2][1] * m_matrix[1][2]
+ mat.m_matrix[2][2] * m_matrix[2][2] + mat.m_matrix[2][3] * m_matrix[3][2]);
tmp[2][3] = (mat.m_matrix[2][0] * m_matrix[0][3] + mat.m_matrix[2][1] * m_matrix[1][3]
+ mat.m_matrix[2][2] * m_matrix[2][3] + mat.m_matrix[2][3] * m_matrix[3][3]);
tmp[3][0] = (mat.m_matrix[3][0] * m_matrix[0][0] + mat.m_matrix[3][1] * m_matrix[1][0]
+ mat.m_matrix[3][2] * m_matrix[2][0] + mat.m_matrix[3][3] * m_matrix[3][0]);
tmp[3][1] = (mat.m_matrix[3][0] * m_matrix[0][1] + mat.m_matrix[3][1] * m_matrix[1][1]
+ mat.m_matrix[3][2] * m_matrix[2][1] + mat.m_matrix[3][3] * m_matrix[3][1]);
tmp[3][2] = (mat.m_matrix[3][0] * m_matrix[0][2] + mat.m_matrix[3][1] * m_matrix[1][2]
+ mat.m_matrix[3][2] * m_matrix[2][2] + mat.m_matrix[3][3] * m_matrix[3][2]);
tmp[3][3] = (mat.m_matrix[3][0] * m_matrix[0][3] + mat.m_matrix[3][1] * m_matrix[1][3]
+ mat.m_matrix[3][2] * m_matrix[2][3] + mat.m_matrix[3][3] * m_matrix[3][3]);
setMatrix(tmp);
return *this;
}
void TransformationMatrix::multVecMatrix(double x, double y, double& resultX, double& resultY) const
{
resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0];
resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1];
double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3];
if (w != 1 && w != 0) {
resultX /= w;
resultY /= w;
}
}
void TransformationMatrix::multVecMatrix(double x, double y, double z, double& resultX, double& resultY, double& resultZ) const
{
resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0] + z * m_matrix[2][0];
resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1] + z * m_matrix[2][1];
resultZ = m_matrix[3][2] + x * m_matrix[0][2] + y * m_matrix[1][2] + z * m_matrix[2][2];
double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3] + z * m_matrix[2][3];
if (w != 1 && w != 0) {
resultX /= w;
resultY /= w;
resultZ /= w;
}
}
bool TransformationMatrix::isInvertible() const
{
if (isIdentityOrTranslation())
return true;
double det = WebCore::determinant4x4(m_matrix);
if (fabs(det) < SMALL_NUMBER)
return false;
return true;
}
TransformationMatrix TransformationMatrix::inverse() const
{
if (isIdentityOrTranslation()) {
if (m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0)
return TransformationMatrix();
return TransformationMatrix(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
-m_matrix[3][0], -m_matrix[3][1], -m_matrix[3][2], 1);
}
TransformationMatrix invMat;
bool inverted = WebCore::inverse(m_matrix, invMat.m_matrix);
if (!inverted)
return TransformationMatrix();
return invMat;
}
void TransformationMatrix::makeAffine()
{
m_matrix[0][2] = 0;
m_matrix[0][3] = 0;
m_matrix[1][2] = 0;
m_matrix[1][3] = 0;
m_matrix[2][0] = 0;
m_matrix[2][1] = 0;
m_matrix[2][2] = 1;
m_matrix[2][3] = 0;
m_matrix[3][2] = 0;
m_matrix[3][3] = 1;
}
static inline void blendFloat(double& from, double to, double progress)
{
if (from != to)
from = from + (to - from) * progress;
}
void TransformationMatrix::blend(const TransformationMatrix& from, double progress)
{
if (from.isIdentity() && isIdentity())
return;
DecomposedType fromDecomp;
DecomposedType toDecomp;
from.decompose(fromDecomp);
decompose(toDecomp);
blendFloat(fromDecomp.scaleX, toDecomp.scaleX, progress);
blendFloat(fromDecomp.scaleY, toDecomp.scaleY, progress);
blendFloat(fromDecomp.scaleZ, toDecomp.scaleZ, progress);
blendFloat(fromDecomp.skewXY, toDecomp.skewXY, progress);
blendFloat(fromDecomp.skewXZ, toDecomp.skewXZ, progress);
blendFloat(fromDecomp.skewYZ, toDecomp.skewYZ, progress);
blendFloat(fromDecomp.translateX, toDecomp.translateX, progress);
blendFloat(fromDecomp.translateY, toDecomp.translateY, progress);
blendFloat(fromDecomp.translateZ, toDecomp.translateZ, progress);
blendFloat(fromDecomp.perspectiveX, toDecomp.perspectiveX, progress);
blendFloat(fromDecomp.perspectiveY, toDecomp.perspectiveY, progress);
blendFloat(fromDecomp.perspectiveZ, toDecomp.perspectiveZ, progress);
blendFloat(fromDecomp.perspectiveW, toDecomp.perspectiveW, progress);
slerp(&fromDecomp.quaternionX, &toDecomp.quaternionX, progress);
recompose(fromDecomp);
}
bool TransformationMatrix::decompose(DecomposedType& decomp) const
{
if (isIdentity()) {
memset(&decomp, 0, sizeof(decomp));
decomp.perspectiveW = 1;
decomp.scaleX = 1;
decomp.scaleY = 1;
decomp.scaleZ = 1;
}
if (!WebCore::decompose(m_matrix, decomp))
return false;
return true;
}
void TransformationMatrix::recompose(const DecomposedType& decomp)
{
makeIdentity();
m_matrix[0][3] = (float) decomp.perspectiveX;
m_matrix[1][3] = (float) decomp.perspectiveY;
m_matrix[2][3] = (float) decomp.perspectiveZ;
m_matrix[3][3] = (float) decomp.perspectiveW;
translate3d((float) decomp.translateX, (float) decomp.translateY, (float) decomp.translateZ);
double xx = decomp.quaternionX * decomp.quaternionX;
double xy = decomp.quaternionX * decomp.quaternionY;
double xz = decomp.quaternionX * decomp.quaternionZ;
double xw = decomp.quaternionX * decomp.quaternionW;
double yy = decomp.quaternionY * decomp.quaternionY;
double yz = decomp.quaternionY * decomp.quaternionZ;
double yw = decomp.quaternionY * decomp.quaternionW;
double zz = decomp.quaternionZ * decomp.quaternionZ;
double zw = decomp.quaternionZ * decomp.quaternionW;
TransformationMatrix rotationMatrix(1 - 2 * (yy + zz), 2 * (xy - zw), 2 * (xz + yw), 0,
2 * (xy + zw), 1 - 2 * (xx + zz), 2 * (yz - xw), 0,
2 * (xz - yw), 2 * (yz + xw), 1 - 2 * (xx + yy), 0,
0, 0, 0, 1);
multLeft(rotationMatrix);
if (decomp.skewYZ) {
TransformationMatrix tmp;
tmp.setM32((float) decomp.skewYZ);
multLeft(tmp);
}
if (decomp.skewXZ) {
TransformationMatrix tmp;
tmp.setM31((float) decomp.skewXZ);
multLeft(tmp);
}
if (decomp.skewXY) {
TransformationMatrix tmp;
tmp.setM21((float) decomp.skewXY);
multLeft(tmp);
}
scale3d((float) decomp.scaleX, (float) decomp.scaleY, (float) decomp.scaleZ);
}
}