You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			1564 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			1564 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			Python
		
	
import math
 | 
						|
import types
 | 
						|
 | 
						|
import numpy as np
 | 
						|
 | 
						|
import matplotlib as mpl
 | 
						|
from matplotlib import _api, cbook
 | 
						|
from matplotlib.axes import Axes
 | 
						|
import matplotlib.axis as maxis
 | 
						|
import matplotlib.markers as mmarkers
 | 
						|
import matplotlib.patches as mpatches
 | 
						|
from matplotlib.path import Path
 | 
						|
import matplotlib.ticker as mticker
 | 
						|
import matplotlib.transforms as mtransforms
 | 
						|
from matplotlib.spines import Spine
 | 
						|
 | 
						|
 | 
						|
def _apply_theta_transforms_warn():
 | 
						|
    _api.warn_deprecated(
 | 
						|
                "3.9",
 | 
						|
                message=(
 | 
						|
                    "Passing `apply_theta_transforms=True` (the default) "
 | 
						|
                    "is deprecated since Matplotlib %(since)s. "
 | 
						|
                    "Support for this will be removed in Matplotlib in %(removal)s. "
 | 
						|
                    "To prevent this warning, set `apply_theta_transforms=False`, "
 | 
						|
                    "and make sure to shift theta values before being passed to "
 | 
						|
                    "this transform."
 | 
						|
                )
 | 
						|
            )
 | 
						|
 | 
						|
 | 
						|
