From dca713b24030bb1dd10adeb45096712a74997fa2 Mon Sep 17 00:00:00 2001 From: DarioS Date: Sun, 7 Nov 2021 12:57:53 +0100 Subject: [PATCH] EgtGeomKernel 2.3k3 : - modifiche per GeneralizedCut di SurfTriMesh - Cut e GeneralizedCut di SurfTriMesh spostati in nuovo sorgente SurfTriMeshCuts.cpp. --- EgtGeomKernel.rc | Bin 11718 -> 11718 bytes EgtGeomKernel.vcxproj | 1 + EgtGeomKernel.vcxproj.filters | 3 + SurfTriMesh.cpp | 332 +------------- SurfTriMesh.h | 6 +- SurfTriMeshBooleans.cpp | 648 +-------------------------- SurfTriMeshCuts.cpp | 807 ++++++++++++++++++++++++++++++++++ SurfTriMeshUtilities.cpp | 189 ++++++++ 8 files changed, 1019 insertions(+), 967 deletions(-) create mode 100644 SurfTriMeshCuts.cpp diff --git a/EgtGeomKernel.rc b/EgtGeomKernel.rc index 6bf733630139e260b21053d930f431b55660da2b..d4263c2bf225d6e443480702617b546152279055 100644 GIT binary patch delta 110 zcmX>WeJpyzA2vqg&G-4vGfl1&(wY2&Q;pGhvZAo=W*@FeEMOU1X1ItO delta 110 zcmX>WeJpyzA2vp#&G-4vGfl1&(wY2&Q;pGRvZAo=W*@FeEMOU1X1ItO + diff --git a/EgtGeomKernel.vcxproj.filters b/EgtGeomKernel.vcxproj.filters index a8f9fbe..951db0c 100644 --- a/EgtGeomKernel.vcxproj.filters +++ b/EgtGeomKernel.vcxproj.filters @@ -456,6 +456,9 @@ File di origine\Geo + + File di origine\Geo + diff --git a/SurfTriMesh.cpp b/SurfTriMesh.cpp index 2d32cfc..0970cbd 100644 --- a/SurfTriMesh.cpp +++ b/SurfTriMesh.cpp @@ -18,10 +18,10 @@ #include "GeoObjFactory.h" #include "NgeWriter.h" #include "NgeReader.h" +#include "SurfFlatRegion.h" #include "DistPointLine.h" #include "Triangulate.h" #include "GeoConst.h" -#include "SurfFlatRegion.h" #include "/EgtDev/Include/EGkIntersLinePlane.h" #include "/EgtDev/Include/EGkPointGrid3d.h" #include "/EgtDev/Include/EGkPolyLine.h" @@ -3399,336 +3399,6 @@ SurfTriMesh::Invert( void) return true ; } -//---------------------------------------------------------------------------- -bool -SurfTriMesh::CutByTriangles( const Plane3d& plPlane, bool bSaveOnEq, bool& bModif) -{ - // la superficie deve essere validata - if ( m_nStatus != OK) - return false ; - - // classifico i vertici rispetto al piano - double dTol = max( min( 0.5 * m_dLinTol, 10 * EPS_SMALL), EPS_SMALL) ; - for ( int i = 0 ; i < GetVertexSize() ; ++ i) { - double dDist = DistPointPlane( m_vVert[i].ptP, plPlane) ; - if ( abs( dDist) < dTol) { - m_vVert[i].nTemp = 0 ; - if ( abs( dDist) > EPS_SMALL) - m_vVert[i].ptP -= plPlane.GetVersN() * dDist ; - } - else if ( dDist > 0) - m_vVert[i].nTemp = +1 ; - else - m_vVert[i].nTemp = -1 ; - } - - // aggiorno timestamp dei triangoli - for ( auto& Tria : m_vTria) - Tria.nTemp = m_nTimeStamp ; - ++ m_nTimeStamp ; - - // sistemo i triangoli (eventualmente li elimino) - for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { - // salto i triangoli cancellati e quelli aggiunti - if ( m_vTria[i].nIdVert[0] == SVT_DEL || m_vTria[i].nTemp == m_nTimeStamp) - continue ; - // se flag abilita e giace sul piano ed equiverso, lo salvo - if ( bSaveOnEq && - m_vVert[ m_vTria[i].nIdVert[0]].nTemp == 0 && - m_vVert[ m_vTria[i].nIdVert[1]].nTemp == 0 && - m_vVert[ m_vTria[i].nIdVert[2]].nTemp == 0 && - AreSameVectorApprox( m_vTria[i].vtN, plPlane.GetVersN())) - ; - // se giace sul piano o dalla parte esterna del piano, lo cancello - else if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp != -1 && - m_vVert[ m_vTria[i].nIdVert[1]].nTemp != -1 && - m_vVert[ m_vTria[i].nIdVert[2]].nTemp != -1) { - bModif = true ; - RemoveTriangle( i) ; - } - // se giace dalla parte interna con al massimo un lato sul piano, lo conservo - else if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp != 1 && - m_vVert[ m_vTria[i].nIdVert[1]].nTemp != 1 && - m_vVert[ m_vTria[i].nIdVert[2]].nTemp != 1) - ; - // altrimenti attraversa il piano, devo modificarlo - else { - bModif = true ; - int nDisc = m_vVert[ m_vTria[i].nIdVert[0]].nTemp + - m_vVert[ m_vTria[i].nIdVert[1]].nTemp + - m_vVert[ m_vTria[i].nIdVert[2]].nTemp ; - // se ha un vertice all'interno e uno sul piano -> 1 nuovo vertice e 1 nuovo triangolo - if ( nDisc == 0) { - // classifico i vertici - int nVertOn, nVertIn, nVertOut ; - if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp == 0) { - nVertOn = 0 ; - nVertIn = 1 ; - nVertOut = 2 ; - if ( m_vVert[ m_vTria[i].nIdVert[1]].nTemp > 0) - swap( nVertIn, nVertOut) ; - } - else if ( m_vVert[ m_vTria[i].nIdVert[1]].nTemp == 0) { - nVertOn = 1 ; - nVertIn = 2 ; - nVertOut = 0 ; - if ( m_vVert[ m_vTria[i].nIdVert[2]].nTemp > 0) - swap( nVertIn, nVertOut) ; - } - else { - nVertOn = 2 ; - nVertIn = 0 ; - nVertOut = 1 ; - if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp > 0) - swap( nVertIn, nVertOut) ; - } - // calcolo il punto di intersezione del lato che attraversa il piano - double dDistIn = - DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertIn]].ptP, plPlane) ; - double dDistOut = DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertOut]].ptP, plPlane) ; - Point3d ptInt = Media( m_vVert[m_vTria[i].nIdVert[nVertIn]].ptP, - m_vVert[m_vTria[i].nIdVert[nVertOut]].ptP, - dDistIn / ( dDistIn + dDistOut)) ; - // inserisco il nuovo vertice - int nNewV = AddVertex( ptInt) ; - // inserisco il nuovo triangolo - int nIdVert[3] = { m_vTria[i].nIdVert[0], m_vTria[i].nIdVert[1], m_vTria[i].nIdVert[2]} ; - nIdVert[nVertOut] = nNewV ; - int nIdTria = AddTriangle( nIdVert) ; - if ( nIdTria == SVT_NULL) - return false ; - if ( nIdTria != SVT_DEL) - m_vTria[nIdTria].nTemp = m_nTimeStamp ; - // cancello il vecchio triangolo - RemoveTriangle( i) ; - } - // se ha un vertice all'interno e due all'esterno -> 2 nuovi vertici e 1 nuovo triangolo - else if ( nDisc == 1) { - // classifico i vertici - int nVertIn, nVertOut1, nVertOut2 ; - if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp == -1) { - nVertIn = 0 ; - nVertOut1 = 1 ; - nVertOut2 = 2 ; - } - else if ( m_vVert[ m_vTria[i].nIdVert[1]].nTemp == -1) { - nVertIn = 1 ; - nVertOut1 = 2 ; - nVertOut2 = 0 ; - } - else { - nVertIn = 2 ; - nVertOut1 = 0 ; - nVertOut2 = 1 ; - } - // calcolo i punti di intersezione dei lati che attraversano il piano - double dDistIn = - DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertIn]].ptP, plPlane) ; - double dDistOut1 = DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertOut1]].ptP, plPlane) ; - double dDistOut2 = DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertOut2]].ptP, plPlane) ; - Point3d ptInt1 = Media( m_vVert[m_vTria[i].nIdVert[nVertIn]].ptP, - m_vVert[m_vTria[i].nIdVert[nVertOut1]].ptP, - dDistIn / ( dDistIn + dDistOut1)) ; - Point3d ptInt2 = Media( m_vVert[m_vTria[i].nIdVert[nVertIn]].ptP, - m_vVert[m_vTria[i].nIdVert[nVertOut2]].ptP, - dDistIn / ( dDistIn + dDistOut2)) ; - // inserisco i nuovi vertici - int nNewV1 = AddVertex( ptInt1) ; - int nNewV2 = AddVertex( ptInt2) ; - // inserisco il nuovo triangolo - int nIdVert[3] = { m_vTria[i].nIdVert[0], m_vTria[i].nIdVert[1], m_vTria[i].nIdVert[2]} ; - nIdVert[nVertOut1] = nNewV1 ; - nIdVert[nVertOut2] = nNewV2 ; - int nIdTria = AddTriangle( nIdVert) ; - if ( nIdTria == SVT_NULL) - return false ; - if ( nIdTria != SVT_DEL) - m_vTria[nIdTria].nTemp = m_nTimeStamp ; - // cancello il vecchio triangolo - RemoveTriangle( i) ; - } - // altrimenti ha due vertici all'interno e uno all'esterno -> 2 nuovi vertici e 2 nuovi triangoli - else { - // classifico i vertici - int nVertIn1, nVertIn2, nVertOut ; - if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp == 1) { - nVertOut = 0 ; - nVertIn1 = 1 ; - nVertIn2 = 2 ; - } - else if ( m_vVert[ m_vTria[i].nIdVert[1]].nTemp == 1) { - nVertOut = 1 ; - nVertIn1 = 2 ; - nVertIn2 = 0 ; - } - else { - nVertOut = 2 ; - nVertIn1 = 0 ; - nVertIn2 = 1 ; - } - // calcolo i punti di intersezione dei lati che attraversano il piano - double dDistIn1 = - DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertIn1]].ptP, plPlane) ; - double dDistIn2 = - DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertIn2]].ptP, plPlane) ; - double dDistOut = DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertOut]].ptP, plPlane) ; - Point3d ptInt1 = Media( m_vVert[m_vTria[i].nIdVert[nVertIn1]].ptP, - m_vVert[m_vTria[i].nIdVert[nVertOut]].ptP, - dDistIn1 / ( dDistIn1 + dDistOut)) ; - Point3d ptInt2 = Media( m_vVert[m_vTria[i].nIdVert[nVertIn2]].ptP, - m_vVert[m_vTria[i].nIdVert[nVertOut]].ptP, - dDistIn2 / ( dDistIn2 + dDistOut)) ; - // inserisco i nuovi vertici - int nNewV1 = AddVertex( ptInt1) ; - int nNewV2 = AddVertex( ptInt2) ; - // inserisco i nuovi triangoli - int nIdVert1[3] = { m_vTria[i].nIdVert[0], m_vTria[i].nIdVert[1], m_vTria[i].nIdVert[2]} ; - int nIdVert2[3] = { m_vTria[i].nIdVert[0], m_vTria[i].nIdVert[1], m_vTria[i].nIdVert[2]} ; - double dSqDist1 = SqDist( m_vVert[m_vTria[i].nIdVert[nVertIn1]].ptP, ptInt2) ; - double dSqDist2 = SqDist( m_vVert[m_vTria[i].nIdVert[nVertIn2]].ptP, ptInt1) ; - if ( dSqDist1 <= dSqDist2) { - nIdVert1[nVertOut] = nNewV1 ; - nIdVert1[nVertIn2] = nNewV2 ; - nIdVert2[nVertOut] = nNewV2 ; - } - else { - nIdVert1[nVertOut] = nNewV1 ; - nIdVert2[nVertOut] = nNewV2 ; - nIdVert2[nVertIn1] = nNewV1 ; - } - int nIdTria1 = AddTriangle( nIdVert1) ; - int nIdTria2 = AddTriangle( nIdVert2) ; - if ( nIdTria1 == SVT_NULL || nIdTria2 == SVT_NULL) - return false ; - if ( nIdTria1 != SVT_DEL) - m_vTria[nIdTria1].nTemp = m_nTimeStamp ; - if ( nIdTria2 != SVT_DEL) - m_vTria[nIdTria2].nTemp = m_nTimeStamp ; - // cancello il vecchio triangolo - RemoveTriangle( i) ; - } - } - } - - return true ; -} - -//---------------------------------------------------------------------------- -bool -SurfTriMesh::CutByFacets( const Plane3d& plPlane, bool bSaveOnEq, bool& bModif) -{ - // Setto posizione triangoli non definita - for ( int nT = 0 ; nT < GetTriangleSize() ; ++ nT) - m_vTria[nT].nTempPart = 0 ; - - INTERSCHAINMAP IntersLineMap ; - - // Ciclo su tutte le facce. - for ( int nF = 0 ; nF < GetFacetCount() ; ++ nF) { - // Dati della faccia - POLYLINEVECTOR vLoopVec ; - GetFacetLoops( nF, vLoopVec) ; - if ( vLoopVec.empty()) - continue ; - // Verifico la posizione del loop esterno - int nIntType = VerifyLoopPlane( vLoopVec[0], plPlane) ; - // Se interseca il piano di taglio, calcolo la divisione della faccia - LineFacetClassVector IntersLinePart ; - if ( nIntType == FPI_CUT) { - PtrOwner pReg( GetBasicSurfFlatRegion( GetSurfFlatRegionFromPolyLineVector( vLoopVec))) ; - if ( IsNull( pReg) || ! pReg->IsValid()) - return false ; - nIntType = IntersFacetPlane( *pReg, plPlane, IntersLinePart) ; - } - // Gestione dei risultati - if ( nIntType == FPI_CUT) { - for ( int nPart = 0 ; nPart < int( IntersLinePart.size()) ; ++ nPart) { - // Salvo intersezione per la faccia. - auto it = IntersLineMap.find( nF) ; - if ( it != IntersLineMap.end()) { - it->second.emplace_back( IntersInnSeg( IntersLinePart[nPart].ptSt, IntersLinePart[nPart].ptEn)) ; - } - else { - IntersLineMap.emplace( nF, IntersInnChain( 1, IntersInnSeg( IntersLinePart[nPart].ptSt, IntersLinePart[nPart].ptEn))) ; - } - } - } - else if ( nIntType == FPI_ON) { - INTVECTOR vT ; - GetAllTriaInFacet( nF, vT) ; - Vector3d vtNf ; - GetFacetNormal( nF, vtNf) ; - for ( auto& nT : vT) - m_vTria[nT].nTempPart = ( vtNf * plPlane.GetVersN() > 0 ? 2 : -2) ; - } - else if ( nIntType == FPI_IN) { - INTVECTOR vT ; - GetAllTriaInFacet( nF, vT) ; - for ( auto& nT : vT) - m_vTria[nT].nTempPart = 1 ; - } - else if ( nIntType == FPI_OUT) { - INTVECTOR vT ; - GetAllTriaInFacet( nF, vT) ; - for ( auto& nT : vT) - m_vTria[nT].nTempPart = -1 ; - } - else - return false ; - } - - // Divido le facce. - PieceMap NewFacet ; - SplitFacet( IntersLineMap, NewFacet) ; - INTERSEDGEMAP EdgeInterLineMap, EdgeEdgeLineMap ; - RetriangulateFacetPieces( NewFacet, EdgeInterLineMap, EdgeEdgeLineMap) ; - - // Elimino i triangoli superflui - for ( int nT = 0 ; nT < GetTriangleSize() ; ++ nT) { - if ( m_vTria[nT].nTempPart == 0 || - m_vTria[nT].nTempPart == -1 || - m_vTria[nT].nTempPart == -2 || - ( ! bSaveOnEq && m_vTria[nT].nTempPart == 2)) { - RemoveTriangle( nT) ; - bModif = true ; - } - } - - return true ; -} - -//---------------------------------------------------------------------------- -bool -SurfTriMesh::Cut( const Plane3d& plPlane, bool bSaveOnEq) -{ - // la superficie deve essere validata - if ( m_nStatus != OK) - return false ; - - // recupero il numero originale di triangoli e di facce - int nTriaOriCnt = GetTriangleCount() ; - int nFacetOriCnt = GetFacetCount() ; - - // eseguo il taglio con il metodo delle faccette o dei triangoli (per ora solo coi triangoli) - bool bModif = false ; - if ( true || ! CutByFacets( plPlane, bSaveOnEq, bModif)) { - bModif = false ; - if ( ! CutByTriangles( plPlane, bSaveOnEq, bModif)) - return false ; - } - - // se effettuate modifiche - if ( bModif) { - // aggiorno tutto - if ( ! AdjustVertices() || ! DoCompacting()) - return false ; - } - - // se superficie originale a facce, cerco di semplificarle in ogni caso - if ( nFacetOriCnt < 200 || double( nTriaOriCnt) / nFacetOriCnt > 4) { - if ( ! SimplifyFacets( 500.0)) - LOG_ERROR( GetEGkLogger(), "Error in SimplifyFacets of Stm::Cut") - } - - return true ; -} - //---------------------------------------------------------------------------- void SurfTriMesh::ResetHashGrids3d( void) const diff --git a/SurfTriMesh.h b/SurfTriMesh.h index 6e61c43..7cf67b3 100644 --- a/SurfTriMesh.h +++ b/SurfTriMesh.h @@ -397,7 +397,6 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW bool RetriangulateFacetPieces( const PieceMap& NewFacet, const INTERSEDGEMAP& EdgeInterLineMap, const INTERSEDGEMAP& EdgeEdgeLineMap) ; - bool SimplifyFacets( double dMaxEdgeLen) ; bool EdgeInteriorContactManager( const SurfTriMesh& OthSurf, const INTERSCHAINMAP& InterInterLineMap, const INTERSEDGEMAP& EdgeInterLineMap) ; @@ -411,6 +410,11 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW bool RemoveTripleTriangles( void) ; bool ScanForTripleTriangles( bool& bModified) ; bool FlipTriangles( int nTA, int nTB) ; + bool SimplifyFacets( double dMaxEdgeLen) ; + bool AddChainToChain( const Chain& ChainToAdd, PNTVECTOR& OrigChain) ; + bool DistPointFacet( const Point3d& ptP, const POLYLINEVECTOR& vPolyVec, double& dPointFacetDist) ; + bool ChangeStart( const Point3d& ptNewStart, PNTVECTOR& Loop) ; + bool SplitAtPoint( const Point3d& ptStop, const PNTVECTOR& Loop, PNTVECTOR& Loop1, PNTVECTOR& Loop2) ; private : ObjGraphicsMgr m_OGrMgr ; // gestore grafica dell'oggetto diff --git a/SurfTriMeshBooleans.cpp b/SurfTriMeshBooleans.cpp index 6ee90d0..2b199d5 100644 --- a/SurfTriMeshBooleans.cpp +++ b/SurfTriMeshBooleans.cpp @@ -1,13 +1,14 @@ //---------------------------------------------------------------------------- -// EgalTech 2019-2020 +// EgalTech 2019-2021 //---------------------------------------------------------------------------- -// File : SurfTriMeshBooleans.cpp Data : 01.10.20 Versione : 2.2j1 +// File : SurfTriMeshBooleans.cpp Data : 06.11.21 Versione : 2.3k3 // Contenuto : Implementazione delle funzioni booleane per SurfFTrimesh. // // // // Modifiche : 10.05.19 LM Creazione modulo. // 01.10.20 LM Aggiunte scalature 1024x. +// 06.11.21 LM Rifacimento GeneralizedCut. // //---------------------------------------------------------------------------- @@ -20,19 +21,19 @@ #include "Triangulate.h" #include "GeoConst.h" #include "/EgtDev/Include/EgtNumUtils.h" -#include "/EgtDev/Include/EgkCurve.h" -#include "/EgtDev/Include/EgkDistPointCurve.h" -#include "/EgtDev/Include/EgkDistPointTria.h" -#include "/EgtDev/Include/EgkIntersLineTria.h" -#include "/EgtDev/Include/EgkIntersLineBox.h" -#include "/EgtDev/Include/EgkIntersPlanePlane.h" -#include "/EgtDev/Include/EgkIntersTriaTria.h" -#include "/EgtDev/Include/EgkSfrCreate.h" +#include "/EgtDev/Include/EGkCurve.h" +#include "/EgtDev/Include/EGkDistPointCurve.h" +#include "/EgtDev/Include/EGkDistPointTria.h" +#include "/EgtDev/Include/EGkIntersLineTria.h" +#include "/EgtDev/Include/EGkIntersLineBox.h" +#include "/EgtDev/Include/EGkIntersPlanePlane.h" +#include "/EgtDev/Include/EGkIntersTriaTria.h" +#include "/EgtDev/Include/EGkSfrCreate.h" #include "/EgtDev/Include/EGkChainCurves.h" #include "/EgtDev/Include/EGkGeoCollection.h" #include "/EgtDev/Include/EGkPolygon3d.h" #include "/EgtDev/Include/EgtPerfCounter.h" -#include "/EgtDev/Include/EgnStringUtils.h" +#include "/EgtDev/Include/EGnStringUtils.h" #include using namespace std ; @@ -40,629 +41,6 @@ using namespace std ; //---------------------------------------------------------------------------- const double BOOLEAN_SCALE = 1024 ; -//---------------------------------------------------------------------------- -static int -IntersRectangleTriangle( const Point3d& ptP, const Vector3d& vtL1, const Vector3d& vtL2, - const Triangle3d& trTria, Point3d& ptStSeg, Point3d& ptEnSeg) -{ - // Definisco i due triangoli formanti il rettangolo - Triangle3d trTriaA ; - trTriaA.Set( ptP, ptP + vtL1, ptP + vtL2) ; - if ( ! trTriaA.Validate()) - return -1 ; - Triangle3d trTriaB ; - trTriaB.Set( ptP + vtL1, ptP + vtL1 + vtL2, ptP + vtL2) ; - if ( ! trTriaB.Validate()) - return -1 ; - // Interseco il triangolo con il primo dei due triangoli del rettangolo - int nIntA = 0 ; - Point3d ptIntA1, ptIntA2 ; - TRIA3DVECTOR vTriaA ; - int nIntTypeA = IntersTriaTria( trTria, trTriaA, ptIntA1, ptIntA2, vTriaA) ; - if ( nIntTypeA == ITTT_VERT_VERT || nIntTypeA == ITTT_VERT_EDGE || nIntTypeA == ITTT_VERT_INT || nIntTypeA == ITTT_EDGE_VERT || nIntTypeA == ITTT_EDGE_EDGE_PNT || nIntTypeA == ITTT_INT_VERT) - nIntA = 1 ; - else if ( /*nIntTypeA == ITTT_EDGE_EDGE_SEG || nIntTypeA == ITTT_EDGE_INT ||*/ nIntTypeA == ITTT_INT_EDGE || nIntTypeA == ITTT_INT_INT_SEG) { - nIntA = 2 ; - } - // Interseco il triangolo con il secondo dei due triangoli del rettangolo - int nIntB = 0 ; - Point3d ptIntB1, ptIntB2 ; - TRIA3DVECTOR vTriaB ; - int nIntTypeB = IntersTriaTria( trTria, trTriaB, ptIntB1, ptIntB2, vTriaB) ; - if ( nIntTypeB == ITTT_VERT_VERT || nIntTypeB == ITTT_VERT_EDGE || nIntTypeB == ITTT_VERT_INT || nIntTypeB == ITTT_EDGE_VERT || nIntTypeB == ITTT_EDGE_EDGE_PNT || nIntTypeB == ITTT_INT_VERT) - nIntB = 1 ; - else if ( /*nIntTypeB == ITTT_EDGE_EDGE_SEG || nIntTypeB == ITTT_EDGE_INT ||*/ nIntTypeB == ITTT_INT_EDGE || nIntTypeB == ITTT_INT_INT_SEG) { - nIntB = 2 ; - } - // Unisco le due intersezioni - int nIntTot = nIntA + nIntB ; - if ( nIntTot == 4) { - if ( AreSamePointApprox( ptIntA2, ptIntB1)) { - ptStSeg = ptIntA1 ; - ptEnSeg = ptIntB2 ; - } - else { - ptStSeg = ptIntB1 ; - ptEnSeg = ptIntA2 ; - } - return 2 ; - } - else if ( nIntTot == 3) { - if ( nIntA == 2) { - ptStSeg = ptIntA1 ; - ptEnSeg = ptIntA2 ; - } - else { - ptStSeg = ptIntB1 ; - ptEnSeg = ptIntB2 ; - } - return 2 ; - } - else if ( nIntTot == 2) { - if ( nIntA == 2) { - ptStSeg = ptIntA1 ; - ptEnSeg = ptIntA2 ; - } - else if ( nIntA == 1) { - ptStSeg = ptIntA1 ; - ptEnSeg = ptIntB1 ; - } - else { - ptStSeg = ptIntB1 ; - ptEnSeg = ptIntB2 ; - } - return 2 ; - } - else if ( nIntTot == 1) { - if ( nIntA == 1) - ptStSeg = ptIntA1 ; - else - ptStSeg = ptIntB1 ; - return 1 ; - } - else - return 0 ; -} - -//---------------------------------------------------------------------------- -static bool -AddChainToChain( const Chain& ChainToAdd, PNTVECTOR& OrigChain) -{ - // Se la catena da aggiungere è vuota, non devo fare alcunchè - if ( ChainToAdd.size() == 0) - return true ; - // Se la catena originale è vuota, non è possibile aggiungere nulla - if ( OrigChain.size() == 0) - return false ; - // Se la catena originale è chiusa non posso aggiungere nulla - int nLastOrig = max( int( OrigChain.size()) - 1, 0) ; - if ( AreSamePointApprox( OrigChain[0], OrigChain[nLastOrig])) - return false ; - int nLastToAdd = max( int( ChainToAdd.size()) - 1, 0) ; - if ( AreSamePointApprox( OrigChain[nLastOrig], ChainToAdd[0].ptSt)) { - for ( int nPt = 1 ; nPt <= nLastToAdd ; ++ nPt) { - if ( nPt == nLastToAdd) { - if ( ! AreSamePointApprox(OrigChain[0], ChainToAdd[nPt].ptSt)) - OrigChain.emplace_back( ChainToAdd[nPt].ptSt) ; - } - else if ( nPt == 1) { - if ( ! AreSamePointApprox( OrigChain[nLastOrig], ChainToAdd[nPt].ptSt)) - OrigChain.emplace_back( ChainToAdd[nPt].ptSt) ; - } - else - OrigChain.emplace_back( ChainToAdd[nPt].ptSt) ; - } - return true ; - } - else - return false ; -} - -//---------------------------------------------------------------------------- -// Una faccia di una trimesh ha una sola componente connessa, con un loop esterno e possibili loop interni -static bool -DistPointFacet( const Point3d& ptP, const POLYLINEVECTOR& vPolyVec, double& dPointFacetDist) -{ - // Verifico la presenza del loop esterno - if ( vPolyVec.size() < 1) - return false ; - // Proietto il punto sul piano della faccia, utilizzando il loop esterno - Plane3d plPlane ; - double dArea ; - if ( ! vPolyVec[0].IsClosedAndFlat( plPlane, dArea)) - return false ; - double dDistPtPl = DistPointPlane( ptP, plPlane) ; - Point3d ptProjP = ptP + dDistPtPl * plPlane.GetVersN() ; - // Verifico se il punto proiettato è esterno al loop esterno - int nPtOut = -1 ; - if ( ! IsPointInsidePolyLine( ptProjP, vPolyVec[0], EPS_SMALL)) - nPtOut = 0 ; - // Verifico se il punto proiettato è interno ai loop interni (quindi esterno alla faccia) - for ( int nLoop = 1 ; nLoop < int( vPolyVec.size()) && nPtOut < 0 ; ++ nLoop) { - Plane3d plPlane ; - double dArea ; - if ( ! vPolyVec[nLoop].IsClosedAndFlat( plPlane, dArea)) - return false ; - if ( IsPointInsidePolyLine( ptProjP, vPolyVec[nLoop], EPS_SMALL)) - nPtOut = nLoop ; - } - // Se il punto si proietta sulla faccia, la distanza dalla faccia coincide con quella dal piano - if ( nPtOut < 0) { - dPointFacetDist = abs( dDistPtPl) ; - return true ; - } - // Altrimenti calcolo la minima distanza del punto dalla polilinea del contorno a cui è esterno - double dDist ; - if ( DistPointPolyLine( ptP, vPolyVec[nPtOut], dDist)) { - dPointFacetDist = dDist ; - return true ; - } - return false ; -} - -//---------------------------------------------------------------------------- -static bool -ChangeStart( const Point3d& ptNewStart, PNTVECTOR& Loop) -{ - // Cerco il tratto del loop chiuso più vicino al punto - int nMinSeg = - 1 ; - double dMinSqDinst = DBL_MAX ; - for ( int nPt = 0 ; nPt < int( Loop.size()) ; ++ nPt) { - // Estremi del segmento corrente del loop - Point3d ptSegSt = Loop[nPt] ; - Point3d ptSegEn = Loop[( nPt + 1) % int( Loop.size())] ; - // Distanza del punto dal segmento del loop - DistPointLine dDistCalc( ptNewStart, ptSegSt, ptSegEn) ; - double dSqDist ; - dDistCalc.GetSqDist( dSqDist) ; - if ( dSqDist < dMinSqDinst) { - dMinSqDinst = dSqDist ; - nMinSeg = nPt ; - } - } - // Se il punto non sta sul loop, errore - if ( dMinSqDinst > SQ_EPS_SMALL) - return false ; - // Verifico che il punto stia su un vertice, in tal caso non devo fare nulla - bool bOnStart = AreSamePointApprox( Loop[nMinSeg], ptNewStart) ; - bool bOnEnd = AreSamePointApprox( Loop[( nMinSeg + 1) % int( Loop.size())], ptNewStart) ; - if ( bOnStart || bOnEnd) { - if ( bOnEnd) { - ++ nMinSeg ; - if ( nMinSeg % int( Loop.size()) == 0) - return true ; - } - PNTVECTOR vTempVec ; - for ( int nPt = 0 ; nPt < nMinSeg ; ++ nPt) - vTempVec.emplace_back( Loop[nPt]) ; - int nSize = int( Loop.size()) ; - for ( int nPt = 0 ; nPt < nSize - nMinSeg ; ++ nPt) { - Loop[nPt] = Loop[nPt + nMinSeg] ; - } - for ( int nPt = 0 ; nPt < int( vTempVec.size()) ; ++ nPt) { - Loop[nPt + nSize - nMinSeg] = vTempVec[nPt] ; - } - return true ; - } - // Ridimensiono il loop - Loop.resize( Loop.size() + 1) ; - // Copio i primi punti - PNTVECTOR LoopTemp ; - for ( int nPt = 0 ; nPt <= nMinSeg ; ++ nPt) - LoopTemp.emplace_back( Loop[nPt]) ; - // Aggiungo il nuovo punto all'inizio - Loop[0] = ptNewStart ; - // Sposto gli ultimi in testa - int nLastPointNum = int( Loop.size()) - 1 - nMinSeg ; - for ( int nPt = 1 ; nPt <= nLastPointNum ; ++ nPt) { - Loop[nPt] = Loop[nPt + nMinSeg] ; - } - // Porto i primi in fondo - for ( int nPt = 0 ; nPt < int( LoopTemp.size()) ; ++ nPt) { - Loop[nPt + nLastPointNum] = LoopTemp[nPt] ; - } - return true ; -} - -//---------------------------------------------------------------------------- -static bool -SplitAtPoint( const Point3d& ptStop, const PNTVECTOR& Loop, PNTVECTOR& Loop1, PNTVECTOR& Loop2) -{ - // Cerco il tratto del loop chiuso più vicino al punto - int nMinSeg = -1 ; - double dMinSqDinst = DBL_MAX ; - for ( int nPt = 0 ; nPt < int( Loop.size()) ; ++ nPt) { - // Estremi del segmento corrente del loop - Point3d ptSegSt = Loop[nPt] ; - Point3d ptSegEn = Loop[( nPt + 1) % int(Loop.size())] ; - // Distanza del punto dal segmento del loop - DistPointLine dDistCalc( ptStop, ptSegSt, ptSegEn) ; - double dSqDist ; - dDistCalc.GetSqDist( dSqDist) ; - if ( dSqDist < dMinSqDinst) { - dMinSqDinst = dSqDist ; - nMinSeg = nPt ; - } - } - // Se il punto non sta sul loop, errore - if ( dMinSqDinst > SQ_EPS_SMALL) - return false ; - // Verifico che il punto stia su un vertice, in tal caso non devo aggiungerlo - bool bFirst = AreSamePointApprox( Loop[nMinSeg], ptStop) ; - bool bLast = AreSamePointApprox( Loop[( nMinSeg + 1) % int( Loop.size())], ptStop) ; - // Se il punto è sul vertice finale del segmento, aggiungo il vertice alla lista da inglobare al primo loop - if ( bLast) - ++ nMinSeg ; - // Inglobo fino a nSeg nel primo loop - for ( int nPt = 0 ; nPt <= nMinSeg ; ++ nPt) - Loop1.emplace_back( Loop[nPt]) ; - // Se il punto è interno al segmento, lo inglobo in entrambi i loop - if ( ! ( bFirst || bLast)) { - Loop1.emplace_back( ptStop) ; - Loop2.emplace_back( ptStop) ; - } - else { - Loop2.emplace_back( Loop[nMinSeg]) ; - } - // Inglobo gli ultimi vertici in Loop2 - for ( int nPt = nMinSeg + 1 ; nPt < int( Loop.size()) ; ++ nPt) - Loop2.emplace_back( Loop[nPt]) ; - Loop2.emplace_back( Loop[0]) ; - - return true ; -} - -//---------------------------------------------------------------------------- -bool -SurfTriMesh::GeneralizedCut( const ICurve& cvCurve, bool bSaveOnEq) -{ - // La superficie deve essere valida - if ( m_nStatus != OK) - return false ; - // La curva deve essere valida e chiusa, il vettore estrusione deve essere non nullo - Vector3d vtExtr ; - if ( ! cvCurve.GetExtrusion( vtExtr) || vtExtr.IsSmall() || ! cvCurve.IsClosed()) - return false ; - - // Recupero il numero originale di triangoli e di facce - int nTriaOriCnt = GetTriangleCount() ; - int nFacetOriCnt = GetFacetCount() ; - - // Approssimo la curva con segmenti - CurveComposite cvCompo ; - PolyLine PL ; - if ( ! cvCurve.ApproxWithLines( LIN_TOL_FINE, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) || ! cvCompo.FromPolyLine( PL)) - return false ; - // Appiattisco la polilinea nel piano perpendicolare all'estrusione - Frame3d frCurve ; - Point3d ptStart ; cvCompo.GetStartPoint( ptStart) ; - frCurve.Set( ptStart, vtExtr) ; - cvCompo.ToLoc( frCurve) ; - if ( ! cvCompo.Scale( GLOB_FRM, 1, 1, 0)) - return false ; - double dArea ; - cvCompo.GetAreaXY( dArea) ; - BBox3d b3Crv ; - cvCompo.GetLocalBBox( b3Crv) ; - cvCompo.ToGlob( frCurve) ; - // Assegno il senso di rotazione della curva (visto dalla punta del vettore estrusione) - bool bCCW = ( dArea > 0) ; - - // Recupero Bounding-box della trimesh - BBox3d b3SurfBox ; - GetLocalBBox( b3SurfBox) ; - // Trovo minima e massima distanza dei vertici del bounding-box della TriMesh dal piano della curva - b3SurfBox.ToLoc( frCurve) ; - Point3d ptMin, ptMax ; - b3SurfBox.GetMinMax( ptMin, ptMax) ; - Vector3d vtMax = ( ptMax.z + 10) * vtExtr ; - Vector3d vtMin = ( ptMin.z - 10) * vtExtr ; - - // Ciclo sui triangoli - bool bModif = false ; - int nNumTria = GetTriangleSize() ; - for ( int nT = 0 ; nT < nNumTria ; ++ nT) { - // Recupero il triangolo - Triangle3d trTria ; - if ( ! GetTriangle( nT, trTria)) - continue ; - // Box del triangolo nel riferimento locale della curva - BBox3d b3Tria ; - trTria.GetLocalBBox( b3Tria) ; - b3Tria.ToLoc( frCurve) ; - // Se il box del triangolo non interseca quello locale della curva - if ( ! b3Crv.OverlapsXY( b3Tria)) { - // Se la parte da conservare è quella all'interno della curva, elimino il triangolo - if ( bCCW) { - RemoveTriangle( nT) ; - bModif = true ; - } - continue ; - } - // Determino il numero di vertici del triangolo che cadono all'interno della curva - int nVertInside = 0 ; - int nVertOutside = 0 ; - for ( int nV = 0 ; nV < 3 ; ++ nV) { - // Determino se il vertice cade dentro la curva - DistPointCurve dstPC( trTria.GetP( nV), cvCompo) ; - int nSide ; - dstPC.GetSideAtMinDistPoint( 0, vtExtr, nSide) ; - if ( nSide == MDS_LEFT || nSide == MDS_ON) - ++ nVertInside ; - else - ++ nVertOutside ; - } - - // Vettore di catene di punti - CHAINVECTOR vChain ; - // Ciclo sui segmenti - int nChainCnt = 0 ; - bool bChain = false ; - Point3d ptChSt, ptChEn ; - const ICurve* pCrv = cvCompo.GetFirstCurve() ; - while ( pCrv != nullptr) { - // estremi del segmento - Point3d ptSt ; pCrv->GetStartPoint( ptSt) ; - Point3d ptEn ; pCrv->GetEndPoint( ptEn) ; - // Intersezione fra il rettangolo (ottenuto dall'estrusione del segmento corrente) e il triangolo - Point3d ptSegSt, ptSegEn ; - int nInt = IntersRectangleTriangle( ptSt + vtMin, ptEn - ptSt, vtMax - vtMin, trTria, ptSegSt, ptSegEn) ; - if ( nInt == 2) { - // Creo nuova catena se non c'è già o se discontinuità - if ( ! bChain || ( ! AreSamePointApprox( ptSegSt, ptChEn) && ! AreSamePointApprox( ptSegEn, ptChSt))) { - ++ nChainCnt ; - vChain.resize( nChainCnt) ; - bChain = false ; - } - // Assegno i dati di intersezione - IntSegment CurInters ; - CurInters.ptSt = ptSegSt ; - CurInters.ptEn = ptSegEn ; - // Inserisco nella catena - if ( ! bChain) { - vChain[nChainCnt - 1].emplace_back( CurInters) ; - ptChSt = CurInters.ptSt ; - ptChEn = CurInters.ptEn ; - } - else if ( AreSamePointApprox(ptSegSt, ptChEn)) { - vChain[nChainCnt - 1].emplace_back(CurInters) ; - ptChEn = CurInters.ptEn ; - } - else { - vChain[nChainCnt - 1].insert(vChain[nChainCnt - 1].begin(), CurInters) ; - ptChSt = CurInters.ptSt ; - } - bChain = true ; - } - else { - bChain = false ; - } - pCrv = cvCompo.GetNextCurve() ; - } - if ( nChainCnt == 0 && nVertOutside > 0) { - nVertInside = 0 ; - vChain.clear() ; - nChainCnt = 0 ; - } - // unisco eventuali catene estreme che sono parte di una stessa catena - if ( nChainCnt > 1) { - if ( AreSamePointApprox( vChain[0].front().ptSt, vChain[nChainCnt - 1].back().ptEn)) { - vChain[0].insert( vChain[0].begin(), vChain[nChainCnt - 1].begin(), vChain[nChainCnt - 1].end()) ; - vChain.pop_back() ; - -- nChainCnt ; - } - else if ( AreSamePointApprox( vChain[0].back().ptEn, vChain[nChainCnt - 1].front().ptSt)) { - vChain[0].insert(vChain[0].end(), vChain[nChainCnt - 1].begin(), vChain[nChainCnt - 1].end()) ; - vChain.pop_back() ; - -- nChainCnt ; - } - } - - // Elimino la seconda copia di catene doppie - for ( int nI = 0 ; nI < nChainCnt ; ++ nI) { - for ( int nJ = nI + 1 ; nJ < nChainCnt ; ++ nJ) { - if ( vChain[nI].size() == vChain[nJ].size()) { - bool bSame = true ; - for ( int nK = 0 ; nK < int( vChain[nI].size()) ; ++ nK) { - if ( ! AreSamePointApprox( vChain[nI][nK].ptSt, vChain[nJ][nK].ptSt) || - ! AreSamePointApprox( vChain[nI][nK].ptEn, vChain[nJ][nK].ptEn)) { - bSame = false ; - break ; - } - } - if ( bSame) { - vChain.erase( vChain.begin() + nJ) ; - -- nChainCnt ; - -- nJ ; - } - } - } - } - - // Fra le catene trovate separo le aperte dalle chiuse - CHAINVECTOR cvClosedChain ; - CHAINVECTOR cvOpenChain ; - for ( int nL = 0 ; nL < int( vChain.size()) ; ++ nL) { - int nCurLoopLast = max( int(vChain[nL].size()) - 1, 0) ; - if ( AreSamePointApprox( vChain[nL][0].ptSt, vChain[nL][nCurLoopLast].ptEn)) - cvClosedChain.emplace_back( vChain[nL]) ; - else { - cvOpenChain.emplace_back( vChain[nL]) ; - } - } - for ( auto it = cvClosedChain.begin() ; it != cvClosedChain.end() ; ) { - if ( int( it->size()) < 3) - it = cvClosedChain.erase( it) ; - else - ++ it ; - } - // Se più di una catena chiusa oppure catene chiuse e aperte, errore - if ( cvClosedChain.size() > 1 || ( cvClosedChain.size() > 0 && int( cvOpenChain.size()) > 0)) - return false ; - - // Se c'è una catena chiusa - if ( cvClosedChain.size() == 1) { - // Ne ricavo una PolyLine - PolyLine plInLoop ; - for ( int nLine = 0 ; nLine < int( cvClosedChain[0].size()) ; ++ nLine) { - plInLoop.AddUPoint( 0., cvClosedChain[0][nLine].ptSt) ; - plInLoop.AddUPoint( 0., cvClosedChain[0][nLine].ptEn) ; - } - // I tre vertici sono dalla parte interna della curva (triangolo con buco) - if ( ! bCCW) { - // Rimuovo il triangolo corrente - RemoveTriangle( nT) ; - // Definisco il loop esterno (è il triangolo) - PolyLine plExtLoop ; - plExtLoop.AddUPoint( 0., trTria.GetP( 0)) ; - plExtLoop.AddUPoint( 0., trTria.GetP( 1)) ; - plExtLoop.AddUPoint( 0., trTria.GetP( 2)) ; - plExtLoop.AddUPoint( 0., trTria.GetP( 0)) ; - // Eseguo triangolazione - POLYLINEVECTOR vPL ; - vPL.emplace_back( plExtLoop) ; - vPL.emplace_back( plInLoop) ; - PNTVECTOR vPt ; - INTVECTOR vTr ; - if ( Triangulate().Make( vPL, vPt, vTr)) { - // Inserisco i nuovi triangoli - for ( int n = 0 ; n < int( vTr.size()) - 2 ; n += 3) { - int nNewTriaVertId[3] = { vTr[n], vTr[n + 1], vTr[n + 2] } ; - int nNewId[3] = { AddVertex( vPt[nNewTriaVertId[0]]), - AddVertex( vPt[nNewTriaVertId[1]]), - AddVertex( vPt[nNewTriaVertId[2]]) } ; - AddTriangle( nNewId) ; - bModif = true ; - } - } - } - // Se nessun vertice dalla parte interna della curva (rimane solo l'area della curva) - else { - // Rimuovo il triangolo corrente - RemoveTriangle( nT) ; - // Eseguo triangolazione - PNTVECTOR vPt ; - INTVECTOR vTr ; - if ( Triangulate().Make( plInLoop, vPt, vTr)) { - // Inserisco i nuovi triangoli - for ( int n = 0 ; n < int( vTr.size()) - 2 ; n += 3) { - int nNewTriaVertId[3] = { vTr[n], vTr[n + 1], vTr[n + 2] } ; - int nNewId[3] = { AddVertex(vPt[nNewTriaVertId[0]]), - AddVertex(vPt[nNewTriaVertId[1]]), - AddVertex(vPt[nNewTriaVertId[2]]) } ; - AddTriangle( nNewId) ; - bModif = true ; - } - } - } - } - - // Loop aperti, devo chiuderli - else if ( cvOpenChain.size() > 0) { - // Creo il loop chiuso padre di tutti, il perimetro del triangolo. - // Questo viene diviso in sotto-loop chiusi mediante quelli aperti. - // I loop chiusi trovati precedentemente sono interni a uno dei sotto-loop - // chiusi di cui è formato il perimetro. - PNTVECTOR cvFirstLoop ; - cvFirstLoop.emplace_back( trTria.GetP( 0)) ; - cvFirstLoop.emplace_back( trTria.GetP( 1)) ; - cvFirstLoop.emplace_back( trTria.GetP( 2)) ; - - PNTMATRIX cvBoundClosedLoopVec ; - cvBoundClosedLoopVec.emplace_back( cvFirstLoop) ; - BOOLVECTOR vbInOut ; - vbInOut.push_back( true) ; - // Divido il loop di partenza in sotto-loop - while ( cvOpenChain.size() > 0) { - int nLastOpenLoopN = int( cvOpenChain.size()) - 1 ; - for ( int nLoop = 0 ; nLoop < int( cvBoundClosedLoopVec.size()) ; ++ nLoop) { - // Estremi del loop aperto - int nLastOpenLoopPoint = max( int( cvOpenChain[nLastOpenLoopN].size()) - 1, 0) ; - Point3d ptOpenLoopStP = cvOpenChain[nLastOpenLoopN][0].ptSt ; - Point3d ptOpenLoopEnP = cvOpenChain[nLastOpenLoopN][nLastOpenLoopPoint].ptEn ; - PNTVECTOR Loop1, Loop2 ; - bool bChangedStart = ChangeStart( ptOpenLoopStP, cvBoundClosedLoopVec[nLoop]) ; - bool bSplitted = SplitAtPoint( ptOpenLoopEnP, cvBoundClosedLoopVec[nLoop], Loop1, Loop2) ; - if ( ! ( bChangedStart && bSplitted)) - continue ; - Chain cvCounterChain ; - for ( int nPt = int( cvOpenChain[nLastOpenLoopN].size()) - 1 ; nPt >= 0 ; -- nPt) { - IntSegment CurSeg ; - CurSeg.ptSt = cvOpenChain[nLastOpenLoopN][nPt].ptEn ; - CurSeg.ptEn = cvOpenChain[nLastOpenLoopN][nPt].ptSt ; - cvCounterChain.emplace_back( CurSeg) ; - } - bool bAdded1 = AddChainToChain( cvCounterChain, Loop1) ; - bool bAdded2 = AddChainToChain( cvOpenChain[nLastOpenLoopN], Loop2) ; - if ( ! ( bAdded1 && bAdded2)) - continue ; - // Aggiungo i nuovi loop nel vettore - int nCurSize = int( cvBoundClosedLoopVec.size()) ; - cvBoundClosedLoopVec.resize( nCurSize + 1) ; - vbInOut.resize( nCurSize + 1) ; - for ( int nCL = nCurSize - 1 ; nCL > nLoop ; -- nCL) { - cvBoundClosedLoopVec[nCL + 1] = cvBoundClosedLoopVec[nCL] ; - vbInOut[nCL + 1] = vbInOut[nCL] ; - } - cvBoundClosedLoopVec[nLoop] = Loop1 ; - cvBoundClosedLoopVec[nLoop + 1] = Loop2 ; - vbInOut[nLoop] = false ; - vbInOut[nLoop + 1] = true ; - ++ nLoop ; - } - cvOpenChain.resize( nLastOpenLoopN) ; - } - // Rimuovo il triangolo corrente - RemoveTriangle( nT) ; - // Trasformo i loop compositi in loop polyline - POLYLINEVECTOR vplPolyVec ; - vplPolyVec.resize(cvBoundClosedLoopVec.size()) ; - for ( int nLoop = 0 ; nLoop < int( vplPolyVec.size()) ; ++ nLoop) { - for ( int nLine = 0 ; nLine < int( cvBoundClosedLoopVec[nLoop].size()) ; ++ nLine) { - vplPolyVec[nLoop].AddUPoint( 0., cvBoundClosedLoopVec[nLoop][nLine]) ; - } - vplPolyVec[nLoop].AddUPoint( 0., cvBoundClosedLoopVec[nLoop][0]) ; - if ( vbInOut[nLoop]) { - // Eseguo triangolazione - Triangulate CreateTriangulation ; - PNTVECTOR vPt ; - INTVECTOR vTr ; - if ( Triangulate().Make( vplPolyVec[nLoop], vPt, vTr)) { - // Inserisco i nuovi triangoli - for ( int n = 0 ; n < int( vTr.size()) - 2 ; n += 3) { - int nNewTriaVertId[3] = { vTr[n], vTr[n + 1], vTr[n + 2] } ; - int nNewId[3] = { AddVertex(vPt[nNewTriaVertId[0]]), - AddVertex(vPt[nNewTriaVertId[1]]), - AddVertex(vPt[nNewTriaVertId[2]]) } ; - AddTriangle( nNewId) ; - bModif = true ; - } - } - } - } - } - - // da eliminare - else if ( nVertInside == 0) - RemoveTriangle( nT) ; - } - - // Se avvenuta modifica, aggiorno tutto - if ( bModif) { - // aggiorno tutto - if ( ! AdjustVertices() || ! DoCompacting()) - return false ; - } - - // se superficie originale a facce, cerco di semplificarle in ogni caso - if ( nFacetOriCnt < 200 || double( nTriaOriCnt) / nFacetOriCnt > 4) { - if ( ! SimplifyFacets( 500.0)) - LOG_ERROR( GetEGkLogger(), "Error in SimplifyFacets of Stm::GeneralizedCut") - } - - return true ; -} - //---------------------------------------------------------------------------- bool SurfTriMesh::DecomposeLoop( CHAINVECTOR& cvOpenChain, INTVECTOR& vnDegVec, PNTMATRIX& cvBoundClosedLoopVec, BOOLVECTOR& vbInOut) @@ -1535,7 +913,7 @@ SurfTriMesh::RetriangulationForBooleanOperation( CHAINMAP& LoopLines, TRIA3DVECT } //---------------------------------------------------------------------------- -bool +static bool FindTriaIncidence( const Triangle3d& trTria, const TRIA3DVECTOR& vOthTriaVec, INTMATRIX& vAdjSegToCurTria) { int nNumFoundContact = 0 ; diff --git a/SurfTriMeshCuts.cpp b/SurfTriMeshCuts.cpp new file mode 100644 index 0000000..a49e846 --- /dev/null +++ b/SurfTriMeshCuts.cpp @@ -0,0 +1,807 @@ +//---------------------------------------------------------------------------- +// EgalTech 2019-2021 +//---------------------------------------------------------------------------- +// File : SurfTriMeshCuts.cpp Data : 06.11.21 Versione : 2.3k3 +// Contenuto : Implementazione delle funzioni di taglio per SurfFTrimesh. +// +// +// +// Modifiche : 06.11.21 DS Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +#include "stdafx.h" +#include "CurveComposite.h" +#include "SurfTriMesh.h" +#include "SurfFlatRegion.h" +#include "DistPointLine.h" +#include "Triangulate.h" +#include "GeoConst.h" +#include "/EgtDev/Include/EgkSfrCreate.h" +#include "/EgtDev/Include/EGkDistPointCurve.h" +#include "/EgtDev/Include/EGkIntersLinePlane.h" +#include "/EgtDev/Include/EGkIntersPlaneTria.h" + +using namespace std ; + +//---------------------------------------------------------------------------- +const double CUT_SCALE = 1024 ; + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::Cut( const Plane3d& plPlane, bool bSaveOnEq) +{ + // la superficie deve essere validata + if ( m_nStatus != OK) + return false ; + + // recupero il numero originale di triangoli e di facce + int nTriaOriCnt = GetTriangleCount() ; + int nFacetOriCnt = GetFacetCount() ; + + // eseguo il taglio con il metodo delle faccette o dei triangoli (per ora solo coi triangoli) + bool bModif = false ; + if ( true || ! CutByFacets( plPlane, bSaveOnEq, bModif)) { + bModif = false ; + if ( ! CutByTriangles( plPlane, bSaveOnEq, bModif)) + return false ; + } + + // se effettuate modifiche + if ( bModif) { + // aggiorno tutto + if ( ! AdjustVertices() || ! DoCompacting()) + return false ; + } + + // se superficie originale a facce, cerco di semplificarle in ogni caso + if ( nFacetOriCnt < 200 || double( nTriaOriCnt) / nFacetOriCnt > 4) { + if ( ! SimplifyFacets( 500.0)) + LOG_ERROR( GetEGkLogger(), "Error in SimplifyFacets of Stm::Cut") + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::CutByTriangles( const Plane3d& plPlane, bool bSaveOnEq, bool& bModif) +{ + // la superficie deve essere validata + if ( m_nStatus != OK) + return false ; + + // classifico i vertici rispetto al piano + double dTol = max( min( 0.5 * m_dLinTol, 10 * EPS_SMALL), EPS_SMALL) ; + for ( int i = 0 ; i < GetVertexSize() ; ++ i) { + double dDist = DistPointPlane( m_vVert[i].ptP, plPlane) ; + if ( abs( dDist) < dTol) { + m_vVert[i].nTemp = 0 ; + if ( abs( dDist) > EPS_SMALL) + m_vVert[i].ptP -= plPlane.GetVersN() * dDist ; + } + else if ( dDist > 0) + m_vVert[i].nTemp = +1 ; + else + m_vVert[i].nTemp = -1 ; + } + + // aggiorno timestamp dei triangoli + for ( auto& Tria : m_vTria) + Tria.nTemp = m_nTimeStamp ; + ++ m_nTimeStamp ; + + // sistemo i triangoli (eventualmente li elimino) + for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { + // salto i triangoli cancellati e quelli aggiunti + if ( m_vTria[i].nIdVert[0] == SVT_DEL || m_vTria[i].nTemp == m_nTimeStamp) + continue ; + // se flag abilita e giace sul piano ed equiverso, lo salvo + if ( bSaveOnEq && + m_vVert[ m_vTria[i].nIdVert[0]].nTemp == 0 && + m_vVert[ m_vTria[i].nIdVert[1]].nTemp == 0 && + m_vVert[ m_vTria[i].nIdVert[2]].nTemp == 0 && + AreSameVectorApprox( m_vTria[i].vtN, plPlane.GetVersN())) + ; + // se giace sul piano o dalla parte esterna del piano, lo cancello + else if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp != -1 && + m_vVert[ m_vTria[i].nIdVert[1]].nTemp != -1 && + m_vVert[ m_vTria[i].nIdVert[2]].nTemp != -1) { + bModif = true ; + RemoveTriangle( i) ; + } + // se giace dalla parte interna con al massimo un lato sul piano, lo conservo + else if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp != 1 && + m_vVert[ m_vTria[i].nIdVert[1]].nTemp != 1 && + m_vVert[ m_vTria[i].nIdVert[2]].nTemp != 1) + ; + // altrimenti attraversa il piano, devo modificarlo + else { + bModif = true ; + int nDisc = m_vVert[ m_vTria[i].nIdVert[0]].nTemp + + m_vVert[ m_vTria[i].nIdVert[1]].nTemp + + m_vVert[ m_vTria[i].nIdVert[2]].nTemp ; + // se ha un vertice all'interno e uno sul piano -> 1 nuovo vertice e 1 nuovo triangolo + if ( nDisc == 0) { + // classifico i vertici + int nVertOn, nVertIn, nVertOut ; + if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp == 0) { + nVertOn = 0 ; + nVertIn = 1 ; + nVertOut = 2 ; + if ( m_vVert[ m_vTria[i].nIdVert[1]].nTemp > 0) + swap( nVertIn, nVertOut) ; + } + else if ( m_vVert[ m_vTria[i].nIdVert[1]].nTemp == 0) { + nVertOn = 1 ; + nVertIn = 2 ; + nVertOut = 0 ; + if ( m_vVert[ m_vTria[i].nIdVert[2]].nTemp > 0) + swap( nVertIn, nVertOut) ; + } + else { + nVertOn = 2 ; + nVertIn = 0 ; + nVertOut = 1 ; + if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp > 0) + swap( nVertIn, nVertOut) ; + } + // calcolo il punto di intersezione del lato che attraversa il piano + double dDistIn = - DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertIn]].ptP, plPlane) ; + double dDistOut = DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertOut]].ptP, plPlane) ; + Point3d ptInt = Media( m_vVert[m_vTria[i].nIdVert[nVertIn]].ptP, + m_vVert[m_vTria[i].nIdVert[nVertOut]].ptP, + dDistIn / ( dDistIn + dDistOut)) ; + // inserisco il nuovo vertice + int nNewV = AddVertex( ptInt) ; + // inserisco il nuovo triangolo + int nIdVert[3] = { m_vTria[i].nIdVert[0], m_vTria[i].nIdVert[1], m_vTria[i].nIdVert[2]} ; + nIdVert[nVertOut] = nNewV ; + int nIdTria = AddTriangle( nIdVert) ; + if ( nIdTria == SVT_NULL) + return false ; + if ( nIdTria != SVT_DEL) + m_vTria[nIdTria].nTemp = m_nTimeStamp ; + // cancello il vecchio triangolo + RemoveTriangle( i) ; + } + // se ha un vertice all'interno e due all'esterno -> 2 nuovi vertici e 1 nuovo triangolo + else if ( nDisc == 1) { + // classifico i vertici + int nVertIn, nVertOut1, nVertOut2 ; + if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp == -1) { + nVertIn = 0 ; + nVertOut1 = 1 ; + nVertOut2 = 2 ; + } + else if ( m_vVert[ m_vTria[i].nIdVert[1]].nTemp == -1) { + nVertIn = 1 ; + nVertOut1 = 2 ; + nVertOut2 = 0 ; + } + else { + nVertIn = 2 ; + nVertOut1 = 0 ; + nVertOut2 = 1 ; + } + // calcolo i punti di intersezione dei lati che attraversano il piano + double dDistIn = - DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertIn]].ptP, plPlane) ; + double dDistOut1 = DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertOut1]].ptP, plPlane) ; + double dDistOut2 = DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertOut2]].ptP, plPlane) ; + Point3d ptInt1 = Media( m_vVert[m_vTria[i].nIdVert[nVertIn]].ptP, + m_vVert[m_vTria[i].nIdVert[nVertOut1]].ptP, + dDistIn / ( dDistIn + dDistOut1)) ; + Point3d ptInt2 = Media( m_vVert[m_vTria[i].nIdVert[nVertIn]].ptP, + m_vVert[m_vTria[i].nIdVert[nVertOut2]].ptP, + dDistIn / ( dDistIn + dDistOut2)) ; + // inserisco i nuovi vertici + int nNewV1 = AddVertex( ptInt1) ; + int nNewV2 = AddVertex( ptInt2) ; + // inserisco il nuovo triangolo + int nIdVert[3] = { m_vTria[i].nIdVert[0], m_vTria[i].nIdVert[1], m_vTria[i].nIdVert[2]} ; + nIdVert[nVertOut1] = nNewV1 ; + nIdVert[nVertOut2] = nNewV2 ; + int nIdTria = AddTriangle( nIdVert) ; + if ( nIdTria == SVT_NULL) + return false ; + if ( nIdTria != SVT_DEL) + m_vTria[nIdTria].nTemp = m_nTimeStamp ; + // cancello il vecchio triangolo + RemoveTriangle( i) ; + } + // altrimenti ha due vertici all'interno e uno all'esterno -> 2 nuovi vertici e 2 nuovi triangoli + else { + // classifico i vertici + int nVertIn1, nVertIn2, nVertOut ; + if ( m_vVert[ m_vTria[i].nIdVert[0]].nTemp == 1) { + nVertOut = 0 ; + nVertIn1 = 1 ; + nVertIn2 = 2 ; + } + else if ( m_vVert[ m_vTria[i].nIdVert[1]].nTemp == 1) { + nVertOut = 1 ; + nVertIn1 = 2 ; + nVertIn2 = 0 ; + } + else { + nVertOut = 2 ; + nVertIn1 = 0 ; + nVertIn2 = 1 ; + } + // calcolo i punti di intersezione dei lati che attraversano il piano + double dDistIn1 = - DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertIn1]].ptP, plPlane) ; + double dDistIn2 = - DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertIn2]].ptP, plPlane) ; + double dDistOut = DistPointPlane( m_vVert[m_vTria[i].nIdVert[nVertOut]].ptP, plPlane) ; + Point3d ptInt1 = Media( m_vVert[m_vTria[i].nIdVert[nVertIn1]].ptP, + m_vVert[m_vTria[i].nIdVert[nVertOut]].ptP, + dDistIn1 / ( dDistIn1 + dDistOut)) ; + Point3d ptInt2 = Media( m_vVert[m_vTria[i].nIdVert[nVertIn2]].ptP, + m_vVert[m_vTria[i].nIdVert[nVertOut]].ptP, + dDistIn2 / ( dDistIn2 + dDistOut)) ; + // inserisco i nuovi vertici + int nNewV1 = AddVertex( ptInt1) ; + int nNewV2 = AddVertex( ptInt2) ; + // inserisco i nuovi triangoli + int nIdVert1[3] = { m_vTria[i].nIdVert[0], m_vTria[i].nIdVert[1], m_vTria[i].nIdVert[2]} ; + int nIdVert2[3] = { m_vTria[i].nIdVert[0], m_vTria[i].nIdVert[1], m_vTria[i].nIdVert[2]} ; + double dSqDist1 = SqDist( m_vVert[m_vTria[i].nIdVert[nVertIn1]].ptP, ptInt2) ; + double dSqDist2 = SqDist( m_vVert[m_vTria[i].nIdVert[nVertIn2]].ptP, ptInt1) ; + if ( dSqDist1 <= dSqDist2) { + nIdVert1[nVertOut] = nNewV1 ; + nIdVert1[nVertIn2] = nNewV2 ; + nIdVert2[nVertOut] = nNewV2 ; + } + else { + nIdVert1[nVertOut] = nNewV1 ; + nIdVert2[nVertOut] = nNewV2 ; + nIdVert2[nVertIn1] = nNewV1 ; + } + int nIdTria1 = AddTriangle( nIdVert1) ; + int nIdTria2 = AddTriangle( nIdVert2) ; + if ( nIdTria1 == SVT_NULL || nIdTria2 == SVT_NULL) + return false ; + if ( nIdTria1 != SVT_DEL) + m_vTria[nIdTria1].nTemp = m_nTimeStamp ; + if ( nIdTria2 != SVT_DEL) + m_vTria[nIdTria2].nTemp = m_nTimeStamp ; + // cancello il vecchio triangolo + RemoveTriangle( i) ; + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::CutByFacets( const Plane3d& plPlane, bool bSaveOnEq, bool& bModif) +{ + // Setto posizione triangoli non definita + for ( int nT = 0 ; nT < GetTriangleSize() ; ++ nT) + m_vTria[nT].nTempPart = 0 ; + + INTERSCHAINMAP IntersLineMap ; + + // Ciclo su tutte le facce. + for ( int nF = 0 ; nF < GetFacetCount() ; ++ nF) { + // Dati della faccia + POLYLINEVECTOR vLoopVec ; + GetFacetLoops( nF, vLoopVec) ; + if ( vLoopVec.empty()) + continue ; + // Verifico la posizione del loop esterno + int nIntType = VerifyLoopPlane( vLoopVec[0], plPlane) ; + // Se interseca il piano di taglio, calcolo la divisione della faccia + LineFacetClassVector IntersLinePart ; + if ( nIntType == FPI_CUT) { + PtrOwner pReg( GetBasicSurfFlatRegion( GetSurfFlatRegionFromPolyLineVector( vLoopVec))) ; + if ( IsNull( pReg) || ! pReg->IsValid()) + return false ; + nIntType = IntersFacetPlane( *pReg, plPlane, IntersLinePart) ; + } + // Gestione dei risultati + if ( nIntType == FPI_CUT) { + for ( int nPart = 0 ; nPart < int( IntersLinePart.size()) ; ++ nPart) { + // Salvo intersezione per la faccia. + auto it = IntersLineMap.find( nF) ; + if ( it != IntersLineMap.end()) { + it->second.emplace_back( IntersInnSeg( IntersLinePart[nPart].ptSt, IntersLinePart[nPart].ptEn)) ; + } + else { + IntersLineMap.emplace( nF, IntersInnChain( 1, IntersInnSeg( IntersLinePart[nPart].ptSt, IntersLinePart[nPart].ptEn))) ; + } + } + } + else if ( nIntType == FPI_ON) { + INTVECTOR vT ; + GetAllTriaInFacet( nF, vT) ; + Vector3d vtNf ; + GetFacetNormal( nF, vtNf) ; + for ( auto& nT : vT) + m_vTria[nT].nTempPart = ( vtNf * plPlane.GetVersN() > 0 ? 2 : -2) ; + } + else if ( nIntType == FPI_IN) { + INTVECTOR vT ; + GetAllTriaInFacet( nF, vT) ; + for ( auto& nT : vT) + m_vTria[nT].nTempPart = 1 ; + } + else if ( nIntType == FPI_OUT) { + INTVECTOR vT ; + GetAllTriaInFacet( nF, vT) ; + for ( auto& nT : vT) + m_vTria[nT].nTempPart = -1 ; + } + else + return false ; + } + + // Divido le facce. + PieceMap NewFacet ; + SplitFacet( IntersLineMap, NewFacet) ; + INTERSEDGEMAP EdgeInterLineMap, EdgeEdgeLineMap ; + RetriangulateFacetPieces( NewFacet, EdgeInterLineMap, EdgeEdgeLineMap) ; + + // Elimino i triangoli superflui + for ( int nT = 0 ; nT < GetTriangleSize() ; ++ nT) { + if ( m_vTria[nT].nTempPart == 0 || + m_vTria[nT].nTempPart == -1 || + m_vTria[nT].nTempPart == -2 || + ( ! bSaveOnEq && m_vTria[nT].nTempPart == 2)) { + RemoveTriangle( nT) ; + bModif = true ; + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +// Risultato : 0=nessuna intersezione, 1=intersezione è un punto, 2=intersezione è un segmento +static int +IntersRectangleTriangle( const Point3d& ptP, const Vector3d& vtL1, const Vector3d& vtL2, + const Triangle3d& trTria, Point3d& ptStSeg, Point3d& ptEnSeg) +{ + // Assegno tolleranza lineare + double dTol = EPS_SMALL ; + // Definisco il piano del rettangolo + Plane3d plRectanglePlane ; + if ( ! plRectanglePlane.Set( ptP, vtL1 ^ vtL2)) + return -1 ; + // Interseco il piano con il triangolo (recupero estremi in ordine inverso per mantenere l'esterno a destra nei loop) + int nPlTrIntRes = IntersPlaneTria( plRectanglePlane, trTria, ptEnSeg, ptStSeg) ; + if ( nPlTrIntRes != IntPlaneTriaType::IPTT_YES) + return 0 ; + // Limito il segmento col rettangolo + Vector3d vtSegDir = ptEnSeg - ptStSeg ; + double dSegLen = vtSegDir.Len() ; + vtSegDir /= dSegLen ; + // utilizzo piani ortogonali al rettangolo passanti per i suoi estremi + Plane3d plTrim1 ; plTrim1.Set( ptP, -vtL1) ; + Plane3d plTrim2 ; plTrim2.Set( ptP + vtL1, vtL1) ; + if ( vtSegDir * vtL1 < 0.) + swap( plTrim1, plTrim2) ; + Point3d ptInt1 ; + int nLnPl1IntRes = IntersLinePlane( ptStSeg, vtSegDir, dSegLen, plTrim1, ptInt1, false) ; + Point3d ptInt2 ; + int nLnPl2IntRes = IntersLinePlane( ptStSeg, vtSegDir, dSegLen, plTrim2, ptInt2, false) ; + // Se il segmento giace in uno dei due piani + if ( ( nLnPl1IntRes == ILPT_NO && nLnPl2IntRes == ILPT_INPLANE) || + ( nLnPl2IntRes == ILPT_NO && nLnPl1IntRes == ILPT_INPLANE)) + return ( AreSamePointEpsilon( ptStSeg, ptEnSeg, dTol) ? 1 : 2) ; + // Se non ci sono intersezioni + if ( nLnPl1IntRes == ILPT_NO && nLnPl2IntRes == ILPT_NO) { + // se è tra i due piani + if (( ptStSeg - plTrim1.GetPoint()) * plTrim1.GetVersN() < 0. && + ( ptStSeg - plTrim2.GetPoint()) * plTrim2.GetVersN() < 0.) + return ( AreSamePointEpsilon( ptStSeg, ptEnSeg, dTol) ? 1 : 2) ; + // altrimenti è esterno + else + return 0 ; + } + // Posizioni parametriche delle intersezioni sul segmento + double dLen1 = ( ptInt1 - ptStSeg) * vtSegDir ; + double dLen2 = ( ptInt2 - ptStSeg) * vtSegDir ; + // Se la prima intersezione supera la fine o la seconda viene prima dell'inizio, non ci sono intersezioni + if ( dLen1 > dSegLen + EPS_ZERO || dLen2 < -EPS_ZERO) + return 0 ; + // Eventuale aggiustamento inizio + if ( dLen1 > 0.) { + ptStSeg = ptInt1 ; + dSegLen -= dLen1 ; + dLen2 -= dLen1 ; + } + // Eventuale aggiustamento fine + if ( dLen2 < dSegLen) { + ptEnSeg = ptInt2 ; + dSegLen = dLen2 ; + } + // Assegno il tipo di intersezione + return ( dSegLen > dTol ? 2 : 1) ; +} + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::GeneralizedCut( const ICurve& cvCurve, bool bSaveOnEq) +{ + // La superficie deve essere valida + if ( m_nStatus != OK) + return false ; + // La curva deve essere valida e chiusa, il vettore estrusione deve essere non nullo + Vector3d vtExtr ; + if ( ! cvCurve.GetExtrusion( vtExtr) || vtExtr.IsSmall() || ! cvCurve.IsClosed()) + return false ; + + // Recupero il numero originale di triangoli e di facce + int nTriaOriCnt = GetTriangleCount() ; + int nFacetOriCnt = GetFacetCount() ; + + // Approssimo la curva con segmenti + CurveComposite cvCompo ; + { + PolyLine PL ; + if ( ! cvCurve.ApproxWithLines( LIN_TOL_FINE, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) || ! cvCompo.FromPolyLine( PL)) + return false ; + } + + // Eseguo scalature + Frame3d frScalingRef; + frScalingRef.Set( m_vVert[0].ptP, X_AX, Y_AX, Z_AX) ; + Scale( frScalingRef, CUT_SCALE, CUT_SCALE, CUT_SCALE) ; + cvCompo.Scale( frScalingRef, CUT_SCALE, CUT_SCALE, CUT_SCALE) ; + + // Appiattisco la polilinea nel piano perpendicolare all'estrusione + Frame3d frCurve ; + Point3d ptStart ; cvCompo.GetStartPoint( ptStart) ; + frCurve.Set( ptStart, vtExtr) ; + cvCompo.ToLoc( frCurve) ; + if ( ! cvCompo.Scale( GLOB_FRM, 1, 1, 0)) { + Scale( frScalingRef, 1. / CUT_SCALE, 1. / CUT_SCALE, 1. / CUT_SCALE) ; + return false ; + } + double dArea ; + cvCompo.GetAreaXY( dArea) ; + BBox3d b3Crv ; + cvCompo.GetLocalBBox( b3Crv) ; + cvCompo.ToGlob( frCurve) ; + // Assegno il senso di rotazione della curva (visto dalla punta del vettore estrusione) + bool bCCW = ( dArea > 0) ; + + // Recupero Bounding-box della trimesh + BBox3d b3SurfBox ; + GetLocalBBox( b3SurfBox) ; + // Trovo minima e massima distanza dei vertici del bounding-box della TriMesh dal piano della curva + b3SurfBox.ToLoc( frCurve) ; + Point3d ptMin, ptMax ; + b3SurfBox.GetMinMax( ptMin, ptMax) ; + Vector3d vtMax = ( ptMax.z + 10) * vtExtr ; + Vector3d vtMin = ( ptMin.z - 10) * vtExtr ; + + // Ciclo sui triangoli + bool bModif = false ; + int nNumTria = GetTriangleSize() ; + for ( int nT = 0 ; nT < nNumTria ; ++ nT) { + // Recupero il triangolo + Triangle3d trTria ; + if ( ! GetTriangle( nT, trTria)) + continue ; + // Box del triangolo nel riferimento locale della curva + BBox3d b3Tria ; + trTria.GetLocalBBox( b3Tria) ; + b3Tria.ToLoc( frCurve) ; + // Se il box del triangolo non interseca quello locale della curva + if ( ! b3Crv.OverlapsXY( b3Tria)) { + // Se la parte da conservare è quella all'interno della curva, elimino il triangolo + if ( bCCW) { + RemoveTriangle( nT) ; + bModif = true ; + } + continue ; + } + // Determino se il centro del triangolo cade all'interno della curva + bool bTriaCenIn = false ; + { + DistPointCurve dstPC( trTria.GetCentroid(), cvCompo) ; + int nSide ; + if ( dstPC.GetSideAtMinDistPoint( 0, vtExtr, nSide)) + bTriaCenIn = ( nSide == MDS_LEFT) ; + } + + // Vettore di catene di punti + CHAINVECTOR vChain ; + // Ciclo sui segmenti + int nChainCnt = 0 ; + bool bChain = false ; + Point3d ptChSt, ptChEn ; + const ICurve* pCrv = cvCompo.GetFirstCurve() ; + while ( pCrv != nullptr) { + // estremi del segmento + Point3d ptSt ; pCrv->GetStartPoint( ptSt) ; + Point3d ptEn ; pCrv->GetEndPoint( ptEn) ; + // Intersezione fra il rettangolo (ottenuto dall'estrusione del segmento corrente) e il triangolo + Point3d ptSegSt, ptSegEn ; + int nInt = IntersRectangleTriangle( ptSt + vtMin, ptEn - ptSt, vtMax - vtMin, trTria, ptSegSt, ptSegEn) ; + if ( nInt == 2) { + // Creo nuova catena se non c'è già o se discontinuità + if ( ! bChain || ( ! AreSamePointApprox( ptSegSt, ptChEn) && ! AreSamePointApprox( ptSegEn, ptChSt))) { + ++ nChainCnt ; + vChain.resize( nChainCnt) ; + bChain = false ; + } + // Assegno i dati di intersezione + IntSegment CurInters ; + CurInters.ptSt = ptSegSt ; + CurInters.ptEn = ptSegEn ; + // Inserisco nella catena + if ( ! bChain) { + vChain[nChainCnt - 1].emplace_back( CurInters) ; + ptChSt = CurInters.ptSt ; + ptChEn = CurInters.ptEn ; + } + else if ( AreSamePointApprox(ptSegSt, ptChEn)) { + vChain[nChainCnt - 1].emplace_back(CurInters) ; + ptChEn = CurInters.ptEn ; + } + else { + vChain[nChainCnt - 1].insert(vChain[nChainCnt - 1].begin(), CurInters) ; + ptChSt = CurInters.ptSt ; + } + bChain = true ; + } + else { + bChain = false ; + } + pCrv = cvCompo.GetNextCurve() ; + } + + for ( auto itI = vChain.begin() ; itI != vChain.end() ; ) { + bool bErased = false ; + auto itJ = itI ; + ++ itJ ; + for ( ; itJ != vChain.end() && ! bErased ; ) { + if ( int( itI->size()) == 1 && int( itJ->size()) == 1 && + AreSamePointEpsilon( itI->back().ptSt, itJ->back().ptEn, 2 * EPS_SMALL) && + AreSamePointEpsilon( itI->back().ptEn, itJ->back().ptSt, 2 * EPS_SMALL)) { + itJ = vChain.erase( itJ) ; + bErased = true ; + } + else + ++ itJ ; + } + if ( bErased) { + itI = vChain.erase( itI) ; + bErased = false ; + } + else + ++ itI ; + } + + // unisco eventuali catene estreme che sono parte di una stessa catena + if ( nChainCnt > 1) { + if ( AreSamePointApprox( vChain[0].front().ptSt, vChain[nChainCnt - 1].back().ptEn)) { + vChain[0].insert( vChain[0].begin(), vChain[nChainCnt - 1].begin(), vChain[nChainCnt - 1].end()) ; + vChain.pop_back() ; + -- nChainCnt ; + } + else if ( AreSamePointApprox( vChain[0].back().ptEn, vChain[nChainCnt - 1].front().ptSt)) { + vChain[0].insert(vChain[0].end(), vChain[nChainCnt - 1].begin(), vChain[nChainCnt - 1].end()) ; + vChain.pop_back() ; + -- nChainCnt ; + } + } + + // Elimino la seconda copia di catene doppie + for ( int nI = 0 ; nI < nChainCnt ; ++ nI) { + for ( int nJ = nI + 1 ; nJ < nChainCnt ; ++ nJ) { + if ( vChain[nI].size() == vChain[nJ].size()) { + bool bSame = true ; + for ( int nK = 0 ; nK < int( vChain[nI].size()) ; ++ nK) { + if ( ! AreSamePointApprox( vChain[nI][nK].ptSt, vChain[nJ][nK].ptSt) || + ! AreSamePointApprox( vChain[nI][nK].ptEn, vChain[nJ][nK].ptEn)) { + bSame = false ; + break ; + } + } + if ( bSame) { + vChain.erase( vChain.begin() + nJ) ; + -- nChainCnt ; + -- nJ ; + } + } + } + } + + // Fra le catene trovate separo le aperte dalle chiuse + CHAINVECTOR cvClosedChain ; + CHAINVECTOR cvOpenChain ; + for ( int nL = 0 ; nL < int( vChain.size()) ; ++ nL) { + int nCurLoopLast = max( int(vChain[nL].size()) - 1, 0) ; + if ( AreSamePointApprox( vChain[nL][0].ptSt, vChain[nL][nCurLoopLast].ptEn) && nCurLoopLast > 0) + cvClosedChain.emplace_back( vChain[nL]) ; + else { + cvOpenChain.emplace_back( vChain[nL]) ; + } + } + for ( auto it = cvClosedChain.begin() ; it != cvClosedChain.end() ; ) { + if ( int( it->size()) < 3) + it = cvClosedChain.erase( it) ; + else + ++ it ; + } + // Se più di una catena chiusa oppure catene chiuse e aperte, errore + if ( cvClosedChain.size() > 1 || ( cvClosedChain.size() > 0 && int( cvOpenChain.size()) > 0)) { + Scale( frScalingRef, 1. / CUT_SCALE, 1. / CUT_SCALE, 1. / CUT_SCALE) ; + return false ; + } + + // Se c'è una catena chiusa + if ( cvClosedChain.size() == 1) { + // Ne ricavo una PolyLine + PolyLine plInLoop ; + for ( int nLine = 0 ; nLine < int( cvClosedChain[0].size()) ; ++ nLine) { + plInLoop.AddUPoint( 0., cvClosedChain[0][nLine].ptSt) ; + plInLoop.AddUPoint( 0., cvClosedChain[0][nLine].ptEn) ; + } + // I tre vertici sono dalla parte interna della curva (triangolo con buco) + if ( ! bCCW) { + // Rimuovo il triangolo corrente + RemoveTriangle( nT) ; + // Definisco il loop esterno (è il triangolo) + PolyLine plExtLoop ; + plExtLoop.AddUPoint( 0., trTria.GetP( 0)) ; + plExtLoop.AddUPoint( 0., trTria.GetP( 1)) ; + plExtLoop.AddUPoint( 0., trTria.GetP( 2)) ; + plExtLoop.AddUPoint( 0., trTria.GetP( 0)) ; + // Eseguo triangolazione + POLYLINEVECTOR vPL ; + vPL.emplace_back( plExtLoop) ; + vPL.emplace_back( plInLoop) ; + PNTVECTOR vPt ; + INTVECTOR vTr ; + if ( Triangulate().Make( vPL, vPt, vTr)) { + // Inserisco i nuovi triangoli + for ( int n = 0 ; n < int( vTr.size()) - 2 ; n += 3) { + int nNewTriaVertId[3] = { vTr[n], vTr[n + 1], vTr[n + 2] } ; + int nNewId[3] = { AddVertex( vPt[nNewTriaVertId[0]]), + AddVertex( vPt[nNewTriaVertId[1]]), + AddVertex( vPt[nNewTriaVertId[2]]) } ; + AddTriangle( nNewId) ; + bModif = true ; + } + } + } + // Se nessun vertice dalla parte interna della curva (rimane solo l'area della curva) + else { + // Rimuovo il triangolo corrente + RemoveTriangle( nT) ; + // Eseguo triangolazione + PNTVECTOR vPt ; + INTVECTOR vTr ; + if ( Triangulate().Make( plInLoop, vPt, vTr)) { + // Inserisco i nuovi triangoli + for ( int n = 0 ; n < int( vTr.size()) - 2 ; n += 3) { + int nNewTriaVertId[3] = { vTr[n], vTr[n + 1], vTr[n + 2] } ; + int nNewId[3] = { AddVertex(vPt[nNewTriaVertId[0]]), + AddVertex(vPt[nNewTriaVertId[1]]), + AddVertex(vPt[nNewTriaVertId[2]]) } ; + AddTriangle( nNewId) ; + bModif = true ; + } + } + } + } + + // Loop aperti, devo chiuderli + else if ( cvOpenChain.size() > 0) { + // Creo il loop chiuso padre di tutti, il perimetro del triangolo. + // Questo viene diviso in sotto-loop chiusi mediante quelli aperti. + // I loop chiusi trovati precedentemente sono interni a uno dei sotto-loop + // chiusi di cui è formato il perimetro. + PNTVECTOR cvFirstLoop ; + cvFirstLoop.emplace_back( trTria.GetP( 0)) ; + cvFirstLoop.emplace_back( trTria.GetP( 1)) ; + cvFirstLoop.emplace_back( trTria.GetP( 2)) ; + + PNTMATRIX cvBoundClosedLoopVec ; + cvBoundClosedLoopVec.emplace_back( cvFirstLoop) ; + BOOLVECTOR vbInOut ; + vbInOut.push_back( true) ; + // Divido il loop di partenza in sotto-loop + while ( cvOpenChain.size() > 0) { + int nLastOpenLoopN = int( cvOpenChain.size()) - 1 ; + for ( int nLoop = 0 ; nLoop < int( cvBoundClosedLoopVec.size()) ; ++ nLoop) { + // Estremi del loop aperto + int nLastOpenLoopPoint = max( int( cvOpenChain[nLastOpenLoopN].size()) - 1, 0) ; + Point3d ptOpenLoopStP = cvOpenChain[nLastOpenLoopN][0].ptSt ; + Point3d ptOpenLoopEnP = cvOpenChain[nLastOpenLoopN][nLastOpenLoopPoint].ptEn ; + PNTVECTOR Loop1, Loop2 ; + bool bChangedStart = ChangeStart( ptOpenLoopStP, cvBoundClosedLoopVec[nLoop]) ; + bool bSplitted = SplitAtPoint( ptOpenLoopEnP, cvBoundClosedLoopVec[nLoop], Loop1, Loop2) ; + if ( ! ( bChangedStart && bSplitted)) + continue ; + Chain cvCounterChain ; + for ( int nPt = int( cvOpenChain[nLastOpenLoopN].size()) - 1 ; nPt >= 0 ; -- nPt) { + IntSegment CurSeg ; + CurSeg.ptSt = cvOpenChain[nLastOpenLoopN][nPt].ptEn ; + CurSeg.ptEn = cvOpenChain[nLastOpenLoopN][nPt].ptSt ; + cvCounterChain.emplace_back( CurSeg) ; + } + bool bAdded1 = AddChainToChain( cvCounterChain, Loop1) ; + bool bAdded2 = AddChainToChain( cvOpenChain[nLastOpenLoopN], Loop2) ; + if ( ! ( bAdded1 && bAdded2)) + continue ; + // Aggiungo i nuovi loop nel vettore + int nCurSize = int( cvBoundClosedLoopVec.size()) ; + cvBoundClosedLoopVec.resize( nCurSize + 1) ; + vbInOut.resize( nCurSize + 1) ; + for ( int nCL = nCurSize - 1 ; nCL > nLoop ; -- nCL) { + cvBoundClosedLoopVec[nCL + 1] = cvBoundClosedLoopVec[nCL] ; + vbInOut[nCL + 1] = vbInOut[nCL] ; + } + cvBoundClosedLoopVec[nLoop] = Loop1 ; + cvBoundClosedLoopVec[nLoop + 1] = Loop2 ; + vbInOut[nLoop] = false ; + vbInOut[nLoop + 1] = true ; + ++ nLoop ; + } + cvOpenChain.resize( nLastOpenLoopN) ; + } + // Rimuovo il triangolo corrente + RemoveTriangle( nT) ; + // Trasformo i loop compositi in loop polyline + POLYLINEVECTOR vplPolyVec ; + vplPolyVec.resize(cvBoundClosedLoopVec.size()) ; + for ( int nLoop = 0 ; nLoop < int( vplPolyVec.size()) ; ++ nLoop) { + for ( int nLine = 0 ; nLine < int( cvBoundClosedLoopVec[nLoop].size()) ; ++ nLine) { + vplPolyVec[nLoop].AddUPoint( 0., cvBoundClosedLoopVec[nLoop][nLine]) ; + } + vplPolyVec[nLoop].AddUPoint( 0., cvBoundClosedLoopVec[nLoop][0]) ; + if ( vbInOut[nLoop]) { + // Eseguo triangolazione + Triangulate CreateTriangulation ; + PNTVECTOR vPt ; + INTVECTOR vTr ; + if ( Triangulate().Make( vplPolyVec[nLoop], vPt, vTr)) { + // Inserisco i nuovi triangoli + for ( int n = 0 ; n < int( vTr.size()) - 2 ; n += 3) { + int nNewTriaVertId[3] = { vTr[n], vTr[n + 1], vTr[n + 2] } ; + int nNewId[3] = { AddVertex(vPt[nNewTriaVertId[0]]), + AddVertex(vPt[nNewTriaVertId[1]]), + AddVertex(vPt[nNewTriaVertId[2]]) } ; + AddTriangle( nNewId) ; + bModif = true ; + } + } + } + } + } + + // da eliminare + else if ( ! bTriaCenIn) { + RemoveTriangle( nT) ; + bModif = true ; + } + } + + // Se avvenuta modifica, aggiorno tutto + if ( bModif) { + // aggiorno tutto + if ( ! AdjustVertices() || ! DoCompacting()) { + Scale( frScalingRef, 1. / CUT_SCALE, 1. / CUT_SCALE, 1. / CUT_SCALE) ; + return false ; + } + } + + // Ripristino scala originale + Scale( frScalingRef, 1. / CUT_SCALE, 1. / CUT_SCALE, 1. / CUT_SCALE) ; + + // se superficie originale a facce, cerco di semplificarle in ogni caso + if ( nFacetOriCnt < 200 || double( nTriaOriCnt) / nFacetOriCnt > 4) { + if ( ! SimplifyFacets( 500.0)) + LOG_ERROR( GetEGkLogger(), "Error in SimplifyFacets of Stm::GeneralizedCut") + } + + return true ; +} diff --git a/SurfTriMeshUtilities.cpp b/SurfTriMeshUtilities.cpp index eb88e7d..8aeeea2 100644 --- a/SurfTriMeshUtilities.cpp +++ b/SurfTriMeshUtilities.cpp @@ -248,3 +248,192 @@ SurfTriMesh::SimplifyFacets( double dMaxEdgeLen) // Eseguo aggiustamenti return ( AdjustVertices() && DoCompacting()) ; } + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::AddChainToChain( const Chain& ChainToAdd, PNTVECTOR& OrigChain) +{ + // Se la catena da aggiungere è vuota, non devo fare alcunchè + if ( ChainToAdd.size() == 0) + return true ; + // Se la catena originale è vuota, non è possibile aggiungere nulla + if ( OrigChain.size() == 0) + return false ; + // Se la catena originale è chiusa non posso aggiungere nulla + int nLastOrig = max( int( OrigChain.size()) - 1, 0) ; + if ( AreSamePointApprox( OrigChain[0], OrigChain[nLastOrig])) + return false ; + int nLastToAdd = max( int( ChainToAdd.size()) - 1, 0) ; + if ( AreSamePointApprox( OrigChain[nLastOrig], ChainToAdd[0].ptSt)) { + for ( int nPt = 1 ; nPt <= nLastToAdd ; ++ nPt) { + if ( nPt == nLastToAdd) { + if ( ! AreSamePointApprox(OrigChain[0], ChainToAdd[nPt].ptSt)) + OrigChain.emplace_back( ChainToAdd[nPt].ptSt) ; + } + else if ( nPt == 1) { + if ( ! AreSamePointApprox( OrigChain[nLastOrig], ChainToAdd[nPt].ptSt)) + OrigChain.emplace_back( ChainToAdd[nPt].ptSt) ; + } + else + OrigChain.emplace_back( ChainToAdd[nPt].ptSt) ; + } + return true ; + } + else + return false ; +} + +//---------------------------------------------------------------------------- +// Una faccia di una trimesh ha una sola componente connessa, con un loop esterno e possibili loop interni +bool +SurfTriMesh::DistPointFacet( const Point3d& ptP, const POLYLINEVECTOR& vPolyVec, double& dPointFacetDist) +{ + // Verifico la presenza del loop esterno + if ( vPolyVec.size() < 1) + return false ; + // Proietto il punto sul piano della faccia, utilizzando il loop esterno + Plane3d plPlane ; + double dArea ; + if ( ! vPolyVec[0].IsClosedAndFlat( plPlane, dArea)) + return false ; + double dDistPtPl = DistPointPlane( ptP, plPlane) ; + Point3d ptProjP = ptP + dDistPtPl * plPlane.GetVersN() ; + // Verifico se il punto proiettato è esterno al loop esterno + int nPtOut = -1 ; + if ( ! IsPointInsidePolyLine( ptProjP, vPolyVec[0], EPS_SMALL)) + nPtOut = 0 ; + // Verifico se il punto proiettato è interno ai loop interni (quindi esterno alla faccia) + for ( int nLoop = 1 ; nLoop < int( vPolyVec.size()) && nPtOut < 0 ; ++ nLoop) { + Plane3d plPlane ; + double dArea ; + if ( ! vPolyVec[nLoop].IsClosedAndFlat( plPlane, dArea)) + return false ; + if ( IsPointInsidePolyLine( ptProjP, vPolyVec[nLoop], EPS_SMALL)) + nPtOut = nLoop ; + } + // Se il punto si proietta sulla faccia, la distanza dalla faccia coincide con quella dal piano + if ( nPtOut < 0) { + dPointFacetDist = abs( dDistPtPl) ; + return true ; + } + // Altrimenti calcolo la minima distanza del punto dalla polilinea del contorno a cui è esterno + double dDist ; + if ( DistPointPolyLine( ptP, vPolyVec[nPtOut], dDist)) { + dPointFacetDist = dDist ; + return true ; + } + return false ; +} + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::ChangeStart( const Point3d& ptNewStart, PNTVECTOR& Loop) +{ + // Cerco il tratto del loop chiuso più vicino al punto + int nMinSeg = - 1 ; + double dMinSqDinst = DBL_MAX ; + for ( int nPt = 0 ; nPt < int( Loop.size()) ; ++ nPt) { + // Estremi del segmento corrente del loop + Point3d ptSegSt = Loop[nPt] ; + Point3d ptSegEn = Loop[( nPt + 1) % int( Loop.size())] ; + // Distanza del punto dal segmento del loop + DistPointLine dDistCalc( ptNewStart, ptSegSt, ptSegEn) ; + double dSqDist ; + dDistCalc.GetSqDist( dSqDist) ; + if ( dSqDist < dMinSqDinst) { + dMinSqDinst = dSqDist ; + nMinSeg = nPt ; + } + } + // Se il punto non sta sul loop, errore + if ( dMinSqDinst > SQ_EPS_SMALL) + return false ; + // Verifico che il punto stia su un vertice, in tal caso non devo fare nulla + bool bOnStart = AreSamePointApprox( Loop[nMinSeg], ptNewStart) ; + bool bOnEnd = AreSamePointApprox( Loop[( nMinSeg + 1) % int( Loop.size())], ptNewStart) ; + if ( bOnStart || bOnEnd) { + if ( bOnEnd) { + ++ nMinSeg ; + if ( nMinSeg % int( Loop.size()) == 0) + return true ; + } + PNTVECTOR vTempVec ; + for ( int nPt = 0 ; nPt < nMinSeg ; ++ nPt) + vTempVec.emplace_back( Loop[nPt]) ; + int nSize = int( Loop.size()) ; + for ( int nPt = 0 ; nPt < nSize - nMinSeg ; ++ nPt) { + Loop[nPt] = Loop[nPt + nMinSeg] ; + } + for ( int nPt = 0 ; nPt < int( vTempVec.size()) ; ++ nPt) { + Loop[nPt + nSize - nMinSeg] = vTempVec[nPt] ; + } + return true ; + } + // Ridimensiono il loop + Loop.resize( Loop.size() + 1) ; + // Copio i primi punti + PNTVECTOR LoopTemp ; + for ( int nPt = 0 ; nPt <= nMinSeg ; ++ nPt) + LoopTemp.emplace_back( Loop[nPt]) ; + // Aggiungo il nuovo punto all'inizio + Loop[0] = ptNewStart ; + // Sposto gli ultimi in testa + int nLastPointNum = int( Loop.size()) - 1 - nMinSeg ; + for ( int nPt = 1 ; nPt <= nLastPointNum ; ++ nPt) { + Loop[nPt] = Loop[nPt + nMinSeg] ; + } + // Porto i primi in fondo + for ( int nPt = 0 ; nPt < int( LoopTemp.size()) ; ++ nPt) { + Loop[nPt + nLastPointNum] = LoopTemp[nPt] ; + } + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::SplitAtPoint( const Point3d& ptStop, const PNTVECTOR& Loop, PNTVECTOR& Loop1, PNTVECTOR& Loop2) +{ + // Cerco il tratto del loop chiuso più vicino al punto + int nMinSeg = -1 ; + double dMinSqDinst = DBL_MAX ; + for ( int nPt = 0 ; nPt < int( Loop.size()) ; ++ nPt) { + // Estremi del segmento corrente del loop + Point3d ptSegSt = Loop[nPt] ; + Point3d ptSegEn = Loop[( nPt + 1) % int(Loop.size())] ; + // Distanza del punto dal segmento del loop + DistPointLine dDistCalc( ptStop, ptSegSt, ptSegEn) ; + double dSqDist ; + dDistCalc.GetSqDist( dSqDist) ; + if ( dSqDist < dMinSqDinst) { + dMinSqDinst = dSqDist ; + nMinSeg = nPt ; + } + } + // Se il punto non sta sul loop, errore + if ( dMinSqDinst > SQ_EPS_SMALL) + return false ; + // Verifico che il punto stia su un vertice, in tal caso non devo aggiungerlo + bool bFirst = AreSamePointApprox( Loop[nMinSeg], ptStop) ; + bool bLast = AreSamePointApprox( Loop[( nMinSeg + 1) % int( Loop.size())], ptStop) ; + // Se il punto è sul vertice finale del segmento, aggiungo il vertice alla lista da inglobare al primo loop + if ( bLast) + ++ nMinSeg ; + // Inglobo fino a nSeg nel primo loop + for ( int nPt = 0 ; nPt <= nMinSeg && nPt < int( Loop.size()) ; ++ nPt) + Loop1.emplace_back( Loop[nPt]) ; + // Se il punto è interno al segmento, lo inglobo in entrambi i loop + if ( ! ( bFirst || bLast)) { + Loop1.emplace_back( ptStop) ; + Loop2.emplace_back( ptStop) ; + } + else { + if ( nMinSeg != int( Loop.size()) ) + Loop2.emplace_back( Loop[nMinSeg]) ; + } + // Inglobo gli ultimi vertici in Loop2 + for ( int nPt = nMinSeg + 1 ; nPt < int( Loop.size()) ; ++ nPt) + Loop2.emplace_back( Loop[nPt]) ; + Loop2.emplace_back( Loop[0]) ; + + return true ; +}