-
-
Notifications
You must be signed in to change notification settings - Fork 8k
Open
Labels
@mapfiable
Description
Problem
Following my recent erroneous bug report, I think it would be great to have more customization options for the FancyArrow
class. More specifically, it would be nice to be able to change the shape of the tail via a simple kwarg, similar to capstyle
e.g.
I was also surprised to find that you have much more customization options for the annotation
arrow than for a class called FancyArrow
. It would be great to also have these options for the FancyArrow
class, and e.g. introduce a similar notation like .-|>
to get a round tail.
Proposed solution
As suggested by @jklymak, I tried to use a custom Polygon
to simulate the effect. Here is my attempt of modifying the FandyArrow
class:
class FancierArrow(FancyArrow): """ Like FancyArrow, but lets you choose a round butt. """ def __str__(self): return "FancierArrow()" def __init__( self, x, y, dx, dy, width=0.001, length_includes_head=False, head_width=None, head_length=None, shape='full', overhang=0, head_starts_at_zero=False, round_tail=False, **kwargs ): self._round_tail = round_tail super().__init__( x, y, dx, dy, width=width, length_includes_head=length_includes_head, head_width=head_width, head_length=head_length, shape=shape, overhang=overhang, head_starts_at_zero=head_starts_at_zero, **kwargs ) def set_data( self, *, x=None, y=None, dx=None, dy=None, width=None, head_width=None, head_length=None, round_tail=None, ): if round_tail is not None: self._round_tail = round_tail super().__init__( x=x, y=y, dx=dx, dy=dy, width=width, head_width=head_width, head_length=head_length ) def _make_verts(self): super()._make_verts() if self._round_tail: self._make_tail_round() def _make_tail_round(self): nvert = 25 add_rot = np.pi/2 radius = width/2 x, y = self._x, self._y dx, dy = self._dx, self._dy if self._head_starts_at_zero: x, y = (self.verts[4] + self.verts[3])/2 dx, dy = (self.verts[4] - self.verts[3])/2 add_rot += np.pi/2 if self._shape in ['left', 'right']: radius /= 2 x, y = (self.verts[4] + self.verts[3])/2 dx, dy = (self.verts[4] - self.verts[3])/2 if self._shape == 'left' and not self._head_starts_at_zero: add_rot += np.pi/2 elif self._shape == 'right': if self._head_starts_at_zero: add_rot += np.pi else: add_rot -= np.pi/2 angle = np.arctan2(dx, dy) + add_rot circ_segs = np.linspace(0, np.pi, nvert+1) + angle tail_verts = np.zeros((nvert, 2), float) tail_verts[:, 0] = np.sin(circ_segs)[:-1]*radius + x tail_verts[:, 1] = np.cos(circ_segs)[:-1]*radius + y if self._shape == 'right': tail_verts = tail_verts[::-1] self.verts = np.array([*self.verts[:4], *tail_verts, *self.verts[4:]]) fig, ax = plt.subplots(dpi=300) fig.tight_layout() width = 0.3 xs = np.sin(np.linspace(0, 2*np.pi, 9))[:-1] ys = np.cos(np.linspace(0, 2*np.pi, 9))[:-1] for x, y in zip(xs, ys): polygon = FancierArrow( x, y, x, y, width=width, edgecolor='black', round_tail=True, head_starts_at_zero=True, shape='left' ) ax.add_patch(polygon) ax.relim() ax.autoscale_view() fig.show()