class Point: __slots__ = 'x', 'y' def __init__(self, x: Coordinate, y: Coordinate) -> None: self.x = x self.y = y __repr__ = generate_repr(__init__) def __add__(self, other: 'Point') -> 'Point': return Point(self.x + other.x, self.y + other.y) def __eq__(self, other: 'Point') -> bool: return (self.x == other.x and self.y == other.y if isinstance( other, Point) else NotImplemented) def __mul__(self, scale: Coordinate) -> 'Point': return Point(self.x * scale, self.y * scale) def __sub__(self, other: 'Point') -> 'Point': return Point(self.x - other.x, self.y - other.y) def cross_z(self, other: 'Point') -> Coordinate: return self.x * other.y - self.y * other.x def is_right_of(self, other: 'Point') -> bool: return (self.x, self.y) > (other.x, other.y)
class RightEvent(Event): is_left = False __slots__ = 'left', '_original_start', '_start' def __init__(self, start: Point, left: LeftEvent, original_start: Point) -> None: self.left, self._original_start, self._start = (left, original_start, start) __repr__ = recursive_repr()(generate_repr(__init__)) @property def end(self) -> Point: return self.left.start @property def from_goal(self) -> bool: return self.left.from_goal @property def from_test(self) -> bool: return self.left.from_test @property def original_end(self) -> Point: return self.left.original_start @property def original_start(self) -> Point: return self._original_start @property def start(self) -> Point: return self._start
class Point: __slots__ = '_x', '_y' def __init__(self, x: Scalar, y: Scalar) -> None: self._x = x self._y = y __repr__ = generate_repr(__init__) def __eq__(self, other: 'Point') -> bool: return (self._x == other._x and self._y == other._y if isinstance( other, Point) else NotImplemented) @property def bounding_box(self) -> BoundingBox: return BoundingBox(self._x, self._y, self._x, self._y) @property def x(self) -> Scalar: return self._x @property def y(self) -> Scalar: return self._y def distance_to(self, other: 'Point') -> Scalar: return math.sqrt((self._x - other._x)**2 + (self._y - other._y)**2)
class LeftNaryEvent(LeftEvent): @classmethod def from_segment_endpoints( cls, segment_endpoints: SegmentEndpoints) -> 'LeftNaryEvent': start, end = segment_endpoints if start > end: start, end = end, start result = cls(start, None) result.right = RightNaryEvent(end, result) return result __slots__ = '_start', def __init__(self, start: Point, right: Optional['RightNaryEvent']) -> None: self.right, self._start = right, start __repr__ = recursive_repr()(generate_repr(__init__)) @property def start(self) -> Point: return self._start def divide(self, break_point: Point) -> 'LeftNaryEvent': tail = self.right.left = LeftNaryEvent(break_point, self.right) self.right = RightNaryEvent(break_point, self) return tail
class MixedEvent(BinaryEvent): __slots__ = ('interior_to_left', 'other_interior_to_left', 'is_overlap', 'in_result') def __init__(self, is_right_endpoint: bool, start: Point, complement: Optional['MixedEvent'], from_left: bool, interior_to_left: bool, other_interior_to_left: bool = False, is_overlap: bool = False, in_result: bool = False) -> None: super().__init__(is_right_endpoint, start, complement, from_left) self.interior_to_left = interior_to_left self.other_interior_to_left = other_interior_to_left self.is_overlap = is_overlap self.in_result = in_result __repr__ = recursive_repr()(generate_repr(__init__)) @property def outside(self) -> bool: """ Checks if the segment touches or disjoint with the intersection. """ return not self.other_interior_to_left and not self.is_overlap
class RuneRange: __slots__ = '_low', '_high' def __new__(cls, low: Rune, high: Optional[Rune] = None) -> 'RuneRange': self = super().__new__(cls) self._low = low self._high = low if high is None else high return self __repr__ = generate_repr(__new__) @property def high(self) -> Rune: return self._high @property def low(self) -> Rune: return self._low def __eq__(self, other: 'RuneRange') -> bool: return (self.low == other.low and self.high == other.high if isinstance(other, RuneRange) else NotImplemented) def __lt__(self, other: 'RuneRange') -> bool: return (self.high < other.low if isinstance(other, RuneRange) else NotImplemented)
class Polygon: __slots__ = '_contours', def __init__(self, contours: List[Contour]) -> None: self._contours = contours __repr__ = generate_repr(__init__) @property def contours(self) -> List[Contour]: return self._contours def __eq__(self, other: 'Polygon') -> bool: return (self._contours == other._contours if isinstance(other, Polygon) else NotImplemented) def __iter__(self) -> Iterator[Contour]: return iter(self._contours) @property def bounding_box(self) -> BoundingBox: if self._contours: return reduce(add, [contour.bounding_box for contour in self._contours]) else: return BoundingBox(0, 0, 0, 0) def join(self, other: 'Polygon') -> None: contours_count = len(self._contours) self._contours.extend(Contour(contour.points, [hole + contours_count for hole in contour.holes], contour.is_external) for contour in other._contours)
class NaryEventsQueueKey: __slots__ = 'event', 'orienteer' def __init__(self, orienteer: Orienteer, event: NaryEvent) -> None: self.orienteer, self.event = orienteer, event __repr__ = generate_repr(__init__) def __lt__(self, other: 'NaryEventsQueueKey') -> bool: event, other_event = self.event, other.event start, other_start = event.start, other_event.start if start.x != other_start.x: # different x-coordinate, # the event with lower x-coordinate is processed first return start.x < other_start.x elif start.y != other_start.y: # different points, but same x-coordinate, # the event with lower y-coordinate is processed first return start.y < other_start.y elif event.is_left is not other_event.is_left: # same start, but one is a left endpoint # and the other a right endpoint, # the right endpoint is processed first return not event.is_left # same start, both events are left endpoints # or both are right endpoints else: # the lowest segment is processed first return (self.orienteer(event.start, event.end, other_event.end) is (Orientation.COUNTERCLOCKWISE if event.is_left else Orientation.CLOCKWISE))
class SweepLine: __slots__ = 'current_x', '_tree' def __init__(self, current_x: Optional[Coordinate] = None) -> None: self.current_x = current_x self._tree = red_black.tree( key=cast(Callable[[Event], SweepLineKey], partial(SweepLineKey, self))) __repr__ = generate_repr(__init__) def __contains__(self, event: Event) -> bool: return event in self._tree def move_to(self, point: Point) -> None: self.current_x, _ = point def add(self, event: Event) -> None: self._tree.add(event) def remove(self, event: Event) -> None: self._tree.remove(event) def above(self, event: Event) -> Optional[Event]: try: return self._tree.next(event) except ValueError: return None def below(self, event: Event) -> Optional[Event]: try: return self._tree.prev(event) except ValueError: return None
class Polygon(abc.Sequence): __slots__ = 'linear_rings', def __init__(self, linear_rings: List[LinearRing]) -> None: self.linear_rings = linear_rings __repr__ = generate_repr(__init__) def __eq__(self, other: 'Polygon') -> bool: return (self.linear_rings == other.linear_rings if isinstance( other, Polygon) else NotImplemented) def __getitem__(self, index: int) -> LinearRing: return self.linear_rings[index] def __len__(self) -> int: return len(self.linear_rings) @classmethod def from_ring(cls, ring: Ring, reverse_output: bool) -> 'Polygon': return cls([point_node_to_linear_ring(ring.node, reverse_output)]) def append(self, ring: Ring, reverse_output: bool) -> None: self.linear_rings.append( point_node_to_linear_ring(ring.node, reverse_output))
class LeftBinaryEvent(LeftEvent): @classmethod def from_segment_endpoints(cls, segment_endpoints: SegmentEndpoints, from_first: bool) -> 'LeftBinaryEvent': start, end = segment_endpoints if start > end: start, end = end, start event = cls(start, None, from_first) event.right = RightBinaryEvent(end, event) return event __slots__ = 'from_first', '_start' def __init__(self, start: Point, right: Optional['RightBinaryEvent'], from_first: bool) -> None: self.from_first, self.right, self._start = from_first, right, start __repr__ = recursive_repr()(generate_repr(__init__)) @property def start(self) -> Point: return self._start def divide(self, point: Point) -> 'LeftBinaryEvent': tail = self.right.left = LeftBinaryEvent(point, self.right, self.from_first) self.right = RightBinaryEvent(point, self) return tail
class ShapedEvent(BinaryEvent): __slots__ = ('interior_to_left', 'other_interior_to_left', 'overlap_kind', 'in_result', 'position') def __init__(self, is_right_endpoint: bool, start: Point, complement: Optional['ShapedEvent'], from_left: bool, interior_to_left: bool, other_interior_to_left: bool = False, overlap_kind: OverlapKind = OverlapKind.NONE, in_result: bool = False, position: int = 0) -> None: super().__init__(is_right_endpoint, start, complement, from_left) self.interior_to_left = interior_to_left self.other_interior_to_left = other_interior_to_left self.overlap_kind = overlap_kind self.in_result = in_result self.position = position __repr__ = recursive_repr()(generate_repr(__init__)) @property def inside(self) -> bool: """ Checks if the segment enclosed by or lies within the region of the intersection. """ return (self.other_interior_to_left and self.overlap_kind is OverlapKind.NONE) @property def is_common_polyline_component(self) -> bool: """ Checks if the segment is a component of intersection's polyline. """ return self.overlap_kind is OverlapKind.DIFFERENT_ORIENTATION @property def is_common_region_boundary(self) -> bool: """ Checks if the segment is a boundary of intersection's region. """ return self.overlap_kind is OverlapKind.SAME_ORIENTATION @property def is_overlap(self) -> bool: """ Checks if the segment lies on the boundary of both operands. """ return self.overlap_kind is not OverlapKind.NONE @property def outside(self) -> bool: """ Checks if the segment touches or disjoint with the intersection. """ return (not self.other_interior_to_left and self.overlap_kind is OverlapKind.NONE)
class Factor: __slots__ = 'argument', 'degree', '_term' def __init__(self, argument: Expression, degree: int) -> None: self.argument, self.degree = argument, degree term = argument for _ in range(degree): term = Term(One, term) self._term = term def express(self) -> Term: return self._term def square(self) -> Expression: return self._term.argument def __eq__(self, other: 'Factor') -> bool: return self.degree == other.degree and self.argument == other.argument def __hash__(self) -> int: return hash((self.argument, self.degree)) def __lt__(self, other: 'Factor') -> bool: return ((self.express().degree, self.express().argument) < (other.express().degree, other.express().argument)) __repr__ = generate_repr(__init__) def __str__(self) -> str: return str(self.express())
class HoleyEvent(ShapedEvent): __slots__ = ('interior_to_left', 'other_interior_to_left', 'overlap_kind', 'in_result', 'result_in_out', 'position', 'contour_id', 'below_in_result_event') def __init__(self, is_right_endpoint: bool, start: Point, complement: Optional['HoleyEvent'], from_left: bool, interior_to_left: bool, other_interior_to_left: bool = False, overlap_kind: OverlapKind = OverlapKind.NONE, in_result: bool = False, result_in_out: bool = False, position: int = 0, contour_id: Optional[int] = None, below_in_result_event: Optional['HoleyEvent'] = None) -> None: super().__init__(is_right_endpoint, start, complement, from_left, interior_to_left, other_interior_to_left, overlap_kind, in_result, position) self.result_in_out = result_in_out self.contour_id = contour_id self.below_in_result_event = below_in_result_event __repr__ = recursive_repr()(generate_repr(__init__))
class XNode(Node): __slots__ = 'point', 'left', 'right' def __init__(self, point: Point, left: Node, right: Node) -> None: super().__init__() self.point = point self.left = left self.right = right self.left._add_parent(self) self.right._add_parent(self) __repr__ = generate_repr(__init__) @property def height(self) -> int: return max(self.left.height, self.right.height) + 1 def locate(self, point: Point) -> Location: return (self.left.locate(point) if point < self.point else (self.right.locate(point) if self.point < point else Location.BOUNDARY)) def search_edge(self, edge: Edge) -> Trapezoid: return (self.right if self.point <= edge.left else self.left).search_edge(edge) def _replace_child(self, current: Node, replacement: Node) -> None: if self.left is current: self.left = replacement else: self.right = replacement
class YNode(Node): __slots__ = 'edge', 'above', 'below' def __init__(self, edge: Edge, below: Node, above: Node) -> None: super().__init__() self.edge = edge self.below = below self.above = above self.below._add_parent(self) self.above._add_parent(self) __repr__ = generate_repr(__init__) @property def height(self) -> int: return max(self.below.height, self.above.height) + 1 def locate(self, point: Point) -> Location: point_orientation = self.edge.orientation_with(point) return (self.above.locate(point) if point_orientation is Orientation.COUNTERCLOCKWISE else (self.below.locate(point) if point_orientation is Orientation.CLOCKWISE else Location.BOUNDARY)) def search_edge(self, edge: Edge) -> Trapezoid: return (self.above if self.edge < edge else self.below).search_edge(edge) def _replace_child(self, current: Node, replacement: Node) -> None: if self.below is current: self.below = replacement else: self.above = replacement
class EventsQueue: __slots__ = 'context', 'key', '_queue' def __init__(self, context: Context) -> None: self.context = context key = self.key = partial(EventsQueueKey, context.angle_orientation) self._queue = PriorityQueue(key=key) __repr__ = generate_repr(__init__) def __bool__(self) -> bool: return bool(self._queue) def peek(self) -> Event: return self._queue.peek() @abstractmethod def register(self, segments_endpoints: Iterable[SegmentEndpoints], *, from_test: bool) -> None: """ Registers segments in the events queue. """ @abstractmethod def sweep(self, stop_x: Scalar) -> Iterable[LeftEvent]: """ Sweeps plane and emits processed segments' events. """ def _divide_segment(self, event: LeftEvent, break_point: Point) -> None: self._queue.push(event.divide(break_point)) self._queue.push(event.right)
def test_basic(method: Method, prefer_keyword: bool, with_module_name: bool) -> None: result = generate_repr(method, prefer_keyword=prefer_keyword, with_module_name=with_module_name) assert callable(result)
class Multipolygon(abc.Sequence): __slots__ = 'polygons', def __init__(self, polygons: List[Polygon]) -> None: self.polygons = polygons __repr__ = generate_repr(__init__) def __eq__(self, other: 'Multipolygon') -> bool: return (self.polygons == other.polygons if isinstance( other, Multipolygon) else NotImplemented) def __getitem__(self, index: int) -> Polygon: return self.polygons[index] def __len__(self) -> int: return len(self.polygons) @classmethod def from_rings(cls, rings: List[Optional[Ring]], reverse_output: bool) -> 'Multipolygon': return cls(list(rings_to_polygons(rings, reverse_output))) def extend(self, rings: List[Optional[Ring]], reverse_output: bool) -> None: self.polygons.extend(rings_to_polygons(rings, reverse_output))
class SweepLine: __slots__ = '_tree', def __init__(self, context: Context) -> None: self._tree = red_black.set_( key=partial(SweepLineKey, context.angle_orientation)) __repr__ = generate_repr(__init__) def __contains__(self, event: Event) -> bool: return event in self._tree def add(self, event: Event) -> None: self._tree.add(event) def remove(self, event: Event) -> None: self._tree.remove(event) def above(self, event: Event) -> Optional[Event]: try: return self._tree.next(event) except ValueError: return None def below(self, event: Event) -> Optional[Event]: try: return self._tree.prev(event) except ValueError: return None
class IntersectNode: __slots__ = 'first_bound', 'second_bound', 'point' def __init__(self, first_bound: Bound, second_bound: Bound, point: Point) -> None: self.first_bound = first_bound self.second_bound = second_bound self.point = point __repr__ = generate_repr(__init__) def __eq__(self, other: 'IntersectNode') -> bool: return (self.first_bound == other.first_bound and self.second_bound == other.second_bound and self.point == other.point if isinstance( other, IntersectNode) else NotImplemented) def __lt__(self, other: 'IntersectNode') -> bool: return (to_int32(self.first_bound.opposite_winding_count + self.second_bound.opposite_winding_count) < to_int32(other.first_bound.opposite_winding_count + other.second_bound.opposite_winding_count) if are_floats_almost_equal(float(self.point.y), float(other.point.y)) else other.point.y < self.point.y) def has_bound(self, bound: Bound) -> bool: return self.first_bound is bound or self.second_bound is bound
class Node: """Represents node of *kd*-tree.""" __slots__ = ('index', 'is_y_axis', 'left', 'metric', 'point', 'projector', 'right') def __init__(self, index: int, point: Point, is_y_axis: bool, left: Union['Node', NIL], right: Union['Node', NIL], metric: Callable[[Point, Point], Scalar]) -> None: self.index, self.point = index, point self.is_y_axis, self.projector = is_y_axis, PROJECTORS[is_y_axis] self.left, self.right = left, right self.metric = metric __repr__ = generate_repr(__init__) @property def item(self) -> Item: return self.index, self.point @property def projection(self) -> Scalar: return self.projector(self.point) def distance_to_point(self, point: Point) -> Scalar: return self.metric(self.point, point) def distance_to_coordinate(self, coordinate: Scalar) -> Scalar: return (self.projection - coordinate) ** 2
class Edge: __slots__ = 'left', 'right' def __init__(self, left: Point, right: Point) -> None: assert right.is_right_of(left), 'Incorrect endpoints order' self.left = left self.right = right __repr__ = generate_repr(__init__) def __eq__(self, other: 'Edge') -> bool: return (self.left == other.left and self.right == other.right if isinstance(other, Edge) else NotImplemented) @property def slope(self) -> Coordinate: difference = self.right - self.left try: return difference.y / difference.x except ZeroDivisionError: return math.copysign(math.inf, difference.x * difference.y) def orientation_with(self, point: Point) -> int: cross_z = (point - self.left).cross_z(self.right - self.left) return (1 if cross_z > 0 else (-1 if cross_z < 0 else 0))
class Point: __slots__ = '_x', '_y' def __init__(self, x: hints.Scalar, y: hints.Scalar) -> None: self._x, self._y = x, y @property def x(self) -> hints.Scalar: return self._x @property def y(self) -> hints.Scalar: return self._y def __eq__(self, other: 'Point') -> bool: return (self.x == other.x and self.y == other.y if isinstance( other, Point) else NotImplemented) def __hash__(self) -> int: return hash((self.x, self.y)) def __le__(self, other: 'Point') -> bool: return (self.x < other.x or self.x == other.x and self.y <= other.y if isinstance(other, Point) else NotImplemented) def __lt__(self, other: 'Point') -> bool: return (self.x < other.x or self.x == other.x and self.y < other.y if isinstance(other, Point) else NotImplemented) __repr__ = generate_repr(__init__)
class Mix: __slots__ = '_discrete', '_linear', '_shaped' def __init__(self, discrete: hints.Maybe[hints.Multipoint], linear: hints.Maybe[hints.Linear], shaped: hints.Maybe[hints.Shaped]) -> None: self._discrete, self._linear, self._shaped = discrete, linear, shaped @property def discrete(self) -> hints.Maybe[hints.Multipoint]: return self._discrete @property def linear(self) -> hints.Maybe[hints.Linear]: return self._linear @property def shaped(self) -> hints.Maybe[hints.Shaped]: return self._shaped def __eq__(self, other: 'Mix') -> bool: return (self.discrete == other.discrete and self.linear == other.linear and self.shaped == other.shaped if isinstance(other, Mix) else NotImplemented) __repr__ = generate_repr(__init__)
class Box: __slots__ = '_min_x', '_max_x', '_min_y', '_max_y' def __init__(self, min_x: hints.Scalar, max_x: hints.Scalar, min_y: hints.Scalar, max_y: hints.Scalar) -> None: self._min_x, self._max_x, self._min_y, self._max_y = (min_x, max_x, min_y, max_y) @property def max_x(self) -> hints.Scalar: return self._max_x @property def max_y(self) -> hints.Scalar: return self._max_y @property def min_x(self) -> hints.Scalar: return self._min_x @property def min_y(self) -> hints.Scalar: return self._min_y def __eq__(self, other: 'Box') -> bool: return (self.min_x == other.min_x and self.max_x == other.max_x and self.min_y == other.min_y and self.max_y == other.max_y if isinstance(other, Box) else NotImplemented) __repr__ = generate_repr(__init__)
class EventsQueueKey: __slots__ = 'event', def __init__(self, event: Event) -> None: self.event = event __repr__ = generate_repr(__init__) def __lt__(self, other: 'EventsQueueKey') -> bool: """ Checks if the event should be processed before the other. """ event, other_event = self.event, other.event start_x, start_y = event.start other_start_x, other_start_y = other_event.start if start_x != other_start_x: # different x-coordinate, # the event with lower x-coordinate is processed first return start_x < other_start_x elif start_y != other_start_y: # different starts, but same x-coordinate, # the event with lower y-coordinate is processed first return start_y < other_start_y elif event.is_left_endpoint is not other_event.is_left_endpoint: # same start, but one is a left endpoint # and the other is a right endpoint, # the right endpoint is processed first return not event.is_left_endpoint else: # same start, # both events are left endpoints or both are right endpoints return event.end < other_event.end
class Event: __slots__ = ('is_left_endpoint', 'start', 'complement', 'from_left', 'is_overlap', 'interior_to_left', 'other_interior_to_left', 'edge') def __init__(self, is_left_endpoint: bool, start: Point, complement: Optional['Event'], from_left_contour: bool, interior_to_left: bool, edge: Optional[QuadEdge] = None) -> None: self.is_left_endpoint = is_left_endpoint self.start = start self.complement = complement self.from_left = from_left_contour self.is_overlap = False self.interior_to_left = interior_to_left self.other_interior_to_left = False self.edge = edge __repr__ = recursive_repr()(generate_repr(__init__)) @property def end(self) -> Point: """ Returns end of the event's segment. >>> event = Event(True, (0, 0), None, False, False) >>> event.complement = Event(False, (1, 0), event, False, False) >>> event.end == (1, 0) True """ return self.complement.start @property def inside(self) -> bool: """ Checks if the segment enclosed by or lies within the region of the intersection. >>> event = Event(True, (0, 0), None, False, False) >>> event.complement = Event(False, (1, 0), event, False, False) >>> event.inside False """ return self.other_interior_to_left and not self.is_overlap @property def segment(self) -> Segment: """ Returns segment of the event. >>> event = Event(True, (0, 0), None, False, False) >>> event.complement = Event(False, (1, 0), event, False, False) >>> event.segment == ((0, 0), (1, 0)) True """ return self.start, self.end
class BeachLineValue: def __init__(self, edge: Optional[Edge], circle_event: Optional[CircleEvent] = None) -> None: self.edge = edge self.circle_event = circle_event __repr__ = generate_repr(__init__)
class EmptySet(Set[Domain]): def __init__(self) -> None: pass def __bool__(self) -> bool: return False def __hash__(self) -> int: return 0 __repr__ = generate_repr(__init__) def __str__(self) -> str: return EMPTY_SET_STRING def __and__(self, other: Set) -> Set: return self def __contains__(self, object_: Domain) -> bool: return False def __eq__(self, other: Set) -> bool: if not isinstance(other, Set): return NotImplemented return not other def __ge__(self, other: Set) -> bool: if not isinstance(other, Set): return NotImplemented return not other def __gt__(self, other: Set) -> bool: if not isinstance(other, Set): return NotImplemented return False def __le__(self, other: Set) -> bool: if not isinstance(other, Set): return NotImplemented return True def __lt__(self, other: Set) -> bool: if not isinstance(other, Set): return NotImplemented return bool(other) def __or__(self, other: Set) -> Set: if not isinstance(other, Set): return NotImplemented return other def __rsub__(self, other: Set) -> Set: return other def __sub__(self, other: Set) -> Set: return self