Files
EgtGeomKernel/SurfFlatRegionBooleans.cpp
T
Dario Sassi b709776f5f EgtGeomKernel :
- piccole mdofiche poco più che estetiche.
2025-01-20 08:30:39 +01:00

498 lines
17 KiB
C++

//----------------------------------------------------------------------------
// 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<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) ;
// 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<SurfFlatRegion> 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<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) ;
// 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<SurfFlatRegion> pSfr ;
if ( vpLoop.empty())
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<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) ;
// 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<SurfFlatRegion> pSfr ;
if ( vpLoop.empty())
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<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
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<int,double> INDAREA ; // coppia indice, area
typedef vector<INDAREA> 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<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 ( 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 ;
}