Subversion Repositories gelsvn

Rev

Rev 507 | Rev 512 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
* Written by Christian Thode Larsen 2009-2010
* Contact: thode2d@gmail.com
* Based on original work by J. Andreas Baerentzen
* Inspired by OpenMesh (www.openmesh.org)
*/

#include "triangulate.h"

#include <queue>
#include <vector>
#include <cassert>

#include <CGLA/Vec3f.h>

#include "Manifold.h"
#include "AttributeVector.h"

namespace HMesh
{
        using namespace std;
        using namespace CGLA;
    
    void get_candidates(const Manifold& m, VertexID v, vector<HalfEdgeID>& candidates)
    {
        vector<VertexID> vertices;
        vector<HalfEdgeID> hedges;

                HalfEdgeWalker wv = m.halfedgewalker(v);
                for(;!wv.full_circle(); wv = wv.circulate_vertex_cw()){
            vertices.push_back(wv.vertex());
            hedges.push_back(wv.halfedge());
        }
        IndexType N = wv.no_steps();
        vector<VertexID> vertices_check(vertices);
        assert(N == vertices.size());

        for(int i = N - 1; i >= 0; --i){
                        for(HalfEdgeWalker w = m.halfedgewalker(hedges[i]).next(); w.vertex() != vertices[(i+N-1)%N]; w = w.next()){
                if(find(vertices_check.begin(), vertices_check.end(), w.vertex()) == vertices_check.end())
                    candidates.push_back(w.halfedge());       
            }
        }
    }

    float curv(const Vec3f& p, vector<Vec3f>& vec)
    {
        int N = vec.size();
        std::vector<Vec3f> normals;
        for(int i=0;i<N;++i){
            Vec3f norm = normalize(cross(vec[i]-p, vec[(i+1)%N]-p));
            normals.push_back(norm);
        }
        float alpha = 0;
        for(int i=0;i<N;++i)
            alpha += (vec[i]-p).length()*acos(dot(normals[i],normals[(i+1)%N]));

        return alpha;
    }

    float get_badness(const Manifold& m, VertexID v, VertexID n)
    {
        vector<HalfEdgeID> hedges;

                HalfEdgeWalker wv = m.halfedgewalker(v);
                for(; !wv.full_circle(); wv = wv.circulate_vertex_cw())
            hedges.push_back(wv.halfedge());

        vector<Vec3f> one_ring;
        vector<Vec3f> one_ring_n;
        int N = wv.no_steps();
        for(int i = N - 1; i >= 0; --i){
                        for(HalfEdgeWalker w = m.halfedgewalker(hedges[i]).next(); w.vertex() != v; w = w.next()){
                one_ring.push_back(m.pos(w.vertex()));
                if(w.vertex() != n)
                    one_ring_n.push_back(m.pos(w.vertex()));
            }
        }
        return curv(m.pos(v), one_ring) - curv(m.pos(v), one_ring_n);
    }


    const CGLA::Vec3f get_normal(const Manifold& m, VertexID v)
    {

        vector<HalfEdgeID> hedges;

                HalfEdgeWalker wv = m.halfedgewalker(v);
                for(; !wv.full_circle(); wv = wv.circulate_vertex_cw())
            hedges.push_back(wv.halfedge());

        vector<Vec3f> one_ring;
        int N = wv.no_steps();
        for(int i = N - 1; i >= 0; --i){
                        for(HalfEdgeWalker w = m.halfedgewalker(hedges[i]).next(); w.vertex() != v; w = w.next())
                one_ring.push_back(m.pos(w.vertex()));   
        }

        Vec3f norm(0);
        N = one_ring.size();
        Vec3f p0 = m.pos(v);
        for(int i = 0; i < N; ++i){
            Vec3f p1 = one_ring[i];
            Vec3f p2 = one_ring[(i+1) % N];
            Vec3f e0 = normalize(p1 - p0);
            Vec3f e1 = normalize(p2 - p0);
            norm += cross(e0, e1) * acos(dot(e0, e1));
        }
        return normalize(norm);
    }

    void triangulate_by_vertex_face_split(Manifold& m)
    {
                vector<FaceID> fv(m.no_faces());

                IndexType i = 0;
        for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f, ++i)
            fv[i] = *f;

