Subversion Repositories gelsvn

Rev

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

Rev 357 Rev 362
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
			double singular_thresh;
58
			double singular_thresh;
59
			bool relocate_origin;
59
			bool relocate_origin;
60
			
60
			
61
			/* Compute the error associated with contraction of he and the
61
			/* Compute the error associated with contraction of he and the
62
				optimal position of resulting vertex. */
62
				optimal position of resulting vertex. */
63
			void push_simplify_rec(HalfEdgeIter he);
63
			void push_simplify_rec(HalfEdgeIter he);
64
			
64
			
65
			/* Check whether the contraction is valid. See below for details*/
65
			/* Check whether the contraction is valid. See below for details*/
66
			bool check_consistency(HalfEdgeIter he, const Vec3d& opt_pos);
66
			bool check_consistency(HalfEdgeIter he, const Vec3d& opt_pos);
67
			
67
			
68
			/* Update the time stamp of a halfedge. A halfedge and its opp edge
68
			/* Update the time stamp of a halfedge. A halfedge and its opp edge
69
				may have different stamps. We choose a stamp that is greater
69
				may have different stamps. We choose a stamp that is greater
70
				than either and assign to both.*/
70
				than either and assign to both.*/
71
			void update_time_stamp(HalfEdgeIter he)
71
			void update_time_stamp(HalfEdgeIter he)
72
			{
72
			{
73
				int time_stamp = s_max(halfedge_vec[he->touched].time_stamp,
73
				int time_stamp = s_max(halfedge_vec[he->touched].time_stamp,
74
									   halfedge_vec[he->opp->touched].time_stamp);
74
									   halfedge_vec[he->opp->touched].time_stamp);
75
				time_stamp++;
75
				time_stamp++;
76
				halfedge_vec[he->touched].time_stamp = time_stamp;
76
				halfedge_vec[he->touched].time_stamp = time_stamp;
77
				halfedge_vec[he->opp->touched].time_stamp = time_stamp;
77
				halfedge_vec[he->opp->touched].time_stamp = time_stamp;
78
			}
78
			}
79
			
79
			
80
			/* Update time stamps for all halfedges in one ring of vi */
80
			/* Update time stamps for all halfedges in one ring of vi */
81
			void update_onering_timestamp(VertexIter vi);
81
			void update_onering_timestamp(VertexIter vi);
82
			
82
			
83
			/* Perform a collapse - if conditions are met. Returns 1 or 0 
83
			/* Perform a collapse - if conditions are met. Returns 1 or 0 
84
				accordingly. */
84
				accordingly. */
85
			int collapse(SimplifyRec& simplify_rec);
85
			int collapse(SimplifyRec& simplify_rec);
86
			
86
			
87
		public:
87
		public:
88
				
88
				
89
				/* Create a simplifier for a manifold */
89
				/* Create a simplifier for a manifold */
90
				QuadricSimplifier(Manifold& _m, double _singular_thresh, bool _relocate_origin): 
90
				QuadricSimplifier(Manifold& _m, double _singular_thresh, bool _relocate_origin): 
91
				m(_m), 
91
				m(_m), 
92
				halfedge_vec(m.no_halfedges()), 
92
				halfedge_vec(m.no_halfedges()), 
93
				qem_vec(m.no_vertices()),
93
				qem_vec(m.no_vertices()),
94
				singular_thresh(_singular_thresh),
94
				singular_thresh(_singular_thresh),
95
				relocate_origin(_relocate_origin)
95
				relocate_origin(_relocate_origin)
96
			{
96
			{
97
					// Enumerate vertices
97
					// Enumerate vertices
98
					m.enumerate_vertices();
98
					m.enumerate_vertices();
99
					
99
					
100
					// Enumerate halfedges
100
					// Enumerate halfedges
101
					m.enumerate_halfedges();
101
					m.enumerate_halfedges();
102
			}
102
			}
103
			
103
			
104
			/* Simplify doing at most max_work contractions */
104
			/* Simplify doing at most max_work contractions */
105
			void reduce(int max_work);
105
			void reduce(int max_work);
106
		};
106
		};
107
		
107
		
