Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

Isosurface.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr
00003  *cr            (C) Copyright 1995-2009 The Board of Trustees of the
00004  *cr                        University of Illinois
00005  *cr                         All Rights Reserved
00006  *cr
00007  ***************************************************************************/
00008 
00009 #include <stdio.h>
00010 #include <stdlib.h>
00011 #include <string.h>
00012 #include <math.h>
00013 #include "Inform.h"
00014 #include "utilities.h"
00015 
00016 #define ISOSURFACE_INTERNAL 1
00017 #include "Isosurface.h"
00018 #include "VolumetricData.h"
00019 
00020 IsoSurface::IsoSurface(void) {}
00021 
00022 void IsoSurface::clear(void) {
00023   numtriangles=0;
00024   v.clear();
00025   n.clear();
00026   f.clear();
00027 }
00028 
00029 
00030 int IsoSurface::compute(const VolumetricData *data, float isovalue, int step) {
00031   int x, y, z; 
00032   int tricount=0;
00033 
00034   vol=data;
00035 
00036   // calculate cell axes
00037   vol->cell_axes(xax, yax, zax);
00038   vol->cell_dirs(xad, yad, zad);
00039 
00040   // flip normals if coordinate system is in the wrong handedness
00041   float vtmp[3];
00042   cross_prod(vtmp, xad, yad);
00043   if (dot_prod(vtmp, zad) < 0) {
00044     xad[0] *= -1;
00045     xad[1] *= -1;
00046     xad[2] *= -1;
00047     yad[0] *= -1;
00048     yad[1] *= -1;
00049     yad[2] *= -1;
00050     zad[0] *= -1;
00051     zad[1] *= -1;
00052     zad[2] *= -1;
00053   }
00054 
00055   for (z=0; z<(vol->zsize - step); z+=step) {
00056     for (y=0; y<(vol->ysize - step); y+=step) {
00057       for (x=0; x<(vol->xsize - step); x+=step) {
00058         tricount += DoCell(x, y, z, isovalue, step);
00059       }
00060     }
00061   }
00062 
00063   return 1;
00064 }
00065 
00066 
00067 
00068 int IsoSurface::DoCell(int x, int y, int z, float isovalue, int step) {
00069   GRIDCELL gc;
00070   int addr, row, plane, rowstep, planestep, tricount;
00071   TRIANGLE tris[5];
00072 
00073   row = vol->xsize; 
00074   plane = vol->xsize * vol->ysize;
00075   addr = z*plane + y*row + x;
00076   rowstep = row*step;
00077   planestep = plane*step;
00078   
00079   gc.val[0] = vol->data[addr                             ];
00080   gc.val[1] = vol->data[addr + step                      ];
00081   gc.val[3] = vol->data[addr +        rowstep            ];
00082   gc.val[2] = vol->data[addr + step + rowstep            ];
00083   gc.val[4] = vol->data[addr +                  planestep];
00084   gc.val[5] = vol->data[addr + step +           planestep];
00085   gc.val[7] = vol->data[addr +        rowstep + planestep];
00086   gc.val[6] = vol->data[addr + step + rowstep + planestep];
00087 
00088   /*
00089      Determine the index into the edge table which
00090      tells us which vertices are inside of the surface
00091   */
00092   int cubeindex = 0;
00093   if (gc.val[0] < isovalue) cubeindex |= 1;
00094   if (gc.val[1] < isovalue) cubeindex |= 2;
00095   if (gc.val[2] < isovalue) cubeindex |= 4;
00096   if (gc.val[3] < isovalue) cubeindex |= 8;
00097   if (gc.val[4] < isovalue) cubeindex |= 16;
00098   if (gc.val[5] < isovalue) cubeindex |= 32;
00099   if (gc.val[6] < isovalue) cubeindex |= 64;
00100   if (gc.val[7] < isovalue) cubeindex |= 128;
00101 
00102   /* Cube is entirely in/out of the surface */
00103   if (edgeTable[cubeindex] == 0)
00104     return(0);
00105   gc.cubeindex = cubeindex;
00106 
00107   gc.p[0].x = (float) x;
00108   gc.p[0].y = (float) y;
00109   gc.p[0].z = (float) z;
00110 
00111   VOXEL_GRADIENT_FAST(vol, x, y, z, &gc.g[0].x)
00112 
00113   gc.p[1].x = (float) x + step;
00114   gc.p[1].y = (float) y;
00115   gc.p[1].z = (float) z;
00116 
00117   VOXEL_GRADIENT_FAST(vol, x + step, y, z, &gc.g[1].x)
00118 
00119   gc.p[3].x = (float) x;
00120   gc.p[3].y = (float) y + step;
00121   gc.p[3].z = (float) z;
00122 
00123   VOXEL_GRADIENT_FAST(vol, x, y + step, z, &gc.g[3].x)
00124 
00125   gc.p[2].x = (float) x + step;
00126   gc.p[2].y = (float) y + step;
00127   gc.p[2].z = (float) z;
00128   
00129   VOXEL_GRADIENT_FAST(vol, x + step, y + step, z, &gc.g[2].x)
00130 
00131   gc.p[4].x = (float) x;
00132   gc.p[4].y = (float) y;
00133   gc.p[4].z = (float) z + step;
00134 
00135   VOXEL_GRADIENT_FAST(vol, x, y, z + step, &gc.g[4].x)
00136 
00137   gc.p[5].x = (float) x + step;
00138   gc.p[5].y = (float) y;
00139   gc.p[5].z = (float) z + step;
00140 
00141   VOXEL_GRADIENT_FAST(vol, x + step, y, z + step, &gc.g[5].x)
00142 
00143   gc.p[7].x = (float) x;
00144   gc.p[7].y = (float) y + step;
00145   gc.p[7].z = (float) z + step;
00146 
00147   VOXEL_GRADIENT_FAST(vol, x, y + step, z + step, &gc.g[7].x)
00148 
00149   gc.p[6].x = (float) x + step;
00150   gc.p[6].y = (float) y + step;
00151   gc.p[6].z = (float) z + step;
00152 
00153   VOXEL_GRADIENT_FAST(vol, x + step, y + step, z + step, &gc.g[6].x)
00154 
00155   // calculate vertices and facets for this cube,
00156   // calculate normals by interpolating between the negated 
00157   // normalized volume gradients for the 8 reference voxels
00158   tricount = Polygonise(gc, isovalue, (TRIANGLE *) &tris);
00159 
00160   if (tricount > 0) {
00161     int i;
00162 
00163     for (i=0; i<tricount; i++) {
00164       float xx, yy, zz;
00165       float xn, yn, zn;
00166 
00167       f.append(numtriangles * 3    );
00168       f.append(numtriangles * 3 + 1);
00169       f.append(numtriangles * 3 + 2);
00170       numtriangles++;
00171 
00172       // add new vertices and normals into vertex and normal lists
00173       xx = tris[i].p[0].x;
00174       yy = tris[i].p[0].y;
00175       zz = tris[i].p[0].z;
00176 
00177       v.append((float) vol->origin[0] + xx * xax[0] + yy * yax[0] + zz * zax[0]);
00178       v.append((float) vol->origin[1] + xx * xax[1] + yy * yax[1] + zz * zax[1]);
00179       v.append((float) vol->origin[2] + xx * xax[2] + yy * yax[2] + zz * zax[2]);
00180 
00181       xn = tris[i].n[0].x;
00182       yn = tris[i].n[0].y;
00183       zn = tris[i].n[0].z;
00184       n.append((float) xn * xad[0] + yn * yad[0] + zn * zad[0]);
00185       n.append((float) xn * xad[1] + yn * yad[1] + zn * zad[1]);
00186       n.append((float) xn * xad[2] + yn * yad[2] + zn * zad[2]);
00187 
00188       xx = tris[i].p[1].x;
00189       yy = tris[i].p[1].y;
00190       zz = tris[i].p[1].z;
00191       v.append((float) vol->origin[0] + xx * xax[0] + yy * yax[0] + zz * zax[0]);
00192       v.append((float) vol->origin[1] + xx * xax[1] + yy * yax[1] + zz * zax[1]);
00193       v.append((float) vol->origin[2] + xx * xax[2] + yy * yax[2] + zz * zax[2]);
00194       xn = tris[i].n[1].x;
00195       yn = tris[i].n[1].y;
00196       zn = tris[i].n[1].z;
00197       n.append((float) xn * xad[0] + yn * yad[0] + zn * zad[0]);
00198       n.append((float) xn * xad[1] + yn * yad[1] + zn * zad[1]);
00199       n.append((float) xn * xad[2] + yn * yad[2] + zn * zad[2]);
00200 
00201       xx = tris[i].p[2].x;
00202       yy = tris[i].p[2].y;
00203       zz = tris[i].p[2].z;
00204       v.append((float) vol->origin[0] + xx * xax[0] + yy * yax[0] + zz * zax[0]);
00205       v.append((float) vol->origin[1] + xx * xax[1] + yy * yax[1] + zz * zax[1]);
00206       v.append((float) vol->origin[2] + xx * xax[2] + yy * yax[2] + zz * zax[2]);
00207       xn = tris[i].n[2].x;
00208       yn = tris[i].n[2].y;
00209       zn = tris[i].n[2].z;
00210       n.append((float) xn * xad[0] + yn * yad[0] + zn * zad[0]);
00211       n.append((float) xn * xad[1] + yn * yad[1] + zn * zad[1]);
00212       n.append((float) xn * xad[2] + yn * yad[2] + zn * zad[2]);
00213     }
00214   }
00215 
00216   return tricount;
00217 }
00218 
00219 
00220 // normalize surface normals resulting from interpolation between 
00221 // unnormalized volume gradients
00222 void IsoSurface::normalize() {
00223   int i;
00224   for (i=0; i<n.num(); i+=3) {
00225     vec_normalize(&n[i]); 
00226   }  
00227 }
00228 
00229 // merge duplicated vertices detected by a simple windowed search
00230 int IsoSurface::vertexfusion(const VolumetricData *data, int offset, int len) {
00231   int i, j, newverts, oldverts, faceverts, matchcount;
00232 
00233   faceverts = f.num();
00234   oldverts = v.num() / 3; 
00235 
00236   // abort if we get an empty list
00237   if (!faceverts || !oldverts)
00238     return 0;
00239 
00240   int * vmap = new int[oldverts];
00241 
00242   vmap[0] = 0;
00243   newverts = 1;
00244   matchcount = 0;
00245 
00246   for (i=1; i<oldverts; i++) {
00247     int matchindex = -1;
00248     int start = ((newverts - offset) < 0)  ? 0        : (newverts - offset);
00249     int end   = ((start + len) > newverts) ? newverts : (start + len);
00250     int matched = 0;
00251     int vi = i * 3;
00252     for (j=start; j<end; j++) {
00253       int vj = j * 3;
00254       if (v[vi  ] == v[vj  ] && 
00255           v[vi+1] == v[vj+1] &&
00256           v[vi+2] == v[vj+2]) {
00257         matched = 1;
00258         matchindex = j;
00259         matchcount++;
00260         break;
00261       } 
00262     }
00263 
00264     if (matched) {
00265       vmap[i] = matchindex;
00266     } else {
00267       int vn = newverts * 3;
00268       v[vn    ] = v[vi    ];
00269       v[vn + 1] = v[vi + 1];
00270       v[vn + 2] = v[vi + 2];
00271       n[vn    ] = n[vi    ];
00272       n[vn + 1] = n[vi + 1];
00273       n[vn + 2] = n[vi + 2];
00274       vmap[i] = newverts;
00275       newverts++;
00276     } 
00277   }
00278 
00279 //  printf("Info) Vertex fusion: found %d shared vertices of %d, %d unique\n",
00280 //         matchcount, oldverts, newverts);
00281 
00282   // zap the old face, vertex, and normal arrays and replace with the new ones
00283   for (i=0; i<faceverts; i++) {
00284     f[i] = vmap[f[i]];
00285   }
00286   delete [] vmap;
00287 
00288   v.truncatelastn((oldverts - newverts) * 3);
00289   n.truncatelastn((oldverts - newverts) * 3);
00290 
00291   return 0;
00292 }
00293 
00294 
00295 /*
00296    Given a grid cell and an isolevel, calculate the triangular
00297    facets required to represent the isosurface through the cell.
00298    Return the number of triangular facets, the array "triangles"
00299    will be loaded up with the vertices at most 5 triangular facets.
00300         0 will be returned if the grid cell is either totally above
00301    of totally below the isolevel.
00302    This code calculates vertex normals by interpolating the volume gradients.
00303 */
00304 int IsoSurface::Polygonise(const GRIDCELL grid, const float isolevel, TRIANGLE *triangles) {
00305    int i,ntriang;
00306    int cubeindex = grid.cubeindex;
00307    XYZ vertlist[12];
00308    XYZ normlist[12];
00309 
00310    /* Find the vertices where the surface intersects the cube */
00311    if (edgeTable[cubeindex] & 1)
00312       VertexInterp(isolevel, grid, 0, 1, &vertlist[0], &normlist[0]);
00313    if (edgeTable[cubeindex] & 2)
00314       VertexInterp(isolevel, grid, 1, 2, &vertlist[1], &normlist[1]);
00315    if (edgeTable[cubeindex] & 4)
00316       VertexInterp(isolevel, grid, 2, 3, &vertlist[2], &normlist[2]);
00317    if (edgeTable[cubeindex] & 8)
00318       VertexInterp(isolevel, grid, 3, 0, &vertlist[3], &normlist[3]);
00319    if (edgeTable[cubeindex] & 16)
00320       VertexInterp(isolevel, grid, 4, 5, &vertlist[4], &normlist[4]);
00321    if (edgeTable[cubeindex] & 32)
00322       VertexInterp(isolevel, grid, 5, 6, &vertlist[5], &normlist[5]);
00323    if (edgeTable[cubeindex] & 64)
00324       VertexInterp(isolevel, grid, 6, 7, &vertlist[6], &normlist[6]);
00325    if (edgeTable[cubeindex] & 128)
00326       VertexInterp(isolevel, grid, 7, 4, &vertlist[7], &normlist[7]);
00327    if (edgeTable[cubeindex] & 256)
00328       VertexInterp(isolevel, grid, 0, 4, &vertlist[8], &normlist[8]);
00329    if (edgeTable[cubeindex] & 512)
00330       VertexInterp(isolevel, grid, 1, 5, &vertlist[9], &normlist[9]);
00331    if (edgeTable[cubeindex] & 1024)
00332       VertexInterp(isolevel, grid, 2, 6, &vertlist[10], &normlist[10]);
00333    if (edgeTable[cubeindex] & 2048)
00334       VertexInterp(isolevel, grid, 3, 7, &vertlist[11], &normlist[11]);
00335 
00336    /* Create the triangle */
00337    ntriang = 0;
00338    for (i=0;triTable[cubeindex][i]!=-1;i+=3) {
00339      triangles[ntriang].p[0] = vertlist[triTable[cubeindex][i  ]];
00340      triangles[ntriang].p[1] = vertlist[triTable[cubeindex][i+1]];
00341      triangles[ntriang].p[2] = vertlist[triTable[cubeindex][i+2]];
00342      triangles[ntriang].n[0] = normlist[triTable[cubeindex][i  ]];
00343      triangles[ntriang].n[1] = normlist[triTable[cubeindex][i+1]];
00344      triangles[ntriang].n[2] = normlist[triTable[cubeindex][i+2]];
00345      ntriang++;
00346    }
00347 
00348    return ntriang;
00349 }
00350 
00351 
00352 /*
00353    Linearly interpolate the position where an isosurface cuts
00354    an edge between two vertices, each with their own scalar value,
00355    interpolating vertex position and vertex normal based on the 
00356    isovalue.
00357 */
00358 void IsoSurface::VertexInterp(float isolevel, const GRIDCELL grid, int ind1, int ind2, XYZ * vert, XYZ * norm) {
00359   XYZ p1 = grid.p[ind1];
00360   XYZ p2 = grid.p[ind2];
00361   XYZ n1 = grid.g[ind1];
00362   XYZ n2 = grid.g[ind2];
00363   float valp1 = grid.val[ind1];
00364   float valp2 = grid.val[ind2];
00365   float isodiffp1   = isolevel - valp1;
00366   float diffvalp2p1 = valp2 - valp1;
00367   float mu = 0.0f;
00368 
00369   // if the difference between vertex values is zero or nearly
00370   // zero, we can get an IEEE NAN for mu.  We must either avoid this
00371   // by testing the denominator beforehand, by coping with the resulting
00372   // NAN value after the fact.  The only important thing is that mu be
00373   // assigned a value between zero and one.
00374   
00375 #if 0
00376   if (fabsf(isodiffp1) < 0.00001) {
00377     *vert = p1;
00378     *norm = n1;
00379     return;
00380   }
00381 
00382   if (fabsf(isolevel-valp2) < 0.00001) {
00383     *vert = p2;
00384     *norm = n2;
00385     return;
00386   }
00387 
00388   if (fabsf(diffvalp2p1) < 0.00001) {
00389     *vert = p1;
00390     *norm = n1;
00391     return;
00392   }
00393 #endif
00394 
00395   if (fabsf(diffvalp2p1) > 0.0f) 
00396     mu = isodiffp1 / diffvalp2p1;
00397 
00398 #if 0
00399   if (mu > 1.0f)
00400     mu=1.0f;
00401 
00402   if (mu < 0.0f)
00403     mu=0.0f;
00404 #endif
00405 
00406   vert->x = p1.x + mu * (p2.x - p1.x);
00407   vert->y = p1.y + mu * (p2.y - p1.y);
00408   vert->z = p1.z + mu * (p2.z - p1.z);
00409 
00410   norm->x = n1.x + mu * (n2.x - n1.x);
00411   norm->y = n1.y + mu * (n2.y - n1.y);
00412   norm->z = n1.z + mu * (n2.z - n1.z);
00413 }
00414 

Generated on Tue Nov 24 01:40:39 2009 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002