def _render_on_subplot(self, subplot): """ TESTS:: sage: P = polygon([(0,0), (1,2), (0,1), (-1,2)]) """ import matplotlib.patches as patches options = self.options() p = patches.Polygon([(self.xdata[i], self.ydata[i]) for i in range(len(self.xdata))]) p.set_linewidth(float(options['thickness'])) a = float(options['alpha']) z = int(options.pop('zorder', 1)) p.set_alpha(a) f = options.pop('fill') p.set_fill(f) c = to_mpl_color(options['rgbcolor']) if f: ec = options['edgecolor'] if ec is None: p.set_color(c) else: p.set_facecolor(c) p.set_edgecolor(to_mpl_color(ec)) else: p.set_color(c) p.set_label(options['legend_label']) p.set_zorder(z) subplot.add_patch(p)
def _render_on_subplot(self, subplot): """ TESTS:: sage: P = polygon([(0,0), (1,2), (0,1), (-1,2)]) """ import matplotlib.patches as patches options = self.options() p = patches.Polygon([(self.xdata[i],self.ydata[i]) for i in xrange(len(self.xdata))]) p.set_linewidth(float(options['thickness'])) a = float(options['alpha']) z = int(options.pop('zorder', 1)) p.set_alpha(a) f = options.pop('fill') p.set_fill(f) c = to_mpl_color(options['rgbcolor']) if f: ec = options['edgecolor'] if ec is None: p.set_color(c) else: p.set_facecolor(c) p.set_edgecolor(to_mpl_color(ec)) else: p.set_color(c) p.set_label(options['legend_label']) p.set_zorder(z) subplot.add_patch(p)
def _render_on_subplot(self, subplot): """ TESTS:: sage: D = disk((2,-1), 2, (0, pi), color='black', thickness=3, fill=False); D Save alpha information in pdf (see :trac:`13732`):: sage: f = tmp_filename(ext='.pdf') sage: p = disk((0,0), 5, (0, pi/4), alpha=0.5) sage: p.save(f) """ import matplotlib.patches as patches options = self.options() deg1 = self.rad1*(180./pi) #convert radians to degrees deg2 = self.rad2*(180./pi) z = int(options.pop('zorder', 0)) p = patches.Wedge((float(self.x), float(self.y)), float(self.r), float(deg1), float(deg2), zorder=z) a = float(options['alpha']) p.set_alpha(a) p.set_linewidth(float(options['thickness'])) p.set_fill(options['fill']) c = to_mpl_color(options['rgbcolor']) p.set_edgecolor(c) p.set_facecolor(c) p.set_label(options['legend_label']) subplot.add_patch(p)
def _render_on_subplot(self, subplot): r""" TESTS: We check to make sure that \#2076 is fixed by verifying all the points are red:: sage: point(((1,1), (2,2), (3,3)), rgbcolor=hue(1), size=30) """ options = self.options() #Convert the color to a hex string so that the scatter #method does not interpret it as a list of 3 floating #point color specifications when there are #three points. This is mentioned in the matplotlib 0.98 #documentation and fixes \#2076 from matplotlib.colors import rgb2hex c = rgb2hex(to_mpl_color(options['rgbcolor'])) a = float(options['alpha']) z = int(options.pop('zorder', 0)) s = int(options['size']) faceted = options['faceted'] #faceted=True colors the edge of point scatteroptions = {} if not faceted: scatteroptions['edgecolors'] = 'none' subplot.scatter(self.xdata, self.ydata, s=s, c=c, alpha=a, zorder=z, label=options['legend_label'], **scatteroptions)
def _plot3d_options(self, options=None): """ Translate 2D plot options into 3D plot options. EXAMPLES:: sage: A=point((1,1),size=22) sage: a=A[0];a Point set defined by 1 point(s) sage: b=a.plot3d() sage: b.size 22 sage: b=a.plot3d(size=3) sage: b.size 3 """ if options is None: options = dict(self.options()) options_3d = {} if 'size' in options: options_3d['size'] = options['size'] del options['size'] if options.pop('faceted', False): raise NotImplementedError("3D points can not be faceted.") for o in ('marker', 'markeredgecolor'): # remove 2D options if o in options: del options[o] options_3d.update(GraphicPrimitive_xydata._plot3d_options(self, options)) return options_3d
def _render_on_subplot(self,subplot): r""" TESTS: We check to make sure that \#2076 is fixed by verifying all the points are red:: sage: point(((1,1), (2,2), (3,3)), rgbcolor=hue(1), size=30) """ options = self.options() #Convert the color to a hex string so that the scatter #method does not interpret it as a list of 3 floating #point color specifications when there are #three points. This is mentioned in the matplotlib 0.98 #documentation and fixes \#2076 from matplotlib.colors import rgb2hex c = rgb2hex(to_mpl_color(options['rgbcolor'])) a = float(options['alpha']) z = int(options.pop('zorder', 0)) s = int(options['size']) faceted = options['faceted'] #faceted=True colors the edge of point scatteroptions={} if not faceted: scatteroptions['edgecolors'] = 'none' subplot.scatter(self.xdata, self.ydata, s=s, c=c, alpha=a, zorder=z, label=options['legend_label'], **scatteroptions)
def _render_on_subplot(self, subplot): """ Render this arrow in a subplot. This is the key function that defines how this arrow graphics primitive is rendered in matplotlib's library. EXAMPLES: This function implicitly ends up rendering this arrow on a matplotlib subplot:: sage: arrow((0,1), (2,-1)) TESTS: The length of the ends (shrinkA and shrinkB) should not depend on the width of the arrow, because Matplotlib already takes this into account. See :trac:`12836`:: sage: fig = Graphics().matplotlib() sage: sp = fig.add_subplot(1,1,1) sage: a = arrow((0,0), (1,1)) sage: b = arrow((0,0), (1,1), width=20) sage: p1 = a[0]._render_on_subplot(sp) sage: p2 = b[0]._render_on_subplot(sp) sage: p1.shrinkA == p2.shrinkA True sage: p1.shrinkB == p2.shrinkB True """ options = self.options() head = options.pop('head') if head == 0: style = '<|-' elif head == 1: style = '-|>' elif head == 2: style = '<|-|>' else: raise KeyError( 'head parameter must be one of 0 (start), 1 (end) or 2 (both).' ) width = float(options['width']) arrowshorten_end = float(options.get('arrowshorten', 0)) / 2.0 arrowsize = float(options.get('arrowsize', 5)) head_width = arrowsize head_length = arrowsize * 2.0 color = to_mpl_color(options['rgbcolor']) from matplotlib.patches import FancyArrowPatch p = FancyArrowPatch((self.xtail, self.ytail), (self.xhead, self.yhead), lw=width, arrowstyle='%s,head_width=%s,head_length=%s' % (style, head_width, head_length), shrinkA=arrowshorten_end, shrinkB=arrowshorten_end, fc=color, ec=color, linestyle=options['linestyle']) p.set_zorder(options['zorder']) p.set_label(options['legend_label']) subplot.add_patch(p) return p
def _plot3d_options(self, options=None): """ Translate 2D plot options into 3D plot options. EXAMPLES:: sage: T = text("ABC",(1,1)) sage: t = T[0] sage: t.options()['rgbcolor'] (0.0, 0.0, 1.0) sage: s=t.plot3d() sage: s.jmol_repr(s.testing_render_params())[0][1] 'color atom [0,0,255]' """ if options is None: options = dict(self.options()) options_3d = {} for s in ['fontfamily', 'fontsize', 'fontstyle', 'fontweight']: if s in options: options_3d[s] = options.pop(s) # TODO: figure out how to implement rather than ignore for s in [ 'axis_coords', 'clip', 'horizontal_alignment', 'rotation', 'vertical_alignment' ]: if s in options: del options[s] options_3d.update(GraphicPrimitive._plot3d_options(self, options)) return options_3d
def _plot3d_options(self, options=None): """ Translate 2D plot options into 3D plot options. EXAMPLES:: sage: A=point((1,1),size=22) sage: a=A[0];a Point set defined by 1 point(s) sage: b=a.plot3d() sage: b.size 22 sage: b=a.plot3d(size=3) sage: b.size 3 """ if options is None: options = dict(self.options()) options_3d = {} if 'size' in options: options_3d['size'] = options['size'] del options['size'] if options.pop('faceted', False): raise NotImplementedError("3D points cannot be faceted.") for o in ('marker', 'markeredgecolor'): # remove 2D options if o in options: del options[o] options_3d.update( GraphicPrimitive_xydata._plot3d_options(self, options)) return options_3d
def _render_on_subplot(self, subplot): """ TESTS:: sage: C = circle((2,pi), 2, edgecolor='black', facecolor='green', fill=True) """ import matplotlib.patches as patches options = self.options() p = patches.Circle((float(self.x), float(self.y)), float(self.r), clip_on=options['clip']) if not options['clip']: self._bbox_extra_artists=[p] p.set_linewidth(float(options['thickness'])) p.set_fill(options['fill']) a = float(options['alpha']) p.set_alpha(a) ec = to_mpl_color(options['edgecolor']) fc = to_mpl_color(options['facecolor']) if 'rgbcolor' in options: ec = fc = to_mpl_color(options['rgbcolor']) p.set_edgecolor(ec) p.set_facecolor(fc) p.set_linestyle(options['linestyle']) p.set_label(options['legend_label']) z = int(options.pop('zorder', 0)) p.set_zorder(z) subplot.add_patch(p)
def _render_on_subplot(self, subplot): """ Render this arrow in a subplot. This is the key function that defines how this arrow graphics primitive is rendered in matplotlib's library. EXAMPLES:: This function implicitly ends up rendering this arrow on a matplotlib subplot: sage: arrow(path=[[(0,1), (2,-1), (4,5)]]) """ options = self.options() width = float(options['width']) head = options.pop('head') if head == 0: style = '<|-' elif head == 1: style = '-|>' elif head == 2: style = '<|-|>' else: raise KeyError('head parameter must be one of 0 (start), 1 (end) or 2 (both).') arrowsize = float(options.get('arrowsize',5)) head_width=arrowsize head_length=arrowsize*2.0 color = to_mpl_color(options['rgbcolor']) from matplotlib.patches import FancyArrowPatch from matplotlib.path import Path bpath = Path(self.vertices, self.codes) p = FancyArrowPatch(path=bpath, lw=width, arrowstyle='%s,head_width=%s,head_length=%s'%(style,head_width, head_length), fc=color, ec=color, linestyle=options['linestyle']) p.set_zorder(options['zorder']) p.set_label(options['legend_label']) subplot.add_patch(p) return p
def _render_on_subplot(self, subplot): """ TESTS:: sage: C = circle((2,pi), 2, edgecolor='black', facecolor='green', fill=True) """ import matplotlib.patches as patches from sage.plot.misc import get_matplotlib_linestyle options = self.options() p = patches.Circle((float(self.x), float(self.y)), float(self.r), clip_on=options['clip']) if not options['clip']: self._bbox_extra_artists = [p] p.set_linewidth(float(options['thickness'])) p.set_fill(options['fill']) a = float(options['alpha']) p.set_alpha(a) ec = to_mpl_color(options['edgecolor']) fc = to_mpl_color(options['facecolor']) if 'rgbcolor' in options: ec = fc = to_mpl_color(options['rgbcolor']) p.set_edgecolor(ec) p.set_facecolor(fc) p.set_linestyle( get_matplotlib_linestyle(options['linestyle'], return_type='long')) p.set_label(options['legend_label']) z = int(options.pop('zorder', 0)) p.set_zorder(z) subplot.add_patch(p)
def _render_on_subplot(self, subplot): """ TESTS:: sage: D = disk((2,-1), 2, (0, pi), color='black', thickness=3, fill=False); D Graphics object consisting of 1 graphics primitive Save alpha information in pdf (see :trac:`13732`):: sage: f = tmp_filename(ext='.pdf') sage: p = disk((0,0), 5, (0, pi/4), alpha=0.5) sage: p.save(f) """ import matplotlib.patches as patches options = self.options() deg1 = self.rad1*(180./pi) #convert radians to degrees deg2 = self.rad2*(180./pi) z = int(options.pop('zorder', 0)) p = patches.Wedge((float(self.x), float(self.y)), float(self.r), float(deg1), float(deg2), zorder=z) a = float(options['alpha']) p.set_alpha(a) p.set_linewidth(float(options['thickness'])) p.set_fill(options['fill']) c = to_mpl_color(options['rgbcolor']) p.set_edgecolor(c) p.set_facecolor(c) p.set_label(options['legend_label']) subplot.add_patch(p)
def get_minmax_data(self): """ Get minimum and maximum horizontal and vertical ranges for the Histogram object. EXAMPLES:: sage: H = histogram([10,3,5], normed=True); h = H[0] sage: h.get_minmax_data() {'xmax': 10.0, 'xmin': 3.0, 'ymax': 0.4761904761904765, 'ymin': 0} sage: G = histogram([random() for _ in range(500)]); g = G[0] sage: g.get_minmax_data() # random output {'xmax': 0.99729312925213209, 'xmin': 0.00013024562219410285, 'ymax': 61, 'ymin': 0} sage: Y = histogram([random()*10 for _ in range(500)], range=[2,8]); y = Y[0] sage: ymm = y.get_minmax_data(); ymm['xmax'], ymm['xmin'] (8.0, 2.0) sage: Z = histogram([[1,3,2,0], [4,4,3,3]]); z = Z[0] sage: z.get_minmax_data() {'xmax': 4.0, 'xmin': 0, 'ymax': 2, 'ymin': 0} """ import numpy options = self.options() opt = dict(range=options.pop('range', None), bins=options.pop('bins', None), normed=options.pop('normed', None), weights=options.pop('weights', None)) #check to see if a list of datasets if not hasattr(self.datalist[0], '__contains__'): ydata, xdata = numpy.histogram(self.datalist, **opt) return minmax_data(xdata, [0] + list(ydata), dict=True) else: m = {'xmax': 0, 'xmin': 0, 'ymax': 0, 'ymin': 0} if not options.pop('stacked', None): for d in self.datalist: ydata, xdata = numpy.histogram(d, **opt) m['xmax'] = max([m['xmax']] + list(xdata)) m['xmin'] = min([m['xmin']] + list(xdata)) m['ymax'] = max([m['ymax']] + list(ydata)) return m else: for d in self.datalist: ydata, xdata = numpy.histogram(d, **opt) m['xmax'] = max([m['xmax']] + list(xdata)) m['xmin'] = min([m['xmin']] + list(xdata)) m['ymax'] = m['ymax'] + max(list(ydata)) return m
def _plot3d_options(self, options=None): """ Translate 2d plot options into 3d plot options. EXAMPLES:: sage: P = polygon([(1,1), (1,2), (2,2), (2,1)], alpha=.5) sage: p=P[0]; p Polygon defined by 4 points sage: q=p.plot3d() sage: q.texture.opacity 0.500000000000000 """ if options is None: options = dict(self.options()) for o in ['thickness', 'zorder', 'legend_label', 'fill']: options.pop(o, None) return GraphicPrimitive_xydata._plot3d_options(self, options)
def _plot3d_options(self, options=None): """ Translate 2d plot options into 3d plot options. EXAMPLES:: sage: P = polygon([(1,1), (1,2), (2,2), (2,1)], alpha=.5) sage: p=P[0]; p Polygon defined by 4 points sage: q=p.plot3d() sage: q.texture.opacity 0.5 """ if options is None: options = dict(self.options()) for o in ['thickness', 'zorder', 'legend_label', 'fill', 'edgecolor']: options.pop(o, None) return GraphicPrimitive_xydata._plot3d_options(self, options)
def _render_on_subplot(self, subplot): """ Render this arrow in a subplot. This is the key function that defines how this arrow graphics primitive is rendered in matplotlib's library. EXAMPLES: This function implicitly ends up rendering this arrow on a matplotlib subplot:: sage: arrow((0,1), (2,-1)) TESTS: The length of the ends (shrinkA and shrinkB) should not depend on the width of the arrow, because Matplotlib already takes this into account. See :trac:`12836`:: sage: fig = Graphics().matplotlib() sage: sp = fig.add_subplot(1,1,1) sage: a = arrow((0,0), (1,1)) sage: b = arrow((0,0), (1,1), width=20) sage: p1 = a[0]._render_on_subplot(sp) sage: p2 = b[0]._render_on_subplot(sp) sage: p1.shrinkA == p2.shrinkA True sage: p1.shrinkB == p2.shrinkB True """ options = self.options() head = options.pop('head') if head == 0: style = '<|-' elif head == 1: style = '-|>' elif head == 2: style = '<|-|>' else: raise KeyError('head parameter must be one of 0 (start), 1 (end) or 2 (both).') width = float(options['width']) arrowshorten_end = float(options.get('arrowshorten',0))/2.0 arrowsize = float(options.get('arrowsize',5)) head_width=arrowsize head_length=arrowsize*2.0 color = to_mpl_color(options['rgbcolor']) from matplotlib.patches import FancyArrowPatch p = FancyArrowPatch((self.xtail, self.ytail), (self.xhead, self.yhead), lw=width, arrowstyle='%s,head_width=%s,head_length=%s'%(style,head_width, head_length), shrinkA=arrowshorten_end, shrinkB=arrowshorten_end, fc=color, ec=color, linestyle=options['linestyle']) p.set_zorder(options['zorder']) p.set_label(options['legend_label']) subplot.add_patch(p) return p
def _render_on_subplot(self, subplot): """ Render this arrow in a subplot. This is the key function that defines how this arrow graphics primitive is rendered in matplotlib's library. EXAMPLES: This function implicitly ends up rendering this arrow on a matplotlib subplot:: sage: arrow(path=[[(0,1), (2,-1), (4,5)]]) Graphics object consisting of 1 graphics primitive """ from sage.plot.misc import get_matplotlib_linestyle options = self.options() width = float(options['width']) head = options.pop('head') if head == 0: style = '<|-' elif head == 1: style = '-|>' elif head == 2: style = '<|-|>' else: raise KeyError( 'head parameter must be one of 0 (start), 1 (end) or 2 (both).' ) arrowsize = float(options.get('arrowsize', 5)) head_width = arrowsize head_length = arrowsize * 2.0 color = to_mpl_color(options['rgbcolor']) from matplotlib.patches import FancyArrowPatch from matplotlib.path import Path bpath = Path(self.vertices, self.codes) p = FancyArrowPatch(path=bpath, lw=width, arrowstyle='%s,head_width=%s,head_length=%s' % (style, head_width, head_length), fc=color, ec=color, linestyle=get_matplotlib_linestyle( options['linestyle'], return_type='long')) p.set_zorder(options['zorder']) p.set_label(options['legend_label']) subplot.add_patch(p) return p
def _render_on_subplot(self, subplot): """ Render this arrow in a subplot. This is the key function that defines how this arrow graphics primitive is rendered in matplotlib's library. EXAMPLES:: This function implicitly ends up rendering this arrow on a matplotlib subplot: sage: arrow(path=[[(0,1), (2,-1), (4,5)]]) Graphics object consisting of 1 graphics primitive """ from sage.plot.misc import get_matplotlib_linestyle options = self.options() width = float(options["width"]) head = options.pop("head") if head == 0: style = "<|-" elif head == 1: style = "-|>" elif head == 2: style = "<|-|>" else: raise KeyError("head parameter must be one of 0 (start), 1 (end) or 2 (both).") arrowsize = float(options.get("arrowsize", 5)) head_width = arrowsize head_length = arrowsize * 2.0 color = to_mpl_color(options["rgbcolor"]) from matplotlib.patches import FancyArrowPatch from matplotlib.path import Path bpath = Path(self.vertices, self.codes) p = FancyArrowPatch( path=bpath, lw=width, arrowstyle="%s,head_width=%s,head_length=%s" % (style, head_width, head_length), fc=color, ec=color, ) p.set_linestyle(get_matplotlib_linestyle(options["linestyle"], return_type="long")) p.set_zorder(options["zorder"]) p.set_label(options["legend_label"]) subplot.add_patch(p) return p
def _render_on_subplot(self, subplot): """ Render this arrow in a subplot. This is the key function that defines how this arrow graphics primitive is rendered in matplotlib's library. EXAMPLES: This function implicitly ends up rendering this arrow on a matplotlib subplot:: sage: arrow((0,1), (2,-1)) """ options = self.options() head = options.pop('head') if head == 0: style = '<|-' elif head == 1: style = '-|>' elif head == 2: style = '<|-|>' else: raise KeyError( 'head parameter must be one of 0 (start), 1 (end) or 2 (both).' ) width = float(options['width']) arrowshorten_end = float(options.get('arrowshorten', 0)) / 2.0 + width * 2 arrowsize = float(options.get('arrowsize', 5)) head_width = arrowsize head_length = arrowsize * 2.0 color = to_mpl_color(options['rgbcolor']) from matplotlib.patches import FancyArrowPatch p = FancyArrowPatch((self.xtail, self.ytail), (self.xhead, self.yhead), lw=width, arrowstyle='%s,head_width=%s,head_length=%s' % (style, head_width, head_length), shrinkA=arrowshorten_end, shrinkB=arrowshorten_end, fc=color, ec=color, linestyle=options['linestyle']) p.set_zorder(options['zorder']) p.set_label(options['legend_label']) subplot.add_patch(p) return p
def _render_on_subplot(self, subplot): """ TESTS:: sage: D = disk((2,-1), 2, (0, pi), color='black', thickness=3, fill=False); D """ import matplotlib.patches as patches options = self.options() deg1 = self.rad1*(180./pi) #convert radians to degrees deg2 = self.rad2*(180./pi) z = int(options.pop('zorder', 0)) p = patches.Wedge((float(self.x), float(self.y)), float(self.r), float(deg1), float(deg2), zorder=z) p.set_linewidth(float(options['thickness'])) p.set_fill(options['fill']) p.set_alpha(options['alpha']) c = to_mpl_color(options['rgbcolor']) p.set_edgecolor(c) p.set_facecolor(c) p.set_label(options['legend_label']) subplot.add_patch(p)
def _render_on_subplot(self, subplot): """ TESTS:: sage: A = arc((1,1),3,4,pi/4,(pi,4*pi/3)); A Graphics object consisting of 1 graphics primitive """ from sage.plot.misc import get_matplotlib_linestyle options = self.options() p = self._matplotlib_arc() p.set_linewidth(float(options['thickness'])) a = float(options['alpha']) p.set_alpha(a) z = int(options.pop('zorder', 1)) p.set_zorder(z) c = to_mpl_color(options['rgbcolor']) p.set_linestyle( get_matplotlib_linestyle(options['linestyle'], return_type='long')) p.set_edgecolor(c) subplot.add_patch(p)
def _render_on_subplot(self, subplot): """ Render this ellipse in a subplot. This is the key function that defines how this ellipse graphics primitive is rendered in matplotlib's library. TESTS:: sage: ellipse((0,0),3,1,pi/6,fill=True,alpha=0.3) Graphics object consisting of 1 graphics primitive :: sage: ellipse((3,2),1,2) Graphics object consisting of 1 graphics primitive """ import matplotlib.patches as patches from sage.plot.misc import get_matplotlib_linestyle options = self.options() p = patches.Ellipse((self.x, self.y), self.r1 * 2., self.r2 * 2., self.angle / pi * 180.) p.set_linewidth(float(options['thickness'])) p.set_fill(options['fill']) a = float(options['alpha']) p.set_alpha(a) ec = to_mpl_color(options['edgecolor']) fc = to_mpl_color(options['facecolor']) if 'rgbcolor' in options: ec = fc = to_mpl_color(options['rgbcolor']) p.set_edgecolor(ec) p.set_facecolor(fc) p.set_linestyle( get_matplotlib_linestyle(options['linestyle'], return_type='long')) p.set_label(options['legend_label']) z = int(options.pop('zorder', 0)) p.set_zorder(z) subplot.add_patch(p)
def _render_on_subplot(self, subplot): """ TESTS:: sage: matrix_plot(random_matrix(RDF, 50), cmap='jet') """ options = self.options() cmap = get_cmap(options.pop("cmap", None)) origin = options["origin"] norm = options["norm"] if norm == "value": import matplotlib norm = matplotlib.colors.NoNorm() if options["subdivisions"]: subdiv_options = options["subdivision_options"] if isinstance(subdiv_options["boundaries"], (list, tuple)): rowsub, colsub = subdiv_options["boundaries"] else: rowsub = subdiv_options["boundaries"] colsub = subdiv_options["boundaries"] if isinstance(subdiv_options["style"], (list, tuple)): rowstyle, colstyle = subdiv_options["style"] else: rowstyle = subdiv_options["style"] colstyle = subdiv_options["style"] if rowstyle is None: rowstyle = dict() if colstyle is None: colstyle = dict() # Make line objects for subdivisions from line import line2d lim = self.get_minmax_data() # First draw horizontal lines representing row subdivisions for y in rowsub: l = line2d([(lim["xmin"], y - 0.5), (lim["xmax"], y - 0.5)], **rowstyle)[0] l._render_on_subplot(subplot) for x in colsub: l = line2d([(x - 0.5, lim["ymin"]), (x - 0.5, lim["ymax"])], **colstyle)[0] l._render_on_subplot(subplot) if hasattr(self.xy_data_array, "tocoo"): # Sparse matrix -- use spy opts = options.copy() for opt in [ "vmin", "vmax", "norm", "origin", "subdivisions", "subdivision_options", "colorbar", "colorbar_options", ]: del opts[opt] if origin == "lower": subplot.spy(self.xy_data_array.tocsr()[::-1], **opts) else: subplot.spy(self.xy_data_array, **opts) else: opts = dict( cmap=cmap, interpolation="nearest", aspect="equal", norm=norm, vmin=options["vmin"], vmax=options["vmax"], origin=origin, zorder=options.get("zorder", None), ) image = subplot.imshow(self.xy_data_array, **opts) if options.get("colorbar", False): colorbar_options = options["colorbar_options"] from matplotlib import colorbar cax, kwds = colorbar.make_axes_gridspec(subplot, **colorbar_options) cb = colorbar.Colorbar(cax, image, **kwds) if origin == "upper": subplot.xaxis.tick_top() elif origin == "lower": subplot.xaxis.tick_bottom() subplot.xaxis.set_ticks_position("both") # only tick marks, not tick labels
def implicit_plot(f, xrange, yrange, **options): r""" ``implicit_plot`` takes a function of two variables, `f(x,y)` and plots the curve `f(x,y) = 0` over the specified ``xrange`` and ``yrange`` as demonstrated below. ``implicit_plot(f, (xmin, xmax), (ymin, ymax), ...)`` ``implicit_plot(f, (x, xmin, xmax), (y, ymin, ymax), ...)`` INPUT: - ``f`` -- a function of two variables or equation in two variables - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values or ``(x,xmin,xmax)`` - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values or ``(y,ymin,ymax)`` The following inputs must all be passed in as named parameters: - ``plot_points`` -- integer (default: 150); number of points to plot in each direction of the grid - ``fill`` -- boolean (default: ``False``); if ``True``, fill the region `f(x,y) < 0`. - ``linewidth`` -- integer (default: None), if a single integer all levels will be of the width given, otherwise the levels will be plotted with the widths in the order given. - ``linestyle`` -- string (default: None), the style of the line to be plotted, one of: solid, dashed, dashdot or dotted. - ``color`` -- string (default: ``blue``), the color of the plot. Colors are defined in :mod:`sage.plot.colors`; try ``colors?`` to see them all. - ``legend_label`` -- the label for this item in the legend EXAMPLES: A simple circle with a radius of 2. Note that since the input function is an expression, we need to explicitly declare the variables in 3-tuples for the range:: sage: var("x y") (x, y) sage: implicit_plot(x^2+y^2-2, (x,-3,3), (y,-3,3)) I can do the same thing, but using a callable function so I don't need to explicitly define the variables in the ranges, and filling the inside:: sage: x,y = var('x,y') sage: f(x,y) = x^2 + y^2 - 2 sage: implicit_plot(f, (-3, 3), (-3, 3),fill=True) The same circle but with a different line width:: sage: implicit_plot(f, (-3,3), (-3,3), linewidth=6) And again the same circle but this time with a dashdot border:: sage: implicit_plot(f, (-3,3), (-3,3), linestyle='dashdot') You can also plot an equation:: sage: var("x y") (x, y) sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3)) You can even change the color of the plot:: sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3), color="red") Here is a beautiful (and long) example which also tests that all colors work with this:: sage: G = Graphics() sage: counter = 0 sage: for col in colors.keys(): # long time ... G += implicit_plot(x^2+y^2==1+counter*.1, (x,-4,4),(y,-4,4),color=col) ... counter += 1 sage: G.show(frame=False) We can define a level-`n` approximation of the boundary of the Mandelbrot set:: sage: def mandel(n): ... c = polygen(CDF, 'c') ... z = 0 ... for i in range(n): ... z = z*z + c ... def f(x, y): ... val = z(CDF(x, y)) ... return val.norm() - 4 ... return f The first-level approximation is just a circle:: sage: implicit_plot(mandel(1), (-3, 3), (-3, 3)) A third-level approximation starts to get interesting:: sage: implicit_plot(mandel(3), (-2, 1), (-1.5, 1.5)) The seventh-level approximation is a degree 64 polynomial, and ``implicit_plot`` does a pretty good job on this part of the curve. (``plot_points=200`` looks even better, but it takes over a second.) :: sage: implicit_plot(mandel(7), (-0.3, 0.05), (-1.15, -0.9),plot_points=50) """ from sage.symbolic.expression import is_SymbolicEquation if is_SymbolicEquation(f): if f.operator() != operator.eq: raise ValueError, "input to implicit plot must be function or equation" f = f.lhs() - f.rhs() linewidths = options.pop('linewidth', None) linestyles = options.pop('linestyle', None) if 'color' in options: options['cmap']=[options.pop('color', None)] return contour_plot(f, xrange, yrange, linewidths=linewidths, linestyles=linestyles, **options)
def implicit_plot(f, xrange, yrange, **options): r""" ``implicit_plot`` takes a function of two variables, `f(x,y)` and plots the curve `f(x,y) = 0` over the specified ``xrange`` and ``yrange`` as demonstrated below. ``implicit_plot(f, (xmin, xmax), (ymin, ymax), ...)`` ``implicit_plot(f, (x, xmin, xmax), (y, ymin, ymax), ...)`` INPUT: - ``f`` -- a function of two variables or equation in two variables - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values or ``(x,xmin,xmax)`` - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values or ``(y,ymin,ymax)`` The following inputs must all be passed in as named parameters: - ``plot_points`` -- integer (default: 150); number of points to plot in each direction of the grid - ``fill`` -- boolean (default: ``False``); if ``True``, fill the region `f(x,y) < 0`. - ``linewidth`` -- integer (default: None), if a single integer all levels will be of the width given, otherwise the levels will be plotted with the widths in the order given. - ``linestyle`` -- string (default: None), the style of the line to be plotted, one of: ``"solid"``, ``"dashed"``, ``"dashdot"`` or ``"dotted"``, respectively ``"-"``, ``"--"``, ``"-."``, or ``":"``. - ``color`` -- string (default: ``blue``), the color of the plot. Colors are defined in :mod:`sage.plot.colors`; try ``colors?`` to see them all. - ``legend_label`` -- the label for this item in the legend - ``base`` - (default: 10) the base of the logarithm if a logarithmic scale is set. This must be greater than 1. The base can be also given as a list or tuple ``(basex, basey)``. ``basex`` sets the base of the logarithm along the horizontal axis and ``basey`` sets the base along the vertical axis. - ``scale`` -- (default: ``"linear"``) string. The scale of the axes. Possible values are ``"linear"``, ``"loglog"``, ``"semilogx"``, ``"semilogy"``. The scale can be also be given as single argument that is a list or tuple ``(scale, base)`` or ``(scale, basex, basey)``. The ``"loglog"`` scale sets both the horizontal and vertical axes to logarithmic scale. The ``"semilogx"`` scale sets the horizontal axis to logarithmic scale. The ``"semilogy"`` scale sets the vertical axis to logarithmic scale. The ``"linear"`` scale is the default value when :class:`~sage.plot.graphics.Graphics` is initialized. EXAMPLES: A simple circle with a radius of 2. Note that since the input function is an expression, we need to explicitly declare the variables in 3-tuples for the range:: sage: var("x y") (x, y) sage: implicit_plot(x^2+y^2-2, (x,-3,3), (y,-3,3)) Graphics object consisting of 1 graphics primitive I can do the same thing, but using a callable function so I don't need to explicitly define the variables in the ranges, and filling the inside:: sage: f(x,y) = x^2 + y^2 - 2 sage: implicit_plot(f, (-3, 3), (-3, 3),fill=True) Graphics object consisting of 1 graphics primitive The same circle but with a different line width:: sage: implicit_plot(f, (-3,3), (-3,3), linewidth=6) Graphics object consisting of 1 graphics primitive And again the same circle but this time with a dashdot border:: sage: implicit_plot(f, (-3,3), (-3,3), linestyle='dashdot') Graphics object consisting of 1 graphics primitive You can also plot an equation:: sage: var("x y") (x, y) sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3)) Graphics object consisting of 1 graphics primitive You can even change the color of the plot:: sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3), color="red") Graphics object consisting of 1 graphics primitive Here is a beautiful (and long) example which also tests that all colors work with this:: sage: G = Graphics() sage: counter = 0 sage: for col in colors.keys(): # long time ... G += implicit_plot(x^2+y^2==1+counter*.1, (x,-4,4),(y,-4,4),color=col) ... counter += 1 sage: G.show(frame=False) We can define a level-`n` approximation of the boundary of the Mandelbrot set:: sage: def mandel(n): ... c = polygen(CDF, 'c') ... z = 0 ... for i in range(n): ... z = z*z + c ... def f(x, y): ... val = z(CDF(x, y)) ... return val.norm() - 4 ... return f The first-level approximation is just a circle:: sage: implicit_plot(mandel(1), (-3, 3), (-3, 3)) Graphics object consisting of 1 graphics primitive A third-level approximation starts to get interesting:: sage: implicit_plot(mandel(3), (-2, 1), (-1.5, 1.5)) Graphics object consisting of 1 graphics primitive The seventh-level approximation is a degree 64 polynomial, and ``implicit_plot`` does a pretty good job on this part of the curve. (``plot_points=200`` looks even better, but it takes over a second.) :: sage: implicit_plot(mandel(7), (-0.3, 0.05), (-1.15, -0.9),plot_points=50) Graphics object consisting of 1 graphics primitive When making a filled implicit plot using a python function rather than a symbolic expression the user should increase the number of plot points to avoid artifacts:: sage: implicit_plot(lambda x,y: x^2+y^2-2, (x,-3,3), (y,-3,3), fill=True, plot_points=500) # long time Graphics object consisting of 1 graphics primitive An example of an implicit plot on 'loglog' scale:: sage: implicit_plot(x^2+y^2 == 200, (x,1,200), (y,1,200), scale='loglog') Graphics object consisting of 1 graphics primitive TESTS:: sage: f(x,y) = x^2 + y^2 - 2 sage: implicit_plot(f, (-3, 3), (-3, 3),fill=5) Traceback (most recent call last): ... ValueError: fill=5 is not supported """ from sage.symbolic.expression import is_SymbolicEquation if is_SymbolicEquation(f): if f.operator() != operator.eq: raise ValueError( "input to implicit plot must be function or equation") f = f.lhs() - f.rhs() linewidths = options.pop('linewidth', None) linestyles = options.pop('linestyle', None) if 'color' in options: options['cmap'] = [options.pop('color', None)] if options['fill'] is True: options.pop('fill') options.pop('contours', None) options.pop('cmap', None) from sage.symbolic.expression import is_Expression if not is_Expression(f): return region_plot(lambda x, y: f(x, y) < 0, xrange, yrange, borderwidth=linewidths, borderstyle=linestyles, **options) else: return region_plot(f < 0, xrange, yrange, borderwidth=linewidths, borderstyle=linestyles, **options) elif options['fill'] is False: return contour_plot(f, xrange, yrange, linewidths=linewidths, linestyles=linestyles, **options) else: raise ValueError("fill=%s is not supported" % options['fill'])
def _render_on_subplot(self, subplot): r""" Render this arrow in a subplot. This is the key function that defines how this arrow graphics primitive is rendered in matplotlib's library. EXAMPLES: This function implicitly ends up rendering this arrow on a matplotlib subplot:: sage: arrow((0,1), (2,-1)) Graphics object consisting of 1 graphics primitive TESTS: The length of the ends (shrinkA and shrinkB) should not depend on the width of the arrow, because Matplotlib already takes this into account. See :trac:`12836`:: sage: fig = Graphics().matplotlib() sage: sp = fig.add_subplot(1,1,1, label='axis1') sage: a = arrow((0,0), (1,1)) sage: b = arrow((0,0), (1,1), width=20) sage: p1 = a[0]._render_on_subplot(sp) sage: p2 = b[0]._render_on_subplot(sp) sage: p1.shrinkA == p2.shrinkA True sage: p1.shrinkB == p2.shrinkB True Dashed arrows should have solid arrowheads, :trac:`12852`. We tried to make up a test for this, which turned out to be fragile and hence was removed. In general, robust testing of graphics seems basically need a human eye or AI. """ from sage.plot.misc import get_matplotlib_linestyle options = self.options() head = options.pop('head') if head == 0: style = '<|-' elif head == 1: style = '-|>' elif head == 2: style = '<|-|>' else: raise KeyError( 'head parameter must be one of 0 (start), 1 (end) or 2 (both).' ) width = float(options['width']) arrowshorten_end = float(options.get('arrowshorten', 0)) / 2.0 arrowsize = float(options.get('arrowsize', 5)) head_width = arrowsize head_length = arrowsize * 2.0 color = to_mpl_color(options['rgbcolor']) from matplotlib.patches import FancyArrowPatch p = FancyArrowPatch( (self.xtail, self.ytail), (self.xhead, self.yhead), lw=width, arrowstyle='%s,head_width=%s,head_length=%s' % (style, head_width, head_length), shrinkA=arrowshorten_end, shrinkB=arrowshorten_end, fc=color, ec=color, linestyle=get_matplotlib_linestyle(options['linestyle'], return_type='long')) p.set_zorder(options['zorder']) p.set_label(options['legend_label']) if options['linestyle'] != 'solid': # The next few lines work around a design issue in matplotlib. # Currently, the specified linestyle is used to draw both the path # and the arrowhead. If linestyle is 'dashed', this looks really # odd. This code is from Jae-Joon Lee in response to a post to the # matplotlib mailing list. # See http://sourceforge.net/mailarchive/forum.php?thread_name=CAG%3DuJ%2Bnw2dE05P9TOXTz_zp-mGP3cY801vMH7yt6vgP9_WzU8w%40mail.gmail.com&forum_name=matplotlib-users import matplotlib.patheffects as pe class CheckNthSubPath(object): def __init__(self, patch, n): """ creates an callable object that returns True if the provided path is the n-th path from the patch. """ self._patch = patch self._n = n def get_paths(self, renderer): self._patch.set_dpi_cor(renderer.points_to_pixels(1.)) paths, fillables = self._patch.get_path_in_displaycoord() return paths def __call__(self, renderer, gc, tpath, affine, rgbFace): path = self.get_paths(renderer)[self._n] vert1, code1 = path.vertices, path.codes import numpy as np return np.array_equal(vert1, tpath.vertices) and np.array_equal( code1, tpath.codes) class ConditionalStroke(pe.RendererBase): def __init__(self, condition_func, pe_list): """ path effect that is only applied when the condition_func returns True. """ super(ConditionalStroke, self).__init__() self._pe_list = pe_list self._condition_func = condition_func def draw_path(self, renderer, gc, tpath, affine, rgbFace): if self._condition_func(renderer, gc, tpath, affine, rgbFace): for pe1 in self._pe_list: pe1.draw_path(renderer, gc, tpath, affine, rgbFace) pe1 = ConditionalStroke(CheckNthSubPath(p, 0), [pe.Stroke()]) pe2 = ConditionalStroke( CheckNthSubPath(p, 1), [pe.Stroke(dashes={ 'dash_offset': 0, 'dash_list': None })]) p.set_path_effects([pe1, pe2]) subplot.add_patch(p) return p
def contour_plot(f, xrange, yrange, **options): r""" ``contour_plot`` takes a function of two variables, `f(x,y)` and plots contour lines of the function over the specified ``xrange`` and ``yrange`` as demonstrated below. ``contour_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a function of two variables - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple ``(x,xmin,xmax)`` - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple ``(y,ymin,ymax)`` The following inputs must all be passed in as named parameters: - ``plot_points`` -- integer (default: 100); number of points to plot in each direction of the grid. For old computers, 25 is fine, but should not be used to verify specific intersection points. - ``fill`` -- bool (default: ``True``), whether to color in the area between contour lines - ``cmap`` -- a colormap (default: ``'gray'``), the name of a predefined colormap, a list of colors or an instance of a matplotlib Colormap. Type: ``import matplotlib.cm; matplotlib.cm.datad.keys()`` for available colormap names. - ``contours`` -- integer or list of numbers (default: ``None``): If a list of numbers is given, then this specifies the contour levels to use. If an integer is given, then this many contour lines are used, but the exact levels are determined automatically. If ``None`` is passed (or the option is not given), then the number of contour lines is determined automatically, and is usually about 5. - ``linewidths`` -- integer or list of integer (default: None), if a single integer all levels will be of the width given, otherwise the levels will be plotted with the width in the order given. If the list is shorter than the number of contours, then the widths will be repeated cyclically. - ``linestyles`` -- string or list of strings (default: None), the style of the lines to be plotted, one of: ``"solid"``, ``"dashed"``, ``"dashdot"``, ``"dotted"``, respectively ``"-"``, ``"--"``, ``"-."``, ``":"``. If the list is shorter than the number of contours, then the styles will be repeated cyclically. - ``labels`` -- boolean (default: False) Show level labels or not. The following options are to adjust the style and placement of labels, they have no effect if no labels are shown. - ``label_fontsize`` -- integer (default: 9), the font size of the labels. - ``label_colors`` -- string or sequence of colors (default: None) If a string, gives the name of a single color with which to draw all labels. If a sequence, gives the colors of the labels. A color is a string giving the name of one or a 3-tuple of floats. - ``label_inline`` -- boolean (default: False if fill is True, otherwise True), controls whether the underlying contour is removed or not. - ``label_inline_spacing`` -- integer (default: 3), When inline, this is the amount of contour that is removed from each side, in pixels. - ``label_fmt`` -- a format string (default: "%1.2f"), this is used to get the label text from the level. This can also be a dictionary with the contour levels as keys and corresponding text string labels as values. It can also be any callable which returns a string when called with a numeric contour level. - ``colorbar`` -- boolean (default: False) Show a colorbar or not. The following options are to adjust the style and placement of colorbars. They have no effect if a colorbar is not shown. - ``colorbar_orientation`` -- string (default: 'vertical'), controls placement of the colorbar, can be either 'vertical' or 'horizontal' - ``colorbar_format`` -- a format string, this is used to format the colorbar labels. - ``colorbar_spacing`` -- string (default: 'proportional'). If 'proportional', make the contour divisions proportional to values. If 'uniform', space the colorbar divisions uniformly, without regard for numeric values. - ``legend_label`` -- the label for this item in the legend - ``region`` - (default: None) If region is given, it must be a function of two variables. Only segments of the surface where region(x,y) returns a number >0 will be included in the plot. EXAMPLES: Here we plot a simple function of two variables. Note that since the input function is an expression, we need to explicitly declare the variables in 3-tuples for the range:: sage: x,y = var('x,y') sage: contour_plot(cos(x^2+y^2), (x, -4, 4), (y, -4, 4)) Here we change the ranges and add some options:: sage: x,y = var('x,y') sage: contour_plot((x^2)*cos(x*y), (x, -10, 5), (y, -5, 5), fill=False, plot_points=150) An even more complicated plot:: sage: x,y = var('x,y') sage: contour_plot(sin(x^2 + y^2)*cos(x)*sin(y), (x, -4, 4), (y, -4, 4),plot_points=150) Some elliptic curves, but with symbolic endpoints. In the first example, the plot is rotated 90 degrees because we switch the variables `x`, `y`:: sage: x,y = var('x,y') sage: contour_plot(y^2 + 1 - x^3 - x, (y,-pi,pi), (x,-pi,pi)) :: sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi)) We can play with the contour levels:: sage: x,y = var('x,y') sage: f(x,y) = x^2 + y^2 sage: contour_plot(f, (-2, 2), (-2, 2)) :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=2, cmap=[(1,0,0), (0,1,0), (0,0,1)]) :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=(0.1, 1.0, 1.2, 1.4), cmap='hsv') :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=(1.0,), fill=False) :: sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,0,1]) We can change the style of the lines:: sage: contour_plot(f, (-2,2), (-2,2), fill=False, linewidths=10) :: sage: contour_plot(f, (-2,2), (-2,2), fill=False, linestyles='dashdot') :: sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['solid','dashed'],fill=False) sage: P :: sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['solid','dashed']) sage: P sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['-',':']) sage: P We can add labels and play with them:: sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv', labels=True) :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',\ ... labels=True, label_fmt="%1.0f", label_colors='black') sage: P :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\ ... contours=[-4,0,4], label_fmt={-4:"low", 0:"medium", 4: "hi"}, label_colors='black') sage: P :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\ ... contours=[-4,0,4], label_fmt=lambda x: "$z=%s$"%x, label_colors='black', label_inline=True, \ ... label_fontsize=12) sage: P :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_fontsize=18) sage: P :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_inline_spacing=1) sage: P :: sage: P= contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_inline=False) sage: P We can change the color of the labels if so desired:: sage: contour_plot(f, (-2,2), (-2,2), labels=True, label_colors='red') We can add a colorbar as well:: sage: f(x,y)=x^2-y^2 sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True) :: sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True,colorbar_orientation='horizontal') :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True) :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True,colorbar_spacing='uniform') :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[0,2,3,6],colorbar=True,colorbar_format='%.3f') :: sage: contour_plot(f, (x,-3,3), (y,-3,3), labels=True,label_colors='red',contours=[0,2,3,6],colorbar=True) :: sage: contour_plot(f, (x,-3,3), (y,-3,3), cmap='winter', contours=20, fill=False, colorbar=True) This should plot concentric circles centered at the origin:: sage: x,y = var('x,y') sage: contour_plot(x^2+y^2-2,(x,-1,1), (y,-1,1)) Extra options will get passed on to show(), as long as they are valid:: sage: f(x, y) = cos(x) + sin(y) sage: contour_plot(f, (0, pi), (0, pi), axes=True) One can also plot over a reduced region:: sage: contour_plot(x**2-y**2, (x,-2, 2), (y,-2, 2),region=x-y,plot_points=300) :: sage: contour_plot(f, (0, pi), (0, pi)).show(axes=True) # These are equivalent Note that with ``fill=False`` and grayscale contours, there is the possibility of confusion between the contours and the axes, so use ``fill=False`` together with ``axes=True`` with caution:: sage: contour_plot(f, (-pi, pi), (-pi, pi), fill=False, axes=True) TESTS: To check that ticket 5221 is fixed, note that this has three curves, not two:: sage: x,y = var('x,y') sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,-2,0], fill=False) """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid region = options.pop('region') ev = [f] if region is None else [f,region] F, ranges = setup_for_eval_on_grid(ev, [xrange, yrange], options['plot_points']) g = F[0] xrange,yrange=[r[:2] for r in ranges] xy_data_array = [[g(x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] if region is not None: import numpy xy_data_array = numpy.ma.asarray(xy_data_array,dtype=float) m = F[1] mask = numpy.asarray([[m(x, y)<=0 for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)],dtype=bool) xy_data_array[mask] = numpy.ma.masked g = Graphics() # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'. # Otherwise matplotlib complains. scale = options.get('scale', None) if isinstance(scale, (list, tuple)): scale = scale[0] if scale == 'semilogy' or scale == 'semilogx': options['aspect_ratio'] = 'automatic' g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax'])) g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, options)) return g
def contour_plot(f, xrange, yrange, **options): r""" ``contour_plot`` takes a function of two variables, `f(x,y)` and plots contour lines of the function over the specified ``xrange`` and ``yrange`` as demonstrated below. ``contour_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a function of two variables - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple ``(x,xmin,xmax)`` - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple ``(y,ymin,ymax)`` The following inputs must all be passed in as named parameters: - ``plot_points`` -- integer (default: 100); number of points to plot in each direction of the grid. For old computers, 25 is fine, but should not be used to verify specific intersection points. - ``fill`` -- bool (default: ``True``), whether to color in the area between contour lines - ``cmap`` -- a colormap (default: ``'gray'``), the name of a predefined colormap, a list of colors or an instance of a matplotlib Colormap. Type: ``import matplotlib.cm; matplotlib.cm.datad.keys()`` for available colormap names. - ``contours`` -- integer or list of numbers (default: ``None``): If a list of numbers is given, then this specifies the contour levels to use. If an integer is given, then this many contour lines are used, but the exact levels are determined automatically. If ``None`` is passed (or the option is not given), then the number of contour lines is determined automatically, and is usually about 5. - ``linewidths`` -- integer or list of integer (default: None), if a single integer all levels will be of the width given, otherwise the levels will be plotted with the width in the order given. If the list is shorter than the number of contours, then the widths will be repeated cyclically. - ``linestyles`` -- string or list of strings (default: None), the style of the lines to be plotted, one of: ``"solid"``, ``"dashed"``, ``"dashdot"``, ``"dotted"``, respectively ``"-"``, ``"--"``, ``"-."``, ``":"``. If the list is shorter than the number of contours, then the styles will be repeated cyclically. - ``labels`` -- boolean (default: False) Show level labels or not. The following options are to adjust the style and placement of labels, they have no effect if no labels are shown. - ``label_fontsize`` -- integer (default: 9), the font size of the labels. - ``label_colors`` -- string or sequence of colors (default: None) If a string, gives the name of a single color with which to draw all labels. If a sequence, gives the colors of the labels. A color is a string giving the name of one or a 3-tuple of floats. - ``label_inline`` -- boolean (default: False if fill is True, otherwise True), controls whether the underlying contour is removed or not. - ``label_inline_spacing`` -- integer (default: 3), When inline, this is the amount of contour that is removed from each side, in pixels. - ``label_fmt`` -- a format string (default: "%1.2f"), this is used to get the label text from the level. This can also be a dictionary with the contour levels as keys and corresponding text string labels as values. It can also be any callable which returns a string when called with a numeric contour level. - ``colorbar`` -- boolean (default: False) Show a colorbar or not. The following options are to adjust the style and placement of colorbars. They have no effect if a colorbar is not shown. - ``colorbar_orientation`` -- string (default: 'vertical'), controls placement of the colorbar, can be either 'vertical' or 'horizontal' - ``colorbar_format`` -- a format string, this is used to format the colorbar labels. - ``colorbar_spacing`` -- string (default: 'proportional'). If 'proportional', make the contour divisions proportional to values. If 'uniform', space the colorbar divisions uniformly, without regard for numeric values. - ``legend_label`` -- the label for this item in the legend - ``region`` - (default: None) If region is given, it must be a function of two variables. Only segments of the surface where region(x,y) returns a number >0 will be included in the plot. EXAMPLES: Here we plot a simple function of two variables. Note that since the input function is an expression, we need to explicitly declare the variables in 3-tuples for the range:: sage: x,y = var('x,y') sage: contour_plot(cos(x^2+y^2), (x, -4, 4), (y, -4, 4)) Graphics object consisting of 1 graphics primitive Here we change the ranges and add some options:: sage: x,y = var('x,y') sage: contour_plot((x^2)*cos(x*y), (x, -10, 5), (y, -5, 5), fill=False, plot_points=150) Graphics object consisting of 1 graphics primitive An even more complicated plot:: sage: x,y = var('x,y') sage: contour_plot(sin(x^2 + y^2)*cos(x)*sin(y), (x, -4, 4), (y, -4, 4),plot_points=150) Graphics object consisting of 1 graphics primitive Some elliptic curves, but with symbolic endpoints. In the first example, the plot is rotated 90 degrees because we switch the variables `x`, `y`:: sage: x,y = var('x,y') sage: contour_plot(y^2 + 1 - x^3 - x, (y,-pi,pi), (x,-pi,pi)) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi)) Graphics object consisting of 1 graphics primitive We can play with the contour levels:: sage: x,y = var('x,y') sage: f(x,y) = x^2 + y^2 sage: contour_plot(f, (-2, 2), (-2, 2)) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=2, cmap=[(1,0,0), (0,1,0), (0,0,1)]) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=(0.1, 1.0, 1.2, 1.4), cmap='hsv') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2, 2), (-2, 2), contours=(1.0,), fill=False) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,0,1]) Graphics object consisting of 1 graphics primitive We can change the style of the lines:: sage: contour_plot(f, (-2,2), (-2,2), fill=False, linewidths=10) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (-2,2), (-2,2), fill=False, linestyles='dashdot') Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['solid','dashed'],fill=False) sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['solid','dashed']) sage: P Graphics object consisting of 1 graphics primitive sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\ ... linewidths=[1,5],linestyles=['-',':']) sage: P Graphics object consisting of 1 graphics primitive We can add labels and play with them:: sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv', labels=True) Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',\ ... labels=True, label_fmt="%1.0f", label_colors='black') sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\ ... contours=[-4,0,4], label_fmt={-4:"low", 0:"medium", 4: "hi"}, label_colors='black') sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\ ... contours=[-4,0,4], label_fmt=lambda x: "$z=%s$"%x, label_colors='black', label_inline=True, \ ... label_fontsize=12) sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_fontsize=18) sage: P Graphics object consisting of 1 graphics primitive :: sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_inline_spacing=1) sage: P Graphics object consisting of 1 graphics primitive :: sage: P= contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \ ... fill=False, cmap='hsv', labels=True, label_inline=False) sage: P Graphics object consisting of 1 graphics primitive We can change the color of the labels if so desired:: sage: contour_plot(f, (-2,2), (-2,2), labels=True, label_colors='red') Graphics object consisting of 1 graphics primitive We can add a colorbar as well:: sage: f(x,y)=x^2-y^2 sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True,colorbar_orientation='horizontal') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True,colorbar_spacing='uniform') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[0,2,3,6],colorbar=True,colorbar_format='%.3f') Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), labels=True,label_colors='red',contours=[0,2,3,6],colorbar=True) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (x,-3,3), (y,-3,3), cmap='winter', contours=20, fill=False, colorbar=True) Graphics object consisting of 1 graphics primitive This should plot concentric circles centered at the origin:: sage: x,y = var('x,y') sage: contour_plot(x^2+y^2-2,(x,-1,1), (y,-1,1)) Graphics object consisting of 1 graphics primitive Extra options will get passed on to show(), as long as they are valid:: sage: f(x, y) = cos(x) + sin(y) sage: contour_plot(f, (0, pi), (0, pi), axes=True) Graphics object consisting of 1 graphics primitive One can also plot over a reduced region:: sage: contour_plot(x**2-y**2, (x,-2, 2), (y,-2, 2),region=x-y,plot_points=300) Graphics object consisting of 1 graphics primitive :: sage: contour_plot(f, (0, pi), (0, pi)).show(axes=True) # These are equivalent Note that with ``fill=False`` and grayscale contours, there is the possibility of confusion between the contours and the axes, so use ``fill=False`` together with ``axes=True`` with caution:: sage: contour_plot(f, (-pi, pi), (-pi, pi), fill=False, axes=True) Graphics object consisting of 1 graphics primitive TESTS: To check that ticket 5221 is fixed, note that this has three curves, not two:: sage: x,y = var('x,y') sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,-2,0], fill=False) Graphics object consisting of 1 graphics primitive """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid region = options.pop('region') ev = [f] if region is None else [f, region] F, ranges = setup_for_eval_on_grid(ev, [xrange, yrange], options['plot_points']) g = F[0] xrange, yrange = [r[:2] for r in ranges] xy_data_array = [[ g(x, y) for x in xsrange(*ranges[0], include_endpoint=True) ] for y in xsrange(*ranges[1], include_endpoint=True)] if region is not None: import numpy xy_data_array = numpy.ma.asarray(xy_data_array, dtype=float) m = F[1] mask = numpy.asarray([[ m(x, y) <= 0 for x in xsrange(*ranges[0], include_endpoint=True) ] for y in xsrange(*ranges[1], include_endpoint=True)], dtype=bool) xy_data_array[mask] = numpy.ma.masked g = Graphics() # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'. # Otherwise matplotlib complains. scale = options.get('scale', None) if isinstance(scale, (list, tuple)): scale = scale[0] if scale == 'semilogy' or scale == 'semilogx': options['aspect_ratio'] = 'automatic' g._set_extra_kwds( Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax'])) g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, options)) return g
def _render_on_subplot(self, subplot): """ TESTS:: sage: matrix_plot(random_matrix(RDF, 50), cmap='jet') Graphics object consisting of 1 graphics primitive """ options = self.options() cmap = get_cmap(options.pop('cmap',None)) origin=options['origin'] norm=options['norm'] if norm=='value': import matplotlib norm=matplotlib.colors.NoNorm() if options['subdivisions']: subdiv_options=options['subdivision_options'] if isinstance(subdiv_options['boundaries'], (list, tuple)): rowsub,colsub=subdiv_options['boundaries'] else: rowsub=subdiv_options['boundaries'] colsub=subdiv_options['boundaries'] if isinstance(subdiv_options['style'], (list, tuple)): rowstyle,colstyle=subdiv_options['style'] else: rowstyle=subdiv_options['style'] colstyle=subdiv_options['style'] if rowstyle is None: rowstyle=dict() if colstyle is None: colstyle=dict() # Make line objects for subdivisions from line import line2d lim=self.get_minmax_data() # First draw horizontal lines representing row subdivisions for y in rowsub: l=line2d([(lim['xmin'],y-0.5), (lim['xmax'],y-0.5)], **rowstyle)[0] l._render_on_subplot(subplot) for x in colsub: l=line2d([(x-0.5, lim['ymin']), (x-0.5, lim['ymax'])], **colstyle)[0] l._render_on_subplot(subplot) if hasattr(self.xy_data_array, 'tocoo'): # Sparse matrix -- use spy opts=options.copy() for opt in ['vmin', 'vmax', 'norm', 'origin','subdivisions','subdivision_options', 'colorbar','colorbar_options']: del opts[opt] if origin=='lower': subplot.spy(self.xy_data_array.tocsr()[::-1], **opts) else: subplot.spy(self.xy_data_array, **opts) else: opts = dict(cmap=cmap, interpolation='nearest', aspect='equal', norm=norm, vmin=options['vmin'], vmax=options['vmax'], origin=origin,zorder=options.get('zorder',None)) image=subplot.imshow(self.xy_data_array, **opts) if options.get('colorbar', False): colorbar_options = options['colorbar_options'] from matplotlib import colorbar cax,kwds=colorbar.make_axes_gridspec(subplot,**colorbar_options) cb=colorbar.Colorbar(cax,image, **kwds) if origin=='upper': subplot.xaxis.tick_top() elif origin=='lower': subplot.xaxis.tick_bottom() subplot.xaxis.set_ticks_position('both') #only tick marks, not tick labels
def _render_on_subplot(self, subplot): """ TESTS:: sage: matrix_plot(random_matrix(RDF, 50), cmap='jet') """ options = self.options() cmap = get_cmap(options.pop('cmap', None)) origin = options['origin'] norm = options['norm'] if norm == 'value': import matplotlib norm = matplotlib.colors.NoNorm() if options['subdivisions']: subdiv_options = options['subdivision_options'] if isinstance(subdiv_options['boundaries'], (list, tuple)): rowsub, colsub = subdiv_options['boundaries'] else: rowsub = subdiv_options['boundaries'] colsub = subdiv_options['boundaries'] if isinstance(subdiv_options['style'], (list, tuple)): rowstyle, colstyle = subdiv_options['style'] else: rowstyle = subdiv_options['style'] colstyle = subdiv_options['style'] if rowstyle is None: rowstyle = dict() if colstyle is None: colstyle = dict() # Make line objects for subdivisions from line import line2d lim = self.get_minmax_data() # First draw horizontal lines representing row subdivisions for y in rowsub: l = line2d([(lim['xmin'], y - 0.5), (lim['xmax'], y - 0.5)], **rowstyle)[0] l._render_on_subplot(subplot) for x in colsub: l = line2d([(x - 0.5, lim['ymin']), (x - 0.5, lim['ymax'])], **colstyle)[0] l._render_on_subplot(subplot) if hasattr(self.xy_data_array, 'tocoo'): # Sparse matrix -- use spy opts = options.copy() for opt in [ 'vmin', 'vmax', 'norm', 'origin', 'subdivisions', 'subdivision_options', 'colorbar', 'colorbar_options' ]: del opts[opt] if origin == 'lower': subplot.spy(self.xy_data_array.tocsr()[::-1], **opts) else: subplot.spy(self.xy_data_array, **opts) else: opts = dict(cmap=cmap, interpolation='nearest', aspect='equal', norm=norm, vmin=options['vmin'], vmax=options['vmax'], origin=origin, zorder=options.get('zorder', None)) image = subplot.imshow(self.xy_data_array, **opts) if options.get('colorbar', False): colorbar_options = options['colorbar_options'] from matplotlib import colorbar cax, kwds = colorbar.make_axes_gridspec( subplot, **colorbar_options) cb = colorbar.Colorbar(cax, image, **kwds) if origin == 'upper': subplot.xaxis.tick_top() elif origin == 'lower': subplot.xaxis.tick_bottom() subplot.xaxis.set_ticks_position( 'both') #only tick marks, not tick labels
def _render_on_subplot(self, subplot): r""" Render this arrow in a subplot. This is the key function that defines how this arrow graphics primitive is rendered in matplotlib's library. EXAMPLES: This function implicitly ends up rendering this arrow on a matplotlib subplot:: sage: arrow((0,1), (2,-1)) Graphics object consisting of 1 graphics primitive TESTS: The length of the ends (shrinkA and shrinkB) should not depend on the width of the arrow, because Matplotlib already takes this into account. See :trac:`12836`:: sage: fig = Graphics().matplotlib() sage: sp = fig.add_subplot(1,1,1) sage: a = arrow((0,0), (1,1)) sage: b = arrow((0,0), (1,1), width=20) sage: p1 = a[0]._render_on_subplot(sp) sage: p2 = b[0]._render_on_subplot(sp) sage: p1.shrinkA == p2.shrinkA True sage: p1.shrinkB == p2.shrinkB True Dashed arrows should have solid arrowheads, :trac:`12852`. This test saves the plot of a dashed arrow to an EPS file. Within the EPS file, ``stroke`` will be called twice: once to draw the line, and again to draw the arrowhead. We check that both calls do not occur while the dashed line style is enabled:: sage: a = arrow((0,0), (1,1), linestyle='dashed') sage: filename = tmp_filename(ext='.eps') sage: a.save(filename=filename) sage: with open(filename, 'r') as f: ....: contents = f.read().replace('\n', ' ') sage: two_stroke_pattern = r'setdash.*stroke.*stroke.*setdash' sage: import re sage: two_stroke_re = re.compile(two_stroke_pattern) sage: two_stroke_re.search(contents) is None True """ from sage.plot.misc import get_matplotlib_linestyle options = self.options() head = options.pop('head') if head == 0: style = '<|-' elif head == 1: style = '-|>' elif head == 2: style = '<|-|>' else: raise KeyError( 'head parameter must be one of 0 (start), 1 (end) or 2 (both).' ) width = float(options['width']) arrowshorten_end = float(options.get('arrowshorten', 0)) / 2.0 arrowsize = float(options.get('arrowsize', 5)) head_width = arrowsize head_length = arrowsize * 2.0 color = to_mpl_color(options['rgbcolor']) from matplotlib.patches import FancyArrowPatch p = FancyArrowPatch((self.xtail, self.ytail), (self.xhead, self.yhead), lw=width, arrowstyle='%s,head_width=%s,head_length=%s' % (style, head_width, head_length), shrinkA=arrowshorten_end, shrinkB=arrowshorten_end, fc=color, ec=color) p.set_linestyle( get_matplotlib_linestyle(options['linestyle'], return_type='long')) p.set_zorder(options['zorder']) p.set_label(options['legend_label']) if options['linestyle'] != 'solid': # The next few lines work around a design issue in matplotlib. Currently, the specified # linestyle is used to draw both the path and the arrowhead. If linestyle is 'dashed', this # looks really odd. This code is from Jae-Joon Lee in response to a post to the matplotlib mailing # list. See http://sourceforge.net/mailarchive/forum.php?thread_name=CAG%3DuJ%2Bnw2dE05P9TOXTz_zp-mGP3cY801vMH7yt6vgP9_WzU8w%40mail.gmail.com&forum_name=matplotlib-users import matplotlib.patheffects as pe class CheckNthSubPath(object): def __init__(self, patch, n): """ creates an callable object that returns True if the provided path is the n-th path from the patch. """ self._patch = patch self._n = n def get_paths(self, renderer): self._patch.set_dpi_cor(renderer.points_to_pixels(1.)) paths, fillables = self._patch.get_path_in_displaycoord() return paths def __call__(self, renderer, gc, tpath, affine, rgbFace): path = self.get_paths(renderer)[self._n] vert1, code1 = path.vertices, path.codes import numpy as np return np.array_equal(vert1, tpath.vertices) and np.array_equal( code1, tpath.codes) class ConditionalStroke(pe.RendererBase): def __init__(self, condition_func, pe_list): """ path effect that is only applied when the condition_func returns True. """ super(ConditionalStroke, self).__init__() self._pe_list = pe_list self._condition_func = condition_func def draw_path(self, renderer, gc, tpath, affine, rgbFace): if self._condition_func(renderer, gc, tpath, affine, rgbFace): for pe1 in self._pe_list: pe1.draw_path(renderer, gc, tpath, affine, rgbFace) pe1 = ConditionalStroke(CheckNthSubPath(p, 0), [pe.Stroke()]) pe2 = ConditionalStroke(CheckNthSubPath(p, 1), [pe.Stroke(linestyle="solid")]) p.set_path_effects([pe1, pe2]) subplot.add_patch(p) return p
def _render_on_subplot(self, subplot): r""" Render this arrow in a subplot. This is the key function that defines how this arrow graphics primitive is rendered in matplotlib's library. EXAMPLES: This function implicitly ends up rendering this arrow on a matplotlib subplot:: sage: arrow((0,1), (2,-1)) TESTS: The length of the ends (shrinkA and shrinkB) should not depend on the width of the arrow, because Matplotlib already takes this into account. See :trac:`12836`:: sage: fig = Graphics().matplotlib() sage: sp = fig.add_subplot(1,1,1) sage: a = arrow((0,0), (1,1)) sage: b = arrow((0,0), (1,1), width=20) sage: p1 = a[0]._render_on_subplot(sp) sage: p2 = b[0]._render_on_subplot(sp) sage: p1.shrinkA == p2.shrinkA True sage: p1.shrinkB == p2.shrinkB True Dashed arrows should have solid arrowheads, :trac:`12852`. This test saves the plot of a dashed arrow to an EPS file. Within the EPS file, ``stroke`` will be called twice: once to draw the line, and again to draw the arrowhead. We check that both calls do not occur while the dashed line style is enabled:: sage: a = arrow((0,0), (1,1), linestyle='dashed') sage: filename = tmp_filename(ext='.eps') sage: a.save(filename=filename) sage: with open(filename, 'r') as f: ....: contents = f.read().replace('\n', ' ') sage: two_stroke_pattern = r'setdash.*stroke.*stroke.*setdash' sage: import re sage: two_stroke_re = re.compile(two_stroke_pattern) sage: two_stroke_re.search(contents) is None True """ options = self.options() head = options.pop('head') if head == 0: style = '<|-' elif head == 1: style = '-|>' elif head == 2: style = '<|-|>' else: raise KeyError('head parameter must be one of 0 (start), 1 (end) or 2 (both).') width = float(options['width']) arrowshorten_end = float(options.get('arrowshorten',0))/2.0 arrowsize = float(options.get('arrowsize',5)) head_width=arrowsize head_length=arrowsize*2.0 color = to_mpl_color(options['rgbcolor']) from matplotlib.patches import FancyArrowPatch p = FancyArrowPatch((self.xtail, self.ytail), (self.xhead, self.yhead), lw=width, arrowstyle='%s,head_width=%s,head_length=%s'%(style,head_width, head_length), shrinkA=arrowshorten_end, shrinkB=arrowshorten_end, fc=color, ec=color, linestyle=options['linestyle']) p.set_zorder(options['zorder']) p.set_label(options['legend_label']) if options['linestyle']!='solid': # The next few lines work around a design issue in matplotlib. Currently, the specified # linestyle is used to draw both the path and the arrowhead. If linestyle is 'dashed', this # looks really odd. This code is from Jae-Joon Lee in response to a post to the matplotlib mailing # list. See http://sourceforge.net/mailarchive/forum.php?thread_name=CAG%3DuJ%2Bnw2dE05P9TOXTz_zp-mGP3cY801vMH7yt6vgP9_WzU8w%40mail.gmail.com&forum_name=matplotlib-users import matplotlib.patheffects as pe class CheckNthSubPath(object): def __init__(self, patch, n): """ creates an callable object that returns True if the provided path is the n-th path from the patch. """ self._patch = patch self._n = n def get_paths(self, renderer): self._patch.set_dpi_cor(renderer.points_to_pixels(1.)) paths, fillables = self._patch.get_path_in_displaycoord() return paths def __call__(self, renderer, gc, tpath, affine, rgbFace): path = self.get_paths(renderer)[self._n] vert1, code1 = path.vertices, path.codes import numpy as np if np.all(vert1 == tpath.vertices) and np.all(code1 == tpath.codes): return True else: return False class ConditionalStroke(pe._Base): def __init__(self, condition_func, pe_list): """ path effect that is only applied when the condition_func returns True. """ super(ConditionalStroke, self).__init__() self._pe_list = pe_list self._condition_func = condition_func def draw_path(self, renderer, gc, tpath, affine, rgbFace): if self._condition_func(renderer, gc, tpath, affine, rgbFace): for pe1 in self._pe_list: pe1.draw_path(renderer, gc, tpath, affine, rgbFace) pe1 = ConditionalStroke(CheckNthSubPath(p, 0),[pe.Stroke()]) pe2 = ConditionalStroke(CheckNthSubPath(p, 1),[pe.Stroke(linestyle="solid")]) p.set_path_effects([pe1, pe2]) subplot.add_patch(p) return p
def implicit_plot(f, xrange, yrange, **options): r""" ``implicit_plot`` takes a function of two variables, `f(x,y)` and plots the curve `f(x,y) = 0` over the specified ``xrange`` and ``yrange`` as demonstrated below. ``implicit_plot(f, (xmin, xmax), (ymin, ymax), ...)`` ``implicit_plot(f, (x, xmin, xmax), (y, ymin, ymax), ...)`` INPUT: - ``f`` -- a function of two variables or equation in two variables - ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values or ``(x,xmin,xmax)`` - ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values or ``(y,ymin,ymax)`` The following inputs must all be passed in as named parameters: - ``plot_points`` -- integer (default: 150); number of points to plot in each direction of the grid - ``fill`` -- boolean (default: ``False``); if ``True``, fill the region `f(x,y) < 0`. - ``linewidth`` -- integer (default: None), if a single integer all levels will be of the width given, otherwise the levels will be plotted with the widths in the order given. - ``linestyle`` -- string (default: None), the style of the line to be plotted, one of: solid, dashed, dashdot or dotted. - ``color`` -- string (default: ``blue``), the color of the plot. Colors are defined in :mod:`sage.plot.colors`; try ``colors?`` to see them all. - ``legend_label`` -- the label for this item in the legend EXAMPLES: A simple circle with a radius of 2. Note that since the input function is an expression, we need to explicitly declare the variables in 3-tuples for the range:: sage: var("x y") (x, y) sage: implicit_plot(x^2+y^2-2, (x,-3,3), (y,-3,3)) I can do the same thing, but using a callable function so I don't need to explicitly define the variables in the ranges, and filling the inside:: sage: f(x,y) = x^2 + y^2 - 2 sage: implicit_plot(f, (-3, 3), (-3, 3),fill=True) The same circle but with a different line width:: sage: implicit_plot(f, (-3,3), (-3,3), linewidth=6) And again the same circle but this time with a dashdot border:: sage: implicit_plot(f, (-3,3), (-3,3), linestyle='dashdot') You can also plot an equation:: sage: var("x y") (x, y) sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3)) You can even change the color of the plot:: sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3), color="red") Here is a beautiful (and long) example which also tests that all colors work with this:: sage: G = Graphics() sage: counter = 0 sage: for col in colors.keys(): # long time ... G += implicit_plot(x^2+y^2==1+counter*.1, (x,-4,4),(y,-4,4),color=col) ... counter += 1 sage: G.show(frame=False) We can define a level-`n` approximation of the boundary of the Mandelbrot set:: sage: def mandel(n): ... c = polygen(CDF, 'c') ... z = 0 ... for i in range(n): ... z = z*z + c ... def f(x, y): ... val = z(CDF(x, y)) ... return val.norm() - 4 ... return f The first-level approximation is just a circle:: sage: implicit_plot(mandel(1), (-3, 3), (-3, 3)) A third-level approximation starts to get interesting:: sage: implicit_plot(mandel(3), (-2, 1), (-1.5, 1.5)) The seventh-level approximation is a degree 64 polynomial, and ``implicit_plot`` does a pretty good job on this part of the curve. (``plot_points=200`` looks even better, but it takes over a second.) :: sage: implicit_plot(mandel(7), (-0.3, 0.05), (-1.15, -0.9),plot_points=50) When making a filled implicit plot using a python function rather than a symbolic expression the user should increase the number of plot points to avoid artifacts:: sage: implicit_plot(lambda x,y: x^2+y^2-2, (x,-3,3), (y,-3,3), fill=True, plot_points=500) # long time TESTS:: sage: f(x,y) = x^2 + y^2 - 2 sage: implicit_plot(f, (-3, 3), (-3, 3),fill=5) Traceback (most recent call last): ... ValueError: fill=5 is not supported """ from sage.symbolic.expression import is_SymbolicEquation if is_SymbolicEquation(f): if f.operator() != operator.eq: raise ValueError, "input to implicit plot must be function or equation" f = f.lhs() - f.rhs() linewidths = options.pop('linewidth', None) linestyles = options.pop('linestyle', None) if 'color' in options: options['cmap']=[options.pop('color', None)] if options['fill'] is True: options.pop('fill') options.pop('contours',None) options.pop('cmap',None) from sage.symbolic.expression import is_Expression if not is_Expression(f): return region_plot(lambda x,y: f(x,y)<0, xrange, yrange, borderwidth=linewidths, borderstyle=linestyles, **options) else: return region_plot(f<0, xrange, yrange, borderwidth=linewidths, borderstyle=linestyles, **options) elif options['fill'] is False: return contour_plot(f, xrange, yrange, linewidths=linewidths, linestyles=linestyles, **options) else: raise ValueError("fill=%s is not supported" % options['fill'])