Ejemplo n.º 1
0
def _unite_segments_cross(first: Segment,
                          second: Segment,
                          context: Context) -> Multisegment:
    cross_point = context.segments_intersection(first, second)
    segment_cls = context.segment_cls
    return context.multisegment_cls([segment_cls(first.start, cross_point),
                                     segment_cls(second.start, cross_point),
                                     segment_cls(cross_point, first.end),
                                     segment_cls(cross_point, second.end)])
Ejemplo n.º 2
0
def test_step(context: Context, segments: List[Segment]) -> None:
    *rest_segments, last_segment = segments

    result = segments_intersections(rest_segments)
    next_result = segments_intersections(segments)

    assert (next_result.keys() == (
        result.keys()
        | {(index, len(segments) - 1)
           for index, segment in enumerate(rest_segments) if context.
           segments_relation(segment, last_segment) is not Relation.DISJOINT}))
    assert result.items() <= next_result.items()
    assert all(segment_id < next_segment_id == len(segments) - 1
               for segment_id, next_segment_id in (next_result.keys() -
                                                   result.keys()))
    assert all(
        context.segments_intersection(
            segments[segment_id], segments[next_segment_id]) == next_result[(
                segment_id, next_segment_id)][0] if len(next_result[(
                    segment_id,
                    next_segment_id)]) == 1 else context.segments_intersection(
                        segments[segment_id], segments[next_segment_id]) not in
        (Relation.DISJOINT, Relation.TOUCH, Relation.CROSS) and (
            to_sorted_pair(*next_result[(segment_id, next_segment_id)]) ==
            next_result[(segment_id, next_segment_id)]) and all(
                context.segment_contains_point(segments[segment_id], point)
                for point in next_result[(segment_id, next_segment_id)])
        and all(
            context.segment_contains_point(segments[next_segment_id], point)
            for point in next_result[(segment_id, next_segment_id)])
        for segment_id, next_segment_id in (next_result.keys() -
                                            result.keys()))
    assert all(
        context.segments_relation(segments[segment_id],
                                  segments[next_segment_id])
        is not Relation.DISJOINT
        for segment_id, next_segment_id in (next_result.keys() -
                                            result.keys()))
Ejemplo n.º 3
0
def intersect_segments(first: Segment,
                       second: Segment,
                       context: Context) -> Union_[Empty, Multipoint, Segment]:
    relation = context.segments_relation(first, second)
    if relation is Relation.DISJOINT:
        return context.empty
    elif relation is Relation.TOUCH or relation is Relation.CROSS:
        return context.multipoint_cls([context.segments_intersection(first,
                                                                     second)])
    else:
        return (first
                if relation is Relation.EQUAL or relation is Relation.COMPONENT
                else
                (second
                 if relation is Relation.COMPOSITE
                 else _intersect_segments_overlap(first, second, context)))
Ejemplo n.º 4
0
def _subtract_segment_from_multisegment(multisegment: Multisegment,
                                        segment: Segment,
                                        context: Context) -> List[Segment]:
    result = []
    segment_cls = context.segment_cls
    for index, sub_segment in enumerate(multisegment.segments):
        relation = context.segments_relation(segment, sub_segment)
        if relation is Relation.EQUAL:
            result.extend(multisegment.segments[index + 1:])
            break
        elif relation is Relation.COMPONENT:
            left_start, left_end, right_start, right_end = sorted([
                sub_segment.start, sub_segment.end, segment.start,
                segment.end])
            result.extend(
                    [segment_cls(right_start, right_end)]
                    if left_start == segment.start or left_start == segment.end
                    else
                    ((([segment_cls(left_start, left_end)]
                       if right_start == right_end
                       else [segment_cls(left_start, left_end),
                             segment_cls(right_start, right_end)])
                      if (right_start == segment.start
                          or right_start == segment.end)
                      else [segment_cls(left_start, left_end)])
                     if left_end == segment.start or left_end == segment.end
                     else [segment_cls(left_start, right_start)]))
            result.extend(multisegment.segments[index + 1:])
            break
        elif relation is Relation.CROSS:
            cross_point = context.segments_intersection(sub_segment, segment)
            result.append(segment_cls(sub_segment.start, cross_point))
            result.append(segment_cls(cross_point, sub_segment.end))
        elif relation is Relation.OVERLAP:
            result.append(_subtract_segments_overlap(sub_segment, segment,
                                                     context))
        elif relation is not Relation.COMPOSITE:
            result.append(sub_segment)
    return result
