_plotutils.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import numpy as np
  2. __all__ = ['delaunay_plot_2d', 'convex_hull_plot_2d', 'voronoi_plot_2d']
  3. def _get_axes():
  4. import matplotlib.pyplot as plt
  5. return plt.figure().gca()
  6. def _adjust_bounds(ax, points):
  7. margin = 0.1 * np.ptp(points, axis=0)
  8. xy_min = points.min(axis=0) - margin
  9. xy_max = points.max(axis=0) + margin
  10. ax.set_xlim(xy_min[0], xy_max[0])
  11. ax.set_ylim(xy_min[1], xy_max[1])
  12. def delaunay_plot_2d(tri, ax=None):
  13. """
  14. Plot the given Delaunay triangulation in 2-D
  15. Parameters
  16. ----------
  17. tri : scipy.spatial.Delaunay instance
  18. Triangulation to plot
  19. ax : matplotlib.axes.Axes instance, optional
  20. Axes to plot on
  21. Returns
  22. -------
  23. fig : matplotlib.figure.Figure instance
  24. Figure for the plot
  25. See Also
  26. --------
  27. Delaunay
  28. matplotlib.pyplot.triplot
  29. Notes
  30. -----
  31. Requires Matplotlib.
  32. Examples
  33. --------
  34. >>> import numpy as np
  35. >>> import matplotlib.pyplot as plt
  36. >>> from scipy.spatial import Delaunay, delaunay_plot_2d
  37. The Delaunay triangulation of a set of random points:
  38. >>> rng = np.random.default_rng()
  39. >>> points = rng.random((30, 2))
  40. >>> tri = Delaunay(points)
  41. Plot it:
  42. >>> _ = delaunay_plot_2d(tri)
  43. >>> plt.show()
  44. """
  45. if tri.points.shape[1] != 2:
  46. raise ValueError("Delaunay triangulation is not 2-D")
  47. x, y = tri.points.T
  48. ax = ax or _get_axes()
  49. ax.plot(x, y, 'o')
  50. ax.triplot(x, y, tri.simplices.copy())
  51. _adjust_bounds(ax, tri.points)
  52. return ax.figure
  53. def convex_hull_plot_2d(hull, ax=None):
  54. """
  55. Plot the given convex hull diagram in 2-D
  56. Parameters
  57. ----------
  58. hull : scipy.spatial.ConvexHull instance
  59. Convex hull to plot
  60. ax : matplotlib.axes.Axes instance, optional
  61. Axes to plot on
  62. Returns
  63. -------
  64. fig : matplotlib.figure.Figure instance
  65. Figure for the plot
  66. See Also
  67. --------
  68. ConvexHull
  69. Notes
  70. -----
  71. Requires Matplotlib.
  72. Examples
  73. --------
  74. >>> import numpy as np
  75. >>> import matplotlib.pyplot as plt
  76. >>> from scipy.spatial import ConvexHull, convex_hull_plot_2d
  77. The convex hull of a random set of points:
  78. >>> rng = np.random.default_rng()
  79. >>> points = rng.random((30, 2))
  80. >>> hull = ConvexHull(points)
  81. Plot it:
  82. >>> _ = convex_hull_plot_2d(hull)
  83. >>> plt.show()
  84. """
  85. from matplotlib.collections import LineCollection
  86. if hull.points.shape[1] != 2:
  87. raise ValueError("Convex hull is not 2-D")
  88. ax = ax or _get_axes()
  89. ax.plot(hull.points[:, 0], hull.points[:, 1], 'o')
  90. line_segments = [hull.points[simplex] for simplex in hull.simplices]
  91. ax.add_collection(LineCollection(line_segments,
  92. colors='k',
  93. linestyle='solid'))
  94. _adjust_bounds(ax, hull.points)
  95. return ax.figure
  96. def voronoi_plot_2d(vor, ax=None, **kw):
  97. """
  98. Plot the given Voronoi diagram in 2-D
  99. Parameters
  100. ----------
  101. vor : scipy.spatial.Voronoi instance
  102. Diagram to plot
  103. ax : matplotlib.axes.Axes instance, optional
  104. Axes to plot on
  105. show_points : bool, optional
  106. Add the Voronoi points to the plot.
  107. show_vertices : bool, optional
  108. Add the Voronoi vertices to the plot.
  109. line_colors : string, optional
  110. Specifies the line color for polygon boundaries
  111. line_width : float, optional
  112. Specifies the line width for polygon boundaries
  113. line_alpha : float, optional
  114. Specifies the line alpha for polygon boundaries
  115. point_size : float, optional
  116. Specifies the size of points
  117. Returns
  118. -------
  119. fig : matplotlib.figure.Figure instance
  120. Figure for the plot
  121. See Also
  122. --------
  123. Voronoi
  124. Notes
  125. -----
  126. Requires Matplotlib. For degenerate input, including collinearity and
  127. other violations of general position, it may be preferable to
  128. calculate the Voronoi diagram with Qhull options ``QJ`` for random
  129. joggling, or ``Qt`` to enforce triangulated output. Otherwise, some
  130. Voronoi regions may not be visible.
  131. Examples
  132. --------
  133. >>> import numpy as np
  134. >>> import matplotlib.pyplot as plt
  135. >>> from scipy.spatial import Voronoi, voronoi_plot_2d
  136. Create a set of points for the example:
  137. >>> rng = np.random.default_rng()
  138. >>> points = rng.random((10,2))
  139. Generate the Voronoi diagram for the points:
  140. >>> vor = Voronoi(points)
  141. Use `voronoi_plot_2d` to plot the diagram:
  142. >>> fig = voronoi_plot_2d(vor)
  143. Use `voronoi_plot_2d` to plot the diagram again, with some settings
  144. customized:
  145. >>> fig = voronoi_plot_2d(vor, show_vertices=False, line_colors='orange',
  146. ... line_width=2, line_alpha=0.6, point_size=2)
  147. >>> plt.show()
  148. """
  149. from matplotlib.collections import LineCollection
  150. if vor.points.shape[1] != 2:
  151. raise ValueError("Voronoi diagram is not 2-D")
  152. ax = ax or _get_axes()
  153. if kw.get('show_points', True):
  154. point_size = kw.get('point_size', None)
  155. ax.plot(vor.points[:, 0], vor.points[:, 1], '.', markersize=point_size)
  156. if kw.get('show_vertices', True):
  157. ax.plot(vor.vertices[:, 0], vor.vertices[:, 1], 'o')
  158. line_colors = kw.get('line_colors', 'k')
  159. line_width = kw.get('line_width', 1.0)
  160. line_alpha = kw.get('line_alpha', 1.0)
  161. center = vor.points.mean(axis=0)
  162. ptp_bound = np.ptp(vor.points, axis=0)
  163. finite_segments = []
  164. infinite_segments = []
  165. for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
  166. simplex = np.asarray(simplex)
  167. if np.all(simplex >= 0):
  168. finite_segments.append(vor.vertices[simplex])
  169. else:
  170. i = simplex[simplex >= 0][0] # finite end Voronoi vertex
  171. t = vor.points[pointidx[1]] - vor.points[pointidx[0]] # tangent
  172. t /= np.linalg.norm(t)
  173. n = np.array([-t[1], t[0]]) # normal
  174. midpoint = vor.points[pointidx].mean(axis=0)
  175. direction = np.sign(np.dot(midpoint - center, n)) * n
  176. if (vor.furthest_site):
  177. direction = -direction
  178. aspect_factor = abs(ptp_bound.max() / ptp_bound.min())
  179. far_point = vor.vertices[i] + direction * ptp_bound.max() * aspect_factor
  180. infinite_segments.append([vor.vertices[i], far_point])
  181. ax.add_collection(LineCollection(finite_segments,
  182. colors=line_colors,
  183. lw=line_width,
  184. alpha=line_alpha,
  185. linestyle='solid'))
  186. ax.add_collection(LineCollection(infinite_segments,
  187. colors=line_colors,
  188. lw=line_width,
  189. alpha=line_alpha,
  190. linestyle='dashed'))
  191. _adjust_bounds(ax, vor.points)
  192. return ax.figure