Пример #1
0
def intersects_with_polygon(box: Box, polygon: Polygon,
                            context: Context) -> bool:
    """
    Checks if the box intersects the polygon.
    """
    border = polygon.border
    polygon_box = context.contour_box(border)
    if not intersects_with(polygon_box, box):
        return False
    elif (is_subset_of(polygon_box, box)
          or any(contains_point(box, vertex) for vertex in border.vertices)):
        return True
    locations = [
        point_in_region(vertex, border)
        for vertex in to_vertices(box, context)
    ]
    if (within_of(box, polygon_box)
            and all(location is Location.INTERIOR for location in locations)
            and all(
                context.segments_relation(edge, border_edge) is
                Relation.DISJOINT for edge in to_edges(box, context)
                for border_edge in context.contour_segments(border))):
        return not any(
            within_of(box, context.contour_box(hole))
            and within_of_region(box, hole, context) for hole in polygon.holes)
    else:
        return (any(location is not Location.EXTERIOR
                    for location in locations) or any(
                        intersects_with_segment(box, edge, context)
                        for edge in context.contour_segments(border)))
Пример #2
0
def relate_region(polygon: Polygon, region: Region,
                  context: Context) -> Relation:
    region_bounding_box = context.contour_box(region)
    border, holes = polygon.border, polygon.holes
    relation_with_border = relate_regions(border, region,
                                          context.contour_box(border),
                                          region_bounding_box, context)
    if relation_with_border in (Relation.DISJOINT, Relation.TOUCH,
                                Relation.OVERLAP, Relation.COVER,
                                Relation.ENCLOSES):
        return relation_with_border
    elif (relation_with_border is Relation.COMPOSITE
          or relation_with_border is Relation.EQUAL):
        return (Relation.ENCLOSES if holes else relation_with_border)
    else:
        relation_with_holes = relate_region_to_regions(holes, region,
                                                       region_bounding_box,
                                                       context)
        if relation_with_holes is Relation.DISJOINT:
            return relation_with_border
        elif relation_with_holes is Relation.TOUCH:
            return Relation.ENCLOSED
        elif relation_with_holes in (Relation.EQUAL, Relation.COMPONENT,
                                     Relation.ENCLOSED):
            return Relation.TOUCH
        elif relation_with_holes is Relation.WITHIN:
            return Relation.DISJOINT
        else:
            return Relation.OVERLAP
Пример #3
0
def relate_multiregion(polygon: Polygon, multiregion: Multiregion,
                       context: Context) -> Relation:
    border, holes = polygon.border, polygon.holes
    border_bounding_box = context.contour_box(border)
    if not holes:
        return relate_region_to_regions(multiregion, border,
                                        border_bounding_box,
                                        context).complement
    none_touch = True
    subsets_regions_indices = []
    for region_index, region in enumerate(multiregion):
        region_relation = relate_regions(border, region, border_bounding_box,
                                         context.contour_box(region), context)
        if region_relation is Relation.TOUCH:
            if none_touch:
                none_touch = False
        elif region_relation in (Relation.OVERLAP, Relation.COVER,
                                 Relation.ENCLOSES):
            return region_relation
        elif (region_relation is Relation.COMPOSITE
              or region_relation is Relation.EQUAL):
            return Relation.ENCLOSES
        elif region_relation is not Relation.DISJOINT:
            if none_touch and (region_relation is Relation.ENCLOSED
                               or region_relation is Relation.COMPONENT):
                none_touch = False
            subsets_regions_indices.append(region_index)
    if subsets_regions_indices:
        is_subset_of_border = len(subsets_regions_indices) == len(multiregion)
        relation_with_holes = relate_multiregions(
            holes, multiregion if is_subset_of_border else
            [multiregion[index] for index in subsets_regions_indices], context)
        if relation_with_holes is Relation.DISJOINT:
            return ((Relation.WITHIN if none_touch else Relation.ENCLOSED)
                    if is_subset_of_border else Relation.OVERLAP)
        elif relation_with_holes is Relation.TOUCH:
            return (Relation.ENCLOSED
                    if is_subset_of_border else Relation.OVERLAP)
        elif relation_with_holes is Relation.OVERLAP:
            return relation_with_holes
        elif relation_with_holes in (Relation.COVER, Relation.ENCLOSES,
                                     Relation.COMPOSITE):
            return Relation.OVERLAP
        elif relation_with_holes is Relation.WITHIN:
            return (Relation.DISJOINT if none_touch else Relation.TOUCH)
        else:
            return Relation.TOUCH
    else:
        return (Relation.DISJOINT if none_touch else Relation.TOUCH)
