1# Copyright (c) 2020, Manfred Moitzi 2# License: MIT License 3from typing import Tuple, Iterable 4import math 5from ezdxf.math import Vec3, Vertex 6 7LineSegment = Tuple[Vec3, Vec3] 8 9 10class LineTypeRenderer: 11 def __init__(self, dashes: Iterable[float]): 12 # Simplified dash pattern: line-gap-line-gap 13 # Dash pattern should end with a gap (even count). 14 # Dash length in drawing units. 15 16 self._dashes = tuple(dashes) 17 self._dash_count = len(self._dashes) 18 self.is_solid = True 19 self._current_dash = 0 20 self._current_dash_length = 0 21 if self._dash_count > 1: 22 self.is_solid = False 23 self._current_dash_length = self._dashes[0] 24 self._is_dash = True 25 26 def line_segment( 27 self, start: Vertex, end: Vertex) -> Iterable[LineSegment]: 28 start = Vec3(start) 29 end = Vec3(end) 30 if self.is_solid or start.isclose(end): 31 yield start, end 32 return 33 34 segment_vec = end - start 35 segment_length = segment_vec.magnitude 36 segment_dir = segment_vec / segment_length # normalize 37 38 for is_dash, dash_length in self._render_dashes(segment_length): 39 end = start + segment_dir * dash_length 40 if is_dash: 41 yield start, end 42 start = end 43 44 def line_segments( 45 self, vertices: Iterable[Vertex]) -> Iterable[LineSegment]: 46 last = None 47 for vertex in vertices: 48 if last is not None: 49 yield from self.line_segment(last, vertex) 50 last = vertex 51 52 def _render_dashes(self, length: float) -> Tuple[bool, float]: 53 if length <= self._current_dash_length: 54 self._current_dash_length -= length 55 yield self._is_dash, length 56 if math.isclose(self._current_dash_length, 0.0): 57 self._cycle_dashes() 58 else: 59 # Avoid deep recursions! 60 while length > self._current_dash_length: 61 length -= self._current_dash_length 62 yield from self._render_dashes(self._current_dash_length) 63 if length > 0.0: 64 yield from self._render_dashes(length) 65 66 def _cycle_dashes(self): 67 self._current_dash = (self._current_dash + 1) % self._dash_count 68 self._current_dash_length = self._dashes[self._current_dash] 69 self._is_dash = not self._is_dash 70