class PolarTransform(mtransforms.Transform):
 | 
						|
    r"""
 | 
						|
    The base polar transform.
 | 
						|
 | 
						|
    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``.
 | 
						|
    """
 | 
						|
 | 
						|
    input_dims = output_dims = 2
 | 
						|
 | 
						|
    def __init__(self, axis=None, use_rmin=True, *,
 | 
						|
                 apply_theta_transforms=True, scale_transform=None):
 | 
						|
        """
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        axis : `~matplotlib.axis.Axis`, optional
 | 
						|
            Axis associated with this transform. This is used to get the
 | 
						|
            minimum radial limit.
 | 
						|
        use_rmin : `bool`, optional
 | 
						|
            If ``True``, subtract the minimum radial axis limit before
 | 
						|
            transforming to Cartesian coordinates. *axis* must also be
 | 
						|
            specified for this to take effect.
 | 
						|
        """
 | 
						|
        super().__init__()
 | 
						|
        self._axis = axis
 | 
						|
        self._use_rmin = use_rmin
 | 
						|
        self._apply_theta_transforms = apply_theta_transforms
 | 
						|
        self._scale_transform = scale_transform
 | 
						|
        if apply_theta_transforms:
 | 
						|
            _apply_theta_transforms_warn()
 | 
						|
 | 
						|
    __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, values):
 | 
						|
        # docstring inherited
 | 
						|
        theta, r = np.transpose(values)
 | 
						|
        # PolarAxes does not use the theta transforms here, but apply them for
 | 
						|
        # backwards-compatibility if not being used by it.
 | 
						|
        if self._apply_theta_transforms and self._axis is not None:
 | 
						|
            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._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)])
 | 
						|
 | 
						|
    def transform_path_non_affine(self, path):
 | 
						|
        # docstring inherited
 | 
						|
        if not len(path) or path._interpolation_steps == 1:
 | 
						|
            return Path(self.transform_non_affine(path.vertices), path.codes)
 | 
						|
        xys = []
 | 
						|
        codes = []
 | 
						|
        last_t = last_r = None
 | 
						|
        for trs, c in path.iter_segments():
 | 
						|
            trs = trs.reshape((-1, 2))
 | 
						|
            if c == Path.LINETO:
 | 
						|
                (t, r), = trs
 | 
						|
                if t == last_t:  # Same angle: draw a straight line.
 | 
						|
                    xys.extend(self.transform_non_affine(trs))
 | 
						|
                    codes.append(Path.LINETO)
 | 
						|
                elif r == last_r:  # Same radius: draw an arc.
 | 
						|
                    # The following is complicated by Path.arc() being
 | 
						|
                    # "helpful" and unwrapping the angles, but we don't want
 | 
						|
                    # that behavior here.
 | 
						|
                    last_td, td = np.rad2deg([last_t, t])
 | 
						|
                    if self._use_rmin and self._axis is not None:
 | 
						|
                        r = ((r - self._get_rorigin())
 | 
						|
                             * self._axis.get_rsign())
 | 
						|
                    if last_td <= td:
 | 
						|
                        while td - last_td > 360:
 | 
						|
                            arc = Path.arc(last_td, last_td + 360)
 | 
						|
                            xys.extend(arc.vertices[1:] * r)
 | 
						|
                            codes.extend(arc.codes[1:])
 | 
						|
                            last_td += 360
 | 
						|
                        arc = Path.arc(last_td, td)
 | 
						|
                        xys.extend(arc.vertices[1:] * r)
 | 
						|
                        codes.extend(arc.codes[1:])
 | 
						|
                    else:
 | 
						|
                        # The reverse version also relies on the fact that all
 | 
						|
                        # codes but the first one are the same.
 | 
						|
                        while last_td - td > 360:
 | 
						|
                            arc = Path.arc(last_td - 360, last_td)
 | 
						|
                            xys.extend(arc.vertices[::-1][1:] * r)
 | 
						|
                            codes.extend(arc.codes[1:])
 | 
						|
                            last_td -= 360
 | 
						|
                        arc = Path.arc(td, last_td)
 | 
						|
                        xys.extend(arc.vertices[::-1][1:] * r)
 | 
						|
                        codes.extend(arc.codes[1:])
 | 
						|
                else:  # Interpolate.
 | 
						|
                    trs = cbook.simple_linear_interpolation(
 | 
						|
                        np.vstack([(last_t, last_r), trs]),
 | 
						|
                        path._interpolation_steps)[1:]
 | 
						|
                    xys.extend(self.transform_non_affine(trs))
 | 
						|
                    codes.extend([Path.LINETO] * len(trs))
 | 
						|
            else:  # Not a straight line.
 | 
						|
                xys.extend(self.transform_non_affine(trs))
 | 
						|
                codes.extend([c] * len(trs))
 | 
						|
            last_t, last_r = trs[-1]
 | 
						|
        return Path(xys, codes)
 | 
						|
 | 
						|
    def inverted(self):
 | 
						|
        # docstring inherited
 | 
						|
        return PolarAxes.InvertedPolarTransform(
 | 
						|
            self._axis, self._use_rmin,
 | 
						|
            apply_theta_transforms=self._apply_theta_transforms
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
class PolarAffine(mtransforms.Affine2DBase):
 | 
						|
    r"""
 | 
						|
    The affine part of the polar projection.
 | 
						|
 | 
						|
    Scales the output so that maximum radius rests on the edge of the Axes
 | 
						|
    circle and the origin is mapped to (0.5, 0.5). The transform applied is
 | 
						|
    the same to x and y components and given by:
 | 
						|
 | 
						|
    .. math::
 | 
						|
 | 
						|
        x_{1} = 0.5 \left [ \frac{x_{0}}{(r_{\max} - r_{\min})} + 1 \right ]
 | 
						|
 | 
						|
    :math:`r_{\min}, r_{\max}` are the minimum and maximum radial limits after
 | 
						|
    any scaling (e.g. log scaling) has been removed.
 | 
						|
    """
 | 
						|
    def __init__(self, scale_transform, limits):
 | 
						|
        """
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        scale_transform : `~matplotlib.transforms.Transform`
 | 
						|
            Scaling transform for the data. This is used to remove any scaling
 | 
						|
            from the radial view limits.
 | 
						|
        limits : `~matplotlib.transforms.BboxBase`
 | 
						|
            View limits of the data. The only part of its bounds that is used
 | 
						|
            is the y limits (for the radius limits).
 | 
						|
        """
 | 
						|
        super().__init__()
 | 
						|
        self._scale_transform = scale_transform
 | 
						|
        self._limits = limits
 | 
						|
        self.set_children(scale_transform, limits)
 | 
						|
        self._mtx = None
 | 
						|
 | 
						|
    __str__ = mtransforms._make_str_method("_scale_transform", "_limits")
 | 
						|
 | 
						|
    def get_matrix(self):
 | 
						|
        # docstring inherited
 | 
						|
        if self._invalid:
 | 
						|
            limits_scaled = self._limits.transformed(self._scale_transform)
 | 
						|
            yscale = limits_scaled.ymax - limits_scaled.ymin
 | 
						|
            affine = mtransforms.Affine2D() \
 | 
						|
                .scale(0.5 / yscale) \
 | 
						|
                .translate(0.5, 0.5)
 | 
						|
            self._mtx = affine.get_matrix()
 | 
						|
            self._inverted = None
 | 
						|
            self._invalid = 0
 | 
						|
        return self._mtx
 | 
						|
 | 
						|
 | 
						|
class InvertedPolarTransform(mtransforms.Transform):
 | 
						|
    """
 | 
						|
    The inverse of the polar transform, mapping Cartesian
 | 
						|
    coordinate space *x* and *y* back to *theta* and *r*.
 | 
						|
    """
 | 
						|
    input_dims = output_dims = 2
 | 
						|
 | 
						|
    def __init__(self, axis=None, use_rmin=True,
 | 
						|
                 *, apply_theta_transforms=True):
 | 
						|
        """
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        axis : `~matplotlib.axis.Axis`, optional
 | 
						|
            Axis associated with this transform. This is used to get the
 | 
						|
            minimum radial limit.
 | 
						|
        use_rmin : `bool`, optional
 | 
						|
            If ``True``, add the minimum radial axis limit after
 | 
						|
            transforming from Cartesian coordinates. *axis* must also be
 | 
						|
            specified for this to take effect.
 | 
						|
        """
 | 
						|
        super().__init__()
 | 
						|
        self._axis = axis
 | 
						|
        self._use_rmin = use_rmin
 | 
						|
        self._apply_theta_transforms = apply_theta_transforms
 | 
						|
        if apply_theta_transforms:
 | 
						|
            _apply_theta_transforms_warn()
 | 
						|
 | 
						|
    __str__ = mtransforms._make_str_method(
 | 
						|
        "_axis",
 | 
						|
        use_rmin="_use_rmin",
 | 
						|
        apply_theta_transforms="_apply_theta_transforms")
 | 
						|
 | 
						|
    def transform_non_affine(self, values):
 | 
						|
        # docstring inherited
 | 
						|
        x, y = values.T
 | 
						|
        r = np.hypot(x, y)
 | 
						|
        theta = (np.arctan2(y, x) + 2 * np.pi) % (2 * np.pi)
 | 
						|
        # PolarAxes does not use the theta transforms here, but apply them for
 | 
						|
        # backwards-compatibility if not being used by it.
 | 
						|
        if self._apply_theta_transforms and self._axis is not None:
 | 
						|
            theta -= self._axis.get_theta_offset()
 | 
						|
            theta *= self._axis.get_theta_direction()
 | 
						|
            theta %= 2 * np.pi
 | 
						|
        if self._use_rmin and self._axis is not None:
 | 
						|
            r += self._axis.get_rorigin()
 | 
						|
            r *= self._axis.get_rsign()
 | 
						|
        return np.column_stack([theta, r])
 | 
						|
 | 
						|
    def inverted(self):
 | 
						|
        # docstring inherited
 | 
						|
        return PolarAxes.PolarTransform(
 | 
						|
            self._axis, self._use_rmin,
 | 
						|
            apply_theta_transforms=self._apply_theta_transforms
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
class ThetaFormatter(mticker.Formatter):
 | 
						|
    """
 | 
						|
    Used to format the *theta* tick labels.  Converts the native
 | 
						|
    unit of radians into degrees and adds a degree symbol.
 | 
						|
    """
 | 
						|
 | 
						|
    def __call__(self, x, pos=None):
 | 
						|
        vmin, vmax = self.axis.get_view_interval()
 | 
						|
        d = np.rad2deg(abs(vmax - vmin))
 | 
						|
        digits = max(-int(np.log10(d) - 1.5), 0)
 | 
						|
        return f"{np.rad2deg(x):0.{digits}f}\N{DEGREE SIGN}"
 | 
						|
 | 
						|
 | 
						|
class _AxisWrapper:
 | 
						|
    def __init__(self, axis):
 | 
						|
        self._axis = axis
 | 
						|
 | 
						|
    def get_view_interval(self):
 | 
						|
        return np.rad2deg(self._axis.get_view_interval())
 | 
						|
 | 
						|
    def set_view_interval(self, vmin, vmax):
 | 
						|
        self._axis.set_view_interval(*np.deg2rad((vmin, vmax)))
 | 
						|
 | 
						|
    def get_minpos(self):
 | 
						|
        return np.rad2deg(self._axis.get_minpos())
 | 
						|
 | 
						|
    def get_data_interval(self):
 | 
						|
        return np.rad2deg(self._axis.get_data_interval())
 | 
						|
 | 
						|
    def set_data_interval(self, vmin, vmax):
 | 
						|
        self._axis.set_data_interval(*np.deg2rad((vmin, vmax)))
 | 
						|
 | 
						|
    def get_tick_space(self):
 | 
						|
        return self._axis.get_tick_space()
 | 
						|
 | 
						|
 | 
						|
class ThetaLocator(mticker.Locator):
 | 
						|
    """
 | 
						|
    Used to locate theta ticks.
 | 
						|
 | 
						|
    This will work the same as the base locator except in the case that the
 | 
						|
    view spans the entire circle. In such cases, the previously used default
 | 
						|
    locations of every 45 degrees are returned.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, base):
 | 
						|
        self.base = base
 | 
						|
        self.axis = self.base.axis = _AxisWrapper(self.base.axis)
 | 
						|
 | 
						|
    def set_axis(self, axis):
 | 
						|
        self.axis = _AxisWrapper(axis)
 | 
						|
        self.base.set_axis(self.axis)
 | 
						|
 | 
						|
    def __call__(self):
 | 
						|
        lim = self.axis.get_view_interval()
 | 
						|
        if _is_full_circle_deg(lim[0], lim[1]):
 | 
						|
            return np.deg2rad(min(lim)) + np.arange(8) * 2 * np.pi / 8
 | 
						|
        else:
 | 
						|
            return np.deg2rad(self.base())
 | 
						|
 | 
						|
    def view_limits(self, vmin, vmax):
 | 
						|
        vmin, vmax = np.rad2deg((vmin, vmax))
 | 
						|
        return np.deg2rad(self.base.view_limits(vmin, vmax))
 | 
						|
 | 
						|
 | 
						|
class ThetaTick(maxis.XTick):
 | 
						|
    """
 | 
						|
    A theta-axis tick.
 | 
						|
 | 
						|
    This subclass of `.XTick` provides angular ticks with some small
 | 
						|
    modification to their re-positioning such that ticks are rotated based on
 | 
						|
    tick location. This results in ticks that are correctly perpendicular to
 | 
						|
    the arc spine.
 | 
						|
 | 
						|
    When 'auto' rotation is enabled, labels are also rotated to be parallel to
 | 
						|
    the spine. The label padding is also applied here since it's not possible
 | 
						|
    to use a generic axes transform to produce tick-specific padding.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, axes, *args, **kwargs):
 | 
						|
        self._text1_translate = mtransforms.ScaledTranslation(
 | 
						|
            0, 0, axes.get_figure(root=False).dpi_scale_trans)
 | 
						|
        self._text2_translate = mtransforms.ScaledTranslation(
 | 
						|
            0, 0, axes.get_figure(root=False).dpi_scale_trans)
 | 
						|
        super().__init__(axes, *args, **kwargs)
 | 
						|
        self.label1.set(
 | 
						|
            rotation_mode='anchor',
 | 
						|
            transform=self.label1.get_transform() + self._text1_translate)
 | 
						|
        self.label2.set(
 | 
						|
            rotation_mode='anchor',
 | 
						|
            transform=self.label2.get_transform() + self._text2_translate)
 | 
						|
 | 
						|
    def _apply_params(self, **kwargs):
 | 
						|
        super()._apply_params(**kwargs)
 | 
						|
        # Ensure transform is correct; sometimes this gets reset.
 | 
						|
        trans = self.label1.get_transform()
 | 
						|
        if not trans.contains_branch(self._text1_translate):
 | 
						|
            self.label1.set_transform(trans + self._text1_translate)
 | 
						|
        trans = self.label2.get_transform()
 | 
						|
        if not trans.contains_branch(self._text2_translate):
 | 
						|
            self.label2.set_transform(trans + self._text2_translate)
 | 
						|
 | 
						|
    def _update_padding(self, pad, angle):
 | 
						|
        padx = pad * np.cos(angle) / 72
 | 
						|
        pady = pad * np.sin(angle) / 72
 | 
						|
        self._text1_translate._t = (padx, pady)
 | 
						|
        self._text1_translate.invalidate()
 | 
						|
        self._text2_translate._t = (-padx, -pady)
 | 
						|
        self._text2_translate.invalidate()
 | 
						|
 | 
						|
    def update_position(self, loc):
 | 
						|
        super().update_position(loc)
 | 
						|
        axes = self.axes
 | 
						|
        angle = loc * axes.get_theta_direction() + axes.get_theta_offset()
 | 
						|
        text_angle = np.rad2deg(angle) % 360 - 90
 | 
						|
        angle -= np.pi / 2
 | 
						|
 | 
						|
        marker = self.tick1line.get_marker()
 | 
						|
        if marker in (mmarkers.TICKUP, '|'):
 | 
						|
            trans = mtransforms.Affine2D().scale(1, 1).rotate(angle)
 | 
						|
        elif marker == mmarkers.TICKDOWN:
 | 
						|
            trans = mtransforms.Affine2D().scale(1, -1).rotate(angle)
 | 
						|
        else:
 | 
						|
            # Don't modify custom tick line markers.
 | 
						|
            trans = self.tick1line._marker._transform
 | 
						|
        self.tick1line._marker._transform = trans
 | 
						|
 | 
						|
        marker = self.tick2line.get_marker()
 | 
						|
        if marker in (mmarkers.TICKUP, '|'):
 | 
						|
            trans = mtransforms.Affine2D().scale(1, 1).rotate(angle)
 | 
						|
        elif marker == mmarkers.TICKDOWN:
 | 
						|
            trans = mtransforms.Affine2D().scale(1, -1).rotate(angle)
 | 
						|
        else:
 | 
						|
            # Don't modify custom tick line markers.
 | 
						|
            trans = self.tick2line._marker._transform
 | 
						|
        self.tick2line._marker._transform = trans
 | 
						|
 | 
						|
        mode, user_angle = self._labelrotation
 | 
						|
        if mode == 'default':
 | 
						|
            text_angle = user_angle
 | 
						|
        else:
 | 
						|
            if text_angle > 90:
 | 
						|
                text_angle -= 180
 | 
						|
            elif text_angle < -90:
 | 
						|
                text_angle += 180
 | 
						|
            text_angle += user_angle
 | 
						|
        self.label1.set_rotation(text_angle)
 | 
						|
        self.label2.set_rotation(text_angle)
 | 
						|
 | 
						|
        # This extra padding helps preserve the look from previous releases but
 | 
						|
        # is also needed because labels are anchored to their center.
 | 
						|
        pad = self._pad + 7
 | 
						|
        self._update_padding(pad,
 | 
						|
                             self._loc * axes.get_theta_direction() +
 | 
						|
                             axes.get_theta_offset())
 | 
						|
 | 
						|
 | 
						|
class ThetaAxis(maxis.XAxis):
 | 
						|
    """
 | 
						|
    A theta Axis.
 | 
						|
 | 
						|
    This overrides certain properties of an `.XAxis` to provide special-casing
 | 
						|
    for an angular axis.
 | 
						|
    """
 | 
						|
    __name__ = 'thetaaxis'
 | 
						|
    axis_name = 'theta'  #: Read-only name identifying the axis.
 | 
						|
    _tick_class = ThetaTick
 | 
						|
 | 
						|
    def _wrap_locator_formatter(self):
 | 
						|
        self.set_major_locator(ThetaLocator(self.get_major_locator()))
 | 
						|
        self.set_major_formatter(ThetaFormatter())
 | 
						|
        self.isDefault_majloc = True
 | 
						|
        self.isDefault_majfmt = True
 | 
						|
 | 
						|
    def clear(self):
 | 
						|
        # docstring inherited
 | 
						|
        super().clear()
 | 
						|
        self.set_ticks_position('none')
 | 
						|
        self._wrap_locator_formatter()
 | 
						|
 | 
						|
    def _set_scale(self, value, **kwargs):
 | 
						|
        if value != 'linear':
 | 
						|
            raise NotImplementedError(
 | 
						|
                "The xscale cannot be set on a polar plot")
 | 
						|
        super()._set_scale(value, **kwargs)
 | 
						|
        # LinearScale.set_default_locators_and_formatters just set the major
 | 
						|
        # locator to be an AutoLocator, so we customize it here to have ticks
 | 
						|
        # at sensible degree multiples.
 | 
						|
        self.get_major_locator().set_params(steps=[1, 1.5, 3, 4.5, 9, 10])
 | 
						|
        self._wrap_locator_formatter()
 | 
						|
 | 
						|
    def _copy_tick_props(self, src, dest):
 | 
						|
        """Copy the props from src tick to dest tick."""
 | 
						|
        if src is None or dest is None:
 | 
						|
            return
 | 
						|
        super()._copy_tick_props(src, dest)
 | 
						|
 | 
						|
        # Ensure that tick transforms are independent so that padding works.
 | 
						|
        trans = dest._get_text1_transform()[0]
 | 
						|
        dest.label1.set_transform(trans + dest._text1_translate)
 | 
						|
        trans = dest._get_text2_transform()[0]
 | 
						|
        dest.label2.set_transform(trans + dest._text2_translate)
 | 
						|
 | 
						|
 | 
						|
class RadialLocator(mticker.Locator):
 | 
						|
    """
 | 
						|
    Used to locate radius ticks.
 | 
						|
 | 
						|
    Ensures that all ticks are strictly positive.  For all other tasks, it
 | 
						|
    delegates to the base `.Locator` (which may be different depending on the
 | 
						|
    scale of the *r*-axis).
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, base, axes=None):
 | 
						|
        self.base = base
 | 
						|
        self._axes = axes
 | 
						|
 | 
						|
    def set_axis(self, axis):
 | 
						|
        self.base.set_axis(axis)
 | 
						|
 | 
						|
    def __call__(self):
 | 
						|
        # Ensure previous behaviour with full circle non-annular views.
 | 
						|
        if self._axes:
 | 
						|
            if _is_full_circle_rad(*self._axes.viewLim.intervalx):
 | 
						|
                rorigin = self._axes.get_rorigin() * self._axes.get_rsign()
 | 
						|
                if self._axes.get_rmin() <= rorigin:
 | 
						|
                    return [tick for tick in self.base() if tick > rorigin]
 | 
						|
        return self.base()
 | 
						|
 | 
						|
    def _zero_in_bounds(self):
 | 
						|
        """
 | 
						|
        Return True if zero is within the valid values for the
 | 
						|
        scale of the radial axis.
 | 
						|
        """
 | 
						|
        vmin, vmax = self._axes.yaxis._scale.limit_range_for_scale(0, 1, 1e-5)
 | 
						|
        return vmin == 0
 | 
						|
 | 
						|
    def nonsingular(self, vmin, vmax):
 | 
						|
        # docstring inherited
 | 
						|
        if self._zero_in_bounds() and (vmin, vmax) == (-np.inf, np.inf):
 | 
						|
            # Initial view limits
 | 
						|
            return (0, 1)
 | 
						|
        else:
 | 
						|
            return self.base.nonsingular(vmin, vmax)
 | 
						|
 | 
						|
    def view_limits(self, vmin, vmax):
 | 
						|
        vmin, vmax = self.base.view_limits(vmin, vmax)
 | 
						|
        if self._zero_in_bounds() and vmax > vmin:
 | 
						|
            # this allows inverted r/y-lims
 | 
						|
            vmin = min(0, vmin)
 | 
						|
        return mtransforms.nonsingular(vmin, vmax)
 | 
						|
 | 
						|
 | 
						|
class _ThetaShift(mtransforms.ScaledTranslation):
 | 
						|
    """
 | 
						|
    Apply a padding shift based on axes theta limits.
 | 
						|
 | 
						|
    This is used to create padding for radial ticks.
 | 
						|
 | 
						|
    Parameters
 | 
						|
    ----------
 | 
						|
    axes : `~matplotlib.axes.Axes`
 | 
						|
        The owning Axes; used to determine limits.
 | 
						|
    pad : float
 | 
						|
        The padding to apply, in points.
 | 
						|
    mode : {'min', 'max', 'rlabel'}
 | 
						|
        Whether to shift away from the start (``'min'``) or the end (``'max'``)
 | 
						|
        of the axes, or using the rlabel position (``'rlabel'``).
 | 
						|
    """
 | 
						|
    def __init__(self, axes, pad, mode):
 | 
						|
        super().__init__(pad, pad, axes.get_figure(root=False).dpi_scale_trans)
 | 
						|
        self.set_children(axes._realViewLim)
 | 
						|
        self.axes = axes
 | 
						|
        self.mode = mode
 | 
						|
        self.pad = pad
 | 
						|
 | 
						|
    __str__ = mtransforms._make_str_method("axes", "pad", "mode")
 | 
						|
 | 
						|
    def get_matrix(self):
 | 
						|
        if self._invalid:
 | 
						|
            if self.mode == 'rlabel':
 | 
						|
                angle = (
 | 
						|
                    np.deg2rad(self.axes.get_rlabel_position()
 | 
						|
                               * self.axes.get_theta_direction())
 | 
						|
                    + self.axes.get_theta_offset()
 | 
						|
                    - np.pi / 2
 | 
						|
                )
 | 
						|
            elif self.mode == 'min':
 | 
						|
                angle = self.axes._realViewLim.xmin - np.pi / 2
 | 
						|
            elif self.mode == 'max':
 | 
						|
                angle = self.axes._realViewLim.xmax + np.pi / 2
 | 
						|
            self._t = (self.pad * np.cos(angle) / 72, self.pad * np.sin(angle) / 72)
 | 
						|
        return super().get_matrix()
 | 
						|
 | 
						|
 | 
						|
class RadialTick(maxis.YTick):
 | 
						|
    """
 | 
						|
    A radial-axis tick.
 | 
						|
 | 
						|
    This subclass of `.YTick` provides radial ticks with some small
 | 
						|
    modification to their re-positioning such that ticks are rotated based on
 | 
						|
    axes limits.  This results in ticks that are correctly perpendicular to
 | 
						|
    the spine. Labels are also rotated to be perpendicular to the spine, when
 | 
						|
    'auto' rotation is enabled.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, *args, **kwargs):
 | 
						|
        super().__init__(*args, **kwargs)
 | 
						|
        self.label1.set_rotation_mode('anchor')
 | 
						|
        self.label2.set_rotation_mode('anchor')
 | 
						|
 | 
						|
    def _determine_anchor(self, mode, angle, start):
 | 
						|
        # Note: angle is the (spine angle - 90) because it's used for the tick
 | 
						|
        # & text setup, so all numbers below are -90 from (normed) spine angle.
 | 
						|
        if mode == 'auto':
 | 
						|
            if start:
 | 
						|
                if -90 <= angle <= 90:
 | 
						|
                    return 'left', 'center'
 | 
						|
                else:
 | 
						|
                    return 'right', 'center'
 | 
						|
            else:
 | 
						|
                if -90 <= angle <= 90:
 | 
						|
                    return 'right', 'center'
 | 
						|
                else:
 | 
						|
                    return 'left', 'center'
 | 
						|
        else:
 | 
						|
            if start:
 | 
						|
                if angle < -68.5:
 | 
						|
                    return 'center', 'top'
 | 
						|
                elif angle < -23.5:
 | 
						|
                    return 'left', 'top'
 | 
						|
                elif angle < 22.5:
 | 
						|
                    return 'left', 'center'
 | 
						|
                elif angle < 67.5:
 | 
						|
                    return 'left', 'bottom'
 | 
						|
                elif angle < 112.5:
 | 
						|
                    return 'center', 'bottom'
 | 
						|
                elif angle < 157.5:
 | 
						|
                    return 'right', 'bottom'
 | 
						|
                elif angle < 202.5:
 | 
						|
                    return 'right', 'center'
 | 
						|
                elif angle < 247.5:
 | 
						|
                    return 'right', 'top'
 | 
						|
                else:
 | 
						|
                    return 'center', 'top'
 | 
						|
            else:
 | 
						|
                if angle < -68.5:
 | 
						|
                    return 'center', 'bottom'
 | 
						|
                elif angle < -23.5:
 | 
						|
                    return 'right', 'bottom'
 | 
						|
                elif angle < 22.5:
 | 
						|
                    return 'right', 'center'
 | 
						|
                elif angle < 67.5:
 | 
						|
                    return 'right', 'top'
 | 
						|
                elif angle < 112.5:
 | 
						|
                    return 'center', 'top'
 | 
						|
                elif angle < 157.5:
 | 
						|
                    return 'left', 'top'
 | 
						|
                elif angle < 202.5:
 | 
						|
                    return 'left', 'center'
 | 
						|
                elif angle < 247.5:
 | 
						|
                    return 'left', 'bottom'
 | 
						|
                else:
 | 
						|
                    return 'center', 'bottom'
 | 
						|
 | 
						|
    def update_position(self, loc):
 | 
						|
        super().update_position(loc)
 | 
						|
        axes = self.axes
 | 
						|
        thetamin = axes.get_thetamin()
 | 
						|
        thetamax = axes.get_thetamax()
 | 
						|
        direction = axes.get_theta_direction()
 | 
						|
        offset_rad = axes.get_theta_offset()
 | 
						|
        offset = np.rad2deg(offset_rad)
 | 
						|
        full = _is_full_circle_deg(thetamin, thetamax)
 | 
						|
 | 
						|
        if full:
 | 
						|
            angle = (axes.get_rlabel_position() * direction +
 | 
						|
                     offset) % 360 - 90
 | 
						|
            tick_angle = 0
 | 
						|
        else:
 | 
						|
            angle = (thetamin * direction + offset) % 360 - 90
 | 
						|
            if direction > 0:
 | 
						|
                tick_angle = np.deg2rad(angle)
 | 
						|
            else:
 | 
						|
                tick_angle = np.deg2rad(angle + 180)
 | 
						|
        text_angle = (angle + 90) % 180 - 90  # between -90 and +90.
 | 
						|
        mode, user_angle = self._labelrotation
 | 
						|
        if mode == 'auto':
 | 
						|
            text_angle += user_angle
 | 
						|
        else:
 | 
						|
            text_angle = user_angle
 | 
						|
 | 
						|
        if full:
 | 
						|
            ha = self.label1.get_horizontalalignment()
 | 
						|
            va = self.label1.get_verticalalignment()
 | 
						|
        else:
 | 
						|
            ha, va = self._determine_anchor(mode, angle, direction > 0)
 | 
						|
        self.label1.set_horizontalalignment(ha)
 | 
						|
        self.label1.set_verticalalignment(va)
 | 
						|
        self.label1.set_rotation(text_angle)
 | 
						|
 | 
						|
        marker = self.tick1line.get_marker()
 | 
						|
        if marker == mmarkers.TICKLEFT:
 | 
						|
            trans = mtransforms.Affine2D().rotate(tick_angle)
 | 
						|
        elif marker == '_':
 | 
						|
            trans = mtransforms.Affine2D().rotate(tick_angle + np.pi / 2)
 | 
						|
        elif marker == mmarkers.TICKRIGHT:
 | 
						|
            trans = mtransforms.Affine2D().scale(-1, 1).rotate(tick_angle)
 | 
						|
        else:
 | 
						|
            # Don't modify custom tick line markers.
 | 
						|
            trans = self.tick1line._marker._transform
 | 
						|
        self.tick1line._marker._transform = trans
 | 
						|
 | 
						|
        if full:
 | 
						|
            self.label2.set_visible(False)
 | 
						|
            self.tick2line.set_visible(False)
 | 
						|
        angle = (thetamax * direction + offset) % 360 - 90
 | 
						|
        if direction > 0:
 | 
						|
            tick_angle = np.deg2rad(angle)
 | 
						|
        else:
 | 
						|
            tick_angle = np.deg2rad(angle + 180)
 | 
						|
        text_angle = (angle + 90) % 180 - 90  # between -90 and +90.
 | 
						|
        mode, user_angle = self._labelrotation
 | 
						|
        if mode == 'auto':
 | 
						|
            text_angle += user_angle
 | 
						|
        else:
 | 
						|
            text_angle = user_angle
 | 
						|
 | 
						|
        ha, va = self._determine_anchor(mode, angle, direction < 0)
 | 
						|
        self.label2.set_ha(ha)
 | 
						|
        self.label2.set_va(va)
 | 
						|
        self.label2.set_rotation(text_angle)
 | 
						|
 | 
						|
        marker = self.tick2line.get_marker()
 | 
						|
        if marker == mmarkers.TICKLEFT:
 | 
						|
            trans = mtransforms.Affine2D().rotate(tick_angle)
 | 
						|
        elif marker == '_':
 | 
						|
            trans = mtransforms.Affine2D().rotate(tick_angle + np.pi / 2)
 | 
						|
        elif marker == mmarkers.TICKRIGHT:
 | 
						|
            trans = mtransforms.Affine2D().scale(-1, 1).rotate(tick_angle)
 | 
						|
        else:
 | 
						|
            # Don't modify custom tick line markers.
 | 
						|
            trans = self.tick2line._marker._transform
 | 
						|
        self.tick2line._marker._transform = trans
 | 
						|
 | 
						|
 | 
						|
