예제 #1
0
    def make_artists(self, axes, show_intruder):
        'make self.artists_dict'

        assert self.vec_list is not None

        posa_list = [(v[0], v[1], v[2]) for v in self.vec_list]
        posb_list = [(v[3], v[4], v[5]) for v in self.vec_list]
        
        pos_lists = [posa_list, posb_list]

        if show_intruder:
            pos_lists.append(posb_list)

        for i, pos_list in enumerate(pos_lists):
            x, y, theta = pos_list[0]
            
            l = axes.plot(*zip(*pos_list), f'c-', lw=0, zorder=1)[0]
            l.set_visible(False)
            self.artists_dict[f'line{i}'] = l

            if i == 0:
                lc = LineCollection([], lw=2, animated=True, color='k', zorder=1)
                axes.add_collection(lc)
                self.artists_dict[f'lc{i}'] = lc

            # only sim_index = 0 gets intruder aircraft
            if i == 0 or (i == 1 and show_intruder):
                size = State.plane_size
                box = Bbox.from_bounds(x - size/2, y - size/2, size, size)
                tbox = TransformedBbox(box, axes.transData)
                box_image = BboxImage(tbox, zorder=2)

                theta_deg = (theta - math.pi / 2) / math.pi * 180 # original image is facing up, not right
                img_rotated = ndimage.rotate(State.img, theta_deg, order=1)

                box_image.set_data(img_rotated)
                axes.add_artist(box_image)
                self.artists_dict[f'plane{i}'] = box_image

            if i == 0:
                dot = axes.plot([x], [y], f'k.', markersize=6.0, zorder=2)[0]
                self.artists_dict[f'dot{i}'] = dot

                rad = 1500
                c = patches.Ellipse((x, y), rad, rad, color='k', lw=3.0, fill=False)
                axes.add_patch(c)
                self.artists_dict[f'circle{i}'] = c
예제 #2
0
파일: renderer.py 프로젝트: marqh/holoviews
    def _compute_bbox(self, fig, kw):
        """
        Compute the tight bounding box for each figure once, reducing
        number of required canvas draw calls from N*2 to N+1 as a
        function of the number of frames.

        Tight bounding box computing code here mirrors:
        matplotlib.backend_bases.FigureCanvasBase.print_figure
        as it hasn't been factored out as a function.
        """
        fig_id = id(fig)
        if kw['bbox_inches'] == 'tight':
            if not fig_id in MPLRenderer.drawn:
                fig.set_dpi(self.dpi)
                renderer = fig._cachedRenderer
                bbox_inches = fig.get_tightbbox(renderer)
                bbox_artists = kw.pop("bbox_extra_artists", [])
                bbox_artists += fig.get_default_bbox_extra_artists()
                bbox_filtered = []
                for a in bbox_artists:
                    bbox = a.get_window_extent(renderer)
                    if isinstance(bbox, tuple):
                        continue
                    if a.get_clip_on():
                        clip_box = a.get_clip_box()
                        if clip_box is not None:
                            bbox = Bbox.intersection(bbox, clip_box)
                        clip_path = a.get_clip_path()
                        if clip_path is not None and bbox is not None:
                            clip_path = clip_path.get_fully_transformed_path()
                            bbox = Bbox.intersection(bbox,
                                                     clip_path.get_extents())
                    if bbox is not None and (bbox.width != 0
                                             or bbox.height != 0):
                        bbox_filtered.append(bbox)
                if bbox_filtered:
                    _bbox = Bbox.union(bbox_filtered)
                    trans = Affine2D().scale(1.0 / self.dpi)
                    bbox_extra = TransformedBbox(_bbox, trans)
                    bbox_inches = Bbox.union([bbox_inches, bbox_extra])
                pad = plt.rcParams['savefig.pad_inches']
                bbox_inches = bbox_inches.padded(pad)
                MPLRenderer.drawn[fig_id] = bbox_inches
                kw['bbox_inches'] = bbox_inches
            else:
                kw['bbox_inches'] = MPLRenderer.drawn[fig_id]
        return kw
예제 #3
0
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)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 = TransformedBbox(inset_axes.viewLim, parent_axes.transData)

    fill = kwargs.pop("fill", False)
    pp = BboxPatch(rect, fill=fill, **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
예제 #4
0
    def update_artists(self, axes):
        '''update artists in self.artists_dict to be consistant with self.vec, returns a list of artists'''

        assert self.artists_dict
        rv = []

        x1, y1, theta1, x2, y2, theta2, _ = self.vec

        for i, x, y, theta in zip([0, 1], [x1, x2], [y1, y2], [theta1, theta2]):
            key = f'plane{i}'

            if key in self.artists_dict:
                plane = self.artists_dict[key]
                rv.append(plane)

                if plane.get_visible():
                    theta_deg = (theta - math.pi / 2) / math.pi * 180 # original image is facing up, not right
                    original_size = list(State.img.shape)
                    img_rotated = ndimage.rotate(State.img, theta_deg, order=1)
                    rotated_size = list(img_rotated.shape)
                    ratios = [r / o for r, o in zip(rotated_size, original_size)]
                    plane.set_data(img_rotated)

                    size = State.plane_size
                    width = size * ratios[0]
                    height = size * ratios[1]
                    box = Bbox.from_bounds(x - width/2, y - height/2, width, height)
                    tbox = TransformedBbox(box, axes.transData)
                    plane.bbox = tbox

            key = f'dot{i}'
            if key in self.artists_dict:
                dot = self.artists_dict[f'dot{i}']
                cir = self.artists_dict[f'circle{i}']
                rv += [dot, cir]

                dot.set_data([x], [y])
                cir.set_center((x, y))

        # line collection
        lc = self.artists_dict['lc0']
        rv.append(lc)

        self.update_lc_artist(lc)

        return rv
예제 #5
0
파일: ui.py 프로젝트: jiejohn/paddle
    def adjust_tight_bbox(self, pad=0.1, extra_artists=None):
        bbox_inches = self.figure.get_tightbbox(self.renderer)
        bbox_artists = self.figure.get_default_bbox_extra_artists()

        if extra_artists is None:
            extra_artists = []
            extra_artists.extend([ax.get_legend() for ax in self.figure.axes if ax.get_legend()])

        bbox_artists.extend(extra_artists)
        bbox_filtered = []
        for a in bbox_artists:
            bbox = a.get_window_extent(self.renderer)
            if a.get_clip_on():
                clip_box = a.get_clip_box()
                if clip_box is not None:
                    bbox = Bbox.intersection(bbox, clip_box)
                clip_path = a.get_clip_path()
                if clip_path is not None and bbox is not None:
                    clip_path = clip_path.get_fully_transformed_path()
                    bbox = Bbox.intersection(bbox,
                                             clip_path.get_extents())
            if bbox is not None and (bbox.width != 0 or
                                     bbox.height != 0):
                bbox_filtered.append(bbox)

        if bbox_filtered:
            _bbox = Bbox.union(bbox_filtered)
            trans = Affine2D().scale(1.0 / self.figure.dpi)
            bbox_extra = TransformedBbox(_bbox, trans)
            bbox_inches = Bbox.union([bbox_inches, bbox_extra])

        if pad:
            bbox_inches = bbox_inches.padded(pad)

        rect = (np.array(bbox_inches.bounds).reshape(-1,2) / self.figure.get_size_inches()).flatten()

        # Adjust the rect; values <0 to +; + to zero
        xpad = -np.min((rect[0], (1-rect[2])))
        xpad = 0 if xpad < 0 else xpad
        ypad = -np.min((rect[1], (1-rect[3])))
        ypad = 0 if ypad < 0 else ypad
        rect = np.array([ xpad, ypad, 1-xpad, 1-ypad ])



        self.figure.tight_layout(rect=np.abs(rect))
예제 #6
0
    def create_artists(self, legend, orig_handle, xdescent, ydescent, width,
                       height, fontsize, trans):

        # enlarge the image by these margins
        sx, sy = self.image_stretch

        # create a bounding box to house the image
        bb = Bbox.from_bounds(xdescent - sx, ydescent - sy, width + sx,
                              height + sy)

        tbb = TransformedBbox(bb, trans)
        image = BboxImage(tbb)
        image.set_data(self.image_data)

        self.update_prop(image, orig_handle, legend)

        return [image]
    def get_transform(self):

        # If we don't override this, the transform includes LogTransforms
        # and the final image gets warped to be 'correct' in data space
        # since Matplotlib 2.x:
        #
        #   https://matplotlib.org/users/prev_whats_new/whats_new_2.0.0.html#non-linear-scales-on-image-plots
        #
        # However, we want pixels to always visually be the same size, so we
        # override the transform to not include the LogTransform components.

        xmin, xmax = self._ax.get_xlim()
        ymin, ymax = self._ax.get_ylim()

        bbox = BboxTransformFrom(TransformedBbox(Bbox([[xmin, ymin], [xmax, ymax]]),
                                                 IDENTITY))

        return bbox + self._ax.transAxes
예제 #8
0
    def set_bbox_to_anchor(self, bbox, transform=None):
        """
        Set the bbox that the legend will be anchored to.

        Parameters
        ----------
        bbox : `~matplotlib.transforms.BboxBase` or tuple
            The bounding box can be specified in the following ways:

            - A `.BboxBase` instance
            - A tuple of ``(left, bottom, width, height)`` in the given
              transform (normalized axes coordinate if None)
            - A tuple of ``(left, bottom)`` where the width and height will be
              assumed to be zero.
            - *None*, to remove the bbox anchoring, and use the parent bbox.

        transform : `~matplotlib.transforms.Transform`, optional
            A transform to apply to the bounding box. If not specified, this
            will use a transform to the bounding box of the parent.
        """
        if bbox is None:
            self._bbox_to_anchor = None
            return
        elif isinstance(bbox, BboxBase):
            self._bbox_to_anchor = bbox
        else:
            try:
                l = len(bbox)
            except TypeError as err:
                raise ValueError("Invalid argument for bbox : %s" %
                                 str(bbox)) from err

            if l == 2:
                bbox = [bbox[0], bbox[1], 0, 0]

            self._bbox_to_anchor = Bbox.from_bounds(*bbox)

        if transform is None:
            transform = BboxTransformTo(self.parent.bbox)

        self._bbox_to_anchor = TransformedBbox(self._bbox_to_anchor,
                                               transform)
        self.stale = True
예제 #9
0
 def _draw_text_with_rotation(self,
                              x0,
                              y0,
                              w,
                              h,
                              txt,
                              offset_x=0,
                              offset_y=0,
                              fg="black",
                              rotation=0,
                              debug=False):
     '''
     draw text txt at coordinates (x0, y0) and width w, height h
     (additional x offset offset_x and y offset offset_y if desired)
     with rotation and color fg
     '''
     if debug:
         self.draw_rectangle(x0 - w / 2.0,
                             y0 - h / 2,
                             x0 + w / 2.0,
                             y0 + h / 2.0,
                             fill='none',
                             outline='red')
     clip_rect = mpatches.Rectangle(xy=[x0 - w / 2.0, y0 - h / 2.0],
                                    width=w,
                                    height=0.1,
                                    transform=self._ax.transData)
     textobj = plt.text(x0 + offset_x,
                        y0 + offset_y,
                        txt,
                        color=fg,
                        ha='left',
                        va='center',
                        clip_path=clip_rect,
                        clip_on=True,
                        rotation=rotation)
     # Let's store the clipping box in the object, so that we can use it
     # when clipping text
     textobj._clip = TransformedBbox(bbox=Bbox(
         ((x0 - w / 2.0, y0 - h / 2.0), (x0 + w / 2, y0 + h / 2.0))),
                                     transform=self._ax.transData)
     textobj.set_rotation_mode('anchor')
예제 #10
0
    def create_artists(self, legend, orig_handle,
                       xdescent, ydescent, width, height, fontsize, trans):

        l = matplotlib.lines.Line2D([xdescent+self.offset,xdescent+(width-self.space)/3.+self.offset],
                                     [ydescent+height/2., ydescent+height/2.])
        l.update_from(orig_handle)
        l.set_clip_on(False)
        l.set_transform(trans)

        bb = Bbox.from_bounds(xdescent +(width+self.space)/3.+self.offset,
                              ydescent,
                              height*self.image_data.shape[1]/self.image_data.shape[0],
                              height)

        tbb = TransformedBbox(bb, trans)
        image = BboxImage(tbb)
        image.set_data(self.image_data)

        self.update_prop(image, orig_handle, legend)
        return [l,image]
예제 #11
0
 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]])
