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);
}
}
}