aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/matplotlib/py3/mpl_toolkits
diff options
context:
space:
mode:
authormaxim-yurchuk <maxim-yurchuk@yandex-team.com>2025-02-11 13:26:52 +0300
committermaxim-yurchuk <maxim-yurchuk@yandex-team.com>2025-02-11 13:57:59 +0300
commitf895bba65827952ed934b2b46f9a45e30a191fd2 (patch)
tree03260c906d9ec41cdc03e2a496b15d407459cec0 /contrib/python/matplotlib/py3/mpl_toolkits
parent5f7060466f7b9707818c2091e1a25c14f33c3474 (diff)
downloadydb-f895bba65827952ed934b2b46f9a45e30a191fd2.tar.gz
Remove deps on pandas
<https://github.com/ydb-platform/ydb/pull/14418> <https://github.com/ydb-platform/ydb/pull/14419> \-- аналогичные правки в gh Хочу залить в обход синка, чтобы посмотреть удалится ли pandas в нашей gh репе через piglet commit_hash:abca127aa37d4dbb94b07e1e18cdb8eb5b711860
Diffstat (limited to 'contrib/python/matplotlib/py3/mpl_toolkits')
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/__init__.py10
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/anchored_artists.py462
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_divider.py694
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_grid.py550
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_rgb.py157
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_size.py248
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/inset_locator.py561
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/mpl_axes.py128
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/parasite_axes.py257
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/__init__.py13
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/angle_helper.py394
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_divider.py2
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_grid.py23
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_rgb.py18
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axis_artist.py1115
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axisline_style.py193
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axislines.py531
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/floating_axes.py298
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/grid_finder.py335
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/grid_helper_curvelinear.py336
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/axisartist/parasite_axes.py7
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/__init__.py3
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/art3d.py1252
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/axes3d.py3448
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/axis3d.py753
-rw-r--r--contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/proj3d.py259
26 files changed, 0 insertions, 12047 deletions
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/__init__.py b/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/__init__.py
deleted file mode 100644
index c55302485e3..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from . import axes_size as Size
-from .axes_divider import Divider, SubplotDivider, make_axes_locatable
-from .axes_grid import AxesGrid, Grid, ImageGrid
-
-from .parasite_axes import host_subplot, host_axes
-
-__all__ = ["Size",
- "Divider", "SubplotDivider", "make_axes_locatable",
- "AxesGrid", "Grid", "ImageGrid",
- "host_subplot", "host_axes"]
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/anchored_artists.py b/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/anchored_artists.py
deleted file mode 100644
index 1238310b462..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/anchored_artists.py
+++ /dev/null
@@ -1,462 +0,0 @@
-from matplotlib import _api, transforms
-from matplotlib.offsetbox import (AnchoredOffsetbox, AuxTransformBox,
- DrawingArea, TextArea, VPacker)
-from matplotlib.patches import (Rectangle, Ellipse, ArrowStyle,
- FancyArrowPatch, PathPatch)
-from matplotlib.text import TextPath
-
-__all__ = ['AnchoredDrawingArea', 'AnchoredAuxTransformBox',
- 'AnchoredEllipse', 'AnchoredSizeBar', 'AnchoredDirectionArrows']
-
-
-class AnchoredDrawingArea(AnchoredOffsetbox):
- def __init__(self, width, height, xdescent, ydescent,
- loc, pad=0.4, borderpad=0.5, prop=None, frameon=True,
- **kwargs):
- """
- An anchored container with a fixed size and fillable `.DrawingArea`.
-
- Artists added to the *drawing_area* will have their coordinates
- interpreted as pixels. Any transformations set on the artists will be
- overridden.
-
- Parameters
- ----------
- width, height : float
- Width and height of the container, in pixels.
- xdescent, ydescent : float
- Descent of the container in the x- and y- direction, in pixels.
- loc : str
- Location of this artist. Valid locations are
- 'upper left', 'upper center', 'upper right',
- 'center left', 'center', 'center right',
- 'lower left', 'lower center', 'lower right'.
- For backward compatibility, numeric values are accepted as well.
- See the parameter *loc* of `.Legend` for details.
- pad : float, default: 0.4
- Padding around the child objects, in fraction of the font size.
- borderpad : float, default: 0.5
- Border padding, in fraction of the font size.
- prop : `~matplotlib.font_manager.FontProperties`, optional
- Font property used as a reference for paddings.
- frameon : bool, default: True
- If True, draw a box around this artist.
- **kwargs
- Keyword arguments forwarded to `.AnchoredOffsetbox`.
-
- Attributes
- ----------
- drawing_area : `~matplotlib.offsetbox.DrawingArea`
- A container for artists to display.
-
- Examples
- --------
- To display blue and red circles of different sizes in the upper right
- of an Axes *ax*:
-
- >>> ada = AnchoredDrawingArea(20, 20, 0, 0,
- ... loc='upper right', frameon=False)
- >>> ada.drawing_area.add_artist(Circle((10, 10), 10, fc="b"))
- >>> ada.drawing_area.add_artist(Circle((30, 10), 5, fc="r"))
- >>> ax.add_artist(ada)
- """
- self.da = DrawingArea(width, height, xdescent, ydescent)
- self.drawing_area = self.da
-
- super().__init__(
- loc, pad=pad, borderpad=borderpad, child=self.da, prop=None,
- frameon=frameon, **kwargs
- )
-
-
-class AnchoredAuxTransformBox(AnchoredOffsetbox):
- def __init__(self, transform, loc,
- pad=0.4, borderpad=0.5, prop=None, frameon=True, **kwargs):
- """
- An anchored container with transformed coordinates.
-
- Artists added to the *drawing_area* are scaled according to the
- coordinates of the transformation used. The dimensions of this artist
- will scale to contain the artists added.
-
- Parameters
- ----------
- transform : `~matplotlib.transforms.Transform`
- The transformation object for the coordinate system in use, i.e.,
- :attr:`matplotlib.axes.Axes.transData`.
- loc : str
- Location of this artist. Valid locations are
- 'upper left', 'upper center', 'upper right',
- 'center left', 'center', 'center right',
- 'lower left', 'lower center', 'lower right'.
- For backward compatibility, numeric values are accepted as well.
- See the parameter *loc* of `.Legend` for details.
- pad : float, default: 0.4
- Padding around the child objects, in fraction of the font size.
- borderpad : float, default: 0.5
- Border padding, in fraction of the font size.
- prop : `~matplotlib.font_manager.FontProperties`, optional
- Font property used as a reference for paddings.
- frameon : bool, default: True
- If True, draw a box around this artist.
- **kwargs
- Keyword arguments forwarded to `.AnchoredOffsetbox`.
-
- Attributes
- ----------
- drawing_area : `~matplotlib.offsetbox.AuxTransformBox`
- A container for artists to display.
-
- Examples
- --------
- To display an ellipse in the upper left, with a width of 0.1 and
- height of 0.4 in data coordinates:
-
- >>> box = AnchoredAuxTransformBox(ax.transData, loc='upper left')
- >>> el = Ellipse((0, 0), width=0.1, height=0.4, angle=30)
- >>> box.drawing_area.add_artist(el)
- >>> ax.add_artist(box)
- """
- self.drawing_area = AuxTransformBox(transform)
-
- super().__init__(loc, pad=pad, borderpad=borderpad,
- child=self.drawing_area, prop=prop, frameon=frameon,
- **kwargs)
-
-
-@_api.deprecated("3.8")
-class AnchoredEllipse(AnchoredOffsetbox):
- def __init__(self, transform, width, height, angle, loc,
- pad=0.1, borderpad=0.1, prop=None, frameon=True, **kwargs):
- """
- Draw an anchored ellipse of a given size.
-
- Parameters
- ----------
- transform : `~matplotlib.transforms.Transform`
- The transformation object for the coordinate system in use, i.e.,
- :attr:`matplotlib.axes.Axes.transData`.
- width, height : float
- Width and height of the ellipse, given in coordinates of
- *transform*.
- angle : float
- Rotation of the ellipse, in degrees, anti-clockwise.
- loc : str
- Location of the ellipse. Valid locations are
- 'upper left', 'upper center', 'upper right',
- 'center left', 'center', 'center right',
- 'lower left', 'lower center', 'lower right'.
- For backward compatibility, numeric values are accepted as well.
- See the parameter *loc* of `.Legend` for details.
- pad : float, default: 0.1
- Padding around the ellipse, in fraction of the font size.
- borderpad : float, default: 0.1
- Border padding, in fraction of the font size.
- frameon : bool, default: True
- If True, draw a box around the ellipse.
- prop : `~matplotlib.font_manager.FontProperties`, optional
- Font property used as a reference for paddings.
- **kwargs
- Keyword arguments forwarded to `.AnchoredOffsetbox`.
-
- Attributes
- ----------
- ellipse : `~matplotlib.patches.Ellipse`
- Ellipse patch drawn.
- """
- self._box = AuxTransformBox(transform)
- self.ellipse = Ellipse((0, 0), width, height, angle=angle)
- self._box.add_artist(self.ellipse)
-
- super().__init__(loc, pad=pad, borderpad=borderpad, child=self._box,
- prop=prop, frameon=frameon, **kwargs)
-
-
-class AnchoredSizeBar(AnchoredOffsetbox):
- def __init__(self, transform, size, label, loc,
- pad=0.1, borderpad=0.1, sep=2,
- frameon=True, size_vertical=0, color='black',
- label_top=False, fontproperties=None, fill_bar=None,
- **kwargs):
- """
- Draw a horizontal scale bar with a center-aligned label underneath.
-
- Parameters
- ----------
- transform : `~matplotlib.transforms.Transform`
- The transformation object for the coordinate system in use, i.e.,
- :attr:`matplotlib.axes.Axes.transData`.
- size : float
- Horizontal length of the size bar, given in coordinates of
- *transform*.
- label : str
- Label to display.
- loc : str
- Location of the size bar. Valid locations are
- 'upper left', 'upper center', 'upper right',
- 'center left', 'center', 'center right',
- 'lower left', 'lower center', 'lower right'.
- For backward compatibility, numeric values are accepted as well.
- See the parameter *loc* of `.Legend` for details.
- pad : float, default: 0.1
- Padding around the label and size bar, in fraction of the font
- size.
- borderpad : float, default: 0.1
- Border padding, in fraction of the font size.
- sep : float, default: 2
- Separation between the label and the size bar, in points.
- frameon : bool, default: True
- If True, draw a box around the horizontal bar and label.
- size_vertical : float, default: 0
- Vertical length of the size bar, given in coordinates of
- *transform*.
- color : str, default: 'black'
- Color for the size bar and label.
- label_top : bool, default: False
- If True, the label will be over the size bar.
- fontproperties : `~matplotlib.font_manager.FontProperties`, optional
- Font properties for the label text.
- fill_bar : bool, optional
- If True and if *size_vertical* is nonzero, the size bar will
- be filled in with the color specified by the size bar.
- Defaults to True if *size_vertical* is greater than
- zero and False otherwise.
- **kwargs
- Keyword arguments forwarded to `.AnchoredOffsetbox`.
-
- Attributes
- ----------
- size_bar : `~matplotlib.offsetbox.AuxTransformBox`
- Container for the size bar.
- txt_label : `~matplotlib.offsetbox.TextArea`
- Container for the label of the size bar.
-
- Notes
- -----
- If *prop* is passed as a keyword argument, but *fontproperties* is
- not, then *prop* is assumed to be the intended *fontproperties*.
- Using both *prop* and *fontproperties* is not supported.
-
- Examples
- --------
- >>> import matplotlib.pyplot as plt
- >>> import numpy as np
- >>> from mpl_toolkits.axes_grid1.anchored_artists import (
- ... AnchoredSizeBar)
- >>> fig, ax = plt.subplots()
- >>> ax.imshow(np.random.random((10, 10)))
- >>> bar = AnchoredSizeBar(ax.transData, 3, '3 data units', 4)
- >>> ax.add_artist(bar)
- >>> fig.show()
-
- Using all the optional parameters
-
- >>> import matplotlib.font_manager as fm
- >>> fontprops = fm.FontProperties(size=14, family='monospace')
- >>> bar = AnchoredSizeBar(ax.transData, 3, '3 units', 4, pad=0.5,
- ... sep=5, borderpad=0.5, frameon=False,
- ... size_vertical=0.5, color='white',
- ... fontproperties=fontprops)
- """
- if fill_bar is None:
- fill_bar = size_vertical > 0
-
- self.size_bar = AuxTransformBox(transform)
- self.size_bar.add_artist(Rectangle((0, 0), size, size_vertical,
- fill=fill_bar, facecolor=color,
- edgecolor=color))
-
- if fontproperties is None and 'prop' in kwargs:
- fontproperties = kwargs.pop('prop')
-
- if fontproperties is None:
- textprops = {'color': color}
- else:
- textprops = {'color': color, 'fontproperties': fontproperties}
-
- self.txt_label = TextArea(label, textprops=textprops)
-
- if label_top:
- _box_children = [self.txt_label, self.size_bar]
- else:
- _box_children = [self.size_bar, self.txt_label]
-
- self._box = VPacker(children=_box_children,
- align="center",
- pad=0, sep=sep)
-
- super().__init__(loc, pad=pad, borderpad=borderpad, child=self._box,
- prop=fontproperties, frameon=frameon, **kwargs)
-
-
-class AnchoredDirectionArrows(AnchoredOffsetbox):
- def __init__(self, transform, label_x, label_y, length=0.15,
- fontsize=0.08, loc='upper left', angle=0, aspect_ratio=1,
- pad=0.4, borderpad=0.4, frameon=False, color='w', alpha=1,
- sep_x=0.01, sep_y=0, fontproperties=None, back_length=0.15,
- head_width=10, head_length=15, tail_width=2,
- text_props=None, arrow_props=None,
- **kwargs):
- """
- Draw two perpendicular arrows to indicate directions.
-
- Parameters
- ----------
- transform : `~matplotlib.transforms.Transform`
- The transformation object for the coordinate system in use, i.e.,
- :attr:`matplotlib.axes.Axes.transAxes`.
- label_x, label_y : str
- Label text for the x and y arrows
- length : float, default: 0.15
- Length of the arrow, given in coordinates of *transform*.
- fontsize : float, default: 0.08
- Size of label strings, given in coordinates of *transform*.
- loc : str, default: 'upper left'
- Location of the arrow. Valid locations are
- 'upper left', 'upper center', 'upper right',
- 'center left', 'center', 'center right',
- 'lower left', 'lower center', 'lower right'.
- For backward compatibility, numeric values are accepted as well.
- See the parameter *loc* of `.Legend` for details.
- angle : float, default: 0
- The angle of the arrows in degrees.
- aspect_ratio : float, default: 1
- The ratio of the length of arrow_x and arrow_y.
- Negative numbers can be used to change the direction.
- pad : float, default: 0.4
- Padding around the labels and arrows, in fraction of the font size.
- borderpad : float, default: 0.4
- Border padding, in fraction of the font size.
- frameon : bool, default: False
- If True, draw a box around the arrows and labels.
- color : str, default: 'white'
- Color for the arrows and labels.
- alpha : float, default: 1
- Alpha values of the arrows and labels
- sep_x, sep_y : float, default: 0.01 and 0 respectively
- Separation between the arrows and labels in coordinates of
- *transform*.
- fontproperties : `~matplotlib.font_manager.FontProperties`, optional
- Font properties for the label text.
- back_length : float, default: 0.15
- Fraction of the arrow behind the arrow crossing.
- head_width : float, default: 10
- Width of arrow head, sent to `.ArrowStyle`.
- head_length : float, default: 15
- Length of arrow head, sent to `.ArrowStyle`.
- tail_width : float, default: 2
- Width of arrow tail, sent to `.ArrowStyle`.
- text_props, arrow_props : dict
- Properties of the text and arrows, passed to `.TextPath` and
- `.FancyArrowPatch`.
- **kwargs
- Keyword arguments forwarded to `.AnchoredOffsetbox`.
-
- Attributes
- ----------
- arrow_x, arrow_y : `~matplotlib.patches.FancyArrowPatch`
- Arrow x and y
- text_path_x, text_path_y : `~matplotlib.text.TextPath`
- Path for arrow labels
- p_x, p_y : `~matplotlib.patches.PathPatch`
- Patch for arrow labels
- box : `~matplotlib.offsetbox.AuxTransformBox`
- Container for the arrows and labels.
-
- Notes
- -----
- If *prop* is passed as a keyword argument, but *fontproperties* is
- not, then *prop* is assumed to be the intended *fontproperties*.
- Using both *prop* and *fontproperties* is not supported.
-
- Examples
- --------
- >>> import matplotlib.pyplot as plt
- >>> import numpy as np
- >>> from mpl_toolkits.axes_grid1.anchored_artists import (
- ... AnchoredDirectionArrows)
- >>> fig, ax = plt.subplots()
- >>> ax.imshow(np.random.random((10, 10)))
- >>> arrows = AnchoredDirectionArrows(ax.transAxes, '111', '110')
- >>> ax.add_artist(arrows)
- >>> fig.show()
-
- Using several of the optional parameters, creating downward pointing
- arrow and high contrast text labels.
-
- >>> import matplotlib.font_manager as fm
- >>> fontprops = fm.FontProperties(family='monospace')
- >>> arrows = AnchoredDirectionArrows(ax.transAxes, 'East', 'South',
- ... loc='lower left', color='k',
- ... aspect_ratio=-1, sep_x=0.02,
- ... sep_y=-0.01,
- ... text_props={'ec':'w', 'fc':'k'},
- ... fontproperties=fontprops)
- """
- if arrow_props is None:
- arrow_props = {}
-
- if text_props is None:
- text_props = {}
-
- arrowstyle = ArrowStyle("Simple",
- head_width=head_width,
- head_length=head_length,
- tail_width=tail_width)
-
- if fontproperties is None and 'prop' in kwargs:
- fontproperties = kwargs.pop('prop')
-
- if 'color' not in arrow_props:
- arrow_props['color'] = color
-
- if 'alpha' not in arrow_props:
- arrow_props['alpha'] = alpha
-
- if 'color' not in text_props:
- text_props['color'] = color
-
- if 'alpha' not in text_props:
- text_props['alpha'] = alpha
-
- t_start = transform
- t_end = t_start + transforms.Affine2D().rotate_deg(angle)
-
- self.box = AuxTransformBox(t_end)
-
- length_x = length
- length_y = length*aspect_ratio
-
- self.arrow_x = FancyArrowPatch(
- (0, back_length*length_y),
- (length_x, back_length*length_y),
- arrowstyle=arrowstyle,
- shrinkA=0.0,
- shrinkB=0.0,
- **arrow_props)
-
- self.arrow_y = FancyArrowPatch(
- (back_length*length_x, 0),
- (back_length*length_x, length_y),
- arrowstyle=arrowstyle,
- shrinkA=0.0,
- shrinkB=0.0,
- **arrow_props)
-
- self.box.add_artist(self.arrow_x)
- self.box.add_artist(self.arrow_y)
-
- text_path_x = TextPath((
- length_x+sep_x, back_length*length_y+sep_y), label_x,
- size=fontsize, prop=fontproperties)
- self.p_x = PathPatch(text_path_x, transform=t_start, **text_props)
- self.box.add_artist(self.p_x)
-
- text_path_y = TextPath((
- length_x*back_length+sep_x, length_y*(1-back_length)+sep_y),
- label_y, size=fontsize, prop=fontproperties)
- self.p_y = PathPatch(text_path_y, **text_props)
- self.box.add_artist(self.p_y)
-
- super().__init__(loc, pad=pad, borderpad=borderpad, child=self.box,
- frameon=frameon, **kwargs)
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_divider.py b/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_divider.py
deleted file mode 100644
index f6c38f35dbc..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_divider.py
+++ /dev/null
@@ -1,694 +0,0 @@
-"""
-Helper classes to adjust the positions of multiple axes at drawing time.
-"""
-
-import functools
-
-import numpy as np
-
-import matplotlib as mpl
-from matplotlib import _api
-from matplotlib.gridspec import SubplotSpec
-import matplotlib.transforms as mtransforms
-from . import axes_size as Size
-
-
-class Divider:
- """
- An Axes positioning class.
-
- The divider is initialized with lists of horizontal and vertical sizes
- (:mod:`mpl_toolkits.axes_grid1.axes_size`) based on which a given
- rectangular area will be divided.
-
- The `new_locator` method then creates a callable object
- that can be used as the *axes_locator* of the axes.
- """
-
- def __init__(self, fig, pos, horizontal, vertical,
- aspect=None, anchor="C"):
- """
- Parameters
- ----------
- fig : Figure
- pos : tuple of 4 floats
- Position of the rectangle that will be divided.
- horizontal : list of :mod:`~mpl_toolkits.axes_grid1.axes_size`
- Sizes for horizontal division.
- vertical : list of :mod:`~mpl_toolkits.axes_grid1.axes_size`
- Sizes for vertical division.
- aspect : bool, optional
- Whether overall rectangular area is reduced so that the relative
- part of the horizontal and vertical scales have the same scale.
- anchor : (float, float) or {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', \
-'NW', 'W'}, default: 'C'
- Placement of the reduced rectangle, when *aspect* is True.
- """
-
- self._fig = fig
- self._pos = pos
- self._horizontal = horizontal
- self._vertical = vertical
- self._anchor = anchor
- self.set_anchor(anchor)
- self._aspect = aspect
- self._xrefindex = 0
- self._yrefindex = 0
- self._locator = None
-
- def get_horizontal_sizes(self, renderer):
- return np.array([s.get_size(renderer) for s in self.get_horizontal()])
-
- def get_vertical_sizes(self, renderer):
- return np.array([s.get_size(renderer) for s in self.get_vertical()])
-
- def set_position(self, pos):
- """
- Set the position of the rectangle.
-
- Parameters
- ----------
- pos : tuple of 4 floats
- position of the rectangle that will be divided
- """
- self._pos = pos
-
- def get_position(self):
- """Return the position of the rectangle."""
- return self._pos
-
- def set_anchor(self, anchor):
- """
- Parameters
- ----------
- anchor : (float, float) or {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', \
-'NW', 'W'}
- Either an (*x*, *y*) pair of relative coordinates (0 is left or
- bottom, 1 is right or top), 'C' (center), or a cardinal direction
- ('SW', southwest, is bottom left, etc.).
-
- See Also
- --------
- .Axes.set_anchor
- """
- if isinstance(anchor, str):
- _api.check_in_list(mtransforms.Bbox.coefs, anchor=anchor)
- elif not isinstance(anchor, (tuple, list)) or len(anchor) != 2:
- raise TypeError("anchor must be str or 2-tuple")
- self._anchor = anchor
-
- def get_anchor(self):
- """Return the anchor."""
- return self._anchor
-
- def get_subplotspec(self):
- return None
-
- def set_horizontal(self, h):
- """
- Parameters
- ----------
- h : list of :mod:`~mpl_toolkits.axes_grid1.axes_size`
- sizes for horizontal division
- """
- self._horizontal = h
-
- def get_horizontal(self):
- """Return horizontal sizes."""
- return self._horizontal
-
- def set_vertical(self, v):
- """
- Parameters
- ----------
- v : list of :mod:`~mpl_toolkits.axes_grid1.axes_size`
- sizes for vertical division
- """
- self._vertical = v
-
- def get_vertical(self):
- """Return vertical sizes."""
- return self._vertical
-
- def set_aspect(self, aspect=False):
- """
- Parameters
- ----------
- aspect : bool
- """
- self._aspect = aspect
-
- def get_aspect(self):
- """Return aspect."""
- return self._aspect
-
- def set_locator(self, _locator):
- self._locator = _locator
-
- def get_locator(self):
- return self._locator
-
- def get_position_runtime(self, ax, renderer):
- if self._locator is None:
- return self.get_position()
- else:
- return self._locator(ax, renderer).bounds
-
- @staticmethod
- def _calc_k(sizes, total):
- # sizes is a (n, 2) array of (rel_size, abs_size); this method finds
- # the k factor such that sum(rel_size * k + abs_size) == total.
- rel_sum, abs_sum = sizes.sum(0)
- return (total - abs_sum) / rel_sum if rel_sum else 0
-
- @staticmethod
- def _calc_offsets(sizes, k):
- # Apply k factors to (n, 2) sizes array of (rel_size, abs_size); return
- # the resulting cumulative offset positions.
- return np.cumsum([0, *(sizes @ [k, 1])])
-
- def new_locator(self, nx, ny, nx1=None, ny1=None):
- """
- Return an axes locator callable for the specified cell.
-
- Parameters
- ----------
- nx, nx1 : int
- Integers specifying the column-position of the
- cell. When *nx1* is None, a single *nx*-th column is
- specified. Otherwise, location of columns spanning between *nx*
- to *nx1* (but excluding *nx1*-th column) is specified.
- ny, ny1 : int
- Same as *nx* and *nx1*, but for row positions.
- """
- if nx1 is None:
- nx1 = nx + 1
- if ny1 is None:
- ny1 = ny + 1
- # append_size("left") adds a new size at the beginning of the
- # horizontal size lists; this shift transforms e.g.
- # new_locator(nx=2, ...) into effectively new_locator(nx=3, ...). To
- # take that into account, instead of recording nx, we record
- # nx-self._xrefindex, where _xrefindex is shifted by 1 by each
- # append_size("left"), and re-add self._xrefindex back to nx in
- # _locate, when the actual axes position is computed. Ditto for y.
- xref = self._xrefindex
- yref = self._yrefindex
- locator = functools.partial(
- self._locate, nx - xref, ny - yref, nx1 - xref, ny1 - yref)
- locator.get_subplotspec = self.get_subplotspec
- return locator
-
- @_api.deprecated(
- "3.8", alternative="divider.new_locator(...)(ax, renderer)")
- def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None):
- """
- Implementation of ``divider.new_locator().__call__``.
-
- Parameters
- ----------
- nx, nx1 : int
- Integers specifying the column-position of the cell. When *nx1* is
- None, a single *nx*-th column is specified. Otherwise, the
- location of columns spanning between *nx* to *nx1* (but excluding
- *nx1*-th column) is specified.
- ny, ny1 : int
- Same as *nx* and *nx1*, but for row positions.
- axes
- renderer
- """
- xref = self._xrefindex
- yref = self._yrefindex
- return self._locate(
- nx - xref, (nx + 1 if nx1 is None else nx1) - xref,
- ny - yref, (ny + 1 if ny1 is None else ny1) - yref,
- axes, renderer)
-
- def _locate(self, nx, ny, nx1, ny1, axes, renderer):
- """
- Implementation of ``divider.new_locator().__call__``.
-
- The axes locator callable returned by ``new_locator()`` is created as
- a `functools.partial` of this method with *nx*, *ny*, *nx1*, and *ny1*
- specifying the requested cell.
- """
- nx += self._xrefindex
- nx1 += self._xrefindex
- ny += self._yrefindex
- ny1 += self._yrefindex
-
- fig_w, fig_h = self._fig.bbox.size / self._fig.dpi
- x, y, w, h = self.get_position_runtime(axes, renderer)
-
- hsizes = self.get_horizontal_sizes(renderer)
- vsizes = self.get_vertical_sizes(renderer)
- k_h = self._calc_k(hsizes, fig_w * w)
- k_v = self._calc_k(vsizes, fig_h * h)
-
- if self.get_aspect():
- k = min(k_h, k_v)
- ox = self._calc_offsets(hsizes, k)
- oy = self._calc_offsets(vsizes, k)
-
- ww = (ox[-1] - ox[0]) / fig_w
- hh = (oy[-1] - oy[0]) / fig_h
- pb = mtransforms.Bbox.from_bounds(x, y, w, h)
- pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh)
- x0, y0 = pb1.anchored(self.get_anchor(), pb).p0
-
- else:
- ox = self._calc_offsets(hsizes, k_h)
- oy = self._calc_offsets(vsizes, k_v)
- x0, y0 = x, y
-
- if nx1 is None:
- nx1 = -1
- if ny1 is None:
- ny1 = -1
-
- x1, w1 = x0 + ox[nx] / fig_w, (ox[nx1] - ox[nx]) / fig_w
- y1, h1 = y0 + oy[ny] / fig_h, (oy[ny1] - oy[ny]) / fig_h
-
- return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
-
- def append_size(self, position, size):
- _api.check_in_list(["left", "right", "bottom", "top"],
- position=position)
- if position == "left":
- self._horizontal.insert(0, size)
- self._xrefindex += 1
- elif position == "right":
- self._horizontal.append(size)
- elif position == "bottom":
- self._vertical.insert(0, size)
- self._yrefindex += 1
- else: # 'top'
- self._vertical.append(size)
-
- def add_auto_adjustable_area(self, use_axes, pad=0.1, adjust_dirs=None):
- """
- Add auto-adjustable padding around *use_axes* to take their decorations
- (title, labels, ticks, ticklabels) into account during layout.
-
- Parameters
- ----------
- use_axes : `~matplotlib.axes.Axes` or list of `~matplotlib.axes.Axes`
- The Axes whose decorations are taken into account.
- pad : float, default: 0.1
- Additional padding in inches.
- adjust_dirs : list of {"left", "right", "bottom", "top"}, optional
- The sides where padding is added; defaults to all four sides.
- """
- if adjust_dirs is None:
- adjust_dirs = ["left", "right", "bottom", "top"]
- for d in adjust_dirs:
- self.append_size(d, Size._AxesDecorationsSize(use_axes, d) + pad)
-
-
-@_api.deprecated("3.8")
-class AxesLocator:
- """
- A callable object which returns the position and size of a given
- `.AxesDivider` cell.
- """
-
- def __init__(self, axes_divider, nx, ny, nx1=None, ny1=None):
- """
- Parameters
- ----------
- axes_divider : `~mpl_toolkits.axes_grid1.axes_divider.AxesDivider`
- nx, nx1 : int
- Integers specifying the column-position of the
- cell. When *nx1* is None, a single *nx*-th column is
- specified. Otherwise, location of columns spanning between *nx*
- to *nx1* (but excluding *nx1*-th column) is specified.
- ny, ny1 : int
- Same as *nx* and *nx1*, but for row positions.
- """
- self._axes_divider = axes_divider
-
- _xrefindex = axes_divider._xrefindex
- _yrefindex = axes_divider._yrefindex
-
- self._nx, self._ny = nx - _xrefindex, ny - _yrefindex
-
- if nx1 is None:
- nx1 = len(self._axes_divider)
- if ny1 is None:
- ny1 = len(self._axes_divider[0])
-
- self._nx1 = nx1 - _xrefindex
- self._ny1 = ny1 - _yrefindex
-
- def __call__(self, axes, renderer):
-
- _xrefindex = self._axes_divider._xrefindex
- _yrefindex = self._axes_divider._yrefindex
-
- return self._axes_divider.locate(self._nx + _xrefindex,
- self._ny + _yrefindex,
- self._nx1 + _xrefindex,
- self._ny1 + _yrefindex,
- axes,
- renderer)
-
- def get_subplotspec(self):
- return self._axes_divider.get_subplotspec()
-
-
-class SubplotDivider(Divider):
- """
- The Divider class whose rectangle area is specified as a subplot geometry.
- """
-
- def __init__(self, fig, *args, horizontal=None, vertical=None,
- aspect=None, anchor='C'):
- """
- Parameters
- ----------
- fig : `~matplotlib.figure.Figure`
-
- *args : tuple (*nrows*, *ncols*, *index*) or int
- The array of subplots in the figure has dimensions ``(nrows,
- ncols)``, and *index* is the index of the subplot being created.
- *index* starts at 1 in the upper left corner and increases to the
- right.
-
- If *nrows*, *ncols*, and *index* are all single digit numbers, then
- *args* can be passed as a single 3-digit number (e.g. 234 for
- (2, 3, 4)).
- horizontal : list of :mod:`~mpl_toolkits.axes_grid1.axes_size`, optional
- Sizes for horizontal division.
- vertical : list of :mod:`~mpl_toolkits.axes_grid1.axes_size`, optional
- Sizes for vertical division.
- aspect : bool, optional
- Whether overall rectangular area is reduced so that the relative
- part of the horizontal and vertical scales have the same scale.
- anchor : (float, float) or {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', \
-'NW', 'W'}, default: 'C'
- Placement of the reduced rectangle, when *aspect* is True.
- """
- self.figure = fig
- super().__init__(fig, [0, 0, 1, 1],
- horizontal=horizontal or [], vertical=vertical or [],
- aspect=aspect, anchor=anchor)
- self.set_subplotspec(SubplotSpec._from_subplot_args(fig, args))
-
- def get_position(self):
- """Return the bounds of the subplot box."""
- return self.get_subplotspec().get_position(self.figure).bounds
-
- def get_subplotspec(self):
- """Get the SubplotSpec instance."""
- return self._subplotspec
-
- def set_subplotspec(self, subplotspec):
- """Set the SubplotSpec instance."""
- self._subplotspec = subplotspec
- self.set_position(subplotspec.get_position(self.figure))
-
-
-class AxesDivider(Divider):
- """
- Divider based on the preexisting axes.
- """
-
- def __init__(self, axes, xref=None, yref=None):
- """
- Parameters
- ----------
- axes : :class:`~matplotlib.axes.Axes`
- xref
- yref
- """
- self._axes = axes
- if xref is None:
- self._xref = Size.AxesX(axes)
- else:
- self._xref = xref
- if yref is None:
- self._yref = Size.AxesY(axes)
- else:
- self._yref = yref
-
- super().__init__(fig=axes.get_figure(), pos=None,
- horizontal=[self._xref], vertical=[self._yref],
- aspect=None, anchor="C")
-
- def _get_new_axes(self, *, axes_class=None, **kwargs):
- axes = self._axes
- if axes_class is None:
- axes_class = type(axes)
- return axes_class(axes.get_figure(), axes.get_position(original=True),
- **kwargs)
-
- def new_horizontal(self, size, pad=None, pack_start=False, **kwargs):
- """
- Helper method for ``append_axes("left")`` and ``append_axes("right")``.
-
- See the documentation of `append_axes` for more details.
-
- :meta private:
- """
- if pad is None:
- pad = mpl.rcParams["figure.subplot.wspace"] * self._xref
- pos = "left" if pack_start else "right"
- if pad:
- if not isinstance(pad, Size._Base):
- pad = Size.from_any(pad, fraction_ref=self._xref)
- self.append_size(pos, pad)
- if not isinstance(size, Size._Base):
- size = Size.from_any(size, fraction_ref=self._xref)
- self.append_size(pos, size)
- locator = self.new_locator(
- nx=0 if pack_start else len(self._horizontal) - 1,
- ny=self._yrefindex)
- ax = self._get_new_axes(**kwargs)
- ax.set_axes_locator(locator)
- return ax
-
- def new_vertical(self, size, pad=None, pack_start=False, **kwargs):
- """
- Helper method for ``append_axes("top")`` and ``append_axes("bottom")``.
-
- See the documentation of `append_axes` for more details.
-
- :meta private:
- """
- if pad is None:
- pad = mpl.rcParams["figure.subplot.hspace"] * self._yref
- pos = "bottom" if pack_start else "top"
- if pad:
- if not isinstance(pad, Size._Base):
- pad = Size.from_any(pad, fraction_ref=self._yref)
- self.append_size(pos, pad)
- if not isinstance(size, Size._Base):
- size = Size.from_any(size, fraction_ref=self._yref)
- self.append_size(pos, size)
- locator = self.new_locator(
- nx=self._xrefindex,
- ny=0 if pack_start else len(self._vertical) - 1)
- ax = self._get_new_axes(**kwargs)
- ax.set_axes_locator(locator)
- return ax
-
- def append_axes(self, position, size, pad=None, *, axes_class=None,
- **kwargs):
- """
- Add a new axes on a given side of the main axes.
-
- Parameters
- ----------
- position : {"left", "right", "bottom", "top"}
- Where the new axes is positioned relative to the main axes.
- size : :mod:`~mpl_toolkits.axes_grid1.axes_size` or float or str
- The axes width or height. float or str arguments are interpreted
- as ``axes_size.from_any(size, AxesX(<main_axes>))`` for left or
- right axes, and likewise with ``AxesY`` for bottom or top axes.
- pad : :mod:`~mpl_toolkits.axes_grid1.axes_size` or float or str
- Padding between the axes. float or str arguments are interpreted
- as for *size*. Defaults to :rc:`figure.subplot.wspace` times the
- main Axes width (left or right axes) or :rc:`figure.subplot.hspace`
- times the main Axes height (bottom or top axes).
- axes_class : subclass type of `~.axes.Axes`, optional
- The type of the new axes. Defaults to the type of the main axes.
- **kwargs
- All extra keywords arguments are passed to the created axes.
- """
- create_axes, pack_start = _api.check_getitem({
- "left": (self.new_horizontal, True),
- "right": (self.new_horizontal, False),
- "bottom": (self.new_vertical, True),
- "top": (self.new_vertical, False),
- }, position=position)
- ax = create_axes(
- size, pad, pack_start=pack_start, axes_class=axes_class, **kwargs)
- self._fig.add_axes(ax)
- return ax
-
- def get_aspect(self):
- if self._aspect is None:
- aspect = self._axes.get_aspect()
- if aspect == "auto":
- return False
- else:
- return True
- else:
- return self._aspect
-
- def get_position(self):
- if self._pos is None:
- bbox = self._axes.get_position(original=True)
- return bbox.bounds
- else:
- return self._pos
-
- def get_anchor(self):
- if self._anchor is None:
- return self._axes.get_anchor()
- else:
- return self._anchor
-
- def get_subplotspec(self):
- return self._axes.get_subplotspec()
-
-
-# Helper for HBoxDivider/VBoxDivider.
-# The variable names are written for a horizontal layout, but the calculations
-# work identically for vertical layouts.
-def _locate(x, y, w, h, summed_widths, equal_heights, fig_w, fig_h, anchor):
-
- total_width = fig_w * w
- max_height = fig_h * h
-
- # Determine the k factors.
- n = len(equal_heights)
- eq_rels, eq_abss = equal_heights.T
- sm_rels, sm_abss = summed_widths.T
- A = np.diag([*eq_rels, 0])
- A[:n, -1] = -1
- A[-1, :-1] = sm_rels
- B = [*(-eq_abss), total_width - sm_abss.sum()]
- # A @ K = B: This finds factors {k_0, ..., k_{N-1}, H} so that
- # eq_rel_i * k_i + eq_abs_i = H for all i: all axes have the same height
- # sum(sm_rel_i * k_i + sm_abs_i) = total_width: fixed total width
- # (foo_rel_i * k_i + foo_abs_i will end up being the size of foo.)
- *karray, height = np.linalg.solve(A, B)
- if height > max_height: # Additionally, upper-bound the height.
- karray = (max_height - eq_abss) / eq_rels
-
- # Compute the offsets corresponding to these factors.
- ox = np.cumsum([0, *(sm_rels * karray + sm_abss)])
- ww = (ox[-1] - ox[0]) / fig_w
- h0_rel, h0_abs = equal_heights[0]
- hh = (karray[0]*h0_rel + h0_abs) / fig_h
- pb = mtransforms.Bbox.from_bounds(x, y, w, h)
- pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh)
- x0, y0 = pb1.anchored(anchor, pb).p0
-
- return x0, y0, ox, hh
-
-
-class HBoxDivider(SubplotDivider):
- """
- A `.SubplotDivider` for laying out axes horizontally, while ensuring that
- they have equal heights.
-
- Examples
- --------
- .. plot:: gallery/axes_grid1/demo_axes_hbox_divider.py
- """
-
- def new_locator(self, nx, nx1=None):
- """
- Create an axes locator callable for the specified cell.
-
- Parameters
- ----------
- nx, nx1 : int
- Integers specifying the column-position of the
- cell. When *nx1* is None, a single *nx*-th column is
- specified. Otherwise, location of columns spanning between *nx*
- to *nx1* (but excluding *nx1*-th column) is specified.
- """
- return super().new_locator(nx, 0, nx1, 0)
-
- def _locate(self, nx, ny, nx1, ny1, axes, renderer):
- # docstring inherited
- nx += self._xrefindex
- nx1 += self._xrefindex
- fig_w, fig_h = self._fig.bbox.size / self._fig.dpi
- x, y, w, h = self.get_position_runtime(axes, renderer)
- summed_ws = self.get_horizontal_sizes(renderer)
- equal_hs = self.get_vertical_sizes(renderer)
- x0, y0, ox, hh = _locate(
- x, y, w, h, summed_ws, equal_hs, fig_w, fig_h, self.get_anchor())
- if nx1 is None:
- nx1 = -1
- x1, w1 = x0 + ox[nx] / fig_w, (ox[nx1] - ox[nx]) / fig_w
- y1, h1 = y0, hh
- return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
-
-
-class VBoxDivider(SubplotDivider):
- """
- A `.SubplotDivider` for laying out axes vertically, while ensuring that
- they have equal widths.
- """
-
- def new_locator(self, ny, ny1=None):
- """
- Create an axes locator callable for the specified cell.
-
- Parameters
- ----------
- ny, ny1 : int
- Integers specifying the row-position of the
- cell. When *ny1* is None, a single *ny*-th row is
- specified. Otherwise, location of rows spanning between *ny*
- to *ny1* (but excluding *ny1*-th row) is specified.
- """
- return super().new_locator(0, ny, 0, ny1)
-
- def _locate(self, nx, ny, nx1, ny1, axes, renderer):
- # docstring inherited
- ny += self._yrefindex
- ny1 += self._yrefindex
- fig_w, fig_h = self._fig.bbox.size / self._fig.dpi
- x, y, w, h = self.get_position_runtime(axes, renderer)
- summed_hs = self.get_vertical_sizes(renderer)
- equal_ws = self.get_horizontal_sizes(renderer)
- y0, x0, oy, ww = _locate(
- y, x, h, w, summed_hs, equal_ws, fig_h, fig_w, self.get_anchor())
- if ny1 is None:
- ny1 = -1
- x1, w1 = x0, ww
- y1, h1 = y0 + oy[ny] / fig_h, (oy[ny1] - oy[ny]) / fig_h
- return mtransforms.Bbox.from_bounds(x1, y1, w1, h1)
-
-
-def make_axes_locatable(axes):
- divider = AxesDivider(axes)
- locator = divider.new_locator(nx=0, ny=0)
- axes.set_axes_locator(locator)
-
- return divider
-
-
-def make_axes_area_auto_adjustable(
- ax, use_axes=None, pad=0.1, adjust_dirs=None):
- """
- Add auto-adjustable padding around *ax* to take its decorations (title,
- labels, ticks, ticklabels) into account during layout, using
- `.Divider.add_auto_adjustable_area`.
-
- By default, padding is determined from the decorations of *ax*.
- Pass *use_axes* to consider the decorations of other Axes instead.
- """
- if adjust_dirs is None:
- adjust_dirs = ["left", "right", "bottom", "top"]
- divider = make_axes_locatable(ax)
- if use_axes is None:
- use_axes = ax
- divider.add_auto_adjustable_area(use_axes=use_axes, pad=pad,
- adjust_dirs=adjust_dirs)
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_grid.py b/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_grid.py
deleted file mode 100644
index 720d985414f..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_grid.py
+++ /dev/null
@@ -1,550 +0,0 @@
-from numbers import Number
-import functools
-from types import MethodType
-
-import numpy as np
-
-from matplotlib import _api, cbook
-from matplotlib.gridspec import SubplotSpec
-
-from .axes_divider import Size, SubplotDivider, Divider
-from .mpl_axes import Axes, SimpleAxisArtist
-
-
-class CbarAxesBase:
- def __init__(self, *args, orientation, **kwargs):
- self.orientation = orientation
- super().__init__(*args, **kwargs)
-
- def colorbar(self, mappable, **kwargs):
- return self.figure.colorbar(
- mappable, cax=self, location=self.orientation, **kwargs)
-
- @_api.deprecated("3.8", alternative="ax.tick_params and colorbar.set_label")
- def toggle_label(self, b):
- axis = self.axis[self.orientation]
- axis.toggle(ticklabels=b, label=b)
-
-
-_cbaraxes_class_factory = cbook._make_class_factory(CbarAxesBase, "Cbar{}")
-
-
-class Grid:
- """
- A grid of Axes.
-
- In Matplotlib, the Axes location (and size) is specified in normalized
- figure coordinates. This may not be ideal for images that needs to be
- displayed with a given aspect ratio; for example, it is difficult to
- display multiple images of a same size with some fixed padding between
- them. AxesGrid can be used in such case.
- """
-
- _defaultAxesClass = Axes
-
- def __init__(self, fig,
- rect,
- nrows_ncols,
- ngrids=None,
- direction="row",
- axes_pad=0.02,
- *,
- share_all=False,
- share_x=True,
- share_y=True,
- label_mode="L",
- axes_class=None,
- aspect=False,
- ):
- """
- Parameters
- ----------
- fig : `.Figure`
- The parent figure.
- rect : (float, float, float, float), (int, int, int), int, or \
- `~.SubplotSpec`
- The axes position, as a ``(left, bottom, width, height)`` tuple,
- as a three-digit subplot position code (e.g., ``(1, 2, 1)`` or
- ``121``), or as a `~.SubplotSpec`.
- nrows_ncols : (int, int)
- Number of rows and columns in the grid.
- ngrids : int or None, default: None
- If not None, only the first *ngrids* axes in the grid are created.
- direction : {"row", "column"}, default: "row"
- Whether axes are created in row-major ("row by row") or
- column-major order ("column by column"). This also affects the
- order in which axes are accessed using indexing (``grid[index]``).
- axes_pad : float or (float, float), default: 0.02
- Padding or (horizontal padding, vertical padding) between axes, in
- inches.
- share_all : bool, default: False
- Whether all axes share their x- and y-axis. Overrides *share_x*
- and *share_y*.
- share_x : bool, default: True
- Whether all axes of a column share their x-axis.
- share_y : bool, default: True
- Whether all axes of a row share their y-axis.
- label_mode : {"L", "1", "all", "keep"}, default: "L"
- Determines which axes will get tick labels:
-
- - "L": All axes on the left column get vertical tick labels;
- all axes on the bottom row get horizontal tick labels.
- - "1": Only the bottom left axes is labelled.
- - "all": All axes are labelled.
- - "keep": Do not do anything.
-
- axes_class : subclass of `matplotlib.axes.Axes`, default: None
- aspect : bool, default: False
- Whether the axes aspect ratio follows the aspect ratio of the data
- limits.
- """
- self._nrows, self._ncols = nrows_ncols
-
- if ngrids is None:
- ngrids = self._nrows * self._ncols
- else:
- if not 0 < ngrids <= self._nrows * self._ncols:
- raise ValueError(
- "ngrids must be positive and not larger than nrows*ncols")
-
- self.ngrids = ngrids
-
- self._horiz_pad_size, self._vert_pad_size = map(
- Size.Fixed, np.broadcast_to(axes_pad, 2))
-
- _api.check_in_list(["column", "row"], direction=direction)
- self._direction = direction
-
- if axes_class is None:
- axes_class = self._defaultAxesClass
- elif isinstance(axes_class, (list, tuple)):
- cls, kwargs = axes_class
- axes_class = functools.partial(cls, **kwargs)
-
- kw = dict(horizontal=[], vertical=[], aspect=aspect)
- if isinstance(rect, (Number, SubplotSpec)):
- self._divider = SubplotDivider(fig, rect, **kw)
- elif len(rect) == 3:
- self._divider = SubplotDivider(fig, *rect, **kw)
- elif len(rect) == 4:
- self._divider = Divider(fig, rect, **kw)
- else:
- raise TypeError("Incorrect rect format")
-
- rect = self._divider.get_position()
-
- axes_array = np.full((self._nrows, self._ncols), None, dtype=object)
- for i in range(self.ngrids):
- col, row = self._get_col_row(i)
- if share_all:
- sharex = sharey = axes_array[0, 0]
- else:
- sharex = axes_array[0, col] if share_x else None
- sharey = axes_array[row, 0] if share_y else None
- axes_array[row, col] = axes_class(
- fig, rect, sharex=sharex, sharey=sharey)
- self.axes_all = axes_array.ravel(
- order="C" if self._direction == "row" else "F").tolist()
- self.axes_column = axes_array.T.tolist()
- self.axes_row = axes_array.tolist()
- self.axes_llc = self.axes_column[0][-1]
-
- self._init_locators()
-
- for ax in self.axes_all:
- fig.add_axes(ax)
-
- self.set_label_mode(label_mode)
-
- def _init_locators(self):
- self._divider.set_horizontal(
- [Size.Scaled(1), self._horiz_pad_size] * (self._ncols-1) + [Size.Scaled(1)])
- self._divider.set_vertical(
- [Size.Scaled(1), self._vert_pad_size] * (self._nrows-1) + [Size.Scaled(1)])
- for i in range(self.ngrids):
- col, row = self._get_col_row(i)
- self.axes_all[i].set_axes_locator(
- self._divider.new_locator(nx=2 * col, ny=2 * (self._nrows - 1 - row)))
-
- def _get_col_row(self, n):
- if self._direction == "column":
- col, row = divmod(n, self._nrows)
- else:
- row, col = divmod(n, self._ncols)
-
- return col, row
-
- # Good to propagate __len__ if we have __getitem__
- def __len__(self):
- return len(self.axes_all)
-
- def __getitem__(self, i):
- return self.axes_all[i]
-
- def get_geometry(self):
- """
- Return the number of rows and columns of the grid as (nrows, ncols).
- """
- return self._nrows, self._ncols
-
- def set_axes_pad(self, axes_pad):
- """
- Set the padding between the axes.
-
- Parameters
- ----------
- axes_pad : (float, float)
- The padding (horizontal pad, vertical pad) in inches.
- """
- self._horiz_pad_size.fixed_size = axes_pad[0]
- self._vert_pad_size.fixed_size = axes_pad[1]
-
- def get_axes_pad(self):
- """
- Return the axes padding.
-
- Returns
- -------
- hpad, vpad
- Padding (horizontal pad, vertical pad) in inches.
- """
- return (self._horiz_pad_size.fixed_size,
- self._vert_pad_size.fixed_size)
-
- def set_aspect(self, aspect):
- """Set the aspect of the SubplotDivider."""
- self._divider.set_aspect(aspect)
-
- def get_aspect(self):
- """Return the aspect of the SubplotDivider."""
- return self._divider.get_aspect()
-
- def set_label_mode(self, mode):
- """
- Define which axes have tick labels.
-
- Parameters
- ----------
- mode : {"L", "1", "all", "keep"}
- The label mode:
-
- - "L": All axes on the left column get vertical tick labels;
- all axes on the bottom row get horizontal tick labels.
- - "1": Only the bottom left axes is labelled.
- - "all": All axes are labelled.
- - "keep": Do not do anything.
- """
- is_last_row, is_first_col = (
- np.mgrid[:self._nrows, :self._ncols] == [[[self._nrows - 1]], [[0]]])
- if mode == "all":
- bottom = left = np.full((self._nrows, self._ncols), True)
- elif mode == "L":
- bottom = is_last_row
- left = is_first_col
- elif mode == "1":
- bottom = left = is_last_row & is_first_col
- else:
- # Use _api.check_in_list at the top of the method when deprecation
- # period expires
- if mode != 'keep':
- _api.warn_deprecated(
- '3.7', name="Grid label_mode",
- message='Passing an undefined label_mode is deprecated '
- 'since %(since)s and will become an error '
- '%(removal)s. To silence this warning, pass '
- '"keep", which gives the same behaviour.')
- return
- for i in range(self._nrows):
- for j in range(self._ncols):
- ax = self.axes_row[i][j]
- if isinstance(ax.axis, MethodType):
- bottom_axis = SimpleAxisArtist(ax.xaxis, 1, ax.spines["bottom"])
- left_axis = SimpleAxisArtist(ax.yaxis, 1, ax.spines["left"])
- else:
- bottom_axis = ax.axis["bottom"]
- left_axis = ax.axis["left"]
- bottom_axis.toggle(ticklabels=bottom[i, j], label=bottom[i, j])
- left_axis.toggle(ticklabels=left[i, j], label=left[i, j])
-
- def get_divider(self):
- return self._divider
-
- def set_axes_locator(self, locator):
- self._divider.set_locator(locator)
-
- def get_axes_locator(self):
- return self._divider.get_locator()
-
-
-class ImageGrid(Grid):
- """
- A grid of Axes for Image display.
-
- This class is a specialization of `~.axes_grid1.axes_grid.Grid` for displaying a
- grid of images. In particular, it forces all axes in a column to share their x-axis
- and all axes in a row to share their y-axis. It further provides helpers to add
- colorbars to some or all axes.
- """
-
- def __init__(self, fig,
- rect,
- nrows_ncols,
- ngrids=None,
- direction="row",
- axes_pad=0.02,
- *,
- share_all=False,
- aspect=True,
- label_mode="L",
- cbar_mode=None,
- cbar_location="right",
- cbar_pad=None,
- cbar_size="5%",
- cbar_set_cax=True,
- axes_class=None,
- ):
- """
- Parameters
- ----------
- fig : `.Figure`
- The parent figure.
- rect : (float, float, float, float) or int
- The axes position, as a ``(left, bottom, width, height)`` tuple or
- as a three-digit subplot position code (e.g., "121").
- nrows_ncols : (int, int)
- Number of rows and columns in the grid.
- ngrids : int or None, default: None
- If not None, only the first *ngrids* axes in the grid are created.
- direction : {"row", "column"}, default: "row"
- Whether axes are created in row-major ("row by row") or
- column-major order ("column by column"). This also affects the
- order in which axes are accessed using indexing (``grid[index]``).
- axes_pad : float or (float, float), default: 0.02in
- Padding or (horizontal padding, vertical padding) between axes, in
- inches.
- share_all : bool, default: False
- Whether all axes share their x- and y-axis. Note that in any case,
- all axes in a column share their x-axis and all axes in a row share
- their y-axis.
- aspect : bool, default: True
- Whether the axes aspect ratio follows the aspect ratio of the data
- limits.
- label_mode : {"L", "1", "all"}, default: "L"
- Determines which axes will get tick labels:
-
- - "L": All axes on the left column get vertical tick labels;
- all axes on the bottom row get horizontal tick labels.
- - "1": Only the bottom left axes is labelled.
- - "all": all axes are labelled.
-
- cbar_mode : {"each", "single", "edge", None}, default: None
- Whether to create a colorbar for "each" axes, a "single" colorbar
- for the entire grid, colorbars only for axes on the "edge"
- determined by *cbar_location*, or no colorbars. The colorbars are
- stored in the :attr:`cbar_axes` attribute.
- cbar_location : {"left", "right", "bottom", "top"}, default: "right"
- cbar_pad : float, default: None
- Padding between the image axes and the colorbar axes.
- cbar_size : size specification (see `.Size.from_any`), default: "5%"
- Colorbar size.
- cbar_set_cax : bool, default: True
- If True, each axes in the grid has a *cax* attribute that is bound
- to associated *cbar_axes*.
- axes_class : subclass of `matplotlib.axes.Axes`, default: None
- """
- _api.check_in_list(["each", "single", "edge", None],
- cbar_mode=cbar_mode)
- _api.check_in_list(["left", "right", "bottom", "top"],
- cbar_location=cbar_location)
- self._colorbar_mode = cbar_mode
- self._colorbar_location = cbar_location
- self._colorbar_pad = cbar_pad
- self._colorbar_size = cbar_size
- # The colorbar axes are created in _init_locators().
-
- super().__init__(
- fig, rect, nrows_ncols, ngrids,
- direction=direction, axes_pad=axes_pad,
- share_all=share_all, share_x=True, share_y=True, aspect=aspect,
- label_mode=label_mode, axes_class=axes_class)
-
- for ax in self.cbar_axes:
- fig.add_axes(ax)
-
- if cbar_set_cax:
- if self._colorbar_mode == "single":
- for ax in self.axes_all:
- ax.cax = self.cbar_axes[0]
- elif self._colorbar_mode == "edge":
- for index, ax in enumerate(self.axes_all):
- col, row = self._get_col_row(index)
- if self._colorbar_location in ("left", "right"):
- ax.cax = self.cbar_axes[row]
- else:
- ax.cax = self.cbar_axes[col]
- else:
- for ax, cax in zip(self.axes_all, self.cbar_axes):
- ax.cax = cax
-
- def _init_locators(self):
- # Slightly abusing this method to inject colorbar creation into init.
-
- if self._colorbar_pad is None:
- # horizontal or vertical arrangement?
- if self._colorbar_location in ("left", "right"):
- self._colorbar_pad = self._horiz_pad_size.fixed_size
- else:
- self._colorbar_pad = self._vert_pad_size.fixed_size
- self.cbar_axes = [
- _cbaraxes_class_factory(self._defaultAxesClass)(
- self.axes_all[0].figure, self._divider.get_position(),
- orientation=self._colorbar_location)
- for _ in range(self.ngrids)]
-
- cb_mode = self._colorbar_mode
- cb_location = self._colorbar_location
-
- h = []
- v = []
-
- h_ax_pos = []
- h_cb_pos = []
- if cb_mode == "single" and cb_location in ("left", "bottom"):
- if cb_location == "left":
- sz = self._nrows * Size.AxesX(self.axes_llc)
- h.append(Size.from_any(self._colorbar_size, sz))
- h.append(Size.from_any(self._colorbar_pad, sz))
- locator = self._divider.new_locator(nx=0, ny=0, ny1=-1)
- elif cb_location == "bottom":
- sz = self._ncols * Size.AxesY(self.axes_llc)
- v.append(Size.from_any(self._colorbar_size, sz))
- v.append(Size.from_any(self._colorbar_pad, sz))
- locator = self._divider.new_locator(nx=0, nx1=-1, ny=0)
- for i in range(self.ngrids):
- self.cbar_axes[i].set_visible(False)
- self.cbar_axes[0].set_axes_locator(locator)
- self.cbar_axes[0].set_visible(True)
-
- for col, ax in enumerate(self.axes_row[0]):
- if h:
- h.append(self._horiz_pad_size)
-
- if ax:
- sz = Size.AxesX(ax, aspect="axes", ref_ax=self.axes_all[0])
- else:
- sz = Size.AxesX(self.axes_all[0],
- aspect="axes", ref_ax=self.axes_all[0])
-
- if (cb_location == "left"
- and (cb_mode == "each"
- or (cb_mode == "edge" and col == 0))):
- h_cb_pos.append(len(h))
- h.append(Size.from_any(self._colorbar_size, sz))
- h.append(Size.from_any(self._colorbar_pad, sz))
-
- h_ax_pos.append(len(h))
- h.append(sz)
-
- if (cb_location == "right"
- and (cb_mode == "each"
- or (cb_mode == "edge" and col == self._ncols - 1))):
- h.append(Size.from_any(self._colorbar_pad, sz))
- h_cb_pos.append(len(h))
- h.append(Size.from_any(self._colorbar_size, sz))
-
- v_ax_pos = []
- v_cb_pos = []
- for row, ax in enumerate(self.axes_column[0][::-1]):
- if v:
- v.append(self._vert_pad_size)
-
- if ax:
- sz = Size.AxesY(ax, aspect="axes", ref_ax=self.axes_all[0])
- else:
- sz = Size.AxesY(self.axes_all[0],
- aspect="axes", ref_ax=self.axes_all[0])
-
- if (cb_location == "bottom"
- and (cb_mode == "each"
- or (cb_mode == "edge" and row == 0))):
- v_cb_pos.append(len(v))
- v.append(Size.from_any(self._colorbar_size, sz))
- v.append(Size.from_any(self._colorbar_pad, sz))
-
- v_ax_pos.append(len(v))
- v.append(sz)
-
- if (cb_location == "top"
- and (cb_mode == "each"
- or (cb_mode == "edge" and row == self._nrows - 1))):
- v.append(Size.from_any(self._colorbar_pad, sz))
- v_cb_pos.append(len(v))
- v.append(Size.from_any(self._colorbar_size, sz))
-
- for i in range(self.ngrids):
- col, row = self._get_col_row(i)
- locator = self._divider.new_locator(nx=h_ax_pos[col],
- ny=v_ax_pos[self._nrows-1-row])
- self.axes_all[i].set_axes_locator(locator)
-
- if cb_mode == "each":
- if cb_location in ("right", "left"):
- locator = self._divider.new_locator(
- nx=h_cb_pos[col], ny=v_ax_pos[self._nrows - 1 - row])
-
- elif cb_location in ("top", "bottom"):
- locator = self._divider.new_locator(
- nx=h_ax_pos[col], ny=v_cb_pos[self._nrows - 1 - row])
-
- self.cbar_axes[i].set_axes_locator(locator)
- elif cb_mode == "edge":
- if (cb_location == "left" and col == 0
- or cb_location == "right" and col == self._ncols - 1):
- locator = self._divider.new_locator(
- nx=h_cb_pos[0], ny=v_ax_pos[self._nrows - 1 - row])
- self.cbar_axes[row].set_axes_locator(locator)
- elif (cb_location == "bottom" and row == self._nrows - 1
- or cb_location == "top" and row == 0):
- locator = self._divider.new_locator(nx=h_ax_pos[col],
- ny=v_cb_pos[0])
- self.cbar_axes[col].set_axes_locator(locator)
-
- if cb_mode == "single":
- if cb_location == "right":
- sz = self._nrows * Size.AxesX(self.axes_llc)
- h.append(Size.from_any(self._colorbar_pad, sz))
- h.append(Size.from_any(self._colorbar_size, sz))
- locator = self._divider.new_locator(nx=-2, ny=0, ny1=-1)
- elif cb_location == "top":
- sz = self._ncols * Size.AxesY(self.axes_llc)
- v.append(Size.from_any(self._colorbar_pad, sz))
- v.append(Size.from_any(self._colorbar_size, sz))
- locator = self._divider.new_locator(nx=0, nx1=-1, ny=-2)
- if cb_location in ("right", "top"):
- for i in range(self.ngrids):
- self.cbar_axes[i].set_visible(False)
- self.cbar_axes[0].set_axes_locator(locator)
- self.cbar_axes[0].set_visible(True)
- elif cb_mode == "each":
- for i in range(self.ngrids):
- self.cbar_axes[i].set_visible(True)
- elif cb_mode == "edge":
- if cb_location in ("right", "left"):
- count = self._nrows
- else:
- count = self._ncols
- for i in range(count):
- self.cbar_axes[i].set_visible(True)
- for j in range(i + 1, self.ngrids):
- self.cbar_axes[j].set_visible(False)
- else:
- for i in range(self.ngrids):
- self.cbar_axes[i].set_visible(False)
- self.cbar_axes[i].set_position([1., 1., 0.001, 0.001],
- which="active")
-
- self._divider.set_horizontal(h)
- self._divider.set_vertical(v)
-
-
-AxesGrid = ImageGrid
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_rgb.py b/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_rgb.py
deleted file mode 100644
index 52fd707e870..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_rgb.py
+++ /dev/null
@@ -1,157 +0,0 @@
-from types import MethodType
-
-import numpy as np
-
-from .axes_divider import make_axes_locatable, Size
-from .mpl_axes import Axes, SimpleAxisArtist
-
-
-def make_rgb_axes(ax, pad=0.01, axes_class=None, **kwargs):
- """
- Parameters
- ----------
- ax : `~matplotlib.axes.Axes`
- Axes instance to create the RGB Axes in.
- pad : float, optional
- Fraction of the Axes height to pad.
- axes_class : `matplotlib.axes.Axes` or None, optional
- Axes class to use for the R, G, and B Axes. If None, use
- the same class as *ax*.
- **kwargs
- Forwarded to *axes_class* init for the R, G, and B Axes.
- """
-
- divider = make_axes_locatable(ax)
-
- pad_size = pad * Size.AxesY(ax)
-
- xsize = ((1-2*pad)/3) * Size.AxesX(ax)
- ysize = ((1-2*pad)/3) * Size.AxesY(ax)
-
- divider.set_horizontal([Size.AxesX(ax), pad_size, xsize])
- divider.set_vertical([ysize, pad_size, ysize, pad_size, ysize])
-
- ax.set_axes_locator(divider.new_locator(0, 0, ny1=-1))
-
- ax_rgb = []
- if axes_class is None:
- axes_class = type(ax)
-
- for ny in [4, 2, 0]:
- ax1 = axes_class(ax.get_figure(), ax.get_position(original=True),
- sharex=ax, sharey=ax, **kwargs)
- locator = divider.new_locator(nx=2, ny=ny)
- ax1.set_axes_locator(locator)
- for t in ax1.yaxis.get_ticklabels() + ax1.xaxis.get_ticklabels():
- t.set_visible(False)
- try:
- for axis in ax1.axis.values():
- axis.major_ticklabels.set_visible(False)
- except AttributeError:
- pass
-
- ax_rgb.append(ax1)
-
- fig = ax.get_figure()
- for ax1 in ax_rgb:
- fig.add_axes(ax1)
-
- return ax_rgb
-
-
-class RGBAxes:
- """
- 4-panel `~.Axes.imshow` (RGB, R, G, B).
-
- Layout::
-
- ┌───────────────┬─────┐
- │ │ R │
- │ ├─────┤
- │ RGB │ G │
- │ ├─────┤
- │ │ B │
- └───────────────┴─────┘
-
- Subclasses can override the ``_defaultAxesClass`` attribute.
- By default RGBAxes uses `.mpl_axes.Axes`.
-
- Attributes
- ----------
- RGB : ``_defaultAxesClass``
- The Axes object for the three-channel `~.Axes.imshow`.
- R : ``_defaultAxesClass``
- The Axes object for the red channel `~.Axes.imshow`.
- G : ``_defaultAxesClass``
- The Axes object for the green channel `~.Axes.imshow`.
- B : ``_defaultAxesClass``
- The Axes object for the blue channel `~.Axes.imshow`.
- """
-
- _defaultAxesClass = Axes
-
- def __init__(self, *args, pad=0, **kwargs):
- """
- Parameters
- ----------
- pad : float, default: 0
- Fraction of the Axes height to put as padding.
- axes_class : `~matplotlib.axes.Axes`
- Axes class to use. If not provided, ``_defaultAxesClass`` is used.
- *args
- Forwarded to *axes_class* init for the RGB Axes
- **kwargs
- Forwarded to *axes_class* init for the RGB, R, G, and B Axes
- """
- axes_class = kwargs.pop("axes_class", self._defaultAxesClass)
- self.RGB = ax = axes_class(*args, **kwargs)
- ax.get_figure().add_axes(ax)
- self.R, self.G, self.B = make_rgb_axes(
- ax, pad=pad, axes_class=axes_class, **kwargs)
- # Set the line color and ticks for the axes.
- for ax1 in [self.RGB, self.R, self.G, self.B]:
- if isinstance(ax1.axis, MethodType):
- ad = Axes.AxisDict(self)
- ad.update(
- bottom=SimpleAxisArtist(ax1.xaxis, 1, ax1.spines["bottom"]),
- top=SimpleAxisArtist(ax1.xaxis, 2, ax1.spines["top"]),
- left=SimpleAxisArtist(ax1.yaxis, 1, ax1.spines["left"]),
- right=SimpleAxisArtist(ax1.yaxis, 2, ax1.spines["right"]))
- else:
- ad = ax1.axis
- ad[:].line.set_color("w")
- ad[:].major_ticks.set_markeredgecolor("w")
-
- def imshow_rgb(self, r, g, b, **kwargs):
- """
- Create the four images {rgb, r, g, b}.
-
- Parameters
- ----------
- r, g, b : array-like
- The red, green, and blue arrays.
- **kwargs
- Forwarded to `~.Axes.imshow` calls for the four images.
-
- Returns
- -------
- rgb : `~matplotlib.image.AxesImage`
- r : `~matplotlib.image.AxesImage`
- g : `~matplotlib.image.AxesImage`
- b : `~matplotlib.image.AxesImage`
- """
- if not (r.shape == g.shape == b.shape):
- raise ValueError(
- f'Input shapes ({r.shape}, {g.shape}, {b.shape}) do not match')
- RGB = np.dstack([r, g, b])
- R = np.zeros_like(RGB)
- R[:, :, 0] = r
- G = np.zeros_like(RGB)
- G[:, :, 1] = g
- B = np.zeros_like(RGB)
- B[:, :, 2] = b
- im_rgb = self.RGB.imshow(RGB, **kwargs)
- im_r = self.R.imshow(R, **kwargs)
- im_g = self.G.imshow(G, **kwargs)
- im_b = self.B.imshow(B, **kwargs)
- return im_rgb, im_r, im_g, im_b
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_size.py b/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_size.py
deleted file mode 100644
index d2514720772..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/axes_size.py
+++ /dev/null
@@ -1,248 +0,0 @@
-"""
-Provides classes of simple units that will be used with `.AxesDivider`
-class (or others) to determine the size of each Axes. The unit
-classes define `get_size` method that returns a tuple of two floats,
-meaning relative and absolute sizes, respectively.
-
-Note that this class is nothing more than a simple tuple of two
-floats. Take a look at the Divider class to see how these two
-values are used.
-"""
-
-from numbers import Real
-
-from matplotlib import _api
-from matplotlib.axes import Axes
-
-
-class _Base:
- def __rmul__(self, other):
- return Fraction(other, self)
-
- def __add__(self, other):
- if isinstance(other, _Base):
- return Add(self, other)
- else:
- return Add(self, Fixed(other))
-
- def get_size(self, renderer):
- """
- Return two-float tuple with relative and absolute sizes.
- """
- raise NotImplementedError("Subclasses must implement")
-
-
-class Add(_Base):
- """
- Sum of two sizes.
- """
-
- def __init__(self, a, b):
- self._a = a
- self._b = b
-
- def get_size(self, renderer):
- a_rel_size, a_abs_size = self._a.get_size(renderer)
- b_rel_size, b_abs_size = self._b.get_size(renderer)
- return a_rel_size + b_rel_size, a_abs_size + b_abs_size
-
-
-class Fixed(_Base):
- """
- Simple fixed size with absolute part = *fixed_size* and relative part = 0.
- """
-
- def __init__(self, fixed_size):
- _api.check_isinstance(Real, fixed_size=fixed_size)
- self.fixed_size = fixed_size
-
- def get_size(self, renderer):
- rel_size = 0.
- abs_size = self.fixed_size
- return rel_size, abs_size
-
-
-class Scaled(_Base):
- """
- Simple scaled(?) size with absolute part = 0 and
- relative part = *scalable_size*.
- """
-
- def __init__(self, scalable_size):
- self._scalable_size = scalable_size
-
- def get_size(self, renderer):
- rel_size = self._scalable_size
- abs_size = 0.
- return rel_size, abs_size
-
-Scalable = Scaled
-
-
-def _get_axes_aspect(ax):
- aspect = ax.get_aspect()
- if aspect == "auto":
- aspect = 1.
- return aspect
-
-
-class AxesX(_Base):
- """
- Scaled size whose relative part corresponds to the data width
- of the *axes* multiplied by the *aspect*.
- """
-
- def __init__(self, axes, aspect=1., ref_ax=None):
- self._axes = axes
- self._aspect = aspect
- if aspect == "axes" and ref_ax is None:
- raise ValueError("ref_ax must be set when aspect='axes'")
- self._ref_ax = ref_ax
-
- def get_size(self, renderer):
- l1, l2 = self._axes.get_xlim()
- if self._aspect == "axes":
- ref_aspect = _get_axes_aspect(self._ref_ax)
- aspect = ref_aspect / _get_axes_aspect(self._axes)
- else:
- aspect = self._aspect
-
- rel_size = abs(l2-l1)*aspect
- abs_size = 0.
- return rel_size, abs_size
-
-
-class AxesY(_Base):
- """
- Scaled size whose relative part corresponds to the data height
- of the *axes* multiplied by the *aspect*.
- """
-
- def __init__(self, axes, aspect=1., ref_ax=None):
- self._axes = axes
- self._aspect = aspect
- if aspect == "axes" and ref_ax is None:
- raise ValueError("ref_ax must be set when aspect='axes'")
- self._ref_ax = ref_ax
-
- def get_size(self, renderer):
- l1, l2 = self._axes.get_ylim()
-
- if self._aspect == "axes":
- ref_aspect = _get_axes_aspect(self._ref_ax)
- aspect = _get_axes_aspect(self._axes)
- else:
- aspect = self._aspect
-
- rel_size = abs(l2-l1)*aspect
- abs_size = 0.
- return rel_size, abs_size
-
-
-class MaxExtent(_Base):
- """
- Size whose absolute part is either the largest width or the largest height
- of the given *artist_list*.
- """
-
- def __init__(self, artist_list, w_or_h):
- self._artist_list = artist_list
- _api.check_in_list(["width", "height"], w_or_h=w_or_h)
- self._w_or_h = w_or_h
-
- def add_artist(self, a):
- self._artist_list.append(a)
-
- def get_size(self, renderer):
- rel_size = 0.
- extent_list = [
- getattr(a.get_window_extent(renderer), self._w_or_h) / a.figure.dpi
- for a in self._artist_list]
- abs_size = max(extent_list, default=0)
- return rel_size, abs_size
-
-
-class MaxWidth(MaxExtent):
- """
- Size whose absolute part is the largest width of the given *artist_list*.
- """
-
- def __init__(self, artist_list):
- super().__init__(artist_list, "width")
-
-
-class MaxHeight(MaxExtent):
- """
- Size whose absolute part is the largest height of the given *artist_list*.
- """
-
- def __init__(self, artist_list):
- super().__init__(artist_list, "height")
-
-
-class Fraction(_Base):
- """
- An instance whose size is a *fraction* of the *ref_size*.
-
- >>> s = Fraction(0.3, AxesX(ax))
- """
-
- def __init__(self, fraction, ref_size):
- _api.check_isinstance(Real, fraction=fraction)
- self._fraction_ref = ref_size
- self._fraction = fraction
-
- def get_size(self, renderer):
- if self._fraction_ref is None:
- return self._fraction, 0.
- else:
- r, a = self._fraction_ref.get_size(renderer)
- rel_size = r*self._fraction
- abs_size = a*self._fraction
- return rel_size, abs_size
-
-
-def from_any(size, fraction_ref=None):
- """
- Create a Fixed unit when the first argument is a float, or a
- Fraction unit if that is a string that ends with %. The second
- argument is only meaningful when Fraction unit is created.
-
- >>> from mpl_toolkits.axes_grid1.axes_size import from_any
- >>> a = from_any(1.2) # => Fixed(1.2)
- >>> from_any("50%", a) # => Fraction(0.5, a)
- """
- if isinstance(size, Real):
- return Fixed(size)
- elif isinstance(size, str):
- if size[-1] == "%":
- return Fraction(float(size[:-1]) / 100, fraction_ref)
- raise ValueError("Unknown format")
-
-
-class _AxesDecorationsSize(_Base):
- """
- Fixed size, corresponding to the size of decorations on a given Axes side.
- """
-
- _get_size_map = {
- "left": lambda tight_bb, axes_bb: axes_bb.xmin - tight_bb.xmin,
- "right": lambda tight_bb, axes_bb: tight_bb.xmax - axes_bb.xmax,
- "bottom": lambda tight_bb, axes_bb: axes_bb.ymin - tight_bb.ymin,
- "top": lambda tight_bb, axes_bb: tight_bb.ymax - axes_bb.ymax,
- }
-
- def __init__(self, ax, direction):
- self._get_size = _api.check_getitem(
- self._get_size_map, direction=direction)
- self._ax_list = [ax] if isinstance(ax, Axes) else ax
-
- def get_size(self, renderer):
- sz = max([
- self._get_size(ax.get_tightbbox(renderer, call_axes_locator=False),
- ax.bbox)
- for ax in self._ax_list])
- dpi = renderer.points_to_pixels(72)
- abs_size = sz / dpi
- rel_size = 0
- return rel_size, abs_size
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/inset_locator.py b/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/inset_locator.py
deleted file mode 100644
index 6d591a45311..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/inset_locator.py
+++ /dev/null
@@ -1,561 +0,0 @@
-"""
-A collection of functions and objects for creating or placing inset axes.
-"""
-
-from matplotlib import _api, _docstring
-from matplotlib.offsetbox import AnchoredOffsetbox
-from matplotlib.patches import Patch, Rectangle
-from matplotlib.path import Path
-from matplotlib.transforms import Bbox, BboxTransformTo
-from matplotlib.transforms import IdentityTransform, TransformedBbox
-
-from . import axes_size as Size
-from .parasite_axes import HostAxes
-
-
-@_api.deprecated("3.8", alternative="Axes.inset_axes")
-class InsetPosition:
- @_docstring.dedent_interpd
- def __init__(self, parent, lbwh):
- """
- An object for positioning an inset axes.
-
- This is created by specifying the normalized coordinates in the axes,
- instead of the figure.
-
- Parameters
- ----------
- parent : `~matplotlib.axes.Axes`
- Axes to use for normalizing coordinates.
-
- lbwh : iterable of four floats
- The left edge, bottom edge, width, and height of the inset axes, in
- units of the normalized coordinate of the *parent* axes.
-
- See Also
- --------
- :meth:`matplotlib.axes.Axes.set_axes_locator`
-
- Examples
- --------
- The following bounds the inset axes to a box with 20%% of the parent
- axes height and 40%% of the width. The size of the axes specified
- ([0, 0, 1, 1]) ensures that the axes completely fills the bounding box:
-
- >>> parent_axes = plt.gca()
- >>> ax_ins = plt.axes([0, 0, 1, 1])
- >>> ip = InsetPosition(parent_axes, [0.5, 0.1, 0.4, 0.2])
- >>> ax_ins.set_axes_locator(ip)
- """
- self.parent = parent
- self.lbwh = lbwh
-
- def __call__(self, ax, renderer):
- bbox_parent = self.parent.get_position(original=False)
- trans = BboxTransformTo(bbox_parent)
- bbox_inset = Bbox.from_bounds(*self.lbwh)
- bb = TransformedBbox(bbox_inset, trans)
- return bb
-
-
-class AnchoredLocatorBase(AnchoredOffsetbox):
- def __init__(self, bbox_to_anchor, offsetbox, loc,
- borderpad=0.5, bbox_transform=None):
- super().__init__(
- loc, pad=0., child=None, borderpad=borderpad,
- bbox_to_anchor=bbox_to_anchor, bbox_transform=bbox_transform
- )
-
- def draw(self, renderer):
- raise RuntimeError("No draw method should be called")
-
- def __call__(self, ax, renderer):
- if renderer is None:
- renderer = ax.figure._get_renderer()
- self.axes = ax
- bbox = self.get_window_extent(renderer)
- px, py = self.get_offset(bbox.width, bbox.height, 0, 0, renderer)
- bbox_canvas = Bbox.from_bounds(px, py, bbox.width, bbox.height)
- tr = ax.figure.transSubfigure.inverted()
- return TransformedBbox(bbox_canvas, tr)
-
-
-class AnchoredSizeLocator(AnchoredLocatorBase):
- def __init__(self, bbox_to_anchor, x_size, y_size, loc,
- borderpad=0.5, bbox_transform=None):
- super().__init__(
- bbox_to_anchor, None, loc,
- borderpad=borderpad, bbox_transform=bbox_transform
- )
-
- self.x_size = Size.from_any(x_size)
- self.y_size = Size.from_any(y_size)
-
- def get_bbox(self, renderer):
- bbox = self.get_bbox_to_anchor()
- dpi = renderer.points_to_pixels(72.)
-
- r, a = self.x_size.get_size(renderer)
- width = bbox.width * r + a * dpi
- r, a = self.y_size.get_size(renderer)
- height = bbox.height * r + a * dpi
-
- fontsize = renderer.points_to_pixels(self.prop.get_size_in_points())
- pad = self.pad * fontsize
-
- return Bbox.from_bounds(0, 0, width, height).padded(pad)
-
-
-class AnchoredZoomLocator(AnchoredLocatorBase):
- def __init__(self, parent_axes, zoom, loc,
- borderpad=0.5,
- bbox_to_anchor=None,
- bbox_transform=None):
- self.parent_axes = parent_axes
- self.zoom = zoom
- if bbox_to_anchor is None:
- bbox_to_anchor = parent_axes.bbox
- super().__init__(
- bbox_to_anchor, None, loc, borderpad=borderpad,
- bbox_transform=bbox_transform)
-
- def get_bbox(self, renderer):
- bb = self.parent_axes.transData.transform_bbox(self.axes.viewLim)
- fontsize = renderer.points_to_pixels(self.prop.get_size_in_points())
- pad = self.pad * fontsize
- return (
- Bbox.from_bounds(
- 0, 0, abs(bb.width * self.zoom), abs(bb.height * self.zoom))
- .padded(pad))
-
-
-class BboxPatch(Patch):
- @_docstring.dedent_interpd
- def __init__(self, bbox, **kwargs):
- """
- Patch showing the shape bounded by a Bbox.
-
- Parameters
- ----------
- bbox : `~matplotlib.transforms.Bbox`
- Bbox to use for the extents of this patch.
-
- **kwargs
- Patch properties. Valid arguments include:
-
- %(Patch:kwdoc)s
- """
- if "transform" in kwargs:
- raise ValueError("transform should not be set")
-
- kwargs["transform"] = IdentityTransform()
- super().__init__(**kwargs)
- self.bbox = bbox
-
- def get_path(self):
- # docstring inherited
- x0, y0, x1, y1 = self.bbox.extents
- return Path._create_closed([(x0, y0), (x1, y0), (x1, y1), (x0, y1)])
-
-
-class BboxConnector(Patch):
- @staticmethod
- def get_bbox_edge_pos(bbox, loc):
- """
- Return the ``(x, y)`` coordinates of corner *loc* of *bbox*; parameters
- behave as documented for the `.BboxConnector` constructor.
- """
- x0, y0, x1, y1 = bbox.extents
- if loc == 1:
- return x1, y1
- elif loc == 2:
- return x0, y1
- elif loc == 3:
- return x0, y0
- elif loc == 4:
- return x1, y0
-
- @staticmethod
- def connect_bbox(bbox1, bbox2, loc1, loc2=None):
- """
- Construct a `.Path` connecting corner *loc1* of *bbox1* to corner
- *loc2* of *bbox2*, where parameters behave as documented as for the
- `.BboxConnector` constructor.
- """
- if isinstance(bbox1, Rectangle):
- bbox1 = TransformedBbox(Bbox.unit(), bbox1.get_transform())
- if isinstance(bbox2, Rectangle):
- bbox2 = TransformedBbox(Bbox.unit(), bbox2.get_transform())
- if loc2 is None:
- loc2 = loc1
- x1, y1 = BboxConnector.get_bbox_edge_pos(bbox1, loc1)
- x2, y2 = BboxConnector.get_bbox_edge_pos(bbox2, loc2)
- return Path([[x1, y1], [x2, y2]])
-
- @_docstring.dedent_interpd
- def __init__(self, bbox1, bbox2, loc1, loc2=None, **kwargs):
- """
- Connect two bboxes with a straight line.
-
- Parameters
- ----------
- bbox1, bbox2 : `~matplotlib.transforms.Bbox`
- Bounding boxes to connect.
-
- loc1, loc2 : {1, 2, 3, 4}
- Corner of *bbox1* and *bbox2* to draw the line. Valid values are::
-
- 'upper right' : 1,
- 'upper left' : 2,
- 'lower left' : 3,
- 'lower right' : 4
-
- *loc2* is optional and defaults to *loc1*.
-
- **kwargs
- Patch properties for the line drawn. Valid arguments include:
-
- %(Patch:kwdoc)s
- """
- if "transform" in kwargs:
- raise ValueError("transform should not be set")
-
- kwargs["transform"] = IdentityTransform()
- kwargs.setdefault(
- "fill", bool({'fc', 'facecolor', 'color'}.intersection(kwargs)))
- super().__init__(**kwargs)
- self.bbox1 = bbox1
- self.bbox2 = bbox2
- self.loc1 = loc1
- self.loc2 = loc2
-
- def get_path(self):
- # docstring inherited
- return self.connect_bbox(self.bbox1, self.bbox2,
- self.loc1, self.loc2)
-
-
-class BboxConnectorPatch(BboxConnector):
- @_docstring.dedent_interpd
- def __init__(self, bbox1, bbox2, loc1a, loc2a, loc1b, loc2b, **kwargs):
- """
- Connect two bboxes with a quadrilateral.
-
- The quadrilateral is specified by two lines that start and end at
- corners of the bboxes. The four sides of the quadrilateral are defined
- by the two lines given, the line between the two corners specified in
- *bbox1* and the line between the two corners specified in *bbox2*.
-
- Parameters
- ----------
- bbox1, bbox2 : `~matplotlib.transforms.Bbox`
- Bounding boxes to connect.
-
- loc1a, loc2a, loc1b, loc2b : {1, 2, 3, 4}
- The first line connects corners *loc1a* of *bbox1* and *loc2a* of
- *bbox2*; the second line connects corners *loc1b* of *bbox1* and
- *loc2b* of *bbox2*. Valid values are::
-
- 'upper right' : 1,
- 'upper left' : 2,
- 'lower left' : 3,
- 'lower right' : 4
-
- **kwargs
- Patch properties for the line drawn:
-
- %(Patch:kwdoc)s
- """
- if "transform" in kwargs:
- raise ValueError("transform should not be set")
- super().__init__(bbox1, bbox2, loc1a, loc2a, **kwargs)
- self.loc1b = loc1b
- self.loc2b = loc2b
-
- def get_path(self):
- # docstring inherited
- path1 = self.connect_bbox(self.bbox1, self.bbox2, self.loc1, self.loc2)
- path2 = self.connect_bbox(self.bbox2, self.bbox1,
- self.loc2b, self.loc1b)
- path_merged = [*path1.vertices, *path2.vertices, path1.vertices[0]]
- return Path(path_merged)
-
-
-def _add_inset_axes(parent_axes, axes_class, axes_kwargs, axes_locator):
- """Helper function to add an inset axes and disable navigation in it."""
- if axes_class is None:
- axes_class = HostAxes
- if axes_kwargs is None:
- axes_kwargs = {}
- inset_axes = axes_class(
- parent_axes.figure, parent_axes.get_position(),
- **{"navigate": False, **axes_kwargs, "axes_locator": axes_locator})
- return parent_axes.figure.add_axes(inset_axes)
-
-
-@_docstring.dedent_interpd
-def inset_axes(parent_axes, width, height, loc='upper right',
- bbox_to_anchor=None, bbox_transform=None,
- axes_class=None, axes_kwargs=None,
- borderpad=0.5):
- """
- Create an inset axes with a given width and height.
-
- Both sizes used can be specified either in inches or percentage.
- For example,::
-
- inset_axes(parent_axes, width='40%%', height='30%%', loc='lower left')
-
- creates in inset axes in the lower left corner of *parent_axes* which spans
- over 30%% in height and 40%% in width of the *parent_axes*. Since the usage
- of `.inset_axes` may become slightly tricky when exceeding such standard
- cases, it is recommended to read :doc:`the examples
- </gallery/axes_grid1/inset_locator_demo>`.
-
- Notes
- -----
- The meaning of *bbox_to_anchor* and *bbox_to_transform* is interpreted
- differently from that of legend. The value of bbox_to_anchor
- (or the return value of its get_points method; the default is
- *parent_axes.bbox*) is transformed by the bbox_transform (the default
- is Identity transform) and then interpreted as points in the pixel
- coordinate (which is dpi dependent).
-
- Thus, following three calls are identical and creates an inset axes
- with respect to the *parent_axes*::
-
- axins = inset_axes(parent_axes, "30%%", "40%%")
- axins = inset_axes(parent_axes, "30%%", "40%%",
- bbox_to_anchor=parent_axes.bbox)
- axins = inset_axes(parent_axes, "30%%", "40%%",
- bbox_to_anchor=(0, 0, 1, 1),
- bbox_transform=parent_axes.transAxes)
-
- Parameters
- ----------
- parent_axes : `matplotlib.axes.Axes`
- Axes to place the inset axes.
-
- width, height : float or str
- Size of the inset axes to create. If a float is provided, it is
- the size in inches, e.g. *width=1.3*. If a string is provided, it is
- the size in relative units, e.g. *width='40%%'*. By default, i.e. if
- neither *bbox_to_anchor* nor *bbox_transform* are specified, those
- are relative to the parent_axes. Otherwise, they are to be understood
- relative to the bounding box provided via *bbox_to_anchor*.
-
- loc : str, default: 'upper right'
- Location to place the inset axes. Valid locations are
- 'upper left', 'upper center', 'upper right',
- 'center left', 'center', 'center right',
- 'lower left', 'lower center', 'lower right'.
- For backward compatibility, numeric values are accepted as well.
- See the parameter *loc* of `.Legend` for details.
-
- bbox_to_anchor : tuple or `~matplotlib.transforms.BboxBase`, optional
- Bbox that the inset axes will be anchored to. If None,
- a tuple of (0, 0, 1, 1) is used if *bbox_transform* is set
- to *parent_axes.transAxes* or *parent_axes.figure.transFigure*.
- Otherwise, *parent_axes.bbox* is used. If a tuple, can be either
- [left, bottom, width, height], or [left, bottom].
- If the kwargs *width* and/or *height* are specified in relative units,
- the 2-tuple [left, bottom] cannot be used. Note that,
- unless *bbox_transform* is set, the units of the bounding box
- are interpreted in the pixel coordinate. When using *bbox_to_anchor*
- with tuple, it almost always makes sense to also specify
- a *bbox_transform*. This might often be the axes transform
- *parent_axes.transAxes*.
-
- bbox_transform : `~matplotlib.transforms.Transform`, optional
- Transformation for the bbox that contains the inset axes.
- If None, a `.transforms.IdentityTransform` is used. The value
- of *bbox_to_anchor* (or the return value of its get_points method)
- is transformed by the *bbox_transform* and then interpreted
- as points in the pixel coordinate (which is dpi dependent).
- You may provide *bbox_to_anchor* in some normalized coordinate,
- and give an appropriate transform (e.g., *parent_axes.transAxes*).
-
- axes_class : `~matplotlib.axes.Axes` type, default: `.HostAxes`
- The type of the newly created inset axes.
-
- axes_kwargs : dict, optional
- Keyword arguments to pass to the constructor of the inset axes.
- Valid arguments include:
-
- %(Axes:kwdoc)s
-
- borderpad : float, default: 0.5
- Padding between inset axes and the bbox_to_anchor.
- The units are axes font size, i.e. for a default font size of 10 points
- *borderpad = 0.5* is equivalent to a padding of 5 points.
-
- Returns
- -------
- inset_axes : *axes_class*
- Inset axes object created.
- """
-
- if (bbox_transform in [parent_axes.transAxes, parent_axes.figure.transFigure]
- and bbox_to_anchor is None):
- _api.warn_external("Using the axes or figure transform requires a "
- "bounding box in the respective coordinates. "
- "Using bbox_to_anchor=(0, 0, 1, 1) now.")
- bbox_to_anchor = (0, 0, 1, 1)
- if bbox_to_anchor is None:
- bbox_to_anchor = parent_axes.bbox
- if (isinstance(bbox_to_anchor, tuple) and
- (isinstance(width, str) or isinstance(height, str))):
- if len(bbox_to_anchor) != 4:
- raise ValueError("Using relative units for width or height "
- "requires to provide a 4-tuple or a "
- "`Bbox` instance to `bbox_to_anchor.")
- return _add_inset_axes(
- parent_axes, axes_class, axes_kwargs,
- AnchoredSizeLocator(
- bbox_to_anchor, width, height, loc=loc,
- bbox_transform=bbox_transform, borderpad=borderpad))
-
-
-@_docstring.dedent_interpd
-def zoomed_inset_axes(parent_axes, zoom, loc='upper right',
- bbox_to_anchor=None, bbox_transform=None,
- axes_class=None, axes_kwargs=None,
- borderpad=0.5):
- """
- Create an anchored inset axes by scaling a parent axes. For usage, also see
- :doc:`the examples </gallery/axes_grid1/inset_locator_demo2>`.
-
- Parameters
- ----------
- parent_axes : `~matplotlib.axes.Axes`
- Axes to place the inset axes.
-
- zoom : float
- Scaling factor of the data axes. *zoom* > 1 will enlarge the
- coordinates (i.e., "zoomed in"), while *zoom* < 1 will shrink the
- coordinates (i.e., "zoomed out").
-
- loc : str, default: 'upper right'
- Location to place the inset axes. Valid locations are
- 'upper left', 'upper center', 'upper right',
- 'center left', 'center', 'center right',
- 'lower left', 'lower center', 'lower right'.
- For backward compatibility, numeric values are accepted as well.
- See the parameter *loc* of `.Legend` for details.
-
- bbox_to_anchor : tuple or `~matplotlib.transforms.BboxBase`, optional
- Bbox that the inset axes will be anchored to. If None,
- *parent_axes.bbox* is used. If a tuple, can be either
- [left, bottom, width, height], or [left, bottom].
- If the kwargs *width* and/or *height* are specified in relative units,
- the 2-tuple [left, bottom] cannot be used. Note that
- the units of the bounding box are determined through the transform
- in use. When using *bbox_to_anchor* it almost always makes sense to
- also specify a *bbox_transform*. This might often be the axes transform
- *parent_axes.transAxes*.
-
- bbox_transform : `~matplotlib.transforms.Transform`, optional
- Transformation for the bbox that contains the inset axes.
- If None, a `.transforms.IdentityTransform` is used (i.e. pixel
- coordinates). This is useful when not providing any argument to
- *bbox_to_anchor*. When using *bbox_to_anchor* it almost always makes
- sense to also specify a *bbox_transform*. This might often be the
- axes transform *parent_axes.transAxes*. Inversely, when specifying
- the axes- or figure-transform here, be aware that not specifying
- *bbox_to_anchor* will use *parent_axes.bbox*, the units of which are
- in display (pixel) coordinates.
-
- axes_class : `~matplotlib.axes.Axes` type, default: `.HostAxes`
- The type of the newly created inset axes.
-
- axes_kwargs : dict, optional
- Keyword arguments to pass to the constructor of the inset axes.
- Valid arguments include:
-
- %(Axes:kwdoc)s
-
- borderpad : float, default: 0.5
- Padding between inset axes and the bbox_to_anchor.
- The units are axes font size, i.e. for a default font size of 10 points
- *borderpad = 0.5* is equivalent to a padding of 5 points.
-
- Returns
- -------
- inset_axes : *axes_class*
- Inset axes object created.
- """
-
- return _add_inset_axes(
- parent_axes, axes_class, axes_kwargs,
- AnchoredZoomLocator(
- parent_axes, zoom=zoom, loc=loc,
- bbox_to_anchor=bbox_to_anchor, bbox_transform=bbox_transform,
- borderpad=borderpad))
-
-
-class _TransformedBboxWithCallback(TransformedBbox):
- """
- Variant of `.TransformBbox` which calls *callback* before returning points.
-
- Used by `.mark_inset` to unstale the parent axes' viewlim as needed.
- """
-
- def __init__(self, *args, callback, **kwargs):
- super().__init__(*args, **kwargs)
- self._callback = callback
-
- def get_points(self):
- self._callback()
- return super().get_points()
-
-
-@_docstring.dedent_interpd
-def mark_inset(parent_axes, inset_axes, loc1, loc2, **kwargs):
- """
- Draw a box to mark the location of an area represented by an inset axes.
-
- This function draws a box in *parent_axes* at the bounding box of
- *inset_axes*, and shows a connection with the inset axes by drawing lines
- at the corners, giving a "zoomed in" effect.
-
- Parameters
- ----------
- parent_axes : `~matplotlib.axes.Axes`
- Axes which contains the area of the inset axes.
-
- inset_axes : `~matplotlib.axes.Axes`
- The inset axes.
-
- loc1, loc2 : {1, 2, 3, 4}
- Corners to use for connecting the inset axes and the area in the
- parent axes.
-
- **kwargs
- Patch properties for the lines and box drawn:
-
- %(Patch:kwdoc)s
-
- Returns
- -------
- pp : `~matplotlib.patches.Patch`
- The patch drawn to represent the area of the inset axes.
-
- p1, p2 : `~matplotlib.patches.Patch`
- The patches connecting two corners of the inset axes and its area.
- """
- rect = _TransformedBboxWithCallback(
- inset_axes.viewLim, parent_axes.transData,
- callback=parent_axes._unstale_viewLim)
-
- kwargs.setdefault("fill", bool({'fc', 'facecolor', 'color'}.intersection(kwargs)))
- pp = BboxPatch(rect, **kwargs)
- parent_axes.add_patch(pp)
-
- p1 = BboxConnector(inset_axes.bbox, rect, loc1=loc1, **kwargs)
- inset_axes.add_patch(p1)
- p1.set_clip_on(False)
- p2 = BboxConnector(inset_axes.bbox, rect, loc1=loc2, **kwargs)
- inset_axes.add_patch(p2)
- p2.set_clip_on(False)
-
- return pp, p1, p2
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/mpl_axes.py b/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/mpl_axes.py
deleted file mode 100644
index 51c8748758c..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/mpl_axes.py
+++ /dev/null
@@ -1,128 +0,0 @@
-import matplotlib.axes as maxes
-from matplotlib.artist import Artist
-from matplotlib.axis import XAxis, YAxis
-
-
-class SimpleChainedObjects:
- def __init__(self, objects):
- self._objects = objects
-
- def __getattr__(self, k):
- _a = SimpleChainedObjects([getattr(a, k) for a in self._objects])
- return _a
-
- def __call__(self, *args, **kwargs):
- for m in self._objects:
- m(*args, **kwargs)
-
-
-class Axes(maxes.Axes):
-
- class AxisDict(dict):
- def __init__(self, axes):
- self.axes = axes
- super().__init__()
-
- def __getitem__(self, k):
- if isinstance(k, tuple):
- r = SimpleChainedObjects(
- # super() within a list comprehension needs explicit args.
- [super(Axes.AxisDict, self).__getitem__(k1) for k1 in k])
- return r
- elif isinstance(k, slice):
- if k.start is None and k.stop is None and k.step is None:
- return SimpleChainedObjects(list(self.values()))
- else:
- raise ValueError("Unsupported slice")
- else:
- return dict.__getitem__(self, k)
-
- def __call__(self, *v, **kwargs):
- return maxes.Axes.axis(self.axes, *v, **kwargs)
-
- @property
- def axis(self):
- return self._axislines
-
- def clear(self):
- # docstring inherited
- super().clear()
- # Init axis artists.
- self._axislines = self.AxisDict(self)
- self._axislines.update(
- bottom=SimpleAxisArtist(self.xaxis, 1, self.spines["bottom"]),
- top=SimpleAxisArtist(self.xaxis, 2, self.spines["top"]),
- left=SimpleAxisArtist(self.yaxis, 1, self.spines["left"]),
- right=SimpleAxisArtist(self.yaxis, 2, self.spines["right"]))
-
-
-class SimpleAxisArtist(Artist):
- def __init__(self, axis, axisnum, spine):
- self._axis = axis
- self._axisnum = axisnum
- self.line = spine
-
- if isinstance(axis, XAxis):
- self._axis_direction = ["bottom", "top"][axisnum-1]
- elif isinstance(axis, YAxis):
- self._axis_direction = ["left", "right"][axisnum-1]
- else:
- raise ValueError(
- f"axis must be instance of XAxis or YAxis, but got {axis}")
- super().__init__()
-
- @property
- def major_ticks(self):
- tickline = "tick%dline" % self._axisnum
- return SimpleChainedObjects([getattr(tick, tickline)
- for tick in self._axis.get_major_ticks()])
-
- @property
- def major_ticklabels(self):
- label = "label%d" % self._axisnum
- return SimpleChainedObjects([getattr(tick, label)
- for tick in self._axis.get_major_ticks()])
-
- @property
- def label(self):
- return self._axis.label
-
- def set_visible(self, b):
- self.toggle(all=b)
- self.line.set_visible(b)
- self._axis.set_visible(True)
- super().set_visible(b)
-
- def set_label(self, txt):
- self._axis.set_label_text(txt)
-
- def toggle(self, all=None, ticks=None, ticklabels=None, label=None):
-
- if all:
- _ticks, _ticklabels, _label = True, True, True
- elif all is not None:
- _ticks, _ticklabels, _label = False, False, False
- else:
- _ticks, _ticklabels, _label = None, None, None
-
- if ticks is not None:
- _ticks = ticks
- if ticklabels is not None:
- _ticklabels = ticklabels
- if label is not None:
- _label = label
-
- if _ticks is not None:
- tickparam = {f"tick{self._axisnum}On": _ticks}
- self._axis.set_tick_params(**tickparam)
- if _ticklabels is not None:
- tickparam = {f"label{self._axisnum}On": _ticklabels}
- self._axis.set_tick_params(**tickparam)
-
- if _label is not None:
- pos = self._axis.get_label_position()
- if (pos == self._axis_direction) and not _label:
- self._axis.label.set_visible(False)
- elif _label:
- self._axis.label.set_visible(True)
- self._axis.set_label_position(self._axis_direction)
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/parasite_axes.py b/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/parasite_axes.py
deleted file mode 100644
index 2a2b5957e84..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axes_grid1/parasite_axes.py
+++ /dev/null
@@ -1,257 +0,0 @@
-from matplotlib import _api, cbook
-import matplotlib.artist as martist
-import matplotlib.transforms as mtransforms
-from matplotlib.transforms import Bbox
-from .mpl_axes import Axes
-
-
-class ParasiteAxesBase:
-
- def __init__(self, parent_axes, aux_transform=None,
- *, viewlim_mode=None, **kwargs):
- self._parent_axes = parent_axes
- self.transAux = aux_transform
- self.set_viewlim_mode(viewlim_mode)
- kwargs["frameon"] = False
- super().__init__(parent_axes.figure, parent_axes._position, **kwargs)
-
- def clear(self):
- super().clear()
- martist.setp(self.get_children(), visible=False)
- self._get_lines = self._parent_axes._get_lines
- self._parent_axes.callbacks._connect_picklable(
- "xlim_changed", self._sync_lims)
- self._parent_axes.callbacks._connect_picklable(
- "ylim_changed", self._sync_lims)
-
- def pick(self, mouseevent):
- # This most likely goes to Artist.pick (depending on axes_class given
- # to the factory), which only handles pick events registered on the
- # axes associated with each child:
- super().pick(mouseevent)
- # But parasite axes are additionally given pick events from their host
- # axes (cf. HostAxesBase.pick), which we handle here:
- for a in self.get_children():
- if (hasattr(mouseevent.inaxes, "parasites")
- and self in mouseevent.inaxes.parasites):
- a.pick(mouseevent)
-
- # aux_transform support
-
- def _set_lim_and_transforms(self):
- if self.transAux is not None:
- self.transAxes = self._parent_axes.transAxes
- self.transData = self.transAux + self._parent_axes.transData
- self._xaxis_transform = mtransforms.blended_transform_factory(
- self.transData, self.transAxes)
- self._yaxis_transform = mtransforms.blended_transform_factory(
- self.transAxes, self.transData)
- else:
- super()._set_lim_and_transforms()
-
- def set_viewlim_mode(self, mode):
- _api.check_in_list([None, "equal", "transform"], mode=mode)
- self._viewlim_mode = mode
-
- def get_viewlim_mode(self):
- return self._viewlim_mode
-
- def _sync_lims(self, parent):
- viewlim = parent.viewLim.frozen()
- mode = self.get_viewlim_mode()
- if mode is None:
- pass
- elif mode == "equal":
- self.viewLim.set(viewlim)
- elif mode == "transform":
- self.viewLim.set(viewlim.transformed(self.transAux.inverted()))
- else:
- _api.check_in_list([None, "equal", "transform"], mode=mode)
-
- # end of aux_transform support
-
-
-parasite_axes_class_factory = cbook._make_class_factory(
- ParasiteAxesBase, "{}Parasite")
-ParasiteAxes = parasite_axes_class_factory(Axes)
-
-
-class HostAxesBase:
- def __init__(self, *args, **kwargs):
- self.parasites = []
- super().__init__(*args, **kwargs)
-
- def get_aux_axes(
- self, tr=None, viewlim_mode="equal", axes_class=None, **kwargs):
- """
- Add a parasite axes to this host.
-
- Despite this method's name, this should actually be thought of as an
- ``add_parasite_axes`` method.
-
- .. versionchanged:: 3.7
- Defaults to same base axes class as host axes.
-
- Parameters
- ----------
- tr : `~matplotlib.transforms.Transform` or None, default: None
- If a `.Transform`, the following relation will hold:
- ``parasite.transData = tr + host.transData``.
- If None, the parasite's and the host's ``transData`` are unrelated.
- viewlim_mode : {"equal", "transform", None}, default: "equal"
- How the parasite's view limits are set: directly equal to the
- parent axes ("equal"), equal after application of *tr*
- ("transform"), or independently (None).
- axes_class : subclass type of `~matplotlib.axes.Axes`, optional
- The `~.axes.Axes` subclass that is instantiated. If None, the base
- class of the host axes is used.
- **kwargs
- Other parameters are forwarded to the parasite axes constructor.
- """
- if axes_class is None:
- axes_class = self._base_axes_class
- parasite_axes_class = parasite_axes_class_factory(axes_class)
- ax2 = parasite_axes_class(
- self, tr, viewlim_mode=viewlim_mode, **kwargs)
- # note that ax2.transData == tr + ax1.transData
- # Anything you draw in ax2 will match the ticks and grids of ax1.
- self.parasites.append(ax2)
- ax2._remove_method = self.parasites.remove
- return ax2
-
- def draw(self, renderer):
- orig_children_len = len(self._children)
-
- locator = self.get_axes_locator()
- if locator:
- pos = locator(self, renderer)
- self.set_position(pos, which="active")
- self.apply_aspect(pos)
- else:
- self.apply_aspect()
-
- rect = self.get_position()
- for ax in self.parasites:
- ax.apply_aspect(rect)
- self._children.extend(ax.get_children())
-
- super().draw(renderer)
- del self._children[orig_children_len:]
-
- def clear(self):
- super().clear()
- for ax in self.parasites:
- ax.clear()
-
- def pick(self, mouseevent):
- super().pick(mouseevent)
- # Also pass pick events on to parasite axes and, in turn, their
- # children (cf. ParasiteAxesBase.pick)
- for a in self.parasites:
- a.pick(mouseevent)
-
- def twinx(self, axes_class=None):
- """
- Create a twin of Axes with a shared x-axis but independent y-axis.
-
- The y-axis of self will have ticks on the left and the returned axes
- will have ticks on the right.
- """
- ax = self._add_twin_axes(axes_class, sharex=self)
- self.axis["right"].set_visible(False)
- ax.axis["right"].set_visible(True)
- ax.axis["left", "top", "bottom"].set_visible(False)
- return ax
-
- def twiny(self, axes_class=None):
- """
- Create a twin of Axes with a shared y-axis but independent x-axis.
-
- The x-axis of self will have ticks on the bottom and the returned axes
- will have ticks on the top.
- """
- ax = self._add_twin_axes(axes_class, sharey=self)
- self.axis["top"].set_visible(False)
- ax.axis["top"].set_visible(True)
- ax.axis["left", "right", "bottom"].set_visible(False)
- return ax
-
- def twin(self, aux_trans=None, axes_class=None):
- """
- Create a twin of Axes with no shared axis.
-
- While self will have ticks on the left and bottom axis, the returned
- axes will have ticks on the top and right axis.
- """
- if aux_trans is None:
- aux_trans = mtransforms.IdentityTransform()
- ax = self._add_twin_axes(
- axes_class, aux_transform=aux_trans, viewlim_mode="transform")
- self.axis["top", "right"].set_visible(False)
- ax.axis["top", "right"].set_visible(True)
- ax.axis["left", "bottom"].set_visible(False)
- return ax
-
- def _add_twin_axes(self, axes_class, **kwargs):
- """
- Helper for `.twinx`/`.twiny`/`.twin`.
-
- *kwargs* are forwarded to the parasite axes constructor.
- """
- if axes_class is None:
- axes_class = self._base_axes_class
- ax = parasite_axes_class_factory(axes_class)(self, **kwargs)
- self.parasites.append(ax)
- ax._remove_method = self._remove_any_twin
- return ax
-
- def _remove_any_twin(self, ax):
- self.parasites.remove(ax)
- restore = ["top", "right"]
- if ax._sharex:
- restore.remove("top")
- if ax._sharey:
- restore.remove("right")
- self.axis[tuple(restore)].set_visible(True)
- self.axis[tuple(restore)].toggle(ticklabels=False, label=False)
-
- @_api.make_keyword_only("3.8", "call_axes_locator")
- def get_tightbbox(self, renderer=None, call_axes_locator=True,
- bbox_extra_artists=None):
- bbs = [
- *[ax.get_tightbbox(renderer, call_axes_locator=call_axes_locator)
- for ax in self.parasites],
- super().get_tightbbox(renderer,
- call_axes_locator=call_axes_locator,
- bbox_extra_artists=bbox_extra_artists)]
- return Bbox.union([b for b in bbs if b.width != 0 or b.height != 0])
-
-
-host_axes_class_factory = host_subplot_class_factory = \
- cbook._make_class_factory(HostAxesBase, "{}HostAxes", "_base_axes_class")
-HostAxes = SubplotHost = host_axes_class_factory(Axes)
-
-
-def host_axes(*args, axes_class=Axes, figure=None, **kwargs):
- """
- Create axes that can act as a hosts to parasitic axes.
-
- Parameters
- ----------
- figure : `~matplotlib.figure.Figure`
- Figure to which the axes will be added. Defaults to the current figure
- `.pyplot.gcf()`.
-
- *args, **kwargs
- Will be passed on to the underlying `~.axes.Axes` object creation.
- """
- import matplotlib.pyplot as plt
- host_axes_class = host_axes_class_factory(axes_class)
- if figure is None:
- figure = plt.gcf()
- ax = host_axes_class(figure, *args, **kwargs)
- figure.add_axes(ax)
- return ax
-
-
-host_subplot = host_axes
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/__init__.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/__init__.py
deleted file mode 100644
index 47242cf7f0c..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from .axislines import (
- Axes, AxesZero, AxisArtistHelper, AxisArtistHelperRectlinear,
- GridHelperBase, GridHelperRectlinear, Subplot, SubplotZero)
-from .axis_artist import AxisArtist, GridlinesCollection
-from .grid_helper_curvelinear import GridHelperCurveLinear
-from .floating_axes import FloatingAxes, FloatingSubplot
-from mpl_toolkits.axes_grid1.parasite_axes import (
- host_axes_class_factory, parasite_axes_class_factory)
-
-
-ParasiteAxes = parasite_axes_class_factory(Axes)
-HostAxes = host_axes_class_factory(Axes)
-SubplotHost = HostAxes
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/angle_helper.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/angle_helper.py
deleted file mode 100644
index 1786cd70bcd..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/angle_helper.py
+++ /dev/null
@@ -1,394 +0,0 @@
-import numpy as np
-import math
-
-from mpl_toolkits.axisartist.grid_finder import ExtremeFinderSimple
-
-
-def select_step_degree(dv):
-
- degree_limits_ = [1.5, 3, 7, 13, 20, 40, 70, 120, 270, 520]
- degree_steps_ = [1, 2, 5, 10, 15, 30, 45, 90, 180, 360]
- degree_factors = [1.] * len(degree_steps_)
-
- minsec_limits_ = [1.5, 2.5, 3.5, 8, 11, 18, 25, 45]
- minsec_steps_ = [1, 2, 3, 5, 10, 15, 20, 30]
-
- minute_limits_ = np.array(minsec_limits_) / 60
- minute_factors = [60.] * len(minute_limits_)
-
- second_limits_ = np.array(minsec_limits_) / 3600
- second_factors = [3600.] * len(second_limits_)
-
- degree_limits = [*second_limits_, *minute_limits_, *degree_limits_]
- degree_steps = [*minsec_steps_, *minsec_steps_, *degree_steps_]
- degree_factors = [*second_factors, *minute_factors, *degree_factors]
-
- n = np.searchsorted(degree_limits, dv)
- step = degree_steps[n]
- factor = degree_factors[n]
-
- return step, factor
-
-
-def select_step_hour(dv):
-
- hour_limits_ = [1.5, 2.5, 3.5, 5, 7, 10, 15, 21, 36]
- hour_steps_ = [1, 2, 3, 4, 6, 8, 12, 18, 24]
- hour_factors = [1.] * len(hour_steps_)
-
- minsec_limits_ = [1.5, 2.5, 3.5, 4.5, 5.5, 8, 11, 14, 18, 25, 45]
- minsec_steps_ = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30]
-
- minute_limits_ = np.array(minsec_limits_) / 60
- minute_factors = [60.] * len(minute_limits_)
-
- second_limits_ = np.array(minsec_limits_) / 3600
- second_factors = [3600.] * len(second_limits_)
-
- hour_limits = [*second_limits_, *minute_limits_, *hour_limits_]
- hour_steps = [*minsec_steps_, *minsec_steps_, *hour_steps_]
- hour_factors = [*second_factors, *minute_factors, *hour_factors]
-
- n = np.searchsorted(hour_limits, dv)
- step = hour_steps[n]
- factor = hour_factors[n]
-
- return step, factor
-
-
-def select_step_sub(dv):
-
- # subarcsec or degree
- tmp = 10.**(int(math.log10(dv))-1.)
-
- factor = 1./tmp
-
- if 1.5*tmp >= dv:
- step = 1
- elif 3.*tmp >= dv:
- step = 2
- elif 7.*tmp >= dv:
- step = 5
- else:
- step = 1
- factor = 0.1*factor
-
- return step, factor
-
-
-def select_step(v1, v2, nv, hour=False, include_last=True,
- threshold_factor=3600.):
-
- if v1 > v2:
- v1, v2 = v2, v1
-
- dv = (v2 - v1) / nv
-
- if hour:
- _select_step = select_step_hour
- cycle = 24.
- else:
- _select_step = select_step_degree
- cycle = 360.
-
- # for degree
- if dv > 1 / threshold_factor:
- step, factor = _select_step(dv)
- else:
- step, factor = select_step_sub(dv*threshold_factor)
-
- factor = factor * threshold_factor
-
- levs = np.arange(np.floor(v1 * factor / step),
- np.ceil(v2 * factor / step) + 0.5,
- dtype=int) * step
-
- # n : number of valid levels. If there is a cycle, e.g., [0, 90, 180,
- # 270, 360], the grid line needs to be extended from 0 to 360, so
- # we need to return the whole array. However, the last level (360)
- # needs to be ignored often. In this case, so we return n=4.
-
- n = len(levs)
-
- # we need to check the range of values
- # for example, -90 to 90, 0 to 360,
-
- if factor == 1. and levs[-1] >= levs[0] + cycle: # check for cycle
- nv = int(cycle / step)
- if include_last:
- levs = levs[0] + np.arange(0, nv+1, 1) * step
- else:
- levs = levs[0] + np.arange(0, nv, 1) * step
-
- n = len(levs)
-
- return np.array(levs), n, factor
-
-
-def select_step24(v1, v2, nv, include_last=True, threshold_factor=3600):
- v1, v2 = v1 / 15, v2 / 15
- levs, n, factor = select_step(v1, v2, nv, hour=True,
- include_last=include_last,
- threshold_factor=threshold_factor)
- return levs * 15, n, factor
-
-
-def select_step360(v1, v2, nv, include_last=True, threshold_factor=3600):
- return select_step(v1, v2, nv, hour=False,
- include_last=include_last,
- threshold_factor=threshold_factor)
-
-
-class LocatorBase:
- def __init__(self, nbins, include_last=True):
- self.nbins = nbins
- self._include_last = include_last
-
- def set_params(self, nbins=None):
- if nbins is not None:
- self.nbins = int(nbins)
-
-
-class LocatorHMS(LocatorBase):
- def __call__(self, v1, v2):
- return select_step24(v1, v2, self.nbins, self._include_last)
-
-
-class LocatorHM(LocatorBase):
- def __call__(self, v1, v2):
- return select_step24(v1, v2, self.nbins, self._include_last,
- threshold_factor=60)
-
-
-class LocatorH(LocatorBase):
- def __call__(self, v1, v2):
- return select_step24(v1, v2, self.nbins, self._include_last,
- threshold_factor=1)
-
-
-class LocatorDMS(LocatorBase):
- def __call__(self, v1, v2):
- return select_step360(v1, v2, self.nbins, self._include_last)
-
-
-class LocatorDM(LocatorBase):
- def __call__(self, v1, v2):
- return select_step360(v1, v2, self.nbins, self._include_last,
- threshold_factor=60)
-
-
-class LocatorD(LocatorBase):
- def __call__(self, v1, v2):
- return select_step360(v1, v2, self.nbins, self._include_last,
- threshold_factor=1)
-
-
-class FormatterDMS:
- deg_mark = r"^{\circ}"
- min_mark = r"^{\prime}"
- sec_mark = r"^{\prime\prime}"
-
- fmt_d = "$%d" + deg_mark + "$"
- fmt_ds = r"$%d.%s" + deg_mark + "$"
-
- # %s for sign
- fmt_d_m = r"$%s%d" + deg_mark + r"\,%02d" + min_mark + "$"
- fmt_d_ms = r"$%s%d" + deg_mark + r"\,%02d.%s" + min_mark + "$"
-
- fmt_d_m_partial = "$%s%d" + deg_mark + r"\,%02d" + min_mark + r"\,"
- fmt_s_partial = "%02d" + sec_mark + "$"
- fmt_ss_partial = "%02d.%s" + sec_mark + "$"
-
- def _get_number_fraction(self, factor):
- ## check for fractional numbers
- number_fraction = None
- # check for 60
-
- for threshold in [1, 60, 3600]:
- if factor <= threshold:
- break
-
- d = factor // threshold
- int_log_d = int(np.floor(np.log10(d)))
- if 10**int_log_d == d and d != 1:
- number_fraction = int_log_d
- factor = factor // 10**int_log_d
- return factor, number_fraction
-
- return factor, number_fraction
-
- def __call__(self, direction, factor, values):
- if len(values) == 0:
- return []
-
- ss = np.sign(values)
- signs = ["-" if v < 0 else "" for v in values]
-
- factor, number_fraction = self._get_number_fraction(factor)
-
- values = np.abs(values)
-
- if number_fraction is not None:
- values, frac_part = divmod(values, 10 ** number_fraction)
- frac_fmt = "%%0%dd" % (number_fraction,)
- frac_str = [frac_fmt % (f1,) for f1 in frac_part]
-
- if factor == 1:
- if number_fraction is None:
- return [self.fmt_d % (s * int(v),) for s, v in zip(ss, values)]
- else:
- return [self.fmt_ds % (s * int(v), f1)
- for s, v, f1 in zip(ss, values, frac_str)]
- elif factor == 60:
- deg_part, min_part = divmod(values, 60)
- if number_fraction is None:
- return [self.fmt_d_m % (s1, d1, m1)
- for s1, d1, m1 in zip(signs, deg_part, min_part)]
- else:
- return [self.fmt_d_ms % (s, d1, m1, f1)
- for s, d1, m1, f1
- in zip(signs, deg_part, min_part, frac_str)]
-
- elif factor == 3600:
- if ss[-1] == -1:
- inverse_order = True
- values = values[::-1]
- signs = signs[::-1]
- else:
- inverse_order = False
-
- l_hm_old = ""
- r = []
-
- deg_part, min_part_ = divmod(values, 3600)
- min_part, sec_part = divmod(min_part_, 60)
-
- if number_fraction is None:
- sec_str = [self.fmt_s_partial % (s1,) for s1 in sec_part]
- else:
- sec_str = [self.fmt_ss_partial % (s1, f1)
- for s1, f1 in zip(sec_part, frac_str)]
-
- for s, d1, m1, s1 in zip(signs, deg_part, min_part, sec_str):
- l_hm = self.fmt_d_m_partial % (s, d1, m1)
- if l_hm != l_hm_old:
- l_hm_old = l_hm
- l = l_hm + s1
- else:
- l = "$" + s + s1
- r.append(l)
-
- if inverse_order:
- return r[::-1]
- else:
- return r
-
- else: # factor > 3600.
- return [r"$%s^{\circ}$" % v for v in ss*values]
-
-
-class FormatterHMS(FormatterDMS):
- deg_mark = r"^\mathrm{h}"
- min_mark = r"^\mathrm{m}"
- sec_mark = r"^\mathrm{s}"
-
- fmt_d = "$%d" + deg_mark + "$"
- fmt_ds = r"$%d.%s" + deg_mark + "$"
-
- # %s for sign
- fmt_d_m = r"$%s%d" + deg_mark + r"\,%02d" + min_mark+"$"
- fmt_d_ms = r"$%s%d" + deg_mark + r"\,%02d.%s" + min_mark+"$"
-
- fmt_d_m_partial = "$%s%d" + deg_mark + r"\,%02d" + min_mark + r"\,"
- fmt_s_partial = "%02d" + sec_mark + "$"
- fmt_ss_partial = "%02d.%s" + sec_mark + "$"
-
- def __call__(self, direction, factor, values): # hour
- return super().__call__(direction, factor, np.asarray(values) / 15)
-
-
-class ExtremeFinderCycle(ExtremeFinderSimple):
- # docstring inherited
-
- def __init__(self, nx, ny,
- lon_cycle=360., lat_cycle=None,
- lon_minmax=None, lat_minmax=(-90, 90)):
- """
- This subclass handles the case where one or both coordinates should be
- taken modulo 360, or be restricted to not exceed a specific range.
-
- Parameters
- ----------
- nx, ny : int
- The number of samples in each direction.
-
- lon_cycle, lat_cycle : 360 or None
- If not None, values in the corresponding direction are taken modulo
- *lon_cycle* or *lat_cycle*; in theory this can be any number but
- the implementation actually assumes that it is 360 (if not None);
- other values give nonsensical results.
-
- This is done by "unwrapping" the transformed grid coordinates so
- that jumps are less than a half-cycle; then normalizing the span to
- no more than a full cycle.
-
- For example, if values are in the union of the [0, 2] and
- [358, 360] intervals (typically, angles measured modulo 360), the
- values in the second interval are normalized to [-2, 0] instead so
- that the values now cover [-2, 2]. If values are in a range of
- [5, 1000], this gets normalized to [5, 365].
-
- lon_minmax, lat_minmax : (float, float) or None
- If not None, the computed bounding box is clipped to the given
- range in the corresponding direction.
- """
- self.nx, self.ny = nx, ny
- self.lon_cycle, self.lat_cycle = lon_cycle, lat_cycle
- self.lon_minmax = lon_minmax
- self.lat_minmax = lat_minmax
-
- def __call__(self, transform_xy, x1, y1, x2, y2):
- # docstring inherited
- x, y = np.meshgrid(
- np.linspace(x1, x2, self.nx), np.linspace(y1, y2, self.ny))
- lon, lat = transform_xy(np.ravel(x), np.ravel(y))
-
- # iron out jumps, but algorithm should be improved.
- # This is just naive way of doing and my fail for some cases.
- # Consider replacing this with numpy.unwrap
- # We are ignoring invalid warnings. They are triggered when
- # comparing arrays with NaNs using > We are already handling
- # that correctly using np.nanmin and np.nanmax
- with np.errstate(invalid='ignore'):
- if self.lon_cycle is not None:
- lon0 = np.nanmin(lon)
- lon -= 360. * ((lon - lon0) > 180.)
- if self.lat_cycle is not None:
- lat0 = np.nanmin(lat)
- lat -= 360. * ((lat - lat0) > 180.)
-
- lon_min, lon_max = np.nanmin(lon), np.nanmax(lon)
- lat_min, lat_max = np.nanmin(lat), np.nanmax(lat)
-
- lon_min, lon_max, lat_min, lat_max = \
- self._add_pad(lon_min, lon_max, lat_min, lat_max)
-
- # check cycle
- if self.lon_cycle:
- lon_max = min(lon_max, lon_min + self.lon_cycle)
- if self.lat_cycle:
- lat_max = min(lat_max, lat_min + self.lat_cycle)
-
- if self.lon_minmax is not None:
- min0 = self.lon_minmax[0]
- lon_min = max(min0, lon_min)
- max0 = self.lon_minmax[1]
- lon_max = min(max0, lon_max)
-
- if self.lat_minmax is not None:
- min0 = self.lat_minmax[0]
- lat_min = max(min0, lat_min)
- max0 = self.lat_minmax[1]
- lat_max = min(max0, lat_max)
-
- return lon_min, lon_max, lat_min, lat_max
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_divider.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_divider.py
deleted file mode 100644
index a01d4e27df9..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_divider.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from mpl_toolkits.axes_grid1.axes_divider import ( # noqa
- Divider, AxesLocator, SubplotDivider, AxesDivider, make_axes_locatable)
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_grid.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_grid.py
deleted file mode 100644
index ecb3e9d92c1..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_grid.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from matplotlib import _api
-
-import mpl_toolkits.axes_grid1.axes_grid as axes_grid_orig
-from .axislines import Axes
-
-
-_api.warn_deprecated(
- "3.8", name=__name__, obj_type="module", alternative="axes_grid1.axes_grid")
-
-
-@_api.deprecated("3.8", alternative=(
- "axes_grid1.axes_grid.Grid(..., axes_class=axislines.Axes"))
-class Grid(axes_grid_orig.Grid):
- _defaultAxesClass = Axes
-
-
-@_api.deprecated("3.8", alternative=(
- "axes_grid1.axes_grid.ImageGrid(..., axes_class=axislines.Axes"))
-class ImageGrid(axes_grid_orig.ImageGrid):
- _defaultAxesClass = Axes
-
-
-AxesGrid = ImageGrid
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_rgb.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_rgb.py
deleted file mode 100644
index 2195747469a..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axes_rgb.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from matplotlib import _api
-from mpl_toolkits.axes_grid1.axes_rgb import ( # noqa
- make_rgb_axes, RGBAxes as _RGBAxes)
-from .axislines import Axes
-
-
-_api.warn_deprecated(
- "3.8", name=__name__, obj_type="module", alternative="axes_grid1.axes_rgb")
-
-
-@_api.deprecated("3.8", alternative=(
- "axes_grid1.axes_rgb.RGBAxes(..., axes_class=axislines.Axes"))
-class RGBAxes(_RGBAxes):
- """
- Subclass of `~.axes_grid1.axes_rgb.RGBAxes` with
- ``_defaultAxesClass`` = `.axislines.Axes`.
- """
- _defaultAxesClass = Axes
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axis_artist.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axis_artist.py
deleted file mode 100644
index 407ad07a3dc..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axis_artist.py
+++ /dev/null
@@ -1,1115 +0,0 @@
-"""
-The :mod:`.axis_artist` module implements custom artists to draw axis elements
-(axis lines and labels, tick lines and labels, grid lines).
-
-Axis lines and labels and tick lines and labels are managed by the `AxisArtist`
-class; grid lines are managed by the `GridlinesCollection` class.
-
-There is one `AxisArtist` per Axis; it can be accessed through
-the ``axis`` dictionary of the parent Axes (which should be a
-`mpl_toolkits.axislines.Axes`), e.g. ``ax.axis["bottom"]``.
-
-Children of the AxisArtist are accessed as attributes: ``.line`` and ``.label``
-for the axis line and label, ``.major_ticks``, ``.major_ticklabels``,
-``.minor_ticks``, ``.minor_ticklabels`` for the tick lines and labels (e.g.
-``ax.axis["bottom"].line``).
-
-Children properties (colors, fonts, line widths, etc.) can be set using
-setters, e.g. ::
-
- # Make the major ticks of the bottom axis red.
- ax.axis["bottom"].major_ticks.set_color("red")
-
-However, things like the locations of ticks, and their ticklabels need to be
-changed from the side of the grid_helper.
-
-axis_direction
---------------
-
-`AxisArtist`, `AxisLabel`, `TickLabels` have an *axis_direction* attribute,
-which adjusts the location, angle, etc. The *axis_direction* must be one of
-"left", "right", "bottom", "top", and follows the Matplotlib convention for
-rectangular axis.
-
-For example, for the *bottom* axis (the left and right is relative to the
-direction of the increasing coordinate),
-
-* ticklabels and axislabel are on the right
-* ticklabels and axislabel have text angle of 0
-* ticklabels are baseline, center-aligned
-* axislabel is top, center-aligned
-
-The text angles are actually relative to (90 + angle of the direction to the
-ticklabel), which gives 0 for bottom axis.
-
-=================== ====== ======== ====== ========
-Property left bottom right top
-=================== ====== ======== ====== ========
-ticklabel location left right right left
-axislabel location left right right left
-ticklabel angle 90 0 -90 180
-axislabel angle 180 0 0 180
-ticklabel va center baseline center baseline
-axislabel va center top center bottom
-ticklabel ha right center right center
-axislabel ha right center right center
-=================== ====== ======== ====== ========
-
-Ticks are by default direct opposite side of the ticklabels. To make ticks to
-the same side of the ticklabels, ::
-
- ax.axis["bottom"].major_ticks.set_tick_out(True)
-
-The following attributes can be customized (use the ``set_xxx`` methods):
-
-* `Ticks`: ticksize, tick_out
-* `TickLabels`: pad
-* `AxisLabel`: pad
-"""
-
-# FIXME :
-# angles are given in data coordinate - need to convert it to canvas coordinate
-
-
-from operator import methodcaller
-
-import numpy as np
-
-import matplotlib as mpl
-from matplotlib import _api, cbook
-import matplotlib.artist as martist
-import matplotlib.colors as mcolors
-import matplotlib.text as mtext
-from matplotlib.collections import LineCollection
-from matplotlib.lines import Line2D
-from matplotlib.patches import PathPatch
-from matplotlib.path import Path
-from matplotlib.transforms import (
- Affine2D, Bbox, IdentityTransform, ScaledTranslation)
-
-from .axisline_style import AxislineStyle
-
-
-class AttributeCopier:
- def get_ref_artist(self):
- """
- Return the underlying artist that actually defines some properties
- (e.g., color) of this artist.
- """
- raise RuntimeError("get_ref_artist must overridden")
-
- def get_attribute_from_ref_artist(self, attr_name):
- getter = methodcaller("get_" + attr_name)
- prop = getter(super())
- return getter(self.get_ref_artist()) if prop == "auto" else prop
-
-
-class Ticks(AttributeCopier, Line2D):
- """
- Ticks are derived from `.Line2D`, and note that ticks themselves
- are markers. Thus, you should use set_mec, set_mew, etc.
-
- To change the tick size (length), you need to use
- `set_ticksize`. To change the direction of the ticks (ticks are
- in opposite direction of ticklabels by default), use
- ``set_tick_out(False)``
- """
-
- def __init__(self, ticksize, tick_out=False, *, axis=None, **kwargs):
- self._ticksize = ticksize
- self.locs_angles_labels = []
-
- self.set_tick_out(tick_out)
-
- self._axis = axis
- if self._axis is not None:
- if "color" not in kwargs:
- kwargs["color"] = "auto"
- if "mew" not in kwargs and "markeredgewidth" not in kwargs:
- kwargs["markeredgewidth"] = "auto"
-
- Line2D.__init__(self, [0.], [0.], **kwargs)
- self.set_snap(True)
-
- def get_ref_artist(self):
- # docstring inherited
- return self._axis.majorTicks[0].tick1line
-
- def set_color(self, color):
- # docstring inherited
- # Unlike the base Line2D.set_color, this also supports "auto".
- if not cbook._str_equal(color, "auto"):
- mcolors._check_color_like(color=color)
- self._color = color
- self.stale = True
-
- def get_color(self):
- return self.get_attribute_from_ref_artist("color")
-
- def get_markeredgecolor(self):
- return self.get_attribute_from_ref_artist("markeredgecolor")
-
- def get_markeredgewidth(self):
- return self.get_attribute_from_ref_artist("markeredgewidth")
-
- def set_tick_out(self, b):
- """Set whether ticks are drawn inside or outside the axes."""
- self._tick_out = b
-
- def get_tick_out(self):
- """Return whether ticks are drawn inside or outside the axes."""
- return self._tick_out
-
- def set_ticksize(self, ticksize):
- """Set length of the ticks in points."""
- self._ticksize = ticksize
-
- def get_ticksize(self):
- """Return length of the ticks in points."""
- return self._ticksize
-
- def set_locs_angles(self, locs_angles):
- self.locs_angles = locs_angles
-
- _tickvert_path = Path([[0., 0.], [1., 0.]])
-
- def draw(self, renderer):
- if not self.get_visible():
- return
-
- gc = renderer.new_gc()
- gc.set_foreground(self.get_markeredgecolor())
- gc.set_linewidth(self.get_markeredgewidth())
- gc.set_alpha(self._alpha)
-
- path_trans = self.get_transform()
- marker_transform = (Affine2D()
- .scale(renderer.points_to_pixels(self._ticksize)))
- if self.get_tick_out():
- marker_transform.rotate_deg(180)
-
- for loc, angle in self.locs_angles:
- locs = path_trans.transform_non_affine(np.array([loc]))
- if self.axes and not self.axes.viewLim.contains(*locs[0]):
- continue
- renderer.draw_markers(
- gc, self._tickvert_path,
- marker_transform + Affine2D().rotate_deg(angle),
- Path(locs), path_trans.get_affine())
-
- gc.restore()
-
-
-class LabelBase(mtext.Text):
- """
- A base class for `.AxisLabel` and `.TickLabels`. The position and
- angle of the text are calculated by the offset_ref_angle,
- text_ref_angle, and offset_radius attributes.
- """
-
- def __init__(self, *args, **kwargs):
- self.locs_angles_labels = []
- self._ref_angle = 0
- self._offset_radius = 0.
-
- super().__init__(*args, **kwargs)
-
- self.set_rotation_mode("anchor")
- self._text_follow_ref_angle = True
-
- @property
- def _text_ref_angle(self):
- if self._text_follow_ref_angle:
- return self._ref_angle + 90
- else:
- return 0
-
- @property
- def _offset_ref_angle(self):
- return self._ref_angle
-
- _get_opposite_direction = {"left": "right",
- "right": "left",
- "top": "bottom",
- "bottom": "top"}.__getitem__
-
- def draw(self, renderer):
- if not self.get_visible():
- return
-
- # save original and adjust some properties
- tr = self.get_transform()
- angle_orig = self.get_rotation()
- theta = np.deg2rad(self._offset_ref_angle)
- dd = self._offset_radius
- dx, dy = dd * np.cos(theta), dd * np.sin(theta)
-
- self.set_transform(tr + Affine2D().translate(dx, dy))
- self.set_rotation(self._text_ref_angle + angle_orig)
- super().draw(renderer)
- # restore original properties
- self.set_transform(tr)
- self.set_rotation(angle_orig)
-
- def get_window_extent(self, renderer=None):
- if renderer is None:
- renderer = self.figure._get_renderer()
-
- # save original and adjust some properties
- tr = self.get_transform()
- angle_orig = self.get_rotation()
- theta = np.deg2rad(self._offset_ref_angle)
- dd = self._offset_radius
- dx, dy = dd * np.cos(theta), dd * np.sin(theta)
-
- self.set_transform(tr + Affine2D().translate(dx, dy))
- self.set_rotation(self._text_ref_angle + angle_orig)
- bbox = super().get_window_extent(renderer).frozen()
- # restore original properties
- self.set_transform(tr)
- self.set_rotation(angle_orig)
-
- return bbox
-
-
-class AxisLabel(AttributeCopier, LabelBase):
- """
- Axis label. Derived from `.Text`. The position of the text is updated
- in the fly, so changing text position has no effect. Otherwise, the
- properties can be changed as a normal `.Text`.
-
- To change the pad between tick labels and axis label, use `set_pad`.
- """
-
- def __init__(self, *args, axis_direction="bottom", axis=None, **kwargs):
- self._axis = axis
- self._pad = 5
- self._external_pad = 0 # in pixels
- LabelBase.__init__(self, *args, **kwargs)
- self.set_axis_direction(axis_direction)
-
- def set_pad(self, pad):
- """
- Set the internal pad in points.
-
- The actual pad will be the sum of the internal pad and the
- external pad (the latter is set automatically by the `.AxisArtist`).
-
- Parameters
- ----------
- pad : float
- The internal pad in points.
- """
- self._pad = pad
-
- def get_pad(self):
- """
- Return the internal pad in points.
-
- See `.set_pad` for more details.
- """
- return self._pad
-
- def get_ref_artist(self):
- # docstring inherited
- return self._axis.get_label()
-
- def get_text(self):
- # docstring inherited
- t = super().get_text()
- if t == "__from_axes__":
- return self._axis.get_label().get_text()
- return self._text
-
- _default_alignments = dict(left=("bottom", "center"),
- right=("top", "center"),
- bottom=("top", "center"),
- top=("bottom", "center"))
-
- def set_default_alignment(self, d):
- """
- Set the default alignment. See `set_axis_direction` for details.
-
- Parameters
- ----------
- d : {"left", "bottom", "right", "top"}
- """
- va, ha = _api.check_getitem(self._default_alignments, d=d)
- self.set_va(va)
- self.set_ha(ha)
-
- _default_angles = dict(left=180,
- right=0,
- bottom=0,
- top=180)
-
- def set_default_angle(self, d):
- """
- Set the default angle. See `set_axis_direction` for details.
-
- Parameters
- ----------
- d : {"left", "bottom", "right", "top"}
- """
- self.set_rotation(_api.check_getitem(self._default_angles, d=d))
-
- def set_axis_direction(self, d):
- """
- Adjust the text angle and text alignment of axis label
- according to the matplotlib convention.
-
- ===================== ========== ========= ========== ==========
- Property left bottom right top
- ===================== ========== ========= ========== ==========
- axislabel angle 180 0 0 180
- axislabel va center top center bottom
- axislabel ha right center right center
- ===================== ========== ========= ========== ==========
-
- Note that the text angles are actually relative to (90 + angle
- of the direction to the ticklabel), which gives 0 for bottom
- axis.
-
- Parameters
- ----------
- d : {"left", "bottom", "right", "top"}
- """
- self.set_default_alignment(d)
- self.set_default_angle(d)
-
- def get_color(self):
- return self.get_attribute_from_ref_artist("color")
-
- def draw(self, renderer):
- if not self.get_visible():
- return
-
- self._offset_radius = \
- self._external_pad + renderer.points_to_pixels(self.get_pad())
-
- super().draw(renderer)
-
- def get_window_extent(self, renderer=None):
- if renderer is None:
- renderer = self.figure._get_renderer()
- if not self.get_visible():
- return
-
- r = self._external_pad + renderer.points_to_pixels(self.get_pad())
- self._offset_radius = r
-
- bb = super().get_window_extent(renderer)
-
- return bb
-
-
-class TickLabels(AxisLabel): # mtext.Text
- """
- Tick labels. While derived from `.Text`, this single artist draws all
- ticklabels. As in `.AxisLabel`, the position of the text is updated
- in the fly, so changing text position has no effect. Otherwise,
- the properties can be changed as a normal `.Text`. Unlike the
- ticklabels of the mainline Matplotlib, properties of a single
- ticklabel alone cannot be modified.
-
- To change the pad between ticks and ticklabels, use `~.AxisLabel.set_pad`.
- """
-
- def __init__(self, *, axis_direction="bottom", **kwargs):
- super().__init__(**kwargs)
- self.set_axis_direction(axis_direction)
- self._axislabel_pad = 0
-
- def get_ref_artist(self):
- # docstring inherited
- return self._axis.get_ticklabels()[0]
-
- def set_axis_direction(self, label_direction):
- """
- Adjust the text angle and text alignment of ticklabels
- according to the Matplotlib convention.
-
- The *label_direction* must be one of [left, right, bottom, top].
-
- ===================== ========== ========= ========== ==========
- Property left bottom right top
- ===================== ========== ========= ========== ==========
- ticklabel angle 90 0 -90 180
- ticklabel va center baseline center baseline
- ticklabel ha right center right center
- ===================== ========== ========= ========== ==========
-
- Note that the text angles are actually relative to (90 + angle
- of the direction to the ticklabel), which gives 0 for bottom
- axis.
-
- Parameters
- ----------
- label_direction : {"left", "bottom", "right", "top"}
-
- """
- self.set_default_alignment(label_direction)
- self.set_default_angle(label_direction)
- self._axis_direction = label_direction
-
- def invert_axis_direction(self):
- label_direction = self._get_opposite_direction(self._axis_direction)
- self.set_axis_direction(label_direction)
-
- def _get_ticklabels_offsets(self, renderer, label_direction):
- """
- Calculate the ticklabel offsets from the tick and their total heights.
-
- The offset only takes account the offset due to the vertical alignment
- of the ticklabels: if axis direction is bottom and va is 'top', it will
- return 0; if va is 'baseline', it will return (height-descent).
- """
- whd_list = self.get_texts_widths_heights_descents(renderer)
-
- if not whd_list:
- return 0, 0
-
- r = 0
- va, ha = self.get_va(), self.get_ha()
-
- if label_direction == "left":
- pad = max(w for w, h, d in whd_list)
- if ha == "left":
- r = pad
- elif ha == "center":
- r = .5 * pad
- elif label_direction == "right":
- pad = max(w for w, h, d in whd_list)
- if ha == "right":
- r = pad
- elif ha == "center":
- r = .5 * pad
- elif label_direction == "bottom":
- pad = max(h for w, h, d in whd_list)
- if va == "bottom":
- r = pad
- elif va == "center":
- r = .5 * pad
- elif va == "baseline":
- max_ascent = max(h - d for w, h, d in whd_list)
- max_descent = max(d for w, h, d in whd_list)
- r = max_ascent
- pad = max_ascent + max_descent
- elif label_direction == "top":
- pad = max(h for w, h, d in whd_list)
- if va == "top":
- r = pad
- elif va == "center":
- r = .5 * pad
- elif va == "baseline":
- max_ascent = max(h - d for w, h, d in whd_list)
- max_descent = max(d for w, h, d in whd_list)
- r = max_descent
- pad = max_ascent + max_descent
-
- # r : offset
- # pad : total height of the ticklabels. This will be used to
- # calculate the pad for the axislabel.
- return r, pad
-
- _default_alignments = dict(left=("center", "right"),
- right=("center", "left"),
- bottom=("baseline", "center"),
- top=("baseline", "center"))
-
- _default_angles = dict(left=90,
- right=-90,
- bottom=0,
- top=180)
-
- def draw(self, renderer):
- if not self.get_visible():
- self._axislabel_pad = self._external_pad
- return
-
- r, total_width = self._get_ticklabels_offsets(renderer,
- self._axis_direction)
-
- pad = self._external_pad + renderer.points_to_pixels(self.get_pad())
- self._offset_radius = r + pad
-
- for (x, y), a, l in self._locs_angles_labels:
- if not l.strip():
- continue
- self._ref_angle = a
- self.set_x(x)
- self.set_y(y)
- self.set_text(l)
- LabelBase.draw(self, renderer)
-
- # the value saved will be used to draw axislabel.
- self._axislabel_pad = total_width + pad
-
- def set_locs_angles_labels(self, locs_angles_labels):
- self._locs_angles_labels = locs_angles_labels
-
- def get_window_extents(self, renderer=None):
- if renderer is None:
- renderer = self.figure._get_renderer()
-
- if not self.get_visible():
- self._axislabel_pad = self._external_pad
- return []
-
- bboxes = []
-
- r, total_width = self._get_ticklabels_offsets(renderer,
- self._axis_direction)
-
- pad = self._external_pad + renderer.points_to_pixels(self.get_pad())
- self._offset_radius = r + pad
-
- for (x, y), a, l in self._locs_angles_labels:
- self._ref_angle = a
- self.set_x(x)
- self.set_y(y)
- self.set_text(l)
- bb = LabelBase.get_window_extent(self, renderer)
- bboxes.append(bb)
-
- # the value saved will be used to draw axislabel.
- self._axislabel_pad = total_width + pad
-
- return bboxes
-
- def get_texts_widths_heights_descents(self, renderer):
- """
- Return a list of ``(width, height, descent)`` tuples for ticklabels.
-
- Empty labels are left out.
- """
- whd_list = []
- for _loc, _angle, label in self._locs_angles_labels:
- if not label.strip():
- continue
- clean_line, ismath = self._preprocess_math(label)
- whd = renderer.get_text_width_height_descent(
- clean_line, self._fontproperties, ismath=ismath)
- whd_list.append(whd)
- return whd_list
-
-
-class GridlinesCollection(LineCollection):
- def __init__(self, *args, which="major", axis="both", **kwargs):
- """
- Collection of grid lines.
-
- Parameters
- ----------
- which : {"major", "minor"}
- Which grid to consider.
- axis : {"both", "x", "y"}
- Which axis to consider.
- *args, **kwargs
- Passed to `.LineCollection`.
- """
- self._which = which
- self._axis = axis
- super().__init__(*args, **kwargs)
- self.set_grid_helper(None)
-
- def set_which(self, which):
- """
- Select major or minor grid lines.
-
- Parameters
- ----------
- which : {"major", "minor"}
- """
- self._which = which
-
- def set_axis(self, axis):
- """
- Select axis.
-
- Parameters
- ----------
- axis : {"both", "x", "y"}
- """
- self._axis = axis
-
- def set_grid_helper(self, grid_helper):
- """
- Set grid helper.
-
- Parameters
- ----------
- grid_helper : `.GridHelperBase` subclass
- """
- self._grid_helper = grid_helper
-
- def draw(self, renderer):
- if self._grid_helper is not None:
- self._grid_helper.update_lim(self.axes)
- gl = self._grid_helper.get_gridlines(self._which, self._axis)
- self.set_segments([np.transpose(l) for l in gl])
- super().draw(renderer)
-
-
-class AxisArtist(martist.Artist):
- """
- An artist which draws axis (a line along which the n-th axes coord
- is constant) line, ticks, tick labels, and axis label.
- """
-
- zorder = 2.5
-
- @property
- def LABELPAD(self):
- return self.label.get_pad()
-
- @LABELPAD.setter
- def LABELPAD(self, v):
- self.label.set_pad(v)
-
- def __init__(self, axes,
- helper,
- offset=None,
- axis_direction="bottom",
- **kwargs):
- """
- Parameters
- ----------
- axes : `mpl_toolkits.axisartist.axislines.Axes`
- helper : `~mpl_toolkits.axisartist.axislines.AxisArtistHelper`
- """
- # axes is also used to follow the axis attribute (tick color, etc).
-
- super().__init__(**kwargs)
-
- self.axes = axes
-
- self._axis_artist_helper = helper
-
- if offset is None:
- offset = (0, 0)
- self.offset_transform = ScaledTranslation(
- *offset,
- Affine2D().scale(1 / 72) # points to inches.
- + self.axes.figure.dpi_scale_trans)
-
- if axis_direction in ["left", "right"]:
- self.axis = axes.yaxis
- else:
- self.axis = axes.xaxis
-
- self._axisline_style = None
- self._axis_direction = axis_direction
-
- self._init_line()
- self._init_ticks(**kwargs)
- self._init_offsetText(axis_direction)
- self._init_label()
-
- # axis direction
- self._ticklabel_add_angle = 0.
- self._axislabel_add_angle = 0.
- self.set_axis_direction(axis_direction)
-
- # axis direction
-
- def set_axis_direction(self, axis_direction):
- """
- Adjust the direction, text angle, and text alignment of tick labels
- and axis labels following the Matplotlib convention for the rectangle
- axes.
-
- The *axis_direction* must be one of [left, right, bottom, top].
-
- ===================== ========== ========= ========== ==========
- Property left bottom right top
- ===================== ========== ========= ========== ==========
- ticklabel direction "-" "+" "+" "-"
- axislabel direction "-" "+" "+" "-"
- ticklabel angle 90 0 -90 180
- ticklabel va center baseline center baseline
- ticklabel ha right center right center
- axislabel angle 180 0 0 180
- axislabel va center top center bottom
- axislabel ha right center right center
- ===================== ========== ========= ========== ==========
-
- Note that the direction "+" and "-" are relative to the direction of
- the increasing coordinate. Also, the text angles are actually
- relative to (90 + angle of the direction to the ticklabel),
- which gives 0 for bottom axis.
-
- Parameters
- ----------
- axis_direction : {"left", "bottom", "right", "top"}
- """
- self.major_ticklabels.set_axis_direction(axis_direction)
- self.label.set_axis_direction(axis_direction)
- self._axis_direction = axis_direction
- if axis_direction in ["left", "top"]:
- self.set_ticklabel_direction("-")
- self.set_axislabel_direction("-")
- else:
- self.set_ticklabel_direction("+")
- self.set_axislabel_direction("+")
-
- def set_ticklabel_direction(self, tick_direction):
- r"""
- Adjust the direction of the tick labels.
-
- Note that the *tick_direction*\s '+' and '-' are relative to the
- direction of the increasing coordinate.
-
- Parameters
- ----------
- tick_direction : {"+", "-"}
- """
- self._ticklabel_add_angle = _api.check_getitem(
- {"+": 0, "-": 180}, tick_direction=tick_direction)
-
- def invert_ticklabel_direction(self):
- self._ticklabel_add_angle = (self._ticklabel_add_angle + 180) % 360
- self.major_ticklabels.invert_axis_direction()
- self.minor_ticklabels.invert_axis_direction()
-
- def set_axislabel_direction(self, label_direction):
- r"""
- Adjust the direction of the axis label.
-
- Note that the *label_direction*\s '+' and '-' are relative to the
- direction of the increasing coordinate.
-
- Parameters
- ----------
- label_direction : {"+", "-"}
- """
- self._axislabel_add_angle = _api.check_getitem(
- {"+": 0, "-": 180}, label_direction=label_direction)
-
- def get_transform(self):
- return self.axes.transAxes + self.offset_transform
-
- def get_helper(self):
- """
- Return axis artist helper instance.
- """
- return self._axis_artist_helper
-
- def set_axisline_style(self, axisline_style=None, **kwargs):
- """
- Set the axisline style.
-
- The new style is completely defined by the passed attributes. Existing
- style attributes are forgotten.
-
- Parameters
- ----------
- axisline_style : str or None
- The line style, e.g. '->', optionally followed by a comma-separated
- list of attributes. Alternatively, the attributes can be provided
- as keywords.
-
- If *None* this returns a string containing the available styles.
-
- Examples
- --------
- The following two commands are equal:
-
- >>> set_axisline_style("->,size=1.5")
- >>> set_axisline_style("->", size=1.5)
- """
- if axisline_style is None:
- return AxislineStyle.pprint_styles()
-
- if isinstance(axisline_style, AxislineStyle._Base):
- self._axisline_style = axisline_style
- else:
- self._axisline_style = AxislineStyle(axisline_style, **kwargs)
-
- self._init_line()
-
- def get_axisline_style(self):
- """Return the current axisline style."""
- return self._axisline_style
-
- def _init_line(self):
- """
- Initialize the *line* artist that is responsible to draw the axis line.
- """
- tran = (self._axis_artist_helper.get_line_transform(self.axes)
- + self.offset_transform)
-
- axisline_style = self.get_axisline_style()
- if axisline_style is None:
- self.line = PathPatch(
- self._axis_artist_helper.get_line(self.axes),
- color=mpl.rcParams['axes.edgecolor'],
- fill=False,
- linewidth=mpl.rcParams['axes.linewidth'],
- capstyle=mpl.rcParams['lines.solid_capstyle'],
- joinstyle=mpl.rcParams['lines.solid_joinstyle'],
- transform=tran)
- else:
- self.line = axisline_style(self, transform=tran)
-
- def _draw_line(self, renderer):
- self.line.set_path(self._axis_artist_helper.get_line(self.axes))
- if self.get_axisline_style() is not None:
- self.line.set_line_mutation_scale(self.major_ticklabels.get_size())
- self.line.draw(renderer)
-
- def _init_ticks(self, **kwargs):
- axis_name = self.axis.axis_name
-
- trans = (self._axis_artist_helper.get_tick_transform(self.axes)
- + self.offset_transform)
-
- self.major_ticks = Ticks(
- kwargs.get(
- "major_tick_size",
- mpl.rcParams[f"{axis_name}tick.major.size"]),
- axis=self.axis, transform=trans)
- self.minor_ticks = Ticks(
- kwargs.get(
- "minor_tick_size",
- mpl.rcParams[f"{axis_name}tick.minor.size"]),
- axis=self.axis, transform=trans)
-
- size = mpl.rcParams[f"{axis_name}tick.labelsize"]
- self.major_ticklabels = TickLabels(
- axis=self.axis,
- axis_direction=self._axis_direction,
- figure=self.axes.figure,
- transform=trans,
- fontsize=size,
- pad=kwargs.get(
- "major_tick_pad", mpl.rcParams[f"{axis_name}tick.major.pad"]),
- )
- self.minor_ticklabels = TickLabels(
- axis=self.axis,
- axis_direction=self._axis_direction,
- figure=self.axes.figure,
- transform=trans,
- fontsize=size,
- pad=kwargs.get(
- "minor_tick_pad", mpl.rcParams[f"{axis_name}tick.minor.pad"]),
- )
-
- def _get_tick_info(self, tick_iter):
- """
- Return a pair of:
-
- - list of locs and angles for ticks
- - list of locs, angles and labels for ticklabels.
- """
- ticks_loc_angle = []
- ticklabels_loc_angle_label = []
-
- ticklabel_add_angle = self._ticklabel_add_angle
-
- for loc, angle_normal, angle_tangent, label in tick_iter:
- angle_label = angle_tangent - 90 + ticklabel_add_angle
- angle_tick = (angle_normal
- if 90 <= (angle_label - angle_normal) % 360 <= 270
- else angle_normal + 180)
- ticks_loc_angle.append([loc, angle_tick])
- ticklabels_loc_angle_label.append([loc, angle_label, label])
-
- return ticks_loc_angle, ticklabels_loc_angle_label
-
- def _update_ticks(self, renderer=None):
- # set extra pad for major and minor ticklabels: use ticksize of
- # majorticks even for minor ticks. not clear what is best.
-
- if renderer is None:
- renderer = self.figure._get_renderer()
-
- dpi_cor = renderer.points_to_pixels(1.)
- if self.major_ticks.get_visible() and self.major_ticks.get_tick_out():
- ticklabel_pad = self.major_ticks._ticksize * dpi_cor
- self.major_ticklabels._external_pad = ticklabel_pad
- self.minor_ticklabels._external_pad = ticklabel_pad
- else:
- self.major_ticklabels._external_pad = 0
- self.minor_ticklabels._external_pad = 0
-
- majortick_iter, minortick_iter = \
- self._axis_artist_helper.get_tick_iterators(self.axes)
-
- tick_loc_angle, ticklabel_loc_angle_label = \
- self._get_tick_info(majortick_iter)
- self.major_ticks.set_locs_angles(tick_loc_angle)
- self.major_ticklabels.set_locs_angles_labels(ticklabel_loc_angle_label)
-
- tick_loc_angle, ticklabel_loc_angle_label = \
- self._get_tick_info(minortick_iter)
- self.minor_ticks.set_locs_angles(tick_loc_angle)
- self.minor_ticklabels.set_locs_angles_labels(ticklabel_loc_angle_label)
-
- def _draw_ticks(self, renderer):
- self._update_ticks(renderer)
- self.major_ticks.draw(renderer)
- self.major_ticklabels.draw(renderer)
- self.minor_ticks.draw(renderer)
- self.minor_ticklabels.draw(renderer)
- if (self.major_ticklabels.get_visible()
- or self.minor_ticklabels.get_visible()):
- self._draw_offsetText(renderer)
-
- _offsetText_pos = dict(left=(0, 1, "bottom", "right"),
- right=(1, 1, "bottom", "left"),
- bottom=(1, 0, "top", "right"),
- top=(1, 1, "bottom", "right"))
-
- def _init_offsetText(self, direction):
- x, y, va, ha = self._offsetText_pos[direction]
- self.offsetText = mtext.Annotation(
- "",
- xy=(x, y), xycoords="axes fraction",
- xytext=(0, 0), textcoords="offset points",
- color=mpl.rcParams['xtick.color'],
- horizontalalignment=ha, verticalalignment=va,
- )
- self.offsetText.set_transform(IdentityTransform())
- self.axes._set_artist_props(self.offsetText)
-
- def _update_offsetText(self):
- self.offsetText.set_text(self.axis.major.formatter.get_offset())
- self.offsetText.set_size(self.major_ticklabels.get_size())
- offset = (self.major_ticklabels.get_pad()
- + self.major_ticklabels.get_size()
- + 2)
- self.offsetText.xyann = (0, offset)
-
- def _draw_offsetText(self, renderer):
- self._update_offsetText()
- self.offsetText.draw(renderer)
-
- def _init_label(self, **kwargs):
- tr = (self._axis_artist_helper.get_axislabel_transform(self.axes)
- + self.offset_transform)
- self.label = AxisLabel(
- 0, 0, "__from_axes__",
- color="auto",
- fontsize=kwargs.get("labelsize", mpl.rcParams['axes.labelsize']),
- fontweight=mpl.rcParams['axes.labelweight'],
- axis=self.axis,
- transform=tr,
- axis_direction=self._axis_direction,
- )
- self.label.set_figure(self.axes.figure)
- labelpad = kwargs.get("labelpad", 5)
- self.label.set_pad(labelpad)
-
- def _update_label(self, renderer):
- if not self.label.get_visible():
- return
-
- if self._ticklabel_add_angle != self._axislabel_add_angle:
- if ((self.major_ticks.get_visible()
- and not self.major_ticks.get_tick_out())
- or (self.minor_ticks.get_visible()
- and not self.major_ticks.get_tick_out())):
- axislabel_pad = self.major_ticks._ticksize
- else:
- axislabel_pad = 0
- else:
- axislabel_pad = max(self.major_ticklabels._axislabel_pad,
- self.minor_ticklabels._axislabel_pad)
-
- self.label._external_pad = axislabel_pad
-
- xy, angle_tangent = \
- self._axis_artist_helper.get_axislabel_pos_angle(self.axes)
- if xy is None:
- return
-
- angle_label = angle_tangent - 90
-
- x, y = xy
- self.label._ref_angle = angle_label + self._axislabel_add_angle
- self.label.set(x=x, y=y)
-
- def _draw_label(self, renderer):
- self._update_label(renderer)
- self.label.draw(renderer)
-
- def set_label(self, s):
- # docstring inherited
- self.label.set_text(s)
-
- def get_tightbbox(self, renderer=None):
- if not self.get_visible():
- return
- self._axis_artist_helper.update_lim(self.axes)
- self._update_ticks(renderer)
- self._update_label(renderer)
-
- self.line.set_path(self._axis_artist_helper.get_line(self.axes))
- if self.get_axisline_style() is not None:
- self.line.set_line_mutation_scale(self.major_ticklabels.get_size())
-
- bb = [
- *self.major_ticklabels.get_window_extents(renderer),
- *self.minor_ticklabels.get_window_extents(renderer),
- self.label.get_window_extent(renderer),
- self.offsetText.get_window_extent(renderer),
- self.line.get_window_extent(renderer),
- ]
- bb = [b for b in bb if b and (b.width != 0 or b.height != 0)]
- if bb:
- _bbox = Bbox.union(bb)
- return _bbox
- else:
- return None
-
- @martist.allow_rasterization
- def draw(self, renderer):
- # docstring inherited
- if not self.get_visible():
- return
- renderer.open_group(__name__, gid=self.get_gid())
- self._axis_artist_helper.update_lim(self.axes)
- self._draw_ticks(renderer)
- self._draw_line(renderer)
- self._draw_label(renderer)
- renderer.close_group(__name__)
-
- def toggle(self, all=None, ticks=None, ticklabels=None, label=None):
- """
- Toggle visibility of ticks, ticklabels, and (axis) label.
- To turn all off, ::
-
- axis.toggle(all=False)
-
- To turn all off but ticks on ::
-
- axis.toggle(all=False, ticks=True)
-
- To turn all on but (axis) label off ::
-
- axis.toggle(all=True, label=False)
-
- """
- if all:
- _ticks, _ticklabels, _label = True, True, True
- elif all is not None:
- _ticks, _ticklabels, _label = False, False, False
- else:
- _ticks, _ticklabels, _label = None, None, None
-
- if ticks is not None:
- _ticks = ticks
- if ticklabels is not None:
- _ticklabels = ticklabels
- if label is not None:
- _label = label
-
- if _ticks is not None:
- self.major_ticks.set_visible(_ticks)
- self.minor_ticks.set_visible(_ticks)
- if _ticklabels is not None:
- self.major_ticklabels.set_visible(_ticklabels)
- self.minor_ticklabels.set_visible(_ticklabels)
- if _label is not None:
- self.label.set_visible(_label)
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axisline_style.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axisline_style.py
deleted file mode 100644
index 5ae188021bb..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axisline_style.py
+++ /dev/null
@@ -1,193 +0,0 @@
-"""
-Provides classes to style the axis lines.
-"""
-import math
-
-import numpy as np
-
-import matplotlib as mpl
-from matplotlib.patches import _Style, FancyArrowPatch
-from matplotlib.path import Path
-from matplotlib.transforms import IdentityTransform
-
-
-class _FancyAxislineStyle:
- class SimpleArrow(FancyArrowPatch):
- """The artist class that will be returned for SimpleArrow style."""
- _ARROW_STYLE = "->"
-
- def __init__(self, axis_artist, line_path, transform,
- line_mutation_scale):
- self._axis_artist = axis_artist
- self._line_transform = transform
- self._line_path = line_path
- self._line_mutation_scale = line_mutation_scale
-
- FancyArrowPatch.__init__(self,
- path=self._line_path,
- arrowstyle=self._ARROW_STYLE,
- patchA=None,
- patchB=None,
- shrinkA=0.,
- shrinkB=0.,
- mutation_scale=line_mutation_scale,
- mutation_aspect=None,
- transform=IdentityTransform(),
- )
-
- def set_line_mutation_scale(self, scale):
- self.set_mutation_scale(scale*self._line_mutation_scale)
-
- def _extend_path(self, path, mutation_size=10):
- """
- Extend the path to make a room for drawing arrow.
- """
- (x0, y0), (x1, y1) = path.vertices[-2:]
- theta = math.atan2(y1 - y0, x1 - x0)
- x2 = x1 + math.cos(theta) * mutation_size
- y2 = y1 + math.sin(theta) * mutation_size
- if path.codes is None:
- return Path(np.concatenate([path.vertices, [[x2, y2]]]))
- else:
- return Path(np.concatenate([path.vertices, [[x2, y2]]]),
- np.concatenate([path.codes, [Path.LINETO]]))
-
- def set_path(self, path):
- self._line_path = path
-
- def draw(self, renderer):
- """
- Draw the axis line.
- 1) Transform the path to the display coordinate.
- 2) Extend the path to make a room for arrow.
- 3) Update the path of the FancyArrowPatch.
- 4) Draw.
- """
- path_in_disp = self._line_transform.transform_path(self._line_path)
- mutation_size = self.get_mutation_scale() # line_mutation_scale()
- extended_path = self._extend_path(path_in_disp,
- mutation_size=mutation_size)
- self._path_original = extended_path
- FancyArrowPatch.draw(self, renderer)
-
- def get_window_extent(self, renderer=None):
-
- path_in_disp = self._line_transform.transform_path(self._line_path)
- mutation_size = self.get_mutation_scale() # line_mutation_scale()
- extended_path = self._extend_path(path_in_disp,
- mutation_size=mutation_size)
- self._path_original = extended_path
- return FancyArrowPatch.get_window_extent(self, renderer)
-
- class FilledArrow(SimpleArrow):
- """The artist class that will be returned for FilledArrow style."""
- _ARROW_STYLE = "-|>"
-
- def __init__(self, axis_artist, line_path, transform,
- line_mutation_scale, facecolor):
- super().__init__(axis_artist, line_path, transform,
- line_mutation_scale)
- self.set_facecolor(facecolor)
-
-
-class AxislineStyle(_Style):
- """
- A container class which defines style classes for AxisArtists.
-
- An instance of any axisline style class is a callable object,
- whose call signature is ::
-
- __call__(self, axis_artist, path, transform)
-
- When called, this should return an `.Artist` with the following methods::
-
- def set_path(self, path):
- # set the path for axisline.
-
- def set_line_mutation_scale(self, scale):
- # set the scale
-
- def draw(self, renderer):
- # draw
- """
-
- _style_list = {}
-
- class _Base:
- # The derived classes are required to be able to be initialized
- # w/o arguments, i.e., all its argument (except self) must have
- # the default values.
-
- def __init__(self):
- """
- initialization.
- """
- super().__init__()
-
- def __call__(self, axis_artist, transform):
- """
- Given the AxisArtist instance, and transform for the path (set_path
- method), return the Matplotlib artist for drawing the axis line.
- """
- return self.new_line(axis_artist, transform)
-
- class SimpleArrow(_Base):
- """
- A simple arrow.
- """
-
- ArrowAxisClass = _FancyAxislineStyle.SimpleArrow
-
- def __init__(self, size=1):
- """
- Parameters
- ----------
- size : float
- Size of the arrow as a fraction of the ticklabel size.
- """
-
- self.size = size
- super().__init__()
-
- def new_line(self, axis_artist, transform):
-
- linepath = Path([(0, 0), (0, 1)])
- axisline = self.ArrowAxisClass(axis_artist, linepath, transform,
- line_mutation_scale=self.size)
- return axisline
-
- _style_list["->"] = SimpleArrow
-
- class FilledArrow(SimpleArrow):
- """
- An arrow with a filled head.
- """
-
- ArrowAxisClass = _FancyAxislineStyle.FilledArrow
-
- def __init__(self, size=1, facecolor=None):
- """
- Parameters
- ----------
- size : float
- Size of the arrow as a fraction of the ticklabel size.
- facecolor : color, default: :rc:`axes.edgecolor`
- Fill color.
-
- .. versionadded:: 3.7
- """
-
- if facecolor is None:
- facecolor = mpl.rcParams['axes.edgecolor']
- self.size = size
- self._facecolor = facecolor
- super().__init__(size=size)
-
- def new_line(self, axis_artist, transform):
- linepath = Path([(0, 0), (0, 1)])
- axisline = self.ArrowAxisClass(axis_artist, linepath, transform,
- line_mutation_scale=self.size,
- facecolor=self._facecolor)
- return axisline
-
- _style_list["-|>"] = FilledArrow
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axislines.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axislines.py
deleted file mode 100644
index 35717da8eaa..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/axislines.py
+++ /dev/null
@@ -1,531 +0,0 @@
-"""
-Axislines includes modified implementation of the Axes class. The
-biggest difference is that the artists responsible for drawing the axis spine,
-ticks, ticklabels and axis labels are separated out from Matplotlib's Axis
-class. Originally, this change was motivated to support curvilinear
-grid. Here are a few reasons that I came up with a new axes class:
-
-* "top" and "bottom" x-axis (or "left" and "right" y-axis) can have
- different ticks (tick locations and labels). This is not possible
- with the current Matplotlib, although some twin axes trick can help.
-
-* Curvilinear grid.
-
-* angled ticks.
-
-In the new axes class, xaxis and yaxis is set to not visible by
-default, and new set of artist (AxisArtist) are defined to draw axis
-line, ticks, ticklabels and axis label. Axes.axis attribute serves as
-a dictionary of these artists, i.e., ax.axis["left"] is a AxisArtist
-instance responsible to draw left y-axis. The default Axes.axis contains
-"bottom", "left", "top" and "right".
-
-AxisArtist can be considered as a container artist and has the following
-children artists which will draw ticks, labels, etc.
-
-* line
-* major_ticks, major_ticklabels
-* minor_ticks, minor_ticklabels
-* offsetText
-* label
-
-Note that these are separate artists from `matplotlib.axis.Axis`, thus most
-tick-related functions in Matplotlib won't work. For example, color and
-markerwidth of the ``ax.axis["bottom"].major_ticks`` will follow those of
-Axes.xaxis unless explicitly specified.
-
-In addition to AxisArtist, the Axes will have *gridlines* attribute,
-which obviously draws grid lines. The gridlines needs to be separated
-from the axis as some gridlines can never pass any axis.
-"""
-
-import numpy as np
-
-import matplotlib as mpl
-from matplotlib import _api
-import matplotlib.axes as maxes
-from matplotlib.path import Path
-from mpl_toolkits.axes_grid1 import mpl_axes
-from .axisline_style import AxislineStyle # noqa
-from .axis_artist import AxisArtist, GridlinesCollection
-
-
-class _AxisArtistHelperBase:
- """
- Base class for axis helper.
-
- Subclasses should define the methods listed below. The *axes*
- argument will be the ``.axes`` attribute of the caller artist. ::
-
- # Construct the spine.
-
- def get_line_transform(self, axes):
- return transform
-
- def get_line(self, axes):
- return path
-
- # Construct the label.
-
- def get_axislabel_transform(self, axes):
- return transform
-
- def get_axislabel_pos_angle(self, axes):
- return (x, y), angle
-
- # Construct the ticks.
-
- def get_tick_transform(self, axes):
- return transform
-
- def get_tick_iterators(self, axes):
- # A pair of iterables (one for major ticks, one for minor ticks)
- # that yield (tick_position, tick_angle, tick_label).
- return iter_major, iter_minor
- """
-
- def update_lim(self, axes):
- pass
-
- def _to_xy(self, values, const):
- """
- Create a (*values.shape, 2)-shape array representing (x, y) pairs.
-
- The other coordinate is filled with the constant *const*.
-
- Example::
-
- >>> self.nth_coord = 0
- >>> self._to_xy([1, 2, 3], const=0)
- array([[1, 0],
- [2, 0],
- [3, 0]])
- """
- if self.nth_coord == 0:
- return np.stack(np.broadcast_arrays(values, const), axis=-1)
- elif self.nth_coord == 1:
- return np.stack(np.broadcast_arrays(const, values), axis=-1)
- else:
- raise ValueError("Unexpected nth_coord")
-
-
-class _FixedAxisArtistHelperBase(_AxisArtistHelperBase):
- """Helper class for a fixed (in the axes coordinate) axis."""
-
- passthru_pt = _api.deprecated("3.7")(property(
- lambda self: {"left": (0, 0), "right": (1, 0),
- "bottom": (0, 0), "top": (0, 1)}[self._loc]))
-
- def __init__(self, loc, nth_coord=None):
- """``nth_coord = 0``: x-axis; ``nth_coord = 1``: y-axis."""
- self.nth_coord = (
- nth_coord if nth_coord is not None else
- _api.check_getitem(
- {"bottom": 0, "top": 0, "left": 1, "right": 1}, loc=loc))
- if (nth_coord == 0 and loc not in ["left", "right"]
- or nth_coord == 1 and loc not in ["bottom", "top"]):
- _api.warn_deprecated(
- "3.7", message=f"{loc=!r} is incompatible with "
- "{nth_coord=}; support is deprecated since %(since)s")
- self._loc = loc
- self._pos = {"bottom": 0, "top": 1, "left": 0, "right": 1}[loc]
- super().__init__()
- # axis line in transAxes
- self._path = Path(self._to_xy((0, 1), const=self._pos))
-
- def get_nth_coord(self):
- return self.nth_coord
-
- # LINE
-
- def get_line(self, axes):
- return self._path
-
- def get_line_transform(self, axes):
- return axes.transAxes
-
- # LABEL
-
- def get_axislabel_transform(self, axes):
- return axes.transAxes
-
- def get_axislabel_pos_angle(self, axes):
- """
- Return the label reference position in transAxes.
-
- get_label_transform() returns a transform of (transAxes+offset)
- """
- return dict(left=((0., 0.5), 90), # (position, angle_tangent)
- right=((1., 0.5), 90),
- bottom=((0.5, 0.), 0),
- top=((0.5, 1.), 0))[self._loc]
-
- # TICK
-
- def get_tick_transform(self, axes):
- return [axes.get_xaxis_transform(),
- axes.get_yaxis_transform()][self.nth_coord]
-
-
-class _FloatingAxisArtistHelperBase(_AxisArtistHelperBase):
-
- def __init__(self, nth_coord, value):
- self.nth_coord = nth_coord
- self._value = value
- super().__init__()
-
- def get_nth_coord(self):
- return self.nth_coord
-
- def get_line(self, axes):
- raise RuntimeError(
- "get_line method should be defined by the derived class")
-
-
-class FixedAxisArtistHelperRectilinear(_FixedAxisArtistHelperBase):
-
- def __init__(self, axes, loc, nth_coord=None):
- """
- nth_coord = along which coordinate value varies
- in 2D, nth_coord = 0 -> x axis, nth_coord = 1 -> y axis
- """
- super().__init__(loc, nth_coord)
- self.axis = [axes.xaxis, axes.yaxis][self.nth_coord]
-
- # TICK
-
- def get_tick_iterators(self, axes):
- """tick_loc, tick_angle, tick_label"""
- if self._loc in ["bottom", "top"]:
- angle_normal, angle_tangent = 90, 0
- else: # "left", "right"
- angle_normal, angle_tangent = 0, 90
-
- major = self.axis.major
- major_locs = major.locator()
- major_labels = major.formatter.format_ticks(major_locs)
-
- minor = self.axis.minor
- minor_locs = minor.locator()
- minor_labels = minor.formatter.format_ticks(minor_locs)
-
- tick_to_axes = self.get_tick_transform(axes) - axes.transAxes
-
- def _f(locs, labels):
- for loc, label in zip(locs, labels):
- c = self._to_xy(loc, const=self._pos)
- # check if the tick point is inside axes
- c2 = tick_to_axes.transform(c)
- if mpl.transforms._interval_contains_close(
- (0, 1), c2[self.nth_coord]):
- yield c, angle_normal, angle_tangent, label
-
- return _f(major_locs, major_labels), _f(minor_locs, minor_labels)
-
-
-class FloatingAxisArtistHelperRectilinear(_FloatingAxisArtistHelperBase):
-
- def __init__(self, axes, nth_coord,
- passingthrough_point, axis_direction="bottom"):
- super().__init__(nth_coord, passingthrough_point)
- self._axis_direction = axis_direction
- self.axis = [axes.xaxis, axes.yaxis][self.nth_coord]
-
- def get_line(self, axes):
- fixed_coord = 1 - self.nth_coord
- data_to_axes = axes.transData - axes.transAxes
- p = data_to_axes.transform([self._value, self._value])
- return Path(self._to_xy((0, 1), const=p[fixed_coord]))
-
- def get_line_transform(self, axes):
- return axes.transAxes
-
- def get_axislabel_transform(self, axes):
- return axes.transAxes
-
- def get_axislabel_pos_angle(self, axes):
- """
- Return the label reference position in transAxes.
-
- get_label_transform() returns a transform of (transAxes+offset)
- """
- angle = [0, 90][self.nth_coord]
- fixed_coord = 1 - self.nth_coord
- data_to_axes = axes.transData - axes.transAxes
- p = data_to_axes.transform([self._value, self._value])
- verts = self._to_xy(0.5, const=p[fixed_coord])
- if 0 <= verts[fixed_coord] <= 1:
- return verts, angle
- else:
- return None, None
-
- def get_tick_transform(self, axes):
- return axes.transData
-
- def get_tick_iterators(self, axes):
- """tick_loc, tick_angle, tick_label"""
- if self.nth_coord == 0:
- angle_normal, angle_tangent = 90, 0
- else:
- angle_normal, angle_tangent = 0, 90
-
- major = self.axis.major
- major_locs = major.locator()
- major_labels = major.formatter.format_ticks(major_locs)
-
- minor = self.axis.minor
- minor_locs = minor.locator()
- minor_labels = minor.formatter.format_ticks(minor_locs)
-
- data_to_axes = axes.transData - axes.transAxes
-
- def _f(locs, labels):
- for loc, label in zip(locs, labels):
- c = self._to_xy(loc, const=self._value)
- c1, c2 = data_to_axes.transform(c)
- if 0 <= c1 <= 1 and 0 <= c2 <= 1:
- yield c, angle_normal, angle_tangent, label
-
- return _f(major_locs, major_labels), _f(minor_locs, minor_labels)
-
-
-class AxisArtistHelper: # Backcompat.
- Fixed = _FixedAxisArtistHelperBase
- Floating = _FloatingAxisArtistHelperBase
-
-
-class AxisArtistHelperRectlinear: # Backcompat.
- Fixed = FixedAxisArtistHelperRectilinear
- Floating = FloatingAxisArtistHelperRectilinear
-
-
-class GridHelperBase:
-
- def __init__(self):
- self._old_limits = None
- super().__init__()
-
- def update_lim(self, axes):
- x1, x2 = axes.get_xlim()
- y1, y2 = axes.get_ylim()
- if self._old_limits != (x1, x2, y1, y2):
- self._update_grid(x1, y1, x2, y2)
- self._old_limits = (x1, x2, y1, y2)
-
- def _update_grid(self, x1, y1, x2, y2):
- """Cache relevant computations when the axes limits have changed."""
-
- def get_gridlines(self, which, axis):
- """
- Return list of grid lines as a list of paths (list of points).
-
- Parameters
- ----------
- which : {"both", "major", "minor"}
- axis : {"both", "x", "y"}
- """
- return []
-
-
-class GridHelperRectlinear(GridHelperBase):
-
- def __init__(self, axes):
- super().__init__()
- self.axes = axes
-
- def new_fixed_axis(self, loc,
- nth_coord=None,
- axis_direction=None,
- offset=None,
- axes=None,
- ):
- if axes is None:
- _api.warn_external(
- "'new_fixed_axis' explicitly requires the axes keyword.")
- axes = self.axes
- if axis_direction is None:
- axis_direction = loc
-
- helper = FixedAxisArtistHelperRectilinear(axes, loc, nth_coord)
- axisline = AxisArtist(axes, helper, offset=offset,
- axis_direction=axis_direction)
- return axisline
-
- def new_floating_axis(self, nth_coord, value,
- axis_direction="bottom",
- axes=None,
- ):
- if axes is None:
- _api.warn_external(
- "'new_floating_axis' explicitly requires the axes keyword.")
- axes = self.axes
-
- helper = FloatingAxisArtistHelperRectilinear(
- axes, nth_coord, value, axis_direction)
- axisline = AxisArtist(axes, helper, axis_direction=axis_direction)
- axisline.line.set_clip_on(True)
- axisline.line.set_clip_box(axisline.axes.bbox)
- return axisline
-
- def get_gridlines(self, which="major", axis="both"):
- """
- Return list of gridline coordinates in data coordinates.
-
- Parameters
- ----------
- which : {"both", "major", "minor"}
- axis : {"both", "x", "y"}
- """
- _api.check_in_list(["both", "major", "minor"], which=which)
- _api.check_in_list(["both", "x", "y"], axis=axis)
- gridlines = []
-
- if axis in ("both", "x"):
- locs = []
- y1, y2 = self.axes.get_ylim()
- if which in ("both", "major"):
- locs.extend(self.axes.xaxis.major.locator())
- if which in ("both", "minor"):
- locs.extend(self.axes.xaxis.minor.locator())
-
- for x in locs:
- gridlines.append([[x, x], [y1, y2]])
-
- if axis in ("both", "y"):
- x1, x2 = self.axes.get_xlim()
- locs = []
- if self.axes.yaxis._major_tick_kw["gridOn"]:
- locs.extend(self.axes.yaxis.major.locator())
- if self.axes.yaxis._minor_tick_kw["gridOn"]:
- locs.extend(self.axes.yaxis.minor.locator())
-
- for y in locs:
- gridlines.append([[x1, x2], [y, y]])
-
- return gridlines
-
-
-class Axes(maxes.Axes):
-
- @_api.deprecated("3.8", alternative="ax.axis")
- def __call__(self, *args, **kwargs):
- return maxes.Axes.axis(self.axes, *args, **kwargs)
-
- def __init__(self, *args, grid_helper=None, **kwargs):
- self._axisline_on = True
- self._grid_helper = (grid_helper if grid_helper
- else GridHelperRectlinear(self))
- super().__init__(*args, **kwargs)
- self.toggle_axisline(True)
-
- def toggle_axisline(self, b=None):
- if b is None:
- b = not self._axisline_on
- if b:
- self._axisline_on = True
- self.spines[:].set_visible(False)
- self.xaxis.set_visible(False)
- self.yaxis.set_visible(False)
- else:
- self._axisline_on = False
- self.spines[:].set_visible(True)
- self.xaxis.set_visible(True)
- self.yaxis.set_visible(True)
-
- @property
- def axis(self):
- return self._axislines
-
- def clear(self):
- # docstring inherited
-
- # Init gridlines before clear() as clear() calls grid().
- self.gridlines = gridlines = GridlinesCollection(
- [],
- colors=mpl.rcParams['grid.color'],
- linestyles=mpl.rcParams['grid.linestyle'],
- linewidths=mpl.rcParams['grid.linewidth'])
- self._set_artist_props(gridlines)
- gridlines.set_grid_helper(self.get_grid_helper())
-
- super().clear()
-
- # clip_path is set after Axes.clear(): that's when a patch is created.
- gridlines.set_clip_path(self.axes.patch)
-
- # Init axis artists.
- self._axislines = mpl_axes.Axes.AxisDict(self)
- new_fixed_axis = self.get_grid_helper().new_fixed_axis
- self._axislines.update({
- loc: new_fixed_axis(loc=loc, axes=self, axis_direction=loc)
- for loc in ["bottom", "top", "left", "right"]})
- for axisline in [self._axislines["top"], self._axislines["right"]]:
- axisline.label.set_visible(False)
- axisline.major_ticklabels.set_visible(False)
- axisline.minor_ticklabels.set_visible(False)
-
- def get_grid_helper(self):
- return self._grid_helper
-
- def grid(self, visible=None, which='major', axis="both", **kwargs):
- """
- Toggle the gridlines, and optionally set the properties of the lines.
- """
- # There are some discrepancies in the behavior of grid() between
- # axes_grid and Matplotlib, because axes_grid explicitly sets the
- # visibility of the gridlines.
- super().grid(visible, which=which, axis=axis, **kwargs)
- if not self._axisline_on:
- return
- if visible is None:
- visible = (self.axes.xaxis._minor_tick_kw["gridOn"]
- or self.axes.xaxis._major_tick_kw["gridOn"]
- or self.axes.yaxis._minor_tick_kw["gridOn"]
- or self.axes.yaxis._major_tick_kw["gridOn"])
- self.gridlines.set(which=which, axis=axis, visible=visible)
- self.gridlines.set(**kwargs)
-
- def get_children(self):
- if self._axisline_on:
- children = [*self._axislines.values(), self.gridlines]
- else:
- children = []
- children.extend(super().get_children())
- return children
-
- def new_fixed_axis(self, loc, offset=None):
- gh = self.get_grid_helper()
- axis = gh.new_fixed_axis(loc,
- nth_coord=None,
- axis_direction=None,
- offset=offset,
- axes=self,
- )
- return axis
-
- def new_floating_axis(self, nth_coord, value, axis_direction="bottom"):
- gh = self.get_grid_helper()
- axis = gh.new_floating_axis(nth_coord, value,
- axis_direction=axis_direction,
- axes=self)
- return axis
-
-
-class AxesZero(Axes):
-
- def clear(self):
- super().clear()
- new_floating_axis = self.get_grid_helper().new_floating_axis
- self._axislines.update(
- xzero=new_floating_axis(
- nth_coord=0, value=0., axis_direction="bottom", axes=self),
- yzero=new_floating_axis(
- nth_coord=1, value=0., axis_direction="left", axes=self),
- )
- for k in ["xzero", "yzero"]:
- self._axislines[k].line.set_clip_path(self.patch)
- self._axislines[k].set_visible(False)
-
-
-Subplot = Axes
-SubplotZero = AxesZero
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/floating_axes.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/floating_axes.py
deleted file mode 100644
index 97dafe98c69..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/floating_axes.py
+++ /dev/null
@@ -1,298 +0,0 @@
-"""
-An experimental support for curvilinear grid.
-"""
-
-# TODO :
-# see if tick_iterator method can be simplified by reusing the parent method.
-
-import functools
-
-import numpy as np
-
-import matplotlib as mpl
-from matplotlib import _api, cbook
-import matplotlib.patches as mpatches
-from matplotlib.path import Path
-
-from mpl_toolkits.axes_grid1.parasite_axes import host_axes_class_factory
-
-from . import axislines, grid_helper_curvelinear
-from .axis_artist import AxisArtist
-from .grid_finder import ExtremeFinderSimple
-
-
-class FloatingAxisArtistHelper(
- grid_helper_curvelinear.FloatingAxisArtistHelper):
- pass
-
-
-class FixedAxisArtistHelper(grid_helper_curvelinear.FloatingAxisArtistHelper):
-
- def __init__(self, grid_helper, side, nth_coord_ticks=None):
- """
- nth_coord = along which coordinate value varies.
- nth_coord = 0 -> x axis, nth_coord = 1 -> y axis
- """
- lon1, lon2, lat1, lat2 = grid_helper.grid_finder.extreme_finder(*[None] * 5)
- value, nth_coord = _api.check_getitem(
- dict(left=(lon1, 0), right=(lon2, 0), bottom=(lat1, 1), top=(lat2, 1)),
- side=side)
- super().__init__(grid_helper, nth_coord, value, axis_direction=side)
- if nth_coord_ticks is None:
- nth_coord_ticks = nth_coord
- self.nth_coord_ticks = nth_coord_ticks
-
- self.value = value
- self.grid_helper = grid_helper
- self._side = side
-
- def update_lim(self, axes):
- self.grid_helper.update_lim(axes)
- self._grid_info = self.grid_helper._grid_info
-
- def get_tick_iterators(self, axes):
- """tick_loc, tick_angle, tick_label, (optionally) tick_label"""
-
- grid_finder = self.grid_helper.grid_finder
-
- lat_levs, lat_n, lat_factor = self._grid_info["lat_info"]
- yy0 = lat_levs / lat_factor
-
- lon_levs, lon_n, lon_factor = self._grid_info["lon_info"]
- xx0 = lon_levs / lon_factor
-
- extremes = self.grid_helper.grid_finder.extreme_finder(*[None] * 5)
- xmin, xmax = sorted(extremes[:2])
- ymin, ymax = sorted(extremes[2:])
-
- def trf_xy(x, y):
- trf = grid_finder.get_transform() + axes.transData
- return trf.transform(np.column_stack(np.broadcast_arrays(x, y))).T
-
- if self.nth_coord == 0:
- mask = (ymin <= yy0) & (yy0 <= ymax)
- (xx1, yy1), (dxx1, dyy1), (dxx2, dyy2) = \
- grid_helper_curvelinear._value_and_jacobian(
- trf_xy, self.value, yy0[mask], (xmin, xmax), (ymin, ymax))
- labels = self._grid_info["lat_labels"]
-
- elif self.nth_coord == 1:
- mask = (xmin <= xx0) & (xx0 <= xmax)
- (xx1, yy1), (dxx2, dyy2), (dxx1, dyy1) = \
- grid_helper_curvelinear._value_and_jacobian(
- trf_xy, xx0[mask], self.value, (xmin, xmax), (ymin, ymax))
- labels = self._grid_info["lon_labels"]
-
- labels = [l for l, m in zip(labels, mask) if m]
-
- angle_normal = np.arctan2(dyy1, dxx1)
- angle_tangent = np.arctan2(dyy2, dxx2)
- mm = (dyy1 == 0) & (dxx1 == 0) # points with degenerate normal
- angle_normal[mm] = angle_tangent[mm] + np.pi / 2
-
- tick_to_axes = self.get_tick_transform(axes) - axes.transAxes
- in_01 = functools.partial(
- mpl.transforms._interval_contains_close, (0, 1))
-
- def f1():
- for x, y, normal, tangent, lab \
- in zip(xx1, yy1, angle_normal, angle_tangent, labels):
- c2 = tick_to_axes.transform((x, y))
- if in_01(c2[0]) and in_01(c2[1]):
- yield [x, y], *np.rad2deg([normal, tangent]), lab
-
- return f1(), iter([])
-
- def get_line(self, axes):
- self.update_lim(axes)
- k, v = dict(left=("lon_lines0", 0),
- right=("lon_lines0", 1),
- bottom=("lat_lines0", 0),
- top=("lat_lines0", 1))[self._side]
- xx, yy = self._grid_info[k][v]
- return Path(np.column_stack([xx, yy]))
-
-
-class ExtremeFinderFixed(ExtremeFinderSimple):
- # docstring inherited
-
- def __init__(self, extremes):
- """
- This subclass always returns the same bounding box.
-
- Parameters
- ----------
- extremes : (float, float, float, float)
- The bounding box that this helper always returns.
- """
- self._extremes = extremes
-
- def __call__(self, transform_xy, x1, y1, x2, y2):
- # docstring inherited
- return self._extremes
-
-
-class GridHelperCurveLinear(grid_helper_curvelinear.GridHelperCurveLinear):
-
- def __init__(self, aux_trans, extremes,
- grid_locator1=None,
- grid_locator2=None,
- tick_formatter1=None,
- tick_formatter2=None):
- # docstring inherited
- super().__init__(aux_trans,
- extreme_finder=ExtremeFinderFixed(extremes),
- grid_locator1=grid_locator1,
- grid_locator2=grid_locator2,
- tick_formatter1=tick_formatter1,
- tick_formatter2=tick_formatter2)
-
- @_api.deprecated("3.8")
- def get_data_boundary(self, side):
- """
- Return v=0, nth=1.
- """
- lon1, lon2, lat1, lat2 = self.grid_finder.extreme_finder(*[None] * 5)
- return dict(left=(lon1, 0),
- right=(lon2, 0),
- bottom=(lat1, 1),
- top=(lat2, 1))[side]
-
- def new_fixed_axis(self, loc,
- nth_coord=None,
- axis_direction=None,
- offset=None,
- axes=None):
- if axes is None:
- axes = self.axes
- if axis_direction is None:
- axis_direction = loc
- # This is not the same as the FixedAxisArtistHelper class used by
- # grid_helper_curvelinear.GridHelperCurveLinear.new_fixed_axis!
- helper = FixedAxisArtistHelper(
- self, loc, nth_coord_ticks=nth_coord)
- axisline = AxisArtist(axes, helper, axis_direction=axis_direction)
- # Perhaps should be moved to the base class?
- axisline.line.set_clip_on(True)
- axisline.line.set_clip_box(axisline.axes.bbox)
- return axisline
-
- # new_floating_axis will inherit the grid_helper's extremes.
-
- # def new_floating_axis(self, nth_coord,
- # value,
- # axes=None,
- # axis_direction="bottom"
- # ):
-
- # axis = super(GridHelperCurveLinear,
- # self).new_floating_axis(nth_coord,
- # value, axes=axes,
- # axis_direction=axis_direction)
-
- # # set extreme values of the axis helper
- # if nth_coord == 1:
- # axis.get_helper().set_extremes(*self._extremes[:2])
- # elif nth_coord == 0:
- # axis.get_helper().set_extremes(*self._extremes[2:])
-
- # return axis
-
- def _update_grid(self, x1, y1, x2, y2):
- if self._grid_info is None:
- self._grid_info = dict()
-
- grid_info = self._grid_info
-
- grid_finder = self.grid_finder
- extremes = grid_finder.extreme_finder(grid_finder.inv_transform_xy,
- x1, y1, x2, y2)
-
- lon_min, lon_max = sorted(extremes[:2])
- lat_min, lat_max = sorted(extremes[2:])
- grid_info["extremes"] = lon_min, lon_max, lat_min, lat_max # extremes
-
- lon_levs, lon_n, lon_factor = \
- grid_finder.grid_locator1(lon_min, lon_max)
- lon_levs = np.asarray(lon_levs)
- lat_levs, lat_n, lat_factor = \
- grid_finder.grid_locator2(lat_min, lat_max)
- lat_levs = np.asarray(lat_levs)
-
- grid_info["lon_info"] = lon_levs, lon_n, lon_factor
- grid_info["lat_info"] = lat_levs, lat_n, lat_factor
-
- grid_info["lon_labels"] = grid_finder.tick_formatter1(
- "bottom", lon_factor, lon_levs)
- grid_info["lat_labels"] = grid_finder.tick_formatter2(
- "bottom", lat_factor, lat_levs)
-
- lon_values = lon_levs[:lon_n] / lon_factor
- lat_values = lat_levs[:lat_n] / lat_factor
-
- lon_lines, lat_lines = grid_finder._get_raw_grid_lines(
- lon_values[(lon_min < lon_values) & (lon_values < lon_max)],
- lat_values[(lat_min < lat_values) & (lat_values < lat_max)],
- lon_min, lon_max, lat_min, lat_max)
-
- grid_info["lon_lines"] = lon_lines
- grid_info["lat_lines"] = lat_lines
-
- lon_lines, lat_lines = grid_finder._get_raw_grid_lines(
- # lon_min, lon_max, lat_min, lat_max)
- extremes[:2], extremes[2:], *extremes)
-
- grid_info["lon_lines0"] = lon_lines
- grid_info["lat_lines0"] = lat_lines
-
- def get_gridlines(self, which="major", axis="both"):
- grid_lines = []
- if axis in ["both", "x"]:
- grid_lines.extend(self._grid_info["lon_lines"])
- if axis in ["both", "y"]:
- grid_lines.extend(self._grid_info["lat_lines"])
- return grid_lines
-
-
-class FloatingAxesBase:
-
- def __init__(self, *args, grid_helper, **kwargs):
- _api.check_isinstance(GridHelperCurveLinear, grid_helper=grid_helper)
- super().__init__(*args, grid_helper=grid_helper, **kwargs)
- self.set_aspect(1.)
-
- def _gen_axes_patch(self):
- # docstring inherited
- x0, x1, y0, y1 = self.get_grid_helper().grid_finder.extreme_finder(*[None] * 5)
- patch = mpatches.Polygon([(x0, y0), (x1, y0), (x1, y1), (x0, y1)])
- patch.get_path()._interpolation_steps = 100
- return patch
-
- def clear(self):
- super().clear()
- self.patch.set_transform(
- self.get_grid_helper().grid_finder.get_transform()
- + self.transData)
- # The original patch is not in the draw tree; it is only used for
- # clipping purposes.
- orig_patch = super()._gen_axes_patch()
- orig_patch.set_figure(self.figure)
- orig_patch.set_transform(self.transAxes)
- self.patch.set_clip_path(orig_patch)
- self.gridlines.set_clip_path(orig_patch)
- self.adjust_axes_lim()
-
- def adjust_axes_lim(self):
- bbox = self.patch.get_path().get_extents(
- # First transform to pixel coords, then to parent data coords.
- self.patch.get_transform() - self.transData)
- bbox = bbox.expanded(1.02, 1.02)
- self.set_xlim(bbox.xmin, bbox.xmax)
- self.set_ylim(bbox.ymin, bbox.ymax)
-
-
-floatingaxes_class_factory = cbook._make_class_factory(
- FloatingAxesBase, "Floating{}")
-FloatingAxes = floatingaxes_class_factory(
- host_axes_class_factory(axislines.Axes))
-FloatingSubplot = FloatingAxes
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/grid_finder.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/grid_finder.py
deleted file mode 100644
index f969b011c4c..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/grid_finder.py
+++ /dev/null
@@ -1,335 +0,0 @@
-import numpy as np
-
-from matplotlib import ticker as mticker
-from matplotlib.transforms import Bbox, Transform
-
-
-def _find_line_box_crossings(xys, bbox):
- """
- Find the points where a polyline crosses a bbox, and the crossing angles.
-
- Parameters
- ----------
- xys : (N, 2) array
- The polyline coordinates.
- bbox : `.Bbox`
- The bounding box.
-
- Returns
- -------
- list of ((float, float), float)
- Four separate lists of crossings, for the left, right, bottom, and top
- sides of the bbox, respectively. For each list, the entries are the
- ``((x, y), ccw_angle_in_degrees)`` of the crossing, where an angle of 0
- means that the polyline is moving to the right at the crossing point.
-
- The entries are computed by linearly interpolating at each crossing
- between the nearest points on either side of the bbox edges.
- """
- crossings = []
- dxys = xys[1:] - xys[:-1]
- for sl in [slice(None), slice(None, None, -1)]:
- us, vs = xys.T[sl] # "this" coord, "other" coord
- dus, dvs = dxys.T[sl]
- umin, vmin = bbox.min[sl]
- umax, vmax = bbox.max[sl]
- for u0, inside in [(umin, us > umin), (umax, us < umax)]:
- crossings.append([])
- idxs, = (inside[:-1] ^ inside[1:]).nonzero()
- for idx in idxs:
- v = vs[idx] + (u0 - us[idx]) * dvs[idx] / dus[idx]
- if not vmin <= v <= vmax:
- continue
- crossing = (u0, v)[sl]
- theta = np.degrees(np.arctan2(*dxys[idx][::-1]))
- crossings[-1].append((crossing, theta))
- return crossings
-
-
-class ExtremeFinderSimple:
- """
- A helper class to figure out the range of grid lines that need to be drawn.
- """
-
- def __init__(self, nx, ny):
- """
- Parameters
- ----------
- nx, ny : int
- The number of samples in each direction.
- """
- self.nx = nx
- self.ny = ny
-
- def __call__(self, transform_xy, x1, y1, x2, y2):
- """
- Compute an approximation of the bounding box obtained by applying
- *transform_xy* to the box delimited by ``(x1, y1, x2, y2)``.
-
- The intended use is to have ``(x1, y1, x2, y2)`` in axes coordinates,
- and have *transform_xy* be the transform from axes coordinates to data
- coordinates; this method then returns the range of data coordinates
- that span the actual axes.
-
- The computation is done by sampling ``nx * ny`` equispaced points in
- the ``(x1, y1, x2, y2)`` box and finding the resulting points with
- extremal coordinates; then adding some padding to take into account the
- finite sampling.
-
- As each sampling step covers a relative range of *1/nx* or *1/ny*,
- the padding is computed by expanding the span covered by the extremal
- coordinates by these fractions.
- """
- x, y = np.meshgrid(
- np.linspace(x1, x2, self.nx), np.linspace(y1, y2, self.ny))
- xt, yt = transform_xy(np.ravel(x), np.ravel(y))
- return self._add_pad(xt.min(), xt.max(), yt.min(), yt.max())
-
- def _add_pad(self, x_min, x_max, y_min, y_max):
- """Perform the padding mentioned in `__call__`."""
- dx = (x_max - x_min) / self.nx
- dy = (y_max - y_min) / self.ny
- return x_min - dx, x_max + dx, y_min - dy, y_max + dy
-
-
-class _User2DTransform(Transform):
- """A transform defined by two user-set functions."""
-
- input_dims = output_dims = 2
-
- def __init__(self, forward, backward):
- """
- Parameters
- ----------
- forward, backward : callable
- The forward and backward transforms, taking ``x`` and ``y`` as
- separate arguments and returning ``(tr_x, tr_y)``.
- """
- # The normal Matplotlib convention would be to take and return an
- # (N, 2) array but axisartist uses the transposed version.
- super().__init__()
- self._forward = forward
- self._backward = backward
-
- def transform_non_affine(self, values):
- # docstring inherited
- return np.transpose(self._forward(*np.transpose(values)))
-
- def inverted(self):
- # docstring inherited
- return type(self)(self._backward, self._forward)
-
-
-class GridFinder:
- """
- Internal helper for `~.grid_helper_curvelinear.GridHelperCurveLinear`, with
- the same constructor parameters; should not be directly instantiated.
- """
-
- def __init__(self,
- transform,
- extreme_finder=None,
- grid_locator1=None,
- grid_locator2=None,
- tick_formatter1=None,
- tick_formatter2=None):
- if extreme_finder is None:
- extreme_finder = ExtremeFinderSimple(20, 20)
- if grid_locator1 is None:
- grid_locator1 = MaxNLocator()
- if grid_locator2 is None:
- grid_locator2 = MaxNLocator()
- if tick_formatter1 is None:
- tick_formatter1 = FormatterPrettyPrint()
- if tick_formatter2 is None:
- tick_formatter2 = FormatterPrettyPrint()
- self.extreme_finder = extreme_finder
- self.grid_locator1 = grid_locator1
- self.grid_locator2 = grid_locator2
- self.tick_formatter1 = tick_formatter1
- self.tick_formatter2 = tick_formatter2
- self.set_transform(transform)
-
- def get_grid_info(self, x1, y1, x2, y2):
- """
- lon_values, lat_values : list of grid values. if integer is given,
- rough number of grids in each direction.
- """
-
- extremes = self.extreme_finder(self.inv_transform_xy, x1, y1, x2, y2)
-
- # min & max rage of lat (or lon) for each grid line will be drawn.
- # i.e., gridline of lon=0 will be drawn from lat_min to lat_max.
-
- lon_min, lon_max, lat_min, lat_max = extremes
- lon_levs, lon_n, lon_factor = self.grid_locator1(lon_min, lon_max)
- lon_levs = np.asarray(lon_levs)
- lat_levs, lat_n, lat_factor = self.grid_locator2(lat_min, lat_max)
- lat_levs = np.asarray(lat_levs)
-
- lon_values = lon_levs[:lon_n] / lon_factor
- lat_values = lat_levs[:lat_n] / lat_factor
-
- lon_lines, lat_lines = self._get_raw_grid_lines(lon_values,
- lat_values,
- lon_min, lon_max,
- lat_min, lat_max)
-
- ddx = (x2-x1)*1.e-10
- ddy = (y2-y1)*1.e-10
- bb = Bbox.from_extents(x1-ddx, y1-ddy, x2+ddx, y2+ddy)
-
- grid_info = {
- "extremes": extremes,
- "lon_lines": lon_lines,
- "lat_lines": lat_lines,
- "lon": self._clip_grid_lines_and_find_ticks(
- lon_lines, lon_values, lon_levs, bb),
- "lat": self._clip_grid_lines_and_find_ticks(
- lat_lines, lat_values, lat_levs, bb),
- }
-
- tck_labels = grid_info["lon"]["tick_labels"] = {}
- for direction in ["left", "bottom", "right", "top"]:
- levs = grid_info["lon"]["tick_levels"][direction]
- tck_labels[direction] = self.tick_formatter1(
- direction, lon_factor, levs)
-
- tck_labels = grid_info["lat"]["tick_labels"] = {}
- for direction in ["left", "bottom", "right", "top"]:
- levs = grid_info["lat"]["tick_levels"][direction]
- tck_labels[direction] = self.tick_formatter2(
- direction, lat_factor, levs)
-
- return grid_info
-
- def _get_raw_grid_lines(self,
- lon_values, lat_values,
- lon_min, lon_max, lat_min, lat_max):
-
- lons_i = np.linspace(lon_min, lon_max, 100) # for interpolation
- lats_i = np.linspace(lat_min, lat_max, 100)
-
- lon_lines = [self.transform_xy(np.full_like(lats_i, lon), lats_i)
- for lon in lon_values]
- lat_lines = [self.transform_xy(lons_i, np.full_like(lons_i, lat))
- for lat in lat_values]
-
- return lon_lines, lat_lines
-
- def _clip_grid_lines_and_find_ticks(self, lines, values, levs, bb):
- gi = {
- "values": [],
- "levels": [],
- "tick_levels": dict(left=[], bottom=[], right=[], top=[]),
- "tick_locs": dict(left=[], bottom=[], right=[], top=[]),
- "lines": [],
- }
-
- tck_levels = gi["tick_levels"]
- tck_locs = gi["tick_locs"]
- for (lx, ly), v, lev in zip(lines, values, levs):
- tcks = _find_line_box_crossings(np.column_stack([lx, ly]), bb)
- gi["levels"].append(v)
- gi["lines"].append([(lx, ly)])
-
- for tck, direction in zip(tcks,
- ["left", "right", "bottom", "top"]):
- for t in tck:
- tck_levels[direction].append(lev)
- tck_locs[direction].append(t)
-
- return gi
-
- def set_transform(self, aux_trans):
- if isinstance(aux_trans, Transform):
- self._aux_transform = aux_trans
- elif len(aux_trans) == 2 and all(map(callable, aux_trans)):
- self._aux_transform = _User2DTransform(*aux_trans)
- else:
- raise TypeError("'aux_trans' must be either a Transform "
- "instance or a pair of callables")
-
- def get_transform(self):
- return self._aux_transform
-
- update_transform = set_transform # backcompat alias.
-
- def transform_xy(self, x, y):
- return self._aux_transform.transform(np.column_stack([x, y])).T
-
- def inv_transform_xy(self, x, y):
- return self._aux_transform.inverted().transform(
- np.column_stack([x, y])).T
-
- def update(self, **kwargs):
- for k, v in kwargs.items():
- if k in ["extreme_finder",
- "grid_locator1",
- "grid_locator2",
- "tick_formatter1",
- "tick_formatter2"]:
- setattr(self, k, v)
- else:
- raise ValueError(f"Unknown update property {k!r}")
-
-
-class MaxNLocator(mticker.MaxNLocator):
- def __init__(self, nbins=10, steps=None,
- trim=True,
- integer=False,
- symmetric=False,
- prune=None):
- # trim argument has no effect. It has been left for API compatibility
- super().__init__(nbins, steps=steps, integer=integer,
- symmetric=symmetric, prune=prune)
- self.create_dummy_axis()
-
- def __call__(self, v1, v2):
- locs = super().tick_values(v1, v2)
- return np.array(locs), len(locs), 1 # 1: factor (see angle_helper)
-
-
-class FixedLocator:
- def __init__(self, locs):
- self._locs = locs
-
- def __call__(self, v1, v2):
- v1, v2 = sorted([v1, v2])
- locs = np.array([l for l in self._locs if v1 <= l <= v2])
- return locs, len(locs), 1 # 1: factor (see angle_helper)
-
-
-# Tick Formatter
-
-class FormatterPrettyPrint:
- def __init__(self, useMathText=True):
- self._fmt = mticker.ScalarFormatter(
- useMathText=useMathText, useOffset=False)
- self._fmt.create_dummy_axis()
-
- def __call__(self, direction, factor, values):
- return self._fmt.format_ticks(values)
-
-
-class DictFormatter:
- def __init__(self, format_dict, formatter=None):
- """
- format_dict : dictionary for format strings to be used.
- formatter : fall-back formatter
- """
- super().__init__()
- self._format_dict = format_dict
- self._fallback_formatter = formatter
-
- def __call__(self, direction, factor, values):
- """
- factor is ignored if value is found in the dictionary
- """
- if self._fallback_formatter:
- fallback_strings = self._fallback_formatter(
- direction, factor, values)
- else:
- fallback_strings = [""] * len(values)
- return [self._format_dict.get(k, v)
- for k, v in zip(values, fallback_strings)]
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/grid_helper_curvelinear.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/grid_helper_curvelinear.py
deleted file mode 100644
index ae17452b6c5..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/grid_helper_curvelinear.py
+++ /dev/null
@@ -1,336 +0,0 @@
-"""
-An experimental support for curvilinear grid.
-"""
-
-import functools
-from itertools import chain
-
-import numpy as np
-
-import matplotlib as mpl
-from matplotlib.path import Path
-from matplotlib.transforms import Affine2D, IdentityTransform
-from .axislines import (
- _FixedAxisArtistHelperBase, _FloatingAxisArtistHelperBase, GridHelperBase)
-from .axis_artist import AxisArtist
-from .grid_finder import GridFinder
-
-
-def _value_and_jacobian(func, xs, ys, xlims, ylims):
- """
- Compute *func* and its derivatives along x and y at positions *xs*, *ys*,
- while ensuring that finite difference calculations don't try to evaluate
- values outside of *xlims*, *ylims*.
- """
- eps = np.finfo(float).eps ** (1/2) # see e.g. scipy.optimize.approx_fprime
- val = func(xs, ys)
- # Take the finite difference step in the direction where the bound is the
- # furthest; the step size is min of epsilon and distance to that bound.
- xlo, xhi = sorted(xlims)
- dxlo = xs - xlo
- dxhi = xhi - xs
- xeps = (np.take([-1, 1], dxhi >= dxlo)
- * np.minimum(eps, np.maximum(dxlo, dxhi)))
- val_dx = func(xs + xeps, ys)
- ylo, yhi = sorted(ylims)
- dylo = ys - ylo
- dyhi = yhi - ys
- yeps = (np.take([-1, 1], dyhi >= dylo)
- * np.minimum(eps, np.maximum(dylo, dyhi)))
- val_dy = func(xs, ys + yeps)
- return (val, (val_dx - val) / xeps, (val_dy - val) / yeps)
-
-
-class FixedAxisArtistHelper(_FixedAxisArtistHelperBase):
- """
- Helper class for a fixed axis.
- """
-
- def __init__(self, grid_helper, side, nth_coord_ticks=None):
- """
- nth_coord = along which coordinate value varies.
- nth_coord = 0 -> x axis, nth_coord = 1 -> y axis
- """
-
- super().__init__(loc=side)
-
- self.grid_helper = grid_helper
- if nth_coord_ticks is None:
- nth_coord_ticks = self.nth_coord
- self.nth_coord_ticks = nth_coord_ticks
-
- self.side = side
-
- def update_lim(self, axes):
- self.grid_helper.update_lim(axes)
-
- def get_tick_transform(self, axes):
- return axes.transData
-
- def get_tick_iterators(self, axes):
- """tick_loc, tick_angle, tick_label"""
- v1, v2 = axes.get_ylim() if self.nth_coord == 0 else axes.get_xlim()
- if v1 > v2: # Inverted limits.
- side = {"left": "right", "right": "left",
- "top": "bottom", "bottom": "top"}[self.side]
- else:
- side = self.side
- g = self.grid_helper
- ti1 = g.get_tick_iterator(self.nth_coord_ticks, side)
- ti2 = g.get_tick_iterator(1-self.nth_coord_ticks, side, minor=True)
- return chain(ti1, ti2), iter([])
-
-
-class FloatingAxisArtistHelper(_FloatingAxisArtistHelperBase):
-
- def __init__(self, grid_helper, nth_coord, value, axis_direction=None):
- """
- nth_coord = along which coordinate value varies.
- nth_coord = 0 -> x axis, nth_coord = 1 -> y axis
- """
- super().__init__(nth_coord, value)
- self.value = value
- self.grid_helper = grid_helper
- self._extremes = -np.inf, np.inf
- self._line_num_points = 100 # number of points to create a line
-
- def set_extremes(self, e1, e2):
- if e1 is None:
- e1 = -np.inf
- if e2 is None:
- e2 = np.inf
- self._extremes = e1, e2
-
- def update_lim(self, axes):
- self.grid_helper.update_lim(axes)
-
- x1, x2 = axes.get_xlim()
- y1, y2 = axes.get_ylim()
- grid_finder = self.grid_helper.grid_finder
- extremes = grid_finder.extreme_finder(grid_finder.inv_transform_xy,
- x1, y1, x2, y2)
-
- lon_min, lon_max, lat_min, lat_max = extremes
- e_min, e_max = self._extremes # ranges of other coordinates
- if self.nth_coord == 0:
- lat_min = max(e_min, lat_min)
- lat_max = min(e_max, lat_max)
- elif self.nth_coord == 1:
- lon_min = max(e_min, lon_min)
- lon_max = min(e_max, lon_max)
-
- lon_levs, lon_n, lon_factor = \
- grid_finder.grid_locator1(lon_min, lon_max)
- lat_levs, lat_n, lat_factor = \
- grid_finder.grid_locator2(lat_min, lat_max)
-
- if self.nth_coord == 0:
- xx0 = np.full(self._line_num_points, self.value)
- yy0 = np.linspace(lat_min, lat_max, self._line_num_points)
- xx, yy = grid_finder.transform_xy(xx0, yy0)
- elif self.nth_coord == 1:
- xx0 = np.linspace(lon_min, lon_max, self._line_num_points)
- yy0 = np.full(self._line_num_points, self.value)
- xx, yy = grid_finder.transform_xy(xx0, yy0)
-
- self._grid_info = {
- "extremes": (lon_min, lon_max, lat_min, lat_max),
- "lon_info": (lon_levs, lon_n, np.asarray(lon_factor)),
- "lat_info": (lat_levs, lat_n, np.asarray(lat_factor)),
- "lon_labels": grid_finder.tick_formatter1(
- "bottom", lon_factor, lon_levs),
- "lat_labels": grid_finder.tick_formatter2(
- "bottom", lat_factor, lat_levs),
- "line_xy": (xx, yy),
- }
-
- def get_axislabel_transform(self, axes):
- return Affine2D() # axes.transData
-
- def get_axislabel_pos_angle(self, axes):
- def trf_xy(x, y):
- trf = self.grid_helper.grid_finder.get_transform() + axes.transData
- return trf.transform([x, y]).T
-
- xmin, xmax, ymin, ymax = self._grid_info["extremes"]
- if self.nth_coord == 0:
- xx0 = self.value
- yy0 = (ymin + ymax) / 2
- elif self.nth_coord == 1:
- xx0 = (xmin + xmax) / 2
- yy0 = self.value
- xy1, dxy1_dx, dxy1_dy = _value_and_jacobian(
- trf_xy, xx0, yy0, (xmin, xmax), (ymin, ymax))
- p = axes.transAxes.inverted().transform(xy1)
- if 0 <= p[0] <= 1 and 0 <= p[1] <= 1:
- d = [dxy1_dy, dxy1_dx][self.nth_coord]
- return xy1, np.rad2deg(np.arctan2(*d[::-1]))
- else:
- return None, None
-
- def get_tick_transform(self, axes):
- return IdentityTransform() # axes.transData
-
- def get_tick_iterators(self, axes):
- """tick_loc, tick_angle, tick_label, (optionally) tick_label"""
-
- lat_levs, lat_n, lat_factor = self._grid_info["lat_info"]
- yy0 = lat_levs / lat_factor
-
- lon_levs, lon_n, lon_factor = self._grid_info["lon_info"]
- xx0 = lon_levs / lon_factor
-
- e0, e1 = self._extremes
-
- def trf_xy(x, y):
- trf = self.grid_helper.grid_finder.get_transform() + axes.transData
- return trf.transform(np.column_stack(np.broadcast_arrays(x, y))).T
-
- # find angles
- if self.nth_coord == 0:
- mask = (e0 <= yy0) & (yy0 <= e1)
- (xx1, yy1), (dxx1, dyy1), (dxx2, dyy2) = _value_and_jacobian(
- trf_xy, self.value, yy0[mask], (-np.inf, np.inf), (e0, e1))
- labels = self._grid_info["lat_labels"]
-
- elif self.nth_coord == 1:
- mask = (e0 <= xx0) & (xx0 <= e1)
- (xx1, yy1), (dxx2, dyy2), (dxx1, dyy1) = _value_and_jacobian(
- trf_xy, xx0[mask], self.value, (-np.inf, np.inf), (e0, e1))
- labels = self._grid_info["lon_labels"]
-
- labels = [l for l, m in zip(labels, mask) if m]
-
- angle_normal = np.arctan2(dyy1, dxx1)
- angle_tangent = np.arctan2(dyy2, dxx2)
- mm = (dyy1 == 0) & (dxx1 == 0) # points with degenerate normal
- angle_normal[mm] = angle_tangent[mm] + np.pi / 2
-
- tick_to_axes = self.get_tick_transform(axes) - axes.transAxes
- in_01 = functools.partial(
- mpl.transforms._interval_contains_close, (0, 1))
-
- def f1():
- for x, y, normal, tangent, lab \
- in zip(xx1, yy1, angle_normal, angle_tangent, labels):
- c2 = tick_to_axes.transform((x, y))
- if in_01(c2[0]) and in_01(c2[1]):
- yield [x, y], *np.rad2deg([normal, tangent]), lab
-
- return f1(), iter([])
-
- def get_line_transform(self, axes):
- return axes.transData
-
- def get_line(self, axes):
- self.update_lim(axes)
- x, y = self._grid_info["line_xy"]
- return Path(np.column_stack([x, y]))
-
-
-class GridHelperCurveLinear(GridHelperBase):
- def __init__(self, aux_trans,
- extreme_finder=None,
- grid_locator1=None,
- grid_locator2=None,
- tick_formatter1=None,
- tick_formatter2=None):
- """
- Parameters
- ----------
- aux_trans : `.Transform` or tuple[Callable, Callable]
- The transform from curved coordinates to rectilinear coordinate:
- either a `.Transform` instance (which provides also its inverse),
- or a pair of callables ``(trans, inv_trans)`` that define the
- transform and its inverse. The callables should have signature::
-
- x_rect, y_rect = trans(x_curved, y_curved)
- x_curved, y_curved = inv_trans(x_rect, y_rect)
-
- extreme_finder
-
- grid_locator1, grid_locator2
- Grid locators for each axis.
-
- tick_formatter1, tick_formatter2
- Tick formatters for each axis.
- """
- super().__init__()
- self._grid_info = None
- self.grid_finder = GridFinder(aux_trans,
- extreme_finder,
- grid_locator1,
- grid_locator2,
- tick_formatter1,
- tick_formatter2)
-
- def update_grid_finder(self, aux_trans=None, **kwargs):
- if aux_trans is not None:
- self.grid_finder.update_transform(aux_trans)
- self.grid_finder.update(**kwargs)
- self._old_limits = None # Force revalidation.
-
- def new_fixed_axis(self, loc,
- nth_coord=None,
- axis_direction=None,
- offset=None,
- axes=None):
- if axes is None:
- axes = self.axes
- if axis_direction is None:
- axis_direction = loc
- helper = FixedAxisArtistHelper(self, loc, nth_coord_ticks=nth_coord)
- axisline = AxisArtist(axes, helper, axis_direction=axis_direction)
- # Why is clip not set on axisline, unlike in new_floating_axis or in
- # the floating_axig.GridHelperCurveLinear subclass?
- return axisline
-
- def new_floating_axis(self, nth_coord,
- value,
- axes=None,
- axis_direction="bottom"
- ):
- if axes is None:
- axes = self.axes
- helper = FloatingAxisArtistHelper(
- self, nth_coord, value, axis_direction)
- axisline = AxisArtist(axes, helper)
- axisline.line.set_clip_on(True)
- axisline.line.set_clip_box(axisline.axes.bbox)
- # axisline.major_ticklabels.set_visible(True)
- # axisline.minor_ticklabels.set_visible(False)
- return axisline
-
- def _update_grid(self, x1, y1, x2, y2):
- self._grid_info = self.grid_finder.get_grid_info(x1, y1, x2, y2)
-
- def get_gridlines(self, which="major", axis="both"):
- grid_lines = []
- if axis in ["both", "x"]:
- for gl in self._grid_info["lon"]["lines"]:
- grid_lines.extend(gl)
- if axis in ["both", "y"]:
- for gl in self._grid_info["lat"]["lines"]:
- grid_lines.extend(gl)
- return grid_lines
-
- def get_tick_iterator(self, nth_coord, axis_side, minor=False):
-
- # axisnr = dict(left=0, bottom=1, right=2, top=3)[axis_side]
- angle_tangent = dict(left=90, right=90, bottom=0, top=0)[axis_side]
- # angle = [0, 90, 180, 270][axisnr]
- lon_or_lat = ["lon", "lat"][nth_coord]
- if not minor: # major ticks
- for (xy, a), l in zip(
- self._grid_info[lon_or_lat]["tick_locs"][axis_side],
- self._grid_info[lon_or_lat]["tick_labels"][axis_side]):
- angle_normal = a
- yield xy, angle_normal, angle_tangent, l
- else:
- for (xy, a), l in zip(
- self._grid_info[lon_or_lat]["tick_locs"][axis_side],
- self._grid_info[lon_or_lat]["tick_labels"][axis_side]):
- angle_normal = a
- yield xy, angle_normal, angle_tangent, ""
- # for xy, a, l in self._grid_info[lon_or_lat]["ticks"][axis_side]:
- # yield xy, a, ""
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/parasite_axes.py b/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/parasite_axes.py
deleted file mode 100644
index 4ebd6acc03b..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/axisartist/parasite_axes.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from mpl_toolkits.axes_grid1.parasite_axes import (
- host_axes_class_factory, parasite_axes_class_factory)
-from .axislines import Axes
-
-
-ParasiteAxes = parasite_axes_class_factory(Axes)
-HostAxes = SubplotHost = host_axes_class_factory(Axes)
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/__init__.py b/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/__init__.py
deleted file mode 100644
index a089fbd6b70..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .axes3d import Axes3D
-
-__all__ = ['Axes3D']
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/art3d.py b/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/art3d.py
deleted file mode 100644
index 4aff115b0c9..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/art3d.py
+++ /dev/null
@@ -1,1252 +0,0 @@
-# art3d.py, original mplot3d version by John Porter
-# Parts rewritten by Reinier Heeres <reinier@heeres.eu>
-# Minor additions by Ben Axelrod <baxelrod@coroware.com>
-
-"""
-Module containing 3D artist code and functions to convert 2D
-artists into 3D versions which can be added to an Axes3D.
-"""
-
-import math
-
-import numpy as np
-
-from contextlib import contextmanager
-
-from matplotlib import (
- artist, cbook, colors as mcolors, lines, text as mtext,
- path as mpath)
-from matplotlib.collections import (
- Collection, LineCollection, PolyCollection, PatchCollection, PathCollection)
-from matplotlib.colors import Normalize
-from matplotlib.patches import Patch
-from . import proj3d
-
-
-def _norm_angle(a):
- """Return the given angle normalized to -180 < *a* <= 180 degrees."""
- a = (a + 360) % 360
- if a > 180:
- a = a - 360
- return a
-
-
-def _norm_text_angle(a):
- """Return the given angle normalized to -90 < *a* <= 90 degrees."""
- a = (a + 180) % 180
- if a > 90:
- a = a - 180
- return a
-
-
-def get_dir_vector(zdir):
- """
- Return a direction vector.
-
- Parameters
- ----------
- zdir : {'x', 'y', 'z', None, 3-tuple}
- The direction. Possible values are:
-
- - 'x': equivalent to (1, 0, 0)
- - 'y': equivalent to (0, 1, 0)
- - 'z': equivalent to (0, 0, 1)
- - *None*: equivalent to (0, 0, 0)
- - an iterable (x, y, z) is converted to an array
-
- Returns
- -------
- x, y, z : array
- The direction vector.
- """
- if zdir == 'x':
- return np.array((1, 0, 0))
- elif zdir == 'y':
- return np.array((0, 1, 0))
- elif zdir == 'z':
- return np.array((0, 0, 1))
- elif zdir is None:
- return np.array((0, 0, 0))
- elif np.iterable(zdir) and len(zdir) == 3:
- return np.array(zdir)
- else:
- raise ValueError("'x', 'y', 'z', None or vector of length 3 expected")
-
-
-class Text3D(mtext.Text):
- """
- Text object with 3D position and direction.
-
- Parameters
- ----------
- x, y, z : float
- The position of the text.
- text : str
- The text string to display.
- zdir : {'x', 'y', 'z', None, 3-tuple}
- The direction of the text. See `.get_dir_vector` for a description of
- the values.
-
- Other Parameters
- ----------------
- **kwargs
- All other parameters are passed on to `~matplotlib.text.Text`.
- """
-
- def __init__(self, x=0, y=0, z=0, text='', zdir='z', **kwargs):
- mtext.Text.__init__(self, x, y, text, **kwargs)
- self.set_3d_properties(z, zdir)
-
- def get_position_3d(self):
- """Return the (x, y, z) position of the text."""
- return self._x, self._y, self._z
-
- def set_position_3d(self, xyz, zdir=None):
- """
- Set the (*x*, *y*, *z*) position of the text.
-
- Parameters
- ----------
- xyz : (float, float, float)
- The position in 3D space.
- zdir : {'x', 'y', 'z', None, 3-tuple}
- The direction of the text. If unspecified, the *zdir* will not be
- changed. See `.get_dir_vector` for a description of the values.
- """
- super().set_position(xyz[:2])
- self.set_z(xyz[2])
- if zdir is not None:
- self._dir_vec = get_dir_vector(zdir)
-
- def set_z(self, z):
- """
- Set the *z* position of the text.
-
- Parameters
- ----------
- z : float
- """
- self._z = z
- self.stale = True
-
- def set_3d_properties(self, z=0, zdir='z'):
- """
- Set the *z* position and direction of the text.
-
- Parameters
- ----------
- z : float
- The z-position in 3D space.
- zdir : {'x', 'y', 'z', 3-tuple}
- The direction of the text. Default: 'z'.
- See `.get_dir_vector` for a description of the values.
- """
- self._z = z
- self._dir_vec = get_dir_vector(zdir)
- self.stale = True
-
- @artist.allow_rasterization
- def draw(self, renderer):
- position3d = np.array((self._x, self._y, self._z))
- proj = proj3d._proj_trans_points(
- [position3d, position3d + self._dir_vec], self.axes.M)
- dx = proj[0][1] - proj[0][0]
- dy = proj[1][1] - proj[1][0]
- angle = math.degrees(math.atan2(dy, dx))
- with cbook._setattr_cm(self, _x=proj[0][0], _y=proj[1][0],
- _rotation=_norm_text_angle(angle)):
- mtext.Text.draw(self, renderer)
- self.stale = False
-
- def get_tightbbox(self, renderer=None):
- # Overwriting the 2d Text behavior which is not valid for 3d.
- # For now, just return None to exclude from layout calculation.
- return None
-
-
-def text_2d_to_3d(obj, z=0, zdir='z'):
- """
- Convert a `.Text` to a `.Text3D` object.
-
- Parameters
- ----------
- z : float
- The z-position in 3D space.
- zdir : {'x', 'y', 'z', 3-tuple}
- The direction of the text. Default: 'z'.
- See `.get_dir_vector` for a description of the values.
- """
- obj.__class__ = Text3D
- obj.set_3d_properties(z, zdir)
-
-
-class Line3D(lines.Line2D):
- """
- 3D line object.
-
- .. note:: Use `get_data_3d` to obtain the data associated with the line.
- `~.Line2D.get_data`, `~.Line2D.get_xdata`, and `~.Line2D.get_ydata` return
- the x- and y-coordinates of the projected 2D-line, not the x- and y-data of
- the 3D-line. Similarly, use `set_data_3d` to set the data, not
- `~.Line2D.set_data`, `~.Line2D.set_xdata`, and `~.Line2D.set_ydata`.
- """
-
- def __init__(self, xs, ys, zs, *args, **kwargs):
- """
-
- Parameters
- ----------
- xs : array-like
- The x-data to be plotted.
- ys : array-like
- The y-data to be plotted.
- zs : array-like
- The z-data to be plotted.
- *args, **kwargs
- Additional arguments are passed to `~matplotlib.lines.Line2D`.
- """
- super().__init__([], [], *args, **kwargs)
- self.set_data_3d(xs, ys, zs)
-
- def set_3d_properties(self, zs=0, zdir='z'):
- """
- Set the *z* position and direction of the line.
-
- Parameters
- ----------
- zs : float or array of floats
- The location along the *zdir* axis in 3D space to position the
- line.
- zdir : {'x', 'y', 'z'}
- Plane to plot line orthogonal to. Default: 'z'.
- See `.get_dir_vector` for a description of the values.
- """
- xs = self.get_xdata()
- ys = self.get_ydata()
- zs = cbook._to_unmasked_float_array(zs).ravel()
- zs = np.broadcast_to(zs, len(xs))
- self._verts3d = juggle_axes(xs, ys, zs, zdir)
- self.stale = True
-
- def set_data_3d(self, *args):
- """
- Set the x, y and z data
-
- Parameters
- ----------
- x : array-like
- The x-data to be plotted.
- y : array-like
- The y-data to be plotted.
- z : array-like
- The z-data to be plotted.
-
- Notes
- -----
- Accepts x, y, z arguments or a single array-like (x, y, z)
- """
- if len(args) == 1:
- args = args[0]
- for name, xyz in zip('xyz', args):
- if not np.iterable(xyz):
- raise RuntimeError(f'{name} must be a sequence')
- self._verts3d = args
- self.stale = True
-
- def get_data_3d(self):
- """
- Get the current data
-
- Returns
- -------
- verts3d : length-3 tuple or array-like
- The current data as a tuple or array-like.
- """
- return self._verts3d
-
- @artist.allow_rasterization
- def draw(self, renderer):
- xs3d, ys3d, zs3d = self._verts3d
- xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, self.axes.M)
- self.set_data(xs, ys)
- super().draw(renderer)
- self.stale = False
-
-
-def line_2d_to_3d(line, zs=0, zdir='z'):
- """
- Convert a `.Line2D` to a `.Line3D` object.
-
- Parameters
- ----------
- zs : float
- The location along the *zdir* axis in 3D space to position the line.
- zdir : {'x', 'y', 'z'}
- Plane to plot line orthogonal to. Default: 'z'.
- See `.get_dir_vector` for a description of the values.
- """
-
- line.__class__ = Line3D
- line.set_3d_properties(zs, zdir)
-
-
-def _path_to_3d_segment(path, zs=0, zdir='z'):
- """Convert a path to a 3D segment."""
-
- zs = np.broadcast_to(zs, len(path))
- pathsegs = path.iter_segments(simplify=False, curves=False)
- seg = [(x, y, z) for (((x, y), code), z) in zip(pathsegs, zs)]
- seg3d = [juggle_axes(x, y, z, zdir) for (x, y, z) in seg]
- return seg3d
-
-
-def _paths_to_3d_segments(paths, zs=0, zdir='z'):
- """Convert paths from a collection object to 3D segments."""
-
- if not np.iterable(zs):
- zs = np.broadcast_to(zs, len(paths))
- else:
- if len(zs) != len(paths):
- raise ValueError('Number of z-coordinates does not match paths.')
-
- segs = [_path_to_3d_segment(path, pathz, zdir)
- for path, pathz in zip(paths, zs)]
- return segs
-
-
-def _path_to_3d_segment_with_codes(path, zs=0, zdir='z'):
- """Convert a path to a 3D segment with path codes."""
-
- zs = np.broadcast_to(zs, len(path))
- pathsegs = path.iter_segments(simplify=False, curves=False)
- seg_codes = [((x, y, z), code) for ((x, y), code), z in zip(pathsegs, zs)]
- if seg_codes:
- seg, codes = zip(*seg_codes)
- seg3d = [juggle_axes(x, y, z, zdir) for (x, y, z) in seg]
- else:
- seg3d = []
- codes = []
- return seg3d, list(codes)
-
-
-def _paths_to_3d_segments_with_codes(paths, zs=0, zdir='z'):
- """
- Convert paths from a collection object to 3D segments with path codes.
- """
-
- zs = np.broadcast_to(zs, len(paths))
- segments_codes = [_path_to_3d_segment_with_codes(path, pathz, zdir)
- for path, pathz in zip(paths, zs)]
- if segments_codes:
- segments, codes = zip(*segments_codes)
- else:
- segments, codes = [], []
- return list(segments), list(codes)
-
-
-class Collection3D(Collection):
- """A collection of 3D paths."""
-
- def do_3d_projection(self):
- """Project the points according to renderer matrix."""
- xyzs_list = [proj3d.proj_transform(*vs.T, self.axes.M)
- for vs, _ in self._3dverts_codes]
- self._paths = [mpath.Path(np.column_stack([xs, ys]), cs)
- for (xs, ys, _), (_, cs) in zip(xyzs_list, self._3dverts_codes)]
- zs = np.concatenate([zs for _, _, zs in xyzs_list])
- return zs.min() if len(zs) else 1e9
-
-
-def collection_2d_to_3d(col, zs=0, zdir='z'):
- """Convert a `.Collection` to a `.Collection3D` object."""
- zs = np.broadcast_to(zs, len(col.get_paths()))
- col._3dverts_codes = [
- (np.column_stack(juggle_axes(
- *np.column_stack([p.vertices, np.broadcast_to(z, len(p.vertices))]).T,
- zdir)),
- p.codes)
- for p, z in zip(col.get_paths(), zs)]
- col.__class__ = cbook._make_class_factory(Collection3D, "{}3D")(type(col))
-
-
-class Line3DCollection(LineCollection):
- """
- A collection of 3D lines.
- """
-
- def set_sort_zpos(self, val):
- """Set the position to use for z-sorting."""
- self._sort_zpos = val
- self.stale = True
-
- def set_segments(self, segments):
- """
- Set 3D segments.
- """
- self._segments3d = segments
- super().set_segments([])
-
- def do_3d_projection(self):
- """
- Project the points according to renderer matrix.
- """
- xyslist = [proj3d._proj_trans_points(points, self.axes.M)
- for points in self._segments3d]
- segments_2d = [np.column_stack([xs, ys]) for xs, ys, zs in xyslist]
- LineCollection.set_segments(self, segments_2d)
-
- # FIXME
- minz = 1e9
- for xs, ys, zs in xyslist:
- minz = min(minz, min(zs))
- return minz
-
-
-def line_collection_2d_to_3d(col, zs=0, zdir='z'):
- """Convert a `.LineCollection` to a `.Line3DCollection` object."""
- segments3d = _paths_to_3d_segments(col.get_paths(), zs, zdir)
- col.__class__ = Line3DCollection
- col.set_segments(segments3d)
-
-
-class Patch3D(Patch):
- """
- 3D patch object.
- """
-
- def __init__(self, *args, zs=(), zdir='z', **kwargs):
- """
- Parameters
- ----------
- verts :
- zs : float
- The location along the *zdir* axis in 3D space to position the
- patch.
- zdir : {'x', 'y', 'z'}
- Plane to plot patch orthogonal to. Default: 'z'.
- See `.get_dir_vector` for a description of the values.
- """
- super().__init__(*args, **kwargs)
- self.set_3d_properties(zs, zdir)
-
- def set_3d_properties(self, verts, zs=0, zdir='z'):
- """
- Set the *z* position and direction of the patch.
-
- Parameters
- ----------
- verts :
- zs : float
- The location along the *zdir* axis in 3D space to position the
- patch.
- zdir : {'x', 'y', 'z'}
- Plane to plot patch orthogonal to. Default: 'z'.
- See `.get_dir_vector` for a description of the values.
- """
- zs = np.broadcast_to(zs, len(verts))
- self._segment3d = [juggle_axes(x, y, z, zdir)
- for ((x, y), z) in zip(verts, zs)]
-
- def get_path(self):
- return self._path2d
-
- def do_3d_projection(self):
- s = self._segment3d
- xs, ys, zs = zip(*s)
- vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs,
- self.axes.M)
- self._path2d = mpath.Path(np.column_stack([vxs, vys]))
- return min(vzs)
-
-
-class PathPatch3D(Patch3D):
- """
- 3D PathPatch object.
- """
-
- def __init__(self, path, *, zs=(), zdir='z', **kwargs):
- """
- Parameters
- ----------
- path :
- zs : float
- The location along the *zdir* axis in 3D space to position the
- path patch.
- zdir : {'x', 'y', 'z', 3-tuple}
- Plane to plot path patch orthogonal to. Default: 'z'.
- See `.get_dir_vector` for a description of the values.
- """
- # Not super().__init__!
- Patch.__init__(self, **kwargs)
- self.set_3d_properties(path, zs, zdir)
-
- def set_3d_properties(self, path, zs=0, zdir='z'):
- """
- Set the *z* position and direction of the path patch.
-
- Parameters
- ----------
- path :
- zs : float
- The location along the *zdir* axis in 3D space to position the
- path patch.
- zdir : {'x', 'y', 'z', 3-tuple}
- Plane to plot path patch orthogonal to. Default: 'z'.
- See `.get_dir_vector` for a description of the values.
- """
- Patch3D.set_3d_properties(self, path.vertices, zs=zs, zdir=zdir)
- self._code3d = path.codes
-
- def do_3d_projection(self):
- s = self._segment3d
- xs, ys, zs = zip(*s)
- vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs,
- self.axes.M)
- self._path2d = mpath.Path(np.column_stack([vxs, vys]), self._code3d)
- return min(vzs)
-
-
-def _get_patch_verts(patch):
- """Return a list of vertices for the path of a patch."""
- trans = patch.get_patch_transform()
- path = patch.get_path()
- polygons = path.to_polygons(trans)
- return polygons[0] if len(polygons) else np.array([])
-
-
-def patch_2d_to_3d(patch, z=0, zdir='z'):
- """Convert a `.Patch` to a `.Patch3D` object."""
- verts = _get_patch_verts(patch)
- patch.__class__ = Patch3D
- patch.set_3d_properties(verts, z, zdir)
-
-
-def pathpatch_2d_to_3d(pathpatch, z=0, zdir='z'):
- """Convert a `.PathPatch` to a `.PathPatch3D` object."""
- path = pathpatch.get_path()
- trans = pathpatch.get_patch_transform()
-
- mpath = trans.transform_path(path)
- pathpatch.__class__ = PathPatch3D
- pathpatch.set_3d_properties(mpath, z, zdir)
-
-
-class Patch3DCollection(PatchCollection):
- """
- A collection of 3D patches.
- """
-
- def __init__(self, *args, zs=0, zdir='z', depthshade=True, **kwargs):
- """
- Create a collection of flat 3D patches with its normal vector
- pointed in *zdir* direction, and located at *zs* on the *zdir*
- axis. 'zs' can be a scalar or an array-like of the same length as
- the number of patches in the collection.
-
- Constructor arguments are the same as for
- :class:`~matplotlib.collections.PatchCollection`. In addition,
- keywords *zs=0* and *zdir='z'* are available.
-
- Also, the keyword argument *depthshade* is available to indicate
- whether to shade the patches in order to give the appearance of depth
- (default is *True*). This is typically desired in scatter plots.
- """
- self._depthshade = depthshade
- super().__init__(*args, **kwargs)
- self.set_3d_properties(zs, zdir)
-
- def get_depthshade(self):
- return self._depthshade
-
- def set_depthshade(self, depthshade):
- """
- Set whether depth shading is performed on collection members.
-
- Parameters
- ----------
- depthshade : bool
- Whether to shade the patches in order to give the appearance of
- depth.
- """
- self._depthshade = depthshade
- self.stale = True
-
- def set_sort_zpos(self, val):
- """Set the position to use for z-sorting."""
- self._sort_zpos = val
- self.stale = True
-
- def set_3d_properties(self, zs, zdir):
- """
- Set the *z* positions and direction of the patches.
-
- Parameters
- ----------
- zs : float or array of floats
- The location or locations to place the patches in the collection
- along the *zdir* axis.
- zdir : {'x', 'y', 'z'}
- Plane to plot patches orthogonal to.
- All patches must have the same direction.
- See `.get_dir_vector` for a description of the values.
- """
- # Force the collection to initialize the face and edgecolors
- # just in case it is a scalarmappable with a colormap.
- self.update_scalarmappable()
- offsets = self.get_offsets()
- if len(offsets) > 0:
- xs, ys = offsets.T
- else:
- xs = []
- ys = []
- self._offsets3d = juggle_axes(xs, ys, np.atleast_1d(zs), zdir)
- self._z_markers_idx = slice(-1)
- self._vzs = None
- self.stale = True
-
- def do_3d_projection(self):
- xs, ys, zs = self._offsets3d
- vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs,
- self.axes.M)
- self._vzs = vzs
- super().set_offsets(np.column_stack([vxs, vys]))
-
- if vzs.size > 0:
- return min(vzs)
- else:
- return np.nan
-
- def _maybe_depth_shade_and_sort_colors(self, color_array):
- color_array = (
- _zalpha(color_array, self._vzs)
- if self._vzs is not None and self._depthshade
- else color_array
- )
- if len(color_array) > 1:
- color_array = color_array[self._z_markers_idx]
- return mcolors.to_rgba_array(color_array, self._alpha)
-
- def get_facecolor(self):
- return self._maybe_depth_shade_and_sort_colors(super().get_facecolor())
-
- def get_edgecolor(self):
- # We need this check here to make sure we do not double-apply the depth
- # based alpha shading when the edge color is "face" which means the
- # edge colour should be identical to the face colour.
- if cbook._str_equal(self._edgecolors, 'face'):
- return self.get_facecolor()
- return self._maybe_depth_shade_and_sort_colors(super().get_edgecolor())
-
-
-class Path3DCollection(PathCollection):
- """
- A collection of 3D paths.
- """
-
- def __init__(self, *args, zs=0, zdir='z', depthshade=True, **kwargs):
- """
- Create a collection of flat 3D paths with its normal vector
- pointed in *zdir* direction, and located at *zs* on the *zdir*
- axis. 'zs' can be a scalar or an array-like of the same length as
- the number of paths in the collection.
-
- Constructor arguments are the same as for
- :class:`~matplotlib.collections.PathCollection`. In addition,
- keywords *zs=0* and *zdir='z'* are available.
-
- Also, the keyword argument *depthshade* is available to indicate
- whether to shade the patches in order to give the appearance of depth
- (default is *True*). This is typically desired in scatter plots.
- """
- self._depthshade = depthshade
- self._in_draw = False
- super().__init__(*args, **kwargs)
- self.set_3d_properties(zs, zdir)
- self._offset_zordered = None
-
- def draw(self, renderer):
- with self._use_zordered_offset():
- with cbook._setattr_cm(self, _in_draw=True):
- super().draw(renderer)
-
- def set_sort_zpos(self, val):
- """Set the position to use for z-sorting."""
- self._sort_zpos = val
- self.stale = True
-
- def set_3d_properties(self, zs, zdir):
- """
- Set the *z* positions and direction of the paths.
-
- Parameters
- ----------
- zs : float or array of floats
- The location or locations to place the paths in the collection
- along the *zdir* axis.
- zdir : {'x', 'y', 'z'}
- Plane to plot paths orthogonal to.
- All paths must have the same direction.
- See `.get_dir_vector` for a description of the values.
- """
- # Force the collection to initialize the face and edgecolors
- # just in case it is a scalarmappable with a colormap.
- self.update_scalarmappable()
- offsets = self.get_offsets()
- if len(offsets) > 0:
- xs, ys = offsets.T
- else:
- xs = []
- ys = []
- self._offsets3d = juggle_axes(xs, ys, np.atleast_1d(zs), zdir)
- # In the base draw methods we access the attributes directly which
- # means we cannot resolve the shuffling in the getter methods like
- # we do for the edge and face colors.
- #
- # This means we need to carry around a cache of the unsorted sizes and
- # widths (postfixed with 3d) and in `do_3d_projection` set the
- # depth-sorted version of that data into the private state used by the
- # base collection class in its draw method.
- #
- # Grab the current sizes and linewidths to preserve them.
- self._sizes3d = self._sizes
- self._linewidths3d = np.array(self._linewidths)
- xs, ys, zs = self._offsets3d
-
- # Sort the points based on z coordinates
- # Performance optimization: Create a sorted index array and reorder
- # points and point properties according to the index array
- self._z_markers_idx = slice(-1)
- self._vzs = None
- self.stale = True
-
- def set_sizes(self, sizes, dpi=72.0):
- super().set_sizes(sizes, dpi)
- if not self._in_draw:
- self._sizes3d = sizes
-
- def set_linewidth(self, lw):
- super().set_linewidth(lw)
- if not self._in_draw:
- self._linewidths3d = np.array(self._linewidths)
-
- def get_depthshade(self):
- return self._depthshade
-
- def set_depthshade(self, depthshade):
- """
- Set whether depth shading is performed on collection members.
-
- Parameters
- ----------
- depthshade : bool
- Whether to shade the patches in order to give the appearance of
- depth.
- """
- self._depthshade = depthshade
- self.stale = True
-
- def do_3d_projection(self):
- xs, ys, zs = self._offsets3d
- vxs, vys, vzs, vis = proj3d.proj_transform_clip(xs, ys, zs,
- self.axes.M)
- # Sort the points based on z coordinates
- # Performance optimization: Create a sorted index array and reorder
- # points and point properties according to the index array
- z_markers_idx = self._z_markers_idx = np.argsort(vzs)[::-1]
- self._vzs = vzs
-
- # we have to special case the sizes because of code in collections.py
- # as the draw method does
- # self.set_sizes(self._sizes, self.figure.dpi)
- # so we cannot rely on doing the sorting on the way out via get_*
-
- if len(self._sizes3d) > 1:
- self._sizes = self._sizes3d[z_markers_idx]
-
- if len(self._linewidths3d) > 1:
- self._linewidths = self._linewidths3d[z_markers_idx]
-
- PathCollection.set_offsets(self, np.column_stack((vxs, vys)))
-
- # Re-order items
- vzs = vzs[z_markers_idx]
- vxs = vxs[z_markers_idx]
- vys = vys[z_markers_idx]
-
- # Store ordered offset for drawing purpose
- self._offset_zordered = np.column_stack((vxs, vys))
-
- return np.min(vzs) if vzs.size else np.nan
-
- @contextmanager
- def _use_zordered_offset(self):
- if self._offset_zordered is None:
- # Do nothing
- yield
- else:
- # Swap offset with z-ordered offset
- old_offset = self._offsets
- super().set_offsets(self._offset_zordered)
- try:
- yield
- finally:
- self._offsets = old_offset
-
- def _maybe_depth_shade_and_sort_colors(self, color_array):
- color_array = (
- _zalpha(color_array, self._vzs)
- if self._vzs is not None and self._depthshade
- else color_array
- )
- if len(color_array) > 1:
- color_array = color_array[self._z_markers_idx]
- return mcolors.to_rgba_array(color_array, self._alpha)
-
- def get_facecolor(self):
- return self._maybe_depth_shade_and_sort_colors(super().get_facecolor())
-
- def get_edgecolor(self):
- # We need this check here to make sure we do not double-apply the depth
- # based alpha shading when the edge color is "face" which means the
- # edge colour should be identical to the face colour.
- if cbook._str_equal(self._edgecolors, 'face'):
- return self.get_facecolor()
- return self._maybe_depth_shade_and_sort_colors(super().get_edgecolor())
-
-
-def patch_collection_2d_to_3d(col, zs=0, zdir='z', depthshade=True):
- """
- Convert a `.PatchCollection` into a `.Patch3DCollection` object
- (or a `.PathCollection` into a `.Path3DCollection` object).
-
- Parameters
- ----------
- zs : float or array of floats
- The location or locations to place the patches in the collection along
- the *zdir* axis. Default: 0.
- zdir : {'x', 'y', 'z'}
- The axis in which to place the patches. Default: "z".
- See `.get_dir_vector` for a description of the values.
- depthshade
- Whether to shade the patches to give a sense of depth. Default: *True*.
-
- """
- if isinstance(col, PathCollection):
- col.__class__ = Path3DCollection
- col._offset_zordered = None
- elif isinstance(col, PatchCollection):
- col.__class__ = Patch3DCollection
- col._depthshade = depthshade
- col._in_draw = False
- col.set_3d_properties(zs, zdir)
-
-
-class Poly3DCollection(PolyCollection):
- """
- A collection of 3D polygons.
-
- .. note::
- **Filling of 3D polygons**
-
- There is no simple definition of the enclosed surface of a 3D polygon
- unless the polygon is planar.
-
- In practice, Matplotlib fills the 2D projection of the polygon. This
- gives a correct filling appearance only for planar polygons. For all
- other polygons, you'll find orientations in which the edges of the
- polygon intersect in the projection. This will lead to an incorrect
- visualization of the 3D area.
-
- If you need filled areas, it is recommended to create them via
- `~mpl_toolkits.mplot3d.axes3d.Axes3D.plot_trisurf`, which creates a
- triangulation and thus generates consistent surfaces.
- """
-
- def __init__(self, verts, *args, zsort='average', shade=False,
- lightsource=None, **kwargs):
- """
- Parameters
- ----------
- verts : list of (N, 3) array-like
- The sequence of polygons [*verts0*, *verts1*, ...] where each
- element *verts_i* defines the vertices of polygon *i* as a 2D
- array-like of shape (N, 3).
- zsort : {'average', 'min', 'max'}, default: 'average'
- The calculation method for the z-order.
- See `~.Poly3DCollection.set_zsort` for details.
- shade : bool, default: False
- Whether to shade *facecolors* and *edgecolors*. When activating
- *shade*, *facecolors* and/or *edgecolors* must be provided.
-
- .. versionadded:: 3.7
-
- lightsource : `~matplotlib.colors.LightSource`, optional
- The lightsource to use when *shade* is True.
-
- .. versionadded:: 3.7
-
- *args, **kwargs
- All other parameters are forwarded to `.PolyCollection`.
-
- Notes
- -----
- Note that this class does a bit of magic with the _facecolors
- and _edgecolors properties.
- """
- if shade:
- normals = _generate_normals(verts)
- facecolors = kwargs.get('facecolors', None)
- if facecolors is not None:
- kwargs['facecolors'] = _shade_colors(
- facecolors, normals, lightsource
- )
-
- edgecolors = kwargs.get('edgecolors', None)
- if edgecolors is not None:
- kwargs['edgecolors'] = _shade_colors(
- edgecolors, normals, lightsource
- )
- if facecolors is None and edgecolors is None:
- raise ValueError(
- "You must provide facecolors, edgecolors, or both for "
- "shade to work.")
- super().__init__(verts, *args, **kwargs)
- if isinstance(verts, np.ndarray):
- if verts.ndim != 3:
- raise ValueError('verts must be a list of (N, 3) array-like')
- else:
- if any(len(np.shape(vert)) != 2 for vert in verts):
- raise ValueError('verts must be a list of (N, 3) array-like')
- self.set_zsort(zsort)
- self._codes3d = None
-
- _zsort_functions = {
- 'average': np.average,
- 'min': np.min,
- 'max': np.max,
- }
-
- def set_zsort(self, zsort):
- """
- Set the calculation method for the z-order.
-
- Parameters
- ----------
- zsort : {'average', 'min', 'max'}
- The function applied on the z-coordinates of the vertices in the
- viewer's coordinate system, to determine the z-order.
- """
- self._zsortfunc = self._zsort_functions[zsort]
- self._sort_zpos = None
- self.stale = True
-
- def get_vector(self, segments3d):
- """Optimize points for projection."""
- if len(segments3d):
- xs, ys, zs = np.vstack(segments3d).T
- else: # vstack can't stack zero arrays.
- xs, ys, zs = [], [], []
- ones = np.ones(len(xs))
- self._vec = np.array([xs, ys, zs, ones])
-
- indices = [0, *np.cumsum([len(segment) for segment in segments3d])]
- self._segslices = [*map(slice, indices[:-1], indices[1:])]
-
- def set_verts(self, verts, closed=True):
- """
- Set 3D vertices.
-
- Parameters
- ----------
- verts : list of (N, 3) array-like
- The sequence of polygons [*verts0*, *verts1*, ...] where each
- element *verts_i* defines the vertices of polygon *i* as a 2D
- array-like of shape (N, 3).
- closed : bool, default: True
- Whether the polygon should be closed by adding a CLOSEPOLY
- connection at the end.
- """
- self.get_vector(verts)
- # 2D verts will be updated at draw time
- super().set_verts([], False)
- self._closed = closed
-
- def set_verts_and_codes(self, verts, codes):
- """Set 3D vertices with path codes."""
- # set vertices with closed=False to prevent PolyCollection from
- # setting path codes
- self.set_verts(verts, closed=False)
- # and set our own codes instead.
- self._codes3d = codes
-
- def set_3d_properties(self):
- # Force the collection to initialize the face and edgecolors
- # just in case it is a scalarmappable with a colormap.
- self.update_scalarmappable()
- self._sort_zpos = None
- self.set_zsort('average')
- self._facecolor3d = PolyCollection.get_facecolor(self)
- self._edgecolor3d = PolyCollection.get_edgecolor(self)
- self._alpha3d = PolyCollection.get_alpha(self)
- self.stale = True
-
- def set_sort_zpos(self, val):
- """Set the position to use for z-sorting."""
- self._sort_zpos = val
- self.stale = True
-
- def do_3d_projection(self):
- """
- Perform the 3D projection for this object.
- """
- if self._A is not None:
- # force update of color mapping because we re-order them
- # below. If we do not do this here, the 2D draw will call
- # this, but we will never port the color mapped values back
- # to the 3D versions.
- #
- # We hold the 3D versions in a fixed order (the order the user
- # passed in) and sort the 2D version by view depth.
- self.update_scalarmappable()
- if self._face_is_mapped:
- self._facecolor3d = self._facecolors
- if self._edge_is_mapped:
- self._edgecolor3d = self._edgecolors
- txs, tys, tzs = proj3d._proj_transform_vec(self._vec, self.axes.M)
- xyzlist = [(txs[sl], tys[sl], tzs[sl]) for sl in self._segslices]
-
- # This extra fuss is to re-order face / edge colors
- cface = self._facecolor3d
- cedge = self._edgecolor3d
- if len(cface) != len(xyzlist):
- cface = cface.repeat(len(xyzlist), axis=0)
- if len(cedge) != len(xyzlist):
- if len(cedge) == 0:
- cedge = cface
- else:
- cedge = cedge.repeat(len(xyzlist), axis=0)
-
- if xyzlist:
- # sort by depth (furthest drawn first)
- z_segments_2d = sorted(
- ((self._zsortfunc(zs), np.column_stack([xs, ys]), fc, ec, idx)
- for idx, ((xs, ys, zs), fc, ec)
- in enumerate(zip(xyzlist, cface, cedge))),
- key=lambda x: x[0], reverse=True)
-
- _, segments_2d, self._facecolors2d, self._edgecolors2d, idxs = \
- zip(*z_segments_2d)
- else:
- segments_2d = []
- self._facecolors2d = np.empty((0, 4))
- self._edgecolors2d = np.empty((0, 4))
- idxs = []
-
- if self._codes3d is not None:
- codes = [self._codes3d[idx] for idx in idxs]
- PolyCollection.set_verts_and_codes(self, segments_2d, codes)
- else:
- PolyCollection.set_verts(self, segments_2d, self._closed)
-
- if len(self._edgecolor3d) != len(cface):
- self._edgecolors2d = self._edgecolor3d
-
- # Return zorder value
- if self._sort_zpos is not None:
- zvec = np.array([[0], [0], [self._sort_zpos], [1]])
- ztrans = proj3d._proj_transform_vec(zvec, self.axes.M)
- return ztrans[2][0]
- elif tzs.size > 0:
- # FIXME: Some results still don't look quite right.
- # In particular, examine contourf3d_demo2.py
- # with az = -54 and elev = -45.
- return np.min(tzs)
- else:
- return np.nan
-
- def set_facecolor(self, colors):
- # docstring inherited
- super().set_facecolor(colors)
- self._facecolor3d = PolyCollection.get_facecolor(self)
-
- def set_edgecolor(self, colors):
- # docstring inherited
- super().set_edgecolor(colors)
- self._edgecolor3d = PolyCollection.get_edgecolor(self)
-
- def set_alpha(self, alpha):
- # docstring inherited
- artist.Artist.set_alpha(self, alpha)
- try:
- self._facecolor3d = mcolors.to_rgba_array(
- self._facecolor3d, self._alpha)
- except (AttributeError, TypeError, IndexError):
- pass
- try:
- self._edgecolors = mcolors.to_rgba_array(
- self._edgecolor3d, self._alpha)
- except (AttributeError, TypeError, IndexError):
- pass
- self.stale = True
-
- def get_facecolor(self):
- # docstring inherited
- # self._facecolors2d is not initialized until do_3d_projection
- if not hasattr(self, '_facecolors2d'):
- self.axes.M = self.axes.get_proj()
- self.do_3d_projection()
- return np.asarray(self._facecolors2d)
-
- def get_edgecolor(self):
- # docstring inherited
- # self._edgecolors2d is not initialized until do_3d_projection
- if not hasattr(self, '_edgecolors2d'):
- self.axes.M = self.axes.get_proj()
- self.do_3d_projection()
- return np.asarray(self._edgecolors2d)
-
-
-def poly_collection_2d_to_3d(col, zs=0, zdir='z'):
- """
- Convert a `.PolyCollection` into a `.Poly3DCollection` object.
-
- Parameters
- ----------
- zs : float or array of floats
- The location or locations to place the polygons in the collection along
- the *zdir* axis. Default: 0.
- zdir : {'x', 'y', 'z'}
- The axis in which to place the patches. Default: 'z'.
- See `.get_dir_vector` for a description of the values.
- """
- segments_3d, codes = _paths_to_3d_segments_with_codes(
- col.get_paths(), zs, zdir)
- col.__class__ = Poly3DCollection
- col.set_verts_and_codes(segments_3d, codes)
- col.set_3d_properties()
-
-
-def juggle_axes(xs, ys, zs, zdir):
- """
- Reorder coordinates so that 2D *xs*, *ys* can be plotted in the plane
- orthogonal to *zdir*. *zdir* is normally 'x', 'y' or 'z'. However, if
- *zdir* starts with a '-' it is interpreted as a compensation for
- `rotate_axes`.
- """
- if zdir == 'x':
- return zs, xs, ys
- elif zdir == 'y':
- return xs, zs, ys
- elif zdir[0] == '-':
- return rotate_axes(xs, ys, zs, zdir)
- else:
- return xs, ys, zs
-
-
-def rotate_axes(xs, ys, zs, zdir):
- """
- Reorder coordinates so that the axes are rotated with *zdir* along
- the original z axis. Prepending the axis with a '-' does the
- inverse transform, so *zdir* can be 'x', '-x', 'y', '-y', 'z' or '-z'.
- """
- if zdir in ('x', '-y'):
- return ys, zs, xs
- elif zdir in ('-x', 'y'):
- return zs, xs, ys
- else:
- return xs, ys, zs
-
-
-def _zalpha(colors, zs):
- """Modify the alphas of the color list according to depth."""
- # FIXME: This only works well if the points for *zs* are well-spaced
- # in all three dimensions. Otherwise, at certain orientations,
- # the min and max zs are very close together.
- # Should really normalize against the viewing depth.
- if len(colors) == 0 or len(zs) == 0:
- return np.zeros((0, 4))
- norm = Normalize(min(zs), max(zs))
- sats = 1 - norm(zs) * 0.7
- rgba = np.broadcast_to(mcolors.to_rgba_array(colors), (len(zs), 4))
- return np.column_stack([rgba[:, :3], rgba[:, 3] * sats])
-
-
-def _generate_normals(polygons):
- """
- Compute the normals of a list of polygons, one normal per polygon.
-
- Normals point towards the viewer for a face with its vertices in
- counterclockwise order, following the right hand rule.
-
- Uses three points equally spaced around the polygon. This method assumes
- that the points are in a plane. Otherwise, more than one shade is required,
- which is not supported.
-
- Parameters
- ----------
- polygons : list of (M_i, 3) array-like, or (..., M, 3) array-like
- A sequence of polygons to compute normals for, which can have
- varying numbers of vertices. If the polygons all have the same
- number of vertices and array is passed, then the operation will
- be vectorized.
-
- Returns
- -------
- normals : (..., 3) array
- A normal vector estimated for the polygon.
- """
- if isinstance(polygons, np.ndarray):
- # optimization: polygons all have the same number of points, so can
- # vectorize
- n = polygons.shape[-2]
- i1, i2, i3 = 0, n//3, 2*n//3
- v1 = polygons[..., i1, :] - polygons[..., i2, :]
- v2 = polygons[..., i2, :] - polygons[..., i3, :]
- else:
- # The subtraction doesn't vectorize because polygons is jagged.
- v1 = np.empty((len(polygons), 3))
- v2 = np.empty((len(polygons), 3))
- for poly_i, ps in enumerate(polygons):
- n = len(ps)
- i1, i2, i3 = 0, n//3, 2*n//3
- v1[poly_i, :] = ps[i1, :] - ps[i2, :]
- v2[poly_i, :] = ps[i2, :] - ps[i3, :]
- return np.cross(v1, v2)
-
-
-def _shade_colors(color, normals, lightsource=None):
- """
- Shade *color* using normal vectors given by *normals*,
- assuming a *lightsource* (using default position if not given).
- *color* can also be an array of the same length as *normals*.
- """
- if lightsource is None:
- # chosen for backwards-compatibility
- lightsource = mcolors.LightSource(azdeg=225, altdeg=19.4712)
-
- with np.errstate(invalid="ignore"):
- shade = ((normals / np.linalg.norm(normals, axis=1, keepdims=True))
- @ lightsource.direction)
- mask = ~np.isnan(shade)
-
- if mask.any():
- # convert dot product to allowed shading fractions
- in_norm = mcolors.Normalize(-1, 1)
- out_norm = mcolors.Normalize(0.3, 1).inverse
-
- def norm(x):
- return out_norm(in_norm(x))
-
- shade[~mask] = 0
-
- color = mcolors.to_rgba_array(color)
- # shape of color should be (M, 4) (where M is number of faces)
- # shape of shade should be (M,)
- # colors should have final shape of (M, 4)
- alpha = color[:, 3]
- colors = norm(shade)[:, np.newaxis] * color
- colors[:, 3] = alpha
- else:
- colors = np.asanyarray(color).copy()
-
- return colors
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/axes3d.py b/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/axes3d.py
deleted file mode 100644
index a74c11f54e6..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/axes3d.py
+++ /dev/null
@@ -1,3448 +0,0 @@
-"""
-axes3d.py, original mplot3d version by John Porter
-Created: 23 Sep 2005
-
-Parts fixed by Reinier Heeres <reinier@heeres.eu>
-Minor additions by Ben Axelrod <baxelrod@coroware.com>
-Significant updates and revisions by Ben Root <ben.v.root@gmail.com>
-
-Module containing Axes3D, an object which can plot 3D objects on a
-2D matplotlib figure.
-"""
-
-from collections import defaultdict
-import functools
-import itertools
-import math
-import textwrap
-
-import numpy as np
-
-import matplotlib as mpl
-from matplotlib import _api, cbook, _docstring, _preprocess_data
-import matplotlib.artist as martist
-import matplotlib.axes as maxes
-import matplotlib.collections as mcoll
-import matplotlib.colors as mcolors
-import matplotlib.image as mimage
-import matplotlib.lines as mlines
-import matplotlib.patches as mpatches
-import matplotlib.container as mcontainer
-import matplotlib.transforms as mtransforms
-from matplotlib.axes import Axes
-from matplotlib.axes._base import _axis_method_wrapper, _process_plot_format
-from matplotlib.transforms import Bbox
-from matplotlib.tri._triangulation import Triangulation
-
-from . import art3d
-from . import proj3d
-from . import axis3d
-
-
-@_docstring.interpd
-@_api.define_aliases({
- "xlim": ["xlim3d"], "ylim": ["ylim3d"], "zlim": ["zlim3d"]})
-class Axes3D(Axes):
- """
- 3D Axes object.
-
- .. note::
-
- As a user, you do not instantiate Axes directly, but use Axes creation
- methods instead; e.g. from `.pyplot` or `.Figure`:
- `~.pyplot.subplots`, `~.pyplot.subplot_mosaic` or `.Figure.add_axes`.
- """
- name = '3d'
-
- _axis_names = ("x", "y", "z")
- Axes._shared_axes["z"] = cbook.Grouper()
- Axes._shared_axes["view"] = cbook.Grouper()
-
- vvec = _api.deprecate_privatize_attribute("3.7")
- eye = _api.deprecate_privatize_attribute("3.7")
- sx = _api.deprecate_privatize_attribute("3.7")
- sy = _api.deprecate_privatize_attribute("3.7")
-
- def __init__(
- self, fig, rect=None, *args,
- elev=30, azim=-60, roll=0, sharez=None, proj_type='persp',
- box_aspect=None, computed_zorder=True, focal_length=None,
- shareview=None,
- **kwargs):
- """
- Parameters
- ----------
- fig : Figure
- The parent figure.
- rect : tuple (left, bottom, width, height), default: None.
- The ``(left, bottom, width, height)`` axes position.
- elev : float, default: 30
- The elevation angle in degrees rotates the camera above and below
- the x-y plane, with a positive angle corresponding to a location
- above the plane.
- azim : float, default: -60
- The azimuthal angle in degrees rotates the camera about the z axis,
- with a positive angle corresponding to a right-handed rotation. In
- other words, a positive azimuth rotates the camera about the origin
- from its location along the +x axis towards the +y axis.
- roll : float, default: 0
- The roll angle in degrees rotates the camera about the viewing
- axis. A positive angle spins the camera clockwise, causing the
- scene to rotate counter-clockwise.
- sharez : Axes3D, optional
- Other Axes to share z-limits with.
- proj_type : {'persp', 'ortho'}
- The projection type, default 'persp'.
- box_aspect : 3-tuple of floats, default: None
- Changes the physical dimensions of the Axes3D, such that the ratio
- of the axis lengths in display units is x:y:z.
- If None, defaults to 4:4:3
- computed_zorder : bool, default: True
- If True, the draw order is computed based on the average position
- of the `.Artist`\\s along the view direction.
- Set to False if you want to manually control the order in which
- Artists are drawn on top of each other using their *zorder*
- attribute. This can be used for fine-tuning if the automatic order
- does not produce the desired result. Note however, that a manual
- zorder will only be correct for a limited view angle. If the figure
- is rotated by the user, it will look wrong from certain angles.
- focal_length : float, default: None
- For a projection type of 'persp', the focal length of the virtual
- camera. Must be > 0. If None, defaults to 1.
- For a projection type of 'ortho', must be set to either None
- or infinity (numpy.inf). If None, defaults to infinity.
- The focal length can be computed from a desired Field Of View via
- the equation: focal_length = 1/tan(FOV/2)
- shareview : Axes3D, optional
- Other Axes to share view angles with.
-
- **kwargs
- Other optional keyword arguments:
-
- %(Axes3D:kwdoc)s
- """
-
- if rect is None:
- rect = [0.0, 0.0, 1.0, 1.0]
-
- self.initial_azim = azim
- self.initial_elev = elev
- self.initial_roll = roll
- self.set_proj_type(proj_type, focal_length)
- self.computed_zorder = computed_zorder
-
- self.xy_viewLim = Bbox.unit()
- self.zz_viewLim = Bbox.unit()
- self.xy_dataLim = Bbox.unit()
- # z-limits are encoded in the x-component of the Bbox, y is un-used
- self.zz_dataLim = Bbox.unit()
-
- # inhibit autoscale_view until the axes are defined
- # they can't be defined until Axes.__init__ has been called
- self.view_init(self.initial_elev, self.initial_azim, self.initial_roll)
-
- self._sharez = sharez
- if sharez is not None:
- self._shared_axes["z"].join(self, sharez)
- self._adjustable = 'datalim'
-
- self._shareview = shareview
- if shareview is not None:
- self._shared_axes["view"].join(self, shareview)
-
- if kwargs.pop('auto_add_to_figure', False):
- raise AttributeError(
- 'auto_add_to_figure is no longer supported for Axes3D. '
- 'Use fig.add_axes(ax) instead.'
- )
-
- super().__init__(
- fig, rect, frameon=True, box_aspect=box_aspect, *args, **kwargs
- )
- # Disable drawing of axes by base class
- super().set_axis_off()
- # Enable drawing of axes by Axes3D class
- self.set_axis_on()
- self.M = None
- self.invM = None
-
- # func used to format z -- fall back on major formatters
- self.fmt_zdata = None
-
- self.mouse_init()
- self.figure.canvas.callbacks._connect_picklable(
- 'motion_notify_event', self._on_move)
- self.figure.canvas.callbacks._connect_picklable(
- 'button_press_event', self._button_press)
- self.figure.canvas.callbacks._connect_picklable(
- 'button_release_event', self._button_release)
- self.set_top_view()
-
- self.patch.set_linewidth(0)
- # Calculate the pseudo-data width and height
- pseudo_bbox = self.transLimits.inverted().transform([(0, 0), (1, 1)])
- self._pseudo_w, self._pseudo_h = pseudo_bbox[1] - pseudo_bbox[0]
-
- # mplot3d currently manages its own spines and needs these turned off
- # for bounding box calculations
- self.spines[:].set_visible(False)
-
- def set_axis_off(self):
- self._axis3don = False
- self.stale = True
-
- def set_axis_on(self):
- self._axis3don = True
- self.stale = True
-
- def convert_zunits(self, z):
- """
- For artists in an Axes, if the zaxis has units support,
- convert *z* using zaxis unit type
- """
- return self.zaxis.convert_units(z)
-
- def set_top_view(self):
- # this happens to be the right view for the viewing coordinates
- # moved up and to the left slightly to fit labels and axes
- xdwl = 0.95 / self._dist
- xdw = 0.9 / self._dist
- ydwl = 0.95 / self._dist
- ydw = 0.9 / self._dist
- # Set the viewing pane.
- self.viewLim.intervalx = (-xdwl, xdw)
- self.viewLim.intervaly = (-ydwl, ydw)
- self.stale = True
-
- def _init_axis(self):
- """Init 3D axes; overrides creation of regular X/Y axes."""
- self.xaxis = axis3d.XAxis(self)
- self.yaxis = axis3d.YAxis(self)
- self.zaxis = axis3d.ZAxis(self)
-
- def get_zaxis(self):
- """Return the ``ZAxis`` (`~.axis3d.Axis`) instance."""
- return self.zaxis
-
- get_zgridlines = _axis_method_wrapper("zaxis", "get_gridlines")
- get_zticklines = _axis_method_wrapper("zaxis", "get_ticklines")
-
- @_api.deprecated("3.7")
- def unit_cube(self, vals=None):
- return self._unit_cube(vals)
-
- def _unit_cube(self, vals=None):
- minx, maxx, miny, maxy, minz, maxz = vals or self.get_w_lims()
- return [(minx, miny, minz),
- (maxx, miny, minz),
- (maxx, maxy, minz),
- (minx, maxy, minz),
- (minx, miny, maxz),
- (maxx, miny, maxz),
- (maxx, maxy, maxz),
- (minx, maxy, maxz)]
-
- @_api.deprecated("3.7")
- def tunit_cube(self, vals=None, M=None):
- return self._tunit_cube(vals, M)
-
- def _tunit_cube(self, vals=None, M=None):
- if M is None:
- M = self.M
- xyzs = self._unit_cube(vals)
- tcube = proj3d._proj_points(xyzs, M)
- return tcube
-
- @_api.deprecated("3.7")
- def tunit_edges(self, vals=None, M=None):
- return self._tunit_edges(vals, M)
-
- def _tunit_edges(self, vals=None, M=None):
- tc = self._tunit_cube(vals, M)
- edges = [(tc[0], tc[1]),
- (tc[1], tc[2]),
- (tc[2], tc[3]),
- (tc[3], tc[0]),
-
- (tc[0], tc[4]),
- (tc[1], tc[5]),
- (tc[2], tc[6]),
- (tc[3], tc[7]),
-
- (tc[4], tc[5]),
- (tc[5], tc[6]),
- (tc[6], tc[7]),
- (tc[7], tc[4])]
- return edges
-
- def set_aspect(self, aspect, adjustable=None, anchor=None, share=False):
- """
- Set the aspect ratios.
-
- Parameters
- ----------
- aspect : {'auto', 'equal', 'equalxy', 'equalxz', 'equalyz'}
- Possible values:
-
- ========= ==================================================
- value description
- ========= ==================================================
- 'auto' automatic; fill the position rectangle with data.
- 'equal' adapt all the axes to have equal aspect ratios.
- 'equalxy' adapt the x and y axes to have equal aspect ratios.
- 'equalxz' adapt the x and z axes to have equal aspect ratios.
- 'equalyz' adapt the y and z axes to have equal aspect ratios.
- ========= ==================================================
-
- adjustable : None or {'box', 'datalim'}, optional
- If not *None*, this defines which parameter will be adjusted to
- meet the required aspect. See `.set_adjustable` for further
- details.
-
- anchor : None or str or 2-tuple of float, optional
- If not *None*, this defines where the Axes will be drawn if there
- is extra space due to aspect constraints. The most common way to
- specify the anchor are abbreviations of cardinal directions:
-
- ===== =====================
- value description
- ===== =====================
- 'C' centered
- 'SW' lower left corner
- 'S' middle of bottom edge
- 'SE' lower right corner
- etc.
- ===== =====================
-
- See `~.Axes.set_anchor` for further details.
-
- share : bool, default: False
- If ``True``, apply the settings to all shared Axes.
-
- See Also
- --------
- mpl_toolkits.mplot3d.axes3d.Axes3D.set_box_aspect
- """
- _api.check_in_list(('auto', 'equal', 'equalxy', 'equalyz', 'equalxz'),
- aspect=aspect)
- super().set_aspect(
- aspect='auto', adjustable=adjustable, anchor=anchor, share=share)
- self._aspect = aspect
-
- if aspect in ('equal', 'equalxy', 'equalxz', 'equalyz'):
- ax_indices = self._equal_aspect_axis_indices(aspect)
-
- view_intervals = np.array([self.xaxis.get_view_interval(),
- self.yaxis.get_view_interval(),
- self.zaxis.get_view_interval()])
- ptp = np.ptp(view_intervals, axis=1)
- if self._adjustable == 'datalim':
- mean = np.mean(view_intervals, axis=1)
- scale = max(ptp[ax_indices] / self._box_aspect[ax_indices])
- deltas = scale * self._box_aspect
-
- for i, set_lim in enumerate((self.set_xlim3d,
- self.set_ylim3d,
- self.set_zlim3d)):
- if i in ax_indices:
- set_lim(mean[i] - deltas[i]/2., mean[i] + deltas[i]/2.)
- else: # 'box'
- # Change the box aspect such that the ratio of the length of
- # the unmodified axis to the length of the diagonal
- # perpendicular to it remains unchanged.
- box_aspect = np.array(self._box_aspect)
- box_aspect[ax_indices] = ptp[ax_indices]
- remaining_ax_indices = {0, 1, 2}.difference(ax_indices)
- if remaining_ax_indices:
- remaining = remaining_ax_indices.pop()
- old_diag = np.linalg.norm(self._box_aspect[ax_indices])
- new_diag = np.linalg.norm(box_aspect[ax_indices])
- box_aspect[remaining] *= new_diag / old_diag
- self.set_box_aspect(box_aspect)
-
- def _equal_aspect_axis_indices(self, aspect):
- """
- Get the indices for which of the x, y, z axes are constrained to have
- equal aspect ratios.
-
- Parameters
- ----------
- aspect : {'auto', 'equal', 'equalxy', 'equalxz', 'equalyz'}
- See descriptions in docstring for `.set_aspect()`.
- """
- ax_indices = [] # aspect == 'auto'
- if aspect == 'equal':
- ax_indices = [0, 1, 2]
- elif aspect == 'equalxy':
- ax_indices = [0, 1]
- elif aspect == 'equalxz':
- ax_indices = [0, 2]
- elif aspect == 'equalyz':
- ax_indices = [1, 2]
- return ax_indices
-
- def set_box_aspect(self, aspect, *, zoom=1):
- """
- Set the Axes box aspect.
-
- The box aspect is the ratio of height to width in display
- units for each face of the box when viewed perpendicular to
- that face. This is not to be confused with the data aspect (see
- `~.Axes3D.set_aspect`). The default ratios are 4:4:3 (x:y:z).
-
- To simulate having equal aspect in data space, set the box
- aspect to match your data range in each dimension.
-
- *zoom* controls the overall size of the Axes3D in the figure.
-
- Parameters
- ----------
- aspect : 3-tuple of floats or None
- Changes the physical dimensions of the Axes3D, such that the ratio
- of the axis lengths in display units is x:y:z.
- If None, defaults to (4, 4, 3).
-
- zoom : float, default: 1
- Control overall size of the Axes3D in the figure. Must be > 0.
- """
- if zoom <= 0:
- raise ValueError(f'Argument zoom = {zoom} must be > 0')
-
- if aspect is None:
- aspect = np.asarray((4, 4, 3), dtype=float)
- else:
- aspect = np.asarray(aspect, dtype=float)
- _api.check_shape((3,), aspect=aspect)
- # default scale tuned to match the mpl32 appearance.
- aspect *= 1.8294640721620434 * zoom / np.linalg.norm(aspect)
-
- self._box_aspect = aspect
- self.stale = True
-
- def apply_aspect(self, position=None):
- if position is None:
- position = self.get_position(original=True)
-
- # in the superclass, we would go through and actually deal with axis
- # scales and box/datalim. Those are all irrelevant - all we need to do
- # is make sure our coordinate system is square.
- trans = self.get_figure().transSubfigure
- bb = mtransforms.Bbox.unit().transformed(trans)
- # this is the physical aspect of the panel (or figure):
- fig_aspect = bb.height / bb.width
-
- box_aspect = 1
- pb = position.frozen()
- pb1 = pb.shrunk_to_aspect(box_aspect, pb, fig_aspect)
- self._set_position(pb1.anchored(self.get_anchor(), pb), 'active')
-
- @martist.allow_rasterization
- def draw(self, renderer):
- if not self.get_visible():
- return
- self._unstale_viewLim()
-
- # draw the background patch
- self.patch.draw(renderer)
- self._frameon = False
-
- # first, set the aspect
- # this is duplicated from `axes._base._AxesBase.draw`
- # but must be called before any of the artist are drawn as
- # it adjusts the view limits and the size of the bounding box
- # of the Axes
- locator = self.get_axes_locator()
- self.apply_aspect(locator(self, renderer) if locator else None)
-
- # add the projection matrix to the renderer
- self.M = self.get_proj()
- self.invM = np.linalg.inv(self.M)
-
- collections_and_patches = (
- artist for artist in self._children
- if isinstance(artist, (mcoll.Collection, mpatches.Patch))
- and artist.get_visible())
- if self.computed_zorder:
- # Calculate projection of collections and patches and zorder
- # them. Make sure they are drawn above the grids.
- zorder_offset = max(axis.get_zorder()
- for axis in self._axis_map.values()) + 1
- collection_zorder = patch_zorder = zorder_offset
-
- for artist in sorted(collections_and_patches,
- key=lambda artist: artist.do_3d_projection(),
- reverse=True):
- if isinstance(artist, mcoll.Collection):
- artist.zorder = collection_zorder
- collection_zorder += 1
- elif isinstance(artist, mpatches.Patch):
- artist.zorder = patch_zorder
- patch_zorder += 1
- else:
- for artist in collections_and_patches:
- artist.do_3d_projection()
-
- if self._axis3don:
- # Draw panes first
- for axis in self._axis_map.values():
- axis.draw_pane(renderer)
- # Then gridlines
- for axis in self._axis_map.values():
- axis.draw_grid(renderer)
- # Then axes, labels, text, and ticks
- for axis in self._axis_map.values():
- axis.draw(renderer)
-
- # Then rest
- super().draw(renderer)
-
- def get_axis_position(self):
- vals = self.get_w_lims()
- tc = self._tunit_cube(vals, self.M)
- xhigh = tc[1][2] > tc[2][2]
- yhigh = tc[3][2] > tc[2][2]
- zhigh = tc[0][2] > tc[2][2]
- return xhigh, yhigh, zhigh
-
- def update_datalim(self, xys, **kwargs):
- """
- Not implemented in `~mpl_toolkits.mplot3d.axes3d.Axes3D`.
- """
- pass
-
- get_autoscalez_on = _axis_method_wrapper("zaxis", "_get_autoscale_on")
- set_autoscalez_on = _axis_method_wrapper("zaxis", "_set_autoscale_on")
-
- def set_zmargin(self, m):
- """
- Set padding of Z data limits prior to autoscaling.
-
- *m* times the data interval will be added to each end of that interval
- before it is used in autoscaling. If *m* is negative, this will clip
- the data range instead of expanding it.
-
- For example, if your data is in the range [0, 2], a margin of 0.1 will
- result in a range [-0.2, 2.2]; a margin of -0.1 will result in a range
- of [0.2, 1.8].
-
- Parameters
- ----------
- m : float greater than -0.5
- """
- if m <= -0.5:
- raise ValueError("margin must be greater than -0.5")
- self._zmargin = m
- self._request_autoscale_view("z")
- self.stale = True
-
- def margins(self, *margins, x=None, y=None, z=None, tight=True):
- """
- Set or retrieve autoscaling margins.
-
- See `.Axes.margins` for full documentation. Because this function
- applies to 3D Axes, it also takes a *z* argument, and returns
- ``(xmargin, ymargin, zmargin)``.
- """
- if margins and (x is not None or y is not None or z is not None):
- raise TypeError('Cannot pass both positional and keyword '
- 'arguments for x, y, and/or z.')
- elif len(margins) == 1:
- x = y = z = margins[0]
- elif len(margins) == 3:
- x, y, z = margins
- elif margins:
- raise TypeError('Must pass a single positional argument for all '
- 'margins, or one for each margin (x, y, z).')
-
- if x is None and y is None and z is None:
- if tight is not True:
- _api.warn_external(f'ignoring tight={tight!r} in get mode')
- return self._xmargin, self._ymargin, self._zmargin
-
- if x is not None:
- self.set_xmargin(x)
- if y is not None:
- self.set_ymargin(y)
- if z is not None:
- self.set_zmargin(z)
-
- self.autoscale_view(
- tight=tight, scalex=(x is not None), scaley=(y is not None),
- scalez=(z is not None)
- )
-
- def autoscale(self, enable=True, axis='both', tight=None):
- """
- Convenience method for simple axis view autoscaling.
-
- See `.Axes.autoscale` for full documentation. Because this function
- applies to 3D Axes, *axis* can also be set to 'z', and setting *axis*
- to 'both' autoscales all three axes.
- """
- if enable is None:
- scalex = True
- scaley = True
- scalez = True
- else:
- if axis in ['x', 'both']:
- self.set_autoscalex_on(bool(enable))
- scalex = self.get_autoscalex_on()
- else:
- scalex = False
- if axis in ['y', 'both']:
- self.set_autoscaley_on(bool(enable))
- scaley = self.get_autoscaley_on()
- else:
- scaley = False
- if axis in ['z', 'both']:
- self.set_autoscalez_on(bool(enable))
- scalez = self.get_autoscalez_on()
- else:
- scalez = False
- if scalex:
- self._request_autoscale_view("x", tight=tight)
- if scaley:
- self._request_autoscale_view("y", tight=tight)
- if scalez:
- self._request_autoscale_view("z", tight=tight)
-
- def auto_scale_xyz(self, X, Y, Z=None, had_data=None):
- # This updates the bounding boxes as to keep a record as to what the
- # minimum sized rectangular volume holds the data.
- if np.shape(X) == np.shape(Y):
- self.xy_dataLim.update_from_data_xy(
- np.column_stack([np.ravel(X), np.ravel(Y)]), not had_data)
- else:
- self.xy_dataLim.update_from_data_x(X, not had_data)
- self.xy_dataLim.update_from_data_y(Y, not had_data)
- if Z is not None:
- self.zz_dataLim.update_from_data_x(Z, not had_data)
- # Let autoscale_view figure out how to use this data.
- self.autoscale_view()
-
- def autoscale_view(self, tight=None, scalex=True, scaley=True,
- scalez=True):
- """
- Autoscale the view limits using the data limits.
-
- See `.Axes.autoscale_view` for full documentation. Because this
- function applies to 3D Axes, it also takes a *scalez* argument.
- """
- # This method looks at the rectangular volume (see above)
- # of data and decides how to scale the view portal to fit it.
- if tight is None:
- _tight = self._tight
- if not _tight:
- # if image data only just use the datalim
- for artist in self._children:
- if isinstance(artist, mimage.AxesImage):
- _tight = True
- elif isinstance(artist, (mlines.Line2D, mpatches.Patch)):
- _tight = False
- break
- else:
- _tight = self._tight = bool(tight)
-
- if scalex and self.get_autoscalex_on():
- x0, x1 = self.xy_dataLim.intervalx
- xlocator = self.xaxis.get_major_locator()
- x0, x1 = xlocator.nonsingular(x0, x1)
- if self._xmargin > 0:
- delta = (x1 - x0) * self._xmargin
- x0 -= delta
- x1 += delta
- if not _tight:
- x0, x1 = xlocator.view_limits(x0, x1)
- self.set_xbound(x0, x1)
-
- if scaley and self.get_autoscaley_on():
- y0, y1 = self.xy_dataLim.intervaly
- ylocator = self.yaxis.get_major_locator()
- y0, y1 = ylocator.nonsingular(y0, y1)
- if self._ymargin > 0:
- delta = (y1 - y0) * self._ymargin
- y0 -= delta
- y1 += delta
- if not _tight:
- y0, y1 = ylocator.view_limits(y0, y1)
- self.set_ybound(y0, y1)
-
- if scalez and self.get_autoscalez_on():
- z0, z1 = self.zz_dataLim.intervalx
- zlocator = self.zaxis.get_major_locator()
- z0, z1 = zlocator.nonsingular(z0, z1)
- if self._zmargin > 0:
- delta = (z1 - z0) * self._zmargin
- z0 -= delta
- z1 += delta
- if not _tight:
- z0, z1 = zlocator.view_limits(z0, z1)
- self.set_zbound(z0, z1)
-
- def get_w_lims(self):
- """Get 3D world limits."""
- minx, maxx = self.get_xlim3d()
- miny, maxy = self.get_ylim3d()
- minz, maxz = self.get_zlim3d()
- return minx, maxx, miny, maxy, minz, maxz
-
- # set_xlim, set_ylim are directly inherited from base Axes.
- def set_zlim(self, bottom=None, top=None, *, emit=True, auto=False,
- zmin=None, zmax=None):
- """
- Set 3D z limits.
-
- See `.Axes.set_ylim` for full documentation
- """
- if top is None and np.iterable(bottom):
- bottom, top = bottom
- if zmin is not None:
- if bottom is not None:
- raise TypeError("Cannot pass both 'bottom' and 'zmin'")
- bottom = zmin
- if zmax is not None:
- if top is not None:
- raise TypeError("Cannot pass both 'top' and 'zmax'")
- top = zmax
- return self.zaxis._set_lim(bottom, top, emit=emit, auto=auto)
-
- set_xlim3d = maxes.Axes.set_xlim
- set_ylim3d = maxes.Axes.set_ylim
- set_zlim3d = set_zlim
-
- def get_xlim(self):
- # docstring inherited
- return tuple(self.xy_viewLim.intervalx)
-
- def get_ylim(self):
- # docstring inherited
- return tuple(self.xy_viewLim.intervaly)
-
- def get_zlim(self):
- """
- Return the 3D z-axis view limits.
-
- Returns
- -------
- left, right : (float, float)
- The current z-axis limits in data coordinates.
-
- See Also
- --------
- set_zlim
- set_zbound, get_zbound
- invert_zaxis, zaxis_inverted
-
- Notes
- -----
- The z-axis may be inverted, in which case the *left* value will
- be greater than the *right* value.
- """
- return tuple(self.zz_viewLim.intervalx)
-
- get_zscale = _axis_method_wrapper("zaxis", "get_scale")
-
- # Redefine all three methods to overwrite their docstrings.
- set_xscale = _axis_method_wrapper("xaxis", "_set_axes_scale")
- set_yscale = _axis_method_wrapper("yaxis", "_set_axes_scale")
- set_zscale = _axis_method_wrapper("zaxis", "_set_axes_scale")
- set_xscale.__doc__, set_yscale.__doc__, set_zscale.__doc__ = map(
- """
- Set the {}-axis scale.
-
- Parameters
- ----------
- value : {{"linear"}}
- The axis scale type to apply. 3D axes currently only support
- linear scales; other scales yield nonsensical results.
-
- **kwargs
- Keyword arguments are nominally forwarded to the scale class, but
- none of them is applicable for linear scales.
- """.format,
- ["x", "y", "z"])
-
- get_zticks = _axis_method_wrapper("zaxis", "get_ticklocs")
- set_zticks = _axis_method_wrapper("zaxis", "set_ticks")
- get_zmajorticklabels = _axis_method_wrapper("zaxis", "get_majorticklabels")
- get_zminorticklabels = _axis_method_wrapper("zaxis", "get_minorticklabels")
- get_zticklabels = _axis_method_wrapper("zaxis", "get_ticklabels")
- set_zticklabels = _axis_method_wrapper(
- "zaxis", "set_ticklabels",
- doc_sub={"Axis.set_ticks": "Axes3D.set_zticks"})
-
- zaxis_date = _axis_method_wrapper("zaxis", "axis_date")
- if zaxis_date.__doc__:
- zaxis_date.__doc__ += textwrap.dedent("""
-
- Notes
- -----
- This function is merely provided for completeness, but 3D axes do not
- support dates for ticks, and so this may not work as expected.
- """)
-
- def clabel(self, *args, **kwargs):
- """Currently not implemented for 3D axes, and returns *None*."""
- return None
-
- def view_init(self, elev=None, azim=None, roll=None, vertical_axis="z",
- share=False):
- """
- Set the elevation and azimuth of the axes in degrees (not radians).
-
- This can be used to rotate the axes programmatically.
-
- To look normal to the primary planes, the following elevation and
- azimuth angles can be used. A roll angle of 0, 90, 180, or 270 deg
- will rotate these views while keeping the axes at right angles.
-
- ========== ==== ====
- view plane elev azim
- ========== ==== ====
- XY 90 -90
- XZ 0 -90
- YZ 0 0
- -XY -90 90
- -XZ 0 90
- -YZ 0 180
- ========== ==== ====
-
- Parameters
- ----------
- elev : float, default: None
- The elevation angle in degrees rotates the camera above the plane
- pierced by the vertical axis, with a positive angle corresponding
- to a location above that plane. For example, with the default
- vertical axis of 'z', the elevation defines the angle of the camera
- location above the x-y plane.
- If None, then the initial value as specified in the `Axes3D`
- constructor is used.
- azim : float, default: None
- The azimuthal angle in degrees rotates the camera about the
- vertical axis, with a positive angle corresponding to a
- right-handed rotation. For example, with the default vertical axis
- of 'z', a positive azimuth rotates the camera about the origin from
- its location along the +x axis towards the +y axis.
- If None, then the initial value as specified in the `Axes3D`
- constructor is used.
- roll : float, default: None
- The roll angle in degrees rotates the camera about the viewing
- axis. A positive angle spins the camera clockwise, causing the
- scene to rotate counter-clockwise.
- If None, then the initial value as specified in the `Axes3D`
- constructor is used.
- vertical_axis : {"z", "x", "y"}, default: "z"
- The axis to align vertically. *azim* rotates about this axis.
- share : bool, default: False
- If ``True``, apply the settings to all Axes with shared views.
- """
-
- self._dist = 10 # The camera distance from origin. Behaves like zoom
-
- if elev is None:
- elev = self.initial_elev
- if azim is None:
- azim = self.initial_azim
- if roll is None:
- roll = self.initial_roll
- vertical_axis = _api.check_getitem(
- dict(x=0, y=1, z=2), vertical_axis=vertical_axis
- )
-
- if share:
- axes = {sibling for sibling
- in self._shared_axes['view'].get_siblings(self)}
- else:
- axes = [self]
-
- for ax in axes:
- ax.elev = elev
- ax.azim = azim
- ax.roll = roll
- ax._vertical_axis = vertical_axis
-
- def set_proj_type(self, proj_type, focal_length=None):
- """
- Set the projection type.
-
- Parameters
- ----------
- proj_type : {'persp', 'ortho'}
- The projection type.
- focal_length : float, default: None
- For a projection type of 'persp', the focal length of the virtual
- camera. Must be > 0. If None, defaults to 1.
- The focal length can be computed from a desired Field Of View via
- the equation: focal_length = 1/tan(FOV/2)
- """
- _api.check_in_list(['persp', 'ortho'], proj_type=proj_type)
- if proj_type == 'persp':
- if focal_length is None:
- focal_length = 1
- elif focal_length <= 0:
- raise ValueError(f"focal_length = {focal_length} must be "
- "greater than 0")
- self._focal_length = focal_length
- else: # 'ortho':
- if focal_length not in (None, np.inf):
- raise ValueError(f"focal_length = {focal_length} must be "
- f"None for proj_type = {proj_type}")
- self._focal_length = np.inf
-
- def _roll_to_vertical(self, arr):
- """Roll arrays to match the different vertical axis."""
- return np.roll(arr, self._vertical_axis - 2)
-
- def get_proj(self):
- """Create the projection matrix from the current viewing position."""
-
- # Transform to uniform world coordinates 0-1, 0-1, 0-1
- box_aspect = self._roll_to_vertical(self._box_aspect)
- worldM = proj3d.world_transformation(
- *self.get_xlim3d(),
- *self.get_ylim3d(),
- *self.get_zlim3d(),
- pb_aspect=box_aspect,
- )
-
- # Look into the middle of the world coordinates:
- R = 0.5 * box_aspect
-
- # elev: elevation angle in the z plane.
- # azim: azimuth angle in the xy plane.
- # Coordinates for a point that rotates around the box of data.
- # p0, p1 corresponds to rotating the box only around the vertical axis.
- # p2 corresponds to rotating the box only around the horizontal axis.
- elev_rad = np.deg2rad(self.elev)
- azim_rad = np.deg2rad(self.azim)
- p0 = np.cos(elev_rad) * np.cos(azim_rad)
- p1 = np.cos(elev_rad) * np.sin(azim_rad)
- p2 = np.sin(elev_rad)
-
- # When changing vertical axis the coordinates changes as well.
- # Roll the values to get the same behaviour as the default:
- ps = self._roll_to_vertical([p0, p1, p2])
-
- # The coordinates for the eye viewing point. The eye is looking
- # towards the middle of the box of data from a distance:
- eye = R + self._dist * ps
-
- # vvec, self._vvec and self._eye are unused, remove when deprecated
- vvec = R - eye
- self._eye = eye
- self._vvec = vvec / np.linalg.norm(vvec)
-
- # Calculate the viewing axes for the eye position
- u, v, w = self._calc_view_axes(eye)
- self._view_u = u # _view_u is towards the right of the screen
- self._view_v = v # _view_v is towards the top of the screen
- self._view_w = w # _view_w is out of the screen
-
- # Generate the view and projection transformation matrices
- if self._focal_length == np.inf:
- # Orthographic projection
- viewM = proj3d._view_transformation_uvw(u, v, w, eye)
- projM = proj3d._ortho_transformation(-self._dist, self._dist)
- else:
- # Perspective projection
- # Scale the eye dist to compensate for the focal length zoom effect
- eye_focal = R + self._dist * ps * self._focal_length
- viewM = proj3d._view_transformation_uvw(u, v, w, eye_focal)
- projM = proj3d._persp_transformation(-self._dist,
- self._dist,
- self._focal_length)
-
- # Combine all the transformation matrices to get the final projection
- M0 = np.dot(viewM, worldM)
- M = np.dot(projM, M0)
- return M
-
- def mouse_init(self, rotate_btn=1, pan_btn=2, zoom_btn=3):
- """
- Set the mouse buttons for 3D rotation and zooming.
-
- Parameters
- ----------
- rotate_btn : int or list of int, default: 1
- The mouse button or buttons to use for 3D rotation of the axes.
- pan_btn : int or list of int, default: 2
- The mouse button or buttons to use to pan the 3D axes.
- zoom_btn : int or list of int, default: 3
- The mouse button or buttons to use to zoom the 3D axes.
- """
- self.button_pressed = None
- # coerce scalars into array-like, then convert into
- # a regular list to avoid comparisons against None
- # which breaks in recent versions of numpy.
- self._rotate_btn = np.atleast_1d(rotate_btn).tolist()
- self._pan_btn = np.atleast_1d(pan_btn).tolist()
- self._zoom_btn = np.atleast_1d(zoom_btn).tolist()
-
- def disable_mouse_rotation(self):
- """Disable mouse buttons for 3D rotation, panning, and zooming."""
- self.mouse_init(rotate_btn=[], pan_btn=[], zoom_btn=[])
-
- def can_zoom(self):
- # doc-string inherited
- return True
-
- def can_pan(self):
- # doc-string inherited
- return True
-
- def sharez(self, other):
- """
- Share the z-axis with *other*.
-
- This is equivalent to passing ``sharez=other`` when constructing the
- Axes, and cannot be used if the z-axis is already being shared with
- another Axes.
- """
- _api.check_isinstance(Axes3D, other=other)
- if self._sharez is not None and other is not self._sharez:
- raise ValueError("z-axis is already shared")
- self._shared_axes["z"].join(self, other)
- self._sharez = other
- self.zaxis.major = other.zaxis.major # Ticker instances holding
- self.zaxis.minor = other.zaxis.minor # locator and formatter.
- z0, z1 = other.get_zlim()
- self.set_zlim(z0, z1, emit=False, auto=other.get_autoscalez_on())
- self.zaxis._scale = other.zaxis._scale
-
- def shareview(self, other):
- """
- Share the view angles with *other*.
-
- This is equivalent to passing ``shareview=other`` when
- constructing the Axes, and cannot be used if the view angles are
- already being shared with another Axes.
- """
- _api.check_isinstance(Axes3D, other=other)
- if self._shareview is not None and other is not self._shareview:
- raise ValueError("view angles are already shared")
- self._shared_axes["view"].join(self, other)
- self._shareview = other
- vertical_axis = {0: "x", 1: "y", 2: "z"}[other._vertical_axis]
- self.view_init(elev=other.elev, azim=other.azim, roll=other.roll,
- vertical_axis=vertical_axis, share=True)
-
- def clear(self):
- # docstring inherited.
- super().clear()
- if self._focal_length == np.inf:
- self._zmargin = mpl.rcParams['axes.zmargin']
- else:
- self._zmargin = 0.
- self.grid(mpl.rcParams['axes3d.grid'])
-
- def _button_press(self, event):
- if event.inaxes == self:
- self.button_pressed = event.button
- self._sx, self._sy = event.xdata, event.ydata
- toolbar = self.figure.canvas.toolbar
- if toolbar and toolbar._nav_stack() is None:
- toolbar.push_current()
-
- def _button_release(self, event):
- self.button_pressed = None
- toolbar = self.figure.canvas.toolbar
- # backend_bases.release_zoom and backend_bases.release_pan call
- # push_current, so check the navigation mode so we don't call it twice
- if toolbar and self.get_navigate_mode() is None:
- toolbar.push_current()
-
- def _get_view(self):
- # docstring inherited
- return {
- "xlim": self.get_xlim(), "autoscalex_on": self.get_autoscalex_on(),
- "ylim": self.get_ylim(), "autoscaley_on": self.get_autoscaley_on(),
- "zlim": self.get_zlim(), "autoscalez_on": self.get_autoscalez_on(),
- }, (self.elev, self.azim, self.roll)
-
- def _set_view(self, view):
- # docstring inherited
- props, (elev, azim, roll) = view
- self.set(**props)
- self.elev = elev
- self.azim = azim
- self.roll = roll
-
- def format_zdata(self, z):
- """
- Return *z* string formatted. This function will use the
- :attr:`fmt_zdata` attribute if it is callable, else will fall
- back on the zaxis major formatter
- """
- try:
- return self.fmt_zdata(z)
- except (AttributeError, TypeError):
- func = self.zaxis.get_major_formatter().format_data_short
- val = func(z)
- return val
-
- def format_coord(self, xv, yv, renderer=None):
- """
- Return a string giving the current view rotation angles, or the x, y, z
- coordinates of the point on the nearest axis pane underneath the mouse
- cursor, depending on the mouse button pressed.
- """
- coords = ''
-
- if self.button_pressed in self._rotate_btn:
- # ignore xv and yv and display angles instead
- coords = self._rotation_coords()
-
- elif self.M is not None:
- coords = self._location_coords(xv, yv, renderer)
-
- return coords
-
- def _rotation_coords(self):
- """
- Return the rotation angles as a string.
- """
- norm_elev = art3d._norm_angle(self.elev)
- norm_azim = art3d._norm_angle(self.azim)
- norm_roll = art3d._norm_angle(self.roll)
- coords = (f"elevation={norm_elev:.0f}\N{DEGREE SIGN}, "
- f"azimuth={norm_azim:.0f}\N{DEGREE SIGN}, "
- f"roll={norm_roll:.0f}\N{DEGREE SIGN}"
- ).replace("-", "\N{MINUS SIGN}")
- return coords
-
- def _location_coords(self, xv, yv, renderer):
- """
- Return the location on the axis pane underneath the cursor as a string.
- """
- p1, pane_idx = self._calc_coord(xv, yv, renderer)
- xs = self.format_xdata(p1[0])
- ys = self.format_ydata(p1[1])
- zs = self.format_zdata(p1[2])
- if pane_idx == 0:
- coords = f'x pane={xs}, y={ys}, z={zs}'
- elif pane_idx == 1:
- coords = f'x={xs}, y pane={ys}, z={zs}'
- elif pane_idx == 2:
- coords = f'x={xs}, y={ys}, z pane={zs}'
- return coords
-
- def _get_camera_loc(self):
- """
- Returns the current camera location in data coordinates.
- """
- cx, cy, cz, dx, dy, dz = self._get_w_centers_ranges()
- c = np.array([cx, cy, cz])
- r = np.array([dx, dy, dz])
-
- if self._focal_length == np.inf: # orthographic projection
- focal_length = 1e9 # large enough to be effectively infinite
- else: # perspective projection
- focal_length = self._focal_length
- eye = c + self._view_w * self._dist * r / self._box_aspect * focal_length
- return eye
-
- def _calc_coord(self, xv, yv, renderer=None):
- """
- Given the 2D view coordinates, find the point on the nearest axis pane
- that lies directly below those coordinates. Returns a 3D point in data
- coordinates.
- """
- if self._focal_length == np.inf: # orthographic projection
- zv = 1
- else: # perspective projection
- zv = -1 / self._focal_length
-
- # Convert point on view plane to data coordinates
- p1 = np.array(proj3d.inv_transform(xv, yv, zv, self.invM)).ravel()
-
- # Get the vector from the camera to the point on the view plane
- vec = self._get_camera_loc() - p1
-
- # Get the pane locations for each of the axes
- pane_locs = []
- for axis in self._axis_map.values():
- xys, loc = axis.active_pane(renderer)
- pane_locs.append(loc)
-
- # Find the distance to the nearest pane by projecting the view vector
- scales = np.zeros(3)
- for i in range(3):
- if vec[i] == 0:
- scales[i] = np.inf
- else:
- scales[i] = (p1[i] - pane_locs[i]) / vec[i]
- pane_idx = np.argmin(abs(scales))
- scale = scales[pane_idx]
-
- # Calculate the point on the closest pane
- p2 = p1 - scale*vec
- return p2, pane_idx
-
- def _on_move(self, event):
- """
- Mouse moving.
-
- By default, button-1 rotates, button-2 pans, and button-3 zooms;
- these buttons can be modified via `mouse_init`.
- """
-
- if not self.button_pressed:
- return
-
- if self.get_navigate_mode() is not None:
- # we don't want to rotate if we are zooming/panning
- # from the toolbar
- return
-
- if self.M is None:
- return
-
- x, y = event.xdata, event.ydata
- # In case the mouse is out of bounds.
- if x is None or event.inaxes != self:
- return
-
- dx, dy = x - self._sx, y - self._sy
- w = self._pseudo_w
- h = self._pseudo_h
-
- # Rotation
- if self.button_pressed in self._rotate_btn:
- # rotate viewing point
- # get the x and y pixel coords
- if dx == 0 and dy == 0:
- return
-
- roll = np.deg2rad(self.roll)
- delev = -(dy/h)*180*np.cos(roll) + (dx/w)*180*np.sin(roll)
- dazim = -(dy/h)*180*np.sin(roll) - (dx/w)*180*np.cos(roll)
- elev = self.elev + delev
- azim = self.azim + dazim
- self.view_init(elev=elev, azim=azim, roll=roll, share=True)
- self.stale = True
-
- # Pan
- elif self.button_pressed in self._pan_btn:
- # Start the pan event with pixel coordinates
- px, py = self.transData.transform([self._sx, self._sy])
- self.start_pan(px, py, 2)
- # pan view (takes pixel coordinate input)
- self.drag_pan(2, None, event.x, event.y)
- self.end_pan()
-
- # Zoom
- elif self.button_pressed in self._zoom_btn:
- # zoom view (dragging down zooms in)
- scale = h/(h - dy)
- self._scale_axis_limits(scale, scale, scale)
-
- # Store the event coordinates for the next time through.
- self._sx, self._sy = x, y
- # Always request a draw update at the end of interaction
- self.figure.canvas.draw_idle()
-
- def drag_pan(self, button, key, x, y):
- # docstring inherited
-
- # Get the coordinates from the move event
- p = self._pan_start
- (xdata, ydata), (xdata_start, ydata_start) = p.trans_inverse.transform(
- [(x, y), (p.x, p.y)])
- self._sx, self._sy = xdata, ydata
- # Calling start_pan() to set the x/y of this event as the starting
- # move location for the next event
- self.start_pan(x, y, button)
- du, dv = xdata - xdata_start, ydata - ydata_start
- dw = 0
- if key == 'x':
- dv = 0
- elif key == 'y':
- du = 0
- if du == 0 and dv == 0:
- return
-
- # Transform the pan from the view axes to the data axes
- R = np.array([self._view_u, self._view_v, self._view_w])
- R = -R / self._box_aspect * self._dist
- duvw_projected = R.T @ np.array([du, dv, dw])
-
- # Calculate pan distance
- minx, maxx, miny, maxy, minz, maxz = self.get_w_lims()
- dx = (maxx - minx) * duvw_projected[0]
- dy = (maxy - miny) * duvw_projected[1]
- dz = (maxz - minz) * duvw_projected[2]
-
- # Set the new axis limits
- self.set_xlim3d(minx + dx, maxx + dx)
- self.set_ylim3d(miny + dy, maxy + dy)
- self.set_zlim3d(minz + dz, maxz + dz)
-
- def _calc_view_axes(self, eye):
- """
- Get the unit vectors for the viewing axes in data coordinates.
- `u` is towards the right of the screen
- `v` is towards the top of the screen
- `w` is out of the screen
- """
- elev_rad = np.deg2rad(art3d._norm_angle(self.elev))
- roll_rad = np.deg2rad(art3d._norm_angle(self.roll))
-
- # Look into the middle of the world coordinates
- R = 0.5 * self._roll_to_vertical(self._box_aspect)
-
- # Define which axis should be vertical. A negative value
- # indicates the plot is upside down and therefore the values
- # have been reversed:
- V = np.zeros(3)
- V[self._vertical_axis] = -1 if abs(elev_rad) > np.pi/2 else 1
-
- u, v, w = proj3d._view_axes(eye, R, V, roll_rad)
- return u, v, w
-
- def _set_view_from_bbox(self, bbox, direction='in',
- mode=None, twinx=False, twiny=False):
- """
- Zoom in or out of the bounding box.
-
- Will center the view in the center of the bounding box, and zoom by
- the ratio of the size of the bounding box to the size of the Axes3D.
- """
- (start_x, start_y, stop_x, stop_y) = bbox
- if mode == 'x':
- start_y = self.bbox.min[1]
- stop_y = self.bbox.max[1]
- elif mode == 'y':
- start_x = self.bbox.min[0]
- stop_x = self.bbox.max[0]
-
- # Clip to bounding box limits
- start_x, stop_x = np.clip(sorted([start_x, stop_x]),
- self.bbox.min[0], self.bbox.max[0])
- start_y, stop_y = np.clip(sorted([start_y, stop_y]),
- self.bbox.min[1], self.bbox.max[1])
-
- # Move the center of the view to the center of the bbox
- zoom_center_x = (start_x + stop_x)/2
- zoom_center_y = (start_y + stop_y)/2
-
- ax_center_x = (self.bbox.max[0] + self.bbox.min[0])/2
- ax_center_y = (self.bbox.max[1] + self.bbox.min[1])/2
-
- self.start_pan(zoom_center_x, zoom_center_y, 2)
- self.drag_pan(2, None, ax_center_x, ax_center_y)
- self.end_pan()
-
- # Calculate zoom level
- dx = abs(start_x - stop_x)
- dy = abs(start_y - stop_y)
- scale_u = dx / (self.bbox.max[0] - self.bbox.min[0])
- scale_v = dy / (self.bbox.max[1] - self.bbox.min[1])
-
- # Keep aspect ratios equal
- scale = max(scale_u, scale_v)
-
- # Zoom out
- if direction == 'out':
- scale = 1 / scale
-
- self._zoom_data_limits(scale, scale, scale)
-
- def _zoom_data_limits(self, scale_u, scale_v, scale_w):
- """
- Zoom in or out of a 3D plot.
-
- Will scale the data limits by the scale factors. These will be
- transformed to the x, y, z data axes based on the current view angles.
- A scale factor > 1 zooms out and a scale factor < 1 zooms in.
-
- For an axes that has had its aspect ratio set to 'equal', 'equalxy',
- 'equalyz', or 'equalxz', the relevant axes are constrained to zoom
- equally.
-
- Parameters
- ----------
- scale_u : float
- Scale factor for the u view axis (view screen horizontal).
- scale_v : float
- Scale factor for the v view axis (view screen vertical).
- scale_w : float
- Scale factor for the w view axis (view screen depth).
- """
- scale = np.array([scale_u, scale_v, scale_w])
-
- # Only perform frame conversion if unequal scale factors
- if not np.allclose(scale, scale_u):
- # Convert the scale factors from the view frame to the data frame
- R = np.array([self._view_u, self._view_v, self._view_w])
- S = scale * np.eye(3)
- scale = np.linalg.norm(R.T @ S, axis=1)
-
- # Set the constrained scale factors to the factor closest to 1
- if self._aspect in ('equal', 'equalxy', 'equalxz', 'equalyz'):
- ax_idxs = self._equal_aspect_axis_indices(self._aspect)
- min_ax_idxs = np.argmin(np.abs(scale[ax_idxs] - 1))
- scale[ax_idxs] = scale[ax_idxs][min_ax_idxs]
-
- self._scale_axis_limits(scale[0], scale[1], scale[2])
-
- def _scale_axis_limits(self, scale_x, scale_y, scale_z):
- """
- Keeping the center of the x, y, and z data axes fixed, scale their
- limits by scale factors. A scale factor > 1 zooms out and a scale
- factor < 1 zooms in.
-
- Parameters
- ----------
- scale_x : float
- Scale factor for the x data axis.
- scale_y : float
- Scale factor for the y data axis.
- scale_z : float
- Scale factor for the z data axis.
- """
- # Get the axis centers and ranges
- cx, cy, cz, dx, dy, dz = self._get_w_centers_ranges()
-
- # Set the scaled axis limits
- self.set_xlim3d(cx - dx*scale_x/2, cx + dx*scale_x/2)
- self.set_ylim3d(cy - dy*scale_y/2, cy + dy*scale_y/2)
- self.set_zlim3d(cz - dz*scale_z/2, cz + dz*scale_z/2)
-
- def _get_w_centers_ranges(self):
- """Get 3D world centers and axis ranges."""
- # Calculate center of axis limits
- minx, maxx, miny, maxy, minz, maxz = self.get_w_lims()
- cx = (maxx + minx)/2
- cy = (maxy + miny)/2
- cz = (maxz + minz)/2
-
- # Calculate range of axis limits
- dx = (maxx - minx)
- dy = (maxy - miny)
- dz = (maxz - minz)
- return cx, cy, cz, dx, dy, dz
-
- def set_zlabel(self, zlabel, fontdict=None, labelpad=None, **kwargs):
- """
- Set zlabel. See doc for `.set_ylabel` for description.
- """
- if labelpad is not None:
- self.zaxis.labelpad = labelpad
- return self.zaxis.set_label_text(zlabel, fontdict, **kwargs)
-
- def get_zlabel(self):
- """
- Get the z-label text string.
- """
- label = self.zaxis.get_label()
- return label.get_text()
-
- # Axes rectangle characteristics
-
- # The frame_on methods are not available for 3D axes.
- # Python will raise a TypeError if they are called.
- get_frame_on = None
- set_frame_on = None
-
- def grid(self, visible=True, **kwargs):
- """
- Set / unset 3D grid.
-
- .. note::
-
- Currently, this function does not behave the same as
- `.axes.Axes.grid`, but it is intended to eventually support that
- behavior.
- """
- # TODO: Operate on each axes separately
- if len(kwargs):
- visible = True
- self._draw_grid = visible
- self.stale = True
-
- def tick_params(self, axis='both', **kwargs):
- """
- Convenience method for changing the appearance of ticks and
- tick labels.
-
- See `.Axes.tick_params` for full documentation. Because this function
- applies to 3D Axes, *axis* can also be set to 'z', and setting *axis*
- to 'both' autoscales all three axes.
-
- Also, because of how Axes3D objects are drawn very differently
- from regular 2D axes, some of these settings may have
- ambiguous meaning. For simplicity, the 'z' axis will
- accept settings as if it was like the 'y' axis.
-
- .. note::
- Axes3D currently ignores some of these settings.
- """
- _api.check_in_list(['x', 'y', 'z', 'both'], axis=axis)
- if axis in ['x', 'y', 'both']:
- super().tick_params(axis, **kwargs)
- if axis in ['z', 'both']:
- zkw = dict(kwargs)
- zkw.pop('top', None)
- zkw.pop('bottom', None)
- zkw.pop('labeltop', None)
- zkw.pop('labelbottom', None)
- self.zaxis.set_tick_params(**zkw)
-
- # data limits, ticks, tick labels, and formatting
-
- def invert_zaxis(self):
- """
- Invert the z-axis.
-
- See Also
- --------
- zaxis_inverted
- get_zlim, set_zlim
- get_zbound, set_zbound
- """
- bottom, top = self.get_zlim()
- self.set_zlim(top, bottom, auto=None)
-
- zaxis_inverted = _axis_method_wrapper("zaxis", "get_inverted")
-
- def get_zbound(self):
- """
- Return the lower and upper z-axis bounds, in increasing order.
-
- See Also
- --------
- set_zbound
- get_zlim, set_zlim
- invert_zaxis, zaxis_inverted
- """
- bottom, top = self.get_zlim()
- if bottom < top:
- return bottom, top
- else:
- return top, bottom
-
- def set_zbound(self, lower=None, upper=None):
- """
- Set the lower and upper numerical bounds of the z-axis.
-
- This method will honor axes inversion regardless of parameter order.
- It will not change the autoscaling setting (`.get_autoscalez_on()`).
-
- Parameters
- ----------
- lower, upper : float or None
- The lower and upper bounds. If *None*, the respective axis bound
- is not modified.
-
- See Also
- --------
- get_zbound
- get_zlim, set_zlim
- invert_zaxis, zaxis_inverted
- """
- if upper is None and np.iterable(lower):
- lower, upper = lower
-
- old_lower, old_upper = self.get_zbound()
- if lower is None:
- lower = old_lower
- if upper is None:
- upper = old_upper
-
- self.set_zlim(sorted((lower, upper),
- reverse=bool(self.zaxis_inverted())),
- auto=None)
-
- def text(self, x, y, z, s, zdir=None, **kwargs):
- """
- Add the text *s* to the 3D Axes at location *x*, *y*, *z* in data coordinates.
-
- Parameters
- ----------
- x, y, z : float
- The position to place the text.
- s : str
- The text.
- zdir : {'x', 'y', 'z', 3-tuple}, optional
- The direction to be used as the z-direction. Default: 'z'.
- See `.get_dir_vector` for a description of the values.
- **kwargs
- Other arguments are forwarded to `matplotlib.axes.Axes.text`.
-
- Returns
- -------
- `.Text3D`
- The created `.Text3D` instance.
- """
- text = super().text(x, y, s, **kwargs)
- art3d.text_2d_to_3d(text, z, zdir)
- return text
-
- text3D = text
- text2D = Axes.text
-
- def plot(self, xs, ys, *args, zdir='z', **kwargs):
- """
- Plot 2D or 3D data.
-
- Parameters
- ----------
- xs : 1D array-like
- x coordinates of vertices.
- ys : 1D array-like
- y coordinates of vertices.
- zs : float or 1D array-like
- z coordinates of vertices; either one for all points or one for
- each point.
- zdir : {'x', 'y', 'z'}, default: 'z'
- When plotting 2D data, the direction to use as z.
- **kwargs
- Other arguments are forwarded to `matplotlib.axes.Axes.plot`.
- """
- had_data = self.has_data()
-
- # `zs` can be passed positionally or as keyword; checking whether
- # args[0] is a string matches the behavior of 2D `plot` (via
- # `_process_plot_var_args`).
- if args and not isinstance(args[0], str):
- zs, *args = args
- if 'zs' in kwargs:
- raise TypeError("plot() for multiple values for argument 'z'")
- else:
- zs = kwargs.pop('zs', 0)
-
- # Match length
- zs = np.broadcast_to(zs, np.shape(xs))
-
- lines = super().plot(xs, ys, *args, **kwargs)
- for line in lines:
- art3d.line_2d_to_3d(line, zs=zs, zdir=zdir)
-
- xs, ys, zs = art3d.juggle_axes(xs, ys, zs, zdir)
- self.auto_scale_xyz(xs, ys, zs, had_data)
- return lines
-
- plot3D = plot
-
- def plot_surface(self, X, Y, Z, *, norm=None, vmin=None,
- vmax=None, lightsource=None, **kwargs):
- """
- Create a surface plot.
-
- By default, it will be colored in shades of a solid color, but it also
- supports colormapping by supplying the *cmap* argument.
-
- .. note::
-
- The *rcount* and *ccount* kwargs, which both default to 50,
- determine the maximum number of samples used in each direction. If
- the input data is larger, it will be downsampled (by slicing) to
- these numbers of points.
-
- .. note::
-
- To maximize rendering speed consider setting *rstride* and *cstride*
- to divisors of the number of rows minus 1 and columns minus 1
- respectively. For example, given 51 rows rstride can be any of the
- divisors of 50.
-
- Similarly, a setting of *rstride* and *cstride* equal to 1 (or
- *rcount* and *ccount* equal the number of rows and columns) can use
- the optimized path.
-
- Parameters
- ----------
- X, Y, Z : 2D arrays
- Data values.
-
- rcount, ccount : int
- Maximum number of samples used in each direction. If the input
- data is larger, it will be downsampled (by slicing) to these
- numbers of points. Defaults to 50.
-
- rstride, cstride : int
- Downsampling stride in each direction. These arguments are
- mutually exclusive with *rcount* and *ccount*. If only one of
- *rstride* or *cstride* is set, the other defaults to 10.
-
- 'classic' mode uses a default of ``rstride = cstride = 10`` instead
- of the new default of ``rcount = ccount = 50``.
-
- color : color-like
- Color of the surface patches.
-
- cmap : Colormap
- Colormap of the surface patches.
-
- facecolors : array-like of colors.
- Colors of each individual patch.
-
- norm : Normalize
- Normalization for the colormap.
-
- vmin, vmax : float
- Bounds for the normalization.
-
- shade : bool, default: True
- Whether to shade the facecolors. Shading is always disabled when
- *cmap* is specified.
-
- lightsource : `~matplotlib.colors.LightSource`
- The lightsource to use when *shade* is True.
-
- **kwargs
- Other keyword arguments are forwarded to `.Poly3DCollection`.
- """
-
- had_data = self.has_data()
-
- if Z.ndim != 2:
- raise ValueError("Argument Z must be 2-dimensional.")
-
- Z = cbook._to_unmasked_float_array(Z)
- X, Y, Z = np.broadcast_arrays(X, Y, Z)
- rows, cols = Z.shape
-
- has_stride = 'rstride' in kwargs or 'cstride' in kwargs
- has_count = 'rcount' in kwargs or 'ccount' in kwargs
-
- if has_stride and has_count:
- raise ValueError("Cannot specify both stride and count arguments")
-
- rstride = kwargs.pop('rstride', 10)
- cstride = kwargs.pop('cstride', 10)
- rcount = kwargs.pop('rcount', 50)
- ccount = kwargs.pop('ccount', 50)
-
- if mpl.rcParams['_internal.classic_mode']:
- # Strides have priority over counts in classic mode.
- # So, only compute strides from counts
- # if counts were explicitly given
- compute_strides = has_count
- else:
- # If the strides are provided then it has priority.
- # Otherwise, compute the strides from the counts.
- compute_strides = not has_stride
-
- if compute_strides:
- rstride = int(max(np.ceil(rows / rcount), 1))
- cstride = int(max(np.ceil(cols / ccount), 1))
-
- fcolors = kwargs.pop('facecolors', None)
-
- cmap = kwargs.get('cmap', None)
- shade = kwargs.pop('shade', cmap is None)
- if shade is None:
- raise ValueError("shade cannot be None.")
-
- colset = [] # the sampled facecolor
- if (rows - 1) % rstride == 0 and \
- (cols - 1) % cstride == 0 and \
- fcolors is None:
- polys = np.stack(
- [cbook._array_patch_perimeters(a, rstride, cstride)
- for a in (X, Y, Z)],
- axis=-1)
- else:
- # evenly spaced, and including both endpoints
- row_inds = list(range(0, rows-1, rstride)) + [rows-1]
- col_inds = list(range(0, cols-1, cstride)) + [cols-1]
-
- polys = []
- for rs, rs_next in zip(row_inds[:-1], row_inds[1:]):
- for cs, cs_next in zip(col_inds[:-1], col_inds[1:]):
- ps = [
- # +1 ensures we share edges between polygons
- cbook._array_perimeter(a[rs:rs_next+1, cs:cs_next+1])
- for a in (X, Y, Z)
- ]
- # ps = np.stack(ps, axis=-1)
- ps = np.array(ps).T
- polys.append(ps)
-
- if fcolors is not None:
- colset.append(fcolors[rs][cs])
-
- # In cases where there are non-finite values in the data (possibly NaNs from
- # masked arrays), artifacts can be introduced. Here check whether such values
- # are present and remove them.
- if not isinstance(polys, np.ndarray) or not np.isfinite(polys).all():
- new_polys = []
- new_colset = []
-
- # Depending on fcolors, colset is either an empty list or has as
- # many elements as polys. In the former case new_colset results in
- # a list with None entries, that is discarded later.
- for p, col in itertools.zip_longest(polys, colset):
- new_poly = np.array(p)[np.isfinite(p).all(axis=1)]
- if len(new_poly):
- new_polys.append(new_poly)
- new_colset.append(col)
-
- # Replace previous polys and, if fcolors is not None, colset
- polys = new_polys
- if fcolors is not None:
- colset = new_colset
-
- # note that the striding causes some polygons to have more coordinates
- # than others
-
- if fcolors is not None:
- polyc = art3d.Poly3DCollection(
- polys, edgecolors=colset, facecolors=colset, shade=shade,
- lightsource=lightsource, **kwargs)
- elif cmap:
- polyc = art3d.Poly3DCollection(polys, **kwargs)
- # can't always vectorize, because polys might be jagged
- if isinstance(polys, np.ndarray):
- avg_z = polys[..., 2].mean(axis=-1)
- else:
- avg_z = np.array([ps[:, 2].mean() for ps in polys])
- polyc.set_array(avg_z)
- if vmin is not None or vmax is not None:
- polyc.set_clim(vmin, vmax)
- if norm is not None:
- polyc.set_norm(norm)
- else:
- color = kwargs.pop('color', None)
- if color is None:
- color = self._get_lines.get_next_color()
- color = np.array(mcolors.to_rgba(color))
-
- polyc = art3d.Poly3DCollection(
- polys, facecolors=color, shade=shade,
- lightsource=lightsource, **kwargs)
-
- self.add_collection(polyc)
- self.auto_scale_xyz(X, Y, Z, had_data)
-
- return polyc
-
- def plot_wireframe(self, X, Y, Z, **kwargs):
- """
- Plot a 3D wireframe.
-
- .. note::
-
- The *rcount* and *ccount* kwargs, which both default to 50,
- determine the maximum number of samples used in each direction. If
- the input data is larger, it will be downsampled (by slicing) to
- these numbers of points.
-
- Parameters
- ----------
- X, Y, Z : 2D arrays
- Data values.
-
- rcount, ccount : int
- Maximum number of samples used in each direction. If the input
- data is larger, it will be downsampled (by slicing) to these
- numbers of points. Setting a count to zero causes the data to be
- not sampled in the corresponding direction, producing a 3D line
- plot rather than a wireframe plot. Defaults to 50.
-
- rstride, cstride : int
- Downsampling stride in each direction. These arguments are
- mutually exclusive with *rcount* and *ccount*. If only one of
- *rstride* or *cstride* is set, the other defaults to 1. Setting a
- stride to zero causes the data to be not sampled in the
- corresponding direction, producing a 3D line plot rather than a
- wireframe plot.
-
- 'classic' mode uses a default of ``rstride = cstride = 1`` instead
- of the new default of ``rcount = ccount = 50``.
-
- **kwargs
- Other keyword arguments are forwarded to `.Line3DCollection`.
- """
-
- had_data = self.has_data()
- if Z.ndim != 2:
- raise ValueError("Argument Z must be 2-dimensional.")
- # FIXME: Support masked arrays
- X, Y, Z = np.broadcast_arrays(X, Y, Z)
- rows, cols = Z.shape
-
- has_stride = 'rstride' in kwargs or 'cstride' in kwargs
- has_count = 'rcount' in kwargs or 'ccount' in kwargs
-
- if has_stride and has_count:
- raise ValueError("Cannot specify both stride and count arguments")
-
- rstride = kwargs.pop('rstride', 1)
- cstride = kwargs.pop('cstride', 1)
- rcount = kwargs.pop('rcount', 50)
- ccount = kwargs.pop('ccount', 50)
-
- if mpl.rcParams['_internal.classic_mode']:
- # Strides have priority over counts in classic mode.
- # So, only compute strides from counts
- # if counts were explicitly given
- if has_count:
- rstride = int(max(np.ceil(rows / rcount), 1)) if rcount else 0
- cstride = int(max(np.ceil(cols / ccount), 1)) if ccount else 0
- else:
- # If the strides are provided then it has priority.
- # Otherwise, compute the strides from the counts.
- if not has_stride:
- rstride = int(max(np.ceil(rows / rcount), 1)) if rcount else 0
- cstride = int(max(np.ceil(cols / ccount), 1)) if ccount else 0
-
- # We want two sets of lines, one running along the "rows" of
- # Z and another set of lines running along the "columns" of Z.
- # This transpose will make it easy to obtain the columns.
- tX, tY, tZ = np.transpose(X), np.transpose(Y), np.transpose(Z)
-
- if rstride:
- rii = list(range(0, rows, rstride))
- # Add the last index only if needed
- if rows > 0 and rii[-1] != (rows - 1):
- rii += [rows-1]
- else:
- rii = []
- if cstride:
- cii = list(range(0, cols, cstride))
- # Add the last index only if needed
- if cols > 0 and cii[-1] != (cols - 1):
- cii += [cols-1]
- else:
- cii = []
-
- if rstride == 0 and cstride == 0:
- raise ValueError("Either rstride or cstride must be non zero")
-
- # If the inputs were empty, then just
- # reset everything.
- if Z.size == 0:
- rii = []
- cii = []
-
- xlines = [X[i] for i in rii]
- ylines = [Y[i] for i in rii]
- zlines = [Z[i] for i in rii]
-
- txlines = [tX[i] for i in cii]
- tylines = [tY[i] for i in cii]
- tzlines = [tZ[i] for i in cii]
-
- lines = ([list(zip(xl, yl, zl))
- for xl, yl, zl in zip(xlines, ylines, zlines)]
- + [list(zip(xl, yl, zl))
- for xl, yl, zl in zip(txlines, tylines, tzlines)])
-
- linec = art3d.Line3DCollection(lines, **kwargs)
- self.add_collection(linec)
- self.auto_scale_xyz(X, Y, Z, had_data)
-
- return linec
-
- def plot_trisurf(self, *args, color=None, norm=None, vmin=None, vmax=None,
- lightsource=None, **kwargs):
- """
- Plot a triangulated surface.
-
- The (optional) triangulation can be specified in one of two ways;
- either::
-
- plot_trisurf(triangulation, ...)
-
- where triangulation is a `~matplotlib.tri.Triangulation` object, or::
-
- plot_trisurf(X, Y, ...)
- plot_trisurf(X, Y, triangles, ...)
- plot_trisurf(X, Y, triangles=triangles, ...)
-
- in which case a Triangulation object will be created. See
- `.Triangulation` for an explanation of these possibilities.
-
- The remaining arguments are::
-
- plot_trisurf(..., Z)
-
- where *Z* is the array of values to contour, one per point
- in the triangulation.
-
- Parameters
- ----------
- X, Y, Z : array-like
- Data values as 1D arrays.
- color
- Color of the surface patches.
- cmap
- A colormap for the surface patches.
- norm : Normalize
- An instance of Normalize to map values to colors.
- vmin, vmax : float, default: None
- Minimum and maximum value to map.
- shade : bool, default: True
- Whether to shade the facecolors. Shading is always disabled when
- *cmap* is specified.
- lightsource : `~matplotlib.colors.LightSource`
- The lightsource to use when *shade* is True.
- **kwargs
- All other keyword arguments are passed on to
- :class:`~mpl_toolkits.mplot3d.art3d.Poly3DCollection`
-
- Examples
- --------
- .. plot:: gallery/mplot3d/trisurf3d.py
- .. plot:: gallery/mplot3d/trisurf3d_2.py
- """
-
- had_data = self.has_data()
-
- # TODO: Support custom face colours
- if color is None:
- color = self._get_lines.get_next_color()
- color = np.array(mcolors.to_rgba(color))
-
- cmap = kwargs.get('cmap', None)
- shade = kwargs.pop('shade', cmap is None)
-
- tri, args, kwargs = \
- Triangulation.get_from_args_and_kwargs(*args, **kwargs)
- try:
- z = kwargs.pop('Z')
- except KeyError:
- # We do this so Z doesn't get passed as an arg to PolyCollection
- z, *args = args
- z = np.asarray(z)
-
- triangles = tri.get_masked_triangles()
- xt = tri.x[triangles]
- yt = tri.y[triangles]
- zt = z[triangles]
- verts = np.stack((xt, yt, zt), axis=-1)
-
- if cmap:
- polyc = art3d.Poly3DCollection(verts, *args, **kwargs)
- # average over the three points of each triangle
- avg_z = verts[:, :, 2].mean(axis=1)
- polyc.set_array(avg_z)
- if vmin is not None or vmax is not None:
- polyc.set_clim(vmin, vmax)
- if norm is not None:
- polyc.set_norm(norm)
- else:
- polyc = art3d.Poly3DCollection(
- verts, *args, shade=shade, lightsource=lightsource,
- facecolors=color, **kwargs)
-
- self.add_collection(polyc)
- self.auto_scale_xyz(tri.x, tri.y, z, had_data)
-
- return polyc
-
- def _3d_extend_contour(self, cset, stride=5):
- """
- Extend a contour in 3D by creating
- """
-
- dz = (cset.levels[1] - cset.levels[0]) / 2
- polyverts = []
- colors = []
- for idx, level in enumerate(cset.levels):
- path = cset.get_paths()[idx]
- subpaths = [*path._iter_connected_components()]
- color = cset.get_edgecolor()[idx]
- top = art3d._paths_to_3d_segments(subpaths, level - dz)
- bot = art3d._paths_to_3d_segments(subpaths, level + dz)
- if not len(top[0]):
- continue
- nsteps = max(round(len(top[0]) / stride), 2)
- stepsize = (len(top[0]) - 1) / (nsteps - 1)
- polyverts.extend([
- (top[0][round(i * stepsize)], top[0][round((i + 1) * stepsize)],
- bot[0][round((i + 1) * stepsize)], bot[0][round(i * stepsize)])
- for i in range(round(nsteps) - 1)])
- colors.extend([color] * (round(nsteps) - 1))
- self.add_collection3d(art3d.Poly3DCollection(
- np.array(polyverts), # All polygons have 4 vertices, so vectorize.
- facecolors=colors, edgecolors=colors, shade=True))
- cset.remove()
-
- def add_contour_set(
- self, cset, extend3d=False, stride=5, zdir='z', offset=None):
- zdir = '-' + zdir
- if extend3d:
- self._3d_extend_contour(cset, stride)
- else:
- art3d.collection_2d_to_3d(
- cset, zs=offset if offset is not None else cset.levels, zdir=zdir)
-
- def add_contourf_set(self, cset, zdir='z', offset=None):
- self._add_contourf_set(cset, zdir=zdir, offset=offset)
-
- def _add_contourf_set(self, cset, zdir='z', offset=None):
- """
- Returns
- -------
- levels : `numpy.ndarray`
- Levels at which the filled contours are added.
- """
- zdir = '-' + zdir
-
- midpoints = cset.levels[:-1] + np.diff(cset.levels) / 2
- # Linearly interpolate to get levels for any extensions
- if cset._extend_min:
- min_level = cset.levels[0] - np.diff(cset.levels[:2]) / 2
- midpoints = np.insert(midpoints, 0, min_level)
- if cset._extend_max:
- max_level = cset.levels[-1] + np.diff(cset.levels[-2:]) / 2
- midpoints = np.append(midpoints, max_level)
-
- art3d.collection_2d_to_3d(
- cset, zs=offset if offset is not None else midpoints, zdir=zdir)
- return midpoints
-
- @_preprocess_data()
- def contour(self, X, Y, Z, *args,
- extend3d=False, stride=5, zdir='z', offset=None, **kwargs):
- """
- Create a 3D contour plot.
-
- Parameters
- ----------
- X, Y, Z : array-like,
- Input data. See `.Axes.contour` for supported data shapes.
- extend3d : bool, default: False
- Whether to extend contour in 3D.
- stride : int
- Step size for extending contour.
- zdir : {'x', 'y', 'z'}, default: 'z'
- The direction to use.
- offset : float, optional
- If specified, plot a projection of the contour lines at this
- position in a plane normal to *zdir*.
- data : indexable object, optional
- DATA_PARAMETER_PLACEHOLDER
-
- *args, **kwargs
- Other arguments are forwarded to `matplotlib.axes.Axes.contour`.
-
- Returns
- -------
- matplotlib.contour.QuadContourSet
- """
- had_data = self.has_data()
-
- jX, jY, jZ = art3d.rotate_axes(X, Y, Z, zdir)
- cset = super().contour(jX, jY, jZ, *args, **kwargs)
- self.add_contour_set(cset, extend3d, stride, zdir, offset)
-
- self.auto_scale_xyz(X, Y, Z, had_data)
- return cset
-
- contour3D = contour
-
- @_preprocess_data()
- def tricontour(self, *args,
- extend3d=False, stride=5, zdir='z', offset=None, **kwargs):
- """
- Create a 3D contour plot.
-
- .. note::
- This method currently produces incorrect output due to a
- longstanding bug in 3D PolyCollection rendering.
-
- Parameters
- ----------
- X, Y, Z : array-like
- Input data. See `.Axes.tricontour` for supported data shapes.
- extend3d : bool, default: False
- Whether to extend contour in 3D.
- stride : int
- Step size for extending contour.
- zdir : {'x', 'y', 'z'}, default: 'z'
- The direction to use.
- offset : float, optional
- If specified, plot a projection of the contour lines at this
- position in a plane normal to *zdir*.
- data : indexable object, optional
- DATA_PARAMETER_PLACEHOLDER
- *args, **kwargs
- Other arguments are forwarded to `matplotlib.axes.Axes.tricontour`.
-
- Returns
- -------
- matplotlib.tri._tricontour.TriContourSet
- """
- had_data = self.has_data()
-
- tri, args, kwargs = Triangulation.get_from_args_and_kwargs(
- *args, **kwargs)
- X = tri.x
- Y = tri.y
- if 'Z' in kwargs:
- Z = kwargs.pop('Z')
- else:
- # We do this so Z doesn't get passed as an arg to Axes.tricontour
- Z, *args = args
-
- jX, jY, jZ = art3d.rotate_axes(X, Y, Z, zdir)
- tri = Triangulation(jX, jY, tri.triangles, tri.mask)
-
- cset = super().tricontour(tri, jZ, *args, **kwargs)
- self.add_contour_set(cset, extend3d, stride, zdir, offset)
-
- self.auto_scale_xyz(X, Y, Z, had_data)
- return cset
-
- def _auto_scale_contourf(self, X, Y, Z, zdir, levels, had_data):
- # Autoscale in the zdir based on the levels added, which are
- # different from data range if any contour extensions are present
- dim_vals = {'x': X, 'y': Y, 'z': Z, zdir: levels}
- # Input data and levels have different sizes, but auto_scale_xyz
- # expected same-size input, so manually take min/max limits
- limits = [(np.nanmin(dim_vals[dim]), np.nanmax(dim_vals[dim]))
- for dim in ['x', 'y', 'z']]
- self.auto_scale_xyz(*limits, had_data)
-
- @_preprocess_data()
- def contourf(self, X, Y, Z, *args, zdir='z', offset=None, **kwargs):
- """
- Create a 3D filled contour plot.
-
- Parameters
- ----------
- X, Y, Z : array-like
- Input data. See `.Axes.contourf` for supported data shapes.
- zdir : {'x', 'y', 'z'}, default: 'z'
- The direction to use.
- offset : float, optional
- If specified, plot a projection of the contour lines at this
- position in a plane normal to *zdir*.
- data : indexable object, optional
- DATA_PARAMETER_PLACEHOLDER
- *args, **kwargs
- Other arguments are forwarded to `matplotlib.axes.Axes.contourf`.
-
- Returns
- -------
- matplotlib.contour.QuadContourSet
- """
- had_data = self.has_data()
-
- jX, jY, jZ = art3d.rotate_axes(X, Y, Z, zdir)
- cset = super().contourf(jX, jY, jZ, *args, **kwargs)
- levels = self._add_contourf_set(cset, zdir, offset)
-
- self._auto_scale_contourf(X, Y, Z, zdir, levels, had_data)
- return cset
-
- contourf3D = contourf
-
- @_preprocess_data()
- def tricontourf(self, *args, zdir='z', offset=None, **kwargs):
- """
- Create a 3D filled contour plot.
-
- .. note::
- This method currently produces incorrect output due to a
- longstanding bug in 3D PolyCollection rendering.
-
- Parameters
- ----------
- X, Y, Z : array-like
- Input data. See `.Axes.tricontourf` for supported data shapes.
- zdir : {'x', 'y', 'z'}, default: 'z'
- The direction to use.
- offset : float, optional
- If specified, plot a projection of the contour lines at this
- position in a plane normal to zdir.
- data : indexable object, optional
- DATA_PARAMETER_PLACEHOLDER
- *args, **kwargs
- Other arguments are forwarded to
- `matplotlib.axes.Axes.tricontourf`.
-
- Returns
- -------
- matplotlib.tri._tricontour.TriContourSet
- """
- had_data = self.has_data()
-
- tri, args, kwargs = Triangulation.get_from_args_and_kwargs(
- *args, **kwargs)
- X = tri.x
- Y = tri.y
- if 'Z' in kwargs:
- Z = kwargs.pop('Z')
- else:
- # We do this so Z doesn't get passed as an arg to Axes.tricontourf
- Z, *args = args
-
- jX, jY, jZ = art3d.rotate_axes(X, Y, Z, zdir)
- tri = Triangulation(jX, jY, tri.triangles, tri.mask)
-
- cset = super().tricontourf(tri, jZ, *args, **kwargs)
- levels = self._add_contourf_set(cset, zdir, offset)
-
- self._auto_scale_contourf(X, Y, Z, zdir, levels, had_data)
- return cset
-
- def add_collection3d(self, col, zs=0, zdir='z'):
- """
- Add a 3D collection object to the plot.
-
- 2D collection types are converted to a 3D version by
- modifying the object and adding z coordinate information.
-
- Supported are:
-
- - PolyCollection
- - LineCollection
- - PatchCollection
- """
- zvals = np.atleast_1d(zs)
- zsortval = (np.min(zvals) if zvals.size
- else 0) # FIXME: arbitrary default
-
- # FIXME: use issubclass() (although, then a 3D collection
- # object would also pass.) Maybe have a collection3d
- # abstract class to test for and exclude?
- if type(col) is mcoll.PolyCollection:
- art3d.poly_collection_2d_to_3d(col, zs=zs, zdir=zdir)
- col.set_sort_zpos(zsortval)
- elif type(col) is mcoll.LineCollection:
- art3d.line_collection_2d_to_3d(col, zs=zs, zdir=zdir)
- col.set_sort_zpos(zsortval)
- elif type(col) is mcoll.PatchCollection:
- art3d.patch_collection_2d_to_3d(col, zs=zs, zdir=zdir)
- col.set_sort_zpos(zsortval)
-
- collection = super().add_collection(col)
- return collection
-
- @_preprocess_data(replace_names=["xs", "ys", "zs", "s",
- "edgecolors", "c", "facecolor",
- "facecolors", "color"])
- def scatter(self, xs, ys, zs=0, zdir='z', s=20, c=None, depthshade=True,
- *args, **kwargs):
- """
- Create a scatter plot.
-
- Parameters
- ----------
- xs, ys : array-like
- The data positions.
- zs : float or array-like, default: 0
- The z-positions. Either an array of the same length as *xs* and
- *ys* or a single value to place all points in the same plane.
- zdir : {'x', 'y', 'z', '-x', '-y', '-z'}, default: 'z'
- The axis direction for the *zs*. This is useful when plotting 2D
- data on a 3D Axes. The data must be passed as *xs*, *ys*. Setting
- *zdir* to 'y' then plots the data to the x-z-plane.
-
- See also :doc:`/gallery/mplot3d/2dcollections3d`.
-
- s : float or array-like, default: 20
- The marker size in points**2. Either an array of the same length
- as *xs* and *ys* or a single value to make all markers the same
- size.
- c : color, sequence, or sequence of colors, optional
- The marker color. Possible values:
-
- - A single color format string.
- - A sequence of colors of length n.
- - A sequence of n numbers to be mapped to colors using *cmap* and
- *norm*.
- - A 2D array in which the rows are RGB or RGBA.
-
- For more details see the *c* argument of `~.axes.Axes.scatter`.
- depthshade : bool, default: True
- Whether to shade the scatter markers to give the appearance of
- depth. Each call to ``scatter()`` will perform its depthshading
- independently.
- data : indexable object, optional
- DATA_PARAMETER_PLACEHOLDER
- **kwargs
- All other keyword arguments are passed on to `~.axes.Axes.scatter`.
-
- Returns
- -------
- paths : `~matplotlib.collections.PathCollection`
- """
-
- had_data = self.has_data()
- zs_orig = zs
-
- xs, ys, zs = np.broadcast_arrays(
- *[np.ravel(np.ma.filled(t, np.nan)) for t in [xs, ys, zs]])
- s = np.ma.ravel(s) # This doesn't have to match x, y in size.
-
- xs, ys, zs, s, c, color = cbook.delete_masked_points(
- xs, ys, zs, s, c, kwargs.get('color', None)
- )
- if kwargs.get("color") is not None:
- kwargs['color'] = color
-
- # For xs and ys, 2D scatter() will do the copying.
- if np.may_share_memory(zs_orig, zs): # Avoid unnecessary copies.
- zs = zs.copy()
-
- patches = super().scatter(xs, ys, s=s, c=c, *args, **kwargs)
- art3d.patch_collection_2d_to_3d(patches, zs=zs, zdir=zdir,
- depthshade=depthshade)
-
- if self._zmargin < 0.05 and xs.size > 0:
- self.set_zmargin(0.05)
-
- self.auto_scale_xyz(xs, ys, zs, had_data)
-
- return patches
-
- scatter3D = scatter
-
- @_preprocess_data()
- def bar(self, left, height, zs=0, zdir='z', *args, **kwargs):
- """
- Add 2D bar(s).
-
- Parameters
- ----------
- left : 1D array-like
- The x coordinates of the left sides of the bars.
- height : 1D array-like
- The height of the bars.
- zs : float or 1D array-like
- Z coordinate of bars; if a single value is specified, it will be
- used for all bars.
- zdir : {'x', 'y', 'z'}, default: 'z'
- When plotting 2D data, the direction to use as z ('x', 'y' or 'z').
- data : indexable object, optional
- DATA_PARAMETER_PLACEHOLDER
- **kwargs
- Other keyword arguments are forwarded to
- `matplotlib.axes.Axes.bar`.
-
- Returns
- -------
- mpl_toolkits.mplot3d.art3d.Patch3DCollection
- """
- had_data = self.has_data()
-
- patches = super().bar(left, height, *args, **kwargs)
-
- zs = np.broadcast_to(zs, len(left))
-
- verts = []
- verts_zs = []
- for p, z in zip(patches, zs):
- vs = art3d._get_patch_verts(p)
- verts += vs.tolist()
- verts_zs += [z] * len(vs)
- art3d.patch_2d_to_3d(p, z, zdir)
- if 'alpha' in kwargs:
- p.set_alpha(kwargs['alpha'])
-
- if len(verts) > 0:
- # the following has to be skipped if verts is empty
- # NOTE: Bugs could still occur if len(verts) > 0,
- # but the "2nd dimension" is empty.
- xs, ys = zip(*verts)
- else:
- xs, ys = [], []
-
- xs, ys, verts_zs = art3d.juggle_axes(xs, ys, verts_zs, zdir)
- self.auto_scale_xyz(xs, ys, verts_zs, had_data)
-
- return patches
-
- @_preprocess_data()
- def bar3d(self, x, y, z, dx, dy, dz, color=None,
- zsort='average', shade=True, lightsource=None, *args, **kwargs):
- """
- Generate a 3D barplot.
-
- This method creates three-dimensional barplot where the width,
- depth, height, and color of the bars can all be uniquely set.
-
- Parameters
- ----------
- x, y, z : array-like
- The coordinates of the anchor point of the bars.
-
- dx, dy, dz : float or array-like
- The width, depth, and height of the bars, respectively.
-
- color : sequence of colors, optional
- The color of the bars can be specified globally or
- individually. This parameter can be:
-
- - A single color, to color all bars the same color.
- - An array of colors of length N bars, to color each bar
- independently.
- - An array of colors of length 6, to color the faces of the
- bars similarly.
- - An array of colors of length 6 * N bars, to color each face
- independently.
-
- When coloring the faces of the boxes specifically, this is
- the order of the coloring:
-
- 1. -Z (bottom of box)
- 2. +Z (top of box)
- 3. -Y
- 4. +Y
- 5. -X
- 6. +X
-
- zsort : str, optional
- The z-axis sorting scheme passed onto `~.art3d.Poly3DCollection`
-
- shade : bool, default: True
- When true, this shades the dark sides of the bars (relative
- to the plot's source of light).
-
- lightsource : `~matplotlib.colors.LightSource`
- The lightsource to use when *shade* is True.
-
- data : indexable object, optional
- DATA_PARAMETER_PLACEHOLDER
-
- **kwargs
- Any additional keyword arguments are passed onto
- `~.art3d.Poly3DCollection`.
-
- Returns
- -------
- collection : `~.art3d.Poly3DCollection`
- A collection of three-dimensional polygons representing the bars.
- """
-
- had_data = self.has_data()
-
- x, y, z, dx, dy, dz = np.broadcast_arrays(
- np.atleast_1d(x), y, z, dx, dy, dz)
- minx = np.min(x)
- maxx = np.max(x + dx)
- miny = np.min(y)
- maxy = np.max(y + dy)
- minz = np.min(z)
- maxz = np.max(z + dz)
-
- # shape (6, 4, 3)
- # All faces are oriented facing outwards - when viewed from the
- # outside, their vertices are in a counterclockwise ordering.
- cuboid = np.array([
- # -z
- (
- (0, 0, 0),
- (0, 1, 0),
- (1, 1, 0),
- (1, 0, 0),
- ),
- # +z
- (
- (0, 0, 1),
- (1, 0, 1),
- (1, 1, 1),
- (0, 1, 1),
- ),
- # -y
- (
- (0, 0, 0),
- (1, 0, 0),
- (1, 0, 1),
- (0, 0, 1),
- ),
- # +y
- (
- (0, 1, 0),
- (0, 1, 1),
- (1, 1, 1),
- (1, 1, 0),
- ),
- # -x
- (
- (0, 0, 0),
- (0, 0, 1),
- (0, 1, 1),
- (0, 1, 0),
- ),
- # +x
- (
- (1, 0, 0),
- (1, 1, 0),
- (1, 1, 1),
- (1, 0, 1),
- ),
- ])
-
- # indexed by [bar, face, vertex, coord]
- polys = np.empty(x.shape + cuboid.shape)
-
- # handle each coordinate separately
- for i, p, dp in [(0, x, dx), (1, y, dy), (2, z, dz)]:
- p = p[..., np.newaxis, np.newaxis]
- dp = dp[..., np.newaxis, np.newaxis]
- polys[..., i] = p + dp * cuboid[..., i]
-
- # collapse the first two axes
- polys = polys.reshape((-1,) + polys.shape[2:])
-
- facecolors = []
- if color is None:
- color = [self._get_patches_for_fill.get_next_color()]
-
- color = list(mcolors.to_rgba_array(color))
-
- if len(color) == len(x):
- # bar colors specified, need to expand to number of faces
- for c in color:
- facecolors.extend([c] * 6)
- else:
- # a single color specified, or face colors specified explicitly
- facecolors = color
- if len(facecolors) < len(x):
- facecolors *= (6 * len(x))
-
- col = art3d.Poly3DCollection(polys,
- zsort=zsort,
- facecolors=facecolors,
- shade=shade,
- lightsource=lightsource,
- *args, **kwargs)
- self.add_collection(col)
-
- self.auto_scale_xyz((minx, maxx), (miny, maxy), (minz, maxz), had_data)
-
- return col
-
- def set_title(self, label, fontdict=None, loc='center', **kwargs):
- # docstring inherited
- ret = super().set_title(label, fontdict=fontdict, loc=loc, **kwargs)
- (x, y) = self.title.get_position()
- self.title.set_y(0.92 * y)
- return ret
-
- @_preprocess_data()
- def quiver(self, X, Y, Z, U, V, W, *,
- length=1, arrow_length_ratio=.3, pivot='tail', normalize=False,
- **kwargs):
- """
- Plot a 3D field of arrows.
-
- The arguments can be array-like or scalars, so long as they can be
- broadcast together. The arguments can also be masked arrays. If an
- element in any of argument is masked, then that corresponding quiver
- element will not be plotted.
-
- Parameters
- ----------
- X, Y, Z : array-like
- The x, y and z coordinates of the arrow locations (default is
- tail of arrow; see *pivot* kwarg).
-
- U, V, W : array-like
- The x, y and z components of the arrow vectors.
-
- length : float, default: 1
- The length of each quiver.
-
- arrow_length_ratio : float, default: 0.3
- The ratio of the arrow head with respect to the quiver.
-
- pivot : {'tail', 'middle', 'tip'}, default: 'tail'
- The part of the arrow that is at the grid point; the arrow
- rotates about this point, hence the name *pivot*.
-
- normalize : bool, default: False
- Whether all arrows are normalized to have the same length, or keep
- the lengths defined by *u*, *v*, and *w*.
-
- data : indexable object, optional
- DATA_PARAMETER_PLACEHOLDER
-
- **kwargs
- Any additional keyword arguments are delegated to
- :class:`.Line3DCollection`
- """
-
- def calc_arrows(UVW):
- # get unit direction vector perpendicular to (u, v, w)
- x = UVW[:, 0]
- y = UVW[:, 1]
- norm = np.linalg.norm(UVW[:, :2], axis=1)
- x_p = np.divide(y, norm, where=norm != 0, out=np.zeros_like(x))
- y_p = np.divide(-x, norm, where=norm != 0, out=np.ones_like(x))
- # compute the two arrowhead direction unit vectors
- rangle = math.radians(15)
- c = math.cos(rangle)
- s = math.sin(rangle)
- # construct the rotation matrices of shape (3, 3, n)
- r13 = y_p * s
- r32 = x_p * s
- r12 = x_p * y_p * (1 - c)
- Rpos = np.array(
- [[c + (x_p ** 2) * (1 - c), r12, r13],
- [r12, c + (y_p ** 2) * (1 - c), -r32],
- [-r13, r32, np.full_like(x_p, c)]])
- # opposite rotation negates all the sin terms
- Rneg = Rpos.copy()
- Rneg[[0, 1, 2, 2], [2, 2, 0, 1]] *= -1
- # Batch n (3, 3) x (3) matrix multiplications ((3, 3, n) x (n, 3)).
- Rpos_vecs = np.einsum("ij...,...j->...i", Rpos, UVW)
- Rneg_vecs = np.einsum("ij...,...j->...i", Rneg, UVW)
- # Stack into (n, 2, 3) result.
- return np.stack([Rpos_vecs, Rneg_vecs], axis=1)
-
- had_data = self.has_data()
-
- input_args = [X, Y, Z, U, V, W]
-
- # extract the masks, if any
- masks = [k.mask for k in input_args
- if isinstance(k, np.ma.MaskedArray)]
- # broadcast to match the shape
- bcast = np.broadcast_arrays(*input_args, *masks)
- input_args = bcast[:6]
- masks = bcast[6:]
- if masks:
- # combine the masks into one
- mask = functools.reduce(np.logical_or, masks)
- # put mask on and compress
- input_args = [np.ma.array(k, mask=mask).compressed()
- for k in input_args]
- else:
- input_args = [np.ravel(k) for k in input_args]
-
- if any(len(v) == 0 for v in input_args):
- # No quivers, so just make an empty collection and return early
- linec = art3d.Line3DCollection([], **kwargs)
- self.add_collection(linec)
- return linec
-
- shaft_dt = np.array([0., length], dtype=float)
- arrow_dt = shaft_dt * arrow_length_ratio
-
- _api.check_in_list(['tail', 'middle', 'tip'], pivot=pivot)
- if pivot == 'tail':
- shaft_dt -= length
- elif pivot == 'middle':
- shaft_dt -= length / 2
-
- XYZ = np.column_stack(input_args[:3])
- UVW = np.column_stack(input_args[3:]).astype(float)
-
- # Normalize rows of UVW
- norm = np.linalg.norm(UVW, axis=1)
-
- # If any row of UVW is all zeros, don't make a quiver for it
- mask = norm > 0
- XYZ = XYZ[mask]
- if normalize:
- UVW = UVW[mask] / norm[mask].reshape((-1, 1))
- else:
- UVW = UVW[mask]
-
- if len(XYZ) > 0:
- # compute the shaft lines all at once with an outer product
- shafts = (XYZ - np.multiply.outer(shaft_dt, UVW)).swapaxes(0, 1)
- # compute head direction vectors, n heads x 2 sides x 3 dimensions
- head_dirs = calc_arrows(UVW)
- # compute all head lines at once, starting from the shaft ends
- heads = shafts[:, :1] - np.multiply.outer(arrow_dt, head_dirs)
- # stack left and right head lines together
- heads = heads.reshape((len(arrow_dt), -1, 3))
- # transpose to get a list of lines
- heads = heads.swapaxes(0, 1)
-
- lines = [*shafts, *heads]
- else:
- lines = []
-
- linec = art3d.Line3DCollection(lines, **kwargs)
- self.add_collection(linec)
-
- self.auto_scale_xyz(XYZ[:, 0], XYZ[:, 1], XYZ[:, 2], had_data)
-
- return linec
-
- quiver3D = quiver
-
- def voxels(self, *args, facecolors=None, edgecolors=None, shade=True,
- lightsource=None, **kwargs):
- """
- ax.voxels([x, y, z,] /, filled, facecolors=None, edgecolors=None, \
-**kwargs)
-
- Plot a set of filled voxels
-
- All voxels are plotted as 1x1x1 cubes on the axis, with
- ``filled[0, 0, 0]`` placed with its lower corner at the origin.
- Occluded faces are not plotted.
-
- Parameters
- ----------
- filled : 3D np.array of bool
- A 3D array of values, with truthy values indicating which voxels
- to fill
-
- x, y, z : 3D np.array, optional
- The coordinates of the corners of the voxels. This should broadcast
- to a shape one larger in every dimension than the shape of
- *filled*. These can be used to plot non-cubic voxels.
-
- If not specified, defaults to increasing integers along each axis,
- like those returned by :func:`~numpy.indices`.
- As indicated by the ``/`` in the function signature, these
- arguments can only be passed positionally.
-
- facecolors, edgecolors : array-like, optional
- The color to draw the faces and edges of the voxels. Can only be
- passed as keyword arguments.
- These parameters can be:
-
- - A single color value, to color all voxels the same color. This
- can be either a string, or a 1D RGB/RGBA array
- - ``None``, the default, to use a single color for the faces, and
- the style default for the edges.
- - A 3D `~numpy.ndarray` of color names, with each item the color
- for the corresponding voxel. The size must match the voxels.
- - A 4D `~numpy.ndarray` of RGB/RGBA data, with the components
- along the last axis.
-
- shade : bool, default: True
- Whether to shade the facecolors.
-
- lightsource : `~matplotlib.colors.LightSource`
- The lightsource to use when *shade* is True.
-
- **kwargs
- Additional keyword arguments to pass onto
- `~mpl_toolkits.mplot3d.art3d.Poly3DCollection`.
-
- Returns
- -------
- faces : dict
- A dictionary indexed by coordinate, where ``faces[i, j, k]`` is a
- `.Poly3DCollection` of the faces drawn for the voxel
- ``filled[i, j, k]``. If no faces were drawn for a given voxel,
- either because it was not asked to be drawn, or it is fully
- occluded, then ``(i, j, k) not in faces``.
-
- Examples
- --------
- .. plot:: gallery/mplot3d/voxels.py
- .. plot:: gallery/mplot3d/voxels_rgb.py
- .. plot:: gallery/mplot3d/voxels_torus.py
- .. plot:: gallery/mplot3d/voxels_numpy_logo.py
- """
-
- # work out which signature we should be using, and use it to parse
- # the arguments. Name must be voxels for the correct error message
- if len(args) >= 3:
- # underscores indicate position only
- def voxels(__x, __y, __z, filled, **kwargs):
- return (__x, __y, __z), filled, kwargs
- else:
- def voxels(filled, **kwargs):
- return None, filled, kwargs
-
- xyz, filled, kwargs = voxels(*args, **kwargs)
-
- # check dimensions
- if filled.ndim != 3:
- raise ValueError("Argument filled must be 3-dimensional")
- size = np.array(filled.shape, dtype=np.intp)
-
- # check xyz coordinates, which are one larger than the filled shape
- coord_shape = tuple(size + 1)
- if xyz is None:
- x, y, z = np.indices(coord_shape)
- else:
- x, y, z = (np.broadcast_to(c, coord_shape) for c in xyz)
-
- def _broadcast_color_arg(color, name):
- if np.ndim(color) in (0, 1):
- # single color, like "red" or [1, 0, 0]
- return np.broadcast_to(color, filled.shape + np.shape(color))
- elif np.ndim(color) in (3, 4):
- # 3D array of strings, or 4D array with last axis rgb
- if np.shape(color)[:3] != filled.shape:
- raise ValueError(
- f"When multidimensional, {name} must match the shape "
- "of filled")
- return color
- else:
- raise ValueError(f"Invalid {name} argument")
-
- # broadcast and default on facecolors
- if facecolors is None:
- facecolors = self._get_patches_for_fill.get_next_color()
- facecolors = _broadcast_color_arg(facecolors, 'facecolors')
-
- # broadcast but no default on edgecolors
- edgecolors = _broadcast_color_arg(edgecolors, 'edgecolors')
-
- # scale to the full array, even if the data is only in the center
- self.auto_scale_xyz(x, y, z)
-
- # points lying on corners of a square
- square = np.array([
- [0, 0, 0],
- [1, 0, 0],
- [1, 1, 0],
- [0, 1, 0],
- ], dtype=np.intp)
-
- voxel_faces = defaultdict(list)
-
- def permutation_matrices(n):
- """Generate cyclic permutation matrices."""
- mat = np.eye(n, dtype=np.intp)
- for i in range(n):
- yield mat
- mat = np.roll(mat, 1, axis=0)
-
- # iterate over each of the YZ, ZX, and XY orientations, finding faces
- # to render
- for permute in permutation_matrices(3):
- # find the set of ranges to iterate over
- pc, qc, rc = permute.T.dot(size)
- pinds = np.arange(pc)
- qinds = np.arange(qc)
- rinds = np.arange(rc)
-
- square_rot_pos = square.dot(permute.T)
- square_rot_neg = square_rot_pos[::-1]
-
- # iterate within the current plane
- for p in pinds:
- for q in qinds:
- # iterate perpendicularly to the current plane, handling
- # boundaries. We only draw faces between a voxel and an
- # empty space, to avoid drawing internal faces.
-
- # draw lower faces
- p0 = permute.dot([p, q, 0])
- i0 = tuple(p0)
- if filled[i0]:
- voxel_faces[i0].append(p0 + square_rot_neg)
-
- # draw middle faces
- for r1, r2 in zip(rinds[:-1], rinds[1:]):
- p1 = permute.dot([p, q, r1])
- p2 = permute.dot([p, q, r2])
-
- i1 = tuple(p1)
- i2 = tuple(p2)
-
- if filled[i1] and not filled[i2]:
- voxel_faces[i1].append(p2 + square_rot_pos)
- elif not filled[i1] and filled[i2]:
- voxel_faces[i2].append(p2 + square_rot_neg)
-
- # draw upper faces
- pk = permute.dot([p, q, rc-1])
- pk2 = permute.dot([p, q, rc])
- ik = tuple(pk)
- if filled[ik]:
- voxel_faces[ik].append(pk2 + square_rot_pos)
-
- # iterate over the faces, and generate a Poly3DCollection for each
- # voxel
- polygons = {}
- for coord, faces_inds in voxel_faces.items():
- # convert indices into 3D positions
- if xyz is None:
- faces = faces_inds
- else:
- faces = []
- for face_inds in faces_inds:
- ind = face_inds[:, 0], face_inds[:, 1], face_inds[:, 2]
- face = np.empty(face_inds.shape)
- face[:, 0] = x[ind]
- face[:, 1] = y[ind]
- face[:, 2] = z[ind]
- faces.append(face)
-
- # shade the faces
- facecolor = facecolors[coord]
- edgecolor = edgecolors[coord]
-
- poly = art3d.Poly3DCollection(
- faces, facecolors=facecolor, edgecolors=edgecolor,
- shade=shade, lightsource=lightsource, **kwargs)
- self.add_collection3d(poly)
- polygons[coord] = poly
-
- return polygons
-
- @_preprocess_data(replace_names=["x", "y", "z", "xerr", "yerr", "zerr"])
- def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='',
- barsabove=False, errorevery=1, ecolor=None, elinewidth=None,
- capsize=None, capthick=None, xlolims=False, xuplims=False,
- ylolims=False, yuplims=False, zlolims=False, zuplims=False,
- **kwargs):
- """
- Plot lines and/or markers with errorbars around them.
-
- *x*/*y*/*z* define the data locations, and *xerr*/*yerr*/*zerr* define
- the errorbar sizes. By default, this draws the data markers/lines as
- well the errorbars. Use fmt='none' to draw errorbars only.
-
- Parameters
- ----------
- x, y, z : float or array-like
- The data positions.
-
- xerr, yerr, zerr : float or array-like, shape (N,) or (2, N), optional
- The errorbar sizes:
-
- - scalar: Symmetric +/- values for all data points.
- - shape(N,): Symmetric +/-values for each data point.
- - shape(2, N): Separate - and + values for each bar. First row
- contains the lower errors, the second row contains the upper
- errors.
- - *None*: No errorbar.
-
- Note that all error arrays should have *positive* values.
-
- fmt : str, default: ''
- The format for the data points / data lines. See `.plot` for
- details.
-
- Use 'none' (case-insensitive) to plot errorbars without any data
- markers.
-
- ecolor : color, default: None
- The color of the errorbar lines. If None, use the color of the
- line connecting the markers.
-
- elinewidth : float, default: None
- The linewidth of the errorbar lines. If None, the linewidth of
- the current style is used.
-
- capsize : float, default: :rc:`errorbar.capsize`
- The length of the error bar caps in points.
-
- capthick : float, default: None
- An alias to the keyword argument *markeredgewidth* (a.k.a. *mew*).
- This setting is a more sensible name for the property that
- controls the thickness of the error bar cap in points. For
- backwards compatibility, if *mew* or *markeredgewidth* are given,
- then they will over-ride *capthick*. This may change in future
- releases.
-
- barsabove : bool, default: False
- If True, will plot the errorbars above the plot
- symbols. Default is below.
-
- xlolims, ylolims, zlolims : bool, default: False
- These arguments can be used to indicate that a value gives only
- lower limits. In that case a caret symbol is used to indicate
- this. *lims*-arguments may be scalars, or array-likes of the same
- length as the errors. To use limits with inverted axes,
- `~.Axes.set_xlim` or `~.Axes.set_ylim` must be called before
- `errorbar`. Note the tricky parameter names: setting e.g.
- *ylolims* to True means that the y-value is a *lower* limit of the
- True value, so, only an *upward*-pointing arrow will be drawn!
-
- xuplims, yuplims, zuplims : bool, default: False
- Same as above, but for controlling the upper limits.
-
- errorevery : int or (int, int), default: 1
- draws error bars on a subset of the data. *errorevery* =N draws
- error bars on the points (x[::N], y[::N], z[::N]).
- *errorevery* =(start, N) draws error bars on the points
- (x[start::N], y[start::N], z[start::N]). e.g. *errorevery* =(6, 3)
- adds error bars to the data at (x[6], x[9], x[12], x[15], ...).
- Used to avoid overlapping error bars when two series share x-axis
- values.
-
- Returns
- -------
- errlines : list
- List of `~mpl_toolkits.mplot3d.art3d.Line3DCollection` instances
- each containing an errorbar line.
- caplines : list
- List of `~mpl_toolkits.mplot3d.art3d.Line3D` instances each
- containing a capline object.
- limmarks : list
- List of `~mpl_toolkits.mplot3d.art3d.Line3D` instances each
- containing a marker with an upper or lower limit.
-
- Other Parameters
- ----------------
- data : indexable object, optional
- DATA_PARAMETER_PLACEHOLDER
-
- **kwargs
- All other keyword arguments for styling errorbar lines are passed
- `~mpl_toolkits.mplot3d.art3d.Line3DCollection`.
-
- Examples
- --------
- .. plot:: gallery/mplot3d/errorbar3d.py
- """
- had_data = self.has_data()
-
- kwargs = cbook.normalize_kwargs(kwargs, mlines.Line2D)
- # Drop anything that comes in as None to use the default instead.
- kwargs = {k: v for k, v in kwargs.items() if v is not None}
- kwargs.setdefault('zorder', 2)
-
- self._process_unit_info([("x", x), ("y", y), ("z", z)], kwargs,
- convert=False)
-
- # make sure all the args are iterable; use lists not arrays to
- # preserve units
- x = x if np.iterable(x) else [x]
- y = y if np.iterable(y) else [y]
- z = z if np.iterable(z) else [z]
-
- if not len(x) == len(y) == len(z):
- raise ValueError("'x', 'y', and 'z' must have the same size")
-
- everymask = self._errorevery_to_mask(x, errorevery)
-
- label = kwargs.pop("label", None)
- kwargs['label'] = '_nolegend_'
-
- # Create the main line and determine overall kwargs for child artists.
- # We avoid calling self.plot() directly, or self._get_lines(), because
- # that would call self._process_unit_info again, and do other indirect
- # data processing.
- (data_line, base_style), = self._get_lines._plot_args(
- self, (x, y) if fmt == '' else (x, y, fmt), kwargs, return_kwargs=True)
- art3d.line_2d_to_3d(data_line, zs=z)
-
- # Do this after creating `data_line` to avoid modifying `base_style`.
- if barsabove:
- data_line.set_zorder(kwargs['zorder'] - .1)
- else:
- data_line.set_zorder(kwargs['zorder'] + .1)
-
- # Add line to plot, or throw it away and use it to determine kwargs.
- if fmt.lower() != 'none':
- self.add_line(data_line)
- else:
- data_line = None
- # Remove alpha=0 color that _process_plot_format returns.
- base_style.pop('color')
-
- if 'color' not in base_style:
- base_style['color'] = 'C0'
- if ecolor is None:
- ecolor = base_style['color']
-
- # Eject any line-specific information from format string, as it's not
- # needed for bars or caps.
- for key in ['marker', 'markersize', 'markerfacecolor',
- 'markeredgewidth', 'markeredgecolor', 'markevery',
- 'linestyle', 'fillstyle', 'drawstyle', 'dash_capstyle',
- 'dash_joinstyle', 'solid_capstyle', 'solid_joinstyle']:
- base_style.pop(key, None)
-
- # Make the style dict for the line collections (the bars).
- eb_lines_style = {**base_style, 'color': ecolor}
-
- if elinewidth:
- eb_lines_style['linewidth'] = elinewidth
- elif 'linewidth' in kwargs:
- eb_lines_style['linewidth'] = kwargs['linewidth']
-
- for key in ('transform', 'alpha', 'zorder', 'rasterized'):
- if key in kwargs:
- eb_lines_style[key] = kwargs[key]
-
- # Make the style dict for caps (the "hats").
- eb_cap_style = {**base_style, 'linestyle': 'None'}
- if capsize is None:
- capsize = mpl.rcParams["errorbar.capsize"]
- if capsize > 0:
- eb_cap_style['markersize'] = 2. * capsize
- if capthick is not None:
- eb_cap_style['markeredgewidth'] = capthick
- eb_cap_style['color'] = ecolor
-
- def _apply_mask(arrays, mask):
- # Return, for each array in *arrays*, the elements for which *mask*
- # is True, without using fancy indexing.
- return [[*itertools.compress(array, mask)] for array in arrays]
-
- def _extract_errs(err, data, lomask, himask):
- # For separate +/- error values we need to unpack err
- if len(err.shape) == 2:
- low_err, high_err = err
- else:
- low_err, high_err = err, err
-
- lows = np.where(lomask | ~everymask, data, data - low_err)
- highs = np.where(himask | ~everymask, data, data + high_err)
-
- return lows, highs
-
- # collect drawn items while looping over the three coordinates
- errlines, caplines, limmarks = [], [], []
-
- # list of endpoint coordinates, used for auto-scaling
- coorderrs = []
-
- # define the markers used for errorbar caps and limits below
- # the dictionary key is mapped by the `i_xyz` helper dictionary
- capmarker = {0: '|', 1: '|', 2: '_'}
- i_xyz = {'x': 0, 'y': 1, 'z': 2}
-
- # Calculate marker size from points to quiver length. Because these are
- # not markers, and 3D Axes do not use the normal transform stack, this
- # is a bit involved. Since the quiver arrows will change size as the
- # scene is rotated, they are given a standard size based on viewing
- # them directly in planar form.
- quiversize = eb_cap_style.get('markersize',
- mpl.rcParams['lines.markersize']) ** 2
- quiversize *= self.figure.dpi / 72
- quiversize = self.transAxes.inverted().transform([
- (0, 0), (quiversize, quiversize)])
- quiversize = np.mean(np.diff(quiversize, axis=0))
- # quiversize is now in Axes coordinates, and to convert back to data
- # coordinates, we need to run it through the inverse 3D transform. For
- # consistency, this uses a fixed elevation, azimuth, and roll.
- with cbook._setattr_cm(self, elev=0, azim=0, roll=0):
- invM = np.linalg.inv(self.get_proj())
- # elev=azim=roll=0 produces the Y-Z plane, so quiversize in 2D 'x' is
- # 'y' in 3D, hence the 1 index.
- quiversize = np.dot(invM, [quiversize, 0, 0, 0])[1]
- # Quivers use a fixed 15-degree arrow head, so scale up the length so
- # that the size corresponds to the base. In other words, this constant
- # corresponds to the equation tan(15) = (base / 2) / (arrow length).
- quiversize *= 1.8660254037844388
- eb_quiver_style = {**eb_cap_style,
- 'length': quiversize, 'arrow_length_ratio': 1}
- eb_quiver_style.pop('markersize', None)
-
- # loop over x-, y-, and z-direction and draw relevant elements
- for zdir, data, err, lolims, uplims in zip(
- ['x', 'y', 'z'], [x, y, z], [xerr, yerr, zerr],
- [xlolims, ylolims, zlolims], [xuplims, yuplims, zuplims]):
-
- dir_vector = art3d.get_dir_vector(zdir)
- i_zdir = i_xyz[zdir]
-
- if err is None:
- continue
-
- if not np.iterable(err):
- err = [err] * len(data)
-
- err = np.atleast_1d(err)
-
- # arrays fine here, they are booleans and hence not units
- lolims = np.broadcast_to(lolims, len(data)).astype(bool)
- uplims = np.broadcast_to(uplims, len(data)).astype(bool)
-
- # a nested list structure that expands to (xl,xh),(yl,yh),(zl,zh),
- # where x/y/z and l/h correspond to dimensions and low/high
- # positions of errorbars in a dimension we're looping over
- coorderr = [
- _extract_errs(err * dir_vector[i], coord, lolims, uplims)
- for i, coord in enumerate([x, y, z])]
- (xl, xh), (yl, yh), (zl, zh) = coorderr
-
- # draws capmarkers - flat caps orthogonal to the error bars
- nolims = ~(lolims | uplims)
- if nolims.any() and capsize > 0:
- lo_caps_xyz = _apply_mask([xl, yl, zl], nolims & everymask)
- hi_caps_xyz = _apply_mask([xh, yh, zh], nolims & everymask)
-
- # setting '_' for z-caps and '|' for x- and y-caps;
- # these markers will rotate as the viewing angle changes
- cap_lo = art3d.Line3D(*lo_caps_xyz, ls='',
- marker=capmarker[i_zdir],
- **eb_cap_style)
- cap_hi = art3d.Line3D(*hi_caps_xyz, ls='',
- marker=capmarker[i_zdir],
- **eb_cap_style)
- self.add_line(cap_lo)
- self.add_line(cap_hi)
- caplines.append(cap_lo)
- caplines.append(cap_hi)
-
- if lolims.any():
- xh0, yh0, zh0 = _apply_mask([xh, yh, zh], lolims & everymask)
- self.quiver(xh0, yh0, zh0, *dir_vector, **eb_quiver_style)
- if uplims.any():
- xl0, yl0, zl0 = _apply_mask([xl, yl, zl], uplims & everymask)
- self.quiver(xl0, yl0, zl0, *-dir_vector, **eb_quiver_style)
-
- errline = art3d.Line3DCollection(np.array(coorderr).T,
- **eb_lines_style)
- self.add_collection(errline)
- errlines.append(errline)
- coorderrs.append(coorderr)
-
- coorderrs = np.array(coorderrs)
-
- def _digout_minmax(err_arr, coord_label):
- return (np.nanmin(err_arr[:, i_xyz[coord_label], :, :]),
- np.nanmax(err_arr[:, i_xyz[coord_label], :, :]))
-
- minx, maxx = _digout_minmax(coorderrs, 'x')
- miny, maxy = _digout_minmax(coorderrs, 'y')
- minz, maxz = _digout_minmax(coorderrs, 'z')
- self.auto_scale_xyz((minx, maxx), (miny, maxy), (minz, maxz), had_data)
-
- # Adapting errorbar containers for 3d case, assuming z-axis points "up"
- errorbar_container = mcontainer.ErrorbarContainer(
- (data_line, tuple(caplines), tuple(errlines)),
- has_xerr=(xerr is not None or yerr is not None),
- has_yerr=(zerr is not None),
- label=label)
- self.containers.append(errorbar_container)
-
- return errlines, caplines, limmarks
-
- @_api.make_keyword_only("3.8", "call_axes_locator")
- def get_tightbbox(self, renderer=None, call_axes_locator=True,
- bbox_extra_artists=None, *, for_layout_only=False):
- ret = super().get_tightbbox(renderer,
- call_axes_locator=call_axes_locator,
- bbox_extra_artists=bbox_extra_artists,
- for_layout_only=for_layout_only)
- batch = [ret]
- if self._axis3don:
- for axis in self._axis_map.values():
- if axis.get_visible():
- axis_bb = martist._get_tightbbox_for_layout_only(
- axis, renderer)
- if axis_bb:
- batch.append(axis_bb)
- return mtransforms.Bbox.union(batch)
-
- @_preprocess_data()
- def stem(self, x, y, z, *, linefmt='C0-', markerfmt='C0o', basefmt='C3-',
- bottom=0, label=None, orientation='z'):
- """
- Create a 3D stem plot.
-
- A stem plot draws lines perpendicular to a baseline, and places markers
- at the heads. By default, the baseline is defined by *x* and *y*, and
- stems are drawn vertically from *bottom* to *z*.
-
- Parameters
- ----------
- x, y, z : array-like
- The positions of the heads of the stems. The stems are drawn along
- the *orientation*-direction from the baseline at *bottom* (in the
- *orientation*-coordinate) to the heads. By default, the *x* and *y*
- positions are used for the baseline and *z* for the head position,
- but this can be changed by *orientation*.
-
- linefmt : str, default: 'C0-'
- A string defining the properties of the vertical lines. Usually,
- this will be a color or a color and a linestyle:
-
- ========= =============
- Character Line Style
- ========= =============
- ``'-'`` solid line
- ``'--'`` dashed line
- ``'-.'`` dash-dot line
- ``':'`` dotted line
- ========= =============
-
- Note: While it is technically possible to specify valid formats
- other than color or color and linestyle (e.g. 'rx' or '-.'), this
- is beyond the intention of the method and will most likely not
- result in a reasonable plot.
-
- markerfmt : str, default: 'C0o'
- A string defining the properties of the markers at the stem heads.
-
- basefmt : str, default: 'C3-'
- A format string defining the properties of the baseline.
-
- bottom : float, default: 0
- The position of the baseline, in *orientation*-coordinates.
-
- label : str, default: None
- The label to use for the stems in legends.
-
- orientation : {'x', 'y', 'z'}, default: 'z'
- The direction along which stems are drawn.
-
- data : indexable object, optional
- DATA_PARAMETER_PLACEHOLDER
-
- Returns
- -------
- `.StemContainer`
- The container may be treated like a tuple
- (*markerline*, *stemlines*, *baseline*)
-
- Examples
- --------
- .. plot:: gallery/mplot3d/stem3d_demo.py
- """
-
- from matplotlib.container import StemContainer
-
- had_data = self.has_data()
-
- _api.check_in_list(['x', 'y', 'z'], orientation=orientation)
-
- xlim = (np.min(x), np.max(x))
- ylim = (np.min(y), np.max(y))
- zlim = (np.min(z), np.max(z))
-
- # Determine the appropriate plane for the baseline and the direction of
- # stemlines based on the value of orientation.
- if orientation == 'x':
- basex, basexlim = y, ylim
- basey, baseylim = z, zlim
- lines = [[(bottom, thisy, thisz), (thisx, thisy, thisz)]
- for thisx, thisy, thisz in zip(x, y, z)]
- elif orientation == 'y':
- basex, basexlim = x, xlim
- basey, baseylim = z, zlim
- lines = [[(thisx, bottom, thisz), (thisx, thisy, thisz)]
- for thisx, thisy, thisz in zip(x, y, z)]
- else:
- basex, basexlim = x, xlim
- basey, baseylim = y, ylim
- lines = [[(thisx, thisy, bottom), (thisx, thisy, thisz)]
- for thisx, thisy, thisz in zip(x, y, z)]
-
- # Determine style for stem lines.
- linestyle, linemarker, linecolor = _process_plot_format(linefmt)
- if linestyle is None:
- linestyle = mpl.rcParams['lines.linestyle']
-
- # Plot everything in required order.
- baseline, = self.plot(basex, basey, basefmt, zs=bottom,
- zdir=orientation, label='_nolegend_')
- stemlines = art3d.Line3DCollection(
- lines, linestyles=linestyle, colors=linecolor, label='_nolegend_')
- self.add_collection(stemlines)
- markerline, = self.plot(x, y, z, markerfmt, label='_nolegend_')
-
- stem_container = StemContainer((markerline, stemlines, baseline),
- label=label)
- self.add_container(stem_container)
-
- jx, jy, jz = art3d.juggle_axes(basexlim, baseylim, [bottom, bottom],
- orientation)
- self.auto_scale_xyz([*jx, *xlim], [*jy, *ylim], [*jz, *zlim], had_data)
-
- return stem_container
-
- stem3D = stem
-
-
-def get_test_data(delta=0.05):
- """Return a tuple X, Y, Z with a test data set."""
- x = y = np.arange(-3.0, 3.0, delta)
- X, Y = np.meshgrid(x, y)
-
- Z1 = np.exp(-(X**2 + Y**2) / 2) / (2 * np.pi)
- Z2 = (np.exp(-(((X - 1) / 1.5)**2 + ((Y - 1) / 0.5)**2) / 2) /
- (2 * np.pi * 0.5 * 1.5))
- Z = Z2 - Z1
-
- X = X * 10
- Y = Y * 10
- Z = Z * 500
- return X, Y, Z
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/axis3d.py b/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/axis3d.py
deleted file mode 100644
index 4c5fa8a9c90..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/axis3d.py
+++ /dev/null
@@ -1,753 +0,0 @@
-# axis3d.py, original mplot3d version by John Porter
-# Created: 23 Sep 2005
-# Parts rewritten by Reinier Heeres <reinier@heeres.eu>
-
-import inspect
-
-import numpy as np
-
-import matplotlib as mpl
-from matplotlib import (
- _api, artist, lines as mlines, axis as maxis, patches as mpatches,
- transforms as mtransforms, colors as mcolors)
-from . import art3d, proj3d
-
-
-def _move_from_center(coord, centers, deltas, axmask=(True, True, True)):
- """
- For each coordinate where *axmask* is True, move *coord* away from
- *centers* by *deltas*.
- """
- coord = np.asarray(coord)
- return coord + axmask * np.copysign(1, coord - centers) * deltas
-
-
-def _tick_update_position(tick, tickxs, tickys, labelpos):
- """Update tick line and label position and style."""
-
- tick.label1.set_position(labelpos)
- tick.label2.set_position(labelpos)
- tick.tick1line.set_visible(True)
- tick.tick2line.set_visible(False)
- tick.tick1line.set_linestyle('-')
- tick.tick1line.set_marker('')
- tick.tick1line.set_data(tickxs, tickys)
- tick.gridline.set_data([0], [0])
-
-
-class Axis(maxis.XAxis):
- """An Axis class for the 3D plots."""
- # These points from the unit cube make up the x, y and z-planes
- _PLANES = (
- (0, 3, 7, 4), (1, 2, 6, 5), # yz planes
- (0, 1, 5, 4), (3, 2, 6, 7), # xz planes
- (0, 1, 2, 3), (4, 5, 6, 7), # xy planes
- )
-
- # Some properties for the axes
- _AXINFO = {
- 'x': {'i': 0, 'tickdir': 1, 'juggled': (1, 0, 2)},
- 'y': {'i': 1, 'tickdir': 0, 'juggled': (0, 1, 2)},
- 'z': {'i': 2, 'tickdir': 0, 'juggled': (0, 2, 1)},
- }
-
- def _old_init(self, adir, v_intervalx, d_intervalx, axes, *args,
- rotate_label=None, **kwargs):
- return locals()
-
- def _new_init(self, axes, *, rotate_label=None, **kwargs):
- return locals()
-
- def __init__(self, *args, **kwargs):
- params = _api.select_matching_signature(
- [self._old_init, self._new_init], *args, **kwargs)
- if "adir" in params:
- _api.warn_deprecated(
- "3.6", message=f"The signature of 3D Axis constructors has "
- f"changed in %(since)s; the new signature is "
- f"{inspect.signature(type(self).__init__)}", pending=True)
- if params["adir"] != self.axis_name:
- raise ValueError(f"Cannot instantiate {type(self).__name__} "
- f"with adir={params['adir']!r}")
- axes = params["axes"]
- rotate_label = params["rotate_label"]
- args = params.get("args", ())
- kwargs = params["kwargs"]
-
- name = self.axis_name
-
- self._label_position = 'default'
- self._tick_position = 'default'
-
- # This is a temporary member variable.
- # Do not depend on this existing in future releases!
- self._axinfo = self._AXINFO[name].copy()
- # Common parts
- self._axinfo.update({
- 'label': {'va': 'center', 'ha': 'center',
- 'rotation_mode': 'anchor'},
- 'color': mpl.rcParams[f'axes3d.{name}axis.panecolor'],
- 'tick': {
- 'inward_factor': 0.2,
- 'outward_factor': 0.1,
- },
- })
-
- if mpl.rcParams['_internal.classic_mode']:
- self._axinfo.update({
- 'axisline': {'linewidth': 0.75, 'color': (0, 0, 0, 1)},
- 'grid': {
- 'color': (0.9, 0.9, 0.9, 1),
- 'linewidth': 1.0,
- 'linestyle': '-',
- },
- })
- self._axinfo['tick'].update({
- 'linewidth': {
- True: mpl.rcParams['lines.linewidth'], # major
- False: mpl.rcParams['lines.linewidth'], # minor
- }
- })
- else:
- self._axinfo.update({
- 'axisline': {
- 'linewidth': mpl.rcParams['axes.linewidth'],
- 'color': mpl.rcParams['axes.edgecolor'],
- },
- 'grid': {
- 'color': mpl.rcParams['grid.color'],
- 'linewidth': mpl.rcParams['grid.linewidth'],
- 'linestyle': mpl.rcParams['grid.linestyle'],
- },
- })
- self._axinfo['tick'].update({
- 'linewidth': {
- True: ( # major
- mpl.rcParams['xtick.major.width'] if name in 'xz'
- else mpl.rcParams['ytick.major.width']),
- False: ( # minor
- mpl.rcParams['xtick.minor.width'] if name in 'xz'
- else mpl.rcParams['ytick.minor.width']),
- }
- })
-
- super().__init__(axes, *args, **kwargs)
-
- # data and viewing intervals for this direction
- if "d_intervalx" in params:
- self.set_data_interval(*params["d_intervalx"])
- if "v_intervalx" in params:
- self.set_view_interval(*params["v_intervalx"])
- self.set_rotate_label(rotate_label)
- self._init3d() # Inline after init3d deprecation elapses.
-
- __init__.__signature__ = inspect.signature(_new_init)
- adir = _api.deprecated("3.6", pending=True)(
- property(lambda self: self.axis_name))
-
- def _init3d(self):
- self.line = mlines.Line2D(
- xdata=(0, 0), ydata=(0, 0),
- linewidth=self._axinfo['axisline']['linewidth'],
- color=self._axinfo['axisline']['color'],
- antialiased=True)
-
- # Store dummy data in Polygon object
- self.pane = mpatches.Polygon([[0, 0], [0, 1]], closed=False)
- self.set_pane_color(self._axinfo['color'])
-
- self.axes._set_artist_props(self.line)
- self.axes._set_artist_props(self.pane)
- self.gridlines = art3d.Line3DCollection([])
- self.axes._set_artist_props(self.gridlines)
- self.axes._set_artist_props(self.label)
- self.axes._set_artist_props(self.offsetText)
- # Need to be able to place the label at the correct location
- self.label._transform = self.axes.transData
- self.offsetText._transform = self.axes.transData
-
- @_api.deprecated("3.6", pending=True)
- def init3d(self): # After deprecation elapses, inline _init3d to __init__.
- self._init3d()
-
- def get_major_ticks(self, numticks=None):
- ticks = super().get_major_ticks(numticks)
- for t in ticks:
- for obj in [
- t.tick1line, t.tick2line, t.gridline, t.label1, t.label2]:
- obj.set_transform(self.axes.transData)
- return ticks
-
- def get_minor_ticks(self, numticks=None):
- ticks = super().get_minor_ticks(numticks)
- for t in ticks:
- for obj in [
- t.tick1line, t.tick2line, t.gridline, t.label1, t.label2]:
- obj.set_transform(self.axes.transData)
- return ticks
-
- def set_ticks_position(self, position):
- """
- Set the ticks position.
-
- Parameters
- ----------
- position : {'lower', 'upper', 'both', 'default', 'none'}
- The position of the bolded axis lines, ticks, and tick labels.
- """
- if position in ['top', 'bottom']:
- _api.warn_deprecated('3.8', name=f'{position=}',
- obj_type='argument value',
- alternative="'upper' or 'lower'")
- return
- _api.check_in_list(['lower', 'upper', 'both', 'default', 'none'],
- position=position)
- self._tick_position = position
-
- def get_ticks_position(self):
- """
- Get the ticks position.
-
- Returns
- -------
- str : {'lower', 'upper', 'both', 'default', 'none'}
- The position of the bolded axis lines, ticks, and tick labels.
- """
- return self._tick_position
-
- def set_label_position(self, position):
- """
- Set the label position.
-
- Parameters
- ----------
- position : {'lower', 'upper', 'both', 'default', 'none'}
- The position of the axis label.
- """
- if position in ['top', 'bottom']:
- _api.warn_deprecated('3.8', name=f'{position=}',
- obj_type='argument value',
- alternative="'upper' or 'lower'")
- return
- _api.check_in_list(['lower', 'upper', 'both', 'default', 'none'],
- position=position)
- self._label_position = position
-
- def get_label_position(self):
- """
- Get the label position.
-
- Returns
- -------
- str : {'lower', 'upper', 'both', 'default', 'none'}
- The position of the axis label.
- """
- return self._label_position
-
- def set_pane_color(self, color, alpha=None):
- """
- Set pane color.
-
- Parameters
- ----------
- color : color
- Color for axis pane.
- alpha : float, optional
- Alpha value for axis pane. If None, base it on *color*.
- """
- color = mcolors.to_rgba(color, alpha)
- self._axinfo['color'] = color
- self.pane.set_edgecolor(color)
- self.pane.set_facecolor(color)
- self.pane.set_alpha(color[-1])
- self.stale = True
-
- def set_rotate_label(self, val):
- """
- Whether to rotate the axis label: True, False or None.
- If set to None the label will be rotated if longer than 4 chars.
- """
- self._rotate_label = val
- self.stale = True
-
- def get_rotate_label(self, text):
- if self._rotate_label is not None:
- return self._rotate_label
- else:
- return len(text) > 4
-
- def _get_coord_info(self, renderer):
- mins, maxs = np.array([
- self.axes.get_xbound(),
- self.axes.get_ybound(),
- self.axes.get_zbound(),
- ]).T
-
- # Get the mean value for each bound:
- centers = 0.5 * (maxs + mins)
-
- # Add a small offset between min/max point and the edge of the plot:
- deltas = (maxs - mins) / 12
- mins -= 0.25 * deltas
- maxs += 0.25 * deltas
-
- # Project the bounds along the current position of the cube:
- bounds = mins[0], maxs[0], mins[1], maxs[1], mins[2], maxs[2]
- bounds_proj = self.axes._tunit_cube(bounds, self.axes.M)
-
- # Determine which one of the parallel planes are higher up:
- means_z0 = np.zeros(3)
- means_z1 = np.zeros(3)
- for i in range(3):
- means_z0[i] = np.mean(bounds_proj[self._PLANES[2 * i], 2])
- means_z1[i] = np.mean(bounds_proj[self._PLANES[2 * i + 1], 2])
- highs = means_z0 < means_z1
-
- # Special handling for edge-on views
- equals = np.abs(means_z0 - means_z1) <= np.finfo(float).eps
- if np.sum(equals) == 2:
- vertical = np.where(~equals)[0][0]
- if vertical == 2: # looking at XY plane
- highs = np.array([True, True, highs[2]])
- elif vertical == 1: # looking at XZ plane
- highs = np.array([True, highs[1], False])
- elif vertical == 0: # looking at YZ plane
- highs = np.array([highs[0], False, False])
-
- return mins, maxs, centers, deltas, bounds_proj, highs
-
- def _get_axis_line_edge_points(self, minmax, maxmin, position=None):
- """Get the edge points for the black bolded axis line."""
- # When changing vertical axis some of the axes has to be
- # moved to the other plane so it looks the same as if the z-axis
- # was the vertical axis.
- mb = [minmax, maxmin] # line from origin to nearest corner to camera
- mb_rev = mb[::-1]
- mm = [[mb, mb_rev, mb_rev], [mb_rev, mb_rev, mb], [mb, mb, mb]]
- mm = mm[self.axes._vertical_axis][self._axinfo["i"]]
-
- juggled = self._axinfo["juggled"]
- edge_point_0 = mm[0].copy() # origin point
-
- if ((position == 'lower' and mm[1][juggled[-1]] < mm[0][juggled[-1]]) or
- (position == 'upper' and mm[1][juggled[-1]] > mm[0][juggled[-1]])):
- edge_point_0[juggled[-1]] = mm[1][juggled[-1]]
- else:
- edge_point_0[juggled[0]] = mm[1][juggled[0]]
-
- edge_point_1 = edge_point_0.copy()
- edge_point_1[juggled[1]] = mm[1][juggled[1]]
-
- return edge_point_0, edge_point_1
-
- def _get_all_axis_line_edge_points(self, minmax, maxmin, axis_position=None):
- # Determine edge points for the axis lines
- edgep1s = []
- edgep2s = []
- position = []
- if axis_position in (None, 'default'):
- edgep1, edgep2 = self._get_axis_line_edge_points(minmax, maxmin)
- edgep1s = [edgep1]
- edgep2s = [edgep2]
- position = ['default']
- else:
- edgep1_l, edgep2_l = self._get_axis_line_edge_points(minmax, maxmin,
- position='lower')
- edgep1_u, edgep2_u = self._get_axis_line_edge_points(minmax, maxmin,
- position='upper')
- if axis_position in ('lower', 'both'):
- edgep1s.append(edgep1_l)
- edgep2s.append(edgep2_l)
- position.append('lower')
- if axis_position in ('upper', 'both'):
- edgep1s.append(edgep1_u)
- edgep2s.append(edgep2_u)
- position.append('upper')
- return edgep1s, edgep2s, position
-
- def _get_tickdir(self, position):
- """
- Get the direction of the tick.
-
- Parameters
- ----------
- position : str, optional : {'upper', 'lower', 'default'}
- The position of the axis.
-
- Returns
- -------
- tickdir : int
- Index which indicates which coordinate the tick line will
- align with.
- """
- _api.check_in_list(('upper', 'lower', 'default'), position=position)
-
- # TODO: Move somewhere else where it's triggered less:
- tickdirs_base = [v["tickdir"] for v in self._AXINFO.values()] # default
- elev_mod = np.mod(self.axes.elev + 180, 360) - 180
- azim_mod = np.mod(self.axes.azim, 360)
- if position == 'upper':
- if elev_mod >= 0:
- tickdirs_base = [2, 2, 0]
- else:
- tickdirs_base = [1, 0, 0]
- if 0 <= azim_mod < 180:
- tickdirs_base[2] = 1
- elif position == 'lower':
- if elev_mod >= 0:
- tickdirs_base = [1, 0, 1]
- else:
- tickdirs_base = [2, 2, 1]
- if 0 <= azim_mod < 180:
- tickdirs_base[2] = 0
- info_i = [v["i"] for v in self._AXINFO.values()]
-
- i = self._axinfo["i"]
- vert_ax = self.axes._vertical_axis
- j = vert_ax - 2
- # default: tickdir = [[1, 2, 1], [2, 2, 0], [1, 0, 0]][vert_ax][i]
- tickdir = np.roll(info_i, -j)[np.roll(tickdirs_base, j)][i]
- return tickdir
-
- def active_pane(self, renderer):
- mins, maxs, centers, deltas, tc, highs = self._get_coord_info(renderer)
- info = self._axinfo
- index = info['i']
- if not highs[index]:
- loc = mins[index]
- plane = self._PLANES[2 * index]
- else:
- loc = maxs[index]
- plane = self._PLANES[2 * index + 1]
- xys = np.array([tc[p] for p in plane])
- return xys, loc
-
- def draw_pane(self, renderer):
- """
- Draw pane.
-
- Parameters
- ----------
- renderer : `~matplotlib.backend_bases.RendererBase` subclass
- """
- renderer.open_group('pane3d', gid=self.get_gid())
- xys, loc = self.active_pane(renderer)
- self.pane.xy = xys[:, :2]
- self.pane.draw(renderer)
- renderer.close_group('pane3d')
-
- def _axmask(self):
- axmask = [True, True, True]
- axmask[self._axinfo["i"]] = False
- return axmask
-
- def _draw_ticks(self, renderer, edgep1, centers, deltas, highs,
- deltas_per_point, pos):
- ticks = self._update_ticks()
- info = self._axinfo
- index = info["i"]
-
- # Draw ticks:
- tickdir = self._get_tickdir(pos)
- tickdelta = deltas[tickdir] if highs[tickdir] else -deltas[tickdir]
-
- tick_info = info['tick']
- tick_out = tick_info['outward_factor'] * tickdelta
- tick_in = tick_info['inward_factor'] * tickdelta
- tick_lw = tick_info['linewidth']
- edgep1_tickdir = edgep1[tickdir]
- out_tickdir = edgep1_tickdir + tick_out
- in_tickdir = edgep1_tickdir - tick_in
-
- default_label_offset = 8. # A rough estimate
- points = deltas_per_point * deltas
- for tick in ticks:
- # Get tick line positions
- pos = edgep1.copy()
- pos[index] = tick.get_loc()
- pos[tickdir] = out_tickdir
- x1, y1, z1 = proj3d.proj_transform(*pos, self.axes.M)
- pos[tickdir] = in_tickdir
- x2, y2, z2 = proj3d.proj_transform(*pos, self.axes.M)
-
- # Get position of label
- labeldeltas = (tick.get_pad() + default_label_offset) * points
-
- pos[tickdir] = edgep1_tickdir
- pos = _move_from_center(pos, centers, labeldeltas, self._axmask())
- lx, ly, lz = proj3d.proj_transform(*pos, self.axes.M)
-
- _tick_update_position(tick, (x1, x2), (y1, y2), (lx, ly))
- tick.tick1line.set_linewidth(tick_lw[tick._major])
- tick.draw(renderer)
-
- def _draw_offset_text(self, renderer, edgep1, edgep2, labeldeltas, centers,
- highs, pep, dx, dy):
- # Get general axis information:
- info = self._axinfo
- index = info["i"]
- juggled = info["juggled"]
- tickdir = info["tickdir"]
-
- # Which of the two edge points do we want to
- # use for locating the offset text?
- if juggled[2] == 2:
- outeredgep = edgep1
- outerindex = 0
- else:
- outeredgep = edgep2
- outerindex = 1
-
- pos = _move_from_center(outeredgep, centers, labeldeltas,
- self._axmask())
- olx, oly, olz = proj3d.proj_transform(*pos, self.axes.M)
- self.offsetText.set_text(self.major.formatter.get_offset())
- self.offsetText.set_position((olx, oly))
- angle = art3d._norm_text_angle(np.rad2deg(np.arctan2(dy, dx)))
- self.offsetText.set_rotation(angle)
- # Must set rotation mode to "anchor" so that
- # the alignment point is used as the "fulcrum" for rotation.
- self.offsetText.set_rotation_mode('anchor')
-
- # ----------------------------------------------------------------------
- # Note: the following statement for determining the proper alignment of
- # the offset text. This was determined entirely by trial-and-error
- # and should not be in any way considered as "the way". There are
- # still some edge cases where alignment is not quite right, but this
- # seems to be more of a geometry issue (in other words, I might be
- # using the wrong reference points).
- #
- # (TT, FF, TF, FT) are the shorthand for the tuple of
- # (centpt[tickdir] <= pep[tickdir, outerindex],
- # centpt[index] <= pep[index, outerindex])
- #
- # Three-letters (e.g., TFT, FTT) are short-hand for the array of bools
- # from the variable 'highs'.
- # ---------------------------------------------------------------------
- centpt = proj3d.proj_transform(*centers, self.axes.M)
- if centpt[tickdir] > pep[tickdir, outerindex]:
- # if FT and if highs has an even number of Trues
- if (centpt[index] <= pep[index, outerindex]
- and np.count_nonzero(highs) % 2 == 0):
- # Usually, this means align right, except for the FTT case,
- # in which offset for axis 1 and 2 are aligned left.
- if highs.tolist() == [False, True, True] and index in (1, 2):
- align = 'left'
- else:
- align = 'right'
- else:
- # The FF case
- align = 'left'
- else:
- # if TF and if highs has an even number of Trues
- if (centpt[index] > pep[index, outerindex]
- and np.count_nonzero(highs) % 2 == 0):
- # Usually mean align left, except if it is axis 2
- align = 'right' if index == 2 else 'left'
- else:
- # The TT case
- align = 'right'
-
- self.offsetText.set_va('center')
- self.offsetText.set_ha(align)
- self.offsetText.draw(renderer)
-
- def _draw_labels(self, renderer, edgep1, edgep2, labeldeltas, centers, dx, dy):
- label = self._axinfo["label"]
-
- # Draw labels
- lxyz = 0.5 * (edgep1 + edgep2)
- lxyz = _move_from_center(lxyz, centers, labeldeltas, self._axmask())
- tlx, tly, tlz = proj3d.proj_transform(*lxyz, self.axes.M)
- self.label.set_position((tlx, tly))
- if self.get_rotate_label(self.label.get_text()):
- angle = art3d._norm_text_angle(np.rad2deg(np.arctan2(dy, dx)))
- self.label.set_rotation(angle)
- self.label.set_va(label['va'])
- self.label.set_ha(label['ha'])
- self.label.set_rotation_mode(label['rotation_mode'])
- self.label.draw(renderer)
-
- @artist.allow_rasterization
- def draw(self, renderer):
- self.label._transform = self.axes.transData
- self.offsetText._transform = self.axes.transData
- renderer.open_group("axis3d", gid=self.get_gid())
-
- # Get general axis information:
- mins, maxs, centers, deltas, tc, highs = self._get_coord_info(renderer)
-
- # Calculate offset distances
- # A rough estimate; points are ambiguous since 3D plots rotate
- reltoinches = self.figure.dpi_scale_trans.inverted()
- ax_inches = reltoinches.transform(self.axes.bbox.size)
- ax_points_estimate = sum(72. * ax_inches)
- deltas_per_point = 48 / ax_points_estimate
- default_offset = 21.
- labeldeltas = (self.labelpad + default_offset) * deltas_per_point * deltas
-
- # Determine edge points for the axis lines
- minmax = np.where(highs, maxs, mins) # "origin" point
- maxmin = np.where(~highs, maxs, mins) # "opposite" corner near camera
-
- for edgep1, edgep2, pos in zip(*self._get_all_axis_line_edge_points(
- minmax, maxmin, self._tick_position)):
- # Project the edge points along the current position
- pep = proj3d._proj_trans_points([edgep1, edgep2], self.axes.M)
- pep = np.asarray(pep)
-
- # The transAxes transform is used because the Text object
- # rotates the text relative to the display coordinate system.
- # Therefore, if we want the labels to remain parallel to the
- # axis regardless of the aspect ratio, we need to convert the
- # edge points of the plane to display coordinates and calculate
- # an angle from that.
- # TODO: Maybe Text objects should handle this themselves?
- dx, dy = (self.axes.transAxes.transform([pep[0:2, 1]]) -
- self.axes.transAxes.transform([pep[0:2, 0]]))[0]
-
- # Draw the lines
- self.line.set_data(pep[0], pep[1])
- self.line.draw(renderer)
-
- # Draw ticks
- self._draw_ticks(renderer, edgep1, centers, deltas, highs,
- deltas_per_point, pos)
-
- # Draw Offset text
- self._draw_offset_text(renderer, edgep1, edgep2, labeldeltas,
- centers, highs, pep, dx, dy)
-
- for edgep1, edgep2, pos in zip(*self._get_all_axis_line_edge_points(
- minmax, maxmin, self._label_position)):
- # See comments above
- pep = proj3d._proj_trans_points([edgep1, edgep2], self.axes.M)
- pep = np.asarray(pep)
- dx, dy = (self.axes.transAxes.transform([pep[0:2, 1]]) -
- self.axes.transAxes.transform([pep[0:2, 0]]))[0]
-
- # Draw labels
- self._draw_labels(renderer, edgep1, edgep2, labeldeltas, centers, dx, dy)
-
- renderer.close_group('axis3d')
- self.stale = False
-
- @artist.allow_rasterization
- def draw_grid(self, renderer):
- if not self.axes._draw_grid:
- return
-
- renderer.open_group("grid3d", gid=self.get_gid())
-
- ticks = self._update_ticks()
- if len(ticks):
- # Get general axis information:
- info = self._axinfo
- index = info["i"]
-
- mins, maxs, _, _, _, highs = self._get_coord_info(renderer)
-
- minmax = np.where(highs, maxs, mins)
- maxmin = np.where(~highs, maxs, mins)
-
- # Grid points where the planes meet
- xyz0 = np.tile(minmax, (len(ticks), 1))
- xyz0[:, index] = [tick.get_loc() for tick in ticks]
-
- # Grid lines go from the end of one plane through the plane
- # intersection (at xyz0) to the end of the other plane. The first
- # point (0) differs along dimension index-2 and the last (2) along
- # dimension index-1.
- lines = np.stack([xyz0, xyz0, xyz0], axis=1)
- lines[:, 0, index - 2] = maxmin[index - 2]
- lines[:, 2, index - 1] = maxmin[index - 1]
- self.gridlines.set_segments(lines)
- gridinfo = info['grid']
- self.gridlines.set_color(gridinfo['color'])
- self.gridlines.set_linewidth(gridinfo['linewidth'])
- self.gridlines.set_linestyle(gridinfo['linestyle'])
- self.gridlines.do_3d_projection()
- self.gridlines.draw(renderer)
-
- renderer.close_group('grid3d')
-
- # TODO: Get this to work (more) properly when mplot3d supports the
- # transforms framework.
- def get_tightbbox(self, renderer=None, *, for_layout_only=False):
- # docstring inherited
- if not self.get_visible():
- return
- # We have to directly access the internal data structures
- # (and hope they are up to date) because at draw time we
- # shift the ticks and their labels around in (x, y) space
- # based on the projection, the current view port, and their
- # position in 3D space. If we extend the transforms framework
- # into 3D we would not need to do this different book keeping
- # than we do in the normal axis
- major_locs = self.get_majorticklocs()
- minor_locs = self.get_minorticklocs()
-
- ticks = [*self.get_minor_ticks(len(minor_locs)),
- *self.get_major_ticks(len(major_locs))]
- view_low, view_high = self.get_view_interval()
- if view_low > view_high:
- view_low, view_high = view_high, view_low
- interval_t = self.get_transform().transform([view_low, view_high])
-
- ticks_to_draw = []
- for tick in ticks:
- try:
- loc_t = self.get_transform().transform(tick.get_loc())
- except AssertionError:
- # Transform.transform doesn't allow masked values but
- # some scales might make them, so we need this try/except.
- pass
- else:
- if mtransforms._interval_contains_close(interval_t, loc_t):
- ticks_to_draw.append(tick)
-
- ticks = ticks_to_draw
-
- bb_1, bb_2 = self._get_ticklabel_bboxes(ticks, renderer)
- other = []
-
- if self.line.get_visible():
- other.append(self.line.get_window_extent(renderer))
- if (self.label.get_visible() and not for_layout_only and
- self.label.get_text()):
- other.append(self.label.get_window_extent(renderer))
-
- return mtransforms.Bbox.union([*bb_1, *bb_2, *other])
-
- d_interval = _api.deprecated(
- "3.6", alternative="get_data_interval", pending=True)(
- property(lambda self: self.get_data_interval(),
- lambda self, minmax: self.set_data_interval(*minmax)))
- v_interval = _api.deprecated(
- "3.6", alternative="get_view_interval", pending=True)(
- property(lambda self: self.get_view_interval(),
- lambda self, minmax: self.set_view_interval(*minmax)))
-
-
-class XAxis(Axis):
- axis_name = "x"
- get_view_interval, set_view_interval = maxis._make_getset_interval(
- "view", "xy_viewLim", "intervalx")
- get_data_interval, set_data_interval = maxis._make_getset_interval(
- "data", "xy_dataLim", "intervalx")
-
-
-class YAxis(Axis):
- axis_name = "y"
- get_view_interval, set_view_interval = maxis._make_getset_interval(
- "view", "xy_viewLim", "intervaly")
- get_data_interval, set_data_interval = maxis._make_getset_interval(
- "data", "xy_dataLim", "intervaly")
-
-
-class ZAxis(Axis):
- axis_name = "z"
- get_view_interval, set_view_interval = maxis._make_getset_interval(
- "view", "zz_viewLim", "intervalx")
- get_data_interval, set_data_interval = maxis._make_getset_interval(
- "data", "zz_dataLim", "intervalx")
diff --git a/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/proj3d.py b/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/proj3d.py
deleted file mode 100644
index 098a7b6f666..00000000000
--- a/contrib/python/matplotlib/py3/mpl_toolkits/mplot3d/proj3d.py
+++ /dev/null
@@ -1,259 +0,0 @@
-"""
-Various transforms used for by the 3D code
-"""
-
-import numpy as np
-
-from matplotlib import _api
-
-
-def world_transformation(xmin, xmax,
- ymin, ymax,
- zmin, zmax, pb_aspect=None):
- """
- Produce a matrix that scales homogeneous coords in the specified ranges
- to [0, 1], or [0, pb_aspect[i]] if the plotbox aspect ratio is specified.
- """
- dx = xmax - xmin
- dy = ymax - ymin
- dz = zmax - zmin
- if pb_aspect is not None:
- ax, ay, az = pb_aspect
- dx /= ax
- dy /= ay
- dz /= az
-
- return np.array([[1/dx, 0, 0, -xmin/dx],
- [0, 1/dy, 0, -ymin/dy],
- [0, 0, 1/dz, -zmin/dz],
- [0, 0, 0, 1]])
-
-
-@_api.deprecated("3.8")
-def rotation_about_vector(v, angle):
- """
- Produce a rotation matrix for an angle in radians about a vector.
- """
- return _rotation_about_vector(v, angle)
-
-
-def _rotation_about_vector(v, angle):
- """
- Produce a rotation matrix for an angle in radians about a vector.
- """
- vx, vy, vz = v / np.linalg.norm(v)
- s = np.sin(angle)
- c = np.cos(angle)
- t = 2*np.sin(angle/2)**2 # more numerically stable than t = 1-c
-
- R = np.array([
- [t*vx*vx + c, t*vx*vy - vz*s, t*vx*vz + vy*s],
- [t*vy*vx + vz*s, t*vy*vy + c, t*vy*vz - vx*s],
- [t*vz*vx - vy*s, t*vz*vy + vx*s, t*vz*vz + c]])
-
- return R
-
-
-def _view_axes(E, R, V, roll):
- """
- Get the unit viewing axes in data coordinates.
-
- Parameters
- ----------
- E : 3-element numpy array
- The coordinates of the eye/camera.
- R : 3-element numpy array
- The coordinates of the center of the view box.
- V : 3-element numpy array
- Unit vector in the direction of the vertical axis.
- roll : float
- The roll angle in radians.
-
- Returns
- -------
- u : 3-element numpy array
- Unit vector pointing towards the right of the screen.
- v : 3-element numpy array
- Unit vector pointing towards the top of the screen.
- w : 3-element numpy array
- Unit vector pointing out of the screen.
- """
- w = (E - R)
- w = w/np.linalg.norm(w)
- u = np.cross(V, w)
- u = u/np.linalg.norm(u)
- v = np.cross(w, u) # Will be a unit vector
-
- # Save some computation for the default roll=0
- if roll != 0:
- # A positive rotation of the camera is a negative rotation of the world
- Rroll = _rotation_about_vector(w, -roll)
- u = np.dot(Rroll, u)
- v = np.dot(Rroll, v)
- return u, v, w
-
-
-def _view_transformation_uvw(u, v, w, E):
- """
- Return the view transformation matrix.
-
- Parameters
- ----------
- u : 3-element numpy array
- Unit vector pointing towards the right of the screen.
- v : 3-element numpy array
- Unit vector pointing towards the top of the screen.
- w : 3-element numpy array
- Unit vector pointing out of the screen.
- E : 3-element numpy array
- The coordinates of the eye/camera.
- """
- Mr = np.eye(4)
- Mt = np.eye(4)
- Mr[:3, :3] = [u, v, w]
- Mt[:3, -1] = -E
- M = np.dot(Mr, Mt)
- return M
-
-
-@_api.deprecated("3.8")
-def view_transformation(E, R, V, roll):
- """
- Return the view transformation matrix.
-
- Parameters
- ----------
- E : 3-element numpy array
- The coordinates of the eye/camera.
- R : 3-element numpy array
- The coordinates of the center of the view box.
- V : 3-element numpy array
- Unit vector in the direction of the vertical axis.
- roll : float
- The roll angle in radians.
- """
- u, v, w = _view_axes(E, R, V, roll)
- M = _view_transformation_uvw(u, v, w, E)
- return M
-
-
-@_api.deprecated("3.8")
-def persp_transformation(zfront, zback, focal_length):
- return _persp_transformation(zfront, zback, focal_length)
-
-
-def _persp_transformation(zfront, zback, focal_length):
- e = focal_length
- a = 1 # aspect ratio
- b = (zfront+zback)/(zfront-zback)
- c = -2*(zfront*zback)/(zfront-zback)
- proj_matrix = np.array([[e, 0, 0, 0],
- [0, e/a, 0, 0],
- [0, 0, b, c],
- [0, 0, -1, 0]])
- return proj_matrix
-
-
-@_api.deprecated("3.8")
-def ortho_transformation(zfront, zback):
- return _ortho_transformation(zfront, zback)
-
-
-def _ortho_transformation(zfront, zback):
- # note: w component in the resulting vector will be (zback-zfront), not 1
- a = -(zfront + zback)
- b = -(zfront - zback)
- proj_matrix = np.array([[2, 0, 0, 0],
- [0, 2, 0, 0],
- [0, 0, -2, 0],
- [0, 0, a, b]])
- return proj_matrix
-
-
-def _proj_transform_vec(vec, M):
- vecw = np.dot(M, vec)
- w = vecw[3]
- # clip here..
- txs, tys, tzs = vecw[0]/w, vecw[1]/w, vecw[2]/w
- return txs, tys, tzs
-
-
-def _proj_transform_vec_clip(vec, M):
- vecw = np.dot(M, vec)
- w = vecw[3]
- # clip here.
- txs, tys, tzs = vecw[0] / w, vecw[1] / w, vecw[2] / w
- tis = (0 <= vecw[0]) & (vecw[0] <= 1) & (0 <= vecw[1]) & (vecw[1] <= 1)
- if np.any(tis):
- tis = vecw[1] < 1
- return txs, tys, tzs, tis
-
-
-def inv_transform(xs, ys, zs, invM):
- """
- Transform the points by the inverse of the projection matrix, *invM*.
- """
- vec = _vec_pad_ones(xs, ys, zs)
- vecr = np.dot(invM, vec)
- if vecr.shape == (4,):
- vecr = vecr.reshape((4, 1))
- for i in range(vecr.shape[1]):
- if vecr[3][i] != 0:
- vecr[:, i] = vecr[:, i] / vecr[3][i]
- return vecr[0], vecr[1], vecr[2]
-
-
-def _vec_pad_ones(xs, ys, zs):
- return np.array([xs, ys, zs, np.ones_like(xs)])
-
-
-def proj_transform(xs, ys, zs, M):
- """
- Transform the points by the projection matrix *M*.
- """
- vec = _vec_pad_ones(xs, ys, zs)
- return _proj_transform_vec(vec, M)
-
-
-transform = _api.deprecated(
- "3.8", obj_type="function", name="transform",
- alternative="proj_transform")(proj_transform)
-
-
-def proj_transform_clip(xs, ys, zs, M):
- """
- Transform the points by the projection matrix
- and return the clipping result
- returns txs, tys, tzs, tis
- """
- vec = _vec_pad_ones(xs, ys, zs)
- return _proj_transform_vec_clip(vec, M)
-
-
-@_api.deprecated("3.8")
-def proj_points(points, M):
- return _proj_points(points, M)
-
-
-def _proj_points(points, M):
- return np.column_stack(_proj_trans_points(points, M))
-
-
-@_api.deprecated("3.8")
-def proj_trans_points(points, M):
- return _proj_trans_points(points, M)
-
-
-def _proj_trans_points(points, M):
- xs, ys, zs = zip(*points)
- return proj_transform(xs, ys, zs, M)
-
-
-@_api.deprecated("3.8")
-def rot_x(V, alpha):
- cosa, sina = np.cos(alpha), np.sin(alpha)
- M1 = np.array([[1, 0, 0, 0],
- [0, cosa, -sina, 0],
- [0, sina, cosa, 0],
- [0, 0, 0, 1]])
- return np.dot(M1, V)