passagemath-plot 10.6.31rc3__cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl

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

Potentially problematic release.


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

Files changed (81) hide show
  1. passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
  2. passagemath_plot-10.6.31rc3.dist-info/RECORD +81 -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-e1b7dfc8.so.5.0.0 +0 -0
  6. passagemath_plot.libs/libgsl-e3525837.so.28.0.0 +0 -0
  7. passagemath_plot.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
  8. sage/all__sagemath_plot.py +15 -0
  9. sage/ext_data/threejs/animation.css +195 -0
  10. sage/ext_data/threejs/animation.html +85 -0
  11. sage/ext_data/threejs/animation.js +273 -0
  12. sage/ext_data/threejs/fat_lines.js +48 -0
  13. sage/ext_data/threejs/threejs-version.txt +1 -0
  14. sage/ext_data/threejs/threejs_template.html +597 -0
  15. sage/interfaces/all__sagemath_plot.py +1 -0
  16. sage/interfaces/gnuplot.py +196 -0
  17. sage/interfaces/jmoldata.py +208 -0
  18. sage/interfaces/povray.py +56 -0
  19. sage/plot/all.py +42 -0
  20. sage/plot/animate.py +1796 -0
  21. sage/plot/arc.py +504 -0
  22. sage/plot/arrow.py +671 -0
  23. sage/plot/bar_chart.py +205 -0
  24. sage/plot/bezier_path.py +400 -0
  25. sage/plot/circle.py +435 -0
  26. sage/plot/colors.py +1606 -0
  27. sage/plot/complex_plot.cpython-314-aarch64-linux-gnu.so +0 -0
  28. sage/plot/complex_plot.pyx +1446 -0
  29. sage/plot/contour_plot.py +1792 -0
  30. sage/plot/density_plot.py +318 -0
  31. sage/plot/disk.py +373 -0
  32. sage/plot/ellipse.py +375 -0
  33. sage/plot/graphics.py +3580 -0
  34. sage/plot/histogram.py +354 -0
  35. sage/plot/hyperbolic_arc.py +404 -0
  36. sage/plot/hyperbolic_polygon.py +416 -0
  37. sage/plot/hyperbolic_regular_polygon.py +296 -0
  38. sage/plot/line.py +626 -0
  39. sage/plot/matrix_plot.py +629 -0
  40. sage/plot/misc.py +509 -0
  41. sage/plot/multigraphics.py +1294 -0
  42. sage/plot/plot.py +4183 -0
  43. sage/plot/plot3d/all.py +23 -0
  44. sage/plot/plot3d/base.cpython-314-aarch64-linux-gnu.so +0 -0
  45. sage/plot/plot3d/base.pxd +12 -0
  46. sage/plot/plot3d/base.pyx +3378 -0
  47. sage/plot/plot3d/implicit_plot3d.py +659 -0
  48. sage/plot/plot3d/implicit_surface.cpython-314-aarch64-linux-gnu.so +0 -0
  49. sage/plot/plot3d/implicit_surface.pyx +1453 -0
  50. sage/plot/plot3d/index_face_set.cpython-314-aarch64-linux-gnu.so +0 -0
  51. sage/plot/plot3d/index_face_set.pxd +32 -0
  52. sage/plot/plot3d/index_face_set.pyx +1873 -0
  53. sage/plot/plot3d/introduction.py +131 -0
  54. sage/plot/plot3d/list_plot3d.py +649 -0
  55. sage/plot/plot3d/parametric_plot3d.py +1130 -0
  56. sage/plot/plot3d/parametric_surface.cpython-314-aarch64-linux-gnu.so +0 -0
  57. sage/plot/plot3d/parametric_surface.pxd +12 -0
  58. sage/plot/plot3d/parametric_surface.pyx +893 -0
  59. sage/plot/plot3d/platonic.py +601 -0
  60. sage/plot/plot3d/plot3d.py +1442 -0
  61. sage/plot/plot3d/plot_field3d.py +162 -0
  62. sage/plot/plot3d/point_c.pxi +148 -0
  63. sage/plot/plot3d/revolution_plot3d.py +309 -0
  64. sage/plot/plot3d/shapes.cpython-314-aarch64-linux-gnu.so +0 -0
  65. sage/plot/plot3d/shapes.pxd +22 -0
  66. sage/plot/plot3d/shapes.pyx +1382 -0
  67. sage/plot/plot3d/shapes2.py +1512 -0
  68. sage/plot/plot3d/tachyon.py +1779 -0
  69. sage/plot/plot3d/texture.py +453 -0
  70. sage/plot/plot3d/transform.cpython-314-aarch64-linux-gnu.so +0 -0
  71. sage/plot/plot3d/transform.pxd +21 -0
  72. sage/plot/plot3d/transform.pyx +268 -0
  73. sage/plot/plot3d/tri_plot.py +589 -0
  74. sage/plot/plot_field.py +362 -0
  75. sage/plot/point.py +624 -0
  76. sage/plot/polygon.py +562 -0
  77. sage/plot/primitive.py +249 -0
  78. sage/plot/scatter_plot.py +199 -0
  79. sage/plot/step.py +85 -0
  80. sage/plot/streamline_plot.py +328 -0
  81. sage/plot/text.py +432 -0
