Created
August 4, 2011 20:15
-
-
Save devongovett/1126129 to your computer and use it in GitHub Desktop.
WebKitCSSTransform for all browsers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CSSTransform | |
constructor: -> | |
@m11 = @m22 = @m33 = @m44 = 1 | |
@m12 = @m13 = @m14 = 0 | |
@m21 = @m23 = @m24 = 0 | |
@m31 = @m32 = @m34 = 0 | |
@m41 = @m42 = @m43 = 0 | |
if arguments.length | |
@setMatrix arguments... | |
setMatrix: (v...) -> | |
if v.length is 6 | |
[@m11, @m12, @m21, @m22, @m41, @m42] = v | |
else | |
[@m11, @m12, @m13, @m14, | |
@m21, @m22, @m23, @m24, | |
@m31, @m32, @m33, @m34, | |
@m41, @m42, @m43, @m44] = v | |
multiply = (a, b) -> | |
c = new CSTransform | |
c.m11 = a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31 + a.m14 * b.m41 | |
c.m12 = a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32 + a.m14 * b.m42 | |
c.m13 = a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33 + a.m14 * b.m43 | |
c.m14 = a.m11 * b.m14 + a.m12 * b.m24 + a.m13 * b.m34 + a.m14 * b.m44 | |
c.m21 = a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31 + a.m24 * b.m41 | |
c.m22 = a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32 + a.m24 * b.m42 | |
c.m23 = a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33 + a.m24 * b.m43 | |
c.m24 = a.m21 * b.m14 + a.m22 * b.m24 + a.m23 * b.m34 + a.m24 * b.m44 | |
c.m31 = a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31 + a.m34 * b.m41 | |
c.m32 = a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32 + a.m34 * b.m42 | |
c.m33 = a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33 + a.m34 * b.m43 | |
c.m34 = a.m31 * b.m14 + a.m32 * b.m24 + a.m33 * b.m34 + a.m34 * b.m44 | |
c.m41 = a.m41 * b.m11 + a.m42 * b.m21 + a.m43 * b.m31 + a.m44 * b.m41 | |
c.m42 = a.m41 * b.m12 + a.m42 * b.m22 + a.m43 * b.m32 + a.m44 * b.m42 | |
c.m43 = a.m41 * b.m13 + a.m42 * b.m23 + a.m43 * b.m33 + a.m44 * b.m43 | |
c.m44 = a.m41 * b.m14 + a.m42 * b.m24 + a.m43 * b.m34 + a.m44 * b.m44 | |
return c | |
v3Length = (a, b, c) -> | |
Math.sqrt (a * a) + (b * b) + (c * c) | |
v3Scale = (a, b, c, desiredLength) -> | |
len = v3Length(a, b, c) | |
if len isnt 0 | |
l = desiredLength / len | |
return [a * l, b * l, c * l] | |
return [a, b, c] | |
v3Dot = (a1, b1, c1, a2, b2, c2) -> | |
(a1 * a2) + (b1 * b2) + (c1 * c2) | |
v3Combine = (a1, b1, c1, a2, b2, c2, ascl, bscl) -> | |
a = (ascl * a1) + (bscl * a2) | |
b = (ascl * b1) + (bscl * b2) | |
c = (ascl * c1) + (bscl * c2) | |
return [a, b, c] | |
v3Cross = (a1, b1, c1, a2, b2, c2) -> | |
a = (b1 * c2) - (c1 * b2) | |
b = (c1 * a2) - (a1 * c2) | |
c = (a1 * b2) - (b1 * a2) | |
return [a, b, c] | |
decompose: -> | |
# get translation | |
result = | |
translateX: @m41 | |
translateY: @m42 | |
translateZ: @m43 | |
# scale and skew | |
{m11, m12, m13, m21, m22, m23, m31, m32, m33} = this | |
# Compute X scale factor and normalize first row. | |
result.scaleX = v3Length(m11, m12, m13) | |
[m11, m12, m13] = v3Scale(m11, m12, m13, 1.0) | |
# Compute XY shear factor and make 2nd row orthogonal to 1st. | |
result.skewXY = v3Dot(m11, m12, m13, m21, m22, m23) | |
[m21, m22, m23] = v3Combine(m21, m22, m23, m11, m12, m13, 1.0, -result.skewXY) | |
# Now, compute Y scale and normalize 2nd row. | |
result.scaleY = v3Length(m21, m22, m23) | |
[m21, m22, m23] = v3Scale(m21, m22, m23, 1.0) | |
result.skewXY /= result.scaleY | |
# Compute XZ and YZ shears, orthogonalize 3rd row. | |
result.skewXZ = v3Dot(m11, m12, m13, m31, m32, m33) | |
[m31, m32, m33] = v3Combine(m31, m32, m33, m11, m12, m13, 1.0, -result.skewXZ) | |
result.skewYZ = v3Dot(m21, m22, m23, m31, m32, m33) | |
[m31, m32, m33] = v3Combine(m31, m32, m33, m11, m12, m13, 1.0, -result.skewYZ) | |
# Next, get Z scale and normalize 3rd row. | |
result.scaleZ = v3Length(m31, m32, m33) | |
[m31, m32, m33] = v3Scale(m31, m32, m33, 1.0) | |
result.skewXZ /= result.scaleZ | |
result.skewYZ /= result.scaleZ | |
# At this point, the matrix is orthonormal. | |
# Check for a coordinate system flip. If the determinant | |
# is -1, then negate the matrix and the scaling factors. | |
[a, b, c] = v3Cross(m21, m22, m23, m31, m32, m33) | |
if v3Dot(m11, m12, m13, a, b, c) < 0 | |
result.scale *= -1 | |
[m11, m12, m13, m21, m22, m23, m31, m32, m33] = (e * -1 for e in [m11, m12, m13, m21, m22, m23, m31, m32, m33]) | |
# Now, get the rotations out | |
t = m11 + m21 + m31 + 1.0 | |
if t > 1e-4 | |
s = 0.5 / Math.sqrt(t) | |
w = 0.25 / s | |
x = (m31 - m23) * s | |
y = (m13 - m31) * s | |
z = (m21 - m12) * s | |
else if m11 > m22 and m11 > m33 | |
s = Math.sqrt(1.0 + m11 - m22 - m33) * 2.0 | |
x = 0.25 * s | |
y = (m12 + m21) / s | |
z = (m13 + m31) / s | |
w = (m32 - m23) / s | |
else if m22 > m33 | |
s = Math.sqrt(1.0 + m22 - m11 - m33) * 2.0 | |
x = (m12 + m21) / s | |
y = 0.25 * s | |
z = (m23 + m32) / s | |
w = (m13 - m31) / s | |
else | |
s = Math.sqrt(1.0 + m33 - m11 - m22) * 2.0 | |
x = (m13 + m31) / s | |
y = (m23 + m32) / s | |
z = 0.25 * s | |
w = (m21 - m12) / s | |
result.quaternionX = x | |
result.quaternionY = y | |
result.quaternionZ = z | |
result.quaternionW = w | |
return result | |
valueAt = (from, to, progress) -> | |
if from isnt to | |
return from + (to - from) * progress | |
return from | |
blend: (progress, from, to) -> | |
from = from.decompose() | |
to = to.decompose() | |
from.scaleX = valueAt from.scaleX, to.scaleX, progress | |
from.scaleY = valueAt from.scaleY, to.scaleY, progress | |
from.scaleZ = valueAt from.scaleZ, to.scaleZ, progress | |
from.skewXY = valueAt from.skewXY, to.skewXY, progress | |
from.skewXZ = valueAt from.skewXZ, to.skewXZ, progress | |
from.skewYZ = valueAt from.skewYZ, to.skewYZ, progress | |
from.translateX = valueAt from.translateX, to.translateX, progress | |
from.translateY = valueAt from.translateY, to.translateY, progress | |
from.translateZ = valueAt from.translateZ, to.translateZ, progress | |
#slerp(&fromDecomp.quaternionX, &toDecomp.quaternionX, progress); | |
translate: (x = 0, y = 0, z = 0) -> | |
t = new CSTransform | |
t.m41 = x | |
t.m42 = y | |
t.m43 = z | |
return multiply(this, t) | |
scale: (sx = 1, sy = sx, sz = 1) -> | |
t = new CSTransform | |
t.m11 = sx | |
t.m22 = sy | |
t.m33 = sz | |
return multiply(this, t) | |
radians = (angle) -> | |
Math.PI * (angle / 180) | |
rotate: (rx, ry, rz) -> | |
rx = 0 if isNaN(rx) | |
if isNaN(ry) and isNaN(rz) | |
rz = rx | |
rx = ry = 0 | |
ry = 0 if isNaN(ry) | |
rz = 0 if isNaN(rz) | |
rx = radians(rx) | |
ry = radians(ry) | |
rz = radians(rz) | |
tx = new CSTransform | |
ty = new CSTransform | |
tz = new CSTransform | |
rz /= 2 | |
sinA = Math.sin(rz) | |
cosA = Math.cos(rz) | |
sinA2 = sinA * sinA | |
# Matrices are identity outside the assigned values | |
tz.m11 = tz.m22 = 1 - 2 * sinA2 | |
tz.m12 = tz.m21 = 2 * sinA * cosA | |
tz.m21 *= -1 | |
ry /= 2 | |
sinA = Math.sin(ry) | |
cosA = Math.cos(ry) | |
sinA2 = sinA * sinA | |
ty.m11 = ty.m33 = 1 - 2 * sinA2 | |
ty.m13 = ty.m31 = 2 * sinA * cosA | |
ty.m13 *= -1 | |
rx /= 2 | |
sinA = Math.sin(rx) | |
cosA = Math.cos(rx) | |
sinA2 = sinA * sinA | |
tx.m22 = tx.m33 = 1 - 2 * sinA2 | |
tx.m23 = tx.m32 = 2 * sinA * cosA | |
tx.m32 *= -1 | |
return multiply(this, multiply(tx, multiply(tz, ty))) | |
skew: (sx = 0, sy = 0) -> | |
sx = radians(sx) | |
sy = radians(sy) | |
t = new CSTransform(1, Math.tan(sy), Math.tan(sx), 1, 0, 0) | |
return multiply(this, t) | |
isAffine: -> | |
@m13 is 0 and @m14 is 0 and | |
@m23 is 0 and @m24 is 0 and | |
@m31 is 0 and @m32 is 0 and | |
@m33 is 1 and @m34 is 0 and | |
@m43 is 0 and @m44 is 1 | |
cssString: -> | |
if @isAffine() | |
prefix = 'matrix(' | |
points = [@m11, @m12, @m21, @m22, @m41, @m42] | |
else | |
prefix = 'matrix3d(' | |
points = [@m11, @m12, @m13, @m14, | |
@m21, @m22, @m23, @m24, | |
@m31, @m32, @m33, @m34, | |
@m41, @m42, @m43, @m44] | |
points = (point.toFixed(6) for point in points) | |
return prefix + points.join(', ') + ')' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment