passagemath-plot 10.6.31rc3__cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.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 (82) hide show
  1. passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
  2. passagemath_plot-10.6.31rc3.dist-info/RECORD +82 -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-83c28eba.so.5.0.0 +0 -0
  6. passagemath_plot.libs/libgsl-cda90e79.so.28.0.0 +0 -0
  7. passagemath_plot.libs/libopenblasp-r0-6dcb67f9.3.29.so +0 -0
  8. passagemath_plot.libs/libquadmath-2284e583.so.0.0.0 +0 -0
  9. sage/all__sagemath_plot.py +15 -0
  10. sage/ext_data/threejs/animation.css +195 -0
  11. sage/ext_data/threejs/animation.html +85 -0
  12. sage/ext_data/threejs/animation.js +273 -0
  13. sage/ext_data/threejs/fat_lines.js +48 -0
  14. sage/ext_data/threejs/threejs-version.txt +1 -0
  15. sage/ext_data/threejs/threejs_template.html +597 -0
  16. sage/interfaces/all__sagemath_plot.py +1 -0
  17. sage/interfaces/gnuplot.py +196 -0
  18. sage/interfaces/jmoldata.py +208 -0
  19. sage/interfaces/povray.py +56 -0
  20. sage/plot/all.py +42 -0
  21. sage/plot/animate.py +1796 -0
  22. sage/plot/arc.py +504 -0
  23. sage/plot/arrow.py +671 -0
  24. sage/plot/bar_chart.py +205 -0
  25. sage/plot/bezier_path.py +400 -0
  26. sage/plot/circle.py +435 -0
  27. sage/plot/colors.py +1606 -0
  28. sage/plot/complex_plot.cpython-314-x86_64-linux-gnu.so +0 -0
  29. sage/plot/complex_plot.pyx +1446 -0
  30. sage/plot/contour_plot.py +1792 -0
  31. sage/plot/density_plot.py +318 -0
  32. sage/plot/disk.py +373 -0
  33. sage/plot/ellipse.py +375 -0
  34. sage/plot/graphics.py +3580 -0
  35. sage/plot/histogram.py +354 -0
  36. sage/plot/hyperbolic_arc.py +404 -0
  37. sage/plot/hyperbolic_polygon.py +416 -0
  38. sage/plot/hyperbolic_regular_polygon.py +296 -0
  39. sage/plot/line.py +626 -0
  40. sage/plot/matrix_plot.py +629 -0
  41. sage/plot/misc.py +509 -0
  42. sage/plot/multigraphics.py +1294 -0
  43. sage/plot/plot.py +4183 -0
  44. sage/plot/plot3d/all.py +23 -0
  45. sage/plot/plot3d/base.cpython-314-x86_64-linux-gnu.so +0 -0
  46. sage/plot/plot3d/base.pxd +12 -0
  47. sage/plot/plot3d/base.pyx +3378 -0
  48. sage/plot/plot3d/implicit_plot3d.py +659 -0
  49. sage/plot/plot3d/implicit_surface.cpython-314-x86_64-linux-gnu.so +0 -0
  50. sage/plot/plot3d/implicit_surface.pyx +1453 -0
  51. sage/plot/plot3d/index_face_set.cpython-314-x86_64-linux-gnu.so +0 -0
  52. sage/plot/plot3d/index_face_set.pxd +32 -0
  53. sage/plot/plot3d/index_face_set.pyx +1873 -0
  54. sage/plot/plot3d/introduction.py +131 -0
  55. sage/plot/plot3d/list_plot3d.py +649 -0
  56. sage/plot/plot3d/parametric_plot3d.py +1130 -0
  57. sage/plot/plot3d/parametric_surface.cpython-314-x86_64-linux-gnu.so +0 -0
  58. sage/plot/plot3d/parametric_surface.pxd +12 -0
  59. sage/plot/plot3d/parametric_surface.pyx +893 -0
  60. sage/plot/plot3d/platonic.py +601 -0
  61. sage/plot/plot3d/plot3d.py +1442 -0
  62. sage/plot/plot3d/plot_field3d.py +162 -0
  63. sage/plot/plot3d/point_c.pxi +148 -0
  64. sage/plot/plot3d/revolution_plot3d.py +309 -0
  65. sage/plot/plot3d/shapes.cpython-314-x86_64-linux-gnu.so +0 -0
  66. sage/plot/plot3d/shapes.pxd +22 -0
  67. sage/plot/plot3d/shapes.pyx +1382 -0
  68. sage/plot/plot3d/shapes2.py +1512 -0
  69. sage/plot/plot3d/tachyon.py +1779 -0
  70. sage/plot/plot3d/texture.py +453 -0
  71. sage/plot/plot3d/transform.cpython-314-x86_64-linux-gnu.so +0 -0
  72. sage/plot/plot3d/transform.pxd +21 -0
  73. sage/plot/plot3d/transform.pyx +268 -0
  74. sage/plot/plot3d/tri_plot.py +589 -0
  75. sage/plot/plot_field.py +362 -0
  76. sage/plot/point.py +624 -0
  77. sage/plot/polygon.py +562 -0
  78. sage/plot/primitive.py +249 -0
  79. sage/plot/scatter_plot.py +199 -0
  80. sage/plot/step.py +85 -0
  81. sage/plot/streamline_plot.py +328 -0
  82. sage/plot/text.py +432 -0
