From 3e51485b13f9df6f4ab48aa34fb78ff86314cb1a Mon Sep 17 00:00:00 2001 From: Dario Sassi Date: Wed, 6 Apr 2016 06:26:49 +0000 Subject: [PATCH] EgtGeomKernel 1.6o4 : - aggiunta OffsetCurveOnX. --- EgtGeomKernel.rc | Bin 11718 -> 11718 bytes EgtGeomKernel.vcxproj | 2 + EgtGeomKernel.vcxproj.filters | 6 + OffsetCurve.cpp | 2 +- OffsetCurveOnX.cpp | 466 ++++++++++++++++++++++++++++++++++ 5 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 OffsetCurveOnX.cpp diff --git a/EgtGeomKernel.rc b/EgtGeomKernel.rc index ad441509753d98025ef584a582ff96b29abb8ad9..1d3afc644e1a9827febf18e01e91aaa93fe9c135 100644 GIT binary patch delta 110 zcmX>WeJpyzA2vpl&G-4vGfl1&(wY2&Q;pGNvZAo=W*@FeEMOU1X1ItOWeJpyzA2vqg&G-4vGfl1&(wY2&Q;pGhvZAo=W*@FeEMOU1X1ItO diff --git a/EgtGeomKernel.vcxproj b/EgtGeomKernel.vcxproj index aa3d012..8a8b939 100644 --- a/EgtGeomKernel.vcxproj +++ b/EgtGeomKernel.vcxproj @@ -321,6 +321,7 @@ copy $(TargetPath) \EgtProg\Dll64 + @@ -401,6 +402,7 @@ copy $(TargetPath) \EgtProg\Dll64 + diff --git a/EgtGeomKernel.vcxproj.filters b/EgtGeomKernel.vcxproj.filters index e5563e0..ccbe36b 100644 --- a/EgtGeomKernel.vcxproj.filters +++ b/EgtGeomKernel.vcxproj.filters @@ -330,6 +330,9 @@ File di origine\GeoCollision + + File di origine\GeoOffset + @@ -737,6 +740,9 @@ File di intestazione\Include + + File di intestazione\Include + diff --git a/OffsetCurve.cpp b/OffsetCurve.cpp index 9ea3da0..32650a9 100644 --- a/OffsetCurve.cpp +++ b/OffsetCurve.cpp @@ -1,7 +1,7 @@ //---------------------------------------------------------------------------- // EgalTech 2015-2015 //---------------------------------------------------------------------------- -// File : CurveOffsetAdv.cpp Data : 23.09.15 Versione : 1.6i8 +// File : OffsetCurve.cpp Data : 23.09.15 Versione : 1.6i8 // Contenuto : Classe per offset avanzato di Curve. // // diff --git a/OffsetCurveOnX.cpp b/OffsetCurveOnX.cpp new file mode 100644 index 0000000..2bd3993 --- /dev/null +++ b/OffsetCurveOnX.cpp @@ -0,0 +1,466 @@ +//---------------------------------------------------------------------------- +// EgalTech 2016-2016 +//---------------------------------------------------------------------------- +// File : OffsetCurveOnX.cpp Data : 02.04.16 Versione : 1.6o4 +// Contenuto : Classe per offset di Curve lungo la sola X. +// +// +// +// Modifiche : 02.04.16 DS Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "CurveComposite.h" +#include "CurveLine.h" +#include "CurveArc.h" +#include "GeoConst.h" +#include "/EgtDev/Include/EgkOffsetCurveOnX.h" +#include "/EgtDev/Include/EgkIntersCurves.h" +#include "/EgtDev/Include/EGkDistPointCurve.h" +#include "/EgtDev/Include/EgtPointerOwner.h" +#include + +using namespace std ; + +//---------------------------------------------------------------------------- +static bool SplitLeftRightArcs( CurveComposite& cCompo) ; +static bool SimpleOffsetOnX( ICurve* pCrv, double dDist) ; +static bool VerifyAndAdjustSamePoint( ICurve* pCrv1, ICurve* pCrv2) ; +static bool VerifyAndAdjustInternalAngle( ICurve* pCrv1, ICurve* pCrv2) ; +static bool SafeDist( double dDist, double dCoeff, ICurve* pCrv1, ICurve* pCrv2) ; + + +//---------------------------------------------------------------------------- +OffsetCurveOnX::~OffsetCurveOnX( void) +{ + for ( auto& pCrv : m_CrvLst) { + if ( pCrv != nullptr) { + delete pCrv ; + pCrv = nullptr ; + } + } + m_CrvLst.clear() ; +} + +//---------------------------------------------------------------------------- +bool +OffsetCurveOnX::Make( const ICurve* pCrv, double dDist) +{ + // verifico che la curva esista e sia piana + Plane3d plPlane ; + if ( pCrv == nullptr || ! pCrv->IsFlat( plPlane)) + return false ; + // se esiste estrusione, verifico sia perpendicolare al piano della curva + Vector3d vtExtr ; + if ( pCrv->GetExtrusion( vtExtr) && ! vtExtr.IsSmall()) { + if ( ! AreSameOrOppositeVectorApprox( plPlane.vtN, vtExtr)) + return false ; + } + else + vtExtr = plPlane.vtN ; + + // determino se necessario cambiare riferimento ( dal vettore estrusione) + bool bNeedRef = ( ! vtExtr.IsZplus()) ; + + // creo una copia della curva + CurveComposite ccCopy ; + if ( ! ccCopy.CopyFrom( pCrv)) + return false ; + ccCopy.SetExtrusion( vtExtr) ; + + // se necessario cambio il riferimento + Frame3d frExtr ; + if ( bNeedRef) { + // calcolo il riferimento OCS con VtExtr come asse Z + if ( ! frExtr.Set( ORIG, vtExtr)) + return false ; + // esprimo la curva in questo riferimento + ccCopy.ToLoc( frExtr) ; + } + + // verifico che la curva sia fatta solo da rette e archi che giacciono nel piano XY (VtExtr è ora Z+) + if ( ! ccCopy.ArcsBezierCurvesToArcsPerpExtr( 10 * EPS_SMALL, ANG_TOL_STD_DEG)) + return false ; + + // spezzo eventuali archi che cambiano pendenza + if ( ! SplitLeftRightArcs( ccCopy)) + return false ; + + // verifico se curva chiusa + bool bClosed = ccCopy.IsClosed() ; + + // Primo passo : estraggo entità dalla copia ed eseguo opportuno offset orizzontale + CurveComposite ccCopy2 ; + if ( ! ccCopy2.CopyFrom( &ccCopy)) + return false ; + // indice della prima curva (1-based, per poter assegnare un significato al segno) + int nInd1 = 1 ; + // recupero la prima curva e la processo + PtrOwner pCrv1( ccCopy2.RemoveFirstOrLastCurve( false)) ; + if ( IsNull( pCrv1)) + return false ; + // eseguo semplice offset orizzontale + pCrv1->SetTempProp( nInd1) ; + SimpleOffsetOnX( Get( pCrv1), dDist) ; + // curve successive + PtrOwner pCrv2( ccCopy2.RemoveFirstOrLastCurve( false)) ; + while ( ! IsNull( pCrv2)) { + // eseguo semplice offset + pCrv2->SetTempProp( nInd1 + 1) ; + SimpleOffsetOnX( Get( pCrv2), dDist) ; + // metto in lista curva precedente + m_CrvLst.push_back( Release( pCrv1)) ; + // aggiorno curva precedente + ++ nInd1 ; + pCrv1.Set( Release( pCrv2)) ; + // passo alla curva successiva + pCrv2.Set( ccCopy2.RemoveFirstOrLastCurve( false)) ; + } + // Metto in lista l'ultima curva rimasta + if ( ! IsNull( pCrv1)) + m_CrvLst.push_back( Release( pCrv1)) ; + + // Secondo passo : taglio angoli tipo interni + ICurve* pPrev = nullptr ; + if ( bClosed && ! m_CrvLst.empty()) + pPrev = m_CrvLst.back() ; + for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ;) { + ICurve* pCrv = *iIter ; + // gestione e verifica di estremi quasi coincidenti ed angoli interni + if ( pPrev != nullptr) { + if ( ! VerifyAndAdjustSamePoint( pPrev, pCrv)) + VerifyAndAdjustInternalAngle( pPrev, pCrv) ; + } + // passo al successivo + pPrev = pCrv ; + ++ iIter ; + } + + // Terzo passo : conversione in curva composita chiudendo gli eventuali buchi con rette + PtrOwner pCrvCompo( CreateBasicCurveComposite()) ; + if ( IsNull( pCrvCompo)) + return false ; + Point3d ptPrec ; + if ( ! m_CrvLst.empty()) + m_CrvLst.front()->GetStartPoint( ptPrec) ; + for ( auto& pCrv : m_CrvLst) { + // se punto iniziale diverso da precedente, inserisco segmento di collegamento + Point3d ptStart ; + pCrv->GetStartPoint( ptStart) ; + if ( ! AreSamePointApprox( ptPrec, ptStart)) { + CurveLine crvLine ; + crvLine.Set( ptPrec, ptStart) ; + pCrvCompo->AddCurve( crvLine) ; + } + // salvo punto finale come nuovo precedente + pCrv->GetEndPoint( ptPrec) ; + // aggiungo la curva alla composita + pCrvCompo->AddCurve( pCrv) ; + pCrv = nullptr ; + } + m_CrvLst.clear() ; + // se curva chiusa, verifico anche gap tra inizio e fine + if ( bClosed) + pCrvCompo->Close() ; + // unisco tutte le curve possibili + pCrvCompo->MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG) ; + + // se non rimasto nulla, esco + double dLen ; + if ( ! pCrvCompo->IsValid() || ! pCrvCompo->GetLength( dLen) || dLen < 10 * EPS_SMALL) + return true ; + // inserisco la curva nella lista + m_CrvLst.push_back( Release( pCrvCompo)) ; + CurveComposite* pCompo1 = GetBasicCurveComposite( m_CrvLst.front()) ; + + // Quarto passo : spezzatura della curva composita negli eventuali punti di auto-intersezione + // calcolo le auto-intersezioni + SelfIntersCurve sintC( *pCompo1) ; + DBLVECTOR vU ; + IntCrvCrvInfo iccInfo ; + for ( int i = 0 ; sintC.GetIntCrvCrvInfo( i, iccInfo) ; ++ i) { + if ( ! iccInfo.bOverlap) { + vU.push_back( iccInfo.IciA[0].dU) ; + vU.push_back( iccInfo.IciB[0].dU) ; + } + else { + vU.push_back( iccInfo.IciA[0].dU) ; + vU.push_back( iccInfo.IciA[1].dU) ; + vU.push_back( iccInfo.IciB[0].dU) ; + vU.push_back( iccInfo.IciB[1].dU) ; + } + } + // ordino il vettore in senso crescente + sort( vU.begin(), vU.end(), less()) ; + // calcolo il vettore delle lunghezze di ogni parte di curva + DBLVECTOR vLen ; + for ( const auto& dU : vU) { + double dULen ; + pCompo1->GetLengthAtParam( dU, dULen) ; + vLen.push_back( dULen) ; + } + // se ultima parte molto piccola, la elimino + if ( ! vU.empty() && ( dLen - vLen.back()) < 2 * EPS_SMALL) { + vU.pop_back() ; + vLen.pop_back() ; + } + // eseguo la divisione + int nCount = 1 ; + double dUPrev = 0 ; + double dLenPrev = 0 ; + for ( int i = 0 ; i < int( vU.size()) ; ++ i) { + // se lunghezza della curva risulta piccola, salto + if ( ( vLen[i] - dLenPrev) < 2 * EPS_SMALL) + continue ; + // copio la curva + PtrOwner pCopy( pCompo1->Clone()) ; + if ( IsNull( pCopy)) + return false ; + m_CrvLst.push_back( Release( pCopy)) ; + ++ nCount ; + // trimmo l'originale + pCompo1->TrimStartEndAtParam( dUPrev, vU[i]) ; + // la copia diventa il nuovo corrente + pCompo1 = GetBasicCurveComposite( m_CrvLst.back()) ; + dUPrev = vU[i] ; + dLenPrev = vLen[i] ; + } + // se fatta almeno una suddivisione, trimmo l'ultima parte + if ( dUPrev > EPS_PARAM) + pCompo1->TrimStartAtParam( dUPrev) ; + + // Quinto passo : elimino le parti che sono troppo vicine al percorso originale + for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ;) { + ICurve* pCrv = *iIter ; + // distanza minima di alcuni punti interni della curva dalla curva originale + if ( ! SafeDist( dDist, 0.5, pCrv, &ccCopy) || + ! SafeDist( dDist, 0.25, pCrv, &ccCopy) || + ! SafeDist( dDist, 0.75, pCrv, &ccCopy)) { + delete pCrv ; + iIter = m_CrvLst.erase( iIter) ; + continue ; + } + // passo alla successiva + ++ iIter ; + } + + // Sesto passo : concateno i percorsi risultanti (senza cambiare verso) + for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ;) { + CurveComposite* pCrvCo = GetBasicCurveComposite( *iIter) ; + // recupero punti iniziale e finale della curva + Point3d ptStart, ptEnd ; + pCrvCo->GetStartPoint( ptStart) ; + pCrvCo->GetEndPoint( ptEnd) ; + // ciclo sulle curve successive per verificare se possibile concatenamento + for ( auto iIter2 = next( iIter) ; iIter2 != m_CrvLst.end() ;) { + CurveComposite* pCrvCo2 = GetBasicCurveComposite( *iIter2) ; + // recupero punti iniziale e finale della curva + Point3d ptStart2, ptEnd2 ; + pCrvCo2->GetStartPoint( ptStart2) ; + pCrvCo2->GetEndPoint( ptEnd2) ; + // verifiche di concatenamento + if ( AreSamePointEpsilon( ptEnd, ptStart2, 10 * EPS_SMALL)) { + pCrvCo->AddCurve( pCrvCo2, true, 10 * EPS_SMALL) ; + m_CrvLst.erase( iIter2) ; + ptEnd = ptEnd2 ; + iIter2 = next( iIter) ; + } + else if ( AreSamePointEpsilon( ptEnd2, ptStart, 10 * EPS_SMALL)) { + pCrvCo->AddCurve( pCrvCo2, false, 10 * EPS_SMALL) ; + m_CrvLst.erase( iIter2) ; + ptStart = ptStart2 ; + iIter2 = next( iIter) ; + } + else + ++ iIter2 ; + } + ++ iIter ; + } + + // riporto il risultato nel riferimento originale + if ( bNeedRef) { + for ( auto pCrv : m_CrvLst) + pCrv->ToGlob( frExtr) ; + } + + return true ; +} + +//---------------------------------------------------------------------------- +ICurve* +OffsetCurveOnX::GetCurve( void) +{ + if ( m_CrvLst.empty()) + return nullptr ; + ICurve* pCrv = m_CrvLst.front() ; + m_CrvLst.pop_front() ; + return pCrv ; +} + +//---------------------------------------------------------------------------- +ICurve* +OffsetCurveOnX::GetLongerCurve( void) +{ + if ( m_CrvLst.empty()) + return nullptr ; + // cerco la curva più lunga + double dMaxLen = 0 ; + auto iMaxIter = m_CrvLst.end() ; + for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ; ++ iIter) { + double dLen ; + if ( (*iIter)->GetLength( dLen) && dLen > dMaxLen) { + dMaxLen = dLen ; + iMaxIter = iIter ; + } + } + // se trovata + if ( iMaxIter != m_CrvLst.end()) { + ICurve* pCrv = (*iMaxIter) ; + m_CrvLst.erase( iMaxIter) ; + return pCrv ; + } + + return nullptr ; +} + +//---------------------------------------------------------------------------- +bool +SplitLeftRightArcs( CurveComposite& cCompo) +{ + int i = 0 ; + const ICurve* pCrv = cCompo.GetCurve( i) ; + while ( pCrv != nullptr) { + if ( pCrv->GetType() == CRV_ARC) { + const CurveArc* pArc = GetBasicCurveArc( pCrv) ; + // determino la rotazione angolare dall'inizio alle direzioni su e giù + double dAng1, dAng2 ; + bool bDet ; + pArc->GetStartVersor().GetRotation( Y_AX, Z_AX, dAng1, bDet) ; + pArc->GetStartVersor().GetRotation( - Y_AX, Z_AX, dAng2, bDet) ; + // oriento queste rotazioni come l'angolo al centro dell'arco + if ( pArc->GetAngCenter() > 0) { + if ( dAng1 < 0) + dAng1 += ANG_FULL ; + if ( dAng2 < 0) + dAng2 += ANG_FULL ; + } + else { + if ( dAng1 > 0) + dAng1 -= ANG_FULL ; + if ( dAng2 > 0) + dAng2 -= ANG_FULL ; + } + // le ordino in senso crescente di ampiezza assoluta + if ( abs( dAng1) > abs( dAng2)) + swap( dAng1, dAng2) ; + // verifico se la prima di queste rotazioni è compresa nell'arco + if ( abs( dAng1) > EPS_ANG_SMALL && abs( dAng1) < abs( pArc->GetAngCenter()) - EPS_ANG_SMALL) { + cCompo.AddJoint( i + dAng1 / pArc->GetAngCenter()) ; + } + // verifico la seconda + else if ( abs( dAng2) > EPS_ANG_SMALL && abs( dAng2) < abs( pArc->GetAngCenter()) - EPS_ANG_SMALL) { + cCompo.AddJoint( i + dAng2 / pArc->GetAngCenter()) ; + } + } + // passo alla successiva + pCrv = cCompo.GetCurve( ++i) ; + } + return true ; +} + +//---------------------------------------------------------------------------- +bool +SimpleOffsetOnX( ICurve* pCrv, double dDist) +{ + Point3d ptStart, ptEnd ; + pCrv->GetStartPoint( ptStart) ; + pCrv->GetEndPoint( ptEnd) ; + if ( abs( ptStart.y - ptEnd.y) < EPS_SMALL) + ; + else if ( ptStart.y < ptEnd.y) + pCrv->Translate( Vector3d( dDist, 0, 0)) ; + else + pCrv->Translate( Vector3d( - dDist, 0, 0)) ; + return true ; +} + + + + +//---------------------------------------------------------------------------- +bool +VerifyAndAdjustSamePoint( ICurve* pCrv1, ICurve* pCrv2) +{ + // verifica dei puntatori + if ( pCrv1 == nullptr || pCrv2 == nullptr) + return false ; + + // calcolo dei punti estremi (finale per prima curva, iniziale per seconda) + Point3d ptP1, ptP2 ; + if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2)) + return false ; + // verifica distanza tra estremi + if ( ! AreSamePointXYEpsilon( ptP1, ptP2, 10 * EPS_SMALL)) + return false ; + // se coincidono esattamente, va bene così + if ( AreSamePointExact( ptP1, ptP2)) + return true ; + // sono in tolleranza, ma devo ricongiungere gli estremi + Point3d ptMid = 0.5 * ( ptP1 + ptP2) ; + return ( pCrv1->ModifyEnd( ptMid) && pCrv2->ModifyStart( ptMid)) ; +} + +//---------------------------------------------------------------------------- +bool +VerifyAndAdjustInternalAngle( ICurve* pCrv1, ICurve* pCrv2) +{ + // verifica dei puntatori + if ( pCrv1 == nullptr || pCrv2 == nullptr) + return false ; + + // calcolo l'intersezione tra le due curve + IntersCurveCurve intCC( *pCrv1, *pCrv2) ; + if ( intCC.GetIntersCount() == 0) + return false ; + // prendo l'intersezione più vicina al punto medio tra gli estremi delle curve + Point3d ptP1, ptP2 ; + if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2)) + return false ; + Point3d ptMid = 0.5 * ( ptP1 + ptP2) ; + Point3d ptNew1, ptNew2 ; + if ( ! intCC.GetIntersPointNearTo( 0, ptMid, ptNew1) || + ! intCC.GetIntersPointNearTo( 1, ptMid, ptNew2)) + return false ; + // modifico le due curve sul punto medio di intersezione + Point3d ptNew = 0.5 * ( ptNew1 + ptNew2) ; + return ( pCrv1->ModifyEnd( ptNew) && pCrv2->ModifyStart( ptNew)) ; +} + +//---------------------------------------------------------------------------- +bool +SafeDist( double dDist, double dCoeff, ICurve* pCrv1, ICurve* pCrv2) +{ + // calcolo il punto e due vicini + double dUStart, dUEnd ; + pCrv1->GetDomain( dUStart, dUEnd) ; + double dU = dCoeff * dUStart + ( 1 - dCoeff) * dUEnd ; + Point3d ptP ; + pCrv1->GetPointD1D2( dU, ICurve::FROM_MINUS, ptP) ; + Point3d ptPrev, ptNext ; + Vector3d vtPrev, vtNext ; + pCrv1->GetPointTang( dU - 100 * EPS_PARAM, ICurve::FROM_MINUS, ptPrev, vtPrev) ; + pCrv1->GetPointTang( dU + 100 * EPS_PARAM, ICurve::FROM_PLUS, ptNext, vtNext) ; + // non si considerano i tratti orizzontali + if ( abs( vtPrev.y) < EPS_SMALL || abs( vtNext.y) < EPS_SMALL) + return true ; + // segmento orizzontale di prova centrato sul punto e lungo dDist + dDist + Vector3d vtDelta( abs(dDist) - 5 * EPS_SMALL, 0, 0) ; + CurveLine Line ; + Line.Set( ptP - vtDelta, ptP + vtDelta) ; + // interseco il segmento di test con la curva originale + IntersCurveCurve IntCC( Line, *pCrv2) ; + return ( IntCC.GetIntersCount() == 0) ; +}