4362 lines
186 KiB
C++
4362 lines
186 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2023
|
|
//----------------------------------------------------------------------------
|
|
// File : Tree.cpp Data : 21.04.23 Versione :
|
|
// Contenuto : Implementazione della classe Tree.
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 21.04.23 DB Creazione modulo.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "Tree.h"
|
|
#include "SurfBezier.h"
|
|
#include "GeoConst.h"
|
|
#include "CurveLine.h"
|
|
#include "CurveComposite.h"
|
|
#include "SurfFlatRegion.h"
|
|
#include "IntersLineLine.h"
|
|
#include "AdjustLoops.h"
|
|
#include "/EgtDev/Include/EGkDistLineLine.h"
|
|
#include "/EgtDev/Include/EGkPolyLine.h"
|
|
#include "/EgtDev/Include/EGkDistPointCurve.h"
|
|
#include "/EgtDev/Include/EGkCurve.h"
|
|
#include "DistPointCrvComposite.h"
|
|
#include "/EgtDev/Include/EGkSfrCreate.h"
|
|
#include <algorithm>
|
|
#include <numeric>
|
|
|
|
using namespace std ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
Tree::Tree( void)
|
|
: m_pSrfBz( nullptr), m_bTrimmed( false), m_bBilinear( false), m_bMulti( false), m_bClosedU( false), m_bClosedV( false), m_vbPole( { false, false, false, false}),
|
|
m_bSplitPatches( true), m_bTestMode( false)
|
|
{
|
|
Point3d ptBl( 0, 0), ptTr ( 1 * SBZ_TREG_COEFF, 1 * SBZ_TREG_COEFF) ;
|
|
Cell cRoot( ptBl, ptTr) ;
|
|
m_mTree.insert( pair< int, Cell>( -1, cRoot)) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Tree::~Tree( void)
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Tree::Tree( const Point3d ptBl, const Point3d ptTr)
|
|
: m_pSrfBz( nullptr), m_bTrimmed( false), m_bBilinear( false), m_bMulti( false), m_bClosedU( false), m_bClosedV( false), m_vbPole( { false, false, false, false}),
|
|
m_bSplitPatches( true), m_bTestMode( false)
|
|
{
|
|
Cell cRoot( ptBl, ptTr) ;
|
|
m_mTree.insert( pair< int, Cell>( -1, cRoot)) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::VerifyLoopOrientation( ICURVEPLIST& vpCrv, BOOLVECTOR& vbOrientation) const
|
|
{
|
|
// verifico che il verso dei loop sia corretto controllando le relazioni tra loop
|
|
vpCrv.sort( []( ICurve* a, ICurve* b) { double AreaA, AreaB ; a->GetAreaXY( AreaA) ; b->GetAreaXY( AreaB) ; return abs(AreaA) > abs(AreaB) ;}) ;
|
|
auto it = vpCrv.begin() ;
|
|
for ( ++it ; it != vpCrv.end() ; ++it) {
|
|
bool bIdentified = false ;
|
|
for ( auto k = it ; k != vpCrv.begin() ;) {
|
|
--k ;
|
|
IntersCurveCurve icc( **k, **it) ;
|
|
int nRes = icc.GetRegionCurveClassification() ;
|
|
if ( nRes == CCREGC_IN1) {
|
|
bIdentified = true ;
|
|
vbOrientation.push_back( ! vbOrientation[ distance( vpCrv.begin(), k)]) ; // l'orientazione deve essere opposta alla prima curva che contiene la corrente
|
|
break ;
|
|
}
|
|
}
|
|
if ( ! bIdentified)
|
|
vbOrientation.push_back( vbOrientation[0]) ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::AdjustLoop( PolyLine& pl, POLYLINEVECTOR& vPl, BOOLVECTOR& vbOrientation) const
|
|
{
|
|
PtrOwner<ICurveComposite> pCC( CreateCurveComposite()) ;
|
|
pCC->FromPolyLine( pl) ;
|
|
ICURVEPLIST vCrv ;
|
|
AdjustLoops( Release( pCC), vCrv, false) ;
|
|
|
|
if ( vCrv.size() > 1)
|
|
VerifyLoopOrientation( vCrv, vbOrientation) ;
|
|
for ( auto itCrv = vCrv.begin() ; itCrv != vCrv.end() ; ++itCrv) {
|
|
PolyLine plApprox ; (*itCrv)->ApproxWithLines( 0.01, 15, 0, plApprox) ;
|
|
vPl.push_back( plApprox) ;
|
|
delete (*itCrv) ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::GetPoint( double dU, double dV, Point3d& pt) const
|
|
{
|
|
pair<int64_t, int64_t> key (static_cast<int64_t>(dU * pow(2,15)), static_cast<int64_t>(dV * pow(2,15))) ;
|
|
bool bOk = true ;
|
|
if ( m_mPt3d.find( key) == m_mPt3d.end()) {
|
|
bOk = bOk && m_pSrfBz->GetPoint( dU / SBZ_TREG_COEFF, dV / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt) ;
|
|
m_mPt3d[key] = pt ;
|
|
}
|
|
pt = m_mPt3d[key] ;
|
|
return bOk ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::SavePoint( double dU, double dV, Point3d& pt)
|
|
{
|
|
pair<int64_t, int64_t> key (static_cast<int64_t>(dU * pow(2,15)), static_cast<int64_t>(dV * pow(2,15))) ;
|
|
if ( m_mPt3d.find( key) == m_mPt3d.end())
|
|
m_mPt3d[key] = pt ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::SetSurf( const SurfBezier* pSrfBz, bool bSplitPatches, const Point3d& ptMin, const Point3d& ptMax)
|
|
{
|
|
if ( pSrfBz == nullptr || ! pSrfBz->IsValid())
|
|
return false ;
|
|
// pulisco i vettori membri
|
|
m_mTree.clear() ;
|
|
m_vnLeaves.clear() ;
|
|
m_vnParents.clear() ;
|
|
m_mPt3d.clear() ;
|
|
m_mChunk.clear() ;
|
|
m_vPlApprox.clear() ;
|
|
m_vCCLoop2D.clear() ;
|
|
|
|
m_pSrfBz = pSrfBz ;
|
|
m_bSplitPatches = bSplitPatches ;
|
|
// le coordinate delle celle sono nello spazio parametrico
|
|
int nDegU, nDegV, nSpanU, nSpanV ;
|
|
bool bIsRat, bTrimmed ;
|
|
m_pSrfBz->GetInfo( nDegU, nDegV, nSpanU, nSpanV, bIsRat, bTrimmed) ;
|
|
m_bTrimmed = bTrimmed ;
|
|
m_nDegU = nDegU ;
|
|
m_nDegV = nDegV ;
|
|
m_nSpanU = nSpanU ;
|
|
m_nSpanV = nSpanV ;
|
|
if ( nDegU == 1 && nDegV == 1)
|
|
m_bBilinear = true ;
|
|
if ( nSpanU * nSpanV != 1)
|
|
m_bMulti = true ;
|
|
// creo la cella Root
|
|
Point3d ptTop( nSpanU * SBZ_TREG_COEFF, nSpanV * SBZ_TREG_COEFF) ;
|
|
bool bLimited = false ;
|
|
if ( ! AreSamePointXYExact( ptMax,ORIG)) {
|
|
if ( ! AreSamePointXYExact( ptMax,ptTop))
|
|
ptTop = ptMax ;
|
|
bLimited = true ;
|
|
}
|
|
Cell cRoot( ptMin, ptTop) ;
|
|
m_mTree.insert( pair< int, Cell>( -1, cRoot)) ;
|
|
// recupero i loop di trim e li divido per chunk
|
|
if ( m_bTrimmed) {
|
|
int nLoop = 0 ;
|
|
// recupero la superficie di trim per avere accesso diretto ai loop e mantenendo le informazioni sui chunk
|
|
PtrOwner<SurfFlatRegion> pTrimReg( m_pSrfBz->GetTrimRegion()->Clone()) ;
|
|
double dLinTol = 0.01 ; // questo è riferito allo spazio parametrico
|
|
double dAngTolDeg = 5 ;
|
|
for ( int i = 0 ; i < pTrimReg->GetChunkCount() ; ++ i) {
|
|
PtrOwner<SurfFlatRegion> pChunk( pTrimReg->CloneChunk( i)) ;
|
|
for ( int j = 0 ; j < pChunk->GetLoopCount( 0) ; ++ j) {
|
|
// i chunk della falt region sono ancora flat region composte da 1 chunk
|
|
// rimuovo i difetti dei loop prima di salvarli
|
|
PtrOwner<ICurveComposite> pLoop( GetBasicCurveComposite( pChunk->GetLoop( 0, j))) ;
|
|
pLoop->MergeCurves( dLinTol, dAngTolDeg) ;
|
|
pLoop->RemoveSmallDefects( dLinTol, dAngTolDeg, true) ;
|
|
pLoop->RemoveSmallParts( dLinTol, dAngTolDeg) ;
|
|
// approssimo i loop di trim con delle spezzate
|
|
PolyLine plApprox ;
|
|
int nType = 0 ;
|
|
pLoop->ApproxWithLines( dLinTol,dAngTolDeg, nType, plApprox) ;
|
|
// calcolo se il loop è CCW o CW
|
|
double dArea ;
|
|
Plane3d plExtPlane ;
|
|
bool bCCW ;
|
|
plApprox.IsClosedAndFlat( plExtPlane, dArea, 50 * EPS_SMALL) ;
|
|
if ( plExtPlane.GetVersN().z > 0)
|
|
bCCW = true ;
|
|
else
|
|
bCCW = false ;
|
|
|
|
POLYLINEVECTOR vPlAdjusted ;
|
|
BOOLVECTOR vbOrientation ;
|
|
vbOrientation.push_back( bCCW) ;
|
|
AdjustLoop( plApprox, vPlAdjusted, vbOrientation) ;
|
|
|
|
nLoop = int ( m_vPlApprox.size()) ;
|
|
for ( int k = 0 ; k < int( vPlAdjusted.size()) ; ++k )
|
|
m_vPlApprox.push_back( tuple<PolyLine,bool>(vPlAdjusted[k], vbOrientation[k])) ;
|
|
// aggiorno la mappa del chunk di appartenenza per tutti i loop aggiunti // do per scontato che siano tutti dello stesso chunk anche se li ho separati
|
|
for ( int k = nLoop ; k < int( m_vPlApprox.size()); ++k)
|
|
m_mChunk[k] = i ;
|
|
}
|
|
}
|
|
}
|
|
// salvo i vertici 3d della cella root
|
|
Point3d ptP00, ptP10, ptP11, ptP01 ;
|
|
bool bOk = false ;
|
|
if ( ! bLimited) {
|
|
ptP00 = m_pSrfBz->GetControlPoint( 0, &bOk) ;
|
|
SavePoint( 0, 0, ptP00) ;
|
|
ptP10 = m_pSrfBz->GetControlPoint( nDegU * nSpanU, &bOk) ;
|
|
SavePoint( nSpanU * SBZ_TREG_COEFF, 0, ptP10) ;
|
|
ptP11 = m_pSrfBz->GetControlPoint( ( nDegU * nSpanU + 1) * ( nDegV * nSpanV + 1) - 1, &bOk) ;
|
|
SavePoint( nSpanU * SBZ_TREG_COEFF, nSpanV * SBZ_TREG_COEFF, ptP11) ;
|
|
ptP01 = m_pSrfBz->GetControlPoint( ( nDegU * nSpanU + 1) * ( nDegV * nSpanV), &bOk) ;
|
|
SavePoint( 0, nSpanV * SBZ_TREG_COEFF, ptP01) ;
|
|
}
|
|
else {
|
|
GetPoint( ptMin.x, ptMin.y, ptP00) ;
|
|
GetPoint( ptMax.x, ptMin.y, ptP10) ;
|
|
GetPoint( ptMax.x, ptMax.y, ptP11) ;
|
|
GetPoint( ptMin.x, ptMax.y, ptP01) ;
|
|
}
|
|
// se richiesto divido preliminarmente le patches
|
|
m_vnParents.clear() ;
|
|
bool bIsPlanar = m_pSrfBz->IsPlanar() ;
|
|
if( ! bIsPlanar || m_bMulti) {
|
|
// se la superficie è chiusa lungo il parametro U, sistemo le adiacenze al bordo
|
|
if ( AreSamePointApprox(ptP00, ptP10) && AreSamePointApprox(ptP01, ptP11) ) {
|
|
m_mTree[-1].m_nLeft = -1 ;
|
|
m_mTree[-1].m_nRight = -1 ;
|
|
m_bClosedU = true ;
|
|
}
|
|
// se la superficie è chiusa lungo il parametro V, sistemo le adiacenze al bordo
|
|
if ( ( AreSamePointApprox(ptP00, ptP01) && AreSamePointApprox(ptP10, ptP11) ) ) {
|
|
m_mTree[-1].m_nTop = -1 ;
|
|
m_mTree[-1].m_nBottom = -1 ;
|
|
m_bClosedV = true ;
|
|
}
|
|
if ( (m_bSplitPatches && ( nSpanU > 1 || nSpanV > 1)) || m_bClosedU || m_bClosedV) {
|
|
int nId = -1 ;
|
|
int nSplit = nSpanU ;
|
|
bool bSplitInHalf = false ;
|
|
// se la superficie è chiusa lungo un parametro la splitto lungo quel parametro almeno una volta
|
|
if( m_bClosedU && nSpanU == 1) {
|
|
nSplit = 2 ;
|
|
bSplitInHalf = true ;
|
|
}
|
|
for ( int i = 1 ; i < nSplit ; ++i) {
|
|
m_mTree[nId].SetSplitDirVert( true) ;
|
|
double dSplit = bSplitInHalf ? i * SBZ_TREG_COEFF / 2 : i * SBZ_TREG_COEFF ;
|
|
if ( Split( nId, dSplit)) {
|
|
//++ nId ;
|
|
//++ nId ;
|
|
nId = m_mTree[nId].m_nChild2 ;
|
|
}
|
|
}
|
|
INTVECTOR vLeaves ;
|
|
GetHeightLeaves( -1, vLeaves) ;
|
|
nSplit = nSpanV - 1 ;
|
|
bSplitInHalf = false ;
|
|
// se la superficie è chiusa lungo un parametro la splitto lungo quel parametro almeno una volta
|
|
if( m_bClosedV && nSpanV == 1) {
|
|
nSplit = 1 ;
|
|
bSplitInHalf = true ;
|
|
}
|
|
for ( int nId : vLeaves) {
|
|
for ( int j = nSplit ; j > 0 ; --j) {
|
|
m_mTree[nId].SetSplitDirVert( false) ;
|
|
double dSplit = bSplitInHalf ? j * SBZ_TREG_COEFF / 2 : j * SBZ_TREG_COEFF ;
|
|
if ( Split( nId, dSplit))
|
|
nId = m_mTree[nId].m_nChild2 ;
|
|
}
|
|
}
|
|
vLeaves.clear() ;
|
|
}
|
|
// controllo se la superficie è chiusa.
|
|
// se è chiusa e non ho già fatto split preliminare, splitto sul parametro su cui è chiusa
|
|
// e sistemo le adiacenze
|
|
if ( ( AreSamePointApprox( ptP00, ptP01) || AreSamePointApprox( ptP10, ptP11)) ||
|
|
( AreSamePointApprox( ptP00, ptP10) || AreSamePointApprox( ptP01, ptP11))) {
|
|
if ( ( AreSamePointApprox( ptP00, ptP01) || AreSamePointApprox( ptP10, ptP11))) {
|
|
|
|
////questo in teoria non serve più perché lo faccio appena sopra
|
|
if( int( m_mTree.size()) == 1) {
|
|
if ( AreSamePointApprox( ptP00, ptP01) && AreSamePointApprox( ptP10, ptP11)) {
|
|
m_mTree[-1].m_nTop = -1 ;
|
|
m_mTree[-1].m_nBottom = -1 ;
|
|
m_bClosedV = true ;
|
|
}
|
|
m_mTree[-1].SetSplitDirVert( false) ;
|
|
Split( -1) ;
|
|
}
|
|
////////
|
|
|
|
// qui devo fare il controllo capped ( chiusura a semisfera)
|
|
// devo controllare se i punti ai parametri U=0 e U=1 sono tutti coincidenti
|
|
// in caso devo fare uno split nell'altra direzione
|
|
bool bOk = false ;
|
|
bool bPole0 = true, bPole1 = true ;
|
|
Point3d ptU0, ptU1 ;
|
|
// controllo se tutti i punti di controllo sull'isoparametrica sono uguali
|
|
for ( int i = 1 ; i < nDegV * nSpanV + 1 ; ++ i) {
|
|
ptU0 = m_pSrfBz->GetControlPoint( i * ( nDegU * nSpanU + 1), &bOk) ;
|
|
bPole0 = bPole0 && AreSamePointApprox( ptP00, ptU0) ;
|
|
ptU1 = m_pSrfBz->GetControlPoint( ( i + 1) * ( nDegU * nSpanU + 1) - 1, &bOk) ;
|
|
bPole1 = bPole1 && AreSamePointApprox( ptP10, ptU1) ;
|
|
}
|
|
m_vbPole[1] = bPole0 ;
|
|
m_vbPole[3] = bPole1 ;
|
|
if ( bPole0 && bPole1 && int( m_mTree.size() == 3)) {
|
|
m_mTree[0].SetSplitDirVert( true) ;
|
|
Split( 0) ;
|
|
m_mTree[1].SetSplitDirVert( true) ;
|
|
Split( 1) ;
|
|
}
|
|
}
|
|
// nella condizione di questo if non controllo eventuali divisioni preliminari, perché ne tengo conto dopo
|
|
if ( AreSamePointApprox( ptP00, ptP10) || AreSamePointApprox( ptP01, ptP11)) {
|
|
|
|
//// questo in teoria non serve più perché lo faccio sopra
|
|
if ( m_mTree.size() == 1) {
|
|
if ( AreSamePointApprox( ptP00, ptP10) && AreSamePointApprox( ptP01, ptP11)) {
|
|
m_mTree[-1].m_nLeft = -1 ;
|
|
m_mTree[-1].m_nRight = -1 ;
|
|
m_bClosedU = true ;
|
|
}
|
|
m_mTree[-1].SetSplitDirVert( true) ;
|
|
Split( -1) ;
|
|
}
|
|
////////
|
|
|
|
// devo controllare se i punti ai parametri V=0 e V=1 sono tutti coincidenti
|
|
// in caso devo fare uno split nell'altra direzione
|
|
bool bOk = false ;
|
|
bool bPole0 = true, bPole1 = true ;
|
|
Point3d ptV0, ptV1 ;
|
|
// controllo se tutti i punti sull'isoparametrica sono uguali
|
|
for ( int i = 1 ; i < nDegU * nSpanU + 1 ; ++ i) {
|
|
ptV0 = m_pSrfBz->GetControlPoint( i, &bOk) ;
|
|
bPole0 = bPole0 && AreSamePointApprox( ptP00, ptV0) ;
|
|
ptV1 = m_pSrfBz->GetControlPoint( i + ( nDegU * nSpanU + 1) * ( nDegV * nSpanV), &bOk) ;
|
|
bPole1 = bPole1 && AreSamePointApprox( ptP01, ptV1) ;
|
|
}
|
|
m_vbPole[0] = bPole1 ;
|
|
m_vbPole[2] = bPole0 ;
|
|
if ( bPole0 && bPole1 && int( m_mTree.size()) == 3) {
|
|
m_mTree[0].SetSplitDirVert( false) ;
|
|
Split( 0) ;
|
|
m_mTree[1].SetSplitDirVert( false) ;
|
|
Split( 1) ;
|
|
}
|
|
// se ho fatto solo 1 split orizzontale e ho due celle foglie nId = 0 e nId = 1
|
|
if ( int( m_mTree.size() == 3) && ! m_mTree.at(-1).IsSplitVert()) {
|
|
m_mTree[0].m_nLeft = -1 ;
|
|
m_mTree[0].m_nRight = -1 ;
|
|
m_mTree[1].m_nLeft = -1 ;
|
|
m_mTree[1].m_nRight = -1 ;
|
|
m_mTree[0].SetSplitDirVert( true) ;
|
|
Split( 0) ;
|
|
m_mTree[1].SetSplitDirVert( true) ;
|
|
Split( 1) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// calcolo i parent che ho creato con le eventuali divisioni preliminari
|
|
INTVECTOR vLeaves ;
|
|
GetHeightLeaves( -1, vLeaves) ;
|
|
m_vnParents = vLeaves ;
|
|
|
|
// calcolo e salvo la lunghezza reale delle curve di bezier di bordo
|
|
PtrOwner<CurveComposite> pCrvV0( m_pSrfBz->GetCurveOnU( 0)) ;
|
|
PtrOwner<CurveComposite> pCrvV1( m_pSrfBz->GetCurveOnU( double(nSpanV))) ;
|
|
PtrOwner<CurveComposite> pCrvU0( m_pSrfBz->GetCurveOnV( 0)) ;
|
|
PtrOwner<CurveComposite> pCrvU1( m_pSrfBz->GetCurveOnV( double(nSpanU))) ;
|
|
double dLen0 ; pCrvV0->GetApproxLength( dLen0) ;
|
|
double dLen1 ; pCrvU1->GetApproxLength( dLen1) ;
|
|
double dLen2 ; pCrvV1->GetApproxLength( dLen2) ;
|
|
double dLen3 ; pCrvU0->GetApproxLength( dLen3) ;
|
|
if ( dLen0 < EPS_ZERO && dLen2 < EPS_ZERO ) {
|
|
PtrOwner<CurveComposite> pCrvV( m_pSrfBz->GetCurveOnU( double(nSpanV) / 2)) ;
|
|
pCrvV->GetApproxLength( dLen0) ;
|
|
if ( dLen0 < EPS_ZERO ) {
|
|
pCrvV.Set( m_pSrfBz->GetCurveOnU( double(nSpanV) / 4)) ;
|
|
pCrvV->GetApproxLength( dLen0) ;
|
|
}
|
|
}
|
|
if ( dLen1 < EPS_ZERO && dLen3 < EPS_ZERO ) {
|
|
PtrOwner<CurveComposite> pCrvU( m_pSrfBz->GetCurveOnV( double(nSpanU) / 2)) ;
|
|
pCrvU->GetApproxLength( dLen1) ;
|
|
if ( dLen1 < EPS_ZERO ) {
|
|
pCrvU.Set( m_pSrfBz->GetCurveOnV( double(nSpanU) / 4)) ;
|
|
pCrvU->GetApproxLength( dLen1) ;
|
|
}
|
|
}
|
|
m_vDim.clear() ;
|
|
m_vDim.push_back( ( dLen0 > EPS_ZERO ? dLen0 : 1)) ;
|
|
m_vDim.push_back( ( dLen1 > EPS_ZERO ? dLen1 : 1)) ;
|
|
m_vDim.push_back( ( dLen2 > EPS_ZERO ? dLen2 : 1)) ;
|
|
m_vDim.push_back( ( dLen3 > EPS_ZERO ? dLen3 : 1)) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddOrMergeBBox( const BBox3d& bBox3dA, vector<BBox3d>& vBBox, bool bAdd = true, int nInd = 0)
|
|
{
|
|
Point3d ptMin = bBox3dA.GetMin() ;
|
|
Point3d ptMax = bBox3dA.GetMax() ;
|
|
if ( vBBox.empty()) {
|
|
vBBox.push_back( bBox3dA) ;
|
|
return true ;
|
|
}
|
|
bool bAdded = false ;
|
|
for ( int b = 0 ; b < (int)vBBox.size() ; ++b) {
|
|
BBox3d bBox3dB = vBBox[b] ;
|
|
BBox3d b3Int ;
|
|
// se sono celle diverse e ho un'intersezione faccio il merge
|
|
if ( ! ( AreSamePointXYExact( ptMin, bBox3dB.GetMin()) && AreSamePointXYExact( ptMax, bBox3dB.GetMax())) &&
|
|
bBox3dA.FindIntersectionXY( bBox3dB, b3Int)) {
|
|
vBBox[b].Add( bBox3dA) ;
|
|
if ( ! bAdd ) {
|
|
vBBox.erase( vBBox.begin() + nInd) ;
|
|
-- b ;
|
|
}
|
|
// se ho fatto un merge devo controllare se ora la nuova bbox ha delle intersezioni
|
|
AddOrMergeBBox( vBBox[b], vBBox, false, b) ;
|
|
bAdded = true ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( ! bAdded && bAdd)
|
|
vBBox.push_back( bBox3dA) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::GetIndependentTrees( BIPNTVECTOR& vTrees)
|
|
{
|
|
if ( ! m_bTrimmed) {
|
|
vTrees.emplace_back( ORIG, ORIG) ;
|
|
}
|
|
else {
|
|
// se ho dei loop di trim trovo le loro BBox3d per costruire l'albero solo all'interno di queste BBox
|
|
BOXVECTOR vBBox ;
|
|
for ( int i = 0 ; i < int( m_vPlApprox.size()) ; ++ i) {
|
|
PolyLine& plLoop = get<0>( m_vPlApprox[i]) ;
|
|
// calcolo la BBox3d
|
|
Point3d ptP ;
|
|
plLoop.GetFirstPoint( ptP) ;
|
|
BBox3d bBox3dA( ptP) ;
|
|
while ( plLoop.GetNextPoint( ptP))
|
|
bBox3dA.Add( ptP) ;
|
|
// controllo se ho intersezioni con altre bbox
|
|
// se ho intersezioni unisco le bbox, altrimenti le lascio indipendenti
|
|
AddOrMergeBBox( bBox3dA, vBBox) ;
|
|
}
|
|
// controllo se dopo aver unito le bbox ho ottenuto Root
|
|
bool bIsRoot = false ;
|
|
Point3d ptTR( m_nSpanU * SBZ_TREG_COEFF, m_nSpanV * SBZ_TREG_COEFF) ;
|
|
for ( int i = 0 ; i < int( vBBox.size()) ; ++ i) {
|
|
BBox3d& bBox3d = vBBox[i] ;
|
|
if ( AreSamePointXYEpsilon( bBox3d.GetMin(), ORIG, 10) && AreSamePointXYEpsilon( bBox3d.GetMax(), ptTR, 10)) {
|
|
bIsRoot = true ;
|
|
break ;
|
|
}
|
|
}
|
|
// restituisco le celle parent di partenza a partire dalle bbox che ho ottenuto
|
|
if ( ! bIsRoot) {
|
|
for ( int i = 0 ; i < int( vBBox.size()) ; ++ i) {
|
|
Point3d ptMin = vBBox[i].GetMin() ;
|
|
Point3d ptMax = vBBox[i].GetMax() ;
|
|
vTrees.emplace_back( ptMin, ptMax) ;
|
|
}
|
|
}
|
|
else
|
|
vTrees.emplace_back( ORIG, ORIG);
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::Split( int nId, double dSplitValue)
|
|
{
|
|
Cell& cToSplit = m_mTree.at(nId) ;
|
|
// controllo che lo split non venga fatto sul lato della cella
|
|
if ( ( cToSplit.IsSplitVert() && dSplitValue > cToSplit.GetBottomLeft().x + EPS_SMALL &&
|
|
dSplitValue < cToSplit.GetTopRight().x - EPS_SMALL) ||
|
|
( ! cToSplit.IsSplitVert() && dSplitValue > cToSplit.GetBottomLeft().y + EPS_SMALL &&
|
|
dSplitValue < cToSplit.GetTopRight().y - EPS_SMALL)) {
|
|
// quando si implementerà lo split a parametro libero bisognerà impedire che si facciano split troppo vicini al bordo della cella!!!!!!!!!!!!!!!!!!!
|
|
cToSplit.m_dSplit = dSplitValue ;
|
|
Cell cChild1, cChild2 ;
|
|
cChild1.m_nDepth = cToSplit.m_nDepth + 1 ;
|
|
cChild2.m_nDepth = cToSplit.m_nDepth + 1 ;
|
|
int nNodes = (int) m_mTree.size() ;
|
|
cChild1.m_nId = nNodes - 1 ;
|
|
cToSplit.m_nChild1 = nNodes - 1 ;
|
|
cChild2.m_nId = nNodes ;
|
|
cToSplit.m_nChild2 = nNodes ;
|
|
Point3d ptVert1, ptVert2 ;
|
|
if ( ! cToSplit.IsSplitVert()) {
|
|
// la cella figlio 1 è quella sopra
|
|
Point3d ptBL( cToSplit.GetBottomLeft().x, dSplitValue) ;
|
|
cChild1.SetBottomLeft( ptBL) ;
|
|
cChild1.SetTopRight( cToSplit.GetTopRight()) ;
|
|
cChild1.m_nTop = cToSplit.m_nTop ;
|
|
cChild1.m_nBottom = cToSplit.m_nChild2 ;
|
|
cChild1.m_nLeft = cToSplit.m_nLeft ;
|
|
cChild1.m_nRight = cToSplit.m_nRight ;
|
|
Point3d ptTR( cToSplit.GetTopRight().x, dSplitValue) ;
|
|
cChild2.SetBottomLeft( cToSplit.GetBottomLeft()) ;
|
|
cChild2.SetTopRight( ptTR) ;
|
|
cChild2.m_nTop = cToSplit.m_nChild1 ;
|
|
cChild2.m_nBottom = cToSplit.m_nBottom ;
|
|
cChild2.m_nLeft = cToSplit.m_nLeft ;
|
|
cChild2.m_nRight = cToSplit.m_nRight ;
|
|
// metto i corrispondenti 3d dei punti dello split nella mappa m_mVert
|
|
// per ogni cella i punti devono essere nell'ordine ptP00, ptP10, ptP11, ptP01
|
|
GetPoint( cToSplit.GetBottomLeft().x, dSplitValue, ptVert1) ;
|
|
GetPoint( cToSplit.GetTopRight().x, dSplitValue, ptVert2) ;
|
|
}
|
|
else {
|
|
// la cella figlio 1 è quella di sinistra
|
|
Point3d ptTR( dSplitValue, cToSplit.GetTopRight().y) ;
|
|
cChild1.SetBottomLeft( cToSplit.GetBottomLeft()) ;
|
|
cChild1.SetTopRight( ptTR) ;
|
|
cChild1.m_nTop = cToSplit.m_nTop ;
|
|
cChild1.m_nBottom = cToSplit.m_nBottom ;
|
|
cChild1.m_nLeft = cToSplit.m_nLeft ;
|
|
cChild1.m_nRight = cToSplit.m_nChild2 ;
|
|
Point3d ptBL( dSplitValue, cToSplit.GetBottomLeft().y) ;
|
|
cChild2.SetBottomLeft( ptBL) ;
|
|
cChild2.SetTopRight( cToSplit.GetTopRight()) ;
|
|
cChild2.m_nTop = cToSplit.m_nTop ;
|
|
cChild2.m_nBottom = cToSplit.m_nBottom ;
|
|
cChild2.m_nLeft = cToSplit.m_nChild1 ;
|
|
cChild2.m_nRight = cToSplit.m_nRight ;
|
|
// metto i corrispondenti 3d dei punti dello split nella mappa m_mVert
|
|
// per ogni cella i punti devono essere nell'ordine ptP00, ptP10, ptP11, ptP01
|
|
GetPoint( dSplitValue, cToSplit.GetBottomLeft().y, ptVert2) ;
|
|
GetPoint( dSplitValue, cToSplit.GetTopRight().y, ptVert1) ;
|
|
}
|
|
cChild1.SetParent( nId) ;
|
|
cChild2.SetParent( nId) ;
|
|
// inserisco nell'albero
|
|
m_mTree.insert( pair<int, Cell>( nNodes - 1, cChild1)) ;
|
|
m_mTree.insert( pair<int, Cell>( nNodes, cChild2)) ;
|
|
return true ;
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::Split( int nId)
|
|
{
|
|
double dValue ;
|
|
Cell& cCell = m_mTree.at(nId) ;
|
|
if ( cCell.IsSplitVert())
|
|
dValue = ( cCell.GetBottomLeft().x + cCell.GetTopRight().x) / 2 ;
|
|
else
|
|
dValue = ( cCell.GetBottomLeft().y + cCell.GetTopRight().y) / 2 ;
|
|
return Split( nId, dValue) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::BuildTree_test( double dLinTol, double dSideMin, double dSideMax)
|
|
{
|
|
// per poter usare questa funzione, anziché quella normale, bisogna:
|
|
// - commentare la parte di funzione di SetSurf dove si fanno gli split preliminare
|
|
// - se si usa anche la funzione GetLeaves, bisogna anche lì usare BuildTree_test al posto di BuildTree
|
|
|
|
//celle 0,1
|
|
m_mTree[-1].SetSplitDirVert( true) ;
|
|
Split( -1) ;
|
|
//celle 2,3
|
|
m_mTree[0].SetSplitDirVert( false) ;
|
|
Split( 0) ;
|
|
//celle 4,5
|
|
m_mTree[2].SetSplitDirVert( false) ;
|
|
Split( 2) ;
|
|
//celle 6,7
|
|
m_mTree[3].SetSplitDirVert( true) ;
|
|
Split( 3) ;
|
|
//celle 8,9
|
|
m_mTree[1].SetSplitDirVert( false) ;
|
|
Split( 1) ;
|
|
//celle 10,11
|
|
m_mTree[8].SetSplitDirVert( true) ;
|
|
Split( 8) ;
|
|
//celle 12,13
|
|
m_mTree[9].SetSplitDirVert( false) ;
|
|
Split( 9) ;
|
|
m_vnLeaves.push_back( 4) ;
|
|
//m_vnLeaves.push_back( 5) ;
|
|
m_vnLeaves.push_back( 6) ;
|
|
//m_vnLeaves.push_back( 7) ;
|
|
//m_vnLeaves.push_back( 10) ;
|
|
m_vnLeaves.push_back( 11) ;
|
|
//m_vnLeaves.push_back( 12) ;
|
|
m_vnLeaves.push_back( 13) ;
|
|
|
|
// aggiunta di split
|
|
//celle 14,15
|
|
m_mTree[5].SetSplitDirVert( true) ;
|
|
Split( 5) ;
|
|
m_vnLeaves.push_back( 14) ;
|
|
m_vnLeaves.push_back( 15) ;
|
|
//celle 16,17
|
|
m_mTree[7].SetSplitDirVert( false) ;
|
|
Split( 7) ;
|
|
m_vnLeaves.push_back( 16) ;
|
|
m_vnLeaves.push_back( 17) ;
|
|
//celle 18,19
|
|
m_mTree[12].SetSplitDirVert( true) ;
|
|
Split( 12) ;
|
|
m_vnLeaves.push_back( 18) ;
|
|
m_vnLeaves.push_back( 19) ;
|
|
//celle 20,21
|
|
m_mTree[10].SetSplitDirVert( false) ;
|
|
Split( 10) ;
|
|
m_vnLeaves.push_back( 20) ;
|
|
m_vnLeaves.push_back( 21) ;
|
|
// riempio anche la lista dei parent delle celle
|
|
m_vnParents = m_vnLeaves ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::BuildTree( double dLinTol, double dSideMin, double dSideMax)
|
|
{
|
|
// suddivido lo spazio parametrico con divisioni a metà su uno dei due parametri
|
|
int nCToSplit = -1 ;
|
|
Cell* pcToSplit = &m_mTree[nCToSplit] ;
|
|
bool bIsPlanar = m_pSrfBz->IsPlanar() ;
|
|
if ( ! m_bBilinear) {
|
|
while ( nCToSplit != -2 && pcToSplit->IsProcessed() == false) {
|
|
// controllo che la cella non sia già stata preliminarmente splittata
|
|
if ( pcToSplit->IsLeaf()) {
|
|
// calcolo in quale direzione ho più curvatura
|
|
// ptP00P10 è un punto tra P00 e P10
|
|
double dCurvU = 0, dCurvV = 0 ;
|
|
double dLenParU = ( pcToSplit->GetTopRight().x - pcToSplit->GetBottomLeft().x) / SBZ_TREG_COEFF ;
|
|
double dLenParV = ( pcToSplit->GetTopRight().y - pcToSplit->GetBottomLeft().y) / SBZ_TREG_COEFF ;
|
|
Point3d ptP00, ptP10, ptP11, ptP01 ;
|
|
// i vertici della cella
|
|
GetPoint(pcToSplit->GetBottomLeft().x, pcToSplit->GetBottomLeft().y, ptP00) ;
|
|
GetPoint(pcToSplit->GetTopRight().x, pcToSplit->GetBottomLeft().y, ptP10) ;
|
|
GetPoint(pcToSplit->GetTopRight().x, pcToSplit->GetTopRight().y, ptP11) ;
|
|
GetPoint(pcToSplit->GetBottomLeft().x, pcToSplit->GetTopRight().y, ptP01) ;
|
|
if ( dLenParU <= 1. / m_nDegV || dLenParV <= 1. / m_nDegU || Dist(ptP00, ptP11) < dSideMin * 2
|
|
|| Dist(ptP10, ptP01) < dSideMin * 2) {
|
|
double dU = ( pcToSplit->GetTopRight().x + pcToSplit->GetBottomLeft().x) / 2 ;
|
|
double dV = ( pcToSplit->GetTopRight().y + pcToSplit->GetBottomLeft().y) / 2 ;
|
|
double dULoc = 0.5, dVLoc = 0.5 ;
|
|
Point3d ptPSrf, ptP00P10, ptP10P11, ptP11P01, ptP01P00 ;
|
|
GetPoint( dU, dV, ptPSrf) ;
|
|
GetPoint( dU, pcToSplit->GetBottomLeft().y, ptP00P10) ;
|
|
GetPoint( pcToSplit->GetTopRight().x, dV, ptP10P11) ;
|
|
GetPoint( dU, pcToSplit->GetTopRight().y, ptP11P01) ;
|
|
GetPoint( pcToSplit->GetBottomLeft().x, dV, ptP01P00) ;
|
|
Point3d ptV = ( 1 - dULoc) * ptP00P10 + dULoc * ptP11P01 ;
|
|
Point3d ptU = ( 1 - dVLoc) * ptP10P11 + dVLoc * ptP01P00 ;
|
|
dCurvV = Dist( ptV, ptPSrf) ;
|
|
dCurvU = Dist( ptU, ptPSrf) ;
|
|
}
|
|
// faccio un'analisi più fine della curvatura se almeno il grado di una curva di uno dei due parametri è alto e
|
|
// se sto ancora guardando una cella abbastanza grande
|
|
else{
|
|
Point3d ptPSrf, ptP00P10, ptP10P11, ptP11P01, ptP01P00, ptPSrfMid ;
|
|
double dStep = 1. / ( m_nDegU * 2) ;
|
|
for ( double k = dStep ; k < 1 + EPS_SMALL ; k = k + dStep) {
|
|
double dU = ( k * pcToSplit->GetTopRight().x + ( 1 - k) * pcToSplit->GetBottomLeft().x) ;
|
|
double dV = ( pcToSplit->GetTopRight().y + pcToSplit->GetBottomLeft().y) / 2 ;
|
|
GetPoint( dU, dV, ptPSrf) ;
|
|
if ( k == 0.5)
|
|
ptPSrfMid = ptPSrf ;
|
|
GetPoint( dU, pcToSplit->GetBottomLeft().y, ptP00P10) ;
|
|
GetPoint( dU, pcToSplit->GetTopRight().y, ptP11P01) ;
|
|
CurveLine clV ;
|
|
clV.Set( ptP00P10, ptP11P01) ;
|
|
DistPointCurve dpc( ptPSrf, clV) ;
|
|
double dDist ;
|
|
dpc.GetDist( dDist) ;
|
|
dCurvV = max( dCurvV, dDist) ;
|
|
}
|
|
dStep = 1. / ( m_nDegV * 2) ;
|
|
for ( double k = dStep ; k < 1 + EPS_SMALL ; k = k + dStep) {
|
|
double dU = ( pcToSplit->GetTopRight().x + pcToSplit->GetBottomLeft().x) / 2 ;
|
|
double dV = ( k * pcToSplit->GetTopRight().y + ( 1 - k) * pcToSplit->GetBottomLeft().y) ;
|
|
if ( k == 0.5 && ! AreSamePointApprox( ORIG, ptPSrfMid))
|
|
ptPSrf = ptPSrfMid ;
|
|
else
|
|
GetPoint( dU, dV, ptPSrf) ;
|
|
GetPoint( pcToSplit->GetTopRight().x, dV, ptP10P11) ;
|
|
GetPoint( pcToSplit->GetBottomLeft().x, dV, ptP01P00) ;
|
|
CurveLine clU ;
|
|
clU.Set( ptP01P00, ptP10P11) ;
|
|
DistPointCurve dpc( ptPSrf, clU) ;
|
|
double dDist ;
|
|
dpc.GetDist( dDist) ;
|
|
dCurvU = max( dCurvU, dDist) ;
|
|
}
|
|
}
|
|
|
|
// devo calcolare anche il twist, in caso di bordi rettilinei ( superficie di grado maggiore di 1, ma che in realtà è una bilineare)
|
|
// NON posso guardare la distanza tra il punto medio delle diagonali e il punto centrale della cella ( uLoc = 0.5, vLoc = 0.5)
|
|
// posso guardare la distanza tra le due diagonali
|
|
bool bTwist = false ;
|
|
// serve una valutazione più fine, sennò approssimo la superficie in modo troppo grossolano
|
|
DistLineLine dll( ptP00, ptP11, ptP10, ptP01, true, true) ;
|
|
double dDist = 0 ; dll.GetDist( dDist) ;
|
|
if ( dDist > max(dCurvU, dCurvV) || dDist < 5 * EPS_SMALL) {
|
|
bool bFlat = false ;
|
|
// controllo se la cella è twistata di 180 gradi e quindi piatta
|
|
Triangle3d tria1, tria2 ;
|
|
tria1.Set( ptP00, ptP10, ptP11) ; tria1.Validate( true) ;
|
|
tria2.Set( ptP00, ptP11, ptP01) ; tria2.Validate( true) ;
|
|
if( AreOppositeVectorEpsilon(tria1.GetN(), tria2.GetN(), 5 * EPS_SMALL)) {
|
|
bTwist = true ;
|
|
bFlat = true ;
|
|
}
|
|
// controllo che la cella non sia piatta
|
|
if( ! bTwist) {
|
|
PolyLine plCell ;
|
|
plCell.AddUPoint(0,ptP00) ;
|
|
plCell.AddUPoint(1,ptP10) ;
|
|
plCell.AddUPoint(2,ptP11) ;
|
|
double dU = (pcToSplit->GetTopRight().x + pcToSplit->GetBottomLeft().x) / 2 ;
|
|
double dV = (pcToSplit->GetTopRight().y + pcToSplit->GetBottomLeft().y) / 2 ;
|
|
Point3d ptCen ; GetPoint( dU, dV, ptCen) ;
|
|
plCell.AddUPoint(3,ptCen) ;
|
|
plCell.AddUPoint(4,ptP01) ;
|
|
plCell.Close() ;
|
|
Plane3d plPlane ; double dArea = 0 ;
|
|
bFlat = plCell.IsClosedAndFlat( plPlane, dArea, 10 * EPS_SMALL) ;
|
|
}
|
|
if( ! bFlat && dDist > 5 * EPS_SMALL) {
|
|
bTwist = true ;
|
|
// devo decidere in quale direzione splittare
|
|
// dovrei capire in quale delle due direzioni è più torta la superficie
|
|
Vector3d vtU0 = ptP10 - ptP00 ;
|
|
Vector3d vtU1 = ptP11 - ptP01 ;
|
|
double dAngU ;
|
|
bool bDetU = false ;
|
|
vtU0.GetRotation( vtU1, vtU0 ^ vtU1, dAngU, bDetU) ;
|
|
Vector3d vtV0 = ptP01 - ptP00 ;
|
|
Vector3d vtV1 = ptP11 - ptP10 ;
|
|
double dAngV ;
|
|
bool bDetV = false ;
|
|
// faccio la get rotation tra le coppie vettori, usando come asse il loro prodotto vettoriale per ottenere la rotazione tra i due lati
|
|
// splitto nella direzione perpendicolare alla coppia di vettori più torti tra loro.
|
|
vtV0.GetRotation( vtV1, vtV0 ^ vtV1, dAngV, bDetV) ;
|
|
if ( dAngU > dAngV)
|
|
dCurvV = dDist ;
|
|
else
|
|
dCurvU = dDist ;
|
|
}
|
|
}
|
|
|
|
// per lo split scelgo la direzione che è più vicina alla superficie originale nel punto di maggior distanza
|
|
// misura approssimativa della curvatura in una direzione
|
|
bool bVert ;
|
|
if ( dCurvV > dCurvU) {
|
|
// lungo la direzione V ho una curvatura maggiore
|
|
bVert = false ;
|
|
}
|
|
else {
|
|
// lungo la direzione U ho una curvatura maggiore
|
|
bVert = true ;
|
|
}
|
|
pcToSplit->SetSplitDirVert( bVert) ;
|
|
// distanza reale tra i vertici della cella
|
|
double dLen0 = Dist( ptP00, ptP10) ;
|
|
double dLen1 = Dist( ptP10, ptP11) ;
|
|
double dLen2 = Dist( ptP01, ptP11) ;
|
|
double dLen3 = Dist( ptP00, ptP01) ;
|
|
if ( dLen0 < EPS_ZERO && dLen2 < EPS_ZERO ) {
|
|
double dV = ( pcToSplit->GetBottomLeft().y + pcToSplit->GetTopRight().y) / 2 / SBZ_TREG_COEFF ;
|
|
PtrOwner<CurveComposite> pCrvV( m_pSrfBz->GetCurveOnU( dV)) ;
|
|
double dLenU0, dLenU1 ;
|
|
pCrvV->GetLengthAtParam( pcToSplit->GetBottomLeft().x / SBZ_TREG_COEFF, dLenU0) ;
|
|
pCrvV->GetLengthAtParam( pcToSplit->GetTopRight().x / SBZ_TREG_COEFF, dLenU1) ;
|
|
dLen0 = abs( dLenU1 - dLenU0) ;
|
|
}
|
|
if ( dLen1 < EPS_ZERO && dLen3 < EPS_ZERO ) {
|
|
double dU = ( pcToSplit->GetBottomLeft().x + pcToSplit->GetTopRight().x) / 2 / SBZ_TREG_COEFF ;
|
|
PtrOwner<CurveComposite> pCrvU( m_pSrfBz->GetCurveOnV( dU)) ;
|
|
double dLenV0, dLenV1 ;
|
|
pCrvU->GetLengthAtParam( pcToSplit->GetBottomLeft().y / SBZ_TREG_COEFF, dLenV0) ;
|
|
pCrvU->GetLengthAtParam( pcToSplit->GetTopRight().y / SBZ_TREG_COEFF, dLenV1) ;
|
|
dLen1 = abs( dLenV1 - dLenV0) ;
|
|
}
|
|
// verifico che la cella sia da splittare e che eventualmente sia abbastanza grande da poterlo fare
|
|
double dSideMinVal = 0, dSideMaxVal = 0 ;
|
|
if ( bVert) {
|
|
if ( dLen0 > EPS_ZERO && dLen2 > EPS_ZERO)
|
|
dSideMinVal = min( dLen0, dLen2) ;
|
|
else
|
|
dSideMinVal = max( dLen0, dLen2) ;
|
|
}
|
|
else {
|
|
if ( dLen1 > EPS_ZERO && dLen3 > EPS_ZERO)
|
|
dSideMinVal = min( dLen1, dLen3) ;
|
|
else
|
|
dSideMinVal = max( dLen1, dLen3) ;
|
|
}
|
|
// calcolo le diagonali per controllare la dimensione massima dei triangoli in cui dividerei la cella
|
|
dSideMaxVal = max( Dist( ptP00, ptP11), Dist( ptP10, ptP01)) ;
|
|
|
|
// se la cella è abbastanza grande da poter essere divisa ancora, calcolo l'errore di approssimazione
|
|
bool bSplit = false ;
|
|
// dSideMinVal potrebbe essere zero se entrambi i lati che dovrei splittare sono collassati in un punto, ma questo non vuol
|
|
// dire che non dovrei eseguire lo split
|
|
if ( ! bTwist && (dSideMinVal / 2 >= dSideMin || dSideMinVal < EPS_SMALL) && dSideMaxVal < dSideMax && ( dCurvV > dLinTol || dCurvU > dLinTol)) {
|
|
CurveLine cl0010, cl0001, cl1011, cl0111 ;
|
|
// V=0
|
|
cl0010.Set( ptP00, ptP10) ;
|
|
// V=1
|
|
cl0111.Set( ptP01, ptP11) ;
|
|
Point3d pt0010, pt0111, ptBz0, ptBz1, ptBzV ;
|
|
int nFlag ;
|
|
CurveLine clV ;
|
|
// determino quanti Step fare per ogni direzione parametrica
|
|
double dDimU = ( dLen0 >= dLen2 ? dLen0 / m_vDim[0] : dLen2 / m_vDim[2]) ;
|
|
dDimU = ( dDimU > 1 ? 1 : dDimU) ;
|
|
double dDimV = ( dLen1 >= dLen3 ? dLen1 / m_vDim[1] : dLen3 / m_vDim[3]) ;
|
|
dDimV = ( dDimV > 1 ? 1 : dDimV) ;
|
|
// numero di Step per campionare la superficie nelle due direzioni parametriche
|
|
int nStepsU = int( 51 * dDimU + 5 * ( 1 - dDimU)) ;
|
|
int nStepsV = int( 51 * dDimV + 5 * ( 1 - dDimV)) ;
|
|
for ( int u = 0 ; u < nStepsU && ! bSplit ; ++ u) {
|
|
double dU = double ( u) / double ( nStepsU - 1) ;
|
|
double dULoc = ( ( 1 - dU) * pcToSplit->GetBottomLeft().x + dU * pcToSplit->GetTopRight().x) ;
|
|
if ( ! GetPoint( dULoc, pcToSplit->GetBottomLeft().y, ptBz0) ||
|
|
! GetPoint( dULoc, pcToSplit->GetTopRight().y, ptBz1))
|
|
return false ;
|
|
// verifico che la cella non sia uno spicchio in verticale, cioè con ptP00 == ptP01 && ptP10 == ptP11
|
|
// ( vedi disegno sotto per uno spicchio verticale)
|
|
// sennò i punti che cerco sono semplicemente i vertici
|
|
if ( cl0010.IsValid()) {
|
|
DistPointCurve dpc0010( ptBz0, cl0010) ;
|
|
dpc0010.GetMinDistPoint( 0, pt0010, nFlag) ;
|
|
}
|
|
else
|
|
pt0010 = ptP00 ;
|
|
if ( cl0111.IsValid()) {
|
|
DistPointCurve dpc0111( ptBz1, cl0111) ;
|
|
dpc0111.GetMinDistPoint( 0, pt0111, nFlag) ;
|
|
}
|
|
else
|
|
pt0111 = ptP01 ;
|
|
// curva a parametro U fisso, con V che scorre
|
|
clV.Set( pt0010, pt0111) ;
|
|
for ( int v = 0 ; v < nStepsV ; ++ v) {
|
|
double dV = double ( v) / double ( nStepsV - 1) ;
|
|
double dVLoc = ( ( 1 - dV) * pcToSplit->GetBottomLeft().y + dV * pcToSplit->GetTopRight().y) ;
|
|
if ( ! GetPoint( dULoc, dVLoc, ptBzV))
|
|
return false ;
|
|
DistPointCurve dpc( ptBzV, clV) ;
|
|
// distanza di approssimazione locale
|
|
double dDist ;
|
|
dpc.GetDist( dDist) ;
|
|
// se la cella è uno spicchio, quindi con due lati collassati, devo calcolare in modo diverso dist
|
|
// ptP00 == ptP01
|
|
// / \
|
|
// / \
|
|
// / \
|
|
// ( )
|
|
// \ /
|
|
// \ /
|
|
// \ /
|
|
// ptP10 == ptP11
|
|
if ( ! clV.IsValid() && AreSamePointApprox( ptP00, ptP01) && AreSamePointApprox( ptP10, ptP11)) {
|
|
DistPointCurve dpcSlice( ptBzV, cl0010) ;
|
|
dpcSlice.GetDist( dDist) ;
|
|
}
|
|
if ( dDist > dLinTol) {
|
|
bSplit = true ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( bTwist ) {
|
|
// se la cella è twistata allora l'errore lo calcolo come nelle bilineari
|
|
//double dErr = 1. / 4. * ( ( ptP00 - ptP01) + ( ptP11 - ptP10)).Len() ;
|
|
double dErr = ( ( ptP00 - ptP01) + ( ptP11 - ptP10)).Len() / 20 ;
|
|
if ( dErr > dLinTol)
|
|
bSplit = true ;
|
|
}
|
|
|
|
if ( bSplit || dSideMaxVal > dSideMax) {
|
|
pcToSplit->SetSplitDirVert( bVert) ;
|
|
// effettuo lo split
|
|
Split( nCToSplit) ;
|
|
|
|
// procedo con lo split del Child1
|
|
nCToSplit = pcToSplit->m_nChild1 ;
|
|
pcToSplit = &m_mTree[nCToSplit] ;
|
|
}
|
|
else {
|
|
// sono arrivato ad una cella Leaf, quindi salvo la cella
|
|
m_vnLeaves.push_back( nCToSplit) ;
|
|
pcToSplit->SetProcessed() ;
|
|
// risalgo i parent finché non trovo il primo Child2 da processare
|
|
nCToSplit = pcToSplit->m_nParent ;
|
|
pcToSplit = &m_mTree[nCToSplit] ;
|
|
if ( nCToSplit == -2)
|
|
return true ;
|
|
if ( m_mTree[pcToSplit->m_nChild1].IsProcessed() && m_mTree[pcToSplit->m_nChild2].IsProcessed())
|
|
pcToSplit->SetProcessed() ;
|
|
while ( m_mTree[pcToSplit->m_nChild2].IsProcessed()) {
|
|
if ( pcToSplit->m_nParent != -2) {
|
|
nCToSplit = pcToSplit->m_nParent ;
|
|
pcToSplit = &m_mTree[nCToSplit] ;
|
|
}
|
|
if ( m_mTree[pcToSplit->m_nChild1].IsProcessed() && m_mTree[pcToSplit->m_nChild2].IsProcessed())
|
|
pcToSplit->SetProcessed() ;
|
|
if ( nCToSplit == -1 && m_mTree[pcToSplit->m_nChild2].IsProcessed())
|
|
break ;
|
|
}
|
|
nCToSplit = pcToSplit->m_nChild2 ;
|
|
pcToSplit = &m_mTree[nCToSplit] ;
|
|
}
|
|
}
|
|
else {
|
|
nCToSplit = pcToSplit->m_nChild1 ;
|
|
pcToSplit = &m_mTree[nCToSplit] ;
|
|
}
|
|
}
|
|
Balance() ; // da implementare quando dividerò ad un parametro a scelta e non a metà // probabilmente mi servirà salvare nella cella il livello di profondità
|
|
}
|
|
// bilineare
|
|
else {
|
|
while ( nCToSplit != -2 && pcToSplit->IsProcessed() == false) {
|
|
if ( pcToSplit->IsLeaf()) {
|
|
// vertici della cella
|
|
Point3d ptP00, ptP10, ptP11, ptP01 ;
|
|
GetPoint(pcToSplit->GetBottomLeft().x, pcToSplit->GetBottomLeft().y, ptP00) ;
|
|
GetPoint(pcToSplit->GetTopRight().x, pcToSplit->GetBottomLeft().y, ptP10) ;
|
|
GetPoint(pcToSplit->GetTopRight().x, pcToSplit->GetTopRight().y, ptP11) ;
|
|
GetPoint(pcToSplit->GetBottomLeft().x, pcToSplit->GetTopRight().y, ptP01) ;
|
|
// distanza reale tra i vertici della cella
|
|
double dLen0 = Dist( ptP00, ptP10) ;
|
|
double dLen1 = Dist( ptP10, ptP11) ;
|
|
double dLen2 = Dist( ptP01, ptP11) ;
|
|
double dLen3 = Dist( ptP00, ptP01) ;
|
|
|
|
bool bVert = false ;
|
|
///per capire in quale direzione splittare devo guardare quale coppia di lati opposti è più sghemba
|
|
Vector3d vtU0 = ptP01 - ptP00 ;
|
|
Vector3d vtU1 = ptP11 - ptP10 ;
|
|
double dLU0, dFU0, dTU0, dLU1, dFU1, dTU1 ;
|
|
vtU0.ToSpherical( &dLU0, &dFU0, &dTU0) ;
|
|
vtU1.ToSpherical( &dLU1, &dFU1, &dTU1) ;
|
|
double dSkewnessV = abs( dFU1 - dFU0) + abs( dTU1 - dTU0) ;
|
|
Vector3d vtV0 = ptP10 - ptP00 ;
|
|
Vector3d vtV1 = ptP11 - ptP01 ;
|
|
double dLV0, dFV0, dTV0, dLV1, dFV1, dTV1 ;
|
|
vtV0.ToSpherical( &dLV0, &dFV0, &dTV0) ;
|
|
vtV1.ToSpherical( &dLV1, &dFV1, &dTV1) ;
|
|
double dSkewnessU = abs( dFV1 - dFV0) + abs( dTV1 - dTV0) ;
|
|
if( dSkewnessU > dSkewnessV)
|
|
bVert = false ;
|
|
else
|
|
bVert = true ;
|
|
|
|
// verifico che la cella sia abbastanza grande da poter essere splittata
|
|
double dSideMinVal = ( bVert ? max( dLen0, dLen2) : max( dLen1, dLen3)) ;
|
|
// calcolo le diagonali per controllare la dimensione massima dei triangoli in cui dividerei la cella
|
|
double dSideMaxVal = max( Dist( ptP00, ptP11), Dist( ptP10, ptP01)) ;
|
|
|
|
double dErr = 0 ;
|
|
if ( m_bMulti) {
|
|
Point3d ptPSrf ;
|
|
Plane3d plAppr ;
|
|
if ( ! AreSamePointApprox( ptP00, ptP10) && ! AreSamePointApprox( ptP00, ptP01))
|
|
plAppr.Set( ptP00, ( ptP00 - ptP01) ^ ( ptP00 - ptP10)) ;
|
|
else if ( AreSamePointApprox( ptP00, ptP10)) {
|
|
plAppr.Set( ptP01, ( ptP00 - ptP01) ^ ( ptP01 - ptP11)) ;
|
|
}
|
|
else if ( AreSamePointApprox( ptP00, ptP01)) {
|
|
plAppr.Set( ptP10, ( ptP10 - ptP11) ^ ( ptP00 - ptP10)) ;
|
|
}
|
|
for ( double i = 0.25 ; i < 1 ; i = i + 0.25) {
|
|
for ( double j = 0.25 ; j < 1 ; j = j + 0.25) {
|
|
double dU = ( ( 1 - i) * pcToSplit->GetTopRight().x + i * pcToSplit->GetBottomLeft().x) ;
|
|
double dV = ( ( 1 - j) * pcToSplit->GetTopRight().y + j * pcToSplit->GetBottomLeft().y) ;
|
|
GetPoint( dU, dV, ptPSrf) ;
|
|
dErr = max( abs( DistPointPlane( ptPSrf, plAppr)), dErr) ;
|
|
}
|
|
}
|
|
}
|
|
else if ( ! bIsPlanar){
|
|
dErr = 1. / 4. * ( ( ptP00 - ptP01) + ( ptP11 - ptP10)).Len() ;
|
|
//int dErr2 = 1. / 4. * ( ( ptP10 - ptP01) + ( ptP11 - ptP00)).Len() ; //correzione che mi verrebbe intuitiva, ma che fa dividere la superficie molto di più ( probabilmente troppo)!! quindi probabilmente sbagliata
|
|
}
|
|
// se la cella è abbastanza grande da poter essere divisa ancora e devo approssimare meglio, la divido
|
|
if ( dSideMinVal / 2 >= dSideMin && dSideMaxVal < dSideMax && dErr > dLinTol) {
|
|
pcToSplit->SetSplitDirVert( bVert) ;
|
|
// effettuo lo split
|
|
Split( nCToSplit) ;
|
|
|
|
// procedo con lo split del Child1
|
|
nCToSplit = pcToSplit->m_nChild1 ;
|
|
pcToSplit = &m_mTree[nCToSplit] ;
|
|
}
|
|
else {
|
|
// sono arrivato ad una cella Leaf, quindi salvo la cella
|
|
m_vnLeaves.push_back( nCToSplit) ;
|
|
pcToSplit->SetProcessed() ;
|
|
// risalgo i parent finché non trovo il primo Child2 da processare
|
|
nCToSplit = pcToSplit->m_nParent ;
|
|
pcToSplit = &m_mTree[nCToSplit] ;
|
|
if ( nCToSplit == -2)
|
|
return true ;
|
|
if ( m_mTree[pcToSplit->m_nChild1].IsProcessed() && m_mTree[pcToSplit->m_nChild2].IsProcessed())
|
|
pcToSplit->SetProcessed() ;
|
|
while ( m_mTree[pcToSplit->m_nChild2].IsProcessed()) {
|
|
if ( pcToSplit->m_nParent != -2) {
|
|
nCToSplit = pcToSplit->m_nParent ;
|
|
pcToSplit = &m_mTree[nCToSplit] ;
|
|
}
|
|
if ( m_mTree[pcToSplit->m_nChild1].IsProcessed() && m_mTree[pcToSplit->m_nChild2].IsProcessed())
|
|
pcToSplit->SetProcessed() ;
|
|
if ( nCToSplit == -1 && m_mTree[pcToSplit->m_nChild2].IsProcessed())
|
|
break ;
|
|
}
|
|
nCToSplit = pcToSplit->m_nChild2 ;
|
|
pcToSplit = &m_mTree[nCToSplit] ;
|
|
}
|
|
}
|
|
else {
|
|
nCToSplit = pcToSplit->m_nChild1 ;
|
|
pcToSplit = &m_mTree[nCToSplit] ;
|
|
}
|
|
}
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
Tree::Balance()
|
|
{
|
|
//for ( int i : vCheck) {
|
|
// // non ancora implementato
|
|
// // rendo il tree balanced : ogni foglia deve avere una profondità di +- 1 rispetto alle foglie adiacenti.
|
|
//}
|
|
|
|
// al momento il problema viene bypassato in fase di generazione dei poligoni, considerando per ogni cella, oltre ai propri vertici
|
|
// i vertici dei vicini che giacciono sui suoi lati
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
Tree::GetTopNeigh( int nId, INTVECTOR& vTopNeighs) const
|
|
{
|
|
const Cell& cell = m_mTree.at( nId) ;
|
|
// le celle restituite sono ordinate per x crescente
|
|
if ( vTopNeighs.empty()) {
|
|
if ( cell.m_nTop == -2)
|
|
return ;
|
|
if ( m_mTree.at( cell.m_nTop).IsLeaf())
|
|
vTopNeighs.push_back( cell.m_nTop) ;
|
|
else {
|
|
if ( m_mTree.at( cell.m_nTop).IsSplitVert()) {
|
|
// se la cella vicina è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima
|
|
if ( m_mTree.at( cell.m_nTop).GetTopRight().x - m_mTree.at( cell.m_nTop).GetBottomLeft().x <=
|
|
cell.GetTopRight().x - cell.GetBottomLeft().x) {
|
|
vTopNeighs.push_back( m_mTree.at( cell.m_nTop).m_nChild1) ;
|
|
vTopNeighs.push_back( m_mTree.at( cell.m_nTop).m_nChild2) ;
|
|
}
|
|
// altrimenti solo uno dei figli lo sarà
|
|
else {
|
|
if ( m_mTree.at( m_mTree.at( cell.m_nTop).m_nChild1).GetTopRight().x <= cell.GetBottomLeft().x ||
|
|
m_mTree.at( m_mTree.at( cell.m_nTop).m_nChild1).GetBottomLeft().x >= cell.GetTopRight().x )
|
|
vTopNeighs.push_back( m_mTree.at( cell.m_nTop).m_nChild2) ;
|
|
else
|
|
vTopNeighs.push_back( m_mTree.at( cell.m_nTop).m_nChild1) ;
|
|
}
|
|
}
|
|
else {
|
|
vTopNeighs.push_back( m_mTree.at( cell.m_nTop).m_nChild2) ;
|
|
}
|
|
}
|
|
bool bAllLeaves = true ;
|
|
for ( int i : vTopNeighs) {
|
|
if ( ! m_mTree.at( i).IsLeaf())
|
|
bAllLeaves = false ;
|
|
}
|
|
if ( ! bAllLeaves)
|
|
// almeno una cella tra i vicini trovati non è leaf quindi devo richiamare ricorsivamente questa funzione per trovare i suoi child
|
|
GetTopNeigh( nId, vTopNeighs) ;
|
|
}
|
|
else {
|
|
for ( int j = 0 ; j != (int) vTopNeighs.size() ; ++ j) {
|
|
int i = vTopNeighs.at( j) ;
|
|
if ( m_mTree.at( i).IsLeaf())
|
|
continue ;
|
|
else {
|
|
// se la cella non è leaf la tolgo dal vettore delle foglie e aggiungo invece i suoi child
|
|
vTopNeighs.erase( remove( vTopNeighs.begin(),vTopNeighs.end(),i)) ;
|
|
-- j ;
|
|
if ( m_mTree.at( i).IsSplitVert()) {
|
|
// se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima
|
|
if ( m_mTree.at( i).GetTopRight().x - m_mTree.at( i).GetBottomLeft().x <=
|
|
cell.GetTopRight().x - cell.GetBottomLeft().x) {
|
|
vTopNeighs.push_back( m_mTree.at( i).m_nChild1) ;
|
|
vTopNeighs.push_back( m_mTree.at( i).m_nChild2) ;
|
|
}
|
|
// altrimenti solo uno dei figli lo sarà
|
|
else {
|
|
if ( m_mTree.at( m_mTree.at( i).m_nChild1).GetTopRight().x <= cell.GetBottomLeft().x ||
|
|
m_mTree.at( m_mTree.at( i).m_nChild1).GetBottomLeft().x >= cell.GetTopRight().x )
|
|
vTopNeighs.push_back( m_mTree.at( i).m_nChild2) ;
|
|
else
|
|
vTopNeighs.push_back( m_mTree.at( i).m_nChild1) ;
|
|
}
|
|
}
|
|
else {
|
|
vTopNeighs.push_back( m_mTree.at( i).m_nChild2) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
vector<Cell> vCells ;
|
|
for ( int k : vTopNeighs)
|
|
vCells.push_back( m_mTree.at( k)) ;
|
|
// le celle restituite sono ordinate per x crescente
|
|
std::sort( vCells.begin(), vCells.end(), Cell::minorX) ;
|
|
vTopNeighs.clear() ;
|
|
for ( Cell& c : vCells)
|
|
vTopNeighs.push_back( c.m_nId) ;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
Tree::GetBottomNeigh( int nId, INTVECTOR& vBottomNeighs) const
|
|
{
|
|
const Cell& cell = m_mTree.at( nId) ;
|
|
// le celle restituite sono ordinate per x crescente
|
|
if ( vBottomNeighs.empty()) {
|
|
if ( cell.m_nBottom == -2)
|
|
return ;
|
|
if ( m_mTree.at( cell.m_nBottom).IsLeaf())
|
|
vBottomNeighs.push_back( cell.m_nBottom) ;
|
|
else {
|
|
if ( m_mTree.at( cell.m_nBottom).IsSplitVert()) {
|
|
// se la cella vicina è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima
|
|
if ( m_mTree.at( cell.m_nBottom).GetTopRight().x - m_mTree.at( cell.m_nBottom).GetBottomLeft().x <=
|
|
cell.GetTopRight().x - cell.GetBottomLeft().x) {
|
|
vBottomNeighs.push_back( m_mTree.at( cell.m_nBottom).m_nChild1) ;
|
|
vBottomNeighs.push_back( m_mTree.at( cell.m_nBottom).m_nChild2) ;
|
|
}
|
|
// altrimenti solo uno dei figli lo sarà
|
|
else{
|
|
if ( m_mTree.at( m_mTree.at( cell.m_nBottom).m_nChild1).GetTopRight().x <= cell.GetBottomLeft().x ||
|
|
m_mTree.at( m_mTree.at( cell.m_nBottom).m_nChild1).GetBottomLeft().x >= cell.GetTopRight().x )
|
|
vBottomNeighs.push_back( m_mTree.at( cell.m_nBottom).m_nChild2) ;
|
|
else
|
|
vBottomNeighs.push_back( m_mTree.at( cell.m_nBottom).m_nChild1) ;
|
|
}
|
|
}
|
|
else {
|
|
vBottomNeighs.push_back( m_mTree.at( cell.m_nBottom).m_nChild1) ;
|
|
}
|
|
}
|
|
bool bAllLeaves = true ;
|
|
for ( int i : vBottomNeighs) {
|
|
if ( ! m_mTree.at( i).IsLeaf())
|
|
bAllLeaves = false ;
|
|
}
|
|
if ( ! bAllLeaves)
|
|
// almeno una cella tra i vicini trovati non è leaf quindi devo richiamare ricorsivamente questa funzione per trovare i suoi child
|
|
GetBottomNeigh( nId, vBottomNeighs) ;
|
|
}
|
|
else {
|
|
for ( int j = 0 ; j < int( vBottomNeighs.size()) ; ++ j) {
|
|
int i = vBottomNeighs.at( j) ;
|
|
if ( m_mTree.at( i).IsLeaf())
|
|
continue ;
|
|
else {
|
|
// se la cella non è leaf la tolgo dal vettore delle foglie e aggiungo invece i suoi child
|
|
vBottomNeighs.erase( remove( vBottomNeighs.begin(),vBottomNeighs.end(),i)) ;
|
|
-- j ;
|
|
if ( m_mTree.at( i).IsSplitVert()) {
|
|
// se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima
|
|
if ( m_mTree.at( i).GetTopRight().x - m_mTree.at( i).GetBottomLeft().x <=
|
|
cell.GetTopRight().x - cell.GetBottomLeft().x) {
|
|
vBottomNeighs.push_back( m_mTree.at( i).m_nChild1) ;
|
|
vBottomNeighs.push_back( m_mTree.at( i).m_nChild2) ;
|
|
}
|
|
// altrimenti solo uno dei figli lo sarà
|
|
else {
|
|
if ( m_mTree.at( m_mTree.at( i).m_nChild1).GetTopRight().x <= cell.GetBottomLeft().x ||
|
|
m_mTree.at( m_mTree.at( i).m_nChild1).GetBottomLeft().x >= cell.GetTopRight().x)
|
|
vBottomNeighs.push_back( m_mTree.at( i).m_nChild2) ;
|
|
else
|
|
vBottomNeighs.push_back( m_mTree.at( i).m_nChild1) ;
|
|
}
|
|
}
|
|
else {
|
|
vBottomNeighs.push_back( m_mTree.at( i).m_nChild1) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
vector<Cell> vCells ;
|
|
for ( int k : vBottomNeighs)
|
|
vCells.push_back( m_mTree.at( k)) ;
|
|
// le celle restituite sono ordinate per x crescente
|
|
std::sort( vCells.begin(), vCells.end(), Cell::minorX) ;
|
|
vBottomNeighs.clear() ;
|
|
for ( Cell& c : vCells)
|
|
vBottomNeighs.push_back( c.m_nId) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
Tree::GetLeftNeigh( int nId, INTVECTOR& vLeftNeighs) const
|
|
{
|
|
const Cell& cell = m_mTree.at( nId) ;
|
|
// le celle restituite sono ordinate per y crescente
|
|
if ( vLeftNeighs.empty()) {
|
|
if ( cell.m_nLeft == -2)
|
|
return ;
|
|
if ( m_mTree.at( cell.m_nLeft).IsLeaf())
|
|
vLeftNeighs.push_back( cell.m_nLeft) ;
|
|
else {
|
|
if ( ! m_mTree.at( cell.m_nLeft).IsSplitVert()) {
|
|
// se la cella vicina è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima
|
|
if ( m_mTree.at( cell.m_nLeft).GetTopRight().y - m_mTree.at( cell.m_nLeft).GetBottomLeft().y <=
|
|
cell.GetTopRight().y - cell.GetBottomLeft().y) {
|
|
vLeftNeighs.push_back( m_mTree.at( cell.m_nLeft).m_nChild1) ;
|
|
vLeftNeighs.push_back( m_mTree.at( cell.m_nLeft).m_nChild2) ;
|
|
}
|
|
// altrimenti solo uno dei figli lo sarà
|
|
else{
|
|
if ( m_mTree.at( m_mTree.at( cell.m_nLeft).m_nChild1).GetTopRight().y <= cell.GetBottomLeft().y ||
|
|
m_mTree.at( m_mTree.at( cell.m_nLeft).m_nChild1).GetBottomLeft().y >= cell.GetTopRight().y)
|
|
vLeftNeighs.push_back( m_mTree.at( cell.m_nLeft).m_nChild2) ;
|
|
else
|
|
vLeftNeighs.push_back( m_mTree.at( cell.m_nLeft).m_nChild1) ;
|
|
}
|
|
}
|
|
else {
|
|
vLeftNeighs.push_back( m_mTree.at( cell.m_nLeft).m_nChild2) ;
|
|
}
|
|
}
|
|
bool bAllLeaves = true ;
|
|
for ( int i : vLeftNeighs) {
|
|
if ( ! m_mTree.at( i).IsLeaf())
|
|
bAllLeaves = false ;
|
|
}
|
|
if ( ! bAllLeaves)
|
|
// almeno una cella tra i vicini trovati non è leaf quindi devo richiamare ricorsivamente questa funzione per trovare i suoi child
|
|
GetLeftNeigh( nId, vLeftNeighs) ;
|
|
}
|
|
else {
|
|
for (int j = 0 ; j < int( vLeftNeighs.size()) ; ++ j) {
|
|
int i = vLeftNeighs.at( j) ;
|
|
if ( m_mTree.at( i).IsLeaf())
|
|
continue ;
|
|
else {
|
|
// se la cella non è leaf la tolgo dal vettore delle foglie e aggiungo invece i suoi child
|
|
vLeftNeighs.erase( remove( vLeftNeighs.begin(),vLeftNeighs.end(),i)) ;
|
|
-- j ;
|
|
if ( ! m_mTree.at( i).IsSplitVert()) {
|
|
// se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima
|
|
if ( m_mTree.at( i).GetTopRight().y - m_mTree.at( i).GetBottomLeft().y <=
|
|
cell.GetTopRight().y - cell.GetBottomLeft().y) {
|
|
vLeftNeighs.push_back( m_mTree.at( i).m_nChild1) ;
|
|
vLeftNeighs.push_back( m_mTree.at( i).m_nChild2) ;
|
|
}
|
|
// altrimenti solo uno dei figli lo sarà
|
|
else {
|
|
if ( m_mTree.at( m_mTree.at( i).m_nChild1).GetTopRight().y <= cell.GetBottomLeft().y ||
|
|
m_mTree.at( m_mTree.at( i).m_nChild1).GetBottomLeft().y >= cell.GetTopRight().y)
|
|
vLeftNeighs.push_back( m_mTree.at( i).m_nChild2) ;
|
|
else
|
|
vLeftNeighs.push_back( m_mTree.at( i).m_nChild1) ;
|
|
}
|
|
}
|
|
else {
|
|
vLeftNeighs.push_back( m_mTree.at( i).m_nChild2) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
vector<Cell> vCells ;
|
|
for ( int k : vLeftNeighs)
|
|
vCells.push_back( m_mTree.at( k)) ;
|
|
// le celle restituite sono ordinate per y crescente
|
|
std::sort( vCells.begin(), vCells.end(), Cell::minorY) ;
|
|
vLeftNeighs.clear() ;
|
|
for ( Cell& c : vCells)
|
|
vLeftNeighs.push_back( c.m_nId) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
Tree::GetRightNeigh( int nId, INTVECTOR& vRightNeighs) const
|
|
{
|
|
const Cell& cell = m_mTree.at( nId) ;
|
|
// le celle restituite sono ordinate per y crescente
|
|
if ( vRightNeighs.empty()) {
|
|
if ( cell.m_nRight == -2)
|
|
return ;
|
|
if ( m_mTree.at( cell.m_nRight).IsLeaf())
|
|
vRightNeighs.push_back( cell.m_nRight) ;
|
|
else {
|
|
if ( ! m_mTree.at( cell.m_nRight).IsSplitVert()) {
|
|
// se la cella vicina è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima
|
|
if ( m_mTree.at( cell.m_nRight).GetTopRight().y - m_mTree.at( cell.m_nRight).GetBottomLeft().y <=
|
|
cell.GetTopRight().y - cell.GetBottomLeft().y) {
|
|
vRightNeighs.push_back( m_mTree.at( cell.m_nRight).m_nChild1) ;
|
|
vRightNeighs.push_back( m_mTree.at( cell.m_nRight).m_nChild2) ;
|
|
}
|
|
// altrimenti solo uno dei figli lo sarà
|
|
else{
|
|
if ( m_mTree.at( m_mTree.at( cell.m_nRight).m_nChild1).GetTopRight().y <= cell.GetBottomLeft().y ||
|
|
m_mTree.at( m_mTree.at( cell.m_nRight).m_nChild1).GetBottomLeft().y >= cell.GetTopRight().y)
|
|
vRightNeighs.push_back( m_mTree.at( cell.m_nRight).m_nChild2) ;
|
|
else
|
|
vRightNeighs.push_back( m_mTree.at( cell.m_nRight).m_nChild1) ;
|
|
}
|
|
}
|
|
else {
|
|
vRightNeighs.push_back( m_mTree.at( cell.m_nRight).m_nChild1) ;
|
|
}
|
|
}
|
|
bool bAllLeaves = true ;
|
|
for ( int i : vRightNeighs) {
|
|
if ( ! m_mTree.at( i).IsLeaf())
|
|
bAllLeaves = false ;
|
|
}
|
|
if ( ! bAllLeaves)
|
|
// almeno una cella tra i vicini trovati non è leaf quindi devo richiamare ricorsivamente questa funzione per trovare i suoi child
|
|
GetRightNeigh( nId, vRightNeighs) ;
|
|
}
|
|
else {
|
|
for (int j = 0 ; j < int( vRightNeighs.size()) ; ++ j) {
|
|
int i = vRightNeighs.at( j) ;
|
|
if ( m_mTree.at( i).IsLeaf())
|
|
continue ;
|
|
else {
|
|
// se la cella non è leaf la tolgo dal vettore delle foglie e aggiungo invece i suoi child
|
|
vRightNeighs.erase( remove( vRightNeighs.begin(),vRightNeighs.end(), i)) ;
|
|
-- j ;
|
|
if ( ! m_mTree.at( i).IsSplitVert()) {
|
|
// se la cella è più piccola della cella indagata, allora entrambi i figli saranno vicini di quest'ultima
|
|
if ( m_mTree.at( i).GetTopRight().y - m_mTree.at( i).GetBottomLeft().y <=
|
|
cell.GetTopRight().y - cell.GetBottomLeft().y) {
|
|
vRightNeighs.push_back( m_mTree.at( i).m_nChild1) ;
|
|
vRightNeighs.push_back( m_mTree.at( i).m_nChild2) ;
|
|
}
|
|
// altrimenti solo uno dei figli lo sarà
|
|
else {
|
|
if ( m_mTree.at( m_mTree.at( i).m_nChild1).GetTopRight().y <= cell.GetBottomLeft().y ||
|
|
m_mTree.at( m_mTree.at( i).m_nChild1).GetBottomLeft().y >= cell.GetTopRight().y)
|
|
vRightNeighs.push_back( m_mTree.at( i).m_nChild2) ;
|
|
else
|
|
vRightNeighs.push_back( m_mTree.at( i).m_nChild1) ;
|
|
}
|
|
}
|
|
else {
|
|
vRightNeighs.push_back( m_mTree.at( i).m_nChild1) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
vector<Cell> vCells ;
|
|
for ( int k : vRightNeighs)
|
|
vCells.push_back( m_mTree.at( k)) ;
|
|
// le celle restituite sono ordinate per y crescente
|
|
std::sort( vCells.begin(), vCells.end(), Cell::minorY) ;
|
|
vRightNeighs.clear() ;
|
|
for ( Cell& c : vCells)
|
|
vRightNeighs.push_back( c.m_nId) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
Tree::GetRootNeigh( int nEdge, INTVECTOR& vNeigh)
|
|
{
|
|
int nId = -1 ;
|
|
bool bMod = false ;
|
|
if ( nEdge == 0) {
|
|
if ( m_mTree[nId].m_nBottom == -2) {
|
|
m_mTree[nId].m_nBottom = -1 ;
|
|
bMod = true ;
|
|
}
|
|
GetBottomNeigh( nId, vNeigh) ;
|
|
if ( bMod)
|
|
m_mTree[nId].m_nBottom = -2 ;
|
|
}
|
|
else if ( nEdge == 1) {
|
|
if ( m_mTree[nId].m_nRight == -2) {
|
|
m_mTree[nId].m_nRight = -1 ;
|
|
bMod = true ;
|
|
}
|
|
GetRightNeigh( nId, vNeigh) ;
|
|
if ( bMod)
|
|
m_mTree[nId].m_nRight = -2 ;
|
|
}
|
|
else if ( nEdge == 2) {
|
|
if ( m_mTree[nId].m_nTop == -2) {
|
|
m_mTree[nId].m_nTop = -1 ;
|
|
bMod = true ;
|
|
}
|
|
GetTopNeigh( nId, vNeigh) ;
|
|
if ( bMod)
|
|
m_mTree[nId].m_nTop = -2 ;
|
|
}
|
|
else if ( nEdge == 3) {
|
|
if ( m_mTree[nId].m_nLeft == -2) {
|
|
m_mTree[nId].m_nLeft = -1 ;
|
|
bMod = true ;
|
|
}
|
|
GetLeftNeigh( nId, vNeigh) ;
|
|
if ( bMod)
|
|
m_mTree[nId].m_nLeft = -2 ;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
Tree::GetHeightLeaves( int nId, INTVECTOR& vnLeaves, int d) const
|
|
{
|
|
if ( nId == -1 && m_mTree.at( -1).IsLeaf()) {
|
|
vnLeaves.push_back( -1) ;
|
|
return 0 ;
|
|
}
|
|
else {
|
|
if ( vnLeaves.empty()) {
|
|
if ( m_mTree.at( nId).IsLeaf())
|
|
return d ;
|
|
else {
|
|
vnLeaves.push_back( m_mTree.at( nId).m_nChild1) ;
|
|
vnLeaves.push_back( m_mTree.at( nId).m_nChild2) ;
|
|
if ( ! m_mTree.at( m_mTree.at( nId).m_nChild1).IsLeaf() || ! m_mTree.at( m_mTree.at( nId).m_nChild2).IsLeaf())
|
|
// almeno un child non è leaf quindi devo richiamare ricorsivamente questa funzione sui child in questione
|
|
d = GetHeightLeaves( nId, vnLeaves, m_mTree.at( m_mTree.at( nId).m_nChild1).m_nDepth) ;
|
|
}
|
|
}
|
|
else {
|
|
for ( int j = 0 ; j < int( vnLeaves.size()) ; ++ j) {
|
|
int i = vnLeaves.at( j) ;
|
|
if ( m_mTree.at( i).IsLeaf()) {
|
|
continue ;
|
|
}
|
|
else {
|
|
// se la cella non è leaf la tolgo dal vettore delle foglie e aggiungo invece i suoi child
|
|
vnLeaves.erase( remove( vnLeaves.begin(),vnLeaves.end(),i)) ;
|
|
-- j ;
|
|
vnLeaves.push_back( m_mTree.at( i).m_nChild1) ;
|
|
vnLeaves.push_back( m_mTree.at( i).m_nChild2) ;
|
|
d = max( d, m_mTree.at( m_mTree.at( i).m_nChild1).m_nDepth) ;
|
|
}
|
|
}
|
|
return d ;
|
|
}
|
|
return ( d - m_mTree.at( nId).m_nDepth) ;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
Tree::GetDepth( int nId, int nRef = -2) const
|
|
{
|
|
int i = 0 ;
|
|
int nCell = nId ;
|
|
while ( m_mTree.at( nCell).m_nParent != nRef) {
|
|
nCell = m_mTree.at( nCell).m_nParent ;
|
|
++ i ;
|
|
}
|
|
return i ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::GetPolygons( POLYLINEMATRIX& vvPolygons, POLYLINEMATRIX& vvPolygons3d, vector<ICRVCOMPOPOVECTOR>& vvCCEdges3D, ICRVCOMPOPOVECTOR& vCCLoops)
|
|
{
|
|
POLYLINEVECTOR vPolygonsBasic ;
|
|
POLYLINEVECTOR vPolygonsCorrected ;
|
|
POLYLINEVECTOR vPolygonsBasic3d ;
|
|
if ( ! m_bTrimmed) {
|
|
vvPolygons.clear() ;
|
|
|
|
GetPolygonsBasic( vPolygonsBasic, vPolygonsCorrected, vPolygonsBasic3d) ;
|
|
if (vPolygonsBasic.empty())
|
|
return false ;
|
|
int c = 0;
|
|
for ( PolyLine& pl : vPolygonsCorrected) {
|
|
vvPolygons.emplace_back() ;
|
|
vvPolygons.back().emplace_back(std::move(pl)) ;
|
|
vvPolygons3d.emplace_back() ;
|
|
vvPolygons3d.back().emplace_back(std::move(vPolygonsBasic3d[c])) ;
|
|
++c ;
|
|
}
|
|
}
|
|
// trimmata
|
|
else {
|
|
GetPolygonsBasic( vPolygonsBasic, vPolygonsCorrected, vPolygonsBasic3d) ;
|
|
if (vPolygonsBasic.empty())
|
|
return false ;
|
|
// aggiungo 4 elementi al vettore che contiene ciò che resta degli edge dopo il trim
|
|
m_vCEdge2D.clear() ;
|
|
for ( int i = 0 ; i < 4 ; ++i) {
|
|
m_vCEdge2D.emplace_back() ;
|
|
m_vCEdge2D.back().second.Init( false, EPS_SMALL, 1) ;
|
|
}
|
|
// percorro i loop, trovo le intersezioni con le celle e le categorizzo
|
|
if ( ! TraceLoopLabelCell( vPolygonsBasic))
|
|
return false ;
|
|
// scorro sulle celle e costruisco i poligoni
|
|
int nCells = int( vPolygonsBasic.size()) ;
|
|
for ( int i = 0 ; i < nCells ; ++ i) {
|
|
// costruisco i poligoni partendo dal vettore delle intersezioni, come spiegato a pag15 di Cripps
|
|
int nId = m_vnLeaves[i] ;
|
|
if ( m_mTree[nId].m_nFlag == 4) {
|
|
// vettore dei poligoni ( loop) della cella nId
|
|
vvPolygons.emplace_back() ;
|
|
vvPolygons.back().push_back(std::move(vPolygonsCorrected[i])) ;
|
|
vvPolygons3d.emplace_back() ;
|
|
vvPolygons3d.back().push_back(std::move(vPolygonsBasic3d[i])) ;
|
|
}
|
|
else if ( m_mTree[nId].m_nFlag == 0)
|
|
continue ;
|
|
else {
|
|
// vettore in cui salvo il chunk di appartenenza di ogni loop che attraversa la cella
|
|
INTVECTOR vnParentChunk ;
|
|
// vettore in cui salvo i loop che non appartengono al poligono che sto cotruendo nel ciclo attuale e da cui ripasserò dopo
|
|
INTVECTOR vToCheck( (int) m_mTree[nId].m_vInters.size()) ;
|
|
iota( vToCheck.begin(), vToCheck.end(), 0) ;
|
|
// numero di poligoni aggiunti
|
|
int nPoly = 0 ;
|
|
// scorro sui vettori intersezione della cella nId e sui suoi vertici
|
|
// in questo for analizzo solo i loop che tagliano la cella
|
|
while ( ! vToCheck.empty()) {
|
|
int nPolyBefore = nPoly ;
|
|
PolyLine pl3d ;
|
|
CreateCellPolygons( i, vvPolygons, vvPolygons3d, vToCheck, nPoly, vnParentChunk, vPolygonsCorrected[i], vPolygonsBasic3d[i]) ;
|
|
if ( nPolyBefore == nPoly)
|
|
break ;
|
|
}
|
|
// ora analizzo anche i loop che sono contenuti nella cella
|
|
CreateIslandAndHoles( i, vvPolygons, vvPolygons3d, nPoly, vnParentChunk, vPolygonsCorrected[i], vPolygonsBasic3d[i]) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// aggiorno gli edge 3d e i loop della superficie
|
|
GetEdges3D( vvCCEdges3D, vPolygonsCorrected) ;
|
|
GetSplitLoops( vCCLoops) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::GetPolygonsBasic( POLYLINEVECTOR& vPolygonsBasic, POLYLINEVECTOR& vPolygonsCorrected, POLYLINEVECTOR& vPolygons3d)
|
|
{
|
|
// condizioni per il calcolo dei poligoni di base
|
|
PNTVECTOR vVertices ;
|
|
PNTVECTOR vVerticesCorr ;
|
|
PNTVECTOR vVertices3d ;
|
|
|
|
INTVECTOR vNeigh ;
|
|
// setto le celle che sono sul LeftEdge e sul TopEdge
|
|
if ( m_bClosedU) {
|
|
GetRootNeigh( 1, vNeigh) ;
|
|
for ( int k : vNeigh) {
|
|
m_mTree[k].m_bOnLeftEdge = true ;
|
|
}
|
|
vNeigh.clear() ;
|
|
}
|
|
if ( m_bClosedV) {
|
|
GetRootNeigh( 0, vNeigh) ;
|
|
for ( int k : vNeigh) {
|
|
m_mTree[k].m_bOnTopEdge = true ;
|
|
}
|
|
}
|
|
bool bBottomRight , bTopLeft ;
|
|
// scorro lungo tutte le celle leaves e oltre agli angoli della cella aggiungo alla polyline della cella anche i vertici, delle celle adiacenti,
|
|
// che sono sui lati della cella corrente
|
|
// N.B. :i poligoni sono costruiti a partire dal ptBL !!!
|
|
int c = 0 ;
|
|
for ( int nId : m_vnLeaves) {
|
|
Cell& cell = m_mTree.at( nId) ;
|
|
vVertices.clear() ;
|
|
vVertices3d.clear() ;
|
|
vNeigh.clear() ;
|
|
vVertices.push_back( cell.GetBottomLeft()) ;
|
|
Point3d pt3d ; GetPoint( cell.GetBottomLeft().x, cell.GetBottomLeft().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
INTVECTOR vnVert ;
|
|
BOOLVECTOR vbBonusVert(4) ;
|
|
std::fill( vbBonusVert.begin(), vbBonusVert.end(), false) ;
|
|
vnVert.push_back( int(vVertices.size()) - 1) ;
|
|
GetBottomNeigh( nId, vNeigh) ;
|
|
Point3d ptP00, ptP10, ptP11, ptP01 ;
|
|
GetPoint( cell.GetBottomLeft().x, cell.GetBottomLeft().y, ptP00) ;
|
|
GetPoint( cell.GetTopRight().x, cell.GetBottomLeft().y, ptP10) ;
|
|
GetPoint( cell.GetTopRight().x, cell.GetTopRight().y, ptP11) ;
|
|
GetPoint( cell.GetBottomLeft().x, cell.GetTopRight().y, ptP01) ;
|
|
|
|
// aggiungo i vertici che sono sul lato bottom, solo se ho più di un vicino bottom
|
|
if ( vNeigh.size() > 1){
|
|
// se la superficie è chiusa lungo il parametro V e le celle vicine bottom sono sul lato Top
|
|
// devo aggiungere i vertici tenendo conto della periodicità dello spazio parametrico.
|
|
if ( m_bClosedV && m_mTree.at(vNeigh[0]).m_bOnTopEdge) {
|
|
for ( int j : vNeigh) {
|
|
Cell& cNeigh = m_mTree.at(j) ;
|
|
Point3d pt( cNeigh.GetTopRight().x, cell.GetBottomLeft().y) ;
|
|
vVertices.push_back( pt) ;
|
|
Point3d pt3d ; GetPoint( cNeigh.GetTopRight().x, cNeigh.GetTopRight().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
}
|
|
}
|
|
else {
|
|
for ( int j : vNeigh) {
|
|
Cell& cNeigh = m_mTree.at(j) ;
|
|
vVertices.push_back( cNeigh.GetTopRight()) ;
|
|
Point3d pt3d ; GetPoint( cNeigh.GetTopRight().x, cNeigh.GetTopRight().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
}
|
|
}
|
|
bBottomRight = true ;
|
|
vbBonusVert[2] = true ;
|
|
}
|
|
else
|
|
bBottomRight = false ;
|
|
vNeigh.clear() ;
|
|
GetRightNeigh ( nId, vNeigh) ;
|
|
// aggiungo i vertici che sono sul lato right, solo se ho più di un vicino right
|
|
if ( vNeigh.size() > 1){
|
|
// se la superficie è chiusa lungo il parametro U e le celle vicine right sono sul lato Left
|
|
// devo aggiungere i vertici tenendo conto della periodicità dello spazio parametrico.
|
|
vnVert.push_back( int(vVertices.size())) ;
|
|
if ( m_bClosedU && m_mTree.at( vNeigh[0]).m_bOnLeftEdge ) {
|
|
for ( int j : vNeigh) {
|
|
Cell& cNeigh = m_mTree.at(j) ;
|
|
Point3d pt( cell.GetTopRight().x, cNeigh.GetBottomLeft().y) ;
|
|
vVertices.push_back( pt) ;
|
|
Point3d pt3d ; GetPoint( cNeigh.GetBottomLeft().x, cNeigh.GetBottomLeft().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
}
|
|
}
|
|
else {
|
|
for ( int j : vNeigh) {
|
|
Cell& cNeigh = m_mTree.at(j) ;
|
|
vVertices.push_back( cNeigh.GetBottomLeft()) ;
|
|
Point3d pt3d ; GetPoint( cNeigh.GetBottomLeft().x, cNeigh.GetBottomLeft().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
}
|
|
}
|
|
vbBonusVert[3] = true ;
|
|
}
|
|
// se non l'ho già aggiunto tramite i vicini bottom aggiungo il punto bottom right
|
|
else if ( ! bBottomRight) {
|
|
Point3d ptBr( cell.GetTopRight().x, cell.GetBottomLeft().y) ;
|
|
vVertices.push_back( ptBr) ;
|
|
Point3d pt3d ; GetPoint( cell.GetTopRight().x, cell.GetBottomLeft().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
vnVert.push_back( int(vVertices.size()) - 1) ;
|
|
}
|
|
vNeigh.clear() ;
|
|
vVertices.push_back( cell.GetTopRight()) ;
|
|
pt3d = ORIG ; GetPoint( cell.GetTopRight().x, cell.GetTopRight().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
vnVert.push_back( int(vVertices.size()) - 1) ;
|
|
GetTopNeigh ( nId, vNeigh) ;
|
|
std::reverse( vNeigh.begin(), vNeigh.end()) ;
|
|
// aggiungo i vertici che sono sul lato top, solo se ho più di un vicino top
|
|
if ( ! vNeigh.empty() && vNeigh.size() != 1) {
|
|
// se la superficie è chiusa lungo il parametro V e la cella è sul lato top
|
|
// devo aggiungere i vertici tenendo conto della periodicità dello spazio parametrico.
|
|
vnVert.push_back( int(vVertices.size())) ;
|
|
if ( m_bClosedV && cell.m_bOnTopEdge) {
|
|
for ( int j : vNeigh) {
|
|
Point3d pt( m_mTree.at( j).GetBottomLeft().x, cell.GetTopRight().y) ;
|
|
vVertices.push_back( pt) ;
|
|
Cell& cNeigh = m_mTree.at(j) ;
|
|
Point3d pt3d ; GetPoint( cNeigh.GetBottomLeft().x, cNeigh.GetBottomLeft().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
}
|
|
}
|
|
else {
|
|
for ( int j : vNeigh) {
|
|
vVertices.push_back( m_mTree.at( j).GetBottomLeft()) ;
|
|
Cell& cNeigh = m_mTree.at(j) ;
|
|
Point3d pt3d ; GetPoint( cNeigh.GetBottomLeft().x, cNeigh.GetBottomLeft().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
}
|
|
}
|
|
bTopLeft = true ;
|
|
vbBonusVert[0] = true ;
|
|
}
|
|
else
|
|
bTopLeft = false ;
|
|
vNeigh.clear() ;
|
|
GetLeftNeigh ( nId, vNeigh) ;
|
|
std::reverse( vNeigh.begin(), vNeigh.end()) ;
|
|
// aggiungo i vertici che sono sul lato left, solo se ho più di un vicino left
|
|
if ( vNeigh.size() > 1) {
|
|
// se la superficie è chiusa lungo il parametro U e la cella è sul lato left
|
|
// devo aggiungere i vertici tenendo conto della periodicità dello spazio parametrico.
|
|
vnVert.push_back( int(vVertices.size())) ;
|
|
if ( m_bClosedU && cell.m_bOnLeftEdge) {
|
|
for ( int j : vNeigh) {
|
|
Cell& cNeigh = m_mTree.at(j) ;
|
|
Point3d pt( cell.GetBottomLeft().x, cNeigh.GetTopRight().y) ;
|
|
vVertices.push_back( pt) ;
|
|
Point3d pt3d ; GetPoint( cNeigh.GetTopRight().x, cNeigh.GetTopRight().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
}
|
|
}
|
|
else {
|
|
for ( int j : vNeigh) {
|
|
Cell& cNeigh = m_mTree.at(j) ;
|
|
vVertices.push_back( cNeigh.GetTopRight()) ;
|
|
Point3d pt3d ; GetPoint( cNeigh.GetTopRight().x, cNeigh.GetTopRight().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
}
|
|
}
|
|
vbBonusVert[1] = true ;
|
|
}
|
|
// se non l'ho già aggiunto tramite i vicini top aggiungo il punto top left
|
|
else if ( ! bTopLeft) {
|
|
Point3d ptTl( cell.GetBottomLeft().x, cell.GetTopRight().y) ;
|
|
vVertices.push_back( ptTl) ;
|
|
Point3d pt3d ; GetPoint( cell.GetBottomLeft().x, cell.GetTopRight().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
vnVert.push_back( int(vVertices.size()) - 1) ;
|
|
}
|
|
vNeigh.clear() ;
|
|
vVertices.push_back( cell.GetBottomLeft()) ;
|
|
pt3d = ORIG ; GetPoint( cell.GetBottomLeft().x, cell.GetBottomLeft().y, pt3d) ;
|
|
vVertices3d.push_back( pt3d) ;
|
|
vnVert.push_back( int(vVertices.size()) - 1) ;
|
|
|
|
BOOLVECTOR vbKeepPoint( vVertices.size()) ;
|
|
std::fill( vbKeepPoint.begin(), vbKeepPoint.end(), true) ;
|
|
|
|
// se non è trimmata aggiusto subito il vettore dei vertici, sennò i salvo le informazioni per quando creerò i poligoni trimmato
|
|
vVerticesCorr = vVertices ;
|
|
// ora devo controllare se uno dei lati della cella è collassato in un punto.
|
|
// se così, devo guardare se sui due lati adiacenti a quello di polo ho messo dei punti extra
|
|
// se ne ho messi solo su uno dei due allora devo togliere il vertice dell'altro lato
|
|
if ( AreSamePointApprox(ptP00, ptP10)) {
|
|
// sennò devo togliere l'estremo del lato su cui NON ho punti bonus
|
|
if ( vbBonusVert[1] && ! vbBonusVert[3]) {// lati contati a partire da quello sopra in senso CCW
|
|
if ( ! m_bTrimmed) {
|
|
vVerticesCorr.erase(vVerticesCorr.begin() + vnVert[1]) ; // vertici della cella contati a partire da ptBL in senso CCW
|
|
vVertices3d.erase(vVertices3d.begin() + vnVert[1]) ;
|
|
}
|
|
vbKeepPoint[vnVert[1]] = false ;
|
|
cell.m_nVertToErase = 1 ; // ptBr
|
|
}
|
|
else if ( ! vbBonusVert[1] && vbBonusVert[3] ) {
|
|
if ( ! m_bTrimmed) {
|
|
// dovrei eliminare ptBL, quindi devo eliminare il primo e l'ultimo punto della polyline e poi chiuderla con quello che era il penultimo punto
|
|
vVerticesCorr.pop_back() ;
|
|
vVerticesCorr[0] = vVerticesCorr.end()[-1] ;
|
|
vVertices3d.pop_back() ;
|
|
vVertices3d[0] = vVertices3d.end()[-1] ;
|
|
}
|
|
vbKeepPoint[0] = false ;
|
|
vbKeepPoint.back() = false ;
|
|
cell.m_nVertToErase = 0 ; // ptBL
|
|
}
|
|
// se non ho bonus su nessuno dei due
|
|
else {
|
|
// ne scelgo uno dei due da togliere
|
|
vbKeepPoint[vnVert[1]] = false ;
|
|
cell.m_nVertToErase = 1 ; // ptBr
|
|
}
|
|
}
|
|
if ( AreSamePointApprox(ptP10, ptP11)) {
|
|
// sennò devo togliere l'estremo del lato su cui NON ho punti bonus
|
|
if ( vbBonusVert[0] && ! vbBonusVert[2]){ // lati contati a partire da quello sopra in senso CCW
|
|
if ( ! m_bTrimmed) {
|
|
vVerticesCorr.erase(vVerticesCorr.begin() + vnVert[1]) ; // vertici della cella contati a partire da ptBL in senso CCW
|
|
vVertices3d.erase(vVertices3d.begin() + vnVert[1]) ;
|
|
}
|
|
vbKeepPoint[vnVert[1]] = false ;
|
|
cell.m_nVertToErase = 1 ; // ptBr
|
|
}
|
|
else if ( ! vbBonusVert[0] && vbBonusVert[2] ){
|
|
if ( ! m_bTrimmed) {
|
|
vVerticesCorr.erase(vVerticesCorr.begin() + vnVert[2]) ;
|
|
vVertices3d.erase(vVertices3d.begin() + vnVert[2]) ;
|
|
}
|
|
vbKeepPoint[vnVert[2]] = false ;
|
|
cell.m_nVertToErase = 2 ; // ptTR
|
|
}
|
|
// se non ho bonus su nessuno dei due
|
|
else {
|
|
// ne scelgo uno dei due da togliere
|
|
vbKeepPoint[vnVert[2]] = false ;
|
|
cell.m_nVertToErase = 2 ; // ptTR
|
|
}
|
|
}
|
|
if ( AreSamePointApprox(ptP11, ptP01)) {
|
|
// sennò devo togliere l'estremo del lato su cui NON ho punti bonus
|
|
if ( vbBonusVert[1] && ! vbBonusVert[3]){ // lati contati a partire da quello sopra in senso CCW
|
|
if ( ! m_bTrimmed) {
|
|
vVerticesCorr.erase(vVerticesCorr.begin() + vnVert[2]) ; // vertici della cella contati a partire da ptBL in senso CCW
|
|
vVertices3d.erase(vVertices3d.begin() + vnVert[2]) ;
|
|
}
|
|
vbKeepPoint[vnVert[2]] = false ;
|
|
cell.m_nVertToErase = 2 ; // ptTR
|
|
}
|
|
else if ( ! vbBonusVert[1] && vbBonusVert[3]) {
|
|
if ( ! m_bTrimmed) {
|
|
vVerticesCorr.erase(vVerticesCorr.begin() + vnVert[3]) ;
|
|
vVertices3d.erase(vVertices3d.begin() + vnVert[3]) ;
|
|
}
|
|
vbKeepPoint[vnVert[3]] = false ;
|
|
cell.m_nVertToErase = 3 ; // ptTl
|
|
}
|
|
// se non ho bonus su nessuno dei due
|
|
else {
|
|
// ne scelgo uno dei due da togliere
|
|
vbKeepPoint[vnVert[3]] = false ;
|
|
cell.m_nVertToErase = 3 ; // ptTl
|
|
}
|
|
}
|
|
if ( AreSamePointApprox(ptP01, ptP00)) {
|
|
// sennò devo togliere l'estremo del lato su cui NON ho punti bonus
|
|
if ( vbBonusVert[0] && ! vbBonusVert[2]) { // lati contati a partire da quello sopra in senso CCW
|
|
if ( ! m_bTrimmed) {
|
|
// dovrei eliminare ptBL, quindi devo eliminare il primo e l'ultimo punto della polyline e poi chiuderla con quello che era il penultimo punto
|
|
vVerticesCorr.pop_back() ;
|
|
vVerticesCorr[0] = vVerticesCorr.end()[-1] ;
|
|
vVertices3d.pop_back() ;
|
|
vVertices3d[0] = vVertices3d.end()[-1] ;
|
|
}
|
|
vbKeepPoint[0] = false ;
|
|
vbKeepPoint.back() = false ;
|
|
cell.m_nVertToErase = 0 ; // ptBL
|
|
}
|
|
else if ( ! vbBonusVert[0] && vbBonusVert[2]) {
|
|
if ( ! m_bTrimmed) {
|
|
vVerticesCorr.erase(vVerticesCorr.begin() + vnVert[3]) ;
|
|
vVertices3d.erase(vVertices3d.begin() + vnVert[3]) ;
|
|
}
|
|
vbKeepPoint[vnVert[3]] = false ;
|
|
cell.m_nVertToErase = 3 ; // ptTl
|
|
}
|
|
// se non ho bonus su nessuno dei due
|
|
else {
|
|
// ne scelgo uno dei due da togliere
|
|
vbKeepPoint[vnVert[3]] = false ;
|
|
cell.m_nVertToErase = 3 ; // ptTl
|
|
}
|
|
}
|
|
|
|
if ( ! m_bTrimmed) {
|
|
// se ho una cella con vicino dello stesso grado ( quindi il poligono ha solo 5 punti) controllo la curvatura nella cella e
|
|
// se necessario cambio l'ordine dei vertici per scegliere la diagonale di split migliore
|
|
if ( vVertices.size() == 5) {
|
|
Point3d ptPSrf ;
|
|
double dU, dV ;
|
|
dU = ( cell.GetBottomLeft().x + cell.GetTopRight().x) / 2 / SBZ_TREG_COEFF ;
|
|
dV = ( cell.GetBottomLeft().y + cell.GetTopRight().y) / 2 / SBZ_TREG_COEFF ;
|
|
GetPoint( dU, dV, ptPSrf) ;
|
|
Point3d ptP00P11 = ( ptP00 + ptP11) / 2 ;
|
|
Point3d ptP10P01 = ( ptP10 + ptP01) / 2 ;
|
|
// ho la curvatura maggiore sulla diagonale tra P10 e P01, ruoto l'ordine dei vertici, in modo che triangulate prenda la diagonale giusta
|
|
if ( Dist( ptP00P11, ptPSrf) + EPS_SMALL > Dist( ptP10P01, ptPSrf)) {
|
|
rotate( vVertices.begin(), vVertices.begin() + 1,vVertices.end()) ;
|
|
vVertices.back() = vVertices.at( 0) ;
|
|
rotate( vVertices3d.begin(), vVertices3d.begin() + 1,vVertices3d.end()) ;
|
|
vVertices3d.back() = vVertices3d.at( 0) ;
|
|
rotate( vbKeepPoint.begin(), vbKeepPoint.begin() + 1,vbKeepPoint.end()) ;
|
|
vbKeepPoint.back() = vbKeepPoint.at( 0) ;
|
|
rotate( vVerticesCorr.begin(), vVerticesCorr.begin() + 1,vVerticesCorr.end()) ;
|
|
vVerticesCorr.back() = vVerticesCorr.at( 0) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
cell.AddPoly( c) ;
|
|
// aggiorno il contatore dei poligoni
|
|
c += 1 ;
|
|
vPolygonsBasic.emplace_back() ;
|
|
vPolygonsCorrected.emplace_back() ;
|
|
vPolygons3d.emplace_back() ;
|
|
int nVertCorr = int(vVerticesCorr.size());
|
|
for ( int i = 0 ; i < (int) vVertices.size() ; ++i) {
|
|
int dPar = i ;
|
|
if( ! vbKeepPoint[i])
|
|
dPar = -1 ;
|
|
vPolygonsBasic.back().AddUPoint( dPar, vVertices.at( i)) ;
|
|
if( i < nVertCorr){
|
|
vPolygonsCorrected.back().AddUPoint( dPar, vVerticesCorr.at( i)) ;
|
|
vPolygons3d.back().AddUPoint( dPar, vVertices3d.at( i)) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
Tree::ResetTree( void)
|
|
{
|
|
// setto tutte le foglie a Processed = false
|
|
for ( int nC : m_vnLeaves) {
|
|
m_mTree[nC].SetProcessed( false) ;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
INTVECTOR
|
|
Tree::FindCell( const Point3d& ptToAssign, const CurveLine& clTrim, bool bRecurs) const
|
|
{
|
|
INTVECTOR nCells ;
|
|
int nId = -1 ;
|
|
// se fallisce ritorna un vettore vuoto
|
|
// verifico che il punto sia all'interno dello spazio parametrico
|
|
// allargo i bordi in modo da tenere anche i punti sul bordo dello spazio parametrico
|
|
if ( ptToAssign.x < m_mTree.at( -1).GetBottomLeft().x - EPS_SMALL || ptToAssign.x > m_mTree.at( -1).GetTopRight().x + EPS_SMALL||
|
|
ptToAssign.y < m_mTree.at( -1).GetBottomLeft().y - EPS_SMALL || ptToAssign.y > m_mTree.at( -1).GetTopRight().y + EPS_SMALL) {
|
|
//nCells.push_back( - 2) ;
|
|
return nCells ;
|
|
}
|
|
|
|
// se ho diviso preliminarmente le patches e in uno dei due parametri ho un numero dispari di patches devo individuare a mano la cella parent
|
|
// in cui individuare la foglia giusta
|
|
if ( (m_bSplitPatches && ( m_nSpanU > 1 || m_nSpanV > 1)) || m_bTestMode) {
|
|
INTVECTOR nParents = FindCell( ptToAssign, clTrim, m_vnParents) ;
|
|
if ( nParents.empty())
|
|
return nCells ;
|
|
nId = nParents.back() ;
|
|
if ( m_bTestMode ) {
|
|
nCells.push_back(nId) ;
|
|
return nCells ;
|
|
}
|
|
}
|
|
// individuo la foglia in cui ho lo start del loop
|
|
while ( ! m_mTree.at( nId).IsLeaf()) {
|
|
if ( m_mTree.at( nId).IsSplitVert()) {
|
|
double dMid = ( m_mTree.at( nId).GetBottomLeft().x + m_mTree.at( nId).GetTopRight().x) / 2 ;
|
|
if ( ptToAssign.x < dMid) {
|
|
nId = m_mTree.at( nId).m_nChild1 ;
|
|
}
|
|
else {
|
|
nId = m_mTree.at( nId).m_nChild2 ;
|
|
}
|
|
}
|
|
else {
|
|
double dMid = ( m_mTree.at( nId).GetBottomLeft().y + m_mTree.at( nId).GetTopRight().y) / 2 ;
|
|
if ( ptToAssign.y < dMid) {
|
|
nId = m_mTree.at( nId).m_nChild2 ;
|
|
}
|
|
else {
|
|
nId = m_mTree.at( nId).m_nChild1 ;
|
|
}
|
|
}
|
|
}
|
|
|
|
nCells.push_back( nId) ;
|
|
if ( ! bRecurs) {
|
|
// se sono in un vertice o su un lato devo controllare di aver trovato la cella giusta
|
|
Point3d ptBr( m_mTree.at( nId).GetTopRight().x , m_mTree.at( nId).GetBottomLeft().y) ;
|
|
Point3d ptTl( m_mTree.at( nId).GetBottomLeft().x , m_mTree.at( nId).GetTopRight().y) ;
|
|
if ( abs( ptToAssign.x - ptTl.x) < EPS_SMALL || abs( ptToAssign.x - ptBr.x) < EPS_SMALL ||
|
|
abs( ptToAssign.y - ptTl.y) < EPS_SMALL || abs( ptToAssign.y - ptBr.y) < EPS_SMALL)
|
|
{
|
|
Vector3d vtDir ;
|
|
clTrim.GetStartDir( vtDir) ;
|
|
// proseguo lungo la curva di trim di EPS_SMALL
|
|
Point3d ptToAssignPlus = ptToAssign + vtDir * EPS_SMALL ;
|
|
// se la curva di trim è praticamente parallela ad un lato allora giro a destra, perché voglio considerare intersecate le celle esterne ( sul bordo) al loop
|
|
if ( abs( vtDir.x) > 1 - EPS_SMALL || abs( vtDir.y) > 1 - EPS_SMALL) {
|
|
Vector3d vtDirDX = vtDir ; vtDirDX.Rotate( Z_AX, 90) ;
|
|
ptToAssignPlus = ptToAssignPlus + vtDir * EPS_SMALL ;
|
|
// controllo di non essere uscito dallo spazio parametrico ed eventualmente giro a sinistra
|
|
if ( ptToAssignPlus.x < m_mTree.at( -1).GetBottomLeft().x - EPS_SMALL || ptToAssignPlus.x > m_mTree.at( -1).GetTopRight().x + EPS_SMALL||
|
|
ptToAssignPlus.y < m_mTree.at( -1).GetBottomLeft().y - EPS_SMALL || ptToAssignPlus.y > m_mTree.at( -1).GetTopRight().y + EPS_SMALL) {
|
|
// rispetto al punto di partenza avanzo lungo la curva di trim
|
|
ptToAssignPlus = ptToAssign + vtDir * EPS_SMALL ;
|
|
Vector3d vtDirSX = vtDir ; vtDirSX.Rotate( Z_AX, -90) ;
|
|
// e poi giro a sinistra
|
|
ptToAssignPlus = ptToAssign + vtDir * EPS_SMALL ;
|
|
}
|
|
}
|
|
INTVECTOR nCellsSave = nCells ;
|
|
nCells = FindCell( ptToAssignPlus, clTrim, true) ;
|
|
if ( nCells.empty()) {
|
|
// se nonostante tutto non trovo nulla allora tengo il risultato ottenuto scorrendo l'albero
|
|
nCells = nCellsSave ;
|
|
}
|
|
}
|
|
}
|
|
return nCells ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
INTVECTOR
|
|
Tree::FindCell( const Point3d& ptToAssign, const CurveLine& cl, INTVECTOR vCells, bool bRecurs) const
|
|
{
|
|
// se non trova nulla restituisce un vettore vuoto
|
|
// restituisce sempre solo una cella
|
|
|
|
// questo punto va trovato con precisione, perché se seleziono la cella sbagliata si incasina tutto
|
|
INTVECTOR nCells ;
|
|
int nId = -1 ;
|
|
for ( int nCell : vCells) {
|
|
if ( ptToAssign.x > m_mTree.at( nCell).GetBottomLeft().x && ptToAssign.x < m_mTree.at( nCell).GetTopRight().x &&
|
|
ptToAssign.y > m_mTree.at( nCell).GetBottomLeft().y && ptToAssign.y < m_mTree.at( nCell).GetTopRight().y) {
|
|
nId = nCell ;
|
|
nCells.push_back( nId) ;
|
|
}
|
|
}
|
|
// se non ho trovato nulla vuol dire che sono su un vertice o su un lato
|
|
if ( nCells.empty() && ! bRecurs) {
|
|
int nEdge = -1 ;
|
|
for ( int nCell : vCells) {
|
|
OnWhichEdge( nCell,ptToAssign, nEdge) ;
|
|
if ( nEdge != -1)
|
|
break ;
|
|
}
|
|
Vector3d vtDir ;
|
|
cl.GetStartDir( vtDir) ;
|
|
Point3d ptIntersPlus = ptToAssign ;
|
|
// N.B. DESTRA e SINISTRA SONO SEMPRE RIFERITE al guardare nella direzione del vettore di partenza della curva di trim
|
|
// potrei essere su un lato
|
|
if ( nEdge == 0 || nEdge == 1 || nEdge == 2 || nEdge == 3) {
|
|
// se è orientato con il lato mi sposto a destra
|
|
if ( ( nEdge == 0 || nEdge == 2 ) && abs(vtDir.x) > 1 - EPS_SMALL/100 ) {
|
|
Vector3d vtDirDX = vtDir ; vtDirDX.Rotate( Z_AX, -90) ;
|
|
ptIntersPlus = ptIntersPlus + vtDirDX * EPS_SMALL ;
|
|
}
|
|
else if ( (nEdge == 0 || nEdge == 2) && abs(vtDir.y) > EPS_SMALL/100)
|
|
ptIntersPlus.y += (vtDir.y > 0 ? 1 : -1) * EPS_SMALL ;
|
|
else if ( ( nEdge == 1 || nEdge == 3 ) && abs(vtDir.y) > 1 - EPS_SMALL/100 ) {
|
|
Vector3d vtDirDX = vtDir ; vtDirDX.Rotate( Z_AX, -90) ;
|
|
ptIntersPlus = ptIntersPlus + vtDirDX * EPS_SMALL ;
|
|
}
|
|
else if ( (nEdge == 1 || nEdge == 3) && abs(vtDir.x) > EPS_SMALL/100)
|
|
ptIntersPlus.x += (vtDir.x > 0 ? 1 : -1) * EPS_SMALL ;
|
|
}
|
|
// o in un vertice
|
|
else if ( nEdge == 4 || nEdge == 5 || nEdge == 6 || nEdge == 7) {
|
|
// se non è orientato con gli assi procedo con la direzione del trim
|
|
if ( abs(vtDir.x) < 1 - EPS_SMALL/100 && abs(vtDir.y) < 1 - EPS_SMALL/100 )
|
|
ptIntersPlus = ptIntersPlus + vtDir * EPS_SMALL ;
|
|
// altrimenti ruoto a destra
|
|
else {
|
|
Vector3d vtDirDX = vtDir ; vtDirDX.Rotate( Z_AX, -45) ;
|
|
ptIntersPlus = ptIntersPlus + vtDirDX * EPS_SMALL ;
|
|
}
|
|
}
|
|
//
|
|
else
|
|
return nCells ;
|
|
// controllo di non essere uscito dallo spazio parametrico (in tal caso riparto da ptToAssign e mi muovo a sinistra)
|
|
if ( ptIntersPlus.x < m_mTree.at( -1).GetBottomLeft().x || ptIntersPlus.x > m_mTree.at( -1).GetTopRight().x ||
|
|
ptIntersPlus.y < m_mTree.at( -1).GetBottomLeft().y || ptIntersPlus.y > m_mTree.at( -1).GetTopRight().y) {
|
|
Vector3d vtDirSX = vtDir ; vtDirSX.Rotate(Z_AX, 45) ;
|
|
ptIntersPlus = ptToAssign + vtDirSX * EPS_SMALL ;
|
|
}
|
|
// rilancio la rigida ricerca
|
|
nCells = FindCell( ptIntersPlus, cl, vCells, true) ;
|
|
}
|
|
|
|
return nCells ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::UpdateSplitLoop( ICurveComposite* pCC, Point3d& pt)
|
|
{
|
|
Point3d ptLast ;
|
|
bool bAdded = false ;
|
|
if ( ! pCC->GetEndPoint( ptLast)) {
|
|
// potrei avere una compo vuota o con solo un punto
|
|
// se non riesco ad aggiungere una linea allora era una compo vuota
|
|
if ( ! pCC->AddLine( pt))
|
|
pCC->AddPoint( pt) ;
|
|
bAdded = true ;
|
|
pCC->GetStartPoint( ptLast) ;
|
|
}
|
|
if( pCC->IsValid()) {
|
|
if ( ! bAdded)
|
|
pCC->AddLine( pt) ;
|
|
Vector3d vtDir = pt - ptLast ;
|
|
if ( ! vtDir.Normalize())
|
|
return false ;
|
|
// se sono allineato con gli edge della cella ROOT
|
|
int nEdge = -1 ;
|
|
if ( abs( vtDir.x) > 1 - EPS_SMALL) {
|
|
if ( pt.y < EPS_SMALL)
|
|
nEdge = 2 ;
|
|
else if ( m_nSpanV * SBZ_TREG_COEFF - pt.y < EPS_SMALL)
|
|
nEdge = 0 ;
|
|
}
|
|
else if ( abs( vtDir.y) > 1 - EPS_SMALL) {
|
|
if ( pt.x < EPS_SMALL )
|
|
nEdge = 1 ;
|
|
else if ( m_nSpanU * SBZ_TREG_COEFF - pt.x < EPS_SMALL )
|
|
nEdge = 3 ;
|
|
}
|
|
if ( nEdge != -1) {
|
|
m_vCEdge2D[nEdge].second.AddCurve( m_vCEdge2D[nEdge].first.size() + 1, ptLast, vtDir, pt, vtDir) ;
|
|
m_vCEdge2D[nEdge].first.emplace_back( BIPOINT( ptLast, pt)) ;
|
|
}
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::TraceLoopLabelCell( const POLYLINEVECTOR& vplPolygons)
|
|
{
|
|
// l'idea è di seguire il loop di trim, passando di cella in cella, restando, quando possibile, a destra del loop.
|
|
// ( destra e sinistra sono determinate dal verso del loop).
|
|
|
|
// il valore è negativo perché voglio considerare contenuto anche un punto che sta su un lato
|
|
double dLinTol = - EPS_SMALL ;
|
|
// percorro i loop trovando le interezioni con le celle e riempiendo i vettori m_vInters delle varie celle
|
|
for ( int i = 0 ; i < (int) m_vPlApprox.size() ; ++ i) {
|
|
PolyLine plLoop = get<0>( m_vPlApprox[i]) ;
|
|
// creo la compo che aggiunge alla polyline originale degli split dove interseca le celle ( serve per ricostruire gli edge aperti della superficie)
|
|
PtrOwner<ICurveComposite> pCCLoopSplit( CreateCurveComposite()) ;
|
|
// controllo se il loop è CCW o CW
|
|
bool bCCW = get<1>( m_vPlApprox[i]) ;
|
|
// trovo in quale cella è il ptStart
|
|
Point3d ptStart ;
|
|
plLoop.GetFirstPoint( ptStart) ;
|
|
PNTULIST lPt = plLoop.GetUPointList() ;
|
|
PNTULIST:: iterator ptFirst = lPt.begin() ;
|
|
PNTULIST:: iterator ptSecond = ptFirst ;
|
|
advance( ptSecond, 1) ;
|
|
CurveLine clFirst ;
|
|
clFirst.Set( ptFirst->first, ptSecond->first) ;
|
|
// aggiorno la compo splittata
|
|
UpdateSplitLoop( pCCLoopSplit, ptFirst->first) ;
|
|
// individuo la cella da cui parte il loop
|
|
INTVECTOR nCells = FindCell( ptStart, clFirst) ;
|
|
int nId ;
|
|
if ( ! nCells.empty())
|
|
nId = nCells.back() ;
|
|
else
|
|
// il loop è risultato fuori dallo spazio parametrico // può capitare se ho una superficie trimmata con più chunk con bbox separate.
|
|
// ma potrebbe anche essere indicatore di un errore
|
|
continue ;
|
|
int nFirstCell = nId ;
|
|
// trovo quali punti della polyline sono nella cella e l'intersezione
|
|
PNTVECTOR vptInters ;
|
|
vptInters.push_back( ptStart) ;
|
|
// qui mi devo salvare quanti elementi ho già nel vettore m_vInters della cella
|
|
// per poter fare il merge con l'ultimo vptInters della curva di trim, che sarà ancora in questa cella
|
|
// e terminerà nel punto di start
|
|
Cell* pCell = &m_mTree[nId] ;
|
|
int nPass = (int) pCell->m_vInters.size() ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
// salvo il verso del loop
|
|
pCell->m_vInters.back().bCCW = bCCW ;
|
|
// salvo il chunk del loop
|
|
pCell->m_vInters.back().nChunk = m_mChunk[i] ;
|
|
bool bLoopInside = true ;
|
|
Point3d ptCurr ;
|
|
auto iter = find( m_vnLeaves.begin(), m_vnLeaves.end(), nId) ;
|
|
int nIdPolygon = distance( m_vnLeaves.begin(), iter) ;
|
|
bool bEraseNextPoint = false ;
|
|
while ( plLoop.GetNextPoint( ptCurr)) {
|
|
Point3d ptTStart, ptTEnd ;
|
|
plLoop.GetPrevPoint( ptTStart) ;
|
|
plLoop.GetNextPoint( ptTEnd) ;
|
|
CurveLine clTrim ;
|
|
clTrim.Set( ptTStart, ptTEnd) ;
|
|
// qui devo mettere una tolleranza negativa per poter tener conto anche dei punti che sono SULLA curva
|
|
while ( ! IsPointInsidePolyLine( ptCurr, vplPolygons[nIdPolygon], dLinTol)) {
|
|
// sto uscendo dalla cella, quindi cerco l'intersezione
|
|
if ( bEraseNextPoint && ! vptInters.empty()) {
|
|
vptInters.pop_back() ;
|
|
bEraseNextPoint = false ;
|
|
}
|
|
bLoopInside = false ;
|
|
// trovo l'intersezione e passo alla cella successiva. nId viene aggiornato dalla funzione FindInters
|
|
// se non trovo l'intersezione vuol dire che non sono nella cella giusta!
|
|
// al precedente FindInters avrei dovuto passare di cella
|
|
if ( ! FindInters( nId, clTrim, vplPolygons[nIdPolygon], vptInters, true)) {
|
|
// scarterò il punto molto vicino al lato e tengo solo l'intersezione del trim col lato
|
|
if ( ! vptInters.empty())
|
|
vptInters.pop_back() ;
|
|
plLoop.GetPrevPoint( ptTEnd) ;
|
|
plLoop.GetPrevPoint( ptTStart) ;
|
|
plLoop.GetNextPoint( ptCurr) ;
|
|
clTrim.Set( ptTStart, ptTEnd) ;
|
|
clTrim.ExtendEndByLen( EPS_SMALL * 2) ;
|
|
clTrim.ExtendStartByLen( EPS_SMALL * 2) ;
|
|
if ( ! FindInters( nId, clTrim, vplPolygons[nIdPolygon], vptInters, true))
|
|
return false ;
|
|
bEraseNextPoint = true ;
|
|
}
|
|
// ricalcolo la posizione di nId nel vettore delle foglie
|
|
iter = find( m_vnLeaves.begin(), m_vnLeaves.end(), nId) ;
|
|
nIdPolygon = distance( m_vnLeaves.begin(), iter) ;
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree[nId] ;
|
|
// salvo il verso del loop
|
|
pCell->m_vInters.back().bCCW = bCCW ;
|
|
// salvo il chunk del loop
|
|
pCell->m_vInters.back().nChunk = m_mChunk[i] ;
|
|
// aggiorno la compo splittata
|
|
UpdateSplitLoop( pCCLoopSplit, vptInters.back()) ;
|
|
}
|
|
// controllo di aggiungere un punto abbastanza distante dal precedente
|
|
if( ! AreSamePointXYEpsilon( vptInters.back(), ptCurr, 10 * EPS_SMALL)) {
|
|
// aggiungo la fine del segmento nel vettore delle intersezioni
|
|
vptInters.push_back( ptCurr) ;
|
|
// aggiorno la compo splittata
|
|
UpdateSplitLoop( pCCLoopSplit, ptCurr) ;
|
|
}
|
|
}
|
|
if ( nId == nFirstCell)
|
|
vptInters.pop_back() ;
|
|
pCell->m_vInters.back().vpt = vptInters ;
|
|
if ( bLoopInside) {
|
|
// setto la categoria della cella
|
|
if ( pCell->m_nFlag == -1)
|
|
pCell->m_nFlag = 2 ;
|
|
else if ( pCell->m_nFlag == 1)
|
|
pCell->m_nFlag = 3 ;
|
|
// setto i lati di ingresso e uscita a -1 per indicare che ho un loop interno alla cella
|
|
pCell->m_vInters.back().nIn = -1 ;
|
|
pCell->m_vInters.back().nOut = -1 ;
|
|
}
|
|
// sono tornato alla cella di partenza, quindi devo fare il merge dei due vettori di intersezione che ho creato per questa cella
|
|
// per lo stesso loop
|
|
else {
|
|
// verifico se sono effettivamente nella cella di partenza o in una cella adiacente
|
|
if ( nId != nFirstCell) {
|
|
Point3d ptFirst = m_mTree[nFirstCell].m_vInters[nPass].vpt[0] ;
|
|
Point3d ptLast = pCell->m_vInters.back().vpt.back() ;
|
|
//sistemo l'ingresso della prima cella
|
|
int nEdge ;
|
|
if ( ! OnWhichEdge( nFirstCell, ptFirst, nEdge))
|
|
return false ;
|
|
m_mTree[nFirstCell].m_vInters[nPass].nIn = nEdge ;
|
|
// sistemo l'uscita dell'ultima cella
|
|
if ( ! OnWhichEdge( nId, ptLast, nEdge))
|
|
return false ;
|
|
pCell->m_vInters.back().nOut = nEdge ;
|
|
// sistemo il flag dell'ultima cella
|
|
if ( pCell->m_nFlag == -1)
|
|
pCell->m_nFlag = 1 ;
|
|
else if ( pCell->m_nFlag == 2)
|
|
pCell->m_nFlag = 3 ;
|
|
}
|
|
// sono tornato nella cella iniziale, quindi giunto i due vettori intersezione
|
|
else if ( nId == nFirstCell) {
|
|
int nOut = pCell->m_vInters[nPass].nOut ;
|
|
pCell->m_vInters.back().vpt.insert( pCell->m_vInters.back().vpt.end(),
|
|
pCell->m_vInters[nPass].vpt.begin(),
|
|
pCell->m_vInters[nPass].vpt.end()) ;
|
|
pCell->m_vInters[nPass] = pCell->m_vInters.back() ;
|
|
pCell->m_vInters.pop_back() ;
|
|
// sistemo il lato d'uscita
|
|
pCell->m_vInters[nPass].nOut = nOut ;
|
|
}
|
|
}
|
|
// salvo la compo splittata
|
|
m_vCCLoop2D.emplace_back( Release( pCCLoopSplit)) ;
|
|
}
|
|
|
|
// riordino i vettori di intersezione per ogni cella e setto il flag RightEdgeIn
|
|
for ( int nId : m_vnLeaves) {
|
|
std::sort( m_mTree[nId].m_vInters.begin(), m_mTree[nId].m_vInters.end()) ;
|
|
SetRightEdgeIn( nId) ;
|
|
}
|
|
|
|
// devo riconoscere le celle dentro i loop che sono ancora con label nFlag2 = 0
|
|
ResetTree() ;
|
|
INTVECTOR vNeigh, vFirst ;
|
|
GetRootNeigh( 1, vFirst) ;
|
|
for ( int k : vFirst) {
|
|
m_mTree[k].m_bOnLeftEdge = true ;
|
|
}
|
|
int nLastLeft = vFirst.back() ;
|
|
int nCell = vFirst[0] ;
|
|
GetRightNeigh( nCell, vNeigh) ;
|
|
// proseguo finché non sono sull'elemento più alto di vFirst e tutti i suoi vicini sono processati/categorizzati
|
|
bool bAllDone = false ;
|
|
// mentre scorro a destra mi basta trovare un vicino da cui non sono passato
|
|
// mentre torno indietro a sinistra, mi fermo quando trovo una cella non processata ( con vicini a destra da cui non sono passato)
|
|
while ( ! bAllDone) {
|
|
// categorizzo la cella
|
|
m_mTree[nCell].m_nFlag2 = 1 ;
|
|
CategorizeCell( nCell) ;
|
|
bool bDone = false ;
|
|
// fintanto che la cella ha tra i vicini a destra una cella non elaborata mi sposto a destra
|
|
// definisco una cella Processed se tutto il ramo a destra è categorizzato
|
|
while ( ! m_mTree[nCell].IsProcessed()) {
|
|
// verso la cella a destra più in basso da cui non sono ancora passato
|
|
bool bProceeded = false ;
|
|
for ( int i = 0 ; i < (int)vNeigh.size() ; ++ i) {
|
|
if ( m_mTree[vNeigh[i]].m_nFlag2 == 0) {
|
|
nCell = vNeigh[i] ;
|
|
bProceeded = true ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( ! bProceeded) {
|
|
m_mTree[nCell].SetProcessed() ;
|
|
}
|
|
else {
|
|
//categorizzo la cella
|
|
m_mTree[nCell].m_nFlag2 = 1 ;
|
|
CategorizeCell( nCell) ;
|
|
}
|
|
|
|
if ( ! m_mTree[nCell].IsProcessed()) {
|
|
// guardo i vicini a destra per passare alla prossima cella
|
|
vNeigh.clear() ;
|
|
GetRightNeigh( nCell, vNeigh) ;
|
|
bDone = true ;
|
|
//controllo che tra i vicini di destra ce ne sia almeno uno da cui non sono passato
|
|
// ( e controllo che non sia una cella sul lato sinistro ( adiacenza in caso di superficie chiusa))
|
|
for ( int t: vNeigh) {
|
|
if ( m_mTree[t].m_nFlag2 == 0 ) {
|
|
if ( ! m_bClosedU) {
|
|
bDone = false ;
|
|
break ;
|
|
}
|
|
else {
|
|
// controllo che non sia sul lato sinistro
|
|
if ( ! m_mTree[t].m_bOnLeftEdge) {
|
|
bDone = false ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
m_mTree[nCell].SetProcessed( bDone) ;
|
|
}
|
|
}
|
|
vNeigh.clear() ;
|
|
GetRightNeigh( nCell, vNeigh) ;
|
|
// se non ho vicini a destra o se i vicini sono già tutti categorizzati
|
|
// torno indietro a sinistra alla cella già categorizzata più bassa
|
|
while ( m_mTree[nCell].IsProcessed()) {
|
|
// trovo il vicino a sinistra, già categorizzato, più basso
|
|
vNeigh.clear() ;
|
|
GetLeftNeigh( nCell, vNeigh) ;
|
|
// però se sono già sul lato sinistro non cerco di spostarmi ancora più a sinistra.
|
|
if ( ! m_mTree[nCell].m_bOnLeftEdge) {
|
|
for ( int p = 0 ; p < (int)vNeigh.size() ; ++ p) {
|
|
if ( m_mTree[vNeigh[p]].m_nFlag2 != 0) {
|
|
nCell = vNeigh[p] ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
if ( ! m_bClosedU) {
|
|
// se non ho vicini a sinistra sono tornato sul lato sinistro
|
|
// se questa cella non è processata devo procedere alla cella più bassa non processata sul lato sinistro
|
|
if ( vNeigh.empty() && m_mTree[nCell].IsProcessed()) {
|
|
for ( int p = 0 ; p < (int)vFirst.size() ; ++ p) {
|
|
if ( m_mTree[vFirst[p]].m_nFlag2 == 0) {
|
|
nCell = vFirst[p] ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( nCell == nLastLeft && m_mTree[nCell].IsProcessed()) {
|
|
bAllDone = true ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// verifico se sono tornato sul lato sinistro, in questo caso procedo con la prima cella di vFirst non processata
|
|
if ( m_mTree[nCell].m_bOnLeftEdge && m_mTree[nCell].IsProcessed()) {
|
|
for ( int p = 0 ; p < (int) vFirst.size(); ++ p) {
|
|
if ( nCell == vFirst[p] && nCell != nLastLeft) {
|
|
nCell = vFirst[p + 1] ;
|
|
break ;
|
|
}
|
|
else if ( nCell == nLastLeft && m_mTree[nCell].IsProcessed()){
|
|
bAllDone = true ;
|
|
}
|
|
}
|
|
}
|
|
if ( bAllDone)
|
|
break ;
|
|
}
|
|
// controllo se tutti i vicini di destra sono categorizzati
|
|
vNeigh.clear() ;
|
|
GetRightNeigh( nCell, vNeigh) ;
|
|
bool bDone = true ;
|
|
for ( int k = 0 ; k < (int)vNeigh.size() ; ++ k) {
|
|
if ( ! m_mTree[vNeigh[k]].IsProcessed()) {
|
|
bDone = false ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( bDone) {
|
|
m_mTree[nCell].SetProcessed( bDone) ;
|
|
if ( m_mTree[nCell].m_nFlag2 == 0) {
|
|
m_mTree[nCell].m_nFlag2 = 1 ;
|
|
CategorizeCell( nCell) ;
|
|
}
|
|
}
|
|
}
|
|
vNeigh.clear() ;
|
|
GetRightNeigh( nCell, vNeigh) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::FindInters( int& nId, const CurveLine& clTrim, const PolyLine& plPolygon, PNTVECTOR& vptInters, bool bFirstInters)
|
|
{
|
|
Cell* pCell = &m_mTree.at(nId) ;
|
|
Point3d ptTR = pCell->GetTopRight() ;
|
|
Point3d ptBL = pCell->GetBottomLeft() ;
|
|
Point3d ptTl( ptBL.x , ptTR.y) ;
|
|
Point3d ptBr( ptTR.x , ptBL.y) ;
|
|
int nEdge ; // flag che indica il lato su cui ho l'intersezione a partire dal lato top in senso antiorario
|
|
Point3d ptInters ;
|
|
|
|
PtrOwner<ICurveComposite> pCC( CreateCurveComposite()) ;
|
|
PolyLine plSimplePolygon ;
|
|
plSimplePolygon = plPolygon ;
|
|
plSimplePolygon.RemoveAlignedPoints() ;
|
|
pCC->FromPolyLine( plSimplePolygon) ;
|
|
IntersCurveCurve icc( clTrim, *pCC) ;
|
|
if ( icc.GetCrossOrOverlapIntersCount() < 1) {
|
|
CurveLine clTrimExtended = clTrim ;
|
|
clTrimExtended.ExtendEndByLen( 5 * EPS_SMALL) ;
|
|
clTrimExtended.ExtendStartByLen( 5 * EPS_SMALL) ;
|
|
IntersCurveCurve iccRetry( clTrimExtended, *pCC) ;
|
|
icc = iccRetry ;
|
|
}
|
|
if ( icc.GetCrossOrOverlapIntersCount() < 1) {
|
|
CurveLine clTrimExtended = clTrim ;
|
|
clTrimExtended.ExtendEndByLen( 5 * EPS_SMALL) ;
|
|
clTrimExtended.ExtendStartByLen( 5 * EPS_SMALL) ;
|
|
IntersCurveCurve iccRetry( clTrimExtended, *pCC) ;
|
|
icc = iccRetry ;
|
|
}
|
|
IntCrvCrvInfo iccInfo ;
|
|
if ( icc.GetOverlaps()) {
|
|
for ( int i = 0 ; i < icc.GetIntersCount() ; ++i) {
|
|
icc.GetIntCrvCrvInfo( i, iccInfo) ;
|
|
if( iccInfo.bOverlap){
|
|
ptInters = iccInfo.IciA[1].ptI ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
int nLastInters = icc.GetIntersCount() -1 ;
|
|
if( nLastInters < 0)
|
|
return false ;
|
|
icc.GetIntCrvCrvInfo( nLastInters, iccInfo) ;
|
|
ptInters = iccInfo.IciA[0].ptI ;
|
|
}
|
|
|
|
// determino il lato/vertice di uscita
|
|
if( ! OnWhichEdge( nId, ptInters, nEdge))
|
|
return false ;
|
|
pCell->m_vInters.back().nOut = nEdge ;
|
|
// se è risultato che sono abbastanza vicino( di EPS_SMALL) ad un vertice allora il punto aggiunto sarà il vertice vero e proprio
|
|
// se è abbastanza vicino ad un lato allora modifico le sue coordinate in modo che sia esattamente sul lato
|
|
if ( nEdge == 0)
|
|
ptInters.y = ptTl.y ;
|
|
else if ( nEdge == 1)
|
|
ptInters.x = ptTl.x ;
|
|
else if ( nEdge == 2)
|
|
ptInters.y = ptBL.y ;
|
|
else if ( nEdge == 3)
|
|
ptInters.x = ptTR.x ;
|
|
else if ( nEdge == 4)
|
|
ptInters = ptTl ;
|
|
else if ( nEdge == 5)
|
|
ptInters = ptBL ;
|
|
else if ( nEdge == 6)
|
|
ptInters = ptBr ;
|
|
else if ( nEdge == 7)
|
|
ptInters = ptTR ;
|
|
// aggiungo il nuovo punto al vettore delle intersezioni
|
|
if ( vptInters.empty() || ! AreSamePointXYEpsilon( ptInters , vptInters.back(), 10 * EPS_SMALL))
|
|
vptInters.push_back( ptInters) ;
|
|
else {
|
|
// se l'ultimo punto del vettore delle intersezioni è quasi uguale al punto che devo aggiungere allora lo sostituisco con quest'ultimo
|
|
vptInters.pop_back() ;
|
|
vptInters.push_back( ptInters) ;
|
|
}
|
|
|
|
// salvo il vettore intersezione per la cella e capisco in quale altra cella passare
|
|
if ( (int)vptInters.size() == 1)
|
|
pCell->m_vInters.back().vpt.push_back( vptInters[0]) ;
|
|
else
|
|
pCell->m_vInters.back().vpt = vptInters ;
|
|
vptInters.clear() ;
|
|
// setto la categoria della cella
|
|
if ( pCell->m_nFlag == -1)
|
|
pCell->m_nFlag = 1 ;
|
|
else if ( pCell->m_nFlag == 2)
|
|
pCell->m_nFlag = 3 ;
|
|
|
|
// seleziono la cella successiva da analizzare
|
|
//
|
|
// se la superficie è chiusa su un parametro devo stare attento a considerare lo spazio parametrico come se non fosse periodico
|
|
// es: non posso saltare dal lato destro al lato sinistro anche se una cella sul lato sinistro risulta la RightNeigh di una cella sul lato destro!
|
|
INTVECTOR vNeigh, vNeigh1 ;
|
|
if ( nEdge == 0) {
|
|
GetTopNeigh( nId, vNeigh) ;
|
|
std::reverse( vNeigh.begin(), vNeigh.end()) ;
|
|
for ( int j : vNeigh) {
|
|
if ( ptInters.x >= m_mTree[j].GetBottomLeft().x) {
|
|
nId = j ;
|
|
break ;
|
|
}
|
|
}
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
Point3d ptBr( pCell->GetTopRight().x, pCell->GetBottomLeft().y) ;
|
|
if ( ptInters.x > pCell->GetBottomLeft().x + EPS_SMALL && ptInters.x < pCell->GetTopRight().x - EPS_SMALL)
|
|
pCell->m_vInters.back().nIn = 2 ;
|
|
else if ( AreSamePointXYApprox( ptInters, pCell->GetBottomLeft()))
|
|
pCell->m_vInters.back().nIn = 5 ;
|
|
else if ( AreSamePointXYApprox( ptInters, ptBr))
|
|
pCell->m_vInters.back().nIn = 6 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 2 ;
|
|
}
|
|
else if ( nEdge == 1) {
|
|
GetLeftNeigh( nId, vNeigh) ;
|
|
std::reverse( vNeigh.begin(), vNeigh.end()) ;
|
|
for ( int j : vNeigh) {
|
|
if ( ptInters.y >= m_mTree[j].GetBottomLeft().y) {
|
|
nId = j ;
|
|
break ;
|
|
}
|
|
}
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
Point3d ptBr( pCell->GetTopRight().x, pCell->GetBottomLeft().y) ;
|
|
if ( ptInters.y > pCell->GetBottomLeft().y + EPS_SMALL && ptInters.y < pCell->GetTopRight().y - EPS_SMALL)
|
|
pCell->m_vInters.back().nIn = 3 ;
|
|
else if ( AreSamePointXYApprox( ptInters, ptBr))
|
|
pCell->m_vInters.back().nIn = 6 ;
|
|
else if ( AreSamePointXYApprox( ptInters, pCell->GetTopRight()))
|
|
pCell->m_vInters.back().nIn = 7 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 3 ;
|
|
}
|
|
else if ( nEdge == 2) {
|
|
GetBottomNeigh( nId, vNeigh) ;
|
|
for ( int j : vNeigh) {
|
|
if ( ptInters.x <= m_mTree[j].GetTopRight().x) {
|
|
nId = j ;
|
|
break ;
|
|
}
|
|
}
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
Point3d ptTl( pCell->GetBottomLeft().x, pCell->GetTopRight().y) ;
|
|
if ( ptInters.x > pCell->GetBottomLeft().x + EPS_SMALL && ptInters.x < pCell->GetTopRight().x - EPS_SMALL)
|
|
pCell->m_vInters.back().nIn = 0 ;
|
|
else if ( AreSamePointXYApprox( ptInters, ptTl))
|
|
pCell->m_vInters.back().nIn = 4 ;
|
|
else if ( AreSamePointXYApprox( ptInters, pCell->GetTopRight()))
|
|
pCell->m_vInters.back().nIn = 7 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 0 ;
|
|
}
|
|
else if ( nEdge == 3) {
|
|
GetRightNeigh( nId, vNeigh) ;
|
|
for ( int j : vNeigh) {
|
|
if ( ptInters.y <= m_mTree[j].GetTopRight().y) {
|
|
nId = j ;
|
|
break ;
|
|
}
|
|
}
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
Point3d ptTl( pCell->GetBottomLeft().x, pCell->GetTopRight().y) ;
|
|
if ( ptInters.y > pCell->GetBottomLeft().y + EPS_SMALL && ptInters.y < pCell->GetTopRight().y - EPS_SMALL)
|
|
pCell->m_vInters.back().nIn = 1 ;
|
|
else if ( AreSamePointXYApprox( ptInters, pCell->GetBottomLeft()))
|
|
pCell->m_vInters.back().nIn = 5 ;
|
|
else if ( AreSamePointXYApprox( ptInters, ptTl))
|
|
pCell->m_vInters.back().nIn = 4 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 1 ;
|
|
}
|
|
// esco da uno dei vertici
|
|
else if ( nEdge == 4) {
|
|
GetTopNeigh( nId, vNeigh) ;
|
|
GetLeftNeigh( nId, vNeigh1) ;
|
|
INTVECTOR nPossible, nPossible1 ;
|
|
nPossible = FindCell( ptInters, clTrim, vNeigh) ;
|
|
nPossible1 = FindCell( ptInters, clTrim, vNeigh1) ;
|
|
// ingresso dal basso
|
|
if ( ! nPossible.empty()) {
|
|
nId = nPossible[0] ;
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
// controllo se entro in un vertice o a metà lato
|
|
Point3d ptBr( pCell->GetTopRight().x, pCell->GetBottomLeft().y) ;
|
|
if ( AreSamePointXYExact( ptInters, ptBr))
|
|
pCell->m_vInters.back().nIn = 6 ;
|
|
else if ( AreSamePointXYExact( ptInters, pCell->GetBottomLeft()))
|
|
pCell->m_vInters.back().nIn = 5 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 2 ;
|
|
}
|
|
// ingresso da destra
|
|
else if ( ! nPossible1.empty()) {
|
|
nId = nPossible1.back() ;
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
// controllo se entro in un vertice o a metà lato
|
|
Point3d ptBr( pCell->GetTopRight().x, pCell->GetBottomLeft().y) ;
|
|
if ( AreSamePointXYExact( ptInters, ptBr))
|
|
pCell->m_vInters.back().nIn = 6 ;
|
|
else if ( AreSamePointXYExact( ptInters, pCell->GetTopRight()))
|
|
pCell->m_vInters.back().nIn = 7 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 3 ;
|
|
}
|
|
// ingresso in diagonale
|
|
else {
|
|
if ( ! vNeigh.empty()) {
|
|
int nIdTemp = vNeigh[0] ;
|
|
vNeigh.clear() ;
|
|
GetLeftNeigh( nIdTemp, vNeigh) ;
|
|
nId = vNeigh[0] ;
|
|
m_mTree[nId].m_vInters.emplace_back() ;
|
|
m_mTree[nId].m_vInters.back().nIn = 6 ;
|
|
}
|
|
else if (! vNeigh1.empty()) {
|
|
nId = vNeigh1.back() ;
|
|
m_mTree[nId].m_vInters.emplace_back() ;
|
|
m_mTree[nId].m_vInters.back().nIn = 7 ;
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
}
|
|
else if ( nEdge == 5) {
|
|
GetLeftNeigh( nId, vNeigh) ;
|
|
GetBottomNeigh( nId, vNeigh1) ;
|
|
INTVECTOR nPossible, nPossible1 ;
|
|
nPossible = FindCell( ptInters, clTrim, vNeigh) ;
|
|
nPossible1 = FindCell( ptInters, clTrim, vNeigh1) ;
|
|
// ingresso dal destra
|
|
if ( ! nPossible.empty()) {
|
|
nId = nPossible[0] ;
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
// controllo se entro in un vertice o a metà lato
|
|
Point3d ptBr( pCell->GetTopRight().x, pCell->GetBottomLeft().y) ;
|
|
if ( AreSamePointXYExact( ptInters, ptBr))
|
|
pCell->m_vInters.back().nIn = 6 ;
|
|
else if ( AreSamePointXYExact( ptInters, pCell->GetTopRight()))
|
|
pCell->m_vInters.back().nIn = 7 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 3 ;
|
|
}
|
|
// ingresso dall'alto
|
|
else if ( ! nPossible1.empty()) {
|
|
nId = nPossible1[0] ;
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
// controllo se entro in un vertice o a metà lato
|
|
Point3d ptTl( pCell->GetBottomLeft().x, pCell->GetTopRight().y) ;
|
|
if ( AreSamePointXYExact( ptInters, ptTl))
|
|
pCell->m_vInters.back().nIn = 4 ;
|
|
else if ( AreSamePointXYExact( ptInters, pCell->GetTopRight()))
|
|
pCell->m_vInters.back().nIn = 7 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 0 ;
|
|
}
|
|
// ingresso in diagonale
|
|
else {
|
|
if ( ! vNeigh.empty()) {
|
|
int nIdTemp = vNeigh[0] ;
|
|
vNeigh.clear() ;
|
|
GetBottomNeigh( nIdTemp, vNeigh) ;
|
|
nId = vNeigh.back() ;
|
|
m_mTree[nId].m_vInters.emplace_back() ;
|
|
m_mTree[nId].m_vInters.back().nIn = 7 ;
|
|
}
|
|
else if (! vNeigh1.empty()) {
|
|
nId = vNeigh1[0] ;
|
|
m_mTree[nId].m_vInters.emplace_back() ;
|
|
m_mTree[nId].m_vInters.back().nIn = 4 ;
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
}
|
|
else if ( nEdge == 6) {
|
|
GetBottomNeigh( nId, vNeigh) ;
|
|
GetRightNeigh( nId, vNeigh1) ;
|
|
INTVECTOR nPossible, nPossible1 ;
|
|
nPossible = FindCell( ptInters, clTrim, vNeigh) ;
|
|
nPossible1 = FindCell( ptInters, clTrim, vNeigh1) ;
|
|
// ingresso dall'alto
|
|
if ( ! nPossible.empty()) {
|
|
nId = nPossible.back() ;
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
// controllo se entro in un vertice o a metà lato
|
|
Point3d ptTl( pCell->GetBottomLeft().x, pCell->GetTopRight().y) ;
|
|
if ( AreSamePointXYExact( ptInters, ptTl))
|
|
pCell->m_vInters.back().nIn = 4 ;
|
|
else if ( AreSamePointXYExact( ptInters, pCell->GetTopRight()))
|
|
pCell->m_vInters.back().nIn = 7 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 0 ;
|
|
}
|
|
// ingresso da sinistra
|
|
else if ( ! nPossible1.empty()) {
|
|
nId = nPossible1[0] ;
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
// controllo se entro in un vertice o a metà lato
|
|
Point3d ptTl( pCell->GetBottomLeft().x, pCell->GetTopRight().y) ;
|
|
if ( AreSamePointXYExact( ptInters, ptTl))
|
|
pCell->m_vInters.back().nIn = 4 ;
|
|
else if ( AreSamePointXYExact( ptInters, pCell->GetBottomLeft()))
|
|
pCell->m_vInters.back().nIn = 5 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 1 ;
|
|
}
|
|
// ingresso in diagonale
|
|
else {
|
|
if ( ! vNeigh.empty()){
|
|
int nIdTemp = vNeigh.back() ;
|
|
vNeigh.clear() ;
|
|
GetRightNeigh( nIdTemp, vNeigh) ;
|
|
nId = vNeigh.back() ;
|
|
m_mTree[nId].m_vInters.emplace_back() ;
|
|
m_mTree[nId].m_vInters.back().nIn = 4 ;
|
|
}
|
|
else if (! vNeigh1.empty()) {
|
|
nId = vNeigh1[0] ;
|
|
m_mTree[nId].m_vInters.emplace_back() ;
|
|
m_mTree[nId].m_vInters.back().nIn = 5 ;
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
}
|
|
else if ( nEdge == 7) {
|
|
GetRightNeigh( nId, vNeigh) ;
|
|
GetTopNeigh( nId, vNeigh1) ;
|
|
INTVECTOR nPossible, nPossible1 ;
|
|
nPossible = FindCell( ptInters, clTrim, vNeigh) ;
|
|
nPossible1 = FindCell( ptInters, clTrim, vNeigh1) ;
|
|
// ingresso da sinistra
|
|
if ( ! nPossible.empty()) {
|
|
nId = nPossible.back() ;
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
// controllo se entro in un vertice o a metà lato
|
|
Point3d ptTl( pCell->GetBottomLeft().x, pCell->GetTopRight().y) ;
|
|
if ( AreSamePointXYExact( ptInters, ptTl))
|
|
pCell->m_vInters.back().nIn = 4 ;
|
|
else if ( AreSamePointXYExact( ptInters, pCell->GetBottomLeft()))
|
|
pCell->m_vInters.back().nIn = 5 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 1 ;
|
|
}
|
|
// ingresso dal basso
|
|
else if ( ! nPossible1.empty()) {
|
|
nId = nPossible1.back() ;
|
|
// aggiorno il puntatore alla cella
|
|
pCell = &m_mTree.at(nId) ;
|
|
pCell->m_vInters.emplace_back() ;
|
|
// controllo se entro in un vertice o a metà lato
|
|
Point3d ptBr( pCell->GetTopRight().x, pCell->GetBottomLeft().y) ;
|
|
if ( AreSamePointXYExact( ptInters, ptBr))
|
|
pCell->m_vInters.back().nIn = 6 ;
|
|
else if ( AreSamePointXYExact( ptInters, pCell->GetBottomLeft()))
|
|
pCell->m_vInters.back().nIn = 5 ;
|
|
else
|
|
pCell->m_vInters.back().nIn = 2 ;
|
|
}
|
|
// ingresso in diagonale
|
|
else {
|
|
if ( ! vNeigh.empty() /*&& ! m_mTree[vNeigh[0]].m_bOnLeftEdge */) { // questa aggiunta in teoria non serve
|
|
int nIdTemp = vNeigh.back() ;
|
|
vNeigh.clear() ;
|
|
GetTopNeigh( nIdTemp, vNeigh) ;
|
|
nId = vNeigh[0] ;
|
|
m_mTree[nId].m_vInters.emplace_back() ;
|
|
m_mTree[nId].m_vInters.back().nIn = 5 ;
|
|
}
|
|
else if (! vNeigh1.empty()) {
|
|
nId = vNeigh1.back() ;
|
|
m_mTree[nId].m_vInters.emplace_back() ;
|
|
m_mTree[nId].m_vInters.back().nIn = 6 ;
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
}
|
|
// aggiungo l'intersezione al vettore delle intersezioni della prossima cella
|
|
vptInters.push_back( ptInters) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::CreateCellPolygons( int nLeafId, POLYLINEMATRIX& vPolygons, POLYLINEMATRIX& vPolygons3d, INTVECTOR& vToCheck, int& nPoly, INTVECTOR& vnParentChunk,
|
|
const PolyLine& plCell, const PolyLine& plCell3d)
|
|
{
|
|
// conto quanti vertici in più ho per lato e creo un vettore dei vertici per lato
|
|
int nId = m_vnLeaves[nLeafId] ;
|
|
Cell& cCell = m_mTree[nId] ;
|
|
PNTMATRIX vEdgeVertex(4) ;
|
|
PNTMATRIX vEdgeVertex3d(4) ;
|
|
Point3d ptBL = cCell.GetBottomLeft() ;
|
|
Point3d ptTR = cCell.GetTopRight() ;
|
|
Point3d ptTl = cCell.GetTopLeft() ;
|
|
Point3d ptBr = cCell.GetBottomRight() ;
|
|
int nVertToErase = m_mTree.at(nId).m_nVertToErase ;
|
|
if( nVertToErase != 2)
|
|
vEdgeVertex[0].push_back( ptTR) ;
|
|
if( nVertToErase != 3)
|
|
vEdgeVertex[1].push_back( ptTl) ;
|
|
if( nVertToErase != 0)
|
|
vEdgeVertex[2].push_back( ptBL) ;
|
|
if( nVertToErase != 1)
|
|
vEdgeVertex[3].push_back( ptBr) ;
|
|
// capisco se sono in modalità ForTriangulation
|
|
bool bForTriangulation = plCell3d.GetPointNbr() != 0 ;
|
|
if ( bForTriangulation) {
|
|
if( nVertToErase != 2) {
|
|
Point3d pt3d ; GetPoint( cCell.GetTopRight().x, cCell.GetTopRight().y, pt3d) ;
|
|
vEdgeVertex3d[0].push_back( pt3d) ;
|
|
}
|
|
if( nVertToErase != 3){
|
|
Point3d pt3d ; GetPoint( cCell.GetBottomLeft().x, cCell.GetTopRight().y, pt3d) ;
|
|
vEdgeVertex3d[1].push_back( pt3d) ;
|
|
}
|
|
if( nVertToErase != 0) {
|
|
Point3d pt3d ; GetPoint( cCell.GetBottomLeft().x, cCell.GetBottomLeft().y, pt3d) ;
|
|
vEdgeVertex3d[2].push_back( pt3d) ;
|
|
}
|
|
if( nVertToErase != 1) {
|
|
Point3d pt3d ; GetPoint( cCell.GetTopRight().x, cCell.GetBottomLeft().y, pt3d) ;
|
|
vEdgeVertex3d[3].push_back( pt3d) ;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// la PolyLine è riempita a partire dal lato bottom
|
|
Point3d ptStart, pt3d ;
|
|
plCell.GetFirstPoint( ptStart) ;
|
|
if ( bForTriangulation)
|
|
plCell3d.GetFirstPoint( pt3d) ;
|
|
INTVECTOR vEdge = { 2, 3, 0, 1} ;
|
|
for ( int p = 0 ; p < 4 ; ++ p) {
|
|
int j = vEdge[p] ;
|
|
int next = j +1 ;
|
|
if ( j == 3)
|
|
next = 0 ;
|
|
Point3d ptToAdd ;
|
|
Point3d ptNextVert ;
|
|
switch ( next ) {
|
|
case 0 : ptNextVert = ptTR ; break ;
|
|
case 1 : ptNextVert = ptTl; break ;
|
|
case 2 : ptNextVert = ptBL ; break ;
|
|
case 3 : ptNextVert = ptBr ; break ;
|
|
}
|
|
if ( ! bForTriangulation) {
|
|
while ( plCell.GetNextPoint( ptToAdd) && ! AreSamePointXYExact( ptToAdd, ptNextVert))
|
|
vEdgeVertex[j].push_back( ptToAdd) ;
|
|
}
|
|
else {
|
|
double dPar = 0 ;
|
|
while ( plCell.GetNextUPoint( &dPar, &ptToAdd) && ! AreSamePointXYExact( ptToAdd, ptNextVert)) {
|
|
vEdgeVertex[j].push_back( ptToAdd) ;
|
|
if ( dPar > 0)
|
|
plCell3d.GetNextPoint( pt3d) ;
|
|
vEdgeVertex3d[j].push_back( pt3d) ;
|
|
}
|
|
if ( dPar > 0)
|
|
plCell3d.GetNextPoint( pt3d) ;
|
|
}
|
|
}
|
|
|
|
// comincio a costruire il poligono
|
|
INTVECTOR vToCheckNow = vToCheck ;
|
|
// costruisco i poligoni partendo dal vettore delle intersezioni, come spiegato a pag15 di Cripps
|
|
PolyLine plTrimmedPoly ;
|
|
PolyLine plTrimmedPoly3d ;
|
|
// numero di volte che la cella è stata attraversata da una curva di trim
|
|
int nPassToCheck = (int) vToCheckNow.size() ;
|
|
// numero di vertici aggiunti al nuovo poligono
|
|
int c = 0 ;
|
|
// scorro sui vettori intersezione della cella nId e sui suoi vertici
|
|
// in questo for analizzo solo i loop che tagliano la cella
|
|
int nEdgeIn = -1 ;
|
|
int nFirstLoopInPoly = -1 ;
|
|
INTVECTOR vAddedLoops ;
|
|
Point3d ptLastAdded( -1, -1, 0) ;
|
|
for ( int w = 0 ; w < (int)vToCheckNow.size() ; ++ w) {
|
|
if ( cCell.m_vInters[w].vpt.size() < 2) {
|
|
continue ;
|
|
}
|
|
// indice del loop in m_vInters
|
|
int j = vToCheckNow[w] ;
|
|
Inters inA = cCell.m_vInters[j] ;
|
|
if ( inA.nIn != -1) {
|
|
int nEdge ;
|
|
if ( nEdgeIn == -1) {
|
|
// salvo il lato di ingresso del primo lato del poligono che sto costruendo
|
|
nEdgeIn = inA.nIn ;
|
|
nFirstLoopInPoly = j ;
|
|
}
|
|
for ( Point3d& ptInt : inA.vpt) {
|
|
AddVertex( nId, vEdgeVertex, vEdgeVertex3d, plTrimmedPoly, c, ptInt, plTrimmedPoly3d, bForTriangulation, ptLastAdded) ;
|
|
}
|
|
vAddedLoops.push_back( j) ;
|
|
nEdge = inA.nOut ;
|
|
// devo verificare di non essere uscito in un vertice, con un tratto sovrapposto al lato che ripercorrerò tra poco
|
|
Point3d ptLast, ptSecondToLast ;
|
|
plTrimmedPoly.GetLastPoint( ptLast) ;
|
|
plTrimmedPoly.GetPrevPoint( ptSecondToLast) ;
|
|
Vector3d vLast = ptLast - ptSecondToLast ;
|
|
vLast.Normalize() ;
|
|
Vector3d vEdge ;
|
|
// estendo: se l'ultimo tratto è sovrapposto e controverso allora elimino l'ultimo punto
|
|
bool bNotEquiverseOverlap = false ;
|
|
int nEdgeLast = -1 ;
|
|
OnWhichEdge(nId,ptLast, nEdgeLast) ;
|
|
if ( (nEdge == 0 || nEdge == 7) && nEdge == nEdgeLast) {
|
|
vEdge.Set( 1,0,0) ;
|
|
if ( AreOppositeVectorApprox( vLast, vEdge)) {
|
|
plTrimmedPoly.EraseLastUPoint() ;
|
|
plTrimmedPoly3d.EraseLastUPoint() ;
|
|
nEdge = 0 ;
|
|
bNotEquiverseOverlap = true ;
|
|
}
|
|
}
|
|
else if ( ( nEdge == 1 || nEdge == 4 ) && nEdge == nEdgeLast) {
|
|
vEdge.Set( 0,-1,0) ;
|
|
if ( AreOppositeVectorApprox( vLast, vEdge)) {
|
|
plTrimmedPoly.EraseLastUPoint() ;
|
|
plTrimmedPoly3d.EraseLastUPoint() ;
|
|
nEdge = 1 ;
|
|
bNotEquiverseOverlap = true ;
|
|
}
|
|
}
|
|
else if ( ( nEdge == 2 || nEdge == 5) && nEdge == nEdgeLast) {
|
|
vEdge.Set( -1,0,0) ;
|
|
if ( AreOppositeVectorApprox( vLast, vEdge)) {
|
|
plTrimmedPoly.EraseLastUPoint() ;
|
|
plTrimmedPoly3d.EraseLastUPoint() ;
|
|
nEdge = 2 ;
|
|
bNotEquiverseOverlap = true ;
|
|
}
|
|
}
|
|
else if ( ( nEdge == 3 || nEdge == 6) && nEdge == nEdgeLast) {
|
|
vEdge.Set( 0,1,0) ;
|
|
if ( AreOppositeVectorApprox( vLast, vEdge)) {
|
|
plTrimmedPoly.EraseLastUPoint() ;
|
|
plTrimmedPoly3d.EraseLastUPoint() ;
|
|
nEdge = 3 ;
|
|
bNotEquiverseOverlap = true ;
|
|
}
|
|
}
|
|
|
|
// se mi è rimasto solo un punto sulla polyline vuol dire che avevo solo un tratto parallelo ad lato
|
|
// quindi salto al prossimo loop
|
|
if ( plTrimmedPoly.GetPointNbr() == 1 && bNotEquiverseOverlap) {
|
|
plTrimmedPoly.Clear() ;
|
|
plTrimmedPoly3d.Clear() ;
|
|
if ( j == nFirstLoopInPoly)
|
|
nEdgeIn = -1 ;
|
|
continue ;
|
|
}
|
|
if ( nEdge > 3 && nEdge != 7) {
|
|
nEdge = nEdge - 3 ;
|
|
}
|
|
else if ( nEdge == 7) {
|
|
nEdge = 0 ;
|
|
}
|
|
// se ho altri Pass vado avanti ad aggiungere vertici finché trovo il prossimo o finché non sono tornato sul lato di partenza
|
|
bool bNotCameBack = true ;
|
|
bool bValidNextStart = false ;
|
|
bool bAtNextStart = false ;
|
|
int nEdgeWithVertexSkipped ;
|
|
switch ( nVertToErase) {
|
|
case 0 : nEdgeWithVertexSkipped = 2 ; break ;
|
|
case 1 : nEdgeWithVertexSkipped = 3 ; break ;
|
|
case 2 : nEdgeWithVertexSkipped = 0 ; break ;
|
|
case 3 : nEdgeWithVertexSkipped = 1 ; break ;
|
|
default : nEdgeWithVertexSkipped = -1 ; break ;
|
|
}
|
|
if ( w < nPassToCheck - 1) {
|
|
int nSecondCheck = 0 ;
|
|
int nNext ;
|
|
// ciclo sui loop successivi per vedere se ne ho uno con un valid start
|
|
for ( int t = w + 1 ; t < nPassToCheck ; ++ t) {
|
|
bValidNextStart = CheckIfBetween( cCell.m_vInters[j], cCell.m_vInters[vToCheckNow[t]]) ;
|
|
if ( bValidNextStart) {
|
|
nNext = t ;
|
|
bAtNextStart = AreSameEdge( cCell.m_vInters[j].nOut, cCell.m_vInters[vToCheckNow[t]].nIn) ;
|
|
break ;
|
|
}
|
|
else {
|
|
++ nSecondCheck ;
|
|
}
|
|
}
|
|
|
|
bNotCameBack = ! ( AreSameEdge( nEdge, nEdgeIn) && CheckIfBefore( plTrimmedPoly, nEdge)) ;
|
|
|
|
while ( ! ( bValidNextStart && bAtNextStart) && bNotCameBack) {
|
|
Point3d ptVert ;
|
|
if ( nEdge == 0)
|
|
ptVert = ptTl ;
|
|
else if ( nEdge == 1)
|
|
ptVert = ptBL ;
|
|
else if ( nEdge == 2)
|
|
ptVert = ptBr ;
|
|
else if ( nEdge == 3)
|
|
ptVert = ptTR ;
|
|
AddVertex( nId, vEdgeVertex, vEdgeVertex3d, plTrimmedPoly, c, ptVert, plTrimmedPoly3d, bForTriangulation, ptLastAdded) ;
|
|
if ( nEdge > 3 && nEdge != 7)
|
|
nEdge = nEdge - 4 ;
|
|
else if ( nEdge < 3)
|
|
++ nEdge ;
|
|
else
|
|
nEdge = 0 ;
|
|
|
|
// aggiorno le condizioni per il while
|
|
if ( bValidNextStart)
|
|
bAtNextStart = AreSameEdge( nEdge, cCell.m_vInters[vToCheckNow[nNext]].nIn) ;
|
|
|
|
bNotCameBack = ! ( AreSameEdge( nEdge, nEdgeIn) && CheckIfBefore( plTrimmedPoly, nEdge)) ;
|
|
}
|
|
|
|
// se ho trovato un altro loop salto all'inizio del for, dopo aver aggiunto eventuali punti intermedi
|
|
if ( bValidNextStart) {
|
|
Point3d ptLast ; plTrimmedPoly.GetLastPoint( ptLast) ;
|
|
for ( int p = nEdge == nEdgeWithVertexSkipped ? 0 : 1 ; p < (int) vEdgeVertex[nEdge].size() ; ++ p) {
|
|
if ( CheckIfBefore( nEdge, vEdgeVertex[nEdge][p], cCell.m_vInters[vToCheckNow[nNext]].vpt[0]) &&
|
|
CheckIfBefore( nEdge, ptLast, vEdgeVertex[nEdge][p])) {
|
|
plTrimmedPoly.AddUPoint( c, vEdgeVertex[nEdge][p]) ;
|
|
plTrimmedPoly3d.AddUPoint( c, vEdgeVertex3d[nEdge][p]) ;
|
|
++ c ;
|
|
}
|
|
}
|
|
w = w + nSecondCheck ;
|
|
continue ;
|
|
}
|
|
// sono tornato indietro
|
|
else if ( ! bNotCameBack){
|
|
Point3d ptStart ;
|
|
plTrimmedPoly.GetFirstPoint( ptStart) ;
|
|
Point3d ptLast ; plTrimmedPoly.GetLastPoint( ptLast) ;
|
|
for ( int p = nEdge == nEdgeWithVertexSkipped ? 0 : 1 ; p < (int) vEdgeVertex[nEdge].size() ; ++ p) {
|
|
if ( CheckIfBefore( nEdge, vEdgeVertex[nEdge][p], ptStart) && CheckIfBefore( nEdge, ptLast, vEdgeVertex[nEdge][p])) {
|
|
plTrimmedPoly.AddUPoint( c, vEdgeVertex[nEdge][p]) ;
|
|
plTrimmedPoly3d.AddUPoint( c, vEdgeVertex3d[nEdge][p]) ;
|
|
++ c ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// non ho altri loop quindi aggiungo vertici finché torno al punto di partenza
|
|
else {
|
|
bNotCameBack = ! ( AreSameEdge( nEdge, nEdgeIn) && CheckIfBefore( plTrimmedPoly, nEdge)) ;
|
|
|
|
Point3d ptStart ;
|
|
plTrimmedPoly.GetFirstPoint( ptStart) ;
|
|
while ( bNotCameBack) {
|
|
Point3d ptVert ;
|
|
if ( nEdge == 0)
|
|
ptVert = ptTl ;
|
|
else if ( nEdge == 1)
|
|
ptVert = ptBL ;
|
|
else if ( nEdge == 2)
|
|
ptVert = ptBr ;
|
|
else if ( nEdge == 3)
|
|
ptVert = ptTR ;
|
|
AddVertex( nId, vEdgeVertex, vEdgeVertex3d, plTrimmedPoly, c, ptVert, plTrimmedPoly3d, bForTriangulation, ptLastAdded) ;
|
|
if ( nEdge > 3 && nEdge != 7)
|
|
nEdge = nEdge - 4 ;
|
|
else if ( nEdge < 3)
|
|
++ nEdge ;
|
|
else
|
|
nEdge = 0 ;
|
|
// aggiorno le condizioni per il while
|
|
bNotCameBack = ! ( AreSameEdge( nEdge, nEdgeIn) && CheckIfBefore( plTrimmedPoly, nEdge)) ;
|
|
}
|
|
Point3d ptLast ; plTrimmedPoly.GetLastPoint( ptLast) ;
|
|
for ( int p = nEdge == nEdgeWithVertexSkipped ? 0 : 1 ; p < (int) vEdgeVertex[nEdge].size() ; ++ p) {
|
|
if ( CheckIfBefore( nEdge, vEdgeVertex[nEdge][p], ptStart) && CheckIfBefore( nEdge, ptLast, vEdgeVertex[nEdge][p])) {
|
|
plTrimmedPoly.AddUPoint( c, vEdgeVertex[nEdge][p]) ;
|
|
plTrimmedPoly3d.AddUPoint( c, vEdgeVertex3d[nEdge][p]) ;
|
|
++ c ;
|
|
}
|
|
}
|
|
|
|
}
|
|
plTrimmedPoly.Close() ;
|
|
plTrimmedPoly3d.Close() ;
|
|
ptLast.Set( -1,-1,0) ;
|
|
// controllo sull'area del poligono, se è 0 ( quindi un segmento), non lo aggiungo
|
|
double dArea ;
|
|
plTrimmedPoly.GetAreaXY( dArea) ;
|
|
//if ( dArea > SQ_EPS_SMALL) {
|
|
if ( dArea > 0) {
|
|
vPolygons.emplace_back() ;
|
|
vPolygons.back().push_back(std::move(plTrimmedPoly)) ;
|
|
if( bForTriangulation) {
|
|
vPolygons3d.emplace_back() ;
|
|
vPolygons3d.back().push_back(std::move(plTrimmedPoly3d)) ;
|
|
}
|
|
++ nPoly ;
|
|
vnParentChunk.push_back( inA.nChunk) ;
|
|
}
|
|
|
|
c = 0 ;
|
|
plTrimmedPoly.Clear() ;
|
|
plTrimmedPoly3d.Clear() ;
|
|
nEdgeIn = -1 ;
|
|
// devo verificare se tra i loop che sono finiti in vToCheck in realtà qualcuno l'ho usato per fare un poligono
|
|
for ( int k = 0 ; k < (int)vToCheck.size() ; ++ k) {
|
|
for ( int i = 0 ; i < (int)vAddedLoops.size() ; ++ i) {
|
|
if ( vToCheck[k] == vAddedLoops[i]) {
|
|
vToCheck.erase( vToCheck.begin() + k) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
continue ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::CreateIslandAndHoles( int nLeafId, POLYLINEMATRIX& vPolygons, POLYLINEMATRIX& vPolygons3d, int& nPoly, INTVECTOR& vnParentChunk,
|
|
const PolyLine& plPolygonsBasic, const PolyLine& plPolygonsBasic3d)
|
|
{
|
|
// costruisco i poligoni partendo dal vettore delle intersezioni
|
|
int nId = m_vnLeaves[nLeafId] ;
|
|
Cell& cCell = m_mTree[nId] ;
|
|
//capisco se sono in modalità ForTriangulation
|
|
|
|
// loop interni in una cella intersecata
|
|
int nChunkBiggestCW = -1 ;
|
|
if ( cCell.m_nFlag == 3 || cCell.m_nFlag == 2) {
|
|
PolyLine plInLoop ;
|
|
PolyLine plInLoop3d ;
|
|
Inters inA ;
|
|
// se ho almeno un loop CW che non è contenuto in un altro poligono o in un loop interno CCW devo aggiungere il bordo
|
|
bool bAllContained = true ;
|
|
bool bContained = false ;
|
|
int nInters = (int) cCell.m_vInters.size() ;
|
|
for ( int n = 0 ; n < nInters ; ++ n) {
|
|
inA = cCell.m_vInters[n] ;
|
|
if ( inA.nIn == -1) {
|
|
// per ogni loop CW verifico che ci sia un loop CCW dello stesso chunk ( che quindi lo contiene)
|
|
if ( ! inA.bCCW) {
|
|
if ( nChunkBiggestCW == -1)
|
|
nChunkBiggestCW = inA.nChunk ;
|
|
bContained = false ;
|
|
Inters inB = cCell.m_vInters[0] ;
|
|
for( int c = 0 ; c < nInters ; ++ c){
|
|
inB = cCell.m_vInters[c] ;
|
|
if ( inB.nIn == -1) {
|
|
if ( inB != inA && inB.nChunk == inA.nChunk && inB.bCCW) {
|
|
bContained = true ;
|
|
break ;
|
|
}
|
|
}
|
|
else
|
|
break ;
|
|
}
|
|
bAllContained = bAllContained && bContained ;
|
|
}
|
|
}
|
|
else
|
|
break ;
|
|
}
|
|
|
|
if ( cCell.m_nFlag == 2 && ! bAllContained) {
|
|
// i loop esterni sono CW, quindi prima dei loop di trim aggiungo il bordo cella
|
|
Point3d ptVert = cCell.GetTopRight() ;
|
|
plInLoop.AddUPoint( 0, ptVert) ;
|
|
ptVert = cCell.GetTopLeft() ;
|
|
plInLoop.AddUPoint( 1, ptVert) ;
|
|
ptVert = cCell.GetBottomLeft() ;
|
|
plInLoop.AddUPoint( 2, ptVert) ;
|
|
ptVert = cCell.GetBottomRight() ;
|
|
plInLoop.AddUPoint( 3, ptVert) ;
|
|
plInLoop.Close() ;
|
|
vPolygons.emplace_back() ;
|
|
vPolygons.back().push_back( std::move(plInLoop)) ;
|
|
++ nPoly ;
|
|
Point3d ptP00, ptP10, ptP11, ptP01 ;
|
|
GetPoint( cCell.GetBottomLeft().x, cCell.GetBottomLeft().y, ptP00) ;
|
|
GetPoint( cCell.GetTopRight().x, cCell.GetBottomLeft().y, ptP10) ;
|
|
GetPoint( cCell.GetTopRight().x, cCell.GetTopRight().y, ptP11) ;
|
|
GetPoint( cCell.GetBottomLeft().x, cCell.GetTopRight().y, ptP01) ;
|
|
plInLoop3d.AddUPoint( 0, ptP11) ;
|
|
plInLoop3d.AddUPoint( 1, ptP01) ;
|
|
plInLoop3d.AddUPoint( 2, ptP00) ;
|
|
plInLoop3d.AddUPoint( 3, ptP10) ;
|
|
plInLoop3d.Close() ;
|
|
vPolygons3d.emplace_back() ;
|
|
vPolygons3d.back().push_back( std::move(plInLoop3d)) ;
|
|
plInLoop3d.Clear() ;
|
|
// imposto il chunk del loop CW più grande ( il primo che ho incontrato)
|
|
vnParentChunk.push_back( nChunkBiggestCW) ;
|
|
plInLoop.Clear() ;
|
|
}
|
|
for ( int nLoop = 0 ; nLoop < nInters ; ++ nLoop) {
|
|
inA = cCell.m_vInters[nLoop] ;
|
|
if ( inA.nIn == -1) {
|
|
// numero di vertici aggiunti al nuovo poligono
|
|
int k = 0 ;
|
|
for ( Point3d& ptInt : inA.vpt) {
|
|
plInLoop.AddUPoint( k, ptInt) ;
|
|
Point3d pt3d ; GetPoint( ptInt.x, ptInt.y, pt3d) ;
|
|
plInLoop3d.AddUPoint( k, pt3d) ;
|
|
++ k ;
|
|
}
|
|
plInLoop.Close() ;
|
|
plInLoop3d.Close() ;
|
|
bool bAdded = false ;
|
|
// se il loop è CW devo controllare in quale altro dei poligoni che ho già aggiunto è contenuto
|
|
if ( ! inA.bCCW) {
|
|
Point3d ptStart ;
|
|
plInLoop.GetFirstPoint( ptStart) ;
|
|
int nOtherPoly = (int)vPolygons.size() ;
|
|
for ( int r = 0 ; r < nPoly ; ++r) {
|
|
if ( IsPointInsidePolyLine( ptStart, vPolygons[nOtherPoly - r - 1][0], -0.01) && vnParentChunk[nPoly - r - 1] == inA.nChunk) {
|
|
vPolygons[nOtherPoly - r - 1].push_back( plInLoop) ;
|
|
plInLoop.Clear() ;
|
|
vPolygons3d[nOtherPoly - r - 1].push_back( std::move(plInLoop3d)) ;
|
|
plInLoop3d.Clear() ;
|
|
bAdded = true ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
if ( ! bAdded) {
|
|
vPolygons.emplace_back() ;
|
|
vPolygons.back().push_back( std::move(plInLoop)) ;
|
|
plInLoop.Clear() ;
|
|
vPolygons3d.emplace_back( ) ;
|
|
vPolygons3d.back().push_back( std::move(plInLoop3d)) ;
|
|
plInLoop3d.Clear() ;
|
|
++ nPoly ;
|
|
vnParentChunk.push_back( inA.nChunk) ;
|
|
}
|
|
plInLoop.Clear() ;
|
|
plInLoop3d.Clear() ;
|
|
}
|
|
else
|
|
continue ;
|
|
}
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::CheckIfBefore( const PolyLine& pl, int nEdge) const
|
|
{
|
|
// controllo se ptEnd è prima di ptStart sul lato nEdge rispetto al senso antiorario ( quindi se è dopo in senso orario)
|
|
Point3d ptStart, ptEnd ;
|
|
pl.GetFirstPoint( ptStart) ;
|
|
pl.GetLastPoint( ptEnd) ;
|
|
if ( AreSameEdge( nEdge, 0)) {
|
|
return ptEnd.x > ptStart.x ;
|
|
}
|
|
else if ( AreSameEdge( nEdge, 1)) {
|
|
return ptEnd.y > ptStart.y ;
|
|
}
|
|
else if ( AreSameEdge( nEdge, 2)) {
|
|
return ptEnd.x < ptStart.x ;
|
|
}
|
|
else if ( AreSameEdge( nEdge, 3)) {
|
|
return ptEnd.y < ptStart.y ;
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::CheckIfBefore( const Inters& inA) const
|
|
{
|
|
// questa funzione è pensata in riferimento al lato 3, quindi nessuno dei due punti può stare su Edge = 3 ( funzione usata per definire RightEdgeIn)
|
|
// controllo se l'ingresso è prima dell'uscita
|
|
int nEdge1 = inA.nIn ;
|
|
int nEdge2 = inA.nOut ;
|
|
if ( nEdge1 == -1)
|
|
return false ; // questo return è usato in modo improprio perché non fa capire che l'intersezione analizzata non era corretta
|
|
PolyLine pl ;
|
|
pl.AddUPoint( 0, inA.vpt.back()) ;
|
|
pl.AddUPoint( 1, inA.vpt[0]) ;
|
|
INTVECTOR vEdges = { 7, 0, 4, 1, 5, 2, 6} ;
|
|
// controllo se nEdge1 viene prima di nEdge2. la partenza è da ptTR e l'arrivo è ptBr
|
|
|
|
auto iter1 = find( vEdges.begin(), vEdges.end(), nEdge1) ;
|
|
int nPos1 = distance( vEdges.begin(), iter1) ;
|
|
auto iter2 = find( vEdges.begin(), vEdges.end(), nEdge2) ;
|
|
int nPos2 = distance( vEdges.begin(), iter2) ;
|
|
if ( nPos1 < nPos2)
|
|
return true ;
|
|
else if ( nPos1 > nPos2)
|
|
return false ;
|
|
// nPos1 == nPos2
|
|
else {
|
|
if ( CheckIfBefore( pl, vEdges[nPos1])) {
|
|
return true ;
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::CheckIfBefore( int nEdge1, const Point3d& ptP1, int nEdge2, const Point3d& ptP2) const
|
|
{
|
|
if ( nEdge1 == -1 || nEdge2 == -1)
|
|
return false ;
|
|
// questa funzione è pensata in riferimento al lato 3, quindi nessuno dei due punti può stare su Edge = 3
|
|
INTVECTOR vEdges = { 7, 0, 4, 1, 5, 2, 6} ;
|
|
// controllo se ptP1, che è su nEdge1, viene prima di ptP2, che è su nEdge2. la partenza è da ptTR e l'arrivo è ptBr
|
|
auto iter1 = find( vEdges.begin(), vEdges.end(), nEdge1) ;
|
|
int nPos1 = distance( vEdges.begin(), iter1) ;
|
|
auto iter2 = find( vEdges.begin(), vEdges.end(), nEdge2) ;
|
|
int nPos2 = distance( vEdges.begin(), iter2) ;
|
|
if ( nPos1 < nPos2)
|
|
return true ;
|
|
else if ( nPos1 > nPos2)
|
|
return false ;
|
|
// ( nPos1 == nPos2)
|
|
else {
|
|
PolyLine pl ;
|
|
pl.AddUPoint( 0, ptP2) ;
|
|
pl.AddUPoint( 1, ptP1) ;
|
|
if ( CheckIfBefore( pl, vEdges[nPos1]))
|
|
return true ;
|
|
else
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::CheckIfBefore( int nEdge, const Point3d& ptP1, const Point3d& ptP2, int nEdge2) const
|
|
{
|
|
// i punti devono essere sullo stesso lato nEdge
|
|
// nEdge2 è di backup, in caso nEdge sia un vertice, per capire di quale lato si tratta
|
|
int nEdgeRef = -1 ;
|
|
if ( nEdge != nEdge2 && nEdge2 != -1) {
|
|
if ( min( nEdge, nEdge2) < 4)
|
|
nEdgeRef = min( nEdge, nEdge2) ;
|
|
else if ( AreSameEdge( nEdge, 0) && AreSameEdge( nEdge2, 0))
|
|
nEdgeRef = 0 ;
|
|
else if ( AreSameEdge( nEdge, 1) && AreSameEdge( nEdge2, 1))
|
|
nEdgeRef = 1 ;
|
|
else if ( AreSameEdge( nEdge, 2) && AreSameEdge( nEdge2, 2))
|
|
nEdgeRef = 2 ;
|
|
else if ( AreSameEdge( nEdge, 3) && AreSameEdge( nEdge2, 3))
|
|
nEdgeRef = 3 ;
|
|
}
|
|
else
|
|
nEdgeRef = nEdge ;
|
|
// sul lato nEdge controllo se ptP1 viene prima di ptP2.
|
|
// i lati vengono percorsi in senso antiorario
|
|
if ( AreSameEdge( nEdgeRef, 0)) {
|
|
return ( ptP1.x > ptP2.x) ;
|
|
}
|
|
else if ( AreSameEdge( nEdgeRef, 1)) {
|
|
return ( ptP1.y > ptP2.y) ;
|
|
}
|
|
else if ( AreSameEdge( nEdgeRef, 2)) {
|
|
return ( ptP1.x < ptP2.x) ;
|
|
}
|
|
else if ( AreSameEdge( nEdgeRef, 3)) {
|
|
return ( ptP1.y < ptP2.y) ;
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::AreSameEdge( int nEdge1, int nEdge2) const
|
|
{
|
|
if ( nEdge1 == 0)
|
|
return ( nEdge2 == 4 || nEdge2 == 0 || nEdge2 == 7) ;
|
|
else if ( nEdge1 == 1)
|
|
return ( nEdge2 == 4 || nEdge2 == 1 || nEdge2 == 5) ;
|
|
else if ( nEdge1 == 2)
|
|
return ( nEdge2 == 6 || nEdge2 == 2 || nEdge2 == 5) ;
|
|
else if ( nEdge1 == 3)
|
|
return ( nEdge2 == 6 || nEdge2 == 3 || nEdge2 == 7) ;
|
|
else if ( nEdge1 == 4)
|
|
return ( nEdge2 == 0 || nEdge2 == 1 || nEdge2 == 7 || nEdge2 == 5) ;
|
|
else if ( nEdge1 == 5)
|
|
return ( nEdge2 == 2 || nEdge2 == 1 || nEdge2 == 4 || nEdge2 == 6) ;
|
|
else if ( nEdge1 == 6)
|
|
return ( nEdge2 == 2 || nEdge2 == 3 || nEdge2 == 5 || nEdge2 == 7) ;
|
|
else if ( nEdge1 == 7)
|
|
return ( nEdge2 == 0 || nEdge2 == 3 || nEdge2 == 4 || nEdge2 == 6) ;
|
|
else
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::AddVertex( int nId, const PNTMATRIX& vEdgeVertex, const PNTMATRIX& vEdgeVertex3d, PolyLine& plTrimmedPoly, int& c,
|
|
const Point3d& ptToAdd, PolyLine& plTrimmedPoly3d, bool bForTriangulation, Point3d& ptLast) const
|
|
{
|
|
Point3d ptBr = m_mTree.at(nId).GetBottomRight() ;
|
|
Point3d ptTR = m_mTree.at(nId).GetTopRight() ;
|
|
Point3d ptTl = m_mTree.at(nId).GetTopLeft() ;
|
|
Point3d ptBL = m_mTree.at(nId).GetBottomLeft() ;
|
|
int nVertToSkip = m_mTree.at(nId).m_nVertToErase ;
|
|
// se è il primo punto della PolyLine lo aggiungo
|
|
if ( plTrimmedPoly.GetPointNbr() == 0) {
|
|
// se cerco di aggiungere il vertice che devo saltare, non faccio nulla
|
|
if( ( nVertToSkip == 0 && AreSamePointXYApprox( ptToAdd, ptBL)) ||
|
|
( nVertToSkip == 1 && AreSamePointXYApprox( ptToAdd, ptBr)) ||
|
|
( nVertToSkip == 2 && AreSamePointXYApprox( ptToAdd, ptTR)) ||
|
|
( nVertToSkip == 3 && AreSamePointXYApprox( ptToAdd, ptTl))) {
|
|
// aggiorno l'ultimo punto aggiunto
|
|
ptLast.Set( ptToAdd.x, ptToAdd.y, 0) ;
|
|
return true ;
|
|
}
|
|
|
|
plTrimmedPoly.AddUPoint( c, ptToAdd) ;
|
|
if ( bForTriangulation) {
|
|
Point3d pt3d ;
|
|
GetPoint( ptToAdd.x, ptToAdd.y, pt3d) ;
|
|
plTrimmedPoly3d.AddUPoint( c, pt3d) ;
|
|
}
|
|
++ c ;
|
|
// aggiorno l'ultimo punto aggiunto
|
|
ptLast.Set( ptToAdd.x, ptToAdd.y, 0) ;
|
|
return true ;
|
|
}
|
|
|
|
// verifico di essere allineato con un lato, sennò aggiungo e basta
|
|
Vector3d vDir ;
|
|
if ( ! AreSamePointXYApprox( ptToAdd, ptLast))
|
|
vDir = ptToAdd - ptLast ;
|
|
else
|
|
return false ;
|
|
// se non riesco a normalizzare perché sono troppo vicino ad un vertice allora aggiungo direttamente il vertice
|
|
if ( ! vDir.Normalize()) {
|
|
plTrimmedPoly.EraseLastUPoint() ;
|
|
plTrimmedPoly3d.EraseLastUPoint() ;
|
|
Point3d ptVert ;
|
|
int nVert = -1 ;
|
|
if ( AreSamePointXYApprox( ptToAdd, ptBr)){
|
|
ptVert = ptBr ;
|
|
nVert = 1 ;
|
|
}
|
|
else if ( AreSamePointXYApprox( ptToAdd, ptTR)) {
|
|
ptVert = ptTR ;
|
|
nVert = 2 ;
|
|
}
|
|
else if ( AreSamePointXYApprox( ptToAdd, ptTl)) {
|
|
ptVert = ptTl ;
|
|
nVert = 3 ;
|
|
}
|
|
else if ( AreSamePointXYApprox( ptToAdd, ptBL)) {
|
|
ptVert = ptBL ;
|
|
nVert = 0 ;
|
|
}
|
|
else
|
|
ptVert = ptToAdd ;
|
|
if ( nVert != nVertToSkip) {
|
|
plTrimmedPoly.AddUPoint( c, ptVert) ;
|
|
if( bForTriangulation){
|
|
Point3d pt3d ; GetPoint( ptVert.x, ptVert.y, pt3d) ;
|
|
plTrimmedPoly3d.AddUPoint( c, pt3d) ;
|
|
}
|
|
++ c ;
|
|
}
|
|
// aggiorno l'ultimo punto aggiunto
|
|
ptLast.Set( ptToAdd.x, ptToAdd.y, 0) ;
|
|
return true ;
|
|
}
|
|
if ( abs( vDir.x) > 1 - EPS_SMALL || abs( vDir.y) > 1 - EPS_SMALL) {
|
|
// se su un edge devo fare dei controlli
|
|
// edge 0
|
|
if ( ptToAdd.x >= ptBL.x && ptToAdd.x <= ptTR.x && ptToAdd.y == ptTR.y && abs( vDir.x) > 1 - EPS_SMALL) {
|
|
for ( int t = nVertToSkip == 2 ? 0 : 1 ; t < (int)vEdgeVertex[0].size() ; ++ t) {
|
|
Point3d ptIntermed = vEdgeVertex[0][t] ;
|
|
if ( ptIntermed.x > ptToAdd.x && ptIntermed.x < ptLast.x) {
|
|
plTrimmedPoly.AddUPoint( c, ptIntermed) ;
|
|
if( bForTriangulation)
|
|
plTrimmedPoly3d.AddUPoint( c, vEdgeVertex3d[0][t]) ;
|
|
++ c ;
|
|
}
|
|
}
|
|
bool bVert = AreSamePointXYApprox( ptToAdd, ptTl) ;
|
|
if ( ! ( nVertToSkip == 3 && bVert)) {
|
|
plTrimmedPoly.AddUPoint( c, ptToAdd) ;
|
|
if( bForTriangulation){
|
|
Point3d pt3d ; GetPoint( ptToAdd.x, ptToAdd.y, pt3d) ;
|
|
plTrimmedPoly3d.AddUPoint( c, pt3d) ;
|
|
}
|
|
++ c ;
|
|
}
|
|
}
|
|
// edge 1
|
|
else if ( ptToAdd.y >= ptBL.y && ptToAdd.y <= ptTR.y && ptToAdd.x == ptBL.x && abs( vDir.y) > 1 - EPS_SMALL) {
|
|
for ( int t = nVertToSkip == 3 ? 0 : 1 ; t < (int)vEdgeVertex[1].size() ; ++ t) {
|
|
Point3d ptIntermed = vEdgeVertex[1][t] ;
|
|
if ( ptIntermed.y > ptToAdd.y && ptIntermed.y < ptLast.y) {
|
|
plTrimmedPoly.AddUPoint( c, ptIntermed) ;
|
|
if( bForTriangulation)
|
|
plTrimmedPoly3d.AddUPoint( c, vEdgeVertex3d[1][t]) ;
|
|
++ c ;
|
|
}
|
|
}
|
|
bool bVert = AreSamePointXYApprox( ptToAdd, ptBL) ;
|
|
if ( ! ( nVertToSkip == 0 && bVert)) {
|
|
plTrimmedPoly.AddUPoint( c, ptToAdd) ;
|
|
if( bForTriangulation) {
|
|
Point3d pt3d ; GetPoint( ptToAdd.x, ptToAdd.y, pt3d) ;
|
|
plTrimmedPoly3d.AddUPoint( c, pt3d) ;
|
|
}
|
|
++ c ;
|
|
}
|
|
}
|
|
// edge 2
|
|
else if ( ptToAdd.x >= ptBL.x && ptToAdd.x <= ptTR.x && ptToAdd.y == ptBL.y && abs( vDir.x) > 1 - EPS_SMALL) {
|
|
for ( int t = nVertToSkip == 0 ? 0 : 1 ; t < (int)vEdgeVertex[2].size() ; ++ t) {
|
|
Point3d ptIntermed = vEdgeVertex[2][t] ;
|
|
if ( ptIntermed.x < ptToAdd.x && ptIntermed.x > ptLast.x) {
|
|
plTrimmedPoly.AddUPoint( c, ptIntermed) ;
|
|
if( bForTriangulation)
|
|
plTrimmedPoly3d.AddUPoint( c, vEdgeVertex3d[2][t]) ;
|
|
++ c ;
|
|
}
|
|
}
|
|
bool bVert = AreSamePointXYApprox( ptToAdd, ptBr) ;
|
|
if ( ! ( nVertToSkip == 1 && bVert)) {
|
|
plTrimmedPoly.AddUPoint( c, ptToAdd) ;
|
|
if( bForTriangulation){
|
|
Point3d pt3d ; GetPoint( ptToAdd.x, ptToAdd.y, pt3d) ;
|
|
plTrimmedPoly3d.AddUPoint( c, pt3d) ;
|
|
}
|
|
++ c ;
|
|
}
|
|
}
|
|
// edge 3
|
|
else if ( ptToAdd.y >= ptBL.y && ptToAdd.y <= ptTR.y && ptToAdd.x == ptTR.x && abs( vDir.y) > 1 - EPS_SMALL) {
|
|
for ( int t = nVertToSkip == 1 ? 0 : 1 ; t < (int)vEdgeVertex[3].size() ; ++ t) {
|
|
Point3d ptIntermed = vEdgeVertex[3][t] ;
|
|
if ( ptIntermed.y < ptToAdd.y && ptIntermed.y > ptLast.y) {
|
|
plTrimmedPoly.AddUPoint( c, ptIntermed) ;
|
|
if( bForTriangulation)
|
|
plTrimmedPoly3d.AddUPoint( c, vEdgeVertex3d[3][t]) ;
|
|
++ c ;
|
|
}
|
|
}
|
|
bool bVert = AreSamePointXYApprox( ptToAdd, ptTR) ;
|
|
if ( ! ( nVertToSkip == 2 && bVert)) {
|
|
plTrimmedPoly.AddUPoint( c, ptToAdd) ;
|
|
if( bForTriangulation) {
|
|
Point3d pt3d ; GetPoint( ptToAdd.x, ptToAdd.y, pt3d) ;
|
|
plTrimmedPoly3d.AddUPoint( c, pt3d) ;
|
|
}
|
|
++ c ;
|
|
}
|
|
}
|
|
// sono allineato con un lato, ma NON sono su un lato
|
|
// aggiungo e basta
|
|
else {
|
|
plTrimmedPoly.AddUPoint( c, ptToAdd) ;
|
|
if( bForTriangulation) {
|
|
Point3d pt3d ; GetPoint( ptToAdd.x, ptToAdd.y, pt3d) ;
|
|
plTrimmedPoly3d.AddUPoint( c, pt3d) ;
|
|
}
|
|
++ c ;
|
|
}
|
|
}
|
|
// non su un edge, quindi aggiungo e basta
|
|
else {
|
|
plTrimmedPoly.AddUPoint( c, ptToAdd) ;
|
|
if( bForTriangulation) {
|
|
Point3d pt3d ; GetPoint( ptToAdd.x, ptToAdd.y, pt3d) ;
|
|
plTrimmedPoly3d.AddUPoint( c, pt3d) ;
|
|
}
|
|
++ c ;
|
|
}
|
|
// aggiorno l'ultimo punto aggiunto
|
|
ptLast.Set( ptToAdd.x, ptToAdd.y, 0) ;
|
|
return true ;
|
|
}
|
|
|
|
//usando le intersezioni con le celle
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::SetRightEdgeIn( int nId)
|
|
{
|
|
Cell& cCell = m_mTree[nId] ;
|
|
// categorizzo la cella in base a quanta parte del lato destro è contenuta all'interno delle curve di trim
|
|
// RightEdgeIn -> 0 non contenuto ; 1 contenuto ; 2 in parte contenuto
|
|
int nPass = (int) cCell.m_vInters.size() ;
|
|
if ( nPass == 0) {
|
|
cCell.m_nRightEdgeIn = -1 ;
|
|
return true ;
|
|
}
|
|
bool bDone = false ;
|
|
// se ho solo loop interni devo controllare se il più esterno è CCW ( lato destro esterno) o CW ( lato destro interno)
|
|
if ( cCell.m_nFlag == 2) {
|
|
bool bAllContained = true ;
|
|
bool bContained = false ;
|
|
Inters inA ;
|
|
int nInters = (int) cCell.m_vInters.size() ;
|
|
for ( int n = 0 ; n < nInters ; ++ n) {
|
|
inA = cCell.m_vInters[n] ;
|
|
if ( inA.nIn == -1) {
|
|
if ( ! inA.bCCW) {
|
|
bContained = false ;
|
|
Inters inB = cCell.m_vInters[0] ;
|
|
for ( int c = 0 ; c < nInters ; ++ c) {
|
|
inB = cCell.m_vInters[c] ;
|
|
if ( inB.nIn == -1) {
|
|
if ( inB != inA && inB.nChunk == inA.nChunk && inB.bCCW) {
|
|
bContained = true ;
|
|
break ;
|
|
}
|
|
}
|
|
else
|
|
break ;
|
|
}
|
|
bAllContained = bAllContained && bContained ;
|
|
}
|
|
}
|
|
else
|
|
break ;
|
|
}
|
|
// se i loop CW interni sono tutti contenuti in un loop CW allora il right edge è esterno
|
|
bool bRightEdgeIn = ! bAllContained ;
|
|
if ( bRightEdgeIn)
|
|
cCell.m_nRightEdgeIn = 1 ;
|
|
else
|
|
cCell.m_nRightEdgeIn = 0 ;
|
|
return true ;
|
|
}
|
|
// se ho inters sul lato destro ( nEdge == 3) allora in parte è dentro
|
|
for ( int k = 0 ; k < nPass ; ++ k) {
|
|
if ( cCell.m_vInters[k].nIn == 3 || cCell.m_vInters[k].nOut == 3) {
|
|
cCell.m_nRightEdgeIn = 2 ;
|
|
bDone = true ;
|
|
break ;
|
|
}
|
|
// considero anche ingressi/ uscite dai vertici 6 e 7
|
|
// controllo nei vertici
|
|
else if ( cCell.m_vInters[k].nOut == 6 && cCell.m_vInters[k].nIn == 7) {
|
|
cCell.m_nRightEdgeIn = 1 ;
|
|
bDone = true ;
|
|
break ;
|
|
}
|
|
else if ( cCell.m_vInters[k].nOut == 7 && cCell.m_vInters[k].nIn == 6) {
|
|
cCell.m_nRightEdgeIn = 0 ;
|
|
bDone = true ;
|
|
break ;
|
|
}
|
|
}
|
|
// se non ho inters sul lato destro devo verificare se è tutto dentro o tutto fuori
|
|
if ( ! bDone) {
|
|
// ciclo sulle inters e cerco quella più a destra, se ha un'uscita più bassa di un'entrata allora il right edge è compreso!
|
|
bool bFound = false ;
|
|
bool bRightMost = false ;
|
|
int nEdgeUp = 6, nEdgeDown = 7, nLoop ;
|
|
Point3d ptDown( cCell.GetTopRight().x, cCell.GetBottomLeft().y) ;
|
|
Point3d ptUp = cCell.GetTopRight() ;
|
|
for ( int k = 0 ; k < nPass ; ++ k) {
|
|
if ( cCell.m_vInters[k].nIn != -1) {
|
|
// trovo il loop che ha l'ingresso o l'uscita più a destra
|
|
if ( CheckIfBefore( cCell.m_vInters[k].nIn, cCell.m_vInters[k].vpt[0], nEdgeUp, ptUp) ||
|
|
CheckIfBefore( cCell.m_vInters[k].nOut, cCell.m_vInters[k].vpt.back(), nEdgeUp, ptUp) ||
|
|
CheckIfBefore( nEdgeDown, ptDown, cCell.m_vInters[k].nIn, cCell.m_vInters[k].vpt[0]) ||
|
|
CheckIfBefore( nEdgeDown, ptDown, cCell.m_vInters[k].nOut, cCell.m_vInters[k].vpt.back())) {
|
|
nLoop = k ;
|
|
if ( CheckIfBefore( cCell.m_vInters[k])) {
|
|
ptUp = cCell.m_vInters[k].vpt[0] ;
|
|
nEdgeUp = cCell.m_vInters[k].nIn ;
|
|
ptDown = cCell.m_vInters[k].vpt.back() ;
|
|
nEdgeDown = cCell.m_vInters[k].nOut ;
|
|
}
|
|
else {
|
|
ptUp = cCell.m_vInters[k].vpt.back() ;
|
|
nEdgeUp = cCell.m_vInters[k].nOut ;
|
|
ptDown = cCell.m_vInters[k].vpt[0] ;
|
|
nEdgeDown = cCell.m_vInters[k].nIn ;
|
|
}
|
|
bFound = true ;
|
|
}
|
|
}
|
|
else
|
|
continue ;
|
|
}
|
|
if ( bFound && CheckIfBefore( cCell.m_vInters[nLoop])) {
|
|
bRightMost = true ;
|
|
}
|
|
// se il mio campione attraversa dall'alto al basso allora il lato destro è dentro!!!
|
|
if ( bFound && bRightMost)
|
|
cCell.m_nRightEdgeIn = 1 ;
|
|
else
|
|
cCell.m_nRightEdgeIn = 0 ;
|
|
}
|
|
|
|
return ( cCell.m_nRightEdgeIn != -1) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::CategorizeCell( int nId)
|
|
{
|
|
Cell& cCell = m_mTree[nId] ;
|
|
if ( cCell.m_nFlag != -1) {
|
|
return true ;
|
|
}
|
|
INTVECTOR vNeigh, vNeigh1 ;
|
|
GetLeftNeigh( nId, vNeigh) ;
|
|
// mi servono i vicini di sinistra per capire se sono dentro il loop o fuori
|
|
// nRightEdgeIn // 0 right edge fuori, 1 right edge dentro, 2 metà e metà
|
|
// nFlag // 0 fuori, 1 intersecata, 2 contiene loop, 3 = 1 & 2, 4 dentro
|
|
if ( vNeigh.empty()) {
|
|
cCell.m_nFlag = 0 ;
|
|
}
|
|
else if ( vNeigh.size() == 1) {
|
|
if ( m_mTree[vNeigh[0]].m_nRightEdgeIn == 1)
|
|
cCell.m_nFlag = 4 ;
|
|
else if ( m_mTree[vNeigh[0]].m_nRightEdgeIn == 0 || m_mTree[vNeigh[0]].m_nRightEdgeIn == -1) {
|
|
// devo verificare se la cella è intersecata
|
|
if ( m_mTree[vNeigh[0]].m_nFlag == 1 || m_mTree[vNeigh[0]].m_nFlag == 3)
|
|
cCell.m_nFlag = 0 ;
|
|
else {
|
|
if ( m_mTree[vNeigh[0]].m_nFlag == 4)
|
|
cCell.m_nFlag = 4 ;
|
|
else if ( m_mTree[vNeigh[0]].m_nFlag == 0)
|
|
cCell.m_nFlag = 0 ;
|
|
}
|
|
}
|
|
// se solo parte del right edge del vicino è compreso, allora devo verificare se la cella è contenuta o no
|
|
// guardando nFlag del vicino bottom, che è già categorizzato!
|
|
else if ( m_mTree[vNeigh[0]].m_nRightEdgeIn == 2) {
|
|
GetBottomNeigh( nId, vNeigh1) ;
|
|
if ( ! vNeigh1.empty()) {
|
|
int nNeigh = vNeigh1[0] ;
|
|
if ( m_mTree[nNeigh].m_nFlag == 0 || m_mTree[nNeigh].m_nFlag == 2) {
|
|
cCell.m_nFlag = 0 ;
|
|
}
|
|
else if ( m_mTree[nNeigh].m_nFlag == 4) {
|
|
cCell.m_nFlag = 4 ;
|
|
}
|
|
else if ( m_mTree[nNeigh].m_nFlag == 1 || m_mTree[nNeigh].m_nFlag == 3) {
|
|
if ( m_mTree[nNeigh].m_nRightEdgeIn == 0 || m_mTree[nNeigh].m_nRightEdgeIn == 1){
|
|
// devo capire dove viene tagliato il vicino bottom per poter trarre qualche informazione
|
|
// se sul lato top del vicino bottom l'intersezione più vicina alla cella in esame (al suo ptBr) è un'uscita, allora sono dentro
|
|
// in tutti gli altri casi sono fuori
|
|
int nPass = int( m_mTree[nNeigh].m_vInters.size()) ;
|
|
bool bIntersTop = false ;
|
|
for ( int r = 0 ; r < nPass ; ++ r) {
|
|
if ( m_mTree[nNeigh].m_vInters[r].nOut == 0 || m_mTree[nNeigh].m_vInters[r].nOut == 7) {
|
|
bIntersTop = true ;
|
|
break ;
|
|
}
|
|
}
|
|
// se nella cella bottom non ho intersezioni con il lato top allora la cella in esame è fuori
|
|
if ( ! bIntersTop && m_mTree[nNeigh].m_nRightEdgeIn == 0)
|
|
cCell.m_nFlag = 0 ;
|
|
else if ( ! bIntersTop && m_mTree[nNeigh].m_nRightEdgeIn == 1)
|
|
cCell.m_nFlag = 4 ;
|
|
else {
|
|
Point3d ptBr = cCell.GetBottomRight() ;
|
|
bool bFound = false ;
|
|
Point3d ptLeftMostInters = m_mTree[nNeigh].GetTopRight() ;
|
|
for ( int r = 0 ; r < nPass ; ++ r) {
|
|
Point3d ptIntersOut = m_mTree[nNeigh].m_vInters[r].vpt.back() ;
|
|
Point3d ptIntersIn = m_mTree[nNeigh].m_vInters[r].vpt[0] ;
|
|
if ( (m_mTree[nNeigh].m_vInters[r].nOut == 0 || m_mTree[nNeigh].m_vInters[r].nOut == 7) &&
|
|
( CheckIfBefore( 2, ptBr, ptIntersOut) || AreSamePointApprox( ptBr, ptIntersOut))) {
|
|
if (ptIntersOut.x < ptLeftMostInters.x) {
|
|
ptLeftMostInters = ptIntersOut ;
|
|
bFound = true ;
|
|
}
|
|
}
|
|
if ( (m_mTree[nNeigh].m_vInters[r].nIn == 0 || m_mTree[nNeigh].m_vInters[r].nIn == 7) &&
|
|
( CheckIfBefore( 2, ptBr, ptIntersIn) || AreSamePointApprox( ptBr, ptIntersIn))) {
|
|
if (ptIntersIn.x < ptLeftMostInters.x) {
|
|
ptLeftMostInters = ptIntersIn ;
|
|
bFound = false ;
|
|
}
|
|
}
|
|
}
|
|
if ( bFound)
|
|
cCell.m_nFlag = 4 ;
|
|
else
|
|
cCell.m_nFlag = 0 ;
|
|
}
|
|
}
|
|
else if ( m_mTree[nNeigh].m_nRightEdgeIn == 2) {
|
|
// in questo caso devo verificare le intersezioni sul lato destro e top del vicino bottom
|
|
// scorro e cerco l'intersezione più vicina al ptBr della cella in esame( percorrendo il bordo della cella bottom in senso orario)
|
|
// se è un nIn allora la cella in esame è dentro, altrimenti è fuori
|
|
int nPass = (int) m_mTree[nNeigh].m_vInters.size() ;
|
|
bool bFound = false ;
|
|
Point3d ptTopLeftMostInters = m_mTree[nNeigh].GetBottomRight() ;
|
|
for ( int r = 0 ; r < nPass ; ++ r) {
|
|
int nOut = m_mTree[nNeigh].m_vInters[r].nOut ;
|
|
int nIn = m_mTree[nNeigh].m_vInters[r].nIn ;
|
|
if ( (AreSameEdge( nOut, 3) || nOut == 0)) {
|
|
Point3d ptInters = m_mTree[nNeigh].m_vInters[r].vpt.back() ;
|
|
if ( ( abs( ptInters.x - ptTopLeftMostInters.x) > EPS_SMALL && ptInters.x < ptTopLeftMostInters.x) ||
|
|
(abs( ptInters.y - ptTopLeftMostInters.y) > EPS_SMALL && ptInters.y > ptTopLeftMostInters.y)) {
|
|
ptTopLeftMostInters = ptInters ;
|
|
bFound = true ;
|
|
}
|
|
}
|
|
if ( (AreSameEdge( nIn, 3) || nIn == 0)) {
|
|
Point3d ptInters = m_mTree[nNeigh].m_vInters[r].vpt[0] ;
|
|
if ( ( abs( ptInters.x - ptTopLeftMostInters.x) > EPS_SMALL && ptInters.x < ptTopLeftMostInters.x) ||
|
|
(abs( ptInters.y - ptTopLeftMostInters.y) > EPS_SMALL && ptInters.y > ptTopLeftMostInters.y)) {
|
|
ptTopLeftMostInters = ptInters ;
|
|
bFound = false ;
|
|
}
|
|
}
|
|
}
|
|
if ( bFound)
|
|
cCell.m_nFlag = 4 ;
|
|
else
|
|
cCell.m_nFlag = 0 ;
|
|
}
|
|
}
|
|
}
|
|
// se non ho vicini bottom devo per forza guardare il vicino a sinistra
|
|
else {
|
|
Point3d ptInters = m_mTree[vNeigh[0]].GetTopRight() ;
|
|
int nEdge = 3 ;
|
|
bool bIntersIn = false ;
|
|
for ( int k = 0 ; k < (int) m_mTree[vNeigh[0]].m_vInters.size() ; ++ k) {
|
|
if ( AreSameEdge( m_mTree[vNeigh[0]].m_vInters[k].nIn, 3)) {
|
|
if ( CheckIfBefore( nEdge, m_mTree[vNeigh[0]].m_vInters[k].vpt[0], ptInters, 3)) {
|
|
ptInters = m_mTree[vNeigh[0]].m_vInters[k].vpt[0] ;
|
|
bIntersIn = true ;
|
|
}
|
|
}
|
|
if ( AreSameEdge( m_mTree[vNeigh[0]].m_vInters[k].nOut, 3)) {
|
|
if ( CheckIfBefore( nEdge, m_mTree[vNeigh[0]].m_vInters[k].vpt.back(), ptInters, 3)) {
|
|
ptInters = m_mTree[vNeigh[0]].m_vInters[k].vpt.back() ;
|
|
bIntersIn = false ;
|
|
}
|
|
}
|
|
}
|
|
if ( bIntersIn)
|
|
cCell.m_nFlag = 4 ;
|
|
else
|
|
cCell.m_nFlag = 0 ;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for ( int r = 0 ; r < (int)vNeigh.size() ; ++ r) {
|
|
if ( m_mTree[vNeigh[r]].m_nRightEdgeIn == 1) {
|
|
cCell.m_nFlag = 4 ;
|
|
break ;
|
|
}
|
|
else if ( m_mTree[vNeigh[r]].m_nRightEdgeIn == 0 || m_mTree[vNeigh[r]].m_nRightEdgeIn == -1) {
|
|
if ( m_mTree[vNeigh[r]].m_nFlag == 4) {
|
|
cCell.m_nFlag = 4 ;
|
|
break ;
|
|
}
|
|
else if ( m_mTree[vNeigh[r]].m_nFlag == 0) {
|
|
cCell.m_nFlag = 0 ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::CheckIfBetween( const Inters& inA, const Inters& inB) const
|
|
{
|
|
// controllo se inB è compreso tra l'end e lo start di inA
|
|
// ( dall'end di A percorro i bordi della cella fino a tornare allo start e devo incontrare In e Out di B)
|
|
INTVECTOR vEdges ;
|
|
int nEdge = inA.nOut ;
|
|
if ( nEdge > 3 && nEdge != 7) {
|
|
nEdge = nEdge - 4 ;
|
|
}
|
|
else if ( nEdge == 7)
|
|
nEdge = 0 ;
|
|
if ( AreSameEdge( inA.nIn, inA.nOut) && ! CheckIfBefore( inA.nIn, inA.vpt[0], inA.vpt.back(), nEdge))
|
|
vEdges.push_back( nEdge) ;
|
|
int nEdgeIn = inA.nIn ;
|
|
if ( nEdgeIn > 3 && nEdgeIn != 7) {
|
|
nEdgeIn = nEdgeIn - 4 ;
|
|
}
|
|
else if ( nEdgeIn == 7)
|
|
nEdgeIn = 0 ;
|
|
// creo la sequenza di Edges da scorrere per trovare i possibili validNextStart
|
|
while ( ! AreSameEdge( nEdge, nEdgeIn) || vEdges.empty()) {
|
|
vEdges.push_back( nEdge) ;
|
|
if ( nEdge == 3)
|
|
nEdge = 0 ;
|
|
else
|
|
++ nEdge ;
|
|
}
|
|
if ( ! AreSameEdge( inA.nIn, inA.nOut))
|
|
vEdges.push_back( nEdge) ;
|
|
bool bFound = false ;
|
|
for ( int i : vEdges) {
|
|
if ( AreSameEdge( inB.nIn, i)) {
|
|
if ( AreSameEdge( inB.nIn, inA.nIn) && AreSameEdge( inA.nIn, inA.nOut) && AreSameEdge( inB.nIn, inA.nOut)) {
|
|
nEdge = inA.nIn ;
|
|
//se l'inizio di A è prima della fine, allora devo controllare che B sia compreso tra Out e In ( esterno)
|
|
if ( CheckIfBefore( nEdge, inA.vpt[0], inA.vpt.back(), inA.nOut)) {
|
|
if ( CheckIfBefore( nEdge, inA.vpt.back(), inB.vpt[0], inA.nOut ) || CheckIfBefore( nEdge, inB.vpt[0], inA.vpt[0], inA.nOut))
|
|
bFound = true ;
|
|
}
|
|
// se l'inizio di A è dopo la fine, allora devo controllare che B sia compreso tra In e Out ( interno)
|
|
else {
|
|
if ( CheckIfBefore( nEdge, inA.vpt.back(), inB.vpt[0], inA.nOut) && CheckIfBefore( nEdge, inB.vpt[0], inA.vpt[0], inA.nOut))
|
|
bFound = true ;
|
|
}
|
|
}
|
|
else if ( AreSameEdge( inB.nIn, inA.nOut)) {
|
|
PolyLine pl ;
|
|
pl.AddUPoint( 0, inA.vpt[0]) ;
|
|
pl.AddUPoint( 1, inA.vpt.back()) ;
|
|
Point3d ptEnd ; pl.GetLastPoint( ptEnd) ;
|
|
if ( CheckIfBefore( i, ptEnd, inB.vpt[0]))
|
|
bFound = true ;
|
|
}
|
|
else if ( AreSameEdge( inB.nIn, inA.nIn)) {
|
|
//devo controllare il loop b sia prima dell'inizio di A
|
|
if ( CheckIfBefore( inA.nIn, inB.vpt[0], inA.vpt[0], inB.nIn))
|
|
bFound = true ;
|
|
}
|
|
else
|
|
// devo controllare che inB sia prima di OutB
|
|
if ( AreSameEdge( inB.nOut, inB.nIn) && CheckIfBefore( inB.nOut, inB.vpt[0], inB.vpt.back(), inB.nIn)) {
|
|
bFound = true ;
|
|
}
|
|
else if ( ! AreSameEdge( inB.nOut,inB.nIn))
|
|
bFound = true ;
|
|
}
|
|
// se incontro prima l'uscita dell'ingresso, sicuramente InB NON è un validNextStart
|
|
if ( AreSameEdge( inB.nOut, i) && ! bFound && CheckIfBefore( i, inA.vpt[0], inB.vpt.back()) && CheckIfBefore( i, inA.vpt.back(), inB.vpt.back()))
|
|
break ;
|
|
}
|
|
return bFound ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::CheckIfBefore( int nEdgeA, int nEdgeB) const
|
|
{
|
|
// questa funzione è pensata per edge che sono uguali per la funzione AreSameEdge
|
|
// su quell'edge verifico se EdgeA viene prima di EdgeB ( il senso di precorrenza è sempre antiorario)
|
|
switch ( nEdgeA ) {
|
|
case 0 : return nEdgeB == 4 ? true : false ; break ;
|
|
case 1 : return nEdgeB == 5 ? true : false ; break ;
|
|
case 2 : return nEdgeB == 6 ? true : false ; break ;
|
|
case 3 : return nEdgeB == 7 ? true : false ; break ;
|
|
case 4 :
|
|
if ( nEdgeB == 1 || nEdgeB == 5)
|
|
return true ;
|
|
break ;
|
|
case 5 :
|
|
if ( nEdgeB == 2 || nEdgeB == 6)
|
|
return true ;
|
|
break ;
|
|
case 6 :
|
|
if ( nEdgeB == 3 || nEdgeB == 7)
|
|
return true ;
|
|
break ;
|
|
case 7 :
|
|
if ( nEdgeB == 0 || nEdgeB == 4)
|
|
return true ;
|
|
break ;
|
|
default : return false ;
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::GetLeaves( vector<Cell>& vLeaves) const
|
|
{
|
|
vLeaves.clear() ;
|
|
for ( int k : m_vnLeaves) {
|
|
Cell cToAdd = m_mTree.at( k) ;
|
|
vLeaves.push_back( cToAdd) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::OnWhichEdge( int nId, const Point3d& ptToAssign, int& nEdge) const
|
|
{
|
|
Point3d ptTR = m_mTree.at( nId).GetTopRight() ;
|
|
Point3d ptBL = m_mTree.at( nId).GetBottomLeft() ;
|
|
Point3d ptTl ( ptBL.x, ptTR.y) ;
|
|
Point3d ptBr ( ptTR.x, ptBL.y) ;
|
|
|
|
if ( AreSamePointXYApprox( ptToAssign, ptTR))
|
|
nEdge = 7 ;
|
|
else if ( AreSamePointXYApprox( ptToAssign, ptTl))
|
|
nEdge = 4 ;
|
|
else if ( AreSamePointXYApprox( ptToAssign, ptBL))
|
|
nEdge = 5 ;
|
|
else if ( AreSamePointXYApprox( ptToAssign, ptBr))
|
|
nEdge = 6 ;
|
|
else if ( ptToAssign.x > ptBL.x && ptToAssign.x < ptTR.x && abs( ptToAssign.y - ptTR.y) < EPS_SMALL)
|
|
nEdge = 0 ;
|
|
else if ( ptToAssign.y > ptBL.y && ptToAssign.y < ptTR.y && abs( ptToAssign.x - ptBL.x) < EPS_SMALL)
|
|
nEdge = 1 ;
|
|
else if ( ptToAssign.x > ptBL.x && ptToAssign.x < ptTR.x && abs( ptToAssign.y - ptBL.y) < EPS_SMALL)
|
|
nEdge = 2 ;
|
|
else if ( ptToAssign.y > ptBL.y && ptToAssign.y < ptTR.y && abs( ptToAssign.x - ptTR.x) < EPS_SMALL)
|
|
nEdge = 3 ;
|
|
else if ( ptToAssign.y > ptBL.y && ptToAssign.y < ptTR.y && ptToAssign.x > ptBL.x && ptToAssign.x < ptTR.x)
|
|
nEdge = -1 ;
|
|
else
|
|
return false ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::GetEdges3D( vector<ICRVCOMPOPOVECTOR>& mCCEdges, POLYLINEVECTOR& vPolygons)
|
|
{
|
|
// se la superficie non è trimmata ricostruisco dalle celle al bordo
|
|
if ( ! m_bTrimmed) {
|
|
INTMATRIX vEdges ; // le righe sono gli edge a partire dallo 0, le colonne sono le celle che compongono quell'edge
|
|
// recupero le celle sui quattro bordi
|
|
vEdges.emplace_back() ;
|
|
GetRootNeigh( 0, vEdges[0]) ;
|
|
// le celle sui bordi orizzontali sono ordinate per x o y crescente, ma i io voglio costruire gli edge in senso antiorario a partire dal ptTR,
|
|
// quindi devo invertire gli Edge 0 e 1
|
|
std::reverse( vEdges[0].begin(), vEdges[0].end()) ;
|
|
vEdges.emplace_back() ;
|
|
GetRootNeigh( 1, vEdges[1]) ;
|
|
std::reverse( vEdges[1].begin(), vEdges[1].end()) ;
|
|
vEdges.emplace_back() ;
|
|
GetRootNeigh( 2, vEdges[2]) ;
|
|
vEdges.emplace_back() ;
|
|
GetRootNeigh( 3, vEdges[3]) ;
|
|
|
|
// scorro sui gruppi di polyline che rappresentano i poligoni delle celle lungo un lato
|
|
for ( int i = 0 ; i < int( vEdges.size()) ; ++i) {
|
|
mCCEdges.emplace_back() ;
|
|
mCCEdges.back().emplace_back(CreateBasicCurveComposite()) ;
|
|
// scorro sui poligoni delle celle di un lato
|
|
for ( int c = 0 ; c < int( vEdges[i].size()) ; ++c) {
|
|
Cell& cNeigh = m_mTree.at(vEdges[i][c]) ;
|
|
PolyLine& plCell = vPolygons[cNeigh.m_vnPolyId[0]] ;
|
|
Point3d pt ; plCell.GetFirstPoint( pt) ;
|
|
Point3d pt3d ;
|
|
// a seconda del lato controllo di stare scorrendo il poligono prendendo solo i punti su quel lato
|
|
if ( i == 0) {
|
|
while ( ! AreSamePointXYApprox(pt, cNeigh.GetTopRight()) && plCell.GetNextPoint(pt) ) {
|
|
continue ;
|
|
}
|
|
Point3d pt3d ; GetPoint( cNeigh.GetTopRight().x, cNeigh.GetTopRight().y, pt3d) ;
|
|
mCCEdges.back().back()->AddPoint( pt3d) ;
|
|
// scorro fino alla fine di quel lato
|
|
while ( plCell.GetNextPoint( pt) && ! AreSamePointXYApprox(pt, cNeigh.GetTopLeft())) {
|
|
GetPoint( pt.x, pt.y, pt3d) ;
|
|
mCCEdges.back().back()->AddLine( pt3d) ;
|
|
}
|
|
pt3d = ORIG ; GetPoint( cNeigh.GetBottomLeft().x, cNeigh.GetTopRight().y, pt3d) ;
|
|
mCCEdges.back().back()->AddLine( pt3d) ;
|
|
if ( ! mCCEdges.back().back()->IsValid())
|
|
mCCEdges.back().back()->FromPoint( pt3d) ;
|
|
}
|
|
else if ( i == 1 ) {
|
|
while ( ! AreSamePointXYApprox(pt, cNeigh.GetTopLeft()) && plCell.GetNextPoint( pt)) {
|
|
continue ;
|
|
}
|
|
Point3d pt3d ; GetPoint( cNeigh.GetBottomLeft().x, cNeigh.GetTopRight().y, pt3d) ;
|
|
mCCEdges.back().back()->AddPoint( pt3d) ;
|
|
// scorro fino alla fine di quel lato
|
|
while ( plCell.GetNextPoint( pt) && ! AreSamePointXYApprox(pt, cNeigh.GetBottomLeft())) {
|
|
GetPoint( pt.x, pt.y, pt3d) ;
|
|
mCCEdges.back().back()->AddLine( pt3d) ;
|
|
}
|
|
pt3d = ORIG ; GetPoint( cNeigh.GetBottomLeft().x, cNeigh.GetBottomLeft().y, pt3d) ;
|
|
mCCEdges.back().back()->AddLine( pt3d) ;
|
|
if ( ! mCCEdges.back().back()->IsValid())
|
|
mCCEdges.back().back()->FromPoint( pt3d) ;
|
|
}
|
|
else if ( i == 2) {
|
|
while ( ! AreSamePointXYApprox(pt, cNeigh.GetBottomLeft()) && plCell.GetNextPoint( pt)) {
|
|
continue ;
|
|
}
|
|
Point3d pt3d ; GetPoint( cNeigh.GetBottomLeft().x, cNeigh.GetBottomLeft().y, pt3d) ;
|
|
mCCEdges.back().back()->AddPoint( pt3d) ;
|
|
// scorro fino alla fine di quel lato
|
|
while ( plCell.GetNextPoint( pt) && ! AreSamePointXYApprox(pt, cNeigh.GetBottomRight())) {
|
|
GetPoint( pt.x, pt.y, pt3d) ;
|
|
mCCEdges.back().back()->AddLine( pt3d) ;
|
|
}
|
|
pt3d = ORIG ; GetPoint( cNeigh.GetTopRight().x, cNeigh.GetBottomLeft().y, pt3d) ;
|
|
mCCEdges.back().back()->AddLine( pt3d) ;
|
|
if ( ! mCCEdges.back().back()->IsValid())
|
|
mCCEdges.back().back()->FromPoint( pt3d) ;
|
|
}
|
|
else if ( i == 3) {
|
|
while ( ! AreSamePointXYApprox(pt, cNeigh.GetBottomRight()) && plCell.GetNextPoint( pt)) {
|
|
continue ;
|
|
}
|
|
Point3d pt3d ; GetPoint( cNeigh.GetTopRight().x, cNeigh.GetBottomLeft().y, pt3d) ;
|
|
mCCEdges.back().back()->AddPoint( pt3d) ;
|
|
// scorro fino alla fine di quel lato
|
|
while ( plCell.GetNextPoint( pt) && ! AreSamePointXYApprox(pt, cNeigh.GetTopRight())) {
|
|
GetPoint( pt.x, pt.y, pt3d) ;
|
|
mCCEdges.back().back()->AddLine( pt3d) ;
|
|
}
|
|
pt3d = ORIG ; GetPoint( cNeigh.GetTopRight().x, cNeigh.GetTopRight().y, pt3d) ;
|
|
mCCEdges.back().back()->AddLine( pt3d) ;
|
|
if ( ! mCCEdges.back().back()->IsValid())
|
|
mCCEdges.back().back()->FromPoint( pt3d) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se la superficie è trimmata ricostruisco dai loop splittati ricostruiti durante il TraceLoop
|
|
else {
|
|
// per ogni edge creo le compo che compongono l'edge dopo i trim ( possono essere più compo separate tra loro)
|
|
for ( int i = 0 ; i < 4 ; ++i) {
|
|
mCCEdges.emplace_back() ;
|
|
INTVECTOR vId ;
|
|
Point3d ptNear = m_mTree.at(-1).GetBottomLeft() ;
|
|
while( m_vCEdge2D[i].second.GetChainFromNear(ptNear, false, vId) ) {
|
|
PtrOwner<ICurveComposite> pCC3D( CreateCurveComposite()) ;
|
|
int nInd = abs( vId[0]) - 1 ;
|
|
Point3d pt2D = m_vCEdge2D[i].first[nInd].first ;
|
|
Point3d pt3D ; GetPoint( pt2D.x, pt2D.y, pt3D) ;
|
|
pCC3D->AddPoint( pt3D) ;
|
|
for ( int j = 1 ; j < int( vId.size()) ; ++j) {
|
|
nInd = abs( vId[j]) - 1 ;
|
|
pt2D = m_vCEdge2D[i].first[nInd].second ;
|
|
GetPoint( pt2D.x, pt2D.y, pt3D) ;
|
|
pCC3D->AddLine( pt3D) ;
|
|
}
|
|
if ( ! pCC3D->IsValid())
|
|
pCC3D->FromPoint( pt3D) ;
|
|
// qui devo fare dei controlli prima di aggiungere questa polyline?
|
|
mCCEdges[i].emplace_back( Release(pCC3D)) ;
|
|
}
|
|
}
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::AddCutsToRoot( POLYLINEVECTOR& vCuts)
|
|
{
|
|
// questa funzione dà per scontato di stare lavorando sulla root di uno spazio parametrico di cui si sta solamente calcolando
|
|
// il Contour. La funzione da chiamare dopo questa è la CreateCellContour.
|
|
// i tagli sono sempre delle linee che tagliano da parte a parte la cella, quindi sono curve aperte
|
|
if ( m_mTree.size() > 1)
|
|
return false ;
|
|
int nRoot = -1 ;
|
|
for ( int i = 0 ; i < int( vCuts.size()); ++i) {
|
|
PolyLine pl = vCuts.at( i) ;
|
|
m_mTree.at( nRoot).m_vInters.emplace_back() ;
|
|
Point3d pt ; pl.GetFirstPoint( pt) ;
|
|
int nEdgeIn ;
|
|
// se non trovo il primo punto esattamente su un lato, estendo il primo tratto della polyline all'inditro finchè trovo un'intersezione con un lato
|
|
// questa intersezione diventa il nuovo primo punto del vettore intersezione
|
|
if ( ! OnWhichEdge(nRoot, pt, nEdgeIn) ) {
|
|
Point3d ptSecond ; pl.GetNextPoint( ptSecond) ;
|
|
PtrOwner<ICurveLine> pCL( CreateCurveLine()) ; pCL->SetPVL( ptSecond, pt - ptSecond, 1e6) ;
|
|
PtrOwner<ICurveLine> pCL0( CreateCurveLine()) ; pCL0->Set( m_mTree.at(-1).GetTopRight(), m_mTree.at(-1).GetTopLeft()) ;
|
|
PtrOwner<ICurveLine> pCL1( CreateCurveLine()) ; pCL1->Set( m_mTree.at(-1).GetTopLeft(), m_mTree.at(-1).GetBottomLeft()) ;
|
|
PtrOwner<ICurveLine> pCL2( CreateCurveLine()) ; pCL2->Set( m_mTree.at(-1).GetBottomLeft(), m_mTree.at(-1).GetBottomRight()) ;
|
|
PtrOwner<ICurveLine> pCL3( CreateCurveLine()) ; pCL3->Set( m_mTree.at(-1).GetBottomRight(), m_mTree.at(-1).GetTopRight()) ;
|
|
IntersCurveCurve icc0( *pCL, *pCL0) ;
|
|
IntersCurveCurve icc1( *pCL, *pCL1) ;
|
|
IntersCurveCurve icc2( *pCL, *pCL2) ;
|
|
IntersCurveCurve icc3( *pCL, *pCL3) ;
|
|
IntCrvCrvInfo iccInfo ;
|
|
if (icc0.GetIntersCount() != 0)
|
|
icc0.GetIntCrvCrvInfo( 0, iccInfo) ;
|
|
else if (icc1.GetIntersCount() != 0)
|
|
icc1.GetIntCrvCrvInfo( 0, iccInfo) ;
|
|
else if (icc2.GetIntersCount() != 0)
|
|
icc2.GetIntCrvCrvInfo( 0, iccInfo) ;
|
|
else if (icc3.GetIntersCount() != 0)
|
|
icc3.GetIntCrvCrvInfo( 0, iccInfo) ;
|
|
pt = iccInfo.IciA[0].ptI ;
|
|
if ( ! OnWhichEdge(nRoot, pt, nEdgeIn))
|
|
return false ;
|
|
}
|
|
m_mTree.at( nRoot).m_vInters.back().nIn = nEdgeIn ;
|
|
PNTVECTOR vInters ;
|
|
vInters.emplace_back( pt) ;
|
|
while ( pl.GetNextPoint( pt))
|
|
vInters.emplace_back( pt) ;
|
|
pl.GetLastPoint( pt) ;
|
|
int nEdgeOut ;
|
|
// se non trovo l'ultimo punto esattamente su un lato, estendo l'ultimo tratto della polyline in avnti finchè trovo un'intersezione con un lato
|
|
// questa intersezione diventa il nuovo ultimo punto del vettore intersezione
|
|
if ( ! OnWhichEdge(nRoot, pt, nEdgeOut) ) {
|
|
Point3d ptSecondToLast ; pl.GetNextPoint( ptSecondToLast) ;
|
|
PtrOwner<ICurveLine> pCL( CreateCurveLine()) ; pCL->SetPVL( ptSecondToLast, pt - ptSecondToLast, 1e6) ;
|
|
PtrOwner<ICurveLine> pCL0( CreateCurveLine()) ; pCL0->Set( m_mTree.at(-1).GetTopRight(), m_mTree.at(-1).GetTopLeft()) ;
|
|
PtrOwner<ICurveLine> pCL1( CreateCurveLine()) ; pCL1->Set( m_mTree.at(-1).GetTopLeft(), m_mTree.at(-1).GetBottomLeft()) ;
|
|
PtrOwner<ICurveLine> pCL2( CreateCurveLine()) ; pCL2->Set( m_mTree.at(-1).GetBottomLeft(), m_mTree.at(-1).GetBottomRight()) ;
|
|
PtrOwner<ICurveLine> pCL3( CreateCurveLine()) ; pCL3->Set( m_mTree.at(-1).GetBottomRight(), m_mTree.at(-1).GetTopRight()) ;
|
|
IntersCurveCurve icc0( *pCL, *pCL0) ;
|
|
IntersCurveCurve icc1( *pCL, *pCL1) ;
|
|
IntersCurveCurve icc2( *pCL, *pCL2) ;
|
|
IntersCurveCurve icc3( *pCL, *pCL3) ;
|
|
IntCrvCrvInfo iccInfo ;
|
|
if (icc0.GetIntersCount() != 0)
|
|
icc0.GetIntCrvCrvInfo( 0, iccInfo) ;
|
|
else if (icc1.GetIntersCount() != 0)
|
|
icc1.GetIntCrvCrvInfo( 0, iccInfo) ;
|
|
else if (icc2.GetIntersCount() != 0)
|
|
icc2.GetIntCrvCrvInfo( 0, iccInfo) ;
|
|
else if (icc3.GetIntersCount() != 0)
|
|
icc3.GetIntCrvCrvInfo( 0, iccInfo) ;
|
|
pt = iccInfo.IciA[0].ptI ;
|
|
vInters.pop_back() ;
|
|
vInters.emplace_back( pt) ;
|
|
if ( ! OnWhichEdge(nRoot, pt, nEdgeOut))
|
|
return false ;
|
|
}
|
|
m_mTree.at( nRoot).m_vInters.back().vpt = vInters ;
|
|
m_mTree.at( nRoot).m_vInters.back().nOut = nEdgeOut ;
|
|
}
|
|
// chiamo una funzione per renderli coerenti
|
|
AdjustCuts() ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::AdjustCuts( void)
|
|
{
|
|
// correzione del verso dei tagli aperti
|
|
if ( int( m_mTree.at( -1).m_vInters.size()) == 1)
|
|
return true ;
|
|
// li riordino per ordine di quali taglio incontrerei percorrendo il bordo della cella a partire da ptTR
|
|
std::sort( m_mTree.at( -1).m_vInters.begin(), m_mTree.at( -1).m_vInters.end(), [](Inters& a, Inters& b){ return Inters::FirstEncounter(a,b) ;}) ;
|
|
// ora controllo che le intersezioni che trovo siano ingressi alternati ad uscite, sennò inverto l'intersezione
|
|
bool bPreviousWasStart = m_mTree.at( -1).m_vInters.at(0).bSortedbyStart ;
|
|
for ( int i = 0 ; i < int( m_mTree.at( -1).m_vInters.size()); ++i) {
|
|
if ( m_mTree.at( -1).m_vInters.at(i).bSortedbyStart == bPreviousWasStart) {
|
|
std::reverse( m_mTree.at( -1).m_vInters.at(i).vpt.begin(), m_mTree.at( -1).m_vInters.at(i).vpt.end()) ;
|
|
int nEdgeOutNew = m_mTree.at( -1).m_vInters.at(i).nIn ;
|
|
m_mTree.at( -1).m_vInters.at(i).nIn = m_mTree.at( -1).m_vInters.at(i).nOut ;
|
|
m_mTree.at( -1).m_vInters.at(i).nOut = nEdgeOutNew ;
|
|
bPreviousWasStart = m_mTree.at( -1).m_vInters.at(i).bSortedbyStart ;
|
|
}
|
|
else
|
|
bPreviousWasStart = ! m_mTree.at( -1).m_vInters.at(i).bSortedbyStart ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Tree::CreateCellContour( POLYLINEMATRIX& vPolygons)
|
|
{
|
|
// questa funzione è pensata per essere chiamata dopo la AddCutsToRoot, per creare il poligono di un'unica cella a cui sono stati aggiunti dei tagli
|
|
if ( m_mTree.size() > 1)
|
|
return false ;
|
|
int nRoot = -1 ;
|
|
// preparo tutto per poter chiamare la createCellPolygon
|
|
m_vnLeaves.push_back( nRoot) ;
|
|
INTVECTOR vToCheck( (int) m_mTree.at(nRoot).m_vInters.size()) ;
|
|
iota( vToCheck.begin(), vToCheck.end(), 0) ;
|
|
int nPoly = 0 ;
|
|
INTVECTOR vnParentChunk ;
|
|
PolyLine pl ;
|
|
pl.AddUPoint(0, m_mTree.at(nRoot).GetTopRight()) ;
|
|
pl.AddUPoint(1, m_mTree.at(nRoot).GetTopLeft()) ;
|
|
pl.AddUPoint(2, m_mTree.at(nRoot).GetBottomLeft()) ;
|
|
pl.AddUPoint(3, m_mTree.at(nRoot).GetBottomRight()) ;
|
|
pl.Close() ;
|
|
PolyLine pl3d ;
|
|
Cell& cRoot = m_mTree.at(nRoot) ;
|
|
Point3d pt3d11 ; GetPoint( cRoot.GetTopRight().x, cRoot.GetTopRight().y, pt3d11) ;
|
|
pl3d.AddUPoint(0, pt3d11) ;
|
|
Point3d pt3d01 ; GetPoint( cRoot.GetBottomLeft().x, cRoot.GetTopRight().y, pt3d01) ;
|
|
pl3d.AddUPoint(1, pt3d01) ;
|
|
Point3d pt3d00 ; GetPoint( cRoot.GetBottomLeft().x, cRoot.GetBottomLeft().y, pt3d00) ;
|
|
pl3d.AddUPoint(2, pt3d00) ;
|
|
Point3d pt3d10 ; GetPoint( cRoot.GetTopRight().x, cRoot.GetBottomLeft().y, pt3d10) ;
|
|
pl3d.AddUPoint(3, pt3d10) ;
|
|
pl3d.Close() ;
|
|
// ora posso creare il poligono della cella con i tagli
|
|
POLYLINEMATRIX vPolygons3d ;
|
|
while( ! vToCheck.empty()) {
|
|
int nPolyBefore = nPoly ;
|
|
CreateCellPolygons( 0, vPolygons, vPolygons3d, vToCheck, nPoly, vnParentChunk, pl, pl3d) ;
|
|
// l'aggiunta di vPolygons3d forse è un problema in questo punto.
|
|
// sarà da valutare quando si aggiungerà la funzione per aggiungere tagli aperti
|
|
// e se si vuole usare la funzione su uno spazio 2D ideale non collegato ad una sup di Bezier
|
|
if ( nPolyBefore == nPoly)
|
|
break ;
|
|
}
|
|
return true ;
|
|
} |