Пример #1
0
def _relate_segment_to_contour(contour: Contour, segment: Segment,
                               context: Context) -> Relation:
    # similar to segment-in-contour check
    # but cross has higher priority over overlap
    # because cross with contour will be considered as cross with region
    # whereas overlap with contour can't be an overlap with region
    # and should be classified by further analysis
    has_no_touch = has_no_overlap = True
    last_touched_edge_index = last_touched_edge_start = None
    start, end = segment.start, segment.end
    for index, edge in enumerate(context.contour_segments(contour)):
        edge_start, edge_end = edge_endpoints = edge.start, edge.end
        relation_with_edge = relate_segments(edge, segment, context)
        if (relation_with_edge is Relation.COMPONENT
                or relation_with_edge is Relation.EQUAL):
            return Relation.COMPONENT
        elif (relation_with_edge is Relation.OVERLAP
              or relation_with_edge is Relation.COMPOSITE):
            if has_no_overlap:
                has_no_overlap = False
        elif relation_with_edge is Relation.TOUCH:
            if has_no_touch:
                has_no_touch = False
            elif (index - last_touched_edge_index == 1
                  and start not in edge_endpoints
                  and end not in edge_endpoints and (context.angle_orientation(
                      start, end, edge_start) is Orientation.COLLINEAR)
                  and point_vertex_line_divides_angle(
                      start, last_touched_edge_start, edge_start, edge_end,
                      context)):
                return Relation.CROSS
            last_touched_edge_index = index
            last_touched_edge_start = edge_start
        elif relation_with_edge is Relation.CROSS:
            return Relation.CROSS
    vertices = contour.vertices
    if not has_no_touch and last_touched_edge_index == len(vertices) - 1:
        first_edge_endpoints = first_edge_start, first_edge_end = (
            vertices[-1], vertices[0])
        if (relate_segments(
                context.segment_cls(first_edge_start, first_edge_end), segment,
                context) is Relation.TOUCH
                and start not in first_edge_endpoints
                and end not in first_edge_endpoints
                and (context.angle_orientation(start, end, first_edge_start) is
                     Orientation.COLLINEAR)
                and point_vertex_line_divides_angle(
                    start, vertices[-2], first_edge_start, first_edge_end,
                    context)):
            return Relation.CROSS
    return ((Relation.DISJOINT if has_no_touch else Relation.TOUCH)
            if has_no_overlap else Relation.OVERLAP)
Пример #2
0
def to_contour_vertices_orientation(vertices: Sequence[Point],
                                    context: Context) -> Orientation:
    if len(vertices) < 3:
        return Orientation.COLLINEAR
    index = min(range(len(vertices)), key=vertices.__getitem__)
    return context.angle_orientation(vertices[index - 1], vertices[index],
                                     vertices[(index + 1) % len(vertices)])
Пример #3
0
def _locate_point(region: Region, point: Point,
                  context: Context) -> Tuple[Optional[int], Location]:
    result = False
    point_y = point.y
    for index, edge in enumerate(context.contour_segments(region)):
        if locate_point_in_segment(edge, point, context) is Location.BOUNDARY:
            return index, Location.BOUNDARY
        start, end = edge.start, edge.end
        if ((start.y > point_y) is not (end.y > point_y)
                and ((end.y > start.y) is (context.angle_orientation(
                    start, end, point) is Orientation.COUNTERCLOCKWISE))):
            result = not result
    return None, (Location.INTERIOR if result else Location.EXTERIOR)
Пример #4
0
def _unite_segments_touch(first: Segment,
                          second: Segment,
                          context: Context) -> Union_[Multisegment, Segment]:
    return (context.multisegment_cls([first, second])
            if ((first.start != second.start
                 or (context.angle_orientation(first.start, first.end,
                                               second.end)
                     is not Orientation.COLLINEAR))
                and (first.start != second.end
                     or (context.angle_orientation(first.start, first.end,
                                                   second.start)
                         is not Orientation.COLLINEAR))
                and (first.end != second.start
                     or (context.angle_orientation(first.end, first.start,
                                                   second.end)
                         is not Orientation.COLLINEAR))
                and (first.end != second.end
                     or (context.angle_orientation(first.end, first.start,
                                                   second.start)
                         is not Orientation.COLLINEAR)))
            else context.segment_cls(min(first.start, first.end,
                                         second.start, second.end),
                                     max(first.start, first.end,
                                         second.start, second.end)))
Пример #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))
Пример #6
0
def to_contour_orientation(contour: Contour, context: Context) -> Orientation:
    vertices = contour.vertices
    index = min(range(len(vertices)),
                key=vertices.__getitem__)
    return context.angle_orientation(vertices[index - 1], vertices[index],
                                     vertices[(index + 1) % len(vertices)])
Пример #7
0
def point_vertex_line_divides_angle(point: Point, first_ray_point: Point,
                                    vertex: Point, second_ray_point: Point,
                                    context: Context) -> bool:
    return (context.angle_orientation(vertex, first_ray_point, point) is
            context.angle_orientation(vertex, point, second_ray_point))