Skip to content

Commit ba03951

Browse files
authored
Merge pull request matplotlib#24825 from dstansby/polar-scales
Allow non-default scales on polar axes
2 parents aa426be + a24daae commit ba03951

File tree

3 files changed

+50
-9
lines changed

3 files changed

+50
-9
lines changed

lib/matplotlib/projections/polar.py

+38-9
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,26 @@
1616

1717

1818
class PolarTransform(mtransforms.Transform):
19-
"""
19+
r"""
2020
The base polar transform.
2121
22-
This transform maps polar coordinates ``(theta, r)`` into Cartesian
23-
coordinates ``(x, y) = (r * cos(theta), r * sin(theta))`` (but does not
22+
This transform maps polar coordinates :math:`\theta, r` into Cartesian
23+
coordinates :math:`x, y = r \cos(\theta), r \sin(\theta)`
24+
(but does not fully transform into Axes coordinates or
2425
handle positioning in screen space).
2526
27+
This transformation is designed to be applied to data after any scaling
28+
along the radial axis (e.g. log-scaling) has been applied to the input
29+
data.
30+
2631
Path segments at a fixed radius are automatically transformed to circular
2732
arcs as long as ``path._interpolation_steps > 1``.
2833
"""
2934

3035
input_dims = output_dims = 2
3136

3237
def __init__(self, axis=None, use_rmin=True,
33-
_apply_theta_transforms=True):
38+
_apply_theta_transforms=True, *, scale_transform=None):
3439
"""
3540
Parameters
3641
----------
@@ -46,12 +51,18 @@ def __init__(self, axis=None, use_rmin=True,
4651
self._axis = axis
4752
self._use_rmin = use_rmin
4853
self._apply_theta_transforms = _apply_theta_transforms
54+
self._scale_transform = scale_transform
4955

5056
__str__ = mtransforms._make_str_method(
5157
"_axis",
5258
use_rmin="_use_rmin",
5359
_apply_theta_transforms="_apply_theta_transforms")
5460

61+
def _get_rorigin(self):
62+
# Get lower r limit after being scaled by the radial scale transform
63+
return self._scale_transform.transform(
64+
(0, self._axis.get_rorigin()))[1]
65+
5566
def transform_non_affine(self, tr):
5667
# docstring inherited
5768
theta, r = np.transpose(tr)
@@ -61,7 +72,7 @@ def transform_non_affine(self, tr):
6172
theta *= self._axis.get_theta_direction()
6273
theta += self._axis.get_theta_offset()
6374
if self._use_rmin and self._axis is not None:
64-
r = (r - self._axis.get_rorigin()) * self._axis.get_rsign()
75+
r = (r - self._get_rorigin()) * self._axis.get_rsign()
6576
r = np.where(r >= 0, r, np.nan)
6677
return np.column_stack([r * np.cos(theta), r * np.sin(theta)])
6778

@@ -85,7 +96,7 @@ def transform_path_non_affine(self, path):
8596
# that behavior here.
8697
last_td, td = np.rad2deg([last_t, t])
8798
if self._use_rmin and self._axis is not None:
88-
r = ((r - self._axis.get_rorigin())
99+
r = ((r - self._get_rorigin())
89100
* self._axis.get_rsign())
90101
if last_td <= td:
91102
while td - last_td > 360:
@@ -877,7 +888,9 @@ def _set_lim_and_transforms(self):
877888
# data. This one is aware of rmin
878889
self.transProjection = self.PolarTransform(
879890
self,
880-
_apply_theta_transforms=False)
891+
_apply_theta_transforms=False,
892+
scale_transform=self.transScale
893+
)
881894
# Add dependency on rorigin.
882895
self.transProjection.set_children(self._originViewLim)
883896

@@ -888,9 +901,25 @@ def _set_lim_and_transforms(self):
888901

889902
# The complete data transformation stack -- from data all the
890903
# way to display coordinates
904+
#
905+
# 1. Remove any radial axis scaling (e.g. log scaling)
906+
# 2. Shift data in the theta direction
907+
# 3. Project the data from polar to cartesian values
908+
# (with the origin in the same place)
909+
# 4. Scale and translate the cartesian values to Axes coordinates
910+
# (here the origin is moved to the lower left of the Axes)
911+
# 5. Move and scale to fill the Axes
912+
# 6. Convert from Axes coordinates to Figure coordinates
891913
self.transData = (
892-
self.transScale + self.transShift + self.transProjection +
893-
(self.transProjectionAffine + self.transWedge + self.transAxes))
914+
self.transScale +
915+
self.transShift +
916+
self.transProjection +
917+
(
918+
self.transProjectionAffine +
919+
self.transWedge +
920+
self.transAxes
921+
)
922+
)
894923

895924
# This is the transform for theta-axis ticks. It is
896925
# equivalent to transData, except it always puts r == 0.0 and r == 1.0
Loading

lib/matplotlib/tests/test_polar.py

+12
Original file line numberDiff line numberDiff line change
@@ -434,3 +434,15 @@ def test_cursor_precision():
434434
assert ax.format_coord(2, 0) == "θ=0.6π (115°), r=0.000"
435435
assert ax.format_coord(2, .1) == "θ=0.64π (115°), r=0.100"
436436
assert ax.format_coord(2, 1) == "θ=0.637π (114.6°), r=1.000"
437+
438+
439+
@image_comparison(['polar_log.png'], style='default')
440+
def test_polar_log():
441+
fig = plt.figure()
442+
ax = fig.add_subplot(polar=True)
443+
444+
ax.set_rscale('log')
445+
ax.set_rlim(1, 1000)
446+
447+
n = 100
448+
ax.plot(np.linspace(0, 2 * np.pi, n), np.logspace(0, 2, n))

0 commit comments

Comments
 (0)