BALL  1.4.79
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
contourSurface.h
Go to the documentation of this file.
1 // -*- Mode: C++; tab-width: 2; -*-
2 // vi: set ts=2:
3 //
4 
5 #ifndef BALL_DATATYPE_CONTOURSURFACE_H
6 #define BALL_DATATYPE_CONTOURSURFACE_H
7 
8 #ifndef BALL_COMMON_H
9 # include <BALL/common.h>
10 #endif
11 
12 #ifndef BALL_DATATYPE_REGULARDATA3D_H
14 #endif
15 
16 #ifndef BALL_MATHS_SURFACE_H
17 # include <BALL/MATHS/surface.h>
18 #endif
19 
20 #ifndef BALL_DATATYPE_HASHMAP_H
21 # include <BALL/DATATYPE/hashMap.h>
22 #endif
23 
24 #include <vector>
25 #include <math.h>
26 
27 namespace BALL
28 {
29  template<>
30  BALL_EXPORT HashIndex Hash(const std::pair<Position, Position>& p);
31 
32  //
33  typedef Index FacetArray[256][12];
34 
35  // function defined in contourSurface.C to precompute some tables.
36  BALL_EXPORT extern const FacetArray& getContourSurfaceFacetData(double threshold);
37 
44  template <typename T>
46  : public Surface
47  {
48  public:
49 
53 
56  typedef std::pair<Position, Position> KeyType;
57 
61  typedef Vector3 PointType;
62 
66  typedef std::vector<std::pair<PointType, std::pair<Position, Position> > > VectorType;
68 
72 
75 
77  TContourSurface(T threshold);
78 
80  TContourSurface(const TContourSurface& surface);
81 
83  TContourSurface(const TRegularData3D<T>& data, T threshold = 0.0);
84 
86  virtual ~TContourSurface();
88 
92 
94  const TContourSurface& operator = (const TContourSurface<T>& surface);
95 
97  const TContourSurface<T>& operator << (const TRegularData3D<T>& data);
98 
100  virtual void clear();
102 
106 
108  bool operator == (const TContourSurface<T>& surface) const;
109 
111 
112 
113  protected:
114 
120  class Cube
121  {
122  public:
123 
124  Cube(const TRegularData3D<T>& grid)
125  : grid_(&grid),
127  ptr_(0),
128  spacing_(grid.getSpacing().x, grid.getSpacing().y, grid.getSpacing().z)
129  {
130  // Retrieve the number of points in the grid along the x- and y-axes.
131  Size nx = grid.getSize().x;
132  Size ny = grid.getSize().y;
133 
134  // Compute the offsets in the grid for the eight
135  // corners of the cube (in absolute grid indices).
136  grid_offset_[0] = nx * ny;
137  grid_offset_[1] = 0;
138  grid_offset_[2] = 1;
139  grid_offset_[3] = 1 + nx * ny;
140  grid_offset_[4] = nx * ny + nx;
141  grid_offset_[5] = nx;
142  grid_offset_[6] = nx + 1;
143  grid_offset_[7] = nx * ny + nx + 1;
144  }
145 
146  void setTo(Position p)
147  {
148  current_position_ = p;
149 
150  ptr_ = &(const_cast<TRegularData3D<T>*>(grid_)->getData(current_position_));
151  for (Position i = 0; i < 8; i++)
152  {
153  values[i] = *(ptr_ + grid_offset_[i]);
154  }
155  }
156 
157  inline Vector3 getOrigin() const
158  {
160  }
161 
162  inline const Vector3& getSpacing() const
163  {
164  return spacing_;
165  }
166 
167  inline Vector3 getCoordinates(Position index) const
168  {
169  return grid_->getCoordinates(index);
170  }
171 
173  inline Position getIndex(Position corner) const
174  {
175  return current_position_ + grid_offset_[corner];
176  }
177 
178  void shift()
179  {
180  // Shift the cube by one along the x-axis.
182  ptr_++;
183 
184  // Keep the four old values for x = 1.
185  values[0] = values[3];
186  values[1] = values[2];
187  values[4] = values[7];
188  values[5] = values[6];
189 
190  // Retrieve the four new values.
191  values[3] = *(ptr_ + grid_offset_[3]);
192  values[2] = *(ptr_ + grid_offset_[2]);
193  values[7] = *(ptr_ + grid_offset_[7]);
194  values[6] = *(ptr_ + grid_offset_[6]);
195  }
196 
198  Position computeTopology(double threshold)
199  {
200  static const Position topology_modifier[8] = {1, 2, 4, 8, 16, 32, 64, 128};
201 
202  // The topology is a bitvector constructed by ORing the
203  // bits corresponding to each of the inside/outside status of
204  // of each individual corner.
205  Position topology = 0;
206  for (Position i = 0; i < 8; i++)
207  {
208  topology |= ((values[i] > threshold) ? topology_modifier[i] : 0);
209  }
210 
211  return topology;
212  }
213 
214  // The values at the eight corners of the cube.
215  double values[8];
216 
217  protected:
218 
219  // A pointer to the grid.
221 
222  // The current position of the cube as an absolute index into the grid.
224 
225  // The gridd offsets: what to add to current_index_ to get to the correct
226  // grid coordinate.
228 
229  // A pointer into (nasty hack!) the grid itself. For speed's sake.
230  const T* ptr_;
231 
232  // The spacing of the grid.
234  };
235 
236 
238  void addTriangles_(Cube& cube, const FacetArray& facet_data);
239 
241  void computeTriangles(Size topology, const TRegularData3D<T>& data);
242 
245 
246  //
248  };
249 
252 
253  template <typename T>
255  : threshold_(0.0)
256  {
257  }
258 
259  template <typename T>
261  : threshold_(threshold)
262  {
263  }
264 
265  template <typename T>
267  : threshold_(threshold)
268  {
269  this->operator << (data);
270  }
271 
272  template <typename T>
274  {
275  }
276 
277  template <typename T>
279  : threshold_(from.threshold_)
280  {
281  }
282 
283  template <typename T>
285  {
286  Surface::clear();
287  cut_hash_map_.clear();
288  }
289 
290  template <typename T>
292  {
293  // Avoid self-assignment
294  if (&data != this)
295  {
296  threshold_ = data.threshold_;
297  }
298 
299  return *this;
300  }
301 
302  template <typename T>
304  {
305  return ((threshold_ == data.threshold_)
306  && Surface::operator == (data.data_));
307  }
308 
309  template <typename T>
311  {
312  // Clear the old stuff:
313  clear();
314 
315 
316  // Marching cube algorithm: construct a contour surface from
317  // a volume data set.
318 
319  // Get the dimensions of the volume data set.
320  Vector3 origin = data.getOrigin();
321  Size number_of_cells_x = (Size)data.getSize().x;
322  Size number_of_cells_y = (Size)data.getSize().y;
323  Size number_of_cells_z = (Size)data.getSize().z;
324 
325  // Precompute the facet data. This depends on the threshold!
326  const FacetArray& facet_data = getContourSurfaceFacetData(threshold_);
327 
328  // We start in the left-front-bottom-most corner of the grid.
329  Position current_index = 0;
330  Cube cube(data);
331  for (Position curr_cell_z = 0; curr_cell_z < (number_of_cells_z - 1); curr_cell_z++)
332  {
333  // Determine the start position in the current XY plane.
334  current_index = curr_cell_z * number_of_cells_y * number_of_cells_x;
335 
336  // Walk along the y-axis....
337  for (Position curr_cell_y = 0; curr_cell_y < (number_of_cells_y - 1); curr_cell_y++)
338  {
339  // Retrieve the cube from the current grid position (the first position along
340  // along the x-axis).
341  cube.setTo(current_index);
342 
343  // Walk along the x-axis....
344  for (Position curr_cell_x = 0; (curr_cell_x < (number_of_cells_x - 2)); )
345  {
346  // Compute topology, triangles, and add those triangles to the surface.
347  addTriangles_(cube, facet_data);
348 
349  // Done. cube.shift() will now shift the cube
350  // along the x-axis and efficently retrieve the four new values.
351  curr_cell_x++;
352  cube.shift();
353  }
354 
355  // Add the triangles from the last cube position.
356  addTriangles_(cube, facet_data);
357 
358  // Shift the cube by one along the y-axis.
359  current_index += number_of_cells_x;
360  }
361  }
362 
363  // Normalize the vertex normals.
364  for (Position i = 0; i < normal.size(); i++)
365  {
366  try
367  {
368  normal[i].normalize();
369  }
370  catch (...)
371  {
372  }
373  }
374 
375  // Return this (stream operator, for command chaining...)
376  return *this;
377  }
378 
379  template <typename T>
381  (typename TContourSurface<T>::Cube& cube, const FacetArray& facet_data)
382  {
383  // Some static variables we need below -- since we will
384  // call this rather often, we would rather want to avoid
385  // to many ctor/dtor calls.
386  static Triangle t;
387  static std::pair<Position, Position> key;
388  static std::pair<Position, Position> indices;
389 
390  // The indices of the corners of a cube's twelve edges.
391  static const Position edge_indices[12][2]
392  = {{1, 0}, {1, 2}, {2, 3}, {0, 3}, {5, 4}, {5, 6},
393  {6, 7}, {4, 7}, {0, 4}, {1, 5}, {2, 6}, {3, 7}
394  };
395 
396  // The index (into Vector3) of the axis along which the
397  // current edged runs (0: x, 1: y, 2: z).
398  static const Position edge_axis[12]
399  = {2, 0, 2, 0, 2, 0, 2, 0, 1, 1, 1, 1};
400 
401  // Retrieve some basic grid properties.
402  const Vector3& spacing = cube.getSpacing();
403 
404  // The indices (into Surface::vertex) of the triangle
405  // under construction.
406  TVector3<Position> triangle_vertices;
407 
408  // A counter for the number of vertices already in triangle_vertices
409  Size vertex_counter = 0;
410 
411  // Compute the cube's topology
412  Position topology = cube.computeTopology(threshold_);
413  if (topology == 0)
414  {
415  return;
416  }
417 
418  // Iterate over all 12 edges and determine whether
419  // there's a cut.
420  for (Position i = 0; i < 12; i++)
421  {
422  // facet_data_ defines whether there's a cut for
423  // a given topology and a given edge.
424  Index facet_index = facet_data[topology][i];
425 
426  // There's a cut only for values larger than -1
427  if (facet_index != -1)
428  {
429  // There is a cut -- determine its position along the edge.
430 
431  // The axis: x = 0, y = 1, z = 2 -- used as in index into Vector3.
432  Position edge = edge_axis[facet_index];
433 
434  indices.first = edge_indices[facet_index][0];
435  indices.second = edge_indices[facet_index][1];
436  key.first = cube.getIndex(indices.first);
437  key.second = cube.getIndex(indices.second);
438 
439  // Check whether we computed this cut already.
440  if (!cut_hash_map_.has(key))
441  {
442  // Compute the position of the cut.
443 
444  // Get the position of the d1 point
445  Vector3 pos = cube.getCoordinates(key.first);
446 
447  // Compute the position of the cut along the edge.
448  const double& d1 = cube.values[indices.first];
449  const double& d2 = cube.values[indices.second];
450  pos[edge] += ((double)threshold_ - d1) / (d2 - d1) * spacing[edge];
451 
452  // Store it as a triangle vertex.
453  triangle_vertices[vertex_counter++] = vertex.size();
454 
455  // Store the index of the vertex in the hash map under the
456  // indices of its grid points.
457  cut_hash_map_.insert(std::pair<std::pair<Position, Position>, Position>(key, (Size)vertex.size()));
458 
459  // Create a vertex and a normal (the normal reamins empty for now).
460  vertex.push_back(pos);
461  static Vector3 null_normal(0.0, 0.0, 0.0);
462  normal.push_back(null_normal);
463  }
464  else
465  {
466  // This one we know already! Retrieve it from the hash map.
467  triangle_vertices[vertex_counter++] = cut_hash_map_[key];
468  }
469 
470  // For every three vertices, create a new triangle.
471  if (vertex_counter == 3)
472  {
473  // Create a new triangle.
474  t.v1 = triangle_vertices.x;
475  t.v2 = triangle_vertices.y;
476  t.v3 = triangle_vertices.z;
477  triangle.push_back(t);
478 
479  // We can start with the next one.
480  vertex_counter = 0;
481 
482  // Compute the normals: add the triangle
483  // normals to each of the triangle vertices.
484  // We will average them out to the correct normals later.
485  Vector3 h1(vertex[t.v1] - vertex[t.v2]);
486  Vector3 h2(vertex[t.v3] - vertex[t.v2]);
487  Vector3 current_normal(h1.y * h2.z - h1.z * h2.y,
488  h1.z * h2.x - h2.z * h1.x,
489  h1.x * h2.y - h1.y * h2.x);
490  normal[t.v1] += current_normal;
491  normal[t.v2] += current_normal;
492  normal[t.v3] += current_normal;
493  }
494  }
495  }
496  }
497 }
498 #endif
499 
HashIndex Hash(const T &key)
Definition: hash.h:47
T threshold_
The threshold separating inside and outside.
const IndexType & getSize() const
TContourSurface()
Default constructor.
Cube(const TRegularData3D< T > &grid)
Index FacetArray[256][12]
Vector3 getCoordinates(Position index) const
bool operator==(const TContourSurface< T > &surface) const
Equality operator.
void computeTriangles(Size topology, const TRegularData3D< T > &data)
const Vector3 & getSpacing() const
CoordinateType getCoordinates(const IndexType &index) const
BALL_EXPORT const FacetArray & getContourSurfaceFacetData(double threshold)
TContourSurface< float > ContourSurface
Default type.
Position computeTopology(double threshold)
Compute the topology code for the current cube.
std::vector< std::pair< PointType, std::pair< Position, Position > > > VectorType
BALL_SIZE_TYPE Size
const TContourSurface & operator=(const TContourSurface< T > &surface)
Assignment operator.
const TRegularData3D< T > * grid_
const TContourSurface< T > & operator<<(const TRegularData3D< T > &data)
Create a contour surface from a given data set.
const CoordinateType & getOrigin() const
std::pair< Position, Position > KeyType
virtual ~TContourSurface()
Destructor.
HashMap< std::pair< Position, Position >, Position > cut_hash_map_
Position getIndex(Position corner) const
Return the absolute grid position for a given corner.
virtual void clear()
Clear method.
HashMap class based on the STL map (containing serveral convenience functions)
Definition: hashMap.h:73
BALL_SIZE_TYPE HashIndex
void addTriangles_(Cube &cube, const FacetArray &facet_data)
#define BALL_EXPORT
Definition: COMMON/global.h:50