Source code for blenderproc.python.utility.MathUtility
""" A collection of math functions. """
from typing import Union, List
import numpy as np
from mathutils import Matrix, Vector, Euler
[docs]
def change_coordinate_frame_of_point(point: Union[np.ndarray, List[float], Vector],
new_frame: List[str]) -> np.ndarray:
""" Transforms the given point into another coordinate frame.
Example: [1, 2, 3] and ["X", "-Z", "Y"] => [1, -3, 2]
:param point: The point to convert in form of a np.ndarray, list or mathutils.Vector.
:param new_frame: An array containing three elements, describing each axis of the new coordinate frame
based on the axes of the current frame. Available: ["X", "Y", "Z", "-X", "-Y", "-Z"].
:return: The converted point also in form of a np.ndarray
"""
assert len(new_frame) == 3, f"The specified coordinate frame has more or less than tree axes: {new_frame}"
point = np.array(point)
output = []
for axis in new_frame:
axis = axis.upper()
if axis.endswith("X"):
output.append(point[0])
elif axis.endswith("Y"):
output.append(point[1])
elif axis.endswith("Z"):
output.append(point[2])
else:
raise ValueError(f"Invalid axis: {axis}")
if axis.startswith("-"):
output[-1] *= -1
return np.array(output)
[docs]
def change_target_coordinate_frame_of_transformation_matrix(matrix: Union[np.ndarray, Matrix],
new_frame: List[str]) -> np.ndarray:
""" Changes the coordinate frame the given transformation matrix is mapping to.
Given a matrix $T_A^B$ that maps from A to B, this function can be used
to change the axes of B into B' and therefore end up with $T_A^B'$.
:param matrix: The matrix to convert in form of a np.ndarray or mathutils.Matrix
:param new_frame: An array containing three elements, describing each axis of the new coordinate frame
based on the axes of the current frame. Available: ["X", "Y", "Z", "-X", "-Y", "-Z"].
:return: The converted matrix is in form of a np.ndarray
"""
tmat = MathUtility.build_coordinate_frame_changing_transformation_matrix(new_frame)
# Apply transformation matrix
output = np.matmul(tmat, matrix)
return output
[docs]
def change_source_coordinate_frame_of_transformation_matrix(matrix: Union[np.ndarray, Matrix],
new_frame: list) -> np.ndarray:
""" Changes the coordinate frame the given transformation matrix is mapping from.
Given a matrix $T_A^B$ that maps from A to B, this function can be used
to change the axes of A into A' and therefore end up with $T_A'^B$.
:param matrix: The matrix to convert in form of a np.ndarray or mathutils.Matrix
:param new_frame: An array containing three elements, describing each axis of the new coordinate frame
based on the axes of the current frame. Available: ["X", "Y", "Z", "-X", "-Y", "-Z"].
:return: The converted matrix is in form of a np.ndarray
"""
tmat = MathUtility.build_coordinate_frame_changing_transformation_matrix(new_frame)
tmat = np.linalg.inv(tmat)
# Apply transformation matrix
output = np.matmul(matrix, tmat)
return output
[docs]
def build_transformation_mat(translation: Union[np.ndarray, List[float], Vector],
rotation: Union[np.ndarray, List[List[float]], Matrix]) -> np.ndarray:
""" Build a transformation matrix from translation and rotation parts.
:param translation: A (3,) vector representing the translation part.
:param rotation: A 3x3 rotation matrix or Euler angles of shape (3,).
:return: The 4x4 transformation matrix.
"""
translation = np.array(translation)
rotation = np.array(rotation)
mat = np.eye(4)
if translation.shape[0] == 3:
mat[:3, 3] = translation
else:
raise RuntimeError(f"Translation has invalid shape: {translation.shape}. Must be (3,) or (3,1) vector.")
if rotation.shape == (3, 3):
mat[:3, :3] = rotation
elif rotation.shape[0] == 3:
mat[:3, :3] = np.array(Euler(rotation).to_matrix())
else:
raise RuntimeError(f"Rotation has invalid shape: {rotation.shape}. Must be rotation matrix of shape "
f"(3,3) or Euler angles of shape (3,) or (3,1).")
return mat
[docs]
class MathUtility:
"""
Math utility class
"""
[docs]
@staticmethod
def build_coordinate_frame_changing_transformation_matrix(destination_frame: List[str]) -> np.ndarray:
""" Builds a transformation matrix that switches the coordinate frame.
:param destination_frame: An array containing three elements, describing each axis of the destination
coordinate frame based on the axes of the source frame.
Available: ["X", "Y", "Z", "-X", "-Y", "-Z"].
:return: The transformation matrix
"""
assert len(destination_frame) == 3, f"The specified coordinate frame has more or less than " \
f"tree axes: {destination_frame}"
# Build transformation matrix that maps the given matrix to the specified coordinate frame.
tmat = np.zeros((4, 4))
for i, axis in enumerate(destination_frame):
axis = axis.upper()
if axis.endswith("X"):
tmat[i, 0] = 1
elif axis.endswith("Y"):
tmat[i, 1] = 1
elif axis.endswith("Z"):
tmat[i, 2] = 1
else:
raise Exception("Invalid axis: " + axis)
if axis.startswith("-"):
tmat[i] *= -1
tmat[3, 3] = 1
return tmat