예제 #12
0
def initalize():
    mpl.rcParams['lines.linewidth'] = 1
    plt.rcParams.update({'font.size': 6})
    theta = np.linspace(0, 2 * np.pi, 150)
    x1 = r1 * np.cos(theta)
    x2 = r1 * np.sin(theta)
    ax1.plot(x1, x2)

    x1 = r2 * np.cos(theta)
    x2 = r2 * np.sin(theta)
    ax1.plot(x1, x2)

    x = np.linspace(0, 3, 100)
    for i in range(51):
        y = np.repeat(i, 100)
        ax2.plot(x, y, color='black')
    y = np.linspace(0, 50, 100)
    for i in range(4):
        x = np.repeat(i, 100)
        ax2.plot(x, y, color='black')
    x = np.linspace(3, 4, 10)
    y = np.repeat(49, 10)
    ax2.plot(x, y, color='black')
    y = np.repeat(50, 10)
    ax2.plot(x, y, color='black')
    x = np.repeat(4, 10)
    y = np.linspace(49, 50, 10)
    ax2.plot(x, y, color='black')
    mpl.rcParams['lines.linewidth'] = 0.5

    for i, m in enumerate(sorted_meals):
        x = i // 50 + 0.01
        y = 49 - i % 50 + 0.1
        x2 = x + 1
        y2 = y + 1
        box = TransformedBbox(Bbox([[x, y], [x2, y2]]), ax2.transData)
        t = ax2.annotate(m, xy=(x, y), clip_box=box)
        bb = t.get_window_extent(renderer=rend)
        if bb.width > max_width:
            overs.append(i)
        clipped.append(t)
예제 #13
0
    def create_artists(self, legend, orig_handle, xdescent, ydescent, width,
                       height, fontsize, trans):
        # save original visibility and then make it visible
        orig_vis = orig_handle.get_visible()
        orig_handle.set_visible(1)
        # set correct state and image data
        if not orig_vis:
            image_data = VisibilityHandler._unchecked
            self.state = False
        else:
            image_data = VisibilityHandler._checked
            self.state = True

        # ratio for square checkbox
        image_ratio = image_data.shape[1] / image_data.shape[0]

        # create a checkbox artist
        bb = Bbox.from_bounds(xdescent, ydescent, height * image_ratio, height)
        tbb = TransformedBbox(bb, trans)
        image = BboxImage(tbb)
        image.set_data(image_data)

        # update self
        self.update_prop(image, orig_handle, legend)
        self.set_events(image, orig_handle)

        # artists to be returned
        artists = [image]

        # if a handler is given, create artists to be return
        if self.handler is not None:
            artists += self.handler.create_artists(legend, orig_handle,
                                                   xdescent - (height * 2.),
                                                   ydescent,
                                                   width - (height * 2.),
                                                   height, fontsize, trans)

        # revert visibility
        orig_handle.set_visible(orig_vis)
        return artists
예제 #14
0
def plotBWImage(x, y, im, ax, lims=(3, 3)):
    """
        Plots image im on a x,y scatter plot

        :param x: x plot location
        :param y: y plot location
        :param im: bw image to be plotted, should be a 2D numpy matrix
        :param ax: axis to plot the image on
        :param lims: (xlim,ylim) tuple where |x| > xlim and |y| > ylim are not
                        plotted
        :returns: matplotlib.image.BboxImage handle to the plotted image
                    or None if x,y outside of lims
    """
    if np.abs(x) > lims[0] or np.abs(y) > lims[1]:
        return

    bb = Bbox.from_bounds(x, y, 0.1, 0.1)
    bb2 = TransformedBbox(bb, ax.transData)
    bbox_image = BboxImage(bb2, norm=None, origin=None, clip_on=False)

    bbox_image.set_data(im)
    ax.add_artist(bbox_image)
예제 #15
0
def scatter_image(feature_x, feature_y, image_paths, title, save=None, code_list=None):
	"""
	Args:
	feature_x: x座標
	feature_y: y座標
	image_paths: 
	"""
	global Scale

	fig = plt.figure()
	ax = fig.add_subplot(111)
	xlim = [np.min(feature_x)-5, np.max(feature_x)+5]
	ylim = [feature_y.min()-5, feature_y.max()+5]

	for (x, y, path) in zip(feature_x, feature_y, image_paths):
		img = plt.imread(path)

		if EmpCode != "" and get_class ( path ) == EmpCode :
			img = frame_image ( img, 30, 0 )
			
		elif code_list != None :
			idx = code_list.index ( get_class (path) )
			img = frame_image ( img, 30, float(idx) / len(code_list), cmap=cm )

		disp_size = max ( xlim[1]-xlim[0], ylim[1]-ylim[0] ) / Num
		bb = Bbox.from_bounds(x, y, disp_size*Scale, disp_size * Scale)
		bb2 = TransformedBbox(bb, ax.transData)
		bbox_image = BboxImage(bb2, cmap=None, norm=None, origin=None, clip_on=False)
			
		bbox_image.set_data(img)
		ax.add_artist(bbox_image)

	ax.set_ylim(*ylim)
	ax.set_xlim(*xlim)
	plt.title(title)
	if save is not None:
		plt.savefig(save, dpi=600)
	plt.show()
예제 #16
0
        def zoom_effect(ax1, ax2, **kwargs):
            """
            ax1 : the main axes
            ax1 : the zoomed axes

            Connect ax1 and ax2.  The xmin & xmax will be taken from the
            ax1.viewLim.
            """

            tt = ax1.transScale + (ax1.transLimits + ax2.transAxes)
            trans = blended_transform_factory(ax2.transData, tt)

            mybbox1 = ax1.bbox
            mybbox2 = TransformedBbox(ax1.viewLim, trans)

            prop_patches = kwargs.copy()
            prop_patches["ec"] = "none"
            prop_patches["alpha"] = 0.2

            c1, c2, bbox_patch1, bbox_patch2, patch = connect_bbox(
                mybbox1,
                mybbox2,
                loc1a=3,
                loc2a=2,
                loc1b=4,
                loc2b=1,
                prop_lines=kwargs,
                prop_patches=prop_patches,
            )

            ax1.add_patch(bbox_patch1)
            ax2.add_patch(bbox_patch2)
            ax2.add_patch(c1)
            ax2.add_patch(c2)
            ax2.add_patch(patch)

            return c1, c2, bbox_patch1, bbox_patch2, patch
예제 #17
0
    def create_artists(self, legend, orig_handle, xdescent, ydescent, width,
                       height, fontsize, trans):

        l = Line2D([xdescent + self.offset], [ydescent + height / 2.],
                   c=self.color,
                   ls="",
                   marker="o",
                   mfc=self.color,
                   mec=self.color)
        l.set_clip_on(False)

        bb = Bbox.from_bounds(
            xdescent + (width + self.space) / 3. + self.offset, ydescent,
            height * self.image_data.shape[1] / self.image_data.shape[0],
            height)

        tbb = TransformedBbox(bb, trans)
        image = BboxImage(tbb)
        image.set_data(self.image_data)
        image.set_alpha(1.0)
        legend.set_alpha(1.0)

        self.update_prop(image, orig_handle, legend)
        return [l, image]
예제 #18
0
def main(argv):
    parser = argparse.ArgumentParser(prog='VIZ')
    parser.add_argument('source', help='path to the source metadata file')
    args = parser.parse_args(argv[1:])

    # read in the data file
    data = pandas.read_csv(args.source, sep='\t')

    # load up data
    vis_x = data['x'].tolist()
    vis_y = data['y'].tolist()
    img_data = data['filename'].tolist()

    # Create figure
    fig = plt.figure()
    ax = fig.add_subplot(111)

    for x, y, filepath in zip(vis_x, vis_y, img_data):
        im = plt.imread(filepath)

        bb = Bbox.from_bounds(x, y, 1, 1)
        bb2 = TransformedBbox(bb, ax.transData)
        bbox_image = BboxImage(bb2, norm=None, origin=None, clip_on=False)

        bbox_image.set_data(im)
        ax.add_artist(bbox_image)

    #plt.scatter(vis_x, vis_y, marker='s', c=vis_y)
    #plt.colorbar(ticks=range(10))
    #plt.clim(-0.5, 9.5)

    # Set the x and y limits
    ax.set_ylim(-50, 50)
    ax.set_xlim(-50, 50)

    plt.show()
예제 #19
0
    def connect_bbox(bbox1, bbox2, loc1, loc2=None):
        """
        Helper function to obtain a Path from one bbox to another.

        Parameters
        ----------
        bbox1, bbox2 : `matplotlib.transforms.Bbox`
            Bounding boxes to connect.

        loc1 : {1, 2, 3, 4}
            Corner of *bbox1* to use. Valid values are::

                'upper right'  : 1,
                'upper left'   : 2,
                'lower left'   : 3,
                'lower right'  : 4

        loc2 : {1, 2, 3, 4}, optional
            Corner of *bbox2* to use. If None, defaults to *loc1*.
            Valid values are::

                'upper right'  : 1,
                'upper left'   : 2,
                'lower left'   : 3,
                'lower right'  : 4

        Returns
        -------
        path : `matplotlib.path.Path`
            A line segment from the *loc1* corner of *bbox1* to the *loc2*
            corner of *bbox2*.
        """
        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]])
예제 #20
0
ncol = 2
nrow = len(maps) // ncol + 1

xpad_fraction = 0.3
dx = 1. / (ncol + xpad_fraction * (ncol - 1))

ypad_fraction = 0.3
dy = 1. / (nrow + ypad_fraction * (nrow - 1))

for i, m in enumerate(maps):
    ix, iy = divmod(i, nrow)

    bbox0 = Bbox.from_bounds(ix * dx * (1 + xpad_fraction),
                             1. - iy * dy * (1 + ypad_fraction) - dy, dx, dy)
    bbox = TransformedBbox(bbox0, ax2.transAxes)

    bbox_image = BboxImage(bbox,
                           cmap=plt.get_cmap(m),
                           norm=None,
                           origin=None,
                           **kwargs)

    bbox_image.set_data(a)
    ax2.add_artist(bbox_image)

plt.show()

#############################################################################
#
# .. admonition:: References
def plot_uncertainty(y_true, y_pred, background_data):
    """ Plots the samples over background data

    :param y_true: ground truth
    :param y_pred: model samples
    :param background_data: numpy array containing the positions with data
    """
    _, ax = plt.subplots()

    # Flips y (imshow assumes 0 on top, growing down)
    y_true[:, 1] = -y_true[:, 1] + GRID_SIZE
    y_pred[:, 1] = -y_pred[:, 1] + GRID_SIZE

    # Plots the background in black and black (white = positions with data)
    ax.imshow(-background_data,
              cmap='Greys',
              vmin=-1.0,
              vmax=0.0,
              alpha=MAP_ALPHA)

    # Plots the MC Dropout samples in blue, with some transparency
    ax.scatter(x=y_pred[:, 0, :],
               y=y_pred[:, 1, :],
               c='b',
               s=3,
               alpha=0.15,
               edgecolors='none',
               label="MC Dropout samples")

    # Plots the true position in solid red
    ax.scatter(x=y_true[:, 0],
               y=y_true[:, 1],
               c='r',
               s=5,
               edgecolors='none',
               label="True positions")
    ax.legend(loc="upper right", labelcolor=['b', 'r'], fontsize="small")

    # Plots a zoomed detail
    axins = zoomed_inset_axes(ax, zoom=3, loc='lower left')
    axins.imshow(-background_data,
                 cmap='Greys',
                 vmin=-1.0,
                 vmax=0.0,
                 alpha=MAP_ALPHA)
    rand_idx = np.random.randint(low=0, high=y_true.shape[0], size=1)
    axins.scatter(
        x=y_pred[rand_idx, 0, :],
        y=y_pred[rand_idx, 1, :],
        c='b',
        s=4,
        alpha=0.15,
        edgecolors='none',
    )
    axins.scatter(x=y_true[rand_idx, 0],
                  y=y_true[rand_idx, 1],
                  c='r',
                  edgecolors='none')

    # Limit the region for zoom
    axins.set_xlim(y_true[rand_idx, 0] - ZOOM_SIZE,
                   y_true[rand_idx, 0] + ZOOM_SIZE)
    axins.set_ylim(y_true[rand_idx, 1] - ZOOM_SIZE,
                   y_true[rand_idx, 1] + ZOOM_SIZE)

    # Hides ticks
    plt.xticks(visible=False)
    plt.yticks(visible=False)

    # draw a bbox of the region of the inset axes in the parent axes and connecting lines between
    # the bbox and the inset axes area. Also inverts the y of the inner plot, as the outer plot
    # is inverted (imshow)
    axins.invert_yaxis()

    # ================================================================
    # (adapted from `mark_inset`, removing the connectors)
    parent_axes = ax
    inset_axes = axins
    kwargs = {"fc": "none", "ec": "0.0"}
    rect = TransformedBbox(inset_axes.viewLim, parent_axes.transData)

    fill = bool({'fc', 'facecolor', 'color'}.intersection(kwargs))
    pp = BboxPatch(rect, fill=fill, **kwargs)
    parent_axes.add_patch(pp)
    # ================================================================

    plt.draw()
    plt.savefig(PLOT_PATH, dpi=300)
    logging.info("Plot written to %s", PLOT_PATH)