@@ -0,0 +1,1453 @@
1
+ # sage_setup: distribution = sagemath-plot
2
+ # sage.doctest: needs sage.symbolic
3
+ r"""
4
+ Graphics 3D object for representing and triangulating isosurfaces
5
+
6
+ AUTHORS:
7
+
8
+ - Robert Hanson (2007): initial Java version, in Jmol.
9
+ - Carl Witty (2009-01): first Cython version.
10
+ - Bill Cauchois (2009): improvements for inclusion into Sage.
11
+ """
12
+
13
+ # ***************************************************************************
14
+ # Copyright (C) 2009 Carl Witty <Carl.Witty@gmail.com>
15
+ #
16
+ # Distributed under the terms of the GNU General Public License (GPL)
17
+ #
18
+ # This code is distributed in the hope that it will be useful,
19
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
+ # General Public License for more details.
22
+ #
23
+ # The full text of the GPL is available at:
24
+ #
25
+ # https://www.gnu.org/licenses/
26
+ # ***************************************************************************
27
+
28
+ # Pieces of this file are strongly based on the marching cubes
29
+ # implementation in Jmol located at src/org/jmol/jvxl/calc/MarchingCubes.java.
30
+ # The data tables are in fact directly copied from there.
31
+
32
+ #*****************************************************************************
33
+ # This copyright is inherited from MarchingCubes.java in the Jmol source code.
34
+ #
35
+ # * Copyright (C) 2007 Miguel, Bob, Jmol Development
36
+ # *
37
+ # * Contact: hansonr@stolaf.edu
38
+ # *
39
+ # * This library is free software; you can redistribute it and/or
40
+ # * modify it under the terms of the GNU Lesser General Public
41
+ # * License as published by the Free Software Foundation; either
42
+ # * version 2.1 of the License, or (at your option) any later version.
43
+ # *
44
+ # * This library is distributed in the hope that it will be useful,
45
+ # * but WITHOUT ANY WARRANTY; without even the implied warranty of
46
+ # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
47
+ # * Lesser General License for more details.
48
+ # *
49
+ # * You should have received a copy of the GNU Lesser General Public
50
+ # * License along with this library; if not, write to the Free Software
51
+ # * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
52
+ #*****************************************************************************
53
+
54
+ # There's a framework in here for computing multiple isosurfaces of a
55
+ # single function. Currently, it's only used for a single
56
+ # implicit_plot3d where contour=... is given a list, but it's ready to
57
+ # be extended. I think the best UI would be if animate(...) and
58
+ # show(...) had a prepass where they went through their rendering
59
+ # trees, found ImplicitSurface objects with the same function,
60
+ # bounding box, and plot_points (other parameters, such as contours,
61
+ # hole, color, would be allowed to be different),
62
+ # and arranged to have them all plotted simultaneously. These
63
+ # prepasses have not been written yet. Combining multiple
64
+ # ImplicitSurface plots would be particularly advantageous for animate(...),
65
+ # but for a big animation, watch out for memory usage.
66
+
67
+ # If you have a reasonably simple surface (not a space-filling fractal),
68
+ # then if n is your resolution, we have n^3 evaluations of the main
69
+ # function, about n^2 evaluations of auxiliary functions (hole, gradient,
70
+ # color), and output of size about n^2.
71
+
72
+ # With this in mind, we pay particular attention to optimizing the n^3
73
+ # function evaluations. (But keep in mind that n may be as small as 20
74
+ # or so, so we shouldn't ignore the efficiency of the n^2 parts.)
75
+
76
+ # Also, we make sure not to ever allocate O(n^3) memory; we do the
77
+ # computation a slice at a time to avoid this. (Jmol always allocates
78
+ # n^3 memory when it reads the JVXL file, but that might be on a different
79
+ # computer; Tachyon would only allocate memory proportional to the
80
+ # output size.)
81
+
82
+ cimport numpy as np
83
+ import numpy as np
84
+
85
+ from sage.plot.plot3d.transform cimport point_c, face_c, color_c, Transformation
86
+ from sage.plot.plot3d.index_face_set cimport IndexFaceSet
87
+ from sage.rings.real_double import RDF
88
+ from sage.plot.misc import setup_for_eval_on_grid
89
+ from sage.plot.colors import check_color_data
90
+
91
+ from libc.math cimport isnan
92
+
93
+ include "point_c.pxi"
94
+
95
+ # The default value for plot_points (a keyword argument to implicit_plot3d()),
96
+ # assumed when plot_points is set to "automatic".
97
+ DEFAULT_PLOT_POINTS = 40
98
+
99
+ cdef double nan = float(RDF('NaN'))
100
+
101
+ cdef inline bint marching_has_edge(double a, double b, double contour, double *f, bint *has_nan) noexcept:
102
+ if isnan(a) or isnan(b):
103
+ has_nan[0] = True
104
+ return False
105
+
106
+ has_nan[0] = False
107
+
108
+ if (a >= contour) == (b >= contour):
109
+ return False
110
+
111
+ f[0] = (contour - a) / (b - a)
112
+ return True
113
+
114
+ # Returns 0 or 1
115
+ cdef inline int marching_is_inside(double v, double contour) noexcept:
116
+ return isnan(v) or v < contour
117
+
118
+ cdef void interpolate_point_c(point_c *result, double frac, point_c *inputs) noexcept:
119
+ result[0].x = inputs[0].x + frac*(inputs[1].x - inputs[0].x)
120
+ result[0].y = inputs[0].y + frac*(inputs[1].y - inputs[0].y)
121
+ result[0].z = inputs[0].z + frac*(inputs[1].z - inputs[0].z)
122
+
123
+
124
+ cdef class VertexInfo:
125
+ # The point in "integer space"
126
+ cdef point_c pt
127
+
128
+ # The gradient at this point in "evaluation space"
129
+ cdef point_c gradient
130
+
131
+ # (R,G,B) color ; a color triple
132
+ cdef color_c color
133
+
134
+ # This point in "evaluation space"
135
+ cdef point_c eval_pt
136
+
137
+ cdef void update_eval_pt(self, point_c *eval_min, point_c *eval_scale) noexcept:
138
+ """
139
+ Use eval_min and eval_scale to transform self.pt into evaluation space
140
+ and store the result in self.eval_pt.
141
+ """
142
+ self.eval_pt.x = eval_min[0].x + eval_scale[0].x*self.pt.x
143
+ self.eval_pt.y = eval_min[0].y + eval_scale[0].y*self.pt.y
144
+ self.eval_pt.z = eval_min[0].z + eval_scale[0].z*self.pt.z
145
+
146
+ def __repr__(self):
147
+ """
148
+ TESTS::
149
+
150
+ sage: from sage.plot.plot3d.implicit_surface import VertexInfo
151
+ sage: VertexInfo()
152
+ <0.0, 0.0, 0.0>
153
+ """
154
+ return '<{}, {}, {}>'.format(self.pt.x, self.pt.y, self.pt.z)
155
+
156
+
157
+ cdef mk_VertexInfo(double x, double y, double z, point_c *eval_min, point_c *eval_scale):
158
+ cdef VertexInfo v
159
+ v = VertexInfo.__new__(VertexInfo)
160
+ v.pt.x = x
161
+ v.pt.y = y
162
+ v.pt.z = z
163
+
164
+ v.update_eval_pt(eval_min, eval_scale)
165
+
166
+ return v
167
+
168
+
169
+ cdef class MarchingCubes:
170
+ r"""
171
+ Handles marching cube rendering.
172
+
173
+ Protocol:
174
+
175
+ 1. Create the class.
176
+ 2. Call ``process_slice`` once for each X slice, from self.nx > x >= 0.
177
+ 3. Call ``finish()``, which returns a list of strings.
178
+
179
+ .. NOTE::
180
+
181
+ Actually, only 4 slices ever exist; the caller will re-use old
182
+ storage.
183
+ """
184
+
185
+ cdef readonly object xrange
186
+ cdef readonly object yrange
187
+ cdef readonly object zrange
188
+ cdef readonly double contour
189
+ cdef readonly int nx
190
+ cdef readonly int ny
191
+ cdef readonly int nz
192
+ cdef readonly Transformation transform
193
+ cdef readonly object region
194
+ cdef readonly object gradient
195
+ cdef readonly bint smooth
196
+ cdef readonly object color_function
197
+ cdef readonly object colormap
198
+ cdef readonly object results
199
+
200
+ # We deal with three coordinate systems. We do most of our work
201
+ # in an integral coordinate system where x ranges from
202
+ # 0 <= x < self.nx, etc.; we do function evaluations where
203
+ # self.xrange[0] <= x <= self.xrange[1], etc.; and output
204
+ # is in a third coordinate system.
205
+ # (Note that in "integer space", function evaluations of the main
206
+ # function happen on integer coordinates; but function evaluations
207
+ # of the auxiliary functions will have one non-integer coordinate.)
208
+
209
+ # These parameters convert from integer space to function-evaluation
210
+ # space: eval_coord = eval_min + int_coord * eval_scale
211
+ cdef point_c eval_min
212
+ cdef point_c eval_scale
213
+
214
+ # The componentwise reciprocal of eval_scale; just used to change
215
+ # some divisions into multiplications
216
+ cdef point_c eval_scale_inv
217
+
218
+ cdef point_c out_origin, out_plus_x, out_plus_y, out_plus_z
219
+
220
+ def __init__(self, xrange, yrange, zrange, contour, plot_points,
221
+ transform=None, region=None, gradient=None, smooth=True,
222
+ color_function=None, colormap=None):
223
+ """
224
+ TESTS:
225
+
226
+ Marching cubes is an abstract base class, you can't do anything with it::
227
+
228
+ sage: from sage.plot.plot3d.implicit_surface import MarchingCubes
229
+ sage: cube_marcher = MarchingCubes((0, 1), (0, 1), (0, 1), 1, (10, 10, 10))
230
+ """
231
+ self.xrange = xrange
232
+ self.yrange = yrange
233
+ self.zrange = zrange
234
+ self.contour = contour
235
+ self.nx = plot_points[0]
236
+ self.ny = plot_points[1]
237
+ self.nz = plot_points[2]
238
+ self.transform = transform
239
+ self.region = region
240
+ self.gradient = gradient
241
+ self.smooth = smooth
242
+
243
+ self.color_function = color_function
244
+ self.colormap = colormap
245
+
246
+ self.eval_min.x = xrange[0]
247
+ self.eval_scale.x = (xrange[1] - xrange[0]) / (self.nx - 1)
248
+ self.eval_min.y = yrange[0]
249
+ self.eval_scale.y = (yrange[1] - yrange[0]) / (self.ny - 1)
250
+ self.eval_min.z = zrange[0]
251
+ self.eval_scale.z = (zrange[1] - zrange[0]) / (self.nz - 1)
252
+ self.eval_scale_inv.x = 1/self.eval_scale.x
253
+ self.eval_scale_inv.y = 1/self.eval_scale.y
254
+ self.eval_scale_inv.z = 1/self.eval_scale.z
255
+
256
+ cdef point_c zero_pt, origin, plus_x, plus_y, plus_z
257
+ zero_pt.x = 0
258
+ zero_pt.y = 0
259
+ zero_pt.z = 0
260
+ origin = self.eval_min
261
+ plus_x = zero_pt
262
+ plus_x.x = self.eval_scale.x
263
+ plus_y = zero_pt
264
+ plus_y.y = self.eval_scale.y
265
+ plus_z = zero_pt
266
+ plus_z.z = self.eval_scale.z
267
+ if self.transform is not None:
268
+ self.transform.transform_point_c(&self.out_origin, origin)
269
+ self.transform.transform_point_c(&self.out_plus_x, plus_x)
270
+ self.transform.transform_point_c(&self.out_plus_y, plus_y)
271
+ self.transform.transform_point_c(&self.out_plus_z, plus_z)
272
+ else:
273
+ self.out_origin = origin
274
+ self.out_plus_x = plus_x
275
+ self.out_plus_y = plus_y
276
+ self.out_plus_z = plus_z
277
+
278
+ self.results = []
279
+
280
+ def finish(self):
281
+ """
282
+ Return the results of the marching cubes algorithm as a list.
283
+
284
+ The format is specific to the subclass implementing this
285
+ method.
286
+
287
+ TESTS:
288
+
289
+ By default, it returns an empty list::
290
+
291
+ sage: from sage.plot.plot3d.implicit_surface import MarchingCubes
292
+ sage: cube_marcher = MarchingCubes((0, 1), (0, 1), (0, 1), 1, (10, 10, 10), None)
293
+ sage: cube_marcher.finish()
294
+ []
295
+ """
296
+ return self.results
297
+
298
+
299
+ cdef class MarchingCubesTriangles(MarchingCubes):
300
+ """
301
+ A subclass of MarchingCubes that returns its results as a list of triangles,
302
+ including their vertices and normals (if ``smooth=True``).
303
+
304
+ And also their vertex colors if a vertex coloring function is given.
305
+ """
306
+
307
+ cdef readonly np.ndarray x_vertices
308
+ cdef readonly np.ndarray y_vertices
309
+ cdef readonly np.ndarray z_vertices
310
+
311
+ cdef readonly np.ndarray y_vertices_swapped
312
+ cdef readonly np.ndarray z_vertices_swapped
313
+
314
+ cdef readonly slices
315
+
316
+ def __init__(self, *args, **kwargs):
317
+ """
318
+ TESTS::
319
+
320
+ sage: from sage.plot.plot3d.implicit_surface import MarchingCubesTriangles
321
+ sage: cube_marcher = MarchingCubesTriangles((0, 1), (0, 1), (0, 1), 1, (10, 10, 10))
322
+ """
323
+
324
+ MarchingCubes.__init__(self, *args, **kwargs)
325
+
326
+ self.x_vertices = np.empty((self.ny, self.nz), dtype=object)
327
+ self.y_vertices = np.empty((2, self.ny-1, self.nz), dtype=object)
328
+ self.z_vertices = np.empty((2, self.ny, self.nz-1), dtype=object)
329
+
330
+ # Create new arrays that share the same underlying data, but
331
+ # have 0 and 1 reversed for the first coordinate.
332
+ self.y_vertices_swapped = self.y_vertices[::-1, ...]
333
+ self.z_vertices_swapped = self.z_vertices[::-1, ...]
334
+
335
+ self.slices = []
336
+
337
+ def process_slice(self, unsigned int x, np.ndarray slice):
338
+ """
339
+ Process a single slice of function evaluations at the specified `x`
340
+ coordinate.
341
+
342
+ EXAMPLES::
343
+
344
+ sage: from sage.plot.plot3d.implicit_surface import MarchingCubesTriangles
345
+ sage: import numpy as np
346
+ sage: cube_marcher = MarchingCubesTriangles((-2, 2), (-2, 2), (-2, 2), 4, (10,)*3, smooth=False)
347
+ sage: f = lambda x, y, z: x^2 + y^2 + z^2
348
+ sage: slices = np.zeros((10, 10, 10), dtype=np.double)
349
+ sage: for x in reversed(range(0, 10)):
350
+ ....: for y in range(0, 10):
351
+ ....: for z in range(0, 10):
352
+ ....: slices[x, y, z] = f(*[a * (4 / 9) -2 for a in (x, y, z)])
353
+ ....: cube_marcher.process_slice(x, slices[x, :, :])
354
+ sage: faces = cube_marcher.finish()
355
+ sage: faces[0][0]
356
+ {'x': 1.555555555555..., 'y': -1.111111111111..., 'z': -0.555555555555...}
357
+
358
+ We render the isosurface using IndexFaceSet::
359
+
360
+ sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
361
+ sage: IndexFaceSet([tuple((p['x'], p['y'], p['z']) for p in face) for face in faces])
362
+ Graphics3d Object
363
+ """
364
+ # We go to a lot of effort to avoid repeating computations.
365
+ # (I hope that the related bookkeeping is actually faster
366
+ # than repeating the computations, but I haven't tested.)
367
+
368
+ # We get evaluation points, one slice at a time. But we
369
+ # don't want to process slices of evaluation points;
370
+ # we want to process slices of cubes. (There is one fewer slice
371
+ # of cubes than there is of evaluation points.)
372
+
373
+ # Without any caching, we would repeat this for each cube:
374
+ # Find which vertices are inside/outside the surface.
375
+ # For edges which cross the surface, interpolate to
376
+ # find the exact crossing location; these will be the vertices
377
+ # of triangles.
378
+ # Compute the color and surface normal for each of these vertices.
379
+ # Assemble the vertices into triangles, using the "marching cubes"
380
+ # tables.
381
+
382
+ # The problem with this is that each vertex is actually shared
383
+ # among four neighbor cubes (except on the edge of the array),
384
+ # so we would be repeating work (potentially a lot of work, if
385
+ # the user-specified gradient or color routines are expensive)
386
+ # four times.
387
+
388
+ # So we cache the information associated with each vertex.
389
+ # Let's call an edge an X, Y, or Z edge depending on which
390
+ # axis it is parallel to; and a vertex is an X, Y, or Z
391
+ # vertex depending on which kind of edge it lies on.
392
+
393
+ # X vertices are shared between cubes that are all within a
394
+ # single cube slice. However, Y and Z vertices are shared
395
+ # between adjacent slices, so we need to keep those caches
396
+ # around. But we still need only two slices of vertex cache
397
+ # at a time (the two that are adjacent to the current cube slice).
398
+
399
+ # To reduce memory allocations, we allocate these caches at the
400
+ # beginning of the run (they are ndarrays of objects). We
401
+ # set a VertexInfo object in the cache when we first compute it;
402
+ # then we look in the cache when we need it for the other three
403
+ # cubes.
404
+
405
+ # The X vertex cache is a 2-D ndarray. The Y and Z vertex caches
406
+ # are 3-D, where the first (x) dimension is indexed by 0 or 1.
407
+
408
+ # The Cython buffer interface (that we use for fast access to
409
+ # ndarray's) has to reinitialize itself separately in each
410
+ # function. For this reason, we use fewer, bigger functions;
411
+ # in particular, we don't call any functions per-cube that
412
+ # require buffer access.
413
+
414
+ # To compute interpolated gradients using central differencing,
415
+ # we need up to 4 slices. Consider a gradient computed at
416
+ # an X vertex, between slices 1 and 2. This is interpolated
417
+ # between the gradient at slice 1 and the gradient at slice 2.
418
+ # Computing the gradient at slice 1 requires slices 0 and 2;
419
+ # computing the gradient at slice 2 requires slices 1 and 3.
420
+
421
+ # This means we need to queue up slices and process them
422
+ # in a somewhat strange order. This function only does
423
+ # the queuing, and then passes all the real work off to
424
+ # other functions.
425
+
426
+ self.slices = ([slice] + self.slices)[:4]
427
+
428
+ if len(self.slices) >= 2:
429
+ self._update_yz_vertices(x+1,
430
+ self.slices[0],
431
+ self.slices[1],
432
+ self.slices[2] if len(self.slices) >= 3 else None)
433
+
434
+ if len(self.slices) >= 3:
435
+ self._update_x_vertices(x+1,
436
+ self.slices[0],
437
+ self.slices[1],
438
+ self.slices[2],
439
+ self.slices[3] if len(self.slices) >= 4 else None)
440
+ self.process_cubes(self.slices[1], self.slices[2])
441
+
442
+ if x == 0:
443
+ self._update_yz_vertices(x,
444
+ None,
445
+ self.slices[0],
446
+ self.slices[1])
447
+
448
+ self._update_x_vertices(x,
449
+ None,
450
+ self.slices[0],
451
+ self.slices[1],
452
+ self.slices[2] if len(self.slices) >= 3 else None)
453
+
454
+ self.process_cubes(self.slices[0], self.slices[1])
455
+
456
+ cpdef _update_yz_vertices(self, int x, np.ndarray _prev, np.ndarray _cur, np.ndarray _next):
457
+ """
458
+ TESTS::
459
+
460
+ sage: from sage.plot.plot3d.implicit_surface import MarchingCubesTriangles
461
+ sage: import numpy as np
462
+ sage: cube_marcher = MarchingCubesTriangles((0, 1), (0, 1), (0, 1), 0, (2,)*3, smooth=False)
463
+ sage: prev_slice = next_slice = np.ones((2, 2), dtype=np.double)
464
+ sage: cur_slice = prev_slice.copy()
465
+ sage: cur_slice[0, 0] = -1 # Seed the slice data with an "interesting" value.
466
+ sage: cube_marcher._update_yz_vertices(1, prev_slice, cur_slice, next_slice)
467
+ sage: cube_marcher.z_vertices.tolist()
468
+ [[[<1.0, 0.0, 0.5>], [None]], [[None], [None]]]
469
+ sage: cube_marcher.y_vertices.tolist()
470
+ [[[<1.0, 0.5, 0.0>, None]], [[None, None]]]
471
+ sage: cube_marcher.x_vertices.any() # This shouldn't affect the X vertices.
472
+ ...
473
+ """
474
+ (self.y_vertices, self.y_vertices_swapped) = \
475
+ (self.y_vertices_swapped, self.y_vertices)
476
+ (self.z_vertices, self.z_vertices_swapped) = \
477
+ (self.z_vertices_swapped, self.z_vertices)
478
+
479
+ cdef bint has_prev = (_prev is not None)
480
+ cdef bint has_next = (_next is not None)
481
+
482
+ cdef np.ndarray[double, ndim=2] prev = _prev
483
+ cdef np.ndarray[double, ndim=2] cur = _cur
484
+ cdef np.ndarray[double, ndim=2] next = _next
485
+
486
+ cdef np.ndarray[object, ndim=2] y_vertices = self.y_vertices[0,...]
487
+ cdef np.ndarray[object, ndim=2] z_vertices = self.z_vertices[0,...]
488
+
489
+ cdef int ny = self.ny
490
+ cdef int nz = self.nz
491
+
492
+ cdef int y
493
+ cdef int z
494
+ cdef VertexInfo v
495
+ cdef double frac
496
+ cdef bint has_nan
497
+ cdef point_c gradients[2]
498
+ cdef int i
499
+ for y in range(ny - 1):
500
+ for z in range(nz):
501
+ if marching_has_edge(cur[y,z], cur[y+1,z], self.contour, &frac, &has_nan):
502
+ v = mk_VertexInfo(x, y+frac, z, &self.eval_min, &self.eval_scale)
503
+ if self.region is not None:
504
+ if not self.in_region(v):
505
+ y_vertices[y,z] = None
506
+ continue
507
+ if self.smooth:
508
+ # We must compute a gradient.
509
+ if self.gradient is not None:
510
+ self.apply_point_func(&v.gradient, self.gradient, v)
511
+ else:
512
+ # Use central differencing.
513
+ for i in range(2):
514
+ self.get_gradient(&gradients[i],
515
+ x, y+i, z,
516
+ cur[y+i,z],
517
+ prev[y+i,z] if has_prev else 0,
518
+ next[y+i,z] if has_next else 0,
519
+ cur[y+i-1,z] if y+i>0 else 0,
520
+ cur[y+i+1,z] if y+i<ny-1 else 0,
521
+ cur[y+i,z-1] if z>0 else 0,
522
+ cur[y+i,z+1] if z<nz-1 else 0)
523
+ interpolate_point_c(&v.gradient, frac, gradients)
524
+ if self.color_function is not None:
525
+ self.apply_color_func(&v.color, self.color_function,
526
+ self.colormap, v)
527
+ y_vertices[y,z] = <object>v
528
+ else:
529
+ y_vertices[y,z] = None
530
+
531
+ # OK, that updated the Y vertices. Now we do almost exactly
532
+ # the same thing to update Z vertices.
533
+ for y in range(ny):
534
+ for z in range(nz - 1):
535
+ if marching_has_edge(cur[y,z], cur[y,z+1], self.contour, &frac, &has_nan):
536
+ v = mk_VertexInfo(x, y, z+frac, &self.eval_min, &self.eval_scale)
537
+ if self.region is not None:
538
+ if not self.in_region(v):
539
+ z_vertices[y,z] = None
540
+ continue
541
+ if self.smooth:
542
+ # We must compute a gradient.
543
+ if self.gradient is not None:
544
+ self.apply_point_func(&v.gradient, self.gradient, v)
545
+ else:
546
+ # Use central differencing.
547
+ for i in range(2):
548
+ self.get_gradient(&gradients[i],
549
+ x, y, z+i,
550
+ cur[y,z+i],
551
+ prev[y,z+i] if has_prev else 0,
552
+ next[y,z+i] if has_next else 0,
553
+ cur[y-1,z+i] if y>0 else 0,
554
+ cur[y+1,z+i] if y<ny-1 else 0,
555
+ cur[y,z+i-1] if z+i>0 else 0,
556
+ cur[y,z+i+1] if z+i<nz-1 else 0)
557
+ interpolate_point_c(&v.gradient, frac, gradients)
558
+ if self.color_function is not None:
559
+ self.apply_color_func(&v.color, self.color_function,
560
+ self.colormap, v)
561
+ z_vertices[y,z] = <object>v
562
+ else:
563
+ z_vertices[y,z] = None
564
+
565
+ cpdef _update_x_vertices(self, int x, np.ndarray _prev, np.ndarray _left, np.ndarray _right, np.ndarray _next):
566
+ """
567
+ TESTS::
568
+
569
+ sage: from sage.plot.plot3d.implicit_surface import MarchingCubesTriangles
570
+ sage: import numpy as np
571
+ sage: cube_marcher = MarchingCubesTriangles((0, 1), (0, 1), (0, 1), 0, (4, 2, 2), smooth=False)
572
+ sage: prev_slice = right_slice = next_slice = np.ones((2, 2), dtype=np.double)
573
+ sage: left_slice = prev_slice.copy()
574
+ sage: left_slice[1, 1] = -1
575
+ sage: cube_marcher._update_x_vertices(1, prev_slice, left_slice, right_slice, next_slice)
576
+ sage: cube_marcher.x_vertices.tolist()
577
+ [[None, None], [None, <1.5, 1.0, 1.0>]]
578
+ sage: cube_marcher.y_vertices.any() or cube_marcher.z_vertices.any() # This shouldn't affect the Y or Z vertices.
579
+ ...
580
+ """
581
+ cdef bint has_prev = (_prev is not None)
582
+ cdef bint has_next = (_next is not None)
583
+
584
+ cdef np.ndarray[double, ndim=2] prev = _prev
585
+ cdef np.ndarray[double, ndim=2] left = _left
586
+ cdef np.ndarray[double, ndim=2] right = _right
587
+ cdef np.ndarray[double, ndim=2] next = _next
588
+
589
+ cdef np.ndarray[object, ndim=2] x_vertices = self.x_vertices
590
+
591
+ cdef int ny = self.ny
592
+ cdef int nz = self.nz
593
+
594
+ cdef int y
595
+ cdef int z
596
+
597
+ cdef VertexInfo v
598
+ cdef double frac
599
+ cdef bint has_nan
600
+ cdef point_c gradients[2]
601
+ for y in range(ny):
602
+ for z in range(nz):
603
+ if marching_has_edge(left[y,z], right[y,z], self.contour, &frac, &has_nan):
604
+ v = mk_VertexInfo(x+frac, y, z, &self.eval_min, &self.eval_scale)
605
+ if self.region is not None:
606
+ if not self.in_region(v):
607
+ x_vertices[y,z] = None
608
+ continue
609
+ if self.smooth:
610
+ # We must compute a gradient.
611
+ if self.gradient is not None:
612
+ self.apply_point_func(&v.gradient, self.gradient, v)
613
+ else:
614
+ # Use central differencing.
615
+ self.get_gradient(&gradients[0],
616
+ x, y, z,
617
+ left[y,z],
618
+ prev[y,z] if has_prev else 0,
619
+ right[y,z],
620
+ left[y-1,z] if y>0 else 0,
621
+ left[y+1,z] if y<ny-1 else 0,
622
+ left[y,z-1] if z>0 else 0,
623
+ left[y,z+1] if z<nz-1 else 0)
624
+ self.get_gradient(&gradients[1],
625
+ x, y, z,
626
+ right[y,z],
627
+ left[y,z],
628
+ next[y,z] if has_next else 0,
629
+ right[y-1,z] if y>0 else 0,
630
+ right[y+1,z] if y<ny-1 else 0,
631
+ right[y,z-1] if z>0 else 0,
632
+ right[y,z+1] if z<nz-1 else 0)
633
+ interpolate_point_c(&v.gradient, frac, gradients)
634
+ if self.color_function is not None:
635
+ self.apply_color_func(&v.color, self.color_function,
636
+ self.colormap, v)
637
+ x_vertices[y,z] = <object>v
638
+ else:
639
+ x_vertices[y,z] = None
640
+
641
+ cdef bint in_region(self, VertexInfo v) noexcept:
642
+ return (self.region(v.eval_pt.x, v.eval_pt.y, v.eval_pt.z) > 0)
643
+
644
+ cdef apply_point_func(self, point_c *pt, fn, VertexInfo v):
645
+ if isinstance(fn, tuple):
646
+ pt[0].x = fn[0](v.eval_pt.x, v.eval_pt.y, v.eval_pt.z)
647
+ pt[0].y = fn[1](v.eval_pt.x, v.eval_pt.y, v.eval_pt.z)
648
+ pt[0].z = fn[2](v.eval_pt.x, v.eval_pt.y, v.eval_pt.z)
649
+ else:
650
+ t = fn(v.eval_pt.x, v.eval_pt.y, v.eval_pt.z)
651
+ pt[0].x = t[0]
652
+ pt[0].y = t[1]
653
+ pt[0].z = t[2]
654
+
655
+ cdef apply_color_func(self, color_c *pt, fn, cm, VertexInfo v):
656
+ t = fn(v.eval_pt.x, v.eval_pt.y, v.eval_pt.z)
657
+ pt[0].r, pt[0].g, pt[0].b, _ = cm(t)
658
+
659
+ cdef get_gradient(self,
660
+ point_c *g,
661
+ int x, int y, int z,
662
+ double center,
663
+ double lx, double ux,
664
+ double ly, double uy,
665
+ double lz, double uz):
666
+ # What a mess! It would be much nicer-looking code to pass slices
667
+ # in here and do the subscripting in here. Unfortunately,
668
+ # that would also be much slower, because we'd have to re-initialize
669
+ # the Cython buffer interface on each call.
670
+
671
+ cdef double dx = ux - lx
672
+ cdef double dy = uy - ly
673
+ cdef double dz = uz - lz
674
+
675
+ cdef double gx = dx * self.eval_scale_inv.x
676
+ cdef double gy = dy * self.eval_scale_inv.y
677
+ cdef double gz = dz * self.eval_scale_inv.z
678
+
679
+ if x > 0 and x < self.nx - 1:
680
+ gx *= 0.5
681
+ if y > 0 and y < self.ny - 1:
682
+ gy *= 0.5
683
+ if z > 0 and z < self.nz - 1:
684
+ gz *= 0.5
685
+
686
+ g[0].x = gx
687
+ g[0].y = gy
688
+ g[0].z = gz
689
+
690
+ cpdef process_cubes(self, np.ndarray _left, np.ndarray _right):
691
+ """
692
+ TESTS::
693
+
694
+ sage: from sage.plot.plot3d.implicit_surface import MarchingCubesTriangles
695
+ sage: import numpy as np
696
+ sage: cube_marcher = MarchingCubesTriangles((0, 1), (0, 1), (0, 1), 0, (3, 2, 2), smooth=False)
697
+ sage: slices = [np.ones((2, 2), dtype=np.double) for i in range(3)]
698
+ sage: slices[0][1, 1] = -1
699
+ sage: cube_marcher._update_yz_vertices(0, None, slices[0], slices[1])
700
+ sage: cube_marcher._update_x_vertices(0, None, slices[0], slices[1], slices[2])
701
+ sage: cube_marcher.process_cubes(slices[0], slices[1])
702
+ sage: cube_marcher.finish()
703
+ [({'x': 0.0, 'y': 1.0, 'z': 0.5},
704
+ {'x': 0.25, 'y': 1.0, 'z': 1.0},
705
+ {'x': 0.0, 'y': 0.5, 'z': 1.0})]
706
+ """
707
+ cdef np.ndarray[double, ndim=2] left = _left
708
+ cdef np.ndarray[double, ndim=2] right = _right
709
+
710
+ cdef np.ndarray[object, ndim=2] x_vertices = self.x_vertices
711
+ cdef np.ndarray[object, ndim=3] y_vertices = self.y_vertices
712
+ cdef np.ndarray[object, ndim=3] z_vertices = self.z_vertices
713
+
714
+ cdef int ny = self.ny
715
+ cdef int nz = self.nz
716
+
717
+ cdef int y
718
+ cdef int z
719
+
720
+ # based on generateSurfaceData in MarchingCubes.java
721
+ cdef int insideMask
722
+
723
+ # Cool ASCII art from MarchingCubes.java:
724
+ # * Y
725
+ # * 4 --------4--------- 5
726
+ # * /| /|
727
+ # * / | / |
728
+ # * / | / |
729
+ # * 7 8 5 |
730
+ # * / | / 9
731
+ # * / | / |
732
+ # * 7 --------6--------- 6 |
733
+ # * | | | |
734
+ # * | 0 ---------0--|----- 1 X
735
+ # * | / | /
736
+ # * 11 / 10 /
737
+ # * | 3 | 1
738
+ # * | / | /
739
+ # * | / | /
740
+ # * 3 ---------2-------- 2
741
+ # * Z
742
+
743
+ # We see from the above that vertices are labeled 0 to 7, and
744
+ # edges are labeled 0 to 11.
745
+
746
+ cdef list all_vertex_info = [None] * 12
747
+ cdef tuple my_triangles
748
+
749
+ cdef int i
750
+
751
+ for y in range(ny - 1):
752
+ for z in range(nz - 1):
753
+ # For each vertex (0 to 7), set the corresponding bit
754
+ # of insideMask iff the vertex is inside the surface.
755
+ insideMask = 0
756
+ insideMask |= marching_is_inside(left[y,z], self.contour)<<0
757
+ insideMask |= marching_is_inside(right[y,z], self.contour)<<1
758
+ insideMask |= marching_is_inside(right[y,z+1], self.contour)<<2
759
+ insideMask |= marching_is_inside(left[y,z+1], self.contour)<<3
760
+ insideMask |= marching_is_inside(left[y+1,z], self.contour)<<4
761
+ insideMask |= marching_is_inside(right[y+1,z], self.contour)<<5
762
+ insideMask |= marching_is_inside(right[y+1,z+1], self.contour)<<6
763
+ insideMask |= marching_is_inside(left[y+1,z+1], self.contour)<<7
764
+
765
+ if insideMask == 0 or insideMask == 255:
766
+ continue
767
+
768
+ # OK, we have a cube on the surface. Copy all of the vertex
769
+ # info into an array for easier reference.
770
+
771
+ all_vertex_info[0] = x_vertices[y,z]
772
+ all_vertex_info[1] = z_vertices[1,y,z]
773
+ all_vertex_info[2] = x_vertices[y,z+1]
774
+ all_vertex_info[3] = z_vertices[0,y,z]
775
+ all_vertex_info[4] = x_vertices[y+1,z]
776
+ all_vertex_info[5] = z_vertices[1,y+1,z]
777
+ all_vertex_info[6] = x_vertices[y+1,z+1]
778
+ all_vertex_info[7] = z_vertices[0,y+1,z]
779
+ all_vertex_info[8] = y_vertices[0,y,z]
780
+ all_vertex_info[9] = y_vertices[1,y,z]
781
+ all_vertex_info[10]= y_vertices[1,y,z+1]
782
+ all_vertex_info[11]= y_vertices[0,y,z+1]
783
+
784
+ my_triangles = triangle_table2[insideMask]
785
+
786
+ for i in range(0, len(my_triangles), 4):
787
+ # In wireframe mode, my_triangles[i+3] specifies
788
+ # whether or not to draw the corresponding edge.
789
+ # See MarchingCubes.java for details.
790
+ self.add_triangle(all_vertex_info[my_triangles[i]],
791
+ all_vertex_info[my_triangles[i+1]],
792
+ all_vertex_info[my_triangles[i+2]])
793
+
794
+ cpdef add_triangle(self, VertexInfo v1, VertexInfo v2, VertexInfo v3):
795
+ """
796
+ Called when a new triangle is generated by the marching cubes algorithm
797
+ to update the results array.
798
+
799
+ TESTS::
800
+
801
+ sage: from sage.plot.plot3d.implicit_surface import MarchingCubesTriangles, VertexInfo
802
+ sage: cube_marcher = MarchingCubesTriangles((0, 1), (0, 1), (0, 1), 0, (10,)*3, smooth=False)
803
+ sage: cube_marcher.add_triangle(VertexInfo(), VertexInfo(), VertexInfo())
804
+ sage: cube_marcher.finish()
805
+ [({'x': 0.0, 'y': 0.0, 'z': 0.0},
806
+ {'x': 0.0, 'y': 0.0, 'z': 0.0},
807
+ {'x': 0.0, 'y': 0.0, 'z': 0.0})]
808
+ """
809
+ if v1 is None or v2 is None or v3 is None:
810
+ # This happens if there is a NaN nearby, or if a hole was
811
+ # specified here.
812
+ return
813
+
814
+ cdef:
815
+ point_c v1_ev_pt, v2_ev_pt, v3_ev_pt
816
+ point_c n1_ev_vec, n2_ev_vec, n3_ev_vec
817
+ color_c v1_col, v2_col, v3_col
818
+
819
+ if self.transform is not None:
820
+ self.transform.transform_point_c(&v1_ev_pt, v1.eval_pt)
821
+ self.transform.transform_point_c(&v2_ev_pt, v2.eval_pt)
822
+ self.transform.transform_point_c(&v3_ev_pt, v3.eval_pt)
823
+ else:
824
+ v1_ev_pt = v1.eval_pt
825
+ v2_ev_pt = v2.eval_pt
826
+ v3_ev_pt = v3.eval_pt
827
+
828
+ face = (v1_ev_pt, v2_ev_pt, v3_ev_pt)
829
+
830
+ if self.color_function is not None:
831
+ v1_col = v1.color
832
+ v2_col = v2.color
833
+ v3_col = v3.color
834
+ face += (v1_col, v2_col, v3_col)
835
+
836
+ if self.smooth:
837
+ # XXX I believe this is wrong for non-uniform transforms
838
+ if self.transform is not None:
839
+ self.transform.transform_vector_c(&n1_ev_vec, v1.gradient)
840
+ self.transform.transform_vector_c(&n2_ev_vec, v2.gradient)
841
+ self.transform.transform_vector_c(&n3_ev_vec, v3.gradient)
842
+ else:
843
+ n1_ev_vec = v1.gradient
844
+ n2_ev_vec = v2.gradient
845
+ n3_ev_vec = v3.gradient
846
+ face += (n1_ev_vec, n2_ev_vec, n3_ev_vec)
847
+
848
+ self.results.append(face)
849
+
850
+
851
+ cpdef render_implicit(f, xrange, yrange, zrange, plot_points, cube_marchers):
852
+ """
853
+ INPUT:
854
+
855
+ - ``f`` -- a (fast!) callable function
856
+
857
+ - ``xrange`` -- a 2-tuple (x_min, x_max)
858
+
859
+ - ``yrange`` -- a 2-tuple (y_min, y_may)
860
+
861
+ - ``zrange`` -- a 2-tuple (z_min, z_maz)
862
+
863
+ - ``plot_points`` -- a triple of integers indicating the number of
864
+ function evaluations in each direction
865
+
866
+ - ``cube_marchers`` -- list of cube marchers, one for each contour
867
+
868
+ OUTPUT:
869
+
870
+ A representation of the isosurface, in the format specified by the
871
+ individual cube marchers.
872
+
873
+ TESTS::
874
+
875
+ sage: from sage.plot.plot3d.implicit_surface import render_implicit, MarchingCubesTriangles
876
+ sage: plot_points, f = (40,)*3, lambda x, y, z: x + y + z
877
+ sage: cube_marcher = MarchingCubesTriangles((0, 1), (0, 1), (0, 1), 1, (10,)*3)
878
+ sage: results = render_implicit(lambda x, y, z: x + y + z,
879
+ ....: (0, 1), (0, 1), (0, 1), (10,)*3, [cube_marcher])
880
+ sage: results[0][0]
881
+ {'x': 1.0, 'y': 0.0, 'z': 0.0}
882
+ """
883
+
884
+ cdef int nx = plot_points[0]
885
+ cdef int ny = plot_points[1]
886
+ cdef int nz = plot_points[2]
887
+
888
+ cdef point_c eval_min, eval_scale
889
+
890
+ eval_min.x = xrange[0]
891
+ eval_scale.x = (xrange[1] - xrange[0]) / (nx - 1)
892
+ eval_min.y = yrange[0]
893
+ eval_scale.y = (yrange[1] - yrange[0]) / (ny - 1)
894
+ eval_min.z = zrange[0]
895
+ eval_scale.z = (zrange[1] - zrange[0]) / (nz - 1)
896
+
897
+ # A possible future extension would be to allow passing in "f"
898
+ # as a numpy ndarray. If that were done, we could slightly modify
899
+ # the following code to just pass slices of f to the renderers
900
+ # (no copying of the data would be required).
901
+
902
+ # The current marching cube renderers need only at most four slices at
903
+ # a time.
904
+
905
+ cdef np.ndarray[double, ndim=3] data = np.zeros((4, ny, nz), dtype=np.double)
906
+ cdef np.ndarray[double, ndim=2] slice
907
+
908
+ cdef unsigned int x, y, z
909
+ cdef int n
910
+
911
+ cdef double eval_x, eval_y, eval_z
912
+
913
+ cdef MarchingCubes marcher
914
+
915
+ for n in range(nx):
916
+ x = nx-1-n
917
+ eval_x = eval_min.x + eval_scale.x * x
918
+ slice = data[n % 4, :, :]
919
+ for y in range(ny):
920
+ eval_y = eval_min.y + eval_scale.y * y
921
+ for z in range(nz):
922
+ eval_z = eval_min.z + eval_scale.z * z
923
+ slice[y, z] = f(eval_x, eval_y, eval_z)
924
+
925
+ for marcher in cube_marchers:
926
+ marcher.process_slice(x, slice)
927
+
928
+ results = []
929
+
930
+ for marcher in cube_marchers:
931
+ results.extend(marcher.finish())
932
+
933
+ return results
934
+
935
+
936
+ cdef class ImplicitSurface(IndexFaceSet):
937
+ cdef readonly object f
938
+ cdef readonly object vars
939
+ cdef readonly tuple xrange
940
+ cdef readonly tuple yrange
941
+ cdef readonly tuple zrange
942
+ cdef readonly list contours
943
+ cdef readonly object region
944
+ cdef readonly bint smooth
945
+ cdef readonly object gradient
946
+ cdef readonly object color_function
947
+ cdef readonly object colormap
948
+ cdef readonly tuple plot_points
949
+
950
+ def __init__(self, f, xrange, yrange, zrange,
951
+ contour=0, plot_points='automatic',
952
+ region=None, smooth=True, gradient=None,
953
+ **kwds):
954
+ """
955
+ TESTS::
956
+
957
+ sage: from sage.plot.plot3d.implicit_surface import ImplicitSurface
958
+ sage: var('x,y,z')
959
+ (x, y, z)
960
+ sage: G = ImplicitSurface(x^2 + y^2 + z^2, (x,-2, 2), (y,-2, 2), (z,-2, 2), contour=4)
961
+ sage: show(G)
962
+
963
+ A colored case::
964
+
965
+ sage: t = (1-sin(2*x*y+3*z)**2).function(x,y,z)
966
+ sage: cm = colormaps.autumn
967
+ sage: G = ImplicitSurface(x^2 + y^2 + z^2, (x,-2, 2), (y,-2, 2), (z,-2, 2), contour=4, color=(t,cm))
968
+ sage: G.show(viewer='tachyon')
969
+ """
970
+ self._extra_kwds = kwds
971
+ color_data = None
972
+ if 'color' in kwds:
973
+ try:
974
+ if len(kwds['color']) == 2 and callable(kwds['color'][0]):
975
+ color_data = kwds['color']
976
+ kwds.pop('color')
977
+ except (TypeError, AttributeError):
978
+ pass
979
+ if color_data is None:
980
+ # case of a global color
981
+ self.color_function = None
982
+ IndexFaceSet.__init__(self, [], [], **kwds)
983
+ else:
984
+ # case of a color depending on parameters
985
+ cf, cm = check_color_data(color_data)
986
+ self.color_function = cf
987
+ self.colormap = cm
988
+ IndexFaceSet.__init__(self, [], [], texture_list=[], **kwds)
989
+ from sage.ext.fast_eval import fast_float
990
+
991
+ orig_f = f
992
+ self.f, ranges, self.vars = setup_for_eval_on_grid(f, [xrange, yrange, zrange], return_vars=True)
993
+ self.xrange = ranges[0][:2]
994
+ self.yrange = ranges[1][:2]
995
+ self.zrange = ranges[2][:2]
996
+ if isinstance(contour, (list, tuple)):
997
+ contours = contour
998
+ else:
999
+ contours = [contour]
1000
+ self.contours = [float(c) for c in contours]
1001
+ if region is not None:
1002
+ self.region = fast_float(region, *self.vars)
1003
+
1004
+ # Comments from Carl Witty, who first wrote this some of this code
1005
+ # See Issue 9483
1006
+ # When I first wrote the code, I had the idea to create a
1007
+ # direct-to-tachyon backend that would use vertex normals
1008
+ # to create much nicer-looking plots with smaller numbers
1009
+ # of plot_points, and laid the groundwork for this backend
1010
+ # with the gradient and smooth arguments. But I abandoned the
1011
+ # code without writing this backend (and leaving many other parts
1012
+ # of the code unfinished).
1013
+ # When William Cauchois took over and finished the code (thank you,
1014
+ # William!), he only wrote an IndexFaceSet backend, that can't
1015
+ # (currently) make use of vertex normals. So the gradient code is
1016
+ # currently useless.
1017
+ # But it's still open for somebody to write a direct-to-tachyon backend,
1018
+ # or to extend IndexFaceSet to support vertex normals.
1019
+
1020
+ # Since IndexFaceSet doesn't even support smooth shading, we overwrite
1021
+ # the passed-in smooth parameter.
1022
+ smooth=False
1023
+
1024
+ self.smooth = smooth
1025
+ if smooth and gradient is None:
1026
+ try:
1027
+ gradient = (orig_f.diff(self.vars[0]),
1028
+ orig_f.diff(self.vars[1]),
1029
+ orig_f.diff(self.vars[2]))
1030
+ except Exception:
1031
+ # Would be nice to have more nuanced error handling here.
1032
+
1033
+ # If anything goes wrong, we'll just use central differencing.
1034
+ pass
1035
+ if gradient is not None:
1036
+ self.gradient = fast_float(gradient, *self.vars)
1037
+ if self.color_function is not None:
1038
+ self.color_function = fast_float(self.color_function, *self.vars)
1039
+ if plot_points == "automatic":
1040
+ plot_points = DEFAULT_PLOT_POINTS
1041
+ my_plot_points = []
1042
+ for i in range(3):
1043
+ if isinstance(plot_points, (list, tuple)):
1044
+ n = int(plot_points[i])
1045
+ else:
1046
+ n = int(plot_points)
1047
+ if n < 2:
1048
+ raise ValueError
1049
+ my_plot_points.append(n)
1050
+ self.plot_points = tuple(my_plot_points)
1051
+
1052
+ def bounding_box(self):
1053
+ """
1054
+ Return a bounding box for the ``ImplicitSurface``, as a tuple of two
1055
+ 3-dimensional points.
1056
+
1057
+ EXAMPLES:
1058
+
1059
+ Note that the bounding box corresponds exactly to the x-, y-, and z- range::
1060
+
1061
+ sage: from sage.plot.plot3d.implicit_surface import ImplicitSurface
1062
+ sage: G = ImplicitSurface(0, (0, 1), (0, 1), (0, 1))
1063
+ sage: G.bounding_box()
1064
+ ((0.0, 0.0, 0.0), (1.0, 1.0, 1.0))
1065
+ """
1066
+ return ((self.xrange[0], self.yrange[0], self.zrange[0]),
1067
+ (self.xrange[1], self.yrange[1], self.zrange[1]))
1068
+
1069
+ def obj_repr(self, render_params):
1070
+ """
1071
+ Return a representation of this object in the .obj format.
1072
+
1073
+ TESTS:
1074
+
1075
+ We graph a simple plane::
1076
+
1077
+ sage: from sage.plot.plot3d.implicit_surface import ImplicitSurface
1078
+ sage: var('x,y,z')
1079
+ (x, y, z)
1080
+ sage: G = ImplicitSurface(x + y + z, (x,-1, 1), (y,-1, 1), (z,-1, 1))
1081
+ sage: obj = G.obj_repr(G.default_render_params())
1082
+ sage: vertices = obj[2]
1083
+
1084
+ The number of vertices in the OBJ representation should equal the number
1085
+ of vertices in the face set::
1086
+
1087
+ sage: len(vertices) == len(G.vertex_list())
1088
+ True
1089
+
1090
+ The vertices in the OBJ representation should also be approximately equal
1091
+ to the vertices in the face set -- the small error is due to rounding
1092
+ which occurs during output (we test only the first 20 points for the
1093
+ sake of speed)::
1094
+
1095
+ sage: def points_equal(a, b, epsilon=(1e-5)):
1096
+ ....: return all(abs(x0-x1) < epsilon for x0, x1 in zip(a, b))
1097
+ sage: checklist = []
1098
+ sage: assert len(vertices) >= 20 # I should hope so, we're rendering at the default resolution!
1099
+ sage: for vertex, surf_vertex in list(zip(vertices, G.vertex_list()))[0:20]:
1100
+ ....: checklist.append(points_equal(map(float, vertex.split(' ')[1:]), surf_vertex))
1101
+ sage: all(checklist)
1102
+ True
1103
+ """
1104
+ self.triangulate()
1105
+ return IndexFaceSet.obj_repr(self, render_params)
1106
+
1107
+ def tachyon_repr(self, render_params):
1108
+ """
1109
+ Return a representation of this object suitable for use with the Tachyon
1110
+ renderer.
1111
+
1112
+ TESTS::
1113
+
1114
+ sage: from sage.plot.plot3d.implicit_surface import ImplicitSurface
1115
+ sage: var('x,y,z')
1116
+ (x, y, z)
1117
+ sage: G = ImplicitSurface(x + y + z, (x,-1, 1), (y,-1, 1), (z,-1, 1))
1118
+ sage: G.tachyon_repr(G.default_render_params())[0].startswith('TRI')
1119
+ True
1120
+ """
1121
+ self.triangulate()
1122
+ return IndexFaceSet.tachyon_repr(self, render_params)
1123
+
1124
+ def jmol_repr(self, render_params):
1125
+ """
1126
+ Return a representation of this object suitable for use with the Jmol
1127
+ renderer.
1128
+
1129
+ TESTS::
1130
+
1131
+ sage: from sage.plot.plot3d.implicit_surface import ImplicitSurface
1132
+ sage: var('x,y,z')
1133
+ (x, y, z)
1134
+ sage: G = ImplicitSurface(x + y + z, (x,-1, 1), (y,-1, 1), (z,-1, 1))
1135
+ sage: show(G, viewer='jmol') # indirect doctest
1136
+ """
1137
+ self.triangulate()
1138
+ return IndexFaceSet.jmol_repr(self, render_params)
1139
+
1140
+ def json_repr(self, render_params):
1141
+ """
1142
+ Return a representation of this object in JavaScript Object Notation (JSON).
1143
+
1144
+ TESTS::
1145
+
1146
+ sage: from sage.plot.plot3d.implicit_surface import ImplicitSurface
1147
+ sage: var('x,y,z')
1148
+ (x, y, z)
1149
+ sage: G = ImplicitSurface(x + y + z, (x,-1, 1), (y,-1, 1), (z,-1, 1))
1150
+ sage: G.json_repr(G.default_render_params())[0].startswith('{"vertices":')
1151
+ True
1152
+ """
1153
+ self.triangulate()
1154
+ return IndexFaceSet.json_repr(self, render_params)
1155
+
1156
+ def threejs_repr(self, render_params):
1157
+ r"""
1158
+ Return a representation of the surface suitable for plotting with three.js.
1159
+
1160
+ EXAMPLES::
1161
+
1162
+ sage: from sage.plot.plot3d.implicit_surface import ImplicitSurface
1163
+ sage: _ = var('x,y,z')
1164
+ sage: G = ImplicitSurface(x + y + z, (x,-1, 1), (y,-1, 1), (z,-1, 1))
1165
+ sage: G.threejs_repr(G.default_render_params())
1166
+ [('surface',
1167
+ {'color': '#6666ff',
1168
+ 'faces': [[0, 1, 2],
1169
+ ...
1170
+ 'opacity': 1.0,
1171
+ 'vertices': [{'x': ...,
1172
+ 'y': -0.9743589743589...,
1173
+ 'z': -0.02564102564102...},
1174
+ ...
1175
+ {'x': -1.0, 'y': 0.9487179487179..., 'z': 0.05128205128205...}]})]
1176
+ """
1177
+ self.triangulate()
1178
+ return IndexFaceSet.threejs_repr(self, render_params)
1179
+
1180
+ def triangulate(self, force=False):
1181
+ """
1182
+ The IndexFaceSet will be empty until you call this method,
1183
+ which generates the faces and vertices according to the
1184
+ parameters specified in the constructor for ImplicitSurface.
1185
+
1186
+ Note that if you call this method more than once, subsequent
1187
+ invocations will have no effect (this is an optimization to
1188
+ avoid repeated work) unless you specify ``force=True`` in the
1189
+ keywords.
1190
+
1191
+ EXAMPLES::
1192
+
1193
+ sage: from sage.plot.plot3d.implicit_surface import ImplicitSurface
1194
+ sage: var('x,y,z')
1195
+ (x, y, z)
1196
+ sage: G = ImplicitSurface(x + y + z, (x,-1, 1), (y,-1, 1), (z,-1, 1))
1197
+ sage: len(G.vertex_list()), len(G.face_list())
1198
+ (0, 0)
1199
+ sage: G.triangulate()
1200
+ sage: len(G.vertex_list()) > 0, len(G.face_list()) > 0
1201
+ (True, True)
1202
+ sage: G.show() # This should be fast, since the mesh is already triangulated.
1203
+ """
1204
+ if self.fcount != 0 and not force:
1205
+ # The mesh is already triangulated
1206
+ return
1207
+
1208
+ options = dict(xrange=self.xrange, yrange=self.yrange, zrange=self.zrange,
1209
+ region=self.region, smooth=self.smooth,
1210
+ gradient=self.gradient,
1211
+ color_function=self.color_function,
1212
+ colormap=self.colormap,
1213
+ plot_points=self.plot_points)
1214
+ cube_marchers = [MarchingCubesTriangles(contour=x, **options) for x in self.contours]
1215
+ results = render_implicit(self.f, self.xrange, self.yrange, self.zrange,
1216
+ self.plot_points, cube_marchers)
1217
+ cdef:
1218
+ face_c* dest_face
1219
+ point_c* dest_vertex
1220
+ int fcount = len(results)
1221
+
1222
+ self.realloc(fcount * 3, fcount, fcount * 3)
1223
+ for i in range(fcount):
1224
+ dest_face = &self._faces[i]
1225
+ src_face = results[i]
1226
+
1227
+ dest_face.n = 3
1228
+ dest_face.vertices = &self.face_indices[3 * i]
1229
+
1230
+ if self.color_function is not None:
1231
+ clist = [(src_face[j]['r'],
1232
+ src_face[j]['g'],
1233
+ src_face[j]['b'])
1234
+ for j in range(3, 6)]
1235
+ ct = (sum(clist[j][i] for j in range(3)) / 3 for i in range(3))
1236
+ # ct is the mean of the colors of vertices
1237
+ dest_face.color.r, dest_face.color.g, dest_face.color.b = ct
1238
+
1239
+ for j in range(3):
1240
+ dest_face.vertices[j] = (3 * i) + j
1241
+ dest_vertex = &self.vs[(3 * i) + j]
1242
+ dest_vertex.x = src_face[j]['x']
1243
+ dest_vertex.y = src_face[j]['y']
1244
+ dest_vertex.z = src_face[j]['z']
1245
+
1246
+
1247
+ # Data table (courtesy of MarchingCubes.java)
1248
+ triangle_table2 = ( None,
1249
+ ( 0, 8, 3, 7 ),
1250
+ ( 0, 1, 9, 7 ), ( 1, 8, 3, 6, 9, 8, 1, 5 ), ( 1, 2, 10, 7 ),
1251
+ ( 0, 8, 3, 7, 1, 2, 10, 7 ), ( 9, 2, 10, 6, 0, 2, 9, 5 ),
1252
+ ( 2, 8, 3, 6, 2, 10, 8, 1, 10, 9, 8, 3 ), ( 3, 11, 2, 7 ),
1253
+ ( 0, 11, 2, 6, 8, 11, 0, 5 ), ( 1, 9, 0, 7, 2, 3, 11, 7 ),
1254
+ ( 1, 11, 2, 6, 1, 9, 11, 1, 9, 8, 11, 3 ), ( 3, 10, 1, 6, 11, 10, 3, 5 ),
1255
+ ( 0, 10, 1, 6, 0, 8, 10, 1, 8, 11, 10, 3 ),
1256
+ ( 3, 9, 0, 6, 3, 11, 9, 1, 11, 10, 9, 3 ), ( 9, 8, 10, 5, 10, 8, 11, 6 ),
1257
+ ( 4, 7, 8, 7 ), ( 4, 3, 0, 6, 7, 3, 4, 5 ), ( 0, 1, 9, 7, 8, 4, 7, 7 ),
1258
+ ( 4, 1, 9, 6, 4, 7, 1, 1, 7, 3, 1, 3 ), ( 1, 2, 10, 7, 8, 4, 7, 7 ),
1259
+ ( 3, 4, 7, 6, 3, 0, 4, 3, 1, 2, 10, 7 ),
1260
+ ( 9, 2, 10, 6, 9, 0, 2, 3, 8, 4, 7, 7 ),
1261
+ ( 2, 10, 9, 3, 2, 9, 7, 0, 2, 7, 3, 6, 7, 9, 4, 6 ),
1262
+ ( 8, 4, 7, 7, 3, 11, 2, 7 ), ( 11, 4, 7, 6, 11, 2, 4, 1, 2, 0, 4, 3 ),
1263
+ ( 9, 0, 1, 7, 8, 4, 7, 7, 2, 3, 11, 7 ),
1264
+ ( 4, 7, 11, 3, 9, 4, 11, 1, 9, 11, 2, 2, 9, 2, 1, 6 ),
1265
+ ( 3, 10, 1, 6, 3, 11, 10, 3, 7, 8, 4, 7 ),
1266
+ ( 1, 11, 10, 6, 1, 4, 11, 0, 1, 0, 4, 3, 7, 11, 4, 5 ),
1267
+ ( 4, 7, 8, 7, 9, 0, 11, 1, 9, 11, 10, 6, 11, 0, 3, 6 ),
1268
+ ( 4, 7, 11, 3, 4, 11, 9, 4, 9, 11, 10, 6 ), ( 9, 5, 4, 7 ),
1269
+ ( 9, 5, 4, 7, 0, 8, 3, 7 ), ( 0, 5, 4, 6, 1, 5, 0, 5 ),
1270
+ ( 8, 5, 4, 6, 8, 3, 5, 1, 3, 1, 5, 3 ), ( 1, 2, 10, 7, 9, 5, 4, 7 ),
1271
+ ( 3, 0, 8, 7, 1, 2, 10, 7, 4, 9, 5, 7 ),
1272
+ ( 5, 2, 10, 6, 5, 4, 2, 1, 4, 0, 2, 3 ),
1273
+ ( 2, 10, 5, 3, 3, 2, 5, 1, 3, 5, 4, 2, 3, 4, 8, 6 ),
1274
+ ( 9, 5, 4, 7, 2, 3, 11, 7 ), ( 0, 11, 2, 6, 0, 8, 11, 3, 4, 9, 5, 7 ),
1275
+ ( 0, 5, 4, 6, 0, 1, 5, 3, 2, 3, 11, 7 ),
1276
+ ( 2, 1, 5, 3, 2, 5, 8, 0, 2, 8, 11, 6, 4, 8, 5, 5 ),
1277
+ ( 10, 3, 11, 6, 10, 1, 3, 3, 9, 5, 4, 7 ),
1278
+ ( 4, 9, 5, 7, 0, 8, 1, 5, 8, 10, 1, 2, 8, 11, 10, 3 ),
1279
+ ( 5, 4, 0, 3, 5, 0, 11, 0, 5, 11, 10, 6, 11, 0, 3, 6 ),
1280
+ ( 5, 4, 8, 3, 5, 8, 10, 4, 10, 8, 11, 6 ), ( 9, 7, 8, 6, 5, 7, 9, 5 ),
1281
+ ( 9, 3, 0, 6, 9, 5, 3, 1, 5, 7, 3, 3 ),
1282
+ ( 0, 7, 8, 6, 0, 1, 7, 1, 1, 5, 7, 3 ), ( 1, 5, 3, 5, 3, 5, 7, 6 ),
1283
+ ( 9, 7, 8, 6, 9, 5, 7, 3, 10, 1, 2, 7 ),
1284
+ ( 10, 1, 2, 7, 9, 5, 0, 5, 5, 3, 0, 2, 5, 7, 3, 3 ),
1285
+ ( 8, 0, 2, 3, 8, 2, 5, 0, 8, 5, 7, 6, 10, 5, 2, 5 ),
1286
+ ( 2, 10, 5, 3, 2, 5, 3, 4, 3, 5, 7, 6 ),
1287
+ ( 7, 9, 5, 6, 7, 8, 9, 3, 3, 11, 2, 7 ),
1288
+ ( 9, 5, 7, 3, 9, 7, 2, 0, 9, 2, 0, 6, 2, 7, 11, 6 ),
1289
+ ( 2, 3, 11, 7, 0, 1, 8, 5, 1, 7, 8, 2, 1, 5, 7, 3 ),
1290
+ ( 11, 2, 1, 3, 11, 1, 7, 4, 7, 1, 5, 6 ),
1291
+ ( 9, 5, 8, 5, 8, 5, 7, 6, 10, 1, 3, 3, 10, 3, 11, 6 ),
1292
+ ( 5, 7, 0, 1, 5, 0, 9, 6, 7, 11, 0, 1, 1, 0, 10, 5, 11, 10, 0, 1 ),
1293
+ ( 11, 10, 0, 1, 11, 0, 3, 6, 10, 5, 0, 1, 8, 0, 7, 5, 5, 7, 0, 1 ),
1294
+ ( 11, 10, 5, 3, 7, 11, 5, 5 ), ( 10, 6, 5, 7 ),
1295
+ ( 0, 8, 3, 7, 5, 10, 6, 7 ), ( 9, 0, 1, 7, 5, 10, 6, 7 ),
1296
+ ( 1, 8, 3, 6, 1, 9, 8, 3, 5, 10, 6, 7 ), ( 1, 6, 5, 6, 2, 6, 1, 5 ),
1297
+ ( 1, 6, 5, 6, 1, 2, 6, 3, 3, 0, 8, 7 ),
1298
+ ( 9, 6, 5, 6, 9, 0, 6, 1, 0, 2, 6, 3 ),
1299
+ ( 5, 9, 8, 3, 5, 8, 2, 0, 5, 2, 6, 6, 3, 2, 8, 5 ),
1300
+ ( 2, 3, 11, 7, 10, 6, 5, 7 ), ( 11, 0, 8, 6, 11, 2, 0, 3, 10, 6, 5, 7 ),
1301
+ ( 0, 1, 9, 7, 2, 3, 11, 7, 5, 10, 6, 7 ),
1302
+ ( 5, 10, 6, 7, 1, 9, 2, 5, 9, 11, 2, 2, 9, 8, 11, 3 ),
1303
+ ( 6, 3, 11, 6, 6, 5, 3, 1, 5, 1, 3, 3 ),
1304
+ ( 0, 8, 11, 3, 0, 11, 5, 0, 0, 5, 1, 6, 5, 11, 6, 6 ),
1305
+ ( 3, 11, 6, 3, 0, 3, 6, 1, 0, 6, 5, 2, 0, 5, 9, 6 ),
1306
+ ( 6, 5, 9, 3, 6, 9, 11, 4, 11, 9, 8, 6 ), ( 5, 10, 6, 7, 4, 7, 8, 7 ),
1307
+ ( 4, 3, 0, 6, 4, 7, 3, 3, 6, 5, 10, 7 ),
1308
+ ( 1, 9, 0, 7, 5, 10, 6, 7, 8, 4, 7, 7 ),
1309
+ ( 10, 6, 5, 7, 1, 9, 7, 1, 1, 7, 3, 6, 7, 9, 4, 6 ),
1310
+ ( 6, 1, 2, 6, 6, 5, 1, 3, 4, 7, 8, 7 ),
1311
+ ( 1, 2, 5, 5, 5, 2, 6, 6, 3, 0, 4, 3, 3, 4, 7, 6 ),
1312
+ ( 8, 4, 7, 7, 9, 0, 5, 5, 0, 6, 5, 2, 0, 2, 6, 3 ),
1313
+ ( 7, 3, 9, 1, 7, 9, 4, 6, 3, 2, 9, 1, 5, 9, 6, 5, 2, 6, 9, 1 ),
1314
+ ( 3, 11, 2, 7, 7, 8, 4, 7, 10, 6, 5, 7 ),
1315
+ ( 5, 10, 6, 7, 4, 7, 2, 1, 4, 2, 0, 6, 2, 7, 11, 6 ),
1316
+ ( 0, 1, 9, 7, 4, 7, 8, 7, 2, 3, 11, 7, 5, 10, 6, 7 ),
1317
+ ( 9, 2, 1, 6, 9, 11, 2, 2, 9, 4, 11, 1, 7, 11, 4, 5, 5, 10, 6, 7 ),
1318
+ ( 8, 4, 7, 7, 3, 11, 5, 1, 3, 5, 1, 6, 5, 11, 6, 6 ),
1319
+ ( 5, 1, 11, 1, 5, 11, 6, 6, 1, 0, 11, 1, 7, 11, 4, 5, 0, 4, 11, 1 ),
1320
+ ( 0, 5, 9, 6, 0, 6, 5, 2, 0, 3, 6, 1, 11, 6, 3, 5, 8, 4, 7, 7 ),
1321
+ ( 6, 5, 9, 3, 6, 9, 11, 4, 4, 7, 9, 5, 7, 11, 9, 1 ),
1322
+ ( 10, 4, 9, 6, 6, 4, 10, 5 ), ( 4, 10, 6, 6, 4, 9, 10, 3, 0, 8, 3, 7 ),
1323
+ ( 10, 0, 1, 6, 10, 6, 0, 1, 6, 4, 0, 3 ),
1324
+ ( 8, 3, 1, 3, 8, 1, 6, 0, 8, 6, 4, 6, 6, 1, 10, 6 ),
1325
+ ( 1, 4, 9, 6, 1, 2, 4, 1, 2, 6, 4, 3 ),
1326
+ ( 3, 0, 8, 7, 1, 2, 9, 5, 2, 4, 9, 2, 2, 6, 4, 3 ),
1327
+ ( 0, 2, 4, 5, 4, 2, 6, 6 ), ( 8, 3, 2, 3, 8, 2, 4, 4, 4, 2, 6, 6 ),
1328
+ ( 10, 4, 9, 6, 10, 6, 4, 3, 11, 2, 3, 7 ),
1329
+ ( 0, 8, 2, 5, 2, 8, 11, 6, 4, 9, 10, 3, 4, 10, 6, 6 ),
1330
+ ( 3, 11, 2, 7, 0, 1, 6, 1, 0, 6, 4, 6, 6, 1, 10, 6 ),
1331
+ ( 6, 4, 1, 1, 6, 1, 10, 6, 4, 8, 1, 1, 2, 1, 11, 5, 8, 11, 1, 1 ),
1332
+ ( 9, 6, 4, 6, 9, 3, 6, 0, 9, 1, 3, 3, 11, 6, 3, 5 ),
1333
+ ( 8, 11, 1, 1, 8, 1, 0, 6, 11, 6, 1, 1, 9, 1, 4, 5, 6, 4, 1, 1 ),
1334
+ ( 3, 11, 6, 3, 3, 6, 0, 4, 0, 6, 4, 6 ), ( 6, 4, 8, 3, 11, 6, 8, 5 ),
1335
+ ( 7, 10, 6, 6, 7, 8, 10, 1, 8, 9, 10, 3 ),
1336
+ ( 0, 7, 3, 6, 0, 10, 7, 0, 0, 9, 10, 3, 6, 7, 10, 5 ),
1337
+ ( 10, 6, 7, 3, 1, 10, 7, 1, 1, 7, 8, 2, 1, 8, 0, 6 ),
1338
+ ( 10, 6, 7, 3, 10, 7, 1, 4, 1, 7, 3, 6 ),
1339
+ ( 1, 2, 6, 3, 1, 6, 8, 0, 1, 8, 9, 6, 8, 6, 7, 6 ),
1340
+ ( 2, 6, 9, 1, 2, 9, 1, 6, 6, 7, 9, 1, 0, 9, 3, 5, 7, 3, 9, 1 ),
1341
+ ( 7, 8, 0, 3, 7, 0, 6, 4, 6, 0, 2, 6 ), ( 7, 3, 2, 3, 6, 7, 2, 5 ),
1342
+ ( 2, 3, 11, 7, 10, 6, 8, 1, 10, 8, 9, 6, 8, 6, 7, 6 ),
1343
+ ( 2, 0, 7, 1, 2, 7, 11, 6, 0, 9, 7, 1, 6, 7, 10, 5, 9, 10, 7, 1 ),
1344
+ ( 1, 8, 0, 6, 1, 7, 8, 2, 1, 10, 7, 1, 6, 7, 10, 5, 2, 3, 11, 7 ),
1345
+ ( 11, 2, 1, 3, 11, 1, 7, 4, 10, 6, 1, 5, 6, 7, 1, 1 ),
1346
+ ( 8, 9, 6, 1, 8, 6, 7, 6, 9, 1, 6, 1, 11, 6, 3, 5, 1, 3, 6, 1 ),
1347
+ ( 0, 9, 1, 7, 11, 6, 7, 7 ),
1348
+ ( 7, 8, 0, 3, 7, 0, 6, 4, 3, 11, 0, 5, 11, 6, 0, 1 ), ( 7, 11, 6, 7 ),
1349
+ ( 7, 6, 11, 7 ), ( 3, 0, 8, 7, 11, 7, 6, 7 ),
1350
+ ( 0, 1, 9, 7, 11, 7, 6, 7 ), ( 8, 1, 9, 6, 8, 3, 1, 3, 11, 7, 6, 7 ),
1351
+ ( 10, 1, 2, 7, 6, 11, 7, 7 ), ( 1, 2, 10, 7, 3, 0, 8, 7, 6, 11, 7, 7 ),
1352
+ ( 2, 9, 0, 6, 2, 10, 9, 3, 6, 11, 7, 7 ),
1353
+ ( 6, 11, 7, 7, 2, 10, 3, 5, 10, 8, 3, 2, 10, 9, 8, 3 ),
1354
+ ( 7, 2, 3, 6, 6, 2, 7, 5 ), ( 7, 0, 8, 6, 7, 6, 0, 1, 6, 2, 0, 3 ),
1355
+ ( 2, 7, 6, 6, 2, 3, 7, 3, 0, 1, 9, 7 ),
1356
+ ( 1, 6, 2, 6, 1, 8, 6, 0, 1, 9, 8, 3, 8, 7, 6, 3 ),
1357
+ ( 10, 7, 6, 6, 10, 1, 7, 1, 1, 3, 7, 3 ),
1358
+ ( 10, 7, 6, 6, 1, 7, 10, 4, 1, 8, 7, 2, 1, 0, 8, 3 ),
1359
+ ( 0, 3, 7, 3, 0, 7, 10, 0, 0, 10, 9, 6, 6, 10, 7, 5 ),
1360
+ ( 7, 6, 10, 3, 7, 10, 8, 4, 8, 10, 9, 6 ), ( 6, 8, 4, 6, 11, 8, 6, 5 ),
1361
+ ( 3, 6, 11, 6, 3, 0, 6, 1, 0, 4, 6, 3 ),
1362
+ ( 8, 6, 11, 6, 8, 4, 6, 3, 9, 0, 1, 7 ),
1363
+ ( 9, 4, 6, 3, 9, 6, 3, 0, 9, 3, 1, 6, 11, 3, 6, 5 ),
1364
+ ( 6, 8, 4, 6, 6, 11, 8, 3, 2, 10, 1, 7 ),
1365
+ ( 1, 2, 10, 7, 3, 0, 11, 5, 0, 6, 11, 2, 0, 4, 6, 3 ),
1366
+ ( 4, 11, 8, 6, 4, 6, 11, 3, 0, 2, 9, 5, 2, 10, 9, 3 ),
1367
+ ( 10, 9, 3, 1, 10, 3, 2, 6, 9, 4, 3, 1, 11, 3, 6, 5, 4, 6, 3, 1 ),
1368
+ ( 8, 2, 3, 6, 8, 4, 2, 1, 4, 6, 2, 3 ), ( 0, 4, 2, 5, 4, 6, 2, 3 ),
1369
+ ( 1, 9, 0, 7, 2, 3, 4, 1, 2, 4, 6, 6, 4, 3, 8, 6 ),
1370
+ ( 1, 9, 4, 3, 1, 4, 2, 4, 2, 4, 6, 6 ),
1371
+ ( 8, 1, 3, 6, 8, 6, 1, 0, 8, 4, 6, 3, 6, 10, 1, 3 ),
1372
+ ( 10, 1, 0, 3, 10, 0, 6, 4, 6, 0, 4, 6 ),
1373
+ ( 4, 6, 3, 1, 4, 3, 8, 6, 6, 10, 3, 1, 0, 3, 9, 5, 10, 9, 3, 1 ),
1374
+ ( 10, 9, 4, 3, 6, 10, 4, 5 ), ( 4, 9, 5, 7, 7, 6, 11, 7 ),
1375
+ ( 0, 8, 3, 7, 4, 9, 5, 7, 11, 7, 6, 7 ),
1376
+ ( 5, 0, 1, 6, 5, 4, 0, 3, 7, 6, 11, 7 ),
1377
+ ( 11, 7, 6, 7, 8, 3, 4, 5, 3, 5, 4, 2, 3, 1, 5, 3 ),
1378
+ ( 9, 5, 4, 7, 10, 1, 2, 7, 7, 6, 11, 7 ),
1379
+ ( 6, 11, 7, 7, 1, 2, 10, 7, 0, 8, 3, 7, 4, 9, 5, 7 ),
1380
+ ( 7, 6, 11, 7, 5, 4, 10, 5, 4, 2, 10, 2, 4, 0, 2, 3 ),
1381
+ ( 3, 4, 8, 6, 3, 5, 4, 2, 3, 2, 5, 1, 10, 5, 2, 5, 11, 7, 6, 7 ),
1382
+ ( 7, 2, 3, 6, 7, 6, 2, 3, 5, 4, 9, 7 ),
1383
+ ( 9, 5, 4, 7, 0, 8, 6, 1, 0, 6, 2, 6, 6, 8, 7, 6 ),
1384
+ ( 3, 6, 2, 6, 3, 7, 6, 3, 1, 5, 0, 5, 5, 4, 0, 3 ),
1385
+ ( 6, 2, 8, 1, 6, 8, 7, 6, 2, 1, 8, 1, 4, 8, 5, 5, 1, 5, 8, 1 ),
1386
+ ( 9, 5, 4, 7, 10, 1, 6, 5, 1, 7, 6, 2, 1, 3, 7, 3 ),
1387
+ ( 1, 6, 10, 6, 1, 7, 6, 2, 1, 0, 7, 1, 8, 7, 0, 5, 9, 5, 4, 7 ),
1388
+ ( 4, 0, 10, 1, 4, 10, 5, 6, 0, 3, 10, 1, 6, 10, 7, 5, 3, 7, 10, 1 ),
1389
+ ( 7, 6, 10, 3, 7, 10, 8, 4, 5, 4, 10, 5, 4, 8, 10, 1 ),
1390
+ ( 6, 9, 5, 6, 6, 11, 9, 1, 11, 8, 9, 3 ),
1391
+ ( 3, 6, 11, 6, 0, 6, 3, 4, 0, 5, 6, 2, 0, 9, 5, 3 ),
1392
+ ( 0, 11, 8, 6, 0, 5, 11, 0, 0, 1, 5, 3, 5, 6, 11, 3 ),
1393
+ ( 6, 11, 3, 3, 6, 3, 5, 4, 5, 3, 1, 6 ),
1394
+ ( 1, 2, 10, 7, 9, 5, 11, 1, 9, 11, 8, 6, 11, 5, 6, 6 ),
1395
+ ( 0, 11, 3, 6, 0, 6, 11, 2, 0, 9, 6, 1, 5, 6, 9, 5, 1, 2, 10, 7 ),
1396
+ ( 11, 8, 5, 1, 11, 5, 6, 6, 8, 0, 5, 1, 10, 5, 2, 5, 0, 2, 5, 1 ),
1397
+ ( 6, 11, 3, 3, 6, 3, 5, 4, 2, 10, 3, 5, 10, 5, 3, 1 ),
1398
+ ( 5, 8, 9, 6, 5, 2, 8, 0, 5, 6, 2, 3, 3, 8, 2, 5 ),
1399
+ ( 9, 5, 6, 3, 9, 6, 0, 4, 0, 6, 2, 6 ),
1400
+ ( 1, 5, 8, 1, 1, 8, 0, 6, 5, 6, 8, 1, 3, 8, 2, 5, 6, 2, 8, 1 ),
1401
+ ( 1, 5, 6, 3, 2, 1, 6, 5 ),
1402
+ ( 1, 3, 6, 1, 1, 6, 10, 6, 3, 8, 6, 1, 5, 6, 9, 5, 8, 9, 6, 1 ),
1403
+ ( 10, 1, 0, 3, 10, 0, 6, 4, 9, 5, 0, 5, 5, 6, 0, 1 ),
1404
+ ( 0, 3, 8, 7, 5, 6, 10, 7 ), ( 10, 5, 6, 7 ),
1405
+ ( 11, 5, 10, 6, 7, 5, 11, 5 ), ( 11, 5, 10, 6, 11, 7, 5, 3, 8, 3, 0, 7 ),
1406
+ ( 5, 11, 7, 6, 5, 10, 11, 3, 1, 9, 0, 7 ),
1407
+ ( 10, 7, 5, 6, 10, 11, 7, 3, 9, 8, 1, 5, 8, 3, 1, 3 ),
1408
+ ( 11, 1, 2, 6, 11, 7, 1, 1, 7, 5, 1, 3 ),
1409
+ ( 0, 8, 3, 7, 1, 2, 7, 1, 1, 7, 5, 6, 7, 2, 11, 6 ),
1410
+ ( 9, 7, 5, 6, 9, 2, 7, 0, 9, 0, 2, 3, 2, 11, 7, 3 ),
1411
+ ( 7, 5, 2, 1, 7, 2, 11, 6, 5, 9, 2, 1, 3, 2, 8, 5, 9, 8, 2, 1 ),
1412
+ ( 2, 5, 10, 6, 2, 3, 5, 1, 3, 7, 5, 3 ),
1413
+ ( 8, 2, 0, 6, 8, 5, 2, 0, 8, 7, 5, 3, 10, 2, 5, 5 ),
1414
+ ( 9, 0, 1, 7, 5, 10, 3, 1, 5, 3, 7, 6, 3, 10, 2, 6 ),
1415
+ ( 9, 8, 2, 1, 9, 2, 1, 6, 8, 7, 2, 1, 10, 2, 5, 5, 7, 5, 2, 1 ),
1416
+ ( 1, 3, 5, 5, 3, 7, 5, 3 ), ( 0, 8, 7, 3, 0, 7, 1, 4, 1, 7, 5, 6 ),
1417
+ ( 9, 0, 3, 3, 9, 3, 5, 4, 5, 3, 7, 6 ), ( 9, 8, 7, 3, 5, 9, 7, 5 ),
1418
+ ( 5, 8, 4, 6, 5, 10, 8, 1, 10, 11, 8, 3 ),
1419
+ ( 5, 0, 4, 6, 5, 11, 0, 0, 5, 10, 11, 3, 11, 3, 0, 3 ),
1420
+ ( 0, 1, 9, 7, 8, 4, 10, 1, 8, 10, 11, 6, 10, 4, 5, 6 ),
1421
+ ( 10, 11, 4, 1, 10, 4, 5, 6, 11, 3, 4, 1, 9, 4, 1, 5, 3, 1, 4, 1 ),
1422
+ ( 2, 5, 1, 6, 2, 8, 5, 0, 2, 11, 8, 3, 4, 5, 8, 5 ),
1423
+ ( 0, 4, 11, 1, 0, 11, 3, 6, 4, 5, 11, 1, 2, 11, 1, 5, 5, 1, 11, 1 ),
1424
+ ( 0, 2, 5, 1, 0, 5, 9, 6, 2, 11, 5, 1, 4, 5, 8, 5, 11, 8, 5, 1 ),
1425
+ ( 9, 4, 5, 7, 2, 11, 3, 7 ),
1426
+ ( 2, 5, 10, 6, 3, 5, 2, 4, 3, 4, 5, 2, 3, 8, 4, 3 ),
1427
+ ( 5, 10, 2, 3, 5, 2, 4, 4, 4, 2, 0, 6 ),
1428
+ ( 3, 10, 2, 6, 3, 5, 10, 2, 3, 8, 5, 1, 4, 5, 8, 5, 0, 1, 9, 7 ),
1429
+ ( 5, 10, 2, 3, 5, 2, 4, 4, 1, 9, 2, 5, 9, 4, 2, 1 ),
1430
+ ( 8, 4, 5, 3, 8, 5, 3, 4, 3, 5, 1, 6 ), ( 0, 4, 5, 3, 1, 0, 5, 5 ),
1431
+ ( 8, 4, 5, 3, 8, 5, 3, 4, 9, 0, 5, 5, 0, 3, 5, 1 ), ( 9, 4, 5, 7 ),
1432
+ ( 4, 11, 7, 6, 4, 9, 11, 1, 9, 10, 11, 3 ),
1433
+ ( 0, 8, 3, 7, 4, 9, 7, 5, 9, 11, 7, 2, 9, 10, 11, 3 ),
1434
+ ( 1, 10, 11, 3, 1, 11, 4, 0, 1, 4, 0, 6, 7, 4, 11, 5 ),
1435
+ ( 3, 1, 4, 1, 3, 4, 8, 6, 1, 10, 4, 1, 7, 4, 11, 5, 10, 11, 4, 1 ),
1436
+ ( 4, 11, 7, 6, 9, 11, 4, 4, 9, 2, 11, 2, 9, 1, 2, 3 ),
1437
+ ( 9, 7, 4, 6, 9, 11, 7, 2, 9, 1, 11, 1, 2, 11, 1, 5, 0, 8, 3, 7 ),
1438
+ ( 11, 7, 4, 3, 11, 4, 2, 4, 2, 4, 0, 6 ),
1439
+ ( 11, 7, 4, 3, 11, 4, 2, 4, 8, 3, 4, 5, 3, 2, 4, 1 ),
1440
+ ( 2, 9, 10, 6, 2, 7, 9, 0, 2, 3, 7, 3, 7, 4, 9, 3 ),
1441
+ ( 9, 10, 7, 1, 9, 7, 4, 6, 10, 2, 7, 1, 8, 7, 0, 5, 2, 0, 7, 1 ),
1442
+ ( 3, 7, 10, 1, 3, 10, 2, 6, 7, 4, 10, 1, 1, 10, 0, 5, 4, 0, 10, 1 ),
1443
+ ( 1, 10, 2, 7, 8, 7, 4, 7 ), ( 4, 9, 1, 3, 4, 1, 7, 4, 7, 1, 3, 6 ),
1444
+ ( 4, 9, 1, 3, 4, 1, 7, 4, 0, 8, 1, 5, 8, 7, 1, 1 ),
1445
+ ( 4, 0, 3, 3, 7, 4, 3, 5 ), ( 4, 8, 7, 7 ),
1446
+ ( 9, 10, 8, 5, 10, 11, 8, 3 ), ( 3, 0, 9, 3, 3, 9, 11, 4, 11, 9, 10, 6 ),
1447
+ ( 0, 1, 10, 3, 0, 10, 8, 4, 8, 10, 11, 6 ),
1448
+ ( 3, 1, 10, 3, 11, 3, 10, 5 ), ( 1, 2, 11, 3, 1, 11, 9, 4, 9, 11, 8, 6 ),
1449
+ ( 3, 0, 9, 3, 3, 9, 11, 4, 1, 2, 9, 5, 2, 11, 9, 1 ),
1450
+ ( 0, 2, 11, 3, 8, 0, 11, 5 ), ( 3, 2, 11, 7 ),
1451
+ ( 2, 3, 8, 3, 2, 8, 10, 4, 10, 8, 9, 6 ), ( 9, 10, 2, 3, 0, 9, 2, 5 ),
1452
+ ( 2, 3, 8, 3, 2, 8, 10, 4, 0, 1, 8, 5, 1, 10, 8, 1 ), ( 1, 10, 2, 7 ),
1453
+ ( 1, 3, 8, 3, 9, 1, 8, 5 ), ( 0, 9, 1, 7 ), ( 0, 3, 8, 7 ), None )