Пример #4
0
def relate_region(multipolygon: Multipolygon, region: Region,
                  context: Context) -> Relation:
    region_bounding_box = context.contour_box(region)
    all_disjoint, none_disjoint, multipolygon_max_x, events_queue = (True,
                                                                     True,
                                                                     None,
                                                                     None)
    for polygon in multipolygon.polygons:
        polygon_bounding_box = context.polygon_box(polygon)
        if box.disjoint_with(region_bounding_box, polygon_bounding_box):
            if none_disjoint:
                none_disjoint = False
        else:
            if all_disjoint:
                all_disjoint = False
                multipolygon_max_x = polygon_bounding_box.max_x
                events_queue = CompoundEventsQueue(context)
                events_queue.register(region_to_oriented_segments(
                    region, context),
                                      from_test=True)
            else:
                multipolygon_max_x = max(multipolygon_max_x,
                                         polygon_bounding_box.max_x)
            events_queue.register(polygon_to_oriented_segments(
                polygon, context),
                                  from_test=False)
    if all_disjoint:
        return Relation.DISJOINT
    relation = process_compound_queue(
        events_queue, min(multipolygon_max_x, region_bounding_box.max_x))
    return (relation if none_disjoint else (
        Relation.COMPONENT if relation is Relation.EQUAL else
        (Relation.OVERLAP if relation in (Relation.COVER, Relation.ENCLOSES,
                                          Relation.COMPOSITE) else relation)))
Пример #5
0
def _relate_region(goal_regions: Iterable[Region], region: Region,
                   region_bounding_box: Box, context: Context) -> Relation:
    all_disjoint, none_disjoint, goal_regions_max_x, events_queue = (True,
                                                                     True,
                                                                     None,
                                                                     None)
    for goal_region in goal_regions:
        goal_region_bounding_box = context.contour_box(goal_region)
        if box.disjoint_with(region_bounding_box, goal_region_bounding_box):
            if none_disjoint:
                none_disjoint = False
        else:
            if all_disjoint:
                all_disjoint = False
                goal_regions_max_x = goal_region_bounding_box.max_x
                events_queue = CompoundEventsQueue(context)
                events_queue.register(region_to_oriented_segments(
                    region, context),
                                      from_test=True)
            else:
                goal_regions_max_x = max(goal_regions_max_x,
                                         goal_region_bounding_box.max_x)
            events_queue.register(region_to_oriented_segments(
                goal_region, context),
                                  from_test=False)
    if all_disjoint:
        return Relation.DISJOINT
    relation = process_compound_queue(
        events_queue, min(goal_regions_max_x, region_bounding_box.max_x))
    return (relation if none_disjoint else (
        Relation.COMPONENT if relation is Relation.EQUAL else
        (Relation.OVERLAP if relation in (Relation.COVER, Relation.ENCLOSES,
                                          Relation.COMPOSITE) else relation)))
