You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			1703 lines
		
	
	
		
			67 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			1703 lines
		
	
	
		
			67 KiB
		
	
	
	
		
			Python
		
	
"""
 | 
						|
Classes to support contour plotting and labelling for the Axes class.
 | 
						|
"""
 | 
						|
 | 
						|
from contextlib import ExitStack
 | 
						|
import functools
 | 
						|
import math
 | 
						|
from numbers import Integral
 | 
						|
 | 
						|
import numpy as np
 | 
						|
from numpy import ma
 | 
						|
 | 
						|
import matplotlib as mpl
 | 
						|
from matplotlib import _api, _docstring
 | 
						|
from matplotlib.backend_bases import MouseButton
 | 
						|
from matplotlib.lines import Line2D
 | 
						|
from matplotlib.path import Path
 | 
						|
from matplotlib.text import Text
 | 
						|
import matplotlib.ticker as ticker
 | 
						|
import matplotlib.cm as cm
 | 
						|
import matplotlib.colors as mcolors
 | 
						|
import matplotlib.collections as mcoll
 | 
						|
import matplotlib.font_manager as font_manager
 | 
						|
import matplotlib.cbook as cbook
 | 
						|
import matplotlib.patches as mpatches
 | 
						|
import matplotlib.transforms as mtransforms
 | 
						|
 | 
						|
 | 
						|
def _contour_labeler_event_handler(cs, inline, inline_spacing, event):
 | 
						|
    canvas = cs.axes.get_figure(root=True).canvas
 | 
						|
    is_button = event.name == "button_press_event"
 | 
						|
    is_key = event.name == "key_press_event"
 | 
						|
    # Quit (even if not in infinite mode; this is consistent with
 | 
						|
    # MATLAB and sometimes quite useful, but will require the user to
 | 
						|
    # test how many points were actually returned before using data).
 | 
						|
    if (is_button and event.button == MouseButton.MIDDLE
 | 
						|
            or is_key and event.key in ["escape", "enter"]):
 | 
						|
        canvas.stop_event_loop()
 | 
						|
    # Pop last click.
 | 
						|
    elif (is_button and event.button == MouseButton.RIGHT
 | 
						|
          or is_key and event.key in ["backspace", "delete"]):
 | 
						|
        # Unfortunately, if one is doing inline labels, then there is currently
 | 
						|
        # no way to fix the broken contour - once humpty-dumpty is broken, he
 | 
						|
        # can't be put back together.  In inline mode, this does nothing.
 | 
						|
        if not inline:
 | 
						|
            cs.pop_label()
 | 
						|
            canvas.draw()
 | 
						|
    # Add new click.
 | 
						|
    elif (is_button and event.button == MouseButton.LEFT
 | 
						|
          # On macOS/gtk, some keys return None.
 | 
						|
          or is_key and event.key is not None):
 | 
						|
        if cs.axes.contains(event)[0]:
 | 
						|
            cs.add_label_near(event.x, event.y, transform=False,
 | 
						|
                              inline=inline, inline_spacing=inline_spacing)
 | 
						|
            canvas.draw()
 | 
						|
 | 
						|
 | 
						|
