ID:1956668
 
I've mentioned before that BYOND's matrix interpolation uses a complex formula to achieve natural-looking results. Here I'll share how that formula works.

As a refresher, the transformation matrix works like so:

          |a d 0|
|x y 1| * |b e 0| = |x' y' 1|
|c f 1|

When two matrices are interpolated, each one is broken down like so:

M = S * R * T

The S is for scale and shear combined, R is for rotation, and T is transformation. There's a routine in the core code called MatrixDeconstruct() that handles this, and it outputs S*T (because the T is downright trivial to pull out) and an angle.

Step 1: Pull out T. This is super easy, because all you have to do is keep track of what the c and f values are. A translation matrix doesn't set anything else.

Step 2: Analyze the x and y axes. The x axis is (a,d), because those are the two values x gets multiplied by; the y axis is (b,e). In a non-rotated matrix, the x axis should be a multiple of (1,0) and the y axis is a multiple of (0,1). Find the cosine and sine of the angle by which the axis has been rotated from normal. The angle with the greater cosine (smallest angle) is the rotation angle.

For the x axis, the cosine is a/sqrt(a*a+d*d), and the sine is -d/sqrt(a*a+d*d). For y, it's e/sqrt(b*b+e*e) and b/sqrt(b*b+e*e). Note: If the square root term for either axis is near 0, the matrix is treated as degenerate; the rotation angle is then 0, and S*T is the same as M, so all the following steps are skipped.

Step 3: Multiply by the opposite rotation, R', to get S.

M = S * R * T
M/T = S * R
S = S * (R * R') = M/T * R'


Step 4: Discard any translation components (c and f) from S, and replace them with the original values (T), to get S * T. These are kept together in the calculation just because you can store them in a single matrix, and they're easy to separate again.

Once you have the rotation angle, S, and T for each matrix, you can determine where to go from there.

1) If either matrix is degnerate, linear interpolation is the only way forward.
2) If the difference in rotation angles is 0, linear interpolation is easier.
3) If the difference in rotation angles is 180°, linear interpolation is preferred. (A flip was probably the intended result, and anyway it's not clear which direction was intended for rotation.)
4) All other cases get done the long way.

For the long form, the final interpolated matrix Mi should be:

Mi = Si * Ri * Ti

Si and Ti can be interpolated linearly. Ri is simply the rotation matrix for angle1 + (angle_diff * t).
Neat!
Only just found this, was a great little read.

Reminds me to ask you, are you planning on giving the matrix() proc a size arg, at the moment you can only create a matrix datum of 3x3. I'd love to be able to abuse some of your interpolation and other matrix functions with different sized matrices.
No plans at this time.
In response to Rushnut
Rushnut wrote:
Only just found this, was a great little read.

Reminds me to ask you, are you planning on giving the matrix() proc a size arg, at the moment you can only create a matrix datum of 3x3. I'd love to be able to abuse some of your interpolation and other matrix functions with different sized matrices.

Something worthwhile to keep in mind here is that you can construct larger matrices from smaller matrices, so you could use the built-in matrix class to build up larger matrices if you wanted. Read this for more information.

Though if you know the size of the matrix you want, you could create a fixed size matrix class that's larger than 3×3. You can precompute the involved operations, and it would be quite fast.
In response to Rushnut
As an example, here is a simple 4x4 matrix.

Matrix_4x4
var
_11
_12
_13
_14

_21
_22
_23
_24

_31
_32
_33
_34

_41
_42
_43
_44

proc
Add(Matrix_4x4/N)
src._11 += N._11
src._12 += N._12
src._13 += N._13
src._14 += N._14

src._21 += N._21
src._22 += N._22
src._23 += N._23
src._24 += N._24

src._31 += N._31
src._32 += N._32
src._33 += N._33
src._34 += N._34

src._41 += N._41
src._42 += N._42
src._43 += N._43
src._44 += N._44

Subtract(Matrix_4x4/N)
src._11 -= N._11
src._12 -= N._12
src._13 -= N._13
src._14 -= N._14

src._21 -= N._21
src._22 -= N._22
src._23 -= N._23
src._24 -= N._24

