Subversion Repositories gelsvn

Rev

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