Пример #1
0
def unpack_segments(segments: Sequence[Segment],
                    context: Context) -> Union[Empty, Multisegment, Segment]:
    return ((context.multisegment_cls(segments)
             if len(segments) > 1
             else segments[0])
            if segments
            else context.empty)
Пример #2
0
def unpack_polygons(polygons: Sequence[Polygon],
                    context: Context) -> Union[Empty, Multipolygon, Polygon]:
    return ((context.multipolygon_cls(polygons)
             if len(polygons) > 1
             else polygons[0])
            if polygons
            else context.empty)
Пример #3
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)))
Пример #4
0
def constrict_convex_hull_size(
        points: Sequence[Point[Scalar]], *, context: Context,
        max_size: Optional[int]) -> Sequence[Point[Scalar]]:
    if max_size is None:
        return points
    convex_hull = context.points_convex_hull(points)
    if len(convex_hull) <= max_size:
        return points
    sorted_convex_hull = sorted(convex_hull,
                                key=partial(context.points_squared_distance,
                                            convex_hull[0]))
    new_border_points = []
    for index in range(max_size):
        quotient, remainder = divmod(index, 2)
        new_border_points.append(sorted_convex_hull[-quotient - 1] if remainder
                                 else sorted_convex_hull[quotient])
    orienteer = context.angle_orientation
    new_border = list(to_max_convex_hull(new_border_points, orienteer))
    new_border_extra_endpoints_pairs = tuple(
        {(new_border[index - 1], new_border[index])
         for index in range(len(new_border))} -
        {(convex_hull[index], convex_hull[index - 1])
         for index in range(len(convex_hull))})
    return (new_border + [
        point for point in set(points) - set(convex_hull) if all(
            orienteer(start, end, point) is Orientation.COUNTERCLOCKWISE
            for start, end in new_border_extra_endpoints_pairs)
    ])
Пример #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 to_convex_vertices_sequence(points: Sequence[Point[Scalar]],
                                random: Random,
                                context: Context) -> Sequence[Point[Scalar]]:
    """
    Based on Valtr algorithm by Sander Verdonschot.

    Time complexity:
        ``O(len(points) * log len(points))``
    Memory complexity:
        ``O(len(points))``
    Reference:
        http://cglab.ca/~sander/misc/ConvexGeneration/convex.html
    """
    xs, ys = [point.x for point in points], [point.y for point in points]
    xs, ys = sorted(xs), sorted(ys)
    min_x, *xs, max_x = xs
    min_y, *ys, max_y = ys

    def to_vectors_coordinates(coordinates: List[Scalar],
                               min_coordinate: Scalar,
                               max_coordinate: Scalar) -> List[Scalar]:
        last_min = last_max = min_coordinate
        result = []
        for coordinate in coordinates:
            if random.getrandbits(1):
                result.append(coordinate - last_min)
                last_min = coordinate
            else:
                result.append(last_max - coordinate)
                last_max = coordinate
        result.extend((max_coordinate - last_min, last_max - max_coordinate))
        return result

    vectors_xs = to_vectors_coordinates(xs, min_x, max_x)
    vectors_ys = to_vectors_coordinates(ys, min_y, max_y)
    random.shuffle(vectors_ys)

    def to_vector_angle(vector: Tuple[Scalar, Scalar]) -> Key:
        x, y = vector
        return atan2(y, x)

    vectors = sorted(zip(vectors_xs, vectors_ys), key=to_vector_angle)
    point_x = point_y = 0
    min_polygon_x = min_polygon_y = 0
    coordinates_pairs = []
    for vector_x, vector_y in vectors:
        coordinates_pairs.append((point_x, point_y))
        point_x += vector_x
        point_y += vector_y
        min_polygon_x, min_polygon_y = (min(min_polygon_x, point_x),
                                        min(min_polygon_y, point_y))
    shift_x, shift_y = min_x - min_polygon_x, min_y - min_polygon_y
    point_cls = context.point_cls
    return context.points_convex_hull([
        point_cls(min(max(x + shift_x, min_x), max_x),
                  min(max(y + shift_y, min_y), max_y))
        for x, y in coordinates_pairs
    ])
Пример #7
0
def unpack_regions(regions: Sequence[Contour],
                   context: Context) -> Union[Empty, Polygon, Multipolygon]:
    polygon_cls = context.polygon_cls
    return ((context.multipolygon_cls([polygon_cls(region, [])
                                       for region in regions])
             if len(regions) > 1
             else polygon_cls(regions[0], []))
            if regions
            else context.empty)
Пример #8
0
def unpack_linear_mix(discrete: Maybe[Multipoint],
                      linear: Maybe[Linear],
                      context: Context
                      ) -> Union[Empty, Mix, Multipoint, Linear]:
    return (linear
            if discrete is context.empty
            else (discrete
                  if linear is context.empty
                  else context.mix_cls(discrete, linear, context.empty)))
