Skip to content

Commit d9855d3

Browse files
committed
Vectorize Arc.draw.
... by replacing generators into functions working on numpy arrays. All modified functions are nested and thus not publically accessible.
1 parent 59b5e0e commit d9855d3

File tree

1 file changed

+21
-30
lines changed

1 file changed

+21
-30
lines changed

lib/matplotlib/patches.py

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,14 +1558,12 @@ def draw(self, renderer):
15581558
calculation much easier than doing rotated ellipse
15591559
intersection directly).
15601560
1561-
This uses the "line intersecting a circle" algorithm
1562-
from:
1561+
This uses the "line intersecting a circle" algorithm from:
15631562
15641563
Vince, John. *Geometry for Computer Graphics: Formulae,
15651564
Examples & Proofs.* London: Springer-Verlag, 2005.
15661565
1567-
2. The angles of each of the intersection points are
1568-
calculated.
1566+
2. The angles of each of the intersection points are calculated.
15691567
15701568
3. Proceeding counterclockwise starting in the positive
15711569
x-direction, each of the visible arc-segments between the
@@ -1598,7 +1596,7 @@ def theta_stretch(theta, scale):
15981596
self._path = Path.arc(theta1, theta2)
15991597
return Patch.draw(self, renderer)
16001598

1601-
def iter_circle_intersect_on_line(x0, y0, x1, y1):
1599+
def line_circle_intersect(x0, y0, x1, y1):
16021600
dx = x1 - x0
16031601
dy = y1 - y0
16041602
dr2 = dx * dx + dy * dy
@@ -1610,7 +1608,7 @@ def iter_circle_intersect_on_line(x0, y0, x1, y1):
16101608
if discrim == 0.0:
16111609
x = (D * dy) / dr2
16121610
y = (-D * dx) / dr2
1613-
yield x, y
1611+
return np.array([[x, y]])
16141612
elif discrim > 0.0:
16151613
# The definition of "sign" here is different from
16161614
# np.sign: we never want to get 0.0
@@ -1619,12 +1617,15 @@ def iter_circle_intersect_on_line(x0, y0, x1, y1):
16191617
else:
16201618
sign_dy = 1.0
16211619
sqrt_discrim = np.sqrt(discrim)
1622-
for sign in (1., -1.):
1623-
x = (D * dy + sign * sign_dy * dx * sqrt_discrim) / dr2
1624-
y = (-D * dx + sign * np.abs(dy) * sqrt_discrim) / dr2
1625-
yield x, y
1620+
return np.array(
1621+
[[(D * dy + sign_dy * dx * sqrt_discrim) / dr2,
1622+
(-D * dx + abs(dy) * sqrt_discrim) / dr2],
1623+
[(D * dy - sign_dy * dx * sqrt_discrim) / dr2,
1624+
(-D * dx - abs(dy) * sqrt_discrim) / dr2]])
1625+
else:
1626+
return np.empty((0, 2))
16261627

1627-
def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
1628+
def segment_circle_intersect(x0, y0, x1, y1):
16281629
epsilon = 1e-9
16291630
if x1 < x0:
16301631
x0e, x1e = x1, x0
@@ -1634,17 +1635,13 @@ def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
16341635
y0e, y1e = y1, y0
16351636
else:
16361637
y0e, y1e = y0, y1
1637-
x0e -= epsilon
1638-
y0e -= epsilon
1639-
x1e += epsilon
1640-
y1e += epsilon
1641-
for x, y in iter_circle_intersect_on_line(x0, y0, x1, y1):
1642-
if x0e <= x <= x1e and y0e <= y <= y1e:
1643-
yield x, y
1638+
xys = line_circle_intersect(x0, y0, x1, y1)
1639+
xs, ys = xys.T
1640+
return xys[(x0e - epsilon < xs) & (xs < x1e + epsilon)
1641+
& (y0e - epsilon < ys) & (ys < y1e + epsilon)]
16441642

16451643
# Transforms the axes box_path so that it is relative to the unit
1646-
# circle in the same way that it is relative to the desired
1647-
# ellipse.
1644+
# circle in the same way that it is relative to the desired ellipse.
16481645
box_path = Path.unit_rectangle()
16491646
box_path_transform = transforms.BboxTransformTo(self.axes.bbox) + \
16501647
self.get_transform().inverted()
@@ -1653,16 +1650,10 @@ def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
16531650
thetas = set()
16541651
# For each of the point pairs, there is a line segment
16551652
for p0, p1 in zip(box_path.vertices[:-1], box_path.vertices[1:]):
1656-
x0, y0 = p0
1657-
x1, y1 = p1
1658-
for x, y in iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
1659-
theta = np.arccos(x)
1660-
if y < 0:
1661-
theta = 2 * np.pi - theta
1662-
# Convert radians to angles
1663-
theta = np.rad2deg(theta)
1664-
if theta1 < theta < theta2:
1665-
thetas.add(theta)
1653+
xy = segment_circle_intersect(*p0, *p1)
1654+
x, y = xy.T
1655+
theta = np.rad2deg(np.arctan2(y, x))
1656+
thetas.update(theta[(theta1 < theta) & (theta < theta2)])
16661657
thetas = sorted(thetas) + [theta2]
16671658

16681659
last_theta = theta1

0 commit comments

Comments
 (0)