diff --git a/DistPointSurfTm.cpp b/DistPointSurfTm.cpp index a339e5d..01cfcfd 100644 --- a/DistPointSurfTm.cpp +++ b/DistPointSurfTm.cpp @@ -94,6 +94,8 @@ DistPointSurfTm::Calculate( const Point3d& ptP, const ISurfTriMesh& tmSurf) { // Inizializzo distanza non calcolata m_dDist = - 1. ; + // Vettore di indici dei triangoli più vicini inizialmente vuoto + m_vnMinDistTriaIndex.clear() ; // Controllo se la superficie è chiusa m_bIsSurfClosed = tmSurf.IsClosed() ; @@ -184,6 +186,10 @@ DistPointSurfTm::Calculate( const Point3d& ptP, const ISurfTriMesh& tmSurf) if ( nMinDistTriaIndex == SVT_NULL) return ; + // Inizializzo il vettore dei triangoli a minima distanza + for ( auto& Tria : vTria) + m_vnMinDistTriaIndex.emplace_back( Tria.first) ; + // salvo la distanza minima m_dDist = dMinDist ; // salvo il punto a distanza minima @@ -279,6 +285,18 @@ DistPointSurfTm::GetMinDistTriaIndex( int& nMinDistIndex) const return true ; } +//---------------------------------------------------------------------------- +bool +DistPointSurfTm::GetMinDistTriaIndices( INTVECTOR& vMinDistTriaIndex) const +{ + // Distanza non valida + if ( m_dDist < - EPS_ZERO) + return false ; + // Distanza valida + vMinDistTriaIndex = m_vnMinDistTriaIndex ; + return true ; +} + //---------------------------------------------------------------------------- int GetSurfTmNearestVertex( const Point3d& ptP, const ISurfTriMesh& tmSurf) diff --git a/EgtGeomKernel.rc b/EgtGeomKernel.rc index 4b21411..4d3eb5c 100644 Binary files a/EgtGeomKernel.rc and b/EgtGeomKernel.rc differ diff --git a/SurfTriMeshOffset.cpp b/SurfTriMeshOffset.cpp index 3ec2c58..0710ba8 100644 --- a/SurfTriMeshOffset.cpp +++ b/SurfTriMeshOffset.cpp @@ -15,8 +15,21 @@ //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "VolZmap.h" +#include "SurfTriMesh.h" +#include "EgtDev/Include/EGkDistPointSurfTm.h" #include "\EgtDev\Include\EGkSurfTriMeshAux.h" +#define DEBUG 0 +#if DEBUG + #include "/EgtDev/Include/EGkGeoObjSave.h" + #include "/EgtDev/Include/EGkGeoPoint3d.h" + #include "/EgtDev/Include/EGkGeoVector3d.h" + #include "/EgtDev/Include/EGkStmStandard.h" + #include "/EgtDev/Include/EgtPerfCounter.h" + std::vector VT ; + std::vector VC ; +#endif + using namespace std ; //---------------------------------------------------------------------------- @@ -73,11 +86,11 @@ CreateSurfTriMeshesOffset( const CISURFTMPVECTOR& vStm, double dOffs, double dPr if ( vStm.empty()) return nullptr ; // controllo sul valore di tolleranza lineare - double dMyPrec = max( dPrec, 100 * EPS_SMALL) ; + double dMyPrec = max( dPrec, 100. * EPS_SMALL) ; // --- NB. ( Il valore di Offset deve essere maggiore di 10 * EPS_SMALL in valore assoluto) // Nel caso sia minore, restituisco semplicemente la somma delle superfici // ( questo valore serve per rimanere coerente con l'Offset delle curve) - if ( abs( dOffs) < 10 * EPS_SMALL) + if ( abs( dOffs) < 10. * EPS_SMALL) return SumStm( vStm) ; // creo lo Zmap associato alle superfici TriMesh @@ -87,8 +100,117 @@ CreateSurfTriMeshesOffset( const CISURFTMPVECTOR& vStm, double dOffs, double dPr if ( ! OneVolZmap.IsValid()) return nullptr ; - // restituisco la superficie TriMesh - return ( OneVolZmap.GetSurfTriMesh()) ; + // recupero le superfici aperte + CISURFTMPVECTOR vStmOpen ; + for ( const ISurfTriMesh* pStm : vStm) { + if ( pStm != nullptr && pStm->IsValid() && ! pStm->IsClosed()) + vStmOpen.emplace_back( pStm) ; + } + + // --- se non ho superfici aperte + if ( vStmOpen.empty()) { + // restituisco la superficie TriMesh di Offset + return ( OneVolZmap.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 + TRIA3DEXVECTOR vTria, vTriaSafe ; + OneVolZmap.GetBlockTriangles( nB, vTria) ; + + // un triangolo viene ritenuto valido se è non è troppo vicino ( dOffs) alle superfici aperte + 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) ; + } + } + + // 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 ; + } + + #if DEBUG + SaveGeoObj( VT, VC, "C:\\Temp\\TriangleSelection.nge") ; + #endif + + + // sistemo la topologia + if ( ! pStm->AdjustTopologyFromZMap()) + return nullptr ; + + return ( Release( pStm)) ; } //---------------------------------------------------------------------------- diff --git a/VolZmap.h b/VolZmap.h index 167345a..582d867 100644 --- a/VolZmap.h +++ b/VolZmap.h @@ -152,6 +152,7 @@ class VolZmap : public IVolZmap, public IGeoObjRW bool AddSurfTm( const ISurfTriMesh* pStm) override ; bool SubtractSurfTm( const ISurfTriMesh* pStm) override ; bool MakeUniform( double dToler) override ; + bool Offset( double dOffs, int nType) override ; public : // IGeoObjRW int GetNgeId( void) const override ; @@ -463,13 +464,15 @@ class VolZmap : public IVolZmap, public IGeoObjRW bool InitVolZMapOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) ; bool InitVolZMapThickeningOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) ; bool UpdateVolZMapByOpenSurfFilletOffset( const ISurfTriMesh* Surf, double dOffs, double dTol) ; + bool UpdateVolZMapByOpenSurfSharpedOffset( const ISurfTriMesh* Surf, int nType, double dOffs, double dTol) ; bool UpdateVolZMapByClosedSurfFilletOffset( const ISurfTriMesh* Surf, double dOffs, double dTol) ; bool UpdateVolZMapByClosedSurfSharpedOffset( const ISurfTriMesh* Surf, int nType, double dOffs, double dTol) ; bool UpdateVolZMapBySurfThickeningFilletOffset( const ISurfTriMesh* Surf, double dOffs, double dTol) ; 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 CreateOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs, bool bThickle) ; + bool CreateFatOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs, bool bThickle) ; + 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) ; @@ -477,6 +480,10 @@ class VolZmap : public IVolZmap, public IGeoObjRW double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, int nToolNum, bool bSkipSwap = false) ; bool CutByPlaneForOffset( const Plane3d& plCut) ; + // Funzioni per Offset di Zmap + bool OffsetFillet( double dOffs) ; + bool OffsetSharped( double dOffs, int nType) ; + public : // ------------------------- ENUM ---------------------------------------------------------------- enum MillingPhase { @@ -567,6 +574,12 @@ class VolZmap : public IVolZmap, public IGeoObjRW double m_dToolAngTolDeg ; } ; +// Offset +enum { + VOLZMAP_OFFS_FILLET = 0, + VOLZMAP_OFFS_CHANFER = 1, + VOLZMAP_OFFS_EXTENDED = 2 +} ; //----------------------------------------------------------------------------- inline VolZmap* CreateBasicVolZmap( void) diff --git a/VolZmapOffset.cpp b/VolZmapOffset.cpp index 61fbabc..6331891 100644 --- a/VolZmapOffset.cpp +++ b/VolZmapOffset.cpp @@ -2,8 +2,12 @@ // 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 +// Contenuto : Dichiarazione delle funzioni per calcolare l'Offset di superfici TriMesh +// mediante ZMap +// - Fillet da ver. 2.7e3 +// - Chamfer ed Extend da ver. 2.7i1 +// - Offset Thickening da ver. 2.7i1 +// Dichiarazione della funzione per calcolare l'Offset di uno ZMap // // // @@ -15,23 +19,31 @@ #include "stdafx.h" #include "VolZmap.h" #include "CurveLine.h" +#include "CurveArc.h" #include "GeoConst.h" #include "/EgtDev/Include/EGkStmFromCurves.h" #include "/EgtDev/Include/EGkIntersLineSurfTm.h" #include "/EgtDev/Include/EgtNumUtils.h" #include - -using namespace std ; +#include "set" #define DEBUG 0 #if DEBUG #include "/EgtDev/Include/EGkGeoObjSave.h" #include "/EgtDev/Include/EGkGeoPoint3d.h" #include "/EgtDev/Include/EGkGeoVector3d.h" - vector VT ; - vector VC ; + #include "/EgtDev/Include/EGkStmStandard.h" + #include "/EgtDev/Include/EgtPerfCounter.h" + /* + PerformanceCounter PC ; PC.Start() ; + LOG_INFO( GetEGkLogger(), ( "myText : " + to_string( PC.Stop())).c_str()) ; + */ + std::vector VT ; + std::vector VC ; #endif +using namespace std ; + // --------------------------------------------------------------------------- // Tipi di Offset : // - Con segno ( quindi Offset orientato, definito come positivo o negativo ) @@ -337,6 +349,7 @@ VolZmap::CutByPlaneForOffset( const Plane3d& plCut) return true ; } + //---------------------------------------------------------------------------- // 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 @@ -433,10 +446,11 @@ VolZmap::CreateOffsetCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, d } //---------------------------------------------------------------------------- -// Funzione per creare una superficie di etrusione definita da una faccia di TriMesh con cui +// Funzione per creare una superficie di etrusione Fat definita da una faccia di TriMesh con cui // aggiungere o sottrarre intervalli lungo i Dexel coinvolti +//---------------------------------------------------------------------------- bool -VolZmap::CreateOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs, bool bThickle) +VolZmap::CreateFatOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs, bool bThickle) { // verifico la validità della superficie if ( Surf == nullptr) @@ -497,6 +511,56 @@ VolZmap::CreateOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs, bool return true ; } +//---------------------------------------------------------------------------- +// Funzione per creare una superficie di estrusione orientata definita da una faccia di TriMesh con +// cui aggiungere ( senza sottrarre) intervalli lungo i Dexel coinvolti ( usata per supercici Trimesh Open ) +//---------------------------------------------------------------------------- +bool +VolZmap::CreateOrientedOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs) +{ + // verifico la validità della superficie + if ( Surf == nullptr) + return false ; + if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) + return true ; + + // 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) { + PtrOwner pCrvCompo( CreateCurveComposite()) ; + if ( IsNull( pCrvCompo) || ! pCrvCompo->FromPolyLine( vPL[i])) { + // dealloco le curve + for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { + delete ( vpCrvs[i]) ; + vpCrvs[i] = nullptr ; + } + return false ; + } + vpCrvs.emplace_back( Release( pCrvCompo)) ; + } + // recupero la TriMesh di estrusione + PtrOwner pStmExtr( GetSurfTriMeshByRegionExtrusion( vpCrvs, dOffs * vtN)) ; + if ( IsNull( pStmExtr) || ! pStmExtr->IsValid() || pStmExtr->GetTriangleCount() == 0) { + // dealloco le curve + for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { + delete ( vpCrvs[i]) ; + vpCrvs[i] = nullptr ; + } + return false ; + } + // aggiorno gli spilloni + AddSurfTm( pStmExtr) ; + } + + return true ; +} + //---------------------------------------------------------------------------- // Funzione per inizializzare lo ZMap a partire da un vettore di superfici TriMesh // L'idea è quella di creare uno ZMap a partire dal Box complessivo delle superfici TriMesh : @@ -600,17 +664,6 @@ VolZmap::InitVolZMapThickeningOffset( const CISURFTMPVECTOR& vSurf, double dOffs return ( CreateEmpty( BBoxGlob.GetMin(), BBoxGlob.GetDimX(), BBoxGlob.GetDimY(), BBoxGlob.GetDimZ(), dTol, true)) ; } -//---------------------------------------------------------------------------- -// [Fillet] Funzione per aggiornare mediante Sfere, Cilindri e Facce di estrusione lo ZMap corrente -// mediante una superficie aperta -//---------------------------------------------------------------------------- -bool -VolZmap::UpdateVolZMapByOpenSurfFilletOffset( const ISurfTriMesh* Surf, double dOffs, double dTol) -{ - // da studiare... - return false ; -} - //---------------------------------------------------------------------------- // [Fillet] Funzione per aggiornare mediante Sfere, Cilindri e Facce di estrusione lo ZMap corrente // mediante una superficie chiusa @@ -689,7 +742,7 @@ VolZmap::UpdateVolZMapByClosedSurfFilletOffset( const ISurfTriMesh* Surf, double vbVert[nV2] = true ; } // ----------------------- Facce ----------------------- - if ( ! CreateOffsetExtrusionFace( Surf, dOffs, false)) + if ( ! CreateFatOffsetExtrusionFace( Surf, dOffs, false)) return true ; return true ; @@ -697,7 +750,7 @@ VolZmap::UpdateVolZMapByClosedSurfFilletOffset( const ISurfTriMesh* Surf, double //---------------------------------------------------------------------------- // [Sharped ( Chamfer/Extend )] Funzione per aggiornare mediantePrismi, Involucri concvessi -// e Facce di estrusione lo ZMap corrente mediante una superficie generica (aperta o chiusa) +// e Facce di estrusione lo ZMap corrente mediante una superficie chiusa //---------------------------------------------------------------------------- bool VolZmap::UpdateVolZMapByClosedSurfSharpedOffset( const ISurfTriMesh* Surf, int nType, double dOffs, double dTol) @@ -717,7 +770,7 @@ VolZmap::UpdateVolZMapByClosedSurfSharpedOffset( const ISurfTriMesh* Surf, int n // dalle facce dei prismi ( sia laterali che di base ) // ----------------------- Facce ----------------------- - if ( ! CreateOffsetExtrusionFace( Surf, dOffs, false)) + if ( ! CreateFatOffsetExtrusionFace( Surf, dOffs, false)) return true ; // definisco una mappa tra vertici e contorni orientati degli ZMap derivanti dagli Edges adiacenti @@ -971,6 +1024,624 @@ 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 +// NB. Lo Zmap creato è orientato +//---------------------------------------------------------------------------- +bool +VolZmap::UpdateVolZMapByOpenSurfFilletOffset( const ISurfTriMesh* Surf, double dOffs, double dTol) +{ + // controlli della superficie + if ( Surf == nullptr || Surf->IsClosed()) + return false ; + 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 + + // ----------------------- Facce ----------------------- + if ( ! CreateOrientedOffsetExtrusionFace( Surf, dOffs)) + return true ; + + // definisco una mappa tra vertici e contorni orientati degli ZMap derivanti dagli Edges adiacenti + // }, { vector }> + unordered_map, vector>> vMapVertBorders( Surf->GetVertexCount()) ; + + // ----------------------- spicchi ----------------------- + 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) ; + // recupero le coordinate dei vertici + Point3d ptP1 ; Surf->GetVertex( nV1, ptP1) ; + Point3d ptP2 ; Surf->GetVertex( nV2, ptP2) ; + // definisco il vettore estrusione uscente da ptP1 ed entrante in ptP2 + Vector3d vtEdge = ( ptP2 - ptP1) ; + // 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 ; + } + } + 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 + } + } + } + + #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 ; + + // recupero il vertice corrente + Point3d ptVertex ; + Surf->GetVertex( nV, ptVertex) ; + + // definisco una sfera di raggio pari 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], + m_dStep, true) ; + // ciclo sulle griglie + for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { + // 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) ; + } + + // 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()) ; + + // 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.)) ; + // 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)) ; + int nStartJ = max( 0, int( BBoxSphere.GetMin().y / VolZMapCut.m_dStep)) ; + int nEndJ = min( VolZMapCut.m_nNy[nGrid] - 1, int( BBoxSphere.GetMax().y / VolZMapCut.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 * VolZMapCut.m_nNx[nGrid] + i ; + 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) ; + } + } + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +// [Sharped ( Chamfer/Extend )] Funzione per aggiornare mediantePrismi, Involucri concvessi +// e Facce di estrusione lo ZMap corrente mediante una superficie aperta +// NB. Lo Zmap creato è orientato +//---------------------------------------------------------------------------- +bool +VolZmap::UpdateVolZMapByOpenSurfSharpedOffset( const ISurfTriMesh* Surf, int nType, double dOffs, double dTol) +{ + // controllo sul tipo di Offset ( ammessi solo Chamfer o Extended ) + if ( nType != STMOFF_CHAMFER && nType != STMOFF_EXTEND) + return false ; + + // controlli della superficie + if ( Surf == nullptr || Surf->IsClosed()) + return false ; + 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 definito un prisma ( estrusione con base di 4 o 5 lati) + // - Su ogni vertice viene definita una sfera che viene tagliata con i piani definiti + // dalle facce delle porzioni del prisma + + // ----------------------- Facce ----------------------- + if ( ! CreateOrientedOffsetExtrusionFace( Surf, dOffs)) + return true ; + + // definisco una mappa tra vertici e contorni orientati degli ZMap derivanti dagli Edges adiacenti + // }, { vector }> + unordered_map, vector>> vMapVertBorders( Surf->GetVertexCount()) ; + + // ----------------------- spicchi ----------------------- + 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) ; + // recupero le coordinate dei vertici + Point3d ptP1 ; Surf->GetVertex( nV1, ptP1) ; + Point3d ptP2 ; Surf->GetVertex( nV2, ptP2) ; + // definisco il vettore estrusione uscente da ptP1 ed entrante in ptP2 + Vector3d vtEdge = ( ptP2 - ptP1) ; + // 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 il prisma 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 ; + } + } + 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() ; + } + // recupero frame intrinseco definito dal piano della base + Frame3d frLoc ; + if ( ! frLoc.Set( ptP1, vtN1 ^ vtN2, vtN1)) + continue ; + // recupero il punto mancante della base + CurveLine Line1, Line2 ; + Point3d ptLine1 = ORIG + abs( dOffs) * X_AX ; + Line1.Set( ptLine1, ptLine1 + Y_AX) ; + Point3d ptLine2 = ORIG + ( abs( dOffs) * GetToLoc( vtN2, frLoc)) ; + Line2.Set( ptLine2, ptLine2 + GetToLoc( GetRotate( vtN2, vtEdge, - ANG_RIGHT), frLoc)) ; + IntersCurveCurve ILL( Line1, Line2, false) ; + if ( ILL.GetIntersCount() != 1) + continue ; + IntCrvCrvInfo aInfo ; + ILL.GetIntCrvCrvInfo( 0, aInfo) ; + // costruisco la base + PolyLine PLBase ; + double dPar = -1. ; + PLBase.AddUPoint( ++ dPar, ptP1) ; + PLBase.AddUPoint( ++ dPar, ptP1 + abs( dOffs) * vtN1) ; + // se l'angolo esterno supera il retto, offset extend diventa offset chamfer + // ( la base del prisma ora diventa pentagonale) + if ( nType == STMOFF_CHAMFER || abs( dAng) > ANG_RIGHT + EPS_ANG_SMALL) { + // lunghezza aggiuntiva in tangenza + double dLen = abs( dOffs) * tan( abs( dAng) / 4 * DEGTORAD) ; + // punti di costruzione smusso + Vector3d vtDir1 = GetToGlob( aInfo.IciA->ptI, frLoc) - ( ptP1 + abs( dOffs) * vtN1) ; + Vector3d vtDir2 = GetToGlob( aInfo.IciA->ptI, frLoc) - ( ptP1 + abs( dOffs) * vtN2) ; + vtDir1.Normalize() ; + vtDir2.Normalize() ; + Point3d ptP1a = ( ptP1 + abs( dOffs) * vtN1) + vtDir1 * dLen ; + Point3d ptP1b = ( ptP1 + abs( dOffs) * vtN2) + vtDir2 * dLen ; + PLBase.AddUPoint( ++ dPar, ptP1a) ; + PLBase.AddUPoint( ++ dPar, ptP1b) ; + } + else { + PLBase.AddUPoint( ++ dPar, GetToGlob( aInfo.IciA->ptI, frLoc)) ; + } + PLBase.AddUPoint( ++ dPar, ptP1 + abs( dOffs) * vtN2) ; + PLBase.AddUPoint( ++ dPar, ptP1) ; + // definisco la superficie di estrusione e aggiorno gli spilloni + CurveComposite CompoBase ; + CompoBase.FromPolyLine( PLBase) ; + PtrOwner pStmPrism( GetSurfTriMeshByExtrusion( &CompoBase, vtEdge, true)) ; + if ( ! IsNull( pStmPrism)) { + // aggiungo gli intervalli definiti dalla superficie di estrusione + AddSurfTm( pStmPrism) ; + // 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 del prisma + 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 + // --- aggiungo i piani definiti dalle normali alle facce laterali del prima + // ( solo quelle che non presentano un estremo presso il vertice della TriMesh + for ( int nU = 0 ; nU < CompoBase.GetCurveCount() ; ++ nU) { + Point3d ptS ; CompoBase.GetCurve( nU)->GetStartPoint( ptS) ; + Point3d ptE ; CompoBase.GetCurve( nU)->GetEndPoint( ptE) ; + if ( AreSamePointApprox( ptS, ptP1) || AreSamePointApprox( ptE, ptP1)) + continue ; + // ricavo la direzione del tratto + Vector3d vtSegDir = ptE - ptS ; + vtSegDir.Normalize() ; + // ruoto il versore per renderlo perpendicolare al segmento ( verso di uscita ) + vtSegDir.Rotate( - vEdgeN, - ANG_RIGHT) ; + // definisco il piano di taglio + if ( plCut.Set( Media( ptS, ptE), vtSegDir)) { + vMapVertBorders[nV1].second.emplace_back( plCut) ; + vMapVertBorders[nV2].second.emplace_back( plCut) ; + #if DEBUG + pt->Set( ptS) ; + VT.emplace_back( pt->Clone()) ; + VC.emplace_back( ORANGE) ; + pt->Translate( vtEdge) ; + VT.emplace_back( pt->Clone()) ; + VC.emplace_back( ORANGE) ; + pt->Set( ptE) ; + VT.emplace_back( pt->Clone()) ; + VC.emplace_back( ORANGE) ; + pt->Translate( vtEdge) ; + VT.emplace_back( pt->Clone()) ; + VC.emplace_back( ORANGE) ; + vt->Set( vtSegDir) ; + vt->Translate( Media( ptS, ptE) - ORIG) ; + VT.emplace_back( vt->Clone()) ; + VC.emplace_back( AQUA) ; + vt->Translate( vtEdge) ; + VT.emplace_back( vt->Clone()) ; + VC.emplace_back( AQUA) ; + #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 ; + + // recupero il vertice corrente + Point3d ptVertex ; + Surf->GetVertex( nV, ptVertex) ; + + // 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], + m_dStep, true) ; + // ciclo sulle griglie + for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { + // 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, 2. * abs( dOffs), nGrid) ; + } + + // recupero le facce adiacenti al vertice + INTVECTOR vTria ; + bool bCirc ; + Surf->GetAllTriaAroundVertex( nV, vTria, bCirc) ; + set setFace ; + for ( int i = 0 ; i < int( vTria.size()) ; ++ i) + setFace.insert( Surf->GetFacetFromTria( vTria[i])) ; + for ( auto it = setFace.begin() ; it != setFace.end() ; ++ it) { + POLYLINEVECTOR vPL ; + Surf->GetFacetLoops( *it, vPL) ; + Vector3d vtN ; + Surf->GetFacetNormal( *it, vtN) ; + // definisco la superficie di estrusione + CICURVEPVECTOR vpCrvs ; vpCrvs.reserve( vPL.size()) ; + for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { + PtrOwner pCrvCompo( CreateCurveComposite()) ; + if ( IsNull( pCrvCompo) || ! pCrvCompo->FromPolyLine( vPL[i])) { + // dealloco le curve + for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { + delete ( vpCrvs[i]) ; + vpCrvs[i] = nullptr ; + } + return false ; + } + vpCrvs.emplace_back( Release( pCrvCompo)) ; + } + // recupero la TriMesh di estrusione + PtrOwner pStmExtr( GetSurfTriMeshByRegionExtrusion( vpCrvs, - 2. * dOffs * vtN)) ; + if ( IsNull( pStmExtr) || ! pStmExtr->IsValid() || pStmExtr->GetTriangleCount() == 0) { + // dealloco le curve + for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { + delete ( vpCrvs[i]) ; + vpCrvs[i] = nullptr ; + } + return false ; + } + // sottraggo i contributi della faccia + VolZMapCut.SubtractSurfTm( pStmExtr) ; + } + + // 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()) ; + + // 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.)) ; + // 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)) ; + int nStartJ = max( 0, int( BBoxSphere.GetMin().y / VolZMapCut.m_dStep)) ; + int nEndJ = min( VolZMapCut.m_nNy[nGrid] - 1, int( BBoxSphere.GetMax().y / VolZMapCut.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 * VolZMapCut.m_nNx[nGrid] + i ; + 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) ; + } + } + } + } + } + + #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 + + return true ; +} + //---------------------------------------------------------------------------- // [Fillet Thickening] Funzione per aggiornare mediante Sfere, Cilindri e Facce di estrusione lo ZMap corrente // mediante una superficie generica (aperta o chiusa) @@ -1026,7 +1697,7 @@ VolZmap::UpdateVolZMapBySurfThickeningFilletOffset( const ISurfTriMesh* Surf, do vbVert[nV2] = true ; } // ----------------------- Facce ----------------------- - if ( ! CreateOffsetExtrusionFace( Surf, abs( dOffs), true)) + if ( ! CreateFatOffsetExtrusionFace( Surf, abs( dOffs), true)) return false ; return true ; @@ -1050,7 +1721,7 @@ VolZmap::UpdateVolZMapBySurfThickeningSharpedOffset( const ISurfTriMesh* Surf, i return true ; // ----------------------- Facce ----------------------- - if ( ! CreateOffsetExtrusionFace( Surf, abs( dOffs), true)) + if ( ! CreateFatOffsetExtrusionFace( Surf, abs( dOffs), true)) return true ; // definisco una mappa tra vertici e contorni orientati degli ZMap derivanti dagli Edges adiacenti @@ -1062,9 +1733,6 @@ VolZmap::UpdateVolZMapBySurfThickeningSharpedOffset( const ISurfTriMesh* Surf, i // recupero lo spigolo int nV1, nV2, nF1, nF2 ; double dAng ; Surf->GetEdge( nE, nV1, nV2, nF1, nF2, dAng) ; - // se non serve il prima a base quadrangolare di estrusione, passo al prossimo Edge - if ( dAng * dOffs < 0) - continue ; // recupero le coordinate dei vertici Point3d ptP1 ; Surf->GetVertex( nV1, ptP1) ; Point3d ptP2 ; Surf->GetVertex( nV2, ptP2) ; @@ -1074,10 +1742,23 @@ VolZmap::UpdateVolZMapBySurfThickeningSharpedOffset( const ISurfTriMesh* Surf, i Vector3d vtN1, vtN2 ; Surf->GetFacetNormal( nF1, vtN1) ; Surf->GetFacetNormal( nF2, vtN2) ; - // definisco la base del prima a base quadrangolare + // definisco la polyline di base del prisma PolyLine PLBase ; - // se esistono due facce adiacenti al lato corrente... - if ( nF1 != SVT_NULL && nF2 != SVT_NULL) { + // --- se lati libero, il prima ha base quadrata + if ( nF1 == SVT_NULL || nF2 == SVT_NULL) { + Vector3d vtOut = ( nF1 != SVT_NULL ? vtN1 : vtN2) ; + Vector3d vtOut1 = GetRotate( vtOut, vtEdge, ANG_RIGHT) ; + Vector3d vtDiag = abs( dOffs) * ( vtOut + vtOut1) ; + for ( int i = 0 ; i <= 4 ; ++ i) + PLBase.AddUPoint( 1. * i, ptP1 + GetRotate( vtDiag, vtEdge, i * ANG_RIGHT)) ; + } + // --- se lato tra due facce definite + else { + // se angolo concavo, le normali vanno invertite + if ( dAng < 0.) { + vtN1.Invert() ; + vtN2.Invert() ; + } // recupero frame intrinseco definito dal piano della base Frame3d frLoc ; if ( ! frLoc.Set( ptP1, vtN1 ^ vtN2, vtN1)) @@ -1118,14 +1799,6 @@ VolZmap::UpdateVolZMapBySurfThickeningSharpedOffset( const ISurfTriMesh* Surf, i PLBase.AddUPoint( ++ dPar, ptP1 + abs( dOffs) * vtN2) ; PLBase.AddUPoint( ++ dPar, ptP1) ; } - // se Free Edge... - else { - Vector3d vtOut = ( nF1 != SVT_NULL ? vtN1 : vtN2) ; - Vector3d vtOut1 = GetRotate( vtOut, vtEdge, ANG_RIGHT) ; - Vector3d vtDiag = abs( dOffs) * ( vtOut + vtOut1) ; - for ( int i = 0 ; i <= 4 ; ++ i) - PLBase.AddUPoint( 1. * i, ptP1 + GetRotate( vtDiag, vtEdge, i * ANG_RIGHT)) ; - } // definisco la superficie di estrusione e aggiorno gli spilloni CurveComposite CompoBase ; CompoBase.FromPolyLine( PLBase) ; @@ -1230,11 +1903,10 @@ VolZmap::UpdateVolZMapBySurfThickeningSharpedOffset( const ISurfTriMesh* Surf, i vFrGrid[3].Set( m_MapFrame.Orig(), m_MapFrame.VersZ(), m_MapFrame.VersX(), m_MapFrame.VersY()) ; // scrorro tutti i vertici della superficie - for ( int nV = 0 ; nV < Surf->GetVertexCount() ; ++ nV) { + for ( auto& Map : vMapVertBorders) { - // se al vertice nV esimo non ho piani di taglio, allora non devo fare nulla - if ( vMapVertBorders.find( nV) == vMapVertBorders.end()) - continue ; + // recupero l'indice del vertice + int nV = Map.first ; // recupero il vertice corrente Point3d ptVertex ; @@ -1307,7 +1979,8 @@ VolZmap::UpdateVolZMapBySurfThickeningSharpedOffset( const ISurfTriMesh* Surf, i // vettore di superfici TriMesh (aperte o chiuse). // Definizione : // - Lo Zmap di Offset ( positivo o negativo) di una superficie chiusa è automaticamente l'Offset stesso -// - Lo Zmap di Offset ( positivo o negativo) di una superficie aperta è ????? ( da capire...) +// - Lo Zmap di Offset ( positivo o negativo) di una superficie aperta è uno Zmap orientato distante +// i cui triangoli calcolati sono distanti al più |dOffs| dalla superficie iniziale //---------------------------------------------------------------------------- bool VolZmap::CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol, int nType) @@ -1334,6 +2007,7 @@ VolZmap::CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, do if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) continue ; // aggiorno lo ZMap + // --- se superficie Closed if ( Surf->IsClosed()) { // --- Fillet if ( nType == STMOFF_FILLET) { @@ -1353,9 +2027,23 @@ VolZmap::CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, do else return false ; } + // --- se superficie Open else { - // --- LE SUPERFICI APERTE SONO PER ORA DISABILITATE --- - Clear() ; + // --- Fillet + if ( nType == STMOFF_FILLET) { + if ( ! UpdateVolZMapByOpenSurfFilletOffset( Surf, dOffs, dTol)) + return false ; + } + // --- Chamfer + else if ( nType == STMOFF_CHAMFER) { + if ( ! UpdateVolZMapByOpenSurfSharpedOffset( Surf, STMOFF_CHAMFER, dOffs, dTol)) + return false ; + } + // --- Extend + else if ( nType == STMOFF_EXTEND) { + if ( ! UpdateVolZMapByOpenSurfSharpedOffset( Surf, STMOFF_EXTEND, dOffs, dTol)) + return false ; + } } } @@ -1417,3 +2105,151 @@ VolZmap::CreateFromTriMeshThickeningOffset( const CISURFTMPVECTOR& vSurf, double m_nShape = OFFSET ; return true ; } + +//----------------------------------------------------------------------------- +// Funzione di Offset Fillet per uno ZMap +// ---------------------------------------------------------------------------- +bool +VolZmap::OffsetFillet( double dOffs) +{ + // se Zmap non valido, errore + if ( ! IsValid()) + return false ; + + // controllo del valore di Offset + if ( abs( dOffs) < 10. * EPS_SMALL) + return true ; + + // se l'Offset è in positivo, devo estendere il Box complessivo dello Zmap + VolZmap VolZmapTemp ; + double dOkExtension = 0. ; + int nExtraDexel = 0 ; + if ( dOffs > 0.) { + double dExtension = dOffs + 1.5 * m_dStep ; + nExtraDexel = max( 1, static_cast( ceil( dExtension / m_dStep))) ; + dOkExtension = nExtraDexel * m_dStep ; + if ( ! VolZmapTemp.CreateEmpty( m_MapFrame.Orig() - Vector3d( 1., 1., 1.) * dOkExtension, + m_nNx[0] * m_dStep + 2. * dOkExtension, + m_nNx[1] * m_dStep + 2. * dOkExtension, + m_nNx[2] * m_dStep + 2. * dOkExtension, m_dStep, IsTriDexel())) + return false ; + } + + // ciclo sulle griglie + vector vMapPoints( m_nMapNum) ; + for ( int nGrid = 0 ; nGrid < m_nMapNum ; ++ nGrid) { + // scorro nI e nJ della griglia attuale + for ( int nI = 0 ; nI < m_nNx[nGrid] ; ++ nI) { + for ( int nJ = 0 ; nJ < m_nNy[nGrid] ; ++ nJ) { + // recupero la posizione corrente e il Voxel associato + int nPos = nJ * m_nNx[nGrid] + nI ; + vector& vDexel = m_Values[nGrid][nPos] ; + // scorro gli intervalli presenti su tale Dexel + for ( Data& Interval : vDexel) { + // se Offset positivo, inserisco tale intervallo nello Zmap creato + if ( dOffs > 0) { + VolZmapTemp.AddIntervals( nGrid, nI + nExtraDexel, nJ + nExtraDexel, + Interval.dMin + dOkExtension, Interval.dMax + dOkExtension, + Interval.vtMinN, Interval.vtMaxN, 0, false) ; + } + // inserisco punto finale ed iniziale dello spillone + for ( int dU = 0 ; dU < 2 ; ++ dU) { + double dZLoc = ( dU == 0 ? Interval.dMin : Interval.dMax) ; + vMapPoints[nGrid].emplace_back( + m_MapFrame.Orig() + + ( nI * m_dStep) * ( nGrid == 0 ? m_MapFrame.VersX() : + nGrid == 1 ? m_MapFrame.VersY() : + m_MapFrame.VersZ()) + + ( nJ * m_dStep) * ( nGrid == 0 ? m_MapFrame.VersY() : + nGrid == 1 ? m_MapFrame.VersZ() : + m_MapFrame.VersX()) + + ( dZLoc) * ( nGrid == 0 ? m_MapFrame.VersZ() : + nGrid == 1 ? m_MapFrame.VersX() : + m_MapFrame.VersY())) ; + } + } + } + } + } + + // se Offset positivo, il corrente diventa il temporaneo + if ( dOffs > 0) + CopyFrom( VolZmapTemp) ; + + // definisco un frame locale per ogni griglia + FRAME3DVECTOR vFrGrid ; vFrGrid.resize( m_nMapNum) ; + vFrGrid[0].Set( m_MapFrame.Orig(), m_MapFrame.VersX(), m_MapFrame.VersY(), m_MapFrame.VersZ()) ; + if ( m_nMapNum == 3) { + vFrGrid[1].Set( m_MapFrame.Orig(), m_MapFrame.VersY(), m_MapFrame.VersZ(), m_MapFrame.VersX()) ; + vFrGrid[2].Set( m_MapFrame.Orig(), m_MapFrame.VersZ(), m_MapFrame.VersX(), m_MapFrame.VersY()) ; + } + + // --- sottraggo le sfere nei punti creati + // ciclo sulle griglie + for ( int nGridS = 0 ; nGridS < m_nMapNum ; ++ nGridS) { + // scorro i punti ricavati dagli estremi degli spilloni + for ( int nP = 0 ; nP < int( vMapPoints[nGridS].size()) ; ++ nP) { + // recupero il punto corrente in globale + Point3d ptCenter = vMapPoints[nGridS][nP] ; + // shift di mezzo Step + ptCenter.Translate( 0.5 * m_dStep * ( vFrGrid[nGridS].VersX() + vFrGrid[nGridS].VersY())) ; + #if DEBUG + IGeoPoint3d* _pt = CreateGeoPoint3d() ; _pt->Set( ptCenter) ; + VT.emplace_back( _pt) ; + VC.emplace_back( nGridS == 0 ? BLUE : nGridS == 1 ? RED : GREEN) ; + ISurfTriMesh* pStm = GetSurfTriMeshSphere( abs( dOffs), 5. * EPS_SMALL) ; + pStm->Translate( ptCenter - ORIG) ; + VT.emplace_back( pStm) ; + VC.emplace_back( nGridS == 0 ? Color( 0., 0., 1., .3) : + nGridS == 1 ? Color( 1., 0., 0., .3) : + Color( 0., 1., 0., .3)) ; + #endif + // ogni sfera centrata nel punto, deve influenzare gli spilloni di ogni griglia + for ( int nGridE = 0 ; nGridE < m_nMapNum ; ++ nGridE) { + // porto il punto in posione corretta in locale alla griglia corrente + Point3d ptLoc = GetToLoc( ptCenter, vFrGrid[nGridE]) ; + if ( ! CreateOffsetSphereOnVertex( ptLoc, dOffs, nGridE, 0)) + return false ; + } + } + } + + #if DEBUG + SaveGeoObj( VT, VC, "C:\\Temp\\VolZMapOffs.nge") ; + #endif + return true ; +} + +//----------------------------------------------------------------------------- +// Funzione di Offset Sharped [Chanfer/Extend] per uno Zmap +// ---------------------------------------------------------------------------- +bool +VolZmap::OffsetSharped( double dOffs, int nType) +{ + // da capire... + return false ; +} + +// ---------------------------------------------------------------------------- +// Funzione di Offset per uno ZMap +// ---------------------------------------------------------------------------- +bool +VolZmap::Offset( double dOffs, int nType) +{ + // se Zmap non valido, errore + if ( ! IsValid()) + return false ; + + // controllo del parametro di Offset + if ( abs( dOffs) < 10. * EPS_SMALL) + return true ; + + // restituisco il tipo di Offset + if ( nType == VOLZMAP_OFFS_FILLET) + return OffsetFillet( dOffs) ; + else + return OffsetSharped( dOffs, nType) ; + + m_nShape = OFFSET ; + return true ; +} \ No newline at end of file