Skip to content

Commit a004409

Browse files
authored
Merge pull request #8047 from dstansby/arc-theta
Correct theta values when drawing a non-circular arc
2 parents 1cf292b + 6e6058b commit a004409

File tree

4 files changed

+53
-14
lines changed

4 files changed

+53
-14
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Elliptical arcs now drawn between correct angles
2+
````````````````````````````````````````````````
3+
4+
The `matplotlib.patches.Arc` patch is now correctly drawn between the given
5+
angles.
6+
7+
Previously a circular arc was drawn and then stretched into an ellipse,
8+
so the resulting arc did not lie between *theta1* and *theta2*.

lib/matplotlib/patches.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,8 +1566,6 @@ def __init__(self, xy, width, height, angle=0.0,
15661566
self.theta1 = theta1
15671567
self.theta2 = theta2
15681568

1569-
self._path = Path.arc(self.theta1, self.theta2)
1570-
15711569
@allow_rasterization
15721570
def draw(self, renderer):
15731571
"""
@@ -1619,15 +1617,25 @@ def draw(self, renderer):
16191617

16201618
self._recompute_transform()
16211619

1622-
# Get the width and height in pixels
16231620
width = self.convert_xunits(self.width)
16241621
height = self.convert_yunits(self.height)
1622+
1623+
# If the width and height of ellipse are not equal, take into account
1624+
# stretching when calculating angles to draw between
1625+
def theta_stretch(theta, scale):
1626+
theta = np.deg2rad(theta)
1627+
x = np.cos(theta)
1628+
y = np.sin(theta)
1629+
return np.rad2deg(np.arctan2(scale * y, x))
1630+
theta1 = theta_stretch(self.theta1, width / height)
1631+
theta2 = theta_stretch(self.theta2, width / height)
1632+
1633+
# Get width and height in pixels
16251634
width, height = self.get_transform().transform_point(
16261635
(width, height))
16271636
inv_error = (1.0 / 1.89818e-6) * 0.5
1628-
16291637
if width < inv_error and height < inv_error:
1630-
# self._path = Path.arc(self.theta1, self.theta2)
1638+
self._path = Path.arc(theta1, theta2)
16311639
return Patch.draw(self, renderer)
16321640

16331641
def iter_circle_intersect_on_line(x0, y0, x1, y1):
@@ -1682,8 +1690,6 @@ def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
16821690
self.get_transform().inverted()
16831691
box_path = box_path.transformed(box_path_transform)
16841692

1685-
theta1 = self.theta1
1686-
theta2 = self.theta2
16871693
thetas = set()
16881694
# For each of the point pairs, there is a line segment
16891695
for p0, p1 in zip(box_path.vertices[:-1], box_path.vertices[1:]):

lib/matplotlib/tests/test_axes.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,32 @@ def test_canonical():
10021002
ax.plot([1, 2, 3])
10031003

10041004

1005+
@image_comparison(baseline_images=['arc_angles'], remove_text=True,
1006+
style='default', extensions=['png'])
1007+
def test_arc_angles():
1008+
from matplotlib import patches
1009+
# Ellipse parameters
1010+
w = 2
1011+
h = 1
1012+
centre = (0.2, 0.5)
1013+
1014+
fig, axs = plt.subplots(3, 3)
1015+
for i, ax in enumerate(axs.flat):
1016+
theta2 = i * 360 / 9
1017+
theta1 = theta2 - 45
1018+
ax.add_patch(patches.Ellipse(centre, w, h, alpha=0.3))
1019+
ax.add_patch(patches.Arc(centre, w, h, theta1=theta1, theta2=theta2))
1020+
# Straight lines intersecting start and end of arc
1021+
ax.plot([2 * np.cos(np.deg2rad(theta1)) + centre[0],
1022+
centre[0],
1023+
2 * np.cos(np.deg2rad(theta2)) + centre[0]],
1024+
[2 * np.sin(np.deg2rad(theta1)) + centre[1],
1025+
centre[1],
1026+
2 * np.sin(np.deg2rad(theta2)) + centre[1]])
1027+
ax.set_xlim(-2, 2)
1028+
ax.set_ylim(-2, 2)
1029+
1030+
10051031
@image_comparison(baseline_images=['arc_ellipse'],
10061032
remove_text=True)
10071033
def test_arc_ellipse():
@@ -1010,15 +1036,14 @@ def test_arc_ellipse():
10101036
width, height = 1e-1, 3e-1
10111037
angle = -30
10121038

1013-
theta = np.arange(0.0, 360.0, 1.0)*np.pi/180.0
1014-
x = width/2. * np.cos(theta)
1015-
y = height/2. * np.sin(theta)
1039+
theta = np.arange(0.0, 360.0, 1.0) * np.pi / 180.0
1040+
x = width / 2. * np.cos(theta)
1041+
y = height / 2. * np.sin(theta)
10161042

1017-
rtheta = angle*np.pi/180.
1043+
rtheta = angle * np.pi / 180.
10181044
R = np.array([
1019-
[np.cos(rtheta), -np.sin(rtheta)],
1020-
[np.sin(rtheta), np.cos(rtheta)],
1021-
])
1045+
[np.cos(rtheta), -np.sin(rtheta)],
1046+
[np.sin(rtheta), np.cos(rtheta)]])
10221047

10231048
x, y = np.dot(R, np.array([x, y]))
10241049
x += xcenter

0 commit comments

Comments
 (0)