Пример #6
0
def relate_contour(polygon: Polygon, contour: Contour,
                   context: Context) -> Relation:
    contour_bounding_box = context.contour_box(contour)
    relation_without_holes = relate_contour_to_region(polygon.border, contour,
                                                      contour_bounding_box,
                                                      context)
    holes = polygon.holes
    if holes and (relation_without_holes is Relation.ENCLOSED
                  or relation_without_holes is Relation.WITHIN):
        relation_with_holes = relate_contour_to_multiregion(
            holes, contour, contour_bounding_box, context)
        if relation_with_holes is Relation.DISJOINT:
            return relation_without_holes
        elif relation_with_holes is Relation.TOUCH:
            return Relation.ENCLOSED
        elif (relation_with_holes is Relation.CROSS
              or relation_with_holes is Relation.COMPONENT):
            return relation_with_holes
        elif relation_with_holes is Relation.ENCLOSED:
            return Relation.TOUCH
        else:
            # contour is within holes
            return Relation.DISJOINT
    else:
        return relation_without_holes
Пример #7
0
def coupled_with_polygon(box: Box, polygon: Polygon, context: Context) -> bool:
    """
    Checks if the box intersects the polygon in continuous points set.
    """
    border = polygon.border
    polygon_box = context.contour_box(border)
    if not coupled_with(polygon_box, box):
        return False
    elif (is_subset_of(polygon_box, box)
          or any(covers_point(box, vertex) for vertex in border.vertices)):
        return True
    locations = [
        point_in_region(vertex, border)
        for vertex in to_vertices(box, context)
    ]
    if any(location is Location.INTERIOR for location in locations):
        return (not all(location is Location.INTERIOR
                        for location in locations)
                or not is_subset_of_multiregion(box, polygon.holes, context))
    else:
        return (not is_subset_of_multiregion(box, polygon.holes, context) if
                (is_subset_of(box, polygon_box)
                 and is_subset_of_region(box, border, context)) else any(
                     segment_in_contour(segment, border) is Relation.OVERLAP
                     or segment_in_region(segment, border) in (
                         Relation.CROSS, Relation.COMPONENT, Relation.ENCLOSED)
                     for segment in to_edges(box, context)))
Пример #8
0
def is_subset_of_multiregion(box: Box, multiregion: Multiregion,
                             context: Context) -> bool:
    """
    Checks if the box is the subset of the multiregion.
    """
    return any(
        is_subset_of(box, context.contour_box(region))
        and is_subset_of_region(box, region, context)
        for region in multiregion)
Пример #9
0
def relate_multisegment(contour: Contour, multisegment: Multisegment,
                        context: Context) -> Relation:
    contour_bounding_box = context.contour_box(contour)
    multisegment_bounding_box = context.segments_box(multisegment.segments)
    if box.disjoint_with(contour_bounding_box, multisegment_bounding_box):
        return Relation.DISJOINT
    events_queue = LinearEventsQueue(context)
    events_queue.register(to_edges_endpoints(contour), from_test=False)
    events_queue.register(to_segments_endpoints(multisegment), from_test=True)
    return process_open_linear_queue(
        events_queue,
        min(contour_bounding_box.max_x, multisegment_bounding_box.max_x))
Пример #10
0
def relate_multisegment(region: Region, multisegment: Multisegment,
                        context: Context) -> Relation:
    multisegment_bounding_box = context.segments_box(multisegment.segments)
    region_bounding_box = context.contour_box(region)
    if box.disjoint_with(multisegment_bounding_box, region_bounding_box):
        return Relation.DISJOINT
    events_queue = CompoundEventsQueue(context)
    events_queue.register(to_oriented_segments(region, context),
                          from_test=False)
    events_queue.register(to_segments_endpoints(multisegment), from_test=True)
    return process_linear_compound_queue(
        events_queue,
        min(multisegment_bounding_box.max_x, region_bounding_box.max_x))
Пример #11
0
def intersects_with_region(box: Box, region: Region, context: Context) -> bool:
    """
    Checks if the box intersects the region.
    """
    region_box = context.contour_box(region)
    return (intersects_with(region_box, box)
            and (is_subset_of(region_box, box) or any(
                contains_point(box, vertex)
                for vertex in region.vertices) or any(
                    point_in_region(vertex, region) is not Location.EXTERIOR
                    for vertex in to_vertices(box, context)) or any(
                        intersects_with_segment(box, edge, context)
                        for edge in context.contour_segments(region))))
