def test_transform_mtext_extrusion(self): """The extrusion vector is always created by the right-hand-rule from the transformed x- and y-axis: Z = X "cross" Y. """ mtext = MTextData() m = Matrix44.scale(-1, 1, 1) mtext.transform(WCSTransform(m)) assert mtext.text_direction.isclose(m.transform(X_AXIS)) assert mtext.extrusion.isclose( -m.transform(Z_AXIS)), "expected reversed z-axis"
def rotate_local_z(self, angle: float) -> "UCS": """Returns a new rotated UCS, rotation axis is the local z-axis. Args: angle: rotation angle in radians """ t = Matrix44.axis_rotate(self.uz, angle) ux, uy = t.transform_vertices([self.ux, self.uy]) return UCS(origin=self.origin, ux=ux, uy=uy, uz=self.uz)
def _matrix_to_qtransform(matrix: Matrix44) -> qg.QTransform: """ Qt also uses row-vectors so the translation elements are placed in the bottom row This is only a simple conversion which assumes that although the transformation is 4x4, it does not involve the z axis. A more correct transformation could be implemented like so: https://stackoverflow.com/questions/10629737/convert-3d-4x4-rotation-matrix-into-2d """ return qg.QTransform(*matrix.get_2d_transformation())
def rotate_axis(self, axis: "Vertex", angle: float) -> "DXFGraphic": """Rotate entity inplace about vector `axis`, returns `self` (floating interface). Args: axis: rotation axis as tuple or :class:`Vec3` angle: rotation angle in radians """ return self.transform(Matrix44.axis_rotate(axis, angle))
def test_to_polylines2d_with_ocs(self, path1): m = Matrix44.x_rotate(math.pi / 4) path = path1.transform(m) extrusion = m.transform((0, 0, 1)) polylines = list(to_polylines2d(path, extrusion=extrusion)) p0 = polylines[0] assert p0.dxf.elevation.isclose((0, 0, 1)) assert p0.dxf.extrusion.isclose(extrusion) assert p0[0].dxf.location.isclose((0, 0, 1)) assert p0[-1].dxf.location.isclose((4, 0, 1))
def rotate_z(self, angle: float): """Rotate mesh around z-axis about `angle` inplace. Args: angle: rotation angle in radians """ self.vertices = list( Matrix44.z_rotate(angle).transform_vertices(self.vertices)) return self
def transform(self, m: Matrix44) -> 'Point': """ Transform POINT entity by transformation matrix `m` inplace. .. versionadded:: 0.13 """ self.dxf.location = m.transform(self.dxf.location) transform_thickness_and_extrusion_without_ocs(self, m) # ignore dxf.angle! return self
def test_matrix44_to_wcs(): ocs = OCS(EXTRUSION) matrix = Matrix44.ucs(ocs.ux, ocs.uy, ocs.uz) matrix.transpose() assert is_close_points( matrix.transform( (9.41378764657076, 13.15481838975576, 0.8689258932616031)), (-9.56460754, 8.44764172, 9.97894327), places=6, )
def test_transform(): point = Point.new( dxfattribs={ "location": (2, 3, 4), "extrusion": (0, 1, 0), "thickness": 2, } ) # 1. rotation - 2. scaling - 3. translation m = Matrix44.chain(Matrix44.scale(2, 3, 1), Matrix44.translate(1, 1, 1)) point.transform(m) assert point.dxf.location == (5, 10, 5) assert point.dxf.extrusion == (0, 1, 0) assert point.dxf.thickness == 6 angle = math.pi / 4 point.transform(Matrix44.z_rotate(math.pi / 4)) assert point.dxf.extrusion.isclose((-math.cos(angle), math.sin(angle), 0)) assert math.isclose(point.dxf.thickness, 6)
def translate(self, dx: float, dy: float, dz: float) -> "XLine": """Optimized XLINE/RAY translation about `dx` in x-axis, `dy` in y-axis and `dz` in z-axis. """ self.dxf.start = Vec3(dx, dy, dz) + self.dxf.start # Avoid Matrix44 instantiation if not required: if self.is_post_transform_required: self.post_transform(Matrix44.translate(dx, dy, dz)) return self
def rotate_local_x(self, angle: float) -> 'UCS': """ Returns a new rotated UCS, rotation axis is the local x-axis. Args: angle: rotation angle in radians """ t = Matrix44.axis_rotate(self.ux, angle) uy, uz = t.transform_vertices([self.uy, self.uz]) return UCS(origin=self.origin, ux=self.ux, uy=uy, uz=uz)
def translate(self, dx: float, dy: float, dz: float) -> "Ellipse": """Optimized ELLIPSE translation about `dx` in x-axis, `dy` in y-axis and `dz` in z-axis, returns `self` (floating interface). """ self.dxf.center = Vec3(dx, dy, dz) + self.dxf.center # Avoid Matrix44 instantiation if not required: if self.is_post_transform_required: self.post_transform(Matrix44.translate(dx, dy, dz)) return self
def test_draw_scaled_path(self, backend): p1 = Path((1, 2)) p1.line_to((2, 3)) backend.draw_path(p1, Properties()) f = backend.factor expected_p2 = p1.transform(Matrix44.scale(f, f, f)) p2 = backend.collector[0][1] for v1, v2 in zip(p2.control_vertices(), expected_p2.control_vertices()): assert v1.isclose(v2)
def transform(self, m: Matrix44) -> None: """ Transform vertices by transformation matrix `m`. .. versionadded:: 0.13 """ values = array('d') for vertex in m.transform_vertices(self): values.extend(vertex) self.values = values
def rotate_z(self, angle: float) -> 'DXFGraphic': """ Rotate entity inplace about z-axis, returns `self` (floating interface). Args: angle: rotation angle in radians .. versionadded:: 0.13 """ return self.transform(Matrix44.z_rotate(angle))
def translate(self, dx: float, dy: float, dz: float) -> 'DXFGraphic': """ Translate entity inplace about `dx` in x-axis, `dy` in y-axis and `dz` in z-axis, returns `self` (floating interface). Basic implementation uses the :meth:`transform` interface, subclasses may have faster implementations. .. versionadded:: 0.13 """ return self.transform(Matrix44.translate(dx, dy, dz))
def rotate_y(self, angle: float): """ Rotate mesh around y-axis about `angle` inplace. Args: angle: rotation angle in radians """ self.vertices = Matrix44.y_rotate(angle).transform_vectors(self.vertices) return self
def test_to_lwpolylines_with_ocs(self, path1): m = Matrix44.x_rotate(math.pi / 4) path = path1.transform(m) extrusion = m.transform((0, 0, 1)) polylines = list(to_lwpolylines(path, extrusion=extrusion)) p0 = polylines[0] assert p0.dxf.elevation == pytest.approx(1) assert p0.dxf.extrusion.isclose(extrusion) assert p0[0] == (0, 0, 0, 0, 0) assert p0[-1] == (4, 0, 0, 0, 0)
def test_transform(p1): p2 = p1.transform(Matrix44.translate(1, 1, 0)) assert p2.start.isclose((1, 1)) assert p2[0].end.isclose((3, 1)) # line to location assert p2[1].end.isclose((5, 1)) # cubic to location assert p2[1].ctrl1.isclose((3, 2)) # cubic ctrl1 assert p2[1].ctrl2.isclose((5, 2)) # cubic ctrl2 assert p2[2].end.isclose((7, 1)) # quadratic to location assert p2[2].ctrl.isclose((6, 0)) # quadratic ctrl assert p2.end.isclose((7, 1))
def test_transform(p1): p2 = p1.transform(Matrix44.translate(1, 1, 0)) assert p2.start == (1, 1) assert p2[0].end == (3, 1) # line to location assert p2[1].end == (5, 1) # cubic to location assert p2[1].ctrl1 == (3, 2) # cubic ctrl1 assert p2[1].ctrl2 == (5, 2) # cubic ctrl2 assert p2[2].end == (7, 1) # quadratic to location assert p2[2].ctrl == (6, 0) # quadratic ctrl assert p2.end == (7, 1)
def test_insert_transform_interface(): insert = Insert() insert.dxf.insert = (1, 0, 0) insert.transform(Matrix44.translate(1, 2, 3)) assert insert.dxf.insert == (2, 2, 3) # optimized translate implementation insert.translate(-1, -2, -3) assert insert.dxf.insert == (1, 0, 0)
def transform(self, m: Matrix44) -> 'Line': """ Transform LINE entity by transformation matrix `m` inplace. .. versionadded:: 0.13 """ start, end = m.transform_vertices([self.dxf.start, self.dxf.end]) self.dxf.start = start self.dxf.end = end transform_thickness_and_extrusion_without_ocs(self, m) return self
def transform(self, m: Matrix44) -> 'Face3d': """ Transform 3DFACE entity by transformation matrix `m` inplace. .. versionadded:: 0.13 """ dxf = self.dxf # 3DFACE is a real 3d entity dxf.vtx0, dxf.vtx1, dxf.vtx2, dxf.vtx3 = m.transform_vertices( (dxf.vtx0, dxf.vtx1, dxf.vtx2, dxf.vtx3)) return self
def test_multi_path_objects(self): path = Path() path.line_to((1, 0, 0)) path.move_to((2, 0, 0)) paths = transform_paths([path], Matrix44.translate(0, 1, 0)) assert len(paths) == 1 path2 = paths[0] assert path2.start.isclose((0, 1, 0)) assert len(path2) == 2 assert path2.end.isclose((2, 1, 0)) assert path2.has_sub_paths is True
def test_transform_interface(): text = Text() text.dxf.insert = (1, 0, 0) text.transform(Matrix44.translate(1, 2, 3)) assert text.dxf.insert == (2, 2, 3) # optimized translate text.dxf.align_point = (3, 2, 1) text.translate(1, 2, 3) assert text.dxf.insert == (3, 4, 6) assert text.dxf.align_point == (4, 4, 4)
def rotate_axis(self, axis: 'Vertex', angle: float) -> 'DXFGraphic': """ Rotate entity inplace about vector `axis`, returns `self` (floating interface). Args: axis: rotation axis as tuple or :class:`Vector` angle: rotation angle in radians .. versionadded:: 0.13 """ return self.transform(Matrix44.axis_rotate(axis, angle))
def test_to_hatches_with_ocs(self, path1): m = Matrix44.x_rotate(math.pi / 4) path = path1.transform(m) extrusion = m.transform((0, 0, 1)) hatches = list(to_hatches(path, extrusion=extrusion)) h0 = hatches[0] assert h0.dxf.elevation == (0, 0, 1) assert h0.dxf.extrusion.isclose(extrusion) polypath0 = h0.paths[0] assert polypath0.vertices[0] == (0, 0, 0) # x, y, bulge assert polypath0.vertices[-1] == (0, 0, 0), "should be closed automatically"
def has_matrix_2d_stretching(m: Matrix44) -> bool: """Returns ``True`` if matrix `m` performs a non-uniform xy-scaling. Uniform scaling is not stretching in this context. Does not check if the target system is a cartesian coordinate system, use the :class:`~ezdxf.math.Matrix44` property :attr:`~ezdxf.math.Matrix44.is_cartesian` for that. """ ux = m.transform_direction(X_AXIS) uy = m.transform_direction(Y_AXIS) return not math.isclose(ux.magnitude_square, uy.magnitude_square)
def transform(self, m: Matrix44) -> 'Bezier': """ General transformation interface, returns a new :class:`Bezier` curve. Args: m: 4x4 transformation matrix (:class:`ezdxf.math.Matrix44`) .. versionadded:: 0.14 """ defpoints = tuple(m.transform_vertices(self.control_points)) return Bezier(defpoints)
def __init__(self, extrusion: Vec3 = None, m: Matrix44 = None): if m is None: self.m = Matrix44() else: self.m = m self.scale_uniform: bool = True if extrusion is None: # fill in dummy values self._reset_ocs(_PLACEHOLDER_OCS, _PLACEHOLDER_OCS, True) else: new_extrusion, scale_uniform = transform_extrusion(extrusion, m) self._reset_ocs(OCS(extrusion), OCS(new_extrusion), scale_uniform)