Files
EgtGeomKernel/VolZmapOffset.cpp
T
Riccardo Elitropi f05c5f1261 EgtGeomKernel :
- aggiunte funzioni per Offset e FatOffset per un insieme di curve
- in VolZmapOffset migliorata la gestione delle normali per l'addizione di intervalli.
2025-06-25 15:51:19 +02:00

695 lines
28 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2025-2025
//----------------------------------------------------------------------------
// File : OffsetSurfTm.cpp Data : 09.06.25 Versione : 2.7e3
// Contenuto : Dichiarazione della funzione per calcolare l'offset di superfici TriMesh
// mediante Zmap
//
//
//
// Modifiche : 09.06.25 RE Creazione modulo.
//
//
//----------------------------------------------------------------------------
#include "stdafx.h"
#include "VolZmap.h"
#include "CurveLine.h"
#include "GeoConst.h"
#include "/EgtDev/Include/EGkStmFromCurves.h"
#include "/EgtDev/Include/EGkIntersLineSurfTm.h"
#include "/EgtDev/Include/EgtNumUtils.h"
#include <future>
using namespace std ;
//----------------------------------------------------------------------------
/* Funzione per aggiungere intervalli lungo un Dexel per l'offset di una superficie TriMesh */
bool
VolZmap::SubtractIntervalsForOffset( int nGrid, int nI, int nJ,
double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax,
int nToolNum, bool bSkipSwap)
{
// per ora la funzione è la stessa della differenza in generale
// TODO -- Aggiustare eventuali tolleranze
return SubtractIntervals( nGrid, nI, nJ, dMin, dMax, vtNMin, vtNMax, nToolNum, bSkipSwap) ;
}
//----------------------------------------------------------------------------
/* Funzione per aggiungere intervalli lungo un Dexel per l'offset di una superficie TriMesh */
bool
VolZmap::AddIntervalsForOffset( int nGrid, int nI, int nJ,
double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax,
int nToolNum, bool bSkipSwap)
{
// 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 = nToolNum ;
vDexel.back().dMax = dMax ;
vDexel.back().vtMaxN = vtNma ;
vDexel.back().nToolMax = nToolNum ;
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 strettamente 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 semgento
vDexel.emplace_back() ;
vDexel.back().dMin = dMin ;
vDexel.back().dMax = dMax ;
vDexel.back().vtMinN = vtNmi ;
vDexel.back().vtMaxN = vtNma ;
vDexel.back().nToolMin = nToolNum ;
vDexel.back().nToolMax = nToolNum ;
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 = nToolNum ;
NewSegment.nToolMax = nToolNum ;
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 = nToolNum ;
}
// il minimo del successivo coincide con quello da aggiungere
else if ( abs( itNextToLastLeft->dMin - dMin) < EPS_SMALL) {
// Media tra le normali
itNextToLastLeft->vtMinN += vtNmi ;
itNextToLastLeft->vtMinN.Normalize() ;
}
// 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 = nToolNum ;
}
// Il massimo dell'ultimo che interferisce coincide con quello da aggiungere.
else if ( abs( itPrevToFirstRight->dMax - dMax) < EPS_SMALL) {
// Media tra le normali
itNextToLastLeft->vtMaxN = ( vtNma + itPrevToFirstRight->vtMaxN) ;
itNextToLastLeft->vtMaxN.Normalize() ;
}
else {
itNextToLastLeft->dMax = itPrevToFirstRight->dMax ;
itNextToLastLeft->vtMaxN = itPrevToFirstRight->vtMaxN ;
itNextToLastLeft->nToolMax = nToolNum ;
}
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 STRETTAMENTE a sinistra
if ( vDexel.begin()->dMin > dMin + EPS_SMALL) {
vDexel.begin()->dMin = dMin ;
vDexel.begin()->vtMinN = vtNmi ;
vDexel.begin()->nToolMin = nToolNum ;
}
// Il minimo del primo intervallo coincide con il minimo di quello corrente
else if ( abs( vDexel.begin()->dMin - dMin) < EPS_SMALL) {
// Media tra le normali
vDexel.begin()->vtMinN += vtNmi ;
vDexel.begin()->vtMinN.Normalize() ;
}
// L'ultimo intervallo sporge a destra.
if ( vDexel.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 = nToolNum ;
}
}
// Il massimo dell'ultimo intervallo coincide con il massimo del corrente
else if ( abs( vDexel.back().dMax - dMax) < EPS_SMALL) {
// Ci sono più segmenti, inglobo tutti nel primo e medio le normali del massimo
if ( vDexel.back().dMax > vDexel.begin()->dMax + EPS_SMALL) {
vDexel.begin()->dMax = vDexel.back().dMax ;
vDexel.begin()->vtMaxN = vDexel.back().vtMaxN + vtNma ;
vDexel.begin()->vtMaxN.Normalize() ;
vDexel.begin()->nToolMax = nToolNum ;
}
// Se singolo segmento
else {
// Media delle normali
vDexel.begin()->vtMaxN += vtNma ;
vDexel.begin()->vtMaxN.Normalize() ;
}
}
// L'ultimo intervallo non sporge a destra.
else {
vDexel.begin()->dMax = dMax ;
vDexel.begin()->vtMaxN = vtNma ;
vDexel.begin()->nToolMax = nToolNum ;
}
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 = nToolNum ;
NewSegment.nToolMax = nToolNum ;
vDexel.insert( vDexel.begin(), NewSegment) ;
bModified = true ;
}
else {
// Se il primo segmento non esce strettamente 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 = nToolNum ;
bModified = true ;
}
// Se il minimo del segmento a sinistra coicide con il minimo del corrente
else if ( abs( vDexel.begin()->dMin - dMin) < EPS_SMALL) {
// Media delle normali
vDexel.begin()->vtMinN += vtNmi ;
vDexel.begin()->vtMinN.Normalize() ;
}
// 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 strettamente a destra da quello da aggiungere.
if ( itPrevToFirstRight->dMax < dMax - EPS_SMALL) {
vDexel.begin()->dMax = dMax ;
vDexel.begin()->vtMaxN = vtNma ;
vDexel.begin()->nToolMax = nToolNum ;
bModified = true ;
}
// Il massimo dell'ultimo segmento coincide con il massimo corrente
else if ( abs( itPrevToFirstRight->dMax - dMax) < EPS_SMALL) {
vDexel.begin()->vtMaxN = itPrevToFirstRight->vtMaxN + vtNma ;
vDexel.begin()->vtMaxN.Normalize() ;
}
else {
vDexel.begin()->dMax = itPrevToFirstRight->dMax ;
vDexel.begin()->vtMaxN = itPrevToFirstRight->vtMaxN ;
vDexel.begin()->nToolMax = nToolNum ;
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 la creazione di una sfera di Offset centrata sul vertice di una TriMesh con cui
aggiungere o sottrarre intervalli lungo i Dexel coinvolti */
bool
VolZmap::CreateOffsSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid)
{
// determino il Box della sfera posizionata su tale vertice
BBox3d BBoxSphere( ptV - dOffs * Vector3d( 1., 1., 1.),
ptV + dOffs * Vector3d( 1., 1., 1.)) ;
// determino gli intervalli di interesse mediante intersezione con Box della sfera
int nStartI = max( 0, int( BBoxSphere.GetMin().x / m_dStep)) ;
int nEndI = min( m_nNx[nGrid] - 1, int( BBoxSphere.GetMax().x / m_dStep)) ;
int nStartJ = max( 0, int( BBoxSphere.GetMin().y / m_dStep)) ;
int nEndJ = min( m_nNy[nGrid] - 1, int( BBoxSphere.GetMax().y / m_dStep)) ;
// aggiorno gli spilloni interessati
double dSqRad = dOffs * dOffs ;
for ( int i = nStartI ; i <= nEndI ; ++ i) {
for ( int j = nStartJ ; j <= nEndJ ; ++ j) {
double dX = ( i + 0.5) * m_dStep ;
double dY = ( j + 0.5) * m_dStep ;
Point3d ptC( dX, dY, 0.) ;
double dStSqDXY = SqDistXY( ptC, ptV) ;
if ( dStSqDXY < dSqRad) {
double dMin = ptV.z - sqrt( dSqRad - dStSqDXY) ;
Vector3d vtNmin = Point3d( dX, dY, dMin) - ptV ;
vtNmin.Normalize() ;
double dMax = ptV.z + sqrt( dSqRad - dStSqDXY) ;
Vector3d vtNmax = Point3d( dX, dY, dMax) - ptV ;
vtNmax.Normalize() ;
if ( dOffs > 0.)
AddIntervalsForOffset( nGrid, i, j, dMin, dMax, vtNmin, vtNmax, 0) ;
else
SubtractIntervalsForOffset( nGrid, i, j, dMin, dMax, -vtNmin, -vtNmax, 0) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
/* Funzione per la creazione di un cilindro di Offset sul vertice di una TriMesh con cui
aggiungere o sottrarre intervalli lungo i Dexel coinvolti */
bool
VolZmap::CreateOffsCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, int nGrid)
{
// determino la lunghezza dello spigolo corrente
double dH = Dist( ptP1, ptP2) ;
// asse del cilindro
Vector3d vtV = ptP2 - ptP1 ; vtV.Normalize() ;
// calcolo box del cilindro
BBox3d BBoxCylinder ;
BBoxCylinder.Add( ptP1) ;
BBoxCylinder.Add( ptP2) ;
if ( AreSameOrOppositeVectorApprox( vtV, X_AX))
BBoxCylinder.Expand( 0., abs( dOffs), abs( dOffs)) ;
else if ( AreSameOrOppositeVectorApprox( vtV, Y_AX))
BBoxCylinder.Expand( abs( dOffs), 0., abs( dOffs)) ;
else if ( AreSameOrOppositeVectorApprox( vtV, Z_AX))
BBoxCylinder.Expand( abs( dOffs), abs( dOffs), 0.) ;
else {
double dExpandX = abs( dOffs) * sqrt( 1 - vtV.x * vtV.x) ;
double dExpandY = abs( dOffs) * sqrt( 1 - vtV.y * vtV.y) ;
double dExpandZ = abs( dOffs) * sqrt( 1 - vtV.z * vtV.z) ;
BBoxCylinder.Expand( dExpandX, dExpandY, dExpandZ) ;
}
// determino gli intervalli di interesse mediante intersezione
int nStartI = max( 0, int( BBoxCylinder.GetMin().x / m_dStep)) ;
int nEndI = min( m_nNx[nGrid] - 1, int( BBoxCylinder.GetMax().x / m_dStep)) ;
int nStartJ = max( 0, int( BBoxCylinder.GetMin().y / m_dStep)) ;
int nEndJ = min( m_nNy[nGrid] - 1, int( BBoxCylinder.GetMax().y / m_dStep)) ;
// aggiorno gli spilloni interessati
Frame3d CylFrame ;
if ( ! CylFrame.Set( ptP1, vtV))
return false ;
for ( int i = nStartI ; i <= nEndI ; ++ i) {
for ( int j = nStartJ ; j <= nEndJ ; ++ j) {
Point3d ptC( ( i + 0.5) * m_dStep, ( j + 0.5) * m_dStep, 0) ;
Point3d ptInt1, ptInt2 ;
Vector3d vtN1, vtN2 ;
if ( IntersLineCylinder( ptC, Z_AX, CylFrame, dH, abs( dOffs), true, true,
ptInt1, vtN1, ptInt2, vtN2)) {
if ( dOffs > 0.)
AddIntervalsForOffset( nGrid, i, j, ptInt1.z, ptInt2.z, -vtN1, -vtN2, 0) ;
else
SubtractIntervalsForOffset( nGrid, i, j, ptInt1.z, ptInt2.z, vtN1, vtN2, 0) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
/* Funzione per la creazine di uno Zmap di Offset (positivo o negativo) a partire da una
superficie TriMesh */
bool
VolZmap::CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol)
{
// controllo delle superfici
for ( const ISurfTriMesh* Surf : vSurf) {
if ( Surf == nullptr)
return false ;
}
// verifica sul parametro di Offset ( coerente con Curve e FlatRegion)
if ( abs( dOffs) < 10 * EPS_SMALL)
return true ;
// se non ho superfici, non faccio nulla
if ( vSurf.empty())
return true ;
// definisco lo Zmap di partenza a partire dalle superfici
// se una sola superficie
double dBoxExpansion = ( abs( dOffs) + 1.5 * dTol) + 10 * EPS_SMALL ;
if ( int( vSurf.size()) == 1) {
// controllo la validità della superficie
if ( ! vSurf[0]->IsValid() || vSurf[0]->GetTriangleCount() == 0)
return true ;
// definisco lo Zamp a partire dall'espansione del Box della superficie
if ( ! CreateFromTriMesh( *vSurf[0], dTol, true, dBoxExpansion))
return false ;
}
// se più superfici
else {
// calcolo il Box complessivo delle superfici TriMesh
BBox3d BBoxGlob ;
for ( int i = 0 ; i < int( vSurf.size()) ; ++ i) {
// controllo la validità della superficie
if ( ! vSurf[i]->IsValid() || vSurf[i]->GetTriangleCount() == 0)
continue ;
// calcolo il Box della superficie
BBox3d BBoxSurf ; vSurf[i]->GetLocalBBox( BBoxSurf) ;
// aggiungo il Box a quello complessivo
BBoxGlob.Add( BBoxSurf) ;
}
// definisco uno Zmap vuoto a partire dal Box
BBoxGlob.Expand( dBoxExpansion) ;
if ( ! CreateEmpty( BBoxGlob.GetMin(), BBoxGlob.GetDimX(), BBoxGlob.GetDimY(), BBoxGlob.GetDimZ(), dTol, true))
return false ;
for ( const ISurfTriMesh* Surf : vSurf) {
if ( ! AddSurfTm( Surf))
return false ;
}
}
/* Assunzioni :
- Idea Generale di Offset
- Su ogni vertice viene definita una sfera ( con raggio pari al valore di Offset e
centro il vertice corrente)
- Su ogni lato viene definito un cilindro ( con raggio di pase pari al valore di Offset
e asse definito dall'edge stesso)
- Su ogni faccia viene definita una superficie di estrusione ( dove le due basi sono
definite dalla traslazione sia in positivo che in negativo della faccia lungo la sua normale)
- Segno dell'Offset :
- Positivo ( si sommano gli intervalli corrisipondenti alle entità create)
- Negativo ( si sottraggono gli intervalli corrispondenti alle enetità create)
- Semplificazione entità :
La creazione di Sfere e Cilindri potrebbe essere resa più "corretta" definitendo solo
"spicchi 3d" di sfera e "spicchi 3d" di cilindri. Dato che le operazioni di somma, sottrazioni e
calcolo delle normali per gli spilloni sono elementari su queste figure, si rischia di appesantire
troppo i conti introducendo variabili angolari che non sommando tutte le parti.
*/
// definisco vettore di frame Locali alle 3 griglie
FRAME3DVECTOR vFrGrid( 4) ;
vFrGrid[0].Set( ORIG, X_AX, Y_AX, Z_AX) ;
vFrGrid[1].Set( m_MapFrame.Orig(), m_MapFrame.VersX(), m_MapFrame.VersY(), m_MapFrame.VersZ()) ;
vFrGrid[2].Set( m_MapFrame.Orig(), m_MapFrame.VersY(), m_MapFrame.VersZ(), m_MapFrame.VersX()) ;
vFrGrid[3].Set( m_MapFrame.Orig(), m_MapFrame.VersZ(), m_MapFrame.VersX(), m_MapFrame.VersY()) ;
// scorro le superfici
for ( const ISurfTriMesh* Surf : vSurf) {
// se superficie non valida, passo alla successiva
if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0)
continue ;
// definisco una mappa dei vertici, in modo da sapere su quali sono state già create le sfere
BOOLVECTOR vbVert( Surf->GetVertexCount(), false) ;
// ----------------------- Cilindri e Sfere -----------------------
// scorro gli Edge della superficie
for ( int nE = 0 ; nE < Surf->GetEdgeCount() ; ++ nE) {
// recupero lo spigolo
int nV1, nV2, nF1, nF2 ; double dAng ;
Surf->GetEdge( nE, nV1, nV2, nF1, nF2, dAng) ;
// controllo se il cilindro serve
// NB. la mancata creazione del cilindro comporta la mancata creazione delle sfere sui suoi vertici
// durante questa iterazione; non significa che questa sfera non verrà mai creata...
// Non esiste la sfera sul vertive V <=> non esiste alcun cilindro su tutti gli edge concorrenti
if ( dAng * dOffs < 0)
continue ;
// recupero le coordinate dei vertici
Point3d ptP1 ; Surf->GetVertex( nV1, ptP1) ;
Point3d ptP2 ; Surf->GetVertex( nV2, ptP2) ;
// ciclo sulle griglie
for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) {
// esprimo gli estremi nel riferimento della griglia
ptP1.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ;
ptP2.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ;
// aggiungo/sottraggo gli intervalli definiti dal cilindro
if ( ! CreateOffsCylinderOnEdge( ptP1, ptP2, dOffs, nGrid))
return false ;
// aggiungo/sottraggo gli intervalli definiti dalla sfera
if ( ! vbVert[nV1]) {
vbVert[nV1] = ( nGrid == 2) ;
if ( ! CreateOffsSphereOnVertex( ptP1, dOffs, nGrid))
return false ;
}
if ( ! vbVert[nV2]) {
vbVert[nV2] = ( nGrid == 2) ;
if ( ! CreateOffsSphereOnVertex( ptP2, dOffs, nGrid))
return false ;
}
}
}
// ----------------------- Facce -----------------------
// scorro tutte le facce definendo una superficie di estrusione
for ( int nF = 0 ; nF < Surf->GetFacetCount() ; ++ nF) {
// recupero lo faccia
POLYLINEVECTOR vPL ; Surf->GetFacetLoops( nF, vPL) ;
// recupero la normale della faccia
Vector3d vtN ; Surf->GetFacetNormal( nF, vtN) ;
// definisco la superficie di estrusione
CICURVEPVECTOR vpCrvs ; vpCrvs.reserve( vPL.size()) ;
for ( int i = 0 ; i < int( vPL.size()) ; ++ i) {
vPL[i].Translate( - abs( dOffs) * vtN) ;
PtrOwner<ICurveComposite> pCrvCompo( CreateCurveComposite()) ;
if ( IsNull( pCrvCompo) || ! pCrvCompo->FromPolyLine( vPL[i]))
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
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 ;
}
}
}
m_nShape = OFFSET ;
return true ;
}