class ContourLabeler:
 | 
						|
    """Mixin to provide labelling capability to `.ContourSet`."""
 | 
						|
 | 
						|
    def clabel(self, levels=None, *,
 | 
						|
               fontsize=None, inline=True, inline_spacing=5, fmt=None,
 | 
						|
               colors=None, use_clabeltext=False, manual=False,
 | 
						|
               rightside_up=True, zorder=None):
 | 
						|
        """
 | 
						|
        Label a contour plot.
 | 
						|
 | 
						|
        Adds labels to line contours in this `.ContourSet` (which inherits from
 | 
						|
        this mixin class).
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        levels : array-like, optional
 | 
						|
            A list of level values, that should be labeled. The list must be
 | 
						|
            a subset of ``cs.levels``. If not given, all levels are labeled.
 | 
						|
 | 
						|
        fontsize : str or float, default: :rc:`font.size`
 | 
						|
            Size in points or relative size e.g., 'smaller', 'x-large'.
 | 
						|
            See `.Text.set_size` for accepted string values.
 | 
						|
 | 
						|
        colors : :mpltype:`color` or colors or None, default: None
 | 
						|
            The label colors:
 | 
						|
 | 
						|
            - If *None*, the color of each label matches the color of
 | 
						|
              the corresponding contour.
 | 
						|
 | 
						|
            - If one string color, e.g., *colors* = 'r' or *colors* =
 | 
						|
              'red', all labels will be plotted in this color.
 | 
						|
 | 
						|
            - If a tuple of colors (string, float, RGB, etc), different labels
 | 
						|
              will be plotted in different colors in the order specified.
 | 
						|
 | 
						|
        inline : bool, default: True
 | 
						|
            If ``True`` the underlying contour is removed where the label is
 | 
						|
            placed.
 | 
						|
 | 
						|
        inline_spacing : float, default: 5
 | 
						|
            Space in pixels to leave on each side of label when placing inline.
 | 
						|
 | 
						|
            This spacing will be exact for labels at locations where the
 | 
						|
            contour is straight, less so for labels on curved contours.
 | 
						|
 | 
						|
        fmt : `.Formatter` or str or callable or dict, optional
 | 
						|
            How the levels are formatted:
 | 
						|
 | 
						|
            - If a `.Formatter`, it is used to format all levels at once, using
 | 
						|
              its `.Formatter.format_ticks` method.
 | 
						|
            - If a str, it is interpreted as a %-style format string.
 | 
						|
            - If a callable, it is called with one level at a time and should
 | 
						|
              return the corresponding label.
 | 
						|
            - If a dict, it should directly map levels to labels.
 | 
						|
 | 
						|
            The default is to use a standard `.ScalarFormatter`.
 | 
						|
 | 
						|
        manual : bool or iterable, default: False
 | 
						|
            If ``True``, contour labels will be placed manually using
 | 
						|
            mouse clicks. Click the first button near a contour to
 | 
						|
            add a label, click the second button (or potentially both
 | 
						|
            mouse buttons at once) to finish adding labels. The third
 | 
						|
            button can be used to remove the last label added, but
 | 
						|
            only if labels are not inline. Alternatively, the keyboard
 | 
						|
            can be used to select label locations (enter to end label
 | 
						|
            placement, delete or backspace act like the third mouse button,
 | 
						|
            and any other key will select a label location).
 | 
						|
 | 
						|
            *manual* can also be an iterable object of (x, y) tuples.
 | 
						|
            Contour labels will be created as if mouse is clicked at each
 | 
						|
            (x, y) position.
 | 
						|
 | 
						|
        rightside_up : bool, default: True
 | 
						|
            If ``True``, label rotations will always be plus
 | 
						|
            or minus 90 degrees from level.
 | 
						|
 | 
						|
        use_clabeltext : bool, default: False
 | 
						|
            If ``True``, use `.Text.set_transform_rotates_text` to ensure that
 | 
						|
            label rotation is updated whenever the Axes aspect changes.
 | 
						|
 | 
						|
        zorder : float or None, default: ``(2 + contour.get_zorder())``
 | 
						|
            zorder of the contour labels.
 | 
						|
 | 
						|
        Returns
 | 
						|
        -------
 | 
						|
        labels
 | 
						|
            A list of `.Text` instances for the labels.
 | 
						|
        """
 | 
						|
 | 
						|
        # Based on the input arguments, clabel() adds a list of "label
 | 
						|
        # specific" attributes to the ContourSet object.  These attributes are
 | 
						|
        # all of the form label* and names should be fairly self explanatory.
 | 
						|
        #
 | 
						|
        # Once these attributes are set, clabel passes control to the labels()
 | 
						|
        # method (for automatic label placement) or blocking_input_loop and
 | 
						|
        # _contour_labeler_event_handler (for manual label placement).
 | 
						|
 | 
						|
        if fmt is None:
 | 
						|
            fmt = ticker.ScalarFormatter(useOffset=False)
 | 
						|
            fmt.create_dummy_axis()
 | 
						|
        self.labelFmt = fmt
 | 
						|
        self._use_clabeltext = use_clabeltext
 | 
						|
        self.labelManual = manual
 | 
						|
        self.rightside_up = rightside_up
 | 
						|
        self._clabel_zorder = 2 + self.get_zorder() if zorder is None else zorder
 | 
						|
 | 
						|
        if levels is None:
 | 
						|
            levels = self.levels
 | 
						|
            indices = list(range(len(self.cvalues)))
 | 
						|
        else:
 | 
						|
            levlabs = list(levels)
 | 
						|
            indices, levels = [], []
 | 
						|
            for i, lev in enumerate(self.levels):
 | 
						|
                if lev in levlabs:
 | 
						|
                    indices.append(i)
 | 
						|
                    levels.append(lev)
 | 
						|
            if len(levels) < len(levlabs):
 | 
						|
                raise ValueError(f"Specified levels {levlabs} don't match "
 | 
						|
                                 f"available levels {self.levels}")
 | 
						|
        self.labelLevelList = levels
 | 
						|
        self.labelIndiceList = indices
 | 
						|
 | 
						|
        self._label_font_props = font_manager.FontProperties(size=fontsize)
 | 
						|
 | 
						|
        if colors is None:
 | 
						|
            self.labelMappable = self
 | 
						|
            self.labelCValueList = np.take(self.cvalues, self.labelIndiceList)
 | 
						|
        else:
 | 
						|
            cmap = mcolors.ListedColormap(colors, N=len(self.labelLevelList))
 | 
						|
            self.labelCValueList = list(range(len(self.labelLevelList)))
 | 
						|
            self.labelMappable = cm.ScalarMappable(cmap=cmap,
 | 
						|
                                                   norm=mcolors.NoNorm())
 | 
						|
 | 
						|
        self.labelXYs = []
 | 
						|
 | 
						|
        if np.iterable(manual):
 | 
						|
            for x, y in manual:
 | 
						|
                self.add_label_near(x, y, inline, inline_spacing)
 | 
						|
        elif manual:
 | 
						|
            print('Select label locations manually using first mouse button.')
 | 
						|
            print('End manual selection with second mouse button.')
 | 
						|
            if not inline:
 | 
						|
                print('Remove last label by clicking third mouse button.')
 | 
						|
            mpl._blocking_input.blocking_input_loop(
 | 
						|
                self.axes.get_figure(root=True),
 | 
						|
                ["button_press_event", "key_press_event"],
 | 
						|
                timeout=-1, handler=functools.partial(
 | 
						|
                    _contour_labeler_event_handler,
 | 
						|
                    self, inline, inline_spacing))
 | 
						|
        else:
 | 
						|
            self.labels(inline, inline_spacing)
 | 
						|
 | 
						|
        return cbook.silent_list('text.Text', self.labelTexts)
 | 
						|
 | 
						|
    def print_label(self, linecontour, labelwidth):
 | 
						|
        """Return whether a contour is long enough to hold a label."""
 | 
						|
        return (len(linecontour) > 10 * labelwidth
 | 
						|
                or (len(linecontour)
 | 
						|
                    and (np.ptp(linecontour, axis=0) > 1.2 * labelwidth).any()))
 | 
						|
 | 
						|
    def too_close(self, x, y, lw):
 | 
						|
        """Return whether a label is already near this location."""
 | 
						|
        thresh = (1.2 * lw) ** 2
 | 
						|
        return any((x - loc[0]) ** 2 + (y - loc[1]) ** 2 < thresh
 | 
						|
                   for loc in self.labelXYs)
 | 
						|
 | 
						|
    def _get_nth_label_width(self, nth):
 | 
						|
        """Return the width of the *nth* label, in pixels."""
 | 
						|
        fig = self.axes.get_figure(root=False)
 | 
						|
        renderer = fig.get_figure(root=True)._get_renderer()
 | 
						|
        return (Text(0, 0,
 | 
						|
                     self.get_text(self.labelLevelList[nth], self.labelFmt),
 | 
						|
                     figure=fig, fontproperties=self._label_font_props)
 | 
						|
                .get_window_extent(renderer).width)
 | 
						|
 | 
						|
    def get_text(self, lev, fmt):
 | 
						|
        """Get the text of the label."""
 | 
						|
        if isinstance(lev, str):
 | 
						|
            return lev
 | 
						|
        elif isinstance(fmt, dict):
 | 
						|
            return fmt.get(lev, '%1.3f')
 | 
						|
        elif callable(getattr(fmt, "format_ticks", None)):
 | 
						|
            return fmt.format_ticks([*self.labelLevelList, lev])[-1]
 | 
						|
        elif callable(fmt):
 | 
						|
            return fmt(lev)
 | 
						|
        else:
 | 
						|
            return fmt % lev
 | 
						|
 | 
						|
    def locate_label(self, linecontour, labelwidth):
 | 
						|
        """
 | 
						|
        Find good place to draw a label (relatively flat part of the contour).
 | 
						|
        """
 | 
						|
        ctr_size = len(linecontour)
 | 
						|
        n_blocks = int(np.ceil(ctr_size / labelwidth)) if labelwidth > 1 else 1
 | 
						|
        block_size = ctr_size if n_blocks == 1 else int(labelwidth)
 | 
						|
        # Split contour into blocks of length ``block_size``, filling the last
 | 
						|
        # block by cycling the contour start (per `np.resize` semantics).  (Due
 | 
						|
        # to cycling, the index returned is taken modulo ctr_size.)
 | 
						|
        xx = np.resize(linecontour[:, 0], (n_blocks, block_size))
 | 
						|
        yy = np.resize(linecontour[:, 1], (n_blocks, block_size))
 | 
						|
        yfirst = yy[:, :1]
 | 
						|
        ylast = yy[:, -1:]
 | 
						|
        xfirst = xx[:, :1]
 | 
						|
        xlast = xx[:, -1:]
 | 
						|
        s = (yfirst - yy) * (xlast - xfirst) - (xfirst - xx) * (ylast - yfirst)
 | 
						|
        l = np.hypot(xlast - xfirst, ylast - yfirst)
 | 
						|
        # Ignore warning that divide by zero throws, as this is a valid option
 | 
						|
        with np.errstate(divide='ignore', invalid='ignore'):
 | 
						|
            distances = (abs(s) / l).sum(axis=-1)
 | 
						|
        # Labels are drawn in the middle of the block (``hbsize``) where the
 | 
						|
        # contour is the closest (per ``distances``) to a straight line, but
 | 
						|
        # not `too_close()` to a preexisting label.
 | 
						|
        hbsize = block_size // 2
 | 
						|
        adist = np.argsort(distances)
 | 
						|
        # If all candidates are `too_close()`, go back to the straightest part
 | 
						|
        # (``adist[0]``).
 | 
						|
        for idx in np.append(adist, adist[0]):
 | 
						|
            x, y = xx[idx, hbsize], yy[idx, hbsize]
 | 
						|
            if not self.too_close(x, y, labelwidth):
 | 
						|
                break
 | 
						|
        return x, y, (idx * block_size + hbsize) % ctr_size
 | 
						|
 | 
						|
    def _split_path_and_get_label_rotation(self, path, idx, screen_pos, lw, spacing=5):
 | 
						|
        """
 | 
						|
        Prepare for insertion of a label at index *idx* of *path*.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        path : Path
 | 
						|
            The path where the label will be inserted, in data space.
 | 
						|
        idx : int
 | 
						|
            The vertex index after which the label will be inserted.
 | 
						|
        screen_pos : (float, float)
 | 
						|
            The position where the label will be inserted, in screen space.
 | 
						|
        lw : float
 | 
						|
            The label width, in screen space.
 | 
						|
        spacing : float
 | 
						|
            Extra spacing around the label, in screen space.
 | 
						|
 | 
						|
        Returns
 | 
						|
        -------
 | 
						|
        path : Path
 | 
						|
            The path, broken so that the label can be drawn over it.
 | 
						|
        angle : float
 | 
						|
            The rotation of the label.
 | 
						|
 | 
						|
        Notes
 | 
						|
        -----
 | 
						|
        Both tasks are done together to avoid calculating path lengths multiple times,
 | 
						|
        which is relatively costly.
 | 
						|
 | 
						|
        The method used here involves computing the path length along the contour in
 | 
						|
        pixel coordinates and then looking (label width / 2) away from central point to
 | 
						|
        determine rotation and then to break contour if desired.  The extra spacing is
 | 
						|
        taken into account when breaking the path, but not when computing the angle.
 | 
						|
        """
 | 
						|
        xys = path.vertices
 | 
						|
        codes = path.codes
 | 
						|
 | 
						|
        # Insert a vertex at idx/pos (converting back to data space), if there isn't yet
 | 
						|
        # a vertex there.  With infinite precision one could also always insert the
 | 
						|
        # extra vertex (it will get masked out by the label below anyways), but floating
 | 
						|
        # point inaccuracies (the point can have undergone a data->screen->data
 | 
						|
        # transform loop) can slightly shift the point and e.g. shift the angle computed
 | 
						|
        # below from exactly zero to nonzero.
 | 
						|
        pos = self.get_transform().inverted().transform(screen_pos)
 | 
						|
        if not np.allclose(pos, xys[idx]):
 | 
						|
            xys = np.insert(xys, idx, pos, axis=0)
 | 
						|
            codes = np.insert(codes, idx, Path.LINETO)
 | 
						|
 | 
						|
        # Find the connected component where the label will be inserted.  Note that a
 | 
						|
        # path always starts with a MOVETO, and we consider there's an implicit
 | 
						|
        # MOVETO (closing the last path) at the end.
 | 
						|
        movetos = (codes == Path.MOVETO).nonzero()[0]
 | 
						|
        start = movetos[movetos <= idx][-1]
 | 
						|
        try:
 | 
						|
            stop = movetos[movetos > idx][0]
 | 
						|
        except IndexError:
 | 
						|
            stop = len(codes)
 | 
						|
 | 
						|
        # Restrict ourselves to the connected component.
 | 
						|
        cc_xys = xys[start:stop]
 | 
						|
        idx -= start
 | 
						|
 | 
						|
        # If the path is closed, rotate it s.t. it starts at the label.
 | 
						|
        is_closed_path = codes[stop - 1] == Path.CLOSEPOLY
 | 
						|
        if is_closed_path:
 | 
						|
            cc_xys = np.concatenate([cc_xys[idx:-1], cc_xys[:idx+1]])
 | 
						|
            idx = 0
 | 
						|
 | 
						|
        # Like np.interp, but additionally vectorized over fp.
 | 
						|
        def interp_vec(x, xp, fp): return [np.interp(x, xp, col) for col in fp.T]
 | 
						|
 | 
						|
        # Use cumulative path lengths ("cpl") as curvilinear coordinate along contour.
 | 
						|
        screen_xys = self.get_transform().transform(cc_xys)
 | 
						|
        path_cpls = np.insert(
 | 
						|
            np.cumsum(np.hypot(*np.diff(screen_xys, axis=0).T)), 0, 0)
 | 
						|
        path_cpls -= path_cpls[idx]
 | 
						|
 | 
						|
        # Use linear interpolation to get end coordinates of label.
 | 
						|
        target_cpls = np.array([-lw/2, lw/2])
 | 
						|
        if is_closed_path:  # For closed paths, target from the other end.
 | 
						|
            target_cpls[0] += (path_cpls[-1] - path_cpls[0])
 | 
						|
        (sx0, sx1), (sy0, sy1) = interp_vec(target_cpls, path_cpls, screen_xys)
 | 
						|
        angle = np.rad2deg(np.arctan2(sy1 - sy0, sx1 - sx0))  # Screen space.
 | 
						|
        if self.rightside_up:  # Fix angle so text is never upside-down
 | 
						|
            angle = (angle + 90) % 180 - 90
 | 
						|
 | 
						|
        target_cpls += [-spacing, +spacing]  # Expand range by spacing.
 | 
						|
 | 
						|
        # Get indices near points of interest; use -1 as out of bounds marker.
 | 
						|
        i0, i1 = np.interp(target_cpls, path_cpls, range(len(path_cpls)),
 | 
						|
                           left=-1, right=-1)
 | 
						|
        i0 = math.floor(i0)
 | 
						|
        i1 = math.ceil(i1)
 | 
						|
        (x0, x1), (y0, y1) = interp_vec(target_cpls, path_cpls, cc_xys)
 | 
						|
 | 
						|
        # Actually break contours (dropping zero-len parts).
 | 
						|
        new_xy_blocks = []
 | 
						|
        new_code_blocks = []
 | 
						|
        if is_closed_path:
 | 
						|
            if i0 != -1 and i1 != -1:
 | 
						|
                # This is probably wrong in the case that the entire contour would
 | 
						|
                # be discarded, but ensures that a valid path is returned and is
 | 
						|
                # consistent with behavior of mpl <3.8
 | 
						|
                points = cc_xys[i1:i0+1]
 | 
						|
                new_xy_blocks.extend([[(x1, y1)], points, [(x0, y0)]])
 | 
						|
                nlines = len(points) + 1
 | 
						|
                new_code_blocks.extend([[Path.MOVETO], [Path.LINETO] * nlines])
 | 
						|
        else:
 | 
						|
            if i0 != -1:
 | 
						|
                new_xy_blocks.extend([cc_xys[:i0 + 1], [(x0, y0)]])
 | 
						|
                new_code_blocks.extend([[Path.MOVETO], [Path.LINETO] * (i0 + 1)])
 | 
						|
            if i1 != -1:
 | 
						|
                new_xy_blocks.extend([[(x1, y1)], cc_xys[i1:]])
 | 
						|
                new_code_blocks.extend([
 | 
						|
                    [Path.MOVETO], [Path.LINETO] * (len(cc_xys) - i1)])
 | 
						|
 | 
						|
        # Back to the full path.
 | 
						|
        xys = np.concatenate([xys[:start], *new_xy_blocks, xys[stop:]])
 | 
						|
        codes = np.concatenate([codes[:start], *new_code_blocks, codes[stop:]])
 | 
						|
 | 
						|
        return angle, Path(xys, codes)
 | 
						|
 | 
						|
    def add_label(self, x, y, rotation, lev, cvalue):
 | 
						|
        """Add a contour label, respecting whether *use_clabeltext* was set."""
 | 
						|
        data_x, data_y = self.axes.transData.inverted().transform((x, y))
 | 
						|
        t = Text(
 | 
						|
            data_x, data_y,
 | 
						|
            text=self.get_text(lev, self.labelFmt),
 | 
						|
            rotation=rotation,
 | 
						|
            horizontalalignment='center', verticalalignment='center',
 | 
						|
            zorder=self._clabel_zorder,
 | 
						|
            color=self.labelMappable.to_rgba(cvalue, alpha=self.get_alpha()),
 | 
						|
            fontproperties=self._label_font_props,
 | 
						|
            clip_box=self.axes.bbox)
 | 
						|
        if self._use_clabeltext:
 | 
						|
            data_rotation, = self.axes.transData.inverted().transform_angles(
 | 
						|
                [rotation], [[x, y]])
 | 
						|
            t.set(rotation=data_rotation, transform_rotates_text=True)
 | 
						|
        self.labelTexts.append(t)
 | 
						|
        self.labelCValues.append(cvalue)
 | 
						|
        self.labelXYs.append((x, y))
 | 
						|
        # Add label to plot here - useful for manual mode label selection
 | 
						|
        self.axes.add_artist(t)
 | 
						|
 | 
						|
    def add_label_near(self, x, y, inline=True, inline_spacing=5,
 | 
						|
                       transform=None):
 | 
						|
        """
 | 
						|
        Add a label near the point ``(x, y)``.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        x, y : float
 | 
						|
            The approximate location of the label.
 | 
						|
        inline : bool, default: True
 | 
						|
            If *True* remove the segment of the contour beneath the label.
 | 
						|
        inline_spacing : int, default: 5
 | 
						|
            Space in pixels to leave on each side of label when placing
 | 
						|
            inline. This spacing will be exact for labels at locations where
 | 
						|
            the contour is straight, less so for labels on curved contours.
 | 
						|
        transform : `.Transform` or `False`, default: ``self.axes.transData``
 | 
						|
            A transform applied to ``(x, y)`` before labeling.  The default
 | 
						|
            causes ``(x, y)`` to be interpreted as data coordinates.  `False`
 | 
						|
            is a synonym for `.IdentityTransform`; i.e. ``(x, y)`` should be
 | 
						|
            interpreted as display coordinates.
 | 
						|
        """
 | 
						|
 | 
						|
        if transform is None:
 | 
						|
            transform = self.axes.transData
 | 
						|
        if transform:
 | 
						|
            x, y = transform.transform((x, y))
 | 
						|
 | 
						|
        idx_level_min, idx_vtx_min, proj = self._find_nearest_contour(
 | 
						|
            (x, y), self.labelIndiceList)
 | 
						|
        path = self._paths[idx_level_min]
 | 
						|
        level = self.labelIndiceList.index(idx_level_min)
 | 
						|
        label_width = self._get_nth_label_width(level)
 | 
						|
        rotation, path = self._split_path_and_get_label_rotation(
 | 
						|
            path, idx_vtx_min, proj, label_width, inline_spacing)
 | 
						|
        self.add_label(*proj, rotation, self.labelLevelList[idx_level_min],
 | 
						|
                       self.labelCValueList[idx_level_min])
 | 
						|
 | 
						|
        if inline:
 | 
						|
            self._paths[idx_level_min] = path
 | 
						|
 | 
						|
    def pop_label(self, index=-1):
 | 
						|
        """Defaults to removing last label, but any index can be supplied"""
 | 
						|
        self.labelCValues.pop(index)
 | 
						|
        t = self.labelTexts.pop(index)
 | 
						|
        t.remove()
 | 
						|
 | 
						|
    def labels(self, inline, inline_spacing):
 | 
						|
        for idx, (icon, lev, cvalue) in enumerate(zip(
 | 
						|
                self.labelIndiceList,
 | 
						|
                self.labelLevelList,
 | 
						|
                self.labelCValueList,
 | 
						|
        )):
 | 
						|
            trans = self.get_transform()
 | 
						|
            label_width = self._get_nth_label_width(idx)
 | 
						|
            additions = []
 | 
						|
            for subpath in self._paths[icon]._iter_connected_components():
 | 
						|
                screen_xys = trans.transform(subpath.vertices)
 | 
						|
                # Check if long enough for a label
 | 
						|
                if self.print_label(screen_xys, label_width):
 | 
						|
                    x, y, idx = self.locate_label(screen_xys, label_width)
 | 
						|
                    rotation, path = self._split_path_and_get_label_rotation(
 | 
						|
                        subpath, idx, (x, y),
 | 
						|
                        label_width, inline_spacing)
 | 
						|
                    self.add_label(x, y, rotation, lev, cvalue)  # Really add label.
 | 
						|
                    if inline:  # If inline, add new contours
 | 
						|
                        additions.append(path)
 | 
						|
                else:  # If not adding label, keep old path
 | 
						|
                    additions.append(subpath)
 | 
						|
            # After looping over all segments on a contour, replace old path by new one
 | 
						|
            # if inlining.
 | 
						|
            if inline:
 | 
						|
                self._paths[icon] = Path.make_compound_path(*additions)
 | 
						|
 | 
						|
    def remove(self):
 | 
						|
        super().remove()
 | 
						|
        for text in self.labelTexts:
 | 
						|
            text.remove()
 | 
						|
 | 
						|
 | 
						|
