def test_apply_transformation_multiple_times(sx, sy, sz, doc1: 'Drawing'): def insert(): return Insert.new(dxfattribs={ 'name': 'AXIS', 'insert': (0, 0, 0), 'xscale': 1, 'yscale': 1, 'zscale': 1, 'rotation': 0, }, doc=doc1), [(0, 0, 0), X_AXIS, Y_AXIS, Z_AXIS] entity, vertices = insert() m = Matrix44.chain( Matrix44.scale(sx, sy, sz), Matrix44.z_rotate(math.radians(10)), Matrix44.translate(1, 1, 1), ) for i in range(5): entity, vertices = synced_transformation(entity, vertices, m) points = list(vertices) for num, line in enumerate(entity.virtual_entities()): assert points[0].isclose(line.dxf.start, abs_tol=1e-9) assert points[num + 1].isclose(line.dxf.end, abs_tol=1e-9)
def matrix44(self) -> Matrix44: """ Returns a transformation :class:`Matrix44` object to transform block entities into WCS. .. versionadded:: 0.13 """ dxf = self.dxf sx = dxf.xscale sy = dxf.yscale sz = dxf.zscale ocs = self.ocs() extrusion = ocs.uz ux = Vector(ocs.to_wcs(X_AXIS)) uy = Vector(ocs.to_wcs(Y_AXIS)) m = Matrix44.ucs(ux=ux * sx, uy=uy * sy, uz=extrusion * sz) angle = math.radians(dxf.rotation) if angle != 0.0: m = Matrix44.chain(m, Matrix44.axis_rotate(extrusion, angle)) insert = ocs.to_wcs(dxf.get('insert', Vector())) block_layout = self.block() if block_layout is not None: # transform block base point into WCS without translation insert -= m.transform_direction(block_layout.block.dxf.base_point) # set translation m.set_row(3, insert.xyz) return m
def test_apply_transformation_multiple_times(sx, sy, sz, doc1: "Drawing"): def insert(): return ( Insert.new( dxfattribs={ "name": "AXIS", "insert": (0, 0, 0), "xscale": 1, "yscale": 1, "zscale": 1, "rotation": 0, }, doc=doc1, ), [(0, 0, 0), X_AXIS, Y_AXIS, Z_AXIS], ) entity, vertices = insert() m = Matrix44.chain( Matrix44.scale(sx, sy, sz), Matrix44.z_rotate(math.radians(10)), Matrix44.translate(1, 1, 1), ) for i in range(5): entity, vertices = synced_transformation(entity, vertices, m) points = list(vertices) for num, line in enumerate(entity.virtual_entities()): assert points[0].isclose(line.dxf.start, abs_tol=1e-6) assert points[num + 1].isclose(line.dxf.end, abs_tol=1e-6)
def build(): circle = Circle() vertices = list(circle.vertices(linspace(0, 360, vertex_count, endpoint=False))) m = Matrix44.chain( Matrix44.axis_rotate(axis=Vec3.random(), angle=random.uniform(0, math.tau)), Matrix44.translate(dx=random.uniform(-2, 2), dy=random.uniform(-2, 2), dz=random.uniform(-2, 2)), ) return synced_transformation(circle, vertices, m)
def build(angle, dx, dy, dz, axis, start, end, count): ellipse = Ellipse.new(dxfattribs={ 'start_param': start, 'end_param': end, }) vertices = list(ellipse.vertices(ellipse.params(count))) m = Matrix44.chain(Matrix44.axis_rotate(axis=axis, angle=angle), Matrix44.translate(dx=dx, dy=dy, dz=dz)) return synced_transformation(ellipse, vertices, m)
def test_circle_default_ocs(): circle = Circle.new(dxfattribs={'center': (2, 3, 4), 'thickness': 2}) # 1. rotation - 2. scaling - 3. translation m = Matrix44.chain(Matrix44.scale(2, 2, 3), Matrix44.translate(1, 1, 1)) # default extrusion is (0, 0, 1), therefore scale(2, 2, ..) is a uniform scaling in the xy-play of the OCS circle.transform(m) assert circle.dxf.center == (5, 7, 13) assert circle.dxf.extrusion == (0, 0, 1) assert circle.dxf.thickness == 6
def build(): arc = Arc.new(dxfattribs={ 'start_angle': random.uniform(0, 360), 'end_angle': random.uniform(0, 360), }) vertices = list(arc.vertices(arc.angles(vertex_count))) m = Matrix44.chain( Matrix44.axis_rotate(axis=Vec3.random(), angle=random.uniform(0, math.tau)), Matrix44.translate(dx=random.uniform(-2, 2), dy=random.uniform(-2, 2), dz=random.uniform(-2, 2)), ) return synced_transformation(arc, vertices, m)
def build(): ellipse = Ellipse.new(dxfattribs={ 'start_param': start, 'end_param': end, }) vertices = list(ellipse.vertices(ellipse.params(vertex_count))) m = Matrix44.chain( Matrix44.axis_rotate(axis=Vec3.random(), angle=random.uniform(0, math.tau)), Matrix44.translate(dx=random.uniform(-2, 2), dy=random.uniform(-2, 2), dz=random.uniform(-2, 2)), ) return synced_transformation(ellipse, vertices, m)
def main_multi_ellipse(layout): m = Matrix44.chain( Matrix44.scale(1.1, 1.3, 1), Matrix44.z_rotate(math.radians(10)), Matrix44.translate(1, 1, 0), ) entity, vertices, axis_vertices = ellipse(start=math.pi / 2, end=-math.pi / 2) for index in range(5): entity, vertices = synced_transformation(entity, vertices, m) add(layout, entity, vertices)
def test_xline_transform(): # same implementation for Ray() xline = XLine.new(dxfattribs={ "start": (2, 3, 4), "unit_vector": (1, 0, 0) }) # 1. scaling - 2. rotation - 3. translation m = Matrix44.chain(Matrix44.scale(2, 2, 3), Matrix44.translate(1, 1, 1)) xline.transform(m) assert xline.dxf.start == (5, 7, 13) assert xline.dxf.unit_vector == (1, 0, 0)
def test_scale_and_reflexion(rx, ry, text2): insert = Vec3(0, 0, 0) m = Matrix44.chain( Matrix44.scale(2 * rx, 3 * ry, 1), Matrix44.z_rotate(math.radians(45)), Matrix44.translate(3 * rx, 3 * ry, 0), ) text2.transform(m) check_point = m.transform(insert) ocs = text2.ocs() assert ocs.to_wcs(text2.dxf.insert).isclose(check_point) assert math.isclose(text2.dxf.height, 3.0) assert math.isclose(text2.dxf.width, 2.0 / 3.0)
def test_random_block_reference_transformation(sx, sy, sz, doc1: "Drawing"): def insert(): return ( Insert.new( dxfattribs={ "name": "AXIS", "insert": (0, 0, 0), "xscale": 1, "yscale": 1, "zscale": 1, "rotation": 0, "layer": "insert", }, doc=doc1, ), [Vec3(0, 0, 0), X_AXIS, Y_AXIS, Z_AXIS], ) def check(lines, chk): origin, x, y, z = chk l1, l2, l3 = lines assert origin.isclose(l1.dxf.start) assert x.isclose(l1.dxf.end) assert origin.isclose(l2.dxf.start) assert y.isclose(l2.dxf.end) assert origin.isclose(l3.dxf.start) assert z.isclose(l3.dxf.end) entity0, vertices0 = insert() entity0, vertices0 = synced_scaling(entity0, vertices0, 1, 2, 3) m = Matrix44.chain( # Transformation order is important: scale - rotate - translate # Because scaling after rotation leads to a non orthogonal # coordinate system, which can not represented by the # INSERT entity. Matrix44.scale(sx, sy, sz), Matrix44.axis_rotate(axis=Vec3.random(), angle=random.uniform(0, math.tau)), Matrix44.translate( dx=random.uniform(-2, 2), dy=random.uniform(-2, 2), dz=random.uniform(-2, 2), ), ) entity, vertices = synced_transformation(entity0, vertices0, m) lines = list(entity.virtual_entities()) check(lines, vertices)
def test_arc_default_ocs(): arc = Arc.new(dxfattribs={'center': (2, 3, 4), 'thickness': 2, 'start_angle': 30, 'end_angle': 60}) # 1. rotation - 2. scaling - 3. translation m = Matrix44.chain(Matrix44.scale(2, 2, 3), Matrix44.translate(1, 1, 1)) # default extrusion is (0, 0, 1), therefore scale(2, 2, ..) is a uniform scaling in the xy-play of the OCS arc.transform(m) assert arc.dxf.center == (5, 7, 13) assert arc.dxf.extrusion == (0, 0, 1) assert arc.dxf.thickness == 6 assert math.isclose(arc.dxf.start_angle, 30, abs_tol=1e-9) assert math.isclose(arc.dxf.end_angle, 60, abs_tol=1e-9) arc.transform(Matrix44.z_rotate(math.radians(30))) assert math.isclose(arc.dxf.start_angle, 60, abs_tol=1e-9) assert math.isclose(arc.dxf.end_angle, 90, abs_tol=1e-9)
def main_ellipse_hatch(layout, spline=False): def draw_ellipse_axis(ellipse): center = ellipse.center major_axis = ellipse.major_axis msp.add_line(center, center + major_axis) entitydb = layout.doc.entitydb hatch = cast(Hatch, layout.add_hatch(color=1)) path = hatch.paths.add_edge_path() path.add_line((0, 0), (5, 0)) path.add_ellipse((2.5, 0), (2.5, 0), ratio=.5, start_angle=0, end_angle=180, ccw=1) if spline: hatch.paths.all_to_line_edges(spline_factor=4) chk_ellipse, chk_vertices, _ = ellipse((2.5, 0), ratio=0.5, start=0, end=math.pi) chk_ellipse, chk_vertices = synced_translation(chk_ellipse, chk_vertices, dx=2.5) m = Matrix44.chain( Matrix44.scale(1.1, 1.3, 1), Matrix44.z_rotate(math.radians(15)), Matrix44.translate(1, 1, 0), ) for index in range(3): color = 2 + index hatch = hatch.copy() entitydb.add(hatch) hatch.dxf.color = color hatch.transform(m) layout.add_entity(hatch) ellipse_edge = hatch.paths[0].edges[1] if not spline: draw_ellipse_axis(ellipse_edge) chk_ellipse, chk_vertices = synced_transformation( chk_ellipse, chk_vertices, m) add(layout, chk_ellipse, chk_vertices)
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 test_circle_user_ocs(): center = (2, 3, 4) extrusion = (0, 1, 0) circle = Circle.new( dxfattribs={'center': center, 'extrusion': extrusion, 'thickness': 2}) ocs = OCS(extrusion) v = ocs.to_wcs(center) # (-2, 4, 3) v = Vector(v.x * 2, v.y * 4, v.z * 2) v += (1, 1, 1) # and back to OCS, extrusion is unchanged result = ocs.from_wcs(v) m = Matrix44.chain(Matrix44.scale(2, 4, 2), Matrix44.translate(1, 1, 1)) circle.transform(m) assert circle.dxf.center == result assert circle.dxf.extrusion == (0, 1, 0) assert circle.dxf.thickness == 8 # in WCS y-axis
def main_non_uniform_hatch_polyline(layout, spline=False): entitydb = layout.doc.entitydb hatch, lwpolyline = hatch_polyline(layout) if spline: hatch.paths.all_to_spline_edges() m = Matrix44.chain( Matrix44.scale(-1.1, 1.1, 1), Matrix44.z_rotate(math.radians(10)), Matrix44.translate(1, 1, 1), ) for index in range(4): color = 2 + index hatch = hatch.copy() entitydb.add(hatch) hatch.dxf.color = color hatch.transform(m) layout.add_entity(hatch)
def test_reflections(self, s, e, rotation, sx, sy): m = Matrix44.chain( Matrix44.scale(sx, sy, 1), Matrix44.z_rotate(rotation), ) expected_start = m.transform(Vec3.from_deg_angle(s)) expected_end = m.transform(Vec3.from_deg_angle(e)) expected_angle_span = arc_angle_span_deg(s, e) ocs = OCSTransform(Z_AXIS, m) new_s, new_e = ocs.transform_ccw_arc_angles_deg(s, e) wcs_start = ocs.new_ocs.to_wcs(Vec3.from_deg_angle(new_s)) wcs_end = ocs.new_ocs.to_wcs(Vec3.from_deg_angle(new_e)) assert arc_angle_span_deg(new_s, new_e) == pytest.approx( expected_angle_span ) assert wcs_start.isclose(expected_start) assert wcs_end.isclose(expected_end)
def main_insert2(layout): entity, vertices = insert() m = Matrix44.chain( Matrix44.scale(-1.1, 1.1, 1), Matrix44.z_rotate(math.radians(10)), Matrix44.translate(1, 1, 1), ) doc.layers.new('exploded axis', dxfattribs={'color': -7}) for i in range(5): entity, vertices = synced_transformation(entity, vertices, m) layout.entitydb.add(entity) layout.add_entity(entity) origin, x, y, z = list(vertices) layout.add_line(origin, x, dxfattribs={ 'color': 2, 'layer': 'new axis' }) layout.add_line(origin, y, dxfattribs={ 'color': 4, 'layer': 'new axis' }) layout.add_line(origin, z, dxfattribs={ 'color': 6, 'layer': 'new axis' }) for line in entity.virtual_entities(): line.dxf.layer = 'exploded axis' line.dxf.color = 7 layout.entitydb.add(line) layout.add_entity(line)
def main_uniform_hatch_polyline(layout): entitydb = layout.doc.entitydb hatch, lwpolyline = hatch_polyline(layout, edge_path=False) m = Matrix44.chain( Matrix44.scale(-1.1, 1.1, 1), Matrix44.z_rotate(math.radians(10)), Matrix44.translate(1, 1, 1), ) for index in range(4): color = 2 + index hatch = hatch.copy() entitydb.add(hatch) hatch.dxf.color = color hatch.transform(m) lwpolyline = lwpolyline.copy() entitydb.add(lwpolyline) lwpolyline.dxf.color = color lwpolyline.transform(m) layout.add_entity(lwpolyline) layout.add_entity(hatch)
def get_transformation(): """ Apply rotation, width factor, translation to the insertion point and if necessary transformation from OCS to WCS. """ # TODO: text generation flags - mirror-x and mirror-y angle = math.radians(entity.dxf.rotation) width_factor = entity.dxf.width if align == 'LEFT': location = p1 elif align in ('ALIGNED', 'FIT'): width_factor = 1.0 # text goes from p1 to p2, no stretching applied location = p1.lerp(p2, factor=0.5) angle = (p2 - p1).angle # override stored angle else: location = p2 m = Matrix44.chain( Matrix44.scale(width_factor, 1, 1), Matrix44.z_rotate(angle), Matrix44.translate(location.x, location.y, location.z), ) ocs = entity.ocs() if ocs.transform: m *= ocs.matrix return m
def main_mtext(layout): content = '{}RSKNZQ' def mtext(num): height = 1.0 width = 1.0 p1 = Vector(0, 0, 0) t = MText.new(dxfattribs={ 'char_height': height, 'width': width, 'text_direction': (1, 0, 0), 'attachment_point': 7, 'layer': 'text', }, doc=doc) t.text = content.format(num) tlen = height * len(t.text) * width p2 = p1.replace(x=tlen) p3 = p2.replace(y=height) p4 = p1.replace(y=height) v = [p1, p2, p3, p4, p3.lerp(p4), p2.lerp(p3)] return t, v def add_box(vertices): p1, p2, p3, p4, center_top, center_right = vertices layout.add_line(p1, p2, dxfattribs={'color': 1, 'layer': 'rect'}) layout.add_line(p2, p3, dxfattribs={'color': 3, 'layer': 'rect'}) layout.add_line(p3, p4, dxfattribs={'color': 1, 'layer': 'rect'}) layout.add_line(p4, p1, dxfattribs={'color': 3, 'layer': 'rect'}) layout.add_line(center_right, p1, dxfattribs={ 'color': 2, 'layer': 'rect' }) layout.add_line(center_right, p4, dxfattribs={ 'color': 2, 'layer': 'rect' }) layout.add_line(center_top, p1, dxfattribs={ 'color': 4, 'layer': 'rect' }) layout.add_line(center_top, p2, dxfattribs={ 'color': 4, 'layer': 'rect' }) entity0, vertices0 = mtext(1) entity0, vertices0 = synced_rotation(entity0, vertices0, axis=Z_AXIS, angle=math.radians(30)) entity0, vertices0 = synced_translation(entity0, vertices0, dx=3, dy=3) for i, reflexion in enumerate([(1, 2), (-1, 2), (-1, -2), (1, -2)]): rx, ry = reflexion m = Matrix44.chain(Matrix44.scale(rx, ry, 1), ) entity, vertices = synced_transformation(entity0, vertices0, m) entity.text = content.format(i + 1) layout.add_entity(entity) add_box(vertices)
from ezdxf.math import UCS, Matrix44 from pathlib import Path OUT_DIR = Path('~/Desktop/Outbox').expanduser() doc = ezdxf.new('R2010') msp = doc.modelspace() # Using an UCS simplifies 3D operations, but UCS definition can happen later # calculating corner points in local (UCS) coordinates without Vec3 class angle = math.radians(360 / 5) corners_ucs = [(math.cos(angle * n), math.sin(angle * n), 0) for n in range(5)] # let's do some transformations tmatrix = Matrix44.chain( # creating a transformation matrix Matrix44.z_rotate(math.radians(15)), # 1. rotation around z-axis Matrix44.translate(0, .333, .333), # 2. translation ) transformed_corners_ucs = tmatrix.transform_vertices(corners_ucs) # transform UCS into WCS ucs = UCS( origin=(0, 2, 2), # center of pentagon ux=(1, 0, 0), # x-axis parallel to WCS x-axis uz=(0, 1, 1), # z-axis ) corners_wcs = list(ucs.points_to_wcs(transformed_corners_ucs)) msp.add_polyline3d( points=corners_wcs, dxfattribs={ 'closed': True,
def tmatrix(dx, dy, sx=1, sy=1, angle=0): return Matrix44.chain( Matrix44.scale(sx=sx, sy=sy, sz=1), Matrix44.z_rotate(radians(angle)), Matrix44.translate(dx, dy, 0), )
def tmatrix(x, y, angle): return Matrix44.chain( Matrix44.z_rotate(radians(angle)), Matrix44.translate(x, y, 0), )
def m44(): return Matrix44.chain( Matrix44.z_rotate(math.pi / 2), Matrix44.translate(1, 2, 0), )
def matrix(self): return Matrix44.chain( Matrix44.x_rotate(0.75), Matrix44.translate(2, 3, 4), )
def main_text(layout): content = "{}RSKNZQ" def text(num): height = 1.0 width = 1.0 p1 = Vec3(0, 0, 0) t = Text.new( dxfattribs={ "text": content.format(num), # should easily show reflexion errors "height": height, "width": width, "rotation": 0, "layer": "text", }, doc=doc, ) t.set_pos(p1, align="LEFT") tlen = height * len(t.dxf.text) * width p2 = p1.replace(x=tlen) p3 = p2.replace(y=height) p4 = p1.replace(y=height) v = [p1, p2, p3, p4, p3.lerp(p4), p2.lerp(p3)] return t, v def add_box(vertices): p1, p2, p3, p4, center_top, center_right = vertices layout.add_line(p1, p2, dxfattribs={"color": 1, "layer": "rect"}) layout.add_line(p2, p3, dxfattribs={"color": 3, "layer": "rect"}) layout.add_line(p3, p4, dxfattribs={"color": 1, "layer": "rect"}) layout.add_line(p4, p1, dxfattribs={"color": 3, "layer": "rect"}) layout.add_line(center_right, p1, dxfattribs={ "color": 2, "layer": "rect" }) layout.add_line(center_right, p4, dxfattribs={ "color": 2, "layer": "rect" }) layout.add_line(center_top, p1, dxfattribs={ "color": 4, "layer": "rect" }) layout.add_line(center_top, p2, dxfattribs={ "color": 4, "layer": "rect" }) entity0, vertices0 = text(1) entity0, vertices0 = synced_rotation(entity0, vertices0, axis=Z_AXIS, angle=math.radians(30)) entity0, vertices0 = synced_translation(entity0, vertices0, dx=3, dy=3) for i, reflexion in enumerate([(1, 2), (-1, 2), (-1, -2), (1, -2)]): rx, ry = reflexion m = Matrix44.chain(Matrix44.scale(rx, ry, 1), ) entity, vertices = synced_transformation(entity0, vertices0, m) entity.dxf.text = content.format(i + 1) layout.add_entity(entity) add_box(vertices)