Subversion Repositories gelsvn

Rev

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