Skip to content

matrix3D

GMatrix3D

Bases: G

This class passes points through a 3D transformation matrix before forwarding them to the G class, allowing transformations in all three dimensions.

The 3D transformation matrices are arranged in a stack, similar to OpenGL.

numpy is required.

Source code in mecode/matrix3D.py
class GMatrix3D(G):
    """This class passes points through a 3D transformation matrix before
    forwarding them to the G class, allowing transformations in all three
    dimensions.

    The 3D transformation matrices are arranged in a stack, similar to OpenGL.

    numpy is required.
    """

    def __init__(self, *args, **kwargs):
        super(GMatrix3D, self).__init__(*args, **kwargs)
        self.stack = [np.identity(4)]  # Start with a 4x4 identity matrix

    def push_matrix(self):
        # Push a copy of the current matrix onto the stack
        self.stack.append(self.stack[-1].copy())

    def pop_matrix(self):
        # Pop the top matrix off the stack
        if len(self.stack) > 1:
            self.stack.pop()
        else:
            self.stack = [np.identity(4)]
            warnings.warn(
                "Cannot pop all items from stack. Resetting to default identity matrix."
            )

    def apply_transform(self, transform):
        # Apply a transformation matrix to the current matrix
        transformed_matrix = self.stack[-1] @ transform

        # Round values smaller than machine epsilon to zero
        epsilon = np.finfo(transformed_matrix.dtype).eps
        self.stack[-1] = np.where(
            np.abs(transformed_matrix) < epsilon, 0, transformed_matrix
        )

    def get_current_matrix(self):
        # Get the current matrix (top of the stack)
        return self.stack[-1]

    def translate(self, x=0, y=0, z=0):
        # Create a 3D translation matrix and apply it
        translation_matrix = np.array([
            [1, 0, 0, x],
            [0, 1, 0, y],
            [0, 0, 1, z],
            [0, 0, 0, 1]
        ])
        self.apply_transform(translation_matrix)

    def rotate_x(self, angle):
        # Create a rotation matrix around the X-axis
        c, s = np.cos(angle), np.sin(angle)
        rotation_matrix = np.array([
            [1, 0, 0, 0],
            [0, c, -s, 0],
            [0, s, c, 0],
            [0, 0, 0, 1]
        ])
        self.apply_transform(rotation_matrix)

    def rotate_y(self, angle):
        # Create a rotation matrix around the Y-axis
        c, s = np.cos(angle), np.sin(angle)
        rotation_matrix = np.array([
            [c, 0, s, 0],
            [0, 1, 0, 0],
            [-s, 0, c, 0],
            [0, 0, 0, 1]
        ])
        self.apply_transform(rotation_matrix)

    def rotate_z(self, angle):
        # Create a rotation matrix around the Z-axis
        c, s = np.cos(angle), np.sin(angle)
        rotation_matrix = np.array([
            [c, -s, 0, 0],
            [s, c, 0, 0],
            [0, 0, 1, 0],
            [0, 0, 0, 1]
        ])
        self.apply_transform(rotation_matrix)

    def scale(self, sx, sy=None, sz=None):
        if sy is None:
            sy = sx
        if sz is None:
            sz = sx
        # Create a scaling matrix and apply it
        scaling_matrix = np.array([
            [sx, 0, 0, 0],
            [0, sy, 0, 0],
            [0, 0, sz, 0],
            [0, 0, 0, 1]
        ])
        self.apply_transform(scaling_matrix)

    def abs_move(self, x=None, y=None, z=None, **kwargs):
        if x is None:
            x = self.current_position["x"]
        if y is None:
            y = self.current_position["y"]
        if z is None:
            z = self.current_position["z"]
        super(GMatrix3D, self).abs_move(x, y, z, **kwargs)

    def move(self, x=None, y=None, z=None, **kwargs):
        x_p, y_p, z_p = self._transform_point(x, y, z)
        super(GMatrix3D, self).move(x_p, y_p, z_p, **kwargs)

    def _transform_point(self, x, y, z):
        current_matrix = self.get_current_matrix()

        if x is None:
            x = 0
        if y is None:
            y = 0
        if z is None:
            z = 0

        transformed_point = current_matrix @ np.array([x, y, z, 1])
        return transformed_point[:3]  # Return only x, y, z