def axline(ax, slope, intercept, **kwargs): ''' originally described (July '18) in ../pluto/hole_areas.ipynb see https://github.com/dstansby/matplotlib/blob/49da75e46ccc4714009b124a58784eaeaea53970/lib/matplotlib/axes/_axes.py and https://github.com/matplotlib/matplotlib/pull/9321 ''' import matplotlib.transforms as mtransforms import matplotlib.lines as mlines if "transform" in kwargs: raise ValueError("'transform' is not allowed as a kwarg; " "axline generates its own transform.") xtrans = mtransforms.BboxTransformTo(ax.viewLim) viewLimT = mtransforms.TransformedBbox( ax.viewLim, mtransforms.Affine2D().rotate_deg(90).scale(-1, 1)) ytrans = (mtransforms.BboxTransformTo(viewLimT) + mtransforms.Affine2D().scale(slope).translate(0, intercept)) trans = mtransforms.blended_transform_factory(xtrans, ytrans) line = mlines.Line2D([0, 1], [0, 1], transform=trans + ax.transData, **kwargs) ax.add_line(line) return line
def plot_model(axes, model): n = len(model.waveforms) offset_step = 1/(n+1) plots = [] text_trans = T.blended_transform_factory(axes.figure.transFigure, axes.transAxes) limits = [(w.y.min(), w.y.max()) for w in model.waveforms] base_scale = np.mean(np.abs(np.array(limits))) bscale_in_box = T.Bbox([[0, -base_scale], [1, base_scale]]) bscale_out_box = T.Bbox([[0, -1], [1, 1]]) bscale_in = T.BboxTransformFrom(bscale_in_box) bscale_out = T.BboxTransformTo(bscale_out_box) tscale_in_box = T.Bbox([[0, -1], [1, 1]]) tscale_out_box = T.Bbox([[0, 0], [1, offset_step]]) tscale_in = T.BboxTransformFrom(tscale_in_box) tscale_out = T.BboxTransformTo(tscale_out_box) boxes = { 'tscale': tscale_in_box, 'tnorm': [], 'norm_limits': limits/base_scale, } for i, waveform in enumerate(model.waveforms): y_min, y_max = waveform.y.min(), waveform.y.max() tnorm_in_box = T.Bbox([[0, -1], [1, 1]]) tnorm_out_box = T.Bbox([[0, -1], [1, 1]]) tnorm_in = T.BboxTransformFrom(tnorm_in_box) tnorm_out = T.BboxTransformTo(tnorm_out_box) boxes['tnorm'].append(tnorm_in_box) offset = offset_step * i + offset_step * 0.5 translate = T.Affine2D().translate(0, offset) y_trans = bscale_in + bscale_out + \ tnorm_in + tnorm_out + \ tscale_in + tscale_out + \ translate + axes.transAxes trans = T.blended_transform_factory(axes.transData, y_trans) plot = WaveformPlot(waveform, axes, trans) plots.append(plot) text_trans = T.blended_transform_factory(axes.transAxes, y_trans) axes.text(-0.05, 0, f'{waveform.level}', transform=text_trans) axes.set_yticks([]) axes.grid() for spine in ('top', 'left', 'right'): axes.spines[spine].set_visible(False) return plots, boxes
def _coords2index(im, x, y, inverted=False): """ Converts data coordinates to index coordinates of the array. Parameters ----------- im : An AxesImage instance The image artist to operation on x : number The x-coordinate in data coordinates. y : number The y-coordinate in data coordinates. inverted : bool, optional If True, convert index to data coordinates instead of data coordinates to index. Returns -------- i, j : Index coordinates of the array associated with the image. """ xmin, xmax, ymin, ymax = im.get_extent() if im.origin == 'upper': ymin, ymax = ymax, ymin data_extent = mtransforms.Bbox([[ymin, xmin], [ymax, xmax]]) array_extent = mtransforms.Bbox([[0, 0], im.get_array().shape[:2]]) trans = mtransforms.BboxTransformFrom(data_extent) +\ mtransforms.BboxTransformTo(array_extent) if inverted: trans = trans.inverted() return trans.transform_point([y, x]).astype(int)
def axline(ax, slope, intercept, **kwargs): if "transform" in kwargs: raise ValueError("'transform' is not allowed as a kwarg; " "axline generates its own transform.") xtrans = mtransforms.BboxTransformTo(ax.viewLim) viewLimT = mtransforms.TransformedBbox( ax.viewLim, mtransforms.Affine2D().rotate_deg(90).scale(-1, 1)) ytrans = (mtransforms.BboxTransformTo(viewLimT) + mtransforms.Affine2D().scale(slope).translate(0, intercept)) trans = mtransforms.blended_transform_factory(xtrans, ytrans) l = mlines.Line2D([0, 1], [0, 1], transform=trans + ax.transData, **kwargs) ax.add_line(l) return l
def _update_patch_transform(self): x = self.convert_xunits(self._x) y = self.convert_yunits(self._y) width = self.convert_xunits(self._width) height = self.convert_yunits(self._height) bbox = transforms.Bbox.from_bounds(x, y, width, height) self._rect_transform = transforms.BboxTransformTo(bbox)
def coords2index(im, x, y): """ This function is modified from here: https://github.com/joferkington/mpldatacursor/blob/7dabc589ed02c35ac5d89de5931f91e0323aa795/mpldatacursor/pick_info.py#L28 Converts data coordinates to index coordinates of the array. Parameters ----------- im : An AxesImage instance The image artist to operate on x : number The x-coordinate in data coordinates. y : number The y-coordinate in data coordinates. Returns -------- i, j : Index coordinates of the array associated with the image. """ xmin, xmax, ymin, ymax = im.get_extent() if im.origin == 'upper': ymin, ymax = ymax, ymin data_extent = mtransforms.Bbox([[ymin, xmin], [ymax, xmax]]) array_extent = mtransforms.Bbox([[0, 0], im.get_array().shape[:2]]) trans = (mtransforms.BboxTransformFrom(data_extent) + mtransforms.BboxTransformTo(array_extent)) return trans.transform_point([y, x]).astype(int)
def _set_lim_and_transforms(self): """ Override transform initialization """ # axis coords to display coords self.transAxes = transforms.BboxTransformTo(self.bbox) # X and Y axis scaling self.transScale = transforms.TransformWrapper( transforms.IdentityTransform()) # transform from given Bbox to unit Bbox # the given transformedBbox is updated every time the # viewLim changes or the transScale changes self.transLimits = transforms.BboxTransformFrom( transforms.TransformedBbox(self.viewLim, self.transScale)) # data to display coordinates self.transData = self.transScale + (self.transLimits + self.transAxes) # blended transforms for xaxis and yaxis self._xaxis_transform = transforms.blended_transform_factory( self.transData, self.transAxes) self._yaxis_transform = transforms.blended_transform_factory( self.transAxes, self.transData)
def _update_patch_transfrom_about_center(self): x = self.convert_xunits(self._x0) y = self.convert_yunits(self._y0) width = self.convert_xunits(self._width) height = self.convert_yunits(self._height) bbox = transforms.Bbox.from_bounds(x, y, width, height) rot_trans = transforms.Affine2D() rot_trans.rotate_deg_around(x + 0.5 * width, y + 0.5 * height, self.angle) self._rect_transform = transforms.BboxTransformTo(bbox) self._rect_transform += rot_trans
def axaline(m, y0, ax=None, **kwargs): if not ax: ax = plt.gca() tr = mtransforms.BboxTransformTo( mtransforms.TransformedBbox(ax.viewLim, ax.transScale)) + \ ax.transScale.inverted() aff = mtransforms.Affine2D.from_values(1, m, 0, 0, 0, y0) trinv = ax.transData line = plt.Line2D([0, 1], [0, 0], transform=tr + aff + trinv, **kwargs) ax.add_line(line)
def _set_transform(self): if self.coord == 'data': self.set_transform(self.Q.ax.transData) elif self.coord == 'axes': self.set_transform(self.Q.ax.transAxes) elif self.coord == 'figure': self.set_transform(self.Q.ax.figure.transFigure) elif self.coord == 'inches': dx = ax.figure.dpi bb = transforms.Bbox.from_extents(0, 0, dx, dy) trans = transforms.BboxTransformTo(bb) self.set_transform(trans) else: raise ValueError('unrecognized coordinates')
def _coords2index(im, x, y): """ Convert data coordinates to index coordinates of the image array. Credit: mpldatacursor developers. Copyright (c) 2012. BSD License Modified from original found at: https://github.com/joferkington/mpldatacursor/blob/master/mpldatacursor/pick_info.py """ xmin, xmax, ymin, ymax = im.get_extent() if im.origin == 'upper': ymin, ymax = ymax, ymin im_shape = im.get_array().shape[:2] data_extent = mtransforms.Bbox([[ymin, xmin], [ymax, xmax]]) array_extent = mtransforms.Bbox([[0, 0], im_shape]) trans = (mtransforms.BboxTransformFrom(data_extent) + mtransforms.BboxTransformTo(array_extent)) j, i = trans.transform_point([y, x]).astype(int) # Clip the coordinates to the array bounds. return min(max(j, 0), im_shape[0] - 1), min(max(i, 0), im_shape[1] - 1)
def _set_lim_and_transforms(self): self.transProjection = self._get_core_transform(self.RESOLUTION) self.transAffine = self._get_affine_transform() self.transAxes = mtransforms.BboxTransformTo(self.bbox) # The complete data transformation stack -- from data all the # way to display coordinates self.transData = self.transProjection + self.transAffine + self.transAxes # This is the transform for longitude ticks. self._xaxis_pretransform = ( mtransforms.Affine2D() .scale(1, self._longitude_cap * 2) .translate(0, -self._longitude_cap) ) self._xaxis_transform = self._xaxis_pretransform + self.transData self._xaxis_text1_transform = ( mtransforms.Affine2D().scale(1, 0) + self.transData + mtransforms.Affine2D().translate(0, 4) ) self._xaxis_text2_transform = ( mtransforms.Affine2D().scale(1, 0) + self.transData + mtransforms.Affine2D().translate(0, -4) ) # This is the transform for latitude ticks. yaxis_stretch = mtransforms.Affine2D().scale(1, 1) yaxis_space = mtransforms.Affine2D().scale(1, 1.1) self._yaxis_transform = yaxis_stretch + self.transData yaxis_text_base = ( yaxis_stretch + self.transProjection + (yaxis_space + self.transAffine + self.transAxes) ) self._yaxis_text1_transform = yaxis_text_base + mtransforms.Affine2D().translate( -8, 0 ) self._yaxis_text2_transform = yaxis_text_base + mtransforms.Affine2D().translate( 8, 0 )
def visitor(y, x, value): color = value[:3] / value[3] radius = numpy.sqrt(value[3] / value[4]) tx, ty = transform.transform_point((x, y)) #print tx, ty, radius #mypath = path.Path(numpy.array([ # [ tx-radius, ty-radius ], # [ tx+radius, ty-radius ], # [ tx+radius, ty+radius ], # [ tx-radius, ty+radius ], # [ tx-radius, ty-radius ], #])) rtrans = transforms.BboxTransformTo( transforms.Bbox.from_bounds(tx, ty, radius, radius)) #tpath = rtrans.transform_path(upath) renderer.draw_path(gc, marker_path, rtrans, tuple(color))
def _coords2index(im, x, y): """ Converts data coordinates to index coordinates of the array. Parameters ----------- im : A matplotlib image artist. x : The x-coordinate in data coordinates. y : The y-coordinate in data coordinates. Returns -------- i, j : Index coordinates of the array associated with the image. """ xmin, xmax, ymin, ymax = im.get_extent() if im.origin == 'upper': ymin, ymax = ymax, ymin data_extent = mtransforms.Bbox([[ymin, xmin], [ymax, xmax]]) array_extent = mtransforms.Bbox([[0, 0], im.get_array().shape[:2]]) trans = mtransforms.BboxTransformFrom(data_extent) +\ mtransforms.BboxTransformTo(array_extent) return trans.transform_point([y, x]).astype(int)
def _set_lim_and_transforms(self): # A view limit where the minimum radius can be locked if the user # specifies an alternate origin. self._originViewLim = mtransforms.LockableBbox(self.viewLim) # Handle angular offset and direction. self._direction = mtransforms.Affine2D() \ .scale(self._default_theta_direction, 1.0) self._theta_offset = mtransforms.Affine2D() \ .translate(self._default_theta_offset, 0.0) self.transShift = self._direction + self._theta_offset # A view limit shifted to the correct location after accounting for # orientation and offset. self._realViewLim = mtransforms.TransformedBbox( self.viewLim, self.transShift) # Transforms the x and y axis separately by a scale factor # It is assumed that this part will have non-linear components self.transScale = mtransforms.TransformWrapper( mtransforms.IdentityTransform()) # Scale view limit into a bbox around the selected wedge. This may be # smaller than the usual unit axes rectangle if not plotting the full # circle. self.axesLim = _WedgeBbox((0.5, 0.5), self._realViewLim, self._originViewLim) # Scale the wedge to fill the axes. self.transWedge = mtransforms.BboxTransformFrom(self.axesLim) # Scale the axes to fill the figure. self.transAxes = mtransforms.BboxTransformTo(self.bbox) # A (possibly non-linear) projection on the (already scaled) # data. This one is aware of rmin self.transProjection = self.PolarTransform( self, _apply_theta_transforms=False) # Add dependency on rorigin. self.transProjection.set_children(self._originViewLim) # An affine transformation on the data, generally to limit the # range of the axes self.transProjectionAffine = self.PolarAffine(self.transScale, self._originViewLim) # The complete data transformation stack -- from data all the # way to display coordinates self.transData = ( self.transScale + self.transShift + self.transProjection + (self.transProjectionAffine + self.transWedge + self.transAxes)) # This is the transform for theta-axis ticks. It is # equivalent to transData, except it always puts r == 0.0 and r == 1.0 # at the edge of the axis circles. self._xaxis_transform = (mtransforms.blended_transform_factory( mtransforms.IdentityTransform(), mtransforms.BboxTransformTo(self.viewLim)) + self.transData) # The theta labels are flipped along the radius, so that text 1 is on # the outside by default. This should work the same as before. flipr_transform = mtransforms.Affine2D() \ .translate(0.0, -0.5) \ .scale(1.0, -1.0) \ .translate(0.0, 0.5) self._xaxis_text_transform = flipr_transform + self._xaxis_transform # This is the transform for r-axis ticks. It scales the theta # axis so the gridlines from 0.0 to 1.0, now go from thetamin to # thetamax. self._yaxis_transform = (mtransforms.blended_transform_factory( mtransforms.BboxTransformTo(self.viewLim), mtransforms.IdentityTransform()) + self.transData) # The r-axis labels are put at an angle and padded in the r-direction self._r_label_position = mtransforms.Affine2D() \ .translate(self._default_rlabel_position, 0.0) self._yaxis_text_transform = mtransforms.TransformWrapper( self._r_label_position + self.transData)
def plot_bot(dset, image_axes, data_slices, image_scales=(0, 0), clim=None, even_scale=False, cmap='RdBu_r', axes=None, figkw={}, title=None, func=None, visible_axes=True): """ Plot a 2d slice of the grid data of a dset/field. Parameters ---------- dset : h5py dset or Dedalus Field object Dataset to plot image_axes: tuple of ints (xi, yi) Data axes to use for image x and y axes data_slices: tuple of slices, ints Slices selecting image data from global data image_scales: tuple of ints or strs (xs, ys) Axis scales (default: (0,0)) clim : tuple of floats, optional Colorbar limits (default: (data min, data max)) even_scale : bool, optional Expand colorbar limits to be symmetric around 0 (default: False) cmap : str, optional Colormap name (default: 'RdBu_r') axes : matplotlib.Axes object, optional Axes to overplot. If None (default), a new figure and axes will be created. figkw : dict, optional Keyword arguments to pass to plt.figure (default: {}) title : str, optional Title for plot (default: dataset name) func : function(xmesh, ymesh, data), optional Function to apply to selected meshes and data before plotting (default: None) visible_axes : bool, optional Set to false to remove x and y ticks, ticklabels, and labels """ # Wrap fields if isinstance(dset, Field): dset = FieldWrapper(dset) # Unpack image axes xaxis, yaxis = image_axes xscale, yscale = image_scales # Get meshes and data xmesh, ymesh, data = get_plane(dset, xaxis, yaxis, data_slices, xscale, yscale) if func is not None: xmesh, ymesh, data = func(xmesh, ymesh, data) # Setup figure if axes is None: fig = plt.figure(**figkw) axes = fig.add_subplot(1, 1, 1) # Setup axes # Bounds (left, bottom, width, height) relative-to-axes pbbox = transforms.Bbox.from_bounds(0.03, 0, 0.94, 0.94) cbbox = transforms.Bbox.from_bounds(0.03, 0.95, 0.94, 0.05) # Convert to relative-to-figure to_axes_bbox = transforms.BboxTransformTo(axes.get_position()) pbbox = pbbox.transformed(to_axes_bbox) cbbox = cbbox.transformed(to_axes_bbox) # Create new axes and suppress base axes paxes = axes.figure.add_axes(pbbox) caxes = axes.figure.add_axes(cbbox) axes.axis('off') # Colormap options cmap = copy.copy(matplotlib.cm.get_cmap(cmap)) cmap.set_bad('0.7') # Plot plot = paxes.pcolormesh(xmesh, ymesh, data, cmap=cmap, zorder=1) paxes.axis(pad_limits(xmesh, ymesh)) paxes.tick_params(length=0, width=0) if clim is None: if even_scale: lim = max(abs(data.min()), abs(data.max())) clim = (-lim, lim) else: clim = (data.min(), data.max()) plot.set_clim(*clim) # Colorbar cbar = plt.colorbar(plot, cax=caxes, orientation='horizontal', ticks=ticker.MaxNLocator(nbins=5)) cbar.outline.set_visible(False) caxes.xaxis.set_ticks_position('top') # Labels if title is None: try: title = dset.attrs['name'] except KeyError: title = dset.name caxes.set_xlabel(title) caxes.xaxis.set_label_position('top') if isinstance(xscale, str): paxes.set_xlabel(xscale) else: paxes.set_xlabel(dset.dims[xaxis].label) if isinstance(yscale, str): paxes.set_ylabel(yscale) else: paxes.set_ylabel(dset.dims[yaxis].label) if not visible_axes: paxes.xaxis.set_visible(False) paxes.yaxis.set_visible(False) return paxes, caxes
def draw(self, renderer): """ Ellipses are normally drawn using an approximation that uses eight cubic bezier splines. The error of this approximation is 1.89818e-6, according to this unverified source: Lancaster, Don. Approximating a Circle or an Ellipse Using Four Bezier Cubic Splines. http://www.tinaja.com/glib/ellipse4.pdf There is a use case where very large ellipses must be drawn with very high accuracy, and it is too expensive to render the entire ellipse with enough segments (either splines or line segments). Therefore, in the case where either radius of the ellipse is large enough that the error of the spline approximation will be visible (greater than one pixel offset from the ideal), a different technique is used. In that case, only the visible parts of the ellipse are drawn, with each visible arc using a fixed number of spline segments (8). The algorithm proceeds as follows: 1. The points where the ellipse intersects the axes bounding box are located. (This is done be performing an inverse transformation on the axes bbox such that it is relative to the unit circle -- this makes the intersection calculation much easier than doing rotated ellipse intersection directly). This uses the "line intersecting a circle" algorithm from: Vince, John. Geometry for Computer Graphics: Formulae, Examples & Proofs. London: Springer-Verlag, 2005. 2. The angles of each of the intersection points are calculated. 3. Proceeding counterclockwise starting in the positive x-direction, each of the visible arc-segments between the pairs of vertices are drawn using the bezier arc approximation technique implemented in Path.arc(). """ if not hasattr(self, 'axes'): raise RuntimeError('Arcs can only be used in Axes instances') self._recompute_transform() # Get the width and height in pixels width = self.convert_xunits(self.width) height = self.convert_yunits(self.height) width, height = self.get_transform().transform_point( (width, height)) inv_error = (1.0 / 1.89818e-6) * 0.5 if width < inv_error and height < inv_error: self._path = Path.arc(self.theta1, self.theta2) return Patch.draw(self, renderer) def iter_circle_intersect_on_line(x0, y0, x1, y1): dx = x1 - x0 dy = y1 - y0 dr2 = dx*dx + dy*dy D = x0*y1 - x1*y0 D2 = D*D discrim = dr2 - D2 # Single (tangential) intersection if discrim == 0.0: x = (D*dy) / dr2 y = (-D*dx) / dr2 yield x, y elif discrim > 0.0: # The definition of "sign" here is different from # np.sign: we never want to get 0.0 if dy < 0.0: sign_dy = -1.0 else: sign_dy = 1.0 sqrt_discrim = np.sqrt(discrim) for sign in (1., -1.): x = (D*dy + sign * sign_dy * dx * sqrt_discrim) / dr2 y = (-D*dx + sign * np.abs(dy) * sqrt_discrim) / dr2 yield x, y def iter_circle_intersect_on_line_seg(x0, y0, x1, y1): epsilon = 1e-9 if x1 < x0: x0e, x1e = x1, x0 else: x0e, x1e = x0, x1 if y1 < y0: y0e, y1e = y1, y0 else: y0e, y1e = y0, y1 x0e -= epsilon y0e -= epsilon x1e += epsilon y1e += epsilon for x, y in iter_circle_intersect_on_line(x0, y0, x1, y1): if x >= x0e and x <= x1e and y >= y0e and y <= y1e: yield x, y # Transforms the axes box_path so that it is relative to the unit # circle in the same way that it is relative to the desired # ellipse. box_path = Path.unit_rectangle() box_path_transform = transforms.BboxTransformTo(self.axes.bbox) + \ self.get_transform().inverted() box_path = box_path.transformed(box_path_transform) PI = np.pi TWOPI = PI * 2.0 RAD2DEG = 180.0 / PI DEG2RAD = PI / 180.0 theta1 = self.theta1 theta2 = self.theta2 thetas = {} # For each of the point pairs, there is a line segment for p0, p1 in zip(box_path.vertices[:-1], box_path.vertices[1:]): x0, y0 = p0 x1, y1 = p1 for x, y in iter_circle_intersect_on_line_seg(x0, y0, x1, y1): theta = np.arccos(x) if y < 0: theta = TWOPI - theta # Convert radians to angles theta *= RAD2DEG if theta > theta1 and theta < theta2: thetas[theta] = None thetas = thetas.keys() thetas.sort() thetas.append(theta2) last_theta = theta1 theta1_rad = theta1 * DEG2RAD inside = box_path.contains_point((np.cos(theta1_rad), np.sin(theta1_rad))) for theta in thetas: if inside: self._path = Path.arc(last_theta, theta, 8) Patch.draw(self, renderer) inside = False else: inside = True last_theta = theta
def axline(a, b, **kwargs): """ Add an infinite straight line across an axis. Parameters ---------- a, b: scalar or tuple Acceptable forms are y0, b: y = y0 + b * x (x0, y0), b: y = y0 + b * (x - x0) (x0, y0), (x1, y1): y = y0 + (y1 - y0) / (x1 - x0) * (x - x0) Additional arguments are passed to the <matplotlib.lines.Line2D> constructor. Returns ------- :class:`~matplotlib.lines.Line2D` Other Parameters ---------------- Valid kwargs are :class:`~matplotlib.lines.Line2D` properties, with the exception of 'transform': %(Line2D)s Examples -------- * Draw a thick red line with slope 1 and y-intercept 0:: >>> axline(0, 1, linewidth=4, color='r') * Draw a default line with slope 1 and y-intercept 1:: >>> axline(1, 1) See Also -------- axhline : for horizontal lines axvline : for vertical lines Notes ----- Currently this method does not work properly with log scaled axes. Taken from https://github.com/matplotlib/matplotlib/pull/9321 """ from matplotlib import pyplot as plt import matplotlib.transforms as mtransforms import matplotlib.lines as mlines if np.isscalar(a): if not np.isscalar(b): raise ValueError("Invalid line parameters.") point, slope = (0, a), b elif np.isscalar(b): if not len(a) == 2: raise ValueError("Invalid line parameters.") point, slope = a, b else: if not len(a) == len(b) == 2: raise ValueError("Invalid line parameters.") if b[0] != a[0]: point, slope = a, (b[1] - a[1]) / (b[0] - a[0]) else: point, slope = a, np.inf ax = plt.gca() if "transform" in kwargs: raise ValueError("'transform' is not allowed as a kwarg; " "axline generates its own transform.") if slope == 0: return ax.axhline(point[1], **kwargs) elif np.isinf(slope): return ax.axvline(point[0], **kwargs) xtrans = mtransforms.BboxTransformTo(ax.viewLim) viewLimT = mtransforms.TransformedBbox( ax.viewLim, mtransforms.Affine2D().rotate_deg(90).scale(-1, 1)) ytrans = (mtransforms.BboxTransformTo(viewLimT) + mtransforms.Affine2D().scale(slope).translate(*point)) trans = mtransforms.blended_transform_factory(xtrans, ytrans) line = mlines.Line2D([0, 1], [0, 1], transform=trans + ax.transData, **kwargs) ax.add_line(line) return line
def __call__(self, ax, renderer): bbox_parent = self.parent.get_position(original=False) trans = mtrans.BboxTransformTo(bbox_parent) bbox_inset = mtrans.Bbox.from_bounds(*self.lbwh) bb = mtrans.TransformedBbox(bbox_inset, trans) return bb
def plot_crit(self, axes=None, transpose=False, xlabel = None, ylabel = None, zlabel="growth rate", cmap="viridis"): """Create a 2D colormap of the grid of growth rates. If available, the root values that have been found will be plotted over the colormap. Parameters ---------- transpose : bool, optional If True, plot dim 0 on the y axis and dim 1 on the x axis. xlabel : str, optional If not None, the x-label of the plot. Otherwise, use parameter name from EVP ylabel : str, optional If not None, the y-label of the plot. Otherwise, use parameter name from EVP zlabel : str, optional Label for the colorbar. (default: growth rate) cmp : str, optional matplotlib colormap name (default: viridis) """ if self.rank != 0: return if axes is None: fig = plt.figure(figsize=[8,8]) ax = fig.add_subplot(111) else: ax = axes fig = axes.figure # Grab out grid data for colormap if transpose: xx = self.parameter_grids[1].T yy = self.parameter_grids[0].T grid = self.evalue_grid.real.T else: xx = self.parameter_grids[0] yy = self.parameter_grids[1] grid = self.evalue_grid.real # Plot colormap, only plot 2 stdevs off zero biggest_val = 2*np.abs(grid).std() # Setup axes # Bounds (left, bottom, width, height) relative-to-axes pbbox = transforms.Bbox.from_bounds(0.03, 0, 0.94, 0.94) cbbox = transforms.Bbox.from_bounds(0.03, 0.95, 0.94, 0.05) # Convert to relative-to-figure to_axes_bbox = transforms.BboxTransformTo(ax.get_position()) pbbox = pbbox.transformed(to_axes_bbox) cbbox = cbbox.transformed(to_axes_bbox) # Create new axes and suppress base axes pax = ax.figure.add_axes(pbbox) cax = ax.figure.add_axes(cbbox) plot = pax.pcolormesh(xx,yy,grid,cmap=cmap,vmin=-biggest_val,vmax=biggest_val) ax.axis('off') cbar = plt.colorbar(plot, cax=cax, label=zlabel, orientation='horizontal') cbar.outline.set_visible(False) cax.xaxis.set_ticks_position('top') cax.xaxis.set_label_position('top') # Plot root data if they're available if self.roots is not None: if transpose: x = self.roots[:] y = self.parameter_grids[0][0,:] else: x = self.parameter_grids[0][0,:] y = self.roots[:] if transpose: y, x = y[np.isfinite(x)], x[np.isfinite(x)] else: y, x = y[np.isfinite(y)], x[np.isfinite(y)] pax.scatter(x,y, color='k') # Pretty up the plot, save. pax.set_ylim(yy.min(),yy.max()) pax.set_xlim(xx.min(),xx.max()) if xlabel is None: xlabel = self.param_names[0] if ylabel is None: ylabel = self.param_names[1] pax.set_xlabel(xlabel) pax.set_ylabel(ylabel) return pax,cax