diff --git a/SurfTriMeshOffset.cpp b/SurfTriMeshOffset.cpp index dba93f3..84e70fe 100644 --- a/SurfTriMeshOffset.cpp +++ b/SurfTriMeshOffset.cpp @@ -81,9 +81,11 @@ CreateSurfTriMeshesOffset( const CISURFTMPVECTOR& vStm, double dOffs, double dPr return SumStm( vStm) ; // creo lo Zmap associato alle superfici TriMesh - PtrOwner pVolZmap( CreateVolZmap()) ; + PtrOwner pVolZmap( CreateBasicVolZmap()) ; if ( IsNull( pVolZmap) || ! pVolZmap->CreateFromTriMeshOffset( vStm, dOffs, dMyPrec, nType)) return nullptr ; + if ( ! pVolZmap->IsValid()) + return nullptr ; // restituisco la superficie TriMesh return ( pVolZmap->GetSurfTriMesh()) ; @@ -107,7 +109,7 @@ CreateSurfTriMeshesThickeningOffset( const CISURFTMPVECTOR& vStm, double dOffs, return SumStm( vStm) ; // creo lo Zmap associato alle superfici TriMesh - PtrOwner pVolZmap( CreateVolZmap()) ; + PtrOwner pVolZmap( CreateBasicVolZmap()) ; if ( IsNull( pVolZmap) || ! pVolZmap->CreateFromTriMeshThickeningOffset( vStm, dOffs, dMyPrec, nType)) return nullptr ; diff --git a/VolZmap.h b/VolZmap.h index 64d4dfb..ff62f3b 100644 --- a/VolZmap.h +++ b/VolZmap.h @@ -82,8 +82,6 @@ class VolZmap : public IVolZmap, public IGeoObjRW 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, double dExtraBox = 0.) override ; - bool CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol, int nType = STMOFF_FILLET) override ; - bool CreateFromTriMeshThickeningOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol, int nType = STMOFF_FILLET) override ; int GetBlockCount( void) const override ; int GetBlockUpdatingCounter( int nBlock) const override ; bool GetBlockTriangles( int nBlock, TRIA3DEXVECTOR& vTria) const override ; @@ -170,6 +168,8 @@ class VolZmap : public IVolZmap, public IGeoObjRW return *this ; } bool GetLineIntersection( const Point3d& ptP, const Vector3d& vtD, ILZIVECTOR& vIntersInfo) const ; bool GetPlaneIntersection( const Plane3d& plPlane, ICURVEPOVECTOR& vpLoop) const ; + bool CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol, int nType = STMOFF_FILLET) ; + bool CreateFromTriMeshThickeningOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol, int nType = STMOFF_FILLET) ; private : enum CubeType { VOX_EXTERN = 1, @@ -440,19 +440,23 @@ class VolZmap : public IVolZmap, public IGeoObjRW 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 InitVolZMapOffs( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) ; - bool InitVolZMapThickeningOffs( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) ; + 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 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 CreateOffsSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid, int nVertexType = 0) ; - bool CreateOffsCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, int nGrid, int nVertexType = 0) ; + 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 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) ; + bool CutByPlaneForOffset( const Plane3d& plCut) ; private : enum Status { ERR = 0, OK = 1, TO_VERIFY = 2} ; diff --git a/VolZmapOffset.cpp b/VolZmapOffset.cpp index 3907beb..61fbabc 100644 --- a/VolZmapOffset.cpp +++ b/VolZmapOffset.cpp @@ -23,6 +23,29 @@ using namespace std ; +#define DEBUG 0 +#if DEBUG + #include "/EgtDev/Include/EGkGeoObjSave.h" + #include "/EgtDev/Include/EGkGeoPoint3d.h" + #include "/EgtDev/Include/EGkGeoVector3d.h" + vector VT ; + vector VC ; +#endif + +// --------------------------------------------------------------------------- +// Tipi di Offset : +// - Con segno ( quindi Offset orientato, definito come positivo o negativo ) +// - Fillet +// - Sharped : +// - Chamfer +// - Extended +// - Senza segno ( Thinkening, analogo alla FatCurve per le curve ) +// - Fillet +// - Sharped : +// - Chamfer +// - Extended +// --------------------------------------------------------------------------- + //---------------------------------------------------------------------------- // Funzione per sottrarre intervalli lungo un Dexel per l'offset di una superficie TriMesh //---------------------------------------------------------------------------- @@ -31,8 +54,207 @@ VolZmap::SubtractIntervalsForOffset( int nGrid, int nI, int nJ, double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, int nToolNum, bool bSkipSwap) { - // TODO -- Aggiustare eventuali tolleranze - return SubtractIntervals( nGrid, nI, nJ, dMin, dMax, vtNMin, vtNMax, nToolNum, bSkipSwap) ; + // Controllo che dMin e dMax non siano quasi coincidenti + if ( abs( dMax - dMin) < EPS_ZERO) + return true ; + + // Controllo che il numero di griglia sia entro i limiti + if ( nGrid < 0 || nGrid > 2) + return false ; + + // Controllo che gli indici nI, nJ siano entro i limiti + if ( nI < 0 || nI >= m_nNx[nGrid] || + nJ < 0 || nJ >= m_nNy[nGrid]) + return false ; + + // Controllo che dMin < dMax + Vector3d vtNmi = vtNMin ; + Vector3d vtNma = vtNMax ; + if ( dMin > dMax) { + swap( dMin, dMax) ; + swap( vtNmi, vtNma) ; + } + + // Riporto le coordinate cicliche delle normali nell'ordine di partenza (da griglia a sistema intrinseco) + if ( ! bSkipSwap && nGrid == 1) { + swap( vtNmi.x, vtNmi.z) ; + swap( vtNmi.y, vtNmi.z) ; + swap( vtNma.x, vtNma.z) ; + swap( vtNma.y, vtNma.z) ; + } + else if ( ! bSkipSwap && nGrid == 2) { + swap( vtNmi.y, vtNmi.z) ; + swap( vtNmi.x, vtNmi.z) ; + swap( vtNma.y, vtNma.z) ; + swap( vtNma.x, vtNma.z) ; + } + + // Recupero dexel da modificare + int nPos = nJ * m_nNx[nGrid] + nI ; + vector& vDexel = m_Values[nGrid][nPos] ; + + // Ciclo sugli intervalli del dexel (ordinati in senso crescente) + bool bModified = false ; + for ( int i = 0 ; i < int( vDexel.size()) ; ++i) { + // Se interseca l'intervallo corrente + if ( dMin < vDexel[i].dMax - EPS_ZERO && dMax > vDexel[i].dMin + EPS_ZERO) { + bModified = true ; + // se devo limitarlo inferiormente + if ( dMin <= vDexel[i].dMin + EPS_ZERO && dMax < vDexel[i].dMax - EPS_ZERO) { + vDexel[i].dMin = dMax ; + vDexel[i].vtMinN = vtNma ; + vDexel[i].nToolMin = nToolNum ; + } + // se devo limitarlo superiormente + else if ( dMin > vDexel[i].dMin + EPS_ZERO && dMax >= vDexel[i].dMax - EPS_ZERO) { + vDexel[i].dMax = dMin ; + vDexel[i].vtMaxN = vtNmi ; + vDexel[i].nToolMax = nToolNum ; + } + // se devo dividerlo in due parti + else if ( dMin > vDexel[i].dMin + EPS_ZERO && dMax < vDexel[i].dMax - EPS_ZERO) { + // inserisco nuovo intervallo (parte superiore) + vDexel.insert( vDexel.begin() + i + 1, + { dMax, vtNma, nToolNum, + vDexel[i].dMax, vDexel[i].vtMaxN, vDexel[i].nToolMax, + vDexel[i].nCompo}) ; + // aggiorno il vecchio (parte inferiore) + vDexel[i].dMax = dMin ; + vDexel[i].vtMaxN = vtNmi ; + vDexel[i].nToolMax = nToolNum ; + ++ i ; + } + // altrimenti devo eliminarlo + else { + vDexel.erase( vDexel.begin() + i) ; + -- i ; + } + } + // se è tutto minore dell'intervallo corrente, ho finito + else if ( dMax <= vDexel[i].dMin + EPS_ZERO) + break ; + // altrimenti è tutto maggiore dell'intervallo corrente, passo al successivo + else + ; + } + + // Se nessuna modifica, esco + if ( ! bModified) + return true ; + + // Imposto ricalcolo della grafica + m_OGrMgr.Reset() ; + // Imposto ricalcolo numero di componenti connesse + m_nConnectedCompoCount = - 1 ; + + // Passo da indici di dexel a indici di voxel + nI /= m_nDexVoxRatio ; + nJ /= m_nDexVoxRatio ; + + // Determino quali blocchi sono stati modificati + if ( nGrid == 0) { + // Voxel lungo X + int nXStop = 1 ; + int nXBlock[2] ; + nXBlock[0] = min( nI / m_nVoxNumPerBlock, m_nFracLin[0] - 1) ; + if ( nI % N_VOXBLOCK == 0 && nXBlock[0] > 0) { + nXBlock[1] = nXBlock[0] - 1 ; + ++ nXStop ; + } + // Voxel lungo Y + int nYStop = 1 ; + int nYBlock[2] ; + nYBlock[0] = min( nJ / m_nVoxNumPerBlock, m_nFracLin[1] - 1) ; + if ( nJ % N_VOXBLOCK == 0 && nYBlock[0] > 0) { + nYBlock[1] = nYBlock[0] - 1 ; + ++ nYStop ; + } + // Voxel lungo Z + int nVoxNumZ = int( m_nNy[1] / m_nDexVoxRatio + ( m_nNy[1] % m_nDexVoxRatio == 0 ? 1 : 2)) ; + int nMinK = Clamp( int( floor( ( ( dMin - 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) - EPS_SMALL))), 0, nVoxNumZ - 2) ; + int nMaxK = Clamp( int( floor( ( ( dMax + 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) + EPS_SMALL))), 0, nVoxNumZ - 2) ; + int nMinZBlock = ( m_nMapNum == 1 ? 0 : Clamp( nMinK / int( m_nVoxNumPerBlock), 0, int( m_nFracLin[2] - 1))) ; + int nMaxZBlock = min( int( m_nFracLin[2] - 1), nMaxK / int( m_nVoxNumPerBlock)) ; + // Assegno flag ai voxel + for ( int tI = 0 ; tI < nXStop ; ++ tI) { + for ( int tJ = 0 ; tJ < nYStop ; ++ tJ) { + for ( int k = nMinZBlock ; k <= nMaxZBlock ; ++ k) { + int nBlockNum = k * m_nFracLin[0] * m_nFracLin[1] + nYBlock[tJ] * m_nFracLin[0] + nXBlock[tI] ; + m_BlockToUpdate[nBlockNum] = true ; + } + } + } + } + + else if ( nGrid == 1) { + // Voxel lungo Y + int nYStop = 1 ; + int nYBlock[2] ; + nYBlock[0] = min( nI / m_nVoxNumPerBlock, m_nFracLin[1] - 1) ; + if ( nI % N_VOXBLOCK == 0 && nYBlock[0] > 0) { + nYBlock[1] = nYBlock[0] - 1 ; + ++ nYStop ; + } + // Voxel lungo Z + int nZStop = 1 ; + int nZBlock[2] ; + nZBlock[0] = min( nJ / m_nVoxNumPerBlock, m_nFracLin[2] - 1) ; + if ( nJ % N_VOXBLOCK == 0 && nZBlock[0] > 0) { + nZBlock[1] = nZBlock[0] - 1 ; + ++ nZStop ; + } + // Voxel lungo X + int nVoxNumX = int( m_nNx[0] / m_nDexVoxRatio + ( m_nNx[0] % m_nDexVoxRatio == 0 ? 1 : 2)) ; + int nMinI = Clamp( int( floor( ( ( dMin - 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) - EPS_SMALL))), 0, nVoxNumX - 2) ; + int nMaxI = Clamp( int( floor( ( ( dMax + 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) + EPS_SMALL))), 0, nVoxNumX - 2) ; + int nMinXBlock = Clamp( nMinI / int( m_nVoxNumPerBlock), 0, int( m_nFracLin[0] - 1)) ; + int nMaxXBlock = min( int( m_nFracLin[0] - 1), nMaxI / int( m_nVoxNumPerBlock)) ; + // Assegno flag ai voxel + for ( int tI = 0 ; tI < nYStop ; ++ tI) { + for ( int tJ = 0 ; tJ < nZStop ; ++ tJ) { + for ( int k = nMinXBlock ; k <= nMaxXBlock ; ++ k) { + int nBlockNum = nZBlock[tJ] * m_nFracLin[0] * m_nFracLin[1] + nYBlock[tI] * m_nFracLin[0] + k ; + m_BlockToUpdate[nBlockNum] = true ; + } + } + } + } + + else if ( nGrid == 2) { + // Voxel lungo X + int nXStop = 1 ; + int nXBlock[2] ; + nXBlock[0] = min( nJ / m_nVoxNumPerBlock, m_nFracLin[0] - 1) ; + if ( nJ % N_VOXBLOCK == 0 && nXBlock[0] > 0) { + nXBlock[1] = nXBlock[0] - 1 ; + ++ nXStop ; + } + // Voxel lungo Z + int nZStop = 1 ; + int nZBlock[2] ; + nZBlock[0] = min( nI / m_nVoxNumPerBlock, m_nFracLin[2] - 1) ; + if ( nI % N_VOXBLOCK == 0 && nZBlock[0] > 0) { + nZBlock[1] = nZBlock[0] - 1 ; + ++ nZStop ; + } + // Voxel lungo Y + int nVoxNumY = int( m_nNy[0] / m_nDexVoxRatio + ( m_nNy[0] % m_nDexVoxRatio == 0 ? 1 : 2)) ; + int nMinJ = Clamp( int( floor( ( ( dMin - 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) - EPS_SMALL))), 0, nVoxNumY - 2) ; + int nMaxJ = Clamp( int( floor( ( ( dMax + 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) + EPS_SMALL))), 0, nVoxNumY - 2) ; + int nMinYBlock = Clamp( nMinJ / int( m_nVoxNumPerBlock), 0, int( m_nFracLin[1] - 1)) ; + int nMaxYBlock = min( int( m_nFracLin[1] - 1), nMaxJ / int( m_nVoxNumPerBlock)) ; + // Assegno flag ai voxel + for ( int tI = 0 ; tI < nZStop ; ++ tI) { + for ( int tJ = 0 ; tJ < nXStop ; ++ tJ) { + for ( int k = nMinYBlock ; k <= nMaxYBlock ; ++ k) { + int nBlockNum = nZBlock[tI] * m_nFracLin[0] * m_nFracLin[1] + k * m_nFracLin[0] + nXBlock[tJ] ; + m_BlockToUpdate[nBlockNum] = true ; + } + } + } + } + + return true ; } //---------------------------------------------------------------------------- @@ -48,11 +270,79 @@ VolZmap::AddIntervalsForOffset( int nGrid, int nI, int nJ, } //---------------------------------------------------------------------------- -// 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 +// Funzione per tagliare uno ZMap di Offset con un piano //---------------------------------------------------------------------------- bool -VolZmap::CreateOffsSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid, int nTool) +VolZmap::CutByPlaneForOffset( const Plane3d& plCut) +{ + // Controllo che il piano di taglio sia valido + if ( ! plCut.IsValid()) + return false ; + + // Porto il piano nel riferimento intrinseco + Plane3d plMyPlane = plCut ; + plMyPlane.ToLoc( m_MapFrame) ; + + // Interseco lo Zmap col piano, ciclando sulle griglie + for ( int nMap = 0 ; nMap < int( m_nMapNum) ; ++ nMap) { + + // Porto il piano nel riferimento di griglia + if ( nMap == 1) { + Frame3d frGrid ; frGrid.Set( ORIG, Y_AX, Z_AX, X_AX) ; + plMyPlane.ToLoc( frGrid) ; + } + else if (nMap == 2) { + Frame3d frGrid ; frGrid.Set( ORIG, Y_AX, Z_AX, X_AX) ; + plMyPlane.ToLoc( frGrid) ; + } + + // Ciclo sui dexel della mappa + for ( int nD = 0 ; nD < int( m_Values[nMap].size()) ; ++ nD) { + // Se spillone vuoto, passo al successivo + if ( m_Values[nMap][nD].empty()) + continue ; + // Recupero gli indici dello spillone + int nI = nD % m_nNx[nMap] ; + int nJ = nD / m_nNx[nMap] ; + // Recupero estremi spillone + double dMin = m_Values[nMap][nD][0].dMin ; + double dMax = m_Values[nMap][nD][m_Values[nMap][nD].size() - 1].dMax ; + Point3d ptSt( ( nI + 0.5) * m_dStep, ( nJ + 0.5) * m_dStep, dMin) ; + Point3d ptEn( ( nI + 0.5) * m_dStep, ( nJ + 0.5) * m_dStep, dMax) ; + // Distanze degli estremi del segmento dal piano + double dStDist = DistPointPlane( ptSt, plMyPlane) ; + double dEnDist = DistPointPlane( ptEn, plMyPlane) ; + + // Se entrambi gli estremi sotto il piano, non faccio nulla + if ( dStDist < EPS_SMALL && dEnDist < EPS_SMALL) + ; + // Se entrambi gli estremi sopra il piano, elimino tutto + else if ( dStDist > - EPS_SMALL && dEnDist > - EPS_SMALL) + SubtractIntervalsForOffset( nMap, nI, nJ, dMin, dMax, V_NULL, V_NULL, 1) ; + // Se parte iniziale da conservare... + else if ( dStDist < 0) { + // ... per similitudine recupero la porzione da eliminare + double dInt = dMin + ( dMax - dMin) * abs( dStDist) / ( abs( dStDist) + abs( dEnDist)) ; + SubtractIntervalsForOffset( nMap, nI, nJ, dInt, dMax, plMyPlane.GetVersN(), V_NULL, 1) ; + } + // Se parte finale da conservare... + else if ( dEnDist < 0) { + // ... per similitudine recupero la porzione da eliminare + double dInt = dMin + ( dMax - dMin) * abs( dStDist) / ( abs( dStDist) + abs( dEnDist)) ; + SubtractIntervalsForOffset( nMap, nI, nJ, dMin, dInt, V_NULL, plMyPlane.GetVersN(), 1) ; + } + } + } + + 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 +//---------------------------------------------------------------------------- +bool +VolZmap::CreateOffsetSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid, int nTool) { // determino il Box della sfera posizionata su tale vertice BBox3d BBoxSphere( ptV - dOffs * Vector3d( 1., 1., 1.), @@ -90,11 +380,10 @@ VolZmap::CreateOffsSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid, //---------------------------------------------------------------------------- // Funzione per la creazione di un cilindro di Offset sul vertice di una TriMesh con cui -// aggiungere o sottrarre intervalli lungo i Dexel coinvolti. +// aggiungere o sottrarre intervalli lungo i Dexel coinvolti. //---------------------------------------------------------------------------- bool -VolZmap::CreateOffsCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, int nGrid, - int nTool) +VolZmap::CreateOffsetCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, int nGrid, int nTool) { // determino la lunghezza dello spigolo corrente double dH = Dist( ptP1, ptP2) ; @@ -143,14 +432,79 @@ VolZmap::CreateOffsCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, dou return true ; } +//---------------------------------------------------------------------------- +// Funzione per creare una superficie di etrusione 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) +{ + // 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) { + vPL[i].Translate( - abs( dOffs) * vtN) ; + 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 * abs( 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 + // se Offset Thickle allora sommo tutti i contributi senza tener conto del segno dell'Offset + if ( bThickle) { + AddSurfTm( pStmExtr) ; + } + // se Offset orientato, allora aggiungo o sottraggo gli intervalli a seconda del segno di Offset + else { + if ( dOffs > 0.) + AddSurfTm( pStmExtr) ; + else + SubtractSurfTm( pStmExtr) ; + } + // dealloco le curve + for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { + delete ( vpCrvs[i]) ; + vpCrvs[i] = nullptr ; + } + } + + 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 : // - le superfici chiuse possono già inserire i rispettivi spilloni -// - le superfici aperte non danno contributo +// - le superfici aperte non danno contributo ( solo per il Box ) //---------------------------------------------------------------------------- bool -VolZmap::InitVolZMapOffs( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) +VolZmap::InitVolZMapOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) { // se non ho superfici, non faccio nulla if ( vSurf.empty()) @@ -216,7 +570,7 @@ VolZmap::InitVolZMapOffs( const CISURFTMPVECTOR& vSurf, double dOffs, double dTo // L'idea è quella di creare uno ZMap vuoto a partire dal Box complessivo delle superfici TriMesh //---------------------------------------------------------------------------- bool -VolZmap::InitVolZMapThickeningOffs( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) +VolZmap::InitVolZMapThickeningOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) { // se non ho superfici, non faccio nulla if ( vSurf.empty()) @@ -227,7 +581,7 @@ VolZmap::InitVolZMapThickeningOffs( const CISURFTMPVECTOR& vSurf, double dOffs, return false ; } - // definisco la tolleranza di espansione del Box per la creazione dellop ZMap + // definisco la tolleranza di espansione del Box per la creazione dello ZMap double dBoxExpansion = ( abs( dOffs) + 1.5 * dTol) + 10 * EPS_SMALL ; // calcolo il Box complessivo @@ -259,7 +613,7 @@ VolZmap::UpdateVolZMapByOpenSurfFilletOffset( const ISurfTriMesh* Surf, double d //---------------------------------------------------------------------------- // [Fillet] Funzione per aggiornare mediante Sfere, Cilindri e Facce di estrusione lo ZMap corrente -// mediante una superficie chiusa +// mediante una superficie chiusa //---------------------------------------------------------------------------- bool VolZmap::UpdateVolZMapByClosedSurfFilletOffset( const ISurfTriMesh* Surf, double dOffs, double dTol) @@ -288,7 +642,6 @@ VolZmap::UpdateVolZMapByClosedSurfFilletOffset( const ISurfTriMesh* Surf, double // "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) ; @@ -320,15 +673,15 @@ VolZmap::UpdateVolZMapByClosedSurfFilletOffset( const ISurfTriMesh* Surf, double 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)) + if ( ! CreateOffsetCylinderOnEdge( ptP1, ptP2, dOffs, nGrid)) return false ; // aggiungo/sottraggo gli intervalli definiti dalla sfera if ( ! vbVert[nV1]) { - if ( ! CreateOffsSphereOnVertex( ptP1, dOffs, nGrid)) + if ( ! CreateOffsetSphereOnVertex( ptP1, dOffs, nGrid)) return false ; } if ( ! vbVert[nV2]) { - if ( ! CreateOffsSphereOnVertex( ptP2, dOffs, nGrid)) + if ( ! CreateOffsetSphereOnVertex( ptP2, dOffs, nGrid)) return false ; } } @@ -336,46 +689,282 @@ VolZmap::UpdateVolZMapByClosedSurfFilletOffset( const ISurfTriMesh* Surf, double vbVert[nV2] = true ; } // ----------------------- 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])) { - // dealloco le curve - for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { - delete ( vpCrvs[i]) ; - vpCrvs[i] = nullptr ; + if ( ! CreateOffsetExtrusionFace( Surf, dOffs, false)) + return true ; + + return true ; +} + +//---------------------------------------------------------------------------- +// [Sharped ( Chamfer/Extend )] Funzione per aggiornare mediantePrismi, Involucri concvessi +// e Facce di estrusione lo ZMap corrente mediante una superficie generica (aperta o chiusa) +//---------------------------------------------------------------------------- +bool +VolZmap::UpdateVolZMapByClosedSurfSharpedOffset( const ISurfTriMesh* Surf, int nType, 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 sia in positivo che in negativo della faccia lungo la sua normale) + // - Su ogni lato viene definito un prisma a base quadrangolare o pentagonale + // - Su ogni vertice viene definita una sfera che viene tagliata con i piani definiti + // dalle facce dei prismi ( sia laterali che di base ) + + // ----------------------- Facce ----------------------- + if ( ! CreateOffsetExtrusionFace( Surf, dOffs, false)) + return true ; + + // definisco una mappa tra vertici e contorni orientati degli ZMap derivanti dagli Edges adiacenti + unordered_map> vMapVertBorders( Surf->GetVertexCount()) ; + + // ----------------------- prismi ----------------------- + 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) ; + // 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) ; + // 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 l'offset è in negativo, devo orientare il prisma verso l'interno della superficie + if ( dOffs < 0.) { + vtN1.Invert() ; + vtN2.Invert() ; + } + // definisco la base del prima a base quadrangolare + PolyLine PLBase ; + // 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 + 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/sottraggo gli intervalli definiti dal prisma + if ( dOffs > 0.) + AddSurfTm( pStmPrism) ; + else + SubtractSurfTm( 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].emplace_back( plCut) ; + if ( plCut.Set( ptP2, - vEdgeN)) + vMapVertBorders[nV2].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].emplace_back( plCut) ; + vMapVertBorders[nV2].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 } - 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) { - // dealloco le curve - for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { - delete ( vpCrvs[i]) ; - vpCrvs[i] = nullptr ; + } + + #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 ( int nV = 0 ; nV < Surf->GetVertexCount() ; ++ nV) { + + // se al vertice nV esimo non ho piani di taglio, allora non devo fare nulla + if ( vMapVertBorders.find( nV) == vMapVertBorders.end()) + 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) ; + } + + // taglio lo Zmap con tutti i piani ricavati ( evitando quelli ripetuti ) + for ( int i = 0 ; i < int( vMapVertBorders[nV].size()) - 1 ; ++ i) { + bool bCut = true ; + const Plane3d& plPlaneA = vMapVertBorders[nV][i] ; + for ( int j = i + 1 ; bCut && j < int( vMapVertBorders[nV].size()) ; ++ j) { + const Plane3d& plPlaneB = vMapVertBorders[nV][j] ; + bCut = ( ! AreSamePlaneApprox( plPlaneA, plPlaneB)) ; } - return false ; + if ( bCut) + VolZMapCut.CutByPlaneForOffset( plPlaneA) ; } - // aggiorno gli spilloni - if ( dOffs > 0.) - AddSurfTm( pStmExtr) ; - else - SubtractSurfTm( pStmExtr) ; - // dealloco le curve - for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { - delete ( vpCrvs[i]) ; - vpCrvs[i] = nullptr ; + VolZMapCut.CutByPlaneForOffset( vMapVertBorders[nV].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 + if ( dOffs > 0.) + AddIntervalsForOffset( nGrid, i, j, Interval.dMin, Interval.dMax, + Interval.vtMinN, Interval.vtMaxN, 0, true) ; + else + SubtractIntervalsForOffset( nGrid, i, j, Interval.dMin, Interval.dMax, + Interval.vtMinN, Interval.vtMaxN, 0, true) ; + } + } + } } } @@ -383,7 +972,7 @@ VolZmap::UpdateVolZMapByClosedSurfFilletOffset( const ISurfTriMesh* Surf, double } //---------------------------------------------------------------------------- -// [Fillet Thickening] Funzione per aggiornare mediante Sfere, Cilindri e Facce di estrusione lo ZMap corrente +// [Fillet Thickening] Funzione per aggiornare mediante Sfere, Cilindri e Facce di estrusione lo ZMap corrente // mediante una superficie generica (aperta o chiusa) //---------------------------------------------------------------------------- bool @@ -421,15 +1010,15 @@ VolZmap::UpdateVolZMapBySurfThickeningFilletOffset( const ISurfTriMesh* Surf, do 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, abs( dOffs), nGrid)) + if ( ! CreateOffsetCylinderOnEdge( ptP1, ptP2, abs( dOffs), nGrid)) return false ; // aggiungo/sottraggo gli intervalli definiti dalla sfera if ( ! vbVert[nV1]) { - if ( ! CreateOffsSphereOnVertex( ptP1, abs( dOffs), nGrid)) + if ( ! CreateOffsetSphereOnVertex( ptP1, abs( dOffs), nGrid)) return false ; } if ( ! vbVert[nV2]) { - if ( ! CreateOffsSphereOnVertex( ptP2, abs( dOffs), nGrid)) + if ( ! CreateOffsetSphereOnVertex( ptP2, abs( dOffs), nGrid)) return false ; } } @@ -437,46 +1026,279 @@ VolZmap::UpdateVolZMapBySurfThickeningFilletOffset( const ISurfTriMesh* Surf, do vbVert[nV2] = true ; } // ----------------------- 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])) { - // dealloco le curve - for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { - delete ( vpCrvs[i]) ; - vpCrvs[i] = nullptr ; + if ( ! CreateOffsetExtrusionFace( Surf, abs( dOffs), true)) + return false ; + + return true ; +} + +//---------------------------------------------------------------------------- +// [Sharped ( Chamfer/Extend ) Thickening] Funzione per aggiornare mediante Prismi, Involucri convessi +// e Facce di estrusione lo ZMap corrente mediante una superficie generica (aperta o chiusa) +//---------------------------------------------------------------------------- +bool +VolZmap::UpdateVolZMapBySurfThickeningSharpedOffset( 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 sulla superficie + if ( Surf == nullptr) + return false ; + if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) + return true ; + + // ----------------------- Facce ----------------------- + if ( ! CreateOffsetExtrusionFace( Surf, abs( dOffs), true)) + return true ; + + // definisco una mappa tra vertici e contorni orientati degli ZMap derivanti dagli Edges adiacenti + unordered_map> vMapVertBorders( Surf->GetVertexCount()) ; + + // ----------------------- prismi ----------------------- + // 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) ; + // 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) ; + // 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) ; + // definisco la base del prima a base quadrangolare + PolyLine PLBase ; + // se esistono due facce adiacenti al lato corrente... + if ( nF1 != SVT_NULL && nF2 != SVT_NULL) { + // 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 + 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) ; + } + // 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) ; + PtrOwner pStmPrism( GetSurfTriMeshByExtrusion( &CompoBase, vtEdge, true)) ; + if ( ! IsNull( pStmPrism)) { + 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].emplace_back( plCut) ; + if ( plCut.Set( ptP2, - vEdgeN)) + vMapVertBorders[nV2].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].emplace_back( plCut) ; + vMapVertBorders[nV2].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 } - 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) { - // 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) ; - // dealloco le curve - for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { - delete ( vpCrvs[i]) ; - vpCrvs[i] = nullptr ; } } + #if DEBUG + VT.emplace_back( Surf->Clone()) ; + VC.emplace_back( GREEN) ; + #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 ( int nV = 0 ; nV < Surf->GetVertexCount() ; ++ nV) { + + // se al vertice nV esimo non ho piani di taglio, allora non devo fare nulla + if ( vMapVertBorders.find( nV) == vMapVertBorders.end()) + 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) ; + } + + // taglio lo Zmap con tutti i piani ricavati ( evitando quelli ripetuti ) + for ( int i = 0 ; i < int( vMapVertBorders[nV].size()) - 1 ; ++ i) { + bool bCut = true ; + const Plane3d& plPlaneA = vMapVertBorders[nV][i] ; + for ( int j = i + 1 ; bCut && j < int( vMapVertBorders[nV].size()) ; ++ j) { + const Plane3d& plPlaneB = vMapVertBorders[nV][j] ; + bCut = ( ! AreSamePlaneApprox( plPlaneA, plPlaneB)) ; + } + if ( bCut) + VolZMapCut.CutByPlaneForOffset( plPlaneA) ; + } + VolZMapCut.CutByPlaneForOffset( vMapVertBorders[nV].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 + SaveGeoObj( VT, VC, "C:\\Temp\\VertInfo.nge") ; + #endif return true ; } @@ -496,14 +1318,14 @@ VolZmap::CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, do return false ; } // verifica sul parametro di Offset ( coerente con Curve e FlatRegion) - if ( abs( dOffs) < 10 * EPS_SMALL) + if ( abs( dOffs) < 10. * EPS_SMALL) return true ; // se non ho superfici, non faccio nulla if ( vSurf.empty()) return true ; // inizializzo lo Zmap di Offset a partire dalle superfici - if ( ! InitVolZMapOffs( vSurf, dOffs, dTol)) + if ( ! InitVolZMapOffset( vSurf, dOffs, dTol)) return false ; // scorro le superfici @@ -513,13 +1335,27 @@ VolZmap::CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, do continue ; // aggiorno lo ZMap if ( Surf->IsClosed()) { + // --- Fillet if ( nType == STMOFF_FILLET) { if ( ! UpdateVolZMapByClosedSurfFilletOffset( Surf, dOffs, dTol)) return false ; } + // --- Chamfer + else if ( nType == STMOFF_CHAMFER) { + if ( ! UpdateVolZMapByClosedSurfSharpedOffset( Surf, STMOFF_CHAMFER, dOffs, dTol)) + return false ; + } + // --- Extend + else if ( nType == STMOFF_EXTEND) { + if ( ! UpdateVolZMapByClosedSurfSharpedOffset( Surf, STMOFF_EXTEND, dOffs, dTol)) + return false ; + } + else + return false ; } else { // --- LE SUPERFICI APERTE SONO PER ORA DISABILITATE --- + Clear() ; } } @@ -540,14 +1376,17 @@ VolZmap::CreateFromTriMeshThickeningOffset( const CISURFTMPVECTOR& vSurf, double return false ; } // verifica sul parametro di Offset ( coerente con Curve e FlatRegion) - if ( abs( dOffs) < 10 * EPS_SMALL) + if ( abs( dOffs) < 10. * EPS_SMALL) return true ; // se non ho superfici, non faccio nulla if ( vSurf.empty()) return true ; // inizializzo lo Zmap di Offset a partire dalle superfici - if ( ! InitVolZMapThickeningOffs( vSurf, dOffs, dTol)) + double dBoxExpand = dOffs ; + if ( nType == STMOFF_EXTEND || nType == STMOFF_CHAMFER) + dBoxExpand *= 2 ; + if ( ! InitVolZMapThickeningOffset( vSurf, dBoxExpand, dTol)) return false ; // scorro le superfici @@ -557,10 +1396,22 @@ VolZmap::CreateFromTriMeshThickeningOffset( const CISURFTMPVECTOR& vSurf, double continue ; // aggiorno lo Zmap + // --- Fillet if ( nType == STMOFF_FILLET) { if ( ! UpdateVolZMapBySurfThickeningFilletOffset( Surf, dOffs, dTol)) return false ; } + // --- Sharped ( Chamfer o Extend ) + else if ( nType == STMOFF_CHAMFER) { + if ( ! UpdateVolZMapBySurfThickeningSharpedOffset( Surf, STMOFF_CHAMFER, dOffs, dTol)) + return false ; + } + else if ( nType == STMOFF_EXTEND) { + if ( ! UpdateVolZMapBySurfThickeningSharpedOffset( Surf, STMOFF_EXTEND, dOffs, dTol)) + return false ; + } + else + return false ; } m_nShape = OFFSET ;