From e254f8613ccc8b86d27a7bd1fb85ac820bbd709d Mon Sep 17 00:00:00 2001 From: Paolo Cignoni cignoni Date: Thu, 23 Oct 2008 15:29:13 +0000 Subject: [PATCH] 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 --- src/fgt/edit_hole/edit_hole.cpp | 29 +- src/fgt/edit_hole/edit_hole.h | 29 +- src/fgt/edit_hole/edit_hole.pro | 1 + src/fgt/edit_hole/fgtBridge.h | 407 ++++++------- src/fgt/edit_hole/fgtHole.h | 307 ++++------ src/fgt/edit_hole/fillerDialog.cpp | 2 +- src/fgt/edit_hole/holeListModel.cpp | 914 +++++++++++++--------------- src/fgt/edit_hole/holeListModel.h | 55 +- src/fgt/edit_hole/holeSetManager.h | 376 ++++++++++++ 9 files changed, 1139 insertions(+), 981 deletions(-) create mode 100644 src/fgt/edit_hole/holeSetManager.h diff --git a/src/fgt/edit_hole/edit_hole.cpp b/src/fgt/edit_hole/edit_hole.cpp index 393453176..5727f6ff5 100644 --- a/src/fgt/edit_hole/edit_hole.cpp +++ b/src/fgt/edit_hole/edit_hole.cpp @@ -44,7 +44,6 @@ EditHolePlugin::EditHolePlugin() { EditHolePlugin::~EditHolePlugin() { - FgtHole::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::PickNearestFace(cur.x(), inverseY, m.cm, pickedFace, 4, 4); - // guardo se nella faccia pi� vicina uno dei vertici � di bordo + if( pickedFace != 0 ) { bool oldAbutmentPresence; switch(holesModel->getState()) { case HoleListModel::Selection: - if(FgtHole::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::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(); diff --git a/src/fgt/edit_hole/edit_hole.h b/src/fgt/edit_hole/edit_hole.h index 2ac08931b..40c77c73a 100644 --- a/src/fgt/edit_hole/edit_hole.h +++ b/src/fgt/edit_hole/edit_hole.h @@ -25,24 +25,37 @@ #define EDITHOLEPLUGIN_H #include -#include #include "fillerDialog.h" #include "holeListModel.h" -#include #include +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 vcgHole; - typedef vcgHole::Info HoleInfo; - typedef std::vector< FgtHole > HoleVector; - typedef vcg::face::Pos PosType; - typedef CMeshO::FaceIterator FaceIterator; - + EditHolePlugin(); virtual ~EditHolePlugin(); static const QString Info(); diff --git a/src/fgt/edit_hole/edit_hole.pro b/src/fgt/edit_hole/edit_hole.pro index 27cfa94ba..d4f249b5b 100644 --- a/src/fgt/edit_hole/edit_hole.pro +++ b/src/fgt/edit_hole/edit_hole.pro @@ -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 \ diff --git a/src/fgt/edit_hole/fgtBridge.h b/src/fgt/edit_hole/fgtBridge.h index bdc5ca1b9..4d9dde09c 100644 --- a/src/fgt/edit_hole/fgtBridge.h +++ b/src/fgt/edit_hole/fgtBridge.h @@ -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 +template class FgtHole; +template class HoleSetManager; /* Struct rappresenting the mesh edge where the bridge starts/ends. */ template @@ -54,7 +57,24 @@ public: FgtHole* 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 PosVector; typedef FgtHole HoleType; typedef typename std::vector HoleVector; - typedef typename MESH::VertexType VertexType; - typedef typename MESH::CoordType CoordType; typedef typename MESH::ScalarType ScalarType; - typedef typename vcg::Triangle3 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 &sideA, BridgeAbutment &sideB, MESH &mesh, - HoleVector &holes, std::vector *app=0) + static bool CreateBridge(BridgeAbutment &sideA, BridgeAbutment &sideB, + HoleSetManager* holesManager, std::vector *app=0) { - assert(FgtHole::IsHoleBorderFace(*sideA.f) && FgtHole::IsHoleBorderFace(*sideB.f)); + assert( holesManager->IsHoleBorderFace(sideA.f) && + holesManager->IsHoleBorderFace(sideB.f)); assert(!sideA.h->IsFilled() && !sideB.h->IsFilled()); std::vector tmpFaceRef; if(app!=0) tmpFaceRef.insert(tmpFaceRef.end(), app->begin(), app->end()); - FgtHole::AddFaceReference(holes, tmpFaceRef); + FgtHole::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* holeManager) { // contains bridge faces reached navigating the holes. std::vector bridgeFaces; @@ -146,22 +165,22 @@ public: PosVector adjBorderPos; PosType curPos; - getBridgeInfo(holes, bridgeFaces, adjBorderPos); + getBridgeInfo(holeManager, bridgeFaces, adjBorderPos); typename std::vector::iterator fit; for(fit=bridgeFaces.begin(); fit!=bridgeFaces.end(); fit++ ) { - (*fit)->ClearUserBit( FgtHole::HolePatchFlag() ); - (*fit)->ClearUserBit( FgtHole::PatchCompFlag() ); - (*fit)->ClearUserBit( FgtHole::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 *holeManager) { // contains bridge faces reached navigating the holes. std::vector 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::iterator fit; for(fit=bridgeFaces.begin(); fit!=bridgeFaces.end(); fit++ ) { - if(FgtHole::IsHoleBorderFace(**fit)) + if(holeManager->IsHoleBorderFace(*fit)) { typename HoleVector::iterator hit; - if(FgtHole::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::DeleteFace(mesh, **fit); + vcg::tri::Allocator::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::iterator pit; for(pit=adjBorderPos.begin(); pit!=adjBorderPos.end(); pit++) - if(FgtHole::IsHoleBorderFace(*pit->f)) + if(holeManager->IsHoleBorderFace(pit->f)) { typename HoleVector::iterator hit; - if(FgtHole::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::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 newHole(initPos, QString("Hole_%1").arg(HoleType::GetHoleId(),3,10,QChar('0')) ); + FgtHole 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 &pickedResult) - { - if( vcg::face::BorderCount(*bFace) == 0 ) - return false; - - typename HoleVector::iterator hit; - if( FgtHole::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(*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 *app=0) + static int AutoSelfBridging(HoleSetManager* holesManager, double dist_coeff=0.0, std::vector *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 gM; - gM.Set(mesh.face.begin(),mesh.face.end()); + gM.Set(holesManager->mesh->face.begin(),holesManager->mesh->face.end()); std::vector tmpFaceRef; HoleType* oldRef = 0; BridgeAbutment sideA, sideB; - int nh = holes.size(); + int nh = holesManager->holes.size(); for(int h=0; hholes.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; iFFp(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(*sideA.f, sideA.z)); assert(vcg::face::IsBorder(*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::AddFaceReference(holes, tmpFaceRef); + FgtHole::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 *app=0 ) + static int AutoMultiBridging(HoleSetManager* holesManager, double dist_coeff=0.0, std::vector *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(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::AddFaceReference(holes, tmpFaceRef); + FgtHole::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 *app=0) + static int CloseNonManifoldVertex(HoleSetManager* holesManager, std::vector *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 tmpFaceRef; - if(app!=0) - tmpFaceRef.insert(tmpFaceRef.end(), app->begin(), app->end()); - FgtHole::AddFaceReference(holes, tmpFaceRef); - HoleType* oldRef = &*holes.begin(); + HoleType* oldRef = 0; for(int i=0; iholes.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::AddFaceReference(holesManager->holes, tmpFaceRef); + oldRef = &*holesManager->holes.begin(); + } + tmpFaceRef.push_back(&p0.f); tmpFaceRef.push_back(&curPos.f); - FaceIterator fit = vcg::tri::Allocator::AddFaces(mesh, 1, tmpFaceRef); + FaceIterator fit = vcg::tri::Allocator::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::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::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::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 &sideA, BridgeAbutment &sideB, MESH &mesh, - HoleVector &holes, std::vector &app) + static bool subdivideHoleWithBridge(BridgeAbutment &sideA, BridgeAbutment &sideB, + HoleSetManager* holesManager, std::vector &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::BridgeFlag()); - newP1.f->SetUserBit(FgtHole::BridgeFlag()); + holesManager->SetBridgeAttr(newP0.f); + holesManager->SetBridgeAttr(newP1.f); sideA.h->SetStartPos(newP0); sideA.h->SetBridged(true); - FgtHole newHole(newP1, QString("Hole_%1").arg(HoleType::GetHoleId(),3,10,QChar('0')) ); + FgtHole 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 &sideA, BridgeAbutment &sideB, MESH &mesh, - HoleVector &holes, std::vector &app) + static bool unifyHolesWithBridge(BridgeAbutment &sideA, BridgeAbutment &sideB, + HoleSetManager* holesManager, std::vector &app) { - assert(FgtHole::IsHoleBorderFace(*sideA.f)); - assert(FgtHole::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::BridgeFlag()); - newP1.f->SetUserBit(FgtHole::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 &sideA, BridgeAbutment &sideB, + static bool build(HoleSetManager *holesManager, BridgeAbutment &sideA, BridgeAbutment &sideB, PosType &pos0, PosType &pos1, std::vector &app) { vcg::GridStaticPtr 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::AddFaces(mesh, 2, app); + FaceIterator fit = vcg::tri::Allocator::AddFaces(*holesManager->mesh, 2, app); + holesManager->faceAttr->UpdateSize(); FacePointer f0 = &*fit; FacePointer f1 = &*(fit+1); - sideA.f->ClearUserBit(FgtHole::HoleFlag()); - sideB.f->ClearUserBit(FgtHole::HoleFlag()); - f0->SetUserBit(FgtHole::HoleFlag()); - f1->SetUserBit(FgtHole::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(*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 &bridgeFaces, std::vector &adjBridgePos) + static void getBridgeInfo(HoleSetManager *holesManager, std::vector &bridgeFaces, std::vector &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::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::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::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::IsBridgeFace(*adjF) && !adjF->IsV()) + if(!holesManager->IsBridgeFace(adjF) && !adjF->IsV()) { adjF->SetV(); if(vf->IsS()) adjF->SetS(); diff --git a/src/fgt/edit_hole/fgtHole.h b/src/fgt/edit_hole/fgtHole.h index b91db3539..98c76103f 100644 --- a/src/fgt/edit_hole/fgtHole.h +++ b/src/fgt/edit_hole/fgtHole.h @@ -35,14 +35,18 @@ #include #include #include "vcg/space/color4.h" +#include "holeSetManager.h" -/** An hole type, extends vcg::tri::Hole::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::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 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 PosType; - typedef typename std::vector PosVector; - typedef typename PosVector::iterator PosIterator; typedef typename vcg::tri::Hole vcgHole; typedef typename vcgHole::Info HoleInfo; typedef typename std::vector< FgtHole > HoleVector; @@ -97,18 +97,20 @@ public: typedef typename vcg::tri::MinimumWeightEar MinimumWeightEar; typedef typename vcg::tri::SelfIntersectionEar SelfIntersectionEar; - FgtHole(HoleInfo &hi, QString holeName) : + FgtHole(HoleInfo &hi, QString holeName, HoleSetManager *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 *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 patch; - //getPatchFaces(patch); typename std::vector::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 bridgesFaces; if( IsFilled() ) { - //std::vector patch; - //getPatchFaces(patch); typename std::vector::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 patches; - //getPatchFaces(patches); _state &= (~FILLED); typename std::vector::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::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::DeleteFace(mesh, **it); + vcg::tri::Allocator::DeleteFace(*parentManager->mesh, **it); } patches.clear(); }; void Fill(FillerMode mode, MESH &mesh, std::vector &local_facePointer) { + int patchBit = FaceType::NewBitFlag(); switch(mode) { case FgtHole::Trivial: - vcgHole::template FillHoleEar< vcg::tri::TrivialEar >(mesh, *this, HolePatchFlag(), local_facePointer); + vcgHole::template FillHoleEar< TrivialEar >(mesh, *this, patchBit, local_facePointer); break; case FgtHole::MinimumWeight: - vcgHole::template FillHoleEar< vcg::tri::MinimumWeightEar >(mesh, *this, HolePatchFlag(), local_facePointer); + vcgHole::template FillHoleEar< MinimumWeightEar >(mesh, *this, patchBit, local_facePointer); break; case FgtHole::SelfIntersection: std::vector 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::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 gM; - gM.Set(mesh.face.begin(),mesh.face.end()); + gM.Set(parentManager->mesh->face.begin(),parentManager->mesh->face.end()); std::vector 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 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::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 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::iterator itH = vhi.begin(); - int i=0; - for( ; itH != vhi.end(); itH++) - { - ret.push_back(FgtHole(*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 &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* parentManager; + private: - static int &HoleId(){static int _holeId=0; return _holeId;}; int _state; ScalarType perimeter; diff --git a/src/fgt/edit_hole/fillerDialog.cpp b/src/fgt/edit_hole/fillerDialog.cpp index 5e34999a5..32e6eb181 100644 --- a/src/fgt/edit_hole/fillerDialog.cpp +++ b/src/fgt/edit_hole/fillerDialog.cpp @@ -38,7 +38,7 @@ FillerDialog::FillerDialog(QWidget *parent) FillerDialog::~FillerDialog() {} -void FillerDialog::closeEvent ( QCloseEvent * event ) +void FillerDialog::closeEvent ( QCloseEvent * /*event*/ ) { emit SGN_Closing(); } diff --git a/src/fgt/edit_hole/holeListModel.cpp b/src/fgt/edit_hole/holeListModel.cpp index d76f6d0f8..f5818f693 100644 --- a/src/fgt/edit_hole/holeListModel.cpp +++ b/src/fgt/edit_hole/holeListModel.cpp @@ -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::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::IsHoleBorderFace(*bface)); - HoleVector::iterator h; - int ind = FgtHole::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::IsHoleBorderFace(*bface)) - ind = FgtHole::FindHoleFromBorderFace(bface, holes, h); - else if(FgtHole::IsPatchFace(*bface) && !FgtHole::IsBridgeFace(*bface)) - ind = FgtHole::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 picked; - if (!FgtBridge::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 local_facePointer; - local_facePointer.push_back(&pickedAbutment.f); - local_facePointer.push_back(&picked.f); - - if(FgtBridge::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::FillerMode mode) -{ - std::vector local_facePointer; - - HoleVector::iterator it; - FgtHole::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::RemoveBridges(mesh->cm, holes); - - int nb=0; - if(singleHole) - nb = FgtBridge::AutoSelfBridging(mesh->cm, holes, infoLabel, distCoeff, 0); - else - nb = FgtBridge::AutoMultiBridging(mesh->cm, holes, infoLabel, distCoeff, 0); - - countSelected(); - - emit SGN_ExistBridge( nb>0 ); - emit layoutChanged(); -} - -void HoleListModel::removeBridges() -{ - FgtBridge::RemoveBridges(mesh->cm, holes); - countSelected(); - - emit SGN_ExistBridge(false); - emit layoutChanged(); -} - -void HoleListModel::acceptBridges() -{ - FgtBridge::AcceptBridges(holes); - state = HoleListModel::Selection; - emit SGN_ExistBridge(false); -} - -void HoleListModel::closeNonManifolds() -{ - if (FgtBridge::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 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 local_facePointer; + local_facePointer.push_back(&pickedAbutment.f); + local_facePointer.push_back(&picked.f); + + if(FgtBridge::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::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; +} diff --git a/src/fgt/edit_hole/holeListModel.h b/src/fgt/edit_hole/holeListModel.h index bc25d84ef..85ea638f4 100644 --- a/src/fgt/edit_hole/holeListModel.h +++ b/src/fgt/edit_hole/holeListModel.h @@ -26,14 +26,14 @@ #include #include #include +#include "meshlab/meshmodel.h" #include "fgtHole.h" #include "fgtBridge.h" -#include -#include -#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 vcgHole; - typedef vcg::tri::Hole::Info HoleInfo; - typedef FgtHole HoleType; - typedef std::vector< HoleType > HoleVector; - typedef FgtBridge BridgeType; - typedef std::vector< BridgeType > BridgeVector; - typedef vcg::face::Pos PosType; - typedef std::vector< PosType > PosVector; - typedef PosVector::iterator PosIterator; - + typedef FgtHole 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::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 pickedAbutment; - int nSelected; - int nAccepted; - + public: - HoleVector holes; + HoleSetManager holesManager; + Q_SIGNALS: void SGN_needUpdateGLA(); diff --git a/src/fgt/edit_hole/holeSetManager.h b/src/fgt/edit_hole/holeSetManager.h new file mode 100644 index 000000000..e5a06ba45 --- /dev/null +++ b/src/fgt/edit_hole/holeSetManager.h @@ -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 + + +/* 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 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 vcgHole; + typedef typename vcgHole::Info HoleInfo; + typedef typename vcg::face::Pos PosType; + typedef typename vcg::SimpleTempData 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 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::AcceptBridges(this); + }; + + inline void DiscardBridges() + { + FgtBridge::RemoveBridges(this); + countSelected(); + }; + + bool CloseNonManifoldHoles() + { + return (FgtBridge::CloseNonManifoldVertex(this) >0); + }; + + int AutoSelfBridging(double distCoeff, std::vector *facesRef) + { + int n = FgtBridge::AutoSelfBridging(this, distCoeff, facesRef); + countSelected(); + return n; + }; + + int AutoMultiBridging(double distCoeff, std::vector *facesRef) + { + int n = FgtBridge::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 &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(*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 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::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(*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