I am proposing here that we develop our own isosurface generator
for use in the cave. The surfaces I have seen generated by Tecplot
appear to have too many small patches in regions where curvatures of
the surface are relatively small, and larger patches would do.
I think we need a surface generator that has the following properties:
What I have to offer at this point is an approach to some of the points outlined above. What we need is a lot of help with the other parts and help with translation of my MATLAB prototypes into more suitable implementations.
Unambiguous decisions follow from two familiar devices:
By way of an example, here is a typical set of isosurfaces.
The function is
F(x,y,z) = x2/4 + sin2y - sin2z |
The surfaces are composed of 3-sided (red), 4-sided (green) and 5-sided (blue) patches. They have been constructed from numerical values of F,x,y and z in simple-cubic arrays. The (MATLAB) prototype of a surface generator of the kind I am suggesting here produced the data for the 18078 patches in 2.6 seconds on a fairly slow (266 Mhz) PC. Similar algorithms that use only tetrahedrons produce triangles and quadrilaterals at a rate slightly greater than 8000 patches per second.
The algorithm that produced the vertices on these patches where F is near zero (say) is one of several different ones that subdivide elementary (empty) cubes that contain a part of the surface into tetrahedrons and/or pyramids (five faces). It proceeds as follows
The point of isolating the computations in pyras() and a similar utility tetras() was to have them all done in an unstructured context. The cubic array of data points in the example was incidental. Advantages of this are:
Here again is a zoomed view of F=[-0.5 0.5] in which the isosurfaces have been partially, doubly covered and sierpinskied.
Here is a slightly different view in which pentagons have been left out and triangles and quadrilaterals have been shrunk.
And here it is as Roy Lichtenstein might have done it.
Or maybe Georges Seurat.
Here are some of the simpler refinements:
The truncated tetrahedron is an octahedron. The three planes cut it in two pyramids, if used one at a time, or in eight tetrahedrons. Another possibility is to choose one of the three diagonals to be the common edge of four tetrahedrons. The eight tetrahedrons then fill equal volumes in the computational grid.
Easy ways to slice a pyramid use a diagonal of the base to define two tetrahedrons or the centroid of the base to define four. There are many other possibilities, including this, with six pyramids and four tetrahedrons.
When the edges of the pyramids have equal lengths, as shown here, and the refinements are tetrahedron -> four tetrahedrons & two pyramids and pyramid -> six pyramids & four tetrahedrons, the tetrahedrons are regular and the pyramids are all geometrically similar.
Here are the utilities (MATLAB prototypes)
function [tri,tet]=tetras(x,y,z,F) % function [tri,tet,pen]=pyras(x,y,z,F) % and function [tri,tet]=tetras(x,y,z,F) % % cols of x,y,z,F at vertices of pyramids or tetrahedrons % for pyramids the first rows are the apex % cols of tri are [xxxyyyzzz]' on patches near F=0 % cols of tet are [xxxxyyyyzzzz]' on patches near F=0 % cols of pen are [xxxxxyyyyyzzzzz]' on patches near F=0 if nargin<4 help tetras; return; end % encode 3 sides and 4 sides in order N=[4 7 6 3 5 2 1 8]; cod=N((1+abs(sum(diag(2.^(0:3))*sign(F))))/2); tri=[]; tet=[]; N=find(cod<5); % 3 sides, 4 ways if N r={[1 1 1],[2 2 2],[3 3 3],[4 4 4]}; % F is pos (neg) s={[2 3 4],[3 1 4],[4 1 2],[1 3 2]}; % F is neg (pos) for k=1:4 n=N(cod(N)==k); if n tri=[tri xyz(x,y,z,F,r{k},s{k},n)]; end; end; end cod(N)=8; cod=cod-4; N=find(cod<4); % 4 sides, 3 ways if N r={[1 2 2 1],[1 3 3 1],[1 4 4 1]}; % F is pos (neg) s={[4 4 3 3],[2 2 4 4],[3 3 2 2]}; % F is neg (pos) for k=1:3 n=N(cod(N)==k); if n tet=[tet xyz(x,y,z,F,r{k},s{k},n)]; end; end; end %----------------------------------------------------------- function w=xyz(x,y,z,F,r,s,n) % lint for F near zero w=(x(r,n).*F(s,n)-x(s,n).*F(r,n))./(F(s,n)-F(r,n)); w=[w;(y(r,n).*F(s,n)-y(s,n).*F(r,n))./(F(s,n)-F(r,n))]; w=[w;(z(r,n).*F(s,n)-z(s,n).*F(r,n))./(F(s,n)-F(r,n))];
function [tri,tet,pen]=pyras(x,y,z,F) % for help, see tetras if nargin<4 help tetras; return; end % encode/decode 4 sides, 5 sides and 3 sides in order N=[13 9 5 3 15 14 8 12 4 2 7 11 6 10 1 16]; cod=N((1+abs(sum(diag(2.^(0:4))*sign(F))))/2); tet=[]; pen=[]; tri=[]; N=find(cod<6); % 4 sides, 5 ways if N r={[1 1 1 1],[2 2 3 3],[3 3 4 4],[4 4 5 5],[5 5 2 2]}; s={[2 3 4 5],[1 5 4 1],[1 2 5 1],[1 3 2 1],[1 4 3 1]}; for k=1:5 n=N(cod(N)==k); if n tet=[tet xyz(x,y,z,F,r{k},s{k},n)]; end; end; end cod(N)=16; cod=cod-5; N=find(cod<5); % 5 sides, 4 ways if N r={[1 1 2 2 1],[1 1 3 3 1],[1 1 4 4 1],[1 1 5 5 1]}; s={[4 5 5 3 3],[5 2 2 4 4],[2 3 3 5 5],[3 4 4 2 2]}; for k=1:4 n=N(cod(N)==k); if n pen=[pen xyz(x,y,z,F,r{k},s{k},n)]; end; end; end cod(N)=16; cod=cod-4; N=find(cod<7); % 3 sides, 6 ways if N r={[2 2 2],[3 3 3],[4 4 4],[5 5 5]}; % F is pos (neg) s={[1 5 3],[1 2 4],[1 3 5],[1 4 2]}; % F is neg (pos) for k=1:4 n=N(cod(N)==k|cod(N)==6-mod(k,2)); if n tri=[tri xyz(x,y,z,F,r{k},s{k},n)]; end; end; end %----------------------------------------------------------- function w=xyz(x,y,z,F,r,s,n) % lint for F near zero w=(x(r,n).*F(s,n)-x(s,n).*F(r,n))./(F(s,n)-F(r,n)); w=[w;(y(r,n).*F(s,n)-y(s,n).*F(r,n))./(F(s,n)-F(r,n))]; w=[w;(z(r,n).*F(s,n)-z(s,n).*F(r,n))./(F(s,n)-F(r,n))];
Here is an example of an m-file that uses pyras() to find surface patches for F=0 when F is numerical data that is specified on a body centered cubic lattice.