Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 74bf6ae

Browse filesBrowse files
committed
Set correct path for Arc
1 parent 3245d39 commit 74bf6ae
Copy full SHA for 74bf6ae

File tree

Expand file treeCollapse file tree

6 files changed

+179
-41
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+179
-41
lines changed

‎lib/matplotlib/patches.py

Copy file name to clipboardExpand all lines: lib/matplotlib/patches.py
+50-37Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,6 +1968,9 @@ def __init__(self, xy, width, height, angle=0.0,
19681968

19691969
self.theta1 = theta1
19701970
self.theta2 = theta2
1971+
self._theta1, self._theta2, self._width, self._height = \
1972+
self._theta_stretch()
1973+
self._path = Path.arc(self._theta1, self._theta2)
19711974

19721975
@artist.allow_rasterization
19731976
def draw(self, renderer):
@@ -2023,36 +2026,7 @@ def draw(self, renderer):
20232026

20242027
self._recompute_transform()
20252028

2026-
width = self.convert_xunits(self.width)
2027-
height = self.convert_yunits(self.height)
2028-
2029-
# If the width and height of ellipse are not equal, take into account
2030-
# stretching when calculating angles to draw between
2031-
def theta_stretch(theta, scale):
2032-
theta = np.deg2rad(theta)
2033-
x = np.cos(theta)
2034-
y = np.sin(theta)
2035-
stheta = np.rad2deg(np.arctan2(scale * y, x))
2036-
# arctan2 has the range [-pi, pi], we expect [0, 2*pi]
2037-
return (stheta + 360) % 360
2038-
2039-
theta1 = self.theta1
2040-
theta2 = self.theta2
2041-
2042-
if (
2043-
# if we need to stretch the angles because we are distorted
2044-
width != height
2045-
# and we are not doing a full circle.
2046-
#
2047-
# 0 and 360 do not exactly round-trip through the angle
2048-
# stretching (due to both float precision limitations and
2049-
# the difference between the range of arctan2 [-pi, pi] and
2050-
# this method [0, 360]) so avoid doing it if we don't have to.
2051-
and not (theta1 != theta2 and theta1 % 360 == theta2 % 360)
2052-
):
2053-
theta1 = theta_stretch(self.theta1, width / height)
2054-
theta2 = theta_stretch(self.theta2, width / height)
2055-
2029+
self._update_path()
20562030
# Get width and height in pixels we need to use
20572031
# `self.get_data_transform` rather than `self.get_transform`
20582032
# because we want the transform from dataspace to the
@@ -2061,12 +2035,12 @@ def theta_stretch(theta, scale):
20612035
# `self.get_transform()` goes from an idealized unit-radius
20622036
# space to screen space).
20632037
data_to_screen_trans = self.get_data_transform()
2064-
pwidth, pheight = (data_to_screen_trans.transform((width, height)) -
2065-
data_to_screen_trans.transform((0, 0)))
2038+
pwidth, pheight = (
2039+
data_to_screen_trans.transform((self._width, self._height)) -
2040+
data_to_screen_trans.transform((0, 0)))
20662041
inv_error = (1.0 / 1.89818e-6) * 0.5
20672042

20682043
if pwidth < inv_error and pheight < inv_error:
2069-
self._path = Path.arc(theta1, theta2)
20702044
return Patch.draw(self, renderer)
20712045

20722046
def line_circle_intersect(x0, y0, x1, y1):
@@ -2118,10 +2092,11 @@ def segment_circle_intersect(x0, y0, x1, y1):
21182092
# arctan2 return [-pi, pi), the rest of our angles are in
21192093
# [0, 360], adjust as needed.
21202094
theta = (np.rad2deg(np.arctan2(y, x)) + 360) % 360
2121-
thetas.update(theta[(theta1 < theta) & (theta < theta2)])
2122-
thetas = sorted(thetas) + [theta2]
2123-
last_theta = theta1
2124-
theta1_rad = np.deg2rad(theta1)
2095+
thetas.update(
2096+
theta[(self._theta1 < theta) & (theta < self._theta2)])
2097+
thetas = sorted(thetas) + [self._theta2]
2098+
last_theta = self._theta1
2099+
theta1_rad = np.deg2rad(self._theta1)
21252100
inside = box_path.contains_point(
21262101
(np.cos(theta1_rad), np.sin(theta1_rad))
21272102
)
@@ -2140,6 +2115,44 @@ def segment_circle_intersect(x0, y0, x1, y1):
21402115
# restore original path
21412116
self._path = path_original
21422117

2118+
def _update_path(self):
2119+
# Compute new values and update and set new _path if changed
2120+
stretched = self._theta_stretch()
2121+
if any(a != b for a, b in zip(stretched, (self._theta1, self._theta2,
2122+
self._width, self._height))):
2123+
self._theta1, self._theta2, self._width, self._height = stretched
2124+
self._path = Path.arc(self._theta1, self._theta2)
2125+
2126+
def _theta_stretch(self):
2127+
# If the width and height of ellipse are not equal, take into account
2128+
# stretching when calculating angles to draw between
2129+
def theta_stretch(theta, scale):
2130+
theta = np.deg2rad(theta)
2131+
x = np.cos(theta)
2132+
y = np.sin(theta)
2133+
stheta = np.rad2deg(np.arctan2(scale * y, x))
2134+
# arctan2 has the range [-pi, pi], we expect [0, 2*pi]
2135+
return (stheta + 360) % 360
2136+
2137+
width = self.convert_xunits(self.width)
2138+
height = self.convert_yunits(self.height)
2139+
if (
2140+
# if we need to stretch the angles because we are distorted
2141+
width != height
2142+
# and we are not doing a full circle.
2143+
#
2144+
# 0 and 360 do not exactly round-trip through the angle
2145+
# stretching (due to both float precision limitations and
2146+
# the difference between the range of arctan2 [-pi, pi] and
2147+
# this method [0, 360]) so avoid doing it if we don't have to.
2148+
and not (self.theta1 != self.theta2 and
2149+
self.theta1 % 360 == self.theta2 % 360)
2150+
):
2151+
theta1 = theta_stretch(self.theta1, width / height)
2152+
theta2 = theta_stretch(self.theta2, width / height)
2153+
return theta1, theta2, width, height
2154+
return self.theta1, self.theta2, width, height
2155+
21432156

21442157
def bbox_artist(artist, renderer, props=None, fill=True):
21452158
"""
Loading
+85Lines changed: 85 additions & 0 deletions
Loading

‎lib/matplotlib/tests/test_patches.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_patches.py
+32-3Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import matplotlib as mpl
99
from matplotlib.patches import (Annulus, Ellipse, Patch, Polygon, Rectangle,
10-
FancyArrowPatch, FancyArrow, BoxStyle)
10+
FancyArrowPatch, FancyArrow, BoxStyle, Arc)
1111
from matplotlib.testing.decorators import image_comparison, check_figures_equal
1212
from matplotlib.transforms import Bbox
1313
import matplotlib.pyplot as plt
@@ -642,7 +642,7 @@ def test_large_arc():
642642
y = -2115
643643
diameter = 4261
644644
for ax in [ax1, ax2]:
645-
a = mpatches.Arc((x, y), diameter, diameter, lw=2, color='k')
645+
a = Arc((x, y), diameter, diameter, lw=2, color='k')
646646
ax.add_patch(a)
647647
ax.set_axis_off()
648648
ax.set_aspect('equal')
@@ -669,7 +669,7 @@ def test_rotated_arcs():
669669
for prescale, centers in zip((1 - .0001, (1 - .0001) / np.sqrt(2)),
670670
(on_axis_centers, diag_centers)):
671671
for j, (x_sign, y_sign) in enumerate(centers, start=k):
672-
a = mpatches.Arc(
672+
a = Arc(
673673
(x_sign * scale * prescale,
674674
y_sign * scale * prescale),
675675
scale * sx,
@@ -808,3 +808,32 @@ def test_default_capstyle():
808808
def test_default_joinstyle():
809809
patch = Patch()
810810
assert patch.get_joinstyle() == 'miter'
811+
812+
813+
@image_comparison(["autoscale_arc"], extensions=['png', 'svg'],
814+
style="mpl20")
815+
def test_autoscale_arc():
816+
fig, axs = plt.subplots(1, 3, squeeze=False, figsize=(4, 1))
817+
arc_lists = (
818+
[Arc((0, 0), 1, 1, theta1=0, theta2=90)],
819+
[Arc((0.5, 0.5), 1.5, 0.5, theta1=10, theta2=20)],
820+
[Arc((0.5, 0.5), 1.5, 0.5, theta1=10, theta2=20),
821+
Arc((0.5, 0.5), 2.5, 0.5, theta1=110, theta2=120),
822+
Arc((0.5, 0.5), 3.5, 0.5, theta1=210, theta2=220),
823+
Arc((0.5, 0.5), 4.5, 0.5, theta1=310, theta2=320)])
824+
825+
for ax, arcs in zip(axs[0], arc_lists):
826+
for arc in arcs:
827+
ax.add_patch(arc)
828+
ax.autoscale()
829+
ax.set_axis_off()
830+
831+
832+
@check_figures_equal(extensions=["png", 'svg', 'pdf', 'eps'])
833+
def test_arc_in_collection(fig_test, fig_ref):
834+
arc1 = Arc([.5, .5], .5, 1, theta1=0, theta2=60, angle=20)
835+
arc2 = Arc([.5, .5], .5, 1, theta1=0, theta2=60, angle=20)
836+
col = mcollections.PatchCollection(patches=[arc2], facecolors='none',
837+
edgecolors='k')
838+
fig_ref.subplots().add_patch(arc1)
839+
fig_test.subplots().add_collection(col)
Loading

‎lib/mpl_toolkits/tests/test_mplot3d.py

Copy file name to clipboardExpand all lines: lib/mpl_toolkits/tests/test_mplot3d.py
+12-1Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import matplotlib as mpl
88
from matplotlib.backend_bases import MouseButton
99
from matplotlib import cm
10-
from matplotlib import colors as mcolors
10+
from matplotlib import colors as mcolors, patches as mpatch
1111
from matplotlib.testing.decorators import image_comparison, check_figures_equal
1212
from matplotlib.testing.widgets import mock_event
1313
from matplotlib.collections import LineCollection, PolyCollection
@@ -1742,3 +1742,14 @@ def test_view_init_vertical_axis(
17421742
tickdir_expected = tickdirs_expected[i]
17431743
tickdir_actual = axis._get_tickdir()
17441744
np.testing.assert_array_equal(tickdir_expected, tickdir_actual)
1745+
1746+
1747+
@image_comparison(baseline_images=['arc_pathpatch.png'],
1748+
remove_text=True,
1749+
style='default')
1750+
def test_arc_pathpatch():
1751+
ax = plt.subplot(1, 1, 1, projection="3d")
1752+
a = mpatch.Arc((0.5, 0.5), width=0.5, height=0.9,
1753+
angle=20, theta1=10, theta2=130)
1754+
ax.add_patch(a)
1755+
art3d.pathpatch_2d_to_3d(a, z=0, zdir='z')

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.