class RadialAxis(maxis.YAxis):
 | 
						|
    """
 | 
						|
    A radial Axis.
 | 
						|
 | 
						|
    This overrides certain properties of a `.YAxis` to provide special-casing
 | 
						|
    for a radial axis.
 | 
						|
    """
 | 
						|
    __name__ = 'radialaxis'
 | 
						|
    axis_name = 'radius'  #: Read-only name identifying the axis.
 | 
						|
    _tick_class = RadialTick
 | 
						|
 | 
						|
    def __init__(self, *args, **kwargs):
 | 
						|
        super().__init__(*args, **kwargs)
 | 
						|
        self.sticky_edges.y.append(0)
 | 
						|
 | 
						|
    def _wrap_locator_formatter(self):
 | 
						|
        self.set_major_locator(RadialLocator(self.get_major_locator(),
 | 
						|
                                             self.axes))
 | 
						|
        self.isDefault_majloc = True
 | 
						|
 | 
						|
    def clear(self):
 | 
						|
        # docstring inherited
 | 
						|
        super().clear()
 | 
						|
        self.set_ticks_position('none')
 | 
						|
        self._wrap_locator_formatter()
 | 
						|
 | 
						|
    def _set_scale(self, value, **kwargs):
 | 
						|
        super()._set_scale(value, **kwargs)
 | 
						|
        self._wrap_locator_formatter()
 | 
						|
 | 
						|
 | 
						|
