EgtGeomKernel :

- aggiunta funzione per l'approsimazione di curve con limitazione sulla lunghezza dei singoli tratti.
This commit is contained in:
Daniele Bariletti
2026-07-08 17:26:51 +02:00
parent f0137d26f4
commit 4d3b7ea6cf
11 changed files with 194 additions and 20 deletions
+31 -2
View File
@@ -41,7 +41,7 @@ GEOOBJ_REGISTER( CRV_ARC, NGE_C_ARC, CurveArc) ;
class ArcApproxer
{
public :
ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const CurveArc& arArc) ;
ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const CurveArc& arArc, double dMaxLen = INFINITO) ;
bool GetPoint( double& dU, Point3d& ptP) ;
private :
@@ -1177,6 +1177,30 @@ CurveArc::ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLin
return true ;
}
//----------------------------------------------------------------------------
bool
CurveArc::ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const
{
// pulisco la polilinea
PL.Clear() ;
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// eseguo approssimazione
bool bInside = true ;
ArcApproxer aAppr( dLinTol, dAngTolDeg, bInside, *this, dMaxLen) ;
double dU ;
Point3d ptPos ;
while ( aAppr.GetPoint( dU, ptPos)) {
if ( ! PL.AddUPoint( dU, ptPos))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveArc::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const
@@ -2170,7 +2194,7 @@ CurveArc::Flip( void)
// usando il versore medio dal centro e moltiplicandolo per il coefficiente ( 2 / ( 1 + cosA)).
// Il versore dell'ultimo punto è già stato calcolato per il penultimo.
//----------------------------------------------------------------------------
ArcApproxer::ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const CurveArc& arArc)
ArcApproxer::ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const CurveArc& arArc, double dMaxLen)
{
// inizializzazioni
m_nTotPnt = 0 ;
@@ -2195,6 +2219,11 @@ ArcApproxer::ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const
dAngStepDeg = sqrt( 8 * dLinTolRel) * RADTODEG ;
else
dAngStepDeg = sqrt( 8 * dLinTolRel / ( 1 + dLinTolRel)) * RADTODEG ;
if ( dMaxLen < INFINITO) {
double dAngStepMaxLen = 2 * asin( dMaxLen / ( 2 * arArc.GetRadius())) * RADTODEG ;
dAngStepDeg = min( dAngStepDeg, dAngStepMaxLen) ;
}
dAngStepDeg = min( dAngStepDeg, dAngTolDeg) ;
// dall'angolo al centro ricavo il numero di passi
+1
View File
@@ -111,6 +111,7 @@ class CurveArc : public ICurveArc, public IGeoObjRW
bool GetArea( Plane3d& plPlane, double& dArea) const override
{ return CurveGetArea( *this, plPlane, dArea) ; }
bool ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLine& PL) const override ;
bool ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const override ;
bool ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const override ;
bool ApproxWithArcsEx( double dLinTol, double dAngTolDeg, double dLinFea, PolyArc& PA) const override
{ return ApproxWithArcs( dLinTol, dAngTolDeg, PA) ; }
+54 -4
View File
@@ -1432,7 +1432,7 @@ CurveBezier::ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, Poly
//----------------------------------------------------------------------------
bool
CurveBezier::FlatOrSplit( int nLev, const CurveBezier& crvBez, double dParStart, double dParEnd,
double dLinTol, double dAngTolDeg, PolyLine& PL) const
double dLinTol, double dAngTolDeg, PolyLine& PL, double dMaxLen) const
{
// se raggiunto il massimo livello di recursione ...
const int MAX_LEV = 10 ;
@@ -1461,7 +1461,10 @@ CurveBezier::FlatOrSplit( int nLev, const CurveBezier& crvBez, double dParStart,
}
// se distanza entro tolleranza
if ( dMaxSqDist <= ( dLinTol * dLinTol)) {
bool bSplit = dMaxSqDist > ( dLinTol * dLinTol) ;
if ( dMaxLen < INFINITO)
bSplit = bSplit || Dist( crvBez.m_vPtCtrl[0], crvBez.m_vPtCtrl[m_nDeg]) > dMaxLen ;
if ( ! bSplit) {
// deviazione angolare tra primo e ultimo tratto del poligono di controllo (grado >= 1)
Vector3d vtDirI = crvBez.m_vPtCtrl[1] - crvBez.m_vPtCtrl[0] ;
Vector3d vtDirF = crvBez.m_vPtCtrl[m_nDeg] - crvBez.m_vPtCtrl[m_nDeg-1] ;
@@ -1495,18 +1498,65 @@ CurveBezier::FlatOrSplit( int nLev, const CurveBezier& crvBez, double dParStart,
// prima metà
crvBez1 = crvBez ;
crvBez1.TrimEndAtParam( dParDiv) ;
if ( ! FlatOrSplit( nLev + 1, crvBez1, dParStart, dParMid, dLinTol, dAngTolDeg, PL))
if ( ! FlatOrSplit( nLev + 1, crvBez1, dParStart, dParMid, dLinTol, dAngTolDeg, PL, dMaxLen))
return false ;
// seconda metà
crvBez1 = crvBez ;
crvBez1.TrimStartAtParam( dParDiv) ;
if ( ! FlatOrSplit( nLev + 1, crvBez1, dParMid, dParEnd, dLinTol, dAngTolDeg, PL))
if ( ! FlatOrSplit( nLev + 1, crvBez1, dParMid, dParEnd, dLinTol, dAngTolDeg, PL, dMaxLen))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveBezier::ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const
{
// pulisco la polilinea
PL.Clear() ;
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// se di primo grado, basta inserire gli estremi
if ( m_nDeg == 1) {
PL.AddUPoint( 0, m_vPtCtrl[0]) ;
PL.AddUPoint( 1, m_vPtCtrl[m_nDeg]) ;
PL.AdjustForMaxSegmentLen( dMaxLen) ;
return true ;
}
// limiti minimi su tolleranza e deviazione angolare
dLinTol = max( dLinTol, LIN_TOL_MIN) ;
dAngTolDeg = max( dAngTolDeg, ANG_TOL_MIN_DEG) ;
// inserisco il punto iniziale
if ( ! PL.AddUPoint( 0, m_vPtCtrl[0]))
return false ;
// verifico se va divisa
if ( ! FlatOrSplit( 0, *this, 0, 1, dLinTol, dAngTolDeg, PL, dMaxLen))
return false ;
// se è stato inserito un solo punto, aggiungo il finale
if ( PL.GetPointNbr() == 1)
return PL.AddUPoint( 1, m_vPtCtrl[m_nDeg]) ;
// altrimenti, se l'ultimo punto non coincide con il finale lo sostituisco (distano al max di dLinTol)
else {
Point3d ptLast ;
PL.GetLastPoint( ptLast) ;
if ( ! AreSamePointApprox( ptLast, m_vPtCtrl[m_nDeg])) {
PL.EraseLastUPoint() ;
return PL.AddUPoint( 1, m_vPtCtrl[m_nDeg]) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveBezier::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const
+2 -1
View File
@@ -111,6 +111,7 @@ class CurveBezier : public ICurveBezier, public IGeoObjRW
bool GetArea( Plane3d& plPlane, double& dArea) const override
{ return CurveGetArea( *this, plPlane, dArea) ; }
bool ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLine& PL) const override ;
bool ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const override ;
bool ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const override ;
bool ApproxWithArcsEx( double dLinTol, double dAngTolDeg, double dLinFea, PolyArc& PA) const override
{ return ApproxWithArcs( dLinTol, dAngTolDeg, PA) ; }
@@ -188,7 +189,7 @@ class CurveBezier : public ICurveBezier, public IGeoObjRW
bool GetSegmentParam( double dLen, double& dCurrLen, double& dSegLen,
double& dUIni, double& dUFin) const ;
bool FlatOrSplit( int nLev, const CurveBezier& crvBez, double dParStart, double dParEnd,
double dLinTol, double dAngTolDeg, PolyLine& PL) const ;
double dLinTol, double dAngTolDeg, PolyLine& PL, double dMaxLen = INFINITO) const ;
bool ApproxWithArcsXY(double dLinTol, double dAngTolDeg, PolyArc& PA) const;
bool BiArcOrSplit(int nLev, PolyLine& PL, double dLinTol, double dAngTolDeg, PolyArc& PA) const;
bool ToPowerBase( PolynomialPoint3d& pol3P) const ;
+62
View File
@@ -1460,6 +1460,68 @@ CurveComposite::ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, P
return PL.RemoveAlignedPoints( dLinTol) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const
{
// funziona solo con le modalità standard e special
if ( nType != APL_STD && nType != APL_SPECIAL && nType != APL_SPECIAL_INT)
return false ;
// pulisco la polilinea
PL.Clear() ;
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// limiti minimi su tolleranza e deviazione angolare
dLinTol = max( dLinTol, LIN_TOL_MIN) ;
dAngTolDeg = max( dAngTolDeg, ANG_TOL_MIN_DEG) ;
// se speciale, approssimo ogni singola entità e conservo le estremità interne (joint)
if ( nType == APL_SPECIAL || nType == APL_SPECIAL_INT) {
// eseguo approssimazione
double dStartPar = 0 ;
for ( auto& pCrv : m_CrvSmplS) {
// assegno estrusione e spessore della curva composita
pCrv->SetExtrusion( m_VtExtr) ;
pCrv->SetThickness( m_dThick) ;
// recupero approssimazione per curva semplice
PolyLine PLSmpl ;
if ( ! pCrv->ApproxWithLimitedLines( dLinTol, dAngTolDeg, nType, dMaxLen, PLSmpl))
return false ;
// se richiesto almeno un punto interno con curve non rettilinee e ci sono solo gli estremi
if ( nType == APL_SPECIAL_INT && pCrv->GetType() != CRV_LINE && PLSmpl.GetPointNbr() == 2) {
// aggiungo il punto interno
Point3d ptMid ;
if ( ! pCrv->GetMidPoint( ptMid))
return false ;
double dU ;
PLSmpl.GetLastU( dU) ;
dU /= 2 ;
PNTULIST& List = PLSmpl.GetUPointList() ;
List.insert( ++ List.begin(), { ptMid, dU}) ;
}
// ripristino estrusione e spessore della curva semplice (annullandoli)
pCrv->SetExtrusion( V_NULL) ;
pCrv->SetThickness( 0) ;
// la accodo opportunamente a quella della curva composita
if ( ! PL.Join( PLSmpl, dStartPar))
return false ;
// incremento inizio parametro per prossima curva semplice
dStartPar += 1 ;
}
}
else if ( nType == APL_STD) {
if ( ! ApproxWithLimitedLines( dLinTol, dAngTolDeg, APL_SPECIAL, dMaxLen, PL))
return false ;
PL.RemoveAlignedPoints( dLinTol, false, dMaxLen) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const
+1
View File
@@ -112,6 +112,7 @@ class CurveComposite : public ICurveComposite, public IGeoObjRW
bool GetArea( Plane3d& plPlane, double& dArea) const override
{ return CurveGetArea( *this, plPlane, dArea) ; }
bool ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLine& PL) const override ;
bool ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const override ;
bool ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const override ;
bool ApproxWithArcsEx( double dLinTol, double dAngTolDeg, double dLinFea, PolyArc& PA) const override ;
ICurve* CopyParamRange( double dUStart, double dUEnd) const override ;
+28
View File
@@ -514,6 +514,34 @@ CurveLine::ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLi
return true ;
}
//----------------------------------------------------------------------------
bool
CurveLine::ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const
{
// pulisco la polilinea
PL.Clear() ;
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// inserisco lo start
PL.AddUPoint( 0, m_PtStart) ;
double dLen = Dist( m_PtStart, m_PtEnd) ;
if ( dLen > dMaxLen) {
int nStep = int( ceil( dLen / dMaxLen)) ;
for ( int i = 1 ; i < nStep ; ++i) {
double dPar = 1. / nStep * i ;
PL.AddUPoint( dPar, Media( m_PtStart, m_PtEnd, dPar)) ;
}
}
// inserisco l'end
PL.AddUPoint( 1, m_PtEnd) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveLine::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const
+1
View File
@@ -112,6 +112,7 @@ class CurveLine : public ICurveLine, public IGeoObjRW
bool GetArea( Plane3d& plPlane, double& dArea) const override
{ return false ; }
bool ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLine& PL) const override ;
bool ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const override ;
bool ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const override ;
bool ApproxWithArcsEx( double dLinTol, double dAngTolDeg, double dLinFea, PolyArc& PA) const override
{ return ApproxWithArcs( dLinTol, dAngTolDeg, PA) ; }
+1 -1
View File
@@ -446,7 +446,7 @@ IsPointInsideCylinder( const Point3d& ptTest, const Cyl& offCyl, double dLinTol)
bool
CalcAdjustConcavePartsInPath( const ICurveComposite* pCrv, const OFFSETSEGVEC& vOffsetCrvs, double dRad, EDITCRVINFOVEC& vEditInfo)
{
const double dLinTol = 0 * EPS_SMALL ;
const double dLinTol = 2 * EPS_SMALL ;
for ( int i = 0 ; i < ssize( vOffsetCrvs) ; ++i) {
int nFlag = vOffsetCrvs[i].nFlag ;
// considero tutt le zone concave
+12 -9
View File
@@ -737,7 +737,7 @@ PolyLine::AdjustForMaxSegmentLen( double dMaxLen)
//-----------------------------------------------------------------------------
static bool
DouglasPeuckerSimplification( const PNTUVECTOR& vPtU, const double dSqTol, const int nIndStart,
const int nIndEnd, INTVECTOR& vInd)
const int nIndEnd, INTVECTOR& vInd, double dMaxLen)
{
// se indici uguali, ritorno
if ( nIndStart == nIndEnd)
@@ -745,7 +745,7 @@ DouglasPeuckerSimplification( const PNTUVECTOR& vPtU, const double dSqTol, const
// distanza massima e indice del punto associato
double dMaxSqDist = 0. ;
int nMaxInd = 0 ;
int nMaxInd = nIndStart + 1 ;
// scorro i punti intermedi tra nIndStart e nIndEnd
for ( int i = nIndStart + 1 ; i < nIndEnd ; ++ i) {
@@ -761,12 +761,15 @@ DouglasPeuckerSimplification( const PNTUVECTOR& vPtU, const double dSqTol, const
// se la distanza massima trovata è sopra la tolleranza, allora controllo la parte di PolyLine tra
// (nIndStart, nMaxInd) e quella tra (nMaxInd, nIndEnd)
if ( dMaxSqDist > dSqTol) {
bool bSplit = dMaxSqDist > dSqTol ;
if ( dMaxLen < INFINITO)
bSplit = bSplit || ( nIndEnd - nIndStart > 1 && Dist( vPtU[nIndStart].first, vPtU[nIndEnd].first) > dMaxLen) ;
if ( bSplit) {
// inserisco il punto
vInd.push_back( nMaxInd) ;
// split
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, nIndStart, nMaxInd, vInd) ||
! DouglasPeuckerSimplification( vPtU, dSqTol, nMaxInd, nIndEnd, vInd))
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, nIndStart, nMaxInd, vInd, dMaxLen) ||
! DouglasPeuckerSimplification( vPtU, dSqTol, nMaxInd, nIndEnd, vInd, dMaxLen))
return false ;
}
@@ -775,7 +778,7 @@ DouglasPeuckerSimplification( const PNTUVECTOR& vPtU, const double dSqTol, const
//----------------------------------------------------------------------------
bool
PolyLine::RemoveAlignedPoints( double dToler, bool bStartEnd)
PolyLine::RemoveAlignedPoints( double dToler, bool bStartEnd, double dMaxLen)
{
// se non ci sono almeno 3 punti, esco subito
if ( m_lUPoints.size() < 3)
@@ -796,7 +799,7 @@ PolyLine::RemoveAlignedPoints( double dToler, bool bStartEnd)
if ( ! IsClosed()) {
// considero tutti i punti della PolyLine
vInd.push_back( 0) ;
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, 0, int( vPtU.size()) - 1, vInd))
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, 0, int( vPtU.size()) - 1, vInd, dMaxLen))
return false ;
vInd.push_back( int( vPtU.size()) - 1) ;
}
@@ -814,10 +817,10 @@ PolyLine::RemoveAlignedPoints( double dToler, bool bStartEnd)
}
// recupero due PolyLine di approssimazione
vInd.push_back( 0) ;
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, 0, nMaxInd, vInd))
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, 0, nMaxInd, vInd, dMaxLen))
return false ;
vInd.push_back( nMaxInd) ;
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, nMaxInd, int( vPtU.size()) - 1, vInd))
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, nMaxInd, int( vPtU.size()) - 1, vInd, dMaxLen))
return false ;
vInd.push_back( int( vPtU.size()) - 1) ;
}
+1 -3
View File
@@ -1295,10 +1295,8 @@ GetCurveOnSurfInfo( const ICurve& crCrv, const ISurfTriMesh& pSurf,
// approssimo la curva con una polilinea entro la tolleranza
PolyLine PL ;
if ( ! crCrv.ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL))
return false ;
const double MAX_SEG_LEN = min( dMaxSegmLen, 0.977) ;
if ( ! PL.AdjustForMaxSegmentLen( MAX_SEG_LEN))
if ( ! crCrv.ApproxWithLimitedLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, MAX_SEG_LEN, PL))
return false ;
// Pulisco e riservo spazio nel vettore dei punti risultanti