BALL  1.4.2
 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 #ifdef BALL_HAS_HASH_MAP
28 namespace BALL_MAP_NAMESPACE
29 {
30  template<>
31  struct hash<std::pair<BALL::Position, BALL::Position> >
32  {
33  size_t operator () (const std::pair<BALL::Position, BALL::Position>& p) const
34  {return (size_t)(p.first + p.second);}
35  };
36 }
37 #endif
38 
39 namespace BALL
40 {
41  template<>
42  BALL_EXPORT HashIndex Hash(const std::pair<Position, Position>& p);
43 
44  //
45  typedef Index FacetArray[256][12];
46 
47  // function defined in contourSurface.C to precompute some tables.
48  BALL_EXPORT extern const FacetArray& getContourSurfaceFacetData(double threshold);
49 
56  template <typename T>
58  : public Surface
59  {
60  public:
61 
65 
68  typedef std::pair<Position, Position> KeyType;
69 
73  typedef Vector3 PointType;
74 
78  typedef std::vector<std::pair<PointType, std::pair<Position, Position> > > VectorType;
80 
84 
87 
89  TContourSurface(T threshold);
90 
92  TContourSurface(const TContourSurface& surface);
93 
95  TContourSurface(const TRegularData3D<T>& data, T threshold = 0.0);
96 
98  virtual ~TContourSurface();
100 
104 
106  const TContourSurface& operator = (const TContourSurface<T>& surface);
107 
109  const TContourSurface<T>& operator << (const TRegularData3D<T>& data);
110 
112  virtual void clear();
114 
118 
120  bool operator == (const TContourSurface<T>& surface) const;
121 
123 
124 
125  protected:
126 
132  class Cube
133  {
134  public:
135 
136  Cube(const TRegularData3D<T>& grid)
137  : grid_(&grid),
139  ptr_(0),
140  spacing_(grid.getSpacing().x, grid.getSpacing().y, grid.getSpacing().z)
141  {
142  // Retrieve the number of points in the grid along the x- and y-axes.
143  Size nx = grid.getSize().x;
144  Size ny = grid.getSize().y;
145 
146  // Compute the offsets in the grid for the eight
147  // corners of the cube (in absolute grid indices).
148  grid_offset_[0] = nx * ny;
149  grid_offset_[1] = 0;
150  grid_offset_[2] = 1;
151  grid_offset_[3] = 1 + nx * ny;
152  grid_offset_[4] = nx * ny + nx;
153  grid_offset_[5] = nx;
154  grid_offset_[6] = nx + 1;
155  grid_offset_[7] = nx * ny + nx + 1;
156  }
157 
158  void setTo(Position p)
159  {
160  current_position_ = p;
161 
162  ptr_ = &(const_cast<TRegularData3D<T>*>(grid_)->getData(current_position_));
163  for (Position i = 0; i < 8; i++)
164  {
165  values[i] = *(ptr_ + grid_offset_[i]);
166  }
167  }
168 
169  inline Vector3 getOrigin() const
170  {
172  }
173 
174  inline const Vector3& getSpacing() const
175  {
176  return spacing_;
177  }
178 
179  inline Vector3 getCoordinates(Position index) const
180  {
181  return grid_->getCoordinates(index);
182  }
183 
185  inline Position getIndex(Position corner) const
186  {
187  return current_position_ + grid_offset_[corner];
188  }
189 
190  void shift()
191  {
192  // Shift the cube by one along the x-axis.
194  ptr_++;
195 
196  // Keep the four old values for x = 1.
197  values[0] = values[3];
198  values[1] = values[2];
199  values[4] = values[7];
200  values[5] = values[6];
201 
202  // Retrieve the four new values.
203  values[3] = *(ptr_ + grid_offset_[3]);
204  values[2] = *(ptr_ + grid_offset_[2]);
205  values[7] = *(ptr_ + grid_offset_[7]);
206  values[6] = *(ptr_ + grid_offset_[6]);
207  }
208 
210  Position computeTopology(double threshold)
211  {
212  static const Position topology_modifier[8] = {1, 2, 4, 8, 16, 32, 64, 128};
213 
214  // The topology is a bitvector constructed by ORing the
215  // bits corresponding to each of the inside/outside status of
216  // of each individual corner.
217  Position topology = 0;
218  for (Position i = 0; i < 8; i++)
219  {
220  topology |= ((values[i] > threshold) ? topology_modifier[i] : 0);
221  }
222 
223  return topology;
224  }
225 
226  // The values at the eight corners of the cube.
227  double values[8];
228 
229  protected:
230 
231  // A pointer to the grid.
233 
234  // The current position of the cube as an absolute index into the grid.
236 
237  // The gridd offsets: what to add to current_index_ to get to the correct
238  // grid coordinate.
240 
241  // A pointer into (nasty hack!) the grid itself. For speed's sake.
242  const T* ptr_;
243 
244  // The spacing of the grid.
246  };
247 
248 
250  void addTriangles_(Cube& cube, const FacetArray& facet_data);
251 
253  void computeTriangles(Size topology, const TRegularData3D<T>& data);
254 
257 
258  //
260  };
261 
264 
265  template <typename T>
267  : threshold_(0.0)
268  {
269  }
270 
271  template <typename T>
273  : threshold_(threshold)
274  {
275  }
276 
277  template <typename T>
279  : threshold_(threshold)
280  {
281  this->operator << (data);
282  }
283 
284  template <typename T>
286  {
287  }
288 
289  template <typename T>
291  : threshold_(from.threshold_)
292  {
293  }
294 
295  template <typename T>
297  {
298  Surface::clear();
299  cut_hash_map_.clear();
300  }
301 
302  template <typename T>
304  {
305  // Avoid self-assignment
306  if (&data != this)
307  {
308  threshold_ = data.threshold_;
309  }
310 
311  return *this;
312  }
313 
314  template <typename T>
316  {
317  return ((threshold_ == data.threshold_)
318  && Surface::operator == (data.data_));
319  }
320 
321  template <typename T>
323  {
324  // Clear the old stuff:
325  clear();
326 
327 
328  // Marching cube algorithm: construct a contour surface from
329  // a volume data set.
330 
331  // Get the dimensions of the volume data set.
332  Vector3 origin = data.getOrigin();
333  Size number_of_cells_x = (Size)data.getSize().x;
334  Size number_of_cells_y = (Size)data.getSize().y;
335  Size number_of_cells_z = (Size)data.getSize().z;
336 
337  // Precompute the facet data. This depends on the threshold!
338  const FacetArray& facet_data = getContourSurfaceFacetData(threshold_);
339 
340  // We start in the left-front-bottom-most corner of the grid.
341  Position current_index = 0;
342  Cube cube(data);
343  for (Position curr_cell_z = 0; curr_cell_z < (number_of_cells_z - 1); curr_cell_z++)
344  {
345  // Determine the start position in the current XY plane.
346  current_index = curr_cell_z * number_of_cells_y * number_of_cells_x;
347 
348  // Walk along the y-axis....
349  for (Position curr_cell_y = 0; curr_cell_y < (number_of_cells_y - 1); curr_cell_y++)
350  {
351  // Retrieve the cube from the current grid position (the first position along
352  // along the x-axis).
353  cube.setTo(current_index);
354 
355  // Walk along the x-axis....
356  for (Position curr_cell_x = 0; (curr_cell_x < (number_of_cells_x - 2)); )
357  {
358  // Compute topology, triangles, and add those triangles to the surface.
359  addTriangles_(cube, facet_data);
360 
361  // Done. cube.shift() will now shift the cube
362  // along the x-axis and efficently retrieve the four new values.
363  curr_cell_x++;
364  cube.shift();
365  }
366 
367  // Add the triangles from the last cube position.
368  addTriangles_(cube, facet_data);
369 
370  // Shift the cube by one along the y-axis.
371  current_index += number_of_cells_x;
372  }
373  }
374 
375  // Normalize the vertex normals.
376  for (Position i = 0; i < normal.size(); i++)
377  {
378  try
379  {
380  normal[i].normalize();
381  }
382  catch (...)
383  {
384  }
385  }
386 
387  // Return this (stream operator, for command chaining...)
388  return *this;
389  }
390 
391  template <typename T>
393  (typename TContourSurface<T>::Cube& cube, const FacetArray& facet_data)
394  {
395  // Some static variables we need below -- since we will
396  // call this rather often, we would rather want to avoid
397  // to many ctor/dtor calls.
398  static Triangle t;
399  static std::pair<Position, Position> key;
400  static std::pair<Position, Position> indices;
401 
402  // The indices of the corners of a cube's twelve edges.
403  static const Position edge_indices[12][2]
404  = {{1, 0}, {1, 2}, {2, 3}, {0, 3}, {5, 4}, {5, 6},
405  {6, 7}, {4, 7}, {0, 4}, {1, 5}, {2, 6}, {3, 7}
406  };
407 
408  // The index (into Vector3) of the axis along which the
409  // current edged runs (0: x, 1: y, 2: z).
410  static const Position edge_axis[12]
411  = {2, 0, 2, 0, 2, 0, 2, 0, 1, 1, 1, 1};
412 
413  // Retrieve some basic grid properties.
414  const Vector3& spacing = cube.getSpacing();
415 
416  // The indices (into Surface::vertex) of the triangle
417  // under construction.
418  TVector3<Position> triangle_vertices;
419 
420  // A counter for the number of vertices already in triangle_vertices
421  Size vertex_counter = 0;
422 
423  // Compute the cube's topology
424  Position topology = cube.computeTopology(threshold_);
425  if (topology == 0)
426  {
427  return;
428  }
429 
430  // Iterate over all 12 edges and determine whether
431  // there's a cut.
432  for (Position i = 0; i < 12; i++)
433  {
434  // facet_data_ defines whether there's a cut for
435  // a given topology and a given edge.
436  Index facet_index = facet_data[topology][i];
437 
438  // There's a cut only for values larger than -1
439  if (facet_index != -1)
440  {
441  // There is a cut -- determine its position along the edge.
442 
443  // The axis: x = 0, y = 1, z = 2 -- used as in index into Vector3.
444  Position edge = edge_axis[facet_index];
445 
446  indices.first = edge_indices[facet_index][0];
447  indices.second = edge_indices[facet_index][1];
448  key.first = cube.getIndex(indices.first);
449  key.second = cube.getIndex(indices.second);
450 
451  // Check whether we computed this cut already.
452  if (!cut_hash_map_.has(key))
453  {
454  // Compute the position of the cut.
455 
456  // Get the position of the d1 point
457  Vector3 pos = cube.getCoordinates(key.first);
458 
459  // Compute the position of the cut along the edge.
460  const double& d1 = cube.values[indices.first];
461  const double& d2 = cube.values[indices.second];
462  pos[edge] += ((double)threshold_ - d1) / (d2 - d1) * spacing[edge];
463 
464  // Store it as a triangle vertex.
465  triangle_vertices[vertex_counter++] = vertex.size();
466 
467  // Store the index of the vertex in the hash map under the
468  // indices of its grid points.
469  cut_hash_map_.insert(std::pair<std::pair<Position, Position>, Position>(key, (Size)vertex.size()));
470 
471  // Create a vertex and a normal (the normal reamins empty for now).
472  vertex.push_back(pos);
473  static Vector3 null_normal(0.0, 0.0, 0.0);
474  normal.push_back(null_normal);
475  }
476  else
477  {
478  // This one we know already! Retrieve it from the hash map.
479  triangle_vertices[vertex_counter++] = cut_hash_map_[key];
480  }
481 
482  // For every three vertices, create a new triangle.
483  if (vertex_counter == 3)
484  {
485  // Create a new triangle.
486  t.v1 = triangle_vertices.x;
487  t.v2 = triangle_vertices.y;
488  t.v3 = triangle_vertices.z;
489  triangle.push_back(t);
490 
491  // We can start with the next one.
492  vertex_counter = 0;
493 
494  // Compute the normals: add the triangle
495  // normals to each of the triangle vertices.
496  // We will average them out to the correct normals later.
497  Vector3 h1(vertex[t.v1] - vertex[t.v2]);
498  Vector3 h2(vertex[t.v3] - vertex[t.v2]);
499  Vector3 current_normal(h1.y * h2.z - h1.z * h2.y,
500  h1.z * h2.x - h2.z * h1.x,
501  h1.x * h2.y - h1.y * h2.x);
502  normal[t.v1] += current_normal;
503  normal[t.v2] += current_normal;
504  normal[t.v3] += current_normal;
505  }
506  }
507  }
508  }
509 }
510 #endif
511