def _is_full_circle_deg(thetamin, thetamax):
 | 
						|
    """
 | 
						|
    Determine if a wedge (in degrees) spans the full circle.
 | 
						|
 | 
						|
    The condition is derived from :class:`~matplotlib.patches.Wedge`.
 | 
						|
    """
 | 
						|
    return abs(abs(thetamax - thetamin) - 360.0) < 1e-12
 | 
						|
 | 
						|
 | 
						|
def _is_full_circle_rad(thetamin, thetamax):
 | 
						|
    """
 | 
						|
    Determine if a wedge (in radians) spans the full circle.
 | 
						|
 | 
						|
    The condition is derived from :class:`~matplotlib.patches.Wedge`.
 | 
						|
    """
 | 
						|
    return abs(abs(thetamax - thetamin) - 2 * np.pi) < 1.74e-14
 | 
						|
 | 
						|
 | 
						|
class _WedgeBbox(mtransforms.Bbox):
 | 
						|
    """
 | 
						|
    Transform (theta, r) wedge Bbox into Axes bounding box.
 | 
						|
 | 
						|
    Parameters
 | 
						|
    ----------
 | 
						|
    center : (float, float)
 | 
						|
        Center of the wedge
 | 
						|
    viewLim : `~matplotlib.transforms.Bbox`
 | 
						|
        Bbox determining the boundaries of the wedge
 | 
						|
    originLim : `~matplotlib.transforms.Bbox`
 | 
						|
        Bbox determining the origin for the wedge, if different from *viewLim*
 | 
						|
    """
 | 
						|
    def __init__(self, center, viewLim, originLim, **kwargs):
 | 
						|
        super().__init__([[0, 0], [1, 1]], **kwargs)
 | 
						|
        self._center = center
 | 
						|
        self._viewLim = viewLim
 | 
						|
        self._originLim = originLim
 | 
						|
        self.set_children(viewLim, originLim)
 | 
						|
 | 
						|
    __str__ = mtransforms._make_str_method("_center", "_viewLim", "_originLim")
 | 
						|
 | 
						|
    def get_points(self):
 | 
						|
        # docstring inherited
 | 
						|
        if self._invalid:
 | 
						|
            points = self._viewLim.get_points().copy()
 | 
						|
            # Scale angular limits to work with Wedge.
 | 
						|
            points[:, 0] *= 180 / np.pi
 | 
						|
            if points[0, 0] > points[1, 0]:
 | 
						|
                points[:, 0] = points[::-1, 0]
 | 
						|
 | 
						|
            # Scale radial limits based on origin radius.
 | 
						|
            points[:, 1] -= self._originLim.y0
 | 
						|
 | 
						|
            # Scale radial limits to match axes limits.
 | 
						|
            rscale = 0.5 / points[1, 1]
 | 
						|
            points[:, 1] *= rscale
 | 
						|
            width = min(points[1, 1] - points[0, 1], 0.5)
 | 
						|
 | 
						|
            # Generate bounding box for wedge.
 | 
						|
            wedge = mpatches.Wedge(self._center, points[1, 1],
 | 
						|
                                   points[0, 0], points[1, 0],
 | 
						|
                                   width=width)
 | 
						|
            self.update_from_path(wedge.get_path())
 | 
						|
 | 
						|
            # Ensure equal aspect ratio.
 | 
						|
            w, h = self._points[1] - self._points[0]
 | 
						|
            deltah = max(w - h, 0) / 2
 | 
						|
            deltaw = max(h - w, 0) / 2
 | 
						|
            self._points += np.array([[-deltaw, -deltah], [deltaw, deltah]])
 | 
						|
 | 
						|
            self._invalid = 0
 | 
						|
 | 
						|
        return self._points
 | 
						|
 | 
						|
 | 
						|
