Subversion Repositories gelsvn

Rev

Rev 215 | Rev 600 | 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
 
149 jab 7
#include "caps_and_needles.h"
8
 
595 jab 9
#include <CGLA/Vec3f.h>
10
#include <CGLA/Vec3d.h>
11
#include <Geometry/QEM.h>
149 jab 12
 
595 jab 13
#include "Manifold.h"
149 jab 14
 
150 jab 15
namespace HMesh
149 jab 16
{
595 jab 17
    using namespace std;
18
    using namespace CGLA;
19
    using namespace Geometry;
149 jab 20
 
595 jab 21
    namespace
22
    {
23
        /// get the minimum angle between 3 vertices
24
        float min_angle(const Vec3d& v0, const Vec3d& v1, const Vec3d& v2)
25
        {
26
            Vec3d a = normalize(v1-v0);
27
            Vec3d b = normalize(v2-v1);
28
            Vec3d c = normalize(v0-v2);
149 jab 29
 
595 jab 30
            return min(dot(a, -c), min(dot(b, -a), dot(c, -b)));
31
        }
149 jab 32
 
595 jab 33
        /// need description
34
        float edge_error(const Manifold& m, HalfEdgeID h, const Vec3d& pa, const Vec3d& pb)
35
        {
36
            QEM q;
37
            Walker j = m.walker(h);
149 jab 38
 
595 jab 39
            FaceID f = j.face();
40
            if(f != InvalidFaceID)
41
                q += QEM(Vec3d(0), Vec3d(normal(m, f)));
149 jab 42
 
595 jab 43
            f = j.opp().face();
44
            if(f != InvalidFaceID)
45
                q += QEM(Vec3d(0), Vec3d(normal(m, f)));
149 jab 46
 
595 jab 47
            return q.error(pb - pa);
48
        }
149 jab 49
 
595 jab 50
        /// need description
51
        float vertex_error(const Manifold& m, VertexID v, const Vec3d& pb)
52
        {
53
            QEM q;
54
            Vec3d pa(m.pos(v));
149 jab 55
 
595 jab 56
            for(Walker vj = m.walker(v); !vj.full_circle(); vj = vj.circulate_vertex_cw()){
57
                FaceID f = vj.face();
58
                if(f != InvalidFaceID)
59
                    q += QEM(Vec3d(0), Vec3d(normal(m, f)));
60
            }
61
            return q.error(pb - pa);
62
        }
63
    }
149 jab 64
 
595 jab 65
    void remove_caps(Manifold& m, float thresh)
66
    {
67
        for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f){
68
            Vec3d p[3];
69
            HalfEdgeID he[3];
70
            VertexID vh[3];
149 jab 71
 
595 jab 72
            // store ids of vertices and halfedges and vertex positions of face
73
            size_t n = 0;
74
            for(Walker fj = m.walker(*f); !fj.full_circle(); fj = fj.circulate_face_cw(), ++n){
75
                vh[n] = fj.vertex();
76
                p[n] = Vec3d(m.pos(vh[n]));
149 jab 77
 
595 jab 78
                // original face circulator implementation returned next halfedge, jumper doesn't. Can this be optimized? 
79
                he[n] = fj.halfedge();
80
            }
81
            assert(n == 3);
149 jab 82
 
595 jab 83
            // calculate the edge lengths of face
84
            bool is_collapsed = false;
85
            Vec3d edges[3];
86
            for(size_t i = 0; i < 3; ++i){
87
                edges[i] = p[(i+1)%3] - p[i];
88
                float l = length(edges[i]);
89
                if(l < 1e-20)
90
                    is_collapsed = true;    
91
                else
92
                    edges[i] /= l;
149 jab 93
 
595 jab 94
            }
95
            // an edge length was close to 1e-20, thus collapsed
96
            if(is_collapsed)
97
                continue;
98
 
99
            for(size_t i = 0; i < 3; ++i){
100
                float ang = acos(max(-1.0, min(1.0, dot(-edges[(i+2)%3], edges[i]))));
101
 
102
                // flip long edge of current face if angle exceeds cap threshold and result is better than current cap
103
                if(ang > thresh){
104
                    size_t iplus1 = (i+1)%3;
105
                    Vec3d edge_dir = edges[iplus1];
106
 
107
                    Walker j = m.walker(he[iplus1]);
108
                    Vec3d v0(m.pos(j.vertex()));
109
                    Vec3d v1(m.pos(j.next().vertex()));
110
                    Vec3d v2(m.pos(j.opp().vertex()));
111
                    Vec3d v3(m.pos(j.opp().next().vertex()));
112
 
113
                    float m1 = min(min_angle(v0, v1, v2), min_angle(v0, v2, v3));
114
                    float m2 = min(min_angle(v0, v1, v3), min_angle(v1, v2, v3));
115
 
116
                    if(m1 < m2){
117
						// If the "cap vertex" projected onto the long edge is better in the 
118
						// sense that there is less geometric error after the flip, then we 
119
						// use the projected vertex. In other words, we see if it pays to snap
120
						// to the edge.
121
						Vec3d pprj = edge_dir * dot(edge_dir, p[i]-p[iplus1])+p[iplus1];
122
                        if(edge_error(m, he[iplus1], pprj, Vec3d(m.pos(vh[i]))) > vertex_error(m, vh[i], pprj))
123
                            m.pos(vh[i]) = pprj;
124
                        // flip if legal
125
                        if(precond_flip_edge(m, he[iplus1]))
126
                            m.flip_edge(he[iplus1]);
127
                        break;
128
                    }
129
                }
130
            }
131
        }
132
 
133
    }
134
 
135
    void remove_needles(Manifold& m, float thresh)
136
    {
137
        bool did_work = false;
138
 
139
        // remove needles until no more can be removed
140
        do{
141
            did_work = false;
142
            for(VertexIDIterator v = m.vertices_begin(); v != m.vertices_end(); ++v){
143
                // don't attempt to remove needles if vertex is boundary
144
                if(boundary(m, *v))
145
                    continue;
146
 
147
                for(Walker vj = m.walker(*v); !vj.full_circle(); vj = vj.circulate_vertex_cw()){
148
                    // don't attempt to remove needles if vertex of jumper halfedge is boundary
149
   //                 if(boundary(m, vj.vertex()))
150
//                        continue;
151
 
152
                    HalfEdgeID h = vj.opp().halfedge();
153
//                    VertexID n = vj.vertex();
154
                    float dist = length(m, h);
155
 
156
                    // collapse edge if allowed and needle is present
157
                    if(dist < thresh && precond_collapse_edge(m, h)){
158
//                        if(vertex_error(m, *v, Vec3d(m.pos(n))) < vertex_error(m, n, Vec3d(m.pos(*v))))
159
//                            m.pos(*v) = m.pos(n);
160
                        m.collapse_edge(h);
161
                        did_work = true;
162
                        break;
163
                    }
164
                }
165
            }
166
        }
167
        while(did_work);
168
    }
169
}