def _find_closest_point_on_path(xys, p):
 | 
						|
    """
 | 
						|
    Parameters
 | 
						|
    ----------
 | 
						|
    xys : (N, 2) array-like
 | 
						|
        Coordinates of vertices.
 | 
						|
    p : (float, float)
 | 
						|
        Coordinates of point.
 | 
						|
 | 
						|
    Returns
 | 
						|
    -------
 | 
						|
    d2min : float
 | 
						|
        Minimum square distance of *p* to *xys*.
 | 
						|
    proj : (float, float)
 | 
						|
        Projection of *p* onto *xys*.
 | 
						|
    imin : (int, int)
 | 
						|
        Consecutive indices of vertices of segment in *xys* where *proj* is.
 | 
						|
        Segments are considered as including their end-points; i.e. if the
 | 
						|
        closest point on the path is a node in *xys* with index *i*, this
 | 
						|
        returns ``(i-1, i)``.  For the special case where *xys* is a single
 | 
						|
        point, this returns ``(0, 0)``.
 | 
						|
    """
 | 
						|
    if len(xys) == 1:
 | 
						|
        return (((p - xys[0]) ** 2).sum(), xys[0], (0, 0))
 | 
						|
    dxys = xys[1:] - xys[:-1]  # Individual segment vectors.
 | 
						|
    norms = (dxys ** 2).sum(axis=1)
 | 
						|
    norms[norms == 0] = 1  # For zero-length segment, replace 0/0 by 0/1.
 | 
						|
    rel_projs = np.clip(  # Project onto each segment in relative 0-1 coords.
 | 
						|
        ((p - xys[:-1]) * dxys).sum(axis=1) / norms,
 | 
						|
        0, 1)[:, None]
 | 
						|
    projs = xys[:-1] + rel_projs * dxys  # Projs. onto each segment, in (x, y).
 | 
						|
    d2s = ((projs - p) ** 2).sum(axis=1)  # Squared distances.
 | 
						|
    imin = np.argmin(d2s)
 | 
						|
    return (d2s[imin], projs[imin], (imin, imin+1))
 | 
						|
 | 
						|
 | 
						|
_docstring.interpd.register(contour_set_attributes=r"""
 | 
						|
Attributes
 | 
						|
----------
 | 
						|
levels : array
 | 
						|
    The values of the contour levels.
 | 
						|
 | 
						|
layers : array
 | 
						|
    Same as levels for line contours; half-way between
 | 
						|
    levels for filled contours.  See ``ContourSet._process_colors``.
 | 
						|
""")
 | 
						|
 | 
						|
 | 
						|
@_docstring.interpd
 | 
						|