예제 #22
0
def auto_adjust_subplotpars(fig,
                            renderer,
                            nrows_ncols,
                            num1num2_list,
                            subplot_list,
                            ax_bbox_list=None,
                            pad=1.08,
                            h_pad=None,
                            w_pad=None,
                            rect=None):
    """
    Return a dict of subplot parameters to adjust spacing between subplots
    or ``None`` if resulting axes would have zero height or width.

    Note that this function ignores geometry information of subplot
    itself, but uses what is given by the *nrows_ncols* and *num1num2_list*
    parameters.  Also, the results could be incorrect if some subplots have
    ``adjustable=datalim``.

    Parameters
    ----------
    nrows_ncols : Tuple[int, int]
        Number of rows and number of columns of the grid.
    num1num2_list : List[int]
        List of numbers specifying the area occupied by the subplot
    subplot_list : list of subplots
        List of subplots that will be used to calculate optimal subplot_params.
    pad : float
        Padding between the figure edge and the edges of subplots, as a
        fraction of the font size.
    h_pad, w_pad : float
        Padding (height/width) between edges of adjacent subplots, as a
        fraction of the font size.  Defaults to *pad*.
    rect : Tuple[float, float, float, float]
        [left, bottom, right, top] in normalized (0, 1) figure coordinates.
    """
    rows, cols = nrows_ncols

    font_size_inches = (
        FontProperties(size=rcParams["font.size"]).get_size_in_points() / 72)
    pad_inches = pad * font_size_inches
    vpad_inches = h_pad * font_size_inches if h_pad is not None else pad_inches
    hpad_inches = w_pad * font_size_inches if w_pad is not None else pad_inches

    if len(num1num2_list) != len(subplot_list) or len(subplot_list) == 0:
        raise ValueError

    if rect is None:
        margin_left = margin_bottom = margin_right = margin_top = None
    else:
        margin_left, margin_bottom, _right, _top = rect
        margin_right = 1 - _right if _right else None
        margin_top = 1 - _top if _top else None

    vspaces = np.zeros((rows + 1, cols))
    hspaces = np.zeros((rows, cols + 1))

    if ax_bbox_list is None:
        ax_bbox_list = [
            Bbox.union([ax.get_position(original=True) for ax in subplots])
            for subplots in subplot_list
        ]

    for subplots, ax_bbox, (num1, num2) in zip(subplot_list, ax_bbox_list,
                                               num1num2_list):
        if all(not ax.get_visible() for ax in subplots):
            continue

        bb = []
        for ax in subplots:
            if ax.get_visible():
                try:
                    bb += [ax.get_tightbbox(renderer, for_layout_only=True)]
                except TypeError:
                    bb += [ax.get_tightbbox(renderer)]

        tight_bbox_raw = Bbox.union(bb)
        tight_bbox = TransformedBbox(tight_bbox_raw,
                                     fig.transFigure.inverted())

        row1, col1 = divmod(num1, cols)
        if num2 is None:
            num2 = num1
        row2, col2 = divmod(num2, cols)

        for row_i in range(row1, row2 + 1):
            hspaces[row_i, col1] += ax_bbox.xmin - tight_bbox.xmin  # left
            hspaces[row_i, col2 + 1] += tight_bbox.xmax - ax_bbox.xmax  # right
        for col_i in range(col1, col2 + 1):
            vspaces[row1, col_i] += tight_bbox.ymax - ax_bbox.ymax  # top
            vspaces[row2 + 1, col_i] += ax_bbox.ymin - tight_bbox.ymin  # bot.

    fig_width_inch, fig_height_inch = fig.get_size_inches()

    # margins can be negative for axes with aspect applied, so use max(, 0) to
    # make them nonnegative.
    if not margin_left:
        margin_left = (max(hspaces[:, 0].max(), 0) +
                       pad_inches / fig_width_inch)
    if not margin_right:
        margin_right = (max(hspaces[:, -1].max(), 0) +
                        pad_inches / fig_width_inch)
    if not margin_top:
        margin_top = (max(vspaces[0, :].max(), 0) +
                      pad_inches / fig_height_inch)
        suptitle = fig._suptitle
        if suptitle and suptitle.get_in_layout():
            rel_suptitle_height = fig.transFigure.inverted().transform_bbox(
                suptitle.get_window_extent(renderer)).height
            margin_top += rel_suptitle_height + pad_inches / fig_height_inch
    if not margin_bottom:
        margin_bottom = (max(vspaces[-1, :].max(), 0) +
                         pad_inches / fig_height_inch)

    if margin_left + margin_right >= 1:
        cbook._warn_external('Tight layout not applied. The left and right '
                             'margins cannot be made large enough to '
                             'accommodate all axes decorations. ')
        return None
    if margin_bottom + margin_top >= 1:
        cbook._warn_external('Tight layout not applied. The bottom and top '
                             'margins cannot be made large enough to '
                             'accommodate all axes decorations. ')
        return None

    kwargs = dict(left=margin_left,
                  right=1 - margin_right,
                  bottom=margin_bottom,
                  top=1 - margin_top)

    if cols > 1:
        hspace = hspaces[:, 1:-1].max() + hpad_inches / fig_width_inch
        # axes widths:
        h_axes = (1 - margin_right - margin_left - hspace * (cols - 1)) / cols
        if h_axes < 0:
            cbook._warn_external('Tight layout not applied. tight_layout '
                                 'cannot make axes width small enough to '
                                 'accommodate all axes decorations')
            return None
        else:
            kwargs["wspace"] = hspace / h_axes
    if rows > 1:
        vspace = vspaces[1:-1, :].max() + vpad_inches / fig_height_inch
        v_axes = (1 - margin_top - margin_bottom - vspace * (rows - 1)) / rows
        if v_axes < 0:
            cbook._warn_external('Tight layout not applied. tight_layout '
                                 'cannot make axes height small enough to '
                                 'accommodate all axes decorations')
            return None
        else:
            kwargs["hspace"] = vspace / v_axes

    return kwargs
예제 #23
0
def auto_adjust_subplotpars(fig,
                            renderer,
                            nrows_ncols,
                            num1num2_list,
                            subplot_list,
                            ax_bbox_list=None,
                            pad=1.08,
                            h_pad=None,
                            w_pad=None,
                            rect=None):
    """
    Return a dict of subplot parameters to adjust spacing between subplots
    or ``None`` if resulting axes would have zero height or width.

    Note that this function ignores geometry information of subplot
    itself, but uses what is given by the *nrows_ncols* and *num1num2_list*
    parameters.  Also, the results could be incorrect if some subplots have
    ``adjustable=datalim``.

    Parameters
    ----------
    nrows_ncols : Tuple[int, int]
        Number of rows and number of columns of the grid.
    num1num2_list : List[int]
        List of numbers specifying the area occupied by the subplot
    subplot_list : list of subplots
        List of subplots that will be used to calculate optimal subplot_params.
    pad : float
        Padding between the figure edge and the edges of subplots, as a
        fraction of the font size.
    h_pad, w_pad : float
        Padding (height/width) between edges of adjacent subplots, as a
        fraction of the font size.  Defaults to *pad*.
    rect : Tuple[float, float, float, float]
        [left, bottom, right, top] in normalized (0, 1) figure coordinates.
    """
    rows, cols = nrows_ncols

    font_size_inches = (
        FontProperties(size=rcParams["font.size"]).get_size_in_points() / 72)
    pad_inches = pad * font_size_inches
    if h_pad is not None:
        vpad_inches = h_pad * font_size_inches
    else:
        vpad_inches = pad_inches

    if w_pad is not None:
        hpad_inches = w_pad * font_size_inches
    else:
        hpad_inches = pad_inches

    if len(num1num2_list) != len(subplot_list) or len(subplot_list) == 0:
        raise ValueError

    if rect is None:
        margin_left = margin_bottom = margin_right = margin_top = None
    else:
        margin_left, margin_bottom, _right, _top = rect
        if _right:
            margin_right = 1 - _right
        else:
            margin_right = None
        if _top:
            margin_top = 1 - _top
        else:
            margin_top = None

    vspaces = [[] for i in range((rows + 1) * cols)]
    hspaces = [[] for i in range(rows * (cols + 1))]

    union = Bbox.union

    if ax_bbox_list is None:
        ax_bbox_list = []
        for subplots in subplot_list:
            ax_bbox = union(
                [ax.get_position(original=True) for ax in subplots])
            ax_bbox_list.append(ax_bbox)

    for subplots, ax_bbox, (num1, num2) in zip(subplot_list, ax_bbox_list,
                                               num1num2_list):
        if all(not ax.get_visible() for ax in subplots):
            continue

        tight_bbox_raw = union([
            ax.get_tightbbox(renderer) for ax in subplots if ax.get_visible()
        ])
        tight_bbox = TransformedBbox(tight_bbox_raw,
                                     fig.transFigure.inverted())

        row1, col1 = divmod(num1, cols)

        if num2 is None:
            # left
            hspaces[row1 * (cols + 1) + col1].append(
                _get_left(tight_bbox, ax_bbox))
            # right
            hspaces[row1 * (cols + 1) + (col1 + 1)].append(
                _get_right(tight_bbox, ax_bbox))
            # top
            vspaces[row1 * cols + col1].append(_get_top(tight_bbox, ax_bbox))
            # bottom
            vspaces[(row1 + 1) * cols + col1].append(
                _get_bottom(tight_bbox, ax_bbox))

        else:
            row2, col2 = divmod(num2, cols)

            for row_i in range(row1, row2 + 1):
                # left
                hspaces[row_i * (cols + 1) + col1].append(
                    _get_left(tight_bbox, ax_bbox))
                # right
                hspaces[row_i * (cols + 1) + (col2 + 1)].append(
                    _get_right(tight_bbox, ax_bbox))
            for col_i in range(col1, col2 + 1):
                # top
                vspaces[row1 * cols + col_i].append(
                    _get_top(tight_bbox, ax_bbox))
                # bottom
                vspaces[(row2 + 1) * cols + col_i].append(
                    _get_bottom(tight_bbox, ax_bbox))

    fig_width_inch, fig_height_inch = fig.get_size_inches()

    # margins can be negative for axes with aspect applied. And we
    # append + [0] to make minimum margins 0

    if not margin_left:
        margin_left = max([sum(s) for s in hspaces[::cols + 1]] + [0])
        margin_left += pad_inches / fig_width_inch

    if not margin_right:
        margin_right = max([sum(s) for s in hspaces[cols::cols + 1]] + [0])
        margin_right += pad_inches / fig_width_inch

    if not margin_top:
        margin_top = max([sum(s) for s in vspaces[:cols]] + [0])
        margin_top += pad_inches / fig_height_inch

    if not margin_bottom:
        margin_bottom = max([sum(s) for s in vspaces[-cols:]] + [0])
        margin_bottom += pad_inches / fig_height_inch

    if margin_left + margin_right >= 1:
        warnings.warn('Tight layout not applied. The left and right margins '
                      'cannot be made large '
                      'enough to accommodate all axes decorations. ')
        return None
    if margin_bottom + margin_top >= 1:
        warnings.warn('Tight layout not applied. '
                      'The bottom and top margins cannot be made large '
                      'enough to accommodate all axes decorations. ')
        return None

    kwargs = dict(left=margin_left,
                  right=1 - margin_right,
                  bottom=margin_bottom,
                  top=1 - margin_top)
    if cols > 1:
        hspace = (max(
            sum(s) for i in range(rows)
            for s in hspaces[i * (cols + 1) + 1:(i + 1) * (cols + 1) - 1]) +
                  hpad_inches / fig_width_inch)
        # axes widths:
        h_axes = (1 - margin_right - margin_left - hspace * (cols - 1)) / cols
        if h_axes < 0:
            warnings.warn('Tight layout not applied. '
                          'tight_layout cannot make axes width small enough '
                          'to accommodate all axes decorations')
            return None
        else:
            kwargs["wspace"] = hspace / h_axes

    if rows > 1:
        vspace = (max(sum(s) for s in vspaces[cols:-cols]) +
                  vpad_inches / fig_height_inch)
        v_axes = (1 - margin_top - margin_bottom - vspace * (rows - 1)) / rows
        if v_axes < 0:
            warnings.warn('Tight layout not applied. '
                          'tight_layout cannot make axes height small enough '
                          'to accommodate all axes decorations')
            return None
        else:
            kwargs["hspace"] = vspace / v_axes

    return kwargs
