You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

3D绘图中实现贴合平面、覆盖全角度的弯曲FancyArrowPatch标注

3D绘图中实现平面内弯曲夹角标注箭头的解决方案

我尝试在3D绘图中绘制弯曲的FancyArrowPatch来标注两条直线的夹角,要求箭头满足以下条件:

  • 箭头可弯曲
  • 始终处于两条直线所在的平面内
  • 完整覆盖目标夹角

基于相关示例编写代码后,当前效果存在如下问题:

  • 蓝色箭头仅作参考
  • 红色箭头虽弯曲但不在两线所在平面
  • 绿色箭头在平面内且弯曲,但未接触到任意一条直线

请问如何实现符合要求的箭头标注?

import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.art3d as art3d
import numpy as np
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d


class myArrow3D(FancyArrowPatch):
    def __init__(self, xs, ys, zs, *args, **kwargs):
        FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
        self._verts3d = xs, ys, zs

    def do_3d_projection(self, renderer=None):
        xs3d, ys3d, zs3d = self._verts3d
        xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
        self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
        return np.min(zs)


class otherArrow3D(FancyArrowPatch):
    def __init__(self, xdir, zdir, *args, **kwargs):
        super().__init__(*args, **kwargs)
        transform_2d_to_3d(self, veca=xdir, vecb=zdir)


def rotation_matrix(x, z):
    x = np.array(x) / np.linalg.norm(x)
    y = np.cross(np.array(z), x)
    z = np.cross(x, y)
    return np.array([x, y / np.linalg.norm(y), z / np.linalg.norm(z)]).T


def transform_2d_to_3d(pathpatch, veca=[-1, 0, 0], vecb=[0, 1, 0], zs=0):
    """
    Transforms a 2D Patch to a 3D patch using the given normal vector.

    The patch is projected into they XY plane, rotated about the origin
    and finally translated by z.
    """
    path = pathpatch.get_path()  # Get the path and the associated transform
    trans = pathpatch.get_patch_transform()
    path = trans.transform_path(path)  # Apply the transform
    pathpatch.__class__ = art3d.PathPatch3D  # Change the class
    pathpatch._code3d = path.codes  # Copy the codes
    pathpatch._facecolor3d = pathpatch.get_facecolor  # Get the face color
    verts = path.vertices  # Get the vertices in 2D
    pathpatch._segment3d = np.array(
        [np.dot(rotation_matrix(veca, vecb), (x, y, 0)) + (0, 0, zs) for x, y in verts]
    )


fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.set_xlim([0, 4])
ax.set_ylim([4, 0])
ax.set_zlim([0, 4])
ax.plot([0, 0], [0, 0], [0, 4], "r-")
ax.plot([0, 4], [0, 4], [0, 4], "g-")
ax.add_artist(
    myArrow3D(
        [0, 1],
        [0, 1],
        [1, 1],
        mutation_scale=25,
        lw=0.5,
        arrowstyle="simple",
        color="b",
    )
)
ax.add_artist(
    myArrow3D(
        [0, 2],
        [0, 2],
        [2, 2],
        mutation_scale=25,
        lw=1,
        arrowstyle="simple",
        connectionstyle="arc3, rad=-0.5",
        color="r",
    )
)
ax.add_artist(
    otherArrow3D(
        [0, 0, 1],
        np.cross([0, 0, 1], [1, 1, 1]),
        (3, 0),
        (3, 3),
        lw=1,
        mutation_scale=1,
        arrowstyle="simple",
        connectionstyle="arc3, rad=0.75",
        color="g",
    )
)
ax.set(xlabel="X", ylabel="Y", zlabel="Z")
plt.show()

内容的提问来源于stack exchange,提问作者troy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 06:13:17