Ejemplo n.º 5
0
def relate_segment(multisegment: Multisegment, segment: Segment,
                   context: Context) -> Relation:
    is_segment_superset = has_no_touch = has_no_cross = has_no_overlap = True
    # orientations of multisegment's segments
    # which touch given segment in the middle
    middle_touching_orientations = {}  # type: Dict[Point, Orientation]
    components = []
    start, end = segment_endpoints = segment.start, segment.end
    if start > end:
        start, end = end, start
    for index, sub_segment in enumerate(multisegment.segments):
        sub_segment_endpoints = sub_segment.start, sub_segment.end
        relation = relate_segments(sub_segment, segment, context)
        if relation is Relation.COMPONENT or relation is Relation.EQUAL:
            return Relation.COMPONENT
        elif relation is Relation.COMPOSITE:
            if has_no_overlap:
                has_no_overlap = False
            if start in sub_segment_endpoints:
                start = max(sub_segment_endpoints)
                segment_endpoints = start, end
            elif end in sub_segment_endpoints:
                end = min(sub_segment_endpoints)
                segment_endpoints = start, end
            else:
                components.append(to_sorted_pair(sub_segment_endpoints))
        elif relation is Relation.OVERLAP:
            if is_segment_superset:
                is_segment_superset = False
            if has_no_overlap:
                has_no_overlap = False
            start, end = segment_endpoints = _subtract_segments_overlap(
                segment_endpoints, sub_segment_endpoints)
        else:
            if is_segment_superset:
                is_segment_superset = False
            if has_no_overlap:
                if relation is Relation.TOUCH:
                    if has_no_touch:
                        has_no_touch = False
                    if has_no_cross:
                        intersection = context.segments_intersection(
                            sub_segment, segment)
                        if intersection != start and intersection != end:
                            sub_start, sub_end = sub_segment_endpoints
                            non_touched_endpoint = (sub_start if intersection
                                                    == sub_end else sub_end)
                            try:
                                previous_orientation = (
                                    middle_touching_orientations[intersection])
                            except KeyError:
                                middle_touching_orientations[intersection] = (
                                    context.angle_orientation(
                                        start, end, non_touched_endpoint))
                            else:
                                if (context.angle_orientation(
                                        start, end, non_touched_endpoint)
                                        is not previous_orientation):
                                    has_no_cross = False
                elif has_no_cross and relation is Relation.CROSS:
                    has_no_cross = False
    if has_no_overlap:
        return (Relation.DISJOINT if has_no_touch and has_no_cross else
                (Relation.TOUCH if has_no_cross else Relation.CROSS))
    elif components:
        components_iterator = iter(components)
        min_component_start, max_component_end = next(components_iterator)
        components_starts = {min_component_start}
        for component_start, component_end in components_iterator:
            components_starts.add(component_start)
            if min_component_start > component_start:
                min_component_start = component_start
            if max_component_end < component_end:
                max_component_end = component_end
        return (
            (Relation.EQUAL if is_segment_superset else Relation.COMPONENT) if
            (min_component_start == start and max_component_end == end
             and all(component_end in components_starts
                     or component_end == max_component_end
                     for _, component_end in components)) else
            (Relation.COMPOSITE if is_segment_superset else Relation.OVERLAP))
    else:
        return (
            (Relation.EQUAL if is_segment_superset else Relation.COMPONENT)
            if start == end else
            (Relation.COMPOSITE if is_segment_superset else Relation.OVERLAP))
Ejemplo n.º 6
0
def segments_intersection(context: Context, first_start, first_end,
                          second_start, second_end):
    return context.segments_intersection(first_start, first_end, second_start,
                                         second_end)