passagemath-plot 10.6.31rc3__cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

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

Potentially problematic release.


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

Files changed (82) hide show
  1. passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
  2. passagemath_plot-10.6.31rc3.dist-info/RECORD +82 -0
  3. passagemath_plot-10.6.31rc3.dist-info/WHEEL +6 -0
  4. passagemath_plot-10.6.31rc3.dist-info/top_level.txt +2 -0
  5. passagemath_plot.libs/libgfortran-83c28eba.so.5.0.0 +0 -0
  6. passagemath_plot.libs/libgsl-cda90e79.so.28.0.0 +0 -0
  7. passagemath_plot.libs/libopenblasp-r0-6dcb67f9.3.29.so +0 -0
  8. passagemath_plot.libs/libquadmath-2284e583.so.0.0.0 +0 -0
  9. sage/all__sagemath_plot.py +15 -0
  10. sage/ext_data/threejs/animation.css +195 -0
  11. sage/ext_data/threejs/animation.html +85 -0
  12. sage/ext_data/threejs/animation.js +273 -0
  13. sage/ext_data/threejs/fat_lines.js +48 -0
  14. sage/ext_data/threejs/threejs-version.txt +1 -0
  15. sage/ext_data/threejs/threejs_template.html +597 -0
  16. sage/interfaces/all__sagemath_plot.py +1 -0
  17. sage/interfaces/gnuplot.py +196 -0
  18. sage/interfaces/jmoldata.py +208 -0
  19. sage/interfaces/povray.py +56 -0
  20. sage/plot/all.py +42 -0
  21. sage/plot/animate.py +1796 -0
  22. sage/plot/arc.py +504 -0
  23. sage/plot/arrow.py +671 -0
  24. sage/plot/bar_chart.py +205 -0
  25. sage/plot/bezier_path.py +400 -0
  26. sage/plot/circle.py +435 -0
  27. sage/plot/colors.py +1606 -0
  28. sage/plot/complex_plot.cpython-314-x86_64-linux-gnu.so +0 -0
  29. sage/plot/complex_plot.pyx +1446 -0
  30. sage/plot/contour_plot.py +1792 -0
  31. sage/plot/density_plot.py +318 -0
  32. sage/plot/disk.py +373 -0
  33. sage/plot/ellipse.py +375 -0
  34. sage/plot/graphics.py +3580 -0
  35. sage/plot/histogram.py +354 -0
  36. sage/plot/hyperbolic_arc.py +404 -0
  37. sage/plot/hyperbolic_polygon.py +416 -0
  38. sage/plot/hyperbolic_regular_polygon.py +296 -0
  39. sage/plot/line.py +626 -0
  40. sage/plot/matrix_plot.py +629 -0
  41. sage/plot/misc.py +509 -0
  42. sage/plot/multigraphics.py +1294 -0
  43. sage/plot/plot.py +4183 -0
  44. sage/plot/plot3d/all.py +23 -0
  45. sage/plot/plot3d/base.cpython-314-x86_64-linux-gnu.so +0 -0
  46. sage/plot/plot3d/base.pxd +12 -0
  47. sage/plot/plot3d/base.pyx +3378 -0
  48. sage/plot/plot3d/implicit_plot3d.py +659 -0
  49. sage/plot/plot3d/implicit_surface.cpython-314-x86_64-linux-gnu.so +0 -0
  50. sage/plot/plot3d/implicit_surface.pyx +1453 -0
  51. sage/plot/plot3d/index_face_set.cpython-314-x86_64-linux-gnu.so +0 -0
  52. sage/plot/plot3d/index_face_set.pxd +32 -0
  53. sage/plot/plot3d/index_face_set.pyx +1873 -0
  54. sage/plot/plot3d/introduction.py +131 -0
  55. sage/plot/plot3d/list_plot3d.py +649 -0
  56. sage/plot/plot3d/parametric_plot3d.py +1130 -0
  57. sage/plot/plot3d/parametric_surface.cpython-314-x86_64-linux-gnu.so +0 -0
  58. sage/plot/plot3d/parametric_surface.pxd +12 -0
  59. sage/plot/plot3d/parametric_surface.pyx +893 -0
  60. sage/plot/plot3d/platonic.py +601 -0
  61. sage/plot/plot3d/plot3d.py +1442 -0
  62. sage/plot/plot3d/plot_field3d.py +162 -0
  63. sage/plot/plot3d/point_c.pxi +148 -0
  64. sage/plot/plot3d/revolution_plot3d.py +309 -0
  65. sage/plot/plot3d/shapes.cpython-314-x86_64-linux-gnu.so +0 -0
  66. sage/plot/plot3d/shapes.pxd +22 -0
  67. sage/plot/plot3d/shapes.pyx +1382 -0
  68. sage/plot/plot3d/shapes2.py +1512 -0
  69. sage/plot/plot3d/tachyon.py +1779 -0
  70. sage/plot/plot3d/texture.py +453 -0
  71. sage/plot/plot3d/transform.cpython-314-x86_64-linux-gnu.so +0 -0
  72. sage/plot/plot3d/transform.pxd +21 -0
  73. sage/plot/plot3d/transform.pyx +268 -0
  74. sage/plot/plot3d/tri_plot.py +589 -0
  75. sage/plot/plot_field.py +362 -0
  76. sage/plot/point.py +624 -0
  77. sage/plot/polygon.py +562 -0
  78. sage/plot/primitive.py +249 -0
  79. sage/plot/scatter_plot.py +199 -0
  80. sage/plot/step.py +85 -0
  81. sage/plot/streamline_plot.py +328 -0
  82. sage/plot/text.py +432 -0
@@ -0,0 +1,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