Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/plot/circle.py
8815 views
1
"""
2
Circles
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2006 Alex Clemesha <[email protected]>,
6
# William Stein <[email protected]>,
7
# 2008 Mike Hansen <[email protected]>,
8
#
9
# Distributed under the terms of the GNU General Public License (GPL)
10
#
11
# This code is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
# General Public License for more details.
15
#
16
# The full text of the GPL is available at:
17
#
18
# http://www.gnu.org/licenses/
19
#*****************************************************************************
20
from primitive import GraphicPrimitive
21
from sage.misc.decorators import options, rename_keyword
22
from sage.plot.colors import to_mpl_color
23
from math import sin, cos, pi
24
25
class Circle(GraphicPrimitive):
26
"""
27
Primitive class for the Circle graphics type. See circle? for information
28
about actually plotting circles.
29
30
INPUT:
31
32
- x - `x`-coordinate of center of Circle
33
34
- y - `y`-coordinate of center of Circle
35
36
- r - radius of Circle object
37
38
- options - dict of valid plot options to pass to constructor
39
40
EXAMPLES:
41
42
Note this should normally be used indirectly via ``circle``::
43
44
sage: from sage.plot.circle import Circle
45
sage: C = Circle(2,3,5,{'zorder':2})
46
sage: C
47
Circle defined by (2.0,3.0) with r=5.0
48
sage: C.options()['zorder']
49
2
50
sage: C.r
51
5.0
52
53
TESTS:
54
55
We test creating a circle::
56
57
sage: C = circle((2,3), 5)
58
"""
59
def __init__(self, x, y, r, options):
60
"""
61
Initializes base class Circle.
62
63
EXAMPLES::
64
65
sage: C = circle((2,3), 5, edgecolor='red', alpha=.5, fill=True)
66
sage: C[0].x
67
2.0
68
sage: C[0].r
69
5.0
70
sage: C[0].options()['edgecolor']
71
'red'
72
sage: C[0].options()['alpha']
73
0.500000000000000
74
"""
75
self.x = float(x)
76
self.y = float(y)
77
self.r = float(r)
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: p = circle((3, 3), 1)
87
sage: d = p.get_minmax_data()
88
sage: d['xmin']
89
2.0
90
sage: d['ymin']
91
2.0
92
"""
93
from sage.plot.plot import minmax_data
94
return minmax_data([self.x - self.r, self.x + self.r],
95
[self.y - self.r, self.y + self.r],
96
dict=True)
97
98
def _allowed_options(self):
99
"""
100
Return the allowed options for the Circle class.
101
102
EXAMPLES::
103
104
sage: p = circle((3, 3), 1)
105
sage: p[0]._allowed_options()['alpha']
106
'How transparent the figure is.'
107
sage: p[0]._allowed_options()['facecolor']
108
'2D only: The color of the face as an RGB tuple.'
109
"""
110
return {'alpha':'How transparent the figure is.',
111
'fill':'Whether or not to fill the circle.',
112
'legend_label':'The label for this item in the legend.',
113
'legend_color':'The color of the legend text.',
114
'thickness':'How thick the border of the circle is.',
115
'edgecolor':'2D only: The color of the edge as an RGB tuple.',
116
'facecolor':'2D only: The color of the face as an RGB tuple.',
117
'rgbcolor':'The color (edge and face) as an RGB tuple.',
118
'hue':'The color given as a hue.',
119
'zorder':'2D only: The layer level in which to draw',
120
'linestyle':"2D only: The style of the line, which is one of "
121
"'dashed', 'dotted', 'solid', 'dashdot', or '--', ':', '-', '-.', "
122
"respectively.",
123
'clip': 'Whether or not to clip the circle.'}
124
125
def _repr_(self):
126
"""
127
String representation of Circle primitive.
128
129
EXAMPLES::
130
131
sage: C = circle((2,3), 5)
132
sage: c = C[0]; c
133
Circle defined by (2.0,3.0) with r=5.0
134
"""
135
return "Circle defined by (%s,%s) with r=%s"%(self.x, self.y, self.r)
136
137
def _render_on_subplot(self, subplot):
138
"""
139
TESTS::
140
141
sage: C = circle((2,pi), 2, edgecolor='black', facecolor='green', fill=True)
142
"""
143
import matplotlib.patches as patches
144
from sage.plot.misc import get_matplotlib_linestyle
145
146
options = self.options()
147
p = patches.Circle((float(self.x), float(self.y)), float(self.r), clip_on=options['clip'])
148
if not options['clip']:
149
self._bbox_extra_artists=[p]
150
p.set_linewidth(float(options['thickness']))
151
p.set_fill(options['fill'])
152
a = float(options['alpha'])
153
p.set_alpha(a)
154
ec = to_mpl_color(options['edgecolor'])
155
fc = to_mpl_color(options['facecolor'])
156
if 'rgbcolor' in options:
157
ec = fc = to_mpl_color(options['rgbcolor'])
158
p.set_edgecolor(ec)
159
p.set_facecolor(fc)
160
p.set_linestyle(get_matplotlib_linestyle(options['linestyle'],return_type='long'))
161
p.set_label(options['legend_label'])
162
z = int(options.pop('zorder', 0))
163
p.set_zorder(z)
164
subplot.add_patch(p)
165
166
def plot3d(self, z=0, **kwds):
167
"""
168
Plots a 2D circle (actually a 50-gon) in 3D,
169
with default height zero.
170
171
INPUT:
172
173
174
- ``z`` - optional 3D height above `xy`-plane.
175
176
EXAMPLES::
177
178
sage: circle((0,0), 1).plot3d()
179
180
This example uses this method implicitly, but does not pass
181
the optional parameter z to this method::
182
183
sage: sum([circle((random(),random()), random()).plot3d(z=random()) for _ in range(20)])
184
185
These examples are explicit, and pass z to this method::
186
187
sage: C = circle((2,pi), 2, hue=.8, alpha=.3, fill=True)
188
sage: c = C[0]
189
sage: d = c.plot3d(z=2)
190
sage: d.texture.opacity
191
0.300000000000000
192
193
::
194
195
sage: C = circle((2,pi), 2, hue=.8, alpha=.3, linestyle='dotted')
196
sage: c = C[0]
197
sage: d = c.plot3d(z=2)
198
sage: d.jmol_repr(d.testing_render_params())[0][-1]
199
'color $line_1 translucent 0.7 [204,0,255]'
200
"""
201
options = dict(self.options())
202
fill = options['fill']
203
for s in ['clip', 'edgecolor', 'facecolor', 'fill', 'linestyle',
204
'zorder']:
205
if s in options:
206
del options[s]
207
208
n = 50
209
dt = float(2*pi/n)
210
x, y, r = self.x, self.y, self.r
211
xdata = [x+r*cos(t*dt) for t in range(n+1)]
212
ydata = [y+r*sin(t*dt) for t in range(n+1)]
213
if fill:
214
from polygon import Polygon
215
return Polygon(xdata, ydata, options).plot3d(z)
216
else:
217
from line import Line
218
return Line(xdata, ydata, options).plot3d().translate((0,0,z))
219
220
@rename_keyword(color='rgbcolor')
221
@options(alpha=1, fill=False, thickness=1, edgecolor='blue', facecolor='blue', linestyle='solid',
222
zorder=5, legend_label=None, legend_color=None, clip=True, aspect_ratio=1.0)
223
def circle(center, radius, **options):
224
"""
225
Return a circle at a point center = `(x,y)` (or `(x,y,z)` and
226
parallel to the `xy`-plane) with radius = `r`. Type
227
``circle.options`` to see all options.
228
229
OPTIONS:
230
231
- ``alpha`` - default: 1
232
233
- ``fill`` - default: False
234
235
- ``thickness`` - default: 1
236
237
- ``linestyle`` - default: ``'solid'`` (2D plotting only) The style of the
238
line, which is one of ``'dashed'``, ``'dotted'``, ``'solid'``, ``'dashdot'``,
239
or ``'--'``, ``':'``, ``'-'``, ``'-.'``, respectively.
240
241
- ``edgecolor`` - default: 'blue' (2D plotting only)
242
243
- ``facecolor`` - default: 'blue' (2D plotting only, useful only
244
if ``fill=True``)
245
246
- ``rgbcolor`` - 2D or 3D plotting. This option overrides
247
``edgecolor`` and ``facecolor`` for 2D plotting.
248
249
- ``legend_label`` - the label for this item in the legend
250
251
- ``legend_color`` - the color for the legend label
252
253
EXAMPLES:
254
255
The default color is blue, the default linestyle is solid, but this is easy to change::
256
257
sage: c = circle((1,1), 1)
258
sage: c
259
260
::
261
262
sage: c = circle((1,1), 1, rgbcolor=(1,0,0), linestyle='-.')
263
sage: c
264
265
We can also use this command to plot three-dimensional circles parallel
266
to the `xy`-plane::
267
268
sage: c = circle((1,1,3), 1, rgbcolor=(1,0,0))
269
sage: c
270
sage: type(c)
271
<class 'sage.plot.plot3d.base.TransformGroup'>
272
273
To correct the aspect ratio of certain graphics, it is necessary
274
to show with a ``figsize`` of square dimensions::
275
276
sage: c.show(figsize=[5,5],xmin=-1,xmax=3,ymin=-1,ymax=3)
277
278
Here we make a more complicated plot, with many circles of different colors::
279
280
sage: g = Graphics()
281
sage: step=6; ocur=1/5; paths=16;
282
sage: PI = math.pi # numerical for speed -- fine for graphics
283
sage: for r in range(1,paths+1):
284
... for x,y in [((r+ocur)*math.cos(n), (r+ocur)*math.sin(n)) for n in srange(0, 2*PI+PI/step, PI/step)]:
285
... g += circle((x,y), ocur, rgbcolor=hue(r/paths))
286
... rnext = (r+1)^2
287
... ocur = (rnext-r)-ocur
288
...
289
sage: g.show(xmin=-(paths+1)^2, xmax=(paths+1)^2, ymin=-(paths+1)^2, ymax=(paths+1)^2, figsize=[6,6])
290
291
Note that the ``rgbcolor`` option overrides the other coloring options.
292
This produces red fill in a blue circle::
293
294
sage: circle((2,3), 1, fill=True, edgecolor='blue')
295
296
This produces an all-green filled circle::
297
298
sage: circle((2,3), 1, fill=True, edgecolor='blue', rgbcolor='green')
299
300
The option ``hue`` overrides *all* other options, so be careful with its use.
301
This produces a purplish filled circle::
302
303
sage: circle((2,3), 1, fill=True, edgecolor='blue', rgbcolor='green', hue=.8)
304
305
And circles with legends::
306
307
sage: circle((4,5), 1, rgbcolor='yellow', fill=True, legend_label='the sun').show(xmin=0, ymin=0)
308
309
::
310
311
sage: circle((4,5), 1, legend_label='the sun', legend_color='yellow').show(xmin=0, ymin=0)
312
313
Extra options will get passed on to show(), as long as they are valid::
314
315
sage: circle((0, 0), 2, figsize=[10,10]) # That circle is huge!
316
317
::
318
319
sage: circle((0, 0), 2).show(figsize=[10,10]) # These are equivalent
320
321
TESTS:
322
323
We cannot currently plot circles in more than three dimensions::
324
325
sage: circle((1,1,1,1), 1, rgbcolor=(1,0,0))
326
Traceback (most recent call last):
327
...
328
ValueError: The center of a plotted circle should have two or three coordinates.
329
330
The default aspect ratio for a circle is 1.0::
331
332
sage: P = circle((1,1), 1)
333
sage: P.aspect_ratio()
334
1.0
335
"""
336
from sage.plot.all import Graphics
337
338
# Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'.
339
# Otherwise matplotlib complains.
340
scale = options.get('scale', None)
341
if isinstance(scale, (list, tuple)):
342
scale = scale[0]
343
if scale == 'semilogy' or scale == 'semilogx':
344
options['aspect_ratio'] = 'automatic'
345
346
g = Graphics()
347
g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
348
g.add_primitive(Circle(center[0], center[1], radius, options))
349
if options['legend_label']:
350
g.legend(True)
351
g._legend_colors = [options['legend_color']]
352
if len(center)==2:
353
return g
354
elif len(center)==3:
355
return g[0].plot3d(z=center[2])
356
else:
357
raise ValueError, 'The center of a plotted circle should have two or three coordinates.'
358
359