@@ -0,0 +1,1779 @@
1
+ # sage_setup: distribution = sagemath-plot
2
+ # sage.doctest: needs tachyon
3
+ r"""
4
+ The Tachyon 3D Ray Tracer
5
+
6
+ Given any 3D graphics object ``M`` one can compute a raytraced
7
+ representation by typing ``M.show(viewer='tachyon')``.
8
+ For example, we draw two translucent spheres that contain a red
9
+ tube, and render the result using Tachyon.
10
+
11
+ ::
12
+
13
+ sage: S = sphere(opacity=0.8, aspect_ratio=[1,1,1])
14
+ sage: L = line3d([(0,0,0),(2,0,0)], thickness=10, color='red')
15
+ sage: M = S + S.translate((2,0,0)) + L
16
+ sage: M.show(viewer='tachyon')
17
+
18
+ A number of options can be given to the
19
+ :meth:`~sage.plot.plot3d.base.Graphics3d.show` method and
20
+ correspondingly to the
21
+ :meth:`~sage.plot.plot3d.base.Graphics3d.save` method for saving
22
+ the generated image to a file::
23
+
24
+ sage: M.show(viewer='tachyon',
25
+ ....: antialiasing=True, raydepth=3,
26
+ ....: figsize=[12,8], # the image resolution is 100*figsize
27
+ ....: camera_position=[4, 4.4, 1], # a distant camera position combined with
28
+ ....: zoom=3, # a large zoom factor will decrease perspective distortion.
29
+ ....: updir=(0, -0.1, 1), # the camera is slightly tilted
30
+ ....: viewdir=(-2.,-2.,-0.5), # slightly off-center
31
+ ....: light_position=(4.0, -3.0, 2.0),
32
+ ....: )
33
+
34
+ One can also directly control Tachyon by creating a ``Tachyon`` object
35
+ and adding elements of the scene one by one, which gives a huge amount of
36
+ flexibility. For example, here we directly use Tachyon to draw 3
37
+ spheres on the coordinate axes::
38
+
39
+ sage: t = Tachyon(xres=500, yres=500, camera_position=(2,0,0))
40
+ sage: t.light((4,3,2), 0.2, (1,1,1))
41
+ sage: t.texture('t2', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(1,0,0))
42
+ sage: t.texture('t3', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(0,1,0))
43
+ sage: t.texture('t4', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(0,0,1))
44
+ sage: t.sphere((0,0.5,0), 0.2, 't2')
45
+ sage: t.sphere((0.5,0,0), 0.2, 't3')
46
+ sage: t.sphere((0,0,0.5), 0.2, 't4')
47
+ sage: t.show()
48
+
49
+ For scenes with many reflections it is helpful to increase the raydepth option, and turn on antialiasing. The following scene is an extreme case with many reflections between four cotangent spheres::
50
+
51
+ sage: t = Tachyon(camera_position=(0,-4,1), xres=800, yres=600, raydepth=12,
52
+ ....: aspectratio=.75, antialiasing=4)
53
+ sage: t.light((0.02,0.012,0.001), 0.01, (1,0,0))
54
+ sage: t.light((0,0,10), 0.01, (0,0,1))
55
+ sage: t.texture('s', color=(.8,1,1), opacity=.9, specular=.95, diffuse=.3, ambient=0.05)
56
+ sage: t.texture('p', color=(0,0,1), opacity=1, specular=.2)
57
+ sage: t.sphere((-1,-.57735,-0.7071),1,'s')
58
+ sage: t.sphere((1,-.57735,-0.7071),1,'s')
59
+ sage: t.sphere((0,1.15465,-0.7071),1,'s')
60
+ sage: t.sphere((0,0,0.9259),1,'s')
61
+ sage: t.plane((0,0,-1.9259),(0,0,1),'p')
62
+ sage: t.show() # long time
63
+
64
+ Different projection options are available. The following examples all
65
+ use a sphere and cube::
66
+
67
+ sage: cedges = [[[1, 1, 1], [-1, 1, 1]], [[1, 1, 1], [1, -1, 1]],
68
+ ....: [[1, 1, 1], [1, 1, -1]], [[-1, 1, 1], [-1, -1, 1]], [[-1, 1, 1],
69
+ ....: [-1, 1, -1]], [[1, -1, 1], [-1, -1, 1]], [[1, -1, 1], [1, -1, -1]],
70
+ ....: [[-1, -1, 1], [-1, -1, -1]], [[1, 1, -1], [-1, 1, -1]],
71
+ ....: [[1, 1, -1], [1, -1, -1]], [[-1, 1, -1], [-1, -1, -1]],
72
+ ....: [[1, -1, -1], [-1, -1, -1]]]
73
+
74
+ The default projection is ``'perspective'``::
75
+
76
+ sage: t = Tachyon(xres=800, yres=600, camera_position=(-1.5,0.0,0.0), zoom=.2)
77
+ sage: t.texture('t1', color=(0,0,1))
78
+ sage: for ed in cedges:
79
+ ....: t.fcylinder(ed[0], ed[1], .05, 't1')
80
+ sage: t.light((-4,-4,4), .1, (1,1,1))
81
+ sage: t.show()
82
+
83
+ Another option is ``projection='fisheye'``, which requires frustum
84
+ information. The frustum data is (bottom angle, top angle, left
85
+ angle, right angle)::
86
+
87
+ sage: t = Tachyon(xres=800, yres=600, camera_position=(-1.5,0.0,0.0),
88
+ ....: projection='fisheye', frustum=(-1.2, 1.2, -1.2, 1.2))
89
+ sage: t.texture('t1', color=(0,0,1))
90
+ sage: for ed in cedges:
91
+ ....: t.fcylinder(ed[0], ed[1], .05, 't1')
92
+ sage: t.light((-4,-4,4), .1, (1,1,1))
93
+ sage: t.show()
94
+
95
+ Finally there is the ``projection='perspective_dof'`` option. ::
96
+
97
+ sage: T = Tachyon(xres=800, antialiasing=4, raydepth=10,
98
+ ....: projection='perspective_dof', focallength='1.0', aperture='.0025')
99
+ sage: T.light((0,5,7), 1.0, (1,1,1))
100
+ sage: T.texture('t1', opacity=1, specular=.3)
101
+ sage: T.texture('t2', opacity=1, specular=.3, color=(0,0,1))
102
+ sage: T.texture('t3', opacity=1, specular=1, color=(1,.8,1), diffuse=0.2)
103
+ sage: T.plane((0,0,-1), (0,0,1), 't3')
104
+ sage: ttlist = ['t1', 't2']
105
+ sage: tt = 't1'
106
+ sage: T.cylinder((0,0,.1), (1,1/3,0), .05, 't3')
107
+ sage: for q in srange(-3, 100, .15):
108
+ ....: if tt == 't1':
109
+ ....: tt = 't2'
110
+ ....: else:
111
+ ....: tt = 't1'
112
+ ....: T.sphere((q, q/3+.3*sin(3*q), .1+.3*cos(3*q)), .1, tt)
113
+ sage: T.show()
114
+
115
+ Image files in the ``ppm`` format can be used to tile planes or cover
116
+ cylinders or spheres. In this example an image is created and then
117
+ used to tile the plane::
118
+
119
+ sage: T = Tachyon(xres=800, yres=600, camera_position=(-2.0,-.1,.3),
120
+ ....: projection='fisheye', frustum=(-1.0, 1.0, -1.0, 1.0))
121
+ sage: T.texture('t1',color=(0,0,1))
122
+ sage: for ed in cedges:
123
+ ....: T.fcylinder(ed[0], ed[1], .05, 't1')
124
+ sage: T.light((-4,-4,4),.1,(1,1,1))
125
+ sage: fname_png = tmp_filename(ext='.png')
126
+ sage: fname_ppm = tmp_filename(ext='.ppm')
127
+ sage: T.save(fname_png)
128
+ sage: from sage.features.imagemagick import Magick
129
+ sage: r2 = os.system(Magick().executable+' '+fname_png+' '+fname_ppm) # optional -- ImageMagick
130
+ sage: # optional - imagemagick
131
+ sage: T = Tachyon(xres=800, yres=600,
132
+ ....: camera_position=(-2.0,-.1,.3),
133
+ ....: projection='fisheye',
134
+ ....: frustum=(-1.0, 1.0, -1.0, 1.0))
135
+ sage: T.texture('t1', color=(1,0,0), specular=.9)
136
+ sage: T.texture('p1', color=(1,1,1), opacity=.1,
137
+ ....: imagefile=fname_ppm, texfunc=9)
138
+ sage: T.sphere((0,0,0), .5, 't1')
139
+ sage: T.plane((0,0,-1), (0,0,1), 'p1')
140
+ sage: T.light((-4,-4,4), .1, (1,1,1))
141
+ sage: T.show()
142
+
143
+ AUTHOR:
144
+
145
+ - John E. Stone (johns@megapixel.com): wrote tachyon ray tracer
146
+
147
+ - William Stein: sage-tachyon interface
148
+
149
+ - Joshua Kantor: 3d function plotting
150
+
151
+ - Tom Boothby: 3d function plotting n'stuff
152
+
153
+ - Leif Hille: key idea for bugfix for texfunc issue (:issue:`799`)
154
+
155
+ - Marshall Hampton: improved doctests, rings, axis-aligned boxes.
156
+
157
+ - Paul Graham: Respect global verbosity settings (:issue:`16228`)
158
+
159
+ .. TODO::
160
+
161
+ - clean up trianglefactory stuff
162
+ """
163
+ from .tri_plot import Triangle, SmoothTriangle, TriangleFactory, TrianglePlot
164
+
165
+ from sage.interfaces.tachyon import tachyon_rt
166
+
167
+ from sage.misc.fast_methods import WithEqualityById
168
+ from sage.structure.sage_object import SageObject
169
+
170
+ from sage.misc.verbose import get_verbose
171
+ from sage.misc.temporary_file import tmp_filename
172
+
173
+ # from sage.ext import fast_tachyon_routines
174
+
175
+ from math import sqrt
176
+
177
+
178
+ class Tachyon(WithEqualityById, SageObject):
179
+ r"""
180
+ Create a scene the can be rendered using the Tachyon ray tracer.
181
+
182
+ INPUT:
183
+
184
+ - ``xres`` -- (default: 350)
185
+ - ``yres`` -- (default: 350)
186
+ - ``zoom`` -- (default: 1.0)
187
+ - ``antialiasing`` -- (default: ``False``)
188
+ - ``aspectratio`` -- (default: 1.0)
189
+ - ``raydepth`` -- (default: 8)
190
+ - ``camera_position`` -- (default: (-3, 0, 0))
191
+ - ``updir`` -- (default: (0, 0, 1))
192
+ - ``look_at`` -- (default: (0,0,0))
193
+ - ``viewdir`` -- (default: ``None``) otherwise list of three numbers
194
+ - ``projection`` -- ``'PERSPECTIVE'`` (default) ``'perspective_dof'``
195
+ or ``'fisheye'``
196
+ - ``frustum`` -- (default: ``''``) otherwise list of four numbers. Only
197
+ used with ``projection='fisheye'``.
198
+ - ``focallength`` -- (default: ``''``) otherwise a number. Only used
199
+ with ``projection='perspective_dof'``.
200
+ - ``aperture`` -- (default: ``''``) otherwise a number. Only used
201
+ with ``projection='perspective_dof'``.
202
+
203
+ OUTPUT: a Tachyon 3d scene
204
+
205
+ Note that the coordinates are by default such that `z` is
206
+ up, positive `y` is to the {left} and `x` is toward
207
+ you. This is not oriented according to the right hand rule.
208
+
209
+ EXAMPLES: Spheres along the twisted cubic.
210
+
211
+ ::
212
+
213
+ sage: t = Tachyon(xres=512,yres=512, camera_position=(3,0.3,0))
214
+ sage: t.light((4,3,2), 0.2, (1,1,1))
215
+ sage: t.texture('t0', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(1.0,0,0))
216
+ sage: t.texture('t1', ambient=0.1, diffuse=0.9, specular=0.3, opacity=1.0, color=(0,1.0,0))
217
+ sage: t.texture('t2', ambient=0.2, diffuse=0.7, specular=0.5, opacity=0.7, color=(0,0,1.0))
218
+ sage: k=0
219
+ sage: for i in srange(-1,1,0.05):
220
+ ....: k += 1
221
+ ....: t.sphere((i,i^2-0.5,i^3), 0.1, 't%s'%(k%3))
222
+ sage: t.show()
223
+
224
+ Another twisted cubic, but with a white background, got by putting
225
+ infinite planes around the scene.
226
+
227
+ ::
228
+
229
+ sage: t = Tachyon(xres=512,yres=512, camera_position=(3,0.3,0), raydepth=8)
230
+ sage: t.light((4,3,2), 0.2, (1,1,1))
231
+ sage: t.texture('t0', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(1.0,0,0))
232
+ sage: t.texture('t1', ambient=0.1, diffuse=0.9, specular=0.3, opacity=1.0, color=(0,1.0,0))
233
+ sage: t.texture('t2', ambient=0.2,diffuse=0.7, specular=0.5, opacity=0.7, color=(0,0,1.0))
234
+ sage: t.texture('white', color=(1,1,1))
235
+ sage: t.plane((0,0,-1), (0,0,1), 'white')
236
+ sage: t.plane((0,-20,0), (0,1,0), 'white')
237
+ sage: t.plane((-20,0,0), (1,0,0), 'white')
238
+
239
+ ::
240
+
241
+ sage: k=0
242
+ sage: for i in srange(-1,1,0.05):
243
+ ....: k += 1
244
+ ....: t.sphere((i,i^2 - 0.5,i^3), 0.1, 't%s'%(k%3))
245
+ ....: t.cylinder((0,0,0), (0,0,1), 0.05,'t1')
246
+ sage: t.show()
247
+
248
+ Many random spheres::
249
+
250
+ sage: t = Tachyon(xres=512,yres=512, camera_position=(2,0.5,0.5), look_at=(0.5,0.5,0.5), raydepth=4)
251
+ sage: t.light((4,3,2), 0.2, (1,1,1))
252
+ sage: t.texture('t0', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(1.0,0,0))
253
+ sage: t.texture('t1', ambient=0.1, diffuse=0.9, specular=0.3, opacity=1.0, color=(0,1.0,0))
254
+ sage: t.texture('t2', ambient=0.2, diffuse=0.7, specular=0.5, opacity=0.7, color=(0,0,1.0))
255
+ sage: k=0
256
+ sage: for i in range(100):
257
+ ....: k += 1
258
+ ....: t.sphere((random(),random(), random()), random()/10, 't%s'%(k%3))
259
+ sage: t.show()
260
+
261
+ Points on an elliptic curve, their height indicated by their height
262
+ above the axis::
263
+
264
+ sage: # needs sage.schemes
265
+ sage: t = Tachyon(camera_position=(5,2,2), look_at=(0,1,0))
266
+ sage: t.light((10,3,2), 0.2, (1,1,1))
267
+ sage: t.texture('t0', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(1,0,0))
268
+ sage: t.texture('t1', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(0,1,0))
269
+ sage: t.texture('t2', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(0,0,1))
270
+ sage: E = EllipticCurve('37a') # needs sage.schemes
271
+ sage: P = E([0,0]) # needs sage.schemes
272
+ sage: Q = P # needs sage.schemes
273
+ sage: n = 100
274
+ sage: for i in range(n): # increase 20 for a better plot # needs sage.schemes
275
+ ....: Q = Q + P
276
+ ....: t.sphere((Q[1], Q[0], ZZ(i)/n), 0.1, 't%s'%(i%3))
277
+ sage: t.show() # needs sage.schemes
278
+
279
+ A beautiful picture of rational points on a rank 1 elliptic curve.
280
+
281
+ ::
282
+
283
+ sage: # needs sage.schemes
284
+ sage: t = Tachyon(xres=1000, yres=800, camera_position=(2,7,4),
285
+ ....: look_at=(2,0,0), raydepth=4)
286
+ sage: t.light((10,3,2), 1, (1,1,1))
287
+ sage: t.light((10,-3,2), 1, (1,1,1))
288
+ sage: t.texture('black', color=(0,0,0))
289
+ sage: t.texture('red', color=(1,0,0))
290
+ sage: t.texture('grey', color=(.9,.9,.9))
291
+ sage: t.plane((0,0,0),(0,0,1),'grey')
292
+ sage: t.cylinder((0,0,0),(1,0,0),.01,'black')
293
+ sage: t.cylinder((0,0,0),(0,1,0),.01,'black')
294
+ sage: E = EllipticCurve('37a') # needs sage.schemes
295
+ sage: P = E([0,0]) # needs sage.schemes
296
+ sage: Q = P # needs sage.schemes
297
+ sage: n = 100
298
+ sage: for i in range(n): # needs sage.schemes
299
+ ....: Q = Q + P
300
+ ....: c = i/n + .1
301
+ ....: t.texture('r%s'%i,color=(float(i/n),0,0))
302
+ ....: t.sphere((Q[0], -Q[1], .01), .04, 'r%s'%i)
303
+ sage: t.show() # long time # needs sage.schemes
304
+
305
+ A beautiful spiral.
306
+
307
+ ::
308
+
309
+ sage: t = Tachyon(xres=800, yres=800, camera_position=(2,5,2), look_at=(2.5,0,0))
310
+ sage: t.light((0,0,100), 1, (1,1,1))
311
+ sage: t.texture('r', ambient=0.1, diffuse=0.9, specular=0.5,
312
+ ....: opacity=1.0, color=(1,0,0))
313
+ sage: for i in srange(0,50,0.1):
314
+ ....: t.sphere((i/10.0,sin(i),cos(i)), 0.05, 'r')
315
+ sage: t.texture('white', color=(1,1,1), opacity=1, specular=1, diffuse=1)
316
+ sage: t.plane((0,0,-100), (0,0,-100), 'white')
317
+ sage: t.show()
318
+
319
+ If the optional parameter ``viewdir`` is not set, the camera
320
+ center should not coincide with the point which
321
+ is looked at (see :issue:`7232`)::
322
+
323
+ sage: t = Tachyon(xres=80,yres=80, camera_position=(2,5,2), look_at=(2,5,2))
324
+ Traceback (most recent call last):
325
+ ...
326
+ ValueError: camera_position and look_at coincide
327
+
328
+ Use of a fisheye lens perspective. ::
329
+
330
+ sage: T = Tachyon(xres=800, yres=600, camera_position=(-1.5,-1.5,.3),
331
+ ....: projection='fisheye', frustum=(-1.0, 1.0, -1.0, 1.0))
332
+ sage: T.texture('t1', color=(0,0,1))
333
+ sage: cedges = [[[1, 1, 1], [-1, 1, 1]], [[1, 1, 1], [1, -1, 1]],
334
+ ....: [[1, 1, 1], [1, 1, -1]], [[-1, 1, 1], [-1, -1, 1]],
335
+ ....: [[-1, 1, 1], [-1, 1, -1]], [[1, -1, 1], [-1, -1, 1]],
336
+ ....: [[1, -1, 1], [1, -1, -1]], [[-1, -1, 1], [-1, -1, -1]],
337
+ ....: [[1, 1, -1], [-1, 1, -1]], [[1, 1, -1], [1, -1, -1]],
338
+ ....: [[-1, 1, -1], [-1, -1, -1]], [[1, -1, -1], [-1, -1, -1]]]
339
+ sage: for ed in cedges:
340
+ ....: T.fcylinder(ed[0], ed[1], .05, 't1')
341
+ sage: T.light((-4,-4,4), .1, (1,1,1))
342
+ sage: T.show()
343
+
344
+ Use of the ``projection='perspective_dof'`` option. This may not be
345
+ implemented correctly. ::
346
+
347
+ sage: T = Tachyon(xres=800, antialiasing=4, raydepth=10,
348
+ ....: projection='perspective_dof', focallength='1.0', aperture='.0025')
349
+ sage: T.light((0,5,7), 1.0, (1,1,1))
350
+ sage: T.texture('t1', opacity=1, specular=.3)
351
+ sage: T.texture('t2', opacity=1, specular=.3, color=(0,0,1))
352
+ sage: T.texture('t3', opacity=1, specular=1, color=(1,.8,1), diffuse=0.2)
353
+ sage: T.plane((0,0,-1), (0,0,1), 't3')
354
+ sage: ttlist = ['t1', 't2']
355
+ sage: tt = 't1'
356
+ sage: T.cylinder((0,0,.1), (1,1/3,0), .05, 't3')
357
+ sage: for q in srange(-3, 100, .15):
358
+ ....: if tt == 't1':
359
+ ....: tt = 't2'
360
+ ....: else:
361
+ ....: tt = 't1'
362
+ ....: T.sphere((q, q/3+.3*sin(3*q), .1+.3*cos(3*q)), .1, tt)
363
+ sage: T.show()
364
+
365
+ TESTS::
366
+
367
+ sage: hash(Tachyon()) # random
368
+ 140658972348064
369
+ """
370
+ def __init__(self,
371
+ xres=350, yres=350,
372
+ zoom=1.0,
373
+ antialiasing=False,
374
+ aspectratio=1.0,
375
+ raydepth=8,
376
+ camera_position=None, # default value (-3, 0, 0),
377
+ camera_center=None, # alternative equivalent name
378
+ updir=[0, 0, 1],
379
+ look_at=[0, 0, 0],
380
+ viewdir=None,
381
+ projection='PERSPECTIVE',
382
+ focallength='',
383
+ aperture='',
384
+ frustum=''):
385
+ r"""
386
+ Create an instance of the Tachyon class.
387
+
388
+ EXAMPLES::
389
+
390
+ sage: t = Tachyon()
391
+ sage: t._xres
392
+ 350
393
+ """
394
+ self._xres = xres
395
+ self._yres = yres
396
+ self._zoom = zoom
397
+ self._aspectratio = aspectratio
398
+ self._antialiasing = antialiasing
399
+ self._raydepth = raydepth
400
+ if camera_position is not None:
401
+ self._camera_position = camera_position
402
+ elif camera_center is not None: # make sure that old programs continue to work
403
+ self._camera_position = camera_center
404
+ else:
405
+ self._camera_position = (-3, 0, 0) # default value
406
+ self._updir = updir
407
+ self._projection = projection
408
+ self._focallength = focallength
409
+ self._aperture = aperture
410
+ self._frustum = frustum
411
+ self._objects = []
412
+ if viewdir is None:
413
+ if look_at != self._camera_position:
414
+ self._viewdir = [look_at[i] - self._camera_position[i]
415
+ for i in range(3)]
416
+ else:
417
+ raise ValueError('camera_position and look_at coincide')
418
+ else:
419
+ self._viewdir = viewdir
420
+
421
+ def save_image(self, filename=None, *args, **kwds):
422
+ r"""
423
+ Save an image representation of ``self``.
424
+
425
+ The image type is
426
+ determined by the extension of the filename. For example,
427
+ this could be ``.png``, ``.jpg``, ``.gif``, ``.pdf``,
428
+ ``.svg``. Currently this is implemented by calling the
429
+ :meth:`save` method of self, passing along all arguments and
430
+ keywords.
431
+
432
+ .. NOTE::
433
+
434
+ Not all image types are necessarily implemented for all
435
+ graphics types. See :meth:`save` for more details.
436
+
437
+ EXAMPLES::
438
+
439
+ sage: q = Tachyon()
440
+ sage: q.light((1,1,11), 1,(1,1,1))
441
+ sage: q.texture('s')
442
+ sage: q.sphere((0,-1,1),1,'s')
443
+ sage: tempname = tmp_filename()
444
+ sage: q.save_image(tempname)
445
+
446
+ TESTS:
447
+
448
+ :meth:`save_image` is used for generating animations::
449
+
450
+ sage: def tw_cubic(t):
451
+ ....: q = Tachyon()
452
+ ....: q.light((1,1,11), 1,(1,1,1))
453
+ ....: q.texture('s')
454
+ ....: for i in srange(-1,t,0.05):
455
+ ....: q.sphere((i,i^2-0.5,i^3), 0.1, 's')
456
+ ....: return q
457
+
458
+ sage: a = animate([tw_cubic(t) for t in srange(-1,1,.3)])
459
+ sage: a # optional -- ImageMagick
460
+ Animation with 7 frames
461
+ sage: a.show() # optional -- ImageMagick
462
+ """
463
+ self.save(filename, *args, **kwds)
464
+
465
+ def save(self, filename='sage.png', verbose=None, extra_opts=''):
466
+ r"""
467
+ Save rendering of the tachyon scene.
468
+
469
+ INPUT:
470
+
471
+ - ``filename`` -- (default: ``'sage.png'``) output
472
+ filename; the extension of the filename determines the type.
473
+ Supported types include:
474
+
475
+ - ``tga`` -- 24-bit (uncompressed)
476
+
477
+ - ``bmp`` -- 24-bit Windows BMP (uncompressed)
478
+
479
+ - ``ppm`` -- 24-bit PPM (uncompressed)
480
+
481
+ - ``rgb`` -- 24-bit SGI RGB (uncompressed)
482
+
483
+ - ``png`` -- 24-bit PNG (compressed, lossless)
484
+
485
+ - ``verbose`` -- integer (default: ``None``); if no verbosity setting
486
+ is supplied, the verbosity level set by
487
+ ``sage.misc.verbose.set_verbose`` is used.
488
+
489
+ - ``0`` -- silent
490
+
491
+ - ``1`` -- some output
492
+
493
+ - ``2`` -- very verbose output
494
+
495
+ - ``extra_opts`` -- passed directly to tachyon command
496
+ line. Use ``tachyon_rt.usage()`` to see some of the possibilities.
497
+
498
+ EXAMPLES::
499
+
500
+ sage: q = Tachyon()
501
+ sage: q.light((1,1,11), 1,(1,1,1))
502
+ sage: q.texture('s')
503
+ sage: q.sphere((0,0,0),1,'s')
504
+ sage: tempname = tmp_filename()
505
+ sage: q.save(tempname)
506
+ """
507
+ if verbose is None:
508
+ verbose = get_verbose()
509
+ tachyon_rt(self.str(), filename, verbose, extra_opts)
510
+
511
+ def _rich_repr_(self, display_manager, **kwds):
512
+ """
513
+ Rich Output Magic Method.
514
+
515
+ See :mod:`sage.repl.rich_output` for details.
516
+
517
+ EXAMPLES::
518
+
519
+ sage: q = Tachyon()
520
+ sage: q.light((1,1,11), 1,(1,1,1))
521
+ sage: q.texture('s')
522
+ sage: q.sphere((0,0,0),1,'s')
523
+ sage: from sage.repl.rich_output import get_display_manager
524
+ sage: dm = get_display_manager()
525
+ sage: q._rich_repr_(dm)
526
+ OutputImagePng container
527
+ """
528
+ OutputImagePng = display_manager.types.OutputImagePng
529
+ if OutputImagePng not in display_manager.supported_output():
530
+ return
531
+ filename = tmp_filename(ext='.png')
532
+ self.save(filename, **kwds)
533
+ from sage.repl.rich_output.buffer import OutputBuffer
534
+ buf = OutputBuffer.from_file(filename)
535
+ return OutputImagePng(buf)
536
+
537
+ def show(self, **kwds):
538
+ r"""
539
+ Create a PNG file of the scene.
540
+
541
+ This method attempts to display the graphics immediately,
542
+ without waiting for the currently running code (if any) to
543
+ return to the command line. Be careful, calling it from within
544
+ a loop will potentially launch a large number of external
545
+ viewer programs.
546
+
547
+ OUTPUT:
548
+
549
+ This method does not return anything. Use :meth:`save` if you
550
+ want to save the figure as an image.
551
+
552
+ EXAMPLES:
553
+
554
+ This example demonstrates how the global Sage verbosity setting
555
+ is used if none is supplied. Firstly, using a global verbosity
556
+ setting of 0 means no extra technical information is displayed,
557
+ and we are simply shown the plot.
558
+
559
+ ::
560
+
561
+ sage: h = Tachyon(xres=512, yres=512, camera_position=(4,-4,3),
562
+ ....: viewdir=(-4,4,-3), raydepth=4)
563
+ sage: h.light((4.4,-4.4,4.4), 0.2, (1,1,1))
564
+ sage: def f(x, y): return float(sin(x*y))
565
+ sage: h.texture('t0', ambient=0.1, diffuse=0.9, specular=0.1,
566
+ ....: opacity=1.0, color=(1.0,0,0))
567
+ sage: h.plot(f, (-4,4), (-4,4), "t0", max_depth=5, initial_depth=3, # needs sage.symbolic
568
+ ....: num_colors=60) # increase min_depth for better picture
569
+ sage: from sage.misc.verbose import set_verbose, get_verbose
570
+ sage: set_verbose(0)
571
+ sage: h.show() # needs sage.symbolic
572
+
573
+ This second example, using a "medium" global verbosity
574
+ setting of 1, displays some extra technical information then
575
+ displays our graph.
576
+
577
+ ::
578
+
579
+ sage: s = Tachyon(xres=512, yres=512, camera_position=(4,-4,3),
580
+ ....: viewdir=(-4,4,-3), raydepth=4)
581
+ sage: s.light((4.4,-4.4,4.4), 0.2, (1,1,1))
582
+ sage: def f(x, y): return float(sin(x*y))
583
+ sage: s.texture('t0', ambient=0.1, diffuse=0.9, specular=0.1,
584
+ ....: opacity=1.0, color=(1.0,0,0))
585
+ sage: s.plot(f, (-4,4), (-4,4), "t0", max_depth=5, initial_depth=3, # needs sage.symbolic
586
+ ....: num_colors=60) # increase min_depth for better picture
587
+ sage: set_verbose(1)
588
+ sage: s.show() # needs sage.symbolic
589
+ ...tachyon...
590
+ Scene contains 2713 objects.
591
+ ...
592
+
593
+ The last example shows how you can override the global Sage
594
+ verbosity setting, my supplying a setting level as an argument.
595
+ In this case we chose the highest verbosity setting level, 2,
596
+ so much more extra technical information is shown, along with
597
+ the plot.
598
+
599
+ ::
600
+
601
+ sage: set_verbose(0)
602
+ sage: d = Tachyon(xres=512, yres=512, camera_position=(4,-4,3),
603
+ ....: viewdir=(-4,4,-3), raydepth=4)
604
+ sage: d.light((4.4,-4.4,4.4), 0.2, (1,1,1))
605
+ sage: def f(x, y): return float(sin(x*y))
606
+ sage: d.texture('t0', ambient=0.1, diffuse=0.9, specular=0.1,
607
+ ....: opacity=1.0, color=(1.0,0,0))
608
+ sage: d.plot(f,(-4,4),(-4,4),"t0",max_depth=5,initial_depth=3, # needs sage.symbolic
609
+ ....: num_colors=60) # increase min_depth for better picture
610
+ sage: get_verbose()
611
+ 0
612
+ sage: d.show(verbose=2) # needs sage.symbolic
613
+ ...tachyon...
614
+ Scene contains 2713 objects.
615
+ ...
616
+ Scene contains 1 non-gridded objects
617
+ ...
618
+ """
619
+ from sage.repl.rich_output import get_display_manager
620
+ dm = get_display_manager()
621
+ dm.display_immediately(self, **kwds)
622
+
623
+ def _res(self):
624
+ r"""
625
+ An internal function that writes the tachyon string for the
626
+ resolution (x and y size of the image).
627
+
628
+ EXAMPLES::
629
+
630
+ sage: t = Tachyon(xres=300, yres=700)
631
+ sage: t._res()
632
+ '\nresolution 300 700\n'
633
+ """
634
+ return f'\nresolution {self._xres} {self._yres}\n'
635
+
636
+ def _camera(self):
637
+ r"""
638
+ An internal function that writes the tachyon string for the
639
+ camera and other rendering information (ray depth, antialiasing).
640
+
641
+ EXAMPLES::
642
+
643
+ sage: t = Tachyon(raydepth=16, zoom=2, antialiasing=True)
644
+ sage: t._camera().split()[3:10]
645
+ ['zoom', '2.0', 'aspectratio', '1.0', 'antialiasing', '1', 'raydepth']
646
+ """
647
+ camera_out = r"""
648
+ camera
649
+ projection %s""" % (tostr(self._projection))
650
+ if self._focallength != '':
651
+ camera_out = camera_out + r"""
652
+ focallength %s""" % (float(self._focallength))
653
+ if self._aperture != '':
654
+ camera_out = camera_out + r"""
655
+ aperture %s""" % (float(self._aperture))
656
+ camera_out = camera_out + fr"""
657
+ zoom {float(self._zoom)}
658
+ aspectratio {float(self._aspectratio)}
659
+ antialiasing {int(self._antialiasing)}
660
+ raydepth {int(self._raydepth)}
661
+ center {tostr(self._camera_position)}
662
+ viewdir {tostr(self._viewdir)}
663
+ updir {tostr(self._updir)}"""
664
+ if self._frustum != '':
665
+ camera_out = camera_out + r"""
666
+ frustum %s""" % (tostr(self._frustum))
667
+ camera_out = camera_out + r"""
668
+ end_camera"""
669
+ return camera_out
670
+
671
+ def str(self):
672
+ r"""
673
+ Return the complete tachyon scene file as a string.
674
+
675
+ EXAMPLES::
676
+
677
+ sage: t = Tachyon(xres=500,yres=500, camera_position=(2,0,0))
678
+ sage: t.light((4,3,2), 0.2, (1,1,1))
679
+ sage: t.texture('t2', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(1,0,0))
680
+ sage: t.texture('t3', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(0,1,0))
681
+ sage: t.texture('t4', ambient=0.1, diffuse=0.9, specular=0.5, opacity=1.0, color=(0,0,1))
682
+ sage: t.sphere((0,0.5,0), 0.2, 't2')
683
+ sage: t.sphere((0.5,0,0), 0.2, 't3')
684
+ sage: t.sphere((0,0,0.5), 0.2, 't4')
685
+ sage: 'PLASTIC' in t.str()
686
+ True
687
+ """
688
+ return r"""
689
+ begin_scene
690
+ {}
691
+ {}
692
+ {}
693
+ end_scene""".format(self._res(),
694
+ self._camera(),
695
+ '\n'.join(x.str() for x in self._objects))
696
+
697
+ def light(self, center, radius, color):
698
+ r"""
699
+ Create a light source of the given center, radius, and color.
700
+
701
+ EXAMPLES::
702
+
703
+ sage: q = Tachyon()
704
+ sage: q.light((1,1,1),1.0,(.2,0,.8))
705
+ sage: q.str().split('\n')[17]
706
+ ' light center 1.0 1.0 1.0 '
707
+ """
708
+ self._objects.append(Light(center, radius, color))
709
+
710
+ def texfunc(self, type=0, center=(0, 0, 0), rotate=(0, 0, 0),
711
+ scale=(1, 1, 1),
712
+ imagefile=''):
713
+ r"""
714
+ INPUT:
715
+
716
+ - ``type`` -- (default: 0)
717
+
718
+ 0. No special texture, plain shading
719
+ 1. 3D checkerboard function, like a rubik's cube
720
+ 2. Grit Texture, randomized surface color
721
+ 3. 3D marble texture, uses object's base color
722
+ 4. 3D wood texture, light and dark brown, not very good yet
723
+ 5. 3D gradient noise function (can't remember what it looks
724
+ like)
725
+ 6. Don't remember
726
+ 7. Cylindrical Image Map, requires ppm filename (with path)
727
+ 8. Spherical Image Map, requires ppm filename (with path)
728
+ 9. Planar Image Map, requires ppm filename (with path)
729
+
730
+ - ``center`` -- (default: (0,0,0))
731
+ - ``rotate`` -- (default: (0,0,0))
732
+ - ``scale`` -- (default: (1,1,1))
733
+
734
+ EXAMPLES: We draw an infinite checkerboard::
735
+
736
+ sage: t = Tachyon(camera_position=(2,7,4), look_at=(2,0,0))
737
+ sage: t.texture('black', color=(0,0,0), texfunc=1)
738
+ sage: t.plane((0,0,0),(0,0,1),'black')
739
+ sage: t.show()
740
+ """
741
+ type = int(type)
742
+ if type < 0 or type > 9:
743
+ raise ValueError("type must be an integer between 0 and 9")
744
+ return Texfunc(type, center, rotate, scale, imagefile=imagefile).str()
745
+
746
+ def texture(self, name, ambient=0.2, diffuse=0.8,
747
+ specular=0.0, opacity=1.0,
748
+ color=(1.0, 0.0, 0.5), texfunc=0, phong=0, phongsize=.5,
749
+ phongtype='PLASTIC', imagefile=''):
750
+ r"""
751
+ INPUT:
752
+
753
+ - ``name`` -- string; the name of the texture (to be
754
+ used later)
755
+
756
+ - ``ambient`` -- (default: 0.2)
757
+
758
+ - ``diffuse`` -- (default: 0.8)
759
+
760
+ - ``specular`` -- (default: 0.0)
761
+
762
+ - ``opacity`` -- (default: 1.0)
763
+
764
+ - ``color`` -- (default: (1.0,0.0,0.5))
765
+
766
+ - ``texfunc`` -- (default: 0) a texture function; this
767
+ is either the output of self.texfunc, or a number between 0 and 9,
768
+ inclusive. See the docs for self.texfunc.
769
+
770
+ - ``phong`` -- (default: 0)
771
+
772
+ - ``phongsize`` -- (default: 0.5)
773
+
774
+ - ``phongtype`` -- (default: ``'PLASTIC'``)
775
+
776
+ EXAMPLES:
777
+
778
+ We draw a scene with 4 spheres that illustrates various uses of
779
+ the texture command::
780
+
781
+ sage: t = Tachyon(camera_position=(2,5,4), look_at=(2,0,0), raydepth=6)
782
+ sage: t.light((10,3,4), 1, (1,1,1))
783
+ sage: t.texture('mirror', ambient=0.05, diffuse=0.05, specular=.9,
784
+ ....: opacity=0.9, color=(.8,.8,.8))
785
+ sage: t.texture('grey', color=(.8,.8,.8), texfunc=3)
786
+ sage: t.plane((0,0,0),(0,0,1),'grey')
787
+ sage: t.sphere((4,-1,1), 1, 'mirror')
788
+ sage: t.sphere((0,-1,1), 1, 'mirror')
789
+ sage: t.sphere((2,-1,1), 0.5, 'mirror')
790
+ sage: t.sphere((2,1,1), 0.5, 'mirror')
791
+ sage: show(t) # known bug (trac #7232)
792
+ """
793
+ if texfunc and not isinstance(texfunc, Texfunc):
794
+ texfunc = self.texfunc(int(texfunc), imagefile=imagefile)
795
+ self._objects.append(Texture(name, ambient, diffuse,
796
+ specular, opacity, color, texfunc,
797
+ phong, phongsize, phongtype,
798
+ imagefile=imagefile))
799
+
800
+ def texture_recolor(self, name, colors):
801
+ r"""
802
+ Recolor default textures.
803
+
804
+ EXAMPLES::
805
+
806
+ sage: t = Tachyon()
807
+ sage: t.texture('s')
808
+ sage: q = t.texture_recolor('s',[(0,0,1)])
809
+ sage: t._objects[1]._color
810
+ (0.0, 0.0, 1.0)
811
+ """
812
+ base_tex = None
813
+ names = []
814
+ ident = "SAGETEX%d" % len(self._objects) # don't collide with other texture names
815
+
816
+ for o in self._objects:
817
+ if isinstance(o, Texture) and o._name == name:
818
+ base_tex = o
819
+ break
820
+ if base_tex is None:
821
+ base_tex = Texture(name)
822
+
823
+ for i in range(len(colors)):
824
+ n = "%s_%d" % (ident, i)
825
+ self._objects.append(base_tex.recolor(n, colors[i]))
826
+ names.append(n)
827
+
828
+ return names
829
+
830
+ def sphere(self, center, radius, texture):
831
+ r"""
832
+ Create the scene information for a sphere with the given
833
+ center, radius, and texture.
834
+
835
+ EXAMPLES::
836
+
837
+ sage: t = Tachyon()
838
+ sage: t.texture('sphere_texture')
839
+ sage: t.sphere((1,2,3), .1, 'sphere_texture')
840
+ sage: t._objects[1].str()
841
+ '\n sphere center 1.0 2.0 3.0 rad 0.1 sphere_texture\n '
842
+ """
843
+ self._objects.append(Sphere(center, radius, texture))
844
+
845
+ def ring(self, center, normal, inner, outer, texture):
846
+ r"""
847
+ Create the scene information for a ring with the given parameters.
848
+
849
+ EXAMPLES::
850
+
851
+ sage: t = Tachyon()
852
+ sage: t.ring([0,0,0], [0,0,1], 1.0, 2.0, 's')
853
+ sage: t._objects[0]._center
854
+ (0.0, 0.0, 0.0)
855
+ """
856
+ self._objects.append(Ring(center, normal, inner, outer, texture))
857
+
858
+ def cylinder(self, center, axis, radius, texture):
859
+ r"""
860
+ Create the scene information for a infinite cylinder with the
861
+ given center, axis direction, radius, and texture.
862
+
863
+ EXAMPLES::
864
+
865
+ sage: t = Tachyon()
866
+ sage: t.texture('c')
867
+ sage: t.cylinder((0,0,0),(-1,-1,-1),.1,'c')
868
+ """
869
+ self._objects.append(Cylinder(center, axis, radius, texture))
870
+
871
+ def plane(self, center, normal, texture):
872
+ r"""
873
+ Create an infinite plane with the given center and normal.
874
+
875
+ TESTS::
876
+
877
+ sage: t = Tachyon()
878
+ sage: t.plane((0,0,0),(1,1,1),'s')
879
+ sage: plane_pos = t.str().index('plane')
880
+ sage: t.str()[plane_pos:plane_pos+42]
881
+ 'plane center 0.0 0.0 0.0 normal 1.0 1.0'
882
+ """
883
+ self._objects.append(Plane(center, normal, texture))
884
+
885
+ def axis_aligned_box(self, min_p, max_p, texture):
886
+ r"""
887
+ Create an axis-aligned box with minimal point ``min_p`` and
888
+ maximum point ``max_p``.
889
+
890
+ EXAMPLES::
891
+
892
+ sage: t = Tachyon()
893
+ sage: t.axis_aligned_box((0,0,0),(2,2,2),'s')
894
+ """
895
+ self._objects.append(Axis_aligned_box(min_p, max_p, texture))
896
+
897
+ def fcylinder(self, base, apex, radius, texture):
898
+ r"""
899
+ Finite cylinders are almost the same as infinite ones, but the
900
+ center and length of the axis determine the extents of the
901
+ cylinder.
902
+
903
+ The finite cylinder is also really a shell, it
904
+ does not have any caps. If you need to close off the ends of
905
+ the cylinder, use two ring objects, with the inner radius set
906
+ to 0.0 and the normal set to be the axis of the cylinder.
907
+ Finite cylinders are built this way to enhance speed.
908
+
909
+ EXAMPLES::
910
+
911
+ sage: t = Tachyon()
912
+ sage: t.fcylinder((1,1,1),(1,2,3),.01,'s')
913
+ sage: len(t.str())
914
+ 451
915
+ """
916
+ self._objects.append(FCylinder(base, apex, radius, texture))
917
+
918
+ def triangle(self, vertex_1, vertex_2, vertex_3, texture):
919
+ r"""
920
+ Create a triangle with the given vertices and texture.
921
+
922
+ EXAMPLES::
923
+
924
+ sage: t = Tachyon()
925
+ sage: t.texture('s')
926
+ sage: t.triangle([1,2,3],[4,5,6],[7,8,10],'s')
927
+ sage: t._objects[1].get_vertices()
928
+ ([1, 2, 3], [4, 5, 6], [7, 8, 10])
929
+ """
930
+ self._objects.append(TachyonTriangle(vertex_1, vertex_2, vertex_3,
931
+ texture))
932
+
933
+ def smooth_triangle(self, vertex_1, vertex_2, vertex_3, normal_1, normal_2, normal_3, texture):
934
+ r"""
935
+ Create a triangle along with a normal vector for smoothing.
936
+
937
+ EXAMPLES::
938
+
939
+ sage: t = Tachyon()
940
+ sage: t.light((1,1,1),.1,(1,1,1))
941
+ sage: t.texture('s')
942
+ sage: t.smooth_triangle([0,0,0],[0,0,1],[0,1,0],[0,1,1],[-1,1,2],[3,0,0],'s')
943
+ sage: t._objects[2].get_vertices()
944
+ ([0, 0, 0], [0, 0, 1], [0, 1, 0])
945
+ sage: t._objects[2].get_normals()
946
+ ([0, 1, 1], [-1, 1, 2], [3, 0, 0])
947
+ """
948
+ self._objects.append(TachyonSmoothTriangle(vertex_1, vertex_2, vertex_3, normal_1, normal_2, normal_3, texture))
949
+
950
+ def fractal_landscape(self, res, scale, center, texture):
951
+ r"""
952
+ Axis-aligned fractal landscape.
953
+
954
+ Not very useful at the moment.
955
+
956
+ EXAMPLES::
957
+
958
+ sage: t = Tachyon()
959
+ sage: t.texture('s')
960
+ sage: t.fractal_landscape([30,30],[80,80],[0,0,0],'s')
961
+ sage: len(t._objects)
962
+ 2
963
+ """
964
+ self._objects.append(FractalLandscape(res, scale, center, texture))
965
+
966
+ def plot(self, f, xmin_xmax, ymin_ymax, texture, grad_f=None,
967
+ max_bend=.7, max_depth=5, initial_depth=3, num_colors=None):
968
+ r"""
969
+ INPUT:
970
+
971
+ - ``f`` -- function of two variables, which returns a
972
+ float (or coercible to a float) (xmin,xmax)
973
+
974
+ - ``(ymin,ymax)`` -- defines the rectangle to plot over
975
+ texture: Name of texture to be used Optional arguments:
976
+
977
+ - ``grad_f`` -- gradient function. If specified,
978
+ smooth triangles will be used
979
+
980
+ - ``max_bend`` -- cosine of the threshold angle
981
+ between triangles used to determine whether or not to recurse after
982
+ the minimum depth
983
+
984
+ - ``max_depth`` -- maximum recursion depth. Maximum
985
+ triangles plotted = `2^{2*max_depth}`
986
+
987
+ - ``initial_depth`` -- minimum recursion depth. No
988
+ error-tolerance checking is performed below this depth. Minimum
989
+ triangles plotted: `2^{2*min_depth}`
990
+
991
+ - ``num_colors`` -- number of rainbow bands to color
992
+ the plot with. Texture supplied will be cloned (with different
993
+ colors) using the texture_recolor method of the Tachyon object.
994
+
995
+
996
+ Plots a function by constructing a mesh with nonstandard sampling
997
+ density without gaps. At very high resolutions (depths 10) it
998
+ becomes very slow. Cython may help. Complexity is approx.
999
+ `O(2^{2*maxdepth})`. This algorithm has been optimized for
1000
+ speed, not memory - values from f(x,y) are recycled rather than
1001
+ calling the function multiple times. At high recursion depth, this
1002
+ may cause problems for some machines.
1003
+
1004
+ Flat Triangles::
1005
+
1006
+ sage: t = Tachyon(xres=512, yres=512, camera_position=(4,-4,3),
1007
+ ....: viewdir=(-4,4,-3), raydepth=4)
1008
+ sage: t.light((4.4,-4.4,4.4), 0.2, (1,1,1))
1009
+ sage: def f(x, y): return float(sin(x*y))
1010
+ sage: t.texture('t0', ambient=0.1, diffuse=0.9, specular=0.1,
1011
+ ....: opacity=1.0, color=(1.0,0,0))
1012
+ sage: t.plot(f, (-4,4), (-4,4), "t0", max_depth=5, initial_depth=3, # needs sage.symbolic
1013
+ ....: num_colors=60) # increase min_depth for better picture
1014
+ sage: t.show(verbose=1) # needs sage.symbolic
1015
+ ...tachyon...
1016
+ Scene contains 2713 objects.
1017
+ ...
1018
+
1019
+ Plotting with Smooth Triangles (requires explicit gradient
1020
+ function)::
1021
+
1022
+ sage: t = Tachyon(xres=512, yres=512, camera_position=(4,-4,3),
1023
+ ....: viewdir=(-4,4,-3), raydepth=4)
1024
+ sage: t.light((4.4,-4.4,4.4), 0.2, (1,1,1))
1025
+ sage: def f(x, y): return float(sin(x*y))
1026
+ sage: def g(x, y): return (float(y*cos(x*y)), float(x*cos(x*y)), 1)
1027
+ sage: t.texture('t0', ambient=0.1, diffuse=0.9, specular=0.1,
1028
+ ....: opacity=1.0, color=(1.0,0,0))
1029
+ sage: t.plot(f, (-4,4), (-4,4), "t0", max_depth=5, initial_depth=3, # needs sage.symbolic
1030
+ ....: grad_f=g) # increase min_depth for better picture
1031
+ sage: t.show(verbose=1) # needs sage.symbolic
1032
+ ...tachyon...
1033
+ Scene contains 2713 objects.
1034
+ ...
1035
+
1036
+ Preconditions: f is a scalar function of two variables, grad_f is
1037
+ None or a triple-valued function of two variables, min_x !=
1038
+ max_x, min_y != max_y
1039
+
1040
+ ::
1041
+
1042
+ sage: f = lambda x,y: x*y
1043
+ sage: t = Tachyon()
1044
+ sage: t.plot(f,(2.,2.),(-2.,2.),'')
1045
+ Traceback (most recent call last):
1046
+ ...
1047
+ ValueError: plot rectangle is really a line; make sure min_x != max_x and min_y != max_y
1048
+ """
1049
+ (xmin, xmax) = xmin_xmax
1050
+ (ymin, ymax) = ymin_ymax
1051
+ factory = TachyonTriangleFactory(self, texture)
1052
+ plot = TrianglePlot(factory, f, (xmin, xmax), (ymin, ymax), g=grad_f,
1053
+ min_depth=initial_depth, max_depth=max_depth,
1054
+ max_bend=max_bend, num_colors=num_colors)
1055
+ self._objects.append(plot)
1056
+
1057
+ def parametric_plot(self, f, t_0, t_f, tex, r=.1, cylinders=True,
1058
+ min_depth=4, max_depth=8, e_rel=.01, e_abs=.01):
1059
+ r"""
1060
+ Plot a space curve as a series of spheres and finite cylinders.
1061
+
1062
+ Example (twisted cubic) ::
1063
+
1064
+ sage: f = lambda t: (t,t^2,t^3)
1065
+ sage: t = Tachyon(camera_position=(5,0,4))
1066
+ sage: t.texture('t')
1067
+ sage: t.light((-20,-20,40), 0.2, (1,1,1))
1068
+ sage: t.parametric_plot(f,-5,5,'t',min_depth=6)
1069
+ sage: t.show(verbose=1)
1070
+ ...tachyon...
1071
+ Scene contains 482 objects.
1072
+ ...
1073
+ """
1074
+ self._objects.append(
1075
+ ParametricPlot(f, t_0, t_f, tex, r=r, cylinders=cylinders,
1076
+ min_depth=min_depth, max_depth=max_depth,
1077
+ e_rel=.01, e_abs=.01))
1078
+
1079
+
1080
+ class Light:
1081
+ r"""
1082
+ Represent lighting objects.
1083
+
1084
+ EXAMPLES::
1085
+
1086
+ sage: from sage.plot.plot3d.tachyon import Light
1087
+ sage: q = Light((1,1,1), 1, (1,1,1))
1088
+ sage: q._center
1089
+ (1.0, 1.0, 1.0)
1090
+ """
1091
+ def __init__(self, center, radius, color):
1092
+ r"""
1093
+ Store the center, radius and color.
1094
+
1095
+ EXAMPLES::
1096
+
1097
+ sage: from sage.plot.plot3d.tachyon import Light
1098
+ sage: q = Light((1,1,1), 1, (1,1,1))
1099
+ sage: print(q._center, q._color, q._radius)
1100
+ (1.0, 1.0, 1.0) (1.0, 1.0, 1.0) 1.0
1101
+ """
1102
+ x, y, z = center
1103
+ self._center = (float(x), float(y), float(z))
1104
+ self._radius = float(radius)
1105
+ r, g, b = color
1106
+ self._color = (float(r), float(g), float(b))
1107
+
1108
+ def str(self):
1109
+ r"""
1110
+ Return the tachyon string defining the light source.
1111
+
1112
+ EXAMPLES::
1113
+
1114
+ sage: from sage.plot.plot3d.tachyon import Light
1115
+ sage: q = Light((1,1,1), 1, (1,1,1))
1116
+ sage: print(q.str())
1117
+ light center 1.0 1.0 1.0
1118
+ rad 1.0
1119
+ color 1.0 1.0 1.0
1120
+ """
1121
+ return fr"""
1122
+ light center {tostr(self._center)}
1123
+ rad {self._radius}
1124
+ color {tostr(self._color)}
1125
+ """
1126
+
1127
+
1128
+ class Texfunc:
1129
+
1130
+ def __init__(self, ttype=0, center=(0, 0, 0), rotate=(0, 0, 0),
1131
+ scale=(1, 1, 1), imagefile=''):
1132
+ r"""
1133
+ Create a texture function.
1134
+
1135
+ EXAMPLES::
1136
+
1137
+ sage: from sage.plot.plot3d.tachyon import Texfunc
1138
+ sage: t = Texfunc()
1139
+ sage: t._ttype
1140
+ 0
1141
+ """
1142
+ self._ttype = ttype
1143
+ x, y, z = center
1144
+ self._center = (float(x), float(y), float(z))
1145
+ x, y, z = rotate
1146
+ self._rotate = (float(x), float(y), float(z))
1147
+ x, y, z = scale
1148
+ self._scale = (float(x), float(y), float(z))
1149
+ self._imagefile = imagefile
1150
+
1151
+ def str(self):
1152
+ r"""
1153
+ Return the scene string for this texture function.
1154
+
1155
+ EXAMPLES::
1156
+
1157
+ sage: from sage.plot.plot3d.tachyon import Texfunc
1158
+ sage: t = Texfunc()
1159
+ sage: t.str()
1160
+ '0'
1161
+ """
1162
+ if self._ttype == 0:
1163
+ return "0"
1164
+ elif self._ttype < 7 and self._ttype > 0:
1165
+ return r"""%d center %s rotate %s scale %s""" % (
1166
+ self._ttype,
1167
+ tostr(self._center),
1168
+ tostr(self._rotate),
1169
+ tostr(self._scale))
1170
+ elif self._ttype < 9:
1171
+ return r"""%d %s center %s rotate %s scale %s""" % (
1172
+ self._ttype,
1173
+ self._imagefile,
1174
+ tostr(self._center),
1175
+ tostr(self._rotate),
1176
+ tostr(self._scale))
1177
+ elif self._ttype == 9:
1178
+ return r"""%d %s center %s rotate %s scale %s
1179
+ uaxis 1.0 0.0 0.0
1180
+ vaxis 0.0 1.0 0.0""" % (
1181
+ self._ttype,
1182
+ self._imagefile,
1183
+ tostr(self._center),
1184
+ tostr(self._rotate),
1185
+ tostr(self._scale))
1186
+ else:
1187
+ raise ValueError
1188
+
1189
+
1190
+ class Texture:
1191
+
1192
+ def __init__(self, name, ambient=0.2, diffuse=0.8,
1193
+ specular=0.0, opacity=1.0,
1194
+ color=(1.0, 0.0, 0.5), texfunc=0,
1195
+ phong=0, phongsize=0, phongtype='PLASTIC', imagefile=''):
1196
+ r"""
1197
+ Store texture information.
1198
+
1199
+ EXAMPLES::
1200
+
1201
+ sage: from sage.plot.plot3d.tachyon import Texture
1202
+ sage: t = Texture('w')
1203
+ sage: t.str().split()[2:6]
1204
+ ['ambient', '0.2', 'diffuse', '0.8']
1205
+ """
1206
+ self._name = str(name)
1207
+ self._ambient = float(ambient)
1208
+ self._diffuse = float(diffuse)
1209
+ self._specular = float(specular)
1210
+ self._opacity = float(opacity)
1211
+ r, g, b = color
1212
+ self._color = (float(r), float(g), float(b))
1213
+ self._texfunc = texfunc
1214
+ self._phong = float(phong)
1215
+ self._phongsize = float(phongsize)
1216
+ self._phongtype = str(phongtype)
1217
+ self._imagefile = str(imagefile)
1218
+
1219
+ def recolor(self, name, color):
1220
+ r"""
1221
+ Return a texture with the new given color.
1222
+
1223
+ EXAMPLES::
1224
+
1225
+ sage: from sage.plot.plot3d.tachyon import Texture
1226
+ sage: t2 = Texture('w')
1227
+ sage: t2w = t2.recolor('w2', (.1,.2,.3))
1228
+ sage: t2ws = t2w.str()
1229
+ sage: color_index = t2ws.find('color')
1230
+ sage: t2ws[color_index:color_index+20]
1231
+ 'color 0.1 0.2 0.3 '
1232
+ """
1233
+ return Texture(name, self._ambient, self._diffuse, self._specular,
1234
+ self._opacity,
1235
+ color, self._texfunc, self._phong, self._phongsize,
1236
+ self._phongtype, self._imagefile)
1237
+
1238
+ def str(self):
1239
+ r"""
1240
+ Return the scene string for this texture.
1241
+
1242
+ EXAMPLES::
1243
+
1244
+ sage: from sage.plot.plot3d.tachyon import Texture
1245
+ sage: t = Texture('w')
1246
+ sage: t.str().split()[2:6]
1247
+ ['ambient', '0.2', 'diffuse', '0.8']
1248
+ """
1249
+ return r"""
1250
+ texdef {} ambient {} diffuse {} specular {} opacity {}
1251
+ phong {} {} phong_size {}
1252
+ color {} texfunc {}
1253
+ """.format(self._name,
1254
+ self._ambient,
1255
+ self._diffuse,
1256
+ self._specular,
1257
+ self._opacity,
1258
+ self._phongtype,
1259
+ self._phong,
1260
+ self._phongsize,
1261
+ tostr(self._color),
1262
+ self._texfunc)
1263
+
1264
+
1265
+ class Sphere:
1266
+ r"""
1267
+ A class for creating spheres in tachyon.
1268
+ """
1269
+ def __init__(self, center, radius, texture):
1270
+ r"""
1271
+ Store the center, radius, and texture information in a class.
1272
+
1273
+ EXAMPLES::
1274
+
1275
+ sage: t = Tachyon()
1276
+ sage: from sage.plot.plot3d.tachyon import Sphere
1277
+ sage: t.texture('r', color=(.8,0,0), ambient=.1)
1278
+ sage: s = Sphere((1,1,1), 1, 'r')
1279
+ sage: s._radius
1280
+ 1.0
1281
+ """
1282
+ x, y, z = center
1283
+ self._center = (float(x), float(y), float(z))
1284
+ self._radius = float(radius)
1285
+ self._texture = texture
1286
+
1287
+ def str(self):
1288
+ r"""
1289
+ Return the scene string for the sphere.
1290
+
1291
+ EXAMPLES::
1292
+
1293
+ sage: t = Tachyon()
1294
+ sage: from sage.plot.plot3d.tachyon import Sphere
1295
+ sage: t.texture('r', color=(.8,0,0), ambient = .1)
1296
+ sage: s = Sphere((1,1,1), 1, 'r')
1297
+ sage: s.str()
1298
+ '\n sphere center 1.0 1.0 1.0 rad 1.0 r\n '
1299
+ """
1300
+ return fr"""
1301
+ sphere center {tostr(self._center)} rad {self._radius} {self._texture}
1302
+ """
1303
+
1304
+
1305
+ class Ring:
1306
+ r"""
1307
+ An annulus of zero thickness.
1308
+ """
1309
+ def __init__(self, center, normal, inner, outer, texture):
1310
+ r"""
1311
+ Create a ring with the given center, normal, inner radius,
1312
+ outer radius, and texture.
1313
+
1314
+ EXAMPLES::
1315
+
1316
+ sage: from sage.plot.plot3d.tachyon import Ring
1317
+ sage: r = Ring((1,1,1), (1,1,0), 1.0, 2.0, 's')
1318
+ sage: r._center
1319
+ (1.0, 1.0, 1.0)
1320
+ """
1321
+ x, y, z = center
1322
+ self._center = (float(x), float(y), float(z))
1323
+ x, y, z = normal
1324
+ self._normal = (float(x), float(y), float(z))
1325
+ self._inner = float(inner)
1326
+ self._outer = float(outer)
1327
+ self._texture = texture
1328
+
1329
+ def str(self):
1330
+ r"""
1331
+ Return the scene string of the ring.
1332
+
1333
+ EXAMPLES::
1334
+
1335
+ sage: from sage.plot.plot3d.tachyon import Ring
1336
+ sage: r = Ring((0,0,0), (1,1,0), 1.0, 2.0, 's')
1337
+ sage: r.str()
1338
+ '\n ring center 0.0 0.0 0.0 normal 1.0 1.0 0.0 inner 1.0 outer 2.0 s\n '
1339
+ """
1340
+ return r"""
1341
+ ring center {} normal {} inner {} outer {} {}
1342
+ """.format(tostr(self._center), tostr(self._normal),
1343
+ self._inner, self._outer, self._texture)
1344
+
1345
+
1346
+ class FractalLandscape:
1347
+ r"""
1348
+ Axis-aligned fractal landscape.
1349
+
1350
+ Does not seem very useful at the moment, but perhaps will be improved in the future.
1351
+ """
1352
+ def __init__(self, res, scale, center, texture):
1353
+ r"""
1354
+ Create a fractal landscape in tachyon.
1355
+
1356
+ EXAMPLES::
1357
+
1358
+ sage: from sage.plot.plot3d.tachyon import FractalLandscape
1359
+ sage: fl = FractalLandscape([20,20],[30,30],[1,2,3],'s')
1360
+ sage: fl._center
1361
+ (1.0, 2.0, 3.0)
1362
+ """
1363
+ x, y = res
1364
+ self._res = (int(x), int(y))
1365
+ x, y = scale
1366
+ self._scale = (int(x), int(y))
1367
+ x, y, z = center
1368
+ self._center = (float(x), float(y), float(z))
1369
+ self._texture = texture
1370
+
1371
+ def str(self):
1372
+ r"""
1373
+ Return the scene string of the fractal landscape.
1374
+
1375
+ EXAMPLES::
1376
+
1377
+ sage: from sage.plot.plot3d.tachyon import FractalLandscape
1378
+ sage: fl = FractalLandscape([20,20],[30,30],[1,2,3],'s')
1379
+ sage: fl.str()
1380
+ '\n scape res 20 20 scale 30 30 center 1.0 2.0 3.0 s\n '
1381
+ """
1382
+ return r"""
1383
+ scape res {} scale {} center {} {}
1384
+ """.format(tostr(self._res, 2, int), tostr(self._scale, 2, int),
1385
+ tostr(self._center), self._texture)
1386
+
1387
+
1388
+ class Cylinder:
1389
+ r"""
1390
+ An infinite cylinder.
1391
+ """
1392
+ def __init__(self, center, axis, radius, texture):
1393
+ r"""
1394
+ Create a cylinder with the given parameters.
1395
+
1396
+ EXAMPLES::
1397
+
1398
+ sage: t = Tachyon()
1399
+ sage: from sage.plot.plot3d.tachyon import Cylinder
1400
+ sage: c = Cylinder((0,0,0),(1,1,1),.1,'s')
1401
+ sage: c.str()
1402
+ '\n cylinder center 0.0 0.0 0.0 axis 1.0 1.0 1.0 rad 0.1 s\n '
1403
+ """
1404
+ x, y, z = center
1405
+ self._center = (float(x), float(y), float(z))
1406
+ x, y, z = axis
1407
+ self._axis = (float(x), float(y), float(z))
1408
+ self._radius = float(radius)
1409
+ self._texture = texture
1410
+
1411
+ def str(self):
1412
+ r"""
1413
+ Return the scene string of the cylinder.
1414
+
1415
+ EXAMPLES::
1416
+
1417
+ sage: t = Tachyon()
1418
+ sage: from sage.plot.plot3d.tachyon import Cylinder
1419
+ sage: c = Cylinder((0,0,0),(1,1,1),.1,'s')
1420
+ sage: c.str()
1421
+ '\n cylinder center 0.0 0.0 0.0 axis 1.0 1.0 1.0 rad 0.1 s\n '
1422
+ """
1423
+ return r"""
1424
+ cylinder center {} axis {} rad {} {}
1425
+ """.format(tostr(self._center), tostr(self._axis), self._radius, self._texture)
1426
+
1427
+
1428
+ class Plane:
1429
+ r"""
1430
+ An infinite plane.
1431
+ """
1432
+ def __init__(self, center, normal, texture):
1433
+ r"""
1434
+ Create the plane object.
1435
+
1436
+ EXAMPLES::
1437
+
1438
+ sage: from sage.plot.plot3d.tachyon import Plane
1439
+ sage: p = Plane((1,2,3), (1,2,4), 's')
1440
+ sage: p.str()
1441
+ '\n plane center 1.0 2.0 3.0 normal 1.0 2.0 4.0 s\n '
1442
+ """
1443
+ x, y, z = center
1444
+ self._center = (float(x), float(y), float(z))
1445
+ x, y, z = normal
1446
+ self._normal = (float(x), float(y), float(z))
1447
+ self._texture = texture
1448
+
1449
+ def str(self):
1450
+ r"""
1451
+ Return the scene string of the plane.
1452
+
1453
+ EXAMPLES::
1454
+
1455
+ sage: from sage.plot.plot3d.tachyon import Plane
1456
+ sage: p = Plane((1,2,3),(1,2,4),'s')
1457
+ sage: p.str()
1458
+ '\n plane center 1.0 2.0 3.0 normal 1.0 2.0 4.0 s\n '
1459
+ """
1460
+ return fr"""
1461
+ plane center {tostr(self._center)} normal {tostr(self._normal)} {self._texture}
1462
+ """
1463
+
1464
+
1465
+ class FCylinder:
1466
+ r"""
1467
+ A finite cylinder.
1468
+ """
1469
+ def __init__(self, base, apex, radius, texture):
1470
+ r"""
1471
+ Create a finite cylinder object.
1472
+
1473
+ EXAMPLES::
1474
+
1475
+ sage: from sage.plot.plot3d.tachyon import FCylinder
1476
+ sage: fc = FCylinder((0,0,0),(1,1,1),.1,'s')
1477
+ sage: fc.str()
1478
+ '\n fcylinder base 0.0 0.0 0.0 apex 1.0 1.0 1.0 rad 0.1 s\n '
1479
+ """
1480
+ x, y, z = base
1481
+ self._center = (float(x), float(y), float(z))
1482
+ x, y, z = apex
1483
+ self._axis = (float(x), float(y), float(z))
1484
+ self._radius = float(radius)
1485
+ self._texture = texture
1486
+
1487
+ def str(self):
1488
+ r"""
1489
+ Return the scene string of the finite cylinder.
1490
+
1491
+ EXAMPLES::
1492
+
1493
+ sage: from sage.plot.plot3d.tachyon import FCylinder
1494
+ sage: fc = FCylinder((0,0,0),(1,1,1),.1,'s')
1495
+ sage: fc.str()
1496
+ '\n fcylinder base 0.0 0.0 0.0 apex 1.0 1.0 1.0 rad 0.1 s\n '
1497
+ """
1498
+ return r"""
1499
+ fcylinder base {} apex {} rad {} {}
1500
+ """.format(tostr(self._center), tostr(self._axis), self._radius, self._texture)
1501
+
1502
+
1503
+ class Axis_aligned_box:
1504
+ r"""
1505
+ Box with axis-aligned edges with the given min and max coordinates.
1506
+ """
1507
+ def __init__(self, min_p, max_p, texture):
1508
+ r"""
1509
+ Create the axis-aligned box object.
1510
+
1511
+ EXAMPLES::
1512
+
1513
+ sage: from sage.plot.plot3d.tachyon import Axis_aligned_box
1514
+ sage: aab = Axis_aligned_box((0,0,0),(1,1,1),'s')
1515
+ sage: aab.str()
1516
+ '\n box min 0.0 0.0 0.0 max 1.0 1.0 1.0 s\n '
1517
+ """
1518
+ x, y, z = min_p
1519
+ self._min_p = (float(x), float(y), float(z))
1520
+ x, y, z = max_p
1521
+ self._max_p = (float(x), float(y), float(z))
1522
+ self._texture = texture
1523
+
1524
+ def str(self):
1525
+ r"""
1526
+ Return the scene string of the axis-aligned box.
1527
+
1528
+ EXAMPLES::
1529
+
1530
+ sage: from sage.plot.plot3d.tachyon import Axis_aligned_box
1531
+ sage: aab = Axis_aligned_box((0,0,0),(1,1,1),'s')
1532
+ sage: aab.str()
1533
+ '\n box min 0.0 0.0 0.0 max 1.0 1.0 1.0 s\n '
1534
+ """
1535
+ return fr"""
1536
+ box min {tostr(self._min_p)} max {tostr(self._max_p)} {self._texture}
1537
+ """
1538
+
1539
+
1540
+ class TachyonTriangle(Triangle):
1541
+ r"""
1542
+ Basic triangle class.
1543
+ """
1544
+ def str(self):
1545
+ r"""
1546
+ Return the scene string for a triangle.
1547
+
1548
+ EXAMPLES::
1549
+
1550
+ sage: from sage.plot.plot3d.tachyon import TachyonTriangle
1551
+ sage: t = TachyonTriangle([-1,-1,-1],[0,0,0],[1,2,3])
1552
+ sage: t.str()
1553
+ '\n TRI V0 -1.0 -1.0 -1.0 V1 0.0 0.0 0.0 V2 1.0 2.0 3.0 \n 0\n '
1554
+ """
1555
+ return fr"""
1556
+ TRI V0 {tostr(self._a)} V1 {tostr(self._b)} V2 {tostr(self._c)}
1557
+ {self._color}
1558
+ """
1559
+
1560
+
1561
+ class TachyonSmoothTriangle(SmoothTriangle):
1562
+ r"""
1563
+ A triangle along with a normal vector, which is used for smoothing.
1564
+ """
1565
+ def str(self):
1566
+ r"""
1567
+ Return the scene string for a smoothed triangle.
1568
+
1569
+ EXAMPLES::
1570
+
1571
+ sage: from sage.plot.plot3d.tachyon import TachyonSmoothTriangle
1572
+ sage: t = TachyonSmoothTriangle([-1,-1,-1],[0,0,0],[1,2,3],[1,0,0],[0,1,0],[0,0,1])
1573
+ sage: t.str()
1574
+ '\n STRI V0 ... 1.0 0.0 0.0 N1 0.0 1.0 0.0 N2 0.0 0.0 1.0 \n 0\n '
1575
+ """
1576
+ return fr"""
1577
+ STRI V0 {tostr(self._a)} V1 {tostr(self._b)} V2 {tostr(self._c)}
1578
+ N0 {tostr(self._da)} N1 {tostr(self._db)} N2 {tostr(self._dc)}
1579
+ {self._color}
1580
+ """
1581
+
1582
+
1583
+ class TachyonTriangleFactory(TriangleFactory):
1584
+ r"""
1585
+ A class to produce triangles of various rendering types.
1586
+ """
1587
+ def __init__(self, tach, tex):
1588
+ r"""
1589
+ Initialize with tachyon instance and texture.
1590
+
1591
+ EXAMPLES::
1592
+
1593
+ sage: from sage.plot.plot3d.tachyon import TachyonTriangleFactory
1594
+ sage: t = Tachyon()
1595
+ sage: t.texture('s')
1596
+ sage: ttf = TachyonTriangleFactory(t, 's')
1597
+ sage: ttf._texture
1598
+ 's'
1599
+ """
1600
+ self._tachyon = tach
1601
+ self._texture = tex
1602
+
1603
+ def triangle(self, a, b, c, color=None):
1604
+ r"""
1605
+ Create a TachyonTriangle with vertices a, b, and c.
1606
+
1607
+ EXAMPLES::
1608
+
1609
+ sage: from sage.plot.plot3d.tachyon import TachyonTriangleFactory
1610
+ sage: t = Tachyon()
1611
+ sage: t.texture('s')
1612
+ sage: ttf = TachyonTriangleFactory(t, 's')
1613
+ sage: ttft = ttf.triangle([1,2,3],[3,2,1],[0,2,1])
1614
+ sage: ttft.str()
1615
+ '\n TRI V0 1.0 2.0 3.0 V1 3.0 2.0 1.0 V2 0.0 2.0 1.0 \n s\n '
1616
+ """
1617
+ if color is None:
1618
+ return TachyonTriangle(a, b, c, self._texture)
1619
+ else:
1620
+ return TachyonTriangle(a, b, c, color)
1621
+
1622
+ def smooth_triangle(self, a, b, c, da, db, dc, color=None):
1623
+ r"""
1624
+ Create a TachyonSmoothTriangle.
1625
+
1626
+ EXAMPLES::
1627
+
1628
+ sage: from sage.plot.plot3d.tachyon import TachyonTriangleFactory
1629
+ sage: t = Tachyon()
1630
+ sage: t.texture('s')
1631
+ sage: ttf = TachyonTriangleFactory(t, 's')
1632
+ sage: ttfst = ttf.smooth_triangle([0,0,0],[1,0,0],[0,0,1],[1,1,1],[1,2,3],[-1,-1,2])
1633
+ sage: ttfst.str()
1634
+ '\n STRI V0 0.0 0.0 0.0 ...'
1635
+ """
1636
+ if color is None:
1637
+ return TachyonSmoothTriangle(a, b, c, da, db, dc, self._texture)
1638
+ else:
1639
+ return TachyonSmoothTriangle(a, b, c, da, db, dc, color)
1640
+
1641
+ def get_colors(self, list):
1642
+ r"""
1643
+ Return a list of color labels.
1644
+
1645
+ EXAMPLES::
1646
+
1647
+ sage: from sage.plot.plot3d.tachyon import TachyonTriangleFactory
1648
+ sage: t = Tachyon()
1649
+ sage: t.texture('s')
1650
+ sage: ttf = TachyonTriangleFactory(t, 's')
1651
+ sage: ttf.get_colors([(1,1,1)])
1652
+ ['SAGETEX1_0']
1653
+ """
1654
+ return self._tachyon.texture_recolor(self._texture, list)
1655
+
1656
+
1657
+ class ParametricPlot:
1658
+ r"""
1659
+ Parametric plotting routines.
1660
+ """
1661
+ def str(self):
1662
+ r"""
1663
+ Return the tachyon string representation of the parameterized curve.
1664
+
1665
+ EXAMPLES::
1666
+
1667
+ sage: from sage.plot.plot3d.tachyon import ParametricPlot
1668
+ sage: f = lambda t: (t,t^2,t^3)
1669
+ sage: q = ParametricPlot(f,0,1,'s')
1670
+ sage: q.str()[9:69]
1671
+ 'sphere center 0.0 0.0 0.0 rad 0.1 s\n \n fcyli'
1672
+ """
1673
+ return "".join(o.str() for o in self._objects)
1674
+
1675
+ def __init__(self, f, t_0, t_f, tex, r=.1, cylinders=True,
1676
+ min_depth=4, max_depth=8, e_rel=.01, e_abs=.01):
1677
+ r"""
1678
+ Create the parametric plotting class.
1679
+
1680
+ EXAMPLES::
1681
+
1682
+ sage: from sage.plot.plot3d.tachyon import ParametricPlot
1683
+ sage: f = lambda t: (t,t^2,t^3)
1684
+ sage: q = ParametricPlot(f,0,1,'s')
1685
+ sage: q._e_rel
1686
+ 0.01
1687
+ """
1688
+ self._e_rel = e_rel
1689
+ self._e_abs = e_abs
1690
+ self._r = r
1691
+ self._f = f
1692
+ self._tex = tex
1693
+ self._cylinders = cylinders
1694
+ self._max_depth = max_depth
1695
+ self._min_depth = min_depth
1696
+
1697
+ f_0 = f(t_0)
1698
+ f_f = f(t_f)
1699
+ self._objects = [Sphere(f_0, r, texture=tex)]
1700
+
1701
+ self._plot_step(0, t_0, t_f, f_0, f_f)
1702
+
1703
+ def _plot_step(self, depth, t_0, t_f, f_0, f_f):
1704
+ r"""
1705
+ Recursively subdivide interval, eventually plotting with cylinders and spheres.
1706
+
1707
+ EXAMPLES::
1708
+
1709
+ sage: from sage.plot.plot3d.tachyon import ParametricPlot
1710
+ sage: f = lambda t: (t,t^2,t^3)
1711
+ sage: q = ParametricPlot(f,0,1,'s')
1712
+ sage: q._plot_step(8,0,1,[0,0,0],[1,1,1])
1713
+ sage: len(q._objects)
1714
+ 515
1715
+ """
1716
+ if depth < self._max_depth:
1717
+ t_mid = (t_f + t_0) / 2
1718
+ f_mid = ((f_f[0] + f_0[0]) / 2, (f_f[1] + f_0[1]) / 2, (f_f[2] + f_0[2]) / 2)
1719
+ f_val = self._f(t_mid)
1720
+ if depth < self._min_depth or self.tol(f_mid, f_val):
1721
+ new_depth = depth + 1
1722
+ else:
1723
+ new_depth = self._max_depth
1724
+
1725
+ self._plot_step(new_depth, t_0, t_mid, f_0, f_val)
1726
+ self._plot_step(new_depth, t_mid, t_f, f_val, f_f)
1727
+ else:
1728
+ if self._cylinders:
1729
+ self._objects.append(FCylinder(f_0, f_f, self._r, self._tex))
1730
+ self._objects.append(Sphere(f_f, self._r, self._tex))
1731
+
1732
+ def tol(self, est, val):
1733
+ r"""
1734
+ Check relative, then absolute tolerance.
1735
+
1736
+ If both fail, return ``False``.
1737
+
1738
+ This is a zero-safe error checker.
1739
+
1740
+ EXAMPLES::
1741
+
1742
+ sage: from sage.plot.plot3d.tachyon import ParametricPlot
1743
+ sage: f = lambda t: (t,t^2,t^3)
1744
+ sage: q = ParametricPlot(f,0,1,'s')
1745
+ sage: q.tol([0,0,0],[1,0,0])
1746
+ False
1747
+ sage: q.tol([0,0,0],[.0001,0,0])
1748
+ True
1749
+ """
1750
+ a, b, c = val
1751
+ delta = sqrt((a - est[0])**2 + (b - est[1])**2 + (c - est[2])**2)
1752
+ if delta < self._e_abs:
1753
+ return True
1754
+
1755
+ r = sqrt(a**2 + b**2 + c**2)
1756
+ if delta < self._e_rel * r:
1757
+ return True
1758
+
1759
+ return False
1760
+
1761
+
1762
+ def tostr(s, length=3, out_type=float):
1763
+ r"""
1764
+ Convert vector information to a space-separated string.
1765
+
1766
+ EXAMPLES::
1767
+
1768
+ sage: from sage.plot.plot3d.tachyon import tostr
1769
+ sage: tostr((1,1,1))
1770
+ ' 1.0 1.0 1.0 '
1771
+ sage: tostr('2 3 2')
1772
+ '2 3 2'
1773
+ """
1774
+ if isinstance(s, str):
1775
+ return s
1776
+ output = ' '
1777
+ for an_item in s:
1778
+ output = output + str(out_type(an_item)) + ' '
1779
+ return output