def test_clockwise(p1): from ezdxf.math import has_clockwise_orientation cw_path = p1.clockwise() ccw_path = p1.counter_clockwise() assert has_clockwise_orientation(cw_path.control_vertices()) is True assert has_clockwise_orientation(ccw_path.control_vertices()) is False
def has_clockwise_orientation(self) -> bool: """Returns ``True`` if 2D path has clockwise orientation, ignores z-axis of all control vertices. Raises: TypeError: can't detect orientation of a :term:`Multi-Path` object """ if self.has_sub_paths: raise TypeError("can't detect orientation of a multi-path object") return has_clockwise_orientation(self._vertices)
def linear_ring(points: List[Vector], ccw=True) -> List[Vector]: """ Return `points` as linear ring (last vertex == first vertex), argument `ccw` defines the winding orientation, ``True`` for counter-clock wise and ``False`` for clock wise. """ if len(points) < 3: raise ValueError(f'Invalid vertex count: {len(points)}') if not points[0].isclose(points[-1]): points.append(points[0]) if has_clockwise_orientation(points): if ccw: points.reverse() else: if not ccw: points.reverse() return points
def clip_polygon_2d( clip: Iterable["Vertex"], subject: Iterable["Vertex"], ccw_check: bool = True, ) -> List["Vec2"]: """Clip the `subject` polygon by the **convex** clipping polygon `clip`. Implements the `Sutherland–Hodgman`_ algorithm for clipping polygons. Args: clip: the convex clipping polygon as iterable of vertices subject: the polygon to clip as a iterable of vertices ccw_check: check if the clipping polygon is in counter clockwise orientation if ``True``, set to ``False`` if the ccw check is done by the caller Returns: the clipped subject as list of :class:`~ezdxf.math.Vec2` .. versionadded:: 0.16 .. _Sutherland–Hodgman: https://de.wikipedia.org/wiki/Algorithmus_von_Sutherland-Hodgman """ def polygon(vertices: Iterable["Vertex"]) -> List[Vec2]: _vertices = Vec2.list(vertices) if len(_vertices) > 1: if _vertices[0].isclose(_vertices[-1]): _vertices.pop() return _vertices def is_inside(point: Vec2) -> bool: return (point_to_line_relation(point, clip_start, clip_end) == -1) # left of line def edge_intersection() -> Vec2: return intersection_line_line_2d((edge_start, edge_end), (clip_start, clip_end)) clipping_polygon = polygon(clip) if ccw_check and has_clockwise_orientation(clipping_polygon): clipping_polygon.reverse() if len(clipping_polygon) > 2: clip_start = clipping_polygon[-1] else: raise ValueError("invalid clipping polygon") clipped = polygon(subject) for clip_end in clipping_polygon: # next clipping edge to test: clip_start -> clip_end if not clipped: # no subject vertices left to test break vertices = list(clipped) clipped.clear() edge_start = vertices[-1] for edge_end in vertices: # next polygon edge to test: edge_start -> edge_end if is_inside(edge_end): if not is_inside(edge_start): clipped.append(edge_intersection()) clipped.append(edge_end) elif is_inside(edge_start): clipped.append(edge_intersection()) edge_start = edge_end clip_start = clip_end return clipped
def test_has_counter_clockwise_orientation(vertices): assert has_clockwise_orientation(vertices) is True
def test_has_clockwise_orientation(vertices): assert has_clockwise_orientation(vertices) is False
def has_clockwise_orientation(self) -> bool: """ Returns ``True`` if 2D path has clockwise orientation, ignores z-axis of all control vertices. """ return has_clockwise_orientation(self.control_vertices())
def test_has_counter_clockwise_orientation(v1): assert has_clockwise_orientation(reversed(v1)) is False
def test_has_clockwise_orientation(v1): assert has_clockwise_orientation(v1) is True