//---------------------------------------------------------------------------- // EgalTech 2020-2020 //---------------------------------------------------------------------------- // File : SurfBezier.cpp Data : 11.08.20 Versione : 2.2h2 // Contenuto : Implementazione della classe Superfici Bezier. // // // // Modifiche : 22.03.20 DS Creazione modulo. // 11.08.20 DS Trasformata in MultiPatch. // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "SurfBezier.h" #include "GeoObjFactory.h" #include "NgeWriter.h" #include "NgeReader.h" #include "Bernstein.h" #include "CurveArc.h" #include "CurveBezier.h" #include "CurveComposite.h" #include "Tree.h" #include "Triangulate.h" #include "SurfTriMesh.h" #include "/EgtDev/Include/EGkSfrCreate.h" #include "/EgtDev/Include/EGkStmFromTriangleSoup.h" #include "/EgtDev/Include/EGkStringUtils3d.h" #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/EGkIntersCurvePlane.h" #include "/EgtDev/Include/EGkDistPointSurfTm.h" #include "/EgtDev/Include/EGkDistPointLine.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EGkCurveComposite.h" #include "/EgtDev/Include/EGkCurveArc.h" #include "/EgtDev/Include/EGkGeoPoint3d.h" #include "/EgtDev/Include/EGkIntervals.h" #include "/EgtDev/Include/EGkSurfAux.h" #define EIGEN_NO_IO #include "/EgtDev/Extern/Eigen/Dense" #define SAVEFAILEDTRIANGULATION 0 #define SAVEREBUILTISO 0 #define SAVERULEDISO 0 #define SAVERULEDGUIDEDISO 0 #define SAVEMATCHCURVES 0 #define SAVEFAILEDTREE 0 #define SAVELIMITSURF 0 #define SAVEPACEDISO 0 #if SAVEFAILEDTRIANGULATION || SAVEREBUILTISO || SAVERULEDISO || SAVERULEDGUIDEDISO || SAVEMATCHCURVES \ || SAVEFAILEDTREE || SAVELIMITSURF || SAVEPACEDISO #include "/EgtDev/Include/EGkGeoObjSave.h" std::vector vGeo ; std::vector vCol ; #endif using namespace std ; //---------------------------------------------------------------------------- GEOOBJ_REGISTER( SRF_BEZIER, NGE_S_BEZ, SurfBezier) ; //---------------------------------------------------------------------------- struct PairHashIntInt { size_t operator()(const pair& key) const { size_t h1 = hash{}( key.first) ; size_t h2 = hash{}( key.second) ; return h1 ^ ( h2 << 1) ; // Combine hashes } }; static unordered_map s_mBernCache ; // mappa dei polinomi di bernstein //---------------------------------------------------------------------------- static double s_dAuxSurfTol = 200 * EPS_SMALL ; static double s_dAuxSurfRefinedTol = 10 * EPS_SMALL ; //---------------------------------------------------------------------------- void SetSurfBezierAuxSurfTol( double dTol) { s_dAuxSurfTol = max( dTol, EPS_SMALL) ; } //---------------------------------------------------------------------------- double GetSurfBezierAuxSurfTol( void) { return s_dAuxSurfTol ; } //---------------------------------------------------------------------------- void SetSurfBezierAuxSurfRefinedTol( double dTol) { s_dAuxSurfRefinedTol = max( dTol, EPS_SMALL) ; } //---------------------------------------------------------------------------- double GetSurfBezierAuxSurfRefinedTol( void) { return s_dAuxSurfRefinedTol ; } //---------------------------------------------------------------------------- SurfBezier::SurfBezier( void) : m_pSTM( nullptr), m_pSTMRefined( nullptr), m_nStatus( TO_VERIFY), m_nDegU(), m_nDegV(), m_nSpanU(), m_nSpanV(), m_bRat( false), m_bTrimmed( false), m_bClosedU( false), m_bClosedV( false), m_pTrimReg( nullptr), m_nTempProp{0,0}, m_dTempParam{0.0,0.0}, m_nIsPlanar( -1) { } //---------------------------------------------------------------------------- SurfBezier::~SurfBezier( void) { ResetTrimRegion() ; m_OGrMgr.Reset() ; ResetAuxSurf() ; } //---------------------------------------------------------------------------- bool SurfBezier::Init( int nDegU, int nDegV, int nSpanU, int nSpanV, bool bIsRational) { // verifico validità grado if ( nDegU < 1 || nDegU > MAXDEG || nDegV < 1 || nDegV > MAXDEG || nSpanU < 1 || nSpanV < 1) return false ; // imposto gradi e flag di razionale m_nDegU = nDegU ; m_nDegV = nDegV ; m_nSpanU = nSpanU ; m_nSpanV = nSpanV ; m_bRat = bIsRational ; m_bTrimmed = false ; ResetTrimRegion() ; // dimensiono i vettori dei punti e dei pesi m_vPtCtrl.assign( GetDim(), ORIG) ; if ( bIsRational) m_vWeCtrl.assign( GetDim(), 1) ; else m_vWeCtrl.clear() ; m_nStatus = TO_VERIFY ; m_nIsPlanar = NOT_CALCULATED ; // setto la dimensione dei vettori m_vBernU.resize( m_nDegU + 1) ; m_vBernV.resize( m_nDegV + 1) ; if (! m_bRat) m_ptTemp.resize(m_nDegV + 1, ORIG) ; else { m_ptTempW.resize(m_nDegV + 1, ORIG) ; m_dTempW.resize( m_nDegV + 1, 0) ; m_vPtWCtrlLoc.resize( GetLocDim()) ; m_vWeCtrlLoc.resize( GetLocDim()) ; } // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; return Validate() ; } //---------------------------------------------------------------------------- bool SurfBezier::SetControlPoint( int nInd, const Point3d& ptCtrl) { // verifico validità indice if ( m_nStatus != OK || m_bRat || nInd < 0 || nInd >= GetDim()) return false ; // assegno il valore m_vPtCtrl[nInd] = ptCtrl ; // se razionale, metto il peso a 1 if ( m_bRat) m_vWeCtrl[nInd] = 1 ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::SetControlPoint( int nInd, const Point3d& ptCtrl, double dW) { // verifico validità, razionalità e indice if ( m_nStatus != OK || ! m_bRat || nInd < 0 || nInd >= GetDim()) return false ; // verifico che il peso non sia nullo o negativo if ( dW < EPS_SMALL) return false ; // assegno il valore e il peso m_vPtCtrl[nInd] = ptCtrl ; m_vWeCtrl[nInd] = dW ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool 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)) ; // 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()) { // provo a offsettare il rettangolo parametrico ( ingrandendolo) per vedere se risolvo problemi di intersezione pSfrTrim->Offset( 10* EPS_SMALL, ICurve::OFF_EXTEND) ; if ( ! pSfrTrim->Intersect( sfrTrimReg) || ! pSfrTrim->IsValid()) return false ; pSfrTrim->Offset( -10* EPS_SMALL, ICurve::OFF_EXTEND) ; } } // 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()) { // provo a offsettare il rettangolo parametrico ( ingrandendolo) per vedere se risolvo problemi di sottrazione pSfrTrim->Offset( 10* EPS_SMALL, ICurve::OFF_EXTEND) ; if ( ! pSfrTrim->Subtract( sfrTrimReg) || ! pSfrTrim->IsValid()) return false ; pSfrTrim->Offset( -10* EPS_SMALL, ICurve::OFF_EXTEND) ; } } 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 ; return true ; } //---------------------------------------------------------------------------- SurfFlatRegion* SurfBezier::GetTrimRegion( void) const { if ( ! m_bTrimmed || m_pTrimReg == nullptr ) return nullptr ; return m_pTrimReg ; } //---------------------------------------------------------------------------- bool SurfBezier::GetInfo( int& nDegU, int& nDegV, int& nSpanU, int& nSpanV, bool& bIsRat, bool& bTrimmed) const { // verifico validità superficie if ( m_nStatus != OK) return false ; // restituisco gradi e flag di razionale nDegU = m_nDegU ; nDegV = m_nDegV ; nSpanU = m_nSpanU ; nSpanV = m_nSpanV ; bIsRat = m_bRat ; bTrimmed = m_bTrimmed ; return true ; } //---------------------------------------------------------------------------- const Point3d& SurfBezier::GetControlPoint( int nInd, bool* pbOk) const { // verifico validità e indice if ( m_nStatus != OK || nInd < 0 || nInd >= GetDim()) { if ( pbOk != NULL) *pbOk = false ; return ORIG ; } // ritorno i dati if ( pbOk != NULL) *pbOk = true ; return m_vPtCtrl[nInd] ; } //---------------------------------------------------------------------------- double SurfBezier::GetControlWeight( int nInd, bool* pbOk) const { // verifico validità, razionalità e indice if ( m_nStatus != OK || ! m_bRat || nInd < 0 || nInd >= GetDim()) { if ( pbOk != NULL) *pbOk = false ; return 0 ; } // ritorno i dati if ( pbOk != NULL) *pbOk = true ; return m_vWeCtrl[nInd] ; } //---------------------------------------------------------------------------- bool SurfBezier::IsAPoint( void) const { // verifico lo stato if ( m_nStatus != OK) return false ; // ciclo sui punti for ( int i = 1 ; i < GetDim() ; ++ i) { if ( ! AreSamePointApprox( m_vPtCtrl[0], m_vPtCtrl[i])) return false ; } return true ; } //---------------------------------------------------------------------------- bool SurfBezier::GetArea( double& dArea) const { // controllo parametro di ritorno if ( &dArea == nullptr) return false ; // inizio con area nulla dArea = 0 ; // calcolo l'area if ( m_pSTM == nullptr) if ( ! GetAuxSurf()) return false ; return m_pSTM->GetArea( dArea) ; } //---------------------------------------------------------------------------- bool SurfBezier::IsClosed( void) const { // altrimenti verifico a mano if ( m_nSpanU == 1 || m_nSpanV == 1) return false ; // controllo la prima riga con l'ultima m_bClosedV = true ; for ( int i = 0 ; i < m_nDegU * m_nSpanU + 1 && m_bClosedV ; ++i) m_bClosedV = AreSamePointApprox( m_vPtCtrl[GetInd( i, 0)], m_vPtCtrl[ GetInd(i, m_nDegV * m_nSpanV)]) ; // controllo la prima colonna con l'ultima m_bClosedU = true ; for ( int j = 0 ; j < m_nDegV * m_nSpanV + 1 && m_bClosedU ; ++j) m_bClosedU = AreSamePointApprox( m_vPtCtrl[GetInd(0,j)], m_vPtCtrl[GetInd(m_nDegU * m_nSpanU,j)]) ; // se ho la chiusura su entrambi i parametri ho la chiusura "in stile toro" if ( m_bClosedU && m_bClosedV) return true ; //verifico se ci sono dei poli che chiudono la superficie, per vedere se ho la chiusura "in stile sfera, da polo a polo" ( classica superficie di rivoluzione) CalcPoles() ; // chiusa in U e con poli sugli altri 2 lati if ( m_bClosedU && ! m_bClosedV && m_vbPole[0] && m_vbPole[2]) return true ; // chiusa in V e con poli sugli altri due lati if ( m_bClosedV && ! m_bClosedU && m_vbPole[1] && m_vbPole[3]) return true ; // se ho due lati opposti che sono dei poli e sono anche coincidenti allora la superficie potrebbe essere chiusa "in stile sfera a saccoccia" if ( m_vbPole[0] && m_vbPole[2] && AreSamePointApprox( m_vPtCtrl[GetInd(0,0)], m_vPtCtrl[GetInd(m_nDegU * m_nSpanU, m_nDegV * m_nSpanV)]) ) { bool bV0BackAndForth = true ; bool bV1BackAndForth = true ; for ( int i = 0 ; i < (m_nDegV * m_nSpanV + 1) / 2 && bV0BackAndForth && bV1BackAndForth; ++i) { bV0BackAndForth = AreSamePointApprox( m_vPtCtrl[GetInd(0,i)], m_vPtCtrl[GetInd(0,m_nDegV * m_nSpanV - i)]) ; bV1BackAndForth = AreSamePointApprox( m_vPtCtrl[GetInd(m_nDegU * m_nSpanU,i)], m_vPtCtrl[GetInd(m_nDegU * m_nSpanU,m_nDegV * m_nSpanV - i)]) ; } if ( bV0BackAndForth && bV1BackAndForth) return true ; } if ( m_vbPole[1] && m_vbPole[3] && AreSamePointApprox( m_vPtCtrl[GetInd(0,0)], m_vPtCtrl[GetInd(m_nDegU * m_nSpanU, m_nDegV * m_nSpanV)]) ) { bool bU0BackAndForth = true ; bool bU1BackAndForth = true ; for ( int i = 0 ; i < (m_nDegU * m_nSpanU + 1) / 2 && bU0BackAndForth && bU1BackAndForth; ++i) { bU0BackAndForth = AreSamePointApprox( m_vPtCtrl[GetInd( i,0)], m_vPtCtrl[GetInd( m_nDegU * m_nSpanU - i, 0)]) ; bU1BackAndForth = AreSamePointApprox( m_vPtCtrl[GetInd( i, m_nDegV * m_nSpanV)], m_vPtCtrl[GetInd( m_nDegU * m_nSpanU - i, m_nDegV * m_nSpanV)]) ; } if ( bU0BackAndForth && bU1BackAndForth) return true ; } return false ; } //---------------------------------------------------------------------------- bool SurfBezier::GetCentroid( Point3d& ptCen) const { // controllo parametro di ritorno if ( &ptCen == nullptr) return false ; // inizio con centro nell'origine ptCen = ORIG ; // calcolo il baricentro della superficie ausiliaria GetAuxSurf() ; if ( m_pSTM != nullptr) return m_pSTM->GetCentroid( ptCen) ; else return false ; } //---------------------------------------------------------------------------- bool SurfBezier::GetBernstein( double dU, int nDegU, DBLVECTOR& vBernU) const { const int TWO_TO_24 = 16777216 ; INTINT key( nDegU, int( dU * TWO_TO_24)) ; if ( s_mBernCache.find( key) == s_mBernCache.end()) { DBLVECTOR vBern( nDegU + 1) ; GetAllBernstein( dU, nDegU, vBern) ; s_mBernCache[key] = vBern ; } vBernU = s_mBernCache[key] ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::GetPoint( double dU, double dV, Side nUs, Side nVs,Point3d& ptPos) const { // la curva deve essere validata if ( m_nStatus != OK) return false ; // i parametri U e V devono essere compresi tra 0 e il corrispondente numero di Span dU = Clamp( dU, 0., double( m_nSpanU)) ; dV = Clamp( dV, 0., double( m_nSpanV)) ; // determino gli intervalli di span e riduco i parametri in essi int nBsU = min( int( dU), m_nSpanU - 1) ; double dLocU = dU - nBsU ; if ( abs( dLocU) < 5 * EPS_PARAM && nBsU > 0 && nUs == ISurfBezier::FROM_MINUS) { -- nBsU ; dLocU = 1 ; } else if ( abs( dLocU) > 1 - 5 * EPS_PARAM && nBsU < m_nSpanU - 1 && nUs == ISurfBezier::FROM_PLUS) { ++ nBsU ; dLocU = 0 ; } int nOffsU = nBsU * m_nDegU ; int nBsV = min( int( dV), m_nSpanV - 1) ; double dLocV = dV - nBsV ; if ( abs( dLocV) < 5 * EPS_PARAM && nBsV > 0 && nVs == ISurfBezier::FROM_MINUS) { -- nBsV ; dLocV = 1 ; } else if ( abs( dLocV) > 1 - 5 * EPS_PARAM && nBsV < m_nSpanV - 1 && nVs == ISurfBezier::FROM_PLUS) { ++ nBsV ; dLocV = 0 ; } int nOffsV = nBsV * m_nDegV ; // se forma polinomiale (o integrale) if ( ! m_bRat) { // calcolo dei polinomi di Bernstein per U di grado opportuno GetBernstein( dLocU, m_nDegU, m_vBernU) ; // calcolo dei punti intermedi m_ptTemp.assign(GetLocDim(), ORIG) ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { for ( int i = 0 ; i <= m_nDegU ; ++ i) m_ptTemp[j] += m_vBernU[i] * m_vPtCtrl[GetInd( nOffsU + i, nOffsV + j)] ; } // calcolo dei polinomi di Bernstein per V di grado opportuno GetBernstein( dLocV, m_nDegV, m_vBernV) ; // calcolo del punto ptPos = ORIG ; for ( int j = 0 ; j <= m_nDegV ; ++ j) ptPos += m_vBernV[j] * m_ptTemp[j] ; } // altrimenti forma razionale else { // recupero punti di controllo e pesi della patch in questione // porto i punti in forma omogenea moltiplicandoli per i pesi m_vPtWCtrlLoc.assign(GetLocDim(), ORIG) ; m_vWeCtrlLoc.assign(GetLocDim(), 0) ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { for ( int i = 0 ; i <= m_nDegU ; ++ i) { m_vPtWCtrlLoc[GetLocInd( i, j)] = m_vWeCtrl[GetInd( nOffsU + i, nOffsV + j)] * m_vPtCtrl[GetInd( nOffsU + i, nOffsV + j)] ; m_vWeCtrlLoc[GetLocInd( i, j)] = m_vWeCtrl[GetInd( nOffsU + i, nOffsV + j)] ; } } // calcolo dei polinomi di Bernstein di grado opportuno GetBernstein( dLocU, m_nDegU, m_vBernU) ; // calcolo dei punti e pesi intermedi m_ptTempW.assign(GetLocDim(), ORIG) ; m_dTempW.assign(GetLocDim(), 0) ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { for ( int i = 0 ; i <= m_nDegU ; ++ i) { m_ptTempW[j] += m_vBernU[i] * m_vPtWCtrlLoc[GetLocInd( i, j)] ; m_dTempW[j] += m_vBernU[i] * m_vWeCtrlLoc[GetLocInd( i, j)] ; } } // calcolo dei polinomi di Bernstein per V di grado opportuno GetBernstein( dLocV, m_nDegV, m_vBernV) ; // calcolo del punto double dW = 0 ; ptPos = ORIG ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { ptPos += m_vBernV[j] * m_ptTempW[j] ; dW += m_vBernV[j] * m_dTempW[j] ; } // ritrasformo da forma omogenea a forma standard double dInvW = 1 / ( ( dW > EPS_ZERO) ? dW : EPS_ZERO) ; ptPos *= dInvW ; } return true ; } //---------------------------------------------------------------------------- bool SurfBezier::GetPointD1D2( double dU, double dV, Side nUs, Side nVs, Point3d& ptPos, Vector3d* pvtDerU, Vector3d* pvtDerV, Vector3d* pvtDerUU, Vector3d* pvtDerVV, Vector3d* pvtDerUV) const { // la curva deve essere validata if ( m_nStatus != OK) return false ; // i parametri U e V devono essere compresi tra 0 e il corrispondente numero di Span dU = Clamp( dU, 0., double( m_nSpanU)) ; dV = Clamp( dV, 0., double( m_nSpanV)) ; // determino gli intervalli di span e riduco i parametri in essi int nBsU = min( int( dU), m_nSpanU - 1) ; double dLocU = dU - nBsU ; if ( abs( dLocU) < 5 * EPS_PARAM && nBsU > 0 && nUs == ISurfBezier::FROM_MINUS) { -- nBsU ; dLocU = 1 ; } else if ( abs( dLocU) > 1 - 5 * EPS_PARAM && nBsU < m_nSpanU - 1 && nUs == ISurfBezier::FROM_PLUS) { ++ nBsU ; dLocU = 0 ; } int nOffsU = nBsU * m_nDegU ; int nBsV = min( int( dV), m_nSpanV - 1) ; double dLocV = dV - nBsV ; if ( abs( dLocV) < 5 * EPS_PARAM && nBsV > 0 && nVs == ISurfBezier::FROM_MINUS) { -- nBsV ; dLocV = 1 ; } else if ( abs( dLocV) > 1 - 5 * EPS_PARAM && nBsV < m_nSpanV - 1 && nVs == ISurfBezier::FROM_PLUS) { ++ nBsV ; dLocV = 0 ; } int nOffsV = nBsV * m_nDegV ; // se forma polinomiale (o integrale) if ( ! m_bRat) { // calcolo dei polinomi di Bernstein per U di grado opportuno DBLVECTOR vBernU( m_nDegU + 1) ; GetAllBernstein( dLocU, m_nDegU - 2, vBernU) ; //// se richiesto, calcolo della derivata seconda // if ( pvtDer2 != nullptr && pvtDer1 != nullptr) { // *pvtDer2 = V_NULL ; // for ( int i = 0 ; i <= m_nDeg - 2 ; ++ i) { // *pvtDer2 += vBern[i] * ( m_vPtCtrl[i+2] + m_vPtCtrl[i] - 2 * m_vPtCtrl[i+1]) ; // } // *pvtDer2 *= m_nDeg * ( m_nDeg - 1) ; // } // aumento il grado IncreaseAllBernsteinOneDegree( dLocU, m_nDegU - 1, vBernU) ; // se richiesto, calcolo dei vettori intermedi per la derivata prima rispetto ad U VCT3DVECTOR vtTemp1( m_nDegV + 1, V_NULL) ; if ( pvtDerU != nullptr) { for ( int j = 0 ; j <= m_nDegV ; ++ j) { for ( int i = 0 ; i <= m_nDegU - 1 ; ++ i) vtTemp1[j] += vBernU[i] * ( m_vPtCtrl[GetInd( nOffsU + i + 1, nOffsV + j)] - m_vPtCtrl[GetInd( nOffsU + i, nOffsV + j)]) ; vtTemp1[j] *= m_nDegU ; } } // aumento il grado IncreaseAllBernsteinOneDegree( dLocU, m_nDegU, vBernU) ; // calcolo dei punti intermedi PNTVECTOR ptTemp( m_nDegV + 1, ORIG) ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { for ( int i = 0 ; i <= m_nDegU ; ++ i) ptTemp[j] += vBernU[i] * m_vPtCtrl[GetInd( nOffsU + i, nOffsV + j)] ; } // calcolo dei polinomi di Bernstein per V di grado opportuno DBLVECTOR vBernV( m_nDegV + 1) ; GetAllBernstein( dLocV, m_nDegV - 1, vBernV) ; // se richiesto, calcolo della derivata prima rispetto a V if ( pvtDerV != nullptr) { *pvtDerV = V_NULL ; for ( int j = 0 ; j <= m_nDegV - 1 ; ++ j) *pvtDerV += vBernV[j] * ( ptTemp[j+1] - ptTemp[j]) ; *pvtDerV *= m_nDegV ; } // aumento il grado IncreaseAllBernsteinOneDegree( dLocV, m_nDegV, vBernV) ; // calcolo del punto ptPos = ORIG ; for ( int j = 0 ; j <= m_nDegV ; ++ j) ptPos += vBernV[j] * ptTemp[j] ; // se richiesto, calcolo della derivata prima rispetto a U if ( pvtDerU != nullptr) { *pvtDerU = V_NULL ; for ( int j = 0 ; j <= m_nDegV ; ++ j) *pvtDerU += vBernV[j] * vtTemp1[j] ; } } // altrimenti forma razionale else { // porto i punti in forma omogenea moltiplicandoli per i pesi PNTVECTOR vPtWCtrl( GetLocDim()) ; DBLVECTOR vWeCtrl( GetLocDim()) ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { for ( int i = 0 ; i <= m_nDegU ; ++ i) { vPtWCtrl[GetLocInd( i, j)] = m_vWeCtrl[GetInd( nOffsU + i, nOffsV + j)] * m_vPtCtrl[GetInd( nOffsU + i, nOffsV + j)] ; vWeCtrl[GetLocInd( i, j)] = m_vWeCtrl[GetInd( nOffsU + i, nOffsV + j)] ; } } // calcolo dei polinomi di Bernstein di grado opportuno DBLVECTOR vBernU( m_nDegU + 1) ; GetAllBernstein( dLocU, m_nDegU - 2, vBernU) ; //// se richiesto, calcolo della derivata seconda // double dW2 = 0 ; // if ( pvtDer2 != nullptr && pvtDer1 != nullptr) { // *pvtDer2 = V_NULL ; // for ( int i = 0 ; i <= m_nDeg - 2 ; ++ i) { // *pvtDer2 += vBern[i] * ( vPtWCtrl[i+2] + vPtWCtrl[i] - 2 * vPtWCtrl[i+1]) ; // dW2 += vBern[i] * ( m_vWeCtrl[i+2] + m_vWeCtrl[i] - 2 * m_vWeCtrl[i+1]) ; // } // *pvtDer2 *= m_nDeg * ( m_nDeg - 1) ; // dW2 *= m_nDeg * ( m_nDeg - 1) ; // } // aumento il grado IncreaseAllBernsteinOneDegree( dLocU, m_nDegU - 1, vBernU) ; // se richiesto, calcolo dei vettori intermedi per la derivata prima rispetto ad U VCT3DVECTOR vtTemp1( m_nDegV + 1, V_NULL) ; DBLVECTOR dTemp1( m_nDegV + 1, 0) ; if ( pvtDerU != nullptr) { for ( int j = 0 ; j <= m_nDegV ; ++ j) { for ( int i = 0 ; i <= m_nDegU - 1 ; ++ i) { vtTemp1[j] += vBernU[i] * ( vPtWCtrl[GetLocInd( i + 1, j)] - vPtWCtrl[GetLocInd( i, j)]) ; dTemp1[j] += vBernU[i] * ( vWeCtrl[GetLocInd( i + 1, j)] - vWeCtrl[GetLocInd( i, j)]) ; } vtTemp1[j] *= m_nDegU ; dTemp1[j] *= m_nDegU ; } } // aumento il grado IncreaseAllBernsteinOneDegree( dLocU, m_nDegU, vBernU) ; // calcolo dei punti e pesi intermedi PNTVECTOR ptTempW( m_nDegV + 1, ORIG) ; DBLVECTOR dTempW( m_nDegV + 1, 0) ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { for ( int i = 0 ; i <= m_nDegU ; ++ i) { ptTempW[j] += vBernU[i] * vPtWCtrl[GetLocInd( i, j)] ; dTempW[j] += vBernU[i] * vWeCtrl[GetLocInd( i, j)] ; } } // calcolo dei polinomi di Bernstein per V di grado opportuno DBLVECTOR vBernV( m_nDegV + 1) ; GetAllBernstein( dLocV, m_nDegV - 1, vBernV) ; // se richiesto, calcolo della derivata prima rispetto a V double dW1v = 0 ; if ( pvtDerV != nullptr) { *pvtDerV = V_NULL ; for ( int j = 0 ; j <= m_nDegV - 1 ; ++ j) { *pvtDerV += vBernV[j] * ( ptTempW[j+1] - ptTempW[j]) ; dW1v += vBernV[j] * ( dTempW[j+1] - dTempW[j]) ; } *pvtDerV *= m_nDegV ; dW1v *= m_nDegV ; } // aumento il grado IncreaseAllBernsteinOneDegree( dLocV, m_nDegV, vBernV) ; // calcolo del punto double dW = 0 ; ptPos = ORIG ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { ptPos += vBernV[j] * ptTempW[j] ; dW += vBernV[j] * dTempW[j] ; } // ritrasformo da forma omogenea a forma standard double dInvW = 1 / ( ( dW > EPS_ZERO) ? dW : EPS_ZERO) ; ptPos *= dInvW ; // se richiesto, calcolo della derivata prima rispetto a U if ( pvtDerU != nullptr) { *pvtDerU = V_NULL ; double dW1u = 0 ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { *pvtDerU += vBernV[j] * vtTemp1[j] ; dW1u += vBernV[j] * dTemp1[j] ; } Vector3d vtPos( ptPos.x, ptPos.y, ptPos.z) ; *pvtDerU = ( *pvtDerU - dW1u * vtPos) * dInvW ; } // se richiesto, completo il calcolo della derivata prima rispetto a V if ( pvtDerV != nullptr) { Vector3d vtPos( ptPos.x, ptPos.y, ptPos.z) ; *pvtDerV = ( *pvtDerV - dW1v * vtPos) * dInvW ; } //if ( pvtDer1 != nullptr) { // Vector3d vtPos( ptPos.x, ptPos.y, ptPos.z) ; // *pvtDer1 = ( *pvtDer1 - dW1 * vtPos) * dInvW ; // if ( pvtDer2 != nullptr) // *pvtDer2 = ( *pvtDer2 - 2 * dW1 * *pvtDer1 - dW2 * vtPos) * dInvW ; //} } return true ; } //---------------------------------------------------------------------------- bool SurfBezier::GetPointNrmD1D2( double dU, double dV, Side nUs, Side nVs, Point3d& ptPos, Vector3d& vtN, Vector3d* pvtDerU, Vector3d* pvtDerV, Vector3d* pvtDerUU, Vector3d* pvtDerVV, Vector3d* pvtDerUV) const { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // i parametri U e V devono essere compresi tra 0 e il corrispondente numero di Span dU = Clamp( dU, 0., double( m_nSpanU)) ; dV = Clamp( dV, 0., double( m_nSpanV)) ; // eseguo calcolo del punto con le derivate prime Vector3d vtDerU ; if ( pvtDerU == nullptr) pvtDerU = &vtDerU ; Vector3d vtDerV ; if ( pvtDerV == nullptr) pvtDerV = &vtDerV ; GetPointD1D2( dU, dV, nUs, nVs, ptPos, pvtDerU, pvtDerV) ; // calcolo la normale e la verifico vtN = *pvtDerU ^ *pvtDerV ; if ( vtN.Normalize()) return true ; // se solo una sola delle due derivate è piccola, mi sposto lungo l'altro parametro if ( pvtDerU->Len() < EPS_SMALL && pvtDerV->Len() > 10 * EPS_SMALL) { double dVm = dV ; if ( nVs == ISurfBezier::FROM_MINUS) dVm = ( dV - 100 * EPS_PARAM < 0 ? dV + 100 * EPS_PARAM : dV - 100 * EPS_PARAM) ; else dVm = ( dV + 100 * EPS_PARAM > m_nSpanV ? dV - 100 * EPS_PARAM : dV + 100 * EPS_PARAM) ; Point3d ptTmp ; Vector3d vtTmpU, vtTmpV ; GetPointD1D2( dU, dVm, nUs, nVs, ptTmp, &vtTmpU, &vtTmpV) ; vtN = vtTmpU ^ vtTmpV ; if ( vtN.Normalize()) return true ; } if ( pvtDerU->Len() > 10 * EPS_SMALL && pvtDerV->Len() < EPS_SMALL) { double dUm = dU ; if ( nUs == ISurfBezier::FROM_MINUS) dUm = ( dU - 100 * EPS_PARAM < 0 ? dU + 100 * EPS_PARAM : dU - 100 * EPS_PARAM) ; else dUm = ( dU + 100 * EPS_PARAM > m_nSpanU ? dU - 100 * EPS_PARAM : dU + 100 * EPS_PARAM) ; Point3d ptTmp ; Vector3d vtTmpU, vtTmpV ; GetPointD1D2( dUm, dV, nUs, nVs, ptTmp, &vtTmpU, &vtTmpV) ; vtN = vtTmpU ^ vtTmpV ; if ( vtN.Normalize()) return true ; } // ricalcolo con una piccola variazione di entrambi i parametri double dUm = dU ; if ( nUs == ISurfBezier::FROM_MINUS) dUm = ( dU - 100 * EPS_PARAM < 0 ? dU + 100 * EPS_PARAM : dU - 100 * EPS_PARAM) ; else dUm = ( dU + 100 * EPS_PARAM > m_nSpanU ? dU - 100 * EPS_PARAM : dU + 100 * EPS_PARAM) ; double dVm = dV ; if ( nVs == ISurfBezier::FROM_MINUS) dVm = ( dV - 100 * EPS_PARAM < 0 ? dV + 100 * EPS_PARAM : dV - 100 * EPS_PARAM) ; else dVm = ( dV + 100 * EPS_PARAM > m_nSpanV ? dV - 100 * EPS_PARAM : dV + 100 * EPS_PARAM) ; Point3d ptTmp ; Vector3d vtTmpU, vtTmpV ; GetPointD1D2( dUm, dVm, nUs, nVs, ptTmp, &vtTmpU, &vtTmpV) ; vtN = vtTmpU ^ vtTmpV ; return vtN.Normalize() ; } //---------------------------------------------------------------------------- SurfBezier* SurfBezier::Clone( void) const { // alloco oggetto SurfBezier* pSbz = new( nothrow) SurfBezier ; if ( pSbz != nullptr) { if ( ! pSbz->CopyFrom( *this)) { delete pSbz ; return nullptr ; } } return pSbz ; } //---------------------------------------------------------------------------- bool SurfBezier::CopyFrom( const IGeoObj* pGObjSrc) { const SurfBezier* pSbz = GetBasicSurfBezier( pGObjSrc) ; if ( pSbz == nullptr) return false ; return CopyFrom( *pSbz) ; } //---------------------------------------------------------------------------- bool SurfBezier::CopyFrom( const SurfBezier& sbSrc) { if ( &sbSrc == this) return true ; if ( ! Init( sbSrc.m_nDegU, sbSrc.m_nDegV, sbSrc.m_nSpanU, sbSrc.m_nSpanV, sbSrc.m_bRat)) return false ; m_nStatus = sbSrc.m_nStatus ; m_vPtCtrl = sbSrc.m_vPtCtrl ; if ( sbSrc.m_bRat) m_vWeCtrl = sbSrc.m_vWeCtrl ; if ( sbSrc.m_bTrimmed) { m_bTrimmed = true ; m_pTrimReg = sbSrc.m_pTrimReg->Clone() ; } #if ! SAVEFAILEDTRIANGULATION && ! SAVEFAILEDTREE if ( sbSrc.GetAuxSurf() != nullptr) m_pSTM = sbSrc.GetAuxSurf()->Clone() ; #endif for ( int i = 0 ; i < int( sbSrc.m_mCCEdge.size()) ; ++i) { m_mCCEdge.emplace_back() ; for ( int j = 0 ; j < int( sbSrc.m_mCCEdge[i].size()) ; ++j ) { m_mCCEdge.back().emplace_back( sbSrc.m_mCCEdge[i][j]->Clone()) ; } } if ( sbSrc.m_bTrimmed) { for ( int i = 0 ; i < int( sbSrc.m_vCCLoop.size()) ; ++i) m_vCCLoop.emplace_back( sbSrc.m_vCCLoop[i]->Clone()); } m_bClosedU = sbSrc.m_bClosedU ; m_bClosedV = sbSrc.m_bClosedV ; m_nTempProp[0] = sbSrc.m_nTempProp[0] ; m_nTempProp[1] = sbSrc.m_nTempProp[1] ; m_dTempParam[0] = sbSrc.m_dTempParam[0] ; m_dTempParam[1] = sbSrc.m_dTempParam[1] ; return true ; } //---------------------------------------------------------------------------- GeoObjType SurfBezier::GetType( void) const { return static_cast( GEOOBJ_GETTYPE( SurfBezier)) ; } //---------------------------------------------------------------------------- const string& SurfBezier::GetTitle( void) const { static const string sTitle = "SurfBezier" ; return sTitle ; } //---------------------------------------------------------------------------- bool SurfBezier::Dump( string& sOut, bool bMM, const char* szNewLine) const { // verifico validità superficie if ( m_nStatus != OK) sOut += string( "Status=Invalid") + szNewLine ; // area double dArea ; GetArea( dArea) ; sOut += "Area=" + ToString( GetAreaInUiUnits( dArea, bMM),1) + szNewLine ; // altri dati per superficie chiusa if ( IsClosed()) { double dVolume ; GetVolume( dVolume) ; sOut += "Closed Volume=" + ToString( GetVolumeInUiUnits( dVolume, bMM), 1) + szNewLine ; } // parametri : flag razionale sOut += ( m_bRat ? "Rat" : "Int") ; // flag trimmata sOut += ( m_bTrimmed ? " Trim " : " Full") ; // gradi in U e V sOut += " DegU=" + ToString( m_nDegU) + " DegV=" + ToString( m_nDegV) ; // pezze in U e V sOut += " SpanU=" + ToString( m_nSpanU) + " SpanV=" + ToString( m_nSpanV) + szNewLine ; // ciclo sui punti di controllo ( con pesi se razionale) for ( int i = 0 ; i < GetDim() ; ++ i) { sOut += "PC(" + ToString( GetInUiUnits( m_vPtCtrl[i], bMM), 3) ; if ( m_bRat) sOut += "," + ToString( m_vWeCtrl[i], 3) ; sOut += string( ")") + szNewLine ; } return true ; } //---------------------------------------------------------------------------- int SurfBezier::GetNgeId( void) const { return GEOOBJ_GETNGEID( SurfBezier) ; } //---------------------------------------------------------------------------- bool SurfBezier::Save( NgeWriter& ngeOut) const { // flag razionale if ( ! ngeOut.WriteBool( m_bRat, ";")) return false ; // flag trimmata if ( ! ngeOut.WriteBool( m_bTrimmed, ";")) return false ; // gradi if ( ! ngeOut.WriteInt( m_nDegU, ",") || ! ngeOut.WriteInt( m_nDegV, ";", false)) return false ; // pezze if ( ! ngeOut.WriteInt( m_nSpanU, ",") || ! ngeOut.WriteInt( m_nSpanV, ";", true)) return false ; // ciclo sui punti di controllo ( con pesi se razionale) for ( int i = 0 ; i < GetDim() ; ++ i) { if ( ! m_bRat) { if ( ! ngeOut.WritePoint( m_vPtCtrl[i], ";", true)) return false ; } else { if ( ! ngeOut.WritePointW( m_vPtCtrl[i], m_vWeCtrl[i], ";", true)) return false ; } } // se trimmata, scrittura della regione if ( m_bTrimmed) { // recupero il gestore di lettura/scrittura della regione const IGeoObjRW* pSFrRW = dynamic_cast( m_pTrimReg) ; if ( pSFrRW == nullptr) return false ; // salvataggio della regione if ( ! pSFrRW->Save( ngeOut)) return false ; } return true ; } //---------------------------------------------------------------------------- bool SurfBezier::Load( NgeReader& ngeIn) { // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // leggo la prossima linea ( 3 parametri) // recupero il flag razionale bool bIsRat ; if ( ! ngeIn.ReadBool( bIsRat, ";")) return false ; // recupero il flag trimmata bool bTrimmed ; if ( ! ngeIn.ReadBool( bTrimmed, ";")) return false ; // recupero i gradi int nDegU, nDegV ; if ( ! ngeIn.ReadInt( nDegU, ",") || ! ngeIn.ReadInt( nDegV, ";")) return false ; // recupero le pezze int nSpanU, nSpanV ; if ( ! ngeIn.ReadInt( nSpanU, ",") || ! ngeIn.ReadInt( nSpanV, ";", true)) return false ; // inizializzo la superficie di Bezier if ( ! Init( nDegU, nDegV, nSpanU, nSpanV, bIsRat)) return false ; // se integrale if ( ! bIsRat) { // recupero e setto punti di controllo Point3d ptP ; for ( int i = 0 ; i < GetDim() ; ++ i) { // leggo la prossima linea ( un punto) if ( ! ngeIn.ReadPoint( ptP, ";", true)) return false ; // lo assegno if ( ! SetControlPoint( i, ptP)) return false ; } } // altrimenti razionale else { // recupero e setto punti di controllo Point3d ptP ; double dW ; for ( int i = 0 ; i < GetDim() ; ++ i) { // leggo la prossima linea ( un punto con peso) if ( ! ngeIn.ReadPointW( ptP, dW, ";", true)) return false ; // lo assegno if ( ! SetControlPoint( i, ptP, dW)) return false ; } } // se trimmata, lettura della regione if ( bTrimmed) { // creo l'oggetto ResetTrimRegion() ; m_pTrimReg = CreateBasicSurfFlatRegion() ; if ( m_pTrimReg == nullptr) return false ; // ne leggo i dati IGeoObjRW* pGObjRW = dynamic_cast( m_pTrimReg) ; if ( pGObjRW == nullptr || ! pGObjRW->Load( ngeIn)) return false ; m_bTrimmed = true ; } #if SAVEREBUILTISO ICURVEPOVECTOR vCrv ; GetAllPatchesIsocurves( false, vCrv) ; vGeo.clear() ; for ( int i = 0 ; i < ssize(vCrv) ; ++i) vGeo.push_back( vCrv[i]->Clone()) ; SaveGeoObj( vGeo, "D:\\Temp\\bezier\\ruled\\rebuild\\isoCrv.nge") ; #endif // eseguo validazione return Validate() ; } //---------------------------------------------------------------------------- bool SurfBezier::Validate( void) { if ( m_nStatus == TO_VERIFY) m_nStatus = ( ( m_nDegU * m_nDegV > 0 && ! m_vPtCtrl.empty()) ? OK : ERR) ; return ( m_nStatus == OK) ; } //---------------------------------------------------------------------------- bool SurfBezier::GetLocalBBox( BBox3d& b3Loc, int nFlag) const { // basta approssimato if ( ( nFlag & BBF_EXACT) == 0) { for ( int i = 0 ; i < GetDim() ; ++ i) { Point3d ptTemp = m_vPtCtrl[i] ; b3Loc.Add( ptTemp) ; } return true ; } // deve essere preciso else { // verifico esistenza trimesh associata if ( m_pSTM == nullptr) if ( ! GetAuxSurf()) return false ; // calcolo il box della trimesh return m_pSTM->GetLocalBBox( b3Loc, nFlag) ; } } //---------------------------------------------------------------------------- bool SurfBezier::GetBBox( const Frame3d& frRef, BBox3d& b3Ref, int nFlag) const { // basta approssimato if ( ( nFlag & BBF_EXACT) == 0 && ! m_bTrimmed) { for ( int i = 0 ; i < GetDim() ; ++ i) { Point3d ptTemp = m_vPtCtrl[i] ; ptTemp.ToGlob( frRef) ; b3Ref.Add( ptTemp) ; } return true ; } // deve essere preciso else { // verifico esistenza trimesh associata if ( m_pSTM == nullptr) if ( ! GetAuxSurf()) return false ; // calcolo il box della trimesh return m_pSTM->GetBBox( frRef, b3Ref, nFlag) ; } } //---------------------------------------------------------------------------- bool SurfBezier::Translate( const Vector3d& vtMove) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // traslo i punti di controllo for ( auto& ptP : m_vPtCtrl) ptP.Translate( vtMove) ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, double dSinAng) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità dell'asse di rotazione if ( vtAx.IsSmall()) return false ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // ruoto i punti di controllo for ( auto& ptP : m_vPtCtrl) ptP.Rotate( ptAx, vtAx, dCosAng, dSinAng) ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double dCoeffZ) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico non sia nulla if ( abs( dCoeffX) < EPS_ZERO && abs( dCoeffY) < EPS_ZERO && abs( dCoeffZ) < EPS_ZERO) return false ; // calcolo bbox allineato con riferimento di scalatura e senza tener conto dello spessore // lo scalo per verificare se tutto si riduce a un punto o una linea (solo se diretta come assi ref) BBox3d b3Ref ; if ( ! GetBBox( frRef, b3Ref)) return false ; Vector3d vtDelta = b3Ref.GetMax() - b3Ref.GetMin() ; bool bZeroX = ( abs( vtDelta.x * dCoeffX) < EPS_SMALL) ; bool bZeroY = ( abs( vtDelta.y * dCoeffY) < EPS_SMALL) ; bool bZeroZ = ( abs( vtDelta.z * dCoeffZ) < EPS_SMALL) ; if ( ( bZeroX && bZeroY) || ( bZeroX && bZeroZ) || ( bZeroY && bZeroZ)) return false ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // scalo i punti di controllo for ( auto& ptP : m_vPtCtrl) ptP.Scale( frRef, dCoeffX, dCoeffY, dCoeffZ) ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::Mirror( const Point3d& ptOn, const Vector3d& vtNorm) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità del piano di specchiatura if ( vtNorm.IsSmall()) return false ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // specchio i punti di controllo for ( auto& ptP : m_vPtCtrl) ptP.Mirror( ptOn, vtNorm) ; // eseguo invert per mantenere l'orientazione return Invert() ; } //---------------------------------------------------------------------------- bool SurfBezier::Shear( const Point3d& ptOn, const Vector3d& vtNorm, const Vector3d& vtDir, double dCoeff) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità dei parametri if ( vtNorm.IsSmall() || vtDir.IsSmall()) return false ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // eseguo scorrimento dei punti di controllo for ( auto& ptP : m_vPtCtrl) ptP.Shear( ptOn, vtNorm, vtDir, dCoeff) ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::ToGlob( const Frame3d& frRef) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità del frame if ( frRef.GetType() == Frame3d::ERR) return false ; // se frame identità, non devo fare alcunché if ( IsGlobFrame( frRef)) return true ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // trasformo i punti di controllo for ( auto& ptP : m_vPtCtrl) ptP.ToGlob( frRef) ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::ToLoc( const Frame3d& frRef) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità del frame if ( frRef.GetType() == Frame3d::ERR) return false ; // se frame identità, non devo fare alcunché if ( IsGlobFrame( frRef)) return true ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // trasformo i punti di controllo for ( auto& ptP : m_vPtCtrl) ptP.ToLoc( frRef) ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::LocToLoc( const Frame3d& frOri, const Frame3d& frDest) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // verifico validità dei frame if ( frOri.GetType() == Frame3d::ERR || frDest.GetType() == Frame3d::ERR) return false ; // se i due riferimenti coincidono, non devo fare alcunché if ( AreSameFrame( frOri, frDest)) return true ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // trasformo i punti di controllo for ( auto& ptP : m_vPtCtrl) ptP.LocToLoc( frOri, frDest) ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::Invert( void) { // la superficie deve essere validata if ( m_nStatus != OK) return false ; // imposto ricalcolo della grafica ResetAuxSurf() ; m_OGrMgr.Reset() ; // inverto i punti del parametro U PNTVECTOR vPtCtrl_inv( m_vPtCtrl.size()) ; for ( int j = 0 ; j < m_nDegV * m_nSpanV + 1; ++j) { for ( int i = 0 ; i < m_nDegU * m_nSpanU + 1; ++i) { vPtCtrl_inv[ i + j * ( m_nDegU * m_nSpanU + 1)] = m_vPtCtrl[ ( m_nDegU * m_nSpanU - i) + j * ( m_nDegU * m_nSpanU + 1)] ; } } m_vPtCtrl = vPtCtrl_inv ; if ( m_bTrimmed) { // inverto la flat region di trim //( la specchio rispetto all'asse verticale) Point3d pt( m_nSpanU * SBZ_TREG_COEFF/2, 0, 0) ; Vector3d vt( 1, 0, 0) ; m_pTrimReg->Mirror( pt, vt) ; } return true ; } //---------------------------------------------------------------------------- int SurfBezier::GetSteps( int nDeg, int nSpan, double dLen, int nQuality) const { const double dCoeff[] = { 0, 1, 2, 2.4, 2.8, 3, 3, 3, 3, 3, 3, 3} ; nDeg = Clamp( nDeg, 0, MAXDEG) ; nSpan = max( nSpan, 1) ; nQuality = Clamp( nQuality, 1, 10) ; double dMult = sqrt( max( dLen / nSpan, 10.)) ; return ( nSpan * int( dMult * dCoeff[nDeg] * 2 / nQuality)) ; } //---------------------------------------------------------------------------- CurveComposite* SurfBezier::GetCurveOnU( double dV) const { // controlli if ( dV < - EPS_PARAM || dV > m_nSpanV + EPS_PARAM) return nullptr ; dV = Clamp( dV, 0., double( m_nSpanV)) ; // determino l'intervallo di span in V e riduco i parametri in essi int nBsV = min( int( dV), m_nSpanV - 1) ; double dLocV = dV - nBsV ; int nOffsV = nBsV * m_nDegV ; // verifico se il parametro è intero allora semplicemente recupero i punti di controllo if ( abs( dV - int( dV)) < EPS_PARAM) { int nV = int( round( dV)) ; PtrOwner pCrvCo( CreateBasicCurveComposite()) ; bool bOk = false ; if ( ! m_bRat) { // ciclo sugli intervalli for ( int s = 0 ; s < m_nSpanU ; ++ s) { PtrOwner pCbz( CreateBasicCurveBezier()) ; pCbz->Init( m_nDegU, m_bRat) ; for ( int j = 0 ; j < m_nDegU ; ++j) pCbz->SetControlPoint( s * m_nDegU + j, GetControlPoint( GetInd( s * m_nDegU + j , nV * m_nDegV), &bOk), GetControlWeight( GetInd( s * m_nDegU + j , nV * m_nDegV), &bOk)) ; pCrvCo->AddCurve( Release( pCbz)) ; } } else { // ciclo sugli intervalli for ( int s = 0 ; s < m_nSpanU ; ++ s) { PtrOwner pCbz( CreateBasicCurveBezier()) ; pCbz->Init( m_nDegU, m_bRat) ; for ( int j = 0 ; j < m_nDegU ; ++j) pCbz->SetControlPoint( s * m_nDegU + j, GetControlPoint( GetInd( s * m_nDegU + j , nV * m_nDegV), &bOk)) ; pCrvCo->AddCurve( Release( pCbz)) ; } } } // se forma polinomiale (o integrale) if ( ! m_bRat) { // preparazione della curva composita PtrOwner pCrvCo( CreateBasicCurveComposite()) ; // ciclo sugli intervalli for ( int k = 0 ; k < m_nSpanU ; ++ k) { // preparazione della curva di Bezier PtrOwner pCbz( CreateBasicCurveBezier()) ; if ( IsNull( pCbz) || ! pCbz->Init( m_nDegU, false)) return nullptr ; // calcolo dei polinomi di Bernstein per V di grado opportuno DBLVECTOR vBernV( m_nDegV + 1) ; GetAllBernstein( dLocV, m_nDegV, vBernV) ; // calcolo offset in U int nOffsU = k * m_nDegU ; // calcolo dei punti di controllo della curva for ( int i = 0 ; i <= m_nDegU ; ++ i) { Point3d ptP = ORIG ; for ( int j = 0 ; j <= m_nDegV ; ++ j) ptP += vBernV[j] * m_vPtCtrl[GetInd( nOffsU + i, nOffsV + j)] ; pCbz->SetControlPoint( i, ptP) ; } // inserisco la curva della pezza in quella complessiva if ( ! IsNull( pCbz)) pCrvCo->AddCurve( Release( pCbz)) ; } return Release( pCrvCo) ; } // altrimenti forma razionale else { // preparazione della curva composita PtrOwner pCrvCo( CreateBasicCurveComposite()) ; // ciclo sugli intervalli for ( int k = 0 ; k < m_nSpanU ; ++ k) { // preparazione della curva di Bezier PtrOwner pCbz( CreateBasicCurveBezier()) ; if ( IsNull( pCbz) || ! pCbz->Init( m_nDegU, true)) return nullptr ; // calcolo dei polinomi di Bernstein per V di grado opportuno DBLVECTOR vBernV( m_nDegV + 1) ; GetAllBernstein( dLocV, m_nDegV, vBernV) ; // calcolo offset in U int nOffsU = k * m_nDegU ; // porto i punti in forma omogenea moltiplicandoli per i pesi PNTVECTOR vPtWCtrl( GetLocDim()) ; DBLVECTOR vWeCtrl( GetLocDim()) ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { for ( int i = 0 ; i <= m_nDegU ; ++ i) { vPtWCtrl[GetLocInd( i, j)] = m_vWeCtrl[GetInd( nOffsU + i, nOffsV + j)] * m_vPtCtrl[GetInd( nOffsU + i, nOffsV + j)] ; vWeCtrl[GetLocInd( i, j)] = m_vWeCtrl[GetInd( nOffsU + i, nOffsV + j)] ; } } // calcolo dei punti di controllo della curva for ( int i = 0 ; i <= m_nDegU ; ++ i) { Point3d ptP = ORIG ; double dW = 0 ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { ptP += vBernV[j] * vPtWCtrl[GetLocInd( i, j)] ; dW += vBernV[j] * vWeCtrl[GetLocInd( i, j)] ; } double dInvW = 1 / ( ( dW > EPS_ZERO) ? dW : EPS_ZERO) ; pCbz->SetControlPoint( i, ptP * dInvW, dW) ; } // inserisco la curva della pezza in quella complessiva if ( ! IsNull( pCbz)) pCrvCo->AddCurve( Release( pCbz)) ; } return Release( pCrvCo) ; } } //---------------------------------------------------------------------------- CurveComposite* SurfBezier::GetCurveOnV( double dU) const { // controlli if ( dU < - EPS_PARAM || dU > m_nSpanU + EPS_PARAM) return nullptr ; dU = Clamp( dU, 0., double( m_nSpanU)) ; // determino l'intervallo di span in U e riduco i parametri in essi int nBsU = min( int( dU), m_nSpanU - 1) ; double dLocU = dU - nBsU ; int nOffsU = nBsU * m_nDegU ; // verifico se il parametro è intero allora semplicemente recupero i punti di controllo if ( abs( dU - int( dU)) < EPS_PARAM) { int nU = int( round( dU)) ; PtrOwner pCrvCo( CreateBasicCurveComposite()) ; bool bOk = false ; if ( ! m_bRat) { // ciclo sugli intervalli for ( int s = 0 ; s < m_nSpanV ; ++ s) { PtrOwner pCbz( CreateBasicCurveBezier()) ; pCbz->Init( m_nDegV, m_bRat) ; for ( int j = 0 ; j < m_nDegV ; ++j) pCbz->SetControlPoint( s * m_nDegV + j, GetControlPoint( GetInd( s * m_nDegV + j , nU * m_nDegU), &bOk), GetControlWeight( GetInd( s * m_nDegV + j , nU * m_nDegU), &bOk)) ; pCrvCo->AddCurve( Release( pCbz)) ; } } else { // ciclo sugli intervalli for ( int s = 0 ; s < m_nSpanV ; ++ s) { PtrOwner pCbz( CreateBasicCurveBezier()) ; pCbz->Init( m_nDegV, m_bRat) ; for ( int j = 0 ; j < m_nDegV ; ++j) pCbz->SetControlPoint( s * m_nDegV + j, GetControlPoint( GetInd( s * m_nDegV + j , nU * m_nDegU), &bOk)) ; pCrvCo->AddCurve( Release( pCbz)) ; } } } // se forma polinomiale (o integrale) if ( ! m_bRat) { // preparazione della curva composita PtrOwner pCrvCo( CreateBasicCurveComposite()) ; // ciclo sugli intervalli for ( int k = 0 ; k < m_nSpanV ; ++ k) { // preparazione della curva di Bezier PtrOwner pCbz( CreateBasicCurveBezier()) ; if ( IsNull( pCbz) || ! pCbz->Init( m_nDegV, false)) return nullptr ; // calcolo dei polinomi di Bernstein per U di grado opportuno DBLVECTOR vBernU( m_nDegU + 1) ; GetAllBernstein( dLocU, m_nDegU, vBernU) ; // calcolo offset in V int nOffsV = k * m_nDegV ; // calcolo dei punti di controllo della curva for ( int j = 0 ; j <= m_nDegV ; ++ j) { Point3d ptP = ORIG ; for ( int i = 0 ; i <= m_nDegU ; ++ i) ptP += vBernU[i] * m_vPtCtrl[GetInd( nOffsU + i, nOffsV + j)] ; pCbz->SetControlPoint( j, ptP) ; } // inserisco la curva della pezza in quella complessiva if ( ! IsNull( pCbz)) pCrvCo->AddCurve( Release( pCbz)) ; } return Release( pCrvCo) ; } // altrimenti forma razionale else { // preparazione della curva composita PtrOwner pCrvCo( CreateBasicCurveComposite()) ; // ciclo sugli intervalli for ( int k = 0 ; k < m_nSpanV ; ++ k) { // preparazione della curva di Bezier PtrOwner pCbz( CreateBasicCurveBezier()) ; if ( IsNull( pCbz) || ! pCbz->Init( m_nDegV, true)) return nullptr ; // calcolo dei polinomi di Bernstein per U di grado opportuno DBLVECTOR vBernU( m_nDegU + 1) ; GetAllBernstein( dLocU, m_nDegU, vBernU) ; // calcolo offset in V int nOffsV = k * m_nDegV ; // porto i punti in forma omogenea moltiplicandoli per i pesi PNTVECTOR vPtWCtrl( GetLocDim()) ; DBLVECTOR vWeCtrl( GetLocDim()) ; for ( int j = 0 ; j <= m_nDegV ; ++ j) { for ( int i = 0 ; i <= m_nDegU ; ++ i) { vPtWCtrl[GetLocInd( i, j)] = m_vWeCtrl[GetInd( nOffsU + i, nOffsV + j)] * m_vPtCtrl[GetInd( nOffsU + i, nOffsV + j)] ; vWeCtrl[GetLocInd( i, j)] = m_vWeCtrl[GetInd( nOffsU + i, nOffsV + j)] ; } } // calcolo dei punti di controllo della curva for ( int j = 0 ; j <= m_nDegV ; ++ j) { Point3d ptP = ORIG ; double dW = 0 ; for ( int i = 0 ; i <= m_nDegU ; ++ i) { ptP += vBernU[i] * vPtWCtrl[GetLocInd( i, j)] ; dW += vBernU[i] * vWeCtrl[GetLocInd( i, j)] ; } double dInvW = 1 / ( ( dW > EPS_ZERO) ? dW : EPS_ZERO) ; pCbz->SetControlPoint( j, ptP * dInvW, dW) ; } // inserisco la curva della pezza in quella complessiva if ( ! IsNull( pCbz)) pCrvCo->AddCurve( Release( pCbz)) ; } return Release( pCrvCo) ; } } //---------------------------------------------------------------------------- CurveComposite* SurfBezier::GetLoop( int nLoop) const { // Il primo loop sono le 4 isoparametriche di bordo concatenate if ( ! m_bTrimmed ) { if ( nLoop != 0 ) return nullptr ; // Loop PtrOwner pLoop( CreateBasicCurveComposite()) ; // prima curva isoparametrica in U con V=0 PtrOwner pCrvCoU0( GetCurveOnU( 0)) ; if ( ! IsNull( pCrvCoU0) && ! pCrvCoU0->IsAPoint()) pLoop->AddCurve( Release( pCrvCoU0)) ; // seconda curva isoparametrica in V con U=m_nSpanU PtrOwner pCrvCoV1( GetCurveOnV( m_nSpanU)) ; if ( ! IsNull( pCrvCoV1) && ! pCrvCoV1->IsAPoint()) pLoop->AddCurve( Release( pCrvCoV1)) ; // terza curva isoparametrica in U con V=m_nSpanV invertita PtrOwner pCrvCoU1( GetCurveOnU( m_nSpanV)) ; if ( ! IsNull( pCrvCoU1) && ! pCrvCoU1->IsAPoint()) { pCrvCoU1->Invert() ; pLoop->AddCurve( Release( pCrvCoU1)) ; } // quarta curva isoparametrica in V con U=0 invertita PtrOwner pCrvCoV0( GetCurveOnV( 0)) ; if ( ! IsNull( pCrvCoV0) && ! pCrvCoV0->IsAPoint()) { pCrvCoV0->Invert() ; pLoop->AddCurve( Release( pCrvCoV0)) ; } // se loop chiuso lo restituisco, altrimenti errore return ( pLoop->IsClosed() ? Release( pLoop) : nullptr) ; } // la superficie è trimmata, quindi devo cercare nei vari chunck il loop corrispondente else { if ( nLoop > m_pTrimReg->GetChunkCount()) return nullptr ; else { int nLoopCount = 0 ; int nChunck = 0, nLoopLoc = 0; INTVECTOR nLoopCountPerChunck ; for ( int i = 0 ; i < m_pTrimReg->GetChunkCount() && nLoopCount != nLoop ; ++ i) { int nLoopCountLoc = 0 ; for ( int j = 0 ; j < m_pTrimReg->GetLoopCount( i) ; ++ j) { ++ nLoopCountLoc ; ++ nLoopCount ; if ( nLoopCount != nLoop ) { nChunck = i ; nLoopLoc = j ; break ; } } nLoopCountPerChunck.push_back( nLoopCountLoc) ; } if ( nLoopCount < nLoop ) return nullptr ; PtrOwner pLoop( GetBasicCurveComposite( m_pTrimReg->GetLoop( nChunck, nLoopLoc))) ; return Release( pLoop) ; } } } //---------------------------------------------------------------------------- bool SurfBezier::GetCurveOnU( double dV, int nStep, PolyLine& plCrvU) const { // controlli plCrvU.Clear() ; if ( dV < - EPS_PARAM || dV > m_nSpanV + EPS_PARAM) return false ; dV = Clamp( dV, 0., double( m_nSpanV)) ; // recupero la curva PtrOwner pCrvCo( GetCurveOnU( dV)) ; if ( IsNull( pCrvCo)) return false ; // ciclo sulle curve componenti (tutte curve di Bezier) int i = 0 ; const ICurve* pSCrv = pCrvCo->GetFirstCurve() ; while ( pSCrv != nullptr) { const CurveBezier* pCbz = GetBasicCurveBezier( pSCrv) ; if ( pCbz == NULL) return false ; int nCurrSpanStep = nStep / m_nSpanU ; if ( nCurrSpanStep <= 0) { double dLenU = 0 ; pCbz->GetApproxLength( dLenU) ; nCurrSpanStep = GetSteps( m_nDegU, 1, dLenU, 2) ; } PolyLine plCurr ; if ( ! pCbz->ApproxWithLines( nCurrSpanStep, plCurr)) return false ; plCrvU.Join( plCurr, i) ; ++ i ; pSCrv = pCrvCo->GetNextCurve() ; } return true ; } //---------------------------------------------------------------------------- bool SurfBezier::GetCurveOnV( double dU, int nStep, PolyLine& plCrvV) const { // controlli plCrvV.Clear() ; if ( dU < - EPS_PARAM || dU > m_nSpanU + EPS_PARAM) return false ; dU = Clamp( dU, 0., double( m_nSpanU)) ; // recupero la curva PtrOwner pCrvCo( GetCurveOnV( dU)) ; if ( IsNull( pCrvCo)) return false ; // ciclo sulle curve componenti (tutte curve di Bezier) int i = 0 ; const ICurve* pSCrv = pCrvCo->GetFirstCurve() ; while ( pSCrv != nullptr) { const CurveBezier* pCbz = GetBasicCurveBezier( pSCrv) ; if ( pCbz == NULL) return false ; int nCurrSpanStep = nStep / m_nSpanU ; if ( nCurrSpanStep <= 0) { double dLenV = 0 ; pCbz->GetApproxLength( dLenV) ; nCurrSpanStep = GetSteps( m_nDegV, 1, dLenV, 2) ; } PolyLine plCurr ; if ( ! pCbz->ApproxWithLines( nCurrSpanStep, plCurr)) return false ; plCrvV.Join( plCurr, i) ; ++ i ; pSCrv = pCrvCo->GetNextCurve() ; } return true ; } //---------------------------------------------------------------------------- bool SurfBezier::GetControlCurveOnU( int nIndV, PolyLine& plCtrlU) const { plCtrlU.Clear() ; if ( nIndV < 0 || nIndV > m_nDegV * m_nSpanV) return false ; for ( int i = 0 ; i <= m_nDegU * m_nSpanU ; ++ i) plCtrlU.AddUPoint( double( i) / m_nDegU, m_vPtCtrl[GetInd( i, nIndV)]) ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::GetControlCurveOnV( int nIndU, PolyLine& plCtrlV) const { plCtrlV.Clear() ; if ( nIndU < 0 || nIndU > m_nDegU * m_nSpanU) return false ; for ( int j = 0 ; j <= m_nDegV * m_nSpanV ; ++ j) plCtrlV.AddUPoint( double( j) / m_nDegV, m_vPtCtrl[GetInd( nIndU, j)]) ; return true ; } //---------------------------------------------------------------------------- double SurfBezier::GetCurveOnUApproxLen( double dV) const { PtrOwner pCrvCo( GetCurveOnU( dV)) ; double dLen ; if ( IsNull( pCrvCo) || ! pCrvCo->GetApproxLength( dLen)) return 0 ; return dLen ; } //---------------------------------------------------------------------------- double SurfBezier::GetCurveOnVApproxLen( double dU) const { PtrOwner pCrvCo( GetCurveOnV( dU)) ; double dLen ; if ( IsNull( pCrvCo) || ! pCrvCo->GetApproxLength( dLen)) return 0 ; return dLen ; } int nSurf = 0 ; //---------------------------------------------------------------------------- const SurfTriMesh* SurfBezier::GetAuxSurf( void) const { // la superficie deve essere validata if ( m_nStatus != OK) { ResetAuxSurf() ; return nullptr ; } // se già calcolata if ( m_pSTM != nullptr) { // se con la stessa tolleranza, la restituisco if ( abs( s_dAuxSurfTol - m_pSTM->GetTempParam()) < EPS_SMALL) return m_pSTM ; // altrimenti la cancello prima di ricalcolarla else { delete( m_pSTM) ; m_pSTM = nullptr ; } } else { // se con la stessa precisione di quella di visualizzazione restituisco quella if ( m_pSTMRefined != nullptr && s_dAuxSurfTol > m_pSTMRefined->GetTempParam() - EPS_SMALL) { m_pSTM = m_pSTMRefined->Clone() ; return m_pSTM ; } } // eseguo calcolo m_pSTM = GetApproxSurf( s_dAuxSurfTol, 10 * EPS_SMALL, false) ; ++nSurf ; if ( m_pSTM != nullptr) m_pSTM->SetTempParam( s_dAuxSurfTol) ; return m_pSTM ; } //---------------------------------------------------------------------------- const SurfTriMesh* SurfBezier::GetAuxSurfRefined( void) const { // la superficie deve essere validata if ( m_nStatus != OK) { ResetAuxSurf() ; return nullptr ; } // se già calcolata, la restituisco if ( m_pSTMRefined != nullptr) { // se con la stessa tolleranza o superiore alla precedente, la restituisco if ( s_dAuxSurfRefinedTol > m_pSTMRefined->GetTempParam() - EPS_SMALL) return m_pSTMRefined ; // altrimenti la cancello prima di ricalcolarla else { delete( m_pSTMRefined) ; m_pSTMRefined = nullptr ; } } else { // se con la stessa precisione di quella di visualizzazione restituisco quella if ( m_pSTM != nullptr && s_dAuxSurfRefinedTol > m_pSTM->GetTempParam() - EPS_SMALL) { m_pSTMRefined = m_pSTM->Clone() ; return m_pSTMRefined ; } } // eseguo calcolo m_pSTMRefined = GetApproxSurf( s_dAuxSurfRefinedTol, 10 * EPS_SMALL, true) ; if ( m_pSTMRefined != nullptr) m_pSTMRefined->SetTempParam( s_dAuxSurfRefinedTol) ; return m_pSTMRefined ; } #if SAVEFAILEDTRIANGULATION || SAVEFAILEDTREE static int nErr = 0 ; #endif //---------------------------------------------------------------------------- SurfTriMesh* SurfBezier::GetApproxSurf( double dTol, double dSideMin, bool bUpdateEdges) const { // la superficie deve essere validata if ( m_nStatus != OK) return nullptr ; // se c'è ausiliaria di visualizzazione con gli stessi parametri, ne restituisco una copia if ( m_pSTM != nullptr && abs( dTol - s_dAuxSurfTol) < EPS_SMALL && abs( dSideMin - 10 * EPS_SMALL) < EPS_SMALL) return m_pSTM->Clone() ; // se c'è ausiliaria e richiesta con gli stessi parametri, ne restituisco una copia if ( m_pSTMRefined != nullptr && abs( dTol - s_dAuxSurfRefinedTol) < EPS_SMALL && abs( dSideMin - 10 * EPS_SMALL) < EPS_SMALL) return m_pSTMRefined->Clone() ; // costruttore della superficie POLYLINEMATRIX vvPL ; POLYLINEMATRIX vvPL3d ; Tree Tree ; if ( ! Tree.SetSurf( this)) return nullptr ; BIPNTVECTOR vTrees ; Tree.GetIndependentTrees( vTrees) ; // resetto il vettore degli edge m_mCCEdge.clear() ; m_vCCLoop.clear() ; for ( int i = 0 ; i < (int) vTrees.size() ; ++ i) { Point3d ptMin = get<0>( vTrees[i]) ; Point3d ptMax = get<1>( vTrees[i]) ; Tree.SetSurf( this, ptMin, ptMax) ; if ( ! Tree.BuildTree( dTol, dSideMin)) { LOG_DBG_ERR( GetEGkLogger(), "ERROR : Bezier Surface parametric space couldn't be split in cells") ; #if SAVEFAILEDTREE SaveGeoObj( Clone(),"D:\\Temp\\bezier\\not_triangulated\\" + ToString( nErr) + ".nge") ; ++nErr ; #endif return nullptr ; } if ( ! Tree.GetPolygons( vvPL, vvPL3d, m_mCCEdge, m_vCCLoop, bUpdateEdges)) continue ; // aggiorno la chiusura della superficie m_bClosedU = m_bClosedU || Tree.IsClosedU() ; m_bClosedV = m_bClosedV || Tree.IsClosedV() ; } if ( vvPL.empty()) { LOG_DBG_ERR( GetEGkLogger(), "ERROR : Bezier Surface couldn't be triangulated") #if SAVEFAILEDTRIANGULATION SaveGeoObj( Clone(),"D:\\Temp\\bezier\\not_triangulated\\" + ToString( nErr) + ".nge") ; ++nErr ; #endif } StmFromTriangleSoup stmSoup ; if ( ! stmSoup.Start()) return nullptr ; // prendo i punti di ogni polyline dell'albero, li triangolo e li porto in 3d int c = 0 ; for ( POLYLINEVECTOR& vPL : vvPL) { PNTVECTOR vPnt ; INTVECTOR vTria ; Triangulate Tri ; if ( ! Tri.Make( vPL, vPnt, vTria)) { LOG_DBG_ERR( GetEGkLogger(), "ERROR : Triangulation failed in Bezier Surface") ; return nullptr ; } POLYLINEVECTOR vPL3d = vvPL3d[c] ; PNTVECTOR vPnt3d ; for ( int i = 0 ; i < int( vPL3d.size()) ; ++i) { PolyLine& pl3d = vPL3d[i] ; Point3d pt3d ; pl3d.GetFirstPoint( pt3d) ; if ( vPL3d.size() > 1) vPnt3d.push_back( pt3d) ; while ( pl3d.GetNextPoint( pt3d)) { vPnt3d.push_back( pt3d) ; } } // se ho ottenuto meno triangoli di quelli che avrei dovuto avere allora potrei aver avuto un problema in prossimità di un polo // se comunque ho corrispondenza tra vPnt e vPnt3d allora eseguo la trinagolazione in 3d int nTriaNumber = ( vPnt.size() - 2) + (2 * (vPL.size() - 1)) - (vPL.size() > 2 ? vPL.size() - 2 : 0) - (vPL.size() != 1 ? vPL.size() : 0) ; bool bTriangulationFailedIn2D = nTriaNumber != ( vTria.size()) / 3 ; //bool bMismatch2D3D = vPnt.size() != vPnt3d.size() ; bool bTriangulatedIn3D = false ; if ( bTriangulationFailedIn2D) { PNTVECTOR vPntBackup = vPnt ; INTVECTOR vTriaBackup = vTria ; bool bTriangulationSucceded = true ; if ( ! Tri.Make( vPL3d, vPnt, vTria)) bTriangulationSucceded = false ; bTriangulationSucceded = bTriangulationSucceded && ( vPnt.size() - 2) + (2 * (vPL.size() - 1)) - (vPL.size() > 2 ? vPL.size() - 2 : 0) - (vPL.size() != 1 ? vPL.size() : 0) == ( vTria.size() ) / 3 ; if ( bTriangulationFailedIn2D) { if ( bTriangulationSucceded) LOG_INFO( GetEGkLogger(), "Info : Last problem in MakeByEC23(1) RESOLVED") else LOG_INFO( GetEGkLogger(), "Info : This problem in MakeByEC23(1) is the same as the previous one") } if ( bTriangulationSucceded) { bTriangulatedIn3D = true ; // calcolo la normale della polyline per flippare eventualmente i triangoli se hanno la normale sbagliata PolyLine& pl3d = vPL3d[0] ; Plane3d plPlane ; double dArea = 0 ; pl3d.IsClosedAndFlat( plPlane, dArea) ; Vector3d vtN = plPlane.GetVersN() ; Triangle3d tria ; tria.Set( vPnt[vTria[0]], vPnt[vTria[1]], vPnt[vTria[2]]) ; if ( tria.GetN() * vtN < 0) reverse( vTria.begin(), vTria.end()) ; } else { vPnt = vPntBackup ; vTria = vTriaBackup ; } } // riordino il vettore dei punti su cui non ho fatto la triangolazione if ( vPL.size() == 2) { //if ( vPnt.size() != vPnt3d.size()) // return nullptr ; PNTVECTOR vPntOrd ; if ( bTriangulatedIn3D) { ReorderPntVector( vPL3d, true, vPnt, vPL, vPntOrd) ; vPnt3d = vPnt ; vPnt = vPntOrd ; } else { ReorderPntVector( vPL, false, vPnt, vPL3d, vPntOrd) ; vPnt3d = vPntOrd ; } } else if ( bTriangulatedIn3D) { if ( vPL.size() == 1) { vPnt3d = vPnt ; PNTVECTOR vPnt2d ; for ( int i = 0 ; i < int( vPL.size()) ; ++i) { PolyLine& pl = vPL[i] ; Point3d pt ; pl.GetFirstPoint( pt) ; //vPnt2D.push_back( pt) ; while ( pl.GetNextPoint( pt)) { vPnt2d.push_back( pt) ; } } vPnt = vPnt2d ; } else { PNTVECTOR vPntOrd ; ReorderPntEnhancedVector( vPL3d, true, vPnt, vPL, vPntOrd) ; vPnt3d = vPnt ; vPnt = vPntOrd ; } } // controllo che i due vettori vPnt e vPnt3d abbiano la stessa lunghezza, sennò vuol dire che nel vettore vPnt3d ho avuto dei Rejected e devo ricalcolarli // i punti in eccesso verranno poi scartati dalla trimesh if ( vPnt.size() != vPnt3d.size()) { vPnt3d.clear() ; for ( int i = 0 ; i < int( vPnt.size()) ; ++ i) { Point3d pt3d ; if ( ! GetPointD1D2( vPnt[i].x / SBZ_TREG_COEFF, vPnt[i].y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, pt3d)) return nullptr ; vPnt3d.push_back( pt3d) ; } } int nTria = int( vTria.size()) / 3 ; for ( int i = 0 ; i < nTria ; ++i) { if ( ! stmSoup.AddTriangle( vPnt3d[vTria[3*i]], vPnt3d[vTria[3*i+1]], vPnt3d[vTria[3*i+2]], vPnt[vTria[3*i]].x, vPnt[vTria[3*i]].y, vPnt[vTria[3*i+1]].x, vPnt[vTria[3*i+1]].y, vPnt[vTria[3*i+2]].x, vPnt[vTria[3*i+2]].y)) return nullptr ; } ++c ; } // termino if ( ! stmSoup.End()) return nullptr ; // restituisco return GetBasicSurfTriMesh( stmSoup.GetSurf()) ; } //---------------------------------------------------------------------------- bool SurfBezier::ReorderPntVector( const POLYLINEVECTOR& vPL, bool bTriangulatedIn3D, const PNTVECTOR& vPnt, const POLYLINEVECTOR& vPLToOrd, PNTVECTOR& vPntOrd) const { BOOLVECTOR vbPolyChecked( vPL.size()) ; fill( vbPolyChecked.begin(), vbPolyChecked.end(), false) ; for ( int p = 0 ; p < int(vPnt.size()) ; ++p) { Point3d pt = vPnt[p] ; int nInd = 0 ; int nPoly = 0 ; int nPoints = 0 ; bool bInverted = false ; for ( int poly = 0 ; poly < int( vPL.size()) ; ++poly ) { if ( vbPolyChecked[poly]) continue ; PolyLine pl = vPL[poly] ; nInd = 0 ; Point3d ptPoly ; pl.GetFirstPoint( ptPoly) ; bool bFound = false ; if ( AreSamePointExact( pt, ptPoly)){ nPoints = pl.GetPointNbr() ; nPoly = poly ; bFound = true ; pl.GetNextPoint( ptPoly) ; if ( ! AreSamePointExact( vPnt[p+1], ptPoly)) bInverted = true ; break ; } while ( pl.GetNextPoint( ptPoly) && ! bFound) { ++ nInd ; if ( AreSamePointExact( pt, ptPoly)) { nPoints = pl.GetPointNbr() ; nPoly = poly ; bFound = true ; pl.GetNextPoint( ptPoly) ; if ( ! AreSamePointExact( vPnt[p+1], ptPoly)) bInverted = true ; break ; } } if ( bFound) break ; } if ( nInd == 0) { Point3d ptPoly ; vPLToOrd[nPoly].GetFirstPoint( ptPoly) ; vPntOrd.push_back( ptPoly) ; while ( vPLToOrd[nPoly].GetNextPoint( ptPoly)) vPntOrd.push_back( ptPoly) ; } else if ( nInd == nPoints - 1 ) { Point3d ptPoly ; vPLToOrd[nPoly].GetLastPoint( ptPoly) ; vPntOrd.push_back( ptPoly) ; while ( vPLToOrd[nPoly].GetPrevPoint( ptPoly)) vPntOrd.push_back( ptPoly) ; } else { PNTVECTOR vPntToRotate ; Point3d ptPoly ; if ( ! bInverted) { vPLToOrd[nPoly].GetFirstPoint( ptPoly) ; vPntToRotate.push_back( ptPoly) ; while ( vPLToOrd[nPoly].GetNextPoint( ptPoly)) vPntToRotate.push_back( ptPoly) ; } else { vPLToOrd[nPoly].GetLastPoint( ptPoly) ; vPntToRotate.push_back( ptPoly) ; while ( vPLToOrd[nPoly].GetPrevPoint( ptPoly)) vPntToRotate.push_back( ptPoly) ; } vPntToRotate.pop_back() ; rotate( vPntToRotate.begin(), vPntToRotate.begin() + nInd, vPntToRotate.end()) ; vPntToRotate.push_back( vPntToRotate[0]) ; vPntOrd.insert( vPntOrd.end(), vPntToRotate.begin(), vPntToRotate.end()) ; } vbPolyChecked[nPoly] = true ; p += nPoints - 1; } return true ; } //---------------------------------------------------------------------------- bool SurfBezier::ReorderPntEnhancedVector( const POLYLINEVECTOR& vPL, bool bTriangulatedIn3D, const PNTVECTOR& vPnt, const POLYLINEVECTOR& vPLToOrd, PNTVECTOR& vPntOrd) const { vPntOrd.clear() ; // costruisco il vettore dei punti da ordinare PNTVECTOR vPntPolyToOrd ; for ( int i = 0 ; i < int( vPLToOrd.size()) ; ++i) { Point3d pt ; vPLToOrd[i].GetFirstPoint( pt) ; vPntPolyToOrd.push_back( pt) ; while ( vPLToOrd[i].GetNextPoint( pt)) vPntPolyToOrd.push_back( pt) ; } // costruisco il vettore con i punti delle polyline in ordine PNTVECTOR vPntPoly ; for ( int i = 0 ; i < int( vPL.size()) ; ++i) { Point3d pt ; vPL[i].GetFirstPoint( pt) ; vPntPoly.push_back( pt) ; while ( vPL[i].GetNextPoint( pt)) vPntPoly.push_back( pt) ; } BOOLVECTOR vbPntChecked( vPnt.size()) ; fill( vbPntChecked.begin(), vbPntChecked.end(), false) ; // confronto questo vettore con il vettore dei punti in ordine sparso for ( int p = 0 ; p < int( vPntPoly.size()) ; ++p) { Point3d pt = vPntPoly[p] ; for ( int t = 0 ; t < int( vPnt.size()) ; ++t) { if ( vbPntChecked[t]) continue ; Point3d ptToCheck = vPnt[t] ; if ( AreSamePointExact( pt, ptToCheck)) { vbPntChecked[t] = true ; vPntOrd.push_back( vPntPolyToOrd[p]) ; } } } // applico la stessa trasformazione al vettore dei punti delle polyline vPLToOrd return true ; } //---------------------------------------------------------------------------- bool SurfBezier::GetLeaves( vector>& vLeaves, bool bRefined) const { Tree Tree ; if ( ! Tree.SetSurf( this)) return false ; BIPNTVECTOR vTrees ; Tree.GetIndependentTrees( vTrees) ; for ( int i = 0 ; i < int( vTrees.size()) ; ++ i) { Point3d ptMin = get<0>( vTrees[i]) ; Point3d ptMax = get<1>( vTrees[i]) ; Tree.SetSurf( this, ptMin, ptMax) ; if ( ! bRefined) Tree.BuildTree( s_dAuxSurfTol, 10 * EPS_SMALL) ; else Tree.BuildTree( s_dAuxSurfRefinedTol, 10 * EPS_SMALL) ; vector vCells ; Tree.GetLeaves( vCells) ; for ( int k = 0 ; k < int( vCells.size()) ; ++ k) { vLeaves.emplace_back( vCells[k].m_nId, vCells[k].GetBottomLeft(), vCells[k].GetTopRight()) ; } } 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 { if ( m_pSTM != nullptr) delete( m_pSTM) ; m_pSTM = nullptr ; if ( m_pSTMRefined != nullptr) delete( m_pSTMRefined) ; m_pSTMRefined = nullptr ; m_mCCEdge.clear() ; m_vCCLoop.clear() ; } //---------------------------------------------------------------------------- void SurfBezier::ResetTrimRegion( void) { if ( m_pTrimReg != nullptr) delete( m_pTrimReg) ; m_pTrimReg = nullptr ; ResetAuxSurf() ; m_bTrimmed = false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; m_mCCEdge.clear() ; m_vCCLoop.clear() ; } //---------------------------------------------------------------------------- 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 && ! bModifyOrig) return false ; 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 ) dUVTest = m_nSpanU ; } else { if ( dUVTest < 0) dUVTest = 0 ; else if ( dUVTest > m_nSpanV ) dUVTest = m_nSpanV ; } if ( bModifyOrig) dUV = dUVTest ; if ( dUVCopy != nullptr) *dUVCopy = dUVTest ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::UnprojectCurveFromStm( const ICurveComposite* pCC, ICRVCOMPOPVECTOR& vpCC, const Plane3d* pPlCut) const { // calcolo la superficie se non già calcolata if ( m_pSTMRefined == nullptr) GetAuxSurfRefined() ; // se necessario calcolo i poli if ( m_vbPole.empty()) CalcPoles() ; // 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 ; pt2D *= SBZ_TREG_COEFF ; vbThroughEdge.push_back( bThroughEdge) ; // aggiungo tutti i successivi BIPNTVECTOR vBPnt ; // per il primo punto non mi interessa sapere se è passato attraverso un edge, tanto ha già le coordinate parametriche giuste bThroughEdge = false ; int nRejected = 0 ; for ( int i = 0 ; i < pCC->GetCurveCount() ; ++i) { const ICurve* pCrv = pCC->GetCurve( i) ; Point3d pt3DPrev = pt3D ; Point3d pt2DPrev = pt2D ; bool bPrevIsPole = false ; bool bHorizontalPole = true ; if ( bThroughEdge) { // devo cambiare le coordinate di pt2DPrev per periodicità // capisco su quale lato è e lo porto sul lato opposto 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 ; } // verifico se il punto era in un polo if ( ( m_vbPole[0] && m_nSpanV * SBZ_TREG_COEFF - pt2DPrev.y < 1 ) || ( m_vbPole[2] && pt2DPrev.y < 1 ) ) { bPrevIsPole = true ; bHorizontalPole = true ; } if ( ( m_vbPole[1] && pt2DPrev.x < 1 ) || ( m_vbPole[3] && m_nSpanU * SBZ_TREG_COEFF - pt2DPrev.x < 1 ) ) { bPrevIsPole = true ; bHorizontalPole = false ; } } pCrv->GetEndPoint( pt3D) ; if ( ! UnprojectPoint( pt3D, pt2D, pt3DPrev, &bThroughEdge, pPlCut)) return false ; pt2D *= SBZ_TREG_COEFF ; // se il precedente era passato attraverso l'edge ed era un edge di polo allora devo sistemare le coordinate if ( bPrevIsPole) { // se il punto precedente era di polo allora devo correggere le sue coordinate 2D if ( bHorizontalPole) pt2DPrev.x = pt2D.x ; else if ( ! bHorizontalPole) pt2DPrev.y = pt2D.y ; } Vector3d vtDir = pt2D - pt2DPrev ; vtDir.Normalize() ; // se mi accorgo che sto per tracciare un taglio lungo un bordo posso semplicemente evitarlo double dDelta = 10 * EPS_SMALL ; if ( ( 1 - abs( vtDir.x) < SQ_EPS_SMALL && ( pt2D.y < dDelta || m_nSpanV * SBZ_TREG_COEFF - pt2D.y < dDelta)) || // parallelo agli edge 0 e 2 e su uno di questi ( 1 - abs( vtDir.y) < SQ_EPS_SMALL && ( pt2D.x < dDelta || m_nSpanU * SBZ_TREG_COEFF - pt2D.x < dDelta))) { // parallello agli edge 1 e 3 e su uno di questi ++ nRejected ; continue ; } double dParamH, dParamL ; dParamH = m_nSpanV * SBZ_TREG_COEFF ; dParamL = m_nSpanU * SBZ_TREG_COEFF ; if ( bThroughEdge && vbThroughEdge.back()) { // sia questo punto che il precedente sono su un edge, ma il segmento che li unisce non è parallelo ad un edge // ( i punti per periodicità hanno coordinate diverse anche se dovrebbero averle uguali, quindi il segmento attraversa tutto lo spazio parametrico, anche se in realtà è lungo il bordo di chiusura) // 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 ; } } // se il primo e l'ultimo punto sono entrambi su edge e indicano un attraversamento, controllo che siano da lati opposti del parametrico // altrimenti li porto dal lato del punto successivo/precedente if ( bThroughEdge && pCC->IsClosed() && i == pCC->GetCurveCount() - 1 && vbThroughEdge[0] && ( m_bClosedU || m_bClosedV)) { if ( m_bClosedU) { if ( abs( vBPnt[0].first.x - vBPnt[0].second.x) > 0.5 * dParamL){ if (dParamL - vBPnt[0].second.x < 0.5 * dParamL) vBPnt[0].first.x = dParamL ; else vBPnt[0].first.x = 0 ; } if ( abs( pt2D.x - pt2DPrev.x) > 0.5 * dParamL){ if ( dParamL - pt2DPrev.x < 0.5 * dParamL) pt2D.x = dParamL ; else pt2D.x = 0 ; } } if ( m_bClosedV) { if ( abs( vBPnt[0].first.y - vBPnt[0].second.y) > 0.5 * dParamH){ if (dParamH - vBPnt[0].second.y < 0.5 * dParamH) vBPnt[0].first.y = dParamH ; else vBPnt[0].first.y = 0 ; } if ( abs( pt2D.y - pt2DPrev.y) > 0.5 * dParamH){ if ( dParamH - pt2DPrev.y < 0.5 * dParamH) pt2D.y = dParamH ; else pt2D.y = 0 ; } } } 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()) ; if ( IsNull( pCC2D)) return false ; for ( int i = 0 ; i < int( vId.size()) ; ++ i) { // creo un segmento di retta PtrOwner pLine( CreateCurveLine()) ; if ( IsNull( pLine)) 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( Release( 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 { 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) ; } } ; //---------------------------------------------------------------------------- ISurfFlatRegion* SurfBezier::CreateTrimRegionFromCuts( ICRVCOMPOPOVECTOR& vpCCOpen, ICRVCOMPOPOVECTOR& vpCCClosed) const { // comincio a creare la superficie aggiungendo i tagli aperti ai bordi attualmente esistenti SurfFlatRegionByContours sfrContour ; if ( int(vpCCOpen.size()) != 0 ) { // 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 flat 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()) ; 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 ; bool bStartFound = false ; bool bEndFound = false ; 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 ; bStartFound = true ; if ( bFirstCurveOfEdge){ // salvo l'inizio del taglio che è la prima curva di questa curva compo tiFirstInters = pair.first ; bFirstCurveOfEdge = false ; } } else { tiEnd = pair.first ; bEndFound = true ; } } } } if ( ! bEndFound || ! bStartFound) return nullptr ; // 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 = INFINITO ; double dEndCurrentCut = 0 ; 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 = INFINITO ; 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; } } // devo verificare se devo aggiungere anche il bordo ( basta verificare se il primo loop chiuso è CCW) double dArea ; if ( ! vpCCClosed.empty()) { vpCCClosed[0]->GetAreaXY( dArea) ; if ( dArea < EPS_SMALL ) { PolyLine plEdge ; plEdge.AddUPoint( 0, ORIG) ; plEdge.AddUPoint( 1, Point3d( m_nSpanU * SBZ_TREG_COEFF, 0)) ; plEdge.AddUPoint( 2, Point3d( m_nSpanU * SBZ_TREG_COEFF, m_nSpanV * SBZ_TREG_COEFF)) ; plEdge.AddUPoint( 3, Point3d( 0, m_nSpanV * SBZ_TREG_COEFF)) ; plEdge.Close() ; vpCCClosed.emplace_back( CreateCurveComposite()) ; vpCCClosed.back()->FromPolyLine( plEdge) ; } } // aggiungo i loop chiusi for ( int i = 0 ; i < int( vpCCClosed.size()); ++i ) sfrContour.AddCurve( Release( vpCCClosed[i])) ; PtrOwner pSfrTrimReg( sfrContour.GetSurf()) ; return Release( pSfrTrimReg) ; } //---------------------------------------------------------------------------- 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 // N.B. : questa funzione non prevede poli interni alla superficie, ma solo lati che sono collassati in poli!!!! // se necessario calcolo i poli if ( m_vbPole.empty()) CalcPoles() ; PNTVECTOR vPnt ; BIPNTVECTOR vBPnt ; TRIA3DVECTOR vTria ; IntersPlaneSurfTm( plPlane, *GetAuxSurfRefined(), vPnt, vBPnt, vTria) ; // concateno le curve 3d ChainCurves chainC ; double dToler = 10 * 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 ; } //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 loop derivati dai triangoli for ( int i = 0 ; i < int( vPLTria.size()); ++i ) { vpCCClosed.emplace_back() ; vpCCClosed.back()->FromPolyLine( vPLTria[i]) ; } // ora posso chiamare la costruzione dello spazio parametrico trimmato PtrOwner pSFR( CreateTrimRegionFromCuts( vpCCOpen, vpCCClosed)) ; 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) { for ( int l = 0 ; l < pSFR->GetLoopCount( c); ++l) { PtrOwner pCrv( pSFR->GetLoop( c, 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 ; } //// taglio la trimesh col piano // questa operazione richiederebbe di ricavare anche i nuovi edge 2D e edge3D, che normalmente sarebbero calcolati durante la GetAuxSurf dal tree //m_pSTM->Cut( plPlane, bSaveOnEq) ; // 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 { // calcolo la superficie se non già calcolata if ( m_pSTMRefined == nullptr) GetAuxSurfRefined() ; ptSP = ORIG ; if ( bThroughEdge != nullptr) *bThroughEdge = false ; // dato un punto sulla trimesh ausiliaria, ne ricavo le coordinate parametriche const ISurfTriMesh* pSurfTm = GetAuxSurfRefined() ; int nTriaIndex = nT ; INTVECTOR vnT ; if ( nT == -1) { DistPointSurfTm distPtStm0( ptI, *pSurfTm) ; distPtStm0.GetMinDistTriaIndex( nTriaIndex) ; distPtStm0.GetMinDistTriaIndices( vnT) ; } // 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 ; bool bNearPole = false ; // devo capire se il triangolo di riferimento ha un vertice in un polo INTVECTOR vInters(4) ; fill( vInters.begin(), vInters.end(), 0) ; // se necessario calcolo i poli if ( m_vbPole.empty()) CalcPoles() ; 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 ; if ( vInters[c] == 1) bIsPole = true ; } else { vInters[c] = m_mCCEdge[c][i]->IsPointOn(ptI) ? 1 : 0 ; } } } // se ho tre intersezioni vuol dire che un lato è collassato in un punto e il punto di cui voglio la controimmagine è esattamente nel polo // oppure sono su un lato di chiusura if ( bIsPole || ( m_bClosedU && ( vInters[1] == 1 || vInters[3] == 1)) || ( m_bClosedV && ( vInters[0] == 1 || vInters[2] == 1))) { // 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) * 10 * EPS_SMALL ; // ricalcolo il triangolo di appartenenza int nTriaOld = nTriaIndex ; DistPointSurfTm dPtStm( ptI2, *pSurfTm) ; dPtStm.GetMinDistTriaIndex( nTriaIndex) ; // se ho trovato un nuovo triangolo, controllo che questo fosse nella lista dei triangoli equidistanti dal punto originale // sennò ripeto il conto con meno scostamento if ( nTriaOld != nTriaIndex) { auto iter = find( vnT.begin(), vnT.end(),nTriaIndex) ; int nIdTria = distance( vnT.begin(), iter) ; if ( nIdTria > ssize( vnT) - 1) { ptI2 = ptI + ( ptIPrevOrNext - ptI) * 5 * EPS_SMALL ; DistPointSurfTm dPtStm2( ptI2, *pSurfTm) ; dPtStm2.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) ; vPtPa[0] *= SBZ_TREG_COEFF ; pSurfTm->GetVertexParam( nVert[1], vPtPa[1].x, vPtPa[1].y) ; vPtPa[1] *= SBZ_TREG_COEFF ; pSurfTm->GetVertexParam( nVert[2], vPtPa[2].x, vPtPa[2].y) ; vPtPa[2] *= SBZ_TREG_COEFF ; PNTVECTOR vPT(3) ; pSurfTm->GetVertex( nVert[0], vPT[0]) ; pSurfTm->GetVertex( nVert[1], vPT[1]) ; pSurfTm->GetVertex( nVert[2], vPT[2]) ; // devo capire se il triangolo ha un vertice su un polo int nVertOnPole = -1 ; int nEdge = -1 ; // do per scontato che al più un vertice possa essere in un polo if ( m_vbPole[0] || m_vbPole[2]) { for ( int p = 0 ; p < 3 ; ++p) { if ( ( m_vbPole[0] && vPtPa[p].y > m_nSpanV * SBZ_TREG_COEFF - EPS_SMALL ) ) { bNearPole = true ; nVertOnPole = p ; nEdge = 0 ; } else if ( m_vbPole[2] && vPtPa[p].y < 0 + EPS_SMALL) { bNearPole = true ; nVertOnPole = p ; nEdge = 2 ; } } } else if ( m_vbPole[1] || m_vbPole[3]) { for ( int p = 0 ; p < 3 ; ++p) { if ( ( m_vbPole[3] && vPtPa[p].x > m_nSpanU * SBZ_TREG_COEFF - EPS_SMALL)) { bNearPole = true ; nVertOnPole = p ; nEdge = 3 ; } else if ( m_vbPole[1] && vPtPa[p].x < 0 + EPS_SMALL) { bNearPole = true ; nVertOnPole = p ; nEdge = 1 ; } } } // se la superficie è chiusa controllo se devo tenere conto della periodicità nel prendere le coordinate parametriche dei vertici double dParamH = 0, dParamL = 0 ; INTVECTOR vOn(3) ; fill( vOn.begin(), vOn.end(), -1) ; bool bOneVertexOnClosureButNotPole = false ; 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 = 0 ; 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à // do per scontato che se la superficie è chiusa lungo un parametro i lati di chiusura non siano dei poli 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 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]->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ù di un vertice sul lato oppure se ne ho solo uno ma non è sul polo allora procedo alla correzione delle coordinate bOneVertexOnClosureButNotPole = (vOn[0] > 0 && 0 != nVertOnPole) || (vOn[1] > 0 && 1 != nVertOnPole) || (vOn[2] > 0 && 2 != nVertOnPole) ; if ( vOn[0] * vOn[1] * vOn[2] < 0 || bOneVertexOnClosureButNotPole) { double dRightX = 0 ; // 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 ( abs(vPtPa[p].x - dRightX) > SBZ_TREG_COEFF / 2) { 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) { 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]->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 bOneVertexOnClosureButNotPole = (vOn[0] > 0 && 0 != nVertOnPole) || (vOn[1] > 0 && 1 != nVertOnPole) || (vOn[2] > 0 && 2 != nVertOnPole) ; if ( vOn[0] * vOn[1] * vOn[2] < 0 || bOneVertexOnClosureButNotPole) { double dRightY = 0 ; // 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 ( abs(vPtPa[p].y - dRightY) > SBZ_TREG_COEFF / 2) { if ( vPtPa[p].y < EPS_SMALL) vPtPa[p].y = dParamH ; else vPtPa[p].y = 0 ; } } } } } else { for ( int p = 0 ; p < 3; ++p ) { //scorro sugli edge for (int ed = 0 ; ed < 4 ; ++ed) { // scorro sui tratti che compongono l'edge for ( int i = 0 ; i < int( m_mCCEdge[ed].size()) ; ++i) { if ( m_mCCEdge[ed][i]->IsPointOn( vPT[p]) && vOn[p] == -1) vOn[p] = ed ; } } } // anche se non ho un triangolo molto stretchato potrei avere il punto di polo con coordinate parametriche sballate bOneVertexOnClosureButNotPole = ((m_bClosedU && (vOn[0] == 1 || vOn[0] == 3)) && 0 != nVertOnPole) || ((m_bClosedU && (vOn[1] == 1 || vOn[1] == 3)) && 1 != nVertOnPole) || ((m_bClosedU && (vOn[2] == 1 || vOn[2] == 3)) && 2 != nVertOnPole) || ((m_bClosedV && (vOn[0] == 0 || vOn[0] == 2)) && 0 != nVertOnPole) || ((m_bClosedV && (vOn[1] == 0 || vOn[1] == 2)) && 1 != nVertOnPole) || ((m_bClosedV && (vOn[2] == 0 || vOn[2] == 2)) && 2 != nVertOnPole) ; } } // devo anche tener conto della possibilità che i lati siano collassati in poli if ( bIsPole || bNearPole) { // controllo di aver trovato il lato collassato in polo su cui sta il vertice del triangolo o il punto di intersezione if ( nEdge == -1) return false ; // trovo la coordinata giusta da tenere ( x o y a seconda dell'edge) double dRightX = 0, dRightY = 0 ; for ( int p = 0 ; p < 3; ++p) { if ( p != nVertOnPole) { if ( nEdge == 0 || nEdge == 2) { dRightY = nEdge == 0 ? dParamH : 0 ; if ( vPtPa[p].x > dRightX ) dRightX= vPtPa[p].x ; // se un vertice è su un lato di chiusura, visto che sono vicino ad un polo, mi accerto che un lato del triangolo sia SUL lato di chiusura if ( m_bClosedU && bOneVertexOnClosureButNotPole) { dRightX = ( dParamL - vPtPa[p].x > vPtPa[p].x ? 0 : dParamL) ; break ; } } else if ( nEdge == 1 || nEdge == 3) { // se un vertice è su un lato di chiusura, visto che sono vicino ad un polo, mi accerto che un lato del triangolo sia SUL lato di chiusura dRightX = nEdge == 1 ? 0 : dParamL ; if ( vPtPa[p].y > dRightY ) dRightY = vPtPa[p].y ; if ( m_bClosedV && bOneVertexOnClosureButNotPole) { dRightY = ( dParamH - vPtPa[p].y > vPtPa[p].y ? 0 : dParamH) ; break ; } } } } // correggo le coordinate del punto sull'edge di polo for ( int p = 0 ; p < 3 ; ++p) { if ( p == nVertOnPole) { 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 == IntLineTriaType::ILTT_VERT && ! 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] ; // restituisco il punto nel parametrico riscalato ptSP / SBZ_TREG_COEFF ; 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 ; // restituisco il punto nel parametrico riscalato IncreaseUV( ptSP.x, ptParam.x() / SBZ_TREG_COEFF, true) ; IncreaseUV( ptSP.y, ptParam.y() / SBZ_TREG_COEFF, false) ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::UnprojectPoint( const Point3d& pt3D, Point3d& ptParam, const Point3d& ptIPrev, bool* bThroughEdge, const Plane3d* pPlCut) const { // calcolo la superficie se non già calcolata if ( m_pSTMRefined == nullptr) GetAuxSurfRefined() ; // se necessario calcolo i poli if ( m_vbPole.empty()) CalcPoles() ; // 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, *GetAuxSurfRefined()) ; Point3d ptI ; dptSurfTm.GetMinDistPoint( ptI) ; if ( ! UnprojectPointFromStm( -1, ptI, ptParam, IntLineTriaType::ILTT_IN, ptIPrev, bThroughEdge)) return false ; Point3d ptBez ; GetPointD1D2( ptParam.x, ptParam.y, 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 ; double dUh ; IncreaseUV( ptParam.x, dh, true, &dUh, false) ; GetPointD1D2( dUh, ptParam.y, 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 ; double dVh ; IncreaseUV( ptParam.y, dh, false, &dVh, false) ; GetPointD1D2( ptParam.x, dVh, 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( dfdU * dfdU + dfdV * dfdV) ; vtDir.Set( - dfdU, - dfdV, 0) ; if ( ! vtDir.Normalize() ) vtDir.Set( - dfdU / dSSum, - dfdV / dSSum, 0) ; if ( ! vtDir.IsValid()) return false ; 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, ptParam.y, 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 < EPS_SMALL) ptParam.x = 0 ; else if ( abs( m_nSpanU - ptParam.x) < EPS_SMALL) ptParam.x = m_nSpanU ; } if ( m_bClosedV) { if ( ptParam.y < EPS_SMALL) ptParam.y = 0 ; else if ( abs( m_nSpanV - ptParam.y) < EPS_SMALL) ptParam.y = m_nSpanV ; } } return nCount != 100 || (dDistNew < dDistPre ? dDistNew : dDistPre) < 10 * EPS_SMALL ; } //---------------------------------------------------------------------------- bool SurfBezier::CalcPoles( void) const { if ( ! m_vbPole.empty()) return true ; // la funzione identifica se degli edge della superficie non trimmata 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) const { // se necessario calcolo i poli if ( m_vbPole.empty()) CalcPoles() ; // 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_pSTMRefined == nullptr) GetAuxSurfRefined() ; // 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 vCC.emplace_back( CreateCurveComposite()) ; if ( ! m_bClosedV ) { if ( ! m_vbPole[0]) vCC.back()->AddCurve( GetSingleEdge3D( bLineOrBezier, 0)) ; } if ( ! m_bClosedU ) { if ( ! m_vbPole[1]) vCC.back()->AddCurve( GetSingleEdge3D( bLineOrBezier, 1)) ; } if ( ! m_bClosedV ) { if ( ! m_vbPole[2]) { // se superficie è chiusa lungo il parametro U, ma non lo è lungo il parametro V allora avrò due curve separate( un cilindro) if ( m_bClosedU) vCC.emplace_back( CreateCurveComposite()) ; vCC.back()->AddCurve( GetSingleEdge3D( bLineOrBezier, 2)) ; } } if ( ! m_bClosedU ) { if ( ! m_vbPole[3]) { // se superficie è chiusa lungo il parametro V, ma non lo è lungo il parametro U allora avrò due curve separate( un cilindro) if ( m_bClosedV) vCC.emplace_back( CreateCurveComposite()) ; vCC.back()->AddCurve( GetSingleEdge3D( bLineOrBezier, 3)) ; } } } // 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( *m_vCCLoop[l], *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 ( auto& 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 ( auto& 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 { // nEdge indica l'edge da restituire // gli edge sono numerati a pratire dal bordo superiore dello spazio parametrico: 0 - Top, 1 - Left, 2 - Bottom, 3 - Right if ( m_mCCEdge.empty() && bLineOrBezier) GetAuxSurfRefined() ; // 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)) ; } pCrvCompo->Invert() ; } 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)) ; } pCrvCompo->Invert() ; } 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 ; } //---------------------------------------------------------------------------- bool SurfBezier::IsPlanar( void) const { if ( m_nIsPlanar != NOT_CALCULATED) { switch ( m_nIsPlanar ) { case PLANAR_SURF : return true ; break ; case NOT_PLANAR_SURF : return false ; break ; } } PolyLine plApprox ; Plane3d plPlane ; if ( ! m_bTrimmed) { // costruisco il contorno della superficie unendo gli edge e chiedo se la polyline è piana. PtrOwner pCCEdge( GetSingleEdge3D( false, 0)) ; pCCEdge->AddCurve( GetSingleEdge3D( false, 1)) ; pCCEdge->AddCurve( GetSingleEdge3D( false, 2)) ; pCCEdge->AddCurve( GetSingleEdge3D( false, 3)) ; pCCEdge->ApproxWithLines( 0.01, 15, 0, plApprox) ; if ( ! plApprox.IsFlat( plPlane, 2 * EPS_SMALL)){ m_nIsPlanar = 0 ; return false ; } // in questo caso se è grado 1 in U e V e ho un unica Patch allora sono sicuro sia piana if ( m_nDegU == 1 && m_nSpanU == 1 && m_nDegV == 1 && m_nSpanV == 1) { m_nIsPlanar = 1 ; return true ; } } else { Point3d ptCtrl ; bool bOk = true ; plApprox.AddUPoint( 0, GetControlPoint(0, &bOk)) ; plApprox.AddUPoint( 1, GetControlPoint(m_nDegU * m_nSpanU, &bOk)) ; plApprox.AddUPoint( 2, GetControlPoint(( m_nDegU * m_nSpanU + 1) * ( m_nDegV * m_nSpanV + 1), &bOk)) ; plApprox.AddUPoint( 3, GetControlPoint(( m_nDegU * m_nSpanU + 1) * ( m_nDegV * m_nSpanV), &bOk)) ; } double dULast ; plApprox.GetLastU( dULast) ; ++ dULast ; // altrimenti devo verificare anche all'interno della superficie, prendendo dei punti campione DBLVECTOR vSampling { 0.2, 0.4, 0.6, 0.8} ; for ( double i : vSampling) { for ( double j : vSampling) { Point3d ptBez ; GetPointD1D2( i * m_nSpanU, j * m_nSpanV, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBez) ; if ( plApprox.AddUPoint( dULast, ptBez)) ++ dULast ; } } plPlane.Reset() ; if ( plApprox.IsFlat( plPlane, 2 * EPS_SMALL)) { m_nIsPlanar = 1 ; return true ; } // nel dubbio restituisco false m_nIsPlanar = 0 ; return false ; } //---------------------------------------------------------------------------- bool SurfBezier::CreateByFlatContour( const PolyLine& PL) { Plane3d plPlane ; double dArea = 0 ; if ( ! PL.IsClosedAndFlat( plPlane, dArea, EPS_SMALL)) return false ; // porto la polyline nel frame del suo piano per trovare gli estremi della sua box in quel piano Frame3d frContour ; frContour.Set( plPlane.GetPoint(), plPlane.GetVersN()) ; PolyLine plFlat = PL ; plFlat.ToLoc( frContour) ; // creo una superficie piana grande come il box della curva nel suo piano e poi la trimmo // recupero il box BBox3d bboxContour ; plFlat.GetLocalBBox( bboxContour) ; // inizializzo la superficie come una bezier di primo grado formata da una sola patch int nDegU = 1, nDegV = 1, nSpanU = 1, nSpanV = 1 ; bool bRat = false ; Init( nDegU, nDegV, nSpanU, nSpanV, bRat) ; // i punti di controllo sono i quattro vertici della bbox proiettata sul piano della polyline double dPlus = 1 ; bboxContour.Expand( dPlus, dPlus, 0) ; Point3d ptBL = bboxContour.GetMin() ; Point3d ptTR = bboxContour.GetMax() ; Point3d ptTl = Point3d( ptBL.x, ptTR.y) ; Point3d ptBr = Point3d( ptTR.x, ptBL.y) ; ptBL.ToGlob( frContour) ; ptTR.ToGlob( frContour) ; ptTl.ToGlob( frContour) ; ptBr.ToGlob( frContour) ; SetControlPoint( 0, ptBL) ; SetControlPoint( 1, ptBr) ; SetControlPoint( 2, ptTl) ; SetControlPoint( 3, ptTR) ; //// calcolo il corrispondente parametrico del contorno PtrOwner pCCContour( CreateCurveComposite()) ; if ( IsNull( pCCContour) || ! pCCContour->FromPolyLine( PL) || ! pCCContour->IsValid()) return false ; // calcolo gli eventuali poli, necessari per poter chiamare le funzioni di unproject CalcPoles() ; ICRVCOMPOPOVECTOR vCCOpen ; ICRVCOMPOPOVECTOR vCCClosed ; AddCurveCompoToCuts( pCCContour, vCCOpen, vCCClosed) ; // creo la regione di trim dai loop di trim PtrOwner pSfrTrim( CreateTrimRegionFromCuts( vCCOpen, vCCClosed)) ; if ( IsNull( pSfrTrim) || ! pSfrTrim->IsValid()) return false ; SetTrimRegion( *pSfrTrim) ; // aggiorno lo stato m_nStatus = OK ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::CreateByRegion( const POLYLINEVECTOR& vPL) { // la regione passata è riferita al parametrico di una superficie quadrata. // La superficie viene creata come se fosse una flatregion a partire dai loop passati. // le polyline in input devono essere già ordinate per area e orientate con il verso giusto ( tenendo conto di chunk e isole) // la prima polyline quindi è il loop esterno del chunk più grande Plane3d plPlane ; double dArea = 0 ; if ( ! vPL[0].IsClosedAndFlat(plPlane, dArea, EPS_SMALL) ) return false ; // porto la polyline nel frame del suo piano per trovare gli estremi della sua box in quel piano Frame3d frContour ; frContour.Set( plPlane.GetPoint(), plPlane.GetVersN()) ; // creo una superficie piana grande come il box della curva nel suo piano e poi la trimmo POLYLINEVECTOR vPLFlat = vPL ; BBox3d bboxContour ; for ( int i = 0 ; i < int( vPLFlat.size()) ; ++i) { // porto le polilinee nel riferimento del loro piano e ne recupero il box vPLFlat[i].ToLoc(frContour) ; BBox3d bboxLoop ; vPLFlat[i].GetLocalBBox(bboxLoop) ; bboxContour.Add( bboxLoop) ; } // inizializzo la superficie come una bezier di primo grado formata da una sola patch int nDegU = 1, nDegV = 1, nSpanU = 1, nSpanV = 1 ; bool bRat = false ; Init( nDegU, nDegV, nSpanU, nSpanV, bRat) ; // i punti di controllo sono i quattro vertici della bbox ( che è piana) double dPlus = 1 ; bboxContour.Expand( dPlus, dPlus, 0) ; Point3d ptBL = bboxContour.GetMin() ; Point3d ptTR = bboxContour.GetMax() ; Point3d ptTl = Point3d( ptBL.x, ptTR.y) ; Point3d ptBr = Point3d( ptTR.x, ptBL.y) ; ptBL.ToGlob( frContour) ; ptTR.ToGlob( frContour) ; ptTl.ToGlob( frContour) ; ptBr.ToGlob( frContour) ; SetControlPoint( 0, ptBL) ; SetControlPoint( 1, ptBr) ; SetControlPoint( 2, ptTl) ; SetControlPoint( 3, ptTR) ; // creo i vettori dei tagli aperti e chiusi CalcPoles() ; ICRVCOMPOPOVECTOR vCCOpen ; ICRVCOMPOPOVECTOR vCCClosed ; for ( int i = 0 ; i < int( vPL.size()) ; ++i) { ICurveComposite* pCC( CreateBasicCurveComposite()) ; if ( pCC == nullptr || ! pCC->FromPolyLine( vPL[i]) || ! pCC->IsValid()) return false ; if ( ! AddCurveCompoToCuts( pCC, vCCOpen, vCCClosed)) return false ; // o metto un continue? } // unisco i tagli e creo la regione di trim PtrOwner pSfrTrim( CreateTrimRegionFromCuts( vCCOpen, vCCClosed)) ; if ( IsNull( pSfrTrim) || ! pSfrTrim->IsValid()) return false ; SetTrimRegion( *pSfrTrim) ; //if ( plPlane.GetVersN().z < EPS_SMALL) // Invert() ; // aggiorno lo stato m_nStatus = OK ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::CreateByExtrusion( const ICurve* pCrv, const Vector3d& vtExtr) { if ( pCrv == nullptr) return false ; CurveComposite CC ; // se composita verifico che curve siano con lo stesso grado e uniformi come tipo bool bRat = false ; int nDegU = 3 ; if ( pCrv->GetType() != CRV_COMPO) { CC.AddCurve( CurveToBezierCurve( pCrv, nDegU, bRat)) ; } else { const ICurveComposite* pCCOrig = GetCurveComposite( pCrv) ; for ( int i = 0 ; i < pCCOrig->GetCurveCount() ; ++i) { if ( pCCOrig->GetCurve( i)->GetType() != CRV_BEZIER) CC.AddCurve( CurveToBezierCurve( pCCOrig->GetCurve(i), nDegU, bRat)) ; else CC.AddCurve( EditBezierCurve( GetCurveBezier( pCCOrig->GetCurve(i)), nDegU, bRat)) ; } } if ( CC.GetCurveCount() == 0 || ! CC.IsValid()) return false ; // riempio la matrice dei punti di controllo // parto dalla curva che sto estrudendo e mi alzo progressivamente seguendo il vettore vtExtr int nDegV = 1 ; int nSpanU = CC.GetCurveCount() ; int nSpanV = 1 ; Init( nDegU, nDegV, nSpanU, nSpanV, bRat) ; for ( int k = 0 ; k < nSpanU ; ++k) { const ICurveBezier* pCrvBezier = GetCurveBezier( CC.GetCurve( k)) ; for ( int i = 0 ; i < nDegU + 1 ; ++i) { if ( k != 0 && i == 0) continue ; Point3d ptCtrl = pCrvBezier->GetControlPoint( i) ; int nInd = k * nDegU + i ; if ( bRat) { double dW = pCrvBezier->GetControlWeight( i) ; SetControlPoint( nInd, ptCtrl, dW) ; } else SetControlPoint( nInd, ptCtrl) ; } } Vector3d vtStep = vtExtr / nDegV ; // calcolo il vettore di progressione del singolo step per passare dalla curva di partenza alla curva finale for ( int j = 1 ; j < nDegV + 1 ; ++j) { for ( int k = 0 ; k < nSpanU ; ++k) { for ( int i = 0 ; i < nDegU + 1 ; ++i) { int nIndPrev = ( j - 1) * ( nDegU * nSpanU + 1) + ( k * nDegU + i) ; Point3d ptCtrl = GetControlPoint( nIndPrev, nullptr) ; ptCtrl += vtStep ; int nInd = j * ( nDegU * nSpanU + 1) + k * nDegU + i ; if ( bRat) { double dW = GetControlWeight( i, nullptr) ; SetControlPoint( nInd, ptCtrl, dW) ; } else SetControlPoint( nInd, ptCtrl) ; } } } // aggiorno lo stato m_nStatus = OK ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::CreateByScrewing( const ICurve* pCurve, const Point3d& ptAx, const Vector3d& vtAx, double dAngRotDeg, double dMove) { // verifico validità curva if ( pCurve == nullptr || ! pCurve->IsValid()) return false ; // verifico che l'asse di rotazione sia non nullo if ( vtAx.IsSmall()) return false ; // verifico che l'angolo di rotazione sia significativo if ( abs( dAngRotDeg) < EPS_ANG_SMALL) return false ; // verifico se solo rivoluzione bool bOnlyRev = ( abs( dMove) < EPS_SMALL) ; // se solo rivoluzione, limito la rotazione entro un giro if ( bOnlyRev && abs( dAngRotDeg) > ANG_FULL) dAngRotDeg = _copysign( ANG_FULL, dAngRotDeg) ; //// se sto facendo una rivoluzione e la curva è piana, controllo che la curva non attraversi l'asse //CurveComposite ccNotTraversing ; //if ( abs( dMove) < EPS_SMALL ) { // Plane3d plPlane ; // if ( pCurve->IsFlat( plPlane, false, 10 * EPS_SMALL)) { // BBox3d b3Crv ; // pCurve->GetLocalBBox( b3Crv) ; // double dAxLen = b3Crv.MaxDistFromPoint( ptAx) ; // CurveLine crvAx ; // crvAx.Set( ptAx - dAxLen * vtAx, ptAx + dAxLen * vtAx) ; // IntersCurveCurve icc( *pCurve, crvAx) ; // Point3d ptStart ; pCurve->GetStartPoint( ptStart) ; // Point3d ptEnd ; pCurve->GetEndPoint( ptEnd) ; // bool bOk = true ; // double dSplit = 0 ; // for ( int i = 0 ; i < icc.GetIntersCount() ; ++i) { // IntCrvCrvInfo iccInfo ; // icc.GetIntCrvCrvInfo( i, iccInfo) ; // // controllo che sia una vera intersezione in 3D, altrimenti passo alla successiva // if ( ! AreSamePointApprox( iccInfo.IciA[0].ptI, iccInfo.IciB[0].ptI)) // continue ; // // se è il punto di inizio o di fine va bene e procedo // if ( AreSamePointApprox( ptStart, iccInfo.IciA[0].ptI) || AreSamePointApprox( ptEnd, iccInfo.IciA[0].ptI)) // continue ; // // se la curva oltrepassa l'asse allora terrò solo la parte prima dell'asse, se tocca l'asse va bene // if ( iccInfo.IciA->nPrevTy != iccInfo.IciA->nNextTy) { // bOk = false ; // dSplit = iccInfo.IciA[0].dU ; // } // } // // tengo solo la parte della curva fino all'intersezione con l'asse // if ( ! bOk) { // ccNotTraversing.AddCurve( pCurve->CopyParamRange( 0, dSplit)) ; // } // else // ccNotTraversing.CopyFrom( pCurve) ; // } //} //// converto in bezier la curva iniziale // CurveComposite CrvU ; //// se la curva è già una bezier singola la tengo, sennò la converto // if ( ccNotTraversing.GetType() != CRV_BEZIER) // CrvU.AddCurve( CurveToBezierCurve( &ccNotTraversing, 3, false)) ; // else { // if ( ! GetCurveBezier(&ccNotTraversing)->IsRational()) // CrvU.AddCurve( ccNotTraversing.Clone()) ; // else { // ICurveBezier* pCrvCopy = GetCurveBezier( ccNotTraversing.Clone()) ; // if ( ! CrvU.AddCurve( EditBezierCurve( pCrvCopy, 3, false))) // return false ; // } // } // converto in bezier la curva iniziale CurveComposite CrvU ; // se la curva è già una bezier singola la tengo, sennò la converto if ( pCurve->GetType() != CRV_BEZIER) CrvU.AddCurve( CurveToBezierCurve( pCurve, 3, true)) ; else { if ( ! GetCurveBezier( pCurve)->IsRational()) CrvU.AddCurve( pCurve->Clone()) ; else { ICurveBezier* pCrvCopy = GetCurveBezier( pCurve->Clone()) ; if ( ! CrvU.AddCurve( EditBezierCurve( pCrvCopy, 3, true))) return false ; } } if ( ! CrvU.IsValid()) return false ; int nSpanU = int( CrvU.GetCurveCount()) ; int nDegU = ( GetCurveBezier( CrvU.GetCurve(0)))->GetDegree() ; // creo la spirale su cui fare la rail della curva ( in questo caso sarà la curva di bezier che delimita il bordo sinistro dello spazio parametrico tra il punto P00 e il punto P01) CurveComposite CrvVSpiral ; // devo trovare un punto che NON stia sull'asse per poter costruire una spirale e sapere quante span in V avrò DBLVECTOR vdSpiralW ; bool bFound = false ; for ( int j = 0 ; j < nSpanU && ! bFound ; ++j) { const ICurveBezier* pSubCrvBezU = GetCurveBezier( CrvU.GetCurve( j)) ; for ( int i = 0 ; i < nDegU + 1 && ! bFound ; ++i) { Point3d ptCtrlU = pSubCrvBezU->GetControlPoint( i) ; DistPointLine dpl( ptCtrlU, ptAx, vtAx, 1, false) ; if ( ! dpl.IsSmall()) { CurveArc crvSpiral ; crvSpiral.SetCPAN( ptAx, ptCtrlU, dAngRotDeg, dMove, vtAx) ; // converto in bezier la spirale CrvVSpiral.AddCurve( ArcToBezierCurve( &crvSpiral, 3 , true)) ; const ICurveBezier* pFirstBez = GetCurveBezier( CrvVSpiral.GetCurve( 0)) ; int nDeg = pFirstBez->GetDegree() ; for ( int k = 0 ; k < nDeg + 1 ; ++k) vdSpiralW.push_back( pFirstBez->GetControlWeight( k)) ; bFound = true ; } } } if ( ! bFound) return false ; if ( ! CrvVSpiral.IsValid()) return false ; int nSpanV = CrvVSpiral.GetCurveCount() ; int nDegV = GetCurveBezier( CrvVSpiral.GetCurve(0))->GetDegree() ; // inizializzo la superficie (deve essere razionale) bool bRat = true ; Init( nDegU, nDegV, nSpanU, nSpanV, bRat) ; // per ogni punto di controllo della curva di base creo la spirale che rappresenta la screw di quel punto // scorro le sottocurve della curva di base for ( int k = 0 ; k < nSpanU ; ++k ) { const ICurveBezier* pSubCrvBezU = GetCurveBezier( CrvU.GetCurve( k)) ; // scorro i punti di controllo for ( int i = 0 ; i < nDegU + 1 ; ++i) { if ( k != 0 && i == 0) continue ; Point3d ptCtrlU = pSubCrvBezU->GetControlPoint( i) ; double dWU = pSubCrvBezU->GetControlWeight( i) ; CurveComposite CrvV ; // verifico se il punto di controllo sta sull'asse // in tal caso anziché una spirale creo semplicemente una linea lungo l'asse DistPointLine dpl( ptCtrlU, ptAx, vtAx, 1, false) ; if ( ! dpl.IsSmall()) { // creo la spirale e la converto in bezier CurveArc CrvSpiral ; CrvSpiral.SetCPAN( ptAx, ptCtrlU, dAngRotDeg, dMove, vtAx) ; ICurve* pSpiralBezier( ArcToBezierCurve( &CrvSpiral)) ; // converto in curva bezier di grado 3 perché l'arco è una spirale CrvV.AddCurve( pSpiralBezier) ; } else if ( abs( dMove) > EPS_SMALL){ // creo un segmento in forma bezier con il giusto numero di span CurveLine CL ; CL.Set( ptCtrlU, ptCtrlU + vtAx * dMove) ; double dStep = 1. / nSpanV ; for ( int j = 0 ; j < nSpanV ; ++j) { PtrOwner pCLSingleSpan( GetCurveLine( CL.CopyParamRange( j * dStep, ( j + 1) * dStep))) ; PtrOwner pCrvBezForm( LineToBezierCurve( pCLSingleSpan, 3, true)) ; // prima di aggiungere le curve alla composito setto i pesi for ( int z = 0 ; z < pCrvBezForm->GetDegree() + 1 ; ++z) pCrvBezForm->SetControlWeight( z, vdSpiralW[z]) ; CrvV.AddCurve( Release( pCrvBezForm)) ; } } // se sto facendo una rivoluzione ( dMove == 0) e ho un punto di controllo sull'asse allora ho un polo // devo aggiungere sempre lo stesso punto per tutta la riga della matrice dei punti di controllo, con i pesi giusti else { CurveBezier CrvBezier ; CrvBezier.Init( nDegV, bRat) ; for ( int z = 0 ; z < nDegV + 1 ; ++z) { CrvBezier.SetControlPoint( z, ptCtrlU, vdSpiralW[z]) ; } for ( int j = 0 ; j < nSpanV ; ++j) CrvV.AddCurve( CrvBezier.Clone()) ; } if ( ! CrvV.IsValid()) return false ; // aggiungo i punti di controllo // scorro le sottocurve della spirale for ( int j = 0 ; j < nSpanV ; ++j) { const ICurveBezier* pSubCrvBezV = GetCurveBezier( CrvV.GetCurve( j)) ; // scorro i punti di controllo for ( int z = 0 ; z < nDegV + 1 ; ++z) { double dWV = pSubCrvBezV->GetControlWeight( z) ; Point3d ptCtrlV = pSubCrvBezV->GetControlPoint( z) ; SetControlPoint( nDegU * k + i, nDegV * j + z, ptCtrlV, dWU * dWV) ; } } } } return true ; } //---------------------------------------------------------------------------- bool SurfBezier::CreateByPointCurve( const Point3d& pt, const ICurve* pCurve) { // converto in bezier la curva iniziale CurveComposite CrvU ; // se la curva è già una bezier singola la tengo, sennò la converto if ( pCurve->GetType() != CRV_BEZIER) CrvU.AddCurve( CurveToBezierCurve( pCurve)) ; else CrvU.AddCurve( pCurve->Clone()) ; if ( ! CrvU.IsValid()) return false ; // recupero span e grado nel parametro U int nSpanU = int( CrvU.GetCurveCount()) ; int nDegU = GetCurveBezier( CrvU.GetCurve(0))->GetDegree() ; bool bRat = GetCurveBezier( CrvU.GetCurve(0))->IsRational() ; // in V decido che la funzione è una patch di grado 1 int nSpanV = 1 ; int nDegV = 1 ; // inizializzo la curva e aggiungo i punti di controllo Init( nDegU, nDegV, nSpanU, nSpanV, bRat) ; // scorro le patch nel parametro U for ( int k = 0 ; k < nSpanU ; ++k) { const ICurveBezier* pSubCrvU = GetCurveBezier( CrvU.GetCurve( k)) ; // scorro i punti di controllo in U for ( int i = 0 ; i < nDegU + 1 ; ++i ) { double dW = pSubCrvU->GetControlWeight( i) ; Point3d ptCtrl = i == 0 ? pSubCrvU->GetControlPoint( i) : pt ; // scorro sul parametro V // do per scontato di avere una patch di grado 1 ( quindi nel parametro V ho solo due punti) for ( int j = 0 ; j < nDegV + 1 ; ++j) { if ( ! bRat) SetControlPoint( nDegU * k + i, j, ptCtrl) ; else SetControlPoint( nDegU * k + i, j , ptCtrl, dW) ; } } } return true ; } //---------------------------------------------------------------------------- static bool ChangeStartForClosed( PolyLine& plU0, PolyLine& plU1, ICurveComposite* pCrvU0, ICurveComposite* pCrvU1) { // se sono chiuse devo controllare che gli start siano il più allineati possibile, se non lo sono cambio gli start if ( plU0.IsClosed() && plU1.IsClosed()) { vector> vDistVert ; tuple tMatch ; Point3d pt0 ; bool bOk0 = plU0.GetFirstPoint( pt0) ; int c0 = 0 ; double dMinDist = INFINITO ; while ( bOk0) { Point3d pt1 ; bool bOk1 = plU1.GetFirstPoint( pt1) ; int c1 = 0 ; while ( bOk1) { double dDist = Dist( pt0, pt1) ; if ( dDist < dMinDist) { tMatch = make_tuple( dDist, c0, pt0, c1, pt1) ; dMinDist = dDist ; } ++c1 ; bOk1 = plU1.GetNextPoint( pt1) ; } vDistVert.push_back( tMatch) ; dMinDist = INFINITO ; c1 = 0 ; ++c0 ; bOk0 = plU0.GetNextPoint( pt0) ; } int nMin = 0 ; dMinDist = INFINITO ; for ( int i = 0 ; i < int( vDistVert.size()) ; ++i) { if ( get<0>(vDistVert[i]) < dMinDist) { dMinDist = get<0>(vDistVert[i]) ; nMin = i ; } } ChangePolyLineStart(plU0, get<2>(vDistVert[nMin]), EPS_SMALL) ; ChangePolyLineStart(plU1, get<4>(vDistVert[nMin]), EPS_SMALL) ; pCrvU0->ChangeStartPoint( double( get<1>(vDistVert[nMin]))) ; pCrvU1->ChangeStartPoint( double( get<3>(vDistVert[nMin]))) ; } return true ; } static bool GetEdgeSplitByAngTol( const ICurveComposite* pCC, double dAngTol, BOOLVECTOR& vEdgeSplit) { int nCrvs = pCC->GetCurveCount() ; vEdgeSplit.clear() ; vEdgeSplit.resize( nCrvs) ; fill( vEdgeSplit.begin(), vEdgeSplit.end(), false) ; bool bClosed = pCC->IsClosed() ; Vector3d vtTanCurr = V_INVALID, vtTanNext = V_INVALID ; // cos della tolleranza angolare massima double dCosTol = cos( dAngTol * DEGTORAD) ; for ( int nC = 0 ; nC < nCrvs ; ++ nC) { const ICurve* pSubCrvCurr = pCC->GetCurve( nC) ; int nNext = nC < nCrvs - 1 ? nC + 1 : 0 ; const ICurve* pSubCrvNext = pCC->GetCurve( nNext) ; pSubCrvCurr->GetEndDir( vtTanCurr) ; vtTanCurr.Normalize() ; pSubCrvNext->GetStartDir( vtTanNext) ; vtTanNext.Normalize() ; // Calcolo il Coseno tra i due versori double dCos = vtTanCurr * vtTanNext ; // Se oltre la tolleranza allora ho incontrato un edge if ( dCos < dCosTol) vEdgeSplit[nC] = true ; } if ( ! bClosed) vEdgeSplit[nCrvs - 1] = false ; return true ; } //---------------------------------------------------------------------------- static bool AdjustParamFromApprox( const PolyLine& plApprox, double& dParam) { // il parametro in ingresso è riferito al numero di linee della polilinea // questa variabile verrà sostituita con il valore del parametro corrispondente alla curva che è stata approssimata dalla polilinea int nCrv = int( dParam) ; if ( nCrv > plApprox.GetPointNbr()) return false ; double dParamPrev = 0 ; plApprox.GetFirstU( dParamPrev) ; int c = 0 ; while ( c < nCrv) { plApprox.GetNextU( dParamPrev) ; ++c ; } double dParamNext = 0 ; plApprox.GetNextU( dParamNext) ; double dFactor = dParam - nCrv ; dParam = dFactor * dParamNext + ( 1 - dFactor) * dParamPrev ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::CreateByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, int nRuledType) { // converto in bezier la curva iniziale CurveComposite CrvU0 ; // se la curva è già una bezier singola la tengo, sennò la converto if ( pCurve0->GetType() != CRV_BEZIER) { // se è una compo controllo che siano già tutte bezier bool bAlreadyBez = true ; if ( pCurve0->GetType() == CRV_COMPO) { const ICurveComposite* pCC0 = GetBasicCurveComposite( pCurve0) ; for ( int i = 0 ; i < pCC0->GetCurveCount() ; ++i) { if ( pCC0->GetCurve( i)->GetType() != CRV_BEZIER) { bAlreadyBez = false ; break ; } } } else bAlreadyBez = false ; if ( ! bAlreadyBez) CrvU0.AddCurve( CurveToBezierCurve( pCurve0)) ; else CrvU0.AddCurve( pCurve0->Clone()) ; } else CrvU0.AddCurve( pCurve0->Clone()) ; if ( ! CrvU0.IsValid()) return false ; // recupero span e grado nel parametro U int nSpanU0 = int( CrvU0.GetCurveCount()) ; int nDegU0 = ( GetCurveBezier( CrvU0.GetCurve(0)))->GetDegree() ; bool bRat0 = (GetCurveBezier( CrvU0.GetCurve(0)))->IsRational() ; // se la curva è già una bezier singola la tengo, sennò la converto CurveComposite CrvU1 ; // se la curva è già una bezier singola la tengo, sennò la converto if ( pCurve1->GetType() != CRV_BEZIER) { // se è una compo controllo che siano già tutte bezier bool bAlreadyBez = true ; if ( pCurve1->GetType() == CRV_COMPO) { const ICurveComposite* pCC1 = GetBasicCurveComposite( pCurve1) ; for ( int i = 0 ; i < pCC1->GetCurveCount() ; ++i) { if ( pCC1->GetCurve( i)->GetType() != CRV_BEZIER) { bAlreadyBez = false ; break ; } } } else bAlreadyBez = false ; if ( ! bAlreadyBez) CrvU1.AddCurve( CurveToBezierCurve( pCurve1)) ; else CrvU1.AddCurve( pCurve1->Clone()) ; } else CrvU1.AddCurve( pCurve1->Clone()) ; // recupero span e grado nel parametro U int nSpanU1 = int( CrvU1.GetCurveCount()) ; int nDegU1 = ( GetCurveBezier( CrvU1.GetCurve(0)))->GetDegree() ; bool bRat1 = (GetCurveBezier( CrvU1.GetCurve(0)))->IsRational() ; // se le due curve hanno grado diverso allora aumento il grado di quella con grado inferiore if ( nDegU0 != nDegU1) { while ( nDegU0 < nDegU1) { CurveComposite CC ; for ( int k = 0 ; k < nSpanU0 ; ++k) CC.AddCurve( BezierIncreaseDegree( GetCurveBezier( CrvU0.GetCurve( k)))) ; ++nDegU0 ; CrvU0 = CC ; } while ( nDegU0 > nDegU1) { CurveComposite CC ; for ( int k = 0 ; k < nSpanU0 ; ++k) CC.AddCurve( BezierIncreaseDegree( GetCurveBezier( CrvU1.GetCurve( k)))) ; ++nDegU1 ; CrvU1 = CC ; } } // omogenizzo la razionalità if ( bRat0 != bRat1) { if ( ! bRat0) { CurveComposite CC ; for ( int k = 0 ; k < nSpanU0 ; ++k) { ICurveBezier* pCrvBez = GetCurveBezier( CrvU0.GetCurve( k)->Clone()) ; pCrvBez->MakeRational() ; CC.AddCurve( pCrvBez) ; } CrvU0 = CC ; bRat0 = true ; } if ( ! bRat1) { CurveComposite CC ; for ( int k = 0 ; k < nSpanU1 ; ++k) { ICurveBezier* pCrvBez = GetCurveBezier( CrvU1.GetCurve( k)->Clone()) ; pCrvBez->MakeRational() ; CC.AddCurve( pCrvBez) ; } CrvU1 = CC ; bRat1 = true ; } } if ( nDegU0 != nDegU1) return false ; int nDegU = nDegU0 ; // in V decido che la funzione è una patch di grado 1 int nSpanV = 1 ; int nDegV = 1 ; // ho bisogno della rappresentazione delle curve in forma di polyline, con inizio e fine delle sottocurve PolyLine plU0, plU1 ; int nCount0 = 0 ; DBLVECTOR vdW0 ; for ( int k = 0 ; k < nSpanU0 ; ++k ) { const ICurveBezier* pSubCrv0 = GetCurveBezier( CrvU0.GetCurve( k)) ; for ( int i = 0 ; i < nDegU + 1 ; ++i ) { if ( i != nDegU && !( i == 0 && k == 0)) continue ; Point3d ptCtrl = pSubCrv0->GetControlPoint( i) ; if ( bRat0) vdW0.push_back( pSubCrv0->GetControlWeight( i)) ; // aggiungo alla polyline solo gli estremi delle sottocurve ( senza ripetizioni) if ( plU0.AddUPoint( nCount0, ptCtrl)) ++ nCount0 ; } } int nCount1 = 0 ; DBLVECTOR vdW1 ; for ( int k = 0 ; k < nSpanU1 ; ++k ) { const ICurveBezier* pSubCrv1 = GetCurveBezier( CrvU1.GetCurve( k)) ; for ( int i = 0 ; i < nDegU1 + 1 ; ++i ) { if ( i != nDegU && !( i == 0 && k == 0)) continue ; Point3d ptCtrl = pSubCrv1->GetControlPoint( i) ; if ( bRat1) vdW1.push_back( pSubCrv1->GetControlWeight( i)) ; if ( plU1.AddUPoint( nCount1, ptCtrl)) ++ nCount1 ; } } // verifico ci siano almeno due punti diversi per curva if ( ( plU0.IsClosed() && plU0.GetPointNbr() < 3) || plU0.GetPointNbr() < 2) return false ; if ( ( plU1.IsClosed() && plU1.GetPointNbr() < 3) || plU1.GetPointNbr() < 2) return false ; // determino il numero di span che avrà la superficie basandomi sulla curva con più sottocurve int nSpanU = max( nSpanU0, nSpanU1) ; // punti per curva int nLastPoint = nDegU + 1 ; // definisco la razionalità bool bRat = bRat0 && bRat1 ; int nSecondRowInd = nDegU * nSpanU + 1 ; // se sono chiuse devo controllare che gli start siano il più allineati possibile, se non lo sono cambio gli start ChangeStartForClosed( plU0, plU1, &CrvU0, &CrvU1) ; // se sto usando la ISOPARM o la MINDIST semplice allora collego più span di una curva allo stesso punto if ( nRuledType == RLT_B_MINDIST) { // creo le liste di punti per le isoparametriche in U PNTIVECTOR vPnt0Match, vPnt1Match ; bool bCommonPoint = false ; AssociatePolyLinesMinDistPoints( plU0, plU1, vPnt0Match, vPnt1Match, bCommonPoint) ; // devo contare il numero di ripetizioni dei match nel mezzo delle curve, perché aumentano il numero di span della superficie!! int nRep0 = 0 ; int nIndMatch = 0 ; int nIndMatchNext = 0 ; int nRep1 = int( vPnt1Match.size() - 1) - vPnt0Match.back().second ; for ( int i = 0 ; i < int( vPnt0Match.size() - 1) ; ++i) { nIndMatch = vPnt0Match[i].second ; nIndMatchNext = vPnt0Match[i+1].second ; if ( nIndMatch != nIndMatchNext) nRep1 += nIndMatchNext - nIndMatch - 1 ; if ( nIndMatch == nIndMatchNext) ++nRep0 ; } // reinizializzo la superficie con il nuovo numero di span in U nSpanU = nSpanU0 + nRep1 ; if ( nSpanU != nSpanU1 + nRep0) LOG_DBG_ERR( GetEGkLogger(), "There could be an errore in the creation of a ruled surface in mode RLT_B_MINDIST") ; if ( nSpanU < max(nSpanU0, nSpanU1)) nSpanU = max(nSpanU0, nSpanU1) ; nSecondRowInd = nDegU * nSpanU + 1 ; Init( nDegU, nDegV, nSpanU, nSpanV, bRat) ; // numero di span aggiunte su U0 e U1 nCount0 = 0 ; nCount1 = 0 ; // scorro gli estremi delle sottocurve della curva U0 for ( int i = 0 ; i < int( vPnt0Match.size() - 1) ; ++i) { nIndMatch = vPnt0Match[i].second ; nIndMatchNext = vPnt0Match[i+1].second ; const ICurveBezier* pSubCrv0 ; if ( nIndMatch == nIndMatchNext) { pSubCrv0 = GetCurveBezier( CrvU0.GetCurve( i)) ; for ( int j = nCount0 == 0 ? 0 : 1 ; j < nLastPoint ; ++j) { if ( ! bRat0) SetControlPoint( nCount0 * nDegU + j, pSubCrv0->GetControlPoint( j)) ; else SetControlPoint( nCount0 * nDegU + j, pSubCrv0->GetControlPoint( j), pSubCrv0->GetControlWeight( j)) ; } ++ nCount0 ; // ripeto l'ultimo punto aggiunto alla riga U1 della superificie int nInd = nIndMatch > 0 ? nIndMatch - 1 : 0 ; const ICurveBezier* pSubCrv0 = GetCurveBezier( CrvU1.GetCurve( nInd)) ; for ( int j = nCount1 == 0 ? 0 : 1 ; j < nLastPoint ; ++j) { int nPoint = nCount1 == 0 ? 0 : nDegU ; if ( ! bRat0) SetControlPoint( nSecondRowInd + nCount1 * nDegU + j, pSubCrv0->GetControlPoint( nPoint)) ; else SetControlPoint( nSecondRowInd + nCount1 * nDegU + j, pSubCrv0->GetControlPoint( nPoint), pSubCrv0->GetControlWeight( nPoint)) ; } ++nCount1 ; } else { // qui devo capire se aggiungere la nuova sottocurva prima o dopo la ripetizione dei punti // se il match del punto della U1 è uguale al punto a cui ero arrivato sulla U0 allora prima aggiungo la ripetizione di punti // se invece il match è più avanti allora aggiuno prima la curva e poi la ripetzione di punti bool bSubCurveAddedFirst = true ; for ( int z = vPnt0Match[i].second ; z <= vPnt0Match[i+1].second ; ++z) bSubCurveAddedFirst = bSubCurveAddedFirst && vPnt1Match[z].second != i ; if ( bSubCurveAddedFirst) { // aggiungo una sottocurva dalla curva U0 pSubCrv0 = GetCurveBezier( CrvU0.GetCurve( i)) ; for ( int j = nCount0 == 0 ? 0 : 1 ; j < nLastPoint ; ++j) { if ( ! bRat0) SetControlPoint( nCount0 * nDegU + j, pSubCrv0->GetControlPoint( j)) ; else SetControlPoint( nCount0 * nDegU + j, pSubCrv0->GetControlPoint( j), pSubCrv0->GetControlWeight( j)) ; } ++ nCount0 ; } // ripeto l'ultimo punto aggiunto per il numero di curve balzate - 1 della curva U1 for ( int k = 0 ; k < nIndMatchNext - nIndMatch - 1 ; ++k) { pSubCrv0 = GetCurveBezier( CrvU0.GetCurve( i != 0 ? i - 1 : i)) ; for ( int j = nCount0 == 0 ? 0 : 1 ; j < nLastPoint ; ++j) { int nPoint = i == 0 && ! bSubCurveAddedFirst ? 0 : nDegU ; if ( ! bRat0) SetControlPoint( nCount0 * nDegU + j, pSubCrv0->GetControlPoint( nPoint)) ; else SetControlPoint( nCount0 * nDegU + j, pSubCrv0->GetControlPoint( nPoint), pSubCrv0->GetControlWeight( nPoint)) ; } ++ nCount0 ; } // se non l'ho già aggiunta prima aggiungo una sottocurva della U0 if ( ! bSubCurveAddedFirst) { pSubCrv0 = GetCurveBezier( CrvU0.GetCurve( i)) ; for ( int j = nCount0 == 0 ? 0 : 1 ; j < nLastPoint ; ++j) { if ( ! bRat0) SetControlPoint( nCount0 * nDegU + j, pSubCrv0->GetControlPoint( j)) ; else SetControlPoint( nCount0 * nDegU + j, pSubCrv0->GetControlPoint( j), pSubCrv0->GetControlWeight( j)) ; } ++ nCount0 ; } // aggiungo tutte le sottocurve che ho balzato della curva U1 for ( int k = 0 ; k < nIndMatchNext - nIndMatch ; ++k) { pSubCrv0 = GetCurveBezier( CrvU1.GetCurve( nIndMatch + k)) ; for ( int j = nCount1 == 0 ? 0 : 1 ; j < nLastPoint ; ++j ) { if ( ! bRat) SetControlPoint( nSecondRowInd + nCount1 * nDegU + j, pSubCrv0->GetControlPoint( j)) ; else SetControlPoint( nSecondRowInd + nCount1 * nDegU + j, pSubCrv0->GetControlPoint( j), pSubCrv0->GetControlWeight( j)) ; } ++ nCount1 ; } } } //controllo se ho aggiunto tutti i punti della curva U1 if ( vPnt0Match.back().second != nSpanU1) { // riaggiungo l'ultimo punto della U0 const ICurveBezier* pSubCrv0 = GetCurveBezier( CrvU0.GetCurve( nSpanU0 - 1)) ; int nPoint = nDegU ; while ( nCount0 < nSpanU) { for ( int j = 1 ; j < nLastPoint ; ++j) { if ( ! bRat0) SetControlPoint( nCount0 * nDegU + j, pSubCrv0->GetControlPoint( nPoint)) ; else SetControlPoint( nCount0 * nDegU + j, pSubCrv0->GetControlPoint( nPoint), pSubCrv0->GetControlWeight( nPoint)) ; } ++ nCount0 ; } // aggiungo le restanti sottocurve della curva U1 int nCrv1 = vPnt0Match.back().second ; while ( nCrv1 < nSpanU1) { const ICurveBezier* pSubCrv1 = GetCurveBezier( CrvU1.GetCurve( nCrv1)) ; for ( int j = 1 ; j < nLastPoint ; ++j) { if ( ! bRat1) SetControlPoint( nSecondRowInd + nCount1 * nDegU + j, pSubCrv1->GetControlPoint( j)) ; else SetControlPoint( nSecondRowInd + nCount1 * nDegU + j, pSubCrv1->GetControlPoint( j), pSubCrv1->GetControlWeight( j)) ; } ++ nCount1 ; ++ nCrv1 ; } } } else if ( nRuledType == RLT_B_ISOPAR) { // inizializzo la superficie Init( nDegU, nDegV, nSpanU, nSpanV, bRat) ; Point3d ptP0Start ; CrvU0.GetStartPoint( ptP0Start) ; Point3d ptP1Start ; CrvU1.GetStartPoint( ptP1Start) ; // setto i primi due punti delle righe nCount0 = 0 ; // sottocurva su pCrvU0 if ( ! bRat) SetControlPoint( 0, ptP0Start) ; else SetControlPoint( 0, ptP0Start, vdW0[0]) ; nCount1 = 0 ; // sottocurva su pCrvU1 if ( ! bRat) SetControlPoint( nSecondRowInd, ptP1Start) ; else SetControlPoint( nSecondRowInd, ptP1Start, vdW1[0]) ; INTVECTOR vMatch ; int nLong = 0 ; // prendo la polyline con più punti e scelgo a quali punti dell'altra polyline vanno associati FindMatchByParam( plU0, plU1, vMatch,nLong) ; for ( int i = 0 ; i < int( vMatch.size() - 1) ; ++i) { int nCount = i ; // span in U aggiunte // scorro i punti della polyline più lunga // se il punto corrente e il successivo sono associati allo stesso punto dell'altra polyline // allora devo aggiungere la sottocurva della curva più lunga e ripetere l'ultimo punto aggiunto dell'altra polyline // se il corrente e il successivo sono associati a punti successivi // allora devo aggiungere una sottocurva per ognuna delle due curve if ( vMatch[i] == vMatch[i+1]) { // se la curva più lunga è la prima if ( nLong == 0) { // aggiungo la curva sulla prima riga for ( int i = 1 ; i < nLastPoint ; ++i) { const ICurveBezier* pSubCrv0 = GetCurveBezier( CrvU0.GetCurve( nCount0)) ; Point3d ptCtrl = pSubCrv0->GetControlPoint( i) ; if ( ! bRat) SetControlPoint( nCount * nDegU + i, ptCtrl) ; else { double dW = pSubCrv0->GetControlWeight( i) ; SetControlPoint( nCount * nDegU + i, ptCtrl, dW) ; } } ++ nCount0 ; // riaggungo lo start point sulla seconda riga // o end point se ho già aggiunto tutte le curve int nInd = nCount1 < nSpanU1 ? 0 : nDegU ; int nSubCrv = nCount1 < nSpanU1 ? nCount1 : nSpanU1 - 1 ; const ICurveBezier* pSubCrv1 = GetCurveBezier( CrvU1.GetCurve( nSubCrv)) ; Point3d ptCtrl = pSubCrv1->GetControlPoint( nInd) ; for ( int i = 1 ; i < nLastPoint ; ++i) { if ( ! bRat) SetControlPoint( nSecondRowInd + nCount * nDegU + i, ptCtrl) ; else { double dW = pSubCrv1->GetControlWeight( i) ; SetControlPoint( nSecondRowInd + nCount * nDegU + i, ptCtrl, dW) ; } } } else { // aggiungo la curva sulla seconda riga for ( int i = 1 ; i < nLastPoint ; ++i) { const ICurveBezier* pSubCrv1 = GetCurveBezier( CrvU1.GetCurve( nCount1)) ; Point3d ptCtrl = pSubCrv1->GetControlPoint( i) ; if ( ! bRat) SetControlPoint( nSecondRowInd + nCount * nDegU + i, ptCtrl) ; else { double dW = pSubCrv1->GetControlWeight( i) ; SetControlPoint( nSecondRowInd + nCount * nDegU + i, ptCtrl, dW) ; } } ++ nCount1 ; // riaggungo lo start point sulla prima riga // o end point se ho già aggiunto tutte le curve int nInd = nCount0 < nSpanU0 ? 0 : nDegU ; int nSubCrv = nCount0 < nSpanU0 ? nCount0 : nSpanU0 - 1 ; const ICurveBezier* pSubCrv0 = GetCurveBezier( CrvU0.GetCurve( nSubCrv)) ; Point3d ptCtrl = pSubCrv0->GetControlPoint( nInd) ; for ( int i = 1 ; i < nLastPoint ; ++i) { if ( ! bRat) SetControlPoint( nCount * nDegU + i, ptCtrl) ; else { double dW = pSubCrv0->GetControlWeight( i) ; SetControlPoint( nCount * nDegU + i, ptCtrl, dW) ; } } } } // altrimenti aggiungo una sottocurva da entrambe le curve else { // aggiungo la curva sulla prima riga for ( int i = 1 ; i < nLastPoint ; ++i) { const ICurveBezier* pSubCrv0 = GetCurveBezier( CrvU0.GetCurve( nCount0)) ; Point3d ptCtrl = pSubCrv0->GetControlPoint( i) ; if ( ! bRat) SetControlPoint( nCount * nDegU + i, ptCtrl) ; else { double dW = pSubCrv0->GetControlWeight( i) ; SetControlPoint( nCount * nDegU + i, ptCtrl, dW) ; } } ++ nCount0 ; // aggiungo la curva sulla seconda riga for ( int i = 1 ; i < nLastPoint ; ++i) { const ICurveBezier* pSubCrv1 = GetCurveBezier( CrvU1.GetCurve( nCount1)) ; Point3d ptCtrl = pSubCrv1->GetControlPoint( i) ; if ( ! bRat) SetControlPoint( nSecondRowInd + nCount * nDegU + i, ptCtrl) ; else { double dW = pSubCrv1->GetControlWeight( i) ; SetControlPoint( nSecondRowInd + nCount * nDegU + i, ptCtrl, dW) ; } } ++ nCount1 ; } } } // spezzo le curve di bezier dove è necessario aggiungere dei punti else if ( nRuledType == RLT_B_MINDIST_PLUS) { // scorro la prima curva e per ogni punto di fine sottocurva cerco il minDistPoint sull'altra curva // in quel punto la curva verrà spezzata, a meno che non si trovi una joint già sufficientemente vicina #if SAVEMATCHCURVES vector> vvGeo0(4) ; vector> vvCol0(4) ; #endif // approssimo le curve con delle polyline per il calcolo delle distanze PolyLine plApproxU0, plApproxU1 ; double dLinTol = 10 * EPS_SMALL ; double dAngTol = 5 ; CrvU0.ApproxWithLines( dLinTol, dAngTol, ICurve::APL_STD, plApproxU0) ; CrvU1.ApproxWithLines( dLinTol, dAngTol, ICurve::APL_STD, plApproxU1) ; // prima trovo le associazioni senza aggiunte di punti bool bIsClosed0 = plU0.IsClosed() ; bool bIsClosed1 = plU1.IsClosed() ; CurveComposite ccPoly0 ; ccPoly0.FromPolyLine( plApproxU0) ; CurveComposite ccPoly1 ; ccPoly1.FromPolyLine( plApproxU1) ; Point3d ptP1 ; plU1.GetFirstPoint( ptP1) ; PNTUVECTOR vMatch1 ; PNTUVECTOR vMatch1b ; PNTVECTOR vPnt1 ; vPnt1.emplace_back( ptP1) ; BOOLVECTOR vbMismatch1( plU1.GetPointNbr() - 1) ; fill( vbMismatch1.begin(), vbMismatch1.end(), false) ; while ( plU1.GetNextPoint( ptP1, true)) { Vector3d vtDirPrev ; CrvU1.GetCurve( vPnt1.size() - 1)->GetEndDir( vtDirPrev) ; Vector3d vtDirCurr ; CrvU1.GetCurve( vPnt1.size())->GetStartDir( vtDirCurr) ; Vector3d vtDir = Media( vtDirPrev, vtDirCurr) ; IntersCurvePlane icp( ccPoly0, ptP1, vtDir) ; DistPointCurve dpc( ptP1, ccPoly0, false) ; int nFlag = 0 ; double dParam ; dpc.GetParamAtMinDistPoint( 0, dParam, nFlag) ; AdjustParamFromApprox( plApproxU0, dParam) ; Point3d ptJoint ; dpc.GetMinDistPoint( 0, ptJoint, nFlag) ; vMatch1b.emplace_back( ptJoint, dParam) ; double dParamMinDist = dParam ; Point3d ptMinDist = ptJoint ; if ( icp.GetIntersCount() > 0) { icp.GetIntersPointNearTo( ptP1, ptJoint, dParam) ; AdjustParamFromApprox( plApproxU0, dParam) ; if ( Dist( ptP1, ptJoint) > 1.5 * Dist( ptP1, ptMinDist)) { ptJoint = ptMinDist ; dParam = dParamMinDist ; vbMismatch1[ssize(vMatch1b) - 1] = true ; } } else { ; } // capisco se il punto avrà bisogno di aggiungere uno split sull'altra curva o no int nParam = int( dParam) ; Point3d ptNearestJoint ; // punto più vicino di joint già esistente sulla curva U0 if ( nParam == nSpanU0) CrvU0.GetCurve( nParam - 1)->GetEndPoint( ptNearestJoint) ; else if ( dParam - nParam > 0.5) CrvU0.GetCurve( nParam)->GetEndPoint( ptNearestJoint) ; else CrvU0.GetCurve( nParam)->GetStartPoint( ptNearestJoint) ; //se sono troppo vicino ad uno split esistente allora non faccio nulla if ( abs(dParam - round( dParam)) < 100 * EPS_PARAM || Dist( ptJoint, ptNearestJoint) < 50 * EPS_SMALL) { dParam = round( dParam) ; ptJoint = ptNearestJoint ; } vMatch1.emplace_back( ptJoint, dParam) ; vPnt1.emplace_back( ptP1) ; #if SAVEMATCHCURVES CurveLine CLa ; CLa.Set( ptP1, ptJoint) ; vvGeo0[0].push_back( CLa.Clone()) ; CurveLine CLb ; CLb.Set( ptP1, vMatch1b.back().first) ; vvGeo0[1].push_back( CLb.Clone()) ; #endif } plU1.GetLastPoint( ptP1) ; vPnt1.emplace_back( ptP1) ; Point3d ptP0 ; plU0.GetLastPoint( ptP0) ; vMatch1.emplace_back( ptP0, nSpanU0) ; vMatch1b.emplace_back( ptP0, nSpanU0) ; plU0.GetFirstPoint( ptP0) ; PNTUVECTOR vMatch0 ; PNTUVECTOR vMatch0b ; PNTVECTOR vPnt0 ; vPnt0.emplace_back( ptP0) ; BOOLVECTOR vbMismatch0( plU0.GetPointNbr() - 1) ; fill( vbMismatch0.begin(), vbMismatch0.end(), false) ; while ( plU0.GetNextPoint( ptP0, true)) { Vector3d vtDirPrev ; CrvU0.GetCurve( vPnt0.size() - 1)->GetEndDir( vtDirPrev) ; Vector3d vtDirCurr ; CrvU0.GetCurve( vPnt0.size())->GetStartDir( vtDirCurr) ; Vector3d vtDir = Media( vtDirPrev, vtDirCurr) ; IntersCurvePlane icp( ccPoly1, ptP0, vtDir) ; DistPointCurve dpc( ptP0, ccPoly1, false) ; int nFlag = 0 ; double dParam ; dpc.GetParamAtMinDistPoint( 0, dParam, nFlag) ; Point3d ptJoint ; dpc.GetMinDistPoint( 0, ptJoint, nFlag) ; AdjustParamFromApprox( plApproxU1, dParam) ; vMatch0b.emplace_back( ptJoint, dParam) ; double dParamMinDist = dParam ; Point3d ptMinDist = ptJoint ; if ( icp.GetIntersCount() > 0) { icp.GetIntersPointNearTo( ptP0, ptJoint, dParam) ; AdjustParamFromApprox( plApproxU1, dParam) ; if ( Dist( ptP0, ptJoint) > 1.5 * Dist( ptP0, ptMinDist)) { ptJoint = ptMinDist ; dParam = dParamMinDist ; vbMismatch0[ssize(vMatch0b) - 1] = true ; } } else { ; } // capisco se il punto avrà bisogno di aggiungere uno split sull'altra curva o no int nParam = int( dParam) ; Point3d ptNearestJoint ; // punto più vicino di joint già esistente sulla curva U1 if ( nParam == nSpanU1) CrvU1.GetCurve( nParam - 1)->GetEndPoint( ptNearestJoint) ; else if ( dParam - nParam > 0.5) CrvU1.GetCurve( nParam)->GetEndPoint( ptNearestJoint) ; else CrvU1.GetCurve( nParam)->GetStartPoint( ptNearestJoint) ; // se sono già troppo vicino ad un split esistente allora non faccio nulla if ( abs(dParam - round( dParam)) < 100 * EPS_PARAM || Dist( ptJoint, ptNearestJoint) < 50 * EPS_SMALL) { dParam = round( dParam) ; ptJoint = ptNearestJoint ; } vMatch0.emplace_back( ptJoint, dParam) ; vPnt0.emplace_back( ptP0) ; #if SAVEMATCHCURVES CurveLine CLa ; CLa.Set( ptP0, ptJoint) ; vvGeo0[2].push_back( CLa.Clone()) ; CurveLine CLb ; CLb.Set( ptP0, vMatch0b.back().first) ; vvGeo0[3].push_back( CLb.Clone()) ; #endif } plU0.GetLastPoint( ptP0) ; vPnt0.emplace_back( ptP0) ; plU1.GetLastPoint( ptP1) ; vMatch0.emplace_back( ptP1, nSpanU1) ; vMatch0b.emplace_back( ptP1, nSpanU1) ; #if SAVEMATCHCURVES vvCol0[0].resize( ssize(vvGeo0[0])) ; fill( vvCol0[0].begin(), vvCol0[0].end(), Color( 128,128,255)) ; vvCol0[1].resize( ssize(vvGeo0[1])) ; fill( vvCol0[1].begin(), vvCol0[1].end(), Color( 0,128,192)) ; vvCol0[2].resize( ssize(vvGeo0[2])) ; fill( vvCol0[2].begin(), vvCol0[2].end(), Color( 255,128,128)) ; vvCol0[3].resize( ssize(vvGeo0[3])) ; fill( vvCol0[3].begin(), vvCol0[3].end(), Color( 192,128,0)) ; SaveGeoObj( vvGeo0, vvCol0, "D:\\Temp\\bezier\\ruled\\match.nge") ; #endif // salvo le coppie di punti su ogni curva che indicano l'inizio e la fine di regioni di mismatch tra i valori di accoppiamento rilevati // tramite due misure diverse della distanza INTINTVECTOR vMismatch0 ; INTINTVECTOR vMismatch1 ; DBLVECTOR vdDistMismatch0 ; double dMaxDist = Dist( vPnt0[0], vPnt1[0]) / 3 ; double dMinDist = Dist( vPnt0[0], vPnt1[0]) / 20 ; int nPnt0 = CrvU0.GetCurveCount() ; for ( int i = 0 ; i < ssize( vMatch0) ; ++i) { vdDistMismatch0.push_back( Dist( vMatch0[i].first, vMatch0b[i].first)) ; if ( ! AreSamePointEpsilon( vMatch0[i].first, vMatch0b[i].first, dMaxDist)) { int c = i + 1 ; // scorro in avanti while ( c < nPnt0 && ( ! AreSamePointEpsilon( vMatch0[c].first, vMatch0b[c].first, dMinDist) || vbMismatch0[c])) { vdDistMismatch0.push_back( Dist( vMatch0[c].first, vMatch0b[c].first)) ; ++c ; } vdDistMismatch0.push_back( Dist( vMatch0[c].first, vMatch0b[c].first)) ; //scorro indietro int p = i - 1 ; while ( p > 0 && ( ! AreSamePointEpsilon( vMatch0[p].first, vMatch0b[p].first, dMinDist) || vbMismatch0[p])) { --p ; } vMismatch0.emplace_back( p, c) ; i = c ; } } DBLVECTOR vdDistMismatch1 ; int nPnt1 = CrvU1.GetCurveCount() ; for ( int i = 0 ; i < ssize( vMatch1) ; ++i) { vdDistMismatch1.push_back( Dist( vMatch1[i].first, vMatch1b[i].first)) ; if ( ! AreSamePointEpsilon( vMatch1[i].first, vMatch1b[i].first, dMaxDist)) { int c = i + 1 ; // scorro in avanti while ( c < nPnt1 && ( ! AreSamePointEpsilon( vMatch1[c].first, vMatch1b[c].first, dMinDist) || vbMismatch1[c])) { vdDistMismatch1.push_back( Dist( vMatch1[c].first, vMatch1b[c].first)) ; ++c ; } vdDistMismatch1.push_back( Dist( vMatch1[c].first, vMatch1b[c].first)) ; // scorro indietro int p = i - 1 ; while ( p > 0 && ( ! AreSamePointEpsilon( vMatch1[p].first, vMatch1b[p].first, dMinDist) || vbMismatch1[p])) { --p ; } vMismatch1.emplace_back( p, c) ; i = c ; } } // verifico la presenza di eventuali "edge" lungo le polyline ( punti di passaggio di un edge e quindi cambi bruschi di direzione della polyline) BOOLVECTOR vEdgeSplit0, vEdgeSplit1 ; double dAngTolSplit = 55 ; GetEdgeSplitByAngTol( &CrvU0, dAngTolSplit, vEdgeSplit0) ; GetEdgeSplitByAngTol( &CrvU1, dAngTolSplit, vEdgeSplit1) ; int c = 0 ; double dLastParamMatch0 = 0 ; plU0.GetFirstPoint( ptP0) ; Point3d ptLastPointMatch0 = ptP0 ; DBLVECTOR vdSplit1 ; // split che sono da applicare alla curva U1 int j = 0 ; double dLastParamMatch1 = 0 ; plU1.GetFirstPoint( ptP1) ; Point3d ptLastPointMatch1 = ptP1 ; DBLVECTOR vdSplit0 ; // split che sono da applicare alla curva U0 INTINTVECTOR vPairs ; vPairs.emplace_back( 0, 0) ; bool bAdvance = true ; int m0 = 0, m1 = 0 ; // contatori delle zone di mismatch // verifico i match effettivi tra le polyline int nPairs = 0 ; while ( bAdvance && ssize( vPairs) > nPairs) { nPairs = ssize( vPairs) ; if ( c > ssize(vMatch0) - 1 || j > ssize( vMatch1) - 1) { LOG_DBG_ERR( GetEGkLogger(), "RLT_B_MINDIST_PLUS: le due curve potrebbero non avere forme coerenti") ; return false ; } // aggiorno il contatore delle eventuali zone di mismatch if ( ssize( vMismatch0) > 0) { while ( m0 < ssize( vMismatch0) - 1 && c > vMismatch0[m0].second) ++m0 ; } if ( ssize( vMismatch1) > 0) { while ( m1 < ssize( vMismatch1) - 1 && j > vMismatch1[m1].second) ++m1 ; } double dParam0 = vMatch0[c].second ; Point3d ptJoint0 = vMatch0[c].first ; double dParam1 = vMatch1[j].second ; Point3d ptJoint1 = vMatch1[j].first ; if ( bIsClosed1 && dParam0 > nSpanU1 - EPS_SMALL && c < ssize( vMatch0) / 2) { dParam0 = 0 ; vMatch0[c].second = dParam0 ; } if ( bIsClosed0 && dParam1 > nSpanU0 - EPS_SMALL && j < ssize( vMatch1) / 2) { dParam1 = 0 ; vMatch1[j].second = dParam1 ; } // capisco con quale delle due polyline avanzo bool bAdvance0 = dParam0 < j + 1 + EPS_SMALL ; bool bAdvance1 = dParam1 < c + 1 + EPS_SMALL ; // controllo se ho un match biunivoco ( controllo parametri e punti) bool bPerfectMatch = ( abs( dParam0 - round( dParam0)) < 10 * EPS_SMALL && round( dParam0) == j + 1 && abs( dParam1 - round( dParam1)) < 10 * EPS_SMALL && round( dParam1) == c + 1) || ( AreSamePointEpsilon( vPnt0[c+1], ptJoint1, 100 * EPS_SMALL) && AreSamePointEpsilon( vPnt1[j+1], ptJoint0, 100 * EPS_SMALL)) ; if ( bPerfectMatch) { // matcho correttamente tutto bAdvance0 = true ; dParam0 = j + 1 ; dParam1 = c + 1 ; ptJoint0 = vPnt1[j+1] ; ptJoint1 = vPnt0[c+1] ; } int nSplit1 = vdSplit1.size() ; int nSplit0 = vdSplit0.size() ; // se con una polyline sono arrivato alla fine non posso più avanzare if ( c == vMatch0.size()) bAdvance0 = false ; if ( j == vMatch1.size()) bAdvance1 = false ; bool bMismatch = false ; bool bMismatch0 = false ; bool bMismatch1 = false ; // controllo la presenza di eventuali zone di mismatch if ( ssize( vMismatch0) > 0 || ssize( vMismatch1) > 0) { if ( m0 < ssize( vMismatch0) && c >= vMismatch0[m0].first && c < vMismatch0[m0].second) bMismatch0 = true ; if ( m1 < ssize( vMismatch1) && j >= vMismatch1[m1].first && j < vMismatch1[m1].second) bMismatch1 = true ; bMismatch = (bMismatch0 && bMismatch1) || (bMismatch0 && ! bAdvance1) || (bMismatch1 && ! bAdvance0); } // se trovo che ho uno spigolo allora procedo con la gestione spigoli if ( vEdgeSplit0[c] && vEdgeSplit1[j]) { // se ho uno spigolo su entrambe le curve forzo l'accoppiamento bAdvance0 = true ; bPerfectMatch = true ; dParam0 = j + 1 ; dParam1 = c + 1 ; ptJoint0 = vPnt1[j+1] ; ptJoint1 = vPnt0[c+1] ; bMismatch = false ; } else if ( ( vEdgeSplit0[c] && ! bAdvance1) || (vEdgeSplit1[j] && ! bAdvance0)) { // da una parte ho uno spigolo e dall'altra non posso avanzare bAdvance0 = false ; bAdvance1 = false ; } if ( bAdvance0 && ! bMismatch) { if ( c < dLastParamMatch1 + EPS_SMALL) ++c ; // ho match con lo start if ( dParam0 < EPS_SMALL) { vPairs.emplace_back( c + nSplit0, j + nSplit1) ; } // ho un match con l'end else if ( dParam0 > nSpanU1 - EPS_SMALL ) { vPairs.emplace_back( c + nSplit0, j + nSplit1) ; } if ( dParam0 <= dLastParamMatch0 || AreSamePointApprox( ptJoint0, ptLastPointMatch0)) { dParam0 = dLastParamMatch0 ; vPairs.emplace_back( c + nSplit0, j + nSplit1) ; } else { // ho un match con una joint esistente if ( abs( dParam0 - round( dParam0)) < EPS_SMALL) { if ( dParam0 > dLastParamMatch0 + EPS_SMALL) vPairs.emplace_back( c + nSplit0, j + nSplit1 + 1) ; else vPairs.emplace_back( c + nSplit0, j + nSplit1) ; } // altrimenti lo aggiungo else { vdSplit1.push_back( dParam0) ; nSplit1 = vdSplit1.size() ; vPairs.emplace_back( c + nSplit0, j + nSplit1) ; } } dLastParamMatch0 = dParam0 ; ptLastPointMatch0 = ptJoint0 ; dLastParamMatch1 = c ; ptLastPointMatch1 = vPnt0[c] ; if ( bPerfectMatch) ++j ; } if ( bAdvance1 && ! bPerfectMatch && ! bMismatch) { if ( j < dLastParamMatch0 + EPS_SMALL) ++j ; // ho un match con lo start if ( dParam1 < EPS_SMALL) { vPairs.emplace_back( c + nSplit0, j + nSplit1) ; } // ho un match con l'end else if ( dParam1 > nSpanU0 - EPS_SMALL ) { vPairs.emplace_back( c + nSplit0, j + nSplit1) ; } if ( dParam1 <= dLastParamMatch1 || AreSamePointApprox( ptJoint1, ptLastPointMatch1)) { dParam1 = dLastParamMatch1 ; vPairs.emplace_back( c + nSplit0, j + nSplit1) ; } else { // ho un match con una joint esistente if ( abs( dParam1 - round( dParam1)) < EPS_SMALL) { if ( dParam1 > dLastParamMatch1 + EPS_SMALL) vPairs.emplace_back( c + nSplit0 + 1, j + nSplit1) ; else vPairs.emplace_back( c + nSplit0, j + nSplit1) ; } //altrimenti lo aggiungo else { vdSplit0.push_back( dParam1) ; nSplit0 = vdSplit0.size() ; vPairs.emplace_back( c + nSplit0, j + nSplit1) ; } } dLastParamMatch1 = dParam1 ; ptLastPointMatch1 = ptJoint1 ; dLastParamMatch0 = j ; ptLastPointMatch0 = vPnt1[j] ; } if ( ( ! bAdvance0 && ! bAdvance1) || bMismatch) { // sono arrivato ad un caso di incrocio!! //1. se sono vicino ad uno spigolo su entrambe le curve allora accoppio gli spigoli e il resto lo aggiusto di conseguenza //2. se sono in una zona di mismatch faccio il match usando la parametrizzazione sulla lunghezza dei tratti di curve interessati //3. sennò accoppio comunque ( la coppia corrente) bool bEdgeFoundOnSecond = false ; if ( vEdgeSplit0[c] && vEdgeSplit1[j]) { // questo caso non è previsto !!!!! e non mi aspetto che avvenga LOG_DBG_ERR( GetEGkLogger(), "RLT_B_MINDIST_PLUS: a not handled mismatch was found, type 0") ; } if ( vEdgeSplit0[c] && ! bMismatch) { ++c ; ++j ; //cerco se ho uno split anche su U1 entro una distanza che sia al massimo il doppio di quella che c'è col punto a mindist double dDist = 0 ; double dMaxDist = Dist( vPnt0[c], vMatch0[c-1].first) * 2 ; int j_temp = j ; while ( dDist < dMaxDist) { if ( vEdgeSplit1[j_temp]) { bEdgeFoundOnSecond = true ; break ; } ++j_temp ; dDist = Dist( vPnt0[c], vPnt1[j_temp]) ; } if ( bEdgeFoundOnSecond) { // collego i due punti in cui ho il passaggio di un edge e tutti i punti che restano non accoppiati li metto come Rep for ( int i = 0 ; i < j_temp - j + 1 ; ++i) vPairs.emplace_back( c + nSplit0, j + i + nSplit1) ; j = j_temp ; dLastParamMatch0 = j - 1 ; ptLastPointMatch0 = vPnt1[j] ; } else { bMismatch = true ; --c ; --j ; } } else if ( vEdgeSplit1[j] && ! bMismatch) { ++c ; ++j ; //cerco se ho uno split anche su U1 entro una distanza che sia al massimo il doppio di quella che c'è col punto a mindist double dDist = 0 ; double dMaxDist = Dist( vPnt1[j], vMatch1[j-1].first) * 2 ; int c_temp = c ; while ( dDist < dMaxDist) { if ( vEdgeSplit0[c_temp]) { bEdgeFoundOnSecond = true ; break ; } ++c_temp ; dDist = Dist( vPnt1[j], vPnt0[c_temp]) ; } if ( bEdgeFoundOnSecond) { // collego i due punti in cui ho il passaggio di un edge e tutti i punti che restano non accoppiati li metto come Rep for ( int i = 0 ; i < c_temp - c + 1 ; ++i) vPairs.emplace_back( c + i + nSplit0, j + nSplit1) ; c = c_temp ; dLastParamMatch1 = c - 1 ; ptLastPointMatch1 = vPnt0[c] ; } else { bMismatch = true ; --c ; --j ; } } if ( ( ! vEdgeSplit0[c] && ! vEdgeSplit1[j]) || bMismatch) { // non ci sono riferimenti per fare un match, quindi li cerco io ///////////OLD VRESION //++c ; //++j ; //vPairs.emplace_back( c + nSplit0, j + nSplit1) ; //dLastParamMatch1 = c ; //ptLastPointMatch1 = vPnt0[c] ; //dLastParamMatch0 = j ; //ptLastPointMatch0 = vPnt1[j] ; //// potrei avere un mismatch, senza però avere degli spigoli.. // identifico la zona in cui ho il mismatch e parametrizzo localmente //conto quanti punti ho nel mezzo bool bEndOnEdge = false ; int c_temp = c, j_temp = j ; if ( ! bMismatch) { c_temp = c, j_temp = j ; bAdvance0 = true ; bAdvance1 = true ; int nParam0, nParam1 ; while ( bAdvance0) { dParam0 = vMatch0[c_temp].second ; nParam0 = int( round( dParam0)) ; dParam1 = vMatch1[nParam0].second ; nParam1 = int( round( dParam1)) ; if ( abs( nParam1 - c_temp) <= 2) bAdvance0 = false ; else ++ c_temp ; } while ( bAdvance1) { dParam1 = vMatch1[j_temp].second ; nParam1 = int( round( dParam1)) ; dParam0 = vMatch0[nParam1].second ; nParam0 = int( round( dParam0)) ; if ( abs( nParam0 - j_temp) <= 2) bAdvance1 = false ; else ++ j_temp ; } } else { // cerco se ho degli spigoli nella zona di mismatch // se ne ho definisco una regione compresa tra un estremo( start o end) e uno spigolo (presente su entrambe le curve) if ( bMismatch0 && bMismatch1) { int c_temp0 = vMismatch0[m0].second ; int j_temp0 = int( vMatch0[c_temp0].second) ; int j_temp1 = vMismatch1[m1].second ; int c_temp1 = int( vMatch1[j_temp1].second) ; c_temp = max( c_temp0, c_temp1) ; j_temp = max( j_temp0, j_temp1) ; } else if ( bMismatch0) { c_temp = vMismatch0[m0].second ; j_temp = int( vMatch0[c_temp].second) ; } else if ( bMismatch1){ j_temp = vMismatch1[m1].second ; c_temp = int( vMatch1[j_temp].second) ; } INTVECTOR vnEdges0, vnEdges1 ; for ( int i = c ; i < c_temp ; ++i) { if ( vEdgeSplit0[i]) vnEdges0.push_back(i) ; } for ( int i = j ; i < j_temp ; ++i) { if ( vEdgeSplit1[i]) vnEdges1.push_back(i) ; } // se ho degli edge che non matchano tra loro semplicemente tratto tutta la regione di mismatch insieme if ( ssize( vnEdges0) != ssize( vnEdges1) || ssize( vnEdges0) == 0 || ssize( vnEdges1) == 0) { ; } // se ho corrispondenza tra gli edge (in numero) allora (do per scontato di accoppiarli e ) li prendo come estremi di zone da trattare separatamente else { // definisco gli estremi della zona da riparametrizzare // controllo se ho già matchato parte della zona di mismatch // cerco la coppia successiva alla corrente per determinare la zona su cui lavorare ora bEndOnEdge = true ; // se un edge fa da chiusura a questo tratto bool bAtHalfwayEdge = false ; // se ho già percorso un tratto di questo mismatch e quindi parto da un edge for ( int i = 0 ; i < ssize( vnEdges0) ; ++i) { if ( c - 1 == vnEdges0[i]) { bAtHalfwayEdge = true ; if ( i < ssize( vnEdges0) - 1) { c_temp = vnEdges0[i+1] ; j_temp = vnEdges1[i+1] ; } else { if ( bMismatch0) { c_temp = vMismatch0[m0].second ; j_temp = int( vMatch0[c_temp].second) ; ++m0 ; } else if ( bMismatch1) { j_temp = vMismatch1[m1].second ; c_temp = int( vMatch1[j_temp].second) ; ++m1 ; } } break ; } } // se non avevo già affrontato la prima parte allora setto come fine della prima zona il primo edge che incontrerò if ( ! bAtHalfwayEdge) { c_temp = vnEdges0[0] ; j_temp = vnEdges1[0] ; } } } if ( j_temp == ssize( vMatch1)) --j_temp ; if ( c_temp == ssize( vMatch0)) -- c_temp ; // se non sono avanzato, allora mi basta accoppiare i due punti in questione if ( c_temp == c && j_temp == j) { ++c ; ++j ; vPairs.emplace_back( c + nSplit0, j + nSplit1) ; dLastParamMatch1 = c ; ptLastPointMatch1 = vPnt0[c] ; dLastParamMatch0 = j ; ptLastPointMatch0 = vPnt1[j] ; } // se sono dovuto avanzare per trovare delle coppie che tornano a matchare allora ho effettivamente trovato una zona di mismatch else { // determino quale delle due coppie è il confine effettivo con la zona di mismatch e quale sarà la prima dopo il mismamtch if ( ! bEndOnEdge) { dParam0 = vMatch0[c_temp].second ; dParam1 = vMatch1[j_temp].second ; bAdvance0 = dParam0 < j_temp + 1 + EPS_SMALL ; bAdvance1 = dParam1 < c_temp + 1 + EPS_SMALL ; } else { bAdvance0 = true ; bAdvance1 = true ; } PtrOwner pCC0 ; PtrOwner pCC1 ; int nPointsBetween0 = 0 ; int nPointsBetween1 = 0 ; // in caso una delle due curve sia un punto ( se non è avanzato o se il match è precedente al punto attuale) // accoppio tutti i punti sull'altra curva con quello if ( bAdvance0 && bAdvance1) { if ( c_temp + 1 > dLastParamMatch1) pCC0.Set( CrvU0.CopyParamRange( dLastParamMatch1, c_temp + 1)) ; if ( j_temp + 1 > dLastParamMatch0) pCC1.Set( CrvU1.CopyParamRange( dLastParamMatch0, j_temp + 1)) ; nPointsBetween0 = int( c_temp + 1 - floor( dLastParamMatch1) - 1) ; nPointsBetween1 = int( j_temp + 1 - floor( dLastParamMatch0) - 1) ; } else if ( bAdvance0){ if ( c_temp + 1 > dLastParamMatch1) pCC0.Set( CrvU0.CopyParamRange( dLastParamMatch1, c_temp + 1)) ; if ( dParam0 > dLastParamMatch0) pCC1.Set( CrvU1.CopyParamRange( dLastParamMatch0, dParam0)) ; nPointsBetween0 = int( c_temp + 1 - floor( dLastParamMatch1) - 1) ; nPointsBetween1 = int( ceil( dParam0) - floor( dLastParamMatch0) - 1) ; } else if ( bAdvance1) { if ( dParam1 > dLastParamMatch1) pCC0.Set( CrvU0.CopyParamRange( dLastParamMatch1, dParam1)) ; if ( j_temp + 1 > dLastParamMatch0) pCC1.Set( CrvU1.CopyParamRange( dLastParamMatch0, j_temp + 1)) ; nPointsBetween0 = int( ceil( dParam1) - floor( dLastParamMatch1) - 1) ; nPointsBetween1 = int( j_temp + 1 - floor( dLastParamMatch0) - 1) ; } if ( ( IsNull( pCC0) || ! pCC0->IsValid()) && ( IsNull( pCC1) || ! pCC1->IsValid())) { LOG_DBG_ERR( GetEGkLogger(), "RLT_B_MINDIST_PLUS: failed attempt to identify mismatch region, type0") ; return false ; } bool bCC0Valid = ! IsNull( pCC0) && pCC0->IsValid() ; bool bCC1Valid = ! IsNull( pCC1) && pCC1->IsValid() ; double dLen0 ; bCC0Valid ? pCC0->GetLength( dLen0) : dLen0 = 0 ; double dLen1 ; bCC1Valid ? pCC1->GetLength( dLen1) : dLen1 = 0 ; if ( ( nPointsBetween0 < 0 && dLen0 > 0) || ( nPointsBetween1 < 0 && dLen1 > 0)) { LOG_DBG_ERR( GetEGkLogger(), "RLT_B_MINDIST_PLUS: failed attempt to identify mismatch region, type1") ; return false ; } nPointsBetween0 = max( 0, nPointsBetween0) ; nPointsBetween1 = max( 0, nPointsBetween1) ; DBLVECTOR vdParamPos0 ; vdParamPos0.reserve( nPointsBetween0 + 2) ; DBLVECTOR vdParamPos1 ; vdParamPos1.reserve( nPointsBetween1 + 2) ; if ( bCC0Valid) { for ( int k = 0 ; k <= nPointsBetween0 ; ++k) { double dLen = 0 ; pCC0->GetLengthAtParam( k, dLen) ; vdParamPos0.push_back( dLen / dLen0) ; } } else vdParamPos0.push_back( 0) ; vdParamPos0.push_back( 1) ; if ( bCC1Valid) { for ( int k = 0 ; k <= nPointsBetween1 ; ++k) { double dLen = 0 ; pCC1->GetLengthAtParam( k, dLen) ; vdParamPos1.push_back( dLen / dLen1) ; } } else vdParamPos1.push_back( 0) ; vdParamPos1.push_back( 1) ; bool bSplitToAdd = true ; int c0 = 1, c1 = 1 ; while ( bSplitToAdd) { if ( c0 > ssize( vdParamPos0) - 1 && c1 > ssize( vdParamPos1) - 1) { LOG_DBG_ERR( GetEGkLogger(), "Surf Bez Ruled Guided: error 1 while reparametrizing some section") ; return false ; } // se ho una corrispondenza tra punti ( e non sono alla fine del tratto) allora non aggiungo split if ( abs( vdParamPos0[c0] - vdParamPos1[c1]) < EPS_SMALL && vdParamPos0[c0] < 1) { ++c ; ++j ; vPairs.emplace_back( c + nSplit0, j + nSplit1) ; ptLastPointMatch0 = vPnt1[j] ; dLastParamMatch0 = j ; ptLastPointMatch1 = vPnt0[c] ; dLastParamMatch1 = c ; ++c0 ; ++c1 ; } // se non ho corrispondenza allora aggiungo uno split sulla curva a cui manca il punto corrispondente else if ( vdParamPos0[c0] < vdParamPos1[c1]) { double dParamMatch ; if ( bCC1Valid) { double dPar ; pCC1->GetParamAtLength( dLen1 * vdParamPos0[c0], dPar) ; Point3d ptMatch ; pCC1->GetPointD1D2( dPar, ICurve::Side::FROM_MINUS, ptMatch) ; ptLastPointMatch0 = ptMatch ; CrvU1.GetParamAtPoint( ptMatch, dParamMatch) ; dLastParamMatch0 = dParamMatch ; } else dParamMatch = dLastParamMatch0 ; // se lo split non è in prossimità di una joint già esistente allora lo aggiungo double dLastSplit = vdSplit1.empty() ? 0 : vdSplit1.back() ; if ( abs( dParamMatch - round( dParamMatch)) > EPS_SMALL && dParamMatch > dLastSplit + EPS_SMALL) { vdSplit1.push_back( dParamMatch) ; nSplit1 = vdSplit1.size() ; } else if ( dParamMatch = round( dParamMatch) ; dParamMatch > j){ ++ j ; } ++c ; vPairs.emplace_back( c + nSplit0, j + nSplit1) ; ptLastPointMatch1 = vPnt0[c] ; dLastParamMatch1 = c ; ++c0 ; } else if ( vdParamPos0[c0] > vdParamPos1[c1]) { double dParamMatch ; if ( bCC0Valid) { double dPar ; pCC0->GetParamAtLength( dLen0 * vdParamPos1[c1], dPar) ; Point3d ptMatch ; pCC0->GetPointD1D2( dPar, ICurve::Side::FROM_MINUS, ptMatch) ; ptLastPointMatch1 = ptMatch ; CrvU0.GetParamAtPoint( ptMatch, dParamMatch) ; dLastParamMatch1 = dParamMatch ; } else dParamMatch = dLastParamMatch1 ; // se lo split non è in prossimità di una joint già esistente allora lo aggiungo if ( abs( dParamMatch - round( dParamMatch)) > EPS_SMALL && dParamMatch > vdSplit0.back() + EPS_SMALL) { vdSplit0.push_back( dParamMatch) ; nSplit0 = vdSplit0.size() ; } else if ( dParamMatch = round( dParamMatch) ; dParamMatch > c){ ++ c ; } ++j ; vPairs.emplace_back( c + nSplit0, j + nSplit1) ; ptLastPointMatch0 = vPnt1[j] ; dLastParamMatch0 = j ; ++c1 ; } else { LOG_DBG_ERR( GetEGkLogger(), "Surf Bez Ruled Guided: error 2 while reparametrizing some section") ; return false ; } bSplitToAdd = ! ( c0 == ssize( vdParamPos0) - 1 && c1 == ssize( vdParamPos1) - 1) ; } } } } bAdvance = ! ( c >= ssize( vMatch0) - 1 && j >= ssize( vMatch1) - 1) ; } // applico effettivamente gli split int nUnit = 0 ; if ( ! vdSplit1.empty()) nUnit = int( vdSplit1.back()) ; for ( int z = int( vdSplit1.size() - 1) ; z >= 0 ; --z) { double dSplit = vdSplit1[z] ; int nSplit = int( dSplit) ; // se sto cercando di fare uno split sulla stessa curva che ho già splittato al passaggio precedente allora devo // riscalare if ( nSplit == nUnit && z < int( vdSplit1.size() - 1)) { dSplit = nSplit + ( dSplit - nSplit) / (vdSplit1[z+1] - nSplit); } nUnit = nSplit ; CrvU1.AddJoint( dSplit) ; } if ( ! vdSplit0.empty()) nUnit = int( vdSplit0.back()) ; for ( int z = int( vdSplit0.size() - 1) ; z >= 0 ; --z) { double dSplit = vdSplit0[z] ; int nSplit = int( dSplit) ; // se sto cercando di fare uno split sulla stessa curva che ho già splittato al passaggio precedente allora devo // riscalare if ( nSplit == nUnit && z < int( vdSplit0.size() - 1)) { dSplit = nSplit + ( dSplit - nSplit) / (vdSplit0[z+1] - nSplit); } nUnit = nSplit ; CrvU0.AddJoint( dSplit) ; } nSpanU0 = CrvU0.GetCurveCount() ; nSpanU1 = CrvU1.GetCurveCount() ; // aggiungo l'ultima coppia vPairs.emplace_back( nSpanU0, nSpanU1) ; // trovo il numero di span che dovrà avere la superficie nSpanU = int(vPairs.size()) - 1 ; nSecondRowInd = nDegU * nSpanU + 1 ; // inizializzo la superficie Init( nDegU, nDegV, nSpanU, nSpanV, bRat) ; // aggiungo i punti di controllo scorrendo in contemporanea le due curve int nAddedSpan = 0 ; int nCrv0 = 0 ; int nCrv1 = 0 ; bool bLast0 = false ; bool bLast1 = false ; bool bOk = true ; while ( nAddedSpan < nSpanU && bOk) { nCrv0 = vPairs[nAddedSpan].first ; nCrv1 = vPairs[nAddedSpan].second ; bool bRep0 = nCrv0 < nSpanU0 ? nCrv0 == vPairs[nAddedSpan + 1].first : nCrv0 == vPairs[nAddedSpan - 1].first ; bool bRep1 = nCrv1 < nSpanU1 ? nCrv1 == vPairs[nAddedSpan + 1].second : nCrv1 == vPairs[nAddedSpan - 1].second ; if ( nCrv0 >= nSpanU0){ nCrv0 = nSpanU0 - 1 ; bLast0 = true ; } if ( nCrv1 >= nSpanU1) { nCrv1 = nSpanU1 - 1 ; bLast1 = true ; } const ICurveBezier* pSubCrv0 = GetCurveBezier( CrvU0.GetCurve( nCrv0)) ; for ( int i = nAddedSpan == 0 ? 0 : 1 ; i < nLastPoint ; ++ i) { int nInd = i ; // se ho una ripetizione allora riaggiungo l'ultimo punto. Se sono ancora alla curva 0 invece devo aggiungere lo start della curva U0 if ( bRep0 || bLast0) nInd = ! bLast0 ? 0 : nDegU ; if ( ! bRat) bOk = SetControlPoint( nAddedSpan * nDegU + i, pSubCrv0->GetControlPoint( nInd)) ; else bOk = SetControlPoint( nAddedSpan * nDegU + i, pSubCrv0->GetControlPoint( nInd), pSubCrv0->GetControlWeight( nInd)) ; } const ICurveBezier* pSubCrv1 = GetCurveBezier( CrvU1.GetCurve( nCrv1)) ; for ( int i = nAddedSpan == 0 ? 0 : 1 ; i < nLastPoint ; ++ i) { int nInd = i ; // se ho una ripetizione allora riaggiungo l'ultimo punto. Se sono ancora alla curva 0 invece devo aggiungere lo start della curva U1 if ( bRep1 || bLast1) nInd = ! bLast1 ? 0 : nDegU ; if ( ! bRat) bOk = SetControlPoint( nSecondRowInd + nAddedSpan * nDegU + i, pSubCrv1->GetControlPoint( nInd)) ; else bOk = SetControlPoint( nSecondRowInd + nAddedSpan * nDegU + i, pSubCrv1->GetControlPoint( nInd), pSubCrv1->GetControlWeight( nInd)) ; } ++ nAddedSpan ; } #if SAVERULEDISO //debug vGeo.clear() ; ICURVEPOVECTOR vCrv ; GetAllPatchesIsocurves( false, vCrv) ; for ( int i = 0 ; i < ssize( vCrv) ; ++i) { vGeo.push_back( vCrv[i]->Clone()) ; } vector vCol( ssize( vCrv)) ; fill( vCol.begin(), vCol.end(), Color( 255,0,128)) ; SaveGeoObj( vGeo, vCol, "D:/Temp/bezier/ruled/isoCurves.nge") ; //debug vGeo.clear() ; vGeo.push_back( CrvU0.Clone()) ; vGeo.push_back( CrvU1.Clone()) ; vCol.clear() ; vCol.push_back(Color(0,64,128)) ; vCol.push_back(Color(128,64,0)) ; SaveGeoObj( vGeo, vCol, "D:/Temp/bezier/ruled/NewCurves.nge") ; #endif return bOk ; } else if ( RLT_B_LENPAR ) { // da implementare return false ; } return true ; } // Struttura per Spigolo struct SharpEdge { double dParU ; Point3d ptEdge ; Vector3d vtTanPrev, vtTanNext ; SharpEdge( double dU, const Point3d ptE, const Vector3d& vtTP, const Vector3d& vtTA) : dParU( dU), ptEdge( ptE), vtTanPrev( vtTP), vtTanNext( vtTA) {} } ; typedef vector SHARPEDGEVECTOR ; // Struttura per Punto a Curvatura Massima struct CurvaturePoint { double dParU ; Point3d ptCurvature ; Vector3d vtTanPrev, vtTanNext ; CurvaturePoint( double dU, const Point3d ptE, const Vector3d& vtTP, const Vector3d& vtTA) : dParU( dU), ptCurvature( ptE), vtTanPrev( vtTP), vtTanNext( vtTA) {} } ; typedef vector SHARPEDGEVECTOR ; // --------------------------------------------------------------------------- // Calcolo delle Curve di Sync tra gli spigoli di una curva e l'altra curva nella sua integrità // [Dato un vettore di SharpEdge di una Curva di Bordo si calcola la Linea di Sincronizzazione tra ognuno di essi // e l'altra Curva di Bordo] // -> Restituisce un vettore di Linee di Sincronizzazione static bool CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite* pCompoSubEdge, const SHARPEDGEVECTOR& vSharpSubEdge, BIPNTVECTOR& vSyncLines) { vSyncLines.clear() ; // Verifico la validità del SubEdge su cui trovare il punto di Sincronizzazione if ( pCompoSubEdge == nullptr || ! pCompoSubEdge->IsValid()) return false ; // Se non ho spigoli da controllare, non devo fare nulla if ( vSharpEdge.empty()) return true ; // Se il numero di Spigoli coincide, allora li unisco sequenzialmente tra loro ( considerazione buona ???) if ( ssize( vSharpEdge) == ssize( vSharpSubEdge)) { for ( int i = 0 ; i < ssize( vSharpEdge) ; ++ i) vSyncLines.emplace_back( vSharpEdge[i].ptEdge, vSharpSubEdge[i].ptEdge) ; return false ; } // Scorro gli SharpEdges double dUPrevSubEdge = 0., dUCurrSubEdge = 0. ; for ( const SharpEdge& mySharpEdge : vSharpEdge) { // Recupero il parametro sulla curva di sincronizzazione // --- Piano di taglio per punto a distanza minima IntersCurvePlane ICP( *pCompoSubEdge, mySharpEdge.ptEdge, Media( mySharpEdge.vtTanPrev, mySharpEdge.vtTanNext)) ; int nIndParCloser = -1, nIndPointCloser = -1 ; double dSqMinDist = INFINITO ; for ( int nInfo = 0 ; nInfo < ICP.GetIntersCount() ; ++ nInfo) { IntCrvPlnInfo aInfo ; if ( ICP.GetIntCrvPlnInfo( nInfo, aInfo) && aInfo.Ici[0].dU > dUPrevSubEdge) { if ( nIndParCloser == -1) nIndParCloser = nInfo ; double dSqDist = SqDist( mySharpEdge.ptEdge, aInfo.Ici[0].ptI) ; if ( dSqDist < dSqMinDist) { dSqMinDist = dSqDist ; nIndPointCloser = nInfo ; } } } bool bOkPlane = ( nIndParCloser != -1 && nIndPointCloser != -1) ; if ( bOkPlane) { // Se gli indici sono tra loro coerenti allora ho individuato il punto if ( nIndParCloser == nIndPointCloser) { IntCrvPlnInfo aInfo ; ICP.GetIntCrvPlnInfo( nIndParCloser, aInfo) ; dUCurrSubEdge = aInfo.Ici[0].dU ; } // Se gli indici sono discordi, devo scegliere quale dei due punti tenere else { // scelgo il punto più vicino al corrente IntCrvPlnInfo aInfoPt, aInfoPar ; ICP.GetIntCrvPlnInfo( nIndPointCloser, aInfoPt) ; ICP.GetIntCrvPlnInfo( nIndParCloser, aInfoPar) ; dUCurrSubEdge = ( SqDist( mySharpEdge.ptEdge, aInfoPt.Ici[0].ptI) < SqDist( mySharpEdge.ptEdge, aInfoPar.Ici[0].ptI) ? aInfoPt.Ici[0].dU : aInfoPar.Ici[0].dU) ; } } if ( ! bOkPlane) { // --- Altrimenti, cerco il punto a minima distanza DistPointCurve DPC( mySharpEdge.ptEdge, *pCompoSubEdge) ; int nFlag ; bool bOkMinDist = ( DPC.GetParamAtMinDistPoint( dUPrevSubEdge, dUCurrSubEdge, nFlag) && dUCurrSubEdge > dUPrevSubEdge) ; if ( ! bOkMinDist) { // --- Alla peggio mi posiziono a metà tra la posizione corrente e quella finale double dPrevLen ; pCompoSubEdge->GetLengthAtParam( dUPrevSubEdge, dPrevLen) ; double dLen ; pCompoSubEdge->GetLength( dLen) ; pCompoSubEdge->GetParamAtLength( ( dPrevLen + dLen) / 2., dUCurrSubEdge) ; } } // Verifico se tale parametro può essere avvicinato ad uno Spigolo presente sulla curva // NB. Come descitto sopra gli spigoli in generale è meglio sincronizzarli tra loro if ( ! vSharpSubEdge.empty()) { const double EDGE_LEN_TOL = 5. ; for ( const SharpEdge& mySharpSubEdge : vSharpSubEdge) { // Se la linea di sincronizzazione è progressiva in parametro (ovvero non si attorciglia) if ( mySharpSubEdge.dParU > dUPrevSubEdge) { double dLenSub ; pCompoSubEdge->GetLengthAtParam( mySharpSubEdge.dParU, dLenSub) ; double dLenSync ; pCompoSubEdge->GetLengthAtParam( dUCurrSubEdge, dLenSync) ; // E Sufficientemente vicina ad uno Spigolo sull'altra if ( abs( dLenSync - dLenSub) < EDGE_LEN_TOL) { // Sposto il suo punto finale sullo Spigolo dUCurrSubEdge = mySharpSubEdge.dParU ; break ; // prendo la prima dato che sono ordinate per parametro } } } } // Definisco una nuova linea dallo spigolo corrente al parametro trovato sull'altra curva di bordo vSyncLines.emplace_back( mySharpEdge.ptEdge, P_INVALID) ; pCompoSubEdge->GetPointD1D2( dUCurrSubEdge, ICurve::FROM_MINUS, vSyncLines.back().second) ; // aggiorno i parametri dUPrevSubEdge = dUCurrSubEdge ; } return true ; } // --------------------------------------------------------------------------- // Gestione degli spigoli all'interno della Quadrangolazione corrente static bool ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComposite* pSubEdge2, BIPNTVECTOR& vSyncLines) { // Verifica validità delle curve if ( pSubEdge1 == nullptr || ! pSubEdge1->IsValid() || pSubEdge2 == nullptr || ! pSubEdge2->IsValid()) return false ; // Recupero il numero delle curve dei Sottotratti correnti dei bordi int nCrv1 = pSubEdge1->GetCurveCount() ; int nCrv2 = pSubEdge2->GetCurveCount() ; if ( nCrv1 < 2 && nCrv2 < 2) return true ; const double COS_EDGE_LIMIT = cos( 35. * DEGTORAD) ; SHARPEDGEVECTOR vSharpEdges1 ; vSharpEdges1.reserve( max( 0, nCrv1 - 1)) ; SHARPEDGEVECTOR vSharpEdges2 ; vSharpEdges2.reserve( max( 0, nCrv2 - 1)) ; // --- Cerco gli Spigoli sul SottoTratto del primo bordo // Se la CurvaA ha più di una sottocurva, verifico l'esistenza di Spigoli (Sharp Edges) if ( nCrv1 > 1) { Point3d ptCurr1 ; // Scorro le sottocurve for ( int nCrv = 0 ; nCrv < nCrv1 - 1 ; ++ nCrv) { // Recupero il parametro corrente sulla curva ( estremo finale) double dU = double( nCrv + 1) ; // Recupero le tangenti prima e dopo tale parametro Vector3d vtTanPrev ; pSubEdge1->GetPointD1D2( dU, ICurve::FROM_MINUS, ptCurr1, &vtTanPrev) ; Vector3d vtTanNext ; pSubEdge1->GetPointD1D2( dU, ICurve::FROM_PLUS, ptCurr1, &vtTanNext) ; // Verifico se ho uno spigolo vtTanPrev.Normalize() ; vtTanNext.Normalize() ; if ( vtTanPrev * vtTanNext < COS_EDGE_LIMIT) vSharpEdges1.emplace_back( dU, ptCurr1, vtTanPrev, vtTanNext) ; } } // --- Cerco gli spigoli sul Sottotratto del secondo bordo // Se la Curva B ha più di una sottocurva, verifico l'esistena di Spigoli (Sharp Edges) if ( nCrv2 > 1) { Point3d ptCurr2 ; // Scorro le sottocurve for ( int nCrv = 0 ; nCrv < nCrv2 - 1 ; ++ nCrv) { // Recupero il parametro corrente sulla curva ( estremo finale) double dU = double( nCrv + 1) ; // Recupero le tangenti prima e dopo tale parametro Vector3d vtTanPrev ; pSubEdge2->GetPointD1D2( dU, ICurve::FROM_MINUS, ptCurr2, &vtTanPrev) ; Vector3d vtTanNext ; pSubEdge2->GetPointD1D2( dU, ICurve::FROM_PLUS, ptCurr2, &vtTanNext) ; // Verifico se ho uno spigolo vtTanPrev.Normalize() ; vtTanNext.Normalize() ; if ( vtTanPrev * vtTanNext < COS_EDGE_LIMIT) vSharpEdges2.emplace_back( dU, ptCurr2, vtTanPrev, vtTanNext) ; } } // Se non ho alcuno spigolo per entrambe le curve di Bordo, non faccio nulla if ( vSharpEdges1.empty() && vSharpEdges2.empty()) return true ; // Recupero le Curve di Sincronizzazione associate agli spigoli dal primo Bordo al secondo Bordo BIPNTVECTOR vSyncLines1 ; CalcSyncPointFromEdge( vSharpEdges1, pSubEdge2, vSharpEdges2, vSyncLines1) ; // Recupero le Curve di Sincronizzazione associate agli spigoli dal secondo Bordo al primo Bordo BIPNTVECTOR vSyncLines2 ; CalcSyncPointFromEdge( vSharpEdges2, pSubEdge1, vSharpEdges1, vSyncLines2) ; // Rimuovo eventuali linee sovrapposte ( le rimuovo da vSyncLines2) const double TOL = 250. * EPS_SMALL ; erase_if( vSyncLines2, [&]( const BIPOINT& SyncLine2) { return any_of( vSyncLines1.begin(), vSyncLines1.end(), [&]( const BIPOINT& SyncLine1) { return ( AreSamePointEpsilon( SyncLine1.first, SyncLine2.second, TOL) && AreSamePointEpsilon( SyncLine1.second, SyncLine2.first, TOL)) ; }) ; }) ; // Ordino tutte le curve in base al parametro dU della prima curva di Bordo vector> vSyncLinesPar ; vSyncLinesPar.reserve( vSyncLines1.size() + vSyncLines2.size()) ; for ( BIPOINT& SyncLine1 : vSyncLines1) { vSyncLinesPar.emplace_back( make_pair( make_pair( SyncLine1.first, SyncLine1.second), vSyncLinesPar.empty() ? 0. : vSyncLinesPar.back().second)) ; pSubEdge1->GetParamAtPoint( SyncLine1.first, vSyncLinesPar.back().second, TOL) ; } for ( BIPOINT& SyncLine2 : vSyncLines2) { vSyncLinesPar.emplace_back( make_pair( make_pair( SyncLine2.second, SyncLine2.first), vSyncLinesPar.empty() ? 0. : vSyncLinesPar.back().second)) ; pSubEdge1->GetParamAtPoint( SyncLine2.second, vSyncLinesPar.back().second, TOL) ; } sort( vSyncLinesPar.begin(), vSyncLinesPar.end(), []( const auto& SyncLineParA, const auto& SyncLineParB) { return SyncLineParA.second < SyncLineParB.second ; }) ; // Controllo che le linee di sincronizzazione non si intreccino e restituisco il risultato // NB. Inserisco solo che curve che progressivamente non si intrecciano sulla seconda curva di Bordo double dUSecondPrev = EPS_PARAM ; for ( auto Iter = vSyncLinesPar.begin() ; Iter != vSyncLinesPar.end() ; ++ Iter) { double dUSecondCurr ; pSubEdge2->GetParamAtPoint( ( *Iter).first.second, dUSecondCurr, TOL) ; if ( dUSecondCurr > dUSecondPrev + EPS_SMALL) { vSyncLines.emplace_back( Iter->first) ; dUSecondPrev = dUSecondCurr ; } } return true ; } // --------------------------------------------------------------------------- // Calcolo delle Curve di Sync per evitare Twist static bool ManageTwistInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComposite* pSubEdge2, BIPNTVECTOR& vSyncLines, bool& bEraseLastIso) { // Verifica validità delle curve if ( pSubEdge1 == nullptr || ! pSubEdge1->IsValid() || pSubEdge2 == nullptr || ! pSubEdge2->IsValid()) return false ; const double COS_LIMIT = cos( 50. * DEGTORAD) ; const int NUM_SAMPLE_PNT = 20 ; const int nStepSkip = NUM_SAMPLE_PNT / 10 ; #if DEBUG_CURVATURE vector> _vPtK1 ; vector> _vPtK2 ; IGeomDB* pGeomDB = GetCurrGeomDB() ; VERIFY_GEOMDB( pGeomDB, false) #endif // --- Verifico il cambiamento di Deviazione angolare tra Inizio-Fine della prima curva di Bordo Vector3d vtS1 ; pSubEdge1->GetStartDir( vtS1) ; Vector3d vtE1 ; pSubEdge1->GetEndDir( vtE1) ; double dLen1 = - EPS_SMALL ; double dLen2 = - EPS_SMALL ; pSubEdge1->GetLength( dLen1) ; pSubEdge2->GetLength( dLen2) ; double dMyDist = min( dLen1, dLen2) * 0.85 ; bool bSplit1 = ( vtS1 * vtE1 < COS_LIMIT) ; if ( bSplit1) { // Individuo il punto a Curvatura Massima double dKMax1 = - INFINITO + 1, dUK1Max = - EPS_SMALL ; Point3d ptKMax1 ; Vector3d vtTanKMax1Prev, vtTanKMax1Next ; for ( int i = nStepSkip ; i <= NUM_SAMPLE_PNT - nStepSkip ; ++ i) { // Ricavo la Lunghezza corrente e il parametro dU associato double dCurrLen1 = Clamp( i * ( dLen1 / NUM_SAMPLE_PNT), 0., dLen1) ; double dUCurr1 ; pSubEdge1->GetParamAtLength( dCurrLen1, dUCurr1) ; // Calcolo la Curvatura Point3d ptCurrSub1 ; Vector3d vtCurrSub1Der1Prev, vtCurrSub1Der2Prev, vtCurrSub1Der1Next, vtCurrSub1Der2Next ; pSubEdge1->GetPointD1D2( dUCurr1, ICurve::FROM_MINUS, ptCurrSub1, &vtCurrSub1Der1Prev, &vtCurrSub1Der2Prev) ; pSubEdge1->GetPointD1D2( dUCurr1, ICurve::FROM_PLUS, ptCurrSub1, &vtCurrSub1Der1Next, &vtCurrSub1Der2Next) ; Vector3d vtCurrSub1Der1 = Media( vtCurrSub1Der1Prev, vtCurrSub1Der1Next) ; Vector3d vtCurrSub1Der2 = Media( vtCurrSub1Der2Prev, vtCurrSub1Der2Next) ; double dCurrK1 = ( vtCurrSub1Der1 ^ vtCurrSub1Der2).Len() / max( EPS_SMALL, Pow( vtCurrSub1Der1.Len(), 3)) ; // Se maggiore della massima trovata, aggiorno la massima if ( dCurrK1 > dKMax1) { dKMax1 = dCurrK1 ; ptKMax1 = ptCurrSub1 ; dUK1Max = dUCurr1 ; vtTanKMax1Prev = vtCurrSub1Der1Prev ; vtTanKMax1Next = vtCurrSub1Der1Next ; } #if DEBUG_CURVATURE _vPtK1.emplace_back( make_pair( ptCurrSub1, dCurrK1)) ; #endif } vtTanKMax1Prev.Normalize() ; vtTanKMax1Next.Normalize() ; // Calcolo la nuova linea di sincronizzazione CurvaturePoint myCurvaturePoint( dUK1Max, ptKMax1, vtTanKMax1Prev, vtTanKMax1Next) ; double dUOn2 ; double dLenCurr2 ; GetIsoPointOnSecondCurve( pSubEdge1, pSubEdge2, dUK1Max, dUOn2, dMyDist, 0, 0, dLenCurr2, dLen2) ; Point3d ptOn2 ; pSubEdge2->GetPointD1D2( dUOn2, ICurve::FROM_MINUS, ptOn2) ; vSyncLines.emplace_back( ptKMax1, ptOn2) ; } // --- Verifico il cambiamento di Deviazione angolare tra Inizio-Fine del tratto per la prima curva di Bordo Vector3d vtS2 ; pSubEdge2->GetStartDir( vtS2) ; Vector3d vtE2 ; pSubEdge2->GetEndDir( vtE2) ; bool bSplit2 = ( vtS2 * vtE2 < COS_LIMIT) ; if ( bSplit2) { // Individuo il punto a Curvatura Massima double dKMax2 = - INFINITO + 1, dUK2Max = - EPS_SMALL ; Point3d ptKMax2 ; Vector3d vtTanKMax2Prev, vtTanKMax2Next ; for ( int i = nStepSkip ; i <= NUM_SAMPLE_PNT - nStepSkip ; ++ i) { // Ricavo la Lunghezza corrente e il parametro dU associato double dCurrLen2 = Clamp( i * ( dLen2 / NUM_SAMPLE_PNT), 0., dLen2) ; double dUCurr2 ; pSubEdge2->GetParamAtLength( dCurrLen2, dUCurr2) ; // Calcolo la Curvatura Point3d ptCurrSub2 ; Vector3d vtCurrSub2Der1Prev, vtCurrSub2Der2Prev, vtCurrSub2Der1Next, vtCurrSub2Der2Next ; pSubEdge2->GetPointD1D2( dUCurr2, ICurve::FROM_MINUS, ptCurrSub2, &vtCurrSub2Der1Prev, &vtCurrSub2Der2Prev) ; pSubEdge2->GetPointD1D2( dUCurr2, ICurve::FROM_PLUS, ptCurrSub2, &vtCurrSub2Der1Next, &vtCurrSub2Der2Next) ; Vector3d vtCurrSub2Der1 = Media( vtCurrSub2Der1Prev, vtCurrSub2Der1Next) ; Vector3d vtCurrSub2Der2 = Media( vtCurrSub2Der2Prev, vtCurrSub2Der2Next) ; double dCurrK2 = ( vtCurrSub2Der1 ^ vtCurrSub2Der2).Len() / max( EPS_SMALL, Pow( vtCurrSub2Der1.Len(), 3)) ; // Se maggiore della massima trovata, aggiorno la massima if ( dCurrK2 > dKMax2) { dKMax2 = dCurrK2 ; ptKMax2 = ptCurrSub2 ; dUK2Max = dUCurr2 ; vtTanKMax2Prev = vtCurrSub2Der1Prev ; vtTanKMax2Next = vtCurrSub2Der1Next ; } #if DEBUG_CURVATURE _vPtK1.emplace_back( make_pair( ptCurrSub2, dCurrK2)) ; #endif } vtTanKMax2Prev.Normalize() ; vtTanKMax2Next.Normalize() ; // Calcolo la nuova linea di sincronizzazione CurvaturePoint myCurvaturePoint( dUK2Max, ptKMax2, vtTanKMax2Prev, vtTanKMax2Next) ; double dUOn1 ; double dLenCurr1 ; GetIsoPointOnSecondCurve( pSubEdge2, pSubEdge1, dUK2Max, dUOn1, dMyDist, 0, 0, dLenCurr1, dLen1) ; Point3d ptOn1 ; pSubEdge1->GetPointD1D2( dUOn1, ICurve::FROM_MINUS, ptOn1) ; vSyncLines.emplace_back( ptOn1, ptKMax2) ; } #if DEBUG_CURVATURE // Curvatura minima -> AQUA | Curvatura massima -> ORANGE auto [itMin1, itMax1] = std::minmax_element( _vPtK1.begin(), _vPtK1.end(), [](auto const& A, auto const& B) { return A.second < B.second ; }) ; double _dK1Max = itMax1->second ; double _dK1Min = itMin1->second ; for ( int i = 0 ; i < ssize( _vPtK1) ; ++ i) { double _t = 0. ; if ( _dK1Max > _dK1Min) _t = ( _vPtK1[i].second - _dK1Min) / ( _dK1Max - _dK1Min) ; double _dh = 30 - 180. ; // AQUA -> HSV( 180, 1, 1) | ORANGE -> HSV( 30, 1, 1) if ( _dh > 180.) _dh -= 360.0 ; if ( _dh < -180.0) _dh += 360.0 ; double _h = 180. + _t * _dh ; if ( _h < 0.0) _h += 360.0 ; if ( _h >= 360.0) _h -= 360.0 ; PtrOwner _ptC( CreateGeoPoint3d()) ; _ptC->Set( _vPtK1[i].first) ; int _nId = pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, Release( _ptC)) ; pGeomDB->SetMaterial( _nId, Color( GetColorFromHSV( HSV( _h, 1., 1.)))) ; } auto [itMin2, itMax2] = std::minmax_element( _vPtK2.begin(), _vPtK2.end(), [](auto const& A, auto const& B) { return A.second < B.second ; }) ; double _dK2Max = itMax2->second ; double _dK2Min = itMin2->second ; for ( int i = 0 ; i < ssize( _vPtK2) ; ++ i) { double _t = 0. ; if ( _dK2Max > _dK2Min) _t = ( _vPtK2[i].second - _dK2Min) / ( _dK2Max - _dK2Min) ; double _dh = 30 - 180. ; // CYAN -> HSV( 180, 1, 1) | ORANGE -> HSV( 30, 1, 1) if ( _dh > 180.) _dh -= 360.0 ; if ( _dh < -180.0) _dh += 360.0 ; double _h = 180. + _t * _dh ; if ( _h < 0.0) _h += 360.0 ; if ( _h >= 360.0) _h -= 360.0 ; PtrOwner _ptC( CreateGeoPoint3d()) ; _ptC->Set( _vPtK2[i].first) ; int _nId = pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, Release( _ptC)) ; pGeomDB->SetMaterial( _nId, Color( GetColorFromHSV( HSV( _h, 1., 1.)))) ; } #endif // Se non ho alcuna linea di sincronizzazione, non faccio nulla if ( vSyncLines.empty()) return true ; const double TOL = 250. * EPS_SMALL ; // Elimino tutte le curve di Sincronizzazione a ridosso degli estremi della Quadrangolazione // NB. Risultano ridondanti, creano SottoQuadrangolazioni molto strette o possono Inclinare eccssivamente l'Utensile // NB. Così facendo evito anche di creare curve di Sync lungo le diagonali della Quadrangolazione const double LEN_TOL = 2. ; const double dParTol = 0.1 ; double dLenPrev1 = - LEN_TOL, dLenPrev2 = - LEN_TOL ; for ( int i = 0 ; i < ssize( vSyncLines) ; ++ i) { double dSyncLen1 ; pSubEdge1->GetLengthAtPoint( vSyncLines[i].first, dSyncLen1, TOL) ; bool bErase = ( dSyncLen1 < max( LEN_TOL, dParTol * dLen1) || dSyncLen1 - dLenPrev1 < LEN_TOL) ; bEraseLastIso = dSyncLen1 > min( dLen1 - LEN_TOL, ( 1 - dParTol) * dLen1) ; if ( ! bErase) { double dSyncLen2 ; pSubEdge2->GetLengthAtPoint( vSyncLines[i].second, dSyncLen2, TOL) ; bErase = ( dSyncLen2 < max( LEN_TOL, dParTol * dLen2) || dSyncLen2 - dLenPrev2 < LEN_TOL) ; bEraseLastIso = bEraseLastIso || dSyncLen2 > min( dLen2 - LEN_TOL, ( 1 - dParTol) * dLen2) ; dLenPrev2 = dSyncLen2 ; } if ( bErase && ! bEraseLastIso) { vSyncLines.erase( vSyncLines.begin() + i) ; -- i ; } dLenPrev1 = dSyncLen1 ; if ( bEraseLastIso) break ; } if ( vSyncLines.empty()) return true ; // Se due curve di Sincronizzazione rimaste if ( ssize( vSyncLines) == 2) { // Se si sovrappongono, ne elimino una if ( AreSamePointEpsilon( vSyncLines[0].first, vSyncLines[1].first, LEN_TOL) && AreSamePointEpsilon( vSyncLines[0].second, vSyncLines[1].second, LEN_TOL)) vSyncLines.pop_back() ; } // Se due curve di Sincronizzazione rimaste if ( ssize( vSyncLines) == 2) { // Ordino tutte le linee di sincronizzazione in base al parametro dU della curva principale double dU0 ; pSubEdge1->GetParamAtPoint( vSyncLines[0].first, dU0, TOL) ; double dU1 ; pSubEdge1->GetParamAtPoint( vSyncLines[1].first, dU1, TOL) ; if ( dU0 > dU1 - EPS_SMALL) swap( vSyncLines[0], vSyncLines[1]) ; // Verifico che le curve non si intreccino pSubEdge2->GetParamAtPoint( vSyncLines[0].second, dU0, TOL) ; pSubEdge2->GetParamAtPoint( vSyncLines[1].second, dU1, TOL) ; if ( dU0 > dU1 - EPS_SMALL) vSyncLines.pop_back() ; // lascio solo la prima... ( bisognerebbe scegliere quale lasciare con un criterio migliore ?) } // se ne ho due tengo solo la prima if ( ssize( vSyncLines) == 2) vSyncLines.pop_back() ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, double dSampleLen) { BIPNTVECTOR vSyncLines ; return CreateSmoothRuledByTwoCurves( pCurve0, pCurve1, dSampleLen, vSyncLines) ; } //---------------------------------------------------------------------------- bool SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, double dSampleLen, BIPNTVECTOR& vSyncLines) { // converto in bezier le curve iniziali PtrOwner pCrvEdge1( ConvertCurveToComposite(CurveToBezierCurve( pCurve0, 3, false))) ; PtrOwner pCrvEdge2( ConvertCurveToComposite(CurveToBezierCurve( pCurve1, 3, false))) ; if ( IsNull( pCrvEdge1) || IsNull( pCrvEdge2)) return false ; #if SAVEPACEDISO vGeo.clear() ; vCol.clear() ; #endif // Verifico che la distanza di campionamento sia ammissibile double dMyDist = Clamp( dSampleLen, 2., 30.) ; // 20.0 sembra un passo di campionamento ideale // Recupero parametri iniziali double dLen1 ; pCrvEdge1->GetLength( dLen1) ; double dLen2 ; pCrvEdge2->GetLength( dLen2) ; double dUS1, dUE1 ; pCrvEdge1->GetDomain( dUS1, dUE1) ; double dUS2, dUE2 ; pCrvEdge2->GetDomain( dUS2, dUE2) ; double dLenPrev1 = 0., dLenCurr1 = 0. ; double dLenPrev2 = 0., dLenCurr2 = 0. ; double dUPrev1 = 0., dUCurr1 = 0. ; double dUPrev2 = 0., dUCurr2 = 0. ; Point3d ptPrev1, ptCurr1 ; pCrvEdge1->GetStartPoint( ptPrev1) ; Point3d ptPrev2, ptCurr2 ; pCrvEdge2->GetStartPoint( ptPrev2) ; Vector3d vtCurr1 = V_NULL, vtCurr2 = V_NULL ; while ( dLenPrev1 + dMyDist < dLen1 - EPS_ZERO) { // Recupero dU, Point3d e dLen corrente sul primo bordo, per un incremento del passo di campionamento dLenCurr1 = Clamp( dLenPrev1 + dMyDist, 0., dLen1) ; pCrvEdge1->GetParamAtLength( dLenCurr1, dUCurr1) ; // se sono abbastanza vicino ad una joint allora prendo quel punto double dUClosestJoint1 = round( dUCurr1) ; double dLenAlt1 = 0 ; pCrvEdge1->GetLengthAtParam( dUClosestJoint1, dLenAlt1) ; if ( abs( dLenCurr1 - dLenAlt1) < 1) dUCurr1 = dUClosestJoint1 ; pCrvEdge1->GetPointD1D2( dUCurr1, ICurve::FROM_MINUS, ptCurr1, &vtCurr1) ; vtCurr1.Normalize() ; #if DEBUG_SYNCLINES PtrOwner ptGeo1( CreateGeoPoint3d()) ; ptGeo1->Set( ptCurr1) ; PtrOwner vtGeo1( CreateGeoVector3d()) ; vtGeo1->Set( 5. * vtCurr1, ptCurr1) ; pGeomDB->AddGeoObj( GDB_ID_NULL, nLay1, Release( ptGeo1)) ; pGeomDB->AddGeoObj( GDB_ID_NULL, nLay1, Release( vtGeo1)) ; #endif GetIsoPointOnSecondCurve( pCrvEdge1, pCrvEdge2, dUCurr1, dUCurr2, dMyDist, dUPrev2, dLenPrev2, dLenCurr2, dLen2) ; //////////////////////////N.B.: se si aggiunge questo pezzo bisogna anche gestire il fatto che i punti precedentemente aggiunti potrebbero non stare più sulla curva modificata!!! //// verifico se sono vicino ad una joint esistente allora modifico la curva 2 //double dUClosestJoint2 = round( dUCurr2) ; //double dLenAlt2 = 0 ; pCrvEdge2->GetLengthAtParam( dUClosestJoint2, dLenAlt2) ; //if ( abs( dLenCurr2 - dLenAlt2) < 1) { // Point3d ptNewJoint ; pCrvEdge2->GetPointD1D2( dUCurr2, ICurve::FROM_MINUS, ptNewJoint) ; // if ( pCrvEdge2->ModifyJoint( int( dUClosestJoint2), ptNewJoint)) // dUCurr2 = dUClosestJoint2 ; //} pCrvEdge2->GetPointD1D2( dUCurr2, ICurve::FROM_MINUS, ptCurr2, &vtCurr2) ; #if DEBUG_SYNCLINES PtrOwner ptGeo2( CreateGeoPoint3d()) ; ptGeo2->Set( ptCurr2) ; PtrOwner vtGeo2( CreateGeoVector3d()) ; vtGeo2->Set( 5. * vtCurr2, ptCurr2) ; pGeomDB->AddGeoObj( GDB_ID_NULL, nLay2, Release( ptGeo2)) ; pGeomDB->AddGeoObj( GDB_ID_NULL, nLay2, Release( vtGeo2)) ; #endif vSyncLines.emplace_back( ptCurr1, ptCurr2) ; // --- Analisi degli spigoli all'interno della Quadrangolazione corrente --- // NB. Non è sempre detto che uno spigolo di una curva sia sincronizzato con lo spigolo di un'altra ( se questi esistono)... // Pertanto la Bezier Ruled ricavata non è detto che sia in grado di approssimare lo spigolo correttamente, potrebbe sdondarlo PtrOwner pCrvQuad1( ConvertCurveToComposite( pCrvEdge1->CopyParamRange( dUPrev1, dUCurr1))) ; PtrOwner pCrvQuad2( ConvertCurveToComposite( pCrvEdge2->CopyParamRange( dUPrev2, dUCurr2))) ; BIPNTVECTOR vEdgeSyncLines ; ManageEdgesInQuadrangulation( pCrvQuad1, pCrvQuad2, vEdgeSyncLines) ; // --- Aggiunta di Linee di Sync all'interno della Quandrangolazione corrente --- // Perchè parametrizzando per la lunghezza i SottoTratti ricavati, nel caso di elevate variazioni angolari tra l'inizio e la fine // ( tra due curva di Sync) si potrebbero generare delle torsioni non volute // -->! NB. Queste linee vengono create considerando le SubQuadrangolazioni con gli Spigoli. !<-- BIPNTVECTOR vTwistSyncLines ; bool bEraseLastIso = false ; if ( vEdgeSyncLines.empty()) ManageTwistInQuadrangulation( pCrvQuad1, pCrvQuad2, vTwistSyncLines, bEraseLastIso) ; else { BIPOINT SyncLinePrev, SyncLineNext ; pCrvQuad1->GetStartPoint( SyncLinePrev.first) ; pCrvQuad2->GetStartPoint( SyncLinePrev.second) ; for ( int i = 0 ; i < ssize( vEdgeSyncLines) ; ++ i) { if ( i == ssize( vEdgeSyncLines) - 1) { pCrvQuad1->GetEndPoint( SyncLineNext.first) ; pCrvQuad2->GetEndPoint( SyncLineNext.second) ; } else { SyncLineNext.first = vEdgeSyncLines[i].first ; SyncLineNext.second = vEdgeSyncLines[i].second ; } // Recupero i parametri correnti double dUS1, dUE1, dUS2, dUE2 ; pCrvQuad1->GetParamAtPoint( SyncLinePrev.first, dUS1) ; pCrvQuad2->GetParamAtPoint( SyncLinePrev.second, dUS2) ; pCrvQuad1->GetParamAtPoint( SyncLineNext.first, dUE1) ; pCrvQuad2->GetParamAtPoint( SyncLineNext.second, dUE2) ; PtrOwner pCrvSubQuad1( ConvertCurveToComposite( pCrvQuad1->CopyParamRange( dUS1, dUE1))) ; PtrOwner pCrvSubQuad2( ConvertCurveToComposite( pCrvQuad2->CopyParamRange( dUE1, dUE2))) ; ManageTwistInQuadrangulation( pCrvSubQuad1, pCrvSubQuad2, vTwistSyncLines, bEraseLastIso) ; // Aggiorno i parametri SyncLinePrev = SyncLineNext ; } bEraseLastIso = false ; } if ( bEraseLastIso) { vSyncLines.pop_back() ; double dQuadLen1 ; pCrvQuad1->GetLengthAtPoint( vTwistSyncLines[0].first, dQuadLen1) ; dLenCurr1 = dLenPrev1 + dQuadLen1 ; double dQuadLen2 ; pCrvQuad2->GetLengthAtPoint( vTwistSyncLines[0].second, dQuadLen2) ; dLenCurr2 = dLenPrev2 + dQuadLen2 ; } // aggiungo le nuove curve ( non importa che siano ordinate per parametro) vSyncLines.insert( vSyncLines.end(), vEdgeSyncLines.begin(), vEdgeSyncLines.end()) ; vSyncLines.insert( vSyncLines.end(), vTwistSyncLines.begin(), vTwistSyncLines.end()) ; // Aggiorno i parametri ptPrev1 = ptCurr1 ; dUPrev1 = dUCurr1 ; dLenPrev1 = dLenCurr1 ; ptPrev2 = ptCurr2 ; dUPrev2 = dUCurr2 ; dLenPrev2 = dLenCurr2 ; } #if SAVEPACEDISO SaveGeoObj( vGeo, vCol, "D:\\Temp\\bezier\\ruled\\trimming\\smooth.nge") ; #endif return CreateByIsoParamSet( pCrvEdge1, pCrvEdge2, vSyncLines) ; } //---------------------------------------------------------------------------- bool SurfBezier::FindMatchByParam( const PolyLine& pl0, const PolyLine& pl1, INTVECTOR& vMatch, int& nLong) const { // per la modalità isoparametrica int nPoints0 = pl0.GetPointNbr() ; int nPoints1 = pl1.GetPointNbr() ; DBLVECTOR vSplit ; int nMin = min( nPoints0, nPoints1) ; for ( int i = 0 ; i < nMin - 1 ; ++i) { // valuto il limite dell'area di influenza dei punti della curva con meno punti // per i punti intermedi il limite è la metà tra i punti // per il primo e l'ultimo punto aumento il peso di quest'ultimi nel calcolo della media double dW = 2./3. ; //double dW = 1./2. ; if ( i == 0) vSplit.push_back( (i * (1- dW) + ( i + 1) * dW) / ( nMin - 1)) ; else if ( i == nMin - 2) vSplit.push_back( (i * dW + ( i + 1) * ( 1 - dW)) / ( nMin - 1)) ; else vSplit.push_back( ( 2. * i + 1) / (2 * ( nMin - 1))) ; } int nCount = 0 ; int nMax = max(nPoints0, nPoints1) ; nLong = nPoints0 < nPoints1 ? 1 : 0 ; for ( double j = 0 ; j < nMax ; ++j) { if ( nCount < int(vSplit.size()) && j / ( nMax - 1) > vSplit[nCount] ) ++nCount ; vMatch.push_back( nCount) ; } return true ; } //---------------------------------------------------------------------------- static bool ParametrizeByLen( const ICurveComposite* pCurve, DBLVECTOR& vParam) { int nSpanU = pCurve->GetCurveCount() ; DBLVECTOR vLen ; double dLenTot = 0 ; vParam.push_back( 0) ; for ( int i = 0 ; i < nSpanU ; ++i) { const ICurve* pSubCrv = pCurve->GetCurve( i) ; double dLen ; pSubCrv->GetLength( dLen) ; dLenTot += dLen ; vLen.push_back( dLenTot) ; } // determino il parametro di ogni curva rispetto alla lunghezza totale for ( int i = 0 ; i < nSpanU ; ++i) vParam.push_back( vLen[i] / dLenTot) ; return true ; } //---------------------------------------------------------------------------- static bool BuildCommonParam( const DBLMATRIX& mParam, DBLVECTOR& vCommonParam) { vCommonParam = mParam[0] ; for ( int i = 1 ; i < int( mParam.size()) ; ++i) { for ( int j = 0 ; j < int( mParam[i].size()) ; ++j) vCommonParam.push_back( mParam[i][j]) ; } //riordino ed elimino i doppioni entro una certa tolleranza sort( vCommonParam.begin(), vCommonParam.end()) ; for ( int i = 0 ; i < int(vCommonParam.size()) - 1 ; ++i) { for ( int j = i + 1 ; j < int(vCommonParam.size()) ; ++j) { if ( vCommonParam[j] - vCommonParam[i] < EPS_SMALL){ vCommonParam.erase(vCommonParam.begin() + j) ; --j ; } else break ; } } return true ; } //---------------------------------------------------------------------------- static bool SplitByCommonParam( ICURVEPOVECTOR& vCrvBezUnif, DBLVECTOR& vCommonParam, DBLMATRIX& mParam) { for ( int i = 0 ; i < int( vCrvBezUnif.size()) ; ++i) { ICurveComposite* pCC = GetCurveComposite(vCrvBezUnif[i]) ; int c = mParam[i].size() - 1 ; DBLVECTOR vParam = mParam[i] ; for ( int j = vCommonParam.size() - 1 ; j >= 0 ; --j) { // capisco su quale sottocurva devo fare lo split e riconverto il parametro rispetto al numero di sottocurve while ( vCommonParam[j] < vParam[c]) --c ; if ( vCommonParam[j] - vParam[c] > EPS_SMALL) { double dSplit = (vCommonParam[j] - vParam[c]) / (vParam[c+1] - vParam[c]) + c ; pCC->AddJoint( dSplit) ; vParam.insert( vParam.begin() + c + 1, vCommonParam[j]) ; ++c ; } } } return true ; } //---------------------------------------------------------------------------- bool SurfBezier::CreateBySetOfCurves( const ICURVEPOVECTOR& vCrvBez, bool bReduceToDeg3) { // uniformo le curve e determino il grado e il numero di span condiviso bool bRat = false ; int nDegU = 3 ; ICURVEPOVECTOR vCrvBezUnif ; DBLMATRIX mParam ; if ( ! bReduceToDeg3) { int nDegMax = 1 ; //trovo il grado massimo for ( int i = 0 ; i < int( vCrvBez.size()) ; ++i) { const ICurveComposite* pCC = GetCurveComposite( vCrvBez[i]) ; for ( int j = 0 ; j < int( pCC->GetCurveCount()) ; ++j) { const ICurveBezier* pCrvBez = GetCurveBezier( pCC->GetCurve( j)) ; int nDeg = pCrvBez->GetDegree() ; nDegMax = max( nDegMax, nDeg) ; } } // porto tutte le curve al grado massimo for ( int i = 0 ; i < int(vCrvBez.size()) ; ++i ) vCrvBezUnif.emplace_back( EditBezierCurve( GetCurveBezier(vCrvBez[i]), nDegMax, false)) ; } else { // scorro le curve e uniformo tutte in curve di grado 3 non razionale for ( int i = 0 ; i < int( vCrvBez.size()) ; ++i) { const ICurveComposite* pCCFromSet = GetCurveComposite( vCrvBez[i]) ; if ( pCCFromSet == nullptr) return false ; PtrOwner pCC( CreateCurveComposite()) ; for ( int j = 0 ; j < int( pCCFromSet->GetCurveCount()) ; ++j) { const ICurveBezier* pCrvBez = GetCurveBezier( pCCFromSet->GetCurve( j)) ; if ( pCrvBez == nullptr) return false ; if ( ! pCC->AddCurve( EditBezierCurve( pCrvBez, nDegU, bRat))) return false ; } mParam.emplace_back() ; ParametrizeByLen( pCC, mParam.back()) ; vCrvBezUnif.emplace_back( Release( pCC)) ; } } // ora vado a splittare le curve in modo da avere una divisione condivisa DBLVECTOR vdCommonPar ; BuildCommonParam( mParam, vdCommonPar) ; SplitByCommonParam( vCrvBezUnif, vdCommonPar, mParam) ; int nSpanU = GetCurveComposite( vCrvBezUnif[0])->GetCurveCount() ; // calcolo i punti di controllo in V int nDegV = 3 ; int nSpanV = int( vCrvBezUnif.size()) - 1 ; PNTMATRIX vPntCrvs ; //versione vecchia for ( int j = 0 ; j < nSpanU ; ++j ) { PNTVECTOR vPntCtrl0 ; PNTVECTOR vPntCtrl1 ; for ( int i = 0 ; i < int( vCrvBezUnif.size()) ; ++i) { const ICurveComposite* pCC = GetCurveComposite( vCrvBezUnif[i]) ; const ICurveBezier* pCrvBez = GetCurveBezier( pCC->GetCurve( j)) ; if ( j == 0) vPntCtrl0.push_back( pCrvBez->GetControlPoint( 0)) ; vPntCtrl1.push_back( pCrvBez->GetControlPoint( nDegU)) ; } if ( j==0 ) vPntCrvs.push_back( vPntCtrl0) ; vPntCrvs.push_back( vPntCtrl1) ; } ////versione nuova //for ( int j = 0 ; j < nSpanU ; ++j ) { // for ( int z = j== 0 ? 0 : 1 ; z < nDegU + 1 ; ++z) { // PNTVECTOR vPntCtrl ; // for ( int i = 0 ; i < int( vCrvBezUnif.size()) ; ++i) { // const ICurveComposite* pCC = GetCurveComposite( vCrvBezUnif[i]) ; // const ICurveBezier* pCrvBez = GetCurveBezier( pCC->GetCurve( j)) ; // vPntCtrl.push_back( pCrvBez->GetControlPoint( z)) ; // } // vPntCrvs.push_back( vPntCtrl) ; // } //} Init( nDegU, nDegV, nSpanU, nSpanV, false) ; // scorro le span for ( int s = 0 ; s < int( nSpanU) ; ++s) { //// trovo la direzione media del parametro V //Point3d ptMean = ORIG ; //// verione vecchia //for ( int i = 1 ; i < int( vPntCrvs[0].size()) ; ++i ) // ptMean += vPntCrvs[s][i] ; //ptMean /= int( vPntCrvs[0].size() - 1) ; //Vector3d vtDirXGeneral = ptMean - vPntCrvs[s][0] ; // prendo le curve a gruppi di 3 per costruire la parabola per trovare la pendenza "intuitiva" della superficie in V for ( int g = 0 ; g < nSpanV - 1 ; ++g) { //// versione nuova //Vector3d vtDirXGeneral ; //// devo scorrere la matrice a blocchi di nDegU //for ( int z = s * nDegU ; z < ( s + 1) * nDegU + 1 ; ++z) { // ptMean = ORIG ; // for ( int i = g ; i < g + 3 ; ++i) // ptMean += vPntCrvs[z][i] ; // ptMean /= int( vPntCrvs[0].size() - 1) ; // vtDirXGeneral += ptMean - vPntCrvs[z][g] ; //} const ICurveComposite* pCC0 = GetCurveComposite( vCrvBezUnif[g]) ; const ICurveComposite* pCC1 = GetCurveComposite( vCrvBezUnif[g + 1]) ; const ICurveComposite* pCC2 = GetCurveComposite( vCrvBezUnif[g + 2]) ; // su ognuna di queste 3 curve prendo un punto per ogni punto di controllo ( semplicemente dividendo uniformemente il parametrico) for ( int n = s == 0 ? 0 : 1 ; n < nDegU + 1 ; ++n) { const ICurveBezier* pCrv0 = GetCurveBezier( pCC0->GetCurve( s)) ; const ICurveBezier* pCrv1 = GetCurveBezier( pCC1->GetCurve( s)) ; const ICurveBezier* pCrv2 = GetCurveBezier( pCC2->GetCurve( s)) ; // setto come punti di controllo i punti delle curve Point3d ptCtrl0 = pCrv0->GetControlPoint( n) ; Point3d ptCtrl1 = pCrv1->GetControlPoint( n) ; Point3d ptCtrl2 = pCrv2->GetControlPoint( n) ; if ( g == 0) { SetControlPoint( n + (s * nDegU) + (nDegU * nSpanU + 1) * g * 3, ptCtrl0) ; SetControlPoint( n + (s * nDegU) + (nDegU * nSpanU + 1) * (g * 3 + 3), ptCtrl1) ; } SetControlPoint( n + (s * nDegU) + (nDegU * nSpanU + 1) * (g * 3 + 6), ptCtrl2) ; //// trovo i punti di controllo intermedi tra le curve usando la parabola che unisce queste tre curve//// Point3d ptP0 = pCrv0->GetControlPoint( n) ; Point3d ptP1 = pCrv1->GetControlPoint( n) ; Point3d ptP2 = pCrv2->GetControlPoint( n) ; DistPointLine dpl( ptP1, ptP0, ptP2, false) ; double dDist = INFINITO ; dpl.GetDist( dDist) ; Point3d ptP3, ptP4, ptP5, ptP6 ; if ( dDist < EPS_SMALL) { // i punti sono allineati quindi mi basta prendere dei punti intermedi su questa retta ptP3 = ptP0 * 2/3 + ptP1 * 1/3 ; ptP4 = ptP0 * 1/3 + ptP1 * 2/3 ; ptP5 = ptP1 * 2/3 + ptP2 * 1/3 ; ptP6 = ptP1 * 1/3 + ptP2 * 2/3 ; } else { // calcolo il piano della parabola e porto i punti in quel riferimento Plane3d plParab ; plParab.Set( ptP0, ptP1, ptP2) ; Point3d ptStartV, ptEndV ; DistPointLine dpl( ptP1, ptP0, ptP2, true) ; Point3d ptPerp ; dpl.GetMinDistPoint( ptPerp) ; Vector3d vtDirY = ptPerp - ptP1 ; Frame3d frParab ; Point3d ptStart, ptEnd ; pCrv1->GetStartPoint( ptStart) ; pCrv1->GetEndPoint( ptEnd) ; Vector3d vtDirCrv1 = ptEnd - ptStart ; Vector3d vtParabNorm = plParab.GetVersN() ; Vector3d vtDirZ = vtDirCrv1 * vtParabNorm > 0 ? vtParabNorm : - vtParabNorm ; //Vector3d vtDirX = vtDirXGeneral - (vtDirXGeneral * vtDirZ * vtDirZ) ; // versione vecchia Vector3d vtDirX = ptP2 - ptP0 ; // versione vecchia vecchia if ( ! frParab.Set( ptP0, vtDirX, vtDirY, vtDirZ)) { vtDirY = vtDirZ ^ vtDirX ; if ( ! frParab.Set( ptP0, vtDirX, vtDirY, vtDirZ)) return false ; } // porto i punti nel piano di riferimento della parabola Point3d pt0 = ptP0 ; pt0.ToLoc( frParab) ; Point3d pt1 = ptP1 ; pt1.ToLoc( frParab) ; Point3d pt2 = ptP2 ; pt2.ToLoc( frParab) ; Eigen::Matrix3d mA ; mA.col(0) << pt0.x * pt0.x, pt1.x * pt1.x, pt2.x * pt2.x ; mA.col(1) << pt0.x, pt1.x , pt2.x ; mA.col(2) << 1, 1, 1 ; if ( abs( mA.determinant()) < EPS_SMALL) return false ; Eigen::Vector3d b ( pt0.y, pt1.y, pt2.y) ; Eigen::Vector3d coeff = mA.fullPivLu().solve(b) ; // pendenze e termini noti nei punti di contatto double dm0 = 2 * coeff.x() * pt0.x + coeff.y() ; double dm1 = 2 * coeff.x() * pt1.x + coeff.y() ; double dm2 = 2 * coeff.x() * pt2.x + coeff.y() ; double dq0 = pt0.y - dm0 * pt0.x ; double dq1 = pt1.y - dm1 * pt1.x ; double dq2 = pt2.y - dm2 * pt2.x ; // trovo le intersezioni a coppie tra le tre rette di tangenza Point3d ptI1 , ptI2; ptI1.x = (dq1 -dq0) / (dm0 - dm1) ; ptI1.y = ptI1.x * dm0 + dq0 ; ptI2.x = ( dq2 - dq1) / ( dm1 - dm2) ; ptI2.y = ptI2.x * dm1 + dq1 ; // porto i punti in globale e trovo i punti medi ptI1.ToGlob( frParab) ; ptI2.ToGlob( frParab) ; ptP3 = ( ptP0 + ptI1) / 2 ; ptP4 = ( ptP1 + ptI1) / 2 ; ptP5 = ( ptP1 + ptI2) / 2 ; ptP6 = ( ptP2 + ptI2) / 2 ; } // setto i punti di controllo di collegamento tra le curve passate in input, per il terzetto di curve selezionate if ( g == 0) SetControlPoint( n + (s * nDegU) + (nDegU * nSpanU + 1) * (g * 3 + 1), ptP3) ; SetControlPoint( n + (s * nDegU) + (nDegU * nSpanU + 1) * (g * 3 + 2), ptP4) ; SetControlPoint( n + (s * nDegU) + (nDegU * nSpanU + 1) * (g * 3 + 4), ptP5) ; SetControlPoint( n + (s * nDegU) + (nDegU * nSpanU + 1) * (g * 3 + 5), ptP6) ; } } } return true ; } //---------------------------------------------------------------------------- PNTVECTOR SurfBezier::GetAllControlPoints( void) const { PNTVECTOR vPntCtrl = m_vPtCtrl ; return vPntCtrl ; } //---------------------------------------------------------------------------- bool SurfBezier::GetAllPatchesIsocurves( bool bUOrV, ICURVEPOVECTOR& vCrv) const { // restituisce tutte le isocurve di separazione tra patch in un parametro o nell'altro if ( bUOrV) { for ( int v = 0 ; v <= m_nSpanV ; ++v) vCrv.emplace_back( GetCurveOnU( v)) ; } else { for ( int u = 0 ; u <= m_nSpanU ; ++u) vCrv.emplace_back( GetCurveOnV( u)) ; } return true ; } struct IsoParam { int nCrv ; double dParam0 ; double dParam1 ; IsoParam( int _nCrv, double _dParam0, double _dParam1) : nCrv(_nCrv), dParam0( _dParam0), dParam1( _dParam1){ } ; bool operator < ( IsoParam& b) { return ( abs(dParam0 - b.dParam0) > EPS_SMALL ? dParam0 < b.dParam0 : dParam1 < b.dParam1) ; } }; typedef vector ISOPARVECT ; //---------------------------------------------------------------------------- bool SurfBezier::CreateByIsoParamSet( const ICurve* pCurve0, const ICurve* pCurve1, const BIPNTVECTOR& vCrv) { // vCrv è il vettore delle isocurve (nel parametro V) che si vogliono forzare per la creazione della rigata tra Curve0 e Curve1 // controllo che siano entrambe chiuse o entrambe aperte if ( pCurve0->IsClosed() != pCurve1->IsClosed()) return false ; bool bClosed = pCurve0->IsClosed() ; // converto in bezier la curva iniziale CurveComposite CrvU0 ; CrvU0.AddCurve( CurveToBezierCurve(pCurve0)) ; if ( ! CrvU0.IsValid()) return false ; CurveComposite CrvU1 ; CrvU1.AddCurve( CurveToBezierCurve(pCurve1)) ; if ( ! CrvU1.IsValid()) return false ; int nDegU = 3 ; int nDegV = 1 ; int nSpanV = 1 ; int nLastPoint = nDegU + 1 ; bool bRat = false ; PNTVECTOR vPnt0, vPnt1 ; Point3d pt ; CrvU0.GetStartPoint( pt) ; vPnt0.push_back( pt) ; for ( int i = 0 ; i < CrvU0.GetCurveCount() ; ++i) { CrvU0.GetCurve(i)->GetEndPoint( pt) ; vPnt0.push_back( pt) ; } CrvU1.GetStartPoint( pt) ; vPnt1.push_back( pt) ; for ( int i = 0 ; i < CrvU1.GetCurveCount() ; ++i) { CrvU1.GetCurve(i)->GetEndPoint( pt) ; vPnt1.push_back( pt) ; } // associo le isocurve passate in input ai relativi punti sulle due curve che generano la rigata INTINTVECTOR vPairs ; DBLVECTOR vdSplit0, vdSplit1 ; int nSplit0 = 0, nSplit1 = 0 ; Point3d ptLast0, ptLast1 ; double dLastParam0 = 0, dLastParam1 = 0 ; double dLenPrev0 = 0 ,dLenPrev1 = 0 ; // costruisco il vettore delle nuove curve isoparamtriche per la nuova superficie bool bFirstAdded = false ; int nSpanU0 = CrvU0.GetCurveCount() ; int nSpanU1 = CrvU1.GetCurveCount() ; ISOPARVECT vIso ; for ( int i = 0 ; i < ssize( vCrv) ; ++i) { Point3d ptU0 = vCrv[i].first ; Point3d ptU1 = vCrv[i].second ; double dParam0 ; CrvU0.GetParamAtPoint( ptU0, dParam0) ; double dParam1 ; CrvU1.GetParamAtPoint( ptU1, dParam1) ; if ( bClosed) { // curve di sync che sono esattamente sugli start/end di churve chiuse if ( ( dParam0 < EPS_SMALL || nSpanU0 - dParam0 < EPS_SMALL) && ( dParam1 < EPS_SMALL || nSpanU1 - dParam1 < EPS_SMALL)) { if ( ! bFirstAdded) { dParam0 = 0 ; dParam1 = 0 ; bFirstAdded = true ; } else { dParam0 = nSpanU0 ; dParam1 = nSpanU1 ; } } // curve si sync che hanno solo uno dei due punti su un punto di start/end, ma NON sono a cavallo dello start else if ( ( dParam0 < EPS_SMALL || nSpanU0 - dParam0 < EPS_SMALL)) { if ( ( nSpanU1 - dParam1) > dParam1) dParam0 = 0 ; else dParam0 = nSpanU0 ; } else if (( dParam1 < EPS_SMALL || nSpanU1 - dParam1 < EPS_SMALL)) { if ( ( nSpanU0 - dParam0) > dParam0) dParam1 = 0 ; else dParam1 = nSpanU1 ; } //else if ( abs(( nSpanU0 - dParam0) / nSpanU0 - ( nSpanU1 - dParam1) / nSpanU1) > 0.1) { // // gestisco il cambio di start se ho una sync a cavallo di start e end // // quella sync diventa il nuovo start //} } vIso.emplace_back( i, dParam0, dParam1) ; } sort( vIso.begin(), vIso.end()) ; // sposto lo start sulla prima curva di sync che trovo if ( bClosed) { double dNewStart0 = vIso[0].dParam0 ; double dNewStart1 = vIso[0].dParam1 ; CrvU0.ChangeStartPoint( dNewStart0) ; CrvU1.ChangeStartPoint( dNewStart1) ; vIso[0].dParam0 = 0 ; vIso[0].dParam1 = 0 ; double dCeil0 = ceil( dNewStart0) ; double dFloor0 = floor( dNewStart0) ; double dCeil1 = ceil( dNewStart1) ; double dFloor1 = floor( dNewStart1) ; vPnt0.pop_back() ; rotate( vPnt0.begin(), vPnt0.begin() + int( dCeil0), vPnt0.end()) ; vPnt1.pop_back() ; rotate( vPnt1.begin(), vPnt1.begin() + int( dCeil1), vPnt1.end()) ; if ( abs( dNewStart0 - round( dNewStart0)) > EPS_SMALL) { Point3d ptNewStart ; CrvU0.GetStartPoint( ptNewStart) ; vPnt0.insert( vPnt0.begin(), ptNewStart) ; ++nSpanU0 ; } if ( abs( dNewStart1 - round( dNewStart1)) > EPS_SMALL) { Point3d ptNewStart ; CrvU1.GetStartPoint( ptNewStart) ; vPnt1.insert( vPnt1.begin(), ptNewStart) ; ++nSpanU1 ; } for ( int i = 1 ; i < ssize( vIso) ; ++i) { if ( vIso[i].dParam0 >= dCeil0) vIso[i].dParam0 -= dFloor0 ; else if ( vIso[i].dParam0 <= dFloor0) vIso[i].dParam0 += nSpanU0 - dCeil0 ; else CrvU0.GetParamAtPoint( vCrv[vIso[i].nCrv].first, vIso[i].dParam0) ; if ( vIso[i].dParam1 >= dCeil1) vIso[i].dParam1 -= dFloor1 ; else if ( vIso[i].dParam1 <= dFloor1) vIso[i].dParam1 += nSpanU1 - dCeil1 ; else CrvU1.GetParamAtPoint( vCrv[vIso[i].nCrv].second, vIso[i].dParam1) ; } } else { vIso.emplace_back( -1, 0, 0) ; rotate( vIso.begin(), vIso.begin() + ssize(vIso) - 1, vIso.end()) ; } // se manca aggiungo la coppia finale if ( vIso.back().dParam0 != nSpanU0 || vIso.back().dParam1 != nSpanU1) vIso.emplace_back( -1, nSpanU0, nSpanU1) ; // scorro vIso per verificare che non ci siano curve che si intersecano int c = 0 ; dLastParam0 = vIso[c].dParam0 ; dLastParam1 = vIso[c].dParam1 ; ++c ; while ( c < ssize( vIso)) { if ( vIso[c].dParam0 < dLastParam0) { if ( dLastParam0 - vIso[c].dParam0 < EPS_ZERO) vIso[c].dParam0 = dLastParam0 + EPS_PARAM ; else return false ; } if ( vIso[c].dParam1 < dLastParam1) { if ( dLastParam1 - vIso[c].dParam1 < EPS_ZERO) vIso[c].dParam1 = dLastParam1 + EPS_PARAM ; else return false ; } dLastParam0 = vIso[c].dParam0 ; dLastParam1 = vIso[c].dParam1 ; ++c ; } dLastParam0 = 0 ; dLastParam1 = 0 ; if ( vIso[0].dParam0 > 0 || vIso[0].dParam1 > 0) vPairs.emplace_back( 0, 0) ; for ( int i = 0 ; i < ssize( vIso) ; ++i) { Point3d ptU0, ptU1 ; if ( vIso[i].nCrv != -1) { const BIPOINT& pCrv = vCrv[vIso[i].nCrv] ; ptU0 = pCrv.first ; ptU1 = pCrv.second ; } else { if ( vIso[i].dParam0 == 0 && vIso[i].dParam1 == 0) { CrvU0.GetStartPoint( ptU0) ; CrvU1.GetStartPoint( ptU1) ; } else { CrvU0.GetEndPoint( ptU0) ; CrvU1.GetEndPoint( ptU1) ; } } double& dParam0 = vIso[i].dParam0 ; double& dParam1 = vIso[i].dParam1 ; // se sono vicino ad un'intero allora considero il parametro intero ( uno split già esistente) bool bIntParam0 = false ; bool bIntParam1 = false ; if ( abs( dParam0 - round( dParam0)) < EPS_SMALL) { dParam0 = round( dParam0) ; bIntParam0 = true ; } if ( abs( dParam1 - round( dParam1)) < EPS_SMALL) { dParam1 = round( dParam1) ; bIntParam1 = true ; } int nParam0 = int( dParam0) ; int nParam1 = int( dParam1) ; int nLastParam0 = int( dLastParam0) ; int nLastParam1 = int( dLastParam1) ; double dLenCurr0 ; CrvU0.GetLengthAtParam( dParam0, dLenCurr0) ; double dLenCurr1 ; CrvU1.GetLengthAtParam( dParam1, dLenCurr1) ; int nPointsBetween0 = 0 ; if ( dParam0 - nLastParam0 > 1) { if ( ! bIntParam0) nPointsBetween0 = int( dParam0 - nLastParam0) ; else nPointsBetween0 = int( dParam0 - nLastParam0) - 1 ; } else nPointsBetween0 = 0 ; int nPointsBetween1 = 0 ; if ( dParam1 - nLastParam1 > 1) { if ( ! bIntParam1) nPointsBetween1 = int( dParam1 - nLastParam1) ; else nPointsBetween1 = int( dParam1 - nLastParam1) - 1 ; } else nPointsBetween1 = 0 ; if ( nPointsBetween0 >= 0 || nPointsBetween1 >= 0) { // calcolo la parametrizzazione locale dei punti compresi tra le due isoparametriche double dLen0 = dLenCurr0 - dLenPrev0 ; double dLen1 = dLenCurr1 - dLenPrev1 ; DBLVECTOR vdParamPos0 ; vdParamPos0.reserve( nPointsBetween0) ; DBLVECTOR vdParamPos1 ; vdParamPos1.reserve( nPointsBetween1) ; for ( int k = 0 ; k < nPointsBetween0 ; ++k) { double dParamIntermed = 0 ; CrvU0.GetParamAtPoint( vPnt0[ nLastParam0 + 1 + k], dParamIntermed) ; double dLen = 0 ; CrvU0.GetLengthAtParam( dParamIntermed, dLen) ; dLen -= dLenPrev0 ; vdParamPos0.push_back( dLen / dLen0) ; } vdParamPos0.push_back( 1) ; for ( int k = 0 ; k < nPointsBetween1 ; ++k) { double dParamIntermed = 0 ; CrvU1.GetParamAtPoint( vPnt1[ nLastParam1 + 1 + k], dParamIntermed) ; double dLen = 0 ; CrvU1.GetLengthAtParam( dParamIntermed, dLen) ; dLen -= dLenPrev1 ; vdParamPos1.push_back( dLen / dLen1) ; } vdParamPos1.push_back( 1) ; bool bSplitToAdd = true ; int c0 = 0, c1 = 0 ; while ( bSplitToAdd) { if ( c0 > ssize( vdParamPos0) - 1 || c1 > ssize( vdParamPos1) - 1) { LOG_DBG_ERR( GetEGkLogger(), "Surf Bez Ruled Guided: error while reparametrizing some section") ; return false ; } // se ho una corrispondenza tra punti ( e non sono alla fine del tratto) allora non aggiungo split if ( abs( vdParamPos0[c0] - vdParamPos1[c1]) < EPS_SMALL && (vdParamPos0[c0] < 1 || vdParamPos1[c1] < 1)) { if ( (vdParamPos0[c0] < 1 - EPS_SMALL || vdParamPos1[c1] < 1 - EPS_SMALL)) { ++c0 ; ++c1 ; ++nLastParam0 ; ++nLastParam1 ; vPairs.emplace_back( nLastParam0 + nSplit0, nLastParam1 + nSplit1) ; } else { if ( vdParamPos0[c0] > 1 - EPS_SMALL && c0 < ssize( vdParamPos0) - 1) { bIntParam0 = true ; dParam0 = round( dParam0) ; } if ( vdParamPos1[c1] > 1 - EPS_SMALL && c1 < ssize( vdParamPos1) - 1) { bIntParam1 = true ; dParam1 = round( dParam1) ; } c0 = ssize( vdParamPos0) - 1 ; c1 = ssize( vdParamPos1) - 1 ; } } // se non ho corrispondenza allora aggiungo uno split sulla curva a cui manca il punto corrispondente else if ( vdParamPos0[c0] < vdParamPos1[c1]) { double dPar ; CrvU1.GetParamAtLength( dLenPrev1 + dLen1 * vdParamPos0[c0], dPar) ; if ( dPar > dLastParam1 + EPS_PARAM) { if ( abs( dPar - round( dPar)) > EPS_SMALL) { vdSplit1.push_back( dPar) ; nSplit1 = vdSplit1.size() ; } else if ( dPar = round( dPar) ; dPar > nLastParam1){ ++ c1 ; ++ nLastParam1 ; } } dLastParam1 = dPar ; ++nLastParam0 ; vPairs.emplace_back( nLastParam0 + nSplit0, nLastParam1 + nSplit1) ; ++c0 ; } else if ( vdParamPos0[c0] > vdParamPos1[c1]) { double dPar ; CrvU0.GetParamAtLength( dLenPrev0 + dLen0 * vdParamPos1[c1], dPar) ; // se lo split non è in prossimità di una joint già esistente allora lo aggiungo if ( dPar > dLastParam0 + EPS_PARAM) { if ( abs( dPar - round( dPar)) > EPS_SMALL) { vdSplit0.push_back( dPar) ; nSplit0 = vdSplit0.size() ; } else if ( dPar = round( dPar) ; dPar > nLastParam0){ ++ c0 ; ++ nLastParam0 ; } } dLastParam0 = dPar ; ++nLastParam1 ; vPairs.emplace_back( nLastParam0 + nSplit0, nLastParam1 + nSplit1) ; ++c1 ; } bSplitToAdd = ! (c0 == ssize( vdParamPos0) - 1 && c1 == ssize( vdParamPos1) - 1) ; } } // salvo i dati di questo accoppiamento dLenPrev0 = dLenCurr0 ; dLenPrev1 = dLenCurr1 ; if ( ! bIntParam0 && dParam0 > dLastParam0) { vdSplit0.push_back( dParam0) ; nSplit0 = vdSplit0.size() ; } if ( ! bIntParam1 && dParam1 > dLastParam1) { vdSplit1.push_back( dParam1) ; nSplit1 = vdSplit1.size() ; } dLastParam0 = dParam0 ; dLastParam1 = dParam1 ; ptLast0 = ptU0 ; ptLast1 = ptU1 ; vPairs.emplace_back( nParam0 + nSplit0, nParam1 + nSplit1) ; } // applico effettivamente gli split int nUnit = 0 ; if ( ! vdSplit1.empty()) nUnit = int( vdSplit1.back()) ; for ( int z = int( vdSplit1.size() - 1) ; z >= 0 ; --z) { double dSplit = vdSplit1[z] ; int nSplit = int( dSplit) ; // se sto cercando di fare uno split sulla stessa curva che ho già splittato al passaggio precedente allora devo // riscalare if ( nSplit == nUnit && z < int( vdSplit1.size() - 1)) { dSplit = nSplit + ( dSplit - nSplit) / (vdSplit1[z+1] - nSplit); } nUnit = nSplit ; CrvU1.AddJoint( dSplit) ; } if ( ! vdSplit0.empty()) nUnit = int( vdSplit0.back()) ; for ( int z = int( vdSplit0.size() - 1) ; z >= 0 ; --z) { double dSplit = vdSplit0[z] ; int nSplit = int( dSplit) ; // se sto cercando di fare uno split sulla stessa curva che ho già splittato al passaggio precedente allora devo // riscalare if ( nSplit == nUnit && z < int( vdSplit0.size() - 1)) { dSplit = nSplit + ( dSplit - nSplit) / (vdSplit0[z+1] - nSplit); } nUnit = nSplit ; CrvU0.AddJoint( dSplit) ; } nSpanU0 = CrvU0.GetCurveCount() ; nSpanU1 = CrvU1.GetCurveCount() ; // aggiungo l'ultima coppia se necessario if ( vPairs.back().first != nSpanU0 || vPairs.back().second != nSpanU1) vPairs.emplace_back( nSpanU0, nSpanU1) ; // trovo il numero di span che dovrà avere la superficie int nSpanU = int( vPairs.size()) - 1 ; int nSecondRowInd = nDegU * nSpanU + 1 ; // inizializzo la superficie Init( nDegU, nDegV, nSpanU, nSpanV, bRat) ; // aggiungo i punti di controllo scorrendo in contemporanea le due curve int nAddedSpan = 0 ; int nCrv0 = 0 ; int nCrv1 = 0 ; bool bLast0 = false ; bool bLast1 = false ; bool bOk = true ; while ( nAddedSpan < nSpanU && bOk) { nCrv0 = vPairs[nAddedSpan].first ; nCrv1 = vPairs[nAddedSpan].second ; bool bRep0 = nCrv0 < nSpanU0 ? nCrv0 == vPairs[nAddedSpan + 1].first : nCrv0 == vPairs[nAddedSpan - 1].first ; bool bRep1 = nCrv1 < nSpanU1 ? nCrv1 == vPairs[nAddedSpan + 1].second : nCrv1 == vPairs[nAddedSpan - 1].second ; if ( nCrv0 >= nSpanU0){ nCrv0 = nSpanU0 - 1 ; bLast0 = true ; } if ( nCrv1 >= nSpanU1) { nCrv1 = nSpanU1 - 1 ; bLast1 = true ; } const ICurveBezier* pSubCrv0 = GetCurveBezier( CrvU0.GetCurve( nCrv0)) ; for ( int i = nAddedSpan == 0 ? 0 : 1 ; i < nLastPoint ; ++ i) { int nInd = i ; // se ho una ripetizione allora riaggiungo l'ultimo punto. Se sono ancora alla curva 0 invece devo aggiungere lo start della curva U0 if ( bRep0 || bLast0) nInd = ! bLast0 ? 0 : nDegU ; if ( ! bRat) bOk = SetControlPoint( nAddedSpan * nDegU + i, pSubCrv0->GetControlPoint( nInd)) ; else bOk = SetControlPoint( nAddedSpan * nDegU + i, pSubCrv0->GetControlPoint( nInd), pSubCrv0->GetControlWeight( nInd)) ; } const ICurveBezier* pSubCrv1 = GetCurveBezier( CrvU1.GetCurve( nCrv1)) ; for ( int i = nAddedSpan == 0 ? 0 : 1 ; i < nLastPoint ; ++ i) { int nInd = i ; // se ho una ripetizione allora riaggiungo l'ultimo punto. Se sono ancora alla curva 0 invece devo aggiungere lo start della curva U1 if ( bRep1 || bLast1) nInd = ! bLast1 ? 0 : nDegU ; if ( ! bRat) bOk = SetControlPoint( nSecondRowInd + nAddedSpan * nDegU + i, pSubCrv1->GetControlPoint( nInd)) ; else bOk = SetControlPoint( nSecondRowInd + nAddedSpan * nDegU + i, pSubCrv1->GetControlPoint( nInd), pSubCrv1->GetControlWeight( nInd)) ; } ++ nAddedSpan ; } #if SAVERULEDGUIDEDISO //debug vGeo.clear() ; ICURVEPOVECTOR vCrvIso ; GetAllPatchesIsocurves( false, vCrvIso) ; for ( int i = 0 ; i < ssize( vCrvIso) ; ++i) { vGeo.push_back( vCrvIso[i]->Clone()) ; } vector vCol( ssize( vCrvIso)) ; fill( vCol.begin(), vCol.end(), Color( 255,0,128)) ; SaveGeoObj( vGeo, vCol, "D:/Temp/bezier/ruled/isoCurves.nge") ; //debug vGeo.clear() ; vGeo.push_back( CrvU0.Clone()) ; vGeo.push_back( CrvU1.Clone()) ; vCol.clear() ; vCol.push_back(Color(0,64,128)) ; vCol.push_back(Color(128,64,0)) ; SaveGeoObj( vGeo, vCol, "D:/Temp/bezier/ruled/NewCurves.nge") ; #endif return true ; } //---------------------------------------------------------------------------- bool SurfBezier::RemoveCollapsedSpans( void) { double dTol = EPS_SMALL ; //controllo se ho delle span collassate e le rimuovo if ( m_nSpanU > 1 || m_nSpanV > 1) { CalcPoles() ; if ( ! m_vbPole[2]) { // scorro i punti della prima riga INTVECTOR vnCollapsedSpan ; for ( int i = 0 ; i < m_nSpanU ; ++i) { bool bSamePoint = true ; Point3d ptFirst = m_vPtCtrl[m_nDegU * i] ; // cerco se trovo tutti i punti in U coincidenti in una delle Span for ( int j = 1 ; j < m_nDegU + 1 && bSamePoint ; ++j) { if ( ! AreSamePointEpsilon( ptFirst, m_vPtCtrl[m_nDegU * i + j], dTol)) bSamePoint = false ; } if ( bSamePoint) { // se trovo un'altra riga collassata do per scontato che tutta span sia collassata ptFirst = m_vPtCtrl[GetInd( m_nDegU * i, 1)] ; for ( int j = 1 ; j < m_nDegU + 1 && bSamePoint ; ++j) { if ( ! AreSamePointEpsilon( ptFirst, m_vPtCtrl[GetInd( m_nDegU * i + j, 1)], dTol)) bSamePoint = false ; } if ( bSamePoint) vnCollapsedSpan.push_back( i) ; } } int nOldSpanU = m_nSpanU ; if ( ! vnCollapsedSpan.empty()) { // cancello le span che risultano collassate int nNewSpanU = m_nSpanU - ssize( vnCollapsedSpan) ; int nNewDim = ( m_nDegU * nNewSpanU + 1) * ( m_nDegV * m_nSpanV + 1) ; PNTVECTOR vNewCtrlPnt( nNewDim) ; DBLVECTOR vNewWeight( nNewDim) ; int nCurrSkipInd = -1 ; int nCurrSkip = -1 ; for ( int nIndV = 0 ; nIndV < m_nSpanV * m_nDegV + 1 ; ++nIndV) { nCurrSkipInd = 0 ; nCurrSkip = vnCollapsedSpan[nCurrSkipInd] ; for ( int i = 0 ; i < m_nSpanU ; ++i) { if ( i != nCurrSkip) { for ( int j = ( i - nCurrSkipInd) ==0 ? 0 : 1 ; j < m_nDegU + 1 ; ++j) { vNewCtrlPnt[nIndV * ( m_nDegU * nNewSpanU + 1) + (i - nCurrSkipInd) * m_nDegU + j] = m_vPtCtrl[GetInd( m_nDegU * i + j, nIndV)] ; if ( m_bRat) { vNewWeight[nIndV * ( m_nDegU * nNewSpanU + 1) + (i - nCurrSkipInd) * m_nDegU + j] = m_vWeCtrl[GetInd( m_nDegU * i + j, nIndV)] ; } } } else { ++nCurrSkipInd ; if ( nCurrSkipInd > ssize( vnCollapsedSpan) - 1) nCurrSkip = -1 ; else nCurrSkip = vnCollapsedSpan[nCurrSkipInd] ; } } } // aggiorno i dati della superficie // vettori dei punti e numero di span ISurfFlatRegion* pSFRTrim = nullptr ; bool bTrimmed = m_bTrimmed ; if ( bTrimmed) { pSFRTrim = GetTrimRegion() ; m_pTrimReg = nullptr ; } Init( m_nDegU, m_nDegV, nNewSpanU, m_nSpanV, m_bRat) ; m_vPtCtrl = vNewCtrlPnt ; if ( m_bRat) m_vWeCtrl = vNewWeight ; if ( bTrimmed) { // elimino le span di troppo dallo spazio parametrico PtrOwner pNewTrim( pSFRTrim->Clone()) ; for ( int i = ssize( vnCollapsedSpan) - 1 ; i >= 0 ; --i) { int nSpan = vnCollapsedSpan[i] ; // tolgo tutta la parte a destra della colonna da togliere PtrOwner pSFRCut ( GetSurfFlatRegionRectangle( ( nOldSpanU - nSpan) * SBZ_TREG_COEFF, m_nSpanV * SBZ_TREG_COEFF + 2)) ; if ( nSpan != 0) { pSFRCut->Translate( Vector3d( nSpan * SBZ_TREG_COEFF, -1)) ; pNewTrim->Subtract( *pSFRCut) ; } if ( pNewTrim->IsValid()) { // ritaglio dal parametrico originale la parte a destra della colonna da eliminare e la incollo alla parte a sinistra PtrOwner pRightPart( pSFRTrim->Clone()) ; pSFRCut.Set( GetSurfFlatRegionRectangle( ( nSpan + 1) * SBZ_TREG_COEFF, m_nSpanV * SBZ_TREG_COEFF + 2)) ; pSFRCut->Translate( Vector3d( nSpan * SBZ_TREG_COEFF, -1)) ; if ( pRightPart->Subtract( *pSFRCut) && pRightPart->IsValid()) { if ( ! pNewTrim->Add( *pRightPart) || ! pNewTrim->IsValid()) break ; } } else break ; } SetTrimRegion( *pSFRTrim) ; } } } if ( ! m_vbPole[1]) { // scorro i punti della prima colonna INTVECTOR vnCollapsedSpan ; for ( int i = 0 ; i < m_nSpanV ; ++i) { bool bSamePoint = true ; Point3d ptFirst = m_vPtCtrl[GetInd( 0, i * m_nDegV)] ; // cerco se trovo tutti i punti in U coincidenti in una delle Span for ( int j = 1 ; j < m_nDegV + 1 && bSamePoint ; ++j) { if ( ! AreSamePointEpsilon( ptFirst, m_vPtCtrl[GetInd( 0, i * m_nDegV + j)], dTol)) bSamePoint = false ; } if ( bSamePoint) { // se trovo un'altra colonna collassata do per scontato che tutta span sia collassata ptFirst = m_vPtCtrl[GetInd( 1, i * m_nDegV)] ; for ( int j = 1 ; j < m_nDegV + 1 && bSamePoint ; ++j) { if ( ! AreSamePointEpsilon( ptFirst, m_vPtCtrl[GetInd( 1, i * m_nDegV + j)], dTol)) bSamePoint = false ; } if ( bSamePoint) vnCollapsedSpan.push_back( i) ; } } int nOldSpanV = m_nSpanV ; if ( ! vnCollapsedSpan.empty()) { // cancello le span che risultano collassate int nNewSpanV = m_nSpanV - ssize( vnCollapsedSpan) ; int nNewDim = ( m_nDegU * m_nSpanU + 1) * ( m_nDegV * nNewSpanV + 1) ; PNTVECTOR vNewCtrlPnt( nNewDim) ; DBLVECTOR vNewWeight( nNewDim) ; int nCurrSkipInd = 0 ; int nCurrSkip = vnCollapsedSpan[nCurrSkipInd] ; for ( int nIndV = 0 ; nIndV < m_nSpanV * m_nDegV + 1 ; ++nIndV) { if ( nIndV / m_nDegV != nCurrSkip) { for ( int i = 0 ; i < m_nSpanU ; ++i) { for ( int j = i==0 ? 0 : 1 ; j < m_nDegU + 1 ; ++j) { vNewCtrlPnt[( nIndV - ( nCurrSkipInd * m_nDegV)) * ( m_nDegU * m_nSpanU + 1) + i * m_nDegU + j] = m_vPtCtrl[GetInd( m_nDegU * i + j, nIndV)] ; if ( m_bRat) { vNewWeight[( nIndV - ( nCurrSkipInd * m_nDegV)) * ( m_nDegU * m_nSpanU + 1) + i * m_nDegU + j] = m_vWeCtrl[GetInd( m_nDegU * i + j, nIndV)] ; } } } } else { nIndV += m_nDegV - 1 ; ++nCurrSkipInd ; if ( nCurrSkipInd > ssize( vnCollapsedSpan) - 1) nCurrSkip = -1 ; else nCurrSkip = vnCollapsedSpan[nCurrSkipInd] ; } } // aggiorno i dati della superficie // vettori dei punti e numero di span ISurfFlatRegion* pSFRTrim = nullptr ; bool bTrimmed = m_bTrimmed ; if ( bTrimmed) { pSFRTrim = GetTrimRegion() ; m_pTrimReg = nullptr ; } Init( m_nDegU, m_nDegV, m_nSpanU, nNewSpanV, m_bRat) ; m_vPtCtrl = vNewCtrlPnt ; if ( m_bRat) m_vWeCtrl = vNewWeight ; if ( bTrimmed) { // elimino le span di troppo dallo spazio parametrico PtrOwner pNewTrim( pSFRTrim->Clone()) ; for ( int i = ssize( vnCollapsedSpan) - 1 ; i >= 0 ; --i) { int nSpan = vnCollapsedSpan[i] ; // tolgo tutta la parte a sopra la riga da togliere PtrOwner pSFRCut ( GetSurfFlatRegionRectangle( m_nSpanU * SBZ_TREG_COEFF + 2, ( nOldSpanV - nSpan) * SBZ_TREG_COEFF)) ; if ( nSpan != 0) { pSFRCut->Translate( Vector3d( -1, nSpan * SBZ_TREG_COEFF)) ; pNewTrim->Subtract( *pSFRCut) ; } if ( pNewTrim->IsValid()) { // ritaglio dal parametrico originale la parte sopra la riga da eliminare e la incollo alla parte sotto la riga PtrOwner pUpperPart( pSFRTrim->Clone()) ; pSFRCut.Set( GetSurfFlatRegionRectangle( m_nSpanU * SBZ_TREG_COEFF + 2, ( nSpan + 1) * SBZ_TREG_COEFF)) ; pSFRCut->Translate( Vector3d( -1, nSpan * SBZ_TREG_COEFF)) ; if ( pUpperPart->Subtract( *pSFRCut) && pUpperPart->IsValid()) { if ( ! pNewTrim->Add( *pUpperPart) || ! pNewTrim->IsValid()) break ; } } else break ; } SetTrimRegion( *pSFRTrim) ; } } } } return true ; } //---------------------------------------------------------------------------- bool SurfBezier::SwapParameters( void) { // inverto il parametro U con il parametro V // salvo i vecchi dati int nSpanU = m_nSpanV ; int nSpanV = m_nSpanU ; int nDegU = m_nDegV ; int nDegV = m_nDegU ; bool bTrimmed = m_bTrimmed ; PtrOwner pSFRTRim ; if ( m_bTrimmed) { pSFRTRim.Set( GetTrimRegion()) ; m_pTrimReg = nullptr ; } // creo il vettore dei punti di controllo PNTVECTOR vNewCtrlPt( GetDim()) ; DBLVECTOR vNewWeight( GetDim()) ; for ( int j = 0 ; j < m_nDegV * m_nSpanV + 1 ; ++j) { for ( int i = 0 ; i < m_nDegU * m_nSpanU + 1 ; ++ i) { vNewCtrlPt[i * ( nDegU * nSpanU + 1) + j] = m_vPtCtrl[GetInd(i,j)] ; if ( m_bRat) vNewWeight[i * ( nDegU * nSpanU + 1) + j] = m_vWeCtrl[GetInd(i,j)] ; } } Init( nDegU, nDegV, nSpanU, nSpanV, m_bRat) ; if ( bTrimmed) { pSFRTRim->Mirror( ORIG, Vector3d(1,-1)) ; SetTrimRegion( *pSFRTRim) ; } m_vPtCtrl = vNewCtrlPt ; m_vWeCtrl = vNewWeight ; return true ; } //---------------------------------------------------------------------------- bool SurfBezier::LimitSurfToTrimmedRegion( void) { if ( ! m_bTrimmed || m_pTrimReg == nullptr) return true ; double dParamArea = m_nSpanU * m_nSpanV * SBZ_TREG_COEFF * SBZ_TREG_COEFF ; double dTrimmedArea = 0 ; m_pTrimReg->GetArea( dTrimmedArea) ; // se la parte trimmata è molto inferiore alla superficie originale limito la superficie alla parte trimmata if ( dTrimmedArea / dParamArea < 0.01) { BBox3d bboxTrim ; m_pTrimReg->GetLocalBBox( bboxTrim) ; // estraggo tutte le isocurve in U e le limito a poco più del box della regione trimmata double dMinU = bboxTrim.GetMin().x - 10 ; dMinU = Clamp( dMinU, 0., double(m_nSpanU * SBZ_TREG_COEFF)) ; double dMaxU = bboxTrim.GetMax().x + 10 ; dMaxU = Clamp( dMaxU, 0., double(m_nSpanU * SBZ_TREG_COEFF)) ; double dMinV = bboxTrim.GetMin().y - 10 ; dMinV = Clamp( dMinV, 0., double(m_nSpanV * SBZ_TREG_COEFF)) ; double dMaxV = bboxTrim.GetMax().y + 10 ; dMaxV = Clamp( dMaxV, 0., double(m_nSpanV * SBZ_TREG_COEFF)) ; int nV = m_nSpanV * m_nDegV + 1 ; int nU = m_nSpanU * m_nDegU + 1 ; vector vIsoU ; vIsoU.reserve( nV) ; for ( int i = 0 ; i < nV ; ++i) { vIsoU.emplace_back() ; bool bOk = false ; for ( int k = 0 ; k < m_nSpanU ; ++k) { CurveBezier cbIsoU ; cbIsoU.Init( m_nDegU, m_bRat) ; for ( int j = 0 ; j <= m_nDegU ; ++j) { if ( ! m_bRat) cbIsoU.SetControlPoint( j, GetControlPoint( k * m_nDegU + j, i, &bOk)) ; else { double dW = GetControlWeight( j, i, &bOk) ; cbIsoU.SetControlPoint( j, GetControlPoint( k * m_nDegU + j, i, &bOk), dW) ; } } vIsoU.back().AddCurve( cbIsoU) ; } vIsoU.back().TrimStartEndAtParam( dMinU / SBZ_TREG_COEFF, dMaxU / SBZ_TREG_COEFF) ; } // a partire dalle curve limitate in U recupero le curve in V e le limito int nSpanU = vIsoU.back().GetCurveCount() ; nU = nSpanU * m_nDegU + 1 ; vector vIsoV ; vIsoV.reserve( nU) ; for ( int i = 0 ; i < nU ; ++i) { int nSubU = i / m_nDegU ; int nPoint = i % m_nDegU ; if ( nSubU == nSpanU) { --nSubU ; nPoint = m_nDegU ; } vIsoV.emplace_back() ; for ( int k = 0 ; k < m_nSpanV ; ++k) { CurveBezier cbIsoV ; cbIsoV.Init( m_nDegV, m_bRat) ; for ( int j = 0 ; j <= m_nDegV ; ++j) { const CurveComposite& ccIsoU = vIsoU[k * m_nDegV + j] ; const ICurveBezier* cbSubCurveU = GetCurveBezier( ccIsoU.GetCurve( nSubU)) ; if ( ! m_bRat) cbIsoV.SetControlPoint( j, cbSubCurveU->GetControlPoint( nPoint)) ; else { double dW = cbSubCurveU->GetControlWeight( nPoint) ; cbIsoV.SetControlPoint( j, cbSubCurveU->GetControlPoint( nPoint), dW) ; } } vIsoV.back().AddCurve( cbIsoV) ; } vIsoV.back().TrimStartEndAtParam( dMinV / SBZ_TREG_COEFF, dMaxV / SBZ_TREG_COEFF) ; } int nSpanV = vIsoV.back().GetCurveCount() ; // ricostruisco la superficie a partire dalle nuove curve // salvo la regione di trim ISurfFlatRegion* pSaveTrim = m_pTrimReg ; m_pTrimReg = nullptr ; Init( m_nDegU, m_nDegV, nSpanU, nSpanV, m_bRat) ; // scorro le isocurve in V for ( int i = 0 ; i < nU ; ++i) { CurveComposite ccIsoV = vIsoV[i] ; for ( int k = 0 ; k < nSpanV ; ++k) { const ICurveBezier* cbSubCurveV = GetCurveBezier( ccIsoV.GetCurve( k)) ; for ( int j = k == 0 ? 0 : 1 ; j <= m_nDegV ; ++j) { if ( ! m_bRat) SetControlPoint( i, k * m_nDegV + j, cbSubCurveV->GetControlPoint( j)) ; else { double dW = cbSubCurveV->GetControlWeight( j) ; SetControlPoint( i, k * m_nDegV + j, cbSubCurveV->GetControlPoint( j), dW) ; } } } } #if SAVELIMITSURF vector vGeo ; vGeo.push_back( pSaveTrim->Clone()) ; #endif // riscalo la superficie di trim double dDimX = dMaxU - dMinU ; double dDimY = dMaxV - dMinV ; Vector3d vtMove( dMinU, dMinV) ; pSaveTrim->Translate( -vtMove) ; bool bRescaled = false ; DBLVECTOR vU, vV ; vU.push_back( 0) ; int nFirst = int( ceil( dMinU / SBZ_TREG_COEFF)) ; nFirst = nFirst == 0 ? 1 : nFirst ; for ( int i = 0 ; i < nSpanU - 1 ; ++i) vU.push_back( nFirst + i - dMinU/ SBZ_TREG_COEFF) ; vU.push_back( (dMaxU - dMinU) / SBZ_TREG_COEFF) ; vV.push_back( 0) ; nFirst = int( ceil( dMinV / SBZ_TREG_COEFF)) ; nFirst = nFirst == 0 ? 1 : nFirst ; for ( int i = 0 ; i < nSpanV - 1 ; ++i) vV.push_back( nFirst + i - dMinV/ SBZ_TREG_COEFF) ; vV.push_back( (dMaxV - dMinV)/ SBZ_TREG_COEFF) ; MakeUniform( pSaveTrim, bRescaled, vU, vV, m_nDegU, m_nDegV, dDimX, dDimY, false) ; //m_pTrimReg->Scale( GLOB_FRM, nSpanU * SBZ_TREG_COEFF / dDimX, nSpanV * SBZ_TREG_COEFF / dDimY, 1) ; m_pTrimReg = GetBasicSurfFlatRegion( pSaveTrim) ; m_bTrimmed = true ; #if SAVELIMITSURF vGeo.push_back( m_pTrimReg->Clone()) ; SaveGeoObj( vGeo, "D:\\Temp\\bezier\\edit surf\\trim_reg_before_after.nge") ; vGeo.clear() ; #endif } return true ; }