Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages

bloomenthal.cpp

Go to the documentation of this file.
00001 /***** bloomenthal.cpp */
00002 
00003 /*
00004  * C++ code adapted from the
00005  * ANSI C code from the article
00006  * "An Implicit Surface Polygonizer"
00007  * by Jules Bloomenthal
00008  * in "Graphics Gems IV", Academic Press, 1994
00009  *
00010 
00011  * Authored by Jules Bloomenthal, Xerox PARC.
00012  * Copyright (c) Xerox Corporation, 1991.  All rights reserved.
00013  * Permission is granted to reproduce, use and distribute this code for
00014  * any and all purposes, provided that this notice appears in all copies
00015  *
00016  * Last modified 11 Jan 95 by Jules Bloomenthal and Paul Heckbert
00017  * 25 Sep 2000 by John Hart to C++
00018  */
00019 
00020 #include <math.h>
00021 #include <malloc.h>
00022 #include <stdio.h>
00023 #include <sys/types.h>
00024 #include <stdlib.h> // for (int)abs(int)
00025 
00026 #include "bloomenthal.h"
00027 
00028 /**** An Implicit Surface Polygonizer ****/
00029 
00030 
00031 /* polygonize: polygonize the implicit surface function
00032  *   arguments are:
00033  *       double function (double x, double y, double z)
00034  *           the implicit surface function given an arbitrary point
00035  *           return negative for inside, positive for outside
00036  *       double size
00037  *           width of the partitioning cube
00038  *       int bounds
00039  *           max. range of cubes (+/- on the three axes) from first cube
00040  *       double x, y, z
00041  *           coordinates of a starting point on or near the surface
00042  *           may be defaulted to 0., 0., 0.
00043  *       int triproc (i1, i2, i3, vertices)
00044  *               int i1, i2, i3 (indices into the vertex array)
00045  *               jbVertices vertices (the vertex array, indexed from 0)
00046  *           called for each triangle
00047  *           the triangle coordinates are (for i = i1, i2, i3):
00048  *               vertices.ptr[i].position.x, .y, and .z
00049  *           vertices are ccw when viewed from the out (positive) side
00050  *               in a left-handed coordinate system
00051  *           vertex normals point outwards
00052  *           return 1 to continue, 0 to abort
00053  *   returns error or NULL
00054  */
00055 
00056 char * jbProcess::polygonize (
00057 //    double (*in_function)(gmVector3),
00058     Implicit *in_f,
00059     double in_size,
00060     int in_bounds,
00061     gmVector3 x,
00062     int (*in_triproc)(int i1, int i2, int i3, jbVertices vertices))
00063 {
00064     int n;
00065     jbTest in, out;
00066     
00067     f = in_f;
00068     triproc = in_triproc;
00069     size = in_size;
00070     bounds = in_bounds;
00071     delta = size/(double)(RES*RES);
00072 
00073     /* allocate hash tables: */
00074     centers = (jbCenterlist **) mycalloc(HSIZE, sizeof(jbCenterlist *));
00075     corners = (jbCornerlist **) mycalloc(HSIZE,   sizeof(jbCornerlist *));
00076     edges   = (jbEdgelist   **) mycalloc(2*HSIZE, sizeof(jbEdgelist *));
00077 
00078     vertices.count = vertices.max = 0; /* no vertices yet */
00079     vertices.ptr = NULL;
00080     
00081     /* find point on surface, beginning search at (x, y, z):  */
00082     srand(1);
00083     in = find(1 == 1, x);
00084     out = find(0 == 1, x);
00085     if (!in.ok || !out.ok) {
00086         freeprocess();
00087         if (!in.ok) printf ("in not ok\n");
00088         if (!out.ok) printf ("out not ok\n");
00089         return "can't find starting point";
00090     }
00091     start = converge(in.pt, out.pt, in.value);
00092 
00093     /* push initial cube on stack: */
00094     cubes = (jbCubes *) mycalloc(1, sizeof(jbCubes)); /* list of 1 */
00095     cubes->cube.i = cubes->cube.j = cubes->cube.k = 0;
00096     cubes->next = NULL;
00097 
00098     /* set corners of initial cube: */
00099     for (n = 0; n < 8; n++)
00100         cubes->cube.corners[n] = \
00101             setcorner(BIT(n,2), BIT(n,1), BIT(n,0));
00102 
00103     setcenter(0, 0, 0);
00104 
00105     while (cubes != NULL) { /* process active cubes till none left */
00106         jbCube c;
00107         jbCubes *temp = cubes;
00108         c = cubes->cube;
00109 
00110         /* decompose into tetrahedra and polygonize: */
00111         if (!(dotet(&c, JB_LBN, JB_LTN, JB_RBN, JB_LBF) &&
00112               dotet(&c, JB_RTN, JB_LTN, JB_LBF, JB_RBN) &&
00113               dotet(&c, JB_RTN, JB_LTN, JB_LTF, JB_LBF) &&
00114               dotet(&c, JB_RTN, JB_RBN, JB_LBF, JB_RBF) &&
00115               dotet(&c, JB_RTN, JB_LBF, JB_LTF, JB_RBF) &&
00116               dotet(&c, JB_RTN, JB_LTF, JB_RTF, JB_RBF)))
00117          {
00118               freeprocess();
00119               return "aborted";
00120          }
00121 
00122         /* pop current cube from stack */
00123         cubes = cubes->next;
00124         free((char *) temp);
00125         /* test six face directions, maybe add to stack: */
00126         testface(c.i-1, c.j, c.k, &c, JB_L, JB_LBN, JB_LBF, JB_LTN, JB_LTF);
00127         testface(c.i+1, c.j, c.k, &c, JB_R, JB_RBN, JB_RBF, JB_RTN, JB_RTF);
00128         testface(c.i, c.j-1, c.k, &c, JB_B, JB_LBN, JB_LBF, JB_RBN, JB_RBF);
00129         testface(c.i, c.j+1, c.k, &c, JB_T, JB_LTN, JB_LTF, JB_RTN, JB_RTF);
00130         testface(c.i, c.j, c.k-1, &c, JB_N, JB_LBN, JB_LTN, JB_RBN, JB_RTN);
00131         testface(c.i, c.j, c.k+1, &c, JB_F, JB_LBF, JB_LTF, JB_RBF, JB_RTF);
00132     }
00133     freeprocess();
00134 
00135     return NULL;
00136 }
00137 
00138 char * jbProcess::marchingcubes (
00139     Implicit *in_f,
00140     double in_size,
00141     gmVector3 x0, gmVector3 x1,
00142     int (*in_triproc)(int i1, int i2, int i3, jbVertices vertices))
00143 {
00144     int i,j,k,n,i1,j1,k1;
00145         jbCube cube;
00146         int pos;
00147     
00148     f = in_f;
00149     triproc = in_triproc;
00150     size = in_size;
00151     bounds = 0; // not needed this time
00152     delta = size/(double)(RES*RES);
00153 
00154     /* allocate hash tables: */
00155     centers = (jbCenterlist **) mycalloc(HSIZE, sizeof(jbCenterlist *));
00156     corners = (jbCornerlist **) mycalloc(HSIZE,   sizeof(jbCornerlist *));
00157     edges   = (jbEdgelist   **) mycalloc(2*HSIZE, sizeof(jbEdgelist *));
00158 
00159     vertices.count = vertices.max = 0; /* no vertices yet */
00160     vertices.ptr = NULL;
00161 
00162 
00163         // add noise to hopefully nudge precise surfaces off lattice
00164 
00165         srand(1);
00166         start = x1 - size*gmVector3(.5,.5,.5) - 0.001*size*gmVector3(RAND(),RAND(),RAND());
00167 
00168         i1 = (int)((x1[0] - x0[0])/size);
00169         j1 = (int)((x1[1] - x0[1])/size);
00170         k1 = (int)((x1[2] - x0[2])/size);
00171 
00172     for (k = 0; k <= k1; k++) {
00173                 for (j = 0; j <= j1; j++) {
00174                         for (i = 0; i <= i1; i++) {
00175 
00176                                 cube.i = i;
00177                                 cube.j = j;
00178                                 cube.k = k;
00179 
00180                                 pos = 0;
00181 
00182                             /* set corners of cube: */
00183                                 for (n = 0; n < 8; n++) {
00184                                         cube.corners[n] = setcorner(i+BIT(n,2), j+BIT(n,1), k+BIT(n,0));
00185                                         if (cube.corners[n]->value > 0.0) pos++;
00186                                 }
00187 
00188                                 if (pos == 0 || pos == 8)
00189                                         continue;       /* cube probably empty */
00190 
00191                                 dotet(&cube, JB_LBN, JB_LTN, JB_RBN, JB_LBF);
00192                                 dotet(&cube, JB_RTN, JB_LTN, JB_LBF, JB_RBN);
00193                                 dotet(&cube, JB_RTN, JB_LTN, JB_LTF, JB_LBF);
00194                                 dotet(&cube, JB_RTN, JB_RBN, JB_LBF, JB_RBF);
00195                                 dotet(&cube, JB_RTN, JB_LBF, JB_LTF, JB_RBF);
00196                                 dotet(&cube, JB_RTN, JB_LTF, JB_RTF, JB_RBF);
00197                         }
00198                 }
00199         }
00200 
00201     freeprocess();
00202 
00203     return NULL;
00204 }
00205 
00206 
00207 /* freeprocess: free all allocated memory */
00208 
00209 void jbProcess::freeprocess() {
00210     int index;
00211     jbCornerlist *l, *lnext;
00212     jbCenterlist *cl, *clnext;
00213     jbEdgelist *edge, *edgenext;
00214 
00215     for (index = 0; index < HSIZE; index++)
00216         for (l = corners[index]; l; l = lnext) {
00217             lnext = l->next;
00218             free((char *) l);           /* free CORNERLIST */
00219         }
00220     for (index = 0; index < HSIZE; index++)
00221         for (cl = centers[index]; cl; cl = clnext) {
00222             clnext = cl->next;
00223             free((char *) cl);          /* free CENTERLIST */
00224         }
00225     for (index = 0; index < 2*HSIZE; index++)
00226         for (edge = edges[index]; edge; edge = edgenext) {
00227             edgenext = edge->next;
00228             free((char *) edge);        /* free EDGELIST */
00229         }
00230     free((char *) edges);            /* free array EDGELIST ptrs */
00231     free((char *) corners);          /* free array CORNERLIST ptrs */
00232     free((char *) centers);          /* free array CENTERLIST ptrs */
00233     if (vertices.ptr)
00234         free((char *) vertices.ptr); /* free VERTEX array */
00235 }
00236 
00237 /* testface: given cube at lattice (i, j, k), and four corners of face,
00238  * if surface crosses face, compute other four corners of adjacent cube
00239  * and add new cube to cube stack */
00240 
00241 void jbProcess::testface (
00242     int i, int j, int k,
00243     jbCube *old,
00244     int face, int c1, int c2, int c3, int c4)
00245 {
00246     jbCube newcube;
00247     jbCubes *oldcubes = cubes;
00248     static int facebit[6] = {2, 2, 1, 1, 0, 0};
00249     int n, pos = old->corners[c1]->value > 0.0 ? 1 : 0;
00250     int bit = facebit[face];
00251 
00252     /* test if  no surface crossing, cube out of bounds, or prev. visited? */
00253     if ((old->corners[c2]->value > 0) == pos &&
00254         (old->corners[c3]->value > 0) == pos &&
00255         (old->corners[c4]->value > 0) == pos) return;
00256     if (abs(i) > bounds || abs(j) > bounds || abs(k) > bounds)
00257         return;
00258     if (setcenter(i, j, k)) return;
00259 
00260     /* create new cube: */
00261     newcube.i = i;
00262     newcube.j = j;
00263     newcube.k = k;
00264     for (n = 0; n < 8; n++) newcube.corners[n] = NULL;
00265     newcube.corners[FLIP(c1, bit)] = old->corners[c1];
00266     newcube.corners[FLIP(c2, bit)] = old->corners[c2];
00267     newcube.corners[FLIP(c3, bit)] = old->corners[c3];
00268     newcube.corners[FLIP(c4, bit)] = old->corners[c4];
00269     for (n = 0; n < 8; n++)
00270         if (newcube.corners[n] == NULL) newcube.corners[n] =
00271             setcorner(i+BIT(n,2), j+BIT(n,1), k+BIT(n,0));
00272 
00273     /*add cube to top of stack: */
00274     cubes = (jbCubes *) mycalloc(1, sizeof(jbCubes));
00275     cubes->cube = newcube;
00276     cubes->next = oldcubes;
00277 }
00278 
00279 
00280 /* setpoint: set point location given lattice location */
00281 
00282 gmVector3 jbProcess::setpoint (int i, int j, int k)
00283 {
00284         return start - size*gmVector3(i-0.5,j-0.5,k-0.5);
00285 }
00286 
00287 
00288 /* setcorner: return corner with the given lattice location
00289    set (and cache) its function value */
00290 
00291 jbCornerlist * jbProcess::setcorner (int i, int j, int k)
00292 {
00293     /* for speed, do corner value caching here */
00294     int index = HASH(i, j, k);
00295     jbCornerlist *l = corners[index];
00296     gmVector3 pt;
00297     
00298     for (; l != NULL; l = l->next)
00299         if (l->i == i && l->j == j && l->k == k) return l;
00300             
00301     pt = setpoint (i, j, k);
00302     l = (jbCornerlist *) mycalloc(1, sizeof(jbCornerlist));
00303     l->i = i; l->j = j; l->k = k;
00304     l->value = f->proc(pt);
00305     l->next = corners[index];
00306     corners[index] = l;
00307     return l;
00308 }
00309 
00310 
00311 /* find: search for point with value of given sign (0: neg, 1: pos) */
00312 
00313 jbTest jbProcess::find(int sign, gmVector3 x)
00314 {
00315     int i;
00316         jbTest test;
00317     double range = size;
00318 
00319     test.ok = 1;
00320     for (i = 0; i < 10000; i++) {
00321                 test.pt = x + range*gmVector3(RAND()-0.5,RAND()-0.5,RAND()-0.5);
00322         test.value = f->proc(test.pt);
00323         if (sign == (test.value > 0.0)) return test;
00324         range = range*1.0005; /* slowly expand search outwards */
00325     }
00326     test.ok = 0;
00327     return test;
00328 }
00329 
00330 
00331 /**** Tetrahedral Polygonization ****/
00332 
00333 
00334 /* dotet: triangulate the tetrahedron
00335  * b, c, d should appear clockwise when viewed from a
00336  * return 0 if client aborts, 1 otherwise */
00337 
00338 int jbProcess::dotet (jbCube *cube, int c1, int c2, int c3, int c4) {
00339     jbCornerlist *a = cube->corners[c1],
00340                                  *b = cube->corners[c2],
00341                                  *c = cube->corners[c3],
00342                                  *d = cube->corners[c4];
00343     int index = 0, apos, bpos, cpos, dpos, e1, e2, e3, e4, e5, e6;
00344 
00345     if (apos = (a->value > 0.0)) index += 8;
00346     if (bpos = (b->value > 0.0)) index += 4;
00347     if (cpos = (c->value > 0.0)) index += 2;
00348     if (dpos = (d->value > 0.0)) index += 1;
00349     /* index now 4-bit number equal to one of the 16 possible cases */
00350     if (apos != bpos) e1 = vertid(a, b);
00351     if (apos != cpos) e2 = vertid(a, c);
00352     if (apos != dpos) e3 = vertid(a, d);
00353     if (bpos != cpos) e4 = vertid(b, c);
00354     if (bpos != dpos) e5 = vertid(b, d);
00355     if (cpos != dpos) e6 = vertid(c, d);
00356     /* 14 productive tet. cases (0000 and 1111 do not yield polygons */
00357     switch (index) {
00358         case 1:  return triproc(e5, e6, e3, vertices);
00359         case 2:  return triproc(e2, e6, e4, vertices);
00360         case 3:  return triproc(e3, e5, e4, vertices) &&
00361                         triproc(e3, e4, e2, vertices);
00362         case 4:  return triproc(e1, e4, e5, vertices);
00363         case 5:  return triproc(e3, e1, e4, vertices) &&
00364                         triproc(e3, e4, e6, vertices);
00365         case 6:  return triproc(e1, e2, e6, vertices) &&
00366                         triproc(e1, e6, e5, vertices);
00367         case 7:  return triproc(e1, e2, e3, vertices);
00368         case 8:  return triproc(e1, e3, e2, vertices);
00369         case 9:  return triproc(e1, e5, e6, vertices) &&
00370                         triproc(e1, e6, e2, vertices);
00371         case 10: return triproc(e1, e3, e6, vertices) &&
00372                         triproc(e1, e6, e4, vertices);
00373         case 11: return triproc(e1, e5, e4, vertices);
00374         case 12: return triproc(e3, e2, e4, vertices) &&
00375                         triproc(e3, e4, e5, vertices);
00376         case 13: return triproc(e6, e2, e4, vertices);
00377         case 14: return triproc(e5, e3, e6, vertices);
00378     }
00379     return 1;
00380 }
00381 
00382 
00383 /**** Storage ****/
00384 
00385 
00386 /* mycalloc: return successful calloc or exit program */
00387 
00388 char *mycalloc (int nitems, int nbytes) {
00389    char *ptr = (char *) calloc(nitems, nbytes);
00390    if (ptr != NULL) return ptr;
00391    fprintf(stderr, "can't calloc %d bytes\n", nitems*nbytes);
00392    exit(1);
00393 }
00394 
00395 
00396 /* setcenter: set (i,j,k) entry of table[]
00397  * return 1 if already set; otherwise, set and return 0 */
00398 
00399 int jbProcess::setcenter(int i, int j, int k) {
00400     int index = HASH(i, j, k);
00401     jbCenterlist *newlist, *l, *q = centers[index];
00402 
00403     for (l = q; l != NULL; l = l->next)
00404         if (l->i == i && l->j == j && l->k == k) return 1;
00405     newlist = (jbCenterlist *) mycalloc(1, sizeof(jbCenterlist));
00406     newlist->i = i; newlist->j = j; newlist->k = k; newlist->next = q;
00407     centers[index] = newlist;
00408     return 0;
00409 }
00410 
00411 
00412 /* setedge: set vertex id for edge */
00413 
00414 void jbProcess::setedge (int i1, int j1, int k1, int i2, int j2, int k2, int vid) {
00415     unsigned int index;
00416     jbEdgelist *newlist;
00417 
00418     if (i1>i2 || (i1==i2 && (j1>j2 || (j1==j2 && k1>k2)))) {
00419         int t=i1; i1=i2; i2=t; t=j1; j1=j2; j2=t; t=k1; k1=k2; k2=t;
00420     }
00421     index = HASH(i1, j1, k1) + HASH(i2, j2, k2);
00422     newlist = (jbEdgelist *) mycalloc(1, sizeof(jbEdgelist));
00423     newlist->i1 = i1; newlist->j1 = j1; newlist->k1 = k1;
00424     newlist->i2 = i2; newlist->j2 = j2; newlist->k2 = k2;
00425     newlist->vid = vid;
00426     newlist->next = edges[index];
00427     edges[index] = newlist;
00428 }
00429 
00430 
00431 /* getedge: return vertex id for edge; return -1 if not set */
00432 
00433 int jbProcess::getedge (int i1, int j1, int k1, int i2, int j2, int k2)
00434 {
00435     jbEdgelist *q;
00436     if (i1>i2 || (i1==i2 && (j1>j2 || (j1==j2 && k1>k2)))) {
00437         int t=i1; i1=i2; i2=t; t=j1; j1=j2; j2=t; t=k1; k1=k2; k2=t;
00438     }
00439     q = edges[HASH(i1, j1, k1)+HASH(i2, j2, k2)];
00440     for (; q != NULL; q = q->next)
00441         if (q->i1 == i1 && q->j1 == j1 && q->k1 == k1 &&
00442             q->i2 == i2 && q->j2 == j2 && q->k2 == k2)
00443             return q->vid;
00444     return -1;
00445 }
00446 
00447 
00448 /**** Vertices ****/
00449 
00450 
00451 /* vertid: return index for vertex on edge:
00452  * c1->value and c2->value are presumed of different sign
00453  * return saved index if any; else compute vertex and save */
00454 
00455 int jbProcess::vertid (jbCornerlist *c1, jbCornerlist *c2) {
00456     jbVertex v;
00457     gmVector3 a, b;
00458     int vid =
00459         getedge(c1->i, c1->j, c1->k, c2->i, c2->j, c2->k);
00460     if (vid != -1) return vid;                /* previously computed */
00461     a = setpoint (c1->i, c1->j, c1->k);
00462     b = setpoint (c2->i, c2->j, c2->k);
00463     v.position = converge (a, b, c1->value); /* posn.  */
00464 //    vid = addtovertices(&vertices, v);                   /* save   */
00465         vid = vertices.add(v);
00466     setedge(c1->i, c1->j, c1->k, c2->i, c2->j, c2->k, vid);
00467     return vid;
00468 }
00469 
00470 
00471 /* addtovertices: add v to sequence of vertices and return its id */
00472 
00473 int jbVertices::add(jbVertex v) {
00474         int i;
00475         jbVertex *newv;
00476         
00477         if (count == max) {
00478       max = count == 0 ? 10 : 2*count;
00479       newv = (jbVertex *) mycalloc((unsigned)max,sizeof(jbVertex));
00480       for (i = 0; i < count; i++) newv[i] = ptr[i];
00481       if (ptr != NULL) free((char *) ptr);
00482       ptr = newv;
00483    }
00484    ptr[count++] = v;
00485    return (count-1);
00486 }
00487 
00488 /* converge: from two points of differing sign, converge to surface */
00489 
00490 gmVector3 jbProcess::converge (gmVector3 p1, gmVector3 p2, double v)
00491 {
00492     int i = 0;
00493     gmVector3 pos, neg, p;
00494 
00495     if (v < 0) {
00496                 pos = p2;
00497                 neg = p1;
00498     }
00499     else {
00500                 pos = p1;
00501                 neg = p2;
00502     }
00503     for (;;) {
00504                 p = 0.5*(pos+neg);
00505         if (i++ == RES) return p;
00506         if ((f->proc(p)) > 0.0) {
00507                         pos = p;
00508         } else {
00509                         neg = p;
00510                 }
00511     }
00512 }

Generated on Mon Jun 28 14:58:13 2004 for Advanced Surface Library by doxygen 1.3.4