//---------------------------------------------------------------------------- // 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( &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 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 ( 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 ; }