//---------------------------------------------------------------------------- // EgalTech 2025 //---------------------------------------------------------------------------- // File : SurfTriMeshOffset.cpp Data : 07.07.25 Versione : 2.7g1 // Contenuto : Implementazione funzione per Offset di Superfici TriMesh. // // // // Modifiche : 10.06.25 RE Creazione modulo. // 10.06.25 RE Offset di superfici chiuse. // 04.07.25 RE Thickening Offset di superfici generiche. // 10.12.25 RE Creazione superfici Shell. // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "VolZmap.h" #include "SurfTriMesh.h" #include "/EgtDev/Include/EGkDistPointSurfTm.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EGkDistPointTria.h" #include "/EgtDev/Include/EGkSurfTriMeshAux.h" #include "/EgtDev/Include/EGkStmFromCurves.h" #include "/EgtDev/Include/EGkStmFromTriangleSoup.h" #include #define DEBUG 0 #if DEBUG #include "/EgtDev/Include/EGnStringUtils.h" #include "/EgtDev/Include/EGkGeoObjSave.h" #include "/EgtDev/Include/EGkGeoPoint3d.h" #include "/EgtDev/Include/EGkGeoVector3d.h" #include "/EgtDev/Include/EGkStmStandard.h" #include "/EgtDev/Include/EgtPerfCounter.h" std::vector VT ; std::vector VC ; #endif using namespace std ; //---------------------------------------------------------------------------- static ISurfTriMesh* SumStm( const CISURFTMPVECTOR& vStm) { // se vettore vuoto, non faccio nulla if ( vStm.empty()) return nullptr ; // definisco la superficie somma tra tutte ( la prima deve essere valida) PtrOwner pStmAdd( CreateSurfTriMesh()) ; if ( IsNull( pStmAdd)) return nullptr ; // scorro le superfici for ( const ISurfTriMesh* pStm : vStm) { if ( pStm == nullptr || ! pStm->IsValid() || pStm->GetTriangleCount() == 0) continue ; if ( ! pStmAdd->IsValid() || pStmAdd->GetTriangleCount() == 0) { if ( ! pStmAdd->CopyFrom( pStm)) return nullptr ; } else pStmAdd->Add( *pStm) ; } // restituisco la superficie ottenuta return ( Release( pStmAdd)) ; } //---------------------------------------------------------------------------- // Funzioni per la distanza tra punto e superficie TriMesh in parallelo //---------------------------------------------------------------------------- static bool BoundingBoxDifference( const BBox3d& boxA, const BBox3d& boxB, BOXVECTOR& vBoxDiff) { // svuoto il risultato vBoxDiff.clear() ; // se box A vuoto, risultato vuoto if ( boxA.IsEmpty()) return false ; // se box B vuoto o i box non si intersecano, risultato è ancora A BBox3d boxInt ; if ( boxB.IsSmall() || ! boxA.FindIntersection( boxB, boxInt)) { vBoxDiff.emplace_back( boxA) ; return true ; } // recupero i punti estremi dei box A e Intersezione Point3d ptMinA, ptMaxA ; boxA.GetMinMax( ptMinA, ptMaxA) ; Point3d ptMinInt, ptMaxInt ; boxInt.GetMinMax( ptMinInt, ptMaxInt) ; // sotto if ( ptMinInt.z - ptMinA.z > EPS_SMALL) { BBox3d boxD( ptMinA, Point3d( ptMaxA.x, ptMaxA.y, ptMinInt.z)) ; vBoxDiff.emplace_back( boxD) ; } // sopra if ( ptMaxA.z - ptMaxInt.z > EPS_SMALL) { BBox3d boxD( Point3d( ptMinA.x, ptMinA.y, ptMaxInt.z), ptMaxA) ; vBoxDiff.emplace_back( boxD) ; } // davanti if ( ptMinInt.y - ptMinA.y > EPS_SMALL) { BBox3d boxD( Point3d( ptMinA.x, ptMinA.y, ptMinInt.z), Point3d( ptMaxA.x, ptMinInt.y, ptMaxInt.z)) ; vBoxDiff.emplace_back( boxD) ; } // dietro if ( ptMaxA.y - ptMaxInt.y > EPS_SMALL) { BBox3d boxD( Point3d( ptMinA.x, ptMaxInt.y, ptMinInt.z), Point3d( ptMaxA.x, ptMaxA.y, ptMaxInt.z)) ; vBoxDiff.emplace_back( boxD) ; } // sinistra if ( ptMinInt.x - ptMinA.x > EPS_SMALL) { BBox3d boxD( Point3d( ptMinA.x, ptMinInt.y, ptMinInt.z), Point3d( ptMinInt.x, ptMaxInt.y, ptMaxInt.z)) ; vBoxDiff.emplace_back( boxD) ; } // destra if ( ptMaxA.y - ptMaxInt.y > EPS_SMALL) { BBox3d boxD( Point3d( ptMaxInt.x, ptMinInt.y, ptMinInt.z), Point3d( ptMaxA.x, ptMaxInt.y, ptMaxInt.z)) ; vBoxDiff.emplace_back( boxD) ; } // risultato return ( ! vBoxDiff.empty()) ; } //---------------------------------------------------------------------------- static bool DistPointSurfTmMultiThread( const Point3d& ptP, const SurfTriMesh& SurfTm, double& dDist, bool& bIsInside, INTVECTOR& vIndClosestTria) { // verifico che la supercicie sia valida if ( ! SurfTm.IsValid()) return false ; // inizializzo distanza non calcolata dDist = - 1. ; // vettore di indici dei triangoli più vicini inizialmente vuoto vIndClosestTria.clear() ; // vettore dei flag temporanei inizialmente tutto a 0 INTVECTOR vIntFlags( SurfTm.GetTriangleCount(), 0) ; // recupero e verifico il box locale della superficie BBox3d b3Stm = SurfTm.GetAllTriaBox() ; if ( b3Stm.IsEmpty()) return false ; // cerco triangoli in box centrati sul punto dato di ampiezza crescente ed escludendo le parti già verificate. // termino quando non trovo più triangoli che possano soddisfare la richiesta. Point3d ptMin, ptMax ; b3Stm.GetMinMax( ptMin, ptMax) ; double dDeltaLen = max( min( min( b3Stm.GetDimX(), b3Stm.GetDimY()), b3Stm.GetDimZ()) / 40., 20.) ; double dBoxHalfLenX = max( max( ptMin.x - ptP.x, ptP.x - ptMax.x), 0.) + dDeltaLen ; double dBoxHalfLenY = max( max( ptMin.y - ptP.y, ptP.y - ptMax.y), 0.) + dDeltaLen ; double dBoxHalfLenZ = max( max( ptMin.z - ptP.z, ptP.z - ptMax.z), 0.) + dDeltaLen ; // considero anche il box precedente per poter analizzare solo il volume differenza tra i due BBox3d boxPPrev( ptP) ; BBox3d boxP( ptP, dBoxHalfLenX, dBoxHalfLenY, dBoxHalfLenZ) ; // variabili distanza minima, indice del triangolo di distanza minima, punto di distanza minima double dMinDist = DBL_MAX ; int nMinDistTriaIndex = SVT_NULL ; Point3d ptMinDistPoint ; // finché non si verifica la condizione di terminazione ingrandisco il box. bool bContinue = true ; // creazione del vettore dei triangoli più vicini a ptP vector> vTria ; // while ( bContinue) { // calcolo il box differenza con il precedente per non esplorare parti già considerate BOXVECTOR vBox ; BoundingBoxDifference( boxP, boxPPrev, vBox) ; // Ciclo sui box differenza bool bCollide = false ; for ( const auto& b3Box : vBox) { // interseco il box con quello della superficie e ne verifico la distanza minima dal punto BBox3d b3Int ; if ( ! b3Box.FindIntersection( b3Stm, b3Int) || b3Int.DistFromPoint( ptP) > dMinDist) continue ; // ricerca sui triangoli nel box bCollide = true ; INTVECTOR vnIds ; if ( SurfTm.GetAllTriaOverlapBox( b3Int, vnIds)) { // ciclo sui triangoli del sotto-box corrente for ( auto nT : vnIds) { Triangle3d trCurTria ; if ( vIntFlags[nT] == 0 && SurfTm.GetTriangle( nT, trCurTria)) { vIntFlags[nT] = 1 ; DistPointTriangle distPT( ptP, trCurTria) ; double dCurrDist ; // se la distanza del triangolo è valida e minore di quella attuale aggiorno if ( distPT.GetDist( dCurrDist)) { // se distanze uguali... if ( abs( dCurrDist - dMinDist) < EPS_SMALL) // aggiungo il triangolo vTria.emplace_back( make_pair( nT, trCurTria)) ; // se minore... else if ( dCurrDist < dMinDist) { // pulisco il vettore vTria.clear() ; dMinDist = dCurrDist ; nMinDistTriaIndex = nT ; distPT.GetMinDistPoint( ptMinDistPoint) ; // aggiungo il triangolo vTria.emplace_back( make_pair( nT, trCurTria)) ; } } } } } } // se si verifica la condizione di terminazione arresto il ciclo altrimenti aggiorno i box if ( ! bCollide || dMinDist < EPS_SMALL) bContinue = false ; else { boxPPrev = boxP ; boxP.Expand( dDeltaLen) ; } } // se non ho trovato nessun triangolo, errore if ( nMinDistTriaIndex == SVT_NULL) return false ; // riempio il vettore dei triangoli a minima distanza for ( auto& Tria : vTria) vIndClosestTria.emplace_back( Tria.first) ; // salvo la distanza dDist = dMinDist ; // determino il Side if ( dDist < EPS_SMALL) { bIsInside = false ; return true ; } // se ho solo un triangolo else if ( int( vTria.size()) == 1) { bIsInside = ( ( ptP - ptMinDistPoint) * vTria.back().second.GetN() < - EPS_SMALL) ; return true ; } // controllo se tutti i triangoli a minima distanza forniscono la stessa informazione // ( il punto potrebbe essere esterno a tutti, interno a tutti o indefinito ) bool bInside = false ; bool bOutside = false ; for ( int i = 0 ; i < int( vTria.size()) ; ++ i) { if ( ( ptP - vTria[i].second.GetP( 0)) * vTria[i].second.GetN() < - EPS_SMALL) bInside = true ; else bOutside = true ; } bIsInside = false ; if ( bOutside == bInside) { Point3d ptBar_tot ; for ( const auto& Tria : vTria) ptBar_tot += Tria.second.GetCentroid() ; for ( const auto& Tria : vTria) { Point3d ptInters1, ptInters2 ; int nType = IntersLineTria( ptP, ptBar_tot, Tria.second, ptInters1, ptInters2) ; if ( nType == ILTT_IN) { DistPointTriangle( ptP, Tria.second).GetMinDistPoint( ptMinDistPoint) ; bIsInside = ( ( ptP - ptMinDistPoint) * Tria.second.GetN() < - EPS_SMALL) ; nMinDistTriaIndex = Tria.first ; break ; } } } else bIsInside = bInside ; return true ; } //---------------------------------------------------------------------------- static bool ClassifyTrianglesMultiThread( const TRIA3DEXVECTOR& vTria, int nIndS, int nIndE, const SurfTriMesh& SurfTm, double dOffs, double dPrec, bool bSaveInside, BOOLVECTOR& vbSafe) { // verifico che la superficie sia valida if ( ! SurfTm.IsValid()) return false ; // verifico la validità degli indici if ( nIndS < 0 || nIndE >= int( vTria.size())) return false ; // verifico la dimensione dei vettori if ( vTria.size() != vbSafe.size()) return false ; // scorro gli indici dei triangoli da classificare for ( int k = nIndS ; k <= nIndE ; ++ k) { // recupero il triangolo corrente const Triangle3dEx& Tria = vTria[k] ; // preparo gli elementi di classificazione DBLVECTOR vDists ; vDists.resize( 3) ; INTMATRIX matIndClosestTria ; matIndClosestTria.resize( 3) ; // verifico che i suoi punti siano distanti almeno |dOffs| - dTol dalla superficie vbSafe[k] = true ; for ( int i = 0 ; vbSafe[k] && i < 3 ; ++ i) { bool bIsInside = false ; DistPointSurfTmMultiThread( Tria.GetP( i), SurfTm, vDists[i], bIsInside, matIndClosestTria[i]) ; vbSafe[k] = ( ( vDists[i] > abs( dOffs) - 0.25 * dPrec) && ( vDists[i] < abs( dOffs) + 0.25 * dPrec) && ( bIsInside == bSaveInside)) ; } // se tutti sufficientemente distanti if ( vbSafe[k]) { // i triangoli a minima distanza devono avere normale simile bool bPerp = true ; for ( int i = 0 ; bPerp && i < 3 ; ++ i) { for ( int j = 0 ; bPerp && j < int( matIndClosestTria[i].size()) ; ++ j) { Triangle3d TriaCloser ; SurfTm.GetTriangle( matIndClosestTria[i][j], TriaCloser) ; bPerp = ( abs( Tria.GetN() * TriaCloser.GetN()) < cos( 30. * DEGTORAD)) ; } } vbSafe[k] = ( ! bPerp) ; } } return true ; } //---------------------------------------------------------------------------- // Funzione che crea l'Offset di una superficie TriMesh //---------------------------------------------------------------------------- ISurfTriMesh* CreateSurfTriMeshOffset( const ISurfTriMesh* pStm, double dOffs, double dPrec, int nType) { return ( CreateSurfTriMeshesOffset( { pStm}, dOffs, dPrec, nType)) ; } //---------------------------------------------------------------------------- // Funzione che crea il Fat Offset di una superficie TriMesh //---------------------------------------------------------------------------- ISurfTriMesh* CreateSurfTriMeshThickeningOffset( const ISurfTriMesh* pStm, double dOffs, double dPrec, int nType) { return ( CreateSurfTriMeshesThickeningOffset( { pStm}, dOffs, dPrec, nType)) ; } //---------------------------------------------------------------------------- // Funzione che crea l'Offset di un insieme di superfici //---------------------------------------------------------------------------- ISurfTriMesh* CreateSurfTriMeshesOffset( const CISURFTMPVECTOR& vStm, double dOffs, double dPrec, int nType) { // se vettore delle superfici vuoto, non faccio nulla if ( vStm.empty()) return nullptr ; // controllo sul valore di tolleranza lineare double dMyPrec = max( dPrec, 100. * EPS_SMALL) ; // --- NB. ( Il valore di Offset deve essere maggiore di 10 * EPS_SMALL in valore assoluto) // Nel caso sia minore, restituisco semplicemente la somma delle superfici // ( questo valore serve per rimanere coerente con l'Offset delle curve) if ( abs( dOffs) < 10. * EPS_SMALL) return SumStm( vStm) ; // creo lo Zmap associato alle superfici TriMesh VolZmap myVolZmap ; if ( ! myVolZmap.CreateFromTriMeshOffset( vStm, dOffs, dMyPrec, nType)) return nullptr ; if ( ! myVolZmap.IsValid()) return nullptr ; // recupero le superfici aperte CISURFTMPVECTOR vStmOpen ; for ( const ISurfTriMesh* pStm : vStm) { if ( pStm != nullptr && pStm->IsValid() && ! pStm->IsClosed()) vStmOpen.emplace_back( pStm) ; } // --- se non ho superfici aperte if ( vStmOpen.empty()) { // restituisco la superficie TriMesh di Offset return ( myVolZmap.GetSurfTriMesh()) ; } // --- se ho delle superfici chiuse TRIA3DEXVECTOR vAllTria, vTriaOffs ; for ( int nB = 0 ; nB < myVolZmap.GetBlockCount() ; ++ nB) { TRIA3DEXVECTOR vTria, vTriaSafe ; myVolZmap.GetBlockTriangles( nB, vTria) ; #if DEBUG TRIA3DVECTOR vTriaUnsafe ; #endif for ( int nT = 0 ; nT < int( vTria.size()) ; ++ nT) { Triangle3dEx& Tria = vTria[nT] ; BBox3d BBoxTria ; Tria.GetLocalBBox( BBoxTria) ; // azzero flag di colore vAllTria.push_back( Tria) ; } } // classifico i triangoli PtrOwner pStmBasic( nullptr) ; if ( int( vStmOpen.size() == 1)) pStmBasic.Set( GetBasicSurfTriMesh( CloneSurfTriMesh( vStmOpen[0]))) ; else { StmFromTriangleSoup AllOpenStmSoup ; AllOpenStmSoup.Start() ; for ( const ISurfTriMesh* pStmOpen : vStmOpen) { if ( pStmOpen != nullptr && pStmOpen->IsValid()) { for ( int nT = 0 ; nT < pStmOpen->GetTriangleCount() ; ++ nT) { Triangle3d Tria ; if ( pStmOpen->GetTriangle( nT, Tria)) AllOpenStmSoup.AddTriangle( Tria) ; } } } AllOpenStmSoup.End() ; pStmBasic.Set( GetBasicSurfTriMesh( AllOpenStmSoup.GetSurf())) ; } if ( pStmBasic == nullptr) return nullptr ; BBox3d b3Stm = pStmBasic->GetAllTriaBox() ; if ( b3Stm.IsEmpty()) return nullptr ; // numero di triangoli da analizzare int nTriaCnt = int( vAllTria.size()) ; // definisco un vettore di Flag per i triangoli già visitati INTVECTOR vIntFlags( pStmBasic->GetTriangleCount()) ; // numero massimo di thread concorrenti int nThreadMax = thread::hardware_concurrency() ; bool bOk = true ; BOOLVECTOR vbSafeTria( vAllTria.size(), true) ; if ( nThreadMax <= 1 || nTriaCnt < 50) ClassifyTrianglesMultiThread( vAllTria, 0, nTriaCnt - 1, *pStmBasic, abs( dOffs), dPrec, ( dOffs < 0.), vbSafeTria) ; else { const int MAX_PARTS = 32 ; INTINTVECTOR vFstLst( MAX_PARTS) ; // calcolo le parti del vettore int nPartCnt = min( nThreadMax, MAX_PARTS) ; int nPartDim = nTriaCnt / nPartCnt + 1 ; for ( int i = 0 ; i < nPartCnt ; ++ i) { vFstLst[i].first = i * nPartDim ; vFstLst[i].second = min( ( i + 1) * nPartDim, nTriaCnt) - 1 ; } // processo le parti future vRes[MAX_PARTS] ; for ( int i = 0 ; i < nPartCnt ; ++ i) { vRes[i] = async( launch::async, &ClassifyTrianglesMultiThread, cref( vAllTria), vFstLst[i].first, vFstLst[i].second, cref( *pStmBasic), abs( dOffs), dPrec, ( dOffs < 0.), ref( vbSafeTria)) ; } // attendo i risultati int nFin = 0 ; while ( nFin < nPartCnt) { for ( int i = 0 ; i < nPartCnt ; ++ i) { if ( vRes[i].valid() && vRes[i].wait_for( chrono::nanoseconds{ 1}) == future_status::ready) { bOk = vRes[i].get() && bOk ; ++ nFin ; } } } } if ( ! bOk) return nullptr ; TRIA3DEXVECTOR vTriaSafe ; vTriaSafe.reserve( vAllTria.size()) ; #if DEBUG TRIA3DEXVECTOR vTriaUnSafe ; vTriaUnSafe.reserve( vAllTria.size()) ; #endif for ( int i = 0 ; i < int( vAllTria.size()) ; ++ i) { if ( vbSafeTria[i]) vTriaSafe.emplace_back( vAllTria[i]) ; #if DEBUG if ( ! vbSafeTria[i]) vTriaUnSafe.emplace_back( vAllTria[i]) ; #endif } // definisco la superficie con i soli triangoli validi StmFromTriangleSoup TriaSoup ; TriaSoup.Start() ; for ( const Triangle3d& SafeTria : vTriaSafe) TriaSoup.AddTriangle( SafeTria) ; TriaSoup.End() ; PtrOwner pStmOffs( TriaSoup.GetSurf()) ; if ( IsNull( pStmOffs) || ! pStmOffs->IsValid() || pStmOffs->GetTriangleCount() == 0) return nullptr ; #if DEBUG StmFromTriangleSoup _invalidSoup ; _invalidSoup.Start() ; for ( const Triangle3d& _unsafeTria : vTriaUnSafe) _invalidSoup.AddTriangle( _unsafeTria) ; _invalidSoup.End() ; VT.emplace_back( pStmOffs->Clone()) ; VC.emplace_back( LIME) ; VT.emplace_back( _invalidSoup.GetSurf()) ; VC.emplace_back( RED) ; SaveGeoObj( VT, VC, "C:\\Temp\\TriangleSelection.nge") ; #endif return ( Release( pStmOffs)) ; } //---------------------------------------------------------------------------- //* Funzione che crea il Fat Offset di un insieme di superfici //---------------------------------------------------------------------------- ISurfTriMesh* CreateSurfTriMeshesThickeningOffset( const CISURFTMPVECTOR& vStm, double dOffs, double dPrec, int nType) { // se vettore delle superfici vuoto, non faccio nulla if ( vStm.empty()) return nullptr ; // controllo sul valore di tolleranza lineare double dMyPrec = max( dPrec, 100 * EPS_SMALL) ; // --- NB. ( Il valore di Offset deve essere maggiore di 10 * EPS_SMALL in valore assoluto) // Nel caso sia minore, restituisco semplicemente la somma delle superfici // ( questo valore serve per rimanere coerente con l'Offset delle curve) if ( abs( dOffs) < 10 * EPS_SMALL) return SumStm( vStm) ; // creo lo Zmap associato alle superfici TriMesh VolZmap OneVolZmap ; if ( ! OneVolZmap.CreateFromTriMeshThickeningOffset( vStm, dOffs, dMyPrec, nType)) return nullptr ; if ( ! OneVolZmap.IsValid()) return nullptr ; // restituisco la superficie TriMesh return ( OneVolZmap.GetSurfTriMesh()) ; } //---------------------------------------------------------------------------- // Funzione per creare la Superficie TriMesh Shell da una Trimesh aperta //---------------------------------------------------------------------------- ISurfTriMesh* CreateSurfTriMeshShell( const ISurfTriMesh* pStm, double dThick, double dPrec) { // verifico che la superficie sia valida ed aperta if ( pStm == nullptr || ! pStm->IsValid() || pStm->IsClosed()) return nullptr ; // lo spessore deve essere sempre positivo, il verso è sempre dato dalla normale dei triangoli dThick = - max( 10. * EPS_SMALL, abs( dThick)) ; // creo il suo Offset ( salvandomi lo Zmap per l'orientamento) #if DEBUG PerformanceCounter PC ; PC.Start() ; #endif VolZmap myVolZMap ; if ( ! myVolZMap.CreateFromTriMeshOffset( { pStm}, dThick, dPrec)) return nullptr ; if ( ! myVolZMap.IsValid()) return nullptr ; #if DEBUG LOG_INFO( GetEGkLogger(), ( string{ "Tria Time : "} + ToString( PC.Stop())).c_str()) ; VT.clear() ; VC.clear() ; VT.emplace_back( myVolZMap.Clone()) ; VC.emplace_back( BLACK) ; VT.emplace_back( pStm->Clone()) ; VC.emplace_back( YELLOW) ; SaveGeoObj( VT, VC, "C:\\Temp\\VolZMapOffs.nge") ; #endif #if DEBUG VT.clear() ; VC.clear() ; PC.Start() ; #endif // recupero i triangoli dallo ZMap creato TRIA3DEXVECTOR vAllTria, vTriaOffs ; for ( int nB = 0 ; nB < myVolZMap.GetBlockCount() ; ++ nB) { TRIA3DEXVECTOR vTria, vTriaSafe ; myVolZMap.GetBlockTriangles( nB, vTria) ; #if DEBUG TRIA3DVECTOR vTriaUnsafe ; #endif for ( int nT = 0 ; nT < int( vTria.size()) ; ++ nT) { Triangle3dEx& Tria = vTria[nT] ; BBox3d BBoxTria ; Tria.GetLocalBBox( BBoxTria) ; // azzero flag di colore vAllTria.push_back( Tria) ; } } // classifico i triangoli const SurfTriMesh* pStmBasic = GetBasicSurfTriMesh( pStm) ; if ( pStmBasic == nullptr) return nullptr ; BBox3d b3Stm = pStmBasic->GetAllTriaBox() ; if ( b3Stm.IsEmpty()) return nullptr ; // numero di triangoli da analizzare int nTriaCnt = int( vAllTria.size()) ; // definisco un vettore di Flag per i triangoli già visitati INTVECTOR vIntFlags( pStmBasic->GetTriangleCount()) ; // numero massimo di thread concorrenti int nThreadMax = thread::hardware_concurrency() ; bool bOk = true ; BOOLVECTOR vbSafeTria( vAllTria.size(), true) ; if ( nThreadMax <= 1 || nTriaCnt < 50) ClassifyTrianglesMultiThread( vAllTria, 0, nTriaCnt - 1, *pStmBasic, dThick, dPrec, true, vbSafeTria) ; else { const int MAX_PARTS = 32 ; INTINTVECTOR vFstLst( MAX_PARTS) ; // calcolo le parti del vettore int nPartCnt = min( nThreadMax, MAX_PARTS) ; int nPartDim = nTriaCnt / nPartCnt + 1 ; for ( int i = 0 ; i < nPartCnt ; ++ i) { vFstLst[i].first = i * nPartDim ; vFstLst[i].second = min( ( i + 1) * nPartDim, nTriaCnt) - 1 ; } // processo le parti future vRes[MAX_PARTS] ; for ( int i = 0 ; i < nPartCnt ; ++ i) { vRes[i] = async( launch::async, &ClassifyTrianglesMultiThread, cref( vAllTria), vFstLst[i].first, vFstLst[i].second, cref( *pStmBasic), dThick, dPrec, true, ref( vbSafeTria)) ; } // attendo i risultati int nFin = 0 ; while ( nFin < nPartCnt) { for ( int i = 0 ; i < nPartCnt ; ++ i) { if ( vRes[i].valid() && vRes[i].wait_for( chrono::nanoseconds{ 1}) == future_status::ready) { bOk = vRes[i].get() && bOk ; ++ nFin ; } } } } if ( ! bOk) return nullptr ; TRIA3DEXVECTOR vTriaSafe ; vTriaSafe.reserve( vAllTria.size()) ; #if DEBUG TRIA3DEXVECTOR vTriaUnSafe ; vTriaUnSafe.reserve( vAllTria.size()) ; #endif for ( int i = 0 ; i < int( vAllTria.size()) ; ++ i) { if ( vbSafeTria[i]) vTriaSafe.emplace_back( vAllTria[i]) ; #if DEBUG if ( ! vbSafeTria[i]) vTriaUnSafe.emplace_back( vAllTria[i]) ; #endif } // definisco la superficie con i soli triangoli validi StmFromTriangleSoup TriaSoup ; TriaSoup.Start() ; for ( const Triangle3d& SafeTria : vTriaSafe) TriaSoup.AddTriangle( SafeTria) ; TriaSoup.End() ; PtrOwner pStmOffs( TriaSoup.GetSurf()) ; if ( IsNull( pStmOffs) || ! pStmOffs->IsValid() || pStmOffs->GetTriangleCount() == 0) return nullptr ; #if DEBUG LOG_INFO( GetEGkLogger(), ( string{ "Tria Time ( exceed Approx) : "} + ToString( PC.Stop())).c_str()) ; StmFromTriangleSoup _invalidSoup ; _invalidSoup.Start() ; for ( const Triangle3d& _unsafeTria : vTriaUnSafe) _invalidSoup.AddTriangle( _unsafeTria) ; _invalidSoup.End() ; VT.emplace_back( pStmOffs->Clone()) ; VC.emplace_back( LIME) ; VT.emplace_back( _invalidSoup.GetSurf()) ; VC.emplace_back( RED) ; SaveGeoObj( VT, VC, "C:\\Temp\\TriangleSelection.nge") ; VT.clear() ; VC.clear() ; PC.Start() ; #endif // recupero i loops della superficie originaria e del suo Offset orientato ( non devono essere diminuiti) POLYLINEVECTOR vPL, vPLOffs ; if ( ! pStm->GetLoops( vPL) || ! pStmOffs->GetLoops( vPLOffs)) return nullptr ; // trasformo ogni loop in curve composite ( devono essere chiuse) ICRVCOMPOPOVECTOR vCompoLoops ; vCompoLoops.reserve( vPL.size()) ; for ( const PolyLine& PL : vPL) { if ( PL.IsClosed()) { if ( ! vCompoLoops.emplace_back( CreateCurveComposite()) || ! vCompoLoops.back()->FromPolyLine( PL) || ! vCompoLoops.back()->IsValid()) return nullptr ; } } ICRVCOMPOPOVECTOR vCompoOffsLoops ; vCompoOffsLoops.reserve( vPLOffs.size()) ; for ( const PolyLine& PLOffs : vPLOffs) { if ( PLOffs.IsClosed()) { if ( ! vCompoOffsLoops.emplace_back( CreateCurveComposite()) || ! vCompoOffsLoops.back()->FromPolyLine( PLOffs) || ! vCompoOffsLoops.back()->IsValid()) return nullptr ; } } #if DEBUG VT.emplace_back( pStmOffs->Clone()) ; VC.emplace_back( YELLOW) ; for ( ICurveComposite* pCompo : vCompoLoops) { VT.emplace_back( pCompo->Clone()) ; VC.emplace_back( AQUA) ; } for ( ICurveComposite* pCompoOffs : vCompoOffsLoops) { VT.emplace_back( pCompoOffs->Clone()) ; VC.emplace_back( ORANGE) ; } SaveGeoObj( VT, VC, "C:\\Temp\\myCurve.nge") ; VT.clear() ; VC.clear() ; #endif // per ogni curva della superficie originale cerco la sua associata // NB. per la creazione della superficie ruled la prima curva è quellla che determina il verso // dei triangoli associati. Per un corretto ed automatico orientamento della superficie // la prima curva deve essere sempre definita dalla superficie originale ed invertita ( le // curve nella rigata devono seguire lo stesso orientamento ISURFTMPOVECTOR vStmRuled ; vStmRuled.reserve( vCompoLoops.size()) ; BOOLVECTOR vIndMatched( vCompoOffsLoops.size(), false) ; for ( ICurveComposite* pCompoLoop : vCompoLoops) { // sposto il punto iniziale della curva nel tratto più lungo double dMaxLen = - INFINITO ; int nIndCrv = 0 ; for ( int nCrv = 0 ; nCrv < pCompoLoop->GetCurveCount() ; ++ nCrv) { const ICurve* pCurve = pCompoLoop->GetCurve( nCrv) ; if ( pCurve != nullptr && pCurve->IsValid()) { double dCurrLen = 0. ; pCurve->GetLength( dCurrLen) ; if ( dCurrLen > dMaxLen) { dMaxLen = dCurrLen ; nIndCrv = nCrv ; } } } pCompoLoop->ChangeStartPoint( nIndCrv + 0.5) ; Point3d ptStart ; pCompoLoop->GetStartPoint( ptStart) ; // dalle altre curve derivanti dalla superficie di Offset cerco quella più vicina al punto inziale double dMinSqDist = INFINITO ; int nIndOffsCrv = -1 ; Point3d ptMinDist ; for ( int nOffsCrv = 0 ; nOffsCrv < int( vCompoOffsLoops.size()) ; ++ nOffsCrv) { if ( vIndMatched[nOffsCrv]) continue ; // recupero la curva e calcolo la distanza const ICurveComposite* pCompoOffsLoop = vCompoOffsLoops[nOffsCrv] ; int nFlag = 0 ; Point3d ptCurrMinDist ; if ( DistPointCurve( ptStart, *pCompoOffsLoop).GetMinDistPoint( 0., ptCurrMinDist, nFlag)) { double dCurrSqDist = SqDist( ptStart, ptCurrMinDist) ; if ( dCurrSqDist < dMinSqDist) { dMinSqDist = dCurrSqDist ; nIndOffsCrv = nOffsCrv ; ptMinDist = ptCurrMinDist ; } } } if ( nIndOffsCrv == -1) return nullptr ; vIndMatched[ nIndOffsCrv] = true ; // associo le due curve ICurveComposite* pCompoOffsLoop = vCompoOffsLoops[nIndOffsCrv] ; double dParMinDist = 0. ; pCompoOffsLoop->GetParamAtPoint( ptMinDist, dParMinDist, 10. * EPS_SMALL) ; pCompoOffsLoop->ChangeStartPoint( dParMinDist) ; #if DEBUG Color _cCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; VT.emplace_back( pCompoLoop->Clone()) ; VC.emplace_back( _cCol) ; VT.emplace_back( pCompoOffsLoop->Clone()) ; VC.emplace_back( _cCol) ; #endif // creo la superficie tra queste due curve e la oriento in modo da definire un volume pCompoLoop->Invert() ; PtrOwner pStmRuled( GetSurfTriMeshRuled( pCompoLoop, pCompoOffsLoop, ISurfTriMesh::RuledType::RLT_MINDIST)) ; if ( IsNull( pStmRuled) || ! pStmRuled->IsValid() || ! vStmRuled.emplace_back( Release( pStmRuled))) return nullptr ; #if DEBUG LOG_INFO( GetEGkLogger(), ( string{ "Strip generation : "} + ToString( PC.Stop())).c_str()) ; VT.emplace_back( vStmRuled.back()->Clone()) ; VC.emplace_back( _cCol) ; _cCol.SetAlpha( .5) ; #endif } #if DEBUG SaveGeoObj( VT, VC, "C:\\Temp\\Strips.nge") ; PC.Start() ; #endif // compongo la superficie finale PtrOwner pStmOrig( CloneSurfTriMesh( pStm)) ; if ( IsNull( pStmOrig) || ! pStmOrig->IsValid()) return nullptr ; PtrOwner pStmRef( Release( pStmOrig)) ; if ( IsNull( pStmRef) || ! pStmRef->IsValid()) return nullptr ; for ( int nStrip = 0 ; nStrip < int( vStmRuled.size()) ; ++ nStrip) { if ( ! pStmRef->DoSewing( *vStmRuled[nStrip])) return nullptr ; } if ( ! pStmRef->DoSewing( *pStmOffs)) return nullptr ; pStmRef->Repair() ; #if DEBUG LOG_INFO( GetEGkLogger(), ( string{ "Sewing : "} + ToString( PC.Stop())).c_str()) ; #endif return ( ( ! IsNull( pStmRef) && pStmRef->IsValid()) ? Release( pStmRef) : nullptr) ; }