Subversion Repositories gelsvn

Rev

Rev 315 | 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
{
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);
276
				QEM q;
277
				for(VertexCirculator vc(vi); !vc.end(); ++vc)
278
				{
279
					FaceIter f = vc.get_face();
280
					if(f != NULL_FACE_ITER)
281
					{
282
						Vec3d n(normal(f));
283
						double a = area(f);
284
						q += QEM(p, n, a / 3.0);
285
					}
286
				}
287
				qem_vec[vi->touched] = q;
288
			}
289
			cout << "Pushing initial halfedges" << endl;
290
 
291
			for(HalfEdgeIter he = m.halfedges_begin();
292
				he != m.halfedges_end(); ++he)
293
				// For all halfedges, 
294
			{
295
				if(halfedge_vec[he->touched].time_stamp == 0)
296
				{
297
					update_time_stamp(he);
298
					push_simplify_rec(he);
299
				}
300
			}
301
 
302
			cout << "Simplify" << endl;
303
 
304
			int work = 0;
305
			while(!sim_queue.empty() && work < max_work)
306
			{
307
				SimplifyRec simplify_record = sim_queue.top();
308
				sim_queue.pop();
309
 
310
				work += collapse(simplify_record);
311
				if((work % 100) == 0)
312
				{
313
					cout << "work = " << work << endl;
314
					cout << "sim Q size = " << sim_queue.size() << endl;
315
				}
316
			}
215 jab 317
		}
357 jab 318
	}
319
 
320
	void quadric_simplify(Manifold& m, int max_work, double singular_thresh, bool relocate_origin)
321
	{
322
		srand(1210);
323
		QuadricSimplifier qsim(m, singular_thresh, relocate_origin);
324
		qsim.reduce(max_work);
325
	}
326
 
149 jab 327
}