From 1cfd283f26910fb7229eadc37231c17b571bdd91 Mon Sep 17 00:00:00 2001 From: Daniele Bariletti Date: Thu, 11 Jun 2026 18:05:15 +0200 Subject: [PATCH 1/2] EgtGeomKernel : - prima versione dell'offset 3d (da correggere). --- EgtGeomKernel.vcxproj | 1 + EgtGeomKernel.vcxproj.filters | 3 + OffsetCurve3d.cpp | 464 ++++++++++++++++++++++++++++++++++ 3 files changed, 468 insertions(+) create mode 100644 OffsetCurve3d.cpp diff --git a/EgtGeomKernel.vcxproj b/EgtGeomKernel.vcxproj index d1aee7b..b8aaaef 100644 --- a/EgtGeomKernel.vcxproj +++ b/EgtGeomKernel.vcxproj @@ -324,6 +324,7 @@ copy $(TargetPath) \EgtProg\Dll64 + diff --git a/EgtGeomKernel.vcxproj.filters b/EgtGeomKernel.vcxproj.filters index 03239a9..420d7be 100644 --- a/EgtGeomKernel.vcxproj.filters +++ b/EgtGeomKernel.vcxproj.filters @@ -576,6 +576,9 @@ File di origine\GeoStriping + + File di origine\GeoOffset + diff --git a/OffsetCurve3d.cpp b/OffsetCurve3d.cpp new file mode 100644 index 0000000..eb277c4 --- /dev/null +++ b/OffsetCurve3d.cpp @@ -0,0 +1,464 @@ +//---------------------------------------------------------------------------- +// EgalTech 2026 +//---------------------------------------------------------------------------- +// File : OffsetCurve3d.cpp Data : 10.06.26 Versione : 3.1f1 +// Contenuto : Classe per offset di Curve 3d. +// +// +// +// Modifiche : 10.06.26 DB Creazione modulo. +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "GeoConst.h" +#include "CurveLine.h" +#include "CurveComposite.h" +#include "RemoveCurveDefects.h" +#include "IntersLineCyl.h" +#include "/EgtDev/Include/EGkPoint3d.h" +#include "/EgtDev/Include/EGkPolyLine.h" +#include "/EgtDev/Include/EGkOffsetCurve3d.h" +#include "/EgtDev/Include/EgtPointerOwner.h" +#include + +using namespace std ; + +#define SAVECVRORIG 1 +#define SAVEOFFDIR 1 +#define SAVECYL 1 +#if SAVECVRORIG || SAVEOFFDIR || SAVECYL +#include "/EgtDev/Include/EGkColor.h" +#include "/EgtDev/Include/EGkGeoVector3d.h" +vector vGeo ; +vector vCol ; +#include "/EgtDev/Include/EGkGeoObjSave.h" +#endif + +//---------------------------------------------------------------------------- +bool AdjustConcavePartsInPath( const ICurveComposite* pCrv, ICURVEPOVECTOR& vOffsetCrvs, const INTVECTOR& vFlag, double dRad) ; + +//---------------------------------------------------------------------------- +OffsetCurve3d::~OffsetCurve3d( void) +{ + Reset() ; +} + +//---------------------------------------------------------------------------- +bool +OffsetCurve3d::Reset( void) +{ + for ( auto& pCrv : m_CrvLst) { + if ( pCrv != nullptr) { + delete pCrv ; + pCrv = nullptr ; + } + } + m_CrvLst.clear() ; + return true ; +} + +//---------------------------------------------------------------------------- +bool +OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOffDist, int nType) +{ + // pulisco tutto + Reset() ; + PtrOwner pCrv( CreateBasicCurveComposite()) ; + if ( ! pCrv->FromPolyLine( PL)) + return false ; + +#if SAVECVRORIG + vGeo.push_back( pCrv->Clone()) ; + vCol.push_back( AQUA) ; + Point3d ptBase ; PL.GetFirstPoint( ptBase) ; + for ( int i = 0 ; i < ssize( vOffDir) ; ++i) { + IGeoVector3d* pVec = CreateGeoVector3d() ; + pVec->Set( vOffDir[i] * dOffDist, ptBase) ; + PL.GetNextPoint( ptBase) ; + vGeo.push_back( pVec) ; + vCol.push_back( BLUE) ; + } +#endif + + // verifico se la curva è un segmento di retta + bool bIsLine = PL.GetPointNbr() == 2 ; + if ( bIsLine) { + // faccio l'offset di una linea + return true ; + } + + // se offset nullo, copio la curva ed esco + if ( abs( dOffDist) < 10 * EPS_SMALL) { + PtrOwner pCopy( CreateBasicCurveComposite()) ; + if ( IsNull( pCopy) || ! pCopy->CopyFrom( pCrv)) + return false ; + // unisco parti allineate (tranne gli estremi) + pCopy->MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG, false) ; + // sposto in lista + m_CrvLst.push_back( Release( pCopy)) ; + return true ; + } + + // elimino tratti molto corti + if ( ! RemoveCurveSmallParts( pCrv, m_dLinTol)) + return false ; + + bool bClosed = pCrv->IsClosed() ; + + INTVECTOR vFlag ; + vFlag.push_back( OffsetCurve3d::AngType::ANG_STR) ; + const ICurve* pSubCrv = pCrv->GetFirstCurve() ; + for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++i) { + pSubCrv = pCrv->GetNextCurve() ; + Vector3d vtDirCurr ; pSubCrv->GetStartDir( vtDirCurr) ; + int nFlag = vtDirCurr * vOffDir[i] > 0 ? OffsetCurve3d::AngType::ANG_SMOOTH_CONC : OffsetCurve3d::AngType::ANG_STR ; + vFlag.push_back( nFlag) ; + } + + double dRadCorr ; + Point3d ptPrev ; pCrv->GetStartPoint( ptPrev) ; + ptPrev += vOffDir[0] * dOffDist ; + Vector3d vtNormPrev ; + Vector3d vtCorrPrev ; + Vector3d vtTangPrev ; + Vector3d vtDirPrev ; pCrv->GetStartDir( vtDirPrev) ; + const ICurve* pCrvPrev = pCrv->GetFirstCurve() ; + const ICurve* pCrvCurr ; + vector> vOffsetCrvs ; + for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++i) { + pCrvCurr = pCrv->GetNextCurve() ; + Vector3d vtOffDir = vOffDir[i] ; + Vector3d vtDirCurr ; pCrvCurr->GetStartDir( vtDirCurr) ; + pCrvPrev->GetStartDir( vtDirPrev) ; + Vector3d vtTang = Media( vtDirCurr, vtDirPrev) ; + vtTang.Normalize() ; + Vector3d vtNorm = vtOffDir ; + vtNorm.Rotate( vtTang, -90) ; + //Vector3d vtCorr = vtTang ^ vtNorm ; vtCorr.Normalize() ; + // devo invertire vtCorr??? se sì, quando?///////////////////////////////////////// + Vector3d vtCorr = vtOffDir ; + double dCorrK = 1 ; + if ( vFlag[i] == OffsetCurve3d::AngType::ANG_CONC) { + double dHalfAlfa = acos( vtTang * vtTangPrev) ; + dCorrK = 1 / sin( 90 - dHalfAlfa) ; + } + + Point3d ptP ; pCrvCurr->GetStartPoint( ptP) ; + dRadCorr = dOffDist ; + ptP = ptP + dRadCorr * dCorrK * vtCorr ; + // se secondo punto di angolo esterno di fianco, inserisco movimenti intermedi + if ( vFlag[i] == OffsetCurve3d::AngType::ANG_CVEX && vFlag[i-1] == OffsetCurve3d::AngType::ANG_CVEX) { + double dAlfa = acos( vtTang * vtTangPrev) ; + double dDelta = dOffDist * tan( dAlfa / 4) ; + Point3d ptAdd1 = ptPrev + dDelta * vtTangPrev ; + ICurveLine* pCL1 = CreateBasicCurveLine() ; + pCL1->Set( ptPrev, ptAdd1) ; + vOffsetCrvs.emplace_back( pCL1) ; + Point3d ptAdd2 = ptP - dDelta * vtTang ; + ICurveLine* pCL2 = CreateBasicCurveLine() ; + pCL2->Set( ptAdd1, ptAdd2) ; + vOffsetCrvs.emplace_back( pCL2) ; + ptPrev = ptAdd2 ; + } + + //// se punto di angolo interno di fianco, elimino eventuali movimenti precedenti invertiti + //if ( vFlag[i] == OffsetCurve3d::AngType::ANG_CVEX == 3) { + // local nLastId = EgtGetLastInGroup( nClPathId) + // while nLastId do + // local vtMlast = ptP - EgtEP( nLastId, GDB_ID.ROOT) ; vtMlast:normalize() + // if vtMlast * vtGpre < 0.5 then + // ptPpre = EgtSP( nLastId, GDB_ID.ROOT) + // EgtErase( nLastId) + // else + // break + // end + // nLastId = EgtGetLastInGroup( nClPathId) + // end + //} + + //// se appena dopo angolo interno di fianco, verifico se da aggiungere + //bool bToAdd = true + //if ( nFlpre == 3 and abs( dSideAng) > GEO.EPS_ANG_SMALL) { + // local vtMove = ptP - ptPpre ; vtMove:normalize() + // if vtMove * vtTang < 0.5 then + // bToAdd = false + // end + //} + + // aggiungo tratto + ICurveLine* pCL = CreateBasicCurveLine() ; + pCL->Set( ptPrev, ptP) ; + vOffsetCrvs.emplace_back( pCL) ; + // aggiorno punto precedente + ptPrev = ptP ; + vtNormPrev = vtNorm ; + vtCorrPrev = vtCorr ; + vtTangPrev = vtTang ; + vtDirPrev = vtDirCurr ; + } + + //// qui faccio la correzione per gli angoli interni + //AdjustConcavePartsInPath( pCrv, vOffsetCrvs, vFlag, dOffDist) ; + +#if SAVECVRORIG || SAVEOFFDIR || SAVECYL + SaveGeoObj( vGeo, vCol, "C:\\Temp\\curve offset 3d\\crvoffset.nge") ; +#endif + + PtrOwner pCrvOffset( CreateBasicCurveComposite()) ; + for ( int i = 0 ; i < ssize( vOffsetCrvs) ; ++i) { + if ( ! pCrvOffset->AddCurve( Release( vOffsetCrvs[i]))) + return false ; + } + m_CrvLst.push_back( Release( pCrvOffset)) ; + + return true ; + + + // raccordi + + // angoli interni + + // angoli esterni + + // auto intersezioni + + //// sesto passo : se curva aperta, elimino i tratti che stanno nella circonferenza di offset dei punti estremi + + //// ottavo passo : concateno i percorsi risultanti (senza cambiare verso) + + //// nono passo : se con smusso o estensione, sostituisco i fillet con questi + + //// ordino le curve in ordine decrescente di lunghezza + //if ( m_CrvLst.size() > 1) { + // for ( auto pCrv : m_CrvLst) { + // double dLen ; + // if ( pCrv->GetLength( dLen)) + // pCrv->SetTempProp( int( 1000 * dLen)) ; + // else + // pCrv->SetTempProp( 0) ; + // } + // m_CrvLst.sort( []( const ICurve* pA, const ICurve* pB) { return ( pA->GetTempProp() > pB->GetTempProp()) ; }) ; + //} + + //// se originale era chiusa, verifico le risultanti e se necessario cerco di chiuderle + //if ( bClosed) { + // for ( auto pCrv : m_CrvLst) { + // CurveComposite* pCrvCo = GetBasicCurveComposite( pCrv) ; + // if ( pCrvCo != nullptr) + // pCrvCo->Close() ; + // } + //} + + //return true ; +} + +//---------------------------------------------------------------------------- +ICurve* +OffsetCurve3d::GetCurve( void) +{ + return GetLongerCurve() ; +} + +//---------------------------------------------------------------------------- +ICurve* +OffsetCurve3d::GetLongerCurve( void) +{ + if ( m_CrvLst.empty()) + return nullptr ; + // le curve sono ordinate in senso decrescente di lunghezza + ICurve* pCrv = m_CrvLst.front() ; + m_CrvLst.pop_front() ; + return pCrv ; +} + +//---------------------------------------------------------------------------- +ICurve* +OffsetCurve3d::GetShorterCurve( void) +{ + if ( m_CrvLst.empty()) + return nullptr ; + // le curve sono ordinate in senso decrescente di lunghezza + ICurve* pCrv = m_CrvLst.back() ; + m_CrvLst.pop_back() ; + return pCrv ; +} + +struct Cyl { + Cyl( void): frCyl( GLOB_FRM), dH( 0.), dRad( 0.) {;} ; + Cyl( const Frame3d& _frCyl, double _dH, double _dRad, double _dLinTol) : + frCyl( _frCyl), dH( _dH), dRad( _dRad) { ;} + Cyl( const Point3d& _ptBase, const Vector3d& vtZ, double _dH, double _dRad, double _dLinTol) : + dH( _dH), dRad( _dRad){ + frCyl.Set( _ptBase, vtZ); } +public : + Frame3d frCyl ; +public: + double dH ; + double dRad ; +}; + +typedef vector CYLVECT ; + +//---------------------------------------------------------------------------- +bool +IsPointInsideCylinder( const Point3d& ptTest, const Cyl& offCyl) +{ + Point3d ptTestLoc = ptTest ; ptTestLoc.ToLoc( offCyl.frCyl) ; + if ( ptTestLoc.z > offCyl.dH || ptTestLoc.z < 0) + return false ; + double dDist = ptTestLoc.x * ptTestLoc.x + ptTestLoc.y * ptTestLoc.y ; + double dRadSq = offCyl.dRad * offCyl.dRad ; + if ( dDist > dRadSq) + return false ; + return true ; +} + +bool +AdjustConcavePartsInPath( const ICurveComposite* pCrv, ICURVEPOVECTOR& vOffsetCrvs, const INTVECTOR& vFlag, double dRad) +{ + const double dLinTol = 5 * EPS_SMALL ; + INTVECTOR vErase ; + for ( int i = 0 ; i < ssize( vOffsetCrvs) ; ++i) { + int nFlag = vFlag[i] ; + if ( nFlag == OffsetCurve3d::AngType::ANG_SMOOTH_CONC) { + // scorro i prossimi finchè trovo la fine della zona concava + INTVECTOR vLines ; + while ( nFlag == OffsetCurve3d::AngType::ANG_SMOOTH_CONC) { + vLines.push_back( i) ; + ++i ; + nFlag = vFlag[i] ; + } + CYLVECT vCyl ; + // creo un cilindro della dimensione del raggio + for ( int j = 0 ; j < ssize( vLines) ; ++j) { + const ICurve* pSubCrv = pCrv->GetCurve( vLines[j]) ; + Point3d ptStart, ptEnd ; + pSubCrv->GetStartPoint( ptStart) ; + pSubCrv->GetEndPoint( ptEnd) ; + Vector3d vtHeight = ptEnd - ptStart ; vtHeight.Normalize() ; + double dHeight = vtHeight.Len() ; + vCyl.emplace_back( ptStart, vtHeight, dHeight, dRad, dLinTol) ; + } + + // controllo l'end di ogni linea per verificare se sta nel cilindro definito da uno degli altri tratti + // controllo tutto i punti + bool bErasedSomePart = false ; + bool bErasedPrev = false ; + INTVECTOR vInters ; + for ( int j = 0 ; j < ssize( vLines) ; ++j) { + Point3d ptStart, ptEnd ; + const ICurve* pSubCrv = pCrv->GetCurve( vLines[j]) ; + if ( pSubCrv == nullptr) + return false ; + pSubCrv->GetEndPoint( ptEnd) ; + pSubCrv->GetStartPoint( ptStart) ; + // se stanno in uno dei cilindri degli altri tratti della zona concava + for ( int k = 0 ; k < ssize( vLines) ; ++k) { + if ( j == k) + continue ; + bool bToErase = IsPointInsideCylinder( ptEnd, vCyl[k]) ; + if ( bErasedPrev && ! bToErase) + bToErase = bToErase || IsPointInsideCylinder( ptStart, vCyl[k]) ; + if ( bToErase) { + bErasedSomePart = true ; + bErasedPrev = true ; + vInters.push_back( vLines[j]) ; + if ( j < ssize( vLines) - 1) + vInters.push_back( vLines[j+1]) ; + ++j ; + break ; + } + else + bErasedPrev = false ; + } + } + if ( bErasedSomePart) { + // calcolo le intersezioni effettive del primo e ultimo tratto cancellati con i cilindri che li hanno cancellati + // controllo che effettivamente tutti i tratti cancellati siano consecutivi + for ( int j = 1 ; j < ssize( vInters) ; ++j) { + if ( vInters[j] != vInters[j-1] + 1) + return false ; + } + for ( int j = 0 ; j < ssize( vInters) ; ++j) { + // cancello i tratti intermedi + if ( j > 0 && j < ssize( vInters) - 1) { + vErase.push_back( vInters[j]) ; + continue ; + } + // per il primo e ultimo controllo le intersezioni con tutti i cilindri + ICurve* pCL = vOffsetCrvs[vInters[j]] ; + Point3d ptStart ; pCL->GetStartPoint( ptStart) ; + Vector3d vtStart ; pCL->GetStartDir( vtStart) ; + double dLen ; pCL->GetLength( dLen) ; + double dUTrim = ( j == 0 ? INFINITO : 0) ; + Point3d ptTrim = P_INVALID ; + for ( int k = 0 ; k < ssize( vCyl) ; ++k) { + if ( vInters[j] == k) + continue ; + Point3d ptInt1 = P_INVALID, ptInt2 = P_INVALID ; + double dU1, dU2 ; + Vector3d vtN1, vtN2 ; + if ( IntersLineCyl( ptStart, vtStart * dLen, vCyl[k].frCyl, vCyl[k].dH, vCyl[k].dRad, false, false, dU1, ptInt1, vtN1, dU2, ptInt2, vtN2, true)) { + bool bUpdate = ( j == 0 ? dU1 < dUTrim : dU1 > dUTrim) ; + bUpdate = bUpdate && ptInt1.IsValid() && dU1 > 0 && dU1 < 1 ; + bUpdate = bUpdate && vtN1 * vtStart < 0 ; + if ( bUpdate) { + dUTrim = dU1 ; + ptTrim = ptInt1 ; + } + bUpdate = ( j == 0 ? dU2 < dUTrim : dU2 > dUTrim) ; + bUpdate = bUpdate && ptInt2.IsValid() && dU2 > 0 && dU2 < 1 ; + bUpdate = bUpdate && vtN2 * vtStart > 0 ; + if ( bUpdate) { + dUTrim = dU2 ; + ptTrim = ptInt2 ; + } + } + } + if ( ptTrim.IsValid()) { + if ( j == 0) { + pCL->ModifyEnd( ptTrim) ; + double dNewLen ; pCL->GetLength( dNewLen) ; + if ( dNewLen < 0.1 && vInters[0] != 0) { // se fosse il primo allora potrei modificare il successivo + int nPrev = vInters[0] - 1 ; + vErase.push_back( vInters[0]) ; + vInters[0] = nPrev ; + ICurve* pCLPrev = vOffsetCrvs[nPrev] ; + pCLPrev->ModifyEnd( ptTrim) ; + } + } + else { + pCL->ModifyStart( ptTrim) ; + double dNewLen ; pCL->GetLength( dNewLen) ; + if ( dNewLen < 0.1 && vInters[j] != ssize( vOffsetCrvs) - 1) { // se fosse l'ultima curva allora potrei modificare la precedente + int nNext = vInters[j] + 1 ; + vErase.push_back( vInters[j]) ; + vInters[j] = nNext ; + ICurve* pCLNext = vOffsetCrvs[nNext] ; + pCLNext->ModifyStart( ptTrim) ; + } + } + } + } + } + i = vLines.back() ; + } + } + + // scorro tutto il vettore delle linee di offset e unisco aggiungendo una linea dove ne ho cancellate + for ( int i = 0 ; i < ssize( vOffsetCrvs) - 1 ; ++i) { + Point3d ptEndCurr, ptStartNext ; + vOffsetCrvs[i]->GetEndPoint( ptEndCurr) ; + vOffsetCrvs[i+1]->GetStartPoint( ptStartNext) ; + if ( ! AreSamePointApprox( ptEndCurr, ptStartNext)) { + ICurveLine* pCL = CreateBasicCurveLine() ; + pCL->Set( ptEndCurr, ptStartNext) ; + PtrOwner pCrvL( pCL) ; + vOffsetCrvs.insert( vOffsetCrvs.begin() + i + 1, std::move(pCrvL)) ; + ++i ; + } + } + return true ; +} \ No newline at end of file From b7b05fb3e1703ec90e22a415274028953995319a Mon Sep 17 00:00:00 2001 From: Daniele Bariletti Date: Fri, 12 Jun 2026 16:48:00 +0200 Subject: [PATCH 2/2] EgtGeomKernel : - correzione e miglioria dell'offset 3d delle curve. --- OffsetCurve3d.cpp | 197 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 140 insertions(+), 57 deletions(-) diff --git a/OffsetCurve3d.cpp b/OffsetCurve3d.cpp index eb277c4..50cc9c3 100644 --- a/OffsetCurve3d.cpp +++ b/OffsetCurve3d.cpp @@ -25,19 +25,32 @@ using namespace std ; -#define SAVECVRORIG 1 -#define SAVEOFFDIR 1 -#define SAVECYL 1 +#define SAVECVRORIG 0 +#define SAVEOFFDIR 0 +#define SAVECYL 0 #if SAVECVRORIG || SAVEOFFDIR || SAVECYL -#include "/EgtDev/Include/EGkColor.h" -#include "/EgtDev/Include/EGkGeoVector3d.h" -vector vGeo ; -vector vCol ; -#include "/EgtDev/Include/EGkGeoObjSave.h" + #include "/EgtDev/Include/EGkColor.h" + #include "/EgtDev/Include/EGkGeoVector3d.h" + vector vGeo ; + vector vCol ; + #include "CurveArc.h" + #include "/EgtDev/Include/EGkGeoObjSave.h" + #include "/EgtDev/Include/EGkStmFromCurves.h" #endif //---------------------------------------------------------------------------- -bool AdjustConcavePartsInPath( const ICurveComposite* pCrv, ICURVEPOVECTOR& vOffsetCrvs, const INTVECTOR& vFlag, double dRad) ; +struct OffsetSegm { + PtrOwner pCrv ; + int nFlag ; + int nParent ; + OffsetSegm( ICurve* _pCrv, int _nFlag, int _nParent) : + nFlag( _nFlag), nParent( _nParent) { pCrv.Set( _pCrv); } ; +} ; + +typedef vector vOffsetSeg ; + +bool AdjustConcavePartsInPath( const ICurveComposite* pCrv, vOffsetSeg& vOffsetCrvs, double dRad) ; + //---------------------------------------------------------------------------- OffsetCurve3d::~OffsetCurve3d( void) @@ -63,23 +76,33 @@ OffsetCurve3d::Reset( void) bool OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOffDist, int nType) { + // la funzione è pensata per lavorare con il risultato dell'operazione ProjectCurveOnSurf + + // vOffDir devono essere normalizzati + // mi aspetto che siano più o meno perpendicolari alla curva + // pulisco tutto Reset() ; PtrOwner pCrv( CreateBasicCurveComposite()) ; if ( ! pCrv->FromPolyLine( PL)) return false ; -#if SAVECVRORIG +#if SAVECVRORIG || SAVEOFFDIR + vGeo.clear() ; + vCol.clear() ; + vGeo.push_back( pCrv->Clone()) ; vCol.push_back( AQUA) ; - Point3d ptBase ; PL.GetFirstPoint( ptBase) ; - for ( int i = 0 ; i < ssize( vOffDir) ; ++i) { - IGeoVector3d* pVec = CreateGeoVector3d() ; - pVec->Set( vOffDir[i] * dOffDist, ptBase) ; - PL.GetNextPoint( ptBase) ; - vGeo.push_back( pVec) ; - vCol.push_back( BLUE) ; - } + #if SAVEOFFDIR + Point3d ptBase ; PL.GetFirstPoint( ptBase) ; + for ( int i = 0 ; i < ssize( vOffDir) ; ++i) { + IGeoVector3d* pVec = CreateGeoVector3d() ; + pVec->Set( vOffDir[i] * dOffDist, ptBase) ; + PL.GetNextPoint( ptBase) ; + vGeo.push_back( pVec) ; + vCol.push_back( BLUE) ; + } + #endif #endif // verifico se la curva è un segmento di retta @@ -101,22 +124,34 @@ OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOff return true ; } - // elimino tratti molto corti - if ( ! RemoveCurveSmallParts( pCrv, m_dLinTol)) - return false ; - bool bClosed = pCrv->IsClosed() ; INTVECTOR vFlag ; vFlag.push_back( OffsetCurve3d::AngType::ANG_STR) ; const ICurve* pSubCrv = pCrv->GetFirstCurve() ; + const double dSinAngSmall = sin( 1 * DEGTORAD) ; for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++i) { pSubCrv = pCrv->GetNextCurve() ; Vector3d vtDirCurr ; pSubCrv->GetStartDir( vtDirCurr) ; - int nFlag = vtDirCurr * vOffDir[i] > 0 ? OffsetCurve3d::AngType::ANG_SMOOTH_CONC : OffsetCurve3d::AngType::ANG_STR ; + vtDirCurr.Normalize() ; + int nFlag ; + double dProj = vtDirCurr * vOffDir[i-1] ; + if ( dProj > dSinAngSmall) + nFlag = OffsetCurve3d::AngType::ANG_SMOOTH_CONC ; + else if ( dProj > - dSinAngSmall) + nFlag = OffsetCurve3d::AngType::ANG_STR ; + else + nFlag = OffsetCurve3d::AngType::ANG_CVEX ; vFlag.push_back( nFlag) ; } + for ( int i = 1 ; i < ssize( vFlag) - 1 ; ++i) { + if ( vFlag[i-1] == OffsetCurve3d::AngType::ANG_SMOOTH_CONC && + vFlag[i+1] == OffsetCurve3d::AngType::ANG_SMOOTH_CONC && + vFlag[i] != OffsetCurve3d::AngType::ANG_SMOOTH_CONC) + vFlag[i] = OffsetCurve3d::AngType::ANG_SMOOTH_CONC ; + } + double dRadCorr ; Point3d ptPrev ; pCrv->GetStartPoint( ptPrev) ; ptPrev += vOffDir[0] * dOffDist ; @@ -126,13 +161,20 @@ OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOff Vector3d vtDirPrev ; pCrv->GetStartDir( vtDirPrev) ; const ICurve* pCrvPrev = pCrv->GetFirstCurve() ; const ICurve* pCrvCurr ; - vector> vOffsetCrvs ; + vOffsetSeg vOffsetCrvs ; + Vector3d vtDirPrevOff = V_INVALID ; for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++i) { pCrvCurr = pCrv->GetNextCurve() ; Vector3d vtOffDir = vOffDir[i] ; Vector3d vtDirCurr ; pCrvCurr->GetStartDir( vtDirCurr) ; pCrvPrev->GetStartDir( vtDirPrev) ; - Vector3d vtTang = Media( vtDirCurr, vtDirPrev) ; + Vector3d vtTang ; + if ( vFlag[i] != OffsetCurve3d::AngType::ANG_CVEX) + vtTang = Media( vtDirCurr, vtDirPrev) ; + else if ( vFlag[i] == OffsetCurve3d::AngType::ANG_CVEX && vFlag[i-1] != OffsetCurve3d::AngType::ANG_CVEX) + vtTang = vtDirPrev ; + else + vtTang = vtDirCurr ; vtTang.Normalize() ; Vector3d vtNorm = vtOffDir ; vtNorm.Rotate( vtTang, -90) ; @@ -155,11 +197,11 @@ OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOff Point3d ptAdd1 = ptPrev + dDelta * vtTangPrev ; ICurveLine* pCL1 = CreateBasicCurveLine() ; pCL1->Set( ptPrev, ptAdd1) ; - vOffsetCrvs.emplace_back( pCL1) ; + vOffsetCrvs.emplace_back( pCL1, OffsetCurve3d::AngType::ANG_CVEX, -1) ; Point3d ptAdd2 = ptP - dDelta * vtTang ; ICurveLine* pCL2 = CreateBasicCurveLine() ; pCL2->Set( ptAdd1, ptAdd2) ; - vOffsetCrvs.emplace_back( pCL2) ; + vOffsetCrvs.emplace_back( pCL2, OffsetCurve3d::AngType::ANG_CVEX, -1) ; ptPrev = ptAdd2 ; } @@ -187,28 +229,55 @@ OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOff // end //} - // aggiungo tratto - ICurveLine* pCL = CreateBasicCurveLine() ; - pCL->Set( ptPrev, ptP) ; - vOffsetCrvs.emplace_back( pCL) ; - // aggiorno punto precedente - ptPrev = ptP ; - vtNormPrev = vtNorm ; - vtCorrPrev = vtCorr ; - vtTangPrev = vtTang ; - vtDirPrev = vtDirCurr ; + Vector3d vtDirCurrOff = ptP - ptPrev ; vtDirCurrOff.Normalize() ; + double dProj = 1 ; + if ( vtDirPrevOff.IsValid()) + dProj = vtDirCurrOff * vtDirPrevOff ; + if ( dProj > - 0.5 || vFlag[i] == OffsetCurve3d::AngType::ANG_SMOOTH_CONC) { + // aggiungo tratto + ICurveLine* pCL = CreateBasicCurveLine() ; + pCL->Set( ptPrev, ptP) ; + vOffsetCrvs.emplace_back( pCL, vFlag[i], i - 1) ; + // aggiorno punto precedente + ptPrev = ptP ; + vtNormPrev = vtNorm ; + vtCorrPrev = vtCorr ; + vtTangPrev = vtTang ; + vtDirPrev = vtDirCurr ; + pCrvPrev = pCrvCurr ; + vtDirPrevOff = vtDirCurrOff ; + } } - //// qui faccio la correzione per gli angoli interni - //AdjustConcavePartsInPath( pCrv, vOffsetCrvs, vFlag, dOffDist) ; + // se chiusa aggiungo il tratto di chiusura + if ( bClosed) { + Point3d ptP ; vOffsetCrvs.front().pCrv->GetStartPoint( ptP) ; + ICurveLine* pCL = CreateBasicCurveLine() ; + pCL->Set( ptPrev, ptP) ; + vOffsetCrvs.emplace_back( pCL, OffsetCurve3d::AngType::ANG_STR, -1) ; + } + + // qui faccio la correzione per gli angoli interni + if ( ! AdjustConcavePartsInPath( pCrv, vOffsetCrvs, dOffDist)) + return false ; #if SAVECVRORIG || SAVEOFFDIR || SAVECYL + for ( int i = 0 ; i < ssize( vOffsetCrvs) ; ++i) { + vGeo.push_back( vOffsetCrvs[i].pCrv->Clone()) ; + if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_SMOOTH_CONC) + vCol.push_back( GREEN) ; + else if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_STR) + vCol.push_back( PURPLE) ; + else if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_CVEX) + vCol.push_back( RED) ; + } SaveGeoObj( vGeo, vCol, "C:\\Temp\\curve offset 3d\\crvoffset.nge") ; + return true ; #endif PtrOwner pCrvOffset( CreateBasicCurveComposite()) ; for ( int i = 0 ; i < ssize( vOffsetCrvs) ; ++i) { - if ( ! pCrvOffset->AddCurve( Release( vOffsetCrvs[i]))) + if ( ! pCrvOffset->AddCurve( Release( vOffsetCrvs[i].pCrv))) return false ; } m_CrvLst.push_back( Release( pCrvOffset)) ; @@ -303,43 +372,52 @@ typedef vector CYLVECT ; //---------------------------------------------------------------------------- bool -IsPointInsideCylinder( const Point3d& ptTest, const Cyl& offCyl) +IsPointInsideCylinder( const Point3d& ptTest, const Cyl& offCyl, double dLinTol) { Point3d ptTestLoc = ptTest ; ptTestLoc.ToLoc( offCyl.frCyl) ; if ( ptTestLoc.z > offCyl.dH || ptTestLoc.z < 0) return false ; double dDist = ptTestLoc.x * ptTestLoc.x + ptTestLoc.y * ptTestLoc.y ; - double dRadSq = offCyl.dRad * offCyl.dRad ; + double dRadSq = ( offCyl.dRad - dLinTol) * ( offCyl.dRad - dLinTol) ; if ( dDist > dRadSq) return false ; return true ; } bool -AdjustConcavePartsInPath( const ICurveComposite* pCrv, ICURVEPOVECTOR& vOffsetCrvs, const INTVECTOR& vFlag, double dRad) +AdjustConcavePartsInPath( const ICurveComposite* pCrv, vOffsetSeg& vOffsetCrvs, double dRad) { const double dLinTol = 5 * EPS_SMALL ; INTVECTOR vErase ; for ( int i = 0 ; i < ssize( vOffsetCrvs) ; ++i) { - int nFlag = vFlag[i] ; + int nFlag = vOffsetCrvs[i].nFlag ; if ( nFlag == OffsetCurve3d::AngType::ANG_SMOOTH_CONC) { // scorro i prossimi finchè trovo la fine della zona concava INTVECTOR vLines ; while ( nFlag == OffsetCurve3d::AngType::ANG_SMOOTH_CONC) { vLines.push_back( i) ; ++i ; - nFlag = vFlag[i] ; + nFlag = vOffsetCrvs[i].nFlag ; } CYLVECT vCyl ; // creo un cilindro della dimensione del raggio for ( int j = 0 ; j < ssize( vLines) ; ++j) { - const ICurve* pSubCrv = pCrv->GetCurve( vLines[j]) ; + if ( vOffsetCrvs[vLines[j]].nParent == -1) + continue ; + const ICurve* pSubCrv = pCrv->GetCurve( vOffsetCrvs[vLines[j]].nParent) ; Point3d ptStart, ptEnd ; pSubCrv->GetStartPoint( ptStart) ; pSubCrv->GetEndPoint( ptEnd) ; - Vector3d vtHeight = ptEnd - ptStart ; vtHeight.Normalize() ; + Vector3d vtHeight = ptEnd - ptStart ; double dHeight = vtHeight.Len() ; + vtHeight.Normalize() ; vCyl.emplace_back( ptStart, vtHeight, dHeight, dRad, dLinTol) ; +#if SAVECYL + CurveArc ca ; ca.Set( ptStart, vtHeight, dRad) ; + ISurfTriMesh* pSurfTm = GetSurfTriMeshByExtrusion( &ca, vtHeight, false, 2 * EPS_SMALL) ; + vGeo.push_back( pSurfTm) ; + vCol.push_back( LGRAY) ; +#endif } // controllo l'end di ogni linea per verificare se sta nel cilindro definito da uno degli altri tratti @@ -349,7 +427,7 @@ AdjustConcavePartsInPath( const ICurveComposite* pCrv, ICURVEPOVECTOR& vOffsetCr INTVECTOR vInters ; for ( int j = 0 ; j < ssize( vLines) ; ++j) { Point3d ptStart, ptEnd ; - const ICurve* pSubCrv = pCrv->GetCurve( vLines[j]) ; + const ICurve* pSubCrv = vOffsetCrvs[vLines[j]].pCrv ; if ( pSubCrv == nullptr) return false ; pSubCrv->GetEndPoint( ptEnd) ; @@ -358,9 +436,9 @@ AdjustConcavePartsInPath( const ICurveComposite* pCrv, ICURVEPOVECTOR& vOffsetCr for ( int k = 0 ; k < ssize( vLines) ; ++k) { if ( j == k) continue ; - bool bToErase = IsPointInsideCylinder( ptEnd, vCyl[k]) ; + bool bToErase = IsPointInsideCylinder( ptEnd, vCyl[k], dLinTol) ; if ( bErasedPrev && ! bToErase) - bToErase = bToErase || IsPointInsideCylinder( ptStart, vCyl[k]) ; + bToErase = bToErase || IsPointInsideCylinder( ptStart, vCyl[k], dLinTol) ; if ( bToErase) { bErasedSomePart = true ; bErasedPrev = true ; @@ -388,7 +466,7 @@ AdjustConcavePartsInPath( const ICurveComposite* pCrv, ICURVEPOVECTOR& vOffsetCr continue ; } // per il primo e ultimo controllo le intersezioni con tutti i cilindri - ICurve* pCL = vOffsetCrvs[vInters[j]] ; + ICurve* pCL = vOffsetCrvs[vInters[j]].pCrv ; Point3d ptStart ; pCL->GetStartPoint( ptStart) ; Vector3d vtStart ; pCL->GetStartDir( vtStart) ; double dLen ; pCL->GetLength( dLen) ; @@ -425,7 +503,7 @@ AdjustConcavePartsInPath( const ICurveComposite* pCrv, ICURVEPOVECTOR& vOffsetCr int nPrev = vInters[0] - 1 ; vErase.push_back( vInters[0]) ; vInters[0] = nPrev ; - ICurve* pCLPrev = vOffsetCrvs[nPrev] ; + ICurve* pCLPrev = vOffsetCrvs[nPrev].pCrv ; pCLPrev->ModifyEnd( ptTrim) ; } } @@ -436,7 +514,7 @@ AdjustConcavePartsInPath( const ICurveComposite* pCrv, ICURVEPOVECTOR& vOffsetCr int nNext = vInters[j] + 1 ; vErase.push_back( vInters[j]) ; vInters[j] = nNext ; - ICurve* pCLNext = vOffsetCrvs[nNext] ; + ICurve* pCLNext = vOffsetCrvs[nNext].pCrv ; pCLNext->ModifyStart( ptTrim) ; } } @@ -447,16 +525,21 @@ AdjustConcavePartsInPath( const ICurveComposite* pCrv, ICURVEPOVECTOR& vOffsetCr } } + // cancello le curve indicate in vErase + for ( int i = ssize( vErase) - 1 ; i >= 0 ; --i) { + vOffsetCrvs.erase( vOffsetCrvs.begin() + vErase[i]) ; + } + // scorro tutto il vettore delle linee di offset e unisco aggiungendo una linea dove ne ho cancellate for ( int i = 0 ; i < ssize( vOffsetCrvs) - 1 ; ++i) { Point3d ptEndCurr, ptStartNext ; - vOffsetCrvs[i]->GetEndPoint( ptEndCurr) ; - vOffsetCrvs[i+1]->GetStartPoint( ptStartNext) ; + vOffsetCrvs[i].pCrv->GetEndPoint( ptEndCurr) ; + vOffsetCrvs[i+1].pCrv->GetStartPoint( ptStartNext) ; if ( ! AreSamePointApprox( ptEndCurr, ptStartNext)) { ICurveLine* pCL = CreateBasicCurveLine() ; pCL->Set( ptEndCurr, ptStartNext) ; - PtrOwner pCrvL( pCL) ; - vOffsetCrvs.insert( vOffsetCrvs.begin() + i + 1, std::move(pCrvL)) ; + vOffsetCrvs.emplace_back( pCL, OffsetCurve3d::AngType::ANG_STR,-1) ; + rotate( vOffsetCrvs.begin() + i + 1, vOffsetCrvs.end() - 1, vOffsetCrvs.end()) ; ++i ; } }