Subversion Repositories gelsvn

Rev

Rev 215 | Rev 220 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
149 jab 1
#include <queue>
2
#include <iostream>
3
#include "quadric_simplify.h"
4
#include "smooth.h"
5
#include "HMesh/VertexCirculator.h"
6
#include "Geometry/QEM.h"
7
 
8
 
150 jab 9
namespace HMesh
149 jab 10
{
215 jab 11
		using namespace CGLA;
12
		using namespace std;
13
		using namespace GEO;
14
		using namespace HMesh;
149 jab 15
 
215 jab 16
		namespace
149 jab 17
		{
215 jab 18
				/* We create a record for each halfedge where we can keep its time
19
					 stamp. If the time stamp on the halfedge record is bigger than
20
					 the stamp on the simplification record, we cannot use the 
21
					 simplification record (see below). */
22
				struct HalfEdgeRec
23
				{
24
						HalfEdgeIter he;
25
						int time_stamp;
26
						void halfedge_removed() {time_stamp = -1;}
27
						HalfEdgeRec(): time_stamp(0) {}
28
				};
149 jab 29
 
215 jab 30
				/* The simpliciation record contains information about a potential
31
					 edge contraction */
32
				struct SimplifyRec
33
				{
34
						Vec3d opt_pos;  // optimal vertex position
35
						int he_index;   // Index (into HalfEdgeRec vector) of edge
36
						                // we want to contract
37
						float err;      // Error associated with contraction
38
						int time_stamp; // Time stamp (see comment on HalfEdgeRec)
39
						int visits;     // Visits (number of times we considered this 
40
						                // record).
41
				};
149 jab 42
 
215 jab 43
				bool operator<(const SimplifyRec& s1, const SimplifyRec& s2)
44
				{
45
						return s1.err > s2.err;
46
				}
149 jab 47
 
215 jab 48
				class QuadricSimplifier
49
				{
50
						typedef priority_queue<SimplifyRec> SimplifyQueue;
51
						typedef vector<QEM> QEMVec;
52
						typedef vector<HalfEdgeRec> HalfEdgeVec;
149 jab 53
 
215 jab 54
						Manifold& m;
55
						HalfEdgeVec halfedge_vec;
56
						QEMVec qem_vec;
57
						SimplifyQueue sim_queue;
149 jab 58
 
215 jab 59
						/* Compute the error associated with contraction of he and the
60
							 optimal position of resulting vertex. */
61
						void push_simplify_rec(HalfEdgeIter he);
149 jab 62
 
215 jab 63
						/* Check whether the contraction is valid. See below for details*/
64
						bool check_consistency(HalfEdgeIter he, const Vec3d& opt_pos);
149 jab 65
 
215 jab 66
						/* Update the time stamp of a halfedge. A halfedge and its opp edge
67
							 may have different stamps. We choose a stamp that is greater
68
							 than either and assign to both.*/
69
						void update_time_stamp(HalfEdgeIter he)
70
								{
71
										int time_stamp = s_max(halfedge_vec[he->touched].time_stamp,
72
																					 halfedge_vec[he->opp->touched].time_stamp);
73
										time_stamp++;
74
										halfedge_vec[he->touched].time_stamp = time_stamp;
75
										halfedge_vec[he->opp->touched].time_stamp = time_stamp;
76
								}
149 jab 77
 
215 jab 78
						/* Update time stamps for all halfedges in one ring of vi */
79
						void update_onering_timestamp(VertexIter vi);
149 jab 80
 
215 jab 81
						/* Perform a collapse - if conditions are met. Returns 1 or 0 
82
							 accordingly. */
83
						int collapse(SimplifyRec& simplify_rec);
84
 
85
				public:
86
 
87
						/* Create a simplifier for a manifold */
88
						QuadricSimplifier(Manifold& _m): 
89
								m(_m), 
90
								halfedge_vec(m.no_halfedges()), 
91
								qem_vec(m.no_vertices()) 
92
								{
93
										// Enumerate vertices
94
										m.enumerate_vertices();
149 jab 95
 
215 jab 96
										// Enumerate halfedges
97
										m.enumerate_halfedges();
98
								}
149 jab 99
 
215 jab 100
						/* Simplify doing at most max_work contractions */
101
						void reduce(int max_work);
102
				};
149 jab 103
 
215 jab 104
				bool QuadricSimplifier::check_consistency(HalfEdgeIter he, 
105
																									const Vec3d& opt_pos)
106
				{
107
						VertexIter v0 = he->vert;
108
						VertexIter v1 = he->opp->vert;
109
						Vec3d p0(v0->pos);
149 jab 110
 
215 jab 111
						/* This test is inspired by Garland's Ph.D. thesis. We try
112
							 to detect whether flipped triangles will occur by sort of
113
							 ensuring that the new vertex is in the hull of the one rings
114
							 of the vertices at either end of the edge being contracted */
115
 
116
						for(VertexCirculator vc(v0); !vc.end(); ++vc)
117
						{
118
								HalfEdgeIter h = vc.get_halfedge();
119
								if(h->vert != v1 && h->next->vert != v1)
120
								{
121
										Vec3d pa(h->vert->pos);
122
										Vec3d pb(h->next->vert->pos);
123
 
124
										Vec3d dir = normalize(pb-pa);
149 jab 125
 
215 jab 126
										Vec3d n = p0 - pa;
127
										n = n - dir * dot(dir,n);
149 jab 128
 
215 jab 129
										if(dot(n,opt_pos - pa) <= 0)
130
												return false;
131
								}
132
						}
133
 
134
						/* Since the test above is not really enough, we also make sure 
135
							 that we do not introduce vertices of very high (>=10) or low 
136
							 (<=3) valency. Flipped faces seem to be always associated with
137
							 very irregular valencies. It seems to get rid of most problems
138
							 not fixed by the check above. */
139
 
140
						if(valency(he->next->vert)<5)
141
								return false;
142
 
143
						if((valency(he->vert) + valency(he->opp->vert) ) > 12)
144
								return false;
145
 
146
						return true;
149 jab 147
				}
215 jab 148
 
149
 
150
				void QuadricSimplifier::push_simplify_rec(HalfEdgeIter he)
149 jab 151
				{
215 jab 152
						// Get QEM for both end points
153
						const QEM& Q1 = qem_vec[he->vert->touched];
154
						const QEM& Q2 = qem_vec[he->opp->vert->touched];
149 jab 155
 
215 jab 156
						QEM q = Q1;
157
						q += Q2;
158
 
159
						float err;
160
						Vec3d opt_pos(0);
161
 
162
						//cout << q.determinant() << endl;
163
						if (q.determinant()>= 1e-5)
149 jab 164
						{
215 jab 165
								opt_pos = q.opt_pos(1e-5);
166
								err = q.error(opt_pos);
149 jab 167
						}
215 jab 168
						else
149 jab 169
						{
215 jab 170
								// To see the effectivenes of the scheme, we can create 
171
								// Random errors for comparison. Uncomment to use:
172
								// 			float e0 = time_stamp + rand()/float(RAND_MAX);//q.error(v0);
173
								// 			float e1 = time_stamp + rand()/float(RAND_MAX);//q.error(v1);
149 jab 174
 
215 jab 175
								Vec3d v0(he->vert->pos);
176
								Vec3d v1(he->opp->vert->pos);
177
								float e0 = q.error(v0);
178
								float e1 = q.error(v1);
179
 
180
								if(e0 < e1)
181
								{
182
										err = e0;
183
										opt_pos = v0;
184
								}
185
								else
186
								{
187
										err = e1;
188
										opt_pos = v1;
189
								}
190
						}			
149 jab 191
 
215 jab 192
						// Create SimplifyRec
193
						int he_index = he->touched;
194
						SimplifyRec simplify_rec;
195
						simplify_rec.opt_pos = opt_pos;
196
						simplify_rec.err = err;
197
						simplify_rec.he_index = he_index;
198
						simplify_rec.time_stamp = halfedge_vec[he->touched].time_stamp;
199
						simplify_rec.visits = 0;
200
						// push it.
201
						sim_queue.push(simplify_rec);
202
 
203
				}
149 jab 204
 
205
 
215 jab 206
				void QuadricSimplifier::update_onering_timestamp(VertexIter vi)
149 jab 207
				{
215 jab 208
						// For all emanating edges he
209
						for(VertexCirculator vc(vi);
210
								!vc.end(); ++vc)
211
						{
212
								HalfEdgeIter he = vc.get_halfedge();
213
								update_time_stamp(he);
214
						}
149 jab 215
				}
216
 
215 jab 217
				int QuadricSimplifier::collapse(SimplifyRec& simplify_rec)
149 jab 218
				{
215 jab 219
						int he_index = simplify_rec.he_index;
220
						HalfEdgeIter he = halfedge_vec[he_index].he;
149 jab 221
 
215 jab 222
						// Check the time stamp to verify that the simplification 
223
						// record is the newest. If the halfedge has been removed
224
						// the time stamp is -1 and the comparison will also fail.
225
						if(halfedge_vec[he_index].time_stamp == simplify_rec.time_stamp)
149 jab 226
						{
215 jab 227
								VertexIter v = he->opp->vert;
228
								VertexIter n = he->vert;
149 jab 229
 
215 jab 230
								// If the edge is, in fact, collapsible
231
								if(m.collapse_precond(he))
232
								{
233
										// If our consistency checks pass, we are relatively
234
										// sure that the contraction does not lead to a face flip.
235
										if(check_consistency(he, simplify_rec.opt_pos) && 
236
											 check_consistency(he->opp, simplify_rec.opt_pos))
237
										{
149 jab 238
 
215 jab 239
												//cout << simplify_rec.err << " " << &(*he->vert) << endl;
240
												// Get QEM for both end points
241
												const QEM& Q1 = qem_vec[he->vert->touched];
242
												const QEM& Q2 = qem_vec[he->opp->vert->touched];
243
 
244
												// Compute Q_new = Q_1 + Q_2
245
												QEM q = Q1;
246
												q += Q2;
247
 
248
												// Mark all halfedges that will be removed as dead
249
												halfedge_vec[he->touched].halfedge_removed();
250
												halfedge_vec[he->opp->touched].halfedge_removed();
149 jab 251
 
215 jab 252
												if(he->next->next->next == he)
253
												{
254
														halfedge_vec[he->next->touched].halfedge_removed();
255
														halfedge_vec[he->next->next->touched].halfedge_removed();
256
												}
257
												if(he->opp->next->next->next == he->opp)
258
												{
259
														halfedge_vec[he->opp->next->touched].halfedge_removed();
260
														halfedge_vec[he->opp->next->next->touched].halfedge_removed();
261
												}
262
 
263
												// Do collapse
264
												m.collapse_halfedge(he,false);
265
												n->pos = Vec3f(simplify_rec.opt_pos);
266
												qem_vec[n->touched] = q;
267
 
268
												update_onering_timestamp(n);
269
												return 1;
270
										}
149 jab 271
								}
215 jab 272
								// If we are here, the collapse was not allowed. If we have
273
								// seen this simplify record less than 100 times, we try to
274
								// increase the error and store the record again. Maybe some
275
								// other contractions will make it more digestible later.
276
								if(simplify_rec.visits < 10)
149 jab 277
								{
215 jab 278
										simplify_rec.err += 5*simplify_rec.err + 1e-10;
279
										++simplify_rec.visits;
280
										sim_queue.push(simplify_rec);
149 jab 281
								}
215 jab 282
						}
283
						// Ok the time stamp did not match. Create a new record.
284
						else if(halfedge_vec[he_index].time_stamp != -1)
285
								push_simplify_rec(he);
149 jab 286
 
215 jab 287
						return 0;
149 jab 288
				}
289
 
215 jab 290
				void QuadricSimplifier::reduce(int max_work)
291
				{
292
						// Set t = 0 for all halfedges
293
						int i=0;
294
						for(HalfEdgeIter he = m.halfedges_begin();
295
								he != m.halfedges_end(); ++he, ++i)
296
								halfedge_vec[i].he = he;
149 jab 297
 
215 jab 298
						cout << "Computing quadrics" << endl;
149 jab 299
 
215 jab 300
						// For all vertices, compute quadric and store in qem_vec
301
						for(VertexIter vi=m.vertices_begin(); vi != m.vertices_end(); ++vi)
149 jab 302
						{
215 jab 303
								Vec3d p(vi->pos);
304
								QEM q;
305
								for(VertexCirculator vc(vi); !vc.end(); ++vc)
149 jab 306
								{
215 jab 307
										FaceIter f = vc.get_face();
308
										if(f != NULL_FACE_ITER)
309
										{
310
												Vec3d n(normal(f));
311
												double a = area(f);
312
												q += QEM(p, n, a / 3.0);
313
										}
149 jab 314
								}
215 jab 315
								qem_vec[vi->touched] = q;
149 jab 316
						}
215 jab 317
						cout << "Pushing initial halfedges" << endl;
149 jab 318
 
215 jab 319
						for(HalfEdgeIter he = m.halfedges_begin();
320
								he != m.halfedges_end(); ++he)
321
								// For all halfedges, 
322
						{
323
								if(halfedge_vec[he->touched].time_stamp == 0)
324
								{
325
										update_time_stamp(he);
326
										push_simplify_rec(he);
327
								}
328
						}
149 jab 329
 
215 jab 330
						cout << "Simplify" << endl;
149 jab 331
 
215 jab 332
						int work = 0;
333
						while(!sim_queue.empty() && work < max_work)
149 jab 334
						{
215 jab 335
								SimplifyRec simplify_record = sim_queue.top();
336
								sim_queue.pop();
337
 
338
								work += collapse(simplify_record);
339
								if((work % 100) == 0)
340
								{
341
										cout << "work = " << work << endl;
342
										cout << "sim Q size = " << sim_queue.size() << endl;
343
								}
149 jab 344
						}
345
				}
346
		}
347
 
215 jab 348
		void quadric_simplify(Manifold& m, int max_work)
349
		{
350
				srand(1210);
351
				QuadricSimplifier qsim(m);
352
				qsim.reduce(max_work);
353
		}
149 jab 354
 
355
}