예제 #24
0
def auto_adjust_subplotpars(fig,
                            renderer,
                            nrows_ncols,
                            num1num2_list,
                            subplot_list,
                            ax_bbox_list=None,
                            pad=1.2,
                            h_pad=None,
                            w_pad=None,
                            rect=None):
    """
    Return a dictionary of subplot parameters so that spacing between
    subplots are adjusted. Note that this function ignore geometry
    information of subplot itself, but uses what is given by
    *nrows_ncols* and *num1num2_list* parameteres. Also, the results could be
    incorrect if some subplots have ``adjustable=datalim``.

    Parameters:

    nrows_ncols
      number of rows and number of columns of the grid.

    num1num2_list
      list of numbers specifying the area occupied by the subplot

    subplot_list
      list of subplots that will be used to calcuate optimal subplot_params.

    pad : float
      padding between the figure edge and the edges of subplots, as a fraction of the font-size.
    h_pad, w_pad : float
      padding (height/width) between edges of adjacent subplots.
        Defaults to `pad_inches`.

    rect
      [left, bottom, right, top] in normalized (0, 1) figure coordinates.
    """

    rows, cols = nrows_ncols

    pad_inches = pad * FontProperties(
        size=rcParams["font.size"]).get_size_in_points() / renderer.dpi

    if h_pad is not None:
        vpad_inches = h_pad * FontProperties(
            size=rcParams["font.size"]).get_size_in_points() / renderer.dpi
    else:
        vpad_inches = pad_inches

    if w_pad is not None:
        hpad_inches = w_pad * FontProperties(
            size=rcParams["font.size"]).get_size_in_points() / renderer.dpi
    else:
        hpad_inches = pad_inches

    if len(subplot_list) == 0:
        raise RuntimeError("")

    if len(num1num2_list) != len(subplot_list):
        raise RuntimeError("")

    if rect is None:
        margin_left, margin_bottom, margin_right, margin_top = None, None, None, None
    else:
        margin_left, margin_bottom, _right, _top = rect
        if _right: margin_right = 1. - _right
        else: margin_right = None
        if _top: margin_top = 1. - _top
        else: margin_top = None

    vspaces = [[] for i in range((rows + 1) * cols)]
    hspaces = [[] for i in range(rows * (cols + 1))]

    union = Bbox.union

    if ax_bbox_list is None:
        ax_bbox_list = []
        for subplots in subplot_list:
            ax_bbox = union(
                [ax.get_position(original=True) for ax in subplots])
            ax_bbox_list.append(ax_bbox)

    for subplots, ax_bbox, (num1, num2) in zip(subplot_list, ax_bbox_list,
                                               num1num2_list):

        #ax_bbox = union([ax.get_position(original=True) for ax in subplots])

        tight_bbox_raw = union([ax.get_tightbbox(renderer) for ax in subplots])
        tight_bbox = TransformedBbox(tight_bbox_raw,
                                     fig.transFigure.inverted())

        row1, col1 = divmod(num1, cols)

        if num2 is None:
            # left
            hspaces[row1 * (cols + 1) + col1].append(
                _get_left(tight_bbox, ax_bbox))
            # right
            hspaces[row1 * (cols + 1) + (col1 + 1)].append(
                _get_right(tight_bbox, ax_bbox))
            # top
            vspaces[row1 * cols + col1].append(_get_top(tight_bbox, ax_bbox))
            # bottom
            vspaces[(row1 + 1) * cols + col1].append(
                _get_bottom(tight_bbox, ax_bbox))

        else:
            row2, col2 = divmod(num2, cols)

            for row_i in range(row1, row2 + 1):
                # left
                hspaces[row_i * (cols + 1) + col1].append(
                    _get_left(tight_bbox, ax_bbox))
                # right
                hspaces[row_i * (cols + 1) + (col2 + 1)].append(
                    _get_right(tight_bbox, ax_bbox))
            for col_i in range(col1, col2 + 1):
                # top
                vspaces[row1 * cols + col_i].append(
                    _get_top(tight_bbox, ax_bbox))
                # bottom
                vspaces[(row2 + 1) * cols + col_i].append(
                    _get_bottom(tight_bbox, ax_bbox))

    fig_width_inch, fig_height_inch = fig.get_size_inches()

    # margins can be negative for axes with aspect applied. And we
    # append + [0] to make minimum margins 0

    if not margin_left:
        margin_left = max([sum(s) for s in hspaces[::cols + 1]] + [0])
        margin_left += pad_inches / fig_width_inch

    if not margin_right:
        margin_right = max([sum(s) for s in hspaces[cols::cols + 1]] + [0])
        margin_right += pad_inches / fig_width_inch

    if not margin_top:
        margin_top = max([sum(s) for s in vspaces[:cols]] + [0])
        margin_top += pad_inches / fig_height_inch

    if not margin_bottom:
        margin_bottom = max([sum(s) for s in vspaces[-cols:]] + [0])
        margin_bottom += pad_inches / fig_height_inch

    kwargs = dict(left=margin_left,
                  right=1 - margin_right,
                  bottom=margin_bottom,
                  top=1 - margin_top)

    if cols > 1:
        hspace = max([
            sum(s) for i in range(rows)
            for s in hspaces[i * (cols + 1) + 1:(i + 1) * (cols + 1) - 1]
        ])
        hspace += hpad_inches / fig_width_inch
        h_axes = ((1 - margin_right - margin_left) - hspace *
                  (cols - 1)) / cols

        kwargs["wspace"] = hspace / h_axes

    if rows > 1:
        vspace = max([sum(s) for s in vspaces[cols:-cols]])
        vspace += vpad_inches / fig_height_inch
        v_axes = ((1 - margin_top - margin_bottom) - vspace *
                  (rows - 1)) / rows

        kwargs["hspace"] = vspace / v_axes

    return kwargs
예제 #25
0
    def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
                    unsampled=False, round_to_pixel_border=True):
        """
        Normalize, rescale and color the image `A` from the given
        in_bbox (in data space), to the given out_bbox (in pixel
        space) clipped to the given clip_bbox (also in pixel space),
        and magnified by the magnification factor.

        `A` may be a greyscale image (MxN) with a dtype of `float32`,
        `float64`, `uint16` or `uint8`, or an RGBA image (MxNx4) with
        a dtype of `float32`, `float64`, or `uint8`.

        If `unsampled` is True, the image will not be scaled, but an
        appropriate affine transformation will be returned instead.

        If `round_to_pixel_border` is True, the output image size will
        be rounded to the nearest pixel boundary.  This makes the
        images align correctly with the axes.  It should not be used
        in cases where you want exact scaling, however, such as
        FigureImage.

        Returns the resulting (image, x, y, trans), where (x, y) is
        the upper left corner of the result in pixel space, and
        `trans` is the affine transformation from the image to pixel
        space.
        """
        if A is None:
            raise RuntimeError('You must first set the image'
                               ' array or the image attribute')

        clipped_bbox = Bbox.intersection(out_bbox, clip_bbox)

        if clipped_bbox is None:
            return None, 0, 0, None

        out_width_base = clipped_bbox.width * magnification
        out_height_base = clipped_bbox.height * magnification

        if out_width_base == 0 or out_height_base == 0:
            return None, 0, 0, None

        if self.origin == 'upper':
            # Flip the input image using a transform.  This avoids the
            # problem with flipping the array, which results in a copy
            # when it is converted to contiguous in the C wrapper
            t0 = Affine2D().translate(0, -A.shape[0]).scale(1, -1)
        else:
            t0 = IdentityTransform()

        t0 += (
            Affine2D()
            .scale(
                in_bbox.width / A.shape[1],
                in_bbox.height / A.shape[0])
            .translate(in_bbox.x0, in_bbox.y0)
            + self.get_transform())

        t = (t0
             + Affine2D().translate(
                 -clipped_bbox.x0,
                 -clipped_bbox.y0)
             .scale(magnification, magnification))

        # So that the image is aligned with the edge of the axes, we want
        # to round up the output width to the next integer.  This also
        # means scaling the transform just slightly to account for the
        # extra subpixel.
        if (t.is_affine and round_to_pixel_border and
            (out_width_base % 1.0 != 0.0 or
             out_height_base % 1.0 != 0.0)):
            out_width = int(ceil(out_width_base) + 1)
            out_height = int(ceil(out_height_base) + 1)
            extra_width = (out_width - out_width_base) / out_width_base
            extra_height = (out_height - out_height_base) / out_height_base
            t += Affine2D().scale(
                1.0 + extra_width, 1.0 + extra_height)
        else:
            out_width = int(out_width_base)
            out_height = int(out_height_base)

        if not unsampled:
            if A.ndim == 2:
                A = self.norm(A)
                if A.dtype.kind == 'f':
                    # For floating-point greyscale images, we treat negative
                    # numbers as transparent.

                    # TODO: Use np.full when we support Numpy 1.9 as a
                    # minimum
                    output = np.empty((out_height, out_width), dtype=A.dtype)
                    output[...] = -100.0
                else:
                    output = np.zeros((out_height, out_width), dtype=A.dtype)

                alpha = 1.0
            elif A.ndim == 3:
                # Always convert to RGBA, even if only RGB input
                if A.shape[2] == 3:
                    A = _rgb_to_rgba(A)
                elif A.shape[2] != 4:
                    raise ValueError("Invalid dimensions, got %s" % (A.shape,))

                output = np.zeros((out_height, out_width, 4), dtype=A.dtype)

                alpha = self.get_alpha()
                if alpha is None:
                    alpha = 1.0
            else:
                raise ValueError("Invalid dimensions, got %s" % (A.shape,))

            _image.resample(
                A, output, t, _interpd_[self.get_interpolation()],
                self.get_resample(), alpha,
                self.get_filternorm() or 0.0, self.get_filterrad() or 0.0)

            output = self.to_rgba(output, bytes=True, norm=False)

            # Apply alpha *after* if the input was greyscale
            if A.ndim == 2:
                alpha = self.get_alpha()
                if alpha is not None and alpha != 1.0:
                    alpha_channel = output[:, :, 3]
                    alpha_channel[:] = np.asarray(
                        np.asarray(alpha_channel, np.float32) * alpha,
                        np.uint8)
        else:
            if self._imcache is None:
                self._imcache = self.to_rgba(A, bytes=True, norm=(A.ndim == 2))
            output = self._imcache

            # Subset the input image to only the part that will be
            # displayed
            subset = TransformedBbox(
                clip_bbox, t0.frozen().inverted()).frozen()
            output = output[
                int(max(subset.ymin, 0)):
                int(min(subset.ymax + 1, output.shape[0])),
                int(max(subset.xmin, 0)):
                int(min(subset.xmax + 1, output.shape[1]))]

            t = Affine2D().translate(
                int(max(subset.xmin, 0)), int(max(subset.ymin, 0))) + t

        return output, clipped_bbox.x0, clipped_bbox.y0, t
