//---------------------------------------------------------------------------- // EgalTech 2013-2013 //---------------------------------------------------------------------------- // File : CurveAux.cpp Data : 22.11.13 Versione : 1.3a1 // Contenuto : Implementazione di alcune funzioni di utilitā per le curve. // // // // Modifiche : 22.11.13 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "CurveAux.h" #include "GeoConst.h" #include "CurveLine.h" #include "CurveArc.h" #include "CurveBezier.h" #include "CurveComposite.h" #include "/EgtDev/Include/EGkStringUtils3d.h" #include "/EgtDev/Include/EgtPointerOwner.h" using namespace std ; //---------------------------------------------------------------------------- bool IsClosed( const ICurve& crvC) { Point3d ptStart ; Point3d ptEnd ; return ( crvC.GetStartPoint( ptStart) && crvC.GetEndPoint( ptEnd) && AreSamePointApprox( ptStart, ptEnd)) ; } //---------------------------------------------------------------------------- bool IsValidParam( const ICurve& crvC, double dPar, ICurve::Side nSide) { // recupero l'intervallo di validitā del parametro double dStart, dEnd ; if ( ! crvC.GetDomain( dStart, dEnd)) return false ; // il parametro deve stare nei limiti if ( dPar < dStart - EPS_PARAM || dPar > dEnd + EPS_PARAM) return false ; // se curva chiusa non sono necessari altri controlli if ( crvC.IsClosed()) return true ; // per curve aperte non posso studiare la curva prima dell'inizio e dopo la fine if ( ( dPar < dStart + EPS_PARAM && nSide == ICurve::FROM_MINUS) || ( dPar > dEnd - EPS_PARAM && nSide == ICurve::FROM_PLUS)) return false ; return true ; } //---------------------------------------------------------------------------- bool IsStartParam( const ICurve& crvC, double dPar) { // recupero l'intervallo di validitā del parametro double dStart, dEnd ; if ( ! crvC.GetDomain( dStart, dEnd)) return false ; // se il parametro non č nell'intorno dell'inizio if ( fabs( dPar - dStart) > EPS_PARAM) return false ; return true ; } //---------------------------------------------------------------------------- bool IsEndParam( const ICurve& crvC, double dPar) { // recupero l'intervallo di validitā del parametro double dStart, dEnd ; if ( ! crvC.GetDomain( dStart, dEnd)) return false ; // se il parametro non č nell'intorno della fine if ( fabs( dPar - dEnd) > EPS_PARAM) return false ; return true ; } //---------------------------------------------------------------------------- bool MoveParamToAvoidTg( double& dU, ICurve::Side nSide, const ICurve& Curve) { // verifico che il parametro sia accettabile if ( ! Curve.IsValidParam( dU, nSide)) return false ; // determino un incremento piccolo ma valido del parametro U double dDeltaU = 1000 * EPS_PARAM ; Point3d ptPos ; Vector3d vtDer1 ; if ( Curve.GetPointD1D2( dU, nSide, ptPos, &vtDer1)) { double dDer1 = vtDer1.LenXY() ; if ( dDer1 > EPS_ZERO) dDeltaU = 10 * EPS_SMALL / dDer1 ; } // cerco di spostarmi per evitare eventuali problemi di tangenza if ( nSide == ICurve::FROM_MINUS) { double dUm = dU - dDeltaU ; if ( ! Curve.IsValidParam( dUm, nSide)) { if ( Curve.IsClosed()) { double dStart, dEnd ; Curve.GetDomain( dStart, dEnd) ; dUm = ( dEnd - dStart) - dDeltaU ; } else dUm = dU ; } dU = dUm ; return true ; } else { // nSide == ICurve::FROM_PLUS double dUm = dU + dDeltaU ; if ( ! Curve.IsValidParam( dUm, nSide)) { if ( Curve.IsClosed()) { double dStart, dEnd ; Curve.GetDomain( dStart, dEnd) ; dUm = dStart + dDeltaU ; } else dUm = dU ; } dU = dUm ; return true ; } } //---------------------------------------------------------------------------- bool GetTang( const ICurve& crvC, double dU, ICurve::Side nS, Vector3d& vtTang) { Point3d ptPos ; return GetPointTang( crvC, dU, nS, ptPos, vtTang) ; } //---------------------------------------------------------------------------- bool GetPointTang( const ICurve& crvC, double dU, ICurve::Side nS, Point3d& ptPos, Vector3d& vtTang) { if ( ! crvC.GetPointD1D2( dU, nS, ptPos, &vtTang)) return false ; if ( vtTang.Normalize( EPS_ZERO)) return true ; // nel caso la derivata prima sia nulla, utilizziamo la seconda Vector3d vtDummy ; if ( ! crvC.GetPointD1D2( dU, nS, ptPos, &vtDummy, &vtTang)) return false ; double dUmod = dU + ( nS == ICurve::FROM_MINUS ? -1 : +1) * 100 * EPS_PARAM ; Point3d ptDummy ; // se anche la derivata seconda č nulla, provo a spostarmi di poco if ( ! vtTang.Normalize( EPS_ZERO)) { if ( ! crvC.GetPointD1D2( dUmod, nS, ptDummy, &vtDummy, &vtTang)) return false ; if ( ! vtTang.Normalize( EPS_ZERO)) return false ; } // per il verso, lo confrontiamo con quello della derivata prima un poco discosta Vector3d vtD1near ; if ( ! crvC.GetPointD1D2( dUmod, nS, ptDummy, &vtD1near)) return false ; if ( ( vtTang * vtD1near) < 0) vtTang.Invert() ; return true ; } //---------------------------------------------------------------------------- bool GetPointDiffGeom( const ICurve& crvC, double dU, ICurve::Side nS, CrvPointDiffGeom& oDiffG) { // calcolo punto e derivate Point3d ptPos ; Vector3d vtDer1, vtDer2 ; if ( ! crvC.GetPointD1D2( dU, nS, ptPos, &vtDer1, &vtDer2)) return false ; // assegno parametro e posizione oDiffG.dU = dU ; oDiffG.ptP = ptPos ; // se esiste la derivata prima non nulla double dSqLenD1 = vtDer1.SqLen() ; if ( vtDer1.Normalize()) { oDiffG.vtT = vtDer1 ; // del vettore deriv2^ tengo la sola componente perpendicolare al vettore tangente oDiffG.vtN = vtDer2 - ( vtDer2 * vtDer1) * vtDer1 ; if ( oDiffG.vtN.Normalize()) { oDiffG.dCurv = ( vtDer1 ^ vtDer2).Len() / dSqLenD1 ; oDiffG.nStatus = CrvPointDiffGeom::NCRV ; } else { oDiffG.dCurv = 0 ; oDiffG.nStatus = CrvPointDiffGeom::TANG ; } } // se esiste almeno derivata seconda non nulla, definisce la tangente else if ( vtDer2.Normalize()) { oDiffG.vtT = vtDer2 ; oDiffG.vtN.Set( 0, 0, 0) ; oDiffG.dCurv = 0 ; // per il verso, lo confrontiamo con quello della derivata prima un poco discosta Point3d ptDummy ; Vector3d vtD1near ; dU += ( nS == ICurve::FROM_MINUS ? -1 : +1) * 100 * EPS_PARAM ; if ( crvC.GetPointD1D2( dU, nS, ptDummy, &vtD1near)) { if ( ( oDiffG.vtT * vtD1near) < 0) oDiffG.vtT.Invert() ; oDiffG.nStatus = CrvPointDiffGeom::TANG ; } else { oDiffG.vtT.Set( 0, 0, 0) ; oDiffG.nStatus = CrvPointDiffGeom::POS ; } } // altrimenti non sono definite la tangente e la normale else { oDiffG.vtT.Set( 0, 0, 0) ; oDiffG.vtN.Set( 0, 0, 0) ; oDiffG.dCurv = 0 ; oDiffG.nStatus = CrvPointDiffGeom::POS ; } return true ; } //---------------------------------------------------------------------------- bool CurveDump( const ICurve& crvC, string& sOut, const char* szNewLine) { // verifico validitā curva if ( ! crvC.IsValid()) return false ; // punti iniziale e finale Point3d ptPS ; crvC.GetStartPoint( ptPS) ; sOut += "PS(" + ToString( ptPS, 3) + ")" + szNewLine ; Point3d ptPE ; crvC.GetEndPoint( ptPE) ; sOut += "PE(" + ToString( ptPE, 3) + ")" + szNewLine ; // direzioni iniziale e finale Vector3d vtDirS ; crvC.GetStartDir( vtDirS) ; sOut += "VS(" + ToString( vtDirS, 3) + ")" + szNewLine ; Vector3d vtDirE ; crvC.GetEndDir( vtDirE) ; sOut += "VE(" + ToString( vtDirE, 3) + ")" + szNewLine ; // eventuali direzione di estrusione e spessore Vector3d vtExtr ; crvC.GetExtrusion( vtExtr) ; if ( ! vtExtr.IsSmall()) { double dThick ; crvC.GetThickness( dThick) ; sOut += "Extr(" + ToString( vtExtr, 3) + ") Th=" + ToString( dThick) + szNewLine ; } // lunghezza double dLen = 0 ; crvC.GetLength( dLen) ; sOut += "Len=" + ToString( dLen,3) + szNewLine ; // altri dati per curva chiusa if ( crvC.IsClosed()) { PolyLine PL ; crvC.ApproxWithLines( LIN_TOL_STD, ANG_TOL_STD_DEG, PL) ; double dAreaXY = 0 ; PL.GetAreaXY( dAreaXY) ; bool bCCW = ( dAreaXY > 0) ; sOut += string( "Closed") + ( bCCW ? " CCW" : " CW") + " AreaXY=" + ToString( fabs( dAreaXY),1) + szNewLine ; } return true ; } //---------------------------------------------------------------------------- bool CopyExtrusion( const ICurve* pSouCrv, ICurve* pDestCrv) { // verifico puntatori if ( pSouCrv == nullptr || pDestCrv == nullptr) return false ; // eseguo copia Vector3d vtExtr ; pSouCrv->GetExtrusion( vtExtr) ; pDestCrv->SetExtrusion( vtExtr) ; return true ; } //---------------------------------------------------------------------------- bool CopyThickness( const ICurve* pSouCrv, ICurve* pDestCrv) { // verifico puntatori if ( pSouCrv == nullptr || pDestCrv == nullptr) return false ; // eseguo copia double dThick = 0 ; pSouCrv->GetThickness( dThick) ; pDestCrv->SetThickness( dThick) ; return true ; } //---------------------------------------------------------------------------- ICurve* ArcToBezierCurve( const ICurve* pCrv) { // verifico sia un arco const CurveArc* pArc = GetBasicCurveArc( pCrv) ; if ( pArc == nullptr) return nullptr ; // se angolo al centro sotto il limite, basta una curva if ( fabs( pArc->GetAngCenter()) < BEZARC_ANG_CEN_MAX + EPS_ANG_SMALL) { // creo la curva di Bezier equivalente all'arco PtrOwner pCrvBez( CreateBasicCurveBezier()) ; if ( IsNull( pCrvBez) || ! pCrvBez->FromArc( *pArc)) return nullptr ; // restituisco la curva return Release( pCrvBez) ; } // altrimenti curva composita di Bezier else { // creo la curva composita PtrOwner pCrvCompo( CreateBasicCurveComposite()) ; if ( IsNull( pCrvCompo)) return nullptr ; // inserisco nella CC le curve di Bezier equivalenti alle parti dell'arco int nParts = (int) ceil( fabs( pArc->GetAngCenter()) / BEZARC_ANG_CEN_MAX) ; nParts = max( nParts, 2) ; for ( int i = 0 ; i < nParts ; ++ i) { // copio l'arco originale CurveArc cArc = *pArc ; // lo limito alla parte di interesse cArc.TrimStartEndAtParam( i / double( nParts), ( i + 1) / double( nParts)) ; // creo la curva di Bezier equivalente PtrOwner pCrvBez( CreateBasicCurveBezier()) ; if ( IsNull( pCrvBez) || ! pCrvBez->FromArc( cArc)) return nullptr ; // aggiungo la curva di Bezier a quella composita if ( ! pCrvCompo->AddCurve( Release( pCrvBez))) return false ; } // copio estrusione e spessore CopyExtrusion( pArc, Get( pCrvCompo)) ; CopyThickness( pArc, Get( pCrvCompo)) ; // restituisco la curva return Release( pCrvCompo) ; } } //---------------------------------------------------------------------------- ICurve* CurveToNoArcsCurve( const ICurve* pCrv) { // verifico validitā curva if ( pCrv == nullptr) return nullptr ; // se arco, devo trasformarlo in curva di Bezier (semplice o composta) if ( pCrv->GetType() == CRV_ARC) { return ArcToBezierCurve( pCrv) ; } // se curva composita, devo trasformarla in composita senza archi else if ( pCrv->GetType() == CRV_COMPO) { PtrOwner pCopyCC( GetBasicCurveComposite( pCrv->Clone())) ; if ( IsNull( pCopyCC) || ! pCopyCC->ArcsToBezierCurves()) return nullptr ; return Release( pCopyCC) ; } // altrimenti devo solo copiarla else { return pCrv->Clone() ; } } //---------------------------------------------------------------------------- ICurve* CurveToArcsPerpExtrCurve( const ICurve* pCrv, double dLinTol, double dAngTolDeg) { // verifico validitā curva if ( pCrv == nullptr) return nullptr ; // se arco in piano non perpendicolare ad estrusione o curva di Bezier trasformo if ( ( pCrv->GetType() == CRV_ARC && ! GetBasicCurveArc(pCrv)->IsInPlanePerpExtr()) || pCrv->GetType() == CRV_BEZ) { // creo un poliarco PolyArc PA ; if ( ! pCrv->ApproxWithArcs( dLinTol, dAngTolDeg, PA)) return nullptr ; // creo una nuova curva composita a partire dal poliarco PtrOwner pCopyCC( CreateBasicCurveComposite()) ; if ( IsNull( pCopyCC)) return nullptr ; if ( ! pCopyCC->FromPolyArc( PA)) return nullptr ; // copio estrusione e spessore CopyExtrusion( pCrv, Get( pCopyCC)) ; CopyThickness( pCrv, Get( pCopyCC)) ; return Release( pCopyCC) ; } // altrimenti devo solo copiarla else { return pCrv->Clone() ; } } //---------------------------------------------------------------------------- bool NurbsCurveCanonicalize( CNurbsData& cnData) { // se periodica if ( cnData.bPeriodic) { // va trasformata in non-periodica (clamped) // vedere The NurbsBook di Les Piegl e Tiller // mancano esempi per testare return false ; } // se con nodi extra if ( cnData.bExtraKnotes) { int nKnotesNbr = int( cnData.vU.size()) ; if ( nKnotesNbr < 4) return false ; cnData.bExtraKnotes = false ; for ( int i = 0 ; i < nKnotesNbr - 2 ; ++ i) cnData.vU[i] = cnData.vU[i+1] ; cnData.vU.resize( nKnotesNbr - 2) ; return true ; } return true ; } //---------------------------------------------------------------------------- ICurve* NurbsToBezierCurve( const CNurbsData& cnData) { // la curva Nurbs deve essere in forma canonica if ( cnData.bPeriodic || cnData.bExtraKnotes) return nullptr ; // numero dei nodi int nU = int( cnData.vCP.size()) + cnData.nDeg - 1 ; // controllo relazione nodi - punti di controllo if ( nU != int( cnData.vU.size())) return nullptr ; // numero degli intervalli int nInt = nU - 2 * cnData.nDeg + 1 ; // se 1 solo intervallo, la Nurbs č giā una curva di Bezier if ( nInt == 1) { // creo la curva di Bezier PtrOwner pCrvBez( CreateCurveBezier()) ; if ( IsNull( pCrvBez)) return nullptr ; // la inizializzo if ( ! pCrvBez->Init( cnData.nDeg, cnData.bRat)) return nullptr ; for ( int i = 0 ; i <= cnData.nDeg ; ++ i) { if ( ! cnData.bRat) { if ( ! pCrvBez->SetControlPoint( i, cnData.vCP[i])) return nullptr ; } else { if ( ! pCrvBez->SetControlPoint( i, cnData.vCP[i], cnData.vW[i])) return nullptr ; } } // se non č una curva ma un punto, la invalido if ( pCrvBez->IsAPoint()) pCrvBez->Init( cnData.nDeg, cnData.bRat) ; // restituisco la curva return Release( pCrvBez) ; } // altrimenti č equivalente ad una curva composita, la creo PtrOwner pCrvCompo( CreateCurveComposite()) ; if ( IsNull( pCrvCompo)) return nullptr ; // vettore dei punti di controllo della curva di Bezier PNTVECTOR vBC ; vBC.resize( cnData.nDeg + 1) ; DBLVECTOR vBW ; vBW.resize( cnData.nDeg + 1) ; if ( ! cnData.bRat) { for ( int i = 0 ; i <= cnData.nDeg ; ++ i) vBC[i] = cnData.vCP[i] ; } else { for ( int i = 0 ; i <= cnData.nDeg ; ++ i) { vBC[i] = cnData.vCP[i] * cnData.vW[i] ; vBW[i] = cnData.vW[i] ; } } // primi coefficienti della successiva PNTVECTOR vNextBC ; vNextBC.resize( cnData.nDeg - 1) ; DBLVECTOR vNextBW ; vNextBW.resize( cnData.nDeg - 1) ; // ... DBLVECTOR vAlfa ; vAlfa.resize( cnData.nDeg - 1) ; int a = cnData.nDeg - 1 ; int b = cnData.nDeg ; bool bPrevRejected = false ; // ciclo while ( b < nU - 1) { int i = b ; while ( b < nU - 1 && fabs( cnData.vU[b+1] - cnData.vU[b]) < EPS_ZERO) ++ b ; int mult = min( b - i + 1, cnData.nDeg) ; if ( mult < cnData.nDeg) { // numeratore di alfa double numer = cnData.vU[b] - cnData.vU[a] ; // calcola e salva gli alfa for ( int j = cnData.nDeg ; j > mult ; -- j) vAlfa[j-mult-1] = numer / ( cnData.vU[a+j] - cnData.vU[a]) ; // inserisco il nodo r volte int r = cnData.nDeg - mult ; for ( int j = 1 ; j <= r ; ++ j) { int save = r - j ; int s = mult + j ; for ( int k = cnData.nDeg ; k >= s ; -- k) vBC[k] = vAlfa[k-s] * vBC[k] + ( 1 - vAlfa[k-s]) * vBC[k-1] ; if ( cnData.bRat) { for ( int k = cnData.nDeg ; k >= s ; -- k) vBW[k] = vAlfa[k-s] * vBW[k] + ( 1 - vAlfa[k-s]) * vBW[k-1] ; } if ( b < nU - 1) { vNextBC[save] = vBC[cnData.nDeg] ; if ( cnData.bRat) vNextBW[save] = vBW[cnData.nDeg] ; } } } // costruisco la curva di Bezier e la inserisco nella curva composita PtrOwner pCrvBez( CreateCurveBezier()) ; if ( IsNull( pCrvBez)) return nullptr ; // se precedente saltata if ( bPrevRejected) { // prendo l'ultimo punto della curva composita per garantire la continuitā Point3d ptEnd ; if ( pCrvCompo->GetEndPoint( ptEnd)) vBC[0] = ptEnd ; } // la inizializzo if ( ! pCrvBez->Init( cnData.nDeg, cnData.bRat)) return nullptr ; if ( ! cnData.bRat) { for ( int i = 0 ; i <= cnData.nDeg ; ++ i) { if ( ! pCrvBez->SetControlPoint( i, vBC[i])) return nullptr ; } } else { for ( int i = 0 ; i <= cnData.nDeg ; ++ i) { if ( ! pCrvBez->SetControlPoint( i, vBC[i] / vBW[i], vBW[i])) return nullptr ; } } // se č una vera curva, la aggiungo alla curva composita if ( ! pCrvBez->IsAPoint()) { if ( ! pCrvCompo->AddCurve( Release( pCrvBez))) return nullptr ; bPrevRejected = false ; } // altrimenti č un punto, la cancello else { pCrvBez.Reset() ; bPrevRejected = true ; } // inizializzazioni per la prossima curva di Bezier if ( b < nU - 1) { if ( ! cnData.bRat) { for ( int i = 0 ; i < cnData.nDeg - 1 ; ++ i) vBC[i] = vNextBC[i] ; for ( int i = cnData.nDeg - mult ; i <= cnData.nDeg ; ++ i) vBC[i] = cnData.vCP[b-cnData.nDeg+i+1] ; } else { for ( int i = 0 ; i < cnData.nDeg - 1 ; ++ i) { vBC[i] = vNextBC[i] ; vBW[i] = vNextBW[i] ; } for ( int i = cnData.nDeg - mult ; i <= cnData.nDeg ; ++ i) { vBC[i] = cnData.vCP[b-cnData.nDeg+i+1] * cnData.vW[b-cnData.nDeg+i+1] ; vBW[i] = cnData.vW[b-cnData.nDeg+i+1] ; } } a = b ; ++ b ; } } // restituisco la curva composita return Release( pCrvCompo) ; }