108
		bool QuadricSimplifier::check_consistency(HalfEdgeIter he, 
108
		bool QuadricSimplifier::check_consistency(HalfEdgeIter he, 
109
												  const Vec3d& opt_pos)
109
												  const Vec3d& opt_pos)
110
		{
110
		{
111
			VertexIter v0 = he->vert;
111
			VertexIter v0 = he->vert;
112
			VertexIter v1 = he->opp->vert;
112
			VertexIter v1 = he->opp->vert;
113
			Vec3d p0(v0->pos);
113
			Vec3d p0(v0->pos);
114
			
114
			
115
			/* This test is inspired by Garland's Ph.D. thesis. We try
115
			/* This test is inspired by Garland's Ph.D. thesis. We try
116
				to detect whether flipped triangles will occur by sort of
116
				to detect whether flipped triangles will occur by sort of
117
				ensuring that the new vertex is in the hull of the one rings
117
				ensuring that the new vertex is in the hull of the one rings
118
				of the vertices at either end of the edge being contracted 
118
				of the vertices at either end of the edge being contracted 
119
				
119
				
120
				I also had an additional check intended to ensure that poor valencies
120
				I also had an additional check intended to ensure that poor valencies
121
				would not be introduced, but it seemed to be unnecessary.
121
				would not be introduced, but it seemed to be unnecessary.
122
				
122
				
123
				*/
123
				*/
124
			
124
			
125
			for(VertexCirculator vc(v0); !vc.end(); ++vc)
125
			for(VertexCirculator vc(v0); !vc.end(); ++vc)
126
			{
126
			{
127
				HalfEdgeIter h = vc.get_halfedge();
127
				HalfEdgeIter h = vc.get_halfedge();
128
				
128
				
129
				if(h->vert != v1 && h->next->vert != v1)
129
				if(h->vert != v1 && h->next->vert != v1)
130
				{
130
				{
131
					Vec3d pa(h->vert->pos);
131
					Vec3d pa(h->vert->pos);
132
					Vec3d pb(h->next->vert->pos);
132
					Vec3d pb(h->next->vert->pos);
133
					
133
					
134
					Vec3d dir = normalize(pb-pa);
134
					Vec3d dir = normalize(pb-pa);
135
					
135
					
136
					Vec3d n = p0 - pa;
136
					Vec3d n = p0 - pa;
137
					n = n - dir * dot(dir,n);
137
					n = n - dir * dot(dir,n);
138
					
138
					
139
					if(dot(n,opt_pos - pa) <= 0)
139
					if(dot(n,opt_pos - pa) <= 0)
140
						return false;
140
						return false;
141
				}
141
				}
142
			}
142
			}
143
			
143
			
144
			return true;
144
			return true;
145
		}
145
		}
146
		
146
		
147
		
147
		
148
		void QuadricSimplifier::push_simplify_rec(HalfEdgeIter he)
148
		void QuadricSimplifier::push_simplify_rec(HalfEdgeIter he)
149
		{
149
		{
150
			// Get QEM for both end points
150
			// Get QEM for both end points
151
			const QEM& Q1 = qem_vec[he->vert->touched];
151
			const QEM& Q1 = qem_vec[he->vert->touched];
152
			const QEM& Q2 = qem_vec[he->opp->vert->touched];
152
			const QEM& Q2 = qem_vec[he->opp->vert->touched];
153
			
153
			
154
			QEM q = Q1;
154
			QEM q = Q1;
155
			q += Q2;
155
			q += Q2;
156
			
156
			
157
			float err;
157
			float err;
158
			Vec3d opt_pos(0);
158
			Vec3d opt_pos(0);
159
			Vec3d opt_origin = Vec3d(he->vert->pos+he->opp->vert->pos)/2.0;
159
			Vec3d opt_origin = Vec3d(he->vert->pos+he->opp->vert->pos)/2.0;
160
			if(relocate_origin)
160
			if(relocate_origin)
161
				opt_pos = q.opt_pos(singular_thresh,opt_origin);
161
				opt_pos = q.opt_pos(singular_thresh,opt_origin);
162
			else
162
			else
163
				opt_pos = q.opt_pos(singular_thresh,Vec3d(0));
163
				opt_pos = q.opt_pos(singular_thresh,Vec3d(0));
164
			
164
			
165
			err = q.error(opt_pos);
165
			err = q.error(opt_pos);
166
			
166
			
167
			// Create SimplifyRec
167
			// Create SimplifyRec
168
			int he_index = he->touched;
168
			int he_index = he->touched;
169
			SimplifyRec simplify_rec;
169
			SimplifyRec simplify_rec;
170
			simplify_rec.opt_pos = opt_pos;
170
			simplify_rec.opt_pos = opt_pos;
171
			simplify_rec.err = err;
171
			simplify_rec.err = err;
172
			simplify_rec.he_index = he_index;
172
			simplify_rec.he_index = he_index;
173
			simplify_rec.time_stamp = halfedge_vec[he->touched].time_stamp;
173
			simplify_rec.time_stamp = halfedge_vec[he->touched].time_stamp;
174
			simplify_rec.visits = 0;
174
			simplify_rec.visits = 0;
175
			// push it.
175
			// push it.
176
			sim_queue.push(simplify_rec);
176
			sim_queue.push(simplify_rec);
177
			
177
			
178
		}
178
		}
