11a46ca58c
- Modificate le funzioni di Offset per superfici TriMesh aperte - Aggiunta la funzione per la creazione di Shell per TriMesh.
2806 lines
119 KiB
C++
2806 lines
119 KiB
C++
//----------------------------------------------------------------------------
|
|
// 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 <future>
|
|
#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<IGeoObj*> VT ;
|
|
std::vector<Color> 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<Data>& 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<Data>& 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<future<bool>> 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<ICurveComposite> 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<ISurfTriMesh> 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<ICurveComposite> 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<ISurfTriMesh> 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<int, vector<Plane3d>> 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<ISurfTriMesh> 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<Data>& 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<int, vector<Plane3d>> 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<Data>& 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<Data>& 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<Data>& 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
|
|
// <IndVer, { vector<Piani Lati Liberi> }, { vector<Piani Facce> }>
|
|
unordered_map<int, pair<vector<Plane3d>, vector<Plane3d>>> 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<ISurfTriMesh> 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<int> 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<ICurveComposite> 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<ISurfTriMesh> 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<Data>& 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<int, vector<Plane3d>> 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<ISurfTriMesh> 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<Data>& 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<int>( 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<PNTVECTOR> 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<Data>& 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 ;
|
|
} |