def region_plot(f, xrange, yrange, plot_points, incol, outcol, bordercol, borderstyle, borderwidth, **options): r""" ``region_plot`` takes a boolean function of two variables, `f(x,y)` and plots the region where f is True over the specified ``xrange`` and ``yrange`` as demonstrated below. ``region_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a boolean 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)`` - ``plot_points`` -- integer (default: 100); number of points to plot in each direction of the grid - ``incol`` -- a color (default: ``'blue'``), the color inside the region - ``outcol`` -- a color (default: ``'white'``), the color of the outside of the region If any of these options are specified, the border will be shown as indicated, otherwise it is only implicit (with color ``incol``) as the border of the inside of the region. - ``bordercol`` -- a color (default: ``None``), the color of the border (``'black'`` if ``borderwidth`` or ``borderstyle`` is specified but not ``bordercol``) - ``borderstyle`` -- string (default: 'solid'), one of ``'solid'``, ``'dashed'``, ``'dotted'``, ``'dashdot'``, respectively ``'-'``, ``'--'``, ``':'``, ``'-.'``. - ``borderwidth`` -- integer (default: None), the width of the border in pixels - ``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: Here we plot a simple function of two variables:: sage: x,y = var('x,y') sage: region_plot(cos(x^2+y^2) <= 0, (x, -3, 3), (y, -3, 3)) Graphics object consisting of 1 graphics primitive Here we play with the colors:: sage: region_plot(x^2+y^3 < 2, (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray') Graphics object consisting of 2 graphics primitives An even more complicated plot, with dashed borders:: sage: region_plot(sin(x)*sin(y) >= 1/4, (x,-10,10), (y,-10,10), incol='yellow', bordercol='black', borderstyle='dashed', plot_points=250) Graphics object consisting of 2 graphics primitives A disk centered at the origin:: sage: region_plot(x^2+y^2<1, (x,-1,1), (y,-1,1)) Graphics object consisting of 1 graphics primitive A plot with more than one condition (all conditions must be true for the statement to be true):: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2)) Graphics object consisting of 1 graphics primitive Since it doesn't look very good, let's increase ``plot_points``:: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2), plot_points=400) Graphics object consisting of 1 graphics primitive To get plots where only one condition needs to be true, use a function. Using lambda functions, we definitely need the extra ``plot_points``:: sage: region_plot(lambda x,y: x^2+y^2<1 or x<y, (x,-2,2), (y,-2,2), plot_points=400) Graphics object consisting of 1 graphics primitive The first quadrant of the unit circle:: sage: region_plot([y>0, x>0, x^2+y^2<1], (x,-1.1, 1.1), (y,-1.1, 1.1), plot_points = 400) Graphics object consisting of 1 graphics primitive Here is another plot, with a huge border:: sage: region_plot(x*(x-1)*(x+1)+y^2<0, (x, -3, 2), (y, -3, 3), incol='lightblue', bordercol='gray', borderwidth=10, plot_points=50) Graphics object consisting of 2 graphics primitives If we want to keep only the region where x is positive:: sage: region_plot([x*(x-1)*(x+1)+y^2<0, x>-1], (x, -3, 2), (y, -3, 3), incol='lightblue', plot_points=50) Graphics object consisting of 1 graphics primitive Here we have a cut circle:: sage: region_plot([x^2+y^2<4, x>-1], (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray', plot_points=200) Graphics object consisting of 2 graphics primitives The first variable range corresponds to the horizontal axis and the second variable range corresponds to the vertical axis:: sage: s,t=var('s,t') sage: region_plot(s>0,(t,-2,2),(s,-2,2)) Graphics object consisting of 1 graphics primitive :: sage: region_plot(s>0,(s,-2,2),(t,-2,2)) Graphics object consisting of 1 graphics primitive An example of a region plot in 'loglog' scale:: sage: region_plot(x^2+y^2<100, (x,1,10), (y,1,10), scale='loglog') Graphics object consisting of 1 graphics primitive """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid import numpy if not isinstance(f, (list, tuple)): f = [f] f = [equify(g) for g in f] g, ranges = setup_for_eval_on_grid(f, [xrange, yrange], plot_points) xrange, yrange = [r[:2] for r in ranges] xy_data_arrays = numpy.asarray( [[[func(x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] for func in g], dtype=float) xy_data_array = numpy.abs(xy_data_arrays.prod(axis=0)) # Now we need to set entries to negative iff all # functions were negative at that point. neg_indices = (xy_data_arrays < 0).all(axis=0) xy_data_array[neg_indices] = -xy_data_array[neg_indices] from matplotlib.colors import ListedColormap incol = rgbcolor(incol) outcol = rgbcolor(outcol) cmap = ListedColormap([incol, outcol]) cmap.set_over(outcol) cmap.set_under(incol) 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, dict(contours=[-1e307, 0, 1e307], cmap=cmap, fill=True, **options))) if bordercol or borderstyle or borderwidth: cmap = [rgbcolor(bordercol)] if bordercol else ['black'] linestyles = [borderstyle] if borderstyle else None linewidths = [borderwidth] if borderwidth else None g.add_primitive( ContourPlot( xy_data_array, xrange, yrange, dict(linestyles=linestyles, linewidths=linewidths, contours=[0], cmap=[bordercol], fill=False, **options))) return g
def region_plot(f, xrange, yrange, plot_points, incol, outcol, bordercol, borderstyle, borderwidth,**options): r""" ``region_plot`` takes a boolean function of two variables, `f(x,y)` and plots the region where f is True over the specified ``xrange`` and ``yrange`` as demonstrated below. ``region_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a boolean 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)`` - ``plot_points`` -- integer (default: 100); number of points to plot in each direction of the grid - ``incol`` -- a color (default: ``'blue'``), the color inside the region - ``outcol`` -- a color (default: ``'white'``), the color of the outside of the region If any of these options are specified, the border will be shown as indicated, otherwise it is only implicit (with color ``incol``) as the border of the inside of the region. - ``bordercol`` -- a color (default: ``None``), the color of the border (``'black'`` if ``borderwidth`` or ``borderstyle`` is specified but not ``bordercol``) - ``borderstyle`` -- string (default: 'solid'), one of 'solid', 'dashed', 'dotted', 'dashdot' - ``borderwidth`` -- integer (default: None), the width of the border in pixels - ``legend_label`` -- the label for this item in the legend EXAMPLES: Here we plot a simple function of two variables:: sage: x,y = var('x,y') sage: region_plot(cos(x^2+y^2) <= 0, (x, -3, 3), (y, -3, 3)) Here we play with the colors:: sage: region_plot(x^2+y^3 < 2, (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray') An even more complicated plot, with dashed borders:: sage: region_plot(sin(x)*sin(y) >= 1/4, (x,-10,10), (y,-10,10), incol='yellow', bordercol='black', borderstyle='dashed', plot_points=250) A disk centered at the origin:: sage: region_plot(x^2+y^2<1, (x,-1,1), (y,-1,1)) A plot with more than one condition (all conditions must be true for the statement to be true):: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2)) Since it doesn't look very good, let's increase ``plot_points``:: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2), plot_points=400) To get plots where only one condition needs to be true, use a function. Using lambda functions, we definitely need the extra ``plot_points``:: sage: region_plot(lambda x,y: x^2+y^2<1 or x<y, (x,-2,2), (y,-2,2), plot_points=400) The first quadrant of the unit circle:: sage: region_plot([y>0, x>0, x^2+y^2<1], (x,-1.1, 1.1), (y,-1.1, 1.1), plot_points = 400) Here is another plot, with a huge border:: sage: region_plot(x*(x-1)*(x+1)+y^2<0, (x, -3, 2), (y, -3, 3), incol='lightblue', bordercol='gray', borderwidth=10, plot_points=50) If we want to keep only the region where x is positive:: sage: region_plot([x*(x-1)*(x+1)+y^2<0, x>-1], (x, -3, 2), (y, -3, 3), incol='lightblue', plot_points=50) Here we have a cut circle:: sage: region_plot([x^2+y^2<4, x>-1], (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray', plot_points=200) The first variable range corresponds to the horizontal axis and the second variable range corresponds to the vertical axis:: sage: s,t=var('s,t') sage: region_plot(s>0,(t,-2,2),(s,-2,2)) :: sage: region_plot(s>0,(s,-2,2),(t,-2,2)) """ from sage.plot.plot import Graphics from sage.plot.misc import setup_for_eval_on_grid import numpy if not isinstance(f, (list, tuple)): f = [f] f = [equify(g) for g in f] g, ranges = setup_for_eval_on_grid(f, [xrange, yrange], plot_points) xrange,yrange=[r[:2] for r in ranges] xy_data_arrays = numpy.asarray([[[func(x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] for func in g],dtype=float) xy_data_array=numpy.abs(xy_data_arrays.prod(axis=0)) # Now we need to set entries to negative iff all # functions were negative at that point. neg_indices = (xy_data_arrays<0).all(axis=0) xy_data_array[neg_indices]=-xy_data_array[neg_indices] from matplotlib.colors import ListedColormap incol = rgbcolor(incol) outcol = rgbcolor(outcol) cmap = ListedColormap([incol, outcol]) cmap.set_over(outcol) cmap.set_under(incol) g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax'])) g.add_primitive(ContourPlot(xy_data_array, xrange,yrange, dict(contours=[-1e307, 0, 1e307], cmap=cmap, fill=True, **options))) if bordercol or borderstyle or borderwidth: cmap = [rgbcolor(bordercol)] if bordercol else ['black'] linestyles = [borderstyle] if borderstyle else None linewidths = [borderwidth] if borderwidth else None g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, dict(linestyles=linestyles, linewidths=linewidths, contours=[0], cmap=[bordercol], fill=False, **options))) return g
def set_vertices(self, **vertex_options): """ Sets the vertex plotting parameters for this GraphPlot. This function is called by the constructor but can also be called to make updates to the vertex options of an existing GraphPlot object. Note that the changes are cumulative. EXAMPLES:: sage: g = Graph({}, loops=True, multiedges=True, sparse=True) sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, edge_style='dashed') sage: GP.set_vertices(talk=True) sage: GP.plot() sage: GP.set_vertices(vertex_colors='pink', vertex_shape='^') sage: GP.plot() """ # Handle base vertex options voptions = {} for arg in vertex_options: self._options[arg] = vertex_options[arg] # First set defaults for styles vertex_colors = None if self._options['talk']: voptions['markersize'] = 500 if self._options['partition'] is None: vertex_colors = '#ffffff' else: voptions['markersize'] = self._options['vertex_size'] if 'vertex_colors' not in self._options or self._options['vertex_colors'] is None: if self._options['partition'] is not None: from sage.plot.colors import rainbow,rgbcolor partition = self._options['partition'] l = len(partition) R = rainbow(l) vertex_colors = {} for i in range(l): vertex_colors[R[i]] = partition[i] elif len(self._graph._boundary) != 0: vertex_colors = {} bdy_verts = [] int_verts = [] for v in self._graph.vertex_iterator(): if v in self._graph._boundary: bdy_verts.append(v) else: int_verts.append(v) vertex_colors['#fec7b8'] = int_verts vertex_colors['#b3e8ff'] = bdy_verts elif not vertex_colors: vertex_colors='#fec7b8' else: vertex_colors = self._options['vertex_colors'] if 'vertex_shape' in self._options: voptions['marker'] = self._options['vertex_shape'] if self._graph.is_directed(): self._vertex_radius = sqrt(voptions['markersize']/pi) self._arrowshorten = 2*self._vertex_radius if self._arcdigraph: self._vertex_radius = sqrt(voptions['markersize']/(20500*pi)) voptions['zorder'] = 7 if not isinstance(vertex_colors, dict): voptions['facecolor'] = vertex_colors if self._arcdigraph: self._plot_components['vertices'] = [circle(center, self._vertex_radius, fill=True, facecolor=vertex_colors, clip=False) for center in self._pos.values()] else: self._plot_components['vertices'] = scatter_plot( self._pos.values(), clip=False, **voptions) else: # Color list must be ordered: pos = [] colors = [] for i in vertex_colors: pos += [self._pos[j] for j in vertex_colors[i]] colors += [i]*len(vertex_colors[i]) # If all the vertices have not been assigned a color if len(self._pos)!=len(pos): from sage.plot.colors import rainbow,rgbcolor vertex_colors_rgb=[rgbcolor(c) for c in vertex_colors] for c in rainbow(len(vertex_colors)+1): if rgbcolor(c) not in vertex_colors_rgb: break leftovers=[j for j in self._pos.values() if j not in pos] pos+=leftovers colors+=[c]*len(leftovers) if self._arcdigraph: self._plot_components['vertices'] = [circle(pos[i], self._vertex_radius, fill=True, facecolor=colors[i], clip=False) for i in range(len(pos))] else: self._plot_components['vertices'] = scatter_plot(pos, facecolor=colors, clip=False, **voptions) if self._options['vertex_labels']: self._plot_components['vertex_labels'] = [] # TODO: allow text options for v in self._nodelist: self._plot_components['vertex_labels'].append(text(str(v), self._pos[v], rgbcolor=(0,0,0), zorder=8))
def set_vertices(self, **vertex_options): """ Sets the vertex plotting parameters for this GraphPlot. This function is called by the constructor but can also be called to make updates to the vertex options of an existing GraphPlot object. Note that the changes are cumulative. EXAMPLES:: sage: g = Graph({}, loops=True, multiedges=True, sparse=True) sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, edge_style='dashed') sage: GP.set_vertices(talk=True) sage: GP.plot() sage: GP.set_vertices(vertex_colors='pink', vertex_shape='^') sage: GP.plot() """ # Handle base vertex options voptions = {} for arg in vertex_options: self._options[arg] = vertex_options[arg] # First set defaults for styles vertex_colors = None if self._options['talk']: voptions['markersize'] = 500 if self._options['partition'] is None: vertex_colors = '#ffffff' else: voptions['markersize'] = self._options['vertex_size'] if 'vertex_colors' not in self._options: if self._options['partition'] is not None: from sage.plot.colors import rainbow, rgbcolor partition = self._options['partition'] l = len(partition) R = rainbow(l) vertex_colors = {} for i in range(l): vertex_colors[R[i]] = partition[i] elif len(self._graph._boundary) != 0: vertex_colors = {} bdy_verts = [] int_verts = [] for v in self._graph.vertex_iterator(): if v in self._graph._boundary: bdy_verts.append(v) else: int_verts.append(v) vertex_colors['#fec7b8'] = int_verts vertex_colors['#b3e8ff'] = bdy_verts elif not vertex_colors: vertex_colors = '#fec7b8' else: vertex_colors = self._options['vertex_colors'] if 'vertex_shape' in self._options: voptions['marker'] = self._options['vertex_shape'] if self._graph.is_directed(): self._vertex_radius = sqrt(voptions['markersize'] / pi) self._arrowshorten = 2 * self._vertex_radius if self._arcdigraph: self._vertex_radius = sqrt(voptions['markersize'] / (20500 * pi)) voptions['zorder'] = 7 if not isinstance(vertex_colors, dict): voptions['facecolor'] = vertex_colors if self._arcdigraph: self._plot_components['vertices'] = [ circle(center, self._vertex_radius, fill=True, facecolor=vertex_colors, clip=False) for center in self._pos.values() ] else: self._plot_components['vertices'] = scatter_plot( self._pos.values(), clip=False, **voptions) else: # Color list must be ordered: pos = [] colors = [] for i in vertex_colors: pos += [self._pos[j] for j in vertex_colors[i]] colors += [i] * len(vertex_colors[i]) # If all the vertices have not been assigned a color if len(self._pos) != len(pos): from sage.plot.colors import rainbow, rgbcolor vertex_colors_rgb = [rgbcolor(c) for c in vertex_colors] for c in rainbow(len(vertex_colors) + 1): if rgbcolor(c) not in vertex_colors_rgb: break leftovers = [j for j in self._pos.values() if j not in pos] pos += leftovers colors += [c] * len(leftovers) if self._arcdigraph: self._plot_components['vertices'] = [ circle(pos[i], self._vertex_radius, fill=True, facecolor=colors[i], clip=False) for i in range(len(pos)) ] else: self._plot_components['vertices'] = scatter_plot( pos, facecolor=colors, clip=False, **voptions) if self._options['vertex_labels']: self._plot_components['vertex_labels'] = [] # TODO: allow text options for v in self._nodelist: self._plot_components['vertex_labels'].append( text(str(v), self._pos[v], rgbcolor=(0, 0, 0), zorder=8))
def region_plot(f, xrange, yrange, plot_points, incol, outcol, bordercol, borderstyle, borderwidth, alpha, **options): r""" ``region_plot`` takes a boolean function of two variables, `f(x,y)` and plots the region where f is True over the specified ``xrange`` and ``yrange`` as demonstrated below. ``region_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a boolean function or a list of boolean functions 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)`` - ``plot_points`` -- integer (default: 100); number of points to plot in each direction of the grid - ``incol`` -- a color (default: ``'blue'``), the color inside the region - ``outcol`` -- a color (default: ``None``), the color of the outside of the region If any of these options are specified, the border will be shown as indicated, otherwise it is only implicit (with color ``incol``) as the border of the inside of the region. - ``bordercol`` -- a color (default: ``None``), the color of the border (``'black'`` if ``borderwidth`` or ``borderstyle`` is specified but not ``bordercol``) - ``borderstyle`` -- string (default: 'solid'), one of ``'solid'``, ``'dashed'``, ``'dotted'``, ``'dashdot'``, respectively ``'-'``, ``'--'``, ``':'``, ``'-.'``. - ``borderwidth`` -- integer (default: None), the width of the border in pixels - ``alpha`` -- (default: 1) How transparent the fill is. A number between 0 and 1. - ``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: Here we plot a simple function of two variables:: sage: x,y = var('x,y') sage: region_plot(cos(x^2+y^2) <= 0, (x, -3, 3), (y, -3, 3)) Graphics object consisting of 1 graphics primitive Here we play with the colors:: sage: region_plot(x^2+y^3 < 2, (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray') Graphics object consisting of 2 graphics primitives An even more complicated plot, with dashed borders:: sage: region_plot(sin(x)*sin(y) >= 1/4, (x,-10,10), (y,-10,10), incol='yellow', bordercol='black', borderstyle='dashed', plot_points=250) Graphics object consisting of 2 graphics primitives A disk centered at the origin:: sage: region_plot(x^2+y^2<1, (x,-1,1), (y,-1,1)) Graphics object consisting of 1 graphics primitive A plot with more than one condition (all conditions must be true for the statement to be true):: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2)) Graphics object consisting of 1 graphics primitive Since it doesn't look very good, let's increase ``plot_points``:: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2), plot_points=400) Graphics object consisting of 1 graphics primitive To get plots where only one condition needs to be true, use a function. Using lambda functions, we definitely need the extra ``plot_points``:: sage: region_plot(lambda x,y: x^2+y^2<1 or x<y, (x,-2,2), (y,-2,2), plot_points=400) Graphics object consisting of 1 graphics primitive The first quadrant of the unit circle:: sage: region_plot([y>0, x>0, x^2+y^2<1], (x,-1.1, 1.1), (y,-1.1, 1.1), plot_points = 400) Graphics object consisting of 1 graphics primitive Here is another plot, with a huge border:: sage: region_plot(x*(x-1)*(x+1)+y^2<0, (x, -3, 2), (y, -3, 3), incol='lightblue', bordercol='gray', borderwidth=10, plot_points=50) Graphics object consisting of 2 graphics primitives If we want to keep only the region where x is positive:: sage: region_plot([x*(x-1)*(x+1)+y^2<0, x>-1], (x, -3, 2), (y, -3, 3), incol='lightblue', plot_points=50) Graphics object consisting of 1 graphics primitive Here we have a cut circle:: sage: region_plot([x^2+y^2<4, x>-1], (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray', plot_points=200) Graphics object consisting of 2 graphics primitives The first variable range corresponds to the horizontal axis and the second variable range corresponds to the vertical axis:: sage: s,t=var('s,t') sage: region_plot(s>0,(t,-2,2),(s,-2,2)) Graphics object consisting of 1 graphics primitive :: sage: region_plot(s>0,(s,-2,2),(t,-2,2)) Graphics object consisting of 1 graphics primitive An example of a region plot in 'loglog' scale:: sage: region_plot(x^2+y^2<100, (x,1,10), (y,1,10), scale='loglog') Graphics object consisting of 1 graphics primitive TESTS: To check that :trac:`16907` is fixed:: sage: x, y = var('x, y') sage: disc1 = region_plot(x^2+y^2 < 1, (x, -1, 1), (y, -1, 1), alpha=0.5) sage: disc2 = region_plot((x-0.7)^2+(y-0.7)^2 < 0.5, (x, -2, 2), (y, -2, 2), incol='red', alpha=0.5) sage: disc1 + disc2 Graphics object consisting of 2 graphics primitives To check that :trac:`18286` is fixed:: sage: x, y = var('x, y') sage: region_plot([x == 0], (x, -1, 1), (y, -1, 1)) Graphics object consisting of 1 graphics primitive sage: region_plot([x^2+y^2==1, x<y], (x, -1, 1), (y, -1, 1)) Graphics object consisting of 1 graphics primitive """ from sage.plot.all import Graphics from sage.plot.misc import setup_for_eval_on_grid from sage.symbolic.expression import is_Expression from warnings import warn import numpy if not isinstance(f, (list, tuple)): f = [f] feqs = [equify(g) for g in f if is_Expression(g) and g.operator() is operator.eq and not equify(g).is_zero()] f = [equify(g) for g in f if not (is_Expression(g) and g.operator() is operator.eq)] neqs = len(feqs) if neqs > 1: warn("There are at least 2 equations; If the region is degenerated to points, plotting might show nothing.") feqs = [sum([fn**2 for fn in feqs])] neqs = 1 if neqs and not bordercol: bordercol = incol if not f: return implicit_plot(feqs[0], xrange, yrange, plot_points=plot_points, fill=False, \ linewidth=borderwidth, linestyle=borderstyle, color=bordercol, **options) f_all, ranges = setup_for_eval_on_grid(feqs + f, [xrange, yrange], plot_points) xrange,yrange=[r[:2] for r in ranges] xy_data_arrays = numpy.asarray([[[func(x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] for func in f_all[neqs::]],dtype=float) xy_data_array=numpy.abs(xy_data_arrays.prod(axis=0)) # Now we need to set entries to negative iff all # functions were negative at that point. neg_indices = (xy_data_arrays<0).all(axis=0) xy_data_array[neg_indices]=-xy_data_array[neg_indices] from matplotlib.colors import ListedColormap incol = rgbcolor(incol) if outcol: outcol = rgbcolor(outcol) cmap = ListedColormap([incol, outcol]) cmap.set_over(outcol, alpha=alpha) else: outcol = rgbcolor('white') cmap = ListedColormap([incol, outcol]) cmap.set_over(outcol, alpha=0) cmap.set_under(incol, alpha=alpha) 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'])) if neqs == 0: g.add_primitive(ContourPlot(xy_data_array, xrange,yrange, dict(contours=[-1e-20, 0, 1e-20], cmap=cmap, fill=True, **options))) else: mask = numpy.asarray([[elt > 0 for elt in rows] for rows in xy_data_array], dtype=bool) xy_data_array = numpy.asarray([[f_all[0](x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)], dtype=float) xy_data_array[mask] = None if bordercol or borderstyle or borderwidth: cmap = [rgbcolor(bordercol)] if bordercol else ['black'] linestyles = [borderstyle] if borderstyle else None linewidths = [borderwidth] if borderwidth else None g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, dict(linestyles=linestyles, linewidths=linewidths, contours=[0], cmap=[bordercol], fill=False, **options))) return g
def region_plot(f, xrange, yrange, plot_points, incol, outcol, bordercol, borderstyle, borderwidth,**options): r""" ``region_plot`` takes a boolean function of two variables, `f(x,y)` and plots the region where f is True over the specified ``xrange`` and ``yrange`` as demonstrated below. ``region_plot(f, (xmin, xmax), (ymin, ymax), ...)`` INPUT: - ``f`` -- a boolean 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)`` - ``plot_points`` -- integer (default: 100); number of points to plot in each direction of the grid - ``incol`` -- a color (default: ``'blue'``), the color inside the region - ``outcol`` -- a color (default: ``'white'``), the color of the outside of the region If any of these options are specified, the border will be shown as indicated, otherwise it is only implicit (with color ``incol``) as the border of the inside of the region. - ``bordercol`` -- a color (default: ``None``), the color of the border (``'black'`` if ``borderwidth`` or ``borderstyle`` is specified but not ``bordercol``) - ``borderstyle`` -- string (default: 'solid'), one of 'solid', 'dashed', 'dotted', 'dashdot' - ``borderwidth`` -- integer (default: None), the width of the border in pixels - ``legend_label`` -- the label for this item in the legend EXAMPLES: Here we plot a simple function of two variables:: sage: x,y = var('x,y') sage: region_plot(cos(x^2+y^2) <= 0, (x, -3, 3), (y, -3, 3)) Here we play with the colors:: sage: region_plot(x^2+y^3 < 2, (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray') An even more complicated plot, with dashed borders:: sage: region_plot(sin(x)*sin(y) >= 1/4, (x,-10,10), (y,-10,10), incol='yellow', bordercol='black', borderstyle='dashed', plot_points=250) A disk centered at the origin:: sage: region_plot(x^2+y^2<1, (x,-1,1), (y,-1,1)) A plot with more than one condition (all conditions must be true for the statement to be true):: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2)) Since it doesn't look very good, let's increase plot_points:: sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2), plot_points=400) To get plots where only one condition needs to be true, use a function:: sage: region_plot(lambda x,y: x^2+y^2<1 or x<y, (x,-2,2), (y,-2,2)) The first quadrant of the unit circle:: sage: region_plot([y>0, x>0, x^2+y^2<1], (x,-1.1, 1.1), (y,-1.1, 1.1), plot_points = 400) Here is another plot, with a huge border:: sage: region_plot(x*(x-1)*(x+1)+y^2<0, (x, -3, 2), (y, -3, 3), incol='lightblue', bordercol='gray', borderwidth=10, plot_points=50) If we want to keep only the region where x is positive:: sage: region_plot([x*(x-1)*(x+1)+y^2<0, x>-1], (x, -3, 2), (y, -3, 3), incol='lightblue', plot_points=50) Here we have a cut circle:: sage: region_plot([x^2+y^2<4, x>-1], (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray', plot_points=200) The first variable range corresponds to the horizontal axis and the second variable range corresponds to the vertical axis:: sage: s,t=var('s,t') sage: region_plot(s>0,(t,-2,2),(s,-2,2)) :: sage: region_plot(s>0,(s,-2,2),(t,-2,2)) """ from sage.plot.plot import Graphics from sage.plot.misc import setup_for_eval_on_grid import numpy if not isinstance(f, (list, tuple)): f = [f] f = [equify(g) for g in f] g, ranges = setup_for_eval_on_grid(f, [xrange, yrange], plot_points) xrange,yrange=[r[:2] for r in ranges] xy_data_arrays = numpy.asarray([[[func(x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] for func in g],dtype=float) xy_data_array=numpy.abs(xy_data_arrays.prod(axis=0)) # Now we need to set entries to negative iff all # functions were negative at that point. neg_indices = (xy_data_arrays<0).all(axis=0) xy_data_array[neg_indices]=-xy_data_array[neg_indices] from matplotlib.colors import ListedColormap incol = rgbcolor(incol) outcol = rgbcolor(outcol) cmap = ListedColormap([incol, outcol]) cmap.set_over(outcol) cmap.set_under(incol) g = Graphics() g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax'])) g.add_primitive(ContourPlot(xy_data_array, xrange,yrange, dict(contours=[-1e307, 0, 1e307], cmap=cmap, fill=True, **options))) if bordercol or borderstyle or borderwidth: cmap = [rgbcolor(bordercol)] if bordercol else ['black'] linestyles = [borderstyle] if borderstyle else None linewidths = [borderwidth] if borderwidth else None g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, dict(linestyles=linestyles, linewidths=linewidths, contours=[0], cmap=[bordercol], fill=False, **options))) return g