def apply(self, pt: "Point2D") -> "Point2D": """ :param pt: the point to which the transformation should be applied :return: the point after transformation """ pmx = Matrix.from_point_with_padding(pt) return Point2D(*(self._mtx * pmx).as_list()[:2])
def build(self) -> "Matrix": """ :return: the compiled transformation matrix """ self._mtx = Matrix.identity_matrix(4) for step in self._steps: self._mtx = step.to_mtx() * self._mtx return self._mtx.clone()
def rotation_matrix(axis: "RotationAxis", rads: SupportsFloat = 0) -> "Matrix": """ :param axis: the axis around which to perform the rotation :param rads: the amount of desired rotation (in radians). Default: 0. :return: A matrix representing the desired rotation operation """ m = None c = math.cos(rads) s = math.sin(rads) if RotationAxis.x == axis: m = Matrix(4, 4, [1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]) elif RotationAxis.y == axis: m = Matrix(4, 4, [c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]) elif RotationAxis.z == axis: m = Matrix(4, 4, [c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]) return m
def get_unhinging_transformation(self): k = self._near_plane_dist / self._far_plane_dist tb = Transform3DBuilder() tb.custom( Matrix(4, 4, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 / (k - 1), k / (k - 1), 0, 0, -1, 0 ])) return tb
def translation_matrix(dx: Number = 0, dy: Number = 0) -> "Matrix": """ :param dx: change in the x direction. Default: 0. :param dy: change in the y direction. Default: 0. :return: A matrix representing the desired translation operation """ return Matrix(3, 3, [1, 0, dx, 0, 1, dy, 0, 0, 1])
def shearing_matrix(x: Number = 0, y: Number = 0) -> "Matrix": """ :param x: The amount of horizontal shear :param y: The amount of vertical shear :return: A matrix representing the desired shearing operation """ return Matrix(3, 3, [1, x, 0, y, 1, 0, 0, 0, 1])
def scaling_matrix(x: Number = 1, y: Number = 1) -> "Matrix": """ :param x: the amount of scaling in the x direction. Default: 1. :param y: the amount of scaling in the y direction. Default: 1. :return: A matrix representing the desired scale operation """ return Matrix(3, 3, [x, 0, 0, 0, y, 0, 0, 0, 1])
def reflect(pt: "Point3D", plane: "ReflectionPlane") -> "Point3D": """ Reflect the given point over the specified reflection plane :param pt: a 3D point to reflect :param plane: a ReflectionPlane3D value specifying the plane of reflection in 3D space. :return: A reflected point """ pmx = Matrix.from_point_with_padding(pt) tmx = Transform3D.reflection_matrix(plane) return Point3D(*(tmx * pmx).as_list()[:3])
def reflect(pt: "Point2D", line: "ReflectionLine") -> "Point2D": """ Reflect the given point over the specified reflection line :param pt: a 2D point to reflect :param line: a ReflectionLine2D value specifying the line of reflection :return: A reflected point """ pmx = Matrix.from_point_with_padding(pt) tmx = Transform2D.reflection_matrix(line) return Point2D(*(tmx * pmx).as_list()[:2])
def rotation_matrix(rads: SupportsFloat = 0) -> "Matrix": """ :param rads: the amount of desired rotation (in radians). Default: 0. :return: A matrix representing the desired rotation operation """ c = math.cos(rads) s = math.sin(rads) return Matrix(3, 3, [c, -s, 0, s, c, 0, 0, 0, 1])
def rotate(pt: "Point2D", rads: SupportsFloat = 0) -> "Point2D": """ Rotate a given point by the amount specified (in radians) :param pt: a 2D Point to rotate :param rads: the amount of desired rotation (in radians). Default: 0. :return: A point that has been rotated by the given amount """ pmx = Matrix.from_point_with_padding(pt) tmx = Transform2D.rotation_matrix(rads) return Point2D(*(tmx * pmx).as_list()[:2])
def translate(pt: "Point2D", dx: Number = 0, dy: Number = 0) -> "Point2D": """ Translate the given point by the x and y amounts specified :param pt: a 2D point to translate :param dx: change in the x direction. Default: 0. :param dy: change in the y direction. Default: 0. :return: A translated point """ pmx = Matrix.from_point_with_padding(pt) tmx = Transform2D.translation_matrix(dx, dy) return Point2D(*(tmx * pmx).as_list()[:2])
def shear(pt: "Point2D", x: Number = 0, y: Number = 0) -> "Point2D": """ Shear a given point by the horizontal and vertical amount specified :param pt: a 2D Point to shear :param x: The amount of horizontal shear. Default: 0. :param y: The amount of vertical shear. Default: 0. :return: A sheared point """ pmx = Matrix.from_point_with_padding(pt) tmx = Transform2D.shearing_matrix(x, y) return Point2D(*(tmx * pmx).as_list()[:2])
def scale(pt: "Point2D", x: Number = 1, y: Number = 1) -> "Point2D": """ Scale a given point by the x_scale and y_scale amounts :param pt: a 2D Point to scale :param x: the amount of scaling in the x direction. Default 1. :param y: the amount of scaling in the y direction. Default 1. :return: A scaled point """ pmx = Matrix.from_point_with_padding(pt) tmx = Transform2D.scaling_matrix(x, y) return Point2D(*(tmx * pmx).as_list()[:2])
def get_std_view_volume_transformation(self): tb = Transform3DBuilder() tb.translate(-self.pos.x, -self.pos.y, -self.pos.z)\ .custom(Matrix(4, 4, [self.u.x, self.u.y, self.u.z, 0, self.v.x, self.v.y, self.v.z, 0, self.w.x, self.w.y, self.w.z, 0, 0, 0, 0, 1]))\ .scale(1 / (self._far_plane_dist * math.tan(self._hor_rads / 2)), 1 / (self._far_plane_dist * math.tan(self._vert_rads / 2)), 1 / self._far_plane_dist) return tb
def apply(self, pt: "Point3D") -> "Point3D": """ :param pt: the point to which the transformation should be applied :return: the point after transformation """ if self._mtx is None: raise Exception('Matrix not built yet') pmx = Matrix.from_point_with_padding(pt) x, y, z, w = (self._mtx * pmx).as_list() # homogenize before returning point return Point3D(x / w, y / w, z / w)
def rotate(pt: "Point3D", axis: "RotationAxis", rads: SupportsFloat = 0) -> "Point3D": """ Rotate a given point by the amount specified (in radians) :param pt: a 3D Point to rotate :param axis: the axis around which to perform the rotation :param rads: the amount of desired rotation (in radians). Default: 0. :return: A point that has been rotated by the given amount """ pmx = Matrix.from_point_with_padding(pt) tmx = Transform3D.rotation_matrix(axis, rads) return Point3D(*(tmx * pmx).as_list()[:3])
def main(): builder = Transform3DBuilder() builder.shear(yx=3, yz=-2)\ .translate(-3, 10, 7)\ .scale(3, 3, 3)\ .rotate(RotationAxis.x, math.pi/4)\ .reflect(ReflectionPlane.origin) mtx = builder.build() rmtx = builder.build_reverse() pt1 = Point3D(3, 1, 5) print("Original Point") print(pt1) print("Transformed Point") pt2 = builder.apply(pt1) print(pt2) print("Transformation Reversed") pt3 = builder.apply_reverse(pt2) print(pt3) print("Forward Matrix") print(builder.build()) print("Forward Matrix Inversed") print(builder.build().inverse()) print("Reverse Matrix") print(builder.build_reverse()) m1 = Matrix(4, 4, [2, 5, 0, 8, 1, 4, 2, 6, 7, 8, 9, 3, 1, 5, 7, 8]) print(m1) print(m1.inverse()) m2 = m1 * m1.inverse() print(m2)
def scale(pt: "Point3D", x: Number = 1, y: Number = 1, z: Number = 1) -> "Point3D": """ Scale a given point by the x, y, and z amounts :param pt: a 3D Point to scale :param x: the amount of scaling in the x direction. Default: 1. :param y: the amount of scaling in the y direction. Default: 1. :param z: the amount of scaling in the z direction. Default: 1. :return: A scaled point """ pmx = Matrix.from_point_with_padding(pt) tmx = Transform3D.scaling_matrix(x, y, z) return Point3D(*(tmx * pmx).as_list()[:3])
def reflection_matrix(plane: "ReflectionPlane") -> Matrix: """ :param plane: the plane over which the reflection should be performed :return: A matrix representing the desired reflection operation """ d = { ReflectionPlane.xy: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1], ReflectionPlane.yz: [-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], ReflectionPlane.zx: [1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], ReflectionPlane.origin: [-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1] } return Matrix(4, 4, d[plane])
def shearing_matrix(xy: Number = 0, xz: Number = 0, yx: Number = 0, yz: Number = 0, zx: Number = 0, zy: Number = 0) -> "Matrix": """ :parameter xy: amount of shearing on x values in the y direction. Default: 0. :parameter xz: amount of shearing on x values in the z direction. Default: 0. :parameter yx: amount of shearing on y values in the x direction. Default: 0. :parameter yz: amount of shearing on y values in the z direction. Default: 0. :parameter zx: amount of shearing on z values in the x direction. Default: 0. :parameter zy: amount of shearing on z values in the y direction. Default: 0. :return: A matrix representing the desired shearing operation """ return Matrix(4, 4, [1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1])
def reflection_matrix(line: "ReflectionLine") -> Matrix: """ :param line: the line over which the reflection should be performed :return: A matrix representing the desired reflection operation """ d = {ReflectionLine.x: [1, 0, 0, 0, -1, 0, 0, 0, 1], ReflectionLine.y: [-1, 0, 0, 0, 1, 0, 0, 0, 1], ReflectionLine.origin: [-1, 0, 0, 0, -1, 0, 0, 0, 1], ReflectionLine.xy: [0, 1, 0, 1, 0, 0, 0, 0, 1]} return Matrix(3, 3, d[line])
def shear(pt: "Point3D", xy: Number = 0, xz: Number = 0, yx: Number = 0, yz: Number = 0, zx: Number = 0, zy: Number = 0) -> "Point3D": """ Shear a given point by the amounts specified :param pt: a 3D Point to shear. Default: 0. :parameter xy: amount of shearing on x values in the y direction. Default: 0. :parameter xz: amount of shearing on x values in the z direction. Default: 0. :parameter yx: amount of shearing on y values in the x direction. Default: 0. :parameter yz: amount of shearing on y values in the z direction. Default: 0. :parameter zy: amount of shearing on z values in the y direction. Default: 0. :parameter zx: amount of shearing on z values in the x direction. Default: 0. :return: A sheared point """ pmx = Matrix.from_point_with_padding(pt) tmx = Transform3D.shearing_matrix(xy, xz, yx, yz, zx, zy) return Point3D(*(tmx * pmx).as_list()[:3])
def __init__(self): """ Composites multiple transformations into a single transformation matrix for efficiency of computation. Can produce the actual finished matrix, or apply the matrix to a given 2D point. """ self._mtx = Matrix.identity_matrix(3)
def apply_custom_matrix(pt: "Point3D", mtx: "Matrix") -> "Point3D": pmx = Matrix.from_point_with_padding(pt) return Point3D(*(mtx * pmx).as_list()[:3])