class ContourSet(ContourLabeler, mcoll.Collection):
 | 
						|
    """
 | 
						|
    Store a set of contour lines or filled regions.
 | 
						|
 | 
						|
    User-callable method: `~.Axes.clabel`
 | 
						|
 | 
						|
    Parameters
 | 
						|
    ----------
 | 
						|
    ax : `~matplotlib.axes.Axes`
 | 
						|
 | 
						|
    levels : [level0, level1, ..., leveln]
 | 
						|
        A list of floating point numbers indicating the contour levels.
 | 
						|
 | 
						|
    allsegs : [level0segs, level1segs, ...]
 | 
						|
        List of all the polygon segments for all the *levels*.
 | 
						|
        For contour lines ``len(allsegs) == len(levels)``, and for
 | 
						|
        filled contour regions ``len(allsegs) = len(levels)-1``. The lists
 | 
						|
        should look like ::
 | 
						|
 | 
						|
            level0segs = [polygon0, polygon1, ...]
 | 
						|
            polygon0 = [[x0, y0], [x1, y1], ...]
 | 
						|
 | 
						|
    allkinds : ``None`` or [level0kinds, level1kinds, ...]
 | 
						|
        Optional list of all the polygon vertex kinds (code types), as
 | 
						|
        described and used in Path. This is used to allow multiply-
 | 
						|
        connected paths such as holes within filled polygons.
 | 
						|
        If not ``None``, ``len(allkinds) == len(allsegs)``. The lists
 | 
						|
        should look like ::
 | 
						|
 | 
						|
            level0kinds = [polygon0kinds, ...]
 | 
						|
            polygon0kinds = [vertexcode0, vertexcode1, ...]
 | 
						|
 | 
						|
        If *allkinds* is not ``None``, usually all polygons for a
 | 
						|
        particular contour level are grouped together so that
 | 
						|
        ``level0segs = [polygon0]`` and ``level0kinds = [polygon0kinds]``.
 | 
						|
 | 
						|
    **kwargs
 | 
						|
        Keyword arguments are as described in the docstring of
 | 
						|
        `~.Axes.contour`.
 | 
						|
 | 
						|
    %(contour_set_attributes)s
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, ax, *args,
 | 
						|
                 levels=None, filled=False, linewidths=None, linestyles=None,
 | 
						|
                 hatches=(None,), alpha=None, origin=None, extent=None,
 | 
						|
                 cmap=None, colors=None, norm=None, vmin=None, vmax=None,
 | 
						|
                 colorizer=None, extend='neither', antialiased=None, nchunk=0,
 | 
						|
                 locator=None, transform=None, negative_linestyles=None, clip_path=None,
 | 
						|
                 **kwargs):
 | 
						|
        """
 | 
						|
        Draw contour lines or filled regions, depending on
 | 
						|
        whether keyword arg *filled* is ``False`` (default) or ``True``.
 | 
						|
 | 
						|
        Call signature::
 | 
						|
 | 
						|
            ContourSet(ax, levels, allsegs, [allkinds], **kwargs)
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        ax : `~matplotlib.axes.Axes`
 | 
						|
            The `~.axes.Axes` object to draw on.
 | 
						|
 | 
						|
        levels : [level0, level1, ..., leveln]
 | 
						|
            A list of floating point numbers indicating the contour
 | 
						|
            levels.
 | 
						|
 | 
						|
        allsegs : [level0segs, level1segs, ...]
 | 
						|
            List of all the polygon segments for all the *levels*.
 | 
						|
            For contour lines ``len(allsegs) == len(levels)``, and for
 | 
						|
            filled contour regions ``len(allsegs) = len(levels)-1``. The lists
 | 
						|
            should look like ::
 | 
						|
 | 
						|
                level0segs = [polygon0, polygon1, ...]
 | 
						|
                polygon0 = [[x0, y0], [x1, y1], ...]
 | 
						|
 | 
						|
        allkinds : [level0kinds, level1kinds, ...], optional
 | 
						|
            Optional list of all the polygon vertex kinds (code types), as
 | 
						|
            described and used in Path. This is used to allow multiply-
 | 
						|
            connected paths such as holes within filled polygons.
 | 
						|
            If not ``None``, ``len(allkinds) == len(allsegs)``. The lists
 | 
						|
            should look like ::
 | 
						|
 | 
						|
                level0kinds = [polygon0kinds, ...]
 | 
						|
                polygon0kinds = [vertexcode0, vertexcode1, ...]
 | 
						|
 | 
						|
            If *allkinds* is not ``None``, usually all polygons for a
 | 
						|
            particular contour level are grouped together so that
 | 
						|
            ``level0segs = [polygon0]`` and ``level0kinds = [polygon0kinds]``.
 | 
						|
 | 
						|
        **kwargs
 | 
						|
            Keyword arguments are as described in the docstring of
 | 
						|
            `~.Axes.contour`.
 | 
						|
        """
 | 
						|
        if antialiased is None and filled:
 | 
						|
            # Eliminate artifacts; we are not stroking the boundaries.
 | 
						|
            antialiased = False
 | 
						|
            # The default for line contours will be taken from the
 | 
						|
            # LineCollection default, which uses :rc:`lines.antialiased`.
 | 
						|
        super().__init__(
 | 
						|
            antialiaseds=antialiased,
 | 
						|
            alpha=alpha,
 | 
						|
            clip_path=clip_path,
 | 
						|
            transform=transform,
 | 
						|
            colorizer=colorizer,
 | 
						|
        )
 | 
						|
        self.axes = ax
 | 
						|
        self.levels = levels
 | 
						|
        self.filled = filled
 | 
						|
        self.hatches = hatches
 | 
						|
        self.origin = origin
 | 
						|
        self.extent = extent
 | 
						|
        self.colors = colors
 | 
						|
        self.extend = extend
 | 
						|
 | 
						|
        self.nchunk = nchunk
 | 
						|
        self.locator = locator
 | 
						|
 | 
						|
        if colorizer:
 | 
						|
            self._set_colorizer_check_keywords(colorizer, cmap=cmap,
 | 
						|
                                               norm=norm, vmin=vmin,
 | 
						|
                                               vmax=vmax, colors=colors)
 | 
						|
            norm = colorizer.norm
 | 
						|
            cmap = colorizer.cmap
 | 
						|
        if (isinstance(norm, mcolors.LogNorm)
 | 
						|
                or isinstance(self.locator, ticker.LogLocator)):
 | 
						|
            self.logscale = True
 | 
						|
            if norm is None:
 | 
						|
                norm = mcolors.LogNorm()
 | 
						|
        else:
 | 
						|
            self.logscale = False
 | 
						|
 | 
						|
        _api.check_in_list([None, 'lower', 'upper', 'image'], origin=origin)
 | 
						|
        if self.extent is not None and len(self.extent) != 4:
 | 
						|
            raise ValueError(
 | 
						|
                "If given, 'extent' must be None or (x0, x1, y0, y1)")
 | 
						|
        if self.colors is not None and cmap is not None:
 | 
						|
            raise ValueError('Either colors or cmap must be None')
 | 
						|
        if self.origin == 'image':
 | 
						|
            self.origin = mpl.rcParams['image.origin']
 | 
						|
 | 
						|
        self._orig_linestyles = linestyles  # Only kept for user access.
 | 
						|
        self.negative_linestyles = negative_linestyles
 | 
						|
        # If negative_linestyles was not defined as a keyword argument, define
 | 
						|
        # negative_linestyles with rcParams
 | 
						|
        if self.negative_linestyles is None:
 | 
						|
            self.negative_linestyles = \
 | 
						|
                mpl.rcParams['contour.negative_linestyle']
 | 
						|
 | 
						|
        kwargs = self._process_args(*args, **kwargs)
 | 
						|
        self._process_levels()
 | 
						|
 | 
						|
        self._extend_min = self.extend in ['min', 'both']
 | 
						|
        self._extend_max = self.extend in ['max', 'both']
 | 
						|
        if self.colors is not None:
 | 
						|
            if mcolors.is_color_like(self.colors):
 | 
						|
                color_sequence = [self.colors]
 | 
						|
            else:
 | 
						|
                color_sequence = self.colors
 | 
						|
 | 
						|
            ncolors = len(self.levels)
 | 
						|
            if self.filled:
 | 
						|
                ncolors -= 1
 | 
						|
            i0 = 0
 | 
						|
 | 
						|
            # Handle the case where colors are given for the extended
 | 
						|
            # parts of the contour.
 | 
						|
 | 
						|
            use_set_under_over = False
 | 
						|
            # if we are extending the lower end, and we've been given enough
 | 
						|
            # colors then skip the first color in the resulting cmap. For the
 | 
						|
            # extend_max case we don't need to worry about passing more colors
 | 
						|
            # than ncolors as ListedColormap will clip.
 | 
						|
            total_levels = (ncolors +
 | 
						|
                            int(self._extend_min) +
 | 
						|
                            int(self._extend_max))
 | 
						|
            if (len(color_sequence) == total_levels and
 | 
						|
                    (self._extend_min or self._extend_max)):
 | 
						|
                use_set_under_over = True
 | 
						|
                if self._extend_min:
 | 
						|
                    i0 = 1
 | 
						|
 | 
						|
            cmap = mcolors.ListedColormap(color_sequence[i0:None], N=ncolors)
 | 
						|
 | 
						|
            if use_set_under_over:
 | 
						|
                if self._extend_min:
 | 
						|
                    cmap.set_under(color_sequence[0])
 | 
						|
                if self._extend_max:
 | 
						|
                    cmap.set_over(color_sequence[-1])
 | 
						|
 | 
						|
        # label lists must be initialized here
 | 
						|
        self.labelTexts = []
 | 
						|
        self.labelCValues = []
 | 
						|
 | 
						|
        self.set_cmap(cmap)
 | 
						|
        if norm is not None:
 | 
						|
            self.set_norm(norm)
 | 
						|
        with self.norm.callbacks.blocked(signal="changed"):
 | 
						|
            if vmin is not None:
 | 
						|
                self.norm.vmin = vmin
 | 
						|
            if vmax is not None:
 | 
						|
                self.norm.vmax = vmax
 | 
						|
        self.norm._changed()
 | 
						|
        self._process_colors()
 | 
						|
 | 
						|
        if self._paths is None:
 | 
						|
            self._paths = self._make_paths_from_contour_generator()
 | 
						|
 | 
						|
        if self.filled:
 | 
						|
            if linewidths is not None:
 | 
						|
                _api.warn_external('linewidths is ignored by contourf')
 | 
						|
            # Lower and upper contour levels.
 | 
						|
            lowers, uppers = self._get_lowers_and_uppers()
 | 
						|
            self.set(
 | 
						|
                edgecolor="none",
 | 
						|
                # Default zorder taken from Collection
 | 
						|
                zorder=kwargs.pop("zorder", 1),
 | 
						|
            )
 | 
						|
 | 
						|
        else:
 | 
						|
            self.set(
 | 
						|
                facecolor="none",
 | 
						|
                linewidths=self._process_linewidths(linewidths),
 | 
						|
                linestyle=self._process_linestyles(linestyles),
 | 
						|
                # Default zorder taken from LineCollection, which is higher
 | 
						|
                # than for filled contours so that lines are displayed on top.
 | 
						|
                zorder=kwargs.pop("zorder", 2),
 | 
						|
                label="_nolegend_",
 | 
						|
            )
 | 
						|
 | 
						|
        self.axes.add_collection(self, autolim=False)
 | 
						|
        self.sticky_edges.x[:] = [self._mins[0], self._maxs[0]]
 | 
						|
        self.sticky_edges.y[:] = [self._mins[1], self._maxs[1]]
 | 
						|
        self.axes.update_datalim([self._mins, self._maxs])
 | 
						|
        self.axes.autoscale_view(tight=True)
 | 
						|
 | 
						|
        self.changed()  # set the colors
 | 
						|
 | 
						|
        if kwargs:
 | 
						|
            _api.warn_external(
 | 
						|
                'The following kwargs were not used by contour: ' +
 | 
						|
                ", ".join(map(repr, kwargs))
 | 
						|
            )
 | 
						|
 | 
						|
    allsegs = property(lambda self: [
 | 
						|
        [subp.vertices for subp in p._iter_connected_components()]
 | 
						|
        for p in self.get_paths()])
 | 
						|
    allkinds = property(lambda self: [
 | 
						|
        [subp.codes for subp in p._iter_connected_components()]
 | 
						|
        for p in self.get_paths()])
 | 
						|
    alpha = property(lambda self: self.get_alpha())
 | 
						|
    linestyles = property(lambda self: self._orig_linestyles)
 | 
						|
 | 
						|
    def get_transform(self):
 | 
						|
        """Return the `.Transform` instance used by this ContourSet."""
 | 
						|
        if self._transform is None:
 | 
						|
            self._transform = self.axes.transData
 | 
						|
        elif (not isinstance(self._transform, mtransforms.Transform)
 | 
						|
              and hasattr(self._transform, '_as_mpl_transform')):
 | 
						|
            self._transform = self._transform._as_mpl_transform(self.axes)
 | 
						|
        return self._transform
 | 
						|
 | 
						|
    def __getstate__(self):
 | 
						|
        state = self.__dict__.copy()
 | 
						|
        # the C object _contour_generator cannot currently be pickled. This
 | 
						|
        # isn't a big issue as it is not actually used once the contour has
 | 
						|
        # been calculated.
 | 
						|
        state['_contour_generator'] = None
 | 
						|
        return state
 | 
						|
 | 
						|
    def legend_elements(self, variable_name='x', str_format=str):
 | 
						|
        """
 | 
						|
        Return a list of artists and labels suitable for passing through
 | 
						|
        to `~.Axes.legend` which represent this ContourSet.
 | 
						|
 | 
						|
        The labels have the form "0 < x <= 1" stating the data ranges which
 | 
						|
        the artists represent.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        variable_name : str
 | 
						|
            The string used inside the inequality used on the labels.
 | 
						|
        str_format : function: float -> str
 | 
						|
            Function used to format the numbers in the labels.
 | 
						|
 | 
						|
        Returns
 | 
						|
        -------
 | 
						|
        artists : list[`.Artist`]
 | 
						|
            A list of the artists.
 | 
						|
        labels : list[str]
 | 
						|
            A list of the labels.
 | 
						|
        """
 | 
						|
        artists = []
 | 
						|
        labels = []
 | 
						|
 | 
						|
        if self.filled:
 | 
						|
            lowers, uppers = self._get_lowers_and_uppers()
 | 
						|
            n_levels = len(self._paths)
 | 
						|
            for idx in range(n_levels):
 | 
						|
                artists.append(mpatches.Rectangle(
 | 
						|
                    (0, 0), 1, 1,
 | 
						|
                    facecolor=self.get_facecolor()[idx],
 | 
						|
                    hatch=self.hatches[idx % len(self.hatches)],
 | 
						|
                ))
 | 
						|
                lower = str_format(lowers[idx])
 | 
						|
                upper = str_format(uppers[idx])
 | 
						|
                if idx == 0 and self.extend in ('min', 'both'):
 | 
						|
                    labels.append(fr'${variable_name} \leq {lower}s$')
 | 
						|
                elif idx == n_levels - 1 and self.extend in ('max', 'both'):
 | 
						|
                    labels.append(fr'${variable_name} > {upper}s$')
 | 
						|
                else:
 | 
						|
                    labels.append(fr'${lower} < {variable_name} \leq {upper}$')
 | 
						|
        else:
 | 
						|
            for idx, level in enumerate(self.levels):
 | 
						|
                artists.append(Line2D(
 | 
						|
                    [], [],
 | 
						|
                    color=self.get_edgecolor()[idx],
 | 
						|
                    linewidth=self.get_linewidths()[idx],
 | 
						|
                    linestyle=self.get_linestyles()[idx],
 | 
						|
                ))
 | 
						|
                labels.append(fr'${variable_name} = {str_format(level)}$')
 | 
						|
 | 
						|
        return artists, labels
 | 
						|
 | 
						|
    def _process_args(self, *args, **kwargs):
 | 
						|
        """
 | 
						|
        Process *args* and *kwargs*; override in derived classes.
 | 
						|
 | 
						|
        Must set self.levels, self.zmin and self.zmax, and update Axes limits.
 | 
						|
        """
 | 
						|
        self.levels = args[0]
 | 
						|
        allsegs = args[1]
 | 
						|
        allkinds = args[2] if len(args) > 2 else None
 | 
						|
        self.zmax = np.max(self.levels)
 | 
						|
        self.zmin = np.min(self.levels)
 | 
						|
 | 
						|
        if allkinds is None:
 | 
						|
            allkinds = [[None] * len(segs) for segs in allsegs]
 | 
						|
 | 
						|
        # Check lengths of levels and allsegs.
 | 
						|
        if self.filled:
 | 
						|
            if len(allsegs) != len(self.levels) - 1:
 | 
						|
                raise ValueError('must be one less number of segments as '
 | 
						|
                                 'levels')
 | 
						|
        else:
 | 
						|
            if len(allsegs) != len(self.levels):
 | 
						|
                raise ValueError('must be same number of segments as levels')
 | 
						|
 | 
						|
        # Check length of allkinds.
 | 
						|
        if len(allkinds) != len(allsegs):
 | 
						|
            raise ValueError('allkinds has different length to allsegs')
 | 
						|
 | 
						|
        # Determine x, y bounds and update axes data limits.
 | 
						|
        flatseglist = [s for seg in allsegs for s in seg]
 | 
						|
        points = np.concatenate(flatseglist, axis=0)
 | 
						|
        self._mins = points.min(axis=0)
 | 
						|
        self._maxs = points.max(axis=0)
 | 
						|
 | 
						|
        # Each entry in (allsegs, allkinds) is a list of (segs, kinds): segs is a list
 | 
						|
        # of (N, 2) arrays of xy coordinates, kinds is a list of arrays of corresponding
 | 
						|
        # pathcodes.  However, kinds can also be None; in which case all paths in that
 | 
						|
        # list are codeless (this case is normalized above).  These lists are used to
 | 
						|
        # construct paths, which then get concatenated.
 | 
						|
        self._paths = [Path.make_compound_path(*map(Path, segs, kinds))
 | 
						|
                       for segs, kinds in zip(allsegs, allkinds)]
 | 
						|
 | 
						|
        return kwargs
 | 
						|
 | 
						|
    def _make_paths_from_contour_generator(self):
 | 
						|
        """Compute ``paths`` using C extension."""
 | 
						|
        if self._paths is not None:
 | 
						|
            return self._paths
 | 
						|
        cg = self._contour_generator
 | 
						|
        empty_path = Path(np.empty((0, 2)))
 | 
						|
        vertices_and_codes = (
 | 
						|
            map(cg.create_filled_contour, *self._get_lowers_and_uppers())
 | 
						|
            if self.filled else
 | 
						|
            map(cg.create_contour, self.levels))
 | 
						|
        return [Path(np.concatenate(vs), np.concatenate(cs)) if len(vs) else empty_path
 | 
						|
                for vs, cs in vertices_and_codes]
 | 
						|
 | 
						|
    def _get_lowers_and_uppers(self):
 | 
						|
        """
 | 
						|
        Return ``(lowers, uppers)`` for filled contours.
 | 
						|
        """
 | 
						|
        lowers = self._levels[:-1]
 | 
						|
        if self.zmin == lowers[0]:
 | 
						|
            # Include minimum values in lowest interval
 | 
						|
            lowers = lowers.copy()  # so we don't change self._levels
 | 
						|
            if self.logscale:
 | 
						|
                lowers[0] = 0.99 * self.zmin
 | 
						|
            else:
 | 
						|
                lowers[0] -= 1
 | 
						|
        uppers = self._levels[1:]
 | 
						|
        return (lowers, uppers)
 | 
						|
 | 
						|
    def changed(self):
 | 
						|
        if not hasattr(self, "cvalues"):
 | 
						|
            self._process_colors()  # Sets cvalues.
 | 
						|
        # Force an autoscale immediately because self.to_rgba() calls
 | 
						|
        # autoscale_None() internally with the data passed to it,
 | 
						|
        # so if vmin/vmax are not set yet, this would override them with
 | 
						|
        # content from *cvalues* rather than levels like we want
 | 
						|
        self.norm.autoscale_None(self.levels)
 | 
						|
        self.set_array(self.cvalues)
 | 
						|
        self.update_scalarmappable()
 | 
						|
        alphas = np.broadcast_to(self.get_alpha(), len(self.cvalues))
 | 
						|
        for label, cv, alpha in zip(self.labelTexts, self.labelCValues, alphas):
 | 
						|
            label.set_alpha(alpha)
 | 
						|
            label.set_color(self.labelMappable.to_rgba(cv))
 | 
						|
        super().changed()
 | 
						|
 | 
						|
    def _autolev(self, N):
 | 
						|
        """
 | 
						|
        Select contour levels to span the data.
 | 
						|
 | 
						|
        The target number of levels, *N*, is used only when the
 | 
						|
        scale is not log and default locator is used.
 | 
						|
 | 
						|
        We need two more levels for filled contours than for
 | 
						|
        line contours, because for the latter we need to specify
 | 
						|
        the lower and upper boundary of each range. For example,
 | 
						|
        a single contour boundary, say at z = 0, requires only
 | 
						|
        one contour line, but two filled regions, and therefore
 | 
						|
        three levels to provide boundaries for both regions.
 | 
						|
        """
 | 
						|
        if self.locator is None:
 | 
						|
            if self.logscale:
 | 
						|
                self.locator = ticker.LogLocator()
 | 
						|
            else:
 | 
						|
                self.locator = ticker.MaxNLocator(N + 1, min_n_ticks=1)
 | 
						|
 | 
						|
        lev = self.locator.tick_values(self.zmin, self.zmax)
 | 
						|
 | 
						|
        try:
 | 
						|
            if self.locator._symmetric:
 | 
						|
                return lev
 | 
						|
        except AttributeError:
 | 
						|
            pass
 | 
						|
 | 
						|
        # Trim excess levels the locator may have supplied.
 | 
						|
        under = np.nonzero(lev < self.zmin)[0]
 | 
						|
        i0 = under[-1] if len(under) else 0
 | 
						|
        over = np.nonzero(lev > self.zmax)[0]
 | 
						|
        i1 = over[0] + 1 if len(over) else len(lev)
 | 
						|
        if self.extend in ('min', 'both'):
 | 
						|
            i0 += 1
 | 
						|
        if self.extend in ('max', 'both'):
 | 
						|
            i1 -= 1
 | 
						|
 | 
						|
        if i1 - i0 < 3:
 | 
						|
            i0, i1 = 0, len(lev)
 | 
						|
 | 
						|
        return lev[i0:i1]
 | 
						|
 | 
						|
    def _process_contour_level_args(self, args, z_dtype):
 | 
						|
        """
 | 
						|
        Determine the contour levels and store in self.levels.
 | 
						|
        """
 | 
						|
        if self.levels is None:
 | 
						|
            if args:
 | 
						|
                levels_arg = args[0]
 | 
						|
            elif np.issubdtype(z_dtype, bool):
 | 
						|
                if self.filled:
 | 
						|
                    levels_arg = [0, .5, 1]
 | 
						|
                else:
 | 
						|
                    levels_arg = [.5]
 | 
						|
            else:
 | 
						|
                levels_arg = 7  # Default, hard-wired.
 | 
						|
        else:
 | 
						|
            levels_arg = self.levels
 | 
						|
        if isinstance(levels_arg, Integral):
 | 
						|
            self.levels = self._autolev(levels_arg)
 | 
						|
        else:
 | 
						|
            self.levels = np.asarray(levels_arg, np.float64)
 | 
						|
        if self.filled and len(self.levels) < 2:
 | 
						|
            raise ValueError("Filled contours require at least 2 levels.")
 | 
						|
        if len(self.levels) > 1 and np.min(np.diff(self.levels)) <= 0.0:
 | 
						|
            raise ValueError("Contour levels must be increasing")
 | 
						|
 | 
						|
    def _process_levels(self):
 | 
						|
        """
 | 
						|
        Assign values to :attr:`layers` based on :attr:`levels`,
 | 
						|
        adding extended layers as needed if contours are filled.
 | 
						|
 | 
						|
        For line contours, layers simply coincide with levels;
 | 
						|
        a line is a thin layer.  No extended levels are needed
 | 
						|
        with line contours.
 | 
						|
        """
 | 
						|
        # Make a private _levels to include extended regions; we
 | 
						|
        # want to leave the original levels attribute unchanged.
 | 
						|
        # (Colorbar needs this even for line contours.)
 | 
						|
        self._levels = list(self.levels)
 | 
						|
 | 
						|
        if self.logscale:
 | 
						|
            lower, upper = 1e-250, 1e250
 | 
						|
        else:
 | 
						|
            lower, upper = -1e250, 1e250
 | 
						|
 | 
						|
        if self.extend in ('both', 'min'):
 | 
						|
            self._levels.insert(0, lower)
 | 
						|
        if self.extend in ('both', 'max'):
 | 
						|
            self._levels.append(upper)
 | 
						|
        self._levels = np.asarray(self._levels)
 | 
						|
 | 
						|
        if not self.filled:
 | 
						|
            self.layers = self.levels
 | 
						|
            return
 | 
						|
 | 
						|
        # Layer values are mid-way between levels in screen space.
 | 
						|
        if self.logscale:
 | 
						|
            # Avoid overflow by taking sqrt before multiplying.
 | 
						|
            self.layers = (np.sqrt(self._levels[:-1])
 | 
						|
                           * np.sqrt(self._levels[1:]))
 | 
						|
        else:
 | 
						|
            self.layers = 0.5 * (self._levels[:-1] + self._levels[1:])
 | 
						|
 | 
						|
    def _process_colors(self):
 | 
						|
        """
 | 
						|
        Color argument processing for contouring.
 | 
						|
 | 
						|
        Note that we base the colormapping on the contour levels
 | 
						|
        and layers, not on the actual range of the Z values.  This
 | 
						|
        means we don't have to worry about bad values in Z, and we
 | 
						|
        always have the full dynamic range available for the selected
 | 
						|
        levels.
 | 
						|
 | 
						|
        The color is based on the midpoint of the layer, except for
 | 
						|
        extended end layers.  By default, the norm vmin and vmax
 | 
						|
        are the extreme values of the non-extended levels.  Hence,
 | 
						|
        the layer color extremes are not the extreme values of
 | 
						|
        the colormap itself, but approach those values as the number
 | 
						|
        of levels increases.  An advantage of this scheme is that
 | 
						|
        line contours, when added to filled contours, take on
 | 
						|
        colors that are consistent with those of the filled regions;
 | 
						|
        for example, a contour line on the boundary between two
 | 
						|
        regions will have a color intermediate between those
 | 
						|
        of the regions.
 | 
						|
 | 
						|
        """
 | 
						|
        self.monochrome = self.cmap.monochrome
 | 
						|
        if self.colors is not None:
 | 
						|
            # Generate integers for direct indexing.
 | 
						|
            i0, i1 = 0, len(self.levels)
 | 
						|
            if self.filled:
 | 
						|
                i1 -= 1
 | 
						|
                # Out of range indices for over and under:
 | 
						|
                if self.extend in ('both', 'min'):
 | 
						|
                    i0 -= 1
 | 
						|
                if self.extend in ('both', 'max'):
 | 
						|
                    i1 += 1
 | 
						|
            self.cvalues = list(range(i0, i1))
 | 
						|
            self.set_norm(mcolors.NoNorm())
 | 
						|
        else:
 | 
						|
            self.cvalues = self.layers
 | 
						|
        self.norm.autoscale_None(self.levels)
 | 
						|
        self.set_array(self.cvalues)
 | 
						|
        self.update_scalarmappable()
 | 
						|
        if self.extend in ('both', 'max', 'min'):
 | 
						|
            self.norm.clip = False
 | 
						|
 | 
						|
    def _process_linewidths(self, linewidths):
 | 
						|
        Nlev = len(self.levels)
 | 
						|
        if linewidths is None:
 | 
						|
            default_linewidth = mpl.rcParams['contour.linewidth']
 | 
						|
            if default_linewidth is None:
 | 
						|
                default_linewidth = mpl.rcParams['lines.linewidth']
 | 
						|
            return [default_linewidth] * Nlev
 | 
						|
        elif not np.iterable(linewidths):
 | 
						|
            return [linewidths] * Nlev
 | 
						|
        else:
 | 
						|
            linewidths = list(linewidths)
 | 
						|
            return (linewidths * math.ceil(Nlev / len(linewidths)))[:Nlev]
 | 
						|
 | 
						|
    def _process_linestyles(self, linestyles):
 | 
						|
        Nlev = len(self.levels)
 | 
						|
        if linestyles is None:
 | 
						|
            tlinestyles = ['solid'] * Nlev
 | 
						|
            if self.monochrome:
 | 
						|
                eps = - (self.zmax - self.zmin) * 1e-15
 | 
						|
                for i, lev in enumerate(self.levels):
 | 
						|
                    if lev < eps:
 | 
						|
                        tlinestyles[i] = self.negative_linestyles
 | 
						|
        else:
 | 
						|
            if isinstance(linestyles, str):
 | 
						|
                tlinestyles = [linestyles] * Nlev
 | 
						|
            elif np.iterable(linestyles):
 | 
						|
                tlinestyles = list(linestyles)
 | 
						|
                if len(tlinestyles) < Nlev:
 | 
						|
                    nreps = int(np.ceil(Nlev / len(linestyles)))
 | 
						|
                    tlinestyles = tlinestyles * nreps
 | 
						|
                if len(tlinestyles) > Nlev:
 | 
						|
                    tlinestyles = tlinestyles[:Nlev]
 | 
						|
            else:
 | 
						|
                raise ValueError("Unrecognized type for linestyles kwarg")
 | 
						|
        return tlinestyles
 | 
						|
 | 
						|
    def _find_nearest_contour(self, xy, indices=None):
 | 
						|
        """
 | 
						|
        Find the point in the unfilled contour plot that is closest (in screen
 | 
						|
        space) to point *xy*.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        xy : tuple[float, float]
 | 
						|
            The reference point (in screen space).
 | 
						|
        indices : list of int or None, default: None
 | 
						|
            Indices of contour levels to consider.  If None (the default), all levels
 | 
						|
            are considered.
 | 
						|
 | 
						|
        Returns
 | 
						|
        -------
 | 
						|
        idx_level_min : int
 | 
						|
            The index of the contour level closest to *xy*.
 | 
						|
        idx_vtx_min : int
 | 
						|
            The index of the `.Path` segment closest to *xy* (at that level).
 | 
						|
        proj : (float, float)
 | 
						|
            The point in the contour plot closest to *xy*.
 | 
						|
        """
 | 
						|
 | 
						|
        # Convert each contour segment to pixel coordinates and then compare the given
 | 
						|
        # point to those coordinates for each contour. This is fast enough in normal
 | 
						|
        # cases, but speedups may be possible.
 | 
						|
 | 
						|
        if self.filled:
 | 
						|
            raise ValueError("Method does not support filled contours")
 | 
						|
 | 
						|
        if indices is None:
 | 
						|
            indices = range(len(self._paths))
 | 
						|
 | 
						|
        d2min = np.inf
 | 
						|
        idx_level_min = idx_vtx_min = proj_min = None
 | 
						|
 | 
						|
        for idx_level in indices:
 | 
						|
            path = self._paths[idx_level]
 | 
						|
            idx_vtx_start = 0
 | 
						|
            for subpath in path._iter_connected_components():
 | 
						|
                if not len(subpath.vertices):
 | 
						|
                    continue
 | 
						|
                lc = self.get_transform().transform(subpath.vertices)
 | 
						|
                d2, proj, leg = _find_closest_point_on_path(lc, xy)
 | 
						|
                if d2 < d2min:
 | 
						|
                    d2min = d2
 | 
						|
                    idx_level_min = idx_level
 | 
						|
                    idx_vtx_min = leg[1] + idx_vtx_start
 | 
						|
                    proj_min = proj
 | 
						|
                idx_vtx_start += len(subpath)
 | 
						|
 | 
						|
        return idx_level_min, idx_vtx_min, proj_min
 | 
						|
 | 
						|
    def find_nearest_contour(self, x, y, indices=None, pixel=True):
 | 
						|
        """
 | 
						|
        Find the point in the contour plot that is closest to ``(x, y)``.
 | 
						|
 | 
						|
        This method does not support filled contours.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        ----------
 | 
						|
        x, y : float
 | 
						|
            The reference point.
 | 
						|
        indices : list of int or None, default: None
 | 
						|
            Indices of contour levels to consider.  If None (the default), all
 | 
						|
            levels are considered.
 | 
						|
        pixel : bool, default: True
 | 
						|
            If *True*, measure distance in pixel (screen) space, which is
 | 
						|
            useful for manual contour labeling; else, measure distance in axes
 | 
						|
            space.
 | 
						|
 | 
						|
        Returns
 | 
						|
        -------
 | 
						|
        path : int
 | 
						|
            The index of the path that is closest to ``(x, y)``.  Each path corresponds
 | 
						|
            to one contour level.
 | 
						|
        subpath : int
 | 
						|
            The index within that closest path of the subpath that is closest to
 | 
						|
            ``(x, y)``.  Each subpath corresponds to one unbroken contour line.
 | 
						|
        index : int
 | 
						|
            The index of the vertices within that subpath that are closest to
 | 
						|
            ``(x, y)``.
 | 
						|
        xmin, ymin : float
 | 
						|
            The point in the contour plot that is closest to ``(x, y)``.
 | 
						|
        d2 : float
 | 
						|
            The squared distance from ``(xmin, ymin)`` to ``(x, y)``.
 | 
						|
        """
 | 
						|
        segment = index = d2 = None
 | 
						|
 | 
						|
        with ExitStack() as stack:
 | 
						|
            if not pixel:
 | 
						|
                # _find_nearest_contour works in pixel space. We want axes space, so
 | 
						|
                # effectively disable the transformation here by setting to identity.
 | 
						|
                stack.enter_context(self._cm_set(
 | 
						|
                    transform=mtransforms.IdentityTransform()))
 | 
						|
 | 
						|
            i_level, i_vtx, (xmin, ymin) = self._find_nearest_contour((x, y), indices)
 | 
						|
 | 
						|
        if i_level is not None:
 | 
						|
            cc_cumlens = np.cumsum(
 | 
						|
                [*map(len, self._paths[i_level]._iter_connected_components())])
 | 
						|
            segment = cc_cumlens.searchsorted(i_vtx, "right")
 | 
						|
            index = i_vtx if segment == 0 else i_vtx - cc_cumlens[segment - 1]
 | 
						|
            d2 = (xmin-x)**2 + (ymin-y)**2
 | 
						|
 | 
						|
        return (i_level, segment, index, xmin, ymin, d2)
 | 
						|
 | 
						|
    def draw(self, renderer):
 | 
						|
        paths = self._paths
 | 
						|
        n_paths = len(paths)
 | 
						|
        if not self.filled or all(hatch is None for hatch in self.hatches):
 | 
						|
            super().draw(renderer)
 | 
						|
            return
 | 
						|
        # In presence of hatching, draw contours one at a time.
 | 
						|
        edgecolors = self.get_edgecolors()
 | 
						|
        if edgecolors.size == 0:
 | 
						|
            edgecolors = ("none",)
 | 
						|
        for idx in range(n_paths):
 | 
						|
            with cbook._setattr_cm(self, _paths=[paths[idx]]), self._cm_set(
 | 
						|
                hatch=self.hatches[idx % len(self.hatches)],
 | 
						|
                array=[self.get_array()[idx]],
 | 
						|
                linewidths=[self.get_linewidths()[idx % len(self.get_linewidths())]],
 | 
						|
                linestyles=[self.get_linestyles()[idx % len(self.get_linestyles())]],
 | 
						|
                edgecolors=edgecolors[idx % len(edgecolors)],
 | 
						|
            ):
 | 
						|
                super().draw(renderer)
 | 
						|
 | 
						|
 | 
						|
@_docstring.interpd
 | 
						|
class QuadContourSet(ContourSet):
 | 
						|
    """
 | 
						|
    Create and store a set of contour lines or filled regions.
 | 
						|
 | 
						|
    This class is typically not instantiated directly by the user but by
 | 
						|
    `~.Axes.contour` and `~.Axes.contourf`.
 | 
						|
 | 
						|
    %(contour_set_attributes)s
 | 
						|
    """
 | 
						|
 | 
						|
    def _process_args(self, *args, corner_mask=None, algorithm=None, **kwargs):
 | 
						|
        """
 | 
						|
        Process args and kwargs.
 | 
						|
        """
 | 
						|
        if args and isinstance(args[0], QuadContourSet):
 | 
						|
            if self.levels is None:
 | 
						|
                self.levels = args[0].levels
 | 
						|
            self.zmin = args[0].zmin
 | 
						|
            self.zmax = args[0].zmax
 | 
						|
            self._corner_mask = args[0]._corner_mask
 | 
						|
            contour_generator = args[0]._contour_generator
 | 
						|
            self._mins = args[0]._mins
 | 
						|
            self._maxs = args[0]._maxs
 | 
						|
            self._algorithm = args[0]._algorithm
 | 
						|
        else:
 | 
						|
            import contourpy
 | 
						|
 | 
						|
            if algorithm is None:
 | 
						|
                algorithm = mpl.rcParams['contour.algorithm']
 | 
						|
            mpl.rcParams.validate["contour.algorithm"](algorithm)
 | 
						|
            self._algorithm = algorithm
 | 
						|
 | 
						|
            if corner_mask is None:
 | 
						|
                if self._algorithm == "mpl2005":
 | 
						|
                    # mpl2005 does not support corner_mask=True so if not
 | 
						|
                    # specifically requested then disable it.
 | 
						|
                    corner_mask = False
 | 
						|
                else:
 | 
						|
                    corner_mask = mpl.rcParams['contour.corner_mask']
 | 
						|
            self._corner_mask = corner_mask
 | 
						|
 | 
						|
            x, y, z = self._contour_args(args, kwargs)
 | 
						|
 | 
						|
            contour_generator = contourpy.contour_generator(
 | 
						|
                x, y, z, name=self._algorithm, corner_mask=self._corner_mask,
 | 
						|
                line_type=contourpy.LineType.SeparateCode,
 | 
						|
                fill_type=contourpy.FillType.OuterCode,
 | 
						|
                chunk_size=self.nchunk)
 | 
						|
 | 
						|
            t = self.get_transform()
 | 
						|
 | 
						|
            # if the transform is not trans data, and some part of it
 | 
						|
            # contains transData, transform the xs and ys to data coordinates
 | 
						|
            if (t != self.axes.transData and
 | 
						|
                    any(t.contains_branch_seperately(self.axes.transData))):
 | 
						|
                trans_to_data = t - self.axes.transData
 | 
						|
                pts = np.vstack([x.flat, y.flat]).T
 | 
						|
                transformed_pts = trans_to_data.transform(pts)
 | 
						|
                x = transformed_pts[..., 0]
 | 
						|
                y = transformed_pts[..., 1]
 | 
						|
 | 
						|
            self._mins = [ma.min(x), ma.min(y)]
 | 
						|
            self._maxs = [ma.max(x), ma.max(y)]
 | 
						|
 | 
						|
        self._contour_generator = contour_generator
 | 
						|
 | 
						|
        return kwargs
 | 
						|
 | 
						|
    def _contour_args(self, args, kwargs):
 | 
						|
        if self.filled:
 | 
						|
            fn = 'contourf'
 | 
						|
        else:
 | 
						|
            fn = 'contour'
 | 
						|
        nargs = len(args)
 | 
						|
 | 
						|
        if 0 < nargs <= 2:
 | 
						|
            z, *args = args
 | 
						|
            z = ma.asarray(z)
 | 
						|
            x, y = self._initialize_x_y(z)
 | 
						|
        elif 2 < nargs <= 4:
 | 
						|
            x, y, z_orig, *args = args
 | 
						|
            x, y, z = self._check_xyz(x, y, z_orig, kwargs)
 | 
						|
 | 
						|
        else:
 | 
						|
            raise _api.nargs_error(fn, takes="from 1 to 4", given=nargs)
 | 
						|
        z = ma.masked_invalid(z, copy=False)
 | 
						|
        self.zmax = z.max().astype(float)
 | 
						|
        self.zmin = z.min().astype(float)
 | 
						|
        if self.logscale and self.zmin <= 0:
 | 
						|
            z = ma.masked_where(z <= 0, z)
 | 
						|
            _api.warn_external('Log scale: values of z <= 0 have been masked')
 | 
						|
            self.zmin = z.min().astype(float)
 | 
						|
        self._process_contour_level_args(args, z.dtype)
 | 
						|
        return (x, y, z)
 | 
						|
 | 
						|
    def _check_xyz(self, x, y, z, kwargs):
 | 
						|
        """
 | 
						|
        Check that the shapes of the input arrays match; if x and y are 1D,
 | 
						|
        convert them to 2D using meshgrid.
 | 
						|
        """
 | 
						|
        x, y = self.axes._process_unit_info([("x", x), ("y", y)], kwargs)
 | 
						|
 | 
						|
        x = np.asarray(x, dtype=np.float64)
 | 
						|
        y = np.asarray(y, dtype=np.float64)
 | 
						|
        z = ma.asarray(z)
 | 
						|
 | 
						|
        if z.ndim != 2:
 | 
						|
            raise TypeError(f"Input z must be 2D, not {z.ndim}D")
 | 
						|
        if z.shape[0] < 2 or z.shape[1] < 2:
 | 
						|
            raise TypeError(f"Input z must be at least a (2, 2) shaped array, "
 | 
						|
                            f"but has shape {z.shape}")
 | 
						|
        Ny, Nx = z.shape
 | 
						|
 | 
						|
        if x.ndim != y.ndim:
 | 
						|
            raise TypeError(f"Number of dimensions of x ({x.ndim}) and y "
 | 
						|
                            f"({y.ndim}) do not match")
 | 
						|
        if x.ndim == 1:
 | 
						|
            nx, = x.shape
 | 
						|
            ny, = y.shape
 | 
						|
            if nx != Nx:
 | 
						|
                raise TypeError(f"Length of x ({nx}) must match number of "
 | 
						|
                                f"columns in z ({Nx})")
 | 
						|
            if ny != Ny:
 | 
						|
                raise TypeError(f"Length of y ({ny}) must match number of "
 | 
						|
                                f"rows in z ({Ny})")
 | 
						|
            x, y = np.meshgrid(x, y)
 | 
						|
        elif x.ndim == 2:
 | 
						|
            if x.shape != z.shape:
 | 
						|
                raise TypeError(
 | 
						|
                    f"Shapes of x {x.shape} and z {z.shape} do not match")
 | 
						|
            if y.shape != z.shape:
 | 
						|
                raise TypeError(
 | 
						|
                    f"Shapes of y {y.shape} and z {z.shape} do not match")
 | 
						|
        else:
 | 
						|
            raise TypeError(f"Inputs x and y must be 1D or 2D, not {x.ndim}D")
 | 
						|
 | 
						|
        return x, y, z
 | 
						|
 | 
						|
    def _initialize_x_y(self, z):
 | 
						|
        """
 | 
						|
        Return X, Y arrays such that contour(Z) will match imshow(Z)
 | 
						|
        if origin is not None.
 | 
						|
        The center of pixel Z[i, j] depends on origin:
 | 
						|
        if origin is None, x = j, y = i;
 | 
						|
        if origin is 'lower', x = j + 0.5, y = i + 0.5;
 | 
						|
        if origin is 'upper', x = j + 0.5, y = Nrows - i - 0.5
 | 
						|
        If extent is not None, x and y will be scaled to match,
 | 
						|
        as in imshow.
 | 
						|
        If origin is None and extent is not None, then extent
 | 
						|
        will give the minimum and maximum values of x and y.
 | 
						|
        """
 | 
						|
        if z.ndim != 2:
 | 
						|
            raise TypeError(f"Input z must be 2D, not {z.ndim}D")
 | 
						|
        elif z.shape[0] < 2 or z.shape[1] < 2:
 | 
						|
            raise TypeError(f"Input z must be at least a (2, 2) shaped array, "
 | 
						|
                            f"but has shape {z.shape}")
 | 
						|
        else:
 | 
						|
            Ny, Nx = z.shape
 | 
						|
        if self.origin is None:  # Not for image-matching.
 | 
						|
            if self.extent is None:
 | 
						|
                return np.meshgrid(np.arange(Nx), np.arange(Ny))
 | 
						|
            else:
 | 
						|
                x0, x1, y0, y1 = self.extent
 | 
						|
                x = np.linspace(x0, x1, Nx)
 | 
						|
                y = np.linspace(y0, y1, Ny)
 | 
						|
                return np.meshgrid(x, y)
 | 
						|
        # Match image behavior:
 | 
						|
        if self.extent is None:
 | 
						|
            x0, x1, y0, y1 = (0, Nx, 0, Ny)
 | 
						|
        else:
 | 
						|
            x0, x1, y0, y1 = self.extent
 | 
						|
        dx = (x1 - x0) / Nx
 | 
						|
        dy = (y1 - y0) / Ny
 | 
						|
        x = x0 + (np.arange(Nx) + 0.5) * dx
 | 
						|
        y = y0 + (np.arange(Ny) + 0.5) * dy
 | 
						|
        if self.origin == 'upper':
 | 
						|
            y = y[::-1]
 | 
						|
        return np.meshgrid(x, y)
 | 
						|
 | 
						|
 | 
						|
_docstring.interpd.register(contour_doc="""
 | 
						|
