applyFilter rewritten from scratch.

Added a method (refineNeededEdges) to refine, if needed, the edges which will be eroded in the next phase (inspired to the RefineE vcg function).
Added a support method and 2 support classes for the refineNeededEdges method.
This commit is contained in:
Paolo Cignoni cignoni 2008-03-03 21:36:11 +00:00
parent 0ea7a257af
commit 59f73d683f
2 changed files with 317 additions and 168 deletions

View File

@ -24,13 +24,13 @@
#include <map>
#include <vector>
#include <iostream> // DEBUG
//#include <vcg/complex/trimesh/update/selection.h> // DEBUG
#include <vcg/complex/trimesh/allocate.h>
//#include <iostream> // DEBUG
#include "filter_aging.h"
#include <vcg/complex/trimesh/allocate.h>
#include <vcg/complex/trimesh/update/normal.h>
// Constructor
GeometryAgingPlugin::GeometryAgingPlugin()
@ -88,7 +88,8 @@ void GeometryAgingPlugin::initParameterSet(QAction *action, MeshModel &m, Filter
{
switch(ID(action)) {
case FP_ERODE:
params.addFloat("AngleThreshold", 60.0, "Angle Threshold", "The minimum angle between two adjacent faces to consider the edge they are sharing.");
params.addAbsPerc("AngleThreshold", 60.0, 10.0, 170.0, "Angle Threshold", "The minimum angle between two adjacent faces to consider the edge they are sharing.");
params.addAbsPerc("EdgeLenThreshold", m.cm.bbox.Diag()*0.01, 0.0, m.cm.bbox.Diag(),"Edge Len Threshold", "The minimum length of an edge. Usefull to avoid the creation of too many small faces.");
break;
default:
assert(0);
@ -99,165 +100,17 @@ void GeometryAgingPlugin::initParameterSet(QAction *action, MeshModel &m, Filter
// The Real Core Function doing the actual mesh processing
bool GeometryAgingPlugin::applyFilter(QAction *filter, MeshModel &m, FilterParameterSet &params, vcg::CallBackPos *cb)
{
CMeshO::FaceIterator fi;
int borderNum = 0;
int chips = 0;
float angleThreshold = params.getFloat("AngleThreshold");
bool hasSel = hasSelected(m);
typedef std::pair<CMeshO::FacePointer, int> sedge; // selected edge
map<sedge, vector<EroderPoint>* > edge2Eroder;
float angleThreshold;
float edgeLenTreshold;
// first loop to clear V bits
for(fi=m.cm.face.begin(); fi!=m.cm.face.end(); ++fi)
(*fi).ClearV();
//tri::UpdateSelection<CMeshO>::ClearFace(m.cm); // DEBUG
// I'm looking for edges whose incident faces normals make an angle of at least
// 60 degrees (or other threshold value chosen by the user).
// I'm also interested in border edges.
for(fi=m.cm.face.begin(); fi!=m.cm.face.end(); ++fi) {
// Typical usage of the callback for showing a nice progress bar in the bottom.
// First parameter is a 0..100 number indicating percentage of completion, the second is an info string.
//cb(100*vcount/model.cm.vert.size(), "Exploring the mesh...");
if(!(*fi).IsD()) {
if(hasSel && !(*fi).IsS()) continue; // some faces of the mesh are selected but not the current one
for(int j=0; j<3; j++) {
if((*fi).FFp(j)->IsV()) continue; // face already visited
if(hasSel && !(*fi).FFp(j)->IsS()) continue; // some faces of the mesh are selected, the current one too, but not its j-th neighbour
if(/*(*fi).FFp(j) == &(*fi)*/(*fi).IsB(j)) { // found border edge
borderNum++;
edge2Eroder[sedge(&(*fi), j)] = generateEdgeEroder(true, vcg::Distance((*fi).P0(j), (*fi).P1(j)));
// (*fi).SetS(); // DEBUG
// (*fi).FFp(j)->SetS(); // DEBUG
}
else { // this is not a border edge
// the angle between the two face normals in degrees
// TODO: check non 2-manifold cases, it's all ok? or there are problems?
double ffangle = vcg::Angle((*fi).N(), (*fi).FFp(j)->N())*180/M_PI;
// the 2 points not shared between the 2 faces
CVertexO *f1p = (*fi).V2(j);
CVertexO *f2p = (*fi).FFp(j)->V2((*fi).FFi(j));
Point3<CVertexO::ScalarType> y, median;
y = (*fi).N().Normalize() ^ (*fi).FFp(j)->N().Normalize();
median = y ^ (Point3<CVertexO::ScalarType>(f1p->P() - f2p->P()));
/* There are always 2 cases wich produce the same angle value:
___|_ ____
| | |
-| |-
In the first case the edge lies in a concave area of the mesh
while in the second case it lies in a convex area.
I need a way to know wich is the current case.
This is done comparing ffangle with the angle between the
normal to the current face and the median vector.
*/
if(ffangle-angleThreshold >= -0.001 && vcg::Angle((*fi).N(), median)*180/M_PI < ffangle) {
chips++;
// (*fi).SetS(); // DEBUG
// (*fi).FFp(j)->SetS(); // DEBUG
}
}
}
(*fi).SetV();
}
switch(ID(filter)) {
case FP_ERODE:
angleThreshold = params.getAbsPerc("AngleThreshold");
edgeLenTreshold = params.getAbsPerc("EdgeLenThreshold");
return refineNeededEdges(m.cm, FractPoint(), EdgePred(angleThreshold, edgeLenTreshold), hasSelected(m), cb);
default:
assert(0);
}
// erode border edges
/*if(borderNum > 0) {
CMeshO::VertexIterator lastv = tri::Allocator<CMeshO>::AddVertices(m.cm, borderNum);
CMeshO::FaceIterator lastf = tri::Allocator<CMeshO>::AddFaces(m.cm, borderNum);
for(map<sedge, vector<EroderPoint>* >::iterator it = edge2Eroder.begin(); it != edge2Eroder.end(); ++it) {
if((*it).first.first->IsB((*it).first.second)) { // border face
CMeshO::FacePointer f = (*it).first.first;
int z = (*it).first.second;
vector<EroderPoint>* e = (*it).second;
vector<CMeshO::VertexPointer> vv;
vector<CMeshO::FacePointer> fv;
vector<CMeshO::FaceType::TexCoordType> wtt;
for(unsigned int j=0; j<3; j++)
vv.push_back(f->V((z+j)%3));
for(unsigned int i=0; i<e->size(); i++) {
(*lastv).P() = f->V(z)->P() * (1.0f - e->at(i).yrel) + f->V1(z)->P() * e->at(i).yrel;
if(CMeshO::HasPerVertexNormal())
(*lastv).N()= (f->V(z)->N() * (1.0f - e->at(i).yrel) + f->V1(z)->N() * e->at(i).yrel).Normalize();
if(CMeshO::HasPerVertexColor())
(*lastv).C().lerp(f->V(z)->C(), f->V1(z)->C(), e->at(i).yrel);
vv.push_back(&*lastv);
++lastv;
}
fv.push_back(f);
for(unsigned int i=0; i<vv.size()-3; i++) {
fv.push_back(&*lastf);
++lastf;
}
if(tri::HasPerWedgeTexCoord(m.cm)) {
for(unsigned int i=0; i<3; i++)
wtt.push_back(f->WT(i));
CMeshO::FaceType::TexCoordType t0 = f->WT(z);
CMeshO::FaceType::TexCoordType t1 = f->WT((z+1)%3);
assert(t0.n() == t1.n());
for(unsigned int i=0; i<e->size(); i++) {
CMeshO::FaceType::TexCoordType tmp;
tmp.n() = t0.n();
tmp.t() = (t0.t() * (1.0f - e->at(i).yrel) + t1.t() * e->at(i).yrel);
wtt.push_back(tmp);
}
}
int orgflag = f->UberFlags();
for(unsigned int i=0; i<fv.size(); i++) {
fv.at(i)->V(0) = vv.at((i>0?2+i:i));
fv.at(i)->V(1) = vv.at((i<fv.size()-1?3+i:1));
fv.at(i)->V(2) = vv.at(2);
if(tri::HasPerWedgeTexCoord(m.cm)) {
fv.at(i)->WT(0) = wtt.at((i>0?2+i:i));
fv.at(i)->WT(1) = wtt.at((i<fv.size()-1?3+i:1));
fv.at(i)->WT(2) = wtt.at(2);
}
}
for(unsigned int i=0; i<fv.size(); i++) {
if(orgflag & (CMeshO::FaceType::BORDER0 << z))
fv.at(i)->SetB(0);
else
fv.at(i)->ClearB(0);
fv.at(i)->ClearB(1);
fv.at(i)->ClearB(2);
}
if(orgflag & (CMeshO::FaceType::BORDER0 << z+1))
fv.at(fv.size()-1)->SetB(1);
else
fv.at(fv.size()-1)->ClearB(1);
if(orgflag & (CMeshO::FaceType::BORDER0 << z+2))
fv.at(0)->SetB(2);
else
fv.at(0)->ClearB(2);
}
}
}
vcg::tri::UpdateTopology<CMeshO>::FaceFace(m.cm);*/
//vcg::tri::UpdateFlags<CMeshO>::FaceBorderFromFF(m.cm);
// clear V bits again
for(fi=m.cm.face.begin(); fi!=m.cm.face.end(); ++fi)
(*fi).ClearV();
// Log function dump textual info in the lower part of the MeshLab screen
Log(0,"Found %i border edges and %i internal edges to erode.", borderNum, chips); // DEBUG
for(map<sedge, vector<EroderPoint>* >::iterator it = edge2Eroder.begin(); it != edge2Eroder.end(); ++it)
delete it->second;
return true;
}
@ -273,14 +126,14 @@ bool GeometryAgingPlugin::hasSelected(MeshModel &m)
// randomly creates a vector of eroderPoints and returns a pointer to it
vector<GeometryAgingPlugin::EroderPoint>* GeometryAgingPlugin::generateEdgeEroder(bool border, float len)
vector<GeometryAgingPlugin::EroderPoint>* GeometryAgingPlugin::generateEdgeEroder(CMeshO::FacePointer f, int z)
{
vector<EroderPoint>* e = new vector<EroderPoint>();
srand(time(NULL));
//std::cout << "len: " << len << std::endl; // DEBUG
if(border)
if(f->IsB(z))
e->push_back(GeometryAgingPlugin::EroderPoint(0.5, 0.0));
/*else
e->push_back(GeometryAgingPlugin::EroderPoint(0.5, 0.0, 0.0));*/
@ -289,4 +142,220 @@ vector<GeometryAgingPlugin::EroderPoint>* GeometryAgingPlugin::generateEdgeErode
}
// refines the edges which will be eroded in the next phase
bool GeometryAgingPlugin::refineNeededEdges(CMeshO &m, FractPoint frpoi, EdgePred ep, bool hasSelected, CallBackPos *cb)
{
int j, newVertNum = 0, newFaceNum = 0;
typedef std::pair<CMeshO::VertexPointer, CMeshO::VertexPointer> vvpair;
typedef std::pair<vvpair, int> vvi;
map<vvi, CMeshO::VertexPointer> edge2Vert;
// First Loop: we analyze the mesh to compute the number of the new faces and new vertices
CMeshO::FaceIterator fi;
int step = 0, percStep = m.fn / 33;
if(percStep == 0) percStep = 1;
for(fi=m.face.begin(),j=0; fi!=m.face.end(); ++fi)
if(!(*fi).IsD())
{
if(cb && (++step%percStep) == 0)
(*cb)(step/percStep, "Eroding...");
for(j=0; j<3; j++) {
if(ep(&(*fi), j) && (!hasSelected || ((*fi).IsS() && (*fi).FFp(j)->IsS()))) {
int numf = ep.numFacesToAdd(&(*fi), j);
if(numf <= 0) continue;
newFaceNum += numf;
if(((*fi).V(j) < (*fi).V1(j)) || (*fi).IsB(j))
newVertNum += numf;
}
}
} // end face loop
if(newVertNum==0) { // temporary error message
errorMessage = QString("Found 0 edges to erode. Change filter parameters and try again.");
return false;
}
CMeshO::VertexIterator lastv = tri::Allocator<CMeshO>::AddVertices(m, newVertNum);
// Second Loop: initialization of the edge-vertexes map
// and the position of new vertexes
for(fi=m.face.begin(); fi!=m.face.end(); ++fi)
if(!(*fi).IsD())
{
if(cb && (++step%percStep) == 0)
(*cb)(step/percStep, "Eroding...");
for(j=0; j<3; j++)
if(ep(&(*fi), j) && (!hasSelected || ((*fi).IsS() && (*fi).FFp(j)->IsS())))
if((*fi).V(j) < (*fi).V1(j) || (*fi).IsB(j)) {
int numf = ep.numFacesToAdd(&(*fi), j) + 1;
for(int p=1; p<numf; p++) {
frpoi((*lastv), face::Pos<CMeshO::FaceType>(&*fi,j), (float)p/numf);
edge2Vert[vvi(vvpair((*fi).V(j), (*fi).V1(j)), p)] = &*lastv;
++lastv;
}
}
}
assert(lastv == m.vert.end());
CMeshO::FaceIterator lastf = tri::Allocator<CMeshO>::AddFaces(m, newFaceNum);
CMeshO::FaceIterator oldendf = lastf;
/*
v0
f0
mp01 mp02
f3
f1 f2
v1 mp12 v2
*/
vector<CMeshO::VertexPointer> vv; // total face vertexes
// 0..2 original face vertexes
// 3... newly created vertexes
vector<CMeshO::FacePointer> nf; // total faces (the original one and the newly created)
vector<CMeshO::FaceType::TexCoordType> wtt; // per wedge texture coordinates
for(fi=m.face.begin(); fi!=oldendf; ++fi)
if(!(*fi).IsD())
{
if(cb && (++step%percStep) == 0)
(*cb)(step/percStep, "Eroding...");
vv.clear();
nf.clear();
wtt.clear();
vv.push_back((*fi).V(0));
vv.push_back((*fi).V(1));
vv.push_back((*fi).V(2));
int n[3];
for(j=0; j<3; j++) {
if(ep(&(*fi), j) && (!hasSelected || ((*fi).IsS() && (*fi).FFp(j)->IsS()))) {
n[j] = ep.numFacesToAdd(&(*fi), j);
if((*fi).V(j) < (*fi).V1(j) || (*fi).IsB(j))
for(int p=1; p<n[j]+1; p++)
vv.push_back(edge2Vert[vvi(vvpair((*fi).V(j), (*fi).V1(j)), p)]);
else
for(int p=1; p<n[j]+1; p++)
vv.push_back(edge2Vert[vvi(vvpair((*fi).V1(j), (*fi).V(j)), p)]);
}
else
n[j] = 0;
}
nf.push_back(&*fi);
int i;
for(i=1; i<=n[0]+n[1]+n[2]; i++) {
nf.push_back(&*lastf);
lastf++;
if(hasSelected || (*fi).IsS())
(*nf[i]).SetS();
}
if(tri::HasPerWedgeTexCoord(m)) {
for(i=0; i<3; ++i)
wtt.push_back((*fi).WT(i));
for(j=1; j<=n[0]; j++)
wtt.push_back(frpoi.WedgeInterp((*fi).WT(0), (*fi).WT(1), j/(n[0]+1)));
for(j=1; j<=n[1]; j++)
wtt.push_back(frpoi.WedgeInterp((*fi).WT(1), (*fi).WT(2), j/(n[1]+1)));
for(j=1; j<=n[2]; j++)
wtt.push_back(frpoi.WedgeInterp((*fi).WT(2), (*fi).WT(0), j/(n[2]+1)));
}
int orgflag = (*fi).UberFlags();
for(j=0; j<3; j++)
splitFaceEdge(m, nf, vv, wtt, n, j, orgflag);
}
assert(lastf == m.face.end());
assert(!m.vert.empty());
for(fi=m.face.begin(); fi!=m.face.end(); ++fi)
if(!(*fi).IsD()){
assert((*fi).V(0) >= &*m.vert.begin() && (*fi).V(0) <= &m.vert.back());
assert((*fi).V(1) >= &*m.vert.begin() && (*fi).V(1) <= &m.vert.back());
assert((*fi).V(2) >= &*m.vert.begin() && (*fi).V(2) <= &m.vert.back());
}
vcg::tri::UpdateTopology<CMeshO>::FaceFace(m);
vcg::tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m);
Log(0,"Created %i new vertexes and %i new faces.", newVertNum, newFaceNum); // DEBUG
return true;
}
// splits the current edge (z) of the current face creating numf[z] new faces.
// actually the face edges are already splitted: the new vertexes (contained in vector vv) are
// already in their final position but the area need to be retriangulated
// (vector f contains the pointers to all the faces needed to retriangulate the old face)
void GeometryAgingPlugin::splitFaceEdge(CMeshO &m, vector<CMeshO::FacePointer> &f, vector<CMeshO::VertexPointer> &vv, vector<CMeshO::FaceType::TexCoordType> &wtt, int numf[3], int z, int orgflag)
{
if(numf[z] <= 0) return;
int idx = 3; // index of the first vertex of the curr face (the second will be idx+1)
int idx3; // index of the third vertex of the curr face
int oldf = 0; // face to split index
int newf = 1; // first new face index
int vf0 = (z+1)%3; // index of the vertex to move in face 0 (original face to split)
if(z == 0)
idx3 = 2;
else if(z == 1) {
if(numf[0] > 0) {
idx3 = 2 + numf[0];
oldf += numf[0];
newf += numf[0];
}
else
idx3 = 0;
}
else {
if(numf[0] > 0) {
idx3 = 3;
newf += numf[0] + numf[1];
}
else if(numf[1] > 0) {
idx3 = 2 + numf[1];
oldf += numf[1];
newf += numf[1];
vf0 = z;
}
else
idx3 = 1;
}
int i;
for(i=0; i<z; i++) idx += numf[i];
f.at(oldf)->V(vf0) = vv.at(idx);
if(tri::HasPerWedgeTexCoord(m))
f.at(oldf)->WT((z+1)%3) = wtt.at(idx);
if(idx > 2)
f.at(oldf)->ClearB((z+1)%3);
for(i=0; i<numf[z]; i++, idx++, newf++) {
f.at(newf)->V(0) = vv.at(idx);
f.at(newf)->V(1) = vv.at((i==(numf[z]-1)?(z+1)%3:idx+1));
f.at(newf)->V(2) = vv.at(idx3);
if(tri::HasPerWedgeTexCoord(m)) {
f.at(newf)->WT(0) = wtt.at(idx);
f.at(newf)->WT(1) = wtt.at((i==(numf[z]-1)?(z+1)%3:idx+1));
f.at(newf)->WT(2) = wtt.at(idx3);
}
for(int j=0; j<3; j++) assert(f.at(newf)->V(j) != 0);
if(orgflag & (CMeshO::FaceType::BORDER0 << z))
f.at(newf)->SetB(0);
else
f.at(newf)->ClearB(0);
f.at(newf)->ClearB(1);
f.at(newf)->ClearB(2);
}
}
Q_EXPORT_PLUGIN(GeometryAgingPlugin)

View File

@ -22,14 +22,93 @@
****************************************************************************/
#ifndef GEOMETRYAGINGPLUGIN_H
#define GEOMETRYAGINGPLUGIN_H
#include <QObject>
#include <meshlab/meshmodel.h>
#include <meshlab/interfaces.h>
//#include <vcg/complex/trimesh/refine.h>
struct FractPoint : public std::unary_function<face::Pos<CMeshO::FaceType>, CMeshO::CoordType>
{
void operator()(CMeshO::VertexType &nv, face::Pos<CMeshO::FaceType> ep, float relpos = 0.5f) {
nv.P() = ep.f->V(ep.z)->P() * (1.0f - relpos) + ep.f->V1(ep.z)->P() * relpos;
if(CMeshO::HasPerVertexNormal())
nv.N() = (ep.f->V(ep.z)->N() * (1.0f - relpos) + ep.f->V1(ep.z)->N() * relpos).Normalize();
if(CMeshO::HasPerVertexColor())
nv.C().lerp(ep.f->V(ep.z)->C(), ep.f->V1(ep.z)->C(), relpos);
}
Color4<CMeshO::ScalarType> WedgeInterp(Color4<CMeshO::ScalarType> &c0, Color4<CMeshO::ScalarType> &c1, float relpos = 0.5f)
{
Color4<CMeshO::ScalarType> cc;
cc.lerp(c0, c1, relpos);
return cc;
}
template<class FL_TYPE>
TexCoord2<FL_TYPE, 1> WedgeInterp(TexCoord2<FL_TYPE, 1> &t0, TexCoord2<FL_TYPE, 1> &t1, float relpos = 0.5f)
{
TexCoord2<FL_TYPE, 1> tmp;
assert(t0.n() == t1.n());
tmp.n() = t0.n();
tmp.t() = t0.t() * (1.0f - relpos) + t1.t() * relpos;
return tmp;
}
};
class EdgePred
{
public:
EdgePred(float angleTh, float edgeLenTh) {
this->angleTh = angleTh;
this->edgeLenTh = edgeLenTh;
}
bool operator()(CMeshO::FacePointer f, int idx) {
if(f->IsB(idx)) return true;
// the angle between the two face normals in degrees
// TODO: check non 2-manifold cases, it's all ok? or there are problems?
/*double ffangle = vcg::Angle(f->N(), f->FFp(idx)->N()) * 180 / M_PI;
// the 2 points not shared between the 2 faces
CVertexO *f1p = f->V2(idx);
CVertexO *f2p = f->FFp(idx)->V2(f->FFi(idx));
Point3<CVertexO::ScalarType> y, median;
y = f->N().Normalize() ^ f->FFp(idx)->N().Normalize();
median = y ^ (Point3<CVertexO::ScalarType>(f1p->P() - f2p->P()));
*/
/* There are always 2 cases wich produce the same angle value:
___|_ ____
| | |
-| |-
In the first case the edge lies in a concave area of the mesh
while in the second case it lies in a convex area.
We need a way to know wich is the current case.
This is done comparing ffangle with the angle between the
normal to the current face and the median vector.
*/
//return (ffangle-angleThreshold >= -0.001 && vcg::Angle(f->N(), median) * 180 / M_PI < ffangle);
return false;
}
int numFacesToAdd(CMeshO::FacePointer f, int idx) {
return (int)(Distance(f->V(idx)->P(), f->V1(idx)->P()) / edgeLenTh) - 1;
}
protected:
float angleTh;
float edgeLenTh;
};
class GeometryAgingPlugin : public QObject, public MeshFilterInterface
@ -62,9 +141,10 @@ class GeometryAgingPlugin : public QObject, public MeshFilterInterface
virtual bool applyFilter(QAction *filter, MeshModel &m, FilterParameterSet &params, vcg::CallBackPos *cb);
protected:
virtual bool hasSelected(MeshModel &m);
vector<EroderPoint>* generateEdgeEroder(bool border, float len);
bool hasSelected(MeshModel &m);
vector<EroderPoint>* generateEdgeEroder(CMeshO::FacePointer f, int z);
bool refineNeededEdges(CMeshO &m, FractPoint mid, EdgePred ep, bool hasSelected, CallBackPos *cb);
void splitFaceEdge(CMeshO &m, vector<CMeshO::FacePointer> &f, vector<CMeshO::VertexPointer> &vv, vector<CMeshO::FaceType::TexCoordType> &wtt, int numf[3], int z, int orgflag);
};