Subsections


3 Matrix classes

Two matrix classes are supplied, Matrix3, a 3x3 matrix for working with 2D affine transformations, and Matrix4, a 4x4 matrix for working with 3D affine transformations.

The default constructor intializes the matrix to the identity:

>>> Matrix3()
Matrix3([    1.00     0.00     0.00
             0.00     1.00     0.00
             0.00     0.00     1.00])
>>> Matrix4()
Matrix4([    1.00     0.00     0.00     0.00
             0.00     1.00     0.00     0.00
             0.00     0.00     1.00     0.00
             0.00     0.00     0.00     1.00])


3.1 Element access

Internally each matrix is stored as a set of attributes named a to p. The layout for Matrix3 is:

# a b c
# e f g
# i j k

and for Matrix4:

# a b c d
# e f g h
# i j k l
# m n o p

If you wish to set or retrieve a number of elements at once, you can do so with a slice:

>>> m = Matrix4()
>>> m[:]
[1.0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 1.0]
>>> m[12:15] = (5, 5, 5)
>>> m
Matrix4([    1.00     0.00     0.00     5.00
             0.00     1.00     0.00     5.00
             0.00     0.00     1.00     5.00
             0.00     0.00     0.00     1.00])

Note that slices operate in column-major order, which makes them suitable for working directly with OpenGL's glLoadMatrix and glGetFloatv functions.


3.2 Class constructors

There are class constructors for the most common types of transform.

new_identity:

Equivalent to the default constructor. Example:

>>> m = Matrix4.new_identity()
>>> m
Matrix4([    1.00     0.00     0.00     0.00
             0.00     1.00     0.00     0.00
             0.00     0.00     1.00     0.00
             0.00     0.00     0.00     1.00])

"new_scale(x, y)" and "new_scale(x, y, z)":

The former is defined on Matrix3, the latter on Matrix4. Equivalent to the OpenGL call glScalef. Example:

>>> m = Matrix4.new_scale(2.0, 3.0, 4.0)
>>> m
Matrix4([    2.00     0.00     0.00     0.00
             0.00     3.00     0.00     0.00
             0.00     0.00     4.00     0.00
             0.00     0.00     0.00     1.00])

"new_translate(x, y)" and "new_translate(x, y, z)":

The former is defined on Matrix3, the latter on Matrix4. Equivalent to the OpenGL call glTranslatef. Example:

>>> m = Matrix4.new_translate(3.0, 4.0, 5.0)
>>> m
Matrix4([    1.00     0.00     0.00     3.00
             0.00     1.00     0.00     4.00
             0.00     0.00     1.00     5.00
             0.00     0.00     0.00     1.00])

new_rotate(angle):

Create a Matrix3 for a rotation around the origin. angle is specified in radians, anti-clockwise. This is not implemented in Matrix4 (see below for equivalent methods). Example:

>>> import math
>>> m = Matrix3.new_rotate(math.pi / 2)
>>> m
Matrix3([    0.00    -1.00     0.00
             1.00     0.00     0.00
             0.00     0.00     1.00])

The following constructors are defined for Matrix4 only.

new_rotatex(angle), new_rotatey(angle), new_rotatez(angle):

Create a Matrix4 for a rotation around the X, Y or Z axis, respectively. angle is specified in radians. Example:

>>> m = Matrix4.new_rotatex(math.pi / 2)
>>> m
Matrix4([    1.00     0.00     0.00     0.00
             0.00     0.00    -1.00     0.00
             0.00     1.00     0.00     0.00
             0.00     0.00     0.00     1.00])

"new_rotate_axis(angle, axis)":

Create a Matrix4 for a rotation around the given axis. angle is specified in radians, and axis must be an instance of Vector3. It is not necessary to normalize the axis. Example:

>>> m = Matrix4.new_rotate_axis(math.pi / 2, Vector3(1.0, 0.0, 0.0))
>>> m
Matrix4([    1.00     0.00     0.00     0.00
             0.00     0.00    -1.00     0.00
             0.00     1.00     0.00     0.00
             0.00     0.00     0.00     1.00])

"new_rotate_euler(heading, attitude, bank)":

