575 lines
17 KiB
C++
575 lines
17 KiB
C++
/****************************************************************************
|
|
* VCGLib o o *
|
|
* Visual and Computer Graphics Library o o *
|
|
* _ O _ *
|
|
* Copyright(C) 2004-2016 \/)\/ *
|
|
* Visual Computing Lab /\/| *
|
|
* ISTI - Italian National Research Council | *
|
|
* \ *
|
|
* All rights reserved. *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
|
|
* for more details. *
|
|
* *
|
|
****************************************************************************/
|
|
|
|
#ifndef MLS_ADVANCE_H
|
|
#define MLS_ADVANCE_H
|
|
|
|
#include <iostream>
|
|
#include <list>
|
|
#include <vcg/complex/algorithms/update/topology.h>
|
|
#include <vcg/complex/algorithms/update/flag.h>
|
|
|
|
namespace vcg {
|
|
namespace tri {
|
|
|
|
/* An active edge on the advancing front.
|
|
* belong to a triangle (v0,v1,v2)
|
|
* v0, v1 the active edge
|
|
* v2 internal vertex
|
|
*/
|
|
class FrontEdge {
|
|
public:
|
|
int v0, v1, v2; //v0, v1 represent the FrontEdge, v2 the other vertex
|
|
//in the face this FrontEdge belongs to
|
|
bool active; //keep tracks of wether it is in front or in deads
|
|
|
|
//the loops in the front are mantained as a double linked list
|
|
std::list<FrontEdge>::iterator next;
|
|
std::list<FrontEdge>::iterator previous;
|
|
|
|
FrontEdge() {}
|
|
FrontEdge(int _v0, int _v1, int _v2):
|
|
v0(_v0), v1(_v1), v2(_v2), active(true) {
|
|
assert(v0 != v1 && v1 != v2 && v0 != v2);
|
|
}
|
|
|
|
bool operator==(const FrontEdge& f) const
|
|
{
|
|
return ((v0 == f.v0) && (v1 == f.v1) && (v2 == f.v2) );
|
|
}
|
|
};
|
|
|
|
template <class MESH> class AdvancingFront {
|
|
public:
|
|
|
|
typedef typename MESH::VertexType VertexType;
|
|
typedef typename MESH::FaceType FaceType;
|
|
typedef typename MESH::FaceIterator FaceIterator;
|
|
typedef typename MESH::ScalarType ScalarType;
|
|
typedef typename MESH::VertexType::CoordType Point3x;
|
|
|
|
//class FrontEdgeLists
|
|
//{
|
|
|
|
//};
|
|
|
|
|
|
// protected:
|
|
std::list<FrontEdge> front;
|
|
std::list<FrontEdge> deads;
|
|
std::vector<int> nb; //number of fronts a vertex is into,
|
|
//this is used for the Visited and Border flags
|
|
//but adding topology may not be needed anymore
|
|
|
|
public:
|
|
|
|
MESH &mesh; //this structure will be filled by the algorithm
|
|
|
|
AdvancingFront(MESH &_mesh): mesh(_mesh) {
|
|
|
|
|
|
UpdateFlags<MESH>::FaceBorderFromNone(mesh);
|
|
UpdateFlags<MESH>::VertexBorderFromFaceBorder(mesh);
|
|
|
|
nb.clear();
|
|
nb.resize(mesh.vert.size(), 0);
|
|
|
|
CreateLoops();
|
|
}
|
|
virtual ~AdvancingFront() {}
|
|
|
|
void BuildMesh(CallBackPos call = NULL, int interval = 512)
|
|
{
|
|
float finalfacesext = mesh.vert.size() * 2.0f;
|
|
if(call) call(0, "Advancing front");
|
|
while(1) {
|
|
|
|
for(int i = 0; i < interval; i++) {
|
|
if(!front.size() && !SeedFace()) return;
|
|
AddFace();
|
|
if(call)
|
|
{
|
|
float rap = float(mesh.face.size()) / finalfacesext;
|
|
int perc = (int) (100.0f * rap);
|
|
(*call)(perc,"Adding Faces");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected:
|
|
//Implement these functions in your subclass
|
|
enum ListID {FRONT,DEADS};
|
|
typedef std::pair< ListID,std::list<FrontEdge>::iterator > ResultIterator;
|
|
virtual bool Seed(int &v0, int &v1, int &v2) = 0;
|
|
// This function must find a vertex to be added to edge 'e'.
|
|
// return -1 in case of failure
|
|
virtual int Place(FrontEdge &e, ResultIterator &touch) = 0;
|
|
|
|
//create the FrontEdge loops from seed faces
|
|
void CreateLoops()
|
|
{
|
|
for(size_t i = 0; i < mesh.face.size(); i++)
|
|
{
|
|
FaceType &f = mesh.face[i];
|
|
if(f.IsD()) continue;
|
|
|
|
for(int k = 0; k < 3; k++) {
|
|
if(f.IsB(k)) {
|
|
addNewEdge(FrontEdge(tri::Index(mesh,f.V0(k)),tri::Index(mesh,f.V1(k)),tri::Index(mesh,f.V2(k))) );
|
|
nb[tri::Index(mesh,f.V0(k))]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for(std::list<FrontEdge>::iterator s = front.begin(); s != front.end(); s++) {
|
|
(*s).previous = front.end();
|
|
(*s).next = front.end();
|
|
}
|
|
//now create loops:
|
|
for(std::list<FrontEdge>::iterator s = front.begin(); s != front.end(); s++) {
|
|
for(std::list<FrontEdge>::iterator j = front.begin(); j != front.end(); j++) {
|
|
if(s == j) continue;
|
|
if((*s).v1 != (*j).v0) continue;
|
|
if((*j).previous != front.end()) continue;
|
|
(*s).next = j;
|
|
(*j).previous = s;
|
|
break;
|
|
}
|
|
}
|
|
for(std::list<FrontEdge>::iterator s = front.begin(); s != front.end(); s++) {
|
|
assert((*s).next != front.end());
|
|
assert((*s).previous != front.end());
|
|
}
|
|
}
|
|
|
|
bool SeedFace() {
|
|
int v[3];
|
|
bool success = Seed(v[0], v[1], v[2]);
|
|
if(!success) return false;
|
|
|
|
nb.resize(mesh.vert.size(), 0);
|
|
|
|
//create the border of the first face
|
|
std::list<FrontEdge>::iterator e = front.end();
|
|
std::list<FrontEdge>::iterator last = e;
|
|
std::list<FrontEdge>::iterator first;
|
|
|
|
for(int i = 0; i < 3; i++) {
|
|
int v0 = v[i];
|
|
int v1 = v[((i+1)%3)];
|
|
int v2 = v[((i+2)%3)];
|
|
|
|
mesh.vert[v0].SetB();
|
|
nb[v[i]]++;
|
|
|
|
e = front.insert(front.begin(), FrontEdge(v0, v1, v2));
|
|
if(i != 0) {
|
|
(*last).next = e;
|
|
(*e).previous = last;
|
|
} else
|
|
first = e;
|
|
|
|
last = e;
|
|
}
|
|
//connect last and first
|
|
(*last).next = first;
|
|
(*first).previous = last;
|
|
|
|
AddFace(v[0], v[1], v[2]);
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
bool AddFace() {
|
|
if(!front.size()) return false;
|
|
|
|
std::list<FrontEdge>::iterator ei = front.begin();
|
|
FrontEdge ¤t = *ei;
|
|
FrontEdge &previous = *current.previous;
|
|
FrontEdge &next = *current.next;
|
|
|
|
int v0 = current.v0, v1 = current.v1;
|
|
assert(nb[v0] < 10 && nb[v1] < 10);
|
|
|
|
ResultIterator touch;
|
|
touch.first = FRONT;
|
|
touch.second = front.end();
|
|
int v2 = Place(current, touch);
|
|
|
|
if(v2 == -1) {
|
|
KillEdge(ei);
|
|
return false;
|
|
}
|
|
|
|
assert(v2 != v0 && v2 != v1);
|
|
|
|
if ( ( (touch.first == FRONT) && (touch.second != front.end()) ) ||
|
|
( (touch.first == DEADS) && (touch.second != deads.end()) ) )
|
|
|
|
{
|
|
//check for orientation and manifoldness
|
|
|
|
//touch == current.previous?
|
|
if(v2 == previous.v0) {
|
|
if(!CheckEdge(v2, v1)) {
|
|
KillEdge(ei);
|
|
return false;
|
|
}
|
|
/*touching previous FrontEdge (we reuse previous)
|
|
next
|
|
------->v2 -----> v1------>
|
|
\ /
|
|
\ /
|
|
previous \ / current
|
|
\ /
|
|
v0 */
|
|
|
|
Detach(v0);
|
|
|
|
std::list<FrontEdge>::iterator up = addNewEdge(FrontEdge(v2, v1, v0));
|
|
MoveFront(up);
|
|
(*up).previous = previous.previous;
|
|
(*up).next = current.next;
|
|
(*previous.previous).next = up;
|
|
next.previous = up;
|
|
Erase(current.previous);
|
|
Erase(ei);
|
|
Glue(up);
|
|
|
|
//touch == (*current.next).next
|
|
} else if(v2 == next.v1) {
|
|
if(!CheckEdge(v0, v2)) {
|
|
KillEdge(ei);
|
|
return false;
|
|
}
|
|
/*touching next FrontEdge (we reuse next)
|
|
previous
|
|
------->v0 -----> v2------>
|
|
\ /
|
|
\ /
|
|
\ / next
|
|
\ /
|
|
v1 */
|
|
|
|
Detach(v1);
|
|
std::list<FrontEdge>::iterator up = addNewEdge(FrontEdge(v0, v2, v1));
|
|
MoveFront(up);
|
|
(*up).previous = current.previous;
|
|
(*up).next = (*current.next).next;
|
|
previous.next = up;
|
|
(*next.next).previous = up;
|
|
Erase(current.next);
|
|
Erase(ei);
|
|
Glue(up);
|
|
} else {
|
|
if(!CheckEdge(v0, v2) || !CheckEdge(v2, v1)) {
|
|
KillEdge(ei);
|
|
return false;
|
|
}
|
|
//touching some loop: split (or merge it is local does not matter.
|
|
//like this
|
|
/*
|
|
left right
|
|
<--------v2-<------
|
|
/|\
|
|
/ \
|
|
up / \ down
|
|
/ \
|
|
/ V
|
|
----v0 - - - > v1---------
|
|
current */
|
|
std::list<FrontEdge>::iterator left = touch.second;
|
|
std::list<FrontEdge>::iterator right = (*touch.second).previous;
|
|
|
|
//this would be a really bad join
|
|
if(v1 == (*right).v0 || v0 == (*left).v1) {
|
|
KillEdge(ei);
|
|
return false;
|
|
}
|
|
|
|
nb[v2]++;
|
|
|
|
std::list<FrontEdge>::iterator down = addNewEdge(FrontEdge(v2, v1, v0));
|
|
std::list<FrontEdge>::iterator up = addNewEdge(FrontEdge(v0, v2, v1));
|
|
|
|
(*right).next = down;
|
|
(*down).previous = right;
|
|
|
|
(*down).next = current.next;
|
|
next.previous = down;
|
|
|
|
(*left).previous = up;
|
|
(*up).next = left;
|
|
|
|
(*up).previous = current.previous;
|
|
previous.next = up;
|
|
Erase(ei);
|
|
}
|
|
|
|
|
|
}
|
|
else if (((touch.first == FRONT) && (touch.second == front.end())) ||
|
|
((touch.first == DEADS) && (touch.second == deads.end())) )
|
|
{
|
|
// assert(CheckEdge(v0, v2));
|
|
// assert(CheckEdge(v2, v1));
|
|
/* adding a new vertex
|
|
|
|
v2
|
|
/|\
|
|
/ \
|
|
up / \ down
|
|
/ \
|
|
/ V
|
|
----v0 - - - > v1--------- */
|
|
assert(!mesh.vert[v2].IsB()); //fatal error! a new point is already a border?
|
|
nb[v2]++;
|
|
mesh.vert[v2].SetB();
|
|
|
|
std::list<FrontEdge>::iterator down = addNewEdge(FrontEdge(v2, v1, v0));
|
|
std::list<FrontEdge>::iterator up = addNewEdge(FrontEdge(v0, v2, v1));
|
|
|
|
(*down).previous = up;
|
|
(*up).next = down;
|
|
(*down).next = current.next;
|
|
next.previous = down;
|
|
(*up).previous = current.previous;
|
|
previous.next = up;
|
|
Erase(ei);
|
|
}
|
|
|
|
AddFace(v0, v2, v1);
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
void AddFace(int v0, int v1, int v2) {
|
|
FaceIterator fi = vcg::tri::Allocator<MESH>::AddFace(mesh,v0,v1,v2);
|
|
fi->N() = TriangleNormal(*fi).Normalize();
|
|
if(tri::HasVFAdjacency(mesh))
|
|
{
|
|
for(int j=0;j<3;++j)
|
|
{
|
|
(*fi).VFp(j) = (*fi).V(j)->VFp();
|
|
(*fi).VFi(j) = (*fi).V(j)->VFi();
|
|
(*fi).V(j)->VFp() = &(*fi);
|
|
(*fi).V(j)->VFi() = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AddVertex(VertexType &vertex) {
|
|
VertexType *oldstart = NULL;
|
|
if(mesh.vert.size()) oldstart = &*mesh.vert.begin();
|
|
mesh.vert.push_back(vertex);
|
|
mesh.vn++;
|
|
VertexType *newstart = &*mesh.vert.begin();
|
|
if(oldstart && oldstart != newstart) {
|
|
for(int i = 0; i < mesh.face.size(); i++) {
|
|
FaceType &face = mesh.face[i];
|
|
for(int k = 0; k < 3; k++)
|
|
face.V(k) = newstart + (face.V(k) - oldstart);
|
|
}
|
|
}
|
|
nb.push_back(0);
|
|
}
|
|
|
|
// Given a possible new edge v0-v1
|
|
// it checks that:
|
|
// 1) the orientation is consistent (all the faces with vertex v0 and v1 have the edge in the opposite way)
|
|
// 2) the edge appears at least once
|
|
|
|
bool CheckEdge(int v0, int v1) {
|
|
int tot = 0;
|
|
VertexType *vv0 = &(mesh.vert[v0]);
|
|
VertexType *vv1 = &(mesh.vert[v1]);
|
|
if(tri::HasVFAdjacency(mesh))
|
|
{
|
|
face::VFIterator<FaceType> vfi(vv0);
|
|
for (;!vfi.End();++vfi)
|
|
{
|
|
FaceType *f = vfi.F();
|
|
for(int k = 0; k < 3; k++) {
|
|
if(vv0 == f->V0(k) && vv1 == f->V1(k)) //orientation non constistent
|
|
return false;
|
|
else if(vv1 == f->V0(k) && vv0 == f->V1(k)) ++tot;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
for(int i = 0; i < (int)mesh.face.size(); i++) {
|
|
FaceType &f = mesh.face[i];
|
|
for(int k = 0; k < 3; k++) {
|
|
if(vv0 == f.V0(k) && vv1 == f.V1(k)) //orientation non constistent
|
|
return false;
|
|
else if(vv1 == f.V0(k) && vv0 == f.V1(k)) ++tot;
|
|
}
|
|
if(tot >= 2) { //non manifold
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
//front management:
|
|
|
|
//Add a new FrontEdge to the back of the queue
|
|
std::list<FrontEdge>::iterator addNewEdge(FrontEdge e) {
|
|
return front.insert(front.end(), e);
|
|
}
|
|
|
|
//move an Edge among the dead ones
|
|
void KillEdge(std::list<FrontEdge>::iterator e)
|
|
{
|
|
if (e->active)
|
|
{
|
|
(*e).active = false;
|
|
//std::list<FrontEdge>::iterator res = std::find(front.begin(),front.end(),e);
|
|
FrontEdge tmp = *e;
|
|
deads.splice(deads.end(), front, e);
|
|
std::list<FrontEdge>::iterator newe = std::find(deads.begin(),deads.end(),tmp);
|
|
tmp.previous->next = newe;
|
|
tmp.next->previous = newe;
|
|
}
|
|
}
|
|
|
|
void Erase(std::list<FrontEdge>::iterator e) {
|
|
if((*e).active) front.erase(e);
|
|
else deads.erase(e);
|
|
}
|
|
|
|
//move an FrontEdge to the back of the queue
|
|
void MoveBack(std::list<FrontEdge>::iterator e) {
|
|
front.splice(front.end(), front, e);
|
|
}
|
|
|
|
void MoveFront(std::list<FrontEdge>::iterator e) {
|
|
front.splice(front.begin(), front, e);
|
|
}
|
|
|
|
//check if e can be sewed with one of oits neighbours
|
|
bool Glue(std::list<FrontEdge>::iterator e) {
|
|
return Glue((*e).previous, e) || Glue(e, (*e).next);
|
|
}
|
|
|
|
//Glue toghether a and b (where a.next = b
|
|
bool Glue(std::list<FrontEdge>::iterator a, std::list<FrontEdge>::iterator b) {
|
|
if((*a).v0 != (*b).v1) return false;
|
|
|
|
std::list<FrontEdge>::iterator previous = (*a).previous;
|
|
std::list<FrontEdge>::iterator next = (*b).next;
|
|
(*previous).next = next;
|
|
(*next).previous = previous;
|
|
Detach((*a).v1);
|
|
Detach((*a).v0);
|
|
Erase(a);
|
|
Erase(b);
|
|
return true;
|
|
}
|
|
|
|
void Detach(int v) {
|
|
assert(nb[v] > 0);
|
|
if(--nb[v] == 0) {
|
|
mesh.vert[v].ClearB();
|
|
}
|
|
}
|
|
};
|
|
|
|
template <class MESH> class AdvancingTest: public AdvancingFront<MESH> {
|
|
public:
|
|
typedef typename MESH::VertexType VertexType;
|
|
typedef typename MESH::VertexIterator VertexIterator;
|
|
typedef typename MESH::FaceType FaceType;
|
|
typedef typename MESH::FaceIterator FaceIterator;
|
|
|
|
typedef typename MESH::ScalarType ScalarType;
|
|
typedef typename MESH::VertexType::CoordType Point3x;
|
|
|
|
AdvancingTest(MESH &_mesh): AdvancingFront<MESH>(_mesh) {}
|
|
|
|
bool Seed(int &v0, int &v1, int &v2) {
|
|
VertexType v[3];
|
|
v[0].P() = Point3x(0, 0, 0);
|
|
v[1].P() = Point3x(1, 0, 0);
|
|
v[2].P() = Point3x(0, 1, 0);
|
|
v[0].ClearFlags();
|
|
v[1].ClearFlags();
|
|
v[2].ClearFlags();
|
|
|
|
v0 = this->mesh.vert.size();
|
|
AddVertex(v[0]);
|
|
v1 = this->mesh.vert.size();
|
|
AddVertex(v[1]);
|
|
v2 = this->mesh.vert.size();
|
|
AddVertex(v[2]);
|
|
return true;
|
|
}
|
|
|
|
int Place(FrontEdge &e, typename AdvancingFront<MESH>::ResultIterator &touch)
|
|
{
|
|
Point3f p[3];
|
|
p[0] = this->mesh.vert[e.v0].P();
|
|
p[1] = this->mesh.vert[e.v1].P();
|
|
p[2] = this->mesh.vert[e.v2].P();
|
|
Point3f point = p[0] + p[1] - p[2];
|
|
|
|
int vn = this->mesh.vert.size();
|
|
for(int i = 0; i < this->mesh.vert.size(); i++)
|
|
{
|
|
if((this->mesh.vert[i].P() - point).Norm() < 0.1)
|
|
{
|
|
vn = i;
|
|
//find the border
|
|
assert(this->mesh.vert[i].IsB());
|
|
for(std::list<FrontEdge>::iterator k = this->front.begin(); k != this->front.end(); k++)
|
|
if((*k).v0 == i)
|
|
{
|
|
touch.first = AdvancingFront<MESH>::FRONT;
|
|
touch.second = k;
|
|
}
|
|
|
|
for(std::list<FrontEdge>::iterator k = this->deads.begin(); k != this->deads.end(); k++)
|
|
if((*k).v0 == i)
|
|
if((*k).v0 == i)
|
|
{
|
|
touch.first = AdvancingFront<MESH>::FRONT;
|
|
touch.second = k;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(vn == this->mesh.vert.size()) {
|
|
VertexType v;
|
|
v.P() = point;
|
|
v.ClearFlags();
|
|
AddVertex(v);
|
|
}
|
|
return vn;
|
|
}
|
|
};
|
|
|
|
}//namespace tri
|
|
}//namespace vcg
|
|
|
|
#endif
|