//---------------------------------------------------------------------------- // 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 "/EgtDev/Include/EGkOffsetCurve.h" #include "/EgtDev/Include/EGkIntervals.h" #include "/EgtDev/Include/EgtNumUtils.h" #include "/EgtDev/Include/EGkChainCurves.h" #include using namespace std ; //---------------------------------------------------------------------------- // Calcolo delle curve elementari di Pocketing //---------------------------------------------------------------------------- // variabili d'appoggio ( per non passare troppi parametri tra le funzioni secondarie) struct PocketParams { int nType = POCKET_SPIRALIN ; double dRad = 0. ; // raggio utensile double dRad_prec = -1. ; // raggio utensile della lavorazione precedente double dSideStep = 0. ; // step double dSideStep_prec = -1. ; // step della lavorazione precedente double dRadialOffset = 0. ; // offset radiale double dRadialOffset_prec = -1. ; // offset radiale precedente double dOpenEdgeRad = 0. ; // massimo raggio per estensione lati aperti double dOpenMinSafe = 2. ; // estensione minima di sicurezza double dMaxOptSize = 0. ; // dimensione per ottimizzazione double dAngle = 0. ; // angolo per orientare le passate OneWay e ZigZag bool bOptOffsets = true ; // flag per evitare Offset non necessari in SpiralIn/Out bool bOptOffsetsAdv = false ; // flag per evitare Offset coperti da curve di MedialAxis in SpiralIn/Out bool bAboveHead = true ; // flag per testa da sopra ( Z+) bool bSmooth = false ; // curve smussate bool bInvert = false ; // inversione dei percorsi bool bAvoidOpt = false ; // flag per evitare casi ottimizzati Point3d ptStart = P_INVALID ; // punto d'inizio Point3d ptEnd = P_INVALID ; // punto di fine SurfFlatRegion SfrLimit ; // superficie limite per estensione lati aperti bool bCalcFeed = true ; // flag per calcolo della Feed double dFeed = 1000 ; // feed di riferimento per frazione double dToolFeed = 1000 ; // feed del tool } ; static double TOL_TRAPEZOID = 50 * EPS_SMALL ; // tolleranza per casi a trapezio SpiralPocket static int TMP_PROP_SINGLE_CURVE = 3 ; // percorso singola curva aperta ( seguendo i chiusi) static int TMP_PROP_CRV_OPEN_SIDE = 2 ; // segmento per entrata da fuori static int TMP_PROP_OPT_TRAPEZOID = 4 ; // caso ottimizzato Trapezio //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- static double GetMaxFeed( const PocketParams& PockParams) { return PockParams.dFeed ; } //--------------------------------------------------------------------------- static double GetMinFeed( const PocketParams& PockParams) { return PockParams.dFeed * PockParams.dSideStep / ( 2 * PockParams.dRad) ; } //--------------------------------------------------------------------------- static double GetFeed( const PocketParams& PockParams) { return ( PockParams.dFeed > 0 ? PockParams.dFeed : PockParams.dToolFeed) ; } //---------------------------------------------------------------------------- static bool AssignMaxFeed( ICurveComposite* pCrv, const PocketParams& PockParams) { // controllo dei parametri if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; // assegno la Feed massima ad ogni sottocurva di pCrv for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ; return true ; } //--------------------------------------------------------------------------- static bool AssignMinFeed( ICurveComposite* pCrv, const PocketParams& PockParams) { // controllo dei parametri if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; // assegno la Feed minima ad ogni sottocurva di pCrv for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ; return true ; } //--------------------------------------------------------------------------- static bool AssignCustomFeed( ICurveComposite* pCrv, const PocketParams& PockParams, double dFeed) { // controllo dei parametri if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; // La Feed deve essere compresa tra la minima e la massima dFeed = Clamp( dFeed, GetMinFeed( PockParams), GetMaxFeed( PockParams)) ; // assegno la Feed ad ogni sottocurva di pCrv for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempParam( u, dFeed, 0) ; return true ; } //--------------------------------------------------------------------------- static bool GetFeedForParam( double dPar, const PocketParams& PockParams, double& dFeed) { /* Feed ^ | GetFeed() + --------------\ | * \ | * \ | * \ | * \ | * \ GetFeed() * GetSideStep() / Diam + * * | * * 0--------------+------+---------------> Length of working angle GetSideStep() Diam */ if ( dPar > PockParams.dRad * 2 || dPar < 0 ) // dominio... return false ; if ( PockParams.dRad * 2 - PockParams.dSideStep < 50 * EPS_SMALL) { // se la funzione è costante... dFeed = GetMaxFeed( PockParams) ; // non ho scelta ... return true ; } else { if ( PockParams.dSideStep < dPar + 50 * EPS_SMALL) { // se sono nel tratto lineare discendente ... // d/2 su parte discendente dFeed = GetFeed( PockParams) + ( GetFeed( PockParams) * ( 1 - ( PockParams.dSideStep / ( PockParams.dRad * 2)))) * ( dPar - PockParams.dSideStep) / ( PockParams.dSideStep - ( PockParams.dRad * 2)) ; } else dFeed = GetMaxFeed( PockParams) ; // se sono nel tratto costante ... } return true ; } //---------------------------------------------------------------------------- static bool AssignFeedZigZagOneWay( ICurveComposite* pCompo, const bool bIsLink, const ICURVEPOVECTOR& vLAbove, const ICURVEPOVECTOR& vLUnder, const ICRVCOMPOPOVECTOR& vAddedLinks, const PocketParams& PockParams) { // controllo che la pCompoLine sia effettivamente una linea valida if ( pCompo == nullptr || ! pCompo->IsValid()) return false ; // controllo se il Flag per assegnare la Feed è attivo if ( ! PockParams.bCalcFeed) return AssignMaxFeed( pCompo, PockParams) ; // inzialmente setto la feed Minima alla curva AssignMinFeed( pCompo, PockParams) ; // se è un link tra livelli diversi, allora esco ( con Feed Minima ) if ( bIsLink) { Point3d ptS_link ; pCompo->GetStartPoint( ptS_link) ; Point3d ptE_link ; pCompo->GetEndPoint( ptE_link) ; if ( abs( ptE_link.y - ptS_link.y) > 500 * EPS_SMALL) return true ; } // se la curva è piccola, allora esco ( con Feed Minima) double dLen = EPS_SMALL ; if ( ! pCompo->GetLength( dLen) || dLen < 0.6 * ( 2 * PockParams.dRad)) return true ; Point3d ptS, ptE ; pCompo->GetStartPoint( ptS) ; pCompo->GetEndPoint( ptE) ; // creo l'intervallo desiderato valutanto le coordinate x Intervals IntMinFeed ; IntMinFeed.Set( ptS.x, ptE.x) ; // creo un vettore contenente i tratti sopra e i tratti sotto attivi ICURVEPOVECTOR vAllInt ; vAllInt.reserve( int( vLUnder.size()) + int( vLAbove.size())) ; for ( int i = 0 ; i < int( vLUnder.size()) ; ++ i) vAllInt.emplace_back( vLUnder[i]->Clone()) ; for ( int i = 0 ; i < int( vLAbove.size()) ; ++ i) vAllInt.emplace_back( vLAbove[i]->Clone()) ; int nDim = int( vAllInt.size()) ; // aggiungo le parti dei link interessate double dCurrY = ptS.y ; BBox3d bBoxLink ; for ( int l = 0 ; l < int( vAddedLinks.size()) ; ++ l) { vAddedLinks[l]->GetLocalBBox( bBoxLink) ; if (( dCurrY - bBoxLink.GetMin().y) < PockParams.dSideStep + 50 * EPS_SMALL || ( dCurrY - bBoxLink.GetMax().y) < PockParams.dSideStep + 50 * EPS_SMALL) { // determino la direzione del Link Vector3d vtLinkDir( - 25, 0, 0) ; // recupero gli estremi Point3d ptS_l ; vAddedLinks[l]->GetStartPoint( ptS_l) ; Point3d ptE_l ; vAddedLinks[l]->GetEndPoint( ptE_l) ; if ( ptE_l.x - ptS_l.x < EPS_SMALL) vtLinkDir.x -= ptS_l.x - ptE_l.x ; // superficie a rettangolo definita dal Box PtrOwner pSfrRectUp( GetSurfFlatRegionRectangle( bBoxLink.GetDimX() + 50, PockParams.dSideStep)) ; pSfrRectUp->Translate(( ptS_l - ORIG) + vtLinkDir) ; // link sopra o sotto alla curva currente ? bool bIsDown = true ; if ( dCurrY - ptS_l.y < EPS_SMALL) bIsDown = false ; // classificazione con la superficie a rettangolo CRVCVECTOR ccClass ; pSfrRectUp->GetCurveClassification( *vAddedLinks[l], EPS_SMALL, ccClass) ; for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) { if (( ccClass[i].nClass == CRVC_IN && bIsDown) || ( ccClass[i].nClass == CRVC_OUT && ! bIsDown)) { PtrOwner pCrvInt( ConvertCurveToComposite( vAddedLinks[l]->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ; if ( ! IsNull( pCrvInt) || pCrvInt->IsValid()) vAllInt.emplace_back( Release( pCrvInt)) ; } } } } // scorro tutti i tratti Attivi for ( int i = 0 ; i < int( vAllInt.size()) ; ++ i) { // controllo che il tratto lineare sotto sia sufficientemente lungo double dLen_iU = EPS_SMALL ; if ( ! vAllInt[i]->GetLength( dLen_iU) || ( dLen_iU < 1.1 * ( 2 * PockParams.dRad) && i < nDim)) continue ; // estremi dell'intervallo Point3d ptS_iU, ptE_iU ; vAllInt[i]->GetStartPoint( ptS_iU) ; vAllInt[i]->GetEndPoint( ptE_iU) ; // la vtDir per Above e Under è sempre invertita rispetto alla linea corrente // sottraggo questo intervallo a quello originale IntMinFeed.Subtract( ptS_iU.x, ptE_iU.x) ; } // l'intervallo orginale ora conterrà i sottointervalli con feed Minima, tolgo quelli troppo piccoli Intervals IntMinFeed_noSmall ; double dParS = EPS_SMALL ; double dParE = EPS_SMALL ; bool bFound = IntMinFeed.GetFirst( dParS, dParE) ; while ( bFound) { if ( dParE - dParS > PockParams.dRad - 5 * EPS_SMALL) IntMinFeed_noSmall.Add( dParS, dParE) ; bFound = IntMinFeed.GetNext( dParS, dParE) ; } // ora che ho solo tratti abbastanza lunghi, creo l'intervallo complementare // questo intervallo contiene tutte le feed Massime Intervals IntMaxFeed ; IntMaxFeed.Set( ptS.x, ptE.x) ; IntMaxFeed.Subtract( IntMinFeed_noSmall) ; // tolgo le parti piccole Intervals IntMaxFeed_noSmall ; bFound = IntMaxFeed.GetFirst( dParS, dParE) ; while ( bFound) { if ( dParE - dParS > PockParams.dRad - 5 * EPS_SMALL) IntMaxFeed_noSmall.Add( dParS, dParE) ; bFound = IntMaxFeed.GetNext( dParS, dParE) ; } // recupero la direzione principale della curva corrente Vector3d vtDir = X_AX ; if ( ptE.x - ptS.x < EPS_SMALL) vtDir = - X_AX ; // trasformo questi intervalli nei parametri corrispondenti sulla linea originale bool bFMax = false ; bFound = IntMaxFeed_noSmall.GetFirst( dParS, dParE) ; if ( bFound && IntMaxFeed_noSmall.IsInside( ptS.x + vtDir.x * 20 * EPS_SMALL)) bFMax = true ; if ( ! bFound && bIsLink) // se non ho intervalli a Feed Massima, allora esco ( con Feed Minima) return true ; while ( bFound) { if ( ! bIsLink) { // se segmento di ZigZag double du_js = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParS, ptS.y, ptS.z), du_js) ; pCompo->AddJoint( du_js) ; double du_je = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParE, ptE.y, ptE.z), du_je) ; pCompo->AddJoint( du_je) ; } bFound = IntMaxFeed_noSmall.GetNext( dParS, dParE) ; } // aggiorno le proprietà temporanee con la feed if ( ! bIsLink) { for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) { if ( IsEven( u) == bFMax) pCompo->SetCurveTempParam( u, GetMaxFeed( PockParams) , 0) ; else pCompo->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ; } } 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 AssignFeedForOpenEdge( ICurveComposite* pCrv, const ICurveComposite* pCrvOF_orig, const PocketParams& PockParams) { // se non rischiesto il calcolo della Feed, lascio quella standard if ( ! PockParams.bCalcFeed) return AssignMaxFeed( pCrv, PockParams) ; // controllo se qualche curva passa sopra ad un lato aperto... if ( pCrvOF_orig != nullptr ) { for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { const ICurve* pCrv_u = pCrv->GetCurve( u) ; if ( pCrv_u == nullptr) return false ; int nStat = -1 ; if ( CheckSimpleOverlap( pCrv_u, pCrvOF_orig, nStat, 1500 * EPS_SMALL) && nStat == 1) { double dFeed = GetMinFeed( PockParams) ; double dPar = PockParams.dRad ; if ( ! GetFeedForParam( dPar, PockParams, dFeed)) return false ; pCrv->SetCurveTempParam( u, dFeed, 0) ; } } } return true ; } //---------------------------------------------------------------------------- static bool SimplifyCurveByFeeds( ICurveComposite* pCrvCompo, const PocketParams& PockParam, double dToler) { // controllo la validità della curva if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0) return false ; // NB. Effettuo il Merge dei tratti di curva che presentano la stessa Feed ( mediante la tolleranza) // curva semplificata PtrOwner pCrvSimple( CreateCurveComposite()) ; if ( IsNull( pCrvSimple)) return false ; /* - effettuo il Merge dei tratti di curva che presentano la stessa Feed - imposto la Feed dei tratti piccoli come quella dei tratti precedenti ( per evitare singolarità) - effettuo un nuovo Merge per uniformare ulteriormente i nuovi tratti ottenuti */ double dCurrTempParam ; int nParStart = 0 ; double dTempParam ; double dLen ; for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) { pCrvCompo->GetCurveTempParam( i, dTempParam) ; if ( i == 0) { dCurrTempParam = dTempParam ; nParStart = i ; } else if ( abs( dCurrTempParam - dTempParam) > dToler) { // se parametri diversi // recupero il tratto di curva uniforme PtrOwner pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, i))) ; if ( IsNull( pCrv)) return false ; // lo semplifico // recupero la lunghezza del tratto e imposto associo la Feed corrente // se curva corta -> il parametro di Feed è definito da quello precedente pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ; pCrv->GetLength( dLen) ; for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ; pCrvSimple->AddCurve( Release( pCrv)) ; dCurrTempParam = dTempParam ; nParStart = i ; } } // ultima parte di curva uniforme ... PtrOwner pCrvLast( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ; if ( ! IsNull( pCrvLast)) { pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ; double dLen ; pCrvLast->GetLength( dLen) ; for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u) pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ; pCrvSimple->AddCurve( Release( pCrvLast)) ; } // pulisco la curva originale pCrvCompo->Clear() ; // scorro i nuovi tratti ottenuti e ripeto il ragionamento for ( int i = 0 ; i < pCrvSimple->GetCurveCount() ; ++ i) { pCrvSimple->GetCurveTempParam( i, dTempParam) ; if ( i == 0) { dCurrTempParam = dTempParam ; nParStart = i ; } else if ( abs( dCurrTempParam - dTempParam) > dToler) { PtrOwner pCrv( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, i))) ; if ( IsNull( pCrv)) return false ; pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ; // recupero la lunghezza di tale curve double dLen ; pCrv->GetLength( dLen) ; for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ; pCrvCompo->AddCurve( Release( pCrv)) ; // aggiungo alla curva finale dCurrTempParam = dTempParam ; nParStart = i ; } } // ultima parte di curva uniforme ... pCrvLast.Set( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, pCrvSimple->GetCurveCount()))) ; if ( ! IsNull( pCrvLast)) { // semplifico pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ; // recupero la lunghezza di tale curva e imposto la Feed pCrvLast->GetLength( dLen) ; for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u) pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ; pCrvCompo->AddCurve( Release( pCrvLast)) ; } return true ; } //---------------------------------------------------------------------------- static bool AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved_offs, bool bIsLink, const ICRVCOMPOPOVECTOR& vLinks_done, const ICurveComposite* pCrv_orig, const PocketParams& PockParams, double dToll) { // controllo la validità della curva if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; // controllo se il Flag per assegnare la Feed è attivo if ( ! PockParams.bCalcFeed) return AssignMaxFeed( pCrv, PockParams) ; // imposto di Default la Feed minima per ogni sottocurva AssignMinFeed( pCrv, PockParams) ; // se non ho una superificie svuotata, allora esco ( con Feed Minima) if ( pSrfRemoved_offs == nullptr || ! pSrfRemoved_offs->IsValid() || pSrfRemoved_offs->GetChunkCount() == 0) { // controllo eventuali sovrapposizioni con lati aperti AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ; return true ; } // clono la superificie ( valida) PtrOwner pSrf_Removed_offs_clone( pSrfRemoved_offs->Clone()) ; if ( IsNull( pSrf_Removed_offs_clone) || ! pSrf_Removed_offs_clone->IsValid() || pSrf_Removed_offs_clone->GetChunkCount() == 0) return true ; // esco ( sempre con Feed Minima) // restringo la superificie in maniera appropriata if ( bIsLink) { // se curva di Link if ( ! pSrf_Removed_offs_clone->Offset( - PockParams.dRad, ICurve::OFF_CHAMFER) || ! pSrf_Removed_offs_clone->IsValid() || pSrf_Removed_offs_clone->GetChunkCount() == 0) return true ; // esco ( sempre con Feed Minima) } else if ( PockParams.dRad < PockParams.dSideStep) { // se curva di Offset e raggio utensile < Side step if ( ! pSrf_Removed_offs_clone->Offset( PockParams.dSideStep - PockParams.dRad, ICurve::OFF_CHAMFER) || ! pSrf_Removed_offs_clone->IsValid() || pSrf_Removed_offs_clone->GetChunkCount() == 0) return true ; // esco ( sempre con Feed Minima) } // classifico le parti interne alla superificie creata solo dagli Offset CRVCVECTOR ccClass ; if ( ! pSrf_Removed_offs_clone->GetCurveClassification( *pCrv, EPS_SMALL, ccClass)) return true ; // esco ( sempre con Feed Minima) // creo la nuova curva con le Feed regolate PtrOwner pCrv_new( CreateCurveComposite()) ; if ( IsNull( pCrv_new)) return false ; for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) { // recupero il tratto di curva ricavato dalla classificazione double dCurrFeed = GetMinFeed( PockParams) ; PtrOwner pCrv_sez( ConvertCurveToComposite( pCrv->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ; if ( IsNull( pCrv_sez)) continue ; // curva troppo piccola, passo alla successiva // controllo se interno o fuori alla regione ( regolando quindi la Feed) if ( ccClass[i].nClass == CRVC_IN) // se interna alla regione rimossa... dCurrFeed = GetMaxFeed( PockParams) ; // assegno tale Feed ad ogni sottocurva ricavata dal tratto di classificazione for ( int u = 0 ; u < pCrv_sez->GetCurveCount() ; ++ u) pCrv_sez->SetCurveTempParam( u, dCurrFeed, 0) ; // aggiungo la nuova curva con la Feed regolata a quella finale pCrv_new->AddCurve( Release( pCrv_sez)) ; } // sotituisco con quanto calcolato pCrv->Clear() ; pCrv->AddCurve( Release( pCrv_new)) ; // controllo eventuali sovrapposizioni tra la curva attuale e i Link in precedenza percorsi for ( int l = 0 ; l < int( vLinks_done.size()) ; ++ l) { // classificazione IntersCurveCurve intCC( *pCrv, *vLinks_done[l]) ; for ( int i = 0 ; i < intCC.GetIntersCount() ; ++ i) { // per ogni tratto ricavato IntCrvCrvInfo aInfo ; if ( ! intCC.GetIntCrvCrvInfo( i, aInfo) || ! aInfo.bOverlap || AreSamePointApprox( aInfo.IciA[0].ptI, aInfo.IciA[1].ptI)) continue ; // passo al successivo se non c'è overlap o se i punti coicidono // tratto precedente PtrOwner pCrv_before( pCrv->Clone()) ; if ( ! pCrv_before->TrimEndAtParam( aInfo.IciA[0].dU)) pCrv_before->Clear() ; // se troppo piccolo, elimino // tratto successivo PtrOwner pCrv_after( pCrv->Clone()) ; if ( ! pCrv_after->TrimStartAtParam( aInfo.IciA[1].dU)) pCrv_after->Clear() ; // se troppo piccolo, elimino // tratto di overlap PtrOwner pCrv_overlap( ConvertCurveToComposite( pCrv->CopyParamRange( aInfo.IciA[0].dU, aInfo.IciA[1].dU))) ; if ( ! IsNull( pCrv_overlap) && pCrv_overlap->IsValid()) { for ( int u = 0 ; u < pCrv_overlap->GetCurveCount() ; ++ u) pCrv_overlap->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ; // sostituisco unendo i 3 tratti pCrv->Clear() ; pCrv->AddCurve( Release( pCrv_before)) ; pCrv->AddCurve( Release( pCrv_overlap)) ; pCrv->AddCurve( Release( pCrv_after)) ; } } } if ( ! bIsLink) { // ---------------- NEL CASO DI OFFSET ---------------- // creo un intervallo con tutte le Feed Minime Intervals IntMinFeed ; for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { double dParam ; pCrv->GetCurveTempParam( u, dParam, 0) ; if ( abs( dParam - GetMinFeed( PockParams)) < 5 * EPS_SMALL) IntMinFeed.Add( u, u + 1) ; } double dParS = EPS_SMALL ; double dParE = EPS_SMALL ; // a questo intervallo tolgo tutti i sottotratti di lunghezza inferiore alla tolleranza richiesta Intervals IntMinFeed_noSmall ; bool bFound = IntMinFeed.GetFirst( dParS, dParE) ; while ( bFound) { PtrOwner pCrv_Crv( pCrv->CopyParamRange( dParS, dParE)) ; double dLen = EPS_SMALL ; pCrv_Crv->GetLength( dLen) ; if ( dLen > dToll + 5 * EPS_SMALL) IntMinFeed_noSmall.Add( dParS, dParE) ; bFound = IntMinFeed.GetNext( dParS, dParE) ; } for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { if ( IntMinFeed_noSmall.IsInside( u + 0.5)) pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ; else pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ; } } else { // ---------------- NEL CASO DI LINK ---------------- // le curve con lunghezza < dToll vanno modificate for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { double dLen = EPS_SMALL ; pCrv->GetCurve( u)->GetLength( dLen) ; if ( dLen < dToll + 5 * EPS_SMALL) { PtrOwner pCrvCompo( pCrv->Clone()) ; if ( ! pCrvCompo->TrimStartAtParam( u)) pCrvCompo->Clear() ; if ( ! pCrvCompo->TrimEndAtLen( dLen + PockParams.dRad * 2)) pCrvCompo->Clear() ; if ( pCrvCompo->IsValid()) { bool bFound = false ; for ( int uu = 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) { double dLenH = EPS_SMALL ; pCrvCompo->GetCurve( uu)->GetLength( dLenH) ; if ( dLenH < dToll + 5 * EPS_SMALL) continue ; // cerco tra le curve successive vicine se ne trovo una con Feed Minima double dParam ; pCrvCompo->GetCurveTempParam( uu, dParam, 0) ; if ( abs( dParam - GetMinFeed( PockParams)) < 5 * EPS_SMALL) { pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ; bFound = true ; break ; } } if ( ! bFound && u != 0) { // arrivato qui, so che successivamente non ho curve con Feed Minima vicine double dParam ; pCrv->GetCurveTempParam( u - 1, dParam, 0) ; if ( abs( dParam - GetMaxFeed( PockParams)) < 5 * EPS_SMALL) // se anche la precedente ha Feed Massima -> la curva u-esima è piccola ed Isolata pCrv->SetCurveTempParam( u, dParam, 0) ; } } else pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ; } } } // controllo eventuali sovrapposizioni con lati aperti AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ; return true ; } //---------------------------------------------------------------------------- static bool AssignFeedSpiralOpt( const int nOptType, const PocketParams& PockParams, ICurveComposite* pCrv ) { // controllo della curva corrente if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; // controllo se il Flag per assegnare la Feed è attivo if ( ! PockParams.bCalcFeed) return AssignMaxFeed( pCrv, PockParams) ; switch ( PockParams.nType) { case POCKET_SPIRALIN : if ( nOptType == 0) { // Spirale dall'Esterno for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { if ( u == 0) // prima circonferenza pCrv->SetCurveTempParam( 0, GetMinFeed( PockParams), 0) ; else // semi cerchi in tangenza pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ; } } else if ( nOptType == 1) { // Trapezoidi for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ; } break ; /* NB. Essendo la funzione CalcSpiral richiamata sia per lo SpiralIN che per lo SpiralOUT le curve sono sempre orientate nello stesso modo, solamente alla fine viene invertita la curva finale per la svuotatura... */ case POCKET_SPIRALOUT : if ( nOptType == 0) { // Spiral verso l'esterno for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { if ( u > pCrv->GetCurveCount() - 3) // prime semi circonferenze pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ; else pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ; } } else if ( nOptType == 1) { // Trapezoidi for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ; } break ; default : break ; } return true ; } //---------------------------------------------------------------------------- static bool IsPointInsideSfr( const ISurfFlatRegion* pSfr, const Point3d& pt, bool& bIsInside) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid() || ! pt.IsValid()) return false ; bIsInside = false ; // ciclo sui chunk della FlatRegion for ( int i = 0 ; i < pSfr->GetChunkCount() ; i ++) { // verifico se è contenuto nel loop esterno PtrOwner pCrv( pSfr->GetLoop( i, 0)) ; PolyLine PL ; pCrv->ApproxWithLines( 10 * EPS_SMALL, 15, ICurve::APL_STD, PL) ; if ( IsPointInsidePolyLine( pt, PL, EPS_SMALL)) { bool bInsideHole = false ; // verifico se è contenuto in un loop interno for ( int j = 1 ; j < pSfr->GetLoopCount( i) ; j ++) { PtrOwner pCrv( pSfr->GetLoop( i, j)) ; pCrv->ApproxWithLines( 10 * EPS_SMALL, 15, ICurve::APL_STD, PL) ; PL.Invert() ; if ( IsPointInsidePolyLine( pt, PL, EPS_SMALL)) bInsideHole = true ; } // se è contenuto nel loop esterno ma non è contenuto in nessuno dei loop interni allora il punto è interno if ( ! bInsideHole) { bIsInside = true ; return true ; } } } return true ; } //---------------------------------------------------------------------------- static bool GetCoeffLinArc( const ICurveArc* pArc, double dDiam, double& dSubArc) { // controllo parametri if ( pArc == nullptr || ! pArc->IsValid()) return false ; // recupero il punto medio Point3d ptMid ; if ( ! pArc->GetMidPoint( ptMid)) return false ; // creo l'ombra del tool nel punto medio PtrOwner pCrvTool( CreateCurveArc()) ; pCrvTool->Set( ptMid, Z_AX, dDiam / 2) ; if ( ! pCrvTool->IsValid()) return false ; // effettuo la classificazione IntersCurveCurve intCC( *pArc, *pCrvTool) ; CRVCVECTOR ccClass ; if ( intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) && int( ccClass.size()) == 3 && ccClass[1].nClass == CRVC_IN) { // recupero i due punti di intersezione Point3d ptS, ptE ; pCrvTool->GetPointD1D2( ccClass[1].dParS, ICurve::FROM_MINUS, ptS) ; pCrvTool->GetPointD1D2( ccClass[1].dParE, ICurve::FROM_PLUS, ptE) ; // vettori uscenti dal centro dell'arco e diretti verso i punti di intersezione Vector3d vtS = ptS - pArc->GetCenter() ; Vector3d vtE = ptE - pArc->GetCenter() ; // angolo tra i due vettori double dTheta ; vtS.GetAngle( vtE, dTheta) ; dTheta = abs( dTheta) ; // lunghezza del tratto di arco compreso dSubArc = pArc->GetRadius() * DEGTORAD * dTheta ; } else return false ; return true ; } //---------------------------------------------------------------------------- static bool GetParamForPtStartOnEdge( const ICurve* pCrvCheck, const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vOtherCrv, const PocketParams& PockParams, double& dPar) { // ==================== INFO ================================================================ // pCrvCheck -> sottocurva di pCrvCompo su cui cercare il parametro ideale per ingresso // vOtherCrv -> tutte le altre curve che non devono essere intersecate durante l'ingresso // ( questo vettore è stato riempito con tutti loop della superificie da // lavorare, i quali Offset non hanno generato alcuna curva di pCrvCheck ) // ========================================================================================== // controllo dei parametri if ( pCrvCheck == nullptr || ! pCrvCheck->IsValid() || pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0) return false ; // clono le curve PtrOwner pCrv( pCrvCheck->Clone()) ; PtrOwner pCompo( pCrvCompo->Clone()) ; if ( IsNull( pCrv) || IsNull( pCompo)) return false ; ICRVCOMPOPOVECTOR vCrvNoInters ; for ( int i = 0 ; i < int( vOtherCrv.size()) ; ++ i) vCrvNoInters.emplace_back( ConvertCurveToComposite( vOtherCrv[i]->Clone())) ; // ricavo l'estrusione della curva composita Vector3d vtExtr ; if ( ! pCompo->GetExtrusion( vtExtr)) vtExtr = Z_AX ; // ricavo il centroide o il punto iniziale della curva composita Point3d ptCen ; if ( ! pCompo->GetCentroid( ptCen)) if ( ! pCompo->GetStartPoint( ptCen)) return false ; // creo un frame locale per creare l'ombra del tool e intersecarla con la curva Frame3d frLoc ; frLoc.Set( ptCen, vtExtr) ; // porto le curve in questo sistema di riferimento pCrv->ToLoc( frLoc) ; pCompo->ToLoc( frLoc) ; for ( int i = 0 ; i < ( int)vCrvNoInters.size() ; ++i) vCrvNoInters[i]->ToLoc( frLoc) ; // fisso il diametro del tool ( più grande per sicurezza ) double dDiam = ( 2 * PockParams.dRad ) + 2 * PockParams.dRadialOffset ; double dLen ; if ( ! pCrv->GetLength( dLen)) return false ; double dSubArc = -1 ; if ( pCrv->GetType() == CRV_ARC) { if ( ! GetCoeffLinArc( GetCurveArc( pCrv), dDiam, dSubArc)) return false ; } double dDiv = dSubArc < 0 ? dDiam : dSubArc ; double dMaxInt = floor( dLen / dDiv) ; int nDen = 2 ; // intervalli in cui suddivido la curva dMaxInt = max( 2.0, dMaxInt) ; // così se entra una volta controllo sempre dParT = 0.5 while ( nDen < dMaxInt + EPS_SMALL) { // EPS_SMALL per comprendere l'uguaglianza for ( int i = 1 ; i < nDen ; ++i) { if ( int( i % nDen) == 0) continue ; double dNum = 1.0 * i ; double dParT = dNum / nDen ; Point3d ptTest ; Vector3d vtPerpIn ; if ( pCrv->GetPointD1D2( dParT, ICurve::FROM_PLUS, ptTest, &vtPerpIn)) { PtrOwner pCompoClone( pCompo->Clone()) ; if ( IsNull( pCompoClone)) return false ; double dU ; pCompoClone->GetParamAtPoint( ptTest, dU) ; pCompoClone->ChangeStartPoint( dU) ; // se chiuso if ( pCrv->GetTempProp() == 0) { // se la curva è chiusa sposto il centro verso l'interno. // Devo essere sicuro di essere su una curva di Offset interno, altrimenti potrebbe essere valido // il punto trovato, però facendo poi il primo offeset tale punto potrebbe diventare un punto // di convergenza tra più chunks... vtPerpIn.Normalize() ; vtPerpIn.Rotate( Z_AX, 0, 1) ; // cos( pi/2) = 0, sin( pi/2) = 1 vtPerpIn *= (( PockParams.dRad + PockParams.dRadialOffset - 50 * EPS_SMALL)) ; ptTest = ptTest + vtPerpIn ; // creo una circonferenza ( ombra del tool ) centrata su ptTest PtrOwner pCrvToolShape( CreateCurveArc()) ; pCrvToolShape->Set( ptTest, Z_AX, 0.5 * dDiam) ; if ( ! pCrvToolShape->IsValid()) return false ; // interseco la curva composita con l'ombra del tool CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCompoClone, *pCrvToolShape) ; if ( intCC.GetCurveClassification( 0, EPS_SMALL, ccClass)) { // se ho solo due intersezioni, controllo di non intersecare isole ( In-Out-In) if ( int( ccClass.size() == 3)) { bool bOk = true ; for ( int j = 0 ; j < ( int)vCrvNoInters.size() && bOk ; ++ j) { IntersCurveCurve intCCI( *vCrvNoInters[j], *pCrvToolShape) ; if ( intCCI.GetCurveClassification( 0, EPS_SMALL, ccClass)) { if (( int)ccClass.size() > 1) bOk = false ; } else return false ; } if ( bOk) { dPar = dParT ; return true ; } } } else return false ; } // se aperto else { dPar = dParT ; return true ; } } } ++nDen ; } return false ; } //---------------------------------------------------- static bool ComputeTrapezoidSpiralLeadInLeadOut( ICurveComposite* pCompo, const Vector3d& vtMainDir, bool bLeadIn, const PocketParams& PockParams, bool& bIsOutsideRaw) { // inizializzazione come interno al grezzo bIsOutsideRaw = false ; Point3d ptP ; Vector3d vtDir ; if ( bLeadIn) { pCompo->GetStartPoint( ptP) ; pCompo->GetStartDir( vtDir) ; } else { pCompo->GetEndPoint( ptP) ; pCompo->GetEndDir( vtDir) ; } // recupero estrusione della curva Vector3d vtExtr ; pCompo->GetExtrusion( vtExtr) ; // recupero info sui lati aperti int nPropOpen = pCompo->GetTempProp( 0) ; bool bEdgeOpen = (( nPropOpen & ( bLeadIn ? 8 : 2)) > 0) ; bool bBase0Open = (( nPropOpen & 1) > 0) ; bool bBase1Open = (( nPropOpen & 4) > 0) ; // recupero info per capire se sto considerando un lato aggiuntivo per pulire angoli int nIdCrv = ( bLeadIn ? 0 : pCompo->GetCurveCount() - 1) ; int nExtraEdge ; pCompo->GetCurveTempProp( nIdCrv, nExtraEdge) ; // tento con allungamento se lato inclinato è aperto oppure se sto considerando un lato aggiuntivo per pulire angoli if ( bEdgeOpen || nExtraEdge == 1) { Vector3d vtDirP = ( bLeadIn ? -vtDir : vtDir) ; Point3d ptNewStart = ptP + vtDirP * ( PockParams.dRad + PockParams.dOpenMinSafe) ; pCompo->AddLine( ptNewStart, ! bLeadIn) ; pCompo->SetCurveTempProp( bLeadIn ? 0 : pCompo->GetCurveCount() - 1, 2) ; bIsOutsideRaw = true ; } // tento con attacco ruotato di 90° se non sto considerando un tratto aggiuntivo per pulire angoli else if (( bBase0Open || bBase1Open) && ! bIsOutsideRaw && nExtraEdge == 0) { Vector3d vtDirO = bBase0Open ? vtDir : - vtDir ; vtDirO.Rotate( vtExtr, ( PockParams.bInvert ? -90 : 90)) ; // se vicino al bordo del grezzo Point3d ptNewStart = ptP + vtDirO * ( PockParams.dRad + PockParams.dOpenMinSafe) ; pCompo->AddLine( ptNewStart, ! bLeadIn) ; pCompo->SetCurveTempProp( bLeadIn ? 0 : pCompo->GetCurveCount() - 1, 2) ; bIsOutsideRaw = true ; } return true ; } //---------------------------------------------------- static bool AdjustTrapezoidSpiralForLeadInLeadOut( ICurveComposite* pCompo, const PocketParams& PockParams) { // recupero la direzione principale della svuotatura Vector3d vtMainDir ; for ( int i = 0 ; i < pCompo->GetCurveCount() ; i++) { int nProp ; if ( pCompo->GetCurveTempProp( i, nProp) && nProp == 0) { // se non è lato aggiuntivo per la pulitura angoli recupero la sua direzione pCompo->GetCurve( i)->GetStartDir( vtMainDir) ; break ; } } // start point bool bStartOutside = false ; ComputeTrapezoidSpiralLeadInLeadOut( pCompo, vtMainDir, true, PockParams, bStartOutside) ; // end point bool bEndOutside = false ; ComputeTrapezoidSpiralLeadInLeadOut( pCompo, vtMainDir, false, PockParams, bEndOutside) ; // eventuale inversione della curva per partire sempre dall'esterno del grezzo if ( bEndOutside && ! bStartOutside) pCompo->Invert() ; return true ; } //---------------------------------------------------------------------------- static bool ExtendPathOnOpenEdge( ICurveComposite* pCrvPath, const PocketParams& PockParams, const Vector3d& vtN, Vector3d& vtMidOut, bool bEndOrStart) { // controllo dei parametri if ( pCrvPath == nullptr || ! pCrvPath->IsValid()) return false ; // recupero il punto iniziale della curva Point3d ptMidOpen ; if ( bEndOrStart) { if ( ! pCrvPath->GetEndPoint( ptMidOpen)) return false ; } else { if ( ! pCrvPath->GetStartPoint( ptMidOpen)) return false ; } // calcolo il punto fuori Point3d ptOut = ptMidOpen + vtMidOut * ( PockParams.dRad + PockParams.dOpenMinSafe) ; // aggiungo al ritorno l'uscita pCrvPath->AddLine( ptOut, bEndOrStart) ; pCrvPath->SetCurveTempProp( ( bEndOrStart ? pCrvPath->GetCurveCount() - 1 : 0), TMP_PROP_CRV_OPEN_SIDE, 0) ; // nProp 2 per le curve LeadIn/Out pCrvPath->SetCurveTempParam( ( bEndOrStart ? pCrvPath->GetCurveCount() - 1 : 0), GetMinFeed( PockParams) , 0) ; // Feed minima return true ; } //---------------------------------------------------------------------------- static bool AdjustContourStart( ICurveComposite* pCompo, const PocketParams& PockParams, const ICRVCOMPOPOVECTOR& vCrvIsl, bool bOrder, Point3d ptRef) { // se cerco semplicemente il tratto lineare chiuso più lungo ... if ( ! bOrder) { // la priorità è essagnata ai tratti lineari chiusi int i = 0 ; // <--- indice della curva corrente int nMax = - 1 ; double dLenMax = 0 ; const ICurve* pCrv = pCompo->GetFirstCurve() ; while ( pCrv != nullptr) { double dLen ; if ( pCrv->GetType() == CRV_LINE && pCrv->GetTempProp() == 0 && pCrv->GetLength( dLen) && dLen > dLenMax) { dLenMax = dLen ; nMax = i ; } ++ i ; pCrv = pCompo->GetNextCurve() ; } // se non trovato o troppo corto, cerco il tratto chiuso più lungo in generale if ( nMax < 0 || dLenMax < 2 * ( 2 * PockParams.dRad)) { i = 0 ; pCrv = pCompo->GetFirstCurve() ; while ( pCrv != nullptr) { double dLen ; if ( pCrv->GetType() != CRV_LINE && pCrv->GetTempProp() == 0 && pCrv->GetLength( dLen) && dLen > dLenMax) { dLenMax = dLen ; nMax = i ; } ++ i ; pCrv = pCompo->GetNextCurve() ; } } // controllo che il tratto chiuso più lungo trovato sia sufficientemente lungo if ( dLenMax < 10 * EPS_SMALL) { // se troppo piccolo allora lo imposto aperto pCompo->SetCurveTempProp( nMax, 0, 1) ; // spezzo la curva nel primo tratto aperto sufficientemente lungo for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) { double dLen ; pCompo->GetCurve( u)->GetLength( dLen) ; if ( dLen > 10 * EPS_SMALL) { nMax = u ; continue ; } } } // se non trovato, imposto il punto iniziale a mentà del primo tratto if ( nMax == -1) { pCompo->ChangeStartPoint( 0.5) ; return true ; } pCompo->ChangeStartPoint( nMax + 0.5) ; } // se invece sto cercando di entrare da un lato chiuso per una svuotatura, allora riordino i lati // chiusi a seconda della lunghezza, e a partire dal più lungo cerco un parametro su tale curva che mi consenta // un'entrata sufficientemente distante da isole e da altre curve della curva stessa su cui cerco l'entrata ... else { // se ho un punto di riferimento, allora come prima cosa controllo il lato chiuso più vicino if ( ptRef.IsValid()) { double dMinDist = INFINITO ; int nIndCrvMinDist = - 1 ; for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) { DistPointCurve DPC( ptRef, *pCompo->GetCurve( u)) ; double dCurrDist = INFINITO ; if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) { nIndCrvMinDist = u ; dMinDist = dCurrDist ; } } // se avessi curve alla stessa distanza ( circa) non controllo le altre, mi accontento della // prima curva trovata if ( nIndCrvMinDist != -1) { // controllo che il lato chiuso sia sufficientemente lungo double dLen = 0. ; pCompo->GetCurve( nIndCrvMinDist)->GetLength( dLen) ; if ( dLen > 2 * PockParams.dRad + 2 * PockParams.dRadialOffset + 500 * EPS_SMALL) { pCompo->ChangeStartPoint( nIndCrvMinDist + 0.5) ; return true ; } } } // creo un vettore di indici che definisce l'ordine delle curve chiuse in base alla lunghezza INTVECTOR vInd ; vInd.reserve( pCompo->GetCurveCount()) ; // vettore di indici già utilizzati INTVECTOR vIndUsed ; vIndUsed.reserve( pCompo->GetCurveCount()) ; double dMaxLen = -INFINITO ; int nCurrInd = 0 ; bool bStop = false ; 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 è chiusa... if ( find( vIndUsed.begin(), vIndUsed.end(), i) == vIndUsed.end() && pCompo->GetCurveTempProp( i, nProp_i, 0) && nProp_i == 0) { // creo la curva i-esima const ICurve* pCrv_i( pCompo->GetCurve( i)) ; double dLen_i ; if ( pCrv_i->GetLength( dLen_i) && dLen_i > dMaxLen) { // se di lunghezza maggiore alla soglia... dMaxLen = dLen_i ; nCurrInd = i ; bStop = false ; } } } vIndUsed.push_back( nCurrInd) ; vInd.push_back( nCurrInd) ; dMaxLen = -INFINITO ; } if ( vInd.empty()) { // se questa condizione fosse vera allora non sono riuscito ad entrare da nessun lato aperto in precedenza e non // ho nemmeno un lato chiuso disponibile per entrare... ( forzo l'entrata a metà del primo lato) pCompo->ChangeStartPoint( 0.5) ; return true ; } // ora che ho riempito il vettore di indici lo scorro e cerco di entrare in questi lati chiusi secondo l'ordine bool bOk = false ; for ( int i = 0 ; i < int( vInd.size()) && !bOk ; ++ i) { const ICurve* pCrv( pCompo->GetCurve( vInd[i])) ; double dPar ; if ( GetParamForPtStartOnEdge( pCrv, pCompo, vCrvIsl, PockParams, dPar)) { pCompo->ChangeStartPoint( vInd[i] + dPar) ; bOk = true ; } } if ( ! bOk) { // se non riesco ad entrare da nessun lato chiuso, considerando che in precedenza ho già provato ad // entrare da tutti i lati aperti... pCompo->ChangeStartPoint( vInd[0] + 0.5) ; return true ; } } return true ; } //---------------------------------------------------------------------------- static bool GetHomogeneousParts( ICurveComposite* pCrvCompo, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vpCrvs) { // controllo dei parametri vpCrvs.clear() ; if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0) // se curva non valida return true ; // la curva ha sia tratti aperti che tratti chiusi, quindi sposto l'inizio a metà del tratto più lungo ( se richiesto)... ICRVCOMPOPOVECTOR vCrvNULL ; AdjustContourStart( pCrvCompo, PockParams, vCrvNULL, false, P_INVALID) ; // estraggo parti con proprietà uniforme in un vettore ( vpCrvs) int nCurrTempProp ; int nParStart = 0 ; for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) { int nTempProp ; pCrvCompo->GetCurveTempProp( i, nTempProp) ; if ( i == 0) { nCurrTempProp = nTempProp ; nParStart = i ; } else if ( nCurrTempProp != nTempProp) { PtrOwner pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, i))) ; if ( IsNull( pCrv)) return false ; pCrv->SetTempProp( nCurrTempProp) ; vpCrvs.emplace_back( Release( pCrv)) ; nCurrTempProp = nTempProp ; nParStart = i ; } } // ultima curva... PtrOwner pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ; if ( IsNull( pCrv)) return false ; pCrv->SetTempProp( nCurrTempProp) ; vpCrvs.emplace_back( Release( pCrv)) ; return true ; } //--------------------------------------------------------------------------- static bool CreateSurfFrIncidence( const ICurveComposite* pCrv, const Vector3d& vtTanS_, const Vector3d& vtTanE_, const double dRad, ISurfFlatRegion* pSfrInc) { // controllo dei parametri if ( pCrv == nullptr || ! pCrv->IsValid()) return false ; pSfrInc->Clear() ; // se la curva è chiusa, allora la regione di incidenza è già definita if ( pCrv->IsClosed()) { PtrOwner pSfrInc_tmp( GetSurfFlatRegionFromFatCurve( pCrv->Clone(), dRad + EPS_SMALL, false, false)) ; if ( IsNull( pSfrInc_tmp) || ! pSfrInc_tmp->IsValid()) return false ; pSfrInc->CopyFrom( pSfrInc_tmp) ; return ( pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0) ; } // creo la curva di Offset esterna ( deve esistere ed essere valida) OffsetCurve OffsCrv ; PtrOwner pOffsExt( CreateCurveComposite()) ; if ( IsNull( pOffsExt) || ! OffsCrv.Make( pCrv, dRad, ICurve::OFF_FILLET) || ! pOffsExt.Set( OffsCrv.GetLongerCurve()) || IsNull( pOffsExt)) return false ; // creo la curva di Offset interna ( se esiste e valida...) PtrOwner pOffsInt( CreateCurveComposite()) ; if ( IsNull( pOffsInt)) return false ; if ( OffsCrv.Make( pCrv, - dRad, ICurve::OFF_FILLET)) { PtrOwner pMyCrv( OffsCrv.GetLongerCurve()) ; if ( ! IsNull( pMyCrv) && pMyCrv->IsValid()) pOffsInt.Set( pMyCrv) ; } // recupero gli estremi della curva aperta corrente Point3d pt1 ; pCrv->GetEndPoint( pt1) ; Point3d pt4 ; pCrv->GetStartPoint( pt4) ; // creo la regione dalle curve PtrOwner pCrvExtLoopSurfInc( CreateCurveComposite()) ; if ( IsNull( pCrvExtLoopSurfInc) || ! pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) || ! pCrvExtLoopSurfInc->AddLine( pt1)) return false ; Point3d pt5 ; pCrvExtLoopSurfInc->GetStartPoint( pt5) ; if ( ! IsNull( pOffsInt) && pOffsInt->IsValid()) { Point3d pt2 ; pOffsInt->GetEndPoint( pt2) ; Point3d pt3 ; pOffsInt->GetStartPoint( pt3) ; if ( ! pCrvExtLoopSurfInc->AddLine( pt2) || ! pOffsInt->Invert() || ! pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) || ! pCrvExtLoopSurfInc->AddLine( pt4) || ! pCrvExtLoopSurfInc->AddLine( pt5)) return false ; } else { if ( ! pCrvExtLoopSurfInc->AddLine( pt4) || ! pCrvExtLoopSurfInc->AddLine( pt5)) return false ; } // per sicurezza... pCrvExtLoopSurfInc->Close() ; // creo la regione if ( ! pSfrInc->AddExtLoop( Release( pCrvExtLoopSurfInc))) return false ; return ( pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0) ; } //---------------------------------------------------------------------------- static bool AdjustOpenEdge( const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vCrvIsland, const double dParS, const double dParE, const Vector3d& vtTanS, const Vector3d& vtTanE, const double dRad, const double dDiamJ, const PocketParams& PockParams, ICurveComposite* pCrvBorder) { /* parametri : pCrvCompo -> curva originaria di bordo vCrvIsland -> vettore delle isole all'interno di pCrvCompo pCrvOpenOffs -> tratto aperto corrente già Offsettato verso l'esterno dParS -> parametro sulla pCrvCompo per l'inizio del tratto aperto dParE -> parametro sulla pCrvCompo per la fine del tratto aperto vtTanS -> vettore di tangenza finale tratto chiuso precedente vtTanE -> vettore di tangenza iniziale ( invertito ) del tratto chiuso successivo dRad -> raggio di Offset per la regione di incidenza dDiamJ -> ampiezza delle curve a fagiolo per estendere la regione di incidenza pCrvRes -> curva da restituire ( inizialmente è il tratto aperto sulla pCrvCompo ; questa curva sarà l'estensione del lato aperto, adattandosi alla geometria dei chiusi ( non posso vedere solo i chiusi adiacenti all'aperto, devo considerare TUTTI i chiusi della pCrvCompo pStmVol -> Volume di svuotatura pStm_Part -> Part corrente */ // controllo la validità dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 || pCrvBorder == nullptr || ! pCrvBorder->IsValid() || pCrvBorder->GetCurveCount() == 0) return false ; // definisco la regione di incidenza PtrOwner pSfrInc( CreateSurfFlatRegion()) ; if ( IsNull( pSfrInc) || ! CreateSurfFrIncidence( pCrvBorder, vtTanS, vtTanE, dRad + 75 * EPS_SMALL, pSfrInc)) return false ; // creo un vettore con tutte le curve che potrebbero cadere, in parte, nella regione di incidenza ICRVCOMPOPOVECTOR vCrvToCheck ; for ( int i = 0 ; i < int( vCrvIsland.size()) ; ++ i) vCrvToCheck.emplace_back( vCrvIsland[i]->Clone()) ; // aggiungo le isole // se la curva originale non è tutta Aperta -> devo aggiungere anche essa nelle curve da controllare bool bIsAllOpen = abs( abs( dParE - dParS) - pCrvCompo->GetCurveCount()) < EPS_SMALL ; if ( ! bIsAllOpen) { // recupero il tratto di curva prima e dopo dell'aperto corrente PtrOwner pCompoOther( ConvertCurveToComposite( pCrvCompo->CopyParamRange( dParE, dParS))) ; if ( IsNull( pCompoOther) || ! pCompoOther->IsValid()) return false ; vCrvToCheck.emplace_back( Release( pCompoOther)) ; // aggiungo il bordo } // controllo se la curva è un'isola ( normale del piano -Z_AX) bool bIsIsland = false ; double dArea ; Plane3d plCheck ; if ( ! pCrvCompo->GetArea( plCheck, dArea)) return false ; bIsIsland = AreSameVectorEpsilon( plCheck.GetVersN(), - Z_AX, 10 * EPS_SMALL) ; // scorro il vettore creato... for ( int c = 0 ; c < int( vCrvToCheck.size()) ; ++ c) { // 1) recupero la curva corrente PtrOwner pCrvCurr( vCrvToCheck[c]->Clone()) ; if ( IsNull( pCrvCurr) || ! pCrvCurr->IsValid()) return false ; // 2) ricavo i tratti con proprietà uniformi ( Aperti/Chiusi ) ICRVCOMPOPOVECTOR vpCrvs ; if ( ! GetHomogeneousParts( pCrvCurr, PockParams, vpCrvs)) return false ; // 3) considero solo i tratti chiusi for ( int cl = 0 ; cl < int( vpCrvs.size()) ; ++ cl) { if ( vpCrvs[cl]->GetTempProp() == 1) continue ; // 4) effettuo l'Offset della curva di metà dDiamJ OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( vpCrvs[cl], - dDiamJ * 0.5 - 20 * EPS_SMALL, ICurve::OFF_FILLET)) return false ; // 5) scorro tutte le curve di Offset che si sono formate, prendendo sempre la più lunga tra le rimanenti PtrOwner pOffLongestCrv( OffsCrv.GetLongerCurve()) ; while ( ! IsNull( pOffLongestCrv)) { // 6) creo la regione di incidenza di tale curva ( "Curva a fagiolo") PtrOwner pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ * 0.5, false, false)) ; if ( IsNull( pSfrBean) || ! pSfrBean->IsValid()) return false ; // inverto se necessario if ( AreOppositeVectorApprox( pSfrBean->GetNormVersor(), pSfrInc->GetNormVersor())) pSfrBean->Invert() ; // 7) se la "Regione a fagiolo" non influenza la regione di incidenza, la transcuro bool bDiscard = false ; if ( ! bIsIsland) { // se tratto un loop esterno for ( int cI = 0 ; cI < pSfrInc->GetChunkCount() && ! bDiscard ; ++ cI) for ( int cB = 0 ; cB < pSfrBean->GetChunkCount() && ! bDiscard ; ++ cB) bDiscard = ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrBean, cB) != REGC_INTERS) ; } else if ( bIsAllOpen && bIsIsland) { // se isola aperta for ( int cI = 0 ; cI < pSfrInc->GetChunkCount() && ! bDiscard ; ++ cI) { for ( int cB = 0 ; cB < pSfrBean->GetChunkCount() && ! bDiscard ; ++ cB) { if ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrBean, cB) == REGC_IN1) { for ( int l = 1 ; l < pSfrBean->GetLoopCount( cB) ; ++ l) { PtrOwner pSfrTmp( CreateSurfFlatRegion()) ; if ( IsNull( pSfrTmp) || ! pSfrTmp->AddExtLoop( pSfrBean->GetLoop( cB, l)) || ! pSfrTmp->Invert()) return false ; bDiscard = ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrTmp, 0) == REGC_IN1) ; } } } } } if ( ! bDiscard) { // 8) aggiorno la regione di incidenza if ( ! pSfrInc->Add( *pSfrBean)) return false ; } pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ; // passo al tratto offsettato successivo } } } // ATTENZIONE ! // L'algoritmo di allargamento presso i lati aperti è Euristico; io mi estendo a seconda della geometria del // lato aperto al di fuori del volume di svuotatura... Devo controllare di non rovinare delle zone al di fuori di // esso ! if ( PockParams.SfrLimit.IsValid()) { // recupero la superficie limite PtrOwner pSfrLimit( PockParams.SfrLimit.Clone()) ; if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid()) return false ; // piccolo Offset per sicurezza pSfrLimit->Offset( 50 * EPS_SMALL, ICurve::OFF_FILLET) ; pSfrInc->Subtract( *pSfrLimit) ; // rimuovo la regione limite // può capitare che la regione Limite mi crei più Chunk sulla pSfrInc // I chunk da togliere sono tutti quelli che si sono separati dalla pCrvBorder // NB. Il mio obiettivo è quello di avere un'unica curva con estremi i due tratti chiusi estremanti if ( pSfrInc->GetChunkCount() > 1) { // se ottengo più chunks PtrOwner pNewSfrInc( CreateSurfFlatRegion()) ; if ( IsNull( pNewSfrInc)) return false ; // ricavo i punti iniziali e finali della curva di bordo Point3d ptS, ptE ; if ( ! pCrvBorder->GetStartPoint( ptS) || ! pCrvBorder->GetEndPoint( ptE)) return false ; // per ogni Chunk ( > 1 ) for ( int i = 0 ; i < pSfrInc->GetChunkCount() ; ++ i) { // bordo esterno PtrOwner pCrvEL( ConvertCurveToComposite( pSfrInc->GetLoop( i, 0))) ; if ( IsNull( pCrvEL) || ! pCrvEL->IsValid()) return false ; // se estremi in comune, la curva cercata è la seguente if ( pCrvEL->IsPointOn( ptS, 300 * EPS_SMALL) && pCrvEL->IsPointOn( ptE, 300 * EPS_SMALL)) { // se la nuova regione di incidenza è vuota, aggiorno if ( ! pNewSfrInc->IsValid()) pNewSfrInc.Set( pSfrInc->CloneChunk( i)) ; // se la nuova regione di incidenza non è vuota, aggiungo else { PtrOwner pSfrToAdd( pSfrInc->CloneChunk( i)) ; if ( IsNull( pSfrToAdd) || ! pNewSfrInc->Add( *pSfrToAdd)) return false ; } } } // definisco quindi la nuova regione di incidenza if ( ! pNewSfrInc->IsValid()) return false ; pSfrInc.Set( pNewSfrInc) ; } } // dalla regione di incidenza devo estrarre il tratto di bordo che ha per estremi i lati chiusi // ( stando al di fuori, quindi estendendomi all'esterno, della regione da svuotare ) PtrOwner pCrvNewBorder( ConvertCurveToComposite( pSfrInc->GetLoop( 0, 0))) ; if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid()) return false ; // imposto la curva come tutta aperta for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u) pCrvNewBorder->SetCurveTempProp( u, 1, 0) ; // se la curva originale era tutta aperta... ( il nuovo lato è il loop esterno della regione di incidenza) if ( bIsAllOpen) { // pulisco la curva originale pCrvBorder->Clear() ; if ( bIsIsland) { // se isola inserisco il loop interno corretto della regione for ( int l = 1 ; l < pSfrInc->GetLoopCount( 0) ; ++ l) { // recupero il Loop PtrOwner pCrvIntLoop( ConvertCurveToComposite( pSfrInc->GetLoop( 0, l))) ; if ( IsNull( pCrvIntLoop) || ! pCrvIntLoop->IsValid()) return false ; // controllo che sia interno alla curva di bordo corrente ( l'isola aperta) IntersCurveCurve ICC( *pCrvIntLoop, *pCrvCompo) ; CRVCVECTOR ccClass ; if ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) && int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) { // il loop interno corretto va impostato come unico e nuovo loop interno della regione pCrvNewBorder->CopyFrom( pCrvIntLoop) ; if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid()) return false ; // imposto la curva come tutta aperta ( gira come girava già l'isola) for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u) pCrvNewBorder->SetCurveTempProp( u, 1, 0) ; break ; } } } // se bordo esterno inserisco il bordo esterno return ( pCrvBorder->AddCurve( Release( pCrvNewBorder))) ; } // altrimenti la spezzo il loop della regione di incidenza nei punti iniziali e finali della curva aperta originale Point3d ptStart ; pCrvBorder->GetStartPoint( ptStart) ; Point3d ptEnd ; pCrvBorder->GetEndPoint( ptEnd) ; double dUTrimS = -1 ; double dUTrimE = -1 ; pCrvNewBorder->GetParamAtPoint( ptStart, dUTrimS, 10000 * EPS_SMALL) ; pCrvNewBorder->GetParamAtPoint( ptEnd, dUTrimE, 10000 * EPS_SMALL) ; // pulisco la curva originale pCrvBorder->Clear() ; if ( ! pCrvBorder->AddCurve( pCrvNewBorder->CopyParamRange( dUTrimS, dUTrimE)) || ! pCrvBorder->IsValid()) { // se la curva non originaria di lato aperto non era chiusa... if ( ! pCrvBorder->IsClosed() && pCrvNewBorder->IsClosed()) { // cerco il punto più vicino al ptStart del chiuso sulla nuova curva di bordo aperta Point3d ptMinDist ; int nFlag ; if ( ! DistPointCurve( ptStart, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag)) return false ; // cambio il punto iniziale della border in ptMinDist double dUTmp ; if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp) || ! pCrvNewBorder->ChangeStartPoint( dUTmp)) return false ; // cerco il punto più vicino al ptEnd del chiuso sulla curva di bordo aperta if ( ! DistPointCurve( ptEnd, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag)) return false ; // ricavo il parametro sulla curva if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp)) return false ; if ( dUTmp < EPS_SMALL) dUTmp = pCrvNewBorder->GetCurveCount() - 5 * EPS_SMALL ; // copio la curva tra questi due parametri PtrOwner pCrvTmp( ConvertCurveToComposite( pCrvNewBorder->CopyParamRange( 0., dUTmp))) ; if ( IsNull( pCrvTmp) || ! pCrvTmp->IsValid()) return false ; // raccordo con il chiuso mediante due tratto lineare pCrvTmp->AddLine( ptStart, false) ; pCrvTmp->AddLine( ptEnd, true) ; // restituisco pCrvBorder->CopyFrom( pCrvTmp) ; return true ; } pCrvBorder->CopyFrom( pCrvCompo) ; } return true ; } //---------------------------------------------------------------------------- static bool AdjustContourWithOpenEdges( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vCrvIsl, const PocketParams& PockParams) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // recupero i parametri di lavorazione correnti double dDiam = PockParams.dRad * 2 ; double dOffR = PockParams.dRadialOffset ; double dStep = PockParams.dSideStep ; // raggio di riferimento per offset double dOutEdge = 0.5 * dDiam ; if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_ZIGZAG) dOutEdge = max( dOutEdge, dDiam - dStep) ; double dRad = dOutEdge + dOffR ; if ( abs( PockParams.dOpenEdgeRad) > 0 && PockParams.dOpenEdgeRad < dRad) dRad = PockParams.dOpenEdgeRad ; // ricavo i tratti omogenei ICRVCOMPOPOVECTOR vpCrvs ; if ( ! GetHomogeneousParts( pCrvCompo, PockParams, vpCrvs)) return false ; // NB. Il primo tratto è la prima metà del tratto aperto più lungo ( se esiste ) // Offset esterno per CurveJ double dDiamJ = 1.05 * ( dDiam) + 2 * dOffR ; if ( abs( PockParams.dOpenEdgeRad) > 0) dDiamJ = 100 * EPS_SMALL ; // NB. 1.05 serve per eccedere leggermente, in modo che il contro-offset della prima curva di svuotatura presenti // dei piccoli archi tra i chiusi e gli aperti ( in modo da svuotare bene lungo il chiuso) // curva finale da restituire PtrOwner pCrvCompo_final( CreateCurveComposite()) ; if ( IsNull( pCrvCompo_final)) return false ; // parametro iniziale del tratto corrente, sulla curva originale double dParS = 0. ; // scorro tutti i tratti omogenei nel vettore for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { // recupero la proprietà della curva composita int nCurrTmpProp = vpCrvs[i]->GetTempProp() ; // aggiorno i parametri del lato aperto corrente sulla curva originale double dParE = dParS + 1. * vpCrvs[i]->GetCurveCount() ; // se aperta if ( nCurrTmpProp == 1) { // ricavo la tangenze dei lati chiusi agli estremi di questa curva Vector3d vtTanS = V_INVALID ; Vector3d vtTanE = V_INVALID ; if ( i != 0) { if ( ! vpCrvs[i-1]->GetEndDir( vtTanS) || // tangente finale del chiuso precedente ! vpCrvs[( i + 1) % int( vpCrvs.size())]->GetStartDir( vtTanE)) // tangente iniziale del chiuso successivo... return false ; vtTanE.Invert() ; // invertita } if ( ! AdjustOpenEdge( pCrvCompo, vCrvIsl, dParS, dParE, vtTanS, vtTanE, dRad, dDiamJ, PockParams, vpCrvs[i])) return false ; } // assegno le proprietà di lato Aperto/Chiuso per la curva corrente for ( int u = 0 ; u < vpCrvs[i]->GetCurveCount() ; ++ u) vpCrvs[i]->SetCurveTempProp( u, nCurrTmpProp, 0) ; // aggiungo la curva ricavata ( se chiusa -> la copio, se aperta -> copio l'estesa) if ( ! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone())) { // per sicurezza, se gli estremi non coincidono, creo un piccolo raccordo lineare Point3d ptH ; vpCrvs[i]->GetStartPoint( ptH) ; if ( ! pCrvCompo_final->AddLine( ptH) || ! pCrvCompo_final->SetCurveTempProp( pCrvCompo_final->GetCurveCount() - 1, 1, 0) || ! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone())) return false ; } // aggiorno dParS = dParE ; } // non dovrebbe esserci un gap, ma meglio prevenire problemi pCrvCompo_final->Close() ; // sostituisco pCrvCompo->Clear() ; pCrvCompo->CopyFrom( pCrvCompo_final) ; return true ; } //---------------------------------------------------------------------------- static bool ChainCompoCurves( ICRVCOMPOPOVECTOR& vCrvCompo) { /* concatenamento delle curve composite */ if ( int( vCrvCompo.size() < 2)) return true ; // controllo validità delle curve for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid()) return false ; // preparo i dati per il concatenamento bool bFirst = true ; Point3d ptNear = ORIG ; double dToler = 500 * EPS_SMALL ; // leggermente maggiore della tolleranza sulla superficie limite ChainCurves chainC ; chainC.Init( false, dToler, int( vCrvCompo.size())) ; for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) { // recupero i dati della curva necessari al concatenamento e li assegno Point3d ptStart, ptEnd ; Vector3d vtStart, vtEnd ; if ( ! vCrvCompo[i]->GetStartPoint( ptStart) || ! vCrvCompo[i]->GetStartDir( vtStart) || ! vCrvCompo[i]->GetEndPoint( ptEnd) || ! vCrvCompo[i]->GetEndDir( vtEnd)) return false ; if ( ! chainC.AddCurve( int( i + 1), ptStart, vtStart, ptEnd, vtEnd)) return false ; // se prima curva, assegno inizio della ricerca if ( bFirst) { ptNear = ptStart + 10 * EPS_SMALL * vtStart ; bFirst = false ; } } // vettore delle curve composite risultante ICRVCOMPOPOVECTOR vCrvCompoChained ; // recupero i percorsi concatenati INTVECTOR vnInd ; while ( chainC.GetChainFromNear( ptNear, true, vnInd)) { // creo una curva composita PtrOwner pCrvCompo( CreateCurveComposite()) ; if ( IsNull( pCrvCompo)) return false ; // recupero le curve semplici e le inserisco nella curva composita for ( size_t i = 0 ; i < vnInd.size() ; ++ i) { int nId = abs( vnInd[i]) - 1 ; bool bInvert = ( vnInd[i] < 0) ; // se necessario, la inverto if ( bInvert) vCrvCompo[nId]->Invert() ; // la aggiungo alla curva composta if ( ! pCrvCompo->AddCurve( ::Release( vCrvCompo[nId]), true, dToler)) return false ; } // aggiorno il nuovo punto vicino if ( pCrvCompo->GetCurveCount() > 0) { pCrvCompo->GetEndPoint( ptNear) ; vCrvCompoChained.emplace_back( Release( pCrvCompo)) ; } } swap( vCrvCompoChained, vCrvCompo) ; return true ; } //---------------------------------------------------------------------------- static bool AdjustOpenCurves( ICRVCOMPOPOVECTOR& vCrvCompo, const PocketParams& PockParams) { /* sistemazione punto di inizio, segmento per LeadIn, e Feed ( Se la curva è chiusa, allora si può cambiare il punto d'inizio) */ // controllo validità delle curve for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid()) return false ; // recupero la superficie limite ( se esiste ed è valida) PtrOwner pSfrLimit( CreateSurfFlatRegion()) ; if ( IsNull( pSfrLimit)) return false ; if ( PockParams.SfrLimit.IsValid()) { // se esiste, allora classifico // recupero la superficie limite pSfrLimit.Set( PockParams.SfrLimit.Clone()) ; if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid()) return false ; // effettuo un Offset della regione, tutto ciò che dista più del raggio utensile non la rovina pSfrLimit->Offset( PockParams.dRad - 250 * EPS_SMALL, ICurve::OFF_FILLET) ; // c'è un offset radiale, quindi tengo tolleranza alta } // per ogni curva signola, aggiungo l'entrata da fuori se ammissibile for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) { int nMaxIter = ( vCrvCompo[i]->IsClosed() ? 4 : 1) ; double dLen = EPS_SMALL ; if ( nMaxIter == 4) vCrvCompo[i]->GetLength( dLen) ; if ( PockParams.bInvert) vCrvCompo[i]->Invert() ; for ( int j = 0 ; j < nMaxIter ; ++ j) { if ( j > 0) vCrvCompo[i]->ChangeStartPoint( dLen / nMaxIter) ; // ricavo punto di inizio e vettore tangente Point3d ptStart ; vCrvCompo[i]->GetStartPoint( ptStart) ; Vector3d vtStart ; vCrvCompo[i]->GetStartDir( vtStart) ; // recupero il punto di caduta dell'utensile Point3d ptFall = ptStart - vtStart * ( 2 * PockParams.dRad + PockParams.dOpenMinSafe) ; // controllo che il tool non sia interno alla regione limite bool bAllowLeadIn = ( ! pSfrLimit->IsValid()) ; if ( ! bAllowLeadIn) { // calcolo l'ombra del tool centrata in ptFall PtrOwner pCrvToolShape( CreateCurveArc()) ; if ( IsNull( pCrvToolShape) || ! pCrvToolShape->Set( ptFall, Z_AX, PockParams.dRad - 100 * EPS_SMALL)) return false ; CRVCVECTOR ccClass ; bAllowLeadIn = ( pSfrLimit->GetCurveClassification( *pCrvToolShape, EPS_SMALL, ccClass) && int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) ; // se esiste intersezione con la limite Offsettata, provo a ruotare di 90° il tratto lineare if ( ! bAllowLeadIn) { vtStart.Rotate( Z_AX, PockParams.bInvert ? - ANG_RIGHT : ANG_RIGHT) ; ptFall = ptStart + vtStart * ( 2 * PockParams.dRad + PockParams.dOpenMinSafe) ; if ( ! pCrvToolShape->Set( ptFall, Z_AX, PockParams.dRad - 100 * EPS_SMALL)) return false ; ccClass.clear() ; bAllowLeadIn = ( pSfrLimit->GetCurveClassification( *pCrvToolShape, EPS_SMALL, ccClass) && int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) ; } } if ( bAllowLeadIn) { // se ammessa entrata da fuori vCrvCompo[i]->AddLine( ptFall, false) ; vCrvCompo[i]->SetCurveTempProp( 0, TMP_PROP_CRV_OPEN_SIDE, 0) ; // nProp 2 per le curve LeadIn/Out vCrvCompo[i]->SetCurveTempParam( 0, GetMinFeed( PockParams) , 0) ; // Feed massima break ; // interrompo a prescindere l'iterazione per i punti iniziali } } } // imposto le Feed per tali curve for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) { /* Idea : Feed proporzionale al minimo Offset per annullare la regione -> Massimo parametro sui bisettori di VORONOI */ // recupero il MaxOffset per la curva double dMaxOffs = vCrvCompo[i]->GetTempParam( 0) ; // calcolo la Feed proporzionale a tale Offset double dFeed ; GetFeedForParam( 2 * dMaxOffs, PockParams, dFeed) ; AssignCustomFeed( vCrvCompo[i], PockParams, dFeed) ; // imposto il flag di curva singola vCrvCompo[i]->SetTempProp( TMP_PROP_SINGLE_CURVE, 0) ; //nProp per curva singola } return true ; } //---------------------------------------------------------------------------- static bool GetPocketCurvesByCloseEdges( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes, bool& bAllRemoved) { // controllo parametri if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid()) return false ; bAllRemoved = false ; // controllo se la superficie si annulla con un controOffset del raggio utensile double dMaxOffs ; pSfrChunk->GetMaxOffset( dMaxOffs) ; if ( dMaxOffs > PockParams.dRad + 200 * EPS_SMALL) return true ; /* NB. Si poteva calcolare la Fat curve del raggio utensile dell'Offset di tutti i chiusi e vedere se l'unione di queste regioni copriva interamente il Chunk attuale... così facendo rischio di trascurare delle parti che spariscono facendo il primo Offset dei chiusi; il controllo va fatto quindi con un contro-offset della regione complessiva */ // controllo se il Chunk ha delle isole bool bHasIslands = ( pSfrChunk->GetLoopCount( 0) > 1) ; // recupero il Loop esterno PtrOwner pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ; if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid()) return false ; // recupero i tratti con proprietà uniformi ICRVCOMPOPOVECTOR vpCrvs ; GetHomogeneousParts( pCrvExtLoop, PockParams, vpCrvs) ; if ( vpCrvs.size() > 1) { // unisco il primo e l'ultimo se estremi compatibili // NB. GetHomogeneousParts() cambia il punto di inizio nel lato chiuso più lungo ( se presente) Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ; Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ; if ( AreSamePointApprox( ptS, ptE)) { vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ; vpCrvs.erase( vpCrvs.end() - 1) ; } } // controllo se il loop Esterno è uniforme ( quindi tutto chiuso o tutto aperto) bool bExtAllClose = false ; bool bExtAllOpen = false ; if ( int( vpCrvs.size()) == 1) { if ( vpCrvs[0]->GetTempProp( 0) == 0) bExtAllClose = true ; else bExtAllOpen = true ; } // NB. Se regione estena tutta chiusa o aperta e senza isole... allora non faccio nulla ( es caso trapezi) // Se tutta chiusa -> Chunk non svuotabile, il contro-offset del raggio utensile annulla la regione // Se tutta aperta -> La regione è definita mediante Offset esterno del contorno aperto if ( ( bExtAllClose || bExtAllOpen) && ! bHasIslands) return true ; // Se il contorno esterno è misto -> non devo avere isole if ( ! bExtAllClose && ! bExtAllOpen && bHasIslands) return true ; // Se il contorno esterno è tutto chiuso e ho più di un'isola non faccio nulla if ( bExtAllClose && pSfrChunk->GetLoopCount( 0) > 2) return true ; // scorro i chiusi e sottraggo la regione da loro creata mediante l'utensile // NB. Queste curve sono orientate in maniera corretta ICRVCOMPOPOVECTOR vCrvCompoRes_tmp ; // salvo il vettore di curve offsettate del raggio utensile for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { if ( vpCrvs[i]->GetTempProp( 0) == 0) { // se tratto chiuso... OffsetCurve OffsCrv ; // con offset verso l'interno if ( OffsCrv.Make( vpCrvs[i], - PockParams.dRad - PockParams.dRadialOffset, ICurve::OFF_FILLET)) { PtrOwner pOffLongestCrv( OffsCrv.GetLongerCurve()) ; while ( ! IsNull( pOffLongestCrv)) { // salvo la curva corrente vCrvCompoRes_tmp.emplace_back( ConvertCurveToComposite( Release( pOffLongestCrv))) ; // passo alla successiva pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ; } } } } /* Casi gestiti : 1) La curva esterna e mista e non ho isole ( ho già il percorso) 2) La curva esterna è tutta aperta e le isole sono tutte chiuse 3) La curva esterna è tutta chiusa e ho una sola isola aperta ( ho già il percorso) */ // 1) if ( ! bExtAllClose && ! bExtAllOpen) ; else { // scorro tutte le isole bool bValidIslands = true ; // flag per isole tutte uniformi for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) && bValidIslands ; ++ i) { // controllo uniformità dell'isola int nCurrTmpProp = -1 ; int nPrecTmpProp = -1 ; bool bIsMixed = false ; PtrOwner pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ; if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid()) return false ; for ( int u = 0 ; u < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ u) { pCrvIsl->GetCurveTempProp( u, nCurrTmpProp, 0) ; bIsMixed = ( u != 0 && nCurrTmpProp != nPrecTmpProp) ; nPrecTmpProp = nCurrTmpProp ; } // se proprità non uniformi -> tutta chiusa ( isole non uniformi non sono definite) if ( bIsMixed) { for ( int u = 0 ; u < pCrvIsl->GetCurveCount() ; ++ u) pCrvIsl->SetTempProp( u, 0) ; nCurrTmpProp = 0 ; // aggiorno il Flag } // 2) e 3) bValidIslands = ( ( nCurrTmpProp == 0 && bExtAllOpen) || ( nCurrTmpProp == 1 && bExtAllClose)) ; } if ( ! bValidIslands) return true ; // nel caso 2) devo inserire le curve // creo una regione formata solo dalla isole PtrOwner pSrfIslands( CreateSurfFlatRegion()) ; if ( IsNull( pSrfIslands)) return false ; for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) ; ++ i) pSrfIslands->AddExtLoop( pSfrChunk->GetLoop( 0, i)) ; pSrfIslands->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET) ; for ( int c = 0 ; c < pSrfIslands->GetChunkCount() ; ++ c) for ( int l = 0 ; l < pSrfIslands->GetLoopCount( l) ; ++ l) vCrvCompoRes_tmp.emplace_back( ConvertCurveToComposite( pSrfIslands->GetLoop( c, l))) ; } // se non ho ottenuto curve, allora ho finito if ( vCrvCompoRes_tmp.empty()) return true ; bAllRemoved = true ; // recupero la superficie limite ( se esiste ed è valida) PtrOwner pSfrLimit( CreateSurfFlatRegion()) ; if ( IsNull( pSfrLimit)) return false ; // per tutte le curve inserite, devo tenere solamente quelle esterne alla superficie limite if ( PockParams.SfrLimit.IsValid()) { // se esiste, allora classifico // recupero la superficie limite pSfrLimit.Set( PockParams.SfrLimit.Clone()) ; if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid()) return false ; // effettuo un Offset della regione, tutto ciò che dista più del raggio utensile non la rovina pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset - 250 * EPS_SMALL, ICurve::OFF_FILLET) ; // c'è un offset radiale, quindi tengo tolleranza alta ICRVCOMPOPOVECTOR vCrvCompoRes_tmp_Splitted ; for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) { CRVCVECTOR ccClass ; if ( pSfrLimit->GetCurveClassification( *vCrvCompoRes_tmp[i], EPS_SMALL, ccClass)) { for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { if ( ccClass[j].nClass == CRVC_OUT) { PtrOwner pCrvRes( ConvertCurveToComposite( vCrvCompoRes_tmp[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ; if ( ! IsNull( pCrvRes) && pCrvRes->IsValid()) vCrvCompoRes_tmp_Splitted.emplace_back( Release( pCrvRes)) ; } } } } // se non ho curve, allora esco if ( vCrvCompoRes_tmp_Splitted.empty()) return true ; // altrimenti aggiorno il vettore di curve con quelle solo esterne alla regione limite swap( vCrvCompoRes_tmp, vCrvCompoRes_tmp_Splitted) ; } // salvo come primo tempParam l'offset massimo della regione ( servirà per la Feed) for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) { vCrvCompoRes_tmp[i]->SetTempParam( dMaxOffs, 0) ; vCrvCompoRes.emplace_back( Release( vCrvCompoRes_tmp[i])) ; // aggiungo la curva al vettore } return true ; } //---------------------------------------------------------------------------- static bool ModifySurfByOpenEdges( ISurfFlatRegion* pSfr, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes, bool& bSkipPocket) { // controllo parametri : if ( pSfr == nullptr || ! pSfr->IsValid()) return true ; // <- se superficie non valida, allora non ho niente da impostare sui suoi lati bSkipPocket = false ; // NB. Tutti i Loop che presentano dei lati aperti possono essere estesi ; sia per loop esterni che per isole... // I lati aperti vanno estesi seguendo lo geometria dei lati chiusi adiacenti e tenendo conto delle isole chiuse // vicine ad essi // creo la superficie da restituire... ( questa superficie sarà estesa presso i lati aperti) PtrOwner pSrfFinal( CreateSurfFlatRegion()) ; if ( IsNull( pSrfFinal)) return false ; int nChunkOneCurve = 0 ; // numero di chunk rimovibili mediante una sola curva di Offset int nChunk = pSfr->GetChunkCount() ; // numero complessivo di Chunk da svuotare // per ogni Chunck della superificie ottenuta... for ( int c = 0 ; c < nChunk ; ++ c) { // 0) controllo se seguendo i chiusi posso svuotare l'interno chunk /* nel caso in cui il chunk c-esimo può essere svuotata da una o più curve che seguono il/i tratto/i chiuso/i, queste vengono salvate direttamente nei percorsi di svuotatura. Il chuck c-esimo verrà poi ignorato ( quindi nessuna estensione presso gli aperti) */ PtrOwner pSfrChunk( pSfr->CloneChunk( c)) ; if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid()) return false ; bool bIsAllRemoved = false ; GetPocketCurvesByCloseEdges( pSfrChunk, PockParams, vCrvCompoRes, bIsAllRemoved) ; if ( bIsAllRemoved) { // se ho rimosso tutto, allora passo la chunk successivo ++ nChunkOneCurve ; // aumento il contatore continue ; // ignoro il chunk c-esimo } // vettore delle isole che userò ( le isole aperte piccole sono trascurate) ICRVCOMPOPOVECTOR vCrvToTIsland ; // Flag per sapere se c'è stata almeno una modifica in un loop bool bIsChunkModified = false ; // 1) ricavo il Loop esterno PtrOwner pCrvEL( ConvertCurveToComposite( pSfr->GetLoop( c, 0))) ; if ( IsNull( pCrvEL) || ! pCrvEL->IsValid()) return false ; // 2) creo un vettore di curve con le isole del Chunk ICRVCOMPOPOVECTOR vCrvIsl ; for ( int l = 1 ; l < pSfr->GetLoopCount( c) ; ++ l) { PtrOwner pCrvIL( ConvertCurveToComposite( pSfr->GetLoop( c, l))) ; if ( IsNull( pCrvIL) || ! pCrvIL->IsValid()) return false ; vCrvIsl.emplace_back( Release( pCrvIL)) ; } // 3) se la curva esterna presenta dei lati aperti -> devo modificarla bool bSomeOpen = false ; int nProp0 = -1 ; for ( int u = 0 ; u < pCrvEL->GetCurveCount() && ! bSomeOpen ; ++ u) if ( pCrvEL->GetCurveTempProp( u, nProp0, 0) && nProp0 == 1) bSomeOpen = true ; if ( bSomeOpen) { // se trovo dei lati aperti // 3.1) sistemo la superificie if ( ! AdjustContourWithOpenEdges( pCrvEL, vCrvIsl, PockParams)) return false ; bIsChunkModified = true ; // la curva è stata modificata } // 4) Controllo i bordi delle isole ottenute // NB. L'isola può essere tutta aperta o tutta chiusa ( se non uniforme, passo a tutta chiusa ) // La definizione di isola con proprietà non uniformi non è definita... for ( int i = 0 ; i < int( vCrvIsl.size()) ; ++ i) { // controllo uniformità int nCurrTmpProp = -1 ; int nPrecTmpProp = -1 ; bool bIsMixed = false ; PtrOwner pCrvIsl( vCrvIsl[i]->Clone()) ; if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid()) return false ; for ( int u = 0 ; u < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ u) { pCrvIsl->GetCurveTempProp( u, nCurrTmpProp, 0) ; bIsMixed = ( u != 0 && nCurrTmpProp != nPrecTmpProp) ; nPrecTmpProp = nCurrTmpProp ; } // se proprità non uniformi -> tutta chiusa if ( bIsMixed) { for ( int u = 0 ; u < pCrvIsl->GetCurveCount() ; ++ u) pCrvIsl->SetTempProp( u, 0) ; nCurrTmpProp = 0 ; // aggiorno il Flag bIsChunkModified = true ; // la curva è stata modificata } // se curva tutta aperta, controllo se l'isola può essere trascurata if ( nCurrTmpProp == 1) { // calcolo il massimo Offset double dMaxOffs ; CalcCurveLimitOffset( *pCrvIsl, dMaxOffs) ; if ( dMaxOffs < 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) continue ; // altrimenti, estendo l'isola aperta ( considero il loop esterno come "isola" per l'estensione) ICRVCOMPOPOVECTOR vCrvExtLoop ; vCrvExtLoop.emplace_back( pCrvEL->Clone()) ; if ( ! AdjustContourWithOpenEdges( pCrvIsl, vCrvExtLoop, PockParams)) return false ; bIsChunkModified = true ; } vCrvToTIsland.emplace_back( pCrvIsl->Clone()) ; // entra nelle curve della regione da svuotare } // 6) Se c'è stata almeno una modifica di lato aperto al chunk (c-esimo), devo ricreare il Chunk // nuovi loop ricavati ( Chunk dopo Chunk creo la superficie finale) if ( bIsChunkModified) { SurfFlatRegionByContours SfrBC ; // per sicurezza analizzo ancora i Loop pCrvEL->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; SfrBC.AddCurve( Release( pCrvEL)) ; // <--- Loop esterno for ( int i = 0 ; i < int( vCrvToTIsland.size()) ; ++ i) { vCrvToTIsland[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; SfrBC.AddCurve( Release( vCrvToTIsland[i])) ; // <--- isole "valide" } // ricavo il nuovo Chunk PtrOwner pNewChunk( SfrBC.GetSurf()) ; if ( IsNull( pNewChunk) || ! pNewChunk->IsValid()) return false ; // aggiungo il Chunk alla superficie finale if ( pSrfFinal->GetChunkCount() == 0) pSrfFinal.Set( pNewChunk) ; else if ( ! pSrfFinal->Add( *pNewChunk)) return false ; } // se il Chunk c-esimo non è mai stato modificato... else { // aggiungo il Chunk alla superficie finale if ( pSrfFinal->GetChunkCount() == 0) pSrfFinal.Set( pSfr->CloneChunk( c)) ; else { PtrOwner pSfrToAdd( pSfr->CloneChunk( c)) ; if ( IsNull( pSfrToAdd) || ! pSrfFinal->Add( *pSfrToAdd)) return false ; } } } // se ho ottenuto delle curve singole, allora le provo a concatenare // ( tenendo solo le curve al di fuori della superficie limite ( con Offset del raggio del tool) // potrei doverle riconcatenare if ( ! ChainCompoCurves( vCrvCompoRes)) return false ; // determino ora il punto di inizio, il tratto per il LeadIn e la Feed delle curve singole if ( ! AdjustOpenCurves( vCrvCompoRes, PockParams)) return false ; // se ho rimosso tutti i Chunk con una singola curva... bSkipPocket = ( nChunk == nChunkOneCurve) ; if ( bSkipPocket) return true ; // restituisco la superficie aggiornata ricavata if ( ! pSrfFinal->IsValid()) return false ; pSfr->Clear() ; pSfr->CopyFrom( pSrfFinal) ; return ( pSrfFinal->IsValid() && pSrfFinal->GetChunkCount() > 0) ; } // *************************************************************************** // ------------- SCELTA DEL PUNTO INIZIALE ----------------------------------- // *************************************************************************** //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- static bool GetParamOnOpenSide( const ICurveComposite* pCompo, const ICRVCOMPOPOVECTOR& vOtherCrv, const Frame3d& frPocket, const PocketParams& PockParams, Point3d& ptMid, Vector3d& vtMidOrt) { // recupero il vettore estrusione Vector3d vtExtr = Z_AX ; pCompo->GetExtrusion( vtExtr) ; // verifico se tutti i lati sono aperti bool bAllOpen = true ; const ICurve* pMyCrv = pCompo->GetFirstCurve() ; while ( pMyCrv != nullptr) { if ( pMyCrv->GetTempProp() != 1) { bAllOpen = false ; break ; } pMyCrv = pCompo->GetNextCurve() ; } // salvo il punto iniziale e la direzione d'uscita del primo lato aperto valido che trovo // NB. Il primo valido non significa il migliore, potrebbe infatti essere il lato aperto più lungo // ma essere all'interno del grezzo... cerco di dare priorità ai lati sul grezzo // flag per la presenza di un primo lato aperto valido per entrare bool bFirstOpenValid = false ; Point3d ptFirstOpenValid = P_INVALID ; Vector3d vtFirstOpenValid = V_INVALID ; // richiedo lunghezza superiore a diametro utensile più doppio offset radiale double dRefLen = ( bAllOpen ? 0 : ( 2 * PockParams.dRad) + 2 * PockParams.dRadialOffset - EPS_SMALL) ; double dMaxLen = dRefLen ; // ciclo sulle singole curve bool bFound = false ; const ICurve* pPrevCrv = pCompo->GetLastCurve() ; double dLenPrev = 0 ; if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == 1) pPrevCrv->GetLength( dLenPrev) ; const ICurve* pCrv = pCompo->GetFirstCurve() ; while ( pCrv != nullptr) { // analizzo la curva successiva const ICurve* pNextCrv = pCompo->GetNextCurve() ; bool bNextOk = ( pNextCrv != nullptr) ; if ( ! bNextOk) pNextCrv = pCompo->GetFirstCurve() ; double dLenNext = 0 ; if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == 1) pNextCrv->GetLength( dLenNext) ; // verifico la curva corrente if ( pCrv->GetTempProp() == 1) { // contributo dalle entità adiacenti (se non tutte aperte) double dLenAgg = 0 ; if ( ! bAllOpen) { if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == 1) { Vector3d vtPrevEnd ; pPrevCrv->GetEndDir( vtPrevEnd) ; Vector3d vtStart ; pCrv->GetStartDir( vtStart) ; dLenAgg += max( 0.4, vtPrevEnd * vtStart) * dLenPrev ; } if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == 1) { Vector3d vtEnd ; pCrv->GetEndDir( vtEnd) ; Vector3d vtNextStart ; pNextCrv->GetStartDir( vtNextStart) ; dLenAgg += max( 0.4, vtEnd * vtNextStart) * dLenNext ; } } // entità corrente double dLen = 0 ; if ( pCrv->GetLength( dLen)) { const double LEN_TOL = 1 ; // se di lunghezza praticamente uguale if ( bFound && dLen + dLenAgg > dRefLen && abs( dLen + dLenAgg - dMaxLen) < LEN_TOL) { Point3d ptTest ; pCrv->GetMidPoint( ptTest) ; if ( ( PockParams.bAboveHead && ptTest.z > ptMid.z + 100 * EPS_SMALL) || ( ! PockParams.bAboveHead && ptTest.z < ptMid.z - 100 * EPS_SMALL) || ( abs( ptTest.z - ptMid.z) < 100 * EPS_SMALL && ptTest.y < ptMid.y - 100 * EPS_SMALL)) { dMaxLen = max( dMaxLen, dLen + dLenAgg) ; ptMid = ptTest ; // vettore ortogonale verso l'esterno (ruotato -90deg attorno a estrusione) pCrv->GetMidDir( vtMidOrt) ; vtMidOrt.Rotate( vtExtr, 0, -1) ; } } // se più lunga ( o non già trovata) else if ( dLen + dLenAgg > dMaxLen || !bFound) { dMaxLen = dLen + dLenAgg ; double dParIn ; // cerco il parametro di tale curva (0 < dParIn < 1) migliore per l'entrata if ( GetParamForPtStartOnEdge( pCrv, pCompo, vOtherCrv, PockParams, dParIn)) { pCrv->GetPointD1D2( dParIn, ICurve::FROM_PLUS, ptMid) ; PtrOwner pCompoClone( pCompo->Clone()) ; if ( IsNull( pCompoClone)) return false ; double dU ; pCompoClone->GetParamAtPoint( ptMid, dU) ; pCompoClone->ChangeStartPoint( dU) ; pCompoClone->GetStartDir( vtMidOrt) ; vtMidOrt.Rotate( vtExtr, 0, -1) ; // salvo l'indice di questo lato aperto se si tratta del primo valido if ( ! bFirstOpenValid) { bFirstOpenValid = true ; ptFirstOpenValid = ptMid ; vtFirstOpenValid = vtMidOrt ; } // controllo se questa quantità è effettivamente fuori dal grezzo // ( privilegio i lati aperti che sono effettivamente sul bordo del grezzo) // punto iniziale nel sistema di riferimento globale Point3d ptStart ; pCompoClone->GetStartPoint( ptStart) ; ptStart.ToGlob( frPocket) ; // vettore d'uscita nel sistema di riferimento globale Vector3d vtMidOut = vtMidOrt ; vtMidOut.ToGlob( frPocket) ; } } dLenPrev = dLen ; } } else dLenPrev = 0 ; // vado alla successiva pPrevCrv = pCrv ; pCrv = ( bNextOk ? pNextCrv : nullptr) ; } // se non ho trovato un lato aperto valido fuori dal grezzo, ma ho trovato un lato aperto // generico accettabile if ( ! bFound && bFirstOpenValid) { ptMid = ptFirstOpenValid ; vtMidOrt = vtFirstOpenValid ; bFound = true ; } return bFound ; } //---------------------------------------------------------------------------- static bool SetSpecialPtStartForOpenEdges( const ICurveComposite* pCrvOrig, const Frame3d& frPocket, const PocketParams& PockParams, ICurveComposite* pCrvCompo, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut) { // controllo dei parametri if ( pCrvOrig == nullptr || ! pCrvOrig->IsValid() || ! frPocket.IsValid() || pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // cerco il lato aperto più lungo double dLenRef = ( 2 * PockParams.dRad) - 50 * EPS_SMALL ; // almeno di questa lunghezza for ( int u = 0 ; u < pCrvOrig->GetCurveCount() ; ++ u) { if ( pCrvOrig->GetCurve( u)->GetTempProp( 0) == 0) continue ; // escludo le chiuse // ricavo la curva u-esima aperta const ICurve* pCrvOpen = pCrvOrig->GetCurve( u) ; if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid()) return false ; // ricavo la lunghezza di tale curva double dLen = 0. ; pCrvOpen->GetLength( dLen) ; if ( dLen > dLenRef) { // se lunghezza accettabile o maggiore della massima trovata Point3d ptSTmp ; Vector3d vtMidOutTmp ; // ricavo il punto medio e il versore tangente associato if ( pCrvOpen->GetPointD1D2( 0.5, ICurve::FROM_MINUS, ptSTmp, &vtMidOutTmp)) { int nFlag ; if ( DistPointCurve( ptSTmp, *pCrvCompo).GetMinDistPoint( EPS_SMALL, ptStart, nFlag)) { // controllo se il punto iniziale sta effettivaente su un lato aperto della curva attuale // di primo Offset double dU_check ; if ( pCrvCompo->GetParamAtPoint( ptStart, dU_check)) { // se il punto trovato è a cavallo tra due curve... if ( abs( dU_check - floor( dU_check)) < EPS_PARAM || abs( dU_check - ceil( dU_check)) < EPS_PARAM) { const ICurve* pCrvA = pCrvCompo->GetCurve( int( floor( dU_check))) ; if ( pCrvA == nullptr) return false ; const ICurve* pCrvB = pCrvCompo->GetCurve( int( ceil( dU_check))) ; if ( pCrvB == nullptr) return false ; if ( pCrvA->GetTempProp( 0) == 0 || pCrvB->GetTempProp( 0) == 1) continue ; // se una delle due è chiusa, salto tale punto } // se invece tocca una sola curva, controllo che sia aperta else { const ICurve* pCrvA = pCrvCompo->GetCurve( int( floor( dU_check))) ; if ( pCrvA == nullptr) return false ; if ( pCrvA->GetTempProp( 0) == 0) continue ; // se chiusa, salto tale punto } } vtMidOutTmp.Normalize() ; vtMidOutTmp.Rotate( Z_AX, - 90) ; vtMidOut = vtMidOutTmp ; bMidOut = true ; } } } } return bMidOut ; } //---------------------------------------------------------------------------- static bool AssignOpenCloseTmpPropToFirstOffsCurve( ICurveComposite* pCrv, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvLoops, INTVECTOR& vIndex, bool& bSomeOpen) { // controllo dei parametri if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) if ( IsNull( vCrvLoops[i]) || ! vCrvLoops[i]->IsValid() || vCrvLoops[i]->GetCurveCount() == 0) return false ; bSomeOpen = false ; // flag per presenza di lati aperti int nInd = -1 ; // indice della prima curva che non è "raccordo" di Offset // scorro tutte le curve presenti in pCrv ( curva da cui devo entrare) for ( int i = 0 ; i < pCrv->GetCurveCount() ; ++ i) { // nProp0 -> #curva il cui Offset ha generato la curva i-esima di pCrv int nProp0 ; pCrv->GetCurveTempProp( i, nProp0, 0) ; // nProp1 -> #loop che contiene la curva espressa in nProp0 int nProp1 ; pCrv->GetCurveTempProp( i, nProp1, 1) ; if ( nProp0 > 0) { // se questa curva non è un "raccordo" di Offset // controllo per maggiore sicurezza che effettivamente nProp1 sia un indice valido per il vettore dei Loops e // che nProp0 non sia maggiore del numero di curve del loop nProp1-esimo del vettore dei Loops della pSrfToWork if ( nProp1 >= 0 && nProp1 < int( vCrvLoops.size()) && nProp0 < vCrvLoops[nProp1]->GetCurveCount()) { // aggiorno il vettore di indici ... if ( find( vIndex.begin(), vIndex.end(), nProp1) == vIndex.end()) vIndex.push_back( nProp1) ; // aggiorno la proprietà della curva da cui devo entrare int nTempProp ; vCrvLoops[nProp1]->GetCurveTempProp( nProp0 - 1, nTempProp, 0) ; pCrv->SetCurveTempProp( i, nTempProp, 0) ; // se la curva è aperta, aggiorno il Flag if ( nTempProp == 1 && ! bSomeOpen) bSomeOpen = true ; // salvo l'indice della prima curva trovata che non è un "raccordo" di Offset if ( nInd == -1) nInd = i ; } else pCrv->SetCurveTempProp( i, 0, 0) ; } else { pCrv->SetCurveTempProp( i, -2, 0) ; pCrv->SetCurveTempProp( i, -2, 1) ; } } // scorro tutte le curve incerte ( che derivano da raccordi ) if ( nInd != -1) pCrv->ChangeStartPoint( nInd) ; // la prima curva non è un "raccordo" di Offset for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++ i) { double dLen = 0. ; if ( pCrv->GetCurve( i)->GetLength( dLen) && dLen < 0.025 * ( 2 * PockParams.dRad)) pCrv->SetCurveTempProp( i, 0, 0) ; else { int nTmpProp0, nTmpProp1 ; if (( pCrv->GetCurveTempProp( i, nTmpProp0, 0) && nTmpProp0 == -2) && ( pCrv->GetCurveTempProp( i, nTmpProp1, 1) && nTmpProp1 == -2)) { // copio la temp prop della curva precedente pCrv->SetCurveTempProp( i, pCrv->GetCurve( i-1)->GetTempProp(), 0) ; // se troppo corta, allora chiusa if ( pCrv->GetCurve( i)->GetLength( dLen) && dLen < 0.01 * ( 2 * PockParams.dRad)) pCrv->SetCurveTempProp( i, 0, 0) ; } } } return true ; } //---------------------------------------------------------------------------- static bool SetPtStartForPath( ICurveComposite* pCrvOffsAct, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork, const Point3d& ptEndPrec, const Frame3d& frPocket, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut, int nOffs, const ICurveComposite* pCrvOrig) { // ============================= INFO ============================================================== // pCrvOffsAct -> Curva di Offset su cui cercare ptStart, vtMidOut, bMidOpen // pSrfToWork -> Superificie originaria da lavorare // ( questa superificie ha i flag di lati aperti/chiusi settati nei loops) // ================================================================================================= /* 1) Come prima cosa ricavo i lati aperti equivalenti dal primo Offset ; Facendo l'Offset di una curva chiusa ho all'interno delle temp prop delle sottcurve due informazioni : // nTmpProp0 -> #curva il cui Offset ha generato la curva i-esima attuale // nTmpProp1 -> #loop che contiene la curva espressa in nTmpProp0 Quindi dal mio contorno con i lati aperti impostati, posso settare i lati aperti "equivalenti" anche sulla curva di primo Offset. NB. Il tool entra dalla curva di primo Offset, non dal bordo della regione da svuotare; quindi l'entrata va calcolata sul primo Offset, non sulla curva di bordo della FlatRegion di Pocketing. 2) Verifico l'esistenza di lati aperti : - Se esistono lati aperti -> cerco il lato più sensato su cui entrare - 2.1) Guardo la curva originale ( senza estensione degli aperti) che delimita la regione di svuotatura e cerco i lati aperti presenti in essa ; se mi posiziono a metà del lato aperto e cerco il punto più vicino sulla curva di primo offset. Se quest'ultima è anch'essa aperta e di lunghezza adeguata, l'entrata è valida - 2.2) ( se 2.1 non accettabile ) Scorro tutte le curve di lato aperto sulla curva di primo Offset e per ogni curva di lunghezza accettabile controllo la precedente e la successiva. Ad ogni curva aperta viene assegnato uno "score", in base alla sua Lenght, alle Lenghts delle curve adiacenti ( se aperte ) e ai loro angoli di adiacenza. Se la curva aperta corrente ha uno "score" sufficiente, viene scelta come curva per l'entrata - 2.3) ( se 2.2 non accettabile ) Considero le curve come tutte chiuse ( vedi sotto ) - Se non esistono dei lati aperti -> entro presso il lato chiuso più lungo - 2.4) */ // controllo dei parametri if ( pCrvOffsAct == nullptr || ! pCrvOffsAct->IsValid() || pCrvOffsAct->GetCurveCount() == 0 || pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1) return false ; // clono le curva su cui devo entrare PtrOwner pCrv( pCrvOffsAct->Clone()) ; if ( IsNull( pCrv)) return false ; // 1) ********************************************************************* bool bSomeOpen = false ; // flag per presenza di lati aperti // creo un vettore di Loops della superificie, ordinati ICRVCOMPOPOVECTOR vCrvLoops ; for ( int l = 0 ; l < pSrfToWork->GetLoopCount( 0) ; ++ l) vCrvLoops.emplace_back( ConvertCurveToComposite( pSrfToWork->GetLoop( 0, l))) ; // creo un vettore di indici. Questi indici si riferiscono alle posizioni di vCrvLoops i quali offset // hanno generato delle curve sulla pCrv ( curva da cui devo entrare ) INTVECTOR vIndex ; if ( ! AssignOpenCloseTmpPropToFirstOffsCurve( pCrv, PockParams, vCrvLoops, vIndex, bSomeOpen)) return false ; // ********************************************************************* // se ho dei lati aperti... double dLenMax = EPS_SMALL ; int nCrvForMax = 0 ; if ( bSomeOpen) { // ... e sono al primo Step, cerco la curva Aperta più lunga ( scelgo la prima che trovo) ... if ( ! ptEndPrec.IsValid()) { for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { int nTmpProp = 0 ; double dLenAct = EPS_SMALL ; if ( pCrv->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 1 && pCrv->GetCurve( u)->GetLength( dLenAct) && dLenAct > dLenMax) { dLenMax = dLenAct ; nCrvForMax = u ; } } // il punto iniziale della curva sarà il punto iniziale della sottocurva trovata pCrv->ChangeStartPoint( nCrvForMax) ; } // ... se invece non sono al primo Step, cerco la prima curva aperta più vicina al punto finale else { int nIndClosesOpenCrv = -1 ; double dMinDist = INFINITO ; for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { if ( pCrv->GetCurve( u)->GetTempProp( 0) == 1) { DistPointCurve DPC( ptEndPrec, *pCrv->GetCurve( u)) ; double dCurrDist = INFINITO ; if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) { dMinDist = dCurrDist ; nIndClosesOpenCrv = u ; } } } // il punto iniziale della curva sarà il punto iniziale della sottocurva trovata pCrv->ChangeStartPoint( nIndClosesOpenCrv) ; } } // creo un vettore con tutti i Loops della pSwfToWork per i quali, mediante l'Offset, non hanno // generato alcuna curva presente nella pCrv ( quella da cui devo entrare) ICRVCOMPOPOVECTOR vOtherCrv ; for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) { bool bOk = true ; for ( int j = 0 ; j < int( vIndex.size()) && bOk ; ++ j) { if ( i == vIndex[j]) bOk = false ; } if ( bOk) vOtherCrv.emplace_back( vCrvLoops[i]->Clone()) ; } // cerchiamo un punto valido per l'entrata ... bMidOut = false ; if ( bSomeOpen) { // se ho dei lati aperti, cerco un parametro ideale per entrare /* ( 2.1 ) */ bool bOK = false ; if ( nOffs == 1 && pCrvOrig != nullptr && pCrvOrig->IsValid()) { // in questo caso le curve a fagiolo dei lati aperti a sinistra e a destra potrebbero // intersecarsi tra loro -> risulta difficile calcolare il versore direzione d'uscita bOK = SetSpecialPtStartForOpenEdges( pCrvOrig, frPocket, PockParams, pCrv, ptStart, vtMidOut, bMidOut) ; } if ( ! bOK) /* ( 2.2 ) */ bMidOut = GetParamOnOpenSide( pCrv, vOtherCrv, frPocket, PockParams, ptStart, vtMidOut) ; } if ( bMidOut) { // se ho trovato e valido, allora imposto il punto inziale trovato const double LEN_OUT = 5 ; double dPar ; int nFlag ; bMidOut = ( DistPointCurve( ptStart + LEN_OUT * vtMidOut, *pCrv).GetParamAtMinDistPoint( 0, dPar, nFlag) && pCrv->ChangeStartPoint( dPar)) ; } /* ( 2.3 | 2.4 ) */ if ( ! bMidOut) // alla peggio, ordino i lati lunghi per lunghezza e cerco un'entrata valida AdjustContourStart( pCrv, PockParams, vOtherCrv, true, ptEndPrec) ; // ora che ho deciso quale sia il punto iniziale, lo imposto effettivamente sulla curva di Offset passata alla funzione pCrv->GetStartPoint( ptStart) ; pCrvOffsAct->Clear() ; pCrvOffsAct->CopyFrom( pCrv) ; return true ; } //---------------------------------------------------------------------------- static bool SetAdvancedPtStartForPath( ICRVCOMPOPOVECTOR& vCrvOffsAct, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork, const Point3d& ptEndPrec, const Frame3d& frPocket, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut, int& nIndex, int nOffs, ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops) { // controllo dei parametri for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i) if ( IsNull( vCrvOffsAct[i]) || ! vCrvOffsAct[i]->IsValid() || vCrvOffsAct[i]->GetCurveCount() == 0) return false ; if ( pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1) return false ; nIndex = 0 ; // scorro le curve di primo Offset // NB. in prima posizione ho il primo Offset del loop esterno, a seguire le isole // NB. Se non riesco ad entrare in nessuna curva, allora tengo valida quella calcolata per il loop esterno bool bMidOut_fist = false ; Vector3d vtMidOut_first ; Point3d ptStart_first ; for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i) { // cambio il suo punto d'inizio dell'Offset attuale if ( SetPtStartForPath( vCrvOffsAct[i], PockParams, pSrfToWork, ptEndPrec, frPocket, ptStart, vtMidOut, bMidOut, nOffs, ( int( vCrvOrigChunkLoops.size()) < i) ? Get( vCrvOrigChunkLoops[i]) : nullptr)) vCrvOffsAct[i]->GetStartPoint( ptStart) ; else return false ; // se ho trovato un'entrata valida da un lato aperto, ho finito if ( bMidOut) { nIndex = i ; // salvo l'indice del vettore return true ; } // se sono nel loop esterno e non ho trovato un'entrata da un lato aperto, salvo i risultati ottenuti else if ( i == 0) { bMidOut_fist = bMidOut ; vtMidOut_first = vtMidOut ; ptStart_first = ptStart ; } } // in questo caso non ho trovato un'entrata da un lato aperto in nessuna curva ( potrebbero essere tutte chiuse) bMidOut = bMidOut_fist ; vtMidOut = vtMidOut_first ; ptStart = ptStart_first ; return true ; } // *************************************************************************** //---------------------------- CASI OTTIMIZZATI ------------------------------ // *************************************************************************** //---------------------------------------------------------------------------- static bool GetOptCrvIndex( const ISurfFlatRegion* pSfrOrig, const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParams, int nReg, ICRVCOMPOPOVECTOR& vCrvOrig) { /* NB. La superficie da lavorare è stata estesa presso i lati aperti; cerco il Chunk di riferimento sulla superficie originale ed estraggo le sue curve ( in prima posizione il loop esterno e a seguire le isole); queste curve verranno utilizzate per la scelta dei casi ottimizzati ( trapezi, circonferenze, ecc ecc..) */ // controllo dei parametri if ( pSfrOrig == nullptr || ! pSfrOrig->IsValid() || pSfrChunk == nullptr || ! pSfrChunk->IsValid()) return false ; // se la superficie originale ha un solo Chunk, allora le curve originali sono già trovate for ( int l = 0 ; l < pSfrOrig->GetLoopCount( 0) ; ++ l) vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( 0, l))) ; // sono inizializzate... if ( pSfrOrig->GetChunkCount() == 1) return true ; // vettore di indici dei chunk candidati INTVECTOR vInds ; // scorro tutti i Chunk della superficie orginaria for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) { // la classificazione può essere fatta semplicemente guardando il Loop esterno PtrOwner pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ; if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid()) return false ; // classifico il Chunk attuale con la curva in esame CRVCVECTOR ccClass ; if ( pSfrChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) { // se il Chunk attuale è stato esteso presso presso i lati aperti mi aspetto che esso non sia più // grande del chunk originale ( se non ci sono lati aperti, allora coincide ) // se il ccClass non contiene parti classificate come esterne allora il Chunk di riferimento è il c-esimo bool bIsThis = true ; for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) { if ( ccClass[k].nClass == CRVC_OUT) bIsThis = false ; } if ( bIsThis) vInds.push_back( c) ; } } // se ho un solo indice allora ho già identificato le curve originali if ( int( vInds.size()) == 1) { vCrvOrig.clear() ; for ( int l = 0 ; l < pSfrOrig->GetLoopCount( vInds[0]) ; ++ l) vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( vInds[0], l))) ; return true ; } // altrimenti... /* NB. Come parametro alla funzione è stato passato "nReg"; Facendo il primo Offset del Chunk attuale questo verrà diviso in n sottoregioni; devo considerare la nReg-esima Situazione: Estendendo presso i lati aperti, due ( o più) chunk si sono uniti formandone uno unico Quando effettuo il primo offset ( del diametro del materiale + offset radiale), questo Chunk si splitterà in n Chunk; Uno ( o più) di questi nuovi chunk potrebbe corrispondere ad un caso ottimizzato. Devo recuperare il Chunk della superficie originaria ( che non essendo estesa presso i lati aperti ha la geomtria originale) */ // effettuo un Offset del Chunk per simulare la creazione degli n Chunks double dOffs = PockParams.dRad + PockParams.dRadialOffset ; PtrOwner pSfrRefChunk( pSfrChunk->Clone()) ; if ( IsNull( pSfrRefChunk) || ! pSfrRefChunk->Offset( - dOffs, ICurve::OFF_FILLET)) return false ; // clono il Chunk nReg-esimo se esiste pSfrRefChunk.Set( pSfrRefChunk->CloneChunk( nReg)) ; if ( IsNull( pSfrRefChunk)) return true ; // bisogna passare al ( pSfrChunk + 1)-esimo se esiste // scorro tutti i Chunk della superficie orginaria e ripeto la classificazione // NB. Questa volta posso fermarmi al primo Chunk classificato come non esterno for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) { // come prima recupero il Loop esterno PtrOwner pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ; if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid()) return false ; // classificazione CRVCVECTOR ccClass ; if ( pSfrRefChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) { bool bIsThis = true ; for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) { if ( ccClass[k].nClass == CRVC_OUT) bIsThis = false ; } if ( bIsThis) { vCrvOrig.clear() ; for ( int l = 0 ; l < pSfrOrig->GetLoopCount( c) ; ++ l) vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( c, l))) ; return true ; // esco } } } return true ; // alla peggio non considero casi ottimizzati e svuoto normalmente... } //---------------------------------------------------------------------------- static bool OptimizedSpiralCirle( const ICurveComposite* pCrvCompo, const double dToll, double& dRad, Point3d& ptC, bool& bIsCirlce) { /* restituisce il centro e il raggio di una circonfereza che approssima pCrvCompo */ // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || dToll < EPS_SMALL) return false ; dRad = 0. ; ptC = P_INVALID ; bIsCirlce = false ; // se aperta, non è un cerchio e non è piana, esco Plane3d plPock ; if ( ! pCrvCompo->IsClosed() || ! pCrvCompo->IsFlat( plPock)) return true ; // creo un sistema di riferimento centrato sulla curva Point3d ptCentroid ; Vector3d vtN ; Frame3d frCurr ; if ( ! pCrvCompo->GetCentroid( ptCentroid) || ! pCrvCompo->GetExtrusion( vtN) || ! frCurr.Set( ptCentroid, vtN) || ! frCurr.IsValid()) return false ; // porto la curva nel sistema di riferimento corrente ( dopo averla clonata) PtrOwner pCrvLoc( pCrvCompo->Clone()) ; if ( IsNull( pCrvLoc) || ! pCrvLoc->IsValid() || ! pCrvLoc->ToLoc( frCurr)) return false ; // approssimo la curva locale con una PolyLine PolyLine pL ; if ( ! pCrvLoc->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, pL) || pL.GetPointNbr() == 0) return false ; // salvo i punti ottenuti ( il punto finale sarà contenuto due volte) PNTVECTOR vPts ; Point3d ptNext ; pL.GetFirstPoint( ptNext) ; vPts.push_back( ptNext) ; while ( pL.GetNextPoint( ptNext)) { vPts.push_back( Media( vPts.back(), ptNext)) ; // inserisco il punto medio vPts.push_back( ptNext) ; // inserisco il punto successivo } // per ogni coppia di punti calcolo la distanza massima e minima dal centro del cerchio locale ( ORIG) double dMaxDist = 0. ; double dMinDist = INFINITO ; for ( int i = 0 ; i < int( floor( 0.5 * int( vPts.size()))) ; i = i + 2) { PtrOwner pSeg( CreateCurveLine()) ; if ( IsNull( pSeg) || ! pSeg->Set( vPts[i], vPts[i+1])) return false ; DistPointCurve DPC( ORIG, *pSeg) ; double dCurrDist = 0. ; if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) dMinDist = dCurrDist ; dCurrDist = Dist( ORIG, vPts[i]) ; if ( dCurrDist > dMaxDist) dMaxDist = dCurrDist ; } // controllo se la distanza tra minima e massima sono simili ( in base alla tolleranza) if ( abs( dMaxDist - dMinDist) > dToll) return true ; // imposto i parametri per la circonferenza dRad = 0.5 * ( dMaxDist + dMinDist) ; ptC = ptCentroid ; bIsCirlce = true ; return true ; } //---------------------------------------------------------------------------- static bool CalcCircleSpiral( const Point3d& ptCen, const Vector3d& vtN, double dOutRad, double dIntRad, const PocketParams& PockParams, ICurveComposite* pMCrv, ICurveComposite* pRCrv) { /* calcola il percorso di a spirale assieme al percorso di ritorno */ // raggio della circonferenza esterna if ( dOutRad < 10 * EPS_SMALL) return false ; // imposto versore estrusione sulle curve composite pMCrv->SetExtrusion( vtN) ; pRCrv->SetExtrusion( vtN) ; // creo e inserisco la circonferenza esterna PtrOwner pArc( CreateCurveArc()) ; if ( IsNull( pArc) || ! pArc->Set( ptCen, vtN, dOutRad)) return false ; // creo la superificie per la Feed PtrOwner pSrfRemoved( CreateSurfFlatRegion()) ; if ( IsNull( pSrfRemoved)) return false ; ICRVCOMPOPOVECTOR vLinksDone ; Vector3d vtDir = pArc->GetStartVersor() ; pMCrv->AddCurve( Release( pArc)) ; // se richiesta percorrenza invertita if ( PockParams.bInvert) pMCrv->Invert() ; // se raggio esterno maggiore dell'interno if ( dOutRad > dIntRad + 10 * EPS_SMALL) { // aggiungo le semicirconferenze della spirale ( devono essere in numero dispari) int nStep = int( ceil( ( dOutRad - dIntRad) / ( 0.5 * PockParams.dSideStep))) ; if ( nStep % 2 == 0) nStep += 1 ; double dStep = ( dOutRad - dIntRad) / nStep ; for ( int i = 1 ; i <= nStep ; ++ i) { if ( ! ( i % 2 == 0)) pMCrv->AddArcTg( ptCen - vtDir * ( dOutRad - i * dStep)) ; else pMCrv->AddArcTg( ptCen + vtDir * ( dOutRad - i * dStep)) ; } // aggiungo la circonferenza interna pMCrv->AddArcTg( ptCen + vtDir * dIntRad) ; pMCrv->AddArcTg( ptCen - vtDir * dIntRad) ; } // verifico il percorso di lavoro if ( pMCrv->GetCurveCount() == 0) return false ; // assegno la Feed AssignFeedSpiralOpt( 0, PockParams, pMCrv) ; // calcolo l'eventuale percorso di ritorno Point3d ptStart ; pMCrv->GetStartPoint( ptStart) ; Point3d ptEnd ; pMCrv->GetEndPoint( ptEnd) ; Vector3d vtStart ; pMCrv->GetStartDir( vtStart) ; if ( ! AreSamePointApprox( ptStart, ptEnd)) { PtrOwner pArc2( CreateCurveArc()) ; if ( IsNull( pArc2) || ! pArc2->Set2PVN( ptStart, ptEnd, - vtStart, vtN)) return false ; pRCrv->AddCurve( Release( pArc2)) ; // inverto e eventualmente sistemo archi pRCrv->Invert() ; } return true ; } //---------------------------------------------------- static bool CalcTrapezoidSpiralLocalFrame( ICurveComposite* pCrvTrap, const Vector3d& vtDir, Frame3d& frLoc) { // cerco i lati paralleli a vtDir int nBaseId = -1 ; for ( int i = 0 ; i < pCrvTrap->GetCurveCount() ; i ++) { Vector3d vtEdge ; pCrvTrap->GetCurve( i)->GetStartDir( vtEdge) ; if ( AreSameOrOppositeVectorApprox( vtEdge, vtDir)) { nBaseId = i ; break ; } } if ( nBaseId != 0 && nBaseId != 1) return false ; // imposto come lato iniziale per la curva uno dei lati paralleli a vtDir pCrvTrap->ChangeStartPoint( nBaseId) ; Point3d ptOrig ; pCrvTrap->GetStartPoint( ptOrig) ; Vector3d vtX ; pCrvTrap->GetStartDir( vtX) ; return frLoc.Set( ptOrig, Z_AX, vtX) ; } //---------------------------------------------------------------------------- static bool GetBoxCrvOptTrap( const int nCrv, const ICurveComposite* pCrvCompo, const double dDiam, int& nType, ICurveComposite* pCrvBox) { nType = - 1 ; // prendo la curva const ICurve* pCrvCurr = pCrvCompo->GetCurve( nCrv) ; if ( pCrvCurr == nullptr) return false ; // controllo se lineare, altrimenti passo alla successiva if ( pCrvCurr->GetType() != CRV_LINE) return true ; // prendo la direzione del tratto lineare Vector3d vtDir ; pCrvCurr->GetStartDir( vtDir) ; // prendo il punto iniziale Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ; // creo il riferimento basato su questo tratto Frame3d frTrap ; frTrap.Set( ptStart, Z_AX, vtDir) ; if ( ! frTrap.IsValid()) return false ; // porto la curva Compo ( clonandola) nel frame e calcolo il suo BBox BBox3d BBox ; PtrOwner pCrvCompo_c( pCrvCompo->Clone()) ; if ( IsNull( pCrvCompo_c) || ! pCrvCompo_c->IsValid() || ! pCrvCompo_c->ToLoc( frTrap) || ! pCrvCompo_c->GetLocalBBox( BBox)) return false ; // controllo dimY e dimX per il box bool bDimYOk = BBox.GetDimY() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ; bool bDimXOk = BBox.GetDimX() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ; // controllo dimensioni ammissibili if ( ! bDimXOk && ! bDimYOk) return true ; // se nessuna else if ( ! bDimXOk) nType = 1 ; // se dimY else if ( ! bDimYOk) nType = 0 ; // se dimX else nType = 2 ; // se entrambe // creo il rettangolo del Box pCrvBox->Clear() ; pCrvBox->AddPoint( BBox.GetMin()) ; pCrvBox->AddLine( BBox.GetMin() + BBox.GetDimX() * X_AX) ; pCrvBox->AddLine( BBox.GetMax()) ; pCrvBox->AddLine( BBox.GetMax() - BBox.GetDimX() * X_AX) ; pCrvBox->Close() ; // determino il punto inziale della curva if ( nType == 0) pCrvBox->ChangeStartPoint( 1.) ; pCrvBox->ToGlob( frTrap) ; return true ; } //---------------------------------------------------------------------------- static bool PreparareTrapezoidTwoBases( const ICurveComposite* pCrvCompo, const double dDiam, const int nType, int& nBase, int& nSecondBase, bool& bOk, ICurveComposite* pCrvTrap) { // controllo parametri if ( pCrvCompo == nullptr) return false ; bOk = false ; // le parti tra le due basi devono essere tutte omogenee ( o tutte aperte o tutte chiuse) // pCrvTest0 sarà la parte destra, pCrvTest1 la parte sinistra Point3d ptSB0, ptSB1 ,ptEB0, ptEB1 ; PtrOwner pCrvTest0( pCrvCompo->Clone()) ; PtrOwner pCrvTest1( pCrvCompo->Clone()) ; pCrvCompo->GetCurve( nBase)->GetStartPoint( ptSB0) ; pCrvCompo->GetCurve( nBase)->GetEndPoint( ptEB0) ; pCrvCompo->GetCurve( nSecondBase)->GetStartPoint( ptSB1) ; pCrvCompo->GetCurve( nSecondBase)->GetEndPoint( ptEB1) ; pCrvTest0->ChangeStartPoint( nBase) ; pCrvTest1->ChangeStartPoint( nSecondBase) ; double dUTrim0, dUTrim1 ; pCrvTest0->GetParamAtPoint( ptSB1, dUTrim0) ; pCrvTest1->GetParamAtPoint( ptSB0, dUTrim1) ; pCrvTest0->TrimStartEndAtParam( 1, dUTrim0) ; pCrvTest1->TrimStartEndAtParam( 1, dUTrim1) ; // controllo che la parte destra si uniforme per le TmpProp for ( int u = 0 ; u < pCrvTest0->GetCurveCount() - 1 ; ++ u) { int nPropAct, nPropSucc ; if ( pCrvTest0->GetCurveTempProp( u, nPropAct, 0) && pCrvTest0->GetCurveTempProp( u + 1, nPropSucc, 0) && nPropAct != nPropSucc) return true ; // se TmpProp diverse => non è un caso ottimizzato } // controllo che la parte sinistra sia uniforme per le TmpProp for ( int u = 0 ; u < pCrvTest1->GetCurveCount() - 1 ; ++ u) { int nPropAct, nPropSucc ; if ( pCrvTest1->GetCurveTempProp( u, nPropAct, 0) && pCrvTest1->GetCurveTempProp( u + 1, nPropSucc, 0) && nPropAct != nPropSucc) return true ; // se TmpProp diverse => non è un caso ottimizzato } // se lato destro aperto ( estendo il punto finale della base principale e il punto iniziale // della base secondaria fino al lato destro del box) bool bCopyRight = false ; if ( pCrvTest0->GetCurve( 0)->GetTempProp( 0) == 1) { if ( nType == 0) { pCrvTrap->GetCurve( 0)->GetStartPoint( ptEB0) ; pCrvTrap->GetCurve( 1)->GetStartPoint( ptSB1) ; } else { pCrvTrap->GetCurve( 0)->GetEndPoint( ptEB0) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptSB1) ; } } // se lato destro chiuso else bCopyRight = true ; // se lato sinistro aperto ( estendo il punto finale della base secondaria e il punto iniziale // della base primaria dino al lato sinistro del box) bool bCopyLeft = false ; if ( pCrvTest1->GetCurve( 0)->GetTempProp( 0) == 1) { if ( nType == 0) { pCrvTrap->GetCurve( 2)->GetEndPoint( ptEB1) ; pCrvTrap->GetCurve( 0)->GetStartPoint( ptSB0) ; } else { pCrvTrap->GetCurve( 1)->GetEndPoint( ptEB1) ; pCrvTrap->GetCurve( 2)->GetEndPoint( ptSB0) ; } } // se lato sinistro chiuso else bCopyLeft = true ; // creo la curva da restituire pCrvTrap->Clear() ; pCrvTrap->AddPoint( ptSB0) ; pCrvTrap->AddLine( ptEB0) ; nBase = 0 ; if ( bCopyRight) pCrvTrap->AddCurve( Release( pCrvTest0)) ; else { pCrvTrap->AddLine( ptSB1) ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta } nSecondBase = pCrvTrap->GetCurveCount() ; pCrvTrap->AddLine( ptEB1) ; if ( bCopyLeft) pCrvTrap->AddCurve( Release( pCrvTest1)) ; else { pCrvTrap->Close() ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta } // verifico dimensione x della svuotatura nel caso tutto chiuso if ( bCopyLeft && bCopyRight) { double dLen0 = 0, dLen2 = 0 ; pCrvTrap->GetCurve( nBase)->GetLength( dLen0) ; pCrvTrap->GetCurve( nSecondBase)->GetLength( dLen2) ; if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL) { pCrvTrap->Clear() ; return true ; } } bOk = true ; return true ; } //---------------------------------------------------------------------------- static bool GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTrap, Frame3d& frTrap, const PocketParams& PockParams, double& dPocketSize, int& nBase, int& nSecondBase) { // controllo parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // diametro reale da tenere in considerazione double dDiam = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ; pCrvTrap->Clear() ; // resterà vuota se il caso non è ottimizzato nBase = -1 ; nSecondBase = -1 ; // se la curva è già un trapezio, non sempre devo adattarla... Point3d pt ; Vector3d vtDir, vtB2, vtOtherDir ; if ( pCrvCompo->IsATrapezoid( 100 * EPS_SMALL, pt, vtDir, vtOtherDir, vtB2)) { pCrvTrap->AddCurve( pCrvCompo->Clone()) ; // se parallelogramma scelgo come base i lati lunghi Vector3d vtL2( - vtDir + vtOtherDir + vtB2) ; if ( AreSameOrOppositeVectorApprox( vtOtherDir, vtL2)) { if ( vtOtherDir.Len() > vtDir.Len()) swap( vtDir, vtOtherDir) ; } vtDir.Normalize() ; Vector3d vtOrtho = OrthoCompo( vtOtherDir, vtDir) ; dPocketSize = vtOrtho.Len() ; // eventuale approssimazione della curva con polyline per ottenere la stessa curva calcolata in ICurveComposite::IsATrapezoid if ( pCrvCompo->GetCurveCount() > 4) { PolyLine PL ; if ( ! pCrvCompo->ApproxWithLines( 100 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL)) return false ; pCrvTrap->FromPolyLine( PL) ; } if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap)) return false ; bool bBaseOpen = false ; bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == 1 || pCrvTrap->GetCurve( 2)->GetTempProp( 0) == 1) ; // controllo dimensioni della svuotatura if ( ! ( bBaseOpen && dPocketSize < dDiam + EPS_SMALL) && abs( dPocketSize - dDiam) > EPS_SMALL) { pCrvTrap->Clear() ; return true ; } // recupero flag aperto/chiuso dei lati if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == 0 && pCrvTrap->GetCurve( 3)->GetTempProp( 0) == 0) { double dLen0, dLen2 ; pCrvTrap->GetCurve( 0)->GetLength( dLen0) ; pCrvTrap->GetCurve( 2)->GetLength( dLen2) ; if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL) pCrvTrap->Clear() ; } // imposto le basi nBase = 0 ; nSecondBase = 2 ; return true ; } // controllo il numero di lati chiusi e salvo i loro indici int nClosedSide = 0 ; INTVECTOR vIndClosedSides ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) { int nTmpProp ; if ( pCrvCompo->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 0) { ++ nClosedSide ; vIndClosedSides.push_back( u) ; } } // se tutti lati aperti switch ( nClosedSide) { // TUTTI I LATI SONO APERTI case 0 : { // ricavo il box minimo della curva aperta ( passo dalla polyLine) PolyLine PL ; Point3d ptCen ; double dWidth ; if ( ! pCrvCompo->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) || ! PL.GetMinAreaRectangleXY( ptCen, vtDir, dWidth, dPocketSize)) return false ; // controllo dimY ( dHeight), se troppo estesa, non è un caso ottimizzato if ( dPocketSize > dDiam + TOL_TRAPEZOID) return true ; // inverto il frame attuale frTrap.Set( ptCen, Z_AX, vtDir) ; if ( ! frTrap.IsValid()) return false ; // creo il rettangolo del Box pCrvTrap->AddPoint( Point3d( - 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ; pCrvTrap->AddLine( Point3d( 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ; pCrvTrap->AddLine( Point3d( 0.5 * dWidth, 0.5 * dPocketSize, 0)) ; pCrvTrap->AddLine( Point3d( - 0.5 * dWidth, 0.5 * dPocketSize, 0)) ; pCrvTrap->Close() ; pCrvTrap->ToGlob( frTrap) ; Point3d ptNewOrig ; pCrvTrap->GetStartPoint( ptNewOrig) ; frTrap.Set( ptNewOrig, Z_AX, vtDir) ; // imposto tutte le 4 curve come aperte for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, 1, 0) ; // imposto le basi nBase = 0 ; nSecondBase = 2 ; } break ; // 1 LATO CHIUSO ( LINEARE) case 1 : { int nType = -1 ; if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType != -1) { // imposto tutte le curve come aperte tranne la prima ( estendo il solo lato chiuso come base del Box) for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u == 0 ? 0 : 1, 0) ; // memorizzo la dimensione di svuotatura pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // imposto le basi nBase = 0 ; nSecondBase = 2 ; } } break ; // 2 LATI CHIUSI ( LINEARI, CONSECUTIVI O PARALLELI) case 2 : { // controllo se entrambi sono lineari if ( pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE && pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE) { // se lineari, ricavo i punti estremanti e le direzioni Point3d ptS0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartPoint( ptS0) ; Point3d ptS1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartPoint( ptS1) ; Point3d ptE0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetEndPoint( ptE0) ; Point3d ptE1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetEndPoint( ptE1) ; Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ; Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ; // CASO PARALLELI ( la base è indifferente) if ( AreOppositeVectorEpsilon( vtDir0, vtDir1, 5 * EPS_SMALL)) { int nType = -1 ; // prendo come base il primo chiuso if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType != -1) { // memorizzo la dimensione di svuotatura pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // controllo che la distanza tra i due chiusi sia effettivamente circa il raggio double dDist = 0. ; DistPointCurve DPL( ptS0, *pCrvCompo->GetCurve( vIndClosedSides[1]), false) ; if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) { // imposto tutte le curve di indice dispari aperte for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u % 2 == 0 ? 0 : 1, 0) ; // imposto le basi nBase = 0 ; nSecondBase = 2 ; } else pCrvTrap->Clear() ; } } // CASO CONSECUTIVI bool bOk_0_1 = AreSamePointApprox( ptS1, ptE0) ; // chiuso1 . chiuso0 bool bOk_1_0 = AreSamePointApprox( ptS0, ptE1) ; // chiuso0 . chiuso1 if ( bOk_0_1 || bOk_1_0) { // provo con il primo lato int nType = -1 ; // prendo come base il primo chiuso if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType == -1) { // provo con l'altro if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType != -1) { // spaw tra gli indici e flags swap( vIndClosedSides[0], vIndClosedSides[1]) ; swap( bOk_0_1, bOk_1_0) ; } } Point3d ptH1, ptH2 ; if ( nType == 1 || nType == 2) { double dLen1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetLength( dLen1) ; pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // i lati inclinati chiusi definiscono il Box if ( abs( dLen1 * ( vtDir0 ^ vtDir1).Len() - dPocketSize) < TOL_TRAPEZOID) { // creo la curva a trapezio if ( bOk_0_1) { pCrvTrap->GetCurve( 2)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 3)->GetEndPoint( ptH2) ; pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->Close() ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ; } else { pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ; pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->AddLine( ptS1) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; pCrvTrap->SetCurveTempProp( 0, 0, 0) ; for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u) pCrvTrap->SetCurveTempProp( u, 1, 0) ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ; } // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } else pCrvTrap->Clear() ; } else if ( nType == 0) { if ( bOk_0_1) { pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ; pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->Close() ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ; pCrvTrap->ChangeStartPoint( 1.) ; // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } else { pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->AddLine( ptS1) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; pCrvTrap->SetCurveTempProp( 0, 0, 0) ; for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u) pCrvTrap->SetCurveTempProp( u, 1, 0) ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ; pCrvTrap->ChangeStartPoint( 1.) ; // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } } } } } break ; // 3 LATI CHIUSI ( LINEARI e CONSECUTIVI) case 3 : { // prendo i 3 lati chiusi const ICurve* pCrv0 = pCrvCompo->GetCurve( vIndClosedSides[0]) ; const ICurve* pCrv1 = pCrvCompo->GetCurve( vIndClosedSides[1]) ; const ICurve* pCrv2 = pCrvCompo->GetCurve( vIndClosedSides[2]) ; if ( pCrv0 == nullptr || pCrv1 == nullptr || pCrv2 == nullptr) return false ; // controllo che siano lineari if ( pCrv0->GetType() == CRV_LINE && pCrv1->GetType() == CRV_LINE && pCrv2->GetType() == CRV_LINE) { // prendo i punti iniziali e finali Point3d ptStart0 ; pCrv0->GetStartPoint( ptStart0) ; Point3d ptEnd0 ; pCrv0->GetEndPoint( ptEnd0) ; Point3d ptStart1 ; pCrv1->GetStartPoint( ptStart1) ; Point3d ptEnd1 ; pCrv1->GetEndPoint( ptEnd1) ; Point3d ptStart2 ; pCrv2->GetStartPoint( ptStart2) ; Point3d ptEnd2 ; pCrv2->GetEndPoint( ptEnd2) ; // controllo che siano consecutivi e cambio l'ordine se necessario bool bOk = true ; if ( AreSamePointApprox( ptEnd0, ptStart1) && AreSamePointApprox( ptEnd1, ptStart2)) ; else if ( AreSamePointApprox( ptEnd1, ptStart2) && AreSamePointApprox( ptEnd2, ptStart0)) { swap( vIndClosedSides[0], vIndClosedSides[1]) ; swap( vIndClosedSides[1], vIndClosedSides[2]) ; } else if ( AreSamePointApprox( ptEnd2, ptStart0) && AreSamePointApprox( ptEnd0, ptStart1)) { swap( vIndClosedSides[0], vIndClosedSides[2]) ; swap( vIndClosedSides[2], vIndClosedSides[1]) ; } else // se non consecutivi bOk = false ; // se i tre lati sono ( mediante lo swap) consecutivi... if ( bOk) { // controllo se la dimensione Y del lato 1 è valida ( il resto è gestito di seguito) int nType = -1 ; if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType == 1) { // bisgna controllare la lunghezza dei chiusi double dLen0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetLength( dLen0) ; double dLen2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetLength( dLen2) ; pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ; Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ; Vector3d vtDir2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetStartDir( vtDir2) ; // i lati inclinati chiusi definiscono il Box if ( abs( dLen2 * ( vtDir1 ^ vtDir2).Len() - dPocketSize) < TOL_TRAPEZOID && abs( dLen0 * ( vtDir1 ^ vtDir0).Len() - dPocketSize) < TOL_TRAPEZOID) { // creo la curva a trapezio pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[2])->Clone()) ; pCrvTrap->Close() ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u < 3 ? 0 : 1 , 1) ; pCrvTrap->ChangeStartPoint( 1.) ; // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } } } } } } // se non ho trovato delle basi, allora cerco in maniera generale un trapezoide ( se esiste) if ( nBase == -1) { bool bOK = false ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() && !bOK ; ++ u) { // cerco un lato chiuso ( esiste per forza) if ( pCrvCompo->GetCurve( u)->GetTempProp( 0) == 0) { // controllo se il lato corrente può essere una base int nType = -1 ; if ( ! GetBoxCrvOptTrap( u, pCrvCompo, dDiam, nType, pCrvTrap)) return false ; // se è una base valida if ( nType != -1) { // ricavo la dimensione di svuotatura pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // ricavo la sua direzione iniziale Vector3d vtBaseDir ; pCrvCompo->GetCurve( u)->GetStartDir( vtBaseDir) ; // ricavo il suo punto iniziale Point3d ptBaseStart ; pCrvCompo->GetCurve( u)->GetStartPoint( ptBaseStart) ; // ricavo il suo punto finale Point3d ptBaseEnd ; pCrvCompo->GetCurve( u)->GetEndPoint( ptBaseEnd) ; // cerco il lato chiuso lineare parallelo ad esso e distante circa dPocketSize for ( int uu = 0 ; uu < pCrvCompo->GetCurveCount() && !bOK ; ++ uu) { if ( uu == u || pCrvCompo->GetCurve( uu)->GetType() != CRV_LINE || pCrvCompo->GetCurve( uu)->GetTempProp() != 0) continue ; Vector3d vtSecondBaseDir ; pCrvCompo->GetCurve( uu)->GetStartDir( vtSecondBaseDir) ; if ( ! AreOppositeVectorEpsilon( vtBaseDir, vtSecondBaseDir, 5 * EPS_SMALL)) continue ; Point3d ptSecondBaseStart ; pCrvCompo->GetCurve( uu)->GetStartPoint( ptSecondBaseStart) ; double dDist = 0. ; DistPointCurve DPL( ptBaseStart, *pCrvCompo->GetCurve( uu), false) ; if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) { nBase = u ; nSecondBase = uu ; if ( ! PreparareTrapezoidTwoBases( pCrvCompo, dDiam, nType, nBase, nSecondBase, bOK, pCrvTrap)) return false ; } } } } } } // la curva a trapezio deve evere almeno 4 lati if ( pCrvTrap->GetCurveCount() < 4 || nBase == -1 || nSecondBase == -1) { pCrvTrap->Clear() ; return true ; } // ricostruisco il frame del trapezio Point3d ptS ; pCrvTrap->GetStartPoint( ptS) ; Vector3d vtS ; pCrvTrap->GetStartDir( vtS) ; if ( ! frTrap.Set( ptS, Z_AX, vtS) || ! frTrap.IsValid()) return false ; // se parametro MaxOptSize non compatibile => non è ottimizzato if ( PockParams.dMaxOptSize > EPS_SMALL && dPocketSize > PockParams.dMaxOptSize) pCrvTrap->Clear() ; return true ; } //---------------------------------------------------- static bool SpecialAdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const bool bEvenClosed, const ICurveComposite* pCrvPocket, const PocketParams& PockParams) { // parametri double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ; double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ; double dRad = 0.5 * dDiam + dOffsR ; // calcolo gli offset dei lati obliqui PtrOwner pLineS( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 0 : 3)->Clone())) ; pLineS->SimpleOffset( - dRad) ; pLineS->Invert() ; PtrOwner pLineE( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 2 : 1)->Clone())) ; pLineE->SimpleOffset( - dRad) ; pLineE->Invert() ; Point3d ptS, ptE ; pLineS->GetEndPoint( ptS) ; pLineE->GetStartPoint( ptE) ; Vector3d vtS, vtE ; pLineS->GetStartDir( vtS) ; pLineE->GetStartDir( vtE) ; PtrOwner pLineLink( CreateCurveLine()) ; pLineLink->Set( ptS, ptE) ; pMCrv->Clear() ; if ( ! pMCrv->AddCurve( Release( pLineS))) return false ; // creo raccordi bool bUseBiArcs = false ; Vector3d vtDir, vtDir2 ; pLineLink->GetStartDir( vtDir) ; pCrvPocket->GetCurve( bEvenClosed ? 1 : 0)->GetStartDir( vtDir2) ; if ( Dist( ptS, ptE) > 500 * EPS_SMALL && AreSameOrOppositeVectorApprox( vtDir, vtDir2)) { Point3d ptCrv1, ptCrv2 ; pLineLink->GetPointD1D2( 0.3, ICurve::FROM_MINUS, ptCrv1) ; pLineLink->GetPointD1D2( 0.7, ICurve::FROM_MINUS, ptCrv2) ; // primo raccordo double dAng ; vtS.GetAngleXY( X_AX, dAng) ; PtrOwner pBiArc1( GetBiArc( ptS, - dAng, ptCrv1, bEvenClosed ? 90 : 0, 0.5)) ; // secondo raccordo vtE.Invert() ; vtE.GetAngleXY( X_AX, dAng) ; PtrOwner pBiArc2( GetBiArc( ptE, - dAng, ptCrv2, bEvenClosed ? - 90 : 180, 0.5)) ; pBiArc2->Invert() ; if ( ! IsNull( pBiArc1) && ! IsNull( pBiArc2)) { bUseBiArcs = true ; pMCrv->AddCurve( Release( pBiArc1)) ; pMCrv->AddLine( ptCrv2) ; pMCrv->AddCurve( Release( pBiArc2)) ; } } // se non è stato possibile creare raccordo, unisco linearmente if ( ! bUseBiArcs) pMCrv->AddCurve( Release( pLineLink)) ; if ( ! pMCrv->AddCurve( Release( pLineE))) return false ; // setto temp prop per ricordare curve aggiuntive per pulire angoli pMCrv->SetCurveTempProp( 0, 1) ; pMCrv->SetCurveTempProp( pMCrv->GetCurveCount() - 1, 1) ; return true ; } //------------------------------------------------------ static bool CalcTrapezoidSpiralXCoord( const ICurveComposite* pCrvPocket, int nBase, int nSecondBase, bool bStart, double dYCoord, double& dXCoord, double dPocketSize, const PocketParams& PockParams) { // parametri double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ; double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ; double dRad = 0.5 * dDiam + dOffsR ; // recupero la curva di interesse int nCrvId = ( bStart ? nSecondBase : nBase) + 1 ; int nProp = - 1 ; if ( ! pCrvPocket->GetCurveTempProp( nCrvId, nProp)) return false ; // se open if ( nProp == 1) { Point3d pt1, pt2 ; pCrvPocket->GetCurve( nCrvId)->GetStartPoint( pt1) ; pCrvPocket->GetCurve( nCrvId)->GetEndPoint( pt2) ; if ( bStart) dXCoord = min( pt1.x, pt2.x) ; else dXCoord = max( pt1.x, pt2.x) ; } // se closed else { // creo la curva destra/sinistra int nLast = bStart ? pCrvPocket->GetCurveCount() : nSecondBase ; PtrOwner pCrvSide( ConvertCurveToComposite( pCrvPocket->CopyParamRange( nCrvId, nLast))) ; if ( IsNull( pCrvSide)) return false ; // Offsetto la curva OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( pCrvSide, - dRad, ICurve::OFF_FILLET)) return false ; if ( OffsCrv.GetCurveCount() == 0) { // controllo se avevo una circonferenza if ( pCrvSide->GetCurveCount() == 1 && pCrvSide->GetFirstCurve()->GetType() == CRV_ARC) { Point3d ptS ; pCrvSide->GetStartPoint( ptS) ; dXCoord = ptS.x ; } else { Point3d ptS ; pCrvSide->GetStartPoint( ptS) ; Point3d ptE ; pCrvSide->GetEndPoint( ptE) ; dXCoord = bStart ? max( ptS.x, ptE.x) + dRad : min( ptS.x, ptE.x) - dRad ; } } else if ( OffsCrv.GetCurveCount() == 1) { // controllo se la curva interseca la linea di svuotatura a YCoord PtrOwner pCrvOffs( OffsCrv.GetLongerCurve()) ; if ( IsNull( pCrvOffs)) return false ; PtrOwner pLineMid( CreateCurveLine()) ; if ( IsNull( pLineMid)) return false ; pLineMid->Set( Point3d( - 3000, dYCoord, 0), Point3d( 3000, dYCoord, 0)) ; IntersCurveCurve intCC( *pLineMid, *pCrvOffs) ; IntCrvCrvInfo ccClass ; if ( intCC.GetIntersCount() != 0) { // se ho almeno una intersezione if ( intCC.GetIntCrvCrvInfo( 0, ccClass)) dXCoord = ccClass.IciA[0].ptI.x ; else return false ; } else { // se non ho intersezioni... // prendo il box della curva BBox3d Box3d ; pCrvSide->GetLocalBBox( Box3d) ; // creo la linea limitie verticale PtrOwner pCrvVertLine( CreateCurveLine()) ; if ( IsNull( pCrvVertLine)) return false ; if ( bStart) pCrvVertLine->SetPDL( Box3d.GetMax() + 5 * TOL_TRAPEZOID * Y_AX, - 90 , 2 * dPocketSize) ; else pCrvVertLine->SetPDL( Box3d.GetMin() - 5 * TOL_TRAPEZOID * Y_AX, 90, 2 * dPocketSize) ; // intersechiamo IntersCurveCurve intCC2( *pCrvVertLine, *pCrvSide) ; if ( intCC2.GetOverlaps()) { if ( bStart) dXCoord = Box3d.GetMax().x + dRad ; else dXCoord = Box3d.GetMin().x - dRad ; } else { dXCoord = bStart ? -INFINITO : INFINITO ; for ( int i = 0 ; i < intCC2.GetIntersCount() ; ++ i) { IntCrvCrvInfo ccClass2 ; if ( intCC2.GetIntCrvCrvInfo( i, ccClass2)) { if ( bStart) dXCoord = max( dXCoord, Box3d.GetMax().x + sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ; else dXCoord = min( dXCoord, Box3d.GetMin().x - sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ; } } } } } else return false ; } return true ; } //---------------------------------------------------- static bool AdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const ICurveComposite* pCrvPocket, const PocketParams& PockParams, bool bStart) { // parametri double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ; double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ; double dRad = 0.5 * dDiam + dOffsR ; PtrOwner pCompo( CreateCurveComposite()) ; if ( ! bStart) pMCrv->Invert() ; Point3d ptTmp ; pMCrv->GetStartPoint( ptTmp) ; double dYCoord = ptTmp.y ; // quota verticale del percorso di svuotatura pCrvPocket->GetCurve( 2)->GetStartPoint( ptTmp) ; double dPocketSize = ptTmp.y ; int nCrvId = ( bStart ? 3 : 1) ; PtrOwner pLine( GetCurveLine( pCrvPocket->GetCurve( nCrvId)->Clone())) ; pLine->SimpleOffset( - dRad) ; Point3d ptP1, ptP2 ; pLine->GetStartPoint( ptP1) ; pLine->GetEndPoint( ptP2) ; if ( ! bStart) swap( ptP1, ptP2) ; int nProp2 ; // lato opposto a quello di riferimento aperto if ( pCrvPocket->GetCurveTempProp( 2, nProp2) && nProp2 != 0) { // caso 1 : pLine ha un estremo sopra e uno sotto il percorso di svuotatura pMCrv if ( ptP2.y < dYCoord && dYCoord < ptP1.y) { // creo tratto da ptP2 a ptP1 pCompo->AddPoint( ptP2) ; pCompo->AddLine( ptP1) ; // trovo il punto di pMCrv da cui ripartire per non lasciare aree residue pLine->SimpleOffset( - 0.5 * dDiam) ; pLine->ExtendStartByLen( EPS_SMALL) ; pLine->ExtendEndByLen( EPS_SMALL) ; IntersCurveCurve intCC( *pLine, *pCrvPocket) ; if ( intCC.GetIntersCount() == 0) return false ; IntCrvCrvInfo aInfo ; intCC.GetIntCrvCrvInfo( 0, aInfo) ; double dDeltaX = sqrt( max( 0., dDiam * dDiam / 4 - dYCoord * dYCoord)) ; if ( ! bStart) dDeltaX *= -1 ; Point3d ptCrv( aInfo.IciA[0].ptI.x + dDeltaX, dYCoord) ; double dPar = 0 ; if ( ! pMCrv->GetParamAtPoint( ptCrv, dPar)) { dPar = 0.5 ; pMCrv->GetPointD1D2( dPar, ICurve::FROM_MINUS, ptCrv) ; } if ( ! pMCrv->TrimStartAtParam( dPar)) pMCrv->Clear() ; if ( ptP1.y > dPocketSize) { // se ptP1 è esterno alla svuotatura scambio i punti in modo da usare ptP2 per il biarco ( così da non farlo fuoriuscire troppo) swap( ptP1, ptP2) ; pCompo->Invert() ; } // creo biarco fra ptP1 e ptCrv Vector3d vtDir ; pCompo->GetStartDir( vtDir) ; double dAng ; vtDir.GetAngleXY( X_AX, dAng) ; PtrOwner pBiArc( GetBiArc( ptP1, - dAng, ptCrv, bStart ? 0 : 180, 0.8)) ; bool bUseBiArc = false ; if ( ! IsNull( pBiArc)) { // verifico che con il biarco non si oltrepassi il lato obliquo chiuso della svuotatura IntersCurveCurve intCC2( *pBiArc, *pCompo) ; if ( intCC2.GetIntersCount() == 1) bUseBiArc = true ; } if ( bUseBiArc) pCompo->AddCurve( Release( pBiArc)) ; else { double dParLine = ( dYCoord - ptP2.y) / ( ptP1.y - ptP2.y) ; Point3d ptOnLine = Media( ptP2, ptP1, dParLine) ; pCompo->AddLine( ptOnLine) ; pCompo->AddLine( ptCrv) ; } } // caso 2 : pLine è completamente sopra/sotto la linea di svuotatura else { // se è sopra modifiche per ricondurmi al caso in cui è sotto if ( ptP2.y > dYCoord) { pLine->Invert() ; swap( ptP1, ptP2) ; } // trovo l'intersezione fra il prolungamento della linea e pMCrv if ( bStart) pLine->ExtendStartByLen( 1000) ; else pLine->ExtendEndByLen( 1000) ; IntersCurveCurve intCC ( *pLine, *pMCrv) ; if ( intCC.GetIntersCount() == 0) return false ; IntCrvCrvInfo aInfo ; intCC.GetIntCrvCrvInfo( 0, aInfo) ; Point3d ptInt ; ptInt = aInfo.IciA[0].ptI ; double dPar = 0 ; pMCrv->GetParamAtPoint( ptInt, dPar) ; if ( ! pMCrv->TrimStartAtParam( dPar)) pMCrv->Clear() ; pCompo->AddPoint( ptP2) ; pCompo->AddLine( ptInt) ; } } // se il lato opposto a quello di riferimento � chiuso bisogna distinguere i casi else { bool bStartPnt = false ; if ( ptP2.y < dYCoord) { bStartPnt = pCompo->AddPoint( ptP2) ; } else { Vector3d vtDir ; pLine->GetStartDir( vtDir) ; if ( bStart) vtDir.Invert() ; Point3d ptP2N = ptP2 - dDiam / 2 * abs( vtDir.x / vtDir.y) * vtDir ; if ( ptP2N.y < dYCoord) bStartPnt = pCompo->AddPoint( ptP2N) ; } if ( bStartPnt) { Point3d ptCrv ; pMCrv->GetStartPoint( ptCrv) ; pCompo->AddLine( ptCrv) ; } } // setto temp prop per ricordare che è curva aggiuntiva per pulire angoli for ( int i = 0 ; i < pCompo->GetCurveCount() ; i++) pCompo->SetCurveTempProp( i, 1) ; pMCrv->AddCurve( Release( pCompo), false) ; if ( ! bStart) pMCrv->Invert() ; // ripristino la direzione originaria return true ; } //---------------------------------------------------- static bool CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double dPocketSize, int nBase, int nSecondBase, const PocketParams& PockParams, ICurveComposite* pMCrv, ICurveComposite* pRCrv, bool& bOptimizedTrap) { // parametri double dDiam = 2 * PockParams.dRad ; double dOffsR = PockParams.dRadialOffset ; bOptimizedTrap = false ; Vector3d vtExtr ; pCrvPocket->GetExtrusion( vtExtr) ; // recupero le temp prop INTVECTOR vnProp( 4, 0) ; if ( pCrvPocket->GetCurveCount() == 4) { for ( int i = 0 ; i < 4 ; i ++) pCrvPocket->GetCurveTempProp( i, vnProp[i]) ; } else { vnProp[1] = pCrvPocket->GetCurve( 1)->GetTempProp() ; vnProp[3] = pCrvPocket->GetCurve( nSecondBase + 1)->GetTempProp() ; } // passo in un sistema di riferimento locale avente asse X allineato con uno dei due lati paralleli // (possibilmente aperto) e centro nel punto iniziale del lato pCrvPocket->ToLoc( frTrap) ; // calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso) double dLen0, dLen1, dLen2, dLen3, dMaxLarg = 0. ; pCrvPocket->GetCurve( nBase)->GetLength( dLen0) ; pCrvPocket->GetCurve( nSecondBase)->GetLength( dLen2) ; bool bRealTrap = pCrvPocket->GetCurveCount() == 4 ; for ( int i = 0 ; i < 4 && bRealTrap ; ++ i) bRealTrap = pCrvPocket->GetCurve( i)->GetType() == CRV_LINE ; if ( bRealTrap) { pCrvPocket->GetCurve( nBase + 1)->GetLength( dLen1) ; pCrvPocket->GetCurve( nSecondBase + 1)->GetLength( dLen3) ; // calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso) Point3d ptOrig ; pCrvPocket->GetCurve( 1)->GetStartPoint( ptOrig) ; Vector3d vtX ; pCrvPocket->GetCurve( 1)->GetStartDir( vtX) ; Frame3d frDim ; frDim.Set( ptOrig, Z_AX, vtX) ; frDim.Invert() ; BBox3d b3Dim ; pCrvPocket->GetBBox( frDim, b3Dim, BBF_EXACT) ; dMaxLarg = ( vnProp[0] != 0 ? b3Dim.GetDimY() : dPocketSize) ; } // calcolo percorso di svuotatura // se lati obliqui sono entrambi chiusi e dimensione svuotatura è maggiore di diametro fresa e minore del doppio gestione speciale if (( bRealTrap && dMaxLarg > PockParams.dRad * 2 + 10 * EPS_SMALL) && ((( vnProp[0] != 0 && vnProp[2] != 0) && ( vnProp[3] == 0 && vnProp[1] == 0) && ( max( dLen0, dLen2) < 2 * dDiam + EPS_SMALL)) || (( vnProp[1] != 0 && vnProp[3] != 0) && ( vnProp[0] == 0 && vnProp[2] == 0) && ( max( dLen1, dLen3) < 2 * dDiam + EPS_SMALL)))) { if ( ! SpecialAdjustTrapezoidSpiralForAngles( pMCrv, vnProp[0] == 0, pCrvPocket, PockParams)) { pMCrv->Clear() ; return false ; } } else { // trovo la quota Y per centro del Tool double dYCoord ; if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) // se base principale chiusa dYCoord = 0.5 * dDiam + dOffsR ; else if ( pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 0) // se base principale aperta e secondaria chiusa dYCoord = dPocketSize - 0.5 * dDiam - dOffsR ; else // se entrambi i lati paralleli sono aperti mi posiziono a metà della svuotatura dYCoord = 0.5 * dPocketSize ; double dXCoordStart, dXCoordEnd ; if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, true, dYCoord, dXCoordStart, dPocketSize, PockParams)) return false ; if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, false, dYCoord, dXCoordEnd, dPocketSize, PockParams)) return false ; if ( dXCoordStart > dXCoordEnd + 500 * EPS_SMALL) return false ; Point3d ptStart( dXCoordStart, dYCoord) ; Point3d ptEnd( dXCoordEnd, dYCoord) ; if ( bRealTrap && AreSamePointEpsilon( ptStart, ptEnd, 500 * EPS_SMALL) && ( vnProp[0] != 0 || vnProp[2] != 0)) { Vector3d vtDir1, vtDir3 ; pCrvPocket->GetCurve( 1)->GetStartDir( vtDir1) ; pCrvPocket->GetCurve( 3)->GetStartDir( vtDir3) ; // gestisco il caso speciale di un parallelogramma in cui anche l'altra dimensione della svuotatura è pari al diametro utensile if ( AreOppositeVectorApprox( vtDir1, vtDir3)) { PtrOwner pLine1( GetCurveLine( pCrvPocket->GetCurve( 1)->Clone())) ; PtrOwner pLine3( GetCurveLine( pCrvPocket->GetCurve( 3)->Clone())) ; if ( IsNull( pLine1) || IsNull( pLine3)) return false ; if ( ! pLine1->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset) || ! pLine3->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset)) return false ; Point3d ptS, ptE ; if ( vtDir3 * X_AX > EPS_SMALL) { pLine1->GetStartPoint( ptS) ; pLine3->GetStartPoint( ptE) ; } else { pLine1->GetEndPoint( ptE) ; pLine3->GetEndPoint( ptS) ; } if ( vnProp[0] != 0) { pMCrv->AddPoint( ptS) ; if ( vnProp[2] != 0) pMCrv->AddLine( ptE) ; else pMCrv->AddLine( ptStart) ; } else { pMCrv->AddPoint( ptE) ; if ( vnProp[0] != 0) pMCrv->AddLine( ptS) ; else pMCrv->AddLine( ptStart) ; pMCrv->Invert() ; } pMCrv->SetCurveTempProp( 0, 1) ; } } else { if ( ! pMCrv->AddPoint( ptStart)) return true ; if ( ! pMCrv->AddLine( ptEnd)) return true ; // aggiustamenti al percorso per rimuovere materiale residuo negli angoli if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 1 || pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 1) { Frame3d frOpen ; bool bSwitch = false ; // Base principale chiusa e base Secondaria aperta if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) { pCrvPocket->ChangeStartPoint( nSecondBase) ; // oriento il Frame ( ho sempre l'origine nell'estremo superiore del lato aperto) Vector3d vtDir ; pCrvPocket->GetStartDir( vtDir) ; Point3d ptORIG ; if ( vtDir.y > 0) pCrvPocket->GetCurve( nBase)->GetEndPoint( ptORIG) ; else pCrvPocket->GetCurve( nBase)->GetStartPoint( ptORIG) ; frOpen.Set( ptORIG, Z_AX, -X_AX) ; if ( ! frOpen.IsValid()) return false ; pCrvPocket->ToLoc( frOpen) ; pMCrv->ToLoc( frOpen) ; pMCrv->Invert() ; bSwitch = true ; } if ( pCrvPocket->GetCurve( 3)->GetTempProp( 0) == 0 && ! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, true)) { pMCrv->Clear() ; return false ; } if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == 0 && ! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, false)) { pMCrv->Clear() ; return false ; } if ( bSwitch) { pCrvPocket->ToGlob( frOpen) ; pMCrv->ToGlob( frOpen) ; pMCrv->Invert() ; } } } } if ( pMCrv->GetCurveCount() == 0) return true ; pMCrv->ToGlob( frTrap) ; if ( ! PockParams.bInvert) { pMCrv->Invert() ; // inverto le proprietà in modo che nProp3 sia sempre legata al punto iniziale e nProp1 a quello finale swap( vnProp[1], vnProp[3]) ; } // segno i lati aperti come temp prop della curva int nOpenEdges = vnProp[0] + vnProp[1] * 2 + vnProp[2] * 4 + vnProp[3] * 8 ; pMCrv->SetTempProp( nOpenEdges, 0) ; pMCrv->SetExtrusion( vtExtr) ; bOptimizedTrap = true ; return true ; } //---------------------------------------------------------------------------- static bool ModifyCurveToSmoothed( ICurveComposite* pCrv, const PocketParams& PockParams, double dRightLen, double dLeftLen, bool bAsParam) { // se non richiesto non faccio nulla if ( ! PockParams.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, const 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 CalcBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, const Vector3d& vtEnd, double dParMeet, const ICRVCOMPOPOVECTOR& vOffIslands, const PocketParams& PockParams, ICurveComposite* pCrvLink) { // se senza smusso, ritorno tratto lineare if ( ! PockParams.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, PockParams.bInvert ? 360 : - 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 && int( 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 ; } } PtrOwner pCrvCompo_noSmooth( pCompo->Clone()) ; if ( ! ModifyCurveToSmoothed( pCompo, PockParams, 0.05, 0.05, true) || ! pCompo->IsValid()) pCompo.Set( pCrvCompo_noSmooth) ; pCrvLink->AddCurve( Release( pCompo)) ; return true ; } //---------------------------------------------------------------------------- static bool CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, const ICRVCOMPOPOVECTOR& vOffsCL, const ICRVCOMPOPOVECTOR& vFirstOffset, const PocketParams& PockParams, 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. : PockParams.dRad) ; double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : PockParams.dRad) ; dLenE = 0 ; // calcolo i possibili BiArchi tra le due curve int nIter = 0 ; if ( ! PockParams.bSmooth) nMaxIter = 1 ; // taglio la curva al massimo nMaxIter-volte e mi fermo al taglio più opportuno while ( nIter < nMaxIter) { // calcolo il BiArco PtrOwner ptBiArc( CreateBasicCurveComposite()) ; if ( ! CalcBoundedSmoothedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, PockParams, 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( Release( ptCrvFinal)) ; // ultimo arco valido trovato return true ; } //---------------------------------------------------------- static bool GetUnclearedRegion( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vCrvs, ICURVEPOVECTOR& vLinks, const ICurveComposite* pCrv_orig, const PocketParams& PockParams, ISurfFlatRegion* pSrfToCut) { // controllo dei parametri if ( vFirstOffs.empty() || int( vCrvs.size()) < int( vFirstOffs.size())) return false ; pSrfToCut->Clear() ; // 1) creo la regione esterna PtrOwner pSrfExtern( CreateSurfFlatRegion()) ; if ( IsNull( pSrfExtern)) return false ; for ( int i = 0 ; i < int( vFirstOffs.size()) ; ++ i) { if ( i == 0) pSrfExtern->AddExtLoop( vFirstOffs[i]->Clone()) ; else { PtrOwner pCrvIntLoop( vFirstOffs[i]->Clone()) ; if ( IsNull( pCrvIntLoop) || ! pCrvIntLoop->Invert() || ! pSrfExtern->AddIntLoop( Release( pCrvIntLoop))) return false ; } } // ---- le seguenti regioni sono distinte per calcolar emeglio la Feed ---- // 2) Creo la regione svuotata dal Tool negli Offset, nei Links e sia dagli Offsets che dai Links PtrOwner pSrfTool_Offs( CreateSurfFlatRegion()) ; PtrOwner pSrfTool_Links( CreateSurfFlatRegion()) ; PtrOwner pSrfTool( CreateSurfFlatRegion()) ; if ( IsNull( pSrfTool_Offs) || IsNull( pSrfTool_Links) || IsNull( pSrfTool)) return false ; // creo un vettore che conterrà solamente i Link percorsi fino ad Ora ICRVCOMPOPOVECTOR vLinks_done ; for ( int i = ( PockParams.nType == POCKET_SPIRALIN ? 0 : int( vCrvs.size()) - 1) ; ( PockParams.nType == POCKET_SPIRALIN ? i < int( vCrvs.size()) : i >= 0 ) ; ( PockParams.nType == POCKET_SPIRALIN ? ++ i : -- i)) { // ================= LINK ================================ // ( Il primo link è nullo, inatti non vi è nessun raccordo per raggiungere il primo Offset) if ( i <= int( vLinks.size()) && ! IsNull( vLinks[i]) && vLinks[i]->IsValid()) { // Feed PtrOwner pCompoLink_i( CreateCurveComposite()) ; if ( IsNull( pCompoLink_i)) return false ; pCompoLink_i->AddCurve( vLinks[i]->Clone()) ; if ( ! AssignFeedSpiral( pCompoLink_i, pSrfTool_Offs, true, vLinks_done, pCrv_orig, PockParams, 2 * PockParams.dRad / 3)) return false ; // per Link ... PtrOwner pCrvLink_i( vLinks[i]->Clone()) ; if ( IsNull( pCrvLink_i)) return false ; PtrOwner pSrfToolRegLinki( GetSurfFlatRegionFromFatCurve( Release( pCrvLink_i), PockParams.dRad + 5 * EPS_SMALL, false, false)) ; if ( ! IsNull( pSrfToolRegLinki)) { if ( ! pSrfTool_Links->IsValid() || pSrfTool_Links->GetChunkCount() == 0) pSrfTool_Links.Set( pSrfToolRegLinki) ; else pSrfTool_Links->Add( *pSrfToolRegLinki) ; } // risetto il Link come curva composita ... vLinks[i].Set( pCompoLink_i->Clone()) ; // inserisco il Link nel vettore dei Link percorsi vLinks_done.emplace_back( Release( pCompoLink_i)) ; } // ================== OFFSET ============================= // Feed if ( ! AssignFeedSpiral( vCrvs[i], pSrfTool_Offs, false, vLinks_done, pCrv_orig, PockParams, 2 * PockParams.dRad / 3)) return false ; // aggiorno la superificie svuotata PtrOwner pCrvOffs_i( vCrvs[i]->Clone()) ; if ( IsNull( pCrvOffs_i)) return false ; PtrOwner pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( Release( pCrvOffs_i) , PockParams.dRad + 5 * EPS_SMALL, false, false)) ; if ( ! IsNull( pSrfToolRegOffi)) { if ( ! pSrfTool_Offs->IsValid() || pSrfTool_Offs->GetChunkCount() == 0) pSrfTool_Offs.Set( pSrfToolRegOffi) ; else pSrfTool_Offs->Add( *pSrfToolRegOffi) ; } } // 3) Calcolo la superificie svuotata pSrfTool.Set( pSrfTool_Offs) ; pSrfTool->Add( *pSrfTool_Links) ; // 4) Creo la regione contenente tutte le parti non svuotate pSrfToCut->CopyFrom( pSrfExtern) ; if ( ! pSrfToCut->Subtract( *pSrfTool)) return false ; return true ; } //---------------------------------------------------------------------------- static bool RemoveFirstLoopFromSfr( ISurfFlatRegion* pSrfOrig) { // 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 ; pSrfHelp->AddExtLoop( pSrfOrig->GetLoop( c, 0)) ; if ( c == 1) pSrfRes.Set( pSrfHelp) ; else pSrfRes->Add( *pSrfHelp) ; } // restituisco pSrfOrig->Clear() ; pSrfOrig->CopyFrom( pSrfRes) ; return true ; } //---------------------------------------------------------------------------- static bool RemoveExtraPartByMedialAxis( const ISurfFlatRegion* pChunkToCut, ICRVCOMPOPOVECTOR& vOffsFirstCurve, const PocketParams& PockParams, 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, PockParams.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, PockParams, 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), PockParams.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 ( ! CalcBoundedSmoothedLink( ptS, vtS, ptE, vtE, 0.5, vOffsFirstCurve, PockParams, 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) && ! CalcBoundedSmoothedLink( ptEOriginal, vtEOriginal, ptSOriginal, vtSOriginal, 0.5, vOffsFirstCurve, PockParams, 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 ( ! CalcBoundedSmoothedLink( ptClosestOnPath, vtTanCpt, vPtCentroid[i], vtTanCpt, 0, vOffsFirstCurve, PockParams, 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, const PocketParams& PockParams, 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 ( ! CalcBoundedSmoothedLink( ptS, vtTanS, ptE, vtTanE, 0, VFirstOff, PockParams, 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() ; // assegno la Feed AssignMinFeed( pPath1, PockParams) ; // 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, PockParams, 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 ( ! CalcBoundedSmoothedLink( ptS, vtTanS, ptE, vtTanS, 0.5, VFirstOff, PockParams, pPath1) || ! CalcBoundedSmoothedLink( ptE, vtTanS, ptS, vtTanS, 0.5, VFirstOff, PockParams, pPath2)) return false ; // assegno la Feed a queste due nuove curve AssignMinFeed( pPath1, PockParams) ; AssignMinFeed( pPath2, PockParams) ; // 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, const PocketParams& PockParams, 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 * PockParams.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 * PockParams.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, PockParams, 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, const PocketParams& PockParams, 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 ( ! CalcBoundedSmoothedLink( ptS, vtTanS, ptE, vtTanE, 0.5, VFirstOff, PockParams, pPath1)) if ( ! CalcBoundedLink( ptS, ptE, VFirstOff, pPath1)) return false ; if ( ! CalcBoundedSmoothedLink( ptH, vtH, ptS, vtTanS, 0.5, VFirstOff, PockParams, 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, PockParams, pPath2, VoffsCl)) return false ; // imposto la Feed AssignMinFeed( pCrvToAdd, PockParams) ; // 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 ( ! CalcBoundedSmoothedLink( ptHS, vtHS, ptHE, vtHE, 0.5, VFirstOff, PockParams, pCrvBiArcHelper)) if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper)) return false ; // assegno la Feed AssignMinFeed( pCrvBiArcHelper, PockParams) ; 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 ( ! CalcBoundedSmoothedLink( ptHS, vtHS, ptHE, vtHE, 0.5, VFirstOff, PockParams, pCrvBiArcHelper)) if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper) ) return false ; // assegno la Feed AssignMinFeed( pCrvBiArcHelper, PockParams) ; 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, const PocketParams& PockParams) { // controllo parametri 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, PockParams, 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, PockParams, 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, PockParams, 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, PockParams, 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, PockParams, 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, PockParams, ptCrvNewOffs)) bSucc = false ; PtrOwner pCrvPathI( pCrvPath->Clone()) ; pCrvPathI->Invert() ; PtrOwner ptCrvNewOffsI( CreateCurveComposite()) ; if ( IsNull( ptCrvNewOffsI)) return false ; if ( ! GetNewCurvetWithPath( pCrvH1I, pCrvH2I, pCrvPathI, vOffsFirstCurve, vOffsClosedCurves, PockParams, 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 CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, const PocketParams& PockParams, const ICRVCOMPOPOVECTOR& vOffsClosedCurves, const ICRVCOMPOPOVECTOR& vOffsFirstCurve) { // controllo dei parametri if ( vOffs.empty() || vOffsFirstCurve.empty()) return false ; vLinks.clear() ; vLinks.resize( int( vOffs.size())) ; // scorro tutte le curva di Offset for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) { // ricavo il punto inziale della curva corrente Point3d ptS ; if ( ! vOffs[i]->GetStartPoint( ptS)) return false ; int nNextInd = -1 ; // indice della curva successiva double dMinDist = INFINITO ; // distanza tra questa curva e la successiva Point3d ptStartNext ; // punto iniziale della curva successiva // tra le curva successive cerco la curva interna e più vicina ad essa for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) { IntersCurveCurve IntCC( *vOffs[i], *vOffs[j]) ; CRVCVECTOR ccClass ; // se interna if ( IntCC.GetCurveClassification( 1, EPS_SMALL, ccClass) && int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) { // calcolo la distanza minima tra essa int nFlag ; Point3d ptClosest ; if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) { double dCurrDist = SqDist( ptS, ptClosest) ; if ( dCurrDist < dMinDist) { dMinDist = dCurrDist ; nNextInd = j ; ptStartNext = ptClosest ; } } } } // se non ho trovato nessuna curva interna... cerco semplicemente la curva più vicina if ( nNextInd == -1) { for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) { int nFlag ; Point3d ptClosest ; if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) { double dCurrDist = SqDist( ptS, ptClosest) ; if ( dCurrDist < dMinDist) { dMinDist = dCurrDist ; nNextInd = j ; ptStartNext = ptClosest ; } } } } // se non ho trovato nessuna curva, errore if ( nNextInd == -1) return false ; // scambio la curva i+1 esima con la j-esima ( se non sono già in ordine) if ( nNextInd != i + 1) swap( vOffs[nNextInd], vOffs[i+1]) ; // cambio il suo punto iniziale nel punto più vicino tovato double dUS ; if ( ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUS, EPS_SMALL)) return false ; vOffs[i+1]->ChangeStartPoint( dUS) ; // accorcio la curva per raccordarmi in tangenza con la successiva if ( int( vOffs.size()) > 1) { // clono le curve i ed i+1 esime ( nel caso non riuscissi ad accorciarle o raccordarle ) PtrOwner pCrv_i( vOffs[i]->Clone()) ; PtrOwner pCrv_ii( vOffs[i+1]->Clone()) ; if ( IsNull( pCrv_i) || IsNull( pCrv_ii) || ! pCrv_i->IsValid() || ! pCrv_ii->IsValid()) return false ; // creo la curva che le collegherà PtrOwner pCrvLink( CreateCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; // NB. Le curve di Offset da tagliare non devono essere quelle di primo Offset if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsClosedCurves, vOffsFirstCurve, PockParams, pCrvLink, i == 0 ? 0 : 0.01, 0.01, 2) || ! pCrvLink->IsValid()) { // se non sono riuscito, cerco una strada più semplice // ripristino le curve pCrvLink->Clear() ; vOffs[i].Set( pCrv_i) ; // chiuso vOffs[i+1].Set( pCrv_ii) ; // chiuso // recupero i vettori tangente iniziali ( le curve sono chiuse ) Vector3d vtS, vtE ; if ( ! vOffs[i]->GetStartDir( vtS) || ! vOffs[i+1]->GetStartDir( vtE)) return false ; // creo il bi-arco tra esse if ( CalcBoundedSmoothedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink)) vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento else return false ; continue ; // passo alla curva i+1 esima successiva } // per sicurezza aggiorno i nuovi punti e i nuovi parametri double dUNewS, dUNewE ; if ( ! pCrvLink->GetStartPoint( ptS) || ! pCrvLink->GetEndPoint( ptStartNext) || ! vOffs[i]->GetParamAtPoint( ptS, dUNewS) || ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUNewE)) return false ; // imposto il punto iniziale della curva successiva ( i+1 esima) vOffs[i+1]->ChangeStartPoint( dUNewE) ; PtrOwner pCrvNewOffs( vOffs[i]->Clone()) ; if ( dUNewS > EPS_SMALL) { pCrvNewOffs.Set( ConvertCurveToComposite( vOffs[i]->CopyParamRange( 0, dUNewS))) ; // sostituisco la curva i esima con quella tagliata vOffs[i].Set( pCrvNewOffs) ; } // aggiorno il collegamento vLinks[i+1].Set( pCrvLink) ; } } return true ; } //---------------------------------------------------------------------------- static bool CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs, const int nOffsCount, const int nIter, const Vector3d& vtN, const PocketParams& PockParams, bool& bInsert) { // controllo dei parametri if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0) return false ; bInsert = true ; // di base inserisco // controllo se richiesta ottimizzazione sul numero di Offsets if ( ! PockParams.bOptOffsets) return true ; if ( nIter != 0) { // controllo se sono almeno al secondo offset... // per ogni curva controllo se il controOffset + 5 * EPS_SMALL genera una regione ... OffsetCurve OffsCrv ; // considero anche i casi in cui ho bSmallRad if ( OffsCrv.Make( pCrvOffs, - PockParams.dRad + dOffs - 5 * EPS_SMALL , ICurve::OFF_FILLET)) { // se questa curva sparisce -> non serve inserirla come offset, non svuota parti aggiuntive if ( OffsCrv.GetCurveCount() == 0) { bInsert = false ; return true ; } } else return true ; // controllo se richista ottimizzazione per Offsets mediante centroidi e medial Axis if ( ! PockParams.bOptOffsetsAdv || OffsCrv.GetCurveCount() > 1) return true ; PtrOwner pCrvOffLonger( OffsCrv.GetLongerCurve()) ; if ( IsNull( pCrvOffLonger) || ! pCrvOffLonger->IsValid()) return true ; // Immagine del tool con centro nel centroide della zona da svuotare Point3d ptC ; pCrvOffLonger->GetCentroid( ptC) ; PtrOwner pCrvTool( CreateCurveArc()) ; if( IsNull( pCrvTool)) return false ; pCrvTool->SetXY( ptC, PockParams.dRad - 5 * EPS_SMALL) ; CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCrvOffLonger, *pCrvTool) ; intCC.GetCurveClassification( 1, EPS_SMALL, ccClass) ; if ( int(ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) { // centroide bInsert = false ; return true ; } // recupero la PolyLine per altezza e lunghezza del Box2D ( rettangolo) PolyLine pl ; if ( ! pCrvOffLonger->ApproxWithLines( 100 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, pl)) return true ; Vector3d vtX ; double dLen = 0 ; double dHeight = 0 ; if ( ! pl.GetMinAreaRectangleXY( ptC, vtX, dLen, dHeight)) return true ; // frame locale, recupero DimX e DimY Frame3d frLoc ; frLoc.Set( ptC, vtN, vtX) ; if ( ! frLoc.IsValid()) return true ; BBox3d bBox ; frLoc.Invert() ; pCrvOffLonger->GetBBox( frLoc, bBox) ; double dDimX = bBox.GetDimX() ; double dDimY = bBox.GetDimY() ; // se dimensioni accettabili, inserisco if ( dDimX < 2 * PockParams.dRad - 100 * EPS_SMALL || dDimY < 2 * 2 * PockParams.dRad - 100 * EPS_SMALL) { bInsert = false ; return true ; } } return true ; } //---------------------------------------------------------------------------- static bool CalcSpiral( const ISurfFlatRegion* pSrfPock, const PocketParams& PockParams, int& nReg, Point3d& ptStart, ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops, ICurveComposite* pMCrv, ICurveComposite* pRCrv, bool& bOptimizedTrap, bool& bMidOut, Vector3d& vtMidOut) { // inizializzo i risultati pMCrv->Clear() ; pRCrv->Clear() ; // Offset corrente ( il primo è definito dal raggio utensile) double dOffs = PockParams.dRad + PockParams.dRadialOffset ; // 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) ; // CONTROLLO PER CASI OTTIMIZZATI bool bHasIsland = ( pSrfPock->GetLoopCount( 0) > 1) ; // se non ho isole e curva valida allora controllo casi ottimizzati if ( ! PockParams.bAvoidOpt && ! bHasIsland && ( ! vCrvOrigChunkLoops.empty()) && ! IsNull( vCrvOrigChunkLoops[0])) { // --------------------------- caso spirale --------------------------------------- PtrOwner pCrvBorder( ConvertCurveToComposite( pSrfPock->GetLoop( 0, 0))) ; if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid()) return false ; pCrvBorder->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; pCrvBorder->SetExtrusion( pSrfPock->GetNormVersor()) ; // setto l'estrusione della curva Vector3d vtN = pSrfPock->GetNormVersor() ; // vettore normale per caso circonferenza Point3d ptCen ; double dRad ; bool bIsCircle = false ; if ( ! OptimizedSpiralCirle( pCrvBorder, 50 * EPS_SMALL, dRad, ptCen, bIsCircle)) return false ; if ( bIsCircle && dRad - dOffs > 10 * EPS_SMALL) { double dIntRad = 0 ; if ( nReg == 0) { bool bOk = CalcCircleSpiral( ptCen, vtN, dRad - dOffs, dIntRad, PockParams, pMCrv, pRCrv) ; if ( bOk) { pMCrv->GetStartPoint( ptStart) ; nReg = 1 ; return true ; } else return false ; } else return true ; } // -------------------------- caso trapezoide --------------------------------------- PtrOwner pCrvTrap( CreateCurveComposite()) ; if ( IsNull( pCrvTrap)) return false ; Frame3d frTrap ; double dPocketSize ; int nBase, nSecondBase ; vCrvOrigChunkLoops[0]->ToLoc( frPocket) ; if ( ! GetTrapezoidFromShape( vCrvOrigChunkLoops[0], pCrvTrap, frTrap, PockParams, dPocketSize, nBase, nSecondBase)) return false ; vCrvOrigChunkLoops[0]->ToGlob( frPocket) ; if ( pCrvTrap->IsValid()) { pCrvTrap->SetExtrusion( vtExtr) ; if ( nReg == 0) { CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParams, pMCrv, pRCrv, bOptimizedTrap) ; if ( bOptimizedTrap) { pMCrv->ToGlob( frPocket) ; pRCrv->ToGlob( frPocket) ; nReg = 1 ; return true ; } } else return true ; } } // porto la superficie nel frame corrente 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( pSrfToWork->Clone()) ; // 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 ; } // se primo Offset if ( nIter == 0) { int my_nReg = nReg ; nReg = pSfrOffsVR->GetChunkCount() ; // aggiorno il nuovo valore delle regioni totali di primo Offset // gli Offset progressivi appartengono al Chunk nReg-esimo pSrfAct.Set( pSfrOffsVR->CloneChunk( my_nReg)) ; if ( IsNull( pSrfAct)) // se supero i chunk ottenuti return true ; pSfrOffsVR.Set( pSrfAct->Clone()) ; } // numero di Chunk attuali ( alla prima iterazione è l'nReg-esimo) 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 ; /* Il materiale deve sempre trovarsi alla destra ( se bInvert -> alla sinistra) del tool soltanto sul primo bordo; Nel caso di isole aperte o nelle passate di Offset intermedio l'orientamente non ha importanza. */ if ( nIter != 0 && j > 0) // inverto l'orientamento delle curve interne ( offset delle isole trovate) pCrvCompoBorder->Invert() ; // controllo quali regioni di Offset possono essere sostituite bool bInsert = true ; if ( ! CheckIfOffsetIsNecessary( pCrvCompoBorder, dOffs - dOffsPrec, int( vOffs.size()), nIter, vtExtr, PockParams, bInsert)) return false ; if ( bInsert) vOffs.emplace_back( Release( pCrvCompoBorder)) ; if ( nIter == 0) { // salvo il bordo per i link ( non invertiti, devo sapere IN/OUT) 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 < PockParams.dRad + EPS_ZERO : dOffs - dOffsPrec < PockParams.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 if ( nIter != 0) { dOffsPrec = dOffs ; dOffs += PockParams.dSideStep ; } else { dOffsPrec = 0. ; dOffs = PockParams.dSideStep ; } } // se devo usare un Offset più piccolo... else if ( ! bSmallRad) dOffs = dOffsPrec + ( nIter == 0 ? PockParams.dRad + PockParams.dRadialOffset : PockParams.dRad) ; // 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 Point3d ptRef = PockParams.ptEnd ; ptRef.ToLoc( frPocket) ; Point3d ptNewStart ; if ( ! IsNull( vCrvOrigChunkLoops[0])) vCrvOrigChunkLoops[0]->ToLoc( frPocket) ; // Se ho come lavorazione uno SpiralIn posso poter entrare dalle isole aperte... int nIndexSwap = 0 ; // indice del vettore di curve per identificare la curva su cui entrare if ( PockParams.nType == POCKET_SPIRALIN) { if ( SetAdvancedPtStartForPath( vOffsFirstCurve, PockParams, pSrfToWork, ptRef, frPocket, ptStart, vtMidOut, bMidOut, nIndexSwap, int( vOffs.size()), vCrvOrigChunkLoops)) vOffsFirstCurve[nIndexSwap]->GetStartPoint( ptNewStart) ; else return false ; } else { if ( SetPtStartForPath( vOffs[0], PockParams, pSrfToWork, ptRef, frPocket, ptStart, vtMidOut, bMidOut, int( vOffs.size()), vCrvOrigChunkLoops[0])) vOffs[0]->GetStartPoint( ptNewStart) ; else return false ; } if ( ! IsNull( vCrvOrigChunkLoops[0])) vCrvOrigChunkLoops[0]->ToGlob( frPocket) ; // se richiesta inversione for ( int i = 0 ; i < int( vOffs.size()) && PockParams.bInvert ; ++ i) vOffs[i]->Invert() ; // smusso le curve di offset ( ad eccezione della prima; le isole sono automaticamente smussate) ICRVCOMPOPOVECTOR vOffsClosedCurves( vOffs.size()) ; // vettore con tutte le curve di Offset Chiuse double dSmoothPar = PockParams.dRad / 8 ; for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) { if ( i != 0) ModifyCurveToSmoothed( vOffs[i], PockParams, dSmoothPar, dSmoothPar, false) ; vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; vOffsClosedCurves[i].Set( vOffs[i]->Clone()) ; } // setto il punto iniziale della svuotatura double dNewUS ; if ( nIndexSwap != 0) swap( vOffs[0], vOffs[nIndexSwap]) ; // se entro da un'isola aperta vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ; vOffs[0]->ChangeStartPoint( dNewUS) ; vOffs[0]->SetTempParam( 0., 0) ; // prima iterazione // riordino le curve cambiando e creo i collegamenti ICURVEPOVECTOR vLinks( vOffs.size()) ; if ( ! CreateSpiralPocketingPath( vOffs, vLinks, PockParams, vOffsClosedCurves, vOffsFirstCurve)) return false ; // controllo eventuali parti non svuotate... if ( ! IsNull( vCrvOrigChunkLoops[0])) vCrvOrigChunkLoops[0]->ToLoc( frPocket) ; PtrOwner pSrfToCut( CreateSurfFlatRegion()) ; if ( GetUnclearedRegion( vOffsFirstCurve, vOffs, vLinks, vCrvOrigChunkLoops[0], PockParams, pSrfToCut)) { // Modifico i percorsi if ( ! RemoveExtraParts( pSrfToCut, vOffs, vOffsClosedCurves, vOffsFirstCurve, vLinks, PockParams)) 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( Release( vLinks[i]))) { return false ; } } pMCrv->AddCurve( Release( vOffs[i])) ; } // semplifico la curva ottenuta SimplifyCurveByFeeds( pMCrv, PockParams, 10 * EPS_SMALL) ; // verifico il percorso di lavoro if ( pMCrv->GetCurveCount() == 0) return false ; // reset delle proprietà temporanee delle curve componenti for ( int i = 0 ; i < pMCrv->GetCurveCount() ; ++ i) { pMCrv->SetCurveTempProp( i, 0, 0) ; pMCrv->SetCurveTempProp( i, 0, 1) ; } // setto estrusione pMCrv->SetExtrusion( vtExtr) ; // riporto tutto nel sistema di riferimento originale pMCrv->ToGlob( frPocket) ; ptStart.ToGlob( frPocket) ; vtMidOut.ToGlob( frPocket) ; if ( ! IsNull( vCrvOrigChunkLoops[0])) vCrvOrigChunkLoops[0]->ToGlob( frPocket) ; return true ; } //---------------------------------------------------------- static bool CalcZigZagLink( ICurveComposite* pCrv1, ICurveComposite* pCrv2, const PocketParams& PockParams, ICurveComposite* pCrvLink) { // se non richiesto, esco if ( ! PockParams.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 * PockParams.dRad) / 16)) / dLen1 ; // % parametro sinistro per smusso double dTollRight = ( ( 2 * PockParams.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, PockParams, 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 * PockParams.dRad) / 16)) / dLenI_e ; // % paramtro sinistro per smusso dTollRight = ( ( 2 * PockParams.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, PockParams, 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, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vpCrvs, 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) / PockParams.dSideStep)) ; dYStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ; } else { nYStep = static_cast< int >( floor(( dDimY + 30 * EPS_SMALL) / PockParams.dSideStep)) ; dYStep = PockParams.dSideStep ; } int nRef = (( nYStep + ( PockParams.bInvert ? 0 : 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))) ; if ( IsNull( pSeg)) continue ; 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) ; // assegno la feed al tratto lineare if ( ! AssignFeedZigZagOneWay( pLineCompo, false, vLineAbove, vLineUnder, vAddedLinks, PockParams)) // Assegno la Feed return false ; 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( pLastSeg->Clone()) ; PtrOwner pSeg2( pLineCompo->Clone()) ; if ( IsNull( pSeg1) || IsNull( pSeg2)) return false ; // aggiorno il link if ( ! CalcZigZagLink( pSeg1, pSeg2, PockParams, 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) ; if ( ! AssignFeedZigZagOneWay( pCrvLink, true, vLineAbove, vLineUnder, vAddedLinks, PockParams)) return false ; 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, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, 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 ; // massimo numero di regioni create dal primo offset 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 ; bool bOptimizedTrap = false ; // cerco le curve originali del chunk cc-esimo ( per casi ottimizzati) ICRVCOMPOPOVECTOR vCrvOrigChunkLoops ; if ( ! GetOptCrvIndex( pSfrOrig, pSrfChunk, PockParams, nReg, vCrvOrigChunkLoops)) return false ; // calcolo il percorso di svuotatura bool bSomeOpen ; Vector3d vtMidOut ; if ( ! CalcSpiral( pSrfChunk, PockParams, nRegTot, ptStart, vCrvOrigChunkLoops, pMCrv, pRCrv, bOptimizedTrap, bSomeOpen, vtMidOut)) return false ; // se terminate le regioni, esco if ( pMCrv->GetCurveCount() == 0) break ; // passo al chunk originale successivo if ( bOptimizedTrap) { // se caso a trapezio AdjustTrapezoidSpiralForLeadInLeadOut( pMCrv, PockParams) ; AssignFeedSpiralOpt( 1, PockParams, pMCrv) ; pMCrv->SetTempProp( TMP_PROP_OPT_TRAPEZOID, 0) ; } else if ( bSomeOpen) { // se presenza di lati aperti if ( ! ExtendPathOnOpenEdge( pMCrv, PockParams, pSrfChunk->GetNormVersor(), vtMidOut, false)) return false ; } // inserisco le curve nel vettore vCrvCompoRes.emplace_back( Release( pMCrv)) ; ++ nReg ; // incremento il numero di regione progressiva } } return true ; } //---------------------------------------------------------------------------- static bool AddSpiralOut( const ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, 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 ; // massimo numero di regioni create dal primo offset 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'interno all'esterno PtrOwner pMCrv( CreateCurveComposite()) ; PtrOwner pRCrv( CreateCurveComposite()) ; if ( IsNull( pMCrv) || IsNull( pRCrv)) return false ; int nRegTot = nReg ; Point3d ptStart ; bool bOptimizedTrap = false ; // cerco la curva originale del chunk cc-esimo ( per casi ottimizzati) ICRVCOMPOPOVECTOR vCrvOrig ; if ( ! GetOptCrvIndex( pSfrOrig, pSrfChunk, PockParams, nReg, vCrvOrig)) return false ; // calcolo il percorso di svuotatura bool bSomeOpen ; Vector3d vtMidOut ; if ( ! CalcSpiral( pSrfChunk, PockParams, nRegTot, ptStart, vCrvOrig, pMCrv, pRCrv, bOptimizedTrap, bSomeOpen, vtMidOut)) return false ; // se terminate le regioni, esco if ( pMCrv->GetCurveCount() == 0) break ; // passo al chunk originale successivo if ( bOptimizedTrap) { // se caso a trapezio AdjustTrapezoidSpiralForLeadInLeadOut( pMCrv, PockParams) ; AssignFeedSpiralOpt( 1, PockParams, pMCrv) ; pMCrv->SetTempProp( TMP_PROP_OPT_TRAPEZOID, 0) ; } // 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 ; } //---------------------------------------------------------------------------- static bool AddZigZag( const ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // Offset della regione per curva ZigZag double dOffs = PockParams.dRad + PockParams.dRadialOffset ; // 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(), PockParams.dAngle) ; // porto la superficie nel nuovo sistema di riferimento pSrfChunk->ToLoc( frPocket) ; // creo la regione per il percorso a ZigZag PtrOwner pSrfZigZag( pSrfChunk->Clone()) ; 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, PockParams, vpCrvs, false)) return false ; // aggiorno il vettore delle curve risultati for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) { // riportando prima in Globale vpCrvs[u]->ToGlob( frPocket) ; // controllo se il punto iniziale e finale sono esterni alla superficie originale Point3d ptStart ; vpCrvs[u]->GetStartPoint( ptStart) ; bool bIsInside = false ; if ( IsPointInsideSfr( pSfrOrig, ptStart, bIsInside) && ! bIsInside) { Vector3d vtMidOut ; vpCrvs[u]->GetStartDir( vtMidOut) ; vtMidOut.Invert() ; ExtendPathOnOpenEdge( vpCrvs[u], PockParams, pSrfChunk->GetNormVersor(), vtMidOut, false) ; } Point3d ptEnd ; vpCrvs[u]->GetEndPoint( ptEnd) ; if ( IsPointInsideSfr( pSfrOrig, ptEnd, bIsInside) && ! bIsInside) { Vector3d vtMidOut ; vpCrvs[u]->GetEndDir( vtMidOut) ; ExtendPathOnOpenEdge( vpCrvs[u], PockParams, pSrfChunk->GetNormVersor(), vtMidOut, true) ; } // inserisco le curve nel vettore risultante 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( pSrfOrig->Clone()) ; 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( 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( 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( 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( pSrfToPock->GetLoop( i, 0)) ; for ( int l = 1 ; l < pSrfToPock->GetLoopCount( i) ; ++ l) pSrfIdeal->AddIntLoop( 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 ( ! vSrfIdeal.empty()) ; } //---------------------------------------------------------------------------- static bool AddOneWay( const ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes) { double dOffs = PockParams.dRad + PockParams.dRadialOffset ; 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( vSrfFlat[nIs]->Clone()) ; 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(), PockParams.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) / PockParams.dSideStep)) ; double dYStep = ( nYStep > 0 ? ( dDimY + 2 * dExtra) / nYStep : 0) ; -- nYStep ; // vettore dei segmenti al di sotto della linea corrente ICURVEPOVECTOR vLineUnder ; ICURVEPOVECTOR vCrvNull ; ICRVCOMPOPOVECTOR vCrvCompoNull ; // 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 ; // se richiesta inversione if ( PockParams.bInvert) pLine->Invert() ; // linea come composita per Feed ( la dovrò spezzare) PtrOwner pCrvCompo( CreateCurveComposite()) ; if ( IsNull( pCrvCompo)) 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))) ; // aggiungo il segmento al vettore dei tratti correnti vAddedLines.emplace_back( pCrvSeg->Clone()) ; // trasformo il tratto lineare in curve composita per il calcolo della Feed PtrOwner pCrvSegCompo( CreateCurveComposite()) ; if ( IsNull( pCrvSegCompo)) return false ; pCrvSegCompo->AddCurve( Release( pCrvSeg)) ; // calcolo la Feed AssignFeedZigZagOneWay( pCrvSegCompo, false, vCrvNull, vLineUnder, vCrvCompoNull, PockParams) ; pCrvSegCompo->ToGlob( frPocket) ; vCrvCompoRes.emplace_back( Release( pCrvSegCompo)) ; } } // i tratti lineari correnti diventano i precedenti vLineUnder.clear() ; for ( int u = 0 ; u < int( vAddedLines.size()) ; ++ u) vLineUnder.emplace_back( Release( vAddedLines[u])) ; } } return true ; } //---------------------------------------------------------------------------- bool CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double dStep, double dAngle, int nType, bool bSmooth, bool bInvert, bool bAvoidOpt, ISurfFlatRegion* pSfrLimit, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid() || dStep < 10 * 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 PocketParams myParams ; myParams.nType = nType ; myParams.dRad = dRad ; myParams.dRadialOffset = dRadOffs ; myParams.bSmooth = bSmooth ; myParams.dAngle = dAngle ; myParams.dSideStep = dStep ; myParams.bInvert = bInvert ; myParams.bAvoidOpt = bAvoidOpt ; if ( pSfrLimit != nullptr && pSfrLimit->IsValid()) myParams.SfrLimit.CopyFrom( pSfrLimit) ; // ------------ gestione dei lati aperti ------------------- // creo un sistema di riferimento locale Point3d ptCenter ; pSfr->GetCentroid( ptCenter) ; Vector3d vtN = pSfr->GetNormVersor() ; Frame3d frLoc ; if ( ! frLoc.Set( ptCenter, vtN)) return false ; // porto la superficie da svuotare in tale sistema PtrOwner pSfrAdj( pSfr->Clone()) ; if ( IsNull( pSfrAdj) || ! pSfrAdj->ToLoc( frLoc)) return false ; // porto la superficie limite ( se valida) in tale sistema if ( myParams.SfrLimit.IsValid()) myParams.SfrLimit.ToLoc( frLoc) ; // modifico la superficie da svuotare estendendo i lati aperti bool bSkipPocket = false ; // se la superficie si svuota mediante curve singole di primo Offset dei chiusi if ( ! ModifySurfByOpenEdges( pSfrAdj, myParams, vCrvCompoRes, bSkipPocket)) return false ; // se ottengo delle curve si svuotatura di primo Offset, le riporto in globale for ( int i = 0 ; i < int( vCrvCompoRes.size()) ; ++ i) vCrvCompoRes[i]->ToGlob( frLoc) ; if ( bSkipPocket) // se tali curva rimuovono tutta la regione, allora non faccio nulla return true ; // riporto tutto nel frame originale pSfrAdj->ToGlob( frLoc) ; if ( myParams.SfrLimit.IsValid()) myParams.SfrLimit.ToGlob( frLoc) ; // ------------ calcolo delle curve elementari della superficie ------------------- switch ( nType) { case POCKET_ZIGZAG : return AddZigZag( pSfrAdj, pSfr, myParams, vCrvCompoRes) ; case POCKET_ONEWAY : return AddOneWay( pSfrAdj, pSfr, myParams, vCrvCompoRes) ; case POCKET_SPIRALIN : return AddSpiralIn( pSfrAdj, pSfr, myParams, vCrvCompoRes) ; case POCKET_SPIRALOUT : return AddSpiralOut( pSfrAdj, pSfr, myParams, vCrvCompoRes) ; } return false ; } //---------------------------------------------------------------------------- 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, const PocketParams& PockParams, 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( pSfrTrap->Clone()) ; if ( IsNull( pSfrTrap_c)) return false ; if ( ! pSfrTrap->Offset( - PockParams.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, const PocketParams& PockParams, 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, PockParams, 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( pCrvCompo->Clone()) ; 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() ; PocketParams myParams ; myParams.bSmooth = false ; myParams.dRad = 0.5 * dStep ; // 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, myParams, vpCrvs, 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], myParams, 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) { myParams.bSmooth = true ; ModifyCurveToSmoothed( vpCrvs[u], myParams, myParams.dRad / 16, myParams.dRad / 16, false) ; } vCrvCompoRes.emplace_back( Release( vpCrvs[u])) ; } } return true ; }