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,1873 @@
1
+ # sage_setup: distribution = sagemath-plot
2
+ """
3
+ Indexed face sets
4
+
5
+ Graphics3D object that consists of a list of polygons, also used for
6
+ triangulations of other objects.
7
+
8
+ Usually these objects are not created directly by users.
9
+
10
+ AUTHORS:
11
+
12
+ - Robert Bradshaw (2007-08-26): initial version
13
+ - Robert Bradshaw (2007-08-28): significant optimizations
14
+
15
+ .. TODO::
16
+
17
+ Smooth triangles using vertex normals
18
+ """
19
+ # ****************************************************************************
20
+ # Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu>
21
+ #
22
+ # Distributed under the terms of the GNU General Public License (GPL)
23
+ #
24
+ # This code is distributed in the hope that it will be useful,
25
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
26
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27
+ # General Public License for more details.
28
+ #
29
+ # The full text of the GPL is available at:
30
+ #
31
+ # https://www.gnu.org/licenses/
32
+ # ****************************************************************************
33
+
34
+ from textwrap import dedent
35
+
36
+ from libc.math cimport INFINITY
37
+ from libc.string cimport memset, memcpy
38
+ from cysignals.memory cimport check_calloc, check_allocarray, check_reallocarray, sig_free
39
+ from cysignals.signals cimport sig_check, sig_on, sig_off
40
+
41
+ cdef extern from *:
42
+ int sprintf_3d "sprintf" (char*, char*, double, double, double)
43
+ int sprintf_3i "sprintf" (char*, char*, int, int, int)
44
+ int sprintf_4i "sprintf" (char*, char*, int, int, int, int)
45
+ int sprintf_5i "sprintf" (char*, char*, int, int, int, int, int)
46
+ int sprintf_6i "sprintf" (char*, char*, int, int, int, int, int, int)
47
+ int sprintf_7i "sprintf" (char*, char*, int, int, int, int, int, int, int)
48
+ int sprintf_9d "sprintf" (char*, char*, double, double, double, double, double, double, double, double, double)
49
+
50
+ from cpython.list cimport *
51
+ from cpython.bytes cimport *
52
+
53
+ include "point_c.pxi"
54
+
55
+ from sage.cpython.string cimport bytes_to_str
56
+
57
+ from sage.rings.real_double import RDF
58
+
59
+ from sage.modules.free_module_element import vector
60
+
61
+ from sage.plot.colors import Color, float_to_integer
62
+ from sage.plot.plot3d.base import Graphics3dGroup
63
+ from sage.plot.plot3d.texture import Texture
64
+
65
+ from sage.plot.plot3d.transform cimport Transformation
66
+
67
+
68
+ # --------------------------------------------------------------------
69
+ # Fast routines for generating string representations of the polygons.
70
+ # --------------------------------------------------------------------
71
+
72
+ cdef inline format_tachyon_texture(color_c rgb):
73
+ cdef char rs[200]
74
+ cdef Py_ssize_t cr = sprintf_3d(rs,
75
+ "TEXTURE\n AMBIENT 0.3 DIFFUSE 0.7 SPECULAR 0 OPACITY 1.0\n COLOR %g %g %g \n TEXFUNC 0",
76
+ rgb.r, rgb.g, rgb.b)
77
+ return bytes_to_str(PyBytes_FromStringAndSize(rs, cr))
78
+
79
+
80
+ cdef inline format_tachyon_triangle(point_c P, point_c Q, point_c R):
81
+ cdef char ss[250]
82
+ # PyBytes_FromFormat doesn't do floats?
83
+ cdef Py_ssize_t r = sprintf_9d(ss,
84
+ "TRI V0 %g %g %g V1 %g %g %g V2 %g %g %g",
85
+ P.x, P.y, P.z,
86
+ Q.x, Q.y, Q.z,
87
+ R.x, R.y, R.z )
88
+ return bytes_to_str(PyBytes_FromStringAndSize(ss, r))
89
+
90
+
91
+ cdef inline format_json_vertex(point_c P):
92
+ cdef char ss[100]
93
+ cdef Py_ssize_t r = sprintf_3d(ss, '{"x":%g,"y":%g,"z":%g}', P.x, P.y, P.z)
94
+ return bytes_to_str(PyBytes_FromStringAndSize(ss, r))
95
+
96
+ cdef inline format_json_face(face_c face):
97
+ s = "[{}]".format(",".join(str(face.vertices[i]) for i in range(face.n)))
98
+ return s
99
+
100
+ cdef inline format_obj_vertex(point_c P):
101
+ cdef char ss[100]
102
+ # PyBytes_FromFormat doesn't do floats?
103
+ cdef Py_ssize_t r = sprintf_3d(ss, "v %g %g %g", P.x, P.y, P.z)
104
+ return bytes_to_str(PyBytes_FromStringAndSize(ss, r))
105
+
106
+ cdef inline format_obj_face(face_c face, int off):
107
+ cdef char ss[100]
108
+ cdef Py_ssize_t r, i
109
+ if face.n == 3:
110
+ r = sprintf_3i(ss, "f %d %d %d", face.vertices[0] + off, face.vertices[1] + off, face.vertices[2] + off)
111
+ elif face.n == 4:
112
+ r = sprintf_4i(ss, "f %d %d %d %d", face.vertices[0] + off, face.vertices[1] + off, face.vertices[2] + off, face.vertices[3] + off)
113
+ else:
114
+ return "f " + " ".join(str(face.vertices[i] + off) for i in range(face.n))
115
+ # PyBytes_FromFormat is almost twice as slow
116
+ return bytes_to_str(PyBytes_FromStringAndSize(ss, r))
117
+
118
+ cdef inline format_obj_face_back(face_c face, int off):
119
+ cdef char ss[100]
120
+ cdef Py_ssize_t r, i
121
+ if face.n == 3:
122
+ r = sprintf_3i(ss, "f %d %d %d", face.vertices[2] + off, face.vertices[1] + off, face.vertices[0] + off)
123
+ elif face.n == 4:
124
+ r = sprintf_4i(ss, "f %d %d %d %d", face.vertices[3] + off, face.vertices[2] + off, face.vertices[1] + off, face.vertices[0] + off)
125
+ else:
126
+ return "f " + " ".join(str(face.vertices[i] + off) for i from face.n > i >= 0)
127
+ return bytes_to_str(PyBytes_FromStringAndSize(ss, r))
128
+
129
+ cdef inline format_pmesh_vertex(point_c P):
130
+ cdef char ss[100]
131
+ # PyBytes_FromFormat doesn't do floats?
132
+ cdef Py_ssize_t r = sprintf_3d(ss, "%g %g %g", P.x, P.y, P.z)
133
+ return bytes_to_str(PyBytes_FromStringAndSize(ss, r))
134
+
135
+ cdef inline format_pmesh_face(face_c face, int has_color):
136
+ cdef char ss[100]
137
+ cdef Py_ssize_t r, i
138
+ cdef int color
139
+ # if the face has an individual color, has_color is -1
140
+ # otherwise it is 1
141
+ if has_color == -1:
142
+ color = float_to_integer(face.color.r,
143
+ face.color.g,
144
+ face.color.b)
145
+ # it seems that Jmol does not like the 0 color at all
146
+ if color == 0:
147
+ color = 1
148
+
149
+ if face.n == 3:
150
+ if has_color == 1:
151
+ r = sprintf_5i(ss, "%d\n%d\n%d\n%d\n%d", has_color * 4,
152
+ face.vertices[0],
153
+ face.vertices[1],
154
+ face.vertices[2],
155
+ face.vertices[0])
156
+ else:
157
+ r = sprintf_6i(ss, "%d\n%d\n%d\n%d\n%d\n%d", has_color * 4,
158
+ face.vertices[0],
159
+ face.vertices[1],
160
+ face.vertices[2],
161
+ face.vertices[0], color)
162
+ elif face.n == 4:
163
+ if has_color == 1:
164
+ r = sprintf_6i(ss, "%d\n%d\n%d\n%d\n%d\n%d", has_color * 5,
165
+ face.vertices[0],
166
+ face.vertices[1],
167
+ face.vertices[2],
168
+ face.vertices[3],
169
+ face.vertices[0])
170
+ else:
171
+ r = sprintf_7i(ss, "%d\n%d\n%d\n%d\n%d\n%d\n%d", has_color * 5,
172
+ face.vertices[0],
173
+ face.vertices[1],
174
+ face.vertices[2],
175
+ face.vertices[3],
176
+ face.vertices[0], color)
177
+ else:
178
+ # Naive triangulation
179
+ all = []
180
+ if has_color == 1:
181
+ for i in range(1, face.n - 1):
182
+ r = sprintf_5i(ss, "%d\n%d\n%d\n%d\n%d", has_color * 4,
183
+ face.vertices[0],
184
+ face.vertices[i],
185
+ face.vertices[i + 1],
186
+ face.vertices[0])
187
+ PyList_Append(all, PyBytes_FromStringAndSize(ss, r))
188
+ else:
189
+ for i in range(1, face.n - 1):
190
+ r = sprintf_6i(ss, "%d\n%d\n%d\n%d\n%d\n%d", has_color * 4,
191
+ face.vertices[0],
192
+ face.vertices[i],
193
+ face.vertices[i + 1],
194
+ face.vertices[0], color)
195
+ PyList_Append(all, PyBytes_FromStringAndSize(ss, r))
196
+ return bytes_to_str(b"\n".join(all))
197
+ # PyBytes_FromFormat is almost twice as slow
198
+ return bytes_to_str(PyBytes_FromStringAndSize(ss, r))
199
+
200
+
201
+ def midpoint(pointa, pointb, w):
202
+ """
203
+ Return the weighted mean of two points in 3-space.
204
+
205
+ INPUT:
206
+
207
+ - ``pointa``, ``pointb`` -- two points in 3-dimensional space
208
+
209
+ - ``w`` -- a real weight between 0 and 1
210
+
211
+ If the weight is zero, the result is ``pointb``. If the weight is
212
+ one, the result is ``pointa``.
213
+
214
+ EXAMPLES::
215
+
216
+ sage: from sage.plot.plot3d.index_face_set import midpoint
217
+ sage: midpoint((1,2,3),(4,4,4),0.8)
218
+ (1.60000000000000, 2.40000000000000, 3.20000000000000)
219
+ """
220
+ xa, ya, za = pointa
221
+ xb, yb, zb = pointb
222
+ v = 1 - w
223
+ return ((w * xa + v * xb), (w * ya + v * yb), (w * za + v * zb))
224
+
225
+
226
+ def cut_edge_by_bisection(pointa, pointb, condition, eps=1.0e-6, N=100):
227
+ """
228
+ Cut an intersecting edge using the bisection method.
229
+
230
+ Given two points (pointa and pointb) and a condition (boolean
231
+ function), this calculates the position at the edge (defined by
232
+ both points) where the boolean condition switches its value.
233
+
234
+ INPUT:
235
+
236
+ - ``pointa``, ``pointb`` -- two points in 3-dimensional space
237
+
238
+ - ``N`` -- max number of steps in the bisection method (default: 100)
239
+ to cut the boundary triangles that are not entirely within
240
+ the domain.
241
+
242
+ - ``eps`` -- target accuracy in the intersection (default: 1.0e-6)
243
+
244
+ OUTPUT:
245
+
246
+ intersection of the edge defined by ``pointa`` and ``pointb``,
247
+ and ``condition``.
248
+
249
+ EXAMPLES::
250
+
251
+ sage: from sage.plot.plot3d.index_face_set import cut_edge_by_bisection
252
+ sage: cut_edge_by_bisection((0.0,0.0,0.0), (1.0,1.0,0.0),
253
+ ....: lambda x,y,z: x**2+y**2+z**2 < 1, eps=1.0E-12)
254
+ (0.7071067811864395, 0.7071067811864395, 0.0)
255
+ """
256
+ cdef point_c a, b
257
+ cdef point_c midp, b_min_a
258
+ cdef double half = 0.5
259
+
260
+ point_c_set(&a, pointa)
261
+ point_c_set(&b, pointb)
262
+
263
+ itern = 0
264
+
265
+ point_c_sub(&b_min_a, b, a)
266
+
267
+ while point_c_len(b_min_a) > eps:
268
+ itern += 1
269
+ if itern > N:
270
+ break
271
+ # (b+a)/2
272
+ point_c_middle(&midp, b, a, half)
273
+
274
+ if condition(a.x, a.y, a.z) and condition(midp.x, midp.y, midp.z):
275
+ a = midp
276
+ else:
277
+ b = midp
278
+ # (b-a)
279
+ point_c_sub(&b_min_a, b, a)
280
+
281
+ point_c_middle(&midp, b, a, half)
282
+
283
+ return midp.x, midp.y, midp.z
284
+
285
+
286
+ cdef class IndexFaceSet(PrimitiveObject):
287
+ """
288
+ Graphics3D object that consists of a list of polygons, also used for
289
+ triangulations of other objects.
290
+
291
+ Polygons (mostly triangles and quadrilaterals) are stored in the
292
+ c struct ``face_c`` (see transform.pyx). Rather than storing
293
+ the points directly for each polygon, each face consists a list
294
+ of pointers into a common list of points which are basically triples
295
+ of doubles in a ``point_c``.
296
+
297
+ Moreover, each face has an attribute ``color`` which is used to
298
+ store color information when faces are colored. The red/green/blue
299
+ components are then available as floats between 0 and 1 using
300
+ ``color.r,color.g,color.b``.
301
+
302
+ Usually these objects are not created directly by users.
303
+
304
+ EXAMPLES::
305
+
306
+ sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
307
+ sage: S = IndexFaceSet([[(1,0,0),(0,1,0),(0,0,1)], [(1,0,0),(0,1,0),(0,0,0)]])
308
+ sage: S.face_list()
309
+ [[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)],
310
+ [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 0.0)]]
311
+ sage: S.vertex_list()
312
+ [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0), (0.0, 0.0, 0.0)]
313
+
314
+ sage: def make_face(n): return [(0,0,n),(0,1,n),(1,1,n),(1,0,n)]
315
+ sage: S = IndexFaceSet([make_face(n) for n in range(10)])
316
+ sage: S.show()
317
+
318
+ sage: point_list = [(1,0,0),(0,1,0)] + [(0,0,n) for n in range(10)]
319
+ sage: face_list = [[0,1,n] for n in range(2,10)]
320
+ sage: S = IndexFaceSet(face_list, point_list, color='red')
321
+ sage: S.face_list()
322
+ [[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 0.0)],
323
+ [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)],
324
+ [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 2.0)],
325
+ [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 3.0)],
326
+ [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 4.0)],
327
+ [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 5.0)],
328
+ [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 6.0)],
329
+ [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 7.0)]]
330
+ sage: S.show()
331
+
332
+ A simple example of colored IndexFaceSet (:issue:`12212`)::
333
+
334
+ sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
335
+ sage: from sage.plot.plot3d.texture import Texture
336
+ sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
337
+ sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
338
+ sage: col = rainbow(10, 'rgbtuple')
339
+ sage: t_list = [Texture(col[i]) for i in range(10)]
340
+ sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
341
+ sage: S.show(viewer='tachyon')
342
+ """
343
+
344
+ def __init__(self, faces, point_list=None,
345
+ enclosed=False, texture_list=None, **kwds):
346
+ if 'alpha' in kwds:
347
+ opacity = float(kwds.pop('alpha'))
348
+ kwds['opacity'] = opacity
349
+ PrimitiveObject.__init__(self, **kwds)
350
+ self._set_extra_kwds(kwds)
351
+
352
+ self.global_texture = (texture_list is None)
353
+
354
+ self.enclosed = enclosed
355
+
356
+ if point_list is None:
357
+ face_list = faces
358
+ faces = []
359
+ point_list = []
360
+ point_index = {}
361
+ for face in face_list:
362
+ iface = []
363
+ for p in face:
364
+ try:
365
+ ix = point_index[p]
366
+ except KeyError:
367
+ ix = len(point_list)
368
+ point_index[p] = ix
369
+ point_list.append(p)
370
+ iface.append(ix)
371
+ faces.append(iface)
372
+
373
+ cdef Py_ssize_t i
374
+ cdef Py_ssize_t index_len = 0
375
+ for i in range(len(faces)):
376
+ index_len += len(faces[i])
377
+
378
+ self.realloc(len(point_list), len(faces), index_len)
379
+
380
+ for i in range(self.vcount):
381
+ self.vs[i].x, self.vs[i].y, self.vs[i].z = point_list[i]
382
+
383
+ cdef int cur_pt = 0
384
+ for i in range(self.fcount):
385
+ self._faces[i].n = len(faces[i])
386
+ self._faces[i].vertices = &self.face_indices[cur_pt]
387
+ if self.global_texture:
388
+ self._faces[i].color.r, self._faces[i].color.g, self._faces[i].color.b = self.texture.color
389
+ else:
390
+ self._faces[i].color.r, self._faces[i].color.g, self._faces[i].color.b = texture_list[i].color
391
+ for ix in faces[i]:
392
+ self.face_indices[cur_pt] = ix
393
+ cur_pt += 1
394
+
395
+ cdef int realloc(self, Py_ssize_t vcount, Py_ssize_t fcount, Py_ssize_t icount) except -1:
396
+ r"""
397
+ Allocate memory for vertices, faces, and face indices. Can
398
+ only be called from Cython, so the doctests must be indirect.
399
+
400
+ EXAMPLES::
401
+
402
+ sage: # needs sage.symbolic
403
+ sage: var('x,y,z')
404
+ (x, y, z)
405
+ sage: G = implicit_plot3d(x^2+y^2+z^2 - 1,
406
+ ....: (x, -2, 2), (y, -2, 2), (z, -2, 2), plot_points=6)
407
+ sage: G.triangulate() # indirect doctest
408
+ sage: len(G.face_list())
409
+ 44
410
+ sage: len(G.vertex_list())
411
+ 132
412
+ sage: G = implicit_plot3d(x^2+y^2+z^2 - 100,
413
+ ....: (x, -2, 2), (y, -2, 2), (z, -2, 2), plot_points=6)
414
+ sage: G.triangulate() # indirect doctest
415
+ sage: len(G.face_list())
416
+ 0
417
+ sage: len(G.vertex_list())
418
+ 0
419
+ """
420
+ self.vs = <point_c*>check_reallocarray(self.vs, vcount, sizeof(point_c))
421
+ self.vcount = vcount
422
+ self._faces = <face_c*>check_reallocarray(self._faces, fcount, sizeof(face_c))
423
+ self.fcount = fcount
424
+ self.face_indices = <int*>check_reallocarray(self.face_indices, icount, sizeof(int))
425
+ self.icount = icount
426
+
427
+ def _clean_point_list(self):
428
+ """
429
+ Clean up the vertices and faces as follows:
430
+
431
+ - Remove all vertices with a coordinate which is NaN or
432
+ infinity.
433
+
434
+ - If a removed vertex occurs in a face, remove it from that
435
+ face, but keep other vertices in that face.
436
+
437
+ - Remove faces with less than 3 vertices.
438
+
439
+ - Remove unused vertices.
440
+
441
+ - Free unused memory for vertices and faces (not indices).
442
+ """
443
+ cdef Py_ssize_t i, j, v
444
+
445
+ # point_map is an array old vertex index -> new vertex index.
446
+ # The special value -1 means that the vertex is not mapped yet.
447
+ # The special value -2 means that the vertex must be deleted
448
+ # because a coordinate is NaN or infinity.
449
+ # When we are done, all vertices with negative indices are not
450
+ # used and will be removed.
451
+ cdef int* point_map = <int*>check_allocarray(self.vcount, sizeof(int))
452
+
453
+ cdef Py_ssize_t nv = 0 # number of new vertices
454
+ for i in range(self.vcount):
455
+ point_map[i] = -1
456
+
457
+ # Process all faces
458
+ cdef Py_ssize_t nf = 0 # number of new faces
459
+ cdef Py_ssize_t fv # number of new vertices on face
460
+ for i in range(self.fcount):
461
+ face = &self._faces[i]
462
+
463
+ # Process vertices in face
464
+ fv = 0
465
+ for j in range(face.n):
466
+ v = face.vertices[j]
467
+ if point_map[v] == -1:
468
+ if point_c_isfinite(self.vs[v]):
469
+ point_map[v] = nv
470
+ nv += 1
471
+ else:
472
+ point_map[v] = -2
473
+ if point_map[v] == -2:
474
+ continue
475
+
476
+ face.vertices[fv] = point_map[face.vertices[j]]
477
+ fv += 1
478
+
479
+ # Skip faces with less than 3 vertices
480
+ if fv < 3:
481
+ continue
482
+
483
+ # Store in newface
484
+ newface = &self._faces[nf]
485
+ newface.n = fv
486
+ if newface is not face:
487
+ newface.vertices = face.vertices
488
+ newface.color = face.color
489
+ nf += 1
490
+
491
+ # Realloc face array
492
+ if nf < self.fcount:
493
+ self._faces = <face_c*>check_reallocarray(self._faces, nf, sizeof(face_c))
494
+ self.fcount = nf
495
+
496
+ # Realloc and map vertex array
497
+ # We cannot copy in-place since we permuted the vertices
498
+ new_vs = <point_c*>check_allocarray(nv, sizeof(point_c))
499
+ for i in range(self.vcount):
500
+ j = point_map[i]
501
+ if j >= 0:
502
+ new_vs[j] = self.vs[i]
503
+
504
+ sig_free(point_map)
505
+ sig_free(self.vs)
506
+ self.vs = new_vs
507
+ self.vcount = nv
508
+
509
+ def _separate_creases(self, threshold):
510
+ """
511
+ Some rendering engines Gouraud shading, which is great for smooth
512
+ surfaces but looks bad if one actually has a polyhedron.
513
+
514
+ INPUT:
515
+
516
+ - ``threshold`` -- the minimum cosine of the angle between adjacent
517
+ faces a higher threshold separates more, all faces if >= 1, no
518
+ faces if <= -1
519
+ """
520
+ cdef Py_ssize_t i, j, k
521
+ cdef face_c *face
522
+ cdef int v, count, total = 0
523
+ cdef int* point_counts = <int *>check_calloc(self.vcount * 2 + 1, sizeof(int))
524
+ # For each vertex, get number of faces
525
+ cdef int* running_point_counts = &point_counts[self.vcount]
526
+ for i in range(self.fcount):
527
+ face = &self._faces[i]
528
+ total += face.n
529
+ for j in range(face.n):
530
+ point_counts[face.vertices[j]] += 1
531
+ # Running used as index into face list
532
+ cdef int running = 0
533
+ cdef int max = 0
534
+ for i in range(self.vcount):
535
+ running_point_counts[i] = running
536
+ running += point_counts[i]
537
+ if point_counts[i] > max:
538
+ max = point_counts[i]
539
+ running_point_counts[self.vcount] = running
540
+ # Create an array, indexed by running_point_counts[v], to the list of faces containing that vertex.
541
+ cdef face_c** point_faces
542
+ try:
543
+ point_faces = <face_c **>check_allocarray(total, sizeof(face_c*))
544
+ except MemoryError:
545
+ sig_free(point_counts)
546
+ raise
547
+ sig_on()
548
+ memset(point_counts, 0, sizeof(int) * self.vcount)
549
+ for i in range(self.fcount):
550
+ face = &self._faces[i]
551
+ for j in range(face.n):
552
+ v = face.vertices[j]
553
+ point_faces[running_point_counts[v]+point_counts[v]] = face
554
+ point_counts[v] += 1
555
+ # Now, for each vertex, see if all faces are close enough,
556
+ # or if it is a crease.
557
+ cdef face_c** faces
558
+ cdef int start = 0
559
+ cdef bint any
560
+ # We compare against face 0, and if it's not flat enough we push it to the end.
561
+ # Then we come around again to compare everything that was put at the end, possibly
562
+ # pushing stuff to the end again (until no further changes are needed).
563
+ while start < self.vcount:
564
+ ix = self.vcount
565
+ # Find creases
566
+ for i in range(self.vcount - start):
567
+ faces = &point_faces[running_point_counts[i]]
568
+ any = 0
569
+ for j from point_counts[i] > j >= 1:
570
+ if cos_face_angle(faces[0][0], faces[j][0], self.vs) < threshold:
571
+ any = 1
572
+ face = faces[j]
573
+ point_counts[i] -= 1
574
+ if j != point_counts[i]:
575
+ faces[j] = faces[point_counts[i]] # swap
576
+ faces[point_counts[i]] = face
577
+ if any:
578
+ ix += 1
579
+ # Reallocate room for vertices at end
580
+ if ix > self.vcount:
581
+ try:
582
+ self.vs = <point_c *>check_reallocarray(self.vs, ix, sizeof(point_c))
583
+ except MemoryError:
584
+ sig_free(point_counts)
585
+ sig_free(point_faces)
586
+ self.vcount = self.fcount = self.icount = 0 # so we don't get segfaults on bad points
587
+ sig_off()
588
+ raise
589
+ ix = self.vcount
590
+ running = 0
591
+ for i in range(self.vcount - start):
592
+ if point_counts[i] != running_point_counts[i+1] - running_point_counts[i]:
593
+ # We have a new vertex
594
+ self.vs[ix] = self.vs[i+start]
595
+ # Update the point_counts and point_faces arrays for the next time around.
596
+ count = running_point_counts[i+1] - running_point_counts[i] - point_counts[i]
597
+ faces = &point_faces[running]
598
+ for j in range(count):
599
+ faces[j] = point_faces[running_point_counts[i] + point_counts[i] + j]
600
+ face = faces[j]
601
+ for k in range(face.n):
602
+ if face.vertices[k] == i + start:
603
+ face.vertices[k] = ix
604
+ point_counts[ix-self.vcount] = count
605
+ running_point_counts[ix-self.vcount] = running
606
+ running += count
607
+ ix += 1
608
+ running_point_counts[ix-self.vcount] = running
609
+ start = self.vcount
610
+ self.vcount = ix
611
+
612
+ sig_free(point_counts)
613
+ sig_free(point_faces)
614
+ sig_off()
615
+
616
+ def _mem_stats(self):
617
+ return self.vcount, self.fcount, self.icount
618
+
619
+ def __dealloc__(self):
620
+ sig_free(self.vs)
621
+ sig_free(self._faces)
622
+ sig_free(self.face_indices)
623
+
624
+ def is_enclosed(self):
625
+ """
626
+ Whether or not it is necessary to render the back sides of the polygons.
627
+
628
+ One is assuming, of course, that they have the correct orientation.
629
+
630
+ This is may be passed in on construction. It is also
631
+ calculated in
632
+ :class:`sage.plot.plot3d.parametric_surface.ParametricSurface`
633
+ by verifying the opposite edges of the rendered domain either
634
+ line up or are pinched together.
635
+
636
+ EXAMPLES::
637
+
638
+ sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
639
+ sage: IndexFaceSet([[(0,0,1),(0,1,0),(1,0,0)]]).is_enclosed()
640
+ False
641
+ """
642
+ return self.enclosed
643
+
644
+ def index_faces(self):
645
+ """
646
+ Return the list over all faces of the indices of the vertices.
647
+
648
+ EXAMPLES::
649
+
650
+ sage: from sage.plot.plot3d.shapes import *
651
+ sage: S = Box(1,2,3)
652
+ sage: S.index_faces()
653
+ [[0, 1, 2, 3],
654
+ [0, 4, 5, 1],
655
+ [0, 3, 6, 4],
656
+ [5, 4, 6, 7],
657
+ [6, 3, 2, 7],
658
+ [2, 1, 5, 7]]
659
+ """
660
+ cdef Py_ssize_t i, j
661
+ return [[self._faces[i].vertices[j]
662
+ for j in range(self._faces[i].n)]
663
+ for i in range(self.fcount)]
664
+
665
+ def has_local_colors(self) -> bool:
666
+ """
667
+ Return ``True`` if and only if every face has an individual color.
668
+
669
+ EXAMPLES::
670
+
671
+ sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
672
+ sage: from sage.plot.plot3d.texture import Texture
673
+ sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
674
+ sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
675
+ sage: col = rainbow(10, 'rgbtuple')
676
+ sage: t_list=[Texture(col[i]) for i in range(10)]
677
+ sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
678
+ sage: S.has_local_colors()
679
+ True
680
+
681
+ sage: from sage.plot.plot3d.shapes import *
682
+ sage: S = Box(1,2,3)
683
+ sage: S.has_local_colors()
684
+ False
685
+ """
686
+ return not self.global_texture
687
+
688
+ def index_faces_with_colors(self):
689
+ """
690
+ Return the list over all faces of (indices of the vertices, color).
691
+
692
+ This only works if every face has its own color.
693
+
694
+ .. SEEALSO::
695
+
696
+ :meth:`has_local_colors`
697
+
698
+ EXAMPLES:
699
+
700
+ A simple colored one::
701
+
702
+ sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
703
+ sage: from sage.plot.plot3d.texture import Texture
704
+ sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
705
+ sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
706
+ sage: col = rainbow(10, 'rgbtuple')
707
+ sage: t_list = [Texture(col[i]) for i in range(10)]
708
+ sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
709
+ sage: S.index_faces_with_colors()
710
+ [([0, 4, 5], '#ff0000'),
711
+ ([3, 4, 5], '#ff9900'),
712
+ ([2, 3, 4], '#cbff00'),
713
+ ([1, 3, 5], '#33ff00')]
714
+
715
+ When the texture is global, an error is raised::
716
+
717
+ sage: from sage.plot.plot3d.shapes import *
718
+ sage: S = Box(1,2,3)
719
+ sage: S.index_faces_with_colors()
720
+ Traceback (most recent call last):
721
+ ...
722
+ ValueError: the texture is global
723
+ """
724
+ cdef Py_ssize_t i, j
725
+ if self.global_texture:
726
+ raise ValueError('the texture is global')
727
+ return [([self._faces[i].vertices[j]
728
+ for j in range(self._faces[i].n)],
729
+ Color(self._faces[i].color.r,
730
+ self._faces[i].color.g,
731
+ self._faces[i].color.b).html_color())
732
+ for i in range(self.fcount)]
733
+
734
+ def faces(self):
735
+ """
736
+ An iterator over the faces.
737
+
738
+ EXAMPLES::
739
+
740
+ sage: from sage.plot.plot3d.shapes import *
741
+ sage: S = Box(1,2,3)
742
+ sage: list(S.faces()) == S.face_list()
743
+ True
744
+ """
745
+ return FaceIter(self)
746
+
747
+ def face_list(self, render_params=None):
748
+ """
749
+ Return the list of faces.
750
+
751
+ Every face is given as a tuple of vertices.
752
+
753
+ EXAMPLES::
754
+
755
+ sage: from sage.plot.plot3d.shapes import *
756
+ sage: S = Box(1,2,3)
757
+ sage: S.face_list(S.default_render_params())[0]
758
+ [(1.0, 2.0, 3.0), (-1.0, 2.0, 3.0), (-1.0, -2.0, 3.0), (1.0, -2.0, 3.0)]
759
+ """
760
+ cdef Transformation transform
761
+ cdef Py_ssize_t i, j
762
+ cdef point_c res
763
+ if render_params is not None:
764
+ transform = render_params.transform
765
+ else:
766
+ transform = None
767
+ if transform is None:
768
+ points = [(self.vs[i].x, self.vs[i].y, self.vs[i].z)
769
+ for i in range(self.vcount)]
770
+ else:
771
+ points = []
772
+ for i in range(self.vcount):
773
+ transform.transform_point_c(&res, self.vs[i])
774
+ PyList_Append(points, (res.x, res.y, res.z))
775
+
776
+ return [[points[self._faces[i].vertices[j]]
777
+ for j in range(self._faces[i].n)]
778
+ for i in range(self.fcount)]
779
+
780
+ def edges(self):
781
+ """
782
+ An iterator over the edges.
783
+
784
+ EXAMPLES::
785
+
786
+ sage: from sage.plot.plot3d.shapes import *
787
+ sage: S = Box(1,2,3)
788
+ sage: list(S.edges())[0]
789
+ ((1.0, -2.0, 3.0), (1.0, 2.0, 3.0))
790
+ """
791
+ return EdgeIter(self)
792
+
793
+ def edge_list(self):
794
+ """
795
+ Return the list of edges.
796
+
797
+ EXAMPLES::
798
+
799
+ sage: from sage.plot.plot3d.shapes import *
800
+ sage: S = Box(1,2,3)
801
+ sage: S.edge_list()[0]
802
+ ((1.0, -2.0, 3.0), (1.0, 2.0, 3.0))
803
+ """
804
+ return list(self.edges())
805
+
806
+ def vertices(self):
807
+ """
808
+ An iterator over the vertices.
809
+
810
+ EXAMPLES::
811
+
812
+ sage: from sage.plot.plot3d.shapes import *
813
+ sage: S = Cone(1,1)
814
+ sage: list(S.vertices()) == S.vertex_list()
815
+ True
816
+ """
817
+ return VertexIter(self)
818
+
819
+ def vertex_list(self):
820
+ """
821
+ Return the list of vertices.
822
+
823
+ EXAMPLES::
824
+
825
+ sage: from sage.plot.plot3d.shapes import *
826
+ sage: S = polygon([(0,0,1), (1,1,1), (2,0,1)])
827
+ sage: S.vertex_list()[0]
828
+ (0.0, 0.0, 1.0)
829
+ """
830
+ cdef Py_ssize_t i
831
+ return [(self.vs[i].x, self.vs[i].y, self.vs[i].z) for i in range(self.vcount)]
832
+
833
+ def x3d_geometry(self):
834
+ """
835
+ Return the x3d data.
836
+
837
+ EXAMPLES:
838
+
839
+ A basic test with a triangle::
840
+
841
+ sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)])
842
+ sage: print(G.x3d_geometry())
843
+ <BLANKLINE>
844
+ <IndexedFaceSet coordIndex='0,1,2,-1'>
845
+ <Coordinate point='0.0 0.0 1.0,1.0 1.0 1.0,2.0 0.0 1.0'/>
846
+ </IndexedFaceSet>
847
+ <BLANKLINE>
848
+
849
+ A simple colored one::
850
+
851
+ sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
852
+ sage: from sage.plot.plot3d.texture import Texture
853
+ sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
854
+ sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
855
+ sage: col = rainbow(10, 'rgbtuple')
856
+ sage: t_list = [Texture(col[i]) for i in range(10)]
857
+ sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
858
+ sage: print(S.x3d_geometry())
859
+ <BLANKLINE>
860
+ <IndexedFaceSet solid='False' colorPerVertex='False' coordIndex='0,4,5,-1,3,4,5,-1,2,3,4,-1,1,3,5,-1'>
861
+ <Coordinate point='2.0 0.0 0.0,0.0 2.0 0.0,0.0 0.0 2.0,0.0 1.0 1.0,1.0 0.0 1.0,1.0 1.0 0.0'/>
862
+ <Color color='1.0 0.0 0.0,1.0 0.6000000000000001 0.0,0.7999999999999998 1.0 0.0,0.20000000000000018 1.0 0.0' />
863
+ </IndexedFaceSet>
864
+ <BLANKLINE>
865
+ """
866
+ cdef Py_ssize_t i
867
+ vs = self.vs
868
+ fs = self._faces
869
+ points = ",".join("%r %r %r" % (vs[i].x, vs[i].y, vs[i].z)
870
+ for i in range(self.vcount))
871
+ coord_idx = ",-1,".join(",".join(repr(fs[i].vertices[j])
872
+ for j in range(fs[i].n))
873
+ for i in range(self.fcount))
874
+ if not self.global_texture:
875
+ color_idx = ",".join('%r %r %r' % (fs[i].color.r, fs[i].color.g, fs[i].color.b)
876
+ for i in range(self.fcount))
877
+ # Note: Don't use f-strings, since Sage on Python 2 still expects
878
+ # this to return a plain str instead of a unicode
879
+ return dedent("""
880
+ <IndexedFaceSet solid='False' colorPerVertex='False' coordIndex='{coord_idx},-1'>
881
+ <Coordinate point='{points}'/>
882
+ <Color color='{color_idx}' />
883
+ </IndexedFaceSet>
884
+ """.format(coord_idx=coord_idx, points=points, color_idx=color_idx))
885
+
886
+ return dedent("""
887
+ <IndexedFaceSet coordIndex='{coord_idx},-1'>
888
+ <Coordinate point='{points}'/>
889
+ </IndexedFaceSet>
890
+ """.format(coord_idx=coord_idx, points=points))
891
+
892
+ def bounding_box(self):
893
+ r"""
894
+ Calculate the bounding box for the vertices in this object
895
+ (ignoring infinite or NaN coordinates).
896
+
897
+ OUTPUT:
898
+
899
+ a tuple ( (low_x, low_y, low_z), (high_x, high_y, high_z)),
900
+ which gives the coordinates of opposite corners of the
901
+ bounding box.
902
+
903
+ EXAMPLES::
904
+
905
+ sage: x,y = var('x,y') # needs sage.symbolic
906
+ sage: p = plot3d(sqrt(sin(x)*sin(y)), (x,0,2*pi), (y,0,2*pi)) # needs sage.symbolic
907
+ sage: p.bounding_box() # needs sage.symbolic
908
+ ((0.0, 0.0, 0.0), (6.283185307179586, 6.283185307179586, 0.9991889981715697))
909
+ """
910
+ if self.vcount == 0:
911
+ return ((0,0,0),(0,0,0))
912
+
913
+ cdef Py_ssize_t i
914
+ cdef point_c low
915
+ cdef point_c high
916
+
917
+ low.x, low.y, low.z = INFINITY, INFINITY, INFINITY
918
+ high.x, high.y, high.z = -INFINITY, -INFINITY, -INFINITY
919
+
920
+ for i in range(self.vcount):
921
+ point_c_update_finite_lower_bound(&low, self.vs[i])
922
+ point_c_update_finite_upper_bound(&high, self.vs[i])
923
+ return ((low.x, low.y, low.z), (high.x, high.y, high.z))
924
+
925
+ def partition(self, f):
926
+ r"""
927
+ Partition the faces of ``self``.
928
+
929
+ The partition is done according to the value of a map
930
+ `f: \RR^3 \rightarrow \ZZ` applied to the center of each face.
931
+
932
+ INPUT:
933
+
934
+ - ``f`` -- a function from `\RR^3` to `\ZZ`
935
+
936
+ EXAMPLES::
937
+
938
+ sage: from sage.plot.plot3d.shapes import *
939
+ sage: S = Box(1,2,3)
940
+ sage: len(S.partition(lambda x,y,z: floor(x+y+z)))
941
+ 6
942
+ """
943
+ cdef Py_ssize_t i, j, ix, face_ix
944
+ cdef int part
945
+ cdef point_c P
946
+ cdef face_c *face
947
+ cdef face_c *new_face
948
+ cdef IndexFaceSet face_set
949
+
950
+ cdef int *partition = <int *>check_allocarray(self.fcount, sizeof(int))
951
+
952
+ part_counts = {}
953
+ for i in range(self.fcount):
954
+ face = &self._faces[i]
955
+ P = self.vs[face.vertices[0]]
956
+ for j in range(1, face.n):
957
+ point_c_add(&P, P, self.vs[face.vertices[j]])
958
+ point_c_mul(&P, P, 1.0/face.n)
959
+ partition[i] = part = f(P.x, P.y, P.z)
960
+ try:
961
+ count = part_counts[part]
962
+ except KeyError:
963
+ part_counts[part] = count = [0, 0]
964
+ count[0] += 1
965
+ count[1] += face.n
966
+ all = {}
967
+ for part, count in part_counts.iteritems():
968
+ face_set = IndexFaceSet([])
969
+ face_set.realloc(self.vcount, count[0], count[1])
970
+ memcpy(face_set.vs, self.vs, sizeof(point_c) * self.vcount)
971
+ face_ix = 0
972
+ ix = 0
973
+ for i in range(self.fcount):
974
+ if partition[i] == part:
975
+ face = &self._faces[i]
976
+ new_face = &face_set._faces[face_ix]
977
+ new_face.n = face.n
978
+ new_face.vertices = &face_set.face_indices[ix]
979
+ for j in range(face.n):
980
+ new_face.vertices[j] = face.vertices[j]
981
+ face_ix += 1
982
+ ix += face.n
983
+ face_set._clean_point_list()
984
+ all[part] = face_set
985
+ sig_free(partition)
986
+ return all
987
+
988
+ def add_condition(self, condition, N=100, eps=1.0E-6):
989
+ """
990
+ Cut the surface according to the given condition.
991
+
992
+ This allows to take the intersection of the surface
993
+ with a domain in 3-space, in such a way that the result
994
+ has a smooth boundary.
995
+
996
+ INPUT:
997
+
998
+ - ``condition`` -- boolean function on ambient space, that
999
+ defines the domain
1000
+
1001
+ - ``N`` -- max number of steps used by the bisection method
1002
+ (default: 100) to cut the boundary triangles that are not
1003
+ entirely within the domain.
1004
+
1005
+ - ``eps`` -- target accuracy in the intersection (default: 1.0e-6)
1006
+
1007
+ OUTPUT: an ``IndexFaceSet``
1008
+
1009
+ This will contain both triangular and quadrilateral faces.
1010
+
1011
+ EXAMPLES::
1012
+
1013
+ sage: var('x,y,z') # needs sage.symbolic
1014
+ (x, y, z)
1015
+ sage: P = implicit_plot3d(z-x*y,(-2,2),(-2,2),(-2,2)) # needs sage.symbolic
1016
+ sage: def condi(x, y, z):
1017
+ ....: return bool(x*x+y*y+z*z <= Integer(1))
1018
+ sage: R = P.add_condition(condi, 20); R # needs sage.symbolic
1019
+ Graphics3d Object
1020
+
1021
+ .. PLOT::
1022
+
1023
+ x,y,z = var('x,y,z')
1024
+ P = implicit_plot3d(z-x*y,(-2,2),(-2,2),(-2,2))
1025
+ def condi(x, y, z):
1026
+ return bool(x*x+y*y+z*z <= Integer(1))
1027
+ sphinx_plot(P.add_condition(condi,40))
1028
+
1029
+ An example with colors::
1030
+
1031
+ sage: def condi(x, y, z):
1032
+ ....: return bool(x*x+y*y <= 1.1)
1033
+ sage: cm = colormaps.hsv
1034
+ sage: cf = lambda x,y,z: float(x+y) % 1
1035
+ sage: P = implicit_plot3d(x**2+y**2+z**2-1-x**2*z+y**2*z, # needs sage.symbolic
1036
+ ....: (-2,2),(-2,2),(-2,2),color=(cm,cf))
1037
+ sage: R = P.add_condition(condi,40); R # needs sage.symbolic
1038
+ Graphics3d Object
1039
+
1040
+ .. PLOT::
1041
+
1042
+ x,y,z = var('x,y,z')
1043
+ def condi(x, y, z):
1044
+ return bool(x*x+y*y <= 1.1)
1045
+ cm = colormaps.hsv
1046
+ cf = lambda x,y,z: float(x+y) % 1
1047
+ P = implicit_plot3d(x**2+y**2+z**2-1-x**2*z+y**2*z,(-2,2),(-2,2),(-2,2),color=(cm,cf))
1048
+ sphinx_plot(P.add_condition(condi,40))
1049
+
1050
+ An example with transparency::
1051
+
1052
+ sage: P = implicit_plot3d(x**4+y**4+z**2-4, (x,-2,2), (y,-2,2), (z,-2,2), # needs sage.symbolic
1053
+ ....: alpha=0.3)
1054
+ sage: def cut(a, b, c):
1055
+ ....: return a*a+c*c > 2
1056
+ sage: Q = P.add_condition(cut,40); Q # needs sage.symbolic
1057
+ Graphics3d Object
1058
+
1059
+ .. PLOT::
1060
+
1061
+ x,y,z = var('x,y,z')
1062
+ P = implicit_plot3d(x**4+y**4+z**2-4,(x,-2,2),(y,-2,2),(z,-2,2),alpha=0.3)
1063
+ def cut(a, b, c):
1064
+ return a*a+c*c > 2
1065
+ sphinx_plot(P.add_condition(cut,40))
1066
+
1067
+ A sombrero with quadrilaterals::
1068
+
1069
+ sage: P = plot3d(-sin(2*x*x+2*y*y)*exp(-x*x-y*y), (x,-2,2), (y,-2,2), # needs sage.symbolic
1070
+ ....: color='gold')
1071
+ sage: def cut(x, y, z):
1072
+ ....: return x*x+y*y < 1
1073
+ sage: Q = P.add_condition(cut);Q # needs sage.symbolic
1074
+ Graphics3d Object
1075
+
1076
+ .. PLOT::
1077
+
1078
+ x,y,z = var('x,y,z')
1079
+ P = plot3d(-sin(2*x*x+2*y*y)*exp(-x*x-y*y),(x,-2,2),(y,-2,2),color='gold')
1080
+ def cut(x, y, z):
1081
+ return x*x+y*y < 1
1082
+ sphinx_plot(P.add_condition(cut))
1083
+
1084
+ TESTS:
1085
+
1086
+ One test for preservation of transparency :issue:`28783`::
1087
+
1088
+ sage: # needs sage.symbolic
1089
+ sage: x,y,z = var('x,y,z')
1090
+ sage: P = plot3d(cos(x*y),(x,-2,2),(y,-2,2),color='red',opacity=0.1)
1091
+ sage: def condi(x, y, z):
1092
+ ....: return not(x*x+y*y <= 1)
1093
+ sage: Q = P.add_condition(condi, 40)
1094
+ sage: L = Q.json_repr(Q.default_render_params())
1095
+ sage: '"opacity":0.1' in L[-1]
1096
+ True
1097
+
1098
+ A test that this works with polygons::
1099
+
1100
+ sage: p = polygon3d([[2,0,0], [0,2,0], [0,0,3]])
1101
+ sage: def f(x, y, z):
1102
+ ....: return bool(x*x+y*y+z*z<=5)
1103
+ sage: cut = p.add_condition(f,60,1.0e-12); cut.face_list() # needs sage.symbolic
1104
+ [[(0.556128491210302, 0.0, 2.165807263184547),
1105
+ (2.0, 0.0, 0.0),
1106
+ (0.0, 2.0, 0.0),
1107
+ (0.0, 0.556128491210302, 2.165807263184547)]]
1108
+
1109
+ .. TODO::
1110
+
1111
+ - Use a dichotomy to search for the place where to cut,
1112
+ - Compute the cut only once for each edge.
1113
+ """
1114
+ index = 0
1115
+ if hasattr(self, 'triangulate'):
1116
+ self.triangulate()
1117
+ local_colored = self.has_local_colors()
1118
+ V = self.vertex_list()
1119
+ old_index_to_index = {}
1120
+ point_list = []
1121
+ for old_index, vertex in enumerate(V):
1122
+ if condition(*vertex):
1123
+ old_index_to_index[old_index] = index
1124
+ point_list.append(vertex)
1125
+ index += 1
1126
+
1127
+ face_list = []
1128
+ if local_colored:
1129
+ texture_list = []
1130
+ index_faces = self.index_faces_with_colors()
1131
+ else:
1132
+ texture = self.texture
1133
+ index_faces = self.index_faces()
1134
+
1135
+ if local_colored:
1136
+ def iter_split_faces():
1137
+ for triple in index_faces:
1138
+ triple, color = triple
1139
+ if len(triple) == 3:
1140
+ yield triple, color
1141
+ else:
1142
+ v0 = triple[0]
1143
+ for i in range(1, len(triple) - 1):
1144
+ yield (v0, triple[i], triple[i + 1]), color
1145
+ else:
1146
+ def iter_split_faces():
1147
+ for triple in index_faces:
1148
+ if len(triple) == 3:
1149
+ yield triple
1150
+ else:
1151
+ v0 = triple[0]
1152
+ for i in range(1, len(triple) - 1):
1153
+ yield (v0, triple[i], triple[i + 1])
1154
+
1155
+ for triple in iter_split_faces():
1156
+ if local_colored:
1157
+ triple, color = triple
1158
+ inside = [x for x in triple if x in old_index_to_index]
1159
+ outside = [x for x in triple if x not in inside]
1160
+ face_degree = len(inside)
1161
+ if face_degree >= 1 and local_colored:
1162
+ texture_list.append(Texture(color=color))
1163
+ if face_degree == 3:
1164
+ face_list.append([old_index_to_index[i] for i in triple])
1165
+ elif face_degree == 2:
1166
+ old_c = outside[0]
1167
+ if old_c == triple[1]:
1168
+ old_b, old_a = inside
1169
+ else:
1170
+ old_a, old_b = inside
1171
+ va = V[old_a]
1172
+ vb = V[old_b]
1173
+ vc = V[old_c]
1174
+ middle_ac = cut_edge_by_bisection(va, vc, condition, eps, N)
1175
+ middle_bc = cut_edge_by_bisection(vb, vc, condition, eps, N)
1176
+ point_list += [middle_ac, middle_bc]
1177
+ face_list.append([index, old_index_to_index[old_a],
1178
+ old_index_to_index[old_b], index + 1])
1179
+ index += 2
1180
+ elif face_degree == 1:
1181
+ old_a = inside[0]
1182
+ if old_a == triple[1]:
1183
+ old_c, old_b = outside
1184
+ else:
1185
+ old_b, old_c = outside
1186
+ va = V[old_a]
1187
+ vb = V[old_b]
1188
+ vc = V[old_c]
1189
+ # Use bisection to find the intersection
1190
+ middle_ab = cut_edge_by_bisection(va, vb, condition, eps, N)
1191
+ middle_ac = cut_edge_by_bisection(va, vc, condition, eps, N)
1192
+
1193
+ point_list += [middle_ac, middle_ab]
1194
+ face_list.append([index, old_index_to_index[old_a], index + 1])
1195
+ index += 2
1196
+
1197
+ if local_colored:
1198
+ return IndexFaceSet(face_list, point_list,
1199
+ texture_list=texture_list)
1200
+ else:
1201
+ opacity = texture.opacity
1202
+ return IndexFaceSet(face_list, point_list, texture=texture,
1203
+ opacity=opacity)
1204
+
1205
+ def tachyon_repr(self, render_params):
1206
+ """
1207
+ Return a tachyon object for ``self``.
1208
+
1209
+ EXAMPLES:
1210
+
1211
+ A basic test with a triangle::
1212
+
1213
+ sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)])
1214
+ sage: s = G.tachyon_repr(G.default_render_params()); s
1215
+ ['TRI V0 0 0 1 V1 1 1 1 V2 2 0 1', ...]
1216
+
1217
+ A simple colored one::
1218
+
1219
+ sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
1220
+ sage: from sage.plot.plot3d.texture import Texture
1221
+ sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
1222
+ sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
1223
+ sage: col = rainbow(10, 'rgbtuple')
1224
+ sage: t_list = [Texture(col[i]) for i in range(10)]
1225
+ sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
1226
+ sage: S.tachyon_repr(S.default_render_params())
1227
+ ['TRI V0 2 0 0 V1 1 0 1 V2 1 1 0',
1228
+ 'TEXTURE... AMBIENT 0.3 DIFFUSE 0.7 SPECULAR 0 OPACITY 1.0... COLOR 1 0 0 ... TEXFUNC 0',...]
1229
+ """
1230
+ cdef Transformation transform = render_params.transform
1231
+ lines = []
1232
+ cdef point_c P, Q, R
1233
+ cdef face_c face
1234
+ cdef Py_ssize_t i, k
1235
+ sig_on()
1236
+ for i in range(self.fcount):
1237
+ face = self._faces[i]
1238
+ if transform is not None:
1239
+ transform.transform_point_c(&P, self.vs[face.vertices[0]])
1240
+ transform.transform_point_c(&Q, self.vs[face.vertices[1]])
1241
+ transform.transform_point_c(&R, self.vs[face.vertices[2]])
1242
+ else:
1243
+ P = self.vs[face.vertices[0]]
1244
+ Q = self.vs[face.vertices[1]]
1245
+ R = self.vs[face.vertices[2]]
1246
+ PyList_Append(lines, format_tachyon_triangle(P, Q, R))
1247
+ if self.global_texture:
1248
+ PyList_Append(lines, self.texture.id)
1249
+ else:
1250
+ PyList_Append(lines, format_tachyon_texture(face.color))
1251
+ if face.n > 3:
1252
+ for k in range(3, face.n):
1253
+ Q = R
1254
+ if transform is not None:
1255
+ transform.transform_point_c(&R, self.vs[face.vertices[k]])
1256
+ else:
1257
+ R = self.vs[face.vertices[k]]
1258
+ PyList_Append(lines, format_tachyon_triangle(P, Q, R))
1259
+ if self.global_texture:
1260
+ PyList_Append(lines, self.texture.id)
1261
+ else:
1262
+ PyList_Append(lines, format_tachyon_texture(face.color))
1263
+ sig_off()
1264
+
1265
+ return lines
1266
+
1267
+ def json_repr(self, render_params):
1268
+ """
1269
+ Return a json representation for ``self``.
1270
+
1271
+ TESTS:
1272
+
1273
+ A basic test with a triangle::
1274
+
1275
+ sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)])
1276
+ sage: G.json_repr(G.default_render_params())
1277
+ ['{"vertices":[{"x":0,"y":0,"z":1},{"x":1,"y":1,"z":1},{"x":2,"y":0,"z":1}], "faces":[[0,1,2]], "color":"#0000ff", "opacity":1.0}']
1278
+
1279
+ A simple colored one::
1280
+
1281
+ sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
1282
+ sage: from sage.plot.plot3d.texture import Texture
1283
+ sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
1284
+ sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
1285
+ sage: col = rainbow(10, 'rgbtuple')
1286
+ sage: t_list=[Texture(col[i]) for i in range(10)]
1287
+ sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
1288
+ sage: S.json_repr(S.default_render_params())
1289
+ ['{"vertices":[{"x":2,"y":0,"z":0},..., "faceColors":["#ff0000","#ff9900","#cbff00","#33ff00"], "opacity":1.0}']
1290
+ """
1291
+ cdef Transformation transform = render_params.transform
1292
+ cdef point_c res
1293
+
1294
+ if transform is None:
1295
+ vertices_str = "[{}]".format(
1296
+ ",".join(format_json_vertex(self.vs[i])
1297
+ for i in range(self.vcount)))
1298
+ else:
1299
+ vertices_str = "["
1300
+ for i in range(self.vcount):
1301
+ transform.transform_point_c(&res, self.vs[i])
1302
+ if i > 0:
1303
+ vertices_str += ","
1304
+ vertices_str += format_json_vertex(res)
1305
+ vertices_str += "]"
1306
+
1307
+ faces_str = "[{}]".format(",".join(format_json_face(self._faces[i])
1308
+ for i in range(self.fcount)))
1309
+ opacity = float(self._extra_kwds.get('opacity', 1))
1310
+
1311
+ if self.global_texture:
1312
+ color_str = '"#{}"'.format(self.texture.hex_rgb())
1313
+ json = ['{{"vertices":{}, "faces":{}, "color":{}, "opacity":{}}}'.format(
1314
+ vertices_str, faces_str, color_str, opacity)]
1315
+ else:
1316
+ color_str = "[{}]".format(",".join('"{}"'.format(
1317
+ Color(self._faces[i].color.r,
1318
+ self._faces[i].color.g,
1319
+ self._faces[i].color.b).html_color())
1320
+ for i in range(self.fcount)))
1321
+ json = ['{{"vertices":{}, "faces":{}, "faceColors":{}, "opacity":{}}}'.format(
1322
+ vertices_str, faces_str, color_str, opacity)]
1323
+
1324
+ if 'render_order' in self._extra_kwds:
1325
+ renderOrder = self._extra_kwds.get('render_order')
1326
+ json[0] = json[0][:-1] + ', "renderOrder": {}}}'.format(renderOrder)
1327
+
1328
+ if self._extra_kwds.get('single_side'):
1329
+ json[0] = json[0][:-1] + ', "singleSide": true}'
1330
+
1331
+ if self._extra_kwds.get('threejs_flat_shading'):
1332
+ json[0] = json[0][:-1] + ', "useFlatShading": true}'
1333
+
1334
+ if self._extra_kwds.get('mesh'):
1335
+ json[0] = json[0][:-1] + ', "showMeshGrid": true}'
1336
+
1337
+ return json
1338
+
1339
+ def threejs_repr(self, render_params):
1340
+ r"""
1341
+ Return representation of the surface suitable for plotting with three.js.
1342
+
1343
+ EXAMPLES:
1344
+
1345
+ A simple triangle::
1346
+
1347
+ sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)])
1348
+ sage: G.threejs_repr(G.default_render_params())
1349
+ [('surface',
1350
+ {'color': '#0000ff',
1351
+ 'faces': [[0, 1, 2]],
1352
+ 'opacity': 1.0,
1353
+ 'vertices': [{'x': 0.0, 'y': 0.0, 'z': 1.0},
1354
+ {'x': 1.0, 'y': 1.0, 'z': 1.0},
1355
+ {'x': 2.0, 'y': 0.0, 'z': 1.0}]})]
1356
+
1357
+ The same but with more options applied::
1358
+
1359
+ sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)], color='red', opacity=0.5,
1360
+ ....: render_order=2, threejs_flat_shading=True,
1361
+ ....: single_side=True, mesh=True, thickness=10, depth_write=True)
1362
+ sage: G.threejs_repr(G.default_render_params())
1363
+ [('surface',
1364
+ {'color': '#ff0000',
1365
+ 'depthWrite': True,
1366
+ 'faces': [[0, 1, 2]],
1367
+ 'linewidth': 10.0,
1368
+ 'opacity': 0.5,
1369
+ 'renderOrder': 2.0,
1370
+ 'showMeshGrid': True,
1371
+ 'singleSide': True,
1372
+ 'useFlatShading': True,
1373
+ 'vertices': [{'x': 0.0, 'y': 0.0, 'z': 1.0},
1374
+ {'x': 1.0, 'y': 1.0, 'z': 1.0},
1375
+ {'x': 2.0, 'y': 0.0, 'z': 1.0}]})]
1376
+
1377
+ TESTS:
1378
+
1379
+ Transformations apply to the surface's vertices::
1380
+
1381
+ sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)]).scale(2,1,-1)
1382
+ sage: G.threejs_repr(G.default_render_params())
1383
+ [('surface',
1384
+ {'color': '#0000ff',
1385
+ 'faces': [[0, 1, 2]],
1386
+ 'opacity': 1.0,
1387
+ 'vertices': [{'x': 0.0, 'y': 0.0, 'z': -1.0},
1388
+ {'x': 2.0, 'y': 1.0, 'z': -1.0},
1389
+ {'x': 4.0, 'y': 0.0, 'z': -1.0}]})]
1390
+
1391
+ Per-face colors::
1392
+
1393
+ sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
1394
+ sage: from sage.plot.plot3d.texture import Texture
1395
+ sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
1396
+ sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
1397
+ sage: col = rainbow(10, 'rgbtuple')
1398
+ sage: t_list=[Texture(col[i]) for i in range(10)]
1399
+ sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
1400
+ sage: S.threejs_repr(S.default_render_params())
1401
+ [('surface',
1402
+ {'faceColors': ['#ff0000', '#ff9900', '#cbff00', '#33ff00'],
1403
+ 'faces': [[0, 4, 5], [3, 4, 5], [2, 3, 4], [1, 3, 5]],
1404
+ 'opacity': 1.0,
1405
+ 'vertices': [{'x': 2.0, 'y': 0.0, 'z': 0.0},
1406
+ {'x': 0.0, 'y': 2.0, 'z': 0.0},
1407
+ {'x': 0.0, 'y': 0.0, 'z': 2.0},
1408
+ {'x': 0.0, 'y': 1.0, 'z': 1.0},
1409
+ {'x': 1.0, 'y': 0.0, 'z': 1.0},
1410
+ {'x': 1.0, 'y': 1.0, 'z': 0.0}]})]
1411
+ """
1412
+ surface = {}
1413
+
1414
+ vertices = []
1415
+ cdef Transformation transform = render_params.transform
1416
+ cdef point_c res
1417
+ for i in range(self.vcount):
1418
+ if transform is None:
1419
+ res = self.vs[i]
1420
+ else:
1421
+ transform.transform_point_c(&res, self.vs[i])
1422
+ vertices.append(dict(x=float(res.x), y=float(res.y), z=float(res.z)))
1423
+ surface['vertices'] = vertices
1424
+
1425
+ faces = []
1426
+ cdef face_c face
1427
+ for i in range(self.fcount):
1428
+ face = self._faces[i]
1429
+ faces.append([int(face.vertices[j]) for j in range(face.n)])
1430
+ surface['faces'] = faces
1431
+
1432
+ if self.global_texture:
1433
+ surface['color'] = '#' + str(self.texture.hex_rgb())
1434
+ else:
1435
+ face_colors = []
1436
+ for i in range(self.fcount):
1437
+ face = self._faces[i]
1438
+ color = Color(face.color.r, face.color.g, face.color.b)
1439
+ face_colors.append(str(color.html_color()))
1440
+ surface['faceColors'] = face_colors
1441
+
1442
+ surface['opacity'] = float(self._extra_kwds.get('opacity', 1.0))
1443
+
1444
+ if 'render_order' in self._extra_kwds:
1445
+ surface['renderOrder'] = float(self._extra_kwds['render_order'])
1446
+
1447
+ if self._extra_kwds.get('single_side'):
1448
+ surface['singleSide'] = True
1449
+
1450
+ if self._extra_kwds.get('threejs_flat_shading'):
1451
+ surface['useFlatShading'] = True
1452
+
1453
+ if self._extra_kwds.get('mesh'):
1454
+ surface['showMeshGrid'] = True
1455
+
1456
+ if self._extra_kwds.get('thickness'):
1457
+ surface['linewidth'] = float(self._extra_kwds['thickness'])
1458
+
1459
+ if 'depth_write' in self._extra_kwds:
1460
+ surface['depthWrite'] = bool(self._extra_kwds['depth_write'])
1461
+
1462
+ return [('surface', surface)]
1463
+
1464
+ def obj_repr(self, render_params):
1465
+ """
1466
+ Return an obj representation for ``self``.
1467
+
1468
+ TESTS::
1469
+
1470
+ sage: from sage.plot.plot3d.shapes import *
1471
+ sage: S = Cylinder(1,1)
1472
+ sage: s = S.obj_repr(S.default_render_params())
1473
+ """
1474
+ cdef Transformation transform = render_params.transform
1475
+ cdef int off = render_params.obj_vertex_offset
1476
+ cdef Py_ssize_t i
1477
+ cdef point_c res
1478
+
1479
+ sig_on()
1480
+ if transform is None:
1481
+ points = [format_obj_vertex(self.vs[i]) for i in range(self.vcount)]
1482
+ else:
1483
+ points = []
1484
+ for i in range(self.vcount):
1485
+ transform.transform_point_c(&res, self.vs[i])
1486
+ PyList_Append(points, format_obj_vertex(res))
1487
+
1488
+ faces = [format_obj_face(self._faces[i], off) for i in range(self.fcount)]
1489
+ if not self.enclosed:
1490
+ back_faces = [format_obj_face_back(self._faces[i], off) for i in range(self.fcount)]
1491
+ else:
1492
+ back_faces = []
1493
+
1494
+ render_params.obj_vertex_offset += self.vcount
1495
+ sig_off()
1496
+
1497
+ return ["g " + render_params.unique_name('obj'),
1498
+ "usemtl " + self.texture.id,
1499
+ points,
1500
+ faces,
1501
+ back_faces]
1502
+
1503
+ def jmol_repr(self, render_params):
1504
+ """
1505
+ Return a jmol representation for ``self``.
1506
+
1507
+ TESTS::
1508
+
1509
+ sage: from sage.plot.plot3d.shapes import *
1510
+ sage: S = Cylinder(1,1)
1511
+ sage: S.show(viewer='jmol') # indirect doctest
1512
+ """
1513
+ cdef Transformation transform = render_params.transform
1514
+ cdef Py_ssize_t i
1515
+ cdef point_c res
1516
+
1517
+ self._separate_creases(render_params.crease_threshold)
1518
+
1519
+ sig_on()
1520
+ if transform is None:
1521
+ points = [format_pmesh_vertex(self.vs[i])
1522
+ for i in range(self.vcount)]
1523
+ else:
1524
+ points = []
1525
+ for i in range(self.vcount):
1526
+ transform.transform_point_c(&res, self.vs[i])
1527
+ PyList_Append(points, format_pmesh_vertex(res))
1528
+
1529
+ # activation of coloring in jmol
1530
+ if self.global_texture:
1531
+ faces = [format_pmesh_face(self._faces[i], 1)
1532
+ for i in range(self.fcount)]
1533
+ else:
1534
+ faces = [format_pmesh_face(self._faces[i], -1)
1535
+ for i in range(self.fcount)]
1536
+
1537
+ # If a face has more than 4 vertices, it gets chopped up in
1538
+ # format_pmesh_face
1539
+ cdef Py_ssize_t extra_faces = 0
1540
+ for i in range(self.fcount):
1541
+ if self._faces[i].n >= 5:
1542
+ extra_faces += self._faces[i].n-3
1543
+
1544
+ sig_off()
1545
+
1546
+ all = [str(self.vcount),
1547
+ points,
1548
+ str(self.fcount + extra_faces),
1549
+ faces]
1550
+
1551
+ from sage.plot.plot3d.base import flatten_list
1552
+ name = render_params.unique_name('obj')
1553
+ all = flatten_list(all)
1554
+ if render_params.output_archive:
1555
+ filename = "%s.pmesh" % (name)
1556
+ render_params.output_archive.writestr(filename, '\n'.join(all))
1557
+ else:
1558
+ filename = "%s-%s.pmesh" % (render_params.output_file, name)
1559
+ f = open(filename, 'w')
1560
+ for line in all:
1561
+ f.write(line)
1562
+ f.write('\n')
1563
+ f.close()
1564
+
1565
+ if self.global_texture:
1566
+ s = 'pmesh {} "{}"\n{}'.format(name, filename,
1567
+ self.texture.jmol_str("pmesh"))
1568
+ else:
1569
+ s = 'pmesh {} "{}"'.format(name, filename)
1570
+
1571
+ # Turn on display of the mesh lines or dots?
1572
+ if render_params.mesh:
1573
+ s += '\npmesh %s mesh\n' % name
1574
+ if render_params.dots:
1575
+ s += '\npmesh %s dots\n' % name
1576
+ return [s]
1577
+
1578
+ def stl_binary_repr(self, render_params):
1579
+ """
1580
+ Return data for STL (STereoLithography) representation of the surface.
1581
+
1582
+ The STL binary representation is a list of binary strings,
1583
+ one for each triangle.
1584
+
1585
+ EXAMPLES::
1586
+
1587
+ sage: G = sphere()
1588
+ sage: data = G.stl_binary_repr(G.default_render_params()); len(data)
1589
+ 1368
1590
+ """
1591
+ import struct
1592
+ from sage.modules.free_module import FreeModule
1593
+ RR3 = FreeModule(RDF, 3)
1594
+
1595
+ if hasattr(self, 'triangulate'):
1596
+ self.triangulate()
1597
+ faces = self.face_list(render_params)
1598
+ faces_iter = iter(faces)
1599
+
1600
+ def chopped_faces_iter():
1601
+ for face in faces_iter:
1602
+ n = len(face)
1603
+ if n == 3:
1604
+ yield face
1605
+ else:
1606
+ # naive cut into triangles
1607
+ v = face[-1]
1608
+ for i in range(n - 2):
1609
+ yield [v, face[i], face[i + 1]]
1610
+
1611
+ main_data = []
1612
+ for i, j, k in chopped_faces_iter():
1613
+ ij = RR3(j) - RR3(i)
1614
+ ik = RR3(k) - RR3(i)
1615
+ n = ij.cross_product(ik)
1616
+ n = n / n.norm()
1617
+ fill = struct.pack('H', 0)
1618
+ # 50 bytes per facet
1619
+ # 12 times 4 bytes (float) for n, i, j, k
1620
+ fill = b''.join(struct.pack('<f', x) for x in n)
1621
+ fill += b''.join(struct.pack('<f', x) for x in i)
1622
+ fill += b''.join(struct.pack('<f', x) for x in j)
1623
+ fill += b''.join(struct.pack('<f', x) for x in k)
1624
+ # plus 2 more bytes
1625
+ fill += b'00'
1626
+ main_data.append(fill)
1627
+
1628
+ return main_data
1629
+
1630
+ def dual(self, **kwds):
1631
+ """
1632
+ Return the dual.
1633
+
1634
+ EXAMPLES::
1635
+
1636
+ sage: S = cube()
1637
+ sage: T = S.dual()
1638
+ sage: len(T.vertex_list())
1639
+ 6
1640
+ """
1641
+ cdef face_c *face
1642
+ cdef Py_ssize_t i, j, ix, ff
1643
+ cdef IndexFaceSet dual = IndexFaceSet([], **kwds)
1644
+ cdef int incoming, outgoing
1645
+ cdef dict dd
1646
+
1647
+ dual.realloc(self.fcount, self.vcount, self.icount)
1648
+
1649
+ # is using dicts overly-heavy?
1650
+ dual_faces = [{} for i in range(self.vcount)]
1651
+
1652
+ for i in range(self.fcount):
1653
+ sig_check()
1654
+ # Let the vertex be centered on the face according to a simple average
1655
+ face = &self._faces[i]
1656
+ dual.vs[i] = self.vs[face.vertices[0]]
1657
+ for j in range(1, face.n):
1658
+ point_c_add(&dual.vs[i], dual.vs[i], self.vs[face.vertices[j]])
1659
+ point_c_mul(&dual.vs[i], dual.vs[i], 1.0/face.n)
1660
+
1661
+ # Now compute the new face
1662
+ for j in range(face.n):
1663
+ if j == 0:
1664
+ incoming = face.vertices[face.n - 1]
1665
+ else:
1666
+ incoming = face.vertices[j - 1]
1667
+ if j == face.n - 1:
1668
+ outgoing = face.vertices[0]
1669
+ else:
1670
+ outgoing = face.vertices[j + 1]
1671
+ dd = dual_faces[face.vertices[j]]
1672
+ dd[incoming] = i, outgoing
1673
+
1674
+ i = 0
1675
+ ix = 0
1676
+ for dd in dual_faces:
1677
+ sig_check()
1678
+ face = &dual._faces[i]
1679
+ face.n = len(dd)
1680
+ if face.n == 0: # skip unused vertices
1681
+ continue
1682
+ face.vertices = &dual.face_indices[ix]
1683
+ ff, next_ = next(iter(dd.itervalues()))
1684
+ face.vertices[0] = ff
1685
+ for j in range(1, face.n):
1686
+ ff, next_ = dd[next_]
1687
+ face.vertices[j] = ff
1688
+ i += 1
1689
+ ix += face.n
1690
+
1691
+ dual.vcount = self.fcount
1692
+ dual.fcount = i
1693
+ dual.icount = ix
1694
+
1695
+ return dual
1696
+
1697
+ def stickers(self, colors, width, hover):
1698
+ """
1699
+ Return a group of IndexFaceSets.
1700
+
1701
+ INPUT:
1702
+
1703
+ - ``colors`` -- list of colors/textures to use (in cyclic order)
1704
+
1705
+ - ``width`` -- offset perpendicular into the edge (to create a border)
1706
+ may also be negative
1707
+
1708
+ - ``hover`` -- offset normal to the face (usually have to float above
1709
+ the original surface so it shows, typically this value is very
1710
+ small compared to the actual object
1711
+
1712
+ OUTPUT: Graphics3dGroup of stickers
1713
+
1714
+ EXAMPLES::
1715
+
1716
+ sage: from sage.plot.plot3d.shapes import Box
1717
+ sage: B = Box(.5,.4,.3, color='black')
1718
+ sage: S = B.stickers(['red','yellow','blue'], 0.1, 0.05)
1719
+ sage: S.show()
1720
+ sage: (S+B).show()
1721
+ """
1722
+ all = []
1723
+ n = self.fcount
1724
+ ct = len(colors)
1725
+ for k in range(len(colors)):
1726
+ if colors[k]:
1727
+ all.append(self.sticker(list(range(k, n, ct)), width, hover,
1728
+ texture=colors[k]))
1729
+ return Graphics3dGroup(all)
1730
+
1731
+ def sticker(self, face_list, width, hover, **kwds):
1732
+ """
1733
+ Return a sticker on the chosen faces.
1734
+ """
1735
+ if not isinstance(face_list, (list, tuple)):
1736
+ face_list = (face_list,)
1737
+ faces = self.face_list()
1738
+ all = []
1739
+ for k in face_list:
1740
+ all.append(sticker(faces[k], width, hover))
1741
+ return IndexFaceSet(all, **kwds)
1742
+
1743
+
1744
+ cdef class FaceIter:
1745
+ """
1746
+ A class for iteration over faces
1747
+
1748
+ EXAMPLES::
1749
+
1750
+ sage: from sage.plot.plot3d.shapes import *
1751
+ sage: S = Box(1,2,3)
1752
+ sage: len(list(S.faces())) == 6 # indirect doctest
1753
+ True
1754
+ """
1755
+ def __init__(self, face_set):
1756
+ self.set = face_set
1757
+ self.i = 0
1758
+
1759
+ def __iter__(self):
1760
+ return self
1761
+
1762
+ def __next__(self):
1763
+ cdef point_c P
1764
+ if self.i >= self.set.fcount:
1765
+ raise StopIteration
1766
+ else:
1767
+ face = []
1768
+ for j in range(self.set._faces[self.i].n):
1769
+ P = self.set.vs[self.set._faces[self.i].vertices[j]]
1770
+ PyList_Append(face, (P.x, P.y, P.z))
1771
+ self.i += 1
1772
+ return face
1773
+
1774
+
1775
+ cdef class EdgeIter:
1776
+ """
1777
+ A class for iteration over edges
1778
+
1779
+ EXAMPLES::
1780
+
1781
+ sage: from sage.plot.plot3d.shapes import *
1782
+ sage: S = Box(1,2,3)
1783
+ sage: len(list(S.edges())) == 12 # indirect doctest
1784
+ True
1785
+ """
1786
+ def __init__(self, face_set):
1787
+ self.set = face_set
1788
+ if not self.set.enclosed:
1789
+ raise TypeError("must be closed to use the simple iterator")
1790
+ self.i = 0
1791
+ self.j = 0
1792
+ self.seen = {}
1793
+
1794
+ def __iter__(self):
1795
+ return self
1796
+
1797
+ def __next__(self):
1798
+ cdef point_c P, Q
1799
+ cdef face_c face = self.set._faces[self.i]
1800
+ while self.i < self.set.fcount:
1801
+ if self.j == face.n:
1802
+ self.i += 1
1803
+ self.j = 0
1804
+ if self.i < self.set.fcount:
1805
+ face = self.set._faces[self.i]
1806
+ else:
1807
+ if self.j == 0:
1808
+ P = self.set.vs[face.vertices[face.n - 1]]
1809
+ else:
1810
+ P = self.set.vs[face.vertices[self.j - 1]]
1811
+ Q = self.set.vs[face.vertices[self.j]]
1812
+ self.j += 1
1813
+ if self.set.enclosed: # Every edge appears exactly twice, once in each orientation.
1814
+ if point_c_cmp(P, Q) < 0:
1815
+ return ((P.x, P.y, P.z), (Q.x, Q.y, Q.z))
1816
+ else:
1817
+ if point_c_cmp(P, Q) > 0:
1818
+ P, Q = Q, P
1819
+ edge = ((P.x, P.y, P.z), (Q.x, Q.y, Q.z))
1820
+ if edge not in self.seen:
1821
+ self.seen[edge] = edge
1822
+ return edge
1823
+ raise StopIteration
1824
+
1825
+
1826
+ cdef class VertexIter:
1827
+ """
1828
+ A class for iteration over vertices
1829
+
1830
+ EXAMPLES::
1831
+
1832
+ sage: from sage.plot.plot3d.shapes import *
1833
+ sage: S = Box(1,2,3)
1834
+ sage: len(list(S.vertices())) == 8 # indirect doctest
1835
+ True
1836
+ """
1837
+ def __init__(self, face_set):
1838
+ self.set = face_set
1839
+ self.i = 0
1840
+
1841
+ def __iter__(self):
1842
+ return self
1843
+
1844
+ def __next__(self):
1845
+ if self.i >= self.set.vcount:
1846
+ raise StopIteration
1847
+ else:
1848
+ self.i += 1
1849
+ return (self.set.vs[self.i-1].x,
1850
+ self.set.vs[self.i-1].y,
1851
+ self.set.vs[self.i-1].z)
1852
+
1853
+
1854
+ def sticker(face, width, hover):
1855
+ """
1856
+ Return a sticker over the given face.
1857
+ """
1858
+ n = len(face)
1859
+ edges = []
1860
+ for i in range(n):
1861
+ edges.append(vector(RDF, [face[i - 1][0] - face[i][0],
1862
+ face[i - 1][1] - face[i][1],
1863
+ face[i - 1][2] - face[i][2]]))
1864
+ sticker = []
1865
+ for i in range(n):
1866
+ v = -edges[i]
1867
+ w = edges[i - 1]
1868
+ N = v.cross_product(w)
1869
+ lenN = N.norm()
1870
+ dv = v * (width * w.norm() / lenN)
1871
+ dw = w * (width * v.norm() / lenN)
1872
+ sticker.append(tuple(vector(RDF, face[i-1]) + dv + dw + N*(hover/lenN)))
1873
+ return sticker