diff --git a/EgtGeomKernel.rc b/EgtGeomKernel.rc index c47a7b8..04141b8 100644 Binary files a/EgtGeomKernel.rc and b/EgtGeomKernel.rc differ diff --git a/SurfTriMeshOffset.cpp b/SurfTriMeshOffset.cpp index 0710ba8..dd1841a 100644 --- a/SurfTriMeshOffset.cpp +++ b/SurfTriMeshOffset.cpp @@ -9,6 +9,7 @@ // 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. // //---------------------------------------------------------------------------- @@ -16,11 +17,17 @@ #include "stdafx.h" #include "VolZmap.h" #include "SurfTriMesh.h" -#include "EgtDev/Include/EGkDistPointSurfTm.h" -#include "\EgtDev\Include\EGkSurfTriMeshAux.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" @@ -58,6 +65,255 @@ SumStm( const CISURFTMPVECTOR& vStm) 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 //---------------------------------------------------------------------------- @@ -94,123 +350,138 @@ CreateSurfTriMeshesOffset( const CISURFTMPVECTOR& vStm, double dOffs, double dPr return SumStm( vStm) ; // creo lo Zmap associato alle superfici TriMesh - VolZmap OneVolZmap ; - if ( ! OneVolZmap.CreateFromTriMeshOffset( vStm, dOffs, dMyPrec, nType)) + VolZmap myVolZmap ; + if ( ! myVolZmap.CreateFromTriMeshOffset( vStm, dOffs, dMyPrec, nType)) return nullptr ; - if ( ! OneVolZmap.IsValid()) + if ( ! myVolZmap.IsValid()) return nullptr ; // recupero le superfici aperte CISURFTMPVECTOR vStmOpen ; for ( const ISurfTriMesh* pStm : vStm) { - if ( pStm != nullptr && pStm->IsValid() && ! pStm->IsClosed()) + 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 ( OneVolZmap.GetSurfTriMesh()) ; + return ( myVolZmap.GetSurfTriMesh()) ; } - // --- se ho delle superfici aperte - // lo Zmap creato è orientato e definisce una superficie chiusa; devo rimuovere i triangoli in eccesso - - // anzitutto controllo che lo Zmap sia valido - if ( ! OneVolZmap.IsValid()) - return nullptr ; - - // inzializzo la superficie TriMesh da restituire - PtrOwner pStm( CreateBasicSurfTriMesh()) ; - if ( IsNull( pStm) || ! pStm->Init( 3, 1)) - return nullptr ; - PointGrid3d VertGrid ; VertGrid.Init( 50000) ; - - // tolleranza di vicinanza alla superficie - double dTolDist = 30. * EPS_SMALL ; - - #if DEBUG - VT.emplace_back( OneVolZmap.Clone()) ; - VC.emplace_back( BLACK) ; - #endif - - // ciclo lungo i blocchi dello ZMap - for ( int nB = 0 ; nB < OneVolZmap.GetBlockCount() ; ++ nB) { - - // recupero i triangoli + // --- se ho delle superfici chiuse + TRIA3DEXVECTOR vAllTria, vTriaOffs ; + for ( int nB = 0 ; nB < myVolZmap.GetBlockCount() ; ++ nB) { TRIA3DEXVECTOR vTria, vTriaSafe ; - OneVolZmap.GetBlockTriangles( nB, vTria) ; - - // un triangolo viene ritenuto valido se è non è troppo vicino ( dOffs) alle superfici aperte + myVolZmap.GetBlockTriangles( nB, vTria) ; + #if DEBUG + TRIA3DVECTOR vTriaUnsafe ; + #endif for ( int nT = 0 ; nT < int( vTria.size()) ; ++ nT) { - - // recupero il triangolo Triangle3dEx& Tria = vTria[nT] ; - - // scorro le superficie aperte - bool bInsert = true ; - for ( int nS = 0 ; bInsert && nS < int( vStm.size()) ; ++ nS) { - - // controllo se posso inserirlo - vector vDistPtStm ; - for ( int i = 0 ; i < 3 && bInsert ; ++ i) { - double dDist = 0. ; - vDistPtStm.emplace_back( DistPointSurfTm( Tria.GetP( i), *vStm[nS])) ; - bInsert = ( vDistPtStm.back().GetDist( dDist) && dDist > abs( dOffs) - dTolDist) ; - } - // se il triangolo è al più a distanza di |dOffs| - dTolDist - if ( bInsert) { - // recupero i triangoli a distanza minima dai vertici del triangolo corrente - bool bPerp = true ; - for ( int i = 0 ; i < 3 && bPerp ; ++ i) { - INTVECTOR vTria ; - vDistPtStm[i].GetMinDistTriaIndices( vTria) ; - for ( int j = 0 ; j < int( vTria.size()) && bPerp ; ++ j) { - Triangle3d TriaCloser ; - vStm[nS]->GetTriangle( vTria[j], TriaCloser) ; - bPerp = ( abs( Tria.GetN() * TriaCloser.GetN()) < dTolDist) ; - } - } - // se tutti i triangoli a distanza minima sono perpendicolari, allora non lo inserisco - bInsert = ( ! bPerp) ; + 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) ; } } - - // se triangolo da inserire - if ( bInsert) - vTriaSafe.emplace_back( Tria) ; - - #if DEBUG - ICurveComposite* pCompo = CreateCurveComposite() ; - pCompo->AddPoint( Tria.GetP( 0)) ; - pCompo->AddLine( Tria.GetP( 1)) ; - pCompo->AddLine( Tria.GetP( 2)) ; - pCompo->Close() ; - Color myCol = ( bInsert ? Color( 0., 1., 0., .5) : Color( 1., 0., 0., .5)) ; - VT.emplace_back( CloneCurveComposite( pCompo)) ; - VC.emplace_back( myCol) ; - ISurfFlatRegion* pSfrTria = CreateSurfFlatRegion() ; - pSfrTria->AddExtLoop( pCompo) ; - VT.emplace_back( pSfrTria) ; - VC.emplace_back( myCol) ; - #endif } - - // inserisco tutti i triangoli validi - if ( ! pStm->AddTriaFromZMap( vTriaSafe, VertGrid)) - return nullptr ; + 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 - - // sistemo la topologia - if ( ! pStm->AdjustTopologyFromZMap()) - return nullptr ; - - return ( Release( pStm)) ; + return ( Release( pStmOffs)) ; } //---------------------------------------------------------------------------- @@ -240,3 +511,280 @@ CreateSurfTriMeshesThickeningOffset( const CISURFTMPVECTOR& vStm, double dOffs, // 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 ; + // 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) ; +} \ No newline at end of file diff --git a/VolZmap.h b/VolZmap.h index ee53485..c8d6f76 100644 --- a/VolZmap.h +++ b/VolZmap.h @@ -472,15 +472,18 @@ class VolZmap : public IVolZmap, public IGeoObjRW bool UpdateVolZMapBySurfThickeningSharpedOffset( const ISurfTriMesh* Surf, int nType, double dOffs, double dTol) ; bool CreateOffsetSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid, int nVertexType = 0) ; bool CreateOffsetCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, int nGrid, int nVertexType = 0) ; - bool CreateFatOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs, bool bThickle) ; + bool CreateFatOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs, bool bThickle, int nTool = 0) ; bool CreateOrientedOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs) ; bool SubtractIntervalsForOffset( int nGrid, int nI, int nJ, double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, int nToolNum, bool bSkipSwap = false) ; bool AddIntervalsForOffset( int nGrid, int nI, int nJ, double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, - int nToolNum, bool bSkipSwap = false) ; + int nToolMin, int nToolMax, bool bSkipSwap = false) ; bool CutByPlaneForOffset( const Plane3d& plCut) ; + bool AddSurfTmForOffset( const ISurfTriMesh* pStm, int nTool) ; + bool AddMapPartForOffset( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig, + const ISurfTriMesh& Surf, int nTool, IntersParLinesSurfTm& intPLSTM) ; // Funzioni per Offset di Zmap bool OffsetFillet( double dOffs) ; bool OffsetSharped( double dOffs, int nType) ; diff --git a/VolZmapOffset.cpp b/VolZmapOffset.cpp index 6331891..a4e62d8 100644 --- a/VolZmapOffset.cpp +++ b/VolZmapOffset.cpp @@ -275,10 +275,357 @@ VolZmap::SubtractIntervalsForOffset( int nGrid, int nI, int nJ, bool VolZmap::AddIntervalsForOffset( int nGrid, int nI, int nJ, double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, - int nToolNum, bool bSkipSwap) + int nToolMin, int nToolMax, bool bSkipSwap) { - // TODO -- Aggiustare eventuali tolleranze - return AddIntervals( nGrid, nI, nJ, dMin, dMax, vtNMin, vtNMax, nToolNum, bSkipSwap) ; + // Controllo che il numero di griglia sia entro i limiti + if ( nGrid < 0 || nGrid > 2) + return false ; + + // Controllo che indici nI, nJ siano entro i limiti + if ( nI < 0 || nI >= m_nNx[nGrid] || + nJ < 0 || nJ >= m_nNy[nGrid]) + return false ; + + // Controllo che dMin < dMax + Vector3d vtNmi = vtNMin ; + Vector3d vtNma = vtNMax ; + if ( dMin > dMax) { + swap( dMin, dMax) ; + swap( vtNmi, vtNma) ; + } + + // Restringo minimo e massimo entro i limiti della mappa + if ( dMin < m_dMinZ[nGrid]) { + dMin = m_dMinZ[nGrid] ; + if ( ! bSkipSwap) + vtNmi = - Z_AX ; + } + else if ( dMin > m_dMaxZ[nGrid]) { + dMin = m_dMaxZ[nGrid] ; + if ( ! bSkipSwap) + vtNmi = - Z_AX ; + } + if ( dMax < m_dMinZ[nGrid]) { + dMax = m_dMinZ[nGrid] ; + if ( ! bSkipSwap) + vtNma = Z_AX ; + } + else if ( dMax > m_dMaxZ[nGrid]) { + dMax = m_dMaxZ[nGrid] ; + if ( ! bSkipSwap) + vtNma = Z_AX ; + } + + // Controllo che dMin e dMax non siano quasi coincidenti + if ( abs( dMax - dMin) < EPS_SMALL) + return true ; + + // Riporto le coordinate cicliche nell'ordine di partenza + if ( !bSkipSwap && nGrid == 1) { + swap( vtNmi.x, vtNmi.z) ; + swap( vtNmi.y, vtNmi.z) ; + swap( vtNma.x, vtNma.z) ; + swap( vtNma.y, vtNma.z) ; + } + else if ( !bSkipSwap && nGrid == 2) { + swap( vtNmi.y, vtNmi.z) ; + swap( vtNmi.x, vtNmi.z) ; + swap( vtNma.y, vtNma.z) ; + swap( vtNma.x, vtNma.z) ; + } + + // Calcolo nPos + int nPos = nJ * m_nNx[nGrid] + nI ; + vector& vDexel = m_Values[nGrid][nPos] ; + + bool bModified = false ; + + // Non esistono segmenti + if ( vDexel.empty()) { + + vDexel.emplace_back() ; + vDexel.back().dMin = dMin ; + vDexel.back().vtMinN = vtNmi ; + vDexel.back().nToolMin = nToolMin ; + vDexel.back().dMax = dMax ; + vDexel.back().vtMaxN = vtNma ; + vDexel.back().nToolMax = nToolMax ; + + m_OGrMgr.Reset() ; + + bModified = true ; + } + // Esiste almeno un segmento + else { + // Cerco l'ultimo intervallo a sinistra e l'ultimo intervallo a destra + // di quello da aggiungere, che non interferiscono con quest'ultimo. + auto itLastLeft = vDexel.end() ; + auto itFirstRight = vDexel.end() ; + for ( auto it = vDexel.begin() ; it != vDexel.end() ; ++ it) { + if ( dMin > it->dMax + EPS_SMALL) + itLastLeft = it ; + if ( dMax < it->dMin - EPS_SMALL && itFirstRight == vDexel.end()) + itFirstRight = it ; + } + // Esistono intervalli a sinistra. + if ( itLastLeft != vDexel.end()) { + // Intervallo successivo all'ultimo a sinistra + auto itNextToLastLeft = itLastLeft ; + ++ itNextToLastLeft ; + // Il successivo non esiste. + if ( itNextToLastLeft == vDexel.end()) { + // Aggiungo il nuovo segmento + vDexel.emplace_back() ; + vDexel.back().dMin = dMin ; + vDexel.back().dMax = dMax ; + vDexel.back().vtMinN = vtNmi ; + vDexel.back().vtMaxN = vtNma; + vDexel.back().nToolMin = nToolMin ; + vDexel.back().nToolMax = nToolMax ; + bModified = true ; + } + // Il successivo esiste. + else { + // Il successivo è il primo a destra. + if ( itNextToLastLeft == itFirstRight) { + // Inserisco nuovo segmento + Data NewSegment ; + NewSegment.dMin = dMin ; + NewSegment.dMax = dMax ; + NewSegment.vtMinN = vtNmi ; + NewSegment.vtMaxN = vtNma ; + NewSegment.nToolMin = nToolMin ; + NewSegment.nToolMax = nToolMax ; + //NewSegment.nCompo = ; + vDexel.insert( itFirstRight, NewSegment) ; + bModified = true ; + } + else { + // Il successivo non esce a sinistra da quello da aggiungere. + if ( itNextToLastLeft->dMin > dMin + EPS_SMALL) { + itNextToLastLeft->dMin = dMin ; + itNextToLastLeft->vtMinN = vtNmi ; + itNextToLastLeft->nToolMin = nToolMin ; + } + // Cerco l'ultimo segmento che interferisce con quello da aggiungere. + auto itPrevToFirstRight = vDexel.end() ; + for ( auto it = itNextToLastLeft ; it != itFirstRight ; ++ it) { + itPrevToFirstRight = it ; + } + // L'ultimo che interferisce non esce a destra da quello da aggiungere. + if ( itPrevToFirstRight->dMax < dMax - EPS_SMALL) { + itNextToLastLeft->dMax = dMax ; + itNextToLastLeft->vtMaxN = vtNma ; + itNextToLastLeft->nToolMax = nToolMax ; + bModified = true ; + } + else { + itNextToLastLeft->dMax = itPrevToFirstRight->dMax ; + itNextToLastLeft->vtMaxN = itPrevToFirstRight->vtMaxN ; + itNextToLastLeft->nToolMax = nToolMax ; + bModified = true ; + } + auto itFirstToCancel = itNextToLastLeft ; + ++ itFirstToCancel ; + vDexel.erase( itFirstToCancel, itFirstRight) ; + } + } + } + // Non esistono neanche a destra. + else if ( itFirstRight == m_Values[nGrid][nPos].end()) { + // Il primo intervallo non sporge a sinistra + if ( vDexel.begin()->dMin > dMin + EPS_SMALL) { + vDexel.begin()->dMin = dMin ; + vDexel.begin()->vtMinN = vtNmi ; + vDexel.begin()->nToolMin = nToolMin ; + bModified = true ; + } + // L'ultimo intervallo sporge a destra. + if ( m_Values[nGrid][nPos].back().dMax > dMax + EPS_SMALL) { + // Ci sono più segmenti, inglobo tutti nel primo. + if ( vDexel.back().dMax > vDexel.begin()->dMax + EPS_SMALL) { + vDexel.begin()->dMax = vDexel.back().dMax ; + vDexel.begin()->vtMaxN = vDexel.back().vtMaxN ; + vDexel.begin()->nToolMax = nToolMax ; + bModified = true ; + } + } + // L'ultimo intervallo non sporge a destra. + else { + vDexel.begin()->dMax = dMax ; + vDexel.begin()->vtMaxN = vtNma ; + vDexel.begin()->nToolMax = nToolMax ; + bModified = true ; + } + vDexel.erase( vDexel.begin() + 1, vDexel.end()) ; + } + // A destra esistono. + else { + // Tutti i segmenti sono a destra di qullo da aggiungere. + if ( itFirstRight == vDexel.begin()) { + // Inserisco nuovo segmento- + Data NewSegment ; + NewSegment.dMin = dMin ; + NewSegment.dMax = dMax ; + NewSegment.vtMinN = vtNmi ; + NewSegment.vtMaxN = vtNma ; + NewSegment.nToolMin = nToolMin ; + NewSegment.nToolMax = nToolMax ; + vDexel.insert( vDexel.begin(), NewSegment) ; + bModified = true ; + } + else { + // Se il primo segmento non esce a sinistra da quello da aggiungere, cambio l'inizio. + if ( vDexel.begin()->dMin > dMin + EPS_SMALL) { + vDexel.begin()->dMin = dMin ; + vDexel.begin()->vtMinN = vtNmi ; + vDexel.begin()->nToolMin = nToolMin ; + bModified = true ; + } + // Cerco l'ultimo segmento che interferisce con quello da aggiungere. + auto itPrevToFirstRight = vDexel.begin() ; + for ( auto it = m_Values[nGrid][nPos].begin() ; it != itFirstRight ; ++ it) { + itPrevToFirstRight = it ; + } + // L'ultimo che interferisce non esce a destra da quello da aggiungere. + if ( itPrevToFirstRight->dMax < dMax - EPS_SMALL) { + vDexel.begin()->dMax = dMax ; + vDexel.begin()->vtMaxN = vtNma ; + vDexel.begin()->nToolMax = nToolMax ; + bModified = true ; + } + else { + vDexel.begin()->dMax = itPrevToFirstRight->dMax ; + vDexel.begin()->vtMaxN = itPrevToFirstRight->vtMaxN ; + vDexel.begin()->nToolMax = nToolMax ; + bModified = true ; + } + auto itFirstToCancel = vDexel.begin() ; + ++ itFirstToCancel ; + vDexel.erase( itFirstToCancel, itFirstRight) ; + } + } + } + + // Se nessuna modifica, esco + if ( ! bModified) + return true ; + + // Imposto ricalcolo della grafica + m_OGrMgr.Reset() ; + // Imposto forma generica + m_nShape = GENERIC ; + // Imposto ricalcolo numero di componenti connesse + m_nConnectedCompoCount = - 1 ; + + // Passo da indici di dexel a indici di voxel + nI /= m_nDexVoxRatio ; + nJ /= m_nDexVoxRatio ; + + // Determino quali blocchi sono stati modificati + if ( nGrid == 0) { + // Voxel lungo X + int nXStop = 1 ; + int nXBlock[2] ; + nXBlock[0] = min( nI / m_nVoxNumPerBlock, m_nFracLin[0] - 1) ; + if ( nI % N_VOXBLOCK == 0 && nXBlock[0] > 0) { + nXBlock[1] = nXBlock[0] - 1 ; + ++ nXStop ; + } + // Voxel lungo Y + int nYStop = 1 ; + int nYBlock[2] ; + nYBlock[0] = min( nJ / m_nVoxNumPerBlock, m_nFracLin[1] - 1) ; + if ( nJ % N_VOXBLOCK == 0 && nYBlock[0] > 0) { + nYBlock[1] = nYBlock[0] - 1 ; + ++ nYStop ; + } + // Voxel lungo Z + int nVoxNumZ = int( m_nNy[1] / m_nDexVoxRatio + ( m_nNy[1] % m_nDexVoxRatio == 0 ? 1 : 2)) ; + int nMinK = Clamp( int( floor( ( ( dMin - 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) - EPS_SMALL))), 0, nVoxNumZ - 2) ; + int nMaxK = Clamp( int( floor( ( ( dMax + 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) + EPS_SMALL))), 0, nVoxNumZ - 2) ; + int nMinZBlock = ( m_nMapNum == 1 ? 0 : Clamp( nMinK / int( m_nVoxNumPerBlock), 0, int( m_nFracLin[2] - 1))) ; + int nMaxZBlock = min( int( m_nFracLin[2] - 1), nMaxK / int( m_nVoxNumPerBlock)) ; + // Assegno flag ai voxel + for ( int tI = 0 ; tI < nXStop ; ++ tI) { + for ( int tJ = 0 ; tJ < nYStop ; ++ tJ) { + for ( int k = nMinZBlock ; k <= nMaxZBlock ; ++ k) { + int nBlockNum = k * m_nFracLin[0] * m_nFracLin[1] + nYBlock[tJ] * m_nFracLin[0] + nXBlock[tI] ; + m_BlockToUpdate[nBlockNum] = true ; + } + } + } + } + + else if ( nGrid == 1) { + // Voxel lungo Y + int nYStop = 1 ; + int nYBlock[2] ; + nYBlock[0] = min( nI / m_nVoxNumPerBlock, m_nFracLin[1] - 1) ; + if ( nI % N_VOXBLOCK == 0 && nYBlock[0] > 0) { + nYBlock[1] = nYBlock[0] - 1 ; + ++ nYStop ; + } + // Voxel lungo Z + int nZStop = 1 ; + int nZBlock[2] ; + nZBlock[0] = min( nJ / m_nVoxNumPerBlock, m_nFracLin[2] - 1) ; + if ( nJ % N_VOXBLOCK == 0 && nZBlock[0] > 0) { + nZBlock[1] = nZBlock[0] - 1 ; + ++ nZStop ; + } + // Voxel lungo X + int nVoxNumX = int( m_nNx[0] / m_nDexVoxRatio + ( m_nNx[0] % m_nDexVoxRatio == 0 ? 1 : 2)) ; + int nMinI = Clamp( int( floor( ( ( dMin - 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) - EPS_SMALL))), 0, nVoxNumX - 2) ; + int nMaxI = Clamp( int( floor( ( ( dMax + 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) + EPS_SMALL))), 0, nVoxNumX - 2) ; + int nMinXBlock = Clamp( nMinI / int( m_nVoxNumPerBlock), 0, int( m_nFracLin[0] - 1)) ; + int nMaxXBlock = min( int( m_nFracLin[0] - 1), nMaxI / int( m_nVoxNumPerBlock)) ; + // Assegno flag ai voxel + for ( int tI = 0 ; tI < nYStop ; ++ tI) { + for ( int tJ = 0 ; tJ < nZStop ; ++ tJ) { + for ( int k = nMinXBlock ; k <= nMaxXBlock ; ++ k) { + int nBlockNum = nZBlock[tJ] * m_nFracLin[0] * m_nFracLin[1] + nYBlock[tI] * m_nFracLin[0] + k ; + m_BlockToUpdate[nBlockNum] = true ; + } + } + } + } + + else if ( nGrid == 2) { + // Voxel lungo X + int nXStop = 1 ; + int nXBlock[2] ; + nXBlock[0] = min( nJ / m_nVoxNumPerBlock, m_nFracLin[0] - 1) ; + if ( nJ % N_VOXBLOCK == 0 && nXBlock[0] > 0) { + nXBlock[1] = nXBlock[0] - 1 ; + ++ nXStop ; + } + // Voxel lungo Z + int nZStop = 1 ; + int nZBlock[2] ; + nZBlock[0] = min( nI / m_nVoxNumPerBlock, m_nFracLin[2] - 1) ; + if ( nI % N_VOXBLOCK == 0 && nZBlock[0] > 0) { + nZBlock[1] = nZBlock[0] - 1 ; + ++ nZStop ; + } + // Voxel lungo Y + int nVoxNumY = int( m_nNy[0] / m_nDexVoxRatio + ( m_nNy[0] % m_nDexVoxRatio == 0 ? 1 : 2)) ; + int nMinJ = Clamp( int( floor( ( ( dMin - 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) - EPS_SMALL))), 0, nVoxNumY - 2) ; + int nMaxJ = Clamp( int( floor( ( ( dMax + 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) + EPS_SMALL))), 0, nVoxNumY - 2) ; + int nMinYBlock = Clamp( nMinJ / int( m_nVoxNumPerBlock), 0, int( m_nFracLin[1] - 1)) ; + int nMaxYBlock = min( int( m_nFracLin[1] - 1), nMaxJ / int( m_nVoxNumPerBlock)) ; + // Assegno flag ai voxel + for ( int tI = 0 ; tI < nZStop ; ++ tI) { + for ( int tJ = 0 ; tJ < nXStop ; ++ tJ) { + for ( int k = nMinYBlock ; k <= nMaxYBlock ; ++ k) { + int nBlockNum = nZBlock[tI] * m_nFracLin[0] * m_nFracLin[1] + k * m_nFracLin[0] + nXBlock[tJ] ; + m_BlockToUpdate[nBlockNum] = true ; + } + } + } + } + + return true ; } //---------------------------------------------------------------------------- @@ -349,6 +696,194 @@ VolZmap::CutByPlaneForOffset( const Plane3d& plCut) return true ; } +//---------------------------------------------------------------------------- +// Funzione per aggiungere allo ZMap quello di una TriMesh chiusa +// --------------------------------------------------------------------------- +bool +VolZmap::AddSurfTmForOffset( const ISurfTriMesh* pStm, int nTool) +{ + // controllo sulla superficie + double dVol ; + if ( pStm == nullptr || ! pStm->IsValid() || ! pStm->IsClosed() || + ! pStm->GetVolume( dVol) || dVol < 0) + return false ; + + // controllo se il Box3d della superficie si interseca con il Box3d dello Zmap corrente + BBox3d BBox_stm, BBox_curr ; + if ( ! pStm->GetLocalBBox( BBox_stm) || ! GetLocalBBox( BBox_curr)) + return false ; + BBox3d BBox_inters ; + if ( BBox_stm.FindIntersection( BBox_curr, BBox_inters) && BBox_inters.IsEmpty()) + return true ; // se non ci sono intersezioni, la superficie non influenza lo Zmap + Vector3d vtLen = BBox_curr.GetMax() - BBox_curr.GetMin() ; // dimensione massima dello spillone + + // ciclo sulle griglie + bool bCompleted = true ; + for ( int nG = 0 ; nG < m_nMapNum ; ++ nG) { + // definisco dei sistemi di riferimento ausiliari + Frame3d frMapFrame ; + if ( nG == 0) + frMapFrame = m_MapFrame ; + else if ( nG == 1) + frMapFrame.Set( m_MapFrame.Orig(), Y_AX, Z_AX, X_AX) ; + else if ( nG == 2) + frMapFrame.Set( m_MapFrame.Orig(), Z_AX, X_AX, Y_AX) ; + + // oggetto per calcolo massivo intersezioni + IntersParLinesSurfTm intPLSTM( frMapFrame, *pStm) ; + + // numero massimo di thread + int nThreadMax = max( 1, int( thread::hardware_concurrency()) - 1) ; + vector> vRes ; + vRes.resize( nThreadMax) ; + // se dimensione griglia in X maggiore di dimensione Y + if ( m_nNx[nG] > m_nNy[nG]) { + int nDexNum = m_nNx[nG] / nThreadMax ; + int nRemainder = m_nNx[nG] % nThreadMax ; + int nInfI = 0 ; + int nSupI = 0 ; + // aggiungo le parti interessate alla mappa + for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { + nInfI = nSupI ; + nSupI = nInfI + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; + vRes[nThread] = async( launch::async, &VolZmap::AddMapPartForOffset, this, nG, + nInfI, nSupI, 0, m_nNy[nG], ref( vtLen), ref( m_MapFrame.Orig()), + ref( *pStm), nTool, ref( intPLSTM)) ; + } + } + // se dimensione griglia in Y maggiore di dimensione X + else { + int nDexNum = m_nNy[nG] / nThreadMax ; + int nRemainder = m_nNy[nG] % nThreadMax ; + int nInfJ = 0 ; + int nSupJ = 0 ; + // aggiungo le parti interessate alla mappa + for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { + nInfJ = nSupJ ; + nSupJ = nInfJ + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; + vRes[nThread] = async( launch::async, &VolZmap::AddMapPartForOffset, this, nG, + 0, m_nNx[nG], nInfJ, nSupJ, ref( vtLen), ref( m_MapFrame.Orig()), + ref( *pStm), nTool, ref( intPLSTM)) ; + } + } + + // ciclo per attendere che tutti gli async abbiano terminato. + int nTerminated = 0 ; + while ( nTerminated < nThreadMax) { + for ( int nL = 0 ; nL < nThreadMax ; ++ nL) { + // async terminato + if ( vRes[nL].valid() && vRes[nL].wait_for( chrono::microseconds{ 1}) == future_status::ready) { + ++ nTerminated ; + bCompleted = bCompleted && vRes[nL].get() ; + } + } + } + + if ( ! bCompleted) + return false ; + } + + return true ; +} + +//---------------------------------------------------------------------------- +// Funzione per aggiungere gli Spilloni di una TriMesh chiusa allo ZMap corrente +//---------------------------------------------------------------------------- +bool +VolZmap::AddMapPartForOffset( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, + const Vector3d& vtLen, const Point3d& ptMapOrig, + const ISurfTriMesh& Surf, int nTool, IntersParLinesSurfTm& intPLSTM) +{ + // controllo sui parametri + if ( nMap < 0 || nMap > 2 || + nInfI < 0 || nInfI > m_nNx[nMap] || + nSupI < 0 || nSupI > m_nNx[nMap] || + nInfJ < 0 || nInfJ > m_nNy[nMap] || + nSupJ < 0 || nSupJ > m_nNy[nMap]) + return false ; + + // determinazione e ridimensionamento dei dexel interni alla trimesh + for ( int i = nInfI ; i < nSupI ; ++ i) { + for ( int j = nInfJ ; j < nSupJ ; ++ j) { + + // definisco la retta da intersecare con la trimesh + double dX = ( i + 0.5) * m_dStep ; + double dY = ( j + 0.5) * m_dStep ; + Point3d ptP0( dX, dY, 0) ; + + // intersezioni della retta con la TriMesh + ILSIVECTOR IntersectionResults ; + intPLSTM.GetInters( ptP0, vtLen.v[(nMap+2)%3], IntersectionResults) ; + + // rimuovo le intersezioni in eccesso + for ( int nI = 0 ; nI < int( IntersectionResults.size()) - 3 ; ++ nI) { + int nJ = nI + 1 ; // prima successiva + int nK = nJ + 1 ; // seconda successiva + int nT = nK + 1 ; // terza successiva + // determino i segni delle 4 intersezioni tra la linea e il trangolo della TriMesh + int nSgnI = IntersectionResults[nI].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nI].dCosDN > -EPS_SMALL ? 0 : - 1 ; + int nSgnJ = IntersectionResults[nJ].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nJ].dCosDN > -EPS_SMALL ? 0 : - 1 ; + int nSgnK = IntersectionResults[nK].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nK].dCosDN > -EPS_SMALL ? 0 : - 1 ; + int nSgnT = IntersectionResults[nT].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nT].dCosDN > -EPS_SMALL ? 0 : - 1 ; + // parametri dell'intersezione sulla linea + double dUJ = IntersectionResults[nJ].dU ; + double dUK = IntersectionResults[nK].dU ; + // controllo coerenza con segni... + if ( nSgnI != 0 && nSgnI == nSgnJ && + nSgnK != 0 && nSgnK == nSgnT && + nSgnI == - nSgnT && + abs( dUJ - dUK) < EPS_SMALL) { + // ... ed elimino le intersezioni in eccesso... + IntersectionResults.erase( IntersectionResults.begin() + nK) ; + IntersectionResults.erase( IntersectionResults.begin() + nJ) ; + } + } + + int nInt = int( IntersectionResults.size()) ; // numero di intersezioni valide + bool bInside = false ; // Flag entrata/uscita per tratto di retta + Point3d ptIn ; Vector3d vtInN ; + + // per ogni intersezione valida trovata... + int nFlagIn = 0 ; + for ( int k = 0 ; k < nInt ; ++ k) { + // ricavo il tipo di intersezione + int nIntType = IntersectionResults[k].nILTT ; + // se c'è intersezione + if ( nIntType != ILTT_NO) { + // ricavo il cos tra i vettori ( normale del triangolo e tangente alla retta) + double dCos = IntersectionResults[k].dCosDN ; + + // se entro nella superficie trimesh... + if ( dCos < - EPS_SMALL) { + ptIn = IntersectionResults[k].ptI ; // punto di intersezione + int nT = IntersectionResults[k].nT ; // triangolo di interesse + int nF = Surf.GetFacetFromTria( nT) ; // faccia di interesse + Surf.GetFacetNormal( nF, vtInN) ; // normale della faccia + Surf.GetTFlag( nT, nFlagIn) ; // nFlag entrata dalla faccia + bInside = true ; // entrata + } + // ...se esco dalla superficie trimesh ( prima sono per forza entrato) + else if ( dCos > EPS_SMALL && bInside) { + Point3d ptOut = IntersectionResults[k].ptI ; // punto di intersezione + int nT = IntersectionResults[k].nT ; // triangolo di interesse + int nF = Surf.GetFacetFromTria( nT) ; // faccia di interesse + Vector3d vtOutN ; Surf.GetFacetNormal( nF, vtOutN) ; // vettore d'uscita + int nFlagOut = 0 ; Surf.GetTFlag( nT, nFlagOut) ; // nFlag uscita dalla faccia + + // Aggiungo un tratto al dexel + AddIntervalsForOffset( nMap, i, j, + ptIn.v[( nMap + 2) % 3] - ptMapOrig.v[( nMap + 2) % 3], + ptOut.v[( nMap + 2) % 3] - ptMapOrig.v[( nMap + 2) % 3], + vtInN, vtOutN, nTool, nTool, true) ; + bInside = false ; // uscita + } + } + } + } + } + + return true ; +} //---------------------------------------------------------------------------- // Funzione per la creazione di una sfera di Offset centrata sul vertice di una TriMesh con cui @@ -381,7 +916,7 @@ VolZmap::CreateOffsetSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid Vector3d vtNmax = Point3d( dX, dY, dMax) - ptV ; vtNmax.Normalize() ; if ( dOffs > 0.) - AddIntervalsForOffset( nGrid, i, j, dMin, dMax, vtNmin, vtNmax, nTool) ; + AddIntervalsForOffset( nGrid, i, j, dMin, dMax, vtNmin, vtNmax, nTool, nTool) ; else SubtractIntervalsForOffset( nGrid, i, j, dMin, dMax, -vtNmin, -vtNmax, nTool) ; } @@ -396,7 +931,8 @@ VolZmap::CreateOffsetSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid // aggiungere o sottrarre intervalli lungo i Dexel coinvolti. //---------------------------------------------------------------------------- bool -VolZmap::CreateOffsetCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, int nGrid, int nTool) +VolZmap::CreateOffsetCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, + int nGrid, int nTool) { // determino la lunghezza dello spigolo corrente double dH = Dist( ptP1, ptP2) ; @@ -435,7 +971,7 @@ VolZmap::CreateOffsetCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, d if ( IntersLineCylinder( ptC, Z_AX, CylFrame, dH, abs( dOffs), true, true, ptInt1, vtN1, ptInt2, vtN2)) { if ( dOffs > 0.) - AddIntervalsForOffset( nGrid, i, j, ptInt1.z, ptInt2.z, -vtN1, -vtN2, nTool) ; + AddIntervalsForOffset( nGrid, i, j, ptInt1.z, ptInt2.z, -vtN1, -vtN2, nTool, nTool) ; else SubtractIntervalsForOffset( nGrid, i, j, ptInt1.z, ptInt2.z, vtN1, vtN2, nTool) ; } @@ -450,7 +986,8 @@ VolZmap::CreateOffsetCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, d // aggiungere o sottrarre intervalli lungo i Dexel coinvolti //---------------------------------------------------------------------------- bool -VolZmap::CreateFatOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs, bool bThickle) +VolZmap::CreateFatOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs, bool bThickle, + int nTool) { // verifico la validità della superficie if ( Surf == nullptr) @@ -489,10 +1026,11 @@ VolZmap::CreateFatOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs, b } return false ; } + // aggiorno gli spilloni // se Offset Thickle allora sommo tutti i contributi senza tener conto del segno dell'Offset if ( bThickle) { - AddSurfTm( pStmExtr) ; + AddSurfTmForOffset( pStmExtr, nTool) ; } // se Offset orientato, allora aggiungo o sottraggo gli intervalli a seconda del segno di Offset else { @@ -1024,7 +1562,6 @@ VolZmap::UpdateVolZMapByClosedSurfSharpedOffset( const ISurfTriMesh* Surf, int n return true ; } - //---------------------------------------------------------------------------- // [Fillet] Funzione per aggiornare mediante Sfere, Cilindri e Facce di estrusione lo ZMap corrente // mediante una superficie aperta @@ -1039,25 +1576,75 @@ VolZmap::UpdateVolZMapByOpenSurfFilletOffset( const ISurfTriMesh* Surf, double d if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) return true ; - // Assunzioni : - // - Idea Generale di Offset - // - Su ogni faccia viene definita una superficie di estrusione ( dove le basi sono - // definite dalla traslazione o in positivo o in negativo della faccia lungo la sua normale - // orientata coerentemente in base al segno dell'offset) - // - Su ogni lato viene definita una porzione di cilindro orientata ( estrusione di uno spicchio) - // - Su ogni vertice viene definita una sfera che viene tagliata con i piani definiti - // dalle facce delle porzioni di cilindro + // definisco vettore di frame Locali alle 3 griglie + FRAME3DVECTOR vFrGrid( 4) ; + vFrGrid[0].Set( ORIG, X_AX, Y_AX, Z_AX) ; + vFrGrid[1].Set( m_MapFrame.Orig(), m_MapFrame.VersX(), m_MapFrame.VersY(), m_MapFrame.VersZ()) ; + vFrGrid[2].Set( m_MapFrame.Orig(), m_MapFrame.VersY(), m_MapFrame.VersZ(), m_MapFrame.VersX()) ; + vFrGrid[3].Set( m_MapFrame.Orig(), m_MapFrame.VersZ(), m_MapFrame.VersX(), m_MapFrame.VersY()) ; - // ----------------------- Facce ----------------------- - if ( ! CreateOrientedOffsetExtrusionFace( Surf, dOffs)) - return true ; + // creo lo Zmap di Thickening : + // l'Offset di thickening può essere scomposto in 3 parti : + // - Offset positivo + // - Offset negativo + // - Raccordi + // l'idea è proprio quella di rimuovere i raccordi dall'Offset thickening mediante + // sottrazione di ZMap ( e successivamente classificare il Side dei triangoli) - // definisco una mappa tra vertici e contorni orientati degli ZMap derivanti dagli Edges adiacenti - // }, { vector }> - unordered_map, vector>> vMapVertBorders( Surf->GetVertexCount()) ; - - // ----------------------- spicchi ----------------------- + // creazione dello ZMap di Thicking della superficie + // [NB. potrebbero esserci modifiche ed ottimizzazioni, quindi non richiamo la funzione di + // Offset thicking direttamente ma ne copio le varie parti] [TODO] + INTVECTOR vOpenEdges ; vOpenEdges.reserve( Surf->GetEdgeCount()) ; + INTVECTOR vOpenVertex ; vOpenVertex.reserve( Surf->GetVertexCount()) ; + BOOLVECTOR vbVert( Surf->GetVertexCount(), false) ; for ( int nE = 0 ; nE < Surf->GetEdgeCount() ; ++ nE) { + int nV1, nV2, nF1, nF2 ; double dAng ; + Surf->GetEdge( nE, nV1, nV2, nF1, nF2, dAng) ; + // se non è un Edge libero, passo al successivo + if ( nF1 == SVT_NULL || nF2 == SVT_NULL) { + vOpenEdges.push_back( nE) ; + vOpenVertex.push_back( nV1) ; + vOpenVertex.push_back( nV2) ; + continue ; + } + Point3d ptP1 ; Surf->GetVertex( nV1, ptP1) ; + Point3d ptP2 ; Surf->GetVertex( nV2, ptP2) ; + for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { + ptP1.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; + ptP2.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; + if ( ! CreateOffsetCylinderOnEdge( ptP1, ptP2, abs( dOffs), nGrid, 0)) + return false ; + if ( ! vbVert[nV1]) { + if ( ! CreateOffsetSphereOnVertex( ptP1, abs( dOffs), nGrid, 0)) + return false ; + } + if ( ! vbVert[nV2]) { + if ( ! CreateOffsetSphereOnVertex( ptP2, abs( dOffs), nGrid, 0)) + return false ; + } + } + vbVert[nV1] = true ; + vbVert[nV2] = true ; + } + if ( ! CreateFatOffsetExtrusionFace( Surf, abs( dOffs), true, 0)) + return false ; + + #if DEBUG + VT.emplace_back( this->Clone()) ; + VC.emplace_back( FUCHSIA) ; + #endif + + // creo lo Zmap derivante dai semi cilindri orientati + VolZmap VolZMapHalfBorder ; + VolZMapHalfBorder.CreateEmpty( m_MapFrame.Orig(), + m_dMaxZ[1] - m_dMinZ[1], m_dMaxZ[2] - m_dMinZ[2], m_dMaxZ[0] - m_dMinZ[0], + m_dStep, true) ; + // salvo i piani di taglio per le sfere durante la creazione dei cilindri + unordered_map> vMapVertBorders ; vMapVertBorders.reserve( Surf->GetVertexCount()) ; + + // ----------------------- semicilindri ----------------------- + const double dNewOffs = abs( dOffs) + 3. * m_dStep + 10. * EPS_SMALL ; + for ( const int& nE : vOpenEdges) { // recupero lo spigolo int nV1, nV2, nF1, nF2 ; double dAng ; Surf->GetEdge( nE, nV1, nV2, nF1, nF2, dAng) ; @@ -1066,161 +1653,110 @@ VolZmap::UpdateVolZMapByOpenSurfFilletOffset( const ISurfTriMesh* Surf, double d Point3d ptP2 ; Surf->GetVertex( nV2, ptP2) ; // definisco il vettore estrusione uscente da ptP1 ed entrante in ptP2 Vector3d vtEdge = ( ptP2 - ptP1) ; + vtEdge.Normalize() ; + + #if DEBUG + CurveLine line ; line.Set( ptP1, ptP2) ; + VT.emplace_back( line.Clone()) ; + Color cCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; + VC.emplace_back( cCol) ; + #endif + // recupero le normali delle facce - Vector3d vtN1, vtN2 ; - Surf->GetFacetNormal( nF1, vtN1) ; - Surf->GetFacetNormal( nF2, vtN2) ; - // --- se lato libero - if ( nF1 == SVT_NULL || nF2 == SVT_NULL) { - // --- non definisco lo spicchio ma aggiungo i piani di taglio - Vector3d vtFaceN = ( nF1 != SVT_NULL ? vtN1 : vtN2) ; - Vector3d vtEdgeN = vtEdge ; - vtEdgeN.Normalize() ; - Vector3d vtPlaneN = vtEdgeN ^ vtFaceN ; - Point3d ptCheck = ptP1 + ( 0.5 * vtEdge) + 5. * EPS_SMALL * vtPlaneN ; - INTVECTOR vnTria ; - Surf->GetAllTriaInFacet( nF1 != SVT_NULL ? nF1 : nF2, vnTria) ; - bool bMirror = false ; - for ( int& nInd : vnTria) { - Triangle3dEx Tria ; - if ( Surf->GetTriangle( nInd, Tria) && IsPointInsideTriangle( ptCheck, Tria, TriangleType::EXACT)) { - bMirror = true ; - break ; + Vector3d vtFaceN ; + if ( nF1 == SVT_NULL) + Surf->GetFacetNormal( nF2, vtFaceN) ; + else + Surf->GetFacetNormal( nF1, vtFaceN) ; + + #if DEBUG + CurveArc myArc ; myArc.SetCPAN( ptP1, ptP1 + dNewOffs * vtFaceN, 360., 0, vtEdge) ; + myArc.SetExtrusion( vtEdge) ; + //myArc.SetThickness( ( ptP2 - ptP1).Len()) ; + CICURVEPVECTOR _vCurve ; + _vCurve.emplace_back( myArc.Clone()) ; + ISurfTriMesh* A = GetSurfTriMeshByRegionExtrusion( _vCurve, ( ptP2 - ptP1).Len() * vtEdge, EPS_SMALL) ; + VT.emplace_back( A) ; + VC.emplace_back( cCol) ; + #endif + + Vector3d vtPlaneN = vtEdge ^ vtFaceN ; + Plane3d plCut ; + plCut.Set( ptP1, - vtPlaneN) ; + // creazione del cilindro + VolZmap VolZMapHalfCyl ; + VolZMapHalfCyl.CreateEmpty( m_MapFrame.Orig(), + m_dMaxZ[1] - m_dMinZ[1], m_dMaxZ[2] - m_dMinZ[2], m_dMaxZ[0] - m_dMinZ[0], + m_dStep, true) ; + for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { + // esprimo gli estremi nel riferimento della griglia + ptP1.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; + ptP2.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; + VolZMapHalfCyl.CreateOffsetCylinderOnEdge( ptP1, ptP2, dNewOffs, nGrid, 0) ; + } + // taglio del cilindro con il piano + VolZMapHalfCyl.CutByPlaneForOffset( plCut) ; + Surf->GetVertex( nV1, ptP1) ; + Surf->GetVertex( nV2, ptP2) ; + for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { + // esprimo gli estremi nel riferimento della griglia + ptP1.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; + ptP2.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; + // asse del cilindro + Vector3d vtV = ptP2 - ptP1 ; vtV.Normalize() ; + // calcolo box del cilindro + BBox3d BBoxCylinder ; + BBoxCylinder.Add( ptP1) ; + BBoxCylinder.Add( ptP2) ; + if ( AreSameOrOppositeVectorApprox( vtV, X_AX)) + BBoxCylinder.Expand( 0., dNewOffs, dNewOffs) ; + else if ( AreSameOrOppositeVectorApprox( vtV, Y_AX)) + BBoxCylinder.Expand( dNewOffs, 0., dNewOffs) ; + else if ( AreSameOrOppositeVectorApprox( vtV, Z_AX)) + BBoxCylinder.Expand( dNewOffs, dNewOffs, 0.) ; + else { + double dExpandX = dNewOffs * sqrt( 1 - vtV.x * vtV.x) ; + double dExpandY = dNewOffs * sqrt( 1 - vtV.y * vtV.y) ; + double dExpandZ = dNewOffs * sqrt( 1 - vtV.z * vtV.z) ; + BBoxCylinder.Expand( dExpandX, dExpandY, dExpandZ) ; + } + // determino gli intervalli di interesse mediante intersezione + int nStartI = max( 0, int( BBoxCylinder.GetMin().x / m_dStep)) ; + int nEndI = min( m_nNx[nGrid] - 1, int( BBoxCylinder.GetMax().x / m_dStep)) ; + int nStartJ = max( 0, int( BBoxCylinder.GetMin().y / m_dStep)) ; + int nEndJ = min( m_nNy[nGrid] - 1, int( BBoxCylinder.GetMax().y / m_dStep)) ; + // aggiorno gli spilloni interessati + for ( int i = nStartI ; i <= nEndI ; ++ i) { + for ( int j = nStartJ ; j <= nEndJ ; ++ j) { + // recupero il Dexel + int nPos = j * VolZMapHalfCyl.m_nNx[nGrid] + i ; + vector& vDexel = VolZMapHalfCyl.m_Values[nGrid][nPos] ; + // scorro i suoi intervalli + for ( auto& Interval : vDexel) { + // aggiungo i contributi + VolZMapHalfBorder.AddIntervalsForOffset( nGrid, i, j, Interval.dMin, Interval.dMax, + Interval.vtMinN, Interval.vtMaxN, 0, 0, true) ; + } } } - Plane3d plCut ; - if ( plCut.Set( ptP1, bMirror ? - vtPlaneN : vtPlaneN)) { - #if DEBUG - IGeoVector3d* vt = CreateGeoVector3d() ; - vt->Set( plCut.GetVersN()) ; - vt->Translate( ptP1 - ORIG) ; - VT.emplace_back( vt->Clone()) ; - VC.emplace_back( YELLOW) ; - vt->Set( plCut.GetVersN()) ; - vt->Translate( ptP2 - ORIG) ; - VT.emplace_back( vt->Clone()) ; - VC.emplace_back( YELLOW) ; - #endif - vMapVertBorders[nV1].first.emplace_back( plCut) ; - vMapVertBorders[nV2].first.emplace_back( plCut) ; - } - } - // --- se lato tra due facce definite - else { - // se non serve lo spicchio di estrusione, passo al prossimo Edge - if ( dAng * dOffs < 0.) - continue ; - // se angolo concavo, le normali vanno invertite - if ( dAng < 0.) { - vtN1.Invert() ; - vtN2.Invert() ; - } - // definisco la base dello spicchio - PolyLine PLBase ; - // recupero frame intrinseco definito dal piano della base - Frame3d frLoc ; - if ( ! frLoc.Set( ptP1, vtN1 ^ vtN2, vtN1)) - continue ; - // costruisco la base - double dPar = -1. ; - PLBase.AddUPoint( ++ dPar, ptP1) ; - CurveArc MyCrvArc ; - Point3d ptA = ptP1 + abs( dOffs) * vtN1 ; - Point3d ptC = ptP1 + abs( dOffs) * vtN2 ; - Vector3d vtTmp = vtN1 + vtN2 ; - vtTmp.Normalize() ; - vtTmp *= abs( dOffs) ; - Point3d ptB = ptP1 + vtTmp ; - MyCrvArc.Set3P( ptA, ptB, ptC) ; - PolyLine PLCrvArc ; - MyCrvArc.ApproxWithLines( 10. * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PLCrvArc) ; - for ( auto& ptU : PLCrvArc.GetUPointList()) - PLBase.AddUPoint( ++ dPar, ptU.first) ; - PLBase.AddUPoint( ++ dPar, ptP1) ; - // definisco la superficie di estrusione e aggiorno gli spilloni - CurveComposite CompoBase ; - CompoBase.FromPolyLine( PLBase) ; - PtrOwner pStmClove( GetSurfTriMeshByExtrusion( &CompoBase, vtEdge, true)) ; - if ( ! IsNull( pStmClove)) { - // aggiungo gli intervalli definiti dalla superficie di estrusione - AddSurfTm( pStmClove) ; - // determino la normale del piano della curva composita definita - Plane3d PlanePL ; - double dArea ; - CompoBase.GetArea( PlanePL, dArea) ; - // per ptP1 la normale del piano di taglio deve essere come vtEdge, mentre per ptP2 opposta. - // ( In questo modo seguo l'orientamento dalla superficie di estrusione ) - Vector3d vEdgeN = vtEdge ; - vEdgeN.Normalize() ; - if ( AreSameVectorEpsilon( PlanePL.GetVersN(), vEdgeN, 50. * EPS_SMALL)) - CompoBase.Invert() ; - CompoBase.SetExtrusion( - vEdgeN) ; - // aggiungo i piani di taglio alla mappa - // --- il piano definito dalle basi dello spicchio - Plane3d plCut ; - if ( plCut.Set( ptP1, vEdgeN)) - vMapVertBorders[nV1].second.emplace_back( plCut) ; - if ( plCut.Set( ptP2, - vEdgeN)) - vMapVertBorders[nV2].second.emplace_back( plCut) ; - #if DEBUG - Point3d ptCentroid ; CompoBase.GetCentroid( ptCentroid) ; - IGeoPoint3d* pt = CreateGeoPoint3d() ; - pt->Set( ptCentroid) ; - VT.emplace_back( pt->Clone()) ; - VC.emplace_back( PURPLE) ; - IGeoVector3d* vt = CreateGeoVector3d() ; - vt->Set( vEdgeN) ; - vt->Translate( ptCentroid - ORIG) ; - VT.emplace_back( vt->Clone()) ; - VC.emplace_back( FUCHSIA) ; - pt->Translate( vEdgeN) ; - VT.emplace_back( pt->Clone()) ; - VC.emplace_back( PURPLE) ; - vt->Set( - vEdgeN) ; - vt->Translate( ( ptCentroid + vtEdge) - ORIG) ; - VT.emplace_back( vt->Clone()) ; - VC.emplace_back( FUCHSIA) ; - VT.emplace_back( CompoBase.Clone()) ; - VC.emplace_back( RED) ; - CompoBase.Translate( vtEdge) ; - VT.emplace_back( CompoBase.Clone()) ; - VC.emplace_back( RED) ; - CompoBase.Translate( - vtEdge) ; - #endif - } } + // recupero i due piani di taglio definiti + Surf->GetVertex( nV1, ptP1) ; + Surf->GetVertex( nV2, ptP2) ; + Plane3d plCut1 ; plCut1.Set( ptP1, vtEdge) ; + Plane3d plCut2 ; plCut2.Set( ptP2, - vtEdge) ; + vMapVertBorders[nV1].push_back( plCut1) ; + vMapVertBorders[nV2].push_back( plCut2) ; + vMapVertBorders[nV1].push_back( plCut) ; + vMapVertBorders[nV2].push_back( plCut) ; } - #if DEBUG - VT.emplace_back( Surf->Clone()) ; - VC.emplace_back( GREEN) ; - VT.emplace_back( this->Clone()) ; - VC.emplace_back( Color( 0., 0., 0., .5)) ; - SaveGeoObj( VT, VC, "C:\\Temp\\ZmapOffs.nge") ; - #endif - - // definisco vettore di frame Locali alle 3 griglie - FRAME3DVECTOR vFrGrid( 4) ; - vFrGrid[0].Set( ORIG, X_AX, Y_AX, Z_AX) ; - vFrGrid[1].Set( m_MapFrame.Orig(), m_MapFrame.VersX(), m_MapFrame.VersY(), m_MapFrame.VersZ()) ; - vFrGrid[2].Set( m_MapFrame.Orig(), m_MapFrame.VersY(), m_MapFrame.VersZ(), m_MapFrame.VersX()) ; - vFrGrid[3].Set( m_MapFrame.Orig(), m_MapFrame.VersZ(), m_MapFrame.VersX(), m_MapFrame.VersY()) ; - - // scrorro tutti i vertici della superficie - for ( auto& Map : vMapVertBorders) { - - // recupero l'indice del vertice - int nV = Map.first ; - - // se il vertice non ha piani derivanti dalle facce, non lo considero - if ( vMapVertBorders[nV].second.empty()) - continue ; - + // ----------------------- sfere ----------------------- + for ( const int& nV : vOpenVertex) { // recupero il vertice corrente Point3d ptVertex ; Surf->GetVertex( nV, ptVertex) ; - - // definisco una sfera di raggio pari all' |Offset| con centro nel vertice + // definisco una sfera di raggio doppio rispetto all'offset con centro nel vertice VolZmap VolZMapCut ; VolZMapCut.CreateEmpty( m_MapFrame.Orig(), m_dMaxZ[1] - m_dMinZ[1], m_dMaxZ[2] - m_dMinZ[2], m_dMaxZ[0] - m_dMinZ[0], @@ -1230,34 +1766,19 @@ VolZmap::UpdateVolZMapByOpenSurfFilletOffset( const ISurfTriMesh* Surf, double d // esprimo il vertice corrente nel riferimento della griglia ptVertex.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; // aggiungo il contributo degli spilloni della griglia corrente - VolZMapCut.CreateOffsetSphereOnVertex( ptVertex, abs( dOffs), nGrid) ; + VolZMapCut.CreateOffsetSphereOnVertex( ptVertex, dNewOffs, nGrid, 0) ; } - - // taglio lo Zmap con tutti i piani ricavati ( evitando quelli ripetuti ) - // --- piani di FreeEdge - for ( int i = 0 ; i < int( vMapVertBorders[nV].first.size()) ; ++ i) - VolZMapCut.CutByPlaneForOffset( vMapVertBorders[nV].first[i]) ; - // --- piani dalle facce - for ( int i = 0 ; i < int( vMapVertBorders[nV].second.size()) - 1 ; ++ i) { - bool bCut = true ; - const Plane3d& plPlaneA = vMapVertBorders[nV].second[i] ; - for ( int j = i + 1 ; bCut && j < int( vMapVertBorders[nV].second.size()) ; ++ j) { - const Plane3d& plPlaneB = vMapVertBorders[nV].second[j] ; - bCut = ( ! AreSamePlaneApprox( plPlaneA, plPlaneB)) ; - } - if ( bCut) - VolZMapCut.CutByPlaneForOffset( plPlaneA) ; - } - VolZMapCut.CutByPlaneForOffset( vMapVertBorders[nV].second.back()) ; - + // taglio lo Zmap con tutti i piani ricavati + for ( int i = 0 ; i < int( vMapVertBorders[nV].size()) ; ++ i) + VolZMapCut.CutByPlaneForOffset( vMapVertBorders[nV][i]) ; // ciclo sulle griglie Surf->GetVertex( nV, ptVertex) ; for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { // esprimo il vertice corrente nel riferimento della griglia ptVertex.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; // determino il Box della sfera posizionata su tale vertice - BBox3d BBoxSphere( ptVertex - 2. * abs( dOffs) * Vector3d( 1., 1., 1.), - ptVertex + 2. * abs( dOffs) * Vector3d( 1., 1., 1.)) ; + BBox3d BBoxSphere( ptVertex - dNewOffs * Vector3d( 1., 1., 1.), + ptVertex + dNewOffs * Vector3d( 1., 1., 1.)) ; // determino gli intervalli di interesse mediante intersezione con Box della sfera int nStartI = max( 0, int( BBoxSphere.GetMin().x / VolZMapCut.m_dStep)) ; int nEndI = min( VolZMapCut.m_nNx[nGrid] - 1, int( BBoxSphere.GetMax().x / VolZMapCut.m_dStep)) ; @@ -1271,20 +1792,50 @@ VolZmap::UpdateVolZMapByOpenSurfFilletOffset( const ISurfTriMesh* Surf, double d vector& vDexel = VolZMapCut.m_Values[nGrid][nPos] ; // scorro i suoi intervalli for ( auto& Interval : vDexel) { - // aggiungo i contributi - AddIntervalsForOffset( nGrid, i, j, Interval.dMin, Interval.dMax, - Interval.vtMinN, Interval.vtMaxN, 0, true) ; + VolZMapHalfBorder.AddIntervalsForOffset( nGrid, i, j, Interval.dMin, Interval.dMax, + Interval.vtMinN, Interval.vtMaxN, 0, 0, true) ; } } } } } + #if DEBUG + VT.clear() ; VC.clear() ; + VT.emplace_back( Surf->Clone()) ; + VC.emplace_back( YELLOW) ; + VT.emplace_back( VolZMapHalfBorder.Clone()) ; + VC.emplace_back( BLACK) ; + SaveGeoObj( VT, VC, "C:\\Temp\\AllStrip.nge") ; + #endif + + for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { + // aggiorno gli spilloni interessati + for ( int i = 0 ; i < m_nNx[nGrid] ; ++ i) { + for ( int j = 0 ; j < m_nNy[nGrid] ; ++ j) { + // recupero il Dexel + int nPos = j * VolZMapHalfBorder.m_nNx[nGrid] + i ; + vector& vDexel = VolZMapHalfBorder.m_Values[nGrid][nPos] ; + // scorro i suoi intervalli + for ( auto& Interval : vDexel) { + AddIntervalsForOffset( nGrid, i, j, Interval.dMin, Interval.dMax, + Interval.vtMinN, Interval.vtMaxN, 0, 0, true) ; + } + } + } + } + + #if DEBUG + VT.emplace_back( this->Clone()) ; + VC.emplace_back( WHITE) ; + SaveGeoObj( VT, VC, "C:\\Temp\\this.nge") ; + #endif + return true ; } //---------------------------------------------------------------------------- -// [Sharped ( Chamfer/Extend )] Funzione per aggiornare mediantePrismi, Involucri concvessi +// [Sharped ( Chamfer/Extend )] Funzione per aggiornare mediantePrismi, Involucri convessi // e Facce di estrusione lo ZMap corrente mediante una superficie aperta // NB. Lo Zmap creato è orientato //----------------------------------------------------------------------------