From 6287c113b428087794b75544d2f0072dfa8a1832 Mon Sep 17 00:00:00 2001 From: Dario Sassi Date: Tue, 17 Dec 2013 09:15:04 +0000 Subject: [PATCH] EgtGeomKernel : notevoli migliorie al calcolo lunghezza delle curve di Bezier. --- CurveBezier.cpp | 179 ++++++++++++++++++++++++++++++++++++------------ CurveBezier.h | 4 ++ 2 files changed, 141 insertions(+), 42 deletions(-) diff --git a/CurveBezier.cpp b/CurveBezier.cpp index 572218b..8c9c4e8 100644 --- a/CurveBezier.cpp +++ b/CurveBezier.cpp @@ -507,15 +507,70 @@ CurveBezier::GetLength( double& dLen) const return ( GetLengthAtParam( 1, dLen) && dLen > EPS_SMALL) ; } +//---------------------------------------------------------------------------- +double +CurveBezier::GetSegmentLength( int nLev, double dU0, double dU1, double dU2, + const Point3d& ptP0, const Point3d& ptP1, const Point3d& ptP2) const +{ + const int MAX_LEV = 5 ; + const double MAX_ARC = 1.05 ; + const double LEN_RATIO = 1.2 ; + double dD1 ; + double dDa ; + double dDb ; + double dD2 ; + + + // algoritmo dell'approssimazione con arco per tre punti di S. Vincent e D. Forsey + + // calcolo delle distanze + dD1 = Dist( ptP0, ptP2) ; + dDa = Dist( ptP0, ptP1) ; + dDb = Dist( ptP1, ptP2) ; + dD2 = dDa + dDb ; + + // distanza piccola o massimo livello di recursione, approx arco ok + if ( dD2 < EPS_SMALL || nLev == MAX_LEV) + return ( dD2 + ( dD2 - dD1) / 3) ; + + // se ci sono anomalie si suddivide + if ( dD1 < EPS_SMALL || dD2 / dD1 > MAX_ARC || + dDa < 0.1 * EPS_SMALL || dDb / dDa > LEN_RATIO || + dDb < 0.1 * EPS_SMALL || dDa / dDb > LEN_RATIO) { + + double dUMid ; + Point3d ptMid ; + + // prima metà + dUMid = 0.5 * ( dU0 + dU1) ; + GetPoint( dUMid, ptMid) ; + double dD3 = GetSegmentLength( nLev +1, dU0, dUMid, dU1, + ptP0, ptMid, ptP1) ; + // seconda metà + dUMid = 0.5 * ( dU1 + dU2) ; + GetPoint( dUMid, ptMid) ; + double dD4 = GetSegmentLength( nLev + 1, dU1, dUMid, dU2, + ptP1, ptMid, ptP2) ; + + // la lunghezza è la somma delle due + return ( dD3 + dD4) ; + } + + // caso normale + return ( dD2 + ( dD2 - dD1) / 3) ; +} + //---------------------------------------------------------------------------- bool CurveBezier::GetLengthAtParam( double dU, double& dLen) const { - const int NUM_SEG = 50 ; + const int NUM_SEG = 16 ; int i ; - double dPar ; - double dSegLen ; + double dUIni ; + double dUMid ; + double dUFin ; Point3d ptIni ; + Point3d ptMid ; Point3d ptFin ; @@ -535,39 +590,88 @@ CurveBezier::GetLengthAtParam( double dU, double& dLen) const return true ; } - // approssimo la curva con un numero fisso di segmenti !!! DA MIGLIORARE !!! + // parto con un numero fisso di punti e poi affino dove serve dLen = 0 ; - for ( i = 0 ; i <= NUM_SEG ; ++ i) { + dUIni = PAR_START ; + GetPoint( PAR_START, ptIni) ; + for ( i = 1 ; i <= NUM_SEG ; ++ i) { // ricavo il punto - dPar = i / (double) NUM_SEG ; - GetPoint( dPar, ptFin) ; - // dal secondo posso calcolare la lunghezza - if ( i > 0) { - dSegLen = Dist( ptIni, ptFin) ; - dLen += dSegLen ; - if ( dPar >= dU) { - dLen += ( dU - dPar) * NUM_SEG * dSegLen ; - break ; - } + dUFin = i / (double) NUM_SEG * dU ; + GetPoint( dUFin, ptFin) ; + // se punto dispari + if ( ( i % 2) == 1) { + // assegno nuovo medio + dUMid = dUFin ; + ptMid = ptFin ; + } + // se punto pari, calcolo la lunghezza + else { + dLen += GetSegmentLength( 0, dUIni, dUMid, dUFin, ptIni, ptMid, ptFin) ; + // nuovo iniziale prende i valori del finale + dUIni = dUFin ; + ptIni = ptFin ; } - // nuovo iniziale prende i valori del finale - ptIni = ptFin ; } return true ; } +//---------------------------------------------------------------------------- +bool +CurveBezier::GetSegmentParam( double dLen, double& dCurrLen, double& dSegLen, + double& dUIni, double& dUFin) const +{ + const int NUM_SEG = 16 ; + int i ; + double dUStart ; + double dUEnd ; + double dUMid ; + Point3d ptIni ; + Point3d ptMid ; + Point3d ptFin ; + + + dUStart = dUIni ; + dUEnd = dUFin ; + GetPoint( dUIni, ptIni) ; + for ( i = 1 ; i <= NUM_SEG ; ++ i) { + // ricavo il punto + dUFin = dUStart + i / (double) NUM_SEG * ( dUEnd - dUStart) ; + GetPoint( dUFin, ptFin) ; + // se punto dispari + if ( ( i % 2) == 1) { + // assegno nuovo medio + dUMid = dUFin ; + ptMid = ptFin ; + } + // se punto pari, calcolo la lunghezza + else { + dSegLen = GetSegmentLength( 0, dUIni, dUMid, dUFin, ptIni, ptMid, ptFin) ; + if ( ( dCurrLen + dSegLen) >= dLen) { + return true ; + } + else + dCurrLen += dSegLen ; + // nuovo iniziale prende i valori del finale + dUIni = dUFin ; + ptIni = ptFin ; + } + } + + return false ; +} + //---------------------------------------------------------------------------- bool CurveBezier::GetParamAtLength( double dLen, double& dU) const { - const int NUM_SEG = 50 ; - int i ; - double dPar ; - double dSegLen ; - double dCurrLen ; - Point3d ptIni ; - Point3d ptFin ; + const int NUM_SEG = 16 ; + const double MAX_SEG_LEN = 0.2 ; + bool bOk ; + double dCurrLen ; + double dSegLen ; + double dUIni ; + double dUFin ; // la curva deve essere validata @@ -580,26 +684,17 @@ CurveBezier::GetParamAtLength( double dLen, double& dU) const return true ; } - // approssimo la curva con un numero fisso di segmenti !!! DA MIGLIORARE !!! + // approfondisco nell'intervallo diinteresse dCurrLen = 0 ; - for ( i = 0 ; i <= NUM_SEG ; ++ i) { - // ricavo il punto - dPar = i / (double) NUM_SEG ; - GetPoint( dPar, ptFin) ; - // dal secondo posso calcolare la lunghezza - if ( i > 0) { - dSegLen = Dist( ptIni, ptFin) ; - dCurrLen += dSegLen ; - if ( dCurrLen >= dLen) { - dU = dPar - ( dCurrLen - dLen) / ( dSegLen * NUM_SEG) ; - break ; - } - } - // nuovo iniziale prende i valori del finale - ptIni = ptFin ; - } + dUIni = PAR_START ; + dUFin = PAR_END ; + while ( ( bOk = GetSegmentParam( dLen, dCurrLen, dSegLen, dUIni, dUFin)) && dSegLen > MAX_SEG_LEN) + ; + // aggiustamento finale parametro + if ( bOk) + dU = dUIni + ( dLen - dCurrLen) / dSegLen * ( dUFin - dUIni) ; - return true ; + return bOk ; } //---------------------------------------------------------------------------- diff --git a/CurveBezier.h b/CurveBezier.h index 2309106..ffae48a 100644 --- a/CurveBezier.h +++ b/CurveBezier.h @@ -86,6 +86,10 @@ class CurveBezier : public ICurveBezier private : bool Validate( void) ; void IncreaseBernsteinOneDegree( double dU, int nDeg, double dBern[]) const ; + double GetSegmentLength( int nLev, double dU0, double dU1, double dU2, + const Point3d& ptP0, const Point3d& ptP1, const Point3d& ptP2) const ; + bool GetSegmentParam( double dLen, double& dCurrLen, double& dSegLen, + double& dUIni, double& dUFin) const ; private : enum Status { ERR = 0, OK = 1, TO_VERIFY = 2} ;