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,893 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-plot
|
|
2
|
+
# sage.doctest: needs sage.symbolic
|
|
3
|
+
r"""
|
|
4
|
+
Parametric surface
|
|
5
|
+
|
|
6
|
+
Graphics 3D object for triangulating surfaces, and a base class for many other
|
|
7
|
+
objects that can be represented by a 2D parametrization.
|
|
8
|
+
|
|
9
|
+
It takes great care to turn degenerate quadrilaterals into triangles and
|
|
10
|
+
to propagate identified points to all attached polygons. This is not
|
|
11
|
+
so much to save space as it is to assist the raytracers/other rendering
|
|
12
|
+
systems to better understand the surface (and especially calculate correct
|
|
13
|
+
surface normals).
|
|
14
|
+
|
|
15
|
+
AUTHORS:
|
|
16
|
+
|
|
17
|
+
- Robert Bradshaw (2007-08-26): initial version
|
|
18
|
+
|
|
19
|
+
EXAMPLES::
|
|
20
|
+
|
|
21
|
+
sage: from sage.plot.plot3d.parametric_surface import ParametricSurface, MoebiusStrip
|
|
22
|
+
sage: def f(x, y): return x+y, sin(x)*sin(y), x*y
|
|
23
|
+
sage: P = ParametricSurface(f, (srange(0,10,0.1), srange(-5,5.0,0.1)))
|
|
24
|
+
sage: show(P)
|
|
25
|
+
|
|
26
|
+
.. PLOT::
|
|
27
|
+
|
|
28
|
+
from sage.plot.plot3d.parametric_surface import ParametricSurface
|
|
29
|
+
def f(x, y): return x+y, sin(x)*sin(y), x*y
|
|
30
|
+
sphinx_plot(ParametricSurface(f, (srange(0,10,0.1), srange(-5,5.0,0.1))))
|
|
31
|
+
|
|
32
|
+
::
|
|
33
|
+
|
|
34
|
+
sage: S = MoebiusStrip(1,.2)
|
|
35
|
+
sage: S.is_enclosed()
|
|
36
|
+
False
|
|
37
|
+
sage: show(S)
|
|
38
|
+
|
|
39
|
+
.. PLOT::
|
|
40
|
+
|
|
41
|
+
from sage.plot.plot3d.parametric_surface import MoebiusStrip
|
|
42
|
+
sphinx_plot(MoebiusStrip(1,.2))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
By default, the surface is colored with one single color. ::
|
|
46
|
+
|
|
47
|
+
sage: P = ParametricSurface(f, (srange(0,10,0.1), srange(-5,5.0,0.1)),
|
|
48
|
+
....: color='red')
|
|
49
|
+
sage: P.show()
|
|
50
|
+
|
|
51
|
+
.. PLOT::
|
|
52
|
+
|
|
53
|
+
from sage.plot.plot3d.parametric_surface import ParametricSurface
|
|
54
|
+
def f(x, y): return x+y, sin(x)*sin(y), x*y
|
|
55
|
+
sphinx_plot(ParametricSurface(f, (srange(0,10,0.1), srange(-5,5.0,0.1)), color='red'))
|
|
56
|
+
|
|
57
|
+
One can instead provide a coloring function and a colormap::
|
|
58
|
+
|
|
59
|
+
sage: def f(x, y): return x+y, x-y, x*y
|
|
60
|
+
sage: def c(x, y): return sin((x+y)/2)**2
|
|
61
|
+
sage: cm = colormaps.RdYlGn
|
|
62
|
+
sage: P = ParametricSurface(f, (srange(-5,5,0.1), srange(-5,5.0,0.1)), color=(c,cm))
|
|
63
|
+
sage: P.show(viewer='tachyon')
|
|
64
|
+
|
|
65
|
+
.. PLOT::
|
|
66
|
+
|
|
67
|
+
from sage.plot.plot3d.parametric_surface import ParametricSurface
|
|
68
|
+
def f(x, y): return x+y, x-y, x*y
|
|
69
|
+
def c(x, y): return sin((x+y)/2)**2
|
|
70
|
+
cm = colormaps.RdYlGn
|
|
71
|
+
sphinx_plot(ParametricSurface(f, (srange(-5,5,0.1), srange(-5,5.0,0.1)), color=(c,cm)))
|
|
72
|
+
|
|
73
|
+
Note that the coloring function should rather have values between 0 and 1.
|
|
74
|
+
This value is passed to the chosen colormap.
|
|
75
|
+
|
|
76
|
+
Another colored example::
|
|
77
|
+
|
|
78
|
+
sage: colm = colormaps.autumn
|
|
79
|
+
sage: def g(x, y): return x, y, x**2 + y**2
|
|
80
|
+
sage: P = ParametricSurface(g, (srange(-10,10,0.1), srange(-5,5.0,0.1)), color=(c,colm))
|
|
81
|
+
sage: P.show(viewer='tachyon')
|
|
82
|
+
|
|
83
|
+
.. PLOT::
|
|
84
|
+
|
|
85
|
+
from sage.plot.plot3d.parametric_surface import ParametricSurface
|
|
86
|
+
colm = colormaps.autumn
|
|
87
|
+
def g(x, y): return x, y, x**2 + y**2
|
|
88
|
+
def c(x, y): return sin((x+y)/2)**2
|
|
89
|
+
sphinx_plot(ParametricSurface(g, (srange(-10,10,0.1), srange(-5,5.0,0.1)), color=(c,colm)))
|
|
90
|
+
|
|
91
|
+
.. NOTE::
|
|
92
|
+
|
|
93
|
+
One may override ``eval()`` or ``eval_c()`` in a subclass
|
|
94
|
+
rather than passing in a function for greater speed.
|
|
95
|
+
One also would want to override get_grid.
|
|
96
|
+
|
|
97
|
+
.. TODO::
|
|
98
|
+
|
|
99
|
+
actually remove unused points, fix the below code::
|
|
100
|
+
|
|
101
|
+
S = ParametricSurface(f=lambda xy: (xy[0],xy[1],0), domain=(range(10),range(10)))
|
|
102
|
+
"""
|
|
103
|
+
# ****************************************************************************
|
|
104
|
+
# Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu>
|
|
105
|
+
#
|
|
106
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
107
|
+
#
|
|
108
|
+
# This code is distributed in the hope that it will be useful,
|
|
109
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
110
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
111
|
+
# General Public License for more details.
|
|
112
|
+
#
|
|
113
|
+
# The full text of the GPL is available at:
|
|
114
|
+
#
|
|
115
|
+
# https://www.gnu.org/licenses/
|
|
116
|
+
# ****************************************************************************
|
|
117
|
+
|
|
118
|
+
from cysignals.memory cimport sig_malloc, sig_free
|
|
119
|
+
from cysignals.signals cimport sig_check
|
|
120
|
+
|
|
121
|
+
from math import cos, sin
|
|
122
|
+
from sage.rings.real_double import RDF
|
|
123
|
+
|
|
124
|
+
from sage.plot.colors import check_color_data
|
|
125
|
+
from sage.plot.plot3d.base import RenderParams
|
|
126
|
+
from sage.plot.plot3d.transform cimport point_c, face_c
|
|
127
|
+
from sage.ext.interpreters.wrapper_rdf cimport Wrapper_rdf
|
|
128
|
+
|
|
129
|
+
include "point_c.pxi"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
cdef inline bint smash_edge(point_c* vs, face_c* f, int a, int b) noexcept:
|
|
133
|
+
if point_c_eq(vs[f.vertices[a]], vs[f.vertices[b]]):
|
|
134
|
+
f.vertices[b] = f.vertices[a]
|
|
135
|
+
f.n -= 1
|
|
136
|
+
return 1
|
|
137
|
+
else:
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
cdef class ParametricSurface(IndexFaceSet):
|
|
142
|
+
"""
|
|
143
|
+
Base class that initializes the ParametricSurface
|
|
144
|
+
graphics type. This sets options, the function to be plotted, and the
|
|
145
|
+
plotting array as attributes.
|
|
146
|
+
|
|
147
|
+
INPUT:
|
|
148
|
+
|
|
149
|
+
- ``f`` -- (default: ``None``) the defining function. Either a tuple of
|
|
150
|
+
three functions, or a single function which returns a tuple, taking
|
|
151
|
+
two python floats as input. To subclass, pass ``None`` for ``f`` and
|
|
152
|
+
override ``eval_c`` or ``eval`` instead.
|
|
153
|
+
|
|
154
|
+
- ``domain`` -- (default: ``None``) a tuple of two lists, defining the
|
|
155
|
+
grid of `u,v` values. If ``None``, this will be calculated automatically.
|
|
156
|
+
|
|
157
|
+
- ``color`` -- (default: ``None``) a pair `(h,c)` where `h` is
|
|
158
|
+
a function with values in `[0,1]` and `c` is a colormap. The
|
|
159
|
+
color of a point `p` is then defined as the composition
|
|
160
|
+
`c(h(p))`
|
|
161
|
+
|
|
162
|
+
EXAMPLES::
|
|
163
|
+
|
|
164
|
+
sage: from sage.plot.plot3d.parametric_surface import ParametricSurface
|
|
165
|
+
sage: def f(x, y): return cos(x)*sin(y), sin(x)*sin(y), cos(y)+log(tan(y/2))+0.2*x
|
|
166
|
+
sage: S = ParametricSurface(f, (srange(0,12.4,0.1), srange(0.1,2,0.1)))
|
|
167
|
+
sage: show(S)
|
|
168
|
+
|
|
169
|
+
sage: len(S.face_list())
|
|
170
|
+
2214
|
|
171
|
+
|
|
172
|
+
.. PLOT::
|
|
173
|
+
|
|
174
|
+
from sage.plot.plot3d.parametric_surface import ParametricSurface
|
|
175
|
+
def f(x, y): return cos(x)*sin(y), sin(x)*sin(y), cos(y)+log(tan(y/2))+0.2*x
|
|
176
|
+
sphinx_plot(ParametricSurface(f, (srange(0,12.4,0.1), srange(0.1,2,0.1))))
|
|
177
|
+
|
|
178
|
+
The Hessenberg surface:
|
|
179
|
+
|
|
180
|
+
::
|
|
181
|
+
|
|
182
|
+
sage: def f(u, v):
|
|
183
|
+
....: a = 1
|
|
184
|
+
....: from math import cos, sin, sinh, cosh
|
|
185
|
+
....: x = cos(a)*(cos(u)*sinh(v)-cos(3*u)*sinh(3*v)/3) + sin(a)*(
|
|
186
|
+
....: sin(u)*cosh(v)-sin(3*u)*cosh(3*v)/3)
|
|
187
|
+
....: y = cos(a)*(sin(u)*sinh(v)+sin(3*u)*sinh(3*v)/3) + sin(a)*(
|
|
188
|
+
....: -cos(u)*cosh(v)-cos(3*u)*cosh(3*v)/3)
|
|
189
|
+
....: z = cos(a)*cos(2*u)*cosh(2*v)+sin(a)*sin(2*u)*sinh(2*v)
|
|
190
|
+
....: return (x,y,z)
|
|
191
|
+
sage: v = srange(float(0),float((3/2)*pi),float(0.1))
|
|
192
|
+
sage: S = ParametricSurface(f, (srange(float(0),float(pi),float(0.1)),
|
|
193
|
+
....: srange(float(-1),float(1),float(0.1))), color='blue')
|
|
194
|
+
sage: show(S)
|
|
195
|
+
|
|
196
|
+
.. PLOT::
|
|
197
|
+
|
|
198
|
+
def f(u, v):
|
|
199
|
+
a = 1
|
|
200
|
+
from math import cos, sin, sinh, cosh
|
|
201
|
+
x = cos(a)*(cos(u)*sinh(v)-cos(3*u)*sinh(3*v)/3) + sin(a)*(sin(u)*cosh(v)-sin(3*u)*cosh(3*v)/3)
|
|
202
|
+
y = cos(a)*(sin(u)*sinh(v)+sin(3*u)*sinh(3*v)/3) + sin(a)*(-cos(u)*cosh(v)-cos(3*u)*cosh(3*v)/3)
|
|
203
|
+
z = cos(a)*cos(2*u)*cosh(2*v)+sin(a)*sin(2*u)*sinh(2*v)
|
|
204
|
+
return (x,y,z)
|
|
205
|
+
from sage.plot.plot3d.parametric_surface import ParametricSurface
|
|
206
|
+
v = srange(float(0),float((3/2)*pi),float(0.1))
|
|
207
|
+
sphinx_plot(ParametricSurface(f, (srange(float(0),float(pi),float(0.1)),srange(float(-1),float(1),float(0.1))), color='blue'))
|
|
208
|
+
|
|
209
|
+
A colored example using the ``color`` keyword::
|
|
210
|
+
|
|
211
|
+
sage: def g(x, y): return x, y, - x**2 + y**2
|
|
212
|
+
sage: def c(x, y): return sin((x-y/2)*y/4)**2
|
|
213
|
+
sage: cm = colormaps.gist_rainbow
|
|
214
|
+
sage: P = ParametricSurface(g, (srange(-10,10,0.1),
|
|
215
|
+
....: srange(-5,5.0,0.1)),color=(c,cm))
|
|
216
|
+
sage: P.show(viewer='tachyon')
|
|
217
|
+
|
|
218
|
+
.. PLOT::
|
|
219
|
+
|
|
220
|
+
from sage.plot.plot3d.parametric_surface import ParametricSurface
|
|
221
|
+
def g(x, y): return x, y, - x**2 + y**2
|
|
222
|
+
def c(x, y): return sin((x-y/2)*y/4)**2
|
|
223
|
+
cm = colormaps.gist_rainbow
|
|
224
|
+
sphinx_plot(ParametricSurface(g, (srange(-10,10,0.1), srange(-5,5.0,0.1)),color=(c,cm)))
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
def __init__(self, f=None, domain=None, **kwds):
|
|
228
|
+
"""
|
|
229
|
+
Create the graphics primitive :class:`ParametricSurface`. See the
|
|
230
|
+
docstring of this class for full documentation.
|
|
231
|
+
|
|
232
|
+
EXAMPLES::
|
|
233
|
+
|
|
234
|
+
sage: from sage.plot.plot3d.parametric_surface import ParametricSurface
|
|
235
|
+
sage: def f(x, y): return x+y, sin(x)*sin(y), x*y
|
|
236
|
+
sage: S = ParametricSurface(f, (srange(0,12.4,0.1), srange(0.1,2,0.1)))
|
|
237
|
+
"""
|
|
238
|
+
if isinstance(f, list):
|
|
239
|
+
f = tuple(f)
|
|
240
|
+
self.f = f
|
|
241
|
+
self.render_grid = domain
|
|
242
|
+
self._extra_kwds = kwds
|
|
243
|
+
color_data = None
|
|
244
|
+
if 'color' in kwds:
|
|
245
|
+
try:
|
|
246
|
+
if len(kwds['color']) == 2 and callable(kwds['color'][0]):
|
|
247
|
+
color_data = kwds['color']
|
|
248
|
+
kwds.pop('color')
|
|
249
|
+
except (TypeError, AttributeError):
|
|
250
|
+
pass
|
|
251
|
+
if color_data is None:
|
|
252
|
+
# case of a global color
|
|
253
|
+
self.color_function = None
|
|
254
|
+
IndexFaceSet.__init__(self, [], [], **kwds)
|
|
255
|
+
else:
|
|
256
|
+
# case of a color depending on parameters
|
|
257
|
+
cf, cm = check_color_data(color_data)
|
|
258
|
+
self.color_function = cf
|
|
259
|
+
self.colormap = cm
|
|
260
|
+
IndexFaceSet.__init__(self, [], [], texture_list=[], **kwds)
|
|
261
|
+
|
|
262
|
+
def default_render_params(self):
|
|
263
|
+
"""
|
|
264
|
+
Return an instance of RenderParams suitable for plotting this object.
|
|
265
|
+
|
|
266
|
+
TESTS::
|
|
267
|
+
|
|
268
|
+
sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip
|
|
269
|
+
sage: type(MoebiusStrip(3,3).default_render_params())
|
|
270
|
+
<class 'sage.plot.plot3d.base.RenderParams'>
|
|
271
|
+
"""
|
|
272
|
+
return RenderParams(ds=.075, crease_threshold=.35)
|
|
273
|
+
|
|
274
|
+
def x3d_geometry(self):
|
|
275
|
+
r"""
|
|
276
|
+
Return XML-like representation of the coordinates of all points
|
|
277
|
+
in a triangulation of the object along with an indexing of those
|
|
278
|
+
points.
|
|
279
|
+
|
|
280
|
+
TESTS::
|
|
281
|
+
|
|
282
|
+
sage: _ = var('x,y')
|
|
283
|
+
sage: P = plot3d(x^2-y^2, (x, -2, 2), (y, -2, 2))
|
|
284
|
+
sage: s = P.x3d_str() # indirect doctest
|
|
285
|
+
sage: s[:100]
|
|
286
|
+
"<Shape>\n<IndexedFaceSet coordIndex='0,1,..."
|
|
287
|
+
"""
|
|
288
|
+
self.triangulate(self.default_render_params())
|
|
289
|
+
return IndexFaceSet.x3d_geometry(self)
|
|
290
|
+
|
|
291
|
+
def tachyon_repr(self, render_params):
|
|
292
|
+
"""
|
|
293
|
+
Return representation of the object suitable for plotting
|
|
294
|
+
using Tachyon ray tracer.
|
|
295
|
+
|
|
296
|
+
TESTS::
|
|
297
|
+
|
|
298
|
+
sage: _ = var('x,y')
|
|
299
|
+
sage: P = plot3d(x^2-y^2, (x, -2, 2), (y, -2, 2))
|
|
300
|
+
sage: s = P.tachyon_repr(P.default_render_params())
|
|
301
|
+
sage: s[:2]
|
|
302
|
+
['TRI V0 -2 -2 0 V1 -2 -1.89744 0.399737 V2 -1.89744 -1.89744 0', 'texture...']
|
|
303
|
+
"""
|
|
304
|
+
self.triangulate(render_params)
|
|
305
|
+
return IndexFaceSet.tachyon_repr(self, render_params)
|
|
306
|
+
|
|
307
|
+
def obj_repr(self, render_params):
|
|
308
|
+
"""
|
|
309
|
+
Return a complete representation of object with name, texture, and
|
|
310
|
+
lists of vertices, faces, and back-faces.
|
|
311
|
+
|
|
312
|
+
TESTS::
|
|
313
|
+
|
|
314
|
+
sage: _ = var('x,y')
|
|
315
|
+
sage: P = plot3d(x^2-y^2, (x, -2, 2), (y, -2, 2))
|
|
316
|
+
sage: s = P.obj_repr(P.default_render_params())
|
|
317
|
+
sage: s[:2]+s[2][:3]+s[3][:3]
|
|
318
|
+
['g obj_1',
|
|
319
|
+
'usemtl texture...',
|
|
320
|
+
'v -2 -2 0',
|
|
321
|
+
'v -2 -1.89744 0.399737',
|
|
322
|
+
'v -1.89744 -1.89744 0',
|
|
323
|
+
'f 1 2 3 4',
|
|
324
|
+
'f 2 5 6 3',
|
|
325
|
+
'f 5 7 8 6']
|
|
326
|
+
"""
|
|
327
|
+
self.triangulate(render_params)
|
|
328
|
+
return IndexFaceSet.obj_repr(self, render_params)
|
|
329
|
+
|
|
330
|
+
def jmol_repr(self, render_params):
|
|
331
|
+
r"""
|
|
332
|
+
Return a representation of the object suitable for plotting
|
|
333
|
+
using Jmol.
|
|
334
|
+
|
|
335
|
+
TESTS::
|
|
336
|
+
|
|
337
|
+
sage: _ = var('x,y')
|
|
338
|
+
sage: P = plot3d(x^2-y^2, (x, -2, 2), (y, -2, 2))
|
|
339
|
+
sage: s = P.jmol_repr(P.testing_render_params())
|
|
340
|
+
sage: s[:10]
|
|
341
|
+
['pmesh obj_1 "obj_1.pmesh"\ncolor pmesh [102,102,255]']
|
|
342
|
+
"""
|
|
343
|
+
self.triangulate(render_params)
|
|
344
|
+
return IndexFaceSet.jmol_repr(self, render_params)
|
|
345
|
+
|
|
346
|
+
def json_repr(self, render_params):
|
|
347
|
+
"""
|
|
348
|
+
Return a representation of the object in JSON format as
|
|
349
|
+
a list with one element, which is a string of a dictionary
|
|
350
|
+
listing vertices, faces and colors.
|
|
351
|
+
|
|
352
|
+
TESTS::
|
|
353
|
+
|
|
354
|
+
sage: _ = var('x,y')
|
|
355
|
+
sage: P = plot3d(x^2-y^2, (x, -2, 2), (y, -2, 2))
|
|
356
|
+
sage: s = P.json_repr(P.default_render_params())
|
|
357
|
+
sage: print(s[0][:100])
|
|
358
|
+
{"vertices":[{"x":-2,"y":-2,"z":0},{"x":-2,"y":-1.89744,"z":0.399737},{"x":-1.89744,"y":-1.89744,"z"
|
|
359
|
+
|
|
360
|
+
One test for :issue:`22688`::
|
|
361
|
+
|
|
362
|
+
sage: P = spherical_plot3d(sqrt(x-pi/2),(x,0,pi),(y,0,2*pi))
|
|
363
|
+
sage: s = P.json_repr(P.default_render_params())
|
|
364
|
+
sage: 'nan' in s or 'NaN' in s
|
|
365
|
+
False
|
|
366
|
+
"""
|
|
367
|
+
self.triangulate(render_params)
|
|
368
|
+
return IndexFaceSet.json_repr(self, render_params)
|
|
369
|
+
|
|
370
|
+
def threejs_repr(self, render_params):
|
|
371
|
+
r"""
|
|
372
|
+
Return a representation of the surface suitable for plotting with three.js.
|
|
373
|
+
|
|
374
|
+
EXAMPLES::
|
|
375
|
+
|
|
376
|
+
sage: _ = var('x,y')
|
|
377
|
+
sage: P = plot3d(x^2-y^2, (x, -2, 2), (y, -2, 2))
|
|
378
|
+
sage: P.threejs_repr(P.default_render_params())
|
|
379
|
+
[('surface',
|
|
380
|
+
{'color': '#6666ff',
|
|
381
|
+
'faces': [[0, 1, 2, 3],
|
|
382
|
+
...
|
|
383
|
+
'opacity': 1.0,
|
|
384
|
+
'vertices': [{'x': -2.0, 'y': -2.0, 'z': 0.0},
|
|
385
|
+
...
|
|
386
|
+
{'x': 2.0, 'y': 2.0, 'z': 0.0}]})]
|
|
387
|
+
"""
|
|
388
|
+
self.triangulate(render_params)
|
|
389
|
+
return IndexFaceSet.threejs_repr(self, render_params)
|
|
390
|
+
|
|
391
|
+
def is_enclosed(self):
|
|
392
|
+
"""
|
|
393
|
+
Return a boolean telling whether or not it is necessary to
|
|
394
|
+
render the back sides of the polygons (assuming, of course,
|
|
395
|
+
that they have the correct orientation).
|
|
396
|
+
|
|
397
|
+
This is calculated in by verifying the opposite edges
|
|
398
|
+
of the rendered domain either line up or are pinched together.
|
|
399
|
+
|
|
400
|
+
EXAMPLES::
|
|
401
|
+
|
|
402
|
+
sage: from sage.plot.plot3d.shapes import Sphere
|
|
403
|
+
sage: Sphere(1).is_enclosed()
|
|
404
|
+
True
|
|
405
|
+
|
|
406
|
+
sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip
|
|
407
|
+
sage: MoebiusStrip(1,0.2).is_enclosed()
|
|
408
|
+
False
|
|
409
|
+
"""
|
|
410
|
+
if self.fcount == 0:
|
|
411
|
+
self.triangulate()
|
|
412
|
+
return self.enclosed
|
|
413
|
+
|
|
414
|
+
def dual(self):
|
|
415
|
+
"""
|
|
416
|
+
Return an ``IndexFaceSet`` which is the dual of the
|
|
417
|
+
:class:`ParametricSurface` object as a triangulated surface.
|
|
418
|
+
|
|
419
|
+
EXAMPLES:
|
|
420
|
+
|
|
421
|
+
As one might expect, this gives an icosahedron::
|
|
422
|
+
|
|
423
|
+
sage: D = dodecahedron()
|
|
424
|
+
sage: D.dual()
|
|
425
|
+
Graphics3d Object
|
|
426
|
+
|
|
427
|
+
But any enclosed surface should work::
|
|
428
|
+
|
|
429
|
+
sage: from sage.plot.plot3d.shapes import Torus
|
|
430
|
+
sage: T = Torus(1, .2)
|
|
431
|
+
sage: T.dual()
|
|
432
|
+
Graphics3d Object
|
|
433
|
+
sage: T.is_enclosed()
|
|
434
|
+
True
|
|
435
|
+
|
|
436
|
+
Surfaces which are not enclosed, though, should raise an exception::
|
|
437
|
+
|
|
438
|
+
sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip
|
|
439
|
+
sage: M = MoebiusStrip(3,1)
|
|
440
|
+
sage: M.is_enclosed()
|
|
441
|
+
False
|
|
442
|
+
sage: M.dual()
|
|
443
|
+
Traceback (most recent call last):
|
|
444
|
+
...
|
|
445
|
+
NotImplementedError: this is only implemented for enclosed surfaces
|
|
446
|
+
"""
|
|
447
|
+
# This doesn't completely make sense...
|
|
448
|
+
if self.fcount == 0:
|
|
449
|
+
self.triangulate()
|
|
450
|
+
if not self.is_enclosed():
|
|
451
|
+
raise NotImplementedError("this is only implemented for enclosed surfaces")
|
|
452
|
+
return IndexFaceSet.dual(self)
|
|
453
|
+
|
|
454
|
+
def bounding_box(self):
|
|
455
|
+
"""
|
|
456
|
+
Return the lower and upper corners of a 3D bounding box for ``self``.
|
|
457
|
+
|
|
458
|
+
This is used for rendering and ``self`` should fit entirely within this
|
|
459
|
+
box.
|
|
460
|
+
|
|
461
|
+
Specifically, the first point returned should have x, y, and z
|
|
462
|
+
coordinates should be the respective infimum over all points in
|
|
463
|
+
``self``, and the second point is the supremum.
|
|
464
|
+
|
|
465
|
+
EXAMPLES::
|
|
466
|
+
|
|
467
|
+
sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip
|
|
468
|
+
sage: M = MoebiusStrip(7,3,2)
|
|
469
|
+
sage: M.bounding_box()
|
|
470
|
+
((-10.0, -7.53907349250478..., -2.9940801852848145),
|
|
471
|
+
(10.0, 7.53907349250478..., 2.9940801852848145))
|
|
472
|
+
"""
|
|
473
|
+
# We must triangulate before computing the bounding box; otherwise
|
|
474
|
+
# we'll get an empty bounding box, as the bounding box is computed
|
|
475
|
+
# using the triangulation, and before triangulating the triangulation
|
|
476
|
+
# is empty.
|
|
477
|
+
self.triangulate()
|
|
478
|
+
return IndexFaceSet.bounding_box(self)
|
|
479
|
+
|
|
480
|
+
def triangulate(self, render_params=None):
|
|
481
|
+
r"""
|
|
482
|
+
Call self.eval_grid() for all `(u,v)` in
|
|
483
|
+
`\text{urange} \times \text{vrange}` to construct this surface.
|
|
484
|
+
|
|
485
|
+
The most complicated part of this code is identifying shared
|
|
486
|
+
vertices and shrinking trivial edges. This is not done so much
|
|
487
|
+
to save memory, rather it is needed so normals of the triangles
|
|
488
|
+
can be calculated correctly.
|
|
489
|
+
|
|
490
|
+
TESTS::
|
|
491
|
+
|
|
492
|
+
sage: from sage.plot.plot3d.parametric_surface import ParametricSurface, MoebiusStrip
|
|
493
|
+
sage: def f(x, y): return x+y, sin(x)*sin(y), x*y # indirect doctests
|
|
494
|
+
sage: P = ParametricSurface(f, (srange(0,10,0.1), srange(-5,5.0,0.1))) # indirect doctests
|
|
495
|
+
sage: P.show() # indirect doctests
|
|
496
|
+
sage: S = MoebiusStrip(1, .2) # indirect doctests
|
|
497
|
+
sage: S.show() # indirect doctests
|
|
498
|
+
"""
|
|
499
|
+
cdef double u, v
|
|
500
|
+
if render_params is None:
|
|
501
|
+
render_params = self.default_render_params()
|
|
502
|
+
ds = render_params.ds
|
|
503
|
+
if render_params.transform is not None:
|
|
504
|
+
ds /= render_params.transform.max_scale()
|
|
505
|
+
urange, vrange = self.get_grid(ds)
|
|
506
|
+
urange = [float(u) for u in urange]
|
|
507
|
+
vrange = [float(v) for v in vrange]
|
|
508
|
+
if self.render_grid == (urange, vrange) and self.fcount:
|
|
509
|
+
# Already triangulated at on this grid.
|
|
510
|
+
return
|
|
511
|
+
|
|
512
|
+
cdef Py_ssize_t i, j
|
|
513
|
+
cdef Py_ssize_t n = len(urange) - 1
|
|
514
|
+
cdef Py_ssize_t m = len(vrange) - 1
|
|
515
|
+
cdef Py_ssize_t ix = 0
|
|
516
|
+
|
|
517
|
+
try:
|
|
518
|
+
self.realloc((m+1)*(n+1), m*n, 4*m*n)
|
|
519
|
+
self.eval_grid(urange, vrange)
|
|
520
|
+
except BaseException:
|
|
521
|
+
self.fcount = self.vcount = 0
|
|
522
|
+
self.render_grid = None
|
|
523
|
+
raise
|
|
524
|
+
|
|
525
|
+
# face_c.vertices:
|
|
526
|
+
#
|
|
527
|
+
# 0 - 1
|
|
528
|
+
# | |
|
|
529
|
+
# 3 - 2
|
|
530
|
+
|
|
531
|
+
cdef face_c *face
|
|
532
|
+
|
|
533
|
+
for i in range(n):
|
|
534
|
+
for j in range(m):
|
|
535
|
+
sig_check()
|
|
536
|
+
ix = i*m + j
|
|
537
|
+
face = &self._faces[ix]
|
|
538
|
+
face.n = 4
|
|
539
|
+
face.vertices = &self.face_indices[4*ix]
|
|
540
|
+
if self.color_function is not None:
|
|
541
|
+
face.color.r, face.color.g, face.color.b, _ = self.colormap(self.color_function(urange[i], vrange[j]))
|
|
542
|
+
|
|
543
|
+
# Connect to the i-1 row
|
|
544
|
+
if i == 0:
|
|
545
|
+
if j == 0:
|
|
546
|
+
face.vertices[0] = 0
|
|
547
|
+
else:
|
|
548
|
+
face.vertices[0] = self._faces[ix-1].vertices[1]
|
|
549
|
+
face.vertices[1] = j+1
|
|
550
|
+
smash_edge(self.vs, face, 0, 1)
|
|
551
|
+
else:
|
|
552
|
+
face.vertices[0] = self._faces[ix-m].vertices[3]
|
|
553
|
+
face.vertices[1] = self._faces[ix-m].vertices[2]
|
|
554
|
+
|
|
555
|
+
# Connect to the j-1 col
|
|
556
|
+
if j == 0:
|
|
557
|
+
face.vertices[3] = (i+1)*(m+1)
|
|
558
|
+
smash_edge(self.vs, face, 0, 3)
|
|
559
|
+
else:
|
|
560
|
+
face.vertices[3] = self._faces[ix-1].vertices[2]
|
|
561
|
+
|
|
562
|
+
# This is the newly-seen vertex, identify if it's a triangle
|
|
563
|
+
face.vertices[2] = (i+1)*(m+1)+j+1
|
|
564
|
+
smash_edge(self.vs, face, 1, 2)
|
|
565
|
+
smash_edge(self.vs, face, 3, 2)
|
|
566
|
+
|
|
567
|
+
# Now we see if it wraps around or is otherwise enclosed
|
|
568
|
+
self.enclosed = True
|
|
569
|
+
|
|
570
|
+
cdef face_c *first
|
|
571
|
+
cdef face_c *last
|
|
572
|
+
cdef point_c first_v0
|
|
573
|
+
cdef point_c first_v1
|
|
574
|
+
cdef point_c first_v3
|
|
575
|
+
cdef point_c last_v1
|
|
576
|
+
cdef point_c last_v2
|
|
577
|
+
cdef point_c last_v3
|
|
578
|
+
for j in range(m):
|
|
579
|
+
sig_check()
|
|
580
|
+
first = &self._faces[j]
|
|
581
|
+
first_v0 = self.vs[first.vertices[0]]
|
|
582
|
+
first_v1 = self.vs[first.vertices[1]]
|
|
583
|
+
if not (point_c_isfinite(first_v0) and point_c_isfinite(first_v1)):
|
|
584
|
+
continue
|
|
585
|
+
last = &self._faces[(n-1)*m+j]
|
|
586
|
+
last_v3 = self.vs[last.vertices[3]]
|
|
587
|
+
last_v2 = self.vs[last.vertices[2]]
|
|
588
|
+
if not (point_c_isfinite(last_v3) and point_c_isfinite(last_v2)):
|
|
589
|
+
continue
|
|
590
|
+
if point_c_eq(first_v0, last_v3):
|
|
591
|
+
last.vertices[3] = first.vertices[0]
|
|
592
|
+
elif first.vertices[0] != first.vertices[1] or last.vertices[3] != last.vertices[2]:
|
|
593
|
+
self.enclosed = False
|
|
594
|
+
if point_c_eq(first_v1, last_v2):
|
|
595
|
+
last.vertices[2] = first.vertices[1]
|
|
596
|
+
elif first.vertices[0] != first.vertices[1] or last.vertices[3] != last.vertices[2]:
|
|
597
|
+
self.enclosed = False
|
|
598
|
+
|
|
599
|
+
for i in range(n):
|
|
600
|
+
sig_check()
|
|
601
|
+
first = &self._faces[i*m]
|
|
602
|
+
first_v0 = self.vs[first.vertices[0]]
|
|
603
|
+
first_v3 = self.vs[first.vertices[3]]
|
|
604
|
+
if not (point_c_isfinite(first_v0) and point_c_isfinite(first_v3)):
|
|
605
|
+
continue
|
|
606
|
+
last = &self._faces[i*m + m-1]
|
|
607
|
+
last_v1 = self.vs[last.vertices[1]]
|
|
608
|
+
last_v2 = self.vs[last.vertices[2]]
|
|
609
|
+
if not (point_c_isfinite(last_v1) and point_c_isfinite(last_v2)):
|
|
610
|
+
continue
|
|
611
|
+
if point_c_eq(first_v0, last_v1):
|
|
612
|
+
last.vertices[1] = first.vertices[0]
|
|
613
|
+
elif first.vertices[0] != first.vertices[3] or last.vertices[1] != last.vertices[2]:
|
|
614
|
+
self.enclosed = False
|
|
615
|
+
if point_c_eq(first_v3, last_v2):
|
|
616
|
+
last.vertices[2] = first.vertices[3]
|
|
617
|
+
elif first.vertices[0] != first.vertices[3] or last.vertices[1] != last.vertices[2]:
|
|
618
|
+
self.enclosed = False
|
|
619
|
+
|
|
620
|
+
# make sure we deleted the correct point from the triangles
|
|
621
|
+
# so that the correct vertices are the first 3 ones
|
|
622
|
+
for ix in range(n * m):
|
|
623
|
+
sig_check()
|
|
624
|
+
face = &self._faces[ix]
|
|
625
|
+
if face.n == 3:
|
|
626
|
+
if face.vertices[3] == face.vertices[2] or face.vertices[3] == face.vertices[0]:
|
|
627
|
+
pass
|
|
628
|
+
else:
|
|
629
|
+
if face.vertices[0] == face.vertices[1]:
|
|
630
|
+
face.vertices[1] = face.vertices[2]
|
|
631
|
+
face.vertices[2] = face.vertices[3]
|
|
632
|
+
|
|
633
|
+
self._clean_point_list()
|
|
634
|
+
|
|
635
|
+
self.render_grid = urange, vrange
|
|
636
|
+
|
|
637
|
+
def get_grid(self, ds):
|
|
638
|
+
"""
|
|
639
|
+
TESTS::
|
|
640
|
+
|
|
641
|
+
sage: from sage.plot.plot3d.parametric_surface import ParametricSurface
|
|
642
|
+
sage: def f(x, y): return x+y,x-y,x*y
|
|
643
|
+
sage: P = ParametricSurface(f)
|
|
644
|
+
sage: P.get_grid(.1)
|
|
645
|
+
Traceback (most recent call last):
|
|
646
|
+
...
|
|
647
|
+
NotImplementedError: you must override the get_grid method
|
|
648
|
+
"""
|
|
649
|
+
if self.render_grid is None:
|
|
650
|
+
raise NotImplementedError("you must override the get_grid method")
|
|
651
|
+
return self.render_grid
|
|
652
|
+
|
|
653
|
+
cdef int eval_grid(self, urange, vrange) except -1:
|
|
654
|
+
r"""
|
|
655
|
+
This fills in the points ``self.vs`` for all
|
|
656
|
+
`u \in \text{urange}, v \in \text{vrange}`.
|
|
657
|
+
We assume enough memory has been allocated.
|
|
658
|
+
|
|
659
|
+
We branch outside the loops for efficiency. The options for self.f are:
|
|
660
|
+
|
|
661
|
+
- ``None`` -- call ``self.eval_c()`` or ``self.eval()``
|
|
662
|
+
(One of these is presumably overridden.)
|
|
663
|
+
- ``tuple`` -- split into fx, fy, fz and call each separately
|
|
664
|
+
- ``callable`` -- call f(u,v)
|
|
665
|
+
|
|
666
|
+
In addition, branches are taken for efficient calling of fast callables
|
|
667
|
+
"""
|
|
668
|
+
cdef Py_ssize_t i, j
|
|
669
|
+
cdef Py_ssize_t m = len(urange)
|
|
670
|
+
cdef Py_ssize_t n = len(vrange)
|
|
671
|
+
cdef double u, v
|
|
672
|
+
cdef double uv[2]
|
|
673
|
+
cdef point_c *res
|
|
674
|
+
cdef double* ulist = NULL
|
|
675
|
+
cdef double* vlist = NULL
|
|
676
|
+
cdef bint fast_x, fast_y, fast_z
|
|
677
|
+
|
|
678
|
+
if self.f is None:
|
|
679
|
+
ulist = to_double_array(urange)
|
|
680
|
+
vlist = to_double_array(vrange)
|
|
681
|
+
|
|
682
|
+
res = self.vs
|
|
683
|
+
for i in range(m):
|
|
684
|
+
u = ulist[i]
|
|
685
|
+
for j in range(n):
|
|
686
|
+
sig_check()
|
|
687
|
+
v = vlist[j]
|
|
688
|
+
self.eval_c(res, u, v)
|
|
689
|
+
res += 1
|
|
690
|
+
elif isinstance(self.f, tuple):
|
|
691
|
+
fx, fy, fz = self.f
|
|
692
|
+
|
|
693
|
+
# First, deal with the fast functions (if any)
|
|
694
|
+
fast_x = isinstance(fx, Wrapper_rdf)
|
|
695
|
+
fast_y = isinstance(fy, Wrapper_rdf)
|
|
696
|
+
fast_z = isinstance(fz, Wrapper_rdf)
|
|
697
|
+
if fast_x or fast_y or fast_z:
|
|
698
|
+
ulist = to_double_array(urange)
|
|
699
|
+
vlist = to_double_array(vrange)
|
|
700
|
+
|
|
701
|
+
res = self.vs
|
|
702
|
+
if fast_x: # must be Wrapper_rdf
|
|
703
|
+
for i in range(m):
|
|
704
|
+
uv[0] = ulist[i]
|
|
705
|
+
for j in range(n):
|
|
706
|
+
sig_check()
|
|
707
|
+
uv[1] = vlist[j]
|
|
708
|
+
(<Wrapper_rdf>fx).call_c(uv, &res.x)
|
|
709
|
+
res += 1
|
|
710
|
+
|
|
711
|
+
res = self.vs
|
|
712
|
+
if fast_y: # must be Wrapper_rdf
|
|
713
|
+
for i in range(m):
|
|
714
|
+
uv[0] = ulist[i]
|
|
715
|
+
for j in range(n):
|
|
716
|
+
sig_check()
|
|
717
|
+
uv[1] = vlist[j]
|
|
718
|
+
(<Wrapper_rdf>fy).call_c(uv, &res.y)
|
|
719
|
+
res += 1
|
|
720
|
+
|
|
721
|
+
res = self.vs
|
|
722
|
+
if fast_z: # must be Wrapper_rdf
|
|
723
|
+
for i in range(m):
|
|
724
|
+
uv[0] = ulist[i]
|
|
725
|
+
for j in range(n):
|
|
726
|
+
sig_check()
|
|
727
|
+
uv[1] = vlist[j]
|
|
728
|
+
(<Wrapper_rdf>fz).call_c(uv, &res.z)
|
|
729
|
+
res += 1
|
|
730
|
+
|
|
731
|
+
# Finally, deal with the slow functions (if any)
|
|
732
|
+
if (not fast_x) or (not fast_y) or (not fast_z):
|
|
733
|
+
res = self.vs
|
|
734
|
+
for uu in urange:
|
|
735
|
+
for vv in vrange:
|
|
736
|
+
sig_check()
|
|
737
|
+
if not fast_x:
|
|
738
|
+
res.x = fx(uu, vv)
|
|
739
|
+
if not fast_y:
|
|
740
|
+
res.y = fy(uu, vv)
|
|
741
|
+
if not fast_z:
|
|
742
|
+
res.z = fz(uu, vv)
|
|
743
|
+
res += 1
|
|
744
|
+
else:
|
|
745
|
+
res = self.vs
|
|
746
|
+
for uu in urange:
|
|
747
|
+
for vv in vrange:
|
|
748
|
+
sig_check()
|
|
749
|
+
res.x, res.y, res.z = self.f(uu, vv)
|
|
750
|
+
res += 1
|
|
751
|
+
|
|
752
|
+
sig_free(ulist)
|
|
753
|
+
sig_free(vlist)
|
|
754
|
+
|
|
755
|
+
# One of the following two methods should be overridden in
|
|
756
|
+
# derived classes.
|
|
757
|
+
|
|
758
|
+
cdef int eval_c(self, point_c *res, double u, double v) except -1:
|
|
759
|
+
# can't do a cpdef because of the point_c* argument
|
|
760
|
+
res.x, res.y, res.z = self.eval(u, v)
|
|
761
|
+
|
|
762
|
+
def eval(self, double u, double v):
|
|
763
|
+
"""
|
|
764
|
+
TESTS::
|
|
765
|
+
|
|
766
|
+
sage: from sage.plot.plot3d.parametric_surface import ParametricSurface
|
|
767
|
+
sage: def f(x, y): return x+y,x-y,x*y
|
|
768
|
+
sage: P = ParametricSurface(f,(srange(0,1,0.1),srange(0,1,0.1)))
|
|
769
|
+
sage: P.eval(0,0)
|
|
770
|
+
Traceback (most recent call last):
|
|
771
|
+
...
|
|
772
|
+
NotImplementedError
|
|
773
|
+
"""
|
|
774
|
+
raise NotImplementedError
|
|
775
|
+
|
|
776
|
+
def plot(self):
|
|
777
|
+
"""
|
|
778
|
+
Draw a 3D plot of this graphics object, which just returns this
|
|
779
|
+
object since this is already a 3D graphics object.
|
|
780
|
+
Needed to support PLOT in doctrings, see :issue:`17498`
|
|
781
|
+
|
|
782
|
+
EXAMPLES::
|
|
783
|
+
|
|
784
|
+
sage: S = parametric_plot3d( (sin, cos, lambda u: u/10), (0, 20))
|
|
785
|
+
sage: S.plot() is S
|
|
786
|
+
True
|
|
787
|
+
"""
|
|
788
|
+
return self
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
class MoebiusStrip(ParametricSurface):
|
|
792
|
+
"""
|
|
793
|
+
Base class for the :class:`MoebiusStrip` graphics type. This sets the
|
|
794
|
+
basic parameters of the object.
|
|
795
|
+
|
|
796
|
+
INPUT:
|
|
797
|
+
|
|
798
|
+
- ``r`` -- a number which can be coerced to a float, serving roughly
|
|
799
|
+
as the radius of the object
|
|
800
|
+
|
|
801
|
+
- ``width`` -- a number which can be coerced to a float, which gives the
|
|
802
|
+
width of the object
|
|
803
|
+
|
|
804
|
+
- ``twists`` -- (default: 1) an integer, giving the number of twists in the
|
|
805
|
+
object (where one twist is the 'traditional' Möbius strip)
|
|
806
|
+
|
|
807
|
+
EXAMPLES::
|
|
808
|
+
|
|
809
|
+
sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip
|
|
810
|
+
sage: M = MoebiusStrip(3,3)
|
|
811
|
+
sage: M.show()
|
|
812
|
+
|
|
813
|
+
.. PLOT::
|
|
814
|
+
|
|
815
|
+
from sage.plot.plot3d.parametric_surface import MoebiusStrip
|
|
816
|
+
sphinx_plot(MoebiusStrip(3,3))
|
|
817
|
+
"""
|
|
818
|
+
|
|
819
|
+
def __init__(self, r, width, twists=1, **kwds):
|
|
820
|
+
"""
|
|
821
|
+
Create the graphics primitive MoebiusStrip. See the docstring of
|
|
822
|
+
this class for full documentation.
|
|
823
|
+
|
|
824
|
+
EXAMPLES:
|
|
825
|
+
|
|
826
|
+
::
|
|
827
|
+
|
|
828
|
+
sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip
|
|
829
|
+
sage: M = MoebiusStrip(3,3); M # Same width and radius, roughly
|
|
830
|
+
Graphics3d Object
|
|
831
|
+
sage: N = MoebiusStrip(7,3,2); N # two twists, lots of open area in the middle
|
|
832
|
+
Graphics3d Object
|
|
833
|
+
sage: O = MoebiusStrip(5,1,plot_points=200,color='red'); O # keywords get passed to plot3d
|
|
834
|
+
Graphics3d Object
|
|
835
|
+
"""
|
|
836
|
+
ParametricSurface.__init__(self, **kwds)
|
|
837
|
+
self.r = float(r)
|
|
838
|
+
self.width = float(width)
|
|
839
|
+
self.twists = int(twists)
|
|
840
|
+
|
|
841
|
+
def get_grid(self, ds):
|
|
842
|
+
"""
|
|
843
|
+
Return appropriate `u` and `v` ranges for this MoebiusStrip instance.
|
|
844
|
+
|
|
845
|
+
This is intended for internal use in creating an actual plot.
|
|
846
|
+
|
|
847
|
+
INPUT:
|
|
848
|
+
|
|
849
|
+
- ``ds`` -- a number, typically coming from a RenderParams object,
|
|
850
|
+
which helps determine the increment for the `v` range for the
|
|
851
|
+
MoebiusStrip object
|
|
852
|
+
|
|
853
|
+
EXAMPLES::
|
|
854
|
+
|
|
855
|
+
sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip
|
|
856
|
+
sage: N = MoebiusStrip(7,3,2) # two twists
|
|
857
|
+
sage: N.get_grid(N.default_render_params().ds)
|
|
858
|
+
([-1, 1], [0.0, 0.12566370614359174, 0.25132741228718347, 0.37699111843077515, ...])
|
|
859
|
+
"""
|
|
860
|
+
twoPi = RDF.pi() * 2
|
|
861
|
+
# Previous code, which doesn't seem to use any of the parameters
|
|
862
|
+
# TODO: figure out how to use it properly.
|
|
863
|
+
# res = max(min(twoPi*(self.r+self.twists*self.width)/ds, 10), 6*self.twists, 50)
|
|
864
|
+
res = max(6 * self.twists, 50)
|
|
865
|
+
return [-1, 1], [twoPi * k / res for k in range(res + 1)]
|
|
866
|
+
|
|
867
|
+
def eval(self, u, v):
|
|
868
|
+
"""
|
|
869
|
+
Return a tuple for `x,y,z` coordinates for the given ``u`` and ``v``
|
|
870
|
+
for this MoebiusStrip instance.
|
|
871
|
+
|
|
872
|
+
EXAMPLES::
|
|
873
|
+
|
|
874
|
+
sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip
|
|
875
|
+
sage: N = MoebiusStrip(7,3,2) # two twists
|
|
876
|
+
sage: N.eval(-1,0)
|
|
877
|
+
(4.0, 0.0, -0.0)
|
|
878
|
+
"""
|
|
879
|
+
return ( (self.r + u*self.width*cos(self.twists*v/2)) * cos(v),
|
|
880
|
+
(self.r + u*self.width*cos(self.twists*v/2)) * sin(v),
|
|
881
|
+
u*self.width*sin(self.twists*v/2) )
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
cdef double* to_double_array(py_list) except NULL:
|
|
885
|
+
cdef double* c_list = <double *>sig_malloc(sizeof(double) * len(py_list))
|
|
886
|
+
if c_list == NULL:
|
|
887
|
+
raise MemoryError
|
|
888
|
+
cdef Py_ssize_t i = 0
|
|
889
|
+
cdef double a
|
|
890
|
+
for a in py_list:
|
|
891
|
+
c_list[i] = a
|
|
892
|
+
i += 1
|
|
893
|
+
return c_list
|