Skip to content

Commit 0449ece

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 0449ece

File tree

1 file changed

+20
-30
lines changed

1 file changed

+20
-30
lines changed

lib/matplotlib/patches.py

Lines changed: 20 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,14 @@ 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.row_stack(
1621+
[[(D * dy + sign * sign_dy * dx * sqrt_discrim) / dr2,
1622+
(-D * dx + sign * np.abs(dy) * sqrt_discrim) / dr2]
1623+
for sign in [1, -1]])
1624+
else:
1625+
return np.empty((0, 2))
16261626

1627-
def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
1627+
def segment_circle_intersect(x0, y0, x1, y1):
16281628
epsilon = 1e-9
16291629
if x1 < x0:
16301630
x0e, x1e = x1, x0
@@ -1634,17 +1634,13 @@ def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
16341634
y0e, y1e = y1, y0
16351635
else:
16361636
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
1637+
xys = line_circle_intersect(x0, y0, x1, y1)
1638+
xs, ys = xys.T
1639+
return xys[(x0e - epsilon < xs) & (xs < x1e + epsilon)
1640+
& (y0e - epsilon < ys) & (ys < y1e + epsilon)]
16441641

16451642
# 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.
1643+
# circle in the same way that it is relative to the desired ellipse.
16481644
box_path = Path.unit_rectangle()
16491645
box_path_transform = transforms.BboxTransformTo(self.axes.bbox) + \
16501646
self.get_transform().inverted()
@@ -1653,16 +1649,10 @@ def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
16531649
thetas = set()
16541650
# For each of the point pairs, there is a line segment
16551651
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)
1652+
xy = segment_circle_intersect(*p0, *p1)
1653+
x, y = xy.T
1654+
theta = np.rad2deg(np.arctan2(y, x))
1655+
thetas.update(theta[(theta1 < theta) & (theta < theta2)])
16661656
thetas = sorted(thetas) + [theta2]
16671657

16681658
last_theta = theta1

0 commit comments

Comments
 (0)