Subversion Repositories gelsvn

Rev

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