# Using the nuke.math Python module to do Vector and Matrix operations

# 3. Matrix methods

Matrix3:

The Matrix3 class defines a 3x3 transformation matrix and methods to operate with that data, as well as methods to transform vectors
You multiply a Vector by one of these to go from one 3D space to another. This cannot represent 3D translations or perspective.· A Matrix4 should be used for that.

makeIdentity(...)

**Syntax:** Matrix3.makeIdentity() -> none :

**Description:** Replaces the contents of the matrix with the identity. The identity is the no-transformation matrix

**Example:**

Using makeIdentity() to initialize a matrix to the identity | |

1 |
m = nuke.math.Matrix3() |

transform(...)

**Syntax:** Matrix3.transform(Vector3) -> Vector3 :

**Description:** Transforms a Vector3 object using this matrix. This is the same as using the multiply operator (matrix * vector). The method returns a new Vector3 object.

**Example:**

Applying a Matrix to a Vector3 using transform() | |

1 |
matrix = nuke.math.Matrix3() |

determinant(...)

**Syntax:** Matrix3.determinant() -> float :

**Description:** Returns the determinant of the matrix. The determinant is a real number (not another matrix) that can be used for characterizing the matrix. A matrix is said to be singular when its determinant is 0. In other words, if the determinant is 0, a matrix cannot be inverted.

inverse(...)

**Syntax:** Matrix3.inverse() -> Matrix3 :** **

**Description:** Returns inverse of the incoming matrix. Note that, unlike other Vector and Matrix methods, this doesn't modify the matrix object· in-place, but returns a new matrix object. The returned matrix will be such that, when multiplied with the original matrix, will equal to the identity matrix (no transformation). This can be useful to jump between different space coordinates (world space, object space, view space...)

**Example:**

Inverting a matrix | |

1 |
matrix = nuke.math.Matrix3() |

scaling(...), rotation(...), rotationX(...), rotationY(...), rotationZ(...)

**Syntax:** Multiple signatures for each method. Check the help of the nuk.math.Matrix3 Class for more details.

**Description:** These are all methods that REPLACE the contents of a matrix object with a scaling or a rotation matrix. These are useful if you just want to quickly set up a matrix that does a certain scale or rotation transformation. However, if you need to build up your matrix by concatenating multiple transformations, you'll need to use the next set of methods, which modify the contents of the matrix object instead of replacing them.

**Example:**

Setting a rotation / scaling Matrix3 | |

1 |
v = nuke.math.Vector3(0,1,0) # Unit vector looking up towards +Y |

##### scale(...), rotate(...), rotateX(...), rotateY(...), rotateZ(...), skew(...)

**Syntax:** Multiple signatures for each method. Check the help of the nuk.math.Matrix3 Class for more details.** **

**Description:** These methods take the existing matrix and modify it by adding a scale, rotate, or skew transformation to it.

**Example:**

Adding scale / rotation transformations to a Matrix3 | |

1 |
m = nuke.math.Matrix3() |

## Matrix4:

The Matrix4 Class defines a 4x4 transformation matrix and methods to operate with that data, as well as methods to transform vectors.
Although it can also hold the same kind of transformations as a Matrix3 , this is most commonly used to represent perspective transforms, such as the one that transforms the view of a perspective camera into screen-space coordinates.

projection(...)

**Syntax:** Matrix4.projection(float lens, float minz, float maxz, bool perspective) -> none :** **

**Description:** Replace the contents with a camera projection. This creates a matrix that matches the projection of a camera sitting at 0,0,0 and pointing along the Z axis. The 'lens' parameter should be the ratio of the camera's focal length to the horizontal aperture of the film.

The area viewed by this camera is transformed to be in a square with X and Y ranging from -1 to 1. The plane Z==minz is transformed to be at Z==-1 and the plane Z==maxz is transformed to Z==1.

The 'perspective' parameter is a boolean that defines whether this is a perspective projection (True) or an orthographic / parallel projection (False)

**Example:**

Building a projection matrix and transforming a 3D point to 2D coordinates | |

1 |
# Projection variables |

translate(...)

**Syntax:** Matrix4.translate(float x, float y, float z) -> none
:

Matrix4.translate(Vector3) -> none :

**Description:** Adds a translation in x,y and z to the transformation matrix. Modifies the matrix in-place. x,y and z can be fed to the translate method as three separate arguments, or by using a Vector3 object containing the translation vector.

translation(...)

**Syntax:** Matrix4.translation(float x, float y, float z) -> none
:

Matrix4.translation(Vector3) -> none :

**Description:** Same as translate(), but this fully replaces the contents of the matrix. So, translate() would add a translation to the existing matrix, keeping any other transformations in it, whereas translation() completely replaces the contents of the matrix with a new matrix holding this translation only.

transform(...), ntransform(...), vtransform(...)

**Syntax:** Matrix4.transform(Vector3) -> Vector3
:

Matrix4.transform(Vector4) -> Vector4
:

Matrix4.ntransform(Vector3) -> Vector3 :

Matrix4.vtransform(Vector3) -> Vector3 :

**Description:** As in the Matrix3 transform method, this will apply a the transformation of the matrix to a Vector object, and return a new Vector. The special ntransform and vtransform are convenience methods aimed at transforming normals and vectors respectively.

Here's the main differences between these methods:

**Matrix4.transform(Vector4) ->** This is the same as multiplying the matrix with a Vector4. Applies the transformation to a point, and returns the new point as a Vector4.

**Matrix4.transform(Vector3) ->** Takes the Vector3 and treats is as a Vector4 with a w value of 1. Returns a new Vector3.

