【问题标题】:Plotting part of FancyArrowPatch line in dashed style以虚线样式绘制 FancyArrowPatch 线的一部分
【发布时间】:2020-02-27 16:45:08
【问题描述】:

我正在尝试以虚线样式绘制matplotlib.patches.FancyArrowPatch 的一部分。使用这篇帖子pyplot: Dotted line with FancyArrowPatch,我设法接近它:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

plt.figure()

kw = dict(arrowstyle = '-', shrinkA = 0, shrinkB = 0, color = 'k', connectionstyle = "arc3, rad = -0.9" )
arrow = mpatches.FancyArrowPatch((0, 0), (5, 3), **kw)
plt.gca().add_patch(arrow)
points = np.array([i[0] for i in arrow.get_path().iter_segments(curves = False)])
# arrow.remove()
a, = plt.plot(points[:-3,0], points[:-3,1])
plt.plot(points[-4:,0], points[-4:,1],  linestyle = '--', color = a.get_color())
plt.tight_layout()
plt.show()

据我了解,蓝线与黑线不匹配,因为 iter_segments() 将曲线转换为点密度过低的直线。

我应该怎么做才能得到更好的结果?

【问题讨论】:

  • 链接的答案使用PathPatch,而不是plot。原因是路径的点是贝塞尔曲线的支点。因此曲线本身根本不会碰到它们。
  • 哦,对了。关于如何将贝塞尔曲线正确转换为一系列点的任何提示?这是我必须绘制这条曲线半纯线半虚线的唯一想法。
  • 哦,由于使用了curves = False,这些点至少应该位于曲线上。他们不这样做的事实是由于在显示空间中评估路径,而不是在数据空间中。您可以查看plt.gca().set_aspect("equal")。因此,您是否需要新箭头来遵循确切的后曲线,或者它是否需要是任何曲线。
  • 我想只要它尊重起点和终点,它就可以是任何曲线。此外,我没有设法使用ax.transDataax.transData.transform 使这些点位于曲线上。这里面有些东西我不明白...

标签: python matplotlib annotate


【解决方案1】:

您可以手动评估由箭头生成的贝塞尔曲线。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from scipy.special import binom

fig, ax = plt.subplots()

kw = dict(arrowstyle = '-', shrinkA = 0, shrinkB = 0, color = 'k', 
          connectionstyle = "arc3, rad = -0.9" )
arrow = mpatches.FancyArrowPatch((0, 0), (5, 3), **kw)
ax.add_patch(arrow)


bernstein = lambda n, k, t: binom(n,k)* t**k * (1.-t)**(n-k)

def bezier(points, t=[0,1], num=200):
    N = len(points)
    t = np.linspace(*t, num=num)
    curve = np.zeros((num, 2))
    for i in range(N):
        curve += np.outer(bernstein(N - 1, i, t), points[i])
    return curve

verts = arrow.get_path().vertices
curve1 = bezier(verts, t=[0.0, 0.5], num=100)
curve2 = bezier(verts, t=[0.5, 1.0], num=100)
ax.plot(curve1[:,0], curve1[:,1], lw=3, color="crimson")
ax.plot(curve2[:,0], curve2[:,1], lw=3, ls="--", color="crimson")

plt.show()

您注意到两条曲线,即原始箭头和手动创建的贝塞尔曲线,并没有相互重叠。这是因为 matplotlib 在屏幕空间中评估贝塞尔曲线,而手动版本在数据空间中评估它。
为了在这两种情况下获得相同的曲线,我们需要在屏幕空间中进行评估,如下所示(我们还在数据空间和像素空间中绘制了三个贝塞尔节点)。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from scipy.special import binom

fig, ax = plt.subplots()

kw = dict(arrowstyle = '-', shrinkA = 0, shrinkB = 0, color = 'k', 
          connectionstyle = "arc3, rad = -0.4" )
arrow = mpatches.FancyArrowPatch((0, 0), (5, 3), **kw)
ax.add_patch(arrow)
ax.autoscale()
print(arrow.get_path().vertices)

bernstein = lambda n, k, t: binom(n,k)* t**k * (1.-t)**(n-k)

def bezier(points, t=[0,1], num=200):
    N = len(points)
    t = np.linspace(*t, num=num)
    curve = np.zeros((num, 2))
    for i in range(N):
        curve += np.outer(bernstein(N - 1, i, t), points[i])
    return curve

trans = ax.transData
trans_inv = trans.inverted()
verts = trans.transform(arrow.get_path().vertices)
curve1 = trans_inv.transform(bezier(verts, t=[0.0, 0.5], num=100))
curve2 = trans_inv.transform(bezier(verts, t=[0.5, 1.0], num=100))

ax.plot(curve1[:,0], curve1[:,1], lw=3, color="crimson", zorder=0)
ax.plot(curve2[:,0], curve2[:,1], lw=3, ls="--", color="crimson", zorder=0)

from matplotlib.transforms import IdentityTransform
ax.plot(*trans.transform(arrow.get_path().vertices).T, ls="", marker="o", 
        color="C1", ms=7, transform=IdentityTransform())
ax.plot(*arrow.get_path().vertices.T, ls="", marker="o", color="C0", ms=3)

plt.show()

【讨论】:

  • 谢谢!您能否详细说明为什么两条曲线不完全相同?根据您之前的评论,我认为在顶点上使用 inv = ax.transData.inverted() 就足够了?
  • 如果使用相等的比例(即plt.gca().set_aspect("equal")),它们将是相同的。正如所评论的,不同之处在于,贝塞尔曲线是由 matplotlib 在屏幕坐标中评估的,而手动解决方案是在数据坐标中评估它。
  • plt.show() 之前添加plt.gca().set_aspect("equal") 到您的答案和我的代码都不会使两条曲线相同......这里有些东西我不明白
  • 是的,抱歉,这实际上需要更多:具有相同纵横比的方轴:fig = plt.figure(figsize=(6,6)); fig.subplots_adjust(.1,.1,.9,.9); plt.gca().set_aspect("equal")
  • 我用代码更新了答案,希望能更清楚。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多