Files
EgtGeomKernel/CAvSilhouetteSurfTm.cpp
T
Dario Sassi c4a05bcb2f EgtGeomKernel :
- miglioramenti al calcolo silhouette con CollisionAvoid
- modifiche a CollisionAvoid per permettere accelerazione con HashGrid anche a TestPosition.
2024-06-17 07:10:27 +02:00

394 lines
15 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2024-2024
//----------------------------------------------------------------------------
// File : CAvSilhouetteSurfTm.cpp Data : 08.06.24 Versione : 2.6f2
// Contenuto : Implementazione della funzione CAvSilhouetteSurfTm.
//
//
//
// Modifiche : 08.06.24 DS Creazione modulo.
//
//
//----------------------------------------------------------------------------
#include "stdafx.h"
#include "CAvSilhouetteSurfTm.h"
#include "GeoConst.h"
#include "/EgtDev/Include/EGkChainCurves.h"
#include "/EgtDev/Include/EGkOffsetCurve.h"
using namespace std ;
//----------------------------------------------------------------------------
// Funzioni locali per calcolo isolinee con metodo Marching Squares
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Quadrato (C=corner E=edge) :
//
// C3 - E2 - C2
// | |
// E3 E1
// | |
// C0 - E0 - C1
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
static Point3d
CalcPoint( const Point3d& ptS, const Point3d& ptE, double dLevel,
const ICAvToolSurfTm& cavTstm, const Frame3d &frGrid)
{
// Distanza tra gli estremi iniziali
double dDist = max( abs( ptS.x - ptE.x), abs( ptS.y - ptE.y)) ;
// Ricerca con metodo di bisezione (si arresta quando il medio è allineato agli estremi)
const int MAX_ITER = 8 ;
int nCount = 0 ;
Point3d ptP1 = ptS ;
Point3d ptP2 = ptE ;
Point3d ptMid = Media( ptP1, ptP2) ;
while ( nCount < MAX_ITER) {
// verifica del punto medio
ptMid.z = 0 ;
Point3d ptTest = GetToGlob( ptMid + cavTstm.GetToolHeight() * Z_AX, frGrid) ;
double dMove ;
const_cast<ICAvToolSurfTm*>( &cavTstm)->TestPosition( ptTest, frGrid.VersZ(), frGrid.VersZ(), dMove) ;
ptMid.z = dMove ;
// se sta sulla linea che unisce gli estremi, basta interpolazione lineare
if ( abs( ptMid.z - ( ptP1.z + ptP2.z) / 2) < 0.004 * dDist)
return Media( ptP1, ptP2, ( ptP1.z - dLevel) / ( ptP1.z - ptP2.z)) ;
// altrimenti nuova suddivisione della parte con estremi da parte opposta rispetto al livello
bool b1On = ( ptP1.z > dLevel) ;
bool bMidOn = ( ptMid.z > dLevel) ;
if ( b1On != bMidOn)
ptP2 = ptMid ;
else
ptP1 = ptMid ;
ptMid = Media( ptP1, ptP2) ;
++ nCount ;
}
ptMid.z = dLevel ;
return ptMid ;
}
//----------------------------------------------------------------------------
static int
ProcessSquare( int nFlag, const Point3d ptP[4], double dLevel,
const ICAvToolSurfTm& cavTstm, const Frame3d &frGrid,
Point3d& ptL1s, Point3d& ptL1e, Point3d& ptL2s, Point3d& ptL2e)
{
static int LineTable[16][5] = { { 0, -1, -1, -1, -1},
{ 1, 0, 3, -1, -1},
{ 1, 1, 0, -1, -1},
{ 1, 1, 3, -1, -1},
{ 1, 2, 1, -1, -1},
{ 2, 0, 1, 2, 3},
{ 1, 2, 0, -1, -1},
{ 1, 2, 3, -1, -1},
{ 1, 3, 2, -1, -1},
{ 1, 0, 2, -1, -1},
{ 2, 1, 2, 3, 0},
{ 1, 1, 2, -1, -1},
{ 1, 3, 1, -1, -1},
{ 1, 0, 1, -1, -1},
{ 1, 3, 0, -1, -1},
{ 0, -1, -1, -1, -1}} ;
// flag fuori dai limiti
if ( nFlag < 0 || nFlag > 15)
return -1 ;
// nessuna linea
if ( LineTable[nFlag][0] == 0)
return 0 ;
// una linea
else if ( LineTable[nFlag][0] == 1) {
int nI1s = LineTable[nFlag][1] ;
int nI1e = LineTable[nFlag][2] ;
ptL1s = CalcPoint( ptP[nI1s], ptP[( nI1s + 1) % 4], dLevel, cavTstm, frGrid) ;
ptL1e = CalcPoint( ptP[nI1e], ptP[( nI1e + 1) % 4], dLevel, cavTstm, frGrid) ;
return ( AreSamePointApprox( ptL1s, ptL1e) ? 0 : 1) ;
}
// due linee
else if ( LineTable[nFlag][0] == 2) {
int nI1s = LineTable[nFlag][1] ;
int nI1e = LineTable[nFlag][2] ;
ptL1s = CalcPoint( ptP[nI1s], ptP[( nI1s + 1) % 4], dLevel, cavTstm, frGrid) ;
ptL1e = CalcPoint( ptP[nI1e], ptP[( nI1e + 1) % 4], dLevel, cavTstm, frGrid) ;
int nI2s = LineTable[nFlag][3] ;
int nI2e = LineTable[nFlag][4] ;
ptL2s = CalcPoint( ptP[nI2s], ptP[( nI2s + 1) % 4], dLevel, cavTstm, frGrid) ;
ptL2e = CalcPoint( ptP[nI2e], ptP[( nI2e + 1) % 4], dLevel, cavTstm, frGrid) ;
return 2 ;
}
return -1 ;
}
//----------------------------------------------------------------------------
static bool
MarchingSquares( const DBLVECTOR& vdGrid, int nStepX, int nStepY, double dStep, double dLevel,
const ICAvToolSurfTm& cavTstm, const Frame3d &frGrid, POLYLINEVECTOR& vPL)
{
// Predispongo il concatenamento
BIPNTVECTOR vBiPnt ;
vBiPnt.reserve( 2 * ( nStepX + nStepY)) ;
ChainCurves chainC ;
chainC.Init( false, EPS_SMALL, 2 * ( nStepX + nStepY)) ;
// Ciclo sui quadrati della griglia
for ( int j = 0 ; j < nStepY ; ++ j) {
for ( int i = 0 ; i < nStepX ; ++ i) {
// indici dei vertici nella griglia
int nInd0 = i + j * ( nStepX + 1) ;
int nInd1 = ( i + 1) + j * ( nStepX + 1) ;
int nInd2 = ( i + 1) + ( j + 1) * ( nStepX + 1) ;
int nInd3 = i + ( j + 1) * ( nStepX + 1) ;
// flag del quadrato
int nFlag = ( vdGrid[nInd0] > dLevel ? 1 : 0) +
( vdGrid[nInd1] > dLevel ? 2 : 0) +
( vdGrid[nInd2] > dLevel ? 4 : 0) +
( vdGrid[nInd3] > dLevel ? 8 : 0) ;
// punti di vertice del quadrato
Point3d ptP[4] = { { i * dStep, j * dStep, vdGrid[nInd0]},
{ ( i + 1) * dStep, j * dStep, vdGrid[nInd1]},
{ ( i + 1) * dStep, ( j + 1) * dStep, vdGrid[nInd2]},
{ i * dStep, ( j + 1) * dStep, vdGrid[nInd3]}} ;
// calcolo segmenti da inserire
Point3d ptL1s, ptL1e, ptL2s, ptL2e ;
int nSegCnt = ProcessSquare( nFlag, ptP, dLevel, cavTstm, frGrid, ptL1s, ptL1e, ptL2s, ptL2e) ;
if ( nSegCnt == -1)
return false ;
else if ( nSegCnt == 1) {
vBiPnt.emplace_back( ptL1s, ptL1e) ;
Vector3d vtDir1 = ptL1e - ptL1s ; vtDir1.Normalize() ;
chainC.AddCurve( int( vBiPnt.size()), ptL1s, vtDir1, ptL1e, vtDir1) ;
}
else if ( nSegCnt == 2) {
vBiPnt.emplace_back( ptL1s, ptL1e) ;
Vector3d vtDir1 = ptL1e - ptL1s ; vtDir1.Normalize() ;
chainC.AddCurve( int( vBiPnt.size()), ptL1s, vtDir1, ptL1e, vtDir1) ;
vBiPnt.emplace_back( ptL2s, ptL2e) ;
Vector3d vtDir2 = ptL2e - ptL2s ; vtDir2.Normalize() ;
chainC.AddCurve( int( vBiPnt.size()), ptL2s, vtDir2, ptL2e, vtDir2) ;
}
}
}
// Recupero i contorni
INTVECTOR vnId ;
while ( chainC.GetChainFromNear( ORIG, false, vnId)) {
// creo una composita
CurveComposite crvCompo ;
crvCompo.AddPoint( vBiPnt[vnId[0]-1].first) ;
for ( int i = 0 ; i < int( vnId.size()) ; ++ i) {
Point3d ptCurr = vBiPnt[vnId[i]-1].second ;
crvCompo.AddLine( ptCurr) ;
}
// la chiudo
crvCompo.Close() ;
// elimino le parti allineate
crvCompo.MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG) ;
// per test mettere a 1
#if 0
vPL.emplace_back( PolyLine()) ;
crvCompo.ApproxWithLines( 0, 0, ICurve::APL_SPECIAL, vPL.back()) ;
#else
// eseguo offset a sinistra pari allo step
OffsetCurve offsCompo ;
offsCompo.Make( &crvCompo, -( dStep - 10 * EPS_SMALL), ICurve::OFF_CHAMFER) ;
PtrOwner<ICurve> pCrvOffset( offsCompo.GetLongerCurve()) ;
if ( ! IsNull( pCrvOffset)) {
vPL.emplace_back( PolyLine()) ;
pCrvOffset->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, vPL.back()) ;
}
#endif
}
return true ;
}
//----------------------------------------------------------------------------
// Silhouette rispetto ad una direzione e sopra una quota di una superficie TriMesh
// ( dTol è il passo della griglia di campionamento e il raggio del cilindro di prova)
//----------------------------------------------------------------------------
bool
CAvSilhouetteSurfTm( const ISurfTriMesh& Stm, const Plane3d& plPlane, double dTol, POLYLINEVECTOR& vPL)
{
// verifico superficie
if ( &Stm == nullptr || ! Stm.IsValid())
return false ;
// verifico piano
if ( &plPlane == nullptr || plPlane.GetVersN().IsSmall())
return false ;
// verifico tolleranza
dTol = max( dTol, 100 * EPS_SMALL) ;
// verifico parametri di ritorno
if ( &vPL == nullptr)
return false ;
vPL.clear() ;
// sistema di riferimento nel piano
Frame3d frGrid ;
if ( ! frGrid.Set( plPlane.GetPoint(), plPlane.GetVersN()))
return false ;
// bounding box della superficie nel riferimento del piano
BBox3d b3Surf ;
if ( ! Stm.GetBBox( GetInvert( frGrid), b3Surf, BBF_STANDARD))
return false ;
// calcolo dati della griglia
const double EXTRA_XY = 1.5 * dTol ;
const double EXTRA_Z = 10 * dTol ;
b3Surf.Expand( EXTRA_XY, EXTRA_XY, EXTRA_Z) ;
int nStepX = int( ceil( b3Surf.GetDimX() / dTol)) ;
int nStepY = int( ceil( b3Surf.GetDimY() / dTol)) ;
frGrid.ChangeOrig( GetToGlob( b3Surf.GetMin(), frGrid)) ;
double dDimZ = b3Surf.GetDimZ() ;
double dLevelOffs = - b3Surf.GetMin().z ;
// calcolo dei punti della griglia (sul top del cilindro)
PNTUVECTOR vPntM( ( nStepX + 1) * ( nStepY + 1)) ;
for ( int j = 0 ; j <= nStepY ; ++ j) {
for ( int i = 0 ; i <= nStepX ; ++ i) {
int nInd = i + j * ( nStepX + 1) ;
Point3d ptP = GetToGlob( Point3d( i * dTol, j * dTol, dDimZ), frGrid) ;
vPntM[nInd] = { ptP, 0.} ;
}
}
// esecuzione della verifica
CAvToolSurfTm cavTstm ;
cavTstm.SetStdTool( dDimZ, dTol, 0) ;
cavTstm.SetSurfTm( Stm) ;
if ( ! cavTstm.TestSeries( vPntM, frGrid.VersZ(), frGrid.VersZ(), -1))
return false ;
// griglia degli spostamenti
DBLVECTOR vdGrid( ( nStepX + 1) * ( nStepY + 1)) ;
for ( int j = 0 ; j <= nStepY ; ++ j) {
for ( int i = 0 ; i <= nStepX ; ++ i) {
int nInd = i + j * ( nStepX + 1) ;
vdGrid[nInd] = vPntM[nInd].second ;
}
}
// calcolo della silhouette con il metodo MarchingSquares
double dLevel = dLevelOffs ;
if ( ! MarchingSquares( vdGrid, nStepX, nStepY, dTol, dLevel, cavTstm, frGrid, vPL))
return false ;
// riporto nella corretta posizione le curve trovate
for ( auto& PL : vPL)
PL.ToGlob( frGrid) ;
return true ;
}
//----------------------------------------------------------------------------
ICAvParSilhouettesSurfTm*
CreateCAvParSilhouettesSurfTm( void)
{
return static_cast<ICAvParSilhouettesSurfTm*> ( new(nothrow) CAvParSilhouettesSurfTm) ;
}
//----------------------------------------------------------------------------
// Silhouette rispetto ad una direzione e sopra diverse quote di una superficie TriMesh
//----------------------------------------------------------------------------
CAvParSilhouettesSurfTm::CAvParSilhouettesSurfTm( void)
: m_dTol( 100 * EPS_SMALL), m_nStepX( 0), m_nStepY( 0), m_dLevelOffs( 0), m_bGridOk( false)
{
}
//----------------------------------------------------------------------------
bool
CAvParSilhouettesSurfTm::SetData( const CISURFTMPVECTOR& vpStm, const Frame3d& frPlanes, double dTol)
{
m_vpStm = vpStm ;
m_frGrid = frPlanes ;
m_dTol = max( dTol, 100 * EPS_SMALL) ;
m_nStepX = 0 ;
m_nStepY = 0 ;
m_dDimZ = 0 ;
m_dLevelOffs = 0 ;
m_bGridOk = false ;
return true ;
}
//----------------------------------------------------------------------------
bool
CAvParSilhouettesSurfTm::Prepare( void)
{
// ingombro delle superfici nel riferimento dei piani
BBox3d b3All ;
Frame3d frInv = GetInvert( m_frGrid) ;
for ( auto pStm : m_vpStm) {
BBox3d b3Surf ;
if ( ! pStm->GetBBox( frInv, b3Surf, BBF_STANDARD))
return false ;
b3All.Add( b3Surf) ;
}
// calcolo dati della griglia
const double EXTRA_XY = 1.5 * m_dTol ;
const double EXTRA_Z = 10 * m_dTol ;
b3All.Expand( EXTRA_XY, EXTRA_XY, EXTRA_Z) ;
m_nStepX = int( ceil( b3All.GetDimX() / m_dTol)) ;
m_nStepY = int( ceil( b3All.GetDimY() / m_dTol)) ;
m_frGrid.ChangeOrig( GetToGlob( b3All.GetMin(), m_frGrid)) ;
m_dDimZ = b3All.GetDimZ() ;
m_dLevelOffs = - b3All.GetMin().z ;
// calcolo dei punti della griglia (sul top del cilindro)
PNTUVECTOR vPntM( ( m_nStepX + 1) * ( m_nStepY + 1)) ;
for ( int j = 0 ; j <= m_nStepY ; ++ j) {
for ( int i = 0 ; i <= m_nStepX ; ++ i) {
int nInd = i + j * ( m_nStepX + 1) ;
Point3d ptP = GetToGlob( Point3d( i * m_dTol, j * m_dTol, m_dDimZ), m_frGrid) ;
vPntM[nInd] = { ptP, 0.} ;
}
}
// esecuzione della verifica
if ( ! m_cavTstm.SetStdTool( m_dDimZ, m_dTol, 0))
return false ;
if ( m_vpStm.empty() || ! m_cavTstm.SetSurfTm( *( m_vpStm[0])))
return false ;
for ( int k = 1 ; k < int( m_vpStm.size()) ; ++ k)
m_cavTstm.AddSurfTm( *( m_vpStm[k])) ;
if ( ! m_cavTstm.TestSeries( vPntM, m_frGrid.VersZ(), m_frGrid.VersZ(), -1))
return false ;
// griglia degli spostamenti
m_vdGrid.clear() ;
m_vdGrid.resize( ( m_nStepX + 1) * ( m_nStepY + 1)) ;
for ( int j = 0 ; j <= m_nStepY ; ++ j) {
for ( int i = 0 ; i <= m_nStepX ; ++ i) {
int nInd = i + j * ( m_nStepX + 1) ;
m_vdGrid[nInd] = vPntM[nInd].second ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
CAvParSilhouettesSurfTm::GetSilhouette( double dLevel, POLYLINEVECTOR& vPL)
{
// reset risultato
vPL.clear() ;
// se necessario eseguo i calcoli preparatori
if ( ! m_bGridOk && ! Prepare())
return false ;
m_bGridOk = true ;
// calcolo della silhouette con il metodo MarchingSquares
dLevel += m_dLevelOffs ;
if ( ! MarchingSquares( m_vdGrid, m_nStepX, m_nStepY, m_dTol, dLevel, m_cavTstm, m_frGrid, vPL))
return false ;
// riporto nella corretta posizione le curve trovate
for ( auto& PL : vPL)
PL.ToGlob( m_frGrid) ;
return true ;
}