Subversion Repositories gelsvn

Rev

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