Beispiel #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)))
Beispiel #2
0
def within_of_region(box: Box, border: Contour, context: Context) -> bool:
    """
    Checks if the box is contained in an interior of the region.
    """
    return (all(
        point_in_region(vertex, border) is Location.INTERIOR
        for vertex in to_vertices(box, context)) 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)))
Beispiel #3
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)
Beispiel #4
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))))
Beispiel #5
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)
Beispiel #6
0
def relate_segment(contour: Contour, segment: Segment,
                   context: Context) -> Relation:
    angle_orientation = context.angle_orientation
    has_no_touch = has_no_cross = True
    last_touched_edge_index = last_touched_edge_start = None
    start, end = segment.start, segment.end
    for index, sub_segment in enumerate(context.contour_segments(contour)):
        sub_segment_start, sub_segment_end = 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.OVERLAP or relation is Relation.COMPOSITE:
            return Relation.OVERLAP
        elif relation is Relation.TOUCH:
            if has_no_touch:
                has_no_touch = False
            elif (has_no_cross and index - last_touched_edge_index == 1
                  and start not in sub_segment_endpoints
                  and end not in sub_segment_endpoints and (angle_orientation(
                      start, end, sub_segment_start) is Orientation.COLLINEAR)
                  and point_vertex_line_divides_angle(
                      start, last_touched_edge_start, sub_segment_start,
                      sub_segment_end, context)):
                has_no_cross = False
            last_touched_edge_index = index
            last_touched_edge_start = sub_segment_start
        elif has_no_cross and relation is Relation.CROSS:
            has_no_cross = False
    vertices = contour.vertices
    if (has_no_cross and not has_no_touch
            and last_touched_edge_index == len(vertices) - 1):
        first_sub_segment_endpoints = (first_sub_segment_start,
                                       first_sub_segment_end) = (vertices[-1],
                                                                 vertices[0])
        if (relate_segments(
                context.segment_cls(first_sub_segment_start,
                                    first_sub_segment_end), segment, context)
                is Relation.TOUCH and start not in first_sub_segment_endpoints
                and end not in first_sub_segment_endpoints
                and (angle_orientation(start, end, first_sub_segment_start) is
                     Orientation.COLLINEAR)
                and point_vertex_line_divides_angle(
                    start, vertices[-2], first_sub_segment_start,
                    first_sub_segment_end, context)):
            has_no_cross = False
    return ((Relation.DISJOINT if has_no_touch else Relation.TOUCH)
            if has_no_cross else Relation.CROSS)
Beispiel #7
0
    def constrained_delaunay(cls,
                             polygon: Polygon,
                             *,
                             extra_constraints: Sequence[Segment] = (),
                             extra_points: Sequence[Point] = (),
                             context: Context) -> 'Triangulation':
        """
        Constructs constrained Delaunay triangulation of given polygon
        (with potentially extra points and constraints).

        Based on

        * divide-and-conquer algorithm by L. Guibas & J. Stolfi
          for generating Delaunay triangulation,

        * algorithm by S. W. Sloan for adding constraints to Delaunay
          triangulation,

        * clipping algorithm by F. Martinez et al. for deleting in-hole
          triangles.

        Time complexity:
            ``O(vertices_count * log vertices_count)`` for convex polygons
            without extra constraints,
            ``O(vertices_count ** 2)`` otherwise
        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:
            http://www.sccg.sk/~samuelcik/dgs/quad_edge.pdf
            https://www.newcastle.edu.au/__data/assets/pdf_file/0019/22519/23_A-fast-algortithm-for-generating-constrained-Delaunay-triangulations.pdf
            https://doi.org/10.1016/j.advengsoft.2013.04.004
            http://www4.ujaen.es/~fmartin/bool_op.html

        :param polygon: target polygon.
        :param extra_points:
            additional points to be presented in the triangulation.
        :param extra_constraints:
            additional constraints to be presented in the triangulation.
        :param context: geometric context.
        :returns:
            triangulation of the border, holes & extra points
            considering constraints.
        """
        border, holes = polygon.border, polygon.holes
        if extra_points:
            border, holes, extra_points = complete_vertices(
                border, holes, extra_points, context)
        result = cls.delaunay(list(
            chain(border.vertices, flatten(hole.vertices for hole in holes),
                  extra_points)),
                              context=context)
        border_edges = context.contour_segments(border)
        constrain(
            result,
            chain(border_edges, flatten(map(context.contour_segments, holes)),
                  extra_constraints))
        bound(result, border_edges)
        cut(result, holes)
        result._triangular_holes_vertices.update(
            frozenset(hole.vertices) for hole in holes
            if len(hole.vertices) == 3)
        return result
Beispiel #8
0
def locate_point(contour: Contour, point: Point, context: Context) -> Location:
    return (Location.EXTERIOR if all(
        locate_point_to_segment(segment, point, context) is Location.EXTERIOR
        for segment in context.contour_segments(contour)) else
            Location.BOUNDARY)