**Matrix4.vtransform(Vector3) ->** Transforms Vector3 as a direction vector, and not as a point, by treating the Vector3 as a Vector4 with a w value of 0. In practice, this means if the matrix has any translations, those are not applied. Returns a new Vector3.

**Matrix4.ntransform(Vector3) ->** Transforms Vector3 as a normal. This is the same as doing matrix.transpose().vtransform(Vector3). To get the correct transformation for normals, you'll need to use the inverse of your matrix too (normals transformation is done by multiplying the normal vector by the transpose of the inverse of a matrix. If this sounds confusing, read the following link to learn more about transforming normals.

http://www.unknownroad.com/rtfm/graphics/rt_normals.html

transpose(...)

**Syntax:** Matrix4.transpose() -> none :

**Description:** Replaces the contents of the Matrix4 with the transposed matrix. This is a matrix with its values reflected through the diagonal or, in other words, the matrix obtained by exchanging thw rows and columns of the original matrix. This can be used to find whether a square matrix (3x3, 4x4) is symetric, since the transpose of a symetric matrix is equal to the original matrix.

scaleOnly(...), rotationOnly(...), translationOnly(...), scaleAndRotationOnly(...)

**Syntax:** Matrix4.scaleOnly(), Matrix4.rotationOnly(), etc. -> none :

**Description:** Modifies the transformation matrix to represent only certain components (scale, rotation, translation, scale and rotation). This is useful if you only care about one set of transformations, and want to ignore all others.

**Example:**

Getting only the translation component of a Matrix4 | |

1 |
matrix = nuke.math.Matrix4() |

rotationsZXY(...)

**Syntax:** Matrix4.rotationsZXY() -> tuple :

**Description:** If this is a rotation-only matrix (you might want to use the rotationOnly method first if it's not), this returns a tuple containing the equivalent rotations around the X, Y and Z axis. This is usually regarded as Matrix to Euler rotations, and becomes useful when you need to set the rotate values in a 3D node to match the rotations of a certain matrix. Note that the returned angles are in radians, so you'll need to convert them to degrees if you use them on a rotate knob. Also note that the returned rotations can be used to match the rotation of the matrix only when the rotation order of your node is set to 'ZXY'.

**Example:**

Finding Euler rotation angles from rotation-only Matrix4 | |

1 |
matrix = nuke.math.Matrix4() |

As you can see from the example above, if you were to re-build the matrix by manually rotating it about X, Y and Z, you would need to do the rotations in the inverse order. So, if the tuple returned by rotationsZXY() is (x,y,z), to rebuild the matrix from those rotations you would need to do rotateY(y), then rotateX(x) and finally rotateZ(z) to get the matrix that does the same transformation.

mapUnitSquareToQuad(...)

**Syntax:** Matrix4.mapUnitSquareToQuad(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) -> none :

**Description:** Replaces the Matrix4 with a cornerpin-like transformation. This builds a 4x4 matrix describing a transformation such that the corners of a 1-unit square (0,0,1,1) get mapped to the 4 corners fed into the function.

**Example:**

Creating a matrix to match the transformation of a CornerPin2D node | |

1 |
cornerPinMatrix = nuke.math.Matrix4() |

mapQuadToUnitSquare(...)

**Syntax:** Matrix4.mapQuadToUnitSquare(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) -> none :

**Description:** Replaces the Matrix4 with a cornerpin-like transformation. This is the reverse of the method described above. It builds a 4x4 matrix describing a transformation such that the 4 corners fed into the function are mapped to the corners of a 1-unit square (0,0,1,1).

xAxis(...), yAxis(...), zAxis(...)

**Syntax:** Matrix4.xAxis() -> Vector3 :

Matrix4.yAxis() -> Vector3 :

Matrix4.zAxis() -> Vector3 :

**Description:** Returns a Vector3. If the Matrix4 is not a persxpective matrix (bottom row must be 0,0,0,1), the Vector3 object represents the transformation of a 1-unit vector along the respective axis (x for xAxis, y for yAxis...). This is a quick and convenient useful to check the transformation of such vectors without having to create them first and multiply them by the matrix.

## Comments

Yes, Michael, completely agree. It's one of those I had to learn the hard way, so I figured it would be useful to share. Plus, I learnt a bit more while writing it. Glad you liked it.

one quick question thou, I was testing out some of those function on Matrix and I found a little problem.

What I am trying to do is to get the XYZ rotation( in degrees ) from the world matrix of a Camera.

Following you tutorial everythign works fine but the final result of the rotation is a bit different from the original rotation.

what I am doing is really simple :

########################################

import math

matrix = nuke.math.Matri x4() #creating a matrix

matrixValues = nuke.toNode('Ca mera1')['world_ matrix'].getVal ue() # getting the value from the camera world matrix

print "matrixValues :", matrixValues

### adding the value from the camera to the matrix just created ###

for VAL in matrixValues :

print "VAL",VAL

matrix[matrixVa lues.index(VAL) ] = VAL

matrix.rotationOnly() #getting sure I have only the rotation values

print math.degrees(ma trix.rotationsZ XY()[0])

print math.degrees(matrix.rotationsZXY()[1])

print math.degrees(matrix.rotationsZXY()[2])

### check if values r the same

if nuke.toNode('Ca mera1')['rotate '].value(0) == math.degrees(ma trix.rotationsZ XY()[0]) : print True

else: print False

########################################

as u may see it seems that there is an approximation and we loose the last 5 digits on every value when we are adding the worldMatrix of the camera to our Matrix4, and that may be causing the little difference in the result.

Do you know by chance if that is something I did wrong or maybe if there is a work around it ?

thanks for your time

RSS feed for comments to this post