예제 #26
0
def test_fill_facecolor():
    fig, ax = plt.subplots(1, 5)
    fig.set_size_inches(5, 5)
    for i in range(1, 4):
        ax[i].yaxis.set_visible(False)
    ax[4].yaxis.tick_right()
    bbox = Bbox.from_extents(0, 0.4, 1, 0.6)

    # fill with blue by setting 'fc' field
    bbox1 = TransformedBbox(bbox, ax[0].transData)
    bbox2 = TransformedBbox(bbox, ax[1].transData)
    # set color to BboxConnectorPatch
    p = BboxConnectorPatch(
        bbox1, bbox2, loc1a=1, loc2a=2, loc1b=4, loc2b=3,
        ec="r", fc="b")
    p.set_clip_on(False)
    ax[0].add_patch(p)
    # set color to marked area
    axins = zoomed_inset_axes(ax[0], 1, loc='upper right')
    axins.set_xlim(0, 0.2)
    axins.set_ylim(0, 0.2)
    plt.gca().axes.xaxis.set_ticks([])
    plt.gca().axes.yaxis.set_ticks([])
    mark_inset(ax[0], axins, loc1=2, loc2=4, fc="b", ec="0.5")

    # fill with yellow by setting 'facecolor' field
    bbox3 = TransformedBbox(bbox, ax[1].transData)
    bbox4 = TransformedBbox(bbox, ax[2].transData)
    # set color to BboxConnectorPatch
    p = BboxConnectorPatch(
        bbox3, bbox4, loc1a=1, loc2a=2, loc1b=4, loc2b=3,
        ec="r", facecolor="y")
    p.set_clip_on(False)
    ax[1].add_patch(p)
    # set color to marked area
    axins = zoomed_inset_axes(ax[1], 1, loc='upper right')
    axins.set_xlim(0, 0.2)
    axins.set_ylim(0, 0.2)
    plt.gca().axes.xaxis.set_ticks([])
    plt.gca().axes.yaxis.set_ticks([])
    mark_inset(ax[1], axins, loc1=2, loc2=4, facecolor="y", ec="0.5")

    # fill with green by setting 'color' field
    bbox5 = TransformedBbox(bbox, ax[2].transData)
    bbox6 = TransformedBbox(bbox, ax[3].transData)
    # set color to BboxConnectorPatch
    p = BboxConnectorPatch(
        bbox5, bbox6, loc1a=1, loc2a=2, loc1b=4, loc2b=3,
        ec="r", color="g")
    p.set_clip_on(False)
    ax[2].add_patch(p)
    # set color to marked area
    axins = zoomed_inset_axes(ax[2], 1, loc='upper right')
    axins.set_xlim(0, 0.2)
    axins.set_ylim(0, 0.2)
    plt.gca().axes.xaxis.set_ticks([])
    plt.gca().axes.yaxis.set_ticks([])
    mark_inset(ax[2], axins, loc1=2, loc2=4, color="g", ec="0.5")

    # fill with green but color won't show if set fill to False
    bbox7 = TransformedBbox(bbox, ax[3].transData)
    bbox8 = TransformedBbox(bbox, ax[4].transData)
    # BboxConnectorPatch won't show green
    p = BboxConnectorPatch(
        bbox7, bbox8, loc1a=1, loc2a=2, loc1b=4, loc2b=3,
        ec="r", fc="g", fill=False)
    p.set_clip_on(False)
    ax[3].add_patch(p)
    # marked area won't show green
    axins = zoomed_inset_axes(ax[3], 1, loc='upper right')
    axins.set_xlim(0, 0.2)
    axins.set_ylim(0, 0.2)
    axins.xaxis.set_ticks([])
    axins.yaxis.set_ticks([])
    mark_inset(ax[3], axins, loc1=2, loc2=4, fc="g", ec="0.5", fill=False)
예제 #27
0
    def __init__(self,
                 figsize   = None,  # defaults to rc figure.figsize
                 dpi       = None,  # defaults to rc figure.dpi
                 facecolor = None,  # defaults to rc figure.facecolor
                 edgecolor = None,  # defaults to rc figure.edgecolor
                 linewidth = 0.0,   # the default linewidth of the frame
                 frameon = True,    # whether or not to draw the figure frame
                 subplotpars = None, # default to rc
                 tight_layout = None, # default to rc figure.autolayout
                 ):
        """
        *figsize*
            w,h tuple in inches

        *dpi*
            Dots per inch

        *facecolor*
            The figure patch facecolor; defaults to rc ``figure.facecolor``

        *edgecolor*
            The figure patch edge color; defaults to rc ``figure.edgecolor``

        *linewidth*
            The figure patch edge linewidth; the default linewidth of the frame

        *frameon*
            If *False*, suppress drawing the figure frame

        *subplotpars*
            A :class:`SubplotParams` instance, defaults to rc

        *tight_layout*
            If *False* use *subplotpars*; if *True* adjust subplot
            parameters using :meth:`tight_layout`.  Defaults to
            rc ``figure.autolayout``.
        """
        Artist.__init__(self)

        self.callbacks = cbook.CallbackRegistry()

        if figsize is None  : figsize   = rcParams['figure.figsize']
        if dpi is None      : dpi       = rcParams['figure.dpi']
        if facecolor is None: facecolor = rcParams['figure.facecolor']
        if edgecolor is None: edgecolor = rcParams['figure.edgecolor']

        self.dpi_scale_trans = Affine2D()
        self.dpi = dpi
        self.bbox_inches = Bbox.from_bounds(0, 0, *figsize)
        self.bbox = TransformedBbox(self.bbox_inches, self.dpi_scale_trans)

        self.frameon = frameon

        self.transFigure = BboxTransformTo(self.bbox)

        # the figurePatch name is deprecated
        self.patch = self.figurePatch = Rectangle(
            xy=(0,0), width=1, height=1,
            facecolor=facecolor, edgecolor=edgecolor,
            linewidth=linewidth,
            )
        self._set_artist_props(self.patch)
        self.patch.set_aa(False)

        self._hold = rcParams['axes.hold']
        self.canvas = None

        if subplotpars is None:
            subplotpars = SubplotParams()

        self.subplotpars = subplotpars
        self.set_tight_layout(tight_layout)

        self._axstack = AxesStack()  # track all figure axes and current axes
        self.clf()
        self._cachedRenderer = None
예제 #28
0
 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
예제 #29
0
def adjust_bbox(fig, bbox_inches, fixed_dpi=None):
    """
    Temporarily adjust the figure so that only the specified area
    (bbox_inches) is saved.

    It modifies figure.bbox, figure.bbox_inches,
    figure.transFigure._boxout, and figure.patch.  While the figure size
    changes, the scale of the original figure is conserved.  A
    function which restores the original values are returned.
    """

    origBbox = fig.bbox
    origBboxInches = fig.bbox_inches
    _boxout = fig.transFigure._boxout

    asp_list = []
    locator_list = []
    for ax in fig.axes:
        pos = ax.get_position(original=False).frozen()
        locator_list.append(ax.get_axes_locator())
        asp_list.append(ax.get_aspect())

        def _l(a, r, pos=pos):
            return pos

        ax.set_axes_locator(_l)
        ax.set_aspect("auto")

    def restore_bbox():

        for ax, asp, loc in zip(fig.axes, asp_list, locator_list):
            ax.set_aspect(asp)
            ax.set_axes_locator(loc)

        fig.bbox = origBbox
        fig.bbox_inches = origBboxInches
        fig.transFigure._boxout = _boxout
        fig.transFigure.invalidate()
        fig.patch.set_bounds(0, 0, 1, 1)

    if fixed_dpi is not None:
        tr = Affine2D().scale(fixed_dpi)
        dpi_scale = fixed_dpi / fig.dpi
    else:
        tr = Affine2D().scale(fig.dpi)
        dpi_scale = 1.

    _bbox = TransformedBbox(bbox_inches, tr)

    fig.bbox_inches = Bbox.from_bounds(0, 0, bbox_inches.width,
                                       bbox_inches.height)
    x0, y0 = _bbox.x0, _bbox.y0
    w1, h1 = fig.bbox.width * dpi_scale, fig.bbox.height * dpi_scale
    fig.transFigure._boxout = Bbox.from_bounds(-x0, -y0, w1, h1)
    fig.transFigure.invalidate()

    fig.bbox = TransformedBbox(fig.bbox_inches, tr)

    fig.patch.set_bounds(x0 / w1, y0 / h1, fig.bbox.width / w1,
                         fig.bbox.height / h1)

    return restore_bbox
예제 #30
0
    years = np.arange(2004, 2009)
    box_colors = [
        (0.8, 0.2, 0.2),
        (0.2, 0.8, 0.2),
        (0.2, 0.2, 0.8),
        (0.7, 0.5, 0.8),
        (0.3, 0.8, 0.7),
    ]
    heights = np.random.random(years.shape) * 7000 + 3000

    fmt = ScalarFormatter(useOffset=False)
    ax.xaxis.set_major_formatter(fmt)

    for year, h, bc in zip(years, heights, box_colors):
        bbox0 = Bbox.from_extents(year - 0.4, 0., year + 0.4, h)
        bbox = TransformedBbox(bbox0, ax.transData)
        rb_patch = RibbonBoxImage(bbox, bc, interpolation="bicubic")

        ax.add_artist(rb_patch)

        ax.annotate(r"%d" % (int(h / 100.) * 100), (year, h),
                    va="bottom",
                    ha="center")

    patch_gradient = BboxImage(
        ax.bbox,
        interpolation="bicubic",
        zorder=0.1,
    )
    gradient = np.zeros((2, 2, 4), dtype=float)
    gradient[:, :, :3] = [1, 1, 0.]
예제 #31
0
    def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0,
                    unsampled=False, round_to_pixel_border=True):
        """
        Normalize, rescale and color the image `A` from the given
        in_bbox (in data space), to the given out_bbox (in pixel
        space) clipped to the given clip_bbox (also in pixel space),
        and magnified by the magnification factor.

        `A` may be a greyscale image (MxN) with a dtype of `float32`,
        `float64`, `uint16` or `uint8`, or an RGBA image (MxNx4) with
        a dtype of `float32`, `float64`, or `uint8`.

        If `unsampled` is True, the image will not be scaled, but an
        appropriate affine transformation will be returned instead.

        If `round_to_pixel_border` is True, the output image size will
        be rounded to the nearest pixel boundary.  This makes the
        images align correctly with the axes.  It should not be used
        in cases where you want exact scaling, however, such as
        FigureImage.

        Returns the resulting (image, x, y, trans), where (x, y) is
        the upper left corner of the result in pixel space, and
        `trans` is the affine transformation from the image to pixel
        space.
        """
        if A is None:
            raise RuntimeError('You must first set the image'
                               ' array or the image attribute')

        clipped_bbox = Bbox.intersection(out_bbox, clip_bbox)

        if clipped_bbox is None:
            return None, 0, 0, None

        out_width_base = clipped_bbox.width * magnification
        out_height_base = clipped_bbox.height * magnification

        if out_width_base == 0 or out_height_base == 0:
            return None, 0, 0, None

        if self.origin == 'upper':
            # Flip the input image using a transform.  This avoids the
            # problem with flipping the array, which results in a copy
            # when it is converted to contiguous in the C wrapper
            t0 = Affine2D().translate(0, -A.shape[0]).scale(1, -1)
        else:
            t0 = IdentityTransform()

        t0 += (
            Affine2D()
            .scale(
                in_bbox.width / A.shape[1],
                in_bbox.height / A.shape[0])
            .translate(in_bbox.x0, in_bbox.y0)
            + self.get_transform())

        t = (t0
             + Affine2D().translate(
                 -clipped_bbox.x0,
                 -clipped_bbox.y0)
             .scale(magnification, magnification))

        # So that the image is aligned with the edge of the axes, we want
        # to round up the output width to the next integer.  This also
        # means scaling the transform just slightly to account for the
        # extra subpixel.
        if (t.is_affine and round_to_pixel_border and
                (out_width_base % 1.0 != 0.0 or out_height_base % 1.0 != 0.0)):
            out_width = int(ceil(out_width_base))
            out_height = int(ceil(out_height_base))
            extra_width = (out_width - out_width_base) / out_width_base
            extra_height = (out_height - out_height_base) / out_height_base
            t += Affine2D().scale(
                1.0 + extra_width, 1.0 + extra_height)
        else:
            out_width = int(out_width_base)
            out_height = int(out_height_base)

        if not unsampled:
            created_rgba_mask = False

            if A.ndim not in (2, 3):
                raise ValueError("Invalid dimensions, got %s" % (A.shape,))

            if A.ndim == 2:
                A = self.norm(A)
                if A.dtype.kind == 'f':
                    # If the image is greyscale, convert to RGBA and
                    # use the extra channels for resizing the over,
                    # under, and bad pixels.  This is needed because
                    # Agg's resampler is very aggressive about
                    # clipping to [0, 1] and we use out-of-bounds
                    # values to carry the over/under/bad information
                    rgba = np.empty((A.shape[0], A.shape[1], 4), dtype=A.dtype)
                    rgba[..., 0] = A  # normalized data
                    rgba[..., 1] = A < 0  # under data
                    rgba[..., 2] = A > 1  # over data
                    rgba[..., 3] = ~A.mask  # bad data
                    A = rgba
                    output = np.zeros((out_height, out_width, 4),
                                      dtype=A.dtype)
                    alpha = 1.0
                    created_rgba_mask = True
                else:
                    # colormap norms that output integers (ex NoNorm
                    # and BoundaryNorm) to RGBA space before
                    # interpolating.  This is needed due to the
                    # Agg resampler only working on floats in the
                    # range [0, 1] and because interpolating indexes
                    # into an arbitrary LUT may be problematic.
                    #
                    # This falls back to interpolating in RGBA space which
                    # can produce it's own artifacts of colors not in the map
                    # showing up in the final image.
                    A = self.cmap(A, alpha=self.get_alpha(), bytes=True)

            if not created_rgba_mask:
                # Always convert to RGBA, even if only RGB input
                if A.shape[2] == 3:
                    A = _rgb_to_rgba(A)
                elif A.shape[2] != 4:
                    raise ValueError("Invalid dimensions, got %s" % (A.shape,))

                output = np.zeros((out_height, out_width, 4), dtype=A.dtype)

                alpha = self.get_alpha()
                if alpha is None:
                    alpha = 1.0

            _image.resample(
                A, output, t, _interpd_[self.get_interpolation()],
                self.get_resample(), alpha,
                self.get_filternorm() or 0.0, self.get_filterrad() or 0.0)

            if created_rgba_mask:
                # Convert back to a masked greyscale array so
                # colormapping works correctly
                hid_output = output
                output = np.ma.masked_array(
                    hid_output[..., 0], hid_output[..., 3] < 0.5)
                # relabel under data
                output[hid_output[..., 1] > .5] = -1
                # relabel over data
                output[hid_output[..., 2] > .5] = 2

            output = self.to_rgba(output, bytes=True, norm=False)

            # Apply alpha *after* if the input was greyscale without a mask
            if A.ndim == 2 or created_rgba_mask:
                alpha = self.get_alpha()
                if alpha is not None and alpha != 1.0:
                    alpha_channel = output[:, :, 3]
                    alpha_channel[:] = np.asarray(
                        np.asarray(alpha_channel, np.float32) * alpha,
                        np.uint8)
        else:
            if self._imcache is None:
                self._imcache = self.to_rgba(A, bytes=True, norm=(A.ndim == 2))
            output = self._imcache

            # Subset the input image to only the part that will be
            # displayed
            subset = TransformedBbox(
                clip_bbox, t0.frozen().inverted()).frozen()
            output = output[
                int(max(subset.ymin, 0)):
                int(min(subset.ymax + 1, output.shape[0])),
                int(max(subset.xmin, 0)):
                int(min(subset.xmax + 1, output.shape[1]))]

            t = Affine2D().translate(
                int(max(subset.xmin, 0)), int(max(subset.ymin, 0))) + t

        return output, clipped_bbox.x0, clipped_bbox.y0, t