class PolarAxes(Axes):
 | 
						|
    """
 | 
						|
    A polar graph projection, where the input dimensions are *theta*, *r*.
 | 
						|
 | 
						|
    Theta starts pointing east and goes anti-clockwise.
 | 
						|
    """
 | 
						|
    name = 'polar'
 | 
						|
 | 
						|
    def __init__(self, *args,
 | 
						|
                 theta_offset=0, theta_direction=1, rlabel_position=22.5,
 | 
						|
                 **kwargs):
 | 
						|
        # docstring inherited
 | 
						|
        self._default_theta_offset = theta_offset
 | 
						|
        self._default_theta_direction = theta_direction
 | 
						|
        self._default_rlabel_position = np.deg2rad(rlabel_position)
 | 
						|
        super().__init__(*args, **kwargs)
 | 
						|
        self.use_sticky_edges = True
 | 
						|
        self.set_aspect('equal', adjustable='box', anchor='C')
 | 
						|
        self.clear()
 | 
						|
 | 
						|
    def clear(self):
 | 
						|
        # docstring inherited
 | 
						|
        super().clear()
 | 
						|
 | 
						|
        self.title.set_y(1.05)
 | 
						|
 | 
						|
        start = self.spines.get('start', None)
 | 
						|
        if start:
 | 
						|
            start.set_visible(False)
 | 
						|
        end = self.spines.get('end', None)
 | 
						|
        if end:
 | 
						|
            end.set_visible(False)
 | 
						|
        self.set_xlim(0.0, 2 * np.pi)
 | 
						|
 | 
						|
        self.grid(mpl.rcParams['polaraxes.grid'])
 | 
						|
        inner = self.spines.get('inner', None)
 | 
						|
        if inner:
 | 
						|
            inner.set_visible(False)
 | 
						|
 | 
						|
        self.set_rorigin(None)
 | 
						|
        self.set_theta_offset(self._default_theta_offset)
 | 
						|
        self.set_theta_direction(self._default_theta_direction)
 | 
						|
 | 
						|
    def _init_axis(self):
 | 
						|
        # This is moved out of __init__ because non-separable axes don't use it
 | 
						|
        self.xaxis = ThetaAxis(self, clear=False)
 | 
						|
        self.yaxis = RadialAxis(self, clear=False)
 | 
						|
        self.spines['polar'].register_axis(self.yaxis)
 | 
						|
        inner_spine = self.spines.get('inner', None)
 | 
						|
        if inner_spine is not None:
 | 
						|
            # Subclasses may not have inner spine.
 | 
						|
            inner_spine.register_axis(self.yaxis)
 | 
						|
 | 
						|
    def _set_lim_and_transforms(self):
 | 
						|
        # A view limit where the minimum radius can be locked if the user
 | 
						|
        # specifies an alternate origin.
 | 
						|
        self._originViewLim = mtransforms.LockableBbox(self.viewLim)
 | 
						|
 | 
						|
        # Handle angular offset and direction.
 | 
						|
        self._direction = mtransforms.Affine2D() \
 | 
						|
            .scale(self._default_theta_direction, 1.0)
 | 
						|
        self._theta_offset = mtransforms.Affine2D() \
 | 
						|
            .translate(self._default_theta_offset, 0.0)
 | 
						|
        self.transShift = self._direction + self._theta_offset
 | 
						|
        # A view limit shifted to the correct location after accounting for
 | 
						|
        # orientation and offset.
 | 
						|
        self._realViewLim = mtransforms.TransformedBbox(self.viewLim,
 | 
						|
                                                        self.transShift)
 | 
						|
 | 
						|
        # Transforms the x and y axis separately by a scale factor
 | 
						|
        # It is assumed that this part will have non-linear components
 | 
						|
        self.transScale = mtransforms.TransformWrapper(
 | 
						|
            mtransforms.IdentityTransform())
 | 
						|
 | 
						|
        # Scale view limit into a bbox around the selected wedge. This may be
 | 
						|
        # smaller than the usual unit axes rectangle if not plotting the full
 | 
						|
        # circle.
 | 
						|
        self.axesLim = _WedgeBbox((0.5, 0.5),
 | 
						|
                                  self._realViewLim, self._originViewLim)
 | 
						|
 | 
						|
        # Scale the wedge to fill the axes.
 | 
						|
        self.transWedge = mtransforms.BboxTransformFrom(self.axesLim)
 | 
						|
 | 
						|
        # Scale the axes to fill the figure.
 | 
						|
        self.transAxes = mtransforms.BboxTransformTo(self.bbox)
 | 
						|
 | 
						|
        # A (possibly non-linear) projection on the (already scaled)
 | 
						|
        # data.  This one is aware of rmin
 | 
						|
        self.transProjection = self.PolarTransform(
 | 
						|
            self,
 | 
						|
            apply_theta_transforms=False,
 | 
						|
            scale_transform=self.transScale
 | 
						|
        )
 | 
						|
        # Add dependency on rorigin.
 | 
						|
        self.transProjection.set_children(self._originViewLim)
 | 
						|
 | 
						|
        # An affine transformation on the data, generally to limit the
 | 
						|
        # range of the axes
 | 
						|
        self.transProjectionAffine = self.PolarAffine(self.transScale,
 | 
						|
                                                      self._originViewLim)
 | 
						|
 | 
						|
        # 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
 | 
						|
            )
 | 
						|
        )
 | 
						|
 | 
						|
        # This is the transform for theta-axis ticks.  It is
 | 
						|
        # equivalent to transData, except it always puts r == 0.0 and r == 1.0
 | 
						|
        # at the edge of the axis circles.
 | 
						|
        self._xaxis_transform = (
 | 
						|
            mtransforms.blended_transform_factory(
 | 
						|
                mtransforms.IdentityTransform(),
 | 
						|
                mtransforms.BboxTransformTo(self.viewLim)) +
 | 
						|
            self.transData)
 | 
						|
        # The theta labels are flipped along the radius, so that text 1 is on
 | 
						|
        # the outside by default. This should work the same as before.
 | 
						|
        flipr_transform = mtransforms.Affine2D() \
 | 
						|
            .translate(0.0, -0.5) \
 | 
						|
            .scale(1.0, -1.0) \
 | 
						|
            .translate(0.0, 0.5)
 | 
						|
        self._xaxis_text_transform = flipr_transform + self._xaxis_transform
 | 
						|
 | 
						|
        # This is the transform for r-axis ticks.  It scales the theta
 | 
						|
        # axis so the gridlines from 0.0 to 1.0, now go from thetamin to
 | 
						|
        # thetamax.
 | 
						|
        self._yaxis_transform = (
 | 
						|
            mtransforms.blended_transform_factory(
 | 
						|
                mtransforms.BboxTransformTo(self.viewLim),
 | 
						|
                mtransforms.IdentityTransform()) +
 | 
						|
            self.transData)
 | 
						|
        # The r-axis labels are put at an angle and padded in the r-direction
 | 
						|
        self._r_label_position = mtransforms.Affine2D() \
 | 
						|
            .translate(self._default_rlabel_position, 0.0)
 | 
						|
        self._yaxis_text_transform = mtransforms.TransformWrapper(
 | 
						|
            self._r_label_position + self.transData)
 | 
						|
 | 
						|
    def get_xaxis_transform(self, which='grid'):
 | 
						|
        _api.check_in_list(['tick1', 'tick2', 'grid'], which=which)
 | 
						|
        return self._xaxis_transform
 | 
						|
 | 
						|
    def get_xaxis_text1_transform(self, pad):
 | 
						|
        return self._xaxis_text_transform, 'center', 'center'
 | 
						|
 | 
						|
    def get_xaxis_text2_transform(self, pad):
 | 
						|
        return self._xaxis_text_transform, 'center', 'center'
 | 
						|
 | 
						|
    def get_yaxis_transform(self, which='grid'):
 | 
						|
        if which in ('tick1', 'tick2'):
 | 
						|
            return self._yaxis_text_transform
 | 
						|
        elif which == 'grid':
 | 
						|
            return self._yaxis_transform
 | 
						|
        else:
 | 
						|
            _api.check_in_list(['tick1', 'tick2', 'grid'], which=which)
 | 
						|
 | 
						|
    def get_yaxis_text1_transform(self, pad):
 | 
						|
        thetamin, thetamax = self._realViewLim.intervalx
 | 
						|
        if _is_full_circle_rad(thetamin, thetamax):
 | 
						|
            return self._yaxis_text_transform, 'bottom', 'left'
 | 
						|
        elif self.get_theta_direction() > 0:
 | 
						|
            halign = 'left'
 | 
						|
            pad_shift = _ThetaShift(self, pad, 'min')
 | 
						|
        else:
 | 
						|
            halign = 'right'
 | 
						|
            pad_shift = _ThetaShift(self, pad, 'max')
 | 
						|
        return self._yaxis_text_transform + pad_shift, 'center', halign
 | 
						|
 | 
						|
    def get_yaxis_text2_transform(self, pad):
 | 
						|
        if self.get_theta_direction() > 0:
 | 
						|
            halign = 'right'
 | 
						|
            pad_shift = _ThetaShift(self, pad, 'max')
 | 
						|
        else:
 | 
						|
            halign = 'left'
 | 
						|
            pad_shift = _ThetaShift(self, pad, 'min')
 | 
						|
        return self._yaxis_text_transform + pad_shift, 'center', halign
 | 
						|
 | 
						|
    def draw(self, renderer):
 | 
						|
        self._unstale_viewLim()
 | 
						|
        thetamin, thetamax = np.rad2deg(self._realViewLim.intervalx)
 | 
						|
        if thetamin > thetamax:
 | 
						|
            thetamin, thetamax = thetamax, thetamin
 | 
						|
        rscale_tr = self.yaxis.get_transform()
 | 
						|
        rmin, rmax = ((rscale_tr.transform(self._realViewLim.intervaly) -
 | 
						|
                       rscale_tr.transform(self.get_rorigin())) *
 | 
						|
                      self.get_rsign())
 | 
						|
        if isinstance(self.patch, mpatches.Wedge):
 | 
						|
            # Backwards-compatibility: Any subclassed Axes might override the
 | 
						|
            # patch to not be the Wedge that PolarAxes uses.
 | 
						|
            center = self.transWedge.transform((0.5, 0.5))
 | 
						|
            self.patch.set_center(center)
 | 
						|
            self.patch.set_theta1(thetamin)
 | 
						|
            self.patch.set_theta2(thetamax)
 | 
						|
 | 
						|
            edge, _ = self.transWedge.transform((1, 0))
 | 
						|
            radius = edge - center[0]
 | 
						|
            width = min(radius * (rmax - rmin) / rmax, radius)
 | 
						|
            self.patch.set_radius(radius)
 | 
						|
            self.patch.set_width(width)
 | 
						|
 | 
						|
            inner_width = radius - width
 | 
						|
            inner = self.spines.get('inner', None)
 | 
						|
            if inner:
 | 
						|
                inner.set_visible(inner_width != 0.0)
 | 
						|
 | 
						|
        visible = not _is_full_circle_deg(thetamin, thetamax)
 | 
						|
        # For backwards compatibility, any subclassed Axes might override the
 | 
						|
        # spines to not include start/end that PolarAxes uses.
 | 
						|
        start = self.spines.get('start', None)
 | 
						|
        end = self.spines.get('end', None)
 | 
						|
        if start:
 | 
						|
            start.set_visible(visible)
 | 
						|
        if end:
 | 
						|
            end.set_visible(visible)
 | 
						|
        if visible:
 | 
						|
            yaxis_text_transform = self._yaxis_transform
 | 
						|
        else:
 | 
						|
            yaxis_text_transform = self._r_label_position + self.transData
 | 
						|
        if self._yaxis_text_transform != yaxis_text_transform:
 | 
						|
            self._yaxis_text_transform.set(yaxis_text_transform)
 | 
						|
            self.yaxis.reset_ticks()
 | 
						|
            self.yaxis.set_clip_path(self.patch)
 | 
						|
 | 
						|
        super().draw(renderer)
 | 
						|
 | 
						|
    def _gen_axes_patch(self):
 | 
						|
        return mpatches.Wedge((0.5, 0.5), 0.5, 0.0, 360.0)
 | 
						|
 | 
						|
    def _gen_axes_spines(self):
 | 
						|
        spines = {
 | 
						|
            'polar': Spine.arc_spine(self, 'top', (0.5, 0.5), 0.5, 0, 360),
 | 
						|
            'start': Spine.linear_spine(self, 'left'),
 | 
						|
            'end': Spine.linear_spine(self, 'right'),
 | 
						|
            'inner': Spine.arc_spine(self, 'bottom', (0.5, 0.5), 0.0, 0, 360),
 | 
						|
        }
 | 
						|
        spines['polar'].set_transform(self.transWedge + self.transAxes)
 | 
						|
        spines['inner'].set_transform(self.transWedge + self.transAxes)
 | 
						|
        spines['start'].set_transform(self._yaxis_transform)
 | 
						|
        spines['end'].set_transform(self._yaxis_transform)
 | 
						|
        return spines
 | 
						|
 | 
						|
    def set_thetamax(self, thetamax):
 | 
						|
        """Set the maximum theta limit in degrees."""
 | 
						|
        self.viewLim.x1 = np.deg2rad(thetamax)
 | 
						|
 | 
						|
    def get_thetamax(self):
 | 
						|
        """Return the maximum theta limit in degrees."""
 | 
						|
        return np.rad2deg(self.viewLim.xmax)
 | 
						|
 | 
						|
    def set_thetamin(self, thetamin):
 | 
						|
        """Set the minimum theta limit in degrees."""
 | 
						|
        self.viewLim.x0 = np.deg2rad(thetamin)
 | 
						|
 | 
						|
    def get_thetamin(self):
 | 
						|
        """Get the minimum theta limit in degrees."""
 | 
						|
        return np.rad2deg(self.viewLim.xmin)
 | 
						|
 | 
						|
    def set_thetalim(self, *args, **kwargs):
 | 
						|
        r"""
 | 
						|
        Set the minimum and maximum theta values.
 | 
						|
 | 
						|
        Can take the following signatures:
 | 
						|
 | 
						|
        - ``set_thetalim(minval, maxval)``: Set the limits in radians.
 | 
						|
        - ``set_thetalim(thetamin=minval, thetamax=maxval)``: Set the limits
 | 
						|
          in degrees.
 | 
						|
 | 
						|
        where minval and maxval are the minimum and maximum limits. Values are
 | 
						|
        wrapped in to the range :math:`[0, 2\pi]` (in radians), so for example
 | 
						|
        it is possible to do ``set_thetalim(-np.pi / 2, np.pi / 2)`` to have
 | 
						|
        an axis symmetric around 0. A ValueError is raised if the absolute
 | 
						|
        angle difference is larger than a full circle.
 | 
						|
        """
 | 
						|
        orig_lim = self.get_xlim()  # in radians
 | 
						|
        if 'thetamin' in kwargs:
 | 
						|
            kwargs['xmin'] = np.deg2rad(kwargs.pop('thetamin'))
 | 
						|
        if 'thetamax' in kwargs:
 | 
						|
            kwargs['xmax'] = np.deg2rad(kwargs.pop('thetamax'))
 | 
						|
        new_min, new_max = self.set_xlim(*args, **kwargs)
 | 
						|
        # Parsing all permutations of *args, **kwargs is tricky; it is simpler
 | 
						|
        # to let set_xlim() do it and then validate the limits.
 | 
						|
        if abs(new_max - new_min) > 2 * np.pi:
 | 
						|
            self.set_xlim(orig_lim)  # un-accept the change
 | 
						|
            raise ValueError("The angle range must be less than a full circle")
 | 
						|
        return tuple(np.rad2deg((new_min, new_max)))
 | 
						|
 | 
						|
    def set_theta_offset(self, offset):
 | 
						|
        """
 | 
						|
        Set the offset for the location of 0 in radians.
 | 
						|
        """
 | 
						|
        mtx = self._theta_offset.get_matrix()
 | 
						|
        mtx[0, 2] = offset
 | 
						|
        self._theta_offset.invalidate()
 | 
						|
 | 
						|
    def get_theta_offset(self):
 | 
						|
        """
 | 
						|
        Get the offset for the location of 0 in radians.
 | 
						|
        """
 | 
						|
        return self._theta_offset.get_matrix()[0, 2]
 | 
						|
 | 
						|
    def set_theta_zero_location(self, loc, offset=0.0):
 | 
						|
        """
 | 
						|
        Set the location of theta's zero.
 | 
						|
 | 
						|
        This simply calls `set_theta_offset` with the correct value in radians.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        loc : str
 | 
						|
            May be one of "N", "NW", "W", "SW", "S", "SE", "E", or "NE".
 | 
						|
        offset : float, default: 0
 | 
						|
            An offset in degrees to apply from the specified *loc*. **Note:**
 | 
						|
            this offset is *always* applied counter-clockwise regardless of
 | 
						|
            the direction setting.
 | 
						|
        """
 | 
						|
        mapping = {
 | 
						|
            'N': np.pi * 0.5,
 | 
						|
            'NW': np.pi * 0.75,
 | 
						|
            'W': np.pi,
 | 
						|
            'SW': np.pi * 1.25,
 | 
						|
            'S': np.pi * 1.5,
 | 
						|
            'SE': np.pi * 1.75,
 | 
						|
            'E': 0,
 | 
						|
            'NE': np.pi * 0.25}
 | 
						|
        return self.set_theta_offset(mapping[loc] + np.deg2rad(offset))
 | 
						|
 | 
						|
    def set_theta_direction(self, direction):
 | 
						|
        """
 | 
						|
        Set the direction in which theta increases.
 | 
						|
 | 
						|
        clockwise, -1:
 | 
						|
           Theta increases in the clockwise direction
 | 
						|
 | 
						|
        counterclockwise, anticlockwise, 1:
 | 
						|
           Theta increases in the counterclockwise direction
 | 
						|
        """
 | 
						|
        mtx = self._direction.get_matrix()
 | 
						|
        if direction in ('clockwise', -1):
 | 
						|
            mtx[0, 0] = -1
 | 
						|
        elif direction in ('counterclockwise', 'anticlockwise', 1):
 | 
						|
            mtx[0, 0] = 1
 | 
						|
        else:
 | 
						|
            _api.check_in_list(
 | 
						|
                [-1, 1, 'clockwise', 'counterclockwise', 'anticlockwise'],
 | 
						|
                direction=direction)
 | 
						|
        self._direction.invalidate()
 | 
						|
 | 
						|
    def get_theta_direction(self):
 | 
						|
        """
 | 
						|
        Get the direction in which theta increases.
 | 
						|
 | 
						|
        -1:
 | 
						|
           Theta increases in the clockwise direction
 | 
						|
 | 
						|
        1:
 | 
						|
           Theta increases in the counterclockwise direction
 | 
						|
        """
 | 
						|
        return self._direction.get_matrix()[0, 0]
 | 
						|
 | 
						|
    def set_rmax(self, rmax):
 | 
						|
        """
 | 
						|
        Set the outer radial limit.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        rmax : float
 | 
						|
        """
 | 
						|
        self.viewLim.y1 = rmax
 | 
						|
 | 
						|
    def get_rmax(self):
 | 
						|
        """
 | 
						|
        Returns
 | 
						|
        -------
 | 
						|
        float
 | 
						|
            Outer radial limit.
 | 
						|
        """
 | 
						|
        return self.viewLim.ymax
 | 
						|
 | 
						|
    def set_rmin(self, rmin):
 | 
						|
        """
 | 
						|
        Set the inner radial limit.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        rmin : float
 | 
						|
        """
 | 
						|
        self.viewLim.y0 = rmin
 | 
						|
 | 
						|
    def get_rmin(self):
 | 
						|
        """
 | 
						|
        Returns
 | 
						|
        -------
 | 
						|
        float
 | 
						|
            The inner radial limit.
 | 
						|
        """
 | 
						|
        return self.viewLim.ymin
 | 
						|
 | 
						|
    def set_rorigin(self, rorigin):
 | 
						|
        """
 | 
						|
        Update the radial origin.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        rorigin : float
 | 
						|
        """
 | 
						|
        self._originViewLim.locked_y0 = rorigin
 | 
						|
 | 
						|
    def get_rorigin(self):
 | 
						|
        """
 | 
						|
        Returns
 | 
						|
        -------
 | 
						|
        float
 | 
						|
        """
 | 
						|
        return self._originViewLim.y0
 | 
						|
 | 
						|
    def get_rsign(self):
 | 
						|
        return np.sign(self._originViewLim.y1 - self._originViewLim.y0)
 | 
						|
 | 
						|
    def set_rlim(self, bottom=None, top=None, *,
 | 
						|
                 emit=True, auto=False, **kwargs):
 | 
						|
        """
 | 
						|
        Set the radial axis view limits.
 | 
						|
 | 
						|
        This function behaves like `.Axes.set_ylim`, but additionally supports
 | 
						|
        *rmin* and *rmax* as aliases for *bottom* and *top*.
 | 
						|
 | 
						|
        See Also
 | 
						|
        --------
 | 
						|
        .Axes.set_ylim
 | 
						|
        """
 | 
						|
        if 'rmin' in kwargs:
 | 
						|
            if bottom is None:
 | 
						|
                bottom = kwargs.pop('rmin')
 | 
						|
            else:
 | 
						|
                raise ValueError('Cannot supply both positional "bottom"'
 | 
						|
                                 'argument and kwarg "rmin"')
 | 
						|
        if 'rmax' in kwargs:
 | 
						|
            if top is None:
 | 
						|
                top = kwargs.pop('rmax')
 | 
						|
            else:
 | 
						|
                raise ValueError('Cannot supply both positional "top"'
 | 
						|
                                 'argument and kwarg "rmax"')
 | 
						|
        return self.set_ylim(bottom=bottom, top=top, emit=emit, auto=auto,
 | 
						|
                             **kwargs)
 | 
						|
 | 
						|
    def get_rlabel_position(self):
 | 
						|
        """
 | 
						|
        Returns
 | 
						|
        -------
 | 
						|
        float
 | 
						|
            The theta position of the radius labels in degrees.
 | 
						|
        """
 | 
						|
        return np.rad2deg(self._r_label_position.get_matrix()[0, 2])
 | 
						|
 | 
						|
    def set_rlabel_position(self, value):
 | 
						|
        """
 | 
						|
        Update the theta position of the radius labels.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        value : number
 | 
						|
            The angular position of the radius labels in degrees.
 | 
						|
        """
 | 
						|
        self._r_label_position.clear().translate(np.deg2rad(value), 0.0)
 | 
						|
 | 
						|
    def set_yscale(self, *args, **kwargs):
 | 
						|
        super().set_yscale(*args, **kwargs)
 | 
						|
        self.yaxis.set_major_locator(
 | 
						|
            self.RadialLocator(self.yaxis.get_major_locator(), self))
 | 
						|
 | 
						|
    def set_rscale(self, *args, **kwargs):
 | 
						|
        return Axes.set_yscale(self, *args, **kwargs)
 | 
						|
 | 
						|
    def set_rticks(self, *args, **kwargs):
 | 
						|
        return Axes.set_yticks(self, *args, **kwargs)
 | 
						|
 | 
						|
    def set_thetagrids(self, angles, labels=None, fmt=None, **kwargs):
 | 
						|
        """
 | 
						|
        Set the theta gridlines in a polar plot.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        angles : tuple with floats, degrees
 | 
						|
            The angles of the theta gridlines.
 | 
						|
 | 
						|
        labels : tuple with strings or None
 | 
						|
            The labels to use at each theta gridline. The
 | 
						|
            `.projections.polar.ThetaFormatter` will be used if None.
 | 
						|
 | 
						|
        fmt : str or None
 | 
						|
            Format string used in `matplotlib.ticker.FormatStrFormatter`.
 | 
						|
            For example '%f'. Note that the angle that is used is in
 | 
						|
            radians.
 | 
						|
 | 
						|
        Returns
 | 
						|
        -------
 | 
						|
        lines : list of `.lines.Line2D`
 | 
						|
            The theta gridlines.
 | 
						|
 | 
						|
        labels : list of `.text.Text`
 | 
						|
            The tick labels.
 | 
						|
 | 
						|
        Other Parameters
 | 
						|
        ----------------
 | 
						|
        **kwargs
 | 
						|
            *kwargs* are optional `.Text` properties for the labels.
 | 
						|
 | 
						|
            .. warning::
 | 
						|
 | 
						|
                This only sets the properties of the current ticks.
 | 
						|
                Ticks are not guaranteed to be persistent. Various operations
 | 
						|
                can create, delete and modify the Tick instances. There is an
 | 
						|
                imminent risk that these settings can get lost if you work on
 | 
						|
                the figure further (including also panning/zooming on a
 | 
						|
                displayed figure).
 | 
						|
 | 
						|
                Use `.set_tick_params` instead if possible.
 | 
						|
 | 
						|
        See Also
 | 
						|
        --------
 | 
						|
        .PolarAxes.set_rgrids
 | 
						|
        .Axis.get_gridlines
 | 
						|
        .Axis.get_ticklabels
 | 
						|
        """
 | 
						|
 | 
						|
        # Make sure we take into account unitized data
 | 
						|
        angles = self.convert_yunits(angles)
 | 
						|
        angles = np.deg2rad(angles)
 | 
						|
        self.set_xticks(angles)
 | 
						|
        if labels is not None:
 | 
						|
            self.set_xticklabels(labels)
 | 
						|
        elif fmt is not None:
 | 
						|
            self.xaxis.set_major_formatter(mticker.FormatStrFormatter(fmt))
 | 
						|
        for t in self.xaxis.get_ticklabels():
 | 
						|
            t._internal_update(kwargs)
 | 
						|
        return self.xaxis.get_ticklines(), self.xaxis.get_ticklabels()
 | 
						|
 | 
						|
    def set_rgrids(self, radii, labels=None, angle=None, fmt=None, **kwargs):
 | 
						|
        """
 | 
						|
        Set the radial gridlines on a polar plot.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        radii : tuple with floats
 | 
						|
            The radii for the radial gridlines
 | 
						|
 | 
						|
        labels : tuple with strings or None
 | 
						|
            The labels to use at each radial gridline. The
 | 
						|
            `matplotlib.ticker.ScalarFormatter` will be used if None.
 | 
						|
 | 
						|
        angle : float
 | 
						|
            The angular position of the radius labels in degrees.
 | 
						|
 | 
						|
        fmt : str or None
 | 
						|
            Format string used in `matplotlib.ticker.FormatStrFormatter`.
 | 
						|
            For example '%f'.
 | 
						|
 | 
						|
        Returns
 | 
						|
        -------
 | 
						|
        lines : list of `.lines.Line2D`
 | 
						|
            The radial gridlines.
 | 
						|
 | 
						|
        labels : list of `.text.Text`
 | 
						|
            The tick labels.
 | 
						|
 | 
						|
        Other Parameters
 | 
						|
        ----------------
 | 
						|
        **kwargs
 | 
						|
            *kwargs* are optional `.Text` properties for the labels.
 | 
						|
 | 
						|
            .. warning::
 | 
						|
 | 
						|
                This only sets the properties of the current ticks.
 | 
						|
                Ticks are not guaranteed to be persistent. Various operations
 | 
						|
                can create, delete and modify the Tick instances. There is an
 | 
						|
                imminent risk that these settings can get lost if you work on
 | 
						|
                the figure further (including also panning/zooming on a
 | 
						|
                displayed figure).
 | 
						|
 | 
						|
                Use `.set_tick_params` instead if possible.
 | 
						|
 | 
						|
        See Also
 | 
						|
        --------
 | 
						|
        .PolarAxes.set_thetagrids
 | 
						|
        .Axis.get_gridlines
 | 
						|
        .Axis.get_ticklabels
 | 
						|
        """
 | 
						|
        # Make sure we take into account unitized data
 | 
						|
        radii = self.convert_xunits(radii)
 | 
						|
        radii = np.asarray(radii)
 | 
						|
 | 
						|
        self.set_yticks(radii)
 | 
						|
        if labels is not None:
 | 
						|
            self.set_yticklabels(labels)
 | 
						|
        elif fmt is not None:
 | 
						|
            self.yaxis.set_major_formatter(mticker.FormatStrFormatter(fmt))
 | 
						|
        if angle is None:
 | 
						|
            angle = self.get_rlabel_position()
 | 
						|
        self.set_rlabel_position(angle)
 | 
						|
        for t in self.yaxis.get_ticklabels():
 | 
						|
            t._internal_update(kwargs)
 | 
						|
        return self.yaxis.get_gridlines(), self.yaxis.get_ticklabels()
 | 
						|
 | 
						|
    def format_coord(self, theta, r):
 | 
						|
        # docstring inherited
 | 
						|
        screen_xy = self.transData.transform((theta, r))
 | 
						|
        screen_xys = screen_xy + np.stack(
 | 
						|
            np.meshgrid([-1, 0, 1], [-1, 0, 1])).reshape((2, -1)).T
 | 
						|
        ts, rs = self.transData.inverted().transform(screen_xys).T
 | 
						|
        delta_t = abs((ts - theta + np.pi) % (2 * np.pi) - np.pi).max()
 | 
						|
        delta_t_halfturns = delta_t / np.pi
 | 
						|
        delta_t_degrees = delta_t_halfturns * 180
 | 
						|
        delta_r = abs(rs - r).max()
 | 
						|
        if theta < 0:
 | 
						|
            theta += 2 * np.pi
 | 
						|
        theta_halfturns = theta / np.pi
 | 
						|
        theta_degrees = theta_halfturns * 180
 | 
						|
 | 
						|
        # See ScalarFormatter.format_data_short.  For r, use #g-formatting
 | 
						|
        # (as for linear axes), but for theta, use f-formatting as scientific
 | 
						|
        # notation doesn't make sense and the trailing dot is ugly.
 | 
						|
        def format_sig(value, delta, opt, fmt):
 | 
						|
            # For "f", only count digits after decimal point.
 | 
						|
            prec = (max(0, -math.floor(math.log10(delta))) if fmt == "f" else
 | 
						|
                    cbook._g_sig_digits(value, delta))
 | 
						|
            return f"{value:-{opt}.{prec}{fmt}}"
 | 
						|
 | 
						|
        # In case fmt_xdata was not specified, resort to default
 | 
						|
 | 
						|
        if self.fmt_ydata is None:
 | 
						|
            r_label = format_sig(r, delta_r, "#", "g")
 | 
						|
        else:
 | 
						|
            r_label = self.format_ydata(r)
 | 
						|
 | 
						|
        if self.fmt_xdata is None:
 | 
						|
            return ('\N{GREEK SMALL LETTER THETA}={}\N{GREEK SMALL LETTER PI} '
 | 
						|
                    '({}\N{DEGREE SIGN}), r={}').format(
 | 
						|
                    format_sig(theta_halfturns, delta_t_halfturns, "", "f"),
 | 
						|
                    format_sig(theta_degrees, delta_t_degrees, "", "f"),
 | 
						|
                    r_label
 | 
						|
                )
 | 
						|
        else:
 | 
						|
            return '\N{GREEK SMALL LETTER THETA}={}, r={}'.format(
 | 
						|
                        self.format_xdata(theta),
 | 
						|
                        r_label
 | 
						|
                        )
 | 
						|
 | 
						|
    def get_data_ratio(self):
 | 
						|
        """
 | 
						|
        Return the aspect ratio of the data itself.  For a polar plot,
 | 
						|
        this should always be 1.0
 | 
						|
        """
 | 
						|
        return 1.0
 | 
						|
 | 
						|
    # # # Interactive panning
 | 
						|
 | 
						|
    def can_zoom(self):
 | 
						|
        """
 | 
						|
        Return whether this Axes supports the zoom box button functionality.
 | 
						|
 | 
						|
        A polar Axes does not support zoom boxes.
 | 
						|
        """
 | 
						|
        return False
 | 
						|
 | 
						|
    def can_pan(self):
 | 
						|
        """
 | 
						|
        Return whether this Axes supports the pan/zoom button functionality.
 | 
						|
 | 
						|
        For a polar Axes, this is slightly misleading. Both panning and
 | 
						|
        zooming are performed by the same button. Panning is performed
 | 
						|
        in azimuth while zooming is done along the radial.
 | 
						|
        """
 | 
						|
        return True
 | 
						|
 | 
						|
    def start_pan(self, x, y, button):
 | 
						|
        angle = np.deg2rad(self.get_rlabel_position())
 | 
						|
        mode = ''
 | 
						|
        if button == 1:
 | 
						|
            epsilon = np.pi / 45.0
 | 
						|
            t, r = self.transData.inverted().transform((x, y))
 | 
						|
            if angle - epsilon <= t <= angle + epsilon:
 | 
						|
                mode = 'drag_r_labels'
 | 
						|
        elif button == 3:
 | 
						|
            mode = 'zoom'
 | 
						|
 | 
						|
        self._pan_start = types.SimpleNamespace(
 | 
						|
            rmax=self.get_rmax(),
 | 
						|
            trans=self.transData.frozen(),
 | 
						|
            trans_inverse=self.transData.inverted().frozen(),
 | 
						|
            r_label_angle=self.get_rlabel_position(),
 | 
						|
            x=x,
 | 
						|
            y=y,
 | 
						|
            mode=mode)
 | 
						|
 | 
						|
    def end_pan(self):
 | 
						|
        del self._pan_start
 | 
						|
 | 
						|
    def drag_pan(self, button, key, x, y):
 | 
						|
        p = self._pan_start
 | 
						|
 | 
						|
        if p.mode == 'drag_r_labels':
 | 
						|
            (startt, startr), (t, r) = p.trans_inverse.transform(
 | 
						|
                [(p.x, p.y), (x, y)])
 | 
						|
 | 
						|
            # Deal with theta
 | 
						|
            dt = np.rad2deg(startt - t)
 | 
						|
            self.set_rlabel_position(p.r_label_angle - dt)
 | 
						|
 | 
						|
            trans, vert1, horiz1 = self.get_yaxis_text1_transform(0.0)
 | 
						|
            trans, vert2, horiz2 = self.get_yaxis_text2_transform(0.0)
 | 
						|
            for t in self.yaxis.majorTicks + self.yaxis.minorTicks:
 | 
						|
                t.label1.set_va(vert1)
 | 
						|
                t.label1.set_ha(horiz1)
 | 
						|
                t.label2.set_va(vert2)
 | 
						|
                t.label2.set_ha(horiz2)
 | 
						|
 | 
						|
        elif p.mode == 'zoom':
 | 
						|
            (startt, startr), (t, r) = p.trans_inverse.transform(
 | 
						|
                [(p.x, p.y), (x, y)])
 | 
						|
 | 
						|
            # Deal with r
 | 
						|
            scale = r / startr
 | 
						|
            self.set_rmax(p.rmax / scale)
 | 
						|
 | 
						|
 | 
						|
# To keep things all self-contained, we can put aliases to the Polar classes
 | 
						|
# defined above. This isn't strictly necessary, but it makes some of the
 | 
						|
# code more readable, and provides a backwards compatible Polar API. In
 | 
						|
# particular, this is used by the :doc:`/gallery/specialty_plots/radar_chart`
 | 
						|
# example to override PolarTransform on a PolarAxes subclass, so make sure that
 | 
						|
# that example is unaffected before changing this.
 | 
						|
PolarAxes.PolarTransform = PolarTransform
 | 
						|
PolarAxes.PolarAffine = PolarAffine
 | 
						|
PolarAxes.InvertedPolarTransform = InvertedPolarTransform
 | 
						|
PolarAxes.ThetaFormatter = ThetaFormatter
 | 
						|
PolarAxes.RadialLocator = RadialLocator
 | 
						|
PolarAxes.ThetaLocator = ThetaLocator
 |