# `affines`¶

Compose and decompose homogenous affine - usually 4x4 - matrices

 `compose`(T, R, Z[, S]) Compose translations, rotations, zooms, [shears] to affine Decompose homogenous affine transformation matrix A into parts. Decompose 4x4 homogenous affine matrix into parts.

## compose¶

`transforms3d.affines.``compose`(T, R, Z, S=None)

Compose translations, rotations, zooms, [shears] to affine

Parameters
Tarray-like shape (N,)

Translations, where N is usually 3 (3D case)

Rarray-like shape (N,N)

Rotation matrix where N is usually 3 (3D case)

Zarray-like shape (N,)

Zooms, where N is usually 3 (3D case)

Sarray-like, shape (P,), optional

Shear vector, such that shears fill upper triangle above diagonal to form shear matrix. P is the (N-2)th Triangular number, which happens to be 3 for a 4x4 affine (3D case)

Returns
Aarray, shape (N+1, N+1)

Affine transformation matrix where N usually == 3 (3D case)

Examples

```>>> T = [20, 30, 40] # translations
>>> R = [[0, -1, 0], [1, 0, 0], [0, 0, 1]] # rotation matrix
>>> Z = [2.0, 3.0, 4.0] # zooms
>>> A = compose(T, R, Z)
>>> A
array([[ 0., -3.,  0., 20.],
[ 2.,  0.,  0., 30.],
[ 0.,  0.,  4., 40.],
[ 0.,  0.,  0.,  1.]])
>>> S = np.zeros(3)
>>> B = compose(T, R, Z, S)
>>> np.all(A == B)
True
```

A null set

```>>> compose(np.zeros(3), np.eye(3), np.ones(3), np.zeros(3))
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
```

## decompose¶

`transforms3d.affines.``decompose`(A)

Decompose homogenous affine transformation matrix A into parts.

The parts are translations, rotations, zooms, shears.

A can be any square matrix, but is typically shape (4,4).

Decomposes A into `T, R, Z, S`, such that, if A is shape (4,4):

```Smat = np.array([[1, S[0], S[1]],
[0,    1, S[2]],
[0,    0,    1]])
RZS = np.dot(R, np.dot(np.diag(Z), Smat))
A = np.eye(4)
A[:3,:3] = RZS
A[:-1,-1] = T
```

The order of transformations is therefore shears, followed by zooms, followed by rotations, followed by translations.

The case above (A.shape == (4,4)) is the most common, and corresponds to a 3D affine, but in fact A need only be square.

Parameters
Aarray shape (N,N)
Returns
Tarray, shape (N-1,)

Translation vector

Rarray shape (N-1, N-1)

rotation matrix

Zarray, shape (N-1,)

Zoom vector. May have one negative zoom to prevent need for negative determinant R matrix above

Sarray, shape (P,)

Shear vector, such that shears fill upper triangle above diagonal to form shear matrix. P is the (N-2)th Triangular number, which happens to be 3 for a 4x4 affine.

Notes

We have used a nice trick from SPM to get the shears. Let us call the starting N-1 by N-1 matrix `RZS`, because it is the composition of the rotations on the zooms on the shears. The rotation matrix `R` must have the property `np.dot(R.T, R) == np.eye(N-1)`. Thus ```np.dot(RZS.T, RZS)``` will, by the transpose rules, be equal to `np.dot((ZS).T, (ZS))`. Because we are doing shears with the upper right part of the matrix, that means that the Cholesky decomposition of `np.dot(RZS.T, RZS)` will give us our `ZS` matrix, from which we take the zooms from the diagonal, and the shear values from the off-diagonal elements.

Examples

```>>> T = [20, 30, 40] # translations
>>> R = [[0, -1, 0], [1, 0, 0], [0, 0, 1]] # rotation matrix
>>> Z = [2.0, 3.0, 4.0] # zooms
>>> S = [0.2, 0.1, 0.3] # shears
>>> # Now we make an affine matrix
>>> A = np.eye(4)
>>> Smat = np.array([[1, S[0], S[1]],
...                  [0,    1, S[2]],
...                  [0,    0,    1]])
>>> RZS = np.dot(R, np.dot(np.diag(Z), Smat))
>>> A[:3,:3] = RZS
>>> A[:-1,-1] = T # set translations
>>> Tdash, Rdash, Zdash, Sdash = decompose(A)
>>> np.allclose(T, Tdash)
True
>>> np.allclose(R, Rdash)
True
>>> np.allclose(Z, Zdash)
True
>>> np.allclose(S, Sdash)
True
```

