File: contourSurface.h

package info (click to toggle)
ball 1.5.0%2Bgit20180813.37fc53c-11.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 239,928 kB
  • sloc: cpp: 326,149; ansic: 4,208; python: 2,303; yacc: 1,778; lex: 1,099; xml: 958; sh: 322; javascript: 164; makefile: 88
file content (498 lines) | stat: -rw-r--r-- 13,055 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//

#ifndef BALL_DATATYPE_CONTOURSURFACE_H
#define BALL_DATATYPE_CONTOURSURFACE_H

#ifndef BALL_COMMON_H
# include <BALL/common.h>
#endif

#ifndef BALL_DATATYPE_REGULARDATA3D_H
# include <BALL/DATATYPE/regularData3D.h>
#endif

#ifndef BALL_MATHS_SURFACE_H
# include <BALL/MATHS/surface.h>
#endif

#ifndef BALL_DATATYPE_HASHMAP_H
# include <BALL/DATATYPE/hashMap.h>
#endif

#include <vector>

namespace BALL
{
	template<>
	BALL_EXPORT HashIndex Hash(const std::pair<Position, Position>& p);

	// 
	typedef Index FacetArray[256][12];

	// function defined in contourSurface.C to precompute some tables.
	BALL_EXPORT extern const FacetArray& getContourSurfaceFacetData(double threshold);

  /** This class contains a contour surface.
			Contour surfaces are created from 3D (volume) data sets, in general from 
			data sets store in \link RegularData3D RegularData3D \endlink using a 
			marching cube algorithm.
    	\ingroup  DatatypeMiscellaneous
  */
  template <typename T>  
  class TContourSurface 
		: public Surface
  {
    public:

		/** @name Type definitions
		 */
		//@{
		
		/**
		*/
		typedef std::pair<Position, Position> KeyType;

		/** The point type.
				This type is used to store points in the 3-d regularData.
		*/
		typedef Vector3 PointType;
		
		/** The vector type.
				This type is used to store the edge points of the contour-Surface.
		*/
		typedef std::vector<std::pair<PointType, std::pair<Position, Position> > > VectorType;
		//@}

		/** @name Constructors and Destructors.
		 */
		//@{

		/// Default constructor
		TContourSurface();

		/// Constructor with threshold
		TContourSurface(T threshold);

		/// Copy constructor
		TContourSurface(const TContourSurface& surface);

		/// Constructor for TRegularData3D
		TContourSurface(const TRegularData3D<T>& data, T threshold = 0.0);

		/// Destructor
		virtual ~TContourSurface();
		//@}

		/** @name Assignment
		 */
		//@{
		
		/// Assignment operator
		const TContourSurface& operator = (const TContourSurface<T>& surface);

		/// Create a contour surface from a given data set.
		const TContourSurface<T>& operator << (const TRegularData3D<T>& data);

		/// Clear method
		virtual void clear();
		//@}

		/** @name Predicates
		 */
		//@{

		/// Equality operator
		bool operator == (const TContourSurface<T>& surface) const;

		//@}

		
		protected:

		/** A cube in the grid.
				This class is used to extract information from the grid,
				store the values of the eight corners of a cube, and 
				determine the topology and the triangles in the cube.
		*/
		class Cube
		{	
			public:

			Cube(const TRegularData3D<T>& grid)
				:	grid_(&grid),
					current_position_(0),
					ptr_(0),
					spacing_(grid.getSpacing().x, grid.getSpacing().y, grid.getSpacing().z)
			{
				// Retrieve the number of points in the grid along the x- and y-axes.
				Size nx = grid.getSize().x;
				Size ny = grid.getSize().y;

				// Compute the offsets in the grid for the eight 
				// corners of the cube (in absolute grid indices).
				grid_offset_[0] = nx * ny;
				grid_offset_[1] = 0;
				grid_offset_[2] = 1;
				grid_offset_[3] = 1 + nx * ny;
				grid_offset_[4] = nx * ny + nx;
				grid_offset_[5] = nx;
				grid_offset_[6] = nx + 1;
				grid_offset_[7] = nx * ny + nx + 1;
			}

			void setTo(Position p) 
			{
				current_position_ = p;

				ptr_ = &(const_cast<TRegularData3D<T>*>(grid_)->getData(current_position_));
				for (Position i = 0; i < 8; i++)
				{
					values[i] = *(ptr_ + grid_offset_[i]);
				}
			}

			inline Vector3 getOrigin() const
			{
				return grid_->getCoordinates(current_position_);
			}

			inline const Vector3& getSpacing() const
			{
				return spacing_;
			}

			inline Vector3 getCoordinates(Position index) const
			{
				return grid_->getCoordinates(index);
			}

			/// Return the absolute grid position for a given corner
			inline Position getIndex(Position corner) const
			{
				return current_position_ + grid_offset_[corner];
			}

			void shift() 
			{
				// Shift the cube by one along the x-axis.
				current_position_++;
				ptr_++;
				
				// Keep the four old values for x = 1.
				values[0] = values[3];
				values[1] = values[2];
				values[4] = values[7];
				values[5] = values[6];
				
				// Retrieve the four new values.
				values[3] = *(ptr_ + grid_offset_[3]);
				values[2] = *(ptr_ + grid_offset_[2]);
				values[7] = *(ptr_ + grid_offset_[7]);
				values[6] = *(ptr_ + grid_offset_[6]);
			}

			/// Compute the topology code for the current cube.
			Position computeTopology(double threshold)	
			{
				static const Position topology_modifier[8] = {1, 2, 4, 8, 16, 32, 64, 128};

				// The topology is a bitvector constructed by ORing the
				// bits corresponding to each of the inside/outside status of
				// of each individual corner.
				Position topology = 0;
				for (Position i = 0; i < 8; i++)
				{
					topology |= ((values[i] > threshold) ? topology_modifier[i] : 0);
				}
				
				return topology;
			}

			// The values at the eight corners of the cube.
			double										values[8];

			protected:

			// A pointer to the grid.
			const TRegularData3D<T>*	grid_;

			// The current position of the cube as an absolute index into the grid.
			Position									current_position_;

			// The gridd offsets: what to add to current_index_ to get to the correct
			// grid coordinate.
			Position									grid_offset_[8];

			// A pointer into (nasty hack!) the grid itself. For speed's sake.
			const T*									ptr_;
		
			// The spacing of the grid.
			Vector3	spacing_;
		};


		/// 
		void addTriangles_(Cube& cube, const FacetArray& facet_data);

		///
		void computeTriangles(Size topology, const TRegularData3D<T>& data);

		/// The threshold separating inside and outside
		T threshold_;

		//
		HashMap<std::pair<Position, Position>, Position> cut_hash_map_;
	};

