Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/plot/polygon.py
8815 views
1
"""
2
Polygons
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 sage.plot.primitive import GraphicPrimitive_xydata
21
from sage.misc.decorators import options, rename_keyword
22
from sage.plot.colors import to_mpl_color
23
24
class Polygon(GraphicPrimitive_xydata):
25
"""
26
Primitive class for the Polygon graphics type. For information
27
on actual plotting, please see :func:`polygon`, :func:`polygon2d`,
28
or :func:`~sage.plot.plot3d.shapes2.polygon3d`.
29
30
INPUT:
31
32
- xdata - list of `x`-coordinates of points defining Polygon
33
34
- ydata - list of `y`-coordinates of points defining Polygon
35
36
- options - dict of valid plot options to pass to constructor
37
38
EXAMPLES:
39
40
Note this should normally be used indirectly via :func:`polygon`::
41
42
sage: from sage.plot.polygon import Polygon
43
sage: P = Polygon([1,2,3],[2,3,2],{'alpha':.5})
44
sage: P
45
Polygon defined by 3 points
46
sage: P.options()['alpha']
47
0.500000000000000
48
sage: P.ydata
49
[2, 3, 2]
50
51
TESTS:
52
53
We test creating polygons::
54
55
sage: polygon([(0,0), (1,1), (0,1)])
56
57
::
58
59
sage: polygon([(0,0,1), (1,1,1), (2,0,1)])
60
"""
61
def __init__(self, xdata, ydata, options):
62
"""
63
Initializes base class Polygon.
64
65
EXAMPLES::
66
67
sage: P = polygon([(0,0), (1,1), (-1,3)], thickness=2)
68
sage: P[0].xdata
69
[0.0, 1.0, -1.0]
70
sage: P[0].options()['thickness']
71
2
72
"""
73
self.xdata = xdata
74
self.ydata = ydata
75
GraphicPrimitive_xydata.__init__(self, options)
76
77
def _repr_(self):
78
"""
79
String representation of Polygon primitive.
80
81
EXAMPLES::
82
83
sage: P = polygon([(0,0), (1,1), (-1,3)])
84
sage: p=P[0]; p
85
Polygon defined by 3 points
86
"""
87
return "Polygon defined by %s points"%len(self)
88
89
def __getitem__(self, i):
90
"""
91
Returns `i`th vertex of Polygon primitive, starting count
92
from 0th vertex.
93
94
EXAMPLES::
95
96
sage: P = polygon([(0,0), (1,1), (-1,3)])
97
sage: p=P[0]
98
sage: p[0]
99
(0.0, 0.0)
100
"""
101
return self.xdata[i], self.ydata[i]
102
103
def __setitem__(self, i, point):
104
"""
105
Changes `i`th vertex of Polygon primitive, starting count
106
from 0th vertex. Note that this only changes a vertex,
107
but does not create new vertices.
108
109
EXAMPLES::
110
111
sage: P = polygon([(0,0), (1,2), (0,1), (-1,2)])
112
sage: p=P[0]
113
sage: [p[i] for i in range(4)]
114
[(0.0, 0.0), (1.0, 2.0), (0.0, 1.0), (-1.0, 2.0)]
115
sage: p[2]=(0,.5)
116
sage: p[2]
117
(0.0, 0.5)
118
"""
119
i = int(i)
120
self.xdata[i] = float(point[0])
121
self.ydata[i] = float(point[1])
122
123
def __len__(self):
124
"""
125
Returns number of vertices of Polygon primitive.
126
127
EXAMPLES::
128
129
sage: P = polygon([(0,0), (1,2), (0,1), (-1,2)])
130
sage: p=P[0]
131
sage: len(p)
132
4
133
"""
134
return len(self.xdata)
135
136
def _allowed_options(self):
137
"""
138
Return the allowed options for the Polygon class.
139
140
EXAMPLES::
141
142
sage: P = polygon([(1,1), (1,2), (2,2), (2,1)], alpha=.5)
143
sage: P[0]._allowed_options()['alpha']
144
'How transparent the figure is.'
145
"""
146
return {'alpha':'How transparent the figure is.',
147
'thickness': 'How thick the border line is.',
148
'fill':'Whether or not to fill the polygon.',
149
'legend_label':'The label for this item in the legend.',
150
'legend_color':'The color of the legend text.',
151
'rgbcolor':'The color as an RGB tuple.',
152
'hue':'The color given as a hue.',
153
'zorder':'The layer level in which to draw'}
154
155
def _plot3d_options(self, options=None):
156
"""
157
Translate 2d plot options into 3d plot options.
158
159
EXAMPLES::
160
161
sage: P = polygon([(1,1), (1,2), (2,2), (2,1)], alpha=.5)
162
sage: p=P[0]; p
163
Polygon defined by 4 points
164
sage: q=p.plot3d()
165
sage: q.texture.opacity
166
0.500000000000000
167
"""
168
if options is None:
169
options = dict(self.options())
170
for o in ['thickness', 'zorder', 'legend_label', 'fill']:
171
options.pop(o, None)
172
return GraphicPrimitive_xydata._plot3d_options(self, options)
173
174
def plot3d(self, z=0, **kwds):
175
"""
176
Plots a 2D polygon in 3D, with default height zero.
177
178
INPUT:
179
180
181
- ``z`` - optional 3D height above `xy`-plane, or a list of
182
heights corresponding to the list of 2D polygon points.
183
184
EXAMPLES:
185
186
A pentagon::
187
188
sage: polygon([(cos(t), sin(t)) for t in srange(0, 2*pi, 2*pi/5)]).plot3d()
189
190
Showing behavior of the optional parameter z::
191
192
sage: P = polygon([(0,0), (1,2), (0,1), (-1,2)])
193
sage: p = P[0]; p
194
Polygon defined by 4 points
195
sage: q = p.plot3d()
196
sage: q.obj_repr(q.testing_render_params())[2]
197
['v 0 0 0', 'v 1 2 0', 'v 0 1 0', 'v -1 2 0']
198
sage: r = p.plot3d(z=3)
199
sage: r.obj_repr(r.testing_render_params())[2]
200
['v 0 0 3', 'v 1 2 3', 'v 0 1 3', 'v -1 2 3']
201
sage: s = p.plot3d(z=[0,1,2,3])
202
sage: s.obj_repr(s.testing_render_params())[2]
203
['v 0 0 0', 'v 1 2 1', 'v 0 1 2', 'v -1 2 3']
204
205
TESTS:
206
207
Heights passed as a list should have same length as
208
number of points::
209
210
sage: P = polygon([(0,0), (1,2), (0,1), (-1,2)])
211
sage: p = P[0]
212
sage: q = p.plot3d(z=[2,-2])
213
Traceback (most recent call last):
214
...
215
ValueError: Incorrect number of heights given
216
"""
217
from sage.plot.plot3d.index_face_set import IndexFaceSet
218
options = self._plot3d_options()
219
options.update(kwds)
220
zdata=[]
221
if type(z) is list:
222
zdata=z
223
else:
224
zdata=[z]*len(self.xdata)
225
if len(zdata)==len(self.xdata):
226
return IndexFaceSet([[(x, y, z) for x, y, z in zip(self.xdata, self.ydata, zdata)]], **options)
227
else:
228
raise ValueError, 'Incorrect number of heights given'
229
230
def _render_on_subplot(self, subplot):
231
"""
232
TESTS::
233
234
sage: P = polygon([(0,0), (1,2), (0,1), (-1,2)])
235
"""
236
import matplotlib.patches as patches
237
options = self.options()
238
p = patches.Polygon([(self.xdata[i],self.ydata[i]) for i in xrange(len(self.xdata))])
239
p.set_linewidth(float(options['thickness']))
240
a = float(options['alpha'])
241
z = int(options.pop('zorder', 1))
242
p.set_alpha(a)
243
f = options.pop('fill')
244
p.set_fill(f)
245
c = to_mpl_color(options['rgbcolor'])
246
p.set_edgecolor(c)
247
p.set_facecolor(c)
248
p.set_label(options['legend_label'])
249
p.set_zorder(z)
250
subplot.add_patch(p)
251
252
def polygon(points, **options):
253
"""
254
Returns either a 2-dimensional or 3-dimensional polygon depending
255
on value of points.
256
257
For information regarding additional arguments, see either
258
:func:`polygon2d` or :func:`~sage.plot.plot3d.shapes2.polygon3d`.
259
Options may be found and set using the dictionaries ``polygon2d.options``
260
and ``polygon3d.options``.
261
262
EXAMPLES::
263
264
sage: polygon([(0,0), (1,1), (0,1)])
265
sage: polygon([(0,0,1), (1,1,1), (2,0,1)])
266
267
Extra options will get passed on to show(), as long as they are valid::
268
269
sage: polygon([(0,0), (1,1), (0,1)], axes=False)
270
sage: polygon([(0,0), (1,1), (0,1)]).show(axes=False) # These are equivalent
271
"""
272
try:
273
return polygon2d(points, **options)
274
except ValueError:
275
from sage.plot.plot3d.shapes2 import polygon3d
276
return polygon3d(points, **options)
277
278
@rename_keyword(color='rgbcolor')
279
@options(alpha=1, rgbcolor=(0,0,1), thickness=None, legend_label=None, legend_color=None,
280
aspect_ratio=1.0, fill=True)
281
def polygon2d(points, **options):
282
r"""
283
Returns a 2-dimensional polygon defined by ``points``.
284
285
Type ``polygon2d.options`` for a dictionary of the default
286
options for polygons. You can change this to change the
287
defaults for all future polygons. Use ``polygon2d.reset()``
288
to reset to the default options.
289
290
EXAMPLES:
291
292
We create a purple-ish polygon::
293
294
sage: polygon2d([[1,2], [5,6], [5,0]], rgbcolor=(1,0,1))
295
296
By default, polygons are filled in, but we can make them
297
without a fill as well::
298
299
sage: polygon2d([[1,2], [5,6], [5,0]], fill=False)
300
301
In either case, the thickness of the border can be controlled::
302
303
sage: polygon2d([[1,2], [5,6], [5,0]], fill=False, thickness=4, color='orange')
304
305
Some modern art -- a random polygon, with legend::
306
307
sage: v = [(randrange(-5,5), randrange(-5,5)) for _ in range(10)]
308
sage: polygon2d(v, legend_label='some form')
309
310
A purple hexagon::
311
312
sage: L = [[cos(pi*i/3),sin(pi*i/3)] for i in range(6)]
313
sage: polygon2d(L, rgbcolor=(1,0,1))
314
315
A green deltoid::
316
317
sage: L = [[-1+cos(pi*i/100)*(1+cos(pi*i/100)),2*sin(pi*i/100)*(1-cos(pi*i/100))] for i in range(200)]
318
sage: polygon2d(L, rgbcolor=(1/8,3/4,1/2))
319
320
A blue hypotrochoid::
321
322
sage: L = [[6*cos(pi*i/100)+5*cos((6/2)*pi*i/100),6*sin(pi*i/100)-5*sin((6/2)*pi*i/100)] for i in range(200)]
323
sage: polygon2d(L, rgbcolor=(1/8,1/4,1/2))
324
325
Another one::
326
327
sage: n = 4; h = 5; b = 2
328
sage: L = [[n*cos(pi*i/100)+h*cos((n/b)*pi*i/100),n*sin(pi*i/100)-h*sin((n/b)*pi*i/100)] for i in range(200)]
329
sage: polygon2d(L, rgbcolor=(1/8,1/4,3/4))
330
331
A purple epicycloid::
332
333
sage: m = 9; b = 1
334
sage: L = [[m*cos(pi*i/100)+b*cos((m/b)*pi*i/100),m*sin(pi*i/100)-b*sin((m/b)*pi*i/100)] for i in range(200)]
335
sage: polygon2d(L, rgbcolor=(7/8,1/4,3/4))
336
337
A brown astroid::
338
339
sage: L = [[cos(pi*i/100)^3,sin(pi*i/100)^3] for i in range(200)]
340
sage: polygon2d(L, rgbcolor=(3/4,1/4,1/4))
341
342
And, my favorite, a greenish blob::
343
344
sage: L = [[cos(pi*i/100)*(1+cos(pi*i/50)), sin(pi*i/100)*(1+sin(pi*i/50))] for i in range(200)]
345
sage: polygon2d(L, rgbcolor=(1/8, 3/4, 1/2))
346
347
This one is for my wife::
348
349
sage: L = [[sin(pi*i/100)+sin(pi*i/50),-(1+cos(pi*i/100)+cos(pi*i/50))] for i in range(-100,100)]
350
sage: polygon2d(L, rgbcolor=(1,1/4,1/2))
351
352
One can do the same one with a colored legend label::
353
354
sage: polygon2d(L, color='red', legend_label='For you!', legend_color='red')
355
356
Polygons have a default aspect ratio of 1.0::
357
358
sage: polygon2d([[1,2], [5,6], [5,0]]).aspect_ratio()
359
1.0
360
361
AUTHORS:
362
363
- David Joyner (2006-04-14): the long list of examples above.
364
365
"""
366
from sage.plot.plot import xydata_from_point_list
367
from sage.plot.all import Graphics
368
if options["thickness"] is None: # If the user did not specify thickness
369
if options["fill"]: # If the user chose fill
370
options["thickness"] = 0
371
else:
372
options["thickness"] = 1
373
xdata, ydata = xydata_from_point_list(points)
374
g = Graphics()
375
376
# Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'.
377
# Otherwise matplotlib complains.
378
scale = options.get('scale', None)
379
if isinstance(scale, (list, tuple)):
380
scale = scale[0]
381
if scale == 'semilogy' or scale == 'semilogx':
382
options['aspect_ratio'] = 'automatic'
383
384
g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
385
g.add_primitive(Polygon(xdata, ydata, options))
386
if options['legend_label']:
387
g.legend(True)
388
g._legend_colors = [options['legend_color']]
389
return g
390
391