//---------------------------------------------------------------------------- // EgalTech 2015-2020 //---------------------------------------------------------------------------- // File : AdjustLoops.cpp Data : 14.04.20 Versione : 2.2d2 // Contenuto : Implementazione funzione di sistemazione curve chiuse per // poter formare loop validi. // // // Modifiche : 02.11.15 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "GeoConst.h" #include "CurveComposite.h" #include "RemoveCurveDefects.h" #include "AdjustLoops.h" #include "/EgtDev/Include/EGkIntersCurves.h" #include "/EgtDev/Include/EGkIntervals.h" #include "/EgtDev/Include/EgtNumUtils.h" #include "/EgtDev/Include/EgtPointerOwner.h" #include using namespace std ; //------------------------------------------------------- static bool SortCrvCrvInfo( const IntCrvCrvInfo& aInfo1, const IntCrvCrvInfo& aInfo2) { // confronto i valori del primo punto della curva A double dU1 = aInfo1.IciA[0].dU ; double dU2 = aInfo2.IciA[0].dU ; if ( abs( dU1 - dU2) < EPS_SMALL) // se sono uguali confronto i valori del primo punto della curva B return aInfo1.IciB[0].dU > aInfo2.IciB[0].dU ; else return dU1 < dU2 ; } //---------------------------------------------------------------------------- static bool MyAdjustLoops( ICurve* pCurve, ICURVEPLIST& CrvLst) { // Pulisco lista di ritorno CrvLst.clear() ; // Acquisisco la curva PtrOwner pMyCrv( pCurve) ; // Verifico validità curva if ( IsNull( pMyCrv) || ! pMyCrv->IsValid() || ! pMyCrv->IsClosed()) return false ; // --- la verifica ed eventuale rimozione va effettuata in un piano perpendicolare al vettore estrusione --- // verifico il vettore estrusione Vector3d vtExtr ; pMyCrv->GetExtrusion( vtExtr) ; bool bNeedRef = ( ! vtExtr.IsSmall() && ! vtExtr.IsZplus()) ; // se necessario cambio il riferimento Frame3d frExtr ; if ( bNeedRef) { // calcolo il riferimento OCS con VtExtr come asse Z if ( ! frExtr.Set( ORIG, vtExtr)) return false ; // esprimo la curva in questo riferimento pMyCrv->ToLoc( frExtr) ; } // Calcolo le auto intersezioni SelfIntersCurve sintC( *pMyCrv) ; // se non ci sono auto-intersezioni che si attraversano o che si toccano (anche con sovrapposizione), non devo fare alcunché if ( sintC.GetCrossIntersCount() == 0 && sintC.GetTouchIntersCount() == 0) { // riporto la curva nel riferimento originale if ( bNeedRef) pMyCrv->ToGlob( frExtr) ; // la inserisco in lista CrvLst.push_back( Release( pMyCrv)) ; return true ; } // Determino intervallo complessivo della curva double dStart, dEnd ; if ( ! pMyCrv->GetDomain( dStart, dEnd)) return false ; Intervals inOk( EPS_PARAM) ; inOk.Set( dStart, dEnd) ; // recupero tutte le info delle autointersezioni IntCrvCrvInfo iccInfo ; ICCIVECTOR vIccInfo( sintC.GetIntersCount()) ; for ( int i = 0 ; sintC.GetIntCrvCrvInfo( i, iccInfo) ; ++ i) vIccInfo[i]= iccInfo ; // ordino il vettore sort( vIccInfo.begin(), vIccInfo.end(), SortCrvCrvInfo) ; // Tolgo le parti da eliminare int nCross = 0 ; for ( int i = 0 ; i < int( vIccInfo.size()) ; ++ i) { if ( i < int( vIccInfo.size()) - 1) { // se anche l'intersezione successiva è overlap che parte dallo stesso punto if ( vIccInfo[i].bOverlap && vIccInfo[i+1].bOverlap && abs( vIccInfo[i].IciA[0].dU - vIccInfo[i+1].IciA[0].dU) < EPS_SMALL && abs( vIccInfo[i].IciB[0].dU - vIccInfo[i+1].IciB[0].dU) < EPS_SMALL) { // elimino il tratto inOk.Subtract( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciB[0].dU) ; // salto le intersezioni successive già considerate rimuovendo questo intervallo int j = i + 2 ; while ( j < ( int)vIccInfo.size() && vIccInfo[j].IciA[0].dU > vIccInfo[i].IciA[0].dU - EPS_SMALL && vIccInfo[j].IciA[1].dU < vIccInfo[i].IciB[0].dU + EPS_SMALL) { j ++ ; } // aggiorno il contatore i = j - 1 ; continue ; } } // se con sovrapposizione if ( vIccInfo[i].bOverlap) { // se solo touch if ( ( vIccInfo[i].IciA[0].nPrevTy == vIccInfo[i].IciA[1].nNextTy || vIccInfo[i].IciA[0].nPrevTy == ICCT_SPK || vIccInfo[i].IciA[1].nNextTy == ICCT_SPK) && vIccInfo[i].IciA[0].nPrevTy != ICCT_NULL && vIccInfo[i].IciA[1].nNextTy != ICCT_NULL) { // obbligatoriamente controversi, elimino entrambi i tratti inOk.Subtract( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciA[1].dU) ; inOk.Subtract( vIccInfo[i].IciB[0].dU, vIccInfo[i].IciB[1].dU) ; } // altrimenti attraversamento else { // elimino la parte interna if ( vIccInfo[i].bCBOverEq) inOk.Subtract( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciB[0].dU) ; else inOk.Subtract( min( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciB[1].dU), max( vIccInfo[i].IciB[0].dU, vIccInfo[i].IciA[1].dU)) ; } } // altrimenti else { // se solo touch if ( vIccInfo[i].IciA[0].nPrevTy == vIccInfo[i].IciA[0].nNextTy && vIccInfo[i].IciA[0].nPrevTy != ICCT_NULL && vIccInfo[i].IciA[0].nNextTy != ICCT_NULL) { inOk.Subtract( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciA[0].dU) ; inOk.Subtract( vIccInfo[i].IciB[0].dU, vIccInfo[i].IciB[0].dU) ; } // altrimenti attraversamento else { double dParA = vIccInfo[i].IciA[0].dU ; double dParB = vIccInfo[i].IciB[0].dU ; if ( dParA > dParB) swap( dParA, dParB) ; // verifico se uno dei due intervalli dà origine ad un tratto trascurabile PtrOwner pCrv1( pMyCrv->CopyParamRange( dParA, dParB)) ; PtrOwner pCrv2( pMyCrv->CopyParamRange( dParB, dParA)) ; double dArea1 = 0, dArea2 = 0 ; if ( ! IsNull( pCrv1)) pCrv1->GetAreaXY( dArea1) ; if ( ! IsNull( pCrv2)) pCrv2->GetAreaXY( dArea2) ; if ( abs( dArea1) > 1e6 * abs( dArea2)) { // se il tratto dParB->dParA non è significativo inOk.Subtract( dStart, dParA) ; inOk.Subtract( dParB, dEnd) ; } else if ( abs( dArea2) > 1e6 * abs( dArea1)) { // se il tratto dParA->dParB non è siginificativo inOk.Subtract( dParA, dParB) ; } else { // se entrambe le regioni sono significative if ( IsEven( nCross)) inOk.Subtract( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciB[0].dU) ; else inOk.Add( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciB[0].dU) ; ++ nCross ; } } } } // Copio le parti da conservare double dParS, dParE ; bool bFound = inOk.GetFirst( dParS, dParE) ; while ( bFound) { ICurve* pCrv = pMyCrv->CopyParamRange( dParS, dParE) ; if ( pCrv != nullptr) { if ( pCrv->GetType() == CRV_COMPO) CrvLst.push_back( pCrv) ; else { CurveComposite* pCrvCo = new( std::nothrow) CurveComposite ; if ( pCrvCo != nullptr) { pCrvCo->AddCurve( pCrv) ; CrvLst.push_back( pCrvCo) ; } } } bFound = inOk.GetNext( dParS, dParE) ; } // Concateno i percorsi risultanti (senza cambiare verso e ignorando le curve già chiuse) : // primo ciclo con loop di due sole curve for ( auto iIter = CrvLst.begin() ; iIter != CrvLst.end() ;) { CurveComposite* pCrvCo = GetBasicCurveComposite( *iIter) ; // recupero punti iniziale e finale della curva Point3d ptStart ; pCrvCo->GetStartPoint( ptStart) ; Point3d ptEnd ; pCrvCo->GetEndPoint( ptEnd) ; // se aperta, cerco di concatenarla if ( ! AreSamePointEpsilon( ptStart, ptEnd, 10 * EPS_SMALL)) { // ciclo sulle curve successive per verificare se possibile concatenamento for ( auto iIter2 = next( iIter) ; iIter2 != CrvLst.end() ;) { CurveComposite* pCrvCo2 = GetBasicCurveComposite( *iIter2) ; // recupero punti iniziale e finale della curva Point3d ptStart2 ; pCrvCo2->GetStartPoint( ptStart2) ; Point3d ptEnd2 ; pCrvCo2->GetEndPoint( ptEnd2) ; // se aperta, verifico se concatenabile alla principale if ( ! AreSamePointEpsilon( ptStart2, ptEnd2, 10 * EPS_SMALL)) { if ( AreSamePointEpsilon( ptEnd, ptStart2, 10 * EPS_SMALL) && AreSamePointEpsilon( ptEnd2, ptStart, 10 * EPS_SMALL)) { pCrvCo->AddCurve( pCrvCo2, true, 10 * EPS_SMALL) ; CrvLst.erase( iIter2) ; ptEnd = ptEnd2 ; iIter2 = next( iIter) ; } else ++ iIter2 ; } else ++ iIter2 ; } } ++ iIter ; } // secondo ciclo con loop generici for ( auto iIter = CrvLst.begin() ; iIter != CrvLst.end() ;) { CurveComposite* pCrvCo = GetBasicCurveComposite( *iIter) ; // recupero punti iniziale e finale della curva Point3d ptStart ; pCrvCo->GetStartPoint( ptStart) ; Point3d ptEnd ; pCrvCo->GetEndPoint( ptEnd) ; // se aperta, cerco di concatenarla if ( ! AreSamePointEpsilon( ptStart, ptEnd, 10 * EPS_SMALL)) { // ciclo sulle curve successive per verificare se possibile concatenamento for ( auto iIter2 = next( iIter) ; iIter2 != CrvLst.end() ;) { CurveComposite* pCrvCo2 = GetBasicCurveComposite( *iIter2) ; // recupero punti iniziale e finale della curva Point3d ptStart2 ; pCrvCo2->GetStartPoint( ptStart2) ; Point3d ptEnd2 ; pCrvCo2->GetEndPoint( ptEnd2) ; // se aperta, verifico se concatenabile alla principale if ( ! AreSamePointEpsilon( ptStart2, ptEnd2, 10 * EPS_SMALL)) { if ( AreSamePointEpsilon( ptEnd, ptStart2, 100 * EPS_SMALL)) { pCrvCo->AddCurve( pCrvCo2, true, 100 * EPS_SMALL) ; CrvLst.erase( iIter2) ; ptEnd = ptEnd2 ; iIter2 = next( iIter) ; } else if ( AreSamePointEpsilon( ptEnd2, ptStart, 100 * EPS_SMALL)) { pCrvCo->AddCurve( pCrvCo2, false, 100 * EPS_SMALL) ; CrvLst.erase( iIter2) ; ptStart = ptStart2 ; iIter2 = next( iIter) ; } else ++ iIter2 ; } else ++ iIter2 ; } } ++ iIter ; } // elimino le curve troppo piccole for ( auto iIter = CrvLst.begin() ; iIter != CrvLst.end() ;) { CurveComposite* pCrvCo = GetBasicCurveComposite( *iIter) ; BBox3d b3CrvCo ; double dDiam ; if ( ! pCrvCo->GetLocalBBox( b3CrvCo) || b3CrvCo.IsEmpty() || ! b3CrvCo.GetDiameter( dDiam) || dDiam < 20 * EPS_SMALL) { delete pCrvCo ; pCrvCo = nullptr ; iIter = CrvLst.erase( iIter) ; } else { ++ iIter ; } } // riporto le curve nel riferimento originale if ( bNeedRef) { for ( auto pCrv : CrvLst) pCrv->ToGlob( frExtr) ; } // assegno il versore estrusione originale for ( auto pCrv : CrvLst) pCrv->SetExtrusion( vtExtr) ; return true ; } //---------------------------------------------------------------------------- bool AdjustLoops( ICurve* pCurve, ICURVEPLIST& CrvLst, bool bNeedSameProp) { // elimino eventuali sovrapposizioni e accostamenti if ( ! MyAdjustLoops( pCurve, CrvLst)) return false ; // unisco eventuali tratti allineati ed elimino eventuali spikes for ( auto pCrv : CrvLst) { // se curva composita CurveComposite* pCrvCo = GetBasicCurveComposite( pCrv) ; if ( pCrvCo != nullptr) { // elimino eventuali Spikes e Small Z pCrvCo->RemoveSmallDefects( 2 * LIN_TOL_MIN, ANG_TOL_STD_DEG, true) ; // unisco eventuali tratti allineati pCrvCo->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG, true, bNeedSameProp) ; // richiudo i loop per sicurezza pCrvCo->Close() ; } } return true ; }