Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 85efd95

Browse files
authored
Merge pull request #14694 from anntzer/arcdraw
Vectorize Arc.draw.
2 parents 805cb72 + f8a87e2 commit 85efd95

File tree

3 files changed

+69
-40
lines changed

3 files changed

+69
-40
lines changed

lib/matplotlib/patches.py

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,14 +1592,12 @@ def draw(self, renderer):
15921592
calculation much easier than doing rotated ellipse
15931593
intersection directly).
15941594
1595-
This uses the "line intersecting a circle" algorithm
1596-
from:
1595+
This uses the "line intersecting a circle" algorithm from:
15971596
15981597
Vince, John. *Geometry for Computer Graphics: Formulae,
15991598
Examples & Proofs.* London: Springer-Verlag, 2005.
16001599
1601-
2. The angles of each of the intersection points are
1602-
calculated.
1600+
2. The angles of each of the intersection points are calculated.
16031601
16041602
3. Proceeding counterclockwise starting in the positive
16051603
x-direction, each of the visible arc-segments between the
@@ -1632,33 +1630,25 @@ def theta_stretch(theta, scale):
16321630
self._path = Path.arc(theta1, theta2)
16331631
return Patch.draw(self, renderer)
16341632

1635-
def iter_circle_intersect_on_line(x0, y0, x1, y1):
1633+
def line_circle_intersect(x0, y0, x1, y1):
16361634
dx = x1 - x0
16371635
dy = y1 - y0
16381636
dr2 = dx * dx + dy * dy
16391637
D = x0 * y1 - x1 * y0
16401638
D2 = D * D
16411639
discrim = dr2 - D2
1642-
1643-
# Single (tangential) intersection
1644-
if discrim == 0.0:
1645-
x = (D * dy) / dr2
1646-
y = (-D * dx) / dr2
1647-
yield x, y
1648-
elif discrim > 0.0:
1649-
# The definition of "sign" here is different from
1650-
# np.sign: we never want to get 0.0
1651-
if dy < 0.0:
1652-
sign_dy = -1.0
1653-
else:
1654-
sign_dy = 1.0
1640+
if discrim >= 0.0:
1641+
sign_dy = np.copysign(1, dy) # +/-1, never 0.
16551642
sqrt_discrim = np.sqrt(discrim)
1656-
for sign in (1., -1.):
1657-
x = (D * dy + sign * sign_dy * dx * sqrt_discrim) / dr2
1658-
y = (-D * dx + sign * np.abs(dy) * sqrt_discrim) / dr2
1659-
yield x, y
1643+
return np.array(
1644+
[[(D * dy + sign_dy * dx * sqrt_discrim) / dr2,
1645+
(-D * dx + abs(dy) * sqrt_discrim) / dr2],
1646+
[(D * dy - sign_dy * dx * sqrt_discrim) / dr2,
1647+
(-D * dx - abs(dy) * sqrt_discrim) / dr2]])
1648+
else:
1649+
return np.empty((0, 2))
16601650

1661-
def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
1651+
def segment_circle_intersect(x0, y0, x1, y1):
16621652
epsilon = 1e-9
16631653
if x1 < x0:
16641654
x0e, x1e = x1, x0
@@ -1668,13 +1658,10 @@ def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
16681658
y0e, y1e = y1, y0
16691659
else:
16701660
y0e, y1e = y0, y1
1671-
x0e -= epsilon
1672-
y0e -= epsilon
1673-
x1e += epsilon
1674-
y1e += epsilon
1675-
for x, y in iter_circle_intersect_on_line(x0, y0, x1, y1):
1676-
if x0e <= x <= x1e and y0e <= y <= y1e:
1677-
yield x, y
1661+
xys = line_circle_intersect(x0, y0, x1, y1)
1662+
xs, ys = xys.T
1663+
return xys[(x0e - epsilon < xs) & (xs < x1e + epsilon)
1664+
& (y0e - epsilon < ys) & (ys < y1e + epsilon)]
16781665

16791666
# Transforms the axes box_path so that it is relative to the unit
16801667
# circle in the same way that it is relative to the desired ellipse.
@@ -1686,16 +1673,10 @@ def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
16861673
thetas = set()
16871674
# For each of the point pairs, there is a line segment
16881675
for p0, p1 in zip(box_path.vertices[:-1], box_path.vertices[1:]):
1689-
x0, y0 = p0
1690-
x1, y1 = p1
1691-
for x, y in iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
1692-
theta = np.arccos(x)
1693-
if y < 0:
1694-
theta = 2 * np.pi - theta
1695-
# Convert radians to angles
1696-
theta = np.rad2deg(theta)
1697-
if theta1 < theta < theta2:
1698-
thetas.add(theta)
1676+
xy = segment_circle_intersect(*p0, *p1)
1677+
x, y = xy.T
1678+
theta = np.rad2deg(np.arctan2(y, x))
1679+
thetas.update(theta[(theta1 < theta) & (theta < theta2)])
16991680
thetas = sorted(thetas) + [theta2]
17001681

17011682
last_theta = theta1
Lines changed: 40 additions & 0 deletions
Loading

lib/matplotlib/tests/test_patches.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,3 +490,11 @@ def test_fancyarrow_units():
490490
fig, ax = plt.subplots()
491491
arrow = FancyArrowPatch((0, dtime), (0.01, dtime))
492492
ax.add_patch(arrow)
493+
494+
495+
@image_comparison(["large_arc.svg"], style="mpl20")
496+
def test_large_arc():
497+
ax = plt.figure().add_subplot()
498+
ax.set_axis_off()
499+
# A large arc that crosses the axes view limits.
500+
ax.add_patch(mpatches.Arc((-100, 0), 201, 201))

0 commit comments

Comments
 (0)