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