`.contour` and `.contourf` draw contour lines and filled contours,
 | 
						|
respectively.  Except as noted, function signatures and return values
 | 
						|
are the same for both versions.
 | 
						|
 | 
						|
Parameters
 | 
						|
----------
 | 
						|
X, Y : array-like, optional
 | 
						|
    The coordinates of the values in *Z*.
 | 
						|
 | 
						|
    *X* and *Y* must both be 2D with the same shape as *Z* (e.g.
 | 
						|
    created via `numpy.meshgrid`), or they must both be 1-D such
 | 
						|
    that ``len(X) == N`` is the number of columns in *Z* and
 | 
						|
    ``len(Y) == M`` is the number of rows in *Z*.
 | 
						|
 | 
						|
    *X* and *Y* must both be ordered monotonically.
 | 
						|
 | 
						|
    If not given, they are assumed to be integer indices, i.e.
 | 
						|
    ``X = range(N)``, ``Y = range(M)``.
 | 
						|
 | 
						|
Z : (M, N) array-like
 | 
						|
    The height values over which the contour is drawn.  Color-mapping is
 | 
						|
    controlled by *cmap*, *norm*, *vmin*, and *vmax*.
 | 
						|
 | 
						|
levels : int or array-like, optional
 | 
						|
    Determines the number and positions of the contour lines / regions.
 | 
						|
 | 
						|
    If an int *n*, use `~matplotlib.ticker.MaxNLocator`, which tries
 | 
						|
    to automatically choose no more than *n+1* "nice" contour levels
 | 
						|
    between minimum and maximum numeric values of *Z*.
 | 
						|
 | 
						|
    If array-like, draw contour lines at the specified levels.
 | 
						|
    The values must be in increasing order.
 | 
						|
 | 
						|
