passagemath-plot 10.6.31rc3__cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-plot might be problematic. Click here for more details.
- passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
- passagemath_plot-10.6.31rc3.dist-info/RECORD +82 -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-83c28eba.so.5.0.0 +0 -0
- passagemath_plot.libs/libgsl-cda90e79.so.28.0.0 +0 -0
- passagemath_plot.libs/libopenblasp-r0-6dcb67f9.3.29.so +0 -0
- passagemath_plot.libs/libquadmath-2284e583.so.0.0.0 +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-x86_64-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-x86_64-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-x86_64-linux-gnu.so +0 -0
- sage/plot/plot3d/implicit_surface.pyx +1453 -0
- sage/plot/plot3d/index_face_set.cpython-314-x86_64-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-x86_64-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-x86_64-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-x86_64-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,589 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-plot
|
|
2
|
+
r"""
|
|
3
|
+
Adaptive refinement code for 3d surface plotting
|
|
4
|
+
|
|
5
|
+
AUTHOR:
|
|
6
|
+
|
|
7
|
+
- Tom Boothby -- Algorithm design, code
|
|
8
|
+
- Joshua Kantor -- Algorithm design
|
|
9
|
+
- Marshall Hampton -- Docstrings and doctests
|
|
10
|
+
|
|
11
|
+
.. TODO::
|
|
12
|
+
|
|
13
|
+
- Parametrizations (cylindrical, spherical)
|
|
14
|
+
- Massive optimization
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
###########################################################################
|
|
18
|
+
# Copyright (C) 2007 Tom Boothby <boothby@u.washington.edu>
|
|
19
|
+
# 2007 Joshua Kantor <jkantor@math.washington.edu>
|
|
20
|
+
#
|
|
21
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
22
|
+
# http://www.gnu.org/licenses/
|
|
23
|
+
###########################################################################
|
|
24
|
+
|
|
25
|
+
from sage.plot.colors import hue
|
|
26
|
+
from math import sqrt
|
|
27
|
+
import random
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Triangle:
|
|
31
|
+
"""
|
|
32
|
+
A graphical triangle class.
|
|
33
|
+
"""
|
|
34
|
+
def __init__(self, a, b, c, color=0):
|
|
35
|
+
"""
|
|
36
|
+
a, b, c : triples (x,y,z) representing corners on a triangle in 3-space.
|
|
37
|
+
|
|
38
|
+
TESTS::
|
|
39
|
+
|
|
40
|
+
sage: from sage.plot.plot3d.tri_plot import Triangle
|
|
41
|
+
sage: tri = Triangle([0,0,0],[-1,2,3],[0,2,0])
|
|
42
|
+
sage: tri._a
|
|
43
|
+
[0, 0, 0]
|
|
44
|
+
sage: tri.__init__([0,0,1],[-1,2,3],[0,2,0])
|
|
45
|
+
sage: tri._a
|
|
46
|
+
[0, 0, 1]
|
|
47
|
+
"""
|
|
48
|
+
self._a = a
|
|
49
|
+
self._b = b
|
|
50
|
+
self._c = c
|
|
51
|
+
self._color = color
|
|
52
|
+
|
|
53
|
+
def str(self):
|
|
54
|
+
"""
|
|
55
|
+
Return a string representation of an instance of the Triangle
|
|
56
|
+
class of the form
|
|
57
|
+
|
|
58
|
+
a b c color
|
|
59
|
+
|
|
60
|
+
where a, b, and c are corner coordinates and color is the color.
|
|
61
|
+
|
|
62
|
+
TESTS::
|
|
63
|
+
|
|
64
|
+
sage: from sage.plot.plot3d.tri_plot import Triangle
|
|
65
|
+
sage: tri = Triangle([0,0,0],[-1,2,3],[0,2,0])
|
|
66
|
+
sage: print(tri.str())
|
|
67
|
+
[0, 0, 0] [-1, 2, 3] [0, 2, 0] 0
|
|
68
|
+
"""
|
|
69
|
+
return f"{self._a} {self._b} {self._c} {self._color}"
|
|
70
|
+
|
|
71
|
+
def set_color(self, color):
|
|
72
|
+
"""
|
|
73
|
+
This method will reset the color of the triangle.
|
|
74
|
+
|
|
75
|
+
TESTS::
|
|
76
|
+
|
|
77
|
+
sage: from sage.plot.plot3d.tri_plot import Triangle
|
|
78
|
+
sage: tri = Triangle([0,0,0],[-1,2,3],[0,2,1])
|
|
79
|
+
sage: tri._color
|
|
80
|
+
0
|
|
81
|
+
sage: tri.set_color(1)
|
|
82
|
+
sage: tri._color
|
|
83
|
+
1
|
|
84
|
+
"""
|
|
85
|
+
self._color = color
|
|
86
|
+
|
|
87
|
+
def get_vertices(self):
|
|
88
|
+
"""
|
|
89
|
+
Return a tuple of vertex coordinates of the triangle.
|
|
90
|
+
|
|
91
|
+
TESTS::
|
|
92
|
+
|
|
93
|
+
sage: from sage.plot.plot3d.tri_plot import Triangle
|
|
94
|
+
sage: tri = Triangle([0,0,0],[-1,2,3],[0,2,1])
|
|
95
|
+
sage: tri.get_vertices()
|
|
96
|
+
([0, 0, 0], [-1, 2, 3], [0, 2, 1])
|
|
97
|
+
"""
|
|
98
|
+
return (self._a, self._b, self._c)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class SmoothTriangle(Triangle):
|
|
102
|
+
"""
|
|
103
|
+
A class for smoothed triangles.
|
|
104
|
+
"""
|
|
105
|
+
def __init__(self, a, b, c, da, db, dc, color=0):
|
|
106
|
+
"""
|
|
107
|
+
a, b, c : triples (x,y,z) representing corners on a triangle in 3-space
|
|
108
|
+
da, db, dc : triples (dx,dy,dz) representing the normal vector at each point a,b,c
|
|
109
|
+
|
|
110
|
+
TESTS::
|
|
111
|
+
|
|
112
|
+
sage: from sage.plot.plot3d.tri_plot import SmoothTriangle
|
|
113
|
+
sage: t = SmoothTriangle([1,2,3],[2,3,4],[0,0,0],[0,0,1],[0,1,0],[1,0,0])
|
|
114
|
+
sage: t._a
|
|
115
|
+
[1, 2, 3]
|
|
116
|
+
"""
|
|
117
|
+
self._a = a
|
|
118
|
+
self._b = b
|
|
119
|
+
self._c = c
|
|
120
|
+
self._da = da
|
|
121
|
+
self._db = db
|
|
122
|
+
self._dc = dc
|
|
123
|
+
self._color = color
|
|
124
|
+
|
|
125
|
+
def str(self):
|
|
126
|
+
"""
|
|
127
|
+
Return a string representation of the SmoothTriangle of the form.
|
|
128
|
+
|
|
129
|
+
a b c color da db dc
|
|
130
|
+
|
|
131
|
+
where a, b, and c are the triangle corner coordinates,
|
|
132
|
+
da, db, dc are normals at each corner, and color is the color.
|
|
133
|
+
|
|
134
|
+
TESTS::
|
|
135
|
+
|
|
136
|
+
sage: from sage.plot.plot3d.tri_plot import SmoothTriangle
|
|
137
|
+
sage: t = SmoothTriangle([1,2,3],[2,3,4],[0,0,0],[0,0,1],[0,1,0],[1,0,0])
|
|
138
|
+
sage: print(t.str())
|
|
139
|
+
[1, 2, 3] [2, 3, 4] [0, 0, 0] 0 [0, 0, 1] [0, 1, 0] [1, 0, 0]
|
|
140
|
+
"""
|
|
141
|
+
return "{} {} {} {} {} {} {}".format(self._a, self._b, self._c, self._color, self._da, self._db, self._dc)
|
|
142
|
+
|
|
143
|
+
def get_normals(self):
|
|
144
|
+
"""
|
|
145
|
+
Return the normals to vertices a, b, and c.
|
|
146
|
+
|
|
147
|
+
TESTS::
|
|
148
|
+
|
|
149
|
+
sage: from sage.plot.plot3d.tri_plot import SmoothTriangle
|
|
150
|
+
sage: t = SmoothTriangle([1,2,3],[2,3,4],[0,0,0],[0,0,1],[0,1,0],[2,0,0])
|
|
151
|
+
sage: t.get_normals()
|
|
152
|
+
([0, 0, 1], [0, 1, 0], [2, 0, 0])
|
|
153
|
+
"""
|
|
154
|
+
return (self._da, self._db, self._dc)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class TriangleFactory:
|
|
158
|
+
def triangle(self, a, b, c, color=None):
|
|
159
|
+
"""
|
|
160
|
+
Parameters:
|
|
161
|
+
a, b, c : triples (x,y,z) representing corners on a triangle in 3-space
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
a Triangle object with the specified coordinates
|
|
165
|
+
|
|
166
|
+
TESTS::
|
|
167
|
+
|
|
168
|
+
sage: from sage.plot.plot3d.tri_plot import TriangleFactory
|
|
169
|
+
sage: factory = TriangleFactory()
|
|
170
|
+
sage: tri = factory.triangle([0,0,0],[0,0,1],[1,1,0])
|
|
171
|
+
sage: tri.get_vertices()
|
|
172
|
+
([0, 0, 0], [0, 0, 1], [1, 1, 0])
|
|
173
|
+
"""
|
|
174
|
+
if color is None:
|
|
175
|
+
return Triangle(a,b,c)
|
|
176
|
+
else:
|
|
177
|
+
return Triangle(a,b,c,color)
|
|
178
|
+
|
|
179
|
+
def smooth_triangle(self, a, b, c, da, db, dc, color=None):
|
|
180
|
+
"""
|
|
181
|
+
Parameters:
|
|
182
|
+
|
|
183
|
+
- a, b, c : triples (x,y,z) representing corners on a triangle in 3-space
|
|
184
|
+
- da, db, dc : triples (dx,dy,dz) representing the normal vector at each point a,b,c
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
a SmoothTriangle object with the specified coordinates and normals
|
|
188
|
+
|
|
189
|
+
TESTS::
|
|
190
|
+
|
|
191
|
+
sage: from sage.plot.plot3d.tri_plot import TriangleFactory
|
|
192
|
+
sage: factory = TriangleFactory()
|
|
193
|
+
sage: sm_tri = factory.smooth_triangle([0,0,0],[0,0,1],[1,1,0],[0,0,1],[0,2,0],[1,0,0])
|
|
194
|
+
sage: sm_tri.get_normals()
|
|
195
|
+
([0, 0, 1], [0, 2, 0], [1, 0, 0])
|
|
196
|
+
"""
|
|
197
|
+
if color is None:
|
|
198
|
+
return SmoothTriangle(a,b,c,da,db,dc)
|
|
199
|
+
else:
|
|
200
|
+
return SmoothTriangle(a,b,c,da,db,dc,color)
|
|
201
|
+
|
|
202
|
+
def get_colors(self, list):
|
|
203
|
+
"""
|
|
204
|
+
Parameters:
|
|
205
|
+
list: an iterable collection of values which can be cast into colors
|
|
206
|
+
-- typically an RGB triple, or an RGBA 4-tuple
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
a list of single parameters which can be passed into the set_color
|
|
210
|
+
method of the Triangle or SmoothTriangle objects generated by this
|
|
211
|
+
factory.
|
|
212
|
+
|
|
213
|
+
TESTS::
|
|
214
|
+
|
|
215
|
+
sage: from sage.plot.plot3d.tri_plot import TriangleFactory
|
|
216
|
+
sage: factory = TriangleFactory()
|
|
217
|
+
sage: factory.get_colors([1,2,3])
|
|
218
|
+
[1, 2, 3]
|
|
219
|
+
"""
|
|
220
|
+
return list
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class TrianglePlot:
|
|
224
|
+
"""
|
|
225
|
+
Recursively plot a function of two variables by building squares of 4 triangles, checking at
|
|
226
|
+
every stage whether or not each square should be split into four more squares. This way,
|
|
227
|
+
more planar areas get fewer triangles, and areas with higher curvature get more triangles.
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
def str(self):
|
|
231
|
+
"""
|
|
232
|
+
Return a string listing the objects in the instance of the TrianglePlot class.
|
|
233
|
+
|
|
234
|
+
TESTS::
|
|
235
|
+
|
|
236
|
+
sage: from sage.plot.plot3d.tri_plot import TrianglePlot, TriangleFactory
|
|
237
|
+
sage: tf = TriangleFactory()
|
|
238
|
+
sage: t = TrianglePlot(tf, lambda x,y: x^3+y*x-1, (-1, 3), (-2, 100), max_depth = 4)
|
|
239
|
+
sage: len(t.str())
|
|
240
|
+
68980
|
|
241
|
+
"""
|
|
242
|
+
return "".join(o.str() for o in self._objects)
|
|
243
|
+
|
|
244
|
+
def __init__(self, triangle_factory, f, min_x__max_x, min_y__max_y, g=None,
|
|
245
|
+
min_depth=4, max_depth=8, num_colors=None, max_bend=.3):
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
TESTS::
|
|
249
|
+
|
|
250
|
+
sage: from sage.plot.plot3d.tri_plot import TrianglePlot, TriangleFactory
|
|
251
|
+
sage: tf = TriangleFactory()
|
|
252
|
+
sage: t = TrianglePlot(tf, lambda x,y: x^2+y^2, (0, 1), (0, 1))
|
|
253
|
+
sage: t._f(1,1)
|
|
254
|
+
2
|
|
255
|
+
"""
|
|
256
|
+
(min_x, max_x) = min_x__max_x
|
|
257
|
+
(min_y, max_y) = min_y__max_y
|
|
258
|
+
self._triangle_factory = triangle_factory
|
|
259
|
+
self._f = f
|
|
260
|
+
self._g = g
|
|
261
|
+
self._min_depth = min_depth
|
|
262
|
+
self._max_depth = max_depth
|
|
263
|
+
self._max_bend = max_bend
|
|
264
|
+
self._objects = []
|
|
265
|
+
if min(max_x - min_x, max_y - min_y) == 0:
|
|
266
|
+
raise ValueError('plot rectangle is really a line; make sure min_x != max_x and min_y != max_y')
|
|
267
|
+
self._num_colors = num_colors
|
|
268
|
+
if g is None:
|
|
269
|
+
def fcn(x, y):
|
|
270
|
+
return [self._f(x,y)]
|
|
271
|
+
else:
|
|
272
|
+
def fcn(x, y):
|
|
273
|
+
return [self._f(x,y), self._g(x,y)]
|
|
274
|
+
|
|
275
|
+
self._fcn = fcn
|
|
276
|
+
|
|
277
|
+
# generate the necessary data to kick-start the recursion
|
|
278
|
+
mid_x = (min_x + max_x)/2
|
|
279
|
+
mid_y = (min_y + max_y)/2
|
|
280
|
+
sw_z = fcn(min_x,min_y)
|
|
281
|
+
nw_z = fcn(min_x,max_y)
|
|
282
|
+
se_z = fcn(max_x,min_y)
|
|
283
|
+
ne_z = fcn(max_x,max_y)
|
|
284
|
+
mid_z = fcn(mid_x,mid_y)
|
|
285
|
+
|
|
286
|
+
self._min = min(sw_z[0], nw_z[0], se_z[0], ne_z[0], mid_z[0])
|
|
287
|
+
self._max = max(sw_z[0], nw_z[0], se_z[0], ne_z[0], mid_z[0])
|
|
288
|
+
|
|
289
|
+
# jump in and start building blocks
|
|
290
|
+
outer = self.plot_block(min_x, mid_x, max_x, min_y, mid_y, max_y, sw_z, nw_z, se_z, ne_z, mid_z, 0)
|
|
291
|
+
|
|
292
|
+
# build the boundary triangles
|
|
293
|
+
self.triangulate(outer.left, outer.left_c)
|
|
294
|
+
self.triangulate(outer.top, outer.top_c)
|
|
295
|
+
self.triangulate(outer.right, outer.right_c)
|
|
296
|
+
self.triangulate(outer.bottom, outer.bottom_c)
|
|
297
|
+
|
|
298
|
+
zrange = self._max - self._min
|
|
299
|
+
if num_colors is not None and zrange != 0:
|
|
300
|
+
colors = triangle_factory.get_colors([hue(float(i/num_colors)) for i in range(num_colors)])
|
|
301
|
+
|
|
302
|
+
for o in self._objects:
|
|
303
|
+
vertices = o.get_vertices()
|
|
304
|
+
avg_z = (vertices[0][2] + vertices[1][2] + vertices[2][2])/3
|
|
305
|
+
o.set_color(colors[int(num_colors * (avg_z - self._min) / zrange)])
|
|
306
|
+
|
|
307
|
+
def plot_block(self, min_x, mid_x, max_x, min_y, mid_y, max_y, sw_z, nw_z, se_z, ne_z, mid_z, depth):
|
|
308
|
+
"""
|
|
309
|
+
Recursive triangulation function for plotting.
|
|
310
|
+
|
|
311
|
+
First six inputs are scalars, next 5 are 2-dimensional lists, and the depth argument
|
|
312
|
+
keeps track of the depth of recursion.
|
|
313
|
+
|
|
314
|
+
TESTS::
|
|
315
|
+
|
|
316
|
+
sage: from sage.plot.plot3d.tri_plot import TrianglePlot, TriangleFactory
|
|
317
|
+
sage: tf = TriangleFactory()
|
|
318
|
+
sage: t = TrianglePlot(tf, lambda x,y: x^2 + y^2, (-1,1), (-1, 1), max_depth=3)
|
|
319
|
+
sage: q = t.plot_block(0,.5,1,0,.5,1,[0,1],[0,1],[0,1],[0,1],[0,1],2)
|
|
320
|
+
sage: q.left
|
|
321
|
+
[[(0, 0, 0)], [(0, 0.500000000000000, 0.250000000000000)], [(0, 1, 0)]]
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
if depth < self._max_depth:
|
|
325
|
+
# recursion is still an option -- step in one last level if we're within tolerance
|
|
326
|
+
# and just keep going if we're not.
|
|
327
|
+
# assumption: it's cheap to build triangles, so we might as well use all the data
|
|
328
|
+
# we calculate
|
|
329
|
+
|
|
330
|
+
# big square boundary midpoints
|
|
331
|
+
mid_w_z = self._fcn(min_x, mid_y)
|
|
332
|
+
mid_n_z = self._fcn(mid_x, max_y)
|
|
333
|
+
mid_e_z = self._fcn(max_x, mid_y)
|
|
334
|
+
mid_s_z = self._fcn(mid_x, min_y)
|
|
335
|
+
|
|
336
|
+
next_depth = depth+1
|
|
337
|
+
if depth < self._min_depth:
|
|
338
|
+
# midpoints locations of sub_squares
|
|
339
|
+
qtr1_x = (min_x + mid_x)/2
|
|
340
|
+
qtr1_y = (min_y + mid_y)/2
|
|
341
|
+
qtr3_x = (mid_x + max_x)/2
|
|
342
|
+
qtr3_y = (mid_y + max_y)/2
|
|
343
|
+
|
|
344
|
+
sw_depth = next_depth
|
|
345
|
+
nw_depth = next_depth
|
|
346
|
+
se_depth = next_depth
|
|
347
|
+
ne_depth = next_depth
|
|
348
|
+
else:
|
|
349
|
+
# compute the midpoint-to-corner vectors
|
|
350
|
+
sw_v = (min_x - mid_x, min_y - mid_y, sw_z[0] - mid_z[0])
|
|
351
|
+
nw_v = (min_x - mid_x, max_y - mid_y, nw_z[0] - mid_z[0])
|
|
352
|
+
se_v = (max_x - mid_x, min_y - mid_y, se_z[0] - mid_z[0])
|
|
353
|
+
ne_v = (max_x - mid_x, max_y - mid_y, ne_z[0] - mid_z[0])
|
|
354
|
+
|
|
355
|
+
# compute triangle normal unit vectors by taking the cross-products
|
|
356
|
+
# of the midpoint-to-corner vectors. always go around clockwise
|
|
357
|
+
# so we're guaranteed to have a positive value near 1 when neighboring
|
|
358
|
+
# triangles are parallel
|
|
359
|
+
# However -- crossunit doesn't really return a unit vector. It returns
|
|
360
|
+
# the length of the vector to avoid numerical instability when the
|
|
361
|
+
# length is nearly zero -- rather than divide by nearly zero, we multiply
|
|
362
|
+
# the other side of the inequality by nearly zero -- in general, this
|
|
363
|
+
# should work a bit better because of the density of floating-point
|
|
364
|
+
# numbers near zero.
|
|
365
|
+
norm_w = crossunit(sw_v, nw_v)
|
|
366
|
+
norm_n = crossunit(nw_v, ne_v)
|
|
367
|
+
norm_e = crossunit(ne_v, se_v)
|
|
368
|
+
norm_s = crossunit(se_v, sw_v)
|
|
369
|
+
|
|
370
|
+
# compute the dot products of the triangle unit norms
|
|
371
|
+
e_sw = norm_w[0]*norm_s[0] + norm_w[1]*norm_s[1] + norm_w[2]*norm_s[2]
|
|
372
|
+
e_nw = norm_w[0]*norm_n[0] + norm_w[1]*norm_n[1] + norm_w[2]*norm_n[2]
|
|
373
|
+
e_se = norm_e[0]*norm_s[0] + norm_e[1]*norm_s[1] + norm_e[2]*norm_s[2]
|
|
374
|
+
e_ne = norm_e[0]*norm_n[0] + norm_e[1]*norm_n[1] + norm_e[2]*norm_n[2]
|
|
375
|
+
|
|
376
|
+
if e_sw < self._max_bend*norm_s[3]*norm_w[3]:
|
|
377
|
+
sw_depth = next_depth
|
|
378
|
+
else:
|
|
379
|
+
sw_depth = self._max_depth
|
|
380
|
+
if e_nw < self._max_bend*norm_n[3]*norm_w[3]:
|
|
381
|
+
nw_depth = next_depth
|
|
382
|
+
else:
|
|
383
|
+
nw_depth = self._max_depth
|
|
384
|
+
if e_se < self._max_bend*norm_s[3]*norm_e[3]:
|
|
385
|
+
se_depth = next_depth
|
|
386
|
+
else:
|
|
387
|
+
se_depth = self._max_depth
|
|
388
|
+
if e_ne < self._max_bend*norm_n[3]*norm_e[3]:
|
|
389
|
+
ne_depth = next_depth
|
|
390
|
+
else:
|
|
391
|
+
ne_depth = self._max_depth
|
|
392
|
+
|
|
393
|
+
qtr1_x = min_x + (.325 + random.random()/4)*(mid_x-min_x)
|
|
394
|
+
qtr3_x = mid_x + (.325 + random.random()/4)*(max_x-mid_x)
|
|
395
|
+
qtr1_y = min_y + (.325 + random.random()/4)*(mid_y-min_y)
|
|
396
|
+
qtr3_y = mid_y + (.325 + random.random()/4)*(max_y-mid_y)
|
|
397
|
+
|
|
398
|
+
# function evaluated at the midpoints (possibly random)
|
|
399
|
+
mid_sw_z = self._fcn(qtr1_x,qtr1_y)
|
|
400
|
+
mid_nw_z = self._fcn(qtr1_x,qtr3_y)
|
|
401
|
+
mid_se_z = self._fcn(qtr3_x,qtr1_y)
|
|
402
|
+
mid_ne_z = self._fcn(qtr3_x,qtr3_y)
|
|
403
|
+
|
|
404
|
+
self.extrema([mid_w_z[0], mid_n_z[0], mid_e_z[0], mid_s_z[0], mid_sw_z[0], mid_se_z[0], mid_nw_z[0], mid_sw_z[0]])
|
|
405
|
+
|
|
406
|
+
# recurse into the sub-squares
|
|
407
|
+
sw = self.plot_block(min_x, qtr1_x, mid_x, min_y, qtr1_y, mid_y, sw_z, mid_w_z, mid_s_z, mid_z, mid_sw_z, sw_depth)
|
|
408
|
+
nw = self.plot_block(min_x, qtr1_x, mid_x, mid_y, qtr3_y, max_y, mid_w_z, nw_z, mid_z, mid_n_z, mid_nw_z, nw_depth)
|
|
409
|
+
se = self.plot_block(mid_x, qtr3_x, max_x, min_y, qtr1_y, mid_y, mid_s_z, mid_z, se_z, mid_e_z, mid_se_z, se_depth)
|
|
410
|
+
ne = self.plot_block(mid_x, qtr3_x, max_x, mid_y, qtr3_y, max_y, mid_z, mid_n_z, mid_e_z, ne_z, mid_ne_z, ne_depth)
|
|
411
|
+
|
|
412
|
+
# join the sub-squares
|
|
413
|
+
self.interface(1, sw.right, sw.right_c, se.left, se.left_c)
|
|
414
|
+
self.interface(1, nw.right, nw.right_c, ne.left, ne.left_c)
|
|
415
|
+
self.interface(0, sw.top, sw.top_c, nw.bottom, nw.bottom_c)
|
|
416
|
+
self.interface(0, se.top, se.top_c, ne.bottom, ne.bottom_c)
|
|
417
|
+
|
|
418
|
+
# get the boundary information about the subsquares
|
|
419
|
+
left = sw.left + nw.left[1:]
|
|
420
|
+
left_c = sw.left_c + nw.left_c
|
|
421
|
+
right = se.right + ne.right[1:]
|
|
422
|
+
right_c = se.right_c + ne.right_c
|
|
423
|
+
top = nw.top + ne.top[1:]
|
|
424
|
+
top_c = nw.top_c + ne.top_c
|
|
425
|
+
bottom = sw.bottom + se.bottom[1:]
|
|
426
|
+
bottom_c = sw.bottom_c + se.bottom_c
|
|
427
|
+
|
|
428
|
+
else:
|
|
429
|
+
# just build the square we're in
|
|
430
|
+
if self._g is None:
|
|
431
|
+
sw = [(min_x, min_y, sw_z[0])]
|
|
432
|
+
nw = [(min_x, max_y, nw_z[0])]
|
|
433
|
+
se = [(max_x, min_y, se_z[0])]
|
|
434
|
+
ne = [(max_x, max_y, ne_z[0])]
|
|
435
|
+
c = [[(mid_x, mid_y, mid_z[0])]]
|
|
436
|
+
else:
|
|
437
|
+
sw = [(min_x, min_y, sw_z[0]), sw_z[1]]
|
|
438
|
+
nw = [(min_x, max_y, nw_z[0]), nw_z[1]]
|
|
439
|
+
se = [(max_x, min_y, se_z[0]), se_z[1]]
|
|
440
|
+
ne = [(max_x, max_y, ne_z[0]), ne_z[1]]
|
|
441
|
+
c = [[(mid_x, mid_y, mid_z[0]), mid_z[1]]]
|
|
442
|
+
|
|
443
|
+
left = [sw, nw]
|
|
444
|
+
left_c = c
|
|
445
|
+
top = [nw, ne]
|
|
446
|
+
top_c = c
|
|
447
|
+
right = [se, ne]
|
|
448
|
+
right_c = c
|
|
449
|
+
bottom = [sw, se]
|
|
450
|
+
bottom_c = c
|
|
451
|
+
|
|
452
|
+
return PlotBlock(left, left_c, top, top_c, right, right_c, bottom, bottom_c)
|
|
453
|
+
|
|
454
|
+
def interface(self, n, p, p_c, q, q_c):
|
|
455
|
+
"""
|
|
456
|
+
Take a pair of lists of points, and compares the (n)th coordinate, and
|
|
457
|
+
"zips" the lists together into one. The "centers", supplied in p_c and
|
|
458
|
+
q_c are matched up such that the lists describe triangles whose sides
|
|
459
|
+
are "perfectly" aligned. This algorithm assumes that p and q start and
|
|
460
|
+
end at the same point, and are sorted smallest to largest.
|
|
461
|
+
|
|
462
|
+
TESTS::
|
|
463
|
+
|
|
464
|
+
sage: from sage.plot.plot3d.tri_plot import TrianglePlot, TriangleFactory
|
|
465
|
+
sage: tf = TriangleFactory()
|
|
466
|
+
sage: t = TrianglePlot(tf, lambda x,y: x^2 - y*x, (0, -2), (0, 2), max_depth=3)
|
|
467
|
+
sage: t.interface(1, [[(-1/4, 0, 1/16)], [(-1/4, 1/4, 1/8)]], [[(-1/8, 1/8, 1/32)]], [[(-1/4, 0, 1/16)], [(-1/4, 1/4, 1/8)]], [[(-3/8, 1/8, 3/16)]])
|
|
468
|
+
sage: t._objects[-1].get_vertices()
|
|
469
|
+
((-1/4, 0, 1/16), (-1/4, 1/4, 1/8), (-3/8, 1/8, 3/16))
|
|
470
|
+
"""
|
|
471
|
+
m = [p[0]] # a sorted union of p and q
|
|
472
|
+
mpc = [p_c[0]] # centers from p_c corresponding to m
|
|
473
|
+
mqc = [q_c[0]] # centers from q_c corresponding to m
|
|
474
|
+
|
|
475
|
+
i = 1
|
|
476
|
+
j = 1
|
|
477
|
+
|
|
478
|
+
while i < len(p_c) or j < len(q_c):
|
|
479
|
+
if p[i][0][n] == q[j][0][n]:
|
|
480
|
+
m.append(p[i])
|
|
481
|
+
mpc.append(p_c[i])
|
|
482
|
+
mqc.append(q_c[j])
|
|
483
|
+
i += 1
|
|
484
|
+
j += 1
|
|
485
|
+
elif p[i][0][n] < q[j][0][n]:
|
|
486
|
+
m.append(p[i])
|
|
487
|
+
mpc.append(p_c[i])
|
|
488
|
+
mqc.append(mqc[-1])
|
|
489
|
+
i += 1
|
|
490
|
+
else:
|
|
491
|
+
m.append(q[j])
|
|
492
|
+
mpc.append(mpc[-1])
|
|
493
|
+
mqc.append(q_c[j])
|
|
494
|
+
j += 1
|
|
495
|
+
|
|
496
|
+
m.append(p[-1])
|
|
497
|
+
|
|
498
|
+
self.triangulate(m, mpc)
|
|
499
|
+
self.triangulate(m, mqc)
|
|
500
|
+
|
|
501
|
+
def triangulate(self, p, c):
|
|
502
|
+
"""
|
|
503
|
+
Pass in a list of edge points (p) and center points (c).
|
|
504
|
+
Triangles will be rendered between consecutive edge points and the
|
|
505
|
+
center point with the same index number as the earlier edge point.
|
|
506
|
+
|
|
507
|
+
TESTS::
|
|
508
|
+
|
|
509
|
+
sage: from sage.plot.plot3d.tri_plot import TrianglePlot, TriangleFactory
|
|
510
|
+
sage: tf = TriangleFactory()
|
|
511
|
+
sage: t = TrianglePlot(tf, lambda x,y: x^2 - y*x, (0, -2), (0, 2))
|
|
512
|
+
sage: t.triangulate([[[1,0,0],[0,0,1]],[[0,1,1],[1,1,1]]],[[[0,3,1]]])
|
|
513
|
+
sage: t._objects[-1].get_vertices()
|
|
514
|
+
([1, 0, 0], [0, 1, 1], [0, 3, 1])
|
|
515
|
+
"""
|
|
516
|
+
|
|
517
|
+
if self._g is None:
|
|
518
|
+
for i in range(0,len(p)-1):
|
|
519
|
+
self._objects.append(self._triangle_factory.triangle(p[i][0], p[i+1][0], c[i][0]))
|
|
520
|
+
else:
|
|
521
|
+
for i in range(0,len(p)-1):
|
|
522
|
+
self._objects.append(self._triangle_factory.smooth_triangle(p[i][0], p[i+1][0], c[i][0],p[i][1], p[i+1][1], c[i][1]))
|
|
523
|
+
|
|
524
|
+
def extrema(self, list):
|
|
525
|
+
"""
|
|
526
|
+
If the num_colors option has been set, this expands the TrianglePlot's _min and _max
|
|
527
|
+
attributes to include the minimum and maximum of the argument list.
|
|
528
|
+
|
|
529
|
+
TESTS::
|
|
530
|
+
|
|
531
|
+
sage: from sage.plot.plot3d.tri_plot import TrianglePlot, TriangleFactory
|
|
532
|
+
sage: tf = TriangleFactory()
|
|
533
|
+
sage: t = TrianglePlot(tf, lambda x,y: x^2+y^2, (0, 1), (0, 1), num_colors = 3)
|
|
534
|
+
sage: t._min, t._max
|
|
535
|
+
(0, 2)
|
|
536
|
+
sage: t.extrema([-1,2,3,4])
|
|
537
|
+
sage: t._min, t._max
|
|
538
|
+
(-1, 4)
|
|
539
|
+
"""
|
|
540
|
+
if self._num_colors is not None:
|
|
541
|
+
self._min = min(list+[self._min])
|
|
542
|
+
self._max = max(list+[self._max])
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def crossunit(u, v):
|
|
546
|
+
"""
|
|
547
|
+
This function computes triangle normal unit vectors by taking the
|
|
548
|
+
cross-products of the midpoint-to-corner vectors. It always goes
|
|
549
|
+
around clockwise so we're guaranteed to have a positive value near
|
|
550
|
+
1 when neighboring triangles are parallel. However -- crossunit
|
|
551
|
+
doesn't really return a unit vector. It returns the length of the
|
|
552
|
+
vector to avoid numerical instability when the length is nearly zero
|
|
553
|
+
-- rather than divide by nearly zero, we multiply the other side of
|
|
554
|
+
the inequality by nearly zero -- in general, this should work a bit
|
|
555
|
+
better because of the density of floating-point numbers near zero.
|
|
556
|
+
|
|
557
|
+
TESTS::
|
|
558
|
+
|
|
559
|
+
sage: from sage.plot.plot3d.tri_plot import crossunit
|
|
560
|
+
sage: crossunit([0,-1,0],[0,0,1])
|
|
561
|
+
(-1, 0, 0, 1.0)
|
|
562
|
+
"""
|
|
563
|
+
p = (u[1]*v[2] - v[1]*u[2], u[0]*v[2] - v[0]*u[2], u[0]*v[1] - u[1]*v[0])
|
|
564
|
+
l = sqrt(p[0]**2 + p[1]**2 + p[2]**2)
|
|
565
|
+
return (p[0], p[1], p[2], l)
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
class PlotBlock:
|
|
569
|
+
"""
|
|
570
|
+
A container class to hold information about spatial blocks.
|
|
571
|
+
"""
|
|
572
|
+
def __init__(self, left, left_c, top, top_c, right, right_c, bottom, bottom_c):
|
|
573
|
+
"""
|
|
574
|
+
|
|
575
|
+
TESTS::
|
|
576
|
+
|
|
577
|
+
sage: from sage.plot.plot3d.tri_plot import PlotBlock
|
|
578
|
+
sage: pb = PlotBlock([0,0,0],[0,1,0],[0,0,1],[0,0,.5],[1,0,0],[1,1,0],[-2,-2,0],[0,0,0])
|
|
579
|
+
sage: pb.left
|
|
580
|
+
[0, 0, 0]
|
|
581
|
+
"""
|
|
582
|
+
self.left = left
|
|
583
|
+
self.left_c = left_c
|
|
584
|
+
self.top = top
|
|
585
|
+
self.top_c = top_c
|
|
586
|
+
self.right = right
|
|
587
|
+
self.right_c = right_c
|
|
588
|
+
self.bottom = bottom
|
|
589
|
+
self.bottom_c = bottom_c
|