Пример #12
0
def relate_contour(goal: Contour, test: Contour, context: Context) -> Relation:
    goal_bounding_box, test_bounding_box = (context.contour_box(goal),
                                            context.contour_box(test))
    if box.disjoint_with(goal_bounding_box, test_bounding_box):
        return Relation.DISJOINT
    if equal(goal, test, context):
        return Relation.EQUAL
    events_queue = CompoundEventsQueue(context)
    events_queue.register(to_oriented_edges_endpoints(goal, context),
                          from_test=False)
    events_queue.register(to_oriented_edges_endpoints(test, context),
                          from_test=True)
    return process_closed_linear_queue(
        events_queue, min(goal_bounding_box.max_x, test_bounding_box.max_x))
Пример #13
0
def _relate_contour(region: Region, contour: Contour,
                    contour_bounding_box: Box, context: Context) -> Relation:
    region_bounding_box = context.contour_box(region)
    if box.disjoint_with(contour_bounding_box, region_bounding_box):
        return Relation.DISJOINT
    if equal(region, contour, context):
        return Relation.COMPONENT
    events_queue = CompoundEventsQueue(context)
    events_queue.register(to_oriented_segments(region, context),
                          from_test=False)
    events_queue.register(contour_to_edges_endpoints(contour), from_test=True)
    return process_linear_compound_queue(
        events_queue, min(contour_bounding_box.max_x,
                          region_bounding_box.max_x))
Пример #14
0
def coupled_with_region(box: Box, region: Region, context: Context) -> bool:
    """
    Checks if the box intersects the region in continuous points set.
    """
    region_box = context.contour_box(region)
    if not coupled_with(region_box, box):
        return False
    elif (is_subset_of(region_box, box)
          or any(covers_point(box, vertex) for vertex in region.vertices)):
        return True
    return (any(
        point_in_region(vertex, region) is Location.INTERIOR
        for vertex in to_vertices(box, context))
            or is_subset_of(box, region_box)
            and is_subset_of_region(box, region, context) or any(
                segment_in_contour(segment, region) is Relation.OVERLAP
                or segment_in_region(segment, region) in
                (Relation.CROSS, Relation.COMPONENT, Relation.ENCLOSED)
                for segment in to_edges(box, context)))
Пример #15
0
def _relate_contour(multiregion: Multiregion, contour: Contour,
                    contour_bounding_box: Box, context: Context) -> Relation:
    disjoint, multiregion_max_x, events_queue = True, None, None
    for region in multiregion:
        region_bounding_box = context.contour_box(region)
        if not box.disjoint_with(region_bounding_box, contour_bounding_box):
            if disjoint:
                disjoint = False
                multiregion_max_x = region_bounding_box.max_x
                events_queue = CompoundEventsQueue(context)
                events_queue.register(contour_to_edges_endpoints(contour),
                                      from_test=True)
            else:
                multiregion_max_x = max(multiregion_max_x,
                                        region_bounding_box.max_x)
            events_queue.register(region_to_oriented_segments(region, context),
                                  from_test=False)
    return (Relation.DISJOINT if disjoint else process_linear_compound_queue(
        events_queue, min(contour_bounding_box.max_x, multiregion_max_x)))
