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

Skip to content

Commit d67550c

Browse files
committed
Backport PR matplotlib#17564: FIX: correctly handle large arcs
Merge pull request matplotlib#17564 from tacaswell/fix_big_arc FIX: big arc code path Conflicts: lib/matplotlib/patches.py - implicitly backport a change from matplotlib#15356 (from `- trans ` -> `+ trans.inverted()`)
1 parent d885c0f commit d67550c

File tree

5 files changed

+632
-32
lines changed

5 files changed

+632
-32
lines changed

lib/matplotlib/patches.py

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,6 +1607,8 @@ def draw(self, renderer):
16071607
"""
16081608
if not hasattr(self, 'axes'):
16091609
raise RuntimeError('Arcs can only be used in Axes instances')
1610+
if not self.get_visible():
1611+
return
16101612

16111613
self._recompute_transform()
16121614

@@ -1619,14 +1621,40 @@ def theta_stretch(theta, scale):
16191621
theta = np.deg2rad(theta)
16201622
x = np.cos(theta)
16211623
y = np.sin(theta)
1622-
return np.rad2deg(np.arctan2(scale * y, x))
1623-
theta1 = theta_stretch(self.theta1, width / height)
1624-
theta2 = theta_stretch(self.theta2, width / height)
1625-
1626-
# Get width and height in pixels
1627-
width, height = self.get_transform().transform((width, height))
1624+
stheta = np.rad2deg(np.arctan2(scale * y, x))
1625+
# arctan2 has the range [-pi, pi], we expect [0, 2*pi]
1626+
return (stheta + 360) % 360
1627+
1628+
theta1 = self.theta1
1629+
theta2 = self.theta2
1630+
1631+
if (
1632+
# if we need to stretch the angles because we are distorted
1633+
width != height
1634+
# and we are not doing a full circle.
1635+
#
1636+
# 0 and 360 do not exactly round-trip through the angle
1637+
# stretching (due to both float precision limitations and
1638+
# the difference between the range of arctan2 [-pi, pi] and
1639+
# this method [0, 360]) so avoid doing it if we don't have to.
1640+
and not (theta1 != theta2 and theta1 % 360 == theta2 % 360)
1641+
):
1642+
theta1 = theta_stretch(self.theta1, width / height)
1643+
theta2 = theta_stretch(self.theta2, width / height)
1644+
1645+
# Get width and height in pixels we need to use
1646+
# `self.get_data_transform` rather than `self.get_transform`
1647+
# because we want the transform from dataspace to the
1648+
# screen space to estimate how big the arc will be in physical
1649+
# units when rendered (the transform that we get via
1650+
# `self.get_transform()` goes from an idealized unit-radius
1651+
# space to screen space).
1652+
data_to_screen_trans = self.get_data_transform()
1653+
pwidth, pheight = (data_to_screen_trans.transform((width, height)) -
1654+
data_to_screen_trans.transform((0, 0)))
16281655
inv_error = (1.0 / 1.89818e-6) * 0.5
1629-
if width < inv_error and height < inv_error:
1656+
1657+
if pwidth < inv_error and pheight < inv_error:
16301658
self._path = Path.arc(theta1, theta2)
16311659
return Patch.draw(self, renderer)
16321660

@@ -1660,29 +1688,32 @@ def segment_circle_intersect(x0, y0, x1, y1):
16601688
y0e, y1e = y0, y1
16611689
xys = line_circle_intersect(x0, y0, x1, y1)
16621690
xs, ys = xys.T
1663-
return xys[(x0e - epsilon < xs) & (xs < x1e + epsilon)
1664-
& (y0e - epsilon < ys) & (ys < y1e + epsilon)]
1691+
return xys[
1692+
(x0e - epsilon < xs) & (xs < x1e + epsilon)
1693+
& (y0e - epsilon < ys) & (ys < y1e + epsilon)
1694+
]
16651695

16661696
# Transforms the axes box_path so that it is relative to the unit
16671697
# circle in the same way that it is relative to the desired ellipse.
1668-
box_path = Path.unit_rectangle()
16691698
box_path_transform = (transforms.BboxTransformTo(self.axes.bbox)
1670-
- self.get_transform())
1671-
box_path = box_path.transformed(box_path_transform)
1699+
+ self.get_transform().inverted())
1700+
box_path = Path.unit_rectangle().transformed(box_path_transform)
16721701

16731702
thetas = set()
16741703
# For each of the point pairs, there is a line segment
16751704
for p0, p1 in zip(box_path.vertices[:-1], box_path.vertices[1:]):
16761705
xy = segment_circle_intersect(*p0, *p1)
16771706
x, y = xy.T
1678-
theta = np.rad2deg(np.arctan2(y, x))
1707+
# arctan2 return [-pi, pi), the rest of our angles are in
1708+
# [0, 360], adjust as needed.
1709+
theta = (np.rad2deg(np.arctan2(y, x)) + 360) % 360
16791710
thetas.update(theta[(theta1 < theta) & (theta < theta2)])
16801711
thetas = sorted(thetas) + [theta2]
1681-
16821712
last_theta = theta1
16831713
theta1_rad = np.deg2rad(theta1)
1684-
inside = box_path.contains_point((np.cos(theta1_rad),
1685-
np.sin(theta1_rad)))
1714+
inside = box_path.contains_point(
1715+
(np.cos(theta1_rad), np.sin(theta1_rad))
1716+
)
16861717

16871718
# save original path
16881719
path_original = self._path
Loading

0 commit comments

Comments
 (0)