mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-20 11:26:11 +00:00
Code refactory:
- use additional data instead of flag - add higher level entity as manager of holes' set bug-fix:close more than one non manifold hole selected
This commit is contained in:
parent
a25c5f9824
commit
e254f8613c
@ -44,7 +44,6 @@ EditHolePlugin::EditHolePlugin() {
|
||||
|
||||
EditHolePlugin::~EditHolePlugin()
|
||||
{
|
||||
FgtHole<CMeshO>::DeleteFlag();
|
||||
if ( dialogFiller !=0 )
|
||||
{
|
||||
delete dialogFiller;
|
||||
@ -63,11 +62,11 @@ const QString EditHolePlugin::Info()
|
||||
return tr("Allow fill one or more hole into place");
|
||||
}
|
||||
|
||||
void EditHolePlugin::mouseReleaseEvent(QMouseEvent * e, MeshModel &/*m*/, GLArea * gla)
|
||||
void EditHolePlugin::mouseReleaseEvent(QMouseEvent * /*e*/, MeshModel &/*m*/, GLArea * /*gla*/)
|
||||
{
|
||||
}
|
||||
|
||||
void EditHolePlugin::mousePressEvent(QMouseEvent * e, MeshModel &m, GLArea * gla)
|
||||
void EditHolePlugin::mousePressEvent(QMouseEvent *e, MeshModel &/*m*/, GLArea * /*gla*/)
|
||||
{
|
||||
if ( (e->button()==Qt::LeftButton) )
|
||||
{
|
||||
@ -77,7 +76,7 @@ void EditHolePlugin::mousePressEvent(QMouseEvent * e, MeshModel &m, GLArea * gla
|
||||
}
|
||||
}
|
||||
|
||||
void EditHolePlugin::mouseMoveEvent(QMouseEvent * e, MeshModel &/*m*/, GLArea * gla)
|
||||
void EditHolePlugin::mouseMoveEvent(QMouseEvent * /*e*/, MeshModel &/*m*/, GLArea * /*gla*/)
|
||||
{
|
||||
}
|
||||
|
||||
@ -131,6 +130,7 @@ void EditHolePlugin::StartEdit(MeshModel &m, GLArea *gla )
|
||||
delete holesModel;
|
||||
}
|
||||
holesModel = new HoleListModel(&m);
|
||||
holesModel->holesManager.autoBridgeCB = new EditHoleAutoBridgingCB(dialogFiller->ui.infoLbl, 800);
|
||||
connect(holesModel, SIGNAL(SGN_needUpdateGLA()), this, SLOT(upGlA()) );
|
||||
connect(holesModel, SIGNAL(SGN_ExistBridge(bool)), dialogFiller, SLOT(SLOT_ExistBridge(bool)) );
|
||||
holeSorter = new HoleSorterFilter();
|
||||
@ -159,15 +159,15 @@ void EditHolePlugin::Decorate(MeshModel &m, GLArea * gla)
|
||||
pickedFace =0;
|
||||
int inverseY = gla->curSiz.height() - cur.y();
|
||||
GLPickTri<CMeshO>::PickNearestFace(cur.x(), inverseY, m.cm, pickedFace, 4, 4);
|
||||
// guardo se nella faccia pi<70> vicina uno dei vertici <20> di bordo
|
||||
|
||||
if( pickedFace != 0 )
|
||||
{
|
||||
bool oldAbutmentPresence;
|
||||
switch(holesModel->getState())
|
||||
{
|
||||
case HoleListModel::Selection:
|
||||
if(FgtHole<CMeshO>::IsHoleBorderFace(*pickedFace))
|
||||
holesModel->toggleSelectionHoleFromBorderFace(pickedFace);
|
||||
if(holesModel->holesManager.IsHoleBorderFace(pickedFace))
|
||||
holesModel->toggleSelectionHoleFromFace(pickedFace);
|
||||
break;
|
||||
case HoleListModel::Filled:
|
||||
holesModel->toggleAcceptanceHole(pickedFace);
|
||||
@ -191,7 +191,7 @@ void EditHolePlugin::Decorate(MeshModel &m, GLArea * gla)
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void EditHolePlugin::EndEdit(MeshModel &m, GLArea *gla ){
|
||||
void EditHolePlugin::EndEdit(MeshModel &/*m*/, GLArea *gla ){
|
||||
if(holesModel == 0) // means editing is not started
|
||||
return;
|
||||
|
||||
@ -199,8 +199,6 @@ void EditHolePlugin::Decorate(MeshModel &m, GLArea * gla)
|
||||
holesModel->acceptFilling(false);
|
||||
holesModel->removeBridges();
|
||||
|
||||
FgtHole<CMeshO>::DeleteFlag();
|
||||
|
||||
if ( dialogFiller!=0) {
|
||||
delete dialogFiller;
|
||||
delete holesModel;
|
||||
@ -222,12 +220,12 @@ void EditHolePlugin::upGlA()
|
||||
|
||||
void EditHolePlugin::setInfoLabel()
|
||||
{
|
||||
int ns = holesModel->SelectionCount();
|
||||
int nh = holesModel->HolesCount();
|
||||
int ns = holesModel->holesManager.SelectionCount();
|
||||
int nh = holesModel->holesManager.HolesCount();
|
||||
QString infoStr;
|
||||
if(holesModel->getState() == HoleListModel::Filled)
|
||||
{
|
||||
int na = holesModel->AcceptedCount();
|
||||
int na = holesModel->holesManager.AcceptedCount();
|
||||
infoStr = QString("Filled: %1/%2; Accepted: %3").arg(ns).arg(nh).arg(na);
|
||||
}
|
||||
else
|
||||
@ -296,7 +294,7 @@ void EditHolePlugin::manualBridge()
|
||||
{
|
||||
holesModel->setEndBridging();
|
||||
dialogFiller->clickEndBridging();
|
||||
gla->setCursor(QCursor());
|
||||
gla->setCursor(QCursor());
|
||||
}
|
||||
gla->update();
|
||||
}
|
||||
@ -304,7 +302,7 @@ void EditHolePlugin::manualBridge()
|
||||
void EditHolePlugin::autoBridge()
|
||||
{
|
||||
bool singleHole = dialogFiller->ui.selfHoleChkB->isChecked();
|
||||
holesModel->autoBridge(singleHole, bridgeOptSldVal*0.0017, dialogFiller->ui.infoLbl);
|
||||
holesModel->autoBridge(singleHole, bridgeOptSldVal*0.0017);
|
||||
upGlA();
|
||||
}
|
||||
|
||||
@ -342,6 +340,7 @@ void EditHolePlugin::skipTab(int index)
|
||||
{
|
||||
holesModel->setEndBridging();
|
||||
dialogFiller->clickEndBridging();
|
||||
gla->setCursor(QCursor());
|
||||
}
|
||||
else
|
||||
cancelFill();
|
||||
|
||||
@ -25,24 +25,37 @@
|
||||
#define EDITHOLEPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include "fillerDialog.h"
|
||||
#include "holeListModel.h"
|
||||
#include <meshlab/meshmodel.h>
|
||||
#include <meshlab/interfaces.h>
|
||||
|
||||
class EditHoleAutoBridgingCB : public AutoBridgingCallback
|
||||
{
|
||||
public:
|
||||
EditHoleAutoBridgingCB(QLabel* lab, int off)
|
||||
{
|
||||
offset = off;
|
||||
label = lab;
|
||||
};
|
||||
|
||||
void Invoke(int val)
|
||||
{
|
||||
label->setText( QString("Auto-bridging: %1%").arg(val) );
|
||||
label->repaint();
|
||||
};
|
||||
|
||||
private:
|
||||
QLabel* label;
|
||||
};
|
||||
|
||||
|
||||
class EditHolePlugin : public QObject, public MeshEditInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(MeshEditInterface)
|
||||
|
||||
public:
|
||||
typedef vcg::tri::Hole<CMeshO> vcgHole;
|
||||
typedef vcgHole::Info HoleInfo;
|
||||
typedef std::vector< FgtHole<CMeshO> > HoleVector;
|
||||
typedef vcg::face::Pos<CMeshO::FaceType> PosType;
|
||||
typedef CMeshO::FaceIterator FaceIterator;
|
||||
|
||||
|
||||
EditHolePlugin();
|
||||
virtual ~EditHolePlugin();
|
||||
static const QString Info();
|
||||
|
||||
@ -6,6 +6,7 @@ HEADERS = edit_hole_factory.h \
|
||||
holeListModel.h \
|
||||
fgtHole.h \
|
||||
fgtBridge.h \
|
||||
holeSetManager.h \
|
||||
../../meshlab/meshmodel.h
|
||||
|
||||
SOURCES = edit_hole_factory.cpp \
|
||||
|
||||
@ -29,8 +29,11 @@
|
||||
#include "vcg/space/line2.h"
|
||||
#include "vcg/space/triangle3.h"
|
||||
#include "fgtHole.h"
|
||||
#include "meshlab/mainwindow.h"
|
||||
#include "holeSetManager.h"
|
||||
#include <ctime>
|
||||
|
||||
template <class MESH> class FgtHole;
|
||||
template <class MESH> class HoleSetManager;
|
||||
|
||||
/* Struct rappresenting the mesh edge where the bridge starts/ends. */
|
||||
template <class MESH>
|
||||
@ -54,7 +57,24 @@ public:
|
||||
FgtHole<MESH>* h;
|
||||
};
|
||||
|
||||
/** Object rapresent connection between two border edges of different faces.
|
||||
/* Class rappresenting callback for feedback while auto bridging is running.
|
||||
* It's necessary because auto bridging can spent a lot of time computing
|
||||
* and user must know it is working.
|
||||
*/
|
||||
class AutoBridgingCallback
|
||||
{
|
||||
public:
|
||||
inline int GetOffset() const { return offset; };
|
||||
virtual void Invoke(int) = 0;
|
||||
|
||||
protected:
|
||||
int offset; // minor time before 2 calling (in ms)
|
||||
};
|
||||
|
||||
|
||||
/** Class entirely static, it offers functions to manage bridges between
|
||||
* different FgtHoles or into the same one.
|
||||
* Bridges rapresent connection between two border edges of different faces.
|
||||
* Connection consists in 2 face adjcent each other over an edge and adjcent
|
||||
* with a mesh face over another edge, so both faces have a border edge.
|
||||
*
|
||||
@ -100,12 +120,9 @@ public:
|
||||
typedef typename std::vector<PosType> PosVector;
|
||||
typedef FgtHole<MESH> HoleType;
|
||||
typedef typename std::vector<HoleType> HoleVector;
|
||||
|
||||
typedef typename MESH::VertexType VertexType;
|
||||
typedef typename MESH::CoordType CoordType;
|
||||
typedef typename MESH::ScalarType ScalarType;
|
||||
typedef typename vcg::Triangle3<ScalarType> TriangleType;
|
||||
|
||||
|
||||
|
||||
/**** Static functions ******/
|
||||
public:
|
||||
@ -114,29 +131,31 @@ public:
|
||||
* If the bridge is inside the same hole it cannot be adjacent the hole border,
|
||||
* this means fill another sub hole.
|
||||
*/
|
||||
static bool CreateBridge(BridgeAbutment<MESH> &sideA, BridgeAbutment<MESH> &sideB, MESH &mesh,
|
||||
HoleVector &holes, std::vector<FacePointer *> *app=0)
|
||||
static bool CreateBridge(BridgeAbutment<MESH> &sideA, BridgeAbutment<MESH> &sideB,
|
||||
HoleSetManager<MESH>* holesManager, std::vector<FacePointer *> *app=0)
|
||||
{
|
||||
assert(FgtHole<MESH>::IsHoleBorderFace(*sideA.f) && FgtHole<MESH>::IsHoleBorderFace(*sideB.f));
|
||||
assert( holesManager->IsHoleBorderFace(sideA.f) &&
|
||||
holesManager->IsHoleBorderFace(sideB.f));
|
||||
assert(!sideA.h->IsFilled() && !sideB.h->IsFilled());
|
||||
|
||||
std::vector<FacePointer *> tmpFaceRef;
|
||||
if(app!=0)
|
||||
tmpFaceRef.insert(tmpFaceRef.end(), app->begin(), app->end());
|
||||
FgtHole<MESH>::AddFaceReference(holes, tmpFaceRef);
|
||||
FgtHole<MESH>::AddFaceReference(holesManager->holes, tmpFaceRef);
|
||||
|
||||
if(sideA.h == sideB.h)
|
||||
{
|
||||
if( testAbutmentDistance(sideA, sideB))
|
||||
return subdivideHoleWithBridge(sideA, sideB, mesh, holes, tmpFaceRef);
|
||||
return subdivideHoleWithBridge(sideA, sideB, holesManager, tmpFaceRef);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return unifyHolesWithBridge(sideA, sideB, mesh, holes, tmpFaceRef);
|
||||
return unifyHolesWithBridge(sideA, sideB, holesManager, tmpFaceRef);
|
||||
};
|
||||
|
||||
static void AcceptBridges(HoleVector &holes)
|
||||
// clear flags of faces marked as bridge, bridge faces become mesh faces
|
||||
static void AcceptBridges(HoleSetManager<MESH>* holeManager)
|
||||
{
|
||||
// contains bridge faces reached navigating the holes.
|
||||
std::vector<FacePointer> bridgeFaces;
|
||||
@ -146,22 +165,22 @@ public:
|
||||
PosVector adjBorderPos;
|
||||
|
||||
PosType curPos;
|
||||
getBridgeInfo(holes, bridgeFaces, adjBorderPos);
|
||||
getBridgeInfo(holeManager, bridgeFaces, adjBorderPos);
|
||||
typename std::vector<FacePointer>::iterator fit;
|
||||
for(fit=bridgeFaces.begin(); fit!=bridgeFaces.end(); fit++ )
|
||||
{
|
||||
(*fit)->ClearUserBit( FgtHole<MESH>::HolePatchFlag() );
|
||||
(*fit)->ClearUserBit( FgtHole<MESH>::PatchCompFlag() );
|
||||
(*fit)->ClearUserBit( FgtHole<MESH>::BridgeFlag() );
|
||||
holeManager->ClearPatchAttr(*fit);
|
||||
holeManager->ClearCompAttr(*fit);
|
||||
holeManager->ClearBridgeAttr(*fit);
|
||||
}
|
||||
|
||||
typename HoleVector::iterator it = holes.begin();
|
||||
for( ; it!=holes.end(); it++ )
|
||||
typename HoleVector::iterator it = holeManager->holes.begin();
|
||||
for( ; it!=holeManager->holes.end(); it++ )
|
||||
it->SetBridged(false);
|
||||
};
|
||||
|
||||
/* Remove all face marked as Bridge. */
|
||||
static void RemoveBridges(MESH &mesh, HoleVector &holes)
|
||||
/* Remove all face marked as bridge. */
|
||||
static void RemoveBridges(HoleSetManager<MESH> *holeManager)
|
||||
{
|
||||
// contains bridge faces reached navigating the holes.
|
||||
std::vector<FacePointer> bridgeFaces;
|
||||
@ -171,37 +190,37 @@ public:
|
||||
PosVector adjBorderPos;
|
||||
|
||||
PosType curPos;
|
||||
getBridgeInfo(holes, bridgeFaces, adjBorderPos);
|
||||
getBridgeInfo(holeManager, bridgeFaces, adjBorderPos);
|
||||
|
||||
// remove all holes which have a bridge face on its border and
|
||||
// remove all bridge face
|
||||
typename std::vector<FacePointer>::iterator fit;
|
||||
for(fit=bridgeFaces.begin(); fit!=bridgeFaces.end(); fit++ )
|
||||
{
|
||||
if(FgtHole<MESH>::IsHoleBorderFace(**fit))
|
||||
if(holeManager->IsHoleBorderFace(*fit))
|
||||
{
|
||||
typename HoleVector::iterator hit;
|
||||
if(FgtHole<MESH>::FindHoleFromBorderFace(*fit, holes, hit) != -1)
|
||||
if(holeManager->FindHoleFromFace(*fit, hit) != -1)
|
||||
{
|
||||
assert(!hit->IsFilled());
|
||||
holes.erase(hit);
|
||||
holeManager->holes.erase(hit);
|
||||
}
|
||||
}
|
||||
if( !(*fit)->IsD() )
|
||||
vcg::tri::Allocator<MESH>::DeleteFace(mesh, **fit);
|
||||
vcg::tri::Allocator<MESH>::DeleteFace(*holeManager->mesh, **fit);
|
||||
}
|
||||
|
||||
// remove also hole which have on its border faces finded from half-edge adjacent to bridge face
|
||||
// these holes will be reinsert at the end
|
||||
typename std::vector<PosType>::iterator pit;
|
||||
for(pit=adjBorderPos.begin(); pit!=adjBorderPos.end(); pit++)
|
||||
if(FgtHole<MESH>::IsHoleBorderFace(*pit->f))
|
||||
if(holeManager->IsHoleBorderFace(pit->f))
|
||||
{
|
||||
typename HoleVector::iterator hit;
|
||||
if(FgtHole<MESH>::FindHoleFromBorderFace(pit->f, holes, hit) != -1)
|
||||
if(holeManager->FindHoleFromFace(pit->f, hit) != -1)
|
||||
{
|
||||
assert(!hit->IsFilled());
|
||||
holes.erase(hit);
|
||||
holeManager->holes.erase(hit);
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,7 +232,7 @@ public:
|
||||
if(!IsBorder(**fit, e))
|
||||
{
|
||||
FacePointer adjF = (*fit)->FFp(e);
|
||||
if(!FgtHole<MESH>::IsBridgeFace(*adjF))
|
||||
if(!holeManager->IsBridgeFace(adjF))
|
||||
{
|
||||
int adjEI = (*fit)->FFi(e);
|
||||
adjF->FFp( adjEI ) = adjF;
|
||||
@ -247,9 +266,9 @@ public:
|
||||
assert(curPos.IsBorder());
|
||||
}while(curPos != initPos);
|
||||
|
||||
FgtHole<MESH> newHole(initPos, QString("Hole_%1").arg(HoleType::GetHoleId(),3,10,QChar('0')) );
|
||||
FgtHole<MESH> newHole(initPos, QString("Hole_%1").arg(HoleType::GetHoleId(),3,10,QChar('0')), holeManager);
|
||||
newHole.SetSelect(sel);
|
||||
holes.push_back( newHole );
|
||||
holeManager->holes.push_back( newHole );
|
||||
}
|
||||
|
||||
// resetto falg visited sulle facce degli hole interessati
|
||||
@ -266,88 +285,52 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** Return boolean indicatind if the picking have select a border face which can be used
|
||||
* as abutment for a bridge. If true it also return BridgeAbutment allowing to know
|
||||
* border edge and its relative hole.
|
||||
*/
|
||||
static bool FindBridgeAbutmentFromPick(FacePointer bFace, int pickedX, int pickedY,
|
||||
HoleVector &holes, BridgeAbutment<MESH> &pickedResult)
|
||||
{
|
||||
if( vcg::face::BorderCount(*bFace) == 0 )
|
||||
return false;
|
||||
|
||||
typename HoleVector::iterator hit;
|
||||
if( FgtHole<MESH>::FindHoleFromBorderFace(bFace, holes, hit) < 0 )
|
||||
{
|
||||
pickedResult.SetNull();
|
||||
return false;
|
||||
}
|
||||
|
||||
pickedResult.h = &*hit;
|
||||
pickedResult.f = bFace;
|
||||
if( vcg::face::BorderCount(*bFace) == 1 )
|
||||
{
|
||||
// it choose the only one border edge
|
||||
for(int i=0; i<3; i++)
|
||||
if(vcg::face::IsBorder<FaceType>(*bFace, i))
|
||||
pickedResult.z = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
// looking for the closest border edge to pick point
|
||||
PosType retPos = getClosestPos(bFace, pickedX, pickedY);
|
||||
pickedResult.f = retPos.f;
|
||||
pickedResult.z = retPos.z;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
typedef void ProgressBridgingCallback(int v);
|
||||
|
||||
/* Build a bridge inner to the same hole. It chooses the best bridge computing quality
|
||||
* of 2 faces and similarity (as number of edge) of two next hole. Bridge is build follow
|
||||
* bridge's rule, bridge must have 2 border edge. exist between edge sharing a vertex or
|
||||
* bridge's rule, bridge must have 2 border edge.
|
||||
* infoLabel paramter is used to show work progress.
|
||||
* Return number of bridge builded.
|
||||
*/
|
||||
static int AutoSelfBridging(MESH &mesh, HoleVector &holes, QLabel *infoLabel=0, double dist_coeff=0.0, std::vector<FacePointer *> *app=0)
|
||||
static int AutoSelfBridging(HoleSetManager<MESH>* holesManager, double dist_coeff=0.0, std::vector<FacePointer *> *app=0)
|
||||
{
|
||||
QTime timer;
|
||||
if(infoLabel != 0)
|
||||
time_t timer;
|
||||
if(holesManager->autoBridgeCB != 0)
|
||||
{
|
||||
infoLabel->setText(QString("Auto-bridging: %1%").arg(0) );
|
||||
infoLabel->repaint();
|
||||
timer.start();
|
||||
holesManager->autoBridgeCB->Invoke(0);
|
||||
timer = clock();
|
||||
}
|
||||
int nb = 0;
|
||||
vcg::GridStaticPtr<FaceType, ScalarType > gM;
|
||||
gM.Set(mesh.face.begin(),mesh.face.end());
|
||||
gM.Set(holesManager->mesh->face.begin(),holesManager->mesh->face.end());
|
||||
|
||||
std::vector<FacePointer *> tmpFaceRef;
|
||||
HoleType* oldRef = 0;
|
||||
BridgeAbutment<MESH> sideA, sideB;
|
||||
|
||||
int nh = holes.size();
|
||||
int nh = holesManager->holes.size();
|
||||
for(int h=0; h<nh; ++h)
|
||||
{
|
||||
HoleType &thehole = holes.at(h);
|
||||
HoleType &thehole = holesManager->holes.at(h);
|
||||
if(!thehole.IsSelected() || thehole.Size()<6 )
|
||||
continue;
|
||||
assert(!thehole.IsFilled());
|
||||
|
||||
ScalarType maxQuality = -1;
|
||||
|
||||
// si scorre l'edge di partenza
|
||||
PosType initP = thehole.p;
|
||||
// initP: first bridge abutment
|
||||
PosType initP = thehole.p;
|
||||
for(int i=0; i<thehole.Size();i++)
|
||||
{
|
||||
// posiziono il secondo edge, la seconda spalla del ponte
|
||||
// initP: second bridge abutment
|
||||
PosType endP = initP;
|
||||
endP.NextB();endP.NextB();
|
||||
for(int j=3; j<=thehole.Size()/2; j++)
|
||||
{
|
||||
endP.NextB();
|
||||
|
||||
// two edge used as bridge abutment are adjacent to the same face... bridge can't be build
|
||||
// i due edge di bordo sono già collegati da 2 triangoli adiacenti,
|
||||
// il bridge si sovrapporrebbe a questi 2 triangoli
|
||||
if( endP.f->FFp(0) == initP.f ||
|
||||
@ -374,8 +357,8 @@ public:
|
||||
FaceType bfA1;
|
||||
bfA1.V(0) = vB1; bfA1.V(1) = vB0; bfA1.V(2) = vA0;
|
||||
|
||||
if( !HoleType::TestFaceMeshCompenetration(mesh, gM, &bfA0) &&
|
||||
!HoleType::TestFaceMeshCompenetration(mesh, gM, &bfA1) )
|
||||
if( !HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfA0) &&
|
||||
!HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfA1) )
|
||||
{
|
||||
ScalarType Aq = QualityFace(bfA0)+ QualityFace(bfA1) + dist_coeff * j;
|
||||
if( Aq > maxQuality)
|
||||
@ -389,8 +372,8 @@ public:
|
||||
FaceType bfB1;
|
||||
bfB1.V(0) = vB1; bfB1.V(1) = vB0; bfB1.V(2) = vA1;
|
||||
|
||||
if( !HoleType::TestFaceMeshCompenetration(mesh, gM, &bfB0) &&
|
||||
!HoleType::TestFaceMeshCompenetration(mesh, gM, &bfB1) )
|
||||
if( !HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfB0) &&
|
||||
!HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfB1) )
|
||||
{
|
||||
ScalarType Bq = QualityFace(bfB0)+ QualityFace(bfB1) + dist_coeff * j;
|
||||
if( Bq > maxQuality)
|
||||
@ -403,16 +386,15 @@ public:
|
||||
sideB.f=endP.f; sideB.z=endP.z; sideB.h=&thehole;
|
||||
}
|
||||
|
||||
if(timer.elapsed() > 800)
|
||||
if(holesManager->autoBridgeCB != 0)
|
||||
{
|
||||
float progress = (float)(((float)( ((float)j/(thehole.Size()-3)) + i) / thehole.Size()) + h) / nh;
|
||||
//h + ((float)i/thehole.Size()) ) / nh );
|
||||
infoLabel->setText(QString("Auto-bridging: %1%").arg((int)(progress*100)) );
|
||||
infoLabel->repaint();
|
||||
timer.restart();
|
||||
if(clock()- timer > holesManager->autoBridgeCB->GetOffset())
|
||||
{
|
||||
float progress = (float)(((float)( ((float)j/(thehole.Size()-3)) + i) / thehole.Size()) + h) / nh;
|
||||
holesManager->autoBridgeCB->Invoke(progress*100);
|
||||
timer = clock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}// scansione del edge di arrivo
|
||||
|
||||
initP.NextB();
|
||||
@ -421,25 +403,25 @@ public:
|
||||
assert(vcg::face::IsBorder<FaceType>(*sideA.f, sideA.z));
|
||||
assert(vcg::face::IsBorder<FaceType>(*sideB.f, sideB.z));
|
||||
|
||||
if( oldRef != &*holes.begin() )
|
||||
if( oldRef != &*holesManager->holes.begin() )
|
||||
{
|
||||
// si può
|
||||
tmpFaceRef.clear();
|
||||
if(app!=0)
|
||||
tmpFaceRef.insert(tmpFaceRef.end(), app->begin(), app->end());
|
||||
FgtHole<MESH>::AddFaceReference(holes, tmpFaceRef);
|
||||
FgtHole<MESH>::AddFaceReference(holesManager->holes, tmpFaceRef);
|
||||
tmpFaceRef.push_back(&sideA.f);
|
||||
tmpFaceRef.push_back(&sideB.f);
|
||||
oldRef = &*holes.begin();
|
||||
oldRef = &*holesManager->holes.begin();
|
||||
}
|
||||
|
||||
if(subdivideHoleWithBridge(sideA, sideB, mesh, holes, tmpFaceRef) )
|
||||
if(subdivideHoleWithBridge(sideA, sideB, holesManager, tmpFaceRef) )
|
||||
{
|
||||
nb++;
|
||||
gM.Set(mesh.face.begin(),mesh.face.end());
|
||||
gM.Set(holesManager->mesh->face.begin(), holesManager->mesh->face.end());
|
||||
// la subdivideHole.. aggiunge un hole pertanto bisogna aggiornare anche la lista di
|
||||
// reference a facce
|
||||
tmpFaceRef.push_back(&holes.back().p.f);
|
||||
tmpFaceRef.push_back(&holesManager->holes.back().p.f);
|
||||
}
|
||||
|
||||
} //scansione degli holes
|
||||
@ -447,16 +429,17 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/* Return number of bridges builded.
|
||||
/* It connects iteratively selected holes with the best bridge.
|
||||
* Result is unique hole instead of init holes.
|
||||
* Return number of bridges builded.
|
||||
*/
|
||||
static int AutoMultiBridging(MESH &mesh, HoleVector &holes, QLabel* infoLabel=0, double dist_coeff=0.0, std::vector<FacePointer *> *app=0 )
|
||||
static int AutoMultiBridging(HoleSetManager<MESH>* holesManager, double dist_coeff=0.0, std::vector<FacePointer *> *app=0 )
|
||||
{
|
||||
QTime timer;
|
||||
if(infoLabel != 0)
|
||||
time_t timer;
|
||||
if(holesManager->autoBridgeCB != 0)
|
||||
{
|
||||
infoLabel->setText(QString("Auto-bridging: %1%").arg(0) );
|
||||
infoLabel->repaint();
|
||||
timer.start();
|
||||
holesManager->autoBridgeCB->Invoke(0);
|
||||
timer = clock();
|
||||
}
|
||||
|
||||
int nb = 0;
|
||||
@ -477,13 +460,13 @@ public:
|
||||
|
||||
// prendo gli hole selezionati
|
||||
selectedHoles.clear();
|
||||
for(hit=holes.begin(); hit!=holes.end(); hit++)
|
||||
for(hit=holesManager->holes.begin(); hit!=holesManager->holes.end(); hit++)
|
||||
if(hit->IsSelected())
|
||||
selectedHoles.push_back(&*hit);
|
||||
|
||||
if(selectedHoles.size() < 2)
|
||||
return nb;
|
||||
gM.Set(mesh.face.begin(),mesh.face.end());
|
||||
gM.Set(holesManager->mesh->face.begin(),holesManager->mesh->face.end());
|
||||
|
||||
float casesViewed = 0, cases2View = 0;
|
||||
for(shit1=selectedHoles.begin(); shit1!=selectedHoles.end(); shit1++)
|
||||
@ -517,8 +500,8 @@ public:
|
||||
FaceType bfA1;
|
||||
bfA1.V(0) = vB1; bfA1.V(1) = vB0; bfA1.V(2) = vA0;
|
||||
|
||||
if( !HoleType::TestFaceMeshCompenetration(mesh, gM, &bfA0) &&
|
||||
!HoleType::TestFaceMeshCompenetration(mesh, gM, &bfA1) )
|
||||
if( !HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfA0) &&
|
||||
!HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfA1) )
|
||||
{
|
||||
ScalarType Aq = QualityFace(bfA0)+ QualityFace(bfA1);
|
||||
if( Aq > maxQuality)
|
||||
@ -532,8 +515,8 @@ public:
|
||||
FaceType bfB1;
|
||||
bfB1.V(0) = vB1; bfB1.V(1) = vB0; bfB1.V(2) = vA1;
|
||||
|
||||
if( !HoleType::TestFaceMeshCompenetration(mesh, gM, &bfB0) &&
|
||||
!HoleType::TestFaceMeshCompenetration(mesh, gM, &bfB1) )
|
||||
if( !HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfB0) &&
|
||||
!HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfB1) )
|
||||
{
|
||||
ScalarType Bq = QualityFace(bfB0)+ QualityFace(bfB1);
|
||||
if( Bq > maxQuality)
|
||||
@ -546,13 +529,16 @@ public:
|
||||
sideB = BridgeAbutment<MESH>(ph2.f, ph2.z, *shit2);
|
||||
}
|
||||
|
||||
if(timer.elapsed() > 800)
|
||||
if(holesManager->autoBridgeCB != 0)
|
||||
{
|
||||
int progress = ( (100 * ( iteration +(casesViewed/cases2View)))/nIteration );
|
||||
infoLabel->setText(QString("Auto-bridging: %1%").arg(progress) );
|
||||
infoLabel->repaint();
|
||||
timer.restart();
|
||||
if(clock() - timer > holesManager->autoBridgeCB->GetOffset())
|
||||
{
|
||||
int progress = ( (100 * ( iteration +(casesViewed/cases2View)))/nIteration );
|
||||
holesManager->autoBridgeCB->Invoke(progress);
|
||||
timer = clock();
|
||||
}
|
||||
}
|
||||
|
||||
casesViewed++;
|
||||
ph2.NextB();
|
||||
}while(ph2 != (*shit2)->p);
|
||||
@ -571,11 +557,11 @@ public:
|
||||
tmpFaceRef.clear();
|
||||
if(app!=0)
|
||||
tmpFaceRef.insert(tmpFaceRef.end(), app->begin(), app->end());
|
||||
FgtHole<MESH>::AddFaceReference(holes, tmpFaceRef);
|
||||
FgtHole<MESH>::AddFaceReference(holesManager->holes, tmpFaceRef);
|
||||
tmpFaceRef.push_back(&sideA.f);
|
||||
tmpFaceRef.push_back(&sideB.f);
|
||||
|
||||
if(unifyHolesWithBridge(sideA, sideB, mesh, holes, tmpFaceRef))
|
||||
if(unifyHolesWithBridge(sideA, sideB, holesManager, tmpFaceRef))
|
||||
nb++;
|
||||
|
||||
iteration ++;
|
||||
@ -589,26 +575,22 @@ public:
|
||||
* add face adjacent to non-manifold vertex.
|
||||
* Return number of faces added.
|
||||
*/
|
||||
static int CloseNonManifoldVertex(MESH &mesh, HoleVector &holes, std::vector<FacePointer *> *app=0)
|
||||
static int CloseNonManifoldVertex(HoleSetManager<MESH>* holesManager, std::vector<FacePointer *> *app=0)
|
||||
{
|
||||
int startNholes = holes.size();
|
||||
int startNholes = holesManager->holes.size();
|
||||
int nf = 0;
|
||||
|
||||
// i riferimenti alle facce presenti nella lista di hole vengono gestiti qui perchè
|
||||
// questo metodo può fare inserimenti sulla lista di hole e quindi provocarne il riallocamento
|
||||
// e perdere così i riferimenti.
|
||||
std::vector<FacePointer *> tmpFaceRef;
|
||||
if(app!=0)
|
||||
tmpFaceRef.insert(tmpFaceRef.end(), app->begin(), app->end());
|
||||
FgtHole<MESH>::AddFaceReference(holes, tmpFaceRef);
|
||||
HoleType* oldRef = &*holes.begin();
|
||||
HoleType* oldRef = 0;
|
||||
|
||||
for(int i=0; i<startNholes; i++)
|
||||
{
|
||||
HoleType *h = &holes.at(i);
|
||||
HoleType *h = &holesManager->holes.at(i);
|
||||
if(!(h->IsNonManifold() && h->IsSelected()))
|
||||
continue;
|
||||
|
||||
// walk the border, mark as visit each vertex. If walk twice over the same vertex go back over the border
|
||||
// to find other edge sharing this vertex
|
||||
PosType curPos = h->p;
|
||||
assert(curPos.IsBorder());
|
||||
assert(!h->IsFilled());
|
||||
@ -626,15 +608,25 @@ public:
|
||||
|
||||
if(!p0.IsNull())
|
||||
{
|
||||
if( oldRef != &*holesManager->holes.begin() )
|
||||
{
|
||||
// holes vector is been reallocated... tmpFaceRes must be recomputed
|
||||
tmpFaceRef.clear();
|
||||
if(app!=0)
|
||||
tmpFaceRef.insert(tmpFaceRef.end(), app->begin(), app->end());
|
||||
FgtHole<MESH>::AddFaceReference(holesManager->holes, tmpFaceRef);
|
||||
oldRef = &*holesManager->holes.begin();
|
||||
}
|
||||
|
||||
tmpFaceRef.push_back(&p0.f);
|
||||
tmpFaceRef.push_back(&curPos.f);
|
||||
FaceIterator fit = vcg::tri::Allocator<MESH>::AddFaces(mesh, 1, tmpFaceRef);
|
||||
FaceIterator fit = vcg::tri::Allocator<MESH>::AddFaces(*holesManager->mesh, 1, tmpFaceRef);
|
||||
holesManager->faceAttr->UpdateSize();
|
||||
nf++;
|
||||
tmpFaceRef.pop_back();
|
||||
tmpFaceRef.pop_back();
|
||||
|
||||
// torno indietro lungo il bordo resettando il flag V dei vertici (tranne quello condiviso)
|
||||
// e prendo l'edge che condivide un vertice (p1)
|
||||
// non-manifold vertex found, go back over the border to find other edge share the vertex with p0
|
||||
int dist = 0;
|
||||
p1 = p0;
|
||||
p1.FlipV();
|
||||
@ -646,10 +638,9 @@ public:
|
||||
|
||||
PosType p2 = p0;
|
||||
p2.FlipV();
|
||||
p2.NextB();
|
||||
p2.NextB();
|
||||
|
||||
// si costruisce la faccia in modo da avere vertice 0 il vertice non manifold
|
||||
// e quindi l'edge 1 l'edge di bordo
|
||||
// face is build to have as vertex 0 the non-manifold vertex, so it have adge 1 as border edge
|
||||
fit->V(0) = p0.v;
|
||||
if( p0.VInd() == p0.z)
|
||||
{
|
||||
@ -683,13 +674,13 @@ public:
|
||||
}
|
||||
|
||||
ComputeNormal(*fit);
|
||||
fit->SetUserBit(FgtHole<MESH>::BridgeFlag());
|
||||
holesManager->SetBridgeAttr(&*fit);
|
||||
|
||||
if(dist==2)
|
||||
{
|
||||
// il buco da chiudere è formato solo da 3 edge e quidni viene completamente chiuso
|
||||
// senza possibilità di ripristinarlo, tanto la soluzione di riempimento è solo una
|
||||
fit->SetUserBit(FgtHole<MESH>::HolePatchFlag());
|
||||
// face used to close non-manifold holes, close entirely a "sub-hole" (sub-hole has
|
||||
// only 3 border edge). This face become a patch face wich fill an invisible subhole.
|
||||
holesManager->SetPatchAttr(&*fit);
|
||||
fit->FFp(1) = p2.f;
|
||||
fit->FFi(1) = p2.z;
|
||||
p2.f->FFp(p2.z) = &*fit;
|
||||
@ -700,23 +691,16 @@ public:
|
||||
fit->FFp(1) = &*fit;
|
||||
fit->FFi(1) = 1;
|
||||
|
||||
HoleType newhole(PosType(&*fit, 1), QString("Hole_%1").arg(HoleType::GetHoleId(),3,10,QChar('0')));
|
||||
HoleType newhole(PosType(&*fit, 1), QString("Hole_%1").arg(HoleType::GetHoleId(),3,10,QChar('0')), holesManager);
|
||||
if(h->IsSelected())
|
||||
newhole.SetSelect(true);
|
||||
newhole.SetBridged(true);
|
||||
holes.push_back(newhole);
|
||||
|
||||
if( oldRef != &*holes.begin() )
|
||||
{
|
||||
tmpFaceRef.clear();
|
||||
if(app!=0)
|
||||
tmpFaceRef.insert(tmpFaceRef.end(), app->begin(), app->end());
|
||||
FgtHole<MESH>::AddFaceReference(holes, tmpFaceRef);
|
||||
oldRef = &*holes.begin();
|
||||
h = &holes.at(i);
|
||||
}
|
||||
else
|
||||
tmpFaceRef.push_back(&holes.back().p.f);
|
||||
holesManager->holes.push_back(newhole);
|
||||
tmpFaceRef.push_back(&holesManager->holes.back().p.f);
|
||||
|
||||
// adding hole can reallocate hole vector so h must be updated
|
||||
if( oldRef != &*holesManager->holes.begin() )
|
||||
h = &holesManager->holes.at(i);
|
||||
}
|
||||
p0.SetNull();
|
||||
}
|
||||
@ -731,12 +715,11 @@ public:
|
||||
//forzo l'aggiornamento delle info dell'hole
|
||||
h->SetStartPos(h->p);
|
||||
h->SetBridged(true);
|
||||
return nf;
|
||||
}// for(int i=0; i<startNholes...
|
||||
return nf;
|
||||
};
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/* Compute distance between bridge side to allow no bridge adjacent to hole border.
|
||||
@ -764,7 +747,7 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
// if exist a face which share a vertex with each side bridge cannot be built
|
||||
// if exist a face which share a vertex with each side then the bridge cannot be built
|
||||
PosType initPos(sideA.f, sideA.z);
|
||||
PosType curPos=initPos;
|
||||
|
||||
@ -788,38 +771,38 @@ private:
|
||||
return true;
|
||||
};
|
||||
|
||||
static bool subdivideHoleWithBridge(BridgeAbutment<MESH> &sideA, BridgeAbutment<MESH> &sideB, MESH &mesh,
|
||||
HoleVector &holes, std::vector<FacePointer *> &app)
|
||||
static bool subdivideHoleWithBridge(BridgeAbutment<MESH> &sideA, BridgeAbutment<MESH> &sideB,
|
||||
HoleSetManager<MESH>* holesManager, std::vector<FacePointer *> &app)
|
||||
{
|
||||
PosType newP0, newP1;
|
||||
if( !build(mesh, sideA, sideB, newP0, newP1, app) )
|
||||
if( !build(holesManager, sideA, sideB, newP0, newP1, app) )
|
||||
return false;
|
||||
|
||||
newP0.f->SetUserBit(FgtHole<MESH>::BridgeFlag());
|
||||
newP1.f->SetUserBit(FgtHole<MESH>::BridgeFlag());
|
||||
holesManager->SetBridgeAttr(newP0.f);
|
||||
holesManager->SetBridgeAttr(newP1.f);
|
||||
|
||||
sideA.h->SetStartPos(newP0);
|
||||
sideA.h->SetBridged(true);
|
||||
FgtHole<MESH> newHole(newP1, QString("Hole_%1").arg(HoleType::GetHoleId(),3,10,QChar('0')) );
|
||||
FgtHole<MESH> newHole(newP1, QString("Hole_%1").arg(HoleType::GetHoleId(),3,10,QChar('0')), holesManager);
|
||||
if(sideA.h->IsSelected())
|
||||
newHole.SetSelect(true);
|
||||
newHole.SetBridged(true);
|
||||
holes.push_back( newHole );
|
||||
holesManager->holes.push_back( newHole );
|
||||
return true;
|
||||
};
|
||||
|
||||
static bool unifyHolesWithBridge(BridgeAbutment<MESH> &sideA, BridgeAbutment<MESH> &sideB, MESH &mesh,
|
||||
HoleVector &holes, std::vector<FacePointer *> &app)
|
||||
static bool unifyHolesWithBridge(BridgeAbutment<MESH> &sideA, BridgeAbutment<MESH> &sideB,
|
||||
HoleSetManager<MESH>* holesManager, std::vector<FacePointer *> &app)
|
||||
{
|
||||
assert(FgtHole<MESH>::IsHoleBorderFace(*sideA.f));
|
||||
assert(FgtHole<MESH>::IsHoleBorderFace(*sideB.f));
|
||||
assert(holesManager->IsHoleBorderFace(sideA.f));
|
||||
assert(holesManager->IsHoleBorderFace(sideB.f));
|
||||
|
||||
PosType newP0, newP1;
|
||||
if( !build(mesh, sideA, sideB, newP0, newP1, app) )
|
||||
if( !build(holesManager, sideA, sideB, newP0, newP1, app) )
|
||||
return false;
|
||||
|
||||
newP0.f->SetUserBit(FgtHole<MESH>::BridgeFlag());
|
||||
newP1.f->SetUserBit(FgtHole<MESH>::BridgeFlag());
|
||||
holesManager->SetBridgeAttr(newP0.f);
|
||||
holesManager->SetBridgeAttr(newP1.f);
|
||||
|
||||
sideA.h->SetStartPos(newP0);
|
||||
if(sideB.h->IsSelected())
|
||||
@ -827,10 +810,10 @@ private:
|
||||
sideA.h->SetBridged(true);
|
||||
|
||||
typename HoleVector::iterator hit;
|
||||
for(hit=holes.begin(); hit!=holes.end(); ++hit)
|
||||
for(hit=holesManager->holes.begin(); hit!=holesManager->holes.end(); ++hit)
|
||||
if(&*hit == sideB.h)
|
||||
{
|
||||
holes.erase(hit);
|
||||
holesManager->holes.erase(hit);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
@ -851,19 +834,18 @@ private:
|
||||
*
|
||||
* Return the pos located into new 2 faces added over its border edge
|
||||
*/
|
||||
static bool build(MESH &mesh, BridgeAbutment<MESH> &sideA, BridgeAbutment<MESH> &sideB,
|
||||
static bool build(HoleSetManager<MESH> *holesManager, BridgeAbutment<MESH> &sideA, BridgeAbutment<MESH> &sideB,
|
||||
PosType &pos0, PosType &pos1, std::vector<FacePointer *> &app)
|
||||
{
|
||||
vcg::GridStaticPtr<FaceType, ScalarType > gM;
|
||||
gM.Set(mesh.face.begin(),mesh.face.end());
|
||||
gM.Set(holesManager->mesh->face.begin(),holesManager->mesh->face.end());
|
||||
|
||||
// prima faccia del bridge
|
||||
VertexType* vA0 = sideA.f->V0( sideA.z ); // first vertex of pos' 1-edge
|
||||
VertexType* vA1 = sideA.f->V1( sideA.z ); // second vertex of pos' 1-edge
|
||||
VertexType* vB0 = sideB.f->V0( sideB.z ); // first vertex of pos' 2-edge
|
||||
VertexType* vB1 = sideB.f->V1( sideB.z ); // second vertex of pos' 2-edge
|
||||
|
||||
// solution A
|
||||
// case A
|
||||
FaceType bfA0;
|
||||
bfA0.V(0) = vA1; bfA0.V(1) = vA0; bfA0.V(2) = vB0;
|
||||
|
||||
@ -871,13 +853,13 @@ private:
|
||||
bfA1.V(0) = vB1; bfA1.V(1) = vB0; bfA1.V(2) = vA0;
|
||||
|
||||
ScalarType Aq;
|
||||
if( HoleType::TestFaceMeshCompenetration(mesh, gM, &bfA0) ||
|
||||
HoleType::TestFaceMeshCompenetration(mesh, gM, &bfA1) )
|
||||
if( HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfA0) ||
|
||||
HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfA1) )
|
||||
Aq = -1;
|
||||
else
|
||||
Aq = QualityFace(bfA0)+ QualityFace(bfA1);
|
||||
|
||||
// solution B
|
||||
// case B
|
||||
FaceType bfB0;
|
||||
bfB0.V(0) = vA1; bfB0.V(1) = vA0; bfB0.V(2) = vB1;
|
||||
|
||||
@ -885,8 +867,8 @@ private:
|
||||
bfB1.V(0) = vB1; bfB1.V(1) = vB0; bfB1.V(2) = vA1;
|
||||
|
||||
ScalarType Bq;
|
||||
if( HoleType::TestFaceMeshCompenetration(mesh, gM, &bfB0) ||
|
||||
HoleType::TestFaceMeshCompenetration(mesh, gM, &bfB1) )
|
||||
if( HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfB0) ||
|
||||
HoleType::TestFaceMeshCompenetration(*holesManager->mesh, gM, &bfB1) )
|
||||
Bq = -1;
|
||||
else
|
||||
Bq = QualityFace(bfB0)+ QualityFace(bfB1);
|
||||
@ -895,14 +877,15 @@ private:
|
||||
if(Aq == -1 && Bq == -1)
|
||||
return false;
|
||||
|
||||
FaceIterator fit = vcg::tri::Allocator<MESH>::AddFaces(mesh, 2, app);
|
||||
FaceIterator fit = vcg::tri::Allocator<MESH>::AddFaces(*holesManager->mesh, 2, app);
|
||||
holesManager->faceAttr->UpdateSize();
|
||||
FacePointer f0 = &*fit;
|
||||
FacePointer f1 = &*(fit+1);
|
||||
|
||||
sideA.f->ClearUserBit(FgtHole<MESH>::HoleFlag());
|
||||
sideB.f->ClearUserBit(FgtHole<MESH>::HoleFlag());
|
||||
f0->SetUserBit(FgtHole<MESH>::HoleFlag());
|
||||
f1->SetUserBit(FgtHole<MESH>::HoleFlag());
|
||||
holesManager->ClearHoleBorderAttr(sideA.f);
|
||||
holesManager->ClearHoleBorderAttr(sideB.f);
|
||||
holesManager->SetHoleBorderAttr(f0);
|
||||
holesManager->SetHoleBorderAttr(f1);
|
||||
|
||||
// the index of edge adjacent between new 2 face, is the same for both new faces
|
||||
int adjEdgeIndex = -1;
|
||||
@ -969,51 +952,13 @@ private:
|
||||
return true;
|
||||
};
|
||||
|
||||
static PosType getClosestPos(FaceType* face, int x, int y)
|
||||
{
|
||||
double mvmatrix[16], projmatrix[16];
|
||||
GLint viewport[4];
|
||||
double rx, ry, rz;
|
||||
vcg::Point2d vertex[3];
|
||||
vcg::Point2d point((double)x, (double)y);
|
||||
|
||||
glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
|
||||
glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
|
||||
for(int i=0; i<3; i++)
|
||||
{
|
||||
gluProject(face->V(i)->P()[0],face->V(i)->P()[1],face->V(i)->P()[2],mvmatrix,projmatrix,viewport,&rx,&ry,&rz);
|
||||
vertex[i] = vcg::Point2d(rx,ry);
|
||||
}
|
||||
|
||||
double dist = DBL_MAX;
|
||||
int nearest = 0;
|
||||
for(int i=0; i<3; i++)
|
||||
{
|
||||
if(!vcg::face::IsBorder<FaceType>(*face, i))
|
||||
continue;
|
||||
|
||||
vcg::Line2d el(vertex[i], vertex[(i+1)%3]-vertex[i]);
|
||||
double tmp = vcg::Distance(el, point);
|
||||
|
||||
if(dist > tmp)
|
||||
{
|
||||
dist = tmp;
|
||||
nearest = i;
|
||||
}
|
||||
}
|
||||
return PosType(face, nearest, face->V(nearest) );
|
||||
};
|
||||
|
||||
|
||||
/* Starting from an hole look for faces added to mesh as bridge jumping also into
|
||||
* holes finded crossing bridges. If bridges'll be removed these holes could belong to the same hole.
|
||||
* Put into bridgeFaces all faces added to mesh as bridge
|
||||
* Put into adjBridgePos half-edges located over the edge which will become border edge
|
||||
* after bridge face removing also mark with flag S the faces related to adjBridgePos
|
||||
*/
|
||||
static void getBridgeInfo(HoleVector &holes, std::vector<FacePointer> &bridgeFaces, std::vector<PosType> &adjBridgePos)
|
||||
static void getBridgeInfo(HoleSetManager<MESH> *holesManager, std::vector<FacePointer> &bridgeFaces, std::vector<PosType> &adjBridgePos)
|
||||
{
|
||||
//scorro gli hole
|
||||
PosType curPos;
|
||||
@ -1022,7 +967,7 @@ private:
|
||||
adjBridgePos.clear();
|
||||
|
||||
|
||||
for(hit=holes.begin(); hit!=holes.end(); ++hit)
|
||||
for(hit=holesManager->holes.begin(); hit!=holesManager->holes.end(); ++hit)
|
||||
{
|
||||
assert(!hit->IsFilled());
|
||||
if(!hit->IsBridged())
|
||||
@ -1032,7 +977,7 @@ private:
|
||||
// This happens after non-manifold closure, it adds only a face to a side of non.manifold vertex,
|
||||
// so a resultant "sub-hole" haven't bridged+border face.
|
||||
// To know this hole is connected to other hole from bridge, we add its half-edge on adjBorderPos.
|
||||
if(!FgtHole<MESH>::IsBridgeFace(*hit->p.f) )
|
||||
if(!holesManager->IsBridgeFace(hit->p.f) )
|
||||
{
|
||||
if(hit->IsSelected())
|
||||
hit->p.f->SetS();
|
||||
@ -1053,7 +998,7 @@ private:
|
||||
lastF = cp2.f;
|
||||
cp2.FlipE();
|
||||
cp2.FlipF();
|
||||
if(FgtHole<MESH>::IsBridgeFace(*cp2.f) )
|
||||
if(holesManager->IsBridgeFace(cp2.f) )
|
||||
{
|
||||
if(hit->IsSelected())
|
||||
cp2.f->SetS();
|
||||
@ -1088,7 +1033,7 @@ private:
|
||||
lastPos = curPos;
|
||||
curPos.FlipE();
|
||||
curPos.FlipF();
|
||||
if(FgtHole<MESH>::IsBridgeFace(*curPos.f))
|
||||
if(holesManager->IsBridgeFace(curPos.f))
|
||||
{
|
||||
if(vf->IsS())
|
||||
curPos.f->SetS();
|
||||
@ -1102,7 +1047,7 @@ private:
|
||||
|
||||
// look for half-edge
|
||||
FacePointer adjF = vf->FFp(e);
|
||||
if(!FgtHole<MESH>::IsBridgeFace(*adjF) && !adjF->IsV())
|
||||
if(!holesManager->IsBridgeFace(adjF) && !adjF->IsV())
|
||||
{
|
||||
adjF->SetV();
|
||||
if(vf->IsS()) adjF->SetS();
|
||||
|
||||
@ -35,14 +35,18 @@
|
||||
#include <vcg/complex/trimesh/closest.h>
|
||||
#include <vcg/space/index/grid_static_ptr.h>
|
||||
#include "vcg/space/color4.h"
|
||||
#include "holeSetManager.h"
|
||||
|
||||
/** An hole type, extends vcg::tri::Hole<MESH>::Info adding more information as filling, selection,
|
||||
* filing autocompenetration and non manifoldness. This class also allow to fill (and restore) an
|
||||
* hole using different criteria.
|
||||
/** An hole type, extends vcg::tri::Hole<MESH>::Info adding more information
|
||||
* as filling, selection, filing autocompenetration and non manifoldness.
|
||||
* This class also allow to fill (and restore) an hole using different criteria.
|
||||
*
|
||||
* FgtHole uses flags to mark interesting face, so surfing the hole faces is driven by face-face
|
||||
* ajacency and flag meaning. Hole face are flagged as:
|
||||
* - HoleBorderFace: face which initially (before filling) have 1/2 border edge.
|
||||
* FgtHole uses meshe's additional data to mark interesting face, so surfing
|
||||
* the hole faces is driven by face-face ajacency and mark meaning.
|
||||
* Additional data are accesible for an hole by parentManager (HoleSetManager)
|
||||
* which links hole to a mesh.
|
||||
* Characteristic faces for an hole are marked as:
|
||||
* - HoleBorderFace: face which initially (before filling) have 1 or 2 border edge.
|
||||
* - HolePatchFace: faces added to fill the hole
|
||||
* - PatchCompFace: patch faces which are selfintersected with mesh.
|
||||
* - BridgeFace: faces added to edit hole (unify 2 holes, subdivide an hole, partitioning a
|
||||
@ -82,13 +86,9 @@ public:
|
||||
typedef typename MESH::FaceType FaceType;
|
||||
typedef typename MESH::FacePointer FacePointer;
|
||||
typedef typename std::vector<FacePointer> FacePointerVector;
|
||||
typedef typename MESH::FaceIterator FaceIterator;
|
||||
typedef typename MESH::CoordType CoordType;
|
||||
typedef typename MESH::VertexType VertexType;
|
||||
typedef typename MESH::ScalarType ScalarType;
|
||||
typedef typename vcg::face::Pos<FaceType> PosType;
|
||||
typedef typename std::vector<PosType> PosVector;
|
||||
typedef typename PosVector::iterator PosIterator;
|
||||
typedef typename vcg::tri::Hole<MESH> vcgHole;
|
||||
typedef typename vcgHole::Info HoleInfo;
|
||||
typedef typename std::vector< FgtHole<MESH> > HoleVector;
|
||||
@ -97,18 +97,20 @@ public:
|
||||
typedef typename vcg::tri::MinimumWeightEar<MESH> MinimumWeightEar;
|
||||
typedef typename vcg::tri::SelfIntersectionEar<MESH> SelfIntersectionEar;
|
||||
|
||||
FgtHole(HoleInfo &hi, QString holeName) :
|
||||
FgtHole(HoleInfo &hi, QString holeName, HoleSetManager<MESH> *parent) :
|
||||
HoleInfo(hi.p, hi.size, hi.bb)
|
||||
{
|
||||
parentManager = parent;
|
||||
name = holeName;
|
||||
_state = ACCEPTED;
|
||||
perimeter = HoleInfo::Perimeter();
|
||||
findNonManifoldness();
|
||||
};
|
||||
|
||||
FgtHole(PosType startPos, QString holeName)
|
||||
FgtHole(PosType startPos, QString holeName, HoleSetManager<MESH> *parent)
|
||||
{
|
||||
assert(startPos.IsBorder());
|
||||
parentManager = parent;
|
||||
name = holeName;
|
||||
_state = ACCEPTED;
|
||||
this->p = startPos;
|
||||
@ -121,18 +123,8 @@ public:
|
||||
inline ScalarType Perimeter() const { return this->perimeter; };
|
||||
inline bool IsFilled() const { return (_state & FILLED) != 0; };
|
||||
inline bool IsSelected() const { return (_state & SELECTED) != 0; };
|
||||
inline void SetSelect(bool val)
|
||||
{
|
||||
if(val) _state |= SELECTED;
|
||||
else _state &= (~SELECTED);
|
||||
};
|
||||
inline bool IsCompenetrating() const { return IsFilled() && (_state & COMPENET) != 0; };
|
||||
inline bool IsAccepted() const { return !IsFilled() || ( (_state & ACCEPTED) != 0); };
|
||||
inline void SetAccepted(bool val)
|
||||
{
|
||||
if(val) _state |= ACCEPTED;
|
||||
else _state &= (~ACCEPTED);
|
||||
};
|
||||
inline bool IsNonManifold() const { return (_state & NONMANIF) != 0; };
|
||||
inline bool IsBridged() const { return (_state & BRIDGED) != 0;; };
|
||||
inline void SetBridged(bool val)
|
||||
@ -141,6 +133,30 @@ public:
|
||||
else _state &= (~BRIDGED);
|
||||
};
|
||||
|
||||
void SetSelect(bool val)
|
||||
{
|
||||
bool oldVal = IsSelected();
|
||||
if(val) _state |= SELECTED;
|
||||
else _state &= (~SELECTED);
|
||||
if(oldVal != val)
|
||||
{
|
||||
if(val) parentManager->nSelected++;
|
||||
else parentManager->nSelected--;
|
||||
}
|
||||
};
|
||||
|
||||
void SetAccepted(bool val)
|
||||
{
|
||||
bool oldVal = IsAccepted();
|
||||
if(val) _state |= ACCEPTED;
|
||||
else _state &= (~ACCEPTED);
|
||||
if(oldVal != val)
|
||||
{
|
||||
if(val) parentManager->nAccepted++;
|
||||
else parentManager->nAccepted--;
|
||||
}
|
||||
};
|
||||
|
||||
inline void SetStartPos(PosType initP)
|
||||
{
|
||||
assert(!IsFilled());
|
||||
@ -162,13 +178,11 @@ public:
|
||||
{
|
||||
assert(IsCompenetrating());
|
||||
|
||||
//std::vector<FacePointer> patch;
|
||||
//getPatchFaces(patch);
|
||||
typename std::vector<FacePointer>::const_iterator it;
|
||||
|
||||
glBegin(glmode);
|
||||
for( it=patches.begin(); it != patches.end(); it++)
|
||||
if((*it)->IsUserBit(PatchCompFlag()))
|
||||
if( parentManager->IsCompFace(*it) )
|
||||
{
|
||||
glVertex( (*it)->V(0)->P() );
|
||||
glVertex( (*it)->V(1)->P() );
|
||||
@ -187,28 +201,24 @@ public:
|
||||
std::vector<FacePointer> bridgesFaces;
|
||||
if( IsFilled() )
|
||||
{
|
||||
//std::vector<FacePointer> patch;
|
||||
//getPatchFaces(patch);
|
||||
typename std::vector<FacePointer>::iterator it;
|
||||
|
||||
//for( it=patches.begin(); it != patches.end(); it++)
|
||||
//{
|
||||
while(patches.size()>0)
|
||||
{
|
||||
FacePointer f = patches.back();
|
||||
patches.pop_back();
|
||||
f->ClearUserBit(HolePatchFlag());
|
||||
f->ClearUserBit(PatchCompFlag());
|
||||
parentManager->ClearPatchAttr(f);
|
||||
parentManager->ClearCompAttr(f);
|
||||
|
||||
// si conferma il filling della faccia BridgedFace+PatchFace aggiunta
|
||||
// dalla partizione per ottenere hole non manifold
|
||||
f->ClearUserBit(BridgeFlag());
|
||||
|
||||
parentManager->ClearBridgeAttr(f);
|
||||
|
||||
for(int i=0; i<3; i++)
|
||||
{
|
||||
FacePointer adjF = f->FFp(i);
|
||||
adjF->ClearUserBit(HoleFlag());
|
||||
if(IsBridgeFace(*adjF))
|
||||
parentManager->ClearHoleBorderAttr(adjF);
|
||||
if(parentManager->IsBridgeFace(adjF))
|
||||
bridgesFaces.push_back(adjF);
|
||||
}
|
||||
}
|
||||
@ -218,8 +228,8 @@ public:
|
||||
// we can walk the border to find hole's faces added by bridges
|
||||
PosType curPos = this->p;
|
||||
do{
|
||||
curPos.f->ClearUserBit(HoleFlag());
|
||||
if( IsBridgeFace(*curPos.f))
|
||||
parentManager->ClearHoleBorderAttr(curPos.f);
|
||||
if( parentManager->IsBridgeFace(curPos.f))
|
||||
bridgesFaces.push_back(curPos.f);
|
||||
curPos.NextB();
|
||||
}while( curPos != this->p );
|
||||
@ -229,11 +239,11 @@ public:
|
||||
{
|
||||
FacePointer f = bridgesFaces.back();
|
||||
bridgesFaces.pop_back();
|
||||
f->ClearUserBit(BridgeFlag());
|
||||
parentManager->ClearBridgeAttr(f);
|
||||
for(int i=0; i<3; i++)
|
||||
{
|
||||
FacePointer adjF = f->FFp(i);
|
||||
if(IsBridgeFace(*adjF) && !IsHoleBorderFace(*adjF))
|
||||
if(parentManager->IsBridgeFace(adjF) && !parentManager->IsHoleBorderFace(adjF))
|
||||
bridgesFaces.push_back(adjF);
|
||||
}
|
||||
}
|
||||
@ -241,25 +251,23 @@ public:
|
||||
|
||||
|
||||
/* Restore hole, remove patch faces applied to mesh to fill this hole. */
|
||||
void RestoreHole(MESH &mesh)
|
||||
void RestoreHole()
|
||||
{
|
||||
assert( IsFilled() );
|
||||
//std::vector<FaceType*> patches;
|
||||
//getPatchFaces(patches);
|
||||
_state &= (~FILLED);
|
||||
typename std::vector<FaceType*>::iterator it;
|
||||
for(it = patches.begin(); it!=patches.end(); it++)
|
||||
{
|
||||
// PathcHoleFlag+BridgeFaceFlag is special case
|
||||
if(IsBridgeFace(**it)) continue;
|
||||
if(parentManager->IsBridgeFace(*it)) continue;
|
||||
|
||||
assert(IsPatchFace(**it));
|
||||
assert(parentManager->IsPatchFace(*it));
|
||||
for(int e=0; e<3; e++)
|
||||
{
|
||||
if(!IsBorder(**it, e))
|
||||
{
|
||||
FacePointer adjF = (*it)->FFp(e);
|
||||
if(!FgtHole<MESH>::IsPatchFace(*adjF))
|
||||
if(!parentManager->IsPatchFace(adjF))
|
||||
{
|
||||
int adjEI = (*it)->FFi(e);
|
||||
adjF->FFp( adjEI ) = adjF;
|
||||
@ -269,20 +277,21 @@ public:
|
||||
}
|
||||
}
|
||||
if(!(**it).IsD())
|
||||
vcg::tri::Allocator<MESH>::DeleteFace(mesh, **it);
|
||||
vcg::tri::Allocator<MESH>::DeleteFace(*parentManager->mesh, **it);
|
||||
}
|
||||
patches.clear();
|
||||
};
|
||||
|
||||
void Fill(FillerMode mode, MESH &mesh, std::vector<FacePointer * > &local_facePointer)
|
||||
{
|
||||
int patchBit = FaceType::NewBitFlag();
|
||||
switch(mode)
|
||||
{
|
||||
case FgtHole<MESH>::Trivial:
|
||||
vcgHole::template FillHoleEar< vcg::tri::TrivialEar<MESH> >(mesh, *this, HolePatchFlag(), local_facePointer);
|
||||
vcgHole::template FillHoleEar< TrivialEar >(mesh, *this, patchBit, local_facePointer);
|
||||
break;
|
||||
case FgtHole<MESH>::MinimumWeight:
|
||||
vcgHole::template FillHoleEar< vcg::tri::MinimumWeightEar<MESH> >(mesh, *this, HolePatchFlag(), local_facePointer);
|
||||
vcgHole::template FillHoleEar< MinimumWeightEar >(mesh, *this, patchBit, local_facePointer);
|
||||
break;
|
||||
case FgtHole<MESH>::SelfIntersection:
|
||||
std::vector<FacePointer * > vfp = local_facePointer;
|
||||
@ -304,7 +313,7 @@ public:
|
||||
for( ; fpi != SelfIntersectionEar::AdjacencyRing().end(); ++fpi)
|
||||
vfp.push_back( &*fpi );
|
||||
|
||||
vcgHole::template FillHoleEar< SelfIntersectionEar >(mesh, *this, HolePatchFlag(), vfp);
|
||||
vcgHole::template FillHoleEar< SelfIntersectionEar >(mesh, *this, patchBit, vfp);
|
||||
SelfIntersectionEar::AdjacencyRing().clear();
|
||||
break;
|
||||
}
|
||||
@ -314,12 +323,43 @@ public:
|
||||
for( ;it!=vertexes.end(); it++)
|
||||
(*it)->ClearV();
|
||||
|
||||
parentManager->faceAttr->UpdateSize();
|
||||
|
||||
_state |= FILLED;
|
||||
_state |= ACCEPTED;
|
||||
_state &= (~COMPENET);
|
||||
updatePatchState(mesh);
|
||||
updatePatchState(patchBit);
|
||||
FaceType::DeleteBitFlag(patchBit);
|
||||
};
|
||||
|
||||
/* Check if face is a border face of this hole */
|
||||
bool HaveBorderFace(FacePointer bFace) const
|
||||
{
|
||||
assert(parentManager->IsHoleBorderFace(bFace));
|
||||
assert( !IsFilled() );
|
||||
|
||||
PosType curPos = this->p;
|
||||
do{
|
||||
if(curPos.f == bFace)
|
||||
return true;
|
||||
curPos.NextB();
|
||||
}while( curPos != this->p );
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if pFace is a patch face of this hole */
|
||||
bool HavePatchFace(FacePointer pFace) const
|
||||
{
|
||||
assert( parentManager->IsPatchFace(pFace) );
|
||||
assert( IsFilled() );
|
||||
|
||||
// follow algorithm used to fill with EAR, each faces added share at least e vertex with hole
|
||||
typename std::vector<VertexType*>::const_iterator it;
|
||||
for(it = vertexes.begin(); it!=vertexes.end(); it++)
|
||||
if(pFace->V(0) == *it || pFace->V(1) == *it || pFace->V(2) == *it)
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
@ -335,13 +375,12 @@ private:
|
||||
PosType curPos = this->p;
|
||||
do{
|
||||
assert(!curPos.f->IsD());
|
||||
curPos.f->SetUserBit(HoleFlag());
|
||||
parentManager->SetHoleBorderAttr(curPos.f);
|
||||
this->bb.Add(curPos.v->cP());
|
||||
++this->size;
|
||||
vertexes.push_back(curPos.v);
|
||||
if(curPos.v->IsV())
|
||||
_state |= NONMANIF;
|
||||
//isNonManifold = true;
|
||||
else
|
||||
curPos.v->SetV();
|
||||
|
||||
@ -367,7 +406,6 @@ private:
|
||||
do{
|
||||
vertexes.push_back(curPos.v);
|
||||
if(curPos.v->IsV())
|
||||
//isNonManifold = true;
|
||||
_state |= NONMANIF;
|
||||
else
|
||||
curPos.v->SetV();
|
||||
@ -382,41 +420,42 @@ private:
|
||||
};
|
||||
|
||||
/* set patch flag and auto-compenetration flag when needed */
|
||||
void updatePatchState(MESH &mesh)
|
||||
void updatePatchState(int patchFlag)
|
||||
{
|
||||
assert( IsFilled() );
|
||||
_state &= (~COMPENET);
|
||||
vcg::GridStaticPtr<FaceType, ScalarType > gM;
|
||||
gM.Set(mesh.face.begin(),mesh.face.end());
|
||||
gM.Set(parentManager->mesh->face.begin(),parentManager->mesh->face.end());
|
||||
|
||||
std::vector<FaceType*> inBox;
|
||||
vcg::Box3< ScalarType> bbox;
|
||||
//FacePointerVector patches;
|
||||
//getPatchFaces(patches);
|
||||
getPatchFaces();
|
||||
getPatchFaces(patchFlag);
|
||||
typename FacePointerVector::iterator pi = patches.begin();
|
||||
for( ; pi!=patches.end(); ++pi)
|
||||
{
|
||||
FacePointer f = *pi;
|
||||
if(TestFaceMeshCompenetration(mesh, gM, f))
|
||||
if(TestFaceMeshCompenetration(*parentManager->mesh, gM, f))
|
||||
{
|
||||
_state |= COMPENET;
|
||||
f->SetUserBit(PatchCompFlag());
|
||||
parentManager->SetCompAttr(f);
|
||||
}
|
||||
(*pi)->ClearUserBit(patchFlag);
|
||||
parentManager->SetPatchAttr(*pi);
|
||||
}
|
||||
};
|
||||
|
||||
/* First patch face is the adjacent one to initial Pos ("p" field of Hole::Info)
|
||||
* Other patch face are found looking adjacent face on each vertex of known patch faces.
|
||||
* NB: looking adjacent faces to vertexes it can find patches also for non manifold hole.
|
||||
*/
|
||||
void getPatchFaces()
|
||||
void getPatchFaces(int patchFlag)
|
||||
{
|
||||
assert( IsFilled() );
|
||||
patches.clear();
|
||||
std::vector<FacePointer> stack;
|
||||
PosType pos = this->p;
|
||||
pos.FlipF();
|
||||
assert(IsPatchFace(*pos.f));
|
||||
assert(pos.f->IsUserBit(patchFlag));
|
||||
pos.f->SetV();
|
||||
stack.push_back(pos.f);
|
||||
while(stack.size()>0)
|
||||
@ -425,14 +464,14 @@ private:
|
||||
stack.pop_back();
|
||||
patches.push_back(f);
|
||||
|
||||
//visito le facce patch adiacenti ai vertici di questa faccia patch
|
||||
// visit faces adjacent to f's vertexes
|
||||
for(int v=0; v<3; v++)
|
||||
{
|
||||
pos = PosType(f, v);
|
||||
do{
|
||||
pos.FlipF();
|
||||
pos.FlipE();
|
||||
if(IsPatchFace(*pos.f) && !pos.f->IsV())
|
||||
if(pos.f->IsUserBit(patchFlag) && !pos.f->IsV())
|
||||
{
|
||||
pos.f->SetV();
|
||||
stack.push_back(pos.f);
|
||||
@ -446,137 +485,12 @@ private:
|
||||
(*it)->ClearV();
|
||||
};
|
||||
|
||||
/* Check if face is a border face of this hole */
|
||||
bool haveBorderFace(FacePointer bFace) const
|
||||
{
|
||||
assert(IsHoleBorderFace(*bFace));
|
||||
assert( !IsFilled() );
|
||||
|
||||
PosType curPos = this->p;
|
||||
do{
|
||||
if(curPos.f == bFace)
|
||||
return true;
|
||||
curPos.NextB();
|
||||
}while( curPos != this->p );
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if pFace is a patch face of this hole */
|
||||
bool havePatchFace(FacePointer pFace) const
|
||||
{
|
||||
assert( IsPatchFace(*pFace) );
|
||||
assert( IsFilled() );
|
||||
|
||||
// follow algorithm used to fill with EAR, each faces added share at least e vertex with hole
|
||||
typename std::vector<VertexType*>::const_iterator it;
|
||||
for(it = vertexes.begin(); it!=vertexes.end(); it++)
|
||||
if(pFace->V(0) == *it || pFace->V(1) == *it || pFace->V(2) == *it)
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
/********* Static functions **********/
|
||||
public:
|
||||
|
||||
static inline bool IsHoleBorderFace(FaceType &face)
|
||||
{ return face.IsUserBit(HoleFlag()); };
|
||||
|
||||
static inline bool IsPatchFace(FaceType &face)
|
||||
{ return face.IsUserBit(HolePatchFlag()); };
|
||||
|
||||
static inline bool IsBridgeFace(FaceType &face)
|
||||
{ return face.IsUserBit(BridgeFlag()); };
|
||||
|
||||
|
||||
/* Inspect a mesh to find its holes. */
|
||||
static int GetMeshHoles(MESH &mesh, HoleVector &ret)
|
||||
{
|
||||
assert(HoleFlag() ==-1);
|
||||
ret.clear();
|
||||
std::vector<HoleInfo> vhi;
|
||||
|
||||
//prendo la lista di info(sugli hole) tutte le facce anche le non selezionate
|
||||
HoleFlag() = vcgHole::GetInfo(mesh, false, vhi);
|
||||
HolePatchFlag() = FaceType::NewBitFlag();
|
||||
PatchCompFlag() = FaceType::NewBitFlag();
|
||||
BridgeFlag() = FaceType::NewBitFlag();
|
||||
|
||||
ResetHoleId();
|
||||
typename std::vector<HoleInfo>::iterator itH = vhi.begin();
|
||||
int i=0;
|
||||
for( ; itH != vhi.end(); itH++)
|
||||
{
|
||||
ret.push_back(FgtHole<MESH>(*itH, QString("Hole_%1").arg(GetHoleId(),3,10,QChar('0')) ));
|
||||
i++;
|
||||
}
|
||||
return HoleFlag();
|
||||
}
|
||||
|
||||
/** Return index of hole adjacent to picked face into holes vector.
|
||||
* Also return the iterator on correct position in holes list.
|
||||
|
||||
/* Starting from holes stored into a vector this function extract all reference to mesh faces
|
||||
* and adds them to vector facesReferences
|
||||
*/
|
||||
static int FindHoleFromBorderFace(FacePointer bFace, HoleVector &holes, HoleIterator &it)
|
||||
{
|
||||
assert(IsHoleBorderFace(*bFace));
|
||||
int index = 0;
|
||||
|
||||
// it know if bFace is adjacent to patchFace
|
||||
FacePointer adjF = 0;
|
||||
for( int i=0; i<3; i++)
|
||||
if(IsPatchFace(*bFace->FFp(i)) && !IsBridgeFace(*bFace->FFp(i)))
|
||||
adjF = bFace->FFp(i);
|
||||
|
||||
HoleIterator hit = holes.begin();
|
||||
if(adjF == 0)
|
||||
{
|
||||
// border face belong to an hole not filled
|
||||
for( ; hit != holes.end(); ++hit)
|
||||
{
|
||||
if(!hit->IsFilled())
|
||||
if(hit->haveBorderFace(bFace))
|
||||
{
|
||||
it = hit;
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
it = holes.end(); // invalid iterator
|
||||
return -1;
|
||||
}
|
||||
else // bFace belong filled hole, adjF is its patch
|
||||
return FindHoleFromPatchFace(adjF, holes, it);
|
||||
}
|
||||
|
||||
|
||||
/** Return index into holes vector of hole adjacent to picked face */
|
||||
static int FindHoleFromPatchFace(FacePointer bFace, HoleVector &holes, HoleIterator &it)
|
||||
{
|
||||
assert(IsPatchFace(*bFace));
|
||||
int index = 0;
|
||||
HoleIterator hit = holes.begin();
|
||||
for( ; hit != holes.end(); ++hit)
|
||||
{
|
||||
// for each hole check if face is its border face
|
||||
if(hit->IsFilled())
|
||||
if(hit->havePatchFace(bFace))
|
||||
{
|
||||
it = hit;
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
it = holes.end(); // invalid iterator
|
||||
return -1; // means no find hole
|
||||
};
|
||||
|
||||
static void DeleteFlag()
|
||||
{
|
||||
FaceType::DeleteBitFlag(BridgeFlag()); BridgeFlag()=-1;
|
||||
FaceType::DeleteBitFlag(PatchCompFlag()); PatchCompFlag()=-1;
|
||||
FaceType::DeleteBitFlag(HolePatchFlag()); HolePatchFlag()=-1;
|
||||
FaceType::DeleteBitFlag(HoleFlag()); HoleFlag()=-1;
|
||||
}
|
||||
|
||||
static void AddFaceReference(HoleVector& holes, std::vector<FacePointer*> &facesReferences)
|
||||
{
|
||||
typename HoleVector::iterator it = holes.begin();
|
||||
@ -608,16 +522,15 @@ public:
|
||||
|
||||
|
||||
public:
|
||||
static int& HoleFlag() {static int _hf=-1; return _hf;};
|
||||
static int& HolePatchFlag() {static int _pf=-1; return _pf; };
|
||||
static int& PatchCompFlag() {static int _pcf=-1; return _pcf; };
|
||||
static int& BridgeFlag() {static int _bf=-1; return _bf; };
|
||||
static int &HoleId(){static int _holeId=0; return _holeId;};
|
||||
static void ResetHoleId() { HoleId()=0; };
|
||||
static int GetHoleId() { return ++HoleId(); };
|
||||
QString name;
|
||||
|
||||
public:
|
||||
HoleSetManager<MESH>* parentManager;
|
||||
|
||||
private:
|
||||
static int &HoleId(){static int _holeId=0; return _holeId;};
|
||||
int _state;
|
||||
ScalarType perimeter;
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ FillerDialog::FillerDialog(QWidget *parent)
|
||||
FillerDialog::~FillerDialog() {}
|
||||
|
||||
|
||||
void FillerDialog::closeEvent ( QCloseEvent * event )
|
||||
void FillerDialog::closeEvent ( QCloseEvent * /*event*/ )
|
||||
{
|
||||
emit SGN_Closing();
|
||||
}
|
||||
|
||||
@ -1,494 +1,420 @@
|
||||
/****************************************************************************
|
||||
* VCGLib o o *
|
||||
* Visual and Computer Graphics Library o o *
|
||||
* _ O _ *
|
||||
* Copyright(C) 2004 \/)\/ *
|
||||
* 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. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#include "holeListModel.h"
|
||||
|
||||
using namespace vcg;
|
||||
|
||||
|
||||
HoleListModel::HoleListModel(MeshModel *m, QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
state = HoleListModel::Selection;
|
||||
mesh = m;
|
||||
userBitHole = -1;
|
||||
pickedAbutment.SetNull();
|
||||
updateModel();
|
||||
}
|
||||
|
||||
void HoleListModel::clearModel()
|
||||
{
|
||||
HoleVector::iterator it;
|
||||
for(it=holes.begin(); it!=holes.end(); it++)
|
||||
it->ResetFlag();
|
||||
|
||||
holes.clear();
|
||||
pickedAbutment.SetNull();
|
||||
}
|
||||
|
||||
void HoleListModel::updateModel()
|
||||
{
|
||||
clearModel();
|
||||
mesh->clearDataMask(MeshModel::MM_FACEFLAGBORDER);
|
||||
mesh->updateDataMask(MeshModel::MM_FACEFACETOPO);
|
||||
mesh->updateDataMask(MeshModel::MM_FACEFLAGBORDER);
|
||||
|
||||
userBitHole = FgtHole<CMeshO>::GetMeshHoles(mesh->cm, holes);
|
||||
nSelected=0;
|
||||
nAccepted=0;
|
||||
emit dataChanged( index(0, 0), index(holes.size(), 2) );
|
||||
emit SGN_needUpdateGLA();
|
||||
}
|
||||
|
||||
void HoleListModel::drawHoles() const
|
||||
{
|
||||
// Disegno i contorni del buco
|
||||
glLineWidth(2.0f);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDepthFunc(GL_GREATER);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// scorro tutti i buchi
|
||||
HoleVector::const_iterator it = holes.begin();
|
||||
for( ; it != holes.end(); ++it)
|
||||
{
|
||||
if( it->IsSelected())
|
||||
{
|
||||
if(it->IsAccepted())
|
||||
glColor(Color4b::DarkGreen);
|
||||
else
|
||||
glColor(Color4b::DarkRed);
|
||||
}
|
||||
else
|
||||
glColor(Color4b::DarkBlue);
|
||||
it->Draw();
|
||||
}
|
||||
|
||||
|
||||
if(!pickedAbutment.IsNull())
|
||||
{
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glLineWidth(2.0f);
|
||||
glColor(Color4b::Yellow);
|
||||
glBegin(GL_LINES);
|
||||
glVertex( pickedAbutment.f->V0(pickedAbutment.z)->P() );
|
||||
glVertex( pickedAbutment.f->V1(pickedAbutment.z)->P() );
|
||||
glEnd();
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
glLineWidth(2.0f);
|
||||
for(it = holes.begin(); it != holes.end(); ++it)
|
||||
{
|
||||
if(it->IsSelected())
|
||||
{
|
||||
if(it->IsAccepted())
|
||||
glColor(Color4b::Green);
|
||||
else
|
||||
glColor(Color4b::Red);
|
||||
}
|
||||
else
|
||||
glColor(Color4b::Blue);
|
||||
|
||||
it->Draw();
|
||||
}
|
||||
}
|
||||
|
||||
void HoleListModel::drawCompenetratingFaces() const
|
||||
{
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glDisable(GL_LIGHTING);
|
||||
glColor3f(0.8f, 0.8f, 0.f);
|
||||
HoleVector::const_iterator it;
|
||||
for(it = holes.begin(); it != holes.end(); ++it)
|
||||
if(it->IsCompenetrating())
|
||||
it->DrawCompenetratingFace(GL_LINE_LOOP);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
for(it = holes.begin(); it != holes.end(); ++it)
|
||||
if(it->IsCompenetrating())
|
||||
it->DrawCompenetratingFace(GL_TRIANGLES);
|
||||
|
||||
glLineWidth(4.0f);
|
||||
glColor3f(1.0f, 1.0f, 0.f);
|
||||
for(it = holes.begin(); it != holes.end(); ++it)
|
||||
if(it->IsCompenetrating())
|
||||
it->DrawCompenetratingFace(GL_LINE_LOOP);
|
||||
|
||||
}
|
||||
|
||||
void HoleListModel::toggleSelectionHoleFromBorderFace(CFaceO *bface)
|
||||
{
|
||||
assert(FgtHole<CMeshO>::IsHoleBorderFace(*bface));
|
||||
HoleVector::iterator h;
|
||||
int ind = FgtHole<CMeshO>::FindHoleFromBorderFace(bface, holes, h);
|
||||
if( ind == -1)
|
||||
return;
|
||||
h->SetSelect( !h->IsSelected() );
|
||||
if(h->IsSelected())
|
||||
nSelected++;
|
||||
else
|
||||
nSelected--;
|
||||
emit dataChanged( index(ind, 4), index(ind, 4) );
|
||||
emit SGN_needUpdateGLA();
|
||||
}
|
||||
|
||||
void HoleListModel::toggleAcceptanceHole(CFaceO *bface)
|
||||
{
|
||||
assert(state == HoleListModel::Filled);
|
||||
HoleVector::iterator h;
|
||||
int ind = -1;
|
||||
if(FgtHole<CMeshO>::IsHoleBorderFace(*bface))
|
||||
ind = FgtHole<CMeshO>::FindHoleFromBorderFace(bface, holes, h);
|
||||
else if(FgtHole<CMeshO>::IsPatchFace(*bface) && !FgtHole<CMeshO>::IsBridgeFace(*bface))
|
||||
ind = FgtHole<CMeshO>::FindHoleFromPatchFace(bface, holes, h);
|
||||
|
||||
if(ind == -1)
|
||||
return;
|
||||
h->SetAccepted( !h->IsAccepted() );
|
||||
if(h->IsAccepted())
|
||||
nAccepted++;
|
||||
else
|
||||
nAccepted--;
|
||||
emit dataChanged( index(ind, 6), index(ind, 6) );
|
||||
emit SGN_needUpdateGLA();
|
||||
}
|
||||
|
||||
void HoleListModel::addBridgeFace(CFaceO *pickedFace, int pickedX, int pickedY)
|
||||
{
|
||||
BridgeAbutment<CMeshO> picked;
|
||||
if (!FgtBridge<CMeshO>::FindBridgeAbutmentFromPick(pickedFace, pickedX, pickedY, holes, picked))
|
||||
return;
|
||||
|
||||
if(pickedAbutment.f == picked.f && pickedAbutment.z == picked.z )
|
||||
{
|
||||
pickedAbutment.SetNull();
|
||||
return;
|
||||
}
|
||||
|
||||
if(pickedAbutment.IsNull() || pickedAbutment.f == picked.f)
|
||||
pickedAbutment = picked;
|
||||
else
|
||||
{
|
||||
std::vector<CMeshO::FacePointer *> local_facePointer;
|
||||
local_facePointer.push_back(&pickedAbutment.f);
|
||||
local_facePointer.push_back(&picked.f);
|
||||
|
||||
if(FgtBridge<CMeshO>::CreateBridge(pickedAbutment, picked, mesh->cm, holes, &local_facePointer))
|
||||
{
|
||||
emit SGN_ExistBridge(true);
|
||||
emit layoutChanged();
|
||||
}
|
||||
else
|
||||
QMessageBox::warning(0, tr("Bridge autocompenetration"), QString("Bridge is compenetrating with mesh."));
|
||||
|
||||
pickedAbutment.SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
void HoleListModel::fill(FgtHole<CMeshO>::FillerMode mode)
|
||||
{
|
||||
std::vector<CMeshO::FacePointer *> local_facePointer;
|
||||
|
||||
HoleVector::iterator it;
|
||||
FgtHole<CMeshO>::AddFaceReference(holes, local_facePointer);
|
||||
|
||||
for(it = holes.begin(); it != holes.end(); it++ )
|
||||
if( it->IsSelected() )
|
||||
{
|
||||
it->Fill(mode, mesh->cm, local_facePointer);
|
||||
state = HoleListModel::Filled;
|
||||
}
|
||||
nAccepted=nSelected;
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
void HoleListModel::acceptFilling(bool accept)
|
||||
{
|
||||
nSelected=0;
|
||||
HoleVector::iterator it = holes.begin();
|
||||
while( it != holes.end() )
|
||||
{
|
||||
if( it->IsFilled() )
|
||||
{
|
||||
if( (it->IsSelected() && !it->IsAccepted()) || !accept)
|
||||
{
|
||||
if( it->IsFilled() )
|
||||
{
|
||||
it->RestoreHole(mesh->cm);
|
||||
nSelected++;
|
||||
}
|
||||
}
|
||||
else if( it->IsSelected() && it->IsAccepted() )
|
||||
{
|
||||
it->ResetFlag();
|
||||
it = holes.erase(it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
state = HoleListModel::Selection;
|
||||
emit dataChanged( index(0, 0), index(holes.size(), 2) );
|
||||
emit SGN_needUpdateGLA();
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
void HoleListModel::autoBridge(bool singleHole, double distCoeff, QLabel *infoLabel)
|
||||
{
|
||||
FgtBridge<CMeshO>::RemoveBridges(mesh->cm, holes);
|
||||
|
||||
int nb=0;
|
||||
if(singleHole)
|
||||
nb = FgtBridge<CMeshO>::AutoSelfBridging(mesh->cm, holes, infoLabel, distCoeff, 0);
|
||||
else
|
||||
nb = FgtBridge<CMeshO>::AutoMultiBridging(mesh->cm, holes, infoLabel, distCoeff, 0);
|
||||
|
||||
countSelected();
|
||||
|
||||
emit SGN_ExistBridge( nb>0 );
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
void HoleListModel::removeBridges()
|
||||
{
|
||||
FgtBridge<CMeshO>::RemoveBridges(mesh->cm, holes);
|
||||
countSelected();
|
||||
|
||||
emit SGN_ExistBridge(false);
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
void HoleListModel::acceptBridges()
|
||||
{
|
||||
FgtBridge<CMeshO>::AcceptBridges(holes);
|
||||
state = HoleListModel::Selection;
|
||||
emit SGN_ExistBridge(false);
|
||||
}
|
||||
|
||||
void HoleListModel::closeNonManifolds()
|
||||
{
|
||||
if (FgtBridge<CMeshO>::CloseNonManifoldVertex(mesh->cm, holes) > 0 )
|
||||
{
|
||||
countSelected();
|
||||
emit SGN_ExistBridge(true);
|
||||
}
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
void HoleListModel::countSelected()
|
||||
{
|
||||
nSelected = 0;
|
||||
HoleVector::iterator hit = holes.begin();
|
||||
for( ; hit!=holes.end(); hit++)
|
||||
if(hit->IsSelected())
|
||||
nSelected++;
|
||||
}
|
||||
|
||||
/************* Implementazione QAbstractItemModel class *****************/
|
||||
|
||||
|
||||
QVariant HoleListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if(!index.isValid() )
|
||||
return QVariant();
|
||||
|
||||
if(role == Qt::DisplayRole)
|
||||
{
|
||||
switch(index.column())
|
||||
{
|
||||
case 0:
|
||||
return holes[index.row()].name;
|
||||
case 1:
|
||||
return holes.at(index.row()).Size();
|
||||
case 2:
|
||||
return QString("%1").arg(holes.at(index.row()).Perimeter(), 0, 'f', 5);
|
||||
}
|
||||
}
|
||||
else if (role == Qt::TextAlignmentRole)
|
||||
{
|
||||
if(index.column() == 0) return Qt::AlignLeft;
|
||||
if(index.column() == 1 ||
|
||||
index.column() == 2) return Qt::AlignRight;
|
||||
return Qt::AlignCenter;
|
||||
}
|
||||
else if (role == Qt::CheckStateRole)
|
||||
{
|
||||
bool checked;
|
||||
if(index.column() == 3)
|
||||
checked = holes[index.row()].IsNonManifold();
|
||||
else if(index.column() == 4)
|
||||
checked = holes[index.row()].IsSelected();
|
||||
else if(state == HoleListModel::Filled && holes[index.row()].IsSelected())
|
||||
{
|
||||
if(index.column() == 5)
|
||||
checked = holes[index.row()].IsCompenetrating();
|
||||
else if(index.column() == 6)
|
||||
checked = holes[index.row()].IsAccepted();
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
else
|
||||
return QVariant();
|
||||
|
||||
if(checked)
|
||||
return Qt::Checked;
|
||||
else
|
||||
return Qt::Unchecked;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
||||
QVariant HoleListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
||||
{
|
||||
switch(section)
|
||||
{
|
||||
case 0:
|
||||
return tr("Hole");
|
||||
case 1:
|
||||
return tr("Edges");
|
||||
case 2:
|
||||
return tr("Perimeter");
|
||||
case 3:
|
||||
return tr("Non Manif.");
|
||||
case 4:
|
||||
if(state == HoleListModel::Filled)
|
||||
return tr("Fill");
|
||||
else
|
||||
return tr("Select");
|
||||
case 5:
|
||||
if(state == HoleListModel::Filled)
|
||||
return tr("Comp.");
|
||||
case 6:
|
||||
if(state == HoleListModel::Filled)
|
||||
return tr("Accept");
|
||||
}
|
||||
}
|
||||
else if (orientation == Qt::Horizontal && role == Qt::SizeHintRole)
|
||||
{
|
||||
switch(section)
|
||||
{
|
||||
case 0:
|
||||
return QSize(63, 20);
|
||||
case 1:
|
||||
return QSize(38, 20);
|
||||
case 2:
|
||||
return QSize(55, 20);
|
||||
case 3:
|
||||
return QSize(60, 20);
|
||||
case 4:
|
||||
if(state == HoleListModel::Filled)
|
||||
return QSize(20, 20);
|
||||
else
|
||||
return QSize(50, 20);
|
||||
case 5:
|
||||
return QSize(38, 20);
|
||||
case 6:
|
||||
return QSize(42, 20);
|
||||
}
|
||||
}
|
||||
else if (orientation == Qt::Horizontal && role == Qt::ToolTip && state==HoleListModel::Filled && section == 4)
|
||||
return tr("Compenetration");
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
||||
QModelIndex HoleListModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if(row >= (int)holes.size())
|
||||
return QModelIndex();
|
||||
return createIndex(row,column, 0);
|
||||
}
|
||||
|
||||
|
||||
Qt::ItemFlags HoleListModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
Qt::ItemFlags ret = QAbstractItemModel::flags(index);
|
||||
|
||||
if (!index.isValid())
|
||||
return Qt::ItemIsEnabled;
|
||||
|
||||
if(index.column() == 0)
|
||||
ret = ret | Qt::ItemIsEditable;
|
||||
else if( (index.column() == 4 && state == HoleListModel::Selection) ||
|
||||
(index.column() == 6 && state == HoleListModel::Filled) )
|
||||
return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
|
||||
//return ret = ret | Qt::ItemIsUserCheckable ;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool HoleListModel::setData( const QModelIndex & index, const QVariant & value, int role )
|
||||
{
|
||||
if(!index.isValid())
|
||||
return false;
|
||||
|
||||
bool ret = false;
|
||||
if(role == Qt::EditRole && index.column() == 0)
|
||||
{
|
||||
QString newName = value.toString().trimmed();
|
||||
if(newName != "")
|
||||
{
|
||||
holes[index.row()].name = newName;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
else if(role == Qt::CheckStateRole)
|
||||
{
|
||||
if(state == HoleListModel::Selection)
|
||||
{
|
||||
if(index.column() == 4 && state == HoleListModel::Selection)
|
||||
{
|
||||
holes[index.row()].SetSelect( !holes[index.row()].IsSelected() );
|
||||
if(holes[index.row()].IsSelected() ) nSelected++;
|
||||
else nSelected--;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
else if(index.column() == 6)
|
||||
{ // check accept
|
||||
holes[index.row()].SetAccepted( !holes[index.row()].IsAccepted() );
|
||||
if(holes[index.row()].IsAccepted() ) nAccepted++;
|
||||
else nAccepted--;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
if(ret)
|
||||
{
|
||||
emit dataChanged(index, index);
|
||||
emit SGN_needUpdateGLA();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
/****************************************************************************
|
||||
* VCGLib o o *
|
||||
* Visual and Computer Graphics Library o o *
|
||||
* _ O _ *
|
||||
* Copyright(C) 2004 \/)\/ *
|
||||
* 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. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#include "holeListModel.h"
|
||||
|
||||
using namespace vcg;
|
||||
|
||||
|
||||
HoleListModel::HoleListModel(MeshModel *m, QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
state = HoleListModel::Selection;
|
||||
mesh = m;
|
||||
pickedAbutment.SetNull();
|
||||
|
||||
mesh->clearDataMask(MeshModel::MM_FACEFLAGBORDER);
|
||||
mesh->updateDataMask(MeshModel::MM_FACEFACETOPO);
|
||||
mesh->updateDataMask(MeshModel::MM_FACEFLAGBORDER);
|
||||
holesManager.Init(&m->cm);
|
||||
emit dataChanged( index(0, 0), index(holesManager.HolesCount(), 2) );
|
||||
emit SGN_needUpdateGLA();
|
||||
}
|
||||
|
||||
|
||||
void HoleListModel::drawHoles() const
|
||||
{
|
||||
glLineWidth(2.0f);
|
||||
glDepthMask(GL_TRUE);
|
||||
glDepthFunc(GL_GREATER);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
HoleVector::const_iterator it = holesManager.holes.begin();
|
||||
for( ; it != holesManager.holes.end(); ++it)
|
||||
{
|
||||
if( it->IsSelected())
|
||||
{
|
||||
if(it->IsAccepted())
|
||||
glColor(Color4b::DarkGreen);
|
||||
else
|
||||
glColor(Color4b::DarkRed);
|
||||
}
|
||||
else
|
||||
glColor(Color4b::DarkBlue);
|
||||
it->Draw();
|
||||
}
|
||||
|
||||
// draw the edge selected as a bridge abutment in manual-bridging
|
||||
if(!pickedAbutment.IsNull())
|
||||
{
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glLineWidth(2.0f);
|
||||
glColor(Color4b::Yellow);
|
||||
glBegin(GL_LINES);
|
||||
glVertex( pickedAbutment.f->V0(pickedAbutment.z)->P() );
|
||||
glVertex( pickedAbutment.f->V1(pickedAbutment.z)->P() );
|
||||
glEnd();
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
glLineWidth(2.0f);
|
||||
for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
|
||||
{
|
||||
if(it->IsSelected())
|
||||
{
|
||||
if(it->IsAccepted())
|
||||
glColor(Color4b::Green);
|
||||
else
|
||||
glColor(Color4b::Red);
|
||||
}
|
||||
else
|
||||
glColor(Color4b::Blue);
|
||||
|
||||
it->Draw();
|
||||
}
|
||||
}
|
||||
|
||||
void HoleListModel::drawCompenetratingFaces() const
|
||||
{
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glDisable(GL_LIGHTING);
|
||||
glColor3f(0.8f, 0.8f, 0.f);
|
||||
|
||||
// draw face border also behind other face
|
||||
HoleVector::const_iterator it;
|
||||
for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
|
||||
if(it->IsCompenetrating())
|
||||
it->DrawCompenetratingFace(GL_LINE_LOOP);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
// draw compenetrating face, only the visible part
|
||||
for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
|
||||
if(it->IsCompenetrating())
|
||||
it->DrawCompenetratingFace(GL_TRIANGLES);
|
||||
|
||||
// draw face border only visible part
|
||||
glLineWidth(4.0f);
|
||||
glColor3f(1.0f, 1.0f, 0.f);
|
||||
for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
|
||||
if(it->IsCompenetrating())
|
||||
it->DrawCompenetratingFace(GL_LINE_LOOP);
|
||||
}
|
||||
|
||||
void HoleListModel::toggleSelectionHoleFromFace(CFaceO *bface)
|
||||
{
|
||||
assert(holesManager.IsHoleBorderFace(bface));
|
||||
HoleVector::iterator h;
|
||||
|
||||
int ind = holesManager.FindHoleFromFace(bface, h);
|
||||
if( ind == -1)
|
||||
return;
|
||||
h->SetSelect( !h->IsSelected() );
|
||||
|
||||
emit dataChanged( index(ind, 4), index(ind, 4) );
|
||||
emit SGN_needUpdateGLA();
|
||||
}
|
||||
|
||||
void HoleListModel::toggleAcceptanceHole(CFaceO *bface)
|
||||
{
|
||||
assert(state == HoleListModel::Filled);
|
||||
HoleVector::iterator h;
|
||||
int ind = holesManager.FindHoleFromFace(bface, h);
|
||||
|
||||
if(ind == -1)
|
||||
return;
|
||||
h->SetAccepted( !h->IsAccepted() );
|
||||
|
||||
emit dataChanged( index(ind, 6), index(ind, 6) );
|
||||
emit SGN_needUpdateGLA();
|
||||
}
|
||||
|
||||
void HoleListModel::addBridgeFace(CFaceO *pickedFace, int pickedX, int pickedY)
|
||||
{
|
||||
BridgeAbutment<CMeshO> picked;
|
||||
if (!holesManager.FindBridgeAbutmentFromPick(pickedFace, pickedX, pickedY, picked))
|
||||
return;
|
||||
|
||||
// reselect an already selected edge... deselect
|
||||
if(pickedAbutment.f == picked.f && pickedAbutment.z == picked.z )
|
||||
{
|
||||
pickedAbutment.SetNull();
|
||||
return;
|
||||
}
|
||||
|
||||
if(pickedAbutment.IsNull() || pickedAbutment.f == picked.f)
|
||||
pickedAbutment = picked;
|
||||
else
|
||||
{
|
||||
// 2 edge selected... bridge building
|
||||
std::vector<CMeshO::FacePointer *> local_facePointer;
|
||||
local_facePointer.push_back(&pickedAbutment.f);
|
||||
local_facePointer.push_back(&picked.f);
|
||||
|
||||
if(FgtBridge<CMeshO>::CreateBridge(pickedAbutment, picked, &holesManager, &local_facePointer))
|
||||
{
|
||||
emit SGN_ExistBridge(true);
|
||||
emit layoutChanged();
|
||||
}
|
||||
else
|
||||
QMessageBox::warning(0, tr("Bridge autocompenetration"), QString("Bridge is compenetrating with mesh."));
|
||||
|
||||
pickedAbutment.SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
void HoleListModel::fill(FgtHole<CMeshO>::FillerMode mode)
|
||||
{
|
||||
if(holesManager.Fill(mode))
|
||||
{
|
||||
state = HoleListModel::Filled;
|
||||
emit layoutChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void HoleListModel::acceptFilling(bool accept)
|
||||
{
|
||||
holesManager.ConfirmFilling(accept);
|
||||
state = HoleListModel::Selection;
|
||||
emit dataChanged( index(0, 0), index(holesManager.HolesCount(), 2) );
|
||||
emit SGN_needUpdateGLA();
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
void HoleListModel::autoBridge(bool singleHole, double distCoeff)
|
||||
{
|
||||
holesManager.DiscardBridges();
|
||||
|
||||
int nb=0;
|
||||
if(singleHole)
|
||||
nb = holesManager.AutoSelfBridging(distCoeff, 0);
|
||||
else
|
||||
nb = holesManager.AutoMultiBridging(distCoeff, 0);
|
||||
|
||||
emit SGN_ExistBridge( nb>0 );
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
void HoleListModel::removeBridges()
|
||||
{
|
||||
holesManager.DiscardBridges();
|
||||
emit SGN_ExistBridge(false);
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
void HoleListModel::acceptBridges()
|
||||
{
|
||||
holesManager.ConfirmBridges();
|
||||
emit SGN_ExistBridge(false);
|
||||
}
|
||||
|
||||
void HoleListModel::closeNonManifolds()
|
||||
{
|
||||
if (holesManager.CloseNonManifoldHoles())
|
||||
emit SGN_ExistBridge(true);
|
||||
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
|
||||
/************* Implementazione QAbstractItemModel class *****************/
|
||||
|
||||
|
||||
QVariant HoleListModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if(!index.isValid() )
|
||||
return QVariant();
|
||||
|
||||
if(role == Qt::DisplayRole)
|
||||
{
|
||||
switch(index.column())
|
||||
{
|
||||
case 0:
|
||||
return holesManager.holes[index.row()].name;
|
||||
case 1:
|
||||
return holesManager.holes.at(index.row()).Size();
|
||||
case 2:
|
||||
return QString("%1").arg(holesManager.holes.at(index.row()).Perimeter(), 0, 'f', 5);
|
||||
}
|
||||
}
|
||||
else if (role == Qt::TextAlignmentRole)
|
||||
{
|
||||
if(index.column() == 0) return Qt::AlignLeft;
|
||||
if(index.column() == 1 ||
|
||||
index.column() == 2) return Qt::AlignRight;
|
||||
return Qt::AlignCenter;
|
||||
}
|
||||
else if (role == Qt::CheckStateRole)
|
||||
{
|
||||
bool checked;
|
||||
if(index.column() == 3)
|
||||
checked = holesManager.holes[index.row()].IsNonManifold();
|
||||
else if(index.column() == 4)
|
||||
checked = holesManager.holes[index.row()].IsSelected();
|
||||
else if(state == HoleListModel::Filled && holesManager.holes[index.row()].IsSelected())
|
||||
{
|
||||
if(index.column() == 5)
|
||||
checked = holesManager.holes[index.row()].IsCompenetrating();
|
||||
else if(index.column() == 6)
|
||||
checked = holesManager.holes[index.row()].IsAccepted();
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
else
|
||||
return QVariant();
|
||||
|
||||
if(checked)
|
||||
return Qt::Checked;
|
||||
else
|
||||
return Qt::Unchecked;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
||||
QVariant HoleListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
||||
{
|
||||
switch(section)
|
||||
{
|
||||
case 0:
|
||||
return tr("Hole");
|
||||
case 1:
|
||||
return tr("Edges");
|
||||
case 2:
|
||||
return tr("Perimeter");
|
||||
case 3:
|
||||
return tr("Non Manif.");
|
||||
case 4:
|
||||
if(state == HoleListModel::Filled)
|
||||
return tr("Fill");
|
||||
else
|
||||
return tr("Select");
|
||||
case 5:
|
||||
if(state == HoleListModel::Filled)
|
||||
return tr("Comp.");
|
||||
case 6:
|
||||
if(state == HoleListModel::Filled)
|
||||
return tr("Accept");
|
||||
}
|
||||
}
|
||||
else if (orientation == Qt::Horizontal && role == Qt::SizeHintRole)
|
||||
{
|
||||
switch(section)
|
||||
{
|
||||
case 0:
|
||||
return QSize(63, 20);
|
||||
case 1:
|
||||
return QSize(38, 20);
|
||||
case 2:
|
||||
return QSize(55, 20);
|
||||
case 3:
|
||||
return QSize(60, 20);
|
||||
case 4:
|
||||
if(state == HoleListModel::Filled)
|
||||
return QSize(20, 20);
|
||||
else
|
||||
return QSize(50, 20);
|
||||
case 5:
|
||||
return QSize(38, 20);
|
||||
case 6:
|
||||
return QSize(42, 20);
|
||||
}
|
||||
}
|
||||
else if (orientation == Qt::Horizontal && role == Qt::ToolTip && state==HoleListModel::Filled && section == 4)
|
||||
return tr("Compenetration");
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
||||
QModelIndex HoleListModel::index(int row, int column, const QModelIndex &/*parent*/) const
|
||||
{
|
||||
if(row >= (int)holesManager.holes.size())
|
||||
return QModelIndex();
|
||||
return createIndex(row,column, 0);
|
||||
}
|
||||
|
||||
|
||||
Qt::ItemFlags HoleListModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
Qt::ItemFlags ret = QAbstractItemModel::flags(index);
|
||||
|
||||
if (!index.isValid())
|
||||
return Qt::ItemIsEnabled;
|
||||
|
||||
if(index.column() == 0)
|
||||
ret = ret | Qt::ItemIsEditable;
|
||||
else if( (index.column() == 4 && state == HoleListModel::Selection) ||
|
||||
(index.column() == 6 && state == HoleListModel::Filled) )
|
||||
return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
|
||||
//return ret = ret | Qt::ItemIsUserCheckable ;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool HoleListModel::setData( const QModelIndex & index, const QVariant & value, int role )
|
||||
{
|
||||
if(!index.isValid())
|
||||
return false;
|
||||
|
||||
bool ret = false;
|
||||
if(role == Qt::EditRole && index.column() == 0)
|
||||
{
|
||||
QString newName = value.toString().trimmed();
|
||||
if(newName != "")
|
||||
{
|
||||
holesManager.holes[index.row()].name = newName;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
else if(role == Qt::CheckStateRole)
|
||||
{
|
||||
if(state == HoleListModel::Selection)
|
||||
{
|
||||
if(index.column() == 4 && state == HoleListModel::Selection)
|
||||
{
|
||||
holesManager.holes[index.row()].SetSelect( !holesManager.holes[index.row()].IsSelected() );
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
else if(index.column() == 6)
|
||||
{ // check accept
|
||||
holesManager.holes[index.row()].SetAccepted( !holesManager.holes[index.row()].IsAccepted() );
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
if(ret)
|
||||
{
|
||||
emit dataChanged(index, index);
|
||||
emit SGN_needUpdateGLA();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -26,14 +26,14 @@
|
||||
#include <QWidget>
|
||||
#include <QHeaderView>
|
||||
#include <QtGui>
|
||||
#include "meshlab/meshmodel.h"
|
||||
#include "fgtHole.h"
|
||||
#include "fgtBridge.h"
|
||||
#include <meshlab/meshmodel.h>
|
||||
#include <meshlab/interfaces.h>
|
||||
#include "vcg/simplex/face/pos.h"
|
||||
#include "vcg/complex/trimesh/base.h"
|
||||
#include "vcg/space/color4.h"
|
||||
#include "holeSetManager.h"
|
||||
|
||||
/* This class is the "model" of model-view architecture, so it implements methods to esposes data
|
||||
* informations about holes as QAbstractItemModel says.
|
||||
*/
|
||||
class HoleListModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -44,28 +44,21 @@ public:
|
||||
Selection, ManualBridging, Filled
|
||||
};
|
||||
|
||||
typedef vcg::tri::Hole<CMeshO> vcgHole;
|
||||
typedef vcg::tri::Hole<CMeshO>::Info HoleInfo;
|
||||
typedef FgtHole<CMeshO> HoleType;
|
||||
typedef std::vector< HoleType > HoleVector;
|
||||
typedef FgtBridge<CMeshO> BridgeType;
|
||||
typedef std::vector< BridgeType > BridgeVector;
|
||||
typedef vcg::face::Pos<CMeshO::FaceType> PosType;
|
||||
typedef std::vector< PosType > PosVector;
|
||||
typedef PosVector::iterator PosIterator;
|
||||
|
||||
typedef FgtHole<CMeshO> HoleType;
|
||||
typedef std::vector< HoleType > HoleVector;
|
||||
|
||||
HoleListModel(MeshModel *m, QObject *parent = 0);
|
||||
virtual ~HoleListModel() { clearModel(); };
|
||||
virtual ~HoleListModel() { holesManager.Clear(); };
|
||||
|
||||
inline int rowCount(const QModelIndex &parent = QModelIndex()) const { return holes.size(); };
|
||||
inline int columnCount(const QModelIndex &parent = QModelIndex()) const
|
||||
inline int rowCount(const QModelIndex &/*parent = QModelIndex()*/) const { return holesManager.HolesCount(); };
|
||||
inline int columnCount(const QModelIndex &/*parent = QModelIndex()*/) const
|
||||
{
|
||||
if(state == HoleListModel::Selection) return 5;
|
||||
else return 7;
|
||||
};
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
|
||||
QModelIndex parent(const QModelIndex &child) const { return QModelIndex(); };
|
||||
QModelIndex parent(const QModelIndex &/*child*/) const { return QModelIndex(); };
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
@ -74,15 +67,12 @@ public:
|
||||
bool setData( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole );
|
||||
|
||||
|
||||
inline int getUserBitHole() const { return userBitHole; };
|
||||
void clearModel();
|
||||
void updateModel();
|
||||
void drawHoles() const;
|
||||
void drawCompenetratingFaces() const;
|
||||
|
||||
inline void setState(HoleListModel::FillerState s) { state = s; emit layoutChanged(); };
|
||||
inline FillerState getState() const { return state; };
|
||||
void toggleSelectionHoleFromBorderFace(CFaceO *bface);
|
||||
void toggleSelectionHoleFromFace(CFaceO *bface);
|
||||
void toggleAcceptanceHole(CFaceO *bface);
|
||||
void fill(FgtHole<CMeshO>::FillerMode mode);
|
||||
void acceptFilling(bool accept=true);
|
||||
@ -91,31 +81,26 @@ public:
|
||||
void closeNonManifolds();
|
||||
inline MeshModel* getMesh() const { return mesh; };
|
||||
|
||||
void autoBridge(bool singleHole=false, double distCoeff=0, QLabel *infoLabel=0);
|
||||
void autoBridge(bool singleHole=false, double distCoeff=0);
|
||||
|
||||
inline bool PickedAbutment() const {return !pickedAbutment.IsNull(); };
|
||||
inline void setStartBridging()
|
||||
{
|
||||
assert(state != HoleListModel::Filled);
|
||||
state = HoleListModel::ManualBridging ;
|
||||
};
|
||||
inline void setEndBridging() { state = HoleListModel::Selection; pickedAbutment.f = 0; };
|
||||
inline void setEndBridging() { state = HoleListModel::Selection; pickedAbutment.SetNull(); };
|
||||
void addBridgeFace(CFaceO *pickedFace, int pickX, int pickY);
|
||||
|
||||
void countSelected();
|
||||
inline int SelectionCount() const { return nSelected; };
|
||||
inline int HolesCount() const { return holes.size(); };
|
||||
inline int AcceptedCount() const { return nAccepted; };
|
||||
inline bool PickedAbutment() const {return !pickedAbutment.IsNull(); };
|
||||
|
||||
private:
|
||||
MeshModel *mesh;
|
||||
FillerState state;
|
||||
int userBitHole;
|
||||
BridgeAbutment<CMeshO> pickedAbutment;
|
||||
int nSelected;
|
||||
int nAccepted;
|
||||
|
||||
|
||||
public:
|
||||
HoleVector holes;
|
||||
HoleSetManager<CMeshO> holesManager;
|
||||
|
||||
|
||||
Q_SIGNALS:
|
||||
void SGN_needUpdateGLA();
|
||||
|
||||
376
src/fgt/edit_hole/holeSetManager.h
Normal file
376
src/fgt/edit_hole/holeSetManager.h
Normal file
@ -0,0 +1,376 @@
|
||||
/****************************************************************************
|
||||
* VCGLib o o *
|
||||
* Visual and Computer Graphics Library o o *
|
||||
* _ O _ *
|
||||
* Copyright(C) 2004 \/)\/ *
|
||||
* 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 HOLESETMANAGER_H
|
||||
#define HOLESETMANAGER_H
|
||||
|
||||
#include "fgtHole.h"
|
||||
#include "fgtBridge.h"
|
||||
#include "vcg/complex/trimesh/hole.h"
|
||||
#include <vcg/container/simple_temporary_data.h>
|
||||
|
||||
|
||||
/* HoleSetManager class rappresent an entity which manages the holes founded
|
||||
* into the same MESH.
|
||||
* It allows to invoke some functionality for each (may be the selected ones)
|
||||
* holes as filling and bridging.
|
||||
* His presence is necessary because it connect holes to a mesh with the
|
||||
* additional data used by holes to mark its "characteristic" faces.
|
||||
*/
|
||||
template <class MESH>
|
||||
class HoleSetManager
|
||||
{
|
||||
enum FaceAttribute
|
||||
{
|
||||
NONE = 0x0000,
|
||||
BORDER = 0x0001,
|
||||
PATCH = 0x0002,
|
||||
COMPENET = 0x0004,
|
||||
BRIDGE = 0x0020
|
||||
};
|
||||
|
||||
typedef FgtHole< MESH > HoleType;
|
||||
typedef typename std::vector< HoleType > HoleVector;
|
||||
typedef typename HoleVector::iterator HoleIterator;
|
||||
typedef typename MESH::FaceType FaceType;
|
||||
typedef typename MESH::FacePointer FacePointer;
|
||||
typedef typename MESH::FaceContainer FaceContainer;
|
||||
typedef typename vcg::tri::Hole<MESH> vcgHole;
|
||||
typedef typename vcgHole::Info HoleInfo;
|
||||
typedef typename vcg::face::Pos<FaceType> PosType;
|
||||
typedef typename vcg::SimpleTempData<FaceContainer, int> SimpleData;
|
||||
typedef typename HoleType::FillerMode FillerMode;
|
||||
|
||||
public:
|
||||
|
||||
HoleSetManager()
|
||||
{
|
||||
autoBridgeCB = 0;
|
||||
};
|
||||
|
||||
void Init(MESH* m)
|
||||
{
|
||||
nAccepted = nSelected = 0;
|
||||
mesh = m;
|
||||
faceAttr = new SimpleData (m->face, NONE);
|
||||
getMeshHoles();
|
||||
};
|
||||
|
||||
void Clear()
|
||||
{
|
||||
typename HoleVector::iterator it;
|
||||
for(it=holes.begin(); it!=holes.end(); it++)
|
||||
it->ResetFlag();
|
||||
|
||||
holes.clear();
|
||||
};
|
||||
|
||||
/***************** PerFace additional data ********************/
|
||||
|
||||
inline bool IsHoleBorderFace(FacePointer f) const { return ( (*faceAttr)[f] & BORDER) != 0; };
|
||||
inline bool IsPatchFace(FacePointer f) const { return ((*faceAttr)[f] & PATCH) != 0; };
|
||||
inline bool IsCompFace(FacePointer f) const { return ((*faceAttr)[f] & COMPENET) != 0; };
|
||||
inline bool IsBridgeFace(FacePointer f) const { return ((*faceAttr)[f] & BRIDGE) != 0; };
|
||||
|
||||
inline void SetHoleBorderAttr(FacePointer f) { (*faceAttr)[f] |= BORDER; };
|
||||
inline void ClearHoleBorderAttr(FacePointer f) { (*faceAttr)[f] &= (~BORDER); };
|
||||
|
||||
inline void SetPatchAttr(FacePointer f) { (*faceAttr)[f] |= PATCH; };
|
||||
inline void ClearPatchAttr(FacePointer f) { (*faceAttr)[f] &= (~PATCH); }
|
||||
|
||||
inline void SetCompAttr(FacePointer f) { (*faceAttr)[f] |= COMPENET; };
|
||||
inline void ClearCompAttr(FacePointer f) { (*faceAttr)[f] &= (~COMPENET); };
|
||||
|
||||
inline void SetBridgeAttr(FacePointer f){ (*faceAttr)[f] |= BRIDGE; };
|
||||
inline void ClearBridgeAttr(FacePointer f){ (*faceAttr)[f] &= (~BRIDGE); };
|
||||
|
||||
/**************** Statistical info *****************/
|
||||
inline int SelectionCount() const { return nSelected; };
|
||||
inline int HolesCount() const { return holes.size(); };
|
||||
inline int AcceptedCount() const { return nAccepted; };
|
||||
|
||||
/**************** Holes editing ********************/
|
||||
|
||||
bool Fill(FillerMode mode)
|
||||
{
|
||||
if(nSelected==0)
|
||||
return false;
|
||||
|
||||
std::vector<FacePointer *> local_facePointer;
|
||||
HoleType::AddFaceReference(holes, local_facePointer);
|
||||
|
||||
HoleIterator it = holes.begin();
|
||||
for( ; it != holes.end(); it++ )
|
||||
if( it->IsSelected() )
|
||||
it->Fill(mode, *mesh, local_facePointer);
|
||||
|
||||
nAccepted=nSelected;
|
||||
return true;
|
||||
};
|
||||
|
||||
void ConfirmFilling(bool accept)
|
||||
{
|
||||
nSelected=0;
|
||||
HoleIterator it = holes.begin();
|
||||
while( it != holes.end() )
|
||||
{
|
||||
if( it->IsFilled() )
|
||||
{
|
||||
if( (it->IsSelected() && !it->IsAccepted()) || !accept)
|
||||
{
|
||||
if( it->IsFilled() )
|
||||
{
|
||||
it->RestoreHole();
|
||||
nSelected++;
|
||||
}
|
||||
}
|
||||
else if( it->IsSelected() && it->IsAccepted() )
|
||||
{
|
||||
it->ResetFlag();
|
||||
it = holes.erase(it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
countSelected();
|
||||
};
|
||||
|
||||
inline void ConfirmBridges()
|
||||
{
|
||||
FgtBridge<CMeshO>::AcceptBridges(this);
|
||||
};
|
||||
|
||||
inline void DiscardBridges()
|
||||
{
|
||||
FgtBridge<CMeshO>::RemoveBridges(this);
|
||||
countSelected();
|
||||
};
|
||||
|
||||
bool CloseNonManifoldHoles()
|
||||
{
|
||||
return (FgtBridge<CMeshO>::CloseNonManifoldVertex(this) >0);
|
||||
};
|
||||
|
||||
int AutoSelfBridging(double distCoeff, std::vector<FacePointer*> *facesRef)
|
||||
{
|
||||
int n = FgtBridge<CMeshO>::AutoSelfBridging(this, distCoeff, facesRef);
|
||||
countSelected();
|
||||
return n;
|
||||
};
|
||||
|
||||
int AutoMultiBridging(double distCoeff, std::vector<FacePointer*> *facesRef)
|
||||
{
|
||||
int n = FgtBridge<CMeshO>::AutoMultiBridging(this, distCoeff, facesRef);
|
||||
countSelected();
|
||||
return n;
|
||||
};
|
||||
|
||||
/** Return index of hole adjacent to picked face into holes vector.
|
||||
* Also return the iterator on correct position in holes list.
|
||||
*/
|
||||
int FindHoleFromFace(FacePointer bFace, HoleIterator &it)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
// it know if bFace is adjacent to patchFace
|
||||
FacePointer patchF = 0;
|
||||
if(IsPatchFace(bFace))
|
||||
patchF = bFace;
|
||||
else
|
||||
{
|
||||
for( int i=0; i<3; i++)
|
||||
if(IsPatchFace(bFace->FFp(i)) && !IsBridgeFace(bFace->FFp(i)))
|
||||
patchF = bFace->FFp(i);
|
||||
}
|
||||
|
||||
HoleIterator hit = holes.begin();
|
||||
if(patchF == 0)
|
||||
{
|
||||
if(IsHoleBorderFace(bFace))
|
||||
{
|
||||
// border face belong to an hole not filled... it can walk on border
|
||||
for( ; hit != holes.end(); ++hit)
|
||||
{
|
||||
if(!hit->IsFilled())
|
||||
if(hit->HaveBorderFace(bFace))
|
||||
{
|
||||
it = hit;
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// bFace belong filled hole, adjF is its patch
|
||||
assert(IsPatchFace(patchF));
|
||||
HoleIterator hit = holes.begin();
|
||||
for( ; hit != holes.end(); ++hit)
|
||||
{
|
||||
// for each hole check if face is its border face
|
||||
if(hit->IsFilled())
|
||||
if(hit->HavePatchFace(patchF))
|
||||
{
|
||||
it = hit;
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
it = holes.end(); // invalid iterator
|
||||
return -1;
|
||||
};
|
||||
|
||||
/** Return boolean indicatind if the picking have select a border face which can be used
|
||||
* as abutment for a bridge. If true it also return BridgeAbutment allowing to know
|
||||
* border edge and its relative hole.
|
||||
*/
|
||||
bool FindBridgeAbutmentFromPick( FacePointer bFace, int pickedX, int pickedY,
|
||||
BridgeAbutment<MESH> &pickedResult)
|
||||
{
|
||||
if( vcg::face::BorderCount(*bFace) == 0 )
|
||||
return false;
|
||||
|
||||
HoleIterator hit;
|
||||
if( FindHoleFromFace(bFace, hit) < 0 )
|
||||
{
|
||||
pickedResult.SetNull();
|
||||
return false;
|
||||
}
|
||||
|
||||
pickedResult.h = &*hit;
|
||||
pickedResult.f = bFace;
|
||||
if( vcg::face::BorderCount(*bFace) == 1 )
|
||||
{
|
||||
// it choose the only one border edge
|
||||
for(int i=0; i<3; i++)
|
||||
if(vcg::face::IsBorder<FaceType>(*bFace, i))
|
||||
pickedResult.z = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
// looking for the closest border edge to pick point
|
||||
PosType retPos = getClosestPos(bFace, pickedX, pickedY);
|
||||
pickedResult.f = retPos.f;
|
||||
pickedResult.z = retPos.z;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/* Inspect a mesh to find its holes. */
|
||||
void getMeshHoles()
|
||||
{
|
||||
holes.clear();
|
||||
std::vector<HoleInfo> vhi;
|
||||
|
||||
//prendo la lista di info(sugli hole) tutte le facce anche le non selezionate
|
||||
FaceType::NewBitFlag();
|
||||
int borderFlag = vcgHole::GetInfo(*mesh, false, vhi);
|
||||
|
||||
HoleType::ResetHoleId();
|
||||
typename std::vector<HoleInfo>::iterator itH = vhi.begin();
|
||||
for( ; itH != vhi.end(); itH++)
|
||||
{
|
||||
holes.push_back(HoleType(*itH, QString("Hole_%1").arg(HoleType::GetHoleId(),3,10,QChar('0')), this));
|
||||
|
||||
// reset flag and setting of
|
||||
PosType curPos = itH->p;
|
||||
do{
|
||||
curPos.f->ClearUserBit(borderFlag);
|
||||
SetHoleBorderAttr(curPos.f);
|
||||
curPos.NextB();
|
||||
assert(curPos.IsBorder());
|
||||
}while( curPos != itH->p );
|
||||
}
|
||||
|
||||
FaceType::DeleteBitFlag(borderFlag);
|
||||
};
|
||||
|
||||
/* Return border half-edge of the same face which is nearest to point(x, y) of glArea.
|
||||
*/
|
||||
static PosType getClosestPos(FaceType* face, int x, int y)
|
||||
{
|
||||
double mvmatrix[16], projmatrix[16];
|
||||
GLint viewport[4];
|
||||
double rx, ry, rz;
|
||||
vcg::Point2d vertex[3];
|
||||
vcg::Point2d point((double)x, (double)y);
|
||||
|
||||
glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
|
||||
glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
|
||||
for(int i=0; i<3; i++)
|
||||
{
|
||||
gluProject(face->V(i)->P()[0],face->V(i)->P()[1],face->V(i)->P()[2],mvmatrix,projmatrix,viewport,&rx,&ry,&rz);
|
||||
vertex[i] = vcg::Point2d(rx,ry);
|
||||
}
|
||||
|
||||
double dist = DBL_MAX;
|
||||
int nearest = 0;
|
||||
for(int i=0; i<3; i++)
|
||||
{
|
||||
if(!vcg::face::IsBorder<FaceType>(*face, i))
|
||||
continue;
|
||||
|
||||
vcg::Line2d el(vertex[i], vertex[(i+1)%3]-vertex[i]);
|
||||
double tmp = vcg::Distance(el, point);
|
||||
|
||||
if(dist > tmp)
|
||||
{
|
||||
dist = tmp;
|
||||
nearest = i;
|
||||
}
|
||||
}
|
||||
return PosType(face, nearest, face->V(nearest) );
|
||||
};
|
||||
|
||||
|
||||
void HoleSetManager::countSelected()
|
||||
{
|
||||
nSelected = 0;
|
||||
HoleIterator hit = holes.begin();
|
||||
for( ; hit!=holes.end(); hit++)
|
||||
if(hit->IsSelected())
|
||||
nSelected++;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
int nSelected;
|
||||
int nAccepted;
|
||||
MESH* mesh;
|
||||
HoleVector holes;
|
||||
SimpleData* faceAttr;
|
||||
|
||||
AutoBridgingCallback* autoBridgeCB;
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
x
Reference in New Issue
Block a user