Пример #9
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)
Пример #10
0
def relate_multipolygon(goal: Multipolygon, test: Multipolygon,
                        context: Context) -> Relation:
    goal_bounding_box = context.polygons_box(goal.polygons)
    test_bounding_box = context.polygons_box(test.polygons)
    events_queue = CompoundEventsQueue(context)
    events_queue.register(to_oriented_segments(goal, context), from_test=False)
    events_queue.register(to_oriented_segments(test, context), from_test=True)
    return process_compound_queue(
        events_queue, min(goal_bounding_box.max_x, test_bounding_box.max_x))
Пример #11
0
def test_step(context: Context, segments: List[Segment]) -> None:
    first_segment, *rest_segments = segments

    result = segments_cross_or_overlap(rest_segments)
    next_result = segments_cross_or_overlap(segments)

    assert (next_result is (result or any(
        context.segments_relation(first_segment, segment) in
        (Relation.COMPONENT, Relation.COMPOSITE, Relation.CROSS,
         Relation.EQUAL, Relation.OVERLAP) for segment in rest_segments)))
Пример #12
0
def _locate_point_in_indexed_polygons(tree: r.Tree,
                                      polygons: Sequence[Polygon],
                                      point: Point,
                                      context: Context) -> Location:
    candidates_indices = tree.find_supersets_indices(
        context.box_cls(point.x, point.x, point.y, point.y))
    for candidate_index in candidates_indices:
        location = polygons[candidate_index].locate(point)
        if location is not Location.EXTERIOR:
            return location
    return Location.EXTERIOR
Пример #13
0
def relate_multisegment(goal: Multisegment, test: Multisegment,
                        context: Context) -> Relation:
    goal_bounding_box = context.segments_box(goal.segments)
    test_bounding_box = context.segments_box(test.segments)
    if box.disjoint_with(goal_bounding_box, test_bounding_box):
        return Relation.DISJOINT
    events_queue = LinearEventsQueue(context)
    events_queue.register(to_segments_endpoints(goal), from_test=False)
    events_queue.register(to_segments_endpoints(test), from_test=True)
    return process_open_linear_queue(
        events_queue, min(goal_bounding_box.max_x, test_bounding_box.max_x))
Пример #14
0
def relate_contour(multipolygon: Multipolygon, contour: Contour,
                   context: Context) -> Relation:
    contour_bounding_box = context.contour_box(contour)
    disjoint, multipolygon_max_x, events_queue = True, None, None
    for polygon in multipolygon.polygons:
        polygon_bounding_box = context.polygon_box(polygon)
        if not box.disjoint_with(polygon_bounding_box, contour_bounding_box):
            if disjoint:
                disjoint = False
                multipolygon_max_x = polygon_bounding_box.max_x
                events_queue = CompoundEventsQueue(context)
                events_queue.register(contour_to_edges_endpoints(contour),
                                      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)
    return (Relation.DISJOINT if disjoint else process_linear_compound_queue(
        events_queue, min(contour_bounding_box.max_x, multipolygon_max_x)))
Пример #15
0
def symmetric_subtract_segments(first: Segment,
                                second: Segment,
                                context: Context
                                ) -> Union_[Empty, Multisegment, Segment]:
    relation = context.segments_relation(first, second)
    if relation is Relation.DISJOINT:
        return context.multisegment_cls([first, second])
    elif relation is Relation.EQUAL:
        return context.empty
    else:
        return (_unite_segments_touch(first, second, context)
                if relation is Relation.TOUCH
                else
                (_unite_segments_cross(first, second, context)
                 if relation is Relation.CROSS
                 else
                 (_symmetric_subtract_segments_overlap(first, second, context)
                  if relation is Relation.OVERLAP
                  else (_subtract_segments_composite(first, second, context)
                        if relation is Relation.COMPOSITE
                        else _subtract_segments_composite(second, first,
                                                          context)))))
Пример #16
0
def relate_multiregion(goal: Multiregion, test: Multiregion,
                       context: Context) -> Relation:
    goal_bounding_box = context.contours_box(goal)
    test_bounding_box = context.contours_box(test)
    if box.disjoint_with(goal_bounding_box, test_bounding_box):
        return Relation.DISJOINT
    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_compound_queue(
        events_queue, min(goal_bounding_box.max_x, test_bounding_box.max_x))
Пример #17
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
def test_step(context: Context, contour: Contour) -> None:
    first_vertex, *rest_vertices = contour.vertices
    rest_contour = Contour(rest_vertices)

    result = edges_intersect(rest_contour)
    next_result = edges_intersect(contour)

    first_edge, last_edge = (Segment(first_vertex, rest_vertices[0]),
                             Segment(rest_vertices[-1], first_vertex))
    rest_edges = contour_to_edges(rest_contour)
    overlap_relations = (Relation.COMPONENT, Relation.COMPOSITE,
                         Relation.EQUAL, Relation.OVERLAP)
    assert (next_result is (result and len(rest_vertices) > 2 and (any(
        segments_pair_intersections(
            rest_edges[index].start, rest_edges[index].end,
            rest_edges[other_index].start, rest_edges[other_index].end)
        for index in range(len(rest_edges) - 1)
        for other_index in chain(range(index -
                                       1), range(index + 2,
                                                 len(rest_edges) - 1))
    ) or any(
        segments_pair_intersections(edge.start, edge.end, other_edge.start,
                                    other_edge.end) in overlap_relations
        for edge, other_edge in combinations(rest_edges[:-1], 2))) or any(
            segments_pair_intersections(first_edge.start, first_edge.end,
                                        edge.start, edge.end)
            for edge in rest_edges[1:-1]) or any(
                segments_pair_intersections(last_edge.start, last_edge.end,
                                            edge.start, edge.end)
                for edge in rest_edges[:-2]) or len(rest_vertices) > 1 and (
                    context.segments_relation(
                        first_edge.start, first_edge.end, rest_edges[0].start,
                        rest_edges[0].end) in overlap_relations or
                    context.segments_relation(first_edge.start, first_edge.end,
                                              last_edge.start, last_edge.end)
                    in overlap_relations or context.segments_relation(
                        last_edge.start, last_edge.end, rest_edges[0].start,
                        rest_edges[0].end) in overlap_relations)))
