passagemath-plot 10.6.31rc3__cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-plot might be problematic. Click here for more details.
- passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
- passagemath_plot-10.6.31rc3.dist-info/RECORD +81 -0
- passagemath_plot-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_plot-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_plot.libs/libgfortran-e1b7dfc8.so.5.0.0 +0 -0
- passagemath_plot.libs/libgsl-e3525837.so.28.0.0 +0 -0
- passagemath_plot.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
- sage/all__sagemath_plot.py +15 -0
- sage/ext_data/threejs/animation.css +195 -0
- sage/ext_data/threejs/animation.html +85 -0
- sage/ext_data/threejs/animation.js +273 -0
- sage/ext_data/threejs/fat_lines.js +48 -0
- sage/ext_data/threejs/threejs-version.txt +1 -0
- sage/ext_data/threejs/threejs_template.html +597 -0
- sage/interfaces/all__sagemath_plot.py +1 -0
- sage/interfaces/gnuplot.py +196 -0
- sage/interfaces/jmoldata.py +208 -0
- sage/interfaces/povray.py +56 -0
- sage/plot/all.py +42 -0
- sage/plot/animate.py +1796 -0
- sage/plot/arc.py +504 -0
- sage/plot/arrow.py +671 -0
- sage/plot/bar_chart.py +205 -0
- sage/plot/bezier_path.py +400 -0
- sage/plot/circle.py +435 -0
- sage/plot/colors.py +1606 -0
- sage/plot/complex_plot.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/complex_plot.pyx +1446 -0
- sage/plot/contour_plot.py +1792 -0
- sage/plot/density_plot.py +318 -0
- sage/plot/disk.py +373 -0
- sage/plot/ellipse.py +375 -0
- sage/plot/graphics.py +3580 -0
- sage/plot/histogram.py +354 -0
- sage/plot/hyperbolic_arc.py +404 -0
- sage/plot/hyperbolic_polygon.py +416 -0
- sage/plot/hyperbolic_regular_polygon.py +296 -0
- sage/plot/line.py +626 -0
- sage/plot/matrix_plot.py +629 -0
- sage/plot/misc.py +509 -0
- sage/plot/multigraphics.py +1294 -0
- sage/plot/plot.py +4183 -0
- sage/plot/plot3d/all.py +23 -0
- sage/plot/plot3d/base.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/base.pxd +12 -0
- sage/plot/plot3d/base.pyx +3378 -0
- sage/plot/plot3d/implicit_plot3d.py +659 -0
- sage/plot/plot3d/implicit_surface.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/implicit_surface.pyx +1453 -0
- sage/plot/plot3d/index_face_set.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/index_face_set.pxd +32 -0
- sage/plot/plot3d/index_face_set.pyx +1873 -0
- sage/plot/plot3d/introduction.py +131 -0
- sage/plot/plot3d/list_plot3d.py +649 -0
- sage/plot/plot3d/parametric_plot3d.py +1130 -0
- sage/plot/plot3d/parametric_surface.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/parametric_surface.pxd +12 -0
- sage/plot/plot3d/parametric_surface.pyx +893 -0
- sage/plot/plot3d/platonic.py +601 -0
- sage/plot/plot3d/plot3d.py +1442 -0
- sage/plot/plot3d/plot_field3d.py +162 -0
- sage/plot/plot3d/point_c.pxi +148 -0
- sage/plot/plot3d/revolution_plot3d.py +309 -0
- sage/plot/plot3d/shapes.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/shapes.pxd +22 -0
- sage/plot/plot3d/shapes.pyx +1382 -0
- sage/plot/plot3d/shapes2.py +1512 -0
- sage/plot/plot3d/tachyon.py +1779 -0
- sage/plot/plot3d/texture.py +453 -0
- sage/plot/plot3d/transform.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/transform.pxd +21 -0
- sage/plot/plot3d/transform.pyx +268 -0
- sage/plot/plot3d/tri_plot.py +589 -0
- sage/plot/plot_field.py +362 -0
- sage/plot/point.py +624 -0
- sage/plot/polygon.py +562 -0
- sage/plot/primitive.py +249 -0
- sage/plot/scatter_plot.py +199 -0
- sage/plot/step.py +85 -0
- sage/plot/streamline_plot.py +328 -0
- sage/plot/text.py +432 -0
|
@@ -0,0 +1,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 )
|