        for(IndexType i = 0; i < fv.size(); ++i)
            m.split_face_by_vertex(fv[i]);
    }

    void triangulate_by_edge_face_split(Manifold& m)
    {
                vector<FaceID> fv(m.no_faces());

                IndexType i  = 0;
        for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f)
            fv[i] = *f;

        for(IndexType i = 0; i < fv.size(); ++i)
            triangulate_face_by_edge_split(m, fv[i]);
    }

    struct PotentialEdge
    {
        int time_tag;
        float badness;
        FaceID f;
        VertexID v0, v1;
    };

    bool operator>(const PotentialEdge& e0, const PotentialEdge& e1)
    { return e0.badness>e1.badness; }

    typedef std::priority_queue<PotentialEdge,
        std::vector<PotentialEdge>,
        std::greater<PotentialEdge> > 
        PotentialEdgeQueue;

    void insert_potential_edges(const Manifold& m, VertexAttributeVector<int>& vtouched, VertexID v, PotentialEdgeQueue& pot_edges)
    {
        vector<Vec3f> one_ring;
        vector<HalfEdgeID> candidates;
        get_candidates(m, v, candidates);

        Vec3f p0 = m.pos(v);
        Vec3f norm = normal(m, v);
        int n = candidates.size();
        for(int i = 0; i < n; ++i){
                        HalfEdgeWalker w = m.halfedgewalker(candidates[i]);
            VertexID v_n = w.vertex();
            Vec3f edir = normalize(m.pos(v_n) - p0);
            Vec3f norm_n = normal(m, v_n);
            float bad = sqr(dot(edir, norm));
            float bad_n = sqr(dot(edir, norm_n));

            PotentialEdge pot;

            /* So if the edge between two vertices is not orthogonal to 
            their normals, the badness is increased. Badness is also
            increased if the normals are very different. */

            pot.badness = bad+bad_n - dot(norm_n,norm);
            pot.time_tag = vtouched[v];
            pot.v0 = v;
            pot.v1 = w.vertex();
                        pot.f = w.face();

            pot_edges.push(pot);
        }
    }

    void curvature_triangulate(Manifold& m)
    {
        PotentialEdgeQueue pot_edges;
                VertexAttributeVector<int> vtouched(m, 0);

        // Create candidates
        for(VertexIDIterator v = m.vertices_begin(); v!= m.vertices_end(); ++v)
                        insert_potential_edges(m, vtouched, *v, pot_edges);

        while(!pot_edges.empty()){
            const PotentialEdge& pot_edge = pot_edges.top();
            // Record all the vertices of the face. We need to 
            // recompute the candidates.
            std::vector<VertexID> reeval_vec;

                        for(HalfEdgeWalker w = m.halfedgewalker(pot_edge.f); !w.full_circle(); w = w.circulate_face_cw())
                reeval_vec.push_back(w.vertex());

            // Make sure that the vertex has not been touched since 
            // we created the potential edge. If the vertex has been
            // touched the candidate edge has either (a) been processed,
            // (b) received a lower priority or (c) become invalid.
            if(pot_edge.time_tag == vtouched[pot_edge.v0]){
                vector<Vec3f> one_ring;
                vector<HalfEdgeID> candidates;

                m.split_face_by_edge(pot_edge.f, pot_edge.v0, pot_edge.v1);

                // Recompute priorities.
                for(IndexType i = 0; i < reeval_vec.size(); ++i){
                    VertexID& v = reeval_vec[i];
                    ++vtouched[v];
                    insert_potential_edges(m, vtouched, v, pot_edges);
                }

            }
            pot_edges.pop();
        }

    }

    void shortest_edge_triangulate(Manifold& m)
    {
        int work;
        do{
            // For every face.
            work = 0;
            for(FaceIDIterator f = m.faces_begin(); f != m.faces_end(); ++f){
                // Create a vector of vertices.
                vector<VertexID> verts;
                                for(HalfEdgeWalker w = m.halfedgewalker(*f); !w.full_circle(); w = w.circulate_face_cw())
                    verts.push_back(w.vertex());

                // If there are just three we are done.
                if(verts.size() == 3) continue;

                // Find vertex pairs that may be connected.
                vector<pair<int,int> > vpairs;
                const int N = verts.size();
                for(int i = 0; i < N - 2; ++i){
                    for(int j = i + 2; j < N; ++j){
                        if(!connected(m, verts[i], verts[j]))
                            vpairs.push_back(pair<int,int>(i, j));
                    }
                }
                if(vpairs.empty()){
                    cout << "Warning: could not triangulate a face." 
                        << "Probably a vertex appears more than one time in other vertex's one-ring" << endl;
                    continue;
                }

                /* For all vertex pairs, find the edge lengths. Combine the
                vertices forming the shortest edge. */

                float min_len=FLT_MAX;
                int min_k = -1;
                for(IndexType k = 0; k < vpairs.size(); ++k){
                    int i = vpairs[k].first;
                    int j = vpairs[k].second;
                    float len = sqr_length(m.pos(verts[i]) - m.pos(verts[j]));

                    if(len<min_len){
                        min_len = len;
                        min_k = k;
                    }
                }
                assert(min_k != -1);

                {
                    // Split faces along edge whose midpoint is closest to isovalue
                    int i = vpairs[min_k].first;
                    int j = vpairs[min_k].second;
                    m.split_face_by_edge(*f, verts[i], verts[j]);
                }
                ++work;

            }
        }
        while(work);
    }

    void triangulate_face_by_edge_split(Manifold& m, FaceID f)
    {
                HalfEdgeWalker w = m.halfedgewalker(f);

                //assert that face has at least 3 edges
                assert(w.next().next().halfedge() != w.halfedge());
        assert(w.next().halfedge() != w.halfedge());
        
        // as long as f is not a triangle
        while(w.next().next().next().halfedge() != w.halfedge()){
                        // assert that face has at least 3 edges
            assert(no_edges(m, f) > 3);
            // f is split into triangle from first three vertices, and becomes remaining polygon in next iteration
            assert(w.next().next().halfedge() != w.halfedge());
            VertexID v0 = w.vertex();
            VertexID v1 = w.next().next().vertex();
            assert(v0 != v1);
            f = m.split_face_by_edge(f, v0, v1);
        }
    }
}