mathPAD Online

mathPAD Logo

The Magazine of the MuPAD Research Group, Vol. 15, Year 2006

Visualizing Waterman Polyhedra with MuPAD

Part 5: Implementing Waterman polyhedra in MuPAD

by M. Majewski

Zayed University, United Arab Emirates

 

Implementing Waterman polyhedra in MuPAD created three interesting computational problems: how to obtain the set of vertices of a given Waterman polyhedron in the most efficient way---most of known algorithms were to slow for for implementing them in MuPAD; how to create the polyhedron structure having its vertices; and finally how to create such polyhedron in MuPAD using means available in OpenGL.

 

Searching for vertices of Waterman Polyhedron

As we said already there are known algorithms to produce points in 3D that can be used to create a Waterman polyhedron. However, most of them are to slow to be implemented in MuPAD. For example, the algorithm used in [1] to create Waterman spheres, after transforming it to MuPAD, turned into code with runtime complexity equal to

Such algorithm will work considerably well in binary, i.e. complied, programs. However, programs in MuPAD, like in any other Computer Algebra System, are interpreted and therefore they are much slower. Therefore, we needed a fast and very efficient algorithm. For this purpose it was created an algorithm that cuts a sphere with a given radius R into slices and chooses from them only those points that have coordinates x+y+z divisible by 2. With some improvements done by Oliver Kluge from SciFace Software GmbH. the algorithm produces results in a considerably faster time. For instance in MuPAD in order to produce w1,100, using the algorithm from [1], we need 1,096,586 ms that is about 18 min. While the optimized algorithm produces the same polyhedron in 2,834 ms, that is about 3 sec.

Below, I show the optimized algorithm implemented as a procedure WatermanData.

WatermanData := proc(center, radius:Type::Positive)
local counter, x, y, z, a, b, c, xra, xrb, yra, yrb, 
zra, zrb,R, Ry, s, radius2, WD; 
begin
   a := center[1]: 
   b := center[2]: 
   c := center[3]: 
   WD := table(): 
   counter := 0: 
   s := radius; 
   radius2 := radius^2: 
   xra := ceil(a-s); 
   xrb := floor(a+s); 
   for x from xra to xrb do 
      R := radius2 - (x-a)*(x-a); 
      if R < 0 then next end_if; 
      s := sqrt(R); 
      yra := ceil(b-s); 
      yrb := floor(b+s); 
      for y from yra to yrb do 
         Ry := R - (y-b)*(y-b); 
         if Ry < 0 then next end_if; //case Ry < 0 
         if Ry = 0 and is(c, Type::Integer) then //case Ry=0 
            if (x+y+c) mod 2 <> 0 then 
               next
            else
               zra := c; 
               zrb := c; 
            end_if 
         else // case Ry > 0 
            s := sqrt(Ry); 
            zra := ceil(c-s); 
            zrb := floor(c+s); 
            if (x+y) mod 2 = 0 then // (x+y)mod2=0 
               if zra mod 2 <> 0 then 
                  if zra <= c then 
                      zra := zra + 1 
                  else 
                      zra := zra - 1 
                  end_if
               end_if 
            else // (x+y) mod 2 <> 0 
               if zra mod 2 = 0 then 
                  if zra <= c then 
                     zra := zra + 1 
                  else 
                     zra := zra - 1 
                  end_if 
               end_if 
            end_if
         end_if; 
         for z from zra to zrb step 2 do 
            counter := counter + 1: 
            WD[counter] := [x, y, z] 
         end_for: 
      end_for: 
   end_for: 
   return( WD ) 
end_proc: 

Observe, the final output from this procedure is a table of point coordinates in 3D. MuPAD tables are very flexible data structures that do not need declarations of size while initiating them.

Using external program to produce convex hull

Producing a table of points was the very first step towards producing a Waterman polyhedron. In the next step we need to filter out those points that do not lie on the surface of the polyhedron and use the remaining points to create convex hull, i.e. the smallest polyhedron containing our points. Instead of implementing a very complex algorithm to produce convex hull, that certainly would be much slower in MuPAD than in binary code, I decided to use a ready program qconvex.exe written in C++. As I did mention before, the QConvex program is part of the Qhull library developed at the National Science and Technology Research Center for Computation and Visualization of Geometric Structures, The Geometry Center, University of Minnesota. The complete Qhull library can be downloaded from http://www.qhull.org.

The simplest way to use QConvex on data produced by the WatermanData procedure is to convert these data to a format acceptable by QConvex, save the data into a text file, apply QConvex on this file and produced results read into MuPAD again.

