EgtGeomKernel : notevoli migliorie al calcolo lunghezza delle curve di Bezier.

This commit is contained in:
Dario Sassi
2013-12-17 09:15:04 +00:00
parent 7de56883b3
commit 6287c113b4
2 changed files with 141 additions and 42 deletions
+137 -42
View File
@@ -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 ;
}
//----------------------------------------------------------------------------
+4
View File
@@ -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} ;