passagemath-plot 10.6.31rc3__cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-plot might be problematic. Click here for more details.

Files changed (81) hide show
  1. passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
  2. passagemath_plot-10.6.31rc3.dist-info/RECORD +81 -0
  3. passagemath_plot-10.6.31rc3.dist-info/WHEEL +6 -0
  4. passagemath_plot-10.6.31rc3.dist-info/top_level.txt +2 -0
  5. passagemath_plot.libs/libgfortran-e1b7dfc8.so.5.0.0 +0 -0
  6. passagemath_plot.libs/libgsl-e3525837.so.28.0.0 +0 -0
  7. passagemath_plot.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
  8. sage/all__sagemath_plot.py +15 -0
  9. sage/ext_data/threejs/animation.css +195 -0
  10. sage/ext_data/threejs/animation.html +85 -0
  11. sage/ext_data/threejs/animation.js +273 -0
  12. sage/ext_data/threejs/fat_lines.js +48 -0
  13. sage/ext_data/threejs/threejs-version.txt +1 -0
  14. sage/ext_data/threejs/threejs_template.html +597 -0
  15. sage/interfaces/all__sagemath_plot.py +1 -0
  16. sage/interfaces/gnuplot.py +196 -0
  17. sage/interfaces/jmoldata.py +208 -0
  18. sage/interfaces/povray.py +56 -0
  19. sage/plot/all.py +42 -0
  20. sage/plot/animate.py +1796 -0
  21. sage/plot/arc.py +504 -0
  22. sage/plot/arrow.py +671 -0
  23. sage/plot/bar_chart.py +205 -0
  24. sage/plot/bezier_path.py +400 -0
  25. sage/plot/circle.py +435 -0
  26. sage/plot/colors.py +1606 -0
  27. sage/plot/complex_plot.cpython-314-aarch64-linux-gnu.so +0 -0
  28. sage/plot/complex_plot.pyx +1446 -0
  29. sage/plot/contour_plot.py +1792 -0
  30. sage/plot/density_plot.py +318 -0
  31. sage/plot/disk.py +373 -0
  32. sage/plot/ellipse.py +375 -0
  33. sage/plot/graphics.py +3580 -0
  34. sage/plot/histogram.py +354 -0
  35. sage/plot/hyperbolic_arc.py +404 -0
  36. sage/plot/hyperbolic_polygon.py +416 -0
  37. sage/plot/hyperbolic_regular_polygon.py +296 -0
  38. sage/plot/line.py +626 -0
  39. sage/plot/matrix_plot.py +629 -0
  40. sage/plot/misc.py +509 -0
  41. sage/plot/multigraphics.py +1294 -0
  42. sage/plot/plot.py +4183 -0
  43. sage/plot/plot3d/all.py +23 -0
  44. sage/plot/plot3d/base.cpython-314-aarch64-linux-gnu.so +0 -0
  45. sage/plot/plot3d/base.pxd +12 -0
  46. sage/plot/plot3d/base.pyx +3378 -0
  47. sage/plot/plot3d/implicit_plot3d.py +659 -0
  48. sage/plot/plot3d/implicit_surface.cpython-314-aarch64-linux-gnu.so +0 -0
  49. sage/plot/plot3d/implicit_surface.pyx +1453 -0
  50. sage/plot/plot3d/index_face_set.cpython-314-aarch64-linux-gnu.so +0 -0
  51. sage/plot/plot3d/index_face_set.pxd +32 -0
  52. sage/plot/plot3d/index_face_set.pyx +1873 -0
  53. sage/plot/plot3d/introduction.py +131 -0
  54. sage/plot/plot3d/list_plot3d.py +649 -0
  55. sage/plot/plot3d/parametric_plot3d.py +1130 -0
  56. sage/plot/plot3d/parametric_surface.cpython-314-aarch64-linux-gnu.so +0 -0
  57. sage/plot/plot3d/parametric_surface.pxd +12 -0
  58. sage/plot/plot3d/parametric_surface.pyx +893 -0
  59. sage/plot/plot3d/platonic.py +601 -0
  60. sage/plot/plot3d/plot3d.py +1442 -0
  61. sage/plot/plot3d/plot_field3d.py +162 -0
  62. sage/plot/plot3d/point_c.pxi +148 -0
  63. sage/plot/plot3d/revolution_plot3d.py +309 -0
  64. sage/plot/plot3d/shapes.cpython-314-aarch64-linux-gnu.so +0 -0
  65. sage/plot/plot3d/shapes.pxd +22 -0
  66. sage/plot/plot3d/shapes.pyx +1382 -0
  67. sage/plot/plot3d/shapes2.py +1512 -0
  68. sage/plot/plot3d/tachyon.py +1779 -0
  69. sage/plot/plot3d/texture.py +453 -0
  70. sage/plot/plot3d/transform.cpython-314-aarch64-linux-gnu.so +0 -0
  71. sage/plot/plot3d/transform.pxd +21 -0
  72. sage/plot/plot3d/transform.pyx +268 -0
  73. sage/plot/plot3d/tri_plot.py +589 -0
  74. sage/plot/plot_field.py +362 -0
  75. sage/plot/point.py +624 -0
  76. sage/plot/polygon.py +562 -0
  77. sage/plot/primitive.py +249 -0
  78. sage/plot/scatter_plot.py +199 -0
  79. sage/plot/step.py +85 -0
  80. sage/plot/streamline_plot.py +328 -0
  81. sage/plot/text.py +432 -0
