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) ])
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 ])
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)