	/// Default type
  typedef TContourSurface<float> ContourSurface;

	template <typename T>
	TContourSurface<T>::TContourSurface()
		: threshold_(0.0)
	{
	}

	template <typename T>
	TContourSurface<T>::TContourSurface(T threshold)
		: threshold_(threshold)
	{
	}
   
	template <typename T>
	TContourSurface<T>::TContourSurface(const TRegularData3D<T>& data, T threshold)
		: threshold_(threshold)
	{
		this->operator << (data);
	}
   
	template <typename T>
	TContourSurface<T>::~TContourSurface()	
  {
  }

	template <typename T>
	TContourSurface<T>::TContourSurface(const TContourSurface<T>& from)
		: threshold_(from.threshold_)
	{
  }

	template <typename T>
	void TContourSurface<T>::clear()
	{
		Surface::clear();
		cut_hash_map_.clear();
	}

	template <typename T>
	const TContourSurface<T>& TContourSurface<T>::operator = (const TContourSurface<T>& data)
	{
		// Avoid self-assignment
		if (&data != this)
		{
			threshold_ = data.threshold_;
		}

		return *this;
	}

	template <typename T>
	bool TContourSurface<T>:: operator == (const TContourSurface<T>& data) const
	{
		return ((threshold_    == data.threshold_)
						 && Surface::operator == (data.data_));
	}

  template <typename T>
	const TContourSurface<T>& TContourSurface<T>::operator << (const TRegularData3D<T>& data)
	{
		// Clear the old stuff:
		clear();
		
		
		// Marching cube algorithm: construct a contour surface from
		// a volume data set.

		// Get the dimensions of the volume data set.
		Vector3 origin = data.getOrigin();
		Size number_of_cells_x = (Size)data.getSize().x;
		Size number_of_cells_y = (Size)data.getSize().y;
		Size number_of_cells_z = (Size)data.getSize().z;

		// Precompute the facet data. This depends on the threshold!
		const FacetArray& facet_data = getContourSurfaceFacetData(threshold_);

		// We start in the left-front-bottom-most corner of the grid.
		Position current_index = 0;
		Cube cube(data);
		for (Position curr_cell_z = 0; curr_cell_z < (number_of_cells_z - 1); curr_cell_z++)
		{ 
			// Determine the start position in the current XY plane.
			current_index = curr_cell_z * number_of_cells_y * number_of_cells_x;

			// Walk along the y-axis....
			for (Position curr_cell_y = 0; curr_cell_y < (number_of_cells_y - 1); curr_cell_y++)
			{
				// Retrieve the cube from the current grid position (the first position along
				// along the x-axis).
				cube.setTo(current_index);

				// Walk along the x-axis....
				for (Position curr_cell_x = 0; (curr_cell_x < (number_of_cells_x - 2)); )
				{
					// Compute topology, triangles, and add those triangles to the surface.
					addTriangles_(cube, facet_data);
						
					// Done. cube.shift() will now shift the cube
					// along the x-axis and efficently retrieve the four new values.
					curr_cell_x++;
					cube.shift();
				}

				// Add the triangles from the last cube position.
				addTriangles_(cube, facet_data);
	
				// Shift the cube by one along the y-axis.
				current_index += number_of_cells_x;
			}
		}

		// Normalize the vertex normals.
		for (Position i = 0; i < normal.size(); i++)
		{
			try
			{
				normal[i].normalize();
			}
			catch (...)
			{
			}
		}

		// Return this (stream operator, for command chaining...)
		return *this;
	}

