//---------------------------------------------------------------------------- // EgalTech 2013-2013 //---------------------------------------------------------------------------- // File : CurveBezier.cpp Data : 22.11.13 Versione : 1.3a1 // Contenuto : Implementazione della classe Curva di Bezier. // // // // Modifiche : 28.12.12 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include #include "\EgtDev\Include\EGnStringUtils.h" #include "CurveBezier.h" #include "GeoObjFactory.h" using namespace std ; //---------------------------------------------------------------------------- GEOOBJ_REGISTER( CRV_BEZ, "C_BEZ", CurveBezier) ; //---------------------------------------------------------------------------- CurveBezier::CurveBezier( void) { m_nDeg = 0 ; m_bRat = false ; m_nDimArr = 0 ; m_aPtCtrl = nullptr ; m_aWeCtrl = nullptr ; m_nStatus = TO_VERIFY ; } //---------------------------------------------------------------------------- CurveBezier::~CurveBezier( void) { if ( m_nDimArr > 0) { delete [] m_aPtCtrl ; delete [] m_aWeCtrl ; } m_nDimArr = 0 ; } //---------------------------------------------------------------------------- bool CurveBezier::Init( int nDeg, bool bIsRational) { const int MINDIM = 4 ; // verifico validità grado if ( nDeg < 1 || nDeg > MAXDEG) return false ; // se spazio dinamico sufficiente if ( ( nDeg + 1) <= m_nDimArr) ; // se altrimenti spazio statico sufficiente else if ( ( nDeg + 1) <= ST_PTC) { m_aPtCtrl = m_aStPtCtrl ; if ( bIsRational) m_aWeCtrl = m_aStWeCtrl ; } // altrimenti else { // se necessaria, pulizia if ( m_nDimArr > 0) { delete [] m_aPtCtrl ; delete [] m_aWeCtrl ; } // alloco punti m_aPtCtrl = new Point3d [ nDeg + 1] ; if ( m_aPtCtrl == nullptr) return false ; // se razionale, alloco pesi if ( bIsRational) { m_aWeCtrl = new double [ nDeg + 1] ; if ( m_aWeCtrl == nullptr) { delete [] m_aPtCtrl ; return false ; } } // salvo dimensione array allocati m_nDimArr = nDeg + 1 ; } // salvo il grado m_nDeg = nDeg ; // salvo flag di razionale m_bRat = bIsRational ; // pulisco, assegnando 0 ai punti e 1 ai pesi memset( m_aPtCtrl, 0, sizeof( Point3d) * ( m_nDeg + 1)) ; if ( m_bRat) { for ( int i = 0 ; i <= m_nDeg ; ++ i) m_aWeCtrl[i] = 1 ; } m_nStatus = TO_VERIFY ; return Validate() ; } //---------------------------------------------------------------------------- bool CurveBezier::SetControlPoint( int nInd, const Point3d& ptCtrl) { // verifico validità indice if ( m_nStatus != OK || nInd < 0 || nInd > m_nDeg) return false ; // assegno il valore m_aPtCtrl[nInd] = ptCtrl ; // se razionale, metto il peso a 1 if ( m_bRat) m_aWeCtrl[nInd] = 1 ; return true ; } //---------------------------------------------------------------------------- bool CurveBezier::SetControlPoint( int nInd, const Point3d& ptCtrl, double dW) { // verifico validità, razionalità e indice if ( m_nStatus != OK || ! m_bRat || nInd < 0 || nInd > m_nDeg) return false ; // verifico che il peso non sia nullo o negativo if ( dW < EPS_SMALL) return false ; // assegno il valore e il peso m_aPtCtrl[nInd] = ptCtrl ; m_aWeCtrl[nInd] = dW ; return true ; } //---------------------------------------------------------------------------- const Point3d& CurveBezier::GetControlPoint( int nInd, bool* pbOk) const { // verifico validità e indice if ( m_nStatus != OK || nInd < 0 || nInd > m_nDeg) { if ( pbOk != NULL) *pbOk = false ; return ORIG ; } // ritorno i dati if ( pbOk != NULL) *pbOk = true ; return m_aPtCtrl[nInd] ; } //---------------------------------------------------------------------------- double CurveBezier::GetControlWeight( int nInd, bool* pbOk) const { // verifico validità, razionalità e indice if ( m_nStatus != OK || ! m_bRat || nInd < 0 || nInd > m_nDeg) { if ( pbOk != NULL) *pbOk = false ; return 0 ; } // ritorno i dati if ( pbOk != NULL) *pbOk = true ; return m_aWeCtrl[nInd] ; } //---------------------------------------------------------------------------- CurveBezier* CurveBezier::Clone( void) const { CurveBezier* pCrv ; // alloco oggetto pCrv = new(nothrow) CurveBezier ; if ( pCrv != nullptr) *pCrv = *(const_cast(this)) ; return pCrv ; } //---------------------------------------------------------------------------- const string& CurveBezier::GetKey( void) const { return GEOOBJ_GETKEY( CurveBezier) ; } //---------------------------------------------------------------------------- bool CurveBezier::Save( ostream& osOut) const { int i ; // flag razionale osOut << m_bRat ; // dati della curva di bezier osOut << ";" << ToString( m_nDeg) ; // ciclo sui punti di controllo ( con pesi se razionale) for ( i = 0 ; i <= m_nDeg ; ++ i) { osOut << ";" << ToString( m_aPtCtrl[i]) ; if ( m_bRat) { osOut << "," << ToString( m_aWeCtrl[i]) ; } } // terminazione osOut << ";" << endl ; return true ; } //---------------------------------------------------------------------------- bool CurveBezier::Load( CScan& TheScanner) { string sLine ; STRVECTOR vsParams ; bool bIsRat ; int nDeg ; Point3d ptP ; double dW ; int i ; // leggo la prossima linea if ( ! TheScanner.GetLine( sLine)) return false ; // la divido in parametri Tokenize( sLine, ";", vsParams) ; // almeno 2 parametri : flag razionale e grado if ( vsParams.size() < 2) return false ; // recupero il flag if ( ! FromString( vsParams[0], bIsRat)) return false ; // recupero il grado if ( ! FromString( vsParams[1], nDeg)) return false ; // ri-controllo il numero dei parametri if ( vsParams.size() != ( 2 + ( nDeg + 1))) return false ; // inizializzo la curva di Bezier if ( ! Init( nDeg, bIsRat)) return false ; // se integrale if ( ! bIsRat) { // recupero e setto punti di controllo for ( i = 0 ; i <= nDeg ; ++ i) { if ( ! FromString( vsParams[i+2], ptP) || ! SetControlPoint( i, ptP)) return false ; } } // altrimenti razionale else { // recupero e setto punti di controllo for ( i = 0 ; i <= nDeg ; ++ i) { if ( ! FromString( vsParams[i+2], ptP, dW) || ! SetControlPoint( i, ptP, dW)) return false ; } } // eseguo validazione return Validate() ; } //---------------------------------------------------------------------------- bool CurveBezier::Validate( void) { if ( m_nStatus == TO_VERIFY) m_nStatus = ( ( m_nDeg > 0 && m_aPtCtrl != nullptr) ? OK : ERR) ; return ( m_nStatus == OK) ; } //---------------------------------------------------------------------------- bool CurveBezier::GetStartPoint( Point3d& ptStart) const { // verifico lo stato if ( m_nStatus != OK) return false ; // assegno il punto ptStart = m_aPtCtrl[0] ; return true ; } //---------------------------------------------------------------------------- bool CurveBezier::GetEndPoint( Point3d& ptEnd) const { // verifico lo stato if ( m_nStatus != OK) return false ; // assegno il punto ptEnd = m_aPtCtrl[m_nDeg] ; return true ; } //---------------------------------------------------------------------------- bool CurveBezier::GetDomain( double& dStart, double& dEnd) const { // verifico lo stato if ( m_nStatus != OK) return false ; // assegno gli estremi del dominio dStart = 0 ; dEnd = 1 ; return true ; } //---------------------------------------------------------------------------- // Calcolo i polinomi di Bernstein e da questi il punto ( vedi Piegl-Tiller) //---------------------------------------------------------------------------- bool CurveBezier::GetPointD1D2( double dU, Point3d& ptPos, Vector3d& vtDer1, Vector3d& vtDer2) const { int i ; double dBern[MAXDEG] ; // la curva deve essere validata if ( m_nStatus != OK) return false ; // il parametro U deve essere compreso tra 0 e 1 if ( dU < 0) dU = 0 ; else if ( dU > 1) dU = 1 ; // se forma polinomiale (o integrale) if ( ! m_bRat) { // calcolo dei polinomi di Bernstein di grado opportuno dBern[0] = 1 ; for ( i = 1 ; i <= m_nDeg - 2 ; ++ i) IncreaseBernsteinOneDegree( dU, i, dBern) ; // calcolo della derivata seconda vtDer2.Set( 0, 0, 0) ; for ( i = 0 ; i <= m_nDeg - 2 ; ++ i) { vtDer2 = vtDer2 + dBern[i] * ( m_aPtCtrl[i+2] + m_aPtCtrl[i] - 2 * m_aPtCtrl[i+1]) ; } vtDer2 = m_nDeg * ( m_nDeg - 1) * vtDer2 ; // aumento il grado IncreaseBernsteinOneDegree( dU, m_nDeg - 1, dBern) ; // calcolo della derivata prima vtDer1.Set( 0, 0, 0) ; for ( i = 0 ; i <= m_nDeg - 1 ; ++ i) { vtDer1 = vtDer1 + dBern[i] * ( m_aPtCtrl[i+1] - m_aPtCtrl[i]) ; } vtDer1 = m_nDeg * vtDer1 ; // aumento il grado IncreaseBernsteinOneDegree( dU, m_nDeg, dBern) ; // calcolo del punto ptPos.Set( 0, 0, 0) ; for ( i = 0 ; i <= m_nDeg ; ++ i) ptPos = ptPos + dBern[i] * m_aPtCtrl[i] ; } // altrimenti forma razionale else { double dW ; double dW1 ; double dW2 ; double dInvW ; Point3d aPtWCtrl[MAXDEG] ; Vector3d vtPos ; // porto i punti in forma omogenea moltiplicandoli per i pesi for ( i = 0 ; i <= m_nDeg ; ++ i) aPtWCtrl[i] = m_aWeCtrl[i] * m_aPtCtrl[i] ; // calcolo dei polinomi di Bernstein di grado opportuno dBern[0] = 1 ; for ( i = 1 ; i <= m_nDeg - 2 ; ++ i) IncreaseBernsteinOneDegree( dU, i, dBern) ; // calcolo della derivata seconda vtDer2.Set( 0, 0, 0) ; dW2 = 0 ; for ( i = 0 ; i <= m_nDeg - 2 ; ++ i) { vtDer2 = vtDer2 + dBern[i] * ( aPtWCtrl[i+2] + aPtWCtrl[i] - 2 * aPtWCtrl[i+1]) ; dW2 = dW2 + dBern[i] * ( m_aWeCtrl[i+2] + m_aWeCtrl[i] - 2 * m_aWeCtrl[i+1]) ; } vtDer2 = m_nDeg * ( m_nDeg - 1) * vtDer2 ; dW2 = m_nDeg * ( m_nDeg - 1) * dW2 ; // aumento il grado IncreaseBernsteinOneDegree( dU, m_nDeg - 1, dBern) ; // calcolo della derivata prima vtDer1.Set( 0, 0, 0) ; dW1 = 0 ; for ( i = 0 ; i <= m_nDeg - 1 ; ++ i) { vtDer1 = vtDer1 + dBern[i] * ( aPtWCtrl[i+1] - aPtWCtrl[i]) ; dW1 = dW1 + dBern[i] * ( m_aWeCtrl[i+1] - m_aWeCtrl[i]) ; } vtDer1 = m_nDeg * vtDer1 ; dW1 = m_nDeg * dW1 ; // aumento il grado IncreaseBernsteinOneDegree( dU, m_nDeg, dBern) ; // calcolo del punto ptPos.Set( 0, 0, 0) ; dW = 0 ; for ( i = 0 ; i <= m_nDeg ; ++ i) { ptPos = ptPos + dBern[i] * aPtWCtrl[i] ; dW = dW + dBern[i] * m_aWeCtrl[i] ; } // ritrasformo da forma omogenea a forma standard dInvW = 1 / ( ( dW > EPS_ZERO) ? dW : EPS_ZERO) ; ptPos = ptPos * dInvW ; vtPos.Set( ptPos.x, ptPos.y, ptPos.z) ; vtDer1 = ( vtDer1 - dW1 * vtPos) * dInvW ; vtDer2 = ( vtDer2 - 2 * dW1 * vtDer1 - dW2 * vtPos) * dInvW ; } return true ; } //---------------------------------------------------------------------------- void CurveBezier::IncreaseBernsteinOneDegree( double dU, int nDeg, double dBern[]) const { int j ; double dU1 ; double dTot ; double dTmp ; dU1 = 1 - dU ; dTot = 0 ; for ( j = 0 ; j < nDeg ; ++ j) { dTmp = dBern[j] ; dBern[j] = dTot + dU1 * dTmp ; dTot = dU * dTmp ; } dBern[nDeg] = ( nDeg > 0 ? dTot : 1) ; } //---------------------------------------------------------------------------- bool CurveBezier::GetLength( double& dLen) const { const int NUM_SEG = 20 ; int i ; double dU ; Point3d ptIni ; Point3d ptFin ; // la curva deve essere validata if ( m_nStatus != OK) return false ; // approssimo la curva con un numero fisso di segmenti !!! DA MIGLIORARE !!! dLen = 0 ; for ( i = 0 ; i <= NUM_SEG ; ++ i) { // ricavo il punto dU = i / (double) NUM_SEG ; GetPoint( dU, ptFin) ; // dal secondo posso calcolare la lunghezza if ( i > 0) dLen += Dist( ptIni, ptFin) ; // nuovo iniziale prende i valori del finale ptIni = ptFin ; } return ( dLen > EPS_SMALL) ; } //---------------------------------------------------------------------------- bool CurveBezier::Reverse( void) { int i ; int nMid ; Point3d ptTemp ; // la curva deve essere validata if ( m_nStatus != OK) return false ; // inverto i punti di controllo nMid = ( m_nDeg + 1) / 2 ; for ( i = 0 ; i < nMid ; ++ i) { ptTemp = m_aPtCtrl[i] ; m_aPtCtrl[i] = m_aPtCtrl[m_nDeg-i] ; m_aPtCtrl[m_nDeg-i] = ptTemp ; } return true ; } //---------------------------------------------------------------------------- bool CurveBezier::Translate( const Vector3d& vtMove) { int i ; // la curva deve essere validata if ( m_nStatus != OK) return false ; // traslo i punti di controllo for ( i = 0 ; i <= m_nDeg ; ++ i) m_aPtCtrl[i].Translate( vtMove) ; return true ; } //---------------------------------------------------------------------------- bool CurveBezier::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, double dSinAng) { int i ; // la curva deve essere validata if ( m_nStatus != OK) return false ; // verifico validità dell'asse di rotazione if ( vtAx.IsSmall()) return false ; // ruoto i punti di controllo for ( i = 0 ; i <= m_nDeg ; ++ i) m_aPtCtrl[i].Rotate( ptAx, vtAx, dCosAng, dSinAng) ; return true ; } //---------------------------------------------------------------------------- bool CurveBezier::Scale( const Point3d& ptCen, double dCoeffX, double dCoeffY, double dCoeffZ) { int i ; // la curva deve essere validata if ( m_nStatus != OK) return false ; // verifico non sia nulla if ( fabs( dCoeffX) < EPS_ZERO && fabs( dCoeffY) < EPS_ZERO && fabs( dCoeffZ) < EPS_ZERO) return false ; // scalo i punti di controllo for ( i = 0 ; i <= m_nDeg ; ++ i) m_aPtCtrl[i].Scale( ptCen, dCoeffX, dCoeffY, dCoeffZ) ; return true ; } //---------------------------------------------------------------------------- bool CurveBezier::Mirror( const Point3d& ptOn, const Vector3d& vtNorm) { int i ; // la curva deve essere validata if ( m_nStatus != OK) return false ; // verifico validità del piano di specchiatura if ( vtNorm.IsSmall()) return false ; // specchio i punti di controllo for ( i = 0 ; i <= m_nDeg ; ++ i) m_aPtCtrl[i].Mirror( ptOn, vtNorm) ; return true ; } //---------------------------------------------------------------------------- bool CurveBezier::ToGlob( const Frame3d& frRef) { int i ; // la curva deve essere validata if ( m_nStatus != OK) return false ; // verifico validità del frame if ( frRef.GetType() == Frame3d::ERR) return false ; // trasformo i punti di controllo for ( i = 0 ; i <= m_nDeg ; ++ i) m_aPtCtrl[i].ToGlob( frRef) ; return true ; } //---------------------------------------------------------------------------- bool CurveBezier::ToLoc( const Frame3d& frRef) { int i ; // la curva deve essere validata if ( m_nStatus != OK) return false ; // verifico validità del frame if ( frRef.GetType() == Frame3d::ERR) return false ; // trasformo i punti di controllo for ( i = 0 ; i <= m_nDeg ; ++ i) m_aPtCtrl[i].ToLoc( frRef) ; return true ; }