I'm in google colab, improve the following python code according to my instructions.
Let P_no_adj be the set of points whose regions are not adjacent to the red point.
You need to calculate the area of the convex envelope of all points in P_adj. This convex envelope normally contains the red point.
From what I can see, this envelope is expressed in terms of area, but it can probably be expressed in other ways.
I'd like you to generate two plots. The first is the same as at present. For the second, you need to take the coordinates of the central point (red) and the coordinates of adjacent points (blue), but randomly generate the coordinates of non-adjacent points (black) again, P_no_adj. Please note that the new black point coordinates must not lie within the convex envelope calculated in the previous step. And the new black point coordinates in P_no_adj must lie within the same plot limits. The number of black dots between the two output figures must also be identical. The color of the points in both plots is also always triple: red, blue and black.
If you wish, you can add a function that adds a minimum distance threshold between the newly generated black points and the blue points, to avoid cases where the former are really extremely close to the latter.
Python code to improve : `import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import Voronoi


# Generate the points
num_points = 21  # Number of points around the central point
R = 10  # Maximum radius for generating points around the center


# Generate random angles and radii
np.random.seed(0)  # For reproducibility
theta = np.random.uniform(0, 2 * np.pi, num_points)
r = R * np.sqrt(np.random.uniform(0, 1, num_points))  # Uniform distribution within circle


# Convert polar coordinates to Cartesian coordinates
x = r * np.cos(theta)
y = r * np.sin(theta)
other_points = np.column_stack((x, y))


# Define the central point at (0, 0)
central_point = np.array([[0, 0]])


# Combine central point with other points
points = np.vstack((central_point, other_points))


# Create the Voronoi diagram
vor = Voronoi(points)


# Find indices of points whose regions are adjacent to the central point
ridge_points = vor.ridge_points  # Indices of points between which each Voronoi ridge lies


# Initialize a list to store indices of adjacent points
Adj_indices = []
for pair in ridge_points:
    if 0 in pair:  # If the central point (index 0) is part of the ridge
        neighbor = pair[0] if pair[1] == 0 else pair[1]
        Adj_indices.append(neighbor)


# Remove duplicates and the central point itself if included
Adj_indices = np.unique(Adj_indices)
Adj_indices = Adj_indices[Adj_indices != 0]  # Exclude central point


# Create the set Adj_P containing only points adjacent to the central point
Adj_P = points[Adj_indices.astype(int)]


# Plot the Voronoi diagram including infinite edges
fig, ax = plt.subplots(figsize=(8, 8))


# Plot all points
ax.plot(points[:, 0], points[:, 1], 'ko', markersize=4, label='Other Points')


# Highlight the central point in red
ax.plot(points[0, 0], points[0, 1], 'ro', markersize=8, label='Central Point')


# Highlight adjacent points in blue
ax.plot(points[Adj_indices.astype(int), 0], points[Adj_indices.astype(int), 1],
        'bo', markersize=6, label='Adjacent Points')


# Set equal aspect ratio
ax.set_aspect('equal')


# Set plot limits to include all points with a margin
all_x = points[:, 0]
all_y = points[:, 1]
margin = R * 0.1  # 10% of R as margin
x_min = all_x.min() - margin
x_max = all_x.max() + margin
y_min = all_y.min() - margin
y_max = all_y.max() + margin
ax.set_xlim(x_min, x_max)
ax.set_ylim(y_min, y_max)


# Plot finite ridges (edges)
for simplex in vor.ridge_vertices:
    simplex = np.asarray(simplex)
    if np.all(simplex >= 0):  # Finite ridge (no vertex at infinity)
        ax.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-', lw=0.8)


# Plot infinite ridges (edges) as solid lines
ptp_bound = points.ptp(axis=0)  # Peak-to-peak (max - min) along each axis
max_bound = ptp_bound.max()


for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
    simplex = np.asarray(simplex)
    if np.any(simplex < 0):  # Infinite ridge
        # Indices of the two points defining the ridge
        p1_idx, p2_idx = pointidx
        p1 = vor.points[p1_idx]
        p2 = vor.points[p2_idx]


        # Compute the direction vector of the edge (perpendicular to the line between p1 and p2)
        t = p2 - p1
        t = t / np.linalg.norm(t)
        n = np.array([-t[1], t[0]])  # Normal vector


        # Finite Voronoi vertex
        finite_vertex_idx = simplex[simplex >= 0][0]
        finite_vertex = vor.vertices[finite_vertex_idx]


        # Determine the direction to extend the infinite edge
        midpoint = (p1 + p2) / 2
        direction = np.sign(np.dot(midpoint - points.mean(axis=0), n)) * n


        # Extend the edge far beyond the plot boundaries
        far_point = finite_vertex + direction * max_bound * 10


        # Plot the infinite edge as a solid line between the finite vertex and far point
        ax.plot([finite_vertex[0], far_point[0]], [finite_vertex[1], far_point[1]], 'k-', lw=0.8)


# Add labels and title
ax.set_xlabel('X-coordinate')
ax.set_ylabel('Y-coordinate')
ax.set_title('Voronoi Diagram with Infinite Edges as Solid Lines')


# Display the legend
ax.legend()
plt.show()`