Subversion Repositories gelsvn

Rev

Rev 219 | Rev 222 | 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
 
220 jab 143
						if((valency(he->vert) + valency(he->opp->vert) ) > 13)
215 jab 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
 
220 jab 162
						if (q.determinant()>= 1e-15)
149 jab 163
						{
220 jab 164
								opt_pos = q.opt_pos(0.001);
215 jab 165
								err = q.error(opt_pos);
149 jab 166
						}
215 jab 167
						else
149 jab 168
						{
215 jab 169
								// To see the effectivenes of the scheme, we can create 
170
								// Random errors for comparison. Uncomment to use:
171
								// 			float e0 = time_stamp + rand()/float(RAND_MAX);//q.error(v0);
172
								// 			float e1 = time_stamp + rand()/float(RAND_MAX);//q.error(v1);
149 jab 173
 
215 jab 174
								Vec3d v0(he->vert->pos);
175
								Vec3d v1(he->opp->vert->pos);
176
								float e0 = q.error(v0);
177
								float e1 = q.error(v1);
178
 
179
								if(e0 < e1)
180
								{
181
										err = e0;
182
										opt_pos = v0;
183
								}
184
								else
185
								{
186
										err = e1;
187
										opt_pos = v1;
188
								}
189
						}			
149 jab 190
 
215 jab 191
						// Create SimplifyRec
192
						int he_index = he->touched;
193
						SimplifyRec simplify_rec;
194
						simplify_rec.opt_pos = opt_pos;
195
						simplify_rec.err = err;
196
						simplify_rec.he_index = he_index;
197
						simplify_rec.time_stamp = halfedge_vec[he->touched].time_stamp;
198
						simplify_rec.visits = 0;
199
						// push it.
200
						sim_queue.push(simplify_rec);
201
 
202
				}
149 jab 203
 
204
 
215 jab 205
				void QuadricSimplifier::update_onering_timestamp(VertexIter vi)
149 jab 206
				{
215 jab 207
						// For all emanating edges he
208
						for(VertexCirculator vc(vi);
209
								!vc.end(); ++vc)
210
						{
211
								HalfEdgeIter he = vc.get_halfedge();
212
								update_time_stamp(he);
213
						}
149 jab 214
				}
215
 
215 jab 216
				int QuadricSimplifier::collapse(SimplifyRec& simplify_rec)
149 jab 217
				{
215 jab 218
						int he_index = simplify_rec.he_index;
219
						HalfEdgeIter he = halfedge_vec[he_index].he;
149 jab 220
 
215 jab 221
						// Check the time stamp to verify that the simplification 
222
						// record is the newest. If the halfedge has been removed
223
						// the time stamp is -1 and the comparison will also fail.
224
						if(halfedge_vec[he_index].time_stamp == simplify_rec.time_stamp)
149 jab 225
						{
215 jab 226
								VertexIter v = he->opp->vert;
227
								VertexIter n = he->vert;
149 jab 228
 
215 jab 229
								// If the edge is, in fact, collapsible
230
								if(m.collapse_precond(he))
231
								{
220 jab 232
 										// If our consistency checks pass, we are relatively
233
 										// sure that the contraction does not lead to a face flip.
215 jab 234
										if(check_consistency(he, simplify_rec.opt_pos) && 
235
											 check_consistency(he->opp, simplify_rec.opt_pos))
236
										{
149 jab 237
 
215 jab 238
												//cout << simplify_rec.err << " " << &(*he->vert) << endl;
239
												// Get QEM for both end points
240
												const QEM& Q1 = qem_vec[he->vert->touched];
241
												const QEM& Q2 = qem_vec[he->opp->vert->touched];
242
 
243
												// Compute Q_new = Q_1 + Q_2
244
												QEM q = Q1;
245
												q += Q2;
246
 
247
												// Mark all halfedges that will be removed as dead
248
												halfedge_vec[he->touched].halfedge_removed();
249
												halfedge_vec[he->opp->touched].halfedge_removed();
149 jab 250
 
215 jab 251
												if(he->next->next->next == he)
252
												{
253
														halfedge_vec[he->next->touched].halfedge_removed();
254
														halfedge_vec[he->next->next->touched].halfedge_removed();
255
												}
256
												if(he->opp->next->next->next == he->opp)
257
												{
258
														halfedge_vec[he->opp->next->touched].halfedge_removed();
259
														halfedge_vec[he->opp->next->next->touched].halfedge_removed();
260
												}
261
 
262
												// Do collapse
263
												m.collapse_halfedge(he,false);
264
												n->pos = Vec3f(simplify_rec.opt_pos);
265
												qem_vec[n->touched] = q;
266
 
267
												update_onering_timestamp(n);
268
												return 1;
269
										}
149 jab 270
								}
215 jab 271
								// If we are here, the collapse was not allowed. If we have
272
								// seen this simplify record less than 100 times, we try to
273
								// increase the error and store the record again. Maybe some
274
								// other contractions will make it more digestible later.
275
								if(simplify_rec.visits < 10)
149 jab 276
								{
215 jab 277
										simplify_rec.err += 5*simplify_rec.err + 1e-10;
278
										++simplify_rec.visits;
279
										sim_queue.push(simplify_rec);
149 jab 280
								}
215 jab 281
						}
282
						// Ok the time stamp did not match. Create a new record.
283
						else if(halfedge_vec[he_index].time_stamp != -1)
284
								push_simplify_rec(he);
149 jab 285
 
215 jab 286
						return 0;
149 jab 287
				}
