Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/plot/polygon.py
4034 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
'rgbcolor':'The color as an RGB tuple.',
151
'hue':'The color given as a hue.',
152
'zorder':'The layer level in which to draw'}
153
154
def _plot3d_options(self, options=None):
155
"""
156
Translate 2d plot options into 3d plot options.
157
158
EXAMPLES::
159
160
sage: P = polygon([(1,1), (1,2), (2,2), (2,1)], alpha=.5)
161
sage: p=P[0]; p
162
Polygon defined by 4 points
163
sage: q=p.plot3d()
164
sage: q.texture.opacity
165
0.500000000000000
166
"""
167
if options is None:
168
options = dict(self.options())
169
for o in ['thickness', 'zorder', 'legend_label', 'fill']:
170
options.pop(o, None)
171
return GraphicPrimitive_xydata._plot3d_options(self, options)
172
173
def plot3d(self, z=0, **kwds):
174
"""
175
Plots a 2D polygon in 3D, with default height zero.
176
177
INPUT:
178
179
180
- ``z`` - optional 3D height above `xy`-plane, or a list of
181
heights corresponding to the list of 2D polygon points.
182
183
EXAMPLES:
184
185
A pentagon::
186
187
sage: polygon([(cos(t), sin(t)) for t in srange(0, 2*pi, 2*pi/5)]).plot3d()
188
189
Showing behavior of the optional parameter z::
190
191
sage: P = polygon([(0,0), (1,2), (0,1), (-1,2)])
192
sage: p = P[0]; p
193
Polygon defined by 4 points
194
sage: q = p.plot3d()
195
sage: q.obj_repr(q.testing_render_params())[2]
196
['v 0 0 0', 'v 1 2 0', 'v 0 1 0', 'v -1 2 0']
197
sage: r = p.plot3d(z=3)
198
sage: r.obj_repr(r.testing_render_params())[2]
199
['v 0 0 3', 'v 1 2 3', 'v 0 1 3', 'v -1 2 3']
200
sage: s = p.plot3d(z=[0,1,2,3])
201
sage: s.obj_repr(s.testing_render_params())[2]
202
['v 0 0 0', 'v 1 2 1', 'v 0 1 2', 'v -1 2 3']
203
204
TESTS:
205
206
Heights passed as a list should have same length as
207
number of points::
208
209
sage: P = polygon([(0,0), (1,2), (0,1), (-1,2)])
210
sage: p = P[0]
211
sage: q = p.plot3d(z=[2,-2])
212
Traceback (most recent call last):
213
...
214
ValueError: Incorrect number of heights given
215
"""
216
from sage.plot.plot3d.index_face_set import IndexFaceSet
217
options = self._plot3d_options()
218
options.update(kwds)
219
zdata=[]
220
if type(z) is list:
221
zdata=z
222
else:
223
zdata=[z]*len(self.xdata)
224
if len(zdata)==len(self.xdata):
225
return IndexFaceSet([[(x, y, z) for x, y, z in zip(self.xdata, self.ydata, zdata)]], **options)
226
else:
227
raise ValueError, 'Incorrect number of heights given'
228
229
def _render_on_subplot(self, subplot):
230
"""
231
TESTS::
232
233
sage: P = polygon([(0,0), (1,2), (0,1), (-1,2)])
234
"""
235
import matplotlib.patches as patches
236
options = self.options()
237
p = patches.Polygon([(self.xdata[i],self.ydata[i]) for i in xrange(len(self.xdata))])
238
p.set_linewidth(float(options['thickness']))
239
a = float(options['alpha'])
240
z = int(options.pop('zorder', 1))
241
p.set_alpha(a)
242
f = options.pop('fill')
243
p.set_fill(f)
244
c = to_mpl_color(options['rgbcolor'])
245
p.set_edgecolor(c)
246
p.set_facecolor(c)
247
p.set_label(options['legend_label'])
248
p.set_zorder(z)
249
subplot.add_patch(p)
250
251
def polygon(points, **options):
252
"""
253
Returns either a 2-dimensional or 3-dimensional polygon depending
254
on value of points.
255
256
For information regarding additional arguments, see either :func:`polygon2d`
257
or :func:`~sage.plot.plot3d.shapes2.polygon3d`.
258
259
EXAMPLES::
260
261
sage: polygon([(0,0), (1,1), (0,1)])
262
sage: polygon([(0,0,1), (1,1,1), (2,0,1)])
263
264
Extra options will get passed on to show(), as long as they are valid::
265
266
sage: polygon([(0,0), (1,1), (0,1)], axes=False)
267
sage: polygon([(0,0), (1,1), (0,1)]).show(axes=False) # These are equivalent
268
"""
269
try:
270
return polygon2d(points, **options)
271
except ValueError:
272
from sage.plot.plot3d.shapes2 import polygon3d
273
return polygon3d(points, **options)
274
275
@rename_keyword(color='rgbcolor')
276
@options(alpha=1, rgbcolor=(0,0,1), thickness=None, legend_label=None, aspect_ratio=1.0, fill=True)
277
def polygon2d(points, **options):
278
r"""
279
Returns a 2-dimensional polygon defined by ``points``.
280
281
Type ``polygon.options`` for a dictionary of the default
282
options for polygons. You can change this to change
283
the defaults for all future polygons. Use ``polygon.reset()``
284
to reset to the default options.
285
286
EXAMPLES:
287
288
We create a purple-ish polygon::
289
290
sage: polygon2d([[1,2], [5,6], [5,0]], rgbcolor=(1,0,1))
291
292
By default, polygons are filled in, but we can make them
293
without a fill as well::
294
295
sage: polygon2d([[1,2], [5,6], [5,0]], fill=False)
296
297
In either case, the thickness of the border can be controlled::
298
299
sage: polygon2d([[1,2], [5,6], [5,0]], fill=False, thickness=4, color='orange')
300
301
Some modern art -- a random polygon, with legend::
302
303
sage: v = [(randrange(-5,5), randrange(-5,5)) for _ in range(10)]
304
sage: polygon2d(v, legend_label='some form')
305
306
A purple hexagon::
307
308
sage: L = [[cos(pi*i/3),sin(pi*i/3)] for i in range(6)]
309
sage: polygon2d(L, rgbcolor=(1,0,1))
310
311
A green deltoid::
312
313
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)]
314
sage: polygon2d(L, rgbcolor=(1/8,3/4,1/2))
315
316
A blue hypotrochoid::
317
318
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)]
319
sage: polygon2d(L, rgbcolor=(1/8,1/4,1/2))
320
321
Another one::
322
323
sage: n = 4; h = 5; b = 2
324
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)]
325
sage: polygon2d(L, rgbcolor=(1/8,1/4,3/4))
326
327
A purple epicycloid::
328
329
sage: m = 9; b = 1
330
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)]
331
sage: polygon2d(L, rgbcolor=(7/8,1/4,3/4))
332
333
A brown astroid::
334
335
sage: L = [[cos(pi*i/100)^3,sin(pi*i/100)^3] for i in range(200)]
336
sage: polygon2d(L, rgbcolor=(3/4,1/4,1/4))
337
338
And, my favorite, a greenish blob::
339
340
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)]
341
sage: polygon2d(L, rgbcolor=(1/8, 3/4, 1/2))
342
343
This one is for my wife::
344
345
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)]
346
sage: polygon2d(L, rgbcolor=(1,1/4,1/2))
347
348
Polygons have a default aspect ratio of 1.0::
349
350
sage: polygon2d([[1,2], [5,6], [5,0]]).aspect_ratio()
351
1.0
352
353
AUTHORS:
354
355
- David Joyner (2006-04-14): the long list of examples above.
356
357
"""
358
from sage.plot.plot import xydata_from_point_list
359
from sage.plot.all import Graphics
360
if options["thickness"] is None: # If the user did not specify thickness
361
if options["fill"]: # If the user chose fill
362
options["thickness"] = 0
363
else:
364
options["thickness"] = 1
365
xdata, ydata = xydata_from_point_list(points)
366
g = Graphics()
367
g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
368
g.add_primitive(Polygon(xdata, ydata, options))
369
if options['legend_label']:
370
g.legend(True)
371
return g
372
373