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 bb6e1aa

Browse filesBrowse files
authored
Fix polar error bar cap orientation (#28966)
1 parent 9489b93 commit bb6e1aa
Copy full SHA for bb6e1aa

File tree

Expand file treeCollapse file tree

6 files changed

+72
-3
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+72
-3
lines changed

‎lib/matplotlib/axes/_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axes/_axes.py
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
_AxesBase, _TransformedBoundsLocator, _process_plot_format)
3737
from matplotlib.axes._secondary_axes import SecondaryAxis
3838
from matplotlib.container import BarContainer, ErrorbarContainer, StemContainer
39+
from matplotlib.transforms import _ScaledRotation
3940

4041
_log = logging.getLogger(__name__)
4142

@@ -3783,13 +3784,14 @@ def apply_mask(arrays, mask):
37833784
caplines[dep_axis].append(mlines.Line2D(
37843785
x_masked, y_masked, marker=marker, **eb_cap_style))
37853786
if self.name == 'polar':
3787+
trans_shift = self.transShift
37863788
for axis in caplines:
37873789
for l in caplines[axis]:
37883790
# Rotate caps to be perpendicular to the error bars
37893791
for theta, r in zip(l.get_xdata(), l.get_ydata()):
3790-
rotation = mtransforms.Affine2D().rotate(theta)
3792+
rotation = _ScaledRotation(theta=theta, trans_shift=trans_shift)
37913793
if axis == 'y':
3792-
rotation.rotate(-np.pi / 2)
3794+
rotation += mtransforms.Affine2D().rotate(np.pi / 2)
37933795
ms = mmarkers.MarkerStyle(marker=marker,
37943796
transform=rotation)
37953797
self.add_line(mlines.Line2D([theta], [r], marker=ms,
Loading

‎lib/matplotlib/tests/test_polar.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_polar.py
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,3 +481,21 @@ def test_polar_neg_theta_lims():
481481
ax.set_thetalim(-np.pi, np.pi)
482482
labels = [l.get_text() for l in ax.xaxis.get_ticklabels()]
483483
assert labels == ['-180°', '-135°', '-90°', '-45°', '0°', '45°', '90°', '135°']
484+
485+
486+
@pytest.mark.parametrize("order", ["before", "after"])
487+
@image_comparison(baseline_images=['polar_errorbar'], remove_text=True,
488+
extensions=['png'], style='mpl20')
489+
def test_polar_errorbar(order):
490+
theta = np.arange(0, 2 * np.pi, np.pi / 8)
491+
r = theta / np.pi / 2 + 0.5
492+
fig = plt.figure(figsize=(5, 5))
493+
ax = fig.add_subplot(projection='polar')
494+
if order == "before":
495+
ax.set_theta_zero_location("N")
496+
ax.set_theta_direction(-1)
497+
ax.errorbar(theta, r, xerr=0.1, yerr=0.1, capsize=7, fmt="o", c="seagreen")
498+
else:
499+
ax.errorbar(theta, r, xerr=0.1, yerr=0.1, capsize=7, fmt="o", c="seagreen")
500+
ax.set_theta_zero_location("N")
501+
ax.set_theta_direction(-1)

‎lib/matplotlib/tests/test_transforms.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_transforms.py
+26-1Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
import matplotlib.pyplot as plt
1010
import matplotlib.patches as mpatches
1111
import matplotlib.transforms as mtransforms
12-
from matplotlib.transforms import Affine2D, Bbox, TransformedBbox
12+
from matplotlib.transforms import Affine2D, Bbox, TransformedBbox, _ScaledRotation
1313
from matplotlib.path import Path
1414
from matplotlib.testing.decorators import image_comparison, check_figures_equal
15+
from unittest.mock import MagicMock
1516

1617

1718
class TestAffine2D:
@@ -1104,3 +1105,27 @@ def test_interval_contains_open():
11041105
assert not mtransforms.interval_contains_open((0, 1), -1)
11051106
assert not mtransforms.interval_contains_open((0, 1), 2)
11061107
assert mtransforms.interval_contains_open((1, 0), 0.5)
1108+
1109+
1110+
def test_scaledrotation_initialization():
1111+
"""Test that the ScaledRotation object is initialized correctly."""
1112+
theta = 1.0 # Arbitrary theta value for testing
1113+
trans_shift = MagicMock() # Mock the trans_shift transformation
1114+
scaled_rot = _ScaledRotation(theta, trans_shift)
1115+
assert scaled_rot._theta == theta
1116+
assert scaled_rot._trans_shift == trans_shift
1117+
assert scaled_rot._mtx is None
1118+
1119+
1120+
def test_scaledrotation_get_matrix_invalid():
1121+
"""Test get_matrix when the matrix is invalid and needs recalculation."""
1122+
theta = np.pi / 2
1123+
trans_shift = MagicMock(transform=MagicMock(return_value=[[theta, 0]]))
1124+
scaled_rot = _ScaledRotation(theta, trans_shift)
1125+
scaled_rot._invalid = True
1126+
matrix = scaled_rot.get_matrix()
1127+
trans_shift.transform.assert_called_once_with([[theta, 0]])
1128+
expected_rotation = np.array([[0, -1],
1129+
[1, 0]])
1130+
assert matrix is not None
1131+
assert_allclose(matrix[:2, :2], expected_rotation, atol=1e-15)

‎lib/matplotlib/transforms.py

Copy file name to clipboardExpand all lines: lib/matplotlib/transforms.py
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2685,6 +2685,25 @@ def get_matrix(self):
26852685
return self._mtx
26862686

26872687

2688+
class _ScaledRotation(Affine2DBase):
2689+
"""
2690+
A transformation that applies rotation by *theta*, after transform by *trans_shift*.
2691+
"""
2692+
def __init__(self, theta, trans_shift):
2693+
super().__init__()
2694+
self._theta = theta
2695+
self._trans_shift = trans_shift
2696+
self._mtx = None
2697+
2698+
def get_matrix(self):
2699+
if self._invalid:
2700+
transformed_coords = self._trans_shift.transform([[self._theta, 0]])[0]
2701+
adjusted_theta = transformed_coords[0]
2702+
rotation = Affine2D().rotate(adjusted_theta)
2703+
self._mtx = rotation.get_matrix()
2704+
return self._mtx
2705+
2706+
26882707
class AffineDeltaTransform(Affine2DBase):
26892708
r"""
26902709
A transform wrapper for transforming displacements between pairs of points.

‎lib/matplotlib/transforms.pyi

Copy file name to clipboardExpand all lines: lib/matplotlib/transforms.pyi
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,8 @@ def offset_copy(
334334
y: float = ...,
335335
units: Literal["inches", "points", "dots"] = ...,
336336
) -> Transform: ...
337+
338+
339+
class _ScaledRotation(Affine2DBase):
340+
def __init__(self, theta: float, trans_shift: Transform) -> None: ...
341+
def get_matrix(self) -> np.ndarray: ...

0 commit comments

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