src._31 -= N._31
src._32 -= N._32
src._33 -= N._33
src._34 -= N._34

src._41 -= N._41
src._42 -= N._42
src._43 -= N._43
src._44 -= N._44

Negate()
src._11 = -src._11
src._12 = -src._12
src._13 = -src._13
src._14 = -src._14

src._21 = -src._21
src._22 = -src._22
src._23 = -src._23
src._24 = -src._24

src._31 = -src._31
src._32 = -src._32
src._33 = -src._33
src._34 = -src._34

src._41 = -src._41
src._42 = -src._42
src._43 = -src._43
src._44 = -src._44

Multiply(Matrix_4x4/N)
// Computes src * N. This is important to note, because
// matrix multiplication is in general non-commutative.

var
p11 = src._11*N._11 + src._12*N._21 + src._13*N._31 + src._14*N._41
p12 = src._11*N._12 + src._12*N._22 + src._13*N._32 + src._14*N._42
p13 = src._11*N._13 + src._12*N._23 + src._13*N._33 + src._14*N._43
p14 = src._11*N._14 + src._12*N._24 + src._13*N._34 + src._14*N._44

p21 = src._21*N._11 + src._22*N._21 + src._23*N._31 + src._24*N._41
p22 = src._21*N._12 + src._22*N._22 + src._23*N._32 + src._24*N._42
p23 = src._21*N._13 + src._22*N._23 + src._23*N._33 + src._24*N._43
p24 = src._21*N._14 + src._22*N._24 + src._23*N._34 + src._24*N._44

p31 = src._31*N._11 + src._32*N._21 + src._33*N._31 + src._34*N._41
p32 = src._31*N._12 + src._32*N._22 + src._33*N._32 + src._34*N._42
p33 = src._31*N._13 + src._32*N._23 + src._33*N._33 + src._34*N._43
p34 = src._31*N._14 + src._32*N._24 + src._33*N._34 + src._34*N._44

p41 = src._41*N._11 + src._42*N._21 + src._43*N._31 + src._44*N._41
p42 = src._41*N._12 + src._42*N._22 + src._43*N._32 + src._44*N._42
p43 = src._41*N._13 + src._42*N._23 + src._43*N._33 + src._44*N._43
p44 = src._41*N._14 + src._42*N._24 + src._43*N._34 + src._44*N._44

src._11 = p11
src._12 = p12
src._13 = p13
src._14 = p14

src._21 = p21
src._22 = p22
src._23 = p23
src._24 = p24

src._31 = p31
src._32 = p32
src._33 = p33
src._34 = p34

src._41 = p41
src._42 = p42
src._43 = p43
src._44 = p44

Determinant()
return src._14*src._23*src._32*src._41 - src._13*src._24*src._32*src._41 - src._14*src._22*src._33*src._41 + src._12*src._24*src._33*src._41 + src._13*src._22*src._34*src._41 - src._12*src._23*src._34*src._41 - src._14*src._23*src._31*src._42 + src._13*src._24*src._31*src._42 + src._14*src._21*src._33*src._42 - src._11*src._24*src._33*src._42 - src._13*src._21*src._34*src._42 + src._11*src._23*src._34*src._42 + src._14*src._22*src._31*src._43 - src._12*src._24*src._31*src._43 - src._14*src._21*src._32*src._43 + src._11*src._24*src._32*src._43 + src._12*src._21*src._34*src._43 - src._11*src._22*src._34*src._43 - src._13*src._22*src._31*src._44 + src._12*src._23*src._31*src._44 + src._13*src._21*src._32*src._44 - src._11*src._23*src._32*src._44 - src._12*src._21*src._33*src._44 + src._11*src._22*src._33*src._44

Inverse()
var
i11 = (-src._24*src._33*src._42+src._23*src._34*src._42+src._24*src._32*src._43-src._22*src._34*src._43-src._23*src._32*src._44+src._22*src._33*src._44)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i12 = (src._14*src._33*src._42-src._13*src._34*src._42-src._14*src._32*src._43+src._12*src._34*src._43+src._13*src._32*src._44-src._12*src._33*src._44)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i13 = (-src._14*src._23*src._42+src._13*src._24*src._42+src._14*src._22*src._43-src._12*src._24*src._43-src._13*src._22*src._44+src._12*src._23*src._44)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i14 = (src._14*src._23*src._32-src._13*src._24*src._32-src._14*src._22*src._33+src._12*src._24*src._33+src._13*src._22*src._34-src._12*src._23*src._34)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)

