Source code for polymerist.maths.linearalg.affine

'''Utilities to streamline creation of 4x4 affine transformation matrices of 3D linear transformations in homogeneous coordinates'''

__author__ = 'Timotej Bernat'
__email__ = 'timotej.bernat@colorado.edu'

import numpy as np
import numpy.typing as npt
from typing import Annotated, Literal


# USEFUL CUSTOM TYPEHINTS
Array4x4 = Annotated[npt.NDArray[np.float64], Literal[4, 4]]
AffineMatrix = Array4x4 # alias for convenience

# RIGID TRANSFORMATIONS
[docs] def xyzTrans(x : float=0.0, y : float=0.0, z : float=0.0) -> AffineMatrix: '''Returns rigid affine matrix which performs an isometric translation by "x", "y", and "z" units Returns the Identity matrix in absence of passed arguments''' return np.array([ [1, 0, 0, x], [0, 1, 0, y], [0, 0, 1, z], [0, 0, 0, 1], ], dtype='float64')
[docs] def xRot(angle_rad : float=0.0) -> AffineMatrix: '''Returns rigid affine matrix which rotates about the positive x-axis by "angle_rad" radians Returns the Identity matrix in absence of passed arguments''' s = np.sin(angle_rad) c = np.cos(angle_rad) return np.array([ [1, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, 1], ], dtype='float64')
[docs] def yRot(angle_rad : float=0.0) -> AffineMatrix: '''Returns rigid affine matrix which rotates about the positive y-axis by "angle_rad" radians Returns the Identity matrix in absence of passed arguments''' s = np.sin(angle_rad) c = np.cos(angle_rad) return np.array([ [c, 0, -s, 0], [0, 1, 0, 0], [s, 0, c, 0], [0, 0, 0, 1], ], dtype='float64')
[docs] def zRot(angle_rad : float=0.0) -> AffineMatrix: '''Returns rigid affine matrix which rotates about the positive z-axis by "angle_rad" radians Returns the Identity matrix in absence of passed arguments''' s = np.sin(angle_rad) c = np.cos(angle_rad) return np.array([ [c, -s, 0, 0], [s, c, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], ], dtype='float64')
[docs] def randRot(about_x : bool=True, about_y : bool=True, about_z : bool=True) -> AffineMatrix: '''Returns an affine matrix for a rotation by some random angle(s) about the x, y, and z axes (or any subset of those axes)''' rot_dir = { # concise way to encapsulate what rotations can be performed and whther to perform them xRot : about_x, yRot : about_y, zRot : about_z, } matrix = np.eye(4, dtype=float) for (rot_fn, should_rotate) in rot_dir.items(): if should_rotate: matrix = rot_fn(2 * np.pi * np.random.rand()) @ matrix # generate random angle and multiply rotation into overall transform matrix (in order) return matrix
# SCALING AND SHEAR
[docs] def xyzScale(sx : float=1.0, sy : float=1.0, sz : float=1.0) -> AffineMatrix: '''Returns rigid affine matrix which scales basis vectors by factors of "sx", "sy", and "sz" units Returns the Identity matrix in absence of passed arguments''' return np.array([ [sx, 0, 0, 0], [ 0, sy, 0, 0], [ 0, 0, sz, 0], [ 0, 0, 0, 1], ], dtype='float64')
# TODO : add shear matrix functions