Subversion Repositories gelsvn

Rev

Rev 222 | Rev 357 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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