## decompose44¶

`transforms3d.affines.``decompose44`(A44)

Decompose 4x4 homogenous affine matrix into parts.

The parts are translations, rotations, zooms, shears.

This is the same as `decompose()` but specialized for 4x4 affines.

Decomposes A44 into `T, R, Z, S`, such that:

```Smat = np.array([[1, S[0], S[1]],
[0,    1, S[2]],
[0,    0,    1]])
RZS = np.dot(R, np.dot(np.diag(Z), Smat))
A44 = np.eye(4)
A44[:3,:3] = RZS
A44[:-1,-1] = T
```

The order of transformations is therefore shears, followed by zooms, followed by rotations, followed by translations.

This routine only works for shape (4,4) matrices

Parameters
A44array shape (4,4)
Returns
Tarray, shape (3,)

Translation vector

Rarray shape (3,3)

rotation matrix

Zarray, shape (3,)

Zoom vector. May have one negative zoom to prevent need for negative determinant R matrix above

Sarray, shape (3,)

Shear vector, such that shears fill upper triangle above diagonal to form shear matrix (type `striu`).

Notes

The implementation inspired by:

Decomposing a matrix into simple transformations by Spencer W. Thomas, pp 320-323 in Graphics Gems II, James Arvo (editor), Academic Press, 1991, ISBN: 0120644819.

The upper left 3x3 of the affine consists of a matrix we’ll call RZS:

```RZS = R * Z *S
```

where R is a rotation matrix, Z is a diagonal matrix of scalings:

```Z = diag([sx, sy, sz])
```

and S is a shear matrix of form:

```S = [[1, sxy, sxz],
[0,   1, syz],
[0,   0,   1]])
```

Running all this through sympy (see ‘derivations’ folder) gives `RZS` as

```[R00*sx, R01*sy + R00*sx*sxy, R02*sz + R00*sx*sxz + R01*sy*syz]
[R10*sx, R11*sy + R10*sx*sxy, R12*sz + R10*sx*sxz + R11*sy*syz]
[R20*sx, R21*sy + R20*sx*sxy, R22*sz + R20*sx*sxz + R21*sy*syz]
```

`R` is defined as being a rotation matrix, so the dot products between the columns of `R` are zero, and the norm of each column is 1. Thus the dot product:

```R[:,0].T * RZS[:,1]
```

that results in:

```[R00*R01*sy + R10*R11*sy + R20*R21*sy + sx*sxy*R00**2 + sx*sxy*R10**2 + sx*sxy*R20**2]
```

simplifies to `sy*0 + sx*sxy*1` == `sx*sxy`. Therefore:

```R[:,1] * sy = RZS[:,1] - R[:,0] * (R[:,0].T * RZS[:,1])
```

allowing us to get `sy` with the norm, and sxy with ```R[:,0].T * RZS[:,1] / sx```.

Similarly `R[:,0].T * RZS[:,2]` simplifies to `sx*sxz`, and `R[:,1].T * RZS[:,2]` to `sy*syz` giving us the remaining unknowns.

Examples

```>>> T = [20, 30, 40] # translations
>>> R = [[0, -1, 0], [1, 0, 0], [0, 0, 1]] # rotation matrix
>>> Z = [2.0, 3.0, 4.0] # zooms
>>> S = [0.2, 0.1, 0.3] # shears
>>> # Now we make an affine matrix
>>> A = np.eye(4)
>>> Smat = np.array([[1, S[0], S[1]],
...                  [0,    1, S[2]],
...                  [0,    0,    1]])
>>> RZS = np.dot(R, np.dot(np.diag(Z), Smat))
>>> A[:3,:3] = RZS
>>> A[:-1,-1] = T # set translations
>>> Tdash, Rdash, Zdash, Sdash = decompose44(A)
>>> np.allclose(T, Tdash)
True
>>> np.allclose(R, Rdash)
True
>>> np.allclose(Z, Zdash)
True
>>> np.allclose(S, Sdash)
True
```