//---------------------------------------------------------------------------- // 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 "CurveLine.h" #include "CurveArc.h" #include "CurveComposite.h" #include "BiArcs.h" #include "SurfFlatRegion.h" #include "GeoConst.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_ZIGZAG = 0, POCKET_ONEWAY = 1, POCKET_SPIRALIN = 2, POCKET_SPIRALOUT = 3 } ; // variabili d'appoggio ( per non passare troppi parametri tra le funzioni secondarie) static int s_nType = 0 ; static double s_dRad = 0 ; static bool s_bSmooth = false ; //---------------------------------------------------------------------------- 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 ; // lunghezza massima int nCurrInd = 0 ; // indice della curva scelta bool bStop = false ; // flag per stoppare la ricerca della curva // mettendo chiuse tutte le curve ( in questo caso non servono i lati aperti) for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) { pCompo->SetCurveTempProp( u, 0, 0) ; pCompo->SetCurveTempProp( u, 0, 1) ; } // miglioro la curva per la ricerca pCompo->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; // scorro tutte le curve 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) { // ricavo 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) ; // in questo caso mi fermo al primo per semplicità break ; } return true ; } //---------------------------------------------------------------------------- static bool ModifyCurveToSmoothed( ICurveComposite* pCrv, double dRightLen, double dLeftLen, bool bAsParam) { // se non richiesto non faccio nulla if ( ! s_bSmooth) 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 ; } // ricavo i valori parametrici associati 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 ; // se valori trattati come parametri... 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 ( limite superiore valido) int nMaxTestForArcs = 3 ; // tentativi per creare l'arco int nIterForArcs = 0 ; // numero di intersezioni con altre curve bool IntersBTWArcs = false ; // caso particolare in cui due archi di raccordo si intersecano causa curva piccola // 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( CreateBasicCurveComposite()) ; 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) ; } // restituisco pCrv->Clear() ; pCrv->AddCurve( Release( pCrvCO_temp)) ; return true ; } //---------------------------------------------------------------------------- static bool CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, ICRVCOMPOPOVECTOR& vOffIslands, ICurveComposite* pCrvLink) { // pulisco 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 pCompoLine( CreateBasicCurveComposite()) ; if ( ! pCompoLine->AddPoint( ptStart) || ! pCompoLine->AddLine( ptEnd)) return false ; pCompoLine->SetExtrusion( vtExtr) ; // la porto in locale al riferimento della svuotatura CurveLocal LineLoc( pCompoLine, 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( pCompoLine->Clone()) ; if ( IsNull( pCompo) ) return false ; if ( ! pCompo->LocToLoc( GLOB_FRM, frLoc) ) return false; // memorizzo il tratto lineare nel caso qualche operazione fallisse PtrOwner pCompoHelp( pCompoLine->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 ; // per ogni intersezione j con l'offset dell'isola i for ( int j = 0 ; j < int( ccClass.size()) ; ++j ) { // se ho intersezione spezzo il segmento if ( ccClass[j].nClass == CRVC_OUT) { 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( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ; PtrOwner pCrvB( 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)) ; } } // se non interseco else { 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) { // controllo dei parametri if ( pBiArcLink == nullptr) return false ; if ( dToll > 0.99 || dToll < 0.01) return false ; pNewBiArc->Clear() ; // dominio double dUS, dUE ; pBiArcLink->GetDomain( dUS, dUE) ; // prendo i due archi della curva BiArco PtrOwner pArc1( pBiArcLink->CopyParamRange( dUS, ( dUS + dUE) / 2)) ; PtrOwner pArc2( 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( pArc1->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1 PtrOwner pArc1B( pArc1->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2 Point3d pt1A, pt1B ; pArc1A->GetEndPoint( pt1A) ; pArc1B->GetStartPoint( pt1B) ; PtrOwner pLine1( CreateBasicCurveLine()) ; pLine1->Set( pt1A, pt1B) ; // secondo pezzo pArc2->GetDomain( dUS, dUE) ; PtrOwner pArc2A( pArc2->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1 PtrOwner pArc2B( pArc2->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2 Point3d pt2A, pt2B ; pArc2A->GetEndPoint( pt2A) ; pArc2B->GetStartPoint( pt2B) ; PtrOwner pLine2( CreateBasicCurveLine()) ; pLine2->Set( pt2A, pt2B) ; // ricostruisco il nuovo link 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 ( ! s_bSmooth) 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( CreateBasicCurveArc()) ; 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( CreateBasicCurveComposite()) ; 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 ; // per ogni intersezione j con l'offset dell'isola i for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { // se ho intersezione spezzo il segmento if ( ccClass[j].nClass == CRVC_OUT && ccClass.size() > 1) { 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( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ; PtrOwner pCrvB( 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) ; } } // se non interseco else { 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 ; } } // smusso e restituisco 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() ; // curva finale da restituire PtrOwner ptCrvFinal( CreateBasicCurveComposite()) ; 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 ... ( si per bordi esterni che per isole) 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 < EPS_SMALL ? 0. : s_dRad) ; double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : s_dRad) ; dLenE = 0 ; // calcolo i possibili BiArchi tra le due curve int nIter = 0 ; // taglio la curva al massimo nMaxIter-volte e mi fermo al taglio più opportuno while ( nIter < nMaxIter) { // calcolo il BiArco PtrOwner ptBiArc( CreateBasicCurveComposite()) ; 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.empty() || vCrvs.size() < vFirstOffs.size()) return false ; pSrfToCut->Clear() ; // 1) creo la regione esterna PtrOwner pSrfExtern( CreateBasicSurfFlatRegion()) ; 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 ; } } // 2) Creo la regione svuotata dal Tool negli Offset, nei Links e sia dagli Offsets che dai Links PtrOwner pSrfTool_Offs( CreateBasicSurfFlatRegion()) ; PtrOwner pSrfTool_Links( CreateBasicSurfFlatRegion()) ; PtrOwner pSrfTool( CreateBasicSurfFlatRegion()) ; 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 = ( s_nType == POCKET_SPIRALIN ? 0 : ( int)vCrvs.size() - 1) ; ( s_nType == POCKET_SPIRALIN ? i < int( vCrvs.size()) : i >= 0) ; ( s_nType == POCKET_SPIRALIN ? ++ i : -- i)) { // ================= LINK ================================ if ( i <= int( vLinks.size()) && ! IsNull( vLinks[i]) && vLinks[i]->IsValid()) { // prendo il Link i-esimo come composita ( potrebbe essere tratto lineare) PtrOwner pCompoLink_i( CreateBasicCurveComposite()) ; 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), s_dRad + 5 * EPS_SMALL, false, false)) ; if ( ! IsNull( pSrfToolRegLinki)) { if ( ! pSrfTool_Links->IsValid() || pSrfTool_Links->GetChunkCount() == 0) pSrfTool_Links.Set( GetBasicSurfFlatRegion( Release( 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 superficie svuotata PtrOwner pCrvOffs_i( CloneBasicCurveComposite( vCrvs[i])) ; if ( IsNull( pCrvOffs_i)) return false ; PtrOwner pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( Release( pCrvOffs_i) , s_dRad + 5 * EPS_SMALL, false, false)) ; if ( ! IsNull( pSrfToolRegOffi)) { if ( ! pSrfTool_Offs->IsValid() || pSrfTool_Offs->GetChunkCount() == 0) pSrfTool_Offs.Set( GetBasicSurfFlatRegion( Release( pSrfToolRegOffi))) ; else pSrfTool_Offs->Add( *pSrfToolRegOffi) ; } } // 3) Calcolo la superficie 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) { // controllo dei parametri if ( pSrfOrig == nullptr) return true ; // superficie da restituire PtrOwner pSrfRes( CreateBasicSurfFlatRegion()) ; if ( IsNull( pSrfRes)) return false ; // scorro tutti i chunk tranne il primo ( escludo quindi il primo chunk) for ( int c = 1 ; c < pSrfOrig->GetChunkCount() ; ++ c) { PtrOwner pSrfHelp( CreateBasicSurfFlatRegion()) ; if ( IsNull( pSrfHelp)) return false ; PtrOwner pCrvMiniChunkBorder( GetBasicCurveComposite( pSrfOrig->GetLoop( c, 0))) ; pSrfHelp->AddExtLoop( pCrvMiniChunkBorder->Clone()) ; if ( c == 1) pSrfRes.Set( pSrfHelp->Clone()) ; else pSrfRes->Add( *( pSrfHelp)) ; } // restituisco 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( CreateBasicCurveComposite()) ; 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 superficie che racchiude il mio tool PtrOwner pCrvToolShape( CreateBasicCurveArc()) ; if ( IsNull( pCrvToolShape)) return false ; pCrvToolShape->SetXY( ptC, s_dRad + 5 * EPS_SMALL) ; PtrOwner pSrfTool( CreateBasicSurfFlatRegion()) ; if ( IsNull( pSrfTool) || ! pSrfTool->AddExtLoop( Release( pCrvToolShape)) || ! pSrfTool->IsValid()) break ; PtrOwner pSrfTest( CloneBasicSurfFlatRegion( pSrfBiggerChunk)) ; if ( IsNull( pSrfTest)) return false ; pSrfTest->Subtract( *pSrfTool) ; if ( IsNull( pSrfTest) || ! pSrfTest->IsValid() || ! pSrfTest->GetArea( dArea) || dArea < 10 * EPS_SMALL || bForceCentroid) { // se prima iterazione -> ritorno il centroide con punto if ( nIter == 1) { nOptFlag = 1 ; ptCentroid = ptC ; return true ; } // se non sono alla prima iterazione -> memorizzo il centroide nel vettore else { // 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( CreateBasicCurveComposite()) ; 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 ; } // abbellisco la curva 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), s_dRad + 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 ( vCrvCoMedAxi.empty() && vPtCentroid.empty()) { nOptFlag = 0 ; return true ; } // ora collego la varie curve medial Axis trovate tra loro (ottenendo quindi una curva che svuota tutta la regione) // curva che collega primo e ultimo medial Axis PtrOwner pCrvCoBackLink( CreateBasicCurveComposite()) ; 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( CreateBasicCurveComposite()) ; 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) && ! CalcBoundedSmootedLink( ptEOriginal, vtEOriginal, ptSOriginal, vtSOriginal, 0.5, vOffsFirstCurve, pCrvCoBackLink) && ! 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( GetBasicCurveComposite( pCrvPath->CopyParamRange( 0, dU))) ; if ( IsNull( pCrvA)) return false ; PtrOwner pCrvB( GetBasicCurveComposite( 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( CreateBasicCurveComposite()) ; if ( IsNull( pCrvPath1)) return false ; if ( ! CalcBoundedSmootedLink( ptClosestOnPath, vtTanCpt, vPtCentroid[i], vtTanCpt, 0, vOffsFirstCurve, pCrvPath1) && ! 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 ( vCurves.empty()) 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 ( vOffs.empty()) return true ; // vettore di curve ausiliario ICRVCOMPOPOVECTOR vOffOrig( vOffs.size()) ; for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) vOffOrig[i].Set( vOffs[i]->Clone()) ; // punto iniziale corrente Point3d ptStart ; if ( ! pCurve->GetStartPoint( ptStart)) return false ; // curve d'appoggio PtrOwner pCompo( pCurve->Clone()) ; PtrOwner pCompoHelp( pCurve->Clone()) ; PtrOwner pOffCheck( CreateCurveComposite()) ; if ( IsNull( pCompoHelp) || IsNull( pCompo) || IsNull( pOffCheck)) return false ; int nTypeOfCut = -1 ; // scorro tutte le curve for ( int i = 0; i < int( vOffOrig.size()) ; ++ i) { // controllo se la curva è quella attuale if ( vOffOrig[i]->IsPointOn( ptStart)) pOffCheck.Set( vOffOrig[i]->Clone()) ; // classificazione CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCompo, *vOffOrig[i]) ; intCC.GetCurveClassification( 0, 10 * EPS_SMALL, ccClass) ; if ( ! pCompoHelp->Clear()) return false ; // se In/Out if ( ccClass.size() > 1) { if ( ccClass[0].nClass == CRVC_IN) nTypeOfCut = CRVC_OUT ; else nTypeOfCut = CRVC_IN ; // scorro le classificazioni ottenute 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( vOffOrig[i]->CopyParamRange( dOffS, dOffE)) ; PtrOwner pCrvB( 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()) ; } } // restituisco la nuova curva 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) { // creazione dei due possibili percorsi per raggiungere il centroide PtrOwner pPath1( CreateCurveComposite()) ; PtrOwner pPath2( CreateCurveComposite()) ; if ( IsNull( pPath1) || IsNull( pPath2)) return false ; pCrvNewCurve->Clear() ; Point3d ptS, ptE = ptC ; Vector3d vtTanS, vtTanE ; // creo una circonferenza if ( bCir) { // 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 ; } // creo un BiArco else { // 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 ; // angolo incrementale nel caso di autointersezioni while ( ! bFound && nIter < 45) { vTanE.Rotate( Z_AX, 90 - nIter) ; // vettore perpendicolare PtrOwner pLine( CreateCurveLine()) ; pLine->SetPVL( ptHS, vTanE, ( 2 * s_dRad) / 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 * s_dRad) / 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) { // inizializzo i due possibili percorsi 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 || vOffs.empty()) 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 superficie, 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 if ( IsNull( pSrfAct) || pSrfAct->GetChunkCount() == 0) return false ; double dOffsPrec = 0. ; while ( nIter < MAX_ITER) { // ricavo la regione piana da VRONI PtrOwner pSfrOffsVR( pSrfAct->CreateOffsetSurf( - dOffs, ICurve::OFF_FILLET)) ; if ( IsNull( pSfrOffsVR)) return false ; if ( ! pSfrOffsVR->IsValid()) { pSfrOffsVR.Set( pSrfAct->CreateOffsetSurf( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) ; if ( IsNull( pSfrOffsVR)) return false ; } // alla prima iterazione svuoto la regione nReg-esima passata alla funzione CalcSpiral if ( nIter == 0) { PtrOwner pSrfChunknReg( pSfrOffsVR->CloneChunk( nReg)) ; nReg = pSfrOffsVR->GetChunkCount() ; // aggiorno il nuovo valore delle regioni if ( IsNull( pSrfChunknReg)) // se supero i chunk ottenuti return true ; } // numero di Chunk attuali int nChunks = pSfrOffsVR->GetChunkCount() ; for ( int i = 0 ; i < nChunks ; ++ i) { // per ogni chunk... int nLoops = pSfrOffsVR->GetLoopCount( i) ; for ( int j = 0 ; j < nLoops ; ++ j) { // per ogni loop... PtrOwner pCrvCompoBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ; if ( IsNull( pCrvCompoBorder) || ! pCrvCompoBorder->IsValid()) return false ; if ( j > 0) // inverto l'orientamento delle curve interne ( offset delle isole trovate) pCrvCompoBorder->Invert() ; // inserisco nel vettore degli Offset vOffs.emplace_back( Release( pCrvCompoBorder)) ; // salvo le curve di Offset esterne della prima iterazione if ( nIter == 0) { // salvo il bordo per i link ( non invertiti) PtrOwner pCrvCompoExtBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ; vOffsFirstCurve.emplace_back( Release( pCrvCompoExtBorder)) ; } } } // controllo se serve un raggio più piccolo di svuotatura bool bSmallRad = ( nIter == 0 ? dOffs < dRad + EPS_ZERO : dOffs - dOffsPrec < dRad + EPS_ZERO) ; // se ho trovato dei chunk, allora aggiorno l'Offset per la passata successiva if ( nChunks > 0) { // memorizzo il valore di Offset per l'iterazione successiva dOffsPrec = dOffs ; dOffs += dStep ; } // se devo usare un Offset più piccolo... else if ( ! bSmallRad) dOffs = dOffsPrec + ( nIter == 0 ? dRad : dRad) ; // superfluo, effettivamente non ho l'offset radiale // altrimenti ho finito la regione da svuotare... else break ; ++ nIter ; // aggiorno iterazione } // se non ho trovato curve di Offset allora esco if ( vOffs.empty()) 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], s_dRad / 8, s_dRad / 8, false) ; vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; } vOffsClosedCurves[i].Set( vOffs[i]->Clone()) ; // NB. Salvo questo vettore in quanto il BiArco di raccordo tra la curva (i)-esima e la (i+1)-esima potrebbe // intersecare altre curve oltre queste due. ( CutCurveToConnect( ...)) } for ( int i = 0 ; i < int( vOffsFirstCurve.size()) ; ++ i) { if ( i != 0) ModifyCurveToSmoothed( vOffsFirstCurve[i], s_dRad / 8, s_dRad / 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) { // prendo il punto iniziale 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 ; // scorro le curve successive 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 ; // memorizzo l'indice della curva più vicina ( aggiornando il valore della distanza) if ( dDist > Dist( ptS, ptE) ) { dDist = Dist( ptS, ptE) ; nClosestInd = j ; ptHelp.Set( ptE.x, ptE.y, ptE.z) ; } } // 1) scambio la curva i con la curva più vicina trovata ( se non è la successiva) 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 ( nel punto più vicino trovato)... 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 raccordarla con la successiva ... if (( int)vOffs.size() > 1) { // se ho almeno un Offset // copio le curve (i)-esime e (i+1)-esime double dUNS, dUNE ; PtrOwner pCrvTest( CreateCurveComposite()) ; // possibile collegamento tra la curva i ed i+1 if ( IsNull( pCrvTest)) return false ; PtrOwner pOff_i0( CloneCurveComposite( vOffs[i])) ; PtrOwner pOff_i1( CloneCurveComposite( vOffs[i + 1])) ; // cerco di tagliare le curve e di raccordarle ( -> ottenendo il collegamento) // NB. se la curva di offset è la prima, non devo tagliarla, accorcio solo le successive ( se non bordi di isole) ... if ( ! s_bSmooth || ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsClosedCurves, vOffsFirstCurve, pCrvTest, ( i == 0 ? 0 : 0.01), 0.01, 2)) { // se non sono riuscito ad accorcirle, ritorno alla configurazione iniziale PtrOwner pCrvLink( CreateCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; // vettori tangenti per raccordo 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 ; // calcolo il raccordo if ( CalcBoundedSmootedLink( ptS, vS, ptNE, vE, 0.5, vOffsFirstCurve, pCrvLink)) vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento else return false ; // passo alla successiva continue ; } // ricavo i parametri nei punti scelti 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 ( IsNull( pCrvNewOffS) || ! pCrvNewOffS->IsValid()) return false ; 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) ; } } // 3) controllo eventuali parti non svuotate... PtrOwner pSrfToCut( CreateSurfFlatRegion()) ; if ( IsNull( pSrfToCut)) return false ; 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 tangente 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 ( ! s_bSmooth) 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 * s_dRad) / 16)) / dLen1 ; // % parametro sinistro per smusso double dTollRight = ( ( 2 * s_dRad) / 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 * s_dRad) / 16)) / dLenI_e ; // % paramtro sinistro per smusso dTollRight = ( ( 2 * s_dRad) / 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, bool bFromInfill) { // check parametri if ( pSrfZigZag == nullptr || ! pSrfZigZag->IsValid()) return true ; // creo il vettore dei contorni ICRVCOMPOPOVECTOR vFirstOff ; for ( int c = 0 ; c < pSrfZigZag->GetChunkCount() ; ++ c) 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 ; double dYStep ; if ( ! bFromInfill) { nYStep = static_cast< int >( ceil(( dDimY - 30 * EPS_SMALL) / dStep)) ; dYStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ; } else { nYStep = static_cast< int >( floor(( dDimY + 30 * EPS_SMALL) / dStep)) ; dYStep = dStep ; } 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 ; if ( ! bFromInfill) ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + 10 * EPS_SMALL + i * dYStep, ptMin.z + dDimZ) ; else { double dShift = ( i == 0 ? 10 * EPS_SMALL : ( i == nYStep ? - 10 * EPS_SMALL : 0.)) ; ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + dShift + 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 superficie 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 ( IsNull( pSeg1) || IsNull( pSeg2)) return false ; // aggiorno il link 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() ; if ( bFromInfill) for ( int u = 0 ; u < pCrvLink->GetCurveCount() ; ++ u) pCrvLink->SetCurveTempProp( u, -1, 1) ; 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( 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 superficie 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 superficie 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 ; // ciclo su tutti i chunks della superficie 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 superficie nel nuovo sistema di riferimento pSrfChunk->ToLoc( frPocket) ; // creo la regione per il percorso a ZigZag PtrOwner pSrfZigZag( CloneSurfFlatRegion( pSrfChunk)) ; if ( ! pSrfZigZag->Offset( - dOffs, 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, false)) 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 superficie 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 = 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 superfici 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 superficie 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() || dStep < EPS_SMALL || ( nType != POCKET_ZIGZAG && nType != POCKET_ONEWAY && nType != POCKET_SPIRALIN && nType != POCKET_SPIRALOUT)) return false ; // pulizia vettore delle curve elementari vCrvCompoRes.clear() ; // assegno dati di modulo s_nType = nType ; s_dRad = dRad ; s_bSmooth = bSmooth ; // calcolo delle curve elementari della superficie switch ( nType) { case POCKET_ZIGZAG: if ( ! AddZigZag( pSfr, dRad, dStep, dAngle, bSmooth, vCrvCompoRes)) return false ; break ; case POCKET_ONEWAY: if ( ! AddOneWay( pSfr, dRad, dStep, dAngle, bSmooth, vCrvCompoRes)) return false ; break ; case POCKET_SPIRALIN: if ( ! AddSpiralIn( pSfr, dRad, dStep, bSmooth, vCrvCompoRes)) return false ; break ; case POCKET_SPIRALOUT: if ( ! AddSpiralOut( pSfr, dRad, dStep, bSmooth, vCrvCompoRes)) return true ; break ; } return true ; } //---------------------------------------------------------------------------- static bool AdjustZigZagPathTangentLinks( ICurveComposite* pCrvCompo) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // miglioro la curva pCrvCompo->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; // scorro tutte le curve int nCurrTmpProp = -1, nCurrTmpProp1 = -1 ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) { if ( pCrvCompo->GetCurveTempProp( u, nCurrTmpProp, 1) && nCurrTmpProp == -1) { // ricavo il link PtrOwner pCrvLink( CreateCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; pCrvLink->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ; for ( int uu = u + 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) { if ( pCrvCompo->GetCurveTempProp( uu, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) { if ( ! pCrvLink->AddCurve( pCrvCompo->GetCurve( uu)->Clone())) return false ; } else break ; } if ( ! pCrvLink->IsValid()) return false ; // se ho più di una curva nel link if ( pCrvLink->GetCurveCount() > 1) { // ricavo la curva precendente al Link ( se presente e se prima curva del Link è lineare) if ( u != 0 && pCrvLink->GetFirstCurve()->GetType() == CRV_LINE) { const ICurve* pCrvSegPrec = pCrvCompo->GetCurve( u - 1) ; if ( pCrvSegPrec == nullptr || pCrvSegPrec->GetType() != CRV_LINE) return false ; // ricavo la sua direzione finale Vector3d vtZigZagEndDir ; pCrvSegPrec->GetEndDir( vtZigZagEndDir) ; // ricavo la direzione iniziale del link Vector3d vtLinkStartDir ; pCrvLink->GetStartDir( vtLinkStartDir) ; // se i vettori sono paralleli, considero la prima curva del link come parte del percorso a ZigZag if ( AreSameVectorApprox( vtZigZagEndDir, vtLinkStartDir)) pCrvCompo->SetCurveTempProp( u, 0, 1) ; } // ricavo la curva successiva al Link ( se presente e se ultima curva del Link è lineare) if ( u + pCrvLink->GetCurveCount() < pCrvCompo->GetCurveCount() && pCrvLink->GetLastCurve()->GetType() == CRV_LINE) { const ICurve* pCrvSegSucc = pCrvCompo->GetCurve( u + pCrvLink->GetCurveCount()) ; if ( pCrvSegSucc == nullptr || pCrvSegSucc->GetType() != CRV_LINE) return false ; // ricavo la sua direzione inziale Vector3d vtZigZagStartDir ; pCrvSegSucc->GetStartDir( vtZigZagStartDir) ; // ricavo la direzione finale del link Vector3d vtLinkStartDir ; pCrvLink->GetEndDir( vtLinkStartDir) ; // se i vettori sono paralleli, considero l'ultima curva del link come parte del percorso a ZigZag if ( AreSameVectorApprox( vtZigZagStartDir, vtLinkStartDir)) pCrvCompo->SetCurveTempProp( u + pCrvLink->GetCurveCount() - 1, 0, 1) ; } } // aggiorno u += pCrvLink->GetCurveCount() ; // così la successiva è corretta nel ciclo } } return true ; } //---------------------------------------------------------------------------- static bool AdjustLinkSameY( ICurveComposite* pCrvLink, bool& bSplit) { // controllo dei parametri if ( pCrvLink == nullptr || ! pCrvLink->IsValid()) return false ; bSplit = false ; // ricavo gli estremi del Link Point3d ptS ; pCrvLink->GetStartPoint( ptS) ; Point3d ptE ; pCrvLink->GetEndPoint( ptE) ; // ricavo il versore uscente da ptS e diretto verso ptE Vector3d vtDir = ptE - ptS ; if ( vtDir.IsValid() && ! vtDir.IsSmall()) vtDir.Normalize() ; else return false ; // creo un frame orientato come questo versore Frame3d frLink ; frLink.Set( ptS, Z_AX, vtDir) ; if ( ! frLink.IsValid()) return false ; // ricavo il box3d del Link in questo frame BBox3d BBoxLink ; pCrvLink->ToLoc( frLink) ; if ( ! pCrvLink->GetLocalBBox( BBoxLink) || BBoxLink.IsEmpty()) return false ; pCrvLink->ToGlob( frLink) ; // controllo la dimensione del Box in Y if ( abs( BBoxLink.GetDimY()) > 20 * EPS_SMALL) bSplit = true ; return true ; } //---------------------------------------------------------------------------- static bool AdjustLinkDifferentY( const ICurve* pCrvSegPrec, const ICurve* pCrvSegSucc, const ICurveComposite* pCrvLink, bool& bSplit) { // controllo dei parametri if ( pCrvLink == nullptr || ! pCrvLink->IsValid()) return false ; if ( pCrvSegPrec == nullptr && pCrvSegSucc == nullptr) return true ; bSplit = false ; // creo un Trapezoide per regione di incidenza Point3d ptH ; PtrOwner pSfrTrap( CreateSurfFlatRegion()) ; PtrOwner pCrvTrapBorder( CreateCurveComposite()) ; if ( IsNull( pSfrTrap) || IsNull( pCrvTrapBorder)) return false ; // 0) primo tratto if ( pCrvSegPrec != nullptr) { // se presente lo aggiungo if ( ! pCrvTrapBorder->AddCurve( pCrvSegPrec->Clone())) return false ; } else { // se non presente, inserisco il punto iniziale del Link pCrvLink->GetStartPoint( ptH) ; pCrvTrapBorder->AddPoint( ptH) ; } // 1) tratto lineare fino al punto finale del Link if ( ! pCrvLink->GetEndPoint( ptH) || ! pCrvTrapBorder->AddLine( ptH)) return false ; // 2) terzo tratto if ( pCrvSegSucc != nullptr) { // se presente lo aggiungo if ( ! pCrvTrapBorder->AddCurve( pCrvSegSucc->Clone())) return false ; } // se non presente non aggiungo nulla, ho alla peggio il Link // 4) ultimo tratto lineare per chiudere la curva if ( ! pCrvTrapBorder->Close() || // ora creo la FlatRegion a trapezio ! pSfrTrap->AddExtLoop( Release( pCrvTrapBorder)) || ! pSfrTrap->IsValid()) return false ; // oriento if ( AreOppositeVectorApprox( Z_AX, pSfrTrap->GetNormVersor())) pSfrTrap->Invert() ; // effettuo un piccolo Offset per una maggiore tolleranza ( se possibile) PtrOwner pSfrTrap_c( CloneSurfFlatRegion( pSfrTrap)) ; if ( IsNull( pSfrTrap_c)) return false ; if ( ! pSfrTrap->Offset( - s_dRad / 5, ICurve::OFF_CHAMFER)) pSfrTrap.Set( pSfrTrap_c) ; // se il Link interseca questa regione, allora va eliminato CRVCVECTOR ccClass ; if ( pSfrTrap->GetCurveClassification( *pCrvLink, EPS_SMALL, ccClass)) bSplit = int( ccClass.size()) > 1 ; return true ; } //---------------------------------------------------------------------------- static bool AdjustLinkZigZagInfill( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vNewCrv) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; vNewCrv.clear() ; // sistemo la curva per evitare che i tratti estremi dei link siano lineari ed in tangenza con percorsi ZigZag if ( ! AdjustZigZagPathTangentLinks( pCrvCompo)) return false ; // definisco un vettore di intervalli su cui spezzare la curva struct CopyRange { double dParS ; double dParE ; } ; vector vIntervals ; // parametri int nCurrTmpProp1 = 0 ; int nCrvStartBreak = 0 ; // NB. I tratti di ZigZag che seguono dei contorni ( i Links) hanno TmpProp1 impostata come -1 for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) { if ( pCrvCompo->GetCurveTempProp( u, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) { // ricavo il link PtrOwner pCrvLink( CreateCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; pCrvLink->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ; for ( int uu = u + 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) { if ( pCrvCompo->GetCurveTempProp( uu, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) { if ( ! pCrvLink->AddCurve( pCrvCompo->GetCurve( uu)->Clone())) return false ; } else break ; } if ( ! pCrvLink->IsValid()) return false ; // ricavo il punto iniziale e finale del Link Point3d ptS ; pCrvLink->GetStartPoint( ptS) ; Point3d ptE ; pCrvLink->GetEndPoint( ptE) ; // parametro per spezzare o meno la curva, togliendo il Link bool bSplit = false ; // se il Link collega due tratti alla stessa quota... if ( abs( ptS.y - ptE.y) < 20 * EPS_SMALL) { if ( ! AdjustLinkSameY( pCrvLink, bSplit)) return false ; } // se il Link collega due tratti a quote diverse else { if ( ! AdjustLinkDifferentY( pCrvCompo->GetCurve( u - 1), pCrvCompo->GetCurve( u + pCrvLink->GetCurveCount()), pCrvLink, bSplit)) return false ; } // aggiorno u += pCrvLink->GetCurveCount() ; // così la successiva è corretta nel ciclo if ( bSplit) { // se dimensione sopra la tolleranza, allora spezzo la curva e rimuovo il link CopyRange CR { 1. * nCrvStartBreak, // dParS 1. * ( u - pCrvLink->GetCurveCount()) // dParE } ; // se il primo tratto è un link non valido, non considero l'intervallo if ( ! ( nCrvStartBreak == 0 && abs( CR.dParS - CR.dParE) < 20 * EPS_SMALL)) vIntervals.push_back( CR) ; nCrvStartBreak = u ; } } } // se non ho mai trovato link non validi, lascio la curva invariata if ( vIntervals.empty()) vNewCrv.emplace_back( CloneCurveComposite( pCrvCompo)) ; else { // per ogni intervallo... for ( int i = 0 ; i < int( vIntervals.size()) ; ++ i) vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( vIntervals[i].dParS, vIntervals[i].dParE))) ; // ultimo intervallo ( se l'ultimo intervallo è un link non valido, non inserisco) if ( abs( vIntervals.back().dParE - pCrvCompo->GetCurveCount()) > 20 * EPS_SMALL) vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nCrvStartBreak, pCrvCompo->GetCurveCount()))) ; } return true ; } //---------------------------------------------------------------------------- bool CalcZigZagInfill( const ISurfFlatRegion* pSfr, double dStep, bool bSmooth, bool bRemoveOverlapLink, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid() || dStep < EPS_SMALL) return false ; vCrvCompoRes.clear() ; s_bSmooth = false ; s_dRad = dStep / 2 ; // vettore con i percorsi a ZigZag ICRVCOMPOPOVECTOR vpCrvs ; // ciclo su tutti i chunk della regione for ( int c = 0 ; c < pSfr->GetChunkCount() ; ++ c) { // prendo il Chunk PtrOwner pSfrChunk( pSfr->CloneChunk( c)) ; if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid()) return false ; // calcolo i percorsi a ZigZag if ( ! CalcZigZag( pSfrChunk, vpCrvs, dStep, true)) return false ; if ( bRemoveOverlapLink) { ICRVCOMPOPOVECTOR vCrvFinal ; for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) vCrvFinal.emplace_back( Release( vpCrvs[u])) ; vpCrvs.clear() ; // sistemo le curve ottenute, aggiustando i Links for ( int u = 0 ; u < int( vCrvFinal.size()) ; ++ u) { ICRVCOMPOPOVECTOR vNewCrv ; if ( ! AdjustLinkZigZagInfill( vCrvFinal[u], vNewCrv)) return false ; for ( int uu = 0 ; uu < int( vNewCrv.size()) ; ++ uu) vpCrvs.emplace_back( Release( vNewCrv[uu])) ; } } // inserisco i percorsi da ritornare for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) { if ( bSmooth) { s_bSmooth = true ; ModifyCurveToSmoothed( vpCrvs[u], s_dRad / 16, s_dRad / 16, false) ; } vCrvCompoRes.emplace_back( Release( vpCrvs[u])) ; } } return true ; }