Subversion Repositories gelsvn

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
85 jab 1
#include <cfloat>
2
#include <algorithm>
3
#include <fstream>
4
#include <iostream>
5
#include <list>
6
 
7
#include "CGLA/Mat2x3f.h"
8
#include "CGLA/Mat2x2f.h"
9
#include "HMesh/Manifold.h"
10
#include "HMesh/VertexCirculator.h"
11
#include "HMesh/FaceCirculator.h"
12
 
13
 
14
#include "Geometry/RGrid.h"
15
#include "Geometry/TrilinFilter.h"
16
#include "Geometry/GradientFilter.h"
17
#include "Geometry/Neighbours.h"
18
#include "Util/HashTable.h"
19
#include "Util/HashKey.h"
20
 
21
#include "fair_polygonize.h"
22
 
23
namespace Geometry
24
{
25
		using namespace HMesh;
26
		using namespace CMP;
27
		using namespace CGLA;
28
		using namespace std;
29
 
30
		namespace 
31
		{
32
				/*
33
					Vectors along edges. For each of six cube faces there are four such vectors
34
					since the faces of a cube are quads.
35
				*/
36
				const Vec3i edges[6][4] = {
37
						{Vec3i(0,-1,0),Vec3i(0,0,1),Vec3i(0,1,0),Vec3i(0,0,-1)},
38
						{Vec3i(0,1,0),Vec3i(0,0,1),Vec3i(0,-1,0),Vec3i(0,0,-1)},
39
						{Vec3i(0,0,-1),Vec3i(1,0,0),Vec3i(0,0,1),Vec3i(-1,0,0)},
40
						{Vec3i(0,0,1),Vec3i(1,0,0),Vec3i(0,0,-1),Vec3i(-1,0,0)},
41
						{Vec3i(-1,0,0),Vec3i(0,1,0),Vec3i(1,0,0),Vec3i(0,-1,0)},
42
						{Vec3i(1,0,0),Vec3i(0,1,0),Vec3i(-1,0,0),Vec3i(0,-1,0)}};
43
 
44
				/* Convert a direction vector to a cube face index. */
45
				int dir_to_face(const Vec3i& dir)
46
				{
47
						assert(dot(dir,dir)==1);
48
 
49
						if(dir[0] != 0)
50
								return (dir[0]<0) ? 0 : 1;
51
						if(dir[1] != 0)
52
								return (dir[1]<0) ? 2 : 3;
53
						if(dir[2] != 0)
54
								return (dir[2]<0) ? 4 : 5;
55
				}
56
 
57
				/* Convert a face index and an edge vector to an edge index. */
58
				int dir_to_edge(int face, const Vec3i& edge)
59
				{
60
						for(int i=0;i<4;++i)
61
								if(edges[face][i] == edge) return i;
62
						assert(0);
63
						return -1;
64
				}
65
 
66
				/* Cube faces are created whenever a voxel that is inside is adjacent
67
					 to a voxel that is outside. Thus the voxel can be seen as a small
68
					 box that either contains material or not. A face is created where
69
					 full and empty boxes meet. A vertex is placed on every box face.
70
 
71
					 A halfedge is created for every edge of the (quadrilateral) face.
72
					 This halfedge connects the face to its neighbour. */
73
				struct CubeFace
74
				{
75
						bool used;
76
						VertexIter vert;
77
						HalfEdgeIter he[4];
78
 
79
						CubeFace();
80
 
81
						HalfEdgeIter& get_he(Manifold* m, int i)
82
								{
83
										if(he[i] == NULL_HALFEDGE_ITER)
84
												he[i] = m->create_halfedge();
85
										return he[i];
86
								}
87
				};
88
 
89
				CubeFace::CubeFace(): used(false), vert(NULL_VERTEX_ITER)
90
				{
91
						for(int i=0;i<4;++i)
92
								he[i] = NULL_HALFEDGE_ITER;
93
				}
94
 
95
 
96
				/* A cube consists of six faces and a position. The cube centre is
97
					 an integer (voxel grid) position. */
98
				struct Cube
99
				{
100
						Vec3i pos;
101
						CubeFace faces[6];
102
 
103
						Cube(const Vec3i& _pos): pos(_pos) {}
104
				};
105
 
106
 
107
				/* The mesher class does the actual work. It is not pretty and it
108
					 is not seen by the user. */
109
				template<class T>
110
				class Mesher
111
				{
112
						const RGrid<T>* const voxel_grid;        // The voxel grid we work on
113
						Manifold* m;		                         // Output manifold
114
						HashTable<HashKey3usi,Cube*> cube_hash;  // Hashtable of cube pointers
115
						list<Cube> cube_table;                   // Linked list containing
116
						// the actual cubes.
117
 
118
				public:
119
 
120
						Mesher(const RGrid<T>* _voxel_grid, // input voxel grid
121
									 Manifold* _m,                // output HMesh
122
									 float iso,                   // iso value
123
									 bool inv_cont);              // invert contour?
124
 
125
						void process_cubes();
126
 
127
						void create_faces();
128
 
129
						void triangulate(float iso);
130
 
131
						void remove_duplicates();
132
 
133
						void push_vertices(float iso);
134
 
135
				};
136
 
137
				template<class T>
138
				inline bool comp(bool d, T a, T b)
139
				{
140
						return d? (a<b) : (b<a); 
141
				}
142
 
143
				template<class T>
144
				Mesher<T>::Mesher(const RGrid<T>* _voxel_grid, 
145
													Manifold* _m, float iso, bool inv_cont): 
146
						voxel_grid(_voxel_grid),
147
						m(_m)
148
				{
149
						/* Loop over all voxels */
150
						for(int k=0;k<voxel_grid->get_dims()[ 2 ];++k)
151
								for(int j=0;j<voxel_grid->get_dims()[ 1 ];++j)
152
										for(int i=0;i<voxel_grid->get_dims()[ 0 ];++i)
153
										{
154
												Vec3i pi0(i,j,k);
155
												float val0 = (*voxel_grid)[pi0];
156
 
157
												/* If the voxel value is above the isovale (or below if we
158
													 are finding the inverse contour) */
159
												if(comp(inv_cont,val0,iso))
160
												{
161
														bool interface_cell = false;
162
														vector<VertexIter> vertices;
163
														Cube* cube;
164
 
165
														// For each neighbouring voxel.
166
														for(int l=0;l<6;++l)
167
														{
168
																// First make sure the voxel is inside the voxel grid.
169
																Vec3i pi1(pi0);
170
																pi1 += N6i[l];
171
																bool indom = voxel_grid->in_domain(pi1);
172
 
173
																/* If the neighbour is **not** in the grid or it 
174
																	 is on the other side of the isovalue */
175
																if(!indom || 
176
																	 !comp(inv_cont,float((*voxel_grid)[pi1]),iso))
177
																{
178
																		/* Create a cube face for the interface between
179
																			 our voxel and this neighbour. */
180
 
181
																		float val1 = iso;
182
																		if(indom)
183
																				val1 = (*voxel_grid)[pi1];
184
 
185
																		/* Store the cube in a hash table. This code is
186
																			 executed the first time we create a face
187
																			 for the cube. In other words only cubes that 
188
																			 actually have faces are stored in the hash */
189
																		if(!interface_cell)
190
																		{
191
																				interface_cell = true;
192
																				Cube** c_ptr;
193
																				cube_hash.create_entry(HashKey3usi(pi0), c_ptr);
194
																				Cube c(pi0);
195
																				cube_table.push_back(c);
196
																				cube = (*c_ptr) = &cube_table.back();
197
																		}
198
																		// Now add the face to the cube.
199
																		int face_idx  = dir_to_face(N6i[l]);
200
																		CubeFace& face = cube->faces[face_idx];
201
																		face.used = true;
202
 
203
																		// Finally, we place a vertex corresponding to the
204
																		// centre of the cube face. The vertex is 
205
																		// positioned on the line between the two voxels
206
																		// (which pierces the new face) at the point where
207
																		// the interpolated value = isovalue
208
																		float t= (iso-val0)/(val1-val0);
209
																		Vec3f p = Vec3f(pi0)*(1.0f-t)+Vec3f(pi1)*t;
210
																		face.vert = m->create_vertex(p);
211
 
212
																		// We mark the vertex with the cube index.
213
																		face.vert->touched = reinterpret_cast<int>(cube);
214
																}
215
														}
216
												}
217
										}		
218
				}
219
 
220
 
221
				/* This complicated function is used to connect the cube faces.
222
					 If the faces are cosidered to be little square pieces of paper, this
223
					 function glues them along the edges. However, it takes care to glue
224
					 faces in such a way as to not create edges with more the two adjacent
225
					 faces. */
226
				template<class T>
227
				void Mesher<T>::process_cubes()
228
				{
229
						/* For each cube in the table of cubes */
230
						typename list<Cube>::iterator cube_iter = cube_table.begin();
231
						for(;cube_iter != cube_table.end(); ++cube_iter)
232
						{
233
								/* For each of the six faces, if the face is used */
234
								Cube& cube = *cube_iter;
235
								for(int i=0;i<6;++i)
236
										if(cube.faces[i].used)
237
										{
238
												Vec3i pos = cube.pos;
239
												Vec3i dir = N6i[i];
240
 
241
												// Let e be one of the four edges of the face
242
												for(int e=0;e<4;++e)
243
												{
244
														Vec3i edge = edges[i][e];	       // Vector _along_ edge
245
														Vec3i to_edge = cross(edge,dir); // vector to edge
246
														CubeFace& face = cube.faces[i];  // The face
247
 
248
														Cube* cube_n;  // Neighbouring cube
249
														Vec3i dir_n;   // direction from center to neighbour face
250
														Vec3i edge_n; // Edge vector for neighbour face
251
 
252
 
253
														/*
254
															Algorithm: Pick faces to glue
255
 
256
															Algorithm which connects cube faces. If both A and B
257
															are above the isovalue and 0 and C are below, A and B
258
															will be seperated, i.e. the 0A and AC face will be 
259
															connected. There is a different out-commented algorithm
260
															at the end of this file which connects AO and 0B
261
															instead.
262
 
263
															Figure.
264
 
265
															0|B
266
															-+-
267
															A|C
268
 
269
															Explanation:
270
 
271
															Let the current cube be A. Since we are here, the voxel
272
															of A is above the isovalue, and the voxel of O is below
273
															since there is a face AO.
274
 
275
															We first check if there is a face between A and C. 
276
															if that is the case, the AO face is connected to the AC 
277
															face. 
278
 
279
															Otherwise, we check if B is also inside the isocontour.
280
															If that is true there is an OB face, and we connect the
281
															A0 and the 0B faces.
282
 
283
															Failing that, there must be a CB face, and we connect
284
															with that.
285
														*/
286
 
287
														// Begin "Pick faces to glue"
288
														Cube* dummy;
289
														if(cube.faces[dir_to_face(to_edge)].used)
290
														{
291
																dir_n = to_edge;
292
																edge_n = - edge; 
293
																cube_n = &cube;  
294
														}
295
														else if(!cube_hash.find_entry(HashKey3usi(pos+dir+to_edge),
296
																													dummy))
297
														{
298
																assert(sqr_length(to_edge)==1);
299
																dir_n = dir;
300
																edge_n = - edge;
301
																cube_hash.find_entry(HashKey3usi(pos + to_edge), 
302
																										 cube_n);
303
														}
304
														else 
305
														{
306
																dir_n = - to_edge;
307
																edge_n = - edge;
308
																cube_n = dummy;
309
														}
310
														// End "Pick faces to glue"
311
 
312
														// Do the actual gluing ...
313
 
314
														/* Let the vertex of this face's halfedge be the one on
315
															 the connected face. */
316
														int i_n = dir_to_face(dir_n);
317
														CubeFace& face_n = cube_n->faces[i_n];
318
														HalfEdgeIter& he = face.get_he(m,e);
319
														face.vert->he = he;
320
														he->vert = face_n.vert;
321
 
322
														/* Link this halfedge to the _next_ halfedge on the 
323
															 connected face. */
324
														int e_n = dir_to_edge(i_n, edge_n);
325
														assert(face_n.used);
326
														HalfEdgeIter& he_n = face_n.get_he(m, (e_n+3)%4);
327
														link(he, he_n);
328
 
329
														/* Glue this halfedge to the corresponding one on the
330
															 connected face.*/
331
														HalfEdgeIter& he_opp = face_n.get_he(m, e_n);
332
														face_n.vert->he = he_opp;
333
														glue(he, he_opp);
334
												}
335
										}
336
 
337
						}
338
				}
339
 
340
				template<class T>
341
				void Mesher<T>::create_faces()
342
				{
343
						// For each halfedge
344
						HalfEdgeIter he0 = m->halfedges_begin();
345
						while(he0 != m->halfedges_end())
346
						{
347
								// If this halfedge is not assigned to a face.
348
								if(he0->face == NULL_FACE_ITER)
349
								{
350
										// Create a face and let the current halfedge belong to it.
351
										FaceIter fi = m->create_face();
352
										fi->last = he0;
353
										he0->face = fi;
354
 
355
										// Loop over all connected halfedges and let them also belong 
356
										// to the face.
357
										HalfEdgeIter he = he0->next;
358
										while(he != he0)
359
										{
360
												he->face = fi;
361
												he = he->next;
362
										}
363
								}
364
								++he0;
365
						}
366
				}
367
 
368
 
369
				/* A fairly complex algorithm which triangulates faces. It triangulates
370
					 by iteratively splitting faces. It always splits a face by picking the
371
					 edge whose midpoint is closest to the isovalue. */
372
 
373
				template<class T>
374
				void Mesher<T>::triangulate(float iso)
375
				{
376
#ifndef NDEBUG
377
						cout << "Initial mesh valid : " << m->is_valid() << endl;
378
#endif
379
						Geometry::TrilinFilter<Geometry::RGrid<T> > grid_interp(voxel_grid);
380
						int work;
381
						do
382
						{
383
								// For every face.
384
								work = 0;
385
								for(FaceIter fi =m->faces_begin(); fi != m->faces_end(); ++fi)
386
								{
387
										// Create a vector of vertices.
388
										vector<VertexIter> verts;
389
										for(FaceCirculator fc(fi); !fc.end(); ++fc)
390
												verts.push_back(fc.get_vertex());
391
 
392
										// If there are just three we are done.
393
										if(verts.size() == 3) continue;
394
 
395
										// Find vertex pairs that may be connected.
396
										vector<pair<int,int> > vpairs;
397
										const int N = verts.size();
398
										for(int i=0;i<N-2;++i)
399
												for(int j=i+2;j<N; ++j)
400
														if(!is_connected(verts[i], verts[j]))
401
																vpairs.push_back(pair<int,int>(i,j));
402
 
403
										assert(!vpairs.empty());
404
 
405
										/* For all vertex pairs, find the absolute value of iso-x
406
											 where x is the interpolated value of the volume at the 
407
											 midpoint of the line segment connecting the two vertices.*/
408
 
409
										float min_val=FLT_MAX;
410
										int min_k = -1;
411
										for(int k=0;k<vpairs.size(); ++k)
412
										{
413
												int i = vpairs[k].first;
414
												int j = vpairs[k].second;
415
												Vec3f centre = 
416
														(verts[i]->pos + 
417
														 verts[j]->pos)/2.0f;
418
 
419
												float val;
420
												if(grid_interp.in_domain(centre))
421
														val = fabs(grid_interp(centre)-iso);
422
												else
423
														val = 0.0f;
424
												if(val<min_val)
425
												{
426
														min_val = val;
427
														min_k = k;
428
												}
429
										}
430
										assert(min_k != -1);
431
 
432
										// Split faces along edge whose midpoint is closest to isovalue
433
										int i = vpairs[min_k].first;
434
										int j = vpairs[min_k].second;
435
										m->split_face(fi, verts[i], verts[j]);
436
										++work;
437
 
438
								}
439
						}
440
						while(work);
441
 
442
#ifndef NDEBUG
443
						cout << "Valid after triangulation : " << m->is_valid() << endl;
444
#endif
445
				}
446
 
447
				/* This function collapses every edge whose endpoints belong to the same 
448
					 cube if this edge can be removed without violating the manifold condition*/
449
				template<class T>
450
				void Mesher<T>::remove_duplicates()
451
				{
452
						// BUG --- we never do this loop more than one time.
453
						// does it matter.
454
						VertexIter vi = m->vertices_begin();
455
						bool did_work = false;
456
						do
457
						{
458
								did_work = false;
459
								while(vi != m->vertices_end())
460
								{
461
										bool did = false;
462
										VertexCirculator vc(vi);
463
										int k=0;
464
										for(;!vc.end();++vc)
465
										{
466
												assert(++k<1000);
467
												HalfEdgeIter he = vc.get_halfedge();
468
												VertexIter n = vc.get_vertex();
469
												if(n->touched == vi->touched && m->collapse_precond(vi,he))
470
												{
471
														VertexIter vi2 = vi;
472
														++vi;
473
														m->collapse_halfedge(vi2,he,true);
474
														did = did_work = true;
475
														break;
476
												}
477
										}
478
										if(!did) ++vi;
479
								}
480
 
481
						}
482
						while(did_work);
483
#ifndef NDEBUG
484
						cout << "Valid after removal of excess vertices : " << m->is_valid() << endl;
485
#endif
486
				}
487
 
488
				/* A function (not used now) which pushes vertices onto the isosurface. */
489
				template<class T>
490
				void Mesher<T>::push_vertices(float iso)
491
				{
492
						GradientFilter<RGrid<T> > grad(voxel_grid);
493
						TrilinFilter<GradientFilter<RGrid<T> > > ginter(&grad);
494
						TrilinFilter<RGrid<T> > inter(voxel_grid);
495
 
496
						cout << "Pushing vertices" << endl;
497
						for(int i=0;i<4;++i)
498
						{
499
								cout << "." << flush;
500
								for(VertexIter vi= m->vertices_begin();
501
										vi != m->vertices_end(); ++vi)
502
								{
503
										Vec3f p = vi->get_pos();
504
										if(ginter.in_domain(p))
505
										{
506
												Vec3f g = ginter(p);
507
												float s = sqr_length(g);
508
												if(s>0.0001)
509
												{
510
														float v = inter(p)-iso;
511
														vi->set_pos(p-g*v/s);
512
												}
513
										}
514
								}
515
						}
516
				}
517
 
518
		}
519
 
520
		template<typename T>
521
		void fair_polygonize(const RGrid<T>& voxel_grid, 
522
												 Manifold& mani, 
523
												 float iso,
524
												 bool invert_contour)
525
		{
526
				Mesher<T> m(&voxel_grid, &mani, iso, invert_contour);
527
				m.process_cubes();
528
				m.create_faces();
529
				m.triangulate(iso);
530
				m.remove_duplicates();
531
				//m.push_vertices(iso);
532
		}
533
 
534
		template void fair_polygonize<unsigned char>(const RGrid<unsigned char>&,
535
																								 Manifold&, float, bool);
536
		template void fair_polygonize<float>(const RGrid<float>&,
537
																				 Manifold&, float, bool);
538
}
539
 
540
 
541
// --- Alternative "Pick faces to glue" code.
542
//                          if(cube_hash.find_entry(HashKey3usi(pos + dir + to_edge),
543
//                                        cube_n))
544
// 														{
545
// 																assert(sqr_length(dir + to_edge)==2);
546
// 																dir_n = - to_edge;
547
// 																edge_n = - edge;
548
// 														}
549
// 														else if(cube_hash.find_entry(HashKey3usi(pos + to_edge), 
550
// 																												 cube_n))
551
// 														{
552
// 																assert(sqr_length(to_edge)==1);
553
// 																dir_n = dir;
554
// 																edge_n = - edge;
555
// 														}
556
// 														else
557
// 														{
558
// 																dir_n = to_edge;
559
// 																edge_n = - edge;
560
// 																											cube_n = &cube;
561
// 														}