Пример #16
0
def relate_polygon(goal: Polygon, test: Polygon, context: Context) -> Relation:
    goal_bounding_box, test_bounding_box = (context.polygon_box(goal),
                                            context.polygon_box(test))
    goal_border, goal_holes = goal.border, goal.holes
    test_border, test_holes = test.border, test.holes
    borders_relation = relate_regions(goal_border, test_border,
                                      goal_bounding_box, test_bounding_box,
                                      context)
    if borders_relation in (Relation.DISJOINT, Relation.TOUCH,
                            Relation.OVERLAP):
        return borders_relation
    elif borders_relation is Relation.EQUAL:
        if goal_holes and test_holes:
            holes_relation = relate_multiregions(test_holes, goal_holes,
                                                 context)
            if holes_relation in (Relation.DISJOINT, Relation.TOUCH,
                                  Relation.OVERLAP):
                return Relation.OVERLAP
            elif holes_relation in (Relation.COVER, Relation.ENCLOSES,
                                    Relation.COMPOSITE):
                return Relation.ENCLOSES
            elif holes_relation is Relation.EQUAL:
                return borders_relation
            else:
                return Relation.ENCLOSED
        else:
            return (Relation.ENCLOSES if goal_holes else
                    (Relation.ENCLOSED if test_holes else Relation.EQUAL))
    elif borders_relation in (Relation.WITHIN, Relation.ENCLOSED,
                              Relation.COMPONENT):
        if goal_holes:
            none_touch = True
            subsets_holes_indices = []
            for hole_index, hole in enumerate(goal_holes):
                hole_relation = relate_regions(test_border, hole,
                                               test_bounding_box,
                                               context.contour_box(hole),
                                               context)
                if hole_relation is Relation.TOUCH:
                    if none_touch:
                        none_touch = False
                elif hole_relation is Relation.OVERLAP:
                    return hole_relation
                elif hole_relation is Relation.COVER:
                    return Relation.DISJOINT
                elif hole_relation in (Relation.ENCLOSES, Relation.COMPOSITE,
                                       Relation.EQUAL):
                    return Relation.TOUCH
                elif hole_relation is not Relation.DISJOINT:
                    subsets_holes_indices.append(hole_index)
            if subsets_holes_indices:
                holes_relation = (relate_multiregions(
                    test_holes, goal_holes
                    if len(subsets_holes_indices) == len(goal_holes) else
                    [goal_holes[index] for index in subsets_holes_indices],
                    context) if test_holes else Relation.DISJOINT)
                if holes_relation is Relation.EQUAL:
                    return (Relation.ENCLOSED
                            if borders_relation is Relation.WITHIN else
                            borders_relation)
                elif (holes_relation is Relation.COMPONENT
                      or holes_relation is Relation.ENCLOSED):
                    return Relation.ENCLOSED
                elif holes_relation is Relation.WITHIN:
                    return borders_relation
                else:
                    return Relation.OVERLAP
            else:
                return (borders_relation if none_touch else Relation.ENCLOSED)
        else:
            return (Relation.ENCLOSED
                    if test_holes and borders_relation is Relation.COMPONENT
                    else borders_relation)
    elif test_holes:
        none_touch = True
        subsets_holes_indices = []
        for hole_index, hole in enumerate(test_holes):
            hole_relation = relate_regions(goal_border, hole,
                                           goal_bounding_box,
                                           context.contour_box(hole), context)
            if hole_relation is Relation.TOUCH:
                if none_touch:
                    none_touch = False
            elif hole_relation is Relation.OVERLAP:
                return hole_relation
            elif hole_relation is Relation.COVER:
                return Relation.DISJOINT
            elif hole_relation in (Relation.ENCLOSES, Relation.COMPOSITE,
                                   Relation.EQUAL):
                return Relation.TOUCH
            elif hole_relation is not Relation.DISJOINT:
                subsets_holes_indices.append(hole_index)
        if subsets_holes_indices:
            holes_relation = (relate_multiregions(
                goal_holes,
                test_holes if len(subsets_holes_indices) == len(test_holes)
                else [test_holes[index]
                      for index in subsets_holes_indices], context)
                              if goal_holes else Relation.DISJOINT)
            if holes_relation is Relation.EQUAL:
                return (Relation.ENCLOSES if borders_relation is Relation.COVER
                        else borders_relation)
            elif (holes_relation is Relation.COMPONENT
                  or holes_relation is Relation.ENCLOSED):
                return Relation.ENCLOSES
            elif holes_relation is Relation.WITHIN:
                return borders_relation
            else:
                return Relation.OVERLAP
        else:
            return (borders_relation if none_touch else Relation.ENCLOSES)
    else:
        return (Relation.ENCLOSES
                if goal_holes and borders_relation is Relation.COMPOSITE else
                borders_relation)
