Skip to content

Commit 4bc50da

Browse files
committed
Correct theta values when drawing a non-circular ellipse
Make arcs work with units Add api change not for elliptical notes Add background ellipse and extra line to test Remove pdf and svg tests for arc angles
1 parent c15694b commit 4bc50da

File tree

4 files changed

+55
-15
lines changed

4 files changed

+55
-15
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 are 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
@@ -1554,8 +1554,6 @@ def __init__(self, xy, width, height, angle=0.0,
15541554
self.theta1 = theta1
15551555
self.theta2 = theta2
15561556

1557-
self._path = Path.arc(self.theta1, self.theta2)
1558-
15591557
@allow_rasterization
15601558
def draw(self, renderer):
15611559
"""
@@ -1607,15 +1605,25 @@ def draw(self, renderer):
16071605

16081606
self._recompute_transform()
16091607

1610-
# Get the width and height in pixels
16111608
width = self.convert_xunits(self.width)
16121609
height = self.convert_yunits(self.height)
1610+
1611+
# If the width and height of ellipse are not equal, take into account
1612+
# stretching when calculating angles to draw between
1613+
def theta_stretch(theta, scale):
1614+
theta = np.deg2rad(theta)
1615+
x = np.cos(theta)
1616+
y = np.sin(theta)
1617+
return np.rad2deg(np.arctan2(scale * y, x))
1618+
theta1 = theta_stretch(self.theta1, width / height)
1619+
theta2 = theta_stretch(self.theta2, width / height)
1620+
1621+
# Get width and height in pixels
16131622
width, height = self.get_transform().transform_point(
16141623
(width, height))
16151624
inv_error = (1.0 / 1.89818e-6) * 0.5
1616-
16171625
if width < inv_error and height < inv_error:
1618-
# self._path = Path.arc(self.theta1, self.theta2)
1626+
self._path = Path.arc(theta1, theta2)
16191627
return Patch.draw(self, renderer)
16201628

16211629
def iter_circle_intersect_on_line(x0, y0, x1, y1):
@@ -1670,8 +1678,6 @@ def iter_circle_intersect_on_line_seg(x0, y0, x1, y1):
16701678
self.get_transform().inverted()
16711679
box_path = box_path.transformed(box_path_transform)
16721680

1673-
theta1 = self.theta1
1674-
theta2 = self.theta2
16751681
thetas = set()
16761682
# For each of the point pairs, there is a line segment
16771683
for p0, p1 in zip(box_path.vertices[:-1], box_path.vertices[1:]):

lib/matplotlib/tests/test_axes.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,32 @@ def test_canonical():
982982
ax.plot([1, 2, 3])
983983

984984

985+
@image_comparison(baseline_images=['arc_angles'], remove_text=True,
986+
style='default', extensions=['png'])
987+
def test_arc_angles():
988+
from matplotlib import patches
989+
# Ellipse parameters
990+
w = 2
991+
h = 1
992+
centre = (0.2, 0.5)
993+
994+
fig, axs = plt.subplots(3, 3)
995+
for i, ax in enumerate(axs.flat):
996+
theta2 = i * 360 / 9
997+
theta1 = theta2 - 45
998+
ax.add_patch(patches.Ellipse(centre, w, h, alpha=0.3))
999+
ax.add_patch(patches.Arc(centre, w, h, theta1=theta1, theta2=theta2))
1000+
# Straight lines intersecting start and end of arc
1001+
ax.plot([2 * np.cos(np.deg2rad(theta1)) + centre[0],
1002+
centre[0],
1003+
2 * np.cos(np.deg2rad(theta2)) + centre[0]],
1004+
[2 * np.sin(np.deg2rad(theta1)) + centre[1],
1005+
centre[1],
1006+
2 * np.sin(np.deg2rad(theta2)) + centre[1]])
1007+
ax.set_xlim(-2, 2)
1008+
ax.set_ylim(-2, 2)
1009+
1010+
9851011
@image_comparison(baseline_images=['arc_ellipse'],
9861012
remove_text=True)
9871013
def test_arc_ellipse():
@@ -990,23 +1016,23 @@ def test_arc_ellipse():
9901016
width, height = 1e-1, 3e-1
9911017
angle = -30
9921018

993-
theta = np.arange(0.0, 360.0, 1.0)*np.pi/180.0
994-
x = width/2. * np.cos(theta)
995-
y = height/2. * np.sin(theta)
1019+
theta = np.arange(0.0, 360.0, 1.0) * np.pi / 180.0
1020+
x = width / 2. * np.cos(theta)
1021+
y = height / 2. * np.sin(theta)
9961022

997-
rtheta = angle*np.pi/180.
1023+
rtheta = angle * np.pi / 180.
9981024
R = np.array([
999-
[np.cos(rtheta), -np.sin(rtheta)],
1000-
[np.sin(rtheta), np.cos(rtheta)],
1001-
])
1025+
[np.cos(rtheta), -np.sin(rtheta)],
1026+
[np.sin(rtheta), np.cos(rtheta)]])
10021027

10031028
x, y = np.dot(R, np.array([x, y]))
10041029
x += xcenter
10051030
y += ycenter
10061031

10071032
fig = plt.figure()
10081033
ax = fig.add_subplot(211, aspect='auto')
1009-
ax.fill(x, y, alpha=0.2, facecolor='yellow', edgecolor='yellow', linewidth=1, zorder=1)
1034+
ax.fill(x, y, alpha=0.2, facecolor='yellow', edgecolor='yellow',
1035+
linewidth=1, zorder=1)
10101036

10111037
e1 = patches.Arc((xcenter, ycenter), width, height,
10121038
angle=angle, linewidth=2, fill=False, zorder=2)

0 commit comments

Comments
 (0)