Subversion Repositories gelsvn

Rev

Rev 521 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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