diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index 3fbb4d2b9cfd..3243c76d8661 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -16,13 +16,18 @@ class PolarTransform(mtransforms.Transform): - """ + r""" The base polar transform. - This transform maps polar coordinates ``(theta, r)`` into Cartesian - coordinates ``(x, y) = (r * cos(theta), r * sin(theta))`` (but does not + This transform maps polar coordinates :math:`\theta, r` into Cartesian + coordinates :math:`x, y = r \cos(\theta), r \sin(\theta)` + (but does not fully transform into Axes coordinates or handle positioning in screen space). + This transformation is designed to be applied to data after any scaling + along the radial axis (e.g. log-scaling) has been applied to the input + data. + Path segments at a fixed radius are automatically transformed to circular arcs as long as ``path._interpolation_steps > 1``. """ @@ -30,7 +35,7 @@ class PolarTransform(mtransforms.Transform): input_dims = output_dims = 2 def __init__(self, axis=None, use_rmin=True, - _apply_theta_transforms=True): + _apply_theta_transforms=True, *, scale_transform=None): """ Parameters ---------- @@ -46,12 +51,18 @@ def __init__(self, axis=None, use_rmin=True, self._axis = axis self._use_rmin = use_rmin self._apply_theta_transforms = _apply_theta_transforms + self._scale_transform = scale_transform __str__ = mtransforms._make_str_method( "_axis", use_rmin="_use_rmin", _apply_theta_transforms="_apply_theta_transforms") + def _get_rorigin(self): + # Get lower r limit after being scaled by the radial scale transform + return self._scale_transform.transform( + (0, self._axis.get_rorigin()))[1] + def transform_non_affine(self, tr): # docstring inherited theta, r = np.transpose(tr) @@ -61,7 +72,7 @@ def transform_non_affine(self, tr): theta *= self._axis.get_theta_direction() theta += self._axis.get_theta_offset() if self._use_rmin and self._axis is not None: - r = (r - self._axis.get_rorigin()) * self._axis.get_rsign() + r = (r - self._get_rorigin()) * self._axis.get_rsign() r = np.where(r >= 0, r, np.nan) return np.column_stack([r * np.cos(theta), r * np.sin(theta)]) @@ -85,7 +96,7 @@ def transform_path_non_affine(self, path): # that behavior here. last_td, td = np.rad2deg([last_t, t]) if self._use_rmin and self._axis is not None: - r = ((r - self._axis.get_rorigin()) + r = ((r - self._get_rorigin()) * self._axis.get_rsign()) if last_td <= td: while td - last_td > 360: @@ -877,7 +888,9 @@ def _set_lim_and_transforms(self): # data. This one is aware of rmin self.transProjection = self.PolarTransform( self, - _apply_theta_transforms=False) + _apply_theta_transforms=False, + scale_transform=self.transScale + ) # Add dependency on rorigin. self.transProjection.set_children(self._originViewLim) @@ -888,9 +901,25 @@ def _set_lim_and_transforms(self): # The complete data transformation stack -- from data all the # way to display coordinates + # + # 1. Remove any radial axis scaling (e.g. log scaling) + # 2. Shift data in the theta direction + # 3. Project the data from polar to cartesian values + # (with the origin in the same place) + # 4. Scale and translate the cartesian values to Axes coordinates + # (here the origin is moved to the lower left of the Axes) + # 5. Move and scale to fill the Axes + # 6. Convert from Axes coordinates to Figure coordinates self.transData = ( - self.transScale + self.transShift + self.transProjection + - (self.transProjectionAffine + self.transWedge + self.transAxes)) + self.transScale + + self.transShift + + self.transProjection + + ( + self.transProjectionAffine + + self.transWedge + + self.transAxes + ) + ) # This is the transform for theta-axis ticks. It is # equivalent to transData, except it always puts r == 0.0 and r == 1.0 diff --git a/lib/matplotlib/tests/baseline_images/test_polar/polar_log.png b/lib/matplotlib/tests/baseline_images/test_polar/polar_log.png new file mode 100644 index 000000000000..ab8e20b482a5 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_polar/polar_log.png differ diff --git a/lib/matplotlib/tests/test_polar.py b/lib/matplotlib/tests/test_polar.py index 1bde5bcc1142..1f8e6a75baca 100644 --- a/lib/matplotlib/tests/test_polar.py +++ b/lib/matplotlib/tests/test_polar.py @@ -434,3 +434,15 @@ def test_cursor_precision(): assert ax.format_coord(2, 0) == "θ=0.6π (115°), r=0.000" assert ax.format_coord(2, .1) == "θ=0.64π (115°), r=0.100" assert ax.format_coord(2, 1) == "θ=0.637π (114.6°), r=1.000" + + +@image_comparison(['polar_log.png'], style='default') +def test_polar_log(): + fig = plt.figure() + ax = fig.add_subplot(polar=True) + + ax.set_rscale('log') + ax.set_rlim(1, 1000) + + n = 100 + ax.plot(np.linspace(0, 2 * np.pi, n), np.logspace(0, 2, n))
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: