Files
EgtGeomKernel/SurfFlatRegionBooleans.cpp
T
Dario Sassi b2db80edc9 EgtGeomKernel 1.6h4 :
- aggiunto metodo GetCrossOrOverlapIntersCount a SelfIntersCurve
- aggiunti metodi GetCrossIntersCount e GetCrossOrOverlapIntersCount a IntersCurveCurve
- allentati controlli sui loop delle regioni, ora possono toccarsi ma non attraversarsi in punti isolati
- aggiunte funzioni per operazioni booleante tra regioni Add, Subtract e Intersect.
2015-08-22 13:48:19 +00:00

427 lines
15 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2015-2015
//----------------------------------------------------------------------------
// File : SurfFlatRegion.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& SfrOther = dynamic_cast<const SurfFlatRegion&>( Other) ;
if ( &SfrOther == nullptr)
return false ;
// verifico che le due regioni giacciano nello stesso piano
if ( ! AreSameVectorApprox( m_frF.VersZ(), SfrOther.m_frF.VersZ()) ||
fabs( ( m_frF.Orig() - SfrOther.m_frF.Orig()) * m_frF.VersZ()) > EPS_SMALL)
return false ;
// gestione eventuale copia dell'altra per trasformare il riferimento
const SurfFlatRegion* pOther = &SfrOther ;
PtrOwner<SurfFlatRegion> 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) ;
// assegno il nuovo riferimento
pCopyOth->m_frF = m_frF ;
// aggiorno puntatore della copia in lavoro
pOther = Get( 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( vpLoop) ;
return false ;
}
// creo una nuova regione a partire da questi loop
PtrOwner<SurfFlatRegion> pSfr( MyNewSurfFromLoops( vpLoop)) ;
if ( IsNull( pSfr))
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() ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfFlatRegion::Subtract( const ISurfFlatRegion& Other)
{
// converto l'altra regione nell'oggetto base
const SurfFlatRegion& SfrOther = dynamic_cast<const SurfFlatRegion&>( Other) ;
if ( &SfrOther == nullptr)
return false ;
// verifico che le due regioni giacciano nello stesso piano
if ( ! AreSameVectorApprox( m_frF.VersZ(), SfrOther.m_frF.VersZ()) ||
fabs( ( m_frF.Orig() - SfrOther.m_frF.Orig()) * m_frF.VersZ()) > EPS_SMALL)
return false ;
// gestione eventuale copia dell'altra per trasformare il riferimento
const SurfFlatRegion* pOther = &SfrOther ;
PtrOwner<SurfFlatRegion> 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) ;
// assegno il nuovo riferimento
pCopyOth->m_frF = m_frF ;
// aggiorno puntatore della copia in lavoro
pOther = Get( 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( vpLoop) ;
return false ;
}
// creo una nuova regione a partire da questi loop
PtrOwner<SurfFlatRegion> pSfr( MyNewSurfFromLoops( vpLoop)) ;
if ( IsNull( pSfr))
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() ;
return true ;
}
//----------------------------------------------------------------------------
bool
SurfFlatRegion::Intersect( const ISurfFlatRegion& Other)
{
// converto l'altra regione nell'oggetto base
const SurfFlatRegion& SfrOther = dynamic_cast<const SurfFlatRegion&>( Other) ;
if ( &SfrOther == nullptr)
return false ;
// verifico che le due regioni giacciano nello stesso piano
if ( ! AreSameVectorApprox( m_frF.VersZ(), SfrOther.m_frF.VersZ()) ||
fabs( ( m_frF.Orig() - SfrOther.m_frF.Orig()) * m_frF.VersZ()) > EPS_SMALL)
return false ;
// gestione eventuale copia dell'altra per trasformare il riferimento
const SurfFlatRegion* pOther = &SfrOther ;
PtrOwner<SurfFlatRegion> 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) ;
// assegno il nuovo riferimento
pCopyOth->m_frF = m_frF ;
// aggiorno puntatore della copia in lavoro
pOther = Get( 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( vpLoop) ;
return false ;
}
// creo una nuova regione a partire da questi loop
PtrOwner<SurfFlatRegion> pSfr( MyNewSurfFromLoops( vpLoop)) ;
if ( IsNull( pSfr))
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() ;
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, ccClass))
return false ;
// creo intervalli validi, tenendo classificazioni ricevute
Intervals inOk1, inOk2 ;
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
ChainCurves chainC ;
chainC.Init( false, 5 * EPS_SMALL, 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<CurveComposite> 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
if ( ! pCrvCompo->AddCurve( vpCurve[i-1], true, 5 * EPS_SMALL))
return false ;
vpCurve[i-1] = nullptr ;
}
// aggiorno il nuovo punto vicino
if ( pCrvCompo->GetCurveCount() > 0)
pCrvCompo->GetEndPoint( ptNearStart) ;
// 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 std::pair<int,double> INDAREA ; // coppia indice, area
typedef std::vector<INDAREA> INDAREAVECTOR ; // vettore di coppie indice, area
INDAREAVECTOR vArea ;
vArea.reserve( vpLoop.size()) ;
for ( int i = 0 ; i < int( vpLoop.size()) ; ++ i) {
double dArea ;
if ( ! vpLoop[i]->GetAreaXY( dArea))
return nullptr ;
vArea.emplace_back( i, dArea) ;
}
// 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<SurfFlatRegion> 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 ( fabs( 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, ccClass) ||
ccClass.empty() || ccClass[0].nClass != CRVC_IN)
continue ;
// lo inserisco
if ( pSfr->MyAddIntLoop( vpLoop[l])) {
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 ;
}