diff --git a/doc/users/next_whats_new/2015-10-31_polar_limits.rst b/doc/users/next_whats_new/2015-10-31_polar_limits.rst new file mode 100644 index 000000000000..6b1f74fb4028 --- /dev/null +++ b/doc/users/next_whats_new/2015-10-31_polar_limits.rst @@ -0,0 +1,37 @@ +Enhancements to polar plot +-------------------------- + +The polar axes transforms have been greatly re-factored to allow for more +customization of view limits and tick labelling. Additional options for view +limits allow for creating an annulus, a sector, or some combination of the two. + +The :meth:`~matplotlib.axes.projections.polar.PolarAxes.set_rorigin` method may +be used to provide an offset to the minimum plotting radius, producing an +annulus. + +The :meth:`~matplotlib.projections.polar.PolarAxes.set_theta_zero_location` now +has an optional :code:`offset` argument. This argument may be used to further +specify the zero location based on the given anchor point. + +.. figure:: ../../gallery/pie_and_polar_charts/images/sphx_glr_polar_scatter_001.png + :target: ../../gallery/pie_and_polar_charts/polar_scatter.html + :align: center + :scale: 50 + + Polar Offset Demo + +The :meth:`~matplotlib.axes.projections.polar.PolarAxes.set_thetamin` and +:meth:`~matplotlib.axes.projections.polar.PolarAxes.set_thetamax` methods may +be used to limit the range of angles plotted, producing sectors of a circle. + +.. figure:: ../../gallery/pie_and_polar_charts/images/sphx_glr_polar_scatter_002.png + :target: ../../gallery/pie_and_polar_charts/polar_scatter.html + :align: center + :scale: 50 + + Polar Sector Demo + +Previous releases allowed plots containing negative radii for which the +negative values are simply used as labels, and the real radius is shifted by +the configured minimum. This release also allows negative radii to be used for +grids and ticks, which were previously silently ignored. diff --git a/examples/api/radar_chart.py b/examples/api/radar_chart.py index 2660f56f6f2a..2f6fd8ac4e3d 100644 --- a/examples/api/radar_chart.py +++ b/examples/api/radar_chart.py @@ -37,11 +37,10 @@ def radar_factory(num_vars, frame='circle'): """ # calculate evenly-spaced axis angles theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False) - # rotate theta such that the first axis is at the top - theta += np.pi/2 def draw_poly_patch(self): - verts = unit_poly_verts(theta) + # rotate theta such that the first axis is at the top + verts = unit_poly_verts(theta + np.pi / 2) return plt.Polygon(verts, closed=True, edgecolor='k') def draw_circle_patch(self): @@ -60,6 +59,11 @@ class RadarAxes(PolarAxes): # define draw_frame method draw_patch = patch_dict[frame] + def __init__(self, *args, **kwargs): + super(RadarAxes, self).__init__(*args, **kwargs) + # rotate plot such that the first axis is at the top + self.set_theta_zero_location('N') + def fill(self, *args, **kwargs): """Override fill so that line is closed by default""" closed = kwargs.pop('closed', True) @@ -93,7 +97,7 @@ def _gen_axes_spines(self): # spine_type must be 'left', 'right', 'top', 'bottom', or `circle`. spine_type = 'circle' - verts = unit_poly_verts(theta) + verts = unit_poly_verts(theta + np.pi / 2) # close off polygon by repeating first vertex verts.append(verts[0]) path = Path(verts) diff --git a/examples/pie_and_polar_charts/polar_scatter.py b/examples/pie_and_polar_charts/polar_scatter.py index 5c9ca2c35f1c..f3ce26b7eb9e 100644 --- a/examples/pie_and_polar_charts/polar_scatter.py +++ b/examples/pie_and_polar_charts/polar_scatter.py @@ -3,8 +3,6 @@ Scatter plot on polar axis ========================== -Demo of scatter plot on a polar axis. - Size increases radially in this example and color increases with angle (just to verify the symbols are being scattered correctly). """ @@ -22,7 +20,37 @@ area = 200 * r**2 colors = theta -ax = plt.subplot(111, projection='polar') +fig = plt.figure() +ax = fig.add_subplot(111, projection='polar') c = ax.scatter(theta, r, c=colors, s=area, cmap='hsv', alpha=0.75) +############################################################################### +# Scatter plot on polar axis, with offset origin +# ---------------------------------------------- +# +# The main difference with the previous plot is the configuration of the origin +# radius, producing an annulus. Additionally, the theta zero location is set to +# rotate the plot. + +fig = plt.figure() +ax = fig.add_subplot(111, polar=True) +c = ax.scatter(theta, r, c=colors, s=area, cmap='hsv', alpha=0.75) + +ax.set_rorigin(-2.5) +ax.set_theta_zero_location('W', offset=10) + +############################################################################### +# Scatter plot on polar axis confined to a sector +# ----------------------------------------------- +# +# The main difference with the previous plots is the configuration of the +# theta start and end limits, producing a sector instead of a full circle. + +fig = plt.figure() +ax = fig.add_subplot(111, polar=True) +c = ax.scatter(theta, r, c=colors, s=area, cmap='hsv', alpha=0.75) + +ax.set_thetamin(45) +ax.set_thetamax(135) + plt.show() diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index e398969df785..e059c1ee375a 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -1,29 +1,23 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) -import six - -import math -import warnings +import collections import numpy as np -import matplotlib -rcParams = matplotlib.rcParams from matplotlib.axes import Axes import matplotlib.axis as maxis from matplotlib import cbook from matplotlib import docstring -from matplotlib.patches import Circle -from matplotlib.path import Path -from matplotlib.ticker import Formatter, Locator, FormatStrFormatter -from matplotlib.transforms import Affine2D, Affine2DBase, Bbox, \ - BboxTransformTo, IdentityTransform, Transform, TransformWrapper, \ - ScaledTranslation, blended_transform_factory, BboxTransformToMaxOnly +import matplotlib.patches as mpatches +import matplotlib.path as mpath +from matplotlib import rcParams +import matplotlib.ticker as mticker +import matplotlib.transforms as mtransforms import matplotlib.spines as mspines -class PolarTransform(Transform): +class PolarTransform(mtransforms.Transform): """ The base polar transform. This handles projection *theta* and *r* into Cartesian coordinate space *x* and *y*, but does not @@ -34,55 +28,53 @@ class PolarTransform(Transform): output_dims = 2 is_separable = False - def __init__(self, axis=None, use_rmin=True): - Transform.__init__(self) + def __init__(self, axis=None, use_rmin=True, + _apply_theta_transforms=True): + mtransforms.Transform.__init__(self) self._axis = axis self._use_rmin = use_rmin + self._apply_theta_transforms = _apply_theta_transforms def transform_non_affine(self, tr): xy = np.empty(tr.shape, float) - if self._axis is not None: - if self._use_rmin: - rmin = self._axis.viewLim.ymin - else: - rmin = 0 - theta_offset = self._axis.get_theta_offset() - theta_direction = self._axis.get_theta_direction() - else: - rmin = 0 - theta_offset = 0 - theta_direction = 1 t = tr[:, 0:1] r = tr[:, 1:2] x = xy[:, 0:1] y = xy[:, 1:2] - t *= theta_direction - t += theta_offset + # 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: + t *= self._axis.get_theta_direction() + t += self._axis.get_theta_offset() - r = r - rmin + if self._use_rmin and self._axis is not None: + r = r - self._axis.get_rorigin() mask = r < 0 x[:] = np.where(mask, np.nan, r * np.cos(t)) y[:] = np.where(mask, np.nan, r * np.sin(t)) return xy - transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__ + transform_non_affine.__doc__ = \ + mtransforms.Transform.transform_non_affine.__doc__ def transform_path_non_affine(self, path): vertices = path.vertices if len(vertices) == 2 and vertices[0, 0] == vertices[1, 0]: - return Path(self.transform(vertices), path.codes) + return mpath.Path(self.transform(vertices), path.codes) ipath = path.interpolated(path._interpolation_steps) - return Path(self.transform(ipath.vertices), ipath.codes) - transform_path_non_affine.__doc__ = Transform.transform_path_non_affine.__doc__ + return mpath.Path(self.transform(ipath.vertices), ipath.codes) + transform_path_non_affine.__doc__ = \ + mtransforms.Transform.transform_path_non_affine.__doc__ def inverted(self): - return PolarAxes.InvertedPolarTransform(self._axis, self._use_rmin) - inverted.__doc__ = Transform.inverted.__doc__ + return PolarAxes.InvertedPolarTransform(self._axis, self._use_rmin, + self._apply_theta_transforms) + inverted.__doc__ = mtransforms.Transform.inverted.__doc__ -class PolarAffine(Affine2DBase): +class PolarAffine(mtransforms.Affine2DBase): """ The affine part of the polar projection. Scales the output so that maximum radius rests on the edge of the axes circle. @@ -90,10 +82,10 @@ class PolarAffine(Affine2DBase): def __init__(self, scale_transform, limits): """ *limits* is the view limit of the data. The only part of - its bounds that is used is ymax (for the radius maximum). - The theta range is always fixed to (0, 2pi). + its bounds that is used is the y limits (for the radius limits). + The theta range is handled by the non-affine transform. """ - Affine2DBase.__init__(self) + mtransforms.Affine2DBase.__init__(self) self._scale_transform = scale_transform self._limits = limits self.set_children(scale_transform, limits) @@ -103,17 +95,17 @@ def get_matrix(self): if self._invalid: limits_scaled = self._limits.transformed(self._scale_transform) yscale = limits_scaled.ymax - limits_scaled.ymin - affine = Affine2D() \ + 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 - get_matrix.__doc__ = Affine2DBase.get_matrix.__doc__ + get_matrix.__doc__ = mtransforms.Affine2DBase.get_matrix.__doc__ -class InvertedPolarTransform(Transform): +class InvertedPolarTransform(mtransforms.Transform): """ The inverse of the polar transform, mapping Cartesian coordinate space *x* and *y* back to *theta* and *r*. @@ -122,24 +114,14 @@ class InvertedPolarTransform(Transform): output_dims = 2 is_separable = False - def __init__(self, axis=None, use_rmin=True): - Transform.__init__(self) + def __init__(self, axis=None, use_rmin=True, + _apply_theta_transforms=True): + mtransforms.Transform.__init__(self) self._axis = axis self._use_rmin = use_rmin + self._apply_theta_transforms = _apply_theta_transforms def transform_non_affine(self, xy): - if self._axis is not None: - if self._use_rmin: - rmin = self._axis.viewLim.ymin - else: - rmin = 0 - theta_offset = self._axis.get_theta_offset() - theta_direction = self._axis.get_theta_direction() - else: - rmin = 0 - theta_offset = 0 - theta_direction = 1 - x = xy[:, 0:1] y = xy[:, 1:] r = np.sqrt(x*x + y*y) @@ -152,38 +134,113 @@ def transform_non_affine(self, xy): theta = np.arccos(x / r) theta = np.where(y < 0, 2 * np.pi - theta, theta) - theta -= theta_offset - theta *= theta_direction - theta %= 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 - r += rmin + if self._use_rmin and self._axis is not None: + r += self._axis.get_rorigin() return np.concatenate((theta, r), 1) - transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__ + transform_non_affine.__doc__ = \ + mtransforms.Transform.transform_non_affine.__doc__ def inverted(self): - return PolarAxes.PolarTransform(self._axis, self._use_rmin) - inverted.__doc__ = Transform.inverted.__doc__ + return PolarAxes.PolarTransform(self._axis, self._use_rmin, + self._apply_theta_transforms) + inverted.__doc__ = mtransforms.Transform.inverted.__doc__ -class ThetaFormatter(Formatter): +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) + if rcParams['text.usetex'] and not rcParams['text.latex.unicode']: - return r"$%0.0f^\circ$" % ((x / np.pi) * 180.0) + format_str = r"${value:0.{digits:d}f}^\circ$" + return format_str.format(value=np.rad2deg(x), digits=digits) else: # we use unicode, rather than mathtext with \circ, so # that it will work correctly with any arbitrary font # (assuming it has a degree sign), whereas $5\circ$ # will only work correctly with one of the supported # math fonts (Computer Modern and STIX) - return "%0.0f\N{DEGREE SIGN}" % ((x / np.pi) * 180.0) + format_str = "{value:0.{digits:d}f}\N{DEGREE SIGN}" + return format_str.format(value=np.rad2deg(x), digits=digits) + + +class _AxisWrapper(object): + 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))) -class RadialLocator(Locator): + 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.arange(8) * 2 * np.pi / 8 + else: + return np.deg2rad(self.base()) + + def autoscale(self): + return self.base.autoscale() + + def pan(self, numsteps): + return self.base.pan(numsteps) + + def refresh(self): + return self.base.refresh() + + def view_limits(self, vmin, vmax): + vmin, vmax = np.rad2deg((vmin, vmax)) + return np.deg2rad(self.base.view_limits(vmin, vmax)) + + def zoom(self, direction): + return self.base.zoom(direction) + + +class RadialLocator(mticker.Locator): """ Used to locate radius ticks. @@ -192,12 +249,23 @@ class RadialLocator(Locator): :class:`~matplotlib.ticker.Locator` (which may be different depending on the scale of the *r*-axis. """ - def __init__(self, base): + def __init__(self, base, axes=None): self.base = base + self._axes = axes def __call__(self): - ticks = self.base() - return [x for x in ticks if x > 0] + show_all = True + # 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() + if self._axes.get_rmin() <= rorigin: + show_all = False + + if show_all: + return self.base() + else: + return [tick for tick in self.base() if tick > rorigin] def autoscale(self): return self.base.autoscale() @@ -213,7 +281,93 @@ def refresh(self): def view_limits(self, vmin, vmax): vmin, vmax = self.base.view_limits(vmin, vmax) - return 0, vmax + return mtransforms.nonsingular(min(0, vmin), vmax) + + +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 : tuple of 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): + mtransforms.Bbox.__init__(self, + np.array([[0.0, 0.0], [1.0, 1.0]], np.float), + **kwargs) + self._center = center + self._viewLim = viewLim + self._originLim = originLim + self.set_children(viewLim, originLim) + + def __repr__(self): + return "_WedgeBbox(%r, %r, %r)" % (self._center, self._viewLim, + self._originLim) + + def get_points(self): + 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] + if h < w: + deltah = (w - h) / 2.0 + deltaw = 0.0 + elif w < h: + deltah = 0.0 + deltaw = (h - w) / 2.0 + else: + deltah = 0.0 + deltaw = 0.0 + self._points += np.array([[-deltaw, -deltah], [deltaw, deltah]]) + + self._invalid = 0 + + return self._points + get_points.__doc__ = mtransforms.Bbox.get_points.__doc__ class PolarAxes(Axes): @@ -230,7 +384,8 @@ def __init__(self, *args, **kwargs): """ self._default_theta_offset = kwargs.pop('theta_offset', 0) self._default_theta_direction = kwargs.pop('theta_direction', 1) - self._default_rlabel_position = kwargs.pop('rlabel_position', 22.5) + self._default_rlabel_position = np.deg2rad( + kwargs.pop('rlabel_position', 22.5)) Axes.__init__(self, *args, **kwargs) self.set_aspect('equal', adjustable='box', anchor='C') @@ -244,17 +399,29 @@ def cla(self): self.xaxis.set_major_formatter(self.ThetaFormatter()) self.xaxis.isDefault_majfmt = True - angles = np.arange(0.0, 360.0, 45.0) - self.set_thetagrids(angles) - self.yaxis.set_major_locator(self.RadialLocator(self.yaxis.get_major_locator())) + 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.xaxis.set_major_locator( + self.ThetaLocator(self.xaxis.get_major_locator())) self.grid(rcParams['polaraxes.grid']) self.xaxis.set_ticks_position('none') + inner = self.spines.get('inner', None) + if inner: + inner.set_visible(False) self.yaxis.set_ticks_position('none') - self.yaxis.set_tick_params(label1On=True) # Why do we need to turn on yaxis tick labels, but # xaxis tick labels are already on? + self.yaxis.set_tick_params(label1On=True) + self.yaxis.set_major_locator( + self.RadialLocator(self.yaxis.get_major_locator(), self)) + self.set_rorigin(None) self.set_theta_offset(self._default_theta_offset) self.set_theta_direction(self._default_theta_direction) @@ -269,62 +436,98 @@ def _init_axis(self): self._update_transScale() def _set_lim_and_transforms(self): - self.transAxes = BboxTransformTo(self.bbox) + # 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 = mtransforms.composite_transform_factory( + 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 = TransformWrapper(IdentityTransform()) + 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) - - # This one is not aware of rmin - self.transPureProjection = self.PolarTransform(self, use_rmin=False) + self.transProjection = self.PolarTransform( + self, + _apply_theta_transforms=False) + # 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.viewLim) + self.transProjectionAffine = self.PolarAffine(self.transScale, + self._originViewLim) # The complete data transformation stack -- from data all the # way to display coordinates - self.transData = self.transScale + self.transProjection + \ - (self.transProjectionAffine + self.transAxes) + 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 == 1.0 at - # the edge of the axis circle. + # equivalent to transData, except it always puts r == 0.0 and r == 1.0 + # at the edge of the axis circles. self._xaxis_transform = ( - self.transPureProjection + - self.PolarAffine(IdentityTransform(), Bbox.unit()) + - self.transAxes) - # The theta labels are moved from radius == 0.0 to radius == 1.1 - self._theta_label1_position = Affine2D().translate(0.0, 1.1) + 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_text1_transform = ( - self._theta_label1_position + + flipr_transform + + mtransforms.Affine2D().translate(0.0, 0.1) + self._xaxis_transform) - self._theta_label2_position = Affine2D().translate(0.0, 1.0 / 1.1) self._xaxis_text2_transform = ( - self._theta_label2_position + + flipr_transform + + mtransforms.Affine2D().translate(0.0, -0.1) + 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 0.0 to - # 2pi. + # axis so the gridlines from 0.0 to 1.0, now go from thetamin to + # thetamax. self._yaxis_transform = ( - Affine2D().scale(np.pi * 2.0, 1.0) + + 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 = ScaledTranslation( - self._default_rlabel_position, 0.0, Affine2D()) - self._yaxis_text_transform = ( - self._r_label_position + - Affine2D().scale(1.0 / 360.0, 1.0) + - self._yaxis_transform - ) - - def get_xaxis_transform(self,which='grid'): - if which not in ['tick1','tick2','grid']: + 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'): + if which not in ['tick1', 'tick2', 'grid']: msg = "'which' must be one of [ 'tick1' | 'tick2' | 'grid' ]" raise ValueError(msg) return self._xaxis_transform @@ -335,71 +538,173 @@ def get_xaxis_text1_transform(self, pad): def get_xaxis_text2_transform(self, pad): return self._xaxis_text2_transform, 'center', 'center' - def get_yaxis_transform(self,which='grid'): - if which not in ['tick1','tick2','grid']: + 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: msg = "'which' must be on of [ 'tick1' | 'tick2' | 'grid' ]" raise ValueError(msg) - return self._yaxis_transform def get_yaxis_text1_transform(self, pad): - angle = self.get_rlabel_position() - if angle < 90.: - return self._yaxis_text_transform, 'bottom', 'left' - elif angle < 180.: - return self._yaxis_text_transform, 'bottom', 'right' - elif angle < 270.: - return self._yaxis_text_transform, 'top', 'right' + thetamin, thetamax = self._realViewLim.intervalx + full = _is_full_circle_rad(thetamin, thetamax) + if full: + angle = self.get_rlabel_position() else: - return self._yaxis_text_transform, 'top', 'left' + angle = np.rad2deg(thetamin) + if angle < 0: + angle += 360 + angle %= 360 - def get_yaxis_text2_transform(self, pad): - angle = self.get_rlabel_position() - if angle < 90.: - return self._yaxis_text_transform, 'top', 'right' - elif angle < 180.: - return self._yaxis_text_transform, 'top', 'left' - elif angle < 270.: - return self._yaxis_text_transform, 'bottom', 'left' - else: - return self._yaxis_text_transform, 'bottom', 'right' + # NOTE: Due to a bug, previous code always used bottom left, contrary + # to its original intentions here. + valign = [['top', 'bottom', 'bottom', 'top'], + # ['bottom', 'bottom', 'top', 'top']] + ['bottom', 'bottom', 'bottom', 'bottom']] + halign = [['left', 'left', 'right', 'right'], + # ['left', 'right', 'right', 'left']] + ['left', 'left', 'left', 'left']] - def _gen_axes_patch(self): - return Circle((0.5, 0.5), 0.5) + ind = np.digitize([angle], np.arange(0, 361, 90))[0] - 1 - def _gen_axes_spines(self): - return {'polar':mspines.Spine.circular_spine(self, - (0.5, 0.5), 0.5)} + return self._yaxis_text_transform, valign[full][ind], halign[full][ind] - def set_rmax(self, rmax): - self.viewLim.y1 = rmax + def get_yaxis_text2_transform(self, pad): + thetamin, thetamax = self._realViewLim.intervalx + full = _is_full_circle_rad(thetamin, thetamax) + if full: + angle = self.get_rlabel_position() + else: + angle = np.rad2deg(thetamax) + if angle < 0: + angle += 360 + angle %= 360 + + # NOTE: Due to a bug, previous code always used top right, contrary to + # its original intentions here. + valign = [['bottom', 'top', 'top', 'bottom'], + # ['top', 'top', 'bottom', 'bottom']] + ['top', 'top', 'top', 'top']] + halign = [['right', 'right', 'left', 'left'], + # ['right', 'left', 'left', 'right']] + ['right', 'right', 'right', 'right']] + + ind = np.digitize([angle], np.arange(0, 361, 90))[0] - 1 + + return self._yaxis_text_transform, valign[full][ind], halign[full][ind] + + def draw(self, *args, **kwargs): + thetamin, thetamax = self._realViewLim.intervalx + thetamin *= 180 / np.pi + thetamax *= 180 / np.pi + if thetamin > thetamax: + thetamin, thetamax = thetamax, thetamin + rmin, rmax = self._realViewLim.intervaly - self.get_rorigin() + + 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_point((0.5, 0.5)) + self.patch.set_center(center) + self.patch.set_theta1(thetamin) + self.patch.set_theta2(thetamax) + + edge, _ = self.transWedge.transform_point((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) - def get_rmax(self): - return self.viewLim.ymax + Axes.draw(self, *args, **kwargs) - def set_rmin(self, rmin): - self.viewLim.y0 = rmin + def _gen_axes_patch(self): + return mpatches.Wedge((0.5, 0.5), 0.5, 0.0, 360.0) - def get_rmin(self): - return self.viewLim.ymin + def _gen_axes_spines(self): + spines = collections.OrderedDict([ + ('polar', mspines.Spine.arc_spine(self, 'top', + (0.5, 0.5), 0.5, 0.0, 360.0)), + ('start', mspines.Spine.linear_spine(self, 'left')), + ('end', mspines.Spine.linear_spine(self, 'right')), + ('inner', mspines.Spine.arc_spine(self, 'bottom', + (0.5, 0.5), 0.0, 0.0, 360.0)) + ]) + 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): + self.viewLim.x1 = np.deg2rad(thetamax) + + def get_thetamax(self): + return np.rad2deg(self.viewLim.xmax) + + def set_thetamin(self, thetamin): + self.viewLim.x0 = np.deg2rad(thetamin) + + def get_thetamin(self): + return np.rad2deg(self.viewLim.xmin) + + def set_thetalim(self, *args, **kwargs): + if 'thetamin' in kwargs: + kwargs['xmin'] = np.deg2rad(kwargs.pop('thetamin')) + if 'thetamax' in kwargs: + kwargs['xmax'] = np.deg2rad(kwargs.pop('thetamax')) + return tuple(np.rad2deg(self.set_xlim(*args, **kwargs))) def set_theta_offset(self, offset): """ Set the offset for the location of 0 in radians. """ - self._theta_offset = offset + 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 + return self._theta_offset.get_matrix()[0, 2] - def set_theta_zero_location(self, loc): + def set_theta_zero_location(self, loc, offset=0.0): """ Sets the location of theta's zero. (Calls set_theta_offset with the correct value in radians under the hood.) - May be one of "N", "NW", "W", "SW", "S", "SE", "E", or "NE". + loc : str + May be one of "N", "NW", "W", "SW", "S", "SE", "E", or "NE". + + offset : float, optional + 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, @@ -409,8 +714,8 @@ def set_theta_zero_location(self, loc): 'S': np.pi * 1.5, 'SE': np.pi * 1.75, 'E': 0, - 'NE': np.pi * 0.25 } - return self.set_theta_offset(mapping[loc]) + 'NE': np.pi * 0.25} + return self.set_theta_offset(mapping[loc] + np.deg2rad(offset)) def set_theta_direction(self, direction): """ @@ -422,14 +727,17 @@ def set_theta_direction(self, direction): counterclockwise, anticlockwise, 1: Theta increases in the counterclockwise direction """ + mtx = self._direction.get_matrix() if direction in ('clockwise',): - self._direction = -1 + mtx[0, 0] = -1 elif direction in ('counterclockwise', 'anticlockwise'): - self._direction = 1 + mtx[0, 0] = 1 elif direction in (1, -1): - self._direction = direction + mtx[0, 0] = direction else: - raise ValueError("direction must be 1, -1, clockwise or counterclockwise") + raise ValueError( + "direction must be 1, -1, clockwise or counterclockwise") + self._direction.invalidate() def get_theta_direction(self): """ @@ -441,7 +749,25 @@ def get_theta_direction(self): 1: Theta increases in the counterclockwise direction """ - return self._direction + return self._direction.get_matrix()[0, 0] + + def set_rmax(self, rmax): + self.viewLim.y1 = rmax + + def get_rmax(self): + return self.viewLim.ymax + + def set_rmin(self, rmin): + self.viewLim.y0 = rmin + + def get_rmin(self): + return self.viewLim.ymin + + def set_rorigin(self, rorigin): + self._originViewLim.locked_y0 = rorigin + + def get_rorigin(self): + return self._originViewLim.y0 def set_rlim(self, *args, **kwargs): if 'rmin' in kwargs: @@ -457,7 +783,7 @@ def get_rlabel_position(self): float The theta position of the radius labels in degrees. """ - return self._r_label_position.to_values()[4] + return np.rad2deg(self._r_label_position.get_matrix()[0, 2]) def set_rlabel_position(self, value): """Updates the theta position of the radius labels. @@ -467,16 +793,16 @@ def set_rlabel_position(self, value): value : number The angular position of the radius labels in degrees. """ - self._r_label_position._t = (value, 0.0) - self._r_label_position.invalidate() + self._r_label_position.clear().translate(np.deg2rad(value), 0.0) def set_yscale(self, *args, **kwargs): Axes.set_yscale(self, *args, **kwargs) self.yaxis.set_major_locator( - self.RadialLocator(self.yaxis.get_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) @@ -514,10 +840,7 @@ def set_thetagrids(self, angles, labels=None, frac=None, fmt=None, if labels is not None: self.set_xticklabels(labels) elif fmt is not None: - self.xaxis.set_major_formatter(FormatStrFormatter(fmt)) - if frac is not None: - self._theta_label1_position.clear().translate(0.0, frac) - self._theta_label2_position.clear().translate(0.0, 1.0 / frac) + self.xaxis.set_major_formatter(mticker.FormatStrFormatter(fmt)) for t in self.xaxis.get_ticklabels(): t.update(kwargs) return self.xaxis.get_ticklines(), self.xaxis.get_ticklabels() @@ -549,15 +872,12 @@ def set_rgrids(self, radii, labels=None, angle=None, fmt=None, # Make sure we take into account unitized data radii = self.convert_xunits(radii) radii = np.asarray(radii) - rmin = radii.min() - if rmin <= 0: - raise ValueError('radial grids must be strictly positive') self.set_yticks(radii) if labels is not None: self.set_yticklabels(labels) elif fmt is not None: - self.yaxis.set_major_formatter(FormatStrFormatter(fmt)) + self.yaxis.set_major_formatter(mticker.FormatStrFormatter(fmt)) if angle is None: angle = self.get_rlabel_position() self.set_rlabel_position(angle) @@ -567,18 +887,17 @@ def set_rgrids(self, radii, labels=None, angle=None, fmt=None, def set_xscale(self, scale, *args, **kwargs): if scale != 'linear': - raise NotImplementedError("You can not set the xscale on a polar plot.") - - def set_xlim(self, *args, **kargs): - # The xlim is fixed, no matter what you do - self.viewLim.intervalx = (0.0, np.pi * 2.0) + raise NotImplementedError( + "You can not set the xscale on a polar plot.") def format_coord(self, theta, r): """ Return a format string formatting the coordinate using Unicode characters. """ - theta /= math.pi + if theta < 0: + theta += 2 * np.pi + theta /= np.pi return ('\N{GREEK SMALL LETTER THETA}=%0.3f\N{GREEK SMALL LETTER PI} ' '(%0.3f\N{DEGREE SIGN}), r=%0.3f') % (theta, theta * 180.0, r) @@ -589,7 +908,7 @@ def get_data_ratio(self): ''' return 1.0 - ### Interactive panning + # # # Interactive panning def can_zoom(self): """ @@ -599,7 +918,7 @@ def can_zoom(self): """ return False - def can_pan(self) : + def can_pan(self): """ Return *True* if this axes supports the pan/zoom button functionality. @@ -621,14 +940,13 @@ def start_pan(self, x, y, button): mode = 'zoom' self._pan_start = cbook.Bunch( - 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 - ) + 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 @@ -662,8 +980,6 @@ def drag_pan(self, button, key, x, y): startt, startr = p.trans_inverse.transform_point((p.x, p.y)) t, r = p.trans_inverse.transform_point((x, y)) - dr = r - startr - # Deal with r scale = r / startr self.set_rmax(p.rmax / scale) @@ -677,6 +993,7 @@ def drag_pan(self, button, key, x, y): PolarAxes.InvertedPolarTransform = InvertedPolarTransform PolarAxes.ThetaFormatter = ThetaFormatter PolarAxes.RadialLocator = RadialLocator +PolarAxes.ThetaLocator = ThetaLocator # These are a couple of aborted attempts to project a polar plot using @@ -698,7 +1015,8 @@ def drag_pan(self, button, key, x, y): # vertices = self.transform(vertices) # result = np.zeros((len(vertices) * 3 - 2, 2), float) -# codes = mpath.Path.CURVE4 * np.ones((len(vertices) * 3 - 2, ), mpath.Path.code_type) +# codes = mpath.Path.CURVE4 * np.ones((len(vertices) * 3 - 2, ), +# mpath.Path.code_type) # result[0] = vertices[0] # codes[0] = mpath.Path.MOVETO @@ -735,8 +1053,8 @@ def drag_pan(self, button, key, x, y): # result[3::3] = p1 -# print vertices[-2:] -# print result[-2:] +# print(vertices[-2:]) +# print(result[-2:]) # return mpath.Path(result, codes) @@ -750,12 +1068,13 @@ def drag_pan(self, button, key, x, y): # maxtd = td.max() # interpolate = np.ceil(maxtd / halfpi) -# print "interpolate", interpolate +# print("interpolate", interpolate) # if interpolate > 1.0: # vertices = self.interpolate(vertices, interpolate) # result = np.zeros((len(vertices) * 3 - 2, 2), float) -# codes = mpath.Path.CURVE4 * np.ones((len(vertices) * 3 - 2, ), mpath.Path.code_type) +# codes = mpath.Path.CURVE4 * np.ones((len(vertices) * 3 - 2, ), +# mpath.Path.code_type) # result[0] = vertices[0] # codes[0] = mpath.Path.MOVETO @@ -777,16 +1096,19 @@ def drag_pan(self, button, key, x, y): # result[1::3, 0] = t0 + (tkappa * td_scaled) # result[1::3, 1] = r0*hyp_kappa -# # result[1::3, 1] = r0 / np.cos(tkappa * td_scaled) # np.sqrt(r0*r0 + ravg_kappa*ravg_kappa) +# # result[1::3, 1] = r0 / np.cos(tkappa * td_scaled) +# # np.sqrt(r0*r0 + ravg_kappa*ravg_kappa) # result[2::3, 0] = t1 - (tkappa * td_scaled) # result[2::3, 1] = r1*hyp_kappa -# # result[2::3, 1] = r1 / np.cos(tkappa * td_scaled) # np.sqrt(r1*r1 + ravg_kappa*ravg_kappa) +# # result[2::3, 1] = r1 / np.cos(tkappa * td_scaled) +# # np.sqrt(r1*r1 + ravg_kappa*ravg_kappa) # result[3::3, 0] = t1 # result[3::3, 1] = r1 -# print vertices[:6], result[:6], t0[:6], t1[:6], td[:6], td_scaled[:6], tkappa +# print(vertices[:6], result[:6], t0[:6], t1[:6], td[:6], +# td_scaled[:6], tkappa) # result = self.transform(result) # return mpath.Path(result, codes) # transform_path_non_affine = transform_path diff --git a/lib/matplotlib/spines.py b/lib/matplotlib/spines.py index c6b04fe43dba..b2f0f8d9fca1 100644 --- a/lib/matplotlib/spines.py +++ b/lib/matplotlib/spines.py @@ -32,10 +32,11 @@ class Spine(mpatches.Patch): Spines are subclasses of class:`~matplotlib.patches.Patch`, and inherit much of their behavior. - Spines draw a line or a circle, depending if - function:`~matplotlib.spines.Spine.set_patch_line` or - function:`~matplotlib.spines.Spine.set_patch_circle` has been - called. Line-like is the default. + Spines draw a line, a circle, or an arc depending if + function:`~matplotlib.spines.Spine.set_patch_line`, + function:`~matplotlib.spines.Spine.set_patch_circle`, or + function:`~matplotlib.spines.Spine.set_patch_arc` has been called. + Line-like is the default. """ def __str__(self): @@ -77,10 +78,11 @@ def __init__(self, axes, spine_type, path, **kwargs): self._path = path # To support drawing both linear and circular spines, this - # class implements Patch behavior two ways. If + # class implements Patch behavior three ways. If # self._patch_type == 'line', behave like a mpatches.PathPatch # instance. If self._patch_type == 'circle', behave like a - # mpatches.Ellipse instance. + # mpatches.Ellipse instance. If self._patch_type == 'arc', behave like + # a mpatches.Arc instance. self._patch_type = 'line' # Behavior copied from mpatches.Ellipse: @@ -102,13 +104,25 @@ def get_smart_bounds(self): """get whether the spine has smart bounds""" return self._smart_bounds + def set_patch_arc(self, center, radius, theta1, theta2): + """set the spine to be arc-like""" + self._patch_type = 'arc' + self._center = center + self._width = radius * 2 + self._height = radius * 2 + self._theta1 = theta1 + self._theta2 = theta2 + self._path = mpath.Path.arc(theta1, theta2) + # arc drawn on axes transform + self.set_transform(self.axes.transAxes) + self.stale = True + def set_patch_circle(self, center, radius): """set the spine to be circular""" self._patch_type = 'circle' self._center = center self._width = radius * 2 self._height = radius * 2 - self._angle = 0 # circle drawn on axes transform self.set_transform(self.axes.transAxes) self.stale = True @@ -125,18 +139,17 @@ def _recompute_transform(self): maxes it very important to call the accessor method and not directly access the transformation member variable. """ - assert self._patch_type == 'circle' + assert self._patch_type in ('arc', 'circle') center = (self.convert_xunits(self._center[0]), self.convert_yunits(self._center[1])) width = self.convert_xunits(self._width) height = self.convert_yunits(self._height) self._patch_transform = mtransforms.Affine2D() \ .scale(width * 0.5, height * 0.5) \ - .rotate_deg(self._angle) \ .translate(*center) def get_patch_transform(self): - if self._patch_type == 'circle': + if self._patch_type in ('arc', 'circle'): self._recompute_transform() return self._patch_transform else: @@ -255,17 +268,48 @@ def _adjust_location(self): else: low, high = self._bounds - v1 = self._path.vertices - assert v1.shape == (2, 2), 'unexpected vertices shape' - if self.spine_type in ['left', 'right']: - v1[0, 1] = low - v1[1, 1] = high - elif self.spine_type in ['bottom', 'top']: - v1[0, 0] = low - v1[1, 0] = high + if self._patch_type == 'arc': + if self.spine_type in ('bottom', 'top'): + try: + direction = self.axes.get_theta_direction() + except AttributeError: + direction = 1 + try: + offset = self.axes.get_theta_offset() + except AttributeError: + offset = 0 + low = low * direction + offset + high = high * direction + offset + if low > high: + low, high = high, low + + self._path = mpath.Path.arc(np.rad2deg(low), np.rad2deg(high)) + + if self.spine_type == 'bottom': + rmin, rmax = self.axes.viewLim.intervaly + try: + rorigin = self.axes.get_rorigin() + except AttributeError: + rorigin = rmin + scaled_diameter = (rmin - rorigin) / (rmax - rorigin) + self._height = scaled_diameter + self._width = scaled_diameter + + else: + raise ValueError('unable to set bounds for spine "%s"' % + self.spine_type) else: - raise ValueError('unable to set bounds for spine "%s"' % - self.spine_type) + v1 = self._path.vertices + assert v1.shape == (2, 2), 'unexpected vertices shape' + if self.spine_type in ['left', 'right']: + v1[0, 1] = low + v1[1, 1] = high + elif self.spine_type in ['bottom', 'top']: + v1[0, 0] = low + v1[1, 0] = high + else: + raise ValueError('unable to set bounds for spine "%s"' % + self.spine_type) @allow_rasterization def draw(self, renderer): @@ -463,6 +507,17 @@ def linear_spine(cls, axes, spine_type, **kwargs): return result + @classmethod + def arc_spine(cls, axes, spine_type, center, radius, theta1, theta2, + **kwargs): + """ + (classmethod) Returns an arc :class:`Spine`. + """ + path = mpath.Path.arc(theta1, theta2) + result = cls(axes, spine_type, path, **kwargs) + result.set_patch_arc(center, radius, theta1, theta2) + return result + @classmethod def circular_spine(cls, axes, center, radius, **kwargs): """ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.pdf b/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.pdf index a41459350312..e4496e4d1fa5 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.png b/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.png index 9524d221552b..6682d5c93a2d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.png and b/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.svg b/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.svg index 02e717e4593f..14cb8b2720c4 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/markevery_polar.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - - + + + + + + + + + + - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - + - - + - - + - - + - - + - - + - - + - - + - - + - - + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.pdf index 3b6519f7d946..e28200a785d6 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.png index 8a137d8a64d8..141c5c3cb3ca 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.png and b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.svg index 6c4ea0f01cd6..3121c5400aab 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - - - - - - - - - - + + @@ -375,17 +112,17 @@ Q 18.5 74.21875 25 74.21875 z " id="DejaVuSans-b0"/> - + - - + + @@ -432,7 +169,7 @@ Q 14.890625 38.140625 10.796875 36.28125 z " id="DejaVuSans-35"/> - + @@ -440,10 +177,10 @@ z - - + + @@ -479,7 +216,7 @@ Q 23.96875 32.421875 30.609375 32.421875 z " id="DejaVuSans-39"/> - + @@ -487,10 +224,10 @@ z - - + + @@ -541,7 +278,7 @@ Q 46.96875 40.921875 40.578125 39.3125 z " id="DejaVuSans-33"/> - + @@ -550,10 +287,10 @@ z - - + + @@ -598,7 +335,7 @@ Q 18.3125 60.0625 18.3125 54.390625 z " id="DejaVuSans-38"/> - + @@ -607,10 +344,10 @@ z - - + + @@ -640,7 +377,7 @@ Q 31.109375 20.453125 19.1875 8.296875 z " id="DejaVuSans-32"/> - + @@ -649,10 +386,10 @@ z - - + + @@ -667,7 +404,7 @@ L 8.203125 64.59375 z " id="DejaVuSans-37"/> - + @@ -676,14 +413,14 @@ z - - + + - + @@ -694,103 +431,43 @@ L 417.388052 338.188052 - - + + - + - + - + - - + + - - + + - + - - + + + + + + + + + + + + + + + - + - - - + + + - + - + - - - + + + - + - + @@ -1529,19 +1191,282 @@ L 468 216 + + + + + + + + + + + + + + + + + - - + - + @@ -1721,16 +1646,42 @@ z - - + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.pdf index be50113a1ed9..c9fa69334e88 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.png index 106b8fdb1cf8..6d5aa60ab3d2 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.png and b/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.svg index 1a3fb1fffc75..919c44c5bf16 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_coords.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - - - - +" style="fill:#ff0000;opacity:0.5;"/> - - - - - - - - +L 0 3.5 +" id="mbdbe0e235e" style="stroke:#000000;stroke-width:0.8;"/> - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - - - - + +L -3.5 0 +" id="ma295a3440f" style="stroke:#000000;stroke-width:0.8;"/> - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + - - - - - - + - + + + + + + + + + + + + + - - + - + - - - - - - + + + + + + - - + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_negative_rmin.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_negative_rmin.pdf new file mode 100644 index 000000000000..b1e2e21d1d0b Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/polar_negative_rmin.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_negative_rmin.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_negative_rmin.png new file mode 100644 index 000000000000..bcebe7a955eb Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/polar_negative_rmin.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_negative_rmin.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_negative_rmin.svg new file mode 100644 index 000000000000..dc37defea1ed --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_negative_rmin.svg @@ -0,0 +1,1663 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.pdf index bb3ea0a2b5c8..c7258af99a41 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.png index 46bc3a796b70..b05d60664d52 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.png and b/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.svg index 2c74df2304cf..be5229d4f7d5 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_rlabel_position.svg @@ -2,7 +2,7 @@ - + - - - - - - + @@ -99,7 +112,7 @@ Q 18.5 74.21875 25 74.21875 z " id="DejaVuSans-b0"/> - + @@ -107,9 +120,9 @@ z - + @@ -156,7 +169,7 @@ Q 14.890625 38.140625 10.796875 36.28125 z " id="DejaVuSans-35"/> - + @@ -165,9 +178,9 @@ z - + @@ -203,7 +216,7 @@ Q 23.96875 32.421875 30.609375 32.421875 z " id="DejaVuSans-39"/> - + @@ -212,9 +225,9 @@ z - + @@ -265,7 +278,7 @@ Q 46.96875 40.921875 40.578125 39.3125 z " id="DejaVuSans-33"/> - + @@ -275,9 +288,9 @@ z - + @@ -322,7 +335,7 @@ Q 18.3125 60.0625 18.3125 54.390625 z " id="DejaVuSans-38"/> - + @@ -332,9 +345,9 @@ z - + @@ -364,7 +377,7 @@ Q 31.109375 20.453125 19.1875 8.296875 z " id="DejaVuSans-32"/> - + @@ -374,9 +387,9 @@ z - + @@ -391,7 +404,7 @@ L 8.203125 64.59375 z " id="DejaVuSans-37"/> - + @@ -401,13 +414,13 @@ z - + - + @@ -419,99 +432,69 @@ L 417.388052 338.188052 - + @@ -523,7 +506,7 @@ L 10.6875 0 z " id="DejaVuSans-2e"/> - + @@ -532,103 +515,103 @@ z - + - + @@ -637,189 +620,99 @@ L 364.32 216 - + @@ -855,7 +748,7 @@ Q 48.484375 72.75 52.59375 71.296875 z " id="DejaVuSans-36"/> - + @@ -864,193 +757,193 @@ z - + - + @@ -1059,193 +952,193 @@ L 433.44 216 - + - + @@ -1253,19 +1146,65 @@ L 468 216 + + + - - + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.pdf index c9d5247eb6e2..49fdb376a72d 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.png index 2a4234c7a7b7..afd5f3faf57e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.png and b/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.svg index 178d23601ff3..0591300b41d9 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_rmin.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - + + @@ -336,17 +112,17 @@ Q 18.5 74.21875 25 74.21875 z " id="DejaVuSans-b0"/> - + - - + + @@ -393,7 +169,7 @@ Q 14.890625 38.140625 10.796875 36.28125 z " id="DejaVuSans-35"/> - + @@ -401,10 +177,10 @@ z - - + + @@ -440,7 +216,7 @@ Q 23.96875 32.421875 30.609375 32.421875 z " id="DejaVuSans-39"/> - + @@ -448,10 +224,10 @@ z - - + + @@ -502,7 +278,7 @@ Q 46.96875 40.921875 40.578125 39.3125 z " id="DejaVuSans-33"/> - + @@ -511,10 +287,10 @@ z - - + + @@ -559,7 +335,7 @@ Q 18.3125 60.0625 18.3125 54.390625 z " id="DejaVuSans-38"/> - + @@ -568,10 +344,10 @@ z - - + + @@ -601,7 +377,7 @@ Q 31.109375 20.453125 19.1875 8.296875 z " id="DejaVuSans-32"/> - + @@ -610,10 +386,10 @@ z - - + + @@ -628,7 +404,7 @@ L 8.203125 64.59375 z " id="DejaVuSans-37"/> - + @@ -637,14 +413,14 @@ z - - + + - + @@ -655,55 +431,55 @@ L 410.188052 338.188052 - - + + @@ -745,7 +521,7 @@ Q 48.484375 72.75 52.59375 71.296875 z " id="DejaVuSans-36"/> - + @@ -753,104 +529,74 @@ z - - + + - + @@ -858,104 +604,104 @@ L 322.56 216 - - + + - + @@ -963,104 +709,104 @@ L 345.6 216 - - + + - + @@ -1068,194 +814,104 @@ L 368.64 216 - - + + - + @@ -1263,194 +919,194 @@ L 391.68 216 - - + + - + @@ -1458,194 +1114,194 @@ L 414.72 216 - - + + - + @@ -1653,194 +1309,194 @@ L 437.76 216 - - + + - + @@ -1848,19 +1504,302 @@ L 460.8 216 + + + + + + - - + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_rorigin.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_rorigin.pdf new file mode 100644 index 000000000000..327b4aad6a6c Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/polar_rorigin.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_rorigin.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_rorigin.png new file mode 100644 index 000000000000..636eab4c6163 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/polar_rorigin.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_rorigin.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_rorigin.svg new file mode 100644 index 000000000000..861919ed4ea8 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_rorigin.svg @@ -0,0 +1,2034 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.pdf index dbc674c59555..365c252dcadb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.png index e9755929a197..13af040c6cbc 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.png and b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.svg index 9e1ad9d99aaa..d20989dfdc3d 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_position.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - + + @@ -383,17 +112,17 @@ Q 18.5 74.21875 25 74.21875 z " id="DejaVuSans-b0"/> - + - - + + @@ -440,7 +169,7 @@ Q 14.890625 38.140625 10.796875 36.28125 z " id="DejaVuSans-35"/> - + @@ -448,10 +177,10 @@ z - - + + @@ -487,7 +216,7 @@ Q 23.96875 32.421875 30.609375 32.421875 z " id="DejaVuSans-39"/> - + @@ -495,10 +224,10 @@ z - - + + @@ -549,7 +278,7 @@ Q 46.96875 40.921875 40.578125 39.3125 z " id="DejaVuSans-33"/> - + @@ -558,10 +287,10 @@ z - - + + @@ -606,7 +335,7 @@ Q 18.3125 60.0625 18.3125 54.390625 z " id="DejaVuSans-38"/> - + @@ -615,10 +344,10 @@ z - - + + @@ -648,7 +377,7 @@ Q 31.109375 20.453125 19.1875 8.296875 z " id="DejaVuSans-32"/> - + @@ -657,10 +386,10 @@ z - - + + @@ -675,7 +404,7 @@ L 8.203125 64.59375 z " id="DejaVuSans-37"/> - + @@ -684,14 +413,14 @@ z - - + + - + @@ -702,73 +431,49 @@ L 115.2 216 - - + + - + - + - + - - + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1580,19 +1270,346 @@ L 165.811948 93.811948 + + + + + + - - + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_wedge.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_wedge.pdf new file mode 100644 index 000000000000..50dff4f92950 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_wedge.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_wedge.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_wedge.png new file mode 100644 index 000000000000..ac952eaf3e7e Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_wedge.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_wedge.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_wedge.svg new file mode 100644 index 000000000000..41760e2897ba --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_theta_wedge.svg @@ -0,0 +1,3904 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_units.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_units.pdf index 16f1947f10ff..4e4b2533e201 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_units.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/polar_units.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_units.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_units.png index 40a154ed6f5e..fa5be4aa1828 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_units.png and b/lib/matplotlib/tests/baseline_images/test_axes/polar_units.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_units.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_units.svg index c020a277164c..7e18f195c92a 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/polar_units.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_units.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - + + @@ -106,17 +112,17 @@ Q 18.5 74.21875 25 74.21875 z " id="DejaVuSans-b0"/> - + - - + + @@ -163,7 +169,7 @@ Q 14.890625 38.140625 10.796875 36.28125 z " id="DejaVuSans-35"/> - + @@ -171,10 +177,10 @@ z - - + + @@ -210,7 +216,7 @@ Q 23.96875 32.421875 30.609375 32.421875 z " id="DejaVuSans-39"/> - + @@ -218,10 +224,10 @@ z - - + + @@ -272,7 +278,7 @@ Q 46.96875 40.921875 40.578125 39.3125 z " id="DejaVuSans-33"/> - + @@ -281,10 +287,10 @@ z - - + + @@ -329,7 +335,7 @@ Q 18.3125 60.0625 18.3125 54.390625 z " id="DejaVuSans-38"/> - + @@ -338,10 +344,10 @@ z - - + + @@ -371,7 +377,7 @@ Q 31.109375 20.453125 19.1875 8.296875 z " id="DejaVuSans-32"/> - + @@ -380,10 +386,10 @@ z - - + + @@ -398,7 +404,7 @@ L 8.203125 64.59375 z " id="DejaVuSans-37"/> - + @@ -407,14 +413,14 @@ z - - + + - + @@ -509,7 +515,7 @@ L 54.390625 54.6875 z " id="DejaVuSans-67"/> - + @@ -518,70 +524,70 @@ z - - + + @@ -593,7 +599,7 @@ L 10.6875 0 z " id="DejaVuSans-2e"/> - + @@ -601,104 +607,104 @@ z - - + + - + @@ -706,104 +712,104 @@ L 338.4 216 - - + + - + @@ -811,104 +817,104 @@ L 360 216 - - + + - + @@ -916,194 +922,104 @@ L 381.6 216 - - + + - + @@ -1111,194 +1027,194 @@ L 403.2 216 - - + + - + @@ -1306,194 +1222,194 @@ L 424.8 216 - - + + - + @@ -1501,194 +1417,194 @@ L 446.4 216 - - + + - + @@ -1696,19 +1612,72 @@ L 468 216 + + + + + + - - + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.pdf index 1fe7e0a9d269..5d459ccde364 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.png index aeb3f5f5aea5..007ad41f51ab 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.png and b/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.svg index 527b738dea2a..332189be1fe1 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_units_2.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - + + @@ -89,7 +95,7 @@ L 10.6875 0 z " id="DejaVuSans-2e"/> - + @@ -97,10 +103,10 @@ z - - + + @@ -283,7 +289,7 @@ Q 48.484375 72.75 52.59375 71.296875 z " id="DejaVuSans-36"/> - + @@ -302,10 +308,10 @@ z - - + + @@ -335,7 +341,7 @@ Q 31.109375 20.453125 19.1875 8.296875 z " id="DejaVuSans-32"/> - + @@ -353,10 +359,10 @@ z - - + + @@ -379,7 +385,7 @@ L 4.890625 26.703125 z " id="DejaVuSans-34"/> - + @@ -397,14 +403,14 @@ z - - + + - + @@ -422,14 +428,14 @@ L 122.4 216 - - + + - + @@ -447,14 +453,14 @@ L 173.011948 338.188052 - - + + - + @@ -472,14 +478,14 @@ L 295.2 388.8 - - + + - + @@ -574,7 +580,7 @@ Q 14.796875 37.203125 14.796875 27.296875 z " id="DejaVuSans-64"/> - + @@ -583,74 +589,74 @@ z - - + + - + @@ -658,104 +664,104 @@ L 316.8 216 - - + + - + @@ -763,104 +769,104 @@ L 338.4 216 - - + + - + @@ -868,104 +874,104 @@ L 360 216 - - + + - + @@ -973,194 +979,104 @@ L 381.6 216 - - + + - + @@ -1168,194 +1084,194 @@ L 403.2 216 - - + + - + @@ -1363,194 +1279,194 @@ L 424.8 216 - - + + - + @@ -1558,194 +1474,194 @@ L 446.4 216 - - + + - + @@ -1799,25 +1715,78 @@ Q 50 49.953125 52 44.1875 z " id="DejaVuSans-6d"/> - + + + + + + + - - + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.pdf index a62f4899e22f..3e874e9159a1 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.png index 63003da54b0e..7ab1a71dcaac 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.png and b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.svg index 93758e566310..7c68e64eef27 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_180.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -143,17 +112,17 @@ Q 18.5 74.21875 25 74.21875 z " id="DejaVuSans-b0"/> - + - - + + @@ -200,7 +169,7 @@ Q 14.890625 38.140625 10.796875 36.28125 z " id="DejaVuSans-35"/> - + @@ -208,10 +177,10 @@ z - - + + @@ -247,7 +216,7 @@ Q 23.96875 32.421875 30.609375 32.421875 z " id="DejaVuSans-39"/> - + @@ -255,10 +224,10 @@ z - - + + @@ -309,7 +278,7 @@ Q 46.96875 40.921875 40.578125 39.3125 z " id="DejaVuSans-33"/> - + @@ -318,10 +287,10 @@ z - - + + @@ -366,7 +335,7 @@ Q 18.3125 60.0625 18.3125 54.390625 z " id="DejaVuSans-38"/> - + @@ -375,10 +344,10 @@ z - - + + @@ -408,7 +377,7 @@ Q 31.109375 20.453125 19.1875 8.296875 z " id="DejaVuSans-32"/> - + @@ -417,10 +386,10 @@ z - - + + @@ -435,7 +404,7 @@ L 8.203125 64.59375 z " id="DejaVuSans-37"/> - + @@ -444,14 +413,14 @@ z - - + + - + @@ -462,70 +431,70 @@ L 417.388052 338.188052 - - + + @@ -537,7 +506,7 @@ L 10.6875 0 z " id="DejaVuSans-2e"/> - + @@ -546,104 +515,104 @@ z - - + + - + @@ -652,104 +621,104 @@ L 352.8 216 - - + + - + @@ -758,194 +727,104 @@ L 381.6 216 - - + + - + @@ -954,194 +833,194 @@ L 410.4 216 - - + + - + @@ -1150,194 +1029,194 @@ L 439.2 216 - - + + - + @@ -1346,19 +1225,109 @@ L 468 216 + + + + + + + + + + + + + + + + + + + + + + + - - + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.pdf index e0961e0ba821..b35d1edc6fa3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.png index f4a32906aaf0..6b281167ce2e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.png and b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.svg index 3ea6ac5f1f2c..0b43965b0365 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_wrap_360.svg @@ -2,7 +2,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -165,17 +112,17 @@ Q 18.5 74.21875 25 74.21875 z " id="DejaVuSans-b0"/> - + - - + + @@ -222,7 +169,7 @@ Q 14.890625 38.140625 10.796875 36.28125 z " id="DejaVuSans-35"/> - + @@ -230,10 +177,10 @@ z - - + + @@ -269,7 +216,7 @@ Q 23.96875 32.421875 30.609375 32.421875 z " id="DejaVuSans-39"/> - + @@ -277,10 +224,10 @@ z - - + + @@ -331,7 +278,7 @@ Q 46.96875 40.921875 40.578125 39.3125 z " id="DejaVuSans-33"/> - + @@ -340,10 +287,10 @@ z - - + + @@ -388,7 +335,7 @@ Q 18.3125 60.0625 18.3125 54.390625 z " id="DejaVuSans-38"/> - + @@ -397,10 +344,10 @@ z - - + + @@ -430,7 +377,7 @@ Q 31.109375 20.453125 19.1875 8.296875 z " id="DejaVuSans-32"/> - + @@ -439,10 +386,10 @@ z - - + + @@ -457,7 +404,7 @@ L 8.203125 64.59375 z " id="DejaVuSans-37"/> - + @@ -466,14 +413,14 @@ z - - + + - + @@ -484,70 +431,70 @@ L 417.388052 338.188052 - - + + @@ -559,7 +506,7 @@ L 10.6875 0 z " id="DejaVuSans-2e"/> - + @@ -568,104 +515,104 @@ z - - + + - + @@ -674,104 +621,104 @@ L 352.8 216 - - + + - + @@ -780,194 +727,104 @@ L 381.6 216 - - + + - + @@ -976,194 +833,194 @@ L 410.4 216 - - + + - + @@ -1172,194 +1029,194 @@ L 439.2 216 - - + + - + @@ -1368,19 +1225,131 @@ L 468 216 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + diff --git a/lib/matplotlib/tests/baseline_images/test_patches/polar_proj.png b/lib/matplotlib/tests/baseline_images/test_patches/polar_proj.png index 7999ff1684b1..207585aa4d5e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_patches/polar_proj.png and b/lib/matplotlib/tests/baseline_images/test_patches/polar_proj.png differ diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 3d340a955cf5..f7e5ffe87cb2 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -349,7 +349,7 @@ def test_annotate_default_arrow(): assert ann.arrow_patch is not None -@image_comparison(baseline_images=['polar_axes']) +@image_comparison(baseline_images=['polar_axes'], style='default') def test_polar_annotations(): # you can specify the xypoint and the xytext in different # positions and coordinate systems, and optionally turn on a @@ -383,7 +383,7 @@ def test_polar_annotations(): ) -@image_comparison(baseline_images=['polar_coords'], +@image_comparison(baseline_images=['polar_coords'], style='default', remove_text=True) def test_polar_coord_annotations(): # You can also use polar notation on a catesian axes. Here the @@ -558,9 +558,8 @@ def test_const_xy(): plt.plot(np.ones((10,)), np.ones((10,)), 'o') -@image_comparison(baseline_images=['polar_wrap_180', - 'polar_wrap_360', - ]) +@image_comparison(baseline_images=['polar_wrap_180', 'polar_wrap_360'], + style='default') def test_polar_wrap(): D2R = np.pi / 180.0 @@ -581,7 +580,8 @@ def test_polar_wrap(): plt.rgrids([0.05, 0.1, 0.15, 0.2, 0.25, 0.3]) -@image_comparison(baseline_images=['polar_units', 'polar_units_2']) +@image_comparison(baseline_images=['polar_units', 'polar_units_2'], + style='default') def test_polar_units(): import matplotlib.testing.jpl_units as units units.register() @@ -612,7 +612,7 @@ def test_polar_units(): units.UnitDblFormatter) -@image_comparison(baseline_images=['polar_rmin']) +@image_comparison(baseline_images=['polar_rmin'], style='default') def test_polar_rmin(): r = np.arange(0, 3.0, 0.01) theta = 2*np.pi*r @@ -624,7 +624,32 @@ def test_polar_rmin(): ax.set_rmin(0.5) -@image_comparison(baseline_images=['polar_theta_position']) +@image_comparison(baseline_images=['polar_negative_rmin'], style='default') +def test_polar_negative_rmin(): + r = np.arange(-3.0, 0.0, 0.01) + theta = 2*np.pi*r + + fig = plt.figure() + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True) + ax.plot(theta, r) + ax.set_rmax(0.0) + ax.set_rmin(-3.0) + + +@image_comparison(baseline_images=['polar_rorigin'], style='default') +def test_polar_rorigin(): + r = np.arange(0, 3.0, 0.01) + theta = 2*np.pi*r + + fig = plt.figure() + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True) + ax.plot(theta, r) + ax.set_rmax(2.0) + ax.set_rmin(0.5) + ax.set_rorigin(0.0) + + +@image_comparison(baseline_images=['polar_theta_position'], style='default') def test_polar_theta_position(): r = np.arange(0, 3.0, 0.01) theta = 2*np.pi*r @@ -632,17 +657,42 @@ def test_polar_theta_position(): fig = plt.figure() ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True) ax.plot(theta, r) - ax.set_theta_zero_location("NW") + ax.set_theta_zero_location("NW", 30) ax.set_theta_direction('clockwise') -@image_comparison(baseline_images=['polar_rlabel_position']) +@image_comparison(baseline_images=['polar_rlabel_position'], style='default') def test_polar_rlabel_position(): fig = plt.figure() ax = fig.add_subplot(111, projection='polar') ax.set_rlabel_position(315) +@image_comparison(baseline_images=['polar_theta_wedge'], style='default', + tol=0.01 if six.PY2 else 0) +def test_polar_theta_limits(): + r = np.arange(0, 3.0, 0.01) + theta = 2*np.pi*r + + theta_mins = np.arange(15.0, 361.0, 90.0) + theta_maxs = np.arange(50.0, 361.0, 90.0) + + fig, axes = plt.subplots(len(theta_mins), len(theta_maxs), + subplot_kw={'polar': True}, + figsize=(8, 6)) + + for i, start in enumerate(theta_mins): + for j, end in enumerate(theta_maxs): + ax = axes[i, j] + if start < end: + ax.plot(theta, r) + ax.yaxis.set_tick_params(label2On=True) + ax.set_thetamin(start) + ax.set_thetamax(end) + else: + ax.set_visible(False) + + @image_comparison(baseline_images=['axvspan_epoch']) def test_axvspan_epoch(): from datetime import datetime @@ -1258,7 +1308,7 @@ def test_markevery_log_scales(): plt.plot(x, y, 'o', ls='-', ms=4, markevery=case) -@image_comparison(baseline_images=['markevery_polar'], +@image_comparison(baseline_images=['markevery_polar'], style='default', remove_text=True) def test_markevery_polar(): cases = [None, diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index 44d8b3915bcf..5edfa10f5644 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -339,7 +339,8 @@ def test_multi_color_hatch(): ax.add_patch(r) -@image_comparison(baseline_images=['polar_proj'], extensions=['png']) +@image_comparison(baseline_images=['polar_proj'], extensions=['png'], + style='default') def test_adding_rectangle_patch_with_polar_projection(): fig = plt.figure() ax = fig.add_subplot(111, projection='polar') diff --git a/pytest.ini b/pytest.ini index 0928004accd6..5ca19fee86a7 100644 --- a/pytest.ini +++ b/pytest.ini @@ -42,7 +42,6 @@ pep8ignore = matplotlib/backend_bases.py E225 E501 E712 matplotlib/projections/__init__.py E302 matplotlib/projections/geo.py E203 E221 E231 E261 E302 E402 E501 E502 - matplotlib/projections/polar.py E202 E203 E221 E231 E251 E301 E402 E501 matplotlib/sphinxext/mathmpl.py E302 matplotlib/sphinxext/only_directives.py E302 matplotlib/sphinxext/plot_directive.py E261 E301 E302 E401 E402 E501 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy