Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/plot/contour_plot.py
4034 views
1
"""
2
Contour Plots
3
"""
4
5
#*****************************************************************************
6
# Copyright (C) 2006 Alex Clemesha <[email protected]>,
7
# William Stein <[email protected]>,
8
# 2008 Mike Hansen <[email protected]>,
9
#
10
# Distributed under the terms of the GNU General Public License (GPL)
11
#
12
# This code is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
# General Public License for more details.
16
#
17
# The full text of the GPL is available at:
18
#
19
# http://www.gnu.org/licenses/
20
#*****************************************************************************
21
from sage.plot.primitive import GraphicPrimitive
22
from sage.misc.decorators import options, suboptions
23
from sage.plot.colors import rgbcolor, get_cmap
24
from sage.misc.misc import xsrange
25
import operator
26
27
class ContourPlot(GraphicPrimitive):
28
"""
29
Primitive class for the contour plot graphics type. See
30
``contour_plot?`` for help actually doing contour plots.
31
32
INPUT:
33
34
- ``xy_data_array`` - list of lists giving evaluated values of the function on the grid
35
36
- ``xrange`` - tuple of 2 floats indicating range for horizontal direction
37
38
- ``yrange`` - tuple of 2 floats indicating range for vertical direction
39
40
- ``options`` - dict of valid plot options to pass to constructor
41
42
EXAMPLES:
43
44
Note this should normally be used indirectly via ``contour_plot``::
45
46
sage: from sage.plot.contour_plot import ContourPlot
47
sage: C = ContourPlot([[1,3],[2,4]],(1,2),(2,3),options={})
48
sage: C
49
ContourPlot defined by a 2 x 2 data grid
50
sage: C.xrange
51
(1, 2)
52
53
TESTS:
54
55
We test creating a contour plot::
56
57
sage: x,y = var('x,y')
58
sage: contour_plot(x^2-y^3+10*sin(x*y), (x, -4, 4), (y, -4, 4),plot_points=121,cmap='hsv')
59
"""
60
def __init__(self, xy_data_array, xrange, yrange, options):
61
"""
62
Initializes base class ContourPlot.
63
64
EXAMPLES::
65
66
sage: x,y = var('x,y')
67
sage: C = contour_plot(x^2-y^3+10*sin(x*y), (x, -4, 4), (y, -4, 4),plot_points=121,cmap='hsv')
68
sage: C[0].xrange
69
(-4.0, 4.0)
70
sage: C[0].options()['plot_points']
71
121
72
"""
73
self.xrange = xrange
74
self.yrange = yrange
75
self.xy_data_array = xy_data_array
76
self.xy_array_row = len(xy_data_array)
77
self.xy_array_col = len(xy_data_array[0])
78
GraphicPrimitive.__init__(self, options)
79
80
def get_minmax_data(self):
81
"""
82
Returns a dictionary with the bounding box data.
83
84
EXAMPLES::
85
86
sage: x,y = var('x,y')
87
sage: f(x,y) = x^2 + y^2
88
sage: d = contour_plot(f, (3, 6), (3, 6))[0].get_minmax_data()
89
sage: d['xmin']
90
3.0
91
sage: d['ymin']
92
3.0
93
"""
94
from sage.plot.plot import minmax_data
95
return minmax_data(self.xrange, self.yrange, dict=True)
96
97
def _allowed_options(self):
98
"""
99
Return the allowed options for the ContourPlot class.
100
101
EXAMPLES::
102
103
sage: x,y = var('x,y')
104
sage: C = contour_plot(x^2-y^2,(x,-2,2),(y,-2,2))
105
sage: isinstance(C[0]._allowed_options(),dict)
106
True
107
"""
108
return {'plot_points':'How many points to use for plotting precision',
109
'cmap':"""the name of a predefined colormap,
110
a list of colors, or an instance of a
111
matplotlib Colormap. Type: import matplotlib.cm; matplotlib.cm.datad.keys()
112
for available colormap names.""",
113
'colorbar': "Include a colorbar indicating the levels",
114
'colorbar_options': "a dictionary of options for colorbars",
115
'fill':'Fill contours or not',
116
'legend_label':'The label for this item in the legend.',
117
'contours':"""Either an integer specifying the number of
118
contour levels, or a sequence of numbers giving
119
the actual contours to use.""",
120
'linewidths':'the width of the lines to be plotted',
121
'linestyles':'the style of the lines to be plotted',
122
'labels':'show line labels or not',
123
'label_options':'a dictionary of options for the labels',
124
'zorder':'The layer level in which to draw'}
125
126
def _repr_(self):
127
"""
128
String representation of ContourPlot primitive.
129
130
EXAMPLES::
131
132
sage: x,y = var('x,y')
133
sage: C = contour_plot(x^2-y^2,(x,-2,2),(y,-2,2))
134
sage: c = C[0]; c
135
ContourPlot defined by a 100 x 100 data grid
136
"""
137
return "ContourPlot defined by a %s x %s data grid"%(self.xy_array_row, self.xy_array_col)
138
139
def _render_on_subplot(self, subplot):
140
"""
141
TESTS:
142
143
A somewhat random plot, but fun to look at::
144
145
sage: x,y = var('x,y')
146
sage: contour_plot(x^2-y^3+10*sin(x*y), (x, -4, 4), (y, -4, 4),plot_points=121,cmap='hsv')
147
"""
148
from sage.rings.integer import Integer
149
options = self.options()
150
fill = options['fill']
151
contours = options['contours']
152
if options.has_key('cmap'):
153
cmap = get_cmap(options['cmap'])
154
elif fill or contours is None:
155
cmap = get_cmap('gray')
156
else:
157
if isinstance(contours, (int, Integer)):
158
cmap = get_cmap([(i,i,i) for i in xsrange(0,1,1/contours)])
159
else:
160
l = Integer(len(contours))
161
cmap = get_cmap([(i,i,i) for i in xsrange(0,1,1/l)])
162
163
x0,x1 = float(self.xrange[0]), float(self.xrange[1])
164
y0,y1 = float(self.yrange[0]), float(self.yrange[1])
165
166
if isinstance(contours, (int, Integer)):
167
contours = int(contours)
168
169
CSF=None
170
if fill:
171
if contours is None:
172
CSF=subplot.contourf(self.xy_data_array, cmap=cmap, extent=(x0,x1,y0,y1), label=options['legend_label'])
173
else:
174
CSF=subplot.contourf(self.xy_data_array, contours, cmap=cmap, extent=(x0,x1,y0,y1),extend='both', label=options['legend_label'])
175
176
linewidths = options.get('linewidths',None)
177
if isinstance(linewidths, (int, Integer)):
178
linewidths = int(linewidths)
179
elif isinstance(linewidths, (list, tuple)):
180
linewidths = tuple(int(x) for x in linewidths)
181
linestyles = options.get('linestyles',None)
182
if contours is None:
183
CS = subplot.contour(self.xy_data_array, cmap=cmap, extent=(x0,x1,y0,y1),
184
linewidths=linewidths, linestyles=linestyles, label=options['legend_label'])
185
else:
186
CS = subplot.contour(self.xy_data_array, contours, cmap=cmap, extent=(x0,x1,y0,y1),
187
linewidths=linewidths, linestyles=linestyles, label=options['legend_label'])
188
if options.get('labels', False):
189
label_options = options['label_options']
190
label_options['fontsize'] = int(label_options['fontsize'])
191
if fill and label_options is None:
192
label_options['inline']=False
193
subplot.clabel(CS, **label_options)
194
if options.get('colorbar', False):
195
colorbar_options = options['colorbar_options']
196
from matplotlib import colorbar
197
cax,kwds=colorbar.make_axes_gridspec(subplot,**colorbar_options)
198
if CSF is None:
199
cb=colorbar.Colorbar(cax,CS, **kwds)
200
else:
201
cb=colorbar.Colorbar(cax,CSF, **kwds)
202
cb.add_lines(CS)
203
204
@suboptions('colorbar', orientation='vertical', format=None, spacing=None)
205
@suboptions('label', fontsize=9, colors='blue', inline=None, inline_spacing=3, fmt="%1.2f")
206
@options(plot_points=100, fill=True, contours=None, linewidths=None, linestyles=None, labels=False, frame=True, axes=False, colorbar=False, legend_label=None, aspect_ratio=1, region=None)
207
def contour_plot(f, xrange, yrange, **options):
208
r"""
209
``contour_plot`` takes a function of two variables, `f(x,y)`
210
and plots contour lines of the function over the specified
211
``xrange`` and ``yrange`` as demonstrated below.
212
213
``contour_plot(f, (xmin, xmax), (ymin, ymax), ...)``
214
215
INPUT:
216
217
- ``f`` -- a function of two variables
218
219
- ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple
220
``(x,xmin,xmax)``
221
222
- ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple
223
``(y,ymin,ymax)``
224
225
The following inputs must all be passed in as named parameters:
226
227
- ``plot_points`` -- integer (default: 100); number of points to plot
228
in each direction of the grid. For old computers, 25 is fine, but
229
should not be used to verify specific intersection points.
230
231
- ``fill`` -- bool (default: ``True``), whether to color in the area
232
between contour lines
233
234
- ``cmap`` -- a colormap (default: ``'gray'``), the name of
235
a predefined colormap, a list of colors or an instance of a matplotlib
236
Colormap. Type: ``import matplotlib.cm; matplotlib.cm.datad.keys()``
237
for available colormap names.
238
239
- ``contours`` -- integer or list of numbers (default: ``None``):
240
If a list of numbers is given, then this specifies the contour levels
241
to use. If an integer is given, then this many contour lines are
242
used, but the exact levels are determined automatically. If ``None``
243
is passed (or the option is not given), then the number of contour
244
lines is determined automatically, and is usually about 5.
245
246
- ``linewidths`` -- integer or list of integer (default: None), if
247
a single integer all levels will be of the width given,
248
otherwise the levels will be plotted with the width in the order
249
given. If the list is shorter than the number of contours, then
250
the widths will be repeated cyclically.
251
252
- ``linestyles`` -- string or list of strings (default: None), the
253
style of the lines to be plotted, one of: solid, dashed,
254
dashdot, or dotted. If the list is shorter than the number of
255
contours, then the styles will be repeated cyclically.
256
257
- ``labels`` -- boolean (default: False) Show level labels or not.
258
259
The following options are to adjust the style and placement of
260
labels, they have no effect if no labels are shown.
261
262
- ``label_fontsize`` -- integer (default: 9), the font size of the labels.
263
264
- ``label_colors`` -- string or sequence of colors (default:
265
None) If a string, gives the name of a single color with which
266
to draw all labels. If a sequence, gives the colors of the
267
labels. A color is a string giving the name of one or a
268
3-tuple of floats.
269
270
- ``label_inline`` -- boolean (default: False if fill is True,
271
otherwise True), controls whether the underlying contour is
272
removed or not.
273
274
- ``label_inline_spacing`` -- integer (default: 3), When inline,
275
this is the amount of contour that is removed from each side,
276
in pixels.
277
278
- ``label_fmt`` -- a format string (default: "%1.2f"), this is
279
used to get the label text from the level. This can also be a
280
dictionary with the contour levels as keys and corresponding
281
text string labels as values. It can also be any callable which
282
returns a string when called with a numeric contour level.
283
284
- ``colorbar`` -- boolean (default: False) Show a colorbar or not.
285
286
The following options are to adjust the style and placement of
287
colorbars. They have no effect if a colorbar is not shown.
288
289
- ``colorbar_orientation`` -- string (default: 'vertical'),
290
controls placement of the colorbar, can be either 'vertical'
291
or 'horizontal'
292
293
- ``colorbar_format`` -- a format string, this is used to format
294
the colorbar labels.
295
296
- ``colorbar_spacing`` -- string (default: 'proportional'). If
297
'proportional', make the contour divisions proportional to
298
values. If 'uniform', space the colorbar divisions uniformly,
299
without regard for numeric values.
300
301
- ``legend_label`` -- the label for this item in the legend
302
303
- ``region`` - (default: None) If region is given, it must be a function
304
of two variables. Only segments of the surface where region(x,y) returns a
305
number >0 will be included in the plot.
306
307
EXAMPLES:
308
309
Here we plot a simple function of two variables. Note that
310
since the input function is an expression, we need to explicitly
311
declare the variables in 3-tuples for the range::
312
313
sage: x,y = var('x,y')
314
sage: contour_plot(cos(x^2+y^2), (x, -4, 4), (y, -4, 4))
315
316
Here we change the ranges and add some options::
317
318
sage: x,y = var('x,y')
319
sage: contour_plot((x^2)*cos(x*y), (x, -10, 5), (y, -5, 5), fill=False, plot_points=150)
320
321
An even more complicated plot::
322
323
sage: x,y = var('x,y')
324
sage: contour_plot(sin(x^2 + y^2)*cos(x)*sin(y), (x, -4, 4), (y, -4, 4),plot_points=150)
325
326
Some elliptic curves, but with symbolic endpoints. In the first
327
example, the plot is rotated 90 degrees because we switch the
328
variables `x`, `y`::
329
330
sage: x,y = var('x,y')
331
sage: contour_plot(y^2 + 1 - x^3 - x, (y,-pi,pi), (x,-pi,pi))
332
333
::
334
335
sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi))
336
337
We can play with the contour levels::
338
339
sage: x,y = var('x,y')
340
sage: f(x,y) = x^2 + y^2
341
sage: contour_plot(f, (-2, 2), (-2, 2))
342
343
::
344
345
sage: contour_plot(f, (-2, 2), (-2, 2), contours=2, cmap=[(1,0,0), (0,1,0), (0,0,1)])
346
347
::
348
349
sage: contour_plot(f, (-2, 2), (-2, 2), contours=(0.1, 1.0, 1.2, 1.4), cmap='hsv')
350
351
::
352
353
sage: contour_plot(f, (-2, 2), (-2, 2), contours=(1.0,), fill=False)
354
355
::
356
357
sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,0,1])
358
359
We can change the style of the lines::
360
361
sage: contour_plot(f, (-2,2), (-2,2), fill=False, linewidths=10)
362
363
::
364
365
sage: contour_plot(f, (-2,2), (-2,2), fill=False, linestyles='dashdot')
366
367
::
368
369
sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\
370
... linewidths=[1,5],linestyles=['solid','dashed'],fill=False)
371
sage: P
372
373
::
374
375
sage: P=contour_plot(x^2-y^2,(x,-3,3),(y,-3,3),contours=[0,1,2,3,4],\
376
... linewidths=[1,5],linestyles=['solid','dashed'])
377
sage: P
378
379
We can add labels and play with them::
380
381
sage: contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv', labels=True)
382
383
::
384
385
sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',\
386
... labels=True, label_fmt="%1.0f", label_colors='black')
387
sage: P
388
389
::
390
391
sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\
392
... contours=[-4,0,4], label_fmt={-4:"low", 0:"medium", 4: "hi"}, label_colors='black')
393
sage: P
394
395
::
396
397
sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), fill=False, cmap='hsv',labels=True,\
398
... contours=[-4,0,4], label_fmt=lambda x: "$z=%s$"%x, label_colors='black', label_inline=True, \
399
... label_fontsize=12)
400
sage: P
401
402
::
403
404
sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \
405
... fill=False, cmap='hsv', labels=True, label_fontsize=18)
406
sage: P
407
408
::
409
410
sage: P=contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \
411
... fill=False, cmap='hsv', labels=True, label_inline_spacing=1)
412
sage: P
413
414
::
415
416
sage: P= contour_plot(y^2 + 1 - x^3 - x, (x,-pi,pi), (y,-pi,pi), \
417
... fill=False, cmap='hsv', labels=True, label_inline=False)
418
sage: P
419
420
We can change the color of the labels if so desired::
421
422
sage: contour_plot(f, (-2,2), (-2,2), labels=True, label_colors='red')
423
424
We can add a colorbar as well::
425
426
sage: f(x,y)=x^2-y^2
427
sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True)
428
429
::
430
431
sage: contour_plot(f, (x,-3,3), (y,-3,3), colorbar=True,colorbar_orientation='horizontal')
432
433
::
434
435
sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True)
436
437
::
438
439
sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[-2,-1,4],colorbar=True,colorbar_spacing='uniform')
440
441
::
442
443
sage: contour_plot(f, (x,-3,3), (y,-3,3), contours=[0,2,3,6],colorbar=True,colorbar_format='%.3f')
444
445
::
446
447
sage: contour_plot(f, (x,-3,3), (y,-3,3), labels=True,label_colors='red',contours=[0,2,3,6],colorbar=True)
448
449
::
450
451
sage: contour_plot(f, (x,-3,3), (y,-3,3), cmap='winter', contours=20, fill=False, colorbar=True)
452
453
This should plot concentric circles centered at the origin::
454
455
sage: x,y = var('x,y')
456
sage: contour_plot(x^2+y^2-2,(x,-1,1), (y,-1,1))
457
458
Extra options will get passed on to show(), as long as they are valid::
459
460
sage: f(x, y) = cos(x) + sin(y)
461
sage: contour_plot(f, (0, pi), (0, pi), axes=True)
462
463
One can also plot over a reduced region::
464
465
sage: contour_plot(x**2-y**2, (x,-2, 2), (y,-2, 2),region=x-y,plot_points=300)
466
467
::
468
469
sage: contour_plot(f, (0, pi), (0, pi)).show(axes=True) # These are equivalent
470
471
Note that with ``fill=False`` and grayscale contours, there is the
472
possibility of confusion between the contours and the axes, so use
473
``fill=False`` together with ``axes=True`` with caution::
474
475
sage: contour_plot(f, (-pi, pi), (-pi, pi), fill=False, axes=True)
476
477
TESTS:
478
479
To check that ticket 5221 is fixed, note that this has three curves, not two::
480
481
sage: x,y = var('x,y')
482
sage: contour_plot(x-y^2,(x,-5,5),(y,-3,3),contours=[-4,-2,0], fill=False)
483
"""
484
from sage.plot.all import Graphics
485
from sage.plot.misc import setup_for_eval_on_grid
486
487
region = options.pop('region')
488
ev = [f] if region is None else [f,region]
489
490
F, ranges = setup_for_eval_on_grid(ev, [xrange, yrange], options['plot_points'])
491
g = F[0]
492
xrange,yrange=[r[:2] for r in ranges]
493
494
xy_data_array = [[g(x, y) for x in xsrange(*ranges[0], include_endpoint=True)]
495
for y in xsrange(*ranges[1], include_endpoint=True)]
496
497
if region is not None:
498
import numpy
499
500
xy_data_array = numpy.ma.asarray(xy_data_array,dtype=float)
501
502
m = F[1]
503
504
mask = numpy.asarray([[m(x, y)<=0 for x in xsrange(*ranges[0], include_endpoint=True)]
505
for y in xsrange(*ranges[1], include_endpoint=True)],dtype=bool)
506
507
xy_data_array[mask] = numpy.ma.masked
508
509
g = Graphics()
510
g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax']))
511
g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, options))
512
return g
513
514
@options(plot_points=150, contours=(0,0), fill=False, cmap=["blue"])
515
def implicit_plot(f, xrange, yrange, **options):
516
r"""
517
``implicit_plot`` takes a function of two variables, `f(x,y)`
518
and plots the curve `f(x,y) = 0` over the specified
519
``xrange`` and ``yrange`` as demonstrated below.
520
521
``implicit_plot(f, (xmin, xmax), (ymin, ymax), ...)``
522
523
``implicit_plot(f, (x, xmin, xmax), (y, ymin, ymax), ...)``
524
525
INPUT:
526
527
- ``f`` -- a function of two variables or equation in two variables
528
529
- ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values or ``(x,xmin,xmax)``
530
531
- ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values or ``(y,ymin,ymax)``
532
533
The following inputs must all be passed in as named parameters:
534
535
- ``plot_points`` -- integer (default: 150); number of points to plot
536
in each direction of the grid
537
538
- ``fill`` -- boolean (default: ``False``); if ``True``, fill the region
539
`f(x,y) < 0`.
540
541
- ``linewidth`` -- integer (default: None), if a single integer all levels
542
will be of the width given, otherwise the levels will be plotted with the
543
widths in the order given.
544
545
- ``linestyle`` -- string (default: None), the style of the line to be
546
plotted, one of: solid, dashed, dashdot or dotted.
547
548
- ``color`` -- string (default: ``blue``), the color of the plot. Colors are
549
defined in :mod:`sage.plot.colors`; try ``colors?`` to see them all.
550
551
- ``legend_label`` -- the label for this item in the legend
552
553
EXAMPLES:
554
555
A simple circle with a radius of 2. Note that
556
since the input function is an expression, we need to explicitly
557
declare the variables in 3-tuples for the range::
558
559
sage: var("x y")
560
(x, y)
561
sage: implicit_plot(x^2+y^2-2, (x,-3,3), (y,-3,3))
562
563
I can do the same thing, but using a callable function so I don't need
564
to explicitly define the variables in the ranges, and filling the inside::
565
566
sage: f(x,y) = x^2 + y^2 - 2
567
sage: implicit_plot(f, (-3, 3), (-3, 3),fill=True)
568
569
The same circle but with a different line width::
570
571
sage: implicit_plot(f, (-3,3), (-3,3), linewidth=6)
572
573
And again the same circle but this time with a dashdot border::
574
575
sage: implicit_plot(f, (-3,3), (-3,3), linestyle='dashdot')
576
577
You can also plot an equation::
578
579
sage: var("x y")
580
(x, y)
581
sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3))
582
583
You can even change the color of the plot::
584
585
sage: implicit_plot(x^2+y^2 == 2, (x,-3,3), (y,-3,3), color="red")
586
587
Here is a beautiful (and long) example which also tests that all
588
colors work with this::
589
590
sage: G = Graphics()
591
sage: counter = 0
592
sage: for col in colors.keys(): # long time
593
... G += implicit_plot(x^2+y^2==1+counter*.1, (x,-4,4),(y,-4,4),color=col)
594
... counter += 1
595
sage: G.show(frame=False)
596
597
We can define a level-`n` approximation of the boundary of the
598
Mandelbrot set::
599
600
sage: def mandel(n):
601
... c = polygen(CDF, 'c')
602
... z = 0
603
... for i in range(n):
604
... z = z*z + c
605
... def f(x, y):
606
... val = z(CDF(x, y))
607
... return val.norm() - 4
608
... return f
609
610
The first-level approximation is just a circle::
611
612
sage: implicit_plot(mandel(1), (-3, 3), (-3, 3))
613
614
A third-level approximation starts to get interesting::
615
616
sage: implicit_plot(mandel(3), (-2, 1), (-1.5, 1.5))
617
618
The seventh-level approximation is a degree 64 polynomial, and
619
``implicit_plot`` does a pretty good job on this part of the curve.
620
(``plot_points=200`` looks even better, but it takes over a second.)
621
622
::
623
624
sage: implicit_plot(mandel(7), (-0.3, 0.05), (-1.15, -0.9),plot_points=50)
625
626
When making a filled implicit plot using a python function rather than a
627
symbolic expression the user should increase the number of plot points to
628
avoid artifacts::
629
630
sage: implicit_plot(lambda x,y: x^2+y^2-2, (x,-3,3), (y,-3,3), fill=True, plot_points=500) # long time
631
632
TESTS::
633
634
sage: f(x,y) = x^2 + y^2 - 2
635
sage: implicit_plot(f, (-3, 3), (-3, 3),fill=5)
636
Traceback (most recent call last):
637
...
638
ValueError: fill=5 is not supported
639
"""
640
from sage.symbolic.expression import is_SymbolicEquation
641
if is_SymbolicEquation(f):
642
if f.operator() != operator.eq:
643
raise ValueError, "input to implicit plot must be function or equation"
644
f = f.lhs() - f.rhs()
645
linewidths = options.pop('linewidth', None)
646
linestyles = options.pop('linestyle', None)
647
648
if 'color' in options:
649
options['cmap']=[options.pop('color', None)]
650
651
if options['fill'] is True:
652
options.pop('fill')
653
options.pop('contours',None)
654
options.pop('cmap',None)
655
from sage.symbolic.expression import is_Expression
656
if not is_Expression(f):
657
return region_plot(lambda x,y: f(x,y)<0, xrange, yrange,
658
borderwidth=linewidths, borderstyle=linestyles,
659
**options)
660
else:
661
return region_plot(f<0, xrange, yrange, borderwidth=linewidths,
662
borderstyle=linestyles, **options)
663
elif options['fill'] is False:
664
return contour_plot(f, xrange, yrange, linewidths=linewidths,
665
linestyles=linestyles, **options)
666
else:
667
raise ValueError("fill=%s is not supported" % options['fill'])
668
669
670
@options(plot_points=100, incol='blue', outcol='white', bordercol=None, borderstyle=None, borderwidth=None,frame=False,axes=True, legend_label=None, aspect_ratio=1)
671
def region_plot(f, xrange, yrange, plot_points, incol, outcol, bordercol, borderstyle, borderwidth,**options):
672
r"""
673
``region_plot`` takes a boolean function of two variables, `f(x,y)`
674
and plots the region where f is True over the specified
675
``xrange`` and ``yrange`` as demonstrated below.
676
677
``region_plot(f, (xmin, xmax), (ymin, ymax), ...)``
678
679
INPUT:
680
681
- ``f`` -- a boolean function of two variables
682
683
- ``(xmin, xmax)`` -- 2-tuple, the range of ``x`` values OR 3-tuple
684
``(x,xmin,xmax)``
685
686
- ``(ymin, ymax)`` -- 2-tuple, the range of ``y`` values OR 3-tuple
687
``(y,ymin,ymax)``
688
689
- ``plot_points`` -- integer (default: 100); number of points to plot
690
in each direction of the grid
691
692
- ``incol`` -- a color (default: ``'blue'``), the color inside the region
693
694
- ``outcol`` -- a color (default: ``'white'``), the color of the outside
695
of the region
696
697
If any of these options are specified, the border will be shown as indicated,
698
otherwise it is only implicit (with color ``incol``) as the border of the
699
inside of the region.
700
701
- ``bordercol`` -- a color (default: ``None``), the color of the border
702
(``'black'`` if ``borderwidth`` or ``borderstyle`` is specified but not ``bordercol``)
703
704
- ``borderstyle`` -- string (default: 'solid'), one of 'solid', 'dashed', 'dotted', 'dashdot'
705
706
- ``borderwidth`` -- integer (default: None), the width of the border in pixels
707
708
- ``legend_label`` -- the label for this item in the legend
709
710
711
EXAMPLES:
712
713
Here we plot a simple function of two variables::
714
715
sage: x,y = var('x,y')
716
sage: region_plot(cos(x^2+y^2) <= 0, (x, -3, 3), (y, -3, 3))
717
718
Here we play with the colors::
719
720
sage: region_plot(x^2+y^3 < 2, (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray')
721
722
An even more complicated plot, with dashed borders::
723
724
sage: region_plot(sin(x)*sin(y) >= 1/4, (x,-10,10), (y,-10,10), incol='yellow', bordercol='black', borderstyle='dashed', plot_points=250)
725
726
A disk centered at the origin::
727
728
sage: region_plot(x^2+y^2<1, (x,-1,1), (y,-1,1))
729
730
A plot with more than one condition (all conditions must be true for the statement to be true)::
731
732
sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2))
733
734
Since it doesn't look very good, let's increase ``plot_points``::
735
736
sage: region_plot([x^2+y^2<1, x<y], (x,-2,2), (y,-2,2), plot_points=400)
737
738
To get plots where only one condition needs to be true, use a function.
739
Using lambda functions, we definitely need the extra ``plot_points``::
740
741
sage: region_plot(lambda x,y: x^2+y^2<1 or x<y, (x,-2,2), (y,-2,2), plot_points=400)
742
743
The first quadrant of the unit circle::
744
745
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)
746
747
Here is another plot, with a huge border::
748
749
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)
750
751
If we want to keep only the region where x is positive::
752
753
sage: region_plot([x*(x-1)*(x+1)+y^2<0, x>-1], (x, -3, 2), (y, -3, 3), incol='lightblue', plot_points=50)
754
755
Here we have a cut circle::
756
757
sage: region_plot([x^2+y^2<4, x>-1], (x, -2, 2), (y, -2, 2), incol='lightblue', bordercol='gray', plot_points=200)
758
759
The first variable range corresponds to the horizontal axis and
760
the second variable range corresponds to the vertical axis::
761
762
sage: s,t=var('s,t')
763
sage: region_plot(s>0,(t,-2,2),(s,-2,2))
764
765
::
766
767
sage: region_plot(s>0,(s,-2,2),(t,-2,2))
768
769
"""
770
771
from sage.plot.all import Graphics
772
from sage.plot.misc import setup_for_eval_on_grid
773
import numpy
774
775
if not isinstance(f, (list, tuple)):
776
f = [f]
777
778
f = [equify(g) for g in f]
779
780
g, ranges = setup_for_eval_on_grid(f, [xrange, yrange], plot_points)
781
xrange,yrange=[r[:2] for r in ranges]
782
783
xy_data_arrays = numpy.asarray([[[func(x, y) for x in xsrange(*ranges[0], include_endpoint=True)]
784
for y in xsrange(*ranges[1], include_endpoint=True)]
785
for func in g],dtype=float)
786
xy_data_array=numpy.abs(xy_data_arrays.prod(axis=0))
787
# Now we need to set entries to negative iff all
788
# functions were negative at that point.
789
neg_indices = (xy_data_arrays<0).all(axis=0)
790
xy_data_array[neg_indices]=-xy_data_array[neg_indices]
791
792
from matplotlib.colors import ListedColormap
793
incol = rgbcolor(incol)
794
outcol = rgbcolor(outcol)
795
cmap = ListedColormap([incol, outcol])
796
cmap.set_over(outcol)
797
cmap.set_under(incol)
798
799
g = Graphics()
800
g._set_extra_kwds(Graphics._extract_kwds_for_show(options, ignore=['xmin', 'xmax']))
801
g.add_primitive(ContourPlot(xy_data_array, xrange,yrange,
802
dict(contours=[-1e307, 0, 1e307], cmap=cmap, fill=True, **options)))
803
804
if bordercol or borderstyle or borderwidth:
805
cmap = [rgbcolor(bordercol)] if bordercol else ['black']
806
linestyles = [borderstyle] if borderstyle else None
807
linewidths = [borderwidth] if borderwidth else None
808
g.add_primitive(ContourPlot(xy_data_array, xrange, yrange,
809
dict(linestyles=linestyles, linewidths=linewidths,
810
contours=[0], cmap=[bordercol], fill=False, **options)))
811
812
return g
813
814
def equify(f):
815
"""
816
Returns the equation rewritten as a symbolic function to give
817
negative values when True, positive when False.
818
819
EXAMPLES::
820
821
sage: from sage.plot.contour_plot import equify
822
sage: var('x, y')
823
(x, y)
824
sage: equify(x^2 < 2)
825
x^2 - 2
826
sage: equify(x^2 > 2)
827
-x^2 + 2
828
sage: equify(x*y > 1)
829
-x*y + 1
830
sage: equify(y > 0)
831
-y
832
sage: f=equify(lambda x,y: x>y)
833
sage: f(1,2)
834
1
835
sage: f(2,1)
836
-1
837
"""
838
import operator
839
from sage.calculus.all import symbolic_expression
840
from sage.symbolic.expression import is_Expression
841
if not is_Expression(f):
842
return lambda x,y: -1 if f(x,y) else 1
843
844
op = f.operator()
845
if op is operator.gt or op is operator.ge:
846
return symbolic_expression(f.rhs() - f.lhs())
847
else:
848
return symbolic_expression(f.lhs() - f.rhs())
849
850