def _set_hexagon2(self): self._transform = Affine2D().scale(0.5).rotate_deg(30) self._snap_threshold = None fs = self.get_fillstyle() polypath = Path.unit_regular_polygon(6) if not self._half_fill(): self._path = polypath else: verts = polypath.vertices # not drawing inside lines x, y = np.sqrt(3) / 4, 3 / 4. top = Path(verts[(1, 0, 5, 4, 1), :]) bottom = Path(verts[(1, 2, 3, 4), :]) left = Path( np.vstack(([x, y], verts[(0, 1, 2), :], [-x, -y], [x, y]))) right = Path(np.vstack(([x, y], verts[(5, 4, 3), :], [-x, -y]))) if fs == 'top': mpath, mpath_alt = top, bottom elif fs == 'bottom': mpath, mpath_alt = bottom, top elif fs == 'left': mpath, mpath_alt = left, right else: mpath, mpath_alt = right, left self._path = mpath self._alt_path = mpath_alt self._alt_transform = self._transform self._joinstyle = 'miter'
def _set_mathtext_path(self): """ Draws mathtext markers '$...$' using TextPath object. Submitted by tcb """ from matplotlib.text import TextPath from matplotlib.font_manager import FontProperties # again, the properties could be initialised just once outside # this function # Font size is irrelevant here, it will be rescaled based on # the drawn size later props = FontProperties(size=1.0) text = TextPath(xy=(0, 0), s=self.get_marker(), fontproperties=props, usetex=rcParams['text.usetex']) if len(text.vertices) == 0: return xmin, ymin = text.vertices.min(axis=0) xmax, ymax = text.vertices.max(axis=0) width = xmax - xmin height = ymax - ymin max_dim = max(width, height) self._transform = Affine2D() \ .translate(-xmin + 0.5 * -width, -ymin + 0.5 * -height) \ .scale(1.0 / max_dim) self._path = text self._snap = False
def _set_octagon(self): self._transform = Affine2D().scale(0.5) self._snap_threshold = 5.0 fs = self.get_fillstyle() polypath = Path.unit_regular_polygon(8) if not self._half_fill(): self._transform.rotate_deg(22.5) self._path = polypath else: x = np.sqrt(2.) / 4. half = Path([[0, -1], [0, 1], [-x, 1], [-1, x], [-1, -x], [-x, -1], [0, -1]]) if fs == 'bottom': rotate = 90. elif fs == 'top': rotate = 270. elif fs == 'right': rotate = 180. else: rotate = 0. self._transform.rotate_deg(rotate) self._path = self._alt_path = half self._alt_transform = self._transform.frozen().rotate_deg(180.0) self._joinstyle = 'miter'
def _draw_circle(self, renderer, gc, path, path_trans): w = renderer.points_to_pixels(self._markersize) * 0.5 rgbFace = self._get_rgb_face() transform = Affine2D().scale(w, w) renderer.draw_markers( gc, Path.unit_circle(), transform, path, path_trans, rgbFace)
def _set_tuple_marker(self): marker = self._marker if is_numlike(marker[0]): if len(marker) == 2: numsides, rotation = marker[0], 0.0 elif len(marker) == 3: numsides, rotation = marker[0], marker[2] symstyle = marker[1] if symstyle == 0: self._path = Path.unit_regular_polygon(numsides) self._joinstyle = 'miter' elif symstyle == 1: self._path = Path.unit_regular_star(numsides) self._joinstyle = 'bevel' elif symstyle == 2: self._path = Path.unit_regular_asterisk(numsides) self._filled = False self._joinstyle = 'bevel' elif symstyle == 3: self._path = Path.unit_circle() self._transform = Affine2D().scale(0.5).rotate_deg(rotation) else: verts = np.asarray(marker[0]) path = Path(verts) self._set_custom_marker(path)
def _draw_star(self, renderer, gc, path, path_trans): offset = 0.5 * renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset) rgbFace = self._get_rgb_face() _starpath = Path.unit_regular_star(5, innerCircle=0.381966) renderer.draw_markers(gc, _starpath, transform, path, path_trans, rgbFace)
def _set_hexagon1(self): self._transform = Affine2D().scale(0.5) self._snap_threshold = None fs = self.get_fillstyle() polypath = Path.unit_regular_polygon(6) if not self._half_fill(): self._path = polypath else: verts = polypath.vertices # not drawing inside lines x = np.abs(np.cos(5 * np.pi / 6.)) top = Path(np.vstack(([-x, 0], verts[(1, 0, 5), :], [x, 0]))) bottom = Path(np.vstack(([-x, 0], verts[2:5, :], [x, 0]))) left = Path(verts[(0, 1, 2, 3), :]) right = Path(verts[(0, 5, 4, 3), :]) if fs == 'top': mpath, mpath_alt = top, bottom elif fs == 'bottom': mpath, mpath_alt = bottom, top elif fs == 'left': mpath, mpath_alt = left, right else: mpath, mpath_alt = right, left self._path = mpath self._alt_path = mpath_alt self._alt_transform = self._transform self._joinstyle = 'miter'
def _draw_hexagon2(self, renderer, gc, path, path_trans): gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0) offset = 0.5 * renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset).rotate_deg(30) rgbFace = self._get_rgb_face() renderer.draw_markers(gc, Path.unit_regular_polygon(6), transform, path, path_trans, rgbFace)
def _set_square(self): self._transform = Affine2D().translate(-0.5, -0.5) self._snap_threshold = 2.0 fs = self.get_fillstyle() if not self._half_fill(): self._path = Path.unit_rectangle() else: # build a bottom filled square out of two rectangles, one # filled. Use the rotation to support left, right, bottom # or top if fs == 'bottom': rotate = 0. elif fs == 'top': rotate = 180. elif fs == 'left': rotate = 270. else: rotate = 90. self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 0.5], [0.0, 0.5], [0.0, 0.0]]) self._alt_path = Path([[0.0, 0.5], [1.0, 0.5], [1.0, 1.0], [0.0, 1.0], [0.0, 0.5]]) self._transform.rotate_deg(rotate) self._alt_transform = self._transform self._joinstyle = 'miter'
def _draw_diamond(self, renderer, gc, path, path_trans): gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0) side = renderer.points_to_pixels(self._markersize) transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45).scale(side) rgbFace = self._get_rgb_face() renderer.draw_markers(gc, Path.unit_rectangle(), transform, path, path_trans, rgbFace)
def _draw_triangle_right(self, renderer, gc, path, path_trans): gc.set_snap(renderer.points_to_pixels(self._markersize) >= 5.0) offset = 0.5*renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset, offset).rotate_deg(-90) rgbFace = self._get_rgb_face() renderer.draw_markers(gc, self._triangle_path, transform, path, path_trans, rgbFace)
def _draw_thin_diamond(self, renderer, gc, path, path_trans): offset = renderer.points_to_pixels(self._markersize) transform = Affine2D().translate(-0.5, -0.5) \ .rotate_deg(45).scale(offset * 0.6, offset) rgbFace = self._get_rgb_face() renderer.draw_markers(gc, Path.unit_rectangle(), transform, path, path_trans, rgbFace)
def _set_triangle(self, rot, skip): self._transform = Affine2D().scale(0.5, 0.5).rotate_deg(rot) self._snap_threshold = 5.0 fs = self.get_fillstyle() if not self._half_fill(): self._path = self._triangle_path else: mpaths = [ self._triangle_path_u, self._triangle_path_l, self._triangle_path_d, self._triangle_path_r ] if fs == 'top': self._path = mpaths[(0 + skip) % 4] self._alt_path = mpaths[(2 + skip) % 4] elif fs == 'bottom': self._path = mpaths[(2 + skip) % 4] self._alt_path = mpaths[(0 + skip) % 4] elif fs == 'left': self._path = mpaths[(1 + skip) % 4] self._alt_path = mpaths[(3 + skip) % 4] else: self._path = mpaths[(3 + skip) % 4] self._alt_path = mpaths[(1 + skip) % 4] self._alt_transform = self._transform self._joinstyle = 'miter'
def _set_star(self): self._transform = Affine2D().scale(0.5) self._snap_threshold = 5.0 fs = self.get_fillstyle() polypath = Path.unit_regular_star(5, innerCircle=0.381966) if not self._half_fill(): self._path = polypath else: verts = polypath.vertices top = Path(np.vstack((verts[0:4, :], verts[7:10, :], verts[0]))) bottom = Path(np.vstack((verts[3:8, :], verts[3]))) left = Path(np.vstack((verts[0:6, :], verts[0]))) right = Path(np.vstack((verts[0], verts[5:10, :], verts[0]))) if fs == 'top': mpath, mpath_alt = top, bottom elif fs == 'bottom': mpath, mpath_alt = bottom, top elif fs == 'left': mpath, mpath_alt = left, right else: mpath, mpath_alt = right, left self._path = mpath self._alt_path = mpath_alt self._alt_transform = self._transform self._joinstyle = 'bevel'
def _set_pentagon(self): self._transform = Affine2D().scale(0.5) self._snap_threshold = 5.0 polypath = Path.unit_regular_polygon(5) fs = self.get_fillstyle() if not self._half_fill(): self._path = polypath else: verts = polypath.vertices y = (1 + np.sqrt(5)) / 4. top = Path([verts[0], verts[1], verts[4], verts[0]]) bottom = Path([verts[1], verts[2], verts[3], verts[4], verts[1]]) left = Path([verts[0], verts[1], verts[2], [0, -y], verts[0]]) right = Path([verts[0], verts[4], verts[3], [0, -y], verts[0]]) if fs == 'top': mpath, mpath_alt = top, bottom elif fs == 'bottom': mpath, mpath_alt = bottom, top elif fs == 'left': mpath, mpath_alt = left, right else: mpath, mpath_alt = right, left self._path = mpath self._alt_path = mpath_alt self._alt_transform = self._transform self._joinstyle = 'miter'
def get_paths_extents(paths, transforms=[]): """ Given a sequence of :class:`Path` objects and optional :class:`~matplotlib.transforms.Transform` objects, returns the bounding box that encapsulates all of them. *paths* is a sequence of :class:`Path` instances. *transforms* is an optional sequence of :class:`~matplotlib.transforms.Affine2D` instances to apply to each path. """ from transforms import Bbox, Affine2D if len(paths) == 0: raise ValueError("No paths provided") return Bbox.from_extents(*_path.get_path_collection_extents( Affine2D(), paths, transforms, [], Affine2D()))
def _recache(self): self._path = Path(np.empty((0, 2))) self._transform = Affine2D() self._alt_path = None self._alt_transform = None self._snap_threshold = None self._filled = True self._marker_function()
def _draw_point(self, renderer, gc, path, path_trans): w = renderer.points_to_pixels(self._markersize) * \ self._point_size_reduction * 0.5 gc.set_snap(renderer.points_to_pixels(self._markersize) > 3.0) rgbFace = self._get_rgb_face() transform = Affine2D().scale(w) renderer.draw_markers(gc, Path.unit_circle(), transform, path, path_trans, rgbFace)
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=1.0, # the default linewidth of the frame frameon=True, # whether or not to draw the figure frame subplotpars=None, # default to rc ): """ figsize is a w,h tuple in inches dpi is dots per inch subplotpars is a SubplotParams instance, defaults to rc """ Artist.__init__(self) 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) self.figurePatch = Rectangle( xy=(0, 0), width=1, height=1, facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth, ) self._set_artist_props(self.figurePatch) self._hold = rcParams['axes.hold'] self.canvas = None if subplotpars is None: subplotpars = SubplotParams() self.subplotpars = subplotpars self._axstack = Stack() # maintain the current axes self.axes = [] self.clf() self._cachedRenderer = None self._autoLayout = rcParams['figure.autolayout']
def _set_pixel(self): self._path = Path.unit_rectangle() # Ideally, you'd want -0.5, -0.5 here, but then the snapping # algorithm in the Agg backend will round this to a 2x2 # rectangle from (-1, -1) to (1, 1). By offsetting it # slightly, we can force it to be (0, 0) to (1, 1), which both # makes it only be a single pixel and places it correctly # aligned to 1-width stroking (i.e. the ticks). This hack is # the best of a number of bad alternatives, mainly because the # backends are not aware of what marker is actually being used # beyond just its path data. self._transform = Affine2D().translate(-0.49999, -0.49999) self._snap_threshold = None
def _set_circle(self, reduction=1.0): self._transform = Affine2D().scale(0.5 * reduction) self._snap_threshold = 6.0 fs = self.get_fillstyle() if not self._half_fill(): self._path = Path.unit_circle() else: # build a right-half circle if fs == 'bottom': rotate = 270. elif fs == 'top': rotate = 90. elif fs == 'left': rotate = 180. else: rotate = 0. self._path = self._alt_path = Path.unit_circle_righthalf() self._transform.rotate_deg(rotate) self._alt_transform = self._transform.frozen().rotate_deg(180.)
def _set_diamond(self): self._transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45) self._snap_threshold = 5.0 fs = self.get_fillstyle() if fs=='full': self._path = Path.unit_rectangle() else: self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]]) self._alt_path = Path([[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [0.0, 0.0]]) if fs=='bottom': rotate = 270. elif fs=='top': rotate = 90. elif fs=='left': rotate = 180. else: rotate = 0. self._transform.rotate_deg(rotate) self._alt_transform = self._transform
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 __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 ): """ *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 """ 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._axstack = AxesStack() # track all figure axes and current axes self.clf() self._cachedRenderer = None
def __init__(self, parent, handles, labels, loc = None, numpoints = None, # the number of points in the legend line prop = None, pad = None, # the fractional whitespace inside the legend border markerscale = None, # the relative size of legend markers vs. original # the following dimensions are in axes coords labelsep = None, # the vertical space between the legend entries handlelen = None, # the length of the legend lines handletextsep = None, # the space between the legend line and legend text axespad = None, # the border between the axes and legend edge shadow = None ): """ parent # the artist that contains the legend handles # a list of artists (lines, patches) to add to the legend labels # a list of strings to label the legend loc # a location code numpoints = 4 # the number of points in the legend line prop = FontProperties(size='smaller') # the font property pad = 0.2 # the fractional whitespace inside the legend border markerscale = 0.6 # the relative size of legend markers vs. original shadow # if True, draw a shadow behind legend The following dimensions are in axes coords labelsep = 0.005 # the vertical space between the legend entries handlelen = 0.05 # the length of the legend lines handletextsep = 0.02 # the space between the legend line and legend text axespad = 0.02 # the border between the axes and legend edge """ from axes import Axes # local import only to avoid circularity from figure import Figure # local import only to avoid circularity Artist.__init__(self) proplist=[numpoints, pad, markerscale, labelsep, handlelen, handletextsep, axespad, shadow] propnames=['numpoints', 'pad', 'markerscale', 'labelsep', 'handlelen', 'handletextsep', 'axespad', 'shadow'] for name, value in safezip(propnames,proplist): if value is None: value=rcParams["legend."+name] setattr(self,name,value) if self.numpoints <= 0: raise ValueError("numpoints must be >= 0; it was %d"% numpoints) if prop is None: self.prop=FontProperties(size=rcParams["legend.fontsize"]) else: self.prop=prop self.fontsize = self.prop.get_size_in_points() if isinstance(parent,Axes): self.isaxes = True self.set_figure(parent.figure) elif isinstance(parent,Figure): self.isaxes = False self.set_figure(parent) else: raise TypeError("Legend needs either Axes or Figure as parent") self.parent = parent self._offsetTransform = Affine2D() self._parentTransform = BboxTransformTo(parent.bbox) Artist.set_transform(self, self._offsetTransform + self._parentTransform) if loc is None: loc = rcParams["legend.loc"] if not self.isaxes and loc in [0,'best']: loc = 'upper right' if is_string_like(loc): if not self.codes.has_key(loc): if self.isaxes: warnings.warn('Unrecognized location "%s". Falling back on "best"; ' 'valid locations are\n\t%s\n' % (loc, '\n\t'.join(self.codes.keys()))) loc = 0 else: warnings.warn('Unrecognized location "%s". Falling back on "upper right"; ' 'valid locations are\n\t%s\n' % (loc, '\n\t'.join(self.codes.keys()))) loc = 1 else: loc = self.codes[loc] if not self.isaxes and loc == 0: warnings.warn('Automatic legend placement (loc="best") not implemented for figure legend. ' 'Falling back on "upper right".') loc = 1 self._loc = loc self.legendPatch = Rectangle( xy=(0.0, 0.0), width=0.5, height=0.5, facecolor='w', edgecolor='k', ) self._set_artist_props(self.legendPatch) # make a trial box in the middle of the axes. relocate it # based on it's bbox left, top = 0.5, 0.5 textleft = left+ self.handlelen+self.handletextsep self.texts = self._get_texts(labels, textleft, top) self.legendHandles = self._get_handles(handles, self.texts) self._drawFrame = True
def _draw_x(self, renderer, gc, path, path_trans): offset = 0.5*renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset) renderer.draw_markers(gc, self._x_path, transform, path, path_trans)
def _draw_caretright(self, renderer, gc, path, path_trans): offset = 0.5*renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset).rotate_deg(90) renderer.draw_markers(gc, self._caret_path, transform, path, path_trans)
def _draw_tickdown(self, renderer, gc, path, path_trans): offset = renderer.points_to_pixels(self._markersize) marker_transform = Affine2D().scale(1.0, -offset) renderer.draw_markers(gc, self._tickvert_path, marker_transform, path, path_trans)
def _draw_tickright(self, renderer, gc, path, path_trans): offset = renderer.points_to_pixels(self._markersize) marker_transform = Affine2D().scale(offset, 1.0) renderer.draw_markers(gc, self._tickhoriz_path, marker_transform, path, path_trans)
def _draw_hexagon1(self, renderer, gc, path, path_trans): offset = 0.5 * renderer.points_to_pixels(self._markersize) transform = Affine2D().scale(offset) rgbFace = self._get_rgb_face() renderer.draw_markers(gc, Path.unit_regular_polygon(6), transform, path, path_trans, rgbFace)