Пример #19
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()))
Пример #20
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))
Пример #21
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))
Пример #22
0
def subtract_segments(minuend: Segment,
                      subtrahend: Segment,
                      context: Context
                      ) -> Union_[Empty, Multisegment, Segment]:
    relation = context.segments_relation(minuend, subtrahend)
    if relation is Relation.COMPONENT or relation is Relation.EQUAL:
        return context.empty
    elif (relation is Relation.DISJOINT
          or relation is Relation.TOUCH
          or relation is Relation.CROSS):
        return minuend
    else:
        return (_subtract_segments_overlap(minuend, subtrahend, context)
                if relation is Relation.OVERLAP
                else _subtract_segments_composite(minuend, subtrahend,
                                                  context))
Пример #23
0
def test_sizes(
        context: Context,
        polygon_with_extra_points: Tuple[Polygon, Sequence[Point]]) -> None:
    polygon, extra_points = polygon_with_extra_points

    result = Triangulation.constrained_delaunay(polygon,
                                                extra_points=extra_points,
                                                context=context)

    triangles = result.triangles()
    assert 0 < len(triangles) <= (2 * (len(
        to_distinct(
            chain(polygon.border.vertices, *
                  [hole.vertices
                   for hole in polygon.holes], extra_points))) - 1) - len(
                       context.points_convex_hull(polygon.border.vertices)))
    assert all(is_contour_triangular(triangle) for triangle in triangles)
Пример #24
0
def _intersect_segment_with_multisegment(segment: Segment,
                                         multisegment: Multisegment,
                                         context: Context) -> List[Segment]:
    result = []
    for sub_segment in multisegment.segments:
        relation = context.segments_relation(segment, sub_segment)
        if (relation is not Relation.DISJOINT
                and relation is not Relation.TOUCH
                and relation is not Relation.CROSS):
            result.append(segment
                          if (relation is Relation.EQUAL
                              or relation is Relation.COMPONENT)
                          else (sub_segment
                                if relation is Relation.COMPOSITE
                                else _intersect_segments_overlap(segment,
                                                                 sub_segment,
                                                                 context)))
    return result
Пример #25
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)))
Пример #26
0
def from_points(points: Iterable[Point],
                *,
                context: Context) -> Box:
    """
    Builds box from points.
    """
    points = iter(points)
    point = next(points)
    min_x, min_y = max_x, max_y = point.x, point.y
    for point in points:
        x, y = point.x, point.y
        if x < min_x:
            min_x = x
        elif x > max_x:
            max_x = x
        if y < min_y:
            min_y = y
        elif y > max_y:
            max_y = y
    return context.box_cls(min_x, max_x, min_y, max_y)
Пример #27
0
def _relate_multisegment(multiregion: Multiregion, multisegment: Multisegment,
                         multisegment_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,
                                 multisegment_bounding_box):
            if disjoint:
                disjoint = False
                multiregion_max_x = region_bounding_box.max_x
                events_queue = CompoundEventsQueue(context)
                events_queue.register(to_segments_endpoints(multisegment),
                                      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(multisegment_bounding_box.max_x, multiregion_max_x)))
Пример #28
0
def _subtract_segments_composite(minuend: Segment,
                                 subtrahend: Segment,
                                 context: Context
                                 ) -> Union_[Multisegment, Segment]:
    left_start, left_end, right_start, right_end = sorted([
        minuend.start, minuend.end, subtrahend.start, subtrahend.end])
    return (context.segment_cls(right_start, right_end)
            if left_start == subtrahend.start or left_start == subtrahend.end
            else (((context.segment_cls(left_start, left_end)
                    if right_start == right_end
                    else
                    context.multisegment_cls([context.segment_cls(left_start,
                                                                  left_end),
                                              context.segment_cls(right_start,
                                                                  right_end)]))
                   if (right_start == subtrahend.start
                       or right_start == subtrahend.end)
                   else context.segment_cls(left_start, left_end))
                  if left_end == subtrahend.start or left_end == subtrahend.end
                  else context.segment_cls(left_start, right_start)))
Пример #29
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)))
Пример #30
0
def relate_segment(goal: Segment, test: Segment, context: Context) -> Relation:
    return context.segments_relation(test, goal)