diff --git a/CalcPocketing.cpp b/CalcPocketing.cpp new file mode 100644 index 0000000..f36850d --- /dev/null +++ b/CalcPocketing.cpp @@ -0,0 +1,2954 @@ +//---------------------------------------------------------------------------- +// EgalTech 2023-2023 +//---------------------------------------------------------------------------- +// File : EGkCalcPocketing.h Data : 16.11.23 Versione : 2.5j1 +// Contenuto : Dichiarazione della funzione per calcolare le curve base di Pocketing +// +// +// +// Modifiche : 16.11.23 RE Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "DllMain.h" +#include "GeoConst.h" +#include "CurveArc.h" +#include "BiArcs.h" +#include "/EgtDev/Include/EGkSurfFlatRegion.h" +#include "/EgtDev/Include/EGkSfrCreate.h" +#include "/EgtDev/Include/EGkCurveAux.h" +#include "/EgtDev/Include/EGkCalcPocketing.h" +#include "EgtDev/Include/EGkFilletChamfer.h" +#include "EgtDev/Include/EGkDistPointCurve.h" +#include "EgtDev/Include/EGkDistPointCurve.h" +#include "EgtDev/Include/EGkCurveLocal.h" +#include "EgtDev/Include/EGkMedialAxis.h" +#include "EgtDev/Include/EGkLinePntTgCurve.h" +#include + +using namespace std ; + +//---------------------------------------------------------------------------- +// Calcolo delle curve elementari di Pocketing +//---------------------------------------------------------------------------- + +enum { POCKET_SUB_ZIGZAG = 0, + POCKET_SUB_ONEWAY = 1, + POCKET_SUB_SPIRALIN = 2, + POCKET_SUB_SPIRALOUT = 3} ; + +// variabili d'appoggio ( per non passare troppi parametri tra le funzioni secondarie) +int TYPE ; +double RAD ; +bool SMOOTH ; + +//---------------------------------------------------------------------------- +static bool +AdjustContourStart( ICurveComposite* pCompo) +{ + + // creo un vettore di indici che definisce l'ordine delle curve chiuse in base alla lunghezza + INTVECTOR vInd ; vInd.reserve( pCompo->GetCurveCount()) ; + + // vettore di indici già utilizzati + INTVECTOR vIndUsed ; vIndUsed.reserve( pCompo->GetCurveCount()) ; + + double dMaxLen = -INFINITO ; + int nCurrInd = 0 ; + bool bStop = false ; + + // imbroglio mettendo chiuse tutte le curve ---- + for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) { + pCompo->SetCurveTempProp( u, 0, 0) ; + pCompo->SetCurveTempProp( u, 0, 1) ; + } + pCompo->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; + // --------------------------------------------- + + for ( int c = 0 ; c < pCompo->GetCurveCount() && ! bStop ; ++ c) { + bStop = true ; + for ( int i = 0 ; i < pCompo->GetCurveCount() ; ++ i) { + + int nProp_i ; + // se la curva i-esima non è già stata considerata ed è aperta... + if ( find( vIndUsed.begin(), vIndUsed.end(), i) == vIndUsed.end() && + pCompo->GetCurveTempProp( i, nProp_i, 0) && nProp_i == 0) { + + // creo la curva i-esima + const ICurve* pCrv_i( pCompo->GetCurve( i)) ; + double dLen_i ; + + if ( pCrv_i->GetLength( dLen_i) && dLen_i > dMaxLen) { // se di lunghezza maggiore alla soglia... + dMaxLen = dLen_i ; + nCurrInd = i ; + bStop = false ; + } + } + } + vIndUsed.push_back( nCurrInd) ; + vInd.push_back( nCurrInd) ; + dMaxLen = -INFINITO ; + } + + // ora che ho riempito il vettore di indici lo scorro e cerco di entrare in questi lati chiusi secondo l'ordine + for ( int i = 0 ; i < ( int)vInd.size() ; ++ i) { + pCompo->ChangeStartPoint( vInd[0] + 0.5) ; + // imbroglio + break ; + } + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +ModifyCurveToSmoothed( ICurveComposite* pCrv, double dRightLen, double dLeftLen, bool bAsParam) +{ + + // se non richiesto non faccio nulla + if ( ! SMOOTH) + return true ; + + // controllo parametri + if ( pCrv == nullptr || dLeftLen < EPS_SMALL || dRightLen < EPS_SMALL) + return false ; + + ICURVEPOVECTOR vCrvStepsToFill ; // vettore delle Curve da raccordare (curve della composita) + ICURVEPOVECTOR vCrvArcs ; // vettore contenente gli Archi (tra le curve della composita) + INTVECTOR vArcsToJump ; // vettore di indici per gli archi saltati (nel caso non si riesca a raccordare ...) + + // se nella composita ho meno di due curve allora non c'è nulla da raccordare + const ICurve* pMyCrv = pCrv->GetFirstCurve() ; + while ( pMyCrv != nullptr) { + vCrvStepsToFill.emplace_back( pMyCrv->Clone()) ; + pMyCrv = pCrv->GetNextCurve() ; + } + if ( vCrvStepsToFill.size() < 2) + return true ; + + // controllo se la curva è chiusa (nel caso inserisco nel vettore la prima curva due volte, all'inizio e alla fine) + if ( pCrv->IsClosed()) { + const ICurve* pMyCrvFirst = pCrv->GetFirstCurve() ; + if ( pMyCrvFirst == nullptr) + return false ; + vCrvStepsToFill.emplace_back( pMyCrvFirst->Clone()) ; + } + + double dUE_ref, dUS_ref, dRadius, dPar1, dPar2 ; + // riempio il vettore degli archi, scorro tutte le curve da raccordare ( la i-esima e la (i+1)-esima) + for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) { + + // controllo che le curve non siano già tangenti + Vector3d vtS, vtE ; + if ( ! vCrvStepsToFill[i]->GetEndDir( vtS) || ! vCrvStepsToFill[i+1]->GetStartDir( vtE)) + continue ; + if ( AreSameVectorApprox( vtS, vtE)) { + vCrvArcs.emplace_back( CreateCurveComposite()) ; // arco nullo + vArcsToJump.push_back( i) ; // da scartare + continue ; + } + + + double dLen_act ; vCrvStepsToFill[i]->GetLength( dLen_act) ; + double dLen_succ ; vCrvStepsToFill[i+1]->GetLength( dLen_succ) ; + double dUE_ref = 1. ; + if ( dLen_act > dLeftLen) + dUE_ref -= dLeftLen / dLen_act ; + double dUS_ref = 0. ; + if ( dLen_succ > dRightLen) + dUS_ref = dRightLen / dLen_succ ; + + if ( bAsParam) { + // cerco i parametri corrispondenti sulle due curve + double dU_cm_S = 0 ; double dULast1 = 1 ; double dULast2 = 1 ; + vCrvStepsToFill[i]->GetDomain( dU_cm_S, dULast1) ; + dUE_ref = ( 1 - dRightLen) * dULast1 ; + vCrvStepsToFill[i+1]->GetDomain( dU_cm_S, dULast2) ; + dUS_ref = dLeftLen * dULast2 ; + } + + + // prendo i punti sulle due curve rispetto a tali parametri + Point3d ptS, ptE ; + if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) || + ! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE)) + return false ; + + dRadius = Dist( ptS, ptE) ; // uso come raggio la distanza tra i due punti + + int nMaxTestForArcs = 3 ; // tentativi per creare l'arco + int nIterForArcs = 0 ; + bool IntersBTWArcs = false ; + + // creo l'arco di raccordo e controllo che non sia troppo piccolo + PtrOwner pCrvArc( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ; + double dArcLen ; + if ( ! IsNull( pCrvArc) && pCrvArc->IsValid() && ( ! pCrvArc->GetLength( dArcLen) || dArcLen < 5 * EPS_SMALL)) { + vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco nullo + vArcsToJump.push_back( i) ; // da scartare + continue ; + } + + // dal secondo arco in poi controllo che non ci siano intersezioni tra essi + if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) { + IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ; + if ( intCCH.GetIntersCount() > 0 ) + IntersBTWArcs = true ; + } + + // se ho intersezioni tra archi o l'arco creato non è valido, allora provo altre nMaxTestForArcs volte a ricrearlo avvicinando i punti + while (( IsNull( pCrvArc) || IntersBTWArcs) && nIterForArcs < nMaxTestForArcs) { + + dUE_ref = ( 1 + dUE_ref ) * 0.5 ; + dUS_ref = dUS_ref * 0.5 ; + + if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) || + ! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE)) + return false ; + + dRadius = Dist( ptS, ptE) ; + + pCrvArc.Set( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ; + nIterForArcs++ ; + + IntersBTWArcs = false ; + if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) { // dal secondo arco in poi controllo che non ci siano intersezioni tra essi + IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ; + if ( intCCH.GetIntersCount() > 0) + IntersBTWArcs = true ; + } + + } + if ( IsNull( pCrvArc) || ! pCrvArc->IsValid()) { // se ancora non riesco... salto l'arco + vCrvArcs.emplace_back( CreateCurveArc()) ; // arco nullo + vArcsToJump.push_back( i) ; // da scartare + continue ; + } + + // se ho creato l'arco lo memorizzo + vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco valido + vArcsToJump.push_back( -1) ; // da considerare + } + + // creo la curva che restituirò + PtrOwner pCrvCO_temp( CreateCurveComposite()) ; + if ( IsNull( pCrvCO_temp)) + return false ; + Point3d ptArcHelp, ptFirstPoint ; + + // unisco la curva i-esima con l'arco i-esimo (non guardo l'ultima curva nel vettore, controllo dopo il caso di curva chiusa) + for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) { + + if ( vArcsToJump[i] == -1) { // se esiste l'arco ... + Point3d ptArcS, ptArcE ; + if ( ! vCrvArcs[i]->GetStartPoint( ptArcS) || + ! vCrvArcs[i]->GetEndPoint( ptArcE) || + ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcS, dUE_ref) || + ! vCrvStepsToFill[i+1]->GetParamAtPoint( ptArcE, dUS_ref)) + return false ; + + if ( i == 0) { // ... e sono nella prima iterazione ... + if ( ! pCrv->IsClosed()) { // ... e se la curva è aperta -> la inserisco nel nuovo percorso + if ( ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref)))) + return false ; + } + ptFirstPoint = ptArcS ; + // ... e se la curva è chiusa -> non la inserisco nel nuovo percorso + } + else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...) + if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) || + ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref)))) + return false ; + // aggiungo la curva 'tagliata per il raccordo' + } + + if ( ! pCrvCO_temp->AddCurve( vCrvArcs[i]->Clone())) // aggiungo l'arco di raccordo + return false ; + ptArcHelp = ptArcE ; + } + else { // se non esiste l'arco ... + if ( i == 0 ) { // e sono nella prima iterazione... + if ( ! vCrvStepsToFill[0]->GetEndPoint( ptArcHelp)) + return false ; + + if ( ! pCrv->IsClosed()) { // ...e se aperta // aggiungo la prima curva per intero + if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUE_ref) || + ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref)))) + return false ; + } + ptFirstPoint = ptArcHelp ; + } + else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...) + double dUS_cm ; + if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) || + ! vCrvStepsToFill[i]->GetDomain( dUS_cm, dUE_ref) || + ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref))) || + ! vCrvStepsToFill[i]->GetEndPoint( ptArcHelp)) + return false ; + } + } + + } + + // ultima curva... + if ( pCrv->IsClosed()) { // se curva chiusa... + if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUS_ref) || + ! vCrvStepsToFill[0]->GetParamAtPoint( ptFirstPoint, dUE_ref) || + ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( dUS_ref, dUE_ref)))) + return false ; + } + else { // se curva aperta... ( non ha importanza l'esistenza o meno degli archi...) + double dUS_cm ; + if ( ! vCrvStepsToFill.back()->GetParamAtPoint( ptArcHelp, dUS_ref) || + ! vCrvStepsToFill.back()->GetDomain( dUS_cm, dUE_ref) || + ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill.back()->CopyParamRange( dUS_ref, dUE_ref)))) + return false ; + } + + // ripristino il punto inziale se la curva è chiusa + double dNewDU ; + if ( ! pCrv->IsClosed()) { + if ( ! pCrvCO_temp->GetParamAtPoint( ptArcHelp, dNewDU)) + return false ; + pCrvCO_temp->ChangeStartPoint( dNewDU) ; + } + + pCrv->Clear() ; + pCrv->AddCurve( Release( pCrvCO_temp)) ; + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, ICRVCOMPOPOVECTOR& vOffIslands, ICurveComposite* pCrvLink) +{ + pCrvLink->Clear() ; + + // recupero il vettore estrusione dal bordo esterno offsettato della superficie + Vector3d vtExtr ; + vOffIslands[0]->GetExtrusion( vtExtr) ; + + // determino il riferimento naturale della svuotatura (OCS con il vettore estrusione come asse Z) + Frame3d frLoc ; + frLoc.Set( ORIG, vtExtr) ; + + // non serve collegare ( può capitare nel tagliare percorsi con isole complesse) + if ( AreSamePointApprox( ptStart, ptEnd)) + return true ; + + // porto la curva di contenimento in locale a questo riferimento + vector vOffsExtr ; + for ( int i = 0 ; i < int( vOffIslands.size()) ; ++ i) { + CurveLocal CrvOutLoc( vOffIslands[i], GLOB_FRM, frLoc) ; + vOffsExtr.push_back( CrvOutLoc) ; + } + + // creo la retta che li unisce + PtrOwner pLine( CreateCurveComposite()) ; + if ( ! pLine->AddPoint( ptStart) || ! pLine->AddLine( ptEnd)) + return false ; + pLine->SetExtrusion( vtExtr) ; + // la porto in locale al riferimento della svuotatura + CurveLocal LineLoc( pLine, GLOB_FRM, frLoc) ; // ... per le intersezioni + + // creo la nuova curva formata dai tratti di linee INTERNI alle isole e dai tratti sul bordo degli offset + PtrOwner pCompo( pLine->Clone()) ; + if ( IsNull( pCompo) ) + return false ; + if ( ! pCompo->LocToLoc( GLOB_FRM, frLoc) ) + return false; + + PtrOwner pCompoHelp( pLine->Clone()) ; + if ( IsNull( pCompoHelp)) + return false ; + if ( ! pCompoHelp->LocToLoc( GLOB_FRM, frLoc)) + return false ; + + // scorro il vettore degli indici degli offset delle isole intersecati + for ( int i = 0 ; i < int( vOffIslands.size()) ; i++) { + CRVCVECTOR ccClass ; + IntersCurveCurve intCC( *pCompo, *vOffIslands[i]) ; + intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; + if ( ! pCompoHelp->Clear()) + return false ; + + for ( int j = 0 ; j < int( ccClass.size()) ; ++j ) { // per ogni intersezione j con l'offset dell'isola i + if ( ccClass[j].nClass == CRVC_OUT) { // se ho intersezione spezzo il segmento + Point3d ptS ; + pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ; + double dOffS ; + vOffIslands[i]->GetParamAtPoint( ptS, dOffS) ; + Point3d ptE ; + pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ; + double dOffE ; + vOffIslands[i]->GetParamAtPoint( ptE, dOffE) ; + // recupero i due possibili percorsi e uso il più corto + PtrOwner pCrvA( GetCurve( vOffIslands[i]->CopyParamRange( dOffS, dOffE))) ; + PtrOwner pCrvB( GetCurve( vOffIslands[i]->CopyParamRange( dOffE, dOffS))) ; + + if ( IsNull( pCrvA) || IsNull( pCrvB) ) + return false ; + double dLenA ; pCrvA->GetLength( dLenA) ; + double dLenB ; pCrvB->GetLength( dLenB) ; + if ( dLenA < dLenB ) { + pCompoHelp->AddCurve( Release( pCrvA)) ; + } + else { + pCrvB->Invert() ; + pCompoHelp->AddCurve( Release( pCrvB)) ; + } + } + else { // se non interseco + pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ; + } + } + + pCompo->Clear() ; + pCompo->AddCurve( pCompoHelp->Clone()) ; + + } + + // riporto pCompo nel sistema di riferimento originale + if ( ! pCompo->LocToLoc( frLoc, GLOB_FRM)) + return false ; + pCrvLink->AddCurve( Release( pCompo)) ; + + return true ; + +} + +//----------------------------------------------------------------------------- +static bool +ModifyBiArc( ICurve* pBiArcLink, double dToll, ICurveComposite* pNewBiArc) { + + if ( pBiArcLink == nullptr) + return false ; + if ( dToll > 0.99 || dToll < 0.01) + return false ; + pNewBiArc->Clear() ; + + double dUS, dUE ; + pBiArcLink->GetDomain( dUS, dUE) ; + + PtrOwner pArc1( GetCurve( pBiArcLink->CopyParamRange( dUS, (dUS + dUE) / 2))) ; + PtrOwner pArc2( GetCurve( pBiArcLink->CopyParamRange(( dUS + dUE ) / 2, dUE))) ; + if ( IsNull(pArc1) || ! pArc1->IsValid() || IsNull( pArc2) || ! pArc2->IsValid()) + return false ; + + // primo pezzo + pArc1->GetDomain( dUS, dUE) ; + PtrOwner pArc1A( GetCurve( pArc1->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll))) ; // Arc1 + PtrOwner pArc1B( GetCurve( pArc1->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE))) ; // Arc2 + Point3d pt1A, pt1B ; + pArc1A->GetEndPoint( pt1A) ; + pArc1B->GetStartPoint( pt1B) ; + PtrOwner pLine1( CreateCurveLine()) ; + pLine1->Set( pt1A, pt1B) ; // Linea + + // secondo pezzo + pArc2->GetDomain( dUS, dUE) ; + PtrOwner pArc2A( GetCurve( pArc2->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll))) ; // Arc1 + PtrOwner pArc2B( GetCurve( pArc2->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE))) ; // Arc2 + Point3d pt2A, pt2B ; + pArc2A->GetEndPoint( pt2A) ; + pArc2B->GetStartPoint( pt2B) ; + PtrOwner pLine2( CreateCurveLine()) ; + pLine2->Set( pt2A, pt2B) ; // Linea + + if ( ! pNewBiArc->AddCurve( Release( pArc1A)) || + ! pNewBiArc->AddCurve( Release( pLine1)) || + ! pNewBiArc->AddCurve( Release( pArc1B)) || + ! pNewBiArc->AddCurve( Release( pArc2A)) || + ! pNewBiArc->AddCurve( Release( pLine2)) || + ! pNewBiArc->AddCurve( Release( pArc2B))) + return false ; + + return true ; +} + +//------------------------------------------------------------------------------ +static bool +CalcBoundedSmootedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, + const Vector3d& vtEnd, double dParMeet, ICRVCOMPOPOVECTOR& vOffIslands, + ICurveComposite* pCrvLink) +{ + + // se senza smusso, ritorno tratto lineare + if ( ! SMOOTH) + return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; + + // creo il BiArc che unisce i due punti + double dAngStart, dAngEnd ; + vtStart.GetAngleXY( X_AX, dAngStart) ; + vtEnd.GetAngleXY( X_AX, dAngEnd) ; + PtrOwner pBiArcLink ; + if ( dParMeet != 0) + pBiArcLink.Set( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, dParMeet)) ; + else { + PtrOwner pCrvCir( CreateCurveArc()) ; + if ( IsNull( pCrvCir)) + return false ; + pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, - 360, 0, Z_AX) ; + pBiArcLink.Set( pCrvCir->Clone()) ; + pCrvLink->AddCurve( pBiArcLink->Clone()) ; + return true ; + } + if ( IsNull( pBiArcLink)) + return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; + + // se BiArco troppo grande allora lo modifico + double dLenBiArc ; + pBiArcLink->GetLength( dLenBiArc) ; + if ( dLenBiArc > 200 * 1000 * EPS_SMALL) { + PtrOwner pCrvNewBiArcS( CreateCurveComposite()) ; + ModifyBiArc( pBiArcLink, 0.2, pCrvNewBiArcS) ; + pBiArcLink.Set( pCrvNewBiArcS) ; + } + + // creo la nuova curva formata inizialmente dai tratti di archi + PtrOwner pCompo( GetCurveComposite( pBiArcLink->Clone())) ; + if ( IsNull( pCompo)) + return false ; + + PtrOwner pCompoHelp( GetCurveComposite( pBiArcLink->Clone())) ; + if ( IsNull( pCompoHelp)) + return false ; + + // scorro tutte le curve per controllare le intersezioni ... + for ( int i = 0 ; i < int( vOffIslands.size()) ; ++ i) { + + CRVCVECTOR ccClass ; + IntersCurveCurve intCC( *pCompo, *vOffIslands[i]) ; + intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; + if ( ! pCompoHelp->Clear()) + return false ; + + for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { // per ogni intersezione j con l'offset dell'isola i + + if ( ccClass[j].nClass == CRVC_OUT && ccClass.size() > 1) { // se ho intersezione spezzo il segmento + Point3d ptS ; + pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ; + double dOffS ; + vOffIslands[i]->GetParamAtPoint( ptS, dOffS, 1500 * EPS_SMALL) ; + Point3d ptE ; + pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ; + double dOffE ; + vOffIslands[i]->GetParamAtPoint( ptE, dOffE, 1500 * EPS_SMALL) ; + // recupero i due possibili percorsi e uso il più corto + PtrOwner pCrvA( GetCurve( vOffIslands[i]->CopyParamRange( dOffS, dOffE))) ; + PtrOwner pCrvB( GetCurve( vOffIslands[i]->CopyParamRange( dOffE, dOffS))) ; + + if ( IsNull( pCrvA) || IsNull( pCrvB)) + return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; + + double dLenA ; pCrvA->GetLength( dLenA) ; + double dLenB ; pCrvB->GetLength( dLenB) ; + + if ( j != 0) { + if ( dLenA < dLenB) { + if ( ! pCompoHelp->AddCurve( Release( pCrvA))) + return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; + } + else { + pCrvB->Invert() ; + if ( ! pCompoHelp->AddCurve( Release( pCrvB))) + return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; + } + } + else { + pCrvB->Invert() ; + if ( ! pCompoHelp->AddCurve( Release( pCrvB))) + return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; + } + } + else { // se non interseco + if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) + return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; + } + } + + pCompo->Clear() ; + if ( ! pCompo->AddCurve( pCompoHelp->Clone())) + if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink)) + return false ; + + } + + // se il BiArco è troppo piccolo allora lo approssimo con un segmento + BBox3d bBox3 ; + if ( pCompo->GetLocalBBox( bBox3)) { + double dRadBB ; + if ( bBox3.GetRadius( dRadBB) && dRadBB < 500 * EPS_SMALL) { + if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink)) + return false ; + return true ; + } + } + + ModifyCurveToSmoothed( pCompo, 0.05, 0.05, true) ; + pCrvLink->AddCurve( Release( pCompo)) ; + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, ICRVCOMPOPOVECTOR& vOffsCL, + ICRVCOMPOPOVECTOR& vFirstOffset, ICurveComposite* pCrvLink, double dLenPercS, + double dLenPercE, int nMaxIter) +{ + // controllo i parametri + if ( pCrvS == nullptr || pCrvE == nullptr ) + return false ; + pCrvLink->Clear() ; + + PtrOwner ptCrvFinal( CreateCurveComposite()) ; + if ( IsNull( ptCrvFinal)) + return false ; + + // Prendo i punti, i vettori tangenti e i parametri di essi per le curve + Point3d ptSS, ptSE, ptES, ptEE ; Vector3d vS, vE ; double dUSS, dUSE, dUES, dUEE, dLenS, dLenE ; + dUSS = 0; dUSE = 0; + + pCrvS->GetEndPoint( ptSE) ; + pCrvS->GetStartPoint( ptSS) ; + pCrvE->GetStartPoint( ptES) ; + pCrvE->GetEndPoint( ptEE) ; + pCrvS->GetEndDir( vS) ; + pCrvE->GetStartDir( vE) ; + pCrvS->GetDomain( dUSS, dUSE) ; + pCrvE->GetDomain( dUES, dUEE) ; + pCrvS->GetLength( dLenS) ; + pCrvE->GetLength( dLenE) ; + + // se ho una curva di primo Offset allora non devo accorciarla ... + for ( int i = 0 ; i < int( vFirstOffset.size()) ; ++ i) { + if ( vFirstOffset[i]->IsPointOn( ptSS) && vFirstOffset[i]->IsPointOn( ptSE)) + dLenPercS = 0 ; + if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE)) + dLenPercE = 0 ; + } + + double dLStepS = dLenPercS * dLenS ; + double dLStepE = dLenPercE * dLenE ; + dLenE = 0 ; + + // calcolo i possibili BiArchi tra le due curve + int nIter = 0 ; + + while ( nIter < nMaxIter) { + + // calcolo il BiArco + PtrOwner ptBiArc( CreateCurveComposite()) ; + if ( ! CalcBoundedSmootedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, ptBiArc) || + ptBiArc->GetCurveCount() == 0) + return false ; + + ptCrvFinal->Clear() ; + ptCrvFinal.Set( ptBiArc->Clone()) ; + + if ( dLenPercE == 0 && dLenPercS == 0 ) + break ; + + // se il BiArco creato interseca le altre curve di Offset allora mi fermo... + bool bInterr = false ; + for ( int i = 0 ; i < int( vOffsCL.size()) && ! bInterr ; ++ i) { + if ( vOffsCL[i]->IsPointOn( ptSE) || vOffsCL[i]->IsPointOn( ptES)) + continue ; + + CRVCVECTOR ccClass ; + IntersCurveCurve intCC( *ptBiArc, *vOffsCL[i]) ; + intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; + if ( ccClass.size() > 1) // se intersezione + bInterr = true ; + } + if ( bInterr) + break ; // così come ultimo arco ho quello precedente (valido) + + // se ho trovato un BiArco valido, allora accorcio le due curve di Offset + dLenS -= dLStepS ; + dLenE += dLStepE ; + + // ricalcolo il punto iniziale e finale + pCrvS->GetParamAtLength( dLenS, dUSE) ; + pCrvS->GetPointD1D2( dUSE, ICurve::FROM_MINUS, ptSE) ; + pCrvE->GetParamAtLength( dLenE, dUES) ; + pCrvE->GetPointD1D2( dUES, ICurve::FROM_PLUS, ptES) ; + + // ricalcolo il vettore tangente iniziale e finale + PtrOwner pCrvS_clone( pCrvS->Clone()) ; // curva di Offset iniziale accorciata + PtrOwner pCrvE_clone( pCrvE->Clone()) ; // curva di Offset finale accorciata + pCrvS_clone->ChangeStartPoint( dUSE) ; + pCrvE_clone->ChangeStartPoint( dUES) ; + pCrvS_clone->GetStartDir( vS) ; + pCrvE_clone->GetStartDir( vE) ; + + ++ nIter ; + + } + + pCrvLink->AddCurve( ptCrvFinal->Clone()) ; // ultimo arco valido trovato + + return true ; +} + +//---------------------------------------------------------- +static bool +GetUnclearedRegion( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vCrvs, ICURVEPOVECTOR& vLinks, + ISurfFlatRegion* pSrfToCut) +{ + + // controllo dei parametri + if ( vFirstOffs.size() == 0 || ( int)vCrvs.size() < ( int)vFirstOffs.size()) + return false ; + pSrfToCut->Clear() ; + + // 1) creo la regione esterna + PtrOwner pSrfExtern( CreateSurfFlatRegion()) ; + if ( IsNull( pSrfExtern)) + return false ; + + for ( int i = 0 ; i < int( vFirstOffs.size()) ; ++ i) { + if ( i == 0) + pSrfExtern->AddExtLoop( vFirstOffs[i]->Clone()) ; + else { + PtrOwner pCrvIntLoop( vFirstOffs[i]->Clone()) ; + if( IsNull( pCrvIntLoop) || ! pCrvIntLoop->Invert() || ! pSrfExtern->AddIntLoop( pCrvIntLoop->Clone())) + return false ; + } + } + + // ---- le seguenti regioni sono distinte per calcolar emeglio la Feed ---- + // 2) Creo la regione svuotata dal Tool negli Offset, nei Links e sia dagli Offsets che dai Links + PtrOwner pSrfTool_Offs( CreateSurfFlatRegion()) ; + PtrOwner pSrfTool_Links( CreateSurfFlatRegion()) ; + PtrOwner pSrfTool( CreateSurfFlatRegion()) ; + if ( IsNull( pSrfTool_Offs) || IsNull( pSrfTool_Links) || IsNull( pSrfTool)) + return false ; + + // creo un vettore che conterrà solamente i Link percorsi fino ad Ora + ICRVCOMPOPOVECTOR vLinks_done ; + + for ( int i = TYPE == POCKET_SUB_SPIRALIN ? 0 : ( int)vCrvs.size() - 1 ; + TYPE == POCKET_SUB_SPIRALIN ? i < int( vCrvs.size()) : i >= 0 ; + TYPE == POCKET_SUB_SPIRALIN ? ++ i : -- i) { + + // ================= LINK ================================ + if ( i <= ( int)vLinks.size() && ! IsNull( vLinks[i]) && vLinks[i]->IsValid()) { + + // Feed + PtrOwner pCompoLink_i( CreateCurveComposite()) ; + if ( IsNull( pCompoLink_i)) + return false ; + pCompoLink_i->AddCurve( vLinks[i]->Clone()) ; + + // per Link ... + PtrOwner pCrvLink_i( vLinks[i]->Clone()) ; + if ( IsNull( pCrvLink_i)) + return false ; + PtrOwner pSrfToolRegLinki( GetSurfFlatRegionFromFatCurve( Release( pCrvLink_i), RAD + 5 * EPS_SMALL, false, false)) ; + if ( ! IsNull( pSrfToolRegLinki)) { + if ( ! pSrfTool_Links->IsValid() || pSrfTool_Links->GetChunkCount() == 0) + pSrfTool_Links.Set( pSrfToolRegLinki) ; + else + pSrfTool_Links->Add( *pSrfToolRegLinki) ; + } + + // risetto il Link come curva composita ... + vLinks[i].Set( pCompoLink_i->Clone()) ; + + // inserisco il Link nel vettore dei Link percorsi + vLinks_done.emplace_back( Release( pCompoLink_i)) ; + } + + // ================== OFFSET ============================= + + // aggiorno la superificie svuotata + PtrOwner pCrvOffs_i( CloneCurveComposite( vCrvs[i])) ; + if ( IsNull( pCrvOffs_i)) + return false ; + PtrOwner pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( Release( pCrvOffs_i) , RAD + 5 * EPS_SMALL, false, false)) ; + if ( ! IsNull( pSrfToolRegOffi)) { + if ( ! pSrfTool_Offs->IsValid() || pSrfTool_Offs->GetChunkCount() == 0) + pSrfTool_Offs.Set( Release( pSrfToolRegOffi)) ; + else + pSrfTool_Offs->Add( *pSrfToolRegOffi) ; + } + } + + // 3) Calcolo la superificie svuotata + pSrfTool.Set( pSrfTool_Offs) ; + pSrfTool->Add( *pSrfTool_Links) ; + + // 4) Creo la regione contenente tutte le parti non svuotate + pSrfToCut->CopyFrom( pSrfExtern) ; + if ( ! pSrfToCut->Subtract( *pSrfTool)) + return false ; + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +RemoveFirstLoopFromSfr( ISurfFlatRegion* pSrfOrig) { + + if ( pSrfOrig == nullptr) + return true ; + + PtrOwner pSrfRes( CreateSurfFlatRegion()) ; + if ( IsNull( pSrfRes)) + return false ; + + for ( int c = 1 ; c < pSrfOrig->GetChunkCount() ; ++ c) { + PtrOwner pSrfHelp( CreateSurfFlatRegion()) ; + if ( IsNull( pSrfHelp)) + return false ; + PtrOwner pCrvMiniChunkBorder( GetCurveComposite( pSrfOrig->GetLoop( c, 0))) ; + pSrfHelp->AddExtLoop( pCrvMiniChunkBorder->Clone()) ; + if ( c == 1) + pSrfRes.Set( pSrfHelp->Clone()) ; + else + pSrfRes->Add( *( pSrfHelp)) ; + } + + pSrfOrig->Clear() ; + pSrfOrig->CopyFrom( pSrfRes) ; + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +RemoveExtraPartByMedialAxis( const ISurfFlatRegion* pChunkToCut, ICRVCOMPOPOVECTOR& vOffsFirstCurve, + int& nOptFlag, Point3d& ptCentroid, ICurveComposite* pCrvPath) +{ + + // nOptFlag : 0 -> non faccio nulla | 1 -> svuoto con un centroide | 2 -> svuoto con un percorso + + // variabili iniziali + nOptFlag = 2 ; + bool bForceCentroid = false ; + if ( pChunkToCut == nullptr) + return false ; + + // curva che l'utensile dovrà seguire per svuotare la regione e curva di ingombro del tool + PtrOwner pCrvStepByStepPath( CreateCurveComposite()) ; + if ( IsNull( pCrvStepByStepPath)) + return false ; + + // copie del chunk che devo svuotare + PtrOwner pSrfChunkToCutClone( pChunkToCut->Clone()) ; + double dArea = INFINITO ; + ICRVCOMPOPOVECTOR vCrvCoMedAxi ; // vettore dei medial Axis + PNTVECTOR vPtCentroid ; // vettore di centroidi + int nIter = 0 ; // numero di iterazioni + + while ( pSrfChunkToCutClone->IsValid() && pSrfChunkToCutClone->GetChunkCount() != 0 && + pSrfChunkToCutClone->GetArea( dArea) && dArea > 10 * EPS_SMALL) { + // finchè restano parti non svuotate la cui area totale è suffcientemente grande... + + if ( nIter > 25) // troppi tentativi... + break ; + + nIter++ ; + PtrOwner pSrfBiggerChunk( pSrfChunkToCutClone->CloneChunk( 0)) ; + if( IsNull( pSrfBiggerChunk)) + return false ; + if( ! pSrfBiggerChunk->IsValid()) + break ; + + double dAreaExt ; + // se l'area del chunk è piccola... rimuovo il chunk dalla superficie da svuotare + if ( pSrfBiggerChunk->GetArea( dAreaExt) && dAreaExt < 10 * EPS_SMALL) { + if ( ! RemoveFirstLoopFromSfr( pSrfChunkToCutClone)) + return false ; + if( IsNull( pSrfChunkToCutClone) || ! pSrfChunkToCutClone->IsValid()) + break ; + continue ; + } + + // prendo il centroide del chunk + Point3d ptC ; + if ( ! pSrfBiggerChunk->GetCentroid( ptC)) + break ; + + // creo la superificie che racchiude il mio tool + PtrOwner pCrvToolShape( CreateCurveArc()) ; + if ( IsNull( pCrvToolShape)) + return false ; + pCrvToolShape->SetXY( ptC, RAD + 5 * EPS_SMALL) ; + PtrOwner pSrfTool( CreateSurfFlatRegion()) ; + if ( IsNull( pSrfTool) || ! pSrfTool->AddExtLoop( Release( pCrvToolShape)) || + ! pSrfTool->IsValid()) + break ; + + PtrOwner pSrfTest( CloneSurfFlatRegion( pSrfBiggerChunk)) ; + if( IsNull( pSrfTest)) + return false ; + + pSrfTest->Subtract( *pSrfTool) ; + if( IsNull( pSrfTest) || ! pSrfTest->IsValid() || ! pSrfTest->GetArea( dArea) || dArea < 10 * EPS_SMALL || + bForceCentroid) { + if ( nIter == 1) { // se prima iterazione -> ritorno il centroide com punto + nOptFlag = 1 ; + ptCentroid = ptC ; + return true ; + } + else { // se non sono alla prima iterazione -> memorizzo il centroide nel vettore + // controllo di non aver trovato un centroide già inserito ( per simmetria del chunk) + for ( int cen = 0 ; cen < int( vPtCentroid.size()) ; ++ cen) { + if ( AreSamePointApprox( vPtCentroid[cen], ptC)) { + pSrfChunkToCutClone->Clear() ; + break ; + } + } + vPtCentroid.push_back( ptC) ; + pSrfChunkToCutClone->Subtract( *pSrfTool) ; + if( IsNull( pSrfChunkToCutClone) || ! pSrfChunkToCutClone->IsValid()) + break ; + continue ; + } + } + bForceCentroid = false ; + + // 1) ricavo il bordo della regione + PtrOwner pCrvBorder( pSrfBiggerChunk->GetLoop( 0, 0)) ; + if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid()) + return false ; + + // 2) ricavo il medial Axis del bordo + PolyLine PlMedAx ; + if ( ! CurveSimpleMedialAxis( pCrvBorder, PlMedAx)) { + bForceCentroid = true ; + -- nIter ; + continue ; + } + + PtrOwner pCrvMedAx( CreateCurveComposite()) ; + if ( IsNull( pCrvMedAx)) + return false ; + if ( ! pCrvMedAx->FromPolyLine( PlMedAx)) { + // se questo medial Axis è troppo piccolo e la polyLine non è convertitibile in curva composita ... + bForceCentroid = true ; // 2a) forzo ad andare nel centroide + -- nIter ; // 2b) scalo una iterzione + continue ; + } + + pCrvMedAx->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ; + PolyArc PlMedAxArc ; + pCrvMedAx->ApproxWithArcsEx( 500 * EPS_SMALL, ANG_TOL_STD_DEG, 20.0, PlMedAxArc) ; + pCrvMedAx->Clear() ; + if( ! pCrvMedAx->FromPolyArc( PlMedAxArc)) { + bForceCentroid = true ; + -- nIter ; + continue ; + } + + // smusso la curva + ModifyCurveToSmoothed( pCrvMedAx, 0.025, 0.025, true) ; + // se curva valida la inserisco nel vettore, altrimenti forzo il centroide + if ( pCrvMedAx->IsValid()) + vCrvCoMedAxi.emplace_back( pCrvMedAx->Clone()) ; + else { + bForceCentroid = true ; + -- nIter ; + continue ; + } + + + // 3) guardo quale regione svuoto con questa curva + PtrOwner pSrfRemoved( GetSurfFlatRegionFromFatCurve( Release( pCrvMedAx), RAD + 5 * EPS_SMALL, false, false)) ; + if ( IsNull( pSrfRemoved) || ! pSrfRemoved->IsValid()) + break ; + + // 4) aggiorno la regione togliendo la parte percorsa dal tool + if ( ! pSrfChunkToCutClone->Subtract( *pSrfRemoved)) + break ; + if ( IsNull( pSrfChunkToCutClone) || ! pSrfChunkToCutClone->IsValid()) + break ; + + } + + // se entrambi i vettori sono vuoti l'area originaria era più piccola di 10 * EPS_SMALL -> non faccio nulla + if (( int)vCrvCoMedAxi.size() == 0 && ( int)vPtCentroid.size() == 0) { + nOptFlag = 0 ; + return true ; + } + + // ora collego la varie curve medial Axis trovate tra loro (ottenendo quindi una curva che svuota tutta la regione) + PtrOwner pCrvCoBackLink( CreateCurveComposite()) ; // curva che collega primo e ultimo medial Axis + if ( IsNull( pCrvCoBackLink)) + return false ; + Point3d ptSOriginal, ptEOriginal ; + Vector3d vtSOriginal, vtEOriginal ; + + // parametri iniziali del primo e ultimo medial Axis trovato + if ( ! vCrvCoMedAxi[0]->GetStartPoint( ptSOriginal) || + ! vCrvCoMedAxi.back()->GetEndPoint( ptEOriginal) || + ! vCrvCoMedAxi[0]->GetStartDir( vtSOriginal) || + ! vCrvCoMedAxi.back()->GetEndDir( vtEOriginal)) + return false ; + + pCrvPath->AddCurve( vCrvCoMedAxi[0]->Clone()) ; // inserisco il primo medial Axis nel percorso finale + + for ( int i = 1 ; i < int( vCrvCoMedAxi.size()) ; ++ i) { // scorro i restanti + Point3d ptS, ptE ; Vector3d vtS, vtE ; + // parametri iniziali del medialAxis i-esimo e (i-1)esimo + if ( ! vCrvCoMedAxi[i]->GetStartPoint( ptE) || + ! vCrvCoMedAxi[i-1]->GetEndPoint( ptS) || + ! vCrvCoMedAxi[i]->GetStartDir( vtE) || + ! vCrvCoMedAxi[i-1]->GetEndDir( vtS)) + return false ; + + PtrOwner pCrvLink( CreateCurveComposite()) ; + if ( IsNull( pCrvLink)) + return false ; + + if ( ! CalcBoundedSmootedLink( ptS, vtS, ptE, vtE, 0.5, vOffsFirstCurve, pCrvLink)) + if ( ! CalcBoundedLink( ptS, ptE, vOffsFirstCurve, pCrvLink)) + return false ; + + if ( ! pCrvPath->AddCurve( pCrvLink->Clone()) || ! pCrvPath->AddCurve( vCrvCoMedAxi[i]->Clone())) + return false ; + + } + + if ( ! AreSamePointEpsilon( ptEOriginal, ptSOriginal, EPS_SMALL)) + if ( ! CalcBoundedSmootedLink( ptEOriginal, vtEOriginal, ptSOriginal, vtSOriginal, 0.5, vOffsFirstCurve, pCrvCoBackLink)) + if ( ! CalcBoundedLink( ptEOriginal, ptSOriginal, vOffsFirstCurve, pCrvCoBackLink)) + return false ; + + // se ho trovato dei centroidi li unisco nei punti più vicini a questo percorso + for ( int i = 0 ; i < int( vPtCentroid.size()) ; ++ i) { + + // 1) cerco il punto sulla curva più vicino al centroide i + Point3d ptClosestOnPath ; int nFlag ; + if ( ! DistPointCurve( vPtCentroid[i], *pCrvPath).GetMinDistPoint( EPS_SMALL, ptClosestOnPath, nFlag)) + return false ; + // 2) spezzo la curva al paramtro del punto più vicino + double dU ; + if ( ! pCrvPath->GetParamAtPoint( ptClosestOnPath, dU)) + return false ; + + PtrOwner pCrvA( GetCurveComposite( pCrvPath->CopyParamRange( 0, dU))) ; + if ( IsNull( pCrvA)) + return false ; + PtrOwner pCrvB( GetCurveComposite( pCrvPath->CopyParamRange( dU, pCrvPath->GetCurveCount()))) ; + if ( IsNull( pCrvB)) + return false ; + + // 3) prendo il vettore tangente al percorso nel punto trovato nel pCrvPath + Vector3d vtTanCpt ; Point3d ptH ; + if ( ! pCrvPath->GetPointTang( dU, ICurve::FROM_MINUS, ptH, vtTanCpt)) + return false ; + + // 4) collego i due punti (centroide e punto più vicino alla curva) + PtrOwner pCrvPath1( CreateCurveComposite()) ; + if ( IsNull( pCrvPath1)) + return false ; + + if ( ! CalcBoundedSmootedLink( ptClosestOnPath, vtTanCpt, vPtCentroid[i], vtTanCpt, 0, vOffsFirstCurve, pCrvPath1)) + if ( ! CalcBoundedLink( ptClosestOnPath, vPtCentroid[i], vOffsFirstCurve, pCrvPath1)) + return false ; + + pCrvPath->Clear() ; + pCrvPath->AddCurve( Release( pCrvA)) ; + if( ! pCrvPath->AddCurve( Release( pCrvPath1))) + return false ; + pCrvPath->AddCurve( Release( pCrvB)) ; + } + + // la curva resitituita è una curva aperta che ha come primo punto il punto iniziale del primo medialAxis + // la curva restituita collega tutti i medial Axis tra di loro + // la curva restitutita collega tutti i centroidi con delle circonferenze + // la curva restituita ha come punto finale l'ultimo punto dell'ultimo medial Axis + + return true ; +} + +//--------------------------------------------------------------------------- +static bool +CutOffsetToClosestPoint( ICRVCOMPOPOVECTOR& vCurves, const Point3d& ptFocus, + ICurveComposite* pCrv1, ICurveComposite* pCrv2, int& nIndex) +{ + + // controllo di avere almeno un offset... + if (( int)vCurves.size() == 0) + return false ; + + // variabili iniziali + Point3d ptCl_H ; + double dDistance = INFINITO ; + int nFlag ; + pCrv1->Clear() ; + pCrv2->Clear() ; + nIndex = -1 ; + Point3d ptCL ; + + // scorro tutti gli offset ad eccezione del primo ( a meno di averne uno solo ) + for ( int j = ( int)vCurves.size() == 1 ? 0 : 1 ; j < int( vCurves.size()) ; ++ j) { + + // non ho offset nulli, però controllo... + if( IsNull( vCurves[j])) + continue ; + + // prendo il punto più vicino + if ( ! DistPointCurve( ptFocus, *vCurves[j]).GetMinDistPoint( EPS_SMALL, ptCl_H, nFlag)) + continue ; + + // cerco il punto in assoluto più vicino + if ( dDistance > Dist( ptCl_H, ptFocus)) { + dDistance = Dist( ptCl_H, ptFocus) ; + ptCL = ptCl_H ; + nIndex = j ; + } + } + + // se non ho trovato nulla, esco + if ( nIndex < 0) + return false ; + + // ricavo le due curve ( l'Offset viene spezzato in due sottocurve mediante il punto trovato) + double dUTan ; + vCurves[nIndex]->GetParamAtPoint( ptCL, dUTan) ; + pCrv1->AddCurve( vCurves[nIndex]->Clone()) ; + if ( ! pCrv1->TrimEndAtParam( dUTan)) + pCrv1->Clear() ; + pCrv2->AddCurve( vCurves[nIndex]->Clone()) ; + if ( ! pCrv2->TrimStartAtParam( dUTan)) + pCrv2->Clear() ; + + return true ; +} + +//--------------------------------------------------------------------------- +static bool +CutLinkToClosestPoint( ICURVEPOVECTOR& vCurves, ICRVCOMPOPOVECTOR& vOffs, const Point3d& ptFocus, + ICurveComposite* pCrv1, ICurveComposite* pCrv2, bool& bFound, int& nIndex) +{ + + // variabili iniziali + double dDistance = INFINITO ; + Point3d ptCl_H ; + int nFlag ; + pCrv1->Clear() ; + pCrv2->Clear() ; + Point3d ptCL ; + nIndex = - 1 ; + bFound = false ; + + // scorro tutti i link cercando il più vicino + for ( int j = 0 ; j < int( vCurves.size()) ; ++ j) { + + // escludo i links nulli ... + if ( IsNull( vCurves[j])) + continue ; + + // distanza minima tra link e curva + if ( ! DistPointCurve( ptFocus, *vCurves[j]).GetMinDistPoint( EPS_SMALL, ptCl_H, nFlag)) + continue ; + + // cerco la distanza minimia assoluta ( non deve essere sopra un Offset ) + if ( dDistance > Dist( ptCl_H, ptFocus) && + ! ( vOffs[j-1]->IsPointOn( ptCl_H) || vOffs[j]->IsPointOn( ptCl_H))) { + dDistance = Dist( ptCl_H, ptFocus) ; + nIndex = j ; + bFound = true ; + } + } + + // se non ho trovato nulla, esco + if ( nIndex < 0) + return false ; + + // ricavo le due curve ( il link viene spezzato in due sottocurve mediante il punto trovato ) + double dUTan ; + vCurves[nIndex]->GetParamAtPoint( ptCL, dUTan) ; + pCrv1->AddCurve( vCurves[nIndex]->Clone()) ; + if ( ! pCrv1->TrimEndAtParam( dUTan)) + pCrv1->Clear( ) ; + pCrv2->AddCurve( vCurves[nIndex]->Clone()) ; + if ( ! pCrv2->TrimStartAtParam( dUTan)) + pCrv2->Clear() ; + + return true ; +} + +//------------------------------------------------------------------------------ +static bool +CutCurveByOffsets( ICurveComposite* pCurve, ICRVCOMPOPOVECTOR& vOffs) { + + // controllo parametri ingresso + if (( int)vOffs.size() == 0) + return true ; + + ICRVCOMPOPOVECTOR vOffOrig( vOffs.size()) ; + for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) { + vOffOrig[i].Set( vOffs[i]->Clone()) ; + } + + Point3d ptStart ; + if ( ! pCurve->GetStartPoint( ptStart)) + return false ; + + PtrOwner pCompo( pCurve->Clone()) ; + PtrOwner pCompoHelp( pCurve->Clone()) ; + PtrOwner pOffCheck( CreateCurveComposite()) ; + if ( IsNull( pCompoHelp) || IsNull( pCompo) || IsNull( pOffCheck)) + return false ; + + int nTypeOfCut = -1 ; + + for ( int i = 0; i < int( vOffOrig.size()) ; ++ i) { + + if ( vOffOrig[i]->IsPointOn( ptStart)) + pOffCheck.Set( vOffOrig[i]->Clone()) ; + + CRVCVECTOR ccClass ; + IntersCurveCurve intCC( *pCompo, *vOffOrig[i]) ; + intCC.GetCurveClassification( 0, 10 * EPS_SMALL, ccClass) ; + + if ( ! pCompoHelp->Clear()) + return false ; + + if ( ccClass.size() > 1) { + if ( ccClass[0].nClass == CRVC_IN) + nTypeOfCut = CRVC_OUT ; + else + nTypeOfCut = CRVC_IN ; + + for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { + if ( ccClass[j].nClass == nTypeOfCut) { + + Point3d ptS ; pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ; + double dOffS ; vOffOrig[i]->GetParamAtPoint( ptS, dOffS) ; + Point3d ptE ; pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ; + double dOffE ; vOffOrig[i]->GetParamAtPoint( ptE, dOffE) ; + // recupero i due possibili percorsi e uso il più corto + PtrOwner pCrvA( GetCurve( vOffOrig[i]->CopyParamRange( dOffS, dOffE))) ; + PtrOwner pCrvB( GetCurve( vOffOrig[i]->CopyParamRange( dOffE, dOffS))) ; + + if ( IsNull( pCrvA) || IsNull( pCrvB)) + return false ; + + double dLenA ; pCrvA->GetLength( dLenA) ; + double dLenB ; pCrvB->GetLength( dLenB) ; + + if ( dLenA < dLenB) { + + CRVCVECTOR ccClassA ; + IntersCurveCurve intCCA( *pCrvA, *pOffCheck) ; + intCCA.GetCurveClassification( 0, 10 * EPS_SMALL, ccClassA) ; + + if ( ccClassA.size() > 0 && i == vOffOrig.size() - 1 && ccClassA[0].nClass == CRVC_ON_M) { + if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) + return false ; + continue ; + } + + if ( ! pCompoHelp->AddCurve( Release( pCrvA))) + return false ; + } + else { + pCrvB->Invert() ; + + CRVCVECTOR ccClassB ; + IntersCurveCurve intCCB( *pCrvB, *pOffCheck) ; + intCCB.GetCurveClassification( 0, 10 * EPS_SMALL, ccClassB) ; + + if ( ccClassB.size() > 0 && i == vOffOrig.size() - 1 && ccClassB[0].nClass == CRVC_ON_M) { + if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) + return false ; + continue ; + } + + if ( ! pCompoHelp->AddCurve( Release( pCrvB))) + return false ; + } + } + else { + if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) + return false ; + } + } + + pCompo->Clear() ; + pCompo->AddCurve( pCompoHelp->Clone()) ; + } + } + + pCurve->Clear() ; + pCurve->AddCurve( Release( pCompo)) ; + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +GetNewCurvetWithCentroid( const ICurveComposite* pCrvH1, const ICurveComposite* pCrvH2, Point3d& ptC, + bool bCir, ICRVCOMPOPOVECTOR& VFirstOff, ICurveComposite* pCrvNewCurve) { + + PtrOwner pPath1( CreateCurveComposite()) ; + PtrOwner pPath2( CreateCurveComposite()) ; + if ( IsNull( pPath1) || IsNull( pPath2)) + return false ; + pCrvNewCurve->Clear() ; + Point3d ptS, ptE = ptC ; + Vector3d vtTanS, vtTanE ; + + if ( bCir) { // creo una circonferenza + // prendo il punto iniziale + if ( pCrvH1 == nullptr || pCrvH1->GetFirstCurve() == nullptr) { + if ( ! pCrvH2->GetStartPoint( ptS)) + return false ; + } + else { + if ( ! pCrvH1->GetEndPoint( ptS)) + return false ; + } + + // creo la circonferenza + if ( ! CalcBoundedSmootedLink( ptS, vtTanS, ptE, vtTanE, 0, VFirstOff, pPath1)) + return false ; + Vector3d vtS_cir ; pPath1->GetStartDir( vtS_cir) ; + Vector3d vtS_CrvH1 ; pCrvH1->GetEndDir( vtS_CrvH1) ; + if ( ! AreSameVectorApprox( vtS_cir, vtS_CrvH1)) + pPath1->Invert() ; + + // controllo che la circonferenza sia ammissibile ( solo che non esca dalle prime curve di Offset ) + Point3d ptCrvS ; pPath1->GetStartPoint( ptCrvS) ; + if ( ! CutCurveByOffsets( pPath1, VFirstOff)) // taglio la curva sugli Offsets + return false ; + + double dUCrvS ; + ModifyCurveToSmoothed( pPath1, 0.075, 0.075, true) ; + if ( pPath1->IsClosed()) { + pPath1->GetParamAtPoint( ptCrvS, dUCrvS) ; + pPath1->ChangeStartPoint( dUCrvS) ; + } + + // creo la nuova curva con la circonferenza + if ( pCrvH1 != nullptr && pCrvH1->GetFirstCurve() != nullptr) + if ( ! pCrvNewCurve->AddCurve( pCrvH1->Clone())) // aggiungo inizio + return false ; + if ( ! pCrvNewCurve->AddCurve( pPath1->Clone())) // aggiungo la circonferenza + return false ; + if ( pCrvH2 != nullptr && pCrvH2->GetFirstCurve() != nullptr) + if ( ! pCrvNewCurve->AddCurve(( pCrvH2->Clone()))) // aggiungo la fine + return false ; + } + else { // creo un BiArco + + // prendo il vettore tangente e il punto iniziale + if ( pCrvH1 == nullptr || pCrvH1->GetFirstCurve() == nullptr) { + if ( ! pCrvH2->GetStartDir( vtTanS) || + ! pCrvH2->GetStartPoint( ptS)) + return false ; + } + else { + if ( ! pCrvH1->GetEndDir( vtTanS) || + ! pCrvH1->GetEndPoint( ptS)) + return false ; + } + + // creo i due Biarchi + if ( ! CalcBoundedSmootedLink( ptS, vtTanS, ptE, vtTanS, 0.5, VFirstOff, pPath1) || + ! CalcBoundedSmootedLink( ptE, vtTanS, ptS, vtTanS, 0.5, VFirstOff, pPath2)) + return false ; + + // creo la nuova curva con il doppio Biarco + if ( pCrvH1 != nullptr && pCrvH1->GetFirstCurve() != nullptr) + if ( ! pCrvNewCurve->AddCurve( pCrvH1->Clone())) // aggiungo inizio + return false ; + if ( ! pCrvNewCurve->AddCurve( pPath1->Clone()) || ! pCrvNewCurve->AddCurve( pPath2->Clone())) // aggiungo i due BiArchi + return false ; + if ( pCrvH2 != nullptr && pCrvH2->GetFirstCurve() != nullptr) + if ( ! pCrvNewCurve->AddCurve(( pCrvH2->Clone()))) // aggiungo la fine + return false ; + } + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +ManageSmoothAndAutoInters( ICurveComposite* pCrv, ICurveComposite* pCrvPath, ICurveComposite* pPath1, + ICurveComposite* pPath2, ICRVCOMPOPOVECTOR& vOffsCL) { + + // controllo parametri + if ( pCrv == nullptr) + return false ; + + // check AutoIntersezioni... + SelfIntersCurve SICrv( *pCrv) ; + if ( SICrv.GetCrossOrOverlapIntersCount() > 0) { // se ci sono autointersezioni + + Vector3d vTanE ; Point3d ptHS ; + pCrvPath->GetEndDir( vTanE) ; + pCrvPath->GetEndPoint( ptHS) ; + + bool bFound = false ; + int nIter = -45 ; + + while ( ! bFound && nIter < 45) { + vTanE.Rotate( Z_AX, 90 - nIter) ; // vettore perpendicolare + PtrOwner pLine( CreateCurveLine()) ; + pLine->SetPVL( ptHS, vTanE, ( 2. * RAD) / 3 - 5 * EPS_SMALL) ; // segmento uscente + + CRVCVECTOR ccClass ; + IntersCurveCurve intCC( *pLine, *pCrv) ; + intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; + if (( int)ccClass.size() > 1) // se intersezione con parte precedente inverto la direzione + pLine->SetPVL( ptHS, - vTanE, ( 2. * RAD) / 3 - 5 * EPS_SMALL) ; + + if ( IsNull( pLine) || ! pLine->IsValid()) + break ; + + // punto finale segmento uscente + Point3d ptEndLine ; + pLine->GetEndPoint( ptEndLine) ; + Point3d ptNear ; + double dUS, dUE ; + pPath2->GetDomain( dUS, dUE) ; + PtrOwner pCrvHelper(( pPath2->CopyParamRange( dUS, dUE - 0.5))) ; + pCrvHelper->GetEndPoint( ptNear) ; + PtrOwner pLine1( GetLinePointTgCurve( ptEndLine, *pPath2, ptNear)) ; // segmento tangente + + if ( IsNull( pLine1) || ! pLine1->IsValid()) + break ; + + // creo la nuova curva formata da BiArco 1 - Path - Segmento uscente - Segmento tangente - parte restante di Biarco 2 + Point3d ptELine1 ; + pLine1->GetEndPoint( ptELine1) ; + pPath2->GetParamAtPoint( ptELine1, dUS) ; + PtrOwner pNewPath2( GetCurveComposite( pPath2->CopyParamRange( dUS, dUE))) ; + + // creo la curva formata dai due segmenti ma smussati tra loro... + PtrOwner pCrvTwoSeg( CreateCurveComposite()) ; + if ( ! pCrvTwoSeg->AddCurve( pCrvPath->Clone()) || + ! pCrvTwoSeg->AddCurve( pLine->Clone()) || + ! pCrvTwoSeg->AddCurve( pLine1->Clone())) + return false ; + + ModifyCurveToSmoothed( pCrvTwoSeg, 0.2, 0.2, true) ; + + PtrOwner ptNewCrv( CreateCurveComposite()) ; + if ( ptNewCrv->AddCurve( pPath1->Clone()) && + ptNewCrv->AddCurve( pCrvTwoSeg->Clone()) && + ptNewCrv->AddCurve( pNewPath2->Clone())) { + + SelfIntersCurve SICrvLoop( *ptNewCrv) ; + + if ( SICrvLoop.GetCrossOrOverlapIntersCount() == 0) { + pCrv->Clear() ; + pCrv->AddCurve( Release( ptNewCrv)) ; + bFound = true ; + } + else { // se trovo autointersezioni, ripeto cambiando l'angolo del segmento uscente + ++nIter ; + } + } + else + break ; + } + } + + // smusso questa curva sulle varie curve di offsets + Point3d ptCheck1, ptCheck2 ; + PtrOwner pCrvH( pCrv->Clone()) ; // copia della curva + + if ( ! CutCurveByOffsets( pCrv, vOffsCL)) { // taglio la curva sugli Offsets + pCrv->Clear() ; + pCrv->AddCurve( Release( pCrvH)) ; + } + else { // controllo che i punti iniziali coincidano + pCrv->GetStartPoint( ptCheck1) ; + pCrvH->GetStartPoint( ptCheck2) ; + if ( ! AreSamePointApprox( ptCheck1, ptCheck2)) { // se i punti iniziali della curva prima e dopo lo smusso non coincidono ... + pCrv->Clear() ; + pCrv->AddCurve( Release( pCrvH)) ; + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +GetNewCurvetWithPath( const ICurveComposite* pCrvH1, const ICurveComposite* pCrvH2, ICurveComposite* pCrvPath, + ICRVCOMPOPOVECTOR& VFirstOff, ICRVCOMPOPOVECTOR& VoffsCl, ICurveComposite* pCrvNewCurve) { + + PtrOwner pPath1( CreateCurveComposite()) ; + PtrOwner pPath2( CreateCurveComposite()) ; + if ( IsNull( pPath1) || IsNull( pPath2) || pCrvPath == nullptr) + return false ; + pCrvNewCurve->Clear() ; + Point3d ptS, ptE, ptH ; + Vector3d vtTanS, vtTanE, vtH ; + + if ( ! pCrvPath->GetStartPoint( ptE) || + ! pCrvPath->GetStartDir( vtTanE) || + ! pCrvPath->GetEndPoint( ptH) || + ! pCrvPath->GetEndDir( vtH)) + return false ; + + if ( pCrvH1 == nullptr || pCrvH1->GetFirstCurve() == nullptr) { + if ( ! pCrvH2->GetStartPoint( ptS) || + ! pCrvH2->GetStartDir( vtTanS)) + return false ; + } + else { + if ( ! pCrvH1->GetEndPoint( ptS) || + ! pCrvH1->GetEndDir( vtTanS)) + return false ; + } + + // creo i due Biarchi + if ( ! CalcBoundedSmootedLink( ptS, vtTanS, ptE, vtTanE, 0.5, VFirstOff, pPath1)) + if ( ! CalcBoundedLink( ptS, ptE, VFirstOff, pPath1)) + return false ; + if ( ! CalcBoundedSmootedLink( ptH, vtH, ptS, vtTanS, 0.5, VFirstOff, pPath2)) + if ( ! CalcBoundedLink( ptH, ptS, VFirstOff, pPath2)) + return false ; + + // creo la curva formata da BiArco1 - Path - BiArco 2 + PtrOwner pCrvToAdd( CreateCurveComposite()) ; + if ( IsNull( pCrvToAdd) || + ! pCrvToAdd->AddCurve( pPath1->Clone()) || + ! pCrvToAdd->AddCurve( pCrvPath->Clone()) || + ! pCrvToAdd->AddCurve( pPath2->Clone())) + return false ; + + // cerco di togliere le auto intersezioni tra i biarchi e le curve di Medial Axis + if ( ! ManageSmoothAndAutoInters( pCrvToAdd, pCrvPath, pPath1, pPath2, VoffsCl)) + return false ; + + // curva finale + if ( pCrvH1 != nullptr && pCrvH1->GetFirstCurve() != nullptr) + if ( ! pCrvNewCurve->AddCurve( pCrvH1->Clone())) // aggiungo inizio + return false ; + + if ( ! pCrvNewCurve->AddCurve( pCrvToAdd->Clone())) { // aggiungo BiArco - Path - BiArco + // nel caso non funzioni questo raccordo, recupero un biarco valido e uso questo ... + Vector3d vtHS, vtHE ; + Point3d ptHS, ptHE ; + pCrvNewCurve->GetEndPoint( ptHS) ; + pCrvNewCurve->GetEndDir( vtHS) ; + pCrvToAdd->GetStartPoint( ptHE) ; + pCrvToAdd->GetStartDir( vtHE) ; + PtrOwner pCrvBiArcHelper( CreateCurveComposite()) ; + if ( ! CalcBoundedSmootedLink( ptHS, vtHS, ptHE, vtHE, 0.5, VFirstOff, pCrvBiArcHelper)) + if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper)) + return false ; + + if ( ! pCrvNewCurve->AddCurve( Release( pCrvBiArcHelper)) || + ! pCrvNewCurve->AddCurve( pCrvToAdd->Clone())) + return false ; + } + if ( pCrvH2 != nullptr && pCrvH2->GetFirstCurve() != nullptr) { + if ( ! pCrvNewCurve->AddCurve( pCrvH2->Clone())) { // aggiungo fine + // nel caso non funzioni questo raccordo, recupero un biarco valido e uso questo ... + int nTry = 1 ; int nMaxTry = 10 ; bool bFound = false ; + while ( nTry < nMaxTry && ! bFound) { + Vector3d vtHS, vtHE ; + Point3d ptHS, ptHE ; + pCrvNewCurve->GetEndPoint( ptHS) ; + pCrvNewCurve->GetEndDir( vtHS) ; + pCrvH2->GetStartPoint( ptHE) ; + pCrvH2->GetStartDir( vtHE) ; + PtrOwner pCrvBiArcHelper( CreateCurveComposite()) ; + if ( ! CalcBoundedSmootedLink( ptHS, vtHS, ptHE, vtHE, 0.5, VFirstOff, pCrvBiArcHelper)) + if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper) ) + return false ; + + if ( ! pCrvNewCurve->AddCurve( pCrvBiArcHelper->Clone()) || + ! pCrvNewCurve->AddCurve( pCrvH2->Clone())) { + ++nTry ; + } + else { + bFound = true ; + } + } + if ( ! bFound) + return false ; + } + } + + return true ; +} + +//--------------------------------------------------------------------------- +static bool +GetCurveWeightInfo( const ICurveComposite* pCrvCompo, double dMaxLen, double& dToTRot, int& nSmallArcs, int& nSmallLines) +{ + + dToTRot = 0 ; nSmallArcs = 0 ; nSmallLines = 0 ; + if ( pCrvCompo == nullptr) + return true ; + + int nLines = 0 ; + PtrOwner pCrvLineOld( CreateCurveLine()) ; + if ( IsNull( pCrvLineOld)) + return false ; + + // scorro tutte le curve della composita + const ICurve* pMyCrv = pCrvCompo->GetFirstCurve() ; + while ( pMyCrv != nullptr) { + if ( pMyCrv->GetType() == CRV_ARC) { // nel caso di archi ... + nLines = 0 ; + PtrOwner pCrvArc( GetCurveArc( pMyCrv->Clone())) ; + double dAngCenter = abs( pCrvArc->GetAngCenter()) ; + dToTRot += abs( dAngCenter) ; + double dLen = 0 ; + if ( pCrvArc->GetLength( dLen) && dLen < dMaxLen) + ++ nSmallArcs ; + } + else if ( pMyCrv->GetType() == CRV_LINE) { // nel caso di linee ... + ++ nLines ; + if ( nLines == 1) + pCrvLineOld.Set( GetCurveLine( pMyCrv->Clone())) ; + else { + PtrOwner pNewMyCrv( GetCurveLine( pMyCrv->Clone())) ; + Vector3d vtNew, vtOld ; + pCrvLineOld->GetEndDir( vtOld) ; + pNewMyCrv->GetStartDir( vtNew) ; + double dAngCorner ; vtOld.GetAngle( vtNew, dAngCorner) ; + dToTRot += abs( dAngCorner) ; + pCrvLineOld.Set( pNewMyCrv) ; + nLines = 0 ; + } + + double dLen = 0 ; + if ( pMyCrv->GetLength( dLen) && dLen < dMaxLen) + ++ nSmallLines ; + } + pMyCrv = pCrvCompo->GetNextCurve() ; + } + + return true ; +} + +//--------------------------------------------------------------------------- +static bool +ChoosePath( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, int nP, double dPerP, double dMaxLen, int& nC) +{ + + // controllo dei parametri + if ( pCrv1 == nullptr || pCrv2 == nullptr || + ! ( nP == 0 || nP == 1) || dPerP > 1 || dPerP < 0 || dMaxLen < EPS_SMALL) + return false ; + + // # di archi piccoli tra le due curve + int dSmallArcs1 = 0 ; int dSmallArcs2 = 0 ; + // # di segmenti piccoli tra le due curve + int dSmallLines1 = 0 ; int dSmallLines2 = 0 ; + + // ------------- lunghezze -------------- + double dLen1 = 0 ; double dLen2 = 0 ; + pCrv1->GetLength( dLen1) ; + pCrv2->GetLength( dLen2) ; + + double CL1, CL2 ; // rapporto tra lunghezza corrente e massima + + if ( dLen1 < EPS_SMALL && dLen2 < EPS_SMALL) { + CL1 = 0 ; + CL2 = 0 ; + } + else { + CL1 = dLen1 / max( dLen1, dLen2) ; + CL2 = dLen2 / max( dLen1, dLen2) ; + } + + // ------------- rotazioni -------------- + double dRot1 = 0 ; double dRot2 = 0 ; + if ( ! GetCurveWeightInfo( pCrv1, dMaxLen, dRot1, dSmallArcs1, dSmallLines1) || + ! GetCurveWeightInfo( pCrv2, dMaxLen, dRot2, dSmallArcs2, dSmallLines2)) + return false ; + + double CR1, CR2 ; // rapporto tra la somma de rotazione degli angoli correnti e quella massima + + if ( dRot1 == 0 && dRot2 == 0) { + CR1 = 0 ; + CR2 = 0 ; + } + else { + CR1 = dRot1 / max( dRot1, dRot2) ; + CR2 = dRot2 / max( dRot1, dRot2) ; + } + + // funzione peso ( bilancio tra lunghezze e rotazioni complessive pesate ) + double Fp1 = ( nP == 0 ? dPerP : ( 1 - dPerP)) * ( CL1 + dSmallLines1) + ( nP == 0 ? ( 1 - dPerP) : dPerP) * ( CR1 + dSmallArcs1) ; + double Fp2 = ( nP == 0 ? dPerP : ( 1 - dPerP)) * ( CL2 + dSmallLines2) + ( nP == 0 ? ( 1 - dPerP) : dPerP) * ( CR2 + dSmallArcs2) ; + + // scelta della prima o della seconda curva + nC = ( Fp1 > Fp2 ? 1 : 0) ; + return true ; +} + +//---------------------------------------------------------------------------- +static bool +RemoveExtraParts( ISurfFlatRegion* pSrfToCut, ICRVCOMPOPOVECTOR& vOffs, ICRVCOMPOPOVECTOR& vOffsClosedCurves, + ICRVCOMPOPOVECTOR& vOffsFirstCurve, ICURVEPOVECTOR& vLinks) +{ + + if ( pSrfToCut == nullptr || ( int)vOffs.size() == 0) + return true ; + + // ciclo tutti i chunk della regione da tagliare + for ( int i = 0 ; i < pSrfToCut->GetChunkCount() ; ++ i) { + + // regione i-esima da rimuovere ( chunk i-esimo ) + PtrOwner pSrfChunkToCut( pSrfToCut->CloneChunk( i)) ; + if ( IsNull( pSrfChunkToCut)) + return false ; + + // Curva nel caso la regione si svuoti con MedialAxis + PtrOwner pCrvPath( CreateCurveComposite()) ; + if ( IsNull( pCrvPath)) + return false ; + + // nel caso la regione si svuoti con un centroide -> ptGoTo è il centroide + // nel caso la regione si svuoti con un medial Axis -> ptGoTo è il punto iniziale + // -> ptGoTo è il punto finale + Point3d ptGoTo, ptGoToI ; + // flag : 0 -> non faccio nulla | 1 -> svuoto con centroide | 2 -> svuoto con un percorso + int nOptFlag = 0 ; + + // ricavo il flag + // ptGoTo è il centroide, pCrvPath è la curva da seguire per svuotare la regione ( nel caso non bastasse il centroide) + if ( ! RemoveExtraPartByMedialAxis( pSrfChunkToCut, vOffsFirstCurve, nOptFlag, ptGoTo, pCrvPath) || + nOptFlag == 0) + continue ; + + if ( nOptFlag == 2 ) // se ho una curva di Medial Axis, ricavo il punto iniziale e finale + if ( ! pCrvPath->GetStartPoint( ptGoTo) || + ! pCrvPath->GetEndPoint( ptGoToI)) + continue ; + + // una volta trovata la curva di medial Axis ( se la regione non si svuota semplicemente passando per il + // centroide, controllo in quale direzione è più conveniente percorrerla ... ( I = Inverso) + PtrOwner pCrvH1( CreateCurveComposite()) ; + PtrOwner pCrvH2( CreateCurveComposite()) ; + PtrOwner pCrvH1I( CreateCurveComposite()) ; + PtrOwner pCrvH2I( CreateCurveComposite()) ; + if ( IsNull( pCrvH1) || IsNull( pCrvH2) || IsNull( pCrvH2) || IsNull( pCrvH2I)) + return false ; + int nIndexO = 0 ; + int nIndexOI = 0 ; + + // cerco il punto pià vicino a ptGoTo sugli tra i vari Offset ( escludendo il primo ) + // NB. PtGoTo è il centroide o il punto iniziale del percorso di medial Axis non invertito + if ( ! CutOffsetToClosestPoint( vOffs, ptGoTo, pCrvH1, pCrvH2, nIndexO)) + continue ; + if ( nOptFlag == 2) // se ho un percorso di Medial Axis, lo cerco anche per PtGoToI + if ( ! CutOffsetToClosestPoint( vOffs, ptGoToI, pCrvH1I, pCrvH2I, nIndexOI)) + continue ; + + // A) SE IL PUNTO PIU' VICINO E' SU UN ESTREMO DELL'OFFSET... -> Cerco un punto sui collegamenti + // ( controllo solo i punti trovati sugli offset mediante Medial Axis non invertiti) + if ( IsNull( pCrvH1) || IsNull( pCrvH2) || pCrvH1->GetCurveCount() == 0 || pCrvH2->GetCurveCount() == 0) { + bool bFound, bFoundI ; int nIndexL, nIndexLI ; + if ( ! CutLinkToClosestPoint( vLinks, vOffs, ptGoTo, pCrvH1, pCrvH2, bFound, nIndexL)) + continue ; + if ( nOptFlag == 2) { // nel caso curva di Medial Axis, controllo anche nel caso invertito + if ( ! CutLinkToClosestPoint( vLinks, vOffs, ptGoToI, pCrvH1I, pCrvH2I, bFoundI, nIndexLI)) + continue ; + } + + // link finale scelto... + PtrOwner ptCrvNewLink( CreateCurveComposite()) ; + if ( IsNull( ptCrvNewLink)) + return false ; + + if ( nOptFlag == 1) { // se svuoto con centroide, aggiusto il link con una circonferenza ( BiArco alla peggio) + if ( ! GetNewCurvetWithCentroid( pCrvH1, pCrvH2, ptGoTo, bFound, vOffsFirstCurve, ptCrvNewLink)) + continue ; + } + else if ( nOptFlag == 2) { // se svuoto con curva di medial Axis... + + bool bSucc = true ; bool bSuccI = true ; + + // ricavo il nuovo Offset con la curva di medial Axis aggiunta + if ( ! GetNewCurvetWithPath( pCrvH1, pCrvH2, pCrvPath, vOffsFirstCurve, vOffsClosedCurves, ptCrvNewLink)) + bSucc = false ; + + PtrOwner pCrvPathI( pCrvPath->Clone()) ; + pCrvPathI->Invert() ; + PtrOwner ptCrvNewLinkI( CreateCurveComposite()) ; + if ( IsNull( ptCrvNewLinkI)) + return false ; + + // ricavo il nuovo Offset con la curva di medial Axis Invertita aggiunta + if ( ! GetNewCurvetWithPath( pCrvH1I, pCrvH2I, pCrvPathI, vOffsFirstCurve, vOffsClosedCurves, ptCrvNewLinkI)) + bSuccI = false ; + + // scelgo il miglior percorso ottenuto ( sempre verificando che siano validi...) + int nC = 0 ; + if ( bSucc && bSuccI) { // se entrambi i percorsi sono validi allora li confronto + if ( ChoosePath( ptCrvNewLink, ptCrvNewLinkI, 0, 0.25, 5 * 1000 * EPS_SMALL, nC) && nC == 1) { + ptCrvNewLink.Set( ptCrvNewLinkI) ; + nIndexL = nIndexLI ; + } + } + else if ( ! bSucc) { // altrimenti tengo l'unico valido + ptCrvNewLink.Set( ptCrvNewLinkI) ; + nIndexL = nIndexLI ; + } + } + vLinks[nIndexL].Set( ptCrvNewLink) ; // setto il nuovo Link + } + else { // B) SE NON SONO AD UN ESTREMO DELLA CURVA DI OFFSET + + // nuovo Offset da restituire + PtrOwner ptCrvNewOffs( CreateCurveComposite()) ; + if ( IsNull( ptCrvNewOffs)) + return false ; + + if ( nOptFlag == 1) { // se svuoto con centroide, aggiusto il link con una circonferenza ( BiArco alla peggio) + if ( ! GetNewCurvetWithCentroid( pCrvH1, pCrvH2, ptGoTo, true, vOffsFirstCurve, ptCrvNewOffs)) + continue ; + } + else if ( nOptFlag == 2) { // nel caso aggiungo un medial Axis non agli estremi di un Offset + + bool bSucc = true ; bool bSuccI = true ; + + // come prima controllo il percorso normal ed invertito + if ( ! GetNewCurvetWithPath( pCrvH1, pCrvH2, pCrvPath, vOffsFirstCurve, vOffsClosedCurves, ptCrvNewOffs)) + bSucc = false ; + + PtrOwner pCrvPathI( pCrvPath->Clone()) ; + pCrvPathI->Invert() ; + PtrOwner ptCrvNewOffsI( CreateCurveComposite()) ; + if ( IsNull( ptCrvNewOffsI)) + return false ; + + if ( ! GetNewCurvetWithPath( pCrvH1I, pCrvH2I, pCrvPathI, vOffsFirstCurve, vOffsClosedCurves, ptCrvNewOffsI)) + bSuccI = false ; + + int nC = 0 ; // se entrambi i percorsi sono validi allora li confronto + if ( bSucc && bSuccI) { + if ( ChoosePath( ptCrvNewOffs, ptCrvNewOffsI, 0, 0.5, 5 * 1000 * EPS_SMALL, nC) && nC == 1) { + ptCrvNewOffs.Set( ptCrvNewOffsI) ; + nIndexO = nIndexOI ; + } + } + else if ( ! bSucc) { // altrimenti tengo l'unico valido + ptCrvNewOffs.Set( ptCrvNewOffsI) ; + nIndexO = nIndexOI ; + } + } + vOffs[nIndexO].Set( ptCrvNewOffs) ; // setto il nuovo Offset + } + } + + return true ; +} + + + +//---------------------------------------------------------------------------- +static bool +CalcSpiral( const ISurfFlatRegion* pSrfPock, double dRad, double dStep, bool bSmooth, int& nReg, Point3d& ptStart, + ICurveComposite* pMCrv, ICurveComposite* pRCrv) +{ + + // inizializzo i risultati + pMCrv->Clear() ; + pRCrv->Clear() ; + + // Offset corrente + double dOffs = dRad ; + + // recupero il versore normale della superificie, ruotandola nel piano XY + PtrOwner pSrfToWork( pSrfPock->Clone()) ; + if ( IsNull( pSrfToWork) || pSrfToWork->GetChunkCount() == 0) + return false; + + // porto il Chunk c-esimo nel sistema di riferimento locale + Vector3d vtExtr = pSrfToWork->GetNormVersor() ; + Point3d ptCen ; pSrfToWork->GetCentroid( ptCen) ; + Frame3d frPocket ; frPocket.Set( ptCen, vtExtr) ; + pSrfToWork->ToLoc( frPocket) ; + + // ciclo di offset verso l'interno + const int MAX_ITER = 1000 ; + int nIter = 0 ; + ICRVCOMPOPOVECTOR vOffs ; // vettore delle curve di offset + ICRVCOMPOPOVECTOR vOffsFirstCurve ; // curve di primo offset + + PtrOwner pSrfAct( CloneSurfFlatRegion( pSrfToWork)) ; // regione attuale + PtrOwner pSrfPrec( CloneSurfFlatRegion( pSrfToWork)) ; // regione precedente + if ( IsNull( pSrfAct) || IsNull( pSrfPrec) || pSrfAct->GetChunkCount() == 0 || pSrfPrec->GetChunkCount() == 0) + return false ; + + while ( nIter < MAX_ITER) { + // salvo la regione + pSrfPrec.Set( pSrfAct->Clone()) ; + if ( ! pSrfAct->Offset( - dOffs, ICurve::OFF_FILLET)) + return false ; + if ( ! pSrfAct->IsValid()) { + pSrfAct.Set( pSrfPrec->Clone()) ; + if ( ! pSrfAct->Offset( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) + return false ; + } + + // estraggo il contorno della FlatRegion (ciclo i Chunks e prendo i rispettivi Loops) + if ( nIter == 0) { + PtrOwner pSrfChunknReg( pSrfAct->CloneChunk( nReg)) ; + nReg = pSrfAct->GetChunkCount() ; // aggiorno il nuovo valore delle regioni + if ( IsNull( pSrfChunknReg)) // se supero i chunk ottenuti + return true ; + pSrfAct.Set( pSrfChunknReg) ; + } + + int nChunks = pSrfAct->GetChunkCount() ; // numero di Chunk attuali + + for ( int i = 0 ; i < nChunks ; ++ i) { + // per ogni chunk... + int nLoops = pSrfAct->GetLoopCount( i) ; + for ( int j = 0 ; j < nLoops ; ++ j) { + // per ogni loop... + PtrOwner ptoCCBorder( ConvertCurveToComposite( pSrfAct->GetLoop( i, j))) ; + if ( j > 0) // inverto l'orientamento delle curve interne (offset delle isole trovate) + ptoCCBorder->Invert() ; + // controllo quali regioni di Offset possono essere sostituite + bool bInsert = true ; + if ( bInsert) + vOffs.emplace_back( Release( ptoCCBorder)) ; + PtrOwner ptoCCExtBorder( ConvertCurveToComposite( pSrfAct->GetLoop( i, j))) ; + if ( nIter == 0) // salvo il bordo per i link (non invertiti) + vOffsFirstCurve.emplace_back( Release( ptoCCExtBorder)) ; + } + } + + bool bSmallRad = ( nIter == 0 ? dOffs < dRad + EPS_ZERO : dOffs < dRad + EPS_ZERO) ; + + if ( nChunks > 0) + dOffs = dStep ; + else if ( ! bSmallRad) { + pSrfAct.Set( pSrfPrec) ; + dOffs = ( nIter == 0 ? dRad : dRad) ; // superfluo, effettivamente non ho l'offset radiale + } + else + break ; + + ++ nIter ; + } + + // se non ho trovato curve di Offset allora esco + if (( int)vOffs.size() == 0) + return true ; + + // cambio il punto iniziale della prima Curva di Offset + AdjustContourStart( vOffs[0]) ; + + // smusso le curve di offset ( ad eccezione della prima, se richisto) + ICRVCOMPOPOVECTOR vOffsClosedCurves( vOffs.size()) ; // vettore con tutte le curve di Offset Chiuse + for ( int i = 0 ; i < int( vOffs.size()) ; ++i ) { + if ( i != 0) + ModifyCurveToSmoothed( vOffs[i], RAD / 8, RAD/8, false) ; + // imbroglio + if ( i != 0) + vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; + vOffsClosedCurves[i].Set( vOffs[i]->Clone()) ; + } + + for ( int i = 0 ; i < int( vOffsFirstCurve.size()) ; ++ i) { + if ( i != 0) + ModifyCurveToSmoothed( vOffsFirstCurve[i], RAD/8, RAD/8, false) ; + vOffsFirstCurve[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; + } + + // riordino le curve cambiando il loro punto di inizio e creando poi i collegamenti + int nClosestInd = -1 ; int nFlag ; + double dDist = INFINITO ; + Point3d ptHelp ; + ICURVEPOVECTOR vLinks( vOffs.size()) ; + + for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) { + + Point3d ptS ; + if ( ! vOffs[i]->GetStartPoint( ptS)) + return false ; + + // setto i default delle variabili + if ( ! DistPointCurve( ptS, *vOffs[i+1]).GetMinDistPoint( EPS_SMALL, ptHelp, nFlag)) + return false ; + dDist = INFINITO ; + + for ( int j = i + 1 ; j <= int( vOffs.size()) - 1 ; ++ j) { + // cerco il punto più vicino della curva + Point3d ptE ; + + if ( ! DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptE, nFlag)) + return false ; + + if ( dDist > Dist( ptS, ptE) ) { + dDist = Dist( ptS, ptE) ; + nClosestInd = j ; + ptHelp.Set( ptE.x, ptE.y, ptE.z) ; + } + } + // avendo la curva più vicina ... + + // 1) scambio la curva i con la curva nClosestInd + if ( nClosestInd != i + 1) { + PtrOwner ptoCCHelp( Release( vOffs[i + 1])) ; + vOffs[i + 1].Set( vOffs[nClosestInd]) ; + vOffs[nClosestInd].Set( ptoCCHelp) ; + } + + // 2) cambio il suo punto iniziale ... + double dU ; + Point3d ptNE( ptHelp.x, ptHelp.y, ptHelp.z) ; + if ( ! vOffs[i + 1]->GetParamAtPoint( ptNE, dU)) + return false ; + vOffs[i + 1]->ChangeStartPoint( dU) ; + + // 2.1) Accorcio la curva per velocizzare ... + if (( int)vOffs.size() > 1) { // accorcio se ho almeno due Offset + // copio le curve i e i+1 nel caso non riesca a tagliarle ... + double dUNS, dUNE ; + PtrOwner pCrvTest( CreateCurveComposite()) ; // possibile collegamento tra la curva i ed i+1 + PtrOwner pOff_i0( CloneCurveComposite( vOffs[i])) ; + PtrOwner pOff_i1( CloneCurveComposite( vOffs[i + 1])) ; + // cerco di tagliare le curve, ottenendo il collegamento + // NB. se la curva di offset è la prima, non devo tagliarla, accorcio solo le successive ... + if ( ! SMOOTH || ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsClosedCurves, vOffsFirstCurve, pCrvTest, i == 0 ? 0 : 0.01, 0.01, 2)) { + // se non sono riuscito, ritorno alla configurazione iniziale + PtrOwner pCrvLink( CreateCurveComposite()) ; + Vector3d vS, vE ; + vOffs[i].Set( pOff_i0) ; vOffs[i+1].Set( pOff_i1) ; + if ( ! vOffs[i]->GetStartDir( vS) || ! vOffs[i+1]->GetStartDir( vE)) + return false ; + if ( CalcBoundedSmootedLink( ptS, vS, ptNE, vE, 0.5, vOffsFirstCurve, pCrvLink)) + vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento + else + return false ; + + continue ; + } + + if ( ! pCrvTest->GetStartPoint( ptS) || + ! pCrvTest->GetEndPoint( ptNE) || + ! vOffs[i]->GetParamAtPoint( ptS, dUNS) || + ! vOffs[i+1]->GetParamAtPoint( ptNE, dUNE)) + return false ; + + // imposto il nuovo punto inziale della curva successiva + vOffs[i+1]->ChangeStartPoint( dUNE) ; + PtrOwner pCrvNewOffS( CloneCurveComposite( vOffs[i])) ; + if ( dUNS > EPS_SMALL) { // se parametro di trim sufficientemente grande + pCrvNewOffS.Set( GetCurveComposite( vOffs[i]->CopyParamRange( 0, dUNS))) ; + // sostituisco la curva i-esima con quella tagliata + vOffs[i]->Clear() ; + vOffs[i].Set( pCrvNewOffS) ; + } + // aggiorno il collegamento + vLinks[i + 1].Set( pCrvTest) ; + } + } + + // copio il vettore degli Offset per settare poi la Feed + ICRVCOMPOPOVECTOR vOffsFeed ; vOffsFeed.reserve(( int)vOffs.size()) ; + for ( int i = 0 ; i < ( int)vOffs.size() ; ++ i) + vOffsFeed.emplace_back( vOffs[i]->Clone()) ; + + // 3) controllo eventuali parti non svuotate... + PtrOwner pSrfToCut( CreateSurfFlatRegion()) ; + if ( GetUnclearedRegion( vOffsFirstCurve, vOffs, vLinks, pSrfToCut)) { + // 4) Modifico i percorsi + if ( ! RemoveExtraParts( pSrfToCut, vOffs, vOffsClosedCurves, vOffsFirstCurve, vLinks)) + return false ; + } + + // calcolo il percorso di ritorno + if (( int)vOffs.size() >= 2) { + pRCrv->Clear() ; + // punto inziale e finale | vettore iniziale e finale + Point3d ptStart ; vOffs.back()->GetEndPoint( ptStart) ; + Point3d ptEnd ; vOffs.front()->GetStartPoint( ptEnd) ; + Vector3d vtStart ; vOffs.back()->GetEndDir( vtStart) ; + Vector3d vtEnd ; vOffs.front()->GetStartDir( vtEnd) ; + + // calcolo il ritorno (garantendo che non esca dalla svuotatura) + PtrOwner pCrvLink( CreateCurveComposite()) ; + if ( CalcBoundedSmootedLink( ptStart, vtStart, ptEnd, vtEnd, 0.5, vOffsFirstCurve, pCrvLink)) { + pRCrv->AddCurve( Release( pCrvLink)) ; + pRCrv->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, false) ; + } + else + return false ; + + } + + // creo il percorso di lavoro a partire dalla raccolta degli offset e dei collegamenti + for ( int i = 0 ; i < int( vOffs.size()) ; ++i) { + // se collegamento da aggiungere + if ( ! IsNull( vLinks[i])) { + // accodo nel percorso di lavorazione + if ( ! pMCrv->AddCurve( vLinks[i]->Clone())) + return false ; + } + pMCrv->AddCurve( vOffs[i]->Clone()) ; + } + + // verifico il percorso di lavoro + if ( pMCrv->GetCurveCount() == 0) + return false ; + + // setto estrusione + pMCrv->SetExtrusion( vtExtr) ; + + // riporto tutto nel sistema di riferimento originale + pMCrv->ToGlob( frPocket) ; + pRCrv->ToGlob( frPocket) ; + ptStart.ToGlob( frPocket) ; + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +CheckSimpleOverlap( const ICurve* pCrv, const ICurveComposite* pCrvOri, int& nStat, double dToll) +{ + // controllo dei parametri + if (( pCrv == nullptr || pCrvOri == nullptr || dToll < EPS_SMALL)) + return false ; + nStat = 1 ; // 0 -> no overlap | 1 -> overlap + + // controllo se una sottocurva della composita è abbastanza vicina a tutti i punti trovati + const double nMAX = 10.0 ; + for ( int i = 0 ; i <= nMAX ; ++i) { + double dPar = i / nMAX ; + Point3d ptC ; pCrv->GetPointD1D2( dPar, ICurve::FROM_PLUS, ptC) ; + if ( ! pCrvOri->IsPointOn( ptC, dToll)) { + nStat = 0 ; + return true ; + } + } + + return true ; +} + +//---------------------------------------------------------- +static bool +CalcZigZagLink( ICurveComposite* pCrv1, ICurveComposite* pCrv2, ICurveComposite* pCrvLink) +{ + + // se non richiesto, esco + if ( ! SMOOTH) + return true ; + + // controllo dei parametri + if ( pCrv1 == nullptr || pCrv2 == nullptr) + return false ; + pCrvLink->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; + + // prendo i parametri per il raccordo + Vector3d vtS, vtE, vtH ; + pCrv1->GetEndDir( vtS) ; + pCrv2->GetStartDir( vtE) ; + Point3d ptS, ptE, ptE1, ptS2 ; + pCrv1->GetEndPoint( ptS) ; + pCrv2->GetStartPoint( ptE) ; + vtH = ptE - ptS ; + double dLen1, dLen2 ; + pCrv1->GetLength( dLen1) ; + pCrv2->GetLength( dLen2) ; + double dUS1, dUE1, dUS2, dUE2 ; + pCrv1->GetDomain( dUS1, dUE1) ; + pCrv2->GetDomain( dUS2, dUE2) ; + double dAngle, dAngleH ; + double dDist = Dist( ptS, ptE) ; + + // curve per controllo validità del collegamento + PtrOwner pCrvH1( CreateCurveComposite()) ; + PtrOwner pCrvH2( CreateCurveComposite()) ; + PtrOwner pCrvTempLink( CreateCurveComposite()) ; + PtrOwner pCrvTest( CreateCurveComposite()) ; + if ( IsNull( pCrvH1) || IsNull( pCrvH2) || IsNull( pCrvTempLink) || IsNull( pCrvTest)) + return false ; + + if ( vtS.GetAngle( vtE, dAngle) && + abs( dAngle) < ANG_STRAIGHT + 5 * EPS_SMALL && abs( dAngle) > ANG_STRAIGHT - 5 * EPS_SMALL && + vtH.GetAngle( vtE, dAngleH) && + abs( dAngleH) < ANG_RIGHT + 5 * EPS_SMALL && abs( dAngleH) > ANG_RIGHT - 5 * EPS_SMALL && + dLen1 > dDist && dLen2 > dDist && + pCrvLink->GetCurveCount() == 1 && pCrvLink->GetFirstCurve()->GetType() == CRV_LINE) { + + // creo una semicirconferenza + double dRad = dDist / 2 ; // raggio + Point3d ptNS, ptNE ; + pCrv1->GetParamAtLength( dLen1 - dRad, dUE1) ; + pCrv2->GetParamAtLength( dRad, dUS2) ; + pCrv1->GetPointD1D2( dUE1, ICurve::FROM_MINUS, ptNS) ; // parametro di intersezione su curva 1 + pCrv2->GetPointD1D2( dUS2, ICurve::FROM_PLUS, ptNE) ; // parametro di intersezione su curva 2 + + PtrOwner pSemiCir( CreateCurveArc()) ; + if ( pSemiCir->Set2PVN( ptNS, ptNE, vtS, Z_AX)) { + pCrvH1.Set( GetCurveComposite( pCrv1->CopyParamRange( dUS1, dUE1))) ; + pCrvH2.Set( GetCurveComposite( pCrv2->CopyParamRange( dUS2, dUE2))) ; + pCrvTempLink->AddCurve( pSemiCir->Clone()) ; + } + + // controllo finale sui raccordi ammissibili ... + if ( pCrvTest->AddCurve( pCrvH1->Clone()) && pCrvTest->AddCurve( pCrvTempLink->Clone()) && + pCrvTest->AddCurve( pCrvH2->Clone())) { + + if ( ! pCrv1->TrimEndAtParam( dUE1)) + pCrv1->Clear() ; + pCrvLink->Clear() ; + pCrvLink->AddCurve( Release( pCrvTempLink)) ; + if ( ! pCrv2->TrimStartAtParam( dUS2)) + pCrv2->Clear() ; + return true ; + } + } + else { + // devo smussare la curva formata da pCrv1 - pCrvLink ( da creare) - pCrv2 + pCrvH1->AddCurve( pCrv1->Clone()) ; + pCrvH2->AddCurve( pCrv2->Clone()) ; + pCrvTempLink->AddCurve( pCrvLink->Clone()) ; + + // prendo le lunghezze necessarie + double dLenSeg1 = EPS_SMALL ; double dLenSeg2 = EPS_SMALL ; + double dLenI_s = EPS_SMALL ; // lunghezza prima curva del Link + double dLenI_e = EPS_SMALL ; // lunghezza ultima curva del Link + pCrvH1->GetLength( dLenSeg1) ; + pCrvH2->GetLength( dLenSeg2) ; + pCrvTempLink->GetFirstCurve()->GetLength( dLenI_s) ; + pCrvTempLink->GetLastCurve()->GetLength( dLenI_e) ; + + // raccordo pSeg1 con pCrvLink + double dTollLeft = 1 - ( dLen1 - ( ( 2 * RAD) / 16)) / dLen1 ; // % parametro sinistro per smusso + double dTollRight = ( ( 2 * RAD) / 16) / dLenI_s ; // % parametro destro di smusso + PtrOwner pCrv_Seg1_Isl( CreateCurveComposite()) ; // curva unione di pSeg1 e pCrvLink smussata + pCrv_Seg1_Isl->AddCurve( pCrvH1->Clone()) ; + pCrv_Seg1_Isl->AddCurve( pCrvTempLink->Clone()) ; + ModifyCurveToSmoothed( pCrv_Seg1_Isl, dTollLeft, dTollRight, true) ; + + // raccordo questa curva con pSeg2 + double dUS_1IH, dUE_1IH, dToTLen ; + pCrv_Seg1_Isl->GetDomain( dUS_1IH, dUE_1IH) ; // dominio della curva smussata tra pSeg1 e PCrvLink + pCrv_Seg1_Isl->GetLength( dToTLen) ; // nuova lunghezza della curva smussata tra pSeg1 e pCrvLink + dTollLeft = 1 - ( dLenI_e - ( ( 2 * RAD) / 16)) / dLenI_e ; // % paramtro sinistro per smusso + dTollRight = ( ( 2 * RAD) / 16) / dLen2 ; // % parametro destro per smusso + PtrOwner pCrv_smoothed( CreateCurveComposite()) ; // curva unione del primo smusso con pSeg2 + pCrv_smoothed->AddCurve( pCrv_Seg1_Isl->Clone()) ; + pCrv_smoothed->AddCurve( pCrvH2->Clone()) ; + ModifyCurveToSmoothed( pCrv_smoothed, dTollLeft, dTollRight, true) ; + + // recupero i parametri del nuovo collegamento che si è creato dallo smusso + pCrvH1->Clear() ; + pCrvTempLink->Clear() ; + pCrvH2->Clear() ; + int nStat = -1 ; + for ( int u = 0 ; u < pCrv_smoothed->GetCurveCount() ; ++ u) { + // recupero la curva u-esima + const ICurve* pCrv = pCrv_smoothed->GetCurve( u) ; + if ( pCrv->GetType() == CRV_LINE) { + if ( CheckSimpleOverlap( pCrv, pCrv1, nStat, EPS_SMALL) && nStat == 1) + pCrvH1->AddCurve( pCrv->Clone()) ; + else if ( CheckSimpleOverlap( pCrv, pCrv2, nStat, EPS_SMALL) && nStat == 1) + pCrvH2->AddCurve( pCrv->Clone()) ; + } + } + + Point3d ptCrv1_e, ptCrv2_s ; + double dULink_s = EPS_SMALL ; + double dULink_e = EPS_SMALL ; + + pCrvH1->GetEndPoint( ptCrv1_e) ; + pCrv1->GetParamAtPoint( ptCrv1_e, dULink_s) ; + if ( ! pCrv1->TrimEndAtParam( dULink_s)) + pCrv1->Clear() ; + + pCrvH2->GetStartPoint( ptCrv2_s) ; + pCrv2->GetParamAtPoint( ptCrv2_s, dULink_e) ; + if ( ! pCrv2->TrimStartAtParam( dULink_e)) + pCrv2->Clear() ; + + pCrv_smoothed->GetParamAtPoint( ptCrv1_e, dULink_s) ; + pCrv_smoothed->GetParamAtPoint( ptCrv2_s, dULink_e) ; + pCrvLink->Clear() ; + pCrvLink->AddCurve( GetCurveComposite( pCrv_smoothed->CopyParamRange( dULink_s, dULink_e))) ; + + return true ; + } + + return false ; +} + +//---------------------------------------------------------------------------- +static bool +CalcZigZag( const ISurfFlatRegion* pSrfZigZag, ICRVCOMPOPOVECTOR& vpCrvs, double dStep) +{ + + // check parametri + if ( pSrfZigZag == nullptr || ! pSrfZigZag->IsValid()) + return true ; + + // creo il vettore dei contorni + ICRVCOMPOPOVECTOR vFirstOff ; + for ( int c = 0 ; c < pSrfZigZag->GetChunkCount() ; ++ c) { // sempre 1... + for ( int l = 0 ; l < pSrfZigZag->GetLoopCount( c) ;++ l) { + vFirstOff.emplace_back( GetCurveComposite( pSrfZigZag->GetLoop( c, l))) ; + } + } + + // ingombro del contorno offsettato + BBox3d b3Pocket ; + vFirstOff[0]->GetLocalBBox( b3Pocket) ; + Point3d ptMin ; double dDimX, dDimY, dDimZ ; + b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ; + + // lunghezza del contorno offsettato + double dLen ; vFirstOff[0]->GetLength( dLen) ; + + // passi in Y + int nYStep = static_cast< int >( ceil(( dDimY - 30 * EPS_SMALL) / dStep)) ; + double dYStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ; + int nRef = (( nYStep + ( 1)) % 2) ; + + // tratto valido + struct Section { + bool bActive ; + Point3d ptS ; + Point3d ptE ; + double dOs ; + double dOe ; + int nOffIndS ; + int nOffIndE ; + } ; + + struct ParIsland { + double dU ; + int nInd ; + } ; + + // raccolta di tratti + typedef vector> VECVECSECT ; VECVECSECT vvSec ; + vvSec.resize( nYStep + 1) ; + + // calcolo le linee di svuotatura + int nCount = 0 ; + for ( int i = 0 ; i <= nYStep ; ++ i) { + // determino senso + bool bPlus = (( i % 2) == nRef) ; + // definisco la linea + PtrOwner pLine( CreateCurveLine()) ; + const double EXP_LEN = 1.0 ; + Point3d ptStart( ptMin.x - EXP_LEN, ptMin.y + 10 * EPS_SMALL + i * dYStep, ptMin.z + dDimZ) ; + if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN)) + return false ; + + if ( ! bPlus) + pLine->Invert() ; + + // calcolo la classificazione della linea rispetto alla superificie + CRVCVECTOR ccClass ; + pSrfZigZag->GetCurveClassification( *pLine, EPS_SMALL, ccClass) ; + for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { + if ( ccClass[j].nClass == CRVC_IN) { // memorizzo il segmento + Section currSec; + currSec.bActive = true; + PtrOwner pSeg( GetCurveLine( pLine->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ; + Point3d ptS, ptE ; + pSeg->GetStartPoint( ptS) ; pSeg->GetEndPoint( ptE) ; + currSec.ptS = ptS ; currSec.ptE = ptE ; + for ( int k = 0 ; k < int( vFirstOff.size()) ; ++ k) { + if ( vFirstOff[k]->IsPointOn( ptS)) { + currSec.nOffIndS = k ; + double dUOS; vFirstOff[k]->GetParamAtPoint( ptS, dUOS) ; + currSec.dOs = dUOS ; + } + if ( vFirstOff[k]->IsPointOn( ptE)){ + currSec.nOffIndE = k ; + double dUOE; vFirstOff[k]->GetParamAtPoint( ptE, dUOE) ; + currSec.dOe = dUOE ; + } + } + vvSec[i].emplace_back( currSec) ; + ++nCount ; // numero di segmenti inseriti + } + } + } + + int nStatus = 0 ; // 0 -> inizio oppure ho inserito una parte di isola | 2 -> sto inserendo un segmento + Point3d ptS_ref ; + PtrOwner pLastSeg( CreateCurveComposite()) ; // ultimo segmento nel percorso + PtrOwner pCrvLink( CreateCurveComposite()) ; // ultimo link aggiunto + // vettore dei link aggiunti ( per Feed ) + ICRVCOMPOPOVECTOR vAddedLinks ; + bool bFirstLine = true ; // flag per prima linea di ogni percorso + + // creo i percorsi di svuotatura + vpCrvs.reserve( nCount) ; + int nI = -1, nJ = -1 ; + while ( true) { + // se sezione non valida + if ( nI < 0 || nJ < 0) { + // ricerco la prima valida ( il primo segmento con bActive messo a true) + for ( int k = 0 ; k < int( vvSec.size()) && nI < 0 ; ++ k) { + for ( int l = 0 ; l < int( vvSec[k].size()) && nJ < 0 ; ++ l) { + if ( vvSec[k][l].bActive) { + nI = k ; + nJ = l ; + } + } + } + // se trovata, creo nuova curva composita + if ( nI >= 0 && nJ >= 0) { + // creo la curva + vpCrvs.emplace_back( CreateCurveComposite()) ; + nStatus = 0 ; + // aggiungo punto iniziale + vpCrvs.back()->AddPoint( vvSec[nI][nJ].ptS) ; + bFirstLine = true ; + } + // altrimenti, esco + else + break ; + } + // determino senso + bool bPlus = (( nI % 2) == nRef) ; + // aggiungo la sezione alla curva + Section& Sec = vvSec[nI][nJ] ; + Sec.bActive = false ; + // creo i vettori con le linee Under e Above nella struttura della Section + ICURVEPOVECTOR vLineAbove ; + if ( nI < nYStep) { + for ( int a = 0 ; a < ( int)vvSec[nI + 1].size() ; ++ a) { + if ( vvSec[nI + 1][a].bActive) + continue ; + PtrOwner pLA( CreateCurveLine()) ; + pLA->Set( vvSec[nI + 1][a].ptS, vvSec[nI + 1][a].ptE) ; + vLineAbove.emplace_back( Release( pLA)) ; + } + } + ICURVEPOVECTOR vLineUnder ; + if ( nI > 0) { + for ( int u = 0 ; u < ( int)vvSec[nI - 1].size() ; ++ u) { + if ( vvSec[ nI - 1][u].bActive) + continue ; + PtrOwner pLU( CreateCurveLine()) ; + pLU->Set( vvSec[nI - 1][u].ptS, vvSec[nI - 1][u].ptE) ; + vLineUnder.emplace_back( Release( pLU)) ; + } + } + // creo la linea come curva composita, aggiungerò dei Joint per ogni tratto con Feed differente + PtrOwner pLineCompo( CreateCurveComposite()) ; + if ( IsNull( pLineCompo)) + return false ; + + Point3d ptE_l ; + if ( bFirstLine) + ptE_l = vvSec[nI][nJ].ptS ; + else + vpCrvs.back()->GetEndPoint( ptE_l) ; + + pLineCompo->AddPoint( ptE_l) ; + pLineCompo->AddLine( vvSec[nI][nJ].ptE) ; + bFirstLine = false ; + vpCrvs.back()->AddCurve( pLineCompo->Clone()) ; // aggiungo la curva al percorso + + if ( nStatus == 0) { // primo segmento per il raccordo smussato + pLastSeg.Set( pLineCompo) ; + pLastSeg->GetStartPoint( ptS_ref) ; + } + if ( nStatus == 2) { // ho precedentemente aggiunto il bordo di un'isola, quindi mi salvo il secondo segmento + + PtrOwner pSeg1( CloneCurveComposite( pLastSeg)) ; + PtrOwner pSeg2( CloneCurveComposite( pLineCompo)) ; + + if ( ! CalcZigZagLink( pSeg1, pSeg2, pCrvLink)) + return false ; + + // aggiorno il vettore delle curve ZigZag + Point3d ptE_Seg1 ; + pSeg1->GetEndPoint( ptE_Seg1) ; + double dUS_Link = EPS_SMALL ; + vpCrvs.back()->GetParamAtPoint( ptE_Seg1, dUS_Link) ; + if ( ! vpCrvs.back()->TrimEndAtParam( dUS_Link)) + vpCrvs.back()->Clear() ; + + vpCrvs.back()->AddCurve( pCrvLink->Clone()) ; // aggiungo il Link + vpCrvs.back()->AddCurve( pSeg2->Clone()) ; // aggiungo pSeg2 + + // aggiorno il vettore dei Link ( nel caso sia un link tra due segmenti sullo stesso livello ) + Point3d ptS1 ; + pSeg1->GetStartPoint( ptS1) ; + Point3d ptS2 ; + pSeg2->GetStartPoint( ptS2) ; + if ( abs( ptS1.y - ptS2.y) < 50 * EPS_SMALL) + vAddedLinks.emplace_back( pCrvLink->Clone()) ; + + // pSeg2 ora diventa pSeg1 e il procedimento si itera per tutto il percorso + pLastSeg.Set( Release( pSeg2)) ; + } + // cerco nella stessa fila o in quella successiva sezione successiva raccordabile tramite il contorno + double dUstart = Sec.dOe ; + int nIndexIslandE = Sec.nOffIndE ; // isola di arrivo + double dUmin, dUmax ; + vFirstOff[nIndexIslandE]->GetDomain( dUmin, dUmax) ; + double dUspan = dUmax - dUmin ; + double dUref = ( bPlus ? INFINITO : -INFINITO) ; + int nNextI = -1 ; + int nNextJ = -1 ; + int li = nJ + 1 ; + for ( int k = nI ; k <= nI + 1 && k < int( vvSec.size()) ; ++ k) { + for ( int l = li ; l < int( vvSec[k].size()) ; ++ l) { + if ( ! vvSec[k][l].bActive) // se sezione non attiva... non la considero + continue ; + if ( vvSec[k][l].nOffIndS != nIndexIslandE) // se isola diversa... non la considero + continue ; + double dU = vvSec[k][l].dOs ; + if ( bPlus) { + if ( dU < dUstart) + dU += dUspan ; + if ( dU < dUref) { + dUref = dU ; + nNextI = k ; + nNextJ = l ; + } + } + else { + if ( dU > dUstart) + dU -= dUspan ; + if ( dU > dUref) { + dUref = dU ; + nNextI = k ; + nNextJ = l ; + } + } + } + li = 0 ; + } + // se trovato, controllo il contorno dell'isola + if ( nNextI != -1) { + PtrOwner pCopy ; + if ( bPlus) { + if ( dUref > dUmax) + dUref -= dUspan ; + pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ; + if ( ! IsNull( pCopy)) { + double dCLen ; pCopy->GetLength( dCLen) ; + if ( dCLen > 0.5 * dLen) { + pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ; + if ( ! IsNull( pCopy)) + pCopy->Invert() ; + } + } + } + else { + if ( dUref < dUmin) + dUref += dUspan ; + pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ; + if ( ! IsNull( pCopy)) { + pCopy->Invert() ; + double dCLen; pCopy->GetLength( dCLen) ; + if ( dCLen > 0.5 * dLen) + pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ; + } + } + // controllo che nel percorso scelto non ritorni indietro superando la precendete passata + BBox3d b3Copy ; + if ( ! IsNull( pCopy)) + pCopy->GetLocalBBox( b3Copy) ; + if ( ! b3Copy.IsEmpty() && ( b3Copy.GetMax().y - b3Copy.GetMin().y) < dYStep + 10 * EPS_SMALL) { // tengo la curva (non ritorno indietro più dello step) + Point3d ptS, ptE ; + pCrvLink->Clear() ; + pCrvLink->AddCurve( pCopy->Clone()) ; + vpCrvs.back()->AddCurve( Release( pCopy)) ; + nStatus = 2 ; // aggiunta parte di isola + // aggiungo il Link + nI = nNextI ; + nJ = nNextJ ; + } + else { + nI = -1 ; + nJ = -1 ; + } + } + else { + nI = -1 ; + nJ = -1 ; + } + } + + return true ; +} + + + + +//---------------------------------------------------------------------------- +static bool +AddSpiralIn( const ISurfFlatRegion* pSrfPock, double dRad, double dStep, bool bSmooth, ICRVCOMPOPOVECTOR& vCrvCompoRes) +{ + + // ciclo sui chunk della superificie da svuotare + for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; ++ c) { + + // copio il chunk c-esimo + PtrOwner pSrfChunk( pSrfPock->CloneChunk( c)) ; + if ( IsNull( pSrfChunk)) + return false ; + + const int MAX_REGS = 50 ; + int nReg = 0 ; // chunk nuovo corrente da svuotare + + // ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-esimo + while ( nReg < MAX_REGS) { + // calcolo la spirale dall'esterno all'interno e la curva che unisce inizio e fine + PtrOwner pMCrv( CreateCurveComposite()) ; // percorso di svuotatura + PtrOwner pRCrv( CreateCurveComposite()) ; // percorso di ritorno + if ( IsNull( pMCrv) || IsNull( pRCrv)) + return false ; + + int nRegTot = nReg ; + Point3d ptStart ; + // calcolo il percorso di svuotatura + if ( ! CalcSpiral( pSrfChunk, dRad, dStep, bSmooth, nRegTot, ptStart, pMCrv, pRCrv)) + return false ; + + // se terminate le regioni, esco + if ( pMCrv->GetCurveCount() == 0) + break ; // passo al chunk originale successivo + + // inserisco le curve nel vettore + vCrvCompoRes.emplace_back( Release( pMCrv)) ; + ++nReg ; + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +AddSpiralOut( const ISurfFlatRegion* pSrfPock, double dRad, double dStep, bool bSmooth, ICRVCOMPOPOVECTOR& vCrvCompoRes) +{ + + // ciclo sui chunk della superificie da svuotare + for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; ++ c) { + + // copio il chunk c-esimo + PtrOwner pSrfChunk( GetSurfFlatRegion( pSrfPock->CloneChunk( c))) ; + if ( IsNull( pSrfChunk)) + return false ; + + const int MAX_REGS = 50 ; + int nReg = 0 ; + + while ( nReg < MAX_REGS) { + + // calcolo la spirale dall'interno all'esterno + PtrOwner pMCrv( CreateCurveComposite()) ; + PtrOwner pRCrv( CreateCurveComposite()) ; + if ( IsNull( pMCrv) || IsNull( pRCrv)) + return false ; + + int nRegTot = nReg ; + Point3d ptStart ; + + if ( ! CalcSpiral( pSrfChunk, dRad, dStep, bSmooth, nRegTot, ptStart, pMCrv, pRCrv)) + return false ; + + // se terminate le regioni, esco + if ( pMCrv->GetCurveCount() == 0) + break ; // passo al chunk originale successivo + + // inverto i percorsi, perchè sono calcolati dall'esterno all'interno (solo nel caso non ottimizzato) + pMCrv->Invert() ; + pRCrv->Invert() ; + + // inserisco le curve nel vettore + vCrvCompoRes.emplace_back( Release( pMCrv)) ; + ++nReg ; + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +AddZigZag( const ISurfFlatRegion* pSrfPock, double dRad, double dStep, double dAngle, bool bSmooth, ICRVCOMPOPOVECTOR& vCrvCompoRes) +{ + + // Offset della regione per curva ZigZag + double dOffs = dRad ; + double dExtra = min( 0.1 * ( 2.0 * dRad), 2.0) ; + + // ciclo su tutti i chunks della superificie originale + for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; ++ c) { + + // copio il chunk c-esimo + PtrOwner pSrfChunk(( pSrfPock->CloneChunk( c))) ; + if ( IsNull( pSrfChunk)) + return false ; + + // determino il riferimento in base alla svuotatura ( per poter orientare il frame per m_dSideAngle) + Frame3d frPocket ; + Point3d ptCen ; pSrfChunk->GetCentroid( ptCen) ; + frPocket.Set( ptCen, pSrfChunk->GetNormVersor()) ; + frPocket.Rotate( ptCen, pSrfChunk->GetNormVersor(), dAngle) ; + + // porto la superificie nel nuovo sistema di riferimento + pSrfChunk->ToLoc( frPocket) ; + + // creo la regione per il percorso a ZigZag + PtrOwner pSrfZigZag( CloneSurfFlatRegion( pSrfChunk)) ; + if ( ! pSrfZigZag->Offset( - dOffs - dExtra, ICurve::OFF_FILLET)) + return false ; + + // vettore con i percorsi a ZigZag + ICRVCOMPOPOVECTOR vpCrvs ; + + // ciclo sui Chunk ottenuti... + for ( int cfz = 0 ; cfz < pSrfZigZag->GetChunkCount() ; ++ cfz) { + // considero un chunk per volta... + PtrOwner pSrfZigZagChunk( pSrfZigZag->CloneChunk( cfz)) ; + if ( IsNull( pSrfZigZagChunk)) + return false ; + // calcolo i percorsi di ZigZag + if ( ! CalcZigZag( pSrfZigZagChunk, vpCrvs, dStep)) + return false ; + // aggiorno il vettore delle curve risultati + for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) { + vpCrvs[u]->ToGlob( frPocket) ; // riportando prima in Globale + vCrvCompoRes.emplace_back( Release( vpCrvs[u])) ; + } + // libero il vettore di curve ZigZag per il chunk successivo + vpCrvs.clear() ; + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +OptimizeChunkOneWay( ISurfFlatRegion* pSrfOrig, ISURFFRPOVECTOR& vSrfIdeal) +{ + // controllo parametri + if ( pSrfOrig == nullptr || pSrfOrig->GetChunkCount() == 0) + return false ; + vSrfIdeal.clear() ; + + // clono la superificie passata alla funzione + PtrOwner pSrfToPock( CloneSurfFlatRegion( pSrfOrig)) ; + if ( IsNull( pSrfToPock) || pSrfToPock->GetChunkCount() == 0) + return false ; + + // classifico i chunks in modo da creare delle regioni ideali; una regione ideale è formata da un chunk principale + // con tutti gli altri contenuti in esso ( in questo modo ottimizzo i percorsi per i bordi) + + INTVECTOR vChunksAvailable( pSrfToPock->GetChunkCount(), 1) ; + + for ( int c = 0 ; c < pSrfToPock->GetChunkCount() ; ++ c) { + PtrOwner pSrfIdeal( CreateSurfFlatRegion()) ; + if ( IsNull( pSrfIdeal)) + return false ; + if ( vChunksAvailable[c] == 1) { // se Chunk valido... + // prendo la curva esterna + PtrOwner pCrvExt( ConvertCurveToComposite( pSrfToPock->GetLoop( c, 0))) ; + if ( IsNull( pCrvExt)) + return false ; + // inserisco il chunk c-esimo ( curva per curva, così non perdo le temp prop) + pSrfIdeal->AddExtLoop( pCrvExt->Clone()) ; // ... inizio a creare la regione ideale da esso + for ( int l = 1 ; l < pSrfToPock->GetLoopCount( c) ; ++ l) + pSrfIdeal->AddIntLoop( ConvertCurveToComposite( pSrfToPock->GetLoop( c, l))) ; + // il chunk c-esimo non è più disponibile... + vChunksAvailable[c] = -1 ; + // scorro tutti gli altri chunk i-esimi ancora disponibili + for ( int i = 0 ; i < int( vChunksAvailable.size()) ; ++ i) { + if ( vChunksAvailable[i] == 1) { + // prendo la curva esterna del chunk i-esimo disponibile + PtrOwner pCrvExtC( ConvertCurveToComposite( pSrfToPock->GetLoop( i, 0))) ; + if ( IsNull( pCrvExtC)) + return false ; + // classifico i bordi esterni ( se il bordo del chunk c-esimo contiene o è contenuto nel loop + // esterno del chunk i-esimo -> sono chunks appartenenti alla stessa superficie ideale) + IntersCurveCurve intCC( *pCrvExtC, *pCrvExt) ; + CRVCVECTOR ccClass, ccClass1 ; + intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; + intCC.GetCurveClassification( 1, EPS_SMALL, ccClass1) ; + if (( ccClass.size() == 1 && ccClass[0].nClass == CRVC_IN) || + ( ccClass1.size() == 1 && ccClass1[0].nClass == CRVC_IN)) { + // inserisco il chunk i-esimo ( curva per curva, così non perdo le temp prop) + pSrfIdeal->AddExtLoop( ConvertCurveToComposite( pSrfToPock->GetLoop( i, 0))) ; + for ( int l = 1 ; l < pSrfToPock->GetLoopCount( i) ; ++ l) + pSrfIdeal->AddIntLoop( ConvertCurveToComposite( pSrfToPock->GetLoop( i, l))) ; + vChunksAvailable[i] = -1 ; // chunk j-esimo non più disponibile + // NB: Se il chunk i-esimo contiene la pSrfIdeal ( quindi chunk i-esimo unito ad eventuali altri chunk + // j-esimi precedenti -> prendo come bordo esterno di riferimento il suo e ricontrollo dall'inizio i + // chunk disponibili + if (( int)ccClass1.size() == 1 && ccClass1[0].nClass == CRVC_IN) { + pCrvExt.Set( pCrvExtC->Clone()) ; + i = 0 ; + } + } + } + } + } + + if ( pSrfIdeal->GetChunkCount() > 0) + vSrfIdeal.emplace_back( Release( pSrfIdeal)) ; + + } + + return ( int( vSrfIdeal.size() != 0)) ; +} + + +//---------------------------------------------------------------------------- +static bool +AddOneWay( const ISurfFlatRegion* pSrfPock, double dRad, double dStep, double dAngle, bool bSmooth, + ICRVCOMPOPOVECTOR& vCrvCompoRes) +{ + + double dOffs = dRad ; + double dExtra = min( 0.1 * ( 2. * dRad), 1.0) ; + + // copio la regione da svuotare + PtrOwner pSrfToPock( pSrfPock->Clone()) ; + if ( IsNull( pSrfToPock)) + return false ; + + // creo le regioni ideali -------------------------------------------- + // creo un frame di riferimento della svuotatura + Frame3d frLoc ; + Point3d ptC ; pSrfToPock->GetCentroid( ptC) ; + Vector3d vtN = pSrfToPock->GetNormVersor() ; + frLoc.Set( ptC, vtN) ; + pSrfToPock->ToLoc( frLoc) ; + + ISURFFRPOVECTOR vSrfFlat ; // vettore delle regioni ideali + if ( ! OptimizeChunkOneWay( pSrfToPock, vSrfFlat)) + return false ; + + // riporto le superifici ideali nel sistema di riferimento globale ( Ogni chunk verrà poi messo nel suo frame) + for ( int i = 0 ; i < ( int)vSrfFlat.size() ; ++ i) + vSrfFlat[i]->ToGlob( frLoc) ; + // ------------------------------------------------------------------ + + // scorro le regioni ideali + for ( int nIs = 0 ; nIs < int( vSrfFlat.size()) ; ++ nIs) { + + // copio la superificie ideale + PtrOwner pSrfIdeal( CloneSurfFlatRegion( vSrfFlat[nIs])) ; + if ( IsNull( pSrfIdeal)) + return false ; + + // effettuo Offset interno + if ( ! pSrfIdeal->Offset( - dOffs, ICurve::OFF_FILLET)) + return false ; + + // creo un frame coerente con i parametri + Frame3d frPocket ; + Point3d ptCen ; pSrfIdeal->GetCentroid( ptCen) ; + frPocket.Set( ptCen, pSrfIdeal->GetNormVersor()) ; + frPocket.Rotate ( ptCen, pSrfIdeal->GetNormVersor(), dAngle) ; + pSrfIdeal->ToLoc( frPocket) ; + BBox3d b3Pocket ; pSrfIdeal->GetLocalBBox( b3Pocket) ; + Point3d ptMin ; double dDimX, dDimY, dDimZ ; + b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ; + + // passi in Y + int nYStep = static_cast( ceil(( dDimY + 2 * dExtra) / dStep)) ; + double dYStep = ( nYStep > 0 ? ( dDimY + 2 * dExtra) / nYStep : 0) ; + --nYStep ; + + // vettore dei segmenti al di sotto della linea corrente + ICURVEPOVECTOR vLineUnder ; + ICURVEPOVECTOR vLineAbove ; + ICRVCOMPOPOVECTOR vCrvLink ; + + // calcolo le linee di svuotatura + const double EXP_LEN = 1.0 ; + for ( int i = 1 ; i <= nYStep ; ++ i) { + + // definisco la linea + PtrOwner pLine( CreateCurveLine()) ; + Point3d ptStart( ptMin.x - EXP_LEN, ptMin.y + ( - dExtra + i * dYStep), ptMin.z + dDimZ) ; + if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN)) + return false ; + + // vettore di tutti i segmenti lineari che si formano nello step nYStep + ICURVEPOVECTOR vAddedLines ; + + // riempio il vettore di segmenti + CRVCVECTOR ccClassSeg ; + pSrfIdeal->GetCurveClassification( *pLine, EPS_SMALL, ccClassSeg) ; + for ( int w = 0 ; w < int( ccClassSeg.size()) ; ++w) { + if ( ccClassSeg[w].nClass == CRVC_IN) { + + PtrOwner pCrvSeg( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ; + double duF, dLen ; + // accorcio leggermente il segmento per non toccare la prima curva di Offset + if ( ! pCrvSeg->GetLength( dLen) || + dLen < 2 * dExtra || + ! pCrvSeg->GetParamAtLength( dLen - dExtra, duF) || + ! pCrvSeg->TrimStartAtLen( dExtra) || + ! pCrvSeg->TrimEndAtParam( duF)) + pCrvSeg.Set( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ; + + PtrOwner pCrvSegCompo( CreateCurveComposite()) ; + if ( IsNull( pCrvSegCompo)) + return false ; + pCrvSegCompo->AddCurve( Release( pCrvSeg)) ; + pCrvSegCompo->ToGlob( frPocket) ; + vCrvCompoRes.emplace_back( Release( pCrvSegCompo)) ; + } + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +CalcPocketing( const ISurfFlatRegion *pSfr, double dRad, double dStep, double dAngle, int nType, bool bSmooth, + ICRVCOMPOPOVECTOR& vCrvCompoRes) +{ + // controllo dei parametri + if ( pSfr == nullptr || ! pSfr->IsValid() || + dRad < EPS_SMALL || + dStep < EPS_SMALL || + ( nType != POCKET_SUB_ZIGZAG && nType != POCKET_SUB_ONEWAY && nType != POCKET_SUB_SPIRALIN && nType != POCKET_SUB_SPIRALOUT)) + return false ; + vCrvCompoRes.clear() ; // pulizia vettore delle curve elementari + TYPE = nType ; + RAD = dRad ; + SMOOTH = bSmooth ; + + // calcolo delle curve elementari della superificie + switch ( nType) { + case POCKET_SUB_ZIGZAG: + if ( ! AddZigZag( pSfr, dRad, dStep, dAngle, bSmooth, vCrvCompoRes)) + return false ; + break ; + case POCKET_SUB_ONEWAY: + if ( ! AddOneWay( pSfr, dRad, dStep, dAngle, bSmooth, vCrvCompoRes)) + return false ; + break ; + case POCKET_SUB_SPIRALIN: + if ( ! AddSpiralIn( pSfr, dRad, dStep, bSmooth, vCrvCompoRes)) + return false ; + break ; + case POCKET_SUB_SPIRALOUT: + if ( ! AddSpiralOut( pSfr, dRad, dStep, bSmooth, vCrvCompoRes)) + return true ; + break ; + } + + return true ; +} \ No newline at end of file diff --git a/EgtGeomKernel.rc b/EgtGeomKernel.rc index 0aa6c48..0ddc02e 100644 Binary files a/EgtGeomKernel.rc and b/EgtGeomKernel.rc differ diff --git a/EgtGeomKernel.vcxproj b/EgtGeomKernel.vcxproj index d61c1f0..eadab1e 100644 --- a/EgtGeomKernel.vcxproj +++ b/EgtGeomKernel.vcxproj @@ -280,6 +280,7 @@ copy $(TargetPath) \EgtProg\Dll64 + diff --git a/EgtGeomKernel.vcxproj.filters b/EgtGeomKernel.vcxproj.filters index 6c34969..4370f17 100644 --- a/EgtGeomKernel.vcxproj.filters +++ b/EgtGeomKernel.vcxproj.filters @@ -495,6 +495,9 @@ File di origine\Geo + + File di origine\GeoOffset +