Returns
 | 
						|
-------
 | 
						|
`~.contour.QuadContourSet`
 | 
						|
 | 
						|
Other Parameters
 | 
						|
----------------
 | 
						|
corner_mask : bool, default: :rc:`contour.corner_mask`
 | 
						|
    Enable/disable corner masking, which only has an effect if *Z* is
 | 
						|
    a masked array.  If ``False``, any quad touching a masked point is
 | 
						|
    masked out.  If ``True``, only the triangular corners of quads
 | 
						|
    nearest those points are always masked out, other triangular
 | 
						|
    corners comprising three unmasked points are contoured as usual.
 | 
						|
 | 
						|
colors : :mpltype:`color` or list of :mpltype:`color`, optional
 | 
						|
    The colors of the levels, i.e. the lines for `.contour` and the
 | 
						|
    areas for `.contourf`.
 | 
						|
 | 
						|
    The sequence is cycled for the levels in ascending order. If the
 | 
						|
    sequence is shorter than the number of levels, it's repeated.
 | 
						|
 | 
						|
    As a shortcut, a single color may be used in place of one-element lists, i.e.
 | 
						|
    ``'red'`` instead of ``['red']`` to color all levels with the same color.
 | 
						|
 | 
						|
    .. versionchanged:: 3.10
 | 
						|
        Previously a single color had to be expressed as a string, but now any
 | 
						|
        valid color format may be passed.
 | 
						|
 | 
						|
    By default (value *None*), the colormap specified by *cmap*
 | 
						|
    will be used.
 | 
						|
 | 
						|