Converting data produced by WatermanData procedure is quite straightforward task and saving these data into a file is also simple job. In order to call QConvex from inside of MuPAD the shell module was used. Below I show the procedure useQconvex. Observe, the procedure uses a global variable QCONVEX_PATH pointing into a place where the qconvex.exe file was saved in our system, then builds a complete access path to this file, and finally calls qconvex.exe with a list of parameters.

useQconvex := proc() 
local exe_file; 
begin 
   shell::changeDir(tempDir): 
   exe_file := QCONVEX_PATH . "qconvex": 
   exe_file := "\"" . exe_file . "\"": 
   shell::system(exe_file . " o <data.txt >data2.txt"): 
end_proc: 

The resulting file data2.txt contains description of the convex hull in Qconvex format. Below I show an extract from such file with my comments.

3 // dimension of the space
201 14 36 // number of points, nr of faces, number of edges
-4 -2 0 // here start declarations points
-4 -1 -1 
-4 -1 1 
-4 0 -2 
 .....
 4 2 0 // end of declarations of points
   
// declarations of faces starts here, first number is the number of points,
// used to create a face, remaining numbers are consecutive indexes of points.
// For example point [-4,-2,0] has index 0, [-4,-1,-1] index 1, etc. 
4 3 8 5 0 
4 165 93 39 111 
6 5 8 49 118 111 39 
6 3 35 107 116 49 8 
4 116 175 118 49 
6 197 192 151 84 93 165 
6 39 93 84 25 0 5 
6 35 3 0 25 82 89 
4 82 25 84 151 
6 192 195 161 89 82 151 
4 89 161 107 35 
6 118 175 200 197 165 111 
6 107 161 195 200 175 116 
4 200 195 192 197 

The next step would be to use these data to produce faces of a polyhedron.

The SurfaceSet class and its role in Waterman polyhedra creation

It is obvious that faces of a polyhedron are polygons in 3D. We can easily create them using the SurfaceSet plot class. Although the SurfaceSet class has many different options, we needed only the one that takes coordinates of points and builds a sequence of triangles combining these triangles into a polygon. If these points lay on a common plane then we will obtain a flat face of a polyhedron. Here is a very simple example showing how SurfaceSet class works. I deliberately use here points that do not lie on a common plane, and I also show edges of triangles. This will give us more information on how SurfaceSet is created.

data := [ 
   1.5, 2.6, -1.0,
  -1.5, 2.6, 1.0, 
  -3.0, 0.0, -1.0, 
  -1.5, -2.6, 1.0, 
   1.5, -2.6, -1.0, 
   3.0, 0.0, 1.0 
]: 


plot(
   plot::SurfaceSet(data, 
      MeshListType=TriangleFan, 
      MeshVisible=TRUE,
      Axes=Boxed
   ) 
) 


Observe, by default SurfaceSet will not produce edges of triangles, as well as edges of the polygon. Therefore, if we wish to see edges of a polyhedron we have to create them separately as an independent plot object.

In the final creation we produce two groups of objects: faces and edges of the polyhedron. These two groups form a plot object that can be displayed on a computer screen using the plot command.

One of the special features of this implementation are random flat colors. The major reason for this approach was to avoid the general MuPAD coloring scheme with red color in top parts of the graph, and blue color in the bottom. MuPAD default coloring scheme works well with smooth surfaces, but at the same time makes very boring images of polyhedra. Here is the code showing how random colors of faces were created.

face := plot::SurfaceSet(facePoints, 
   MeshListType=TriangleFan, 
   FillColor=[frandom(),frandom(),frandom()], 
   FillColorType=Flat 
): 

Of course, this place is a great opportunity to experiment different coloring schemes. We may return to this problem in another article.

Developing Waterman polyhedra was a very interesting computational problem. The methods used here can be applied to many other similar problems. For example, one may consider completely different set of points in 3D as a starting point for developing polyhedra. Instead of taking a big sphere with radius R, one could take a large octahedron or dodecahedron. This would perhaps lead to a new family of polyhedra. Finally, using external executable programs can enrich calculations in MuPAD and allow access into areas where MuPAD programs doesn't fit or simply were not yet developed.

Literature

  1. Bourke P., http://astronomy.swin.edu.au/~pbourke/geometry/waterman/
  2. Newbold M., http://dogfeathers.com/java/ccppoly.html
  3. Urner K., http://www.4dsolutions.net/ocn/
  4. Waterman S., http://www.watermanpolyhedron.com/
  5. Waterman S., Waterman Polyhedra, Symmetry: Culture and Science, Vol. 13, No.1, 2002
  6. Webb R., http://web.aanet.com.au/robertw/Stella.html
Copyright © 2002-2007 mathPAD Online and the authors.