diff --git a/SurfFinishing.cpp b/SurfFinishing.cpp index fe2ade9..7bff482 100644 --- a/SurfFinishing.cpp +++ b/SurfFinishing.cpp @@ -110,12 +110,13 @@ enum { #define DEBUG 0 #define DEBUG_SFR 0 #define ENABLE_ZCONST_DEBUG 0 +#define ENABLE_ZCONST_PATH_DEBUG 0 #define ENABLE_OPTIMAL_DEBUG 0 #define ENABLE_OPTIMAL_SOFT_DEBUG 0 #define ENABLE_OPTIMAL_FINAL_CURVES_DEBUG 0 #define ENABLE_PENCIL_DEBUG 0 #define ENABLE_LINK_DEBUG 0 -#if DEBUG || DEBUG_SFR || ENABLE_ZCONST_DEBUG || ENABLE_OPTIMAL_DEBUG || ENABLE_OPTIMAL_SOFT_DEBUG || ENABLE_PENCIL_DEBUG || ENABLE_OPTIMAL_FINAL_CURVES_DEBUG || ENABLE_LINK_DEBUG +#if DEBUG || DEBUG_SFR || ENABLE_ZCONST_DEBUG || ENABLE_ZCONST_PATH_DEBUG || ENABLE_OPTIMAL_DEBUG || ENABLE_OPTIMAL_SOFT_DEBUG || ENABLE_PENCIL_DEBUG || ENABLE_OPTIMAL_FINAL_CURVES_DEBUG || ENABLE_LINK_DEBUG #include "/EgtDev/Include/EGkGeoPoint3d.h" #include "/EgtDev/Include/EGkGeoVector3d.h" #include "/EgtDev/Include/EgtPerfCounter.h" @@ -2532,278 +2533,461 @@ SurfFinishing::CreateZConstPaths( ICAvToolSurfTm* pCAvTlStm, const Frame3d& frSu { // funzione per collegare le curve trovate creando quindi i percorsi di lavoro // NB. Nel primo TempParam deve essere memorizzata la distanza del piano della curva con il piano della Sfr - // NB. Vengono memorizzate le seguenti informazioni : - // - la prima tempProp della curva è il numero del piano in cui giace il punto iniziale - // - la seconda tempProp della curva è il numero del piano in cui giace il punto finale // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid()) return false ; - PtrOwner pSfrClass( CloneSurfFlatRegion( pSfr)) ; - if ( IsNull( pSfrClass) || ! pSfrClass->IsValid()) - return false ; // porto il tool nel frame della superficie Vector3d vtToolLoc = GetToLoc( vtTool, frSurf) ; - // porto tutte le curve nel piano XY locale alla pSfr + // porto tutte le curve nel piano XY locale alla pSfr e, per ognuna di esse, definisco la regione + // piana di proiezione del materiale rimosso orientato a sinistra della curva Frame3d frXY ; Point3d ptCenter ; pSfr->GetCentroid( ptCenter) ; if ( ! frXY.Set( ptCenter, vtToolLoc)) return false ; + vector vSfrRemMatCol ; vSfrRemMatCol.resize( vCrvCompo.size()) ; // RemovedMaterialColumn + double dRadSignOffs = m_Params.m_bInvert ? m_TParams.m_dDiam / 2. : - m_TParams.m_dDiam / 2. ; + #if ENABLE_ZCONST_PATH_DEBUG + int nGrp = m_pGeomDB->AddGroup( GDB_ID_NULL, GDB_ID_ROOT, GLOB_FRM) ; + m_pGeomDB->SetName( nGrp, "Crv & Sfr") ; + m_pGeomDB->SetStatus( nGrp, GDB_ST_OFF) ; + #endif for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) { + vSfrRemMatCol[i].resize( vCrvCompo[i].size()) ; for ( int j = 0 ; j < int( vCrvCompo[i].size()) ; ++ j) { + // --- Frame locale XY vCrvCompo[i][j]->ToLoc( frXY) ; - #if ENABLE_ZCONST_DEBUG - int _a = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, vCrvCompo[i][j]->Clone()) ; - m_pGeomDB->SetMaterial( _a, OLIVE) ; + vCrvCompo[i][j]->SetExtrusion( m_Params.m_bInvert ? - Z_AX : Z_AX) ; + SurfFlatRegionByContours SfrBC ; + // --- Se curva chiusa + if ( vCrvCompo[i][j]->IsClosed()) { + SfrBC.AddCurve( CloneCurveComposite( vCrvCompo[i][j])) ; + OffsetCurve OffsCrv ; + OffsCrv.Make( vCrvCompo[i][j], dRadSignOffs, ICurve::OFF_FILLET) ; + PtrOwner pCrvIsl( OffsCrv.GetLongerCurve()) ; + while ( ! IsNull( pCrvIsl)) { + SfrBC.AddCurve( Release( pCrvIsl)) ; + pCrvIsl.Set( OffsCrv.GetLongerCurve()) ; + } + } + // --- Se curva aperta + else { + OffsetCurve OffsCrv ; + OffsCrv.Make( vCrvCompo[i][j], dRadSignOffs, ICurve::OFF_FILLET) ; + // [euristico, migliorabile/da_correggere] + PtrOwner pCrvInt( OffsCrv.GetLongerCurve()) ; + if ( ! IsNull( pCrvInt)) { + pCrvInt->Invert() ; + Point3d ptS ; pCrvInt->GetStartPoint( ptS) ; + Point3d ptE ; vCrvCompo[i][j]->GetStartPoint( ptE) ; + PtrOwner pSfrLoop( CreateCurveComposite()) ; + if ( IsNull( pSfrLoop)) + return false ; + pSfrLoop->AddCurve( CloneCurveComposite( vCrvCompo[i][j])) ; + pSfrLoop->AddLine( ptS) ; + pSfrLoop->AddCurve( Release( pCrvInt)) ; + pSfrLoop->AddLine( ptE) ; + SfrBC.AddCurve( Release( pSfrLoop)) ; + } + } + vSfrRemMatCol[i][j].Set( SfrBC.GetSurf()) ; + if ( IsNull( vSfrRemMatCol[i][j]) || ! vSfrRemMatCol[i][j]->IsValid()) + vSfrRemMatCol[i][j].Set( GetSurfFlatRegionFromFatCurve( vCrvCompo[i][j]->Clone(), m_TParams.m_dDiam / 2., false, false)) ; + // tutte rivolte come Z_AX ( vtTool locale) + if ( AreOppositeVectorApprox( vSfrRemMatCol[i][j]->GetNormVersor(), Z_AX)) + vSfrRemMatCol[i][j]->Invert() ; + #if ENABLE_ZCONST_PATH_DEBUG + Color myCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; + int nLay = m_pGeomDB->AddGroup( GDB_ID_NULL, nGrp, GLOB_FRM) ; + m_pGeomDB->SetName( nLay, ToString( vCrvCompo[i][j]->GetTempParam( 0))) ; + int nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nLay, vCrvCompo[i][j]->Clone()) ; + m_pGeomDB->SetMaterial( nId, myCol) ; + nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nLay, vSfrRemMatCol[i][j]->Clone()) ; + myCol.SetAlpha( .25) ; + m_pGeomDB->SetMaterial( nId, myCol) ; #endif } } - // tengo una copia dei bordi della regione - pSfrClass->ToLoc( frXY) ; - #if ENABLE_ZCONST_DEBUG - int _a = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pSfrClass->Clone()) ; - m_pGeomDB->SetMaterial( _a, Color( 0., 1., 0., .15)) ; + + // definisco le relazioni delle curve mediante le superfici create + int nRowSize = int( vCrvCompo.size()) ; + int nColSize = 0 ; + for ( int i = 0 ; i < nRowSize ; ++ i) + nColSize = max( nColSize, int( vCrvCompo[i].size())) ; + unordered_map MapLock ; + unordered_map MapUnlock ; + for ( int nR = int( vCrvCompo.size()) - 1 ; nR >= 1 ; -- nR) { + for ( int nC = 0 ; nC < int( vCrvCompo[nR].size()) ; ++ nC) { + for ( int nCol = 0 ; nCol < int( vCrvCompo[nR - 1].size()) ; ++ nCol) { + PtrOwner pSfrCheck( CloneSurfFlatRegion( vSfrRemMatCol[nR][nC])) ; + if ( ! IsNull( pSfrCheck)) { + pSfrCheck->Intersect( *vSfrRemMatCol[nR - 1][nCol]) ; + if ( pSfrCheck->IsValid() && pSfrCheck->GetChunkCount() > 0) { + int nKeyLock = nR * nColSize + nC ; + int nKeyUnLock = ( nR - 1) * nColSize + nCol ; + MapLock[nKeyLock].emplace_back( make_pair( nR - 1, nCol)) ; + MapUnlock[nKeyUnLock].emplace_back( make_pair( nR, nC)) ; + } + } + } + } + } + + // definisco i gruppi di curve indpendenti; Un insieme di curve risulta indipendente da un altro + // insieme se l'ordine di esecuzione di essi è indifferente ; + // [non esiste alcuna curva nel gruppo A che interferisce con una curva del gruppo B) + vector vIndependentCurveGroup ; + bool bGroup = true ; + int nCont = 0 ; + while ( bGroup && nCont < nRowSize * nColSize) { + ++ nCont ; + bGroup = false ; + // cerco la prima curva non ancora inserita in un gruppo + for ( int nR = int( vCrvCompo.size()) - 1 ; nR >= 0 && ! bGroup ; -- nR) { + // scorro sulle colonne ( dalla prima all'ultima) [ordine casuale] + for ( int nC = 0 ; nC < int( vCrvCompo[nR].size()) && ! bGroup ; ++ nC) { + // se la curva attuale già in un gruppo ( quindi nullptr), passo alla successiva + if ( vCrvCompo[nR][nC] == nullptr) + continue ; + // se trovo una curva, allora ho un nuovo gruppo da analizzare + bGroup = true ; + // creazione spazio per un nuovo gruppo + vIndependentCurveGroup.resize( vIndependentCurveGroup.size() + 1) ; + INTINTVECTOR vMapIndCheck = { make_pair( nR, nC)} ; + // finchè ho indici di riga-colonna da verificare + while ( ! vMapIndCheck.empty()) { + // memorizzo gli indici correnti + int nCurrRow = vMapIndCheck.back().first ; + int nCurrCol = vMapIndCheck.back().second ; + // tolgo l'elemento ( non devo ricontrollarlo all'iterazione successiva) + vMapIndCheck.pop_back() ; + // aggiungo la curva al percorso corrente + vIndependentCurveGroup.back().emplace_back( Release( vCrvCompo[nCurrRow][nCurrCol])) ; + // --- se la curva non è bloccata da nessun'altra + auto itLock = MapLock.find( nCurrRow * nColSize + nCurrCol) ; + if ( itLock == MapLock.end()) { + ; // non faccio nulla ( ultimo piano locale) + } + // --- altrimenti + else { + // --- scorro le curve che bloccano quella corrente + for ( int i = 0 ; i < int( itLock->second.size()) ; ++ i) { + int nBR = itLock->second[i].first ; + int nBC = itLock->second[i].second ; + INTINT Key = make_pair( nBR, nBC) ; + auto itUnLock = MapUnlock.find( nBR * nColSize + nBC) ; + // verifico che non ci siano altre curve bloccate + bool bLock = false ; + for ( int j = 0 ; j < int( itUnLock->second.size()) && ! bLock ; ++ j) + bLock = ( vCrvCompo[itUnLock->second[j].first][itUnLock->second[j].second] != nullptr) ; + if ( ! bLock) + vMapIndCheck.emplace_back( Key) ; + } + } + } + } + } + } + + #if ENABLE_ZCONST_PATH_DEBUG + nGrp = m_pGeomDB->AddGroup( GDB_ID_NULL, GDB_ID_ROOT, GLOB_FRM) ; + m_pGeomDB->SetName( nGrp, "Independent Groups") ; + m_pGeomDB->SetStatus( nGrp, GDB_ST_OFF) ; + vector vCol ; + vector vLay ; + for ( int nPath = 0 ; nPath < int( vIndependentCurveGroup.size()) ; ++ nPath) { + int nLay = m_pGeomDB->AddGroup( GDB_ID_NULL, nGrp, GLOB_FRM) ; + vLay.emplace_back( nLay) ; + Color myCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; + vCol.emplace_back( myCol) ; + for ( auto& pCrv : vIndependentCurveGroup[nPath]) { + int nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nLay, CloneCurveComposite( pCrv)) ; + m_pGeomDB->SetMaterial( nId, myCol) ; + } + } #endif + + // definisco il piano locale della regione piana ( frXY è centrato su di essa) + Plane3d plProjection ; + if ( ! plProjection.Set( ORIG, Z_AX)) + return false ; + + // ordino i gruppi di curve indipendenti + typedef pair> INTDBLDBL ; // > + vector vMapGrpZLoc ; + vMapGrpZLoc.reserve( vIndependentCurveGroup.size()) ; + for ( int i = 0 ; i < int( vIndependentCurveGroup.size()) ; ++ i) { + // scorro le curve del gruppo cercando quella più in alto ( rispetto alla Zlocale di frXY) + double dZMax = - INFINITO ; + double dZMin = INFINITO ; + for ( auto& pCrv : vIndependentCurveGroup[i]) { + double dCurrZ = pCrv->GetTempParam( 0) ; + dZMax = max( dZMax, dCurrZ) ; + dZMin = min( dZMin, dCurrZ) ; + } + vMapGrpZLoc.emplace_back( make_pair( i, make_pair( dZMax, dZMin))) ; + } + // ordino + sort( vMapGrpZLoc.begin(), vMapGrpZLoc.end(), []( const INTDBLDBL& GrpA, const INTDBLDBL& GrpB) { + // Se ZMax coincidenti, viene prima quello con ZMin superiore + if ( abs( GrpA.second.first - GrpB.second.first) < 10 * EPS_SMALL) + return ( GrpA.second.second > GrpB.second.second) ; + // Se diffeenti, viene prima quello con ZMax superiore + return ( GrpA.second > GrpB.second) ; + }) ; + + #if ENABLE_ZCONST_PATH_DEBUG + nGrp = m_pGeomDB->AddGroup( GDB_ID_NULL, GDB_ID_ROOT, GLOB_FRM) ; + m_pGeomDB->SetName( nGrp, "Ordered Independent Groups") ; + m_pGeomDB->SetStatus( nGrp, GDB_ST_OFF) ;; + vLay.clear() ; + for ( int nPath = 0 ; nPath < int( vMapGrpZLoc.size()) ; ++ nPath) { + int nLay = m_pGeomDB->AddGroup( GDB_ID_NULL, nGrp, GLOB_FRM) ; + vLay.emplace_back( nLay) ; + for ( auto& pCrv : vIndependentCurveGroup[vMapGrpZLoc[nPath].first]) { + int nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nLay, CloneCurveComposite( pCrv)) ; + m_pGeomDB->SetMaterial( nId, vCol[vMapGrpZLoc[nPath].first]) ; + } + } + #endif + + // tengo una copia dei bordi della regione ( servono per limitare i collegamenti tra le curve dei Path) + PtrOwner pSfrClass( CloneSurfFlatRegion( pSfr)) ; + if ( IsNull( pSfrClass) || ! pSfrClass->IsValid() || ! pSfrClass->ToLoc( frXY)) + return false ; ICRVCOMPOPOVECTOR vCrvSfrLoops ; for ( int nC = 0 ; nC < pSfrClass->GetChunkCount() ; ++ nC) { for ( int nL = 0 ; nL < pSfrClass->GetLoopCount( nC) ; ++ nL) { PtrOwner pCrvLoop( ConvertCurveToComposite( pSfrClass->GetLoop( nC, nL))) ; if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid()) return false ; - // tutti i loop sono orientati come il loop esterno ( aiuta per la classificazione della curve) + // tutti i loop sono orientati come il loop esterno + // NB. Per classificare i collegamenti si usa la regione, non queste curve ! + // [ Vengono utilizzate per non inizializzare ogni volta una curva corrispondente al bordo + // della superficie di classificazione] if ( nL > 0) pCrvLoop->Invert() ; vCrvSfrLoops.emplace_back( Release( pCrvLoop)) ; - #if ENABLE_ZCONST_DEBUG - int _a = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, vCrvSfrLoops.back()->Clone()) ; - m_pGeomDB->SetMaterial( _a, PURPLE) ; - #endif - } } - // definisco il piano locale della regione piana - Plane3d plProjection ; - if ( ! plProjection.Set( ORIG, Z_AX)) - return false ; - int nCurrRow = 0 ; - // scorro le righe - for ( ; nCurrRow < int( vCrvCompo.size()) - 1 ; ++ nCurrRow) { - // scorro sulle colonne - for ( int nCurrCol = 0 ; nCurrCol < int( vCrvCompo[nCurrRow].size()) ; ++ nCurrCol) { - // se la curva attuale è nulla, passo alla successiva - if ( vCrvCompo[nCurrRow][nCurrCol] == nullptr) - continue ; - // recupero il punto finale della curva corrente - Point3d ptCurrEnd ; vCrvCompo[nCurrRow][nCurrCol]->GetEndPoint( ptCurrEnd) ; - // recupero la distanza con la Sfr - double dSfrDistCurr = vCrvCompo[nCurrRow][nCurrCol]->GetTempParam( 0) ; - // inizializzo spazio per un nuovo percorso ( inserisco la curva corrente) - vCrvPath.resize( vCrvPath.size() + 1) ; - vCrvPath.back().Set( vCrvCompo[nCurrRow][nCurrCol]) ; - // salvo come prima temp prop il numero del piano a cui sono - vCrvPath.back()->SetTempProp( nCurrRow + 1, 0) ; - // scorro sulle righe successive - for ( int nNextRow = nCurrRow + 1 ; nNextRow < int( vCrvCompo.size()) ; ++ nNextRow) { - // cerco le curve valide tra le colonne - double dMinSqDist = INFINITO ; // euristico - int nNextCol_path = -1 ; - for ( int nNextCol = 0 ; nNextCol < int( vCrvCompo[nNextRow].size()) ; ++ nNextCol) { - // se curva già in un Path, cerco la successiva - if ( vCrvCompo[nNextRow][nNextCol] == nullptr) - continue ; - // se la curva successiva è aperta - if ( ! vCrvCompo[nNextRow][nNextCol]->IsClosed()) { - // controllo la distanza con il punto iniziale della curva - Point3d ptS ; vCrvCompo[nNextRow][nNextCol]->GetStartPoint( ptS) ; - double dNextSqDist = SqDist( ptS, ptCurrEnd) ; - if ( dNextSqDist < dMinSqDist) { - dMinSqDist = dNextSqDist ; - nNextCol_path = nNextCol ; - } - // controllo la distanza con il punto finale della curva - Point3d ptE ; vCrvCompo[nNextRow][nNextCol]->GetEndPoint( ptE) ; - dNextSqDist = SqDist( ptE, ptCurrEnd) ; - if ( dNextSqDist < dMinSqDist) { - dMinSqDist = dNextSqDist ; - nNextCol_path = nNextCol ; - vCrvCompo[nNextRow][nNextCol]->Invert() ; // inverto - } - } - // se la curva successiva è chiusa - else { - // cerco il punto a minima distanza - DistPointCurve DistPtCrv( ptCurrEnd, *vCrvCompo[nNextRow][nNextCol]) ; - double dNextSqDist = 0. ; - if ( DistPtCrv.GetSqDist( dNextSqDist)) { - if ( dNextSqDist < dMinSqDist) { - dMinSqDist = dNextSqDist ; - nNextCol_path = nNextCol ; - double dMinPar = 0. ; - int nFlag = 0 ; - if ( DistPtCrv.GetParamAtMinDistPoint( 0., dMinPar, nFlag)) - vCrvCompo[nNextRow][nNextCol]->ChangeStartPoint( dMinPar) ; - } - } - } + // definisco i collegamenti tra i percorsi + #if ENABLE_ZCONST_PATH_DEBUG + nGrp = m_pGeomDB->AddGroup( GDB_ID_NULL, GDB_ID_ROOT, GLOB_FRM) ; + m_pGeomDB->SetName( nGrp, "Paths") ; + m_pGeomDB->SetStatus( nGrp, GDB_ST_OFF) ; + #endif + const double LINK_TOL = 25. * EPS_SMALL ; + Point3d ptRef = P_INVALID ; + for ( int i = 0 ; i < int( vMapGrpZLoc.size()) ; ++ i) { + // recupero l'indice ordinato del percorso + int nPath = vMapGrpZLoc[i].first ; + // inverto il vettore delle curve di bordo del percorso corrente + reverse( vIndependentCurveGroup[nPath].begin(), vIndependentCurveGroup[nPath].end()) ; + // definisco il vettore dei collegamenti creati tra le curve + // NB. Un collegamento potrebbe non essere valido e non realizzabile + // NB. Il collegamento i-esimo collega la curva di bordo (i-1)-esima con la curva di bordo i-esima + ICRVCOMPOPOVECTOR vCompoLink ; + vCompoLink.resize( vIndependentCurveGroup[nPath].size()) ; // nullptr per ogni Link + + // --- se non ho curve, passo al gruppo successivo + if ( vIndependentCurveGroup[nPath].empty()) + continue ; + + // --- se ho una sola curva, allora controllo se posso cambiare il suo punto d'inizio + // nel caso non sia la prima in assoluto [ -> controllo ptRef] + if ( int( vIndependentCurveGroup[nPath].size()) == 1) { + if ( ptRef.IsValid()) { + // --- se la curva di bordo singola è aperta + if ( ! vIndependentCurveGroup[nPath][0]->IsClosed()) { + // ... se necessario la inverto per recuperare l'estremo più vicino a ptRef + // [ per ora le curve aperte possono essere invertite nelle finiture a ZLevel] + Point3d ptS ; vIndependentCurveGroup[nPath][0]->GetStartPoint( ptS) ; + Point3d ptE ; vIndependentCurveGroup[nPath][0]->GetEndPoint( ptE) ; + if ( SqDist( ptE, ptRef) < SqDist( ptS, ptRef)) + vIndependentCurveGroup[nPath][0]->Invert() ; } - // se non ho trovato nessuna colonna valida - if ( nNextCol_path == -1) { - nCurrRow = 0 ; // azzero il contatore delle righe - break ; // interrompo ciclo sulle righe successive di ricerca - } - // altrimenti creo raccordo e aggiungo la curva al percorso + // --- se la curva di bordo singola è chiusa else { - // definisco un tratto lineare che collega gli estremi - Point3d ptStartLink = ptCurrEnd ; - Point3d ptEndLink ; vCrvCompo[nNextRow][nNextCol_path]->GetStartPoint( ptEndLink) ; - PtrOwner pLineLink( CreateCurveLine()) ; - if ( IsNull( pLineLink) || ! pLineLink->Set( ptStartLink, ptEndLink) || - ! pLineLink->IsValid()) { - // se tratto lineare non valido, non creo il collegamento - nCurrRow = 0 ; // azzero il contatore delle righe - break ; // interrompo ciclo sulle righe successive di ricerca - } - // se la linea è parallela a vtTool( Z_AX), allora ho una parete verticale (localmente) - Vector3d vtLineLink ; pLineLink->GetStartDir( vtLineLink) ; - if ( AreSameOrOppositeVectorEpsilon( vtLineLink, Z_AX, 100 * EPS_SMALL)) { - // in questo caso la proiezione del link è automaticamente sul bordo della pSrfClass - vCrvPath.back()->AddCurve( Release( pLineLink), true, 1500 * EPS_SMALL) ; - vCrvPath.back()->AddCurve( Release( vCrvCompo[nNextRow][nNextCol_path]), true, 1500 * EPS_SMALL) ; + // cerco il punto a minima distanza da ptRef ( non inverto, tengo il senso di lavorazione) + double dMinPar = 0. ; + int nFlag = 0 ; + DistPointCurve( ptRef, *vIndependentCurveGroup[nPath][0]).GetParamAtMinDistPoint( 0., dMinPar, nFlag) ; + vIndependentCurveGroup[nPath][0]->ChangeStartPoint( dMinPar) ; + } + } + // inserisco la curva nel percorso + vCrvPath.emplace_back( Release( vIndependentCurveGroup[nPath][0])) ; + // aggiorno il punto di riferimento + vCrvPath.back()->GetEndPoint( ptRef) ; + #if ENABLE_ZCONST_PATH_DEBUG + int nLay = m_pGeomDB->AddGroup( GDB_ID_NULL, nGrp, GLOB_FRM) ; + m_pGeomDB->SetName( nLay, "Single") ; + Color myCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; + int nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nLay, CloneCurveComposite( vCrvPath.back())) ; + m_pGeomDB->SetMaterial( nId, myCol) ; + #endif + continue ; + } + + // --- se ho più curve di bordo, scorro le curve a due a due per creare i collegamenti + for ( int nCompo = 0 ; nCompo < int( vIndependentCurveGroup[nPath].size()) - 1 ; ++ nCompo) { + // se prima curva di bordo e ho un punto di riferimento valido + if ( nCompo == 0 && ptRef.IsValid()) { + // --- se la prima curva di bordo è aperta + if ( ! vIndependentCurveGroup[nPath][0]->IsClosed()) { + // ... se necessario la inverto per recuperare l'estremo più vicino a ptRef + // [ per ora le curve aperte possono essere invertite nelle finiture a ZLevel] + Point3d ptS ; vIndependentCurveGroup[nPath][0]->GetStartPoint( ptS) ; + Point3d ptE ; vIndependentCurveGroup[nPath][0]->GetEndPoint( ptE) ; + if ( SqDist( ptE, ptRef) < SqDist( ptS, ptRef)) + vIndependentCurveGroup[nPath][0]->Invert() ; + } + // --- se la prima curva di bordo è chiusa + else { + // cerco il punto a minima distanza da ptRef ( non inverto, tengo il senso di lavorazione) + double dMinPar = 0. ; + int nFlag = 0 ; + DistPointCurve( ptRef, *vIndependentCurveGroup[nPath][0]).GetParamAtMinDistPoint( 0., dMinPar, nFlag) ; + vIndependentCurveGroup[nPath][0]->ChangeStartPoint( dMinPar) ; + } + } + // come riferimento imposto inizialmente il punto finale della curva corrente + vIndependentCurveGroup[nPath][nCompo]->GetEndPoint( ptRef) ; + // --- se la curva di bordo successiva è aperta... + if ( ! vIndependentCurveGroup[nPath][nCompo + 1]->IsClosed()) { + // ... se necessario la inverto per recuperare l'estremo più vicino a quello corrente + // [ per ora le curve aperte possono essere invertite nelle finiture a ZLevel] + Point3d ptS ; vIndependentCurveGroup[nPath][nCompo + 1]->GetStartPoint( ptS) ; + Point3d ptE ; vIndependentCurveGroup[nPath][nCompo + 1]->GetEndPoint( ptE) ; + if ( SqDist( ptE, ptRef) < SqDist( ptS, ptRef)) + vIndependentCurveGroup[nPath][nCompo + 1]->Invert() ; + } + // --- se la curva successiva è chiusa + else { + // cerco il punto a minima distanza ( non inverto, tengo il senso di lavorazione) + double dMinPar = 0. ; + int nFlag = 0 ; + DistPointCurve( ptRef, *vIndependentCurveGroup[nPath][nCompo + 1]).GetParamAtMinDistPoint( 0., dMinPar, nFlag) ; + vIndependentCurveGroup[nPath][nCompo + 1]->ChangeStartPoint( dMinPar) ; + } + + // definisco il collegamento diretto come tratto lineare + Point3d ptStartLink ; vIndependentCurveGroup[nPath][nCompo]->GetEndPoint( ptStartLink) ; + Point3d ptEndLink ; vIndependentCurveGroup[nPath][nCompo + 1]->GetStartPoint( ptEndLink) ; + PtrOwner pLineLink( CreateCurveLine()) ; + if ( IsNull( pLineLink) || ! pLineLink->Set( ptStartLink, ptEndLink) || ! pLineLink->IsValid()) + continue ; // il Link rimane nullptr + + // --- se il Link è parallelo a vtTool ( Z_AX in locale), allora ho una parete verticale ( localemente) + Vector3d vtDirLink ; pLineLink->GetStartDir( vtDirLink) ; + if ( AreSameOrOppositeVectorEpsilon( vtDirLink, Z_AX, 100 * EPS_SMALL)) { + // la proiezione del Link è automaticamente all'interno della superficie di classificazione + vCompoLink[nCompo + 1].Set( ConvertCurveToComposite( Release( pLineLink))) ; + } + // --- se il tratto lineare è troppo lungo, non considero il collegamento come valido + else { + if ( SqDist( ptStartLink, ptEndLink) > 4. * m_TParams.m_dDiam * m_TParams.m_dDiam) + continue ; // il Link rimane nullptr + + // proietto la curva nel piano locale della superficie di classificazione ( Z=0 ; Z_AX) + PtrOwner pLineLinkProj( ProjectCurveOnPlane( *pLineLink, plProjection)) ; + if ( IsNull( pLineLinkProj) || ! pLineLinkProj->IsValid()) + return false ; + // effettuo la classificazione + CRVCVECTOR ccClass ; + if ( ! pSfrClass->GetCurveClassification( *pLineLinkProj, EPS_SMALL, ccClass)) + continue ; // il Link rimane nullptr + + // inizializzo il link come curva vuota e scorro le classificazioni ottenute + vCompoLink[nCompo + 1].Set( CreateCurveComposite()) ; + for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) { + // recupero il tratto di curve corrente + PtrOwner pSubCrv( pLineLinkProj->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE)) ; + if ( IsNull( pSubCrv) || ! pSubCrv->IsValid()) // troppo piccolo... + continue ; // passo al tratto di classificazione successivo + // --- se la curva non è esterna alla regione, la conservo + if ( ccClass[i].nClass != CRVC_OUT) { + if ( ! vCompoLink[nCompo + 1]->AddCurve( Release( pSubCrv), true, LINK_TOL)) + return false ; } + // --- se la curva è esterna, devo seguire il bordo della regione di classificazione else { - // altrimenti, se i punti sono distanti, allora non considero valido il collegamento - if ( SqDist( ptStartLink, ptEndLink) > 2. * m_TParams.m_dDiam) { - nCurrRow = 0 ; // azzero il contatore delle righe - break ; // interrompo ciclo sulle righe successive di ricerca - } - - #if ENABLE_ZCONST_DEBUG - int _b = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pLineLink->Clone()) ; - m_pGeomDB->SetMaterial( _b, ORANGE) ; - #endif - - // proietto la curva nel piano locale della pSfrClass ( Z=0 ; Z_AX) - PtrOwner pLineLinkProjected( ProjectCurveOnPlane( *pLineLink, plProjection)) ; - if ( IsNull( pLineLinkProjected) || ! pLineLinkProjected->IsValid()) - return false ; - - #if ENABLE_ZCONST_DEBUG - int _c = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pLineLinkProjected->Clone()) ; - m_pGeomDB->SetMaterial( _c, YELLOW) ; - #endif - - // classifico la curva proiettata in base alla superficie - CRVCVECTOR ccClass ; - if ( ! pSfrClass->GetCurveClassification( *pLineLinkProjected, EPS_SMALL, ccClass)) - return false ; - // inizializzo la nuova curva di Link - PtrOwner pCompoLink( CreateCurveComposite()) ; - if ( IsNull( pCompoLink)) - return false ; - // scorro sulle classificazioni ottenute - for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) { - // recupero il tratto di curva corrente - PtrOwner pCrv( pLineLinkProjected->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE)) ; - if ( IsNull( pCrv) || ! pCrv->IsValid()) // troppo piccolo... - continue ; // passo al tratto di classificazione successivo - // se la curva non è esterna alla regione, allora la conservo (non rivina le Stm) - if ( ccClass[i].nClass != CRVC_OUT) { - if ( ! pCompoLink->AddCurve( Release( pCrv), true, 25 * EPS_SMALL)) + // recupero gli estremi del segmento di classificazione esterno alla pSfrClass + Point3d ptS_sfr ; pLineLinkProj->GetPointD1D2( ccClass[i].dParS, ICurve::FROM_MINUS, ptS_sfr) ; + Point3d ptE_sfr ; pLineLinkProj->GetPointD1D2( ccClass[i].dParE, ICurve::FROM_MINUS, ptE_sfr) ; + // scorro tutti i loop della regione di classificazione + for ( int j = 0 ; j < int( vCrvSfrLoops.size()) ; ++ j) { + // se i punti trovati sono sul loop corrente + if ( vCrvSfrLoops[j]->IsPointOn( ptS_sfr, LINK_TOL) && + vCrvSfrLoops[j]->IsPointOn( ptE_sfr, LINK_TOL)) { + // devo orientare la SubCrv secondo il giusto verso di percorrenza + double dUS ; vCrvSfrLoops[j]->GetParamAtPoint( ptS_sfr, dUS, LINK_TOL) ; + double dUE ; vCrvSfrLoops[j]->GetParamAtPoint( ptE_sfr, dUE, LINK_TOL) ; + PtrOwner pCrvA( ( vCrvSfrLoops[j]->CopyParamRange( dUS, dUE))) ; + PtrOwner pCrvB( ( vCrvSfrLoops[j]->CopyParamRange( dUE, dUS))) ; + double dLenA = INFINITO ; double dLenB = INFINITO ; + if ( ! IsNull( pCrvA) && pCrvA->IsValid()) + pCrvA->GetLength( dLenA) ; + if ( ! IsNull( pCrvB) && pCrvB->IsValid()) { + // sempre percorsa in senso opposto + pCrvB->Invert() ; + pCrvB->GetLength( dLenB) ; + } + // aggiungo la curva + if ( ! vCompoLink[nCompo + 1]->AddCurve( dLenA < dLenB ? Release( pCrvA) : Release( pCrvB), true, LINK_TOL)) return false ; } - // se la curva è esterna, allora devo seguire il bordo della regione di classificazione - else { - // recupero gli estremi del segmento di classificazione esterno alla pSfrClass - Point3d ptS_sfr ; pLineLinkProjected->GetPointD1D2( ccClass[i].dParS, ICurve::FROM_MINUS, ptS_sfr) ; - Point3d ptE_sfr ; pLineLinkProjected->GetPointD1D2( ccClass[i].dParE, ICurve::FROM_MINUS, ptE_sfr) ; - // scorro tutti i loop della regione di classificazione - for ( int j = 0 ; j < int( vCrvSfrLoops.size()) ; ++ j) { - // se i punti trovati sono sul loop corrente - if ( vCrvSfrLoops[j]->IsPointOn( ptS_sfr, 25 * EPS_SMALL) && - vCrvSfrLoops[j]->IsPointOn( ptE_sfr, 25 * EPS_SMALL)) { - // devo capire quale delle due curve (pCrvA, pCrvB) devo seguire sul bordo - double dUS ; vCrvSfrLoops[j]->GetParamAtPoint( ptS_sfr, dUS, 25 * EPS_SMALL) ; - double dUE ; vCrvSfrLoops[j]->GetParamAtPoint( ptE_sfr, dUE, 25 * EPS_SMALL) ; - PtrOwner pCrvA( ConvertCurveToComposite( vCrvSfrLoops[j]->CopyParamRange( dUS, dUE))) ; - PtrOwner pCrvB( ConvertCurveToComposite( vCrvSfrLoops[j]->CopyParamRange( dUE, dUS))) ; - // tra le due curve (pCrvA, pCrvB) scelgo quella con lunghezza minore - double dLenA = INFINITO ; double dLenB = INFINITO ; - if ( ! IsNull( pCrvA) && pCrvA->IsValid()) - pCrvA->GetLength( dLenA) ; - if ( ! IsNull( pCrvB) && pCrvB->IsValid()) { - // la pCrvB è sempre percorsa in senso opposto - pCrvB->Invert() ; - pCrvB->GetLength( dLenB) ; - } - // aggiungo la curva - if ( ! pCompoLink->AddCurve( dLenA < dLenB ? Release( pCrvA) : Release( pCrvB), true, 25 * EPS_SMALL)) - return false ; - } - } - } } - // se il link trovato non è valido, ricomincio la ricerca - if ( ! pCompoLink->IsValid()) { - nCurrRow = 0 ; // azzero il contatore delle righe - break ; // interrompo ciclo sulle righe successive di ricerca - } - - // porto il link sulla curva attuale di silhouette e lo proietto sulla pStm (sono in locale) - double dSfrDistNext = vCrvCompo[nNextRow][nNextCol_path]->GetTempParam( 0) ; - pCompoLink->Translate( Z_AX * min( dSfrDistCurr, dSfrDistNext)) ; - - #if ENABLE_ZCONST_DEBUG - int _d = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pCompoLink->Clone()) ; - m_pGeomDB->SetMaterial( _d, LIME) ; - #endif - - if ( ! CalcZConstProjectedLink( pCAvTlStm, frXY, frSurf, vtTool, dDepth, ptStartLink, ptEndLink, pCompoLink)) - return false ; - - #if ENABLE_ZCONST_DEBUG - int _e = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pCompoLink->Clone()) ; - m_pGeomDB->SetMaterial( _e, RED) ; - #endif - - // aggiungo le curve ritrovate al percorso attuale - if ( ! vCrvPath.back()->AddCurve( Release( pCompoLink), true, 1500 * EPS_SMALL) || - ! vCrvPath.back()->AddCurve( Release( vCrvCompo[nNextRow][nNextCol_path]), true, 1500 * EPS_SMALL)) - return false ; } - vCrvPath.back()->GetEndPoint( ptCurrEnd) ; // aggiorno il punto finale del percorso - vCrvPath.back()->SetTempProp( nNextRow + 1, 1) ; // salvo come seconda temp prop il piano a cui sono } - } - } - } - // se non ho percorsi, non faccio nulla - if ( vCrvPath.empty()) - return true ; - // se ho percorsi non validi, errore - for ( int i = 0 ; i < int( vCrvPath.size()) ; ++ i) { - if ( vCrvPath[i] == nullptr || ! vCrvPath[i]->IsValid()) - return false ; - } + // se il link trovato non è valido, passo al successivo + if ( ! vCompoLink[nCompo + 1]->IsValid()) + continue ; // il link rimane inizializzato e non valido ( 0 curve) - // preso come riferimento il primo percorso, ordino i successivi in base alla vicinanza - Point3d ptRef ; vCrvPath.front()->GetEndPoint( ptRef) ; - double dSqMaxDist = INFINITO ; - for ( int i = 1 ; i < int( vCrvPath.size()) ; ++ i) { - int nNextInd = i + 1 ; - for ( int j = nNextInd ; j < int( vCrvPath.size()) ; ++ j) { - Point3d ptS ; vCrvPath[j]->GetStartPoint( ptS) ; - double dCurrSqDist = SqDist( ptS, ptRef) ; - if ( dCurrSqDist < dSqMaxDist) { - dSqMaxDist = dCurrSqDist ; - nNextInd = j ; + // porto il link sul piano della curva più in alto tra le due + Point3d ptCheck ; + vIndependentCurveGroup[nPath][nCompo + 1]->GetStartPoint( ptCheck) ; + double dSfrDistNext = DistPointPlane( ptCheck, plProjection) ; + vIndependentCurveGroup[nPath][nCompo]->GetStartPoint( ptCheck) ; + double dSfrDistCurr = DistPointPlane( ptCheck, plProjection) ; + vCompoLink[nCompo + 1]->Translate( Z_AX * min( dSfrDistCurr, dSfrDistNext)) ; + // proietto tale Link sulla superficie + if ( ! CalcZConstProjectedLink( pCAvTlStm, frXY, frSurf, vtTool, dDepth, ptStartLink, ptEndLink, vCompoLink[nCompo + 1])) + return false ; } } - if ( nNextInd != i + 1) - swap( vCrvPath[i], vCrvPath[nNextInd]) ; - dSqMaxDist = INFINITO ; + + // calcolo il percorso unendo le curve di bordo con i Link ricavati + const double PATH_TOL = 1500. * EPS_SMALL ; + #if ENABLE_ZCONST_PATH_DEBUG + int nLay = GDB_ID_NULL ; + Color myCol = INVISIBLE ; + #endif + for ( int nCompo = 0 ; nCompo < int( vIndependentCurveGroup[nPath].size()) - 1 ; ++ nCompo) { + if ( IsNull( vCompoLink[nCompo]) || ! vCompoLink[nCompo]->IsValid()) { + #if ENABLE_ZCONST_PATH_DEBUG + nLay = m_pGeomDB->AddGroup( GDB_ID_NULL, nGrp, GLOB_FRM) ; + myCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; + #endif + if ( ! vCrvPath.emplace_back( CreateCurveComposite())) + return false ; + } + else { + #if ENABLE_ZCONST_PATH_DEBUG + int nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nLay, CloneCurveComposite( vCompoLink[nCompo])) ; + m_pGeomDB->SetMaterial( nId, myCol) ; + #endif + vCrvPath.back()->AddCurve( Release( vCompoLink[nCompo]), true, PATH_TOL) ; + } + #if ENABLE_ZCONST_PATH_DEBUG + int nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nLay, CloneCurveComposite( vIndependentCurveGroup[nPath][nCompo])) ; + m_pGeomDB->SetMaterial( nId, myCol) ; + #endif + vCrvPath.back()->AddCurve( Release( vIndependentCurveGroup[nPath][nCompo]), true, PATH_TOL) ; + } } // riporto i percorsi nel frame originale