179
		
179
		
180
		
180
		
181
		void QuadricSimplifier::update_onering_timestamp(VertexIter vi)
181
		void QuadricSimplifier::update_onering_timestamp(VertexIter vi)
182
		{
182
		{
183
			// For all emanating edges he
183
			// For all emanating edges he
184
			for(VertexCirculator vc(vi);
184
			for(VertexCirculator vc(vi);
185
				!vc.end(); ++vc)
185
				!vc.end(); ++vc)
186
			{
186
			{
187
				HalfEdgeIter he = vc.get_halfedge();
187
				HalfEdgeIter he = vc.get_halfedge();
188
				update_time_stamp(he);
188
				update_time_stamp(he);
189
				push_simplify_rec(he);
189
				push_simplify_rec(he);
190
			}
190
			}
191
		}
191
		}
192
		
192
		
193
		int QuadricSimplifier::collapse(SimplifyRec& simplify_rec)
193
		int QuadricSimplifier::collapse(SimplifyRec& simplify_rec)
194
		{
194
		{
195
			int he_index = simplify_rec.he_index;
195
			int he_index = simplify_rec.he_index;
196
			HalfEdgeIter he = halfedge_vec[he_index].he;
196
			HalfEdgeIter he = halfedge_vec[he_index].he;
197
			
197
			
198
			// Check the time stamp to verify that the simplification 
198
			// Check the time stamp to verify that the simplification 
199
			// record is the newest. If the halfedge has been removed
199
			// record is the newest. If the halfedge has been removed
200
			// the time stamp is -1 and the comparison will also fail.
200
			// the time stamp is -1 and the comparison will also fail.
201
			if(halfedge_vec[he_index].time_stamp == simplify_rec.time_stamp)
201
			if(halfedge_vec[he_index].time_stamp == simplify_rec.time_stamp)
202
			{
202
			{
203
				VertexIter v = he->opp->vert;
203
				VertexIter v = he->opp->vert;
204
				VertexIter n = he->vert;
204
				VertexIter n = he->vert;
205
				
205
				
206
				// If the edge is, in fact, collapsible
206
				// If the edge is, in fact, collapsible
207
				if(m.collapse_precond(he))
207
				if(m.collapse_precond(he))
208
				{
208
				{
209
					// If our consistency checks pass, we are relatively
209
					// If our consistency checks pass, we are relatively
210
					// sure that the contraction does not lead to a face flip.
210
					// sure that the contraction does not lead to a face flip.
211
					if(check_consistency(he, simplify_rec.opt_pos) && 
211
					if(check_consistency(he, simplify_rec.opt_pos) && 
212
					   check_consistency(he->opp, simplify_rec.opt_pos))
212
					   check_consistency(he->opp, simplify_rec.opt_pos))
213
					{
213
					{
214
						
214
						
215
						//cout << simplify_rec.err << " " << &(*he->vert) << endl;
215
						//cout << simplify_rec.err << " " << &(*he->vert) << endl;
216
						// Get QEM for both end points
216
						// Get QEM for both end points
217
						const QEM& Q1 = qem_vec[he->vert->touched];
217
						const QEM& Q1 = qem_vec[he->vert->touched];
218
						const QEM& Q2 = qem_vec[he->opp->vert->touched];
218
						const QEM& Q2 = qem_vec[he->opp->vert->touched];
219
						
219
						
220
						// Compute Q_new = Q_1 + Q_2
220
						// Compute Q_new = Q_1 + Q_2
221
						QEM q = Q1;
221
						QEM q = Q1;
222
						q += Q2;
222
						q += Q2;
223
						
223
						
224
						// Mark all halfedges that will be removed as dead
224
						// Mark all halfedges that will be removed as dead
225
						halfedge_vec[he->touched].halfedge_removed();
225
						halfedge_vec[he->touched].halfedge_removed();
226
						halfedge_vec[he->opp->touched].halfedge_removed();
226
						halfedge_vec[he->opp->touched].halfedge_removed();
227
						
227
						
228
						if(he->next->next->next == he)
228
						if(he->next->next->next == he)
229
						{
229
						{
230
							halfedge_vec[he->next->touched].halfedge_removed();
230
							halfedge_vec[he->next->touched].halfedge_removed();
231
							halfedge_vec[he->next->next->touched].halfedge_removed();
231
							halfedge_vec[he->next->next->touched].halfedge_removed();
232
						}
232
						}
233
						if(he->opp->next->next->next == he->opp)
233
						if(he->opp->next->next->next == he->opp)
234
						{
234
						{
235
							halfedge_vec[he->opp->next->touched].halfedge_removed();
235
							halfedge_vec[he->opp->next->touched].halfedge_removed();
236
							halfedge_vec[he->opp->next->next->touched].halfedge_removed();
236
							halfedge_vec[he->opp->next->next->touched].halfedge_removed();
237
						}
237
						}
238
						
238
						
239
						// Do collapse
239
						// Do collapse
240
						m.collapse_halfedge(he,false);
240
						m.collapse_halfedge(he,false);
241
						n->pos = Vec3f(simplify_rec.opt_pos);
241
						n->pos = Vec3f(simplify_rec.opt_pos);
242
						qem_vec[n->touched] = q;
242
						qem_vec[n->touched] = q;
243
						
243
						
244
						update_onering_timestamp(n);
244
						update_onering_timestamp(n);
245
						return 1;
245
						return 1;
246
					}
246
					}
247
				}
247
				}
248
				// If we are here, the collapse was not allowed. If we have
248
				// If we are here, the collapse was not allowed. If we have
249
				// seen this simplify record less than 100 times, we try to
249
				// seen this simplify record less than 100 times, we try to
250
				// increase the error and store the record again. Maybe some
250
				// increase the error and store the record again. Maybe some
251
				// other contractions will make it more digestible later.
251
				// other contractions will make it more digestible later.
252
				if(simplify_rec.visits < 100)
252
				if(simplify_rec.visits < 100)
253
				{
253
				{
254
					simplify_rec.err *= 1.01;
254
					simplify_rec.err *= 1.01;
255
					++simplify_rec.visits;
255
					++simplify_rec.visits;
256
					sim_queue.push(simplify_rec);
256
					sim_queue.push(simplify_rec);
257
				}
257
				}
258
			}
258
			}
259
			return 0;
259
			return 0;
260
		}
260
		}
