From 4d3b7ea6cf7d99721bbc9c836fececa89fc64025 Mon Sep 17 00:00:00 2001 From: Daniele Bariletti Date: Wed, 8 Jul 2026 17:26:51 +0200 Subject: [PATCH] EgtGeomKernel : - aggiunta funzione per l'approsimazione di curve con limitazione sulla lunghezza dei singoli tratti. --- CurveArc.cpp | 33 +++++++++++++++++++++-- CurveArc.h | 1 + CurveBezier.cpp | 58 ++++++++++++++++++++++++++++++++++++++--- CurveBezier.h | 3 ++- CurveComposite.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++ CurveComposite.h | 1 + CurveLine.cpp | 28 ++++++++++++++++++++ CurveLine.h | 1 + OffsetCurve3d.cpp | 2 +- PolyLine.cpp | 21 ++++++++------- ProjectCurveSurf.cpp | 4 +-- 11 files changed, 194 insertions(+), 20 deletions(-) diff --git a/CurveArc.cpp b/CurveArc.cpp index 30e130c..3199431 100644 --- a/CurveArc.cpp +++ b/CurveArc.cpp @@ -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 diff --git a/CurveArc.h b/CurveArc.h index 10ee53f..a060c7e 100644 --- a/CurveArc.h +++ b/CurveArc.h @@ -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) ; } diff --git a/CurveBezier.cpp b/CurveBezier.cpp index 5be2d71..e543c6e 100644 --- a/CurveBezier.cpp +++ b/CurveBezier.cpp @@ -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 diff --git a/CurveBezier.h b/CurveBezier.h index b91d282..54d6651 100644 --- a/CurveBezier.h +++ b/CurveBezier.h @@ -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 ; diff --git a/CurveComposite.cpp b/CurveComposite.cpp index 74982f5..d063560 100644 --- a/CurveComposite.cpp +++ b/CurveComposite.cpp @@ -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 diff --git a/CurveComposite.h b/CurveComposite.h index d81a08b..6d0f3db 100644 --- a/CurveComposite.h +++ b/CurveComposite.h @@ -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 ; diff --git a/CurveLine.cpp b/CurveLine.cpp index 0731bbe..9f7581b 100644 --- a/CurveLine.cpp +++ b/CurveLine.cpp @@ -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 diff --git a/CurveLine.h b/CurveLine.h index 6f224da..ac152e9 100644 --- a/CurveLine.h +++ b/CurveLine.h @@ -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) ; } diff --git a/OffsetCurve3d.cpp b/OffsetCurve3d.cpp index ccee0c2..e9a5cb4 100644 --- a/OffsetCurve3d.cpp +++ b/OffsetCurve3d.cpp @@ -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 diff --git a/PolyLine.cpp b/PolyLine.cpp index a2629d9..dff395c 100644 --- a/PolyLine.cpp +++ b/PolyLine.cpp @@ -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) ; } diff --git a/ProjectCurveSurf.cpp b/ProjectCurveSurf.cpp index a57a928..3b2edeb 100644 --- a/ProjectCurveSurf.cpp +++ b/ProjectCurveSurf.cpp @@ -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