diff --git a/CurveComposite.cpp b/CurveComposite.cpp index 4537dfc..a5f0ae0 100644 --- a/CurveComposite.cpp +++ b/CurveComposite.cpp @@ -3773,3 +3773,28 @@ CurveComposite::ResetVoronoiObject() const delete m_pVoronoiObj ; m_pVoronoiObj = nullptr ; } + +//---------------------------------------------------------------------------- +bool +CurveComposite::FromPoint(Point3d& ptStart) +{ + // verifico lo stato + if ( m_nStatus != TO_VERIFY) + return false ; + // assegno il punto e setto lo stato + m_ptStart = ptStart ; + m_nStatus = IS_A_POINT ; + return true ; +} + +//---------------------------------------------------------------------------- +bool +CurveComposite::GetOnlyPoint(Point3d& ptStart) const +{ + // verifico lo stato + if ( m_nStatus != IS_A_POINT) + return false ; + // restituisco il punto + ptStart = m_ptStart ; + return true ; +} diff --git a/CurveComposite.h b/CurveComposite.h index 90098bb..1c9b2eb 100644 --- a/CurveComposite.h +++ b/CurveComposite.h @@ -150,7 +150,7 @@ class CurveComposite : public ICurveComposite, public IGeoObjRW bool IsParamAtJoint( double dU) const override ; ICurve* RemoveFirstOrLastCurve( bool bLast = true) override ; bool ChangeStartPoint( double dU) override ; - bool AddPoint( const Point3d& ptStart) override ; + bool AddPoint( const Point3d& ptStart) override ; // funzione per aggiungere il ptStart prima di usare la funzione AddLine bool AddLine( const Point3d& ptNew, bool bEndOrStart = true) override ; bool AddLineTg( double dLen, bool bEndOrStart = true) override ; bool AddArc2P( const Point3d& ptOther, const Point3d& ptNew, bool bEndOrStart = true) override ; @@ -177,6 +177,8 @@ class CurveComposite : public ICurveComposite, public IGeoObjRW bool GetCurveTempProp( int nCrv, int& nProp, int nPropInd = 0) const override ; bool SetCurveTempParam( int nCrv, double dParam, int nParamInd = 0) override ; bool GetCurveTempParam( int nCrv, double& dParam, int nParamInd = 0) const override ; + bool FromPoint( Point3d& ptStart) override ; // funzione per settare la curva ad un unico punto + bool GetOnlyPoint( Point3d& ptStart) const override ; // funzione per recuperare l'unico punto da cui è composta la curva ( degenere) public : // IGeoObjRW int GetNgeId( void) const override ; @@ -212,7 +214,7 @@ class CurveComposite : public ICurveComposite, public IGeoObjRW bool CalcVoronoiObject( void) const ; private : - enum Status { ERR = 0, OK = 1, TO_VERIFY = 2} ; + enum Status { ERR = 0, OK = 1, TO_VERIFY = 2, IS_A_POINT = 3} ; private : typedef std::deque PCRVSMPL_DEQUE ; diff --git a/EgtGeomKernel.vcxproj b/EgtGeomKernel.vcxproj index 6277db6..cb4717d 100644 --- a/EgtGeomKernel.vcxproj +++ b/EgtGeomKernel.vcxproj @@ -317,7 +317,9 @@ copy $(TargetPath) \EgtProg\Dll64 + + @@ -460,6 +462,7 @@ copy $(TargetPath) \EgtProg\Dll64 + diff --git a/EgtGeomKernel.vcxproj.filters b/EgtGeomKernel.vcxproj.filters index 75fe444..2144f62 100644 --- a/EgtGeomKernel.vcxproj.filters +++ b/EgtGeomKernel.vcxproj.filters @@ -276,6 +276,9 @@ File di origine\GeoInters + + File di origine\GeoInters + File di origine\GeoCreate @@ -522,6 +525,9 @@ File di origine\GeoInters + + File di origine\GeoCreate + diff --git a/IntersLineSurfBez.cpp b/IntersLineSurfBez.cpp new file mode 100644 index 0000000..3f792e7 --- /dev/null +++ b/IntersLineSurfBez.cpp @@ -0,0 +1,260 @@ +//---------------------------------------------------------------------------- +// EgalTech 2024 +//---------------------------------------------------------------------------- +// File : IntersLineSurfBez.cpp Data : 06.02.24 Versione : 2.6b1 +// Contenuto : Implementazione della intersezione linea/superficie bezier. +// +// +// +// Modifiche : 06.02.24 DB Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "/EgtDev/Include/EGkIntersLineTria.h" +#include "/EgtDev/Include/EGkIntersLineSurfTm.h" +#include "/EgtDev/Include/EGkIntersLineSurfBez.h" +#include "/EgtDev/Include/EGkSurfBezier.h" +#include "DistPointLine.h" +#include "CurveLine.h" + +using namespace std ; + +//---------------------- +bool +RefineIntersNewton( const Point3d& ptL, const Vector3d& vtL, double dLen, bool bFinite, + const ISurfBezier* pSurfBz, Point3d& ptSP, Point3d& ptIBz) { + // la funzione raffina la posisione del punto ptSP, minimizzando la distanza dalla retta e restituisce il punto di intersezione ptIBz + pSurfBz->GetPointD1D2( ptSP.x / SBZ_TREG_COEFF, ptSP.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptIBz) ; + // usando un algoritmo di newton cerco di avvicinarmi il più possibile alla retta + DistPointLine dpl( ptIBz, ptL, vtL, dLen, bFinite) ; + double dDistNew = 0, dDistPre = 0 ; + dpl.GetDist(dDistNew) ; + + int nCount = 0 ; + double dh = EPS_SMALL ; + pSurfBz->GetPointD1D2( ptSP.x, ptSP.y, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptIBz) ; + // metodo di newton in più dimensioni + // vario sia il parametro U che il parametro V e verifico se la distanza dalla retta diminuisce per scostamenti positivi o negativi. + while ( dDistNew > EPS_SMALL && nCount < 100) { + dDistPre = dDistNew ; + Point3d ptIBzNew1 ; + pSurfBz->GetPointD1D2( ( ptSP.x + dh) / SBZ_TREG_COEFF, ptSP.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptIBzNew1) ; + DistPointLine dplNewU( ptIBzNew1, ptL, vtL, dLen, bFinite) ; + dplNewU.GetDist( dDistNew) ; + double dfdU = ( dDistNew - dDistPre) / dh ; + Point3d ptIBzNew2 ; + pSurfBz->GetPointD1D2( ptSP.x / SBZ_TREG_COEFF, ( ptSP.y + dh) / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptIBzNew2) ; + DistPointLine dplNewV( ptIBzNew2, ptL, vtL, dLen, bFinite) ; + dplNewV.GetDist( dDistNew) ; + double dfdV = ( dDistNew - dDistPre) / dh ; + //// opzione 0 + ////scelgo h1 e h2 separatamente e in modo da annullare f(x) + //// opzione 1 + //// valore fisso + //double dr = EPS_SMALL ; + //if ( dDistPre > 1) + // dr = 1 ; + //else if ( dDistPre > 0.1) + // dr = 0.1 ; + //else if ( dDistPre > 0.01) + // dr = 0.01 ; + //// opzione 2 + //// valore direttamente vincolato + //double dr = dDistPre ; + //// opzione 3 + //// valuto la deformazione locale in base allo spostamento del punto sulla bezier // non serve + //double dh1 = Dist( ptIBz, ptIBzNew1) ; + //double dh2 = Dist( ptIBz, ptIBzNew2) ; + // potrei valutare il nuovo spostamento in base all'ultima variazione di dDist + // potrei anche vedere se sto uscendo dal triangolo ( definito nello spazio parametrico) + // mi avvicino cercando di annullare la distanza in un colpo solo + double dr = - dDistPre / ( dfdU + dfdV) ; + pSurfBz->GetPointD1D2(( ptSP.x + dr * dfdU) / SBZ_TREG_COEFF, ( ptSP.y + dr * dfdV) / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptIBz) ; + DistPointLine dplNew( ptIBz, ptL, vtL, dLen, bFinite) ; + dplNew.GetDist( dDistNew) ; + ++nCount ; + } + + return nCount != 99 ; +} + +////---------------------------------------------------------------------------- +//bool +//RefineIntersBisec( const Point3d& ptL, const Vector3d& vtL, double dLen, bool bFinite, +// const ISurfBezier* pSurfBz, Point3d& ptSP, Point3d& ptIBz) { +// +//} + +//---------------------------------------------------------------------------- +void +UpdateInfoIntersLineSurfBz( const Point3d& ptL, const Vector3d& vtDir, int nILT, int nT, const Point3d& ptSP, const Point3d& ptIBz, double dCos, + const Point3d& ptSP2, const Point3d& ptIBz2, double dCos2, ILSBIVECTOR& vInfo) +{ + if ( nILT == ILTT_IN || nILT == ILTT_EDGE || nILT == ILTT_VERT) { + double dU = ( ptIBz - ptL) * vtDir ; + vInfo.emplace_back( nILT, dU, nT, dCos, ptIBz, ptSP) ; + } + else if ( nILT == ILTT_SEGM || nILT == ILTT_SEGM_ON_EDGE) { + double dU = ( ptIBz - ptL) * vtDir ; + double dU2 = ( ptIBz2 - ptL) * vtDir ; + vInfo.emplace_back( nILT, dU, dU2, nT, dCos2, ptIBz, ptIBz2, ptSP, ptSP2) ; + } +} + +//---------------------------------------------------------------------------- +void +OrderInfoIntersLineSurfBz( ILSBIVECTOR& vInfo) +{ + // se non trovati, esco + if ( vInfo.size() == 0) + return ; + // ordino il vettore delle intersezioni secondo il senso crescente del parametro di linea + sort( vInfo.begin(), vInfo.end(), + []( const IntLinSbzInfo& a, const IntLinSbzInfo& b) + { double dUa = ( ( a.nILTT == ILTT_SEGM || a.nILTT == ILTT_SEGM_ON_EDGE) ? ( a.dU + a.dU2) / 2 : a.dU) ; + double dUb = ( ( b.nILTT == ILTT_SEGM || b.nILTT == ILTT_SEGM_ON_EDGE) ? ( b.dU + b.dU2) / 2 : b.dU) ; + return ( dUa < dUb) ; }) ; +} + +//---------------------------------------------------------------------------- +// Intersezione di una linea con una superficie TriMesh +//---------------------------------------------------------------------------- +bool +IntersLineSurfBz( const Point3d& ptL, const Vector3d& vtL, double dLen, const ISurfBezier* pSurfBz, + ILSBIVECTOR& vInfo, bool bFinite) +{ + PtrOwner pCL( CreateCurveLine()) ; + if ( bFinite) + pCL->SetPVL(ptL, vtL, dLen) ; + else + pCL->SetPVL(ptL, vtL, 1e6) ; + // verifico linea + Vector3d vtDir = vtL ; + if ( ! vtDir.Normalize( EPS_ZERO)) + return false ; + // verifico superficie + if ( pSurfBz == nullptr) + return false ; + // verifico parametro di ritorno + if ( &vInfo == nullptr) + return false ; + vInfo.clear() ; + + // trovo le intersezioni con la trimesh ausiliaria + const ISurfTriMesh* pSurfTm = pSurfBz->GetAuxSurf() ; + ILSIVECTOR vInfoTm ; + if ( ! IntersLineSurfTm( ptL, vtL, dLen, *pSurfTm, vInfoTm, bFinite)) + return false ; + // ricavo le intersezioni con la superficie di Bezier + for ( IntLinStmInfo InfoTm : vInfoTm ) { + // devo raffinare i parametri lungo la curva, l'angolo e i punti di intersezione + Point3d ptI, ptI2 ; + // devo trovare le intersezioni + Point3d ptSP, ptSP2 ; // coordinate parametriche delle soluzioni + pSurfBz->UnprojectPointFromStm( InfoTm.nT, InfoTm.ptI, ptSP, InfoTm.nILTT) ; + Point3d ptIBz, ptIBz2 ; + if ( ! RefineIntersNewton( ptL, vtL, dLen, bFinite, pSurfBz, ptSP, ptIBz)) { + /////// posso provare anche a rilanciare newton con un punto di partenza diverso oppure con una direzione di avvicinamento diversa/////////////////////////////////// + // per restare nel triangolo mi sposto verso un vertice + int nVert[3] ; + pSurfTm->GetTriangle( InfoTm.nT, nVert) ; + double dU0, dV0 ; + pSurfTm->GetVertexParam( nVert[0], dU0, dV0) ; + ptSP = ptSP + Point3d(dU0, dV0, 0) ; + if ( ! RefineIntersNewton( ptL,vtL, dLen, bFinite, pSurfBz, ptSP, ptIBz)) + return false ; + } + Vector3d vtN ; + pSurfBz->GetPointNrmD1D2(ptSP.x / SBZ_TREG_COEFF, ptSP.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptIBz, vtN) ; + double dCos = vtN * vtL ; + double dCos2 = 0 ; + // eventualmente ripeto tutto per ptI2 ( se ho un'intersezione con sovrapposizione) + if ( InfoTm.nILTT == ILTT_SEGM || InfoTm.nILTT == ILTT_SEGM_ON_EDGE ) { + pSurfBz->UnprojectPointFromStm( InfoTm.nT, InfoTm.ptI2, ptSP2, InfoTm.nILTT) ; + if ( ! RefineIntersNewton(ptL, vtL, dLen, bFinite, pSurfBz, ptSP2, ptIBz2) ) { + int nVert[3] ; + pSurfTm->GetTriangle( InfoTm.nT, nVert) ; + double dU0, dV0 ; + pSurfTm->GetVertexParam( nVert[0], dU0, dV0) ; + ptSP = ptSP + Point3d(dU0, dV0, 0) ; + if ( ! RefineIntersNewton( ptL,vtL, dLen, bFinite, pSurfBz, ptSP, ptIBz)) + return false ; + } + pSurfBz->GetPointNrmD1D2( ptSP2.x / SBZ_TREG_COEFF, ptSP2.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptIBz2, vtN) ; + dCos2 = vtN * vtL ; + } + UpdateInfoIntersLineSurfBz( ptL, vtL, InfoTm.nILTT, InfoTm.nT, ptSP, ptIBz, dCos, ptSP2, ptIBz2, dCos2, vInfo) ; + } + + OrderInfoIntersLineSurfBz( vInfo) ; + + return true ; +} + +//---------------------------------------------------------------------------- +bool +FilterLineSurfBzInters( const ILSBIVECTOR& vInfo, INTDBLVECTOR& vInters) +{ + // tengo per buone la classificazione delle intersezioni fatte sulla trimesh + // ciclo sulle intersezioni + for ( const auto& Info : vInfo) { + // se intersezione puntuale + if ( Info.nILTT == ILTT_VERT || Info.nILTT == ILTT_EDGE || Info.nILTT == ILTT_IN) { + int nFlag = LSBT_TOUCH ; + if ( Info.dCosDN > EPS_ZERO) + nFlag = LSBT_OUT ; + else if ( Info.dCosDN < -EPS_ZERO) + nFlag = LSBT_IN ; + vInters.emplace_back( nFlag, Info.dU) ; + } + // se altrimenti intersezione con coincidenza + else if ( Info.nILTT == ILTT_SEGM || Info.nILTT == ILTT_SEGM_ON_EDGE) { + vInters.emplace_back( LSBT_TG_INI, Info.dU) ; + vInters.emplace_back( LSBT_TG_FIN, Info.dU2) ; + } + } + // elimino intersezioni ripetute + for ( size_t j = 1 ; j < vInters.size() ; ) { + // intersezione precedente + size_t i = j - 1 ; + // se hanno lo stesso parametro + if ( abs( vInters[i].second - vInters[j].second) < EPS_SMALL) { + // se sono entrambe entranti o uscenti, elimino la seconda + if ( ( vInters[i].first == LSBT_IN && vInters[j].first == LSBT_IN) || + ( vInters[i].first == LSBT_OUT && vInters[j].first == LSBT_OUT)) { + vInters.erase( vInters.begin() + j) ; + continue ; + } + // se una entrante e l'altra uscente, cambio in touch ed elimino la seconda + else if ( ( vInters[i].first == LSBT_IN && vInters[j].first == LSBT_OUT) || + ( vInters[i].first == LSBT_OUT && vInters[j].first == LSBT_IN)) { + vInters[i].first = LSBT_TOUCH ; + vInters.erase( vInters.begin() + j) ; + continue ; + } + // se una puntuale e l'altra inizio di coincidenza, elimino la prima + else if ( ( vInters[i].first == LSBT_IN || vInters[i].first == LSBT_OUT || vInters[i].first == LSBT_TOUCH) && vInters[j].first == LSBT_TG_INI) { + vInters.erase( vInters.begin() + i) ; + continue ; + } + // se una fine di coincidenza e l'altra puntuale, elimino la seconda + else if ( vInters[i].first == LSBT_TG_FIN && ( vInters[j].first == LSBT_IN || vInters[j].first == LSBT_OUT || vInters[j].first == LSBT_TOUCH)) { + vInters.erase( vInters.begin() + j) ; + continue ; + } + // se una fine di coincidenza e l'altra inizio di coincidenza, elimino entrambe + else if ( i > 0 && vInters[i].first == LSBT_TG_FIN && vInters[j].first == LSBT_TG_INI) { + vInters.erase( vInters.begin() + j) ; + vInters.erase( vInters.begin() + i) ; + -- j ; + continue ; + } + } + // passo alla successiva + ++ j ; + } + return true ; +} \ No newline at end of file diff --git a/IntersLineSurfBez.h b/IntersLineSurfBez.h new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/IntersLineSurfBez.h @@ -0,0 +1 @@ +#pragma once diff --git a/SbzStandard.cpp b/SbzStandard.cpp new file mode 100644 index 0000000..5dd2cbe --- /dev/null +++ b/SbzStandard.cpp @@ -0,0 +1,80 @@ +//---------------------------------------------------------------------------- +// EgalTech 2024 +//---------------------------------------------------------------------------- +// File : SbzSphere.cpp Data : 14.02.2024 Versione : 2.6b2 +// Contenuto : Implementazione di funzioni per creazione di superfici Sbz +// standard : Box, Pyramid, Cylinder, Sphere, Cone. +// +// +// Modifiche : 14.02.2024 DB Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "CurveArc.h" +#include "SurfTriMesh.h" +#include "SurfBezier.h" +#include "/EgtDev/Include/EGkSbzStandard.h" + +using namespace std ; + +//------------------------------------------------------------------------------- +ISurfBezier* +CreateBezierSphere( const Point3d& ptCenter, double dR) +{ + // creo una superficie di Bezier di grado 2 con 45 punti di controllo + PtrOwner pSrfBez( CreateSurfBezier()) ; + int nDegU = 2 ; + int nDegV = 2 ; + int nSpanU = 4 ; // i poli della sfera sono a coordinate ( 0,0,-R) e ( 0,0,R) + int nSpanV = 2 ; + bool bRat = true ; + double dW = SQRT2 ; + pSrfBez->Init(nDegU, nDegV, nSpanU, nSpanV, bRat) ; + // polo inferiore // dW = 1, dW = SQRT2 / 2 + for ( int i = 0 ; i < 9; ++i) { + if ( i % 2 == 0) + dW = 1 ; + else + dW = SQRT2 / 2 ; + pSrfBez->SetControlPoint( i, 0, ptCenter + Point3d( 0, 0, -dR), dW) ; + } + // definisco la gabbia esterna // dW = SQRT2 / 2, dW = 1 / 2 + // parto dal punto ( 0,-dR, -dR) e completo riga per riga + double dH = -dR ; + for ( int j = 1 ; j < 4 ; ++j ) { + Vector3d vtDir ( 0, -dR, 0) ; + Point3d pt(-dR,-dR,dH) ; + for ( int i = 0; i < 9 ; ++i ) { + // ogni due punti ruoto di 90 gradi a sinistra + if ( i%2 == 0) { + vtDir.Rotate( Z_AX, 90) ; + if ( j%2 == 1) + dW = SQRT2 / 2 ; + else + dW = 1 ; + } + else { + if ( j%2 == 1) + dW = 1. / 2. ; + else + dW = SQRT2 / 2 ; + } + pt += vtDir ; + pSrfBez->SetControlPoint( i, j, ptCenter + pt, dW) ; + } + dH += dR ; + } + // polo superiore // dW = 1, dW = SQRT2 / 2 + for ( int i = 0 ; i < 9; ++i) { + if ( i % 2 == 0) + dW = 1 ; + else + dW = SQRT2 / 2 ; + pSrfBez->SetControlPoint( i, 4, ptCenter + Point3d( 0, 0, dR), dW) ; + } + + return Release( pSrfBez) ; +} diff --git a/SurfBezier.cpp b/SurfBezier.cpp index 265236c..e0a3465 100644 --- a/SurfBezier.cpp +++ b/SurfBezier.cpp @@ -29,6 +29,16 @@ #include "/EgtDev/Include/EGkUiUnits.h" #include "/EgtDev/Include/EgtNumUtils.h" #include "/EgtDev/Include/EgtPointerOwner.h" +#include "/EgtDev/Include/EGkIntersPlaneSurfTm.h" +#include "/EgtDev/Include/EGkChainCurves.h" +#include "/EgtDev/Include/EGkIntersLineSurfBez.h" +#include "/EgtDev/Include/EGkDistPointSurfTm.h" +#include "/EgtDev/Extern/Eigen/Dense" +#include "/EgtDev/Include/EGkCurveComposite.h" +#include +#include "/EgtDev/Include/EGkGeoObjSave.h" +#include "/EgtDev/Include/EGkGeoPoint3d.h" +#include "/EgtDev/Include/EGkIntervals.h" using namespace std ; @@ -38,7 +48,7 @@ GEOOBJ_REGISTER( SRF_BEZIER, NGE_S_BEZ, SurfBezier) ; //---------------------------------------------------------------------------- SurfBezier::SurfBezier( void) : m_pSTM( nullptr), m_nStatus( TO_VERIFY), m_nDegU(), m_nDegV(), m_nSpanU(), m_nSpanV(), m_bRat( false), - m_bTrimmed( false), m_pTrimReg( nullptr), m_nTempProp{0,0}, m_dTempParam{0.0,0.0} + m_bTrimmed( false), m_bClosedU( false), m_bClosedV( false), m_pTrimReg(nullptr), m_nTempProp{0,0}, m_dTempParam{0.0,0.0} { } @@ -129,19 +139,40 @@ SurfBezier::SetControlPoint( int nInd, const Point3d& ptCtrl, double dW) //---------------------------------------------------------------------------- bool -SurfBezier::SetTrimRegion( const ISurfFlatRegion& sfrTrimReg) +SurfBezier::SetTrimRegion( ISurfFlatRegion& sfrTrimReg, bool bIntersectOrSubtract) { + // controllo se aveo trim precedenti ed eventualmente faccio un'intersezione con lo spazio esistente + // verifico la regione passata if ( &sfrTrimReg == nullptr || ! sfrTrimReg.IsValid()) return false ; + // se la normale ha z negativa ribalto la superficie, sennò le operazioni di intersect e subtract non funzionano + if ( sfrTrimReg.GetNormVersor().z < 0) + sfrTrimReg.Invert() ; // limito la regione allo spazio parametrico della superficie PtrOwner< ISurfFlatRegion> pSfrTrim( GetSurfFlatRegionRectangle( SBZ_TREG_COEFF * m_nSpanU, SBZ_TREG_COEFF * m_nSpanV)) ; - if ( IsNull( pSfrTrim) || ! pSfrTrim->Intersect( sfrTrimReg) || ! pSfrTrim->IsValid()) - return false ; + // bIntersectOrSubtract == true per ottenere lo spazio parametrico trimmato devo fare l'INTERSEZIONE tra il rettangolo totale e l'area passata + if ( bIntersectOrSubtract) { + if ( IsNull( pSfrTrim) || ! pSfrTrim->Intersect( sfrTrimReg) || ! pSfrTrim->IsValid()) + return false ; + } + // bIntersectOrSubtract == false per ottenere lo spazio parametrico trimmato devo fare la SOTTRAZIONE tra il rettangolo totale e l'area passata + else { + if ( IsNull( pSfrTrim) || ! pSfrTrim->Subtract( sfrTrimReg) || ! pSfrTrim->IsValid()) + return false ; + } + ResetAuxSurf() ; // assegno la regione di trim + if ( m_pTrimReg != nullptr ) { + if ( ! m_pTrimReg->Intersect( *pSfrTrim)) + return false ; + } + else + m_pTrimReg = GetBasicSurfFlatRegion( Release( pSfrTrim)) ; + // imposto ricalcolo della grafica + m_OGrMgr.Reset() ; + // setto la superficie trimmata m_bTrimmed = true ; - delete m_pTrimReg ; - m_pTrimReg = GetBasicSurfFlatRegion( Release( pSfrTrim)) ; return true ; } @@ -215,7 +246,7 @@ SurfBezier::IsAPoint( void) const if ( ! AreSamePointApprox( m_vPtCtrl[0], m_vPtCtrl[i])) return false ; } - + return true ; } @@ -744,7 +775,6 @@ SurfBezier::Load( NgeReader& ngeIn) } // se trimmata, lettura della regione if ( bTrimmed) { - m_bTrimmed = true ; // creo l'oggetto ResetTrimRegion() ; m_pTrimReg = CreateBasicSurfFlatRegion() ; @@ -754,6 +784,7 @@ SurfBezier::Load( NgeReader& ngeIn) IGeoObjRW* pGObjRW = dynamic_cast( m_pTrimReg) ; if ( pGObjRW == nullptr || ! pGObjRW->Load( ngeIn)) return false ; + m_bTrimmed = true ; } // eseguo validazione @@ -1411,82 +1442,38 @@ SurfBezier::GetCurveOnVApproxLen( double dU) const return 0 ; return dLen ; } -// -////---------------------------------------------------------------------------- -//const SurfTriMesh* -//SurfBezier::GetAuxSurf( void) const -//{ -// // la superficie deve essere validata -// if ( m_nStatus != OK) { -// ResetAuxSurf() ; -// return nullptr ; -// } -// // se gi� calcolata, la restituisco -// if ( m_pSTM != nullptr) -// return m_pSTM ; -// // costruttore della superficie -// StmFromTriangleSoup stmSoup ; -// if ( ! stmSoup.Start()) -// return nullptr ; -// // definisco il numero degli step in U e in V -// double dMaxLenU = 0 ; -// for ( int j = 0 ; j <= m_nDegV * m_nSpanV ; ++ j) -// dMaxLenU = max( dMaxLenU, GetCurveOnUApproxLen( double( j) / m_nDegV)) ; -// int nStepU = GetSteps( m_nDegU, m_nSpanU, dMaxLenU, 2) ; -// double dMaxLenV = 0 ; -// for ( int i = 0 ; i <= m_nDegU * m_nSpanU ; ++ i) -// dMaxLenV = max( dMaxLenV, GetCurveOnVApproxLen( double( i) / m_nDegU)) ; -// int nStepV = GetSteps( m_nDegV, m_nSpanV, dMaxLenV, 2) ; -// // prima curva isoparametrica (potrebbe essere un solo punto) -// PolyLine PL1 ; -// GetCurveOnU( 0, nStepU, PL1) ; -// bool bSingle1 = ( PL1.GetPointNbr() == 1) ; -// // ciclo sulle isoparametriche -// for ( int i = 1 ; i <= nStepV ; ++ i) { -// // seconda curva isoparametrica (con tanti punti quanti la prima, oppure uno solo) -// double dV = double( i) * m_nSpanV / nStepV ; -// PolyLine PL2 ; -// GetCurveOnU( dV, nStepU, PL2) ; -// bool bSingle2 = ( PL2.GetPointNbr() == 1) ; -// // inserisco i triangoli della striscia nel costruttore della TriMesh -// Point3d ptP1c, ptP2c ; -// Point3d ptP1n, ptP2n ; -// bool bNext = PL1.GetFirstPoint( ptP1c) && PL2.GetFirstPoint( ptP2c) ; -// if ( bNext) { -// if ( bSingle1 && bSingle2) -// bNext = false ; -// if ( bSingle1) -// ptP1n = ptP1c ; -// else -// bNext = bNext && PL1.GetNextPoint( ptP1n) ; -// if ( bSingle2) -// ptP2n = ptP2c ; -// else -// bNext = bNext && PL2.GetNextPoint( ptP2n) ; -// } -// while ( bNext) { -// // eventuale primo triangolo (con base sui correnti e vertice su P2 successivo) -// if ( ! AreSamePointApprox( ptP1c, ptP2c)) -// stmSoup.AddTriangle( ptP2c, ptP1c, ptP2n) ; -// // eventuale secondo triangolo (con vertice su P1 corrente e base sui successivi) -// if ( ! AreSamePointApprox( ptP1n, ptP2n)) -// stmSoup.AddTriangle( ptP1c, ptP1n, ptP2n) ; -// // passo alla successiva coppia -// ptP1c = ptP1n ; -// ptP2c = ptP2n ; -// bNext = ( bSingle1 || PL1.GetNextPoint( ptP1n)) && ( bSingle2 || PL2.GetNextPoint( ptP2n)) ; -// } -// // salvo isoparametrica PL2 in PL1 -// PL1.GetUPointList().swap( PL2.GetUPointList()) ; -// bSingle1 = bSingle2 ; -// } -// // la completo -// if ( ! stmSoup.End()) -// return nullptr ; -// // la salvo -// m_pSTM = GetBasicSurfTriMesh( stmSoup.GetSurf()) ; -// return m_pSTM ; -//} + +//---------------------------------------------------------------------------- +bool +SurfBezier::UpdateEdgesFromTree( Tree& tr) const +{ + POLYLINEMATRIX mPlEdges ; + tr.GetEdges3D( mPlEdges) ; + for ( int i= 0 ; i < int( mPlEdges.size()); ++i) { + for ( int j = 0 ; j < int ( mPlEdges[i].size()) ; ++j) { + m_mCCEdge[i].emplace_back(CreateBasicCurveComposite()) ; + if ( ! m_mCCEdge[i].back()->FromPolyLine(mPlEdges[i][j]) ) { + Point3d ptStart ; + if ( ! mPlEdges[i][j].GetFirstPoint( ptStart)) + continue ; + m_mCCEdge[i].back()->FromPoint( ptStart) ; + } + } + } + + if ( m_bTrimmed) { + POLYLINEVECTOR vPl ; + tr.GetSplitLoops( vPl) ; + + // recupero i loop nel parametrico + for( int i = 0 ; i < int( vPl.size()); ++i) { + m_vCCLoop.emplace_back(CreateBasicCurveComposite()) ; + m_vCCLoop.back()->FromPolyLine(vPl[i]) ; + } + } + + return true ; +} //---------------------------------------------------------------------------- const SurfTriMesh* @@ -1497,7 +1484,7 @@ SurfBezier::GetAuxSurf( void) const ResetAuxSurf() ; return nullptr ; } - // se gi� calcolata, la restituisco + // se già calcolata, la restituisco if ( m_pSTM != nullptr) return m_pSTM ; @@ -1508,6 +1495,10 @@ SurfBezier::GetAuxSurf( void) const BIPNTVECTOR vTrees ; Tree.GetIndependentTrees( vTrees) ; bool bTest = false ; // per debug + // resetto il vettore degli edge + m_mCCEdge.clear() ; + m_mCCEdge = vector(4) ; + m_vCCLoop.clear() ; for ( int i = 0 ; i < (int) vTrees.size() ; ++ i) { Point3d ptMin = std::get<0>( vTrees[i]) ; Point3d ptMax = std::get<1>( vTrees[i]) ; @@ -1521,8 +1512,13 @@ SurfBezier::GetAuxSurf( void) const Tree.BuildTree( 5 * LIN_TOL_FINE, 0.1) ; } Tree.GetPolygons( vvPL) ; - //Tree.GetPolygonsBasic( vPL) ; // per usare i polygon basic + + // aggiorno la chiusura della superficie + m_bClosedU = m_bClosedU || Tree.IsClosedU() ; + m_bClosedV = m_bClosedV || Tree.IsClosedV() ; + // salvo i bordi in 3d, che servono in caso si voglia trimmare la superficie DOPO aver costruito la trimesh ausiliaria + UpdateEdgesFromTree( Tree) ; } //// per usare i polygon basic////////////////////// //for (int k = 0 ; k < (int)vPL.size(); ++k) { @@ -1531,7 +1527,7 @@ SurfBezier::GetAuxSurf( void) const //} //// per usare i polygon basic/////////////////// - // qui non sarebbe male stampare un messaggio di errore nel log se avevo un'area da disegnare ma non sono usciti dei poligoni + if ( int(vvPL.size()) == 0) LOG_DBG_ERR( GetEGkLogger(), "ERROR : Bezier Surface couldn't be triangulated, hence wasn't drawn") ; @@ -1601,6 +1597,24 @@ SurfBezier::GetLeaves( vector>& vLeaves) const return true ; } +//---------------------------------------------------------------------------- +bool +SurfBezier::GetTriangles2D( vector>& vTria2D) const +{ + const ISurfTriMesh* pSTM = GetAuxSurf() ; + for ( int t = 0 ; t < int(pSTM->GetTriangleCount()) ; ++t ) { + double dU0, dU1, dU2, dV0, dV1, dV2 ; + int nVert[3] ; + pSTM->GetTriangle( t, nVert); + pSTM->GetVertexParam( nVert[0], dU0, dV0) ; + pSTM->GetVertexParam( nVert[1], dU1, dV1) ; + pSTM->GetVertexParam( nVert[2], dU2, dV2) ; + Point3d pt0(dU0,dV0), pt1(dU1,dV1), pt2(dU2,dV2) ; + vTria2D.emplace_back( tuple(t, pt0, pt1, pt2)) ; + } + return true ; +} + //---------------------------------------------------------------------------- void SurfBezier::ResetAuxSurf( void) const @@ -1617,4 +1631,1542 @@ SurfBezier::ResetTrimRegion( void) if ( m_pTrimReg != nullptr) delete( m_pTrimReg) ; m_pTrimReg = nullptr ; + ResetAuxSurf() ; + m_bTrimmed = false ; + // imposto ricalcolo della grafica + m_OGrMgr.Reset() ; } + +//---------------------------------------------------------------------------- +bool +SurfBezier::IncreaseUV( Point3d& ptUV, Vector3d vtH , Point3d* ptUVCopy, bool bModifyOrig) const +{ + if ( ptUVCopy != nullptr) { + IncreaseUV( ptUV.x, vtH.x, true, &(*ptUVCopy).x, bModifyOrig) ; + IncreaseUV( ptUV.y, vtH.y, true, &(*ptUVCopy).y, bModifyOrig) ; + } + else { + IncreaseUV( ptUV.x, vtH.x, true, nullptr, bModifyOrig) ; + IncreaseUV( ptUV.y, vtH.y, true, nullptr, bModifyOrig) ; + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfBezier::IncreaseUV( double& dUV, double dxy, bool bUOrV, double* dUVCopy, bool bModifyOrig) const +{ + double dUVTest ; + if ( dUVCopy != nullptr) { + *dUVCopy = dUV + dxy ; + dUVTest = *dUVCopy ; + } + if ( bModifyOrig) { + dUV += dxy ; + dUVTest = dUV ; + } + + if ( bUOrV) { + if ( dUVTest < 0) + dUVTest = 0 ; + else if ( dUVTest > m_nSpanU * SBZ_TREG_COEFF ) + dUVTest = m_nSpanU * SBZ_TREG_COEFF ; + } + else { + if ( dUVTest < 0) + dUVTest = 0 ; + else if ( dUVTest > m_nSpanV * SBZ_TREG_COEFF ) + dUVTest = m_nSpanV * SBZ_TREG_COEFF ; + } + if ( bModifyOrig) + dUV = dUVTest ; + if ( dUVCopy != nullptr) + *dUVCopy = dUVTest ; + + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfBezier::UnprojectCurveFromStm( const ICurveComposite* pCC, ICRVCOMPOPVECTOR& vpCC, const Plane3d* pPlCut) const +{ + // do per scontato che la compo sia una spezzata, visto che arriva dall'intersezione tra un piano e una trimesh + // creo la chain dei punti che sto riportando nel parametrico + ChainCurves chainC ; + double dToler = EPS_SMALL ; + chainC.Init( false, dToler, 2) ; + + const ICurve* pCrv0 = pCC->GetCurve( 0) ; + PolyLine pl ; + Point3d pt3D, pt2D ; pCrv0->GetStartPoint( pt3D) ; + Point3d pt3DEnd ; pCrv0->GetEndPoint( pt3DEnd) ; + bool bThroughEdge = false ; + BOOLVECTOR vbThroughEdge ; + if ( ! UnprojectPoint(pt3D, pt2D, pt3DEnd, &bThroughEdge, pPlCut)) + return false ; + vbThroughEdge.push_back( bThroughEdge) ; + + // aggiungo tutti i successivi + BIPNTVECTOR vBPnt ; + + bThroughEdge = false ; + int nRejected = 0 ; + for ( int i = 0 ; i < int( pCC->GetCurveCount()) ; ++i) { + const ICurve* pCrv = pCC->GetCurve( i) ; + Point3d pt3DPrev = pt3D ; + Point3d pt2DPrev = pt2D ; + bool bPrevIsPole = false ; + if ( bThroughEdge) { + // devo cambiare le coordinate di pt2DPrev per periodicità + // capisco su quale lato è e lo porto sul lato opposto + Point3d pt = pt2DPrev ; + if ( m_bClosedU) { + if ( pt2DPrev.x < 1) + pt2DPrev.x = m_nSpanU * SBZ_TREG_COEFF ; + else if ( (m_nSpanU * SBZ_TREG_COEFF - pt2DPrev.x) < 1) + pt2DPrev.x = 0 ; + } + if ( m_bClosedV) { + if ( pt2DPrev.y < 1) + pt2DPrev.y = m_nSpanV * SBZ_TREG_COEFF ; + else if ( (m_nSpanV * SBZ_TREG_COEFF - pt2DPrev.y) < 1) + pt2DPrev.y = 0 ; + } + bPrevIsPole = AreSamePointApprox( pt, pt2DPrev) ; + } + pCrv->GetEndPoint( pt3D) ; + if ( ! UnprojectPoint( pt3D, pt2D, pt3DPrev, &bThroughEdge, pPlCut)) + return false ; + if ( bPrevIsPole) { + // se il punto precedente era di polo allora devo correggere le sue coordinate 2D + if ( ( m_vbPole[0] || m_vbPole[2] )) { + if ( pt2DPrev.y < 1 || m_nSpanV * SBZ_TREG_COEFF - pt2DPrev.y < 1) + pt2DPrev.x = pt2D.x ; + } + if ( ( m_vbPole[1] || m_vbPole[3] )) { + if ( pt2DPrev.x < 1 || m_nSpanU * SBZ_TREG_COEFF - pt2DPrev.x < 1) + pt2DPrev.y = pt2D.y ; + } + } + Vector3d vtDir = pt2D - pt2DPrev ; + vtDir.Normalize() ; + // se mi accorgo che sto per tracciare un taglio lungo un bordo posso semplicmente evitarlo + if ( (1 - abs(vtDir.x) < EPS_SMALL && (pt2D.y < EPS_SMALL || m_nSpanV * SBZ_TREG_COEFF - pt2D.y < 1)) || // parallelo agli edge 0 e 2 e su uno di questi + (1 - abs(vtDir.y) < EPS_SMALL && (pt2D.x < EPS_SMALL || m_nSpanU * SBZ_TREG_COEFF - pt2D.x < 1))) { // parallello agli edge 1 e 3 e su uno di questi + ++ nRejected ; + continue ; + } + if ( bThroughEdge && vbThroughEdge.back()) { + double dParamH, dParamL ; + dParamH = m_nSpanV * SBZ_TREG_COEFF ; + dParamL = m_nSpanU * SBZ_TREG_COEFF ; + // sia questo punto che il precedente sono su un edge, ma il segmento che li unisce non è parallelo ad un edge + // potrei star tracciando un taglio sul bordo di chiusura + // controllo se sto tracciando una linea che unisce due lati di chiusura, allora in realtà sdtarei tracciando un taglio sull'edge e quindi posso non tracciarlo + if ( (abs( vtDir.x) > abs( vtDir.y) && Dist( pt2D, pt2DPrev) > dParamL * 0.5) || + (abs( vtDir.y) > abs( vtDir.x) && Dist( pt2D, pt2DPrev) > dParamH * 0.5)) { + ++ nRejected ; + continue ; + } + } + vbThroughEdge.push_back( bThroughEdge) ; + vBPnt.emplace_back( BIPOINT( pt2DPrev, pt2D)) ; + if ( ! chainC.AddCurve( i + 1 - nRejected, pt2DPrev, vtDir, pt2D, vtDir)) + return false ; + } + // ricostruisco le catene in 2D + Point3d ptNear ; pCrv0->GetStartPoint( ptNear) ; + INTVECTOR vId ; + bool bAdded = true ; + while( chainC.GetChainFromNear( pt3D, false, vId)) { + PtrOwner pCC2D ( CreateCurveComposite()) ; + for ( int i = 0 ; i < int( vId.size()) ; ++ i) { + // creo un segmento di retta + ICurveLine* pLine( CreateCurveLine()) ; + if ( pLine == nullptr) + return false ; + // recupero gli estremi (non vanno mai invertiti per opzione di concatenamento) + int nInd = abs( vId[i]) - 1 ; + Point3d ptStart = ( bAdded ? vBPnt[nInd].first : ptNear) ; + Point3d ptEnd = vBPnt[nInd].second ; + // provo ad accodarlo alla composita + bAdded = ( Dist( ptStart, ptEnd) > dToler / 2 && + pLine->Set( ptStart, ptEnd)) ; + bAdded = bAdded && pCC2D->AddCurve( pLine, true, dToler) ; + ptNear = ( bAdded ? ptEnd : ptStart) ; + } + if ( pCC2D->IsValid()) + vpCC.emplace_back( Release(pCC2D)) ; + } + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfBezier::AddCurveCompoToCuts( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vpCCOpen, ICRVCOMPOPOVECTOR& vpCCClosed, double dToler, const Plane3d* pPlCut) const +{ + // se lunghezza curva inferiore a 5 volte la tolleranza, la ignoro e sposto il punto finale nel punto di fine della curva che sto ignorando + double dCrvLen ; + if ( ! pCrvCompo->GetLength( dCrvLen) || dCrvLen < 5. * dToler) + return true ; + // se curva chiusa entro 5 volte la tolleranza ma considerata aperta, la chiudo bene + Point3d ptStart, ptEnd ; + if ( pCrvCompo->GetStartPoint( ptStart) && + pCrvCompo->GetEndPoint( ptEnd) && + AreSamePointEpsilon( ptStart, ptEnd, 5. * dToler) && + ! AreSamePointApprox( ptStart, ptEnd)) { + // porto il punto finale a coincidere esattamente con l'inizio + pCrvCompo->ModifyEnd( ptStart) ; + } + // unisco segmenti allineati + pCrvCompo->MergeCurves( 0.5 * dToler, ANG_TOL_STD_DEG) ; + + // porto la curva nello spazio parametrico + ICRVCOMPOPVECTOR vCC ; + if ( ! UnprojectCurveFromStm( pCrvCompo, vCC, pPlCut)) + return false ; + for ( int i = 0 ; i < int( vCC.size()); ++i) { + // le curve aperte le tengo da parte per giuntarle alla fine col bordo + if ( ! vCC[i]->IsClosed() ) + vpCCOpen.emplace_back( vCC[i]) ; + // le curve chiuse le metto tutte insieme subito + else + vpCCClosed.emplace_back( vCC[i]) ; + } + return true ; +} + +typedef tuple TRINT ; + +template<> +struct hash { + std::size_t operator()(const TRINT& t) const + { + // Compute individual hash values for first, second and third and combine them using XOR and bit shifting: + return ((hash()(get<0>(t))) ^ (hash()(get<1>(t)) << 1) >> 1) ^ (hash()(get<2>(t)) << 1) ; + } +}; + +//---------------------------------------------------------------------------- +bool +SurfBezier::Cut( const Plane3d& plPlane, bool bSaveOnEq) +{ + // faccio l'intersezione della trimesh ausiliaria con il piano posso ottenere: punti, curve 3d e triangoli( coplanari al piano di taglio) + // i punti li escludo + // le curve 3d le trasformo in curve 2d e le aggiungo alle curve di trim + // accorpo eventuali triangoli adiacenti ed estraggo i loop delle regioni ottenute; questi vengono poi portati in 2d e aggiunti alle curve di trim + + PNTVECTOR vPnt ; + BIPNTVECTOR vBPnt ; + TRIA3DVECTOR vTria ; + IntersPlaneSurfTm( plPlane, *GetAuxSurf(), vPnt, vBPnt, vTria) ; + + // concateno le curve 3d + ChainCurves chainC ; + double dToler = EPS_SMALL ; + chainC.Init( false, dToler, int( vBPnt.size())) ; + for ( int i = 0 ; i < int( vBPnt.size()) ; ++ i) { + Vector3d vtDir = vBPnt[i].second - vBPnt[i].first ; + vtDir.Normalize() ; + if ( ! chainC.AddCurve( i + 1, vBPnt[i].first, vtDir, vBPnt[i].second, vtDir)) + return false ; + } + // GESTIONE DELLE CURVE OTTENUTE DALL'INTERSEZIONE + + // recupero i percorsi concatenati + Point3d ptNear = ( vBPnt.empty() ? ORIG : vBPnt[0].first) ; + INTVECTOR vId ; + + // separo tra loop chiusi, interni allo spazio parametrico e loop passanti che tagliano lo spazio intersecando i bordi + ICRVCOMPOPOVECTOR vpCCOpen ; + ICRVCOMPOPOVECTOR vpCCClosed ; + + while ( chainC.GetChainFromNear( ptNear, false, vId)) { + // creo una curva composita + PtrOwner pCrvCompo( CreateCurveComposite()) ; + if ( IsNull( pCrvCompo)) + return false ; + // recupero gli estremi dei segmenti, creo le linee e le inserisco nella composita + bool bAdded = true ; + for ( int i = 0 ; i < int( vId.size()) ; ++ i) { + // creo un segmento di retta + ICurveLine* pLine( CreateCurveLine()) ; + if ( pLine == nullptr) + return false ; + // recupero gli estremi (non vanno mai invertiti per opzione di concatenamento) + int nInd = abs( vId[i]) - 1 ; + Point3d ptStart = ( bAdded ? vBPnt[nInd].first : ptNear) ; + Point3d ptEnd = vBPnt[nInd].second ; + // provo ad accodarlo alla composita + bAdded = ( Dist( ptStart, ptEnd) > dToler / 2 && + pLine->Set( ptStart, ptEnd)) ; + bAdded = bAdded && pCrvCompo->AddCurve( pLine, true, dToler) ; + ptNear = ( bAdded ? ptEnd : ptStart) ; + } + if ( ! AddCurveCompoToCuts( pCrvCompo, vpCCOpen, vpCCClosed, EPS_SMALL, &plPlane)) + return false ; + } + + //comincio a creare la superficie aggiungendo i tagli aperti ai bordi attualmente esistenti + SurfFlatRegionByContours sfrContour ; + if ( int(vpCCOpen.size()) != 0 ) { + // qui devo aggiungere tutto del codice nuovo per ricostruire in altro modo il nuovo bordo della superficie + // recupero la regione attuale + + + PtrOwner pNewTrim( CreateBasicSurfFlatRegion()) ; + if ( m_bTrimmed) + pNewTrim.Set( GetTrimRegion()->Clone()) ; + else + pNewTrim.Set( GetSurfFlatRegionRectangle( SBZ_TREG_COEFF * m_nSpanU, SBZ_TREG_COEFF * m_nSpanV)) ; + + // costruisco la mappa delle intersezioni, trovando tutte le intersezioni tra i trim e i loop dei vari chunk della falr region + unordered_map mInters ; + int nInters = 0 ; + bool bStartFound = false ; + bool bEndFound = false ; + // trim + for ( int t = 0 ; t < int( vpCCOpen.size()); ++t) { + nInters = 0 ; + bStartFound = false ; + bEndFound = false ; + //chunk + for ( int c = 0 ; c < pNewTrim->GetChunkCount() ; ++c) { + // loop + for ( int l = 0 ; l < pNewTrim->GetLoopCount( c) ; ++l) { + PtrOwner pLoop( pNewTrim->GetLoop( c, l)) ; + // prima curva è il loop, seconda curva è il trim + IntersCurveCurve icc( *pLoop, *vpCCOpen[t]) ; + if ( icc.GetIntersCount() != 0) { + ICCIVECTOR vICC ; + for ( int i = 0 ; i < int( icc.GetIntersCount()); ++i) { + IntCrvCrvInfo iccInfo ; + icc.GetIntCrvCrvInfo( i, iccInfo) ; + vICC.emplace_back( iccInfo) ; + } + mInters.insert( pair( TRINT(c,l,t), vICC)) ; + if ( int( vICC.size() == 2)) { + bStartFound = true ; + bEndFound = true ; + } + else if ( int(vICC.size() == 1) ) { + if ( vICC[0].IciB->dU < EPS_SMALL) + bStartFound = true ; + else + bEndFound = true ; + } + } + nInters += int( icc.GetIntersCount()) ; + } + } + if ( nInters != 2) { + // se un trim non fa 2 intersezioni allora devo estendere la curva allo start e/o all'end per creare le intersezioni + Point3d ptStart ; vpCCOpen[t]->GetStartPoint( ptStart) ; + Point3d ptEnd ; vpCCOpen[t]->GetEndPoint( ptEnd) ; + PtrOwner pCrv( vpCCOpen[t]->Clone()) ; + double dExtension = m_nSpanU > m_nSpanV ? m_nSpanU : m_nSpanV ; + dExtension *= SBZ_TREG_COEFF ; + if ( ! bStartFound) + pCrv->ExtendStartByLen( dExtension) ; + if ( ! bEndFound) + pCrv->ExtendEndByLen( dExtension) ; + double dDistStart = 1e6, dDistEnd = 1e6 ; + // vettore per l'intersezione di start + ICCIVECTOR vICCStart ; + vICCStart.emplace_back() ; + // vettore per l'intersezione di end + ICCIVECTOR vICCEnd ; + vICCEnd.emplace_back() ; + TRINT tStart, tEnd ; + //chunk + for ( int c = 0 ; c < pNewTrim->GetChunkCount() ; ++c) { + // loop + for ( int l = 0 ; l < pNewTrim->GetLoopCount( c) ; ++l) { + PtrOwner pLoop( pNewTrim->GetLoop( c, l)) ; + // prima curva è il loop, seconda curva è il trim + IntersCurveCurve icc( *pLoop, *pCrv) ; + if ( icc.GetIntersCount() != 0) { + for ( int i = 0 ; i < int( icc.GetIntersCount()); ++i) { + IntCrvCrvInfo iccInfo ; icc.GetIntCrvCrvInfo( i, iccInfo) ; + if ( ! bStartFound && Dist( iccInfo.IciA->ptI, ptStart) < dDistStart) { + dDistStart = Dist( iccInfo.IciA->ptI, ptStart) ; + vICCStart[0] = iccInfo ; + tStart = TRINT( c, l, t) ; + } + if ( ! bEndFound && Dist( iccInfo.IciA->ptI, ptEnd) < dDistEnd) { + dDistEnd = Dist( iccInfo.IciA->ptI, ptEnd) ; + vICCEnd[0] = iccInfo ; + tEnd = TRINT( c, l, t) ; + } + } + } + } + } + + // ricostruisco gli elementi per la mappa mInters + if ( ! bStartFound && ! bEndFound) { + // ridefinisco il taglio aperto con la sua versione estesa che arriva a toccare i loop dello spazio parametrico + vpCCOpen[t].Set( GetCurveComposite(pCrv->CopyParamRange( vICCStart[0].IciB->dU, vICCEnd[0].IciB->dU))) ; + // correggo il parametro dell'intersezione allo start + vICCStart[0].IciB->dU = 0 ; + if ( tStart == tEnd) { + // se ho intersezione con un loop solo allora accorpo i due vettori delle intersezioni + vICCStart.emplace_back( vICCEnd[0]) ; + mInters.insert( pair(tStart, vICCStart)) ; + } + else{ + // se ho intersezione con due loop diverse due entry diverse le inserisco + mInters.insert( pair(tStart, vICCStart)) ; + mInters.insert( pair(tEnd, vICCEnd)) ; + } + } + else { + // devo verificare se avevo già trovato una delle due intersezioni e se era sullo stesso loop o no + if ( ! bStartFound) { + pCrv->TrimStartAtParam( vICCStart[0].IciB->dU) ; + vpCCOpen[t].Set( GetCurveComposite( Release( pCrv))) ; + // correggo il parametro dell'intersezione allo start + vICCStart[0].IciB->dU = 0 ; + if ( mInters.count( tStart) == 1) + mInters[tStart].emplace_back( vICCStart[0]) ; + else + mInters.insert( pair( tStart, vICCStart)) ; + } + if ( ! bEndFound) { + if ( mInters.count( tEnd) == 1) + mInters[tEnd].emplace_back( vICCEnd[0]) ; + else + mInters.insert( pair( tEnd, vICCEnd)) ; + pCrv->TrimEndAtParam( vICCEnd[0].IciB->dU) ; + vpCCOpen[t].Set( GetCurveComposite( Release( pCrv))) ; + } + } + } + } + + // vettore di flag che mi indica quali tagli aperti sono stati aggiunti al nuovo bordo + BOOLVECTOR vbAdded( vpCCOpen.size()) ; + std::fill( vbAdded.begin(), vbAdded.end(), false) ; + PtrOwner pCCNewEdge( CreateCurveComposite()) ; + PtrOwner pCL( CreateCurveLine()) ; + TRINT tiFirstInters ; + // parto aggiungendo il primo taglio + int nNewToAdd = 0 ; + bool bFirstCurveOfEdge = true ; + while ( nNewToAdd != -1) { + // aggiungo il taglio + pCCNewEdge->AddCurve( Release( vpCCOpen[nNewToAdd])) ; + // aggiorno la lista degli aggiunti + vbAdded[nNewToAdd] = true ; + // di questo taglio mi salvo il chunk e loop di start e end + TRINT tiStart, tiEnd ; + for (const auto& pair : mInters) { + if ( get<2>(pair.first) == nNewToAdd ) { + for (int p = 0 ; p < int(pair.second.size()) ; ++p) { + if ( pair.second[p].IciB->dU < EPS_SMALL) { + tiStart = pair.first ; + if ( bFirstCurveOfEdge){ + // salvo l'inizio del taglio che è la prima curva di questa curva compo + tiFirstInters = pair.first ; + bFirstCurveOfEdge = false ; + } + } + else + tiEnd = pair.first ; + } + } + } + + // devo trovare fino a che punto seguire il loop che ho trovato come prosecuzione del taglio corrente + // devo quindi trovare la prossima intersezione con un taglio + int nInters = -1 ; + double dNextCut = numeric_limits::infinity() ; + double dEndCurrentCut ; + for ( int i = 0 ; i < int( mInters[tiEnd].size()); ++i) { + // se ho trovato l'intersezione con la fine del taglio corrente, salvo il parametro sul loop + if ( mInters[tiEnd][i].IciB->dU > EPS_SMALL) + dEndCurrentCut = mInters[tiEnd][i].IciA->dU ; + } + // se non trovo nessuna altra intersezione prima della fine del loop allora devo ripetere tutto cercando a partire dall'inizio del loop + for ( const auto& pair : mInters) { + if ( get<0>(pair.first) == get<0>(tiEnd) && get<1>(pair.first) == get<1>(tiEnd)) { + for ( int i = 0 ; i < int(pair.second.size()); ++i ) { + // se trovo una nuova intersezione che incontro prima di quella che mi ero salvato precedentemente allora + // mi salvo questa nuova che ho trovato + if ( pair.second[i].IciA->dU < dNextCut && pair.second[i].IciA->dU > dEndCurrentCut) { + dNextCut = pair.second[i].IciA->dU ; + nInters = get<2>(pair.first) ; + } + } + } + } + if ( nInters == -1) { + dNextCut = numeric_limits::infinity() ; + for ( const auto& pair : mInters) { + if ( get<0>(pair.first) == get<0>(tiEnd) && get<1>(pair.first) == get<1>(tiEnd)) { + for ( int i = 0 ; i < int(pair.second.size()); ++i ) { + // se trovo una nuova intersezione che incontro prima di quella che mi ero salvato precedentemente allora + // mi salvo questa nuova che ho trovato + if ( pair.second[i].IciA->dU < dNextCut) { + dNextCut = pair.second[i].IciA->dU ; + nInters = get<2>(pair.first) ; + } + } + } + } + } + + // se tutto va bene queste due righe sostituiscono tutto il casino qua sotto + PtrOwner pLoopTrimmed( pNewTrim->GetLoop( get<0>(tiEnd), get<1>(tiEnd))) ; + pCCNewEdge->AddCurve(pLoopTrimmed->CopyParamRange( dEndCurrentCut, dNextCut)) ; + + // se il prossimo taglio identificato è quello da cui sono partito allora aggiungo il bordo ricostruito fino a questo momento + // alla flat region e comincio a costruire un altro bordo + // altrimenti continuo ad aggiungere curve al bordo corrente + if ( nInters == get<2>(tiFirstInters) ) { + pCCNewEdge->Close() ; + sfrContour.AddCurve( Release( pCCNewEdge)) ; + pCCNewEdge.Set( CreateBasicCurveComposite()) ; + bFirstCurveOfEdge = true ; + // trovo il prossimo taglio ancora da aggiungere + nNewToAdd = -1 ; + for ( int b = 0 ; b < int(vbAdded.size()) ; ++b ) { + if ( ! vbAdded[b]) { + nNewToAdd = b ; + break ; + } + } + } + else + nNewToAdd = nInters; + } + } + + //GESTIONE DEI TRIANGOLI RISULTANTI DALL'INTERSEZIONE + StmFromTriangleSoup StmFts ; + if ( ! StmFts.Start()) + return GDB_ID_NULL ; + for ( int i = 0 ; i < int( vTria.size()) ; ++ i) + // inserisco il triangolo nella nuova superficie + StmFts.AddTriangle( vTria[i]) ; + // valido la superficie e calcolo le adiacenze + if ( ! StmFts.End()) + return GDB_ID_NULL ; + // se superficie con triangoli + PtrOwner pNewStm( StmFts.GetSurf()) ; + POLYLINEVECTOR vPLTria ; + if ( ! IsNull( pNewStm) && pNewStm->GetTriangleCount() > 0) { + pNewStm->GetLoops( vPLTria) ; + } + + // aggiungo i loop chiusi + for ( int i = 0 ; i < int( vpCCClosed.size()); ++i ) + sfrContour.AddCurve( Release( vpCCClosed[i])) ; + // aggiungo loop derivati dai triangoli + for ( int i = 0 ; i < int( vPLTria.size()); ++i ) { + PtrOwner pCC( CreateCurveComposite()) ; + pCC->FromPolyLine( vPLTria[i]) ; + sfrContour.AddCurve( Release( pCC)) ; + } + PtrOwner pSFR( sfrContour.GetSurf()) ; + if ( IsNull( pSFR) || ! pSFR->IsValid()) + return false ; + + // se la superficie ha normale con z negativa la inverto + if ( pSFR->GetNormVersor().z < 0) + pSFR->Invert() ; + + // verifico se la superficie che ho ottenuto è corretta o devo prendere il complementare ( rispetto allo spazio parametrico totale) + // per verificarlo prendo un punto su questa superficie e verifico dove sta il suo corrispettivo 3D rispetto al piano di taglio + int nChunkMin = 0 , nLoopMin = 0 ; + // sono nello spazio parametrico, quindi le aree delle curve possono essere molto grandi + double dAreaMin = 1e30 ; + bool bPos = false ; + for ( int c = 0 ; c < int( pSFR->GetChunkCount()); ++c) { + PtrOwner pSurf( pSFR->CloneChunk( c)) ; + for ( int l = 0 ; l < pSurf->GetLoopCount( 0); ++l) { + PtrOwner pCrv( pSurf->GetLoop( 0, l)) ; + double dArea ; pCrv->GetAreaXY( dArea) ; + if ( abs( dArea) < dAreaMin) { + nChunkMin = c ; + nLoopMin = l ; + dAreaMin = abs( dArea) ; + bPos = dArea > 0 ; + } + } + } + + // aggiorno la superficie di trim + Point3d ptStart ; + Vector3d vtDir, vtDirS, vtDirE ; + PtrOwner pCrv( pSFR->GetLoop( nChunkMin, nLoopMin)) ; + pCrv->GetStartPoint( ptStart) ; + pCrv->GetStartDir( vtDirS) ; + pCrv->GetEndDir( vtDirE) ; + vtDir = vtDirS + vtDirE ; + PtrOwner pCL( CreateCurveLine()) ; + pCL->SetPVL( ptStart, vtDir, 1e6) ; + IntersCurveCurve icc( *pCL, *pCrv) ; + IntCrvCrvInfo iccInfo ; + // verifico di guardare verso l'interno ( il numero di intersezioni deve essere pari visto che partivo da un punto sulla curva) + if ( icc.GetIntersCount()%2 != 0) { + vtDir = vtDirS - vtDirE ; + PtrOwner pCL2( CreateCurveLine()) ; pCL2->SetPVL( ptStart, vtDir, 1e6) ; + IntersCurveCurve icc2( *pCL2, *pCrv) ; + if ( icc2.GetIntersCount()%2 != 0) { + vtDir = vtDirS ; + vtDir.Rotate( Z_AX, bPos? 90 : -90) ; + PtrOwner pCL3( CreateCurveLine()) ; pCL3->SetPVL( ptStart, vtDir, 1e6) ; + IntersCurveCurve icc3( *pCL3, *pCrv) ; + if ( icc3.GetIntersCount()%2 != 0) + return false ; + icc3.GetIntCrvCrvInfo( 1, iccInfo) ; + } + else + icc2.GetIntCrvCrvInfo( 1, iccInfo) ; + } + else + icc.GetIntCrvCrvInfo( 1, iccInfo) ; + + Point3d ptI = iccInfo.IciA[0].ptI ; + Point3d ptToCheck = ( ptStart + ptI) / 2 ; + Point3d pt3D ; GetPointD1D2( ptToCheck.x / SBZ_TREG_COEFF, ptToCheck.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt3D) ; + double dDist = DistPointPlane( pt3D, plPlane) ; + + // ho due casi in cui devo invertire(prendere il complementare rispetto allo spazio parametrico) la superficie: + // 1. se il punto è sopra il piano ed era dentro una curva CCW + // 2. se il punto è sotto il piano ed era interno ad una curva CW + + // la SetTrimRegion controlla se avevo trim precedenti ed eventualmente fa l'intersezione con lo spazio esistente + if ( ( dDist > 0 && bPos) || ( dDist < 0 && ! bPos)) { + if ( ! SetTrimRegion( *pSFR, false) || ! m_pTrimReg->IsValid()) + return false ; + } + else { + if ( ! SetTrimRegion( *pSFR) || ! m_pTrimReg->IsValid()) + return false ; + } + + // imposto ricalcolo della grafica + m_OGrMgr.Reset() ; + + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfBezier::UnprojectPointFromStm( int nT, const Point3d& ptI, Point3d& ptSP, int nIL) const +{ + return UnprojectPointFromStm( nT, ptI, ptSP, nIL, P_INVALID) ; +} + +//---------------------------------------------------------------------------- +bool +SurfBezier::UnprojectPointFromStm( int nT, const Point3d& ptI, Point3d& ptSP, int nIL, const Point3d& ptIPrevOrNext, bool* bThroughEdge) const +{ + ptSP = ORIG ; + if ( bThroughEdge != nullptr) + *bThroughEdge = false ; + // dato un punto sulla trimesh ausiliaria, ne ricavo le coordinate parametriche + const ISurfTriMesh* pSurfTm = GetAuxSurf() ; + int nTriaIndex = nT ; + if ( nT == -1) { + DistPointSurfTm distPtStm0( ptI, *pSurfTm) ; + distPtStm0.GetMinDistTriaIndex( nTriaIndex) ; + } + // aggiungo il primo punto + // devo subito capire se sono in un polo o no + // se sono in polo e mi hanno passato un punto precedente allora devo prendere il triangolo di quel punto + bool bIsPole = false ; + int nInters = 0 ; + INTVECTOR vInters(4) ; + fill( vInters.begin(), vInters.end(), 0) ; + // se il vettore dei poli non è stato riempito vuol dire che quando è stata creata la superficie non è stata chiamata la funzione CalcPoles + if ( int( m_vbPole.size()) == 0) + return false ; + if ( m_vbPole[0] || m_vbPole[1] || m_vbPole[2] || m_vbPole[3] || m_bClosedU || m_bClosedV) { + // scorro sugli edge + for ( int c = 0 ; c < 4 ; ++c) { + // scorro sui tratti che compongono l'edge + for ( int i = 0 ; i < int( m_mCCEdge[c].size()) ; ++i) { + if ( ! m_mCCEdge[c][i]->IsValid()) { + Point3d pt ; + if ( ! m_mCCEdge[c][i]->GetOnlyPoint(pt)) + return false ; + vInters[c] = AreSamePointApprox( pt, ptI) ? 1 : 0 ; + nInters += vInters[c] ; + } + else { + vInters[c] = m_mCCEdge[c][i]->IsPointOn(ptI) ? 1 : 0 ; + nInters += vInters[c] ; + } + } + } + + // se ho tre intersezioni vuol dire che un lato è collassato in un punto e il punto di cui voglio la controimmagine è esattamente nel polo + if ( nInters == 3 || ( m_bClosedU && ( vInters[1] == 1 || vInters[3] == 1)) || ( m_bClosedV && ( vInters[1] == 1 || vInters[3] == 1))) { + if ( nInters == 3) + bIsPole = true ; + // visto che sono in un polo o su un lato di chiusura devo verificare di aver ricevuto il triangolo giusto + // se è stato passato il punto successivo o precedente mi sposto verso quello e ricalcolo il triangolo di appartenenza + if ( ! ptIPrevOrNext.IsValid()) + return false ; + if ( bThroughEdge != nullptr) + *bThroughEdge = true ; + Point3d ptI2 = ptI + ( ptIPrevOrNext - ptI) * EPS_SMALL ; + // ricalcolo il triangolo di appartenenza + DistPointSurfTm dPtStm( ptI2, *pSurfTm) ; + dPtStm.GetMinDistTriaIndex( nTriaIndex) ; + } + } + + // recupero i dati dei vertici del triangolo che fa intersezione + int nVert[3] ; + pSurfTm->GetTriangle( nTriaIndex, nVert) ; + PNTVECTOR vPtPa(3) ; + pSurfTm->GetVertexParam( nVert[0], vPtPa[0].x,vPtPa[0].y) ; + pSurfTm->GetVertexParam( nVert[1], vPtPa[1].x,vPtPa[1].y) ; + pSurfTm->GetVertexParam( nVert[2], vPtPa[2].x,vPtPa[2].y) ; + PNTVECTOR vPT(3) ; + pSurfTm->GetVertex( nVert[0], vPT[0]) ; + pSurfTm->GetVertex( nVert[1], vPT[1]) ; + pSurfTm->GetVertex( nVert[2], vPT[2]) ; + // se la superficie è chiusa controllo se devo tenere conto della periodicità nel prendere le coordinate parametriche dei vertici + double dParamH, dParamL ; + if ( m_bClosedU || m_bClosedV) { + dParamH = m_nSpanV * SBZ_TREG_COEFF ; + dParamL = m_nSpanU * SBZ_TREG_COEFF ; + // devo trovare il lato più lungo e confrontarlo con le dimensioni dello spazio parametrico + Vector3d vtDir ; + double dDist ; + if ( DistXY( vPtPa[0], vPtPa[1]) > DistXY( vPtPa[1], vPtPa[2]) && DistXY( vPtPa[0], vPtPa[1]) > Dist( vPtPa[0], vPtPa[2])){ + vtDir = vPtPa[1] - vPtPa[0] ; + dDist = DistXY( vPtPa[0], vPtPa[1]) ; + } + else if ( DistXY( vPtPa[1], vPtPa[2]) > DistXY( vPtPa[0], vPtPa[1]) && DistXY( vPtPa[1], vPtPa[2]) > Dist( vPtPa[0], vPtPa[2])){ + vtDir = vPtPa[2] - vPtPa[1] ; + dDist = DistXY( vPtPa[1], vPtPa[2]) ; + } + else if ( DistXY( vPtPa[0], vPtPa[2]) > DistXY( vPtPa[0], vPtPa[1]) && DistXY( vPtPa[0], vPtPa[2]) > Dist( vPtPa[1], vPtPa[2])){ + vtDir = vPtPa[2] - vPtPa[0] ; + dDist = DistXY( vPtPa[0], vPtPa[2]) ; + } + vtDir.Normalize() ; + // se la dimensione maggiore è grande come la dimensione dello spazio parametrico allora potrebbe essere che le coordinate parametriche di un vertice + // siano da correggere per periodicità + if ( m_bClosedU && abs(vtDir.x) > abs( vtDir.y) && dDist > dParamL * 0.5 ) { + // trovo se dei vertici del triangolo sono sul bordo dello spazio parametrico + INTVECTOR vOn(3) ; + fill( vOn.begin(), vOn.end(), -1) ; + int nVertOnPole = -1 ; + INTVECTOR vEdgesClosed = { 1, 3} ; + // scorro sui vertici + for ( int p = 0 ; p < 3; ++p ) { + // scorro sugli edge + for ( int ed : vEdgesClosed) { + // scorro sui tratti che compongono l'edge + for ( int i = 0 ; i < int( m_mCCEdge[ed].size()) ; ++i) { + if ( ! m_mCCEdge[ed][i]->IsValid()) { + Point3d pt ; + if ( ! m_mCCEdge[ed][i]->GetOnlyPoint(pt)) + return false ; + if ( AreSamePointApprox( pt, vPT[p])) { + vOn[p] = ed ; + // se un vertice sta su un polo me lo segno + nVertOnPole = p ; + } + } + else { + if (m_mCCEdge[ed][i]->IsPointOn(vPT[p]) && vOn[p] == -1 ) + vOn[p] = ed ; + } + } + } + } + // controllo che almeno un vertice sia su un edge e se è l'unico vertice, che non sia su un polo + if ( vOn[0] > 0 || vOn[1] > 0 || vOn[2] > 0) { + // se ho più un vertice sul lato oppure se ne ho solo uno ma non è sul polo allora procedo alla correzione delle coordinate + if ( vOn[0] * vOn[1] * vOn[2] < 0 || + (vOn[0] > 0 && vOn[0] != nVertOnPole) || + (vOn[1] > 0 && vOn[1] != nVertOnPole) || + (vOn[2] > 0 && vOn[2] != nVertOnPole)) { + double dRightX ; + // tengo per buone le coordinate dei vertici che NON sono sul bordo dello spazio parametrico + for ( int p = 0 ; p < 3; ++p) { + if ( vOn[p] == -1) { + dRightX = vPtPa[p].x ; + break ; + } + } + for ( int p = 0 ; p < 3; ++p) { + if ( abs(vPtPa[p].x - dRightX) > EPS_SMALL ) { + if ( vPtPa[p].x < EPS_SMALL) + vPtPa[p].x = dParamL ; + else + vPtPa[p].x = 0 ; + } + } + } + } + } + else if ( m_bClosedV && abs(vtDir.y) > abs(vtDir.x) && dDist > dParamH * 0.5) { + INTVECTOR vOn(3) ; + fill( vOn.begin(), vOn.end(), -1) ; + int nVertOnPole = -1 ; + INTVECTOR vEdgesClosed = { 0, 2} ; + // scorro sui vertici + for ( int p = 0 ; p < 3; ++p ) { + //scorro sugli edge + for (int ed : vEdgesClosed) { + // scorro sui tratti che compongono l'edge + for ( int i = 0 ; i < int( m_mCCEdge[ed].size()) ; ++i) { + if ( ! m_mCCEdge[ed][i]->IsValid()) { + Point3d pt ; + if ( ! m_mCCEdge[ed][i]->GetOnlyPoint( pt)) + return false ; + if ( AreSamePointApprox( pt, vPT[p])) { + vOn[p] = ed ; + // se un vertice sta su un polo me lo segno + nVertOnPole = p ; + } + } + else { + if( m_mCCEdge[ed][i]->IsPointOn( vPT[p]) && vOn[p] == -1) + vOn[p] = ed ; + } + } + } + } + // controllo che almeno un vertice sia su un edge + if ( vOn[0] > 0 || vOn[1] > 0 || vOn[2] > 0) { + // se ho più un vertice sul lato oppure se ne ho solo uno ma non è sul polo allora procedo alla correzione delle coordinate + if ( vOn[0] * vOn[1] * vOn[2] < 0 || + (vOn[0] > 0 && vOn[0] != nVertOnPole) || + (vOn[1] > 0 && vOn[1] != nVertOnPole) || + (vOn[2] > 0 && vOn[2] != nVertOnPole)) { + double dRightY ; + // tengo per buone le coordinate dei vertici che NON sono sul bordo dello spazio parametrico + for ( int p = 0 ; p < 3; ++p) { + if ( vOn[p] == -1) { + dRightY = vPtPa[p].y ; + break ; + } + } + for ( int p = 0 ; p < 3; ++p) { + if ( abs(vPtPa[p].y - dRightY) > EPS_SMALL) { + if ( vPtPa[p].y < EPS_SMALL) + vPtPa[p].y = dParamH ; + else + vPtPa[p].y = 0 ; + } + } + } + } + } + } + // devo anche tener conto della possibilità che i lati siano collassati in poli + if ( bIsPole) { + int nInters = -1 ; + for ( int c = 0 ; c < 4 ; ++c) { + if ( ( c == 0 && vInters[0] == vInters[3]) || + ( c != 0 && vInters[c] == vInters[c - 1])){ + nInters = c ; + break ; + } + } + // se non ho trovato il lato su cui ho il polo + if ( nInters == -1) + return false ; + // trovo quale vertice è sull'edge di polo + BOOLVECTOR vbOn(3) ; + std::fill( vbOn.begin(), vbOn.end(), false) ; + for ( int p = 0 ; p < 3; ++p ) { + for ( int c = 0 ; c < 4; ++c) { + for( int i = 0 ; int( m_mCCEdge[c].size()) ; ++i) { + if ( ! m_mCCEdge[c][i]->IsValid()) { + Point3d pt ; + if ( ! m_mCCEdge[c][i]->GetOnlyPoint( pt)) + return false ; + vbOn[p] = vbOn[p] || AreSamePointApprox( pt, vPT[p]) ; + } + else + vbOn[p] = vbOn[p] || m_mCCEdge[c][i]->IsPointOn( vPT[p]) ; + } + } + } + // trovo la coordinata giusta da tenere ( x o y a seconda dell'edge) + double dRightX, dRightY ; + for ( int p = 0 ; p < 3; ++p) { + if ( ! vbOn[p]) { + if ( nInters == 0 || nInters == 2) { + dRightX = vPtPa[p].x ; + dRightY = nInters == 0 ? dParamH : 0 ; + } + else if ( nInters == 1 || nInters == 3) { + dRightX = nInters == 1 ? 0 : dParamL ; + dRightY = vPtPa[p].y ; + } + } + } + // correggo le coordinate del punto sull'edge di polo + for ( int p = 0 ; p < 3 ; ++p) { + if ( vbOn[p]) { + vPtPa[p].x = dRightX ; + vPtPa[p].y = dRightY ; + } + } + } + + // se l'intersezione era su un vertice ( NON DI POLO) restituisco le coordinate parametriche del vertice + if ( nIL == 3 && ! bIsPole) { + if ( AreSamePointApprox(ptI, vPT[0])) + ptSP = vPtPa[0] ; + else if ( AreSamePointApprox(ptI, vPT[1])) + ptSP = vPtPa[1] ; + else if ( AreSamePointApprox(ptI, vPT[2])) + ptSP = vPtPa[2] ; + return true ; + } + // calcolo approssimativamente le coordinate nello spazio parametrico del punto di intersezione + // quindi prima calcolo la composizione lineare tra i vertici del triangolo per ottenere il punto di intersezione + Eigen::Matrix3d mA ; + mA.col(0) << vPT[0].x, vPT[0].y , vPT[0].z ; + mA.col(1) << vPT[1].x, vPT[1].y , vPT[1].z ; + mA.col(2) << vPT[2].x, vPT[2].y , vPT[2].z ; + int nCount = 0 ; + Vector3d vtMod( 0, 0, 0) ; + while ( abs( mA.determinant()) < EPS_SMALL) { + if ( nCount %3 == 0) { + mA.row(0) << vPT[0].x + 1, vPT[1].x + 1, vPT[2].x + 1 ; + vtMod.x += 1 ; + } + else if ( nCount %3 == 1) { + mA.row(1) << vPT[0].y + 1, vPT[1].y + 1, vPT[2].y + 1 ; + vtMod.y += 1 ; + } + else if ( nCount %3 == 2) { + mA.row(2) << vPT[0].z + 1, vPT[1].z + 1, vPT[2].z + 1 ; + vtMod.z += 1 ; + } + ++nCount ; + if ( nCount == 10) + return false ; + } + Point3d ptNewI = ptI + vtMod ; + Eigen::Vector3d b ( ptNewI.x, ptNewI.y, ptNewI.z) ; + Eigen::Vector3d x = mA.fullPivLu().solve(b) ; + // applico questa composizione alle loro coordinate parametriche + Eigen::Matrix3d mB ; + mB.col(0) << vPtPa[0].x, vPtPa[0].y, 0 ; + mB.col(1) << vPtPa[1].x, vPtPa[1].y, 0 ; + mB.col(2) << vPtPa[2].x, vPtPa[2].y, 0 ; + Eigen::Vector3d ptParam = mB * x ; + IncreaseUV( ptSP.x, ptParam.x(), true) ; + IncreaseUV( ptSP.y, ptParam.y(), false) ; + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfBezier::UnprojectPoint( const Point3d& pt3D, Point3d& ptParam, const Point3d& ptIPrev, bool* bThroughEdge, const Plane3d* pPlCut) const +{ + // dato il punto pt3D sulla superficie di Bezier si cercano le coordinate parametriche ( ptParam) , iterativamente con Newton + // trovato un primo candidato ptParam, ne calcolo l'immagine sulla superficie ( ptBez) e ne calcolo la distanza con il punto pt3D + // ripeto cercando di avvicinarmi il più possibile + // per trovare il primo punto trovo il triangolo della trimesh ausiliaria più vicino e il punto più vicino + DistPointSurfTm dptSurfTm( pt3D, *GetAuxSurf()) ; + Point3d ptI ; dptSurfTm.GetMinDistPoint( ptI) ; + if ( ! UnprojectPointFromStm( -1, ptI, ptParam, 5, ptIPrev, bThroughEdge)) + return false ; + Point3d ptBez ; + GetPointD1D2( ptParam.x / SBZ_TREG_COEFF, ptParam.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBez) ; + // usando un algoritmo di newton cerco di avvicinarmi il più possibile al punto + + double dDistNew = pPlCut == nullptr ? Dist( pt3D, ptBez) : abs(DistPointPlane( ptBez, *pPlCut)) ; + double dDistPre ; + double dDist0, dDist1; + + int nCount = 0 ; + double dh = EPS_SMALL ; + // metodo di newton in più dimensioni + // vario sia il parametro U che il parametro V e verifico se la distanza dalla retta diminuisce per scostamenti positivi o negativi. + bool bRetry = false ; + double dApproach = 0.01 ; + bool bDesperate = false ; + double dAng = 1 ; + double dr = 5 ; + double dfdU, dfdV ; + + while ( dDistNew > 2 * EPS_SMALL && nCount < 100) { + + if ( ! bRetry) { + dDistPre = dDistNew ; + // derivata in U + Point3d ptIBzNew1 ; + //GetPointD1D2( ( ptParam.x + dh) / SBZ_TREG_COEFF, ptParam.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptIBzNew1) ; + double dUh ; IncreaseUV( ptParam.x, dh, true, &dUh, false) ; + GetPointD1D2( dUh / SBZ_TREG_COEFF, ptParam.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptIBzNew1) ; + dDist0 = pPlCut == nullptr ? Dist( pt3D, ptIBzNew1) : abs(DistPointPlane( ptIBzNew1, *pPlCut)) ; + dfdU = ( dDist0 - dDistPre) / dh ; + // derivata in V + Point3d ptIBzNew2 ; + //GetPointD1D2( ptParam.x / SBZ_TREG_COEFF, ( ptParam.y + dh) / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptIBzNew2) ; + double dVh ; IncreaseUV( ptParam.y, dh, false, &dVh, false) ; + GetPointD1D2( ptParam.x / SBZ_TREG_COEFF, dVh / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptIBzNew2) ; + dDist1 = pPlCut == nullptr ? Dist( pt3D, ptIBzNew2) : abs(DistPointPlane( ptIBzNew2, *pPlCut)) ; + dfdV = ( dDist1 - dDistPre) / dh ; + } + // calcolo le nuove coordinate + Vector3d vtDir ; + double dASum = abs(dfdU) + abs(dfdV) ; + double dSSum = sqrt( pow(dfdU,2) + pow( dfdV,2)) ; + vtDir.Set( - dfdU, - dfdV, 0) ; + if ( ! vtDir.Normalize() ) + vtDir.Set( - dfdU / dSSum, - dfdV / dSSum, 0) ; + dr = dDistPre / dASum ; + // in modalità Retry riduco lo spostamento + vtDir *= dr * ( bRetry ? 0.1 : 0.5) ; + if ( bDesperate) { + // in depserate mode riduco lo spostamento e comincio a cambiare progressivamente la direzione oscillando tra destra e sinistra della direzione "naturale" + vtDir *= 0.5 ; + dAng *= -1.25 ; + // riduco ulteriormente lo spostamento dopo qualche giro in desperate mode + if ( abs( dAng) > 5) + vtDir *= 0.5 ; + vtDir.Rotate( Z_AX, dAng) ; + } + Point3d ptCopy = ptParam ; + IncreaseUV( ptParam, vtDir, nullptr, true) ; + // calcolo la nuova distanza tra il punto di partenza e quello che sto trovando con le coordinate attuali + GetPointD1D2( ptParam.x / SBZ_TREG_COEFF, ptParam.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBez) ; + dDistNew = pPlCut == nullptr ? Dist( pt3D, ptBez) : abs(DistPointPlane( ptBez, *pPlCut)) ; + + dApproach = dDistPre - dDistNew ; + // se ho peggiorato la situazione rispetto allo step precedente torno indietro e vado in Retry mode + if ( dApproach < EPS_ZERO) { + if ( bRetry) + bDesperate = true ; // entro in desperate mode + ptParam = ptCopy ; + bRetry = true ; + // se ho già fatto 9 giri in desperate mode allora esco + if ( abs( dAng) > 8) + break ; + } + else { + bRetry = false ; + bDesperate = false ; + dAng = 1 ; + } + ++nCount ; + } + + // se il punto era su un edge allora verifico che sia ancora su un edge, sennò ce lo riporto + // guardo a quale dei due lati sono più vicino + // devo distinguere il caso di un triangolo a metà dello spazio, con un vertice su un lato di polo, ma senza vertici su lati di chiusura + if ( bThroughEdge != nullptr && *bThroughEdge) { + if ( m_bClosedU) { + if ( ptParam.x < 1) + ptParam.x = 0 ; + else if ( abs( m_nSpanU * SBZ_TREG_COEFF - ptParam.x) < 1) + ptParam.x = m_nSpanU * SBZ_TREG_COEFF ; + } + if ( m_bClosedV) { + if ( ptParam.y < 1) + ptParam.y = 0 ; + else if ( abs( m_nSpanV * SBZ_TREG_COEFF - ptParam.y) < 1) + ptParam.y = m_nSpanV * SBZ_TREG_COEFF ; + } + } + + return nCount != 100 || (dDistNew < dDistPre ? dDistNew : dDistPre) < 10 * EPS_SMALL ; +} + +//---------------------------------------------------------------------------- +bool +SurfBezier::CalcPoles( void) +{ + // controllo se uno o più lati sono in realtà dei poli + for ( int i = 0 ; i < 4 ; ++i) + m_vbPole.emplace_back( true) ; + // scorro i punti di controllo e vedo subito + bool bOk = false ; + bool bPole0 = true, bPole1 = true ; + Point3d ptU0, ptU1 ; + // controllo l'edge 0 e 2 per vedere se tutti i punti dell'edge sono coincidenti + Point3d ptP00 = GetControlPoint( 0, &bOk) ; + Point3d ptP10 = GetControlPoint( m_nDegU * m_nSpanU, &bOk) ; + for ( int i = 1 ; i < m_nDegV * m_nSpanV + 1 ; ++ i) { + ptU0 = GetControlPoint( i * ( m_nDegU * m_nSpanU + 1), &bOk) ; + bPole0 = bPole0 && AreSamePointApprox( ptP00, ptU0) ; + ptU1 = GetControlPoint( ( i + 1) * ( m_nDegU * m_nSpanU + 1) - 1, &bOk) ; + bPole1 = bPole1 && AreSamePointApprox( ptP10, ptU1) ; + if ( ! bPole0 && ! bPole1) + break ; + } + m_vbPole[1] = bPole0 ; // u = 0 corrisponde all'edge 1 + m_vbPole[3] = bPole1 ; // u = 1 corrisponde all'edge 3 + // controllo l'edge 1 e 3 per vedere se tutti i punti dell'edge sono coincidenti + Point3d ptV0, ptV1 ; + Point3d ptP01 = GetControlPoint( ( m_nDegU * m_nSpanU + 1) * ( m_nDegV * m_nSpanV), &bOk) ; + bPole0 = true ; + bPole1 = true ; + for ( int i = 1 ; i < m_nDegU * m_nSpanU + 1 ; ++ i) { + ptV0 = GetControlPoint( i, &bOk) ; + bPole0 = bPole0 && AreSamePointApprox( ptP00, ptV0) ; + ptV1 = GetControlPoint( i + ( m_nDegU * m_nSpanU + 1) * ( m_nDegV * m_nSpanV), &bOk) ; + bPole1 = bPole1 && AreSamePointApprox( ptP01, ptV1) ; + if ( ! bPole0 && ! bPole1) + break ; + } + m_vbPole[0] = bPole1 ; // v = 1 corrisponde all'edge 0 + m_vbPole[2] = bPole0 ; // v = 0 corrisponde all'edge 2 + + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfBezier::GetLoops( ICRVCOMPOPOVECTOR& vCC, bool bLineOrBezier, int nEdge) const +{ + // se nEdge non è definito ( == -1) allora restituisco tutti gli edge in ordine + // se bOpenOrAll è true allora restituisco solo gli edge aperti e che non sono di polo + if ( m_pSTM == nullptr) + GetAuxSurf() ; + + // se la superficie non è trimmata mi basta recuperare gli edge della superficie + if ( ! m_bTrimmed) { + // se decidessi di non restituire gli edge chiusi e i poli posso discriminare qui + if ( nEdge == -1 ) { + vCC = ICRVCOMPOPOVECTOR(4) ; + if ( ! m_bClosedV ) { + if ( ! m_vbPole[0]) + vCC[0].Set( GetSingleEdge3D( bLineOrBezier, 0)) ; + if ( ! m_vbPole[2]) + vCC[2].Set( GetSingleEdge3D( bLineOrBezier, 2)) ; + } + if ( ! m_bClosedU ) { + if ( ! m_vbPole[1]) + vCC[1].Set( GetSingleEdge3D( bLineOrBezier, 1)) ; + if ( ! m_vbPole[3]) + vCC[3].Set( GetSingleEdge3D( bLineOrBezier, 3)) ; + } + //// se li volessi restituire tutti + //vCC.emplace_back( GetSingleEdge3D( bLineOrBezier, 0)) ; + //vCC.emplace_back( GetSingleEdge3D( bLineOrBezier, 1)) ; + //vCC.emplace_back( GetSingleEdge3D( bLineOrBezier, 2)) ; + //vCC.emplace_back( GetSingleEdge3D( bLineOrBezier, 3)) ; + } + else { + if ( (((nEdge == 0 || nEdge == 2) && ! m_bClosedV) || ((nEdge == 1 || nEdge == 3) && ! m_bClosedU)) && ! m_vbPole[nEdge]) + vCC.emplace_back( GetSingleEdge3D( bLineOrBezier, nEdge)) ; + } + } + // se la superficie è trimmata devo recuperare i loop dello spazio parametrico + else { + // devo ricostruire i bordi aperti in caso di superficie chiusa + // costruisco gli edge + ICurveLine* pCLU0( CreateCurveLine()) ; + ICurveLine* pCLU1( CreateCurveLine()) ; + ICurveLine* pCLV0( CreateCurveLine()) ; + ICurveLine* pCLV1( CreateCurveLine()) ; + if ( m_bClosedU ) { + pCLU0->Set( Point3d( 0, m_nSpanV * SBZ_TREG_COEFF, 0), ORIG) ; + pCLU1->Set( Point3d( m_nSpanU * SBZ_TREG_COEFF, 0, 0), Point3d( m_nSpanU * SBZ_TREG_COEFF, m_nSpanV * SBZ_TREG_COEFF, 0)) ; + } + if ( m_bClosedV ) { + pCLV0->Set( ORIG, Point3d( m_nSpanU * SBZ_TREG_COEFF, 0, 0)) ; + pCLV1->Set( Point3d( m_nSpanU * SBZ_TREG_COEFF, m_nSpanV * SBZ_TREG_COEFF, 0), Point3d( 0, m_nSpanV * SBZ_TREG_COEFF, 0)) ; + } + ICRVLINEPOVECTOR vEdge ; + // li metto con l'ordine degli edge + vEdge.emplace_back( pCLV1) ; + vEdge.emplace_back( pCLU0) ; + vEdge.emplace_back( pCLV0) ; + vEdge.emplace_back( pCLU1) ; + // costruisco la mappa delle intersezioni, trovando tutte le intersezioni tra i trim e il loop esterno dello spazio parametrico + // i 4 elementi fanno riferimento ai 4 edge. per ogni loop viene salvato il vettore delle intersezioni con tale edege + vector> vmInters(4) ; + // comincio anche a creare il vettore di vettori associati ad ogni loop che fa almeno un'intersezione con un edge. + // in ogni vettore verranno salvati l'indice del loop all'interno del vettore dei loop e gli indici di tutti i loop che deriveranno da sue divisioni in più curve + unordered_map mSplitLoop ; + int nOriginalLoops = int( m_vCCLoop.size()) ; + for ( int l = 0 ; l < nOriginalLoops ; ++ l) { + bool bInters = false ; + // devo controllare se i loop si appoggiano a bordi chiusi + // se ne ho più di uno che si appoggia allora devo vedere se trovo dei gruppi di loop che in realtà in 3d sono un edge unico, ma nel 2d risultano a cavallo dell'edge di chiusura + PtrOwner pLoop( m_vCCLoop[l]->Clone()) ; + for ( int j = 0 ; j < 4 ; ++j) { + // il primo è un loop di trim, il secondo è un edge dello spazio parametrico + IntersCurveCurve icc( *pLoop, *vEdge[j]) ; + ICCIVECTOR vICC ; + for ( int i = 0 ; i < int(icc.GetIntersCount()) ; ++i ) { + IntCrvCrvInfo iccInfo ; + icc.GetIntCrvCrvInfo( i, iccInfo) ; + vICC.push_back( iccInfo) ; + } + if ( int(vICC.size()) > 0) { + vmInters[j].insert(pair(l, vICC)) ; + bInters = true ; + } + } + if ( bInters) + mSplitLoop.insert( pair( l, INTVECTOR({l}))) ; + } + + if ( m_bClosedV) { + // scorro i loop che fanno intersezioni con l'edge 0 ( V=1) e le riporto sull'edge opposto + for ( pair vInters0 : vmInters[0]) { + // scorro le intersezioni del loop + for ( IntCrvCrvInfo inters0 : vInters0.second) { + if ( ! inters0.bOverlap) + continue ; + Point3d ptStart0 = inters0.IciA[0].ptI ; + Point3d ptEnd0 = inters0.IciA[1].ptI ; + // porto i punti sull'edge opposto + ptStart0.y = 0 ; + ptEnd0.y = 0 ; + // scorro le intersezioni sull'edge opposto e quando trovo un loop che tra le sue intersezioni contiene uno dei punti che ho appena portato + // su qusto edge allora cancello la parte comune + for ( pair vInters2 : vmInters[2] ) { + // scorro le intersezioni di questo loop + for ( IntCrvCrvInfo inters2 : vInters2.second) { + // se lo start o end del loop corrente è compreso tra lo start e l'end di un loop che fa intesezione sull'edge opposto allora + // devo cancellare la parte comune + Point3d ptStart2 = inters2.IciA[0].ptI ; + Point3d ptEnd2 = inters2.IciA[1].ptI ; + // come ptStart2 prendo quello con la x maggiore + if ( ptStart2.x < ptEnd2.x) + swap( ptStart2, ptEnd2) ; + if ( (( ptEnd0.x - EPS_SMALL< ptStart2.x && ptStart2.x < ptStart0.x + EPS_SMALL) || ( ptEnd0.x - EPS_SMALL < ptEnd2.x && ptEnd2.x < ptStart0.x + EPS_SMALL)) || + (( ptEnd2.x - EPS_SMALL < ptStart0.x && ptStart0.x < ptStart2.x + EPS_SMALL) || ( ptEnd2.x - EPS_SMALL < ptEnd0.x && ptEnd0.x < ptStart2.x + EPS_SMALL))) { + PtrOwner pCL( CreateBasicCurveLine()) ; + pCL->Set( ptStart0, ptEnd0) ; + // devo scorrere su tutte le curve che sono state ottenute dallo split del loop originale + for ( int w = 0 ; w < int( mSplitLoop[vInters2.first].size()) ; ++w) { + int nIndex2 = mSplitLoop[vInters2.first][w] ; + IntersCurveCurve icc( *m_vCCLoop[nIndex2], *pCL) ; + IntCrvCrvInfo iccInfo ; icc.GetIntCrvCrvInfo( 0, iccInfo) ; + for ( int k = 0 ; k < icc.GetIntersCount() ; ++k) { + icc.GetIntCrvCrvInfo( k, iccInfo) ; + if ( iccInfo.bOverlap) + break ; + } + if ( ! iccInfo.bOverlap) + continue ; + // se parto da una curva chiusa semplicemente tolgo un pezzo + if ( m_vCCLoop[nIndex2]->IsClosed()) { + ICurveComposite* pCC2( GetCurveComposite( m_vCCLoop[nIndex2]->CopyParamRange( iccInfo.IciA[1].dU, iccInfo.IciA[0].dU))) ; + m_vCCLoop[nIndex2].Set( pCC2) ; + } + else { + // se la curva era già aperta allora otterrò due curve separate + ICurveComposite* pCC2a( GetCurveComposite( m_vCCLoop[nIndex2]->Clone())) ; + bool bDoneA = pCC2a->TrimEndAtParam( iccInfo.IciA[0].dU) ; + ICurveComposite* pCC2b( GetCurveComposite( m_vCCLoop[nIndex2]->Clone())) ; + bool bDoneB = pCC2b->TrimStartAtParam( iccInfo.IciA[1].dU) ; + if ( bDoneA) { + m_vCCLoop[nIndex2].Set( pCC2a) ; + if ( bDoneB) { + m_vCCLoop.emplace_back( pCC2b) ; + mSplitLoop[vInters2.first].push_back( m_vCCLoop.size() - 1) ; + } + } + else if ( bDoneB) + m_vCCLoop[nIndex2].Set( pCC2b) ; + } + + // per togliere la parte comune al loop corrente devo riportare i punti di intersezione sull'edge di partenza + Point3d ptOverlapS = iccInfo.IciB[0].ptI ; + Point3d ptOverlapE = iccInfo.IciB[1].ptI ; + ptOverlapS.y = m_nSpanV * SBZ_TREG_COEFF ; + ptOverlapE.y = m_nSpanV * SBZ_TREG_COEFF ; + double dStart0, dEnd0 ; + // scorro tutte le curve in cui è stato splittato il loop corrente + for ( int j = 0 ; j < int( mSplitLoop[vInters0.first].size()) ; ++j ) { + int nIndex0 = mSplitLoop[vInters0.first][j] ; + if ( ! m_vCCLoop[nIndex0]->GetParamAtPoint( ptOverlapS, dStart0) || ! m_vCCLoop[nIndex0]->GetParamAtPoint( ptOverlapE, dEnd0)) + continue ; + // se parto da una curva chiusa semplicemente tolgo un pezzo + if ( m_vCCLoop[nIndex0]->IsClosed()) { + ICurveComposite* pCC0( GetCurveComposite( m_vCCLoop[nIndex0]->CopyParamRange( dStart0, dEnd0))) ; + m_vCCLoop[nIndex0].Set( pCC0) ; + } + else { + // se la curva era già aperta allora otterrò due curve separate + ICurveComposite* pCC0a( GetCurveComposite( m_vCCLoop[nIndex0]->Clone())) ; + bool bDoneA = pCC0a->TrimEndAtParam( dEnd0) ; + ICurveComposite* pCC0b( GetCurveComposite( m_vCCLoop[nIndex0]->Clone())) ; + bool bDoneB = pCC0b->TrimStartAtParam( dStart0) ; + if ( bDoneA) { + m_vCCLoop[nIndex0].Set( pCC0a) ; + if ( bDoneB) { + m_vCCLoop.emplace_back( pCC0b) ; + mSplitLoop[vInters0.first].push_back( m_vCCLoop.size() - 1) ; + } + } + else if ( bDoneB) + m_vCCLoop[nIndex0].Set( pCC0b) ; + } + //break ; + } + //break ; + } + } + } + } + } + } + } + + if ( m_bClosedU) { + // scorro i loop che fanno intersezioni con l'edge 1 ( U=0) e le riporto sull'edge opposto + for ( pair vInters1 : vmInters[1]) { + // scorro le intersezioni del loop + for ( IntCrvCrvInfo inters1 : vInters1.second) { + if ( ! inters1.bOverlap) + continue ; + Point3d ptStart1 = inters1.IciA[0].ptI ; + Point3d ptEnd1 = inters1.IciA[1].ptI ; + // porto i punti sull'edge opposto + ptStart1.x = m_nSpanU * SBZ_TREG_COEFF ; + ptEnd1.x = m_nSpanU * SBZ_TREG_COEFF ; + // scorro le intersezioni sull'edge opposto e quando trovo un loop che tra le sue intersezioni contiene uno dei punti che ho appena portato + // su qusto edge allora cancello la parte comune + for ( pair vInters3 : vmInters[3] ) { + // scorro le intersezioni di questo loop + for ( IntCrvCrvInfo inters3 : vInters3.second) { + // se lo start o end del loop corrente è compreso tra lo start e l'end di un loop che fa intesezione sull'edge opposto allora + // devo cancellare la parte comune + Point3d ptStart3 = inters3.IciA[0].ptI ; + Point3d ptEnd3 = inters3.IciA[1].ptI ; + // come ptStart3 prendo quello con la y maggiore + if ( ptStart3.y < ptEnd3.y) + swap( ptStart3, ptEnd3) ; + if ( (( ptEnd1.y - EPS_SMALL< ptStart3.y && ptStart3.y < ptStart1.y + EPS_SMALL) || ( ptEnd1.y - EPS_SMALL < ptEnd3.y && ptEnd3.y < ptStart1.y + EPS_SMALL)) || + (( ptEnd3.y - EPS_SMALL < ptStart1.y && ptStart1.y < ptStart3.y + EPS_SMALL) || ( ptEnd3.y - EPS_SMALL < ptEnd1.y && ptEnd1.y < ptStart3.y + EPS_SMALL))) { + PtrOwner pCL( CreateBasicCurveLine()) ; + pCL->Set( ptStart1, ptEnd1) ; + // devo scorrere su tutte le curve che sono state ottenute dallo split del loop originale + for ( int w = 0 ; w < int( mSplitLoop[vInters3.first].size()) ; ++w) { + int nIndex3 = mSplitLoop[vInters3.first][w] ; + IntersCurveCurve icc( *m_vCCLoop[nIndex3], *pCL) ; + IntCrvCrvInfo iccInfo ; + for ( int k = 0 ; k < icc.GetIntersCount() ; ++k) { + icc.GetIntCrvCrvInfo( k, iccInfo) ; + if ( iccInfo.bOverlap) + break ; + } + if ( ! iccInfo.bOverlap) + continue ; + // se parto da una curva chiusa semplicemente tolgo un pezzo + if ( m_vCCLoop[nIndex3]->IsClosed()) { + ICurveComposite* pCC3( GetCurveComposite( m_vCCLoop[nIndex3]->CopyParamRange( iccInfo.IciA[1].dU, iccInfo.IciA[0].dU))) ; + m_vCCLoop[nIndex3].Set( pCC3) ; + } + else { + // se la curva era già aperta allora otterrò due curve separate + ICurveComposite* pCC3a( GetCurveComposite( m_vCCLoop[nIndex3]->Clone())) ; + bool bDoneA = pCC3a->TrimEndAtParam( iccInfo.IciA[0].dU) ; + ICurveComposite* pCC3b( GetCurveComposite( m_vCCLoop[nIndex3]->Clone())) ; + bool bDoneB = pCC3b->TrimStartAtParam( iccInfo.IciA[1].dU) ; + if ( bDoneA) { + m_vCCLoop[nIndex3].Set( pCC3a) ; + if ( bDoneB) { + m_vCCLoop.emplace_back( pCC3b) ; + mSplitLoop[vInters3.first].push_back( m_vCCLoop.size() - 1) ; + } + } + else if ( bDoneB ) + m_vCCLoop[nIndex3].Set( pCC3b) ; + } + + // per togliere la parte comune al loop corrente devo riportare i punti di intersezione sull'edge di partenza + Point3d ptOverlapS = iccInfo.IciB[0].ptI ; + Point3d ptOverlapE = iccInfo.IciB[1].ptI ; + ptOverlapS.x = 0 ; + ptOverlapE.x = 0 ; + double dStart1, dEnd1 ; + // scorro tutte le curve in cui è stato splittato il loop iniziale + for ( int j = 0 ; j < int( mSplitLoop[vInters1.first].size()) ; ++j ) { + int nIndex1 = mSplitLoop[vInters1.first][j] ; + if ( ! m_vCCLoop[nIndex1]->GetParamAtPoint( ptOverlapS, dStart1) || ! m_vCCLoop[nIndex1]->GetParamAtPoint( ptOverlapE, dEnd1)) + continue ; + // se parto da una curva chiusa semplicemente tolgo un pezzo + if ( m_vCCLoop[nIndex1]->IsClosed()) { + ICurveComposite* pCC1( GetCurveComposite( m_vCCLoop[nIndex1]->CopyParamRange( dStart1, dEnd1))) ; + m_vCCLoop[nIndex1].Set( pCC1) ; + } + else { + // se la curva era già aperta allora otterrò due curve separate + ICurveComposite* pCC1a( GetCurveComposite( m_vCCLoop[nIndex1]->Clone())) ; + bool bDoneA = pCC1a->TrimEndAtParam( dEnd1) ; + ICurveComposite* pCC1b( GetCurveComposite( m_vCCLoop[nIndex1]->Clone())) ; + bool bDoneB = pCC1b->TrimStartAtParam( dStart1) ; + if ( bDoneA) { + m_vCCLoop[nIndex1].Set( pCC1a) ; + if ( bDoneB) { + m_vCCLoop.emplace_back( pCC1b) ; + mSplitLoop[vInters1.first].push_back( m_vCCLoop.size() - 1) ; + } + } + else if ( bDoneB) + m_vCCLoop[nIndex1].Set( pCC1b) ; + } + //break ; + } + //break ; + } + } + } + } + } + } + } + + // qui portei estrarre una funzione che proietta curve dal parametrico al 3D + // scorro i gruppi di loop 2D formati da loop che partecipano alla formazione dello stesso loop nel 3D + ICRVCOMPOPOVECTOR vpCCOpen ; + for( int i = 0 ; i < int( m_vCCLoop.size()); ++i) { + vpCCOpen.emplace_back(CreateBasicCurveComposite()) ; + PolyLine pl3D ; + // la composita è una spezzata composta da linee, quindi la ricostruisco come polyline in 3D + Point3d pt ; m_vCCLoop[i]->GetStartPoint( pt) ; + Point3d pt3D ; GetPointD1D2( pt.x / SBZ_TREG_COEFF, pt.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt3D) ; + int nCount = 0 ; + pl3D.AddUPoint( nCount, pt3D) ; + ++ nCount ; + // scorro le curve singole della composita + for ( int k = 0 ; k < m_vCCLoop[i]->GetCurveCount() ; ++k){ + // recupero l'end point della curva e lo porto in 3D + m_vCCLoop[i]->GetCurve( k)->GetEndPoint( pt) ; + GetPointD1D2( pt.x / SBZ_TREG_COEFF, pt.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt3D) ; + pl3D.AddUPoint( nCount, pt3D) ; + ++ nCount ; + } + vpCCOpen.back()->FromPolyLine(pl3D) ; + } + // creo la chain a mano con le compo + BOOLVECTOR vbAdded( vpCCOpen.size()) ; + fill( vbAdded.begin(), vbAdded.end(), false) ; + for ( int k = 0 ; k < int( vpCCOpen.size()); ++k ) { + if ( vbAdded[k]) + continue ; + PtrOwner pCC( vpCCOpen[k]->Clone()) ; + vbAdded[k] = true ; + bool bAddedOne = true ; + while( bAddedOne) { + bAddedOne = false ; + for ( int t = k ; t < int(vpCCOpen.size()); ++t ) { + if ( vbAdded[t]) + continue ; + if ( pCC->AddCurve( vpCCOpen[t]->Clone())) { + vbAdded[t] = true ; + bAddedOne = true ; + } + } + } + // aggiungo la curva agli edge 3d da restituire + vCC.emplace_back( Release( pCC)) ; + } + } + return true ; +} + +//---------------------------------------------------------------------------- +ICurveComposite* +SurfBezier::GetSingleEdge3D( bool bLineOrBezier, int nEdge) const +{ + // questa funzione dà per scontato che la superficie NON sia trimmata + if ( nEdge < 0 || nEdge > 3 || m_bTrimmed) + return nullptr ; + ICurveComposite* pCrvCompo( CreateBasicCurveComposite()) ; + switch ( nEdge) { + case 0 : { + // se il bool è true allora restituisco gli edge con la loro approssimazione in forma di linea spezzata 3D + if ( bLineOrBezier) + pCrvCompo = m_mCCEdge[nEdge][0]->Clone() ; + // se il bool è falso restituisco le curve Bezier di edge + else { + //edge 0, scorro sulle patch in U + for ( int i = 0 ; i < m_nSpanU ; ++i) { + PtrOwner pCrvBz0( CreateBasicCurveBezier()) ; + if ( IsNull(pCrvBz0) || ! pCrvBz0->Init( m_nDegU, m_bRat)) + return nullptr ; + for ( int p = 0 ; p < m_nDegU + 1 ; ++p ) { + int nIndex = ( m_nDegU * m_nSpanU + 1) * ( m_nDegV * m_nSpanV) + m_nDegU * i + p ; + if ( ! m_bRat) + pCrvBz0->SetControlPoint( p, GetControlPoint( nIndex, nullptr)) ; + else + pCrvBz0->SetControlPoint( p, GetControlPoint( nIndex, nullptr), GetControlWeight( nIndex, nullptr)) ; + } + pCrvCompo->AddCurve( Release( pCrvBz0)) ; + } + } + break ; + } + case 1 : { + if ( bLineOrBezier) + pCrvCompo = m_mCCEdge[nEdge][0]->Clone() ; + else { + //edge 1, scorro sulle patch in V + for ( int i = 0 ; i < m_nSpanV ; ++i) { + PtrOwner pCrvBz1( CreateBasicCurveBezier()) ; + if ( IsNull(pCrvBz1) || ! pCrvBz1->Init( m_nDegV, m_bRat)) + return nullptr ; + for ( int p = 0 ; p < m_nDegU + 1 ; ++p ) { + int nIndex = ( m_nDegU * m_nSpanU + 1) * p ; + if ( ! m_bRat) + pCrvBz1->SetControlPoint( p, GetControlPoint( nIndex, nullptr)) ; + else + pCrvBz1->SetControlPoint( p, GetControlPoint( nIndex, nullptr), GetControlWeight( nIndex, nullptr)) ; + } + pCrvCompo->AddCurve( Release( pCrvBz1)) ; + } + } + break ; + } + case 2 : { + if ( bLineOrBezier) + pCrvCompo = m_mCCEdge[nEdge][0]->Clone() ; + else { + // edge 2, scorro sulle patch in U + for ( int i = 0 ; i < m_nSpanU ; ++i) { + PtrOwner pCrvBz2( CreateBasicCurveBezier()) ; + if ( IsNull(pCrvBz2) || ! pCrvBz2->Init( m_nDegU, m_bRat)) + return nullptr ; + for ( int p = 0 ; p < m_nDegU + 1 ; ++p ) { + if ( ! m_bRat) + pCrvBz2->SetControlPoint( p, GetControlPoint(m_nDegU * i + p, nullptr)) ; + else + pCrvBz2->SetControlPoint( p, GetControlPoint(m_nDegU * i + p, nullptr), GetControlWeight(m_nDegU * i + p, nullptr)) ; + } + pCrvCompo->AddCurve( Release( pCrvBz2)) ; + } + } + break ; + } + case 3 : { + if ( bLineOrBezier) + pCrvCompo = m_mCCEdge[nEdge][0]->Clone() ; + else { + // edge 3, scorro sulle patch in V + for ( int i = 0 ; i < m_nSpanV ; ++i) { + PtrOwner pCrvBz3( CreateBasicCurveBezier()) ; + if ( IsNull(pCrvBz3) || ! pCrvBz3->Init( m_nDegV, m_bRat)) + return nullptr ; + for ( int p = 0 ; p < m_nDegV + 1 ; ++p ) { + int nIndex = ( m_nDegU * m_nSpanU + 1) * ( i + p + 1) - 1 ; + if ( ! m_bRat) + pCrvBz3->SetControlPoint( p, GetControlPoint( nIndex, nullptr)) ; + else + pCrvBz3->SetControlPoint( p, GetControlPoint( nIndex, nullptr), GetControlWeight( nIndex, nullptr)) ; + } + pCrvCompo->AddCurve( Release( pCrvBz3)) ; + } + } + break ; + } + } + return pCrvCompo ; +} \ No newline at end of file diff --git a/SurfBezier.h b/SurfBezier.h index 34b1e7f..66dd4a5 100644 --- a/SurfBezier.h +++ b/SurfBezier.h @@ -19,9 +19,12 @@ #include "CurveComposite.h" #include "SurfTriMesh.h" #include "SurfFlatRegion.h" +//#include "Tree.h" #include "/EgtDev/Include/EGkSurfBezier.h" #include "/EgtDev/Include/EGkGeoCollection.h" +using namespace std ; +class Tree ; //---------------------------------------------------------------------------- class SurfBezier : public ISurfBezier, public IGeoObjRW @@ -87,7 +90,7 @@ class SurfBezier : public ISurfBezier, public IGeoObjRW bool SetControlPoint( int nIndU, int nIndV, const Point3d& ptCtrl, double dW) override { return SetControlPoint( GetInd( nIndU, nIndV), ptCtrl, dW) ; } bool SetControlPoint( int nInd, const Point3d& ptCtrl, double dW) override ; - bool SetTrimRegion( const ISurfFlatRegion& sfrTrimReg) override ; + bool SetTrimRegion( ISurfFlatRegion& sfrTrimReg, bool bIntersectOrSubtract = true) override ; SurfFlatRegion* GetTrimRegion( void) const override ; bool GetInfo( int& nDegU, int& nDegV, int& nSpanU, int& nSpanV, bool& bIsRat, bool& bTrimmed) const override ; const Point3d& GetControlPoint( int nIndU, int nIndV, bool* pbOk) const override @@ -111,7 +114,30 @@ class SurfBezier : public ISurfBezier, public IGeoObjRW bool GetControlCurveOnU( int nIndV, PolyLine& plCtrlU) const override ; bool GetControlCurveOnV( int nIndU, PolyLine& plCtrlV) const override ; const SurfTriMesh* GetAuxSurf( void) const override ; + // funzione per ottenere la suddivisione dello spazio parametrico nelle celle utilizzate per la triangolazione. bool GetLeaves( std::vector>& vLeaves) const override ; + bool GetTriangles2D( std::vector>& vTria2D) const override ; + // funzioni che servono per ricavare l'immagine nel parametrico di un punto appartenente alla trimesh ausiliaria della superficie di Bezier + // a nIL si può passare 5 come valore di default + bool UnprojectPointFromStm( int nT, const Point3d& ptI, Point3d& ptSP, int nIL = 5) const override ; + bool UnprojectPointFromStm( int nT, const Point3d& ptI, Point3d& ptSP, int nIL, const Point3d& ptIPrev, bool* bTroughEdge = nullptr) const override ; + // restituisce il corrispettivo parametrico di un punto qualunque della trimesh associata alla superficie + // ptIPrev è un punto addizionale che precede o segue il punto pt3D nel caso in cui il punto faccia parte di una curva 3d sulla superficie + // pPlCut è il piano di taglio su cui dovrebbe giacere il punto raffinato + bool UnprojectPoint( const Point3d& pt3D, Point3d& ptParam, const Point3d& ptIPrev, bool* bTroughEdge = nullptr, const Plane3d* plCut = nullptr) const override ; + // pPlCut è il piano di taglio su cui giace la curva + bool UnprojectCurveFromStm( const ICurveComposite* pCC, ICRVCOMPOPVECTOR& vpCC, const Plane3d* pPlCut) const override ; + // funzione per tagliare una superficie di bezier con un piano ( cancello la parte dal lato positivo della normale del piano). + // bSaveOnEq indica se tenere i triangoli (della trimesh associata) che sono sul piano + bool Cut( const Plane3d& plPlane, bool bSaveOnEq = false) override ; + // funzione che calcola se gli edge sono collassati in poli. DEVE ESSERE STATA CHIAMATA PRIMA DI UN CUT + bool CalcPoles( void) override ; + // funzioni per incrementare le coordinate restando dentro lo spazio parametrico + bool IncreaseUV( double& dU, double dx, bool bUOrV, double* dUVCopy = nullptr, bool bModifyOrig = true) const override ; + bool IncreaseUV( Point3d& ptUV, Vector3d vtH , Point3d* ptUVCopy, bool bModifyOrig) const override ; + // funzione che restituisce gli edge della superficie o in forma di linea spezzata o in forma di curva di Bezier + // se la superficie è trimmata restituisce i loop dello spazio parametrico in forma di linee spezzate + bool GetLoops( ICRVCOMPOPOVECTOR& vCC, bool bLineOrBezier, int nEdge = -1) const override ; public : // IGeoObjRW int GetNgeId( void) const override ; @@ -162,6 +188,11 @@ class SurfBezier : public ISurfBezier, public IGeoObjRW bool GetCurveOnV( double dU, int nStep, PolyLine& plCrvV) const ; double GetCurveOnUApproxLen( double dV) const ; double GetCurveOnVApproxLen( double dU) const ; + // funzione che proietta nello spazio parametrico un trim derivante da un taglio con un piano, categorizzandolo come aperto o chiuso ( nel parametrico) + bool AddCurveCompoToCuts( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vpCCOpen, ICRVCOMPOPOVECTOR& vpCCClosed, double dToler = EPS_SMALL, const Plane3d* pPlCut = nullptr) const ; + // restituisce il singolo edge della superficie non trimmata + ICurveComposite* GetSingleEdge3D( bool bLineOrBezier, int nEdge) const ; + bool UpdateEdgesFromTree( Tree& tr) const ; private : ObjGraphicsMgr m_OGrMgr ; // gestore grafica dell'oggetto @@ -173,11 +204,16 @@ class SurfBezier : public ISurfBezier, public IGeoObjRW int m_nSpanV ; // numero di pezze in V bool m_bRat ; // flag di razionale/polinomiale bool m_bTrimmed ; // flag per presenza regione di trim + mutable bool m_bClosedU ; // flag che indica se la superficie è chiusa lungo il parametro U + mutable bool m_bClosedV ; // flag che indica se la superficie è chiusa lungo il parametro V + mutable BOOLVECTOR m_vbPole ; // vettore di flag che indicano se i lati sono collassati in dei poli PNTVECTOR m_vPtCtrl ; // vettore dei punti di controllo DBLVECTOR m_vWeCtrl ; // vettore dei pesi di controllo SurfFlatRegion* m_pTrimReg ; // eventuale regione di trim int m_nTempProp[2] ; // vettore proprietà temporanee double m_dTempParam[2] ; // vettore parametri temporanei + mutable vector m_mCCEdge ;// vettore dei vettori che contengono le curve compo degli edge della superficie nello spazio 3D + mutable ICRVCOMPOPOVECTOR m_vCCLoop ; // vettore dei loop della superficie trimmata } ; //----------------------------------------------------------------------------- diff --git a/Tree.cpp b/Tree.cpp index 9bdfaca..12568fd 100644 --- a/Tree.cpp +++ b/Tree.cpp @@ -30,7 +30,8 @@ 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_bSplitPatches( true), m_bTestMode( false) + : 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) ; @@ -39,7 +40,8 @@ Tree::Tree( void) //---------------------------------------------------------------------------- Tree::Tree( const SurfBezier* pSrfBz, const bool bSplitPatches, const Point3d& ptMin, const Point3d& ptMax) - : m_pSrfBz( nullptr), m_bTrimmed( false), m_bBilinear( false), m_bMulti( false), m_bClosedU( false), m_bClosedV( false), m_bSplitPatches( true), m_bTestMode( false) + : 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) { SetSurf( pSrfBz, bSplitPatches, ptMin, ptMax) ; } @@ -49,6 +51,15 @@ 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)) ; +} + //---------------------------------------------------------------------------- void Tree::SetSurf( const SurfBezier* pSrfBz, bool bSplitPatches, const Point3d& ptMin, const Point3d& ptMax) @@ -62,6 +73,7 @@ Tree::SetSurf( const SurfBezier* pSrfBz, bool bSplitPatches, const Point3d& ptMi m_vPlApprox.clear() ; m_vChunk.clear() ; m_vPolygons.clear() ; + m_vPlLoop2D.clear() ; m_pSrfBz = pSrfBz ; m_bSplitPatches = bSplitPatches ; @@ -219,16 +231,18 @@ Tree::SetSurf( const SurfBezier* pSrfBz, bool bSplitPatches, const Point3d& ptMi // 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 bCapped0 = true, bCapped1 = true ; - Point3d ptV0, ptV1 ; - // controllo se tutti i punti sull'isoparametrica sono uguali + 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) { - ptV0 = m_pSrfBz->GetControlPoint( i * ( nDegU * nSpanU + 1), &bOk) ; - bCapped0 = bCapped0 && AreSamePointApprox( ptP00, ptV0) ; - ptV1 = m_pSrfBz->GetControlPoint( ( i + 1) * ( nDegU * nSpanU + 1) - 1, &bOk) ; - bCapped1 = bCapped1 && AreSamePointApprox( ptP10, ptV1) ; + 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) ; } - if ( bCapped0 && bCapped1) { + m_vbPole[1] = bPole0 ; + m_vbPole[3] = bPole1 ; + if ( bPole0 && bPole1) { m_mTree[0].SetSplitDirVert( true) ; Split( 0) ; m_mTree[1].SetSplitDirVert( true) ; @@ -248,16 +262,18 @@ Tree::SetSurf( const SurfBezier* pSrfBz, bool bSplitPatches, const Point3d& ptMi // 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 bCapped0 = true, bCapped1 = true ; - Point3d ptU0, ptU1 ; + 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) { - ptU0 = m_pSrfBz->GetControlPoint( i, &bOk) ; - bCapped0 = bCapped0 && AreSamePointApprox( ptP00, ptU0) ; - ptU1 = m_pSrfBz->GetControlPoint( i + ( nDegU * nSpanU + 1) * ( nDegV * nSpanV), &bOk) ; - bCapped1 = bCapped1 && AreSamePointApprox( ptP01, ptU1) ; + 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) ; } - if ( bCapped0 && bCapped1) { + m_vbPole[0] = bPole0 ; + m_vbPole[2] = bPole1 ; + if ( bPole0 && bPole1) { m_mTree[0].SetSplitDirVert( false) ; Split( 0) ; m_mTree[1].SetSplitDirVert( false) ; @@ -931,6 +947,7 @@ Tree::Balance() void Tree::GetTopNeigh( int nId, INTVECTOR& vTopNeighs) const { + // le celle restituite sono ordinate per x crescente if ( vTopNeighs.empty()) { if ( m_mTree.at( nId).m_nTop == -2) return ; @@ -1000,6 +1017,7 @@ Tree::GetTopNeigh( int nId, INTVECTOR& vTopNeighs) const vector vCells ; for ( int k : vTopNeighs) vCells.push_back( m_mTree.at( k)) ; + // le celle restituite sono ordinate per x crescente sort( vCells.begin(), vCells.end(), Cell::minorX) ; vTopNeighs.clear() ; for ( Cell c : vCells) @@ -1011,6 +1029,7 @@ Tree::GetTopNeigh( int nId, INTVECTOR& vTopNeighs) const void Tree::GetBottomNeigh( int nId, INTVECTOR& vBottomNeighs) const { + // le celle restituite sono ordinate per x crescente if ( vBottomNeighs.empty()) { if ( m_mTree.at( nId).m_nBottom == -2) return ; @@ -1080,6 +1099,7 @@ Tree::GetBottomNeigh( int nId, INTVECTOR& vBottomNeighs) const vector vCells ; for ( int k : vBottomNeighs) vCells.push_back( m_mTree.at( k)) ; + // le celle restituite sono ordinate per x crescente sort( vCells.begin(), vCells.end(), Cell::minorX) ; vBottomNeighs.clear() ; for ( Cell c : vCells) @@ -1090,6 +1110,7 @@ Tree::GetBottomNeigh( int nId, INTVECTOR& vBottomNeighs) const void Tree::GetLeftNeigh( int nId, INTVECTOR& vLeftNeighs) const { + // le celle restituite sono ordinate per y crescente if ( vLeftNeighs.empty()) { if ( m_mTree.at( nId).m_nLeft == -2) return ; @@ -1159,6 +1180,7 @@ Tree::GetLeftNeigh( int nId, INTVECTOR& vLeftNeighs) const vector vCells ; for ( int k : vLeftNeighs) vCells.push_back( m_mTree.at( k)) ; + // le celle restituite sono ordinate per y crescente sort( vCells.begin(), vCells.end(), Cell::minorY) ; vLeftNeighs.clear() ; for ( Cell c : vCells) @@ -1169,6 +1191,7 @@ Tree::GetLeftNeigh( int nId, INTVECTOR& vLeftNeighs) const void Tree::GetRightNeigh( int nId, INTVECTOR& vRightNeighs) const { + // le celle restituite sono ordinate per y crescente if ( vRightNeighs.empty()) { if ( m_mTree.at( nId).m_nRight == -2) return ; @@ -1238,6 +1261,7 @@ Tree::GetRightNeigh( int nId, INTVECTOR& vRightNeighs) const vector vCells ; for ( int k : vRightNeighs) vCells.push_back( m_mTree.at( k)) ; + // le celle restituite sono ordinate per y crescente sort( vCells.begin(), vCells.end(), Cell::minorY) ; vRightNeighs.clear() ; for ( Cell c : vCells) @@ -1372,6 +1396,12 @@ Tree::GetPolygons( POLYLINEMATRIX& vPolygons) else { POLYLINEVECTOR vPolygonsBasic ; GetPolygonsBasic( vPolygonsBasic) ; + // aggiungo 4 elementi al vettore che contiene ciò che resta degli edge dopo il trim + 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 @@ -1418,9 +1448,17 @@ Tree::GetPolygons( POLYLINEMATRIX& vPolygons) //---------------------------------------------------------------------------- bool -Tree::GetPolygonsBasic( POLYLINEVECTOR& vPolygons) +Tree::GetPolygonsBasic( POLYLINEVECTOR& vPolygons, INTVECTOR vCells) { - if ( m_vPolygons.empty()) { + // condizioni per il calcolo dei poligoni di base + if ( m_vPolygons.empty() || // se non li ho mai calcolati + ( ! m_vPolygons.empty() && ! vCells.empty()) || // se ho già calcolato dei poligoni ma ne sto chiedendo di un altro gruppo di celle + ( vCells.empty() && m_vPolygons.size() != m_vnLeaves.size())) { // se sto chiedendo i poligoni di tutte le celle, ma i poligoni già calcolati sono meno delle celle + m_vPolygons.clear() ; + // se non ho dato un elenco di celle in input do per scontato che la chiamata sia per calcolare i poligoni di tutte le foglie + if ( vCells.empty()) + vCells = m_vnLeaves ; + PNTVECTOR vVertices ; INTVECTOR vNeigh ; // setto le celle che sono sul LeftEdge e sul TopEdge @@ -1440,7 +1478,8 @@ Tree::GetPolygonsBasic( POLYLINEVECTOR& vPolygons) 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 - for ( int nId : m_vnLeaves) { + // N.B. :i poligoni sono costruiti a partire dal ptBL !!! + for ( int nId : vCells) { vVertices.clear() ; vNeigh.clear() ; vVertices.push_back( m_mTree.at( nId).GetBottomLeft()) ; @@ -1776,6 +1815,40 @@ Tree::FindCell( const Point3d& ptToAssign, const CurveLine& cl, INTVECTOR vCells return nCells ; } +//---------------------------------------------------------------------------- +bool +Tree::UpdateSplitLoop( PolyLine& pl, int& nCount, Point3d& pt) +{ + Point3d ptLast ; + pl.GetLastPoint( ptLast) ; + if ( pl.AddUPoint( nCount, pt)) + ++ nCount ; + if ( pl.GetPointNbr() != 1 ) { + 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) @@ -1788,6 +1861,9 @@ Tree::TraceLoopLabelCell( const POLYLINEVECTOR& vplPolygons) // 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 polyline che aggiunge alla polyline originale degli split dove interseca le celle ( serve per ricostruire gli edge aperti della superficie) + PolyLine plLoopSplit ; + int nPtLoopSplit = 0 ; // controllo se il loop è CCW o CW bool bCCW = get<1>( m_vPlApprox[i]) ; // trovo in quale cella è il ptStart @@ -1799,6 +1875,8 @@ Tree::TraceLoopLabelCell( const POLYLINEVECTOR& vplPolygons) advance( ptSecond, 1) ; CurveLine clFirst ; clFirst.Set( ptFirst->first, ptSecond->first) ; + // aggiorno la polyline splittata + UpdateSplitLoop( plLoopSplit, nPtLoopSplit, ptFirst->first) ; // individuo la cella da cui parte il loop INTVECTOR nCells = FindCell( ptStart, clFirst) ; int nId ; @@ -1863,9 +1941,13 @@ Tree::TraceLoopLabelCell( const POLYLINEVECTOR& vplPolygons) m_mTree[nId].m_vInters.back().bCCW = bCCW ; // salvo il chunk del loop m_mTree[nId].m_vInters.back().nChunk = m_mChunk[i] ; + // aggiorno la polyline splittata + UpdateSplitLoop( plLoopSplit, nPtLoopSplit, vptInters.back()) ; } // aggiungo la fine del segmento nel vettore delle intersezioni vptInters.push_back( ptCurr) ; + // aggiorno la polyline splittata + UpdateSplitLoop( plLoopSplit, nPtLoopSplit, ptCurr) ; } if ( nId == nFirstCell) vptInters.pop_back() ; @@ -1914,6 +1996,8 @@ Tree::TraceLoopLabelCell( const POLYLINEVECTOR& vplPolygons) m_mTree[nId].m_vInters[nPass].nOut = nOut ; } } + // salvo la polyline splittata + m_vPlLoop2D.emplace_back( plLoopSplit) ; } // riordino i vettori di intersezione per ogni cella e setto il flag RightEdgeIn @@ -3521,3 +3605,275 @@ Tree::OnWhichEdge( int nId, const Point3d& ptToAssign, int& nEdge) const return false ; return true ; } + +//---------------------------------------------------------------------------- +bool +Tree::GetEdges3D( POLYLINEMATRIX& mPLEdges) +{ + // 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 + reverse( vEdges[0].begin(), vEdges[0].end()) ; + vEdges.emplace_back() ; + GetRootNeigh( 1, vEdges[1]) ; + reverse( vEdges[1].begin(), vEdges[1].end()) ; + vEdges.emplace_back() ; + GetRootNeigh( 2, vEdges[2]) ; + vEdges.emplace_back() ; + GetRootNeigh( 3, vEdges[3]) ; + + // recupero i poligoni base delle celle sui bordi + POLYLINEMATRIX mPL ; + mPL.emplace_back() ; + GetPolygonsBasic( mPL[0], vEdges[0]) ; + mPL.emplace_back() ; + GetPolygonsBasic( mPL[1], vEdges[1]) ; + mPL.emplace_back() ; + GetPolygonsBasic( mPL[2], vEdges[2]) ; + mPL.emplace_back() ; + GetPolygonsBasic( mPL[3], vEdges[3]) ; + + // scorro sui gruppi di polyline che rappresentano i poligoni delle celle lungo un lato + for ( int i = 0 ; i < int( mPL.size()) ; ++i) { + mPLEdges.emplace_back() ; + mPLEdges.back().emplace_back() ; + int nPtCount = 0 ; + // scorro sui poligoni delle celle di un lato + for ( int c = 0 ; c < int( mPL[i].size()) ; ++c) { + Point3d pt ; mPL[i][c].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 ( ! AreSamePointApprox(pt, m_mTree.at(vEdges[0][c]).GetTopRight()) && mPL[i][c].GetNextPoint( pt)) { + continue ; + } + mPLEdges.back().back().AddUPoint( nPtCount, m_mVert.at(vEdges[0][c])[2]) ; + ++ nPtCount ; + // scorro fino alla fine di quel lato + while ( mPL[i][c].GetNextPoint( pt) && ! AreSamePointApprox(pt, m_mTree.at(vEdges[0][c]).GetTopLeft())) { + m_pSrfBz->GetPointD1D2( pt.x / SBZ_TREG_COEFF, pt.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt3d) ; + mPLEdges.back().back().AddUPoint( nPtCount, pt3d) ; + ++ nPtCount ; + } + mPLEdges.back().back().AddUPoint( nPtCount, m_mVert.at(vEdges[0][c])[3]) ; + ++ nPtCount ; + } + else if ( i == 1 ) { + while ( ! AreSamePointApprox(pt, m_mTree.at(vEdges[1][c]).GetTopLeft()) && mPL[i][c].GetNextPoint( pt)) { + continue ; + } + mPLEdges.back().back().AddUPoint( nPtCount, m_mVert.at(vEdges[1][c])[3]) ; + ++ nPtCount ; + // scorro fino alla fine di quel lato + while ( mPL[i][c].GetNextPoint( pt) && ! AreSamePointApprox(pt, m_mTree.at(vEdges[1][c]).GetBottomLeft())) { + m_pSrfBz->GetPointD1D2( pt.x / SBZ_TREG_COEFF, pt.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt3d) ; + mPLEdges.back().back().AddUPoint( nPtCount, pt3d) ; + ++ nPtCount ; + } + mPLEdges.back().back().AddUPoint( nPtCount, m_mVert.at(vEdges[1][c])[0]) ; + ++ nPtCount ; + } + else if ( i == 2) { + while ( ! AreSamePointApprox(pt, m_mTree.at(vEdges[2][c]).GetBottomLeft()) && mPL[i][c].GetNextPoint( pt)) { + continue ; + } + mPLEdges.back().back().AddUPoint( nPtCount, m_mVert.at(vEdges[2][c])[0]) ; + ++ nPtCount ; + // scorro fino alla fine di quel lato + while ( mPL[i][c].GetNextPoint( pt) && ! AreSamePointApprox(pt, m_mTree.at(vEdges[2][c]).GetBottomRight())) { + m_pSrfBz->GetPointD1D2( pt.x / SBZ_TREG_COEFF, pt.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt3d) ; + mPLEdges.back().back().AddUPoint( nPtCount, pt3d) ; + ++ nPtCount ; + } + mPLEdges.back().back().AddUPoint( nPtCount, m_mVert.at(vEdges[2][c])[1]) ; + ++ nPtCount ; + } + else if ( i == 3) { + while ( ! AreSamePointApprox(pt, m_mTree.at(vEdges[3][c]).GetBottomRight()) && mPL[i][c].GetNextPoint( pt)) { + continue ; + } + mPLEdges.back().back().AddUPoint( nPtCount, m_mVert.at(vEdges[3][c])[1]) ; + ++ nPtCount ; + // scorro fino alla fine di quel lato + while ( mPL[i][c].GetNextPoint( pt) && ! AreSamePointApprox(pt, m_mTree.at(vEdges[3][c]).GetTopRight())) { + m_pSrfBz->GetPointD1D2( pt.x / SBZ_TREG_COEFF, pt.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt3d) ; + mPLEdges.back().back().AddUPoint( nPtCount, pt3d) ; + ++ nPtCount ; + } + mPLEdges.back().back().AddUPoint( nPtCount, m_mVert.at(vEdges[3][c])[2]) ; + ++ nPtCount ; + } + } + } + } + // 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) { + mPLEdges.emplace_back() ; + INTVECTOR vId ; + Point3d ptNear = m_mTree.at(-1).GetBottomLeft() ; + while( m_vCEdge2D[i].second.GetChainFromNear(ptNear, false, vId) ) { + PolyLine pl3D ; + int nInd = abs( vId[0]) - 1 ; + Point3d pt2D = m_vCEdge2D[i].first[nInd].first ; + Point3d pt3D ; m_pSrfBz->GetPointD1D2( pt2D.x / SBZ_TREG_COEFF, pt2D.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt3D) ; + pl3D.AddUPoint( 0, pt3D) ; + int nCount = 1 ; + for ( int j = 1 ; j < int( vId.size()) ; ++j) { + nInd = abs( vId[j]) - 1 ; + pt2D = m_vCEdge2D[i].first[nInd].second ; + m_pSrfBz->GetPointD1D2( pt2D.x / SBZ_TREG_COEFF, pt2D.y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt3D) ; + if ( pl3D.AddUPoint( nCount, pt3D)) + ++ nCount ; + } + // qui devo fare dei controlli prima di aggiungere questa polyline? + mPLEdges[i].emplace_back( pl3D) ; + } + } + } + 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 pCL( CreateCurveLine()) ; pCL->SetPVL( ptSecond, pt - ptSecond, 1e6) ; + PtrOwner pCL0( CreateCurveLine()) ; pCL0->Set( m_mTree.at(-1).GetTopRight(), m_mTree.at(-1).GetTopLeft()) ; + PtrOwner pCL1( CreateCurveLine()) ; pCL1->Set( m_mTree.at(-1).GetTopLeft(), m_mTree.at(-1).GetBottomLeft()) ; + PtrOwner pCL2( CreateCurveLine()) ; pCL2->Set( m_mTree.at(-1).GetBottomLeft(), m_mTree.at(-1).GetBottomRight()) ; + PtrOwner 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 pCL( CreateCurveLine()) ; pCL->SetPVL( ptSecondToLast, pt - ptSecondToLast, 1e6) ; + PtrOwner pCL0( CreateCurveLine()) ; pCL0->Set( m_mTree.at(-1).GetTopRight(), m_mTree.at(-1).GetTopLeft()) ; + PtrOwner pCL1( CreateCurveLine()) ; pCL1->Set( m_mTree.at(-1).GetTopLeft(), m_mTree.at(-1).GetBottomLeft()) ; + PtrOwner pCL2( CreateCurveLine()) ; pCL2->Set( m_mTree.at(-1).GetBottomLeft(), m_mTree.at(-1).GetBottomRight()) ; + PtrOwner 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) +{ + 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 + 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) { + 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()) ; + generate_n( vToCheck.begin(), (int) m_mTree.at(nRoot).m_vInters.size(), generator()) ; + 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() ; + // ora posso creare il poligono della cella con i tagli + while( (int)vToCheck.size() != 0) { + int nPolyBefore = nPoly ; + CreateCellPolygons( 0, vPolygons, vToCheck, nPoly, vnParentChunk, pl) ; + if ( nPolyBefore == nPoly) + break ; + } + return true ; +} \ No newline at end of file diff --git a/Tree.h b/Tree.h index ea60bb6..8c45bc7 100644 --- a/Tree.h +++ b/Tree.h @@ -18,6 +18,7 @@ #include "GeoConst.h" #include "CurveLine.h" #include "/EgtDev/Include/EGkPolyLine.h" +#include "/EgtDev/Include/EGkChainCurves.h" #include //---------------------------------------------------------------------------- @@ -27,11 +28,12 @@ struct Inters { int nOut ; bool bCCW ; int nChunk ; + bool bSortedbyStart ; // riordino le intersezioni per lato in senso antiorario dal top - // se ho pi� intersezioni che entrano in un lato le riordino considerando che percorro i lati in senso antiorario a partire da ptTR + // se ho più intersezioni che entrano in un lato le riordino considerando che percorro i lati in senso antiorario a partire da ptTR bool operator < ( Inters& b) { - // trovo in che ordine stanno i due strat, tenendo conto anche della possibilit� che siano vertici + // trovo in che ordine stanno i due start, tenendo conto anche della possibilità che siano vertici INTVECTOR vEdges = { 7, 0, 4, 1, 5, 2, 6, 3} ; const auto iter1 = find( vEdges.begin(), vEdges.end(), nIn) ; int nPos1 = std::distance( vEdges.begin(), iter1) ; @@ -52,7 +54,7 @@ struct Inters { pl.Close() ; pl.GetAreaXY( dAreaB) ; } - // se nIn � un vertice sistemo il valore + // se nIn è un vertice sistemo il valore int nEdgeIn = nIn ; if ( nIn > 3) nEdgeIn = nIn - 4 ; @@ -63,6 +65,67 @@ struct Inters { ( bEqIn && nEdgeIn == 2 && vpt[0].x < b.vpt[0].x) || ( bEqIn && nEdgeIn == 3 && vpt[0].y < b.vpt[0].y)) ; } + + static bool FirstEncounter ( Inters& a, Inters& b) + { + // riordino in base al lato toccato, o dall'uscita o dall'ingresso, che viene prima. + // ottengo l'ordine che avrei percorrendo il bordo da ptTR e considerando i loop che incontro, indipendentemente se li incontro nel punto di uscita o ingresso + // nell'intersezione salvo se il taglio è stato ordinato guardando l'ingresso o l'uscita + INTVECTOR vEdges = { 7, 0, 4, 1, 5, 2, 6, 3} ; + // trovo i lati di ingresso e uscita + const auto iter1 = find( vEdges.begin(), vEdges.end(), a.nIn) ; + int nPos1 = std::distance( vEdges.begin(), iter1) ; + const auto iter2 = find( vEdges.begin(), vEdges.end(), a.nOut) ; + int nPos2 = std::distance( vEdges.begin(), iter2) ; + const auto iter3 = find( vEdges.begin(), vEdges.end(), b.nIn) ; + int nPos3 = std::distance( vEdges.begin(), iter3) ; + const auto iter4 = find( vEdges.begin(), vEdges.end(), b.nOut) ; + int nPos4 = std::distance( vEdges.begin(), iter4) ; + int nFirstA = 0 ; + int nFirstB = 0 ; + // salvo l'indice del primo punto dell'intersezione che ho incontrato scorrendo il bordo da ptTR + // salvo il lato che viene prima confrontando ingresso e uscita + if ( nPos2 < nPos1) { + nPos1 = nPos2 ; + nFirstA = int( a.vpt.size()) - 1 ; + } + // se ingresso e uscita sono sullo stesso lato allora confronto le coordinate per capire se viene prima l'ingresso o l'uscita + else if ( nPos2 == nPos1 ) { + if ( nPos1 == 0 ) + nFirstA = a.vpt[0].x > a.vpt.back().x ? 0 : ( int( a.vpt.size()) - 1) ; + else if ( nPos1 == 1 ) + nFirstA = a.vpt[0].y > a.vpt.back().y ? 0 : ( int( a.vpt.size()) - 1) ; + else if ( nPos1 == 2 ) + nFirstA = a.vpt[0].x < a.vpt.back().x ? 0 : ( int( a.vpt.size()) - 1) ; + else if ( nPos1 == 3 ) + nFirstA = a.vpt[0].y < a.vpt.back().y ? 0 : ( int( a.vpt.size()) - 1) ; + } + if ( nPos4 < nPos3) { + nPos3 = nPos4 ; + nFirstB = int( b.vpt.size()) - 1 ; + } + else if ( nPos4 == nPos3 ) { + if ( nPos3 == 0 ) + nFirstB = b.vpt[0].x > b.vpt.back().x ? 0 : ( int( b.vpt.size()) - 1) ; + else if ( nPos3 == 1 ) + nFirstB = b.vpt[0].y > b.vpt.back().y ? 0 : ( int( b.vpt.size()) - 1) ; + else if ( nPos3 == 2 ) + nFirstB = b.vpt[0].x < b.vpt.back().x ? 0 : ( int( b.vpt.size()) - 1) ; + else if ( nPos3 == 3 ) + nFirstB = b.vpt[0].y < b.vpt.back().y ? 0 : ( int( b.vpt.size()) - 1) ; + } + a.bSortedbyStart = nFirstA == 0 ; + b.bSortedbyStart = nFirstB == 0 ; + // se sono diversi ritorno il confronto + if ( nPos1 != nPos3) + return nPos1 < nPos3 ; + // se sono uguali devo valutare il punto di intersezione + return ( nPos1 == 0 && a.vpt[nFirstA].x > b.vpt[nFirstB].x) || + ( nPos1 == 1 && a.vpt[nFirstA].y > b.vpt[nFirstB].y) || + ( nPos1 == 2 && a.vpt[nFirstA].x < b.vpt[nFirstB].x) || + ( nPos1 == 3 && a.vpt[nFirstA].y < b.vpt[nFirstB].y) ; + } + bool operator == ( Inters& b) { return AreSamePointExact( vpt[0], b.vpt[0]) ; @@ -73,8 +136,8 @@ struct Inters { } } ; // nIn e nOut sono flag che indicano da quale lato ho l'ingresso e l'uscita a partire dal lato top in senso antiorario -// oltre il 3 sono le celle adiacenti in diagonale al vertice-> 4 corrisponde al ptTl e da l� in senso antiorario -// -1 se la curva � sempre dentro la cella +// oltre il 3 sono le celle adiacenti in diagonale al vertice-> 4 corrisponde al ptTl e da lì in senso antiorario +// -1 se la curva è sempre dentro la cella //---------------------------------------------------------------------------- class Cell @@ -116,6 +179,10 @@ class Cell { return m_ptPbl ; } Point3d GetTopRight( void) const { return m_ptPtr ; } + Point3d GetTopLeft( void) const + { return Point3d( m_ptPbl.x, m_ptPtr.y) ; } + Point3d GetBottomRight( void) const + { return Point3d( m_ptPtr.x, m_ptPbl.y); } double GetSplitValue( void) const { return m_dSplit ; } bool IsSplitVert( void) const // se true la cella verrebbe splittata verticalmente, senn� orizzontalmente @@ -138,24 +205,24 @@ class Cell int m_nLeft ; // cella adiacente al lato left int m_nRight ; // cella adiacente al lato right int m_nParent ; // cella genitore - int m_nDepth ; // profondit� della cella rispetto a root - double m_dSplit ; // parametro a cui � stata splittata la cella + int m_nDepth ; // profondità della cella rispetto a root + double m_dSplit ; // parametro a cui è stata splittata la cella int m_nChild1 ; // prima cella figlio int m_nChild2 ; // seconda cella figlio int m_nFlag ; // falg che indica la caratterizzazione della cella rispetto ai loop di trim // 0 esterna, 1 intersecata, 2 contiene un loop, 3 intersecata e contenente un loop, 4 contenuta in un loop - int m_nFlag2 ; // falg che indica se la cella � stata attraversata durante l'ultima fase del labelling - int m_nRightEdgeIn ; // 0 right edge fuori, 1 right edge dentro, 2 met� e met� - bool m_bOnLeftEdge ; // flag che indica se la cella � sul lato sinistro ( per superfici chiuse sul parametro U) - bool m_bOnTopEdge ; // flag che indica se la cella � sul lato top ( per superfici chiuse sul parametro V) + int m_nFlag2 ; // falg che indica se la cella è stata attraversata durante l'ultima fase del labelling + int m_nRightEdgeIn ; // 0 right edge fuori, 1 right edge dentro, 2 metà e metà + bool m_bOnLeftEdge ; // flag che indica se la cella è sul lato sinistro ( per superfici chiuse sul parametro U) + bool m_bOnTopEdge ; // flag che indica se la cella è sul lato top ( per superfici chiuse sul parametro V) std::vector m_vInters ; // vettore delle intersezioni della cella con i loop di trim - // ogni elemento del vettore � l'insieme dei punti che caratterizza un atrtaversamento della cella + // ogni elemento del vettore è l'insieme dei punti che caratterizza un attraversamento della cella private : Point3d m_ptPbl ; // punto bottom left Point3d m_ptPtr ; // punto top right - bool m_bProcessed ; // flag che indica se la cella � stata processata - bool m_bSplitVert ; // flag che indica in quale direzione � stata divisa la cella + bool m_bProcessed ; // flag che indica se la cella è stata processata + bool m_bSplitVert ; // flag che indica in quale direzione è stata divisa la cella } ; //---------------------------------------------------------------------------- @@ -165,16 +232,26 @@ class Tree ~Tree( void) ; Tree( void) ; Tree ( const SurfBezier* pSrfBz, bool bSplitPatches = true, const Point3d& ptMin = ORIG, const Point3d& ptMax = ORIG) ; + Tree( const Point3d ptBl, const Point3d ptTr) ; // creatore da usare solo nel caso in cui si voglia aggiungere tagli ad un'unica cella e del risultato ottenere il contorno void SetSurf( const SurfBezier* pSrfBz, bool bSplitPatches = true, const Point3d& ptMin = ORIG, const Point3d& ptMax = ORIG) ; bool GetIndependentTrees( BIPNTVECTOR& vTrees) ; // calcolo la suddivisione della superficie solo sulle singole bbox dei loop di trim ( unendo quelli vicini) bool BuildTree( double dLinTol = LIN_TOL_STD, double dSideMin = 1, double dSideMax = INFINITO) ; // dSideMax � il massimo per la dimensione maggiore di un triangolo della trimesh // dSideMin � lunghezza minima del lato di una cella nello spazio reale bool BuildTree_test( double dLinTol = LIN_TOL_STD, double dSideMin = 1, double dSideMax = INFINITO) ; bool GetPolygons( POLYLINEMATRIX& vPolygons) ; - bool GetPolygonsBasic( POLYLINEVECTOR& vPolygons) ; // restituisce il poligono corrispondente ad ogni cella foglia dell'albero + bool GetPolygonsBasic( POLYLINEVECTOR& vPolygons, INTVECTOR vCells = {}) ; // restituisce il poligono corrispondente ad ogni cella foglia dell'albero // ad ogni poligono sono stati aggiunti tutti i vertici dei vicini posizionati sui suoi lati bool GetLeaves ( std::vector& vLeaves) const ; // restituisce gli indici delle foglie nell'albero + bool GetEdges3D ( POLYLINEMATRIX& mPLEdges) ; // restituisce gli edge 3D come polyline + bool GetSplitLoops( POLYLINEVECTOR& vPl) const // funzione che restituisce i loop splitatti ai confini delle celle + { for ( int i = 0 ; i < int( m_vPlLoop2D.size()); ++i) vPl.emplace_back( m_vPlLoop2D[i]) ; return true ; }; void SetTestMode( void) { m_bTestMode = true ;} ; // attivando la test mode, per la costruzione dell'albero viene usata la funzione BuiltTree_test e viene corretta di conseguenza la FindCell + // funzioni da usare per ricostruire tagli che vanno aggiunti allo spazio parametrico + bool AddCutsToRoot( POLYLINEVECTOR& vCuts) ; // aggiunge i tagli al tree + bool CreateCellContour( POLYLINEMATRIX& vPolygons) ; // crea il nuovo contorno esterno, tenendo conto dei tagli + bool IsClosedU( void) const { return m_bClosedU ;} ; // funzione che riferisce se la superficie è chiusa lungo il parametro U + bool IsClosedV( void) const { return m_bClosedV ;} ; // funzione che riferisce se la superficie è chiusa lungo il parametro V + std::vector GetPoles( void) { return m_vbPole ;} ; // funzione che restituisce i flag che indicano se i lati sono collassati in dei poli private : bool Split( int nId, double dSplitValue) ; // funzione di split di una cella al parametro indicato nella direzione data da bVert @@ -205,6 +282,9 @@ class Tree bool CategorizeCell( int nId) ; // categorizza la cella in base al flag m_nFlag (dentro, fuori, intersecata) bool CheckIfBetween( const Inters& inA, const Inters& inB) const ; // / controllo se inB è compreso tra l'end e lo start di inA (in senso CCW) bool OnWhichEdge( int nId, const Point3d& ptToAssign, int& nEdge) const ; // indica a quale edge o vertice il punto è vicino entro EPS_SMALL + bool AdjustCuts( void) ; + bool UpdateSplitLoop( PolyLine& pl, int& nCount, Point3d& pt) ; + private : const SurfBezier* m_pSrfBz ; // superficie di bezier @@ -213,11 +293,12 @@ class Tree INTMATRIX m_vChunk ; // elenco dei loop divisi per chunk std::map m_mChunk ; // mappa in cui vengono salvati chunk di appartenza per ogni loop di trim ICURVEPOVECTOR m_vLoop ; // curve di loop - std::vector> m_vPlApprox ; // vettore contenente le approssimazioni dei loop + std::vector> m_vPlApprox ; // vettore contenente le approssimazioni dei loop // il bool indica se la curva è CCW bool m_bBilinear ; // superficie bilineare bool m_bMulti ; // superficie multi-patch bool m_bClosedU ; // superficie chiusa lungo il parametro U bool m_bClosedV ; // superficie chiusa lungo il parametro V + BOOLVECTOR m_vbPole ; // vettore che indica se i vari lati sono collassati in poli ( indici riferiti all'ordine degli edge) bool m_bSplitPatches ; // flag che indica se le patches sono state divise prima della creazione dell'albero int m_nDegU ; // grado della superficie nel parametro U int m_nDegV ; // grado della superficie nel parametro V @@ -229,4 +310,6 @@ class Tree INTVECTOR m_vnLeaves ; // vettore delle foglie INTVECTOR m_vnParents ; // vettore delle celle ottenute dalla divisione preliminare in singole patch bool m_bTestMode ; // bool che indica se la test mode è attiva + POLYLINEVECTOR m_vPlLoop2D ; // vettore che contiene le polyline che rappresentano i loop di trim tenendo conto della divisione in celle + std::vector> m_vCEdge2D ; // vettore che le chain che rappresentano ciò che resta degli edge originali, tenendo conto dei trim. } ; \ No newline at end of file