|
import numpy as np |
|
from scipy._lib.decorator import decorator as _decorator |
|
|
|
__all__ = ['delaunay_plot_2d', 'convex_hull_plot_2d', 'voronoi_plot_2d'] |
|
|
|
|
|
@_decorator |
|
def _held_figure(func, obj, ax=None, **kw): |
|
import matplotlib.pyplot as plt |
|
|
|
if ax is None: |
|
fig = plt.figure() |
|
ax = fig.gca() |
|
return func(obj, ax=ax, **kw) |
|
|
|
|
|
|
|
was_held = getattr(ax, 'ishold', lambda: True)() |
|
if was_held: |
|
return func(obj, ax=ax, **kw) |
|
try: |
|
ax.hold(True) |
|
return func(obj, ax=ax, **kw) |
|
finally: |
|
ax.hold(was_held) |
|
|
|
|
|
def _adjust_bounds(ax, points): |
|
margin = 0.1 * np.ptp(points, axis=0) |
|
xy_min = points.min(axis=0) - margin |
|
xy_max = points.max(axis=0) + margin |
|
ax.set_xlim(xy_min[0], xy_max[0]) |
|
ax.set_ylim(xy_min[1], xy_max[1]) |
|
|
|
|
|
@_held_figure |
|
def delaunay_plot_2d(tri, ax=None): |
|
""" |
|
Plot the given Delaunay triangulation in 2-D |
|
|
|
Parameters |
|
---------- |
|
tri : scipy.spatial.Delaunay instance |
|
Triangulation to plot |
|
ax : matplotlib.axes.Axes instance, optional |
|
Axes to plot on |
|
|
|
Returns |
|
------- |
|
fig : matplotlib.figure.Figure instance |
|
Figure for the plot |
|
|
|
See Also |
|
-------- |
|
Delaunay |
|
matplotlib.pyplot.triplot |
|
|
|
Notes |
|
----- |
|
Requires Matplotlib. |
|
|
|
Examples |
|
-------- |
|
|
|
>>> import numpy as np |
|
>>> import matplotlib.pyplot as plt |
|
>>> from scipy.spatial import Delaunay, delaunay_plot_2d |
|
|
|
The Delaunay triangulation of a set of random points: |
|
|
|
>>> rng = np.random.default_rng() |
|
>>> points = rng.random((30, 2)) |
|
>>> tri = Delaunay(points) |
|
|
|
Plot it: |
|
|
|
>>> _ = delaunay_plot_2d(tri) |
|
>>> plt.show() |
|
|
|
""" |
|
if tri.points.shape[1] != 2: |
|
raise ValueError("Delaunay triangulation is not 2-D") |
|
|
|
x, y = tri.points.T |
|
ax.plot(x, y, 'o') |
|
ax.triplot(x, y, tri.simplices.copy()) |
|
|
|
_adjust_bounds(ax, tri.points) |
|
|
|
return ax.figure |
|
|
|
|
|
@_held_figure |
|
def convex_hull_plot_2d(hull, ax=None): |
|
""" |
|
Plot the given convex hull diagram in 2-D |
|
|
|
Parameters |
|
---------- |
|
hull : scipy.spatial.ConvexHull instance |
|
Convex hull to plot |
|
ax : matplotlib.axes.Axes instance, optional |
|
Axes to plot on |
|
|
|
Returns |
|
------- |
|
fig : matplotlib.figure.Figure instance |
|
Figure for the plot |
|
|
|
See Also |
|
-------- |
|
ConvexHull |
|
|
|
Notes |
|
----- |
|
Requires Matplotlib. |
|
|
|
|
|
Examples |
|
-------- |
|
|
|
>>> import numpy as np |
|
>>> import matplotlib.pyplot as plt |
|
>>> from scipy.spatial import ConvexHull, convex_hull_plot_2d |
|
|
|
The convex hull of a random set of points: |
|
|
|
>>> rng = np.random.default_rng() |
|
>>> points = rng.random((30, 2)) |
|
>>> hull = ConvexHull(points) |
|
|
|
Plot it: |
|
|
|
>>> _ = convex_hull_plot_2d(hull) |
|
>>> plt.show() |
|
|
|
""" |
|
from matplotlib.collections import LineCollection |
|
|
|
if hull.points.shape[1] != 2: |
|
raise ValueError("Convex hull is not 2-D") |
|
|
|
ax.plot(hull.points[:, 0], hull.points[:, 1], 'o') |
|
line_segments = [hull.points[simplex] for simplex in hull.simplices] |
|
ax.add_collection(LineCollection(line_segments, |
|
colors='k', |
|
linestyle='solid')) |
|
_adjust_bounds(ax, hull.points) |
|
|
|
return ax.figure |
|
|
|
|
|
@_held_figure |
|
def voronoi_plot_2d(vor, ax=None, **kw): |
|
""" |
|
Plot the given Voronoi diagram in 2-D |
|
|
|
Parameters |
|
---------- |
|
vor : scipy.spatial.Voronoi instance |
|
Diagram to plot |
|
ax : matplotlib.axes.Axes instance, optional |
|
Axes to plot on |
|
show_points : bool, optional |
|
Add the Voronoi points to the plot. |
|
show_vertices : bool, optional |
|
Add the Voronoi vertices to the plot. |
|
line_colors : string, optional |
|
Specifies the line color for polygon boundaries |
|
line_width : float, optional |
|
Specifies the line width for polygon boundaries |
|
line_alpha : float, optional |
|
Specifies the line alpha for polygon boundaries |
|
point_size : float, optional |
|
Specifies the size of points |
|
|
|
Returns |
|
------- |
|
fig : matplotlib.figure.Figure instance |
|
Figure for the plot |
|
|
|
See Also |
|
-------- |
|
Voronoi |
|
|
|
Notes |
|
----- |
|
Requires Matplotlib. For degenerate input, including collinearity and |
|
other violations of general position, it may be preferable to |
|
calculate the Voronoi diagram with Qhull options ``QJ`` for random |
|
joggling, or ``Qt`` to enforce triangulated output. Otherwise, some |
|
Voronoi regions may not be visible. |
|
|
|
Examples |
|
-------- |
|
>>> import numpy as np |
|
>>> import matplotlib.pyplot as plt |
|
>>> from scipy.spatial import Voronoi, voronoi_plot_2d |
|
|
|
Create a set of points for the example: |
|
|
|
>>> rng = np.random.default_rng() |
|
>>> points = rng.random((10,2)) |
|
|
|
Generate the Voronoi diagram for the points: |
|
|
|
>>> vor = Voronoi(points) |
|
|
|
Use `voronoi_plot_2d` to plot the diagram: |
|
|
|
>>> fig = voronoi_plot_2d(vor) |
|
|
|
Use `voronoi_plot_2d` to plot the diagram again, with some settings |
|
customized: |
|
|
|
>>> fig = voronoi_plot_2d(vor, show_vertices=False, line_colors='orange', |
|
... line_width=2, line_alpha=0.6, point_size=2) |
|
>>> plt.show() |
|
|
|
""" |
|
from matplotlib.collections import LineCollection |
|
|
|
if vor.points.shape[1] != 2: |
|
raise ValueError("Voronoi diagram is not 2-D") |
|
|
|
if kw.get('show_points', True): |
|
point_size = kw.get('point_size', None) |
|
ax.plot(vor.points[:, 0], vor.points[:, 1], '.', markersize=point_size) |
|
if kw.get('show_vertices', True): |
|
ax.plot(vor.vertices[:, 0], vor.vertices[:, 1], 'o') |
|
|
|
line_colors = kw.get('line_colors', 'k') |
|
line_width = kw.get('line_width', 1.0) |
|
line_alpha = kw.get('line_alpha', 1.0) |
|
|
|
center = vor.points.mean(axis=0) |
|
ptp_bound = np.ptp(vor.points, axis=0) |
|
|
|
finite_segments = [] |
|
infinite_segments = [] |
|
for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices): |
|
simplex = np.asarray(simplex) |
|
if np.all(simplex >= 0): |
|
finite_segments.append(vor.vertices[simplex]) |
|
else: |
|
i = simplex[simplex >= 0][0] |
|
|
|
t = vor.points[pointidx[1]] - vor.points[pointidx[0]] |
|
t /= np.linalg.norm(t) |
|
n = np.array([-t[1], t[0]]) |
|
|
|
midpoint = vor.points[pointidx].mean(axis=0) |
|
direction = np.sign(np.dot(midpoint - center, n)) * n |
|
if (vor.furthest_site): |
|
direction = -direction |
|
aspect_factor = abs(ptp_bound.max() / ptp_bound.min()) |
|
far_point = vor.vertices[i] + direction * ptp_bound.max() * aspect_factor |
|
|
|
infinite_segments.append([vor.vertices[i], far_point]) |
|
|
|
ax.add_collection(LineCollection(finite_segments, |
|
colors=line_colors, |
|
lw=line_width, |
|
alpha=line_alpha, |
|
linestyle='solid')) |
|
ax.add_collection(LineCollection(infinite_segments, |
|
colors=line_colors, |
|
lw=line_width, |
|
alpha=line_alpha, |
|
linestyle='dashed')) |
|
|
|
_adjust_bounds(ax, vor.points) |
|
|
|
return ax.figure |
|
|