matplotlib.PathPatch 的最小覆盖圆

问题描述 投票:0回答:1

对于任意的 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()
python matplotlib bezier
1个回答
0
投票

我建议使用路径而不是艺术家。这样你就可以使用 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()

输出:

© www.soinside.com 2019 - 2024. All rights reserved.