alpha : float, default: 1
 | 
						|
    The alpha blending value, between 0 (transparent) and 1 (opaque).
 | 
						|
 | 
						|
%(cmap_doc)s
 | 
						|
 | 
						|
    This parameter is ignored if *colors* is set.
 | 
						|
 | 
						|
%(norm_doc)s
 | 
						|
 | 
						|
    This parameter is ignored if *colors* is set.
 | 
						|
 | 
						|
%(vmin_vmax_doc)s
 | 
						|
 | 
						|
    If *vmin* or *vmax* are not given, the default color scaling is based on
 | 
						|
    *levels*.
 | 
						|
 | 
						|
    This parameter is ignored if *colors* is set.
 | 
						|
 | 
						|
%(colorizer_doc)s
 | 
						|
 | 
						|
    This parameter is ignored if *colors* is set.
 | 
						|
 | 
						|
origin : {*None*, 'upper', 'lower', 'image'}, default: None
 | 
						|
    Determines the orientation and exact position of *Z* by specifying
 | 
						|
    the position of ``Z[0, 0]``.  This is only relevant, if *X*, *Y*
 | 
						|
    are not given.
 | 
						|
 | 
						|
    - *None*: ``Z[0, 0]`` is at X=0, Y=0 in the lower left corner.
 | 
						|
    - 'lower': ``Z[0, 0]`` is at X=0.5, Y=0.5 in the lower left corner.
 | 
						|
    - 'upper': ``Z[0, 0]`` is at X=N+0.5, Y=0.5 in the upper left
 | 
						|
      corner.
 | 
						|
    - 'image': Use the value from :rc:`image.origin`.
 | 
						|
 | 
						|