Пример #17
0
def relate_region(goal: Region, test: Region, context: Context) -> Relation:
    return _relate_region(goal, test, context.contour_box(goal),
                          context.contour_box(test), context)
Пример #18
0
def relate_region(multiregion: Multiregion, region: Region,
                  context: Context) -> Relation:
    return _relate_region(multiregion, region, context.contour_box(region),
                          context)
Пример #19
0
def relate_contour(multiregion: Multiregion, contour: Contour,
                   context: Context) -> Relation:
    return _relate_contour(multiregion, contour, context.contour_box(contour),
                           context)
Пример #20
0
    def from_polygon(cls,
                     polygon: Polygon,
                     *,
                     shuffler: Shuffler = random.shuffle,
                     context: Context) -> 'Graph':
        """
        Constructs trapezoidal decomposition graph of given polygon.

        Based on incremental randomized algorithm by R. Seidel.

        Time complexity:
            ``O(vertices_count * log vertices_count)`` expected,
            ``O(vertices_count ** 2)`` worst
        Memory complexity:
            ``O(vertices_count)``

        where

            .. code-block:: python

                vertices_count = (len(polygon.border.vertices)
                                  + sum(len(hole.vertices)
                                        for hole in polygon.holes)
                                  + len(extra_points) + len(extra_constraints))

        Reference:
            https://doi.org/10.1016%2F0925-7721%2891%2990012-4
            https://www.cs.princeton.edu/courses/archive/fall05/cos528/handouts/A%20Simple%20and%20fast.pdf

        :param polygon: target polygon.
        :param shuffler:
            function which mutates sequence by shuffling its elements,
            required for randomization.
        :param context: geometric context.
        :returns: trapezoidal decomposition graph of the border and holes.

        >>> from ground.base import get_context
        >>> context = get_context()
        >>> Contour, Point, Polygon = (context.contour_cls, context.point_cls,
        ...                            context.polygon_cls)
        >>> graph = Graph.from_polygon(
        ...     Polygon(Contour([Point(0, 0), Point(6, 0), Point(6, 6),
        ...                      Point(0, 6)]),
        ...             [Contour([Point(2, 2), Point(2, 4), Point(4, 4),
        ...                       Point(4, 2)])]),
        ...     context=context)
        >>> Point(1, 1) in graph
        True
        >>> Point(2, 2) in graph
        True
        >>> Point(3, 3) in graph
        False
        >>> graph.locate(Point(1, 1)) is Location.INTERIOR
        True
        >>> graph.locate(Point(2, 2)) is Location.BOUNDARY
        True
        >>> graph.locate(Point(3, 3)) is Location.EXTERIOR
        True
        """
        border = polygon.border
        orienteer = context.angle_orientation
        is_border_positively_oriented = (to_contour_orientation(
            border, orienteer) is Orientation.COUNTERCLOCKWISE)
        edges = [
            Edge.from_endpoints(start, end, is_border_positively_oriented,
                                context)
            if start < end else Edge.from_endpoints(
                end, start, not is_border_positively_oriented, context)
            for start, end in contour_to_edges_endpoints(border)
        ]
        for hole in polygon.holes:
            is_hole_negatively_oriented = (to_contour_orientation(
                hole, orienteer) is Orientation.CLOCKWISE)
            edges.extend(
                Edge.from_endpoints(
                    start, end, is_hole_negatively_oriented, context
                ) if start < end else Edge.from_endpoints(
                    end, start, not is_hole_negatively_oriented, context)
                for start, end in contour_to_edges_endpoints(hole))
        shuffler(edges)
        result = cls(box_to_node(context.contour_box(border), context))
        for edge in edges:
            add_edge(result, edge)
        return result