Source code for pyrr.matrix44

# -*- coding: utf-8 -*-
"""4x4 Matrix which supports rotation, translation, scale and skew.

Matrices are laid out in row-major format and can be loaded directly
into OpenGL.
To convert to column-major format, transpose the array using the
numpy.array.T method.
"""
from __future__ import absolute_import, division, print_function
import numpy as np
from . import matrix33
from . import vector
from . import vector3
from . import quaternion
from .utils import all_parameters_as_numpy_arrays, parameters_as_numpy_arrays


[docs]def create_identity(dtype=None): """Creates a new matrix44 and sets it to an identity matrix. :rtype: numpy.array :return: A matrix representing an identity matrix with shape (4,4). """ return np.identity(4, dtype=dtype)
[docs]def create_from_matrix33(mat, dtype=None): """Creates a Matrix44 from a Matrix33. The translation will be 0,0,0. :rtype: numpy.array :return: A matrix with shape (4,4) with the input matrix rotation. """ mat4 = np.identity(4, dtype=dtype) mat4[0:3, 0:3] = mat return mat4
[docs]def create_matrix33_view(mat): """Returns a view into the matrix in Matrix33 format. This is different from matrix33.create_from_matrix44, in that changes to the returned matrix will also alter the original matrix. :rtype: numpy.array :return: A view into the matrix in the format of a matrix33 (shape (3,3)). """ return mat[0:3, 0:3]
[docs]@parameters_as_numpy_arrays('eulers') def create_from_eulers(eulers, dtype=None): """Creates a matrix from the specified Euler rotations. :param numpy.array eulers: A set of euler rotations in the format specified by the euler modules. :rtype: numpy.array :return: A matrix with shape (4,4) with the euler's rotation. """ dtype = dtype or eulers.dtype # set to identity matrix # this will populate our extra rows for us mat = create_identity(dtype) # we'll use Matrix33 for our conversion mat[0:3, 0:3] = matrix33.create_from_eulers(eulers, dtype) return mat
[docs]@parameters_as_numpy_arrays('axis') def create_from_axis_rotation(axis, theta, dtype=None): """Creates a matrix from the specified rotation theta around an axis. :param numpy.array axis: A (3,) vector. :param float theta: A rotation in radians. :rtype: numpy.array :return: A matrix with shape (4,4). """ dtype = dtype or axis.dtype # set to identity matrix # this will populate our extra rows for us mat = create_identity(dtype) # we'll use Matrix33 for our conversion mat[0:3, 0:3] = matrix33.create_from_axis_rotation(axis, theta, dtype) return mat
[docs]@parameters_as_numpy_arrays('quat') def create_from_quaternion(quat, dtype=None): """Creates a matrix with the same rotation as a quaternion. :param quat: The quaternion to create the matrix from. :rtype: numpy.array :return: A matrix with shape (4,4) with the quaternion's rotation. """ dtype = dtype or quat.dtype # set to identity matrix # this will populate our extra rows for us mat = create_identity(dtype) # we'll use Matrix33 for our conversion mat[0:3, 0:3] = matrix33.create_from_quaternion(quat, dtype) return mat
[docs]@parameters_as_numpy_arrays('quat') def create_from_inverse_of_quaternion(quat, dtype=None): """Creates a matrix with the inverse rotation of a quaternion. This can be used to go from object space to inertial space. :param numpy.array quat: The quaternion to make the matrix from (shape 4). :rtype: numpy.array :return: A matrix with shape (4,4) that respresents the inverse of the quaternion. """ dtype = dtype or quat.dtype # set to identity matrix # this will populate our extra rows for us mat = create_identity(dtype) # we'll use Matrix33 for our conversion mat[0:3, 0:3] = matrix33.create_from_inverse_of_quaternion(quat, dtype) return mat
[docs]@parameters_as_numpy_arrays('vec') def create_from_translation(vec, dtype=None): """Creates an identity matrix with the translation set. :param numpy.array vec: The translation vector (shape 3 or 4). :rtype: numpy.array :return: A matrix with shape (4,4) that represents a matrix with the translation set to the specified vector. """ dtype = dtype or vec.dtype mat = create_identity(dtype) mat[3, 0:3] = vec[:3] return mat
[docs]def create_from_scale(scale, dtype=None): """Creates an identity matrix with the scale set. :param numpy.array scale: The scale to apply as a vector (shape 3). :rtype: numpy.array :return: A matrix with shape (4,4) with the scale set to the specified vector. """ # we need to expand 'scale' into it's components # because numpy isn't flattening them properly. m = np.diagflat([scale[0], scale[1], scale[2], 1.0]) if dtype: m = m.astype(dtype) return m
[docs]def create_from_x_rotation(theta, dtype=None): """Creates a matrix with the specified rotation about the X axis. :param float theta: The rotation, in radians, about the X-axis. :rtype: numpy.array :return: A matrix with the shape (4,4) with the specified rotation about the X-axis. .. seealso:: http://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions """ mat = create_identity(dtype) mat[0:3, 0:3] = matrix33.create_from_x_rotation(theta, dtype) return mat
[docs]def create_from_y_rotation(theta, dtype=None): """Creates a matrix with the specified rotation about the Y axis. :param float theta: The rotation, in radians, about the Y-axis. :rtype: numpy.array :return: A matrix with the shape (4,4) with the specified rotation about the Y-axis. .. seealso:: http://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions """ mat = create_identity(dtype) mat[0:3, 0:3] = matrix33.create_from_y_rotation(theta, dtype) return mat
[docs]def create_from_z_rotation(theta, dtype=None): """Creates a matrix with the specified rotation about the Z axis. :param float theta: The rotation, in radians, about the Z-axis. :rtype: numpy.array :return: A matrix with the shape (4,4) with the specified rotation about the Z-axis. .. seealso:: http://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions """ mat = create_identity(dtype) mat[0:3, 0:3] = matrix33.create_from_z_rotation(theta, dtype) return mat
[docs]@all_parameters_as_numpy_arrays def apply_to_vector(mat, vec): """Apply a matrix to a vector. The matrix's rotation and translation are applied to the vector. Supports multiple matrices and vectors. :param numpy.array mat: The rotation / translation matrix. Can be a list of matrices. :param numpy.array vec: The vector to modify. Can be a numpy.array of vectors. ie. numpy.array([[x1,...], [x2,...], ...]) :rtype: numpy.array :return: The vectors rotated by the specified matrix. """ size = vec.shape[len(vec.shape) - 1] if size == 3: # convert to a vec4 if len(vec.shape) == 1: vec4 = np.array([vec[0], vec[1], vec[2], 1.], dtype=vec.dtype) vec4 = np.dot(vec4, mat) if np.abs(vec4[3]) < 1e-8: vec4[:] = [np.inf, np.inf, np.inf, np.inf] else: vec4 /= vec4[3] return vec4[:3] else: vec4 = np.array([[v[0], v[1], v[2], 1.] for v in vec], dtype=vec.dtype) vec4 = np.dot(vec4, mat) for i in range(vec4.shape[0]): if np.abs(vec4[i,3])<1e-8: vec4[i,:] = [np.inf, np.inf, np.inf, np.inf] else: vec4[i,:] /= vec4[i,3] return vec4[:,:3] elif size == 4: return np.dot(vec, mat) else: raise ValueError("Vector size unsupported")
[docs]def multiply(m1, m2): """Multiply two matricies, m1 . m2. This is essentially a wrapper around numpy.dot(m1, m2) :param numpy.array m1: The first matrix. Can be a list of matrices. :param numpy.array m2: The second matrix. Can be a list of matrices. :rtype: numpy.array :return: A matrix that results from multiplying m1 by m2. """ return np.dot(m1, m2)
[docs]def create_perspective_projection(fovy, aspect, near, far, dtype=None): """Creates perspective projection matrix. .. seealso:: http://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml .. seealso:: http://www.geeks3d.com/20090729/howto-perspective-projection-matrix-in-opengl/ :param float fovy: field of view in y direction in degrees :param float aspect: aspect ratio of the view (width / height) :param float near: distance from the viewer to the near clipping plane (only positive) :param float far: distance from the viewer to the far clipping plane (only positive) :rtype: numpy.array :return: A projection matrix representing the specified perpective. """ ymax = near * np.tan(fovy * np.pi / 360.0) xmax = ymax * aspect return create_perspective_projection_from_bounds(-xmax, xmax, -ymax, ymax, near, far, dtype=dtype)
[docs]def create_perspective_projection_matrix(fovy, aspect, near, far, dtype=None): # TDOO: mark as deprecated """Creates perspective projection matrix. .. seealso:: http://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml .. seealso:: http://www.geeks3d.com/20090729/howto-perspective-projection-matrix-in-opengl/ :param float fovy: field of view in y direction in degrees :param float aspect: aspect ratio of the view (width / height) :param float near: distance from the viewer to the near clipping plane (only positive) :param float far: distance from the viewer to the far clipping plane (only positive) :rtype: numpy.array :return: A projection matrix representing the specified perpective. """ return create_perspective_projection(fovy, aspect, near, far, dtype)
[docs]def create_perspective_projection_from_bounds( left, right, bottom, top, near, far, dtype=None ): """Creates a perspective projection matrix using the specified near plane dimensions. :param float left: The left of the near plane relative to the plane's centre. :param float right: The right of the near plane relative to the plane's centre. :param float top: The top of the near plane relative to the plane's centre. :param float bottom: The bottom of the near plane relative to the plane's centre. :param float near: The distance of the near plane from the camera's origin. It is recommended that the near plane is set to 1.0 or above to avoid rendering issues at close range. :param float far: The distance of the far plane from the camera's origin. :rtype: numpy.array :return: A projection matrix representing the specified perspective. .. seealso:: http://www.gamedev.net/topic/264248-building-a-projection-matrix-without-api/ .. seealso:: http://www.glprogramming.com/red/chapter03.html """ """ E 0 A 0 0 F B 0 0 0 C D 0 0-1 0 A = (right+left)/(right-left) B = (top+bottom)/(top-bottom) C = -(far+near)/(far-near) D = -2*far*near/(far-near) E = 2*near/(right-left) F = 2*near/(top-bottom) """ A = (right + left) / (right - left) B = (top + bottom) / (top - bottom) C = -(far + near) / (far - near) D = -2. * far * near / (far - near) E = 2. * near / (right - left) F = 2. * near / (top - bottom) return np.array(( ( E, 0., 0., 0.), ( 0., F, 0., 0.), ( A, B, C,-1.), ( 0., 0., D, 0.), ), dtype=dtype)
[docs]def create_perspective_projection_matrix_from_bounds( left, right, bottom, top, near, far, dtype=None): # TDOO: mark as deprecated """Creates a perspective projection matrix using the specified near plane dimensions. :param float left: The left of the near plane relative to the plane's centre. :param float right: The right of the near plane relative to the plane's centre. :param float top: The top of the near plane relative to the plane's centre. :param float bottom: The bottom of the near plane relative to the plane's centre. :param float near: The distance of the near plane from the camera's origin. It is recommended that the near plane is set to 1.0 or above to avoid rendering issues at close range. :param float far: The distance of the far plane from the camera's origin. :rtype: numpy.array :return: A projection matrix representing the specified perspective. .. seealso:: http://www.gamedev.net/topic/264248-building-a-projection-matrix-without-api/ .. seealso:: http://www.glprogramming.com/red/chapter03.html """ """ E 0 A 0 0 F B 0 0 0 C D 0 0-1 0 A = (right+left)/(right-left) B = (top+bottom)/(top-bottom) C = -(far+near)/(far-near) D = -2*far*near/(far-near) E = 2*near/(right-left) F = 2*near/(top-bottom) """ return create_perspective_projection_from_bounds( left, right, bottom, top, near, far, dtype )
[docs]def create_orthogonal_projection( left, right, bottom, top, near, far, dtype=None ): """Creates an orthogonal projection matrix. :param float left: The left of the near plane relative to the plane's centre. :param float right: The right of the near plane relative to the plane's centre. :param float top: The top of the near plane relative to the plane's centre. :param float bottom: The bottom of the near plane relative to the plane's centre. :param float near: The distance of the near plane from the camera's origin. It is recommended that the near plane is set to 1.0 or above to avoid rendering issues at close range. :param float far: The distance of the far plane from the camera's origin. :rtype: numpy.array :return: A projection matrix representing the specified orthogonal perspective. .. seealso:: http://msdn.microsoft.com/en-us/library/dd373965(v=vs.85).aspx """ """ A 0 0 Tx 0 B 0 Ty 0 0 C Tz 0 0 0 1 A = 2 / (right - left) B = 2 / (top - bottom) C = -2 / (far - near) Tx = (right + left) / (right - left) Ty = (top + bottom) / (top - bottom) Tz = (far + near) / (far - near) """ rml = right - left tmb = top - bottom fmn = far - near A = 2. / rml B = 2. / tmb C = -2. / fmn Tx = -(right + left) / rml Ty = -(top + bottom) / tmb Tz = -(far + near) / fmn return np.array(( ( A, 0., 0., 0.), (0., B, 0., 0.), (0., 0., C, 0.), (Tx, Ty, Tz, 1.), ), dtype=dtype)
[docs]def create_orthogonal_projection_matrix( left, right, bottom, top, near, far, dtype=None): # TDOO: mark as deprecated """Creates an orthogonal projection matrix. :param float left: The left of the near plane relative to the plane's centre. :param float right: The right of the near plane relative to the plane's centre. :param float top: The top of the near plane relative to the plane's centre. :param float bottom: The bottom of the near plane relative to the plane's centre. :param float near: The distance of the near plane from the camera's origin. It is recommended that the near plane is set to 1.0 or above to avoid rendering issues at close range. :param float far: The distance of the far plane from the camera's origin. :rtype: numpy.array :return: A projection matrix representing the specified orthogonal perspective. .. seealso:: http://msdn.microsoft.com/en-us/library/dd373965(v=vs.85).aspx """ """ A 0 0 Tx 0 B 0 Ty 0 0 C Tz 0 0 0 1 A = 2 / (right - left) B = 2 / (top - bottom) C = -2 / (far - near) Tx = (right + left) / (right - left) Ty = (top + bottom) / (top - bottom) Tz = (far + near) / (far - near) """ return create_orthogonal_projection( left, right, bottom, top, near, far, dtype )
[docs]def create_look_at(eye, target, up, dtype=None): """Creates a look at matrix according to OpenGL standards. :param numpy.array eye: Position of the camera in world coordinates. :param numpy.array target: The position in world coordinates that the camera is looking at. :param numpy.array up: The up vector of the camera. :rtype: numpy.array :return: A look at matrix that can be used as a viewMatrix """ eye = np.asarray(eye) target = np.asarray(target) up = np.asarray(up) forward = vector.normalize(target - eye) side = vector.normalize(np.cross(forward, up)) up = vector.normalize(np.cross(side, forward)) return np.array(( (side[0], up[0], -forward[0], 0.), (side[1], up[1], -forward[1], 0.), (side[2], up[2], -forward[2], 0.), (-np.dot(side, eye), -np.dot(up, eye), np.dot(forward, eye), 1.0) ), dtype=dtype)
[docs]def inverse(m): """Returns the inverse of the matrix. This is essentially a wrapper around numpy.linalg.inv. :param numpy.array m: A matrix. :rtype: numpy.array :return: The inverse of the specified matrix. .. seealso:: http://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.inv.html """ return np.linalg.inv(m)
[docs]def decompose(m): """Decomposes an affine transformation matrix into its scale, rotation and translation components. :param numpy.array m: A matrix. :return: tuple (scale, rotation, translation) numpy.array scale vector3 numpy.array rotation quaternion numpy.array translation vector3 """ m = np.asarray(m) scale = np.linalg.norm(m[:3, :3], axis=1) det = np.linalg.det(m) if det < 0: scale[0] *= -1 position = m[3, :3] rotation = m[:3, :3] * (1 / scale)[:, None] return scale, quaternion.create_from_matrix(rotation), position