FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

[ENH]: FancyArrow in 3D · Issue #22571 · matplotlib/matplotlib · GitHub

Repository navigation

[ENH]: FancyArrow in 3D #22571

Description

Problem

Hello,

FancyArrow can not be used in 3D.
Some proposals (#21688) consists in creating a new class.
Could it be possible:

  1. to extend FancyArrow to 3D (as for quiver)
  2. to solve -if possible- the arrow head problem representation when loocking the arrow along its axis (see the 2nd plot in the figure below)

Thanks,
Patrick

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


class Arrow3D(FancyArrowPatch):
    """ Create fancy arrow (artist) for 3d matplotlib visualisation """

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

    def draw(self, renderer):
        xs3d, ys3d, zs3d = self._verts3d
        xarr, yarr, _ = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
        self.set_positions((xarr[0], yarr[0]), (xarr[1], yarr[1]))
        FancyArrowPatch.draw(self, renderer)


fig, ax = plt.subplots(1, 2, figsize=(12, 5),
                       subplot_kw=dict(projection="3d",
                                       proj_type='ortho'))

ax[0].voxels(np.ones((1, 1, 1)), facecolors=[0, 0, 0, 0], edgecolors='k')
# ax[0].add_artist(FancyArrow(0, 0, 0, 1, 1, 1))
ax[0].add_artist(Arrow3D((0, 1), (0, 1), (0, 1), mutation_scale=50))

ax[1].voxels(np.ones((1, 1, 1)), facecolors=[0, 0, 0, 0], edgecolors='k')
ax[1].view_init(elev=28, azim=45)
# ax[1].add_artist(FancyArrow(0, 0, 0, 1, 1, 1)))
ax[1].add_artist(Arrow3D((0, 1), (0, 1), (0, 1), mutation_scale=50))

plt.show()

Proposed solution

No response

Activity

  1. soma2000-lang commented on Mar 1, 2022

    @patquem Can u give some insight s on how to solve this issue

  2. patquem commented on Mar 1, 2022

    ContributorAuthor

    Adding Arrow3D class in mplot3d, this should be easy :) .
    Solving the arrow head problem during visualization, I have no idea how to fix that. Sorry :( .

  3. patquem commented on Mar 7, 2022

    ContributorAuthor

    Hello @soma2000-lang
    introducing the penultimate line in the code (if not....) avoids a warning message and drawing the wrong arrowhead as mentioned above. Not sure if this is the right way to fix the bug, but temporarily it might be a solution.
    Patrick

    class Arrow3D(FancyArrowPatch):
        """ Create fancy arrow (artist) for 3d matplotlib visualisation """
    
        def __init__(self, xs, ys, zs, *args, **kwargs):
            FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
            self._verts3d = xs, ys, zs
    
        def draw(self, renderer):
            xs3d, ys3d, zs3d = self._verts3d
            xarr, yarr, _ = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
            self.set_positions((xarr[0], yarr[0]), (xarr[1], yarr[1]))
            if not (np.allclose(xarr, 0.) and np.allclose(yarr, 0.)):
                FancyArrowPatch.draw(self, renderer)
    
  4. AnnaMastori commented on Apr 19, 2022

    Contributor

    Hi, is anyone working on this ? I know you don't assign issues but I would like to try it !

  5. oscargus commented on Apr 19, 2022

    Member

    I believe that maybe @soma2000-lang is not actively working on this, so please give it a go!

  6. added this to the v3.7.0 milestone on Aug 26, 2022
  7. added
    🌱 Good first issueOpen a pull request against these issues if there are no active ones!
    Difficulty: Mediumhttps://matplotlib.org/devdocs/devel/contribute.html#good-first-issues
    on Aug 26, 2022
  8. tacaswell commented on Aug 26, 2022

    Member

    I have labeled this a a good first issue because there is ~working code for the implementation, but medium because it involves (limited) API design.

    This will need:

    • a method added to Axes3D
      • numpydoc style docstring
      • tests
    • get to a consensus on the API (which in addition to the actually time it takes to have the conversation may take very long in calendar time)
    • a whats-new entry
    • an example in the gallery

    This is a good project for someone with library design experience but who is new to Matplotlib.

  9. story645 commented on Dec 20, 2022

    Member

    So I'm just gonna throw out this sketch because the signature is closer to FancyArrowPatch

    class FancyArrowPatch3D(mpatches.FancyArrowPatch):
        def __init__(self, posA, posB, *args, **kwargs):
            mpatches.FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
            self._verts3d = list(zip(posA, posB))
  10. Evidlo commented on Jan 10, 2023

    Here is a function based on the solution from @tacaswell which takes an array of arrow start points and endpoints. Additionally, it sets the axes limits so that all start/end points remain visible.

    def arrows3d(ends, starts=None, ax=None, label=None, **kwargs):
        """3D plot of multiple arrows
    
        Args:
            ends (ndarray): (N, 3) size array of arrow end coordinates
            starts (ndarray): (N, 3) size array of arrow start coordinates.
                Assume start position of (0, 0, 0) if not given
            ax (Axes3DSubplot): existing axes to add to
            label (str): legend label to apply to this group of arrows
            kwargs (dict): additional arrow properties
        """
        if starts is None:
            starts = np.zeros_like(ends)
    
        assert starts.shape == ends.shape, "`starts` and `ends` shape must match"
        assert len(ends.shape) == 2 and ends.shape[1] == 3, \
            "`starts` and `ends` must be shape (N, 3)"
    
        class Arrow3D(FancyArrowPatch):
            def __init__(self, xs, ys, zs, *args, **kwargs):
                super().__init__((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)
    
        # create new axes if none given
        if ax is None:
            ax = plt.figure().add_subplot(111, projection='3d')
    
        arrow_prop_dict = dict(mutation_scale=20, arrowstyle='-|>', color='k', shrinkA=0, shrinkB=0)
        arrow_prop_dict.update(kwargs)
        for ind, (s, e) in enumerate(np.stack((starts, ends), axis=1)):
            a = Arrow3D(
                [s[0], e[0]], [s[1], e[1]], [s[2], e[2]],
                # only give label to first arrow
                label=label if ind == 0 else None,
                **arrow_prop_dict
            )
            ax.add_artist(a)
    
        # store starts/ends on the axes for setting the limits
        ax.points = np.vstack((starts, ends, getattr(ax, 'points', np.empty((0, 3)))))
        ax.set_xlim3d(ax.points[:, 0].min(),ax.points[:, 0].max())
        ax.set_ylim3d(ax.points[:, 1].min(),ax.points[:, 1].max())
        ax.set_zlim3d(ax.points[:, 2].min(),ax.points[:, 2].max())
    
        return ax
    ax = arrows3d(np.random.normal(size=(5, 3)), label='Foo')
    ax = arrows3d(np.random.normal(size=(5, 3)), ax=ax, color='r',label='Bar')
    plt.legend()
    plt.show()

  11. ShubhamPatel-16 commented on Mar 10, 2026

    heyy @patquem looking to the issue , seems like it can be done.
    Let me work on this issue and fix it.

  12. story645 commented on Mar 10, 2026

    Member

    @ShubhamPatel-16 this issue can't be moved forward until decisions are made in #22435 b/c this API should match that one as much as possible.

    Removing the good first issue label for now b/c this isn't ready for work yet.

  13. added and removed
    🌱 Good first issueOpen a pull request against these issues if there are no active ones!
    on Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL