对于任意的 matplotlib.PathPatch 实例和给定的原点,我想找到完全覆盖补丁的最小半径的圆。 对于直截了当的直线路径:只需计算路径顶点与原点之间的距离;最大值是所需的半径。 然而,对于带有贝塞尔曲线的路径,路径顶点是可以位于补丁之外的控制点,因此圆最终会变得太大。在这些情况下如何找到最小半径?
#!/usr/bin/env python
"""
Find the circle with minimum radius that given an xy origin fully covers a matplotlib.patch.PathPatch instance.
"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch
fig, ax = plt.subplots()
# arbitrary PathPatch
vertices = np.array([[ 0.44833333, -2.75444444],
[-0.78166667, -1.28444444],
[-2.88166667, 1.81555556],
[-0.75666667, 1.81555556],
[-0.28166667, 0.96555556],
[ 1.06833333, 3.01555556],
[ 1.86833333, -0.13444444],
[ 0.86833333, -0.68444444],
[ 0.44833333, -2.75444444]])
codes = (1, 4, 4, 4, 2, 4, 4, 4, 79)
ax.add_artist(PathPatch(Path(vertices, codes), color='red'))
# plot control points separately
ax.scatter(*vertices.T, c='black', marker='x')
# covering circle
origin = np.array((0, 0))
deltas = vertices - origin[np.newaxis, :]
distances = np.linalg.norm(deltas, axis=-1)
radius = np.max(distances)
ax.add_artist(plt.Circle(origin, radius, alpha=0.1))
ax.axis([-4, 4, -4, 4])
ax.set_aspect("equal")
plt.show()
我建议使用路径而不是艺术家。这样你就可以使用 contains_path 来检查你的圈子是否包含你的初始补丁。
此外,您可以使用 scipy.optimize.fmin 找到最小值:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch, Circle
from scipy.optimize import fmin
fig, ax = plt.subplots()
# arbitrary PathPatch
vertices = np.array([[ 0.44833333, -2.75444444],
[-0.78166667, -1.28444444],
[-2.88166667, 1.81555556],
[-0.75666667, 1.81555556],
[-0.28166667, 0.96555556],
[ 1.06833333, 3.01555556],
[ 1.86833333, -0.13444444],
[ 0.86833333, -0.68444444],
[ 0.44833333, -2.75444444]])
codes = (1, 4, 4, 4, 2, 4, 4, 4, 79)
custom_path = Path(vertices, codes) # the path that is supposed to be inside the circle path
ax.add_patch(PathPatch(custom_path, color='red'))
# plot control points separately
ax.scatter(*vertices.T, c='black', marker='x')
# covering circle (init)
origin = np.array((0, 0))
deltas = vertices - origin[np.newaxis, :]
distances = np.linalg.norm(deltas, axis=-1)
radius = np.max(distances)
def patch_in_circle(r):
circle_path = Path.circle(origin, radius=r)
if circle_path.contains_path(custom_path):
return r
else:
return radius
r = fmin(patch_in_circle, radius)
ax.add_patch( Circle(origin, radius=r, alpha=0.1))
ax.axis([-4, 4, -4, 4])
ax.set_aspect("equal")
plt.show()
输出: