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.
- passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
- passagemath_plot-10.6.31rc3.dist-info/RECORD +81 -0
- passagemath_plot-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_plot-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_plot.libs/libgfortran-e1b7dfc8.so.5.0.0 +0 -0
- passagemath_plot.libs/libgsl-e3525837.so.28.0.0 +0 -0
- passagemath_plot.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
- sage/all__sagemath_plot.py +15 -0
- sage/ext_data/threejs/animation.css +195 -0
- sage/ext_data/threejs/animation.html +85 -0
- sage/ext_data/threejs/animation.js +273 -0
- sage/ext_data/threejs/fat_lines.js +48 -0
- sage/ext_data/threejs/threejs-version.txt +1 -0
- sage/ext_data/threejs/threejs_template.html +597 -0
- sage/interfaces/all__sagemath_plot.py +1 -0
- sage/interfaces/gnuplot.py +196 -0
- sage/interfaces/jmoldata.py +208 -0
- sage/interfaces/povray.py +56 -0
- sage/plot/all.py +42 -0
- sage/plot/animate.py +1796 -0
- sage/plot/arc.py +504 -0
- sage/plot/arrow.py +671 -0
- sage/plot/bar_chart.py +205 -0
- sage/plot/bezier_path.py +400 -0
- sage/plot/circle.py +435 -0
- sage/plot/colors.py +1606 -0
- sage/plot/complex_plot.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/complex_plot.pyx +1446 -0
- sage/plot/contour_plot.py +1792 -0
- sage/plot/density_plot.py +318 -0
- sage/plot/disk.py +373 -0
- sage/plot/ellipse.py +375 -0
- sage/plot/graphics.py +3580 -0
- sage/plot/histogram.py +354 -0
- sage/plot/hyperbolic_arc.py +404 -0
- sage/plot/hyperbolic_polygon.py +416 -0
- sage/plot/hyperbolic_regular_polygon.py +296 -0
- sage/plot/line.py +626 -0
- sage/plot/matrix_plot.py +629 -0
- sage/plot/misc.py +509 -0
- sage/plot/multigraphics.py +1294 -0
- sage/plot/plot.py +4183 -0
- sage/plot/plot3d/all.py +23 -0
- sage/plot/plot3d/base.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/base.pxd +12 -0
- sage/plot/plot3d/base.pyx +3378 -0
- sage/plot/plot3d/implicit_plot3d.py +659 -0
- sage/plot/plot3d/implicit_surface.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/implicit_surface.pyx +1453 -0
- sage/plot/plot3d/index_face_set.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/index_face_set.pxd +32 -0
- sage/plot/plot3d/index_face_set.pyx +1873 -0
- sage/plot/plot3d/introduction.py +131 -0
- sage/plot/plot3d/list_plot3d.py +649 -0
- sage/plot/plot3d/parametric_plot3d.py +1130 -0
- sage/plot/plot3d/parametric_surface.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/parametric_surface.pxd +12 -0
- sage/plot/plot3d/parametric_surface.pyx +893 -0
- sage/plot/plot3d/platonic.py +601 -0
- sage/plot/plot3d/plot3d.py +1442 -0
- sage/plot/plot3d/plot_field3d.py +162 -0
- sage/plot/plot3d/point_c.pxi +148 -0
- sage/plot/plot3d/revolution_plot3d.py +309 -0
- sage/plot/plot3d/shapes.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/shapes.pxd +22 -0
- sage/plot/plot3d/shapes.pyx +1382 -0
- sage/plot/plot3d/shapes2.py +1512 -0
- sage/plot/plot3d/tachyon.py +1779 -0
- sage/plot/plot3d/texture.py +453 -0
- sage/plot/plot3d/transform.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/transform.pxd +21 -0
- sage/plot/plot3d/transform.pyx +268 -0
- sage/plot/plot3d/tri_plot.py +589 -0
- sage/plot/plot_field.py +362 -0
- sage/plot/point.py +624 -0
- sage/plot/polygon.py +562 -0
- sage/plot/primitive.py +249 -0
- sage/plot/scatter_plot.py +199 -0
- sage/plot/step.py +85 -0
- sage/plot/streamline_plot.py +328 -0
- 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
|