From 626d5b0e51839670182ddc91e4981726f12aa48d Mon Sep 17 00:00:00 2001 From: Riccardo Elitropi Date: Mon, 16 Jun 2025 11:34:23 +0200 Subject: [PATCH] EgtGeomKernel 2.7f2 : - Aggiunte funzioni per calcolo di Offset per superfici chiuse TriMesh - Piccola miglioria alla triangolazione (con SaraP) - Migliorie per rimozioni TJunction, calcolo delle normali dei triangoli e creazione di una TriMesh a partire da uno ZMap (con SaraP) - Aggiunte funzioni di SubtractMap e piccole modifiche per estensione dei Box di creazione per gli Zmap. --- EgtGeomKernel.rc | Bin 11710 -> 11710 bytes EgtGeomKernel.vcxproj | 2 + EgtGeomKernel.vcxproj.filters | 6 + SurfTriMesh.cpp | 99 ++++++++++- SurfTriMesh.h | 15 +- SurfTriMeshOffset.cpp | 75 +++++++++ SurfTriMeshUtilities.cpp | 68 ++++---- Triangulate.cpp | 24 ++- Triangulate.h | 1 + VolZmap.cpp | 118 +++++++++++-- VolZmap.h | 19 ++- VolZmapCreation.cpp | 133 ++++++++++++--- VolZmapGraphics.cpp | 32 +++- VolZmapOffset.cpp | 300 ++++++++++++++++++++++++++++++++++ 14 files changed, 811 insertions(+), 81 deletions(-) create mode 100644 SurfTriMeshOffset.cpp create mode 100644 VolZmapOffset.cpp diff --git a/EgtGeomKernel.rc b/EgtGeomKernel.rc index cdc95ef3f0ae957f1f1ade7344b446ee3b842263..bfc65202e856703942494e91624054296ebcd77c 100644 GIT binary patch delta 81 zcmdlNy)SyhH#SD2&FAG5nSs + + diff --git a/EgtGeomKernel.vcxproj.filters b/EgtGeomKernel.vcxproj.filters index a313b23..6120010 100644 --- a/EgtGeomKernel.vcxproj.filters +++ b/EgtGeomKernel.vcxproj.filters @@ -546,6 +546,12 @@ File di origine\GeoDist + + File di origine\Geo + + + File di origine\Geo + diff --git a/SurfTriMesh.cpp b/SurfTriMesh.cpp index d7ff791..0461250 100644 --- a/SurfTriMesh.cpp +++ b/SurfTriMesh.cpp @@ -237,6 +237,10 @@ SurfTriMesh::AddTriangle( const int nIdVert[3], int nTFlag) // inserisco il triangolo try { m_vTria.emplace_back( nIdVert, nTFlag) ;} catch(...) { return SVT_NULL ;} + // aggiorno la sua normale + if ( ! vtN.Normalize( EPS_ZERO)) + return false ; + m_vTria.back().vtN = vtN ; // aggiorno massimo TFlag m_nMaxTFlag = max( m_nMaxTFlag, nTFlag) ; // ne determino l'indice @@ -1688,11 +1692,13 @@ SurfTriMesh::AdjustVertices( void) //---------------------------------------------------------------------------- bool -SurfTriMesh::AdjustAdjacencies( void) +SurfTriMesh::AdjustAdjacencies( bool AdjustVert) { // sistemo le relazioni tra triangoli e vertici - if ( ! AdjustVertices()) - return false ; + if ( AdjustVert) { + if ( ! AdjustVertices()) + return false ; + } // matrice di incidenza vertici-triangoli INTMATRIX mVertTria( GetVertexSize()) ; for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { @@ -1875,7 +1881,7 @@ SurfTriMesh::TestSealing( void) m_vTria[i].nIdAdjac[1] == SVT_NULL || m_vTria[i].nIdAdjac[2] == SVT_NULL) { bClosed = false ; - break ; + break ; } } } @@ -1937,7 +1943,7 @@ SurfTriMesh::PackVertices( void) ++ nFirstFree ; } else - vVId.push_back( nId) ; + vVId.push_back( nId) ; } else { if ( nFirstFree == SVT_NULL) @@ -4361,3 +4367,86 @@ SurfTriMesh::SetTempInt( int nId, int nTempInt) const m_vTria[nId].nTemp = nTempInt ; return true ; } + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::AddTriaFromZMap( const TRIA3DEXVECTOR& vTria, PointGrid3d& VertGrid, double dVertexTol) +{ + // scorro i triangoli + for ( const Triangle3dEx& Tria : vTria) { + // ciclo sui tre vertici + int nIdV[3]{} ; + for ( int nV = 0 ; nV < 3 ; ++ nV) { + // verifico se vertice già presente + int nId ; + if ( ! VertGrid.Find( Tria.GetP( nV), dVertexTol, nId)) { + // se non presente, lo aggiugo + nIdV[nV] = AddVertex( Tria.GetP( nV)) ; + if ( nIdV[nV] == SVT_NULL) + return false ; + VertGrid.InsertPoint( Tria.GetP( nV), nIdV[nV]) ; + } + else + nIdV[nV] = nId ; + } + // se i vertici sono tutti diversi tra loro, inserisco il triangolo + if ( nIdV[0] != nIdV[1] && nIdV[0] != nIdV[2] && nIdV[1] != nIdV[2]) { + int nT = AddTriangle( nIdV, Tria.GetGrade()) ; + if ( nT != SVT_NULL && nT != SVT_DEL) { + // associo relazione vertice-triangolo + for ( int i = 0 ; i < 3 ; ++ i) + m_vVert[nIdV[i]].nIdTria = nT ; + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::AdjustTopologyFromZMap( void) +{ + // cancello tutti i vertici che puntano a triangoli cancellati + bool bPack = false ; + for ( int i = 0 ; i < GetVertexSize() ; ++ i) { + if ( m_vVert[i].nIdTria == SVT_NULL) { + m_vVert[i].nIdTria = SVT_DEL ; + bPack = true ; + } + } + if ( bPack) + PackVertices() ; + m_nStatus = SurfTriMesh::OK ; + + // calcolo le adiacenze + if ( ! AdjustAdjacencies()) + return false ; + + // verifico l'orientamento ( TODO -- Da migliorare...) + // Ora per funzionare è necessario che il ciclo sui triangoli parta da un triangolo orientato + // correttamente e che i triangoli invertiti siano "circondati" da triangoli tutti orientati correttamente + if ( ! AdjustOrientations()) + return false ; + + // calcolo le facce + if ( ! VerifyFaceting()) + return false ; + + // rimozione delle TJunction + bool bModified = false ; + if ( ! RemoveTJunctions( bModified, SQ_EPS_SMALL)) + return false ; + if ( bModified) { + if ( ! AdjustVertices() || ! DoCompacting()) + return false ; + } + else + TestSealing() ; + + // semplifico le facce ( anche più piccola) + if ( ! SimplifyFacets( MAX_EDGE_LEN_STD, false, 5. * EPS_SMALL)) + LOG_ERROR( GetEGkLogger(), "Error in SimplifyFacets of Stm::AdjustTopologyFromZMap") + + return true ; +} diff --git a/SurfTriMesh.h b/SurfTriMesh.h index 90bf43d..36cc13a 100644 --- a/SurfTriMesh.h +++ b/SurfTriMesh.h @@ -18,6 +18,7 @@ #include "GeoObjRW.h" #include "/EgtDev/Include/EGkSurfTriMesh.h" #include "/EgtDev/Include/EGkHashGrids3d.h" +#include "/EgtDev/Include/EGkPointGrid3d.h" #include #include @@ -327,7 +328,7 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW bool Intersect( const ISurfTriMesh& Other) override ; bool Subtract( const ISurfTriMesh& Other) override ; bool GetSurfClassification( const ISurfTriMesh& ClassifierSurf, - INTVECTOR& vTriaIn, INTVECTOR& vTriaOut, INTVECTOR& vTriaOnP, INTVECTOR& vTriaOnM, INTVECTOR& vTriaIndef) override ; + INTVECTOR& vTriaIn, INTVECTOR& vTriaOut, INTVECTOR& vTriaOnP, INTVECTOR& vTriaOnM, INTVECTOR& vTriaIndef) override ; bool CutWithOtherSurf( const ISurfTriMesh& CutterSurf, bool bInVsOut, bool bSaveOnEq) override ; bool Repair( double dMaxEdgeLen = MAX_EDGE_LEN_STD) override ; bool GetAllTriaOverlapBox( const BBox3d& b3Box, INTVECTOR& vT) const override ; @@ -373,6 +374,8 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW bool GetTempInt( int nId, int& nTempInt) const ; bool ResetTempInts( void) const ; bool SetTempInt( int nId, int nTempInt) const ; + bool AddTriaFromZMap( const TRIA3DEXVECTOR& vTria, PointGrid3d& VertGrid, double dVertexTol = 2 * EPS_SMALL) ; + bool AdjustTopologyFromZMap( void) ; private : typedef std::vector VERTVECTOR ; @@ -388,7 +391,7 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW bool CopyFrom( const SurfTriMesh& clSrc) ; bool Validate( bool bCorrect = false) ; bool AdjustVertices( void) ; - bool AdjustAdjacencies( void) ; + bool AdjustAdjacencies( bool AdjustVert = true) ; bool AdjustOrientations( void) ; bool AdjustTriaOrientation( TRINTDEQUE& S3iQ) ; bool TestSealing( void) ; @@ -433,18 +436,16 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW bool IntersectTriMeshTriangle( SurfTriMesh& Other) ; bool IdentifyShells( void) const ; bool RemoveDoubleTriangles( bool& bModified) ; - bool RemoveTJunctions( bool& bModified) ; + bool RemoveTJunctions( bool& bModified, double dMinSqDist = SQ_EPS_TRIA_H) ; bool FlipTriangles( int nTA, int nTB) ; - bool SimplifyFacets( double dMaxEdgeLen = MAX_EDGE_LEN_STD, bool bForced = true) ; + bool SimplifyFacets( double dMaxEdgeLen = MAX_EDGE_LEN_STD, bool bForced = true, double dTolAlign = 50 * EPS_SMALL) ; bool AddChainToChain( const Chain& ChainToAdd, PNTVECTOR& OrigChain) ; bool DistPointFacet( const Point3d& ptP, const POLYLINEVECTOR& vPolyVec, double& dPointFacetDist) ; bool ChangeStart( const Point3d& ptNewStart, PNTVECTOR& Loop) ; bool SplitAtPoint( const Point3d& ptStop, const PNTVECTOR& Loop, PNTVECTOR& Loop1, PNTVECTOR& Loop2) ; - bool AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, int nF, bool& bModif) const ; + bool AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, double dTolAlign, bool& bModif) const ; bool RemoveInvalidTriangles( const INTVECTOR& vIds) ; bool FindAdjacentOnLongerEdge( int nT, int& nEdge, int& nAdjTrg) const ; - - private : ObjGraphicsMgr m_OGrMgr ; // gestore grafica dell'oggetto Status m_nStatus ; // stato diff --git a/SurfTriMeshOffset.cpp b/SurfTriMeshOffset.cpp new file mode 100644 index 0000000..666e863 --- /dev/null +++ b/SurfTriMeshOffset.cpp @@ -0,0 +1,75 @@ +//---------------------------------------------------------------------------- +// EgalTech 2014-2022 +//---------------------------------------------------------------------------- +// File : SurfTriMeshOffset.cpp Data : 10.06.25 Versione : 2.7e4 +// Contenuto : Implementazione funzione per Offset di Superfici TriMesh. +// +// +// +// Modifiche : 10.06.25 RE Creazione modulo. +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "SurfTriMesh.h" +#include "VolZmap.h" + +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)) ; +} + +//---------------------------------------------------------------------------- +/* Funzione che crea l'Offset di una superficie TriMesh chiusa */ +ISurfTriMesh* +CreateSurfTriMeshOffset( const ISurfTriMesh* pStm, double dOffs, double dLinTol) +{ + return CreateSurfTriMeshesOffset( { pStm}, dOffs, dLinTol) ; +} + +//---------------------------------------------------------------------------- +/* Funzione che crea l'Offset di un insieme di superfici */ +ISurfTriMesh* +CreateSurfTriMeshesOffset( const CISURFTMPVECTOR& vStm, double dOffs, double dLinTol) +{ + // se vettore delle superfici vuoto, non faccio nulla + if ( vStm.empty()) + return nullptr ; + // controllo sul valore di tolleranza lineare + double dMyLinTol = max( dLinTol, 10 * 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) ; + // --- NB. Per la creazione dello Zmap è necessario che le superfici siano chiuse + PtrOwner pVolZmap( CreateVolZmap()) ; + if ( IsNull( pVolZmap) || ! pVolZmap->CreateFromTriMeshOffset( vStm, dOffs, dMyLinTol)) + return nullptr ; + // restituisco la superficie TriMesh + return ( pVolZmap->GetSurfTriMesh()) ; +} \ No newline at end of file diff --git a/SurfTriMeshUtilities.cpp b/SurfTriMeshUtilities.cpp index b1f10f1..7174040 100644 --- a/SurfTriMeshUtilities.cpp +++ b/SurfTriMeshUtilities.cpp @@ -138,7 +138,7 @@ SurfTriMesh::FlipTriangles( int nTA, int nTB) //---------------------------------------------------------------------------- bool -SurfTriMesh::RemoveTJunctions( bool& bModified) +SurfTriMesh::RemoveTJunctions( bool& bModified, double dMinSqDist) { bModified = false ; @@ -147,6 +147,11 @@ SurfTriMesh::RemoveTJunctions( bool& bModified) // Ciclo sui triangoli della superficie per determinare gli altri vertici sul loro perimetro for ( int nT = 0 ; nT < int( m_vTria.size()) ; ++ nT) { + // se adiacenze tutte valide, passo al successivo + if ( m_vTria[nT].nIdAdjac[0] != SVT_DEL && m_vTria[nT].nIdAdjac[0] != SVT_NULL && + m_vTria[nT].nIdAdjac[1] != SVT_DEL && m_vTria[nT].nIdAdjac[1] != SVT_NULL && + m_vTria[nT].nIdAdjac[2] != SVT_DEL && m_vTria[nT].nIdAdjac[2] != SVT_NULL) + continue ; // Se il triangolo non è valido, passo al successivo Triangle3d trTria ; if ( ! GetTriangle( nT, trTria) || ! trTria.Validate( true)) @@ -190,9 +195,9 @@ SurfTriMesh::RemoveTJunctions( bool& bModified) if ( ! GetVertex( m_vTria[vNearTria[nI]].nIdVert[nVert], ptVert)) continue ; double dProj = ( ptVert - ptSegSt) * vtSeg ; - double dOrt = ( ( ptVert - ptSegSt) - dProj * vtSeg).SqLen() ; - if ( dProj > EPS_SMALL && dProj < dSegLen - EPS_SMALL && dOrt < SQ_EPS_TRIA_H) - vVertOtl.emplace_back( m_vTria[vNearTria[nI]].nIdVert[nVert]) ; + double dOrt = ( ( ptVert - ptSegSt) - dProj * vtSeg).SqLen() ; + if ( dProj > EPS_SMALL && dProj < dSegLen - EPS_SMALL && dOrt < dMinSqDist) + vVertOtl.emplace_back( m_vTria[vNearTria[nI]].nIdVert[nVert]) ; } } // Riordino i vertici sul segmento @@ -338,8 +343,10 @@ ChooseGoodStartPoint( PNTULIST& PointList) // ------------------------------------------------------------- bool -SurfTriMesh::AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, int nF, bool& bModif) const +SurfTriMesh::AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, double dTolAlign, bool& bModif) const { + // vettore dei loop della faccia adiacente + POLYLINEVECTOR LoopVec ; // Ciclo sui punti del loop auto itLast = PointList.begin() ; for ( auto it = next( itLast) ; it != PointList.end() ; ++ it) { @@ -347,30 +354,26 @@ SurfTriMesh::AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, int nF, bool& // bisogna fermarsi per analizzare il tratto corrente alla ricerca di punti allineati se dal punto corrente // inizia un tratto adiacente ad un'altra faccia oppure se il punto corrente non verrà eliminato dal loop // della faccia adiacente + bool bAnalyze = ( itLast->second != it->second) ; + if ( bAnalyze) + LoopVec.clear() ; if ( ! bAnalyze && itLast->second != - 1) { - // recupero i loop della faccia adiacente - POLYLINEVECTOR LoopVec ; - GetFacetLoops( int( itLast->second), LoopVec) ; - bool bFound = false ; - for ( int i = 0 ; i < int( LoopVec.size()) && ! bFound ; i ++) { + if ( LoopVec.empty()) + GetFacetLoops( int( itLast->second), LoopVec) ; + for ( int i = 0 ; i < int( LoopVec.size()) && ! bAnalyze ; i ++) { const PNTULIST& PointListAdj = LoopVec[i].GetUPointList() ; - for ( auto itAdj = PointListAdj.begin() ; itAdj != prev( PointListAdj.end()) && ! bFound ; ++ itAdj) { + int nSamePoints = 0 ; + for ( auto itAdj = PointListAdj.begin() ; itAdj != prev( PointListAdj.end()) ; ++ itAdj) { // cerco il punto corrente sul loop della faccia adiacente - if ( itAdj->second == nF && AreSamePointApprox( it->first, itAdj->first)) { - bFound = true ; - auto itPrev = ( itAdj == PointListAdj.begin() ? prev( prev( PointListAdj.end())) : prev( itAdj)) ; - auto itNext = next( itAdj) ; - DistPointLine PointLineDistCalc( itAdj->first, itPrev->first, itNext->first) ; - double dDist ; - if ( PointLineDistCalc.GetDist( dDist) && dDist > 50 * EPS_SMALL) - // se il punto non è allineato al precedente e al successivo non verrà rimosso dal loop della faccia adiacente, - // quindi non deve essere rimosso dal loop corrente altrimenti si genererebbe una T-Junction. Quindi posso - // interrompere e analizzare il tratto trovato fino ad ora - bAnalyze = true ; + if ( AreSamePointApprox( it->first, itAdj->first)) + ++ nSamePoints ; + if ( ( nSamePoints == 1 && int( PointListAdj.size()) <= 4) || nSamePoints > 1) { + bAnalyze = true ; + break ; } - } - } + } + } } if ( bAnalyze) { @@ -383,7 +386,7 @@ SurfTriMesh::AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, int nF, bool& } PL.AddUPoint( ++nPar, it->first) ; // Provo ad eliminare i punti allineati - PL.RemoveAlignedPoints( 50 * EPS_SMALL) ; + PL.RemoveAlignedPoints( dTolAlign) ; if ( PL.GetPointNbr() < nPar + 1) { // rimuovo dalla lista dei punti gli eliminati (salto gli estremi) int nUCurr = 1 ; @@ -475,7 +478,7 @@ SurfTriMesh::AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, int nF, bool& //---------------------------------------------------------------------------- bool -SurfTriMesh::SimplifyFacets( double dMaxEdgeLen, bool bForced) +SurfTriMesh::SimplifyFacets( double dMaxEdgeLen, bool bForced, double dTolAlign) { // La trimesh deve essere valida if ( ! IsValid()) @@ -502,13 +505,17 @@ SurfTriMesh::SimplifyFacets( double dMaxEdgeLen, bool bForced) // Lista dei punti del loop PNTULIST& PointList = LoopVec[nL].GetUPointList() ; + // Se il loop è un triangolo, non va modificato + if ( int( PointList.size()) <= 4) + continue ; + // Mi assicuro che il punto iniziale/finale non sia all'interno di un possibile segmento if ( ! ChooseGoodStartPoint( PointList)) continue ; // Sistemo il loop bool bModif = false ; - if ( ! AdjustLoop( PointList, dMaxEdgeLen, nF, bModif)) + if ( ! AdjustLoop( PointList, dMaxEdgeLen, dTolAlign, bModif)) return false ; if ( bModif) bToRetriangulate = true ; @@ -536,12 +543,11 @@ SurfTriMesh::SimplifyFacets( double dMaxEdgeLen, bool bForced) // Eseguo la ritriangolazione della faccia PNTVECTOR vPt ; INTVECTOR vTr ; - if ( Triangulate().Make( LoopVec, vPt, vTr)) { + if ( Triangulate().Make( LoopVec, vPt, vTr) && ! vTr.empty()) FacetMap.emplace( nF, make_pair( vPt, vTr)) ; - } // Se non riesco a triangolare anche solo questa faccia, interrompo tutto - else - return false ; + else + return false ; } } diff --git a/Triangulate.cpp b/Triangulate.cpp index e942a08..4be97fb 100644 --- a/Triangulate.cpp +++ b/Triangulate.cpp @@ -818,7 +818,7 @@ Triangulate::TestTriangle( const PNTVECTOR& vPt, const INTVECTOR& vPol, } } // If vertex k is inside the ear triangle, then this is not an ear - else if ( TestPointInTriangle( vPt[vPol[k]], vPt[vPol[vPrev[i]]], vPt[vPol[i]], vPt[vPol[vNext[i]]])) { + else if ( TestPointInOrOnTriangle( vPt[vPol[k]], vPt[vPol[vPrev[i]]], vPt[vPol[i]], vPt[vPol[vNext[i]]])) { bIsEar = false ; break ; } @@ -978,6 +978,28 @@ Triangulate::TestPointInTriangle( const Point3d& ptP, const Point3d& ptA, const return true ; } +//---------------------------------------------------------------------------- +// test if point p is inside or on the border of triangle (a, b, c) +bool +Triangulate::TestPointInOrOnTriangle( const Point3d& ptP, const Point3d& ptA, const Point3d& ptB, const Point3d& ptC) +{ + // If P is on a vertex is considered inside + if ( AreSamePoint( ptP, ptA)) + return true ; + if ( AreSamePoint( ptP, ptB)) + return true ; + if ( AreSamePoint( ptP, ptC)) + return true ; + // If P is on the right of at least one edge is outside + if ( TriangleIsCCW( ptA, ptP, ptB, EPS_SMALL)) + return false ; + if ( TriangleIsCCW( ptB, ptP, ptC, EPS_SMALL)) + return false ; + if ( TriangleIsCCW( ptC, ptP, ptA, EPS_SMALL)) + return false ; + return true ; +} + //---------------------------------------------------------------------------- bool Triangulate::SortInternalLoops( const POLYLINEVECTOR& vPL, INTVECTOR& vOrd) diff --git a/Triangulate.h b/Triangulate.h index 7145157..b0d9d91 100644 --- a/Triangulate.h +++ b/Triangulate.h @@ -45,6 +45,7 @@ class Triangulate bool TriangleIsCCW( const Point3d& ptA, const Point3d& ptB, const Point3d& ptC, double dToler = 0.1 * EPS_SMALL) ; bool TestIntersection( const Point3d& ptA1, const Point3d& ptA2, const Point3d& ptB1, const Point3d& ptB2) ; bool TestPointInTriangle( const Point3d& ptP, const Point3d& ptA, const Point3d& ptB, const Point3d& ptC) ; + bool TestPointInOrOnTriangle( const Point3d& ptP, const Point3d& ptA, const Point3d& ptB, const Point3d& ptC) ; bool SortInternalLoops( const POLYLINEVECTOR& vPL, INTVECTOR& vOrd) ; bool GetPntVectorFromPolyline( const PolyLine& PL, bool bXmaxStart, PNTVECTOR& vPi) ; bool GetOuterPntToJoin( const PNTVECTOR& vPt, const Point3d& ptP, int& nI) ; diff --git a/VolZmap.cpp b/VolZmap.cpp index bdb0987..35a6ecd 100644 --- a/VolZmap.cpp +++ b/VolZmap.cpp @@ -1520,14 +1520,14 @@ VolZmap::AddSurfTm( const ISurfTriMesh* pStm) // ciclo sulle griglie bool bCompleted = true ; - for ( int g = 0 ; g < m_nMapNum ; ++ g) { + for ( int nG = 0 ; nG < m_nMapNum ; ++ nG) { // definisco dei sistemi di riferimento ausiliari Frame3d frMapFrame ; - if ( g == 0) + if ( nG == 0) frMapFrame = m_MapFrame ; - else if ( g == 1) + else if ( nG == 1) frMapFrame.Set( m_MapFrame.Orig(), Y_AX, Z_AX, X_AX) ; - else if ( g == 2) + else if ( nG == 2) frMapFrame.Set( m_MapFrame.Orig(), Z_AX, X_AX, Y_AX) ; // oggetto per calcolo massivo intersezioni @@ -1538,33 +1538,121 @@ VolZmap::AddSurfTm( const ISurfTriMesh* pStm) vector> vRes ; vRes.resize( nThreadMax) ; // se dimensione griglia in X maggiore di dimensione Y - if ( m_nNx[g] > m_nNy[g]) { - int nDexNum = m_nNx[g] / nThreadMax ; - int nRemainder = m_nNx[g] % nThreadMax ; + 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::AddMapPart, this, g, - nInfI, nSupI, 0, m_nNy[g], ref( vtLen), ref( m_MapFrame.Orig()), - ref( *pStm), ref( intPLSTM)) ; + vRes[nThread] = async( launch::async, &VolZmap::AddMapPart, this, nG, + nInfI, nSupI, 0, m_nNy[nG], ref( vtLen), ref( m_MapFrame.Orig()), + ref( *pStm), ref( intPLSTM)) ; } } // se dimensione griglia in Y maggiore di dimensione X else { - int nDexNum = m_nNy[g] / nThreadMax ; - int nRemainder = m_nNy[g] % nThreadMax ; + 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::AddMapPart, this, g, - 0, m_nNx[g], nInfJ, nSupJ, ref( vtLen), ref( m_MapFrame.Orig()), - ref( *pStm), ref( intPLSTM)) ; + vRes[nThread] = async( launch::async, &VolZmap::AddMapPart, this, nG, + 0, m_nNx[nG], nInfJ, nSupJ, ref( vtLen), ref( m_MapFrame.Orig()), + ref( *pStm), 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 ; +} + +//---------------------------------------------------------------------------- +bool +VolZmap::SubtractSurfTm( const ISurfTriMesh* pStm) +{ + // 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::SubtractMapPart, this, nG, + nInfI, nSupI, 0, m_nNy[nG], ref( vtLen), ref( m_MapFrame.Orig()), + ref( *pStm), 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::SubtractMapPart, this, nG, + 0, m_nNx[nG], nInfJ, nSupJ, ref( vtLen), ref( m_MapFrame.Orig()), + ref( *pStm), ref( intPLSTM)) ; } } diff --git a/VolZmap.h b/VolZmap.h index c7fcb86..e81bd23 100644 --- a/VolZmap.h +++ b/VolZmap.h @@ -18,6 +18,7 @@ #include "Tool.h" #include "/EgtDev/Include/EGkVolZmap.h" #include "/EgtDev/Include/EGkIntersLineVolZmap.h" +#include "/EgtDev/Include/EGkSurfTriMesh.h" #include #include #include @@ -80,10 +81,12 @@ class VolZmap : public IVolZmap, public IGeoObjRW bool Create( const Point3d& ptO, double dDimX, double dDimY, double dDimZ, double dStep, bool bTriDex) override ; bool CreateEmpty( const Point3d& ptO, double dDimX, double dDimY, double dDimZ, double dStep, bool bTriDex) override ; bool CreateFromFlatRegion( const ISurfFlatRegion& Surf, double dDimZ, double dStep, bool bTriDex) override ; - bool CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex) override ; + bool CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex, double dExtraBox = 100 * EPS_SMALL) override ; + bool CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) override ; int GetBlockCount( void) const override ; int GetBlockUpdatingCounter( int nBlock) const override ; bool GetBlockTriangles( int nBlock, TRIA3DEXVECTOR& vTria) const override ; + ISurfTriMesh* GetSurfTriMesh( void) const override ; bool GetEdges( ICURVEPOVECTOR& vpCurve) const override ; bool GetVolume( double& dVol) const override ; bool IsTriDexel( void) const override @@ -144,6 +147,7 @@ class VolZmap : public IVolZmap, public IGeoObjRW bool RemovePart( int nPart) override ; int GetPartMinDistFromPoint( const Point3d& ptP) const override ; bool AddSurfTm( const ISurfTriMesh* pStm) override ; + bool SubtractSurfTm( const ISurfTriMesh* pStm) override ; bool MakeUniform( double dToler) override ; public : // IGeoObjRW @@ -432,10 +436,21 @@ class VolZmap : public IVolZmap, public IGeoObjRW const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) ; bool AddMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig, const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) ; + bool SubtractMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig, + const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) ; + // Funzioni per Offset di superfici + bool CreateOffsSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid) ; + bool CreateOffsCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, int nGrid) ; + 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) ; private : enum Status { ERR = 0, OK = 1, TO_VERIFY = 2} ; - enum Shape { GENERIC = 0, BOX = 1, EXTRUSION = 2} ; + enum Shape { GENERIC = 0, BOX = 1, EXTRUSION = 2, OFFSET = 3} ; static const int N_MAPS = 3 ; static const int N_VOXBLOCK = 32 ; diff --git a/VolZmapCreation.cpp b/VolZmapCreation.cpp index 4fefdee..fef6eae 100644 --- a/VolZmapCreation.cpp +++ b/VolZmapCreation.cpp @@ -17,6 +17,7 @@ #include "CurveLine.h" #include "VolZmap.h" #include "GeoConst.h" +#include "/EgtDev/Include/EGkStmFromCurves.h" #include "/EgtDev/Include/EGkIntersLineSurfTm.h" #include "/EgtDev/Include/EgtNumUtils.h" #include @@ -597,6 +598,7 @@ VolZmap::CreateMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, co return true ; } +//---------------------------------------------------------------------------- bool VolZmap::AddMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig, const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) @@ -691,7 +693,100 @@ VolZmap::AddMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const //---------------------------------------------------------------------------- bool -VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex) +VolZmap::SubtractMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig, + const ISurfTriMesh& Surf, 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... + 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) ; + 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 + + // Aggiungo un tratto al dexel + SubtractIntervals( 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, 0, true) ; + bInside = false ; // uscita + } + } + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex, double dExtraBox) { // Se la superficie non è chiusa oppure orientata al contrario non ha senso continuare double dVol ; @@ -704,14 +799,14 @@ VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex // Determino il bounding box della TriMesh BBox3d SurfBBox ; Surf.GetLocalBBox( SurfBBox) ; - - // Determino i punti estremi del bounding box - Point3d ptMapOrig, ptMapEnd ; - SurfBBox.GetMinMax( ptMapOrig, ptMapEnd) ; // Il dexel se parte da un triangolo della trimesh può non trovare l'intersezione, // quindi espandiamo il bounding box per ovviare al problema. - SurfBBox.Expand( 100 * EPS_SMALL, 100 * EPS_SMALL, 100 * EPS_SMALL) ; + SurfBBox.Expand( dExtraBox) ; + + // Determino i punti estremi del bounding box + Point3d ptMapOrig, ptMapEnd ; + SurfBBox.GetMinMax( ptMapOrig, ptMapEnd) ; // Sistema di riferimento intrinseco dello Zmap m_MapFrame.Set( ptMapOrig, Frame3d::TOP) ; @@ -760,15 +855,15 @@ VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex // ciclo sulle griglie bool bCompleted = true ; - for ( int g = 0 ; g < m_nMapNum ; ++ g) { + for ( int nG = 0 ; nG < m_nMapNum ; ++ nG) { // Definisco dei sistemi di riferimento ausiliari Frame3d frMapFrame ; - if ( g == 0) + if ( nG == 0) frMapFrame = m_MapFrame ; - else if ( g == 1) + else if ( nG == 1) frMapFrame.Set( ptMapOrig, Y_AX, Z_AX, X_AX) ; - else if ( g == 2) + else if ( nG == 2) frMapFrame.Set( ptMapOrig, Z_AX, X_AX, Y_AX) ; // Oggetto per calcolo massivo intersezioni @@ -778,28 +873,28 @@ VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex int nThreadMax = max( 1, int( thread::hardware_concurrency()) - 1) ; vector< future> vRes ; vRes.resize( nThreadMax) ; - if ( m_nNx[g] > m_nNy[g]) { - int nDexNum = m_nNx[g] / nThreadMax ; - int nRemainder = m_nNx[g] % nThreadMax ; + 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 ; for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { nInfI = nSupI ; nSupI = nInfI + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; - vRes[nThread] = async( launch::async, &VolZmap::CreateMapPart, this, g, - nInfI, nSupI, 0, m_nNy[g], ref( vtLen), ref( ptMapOrig), ref( Surf), ref( intPLSTM)) ; + vRes[nThread] = async( launch::async, &VolZmap::CreateMapPart, this, nG, + nInfI, nSupI, 0, m_nNy[nG], ref( vtLen), ref( ptMapOrig), ref( Surf), ref( intPLSTM)) ; } } else { - int nDexNum = m_nNy[g] / nThreadMax ; - int nRemainder = m_nNy[g] % nThreadMax ; + int nDexNum = m_nNy[nG] / nThreadMax ; + int nRemainder = m_nNy[nG] % nThreadMax ; int nInfJ = 0 ; int nSupJ = 0 ; for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { nInfJ = nSupJ ; nSupJ = nInfJ + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; - vRes[nThread] = async( launch::async, &VolZmap::CreateMapPart, this, g, - 0, m_nNx[g], nInfJ, nSupJ, ref( vtLen), ref( ptMapOrig), ref( Surf),ref( intPLSTM)) ; + vRes[nThread] = async( launch::async, &VolZmap::CreateMapPart, this, nG, + 0, m_nNx[nG], nInfJ, nSupJ, ref( vtLen), ref( ptMapOrig), ref( Surf),ref( intPLSTM)) ; } } diff --git a/VolZmapGraphics.cpp b/VolZmapGraphics.cpp index 01fe8ae..9fff5ef 100644 --- a/VolZmapGraphics.cpp +++ b/VolZmapGraphics.cpp @@ -19,6 +19,7 @@ #include "MC_Tables.h" #include "PolygonPlane.h" #include "IntersLineBox.h" +#include "SurfTriMesh.h" #include "/EgtDev/Include/EGkIntervals.h" #include "/EgtDev/Include/EGkStringUtils3d.h" #include "/EgtDev/Include/EGkChainCurves.h" @@ -571,6 +572,35 @@ VolZmap::GetBlockTriangles( int nBlock, TRIA3DEXVECTOR& vTria) const } } +//---------------------------------------------------------------------------- +ISurfTriMesh* +VolZmap::GetSurfTriMesh( void) const +{ + // controllo che lo Zmap sia valido + if ( ! IsValid()) + return nullptr ; + + // inizializzo la superficie + PtrOwner pStm( CreateBasicSurfTriMesh()) ; + if ( IsNull( pStm) || ! pStm->Init( 3, 1)) + return nullptr ; + PointGrid3d VertGrid ; VertGrid.Init( 50000) ; + + // ciclo lungo i blocchi dello Zmap + for ( int nB = 0 ; nB < GetBlockCount() ; ++ nB) { + TRIA3DEXVECTOR vTria ; + GetBlockTriangles( nB, vTria) ; + if ( ! pStm->AddTriaFromZMap( vTria, VertGrid)) + return nullptr ; + } + + // sistemo la topologia + if ( ! pStm->AdjustTopologyFromZMap()) + return nullptr ; + + return ( Release( pStm)) ; +} + //---------------------------------------------------------------------------- int VolZmap::GetBlockCount( void) const @@ -1142,7 +1172,7 @@ VolZmap::ExtMarchingCubes( int nBlock, VoxelContainer& vVox) const } // Controllo se il voxel ha una sola faccia che giace in un piano canonico e quindi ha gestione speciale - if ( m_nShape != BOX) { + if ( m_nShape != BOX && m_nShape != OFFSET) { // Faccia XY normale Z+ if ( nIndex == 15) { int nTool ; double dPos ; diff --git a/VolZmapOffset.cpp b/VolZmapOffset.cpp new file mode 100644 index 0000000..1812b0a --- /dev/null +++ b/VolZmapOffset.cpp @@ -0,0 +1,300 @@ +//---------------------------------------------------------------------------- +// EgalTech 2025-2025 +//---------------------------------------------------------------------------- +// File : OffsetSurfTm.cpp Data : 09.06.25 Versione : 2.7e3 +// Contenuto : Dichiarazione della funzione per calcolare l'offset di superfici TriMesh +// mediante Zmap +// +// +// +// Modifiche : 09.06.25 RE Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +#include "stdafx.h" +#include "VolZmap.h" +#include "CurveLine.h" +#include "GeoConst.h" +#include "/EgtDev/Include/EGkStmFromCurves.h" +#include "/EgtDev/Include/EGkIntersLineSurfTm.h" +#include "/EgtDev/Include/EgtNumUtils.h" +#include + +using namespace std ; + + +//---------------------------------------------------------------------------- +/* Funzione per aggiungere intervalli lungo un Dexel per l'offset di una superficie TriMesh */ +bool +VolZmap::SubtractIntervalsForOffset( int nGrid, int nI, int nJ, + double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, + int nToolNum, bool bSkipSwap) +{ + // per ora la funzione è la stessa della differenza in generale + // TODO -- Aggiustare eventuali tolleranze + return SubtractIntervals( nGrid, nI, nJ, dMin, dMax, vtNMin, vtNMax, nToolNum, bSkipSwap) ; +} + +//---------------------------------------------------------------------------- +/* Funzione per aggiungere intervalli lungo un Dexel per l'offset di una superficie TriMesh */ +bool +VolZmap::AddIntervalsForOffset( int nGrid, int nI, int nJ, + double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, + int nToolNum, bool bSkipSwap) +{ + // per ora la funzione è la stessa della somma generale + // TODO -- Aggiustare eventuali tolleranze + return AddIntervals( nGrid, nI, nJ, dMin, dMax, vtNMin, vtNMax, nToolNum, bSkipSwap) ; +} + +//---------------------------------------------------------------------------- +/* Funzione per la creazione di una sfera di Offset centrata sul vertice di una TriMesh con cui + aggiungere o sottrarre intervalli lungo i Dexel coinvolti */ +bool +VolZmap::CreateOffsSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid) +{ + // determino il Box della sfera posizionata su tale vertice + BBox3d BBoxSphere( ptV - dOffs * Vector3d( 1., 1., 1.), + ptV + dOffs * Vector3d( 1., 1., 1.)) ; + // determino gli intervalli di interesse mediante intersezione con Box della sfera + int nStartI = max( 0, int( BBoxSphere.GetMin().x / m_dStep)) ; + int nEndI = min( m_nNx[nGrid] - 1, int( BBoxSphere.GetMax().x / m_dStep)) ; + int nStartJ = max( 0, int( BBoxSphere.GetMin().y / m_dStep)) ; + int nEndJ = min( m_nNy[nGrid] - 1, int( BBoxSphere.GetMax().y / m_dStep)) ; + // aggiorno gli spilloni interessati + double dSqRad = dOffs * dOffs ; + for ( int i = nStartI ; i <= nEndI ; ++ i) { + for ( int j = nStartJ ; j <= nEndJ ; ++ j) { + double dX = ( i + 0.5) * m_dStep ; + double dY = ( j + 0.5) * m_dStep ; + Point3d ptC( dX, dY, 0.) ; + double dStSqDXY = SqDistXY( ptC, ptV) ; + if ( dStSqDXY < dSqRad) { + double dMin = ptV.z - sqrt( dSqRad - dStSqDXY) ; + Vector3d vtNmin = Point3d( dX, dY, dMin) - ptV ; + vtNmin.Normalize() ; + double dMax = ptV.z + sqrt( dSqRad - dStSqDXY) ; + Vector3d vtNmax = Point3d( dX, dY, dMax) - ptV ; + vtNmax.Normalize() ; + if ( dOffs > 0.) + AddIntervalsForOffset( nGrid, i, j, dMin, dMax, vtNmin, vtNmax, 0) ; + else + SubtractIntervalsForOffset( nGrid, i, j, dMin, dMax, -vtNmin, -vtNmax, 0) ; + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per la creazione di un cilindro di Offset sul vertice di una TriMesh con cui + aggiungere o sottrarre intervalli lungo i Dexel coinvolti */ +bool +VolZmap::CreateOffsCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, int nGrid) +{ + // determino la lunghezza dello spigolo corrente + double dH = Dist( ptP1, ptP2) ; + // 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., abs( dOffs), abs( dOffs)) ; + else if ( AreSameOrOppositeVectorApprox( vtV, Y_AX)) + BBoxCylinder.Expand( abs( dOffs), 0., abs( dOffs)) ; + else if ( AreSameOrOppositeVectorApprox( vtV, Z_AX)) + BBoxCylinder.Expand( abs( dOffs), abs( dOffs), 0.) ; + else { + double dExpandX = abs( dOffs) * sqrt( 1 - vtV.x * vtV.x) ; + double dExpandY = abs( dOffs) * sqrt( 1 - vtV.y * vtV.y) ; + double dExpandZ = abs( dOffs) * 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 + Frame3d CylFrame ; + if ( ! CylFrame.Set( ptP1, vtV)) + return false ; + for ( int i = nStartI ; i <= nEndI ; ++ i) { + for ( int j = nStartJ ; j <= nEndJ ; ++ j) { + Point3d ptC( ( i + 0.5) * m_dStep, ( j + 0.5) * m_dStep, 0) ; + Point3d ptInt1, ptInt2 ; + Vector3d vtN1, vtN2 ; + 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, 0) ; + else + SubtractIntervalsForOffset( nGrid, i, j, ptInt1.z, ptInt2.z, vtN1, vtN2, 0) ; + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per la creazine di uno Zmap di Offset (positivo o negativo) a partire da una + superficie TriMesh */ +bool +VolZmap::CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) +{ + // controllo delle superfici + for ( const ISurfTriMesh* Surf : vSurf) { + if ( Surf == nullptr) + return false ; + } + // verifica sul parametro di Offset ( coerente con Curve e FlatRegion) + if ( abs( dOffs) < 10 * EPS_SMALL) + return true ; + // se non ho superfici, non faccio nulla + if ( vSurf.empty()) + return true ; + + // definisco lo Zmap di partenza a partire dalle superfici + // se una sola superficie + double dBoxExpansion = ( abs( dOffs) + 1.5 * dTol) + 10 * EPS_SMALL ; + if ( int( vSurf.size()) == 1) { + // controllo la validità della superficie + if ( ! vSurf[0]->IsValid() || vSurf[0]->GetTriangleCount() == 0) + return true ; + // definisco lo Zamp a partire dall'espansione del Box della superficie + if ( ! CreateFromTriMesh( *vSurf[0], dTol, true, dBoxExpansion)) + return false ; + } + // se più superfici + else { + // calcolo il Box complessivo delle superfici TriMesh + BBox3d BBoxGlob ; + for ( int i = 0 ; i < int( vSurf.size()) ; ++ i) { + // controllo la validità della superficie + if ( ! vSurf[i]->IsValid() || vSurf[i]->GetTriangleCount() == 0) + continue ; + // calcolo il Box della superficie + BBox3d BBoxSurf ; vSurf[i]->GetLocalBBox( BBoxSurf) ; + // aggiungo il Box a quello complessivo + BBoxGlob.Add( BBoxSurf) ; + } + // definisco uno Zmap vuoto a partire dal Box + BBoxGlob.Expand( dBoxExpansion) ; + if ( ! CreateEmpty( BBoxGlob.GetMin(), BBoxGlob.GetDimX(), BBoxGlob.GetDimY(), BBoxGlob.GetDimZ(), dTol, true)) + return false ; + for ( const ISurfTriMesh* Surf : vSurf) { + if ( ! AddSurfTm( Surf)) + return false ; + } + } + + /* Assunzioni : + - Idea Generale di Offset + - Su ogni vertice viene definita una sfera ( con raggio pari al valore di Offset e + centro il vertice corrente) + - Su ogni lato viene definito un cilindro ( con raggio di pase pari al valore di Offset + e asse definito dall'edge stesso) + - Su ogni faccia viene definita una superficie di estrusione ( dove le due basi sono + definite dalla traslazione sia in positivo che in negativo della faccia lungo la sua normale) + + - Segno dell'Offset : + - Positivo ( si sommano gli intervalli corrisipondenti alle entità create) + - Negativo ( si sottraggono gli intervalli corrispondenti alle enetità create) + + - Semplificazione entità : + La creazione di Sfere e Cilindri potrebbe essere resa più "corretta" definitendo solo + "spicchi 3d" di sfera e "spicchi 3d" di cilindri. Dato che le operazioni di somma, sottrazioni e + calcolo delle normali per gli spilloni sono elementari su queste figure, si rischia di appesantire + troppo i conti introducendo variabili angolari che non sommando tutte le parti. + */ + + // 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()) ; + + + // scorro le superfici + for ( const ISurfTriMesh* Surf : vSurf) { + // se superficie non valida, passo alla successiva + if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) + continue ; + // definisco una mappa dei vertici, in modo da sapere su quali sono state già create le sfere + BOOLVECTOR vbVert( Surf->GetVertexCount(), false) ; + // ----------------------- Cilindri e Sfere ----------------------- + // scorro gli Edge della superficie + for ( int nE = 0 ; nE < Surf->GetEdgeCount() ; ++ nE) { + // recupero lo spigolo + int nV1, nV2, nF1, nF2 ; double dAng ; + Surf->GetEdge( nE, nV1, nV2, nF1, nF2, dAng) ; + // controllo se il cilindro serve + // NB. la mancata creazione del cilindro comporta la mancata creazione delle sfere sui suoi vertici + // durante questa iterazione; non significa che questa sfera non verrà mai creata... + // Non esiste la sfera sul vertive V <=> non esiste alcun cilindro su tutti gli edge concorrenti + if ( dAng * dOffs < 0) + continue ; + // recupero le coordinate dei vertici + Point3d ptP1 ; Surf->GetVertex( nV1, ptP1) ; + Point3d ptP2 ; Surf->GetVertex( nV2, ptP2) ; + // ciclo sulle griglie + 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]) ; + // aggiungo/sottraggo gli intervalli definiti dal cilindro + if ( ! CreateOffsCylinderOnEdge( ptP1, ptP2, dOffs, nGrid)) + return false ; + // aggiungo/sottraggo gli intervalli definiti dalla sfera + if ( ! vbVert[nV1]) { + vbVert[nV1] = ( nGrid == 2) ; + if ( ! CreateOffsSphereOnVertex( ptP1, dOffs, nGrid)) + return false ; + } + if ( ! vbVert[nV2]) { + vbVert[nV2] = ( nGrid == 2) ; + if ( ! CreateOffsSphereOnVertex( ptP2, dOffs, nGrid)) + return false ; + } + } + } + // ----------------------- Facce ----------------------- + // scorro tutte le facce definendo una superficie di estrusione + for ( int nF = 0 ; nF < Surf->GetFacetCount() ; ++ nF) { + // recupero lo faccia + POLYLINEVECTOR vPL ; Surf->GetFacetLoops( nF, vPL) ; + // recupero la normale della faccia + Vector3d vtN ; Surf->GetFacetNormal( nF, vtN) ; + // definisco la superficie di estrusione + CICURVEPVECTOR vpCrvs ; vpCrvs.reserve( vPL.size()) ; + for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { + vPL[i].Translate( - abs( dOffs) * vtN) ; + PtrOwner pCrvCompo( CreateCurveComposite()) ; + if ( IsNull( pCrvCompo) || ! pCrvCompo->FromPolyLine( vPL[i])) + return false ; + vpCrvs.emplace_back( Release( pCrvCompo)) ; + } + // recupero la TriMesh di estrusione + PtrOwner pStmExtr( GetSurfTriMeshByRegionExtrusion( vpCrvs, 2 * abs( dOffs) * vtN)) ; + if ( IsNull( pStmExtr) || ! pStmExtr->IsValid() || pStmExtr->GetTriangleCount() == 0) + return false ; + // aggiorno gli spilloni + if ( dOffs > 0.) + AddSurfTm( pStmExtr) ; + else + SubtractSurfTm( pStmExtr) ; + } + } + m_nShape = OFFSET ; // OFFSET (?) ... per ora va bene così + + return true ; +} + + +