Create a Matrix4 for the given Euler rotation. heading is a rotation around the Y axis, attitude around the X axis and bank around the Z axis. All rotations are performed simultaneously, so this method avoids "gimbal lock" and is the usual method for implemented 3D rotations in a game. Example:

>>> m = Matrix4.new_rotate_euler(math.pi / 2, math.pi / 2, 0.0)
>>> m
Matrix4([    0.00    -0.00     1.00     0.00
             1.00     0.00    -0.00     0.00
            -0.00     1.00     0.00     0.00
             0.00     0.00     0.00     1.00])

"new_perspective(fov_y, aspect, near, far)":

Create a Matrix4 for projection onto the 2D viewing plane. This method is equivalent to the OpenGL call gluPerspective. fov_y is the view angle in the Y direction, in radians. aspect is the aspect ration width / height of the viewing plane. near and far are the distance to the near and far clipping planes. They must be positive and non-zero. Example:

>>> m = Matrix4.new_perspective(math.pi / 2, 1024.0 / 768, 1.0, 100.0)
>>> m
Matrix4([    0.75     0.00     0.00     0.00
             0.00     1.00     0.00     0.00
             0.00     0.00    -1.02    -2.02
             0.00     0.00    -1.00     0.00])


3.3 Operators

Matrices of the same dimension may be multiplied to give a new matrix. For example, to create a transform which translates and scales:

>>> m1 = Matrix3.new_translate(5.0, 6.0)
>>> m2 = Matrix3.new_scale(1.0, 2.0)
>>> m1 * m2
Matrix3([    1.00     0.00     5.00
             0.00     2.00     6.00
             0.00     0.00     1.00])

Note that multiplication is not commutative (the order that you apply transforms matters):

>>> m2 * m1
Matrix3([    1.00     0.00     5.00
             0.00     2.00    12.00
             0.00     0.00     1.00])

In-place multiplication is also permitted (and optimised):

>>> m1 *= m2
>>> m1
Matrix3([    1.00     0.00     5.00
             0.00     2.00     6.00
             0.00     0.00     1.00])

Multiplying a matrix by a vector returns a vector, and is used to transform a vector:

>>> m1 = Matrix3.new_rotate(math.pi / 2)
>>> m1 * Vector2(1.0, 1.0)
Vector2(-1.00, 1.00)

Note that translations have no effect on vectors. They do affect points, however:

>>> m1 = Matrix3.new_translate(5.0, 6.0)
>>> m1 * Vector2(1.0, 2.0)
Vector2(1.00, 2.00)
>>> m1 * Point2(1.0, 2.0)
Point2(6.00, 8.00)

A Matrix3 can be multiplied with a Vector2 or any of the 2D geometry objects (Point2, Line2, Circle, etc).

A Matrix4 can be multiplied with a Vector3 or any of the 3D geometry objects (Point3, Line3, Sphere, etc).

For convenience, each of the matrix constructors are also available as in-place operators. For example, instead of writing:

>>> m1 = Matrix3.new_translate(5.0, 6.0)
>>> m2 = Matrix3.new_scale(1.0, 2.0)
>>> m1 *= m2

you can apply the scale directly to m1:

>>> m1 = Matrix3.new_translate(5.0, 6.0)
>>> m1.scale(1.0, 2.0)
Matrix3([    1.00     0.00     5.00
             0.00     2.00     6.00
             0.00     0.00     1.00])
>>> m1
Matrix3([    1.00     0.00     5.00
             0.00     2.00     6.00
             0.00     0.00     1.00])

Note that these methods operate in-place (they modify the original matrix), and they also return themselves as a result. This allows you to chain transforms together directly:

>>> Matrix3().translate(1.0, 2.0).rotate(math.pi / 2).scale(4.0, 4.0)
Matrix3([    0.00    -4.00     1.00
             4.00     0.00     2.00
             0.00     0.00     1.00])

All constructors have an equivalent in-place method. For Matrix3, they are identity, translate, scale and rotate. For Matrix4, they are identity, translate, scale, rotatex, rotatey, rotatez, rotate_axis and rotate_euler.

The copy method is also implemented in both matrix classes and behaves in the obvious way.