261
		
261
		
262
		void QuadricSimplifier::reduce(int max_work)
262
		void QuadricSimplifier::reduce(int max_work)
263
		{
263
		{
264
			// Set t = 0 for all halfedges
264
			// Set t = 0 for all halfedges
265
			int i=0;
265
			int i=0;
266
			for(HalfEdgeIter he = m.halfedges_begin();
266
			for(HalfEdgeIter he = m.halfedges_begin();
267
				he != m.halfedges_end(); ++he, ++i)
267
				he != m.halfedges_end(); ++he, ++i)
268
				halfedge_vec[i].he = he;
268
				halfedge_vec[i].he = he;
269
			
269
			
270
			cout << "Computing quadrics" << endl;
270
			cout << "Computing quadrics" << endl;
271
			
271
			
272
			// For all vertices, compute quadric and store in qem_vec
272
			// For all vertices, compute quadric and store in qem_vec
273
			for(VertexIter vi=m.vertices_begin(); vi != m.vertices_end(); ++vi)
273
			for(VertexIter vi=m.vertices_begin(); vi != m.vertices_end(); ++vi)
274
			{
274
			{
275
				Vec3d p(vi->pos);
275
				Vec3d p(vi->pos);
-
 
276
				Vec3d vn(normal(vi));
276
				QEM q;
277
				QEM q;
277
				for(VertexCirculator vc(vi); !vc.end(); ++vc)
278
				for(VertexCirculator vc(vi); !vc.end(); ++vc)
278
				{
279
				{
279
					FaceIter f = vc.get_face();
280
					FaceIter f = vc.get_face();
280
					if(f != NULL_FACE_ITER)
281
					if(f != NULL_FACE_ITER)
281
					{
282
					{
282
						Vec3d n(normal(f));
283
						Vec3d n(normal(f));
283
						double a = area(f);
284
						double a = area(f);
284
						q += QEM(p, n, a / 3.0);
285
						q += QEM(p, n, a / 3.0);
285
					}
286
					}
-
 
287
					else if (sqr_length(vn) > 0.0)
-
 
288
					{
-
 
289
						Vec3d edge = Vec3d(vc.get_halfedge()->vert->pos)-p;
-
 
290
						double edge_len = sqr_length(edge); 
-
 
291
						if(edge_len > 0.0)
-
 
292
							{
-
 
293
								Vec3d n = cross(vn, edge);
-
 
294
								q += QEM(p, n, 2*edge_len);
-
 
295
							}
-
 
296
					}
286
				}
297
				}
287
				qem_vec[vi->touched] = q;
298
				qem_vec[vi->touched] = q;
288
			}
299
			}
289
			cout << "Pushing initial halfedges" << endl;
300
			cout << "Pushing initial halfedges" << endl;
290
			
301
			
291
			for(HalfEdgeIter he = m.halfedges_begin();
302
			for(HalfEdgeIter he = m.halfedges_begin();
292
				he != m.halfedges_end(); ++he)
303
				he != m.halfedges_end(); ++he)
293
				// For all halfedges, 
304
				// For all halfedges, 
294
			{
305
			{
295
				if(halfedge_vec[he->touched].time_stamp == 0)
306
				if(halfedge_vec[he->touched].time_stamp == 0)
296
				{
307
				{
297
					update_time_stamp(he);
308
					update_time_stamp(he);
298
					push_simplify_rec(he);
309
					push_simplify_rec(he);
299
				}
310
				}
300
			}
311
			}
301
			
312
			
302
			cout << "Simplify" << endl;
313
			cout << "Simplify" << endl;
303
			
314
			
304
			int work = 0;
315
			int work = 0;
305
			while(!sim_queue.empty() && work < max_work)
316
			while(!sim_queue.empty() && work < max_work)
306
			{
317
			{
307
				SimplifyRec simplify_record = sim_queue.top();
318
				SimplifyRec simplify_record = sim_queue.top();
308
				sim_queue.pop();
319
				sim_queue.pop();
309
				
320
				
310
				work += collapse(simplify_record);
321
				work += collapse(simplify_record);
311
				if((work % 100) == 0)
322
				if((work % 100) == 0)
312
				{
323
				{
313
					cout << "work = " << work << endl;
324
					cout << "work = " << work << endl;
314
					cout << "sim Q size = " << sim_queue.size() << endl;
325
					cout << "sim Q size = " << sim_queue.size() << endl;
315
				}
326
				}
316
			}
327
			}
317
		}
328
		}
318
	}
329
	}
319
	
330
	
320
	void quadric_simplify(Manifold& m, int max_work, double singular_thresh, bool relocate_origin)
331
	void quadric_simplify(Manifold& m, int max_work, double singular_thresh, bool relocate_origin)
321
	{
332
	{
322
		srand(1210);
333
		srand(1210);
323
		QuadricSimplifier qsim(m, singular_thresh, relocate_origin);
334
		QuadricSimplifier qsim(m, singular_thresh, relocate_origin);
324
		qsim.reduce(max_work);
335
		qsim.reduce(max_work);
325
	}
336
	}
326
	
337
	
327
}
338
}
328
 
339