def test_multi_path_to_lwpolylines(self): path = Path() path.line_to((1, 0, 0)) path.move_to((2, 0, 0)) path.line_to((3, 0, 0)) polylines = list(to_lwpolylines(path)) assert len(polylines) == 2 assert len(polylines[0]) == 2 assert len(polylines[1]) == 2
def test_append_empty_path(): path = Path((1, 0, 0)) path.line_to((2, 0, 0)) start = path.start end = path.end path.append_path(Path()) assert start == path.start and end == path.end, "path should be unchanged"
def test_approximate_curves(): path = Path() path.curve3_to((2, 0), (1, 1)) path.curve4_to((3, 0), (2, 1), (3, 1)) vertices = list(path.approximate(20)) assert len(vertices) == 41 assert vertices[0] == (0, 0) assert vertices[-1] == (3, 0)
def path(self) -> Optional[Path]: """Create path representation on demand. :class:`Path` can not represent a point, a :class:`Path` with only a start point yields not vertices! """ if self._path is None: self._path = Path(self.entity.dxf.location) return self._path
def test_reversing_multi_path(): p = Path() p.line_to((1, 0, 0)) p.move_to((2, 0, 0)) p.line_to((3, 0, 0)) r = p.reversed() assert r.has_sub_paths is True assert len(r) == 3 assert r.start == (3, 0, 0) assert r.end == (0, 0, 0) r0, r1 = r.sub_paths() assert r0.start == (3, 0, 0) assert r0.end == (2, 0, 0) assert r1.start == (1, 0, 0) assert r1.end == (0, 0, 0)
def _convert_entity(self): """ Calculates the rough border path for a single line text. Calculation is based on a mono-spaced font and therefore the border path is just an educated guess. Vertical text generation and oblique angle is ignored. """ def get_text_rotation() -> float: if alignment in ('FIT', 'ALIGNED') and not p1.isclose(p2): return (p2 - p1).angle else: return math.degrees(text.dxf.rotation) def get_insert() -> Vec3: if alignment == 'LEFT': return p1 elif alignment in ('FIT', 'ALIGNED'): return p1.lerp(p2, factor=0.5) else: return p2 text = cast('Text', self.entity) if text.dxftype() == 'ATTDEF': # ATTDEF outside of a BLOCK renders the tag rather than the value content = text.dxf.tag else: content = text.dxf.text content = plain_text(content) if len(content) == 0: # empty path - does not render any vertices! self._path = Path() return p1: Vec3 = text.dxf.insert p2: Vec3 = text.dxf.align_point font = fonts.make_font(get_font_name(text), text.dxf.height, text.dxf.width) text_line = TextLine(content, font) alignment: str = text.get_align() if text.dxf.halign > 2: # ALIGNED=3, MIDDLE=4, FIT=5 text_line.stretch(alignment, p1, p2) halign, valign = unified_alignment(text) corner_vertices = text_line.corner_vertices(get_insert(), halign, valign, get_text_rotation()) ocs = text.ocs() self._path = from_vertices( ocs.points_to_wcs(corner_vertices), close=True, )
def test_one_path_multiple_command(self): path = Path() path.line_to((1, 0)) path.curve3_to((2, 0), (2.5, 1)) path.curve4_to((3, 0), (2, 1), (3, 1)) result = transform_paths([path], Matrix44()) path0 = result[0] assert path0[0].type == Command.LINE_TO assert path0[1].type == Command.CURVE3_TO assert path0[2].type == Command.CURVE4_TO assert path0.start == (0, 0) assert path0.end == (3, 0)
def test_add_spline(): from ezdxf.math import BSpline spline = BSpline.from_fit_points([(2, 0), (4, 1), (6, -1), (8, 0)]) path = Path() tools.add_spline(path, spline) assert path.start == (2, 0) assert path.end == (8, 0) # set start point to end of spline path = Path(start=(8, 0)) # add reversed spline, by default the start of # an empty path is set to the spline start tools.add_spline(path, spline, reset=False) assert path.start == (8, 0) assert path.end == (2, 0) path = Path() # add a line segment from (0, 0) to start of spline tools.add_spline(path, spline, reset=False) assert path.start == (0, 0) assert path.end == (8, 0)
def test_has_clockwise_orientation(): # basic has_clockwise_orientation() function is tested in: # test_617_clockwise_orientation path = converter.from_vertices([(0, 0), (1, 0), (1, 1), (0, 1)]) assert path.has_clockwise_orientation() is False path = Path() path.line_to((2, 0)) path.curve4_to((4, 0), (2, 1), (4, 1)) # end, ctrl1, ctrl2 assert path.has_clockwise_orientation() is True
def test_move_to_creates_a_multi_path_object(self): path = Path(start=(1, 0, 0)) path.line_to((2, 0, 0)) path.move_to((3, 0, 0)) assert len(path) == 2, "should add a MOVETO cmd as last cmd" assert path.has_sub_paths is True, "should be a multi path object" assert path.end.isclose((3, 0, 0)), "should end at the MOVETO location"
def test_reversing_multi_path_with_a_move_to_cmd_at_the_end(): p = Path() p.line_to((1, 0, 0)) p.move_to((2, 0, 0)) # The last move_to will become the first move_to. # A move_to as first command just moves the start point. r = p.reversed() assert len(r) == 1 assert r.start == (1, 0, 0) assert r.end == (0, 0, 0) assert r.has_sub_paths is False
def test_multiple_first_move_to(self): path = Path(start=(1, 0, 0)) path.move_to((2, 0, 0)) path.move_to((3, 0, 0)) path.move_to((4, 0, 0)) assert path.start.isclose((4, 0, 0)), "should reset the start point" assert len(path) == 0, "should not add a MOVETO cmd as first cmd" assert path.has_sub_paths is False
def test_two_paths_one_command(self): path_a = Path() path_a.line_to((1, 0)) path_b = Path((2, 0)) path_b.line_to((3, 0)) result = transform_paths([path_a, path_b], Matrix44()) path0 = result[0] assert path0[0].type == Command.LINE_TO assert path0.start == (0, 0) assert path0.end == (1, 0) path1 = result[1] assert path1[0].type == Command.LINE_TO assert path1.start == (2, 0) assert path1.end == (3, 0)
def test_control_vertices(p1): vertices = list(p1.control_vertices()) assert close_vectors(vertices, [(0, 0), (2, 0), (2, 1), (4, 1), (4, 0), (5, -1), (6, 0)]) path = Path() assert len(list(path.control_vertices())) == 0 assert list(path.control_vertices()) == list(path.approximate(2)) path = converter.from_vertices([(0, 0), (1, 0)]) assert len(list(path.control_vertices())) == 2
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_two_paths_multiple_commands(self): path_a = Path() path_a.line_to((1, 0)) path_a.curve3_to((2, 0), (2.5, 1)) path_a.curve4_to((3, 0), (2, 1), (3, 1)) path_b = path_a.transform(Matrix44.translate(4, 0, 0)) result = transform_paths([path_a, path_b], Matrix44()) path0 = result[0] assert path0[0].type == Command.LINE_TO assert path0[1].type == Command.CURVE3_TO assert path0[2].type == Command.CURVE4_TO assert path0.start == (0, 0) assert path0.end == (3, 0) path1 = result[1] assert path1[0].type == Command.LINE_TO assert path1[1].type == Command.CURVE3_TO assert path1[2].type == Command.CURVE4_TO assert path1.start == (4, 0) assert path1.end == (7, 0)
def draw_path(self, path: Path, properties: Properties) -> None: """ Draw an outline path (connected string of line segments and Bezier curves). The :meth:`draw_path` implementation is a fall-back implementation which approximates Bezier curves by flattening as line segments. Backends can override this method if better path drawing functionality is available for that backend. """ if len(path): vertices = iter( path.flattening(distance=self.max_flattening_distance)) prev = next(vertices) for vertex in vertices: self.draw_line(prev, vertex, properties) prev = vertex
def make_path_from_str( s: str, font: fonts.FontFace, size: float = 1.0, align=TextEntityAlignment.LEFT, length: float = 0, m: Matrix44 = None, ) -> Path: """Convert a single line string `s` into a :term:`Multi-Path` object. The text `size` is the height of the uppercase letter "X" (cap height). The paths are aligned about the insertion point at (0, 0). BASELINE means the bottom of the letter "X". Args: s: text to convert font: font face definition as :class:`~ezdxf.tools.fonts.FontFace` object size: text size (cap height) in drawing units align: alignment as :class:`ezdxf.enums.TextEntityAlignment`, default is :attr:`LEFT` length: target length for the :attr:`ALIGNED` and :attr:`FIT` alignments m: transformation :class:`~ezdxf.math.Matrix44` .. versionadded:: 0.17 .. version changed: 0.17.2 Enum :class:`ezdxf.enums.TextEntityAlignment` replaces string values. """ if len(s) == 0: return Path() font_properties, font_measurements = _get_font_data(font) # scale font rendering units to drawing units: render_size = size / font_measurements.cap_height p = _str_to_path(s, font_properties, render_size) bbox = path.bbox([p], flatten=0) # Text is rendered in drawing units, # therefore do alignment in drawing units: draw_units_fm = font_measurements.scale_from_baseline(size) matrix = alignment_transformation(draw_units_fm, bbox, align, length) if m is not None: matrix *= m return p.transform(matrix)
def test_remove_line_segments_of_zero_length_between_commands(self): # CURVE3_TO and CURVE4_TO can not process zero length segments path = Path() path.line_to((1, 0)) path.line_to((1, 0)) # line segment of length==0 should be removed path.line_to((2, 0)) path = lines_to_curve4(path) assert len(path) == 2 assert path.start == (0, 0) assert path[0].type == Command.CURVE4_TO assert path[0].end == (1, 0) assert path[1].type == Command.CURVE4_TO assert path[1].end == (2, 0)
def test_line_to(): path = Path() path.line_to((1, 2, 3)) assert path[0] == (Vec3(1, 2, 3), ) assert path.end == (1, 2, 3)
def test_add_curves4_reverse(): path = Path(start=(0, 0, 0)) c1 = Bezier4P(((2, 0, 0), (2, 1, 0), (0, 1, 0), (0, 0, 0))) tools.add_bezier4p(path, [c1]) assert len(path) == 1 assert path.end == (2, 0, 0)
def test_reversing_one_curve4(): p = Path() p.curve4_to((3, 0), (1, 1), (2, 1)) p2 = list(p.reversed().control_vertices()) assert p2 == [(3, 0), (2, 1), (1, 1), (0, 0)]
def test_reversing_one_line(): p = Path() p.line_to((1, 0)) p2 = list(p.reversed().control_vertices()) assert p2 == [(1, 0), (0, 0)]
def test_reversing_empty_path(): p = Path() assert len(p.reversed()) == 0
def p1(): path = Path() path.line_to((2, 0)) path.curve4_to((4, 0), (2, 1), (4, 1)) # end, ctrl1, ctrl2 path.curve3_to((6, 0), (5, -1)) # end, ctrl return path
def test_curve4_to(): path = Path() path.curve4_to((1, 2, 3), (0, 1, 0), (0, 2, 0)) assert path[0] == ((1, 2, 3), (0, 1, 0), (0, 2, 0)) assert path.end == (1, 2, 3)
def test_does_not_remove_a_line_representing_a_single_point(self): path = Path((1, 0)) path.line_to((1, 0)) # represents the point (1, 0) path = lines_to_curve4(path) assert len(path) == 1 assert path[0].type == Command.LINE_TO
def test_curve3_to(): path = Path() path.curve3_to((10, 0), (5, 5)) assert path[0] == ((10, 0), (5, 5)) assert path.end == (10, 0)
def test_init(): path = Path() assert path.start == (0, 0) assert len(path) == 0 assert path.end == (0, 0)
def test_init_start(): path = Path(start=(1, 2)) assert path.start == (1, 2)