	template <typename T>
	void TContourSurface<T>::addTriangles_
		(typename TContourSurface<T>::Cube& cube, const FacetArray& facet_data)
	{ 
		// Some static variables we need below -- since we will
		// call this rather often, we would rather want to avoid
		// to many ctor/dtor calls.
		static Triangle t;
		static std::pair<Position, Position> key;
		static std::pair<Position, Position> indices;

		// The indices of the corners of a cube's twelve edges.
		static const Position edge_indices[12][2] 
			= {{1, 0}, {1, 2}, {2, 3}, {0, 3}, {5, 4}, {5, 6},
				 {6, 7}, {4, 7}, {0, 4}, {1, 5}, {2, 6}, {3, 7}
				};

		// The index (into Vector3) of the axis along which the
		// current edged runs (0: x, 1: y, 2: z).
		static const Position edge_axis[12] 
			= {2, 0, 2, 0, 2, 0, 2, 0, 1, 1, 1, 1};

		// Retrieve some basic grid properties.
		const Vector3& spacing = cube.getSpacing();

		// The indices (into Surface::vertex) of the triangle
		// under construction.
		TVector3<Position> triangle_vertices;

		// A counter for the number of vertices already in triangle_vertices
		Size vertex_counter = 0;
		
		// Compute the cube's topology
		Position topology = cube.computeTopology(threshold_);
		if (topology == 0)
		{
			return;
		}

		// Iterate over all 12 edges and determine whether
		// there's a cut. 
		for (Position i = 0; i < 12; i++) 
		{ 
			// facet_data_ defines whether there's a cut for
			// a given topology and a given edge.
			Index facet_index = facet_data[topology][i];

			// There's a cut only for values larger than -1
			if (facet_index != -1)
			{	
				// There is a cut -- determine its position along the edge.

				// The axis: x = 0, y = 1, z = 2 -- used as in index into Vector3.
				Position edge = edge_axis[facet_index];

				indices.first = edge_indices[facet_index][0];
				indices.second = edge_indices[facet_index][1];
				key.first = cube.getIndex(indices.first);
				key.second = cube.getIndex(indices.second);

				// Check whether we computed this cut already.
				if (!cut_hash_map_.has(key))
				{
					// Compute the position of the cut.
					
					// Get the position of the d1 point
					Vector3 pos = cube.getCoordinates(key.first);

					// Compute the position of the cut along the edge.
					const double& d1 = cube.values[indices.first];
					const double& d2 = cube.values[indices.second];
					pos[edge] += ((double)threshold_ - d1) / (d2 - d1) * spacing[edge];
					
					// Store it as a triangle vertex.
					triangle_vertices[vertex_counter++] = vertex.size();

					// Store the index of the vertex in the hash map under the
					// indices of its grid points.
					cut_hash_map_.insert(std::pair<std::pair<Position, Position>, Position>(key, (Size)vertex.size()));

					// Create a vertex and a normal (the normal reamins empty for now).
					vertex.push_back(pos);
					static Vector3 null_normal(0.0, 0.0, 0.0);
					normal.push_back(null_normal);
				}
				else
				{
					// This one we know already! Retrieve it from the hash map.
					triangle_vertices[vertex_counter++] = cut_hash_map_[key];
				}
				
				// For every three vertices, create a new triangle.
				if (vertex_counter == 3)
				{
					// Create a new triangle.
					t.v1 = triangle_vertices.x;
					t.v2 = triangle_vertices.y;
					t.v3 = triangle_vertices.z;
					triangle.push_back(t);

					// We can start with the next one.
					vertex_counter = 0;

					// Compute the normals: add the triangle
					// normals to each of the triangle vertices.
					// We will average them out to the correct normals later.
					Vector3 h1(vertex[t.v1] - vertex[t.v2]);
					Vector3 h2(vertex[t.v3] - vertex[t.v2]);
					Vector3 current_normal(h1.y * h2.z - h1.z * h2.y,
																 h1.z * h2.x - h2.z * h1.x,
																 h1.x * h2.y - h1.y * h2.x);
					normal[t.v1] += current_normal;
					normal[t.v2] += current_normal;
					normal[t.v3] += current_normal;
				}
			}
		}
	}
}
#endif