-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c163e43
commit d4a1e6a
Showing
16 changed files
with
610 additions
and
412 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
from typing import (Iterable, | ||
List, | ||
Optional, | ||
Sequence) | ||
|
||
from ground.base import (Context, | ||
Orientation, | ||
Relation) | ||
from ground.hints import (Point, | ||
Segment) | ||
|
||
from .event import (Event, | ||
LeftEvent) | ||
from .events_queue import EventsQueue | ||
from .sweep_line import SweepLine | ||
from .utils import (classify_overlap, | ||
to_pairs_combinations) | ||
|
||
|
||
def sweep(segments: Sequence[Segment], | ||
*, | ||
context: Context) -> Iterable[LeftEvent]: | ||
events_queue = EventsQueue.from_segments(segments, | ||
context=context) | ||
sweep_line = SweepLine(context) | ||
start = (events_queue.peek().start | ||
if events_queue | ||
else None) # type: Optional[Point] | ||
same_start_events = [] # type: List[Event] | ||
while events_queue: | ||
event = events_queue.pop() | ||
if event.start == start: | ||
same_start_events.append(event) | ||
else: | ||
complete_events_relations(same_start_events, | ||
context=context) | ||
yield from to_processed_events(same_start_events) | ||
same_start_events, start = [event], event.start | ||
if event.is_left: | ||
equal_segment_event = sweep_line.find_equal(event) | ||
if equal_segment_event is None: | ||
sweep_line.add(event) | ||
below_event = sweep_line.below(event) | ||
if below_event is not None: | ||
events_queue.detect_intersection(below_event, event, | ||
sweep_line) | ||
above_event = sweep_line.above(event) | ||
if above_event is not None: | ||
events_queue.detect_intersection(event, above_event, | ||
sweep_line) | ||
else: | ||
# found equal segments' fragments | ||
equal_segment_event.merge_with(event) | ||
else: | ||
event = event.left | ||
equal_segment_event = sweep_line.find_equal(event) | ||
if equal_segment_event is not None: | ||
above_event, below_event = ( | ||
sweep_line.above(equal_segment_event), | ||
sweep_line.below(equal_segment_event)) | ||
sweep_line.remove(equal_segment_event) | ||
if below_event is not None and above_event is not None: | ||
events_queue.detect_intersection(below_event, above_event, | ||
sweep_line) | ||
if event is not equal_segment_event: | ||
event.merge_with(equal_segment_event) | ||
complete_events_relations(same_start_events, | ||
context=context) | ||
yield from to_processed_events(same_start_events) | ||
|
||
|
||
def complete_events_relations(same_start_events: Sequence[Event], | ||
*, | ||
context: Context) -> None: | ||
for first, second in to_pairs_combinations(same_start_events): | ||
first_left, second_left = (first if first.is_left else first.left, | ||
second if second.is_left else second.left) | ||
segments_overlap = ( | ||
first.is_left is not second.is_left | ||
and first.original_start != second.original_start | ||
and (context.angle_orientation(first.start, first.end, | ||
second.end) | ||
is Orientation.COLLINEAR)) | ||
if segments_overlap: | ||
relation = classify_overlap(first_left.original_start, | ||
first_left.original_end, | ||
second_left.original_start, | ||
second_left.original_end) | ||
else: | ||
intersection_point = first.start | ||
relation = (Relation.TOUCH | ||
if (intersection_point == first.original_start | ||
or intersection_point == second.original_start) | ||
else Relation.CROSS) | ||
first.register_tangent(second) | ||
second.register_tangent(first) | ||
first_left.register_relation(relation) | ||
second_left.register_relation(relation.complement) | ||
|
||
|
||
def to_processed_events(events: Iterable[Event]) -> List[LeftEvent]: | ||
return [candidate.left for candidate in events if not candidate.is_left] |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,189 @@ | ||
from abc import (ABC, | ||
abstractmethod) | ||
from reprlib import recursive_repr | ||
from typing import (Dict, | ||
List, | ||
Optional) | ||
Optional, | ||
Sequence, | ||
Set) | ||
|
||
from ground.base import Relation | ||
from ground.hints import Point | ||
from ground.hints import (Point, | ||
Segment) | ||
from reprit.base import generate_repr | ||
|
||
from .hints import Ids | ||
from .utils import (classify_overlap, | ||
to_sorted_pair) | ||
|
||
|
||
class Event: | ||
__slots__ = ('complement', 'is_left_endpoint', 'original_start', | ||
'relations', 'segments_ids', 'start') | ||
class Event(ABC): | ||
__slots__ = () | ||
|
||
is_left = False | ||
left = None # type: Optional['LeftEvent'] | ||
right = None # type: Optional['RightEvent'] | ||
|
||
@property | ||
@abstractmethod | ||
def end(self) -> Point: | ||
"""Returns end of the event.""" | ||
|
||
@property | ||
@abstractmethod | ||
def original_end(self) -> Point: | ||
"""Returns original end of the event.""" | ||
|
||
@property | ||
@abstractmethod | ||
def original_start(self) -> Point: | ||
"""Returns original start of the segment.""" | ||
|
||
@abstractmethod | ||
def register_tangent(self, tangent: 'Event') -> None: | ||
"""Registers new tangent to the event""" | ||
|
||
@property | ||
@abstractmethod | ||
def segments_ids(self) -> Set[int]: | ||
"""Returns segments ids of the event.""" | ||
|
||
@property | ||
@abstractmethod | ||
def start(self) -> Point: | ||
"""Returns start of the event.""" | ||
|
||
@property | ||
@abstractmethod | ||
def tangents(self) -> Sequence['Event']: | ||
"""Returns tangents of the event.""" | ||
|
||
|
||
class LeftEvent(Event): | ||
@classmethod | ||
def from_segment(cls, segment: Segment, segment_id: int) -> 'LeftEvent': | ||
start, end = to_sorted_pair(segment.start, segment.end) | ||
result = LeftEvent(start, None, start, {start: {end: {segment_id}}}) | ||
result.right = RightEvent(end, result, end) | ||
return result | ||
|
||
is_left = True | ||
|
||
@property | ||
def segments_ids(self) -> Set[int]: | ||
return self.parts_ids[self.start][self.end] | ||
|
||
@property | ||
def start(self) -> Point: | ||
return self._start | ||
|
||
@property | ||
def original_start(self) -> Point: | ||
return self._original_start | ||
|
||
@property | ||
def end(self) -> Point: | ||
return self.right.start | ||
|
||
@property | ||
def original_end(self) -> Point: | ||
return self.right.original_start | ||
|
||
@property | ||
def tangents(self) -> Sequence[Event]: | ||
return self._tangents | ||
|
||
__slots__ = ('right', 'parts_ids', '_original_start', | ||
'_start', '_tangents', '_relations_mask') | ||
|
||
def __init__(self, | ||
start: Point, | ||
complement: Optional['Event'], | ||
is_left_endpoint: bool, | ||
right: Optional['RightEvent'], | ||
original_start: Point, | ||
segments_ids: Ids, | ||
relations: Dict[Relation, List[Ids]]) -> None: | ||
self.complement, self.original_start, self.start = ( | ||
complement, original_start, start) | ||
self.is_left_endpoint = is_left_endpoint | ||
self.relations, self.segments_ids = relations, segments_ids | ||
parts_ids: Dict[Point, Dict[Point, Set[int]]]) -> None: | ||
self.right, self.parts_ids, self._original_start, self._start = ( | ||
right, parts_ids, original_start, start) | ||
self._relations_mask = 0 | ||
self._tangents = [] # type: List[Event] | ||
|
||
__repr__ = recursive_repr()(generate_repr(__init__)) | ||
|
||
def divide(self, break_point: Point) -> 'LeftEvent': | ||
"""Divides the event at given break point and returns tail.""" | ||
segments_ids = self.segments_ids | ||
(self.parts_ids.setdefault(self.start, {}) | ||
.setdefault(break_point, set()).update(segments_ids)) | ||
(self.parts_ids.setdefault(break_point, {}) | ||
.setdefault(self.end, set()).update(segments_ids)) | ||
result = self.right.left = LeftEvent( | ||
break_point, self.right, self.original_start, | ||
self.parts_ids) | ||
self.right = RightEvent(break_point, self, self.original_end) | ||
return result | ||
|
||
def has_only_relations(self, *relations: Relation) -> bool: | ||
mask = self._relations_mask | ||
for relation in relations: | ||
mask &= ~(1 << relation) | ||
return not mask | ||
|
||
def merge_with(self, other: 'LeftEvent') -> None: | ||
assert self.start == other.start and self.end == other.end | ||
full_relation = classify_overlap( | ||
other.original_start, other.original_end, self.original_start, | ||
self.original_end) | ||
self.register_relation(full_relation) | ||
other.register_relation(full_relation.complement) | ||
start, end = self.start, self.end | ||
self.parts_ids[start][end] = other.parts_ids[start][end] = ( | ||
self.parts_ids[start][end] | other.parts_ids[start][end]) | ||
|
||
def register_tangent(self, tangent: Event) -> None: | ||
assert self.start == tangent.start | ||
assert tangent not in self._tangents | ||
self._tangents.append(tangent) | ||
|
||
def register_relation(self, relation: Relation) -> None: | ||
self._relations_mask |= 1 << relation | ||
|
||
|
||
class RightEvent(Event): | ||
@property | ||
def end(self) -> Point: | ||
return self.complement.start | ||
return self.left.start | ||
|
||
@property | ||
def original_end(self) -> Point: | ||
return self.complement.original_start | ||
return self.left.original_start | ||
|
||
@property | ||
def original_start(self) -> Point: | ||
return self._original_start | ||
|
||
@property | ||
def segments_ids(self) -> Set[int]: | ||
return self.left.segments_ids | ||
|
||
@property | ||
def start(self) -> Point: | ||
return self._start | ||
|
||
@property | ||
def tangents(self) -> Sequence[Event]: | ||
return self._tangents | ||
|
||
__slots__ = 'left', '_original_start', '_start', '_tangents' | ||
|
||
def __init__(self, | ||
start: Point, | ||
left: Optional[LeftEvent], | ||
original_start: Point) -> None: | ||
self.left, self._original_start, self._start = (left, original_start, | ||
start) | ||
self._tangents = [] # type: List[Event] | ||
|
||
__repr__ = recursive_repr()(generate_repr(__init__)) | ||
|
||
def register_tangent(self, tangent: 'Event') -> None: | ||
assert self.start == tangent.start | ||
assert tangent not in self._tangents | ||
self._tangents.append(tangent) |
Oops, something went wrong.