//---------------------------------------------------------------------------- // 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 "Triangulate.h" #include "GeoConst.h" #include "/EgtDev/Include/EGkSfrCreate.h" #include "/EgtDev/Include/EGkDistPointLine.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EGkIntersLinePlane.h" #include "/EgtDev/Include/EGkIntersPlaneTria.h" using namespace std ; //---------------------------------------------------------------------------- const double CUT_SCALE = PREC_SCALE_COEFF ; //---------------------------------------------------------------------------- bool SurfTriMesh::Cut( const Plane3d& plPlane, bool bSaveOnEq) { // La superficie deve essere validata if ( m_nStatus != OK) return false ; // Se la superficie è vuota non devo fare alcunchè if ( IsEmpty()) return true ; // recupero il numero originale di triangoli e di facce int nTriaOriCnt = GetTriangleCount() ; int nFacetOriCnt = GetFacetCount() ; // eseguo il taglio con il metodo dei triangoli bool 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()) 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 for ( int i = 0 ; i < GetVertexSize() ; ++ i) { // salto i vertici cancellati if ( m_vVert[i].nIdTria == SVT_DEL) continue ; double dDist = DistPointPlane( m_vVert[i].ptP, plPlane) ; if ( abs( dDist) < 1.1 * EPS_SMALL) m_vVert[i].nTemp = 0 ; 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 ; CutTriangleByPlane( i, plPlane, bSaveOnEq, bModif) ; } return true ; } //---------------------------------------------------------------------------- bool SurfTriMesh::CutTriangleByPlane( int nT, const Plane3d& plPlane, bool bSaveOnEq, bool& bModif) { // se flag abilita e giace sul piano ed equiverso, lo salvo if ( bSaveOnEq && m_vVert[ m_vTria[nT].nIdVert[0]].nTemp == 0 && m_vVert[ m_vTria[nT].nIdVert[1]].nTemp == 0 && m_vVert[ m_vTria[nT].nIdVert[2]].nTemp == 0 && AreSameVectorApprox( m_vTria[nT].vtN, plPlane.GetVersN())) ; // se giace sul piano o dalla parte esterna del piano, lo cancello else if ( m_vVert[ m_vTria[nT].nIdVert[0]].nTemp != -1 && m_vVert[ m_vTria[nT].nIdVert[1]].nTemp != -1 && m_vVert[ m_vTria[nT].nIdVert[2]].nTemp != -1) { bModif = true ; RemoveTriangle( nT) ; } // se giace dalla parte interna con al massimo un lato sul piano, lo conservo else if ( m_vVert[ m_vTria[nT].nIdVert[0]].nTemp != 1 && m_vVert[ m_vTria[nT].nIdVert[1]].nTemp != 1 && m_vVert[ m_vTria[nT].nIdVert[2]].nTemp != 1) ; // altrimenti attraversa il piano, devo modificarlo else { bModif = true ; int nDisc = m_vVert[ m_vTria[nT].nIdVert[0]].nTemp + m_vVert[ m_vTria[nT].nIdVert[1]].nTemp + m_vVert[ m_vTria[nT].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[nT].nIdVert[0]].nTemp == 0) { nVertOn = 0 ; nVertIn = 1 ; nVertOut = 2 ; if ( m_vVert[ m_vTria[nT].nIdVert[1]].nTemp > 0) swap( nVertIn, nVertOut) ; } else if ( m_vVert[ m_vTria[nT].nIdVert[1]].nTemp == 0) { nVertOn = 1 ; nVertIn = 2 ; nVertOut = 0 ; if ( m_vVert[ m_vTria[nT].nIdVert[2]].nTemp > 0) swap( nVertIn, nVertOut) ; } else { nVertOn = 2 ; nVertIn = 0 ; nVertOut = 1 ; if ( m_vVert[ m_vTria[nT].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[nT].nIdVert[nVertIn]].ptP, plPlane) ; double dDistOut = DistPointPlane( m_vVert[m_vTria[nT].nIdVert[nVertOut]].ptP, plPlane) ; Point3d ptInt = Media( m_vVert[m_vTria[nT].nIdVert[nVertIn]].ptP, m_vVert[m_vTria[nT].nIdVert[nVertOut]].ptP, dDistIn / ( dDistIn + dDistOut)) ; // inserisco il nuovo vertice int nNewV = AddVertex( ptInt) ; // inserisco il nuovo triangolo int nIdVert[3] = { m_vTria[nT].nIdVert[0], m_vTria[nT].nIdVert[1], m_vTria[nT].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( nT) ; } // 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[nT].nIdVert[0]].nTemp == -1) { nVertIn = 0 ; nVertOut1 = 1 ; nVertOut2 = 2 ; } else if ( m_vVert[ m_vTria[nT].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[nT].nIdVert[nVertIn]].ptP, plPlane) ; double dDistOut1 = DistPointPlane( m_vVert[m_vTria[nT].nIdVert[nVertOut1]].ptP, plPlane) ; double dDistOut2 = DistPointPlane( m_vVert[m_vTria[nT].nIdVert[nVertOut2]].ptP, plPlane) ; Point3d ptInt1 = Media( m_vVert[m_vTria[nT].nIdVert[nVertIn]].ptP, m_vVert[m_vTria[nT].nIdVert[nVertOut1]].ptP, dDistIn / ( dDistIn + dDistOut1)) ; Point3d ptInt2 = Media( m_vVert[m_vTria[nT].nIdVert[nVertIn]].ptP, m_vVert[m_vTria[nT].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[nT].nIdVert[0], m_vTria[nT].nIdVert[1], m_vTria[nT].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( nT) ; } // 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[nT].nIdVert[0]].nTemp == 1) { nVertOut = 0 ; nVertIn1 = 1 ; nVertIn2 = 2 ; } else if ( m_vVert[ m_vTria[nT].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[nT].nIdVert[nVertIn1]].ptP, plPlane) ; double dDistIn2 = - DistPointPlane( m_vVert[m_vTria[nT].nIdVert[nVertIn2]].ptP, plPlane) ; double dDistOut = DistPointPlane( m_vVert[m_vTria[nT].nIdVert[nVertOut]].ptP, plPlane) ; Point3d ptInt1 = Media( m_vVert[m_vTria[nT].nIdVert[nVertIn1]].ptP, m_vVert[m_vTria[nT].nIdVert[nVertOut]].ptP, dDistIn1 / ( dDistIn1 + dDistOut)) ; Point3d ptInt2 = Media( m_vVert[m_vTria[nT].nIdVert[nVertIn2]].ptP, m_vVert[m_vTria[nT].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[nT].nIdVert[0], m_vTria[nT].nIdVert[1], m_vTria[nT].nIdVert[2]} ; int nIdVert2[3] = { m_vTria[nT].nIdVert[0], m_vTria[nT].nIdVert[1], m_vTria[nT].nIdVert[2]} ; double dSqDist1 = SqDist( m_vVert[m_vTria[nT].nIdVert[nVertIn1]].ptP, ptInt2) ; double dSqDist2 = SqDist( m_vVert[m_vTria[nT].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( nT) ; } } 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 ; // Se la superficie è vuota non devo fare alcunchè if ( IsEmpty()) return true ; // 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 ; { // centro proiettato sul piano della curva Point3d ptCen = trTria.GetCentroid() ; ptCen = ptCen - ( ptCen - ptStart) * vtExtr * vtExtr ; // calcolo distanza DistPointCurve dstPC( ptCen, cvCompo) ; double dDist ; dstPC.GetDist( dDist) ; // se maggiore oltre il limite originale if ( dDist > CUT_SCALE * EPS_SMALL) { int nSide ; if ( dstPC.GetSideAtMinDistPoint( 0, vtExtr, nSide)) bTriaCenIn = ( nSide == MDS_LEFT) ; } // altrimenti ricalcolo con centro del triangolo spostato all'interno sulla normale else { Point3d ptCen2 = trTria.GetCentroid() - CUT_SCALE * EPS_SMALL * trTria.GetN() ; DistPointCurve dstP2C( ptCen2, cvCompo) ; int nSide2 ; if ( dstP2C.GetSideAtMinDistPoint( 0, vtExtr, nSide2)) bTriaCenIn = ( nSide2 == MDS_LEFT && bSaveOnEq) ; } } // Vettore di catene di punti e flag sulla complanarità del triangolo con uno dei rettangoli CHAINVECTOR vChain ; bool bTriaOn = false ; // Segnalatore di taglio piccolo, punto e vettore per costruire il piano per eseguirlo int nSmallCutState = 0 ; Point3d ptSmallCutPnt ; Vector3d vtSmallCutVec ; // 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) ; // Se il segmento giace sul piano del triangolo, lo segnalo. if ( abs( ( ptSt - trTria.GetCentroid()) * trTria.GetN()) < CUT_SCALE * EPS_SMALL && abs( ( ptEn - trTria.GetCentroid()) * trTria.GetN()) < CUT_SCALE * EPS_SMALL && abs( trTria.GetN() * vtExtr) < EPS_SMALL) bTriaOn = true ; // 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 { // Taglio piccolo if ( nInt == 1) { if ( nSmallCutState == 0) nSmallCutState = 1 ; else nSmallCutState = 2 ; ptSmallCutPnt = ptSt ; vtSmallCutVec = ptEn - ptSt ; } bChain = false ; } pCrv = cvCompo.GetNextCurve() ; } // Gestisco il taglio piccolo. if ( nSmallCutState == 1 && int( vChain.size()) == 0) { Plane3d plPlane ; plPlane.Set( ptSmallCutPnt, vtSmallCutVec ^ ( vtMax - vtMin)) ; for ( int nV = 0 ; nV < 3 ; ++ nV) { double dDist = DistPointPlane( m_vVert[m_vTria[nT].nIdVert[nV]].ptP, plPlane) ; if ( abs( dDist) < EPS_SMALL) { m_vVert[m_vTria[nT].nIdVert[nV]].nTemp = 0 ; if ( abs( dDist) > EPS_SMALL) m_vVert[m_vTria[nT].nIdVert[nV]].ptP -= plPlane.GetVersN() * dDist ; } else if ( dDist > 0) m_vVert[m_vTria[nT].nIdVert[nV]].nTemp = +1 ; else m_vVert[m_vTria[nT].nIdVert[nV]].nTemp = -1 ; } CutTriangleByPlane( nT, plPlane, bSaveOnEq, bModif) ; continue ; } 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 ; } nChainCnt = int( vChain.size()) ; // 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.empty() && ! cvOpenChain.empty())) { 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.empty()) { // 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.empty()) { 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) ; // Se il triangolo originale aveva il centro interno alla curva oppure devo tenere le parti // di superficie che hanno una parte giacente sul rettangolo della superficie di taglio, // aggiungo i nuovi triangoli. if ( ! bTriaOn || bSaveOnEq) { // 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 ; } } // se superficie originale a facce, cerco di semplificarle in ogni caso if ( nFacetOriCnt < 200 || double( nTriaOriCnt) / nFacetOriCnt > 4) { if ( ! SimplifyFacets( CUT_SCALE * MAX_EDGE_LEN_STD)) LOG_ERROR( GetEGkLogger(), "Error in SimplifyFacets of Stm::GeneralizedCut") } // Ripristino scala originale Scale( frScalingRef, 1. / CUT_SCALE, 1. / CUT_SCALE, 1. / CUT_SCALE) ; return true ; }