//---------------------------------------------------------------------------- // EgalTech 2015-2015 //---------------------------------------------------------------------------- // File : SurfFlatRegionBooleans.cpp Data : 18.08.15 Versione : 1.6h4 // Contenuto : Implementazione delle funzioni booleane per SurfFlatRegion. // // // // Modifiche : 18.08.15 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "SurfFlatRegion.h" #include "CurveComposite.h" #include "GeoConst.h" #include "/EgtDev/Include/EGkChainCurves.h" #include "/EgtDev/Include/EGkIntervals.h" #include "/EgtDev/Include/EgtPointerOwner.h" using namespace std ; //---------------------------------------------------------------------------- bool SurfFlatRegion::Add( const ISurfFlatRegion& Other) { // converto l'altra regione nell'oggetto base const SurfFlatRegion* pSfrOther = GetBasicSurfFlatRegion( &Other) ; if ( pSfrOther == nullptr) return false ; const SurfFlatRegion& SfrOther = *pSfrOther ; // verifico che le due regioni giacciano in piani paralleli if ( ! AreSameVectorApprox( m_frF.VersZ(), SfrOther.m_frF.VersZ())) return false ; // gestione eventuale copia dell'altra per trasformare il riferimento const SurfFlatRegion* pOther = &SfrOther ; PtrOwner pCopyOth ; // se riferimenti intrinseci diversi, la trasformo if ( ! AreSameFrame( m_frF, SfrOther.m_frF)) { // creo la copia pCopyOth.Set( SfrOther.Clone()) ; if ( IsNull( pCopyOth)) return false ; // calcolo riferimento di traformazione Frame3d frTransf = pCopyOth->m_frF ; frTransf.ToLoc( m_frF) ; // trasformo le curve for ( auto& pLoop : pCopyOth->m_vpLoop) pLoop->ToGlob( frTransf) ; // calcolo vettore di movimento per portarle nel piano XY (Z=0) Vector3d vtMove( 0, 0, - frTransf.Orig().z) ; if ( ! vtMove.IsSmall()) { for ( auto& pLoop : pCopyOth->m_vpLoop) pLoop->Translate( vtMove) ; } // assegno il nuovo riferimento pCopyOth->m_frF = m_frF ; // aggiorno puntatore della copia in lavoro pOther = pCopyOth ; } // vettore con curve utili da entrambe le regioni PCRV_DEQUE vpCurve ; if ( ! MySelectCurves( m_vpLoop, *pOther, CRVC_OUT, false, CRVC_ON_P, false, vpCurve) || ! MySelectCurves( pOther->m_vpLoop, *this, CRVC_OUT, false, CRVC_NULL, false, vpCurve)) { MyTestAndDelete( vpCurve) ; return false ; } // vettore con curve concatenate che sono i contorni del risultato PCRV_DEQUE vpLoop ; // concateno e verifico di aver usato tutte le curve if ( ! MyChainCurves( vpCurve, vpLoop) || ! MyTestAndDelete( vpCurve)) { MyTestAndDelete( vpCurve) ; MyTestAndDelete( vpLoop) ; return false ; } // creo una nuova regione a partire da questi loop PtrOwner pSfr( MyNewSurfFromLoops( vpLoop)) ; if ( IsNull( pSfr)) { MyTestAndDelete( vpCurve) ; MyTestAndDelete( vpLoop) ; return false ; } // pulisco la superficie corrente for ( auto& pLoop : m_vpLoop) delete pLoop ; m_vpLoop.clear() ; // sposto i dati della nuova superficie in quella corrente m_vExtInd = pSfr->m_vExtInd ; m_vpLoop = pSfr->m_vpLoop ; for ( auto& pLoop : pSfr->m_vpLoop) pLoop = nullptr ; m_nStatus = pSfr->m_nStatus ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // imposto ricalcolo Voronoi ResetVoronoiObject() ; return true ; } //---------------------------------------------------------------------------- bool SurfFlatRegion::Subtract( const ISurfFlatRegion& Other) { // converto l'altra regione nell'oggetto base const SurfFlatRegion* pSfrOther = GetBasicSurfFlatRegion( &Other) ; if ( pSfrOther == nullptr) return false ; const SurfFlatRegion& SfrOther = *pSfrOther ; // verifico che le due regioni giacciano in piani paralleli if ( ! AreSameVectorApprox( m_frF.VersZ(), SfrOther.m_frF.VersZ())) return false ; // gestione eventuale copia dell'altra per trasformare il riferimento const SurfFlatRegion* pOther = &SfrOther ; PtrOwner pCopyOth ; // se riferimenti intrinseci diversi, la trasformo if ( ! AreSameFrame( m_frF, SfrOther.m_frF)) { // creo la copia pCopyOth.Set( SfrOther.Clone()) ; if ( IsNull( pCopyOth)) return false ; // calcolo riferimento di traformazione Frame3d frTransf = pCopyOth->m_frF ; frTransf.ToLoc( m_frF) ; // trasformo le curve for ( auto& pLoop : pCopyOth->m_vpLoop) pLoop->ToGlob( frTransf) ; // calcolo vettore di movimento per portarle nel piano XY (Z=0) Vector3d vtMove( 0, 0, - frTransf.Orig().z) ; if ( ! vtMove.IsSmall()) { for ( auto& pLoop : pCopyOth->m_vpLoop) pLoop->Translate( vtMove) ; } // assegno il nuovo riferimento pCopyOth->m_frF = m_frF ; // aggiorno puntatore della copia in lavoro pOther = pCopyOth ; } // vettore con curve utili da entrambe le regioni PCRV_DEQUE vpCurve ; if ( ! MySelectCurves( m_vpLoop, *pOther, CRVC_OUT, false, CRVC_ON_M, false, vpCurve) || ! MySelectCurves( pOther->m_vpLoop, *this, CRVC_IN, true, CRVC_NULL, false, vpCurve)) { MyTestAndDelete( vpCurve) ; return false ; } // vettore con curve concatenate che sono i contorni del risultato PCRV_DEQUE vpLoop ; // concateno e verifico di aver usato tutte le curve if ( ! MyChainCurves( vpCurve, vpLoop) || ! MyTestAndDelete( vpCurve)) { MyTestAndDelete( vpCurve) ; MyTestAndDelete( vpLoop) ; return false ; } // creo una nuova regione a partire da questi loop PtrOwner pSfr ; if ( vpLoop.size() == 0) pSfr.Set( new( nothrow) SurfFlatRegion) ; else pSfr.Set( MyNewSurfFromLoops( vpLoop)) ; if ( IsNull( pSfr)) { MyTestAndDelete( vpCurve) ; MyTestAndDelete( vpLoop) ; return false ; } // pulisco la superficie corrente for ( auto& pLoop : m_vpLoop) delete pLoop ; m_vpLoop.clear() ; // sposto i dati della nuova superficie in quella corrente m_vExtInd = pSfr->m_vExtInd ; m_vpLoop = pSfr->m_vpLoop ; for ( auto& pLoop : pSfr->m_vpLoop) pLoop = nullptr ; m_nStatus = pSfr->m_nStatus ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // imposto ricalcolo Voronoi ResetVoronoiObject() ; return true ; } //---------------------------------------------------------------------------- bool SurfFlatRegion::Intersect( const ISurfFlatRegion& Other) { // converto l'altra regione nell'oggetto base const SurfFlatRegion* pSfrOther = GetBasicSurfFlatRegion( &Other) ; if ( pSfrOther == nullptr) return false ; const SurfFlatRegion& SfrOther = *pSfrOther ; // verifico che le due regioni giacciano in piani paralleli if ( ! AreSameVectorApprox( m_frF.VersZ(), SfrOther.m_frF.VersZ())) return false ; // gestione eventuale copia dell'altra per trasformare il riferimento const SurfFlatRegion* pOther = &SfrOther ; PtrOwner pCopyOth ; // se riferimenti intrinseci diversi, la trasformo if ( ! AreSameFrame( m_frF, SfrOther.m_frF)) { // creo la copia pCopyOth.Set( SfrOther.Clone()) ; if ( IsNull( pCopyOth)) return false ; // calcolo riferimento di traformazione Frame3d frTransf = pCopyOth->m_frF ; frTransf.ToLoc( m_frF) ; // trasformo le curve for ( auto& pLoop : pCopyOth->m_vpLoop) pLoop->ToGlob( frTransf) ; // calcolo vettore di movimento per portarle nel piano XY (Z=0) Vector3d vtMove( 0, 0, - frTransf.Orig().z) ; if ( ! vtMove.IsSmall()) { for ( auto& pLoop : pCopyOth->m_vpLoop) pLoop->Translate( vtMove) ; } // assegno il nuovo riferimento pCopyOth->m_frF = m_frF ; // aggiorno puntatore della copia in lavoro pOther = pCopyOth ; } // vettore con curve utili da entrambe le regioni PCRV_DEQUE vpCurve ; if ( ! MySelectCurves( m_vpLoop, *pOther, CRVC_IN, false, CRVC_ON_P, false, vpCurve) || ! MySelectCurves( pOther->m_vpLoop, *this, CRVC_IN, false, CRVC_NULL, false, vpCurve)) { MyTestAndDelete( vpCurve) ; return false ; } // vettore con curve concatenate che sono i contorni del risultato PCRV_DEQUE vpLoop ; // concateno e verifico di aver usato tutte le curve if ( ! MyChainCurves( vpCurve, vpLoop) || ! MyTestAndDelete( vpCurve)) { MyTestAndDelete( vpCurve) ; MyTestAndDelete( vpLoop) ; return false ; } // creo una nuova regione a partire da questi loop PtrOwner pSfr ; if ( vpLoop.size() == 0) pSfr.Set( new( nothrow) SurfFlatRegion) ; else pSfr.Set( MyNewSurfFromLoops( vpLoop)) ; if ( IsNull( pSfr)) { MyTestAndDelete( vpCurve) ; MyTestAndDelete( vpLoop) ; return false ; } // pulisco la superficie corrente for ( auto& pLoop : m_vpLoop) delete pLoop ; m_vpLoop.clear() ; // sposto i dati della nuova superficie in quella corrente m_vExtInd = pSfr->m_vExtInd ; m_vpLoop = pSfr->m_vpLoop ; for ( auto& pLoop : pSfr->m_vpLoop) pLoop = nullptr ; m_nStatus = pSfr->m_nStatus ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // imposto ricalcolo Voronoi ResetVoronoiObject() ; return true ; } //---------------------------------------------------------------------------- bool SurfFlatRegion::MySelectCurves( const PCRV_DEQUE& vpLoop, const SurfFlatRegion& Other, int nType1, bool bInvert1, int nType2, bool bInvert2, PCRV_DEQUE& vpCurve) { // classifico le curve rispetto alla regione altra e copio le parti utili for ( auto& pLoop : vpLoop) { // eseguo classificazione CRVCVECTOR ccClass ; if ( ! Other.MyGetCurveClassification( *pLoop, EPS_SMALL, ccClass)) return false ; // creo intervalli validi, tenendo classificazioni ricevute Intervals inOk1( EPS_PARAM), inOk2( EPS_PARAM) ; for ( auto& ccOne : ccClass) { if ( nType1 != CRVC_NULL && ccOne.nClass == nType1) inOk1.Add( ccOne.dParE, ccOne.dParS) ; else if ( nType2 != CRVC_NULL && ccOne.nClass == nType2) inOk2.Add( ccOne.dParE, ccOne.dParS) ; } // copio queste parti double dParS, dParE ; bool bFound = inOk1.GetFirst( dParS, dParE) ; while ( bFound) { ICurve* pCrv = pLoop->CopyParamRange( dParS, dParE) ; if ( pCrv != nullptr) { if ( bInvert1) pCrv->Invert() ; vpCurve.push_back( pCrv) ; } bFound = inOk1.GetNext( dParS, dParE) ; } bFound = inOk2.GetFirst( dParS, dParE) ; while ( bFound) { ICurve* pCrv = pLoop->CopyParamRange( dParS, dParE) ; if ( pCrv != nullptr) { if ( bInvert2) pCrv->Invert() ; vpCurve.push_back( pCrv) ; } bFound = inOk2.GetNext( dParS, dParE) ; } } return true ; } //---------------------------------------------------------------------------- bool SurfFlatRegion::MyChainCurves( PCRV_DEQUE& vpCurve, PCRV_DEQUE& vpLoop) { // concateno le curve double dToler = 5 * EPS_SMALL ; ChainCurves chainC ; chainC.Init( false, dToler, int( vpCurve.size())) ; for ( int i = 0 ; i < int( vpCurve.size()) ; ++i) { // recupero i dati della curva necessari al concatenamento e li assegno Point3d ptStart, ptEnd ; Vector3d vtStart, vtEnd ; if ( ! vpCurve[i]->GetStartPoint( ptStart) || ! vpCurve[i]->GetStartDir( vtStart) || ! vpCurve[i]->GetEndPoint( ptEnd) || ! vpCurve[i]->GetEndDir( vtEnd)) return false ; if ( ! chainC.AddCurve( i + 1, ptStart, vtStart, ptEnd, vtEnd)) return false ; } // recupero i percorsi concatenati INTVECTOR vIds ; Point3d ptNearStart ; while ( chainC.GetChainFromNear( ptNearStart, false, vIds)) { // creo una curva composita PtrOwner pCrvCompo( CreateBasicCurveComposite()) ; if ( IsNull( pCrvCompo)) return false ; // recupero le curve e le inserisco nella nuova curva composita for ( auto i : vIds) { // la aggiungo alla curva composta bool bOk = pCrvCompo->AddCurve( vpCurve[i-1], true, dToler) ; vpCurve[i-1] = nullptr ; if ( ! bOk) return false ; } // pulisco pCrvCompo->RemoveSmallParts( 2 * EPS_SMALL, EPS_ANG_SMALL) ; // aggiorno il nuovo punto vicino if ( pCrvCompo->GetCurveCount() > 0) pCrvCompo->GetEndPoint( ptNearStart) ; // se lunghezza curva inferiore a 2 volte la tolleranza, la salto double dCrvLen ; if ( ! pCrvCompo->GetLength( dCrvLen) || dCrvLen < 2. * dToler) continue ; // se curva chiusa entro 3 volte la tolleranza ma considerata aperta, la chiudo bene Point3d ptStart, ptEnd ; if ( pCrvCompo->GetStartPoint( ptStart) && pCrvCompo->GetEndPoint( ptEnd) && AreSamePointEpsilon( ptStart, ptEnd, 3 * dToler) && ! AreSamePointApprox( ptStart, ptEnd)) { // porto i punti iniziale e finale a coincidere esattamente Point3d ptNew = Media( ptStart, ptEnd) ; pCrvCompo->ModifyStart( ptNew) ; pCrvCompo->ModifyEnd( ptNew) ; } // compatto la nuova curva pCrvCompo->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG) ; // inserisco la curva composita nel gruppo destinazione if ( pCrvCompo->GetCurveCount() > 0) { vpLoop.push_back( ::Release( pCrvCompo)) ; } } return true ; } //---------------------------------------------------------------------------- SurfFlatRegion* SurfFlatRegion::MyNewSurfFromLoops( PCRV_DEQUE& vpLoop) { // verifico che le curve siano chiuse e ne determino l'area typedef pair INDAREA ; // coppia indice, area typedef vector INDAREAVECTOR ; // vettore di coppie indice, area INDAREAVECTOR vArea ; vArea.reserve( vpLoop.size()) ; for ( int i = 0 ; i < int( vpLoop.size()) ;) { double dArea ; if ( ! vpLoop[i]->GetAreaXY( dArea)) return nullptr ; if ( abs( dArea) > SQ_EPS_SMALL) { vArea.emplace_back( i, dArea) ; ++ i ; } else { delete vpLoop[i] ; vpLoop.erase( vpLoop.begin() + i) ; } } // ordino in senso decrescente sull'area sort( vArea.begin(), vArea.end(), []( const INDAREA& a, const INDAREA& b) { return ( a.second > b.second) ; }) ; // determino quanti sono i nuovi cicli esterni (area positiva) int nLoopCount = int( vArea.size()) ; int nExtCount = 0 ; for ( int i = 0 ; i < nLoopCount ; ++ i) { if ( vArea[i].second > 0) ++ nExtCount ; } // creo una nuova regione PtrOwner pSfr( CreateBasicSurfFlatRegion()) ; // ciclo sui loop esterni, all'indietro dal pił piccolo al pił grande for ( int i = nExtCount - 1 ; i >= 0 ; -- i) { // inserisco il nuovo esterno int j = vArea[i].first ; ICurve* pExtLoop = vpLoop[j] ; double dExtArea = vArea[i].second ; if ( pSfr->MyAddExtLoop( vpLoop[j])) { vpLoop[j] = nullptr ; vArea[i].first = - 1 ; } else continue ; // provo ad inserire i loop interni for ( int k = nExtCount ; k < nLoopCount ; ++ k) { // verifico non sia gią stato inserito if ( vArea[k].first < 0) continue ; // salto gli interni con area maggiore dell'esterno corrente if ( abs( vArea[k].second) > dExtArea) continue ; int l = vArea[k].first ; // verifico che sia interno all'esterno corrente CRVCVECTOR ccClass ; IntersCurveCurve ccInt( *vpLoop[l], *pExtLoop) ; if ( ccInt.GetCrossOrOverlapIntersCount() > 0 || ! ccInt.GetCurveClassification( 0, EPS_SMALL, ccClass) || ccClass.empty() || ccClass[0].nClass != CRVC_IN) continue ; // lo inserisco if ( pSfr->MyAddIntLoop( vpLoop[l], -1)) { vpLoop[l] = nullptr ; vArea[k].first = - 1 ; } } } // se non valida, errore if ( ! pSfr->IsValid()) return nullptr ; // verifico non siano rimasti loop inutilizzati if ( ! MyTestAndDelete( vpLoop)) return nullptr ; // tutto bene, ritorno la superficie appena creata return Release( pSfr) ; } //---------------------------------------------------------------------------- bool SurfFlatRegion::MyTestAndDelete( PCRV_DEQUE& vpCrv) { bool bOk = true ; for ( auto& pCrv : vpCrv) { if ( pCrv != nullptr) { delete pCrv ; pCrv = nullptr ; bOk = false ; } } return bOk ; }