def compose_matrix(scale=None, shear=None, angles=None, translation=None, perspective=None): """Calculates a matrix from the components of scale, shear, euler_angles, translation and perspective. Parameters ---------- scale : :obj:`list` of :obj:`float` The 3 scale factors in x-, y-, and z-direction. shear : :obj:`list` of :obj:`float` The 3 shear factors for x-y, x-z, and y-z axes. angles : :obj:`list` of :obj:`float` The rotation specified through the 3 Euler angles about static x, y, z axes. translation : :obj:`list` of :obj:`float` The 3 values of translation. perspective : :obj:`list` of :obj:`float` The 4 perspective entries of the matrix. Examples -------- >>> trans1 = [1, 2, 3] >>> angle1 = [-2.142, 1.141, -0.142] >>> scale1 = [0.123, 2, 0.5] >>> M = compose_matrix(scale1, None, angle1, trans1, None) >>> scale2, shear2, angle2, trans2, persp2 = decompose_matrix(M) >>> allclose(scale1, scale2) True >>> allclose(angle1, angle2) True >>> allclose(trans1, trans2) True """ M = [[1. if i == j else 0. for i in range(4)] for j in range(4)] if perspective is not None: P = matrix_from_perspective_entries(perspective) M = multiply_matrices(M, P) if translation is not None: T = matrix_from_translation(translation) M = multiply_matrices(M, T) if angles is not None: R = matrix_from_euler_angles(angles, static=True, axes="xyz") M = multiply_matrices(M, R) if shear is not None: Sh = matrix_from_shear_entries(shear) M = multiply_matrices(M, Sh) if scale is not None: Sc = matrix_from_scale_factors(scale) M = multiply_matrices(M, Sc) for i in range(4): for j in range(4): M[i][j] /= M[3][3] return M
def concatenate(self, other): """Concatenate two transformations into one ``Transformation``. Note: Rz * Ry * Rx means that Rx is first transformation, Ry second, and Rz third. """ cls = type(self) if not isinstance(other, cls): return Transformation(multiply_matrices(self.matrix, other.matrix)) else: return cls(multiply_matrices(self.matrix, other.matrix))
def concatenate(self, other): """Calculate dot product of two transformation matrices. Args: other (Transformation, Rotation,... list): The matrix to dot. """ if type(other) == type([]): if len(other) == 4 and len(other[0]) == 4: # concatenate with 4x4 transformation matrix return Transformation.from_matrix(multiply_matrices(self.matrix, other)) else: raise Exception("The type %s is not supported to be multiplied by this class." % type(other)) else: # concatenate with instances of Transformation, Rotation, Translation, etc. return Transformation.from_matrix(multiply_matrices(self.matrix, other.matrix))
def change_basis(cls, frame_from, frame_to): """Computes a change of basis transformation between two frames. A basis change is essentially a remapping of geometry from one coordinate system to another. Args: frame_from (:class:`Frame`): a frame defining the original Cartesian coordinate system frame_to (:class:`Frame`): a frame defining the targeted Cartesian coordinate system Example: >>> from compas.geometry import Point, Frame >>> f1 = Frame([2, 2, 2], [0.12, 0.58, 0.81], [-0.80, 0.53, -0.26]) >>> f2 = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15]) >>> T = Transformation.change_basis(f1, f2) >>> p_f1 = Point(1, 1, 1) # point in f1 >>> p_f1.transformed(T) # point represented in f2 Point(1.395, 0.955, 1.934) >>> Frame.local_to_local_coords(f1, f2, p_f1) Point(1.395, 0.955, 1.934) """ T1 = cls.from_frame(frame_from) T2 = cls.from_frame(frame_to) return cls(multiply_matrices(matrix_inverse(T2.matrix), T1.matrix))
def from_frame_to_frame(cls, frame_from, frame_to): """Computes a transformation between two frames. This transformation allows to transform geometry from one Cartesian coordinate system defined by "frame_from" to another Cartesian coordinate system defined by "frame_to". Parameters ---------- frame_from : :class:`Frame` A frame defining the original Cartesian coordinate system. frame_to : :class:`Frame` A frame defining the targeted Cartesian coordinate system. Returns ------- Transformation The transformation representing a change of basis. Examples -------- >>> from compas.geometry import Frame >>> f1 = Frame([2, 2, 2], [0.12, 0.58, 0.81], [-0.80, 0.53, -0.26]) >>> f2 = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15]) >>> T = Transformation.from_frame_to_frame(f1, f2) >>> f1.transform(T) >>> f1 == f2 True """ T1 = cls.from_frame(frame_from) T2 = cls.from_frame(frame_to) return cls(multiply_matrices(T2.matrix, matrix_inverse(T1.matrix)))
def from_frame_to_frame(cls, frame_from, frame_to): """Computes a change of basis transformation between two frames. This transformation maps geometry from one Cartesian coordinate system defined by "frame_from" to the other Cartesian coordinate system defined by "frame_to". Args: frame_from (:class:`Frame`): a frame defining the original Cartesian coordinate system frame_to (:class:`Frame`): a frame defining the targeted Cartesian coordinate system Example: >>> from compas.geometry import Frame >>> f1 = Frame([2, 2, 2], [0.12, 0.58, 0.81], [-0.80, 0.53, -0.26]) >>> f2 = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15]) >>> T = Transformation.from_frame_to_frame(f1, f2) >>> f1.transform(T) >>> f1 == f2 True """ T1 = cls.from_frame(frame_from) T2 = cls.from_frame(frame_to) return cls(multiply_matrices(T2.matrix, inverse(T1.matrix)))
def concatenated(self, other): """Concatenate two transformations into one ``Transformation``. Parameters ---------- other: :class:`compas.geometry.Transformation` The transformation object to concatenate. Returns ------- T: :class:`compas.geometry.Transformation` The new transformation that is the concatenation of this one and the other. Notes ----- Rz * Ry * Rx means that Rx is first transformation, Ry second, and Rz third. """ # T = self.copy() # T.concatenate(other) # return T cls = type(self) if isinstance(other, cls): return cls(multiply_matrices(self.matrix, other.matrix)) return Transformation(multiply_matrices(self.matrix, other.matrix))
def transform_vectors(vectors, T): """Transform multiple vectors with one transformation matrix. Parameters ---------- vectors : list of :class:`Vector` or list of list of float A list of vectors to be transformed. T : :class:`Transformation` list of list of float The transformation to apply. Examples -------- >>> vectors = [[1, 0, 0], [1, 2, 4], [4, 7, 1]] >>> T = matrix_from_axis_and_angle([0, 2, 0], math.radians(45), point=[4, 5, 6]) >>> vectors_transformed = transform_vectors(vectors, T) """ return dehomogenize(multiply_matrices(homogenize(vectors, w=0.0), transpose_matrix(T)))
def transform(self, xyz): """Transforms a point, vector, xyz coordinates or a list therefrom. Should this be split into transform_xyz_list and transform_xyz TODO: should be attached to the elements. """ xyz = list(xyz) if type(xyz[0]) == float or type(xyz[0]) == int: # point, vector, xyz coordinates point = xyz + [1.] # make homogeneous coordinates point = multiply_matrix_vector(self.matrix, point) return point[:3] else: # it is a list of xyz coordinates xyz = zip(*xyz) # transpose matrix xyz += [[1] * len(xyz[0])] # make homogeneous coordinates xyz = multiply_matrices(self.matrix, xyz) return zip(*xyz[:3]) # cutoff 1 and transpose again
def concatenate(self, other): """Concatenate another transformation to this transformation. Parameters ---------- other: :class:`compas.geometry.Transformation` The transformation object to concatenate. Returns ------- None This transformation object is changed in-place. Notes ----- Rz * Ry * Rx means that Rx is first transformation, Ry second, and Rz third. """ self.matrix = multiply_matrices(self.matrix, other.matrix)
def transform_frames(frames, T): """Transform multiple frames with one transformation matrix. Parameters ---------- frames : list of :class:`Frame` A list of frames to be transformed. T : :class:`Transformation` The transformation to apply on the frames. Examples -------- >>> frames = [Frame([1, 0, 0], [1, 2, 4], [4, 7, 1]), Frame([0, 2, 0], [5, 2, 1], [0, 2, 1])] >>> T = matrix_from_axis_and_angle([0, 2, 0], math.radians(45), point=[4, 5, 6]) >>> transformed_frames = transform_frames(frames, T) """ points_and_vectors = homogenize_and_flatten_frames(frames) return dehomogenize_and_unflatten_frames(multiply_matrices(points_and_vectors, transpose_matrix(T)))
def matrix_change_basis(frame_from, frame_to): """Computes a change of basis transformation between two frames. A basis change is essentially a remapping of geometry from one coordinate system to another. Parameters ---------- frame_from : :class:`Frame` A frame defining the original Cartesian coordinate system frame_to : :class:`Frame` A frame defining the targeted Cartesian coordinate system Example: >>> from compas.geometry import Point, Frame >>> f1 = Frame([2, 2, 2], [0.12, 0.58, 0.81], [-0.80, 0.53, -0.26]) >>> f2 = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15]) >>> T = matrix_change_basis(f1, f2) """ T1 = matrix_from_frame(frame_from) T2 = matrix_from_frame(frame_to) return multiply_matrices(matrix_inverse(T2), T1)
def matrix_from_frame_to_frame(frame_from, frame_to): """Computes a transformation between two frames. This transformation allows to transform geometry from one Cartesian coordinate system defined by "frame_from" to another Cartesian coordinate system defined by "frame_to". Parameters ---------- frame_from : :class:`Frame` A frame defining the original Cartesian coordinate system frame_to : :class:`Frame` A frame defining the targeted Cartesian coordinate system Examples -------- >>> f1 = Frame([2, 2, 2], [0.12, 0.58, 0.81], [-0.80, 0.53, -0.26]) >>> f2 = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15]) >>> T = matrix_from_frame_to_frame(f1, f2) """ T1 = matrix_from_frame(frame_from) T2 = matrix_from_frame(frame_to) return multiply_matrices(T2, matrix_inverse(T1))
def transform_vectors(vectors, T): return dehomogenize( multiply_matrices(homogenize(vectors, w=0.0), transpose_matrix(T)))
def transform_points(points, T): return dehomogenize( multiply_matrices(homogenize(points, w=1.0), transpose_matrix(T)))
def mesh_get_transformed_vertices(mesh, transformation_matrix): xyz = zip(*mesh.xyz) # transpose matrix xyz += [[1] * len(xyz[0])] # homogenize xyz = multiply_matrices(transformation_matrix, xyz) return zip(*xyz[:3])
def transform(points, T): points = homogenize(points) points = transpose_matrix(multiply_matrices(T, transpose_matrix(points))) return dehomogenize(points)
f1 = Frame([2, 2, 2], [0.12, 0.58, 0.81], [-0.80, 0.53, -0.26]) f2 = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15]) T = Transformation.from_frame_to_frame(f1, f2) f1.transform(T) print(f1 == f2) f = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15]) T = Transformation.from_frame(f) p = Point(0, 0, 0) p.transform(T) print(allclose(f.point, p)) f1 = Frame([1, 1, 1], [0.68, 0.68, 0.27], [-0.67, 0.73, -0.15]) T = Transformation.from_frame(f1) points = [[1.0, 1.0, 1.0], [1.68, 1.68, 1.27], [0.33, 1.73, 0.85]] points = transform_points(points, T) trans1 = [1, 2, 3] angle1 = [-2.142, 1.141, -0.142] scale1 = [0.123, 2, 0.5] T = matrix_from_translation(trans1) R = matrix_from_euler_angles(angle1) S = matrix_from_scale_factors(scale1) M = multiply_matrices(multiply_matrices(T, R), S) # M = compose_matrix(scale1, None, angle1, trans1, None) scale2, shear2, angle2, trans2, persp2 = decompose_matrix(M) print(allclose(scale1, scale2)) print(allclose(angle1, angle2)) print(allclose(trans1, trans2))