Skip to content

Commit bd946aa

Browse files
committed
Address review: simplify arc docstring/comments and trim polar tests
- Drop the contrast and GitHub issue references from the Path.arc docstring; just state that a complete circle is drawn. - Remove issue references and reword the test_path.py comments for a reader unfamiliar with this PR's history. - Make test_arc_full_circle_snap compare all vertices against the reference circle, consistent with (and slightly stricter than) the neighbouring test. - Remove test_polar_transform_constant_r_arc (it passes against main and no longer guards the fix) and merge the two outer-spine tests into a single test_polar_outer_spine_not_collapsed using the spine bbox.
1 parent 26c160c commit bd946aa

3 files changed

Lines changed: 24 additions & 59 deletions

File tree

lib/matplotlib/path.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -980,8 +980,7 @@ def arc(cls, theta1, theta2, n=None, is_wedge=False):
980980
981981
As a special case, if the span *theta2* - *theta1* is within
982982
floating-point tolerance of a whole number of turns, a complete circle
983-
is drawn rather than collapsing a delta of 360*n plus a tiny rounding
984-
error to a near-empty arc (matplotlib issues #20388 and #26972).
983+
is drawn.
985984
986985
If *n* is provided, it is the number of spline segments to make.
987986
If *n* is not provided, the number of spline segments is

lib/matplotlib/tests/test_path.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -519,20 +519,15 @@ def test_full_arc(offset):
519519
])
520520
def test_arc_full_circle_snap(theta2):
521521
# A span within floating-point tolerance of a whole number of turns must
522-
# draw a complete circle, not collapse to a near-empty arc. This is the
523-
# floating-point edge case behind gh-20388 and gh-26972.
524-
full = Path.arc(0, 360)
525-
snapped = Path.arc(0, theta2)
526-
assert len(snapped.vertices) == len(full.vertices)
527-
np.testing.assert_allclose(np.min(snapped.vertices, axis=0), -1, atol=1e-12)
528-
np.testing.assert_allclose(np.max(snapped.vertices, axis=0), 1, atol=1e-12)
522+
# draw a complete circle, not collapse to a near-empty arc.
523+
np.testing.assert_allclose(Path.arc(0, theta2).vertices,
524+
Path.arc(0, 360).vertices)
529525

530526

531527
@pytest.mark.parametrize('theta1, theta2', [(0, -360), (0, -720), (360, 0),
532528
(10, -350)])
533529
def test_arc_negative_full_circle(theta1, theta2):
534-
# An exact negative multiple of 360 must still draw a complete circle,
535-
# matching the legacy behaviour (regression guard for the unwrap rework).
530+
# An exact negative multiple of 360 must draw a complete circle.
536531
# The result is the same complete circle as the equivalent positive turn
537532
# starting from *theta1* (so the assertion holds for non-cardinal starts).
538533
np.testing.assert_allclose(Path.arc(theta1, theta2).vertices,
@@ -541,7 +536,7 @@ def test_arc_negative_full_circle(theta1, theta2):
541536

542537
def test_arc_unwrap_partial_turn():
543538
# A span comfortably more than a whole number of turns (not near-integer)
544-
# is still unwrapped to the equivalent shortest arc within 360 degrees.
539+
# is unwrapped to the equivalent shortest arc within 360 degrees.
545540
np.testing.assert_allclose(Path.arc(0, 410).vertices,
546541
Path.arc(0, 50).vertices)
547542
np.testing.assert_allclose(Path.arc(0, 540).vertices,

lib/matplotlib/tests/test_polar.py

Lines changed: 18 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import pytest
66

77
import matplotlib as mpl
8-
from matplotlib.path import Path
98
from matplotlib.projections.polar import RadialLocator
109
from matplotlib import pyplot as plt
1110
from matplotlib.testing.decorators import image_comparison, check_figures_equal
@@ -329,54 +328,26 @@ def test_polar_gridlines():
329328
assert ax.yaxis.majorTicks[0].gridline.get_alpha() == .2
330329

331330

332-
@pytest.mark.parametrize('span_deg', [359.999999, 360.0,
333-
360 - 1e-9, 720.0])
334-
def test_polar_transform_constant_r_arc(span_deg):
335-
# PolarTransform's chunking boundary used to disagree with Path.arc's
336-
# angle-unwrap step, so an angular delta of nearly-but-not-exactly
337-
# 360 degrees collapsed to a near-empty arc. Apply the transform to
338-
# a constant-r path of varying angular span and check that the
339-
# result remains a non-degenerate circle.
331+
@pytest.mark.parametrize('theta_zero_location, theta_offset', [
332+
(("N", 20), None),
333+
(("N", 30), None),
334+
(None, 1.570796327),
335+
])
336+
def test_polar_outer_spine_not_collapsed(theta_zero_location, theta_offset):
337+
# The polar outer spine spans a full turn via Path.arc. For some theta
338+
# offsets/directions, floating-point error left the span just short of a
339+
# full 360 degrees and the spine collapsed to a near-empty arc. Check it
340+
# still occupies a sensible area.
340341
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
342+
if theta_zero_location is not None:
343+
ax.set_theta_direction(-1)
344+
ax.set_theta_zero_location(*theta_zero_location)
345+
if theta_offset is not None:
346+
ax.set_theta_offset(theta_offset)
341347
fig.canvas.draw()
342-
r = 1.0
343-
path = Path([(0.0, r), (np.deg2rad(span_deg), r)],
344-
[Path.MOVETO, Path.LINETO])
345-
path._interpolation_steps = 100
346-
out = ax.transProjection.transform_path_non_affine(path)
347-
spread = np.ptp(out.vertices, axis=0)
348-
assert spread.min() > r * 1.5, (
349-
f"transformed arc collapsed for span={span_deg}: spread={spread}")
350-
351-
352-
@pytest.mark.parametrize('angle', [10, 20, 30, 45, 90, 110])
353-
def test_polar_inverted_theta_outer_spine(angle):
354-
# With set_theta_direction(-1) and certain values of
355-
# set_theta_zero_location offset, the polar outer spine used to
356-
# collapse to a near-empty arc.
357-
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
358-
ax.set_theta_direction(-1)
359-
ax.set_theta_zero_location("N", angle)
360-
fig.canvas.draw()
361-
spine = ax.spines['polar']
362-
tpath = spine.get_transform().transform_path(spine.get_path())
363-
spread = np.ptp(tpath.vertices, axis=0)
364-
assert spread.min() > 50, (
365-
f"outer spine collapsed for angle={angle}: spread={spread}")
366-
367-
368-
@pytest.mark.parametrize('offset', [1.0, np.pi / 2, np.pi, 1.570796327])
369-
def test_polar_theta_offset_outer_spine(offset):
370-
# set_theta_offset used to remove the outer spine outline; same
371-
# floating-point root cause as the inverted-theta case above.
372-
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
373-
ax.set_theta_offset(offset)
374-
fig.canvas.draw()
375-
spine = ax.spines['polar']
376-
tpath = spine.get_transform().transform_path(spine.get_path())
377-
spread = np.ptp(tpath.vertices, axis=0)
378-
assert spread.min() > 50, (
379-
f"outer spine collapsed for theta_offset={offset}: spread={spread}")
348+
# A collapsed spine has a near-zero (~1e-13) bounding box; a healthy one is
349+
# hundreds of points across.
350+
assert ax.spines['polar'].get_tightbbox().size.min() > 1
380351

381352

382353
def test_get_tightbbox_polar():

0 commit comments

Comments
 (0)