def test_three_polygons_from_one_hatch(): hatch = factory.new('HATCH') paths = hatch.paths paths.add_polyline_path(square(1), flags=1) paths.add_polyline_path(translate(square(1), (3, 1)), flags=1) paths.add_polyline_path(translate(square(1), (6, 2)), flags=1) mapping = geo.proxy(hatch).__geo_interface__ assert mapping['type'] == 'MultiPolygon' assert len(mapping['coordinates']) == 3
def test_polygon_from_hatch_hole_in_hole(): hatch = factory.new('HATCH') paths = hatch.paths paths.add_polyline_path(square(10), flags=1) paths.add_polyline_path(translate(square(8), (1, 1)), flags=0) paths.add_polyline_path(translate(square(6), (2, 2)), flags=0) mapping = geo.proxy(hatch).__geo_interface__ assert mapping['type'] == 'Polygon' assert len(mapping['coordinates']) == 2, 'inner hole should be removed' mapping = geo.proxy(hatch, force_line_string=True).__geo_interface__ assert mapping['type'] == 'MultiLineString' assert len(mapping['coordinates']) == 3, 'inner hole should not be removed'
def test_polygon_from_hatch_hole_in_hole(): hatch = factory.new("HATCH") paths = hatch.paths paths.add_polyline_path(square(10), flags=1) paths.add_polyline_path(translate(square(8), (1, 1)), flags=0) paths.add_polyline_path(translate(square(6), (2, 2)), flags=0) mapping = geo.proxy(hatch).__geo_interface__ assert mapping["type"] == "Polygon" assert len(mapping["coordinates"]) == 2, "inner hole should be removed" mapping = geo.proxy(hatch, force_line_string=True).__geo_interface__ assert mapping["type"] == "MultiLineString" assert len(mapping["coordinates"]) == 3, "inner hole should not be removed"
def test_valid_hatch(): hatch = factory.new('HATCH') paths = hatch.paths paths.add_polyline_path(square(10), flags=const.BOUNDARY_PATH_EXTERNAL) paths.add_polyline_path(translate(square(3), (1, 1)), flags=const.BOUNDARY_PATH_DEFAULT) paths.add_polyline_path(translate(square(3), (5, 1)), flags=const.BOUNDARY_PATH_DEFAULT) p = geo.proxy(hatch) polygon = shapely_geometry.shape(p) assert polygon.is_valid is True p.filter(validate) assert p.root != {}
def test_invalid_hatch_with_intersecting_holes(): hatch = factory.new('HATCH') paths = hatch.paths paths.add_polyline_path(square(10), flags=const.BOUNDARY_PATH_EXTERNAL) paths.add_polyline_path(translate(square(3), (1, 1)), flags=const.BOUNDARY_PATH_DEFAULT) paths.add_polyline_path(translate(square(3), (2, 2)), flags=const.BOUNDARY_PATH_DEFAULT) p = geo.proxy(hatch) polygon = shapely_geometry.shape(p) assert polygon.is_valid is False p.filter(validate) assert p.root == {}
def test_resolved_hatch_with_intersecting_holes(): hatch = factory.new('HATCH') paths = hatch.paths paths.add_polyline_path(square(10), flags=const.BOUNDARY_PATH_EXTERNAL) paths.add_polyline_path(translate(square(3), (1, 1)), flags=const.BOUNDARY_PATH_DEFAULT) paths.add_polyline_path(translate(square(3), (2, 2)), flags=const.BOUNDARY_PATH_DEFAULT) p = geo.proxy(hatch) # Overlapping holes already resolved by fast_bbox_detection() polygon = shapely_geometry.shape(p) assert polygon.is_valid is True p.filter(validate) assert p.root['type'] == 'Polygon' assert len(p.root['coordinates']) == 2
def test_intersecting_squares(self): square1 = forms.close_polygon(forms.square(2.0)) square2 = forms.translate(square1, (1, 1)) res = intersect_polylines_2d(Vec2.list(square1), Vec2.list(square2)) assert len(res) == 2 res.sort() assert res[0].isclose(Vec2(1, 2)) assert res[1].isclose(Vec2(2, 1))
def add_predefined_hatch_pattern(msp, cols, size, gap, scale): for index, name in enumerate(pattern.ISO_PATTERN.keys()): x = (index % cols) * (size + gap) y = (index // cols) * (size + gap) boundary = list(translate(square(size), (x, HEIGHT - y))) msp.add_lwpolyline(boundary, close=True, dxfattribs={"layer": "LINE"}) hatch = msp.add_hatch(dxfattribs={"layer": "HATCH"}) hatch.set_pattern_fill(name=name, scale=scale) hatch.paths.add_polyline_path(boundary, is_closed=True)
def add_rectangle(self, rectangle: Rectangle, start_from_x: float = 0.0, start_from_y: float = 0.0) -> None: # Rectangle with evenly spaced holes rect_coordinates = translate(box(rectangle.width, rectangle.height), (start_from_x, start_from_y)) self.msp.add_lwpolyline(rect_coordinates, close=True, dxfattribs=self.attribs) space_minus_offsets_from_side = rectangle.width - ( 2 * rectangle.offset_from_side) space_remaining = space_minus_offsets_from_side - rectangle.holes_total_width if len(rectangle.holes) <= 1: raise ValueError("Need to have at least two holes") space_between_holes = space_remaining / (len(rectangle.holes) - 1) for i, hole in enumerate(rectangle.holes): hole_center_x = (start_from_x + rectangle.offset_from_side + (i * hole.width + (hole.width / 2)) + (i * space_between_holes)) hole_center_y = (start_from_y + rectangle.offset_from_bottom + (hole.height / 2)) if isinstance(hole, Circle): self.msp.add_circle( (hole_center_x, hole_center_y), radius=hole.radius, dxfattribs=self.attribs, ) elif isinstance(hole, Slot): # TODO doesn't work correctly yet # TODO add lines connecting circles left_x = hole_center_x - ( (hole.length / 2) * math.cos(hole.angle)) left_y = hole_center_y + ( (hole.length / 2) * math.sin(hole.angle)) self.msp.add_circle( (left_x, left_y), radius=hole.radius, dxfattribs=self.attribs, ) right_x = hole_center_x + ( (hole.length / 2) * math.cos(hole.angle)) right_y = hole_center_y - ( (hole.length / 2) * math.sin(hole.angle)) self.msp.add_circle( (right_x, right_y), radius=hole.radius, dxfattribs=self.attribs, )
def polylines(filename): with r12writer(filename) as r12: r12.add_polyline_2d(circle(8), color=1, closed=False) r12.add_polyline_2d( translate(circle(8), vec=(3, 0)), color=3, closed=True ) r12.add_polyline_2d( [(0, 4), (4, 4, 1), (8, 4, 0, 0.2, 0.000001), (12, 4)], format="xybse", start_width=0.1, end_width=0.1, color=5, ) print(f'saved as "{filename}".')
def solid_entities(): lay = VirtualLayout() lay.add_solid(translate(square(1), (-10, -10))) lay.add_solid(translate(square(1), (10, 10))) return lay
def test_translate(): p = [(1, 2, 3), (4, 5, 6)] r = list(translate(p, (3, 2, 1))) assert is_close_points(r[0], (4, 4, 4)) assert is_close_points(r[1], (7, 7, 7))
def test_squares_with_common_corner_vertex(self): square1 = forms.close_polygon(forms.square(2.0)) square2 = forms.translate(square1, (2, 2)) res = intersect_polylines_2d(Vec2.list(square1), Vec2.list(square2)) assert len(res) == 1 assert res[0].isclose(Vec2(2, 2))
import ezdxf from ezdxf.render import forms DIR = Path("~/Desktop/Outbox").expanduser() doc = ezdxf.new(setup=True) msp = doc.modelspace() # Create a special DIMSTYLE for "vertical" centered measurement text: dimstyle = doc.dimstyles.duplicate_entry("EZDXF", "ORD_CENTER") dimstyle.dxf.dimtad = 0 # "vertical" centered measurement text # Add a rectangle: width=4, height = 2.5, lower left corner is WCS(x=2, y=3), # rotated about 30 degrees: origin = Vec3(2, 3) msp.add_lwpolyline(forms.translate(forms.rotate(forms.box(4, 2.5), 30), origin), close=True) # Define the rotated local render UCS. # The origin is the lower-left corner of the rectangle and the axis are # aligned to the rectangle edges: # The y-axis "uy" is calculated automatically by the right-hand rule. ucs = UCS(origin, ux=Vec3.from_deg_angle(30), uz=(0, 0, 1)) # Add a x-type ordinate DIMENSION with local feature locations: # the origin is now the origin of the UCS, which is (0, 0) the default value of # "origin" and the feature coordinates are located in the UCS: msp.add_ordinate_x_dim( # lower left corner feature_location=(0, 0), # feature location in the UCS offset=(0.25, -2), # # leader with a "knee"
from pathlib import Path from ezdxf.math import Vec3 import ezdxf from ezdxf.render import forms DIR = Path("~/Desktop/Outbox").expanduser() # Use argument setup=True to setup the default dimension styles. doc = ezdxf.new(setup=True) # Add new entities to the modelspace: msp = doc.modelspace() # Add a rectangle: width=4, height = 2.5, lower left corner is WCS(x=2, y=3) origin = Vec3(2, 3) msp.add_lwpolyline(forms.translate(forms.box(4, 2.5), origin), close=True) # Add a x-type ordinate DIMENSION with global feature locations: msp.add_ordinate_x_dim( # lower left corner feature_location=origin + (0, 0), # feature location in the WCS offset=(0, -2), # end of leader, relative to the feature location origin=origin, ).render() msp.add_ordinate_x_dim( # lower right corner feature_location=origin + (4, 0), # feature location in the WCS offset=(0, -2), origin=origin, ).render()
doc = ezdxf.new() doc.layers.new("FORMS", dxfattribs={"color": 1}) doc.layers.new("HATCHES") msp = doc.modelspace() attribs = {"layer": "FORMS"} # Create DXF primitives: msp.add_circle((2, 3), radius=2, dxfattribs=attribs) # Ellipse with hole: msp.add_ellipse((5, 0), major_axis=(3, 1), ratio=0.5, dxfattribs=attribs) msp.add_circle((5, 0), radius=1.5, dxfattribs=attribs) # Rectangle with a hole rect = translate(box(3, 2), (3, 6)) msp.add_lwpolyline(rect, close=True, dxfattribs=attribs) hole = translate(box(2, 1), (3.4, 6.4)) msp.add_lwpolyline(hole, close=True, dxfattribs=attribs) # Convert entities to primitives primitives = disassemble.to_primitives(msp) # Collect paths from primitives: paths = [p.path for p in primitives if p.path] # Render this paths as HATCH entities path.render_hatches(msp, paths, dxfattribs={"layer": "HATCHES", "color": 2}) doc.set_modelspace_vport(15, (4, 4)) doc.saveas(DIR / "hatches_from_entities.dxf")
def test_translate(): p = [(1, 2, 3), (4, 5, 6)] r = list(translate(p, (3, 2, 1))) assert r[0].isclose((4, 4, 4)) assert r[1].isclose((7, 7, 7))
# Copyright (c) 2020, Manfred Moitzi # License: MIT License import pytest from ezdxf.render.forms import square, translate from ezdxf.path import Path, nesting, from_vertices EXTERIOR = list(translate(square(10), (-5, -5))) EXT1_PATH = from_vertices(EXTERIOR) EXT2_PATH = from_vertices(translate(EXTERIOR, (11, 0))) CENTER_HOLE1 = list(translate(square(8), (-4, -4))) CH1_PATH = from_vertices(CENTER_HOLE1) CENTER_HOLE2 = list(translate(square(6), (-3, -3))) CH2_PATH = from_vertices(CENTER_HOLE2) LEFT_HOLE = list(translate(square(2.1), (-3, -1))) LH_PATH = from_vertices(LEFT_HOLE) RIGHT_HOLE = list(translate(square(2.0), (3, -1))) RH_PATH = from_vertices(RIGHT_HOLE) DETECTION_DATA = [ pytest.param( # Each polygon is a list of paths [EXT1_PATH], [[EXT1_PATH]], id="1 path", ), pytest.param(