i21 = (src._24*src._33*src._41-src._23*src._34*src._41-src._24*src._31*src._43+src._21*src._34*src._43+src._23*src._31*src._44-src._21*src._33*src._44)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i22 = (-src._14*src._33*src._41+src._13*src._34*src._41+src._14*src._31*src._43-src._11*src._34*src._43-src._13*src._31*src._44+src._11*src._33*src._44)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i23 = (src._14*src._23*src._41-src._13*src._24*src._41-src._14*src._21*src._43+src._11*src._24*src._43+src._13*src._21*src._44-src._11*src._23*src._44)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i24 = (-src._14*src._23*src._31+src._13*src._24*src._31+src._14*src._21*src._33-src._11*src._24*src._33-src._13*src._21*src._34+src._11*src._23*src._34)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)

i31 = (-src._24*src._32*src._41+src._22*src._34*src._41+src._24*src._31*src._42-src._21*src._34*src._42-src._22*src._31*src._44+src._21*src._32*src._44)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i32 = (src._14*src._32*src._41-src._12*src._34*src._41-src._14*src._31*src._42+src._11*src._34*src._42+src._12*src._31*src._44-src._11*src._32*src._44)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i33 = (-src._14*src._22*src._41+src._12*src._24*src._41+src._14*src._21*src._42-src._11*src._24*src._42-src._12*src._21*src._44+src._11*src._22*src._44)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i34 = (src._14*src._22*src._31-src._12*src._24*src._31-src._14*src._21*src._32+src._11*src._24*src._32+src._12*src._21*src._34-src._11*src._22*src._34)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)

i41 = (src._23*src._32*src._41-src._22*src._33*src._41-src._23*src._31*src._42+src._21*src._33*src._42+src._22*src._31*src._43-src._21*src._32*src._43)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i42 = (-src._13*src._32*src._41+src._12*src._33*src._41+src._13*src._31*src._42-src._11*src._33*src._42-src._12*src._31*src._43+src._11*src._32*src._43)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i43 = (src._13*src._22*src._41-src._12*src._23*src._41-src._13*src._21*src._42+src._11*src._23*src._42+src._12*src._21*src._43-src._11*src._22*src._43)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)
i44 = (-src._13*src._22*src._31+src._12*src._23*src._31+src._13*src._21*src._32-src._11*src._23*src._32-src._12*src._21*src._33+src._11*src._22*src._33)/(src._14*src._23*src._32*src._41-src._13*src._24*src._32*src._41-src._14*src._22*src._33*src._41+src._12*src._24*src._33*src._41+src._13*src._22*src._34*src._41-src._12*src._23*src._34*src._41-src._14*src._23*src._31*src._42+src._13*src._24*src._31*src._42+src._14*src._21*src._33*src._42-src._11*src._24*src._33*src._42-src._13*src._21*src._34*src._42+src._11*src._23*src._34*src._42+src._14*src._22*src._31*src._43-src._12*src._24*src._31*src._43-src._14*src._21*src._32*src._43+src._11*src._24*src._32*src._43+src._12*src._21*src._34*src._43-src._11*src._22*src._34*src._43-src._13*src._22*src._31*src._44+src._12*src._23*src._31*src._44+src._13*src._21*src._32*src._44-src._11*src._23*src._32*src._44-src._12*src._21*src._33*src._44+src._11*src._22*src._33*src._44)

src._11 = i11
src._12 = i12
src._13 = i13
src._14 = i14

src._21 = i21
src._22 = i22
src._23 = i23
src._24 = i24

src._31 = i31
src._32 = i32
src._33 = i33
src._34 = i34

src._41 = i41
src._42 = i42
src._43 = i43
src._44 = i44


Now, it's not necessarily the most readable bit of code in the world (I largely generated it), but it should be fairly fast, as it's mostly-constrained by hwo quickly the processor can handle addition, multiplication, division, and assignment.