예제 #32
0
class Figure(Artist):

    """
    The Figure instance supports callbacks through a *callbacks*
    attribute which is a :class:`matplotlib.cbook.CallbackRegistry`
    instance.  The events you can connect to are 'dpi_changed', and
    the callback will be called with ``func(fig)`` where fig is the
    :class:`Figure` instance.

    *patch*
       The figure patch is drawn by a
       :class:`matplotlib.patches.Rectangle` instance

    *suppressComposite*
       For multiple figure images, the figure will make composite
       images depending on the renderer option_image_nocomposite
       function.  If suppressComposite is True|False, this will
       override the renderer.
    """

    def __str__(self):
        return "Figure(%gx%g)" % tuple(self.bbox.size)

    def __init__(self,
                 figsize   = None,  # defaults to rc figure.figsize
                 dpi       = None,  # defaults to rc figure.dpi
                 facecolor = None,  # defaults to rc figure.facecolor
                 edgecolor = None,  # defaults to rc figure.edgecolor
                 linewidth = 0.0,   # the default linewidth of the frame
                 frameon = True,    # whether or not to draw the figure frame
                 subplotpars = None, # default to rc
                 tight_layout = None, # default to rc figure.autolayout
                 ):
        """
        *figsize*
            w,h tuple in inches

        *dpi*
            Dots per inch

        *facecolor*
            The figure patch facecolor; defaults to rc ``figure.facecolor``

        *edgecolor*
            The figure patch edge color; defaults to rc ``figure.edgecolor``

        *linewidth*
            The figure patch edge linewidth; the default linewidth of the frame

        *frameon*
            If *False*, suppress drawing the figure frame

        *subplotpars*
            A :class:`SubplotParams` instance, defaults to rc

        *tight_layout*
            If *False* use *subplotpars*; if *True* adjust subplot
            parameters using :meth:`tight_layout`.  Defaults to
            rc ``figure.autolayout``.
        """
        Artist.__init__(self)

        self.callbacks = cbook.CallbackRegistry()

        if figsize is None  : figsize   = rcParams['figure.figsize']
        if dpi is None      : dpi       = rcParams['figure.dpi']
        if facecolor is None: facecolor = rcParams['figure.facecolor']
        if edgecolor is None: edgecolor = rcParams['figure.edgecolor']

        self.dpi_scale_trans = Affine2D()
        self.dpi = dpi
        self.bbox_inches = Bbox.from_bounds(0, 0, *figsize)
        self.bbox = TransformedBbox(self.bbox_inches, self.dpi_scale_trans)

        self.frameon = frameon

        self.transFigure = BboxTransformTo(self.bbox)

        # the figurePatch name is deprecated
        self.patch = self.figurePatch = Rectangle(
            xy=(0,0), width=1, height=1,
            facecolor=facecolor, edgecolor=edgecolor,
            linewidth=linewidth,
            )
        self._set_artist_props(self.patch)
        self.patch.set_aa(False)

        self._hold = rcParams['axes.hold']
        self.canvas = None

        if subplotpars is None:
            subplotpars = SubplotParams()

        self.subplotpars = subplotpars
        self.set_tight_layout(tight_layout)

        self._axstack = AxesStack()  # track all figure axes and current axes
        self.clf()
        self._cachedRenderer = None

    def _get_axes(self):
        return self._axstack.as_list()

    axes = property(fget=_get_axes, doc="Read-only: list of axes in Figure")

    def _get_dpi(self):
        return self._dpi
    def _set_dpi(self, dpi):
        self._dpi = dpi
        self.dpi_scale_trans.clear().scale(dpi, dpi)
        self.callbacks.process('dpi_changed', self)
    dpi = property(_get_dpi, _set_dpi)

    def get_tight_layout(self):
        """
        Return the Boolean flag, True to use :meth`tight_layout` when drawing.
        """
        return self._tight

    def set_tight_layout(self, tight):
        """
        Set whether :meth:`tight_layout` is used upon drawing.
        If None, the rcParams['figure.autolayout'] value will be set.

        ACCEPTS: [True | False | None]
        """
        if tight is None:
            tight = rcParams['figure.autolayout']
        tight = bool(tight)
        self._tight = tight

    def autofmt_xdate(self, bottom=0.2, rotation=30, ha='right'):
        """
        Date ticklabels often overlap, so it is useful to rotate them
        and right align them.  Also, a common use case is a number of
        subplots with shared xaxes where the x-axis is date data.  The
        ticklabels are often long, and it helps to rotate them on the
        bottom subplot and turn them off on other subplots, as well as
        turn off xlabels.

        *bottom*
            The bottom of the subplots for :meth:`subplots_adjust`

        *rotation*
            The rotation of the xtick labels

        *ha*
            The horizontal alignment of the xticklabels
        """
        allsubplots = np.alltrue([hasattr(ax, 'is_last_row') for ax in self.axes])
        if len(self.axes)==1:
            for label in self.axes[0].get_xticklabels():
                label.set_ha(ha)
                label.set_rotation(rotation)
        else:
            if allsubplots:
                for ax in self.get_axes():
                    if ax.is_last_row():
                        for label in ax.get_xticklabels():
                            label.set_ha(ha)
                            label.set_rotation(rotation)
                    else:
                        for label in ax.get_xticklabels():
                            label.set_visible(False)
                        ax.set_xlabel('')

        if allsubplots:
            self.subplots_adjust(bottom=bottom)

    def get_children(self):
        'get a list of artists contained in the figure'
        children = [self.patch]
        children.extend(self.artists)
        children.extend(self.axes)
        children.extend(self.lines)
        children.extend(self.patches)
        children.extend(self.texts)
        children.extend(self.images)
        children.extend(self.legends)
        return children

    def contains(self, mouseevent):
        """
        Test whether the mouse event occurred on the figure.

        Returns True,{}
        """
        if callable(self._contains): return self._contains(self,mouseevent)
        #inside = mouseevent.x >= 0 and mouseevent.y >= 0
        inside = self.bbox.contains(mouseevent.x,mouseevent.y)

        return inside,{}

    def get_window_extent(self, *args, **kwargs):
        'get the figure bounding box in display space; kwargs are void'
        return self.bbox

    def suptitle(self, t, **kwargs):
        """
        Add a centered title to the figure.

        kwargs are :class:`matplotlib.text.Text` properties.  Using figure
        coordinates, the defaults are:

          *x* : 0.5
            The x location of the text in figure coords

          *y* : 0.98
            The y location of the text in figure coords

          *horizontalalignment* : 'center'
            The horizontal alignment of the text

          *verticalalignment* : 'top'
            The vertical alignment of the text

        A :class:`matplotlib.text.Text` instance is returned.

        Example::

          fig.suptitle('this is the figure title', fontsize=12)
        """
        x = kwargs.pop('x', 0.5)
        y = kwargs.pop('y', 0.98)
        if ('horizontalalignment' not in kwargs) and ('ha' not in kwargs):
            kwargs['horizontalalignment'] = 'center'

        if ('verticalalignment' not in kwargs) and ('va' not in kwargs):
            kwargs['verticalalignment'] = 'top'

        t = self.text(x, y, t, **kwargs)
        return t

    def set_canvas(self, canvas):
        """
        Set the canvas the contains the figure

        ACCEPTS: a FigureCanvas instance
        """
        self.canvas = canvas

    def hold(self, b=None):
        """
        Set the hold state.  If hold is None (default), toggle the
        hold state.  Else set the hold state to boolean value b.

        Eg::

            hold()      # toggle hold
            hold(True)  # hold is on
            hold(False) # hold is off
        """
        if b is None: self._hold = not self._hold
        else: self._hold = b

    def figimage(self, X,
                 xo=0,
                 yo=0,
                 alpha=None,
                 norm=None,
                 cmap=None,
                 vmin=None,
                 vmax=None,
                 origin=None,
                 **kwargs):
        """
        Adds a non-resampled image to the figure.

        call signatures::

          figimage(X, **kwargs)

        adds a non-resampled array *X* to the figure.

        ::

          figimage(X, xo, yo)

        with pixel offsets *xo*, *yo*,

        *X* must be a float array:

        * If *X* is MxN, assume luminance (grayscale)
        * If *X* is MxNx3, assume RGB
        * If *X* is MxNx4, assume RGBA

        Optional keyword arguments:

          =========   ==========================================================
          Keyword     Description
          =========   ==========================================================
          xo or yo    An integer, the *x* and *y* image offset in pixels
          cmap        a :class:`matplotlib.colors.Colormap` instance, eg cm.jet.
                      If *None*, default to the rc ``image.cmap`` value
          norm        a :class:`matplotlib.colors.Normalize` instance. The
                      default is normalization().  This scales luminance -> 0-1
          vmin|vmax   are used to scale a luminance image to 0-1.  If either is
                      *None*, the min and max of the luminance values will be
                      used.  Note if you pass a norm instance, the settings for
                      *vmin* and *vmax* will be ignored.
          alpha       the alpha blending value, default is *None*
          origin      [ 'upper' | 'lower' ] Indicates where the [0,0] index of
                      the array is in the upper left or lower left corner of
                      the axes. Defaults to the rc image.origin value
          =========   ==========================================================

        figimage complements the axes image
        (:meth:`~matplotlib.axes.Axes.imshow`) which will be resampled
        to fit the current axes.  If you want a resampled image to
        fill the entire figure, you can define an
        :class:`~matplotlib.axes.Axes` with size [0,1,0,1].

        An :class:`matplotlib.image.FigureImage` instance is returned.

        .. plot:: mpl_examples/pylab_examples/figimage_demo.py


        Additional kwargs are Artist kwargs passed on to
        :class:`~matplotlib.image.FigureImage`
        """

        if not self._hold: self.clf()

        im = FigureImage(self, cmap, norm, xo, yo, origin, **kwargs)
        im.set_array(X)
        im.set_alpha(alpha)
        if norm is None:
            im.set_clim(vmin, vmax)
        self.images.append(im)
        return im

    def set_size_inches(self, *args, **kwargs):
        """
        set_size_inches(w,h, forward=False)

        Set the figure size in inches

        Usage::

             fig.set_size_inches(w,h)  # OR
             fig.set_size_inches((w,h) )

        optional kwarg *forward=True* will cause the canvas size to be
        automatically updated; eg you can resize the figure window
        from the shell

        ACCEPTS: a w,h tuple with w,h in inches
        """

        forward = kwargs.get('forward', False)
        if len(args)==1:
            w,h = args[0]
        else:
            w,h = args

        dpival = self.dpi
        self.bbox_inches.p1 = w, h

        if forward:
            dpival = self.dpi
            canvasw = w*dpival
            canvash = h*dpival
            manager = getattr(self.canvas, 'manager', None)
            if manager is not None:
                manager.resize(int(canvasw), int(canvash))

    def get_size_inches(self):
        return self.bbox_inches.p1

    def get_edgecolor(self):
        'Get the edge color of the Figure rectangle'
        return self.patch.get_edgecolor()

    def get_facecolor(self):
        'Get the face color of the Figure rectangle'
        return self.patch.get_facecolor()

    def get_figwidth(self):
        'Return the figwidth as a float'
        return self.bbox_inches.width

    def get_figheight(self):
        'Return the figheight as a float'
        return self.bbox_inches.height

    def get_dpi(self):
        'Return the dpi as a float'
        return self.dpi

    def get_frameon(self):
        'get the boolean indicating frameon'
        return self.frameon

    def set_edgecolor(self, color):
        """
        Set the edge color of the Figure rectangle

        ACCEPTS: any matplotlib color - see help(colors)
        """
        self.patch.set_edgecolor(color)

    def set_facecolor(self, color):
        """
        Set the face color of the Figure rectangle

        ACCEPTS: any matplotlib color - see help(colors)
        """
        self.patch.set_facecolor(color)

    def set_dpi(self, val):
        """
        Set the dots-per-inch of the figure

        ACCEPTS: float
        """
        self.dpi = val

    def set_figwidth(self, val):
        """
        Set the width of the figure in inches

        ACCEPTS: float
        """
        self.bbox_inches.x1 = val

    def set_figheight(self, val):
        """
        Set the height of the figure in inches

        ACCEPTS: float
        """
        self.bbox_inches.y1 = val

    def set_frameon(self, b):
        """
        Set whether the figure frame (background) is displayed or invisible

        ACCEPTS: boolean
        """
        self.frameon = b

    def delaxes(self, a):
        'remove a from the figure and update the current axes'
        self._axstack.remove(a)
        for func in self._axobservers: func(self)

    def _make_key(self, *args, **kwargs):
        'make a hashable key out of args and kwargs'

        def fixitems(items):
            #items may have arrays and lists in them, so convert them
            # to tuples for the key
            ret = []
            for k, v in items:
                if iterable(v): v = tuple(v)
                ret.append((k,v))
            return tuple(ret)

        def fixlist(args):
            ret = []
            for a in args:
                if iterable(a): a = tuple(a)
                ret.append(a)
            return tuple(ret)

        key = fixlist(args), fixitems(kwargs.iteritems())
        return key

    @docstring.dedent_interpd
    def add_axes(self, *args, **kwargs):
        """
        Add an axes at position *rect* [*left*, *bottom*, *width*,
        *height*] where all quantities are in fractions of figure
        width and height.  kwargs are legal
        :class:`~matplotlib.axes.Axes` kwargs plus *projection* which
        sets the projection type of the axes.  (For backward
        compatibility, ``polar=True`` may also be provided, which is
        equivalent to ``projection='polar'``).  Valid values for
        *projection* are: %(projection_names)s.  Some of these
        projections support  additional kwargs, which may be provided
        to :meth:`add_axes`. Typical usage::

            rect = l,b,w,h
            fig.add_axes(rect)
            fig.add_axes(rect, frameon=False, axisbg='g')
            fig.add_axes(rect, polar=True)
            fig.add_axes(rect, projection='polar')
            fig.add_axes(ax)

        If the figure already has an axes with the same parameters,
        then it will simply make that axes current and return it.  If
        you do not want this behavior, e.g. you want to force the
        creation of a new Axes, you must use a unique set of args and
        kwargs.  The axes :attr:`~matplotlib.axes.Axes.label`
        attribute has been exposed for this purpose.  Eg., if you want
        two axes that are otherwise identical to be added to the
        figure, make sure you give them unique labels::

            fig.add_axes(rect, label='axes1')
            fig.add_axes(rect, label='axes2')

        In rare circumstances, add_axes may be called with a single
        argument, an Axes instance already created in the present
        figure but not in the figure's list of axes.  For example,
        if an axes has been removed with :meth:`delaxes`, it can
        be restored with::

            fig.add_axes(ax)

        In all cases, the :class:`~matplotlib.axes.Axes` instance
        will be returned.

        In addition to *projection*, the following kwargs are supported:

        %(Axes)s
        """
        if not len(args): return

        # shortcut the projection "key" modifications later on, if an axes
        # with the exact args/kwargs exists, return it immediately.
        key = self._make_key(*args, **kwargs)
        ax = self._axstack.get(key)
        if ax is not None:
            self.sca(ax)
            return ax

        if isinstance(args[0], Axes):
            a = args[0]
            assert(a.get_figure() is self)
        else:
            rect = args[0]
            projection_class, kwargs, key = \
                            process_projection_requirements(self, *args, **kwargs)

            # check that an axes of this type doesn't already exist, if it
            # does, set it as active and return it
            ax = self._axstack.get(key)
            if ax is not None and isinstance(ax, projection_class):
                self.sca(ax)
                return ax

            # create the new axes using the axes class given
            a = projection_class(self, rect, **kwargs)

        self._axstack.add(key, a)
        self.sca(a)
        return a

    @docstring.dedent_interpd
    def add_subplot(self, *args, **kwargs):
        """
        Add a subplot.  Examples::

            fig.add_subplot(111)

            # equivalent but more general
            fig.add_subplot(1,1,1)

            # add subplot with red background
            fig.add_subplot(212, axisbg='r')

            # add a polar subplot
            fig.add_subplot(111, projection='polar')

            # add Subplot instance sub
            fig.add_subplot(sub)

        *kwargs* are legal :class:`~matplotlib.axes.Axes` kwargs plus
        *projection*, which chooses a projection type for the axes.
        (For backward compatibility, *polar=True* may also be
        provided, which is equivalent to *projection='polar'*). Valid
        values for *projection* are: %(projection_names)s.  Some of
        these projections
        support additional *kwargs*, which may be provided to
        :meth:`add_axes`.

        The :class:`~matplotlib.axes.Axes` instance will be returned.

        If the figure already has a subplot with key (*args*,
        *kwargs*) then it will simply make that subplot current and
        return it.

        The following kwargs are supported:

        %(Axes)s
        """
        if not len(args): return

        if len(args) == 1 and isinstance(args[0], int):
            args = tuple([int(c) for c in str(args[0])])

        if isinstance(args[0], SubplotBase):

            a = args[0]
            assert(a.get_figure() is self)
            # make a key for the subplot (which includes the axes object id
            # in the hash)
            key = self._make_key(*args, **kwargs)
        else:
            projection_class, kwargs, key = \
                        process_projection_requirements(self, *args, **kwargs)

            # try to find the axes with this key in the stack
            ax = self._axstack.get(key)

            if ax is not None:
                if isinstance(ax, projection_class):
                    # the axes already existed, so set it as active & return
                    self.sca(ax)
                    return ax
                else:
                    # Undocumented convenience behavior:
                    # subplot(111); subplot(111, projection='polar')
                    # will replace the first with the second.
                    # Without this, add_subplot would be simpler and
                    # more similar to add_axes.
                    self._axstack.remove(ax)

            a = subplot_class_factory(projection_class)(self, *args, **kwargs)

        self._axstack.add(key, a)
        self.sca(a)
        return a

    def clf(self, keep_observers=False):
        """
        Clear the figure.

        Set *keep_observers* to True if, for example,
        a gui widget is tracking the axes in the figure.
        """
        self.suppressComposite = None
        self.callbacks = cbook.CallbackRegistry()

        for ax in tuple(self.axes):  # Iterate over the copy.
            ax.cla()
            self.delaxes(ax)         # removes ax from self._axstack

        toolbar = getattr(self.canvas, 'toolbar', None)
        if toolbar is not None:
            toolbar.update()
        self._axstack.clear()
        self.artists = []
        self.lines = []
        self.patches = []
        self.texts=[]
        self.images = []
        self.legends = []
        if not keep_observers:
            self._axobservers = []

    def clear(self):
        """
        Clear the figure -- synonym for :meth:`clf`.
        """
        self.clf()

    @allow_rasterization
    def draw(self, renderer):
        """
        Render the figure using :class:`matplotlib.backend_bases.RendererBase`
        instance *renderer*.
        """
        # draw the figure bounding box, perhaps none for white figure
        if not self.get_visible(): return
        renderer.open_group('figure')

        if self.get_tight_layout() and self.axes:
            try:
                self.tight_layout(renderer)
            except ValueError:
                pass
                # ValueError can occur when resizing a window.

        if self.frameon: self.patch.draw(renderer)

        # a list of (zorder, func_to_call, list_of_args)
        dsu = []

        for a in self.patches:
            dsu.append( (a.get_zorder(), a, a.draw, [renderer]))

        for a in self.lines:
            dsu.append( (a.get_zorder(), a, a.draw, [renderer]))

        for a in self.artists:
            dsu.append( (a.get_zorder(), a, a.draw, [renderer]))

        # override the renderer default if self.suppressComposite
        # is not None
        not_composite = renderer.option_image_nocomposite()
        if self.suppressComposite is not None:
            not_composite = self.suppressComposite

        if len(self.images)<=1 or not_composite or \
                not cbook.allequal([im.origin for im in self.images]):
            for a in self.images:
                dsu.append( (a.get_zorder(), a, a.draw, [renderer]))
        else:
            # make a composite image blending alpha
            # list of (_image.Image, ox, oy)
            mag = renderer.get_image_magnification()
            ims = [(im.make_image(mag), im.ox, im.oy)
                   for im in self.images]

            im = _image.from_images(self.bbox.height * mag,
                                    self.bbox.width * mag,
                                    ims)

            im.is_grayscale = False
            l, b, w, h = self.bbox.bounds

            def draw_composite():
                gc = renderer.new_gc()
                gc.set_clip_rectangle(self.bbox)
                gc.set_clip_path(self.get_clip_path())
                renderer.draw_image(gc, l, b, im)
                gc.restore()

            dsu.append((self.images[0].get_zorder(), self.images[0], draw_composite, []))

        # render the axes
        for a in self.axes:
            dsu.append( (a.get_zorder(), a, a.draw, [renderer]))

        # render the figure text
        for a in self.texts:
            dsu.append( (a.get_zorder(), a, a.draw, [renderer]))

        for a in self.legends:
            dsu.append( (a.get_zorder(), a, a.draw, [renderer]))

        dsu = [row for row in dsu if not row[1].get_animated()]
        dsu.sort(key=itemgetter(0))
        for zorder, a, func, args in dsu:
            func(*args)

        renderer.close_group('figure')

        self._cachedRenderer = renderer

        self.canvas.draw_event(renderer)

    def draw_artist(self, a):
        """
        draw :class:`matplotlib.artist.Artist` instance *a* only --
        this is available only after the figure is drawn
        """
        assert self._cachedRenderer is not None
        a.draw(self._cachedRenderer)

    def get_axes(self):
        return self.axes

    def legend(self, handles, labels, *args, **kwargs):
        """
        Place a legend in the figure.  Labels are a sequence of
        strings, handles is a sequence of
        :class:`~matplotlib.lines.Line2D` or
        :class:`~matplotlib.patches.Patch` instances, and loc can be a
        string or an integer specifying the legend location

        USAGE::

          legend( (line1, line2, line3),
                  ('label1', 'label2', 'label3'),
                  'upper right')

        The *loc* location codes are::

          'best' : 0,          (currently not supported for figure legends)
          'upper right'  : 1,
          'upper left'   : 2,
          'lower left'   : 3,
          'lower right'  : 4,
          'right'        : 5,
          'center left'  : 6,
          'center right' : 7,
          'lower center' : 8,
          'upper center' : 9,
          'center'       : 10,

        *loc* can also be an (x,y) tuple in figure coords, which
        specifies the lower left of the legend box.  figure coords are
        (0,0) is the left, bottom of the figure and 1,1 is the right,
        top.

        Keyword arguments:

          *prop*: [ *None* | FontProperties | dict ]
            A :class:`matplotlib.font_manager.FontProperties`
            instance. If *prop* is a dictionary, a new instance will be
            created with *prop*. If *None*, use rc settings.

          *numpoints*: integer
            The number of points in the legend line, default is 4

          *scatterpoints*: integer
            The number of points in the legend line, default is 4

          *scatteroffsets*: list of floats
            a list of yoffsets for scatter symbols in legend

          *markerscale*: [ *None* | scalar ]
            The relative size of legend markers vs. original. If *None*, use rc
            settings.

          *fancybox*: [ *None* | *False* | *True* ]
            if *True*, draw a frame with a round fancybox.  If *None*, use rc

          *shadow*: [ *None* | *False* | *True* ]
            If *True*, draw a shadow behind legend. If *None*, use rc settings.

          *ncol* : integer
            number of columns. default is 1

          *mode* : [ "expand" | *None* ]
            if mode is "expand", the legend will be horizontally expanded
            to fill the axes area (or *bbox_to_anchor*)

          *title* : string
            the legend title

        Padding and spacing between various elements use following keywords
        parameters. The dimensions of these values are given as a fraction
        of the fontsize. Values from rcParams will be used if None.

        ================   ==================================================================
        Keyword            Description
        ================   ==================================================================
        borderpad          the fractional whitespace inside the legend border
        labelspacing       the vertical space between the legend entries
        handlelength       the length of the legend handles
        handletextpad      the pad between the legend handle and text
        borderaxespad      the pad between the axes and legend border
        columnspacing      the spacing between columns
        ================   ==================================================================

        .. Note:: Not all kinds of artist are supported by the legend.
                  See LINK (FIXME) for details.

        **Example:**

        .. plot:: mpl_examples/pylab_examples/figlegend_demo.py
        """
        l = Legend(self, handles, labels, *args, **kwargs)
        self.legends.append(l)
        return l

    @docstring.dedent_interpd
    def text(self, x, y, s, *args, **kwargs):
        """
        Add text to figure.

        Call signature::

          text(x, y, s, fontdict=None, **kwargs)

        Add text to figure at location *x*, *y* (relative 0-1
        coords). See :func:`~matplotlib.pyplot.text` for the meaning
        of the other arguments.

        kwargs control the :class:`~matplotlib.text.Text` properties:

        %(Text)s
        """

        override = _process_text_args({}, *args, **kwargs)
        t = Text(
            x=x, y=y, text=s,
            )

        t.update(override)
        self._set_artist_props(t)
        self.texts.append(t)
        return t

    def _set_artist_props(self, a):
        if a!= self:
            a.set_figure(self)
        a.set_transform(self.transFigure)

    @docstring.dedent_interpd
    def gca(self, **kwargs):
        """
        Return the current axes, creating one if necessary

        The following kwargs are supported for ensuring the returned axes
        adheres to the given projection etc., and for axes creation if
        the active axes does not exist:

        %(Axes)s

        """
        ckey, cax = self._axstack.current_key_axes()
        # if there exists an axes on the stack see if it maches
        # the desired axes configuration
        if cax is not None:

            # if no kwargs are given just return the current axes
            # this is a convenience for gca() on axes such as polar etc.
            if not kwargs:
                return cax

            # if the user has specified particular projection detail
            # then build up a key which can represent this
            else:
                # we don't want to modify the original kwargs
                # so take a copy so that we can do what we like to it
                kwargs_copy = kwargs.copy()
                projection_class, _, key = \
                        process_projection_requirements(self, **kwargs_copy)

                # let the returned axes have any gridspec by removing it from the key
                ckey = ckey[1:]
                key = key[1:]

                # if the cax matches this key then return the axes, otherwise
                # continue and a new axes will be created
                if key == ckey and isinstance(cax, projection_class):
                    return cax

        # no axes found, so create one which spans the figure
        return self.add_subplot(1, 1, 1, **kwargs)

    def sca(self, a):
        'Set the current axes to be a and return a'
        self._axstack.bubble(a)
        for func in self._axobservers: func(self)
        return a

    def _gci(self):
        """
        helper for :func:`~matplotlib.pyplot.gci`;
        do not use elsewhere.
        """
        for ax in reversed(self.axes):
            im = ax._gci()
            if im is not None:
                return im
        return None

    def add_axobserver(self, func):
        'whenever the axes state change, ``func(self)`` will be called'
        self._axobservers.append(func)


    def savefig(self, *args, **kwargs):
        """
        Save the current figure.

        Call signature::

          savefig(fname, dpi=None, facecolor='w', edgecolor='w',
                  orientation='portrait', papertype=None, format=None,
                  transparent=False, bbox_inches=None, pad_inches=0.1)

        The output formats available depend on the backend being used.

        Arguments:

          *fname*:
            A string containing a path to a filename, or a Python
            file-like object, or possibly some backend-dependent object
            such as :class:`~matplotlib.backends.backend_pdf.PdfPages`.

            If *format* is *None* and *fname* is a string, the output
            format is deduced from the extension of the filename. If
            the filename has no extension, the value of the rc parameter
            ``savefig.format`` is used.

            If *fname* is not a string, remember to specify *format* to
            ensure that the correct backend is used.

        Keyword arguments:

          *dpi*: [ *None* | ``scalar > 0`` ]
            The resolution in dots per inch.  If *None* it will default to
            the value ``savefig.dpi`` in the matplotlibrc file.

          *facecolor*, *edgecolor*:
            the colors of the figure rectangle

          *orientation*: [ 'landscape' | 'portrait' ]
            not supported on all backends; currently only on postscript output

          *papertype*:
            One of 'letter', 'legal', 'executive', 'ledger', 'a0' through
            'a10', 'b0' through 'b10'. Only supported for postscript
            output.

          *format*:
            One of the file extensions supported by the active
            backend.  Most backends support png, pdf, ps, eps and svg.

          *transparent*:
            If *True*, the axes patches will all be transparent; the
            figure patch will also be transparent unless facecolor
            and/or edgecolor are specified via kwargs.
            This is useful, for example, for displaying
            a plot on top of a colored background on a web page.  The
            transparency of these patches will be restored to their
            original values upon exit of this function.

          *bbox_inches*:
            Bbox in inches. Only the given portion of the figure is
            saved. If 'tight', try to figure out the tight bbox of
            the figure.

          *pad_inches*:
            Amount of padding around the figure when bbox_inches is
            'tight'.

          *bbox_extra_artists*:
            A list of extra artists that will be considered when the
            tight bbox is calculated.

        """

        kwargs.setdefault('dpi', rcParams['savefig.dpi'])

        transparent = kwargs.pop('transparent', False)
        if transparent:
            kwargs.setdefault('facecolor', 'none')
            kwargs.setdefault('edgecolor', 'none')
            original_axes_colors = []
            for ax in self.axes:
                patch = ax.patch
                original_axes_colors.append((patch.get_facecolor(),
                                             patch.get_edgecolor()))
                patch.set_facecolor('none')
                patch.set_edgecolor('none')
        else:
            kwargs.setdefault('facecolor', rcParams['savefig.facecolor'])
            kwargs.setdefault('edgecolor', rcParams['savefig.edgecolor'])

        self.canvas.print_figure(*args, **kwargs)

        if transparent:
            for ax, cc in zip(self.axes, original_axes_colors):
                ax.patch.set_facecolor(cc[0])
                ax.patch.set_edgecolor(cc[1])

    @docstring.dedent_interpd
    def colorbar(self, mappable, cax=None, ax=None, **kw):
        """
        Create a colorbar for a ScalarMappable instance, *mappable*.

        Documentation for the pylab thin wrapper:
        %(colorbar_doc)s
        """
        if ax is None:
            ax = self.gca()
        use_gridspec = kw.pop("use_gridspec", True)
        if cax is None:
            if use_gridspec and isinstance(ax, SubplotBase):
                cax, kw = cbar.make_axes_gridspec(ax, **kw)
            else:
                cax, kw = cbar.make_axes(ax, **kw)
        cax.hold(True)
        cb = cbar.colorbar_factory(cax, mappable, **kw)

        self.sca(ax)
        return cb

    def subplots_adjust(self, *args, **kwargs):
        """
        Call signature::

          subplots_adjust(left=None, bottom=None, right=None, top=None,
                              wspace=None, hspace=None)

        Update the :class:`SubplotParams` with *kwargs* (defaulting to rc when
        *None*) and update the subplot locations

        """
        self.subplotpars.update(*args, **kwargs)
        import matplotlib.axes
        for ax in self.axes:
            if not isinstance(ax, matplotlib.axes.SubplotBase):
                # Check if sharing a subplots axis
                if ax._sharex is not None and isinstance(ax._sharex, matplotlib.axes.SubplotBase):
                    ax._sharex.update_params()
                    ax.set_position(ax._sharex.figbox)
                elif ax._sharey is not None and isinstance(ax._sharey, matplotlib.axes.SubplotBase):
                    ax._sharey.update_params()
                    ax.set_position(ax._sharey.figbox)
            else:
                ax.update_params()
                ax.set_position(ax.figbox)

    def ginput(self, n=1, timeout=30, show_clicks=True, mouse_add=1, mouse_pop=3, mouse_stop=2):
        """
        Call signature::

          ginput(self, n=1, timeout=30, show_clicks=True,
                 mouse_add=1, mouse_pop=3, mouse_stop=2)

        Blocking call to interact with the figure.

        This will wait for *n* clicks from the user and return a list of the
        coordinates of each click.

        If *timeout* is zero or negative, does not timeout.

        If *n* is zero or negative, accumulate clicks until a middle click
        (or potentially both mouse buttons at once) terminates the input.

        Right clicking cancels last input.

        The buttons used for the various actions (adding points, removing
        points, terminating the inputs) can be overriden via the
        arguments *mouse_add*, *mouse_pop* and *mouse_stop*, that give
        the associated mouse button: 1 for left, 2 for middle, 3 for
        right.

        The keyboard can also be used to select points in case your mouse
        does not have one or more of the buttons.  The delete and backspace
        keys act like right clicking (i.e., remove last point), the enter key
        terminates input and any other key (not already used by the window
        manager) selects a point.
        """

        blocking_mouse_input = BlockingMouseInput(self, mouse_add =mouse_add,
                                                        mouse_pop =mouse_pop,
                                                        mouse_stop=mouse_stop)
        return blocking_mouse_input(n=n, timeout=timeout,
                                    show_clicks=show_clicks)

    def waitforbuttonpress(self, timeout=-1):
        """
        Call signature::

          waitforbuttonpress(self, timeout=-1)

        Blocking call to interact with the figure.

        This will return True is a key was pressed, False if a mouse
        button was pressed and None if *timeout* was reached without
        either being pressed.

        If *timeout* is negative, does not timeout.
        """

        blocking_input = BlockingKeyMouseInput(self)
        return blocking_input(timeout=timeout)


    def get_default_bbox_extra_artists(self):
        bbox_extra_artists = [t for t in self.texts if t.get_visible()]
        for ax in self.axes:
            if ax.get_visible():
                bbox_extra_artists.extend(ax.get_default_bbox_extra_artists())
        return bbox_extra_artists


    def get_tightbbox(self, renderer):
        """
        Return a (tight) bounding box of the figure in inches.

        It only accounts axes title, axis labels, and axis
        ticklabels. Needs improvement.
        """

        bb = []
        for ax in self.axes:
            if ax.get_visible():
                bb.append(ax.get_tightbbox(renderer))

        _bbox = Bbox.union([b for b in bb if b.width!=0 or b.height!=0])

        bbox_inches = TransformedBbox(_bbox,
                                      Affine2D().scale(1./self.dpi))

        return bbox_inches

    def tight_layout(self, renderer=None, pad=1.08, h_pad=None, w_pad=None, rect=None):
        """
        Adjust subplot parameters to give specified padding.

        Parameters:

          *pad* : float
            padding between the figure edge and the edges of subplots,
            as a fraction of the font-size.
          *h_pad*, *w_pad* : float
            padding (height/width) between edges of adjacent subplots.
            Defaults to `pad_inches`.
          *rect* : if rect is given, it is interpreted as a rectangle
            (left, bottom, right, top) in the normalized figure
            coordinate that the whole subplots area (including
            labels) will fit into. Default is (0, 0, 1, 1).
        """

        from tight_layout import get_renderer, get_tight_layout_figure

        no_go = [ax for ax in self.axes if not isinstance(ax, SubplotBase)]
        if no_go:
            warnings.Warn("Cannot use tight_layout;"
                          " all Axes must descend from SubplotBase")
            return

        if renderer is None:
            renderer = get_renderer(self)

        kwargs = get_tight_layout_figure(self, self.axes, renderer,
                                         pad=pad, h_pad=h_pad, w_pad=w_pad,
                                         rect=rect)

        self.subplots_adjust(**kwargs)