288
 
215 jab 289
				void QuadricSimplifier::reduce(int max_work)
290
				{
291
						// Set t = 0 for all halfedges
292
						int i=0;
293
						for(HalfEdgeIter he = m.halfedges_begin();
294
								he != m.halfedges_end(); ++he, ++i)
295
								halfedge_vec[i].he = he;
149 jab 296
 
215 jab 297
						cout << "Computing quadrics" << endl;
149 jab 298
 
215 jab 299
						// For all vertices, compute quadric and store in qem_vec
300
						for(VertexIter vi=m.vertices_begin(); vi != m.vertices_end(); ++vi)
149 jab 301
						{
215 jab 302
								Vec3d p(vi->pos);
303
								QEM q;
304
								for(VertexCirculator vc(vi); !vc.end(); ++vc)
149 jab 305
								{
215 jab 306
										FaceIter f = vc.get_face();
307
										if(f != NULL_FACE_ITER)
308
										{
309
												Vec3d n(normal(f));
310
												double a = area(f);
311
												q += QEM(p, n, a / 3.0);
312
										}
149 jab 313
								}
215 jab 314
								qem_vec[vi->touched] = q;
149 jab 315
						}
215 jab 316
						cout << "Pushing initial halfedges" << endl;
149 jab 317
 
215 jab 318
						for(HalfEdgeIter he = m.halfedges_begin();
319
								he != m.halfedges_end(); ++he)
320
								// For all halfedges, 
321
						{
322
								if(halfedge_vec[he->touched].time_stamp == 0)
323
								{
324
										update_time_stamp(he);
325
										push_simplify_rec(he);
326
								}
327
						}
149 jab 328
 
215 jab 329
						cout << "Simplify" << endl;
149 jab 330
 
215 jab 331
						int work = 0;
332
						while(!sim_queue.empty() && work < max_work)
149 jab 333
						{
215 jab 334
								SimplifyRec simplify_record = sim_queue.top();
335
								sim_queue.pop();
336
 
337
								work += collapse(simplify_record);
338
								if((work % 100) == 0)
339
								{
340
										cout << "work = " << work << endl;
341
										cout << "sim Q size = " << sim_queue.size() << endl;
342
								}
149 jab 343
						}
344
				}
345
		}
346
 
215 jab 347
		void quadric_simplify(Manifold& m, int max_work)
348
		{
349
				srand(1210);
350
				QuadricSimplifier qsim(m);
351
				qsim.reduce(max_work);
352
		}
149 jab 353
 
354
}