extent : (x0, x1, y0, y1), optional
 | 
						|
    If *origin* is not *None*, then *extent* is interpreted as in
 | 
						|
    `.imshow`: it gives the outer pixel boundaries. In this case, the
 | 
						|
    position of Z[0, 0] is the center of the pixel, not a corner. If
 | 
						|
    *origin* is *None*, then (*x0*, *y0*) is the position of Z[0, 0],
 | 
						|
    and (*x1*, *y1*) is the position of Z[-1, -1].
 | 
						|
 | 
						|
    This argument is ignored if *X* and *Y* are specified in the call
 | 
						|
    to contour.
 | 
						|
 | 
						|
locator : ticker.Locator subclass, optional
 | 
						|
    The locator is used to determine the contour levels if they
 | 
						|
    are not given explicitly via *levels*.
 | 
						|
    Defaults to `~.ticker.MaxNLocator`.
 | 
						|
 | 
						|
extend : {'neither', 'both', 'min', 'max'}, default: 'neither'
 | 
						|
    Determines the ``contourf``-coloring of values that are outside the
 | 
						|
    *levels* range.
 | 
						|
 | 
						|
    If 'neither', values outside the *levels* range are not colored.
 | 
						|
    If 'min', 'max' or 'both', color the values below, above or below
 | 
						|
    and above the *levels* range.
 | 
						|
 | 
						|
    Values below ``min(levels)`` and above ``max(levels)`` are mapped
 | 
						|
    to the under/over values of the `.Colormap`. Note that most
 | 
						|
    colormaps do not have dedicated colors for these by default, so
 | 
						|
    that the over and under values are the edge values of the colormap.
 | 
						|
    You may want to set these values explicitly using
 | 
						|
    `.Colormap.set_under` and `.Colormap.set_over`.
 | 
						|
 | 
						|
    .. note::
 | 
						|
 | 
						|
        An existing `.QuadContourSet` does not get notified if
 | 
						|
        properties of its colormap are changed. Therefore, an explicit
 | 
						|
        call `~.ContourSet.changed()` is needed after modifying the
 | 
						|
        colormap. The explicit call can be left out, if a colorbar is
 | 
						|
        assigned to the `.QuadContourSet` because it internally calls
 | 
						|
        `~.ContourSet.changed()`.
 | 
						|
 | 
						|
    Example::
 | 
						|
 | 
						|
        x = np.arange(1, 10)
 | 
						|
        y = x.reshape(-1, 1)
 | 
						|
        h = x * y
 | 
						|
 | 
						|
        cs = plt.contourf(h, levels=[10, 30, 50],
 | 
						|
            colors=['#808080', '#A0A0A0', '#C0C0C0'], extend='both')
 | 
						|
        cs.cmap.set_over('red')
 | 
						|
        cs.cmap.set_under('blue')
 | 
						|
        cs.changed()
 | 
						|
 | 
						|
xunits, yunits : registered units, optional
 | 
						|
    Override axis units by specifying an instance of a
 | 
						|
    :class:`matplotlib.units.ConversionInterface`.
 | 
						|
 | 
						|
antialiased : bool, optional
 | 
						|
    Enable antialiasing, overriding the defaults.  For
 | 
						|
    filled contours, the default is *False*.  For line contours,
 | 
						|
    it is taken from :rc:`lines.antialiased`.
 | 
						|
 | 
						|
nchunk : int >= 0, optional
 | 
						|
    If 0, no subdivision of the domain.  Specify a positive integer to
 | 
						|
    divide the domain into subdomains of *nchunk* by *nchunk* quads.
 | 
						|
    Chunking reduces the maximum length of polygons generated by the
 | 
						|
    contouring algorithm which reduces the rendering workload passed
 | 
						|
    on to the backend and also requires slightly less RAM.  It can
 | 
						|
    however introduce rendering artifacts at chunk boundaries depending
 | 
						|
    on the backend, the *antialiased* flag and value of *alpha*.
 | 
						|
 | 
						|
linewidths : float or array-like, default: :rc:`contour.linewidth`
 | 
						|
    *Only applies to* `.contour`.
 | 
						|
 | 
						|
    The line width of the contour lines.
 | 
						|
 | 
						|
    If a number, all levels will be plotted with this linewidth.
 | 
						|
 | 
						|
    If a sequence, the levels in ascending order will be plotted with
 | 
						|
    the linewidths in the order specified.
 | 
						|
 | 
						|
    If None, this falls back to :rc:`lines.linewidth`.
 | 
						|
 | 
						|
linestyles : {*None*, 'solid', 'dashed', 'dashdot', 'dotted'}, optional
 | 
						|
    *Only applies to* `.contour`.
 | 
						|
 | 
						|
    If *linestyles* is *None*, the default is 'solid' unless the lines are
 | 
						|
    monochrome. In that case, negative contours will instead take their
 | 
						|
    linestyle from the *negative_linestyles* argument.
 | 
						|
 | 
						|
    *linestyles* can also be an iterable of the above strings specifying a set
 | 
						|
    of linestyles to be used. If this iterable is shorter than the number of
 | 
						|
    contour levels it will be repeated as necessary.
 | 
						|
 | 
						|
negative_linestyles : {*None*, 'solid', 'dashed', 'dashdot', 'dotted'}, \
 | 
						|
                       optional
 | 
						|
    *Only applies to* `.contour`.
 | 
						|
 | 
						|
    If *linestyles* is *None* and the lines are monochrome, this argument
 | 
						|
    specifies the line style for negative contours.
 | 
						|
 | 
						|
    If *negative_linestyles* is *None*, the default is taken from
 | 
						|
    :rc:`contour.negative_linestyle`.
 | 
						|
 | 
						|
    *negative_linestyles* can also be an iterable of the above strings
 | 
						|
    specifying a set of linestyles to be used. If this iterable is shorter than
 | 
						|
    the number of contour levels it will be repeated as necessary.
 | 
						|
 | 
						|
hatches : list[str], optional
 | 
						|
    *Only applies to* `.contourf`.
 | 
						|
 | 
						|
    A list of cross hatch patterns to use on the filled areas.
 | 
						|
    If None, no hatching will be added to the contour.
 | 
						|
 | 
						|
algorithm : {'mpl2005', 'mpl2014', 'serial', 'threaded'}, optional
 | 
						|
    Which contouring algorithm to use to calculate the contour lines and
 | 
						|
    polygons. The algorithms are implemented in
 | 
						|
    `ContourPy <https://github.com/contourpy/contourpy>`_, consult the
 | 
						|
    `ContourPy documentation <https://contourpy.readthedocs.io>`_ for
 | 
						|
    further information.
 | 
						|
 | 
						|
    The default is taken from :rc:`contour.algorithm`.
 | 
						|
 | 
						|
clip_path : `~matplotlib.patches.Patch` or `.Path` or `.TransformedPath`
 | 
						|
    Set the clip path.  See `~matplotlib.artist.Artist.set_clip_path`.
 | 
						|
 | 
						|
    .. versionadded:: 3.8
 | 
						|
 | 
						|
data : indexable object, optional
 | 
						|
    DATA_PARAMETER_PLACEHOLDER
 | 
						|
 | 
						|
Notes
 | 
						|
-----
 | 
						|
1. `.contourf` differs from the MATLAB version in that it does not draw
 | 
						|
   the polygon edges. To draw edges, add line contours with calls to
 | 
						|
   `.contour`.
 | 
						|
 | 
						|
2. `.contourf` fills intervals that are closed at the top; that is, for
 | 
						|
   boundaries *z1* and *z2*, the filled region is::
 | 
						|
 | 
						|
      z1 < Z <= z2
 | 
						|
 | 
						|
   except for the lowest interval, which is closed on both sides (i.e.
 | 
						|
   it includes the lowest value).
 | 
						|
 | 
						|
3. `.contour` and `.contourf` use a `marching squares
 | 
						|
   <https://en.wikipedia.org/wiki/Marching_squares>`_ algorithm to
 | 
						|
   compute contour locations.  More information can be found in
 | 
						|
   `ContourPy documentation <https://contourpy.readthedocs.io>`_.
 | 
						|
""" % _docstring.interpd.params)
 |