contourSurface.h

Go to the documentation of this file.
00001 // -*- Mode: C++; tab-width: 2; -*-
00002 // vi: set ts=2:
00003 //
00004 // $Id: contourSurface.h,v 1.20 2005/12/23 17:01:42 amoll Exp $
00005 //
00006 
00007 #ifndef BALL_DATATYPE_CONTOURSURFACE_H
00008 #define BALL_DATATYPE_CONTOURSURFACE_H
00009 
00010 #ifndef BALL_COMMON_H
00011 # include <BALL/common.h>
00012 #endif
00013 
00014 #ifndef BALL_DATATYPE_REGULARDATA3D_H
00015 # include <BALL/DATATYPE/regularData3D.h>
00016 #endif
00017 
00018 #ifndef BALL_MATHS_SURFACE_H
00019 # include <BALL/MATHS/surface.h>
00020 #endif
00021 
00022 #ifndef BALL_DATATYPE_HASHMAP_H
00023 # include <BALL/DATATYPE/hashMap.h>
00024 #endif
00025 
00026 #include <vector>
00027 #include <math.h>
00028 
00029 #ifdef BALL_HAS_HASH_MAP
00030 namespace BALL_MAP_NAMESPACE
00031 {
00032   template<>
00033   struct hash<std::pair<BALL::Position, BALL::Position> >
00034   {
00035     size_t operator () (const std::pair<BALL::Position, BALL::Position>& p) const
00036     {return (size_t)(p.first + p.second);}
00037   };
00038 }
00039 #endif
00040 
00041 namespace BALL
00042 {
00043   template<>
00044   BALL_EXPORT HashIndex Hash(const std::pair<Position, Position>& p);
00045 
00046   // 
00047   typedef Index FacetArray[256][12];
00048 
00049   // function defined in contourSurface.C to precompute some tables.
00050   BALL_EXPORT extern const FacetArray& getContourSurfaceFacetData(double threshold);
00051 
00058   template <typename T>  
00059   class TContourSurface 
00060     : public Surface
00061   {
00062     public:
00063 
00067     
00070     typedef std::pair<Position, Position> KeyType;
00071 
00075     typedef Vector3 PointType;
00076     
00080     typedef std::vector<std::pair<PointType, std::pair<Position, Position> > > VectorType;
00082 
00086 
00088     TContourSurface();
00089 
00091     TContourSurface(T threshold);
00092 
00094     TContourSurface(const TContourSurface& surface);
00095 
00097     TContourSurface(const TRegularData3D<T>& data, T threshold = 0.0);
00098 
00100     virtual ~TContourSurface();
00102 
00106     
00108     const TContourSurface& operator = (const TContourSurface<T>& surface);
00109 
00111     const TContourSurface<T>& operator << (const TRegularData3D<T>& data);
00112 
00114     virtual void clear();
00116 
00120 
00122     bool operator == (const TContourSurface<T>& surface) const;
00123 
00125 
00126     
00127     protected:
00128 
00134     class Cube
00135     { 
00136       public:
00137 
00138       Cube(const TRegularData3D<T>& grid)
00139         : grid_(&grid),
00140           current_position_(0),
00141           ptr_(0),
00142           spacing_(grid.getSpacing().x, grid.getSpacing().y, grid.getSpacing().z)
00143       {
00144         // Retrieve the number of points in the grid along the x- and y-axes.
00145         Size nx = grid.getSize().x;
00146         Size ny = grid.getSize().y;
00147 
00148         // Compute the offsets in the grid for the eight 
00149         // corners of the cube (in absolute grid indices).
00150         grid_offset_[0] = nx * ny;
00151         grid_offset_[1] = 0;
00152         grid_offset_[2] = 1;
00153         grid_offset_[3] = 1 + nx * ny;
00154         grid_offset_[4] = nx * ny + nx;
00155         grid_offset_[5] = nx;
00156         grid_offset_[6] = nx + 1;
00157         grid_offset_[7] = nx * ny + nx + 1;
00158       }
00159 
00160       void setTo(Position p) 
00161       {
00162         current_position_ = p;
00163 
00164         ptr_ = &(const_cast<TRegularData3D<T>*>(grid_)->getData(current_position_));
00165         for (Position i = 0; i < 8; i++)
00166         {
00167           values[i] = *(ptr_ + grid_offset_[i]);
00168         }
00169       }
00170 
00171       inline Vector3 getOrigin() const
00172       {
00173         return grid_->getCoordinates(current_position_);
00174       }
00175 
00176       inline const Vector3& getSpacing() const
00177       {
00178         return spacing_;
00179       }
00180 
00181       inline Vector3 getCoordinates(Position index) const
00182       {
00183         return grid_->getCoordinates(index);
00184       }
00185 
00187       inline Position getIndex(Position corner) const
00188       {
00189         return current_position_ + grid_offset_[corner];
00190       }
00191 
00192       void shift() 
00193       {
00194         // Shift the cube by one along the x-axis.
00195         current_position_++;
00196         ptr_++;
00197         
00198         // Keep the four old values for x = 1.
00199         values[0] = values[3];
00200         values[1] = values[2];
00201         values[4] = values[7];
00202         values[5] = values[6];
00203         
00204         // Retrieve the four new values.
00205         values[3] = *(ptr_ + grid_offset_[3]);
00206         values[2] = *(ptr_ + grid_offset_[2]);
00207         values[7] = *(ptr_ + grid_offset_[7]);
00208         values[6] = *(ptr_ + grid_offset_[6]);
00209       }
00210 
00212       Position computeTopology(double threshold)  
00213       {
00214         static const Position topology_modifier[8] = {1, 2, 4, 8, 16, 32, 64, 128};
00215 
00216         // The topology is a bitvector constructed by ORing the
00217         // bits corresponding to each of the inside/outside status of
00218         // of each individual corner.
00219         Position topology = 0;
00220         for (Position i = 0; i < 8; i++)
00221         {
00222           topology |= ((values[i] > threshold) ? topology_modifier[i] : 0);
00223         }
00224         
00225         return topology;
00226       }
00227 
00228       // The values at the eight corners of the cube.
00229       double                    values[8];
00230 
00231       protected:
00232 
00233       // A pointer to the grid.
00234       const TRegularData3D<T>*  grid_;
00235 
00236       // The current position of the cube as an absolute index into the grid.
00237       Position                  current_position_;
00238 
00239       // The gridd offsets: what to add to current_index_ to get to the correct
00240       // grid coordinate.
00241       Position                  grid_offset_[8];
00242 
00243       // A pointer into (nasty hack!) the grid itself. For speed's sake.
00244       const T*                  ptr_;
00245     
00246       // The spacing of the grid.
00247       Vector3 spacing_;
00248     };
00249 
00250 
00252     void addTriangles_(Cube& cube, const FacetArray& facet_data);
00253 
00255     void computeTriangles(Size topology, const TRegularData3D<T>& data);
00256 
00258     T threshold_;
00259 
00260     //
00261     HashMap<std::pair<Position, Position>, Position> cut_hash_map_;
00262   };
00263 
00265   typedef TContourSurface<float> ContourSurface;
00266 
00267   template <typename T>
00268   TContourSurface<T>::TContourSurface()
00269     : threshold_(0.0)
00270   {
00271   }
00272 
00273   template <typename T>
00274   TContourSurface<T>::TContourSurface(T threshold)
00275     : threshold_(threshold)
00276   {
00277   }
00278    
00279   template <typename T>
00280   TContourSurface<T>::TContourSurface(const TRegularData3D<T>& data, T threshold)
00281     : threshold_(threshold)
00282   {
00283     this->operator << (data);
00284   }
00285    
00286   template <typename T>
00287   TContourSurface<T>::~TContourSurface()  
00288   {
00289   }
00290 
00291   template <typename T>
00292   TContourSurface<T>::TContourSurface(const TContourSurface<T>& from)
00293     : threshold_(from.threshold_)
00294   {
00295   }
00296 
00297   template <typename T>
00298   void TContourSurface<T>::clear()
00299   {
00300     Surface::clear();
00301     cut_hash_map_.clear();
00302   }
00303 
00304   template <typename T>
00305   const TContourSurface<T>& TContourSurface<T>::operator = (const TContourSurface<T>& data)
00306   {
00307     // Avoid self-assignment
00308     if (&data != this)
00309     {
00310       threshold_ = data.threshold_;
00311     }
00312 
00313     return *this;
00314   }
00315 
00316   template <typename T>
00317   bool TContourSurface<T>:: operator == (const TContourSurface<T>& data) const
00318   {
00319     return ((threshold_    == data.threshold_)
00320              && Surface::operator == (data.data_));
00321   }
00322 
00323   template <typename T>
00324   const TContourSurface<T>& TContourSurface<T>::operator << (const TRegularData3D<T>& data)
00325   {
00326     // Clear the old stuff:
00327     clear();
00328     
00329     
00330     // Marching cube algorithm: construct a contour surface from
00331     // a volume data set.
00332 
00333     // Get the dimensions of the volume data set.
00334     Vector3 origin = data.getOrigin();
00335     Size number_of_cells_x = (Size)data.getSize().x;
00336     Size number_of_cells_y = (Size)data.getSize().y;
00337     Size number_of_cells_z = (Size)data.getSize().z;
00338 
00339     // Precompute the facet data. This depends on the threshold!
00340     const FacetArray& facet_data = getContourSurfaceFacetData(threshold_);
00341 
00342     // We start in the left-front-bottom-most corner of the grid.
00343     Position current_index = 0;
00344     Cube cube(data);
00345     for (Position curr_cell_z = 0; curr_cell_z < (number_of_cells_z - 1); curr_cell_z++)
00346     { 
00347       // Determine the start position in the current XY plane.
00348       current_index = curr_cell_z * number_of_cells_y * number_of_cells_x;
00349 
00350       // Walk along the y-axis....
00351       for (Position curr_cell_y = 0; curr_cell_y < (number_of_cells_y - 1); curr_cell_y++)
00352       {
00353         // Retrieve the cube from the current grid position (the first position along
00354         // along the x-axis).
00355         cube.setTo(current_index);
00356 
00357         // Walk along the x-axis....
00358         for (Position curr_cell_x = 0; (curr_cell_x < (number_of_cells_x - 2)); )
00359         {
00360           // Compute topology, triangles, and add those triangles to the surface.
00361           addTriangles_(cube, facet_data);
00362             
00363           // Done. cube.shift() will now shift the cube
00364           // along the x-axis and efficently retrieve the four new values.
00365           curr_cell_x++;
00366           cube.shift();
00367         }
00368 
00369         // Add the triangles from the last cube position.
00370         addTriangles_(cube, facet_data);
00371   
00372         // Shift the cube by one along the y-axis.
00373         current_index += number_of_cells_x;
00374       }
00375     }
00376 
00377     // Normalize the vertex normals.
00378     for (Position i = 0; i < normal.size(); i++)
00379     {
00380       try
00381       {
00382         normal[i].normalize();
00383       }
00384       catch (...)
00385       {
00386       }
00387     }
00388 
00389     // Return this (stream operator, for command chaining...)
00390     return *this;
00391   }
00392 
00393   template <typename T>
00394   void TContourSurface<T>::addTriangles_
00395     (typename TContourSurface<T>::Cube& cube, const FacetArray& facet_data)
00396   { 
00397     // Some static variables we need below -- since we will
00398     // call this rather often, we would rather want to avoid
00399     // to many ctor/dtor calls.
00400     static Triangle t;
00401     static std::pair<Position, Position> key;
00402     static std::pair<Position, Position> indices;
00403 
00404     // The indices of the corners of a cube's twelve edges.
00405     static const Position edge_indices[12][2] 
00406       = {{1, 0}, {1, 2}, {2, 3}, {0, 3}, {5, 4}, {5, 6},
00407          {6, 7}, {4, 7}, {0, 4}, {1, 5}, {2, 6}, {3, 7}
00408         };
00409 
00410     // The index (into Vector3) of the axis along which the
00411     // current edged runs (0: x, 1: y, 2: z).
00412     static const Position edge_axis[12] 
00413       = {2, 0, 2, 0, 2, 0, 2, 0, 1, 1, 1, 1};
00414 
00415     // Retrieve some basic grid properties.
00416     const Vector3& spacing = cube.getSpacing();
00417 
00418     // The indices (into Surface::vertex) of the triangle
00419     // under construction.
00420     TVector3<Position> triangle_vertices;
00421 
00422     // A counter for the number of vertices already in triangle_vertices
00423     Size vertex_counter = 0;
00424     
00425     // Compute the cube's topology
00426     Position topology = cube.computeTopology(threshold_);
00427     if (topology == 0)
00428     {
00429       return;
00430     }
00431 
00432     // Iterate over all 12 edges and determine whether
00433     // there's a cut. 
00434     for (Position i = 0; i < 12; i++) 
00435     { 
00436       // facet_data_ defines whether there's a cut for
00437       // a given topology and a given edge.
00438       Index facet_index = facet_data[topology][i];
00439 
00440       // There's a cut only for values larger than -1
00441       if (facet_index != -1)
00442       { 
00443         // There is a cut -- determine its position along the edge.
00444 
00445         // The axis: x = 0, y = 1, z = 2 -- used as in index into Vector3.
00446         Position edge = edge_axis[facet_index];
00447 
00448         indices.first = edge_indices[facet_index][0];
00449         indices.second = edge_indices[facet_index][1];
00450         key.first = cube.getIndex(indices.first);
00451         key.second = cube.getIndex(indices.second);
00452 
00453         // Check whether we computed this cut already.
00454         if (!cut_hash_map_.has(key))
00455         {
00456           // Compute the position of the cut.
00457           
00458           // Get the position of the d1 point
00459           Vector3 pos = cube.getCoordinates(key.first);
00460 
00461           // Compute the position of the cut along the edge.
00462           const double& d1 = cube.values[indices.first];
00463           const double& d2 = cube.values[indices.second];
00464           pos[edge] += ((double)threshold_ - d1) / (d2 - d1) * spacing[edge];
00465           
00466           // Store it as a triangle vertex.
00467           triangle_vertices[vertex_counter++] = vertex.size();
00468 
00469           // Store the index of the vertex in the hash map under the
00470           // indices of its grid points.
00471           cut_hash_map_.insert(std::pair<std::pair<Position, Position>, Position>(key, (Size)vertex.size()));
00472 
00473           // Create a vertex and a normal (the normal reamins empty for now).
00474           vertex.push_back(pos);
00475           static Vector3 null_normal(0.0, 0.0, 0.0);
00476           normal.push_back(null_normal);
00477         }
00478         else
00479         {
00480           // This one we know already! Retrieve it from the hash map.
00481           triangle_vertices[vertex_counter++] = cut_hash_map_[key];
00482         }
00483         
00484         // For every three vertices, create a new triangle.
00485         if (vertex_counter == 3)
00486         {
00487           // Create a new triangle.
00488           t.v1 = triangle_vertices.x;
00489           t.v2 = triangle_vertices.y;
00490           t.v3 = triangle_vertices.z;
00491           triangle.push_back(t);
00492 
00493           // We can start with the next one.
00494           vertex_counter = 0;
00495 
00496           // Compute the normals: add the triangle
00497           // normals to each of the triangle vertices.
00498           // We will average them out to the correct normals later.
00499           Vector3 h1(vertex[t.v1] - vertex[t.v2]);
00500           Vector3 h2(vertex[t.v3] - vertex[t.v2]);
00501           Vector3 current_normal(h1.y * h2.z - h1.z * h2.y,
00502                                  h1.z * h2.x - h2.z * h1.x,
00503                                  h1.x * h2.y - h1.y * h2.x);
00504           normal[t.v1] += current_normal;
00505           normal[t.v2] += current_normal;
00506           normal[t.v3] += current_normal;
00507         }
00508       }
00509     }
00510   }
00511 }
00512 #endif
00513