@@ -0,0 +1,1382 @@
1
+ # sage_setup: distribution = sagemath-plot
2
+ """
3
+ Basic objects such as Sphere, Box, Cone, etc.
4
+
5
+ AUTHORS:
6
+
7
+ - Robert Bradshaw 2007-02: initial version
8
+ - Robert Bradshaw 2007-08: obj/tachyon rendering, much updating
9
+ - Robert Bradshaw 2007-08: cythonization
10
+
11
+ EXAMPLES::
12
+
13
+ sage: from sage.plot.plot3d.shapes import *
14
+ sage: S = Sphere(.5, color='yellow')
15
+ sage: S += Cone(.5, .5, color='red').translate(0,0,.3)
16
+ sage: S += Sphere(.1, color='white').translate(.45,-.1,.15)
17
+ sage: S += Sphere(.05, color='black').translate(.51,-.1,.17)
18
+ sage: S += Sphere(.1, color='white').translate(.45, .1,.15)
19
+ sage: S += Sphere(.05, color='black').translate(.51, .1,.17)
20
+ sage: S += Sphere(.1, color='yellow').translate(.5, 0, -.2)
21
+ sage: S.show()
22
+ sage: S.scale(1,1,2).show()
23
+
24
+ .. PLOT::
25
+
26
+ from sage.plot.plot3d.shapes import *
27
+ S = Sphere(.5, color='yellow')
28
+ S += Cone(.5, .5, color='red').translate(0,0,.3)
29
+ S += Sphere(.1, color='white').translate(.45,-.1,.15) + Sphere(.05, color='black').translate(.51,-.1,.17)
30
+ S += Sphere(.1, color='white').translate(.45, .1,.15) + Sphere(.05, color='black').translate(.51, .1,.17)
31
+ S += Sphere(.1, color='yellow').translate(.5, 0, -.2)
32
+ sphinx_plot(S)
33
+
34
+ ::
35
+
36
+ sage: from sage.plot.plot3d.shapes import *
37
+ sage: Torus(.7, .2, color=(0,.3,0)).show()
38
+
39
+ .. PLOT::
40
+
41
+ from sage.plot.plot3d.shapes import *
42
+ sphinx_plot(Torus(.7, .2, color=(0,.3,0)))
43
+ """
44
+
45
+ # ****************************************************************************
46
+ # Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu>
47
+ #
48
+ # This program is free software: you can redistribute it and/or modify
49
+ # it under the terms of the GNU General Public License as published by
50
+ # the Free Software Foundation, either version 2 of the License, or
51
+ # (at your option) any later version.
52
+ # https://www.gnu.org/licenses/
53
+ # ****************************************************************************
54
+
55
+ from libc.math cimport sqrt, sin, cos, acos, M_PI
56
+ from sage.rings.real_double import RDF
57
+ from sage.modules.free_module_element import vector
58
+ from sage.misc.decorators import rename_keyword
59
+ from sage.plot.plot3d.base import Graphics3dGroup
60
+ from sage.plot.plot3d.index_face_set cimport IndexFaceSet, PrimitiveObject
61
+ from sage.plot.plot3d.transform cimport point_c
62
+
63
+
64
+ # Helper function to check that Box input is right
65
+ def validate_frame_size(size):
66
+ """
67
+ Check that the input is an iterable of length 3 with all
68
+ elements nonnegative and coercible to floats.
69
+
70
+ EXAMPLES::
71
+
72
+ sage: from sage.plot.plot3d.shapes import validate_frame_size
73
+ sage: validate_frame_size([3,2,1])
74
+ [3.0, 2.0, 1.0]
75
+
76
+ TESTS::
77
+
78
+ sage: from sage.plot.plot3d.shapes import validate_frame_size
79
+ sage: validate_frame_size([3,2,-1])
80
+ Traceback (most recent call last):
81
+ ...
82
+ ValueError: each box dimension must be nonnegative
83
+ sage: validate_frame_size([sqrt(-1),3,2]) # needs sage.symbolic
84
+ Traceback (most recent call last):
85
+ ...
86
+ TypeError: each box dimension must coerce to a float
87
+ """
88
+ if not isinstance(size, (list, tuple)):
89
+ raise TypeError("size must be a list or tuple")
90
+ if len(size) != 3:
91
+ raise TypeError("size must be of length 3")
92
+ try:
93
+ size = [float(x) for x in size]
94
+ except TypeError:
95
+ raise TypeError("each box dimension must coerce to a float")
96
+ for x in size:
97
+ if x < 0:
98
+ raise ValueError("each box dimension must be nonnegative")
99
+ return size
100
+
101
+
102
+ class Box(IndexFaceSet):
103
+ """
104
+ Return a box.
105
+
106
+ EXAMPLES::
107
+
108
+ sage: from sage.plot.plot3d.shapes import Box
109
+
110
+ A square black box::
111
+
112
+ sage: show(Box([1,1,1]), color='black')
113
+
114
+ .. PLOT::
115
+
116
+ from sage.plot.plot3d.shapes import Box
117
+ sphinx_plot(Box([1,1,1], color='black'))
118
+
119
+ A red rectangular box::
120
+
121
+ sage: show(Box([2,3,4], color='red'))
122
+
123
+ .. PLOT::
124
+
125
+ from sage.plot.plot3d.shapes import Box
126
+ sphinx_plot(Box([2,3,4], color='red'))
127
+
128
+ A stack of boxes::
129
+
130
+ sage: show(sum(Box([2,3,1], color='red').translate((0,0,6*i))
131
+ ....: for i in [0..3]))
132
+
133
+ .. PLOT::
134
+
135
+ from sage.plot.plot3d.shapes import Box
136
+ P = sum([Box([2,3,1], color='red').translate((0,0,6*i)) for i in range(0,4)])
137
+ sphinx_plot(P)
138
+
139
+ A sinusoidal stack of multicolored boxes::
140
+
141
+ sage: B = sum(Box([2,4,1/4], # needs sage.symbolic
142
+ ....: color=(i/4,i/5,1)).translate((sin(i),0,5-i))
143
+ ....: for i in [0..20])
144
+ sage: show(B, figsize=6) # needs sage.symbolic
145
+
146
+ .. PLOT::
147
+
148
+ from sage.plot.plot3d.shapes import Box
149
+ B = sum([Box([2,4,1/4], color=(i/4.0,i/5.0,1)).translate((sin(i),0,5-i)) for i in range(0,21)])
150
+ sphinx_plot(B)
151
+ """
152
+ def __init__(self, *size, **kwds):
153
+ """
154
+ EXAMPLES::
155
+
156
+ sage: from sage.plot.plot3d.shapes import Box
157
+ sage: Box(10, 1, 1) + Box(1, 10, 1) + Box(1, 1, 10)
158
+ Graphics3d Object
159
+ """
160
+ if isinstance(size[0], (tuple, list)):
161
+ size = validate_frame_size(size[0])
162
+ self.size = size
163
+ x, y, z = self.size
164
+ faces = [[(x, y, z), (-x, y, z), (-x,-y, z), ( x,-y, z)],
165
+ [(x, y, z), ( x, y,-z), (-x, y,-z), (-x, y, z)],
166
+ [(x, y, z), ( x,-y, z), ( x,-y,-z), ( x, y,-z)] ]
167
+ faces += [list(reversed([(-x,-y,-z) for x,y,z in face])) for face in faces]
168
+ IndexFaceSet.__init__(self, faces, enclosed=True, **kwds)
169
+
170
+ def bounding_box(self):
171
+ """
172
+ EXAMPLES::
173
+
174
+ sage: from sage.plot.plot3d.shapes import Box
175
+ sage: Box([1,2,3]).bounding_box()
176
+ ((-1.0, -2.0, -3.0), (1.0, 2.0, 3.0))
177
+ """
178
+ return tuple([-a for a in self.size]), tuple(self.size)
179
+
180
+ def x3d_geometry(self):
181
+ """
182
+ EXAMPLES::
183
+
184
+ sage: from sage.plot.plot3d.shapes import Box
185
+ sage: Box([1,2,1/4]).x3d_geometry()
186
+ "<Box size='1.0 2.0 0.25'/>"
187
+ """
188
+ return "<Box size='%s %s %s'/>" % tuple(self.size)
189
+
190
+
191
+ @rename_keyword(alpha='opacity')
192
+ def ColorCube(size, colors, opacity=1, **kwds):
193
+ """
194
+ Return a cube with given size and sides with given colors.
195
+
196
+ INPUT:
197
+
198
+ - ``size`` -- 3-tuple of sizes (same as for box and frame)
199
+ - ``colors`` -- list of either 3 or 6 colors
200
+ - ``opacity`` -- (default: 1) opacity of cube sides
201
+ - ``**kwds`` -- passed to the face constructor
202
+
203
+ OUTPUT: a 3d graphics object
204
+
205
+ EXAMPLES:
206
+
207
+ A color cube with translucent sides::
208
+
209
+ sage: from sage.plot.plot3d.shapes import ColorCube
210
+ sage: c = ColorCube([1,2,3], ['red', 'blue', 'green', 'black', 'white', 'orange'], opacity=0.5)
211
+ sage: c.show()
212
+
213
+ .. PLOT::
214
+
215
+ from sage.plot.plot3d.shapes import ColorCube
216
+ c = ColorCube([1,2,3], ['red', 'blue', 'green', 'black', 'white', 'orange'], opacity=0.5)
217
+ sphinx_plot(c)
218
+
219
+ ::
220
+
221
+ sage: list(c.texture_set())[0].opacity
222
+ 0.5
223
+
224
+ If you omit the last 3 colors then the first three are repeated (with
225
+ repeated colors on opposing faces)::
226
+
227
+ sage: c = ColorCube([0.5,0.5,0.5], ['red', 'blue', 'green'])
228
+
229
+ .. PLOT::
230
+
231
+ from sage.plot.plot3d.shapes import ColorCube
232
+ c = ColorCube([0.5,0.5,0.5], ['red', 'blue', 'green'])
233
+ sphinx_plot(c)
234
+ """
235
+ if not isinstance(size, (tuple, list)):
236
+ size = (size, size, size)
237
+ box = Box(size)
238
+ faces = box.face_list()
239
+ if len(colors) == 3:
240
+ colors = colors * 2
241
+ all = []
242
+ kwds['opacity'] = opacity
243
+
244
+ from sage.plot.plot3d.texture import Texture
245
+ for k in range(6):
246
+ all.append(IndexFaceSet([faces[k]], enclosed=True,
247
+ texture=Texture(colors[k], opacity=opacity),
248
+ **kwds))
249
+ return Graphics3dGroup(all)
250
+
251
+
252
+ cdef class Cone(ParametricSurface):
253
+ """
254
+ A cone, with base in the xy-plane pointing up the z-axis.
255
+
256
+ INPUT:
257
+
258
+ - ``radius`` -- positive real number
259
+
260
+ - ``height`` -- positive real number
261
+
262
+ - ``closed`` -- whether or not to include the base (default: ``True``)
263
+
264
+ - ``**kwds`` -- passed to the ParametricSurface constructor
265
+
266
+ EXAMPLES::
267
+
268
+ sage: from sage.plot.plot3d.shapes import Cone
269
+ sage: c = Cone(3/2, 1, color='red')
270
+ sage: c += Cone(1, 2, color='yellow').translate(3, 0, 0)
271
+ sage: c.show(aspect_ratio=1)
272
+
273
+ .. PLOT::
274
+
275
+ from sage.plot.plot3d.shapes import Cone
276
+ c = Cone(3/2, 1, color='red') + Cone(1, 2, color='yellow').translate(3, 0, 0)
277
+ sphinx_plot(c)
278
+
279
+ We may omit the base::
280
+
281
+ sage: Cone(1, 1, closed=False)
282
+ Graphics3d Object
283
+
284
+ .. PLOT::
285
+
286
+ from sage.plot.plot3d.shapes import Cone
287
+ sphinx_plot(Cone(1, 1, closed=False))
288
+
289
+ A spiky plot of the sine function::
290
+
291
+ sage: sum(Cone(.1, sin(n), color='yellow').translate(n, sin(n), 0) # needs sage.symbolic
292
+ ....: for n in [0..10, step=.1])
293
+ Graphics3d Object
294
+
295
+ .. PLOT::
296
+
297
+ from sage.plot.plot3d.shapes import Cone
298
+ sphinx_plot(sum(Cone(.1, sin(n/10.0), color='yellow').translate(n/10.0, sin(n/10.0), 0) for n in range(0,100)))
299
+
300
+ A Christmas tree::
301
+
302
+ sage: T = sum(Cone(exp(-n/5), 4/3*exp(-n/5), # needs sage.symbolic
303
+ ....: color=(0, .5, 0)).translate(0, 0, -3*exp(-n/5))
304
+ ....: for n in [1..7])
305
+ sage: T += Cone(1/8, 1, color='brown').translate(0, 0, -3) # needs sage.symbolic
306
+ sage: T.show(aspect_ratio=1, frame=False) # needs sage.symbolic
307
+
308
+ .. PLOT::
309
+
310
+ from sage.plot.plot3d.shapes import Cone
311
+ T = sum(Cone(exp(-n/5.0), 4/3*exp(-n/5.0), color=(0, .5, 0)).translate(0, 0, -3*exp(-n/5.0)) for n in range(8))
312
+ T += Cone(1/8, 1, color='brown').translate(0, 0, -3)
313
+ sphinx_plot(T)
314
+ """
315
+ def __init__(self, radius, height, closed=True, **kwds):
316
+ """
317
+ TESTS::
318
+
319
+ sage: from sage.plot.plot3d.shapes import Cone
320
+ sage: c = Cone(1/2, 1, opacity=.5)
321
+ """
322
+ ParametricSurface.__init__(self, **kwds)
323
+ self.radius = radius
324
+ self.height = height
325
+ self.closed = closed
326
+
327
+ def x3d_geometry(self):
328
+ """
329
+ EXAMPLES::
330
+
331
+ sage: from sage.plot.plot3d.shapes import Cone
332
+ sage: Cone(1, 3).x3d_geometry()
333
+ "<Cone bottomRadius='1.0' height='3.0'/>"
334
+ """
335
+ return "<Cone bottomRadius='%s' height='%s'/>" % (self.radius,
336
+ self.height)
337
+
338
+ def get_grid(self, ds):
339
+ """
340
+ Return the grid on which to evaluate this parametric surface.
341
+
342
+ EXAMPLES::
343
+
344
+ sage: from sage.plot.plot3d.shapes import Cone
345
+ sage: Cone(1, 3, closed=True).get_grid(100)
346
+ ([1, 0, -1], [0.0, 1.2566..., 2.5132..., 3.7699..., 5.0265..., 0.0])
347
+ sage: Cone(1, 3, closed=False).get_grid(100)
348
+ ([1, 0], [0.0, 1.2566..., 2.5132..., 3.7699..., 5.0265..., 0.0])
349
+ sage: len(Cone(1, 3).get_grid(.001)[1])
350
+ 38
351
+ """
352
+ cdef int k, t_res = min(max(int(2*M_PI*self.radius/ds), 5), 37)
353
+ if self.closed:
354
+ urange = [1,0,-1]
355
+ else:
356
+ urange = [1,0]
357
+ vrange = [2*M_PI*k/t_res for k in range(t_res)] + [0.0]
358
+ return urange, vrange
359
+
360
+ cdef int eval_c(self, point_c *res, double u, double v) except -1:
361
+ if u == -1:
362
+ res.x, res.y, res.z = 0, 0, 0
363
+ elif u == 0:
364
+ res.x = self.radius*sin(v)
365
+ res.y = self.radius*cos(v)
366
+ res.z = 0
367
+ else: # u == 1:
368
+ res.x, res.y, res.z = 0, 0, self.height
369
+
370
+
371
+ cdef class Cylinder(ParametricSurface):
372
+ """
373
+ A cone, with base in the xy-plane pointing up the z-axis.
374
+
375
+ INPUT:
376
+
377
+ - ``radius`` -- positive real number
378
+
379
+ - ``height`` -- positive real number
380
+
381
+ - ``closed`` -- whether or not to include the ends (default: ``True``)
382
+
383
+ - ``**kwds`` -- passed to the :class:`ParametricSurface` constructor
384
+
385
+ EXAMPLES::
386
+
387
+ sage: from sage.plot.plot3d.shapes import Cylinder
388
+ sage: c = Cylinder(3/2, 1, color='red')
389
+ sage: c += Cylinder(1, 2, color='yellow').translate(3, 0, 0)
390
+ sage: c.show(aspect_ratio=1)
391
+
392
+ .. PLOT::
393
+
394
+ from sage.plot.plot3d.shapes import Cylinder
395
+ c = Cylinder(3/2, 1, color='red') + Cylinder(1, 2, color='yellow').translate(3, 0, 0)
396
+ sphinx_plot(c)
397
+
398
+ We may omit the base::
399
+
400
+ sage: Cylinder(1, 1, closed=False)
401
+ Graphics3d Object
402
+
403
+ .. PLOT::
404
+
405
+ from sage.plot.plot3d.shapes import Cylinder
406
+ sphinx_plot(Cylinder(1, 1, closed=False))
407
+
408
+ Some gears::
409
+
410
+ sage: # needs sage.symbolic
411
+ sage: G = Cylinder(1, .5) + Cylinder(.25, 3).translate(0, 0, -3)
412
+ sage: G += sum(Cylinder(.2, 1).translate(cos(2*pi*n/9), sin(2*pi*n/9), 0)
413
+ ....: for n in [1..9])
414
+ sage: G += G.translate(2.3, 0, -.5)
415
+ sage: G += G.translate(3.5, 2, -1)
416
+ sage: G.show(aspect_ratio=1, frame=False)
417
+
418
+ .. PLOT::
419
+
420
+ from sage.plot.plot3d.shapes import Cylinder
421
+ G = Cylinder(1, .5) + Cylinder(.25, 3).translate(0, 0, -3)
422
+ G += sum(Cylinder(.2, 1).translate(cos(2*pi*n/9.0), sin(2*pi*n/9.0), 0) for n in range(10))
423
+ G += G.translate(2.3, 0, -.5)
424
+ G += G.translate(3.5, 2, -1)
425
+ sphinx_plot(G)
426
+ """
427
+ def __init__(self, radius, height, closed=True, **kwds):
428
+ """
429
+ TESTS::
430
+
431
+ sage: from sage.plot.plot3d.shapes import Cylinder
432
+ sage: Cylinder(1, 1, color='red')
433
+ Graphics3d Object
434
+ """
435
+ ParametricSurface.__init__(self, **kwds)
436
+ self.radius = radius
437
+ self.height = height
438
+ self.closed = closed
439
+
440
+ def bounding_box(self):
441
+ """
442
+ EXAMPLES::
443
+
444
+ sage: from sage.plot.plot3d.shapes import Cylinder
445
+ sage: Cylinder(1, 2).bounding_box()
446
+ ((-1.0, -1.0, 0), (1.0, 1.0, 2.0))
447
+ """
448
+ return ((-self.radius, -self.radius, 0),
449
+ (self.radius, self.radius, self.height))
450
+
451
+ def x3d_geometry(self):
452
+ """
453
+ EXAMPLES::
454
+
455
+ sage: from sage.plot.plot3d.shapes import Cylinder
456
+ sage: Cylinder(1, 2).x3d_geometry()
457
+ "<Cylinder radius='1.0' height='2.0'/>"
458
+ """
459
+ return "<Cylinder radius='%s' height='%s'/>" % (self.radius,
460
+ self.height)
461
+
462
+ def tachyon_repr(self, render_params):
463
+ r"""
464
+ EXAMPLES::
465
+
466
+ sage: from sage.plot.plot3d.shapes import Cylinder
467
+ sage: C = Cylinder(1/2, 4, closed=False)
468
+ sage: C.tachyon_repr(C.default_render_params())
469
+ 'FCylinder\n Base 0 0 0\n Apex 0 0 4.0\n Rad 0.5\n texture... '
470
+ sage: C = Cylinder(1, 2)
471
+ sage: C.tachyon_repr(C.default_render_params())
472
+ ['Ring Center 0 0 0 Normal 0 0 1 Inner 0 Outer 1.0 texture...',
473
+ 'FCylinder\n Base 0 0 0\n Apex 0 0 2.0\n Rad 1.0\n texture... ',
474
+ 'Ring Center 0 0 2.0 Normal 0 0 1 Inner 0 Outer 1.0 texture...']
475
+ """
476
+ transform = render_params.transform
477
+ if not (transform is None or transform.is_uniform_on([(1,0,0),(0,1,0)])):
478
+ # Tachyon can't do squashed
479
+ return ParametricSurface.tachyon_repr(self, render_params)
480
+
481
+ base, top = self.get_endpoints(transform)
482
+ rad = self.get_radius(transform)
483
+ cyl = """FCylinder
484
+ Base %s %s %s
485
+ Apex %s %s %s
486
+ Rad %s
487
+ %s """ % (base[0], base[1], base[2], top[0], top[1], top[2], rad, self.texture.id)
488
+ if self.closed:
489
+ normal = (0,0,1)
490
+ if transform is not None:
491
+ normal = transform.transform_vector(normal)
492
+ base_cap = """Ring Center %s %s %s Normal %s %s %s Inner 0 Outer %s %s""" \
493
+ % (base[0], base[1], base[2], normal[0], normal[1], normal[2], rad, self.texture.id)
494
+ top_cap = """Ring Center %s %s %s Normal %s %s %s Inner 0 Outer %s %s""" \
495
+ % (top[0], top[1], top[2], normal[0], normal[1], normal[2], rad, self.texture.id)
496
+ return [base_cap, cyl, top_cap]
497
+ else:
498
+ return cyl
499
+
500
+ def jmol_repr(self, render_params):
501
+ r"""
502
+ EXAMPLES::
503
+
504
+ sage: from sage.plot.plot3d.shapes import Cylinder
505
+
506
+ For thin cylinders, lines are used::
507
+
508
+ sage: C = Cylinder(.1, 4)
509
+ sage: C.jmol_repr(C.default_render_params())
510
+ ['\ndraw line_1 width 0.1 {0 0 0} {0 0 4.0}\ncolor $line_1 [102,102,255]\n']
511
+
512
+ For anything larger, we use a pmesh::
513
+
514
+ sage: C = Cylinder(3, 1, closed=False)
515
+ sage: C.jmol_repr(C.testing_render_params())
516
+ ['pmesh obj_1 "obj_1.pmesh"\ncolor pmesh [102,102,255]']
517
+ """
518
+ transform = render_params.transform
519
+ base, top = self.get_endpoints(transform)
520
+ rad = self.get_radius(transform)
521
+
522
+ cdef double ratio = sqrt(rad*rad / ((base[0]-top[0])**2 + (base[1]-top[1])**2 + (base[2]-top[2])**2))
523
+
524
+ if ratio > .02:
525
+ if not (transform is None or transform.is_uniform_on([(1,0,0),(0,1,0)])) or ratio > .05:
526
+ # Jmol can't do squashed
527
+ return ParametricSurface.jmol_repr(self, render_params)
528
+
529
+ name = render_params.unique_name('line')
530
+ return ["""
531
+ draw %s width %s {%s %s %s} {%s %s %s}\n%s
532
+ """ % (name, rad,
533
+ base[0], base[1], base[2],
534
+ top[0], top[1], top[2],
535
+ self.texture.jmol_str("$" + name))]
536
+
537
+ def get_endpoints(self, transform=None):
538
+ """
539
+ EXAMPLES::
540
+
541
+ sage: from sage.plot.plot3d.shapes import Cylinder
542
+ sage: from sage.plot.plot3d.transform import Transformation
543
+ sage: Cylinder(1, 5).get_endpoints()
544
+ ((0, 0, 0), (0, 0, 5.0))
545
+ sage: Cylinder(1, 5).get_endpoints(Transformation(trans=(1,2,3),
546
+ ....: scale=(2,2,2)))
547
+ ((1.0, 2.0, 3.0), (1.0, 2.0, 13.0))
548
+ """
549
+ if transform is None:
550
+ return (0,0,0), (0,0,self.height)
551
+ else:
552
+ return transform.transform_point((0,0,0)), transform.transform_point((0,0,self.height))
553
+
554
+ def get_radius(self, transform=None):
555
+ """
556
+ EXAMPLES::
557
+
558
+ sage: from sage.plot.plot3d.shapes import Cylinder
559
+ sage: from sage.plot.plot3d.transform import Transformation
560
+ sage: Cylinder(3, 1).get_radius()
561
+ 3.0
562
+ sage: Cylinder(3, 1).get_radius(Transformation(trans=(1,2,3),
563
+ ....: scale=(2,2,2)))
564
+ 6.0
565
+ """
566
+ if transform is None:
567
+ return self.radius
568
+ radv = transform.transform_vector((self.radius, 0, 0))
569
+ return sqrt(sum([x * x for x in radv]))
570
+
571
+ def get_grid(self, ds):
572
+ """
573
+ Return the grid on which to evaluate this parametric surface.
574
+
575
+ EXAMPLES::
576
+
577
+ sage: from sage.plot.plot3d.shapes import Cylinder
578
+ sage: Cylinder(1, 3, closed=True).get_grid(100)
579
+ ([2, 1, -1, -2], [0.0, 1.2566..., 2.5132..., 3.7699..., 5.0265..., 0.0])
580
+ sage: Cylinder(1, 3, closed=False).get_grid(100)
581
+ ([1, -1], [0.0, 1.2566..., 2.5132..., 3.7699..., 5.0265..., 0.0])
582
+ sage: len(Cylinder(1, 3).get_grid(.001)[1])
583
+ 38
584
+ """
585
+ cdef int k, v_res = min(max(int(2*M_PI*self.radius/ds), 5), 37)
586
+ if self.closed:
587
+ urange = [2,1,-1,-2]
588
+ else:
589
+ urange = [1,-1]
590
+ vrange = [2*M_PI*k/v_res for k in range(v_res)] + [0.0]
591
+ return urange, vrange
592
+
593
+ cdef int eval_c(self, point_c *res, double u, double v) except -1:
594
+ if u == -2:
595
+ res.x, res.y, res.z = 0, 0, 0
596
+ elif u == -1:
597
+ res.x = self.radius*sin(v)
598
+ res.y = self.radius*cos(v)
599
+ res.z = 0
600
+ elif u == 1:
601
+ res.x = self.radius*sin(v)
602
+ res.y = self.radius*cos(v)
603
+ res.z = self.height
604
+ else: # u == 2:
605
+ res.x, res.y, res.z = 0, 0, self.height
606
+
607
+
608
+ @rename_keyword(alpha='opacity')
609
+ def LineSegment(start, end, thickness=1, radius=None, **kwds):
610
+ """
611
+ Create a line segment, which is drawn as a cylinder from start to
612
+ end with radius ``radius``.
613
+
614
+ EXAMPLES::
615
+
616
+ sage: from sage.plot.plot3d.shapes import LineSegment, Sphere
617
+ sage: P = (0,0,0.1)
618
+ sage: Q = (0.5,0.6,0.7)
619
+ sage: S = Sphere(.2, color='red').translate(P)
620
+ sage: S += Sphere(.2, color='blue').translate(Q)
621
+ sage: S += LineSegment(P, Q, .05, color='black')
622
+ sage: S.show()
623
+
624
+ .. PLOT::
625
+
626
+ from sage.plot.plot3d.shapes import LineSegment, Sphere
627
+ P = (0,0,0.1)
628
+ Q = (0.5,0.6,0.7)
629
+ S = Sphere(.2, color='red').translate(P)
630
+ S += Sphere(.2, color='blue').translate(Q)
631
+ S += LineSegment(P, Q, .05, color='black')
632
+ sphinx_plot(S)
633
+
634
+ ::
635
+
636
+ sage: S = Sphere(.1, color='red').translate(P)
637
+ sage: S += Sphere(.1, color='blue').translate(Q)
638
+ sage: S += LineSegment(P, Q, .15, color='black')
639
+ sage: S.show()
640
+
641
+ .. PLOT::
642
+
643
+ from sage.plot.plot3d.shapes import LineSegment, Sphere
644
+ P = (0,0,0.1)
645
+ Q = (0.5,0.6,0.7)
646
+ S = Sphere(.1, color='red').translate(P)
647
+ S += Sphere(.1, color='blue').translate(Q)
648
+ S += LineSegment(P, Q, .15, color='black')
649
+ sphinx_plot(S)
650
+
651
+ AUTHOR:
652
+
653
+ - Robert Bradshaw
654
+ """
655
+ if radius is None:
656
+ radius = thickness/50.0
657
+ start = vector(RDF, start, sparse=False)
658
+ end = vector(RDF, end, sparse=False)
659
+ zaxis = vector(RDF, (0,0,1), sparse=False)
660
+ diff = end - start
661
+ height= sqrt(diff.dot_product(diff))
662
+ cyl = Cylinder(radius, height, **kwds)
663
+ axis = zaxis.cross_product(diff)
664
+ if axis == 0:
665
+ if diff[2] < 0:
666
+ return cyl.translate(end)
667
+ else:
668
+ return cyl.translate(start)
669
+ else:
670
+ theta = -acos(diff[2]/height)
671
+ return cyl.rotate(axis, theta).translate(start)
672
+
673
+
674
+ def arrow3d(start, end, width=1, radius=None, head_radius=None, head_len=None, **kwds):
675
+ """
676
+ Create a 3d arrow.
677
+
678
+ INPUT:
679
+
680
+ - ``start`` -- (x,y,z) point; the starting point of the arrow
681
+ - ``end`` -- (x,y,z) point; the end point
682
+ - ``width`` -- (default: 1) how wide the arrow is
683
+ - ``radius`` -- (default: ``width/50.0``) the radius of the arrow
684
+ - ``head_radius`` -- (default: ``3*radius``) radius of arrow head
685
+ - ``head_len`` -- (default: ``3*head_radius``) len of arrow head
686
+
687
+ EXAMPLES:
688
+
689
+ The default arrow::
690
+
691
+ sage: arrow3d((0,0,0), (1,1,1), 1)
692
+ Graphics3d Object
693
+
694
+ .. PLOT::
695
+
696
+ sphinx_plot(arrow3d((0,0,0), (1,1,1), 1))
697
+
698
+ A fat arrow::
699
+
700
+ sage: arrow3d((0,0,0), (1,1,1), radius=0.1)
701
+ Graphics3d Object
702
+
703
+ .. PLOT::
704
+
705
+ sphinx_plot(arrow3d((0,0,0), (1,1,1), radius=0.1))
706
+
707
+ A green arrow::
708
+
709
+ sage: arrow3d((0,0,0), (1,1,1), color='green')
710
+ Graphics3d Object
711
+
712
+ .. PLOT::
713
+
714
+ sphinx_plot(arrow3d((0,0,0), (1,1,1), color='green'))
715
+
716
+ A fat arrow head::
717
+
718
+ sage: arrow3d((2,1,0), (1,1,1), color='green', head_radius=0.3,
719
+ ....: aspect_ratio=[1,1,1])
720
+ Graphics3d Object
721
+
722
+ .. PLOT::
723
+
724
+ sphinx_plot(arrow3d((2,1,0), (1,1,1), color='green', head_radius=0.3, aspect_ratio=[1,1,1]))
725
+
726
+ Many arrows arranged in a circle (flying spears?)::
727
+
728
+ sage: sum(arrow3d((cos(t),sin(t),0), (cos(t),sin(t),1)) # needs sage.symbolic
729
+ ....: for t in [0,0.3,..,2*pi])
730
+ Graphics3d Object
731
+
732
+ .. PLOT::
733
+
734
+ t=0
735
+ G=Graphics()
736
+ while (t<=2*pi):
737
+ G += arrow3d((cos(t),sin(t),0),(cos(t),sin(t),1))
738
+ t +=0.3
739
+ sphinx_plot(G)
740
+
741
+ Change the width of the arrow. (Note: for an arrow that scales with zoom, please consider
742
+ the :func:`line3d` function with the option ``arrow_head=True``)::
743
+
744
+ sage: arrow3d((0,0,0), (1,1,1), width=1)
745
+ Graphics3d Object
746
+
747
+ .. PLOT::
748
+
749
+ sphinx_plot(arrow3d((0,0,0), (1,1,1), width=1))
750
+
751
+ TESTS:
752
+
753
+ If the arrow is too long, the shaft and part of the head is cut off. ::
754
+
755
+ sage: a = arrow3d((0,0,0), (0,0,0.5), head_len=1)
756
+ sage: len(a.all)
757
+ 1
758
+ sage: type(a.all[0])
759
+ <... 'sage.plot.plot3d.shapes.Cone'>
760
+
761
+ Arrows are always constructed pointing up in the z direction from
762
+ the origin, and then rotated/translated into place. This works for
763
+ every arrow direction except the -z direction. We take care of the
764
+ anomaly by testing to see if the arrow should point in the -z
765
+ direction, and if it should, just scaling the constructed arrow by
766
+ -1 (i.e., every point is sent to its negative). The scaled arrow
767
+ then points downwards. The doctest just tests that the scale of -1
768
+ is applied to the arrow. ::
769
+
770
+ sage: a = arrow3d((0,0,0), (0,0,-1))
771
+ sage: a.all[0].get_transformation().transform_point((0,0,1))
772
+ (0.0, 0.0, -1.0)
773
+ """
774
+ if radius is None:
775
+ radius = width/50.0
776
+ if head_radius is None:
777
+ head_radius = 3*radius
778
+ if head_len is None:
779
+ head_len = 3*head_radius
780
+ start = vector(RDF, start, sparse=False)
781
+ end = vector(RDF, end, sparse=False)
782
+ zaxis = vector(RDF, (0,0,1), sparse=False)
783
+ diff = end - start
784
+ length = sqrt(diff.dot_product(diff))
785
+ if length <= head_len:
786
+ arrow = Cone(head_radius*length/head_len, length, **kwds)
787
+ else:
788
+ arrow = Cylinder(radius, length-head_len, **kwds) \
789
+ + Cone(head_radius, head_len, **kwds).translate(0, 0, length-head_len)
790
+ axis = zaxis.cross_product(diff)
791
+ if axis == 0:
792
+ if diff[2] >= 0:
793
+ return arrow.translate(start)
794
+ else:
795
+ return arrow.scale(-1).translate(start)
796
+ else:
797
+ theta = -acos(diff[2]/length)
798
+ return arrow.rotate(axis, theta).translate(start)
799
+
800
+
801
+ cdef class Sphere(ParametricSurface):
802
+ """
803
+ This class represents a sphere centered at the origin.
804
+
805
+ EXAMPLES::
806
+
807
+ sage: from sage.plot.plot3d.shapes import Sphere
808
+ sage: Sphere(3)
809
+ Graphics3d Object
810
+
811
+ .. PLOT::
812
+
813
+ from sage.plot.plot3d.shapes import Sphere
814
+ sphinx_plot(Sphere(3))
815
+
816
+ Plot with ``aspect_ratio=1`` to see it unsquashed::
817
+
818
+ sage: S = Sphere(3, color='blue') + Sphere(2, color='red').translate(0,3,0)
819
+ sage: S.show(aspect_ratio=1)
820
+
821
+ .. PLOT::
822
+
823
+ from sage.plot.plot3d.shapes import Sphere
824
+ S = Sphere(3, color='blue') + Sphere(2, color='red').translate(0,3,0)
825
+ sphinx_plot(S)
826
+
827
+ Scale to get an ellipsoid::
828
+
829
+ sage: S = Sphere(1).scale(1,2,1/2)
830
+ sage: S.show(aspect_ratio=1)
831
+
832
+ .. PLOT::
833
+
834
+ from sage.plot.plot3d.shapes import Sphere
835
+ S = Sphere(1).scale(1,2,1/2)
836
+ sphinx_plot(S)
837
+ """
838
+ def __init__(self, radius, **kwds):
839
+ """
840
+ TESTS::
841
+
842
+ sage: from sage.plot.plot3d.shapes import Sphere
843
+ sage: Sphere(3)
844
+ Graphics3d Object
845
+ """
846
+ ParametricSurface.__init__(self, **kwds)
847
+ self.radius = radius
848
+
849
+ def bounding_box(self):
850
+ """
851
+ Return the bounding box that contains this sphere.
852
+
853
+ EXAMPLES::
854
+
855
+ sage: from sage.plot.plot3d.shapes import Sphere
856
+ sage: Sphere(3).bounding_box()
857
+ ((-3.0, -3.0, -3.0), (3.0, 3.0, 3.0))
858
+ """
859
+ return ((-self.radius, -self.radius, -self.radius),
860
+ (self.radius, self.radius, self.radius))
861
+
862
+ def x3d_geometry(self):
863
+ """
864
+ EXAMPLES::
865
+
866
+ sage: from sage.plot.plot3d.shapes import Sphere
867
+ sage: Sphere(12).x3d_geometry()
868
+ "<Sphere radius='12.0'/>"
869
+ """
870
+ return "<Sphere radius='%s'/>" % (self.radius)
871
+
872
+ def tachyon_repr(self, render_params):
873
+ r"""
874
+ Tachyon can natively handle spheres. Ellipsoids rendering is done
875
+ as a parametric surface.
876
+
877
+ EXAMPLES::
878
+
879
+ sage: from sage.plot.plot3d.shapes import Sphere
880
+ sage: S = Sphere(2)
881
+ sage: S.tachyon_repr(S.default_render_params())
882
+ 'Sphere center 0 0 0 Rad 2.0 texture...'
883
+ sage: S.translate(1, 2, 3).scale(3).tachyon_repr(S.default_render_params())
884
+ [['Sphere center 3.0 6.0 9.0 Rad 6.0 texture...']]
885
+ sage: S.scale(1,1/2,1/4).tachyon_repr(S.default_render_params())
886
+ [['TRI V0 0 0 -0.5 V1 0.308116 0.0271646 -0.493844 V2 0.312869 0 -0.493844',
887
+ 'texture...',
888
+ ...
889
+ 'TRI V0 0.308116 -0.0271646 0.493844 V1 0.312869 0 0.493844 V2 0 0 0.5',
890
+ 'texture...']]
891
+ """
892
+ transform = render_params.transform
893
+ if not (transform is None or transform.is_uniform()):
894
+ return ParametricSurface.tachyon_repr(self, render_params)
895
+
896
+ if transform is None:
897
+ cen = (0,0,0)
898
+ rad = self.radius
899
+ else:
900
+ cen = transform.transform_point((0,0,0))
901
+ radv = transform.transform_vector((self.radius,0,0))
902
+ rad = sqrt(sum([x*x for x in radv]))
903
+ return "Sphere center %s %s %s Rad %s %s" % (cen[0], cen[1], cen[2], rad, self.texture.id)
904
+
905
+ def jmol_repr(self, render_params):
906
+ r"""
907
+ EXAMPLES::
908
+
909
+ sage: from sage.plot.plot3d.shapes import Sphere
910
+
911
+ Jmol has native code for handling spheres::
912
+
913
+ sage: S = Sphere(2)
914
+ sage: S.jmol_repr(S.default_render_params())
915
+ ['isosurface sphere_1 center {0 0 0} sphere 2.0\ncolor isosurface [102,102,255]']
916
+ sage: S.translate(10, 100, 1000).jmol_repr(S.default_render_params())
917
+ [['isosurface sphere_1 center {10.0 100.0 1000.0} sphere 2.0\ncolor isosurface [102,102,255]']]
918
+
919
+ It cannot natively handle ellipsoids::
920
+
921
+ sage: Sphere(1).scale(2, 3, 4).jmol_repr(S.testing_render_params())
922
+ [['pmesh obj_2 "obj_2.pmesh"\ncolor pmesh [102,102,255]']]
923
+
924
+ Small spheres need extra hints to render well::
925
+
926
+ sage: Sphere(.01).jmol_repr(S.default_render_params())
927
+ ['isosurface sphere_1 resolution 100 center {0 0 0} sphere 0.01\ncolor isosurface [102,102,255]']
928
+ """
929
+ name = render_params.unique_name('sphere')
930
+ transform = render_params.transform
931
+ if not (transform is None or transform.is_uniform()):
932
+ return ParametricSurface.jmol_repr(self, render_params)
933
+
934
+ if transform is None:
935
+ cen = (0,0,0)
936
+ rad = self.radius
937
+ else:
938
+ cen = transform.transform_point((0,0,0))
939
+ radv = transform.transform_vector((self.radius,0,0))
940
+ rad = sqrt(sum([x*x for x in radv]))
941
+ if rad < 0.5:
942
+ res = "resolution %s" % min(int(7/rad), 100)
943
+ else:
944
+ res = ""
945
+ return ["isosurface %s %s center {%s %s %s} sphere %s\n%s" % (name, res, cen[0], cen[1], cen[2], rad, self.texture.jmol_str("isosurface"))]
946
+
947
+ def get_grid(self, double ds):
948
+ """
949
+ Return the range of variables to be evaluated on to render as a
950
+ parametric surface.
951
+
952
+ EXAMPLES::
953
+
954
+ sage: from sage.plot.plot3d.shapes import Sphere
955
+ sage: Sphere(1).get_grid(100)
956
+ ([-10.0, ..., 10.0],
957
+ [0.0, ..., 3.141592653589793, ..., 0.0])
958
+ """
959
+ cdef int u_res, v_res
960
+ u_res = min(max(int(M_PI*self.radius/ds), 6), 20)
961
+ v_res = min(max(int(2*M_PI * self.radius/ds), 6), 36)
962
+ urange = [-10.0] + [M_PI * k/u_res - M_PI/2 for k in range(1, u_res)] + [10.0]
963
+ vrange = [2*M_PI * k/v_res for k in range(v_res)] + [0.0]
964
+ return urange, vrange
965
+
966
+ cdef int eval_c(self, point_c *res, double u, double v) except -1:
967
+ if u == -10:
968
+ res.x, res.y, res.z = 0, 0, -self.radius
969
+ elif u == 10:
970
+ res.x, res.y, res.z = 0, 0, self.radius
971
+ else:
972
+ res.x = self.radius * cos(v) * cos(u)
973
+ res.y = self.radius * sin(v) * cos(u)
974
+ res.z = self.radius * sin(u)
975
+
976
+
977
+ cdef class Torus(ParametricSurface):
978
+ """
979
+ INPUT:
980
+
981
+ - ``R`` -- (default: ``1``) outer radius
982
+ - ``r`` -- (default: ``.3``) inner radius
983
+
984
+ OUTPUT: a 3d torus
985
+
986
+ EXAMPLES::
987
+
988
+ sage: from sage.plot.plot3d.shapes import Torus
989
+ sage: Torus(1, .2).show(aspect_ratio=1)
990
+
991
+ .. PLOT::
992
+
993
+ from sage.plot.plot3d.shapes import Torus
994
+ sphinx_plot(Torus(1, .2))
995
+
996
+ ::
997
+
998
+ sage: Torus(1, .7, color='red').show(aspect_ratio=1)
999
+
1000
+ .. PLOT::
1001
+
1002
+ from sage.plot.plot3d.shapes import Torus
1003
+ sphinx_plot(Torus(1, .7, color='red'))
1004
+
1005
+ A rubberband ball::
1006
+
1007
+ sage: show(sum(Torus(1, .03, color=(1, t/30.0, 0)).rotate((1,1,1), t)
1008
+ ....: for t in range(30)))
1009
+
1010
+ .. PLOT::
1011
+
1012
+ from sage.plot.plot3d.shapes import Torus
1013
+ sphinx_plot(sum([Torus(1, .03, color=(1, t/30.0, 0)).rotate((1,1,1),t) for t in range(30)]))
1014
+
1015
+ Mmm... doughnuts::
1016
+
1017
+ sage: D = Torus(1, .4, color=(.5, .3, .2))
1018
+ sage: D += Torus(1, .3, color='yellow').translate(0, 0, .15)
1019
+ sage: G = sum(D.translate(RDF.random_element(-.2, .2),
1020
+ ....: RDF.random_element(-.2, .2),
1021
+ ....: .8*t)
1022
+ ....: for t in range(10))
1023
+ sage: G.show(aspect_ratio=1, frame=False)
1024
+
1025
+ .. PLOT::
1026
+
1027
+ from sage.plot.plot3d.shapes import Torus
1028
+ D = Torus(1, .4, color=(.5, .3, .2)) + Torus(1, .3, color='yellow').translate(0, 0, .15)
1029
+ G = sum(D.translate(RDF.random_element(-.2, .2), RDF.random_element(-.2, .2), .8*t) for t in range(10))
1030
+ sphinx_plot(G)
1031
+ """
1032
+ def __init__(self, R=1, r=.3, **kwds):
1033
+ """
1034
+ TESTS::
1035
+
1036
+ sage: from sage.plot.plot3d.shapes import Torus
1037
+ sage: T = Torus(1, .5)
1038
+ """
1039
+ ParametricSurface.__init__(self, None, **kwds)
1040
+ self.R = R
1041
+ self.r = r
1042
+
1043
+ def get_grid(self, ds):
1044
+ """
1045
+ Return the range of variables to be evaluated on to render as a
1046
+ parametric surface.
1047
+
1048
+ EXAMPLES::
1049
+
1050
+ sage: from sage.plot.plot3d.shapes import Torus
1051
+ sage: Torus(2, 1).get_grid(100)
1052
+ ([0.0, -1.047..., -3.141592653589793, ..., 0.0],
1053
+ [0.0, 1.047..., 3.141592653589793, ..., 0.0])
1054
+ """
1055
+ cdef int k, u_divs, v_divs
1056
+ u_divs = min(max(int(4*M_PI * self.R/ds), 6), 37)
1057
+ v_divs = min(max(int(4*M_PI * self.r/ds), 6), 37)
1058
+ urange = [0.0] + [-2*M_PI * k/u_divs for k in range(1, u_divs)] + [0.0]
1059
+ vrange = [ 2*M_PI * k/v_divs for k in range(v_divs)] + [0.0]
1060
+ return urange, vrange
1061
+
1062
+ cdef int eval_c(self, point_c *res, double u, double v) except -1:
1063
+ res.x = (self.R+self.r*sin(v))*sin(u)
1064
+ res.y = (self.R+self.r*sin(v))*cos(u)
1065
+ res.z = self.r*cos(v)
1066
+
1067
+
1068
+ class Text(PrimitiveObject):
1069
+ """
1070
+ A text label attached to a point in 3d space. It always starts at the
1071
+ origin, translate it to move it elsewhere.
1072
+
1073
+ EXAMPLES::
1074
+
1075
+ sage: from sage.plot.plot3d.shapes import Text
1076
+ sage: Text("Just a lonely label.")
1077
+ Graphics3d Object
1078
+
1079
+ .. PLOT::
1080
+
1081
+ from sage.plot.plot3d.shapes import Text
1082
+ sphinx_plot(Text("Just a lonely label. "))
1083
+
1084
+ ::
1085
+
1086
+ sage: pts = [(RealField(10)^3).random_element() for k in range(20)]
1087
+ sage: sum(Text(str(P)).translate(P) for P in pts)
1088
+ Graphics3d Object
1089
+
1090
+ .. PLOT::
1091
+
1092
+ from sage.plot.plot3d.shapes import Text
1093
+ pts = [(RealField(10)**3).random_element() for k in range(20)]
1094
+ sphinx_plot(sum(Text(str(P)).translate(P) for P in pts))
1095
+ """
1096
+ def __init__(self, string, **kwds):
1097
+ """
1098
+ TESTS::
1099
+
1100
+ sage: from sage.plot.plot3d.shapes import Text
1101
+ sage: T = Text("Hi")
1102
+ """
1103
+ PrimitiveObject.__init__(self, **kwds)
1104
+ self.string = string
1105
+ self._set_extra_kwds(kwds)
1106
+
1107
+ def x3d_geometry(self):
1108
+ """
1109
+ EXAMPLES::
1110
+
1111
+ sage: from sage.plot.plot3d.shapes import Text
1112
+ sage: Text("Hi").x3d_geometry()
1113
+ "<Text string='Hi' solid='true'/>"
1114
+ """
1115
+ return "<Text string='%s' solid='true'/>" % self.string
1116
+
1117
+ def obj_repr(self, render_params):
1118
+ """
1119
+ The obj file format does not support text strings::
1120
+
1121
+ sage: from sage.plot.plot3d.shapes import Text
1122
+ sage: Text("Hi").obj_repr(None)
1123
+ ''
1124
+ """
1125
+ return ''
1126
+
1127
+ def tachyon_repr(self, render_params):
1128
+ """
1129
+ Strings are not yet supported in Tachyon, so we ignore them for now::
1130
+
1131
+ sage: from sage.plot.plot3d.shapes import Text
1132
+ sage: Text("Hi").tachyon_repr(None)
1133
+ ''
1134
+ """
1135
+ return ''
1136
+ # Text in Tachyon not implemented yet.
1137
+ # I have no idea what the code below is supposed to do.
1138
+ ## transform = render_params.transform
1139
+ ## if not (transform is None or transform.is_uniform()):
1140
+ ## return ParametricSurface.tachyon_repr(self, render_params)
1141
+ ##
1142
+ ## if transform is None:
1143
+ ## cen = (0,0,0)
1144
+ ## rad = self.radius
1145
+ ## else:
1146
+ ## cen = transform.transform_point((0,0,0))
1147
+ ## radv = transform.transform_vector((self.radius,0,0))
1148
+ ## rad = sqrt(sum([x*x for x in radv]))
1149
+ ## return "Sphere center %s %s %s Rad %s %s" % (cen[0], cen[1], cen[2], rad, self.texture.id)
1150
+
1151
+ def jmol_repr(self, render_params):
1152
+ """
1153
+ Labels in jmol must be attached to atoms.
1154
+
1155
+ EXAMPLES::
1156
+
1157
+ sage: from sage.plot.plot3d.shapes import Text
1158
+ sage: T = Text("Hi")
1159
+ sage: T.jmol_repr(T.testing_render_params())
1160
+ ['select atomno = 1', 'color atom [102,102,255]', 'label "Hi"']
1161
+ sage: T = Text("Hi").translate(-1, 0, 0) + Text("Bye").translate(1, 0, 0)
1162
+ sage: T.jmol_repr(T.testing_render_params())
1163
+ [[['select atomno = 1', 'color atom [102,102,255]', 'label "Hi"']],
1164
+ [['select atomno = 2', 'color atom [102,102,255]', 'label "Bye"']]]
1165
+ """
1166
+ cen = (0, 0, 0)
1167
+ if render_params.transform is not None:
1168
+ cen = render_params.transform.transform_point(cen)
1169
+ render_params.atom_list.append(cen)
1170
+ atom_no = len(render_params.atom_list)
1171
+ return ['select atomno = %s' % atom_no,
1172
+ self.get_texture().jmol_str("atom"),
1173
+ 'label "%s"' % self.string] # .replace('\n', '|')]
1174
+
1175
+ def threejs_repr(self, render_params):
1176
+ r"""
1177
+ Return representation of the text suitable for plotting in three.js.
1178
+
1179
+ EXAMPLES::
1180
+
1181
+ sage: T = text3d("Hi", (1, 2, 3), color='red', fontfamily='serif',
1182
+ ....: fontweight='bold', fontstyle='italic', fontsize=20,
1183
+ ....: opacity=0.5)
1184
+ sage: T.threejs_repr(T.default_render_params())
1185
+ [('text',
1186
+ {'color': '#ff0000',
1187
+ 'fontFamily': ['serif'],
1188
+ 'fontSize': 20.0,
1189
+ 'fontStyle': 'italic',
1190
+ 'fontWeight': 'bold',
1191
+ 'opacity': 0.5,
1192
+ 'text': 'Hi',
1193
+ 'x': 1.0,
1194
+ 'y': 2.0,
1195
+ 'z': 3.0})]
1196
+
1197
+ TESTS:
1198
+
1199
+ When created directly via the ``Text`` constructor instead of ``text3d``,
1200
+ the text is located at the origin::
1201
+
1202
+ sage: from sage.plot.plot3d.shapes import Text
1203
+ sage: T = Text("Hi")
1204
+ sage: T.threejs_repr(T.default_render_params())
1205
+ [('text',
1206
+ {'color': '#6666ff',
1207
+ 'fontFamily': ['monospace'],
1208
+ 'fontSize': 14.0,
1209
+ 'fontStyle': 'normal',
1210
+ 'fontWeight': 'normal',
1211
+ 'opacity': 1.0,
1212
+ 'text': 'Hi',
1213
+ 'x': 0.0,
1214
+ 'y': 0.0,
1215
+ 'z': 0.0})]
1216
+ """
1217
+ center = (float(0), float(0), float(0))
1218
+ if render_params.transform is not None:
1219
+ center = render_params.transform.transform_point(center)
1220
+ color = '#' + str(self.texture.hex_rgb())
1221
+ string = str(self.string)
1222
+ text = _validate_threejs_text_style(self._extra_kwds)
1223
+ text.update(dict(text=string, x=center[0], y=center[1], z=center[2], color=color))
1224
+ return [('text', text)]
1225
+
1226
+ def bounding_box(self):
1227
+ """
1228
+ Text labels have no extent::
1229
+
1230
+ sage: from sage.plot.plot3d.shapes import Text
1231
+ sage: Text("Hi").bounding_box()
1232
+ ((0, 0, 0), (0, 0, 0))
1233
+ """
1234
+ return (0,0,0), (0,0,0)
1235
+
1236
+
1237
+ def _validate_threejs_text_style(style):
1238
+ """
1239
+ Validate a combination of text styles for use in the Three.js viewer.
1240
+
1241
+ INPUT:
1242
+
1243
+ - ``style`` -- dictionary optionally containing keys: 'color', 'fontSize',
1244
+ 'fontFamily', 'fontStyle', 'fontWeight', and 'opacity'
1245
+
1246
+ OUTPUT:
1247
+
1248
+ A corrected version of the dict, having printed out warning messages for
1249
+ any problems found
1250
+
1251
+ TESTS:
1252
+
1253
+ Default values::
1254
+
1255
+ sage: from sage.plot.plot3d.shapes import _validate_threejs_text_style as validate
1256
+ sage: validate(dict())
1257
+ {'color': '#000000',
1258
+ 'fontFamily': ['monospace'],
1259
+ 'fontSize': 14.0,
1260
+ 'fontStyle': 'normal',
1261
+ 'fontWeight': 'normal',
1262
+ 'opacity': 1.0}
1263
+
1264
+ Color by name or by HTML hex code::
1265
+
1266
+ sage: validate(dict(color='red'))
1267
+ {'color': '#ff0000',...}
1268
+ sage: validate(dict(color='#ff0000'))
1269
+ {'color': '#ff0000',...}
1270
+ sage: validate(dict(color='octarine'))
1271
+ ...UserWarning: invalid color: octarine...
1272
+
1273
+ Font size in absolute units or in percentage/keyword relative to default::
1274
+
1275
+ sage: validate(dict(fontsize=20.5))
1276
+ {...'fontSize': 20.5...}
1277
+ sage: validate(dict(fontsize="200%"))
1278
+ {...'fontSize': 28...}
1279
+ sage: validate(dict(fontsize="x%"))
1280
+ ...UserWarning: invalid fontsize: x%...
1281
+ sage: validate(dict(fontsize="large"))
1282
+ {...'fontSize': 16.8...}
1283
+ sage: validate(dict(fontsize="ginormous"))
1284
+ ...UserWarning: unknown fontsize: ginormous...
1285
+
1286
+ Font family as list or comma-delimited string::
1287
+
1288
+ sage: validate(dict(fontfamily=[" Times New Roman ", " Georgia", "serif "]))
1289
+ {...'fontFamily': ['Times New Roman', 'Georgia', 'serif']...}
1290
+ sage: validate(dict(fontfamily=" Times New Roman , Georgia,serif "))
1291
+ {...'fontFamily': ['Times New Roman', 'Georgia', 'serif']...}
1292
+
1293
+ Font style keywords including the special syntax for 'oblique' angle::
1294
+
1295
+ sage: validate(dict(fontstyle='italic'))
1296
+ {...'fontStyle': 'italic'...}
1297
+ sage: validate(dict(fontstyle='oblique 30deg'))
1298
+ {...'fontStyle': 'oblique 30deg'...}
1299
+ sage: validate(dict(fontstyle='garrish'))
1300
+ ...UserWarning: unknown style: garrish...
1301
+
1302
+ Font weight as keyword or integer::
1303
+
1304
+ sage: validate(dict(fontweight='bold'))
1305
+ {...'fontWeight': 'bold'...}
1306
+ sage: validate(dict(fontweight=500))
1307
+ {...'fontWeight': 500...}
1308
+ sage: validate(dict(fontweight='roman'))
1309
+ {...'fontWeight': 500...}
1310
+ sage: validate(dict(fontweight='bold & beautiful'))
1311
+ ...UserWarning: unknown fontweight: bold & beautiful...
1312
+
1313
+ Opacity::
1314
+
1315
+ sage: validate(dict(opacity=0.5))
1316
+ {...'opacity': 0.5}
1317
+ """
1318
+ default_color = '#000000' # black
1319
+ color = style.get('color', default_color)
1320
+ from sage.plot.plot3d.texture import Texture
1321
+ try:
1322
+ texture = Texture(color=color)
1323
+ except ValueError:
1324
+ import warnings
1325
+ warnings.warn(f"invalid color: {color}, using: {default_color}")
1326
+ color = default_color
1327
+ else:
1328
+ color = '#' + str(texture.hex_rgb())
1329
+
1330
+ default_size = 14.0
1331
+ size = style.get('fontsize', default_size)
1332
+ try:
1333
+ size = float(size)
1334
+ except (TypeError, ValueError):
1335
+ scale = str(size).lower()
1336
+ if scale.endswith('%'):
1337
+ try:
1338
+ scale = float(scale[:-1]) / 100.0
1339
+ size = default_size * scale
1340
+ except ValueError:
1341
+ import warnings
1342
+ warnings.warn(f"invalid fontsize: {size}, using: {default_size}")
1343
+ size = default_size
1344
+ else:
1345
+ from matplotlib.font_manager import font_scalings
1346
+ try:
1347
+ size = default_size * font_scalings[scale]
1348
+ except KeyError:
1349
+ import warnings
1350
+ warnings.warn(f"unknown fontsize: {size}, using: {default_size}")
1351
+ size = default_size
1352
+
1353
+ font = style.get('fontfamily', ['monospace'])
1354
+ if isinstance(font, str):
1355
+ font = font.split(',')
1356
+ font = [str(f).strip() for f in font]
1357
+
1358
+ default_style = 'normal'
1359
+ fontstyle = str(style.get('fontstyle', default_style))
1360
+ if fontstyle not in ['normal', 'italic'] and not fontstyle.startswith('oblique'): # ex: oblique 30deg
1361
+ import warnings
1362
+ warnings.warn(f"unknown style: {fontstyle}, using: {default_style}")
1363
+ fontstyle = default_style
1364
+
1365
+ default_weight = 'normal'
1366
+ weight = style.get('fontweight', default_weight)
1367
+ if weight not in ['normal', 'bold']:
1368
+ try:
1369
+ weight = int(weight)
1370
+ except (TypeError, ValueError):
1371
+ from matplotlib.font_manager import weight_dict
1372
+ try:
1373
+ weight = weight_dict[weight]
1374
+ except KeyError:
1375
+ import warnings
1376
+ warnings.warn(f"unknown fontweight: {weight}, using: {default_weight}")
1377
+ weight = default_weight
1378
+
1379
+ opacity = float(style.get('opacity', 1.0))
1380
+
1381
+ return dict(color=color, fontSize=size, fontFamily=font, fontStyle=fontstyle,
1382
+ fontWeight=weight, opacity=opacity)