//---------------------------------------------------------------------------- // EgalTech 2025-2025 //---------------------------------------------------------------------------- // File : OffsetSurfTm.cpp Data : 09.06.25 Versione : 2.7e3 // 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 // // // // Modifiche : 09.06.25 RE Creazione modulo. // // //---------------------------------------------------------------------------- #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 #include "set" #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" /* 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 ) // - 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 //---------------------------------------------------------------------------- bool VolZmap::SubtractIntervalsForOffset( int nGrid, int nI, int nJ, double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, int nToolNum, bool 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 ; } //---------------------------------------------------------------------------- // Funzione per aggiungere intervalli lungo un Dexel per l'offset di una superficie TriMesh //---------------------------------------------------------------------------- bool VolZmap::AddIntervalsForOffset( int nGrid, int nI, int nJ, double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, int nToolMin, int nToolMax, bool bSkipSwap) { // Controllo che il numero di griglia sia entro i limiti if ( nGrid < 0 || nGrid > 2) return false ; // Controllo che indici nI, nJ siano entro i limiti if ( nI < 0 || nI >= m_nNx[nGrid] || nJ < 0 || nJ >= m_nNy[nGrid]) return false ; // Controllo che dMin < dMax Vector3d vtNmi = vtNMin ; Vector3d vtNma = vtNMax ; if ( dMin > dMax) { swap( dMin, dMax) ; swap( vtNmi, vtNma) ; } // Restringo minimo e massimo entro i limiti della mappa if ( dMin < m_dMinZ[nGrid]) { dMin = m_dMinZ[nGrid] ; if ( ! bSkipSwap) vtNmi = - Z_AX ; } else if ( dMin > m_dMaxZ[nGrid]) { dMin = m_dMaxZ[nGrid] ; if ( ! bSkipSwap) vtNmi = - Z_AX ; } if ( dMax < m_dMinZ[nGrid]) { dMax = m_dMinZ[nGrid] ; if ( ! bSkipSwap) vtNma = Z_AX ; } else if ( dMax > m_dMaxZ[nGrid]) { dMax = m_dMaxZ[nGrid] ; if ( ! bSkipSwap) vtNma = Z_AX ; } // Controllo che dMin e dMax non siano quasi coincidenti if ( abs( dMax - dMin) < EPS_SMALL) return true ; // Riporto le coordinate cicliche nell'ordine di partenza if ( !bSkipSwap && nGrid == 1) { swap( vtNmi.x, vtNmi.z) ; swap( vtNmi.y, vtNmi.z) ; swap( vtNma.x, vtNma.z) ; swap( vtNma.y, vtNma.z) ; } else if ( !bSkipSwap && nGrid == 2) { swap( vtNmi.y, vtNmi.z) ; swap( vtNmi.x, vtNmi.z) ; swap( vtNma.y, vtNma.z) ; swap( vtNma.x, vtNma.z) ; } // Calcolo nPos int nPos = nJ * m_nNx[nGrid] + nI ; vector& vDexel = m_Values[nGrid][nPos] ; bool bModified = false ; // Non esistono segmenti if ( vDexel.empty()) { vDexel.emplace_back() ; vDexel.back().dMin = dMin ; vDexel.back().vtMinN = vtNmi ; vDexel.back().nToolMin = nToolMin ; vDexel.back().dMax = dMax ; vDexel.back().vtMaxN = vtNma ; vDexel.back().nToolMax = nToolMax ; m_OGrMgr.Reset() ; bModified = true ; } // Esiste almeno un segmento else { // Cerco l'ultimo intervallo a sinistra e l'ultimo intervallo a destra // di quello da aggiungere, che non interferiscono con quest'ultimo. auto itLastLeft = vDexel.end() ; auto itFirstRight = vDexel.end() ; for ( auto it = vDexel.begin() ; it != vDexel.end() ; ++ it) { if ( dMin > it->dMax + EPS_SMALL) itLastLeft = it ; if ( dMax < it->dMin - EPS_SMALL && itFirstRight == vDexel.end()) itFirstRight = it ; } // Esistono intervalli a sinistra. if ( itLastLeft != vDexel.end()) { // Intervallo successivo all'ultimo a sinistra auto itNextToLastLeft = itLastLeft ; ++ itNextToLastLeft ; // Il successivo non esiste. if ( itNextToLastLeft == vDexel.end()) { // Aggiungo il nuovo segmento vDexel.emplace_back() ; vDexel.back().dMin = dMin ; vDexel.back().dMax = dMax ; vDexel.back().vtMinN = vtNmi ; vDexel.back().vtMaxN = vtNma; vDexel.back().nToolMin = nToolMin ; vDexel.back().nToolMax = nToolMax ; bModified = true ; } // Il successivo esiste. else { // Il successivo è il primo a destra. if ( itNextToLastLeft == itFirstRight) { // Inserisco nuovo segmento Data NewSegment ; NewSegment.dMin = dMin ; NewSegment.dMax = dMax ; NewSegment.vtMinN = vtNmi ; NewSegment.vtMaxN = vtNma ; NewSegment.nToolMin = nToolMin ; NewSegment.nToolMax = nToolMax ; //NewSegment.nCompo = ; vDexel.insert( itFirstRight, NewSegment) ; bModified = true ; } else { // Il successivo non esce a sinistra da quello da aggiungere. if ( itNextToLastLeft->dMin > dMin + EPS_SMALL) { itNextToLastLeft->dMin = dMin ; itNextToLastLeft->vtMinN = vtNmi ; itNextToLastLeft->nToolMin = nToolMin ; } // Cerco l'ultimo segmento che interferisce con quello da aggiungere. auto itPrevToFirstRight = vDexel.end() ; for ( auto it = itNextToLastLeft ; it != itFirstRight ; ++ it) { itPrevToFirstRight = it ; } // L'ultimo che interferisce non esce a destra da quello da aggiungere. if ( itPrevToFirstRight->dMax < dMax - EPS_SMALL) { itNextToLastLeft->dMax = dMax ; itNextToLastLeft->vtMaxN = vtNma ; itNextToLastLeft->nToolMax = nToolMax ; bModified = true ; } else { itNextToLastLeft->dMax = itPrevToFirstRight->dMax ; itNextToLastLeft->vtMaxN = itPrevToFirstRight->vtMaxN ; itNextToLastLeft->nToolMax = nToolMax ; bModified = true ; } auto itFirstToCancel = itNextToLastLeft ; ++ itFirstToCancel ; vDexel.erase( itFirstToCancel, itFirstRight) ; } } } // Non esistono neanche a destra. else if ( itFirstRight == m_Values[nGrid][nPos].end()) { // Il primo intervallo non sporge a sinistra if ( vDexel.begin()->dMin > dMin + EPS_SMALL) { vDexel.begin()->dMin = dMin ; vDexel.begin()->vtMinN = vtNmi ; vDexel.begin()->nToolMin = nToolMin ; bModified = true ; } // L'ultimo intervallo sporge a destra. if ( m_Values[nGrid][nPos].back().dMax > dMax + EPS_SMALL) { // Ci sono più segmenti, inglobo tutti nel primo. if ( vDexel.back().dMax > vDexel.begin()->dMax + EPS_SMALL) { vDexel.begin()->dMax = vDexel.back().dMax ; vDexel.begin()->vtMaxN = vDexel.back().vtMaxN ; vDexel.begin()->nToolMax = nToolMax ; bModified = true ; } } // L'ultimo intervallo non sporge a destra. else { vDexel.begin()->dMax = dMax ; vDexel.begin()->vtMaxN = vtNma ; vDexel.begin()->nToolMax = nToolMax ; bModified = true ; } vDexel.erase( vDexel.begin() + 1, vDexel.end()) ; } // A destra esistono. else { // Tutti i segmenti sono a destra di qullo da aggiungere. if ( itFirstRight == vDexel.begin()) { // Inserisco nuovo segmento- Data NewSegment ; NewSegment.dMin = dMin ; NewSegment.dMax = dMax ; NewSegment.vtMinN = vtNmi ; NewSegment.vtMaxN = vtNma ; NewSegment.nToolMin = nToolMin ; NewSegment.nToolMax = nToolMax ; vDexel.insert( vDexel.begin(), NewSegment) ; bModified = true ; } else { // Se il primo segmento non esce a sinistra da quello da aggiungere, cambio l'inizio. if ( vDexel.begin()->dMin > dMin + EPS_SMALL) { vDexel.begin()->dMin = dMin ; vDexel.begin()->vtMinN = vtNmi ; vDexel.begin()->nToolMin = nToolMin ; bModified = true ; } // Cerco l'ultimo segmento che interferisce con quello da aggiungere. auto itPrevToFirstRight = vDexel.begin() ; for ( auto it = m_Values[nGrid][nPos].begin() ; it != itFirstRight ; ++ it) { itPrevToFirstRight = it ; } // L'ultimo che interferisce non esce a destra da quello da aggiungere. if ( itPrevToFirstRight->dMax < dMax - EPS_SMALL) { vDexel.begin()->dMax = dMax ; vDexel.begin()->vtMaxN = vtNma ; vDexel.begin()->nToolMax = nToolMax ; bModified = true ; } else { vDexel.begin()->dMax = itPrevToFirstRight->dMax ; vDexel.begin()->vtMaxN = itPrevToFirstRight->vtMaxN ; vDexel.begin()->nToolMax = nToolMax ; bModified = true ; } auto itFirstToCancel = vDexel.begin() ; ++ itFirstToCancel ; vDexel.erase( itFirstToCancel, itFirstRight) ; } } } // Se nessuna modifica, esco if ( ! bModified) return true ; // Imposto ricalcolo della grafica m_OGrMgr.Reset() ; // Imposto forma generica m_nShape = GENERIC ; // Imposto ricalcolo numero di componenti connesse m_nConnectedCompoCount = - 1 ; // Passo da indici di dexel a indici di voxel nI /= m_nDexVoxRatio ; nJ /= m_nDexVoxRatio ; // Determino quali blocchi sono stati modificati if ( nGrid == 0) { // Voxel lungo X int nXStop = 1 ; int nXBlock[2] ; nXBlock[0] = min( nI / m_nVoxNumPerBlock, m_nFracLin[0] - 1) ; if ( nI % N_VOXBLOCK == 0 && nXBlock[0] > 0) { nXBlock[1] = nXBlock[0] - 1 ; ++ nXStop ; } // Voxel lungo Y int nYStop = 1 ; int nYBlock[2] ; nYBlock[0] = min( nJ / m_nVoxNumPerBlock, m_nFracLin[1] - 1) ; if ( nJ % N_VOXBLOCK == 0 && nYBlock[0] > 0) { nYBlock[1] = nYBlock[0] - 1 ; ++ nYStop ; } // Voxel lungo Z int nVoxNumZ = int( m_nNy[1] / m_nDexVoxRatio + ( m_nNy[1] % m_nDexVoxRatio == 0 ? 1 : 2)) ; int nMinK = Clamp( int( floor( ( ( dMin - 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) - EPS_SMALL))), 0, nVoxNumZ - 2) ; int nMaxK = Clamp( int( floor( ( ( dMax + 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) + EPS_SMALL))), 0, nVoxNumZ - 2) ; int nMinZBlock = ( m_nMapNum == 1 ? 0 : Clamp( nMinK / int( m_nVoxNumPerBlock), 0, int( m_nFracLin[2] - 1))) ; int nMaxZBlock = min( int( m_nFracLin[2] - 1), nMaxK / int( m_nVoxNumPerBlock)) ; // Assegno flag ai voxel for ( int tI = 0 ; tI < nXStop ; ++ tI) { for ( int tJ = 0 ; tJ < nYStop ; ++ tJ) { for ( int k = nMinZBlock ; k <= nMaxZBlock ; ++ k) { int nBlockNum = k * m_nFracLin[0] * m_nFracLin[1] + nYBlock[tJ] * m_nFracLin[0] + nXBlock[tI] ; m_BlockToUpdate[nBlockNum] = true ; } } } } else if ( nGrid == 1) { // Voxel lungo Y int nYStop = 1 ; int nYBlock[2] ; nYBlock[0] = min( nI / m_nVoxNumPerBlock, m_nFracLin[1] - 1) ; if ( nI % N_VOXBLOCK == 0 && nYBlock[0] > 0) { nYBlock[1] = nYBlock[0] - 1 ; ++ nYStop ; } // Voxel lungo Z int nZStop = 1 ; int nZBlock[2] ; nZBlock[0] = min( nJ / m_nVoxNumPerBlock, m_nFracLin[2] - 1) ; if ( nJ % N_VOXBLOCK == 0 && nZBlock[0] > 0) { nZBlock[1] = nZBlock[0] - 1 ; ++ nZStop ; } // Voxel lungo X int nVoxNumX = int( m_nNx[0] / m_nDexVoxRatio + ( m_nNx[0] % m_nDexVoxRatio == 0 ? 1 : 2)) ; int nMinI = Clamp( int( floor( ( ( dMin - 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) - EPS_SMALL))), 0, nVoxNumX - 2) ; int nMaxI = Clamp( int( floor( ( ( dMax + 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) + EPS_SMALL))), 0, nVoxNumX - 2) ; int nMinXBlock = Clamp( nMinI / int( m_nVoxNumPerBlock), 0, int( m_nFracLin[0] - 1)) ; int nMaxXBlock = min( int( m_nFracLin[0] - 1), nMaxI / int( m_nVoxNumPerBlock)) ; // Assegno flag ai voxel for ( int tI = 0 ; tI < nYStop ; ++ tI) { for ( int tJ = 0 ; tJ < nZStop ; ++ tJ) { for ( int k = nMinXBlock ; k <= nMaxXBlock ; ++ k) { int nBlockNum = nZBlock[tJ] * m_nFracLin[0] * m_nFracLin[1] + nYBlock[tI] * m_nFracLin[0] + k ; m_BlockToUpdate[nBlockNum] = true ; } } } } else if ( nGrid == 2) { // Voxel lungo X int nXStop = 1 ; int nXBlock[2] ; nXBlock[0] = min( nJ / m_nVoxNumPerBlock, m_nFracLin[0] - 1) ; if ( nJ % N_VOXBLOCK == 0 && nXBlock[0] > 0) { nXBlock[1] = nXBlock[0] - 1 ; ++ nXStop ; } // Voxel lungo Z int nZStop = 1 ; int nZBlock[2] ; nZBlock[0] = min( nI / m_nVoxNumPerBlock, m_nFracLin[2] - 1) ; if ( nI % N_VOXBLOCK == 0 && nZBlock[0] > 0) { nZBlock[1] = nZBlock[0] - 1 ; ++ nZStop ; } // Voxel lungo Y int nVoxNumY = int( m_nNy[0] / m_nDexVoxRatio + ( m_nNy[0] % m_nDexVoxRatio == 0 ? 1 : 2)) ; int nMinJ = Clamp( int( floor( ( ( dMin - 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) - EPS_SMALL))), 0, nVoxNumY - 2) ; int nMaxJ = Clamp( int( floor( ( ( dMax + 0.5 * m_dStep) / ( m_nDexVoxRatio * m_dStep) + EPS_SMALL))), 0, nVoxNumY - 2) ; int nMinYBlock = Clamp( nMinJ / int( m_nVoxNumPerBlock), 0, int( m_nFracLin[1] - 1)) ; int nMaxYBlock = min( int( m_nFracLin[1] - 1), nMaxJ / int( m_nVoxNumPerBlock)) ; // Assegno flag ai voxel for ( int tI = 0 ; tI < nZStop ; ++ tI) { for ( int tJ = 0 ; tJ < nXStop ; ++ tJ) { for ( int k = nMinYBlock ; k <= nMaxYBlock ; ++ k) { int nBlockNum = nZBlock[tI] * m_nFracLin[0] * m_nFracLin[1] + k * m_nFracLin[0] + nXBlock[tJ] ; m_BlockToUpdate[nBlockNum] = true ; } } } } return true ; } //---------------------------------------------------------------------------- // Funzione per tagliare uno ZMap di Offset con un piano //---------------------------------------------------------------------------- bool 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 aggiungere allo ZMap quello di una TriMesh chiusa // --------------------------------------------------------------------------- bool VolZmap::AddSurfTmForOffset( const ISurfTriMesh* pStm, int nTool) { // controllo sulla superficie double dVol ; if ( pStm == nullptr || ! pStm->IsValid() || ! pStm->IsClosed() || ! pStm->GetVolume( dVol) || dVol < 0) return false ; // controllo se il Box3d della superficie si interseca con il Box3d dello Zmap corrente BBox3d BBox_stm, BBox_curr ; if ( ! pStm->GetLocalBBox( BBox_stm) || ! GetLocalBBox( BBox_curr)) return false ; BBox3d BBox_inters ; if ( BBox_stm.FindIntersection( BBox_curr, BBox_inters) && BBox_inters.IsEmpty()) return true ; // se non ci sono intersezioni, la superficie non influenza lo Zmap Vector3d vtLen = BBox_curr.GetMax() - BBox_curr.GetMin() ; // dimensione massima dello spillone // ciclo sulle griglie bool bCompleted = true ; for ( int nG = 0 ; nG < m_nMapNum ; ++ nG) { // definisco dei sistemi di riferimento ausiliari Frame3d frMapFrame ; if ( nG == 0) frMapFrame = m_MapFrame ; else if ( nG == 1) frMapFrame.Set( m_MapFrame.Orig(), Y_AX, Z_AX, X_AX) ; else if ( nG == 2) frMapFrame.Set( m_MapFrame.Orig(), Z_AX, X_AX, Y_AX) ; // oggetto per calcolo massivo intersezioni IntersParLinesSurfTm intPLSTM( frMapFrame, *pStm) ; // numero massimo di thread int nThreadMax = max( 1, int( thread::hardware_concurrency()) - 1) ; vector> vRes ; vRes.resize( nThreadMax) ; // se dimensione griglia in X maggiore di dimensione Y if ( m_nNx[nG] > m_nNy[nG]) { int nDexNum = m_nNx[nG] / nThreadMax ; int nRemainder = m_nNx[nG] % nThreadMax ; int nInfI = 0 ; int nSupI = 0 ; // aggiungo le parti interessate alla mappa for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { nInfI = nSupI ; nSupI = nInfI + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; vRes[nThread] = async( launch::async, &VolZmap::AddMapPartForOffset, this, nG, nInfI, nSupI, 0, m_nNy[nG], ref( vtLen), ref( m_MapFrame.Orig()), ref( *pStm), nTool, ref( intPLSTM)) ; } } // se dimensione griglia in Y maggiore di dimensione X else { int nDexNum = m_nNy[nG] / nThreadMax ; int nRemainder = m_nNy[nG] % nThreadMax ; int nInfJ = 0 ; int nSupJ = 0 ; // aggiungo le parti interessate alla mappa for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { nInfJ = nSupJ ; nSupJ = nInfJ + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; vRes[nThread] = async( launch::async, &VolZmap::AddMapPartForOffset, this, nG, 0, m_nNx[nG], nInfJ, nSupJ, ref( vtLen), ref( m_MapFrame.Orig()), ref( *pStm), nTool, ref( intPLSTM)) ; } } // ciclo per attendere che tutti gli async abbiano terminato. int nTerminated = 0 ; while ( nTerminated < nThreadMax) { for ( int nL = 0 ; nL < nThreadMax ; ++ nL) { // async terminato if ( vRes[nL].valid() && vRes[nL].wait_for( chrono::microseconds{ 1}) == future_status::ready) { ++ nTerminated ; bCompleted = bCompleted && vRes[nL].get() ; } } } if ( ! bCompleted) return false ; } return true ; } //---------------------------------------------------------------------------- // Funzione per aggiungere gli Spilloni di una TriMesh chiusa allo ZMap corrente //---------------------------------------------------------------------------- bool VolZmap::AddMapPartForOffset( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig, const ISurfTriMesh& Surf, int nTool, IntersParLinesSurfTm& intPLSTM) { // controllo sui parametri if ( nMap < 0 || nMap > 2 || nInfI < 0 || nInfI > m_nNx[nMap] || nSupI < 0 || nSupI > m_nNx[nMap] || nInfJ < 0 || nInfJ > m_nNy[nMap] || nSupJ < 0 || nSupJ > m_nNy[nMap]) return false ; // determinazione e ridimensionamento dei dexel interni alla trimesh for ( int i = nInfI ; i < nSupI ; ++ i) { for ( int j = nInfJ ; j < nSupJ ; ++ j) { // definisco la retta da intersecare con la trimesh double dX = ( i + 0.5) * m_dStep ; double dY = ( j + 0.5) * m_dStep ; Point3d ptP0( dX, dY, 0) ; // intersezioni della retta con la TriMesh ILSIVECTOR IntersectionResults ; intPLSTM.GetInters( ptP0, vtLen.v[(nMap+2)%3], IntersectionResults) ; // rimuovo le intersezioni in eccesso for ( int nI = 0 ; nI < int( IntersectionResults.size()) - 3 ; ++ nI) { int nJ = nI + 1 ; // prima successiva int nK = nJ + 1 ; // seconda successiva int nT = nK + 1 ; // terza successiva // determino i segni delle 4 intersezioni tra la linea e il trangolo della TriMesh int nSgnI = IntersectionResults[nI].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nI].dCosDN > -EPS_SMALL ? 0 : - 1 ; int nSgnJ = IntersectionResults[nJ].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nJ].dCosDN > -EPS_SMALL ? 0 : - 1 ; int nSgnK = IntersectionResults[nK].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nK].dCosDN > -EPS_SMALL ? 0 : - 1 ; int nSgnT = IntersectionResults[nT].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nT].dCosDN > -EPS_SMALL ? 0 : - 1 ; // parametri dell'intersezione sulla linea double dUJ = IntersectionResults[nJ].dU ; double dUK = IntersectionResults[nK].dU ; // controllo coerenza con segni... if ( nSgnI != 0 && nSgnI == nSgnJ && nSgnK != 0 && nSgnK == nSgnT && nSgnI == - nSgnT && abs( dUJ - dUK) < EPS_SMALL) { // ... ed elimino le intersezioni in eccesso... IntersectionResults.erase( IntersectionResults.begin() + nK) ; IntersectionResults.erase( IntersectionResults.begin() + nJ) ; } } int nInt = int( IntersectionResults.size()) ; // numero di intersezioni valide bool bInside = false ; // Flag entrata/uscita per tratto di retta Point3d ptIn ; Vector3d vtInN ; // per ogni intersezione valida trovata... int nFlagIn = 0 ; for ( int k = 0 ; k < nInt ; ++ k) { // ricavo il tipo di intersezione int nIntType = IntersectionResults[k].nILTT ; // se c'è intersezione if ( nIntType != ILTT_NO) { // ricavo il cos tra i vettori ( normale del triangolo e tangente alla retta) double dCos = IntersectionResults[k].dCosDN ; // se entro nella superficie trimesh... if ( dCos < - EPS_SMALL) { ptIn = IntersectionResults[k].ptI ; // punto di intersezione int nT = IntersectionResults[k].nT ; // triangolo di interesse int nF = Surf.GetFacetFromTria( nT) ; // faccia di interesse Surf.GetFacetNormal( nF, vtInN) ; // normale della faccia Surf.GetTFlag( nT, nFlagIn) ; // nFlag entrata dalla faccia bInside = true ; // entrata } // ...se esco dalla superficie trimesh ( prima sono per forza entrato) else if ( dCos > EPS_SMALL && bInside) { Point3d ptOut = IntersectionResults[k].ptI ; // punto di intersezione int nT = IntersectionResults[k].nT ; // triangolo di interesse int nF = Surf.GetFacetFromTria( nT) ; // faccia di interesse Vector3d vtOutN ; Surf.GetFacetNormal( nF, vtOutN) ; // vettore d'uscita int nFlagOut = 0 ; Surf.GetTFlag( nT, nFlagOut) ; // nFlag uscita dalla faccia // Aggiungo un tratto al dexel AddIntervalsForOffset( nMap, i, j, ptIn.v[( nMap + 2) % 3] - ptMapOrig.v[( nMap + 2) % 3], ptOut.v[( nMap + 2) % 3] - ptMapOrig.v[( nMap + 2) % 3], vtInN, vtOutN, nTool, nTool, true) ; bInside = false ; // uscita } } } } } return true ; } //---------------------------------------------------------------------------- // Funzione per la creazione di una sfera di Offset centrata sul vertice di una TriMesh con cui // 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.), ptV + dOffs * Vector3d( 1., 1., 1.)) ; // determino gli intervalli di interesse mediante intersezione con Box della sfera int nStartI = max( 0, int( BBoxSphere.GetMin().x / m_dStep)) ; int nEndI = min( m_nNx[nGrid] - 1, int( BBoxSphere.GetMax().x / m_dStep)) ; int nStartJ = max( 0, int( BBoxSphere.GetMin().y / m_dStep)) ; int nEndJ = min( m_nNy[nGrid] - 1, int( BBoxSphere.GetMax().y / m_dStep)) ; // aggiorno gli spilloni interessati double dSqRad = dOffs * dOffs ; for ( int i = nStartI ; i <= nEndI ; ++ i) { for ( int j = nStartJ ; j <= nEndJ ; ++ j) { double dX = ( i + 0.5) * m_dStep ; double dY = ( j + 0.5) * m_dStep ; Point3d ptC( dX, dY, 0.) ; double dStSqDXY = SqDistXY( ptC, ptV) ; if ( dStSqDXY < dSqRad) { double dMin = ptV.z - sqrt( dSqRad - dStSqDXY) ; Vector3d vtNmin = Point3d( dX, dY, dMin) - ptV ; vtNmin.Normalize() ; double dMax = ptV.z + sqrt( dSqRad - dStSqDXY) ; Vector3d vtNmax = Point3d( dX, dY, dMax) - ptV ; vtNmax.Normalize() ; if ( dOffs > 0.) AddIntervalsForOffset( nGrid, i, j, dMin, dMax, vtNmin, vtNmax, nTool, nTool) ; else SubtractIntervalsForOffset( nGrid, i, j, dMin, dMax, -vtNmin, -vtNmax, nTool) ; } } } return true ; } //---------------------------------------------------------------------------- // Funzione per la creazione di un cilindro di Offset sul vertice di una TriMesh con cui // aggiungere o sottrarre intervalli lungo i Dexel coinvolti. //---------------------------------------------------------------------------- bool VolZmap::CreateOffsetCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, int nGrid, int nTool) { // determino la lunghezza dello spigolo corrente double dH = Dist( ptP1, ptP2) ; // asse del cilindro Vector3d vtV = ptP2 - ptP1 ; vtV.Normalize() ; // calcolo box del cilindro BBox3d BBoxCylinder ; BBoxCylinder.Add( ptP1) ; BBoxCylinder.Add( ptP2) ; if ( AreSameOrOppositeVectorApprox( vtV, X_AX)) BBoxCylinder.Expand( 0., abs( dOffs), abs( dOffs)) ; else if ( AreSameOrOppositeVectorApprox( vtV, Y_AX)) BBoxCylinder.Expand( abs( dOffs), 0., abs( dOffs)) ; else if ( AreSameOrOppositeVectorApprox( vtV, Z_AX)) BBoxCylinder.Expand( abs( dOffs), abs( dOffs), 0.) ; else { double dExpandX = abs( dOffs) * sqrt( 1 - vtV.x * vtV.x) ; double dExpandY = abs( dOffs) * sqrt( 1 - vtV.y * vtV.y) ; double dExpandZ = abs( dOffs) * sqrt( 1 - vtV.z * vtV.z) ; BBoxCylinder.Expand( dExpandX, dExpandY, dExpandZ) ; } // determino gli intervalli di interesse mediante intersezione int nStartI = max( 0, int( BBoxCylinder.GetMin().x / m_dStep)) ; int nEndI = min( m_nNx[nGrid] - 1, int( BBoxCylinder.GetMax().x / m_dStep)) ; int nStartJ = max( 0, int( BBoxCylinder.GetMin().y / m_dStep)) ; int nEndJ = min( m_nNy[nGrid] - 1, int( BBoxCylinder.GetMax().y / m_dStep)) ; // aggiorno gli spilloni interessati Frame3d CylFrame ; if ( ! CylFrame.Set( ptP1, vtV)) return false ; for ( int i = nStartI ; i <= nEndI ; ++ i) { for ( int j = nStartJ ; j <= nEndJ ; ++ j) { Point3d ptC( ( i + 0.5) * m_dStep, ( j + 0.5) * m_dStep, 0) ; Point3d ptInt1, ptInt2 ; Vector3d vtN1, vtN2 ; if ( IntersLineCylinder( ptC, Z_AX, CylFrame, dH, abs( dOffs), true, true, ptInt1, vtN1, ptInt2, vtN2)) { if ( dOffs > 0.) AddIntervalsForOffset( nGrid, i, j, ptInt1.z, ptInt2.z, -vtN1, -vtN2, nTool, nTool) ; else SubtractIntervalsForOffset( nGrid, i, j, ptInt1.z, ptInt2.z, vtN1, vtN2, nTool) ; } } } return true ; } //---------------------------------------------------------------------------- // 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::CreateFatOffsetExtrusionFace( const ISurfTriMesh* Surf, double dOffs, bool bThickle, int nTool) { // 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) { AddSurfTmForOffset( pStmExtr, nTool) ; } // 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 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 : // - le superfici chiuse possono già inserire i rispettivi spilloni // - le superfici aperte non danno contributo ( solo per il Box ) //---------------------------------------------------------------------------- bool VolZmap::InitVolZMapOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) { // se non ho superfici, non faccio nulla if ( vSurf.empty()) return true ; // controllo delle superfici for ( const ISurfTriMesh* Surf : vSurf) { if ( Surf == nullptr) return false ; } // definisco la tolleranza di espansione del Box per la creazione dellop ZMap double dBoxExpansion = ( abs( dOffs) + 1.5 * dTol) + 10 * EPS_SMALL ; // --- se una sola superficie if ( int( vSurf.size()) == 1) { // controllo la validità della superficie if ( ! vSurf[0]->IsValid() || vSurf[0]->GetTriangleCount() == 0) return true ; // --- se superficie chiusa posso direttamente creare lo Zmap iniziale if ( vSurf[0]->IsClosed()) { // definisco lo Zamp a partire dall'espansione del Box della superficie if ( ! CreateFromTriMesh( *vSurf[0], dTol, true, dBoxExpansion)) return false ; } // --- se superficie aperta, lo creo inizialmente vuoto a partire dal suo Box BBox3d BBoxSurf ; vSurf[0]->GetLocalBBox( BBoxSurf) ; BBoxSurf.Expand( dBoxExpansion) ; if ( ! CreateEmpty( BBoxSurf.GetMin(), BBoxSurf.GetDimX(), BBoxSurf.GetDimY(), BBoxSurf.GetDimZ(), dTol, true)) return false ; } // --- se più superfici else { // calcolo il Box complessivo BBox3d BBoxGlob ; for ( const ISurfTriMesh* Surf : vSurf) { // controllo la validità della superficie if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) continue ; // calcolo il Box della superficie BBox3d BBoxSurf ; Surf->GetLocalBBox( BBoxSurf) ; // aggiungo il Box a quello complessivo BBoxGlob.Add( BBoxSurf) ; } // definisco uno Zmap vuoto a partire dal Box BBoxGlob.Expand( dBoxExpansion) ; if ( ! CreateEmpty( BBoxGlob.GetMin(), BBoxGlob.GetDimX(), BBoxGlob.GetDimY(), BBoxGlob.GetDimZ(), dTol, true)) return false ; // aggiungo tutte le superfici chiuse for ( const ISurfTriMesh* Surf : vSurf) { if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0 || ! Surf->IsClosed()) continue ; if ( ! AddSurfTm( Surf)) return false ; } } return true ; } //---------------------------------------------------------------------------- // Funzione per inizializzare lo ZMap a partire da un vettore di superfici TriMesh // L'idea è quella di creare uno ZMap vuoto a partire dal Box complessivo delle superfici TriMesh //---------------------------------------------------------------------------- bool VolZmap::InitVolZMapThickeningOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) { // se non ho superfici, non faccio nulla if ( vSurf.empty()) return true ; // controllo delle superfici for ( const ISurfTriMesh* Surf : vSurf) { if ( Surf == nullptr) return false ; } // 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 BBox3d BBoxGlob ; for ( const ISurfTriMesh* Surf : vSurf) { // controllo la validità della superficie if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) continue ; // calcolo il Box della superficie BBox3d BBoxSurf ; Surf->GetLocalBBox( BBoxSurf) ; // aggiungo il Box a quello complessivo BBoxGlob.Add( BBoxSurf) ; } // definisco uno Zmap vuoto a partire dal Box BBoxGlob.Expand( dBoxExpansion) ; 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 chiusa //---------------------------------------------------------------------------- bool VolZmap::UpdateVolZMapByClosedSurfFilletOffset( const ISurfTriMesh* Surf, double dOffs, double dTol) { // controlli sulla superficie if ( Surf == nullptr || ! Surf->IsClosed()) return false ; if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) return true ; // Assunzioni : // - Idea Generale di Offset // - Su ogni vertice viene definita una sfera ( con raggio pari al valore di Offset e // centro il vertice corrente) // - Su ogni lato viene definito un cilindro ( con raggio di base pari al valore di Offset // e asse definito dall'edge stesso) // - Su ogni faccia viene definita una superficie di estrusione ( dove le due basi sono // definite dalla traslazione sia in positivo che in negativo della faccia lungo la sua normale) // // - Segno dell'Offset : // - Positivo ( si sommano gli intervalli corrisipondenti alle entità create) // - Negativo ( si sottraggono gli intervalli corrispondenti alle enetità create) // // - Semplificazione entità : // La creazione di Sfere e Cilindri potrebbe essere resa più "corretta" definitendo solo // "spicchi 3d" di sfera e "spicchi 3d" di cilindri. Dato che le operazioni di somma, sottrazioni e // calcolo delle normali per gli spilloni sono elementari su queste figure, si rischia di appesantire // troppo i conti introducendo variabili angolari che non sommando tutte le parti. // definisco vettore di frame Locali alle 3 griglie FRAME3DVECTOR vFrGrid( 4) ; vFrGrid[0].Set( ORIG, X_AX, Y_AX, Z_AX) ; vFrGrid[1].Set( m_MapFrame.Orig(), m_MapFrame.VersX(), m_MapFrame.VersY(), m_MapFrame.VersZ()) ; vFrGrid[2].Set( m_MapFrame.Orig(), m_MapFrame.VersY(), m_MapFrame.VersZ(), m_MapFrame.VersX()) ; vFrGrid[3].Set( m_MapFrame.Orig(), m_MapFrame.VersZ(), m_MapFrame.VersX(), m_MapFrame.VersY()) ; // definisco una mappa dei vertici, in modo da sapere su quali sono state già create le sfere BOOLVECTOR vbVert( Surf->GetVertexCount(), false) ; // ----------------------- Cilindri e Sfere ----------------------- // scorro gli Edge della superficie for ( int nE = 0 ; nE < Surf->GetEdgeCount() ; ++ nE) { // recupero lo spigolo int nV1, nV2, nF1, nF2 ; double dAng ; Surf->GetEdge( nE, nV1, nV2, nF1, nF2, dAng) ; // controllo se il cilindro serve // NB. la mancata creazione del cilindro comporta la mancata creazione delle sfere sui suoi vertici // durante questa iterazione; non significa che questa sfera non verrà mai creata... // Non esiste la sfera sul vertive V <=> non esiste alcun cilindro su tutti gli edge concorrenti if ( dAng * dOffs < 0) continue ; // recupero le coordinate dei vertici Point3d ptP1 ; Surf->GetVertex( nV1, ptP1) ; Point3d ptP2 ; Surf->GetVertex( nV2, ptP2) ; // ciclo sulle griglie for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { // esprimo gli estremi nel riferimento della griglia ptP1.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; ptP2.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; // aggiungo/sottraggo gli intervalli definiti dal cilindro if ( ! CreateOffsetCylinderOnEdge( ptP1, ptP2, dOffs, nGrid)) return false ; // aggiungo/sottraggo gli intervalli definiti dalla sfera if ( ! vbVert[nV1]) { if ( ! CreateOffsetSphereOnVertex( ptP1, dOffs, nGrid)) return false ; } if ( ! vbVert[nV2]) { if ( ! CreateOffsetSphereOnVertex( ptP2, dOffs, nGrid)) return false ; } } vbVert[nV1] = true ; vbVert[nV2] = true ; } // ----------------------- Facce ----------------------- if ( ! CreateFatOffsetExtrusionFace( 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 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 ( ! CreateFatOffsetExtrusionFace( 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 } } } } #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)) ; } 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 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) ; } } } } } 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 ; // 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()) ; // creo lo Zmap di Thickening : // l'Offset di thickening può essere scomposto in 3 parti : // - Offset positivo // - Offset negativo // - Raccordi // l'idea è proprio quella di rimuovere i raccordi dall'Offset thickening mediante // sottrazione di ZMap ( e successivamente classificare il Side dei triangoli) // creazione dello ZMap di Thicking della superficie // [NB. potrebbero esserci modifiche ed ottimizzazioni, quindi non richiamo la funzione di // Offset thicking direttamente ma ne copio le varie parti] [TODO] INTVECTOR vOpenEdges ; vOpenEdges.reserve( Surf->GetEdgeCount()) ; INTVECTOR vOpenVertex ; vOpenVertex.reserve( Surf->GetVertexCount()) ; BOOLVECTOR vbVert( Surf->GetVertexCount(), false) ; for ( int nE = 0 ; nE < Surf->GetEdgeCount() ; ++ nE) { int nV1, nV2, nF1, nF2 ; double dAng ; Surf->GetEdge( nE, nV1, nV2, nF1, nF2, dAng) ; // se non è un Edge libero, passo al successivo if ( nF1 == SVT_NULL || nF2 == SVT_NULL) { vOpenEdges.push_back( nE) ; vOpenVertex.push_back( nV1) ; vOpenVertex.push_back( nV2) ; continue ; } Point3d ptP1 ; Surf->GetVertex( nV1, ptP1) ; Point3d ptP2 ; Surf->GetVertex( nV2, ptP2) ; for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { ptP1.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; ptP2.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; if ( ! CreateOffsetCylinderOnEdge( ptP1, ptP2, abs( dOffs), nGrid, 0)) return false ; if ( ! vbVert[nV1]) { if ( ! CreateOffsetSphereOnVertex( ptP1, abs( dOffs), nGrid, 0)) return false ; } if ( ! vbVert[nV2]) { if ( ! CreateOffsetSphereOnVertex( ptP2, abs( dOffs), nGrid, 0)) return false ; } } vbVert[nV1] = true ; vbVert[nV2] = true ; } if ( ! CreateFatOffsetExtrusionFace( Surf, abs( dOffs), true, 0)) return false ; #if DEBUG VT.emplace_back( this->Clone()) ; VC.emplace_back( FUCHSIA) ; #endif // creo lo Zmap derivante dai semi cilindri orientati VolZmap VolZMapHalfBorder ; VolZMapHalfBorder.CreateEmpty( m_MapFrame.Orig(), m_dMaxZ[1] - m_dMinZ[1], m_dMaxZ[2] - m_dMinZ[2], m_dMaxZ[0] - m_dMinZ[0], m_dStep, true) ; // salvo i piani di taglio per le sfere durante la creazione dei cilindri unordered_map> vMapVertBorders ; vMapVertBorders.reserve( Surf->GetVertexCount()) ; // ----------------------- semicilindri ----------------------- const double dNewOffs = abs( dOffs) + 3. * m_dStep + 10. * EPS_SMALL ; for ( const int& nE : vOpenEdges) { // recupero lo spigolo int nV1, nV2, nF1, nF2 ; double dAng ; Surf->GetEdge( nE, nV1, nV2, nF1, nF2, dAng) ; // 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) ; vtEdge.Normalize() ; #if DEBUG CurveLine line ; line.Set( ptP1, ptP2) ; VT.emplace_back( line.Clone()) ; Color cCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; VC.emplace_back( cCol) ; #endif // recupero le normali delle facce Vector3d vtFaceN ; if ( nF1 == SVT_NULL) Surf->GetFacetNormal( nF2, vtFaceN) ; else Surf->GetFacetNormal( nF1, vtFaceN) ; #if DEBUG CurveArc myArc ; myArc.SetCPAN( ptP1, ptP1 + dNewOffs * vtFaceN, 360., 0, vtEdge) ; myArc.SetExtrusion( vtEdge) ; //myArc.SetThickness( ( ptP2 - ptP1).Len()) ; CICURVEPVECTOR _vCurve ; _vCurve.emplace_back( myArc.Clone()) ; ISurfTriMesh* A = GetSurfTriMeshByRegionExtrusion( _vCurve, ( ptP2 - ptP1).Len() * vtEdge, EPS_SMALL) ; VT.emplace_back( A) ; VC.emplace_back( cCol) ; #endif Vector3d vtPlaneN = vtEdge ^ vtFaceN ; Plane3d plCut ; plCut.Set( ptP1, - vtPlaneN) ; // creazione del cilindro VolZmap VolZMapHalfCyl ; VolZMapHalfCyl.CreateEmpty( m_MapFrame.Orig(), m_dMaxZ[1] - m_dMinZ[1], m_dMaxZ[2] - m_dMinZ[2], m_dMaxZ[0] - m_dMinZ[0], m_dStep, true) ; for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { // esprimo gli estremi nel riferimento della griglia ptP1.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; ptP2.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; VolZMapHalfCyl.CreateOffsetCylinderOnEdge( ptP1, ptP2, dNewOffs, nGrid, 0) ; } // taglio del cilindro con il piano VolZMapHalfCyl.CutByPlaneForOffset( plCut) ; Surf->GetVertex( nV1, ptP1) ; Surf->GetVertex( nV2, ptP2) ; for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { // esprimo gli estremi nel riferimento della griglia ptP1.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; ptP2.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; // asse del cilindro Vector3d vtV = ptP2 - ptP1 ; vtV.Normalize() ; // calcolo box del cilindro BBox3d BBoxCylinder ; BBoxCylinder.Add( ptP1) ; BBoxCylinder.Add( ptP2) ; if ( AreSameOrOppositeVectorApprox( vtV, X_AX)) BBoxCylinder.Expand( 0., dNewOffs, dNewOffs) ; else if ( AreSameOrOppositeVectorApprox( vtV, Y_AX)) BBoxCylinder.Expand( dNewOffs, 0., dNewOffs) ; else if ( AreSameOrOppositeVectorApprox( vtV, Z_AX)) BBoxCylinder.Expand( dNewOffs, dNewOffs, 0.) ; else { double dExpandX = dNewOffs * sqrt( 1 - vtV.x * vtV.x) ; double dExpandY = dNewOffs * sqrt( 1 - vtV.y * vtV.y) ; double dExpandZ = dNewOffs * sqrt( 1 - vtV.z * vtV.z) ; BBoxCylinder.Expand( dExpandX, dExpandY, dExpandZ) ; } // determino gli intervalli di interesse mediante intersezione int nStartI = max( 0, int( BBoxCylinder.GetMin().x / m_dStep)) ; int nEndI = min( m_nNx[nGrid] - 1, int( BBoxCylinder.GetMax().x / m_dStep)) ; int nStartJ = max( 0, int( BBoxCylinder.GetMin().y / m_dStep)) ; int nEndJ = min( m_nNy[nGrid] - 1, int( BBoxCylinder.GetMax().y / m_dStep)) ; // aggiorno gli spilloni interessati for ( int i = nStartI ; i <= nEndI ; ++ i) { for ( int j = nStartJ ; j <= nEndJ ; ++ j) { // recupero il Dexel int nPos = j * VolZMapHalfCyl.m_nNx[nGrid] + i ; vector& vDexel = VolZMapHalfCyl.m_Values[nGrid][nPos] ; // scorro i suoi intervalli for ( auto& Interval : vDexel) { // aggiungo i contributi VolZMapHalfBorder.AddIntervalsForOffset( nGrid, i, j, Interval.dMin, Interval.dMax, Interval.vtMinN, Interval.vtMaxN, 0, 0, true) ; } } } } // recupero i due piani di taglio definiti Surf->GetVertex( nV1, ptP1) ; Surf->GetVertex( nV2, ptP2) ; Plane3d plCut1 ; plCut1.Set( ptP1, vtEdge) ; Plane3d plCut2 ; plCut2.Set( ptP2, - vtEdge) ; vMapVertBorders[nV1].push_back( plCut1) ; vMapVertBorders[nV2].push_back( plCut2) ; vMapVertBorders[nV1].push_back( plCut) ; vMapVertBorders[nV2].push_back( plCut) ; } // ----------------------- sfere ----------------------- for ( const int& nV : vOpenVertex) { // 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, dNewOffs, nGrid, 0) ; } // taglio lo Zmap con tutti i piani ricavati for ( int i = 0 ; i < int( vMapVertBorders[nV].size()) ; ++ i) VolZMapCut.CutByPlaneForOffset( vMapVertBorders[nV][i]) ; // ciclo sulle griglie Surf->GetVertex( nV, ptVertex) ; for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { // esprimo il vertice corrente nel riferimento della griglia ptVertex.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; // determino il Box della sfera posizionata su tale vertice BBox3d BBoxSphere( ptVertex - dNewOffs * Vector3d( 1., 1., 1.), ptVertex + dNewOffs * Vector3d( 1., 1., 1.)) ; // determino gli intervalli di interesse mediante intersezione con Box della sfera int nStartI = max( 0, int( BBoxSphere.GetMin().x / VolZMapCut.m_dStep)) ; int nEndI = min( VolZMapCut.m_nNx[nGrid] - 1, int( BBoxSphere.GetMax().x / VolZMapCut.m_dStep)) ; 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) { VolZMapHalfBorder.AddIntervalsForOffset( nGrid, i, j, Interval.dMin, Interval.dMax, Interval.vtMinN, Interval.vtMaxN, 0, 0, true) ; } } } } } #if DEBUG VT.clear() ; VC.clear() ; VT.emplace_back( Surf->Clone()) ; VC.emplace_back( YELLOW) ; VT.emplace_back( VolZMapHalfBorder.Clone()) ; VC.emplace_back( BLACK) ; SaveGeoObj( VT, VC, "C:\\Temp\\AllStrip.nge") ; #endif for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { // aggiorno gli spilloni interessati for ( int i = 0 ; i < m_nNx[nGrid] ; ++ i) { for ( int j = 0 ; j < m_nNy[nGrid] ; ++ j) { // recupero il Dexel int nPos = j * VolZMapHalfBorder.m_nNx[nGrid] + i ; vector& vDexel = VolZMapHalfBorder.m_Values[nGrid][nPos] ; // scorro i suoi intervalli for ( auto& Interval : vDexel) { AddIntervalsForOffset( nGrid, i, j, Interval.dMin, Interval.dMax, Interval.vtMinN, Interval.vtMaxN, 0, 0, true) ; } } } } #if DEBUG VT.emplace_back( this->Clone()) ; VC.emplace_back( WHITE) ; SaveGeoObj( VT, VC, "C:\\Temp\\this.nge") ; #endif return true ; } //---------------------------------------------------------------------------- // [Sharped ( Chamfer/Extend )] Funzione per aggiornare mediantePrismi, Involucri convessi // 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) //---------------------------------------------------------------------------- bool VolZmap::UpdateVolZMapBySurfThickeningFilletOffset( const ISurfTriMesh* Surf, double dOffs, double dTol) { // controlli sulla superficie if ( Surf == nullptr) return false ; if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) return true ; // non inizializzo lo Zmap, semplicemente lavoro con facce, spigoli e vertici // 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()) ; // definisco una mappa dei vertici, in modo da sapere su quali sono state già create le sfere BOOLVECTOR vbVert( Surf->GetVertexCount(), false) ; // ----------------------- Cilindri e Sfere ----------------------- // scorro gli Edge della superficie for ( int nE = 0 ; nE < Surf->GetEdgeCount() ; ++ nE) { // recupero lo spigolo int nV1, nV2, nF1, nF2 ; double dAng ; Surf->GetEdge( nE, nV1, nV2, nF1, nF2, dAng) ; // recupero le coordinate dei vertici Point3d ptP1 ; Surf->GetVertex( nV1, ptP1) ; Point3d ptP2 ; Surf->GetVertex( nV2, ptP2) ; // ciclo sulle griglie for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { // esprimo gli estremi nel riferimento della griglia ptP1.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; ptP2.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; // aggiungo/sottraggo gli intervalli definiti dal cilindro if ( ! CreateOffsetCylinderOnEdge( ptP1, ptP2, abs( dOffs), nGrid)) return false ; // aggiungo/sottraggo gli intervalli definiti dalla sfera if ( ! vbVert[nV1]) { if ( ! CreateOffsetSphereOnVertex( ptP1, abs( dOffs), nGrid)) return false ; } if ( ! vbVert[nV2]) { if ( ! CreateOffsetSphereOnVertex( ptP2, abs( dOffs), nGrid)) return false ; } } vbVert[nV1] = true ; vbVert[nV2] = true ; } // ----------------------- Facce ----------------------- if ( ! CreateFatOffsetExtrusionFace( 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 ( ! CreateFatOffsetExtrusionFace( 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) ; // 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 polyline di base del prisma PolyLine PLBase ; // --- 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)) 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)) { 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 } } } } #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 ( auto& Map : vMapVertBorders) { // recupero l'indice del vertice int nV = Map.first ; // 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 ; } //---------------------------------------------------------------------------- // Funzione per la creazine di uno Zmap di Offset (positivo o negativo) a partire da un // 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 è 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) { // controllo delle superfici for ( const ISurfTriMesh* Surf : vSurf) { if ( Surf == nullptr) return false ; } // verifica sul parametro di Offset ( coerente con Curve e FlatRegion) if ( abs( dOffs) < 10. * EPS_SMALL) return true ; // se non ho superfici, non faccio nulla if ( vSurf.empty()) return true ; // inizializzo lo Zmap di Offset a partire dalle superfici if ( ! InitVolZMapOffset( vSurf, dOffs, dTol)) return false ; // scorro le superfici for ( const ISurfTriMesh* Surf : vSurf) { // se superficie non valida, passo alla successiva if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) continue ; // aggiorno lo ZMap // --- se superficie Closed 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 ; } // --- se superficie Open else { // --- 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 ; } } } m_nShape = OFFSET ; return true ; } //---------------------------------------------------------------------------- // Funzione per la creazine di uno Zmap di Fat Offset a partire da un // vettore di superfici TriMesh (aperte o chiuse). //---------------------------------------------------------------------------- bool VolZmap::CreateFromTriMeshThickeningOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol, int nType) { // controllo delle superfici for ( const ISurfTriMesh* Surf : vSurf) { if ( Surf == nullptr) return false ; } // verifica sul parametro di Offset ( coerente con Curve e FlatRegion) if ( abs( dOffs) < 10. * EPS_SMALL) return true ; // se non ho superfici, non faccio nulla if ( vSurf.empty()) return true ; // inizializzo lo Zmap di Offset a partire dalle superfici double dBoxExpand = dOffs ; if ( nType == STMOFF_EXTEND || nType == STMOFF_CHAMFER) dBoxExpand *= 2 ; if ( ! InitVolZMapThickeningOffset( vSurf, dBoxExpand, dTol)) return false ; // scorro le superfici for ( const ISurfTriMesh* Surf : vSurf) { // se superficie non valida, passo alla successiva if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) 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 ; 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 ; }