Subversion Repositories gelsvn

Rev

Rev 521 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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