Skip to content

Commit 963f528

Browse files
committed
make FancyArrow animatable
1 parent 6d629a1 commit 963f528

File tree

2 files changed

+83
-18
lines changed

2 files changed

+83
-18
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
``set_data`` method for ``FancyArrow`` patch
2+
--------------------------------------------
3+
4+
`.FancyArrow`, the patch returned by ``ax.arrow``, now has a ``set_data``
5+
method that allows for animating the arrow.

lib/matplotlib/patches.py

Lines changed: 78 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,22 +1333,81 @@ def __init__(self, x, y, dx, dy, width=0.001, length_includes_head=False,
13331333
13341334
%(Patch_kwdoc)s
13351335
"""
1336-
if head_width is None:
1337-
head_width = 3 * width
1338-
if head_length is None:
1336+
self.x = x
1337+
self.y = y
1338+
self.dx = dx
1339+
self.dy = dy
1340+
self.width = width
1341+
self.length_includes_head = length_includes_head
1342+
self.head_width = head_width
1343+
self.head_length = head_length
1344+
self.shape = shape
1345+
self.overhang = overhang
1346+
self.head_starts_at_zero = head_starts_at_zero
1347+
self._make_verts()
1348+
super().__init__(self.verts, closed=True, **kwargs)
1349+
1350+
def set_data(self, *, x=None, y=None, dx=None, dy=None, width=None,
1351+
head_width=None, head_length=None):
1352+
"""
1353+
Set `.FancyArrow` x, y, dx, dy, width, head_with, and head_length.
1354+
Values left as None will not be updated.
1355+
1356+
Parameters
1357+
----------
1358+
x, y : float or None, default: None
1359+
The x and y coordinates of the arrow base.
1360+
1361+
dx, dy : float or None, default: None
1362+
The length of the arrow along x and y direction.
1363+
1364+
width: float or None, default: None
1365+
Width of full arrow tail.
1366+
1367+
head_width: float or None, default: None
1368+
Total width of the full arrow head.
1369+
1370+
head_length: float or None, default: None
1371+
Length of arrow head.
1372+
"""
1373+
if x is not None:
1374+
self.x = x
1375+
if y is not None:
1376+
self.y = y
1377+
if dx is not None:
1378+
self.dx = dx
1379+
if dy is not None:
1380+
self.dy = dy
1381+
if width is not None:
1382+
self.width = width
1383+
if head_width is not None:
1384+
self.head_width = head_width
1385+
if head_length is not None:
1386+
self.head_length = head_length
1387+
self._make_verts()
1388+
self.set_xy(self.verts)
1389+
1390+
def _make_verts(self):
1391+
if self.head_width is None:
1392+
head_width = 3 * self.width
1393+
else:
1394+
head_width = self.head_width
1395+
if self.head_length is None:
13391396
head_length = 1.5 * head_width
1397+
else:
1398+
head_length = self.head_length
13401399

1341-
distance = np.hypot(dx, dy)
1400+
distance = np.hypot(self.dx, self.dy)
13421401

1343-
if length_includes_head:
1402+
if self.length_includes_head:
13441403
length = distance
13451404
else:
13461405
length = distance + head_length
13471406
if not length:
1348-
verts = np.empty([0, 2]) # display nothing if empty
1407+
self.verts = np.empty([0, 2]) # display nothing if empty
13491408
else:
13501409
# start by drawing horizontal arrow, point at (0, 0)
1351-
hw, hl, hs, lw = head_width, head_length, overhang, width
1410+
hw, hl, hs, lw = head_width, head_length, self.overhang, self.width
13521411
left_half_arrow = np.array([
13531412
[0.0, 0.0], # tip
13541413
[-hl, -hw / 2], # leftmost
@@ -1357,36 +1416,37 @@ def __init__(self, x, y, dx, dy, width=0.001, length_includes_head=False,
13571416
[-length, 0],
13581417
])
13591418
# if we're not including the head, shift up by head length
1360-
if not length_includes_head:
1419+
if not self.length_includes_head:
13611420
left_half_arrow += [head_length, 0]
13621421
# if the head starts at 0, shift up by another head length
1363-
if head_starts_at_zero:
1422+
if self.head_starts_at_zero:
13641423
left_half_arrow += [head_length / 2, 0]
13651424
# figure out the shape, and complete accordingly
1366-
if shape == 'left':
1425+
if self.shape == 'left':
13671426
coords = left_half_arrow
13681427
else:
13691428
right_half_arrow = left_half_arrow * [1, -1]
1370-
if shape == 'right':
1429+
if self.shape == 'right':
13711430
coords = right_half_arrow
1372-
elif shape == 'full':
1431+
elif self.shape == 'full':
13731432
# The half-arrows contain the midpoint of the stem,
13741433
# which we can omit from the full arrow. Including it
13751434
# twice caused a problem with xpdf.
13761435
coords = np.concatenate([left_half_arrow[:-1],
13771436
right_half_arrow[-2::-1]])
13781437
else:
1379-
raise ValueError("Got unknown shape: %s" % shape)
1438+
raise ValueError("Got unknown shape: %s" % self.shape)
13801439
if distance != 0:
1381-
cx = dx / distance
1382-
sx = dy / distance
1440+
cx = self.dx / distance
1441+
sx = self.dy / distance
13831442
else:
13841443
# Account for division by zero
13851444
cx, sx = 0, 1
13861445
M = [[cx, sx], [-sx, cx]]
1387-
verts = np.dot(coords, M) + (x + dx, y + dy)
1388-
1389-
super().__init__(verts, closed=True, **kwargs)
1446+
self.verts = np.dot(coords, M) + [
1447+
self.x + self.dx,
1448+
self.y + self.dy,
1449+
]
13901450

13911451

13921452
docstring.interpd.update(

0 commit comments

Comments
 (0)