Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/plot/plot3d/platonic.py
4036 views
1
r"""
2
Platonic Solids
3
4
EXAMPLES: The five platonic solids in a row;
5
6
::
7
8
sage: G = tetrahedron((0,-3.5,0), color='blue') + cube((0,-2,0),color=(.25,0,.5)) +\
9
octahedron(color='red') + dodecahedron((0,2,0), color='orange') +\
10
icosahedron(center=(0,4,0), color='yellow')
11
sage: G.show(aspect_ratio=[1,1,1])
12
13
All the platonic solids in the same place::
14
15
sage: G = tetrahedron(color='blue',opacity=0.7) + \
16
cube(color=(.25,0,.5), opacity=0.7) +\
17
octahedron(color='red', opacity=0.7) + \
18
dodecahedron(color='orange', opacity=0.7) + icosahedron(opacity=0.7)
19
sage: G.show(aspect_ratio=[1,1,1])
20
21
Display nice faces only::
22
23
sage: icosahedron().stickers(['red','blue'], .075, .1)
24
25
AUTHORS:
26
27
- Robert Bradshaw (2007, 2008): initial version
28
29
- William Stein
30
"""
31
32
33
#*****************************************************************************
34
# Copyright (C) 2007 Robert Bradshaw <[email protected]>
35
#
36
# Distributed under the terms of the GNU General Public License (GPL)
37
#
38
# This code is distributed in the hope that it will be useful,
39
# but WITHOUT ANY WARRANTY; without even the implied warranty of
40
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41
# General Public License for more details.
42
#
43
# The full text of the GPL is available at:
44
#
45
# http://www.gnu.org/licenses/
46
#*****************************************************************************
47
48
49
50
from sage.rings.all import RDF
51
from sage.matrix.constructor import matrix
52
53
from shapes import Box, ColorCube
54
from shapes2 import frame3d
55
from index_face_set import IndexFaceSet
56
57
def index_face_set(face_list, point_list, enclosed, **kwds):
58
"""
59
Helper function that creates ``IndexFaceSet`` object for the
60
tetrahedron, dodecahedron, and icosahedron.
61
62
INPUT:
63
64
- ``face_list`` - list of faces, given explicitly from the
65
solid invocation
66
67
- ``point_list`` - list of points, given explicitly from the
68
solid invocation
69
70
- ``enclosed`` - boolean (default passed is always True
71
for these solids)
72
73
TESTS:
74
75
Verify that these are working and passing on keywords::
76
77
sage: tetrahedron(center=(2,0,0),size=2,color='red')
78
79
::
80
81
sage: dodecahedron(center=(2,0,0),size=2,color='red')
82
83
::
84
85
sage: icosahedron(center=(2,0,0),size=2,color='red')
86
"""
87
if kwds.has_key('center'):
88
center = kwds['center']
89
del kwds['center']
90
else:
91
center = (0,0,0)
92
if kwds.has_key('size'):
93
size = kwds['size']
94
del kwds['size']
95
else:
96
size = 1
97
I = IndexFaceSet(face_list, point_list, enclosed=enclosed, **kwds)
98
return prep(I, center, size, kwds)
99
100
def prep(G, center, size, kwds):
101
"""
102
Helper function that scales and translates the platonic
103
solid, and passes extra keywords on.
104
105
INPUT:
106
107
- ``center`` - 3-tuple indicating the center (default passed
108
from :func:`index_face_set` is the origin `(0,0,0)`)
109
110
- ``size`` - number indicating amount to scale by (default
111
passed from :func:`index_face_set` is 1)
112
113
- ``kwds`` - a dictionary of keywords, passed from solid
114
invocation by :func:`index_face_set`
115
116
TESTS:
117
118
Verify that scaling and moving the center work together properly,
119
and that keywords are passed (see Trac #10796)::
120
121
sage: octahedron(center=(2,0,0),size=2,color='red')
122
"""
123
if size != 1:
124
G = G.scale(size)
125
if center != (0,0,0):
126
G = G.translate(center)
127
G._set_extra_kwds(kwds)
128
return G
129
130
def tetrahedron(center=(0,0,0), size=1, **kwds):
131
"""
132
A 3d tetrahedron.
133
134
INPUT:
135
136
137
- ``center`` - (default: (0,0,0))
138
139
- ``size`` - (default: 1)
140
141
- ``color`` - a word that describes a color
142
143
- ``rgbcolor`` - (r,g,b) with r, g, b between 0 and 1
144
that describes a color
145
146
- ``opacity`` - (default: 1) if less than 1 then is
147
transparent
148
149
150
EXAMPLES: A default colored tetrahedron at the origin::
151
152
sage: tetrahedron()
153
154
A transparent green tetrahedron in front of a solid red one::
155
156
sage: tetrahedron(opacity=0.8, color='green') + tetrahedron((-2,1,0),color='red')
157
158
A translucent tetrahedron sharing space with a sphere::
159
160
sage: tetrahedron(color='yellow',opacity=0.7) + sphere(r=.5, color='red')
161
162
A big tetrahedron::
163
164
sage: tetrahedron(size=10)
165
166
A wide tetrahedron::
167
168
sage: tetrahedron(aspect_ratio=[1,1,1]).scale((4,4,1))
169
170
A red and blue tetrahedron touching noses::
171
172
sage: tetrahedron(color='red') + tetrahedron((0,0,-2)).scale([1,1,-1])
173
174
A Dodecahedral complex of 5 tetrahedrons (a more elaborate examples
175
from Peter Jipsen)::
176
177
sage: v=(sqrt(5.)/2-5/6, 5/6*sqrt(3.)-sqrt(15.)/2, sqrt(5.)/3)
178
sage: t=acos(sqrt(5.)/3)/2
179
sage: t1=tetrahedron(aspect_ratio=(1,1,1), opacity=0.5).rotateZ(t)
180
sage: t2=tetrahedron(color='red', opacity=0.5).rotateZ(t).rotate(v,2*pi/5)
181
sage: t3=tetrahedron(color='green', opacity=0.5).rotateZ(t).rotate(v,4*pi/5)
182
sage: t4=tetrahedron(color='yellow', opacity=0.5).rotateZ(t).rotate(v,6*pi/5)
183
sage: t5=tetrahedron(color='orange', opacity=0.5).rotateZ(t).rotate(v,8*pi/5)
184
sage: show(t1+t2+t3+t4+t5, frame=False, zoom=1.3)
185
186
AUTHORS:
187
188
- Robert Bradshaw and William Stein
189
"""
190
RR = RDF
191
one = RR(1)
192
sqrt2 = RR(2).sqrt()
193
sqrt6 = RR(6).sqrt()
194
point_list = [(0,0,1),
195
(2*sqrt2/3, 0, -one/3),
196
( -sqrt2/3, sqrt6/3, -one/3),
197
( -sqrt2/3, -sqrt6/3, -one/3)]
198
face_list = [[0,1,2],[1,3,2],[0,2,3],[0,3,1]]
199
if 'aspect_ratio' not in kwds:
200
kwds['aspect_ratio'] = [1,1,1]
201
return index_face_set(face_list, point_list, enclosed=True, center=center, size=size, **kwds)
202
203
def cube(center=(0,0,0), size=1, color=None, frame_thickness=0, frame_color=None, **kwds):
204
"""
205
A 3D cube centered at the origin with default side lengths 1.
206
207
INPUT:
208
209
210
- ``center`` - (default: (0,0,0))
211
212
- ``size`` - (default: 1) the side lengths of the
213
cube
214
215
- ``color`` - a string that describes a color; this
216
can also be a list of 3-tuples or strings length 6 or 3, in which
217
case the faces (and oppositive faces) are colored.
218
219
- ``frame_thickness`` - (default: 0) if positive,
220
then thickness of the frame
221
222
- ``frame_color`` - (default: None) if given, gives
223
the color of the frame
224
225
- ``opacity`` - (default: 1) if less than 1 then it's
226
transparent
227
228
229
EXAMPLES:
230
231
A simple cube::
232
233
sage: cube()
234
235
A red cube::
236
237
sage: cube(color="red")
238
239
A transparent grey cube that contains a red cube::
240
241
sage: cube(opacity=0.8, color='grey') + cube(size=3/4)
242
243
A transparent colored cube::
244
245
sage: cube(color=['red', 'green', 'blue'], opacity=0.5)
246
247
A bunch of random cubes::
248
249
sage: v = [(random(), random(), random()) for _ in [1..30]]
250
sage: sum([cube((10*a,10*b,10*c), size=random()/3, color=(a,b,c)) for a,b,c in v])
251
252
Non-square cubes (boxes)::
253
254
sage: cube(aspect_ratio=[1,1,1]).scale([1,2,3])
255
sage: cube(color=['red', 'blue', 'green'],aspect_ratio=[1,1,1]).scale([1,2,3])
256
257
And one that is colored::
258
259
sage: cube(color=['red', 'blue', 'green', 'black', 'white', 'orange'], \
260
aspect_ratio=[1,1,1]).scale([1,2,3])
261
262
A nice translucent color cube with a frame::
263
264
sage: c = cube(color=['red', 'blue', 'green'], frame=False, frame_thickness=2, \
265
frame_color='brown', opacity=0.8)
266
sage: c
267
268
A raytraced color cube with frame and transparency::
269
270
sage: c.show(viewer='tachyon')
271
272
This shows #11272 has been fixed::
273
274
sage: cube(center=(10, 10, 10), size=0.5).bounding_box()
275
((9.75, 9.75, 9.75), (10.25, 10.25, 10.25))
276
277
AUTHORS:
278
279
- William Stein
280
"""
281
if isinstance(color, (list, tuple)) and len(color) > 0 and isinstance(color[0], (list,tuple,str)):
282
B = ColorCube(size=[0.5,0.5,0.5], colors=color, **kwds)
283
else:
284
if color is not None:
285
kwds['color'] = color
286
B = Box(0.5,0.5,0.5, **kwds)
287
if frame_thickness > 0:
288
if frame_color is None:
289
B += frame3d((-0.5,-0.5,-0.5),(0.5,0.5,0.5), thickness=frame_thickness)
290
else:
291
B += frame3d((-0.5,-0.5,-0.5),(0.5,0.5,0.5), thickness=frame_thickness, color=frame_color)
292
return prep(B, center, size, kwds)
293
294
def octahedron(center=(0,0,0), size=1, **kwds):
295
r"""
296
Return an octahedron.
297
298
INPUT:
299
300
301
- ``center`` - (default: (0,0,0))
302
303
- ``size`` - (default: 1)
304
305
- ``color`` - a string that describes a color; this
306
can also be a list of 3-tuples or strings length 6 or 3, in which
307
case the faces (and oppositive faces) are colored.
308
309
- ``opacity`` - (default: 1) if less than 1 then is
310
transparent
311
312
313
EXAMPLES::
314
315
sage: octahedron((1,4,3), color='orange') + \
316
octahedron((0,2,1), size=2, opacity=0.6)
317
"""
318
if 'aspect_ratio' not in kwds:
319
kwds['aspect_ratio'] = [1,1,1]
320
return prep(Box(1,1,1).dual(**kwds), center, size, kwds)
321
322
def dodecahedron(center=(0,0,0), size=1, **kwds):
323
r"""
324
A dodecahedron.
325
326
INPUT:
327
328
329
- ``center`` - (default: (0,0,0))
330
331
- ``size`` - (default: 1)
332
333
- ``color`` - a string that describes a color; this
334
can also be a list of 3-tuples or strings length 6 or 3, in which
335
case the faces (and oppositive faces) are colored.
336
337
- ``opacity`` - (default: 1) if less than 1 then is
338
transparent
339
340
341
EXAMPLES: A plain Dodecahedron::
342
343
sage: dodecahedron()
344
345
A translucent dodecahedron that contains a black sphere::
346
347
sage: dodecahedron(color='orange', opacity=0.8) + \
348
sphere(size=0.5, color='black')
349
350
CONSTRUCTION: This is how we construct a dodecahedron. We let one
351
point be `Q = (0,1,0)`.
352
353
Now there are three points spaced equally on a circle around the
354
north pole. The other requirement is that the angle between them be
355
the angle of a pentagon, namely `3\pi/5`. This is enough to
356
determine them. Placing one on the `xz`-plane we have.
357
358
`P_1 = \left(t, 0, \sqrt{1-t^2}\right)`
359
360
`P_2 = \left(-\frac{1}{2}t, \frac{\sqrt{3}}{2}t, \sqrt{1-t^2}\right)`
361
362
`P_3 = \left(-\frac{1}{2}t, \frac{\sqrt{3}}{2}t, \sqrt{1-t^2}\right)`
363
364
Solving
365
`\frac{(P_1-Q) \cdot (P_2-Q)}{|P_1-Q||P_2-Q|} = \cos(3\pi/5)`
366
we get `t = 2/3`.
367
368
Now we have 6 points `R_1, ..., R_6` to close the three
369
top pentagons. These can be found by mirroring `P_2` and
370
`P_3` by the `yz`-plane and rotating around the
371
`y`-axis by the angle `\theta` from `Q` to
372
`P_1`. Note that `\cos(\theta) = t = 2/3` and so
373
`\sin(\theta) = \sqrt{5}/3`. Rotation gives us the other
374
four.
375
376
Now we reflect through the origin for the bottom half.
377
378
AUTHORS:
379
380
- Robert Bradshaw, William Stein
381
"""
382
RR = RDF
383
one = RR(1)
384
sqrt3 = RR(3).sqrt();
385
sqrt5 = RR(5).sqrt()
386
R3 = RR**3
387
rot = matrix(RR, [[ -one/2,-sqrt3/2, 0],
388
[ sqrt3/2, -one/2, 0],
389
[ 0, 0, 1]])
390
rot2 = rot*rot
391
392
# The top
393
Q = R3([0,0,1])
394
# The first ring
395
P1 = R3([2*one/3, 0, sqrt5/3])
396
# The second ring
397
R1 = R3([sqrt5/3, 1/sqrt3, one/3])
398
R2 = R3([sqrt5/3, -1/sqrt3, one/3])
399
400
top = [Q, P1, rot*P1, rot2*P1, R1, rot*R2, rot*R1, rot2*R2, rot2*R1, R2]
401
point_list = top + [-p for p in reversed(top)]
402
403
top_faces = [[0,1,4,5,2],
404
[0,2,6,7,3],
405
[0,3,8,9,1],
406
[1,9,13,12,4],
407
[2,5,11,10,6],
408
[3,7,15,14,8]]
409
face_list = top_faces + [[19-p for p in reversed(f)] for f in top_faces]
410
411
if 'aspect_ratio' not in kwds:
412
kwds['aspect_ratio'] = [1,1,1]
413
return index_face_set(face_list, point_list, enclosed=True, center=center, size=size, **kwds)
414
415
# if style == 'vertices' or style == 'edges':
416
# from sage.plot.colors import rainbow
417
# colors = rainbow(len(vs), 'rgbtuple')
418
# #vertex_spheres = [Box(.05, .05, .05, color=color).translate(p) for p in vs]
419
# vertex_spheres = [Box(.05, .05, .05, color=c).translate(p) for p,c in zip(vs,colors)]
420
# faces = IndexFaceSet([[tuple(vs[i]) for i in f] for f in face_list])
421
# vertex_spheres += [faces.stickers(['red','yellow','blue','purple','black','orange'], .1, .1)] # [faces]
422
# return Graphics3dGroup(vertex_spheres)
423
424
425
def icosahedron(center=(0,0,0), size=1, **kwds):
426
r"""
427
An icosahedron.
428
429
INPUT:
430
431
432
- ``center`` - (default: (0,0,0))
433
434
- ``size`` - (default: 1)
435
436
- ``color`` - a string that describes a color; this
437
can also be a list of 3-tuples or strings length 6 or 3, in which
438
case the faces (and oppositive faces) are colored.
439
440
- ``opacity`` - (default: 1) if less than 1 then is
441
transparent
442
443
444
EXAMPLES::
445
446
sage: icosahedron()
447
448
Two icosahedrons at different positions of different sizes.
449
450
::
451
452
sage: icosahedron((-1/2,0,1), color='orange') + \
453
icosahedron((2,0,1), size=1/2, aspect_ratio=[1,1,1])
454
"""
455
if 'aspect_ratio' not in kwds:
456
kwds['aspect_ratio'] = [1,1,1]
457
return prep(dodecahedron().dual(**kwds), center, size, kwds)
458
459