//---------------------------------------------------------------------------- // 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 "Voronoi.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/EGkDistPointSurfFr.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/EGkChainCurves.h" #include "/EgtDev/Include/EgtNumUtils.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 ; // tipo di lavorazione int nLiType = LEAD_IN_NONE ; // tipo di ingresso (LeadIn) int nLoType = LEAD_OUT_NONE ; // tipo di uscita (LeadOut) 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 dMaxOpenEdgeRad = 0. ; // massimo raggio per estensione lati aperti double dOpenMinSafe = 5. ; // estensione minima di sicurezza double dMaxOptSize = 0. ; // dimensione per ottimizzazione double dAngle = 0. ; // angolo per orientare le passate OneWay e ZigZag double dSmooth = 5. ; // parametro di smusso per link (raccordo) double dLiTang = INFINITO ; // valore tangente di LeadIn double dLiElev = INFINITO ; // valore elevazione di LeadIn (usato anche per LeadOut) double dLoTang = INFINITO ; // valore di LeadOut bool bCalcUnclearedRegs = true ; // flag per calcolare o meno le regioni non svuotate bool bOptOffsets = true ; // flag per evitare Offset non necessari 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 bool bConventionalMilling = true ; // flag per Conventional Milling Vs Climb Milling per svuotature a curva singola bool bAllowZigZagOneWayBorders = false ; // flag per abilitare le curve di bordo per ZigZag/OneWay 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 // ------------------------------------------------------- bool bAllClosed = true ; // flag per indicare se tutti i bordi della *pSfr da svuotare sono chiusi bool bAllOpen = true ; // flag per indicare se tutti i bordi della *pSfr da svuotare sono aperti double dOffsExtra = 2. ; // Offset aggiuntivo per percosi a ZigZag/OneWay se richieste le curve di bordo Frame3d frLocXY ; // frame per conti nel piano XY int nOffsType = ICurve::OFF_FILLET ; // tipologia di Offset per estensione degli aperti double dOpenEdgeRad = 0. ; // raggio effettivo di estensione degli aperti // -------------------------------------------------------- bool bPolishing = false ; // flag per lucidatura double dEpicyclesRad = 0. ; // raggio degli epicicli double dEpicyclesDist = 0. ; // distanza degli epicicli } ; static double TOL_TRAPEZOID = 50 * EPS_SMALL ; // tolleranza per casi a trapezio SpiralPocket static double TOL_REMOVE_OFFSET = 2. ; // tolleranza per controllo materiale lasciato da un Offset typedef vector VICRVCOMPOPOVECTOR ; //--------------------------------------------------------------------------- // definizione varibaile di debug #define ENABLE_DEBUG 0 #if ENABLE_DEBUG #include "EgtDev/Include/EGkGeoObjSave.h" #include "EgtDev/Include/EGkGeoPoint3d.h" #include "EgtDev/Include/EGkGeoVector3d.h" // Varibili ausiliarie vector VT ; vector VC ; //--------------------------------------------------------------------------- inline Color GetRandomColor() { return Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; } //--------------------------------------------------------------------------- inline void DrawCurve( const ICurveComposite* pCrvCompo) { if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return ; for ( int nU = 0 ; nU < pCrvCompo->GetCurveCount() ; ++ nU) { int nProp0 ; pCrvCompo->GetCurveTempProp( nU, nProp0, 0) ; VT.emplace_back( static_cast( pCrvCompo->GetCurve( nU)->Clone())) ; VC.emplace_back( nProp0 == 0 ? BLUE : RED) ; } return ; } //--------------------------------------------------------------------------- inline void DrawSfrAndLoops( const ISurfFlatRegion* pSfr, Color cCol = Color( 0., 255., 0., .5)) { if ( pSfr == nullptr || ! pSfr->IsValid()) return ; VT.emplace_back( static_cast( pSfr->Clone())) ; VC.emplace_back( cCol) ; for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) { for ( int nL = 0 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) { PtrOwner pCrvCompo( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ; DrawCurve( pCrvCompo->Clone()) ; } } return ; } #endif //--------------------------------------------------------------------------- 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 nU = 0 ; nU < pCrv->GetCurveCount() ; ++ nU) pCrv->SetCurveTempParam( nU, 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 */ // dominio dPar = Clamp( dPar, 0., 2. * PockParams.dRad) ; // se la funzione si riduce ad un unico tratto costante if ( 2. * PockParams.dRad - PockParams.dSideStep < 50. * EPS_SMALL) { dFeed = GetMaxFeed( PockParams) ; return true ; } // altrimenti controllo in quale tratto lineare cade il parametro else { if ( PockParams.dSideStep < dPar + 50. * EPS_SMALL) { dFeed = GetFeed( PockParams) + ( GetFeed( PockParams) * ( 1. - ( PockParams.dSideStep / ( 2. * PockParams.dRad)))) * ( dPar - PockParams.dSideStep) / ( PockParams.dSideStep - ( 2. * PockParams.dRad)) ; } 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 SwapSideBySecondTempParam( ICurveComposite* pCompo) { if ( pCompo == nullptr || ! pCompo->IsValid()) return false ; pCompo->SetTempParam( int( pCompo->GetTempParam( 1)) == MDS_LEFT ? MDS_RIGHT : MDS_LEFT, 1) ; 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 ISurfFlatRegion* pSfrOpenEdges, const PocketParams& PockParams) { // NB. pSfrOpenEdges deve essere una regione che all'esterno contiene i lati considerati aperti // Solo ai tratti esterni vengono assegnate le Feed // se non rischiesto il calcolo della Feed, lascio quella standard if ( ! PockParams.bCalcFeed) return AssignMaxFeed( pCrv, PockParams) ; // se non ho una regione di classificazione, non faccio nulla if ( pSfrOpenEdges == nullptr || ! pSfrOpenEdges->IsValid()) return true ; // classifico la curva in base alla regione CRVCVECTOR ccClass ; if ( pSfrOpenEdges->GetCurveClassification( *pCrv, EPS_SMALL, ccClass)) { PtrOwner pNewCrv( CreateCurveComposite()) ; if ( IsNull( pNewCrv)) return false ; for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) { // recupero il tratto di curva corrente PtrOwner pCrvRange( ConvertCurveToComposite( pCrv->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ; if ( ! IsNull( pCrvRange) && pCrvRange->IsValid()) { // se il tratto non è interno, calcolo la sua Feed if ( ccClass[i].nClass != CRVC_IN) { double dFeed = GetMinFeed( PockParams) ; GetFeedForParam( 2. * PockParams.dRad - PockParams.dOpenEdgeRad, PockParams, dFeed) ; AssignCustomFeed( pCrvRange, PockParams, dFeed) ; } // aggiungo il tratto alla sottocurva if ( ! pNewCrv->AddCurve( Release( pCrvRange))) return false ; } } // assegno la curva calcolata pNewCrv->SetTempProp( pCrv->GetTempProp( 0), 0) ; pNewCrv->SetTempProp( pCrv->GetTempProp( 1), 1) ; pNewCrv->SetTempParam( pCrv->GetTempParam( 0), 0) ; pNewCrv->SetTempParam( pCrv->GetTempParam( 1), 1) ; double dThick ; pCrv->GetThickness( dThick) ; pNewCrv->SetThickness( dThick) ; Vector3d vtExtr ; pCrv->GetExtrusion( vtExtr) ; pNewCrv->SetExtrusion( vtExtr) ; pCrv->CopyFrom( pNewCrv) ; } 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, ANG_TOL_STD_DEG, false) ; pCrv->GetLength( dLen) ; for ( int nU = 0 ; nU < pCrv->GetCurveCount() ; ++ nU) pCrv->SetCurveTempParam( nU, ( 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, ANG_TOL_STD_DEG, false) ; double dLen ; pCrvLast->GetLength( dLen) ; for ( int nU = 0 ; nU < pCrvLast->GetCurveCount() ; ++ nU) pCrvLast->SetCurveTempParam( nU, ( 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, ANG_TOL_STD_DEG, 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, ANG_TOL_STD_DEG, false) ; // recupero la lunghezza di tale curva e imposto la Feed pCrvLast->GetLength( dLen) ; for ( int nU = 0 ; nU < pCrvLast->GetCurveCount() ; ++ nU) pCrvLast->SetCurveTempParam( nU, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ; pCrvCompo->AddCurve( Release( pCrvLast)) ; } return true ; } //---------------------------------------------------------------------------- static bool AssignFeedSpiral( ICurveComposite* pCompo, const ISurfFlatRegion* pSrfRemoved, bool bIsLink, bool bFirstOffs, const ISurfFlatRegion* pSfrOpenEdges, const PocketParams& PockParams, double dTol) { // controllo la validità della curva if ( pCompo == nullptr || ! pCompo->IsValid() || pCompo->GetCurveCount() == 0) return false ; // controllo se il Flag per assegnare la Feed è attivo if ( ! PockParams.bCalcFeed) return AssignMaxFeed( pCompo, PockParams) ; // Se link di conformal ZigZag if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG && bIsLink) return AssignMaxFeed( pCompo, PockParams) ; // imposto di Default la Feed minima per ogni sottocurva AssignMinFeed( pCompo, PockParams) ; // se non ho una superificie svuotata, allora esco ( con Feed Minima) if ( pSrfRemoved == nullptr || ! pSrfRemoved->IsValid() || pSrfRemoved->GetChunkCount() == 0) { // controllo eventuali sovrapposizioni con lati aperti return AssignFeedForOpenEdge( pCompo, pSfrOpenEdges, PockParams) ; } // parametro di Offset per regione svuotata ( restringo la superficie in maniera appropriata) double dOffs = 0. ; if ( bIsLink) dOffs = - PockParams.dRad ; else if ( PockParams.dRad < PockParams.dSideStep) dOffs = PockParams.dSideStep - PockParams.dRad ; // clono la superificie ( valida) PtrOwner pSrfRemovedOffs( pSrfRemoved->CreateOffsetSurf( dOffs, ICurve::OFF_FILLET)) ; if ( IsNull( pSrfRemovedOffs) || ! pSrfRemovedOffs->IsValid() || pSrfRemovedOffs->GetChunkCount() == 0) return true ; // esco ( sempre con Feed Minima) // classifico le parti interne alla superificie creata solo dagli Offset CRVCVECTOR ccClass ; if ( ! pSrfRemovedOffs->GetCurveClassification( *pCompo, EPS_SMALL, ccClass)) return true ; // esco ( sempre con Feed Minima) // creo la nuova curva con le Feed regolate PtrOwner pCompoNew( CreateCurveComposite()) ; if ( IsNull( pCompoNew)) return false ; for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) { // recupero il tratto di curva ricavato dalla classificazione PtrOwner pSubCompo( ConvertCurveToComposite( pCompo->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ; if ( IsNull( pSubCompo)) continue ; // curva troppo piccola, passo alla successiva // controllo se interno o fuori alla regione ( regolando quindi la Feed) double dCurrFeed = ( ccClass[i].nClass == CRVC_IN ? GetMaxFeed( PockParams) : GetMinFeed( PockParams)) ; // assegno tale Feed ad ogni sottocurva ricavata dal tratto di classificazione for ( int nU = 0 ; nU < pSubCompo->GetCurveCount() ; ++ nU) pSubCompo->SetCurveTempParam( nU, dCurrFeed, 0) ; // aggiungo la nuova curva con la Feed regolata a quella finale pCompoNew->AddCurve( Release( pSubCompo)) ; } // sotituisco con quanto calcolato pCompo->Clear() ; pCompo->AddCurve( Release( pCompoNew)) ; // cerco di uniformare i tratti ricavati if ( ! bIsLink) { // ---------------- NEL CASO DI OFFSET ---------------- // creo un intervallo con tutte le Feed Minime Intervals IntMinFeed ; for ( int nU = 0 ; nU < pCompo->GetCurveCount() ; ++ nU) { double dFeed ; pCompo->GetCurveTempParam( nU, dFeed, 0) ; if ( abs( dFeed - GetMinFeed( PockParams)) < 5. * EPS_SMALL) IntMinFeed.Add( nU, nU + 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( pCompo->CopyParamRange( dParS, dParE)) ; double dLen = EPS_SMALL ; pCrv->GetLength( dLen) ; if ( dLen > dTol + 5. * EPS_SMALL) IntMinFeed_noSmall.Add( dParS, dParE) ; bFound = IntMinFeed.GetNext( dParS, dParE) ; } for ( int nU = 0 ; nU < pCompo->GetCurveCount() ; ++ nU) { if ( ! IntMinFeed_noSmall.IsInside( nU + 0.5)) pCompo->SetCurveTempParam( nU, GetMaxFeed( PockParams), 0) ; } } else { // ---------------- NEL CASO DI LINK ---------------- // le curve con lunghezza inferiore alla tolleranza vanno modificate for ( int j = 0 ; j < pCompo->GetCurveCount() ; ++ j) { double dLen = EPS_SMALL ; pCompo->GetCurve( j)->GetLength( dLen) ; if ( dLen < dTol + 5. * EPS_SMALL) { // se curva piccola, ricavo il tratto successivo lungo quanto il diametro double dLenStart ; pCompo->GetLengthAtParam( j, dLenStart) ; double dLenEnd = dLenStart + dLen + 2 * PockParams.dRad ; double dUE = pCompo->GetCurveCount() ; pCompo->GetParamAtLength( dLenEnd, dUE) ; bool bFound = false ; for ( int k = j + 1 ; k < int( ceil( dUE)) ; ++ k) { double dLenH = EPS_SMALL ; pCompo->GetCurve( k)->GetLength( dLenH) ; if ( dLenH < dTol + 5 * EPS_SMALL) continue ; // cerco tra le curve successive vicine se ne trovo una con Feed Minima double dParam ; pCompo->GetCurveTempParam( k, dParam, 0) ; if ( abs( dParam - GetMinFeed( PockParams)) < 5. * EPS_SMALL) { pCompo->SetCurveTempParam( k, dParam, 0) ; bFound = true ; break ; } } if ( ! bFound && j != 0) { // arrivato qui, so che successivamente non ho curve con Feed Minima vicine double dParam ; pCompo->GetCurveTempParam( j - 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 pCompo->SetCurveTempParam( j, dParam, 0) ; } else pCompo->SetCurveTempParam( j, GetMinFeed( PockParams), 0) ; } } } if ( bFirstOffs) AssignFeedForOpenEdge( pCompo, pSfrOpenEdges, PockParams) ; return true ; } //---------------------------------------------------------------------------- static bool AssignFeedSpiralOpt( 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) ; if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) { 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) ; } } /* 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... */ else { 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) ; } } return true ; } //---------------------------------------------------------------------------- static bool ResetCurveTempProps( ICurveComposite* pCrvCompo) { // controllo parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // scorro le curve for ( int nU = 0 ; nU < pCrvCompo->GetCurveCount() ; ++ nU) { pCrvCompo->SetCurveTempProp( nU, 0, 0) ; pCrvCompo->SetCurveTempProp( nU, 0, 1) ; } return true ; } //---------------------------------------------------------------------------- static bool IsCurveCompoHomogeneous( const ICurveComposite* pCrvCompo, bool& bAllClosed, bool& bAllOpen) { // controllo validità della curva if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; bAllClosed = true ; bAllOpen = true ; if ( pCrvCompo->GetCurveCount() == 0) return true ; // scorro le curve for ( int i = 0 ; i < pCrvCompo->GetCurveCount() && ( bAllOpen || bAllClosed) ; ++ i) { // recupero la sottocurva i-esima const ICurve* pCrv = pCrvCompo->GetCurve( i) ; if ( pCrv == nullptr || ! pCrv->IsValid()) return false ; int nTmpProp = pCrv->GetTempProp( 0) ; bAllOpen = bAllOpen && ( nTmpProp == TEMP_PROP_OPEN_EDGE) ; bAllClosed = bAllClosed && ( nTmpProp == TEMP_PROP_CLOSE_EDGE) ; } return true ; } //---------------------------------------------------------------------------- static bool GetSfrCrvCompoLoops( const ISurfFlatRegion* pSfr, ICRVCOMPOPOVECTOR& vCrvCompo) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid()) return false ; vCrvCompo.clear() ; // scorro i Chunk della superficie for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) { // scorro i loop del Chunk nC-esimo for ( int nL = 0 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) { // recupero il Loop come curva composita PtrOwner pCrvLoop( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ; if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid()) return false ; // inserisco la curva nel vettore vCrvCompo.emplace_back( Release( pCrvLoop)) ; } } return true ; } //---------------------------------------------------------------------------- static bool IsChunkAllHomogeneous( const ISurfFlatRegion* pSfr, int nChunk, bool& bAllClose, bool& bAllOpen) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid() || nChunk < 0 || nChunk > pSfr->GetChunkCount()) return false ; bAllClose = true ; bAllOpen = true ; // scorro tutti i loop del Chunk for ( int nL = 0 ; nL < pSfr->GetLoopCount( nChunk) ; ++ nL) { // recupero il loop PtrOwner pCrvLoop( ConvertCurveToComposite( pSfr->GetLoop( nChunk, nL))) ; if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid()) return false ; // scorro le curve semplici int nCurrProp = TEMP_PROP_INVALID ; for ( int nU = 0 ; nU < pCrvLoop->GetCurveCount() ; ++ nU) { bAllClose = ( bAllClose && pCrvLoop->GetCurveTempProp( nU, nCurrProp, 0) && nCurrProp == TEMP_PROP_CLOSE_EDGE) ; bAllOpen = ( bAllOpen && pCrvLoop->GetCurveTempProp( nU, nCurrProp, 0) && nCurrProp == TEMP_PROP_OPEN_EDGE) ; if ( ! bAllClose && ! bAllOpen) return true ; } } return true ; } //---------------------------------------------------------------------------- static bool IsSfrAllHomogeneous( const ISurfFlatRegion* pSfr, bool& bAllClose, bool& bAllOpen) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid()) return false ; bAllClose = true ; bAllOpen = true ; // ciclo sui Chunk della regione for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) { bool bChunkClose = true ; bool bChunkOpen = true ; if ( ! IsChunkAllHomogeneous( pSfr, nC, bChunkClose, bChunkOpen)) return false ; bAllClose = ( bAllClose && bChunkClose) ; bAllOpen = ( bAllOpen && bChunkOpen) ; } return true ; } //---------------------------------------------------------------------------- static bool IsSfrChunkMadeOnlyByClosedIslands( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParams, bool& bOK, ICRVCOMPOPOVECTOR& vCrvIsl) { // controllo dei parametri if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid()) return false ; vCrvIsl.clear() ; bOK = false ; // se non esistono isole if ( pSfrChunk->GetLoopCount( 0) == 1) return true ; // controllo il loop esterno PtrOwner pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ; if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid()) return false ; for ( int i = 0 ; i < pCrvExtLoop->GetCurveCount() ; ++ i) { int nTmpProp = TEMP_PROP_INVALID ; if ( pCrvExtLoop->GetCurveTempProp( i, nTmpProp, 0) && nTmpProp == TEMP_PROP_CLOSE_EDGE) return true ; } // controllo le Isole bool bOkIsl = true ; for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) && bOkIsl ; ++ i) { // recupero l'isola PtrOwner pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ; if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid()) return false ; // controllo le sue TmpProps for ( int i = 0 ; i < pCrvIsl->GetCurveCount() && bOkIsl ; ++ i) { int nTmpProp = TEMP_PROP_INVALID ; bOkIsl = ( pCrvIsl->GetCurveTempProp( i, nTmpProp, 0) && nTmpProp == TEMP_PROP_CLOSE_EDGE) ; } if ( bOkIsl) vCrvIsl.emplace_back( Release( pCrvIsl)) ; } if ( ! bOkIsl) vCrvIsl.clear() ; else bOK = true ; return true ; } //---------------------------------------------------------------------------- static bool GetIndOfHypoteticalChunk( const ICRVCOMPOPOVECTOR& vCrv, const ICurveComposite* pCrvA, const ICurveComposite* pCrvB, INTVECTOR& vIndA, INTVECTOR& vIndB) { /* date due curve interne ad una regione piana descritta da vCrv, individuare i chunk in cui sono rispettivamente contenute */ // controllo dei parametri for ( int i = 0 ; i < int( vCrv.size()) ; ++ i) { if ( vCrv[i] == nullptr || ! vCrv[i]->IsValid()) return false ; } if ( pCrvA == nullptr || ! pCrvA->IsValid() || pCrvB == nullptr || ! pCrvB->IsValid()) return false ; vIndA.clear() ; vIndB.clear() ; // ricavo le polyLines dalle curve POLYLINEVECTOR vPL ; for ( int i = 0 ; i < int( vCrv.size()) ; ++ i) { vPL.emplace_back( PolyLine()) ; vCrv[i]->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, vPL.back()) ; } // calcolo la matrice dei Chunks e dei Loops Vector3d vtN ; INTMATRIX vnPLIndMat ; BOOLVECTOR vbInvert ; if ( ! CalcRegionPolyLines( vPL, vtN, vnPLIndMat, vbInvert)) return false ; // scorro i Chunks for ( int nC = 0 ; nC < int( vnPLIndMat.size()) ; ++ nC) { PtrOwner pSfrTmp( CreateSurfFlatRegion()) ; if ( IsNull( pSfrTmp)) return false ; // scorro i Loops for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL) { if ( nL == 0) { if ( ! pSfrTmp->AddExtLoop( CloneCurveComposite( vCrv[vnPLIndMat[nC][0]]))) return false ; } else { if ( ! pSfrTmp->AddIntLoop( CloneCurveComposite( vCrv[vnPLIndMat[nC][nL]]))) return false ; } } // piccolo Offset di correzione della superfice pSfrTmp->Offset( 500 * EPS_SMALL, ICurve::OFF_FILLET) ; // classifico le curve con il Chunk nC-esimo CRVCVECTOR ccClass ; if ( pSfrTmp->GetCurveClassification( *pCrvA, EPS_SMALL, ccClass) && int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) { for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL) vIndA.emplace_back( vnPLIndMat[nC][nL]) ; } ccClass.clear() ; if ( pSfrTmp->GetCurveClassification( *pCrvB, EPS_SMALL, ccClass) && int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) { for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL) vIndB.emplace_back( vnPLIndMat[nC][nL]) ; } // se entrambi indici trovati, esco if ( ! vIndA.empty() && ! vIndB.empty()) return true ; } return true ; } //---------------------------------------------------------------------------- static double GetExtendPathLen( const PocketParams& PockParam) { return ( Clamp( 2. * PockParam.dRad - PockParam.dOpenEdgeRad, 0., PockParam.dRad) + PockParam.dOpenMinSafe) ; } //---------------------------------------------------------------------------- static bool ExtendPath( ICurveComposite* pCompo, const ISurfFlatRegion* pSfr, const PocketParams& PockParams, const Vector3d& vtFirstOut, bool bEndOrStart, double dExtension, bool& bOkExtended) { /* Estensione della curva pCompo con un segmento lineare. bEndOrStart: definisce se la curva va estesa all'inizio o alla fine vtFirstOut: prima direzione su cui tentare l'entrata, se questa non andasse bene ruoto progressivamente di 90° La curva viene estesa di un tratto lineare in direzione vtRotOut per una lunghezza pari a Rad + OffsR + OpenMinSafe. */ // controllo dei parametri if ( pCompo == nullptr || ! pCompo->IsValid() || pSfr == nullptr || ! pSfr->IsValid()) return false ; bOkExtended = false ; // recupero il punto iniziale/finale della curva con versore tangente Point3d pt ; Vector3d vtTan ; if ( bEndOrStart) { if ( ! pCompo->GetEndPoint( pt) || ! pCompo->GetEndDir( vtTan)) return false ; } else { if ( ! pCompo->GetStartPoint( pt) || ! pCompo->GetStartDir( vtTan)) return false ; vtTan.Invert() ; } // angoli di rotazione ( potrebbero servirne altri) const double ANG_45 = ANG_RIGHT / 2. ; const array vAngles{ 0., ANG_RIGHT, 3 * ANG_RIGHT, ANG_45, 3 * ANG_45, 5 * ANG_45, 7 * ANG_45} ; // ruoto il versore di uscita cercando un'entrata valida double dMinDist = PockParams.dRad + PockParams.dRadialOffset ; for ( int i = 0 ; i < ssize( vAngles) ; ++ i) { // ruoto il versore d'uscita Vector3d vtRotOut = GetRotate( vtFirstOut, Z_AX, - vAngles[i]) ; // calcolo il punto di caduta dell'utensile Point3d ptFall = pt + vtRotOut * dExtension ; // controllo che sia sufficientemente distante dalla superficie limite bool bInside = true ; bool bOkOut = true ; if ( PockParams.SfrLimit.IsValid()) { bOkOut = ( IsPointInsideSurfFr( ptFall, &PockParams.SfrLimit, dMinDist - 10. * EPS_SMALL, bInside) && ! bInside) ; if ( bOkOut) bOkOut = ( IsPointInsideSurfFr( Media( ptFall, pt), &PockParams.SfrLimit, dMinDist - 10. * EPS_SMALL, bInside) && ! bInside) ; } if ( bOkOut) bOkOut = ( IsPointInsideSurfFr( ptFall, pSfr, dMinDist, bInside) && ! bInside) ; if ( bOkOut) { // aggiungo al ritorno l'uscita pCompo->AddLine( ptFall, bEndOrStart) ; pCompo->SetCurveTempProp( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0), TEMP_PROP_OUT_START, 0) ; pCompo->SetCurveTempParam( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0), ( bEndOrStart ? GetMaxFeed( PockParams) : GetMinFeed( PockParams)), 0) ; bOkExtended = true ; return true ; } } return true ; } //---------------------------------------------------------------------------- static bool AdjustContourStart( ICurveComposite* pCompo, const PocketParams& PockParams) { // controllo dei parametri if ( pCompo == nullptr || ! pCompo->IsValid()) return false ; // cerco il tratto lineare chiuso più lungo ... 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() == TEMP_PROP_CLOSE_EDGE && 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 ( non lineare) if ( nMax < 0 || dLenMax < 1.25 * PockParams.dRad) { i = 0 ; pCrv = pCompo->GetFirstCurve() ; while ( pCrv != nullptr) { double dLen ; if ( pCrv->GetType() != CRV_LINE && pCrv->GetTempProp() == TEMP_PROP_CLOSE_EDGE && pCrv->GetLength( dLen) && dLen > dLenMax) { dLenMax = dLen ; nMax = i ; } ++ i ; pCrv = pCompo->GetNextCurve() ; } } // 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) ; 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)... AdjustContourStart( pCrvCompo, PockParams) ; // 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 PocketParams& PockParams, 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, PockParams.nOffsType) || ! 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, PockParams.nOffsType)) { PtrOwner pMyCrv( OffsCrv.GetLongerCurve()) ; while ( ! IsNull( pMyCrv) && pMyCrv->IsValid()) { if ( ! pMyCrv->IsClosed()) { pOffsInt.Set( pMyCrv) ; break ; } else pMyCrv.Set( OffsCrv.GetLongerCurve()) ; } } // recupero gli estremi della curva aperta corrente Point3d pt1 ; pCrv->GetEndPoint( pt1) ; Point3d pt4 ; pCrv->GetStartPoint( pt4) ; // verifico se una delle due curve esiste bool bExistExt = ( ! IsNull( pOffsExt) && pOffsExt->IsValid()) ; bool bExistInt = ( ! IsNull( pOffsInt) && pOffsInt->IsValid()) ; PtrOwner pCrvExtLoopSurfInc( CreateCurveComposite()) ; if ( IsNull( pCrvExtLoopSurfInc)) return false ; if ( bExistExt && bExistInt) { Point3d pt2 ; pOffsInt->GetEndPoint( pt2) ; Point3d pt3 ; pOffsInt->GetStartPoint( pt3) ; Point3d pt5 ; pOffsExt->GetStartPoint( pt5) ; pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ; pCrvExtLoopSurfInc->AddLine( pt1) ; pCrvExtLoopSurfInc->AddLine( pt2) ; pOffsInt->Invert() ; pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ; pCrvExtLoopSurfInc->AddLine( pt4) ; pCrvExtLoopSurfInc->AddLine( pt5) ; } else if ( bExistExt) { Point3d pt5 ; pOffsExt->GetStartPoint( pt5) ; pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ; pCrvExtLoopSurfInc->AddLine( pt1) ; pCrvExtLoopSurfInc->AddLine( pt4) ; pCrvExtLoopSurfInc->AddLine( pt5) ; } else if ( bExistInt) { Point3d pt5 ; pOffsInt->GetStartPoint( pt5) ; pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ; pCrvExtLoopSurfInc->AddLine( pt1) ; pCrvExtLoopSurfInc->AddLine( pt4) ; pCrvExtLoopSurfInc->AddLine( pt5) ; } else return false ; // per sicurezza... pCrvExtLoopSurfInc->Close() ; // creo la regione ( la curva potrebbe autointersecarsi) SurfFlatRegionByContours SfrByC ; if ( ! SfrByC.AddCurve( Release( pCrvExtLoopSurfInc))) return false ; PtrOwner pSfr( SfrByC.GetSurf()) ; if ( pSfr->IsValid() && pSfr->GetChunkCount() > 0) { if ( AreOppositeVectorApprox( pSfr->GetNormVersor(), Z_AX)) pSfr->Invert() ; pSfrInc->CopyFrom( pSfr) ; return true ; } return false ; } //---------------------------------------------------------------------------- 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 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 */ // 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 ( leggermente più grande ) PtrOwner pSfrInc( CreateSurfFlatRegion()) ; if ( IsNull( pSfrInc)) return false ; if ( ! CreateSurfFrIncidence( pCrvBorder, PockParams, dRad + 75. * EPS_SMALL, pSfrInc)) { pSfrInc.Set( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( pCrvBorder), dRad + 75 * EPS_SMALL, false, false)) ; if ( IsNull( pSfrInc) || ! pSfrInc->IsValid()) 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 i = 0 ; i < int( vCrvToCheck.size()) ; ++ i) { // 1) recupero la curva corrente PtrOwner pCrvCurr( vCrvToCheck[i]->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 nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU) { if ( vpCrvs[nU]->GetTempProp() == TEMP_PROP_OPEN_EDGE) continue ; // 4) effettuo l'Offset della curva di metà dDiamJ OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( vpCrvs[nU], - dDiamJ / 2. - 20. * EPS_SMALL, PockParams.nOffsType)) 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") bool bSquareEnds = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ; bool bSquareMids = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ; PtrOwner pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ / 2., bSquareEnds, bSquareMids)) ; 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 PtrOwner pSfrBean_test( CloneSurfFlatRegion( pSfrBean)) ; if ( IsNull( pSfrBean_test) || ! pSfrBean_test->IsValid()) return false ; pSfrBean_test->Intersect( *pSfrInc) ; bDiscard = ( IsNull( pSfrBean_test) || ! pSfrBean_test->IsValid() || pSfrBean_test->GetChunkCount() == 0) ; } 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 ; 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 // se curva originale non tutta aperta e multi-chunk if ( ! bIsAllOpen && 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) bool bFound = false ; 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)) { bFound = true ; // 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 ; } } } // se ho trovato qualcosa, allora aggiorno la superficie di incidenza if ( bFound && pNewSfrInc->IsValid()) pSfrInc.Set( pNewSfrInc) ; } // se la regione è formata da più chunks, prendo quella con area di loop esterno maggiore if ( ! IsNull( pSfrInc) && pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 1) { double dMaxArea = - INFINITO ; int nChunk = 0 ; for ( int nC = 0 ; nC < pSfrInc->GetChunkCount() ; ++ nC) { PtrOwner pExtLoop( pSfrInc->GetLoop( nC, 0)) ; if ( IsNull( pExtLoop) || ! pExtLoop->IsValid()) return false ; double dArea = EPS_SMALL ; pExtLoop->GetAreaXY( dArea) ; if ( dArea > dMaxArea) { dMaxArea = dArea ; nChunk = nC ; } } pSfrInc.Set( pSfrInc->CloneChunk( nChunk)) ; } // se regione di incidenza nulla o non valida, errore if ( IsNull( pSfrInc) || ! pSfrInc->IsValid()) return false ; } // 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, TEMP_PROP_OPEN_EDGE, 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, TEMP_PROP_OPEN_EDGE, 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, 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.dMaxOpenEdgeRad) > 0 && PockParams.dMaxOpenEdgeRad < dRad) dRad = PockParams.dMaxOpenEdgeRad ; // se lavorazione ZigZag/OneWay con richiesta di curve di Bordo, gli aperti vanno leggermente allargati if ( PockParams.bAllowZigZagOneWayBorders && ( PockParams.nType == POCKET_ZIGZAG || PockParams.nType == POCKET_ONEWAY)) dRad += PockParams.dOffsExtra ; // se lavorazione a ZigZag con smusso if ( PockParams.nType == POCKET_ZIGZAG && PockParams.bSmooth) dRad += PockParams.bSmooth ; // se lavorazione Conformal if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) { if ( PockParams.dRad - PockParams.dSideStep > 0) dRad -= PockParams.dSideStep ; } // salvo il raggio trovato PockParams.dOpenEdgeRad = dRad ; // 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.dMaxOpenEdgeRad) > 0) dDiamJ = 100 * EPS_SMALL ; else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) dDiamJ = 1.05 * ( PockParams.dRad + dRad) + dOffR ; // 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 == TEMP_PROP_OPEN_EDGE) { // controllo che sia almeno lungo quanto il diametro utensile double dLen = 0. ; vpCrvs[i]->GetLength( dLen) ; // 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, TEMP_PROP_CLOSE_EDGE) ; // 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 ( ssize( vCrvCompo) < 2) return true ; // controllo validità delle curve for ( int i = 0 ; i < ssize( vCrvCompo) ; ++ 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, ssize( vCrvCompo)) ; // evito inversioni, le curve devono essere coerenti for ( int i = 0 ; i < ssize( vCrvCompo) ; ++ 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( 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 ( int i = 0 ; i < ssize( vnInd) ; ++ 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 ChangePtStartForSinglePocketCurve( ICurveComposite* pCrvCompo, const PocketParams& PockParams, const Point3d& ptRef, bool bInvertOpenCrv) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // se la curva è aperta if ( ! pCrvCompo->IsClosed()) { // se richiesta inversione if ( bInvertOpenCrv) { // se non ho un punto di riferimento, non faccio nulla if ( ! ptRef.IsValid()) return true ; // se ho un punto di riferimento controllo quale estremo è più vicino Point3d ptStart ; pCrvCompo->GetStartPoint( ptStart) ; Point3d ptEnd ; pCrvCompo->GetEndPoint( ptEnd) ; if ( SqDist( ptRef, ptEnd) < SqDist( ptRef, ptStart)) pCrvCompo->Invert() ; } } // se la curva è chiusa else { // se non ho un punto di riferimento, scelgo il tratto più lungo if ( ! ptRef.IsValid()) { ResetCurveTempProps( pCrvCompo) ; AdjustContourStart( pCrvCompo, PockParams) ; } // se ho un punto di riferimento else { // cerco il punto più vicino DistPointCurve DistPtCrv( ptRef, *pCrvCompo) ; double dPar = 0. ; int nFlag ; if ( ! DistPtCrv.GetParamAtMinDistPoint( 0, dPar, nFlag)) return false ; pCrvCompo->ChangeStartPoint( dPar) ; } } return true ; } //---------------------------------------------------------------------------- static bool AdvanceExtendCurves( ICRVCOMPOPOVECTOR& vCrvCompo, const ISurfFlatRegion* pSfr, const PocketParams& PockParams, bool bInvertOpenCrv) { // se non ho curve, allora non faccio nulla if ( vCrvCompo.empty()) return true ; // per ogni curva for ( int i = 0 ; i < ssize( vCrvCompo) ; ++ i) { // controllo validità della curva if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid()) return false ; // cambio il punto iniziale della curva if ( ! ChangePtStartForSinglePocketCurve( vCrvCompo[i], PockParams, PockParams.ptStart, bInvertOpenCrv)) return false ; } // se superficie tutta chiusa, allora ho finito if ( PockParams.bAllClosed) return true ; /* L'estensione della curva permettendo un'entrata da fuori è valida se il nuovo punto di Inizio ( ptFall) : - E' al di fuori della superficie Limite ( di almeno Rad + OffsR) - E' al di fuori della superifice di svuotatura ( di almeno dRad + OffsR) */ // scorro ogni singola curva composita const int MAX_ITER = 4 ; for ( int i = 0 ; i < ssize( vCrvCompo) ; ++ i) { // controllo se la curva è chiusa o aperta bool bIsClosed = vCrvCompo[i]->IsClosed() ; // calcolo il numero massimo di punti in cui testare l'entrata da fuori int nMaxIter = ( bIsClosed ? MAX_ITER : 1) ; double dLen = EPS_SMALL ; if ( nMaxIter == MAX_ITER) vCrvCompo[i]->GetLength( dLen) ; // scorro il numero di punti su cui tentare l'entrata da fuori for ( int j = 0 ; j < nMaxIter ; ++ j) { if ( j > 0) { double dParAtLen = EPS_SMALL ; vCrvCompo[i]->GetParamAtLength( dLen / nMaxIter, dParAtLen) ; vCrvCompo[i]->ChangeStartPoint( dParAtLen) ; } // ricavo vettore tangente iniziale e provo ad estendere Vector3d vtStart ; vCrvCompo[i]->GetStartDir( vtStart) ; vtStart.Invert() ; bool bIsStartExtended = false ; if ( ! ExtendPath( vCrvCompo[i], pSfr, PockParams, vtStart, false, GetExtendPathLen( PockParams), bIsStartExtended)) return false ; // se aperta, controllo la fine if ( ! bIsClosed) { // ricavo vettore tangente finale e provo ad estendere Vector3d vtEnd ; vCrvCompo[i]->GetEndDir( vtEnd) ; bool bIsEndExtended = false ; if ( ! ExtendPath( vCrvCompo[i], pSfr, PockParams, vtEnd, true, GetExtendPathLen( PockParams), bIsEndExtended)) return false ; if ( bIsEndExtended) break ; } if ( bIsStartExtended) break ; } } return true ; } //---------------------------------------------------------------------------- static bool GetPocketCurvesByClosedEdges( 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 ; // controllo che il MaxOptSize sia coerente if ( 2. * dMaxOffs > PockParams.dMaxOptSize + 10. * 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) ; // unisco il primo e l'ultimo se estremi compatibili if ( ssize( vpCrvs) > 1) { 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) ; } } // CASO PARTICOLARE : Tunnel ( 2 tratti open e due tratto chiusi) -> non uso le curve singole if ( int( vpCrvs.size() == 4)) return true ; // controllo se il loop Esterno è uniforme ( quindi tutto chiuso o tutto aperto) bool bExtAllClose = false ; bool bExtAllOpen = false ; if ( ssize( vpCrvs) == 1) { if ( vpCrvs[0]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) bExtAllClose = true ; else bExtAllOpen = true ; } // NB. Se regione estena tutta chiusa o aperta e senza isole, 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 vCrvCompoResTmp ; // salvo il vettore di curve offsettate del raggio utensile Voronoi myVRONI ; for ( int i = 0 ; i < ssize( vpCrvs) ; ++ i) { // se tratto chiuso if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) myVRONI.AddCurve( vpCrvs[i]) ; } // offset delle parti chiuse ICURVEPOVECTOR vCrvVroniOffs ; myVRONI.CalcOffset( vCrvVroniOffs, - PockParams.dRad - PockParams.dRadialOffset + EPS_SMALL, ICurve::OFF_FILLET) ; for ( int i = 0 ; i < ssize( vCrvVroniOffs) ; ++ i) vCrvCompoResTmp.emplace_back( ConvertCurveToComposite( Release( vCrvVroniOffs[i]))) ; /* 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 = TEMP_PROP_INVALID ; int nPrecTmpProp = TEMP_PROP_INVALID ; bool bIsMixed = false ; PtrOwner pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ; if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid()) return false ; for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ nU) { pCrvIsl->GetCurveTempProp( nU, nCurrTmpProp, 0) ; bIsMixed = ( nU != 0 && nCurrTmpProp != nPrecTmpProp) ; nPrecTmpProp = nCurrTmpProp ; } // se proprità non uniformi -> tutta chiusa ( isole non uniformi non sono definite) if ( bIsMixed) { for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() ; ++ nU) pCrvIsl->SetTempProp( nU, TEMP_PROP_CLOSE_EDGE) ; nCurrTmpProp = TEMP_PROP_CLOSE_EDGE ; // aggiorno il Flag } // 2) e 3) bValidIslands = ( ( nCurrTmpProp == TEMP_PROP_CLOSE_EDGE && bExtAllOpen) || ( nCurrTmpProp == TEMP_PROP_OPEN_EDGE && bExtAllClose)) ; } if ( ! bValidIslands) return true ; // nel caso 2) devo inserire le curve if ( bExtAllOpen) { // creo una regione formata solo dalla isole PtrOwner pSrfIslands( CreateSurfFlatRegion()) ; if ( IsNull( pSrfIslands)) return false ; for ( int nL = 1 ; nL < pSfrChunk->GetLoopCount( 0) ; ++ nL) { pSrfIslands->AddExtLoop( pSfrChunk->GetLoop( 0, nL)) ; vpCrvs.emplace_back( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, nL))) ; } pSrfIslands->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET) ; for ( int nC = 0 ; nC < pSrfIslands->GetChunkCount() ; ++ nC) { for ( int nL = 0 ; nL < pSrfIslands->GetLoopCount( nL) ; ++ nL) vCrvCompoResTmp.emplace_back( ConvertCurveToComposite( pSrfIslands->GetLoop( nC, nL))) ; } } } // se non ho ottenuto curve, allora ho finito if ( vCrvCompoResTmp.empty()) return true ; // controllo che effettivamente l'utensile svuoti la regione PtrOwner pSfrNotRemoved( CloneSurfFlatRegion( pSfrChunk)) ; if ( IsNull( pSfrNotRemoved) || ! pSfrNotRemoved->IsValid()) return false ; double dOffs = PockParams.dRad + PockParams.dRadialOffset + 50. * EPS_SMALL ; double dTol = 25. * EPS_SMALL ; for ( int i = 0 ; i < ssize( vCrvCompoResTmp) ; ++ i) { PtrOwner pSfrRemoved( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vCrvCompoResTmp[i]), dOffs, false, false)) ; if ( ! IsNull( pSfrRemoved) && pSfrRemoved->IsValid()) { if ( AreOppositeVectorEpsilon( pSfrRemoved->GetNormVersor(), pSfrNotRemoved->GetNormVersor(), dTol)) pSfrRemoved->Invert() ; pSfrNotRemoved->Subtract( *pSfrRemoved) ; } } for ( int i = 0 ; i < ssize( vpCrvs) ; ++ i) { if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) { PtrOwner pSfrRemoved( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vpCrvs[i]), dOffs, false, false)) ; if ( ! IsNull( pSfrRemoved) && pSfrRemoved->IsValid()) { if ( AreOppositeVectorEpsilon( pSfrRemoved->GetNormVersor(), pSfrNotRemoved->GetNormVersor(), dTol)) pSfrRemoved->Invert() ; pSfrNotRemoved->Subtract( *pSfrRemoved) ; } } } if ( pSfrNotRemoved->IsValid() && pSfrNotRemoved->GetChunkCount() > 0) 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()) { // 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 - dTol, ICurve::OFF_FILLET) ; // c'è un offset radiale, quindi tengo tolleranza alta ICRVCOMPOPOVECTOR vCrvCompoResTmpSplitted ; for ( int i = 0 ; i < ssize( vCrvCompoResTmp) ; ++ i) { CRVCVECTOR ccClass ; if ( pSfrLimit->GetCurveClassification( *vCrvCompoResTmp[i], EPS_SMALL, ccClass)) { for ( int j = 0 ; j < ssize( ccClass) ; ++ j) { if ( ccClass[j].nClass == CRVC_OUT) { PtrOwner pCrvRes( ConvertCurveToComposite( vCrvCompoResTmp[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ; if ( ! IsNull( pCrvRes) && pCrvRes->IsValid()) vCrvCompoResTmpSplitted.emplace_back( Release( pCrvRes)) ; } } } } // se non ho curve, allora esco if ( vCrvCompoResTmpSplitted.empty()) return true ; // altrimenti aggiorno il vettore di curve con quelle solo esterne alla regione limite swap( vCrvCompoResTmp, vCrvCompoResTmpSplitted) ; } // salvo come primo tempParam l'offset massimo della regione ( servirà per la Feed) for ( int i = 0 ; i < ssize( vCrvCompoResTmp) ; ++ i) { vCrvCompoResTmp[i]->SetTempParam( dMaxOffs, 0) ; // se richiesta inversione della curva, allora inverto if ( PockParams.bInvert) vCrvCompoResTmp[i]->Invert() ; vCrvCompoRes.emplace_back( Release( vCrvCompoResTmp[i])) ; } return true ; } //---------------------------------------------------------------------------- static bool GetSinglePocketingCurves( ISurfFlatRegion* pSfr, PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvSingleCurves) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid()) return false ; vCrvSingleCurves.clear() ; // tengo una copia della superficie ( serve per l'estensione dei percorsi fuori dal grezzo) PtrOwner pSfrClone( CloneSurfFlatRegion( pSfr)) ; if ( IsNull( pSfrClone) || ! pSfrClone->IsValid()) return false ; // scorro i Chunk della superficie int nChunks = pSfr->GetChunkCount() ; int nCurrChunk = 0 ; for ( int i = 0 ; i < nChunks ; ++ i) { // recupero il Chunk corrente PtrOwner pSfrChunk( pSfr->CloneChunk( nCurrChunk)) ; if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid()) return false ; // se il Chunk corrente si svuota con curva singola, allora lo rimuovo dalla superficie bool bRemoved = false ; GetPocketCurvesByClosedEdges( pSfrChunk, PockParams, vCrvSingleCurves, bRemoved) ; if ( bRemoved) pSfr->EraseChunk( nCurrChunk) ; else ++ nCurrChunk ; } // 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( vCrvSingleCurves)) return false ; // estendo le curve, definendo ptStart e calcolando le Feeds if ( ! AdvanceExtendCurves( vCrvSingleCurves, pSfrClone, PockParams, false)) return false ; // imposto le Feed e inverto i percorsi per tali curve se richiesto bool bAllowInvert = ( ! PockParams.bConventionalMilling) ; for ( int i = 0 ; i < ssize( vCrvSingleCurves) ; ++ i) { /* Idea : Feed proporzionale al minimo Offset per annullare la regione -> Massimo parametro sui bisettori di VORONOI se le curve risultano aperte, cambio il loro punto iniziale a seconda dell'estensione fatta */ if ( vCrvSingleCurves[i]->IsValid() && vCrvSingleCurves[i]->GetCurveCount() > 0 && ! vCrvSingleCurves[i]->IsClosed()) { int nFirstTempProp = vCrvSingleCurves[i]->GetFirstCurve()->GetTempProp( 0) ; int nLastTempProp = vCrvSingleCurves[i]->GetLastCurve()->GetTempProp( 0) ; // se estremi entrambi estesi if ( nFirstTempProp == TEMP_PROP_OUT_START && nLastTempProp == TEMP_PROP_OUT_START) { // se inversione delle curve accettata if ( bAllowInvert) { if ( PockParams.ptStart.IsValid()) { Point3d ptStart ; vCrvSingleCurves[i]->GetStartPoint( ptStart) ; double dSqStartDist = SqDist( PockParams.ptStart, ptStart) ; Point3d ptEnd ; vCrvSingleCurves[i]->GetEndPoint( ptEnd) ; double dSqEndDist = SqDist( PockParams.ptStart, ptEnd) ; if ( dSqEndDist < dSqStartDist) vCrvSingleCurves[i]->Invert() ; } } vCrvSingleCurves[i]->GetEndPoint( PockParams.ptStart) ; } // se invece l'estensione è alla fine, sono forzato ad invertila per entrare presso il lato aperto else if ( nLastTempProp == TEMP_PROP_OUT_START) vCrvSingleCurves[i]->Invert() ; } // recupero il MaxOffset per la curva double dMaxOffs = vCrvSingleCurves[i]->GetTempParam( 0) ; // calcolo la Feed proporzionale a tale Offset double dFeed ; GetFeedForParam( 2 * dMaxOffs, PockParams, dFeed) ; AssignCustomFeed( vCrvSingleCurves[i], PockParams, dFeed) ; // imposto il flag di curva singola vCrvSingleCurves[i]->SetTempProp( TEMP_PROP_SINGLE_CURVE, 0) ; } return true ; } //---------------------------------------------------------------------------- static bool ModifySurfByOpenEdges( ISurfFlatRegion* pSfr, PocketParams& PockParams) { // controllo parametri : if ( pSfr == nullptr || ! pSfr->IsValid()) return true ; // <- se superficie non valida, allora non ho niente da impostare sui suoi lati // se lati tutti chiusi, allora non devo fare nulla if ( PockParams.bAllClosed) return true ; // 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 ; // per ogni Chunck della superificie ottenuta... for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) { // flag per indicare se il Chunk è stato modificato mediante estensione degli aperti bool bIsChunkModified = true ; // ricavo il Loop esterno ( External Loop) PtrOwner pCrvEL( ConvertCurveToComposite( pSfr->GetLoop( nC, 0))) ; if ( IsNull( pCrvEL) || ! pCrvEL->IsValid()) return false ; // creo un vettore di curve con le isole del Chunk ( Internal Loops) ICRVCOMPOPOVECTOR vCrvIsl ; for ( int nL = 1 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) { PtrOwner pCrvIL( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ; if ( IsNull( pCrvIL) || ! pCrvIL->IsValid()) return false ; vCrvIsl.emplace_back( Release( pCrvIL)) ; } // se la curva esterna presenta dei lati aperti -> devo modificarla bool bSomeOpen = false ; int nProp0 = TEMP_PROP_INVALID ; for ( int u = 0 ; u < pCrvEL->GetCurveCount() && ! bSomeOpen ; ++ u) bSomeOpen = ( pCrvEL->GetCurveTempProp( u, nProp0, 0) && nProp0 == TEMP_PROP_OPEN_EDGE) ; if ( bSomeOpen) { // allorargo il Loop esterno presso i lati aperti if ( ! AdjustContourWithOpenEdges( pCrvEL, vCrvIsl, PockParams)) return false ; bIsChunkModified = true ; } // controllo i bordi delle isole ottenute // NB. L'isola può essere tutta aperta o tutta chiusa ( se non uniforme, la forzo chiusa) // [ La definizione di isola con proprietà non uniformi non è definita ] ICRVCOMPOPOVECTOR vCrvToTIsland ; // isole che considero valide for ( int nI = 0 ; nI < int( vCrvIsl.size()) ; ++ nI) { // controllo uniformità int nCurrTmpProp = TEMP_PROP_INVALID ; int nPrecTmpProp = TEMP_PROP_INVALID ; bool bIsMixed = false ; PtrOwner pCrvIsl( CloneCurveComposite( vCrvIsl[nI])) ; if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid()) return false ; for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ nU) { pCrvIsl->GetCurveTempProp( nU, nCurrTmpProp, 0) ; bIsMixed = ( nU != 0 && nCurrTmpProp != nPrecTmpProp) ; nPrecTmpProp = nCurrTmpProp ; } // se proprità non uniformi -> tutta chiusa if ( bIsMixed) { for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() ; ++ nU) pCrvIsl->SetTempProp( nU, TEMP_PROP_CLOSE_EDGE) ; nCurrTmpProp = TEMP_PROP_CLOSE_EDGE ; bIsChunkModified = true ; } // se curva tutta aperta, controllo se l'isola può essere trascurata if ( nCurrTmpProp == TEMP_PROP_OPEN_EDGE) { // calcolo il massimo Offset double dMaxOffs ; CalcCurveLimitOffset( *pCrvIsl, dMaxOffs) ; // se l'isola è trascurabile passo alla successiva if ( dMaxOffs < 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) continue ; // altrimenti la restringo ICRVCOMPOPOVECTOR vCrvOther ; vCrvOther.emplace_back( pCrvEL->Clone()) ; for ( int nII = 0 ; nII < int( vCrvIsl.size()) ; ++ nII) { if ( nII != nI) vCrvOther.emplace_back( vCrvIsl[nII]->Clone()) ; } if ( ! AdjustContourWithOpenEdges( pCrvIsl, vCrvOther, PockParams)) return false ; bIsChunkModified = true ; } // conservo l'isola ( estesa o meno) vCrvToTIsland.emplace_back( pCrvIsl->Clone()) ; // <-- valida } // se c'è stata almeno una modifica di lato aperto al Chunk ( nC-esimo), devo ricreare il Chunk // con i nuovi loop ricavati ( Chunk dopo Chunk creo la superficie finale) if ( bIsChunkModified) { /* l'estensione degli aperti, può unire il loop esterno con il loop delle sue isole. Creo una nuova regione dai nuovi contorni ottenuti */ SurfFlatRegionByContours SfrBC ; // loop Esterno pCrvEL->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; SfrBC.AddCurve( Release( pCrvEL)) ; // isole non trascurate for ( int nI = 0 ; nI < int( vCrvToTIsland.size()) ; ++ nI) { vCrvToTIsland[nI]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; SfrBC.AddCurve( Release( vCrvToTIsland[nI])) ; } // ricavo il nuovo Chunk nC-esimo esteso presso i lati aperti PtrOwner pNewChunk( SfrBC.GetSurf()) ; if ( IsNull( pNewChunk) || ! pNewChunk->IsValid()) return false ; // aggiungo il Chunk nC-esimo 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( nC)) ; else if ( ! pSrfFinal->Add( *pSfr->CloneChunk( nC))) return false ; } } // se la superficie finale estesa non è valida, errore if ( ! pSrfFinal->IsValid()) return false ; // chiudo le isole che risultano ambigue ( quindi non omgenee) for ( int nC = 0 ; nC < pSrfFinal->GetChunkCount() ; ++ nC) { for ( int nL = 1 ; nL < pSrfFinal->GetLoopCount( nC) ; ++ nL) { // controllo uniformità int nCurrTmpProp = TEMP_PROP_INVALID ; int nPrecTmpProp = TEMP_PROP_INVALID ; bool bIsMixed = false ; PtrOwner pCrvIsl( ConvertCurveToComposite( pSrfFinal->GetLoop( nC, nL))) ; if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid()) return false ; for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ nU) { pCrvIsl->GetCurveTempProp( nU, nCurrTmpProp, 0) ; bIsMixed = ( nU != 0 && nCurrTmpProp != nPrecTmpProp) ; nPrecTmpProp = nCurrTmpProp ; } // se proprità non uniformi -> tutta chiusa if ( bIsMixed) { for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() ; ++ nU) pSrfFinal->SetCurveTempProp( nC, nL, nU, TEMP_PROP_CLOSE_EDGE) ; } } } // restituisco la superficie estesa pSfr->Clear() ; pSfr->CopyFrom( pSrfFinal) ; return ( pSfr->IsValid() && pSfr->GetChunkCount() > 0) ; } //---------------------------------------------------------------------------- static bool OptimizedSpiralCircle( 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) { // raggio della circonferenza esterna if ( dOutRad < 10 * EPS_SMALL) return false ; // imposto versore estrusione sulle curve composite pMCrv->SetExtrusion( vtN) ; // creo e inserisco la circonferenza esterna PtrOwner pArc( CreateCurveArc()) ; if ( IsNull( pArc) || ! pArc->Set( ptCen, vtN, dOutRad)) return false ; // cambio il punto iniziale se necessario if ( PockParams.ptStart.IsValid()) { double dUStart = 0. ; int nFlag = 0 ; DistPointCurve( PockParams.ptStart, *pArc).GetParamAtMinDistPoint( 0., dUStart, nFlag) ; pArc->ChangeStartPoint( dUStart) ; } // 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) ; // assegno proprietà di riconoscimento pMCrv->SetTempProp( TEMP_PROP_OPT_CIRCLE, 0) ; 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 ; if ( pCrvTrap->GetCurve( nBaseId)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) nBaseId += 2 ; // 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 || ! pCrvCompo->IsValid() || nBase < 0 || nBase >= pCrvCompo->GetCurveCount() || nSecondBase < 0 || nSecondBase >= pCrvCompo->GetCurveCount()) 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( CloneCurveComposite( pCrvCompo)) ; PtrOwner pCrvTest1( CloneCurveComposite( pCrvCompo)) ; 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) == TEMP_PROP_OPEN_EDGE) { int nCrvRight_ref = ( nType == 0 ? 0 : 1) ; pCrvTrap->GetCurve( nCrvRight_ref)->GetStartPoint( ptEB0) ; pCrvTrap->GetCurve( nCrvRight_ref)->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) == TEMP_PROP_OPEN_EDGE) { int nCrvLeft_ref = ( nType == 0 ? 2 : 3) ; pCrvTrap->GetCurve( nCrvLeft_ref)->GetStartPoint( ptEB1) ; pCrvTrap->GetCurve( nCrvLeft_ref)->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, TEMP_PROP_OPEN_EDGE, 0) ; // aperta } nSecondBase = pCrvTrap->GetCurveCount() ; pCrvTrap->AddLine( ptEB1) ; if ( bCopyLeft) pCrvTrap->AddCurve( Release( pCrvTest1)) ; else { pCrvTrap->Close() ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, TEMP_PROP_OPEN_EDGE, 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 IsForcedStepTrapezoid( const ICurveComposite* pCrvTrap, const PocketParams& PockParam, int nBase, int nSecondBase, bool& bForced) { bForced = false ; // se la curva non è valida, allora non può essere forzato if ( pCrvTrap == nullptr || ! pCrvTrap->IsValid()) return false ; // scorro la curva e ricavo le TempProps array vnProps ; int nClose = 0 ; for ( int i = 0 ; i < 4 ; ++ i) { if ( ! pCrvTrap->GetCurveTempProp( i, vnProps[i], 0)) return false ; if ( vnProps[i] == TEMP_PROP_CLOSE_EDGE) ++ nClose ; } double dDiam = 2. * PockParam.dRad ; switch ( nClose) { // se trapezio tutto aperto, allora non è forzato case 0 : bForced = false ; break ; // se ho un lato chiuso, non è forzato case 1 : bForced = false ; break ; // se ho due lati chiusi case 2 : { if ( nBase < 0 || nBase > 4 || nSecondBase < 0 || nSecondBase > 4) return false ; // se entrambe le basi sono chiuse, è forzato if ( vnProps[nBase] == TEMP_PROP_CLOSE_EDGE && vnProps[nSecondBase] == TEMP_PROP_CLOSE_EDGE) bForced = true ; // se entrambe le basi sono aperte else if ( vnProps[nBase] == TEMP_PROP_OPEN_EDGE && vnProps[nSecondBase] == TEMP_PROP_OPEN_EDGE) { double dLenOpen = 0. ; for ( int i = 0 ; i < 4 ; ++ i) { if ( i == nBase || i == nSecondBase) { const ICurve* pCrvOpen = pCrvTrap->GetCurve( i) ; if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid()) return false ; // essendo nei casi a trapezio, ho solo segmenti pCrvOpen->GetLength( dLenOpen) ; Vector3d vtDir ; pCrvOpen->GetStartDir( vtDir) ; vtDir *= dLenOpen ; const ICurve* pCrvClosePrev = pCrvTrap->GetCurve( ( i == 0 ? 3 : i - 1)) ; if ( pCrvClosePrev == nullptr || ! pCrvClosePrev->IsValid()) return false ; const ICurve* pCrvCloseAft = pCrvTrap->GetCurve( ( i == 3 ? 0 : i + 1)) ; if ( pCrvCloseAft == nullptr || ! pCrvCloseAft->IsValid()) return false ; // essendo un trapezio queste due direzioni me le aspetto parallele tra loro Vector3d vtDirPrev ; pCrvClosePrev->GetEndDir( vtDirPrev) ; Vector3d vtDirAft ; pCrvCloseAft->GetStartDir( vtDirAft) ; dLenOpen = min( { OrthoCompo( vtDir, vtDirPrev).Len(), OrthoCompo( vtDir, vtDirAft).Len(), dLenOpen}) ; break ; } } bForced = ( dLenOpen < dDiam + TOL_TRAPEZOID) ; } // se alternate, non forzo else bForced = false ; } break ; // se ho tre lati chiusi case 3 : { // diventa forzato se il lato aperto non ha una componente perpendicolare grande rispetto al chiuso precedente e successivo double dLenOpen = 0. ; for ( int i = 0 ; i < 4 ; ++ i) { if ( vnProps[i] == TEMP_PROP_OPEN_EDGE) { const ICurve* pCrvOpen = pCrvTrap->GetCurve( i) ; if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid()) return false ; // essendo nei casi a trapezio, ho solo segmenti pCrvOpen->GetLength( dLenOpen) ; Vector3d vtDir ; pCrvOpen->GetStartDir( vtDir) ; vtDir *= dLenOpen ; const ICurve* pCrvClosePrev = pCrvTrap->GetCurve( ( i == 0 ? 3 : i - 1)) ; if ( pCrvClosePrev == nullptr || ! pCrvClosePrev->IsValid()) return false ; const ICurve* pCrvCloseAft = pCrvTrap->GetCurve( ( i == 3 ? 0 : i + 1)) ; if ( pCrvCloseAft == nullptr || ! pCrvCloseAft->IsValid()) return false ; // essendo un trapezio queste due direzioni me le aspetto parallele tra loro Vector3d vtDirPrev ; pCrvClosePrev->GetEndDir( vtDirPrev) ; Vector3d vtDirAft ; pCrvCloseAft->GetStartDir( vtDirAft) ; dLenOpen = min( { OrthoCompo( vtDir, vtDirPrev).Len(), OrthoCompo( vtDir, vtDirAft).Len(), dLenOpen}) ; break ; } } bForced = ( dLenOpen < dDiam + TOL_TRAPEZOID) ; } break ; // se tutto chiuso, è forzato case 4 : bForced = true ; break ; default : return false ; } 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)) { // data la tolleranza, creo la curva a trapezio dai 4 punti pCrvTrap->AddPoint( pt) ; pCrvTrap->AddLine( pt + vtDir) ; pCrvTrap->AddLine( pt + vtOtherDir + vtB2) ; pCrvTrap->AddLine( pt + vtOtherDir) ; pCrvTrap->Close() ; // 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() ; // assegno le tempProp della curva trapezio for ( int nU = 0 ; nU < 4 ; ++ nU) { Vector3d vtCrvDir ; pCrvTrap->GetCurve( nU)->GetStartDir( vtCrvDir) ; for ( int nI = 0 ; nI < pCrvCompo->GetCurveCount() ; ++ nI) { Vector3d vtCurrCrvDir ; pCrvCompo->GetCurve( nI)->GetStartDir( vtCurrCrvDir) ; if ( AreSameVectorEpsilon( vtCrvDir, vtCurrCrvDir, 150 * EPS_SMALL)) { pCrvTrap->SetCurveTempProp( nU, pCrvCompo->GetCurve( nI)->GetTempProp( 0), 0) ; break ; } } } if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap)) return false ; bool bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE || pCrvTrap->GetCurve( 2)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) ; // controllo dimensioni della svuotatura if ( ! ( bBaseOpen && dPocketSize < dDiam + TOL_TRAPEZOID) && abs( dPocketSize - dDiam) > EPS_SMALL + TOL_TRAPEZOID) { pCrvTrap->Clear() ; return true ; } // recupero flag aperto/chiuso dei lati if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE && pCrvTrap->GetCurve( 3)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) { 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, TEMP_PROP_OPEN_EDGE, 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 : TEMP_PROP_OPEN_EDGE, 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 ? TEMP_PROP_CLOSE_EDGE : TEMP_PROP_OPEN_EDGE, 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 nU = 0 ; nU < pCrvCompo->GetCurveCount() && ! bOK ; ++ nU) { // cerco un lato chiuso ( esiste per forza) if ( pCrvCompo->GetCurve( nU)->GetTempProp( 0) == 0) { // controllo se il lato corrente può essere una base int nType = -1 ; if ( ! GetBoxCrvOptTrap( nU, 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( nU)->GetStartDir( vtBaseDir) ; // ricavo il suo punto iniziale Point3d ptBaseStart ; pCrvCompo->GetCurve( nU)->GetStartPoint( ptBaseStart) ; // ricavo il suo punto finale Point3d ptBaseEnd ; pCrvCompo->GetCurve( nU)->GetEndPoint( ptBaseEnd) ; // cerco il lato chiuso lineare parallelo ad esso e distante circa dPocketSize for ( int nOtherU = 0 ; nOtherU < pCrvCompo->GetCurveCount() && ! bOK ; ++ nOtherU) { if ( nOtherU == nU || pCrvCompo->GetCurve( nOtherU)->GetType() != CRV_LINE || pCrvCompo->GetCurve( nOtherU)->GetTempProp() != 0) continue ; Vector3d vtSecondBaseDir ; pCrvCompo->GetCurve( nOtherU)->GetStartDir( vtSecondBaseDir) ; if ( ! AreOppositeVectorEpsilon( vtBaseDir, vtSecondBaseDir, 5 * EPS_SMALL)) continue ; Point3d ptSecondBaseStart ; pCrvCompo->GetCurve( nOtherU)->GetStartPoint( ptSecondBaseStart) ; double dDist = 0. ; DistPointCurve DPL( ptBaseStart, *pCrvCompo->GetCurve( nOtherU), false) ; if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) { nBase = nU ; nSecondBase = nOtherU ; if ( ! PreparareTrapezoidTwoBases( pCrvCompo, dDiam, nType, nBase, nSecondBase, bOK, pCrvTrap)) return false ; } } } } } if ( ! bOK) { pCrvTrap->Clear() ; nBase = -1 ; nSecondBase = -1 ; return true ; } } // 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 bool bForced = false ; IsForcedStepTrapezoid( pCrvTrap, PockParams, nBase, nSecondBase, bForced) ; if ( ! bForced) { 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, const Point3d& ptRef) { // 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) ; PtrOwner pLineE( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 2 : 1)->Clone())) ; pLineE->SimpleOffset( - dRad) ; // verifico orientamento delle curve in base al punto di riferimento ( se valido) double dSqDistS = 0., dSqDistE = 0. ; if ( ! ptRef.IsValid() || ( ( DistPointCurve( ptRef, *pCrvPocket->GetCurve( bEvenClosed ? 3 : 0)).GetSqDist( dSqDistS)) && ( DistPointCurve( ptRef, *pCrvPocket->GetCurve( bEvenClosed ? 1 : 2)).GetSqDist( dSqDistE)) && dSqDistS < dSqDistE)) { pLineS->Invert() ; 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 == TEMP_PROP_OPEN_EDGE) { 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, - ANG_RIGHT , 2 * dPocketSize) ; else pCrvVertLine->SetPDL( Box3d.GetMin() - 5 * TOL_TRAPEZOID * Y_AX, ANG_RIGHT, 2 * dPocketSize) ; // intersezione 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)) { double dDiffY = ccClass2.IciA[0].ptI.y - dYCoord ; double dOffs = sqrt( dRad * dRad - dDiffY * dDiffY) ; if ( bStart) dXCoord = max( dXCoord, Box3d.GetMax().x + dOffs) ; else dXCoord = min( dXCoord, Box3d.GetMin().x - dOffs) ; } } } } } 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 ; if ( ! bStart) pMCrv->Invert() ; // recupero la coordinata Y del tratto lineare e la dimensione della tasca a trapezio Point3d ptTmp ; pMCrv->GetStartPoint( ptTmp) ; double dYCoord = ptTmp.y ; pCrvPocket->GetCurve( 2)->GetStartPoint( ptTmp) ; double dPocketSize = ptTmp.y ; // offset del lato chiuso del raggio utensile, verso l'interno della tasca a trapezio int nCrvId = ( bStart ? 3 : 1) ; PtrOwner pLine( GetCurveLine( pCrvPocket->GetCurve( nCrvId)->Clone())) ; pLine->SimpleOffset( - dRad) ; // inverto gli estremi se necessario Point3d ptP1, ptP2 ; pLine->GetStartPoint( ptP1) ; pLine->GetEndPoint( ptP2) ; if ( ! bStart) swap( ptP1, ptP2) ; int nProp2 ; PtrOwner pCompo( CreateCurveComposite()) ; if ( IsNull( pCompo)) return false ; // lato opposto a quello di riferimento aperto if ( pCrvPocket->GetCurveTempProp( 2, nProp2) && nProp2 != TEMP_PROP_CLOSE_EDGE) { // 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 + 100. * EPS_SMALL) { // 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) ; bool bUseBiArc = false ; PtrOwner pBiArc( GetBiArc( ptP1, - dAng, ptCrv, bStart ? 0 : ANG_STRAIGHT, 0.8)) ; 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, TEMP_PROP_OPEN_EDGE) ; pMCrv->AddCurve( Release( pCompo), false) ; // se richiesto ripristino la direzione originaria if ( ! bStart) pMCrv->Invert() ; return true ; } //---------------------------------------------------- static bool CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double dPocketSize, int nBase, int nSecondBase, const PocketParams& PockParams, ICurveComposite* pMCrv, 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) ; Point3d ptRef = P_INVALID ; if ( PockParams.ptStart.IsValid()) ptRef = GetToLoc( PockParams.ptStart, 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] != TEMP_PROP_CLOSE_EDGE ? 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 > dDiam + TOL_TRAPEZOID) && ( ( ( vnProp[0] != TEMP_PROP_CLOSE_EDGE && vnProp[2] != TEMP_PROP_CLOSE_EDGE) && ( vnProp[3] == TEMP_PROP_CLOSE_EDGE && vnProp[1] == TEMP_PROP_CLOSE_EDGE) && ( max( dLen0, dLen2) < 2. * dDiam + TOL_TRAPEZOID)) || ( ( vnProp[1] != TEMP_PROP_CLOSE_EDGE && vnProp[3] != TEMP_PROP_CLOSE_EDGE) && ( vnProp[0] == TEMP_PROP_CLOSE_EDGE && vnProp[2] == TEMP_PROP_CLOSE_EDGE) && ( max( dLen0, dLen2) < 2. * dDiam + TOL_TRAPEZOID)))) { if ( ! SpecialAdjustTrapezoidSpiralForAngles( pMCrv, ( vnProp[0] == TEMP_PROP_CLOSE_EDGE), pCrvPocket, PockParams, ptRef)) { pMCrv->Clear() ; return false ; } } else { // trovo la quota Y per centro del Tool double dYCoord ; // se base principale chiusa if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) dYCoord = 0.5 * dDiam + dOffsR ; // se base principale aperta e secondaria chiusa else if ( pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) dYCoord = dPocketSize - 0.5 * dDiam - dOffsR ; // se entrambi i lati paralleli sono aperti mi posiziono a metà della svuotatura else dYCoord = 0.5 * dPocketSize ; // determino le quote coordinate in X inziale e finale double dXCoordStart = -INFINITO ; double dXCoordEnd = INFINITO ; if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, true, dYCoord, dXCoordStart, dPocketSize, PockParams) || dXCoordStart < - INFINITO + 1) return false ; if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, false, dYCoord, dXCoordEnd, dPocketSize, PockParams) || dXCoordEnd > INFINITO - 1) return false ; if ( dXCoordStart > dXCoordEnd + 500 * EPS_SMALL) return false ; // determino se devo invertire la curva dato il punto di riferimento bool bInvertByRef = false ; if ( ptRef.IsValid()) { PtrOwner pCrvRight( pCrvPocket->CopyParamRange( nBase + 1, nSecondBase)) ; PtrOwner pCrvLeft( pCrvPocket->CopyParamRange( nSecondBase + 1, nBase)) ; if ( IsNull( pCrvRight) || IsNull( pCrvLeft) || ! pCrvRight->IsValid() || ! pCrvLeft->IsValid()) return false ; double dSqDistBase = INFINITO, dSqDistSecondBase = INFINITO, dSqDistLeft = INFINITO, dSqDistRight = INFINITO ; DistPointCurve( ptRef, *pCrvPocket->GetCurve( nBase)).GetSqDist( dSqDistBase) ; DistPointCurve( ptRef, *pCrvPocket->GetCurve( nSecondBase)).GetSqDist( dSqDistSecondBase) ; DistPointCurve( ptRef, *pCrvRight).GetSqDist( dSqDistRight) ; DistPointCurve( ptRef, *pCrvLeft).GetSqDist( dSqDistLeft) ; bInvertByRef = ( dSqDistSecondBase < min( {dSqDistBase, dSqDistLeft, dSqDistRight}) || dSqDistRight < min( {dSqDistBase, dSqDistSecondBase, dSqDistLeft})) ; } // determino gli estremi del segmento di svuotatura Point3d ptStart( dXCoordStart, dYCoord) ; Point3d ptEnd( dXCoordEnd, dYCoord) ; // verifico se il punto iniziale e finale coincidono if ( bRealTrap && AreSamePointEpsilon( ptStart, ptEnd, 500 * EPS_SMALL) && ( vnProp[0] != TEMP_PROP_CLOSE_EDGE || vnProp[2] != TEMP_PROP_CLOSE_EDGE)) { 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] != TEMP_PROP_CLOSE_EDGE) { pMCrv->AddPoint( ptS) ; if ( vnProp[2] != TEMP_PROP_CLOSE_EDGE) pMCrv->AddLine( ptE) ; else pMCrv->AddLine( ptStart) ; } else { pMCrv->AddPoint( ptE) ; if ( vnProp[0] != TEMP_PROP_CLOSE_EDGE) pMCrv->AddLine( ptS) ; else pMCrv->AddLine( ptStart) ; pMCrv->Invert() ; } pMCrv->SetCurveTempProp( 0, TEMP_PROP_OPEN_EDGE) ; // verifico se invertirla nel caso di un punto di riferimento if ( bInvertByRef && vnProp[0] != TEMP_PROP_CLOSE_EDGE && vnProp[2] != TEMP_PROP_CLOSE_EDGE) pMCrv->Invert() ; } } // se estremi del segmento differenti, allora il percorso è definito else { // creo la linea 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) == TEMP_PROP_OPEN_EDGE || pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) { Frame3d frOpen ; bool bSwitch = false ; // Base Principale chiusa e Base Secondaria aperta o inversione per punto di riferimento if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE || bInvertByRef) { pCrvPocket->ChangeStartPoint( nSecondBase) ; // oriento il Frame ( ho sempre l'origine nell'estremo superiore del lato aperto) Vector3d vtDir ; pCrvPocket->GetStartDir( vtDir) ; Point3d ptOpen ; if ( vtDir.y > 0) pCrvPocket->GetFirstCurve()->GetEndPoint( ptOpen) ; else pCrvPocket->GetFirstCurve()->GetStartPoint( ptOpen) ; frOpen.Set( ptOpen, Z_AX, -X_AX) ; if ( ! frOpen.IsValid()) return false ; pCrvPocket->ToLoc( frOpen) ; pMCrv->ToLoc( frOpen) ; pMCrv->Invert() ; bSwitch = true ; } // la Base principale è sempre aperta if ( pCrvPocket->GetCurve( 3)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE && ! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, true)) { pMCrv->Clear() ; return false ; } if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE && ! 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) ; // 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 AdjustTrapeziodLeadIn( ICurveComposite* pCrvRes, const PocketParams& PockParam, const ISurfFlatRegion* pSfrChunk) { // recupero la TempProp int nTmpProp = pCrvRes->GetTempProp( 0) ; // se esiste almeno un aperto if ( nTmpProp > 0) { // se solo lato3 aperto if ( nTmpProp == 2) pCrvRes->Invert() ; // entro dall'unico aperto bool bCheckHead = ( nTmpProp != 8 && nTmpProp != 2) ; if ( bCheckHead) { // recupero gli estremi della curva corrente e la inverto in base alla Testa Point3d ptS ; pCrvRes->GetStartPoint( ptS) ; Point3d ptE ; pCrvRes->GetEndPoint( ptE) ; Point3d ptSGlob = GetToGlob( ptS, PockParam.frLocXY) ; Point3d ptEGlob = GetToGlob( ptE, PockParam.frLocXY) ; if ( ( PockParam.bAboveHead && ptEGlob.z > ptSGlob.z) || ( ! PockParam.bAboveHead && ptEGlob.z < ptSGlob.z)) pCrvRes->Invert() ; } } else if ( ! PockParam.bConventionalMilling) { // per sgrossature Point3d ptS ; pCrvRes->GetStartPoint( ptS) ; Point3d ptE ; pCrvRes->GetEndPoint( ptE) ; if ( SqDist( ptE, PockParam.ptEnd) < SqDist( ptE, PockParam.ptEnd) + EPS_SMALL) pCrvRes->Invert() ; } // Assegno la Feed AssignFeedSpiralOpt( 1, PockParam, pCrvRes) ; // Se curva da invertire, inverto if ( PockParam.bInvert) pCrvRes->Invert() ; // se esiste almeno un aperto, provo ad estendere il percorso if ( nTmpProp > 0) { // Calcolo eventuale entrata da fuori Vector3d vtRef ; pCrvRes->GetStartDir( vtRef) ; vtRef.Invert() ; bool bIsStartExtended = false ; if ( ! ExtendPath( pCrvRes, pSfrChunk, PockParam, vtRef, false, GetExtendPathLen( PockParam), bIsStartExtended)) return false ; } return true ; } //---------------------------------------------------------------------------- static bool GetZigZagOptimizedCurves( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParam, ICurveComposite* pCrvRes) { // controllo dei parametri if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid()) return false ; pCrvRes->Clear() ; // ricavo la curva di bordo del chunk corrente PtrOwner pCrvBorder( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ; if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid()) return false ; pCrvBorder->MergeCurves( 10. * EPS_SMALL, 10. * EPS_ANG_SMALL, true, true) ; pCrvBorder->SetExtrusion( pSfrChunk->GetNormVersor()) ; /* TRAPEZI - E' richiesto che una dimensione del box della curva sia compatibile con il primo Offset, il quale sarebbe una singola curva aperta */ PtrOwner pCrvTrap( CreateCurveComposite()) ; if ( IsNull( pCrvTrap)) return false ; Frame3d frTrap ; double dPocketSize ; int nBase, nSecondBase ; bool bOkTrap = GetTrapezoidFromShape( pCrvBorder, pCrvTrap, frTrap, PockParam, dPocketSize, nBase, nSecondBase) ; if ( bOkTrap && pCrvTrap->IsValid()) { // verifico se il trapezio ottenuto deve o meno rispettare il SideStep bool bForcedTrap = false ; IsForcedStepTrapezoid( pCrvTrap, PockParam, nBase, nSecondBase, bForcedTrap) ; if ( ! bForcedTrap) bOkTrap = ( dPocketSize < PockParam.dMaxOptSize + 10. * EPS_SMALL) ; } if ( bOkTrap && pCrvTrap->IsValid()) { pCrvTrap->SetExtrusion( Z_AX) ; CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParam, pCrvRes, bOkTrap) ; if ( bOkTrap) { // verifico che tale curva non interferisca con la regione limite if ( PockParam.SfrLimit.IsValid()) { double dOffsCheck = PockParam.dRad + PockParam.dRadialOffset - 50. * EPS_SMALL ; // restrittivo per sicurezza PtrOwner pSfrToolShape( GetSurfFlatRegionFromFatCurve( pCrvRes->Clone(), dOffsCheck, false, false)) ; bOkTrap = ( ! IsNull( pSfrToolShape) && pSfrToolShape->IsValid()) ; if ( bOkTrap) { bOkTrap = ( pSfrToolShape->Intersect( PockParam.SfrLimit) && ! pSfrToolShape->IsValid()) ; if ( ! bOkTrap) pCrvRes->Clear() ; } } if ( bOkTrap) { AdjustTrapeziodLeadIn( pCrvRes, PockParam, pSfrChunk) ; // imposto il flag di curva singola pCrvRes->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ; } } } return true ; } //---------------------------------------------------------------------------- static bool GetSpiralOptimizedCurves( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParam, ICurveComposite* pCrvRes) { // controllo dei parametri if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid()) return false ; pCrvRes->Clear() ; // ricavo la curva di bordo del chunk corrente PtrOwner pCrvBorder( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ; if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid()) return false ; pCrvBorder->MergeCurves( 10. * EPS_SMALL, 10. * EPS_ANG_SMALL, true, true) ; pCrvBorder->SetExtrusion( pSfrChunk->GetNormVersor()) ; /* SPIRALE EUCLIDEA - E' richiesto che la curva di bordo sia una circonferenza tutta OPEN o tutta CLOSED */ bool bOkSpiral = true ; int nTempPropRef = pCrvBorder->GetCurve( 0)->GetTempProp( 0) ; for ( int nU = 1 ; nU < pCrvBorder->GetCurveCount() && bOkSpiral ; ++ nU) bOkSpiral = ( pCrvBorder->GetCurve( nU)->GetTempProp( 0) == nTempPropRef) ; if ( bOkSpiral) { // controllo che sia una circonferenza Point3d ptCen ; double dRad ; if ( ! OptimizedSpiralCircle( pCrvBorder, 50. * EPS_SMALL, dRad, ptCen, bOkSpiral)) return false ; // se è una circonferenza if ( bOkSpiral) { double dOffs = PockParam.dRad + PockParam.dRadialOffset ; // se curva tutta Open if ( nTempPropRef == TEMP_PROP_OPEN_EDGE) { // ingrandisco il raggio dRad += 1.05 * PockParam.dRad + PockParam.dRadialOffset ; // verifico che l'utensile ora non interferisca con la regione limite if ( PockParam.SfrLimit.IsValid()) { // definisco il nuovo bordo PtrOwner pCrvArc( CreateCurveArc()) ; bOkSpiral = ( ! IsNull( pCrvArc) && pCrvArc->Set( ptCen, Z_AX, dRad + PockParam.dRad)) ; if ( bOkSpiral) { CRVCVECTOR ccClass ; bOkSpiral = ( PockParam.SfrLimit.GetCurveClassification( *pCrvArc, EPS_SMALL, ccClass) && ssize( ccClass) == 1 && ccClass[0].nClass == CRVC_OUT) ; // NB. una versione più complessa dovrebbe verificare se la sottrazione tra la // superficie dell'utensile e la regione limite non genera un'altra circonferenza... // In questo caso la sottrazione potrebbe essere trattata come una circonferenza // chiusa ed essere ancora svotata a spirale... } } } // se curva chiusa, controllo che il raggio sia compatibile con il primo Offset else bOkSpiral = ( dRad - dOffs > 10. * EPS_SMALL) ; if ( bOkSpiral) { double dIntRad = 0 ; if ( PockParam.nType == POCKET_SPIRALOUT && PockParam.nLiType == LEAD_IN_HELIX) dIntRad = min( 0.5 * min( PockParam.dLiTang, 2. * PockParam.dRad), dRad - dOffs) ; bool bOkSpiral = CalcCircleSpiral( ptCen, pSfrChunk->GetNormVersor(), dRad - dOffs, dIntRad, PockParam, pCrvRes) ; if ( bOkSpiral && pCrvRes->IsValid() && pCrvRes->GetCurveCount() > 0) { // se curva di bordo OPEN e lavorazione SpiralIn, imposto i parametri per LeadIn if ( nTempPropRef == TEMP_PROP_OPEN_EDGE && PockParam.nType == POCKET_SPIRALIN) { Vector3d vtMidOut ; pCrvRes->GetStartDir( vtMidOut) ; vtMidOut.Rotate( pSfrChunk->GetNormVersor(), PockParam.bInvert ? ANG_RIGHT : - ANG_RIGHT) ; bool bIsExtended ; ExtendPath( pCrvRes, pSfrChunk, PockParam, vtMidOut, false, GetExtendPathLen( PockParam), bIsExtended) ; } // se invece lavorazione in SpiralOut, inverto il percorso else if ( PockParam.nType == POCKET_SPIRALOUT) pCrvRes->Invert() ; return true ; } } } } /* TRAPEZI - E' richiesto che una dimensione del box della curva sia compatibile con il primo Offset, il quale sarebbe una singola curva aperta */ PtrOwner pCrvTrap( CreateCurveComposite()) ; if ( IsNull( pCrvTrap)) return false ; Frame3d frTrap ; double dPocketSize ; int nBase, nSecondBase ; bool bOkTrap = GetTrapezoidFromShape( pCrvBorder, pCrvTrap, frTrap, PockParam, dPocketSize, nBase, nSecondBase) ; if ( bOkTrap && pCrvTrap->IsValid()) { // verifico se il trapezio ottenuto deve o meno rispettare il SideStep bool bForcedTrap = false ; IsForcedStepTrapezoid( pCrvTrap, PockParam, nBase, nSecondBase, bForcedTrap) ; if ( ! bForcedTrap) bOkTrap = ( dPocketSize < PockParam.dMaxOptSize + 10. * EPS_SMALL) ; } if ( bOkTrap && pCrvTrap->IsValid()) { pCrvTrap->SetExtrusion( Z_AX) ; CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParam, pCrvRes, bOkTrap) ; if ( bOkTrap) { // verifico che tale curva non interferisca con la regione limite if ( PockParam.SfrLimit.IsValid()) { double dOffsCheck = PockParam.dRad + PockParam.dRadialOffset - 50. * EPS_SMALL ; // restrittivo per sicurezza PtrOwner pSfrToolShape( GetSurfFlatRegionFromFatCurve( pCrvRes->Clone(), dOffsCheck, false, false)) ; bOkTrap = ( ! IsNull( pSfrToolShape) && pSfrToolShape->IsValid()) ; if ( bOkTrap) { bOkTrap = ( pSfrToolShape->Intersect( PockParam.SfrLimit) && ! pSfrToolShape->IsValid()) ; if ( ! bOkTrap) pCrvRes->Clear() ; } } if ( bOkTrap) { AdjustTrapeziodLeadIn( pCrvRes, PockParam, pSfrChunk) ; // imposto il flag di curva singola pCrvRes->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ; } } } return true ; } //---------------------------------------------------------------------------- static bool GetPocketingOptimizedCurves( ISurfFlatRegion* pSfr, const PocketParams& PockParam, ICRVCOMPOPOVECTOR& vCrvOptCurves) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid()) return false ; vCrvOptCurves.clear() ; // se non sono richiesti i casi ottimizzati esco if ( PockParam.bAvoidOpt) return true ; // scorro i Chunk della superficie int nChunks = pSfr->GetChunkCount() ; int nCurrChunk = 0 ; for ( int i = 0 ; i < nChunks ; ++ i) { // recupero il Chunk corrente PtrOwner pSfrChunk( pSfr->CloneChunk( nCurrChunk)) ; if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid()) return false ; // se il Chunk ha isole, non ho casi ottimizzati if ( pSfrChunk->GetLoopCount( 0) > 1) { ++ nCurrChunk ; continue ; } // ricavo le curve ottimizzate a seconda della lavorazione richiesta if ( PockParam.nType == POCKET_SPIRALIN || PockParam.nType == POCKET_SPIRALOUT || PockParam.nType == POCKET_CONFORMAL_ZIGZAG || PockParam.nType == POCKET_CONFORMAL_ONEWAY) { // curva da resituire PtrOwner pCrvOptSpiral( CreateCurveComposite()) ; if ( IsNull( pCrvOptSpiral) || ! GetSpiralOptimizedCurves( pSfrChunk, PockParam, pCrvOptSpiral)) return false ; // se ho ricavato una curva ottimizzata if ( ! IsNull( pCrvOptSpiral) && pCrvOptSpiral->IsValid() && pCrvOptSpiral->GetCurveCount() > 0) { vCrvOptCurves.emplace_back( Release( pCrvOptSpiral)) ; pSfr->EraseChunk( nCurrChunk) ; } else ++ nCurrChunk ; } else if ( PockParam.nType == POCKET_ZIGZAG || PockParam.nType == POCKET_ONEWAY) { // curva da restituire PtrOwner pCrvOptZigZag( CreateCurveComposite()) ; if ( IsNull( pCrvOptZigZag) || ! GetZigZagOptimizedCurves( pSfrChunk, PockParam, pCrvOptZigZag)) return false ; // se ho ricavato una curva ottimizzata if ( ! IsNull( pCrvOptZigZag) && pCrvOptZigZag->IsValid() && pCrvOptZigZag->GetCurveCount() > 0) { vCrvOptCurves.emplace_back( Release( pCrvOptZigZag)) ; pSfr->EraseChunk( nCurrChunk) ; } else ++ nCurrChunk ; } } return true ; } // *************************************************************************** // ------------- SCELTA DEL PUNTO INIZIALE ----------------------------------- // *************************************************************************** //---------------------------------------------------------------------------- static bool GetParamOnOpenCurve( const ICurveComposite* pCompo, const PocketParams& PockParams, const Point3d& ptEndPrec, bool& bOutStart, Point3d& ptStart, Vector3d& vtOut) { // controllo dei parametri if ( pCompo == nullptr || ! pCompo->IsValid()) return false ; // verifico se tutti i lati sono aperti bool bAllOpen = true ; const ICurve* pMyCrv = pCompo->GetFirstCurve() ; while ( pMyCrv != nullptr) { if ( pMyCrv->GetTempProp() != TEMP_PROP_OPEN_EDGE) { bAllOpen = false ; break ; } pMyCrv = pCompo->GetNextCurve() ; } // se ho un punto di riferimento, cambio il punto iniziale della curva, partendo da questa PtrOwner pMyCompo( CloneCurveComposite( pCompo)) ; if ( IsNull( pMyCompo) || ! pMyCompo->IsValid()) return false ; if ( ptEndPrec.IsValid()) { double dMinDistPar = 0. ; int nFlag = 0 ; DistPointCurve( ptEndPrec, *pMyCompo).GetParamAtMinDistPoint( 0., dMinDistPar, nFlag) ; if ( dMinDistPar > EPS_PARAM) pMyCompo->ChangeStartPoint( floor( dMinDistPar)) ; } // 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, bFirst = true ; const ICurve* pPrevCrv = pMyCompo->GetLastCurve() ; double dLenPrev = 0. ; if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) pPrevCrv->GetLength( dLenPrev) ; const ICurve* pCrv = pMyCompo->GetFirstCurve() ; while ( pCrv != nullptr) { // analizzo la curva successiva const ICurve* pNextCrv = pMyCompo->GetNextCurve() ; bool bNextOk = ( pNextCrv != nullptr) ; if ( ! bNextOk) pNextCrv = pMyCompo->GetFirstCurve() ; double dLenNext = 0. ; if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) pNextCrv->GetLength( dLenNext) ; // verifico la curva corrente if ( pCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) { // contributo dalle entità adiacenti ( se non tutte aperte) double dLenAgg = 0. ; if ( ! bAllOpen) { if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) { Vector3d vtPrevEnd ; pPrevCrv->GetEndDir( vtPrevEnd) ; Vector3d vtStart ; pCrv->GetStartDir( vtStart) ; dLenAgg += max( 0.4, vtPrevEnd * vtStart) * dLenPrev ; } if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) { Vector3d vtEnd ; pCrv->GetEndDir( vtEnd) ; Vector3d vtNextStart ; pNextCrv->GetStartDir( vtNextStart) ; dLenAgg += max( 0.4, vtEnd * vtNextStart) * dLenNext ; } } if ( ptEndPrec.IsValid() && bFirst) dLenAgg += 2. * ( dLenPrev + 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 > ptStart.z + 100 * EPS_SMALL) || ( ! PockParams.bAboveHead && ptTest.z < ptStart.z - 100 * EPS_SMALL) || ( abs( ptTest.z - ptStart.z) < 100 * EPS_SMALL && ptTest.y < ptStart.y - 100 * EPS_SMALL)) { dMaxLen = max( dMaxLen, dLen + dLenAgg) ; ptStart = ptTest ; bOutStart = true ; // versore ortogonale verso l'esterno pCrv->GetMidDir( vtOut) ; vtOut.Rotate( Z_AX, 0, -1) ; } } // se più lunga ( o non già trovata) else if ( dLen + dLenAgg > dMaxLen || ! bFound) { dMaxLen = dLen + dLenAgg ; if ( pCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart, &vtOut)) { // versore ortogonale verso l'esterno vtOut.Normalize() ; vtOut.Rotate( Z_AX, 0, -1) ; bOutStart = true ; bFound = true ; } } dLenPrev = dLen ; } } else dLenPrev = 0 ; // vado alla successiva pPrevCrv = pCrv ; pCrv = ( bNextOk ? pNextCrv : nullptr) ; bFirst = false ; } return bFound ; } //---------------------------------------------------------------------------- static bool GetPtStartOnOpenEdgeByOrigCurve( const Point3d& ptRef, const ICurveComposite* pCrvOrig, const PocketParams& PockParams, const ICurveComposite* pCrvCompo, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut) { // controllo dei parametri if ( pCrvOrig == nullptr || ! pCrvOrig->IsValid() || pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; bMidOut = false ; double dLenRef = ( 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) - 50 * EPS_SMALL ; // se ho un punto di riferimento, cerco il lato aperto sull'originale più vicino ad essa ( sufficientemente lungo) if ( ptRef.IsValid()) { double dSqMinDist = INFINITO ; int nIndCrv = -1 ; for ( int i = 0 ; i < pCrvOrig->GetCurveCount() ; ++ i) { // escludo le sottocurve chiuse if ( pCrvOrig->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) continue ; // recupero la curva i-esima aperta dalla curva originale const ICurve* pCrvOpen = pCrvOrig->GetCurve( i) ; if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid()) return false ; // ricavo la lunghezza di tale curva double dLen = 0. ; pCrvOpen->GetLength( dLen) ; // se lunghezza accettabile o maggiore della massima trovata if ( dLen > dLenRef - 10. * EPS_SMALL) { // verifico la distanza con il punto di riferimento double dCurrSqDist = 0. ; if ( DistPointCurve( ptRef, *pCrvOpen).GetSqDist( dCurrSqDist) && dCurrSqDist < dSqMinDist) { dSqMinDist = dCurrSqDist ; nIndCrv = i ; } } } // se ho un indice di curva valido, allora ricavo i valori if ( nIndCrv != -1) { // recupero la curva const ICurve* pCrvOpen = pCrvOrig->GetCurve( nIndCrv) ; if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid()) return false ; Point3d ptSTmp ; Vector3d vtMidOutTmp ; // ricavo il punto medio e il vettore tangente ad esso associato if ( pCrvOpen->GetPointD1D2( 0.5, ICurve::FROM_MINUS, ptSTmp, &vtMidOutTmp)) { // versore d'uscita vtMidOutTmp.Normalize() ; vtMidOutTmp.Rotate( Z_AX, 0, -1) ; ptSTmp += vtMidOutTmp * max( PockParams.dRad - PockParams.dSideStep, 0.) ; // cerco la sottocurva più vicina a ptSTmp int nFlag ; double dMyPar ; if ( DistPointCurve( ptSTmp, *pCrvCompo).GetParamAtMinDistPoint( 0, dMyPar, nFlag)) { int nCrv = int( floor( dMyPar)) ; // recupero tale sottocurva e controllo che anche essa sia OPEN const ICurve* pMyCrv = pCrvCompo->GetCurve( nCrv) ; if ( pMyCrv != nullptr && pMyCrv->IsValid() && pMyCrv->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) { // recupero ptStart pMyCrv->GetMidPoint( ptStart) ; // assegno direzione fuori vtMidOut = vtMidOutTmp ; // flag per possibile entrata da fuori bMidOut = true ; } } } // esco return true ; } } // se non ho un punto di riferimento valido, o avendolo non si riesce ad entrare dal lato aperto più vicino // cerco il lato aperto più lungo ( sufficientemente lungo) for ( int i = 0 ; i < pCrvOrig->GetCurveCount() ; ++ i) { // escludo le sottocurve chiuse if ( pCrvOrig->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) continue ; // recupero la curva i-esima aperta dalla curva originale const ICurve* pCrvOpen = pCrvOrig->GetCurve( i) ; if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid()) return false ; // ricavo la lunghezza di tale curva double dLen = 0. ; pCrvOpen->GetLength( dLen) ; // se lunghezza accettabile o maggiore della massima trovata if ( dLen > dLenRef - 10. * EPS_SMALL) { Point3d ptSTmp ; Vector3d vtMidOutTmp ; // ricavo il punto medio e il vettore tangente ad esso associato if ( pCrvOpen->GetPointD1D2( 0.5, ICurve::FROM_MINUS, ptSTmp, &vtMidOutTmp)) { // versore d'uscita vtMidOutTmp.Normalize() ; vtMidOutTmp.Rotate( Z_AX, 0, -1) ; ptSTmp += vtMidOutTmp * max( PockParams.dRad - PockParams.dSideStep, 0.) ; // cerco la sottocurva più vicina a ptSTmp int nFlag ; double dMyPar ; if ( DistPointCurve( ptSTmp, *pCrvCompo).GetParamAtMinDistPoint( 0, dMyPar, nFlag)) { int nCrv = int( floor( dMyPar)) ; // recupero tale sottocurva e controllo che anche essa sia OPEN const ICurve* pMyCrv = pCrvCompo->GetCurve( nCrv) ; if ( pMyCrv != nullptr && pMyCrv->IsValid() && pMyCrv->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) { // recupero ptStart pMyCrv->GetMidPoint( ptStart) ; // assegno direzione fuori vtMidOut = vtMidOutTmp ; // flag per possibile entrata da fuori bMidOut = true ; // aggiornamento della lunghezza di riferimento dLenRef = dLen ; } } } } } return true ; } //---------------------------------------------------------------------------- static bool GetPtStartOnOpenEdgeByPtRef( const Point3d& ptRef, const PocketParams& PockParams, const ICurveComposite* pCrvCompo, const ICurveComposite* pCrvOrig, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut) { // controllo dei parametri if ( ! ptRef.IsValid() || pCrvCompo == nullptr || ! pCrvCompo->IsValid() || ! ptRef.IsValid()) return false ; bMidOut = false ; // lunghezza minima di riferimento per un lato aperto valido double dLenRef = ( 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) - 50 * EPS_SMALL ; // cerco la curva OPEN a minima distanza da ptRef, sufficientemente lunga int nStartCrv = -1 ; double dMinDist = INFINITO ; for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) { // considero solo le sottocurve OPEN if ( pCrvCompo->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) { // sufficientemente lunghe double dLen ; if ( pCrvCompo->GetCurve( i)->GetLength( dLen) && dLen > dLenRef) { DistPointCurve DPC( ptRef, *pCrvCompo->GetCurve( i)) ; double dCurrDist = INFINITO ; // sufficiententemente vicine a ptRef if ( DPC.GetSqDist( dCurrDist) && dCurrDist < dMinDist) { dMinDist = dCurrDist ; nStartCrv = i ; } } } } // se non ho trovato nulla, allora cerco un punto iniziale valido su una sottocurva OPEN if ( nStartCrv == -1 && pCrvOrig != nullptr && pCrvOrig->IsValid()) { return GetPtStartOnOpenEdgeByOrigCurve( ptRef, pCrvOrig, PockParams, pCrvCompo, ptStart, vtMidOut, bMidOut) ; } // se ho trovato la curva più vicina, cerco un punto iniziale valido else { const ICurve* pMyCrv = pCrvCompo->GetCurve( nStartCrv) ; if ( pMyCrv != nullptr && pMyCrv->IsValid()) { // ricavo ptStart e vettore tangente if ( pMyCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart, &vtMidOut)) { // versore ortogonale verso l'esterno vtMidOut.Normalize() ; vtMidOut.Rotate( Z_AX, 0, -1) ; // flag per possibile entrata da fuori bMidOut = true ; } } } return true ; } //---------------------------------------------------------------------------- static bool GetPtStartOnHomogeousEdges( const ICurveComposite* pCrvCompo, const PocketParams& PockParams, int nType, const Point3d& ptRef, bool& bMidOut, Point3d& ptStart, Vector3d& vtMidOut) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; bMidOut = false ; // analizzo i sottotratti omogenei ICRVCOMPOPOVECTOR vpCrvs ; PtrOwner pCrvCompoClone( CloneCurveComposite( pCrvCompo)) ; if ( IsNull( pCrvCompoClone) || ! pCrvCompoClone->IsValid()) return false ; GetHomogeneousParts( pCrvCompoClone, PockParams, vpCrvs) ; // se ho un solo tratto omogeneo e coerente al tipo scelto ... if ( int( vpCrvs.size()) == 1 && vpCrvs[0]->GetTempProp( 0) == nType) { // ... e non ho un punto di riferimento valido if ( ! ptRef.IsValid()) { // il punto iniziale è già definito pCrvCompoClone->GetStartPoint( ptStart) ; } // ho un punto di riferimento valido else { // cerco il punto più vicino DistPointCurve DistPtCrv( ptRef, *pCrvCompoClone) ; int nFlag ; if ( ! DistPtCrv.GetMinDistPoint( 0, ptStart, nFlag)) return false ; } bMidOut = ( nType == TEMP_PROP_OPEN_EDGE) ; return true ; } // scorro i tratti omogenei e cerco il tratto del tipo definito più lungo int nMaxInd = -1 ; double dMaxLen = 0. ; for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { // considero solo i tratti aperti if ( vpCrvs[i]->GetTempProp( 0) == nType) { // calcolo la lunghezza del tratto aperto double dLen ; vpCrvs[i]->GetLength( dLen) ; if ( dLen > dMaxLen) { dMaxLen = dLen ; nMaxInd = i ; } } } // recupero il tratto del tipo definito più lungo if ( nMaxInd != -1) { // recupero il parametro alla sua metà double dParIn ; vpCrvs[nMaxInd]->GetParamAtLength( 0.5 * dMaxLen, dParIn) ; // se tratto OPEN if ( nType == TEMP_PROP_OPEN_EDGE) { bMidOut = true ; vpCrvs[nMaxInd]->GetPointD1D2( dParIn, ICurve::FROM_MINUS, ptStart, &vtMidOut) ; vtMidOut.Normalize() ; vtMidOut.Rotate( Z_AX, 0, -1) ; } // se tratto CLOSED else vpCrvs[nMaxInd]->GetPointD1D2( dParIn, ICurve::FROM_MINUS, ptStart) ; } return true ; } //---------------------------------------------------------------------------- static bool GetPtStartOnGenericEdge( const ICurveComposite* pCrvCompo, const PocketParams& PockParams, Point3d& ptStart) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; pCrvCompo->GetStartPoint( ptStart) ; // scorro tutte le curve double dLenRef = 0. ; bool bAllShort = true ; // flag per curve tutte piccole for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) { // ricavo la curva i-esima dalla curva originale const ICurve* pMyCrv = pCrvCompo->GetCurve( i) ; if ( pMyCrv == nullptr || ! pMyCrv->IsValid()) return false ; // ricavo la lunghezza di tale curva double dLen = 0. ; pMyCrv->GetLength( dLen) ; // se lunghezza accettabile o maggiore della massima trovata, ricavo ptStart if ( dLen > dLenRef) { if ( pMyCrv->GetMidPoint( ptStart)) { dLenRef = dLen ; bAllShort = false ; } } } // se curve tutte piccole if ( bAllShort) { bool bMidOutFake ; Vector3d vtMidOutFake ; if ( ! GetPtStartOnHomogeousEdges( pCrvCompo, PockParams, TEMP_PROP_CLOSE_EDGE, P_INVALID, bMidOutFake, ptStart, vtMidOutFake)) return false ; } return true ; } //---------------------------------------------------------------------------- static bool GetPtStartOnGenericEdgeByPtRef( const ICurveComposite* pCrvCompo, const PocketParams& PockParams, const Point3d& ptRef, Point3d& ptStart) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 || ! ptRef.IsValid()) return false ; // ordino le curve di pCrvCompo in base alla vicinanza da ptRef // vector> vCrvInfoDist ; vCrvInfoDist.resize( pCrvCompo->GetCurveCount()) ; // scorro le curve di pCrvCompo for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) { vCrvInfoDist[i].first = i ; // calcolo la distanza tra la curva i-esima e ptRef double dDist = INFINITO ; DistPointCurve DistPtCrv( ptRef, *pCrvCompo->GetCurve( i)) ; DistPtCrv.GetSqDist( dDist) ; vCrvInfoDist[i].second = dDist ; } // ordino il vettore in base alla distanza sort( vCrvInfoDist.begin(), vCrvInfoDist.end(), [] ( const pair& a, const pair& b) { return a.second < b.second ; }) ; // cerco un parametro iniziale valido per le sottocurve ordinate double dLenRef = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ; for ( int i = 0 ; i < int( vCrvInfoDist.size()) ; ++ i) { // recupero la curva i-esima const ICurve* pMyCrv = pCrvCompo->GetCurve( vCrvInfoDist[i].first) ; if ( pMyCrv != nullptr && pMyCrv->IsValid()) { // controllo che sia abbastanza lunga e aggiorno ptStart double dLen = 0. ; pMyCrv->GetLength( dLen) ; if ( dLen > dLenRef) { if ( pMyCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart)) return true ; } } } // alla peggio... bool bMidOutFake ; Vector3d vtMidOutFake ; return GetPtStartOnHomogeousEdges( pCrvCompo, PockParams, TEMP_PROP_CLOSE_EDGE, ptRef, bMidOutFake, ptStart, vtMidOutFake) ; } //---------------------------------------------------------------------------- static bool AssignOpenCloseTmpPropToFirstOffsCurve( ICurveComposite* pCrv, const PocketParams& PockParams, const ISurfFlatRegion* pSfrToWork, bool& bSomeOpen) { // controllo dei parametri if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0 || pSfrToWork == nullptr || ! pSfrToWork->IsValid() || pSfrToWork->GetChunkCount() != 1) 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 = 0 ; pCrv->GetCurveTempProp( i, nProp0, 0) ; // nProp1 -> #loop che contiene la curva espressa in nProp0 int nProp1 = 0 ; pCrv->GetCurveTempProp( i, nProp1, 1) ; // se questa curva non è un "raccordo" di Offset if ( nProp0 > 0) { // aggiorno la proprietà della curva da cui devo entrare int nTempProp = TEMP_PROP_CLOSE_EDGE ; pSfrToWork->GetCurveTempProp( 0, nProp1, nProp0 - 1, nTempProp, 0) ; pCrv->SetCurveTempProp( i, nTempProp, 0) ; // se la curva è aperta, aggiorno il Flag if ( nTempProp == TEMP_PROP_OPEN_EDGE && ! bSomeOpen) bSomeOpen = true ; // salvo l'indice della prima curva trovata che non è un "raccordo" di Offset if ( nInd == -1) nInd = i ; } // se questa curva è un "raccordo" di Offset else { pCrv->SetCurveTempProp( i, TEMP_PROP_SMOOTH, 0) ; pCrv->SetCurveTempProp( i, TEMP_PROP_SMOOTH, 1) ; } } // cambio il punto iniziale all'inizio della prima curva che non deriva da un "raccordo" Point3d ptStart ; if ( nInd != -1) { if ( ! pCrv->GetStartPoint( ptStart)) return false ; pCrv->ChangeStartPoint( nInd) ; // la prima curva non è un "raccordo" di Offset } // scorro tutte le curve successive alla prima for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++ i) { int nTmpProp0, nTmpProp1 ; // se "raccordo" copio la tempProp precedente if (( pCrv->GetCurveTempProp( i, nTmpProp0, 0) && nTmpProp0 == TEMP_PROP_SMOOTH) && ( pCrv->GetCurveTempProp( i, nTmpProp1, 1) && nTmpProp1 == TEMP_PROP_SMOOTH)) // copio la temp prop della curva precedente pCrv->SetCurveTempProp( i, pCrv->GetCurve( i-1)->GetTempProp( 0), 0) ; } // lascio invariato il punto iniziale della curva originaria if ( nInd != -1) { double dUNewS ; pCrv->GetParamAtPoint( ptStart, dUNewS) ; pCrv->ChangeStartPoint( dUNewS) ; } return true ; } //---------------------------------------------------------------------------- static bool SetPtStartForPath( ICurveComposite* pCrvOffs, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork, const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut, const ICurveComposite* pCrvOrig) { /* 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 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. */ // controllo dei parametri if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0 || pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1) return false ; // assegno proprietà di OPEN/CLOSE alle sottocurve dell'offset corrente bool bSomeOpen = false ; if ( ! AssignOpenCloseTmpPropToFirstOffsCurve( pCrvOffs, PockParams, pSrfToWork, bSomeOpen)) return false ; // punto valido per l'entrata ... bMidOut = false ; if ( bSomeOpen) { // se esistono lati aperti... if ( pCrvOrig != nullptr && pCrvOrig->IsValid()) { // ... e non ho un punto di riferimento, lo cerco sulla curva originale if ( ! ptEndPrec.IsValid()) { if ( ! GetPtStartOnOpenEdgeByOrigCurve( P_INVALID, pCrvOrig, PockParams, pCrvOffs, ptStart, vtMidOut, bMidOut)) return false ; } // ... e ho un punto di riferimento else { if ( ! GetPtStartOnOpenEdgeByPtRef( ptEndPrec, PockParams, pCrvOffs, pCrvOrig, ptStart, vtMidOut, bMidOut)) return false ; } } // se punto non trovato precedentemente, tento con una sottocurva OPEN generica if ( ! bMidOut) { if ( ! GetParamOnOpenCurve( pCrvOffs, PockParams, ptEndPrec, bMidOut, ptStart, vtMidOut)) return false ; } // se queste curve sono troppo corte, analizzo le parti uniformi if ( ! bMidOut) { if ( ! GetPtStartOnHomogeousEdges( pCrvOffs, PockParams, TEMP_PROP_OPEN_EDGE, ptEndPrec, bMidOut, ptStart, vtMidOut)) return false ; } } // se sottocurve tutte CLOSED o un punto iniziale non valido, lo cerco su un lato generico if ( ! bMidOut) { if ( ! ptEndPrec.IsValid()) { if ( ! GetPtStartOnGenericEdge( pCrvOffs, PockParams, ptStart)) return false ; } else { if ( ! GetPtStartOnGenericEdgeByPtRef( pCrvOffs, PockParams, ptEndPrec, ptStart)) return false ; } } // imposto il punto iniziale scelto double dPar = 0. ; if ( ! pCrvOffs->GetParamAtPoint( ptStart, dPar) || ! pCrvOffs->ChangeStartPoint( dPar)) pCrvOffs->ChangeStartPoint( .5) ; return true ; } //---------------------------------------------------------------------------- static bool SetAdvancedPtStartForPath( ICRVCOMPOPOVECTOR& vCrvOffsAct, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork, const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut, int& nIndex, 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, ptStart, vtMidOut, bMidOut, i < ( int( vCrvOrigChunkLoops.size())) ? 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))) ; 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 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)) ; if ( IsNull( pCrvArc) || ! pCrvArc->IsValid() || ! pCrvArc->GetLength( dArcLen) || dArcLen < 5 * EPS_SMALL) break ; ++ 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' } // imposto la proprietà di raccordo vCrvArcs[i]->SetTempProp( TEMP_PROP_SMOOTH, 1) ; 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 int nProp0 = pCrv->GetTempProp( 0) ; int nProp1 = pCrv->GetTempProp( 1) ; double dParam0 = pCrv->GetTempParam( 0) ; double dParam1 = pCrv->GetTempParam( 1) ; pCrv->Clear() ; pCrv->AddCurve( Release( pCrvCO_temp)) ; pCrv->SetTempProp( nProp0, 0) ; pCrv->SetTempProp( nProp1, 1) ; pCrv->SetTempParam( dParam0, 0) ; pCrv->SetTempParam( dParam1, 1) ; return true ; } //---------------------------------------------------------------------------- static bool CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, const ICRVCOMPOPOVECTOR& vOffIslands, ICurveComposite* pCrvLink) { // pulisco pCrvLink->Clear() ; // non serve collegare ( può capitare nel tagliare percorsi con isole complesse) if ( AreSamePointApprox( ptStart, ptEnd)) return true ; // creo la retta che li unisce PtrOwner pCompoLine( CreateBasicCurveComposite()) ; if ( ! pCompoLine->AddPoint( ptStart) || ! pCompoLine->AddLine( ptEnd)) return false ; pCompoLine->SetExtrusion( Z_AX) ; // 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 ; // memorizzo il tratto lineare nel caso qualche operazione fallisse PtrOwner pCompoHelp( pCompoLine->Clone()) ; if ( IsNull( pCompoHelp)) 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()) ; } pCrvLink->AddCurve( Release( pCompo)) ; return true ; } //------------------------------------------------------------------------------ static bool CalcSpecialBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, const Vector3d& vtEnd, const PocketParams& PockParams, ICurveComposite* pCrvLink) { // controllo dei parametri if ( pCrvLink == nullptr) return false ; pCrvLink->Clear() ; // parametro di estensione in tangenza double dLenExtension = PockParams.dRad / 2. ; // determino i punti della curva PNTVECTOR vPnts = { ptStart, ptStart + ( dLenExtension * vtStart), ptEnd - ( dLenExtension * vtEnd), ptEnd} ; int nInd = 1 ; static const double COS_TOL = cos( 130 * DEGTORAD) ; // se primo angolo minore della tolleranza, aggiungo un punto Vector3d vtPrev = vPnts[nInd] - vPnts[nInd-1] ; vtPrev.Normalize() ; Vector3d vtNext = vPnts[nInd+1] - vPnts[nInd] ; vtNext.Normalize() ; double dCos = vtPrev * vtNext ; if ( dCos < COS_TOL) { Point3d ptA = vPnts[nInd] + dLenExtension * GetRotate( vtStart, Z_AX, ANG_RIGHT) ; Point3d ptB = vPnts[nInd] - dLenExtension * GetRotate( vtStart, Z_AX, ANG_RIGHT) ; if ( SqDist( ptA, vPnts[nInd+1]) < SqDist( ptB, vPnts[nInd+1])) vPnts.insert( vPnts.begin() + nInd + 1, ptA) ; else vPnts.insert( vPnts.begin() + nInd + 1, ptB) ; ++ nInd ; } ++ nInd ; // se secondo angolo maggiore della tolleranza, aggiungo un punto vtPrev = vPnts[nInd] - vPnts[nInd-1] ; vtPrev.Normalize() ; vtNext = vPnts[nInd+1] - vPnts[nInd] ; vtNext.Normalize() ; dCos = vtPrev * vtNext ; if ( dCos < COS_TOL) { Point3d ptA = vPnts[nInd] + dLenExtension * GetRotate( - vtEnd, Z_AX, ANG_RIGHT) ; Point3d ptB = vPnts[nInd] - dLenExtension * GetRotate( - vtEnd, Z_AX, ANG_RIGHT) ; if ( SqDist( ptA, vPnts[nInd-1]) < SqDist( ptB, vPnts[nInd-1])) vPnts.insert( vPnts.begin() + nInd + 1, ptA) ; else vPnts.insert( vPnts.begin() + nInd + 1, ptB) ; } // definisco raccordo a ZigZag ( Lineare) PtrOwner pZigZagLink( CreateCurveComposite()) ; if ( IsNull( pZigZagLink)) return false ; bool bFirst = true ; for ( const Point3d& ptNew : vPnts) { if ( bFirst) { bFirst = false ; pZigZagLink->AddPoint( ptNew) ; } else pZigZagLink->AddLine( ptNew) ; } // questa composita contiene al massimo 5 curve e, in generale, una curva molto più lunga // delle altre ( quella che collega le due estensioni calcolate). Spezzo la composita ottenuta in curve // la cui lunghezza massima è circa il raggio utensile PolyLine PL ; pZigZagLink->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL) ; PL.AdjustForMaxSegmentLen( PockParams.dRad + 10 * EPS_SMALL) ; pZigZagLink->Clear() ; pZigZagLink->FromPolyLine( PL) ; // Smusso il raccordo a ZigZag ModifyCurveToSmoothed( pZigZagLink, PockParams, .4, .4, true) ; pCrvLink->CopyFrom( pZigZagLink) ; return ( pCrvLink != nullptr && pCrvLink->IsValid() && pCrvLink->GetCurveCount() > 0) ; } //------------------------------------------------------------------------------ static bool CalcBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, const Vector3d& vtEnd, double dParMeet, const ICRVCOMPOPOVECTOR& vCrvBorders, const PocketParams& PockParams, ICurveComposite* pCrvLink, bool& bSpecial) { // controllo parametri if ( pCrvLink == nullptr) return false ; pCrvLink->Clear() ; // se senza smusso (e non caso circonferenza), ritorno tratto lineare bSpecial = false ; if ( ! PockParams.bSmooth && dParMeet > EPS_ZERO) return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; // inizializzo la curva di Link da restituire PtrOwner pMyCrvLink( CreateCurveComposite()) ; if ( IsNull( pMyCrvLink)) return false ; // controllo se la distanza lineare tra i due punti è maggiore della tolleranza... const double dSqTol = 4 * PockParams.dRad * PockParams.dRad + PockParams.dSideStep * PockParams.dSideStep ; if ( SqDist( ptStart, ptEnd) > dSqTol) bSpecial = ( CalcSpecialBoundedSmoothedLink( ptStart, vtStart, ptEnd, vtEnd, PockParams, pMyCrvLink)) ; if ( ! pMyCrvLink->IsValid() || pMyCrvLink->GetCurveCount() == 0) { // ...in caso negativo creo il Biarco double dAngStart ; vtStart.GetAngleXY( X_AX, dAngStart) ; double dAngEnd ; vtEnd.GetAngleXY( X_AX, dAngEnd) ; if ( dParMeet > EPS_ZERO) pMyCrvLink.Set( ConvertCurveToComposite( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, dParMeet))) ; else { // ... o una circonferenza, orientata in modo da poter essere classificata con le curve di bordo PtrOwner pCrvCir( CreateBasicCurveArc()) ; if ( IsNull( pCrvCir)) return false ; if ( pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, PockParams.bInvert ? 360 : -360, 0, Z_AX)) pMyCrvLink.Set( ConvertCurveToComposite( Release( pCrvCir))) ; } } if ( ! pMyCrvLink->IsValid() || pMyCrvLink->GetCurveCount() == 0) return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; // se il BiArco creato è troppo piccolo, lo approssimo ad un tratto lineare BBox3d bBox3 ; if ( pMyCrvLink->GetLocalBBox( bBox3)) { double dRadBB ; if ( bBox3.GetRadius( dRadBB) && dRadBB < 500 * EPS_SMALL) return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; } // il link ottenuto non deve uscire dalle curve di bordo ( Loop esterno ed Isole) // curva di test PtrOwner pCompoTest( CloneCurveComposite( pMyCrvLink)) ; if ( IsNull( pCompoTest) || ! pCompoTest->IsValid()) return false ; // curva ausiliaria PtrOwner pCompoHelp( CloneCurveComposite( pMyCrvLink)) ; if ( IsNull( pCompoHelp) || ! pCompoHelp->IsValid()) return false ; // scorro tutte le curve le curve di bordo for ( const ICurveComposite* pCrvBorder : vCrvBorders) { // calcolo eventuali intersezioni CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCompoTest, *pCrvBorder) ; intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; // pulisco la curva ausiliaria pCompoHelp->Clear() ; // analizzo le intersezioni for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) { // se ho intersezione spezzo il segmento if ( ccClass[i].nClass == CRVC_OUT && int( ccClass.size()) > 1) { // recupero eventuali punti di intersezioni e parametri Point3d ptS ; pCompoTest->GetPointD1D2( ccClass[i].dParS, ICurve::FROM_PLUS, ptS) ; double dOffS ; pCrvBorder->GetParamAtPoint( ptS, dOffS, 1500 * EPS_SMALL) ; Point3d ptE ; pCompoTest->GetPointD1D2( ccClass[i].dParE, ICurve::FROM_MINUS, ptE) ; double dOffE ; pCrvBorder->GetParamAtPoint( ptE, dOffE, 1500 * EPS_SMALL) ; // recupero i due possibili percorsi e uso il più corto PtrOwner pCrvA( pCrvBorder->CopyParamRange( dOffS, dOffE)) ; PtrOwner pCrvB( pCrvBorder->CopyParamRange( dOffE, dOffS)) ; if ( IsNull( pCrvA) || IsNull( pCrvB)) return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; double dLenA ; pCrvA->GetLength( dLenA) ; double dLenB ; pCrvB->GetLength( dLenB) ; // definisco il nuovo bordo mediante la curva ausiliaria if ( i != 0) { if ( dLenA < dLenB) { if ( ! pCompoHelp->AddCurve( Release( pCrvA))) return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; } else { pCrvB->Invert() ; if ( ! pCompoHelp->AddCurve( Release( pCrvB))) return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; } } else { pCrvB->Invert() ; if ( ! pCompoHelp->AddCurve( Release( pCrvB))) return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; } } // se non interseco else { if ( ! pCompoHelp->AddCurve( pCompoTest->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; } } // la curva di test diventa la curva ausiliaria pCompoTest->Clear() ; if ( ! pCompoTest->AddCurve( pCompoHelp->Clone())) if ( ! CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink)) return false ; } // La curva necessita di un ulteriore smusso, in quanto tagliata su un bordo ModifyCurveToSmoothed( pCompoTest, PockParams, PockParams.dSmooth, PockParams.dSmooth, false) ; // nel caso speciale della circonferenza, devo impostare il punto iniziale if ( dParMeet < EPS_ZERO) { if ( pCompoTest->IsClosed()) { // sempre... double dU = 0. ; int nFlag = 0 ; if ( DistPointCurve( ptStart, *pCompoTest).GetParamAtMinDistPoint( 0., dU, nFlag)) pCompoTest->ChangeStartPoint( dU) ; } } // restituisco il Link if ( IsNull( pCompoTest) || ! pCompoTest->IsValid() || pCompoTest->GetCurveCount() == 0) return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; pCrvLink->AddCurve( Release( pCompoTest)) ; return true ; } //---------------------------------------------------------------------------- static bool CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, const ICRVCOMPOPOVECTOR& vFirstOffset, const PocketParams& PockParams, double dLenPercS, double dLenPercE, ICurveComposite* pCrvLink) { // controllo i parametri if ( pCrvS == nullptr || pCrvE == nullptr ) return false ; pCrvLink->Clear() ; // curva finale da restituire PtrOwner pCrvFinal( CreateBasicCurveComposite()) ; if ( IsNull( pCrvFinal)) return false ; // recupero gli estremi delle due curve ( S = Start, E = End) Point3d ptSS ; pCrvS->GetStartPoint( ptSS) ; Point3d ptSE ; pCrvS->GetEndPoint( ptSE) ; Point3d ptES ; pCrvE->GetStartPoint( ptES) ; Point3d ptEE ; pCrvE->GetEndPoint( ptEE) ; // recupero i versori per il BiArco Vector3d vS ; pCrvS->GetEndDir( vS) ; Vector3d vE ; pCrvE->GetStartDir( vE) ; // recupero i domini delle curve double dUSS, dUSE ; pCrvS->GetDomain( dUSS, dUSE) ; double dUES, dUEE ; pCrvE->GetDomain( dUES, dUEE) ; // recupero le lunghezze delle curve double dLenS ; pCrvS->GetLength( dLenS) ; double dLenE ; pCrvE->GetLength( dLenE) ; // azzero i parametri per curve di primo Offset for ( int i = 0 ; i < int( vFirstOffset.size()) ; ++ i) { if ( vFirstOffset[i]->IsPointOn( ptSS) && vFirstOffset[i]->IsPointOn( ptSE)) dLenPercS = 0. ; #if 0 if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE)) dLenPercE = 0. ; #endif } #if 0 // nel caso volessi estendere anche prima del punto finale double dLStepS = ( dLenPercS < EPS_SMALL ? 0. : PockParams.dRad) ; #endif double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : PockParams.dRad) ; dLenE = 0 ; // recupero il numero massimo di iterazioni per il calcolo del BiArco int nMaxIter = 2 ; if ( ! PockParams.bSmooth) nMaxIter = 1 ; // taglio la curva al massimo nMaxIter-volte e mi fermo al taglio più opportuno int nIter = 0 ; while ( nIter < nMaxIter) { // calcolo il biArco bool bSpecial = false ; PtrOwner pCrvBiArc( CreateBasicCurveComposite()) ; if ( IsNull( pCrvBiArc) || ! CalcBoundedSmoothedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, PockParams, pCrvBiArc, bSpecial) || pCrvBiArc->GetCurveCount() == 0) return false ; pCrvFinal->Clear() ; pCrvFinal.Set( pCrvBiArc->Clone()) ; // se non devo cercarne altri, allora tengo quest'ultimo if ( ( dLenPercE == 0 && dLenPercS == 0) || bSpecial) break ; // altrimenti, calcolo un nuovo BiArco, estendendo i suoi estremi #if 0 // nel caso volessi estendere anche prima del punto finale dLenS -= dLStepS ; #endif dLenE += dLStepE ; // ricalcolo il punto iniziale e finale pCrvS->GetParamAtLength( dLenS, dUSE) ; if ( ! pCrvS->GetPointD1D2( dUSE, ICurve::FROM_MINUS, ptSE, &vS)) return false ; vS.Normalize() ; pCrvE->GetParamAtLength( dLenE, dUES) ; if ( ! pCrvE->GetPointD1D2( dUES, ICurve::FROM_PLUS, ptES, &vE)) return false ; vE.Normalize() ; ++ nIter ; } pCrvLink->AddCurve( Release( pCrvFinal)) ; // ultimo arco valido trovato return true ; } //---------------------------------------------------------- static bool GetUnclearedRegionAndSetFeed( const ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ISurfFlatRegion* pSfrUncleared) { // se non devo nè calcolare la Feed nè calcolare le regioni svuotate, allora non faccio nulla if ( ! PockParams.bCalcFeed && ! PockParams.bCalcUnclearedRegs) return true ; // controllo dei parametri if ( vFirstOffs.empty() || ssize( vOffs) < ssize( vFirstOffs)) return false ; pSfrUncleared->Clear() ; // creo la regione esterna ( definita dalle curve di primo Offset) // NB. Questa regione è diversa dalla pSfrOrig : // - i tratti chiusi sono Offsettati di R + OffsR() // - i tratti aperti sono stati estesi all'esterno di PockParams.dMaxOpenEdgeRad PtrOwner pSrfExtern( CreateSurfFlatRegion()) ; if ( IsNull( pSrfExtern)) return false ; for ( int i = 0 ; i < ssize( vFirstOffs) ; ++ 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 ; } } // creo la regione che all'esterno contiene i lati aperti // NB. Prendendo la superficie originale ( con lati Open/Close impostati), se effettuo un // Offset di una piccola quantità, tutte le curve esterne a tale regione sono associate a lati aperti const double OPEN_OFFS_TOL = 1000. * EPS_SMALL ; PtrOwner pSfrForOpenEdges( CreateSurfFlatRegion()) ; if ( IsNull( pSfrForOpenEdges)) return false ; if ( pSfrOrig != nullptr && pSfrOrig->IsValid()) { if ( ! pSfrForOpenEdges.Set( CloneSurfFlatRegion( pSfrOrig)) || ! pSfrForOpenEdges->Offset( OPEN_OFFS_TOL, ICurve::OFF_FILLET)) return false ; } // definisco la regione rimossa dall'utensile PtrOwner pSfrTool( CreateSurfFlatRegion()) ; if ( IsNull( pSfrTool)) return false ; // creo un vettore che conterrà solamente i Link percorsi fino ad ora // NB. Per SpiralOut devo scorrere le curva di Offset in maniera inversa bool bFollowOrder = ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_CONFORMAL_ZIGZAG) ; for ( int i = ( bFollowOrder ? 0 : ssize( vOffs) - 1) ; ( bFollowOrder ? i < ssize( vOffs) : i >= 0) ; ( bFollowOrder ? ++ i : -- i)) { // ================== OFFSET ============================= // recupero le proprietà temporanee int nProp0 = vOffs[i]->GetTempProp( 0) ; int nProp1 = vOffs[i]->GetTempProp( 1) ; // il primo TempParam viene settato in proporzione alla Feed // il secondo TempParam contiene l'nSide della curva double dTempPar1 = vOffs[i]->GetTempParam( 1) ; // Feed bool bFirstOffs = false ; if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) bFirstOffs = true ; else { Point3d ptStart ; vOffs[i]->GetStartPoint( ptStart) ; for ( int j = 0 ; j < ssize( vFirstOffs) && ! bFirstOffs ; ++ j) bFirstOffs = vFirstOffs[j]->IsPointOn( ptStart, 100. * EPS_SMALL) ; } // calcolo gli intervalli di Feed per la curva di Offset if ( ! AssignFeedSpiral( vOffs[i], pSfrTool, false, bFirstOffs, pSfrForOpenEdges, PockParams, 2. * PockParams.dRad / 3.)) return false ; // aggiorno la superificie svuotata PtrOwner pSrfToolOffs( GetSurfFlatRegionFromFatCurve( vOffs[i]->Clone() , PockParams.dRad + 5. * EPS_SMALL, false, false, 10. * EPS_SMALL, false)) ; if ( ! IsNull( pSrfToolOffs)) { if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0) pSfrTool.Set( pSrfToolOffs) ; else pSfrTool->Add( *pSrfToolOffs) ; } vOffs[i]->SetTempProp( nProp0, 0) ; vOffs[i]->SetTempProp( nProp1, 1) ; vOffs[i]->SetTempParam( dTempPar1, 1) ; // ================= LINK ================================ int nIndLink = ( bFollowOrder ? i + 1 : i) ; // ( Il primo link è nullo, infatti non vi è nessun raccordo per raggiungere il primo Offset) if ( nIndLink < int( vLinks.size()) && ! IsNull( vLinks[nIndLink]) && vLinks[nIndLink]->IsValid()) { // recupero le proprietà temporanee int nProp0 = vLinks[nIndLink]->GetTempProp( 0) ; int nProp1 = vLinks[nIndLink]->GetTempProp( 1) ; // Feed PtrOwner pCrvLink( ConvertCurveToComposite( vLinks[nIndLink]->Clone())) ; // calcolo gli intervalli di Feed per la curva di Link if ( IsNull( pCrvLink) || ! pCrvLink->IsValid() || ! AssignFeedSpiral( pCrvLink, pSfrTool, true, false, pSfrForOpenEdges, PockParams, 2. * PockParams.dRad / 3.)) return false ; // aggiorno la regione svuotata PtrOwner pSfrToolLink( GetSurfFlatRegionFromFatCurve( pCrvLink->Clone(), PockParams.dRad + 10. * EPS_SMALL, false, false, 10. * EPS_SMALL, false)) ; if ( ! IsNull( pSfrToolLink)) { if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0) pSfrTool.Set( pSfrToolLink) ; else pSfrTool->Add( *pSfrToolLink) ; } // Imposto il Link come Curva Composita pCrvLink->SetTempProp( nProp0, 0) ; pCrvLink->SetTempProp( nProp1, 1) ; vLinks[nIndLink].Set( pCrvLink) ; } } // se rischiesto, creo la regione contenente tutte le parti non svuotate if ( PockParams.bCalcUnclearedRegs) { pSfrUncleared->CopyFrom( pSrfExtern) ; if ( ! pSfrUncleared->Subtract( *pSfrTool)) return false ; } return true ; } //---------------------------------------------------------------------------- static bool ChooseCurveForRemovingUnclearedRegion( const ICRVCOMPOPOVECTOR& vOffs, const ICRVCOMPOPOVECTOR& vOffsFirstCurve, const Point3d& ptToGo, const PocketParams& PockParams, int& nInd, Point3d& ptCloser, bool& bFirstOffs) { // E' richiesto che le curve di Offset presentino le seguenti proprietà : // - CONFORMAL // * TempProp0 -> Offset progressivo ricavato ( 0, 1, 2, ... nMaxIter) // * TempProp1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT) // - SPIRAL // * vOffsFirstCurve -> per capire se l'Offset corrente è di primo Offset o meno // * TempParam1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT) // controllo dei parametri nInd = -1 ; if ( vOffs.empty()) return true ; // scorro tutte le curve di Offset int nFlag ; struct OffsPtMinDist { int nInd = 0 ; double dSqDist = INFINITO - 1 ; Point3d ptMinDist = P_INVALID ; bool bInVsOut = true ; bool bFirstOffs = false ; } ; vector vOffsPtMinDist( vOffs.size()) ; for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) { // indice dell'Offset vOffsPtMinDist[i].nInd = i ; // nel caso di Conformal, escludo le curve di primo Offset dalla ricerca if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) { // prima iterazione if ( vOffs[i]->GetTempProp( 0) == 0) { vOffsPtMinDist[i].bFirstOffs = true ; continue ; } } // Controllo se il punto da raggiungere è interno o esterno alla curva // NB. cerco di privilegiare le curve che presentano il punto da raggiungere al loro esterno; // in questo modo riesco a creare le curve a ricciolo int nSide ; DistPointCurve DistPtCrv( ptToGo, *vOffs[i]) ; if ( DistPtCrv.GetSideAtMinDistPoint( EPS_SMALL, Z_AX, nSide)) { // nel caso Spiral if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_SPIRALOUT) { // InVsOut vOffsPtMinDist[i].bInVsOut = ( int( vOffs[i]->GetTempParam( 1) != nSide)) ; // controllo se la curva in questione è di primo Offset Point3d ptCheck ; vOffs[i]->GetStartPoint( ptCheck) ; for ( int j = 0 ; j < int( vOffsFirstCurve.size()) ; ++ j) { if ( vOffsFirstCurve[j]->IsPointOn( ptCheck, 100 * EPS_SMALL)) { vOffsPtMinDist[i].bFirstOffs = true ; break ; } } } // nel caso Conformal else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) { if ( nSide != vOffs[i]->GetTempProp( 1)) vOffsPtMinDist[i].bInVsOut = false ; } // recupero il punto a minima distanza e la distanza DistPtCrv.GetMinDistPoint( EPS_SMALL, vOffsPtMinDist[i].ptMinDist, nFlag) ; DistPtCrv.GetSqDist( vOffsPtMinDist[i].dSqDist) ; // se il punto da raggiungere è interno alla curva, aggiungo una penalità ( euristica) if ( vOffsPtMinDist[i].bInVsOut) { vOffsPtMinDist[i].dSqDist += PockParams.dRad + PockParams.dRad + PockParams.dSideStep + PockParams.dSideStep ; } // se curva di bordo esterno, aggiungo un'altra penalità ( euristica) if ( vOffsPtMinDist[i].bFirstOffs) vOffsPtMinDist[i].dSqDist *= 2. ; } } // ordino il vettore in base alle distanze calcolate sort( vOffsPtMinDist.begin(), vOffsPtMinDist.end(), []( const OffsPtMinDist& a, const OffsPtMinDist& b) { return a.dSqDist < b.dSqDist ; }) ; // recupero la curva migliore OffsPtMinDist& BestOffs = vOffsPtMinDist[0] ; nInd = BestOffs.nInd ; ptCloser = BestOffs.ptMinDist ; bFirstOffs = BestOffs.bFirstOffs ; return true ; } //---------------------------------------------------------------------------- static bool RemoveUnclearedRegions( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs, ICRVCOMPOPOVECTOR& vOffsFirstCurve, const PocketParams& PockParams) { // se non devo calcolare le regioni non svuotate, esco subito if ( ! PockParams.bCalcUnclearedRegs) return true ; // E' richiesto che le curve di Offset presentino le seguenti proprietà : // - CONFORMAL // * TempProp0 -> Offset progressivo ricavato ( 0, 1, 2, ... nMaxIter) // * TempProp1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT) // - SPIRAL // * vOffsFirstCurve -> per capire se l'Offset corrente è di primo Offset o meno // * TempParam1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT) // se non ho nessuna superficie valida da svuotare, allora esco if ( pSfrUncleared == nullptr) return false ; if ( ! pSfrUncleared->IsValid() || pSfrUncleared->GetChunkCount() == 0) return true ; // scorro tutti i Chunk della regione ICRVCOMPOPOVECTOR vCrvCurl ; for ( int nC = 0 ; nC < pSfrUncleared->GetChunkCount() ; ++ nC) { // recupero il centroide del Chunk da rimuovere Point3d ptCentroid ; pSfrUncleared->GetChunkCentroid( nC, ptCentroid) ; // Tra tutte le curve di Offset escludo le curve che : // - Sono di primo Offset // - Non contengono il centroide ( condizione già verificata per quelle di primo Offset) // ... cerco la più vicina tra le rimanenti // recupero la curva migliore int nInd = -1 ; Point3d ptCloser ; bool bFirstOffs = false ; if ( ! ChooseCurveForRemovingUnclearedRegion( vOffs, vOffsFirstCurve, ptCentroid, PockParams, nInd, ptCloser, bFirstOffs)) return false ; // se nessun indice trovato, salto il chunk if ( nInd == -1) continue ; // cerco sulla curva di Offset nInd-esima la sottocurva dove cade il punto a minima distanza double dURef = 0 ; if ( ! vOffs[nInd]->GetParamAtPoint( ptCloser, dURef)) continue ; int nCrv = int( floor( dURef)) ; const ICurve* pCrv = vOffs[nInd]->GetCurve( nCrv) ; if ( pCrv == nullptr || ! pCrv->IsValid()) continue ; // determino se la curva è di raccordo o meno bool bIsSmoothCrv = false ; // se non di primo Offset allora leggo la proprietà if ( ! bFirstOffs) bIsSmoothCrv = ( PockParams.bSmooth && pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ; else bIsSmoothCrv = ( pCrv->GetTempProp( 1) > 0) ; // clono la curva di Offset corrente ( in questo modo non modifico l'originale) PtrOwner pMyOffs( CloneCurveComposite( vOffs[nInd])) ; if ( IsNull( pMyOffs) || ! pMyOffs->IsValid()) return false ; // versore uscente ed entrante dall'Offset attuale per creazione della curva a ricciolo Vector3d vt1, vt2, vtMain ; // punto di uscita e di ingresso dall'Offset attuale per creazione della curva a ricciolo Point3d pt1, pt2, ptMain ; // paramtro di uscita e di ingresso dall'Offset attuale per creazione della curva a ricciolo double dURef1, dURef2 ; // numero di curve dell'offset corrente int nCrvNumber = ( pMyOffs->GetCurveCount()) ; // inizializzo la curva a ricciolo PtrOwner pCrvCurl( CreateCurveComposite()) ; if ( IsNull( pCrvCurl)) return false ; // se è di raccordo, ricostruisco lo spigolo vivo della curva originale di Offset if ( bIsSmoothCrv) { vt1 = V_INVALID ; vt2 = V_INVALID ; // cerco la prima sottocurva che non sia di raccordo in precedenza for ( int i = ( nCrv + nCrvNumber - 1) % nCrvNumber ; ( i % nCrvNumber) == i ; -- i) { // recupero la sottocurva const ICurve* pCrvPrec = pMyOffs->GetCurve( i) ; if ( pCrvPrec == nullptr || ! pCrvPrec->IsValid()) break ; // se non di raccordo if ( pCrvPrec->GetTempProp( 1) != TEMP_PROP_SMOOTH) { // definisco il versore uscente pCrvPrec->GetEndDir( vt1) ; // definisco il punto di uscita pCrvPrec->GetEndPoint( pt1) ; // definisco il parametro di uscita dURef1 = ( i + 1) % nCrvNumber ; break ; } } bIsSmoothCrv = ( vt1.IsValid()) ; if ( bIsSmoothCrv) { // cerco la prima sottocurva che non sia di raccordo in sucessione for ( int i = ( nCrv + nCrvNumber + 1) % nCrvNumber ; ( i % nCrvNumber) == i ; ++ i) { // recupero la sottocurva const ICurve* pCrvSucc = pMyOffs->GetCurve( i) ; if ( pCrvSucc == nullptr || ! pCrvSucc->IsValid()) break ; // se non di raccordo if ( pCrvSucc->GetTempProp( 1) != TEMP_PROP_SMOOTH) { // definisco il versore entrante pCrvSucc->GetStartDir( vt2) ; // definisco il punto di entrata pCrvSucc->GetStartPoint( pt2) ; // definisco il parametro di entrata dURef2 = ( i % nCrvNumber) ; break ; } } } bIsSmoothCrv = bIsSmoothCrv && ( vt2.IsValid()) ; if ( bIsSmoothCrv) { // trovo il punto di intersezione tra le due direzioni tangenti applicate const double EXTENSION_LEN = 1000. ; // segmento iniziale ------- PtrOwner pLineS( CreateCurveLine()) ; if ( IsNull( pLineS) || ! pLineS->Set( pt1, pt1 + vt1 * EXTENSION_LEN)) continue ; // segmento finale -------- PtrOwner pLineE( CreateCurveLine()) ; if ( IsNull( pLineE) || ! pLineE->Set( pt2, pt2 - vt2 * EXTENSION_LEN)) continue ; // intersezione IntersCurveCurve ILL( *pLineS, *pLineE) ; bIsSmoothCrv = ( ILL.GetCrossIntersCount() == 1) ; if ( bIsSmoothCrv) { IntCrvCrvInfo aInfo ; bIsSmoothCrv = ( ( ILL.GetIntCrvCrvInfo( 0, aInfo)) && pCrvCurl->AddPoint( pt1) && pCrvCurl->AddLine( aInfo.IciA[0].ptI)) ; if ( bIsSmoothCrv) ptMain = aInfo.IciA[0].ptI ; } } } // se non è di raccordo, allora ho già lo spigolo vivo if ( ! bIsSmoothCrv) { pCrvCurl->Clear() ; // definisco il versore entrante ed uscente // definisco il punto di entrata e di uscita ( sono coincidenti) dURef1 = dURef ; dURef2 = dURef ; if ( ! vOffs[nInd]->GetPointD1D2( dURef1, ICurve::FROM_MINUS, ptMain, &vt1) || ! vOffs[nInd]->GetPointD1D2( dURef2, ICurve::FROM_PLUS, ptMain, &vt2)) continue ; vt1.Normalize() ; vt2.Normalize() ; } // recupero le due direzioni per i Biarchi bool bSameDir = AreSameVectorEpsilon( vt1, vt2, 2 * EPS_SMALL) ; bool bSpecial = false ; // se parallele, allora trasformo in una circonferenza if ( bSameDir) { if ( ! CalcBoundedSmoothedLink( ptMain, vt1, ptCentroid, vt2, 0., vOffsFirstCurve, PockParams, pCrvCurl, bSpecial)) continue ; Vector3d vtStartCheck ; pCrvCurl->GetStartDir( vtStartCheck) ; if ( AreOppositeVectorApprox( vtStartCheck, vt1)) pCrvCurl->Invert() ; } // se distinte... else { // calcolo la direzione tra pt1 e ptCentroid Vector3d vtDir = ( ptCentroid - ptMain) ; vtDir.Normalize() ; vtDir.Rotate( Z_AX, ANG_RIGHT) ; double dAngMain ; bool bDet ; vt1.GetRotation( vt2, Z_AX, dAngMain, bDet) ; if ( dAngMain > - EPS_ZERO) vtDir.Invert() ; // calcolo i due Biarchi di raccordo PtrOwner pCrvBiArc1( CreateCurveComposite()) ; PtrOwner pCrvBiArc2( CreateCurveComposite()) ; if ( IsNull( pCrvBiArc1) || IsNull( pCrvBiArc2) || ! CalcBoundedSmoothedLink( ptMain, vt1, ptCentroid, vtDir, bSameDir ? 0. : 0.5, vOffsFirstCurve, PockParams, pCrvBiArc1, bSpecial) || ! CalcBoundedSmoothedLink( ptCentroid, vtDir, ptMain, vt2, bSameDir ? 0. : 0.5, vOffsFirstCurve, PockParams, pCrvBiArc2, bSpecial)) continue ; // aggiorno la curva a ricciolo if ( ! pCrvCurl->AddCurve( Release( pCrvBiArc1)) || ! pCrvCurl->AddCurve( Release( pCrvBiArc2)) || ! pCrvCurl->IsValid()) continue ; } // controllo se devo raccordarmi con Offset if ( bIsSmoothCrv) pCrvCurl->AddLine( pt2) ; // controllo se tale curva effettivamente rimuove il Chunk nC-esimo PtrOwner pSfrFatCurl( GetSurfFlatRegionFromFatCurve( pCrvCurl->Clone(), PockParams.dRad, false, false)) ; if ( IsNull( pSfrFatCurl) || ! pSfrFatCurl->IsValid()) continue ; PtrOwner pSfrChunk( pSfrUncleared->CloneChunk( nC)) ; if ( IsNull( pSfrFatCurl) || ! pSfrFatCurl->IsValid()) return false ; pSfrChunk->Subtract( *pSfrFatCurl) ; // controllo se la regione è tutta rimossa if ( pSfrChunk->IsValid() || pSfrChunk->GetChunkCount() > 0) { // so che il punto più vicino alla curva è ptCloser // ricavo dunque il punto più vicino sul bordo del Chunk nC-esimo PtrOwner pCompoChunkBorder( ConvertCurveToComposite( pSfrUncleared->GetLoop( nC, 0))) ; if ( IsNull( pCompoChunkBorder) || ! pCompoChunkBorder->IsValid()) return false ; if ( PockParams.bInvert) pCompoChunkBorder->Invert() ; DistPointCurve distPtChunkBorder( ptCloser, *pCompoChunkBorder) ; Point3d ptCloserOnChunk ; int nFlag ; if ( ! distPtChunkBorder.GetMinDistPoint( EPS_SMALL, ptCloserOnChunk, nFlag)) continue ; // cambio il punto di inizio della curva su tale punto double dUS = 0. ; pCompoChunkBorder->GetParamAtPoint( ptCloserOnChunk, dUS) ; pCompoChunkBorder->ChangeStartPoint( dUS) ; /* Creo due curve composite rappresentanti un piccolo tratto tangente sulla curva di Offset attuale; questi servono per la partenza e l'arrivo dei biArchi */ if ( bSameDir) { pt1 = ptMain ; pt2 = ptMain ; } PtrOwner pCompoHelpS( CreateCurveComposite()) ; if ( IsNull( pCompoHelpS) || ! pCompoHelpS->AddPoint( pt1 - vt1 / 4) || ! vt1.Normalize() || ! pCompoHelpS->AddLine( pt1)) continue ; PtrOwner pCompoHelpE( CreateCurveComposite()) ; if ( IsNull( pCompoHelpE) || ! pCompoHelpE->AddPoint( pt2) || ! vt2.Normalize() || ! pCompoHelpE->AddLine( pt2 + vt2 / 4)) continue ; /* creo il Biarco iniziale ( nel caso non si riuscisse diventa un segmento lineare) */ PtrOwner pCrvBiArc1( CreateCurveComposite()) ; if ( IsNull( pCrvBiArc1)) return false ; if ( ! CutCurveToConnect( pCompoHelpS, pCompoChunkBorder, vOffsFirstCurve, PockParams, 0, 10 * EPS_SMALL, pCrvBiArc1)) { if ( ! CalcBoundedLink( ptCloser, ptCloserOnChunk, vOffsFirstCurve, pCrvBiArc1)) continue ; } // aggiorno i nuovi punti e i nuovi parametri double dMyU ; Point3d ptMyPoint ; if ( ! pCrvBiArc1->GetEndPoint( ptMyPoint) || ! pCompoChunkBorder->GetParamAtPoint( ptMyPoint, dMyU)) continue ; // accorgio il bordo dell'Offset in raccordo al BiArco iniziale if ( dMyU > EPS_SMALL) pCompoChunkBorder->TrimStartAtParam( dMyU) ; /* creo il Biarco finale ( nel caso non si riuscisse diventa un segmento lineare) */ // creo il Biarco iniziale ( nel caso non si riuscisse diventa un segmento lineare) PtrOwner pCrvBiArc2( CreateCurveComposite()) ; if ( IsNull( pCrvBiArc2)) return false ; if ( ! CutCurveToConnect( pCompoChunkBorder, pCompoHelpE, vOffsFirstCurve, PockParams, 0., 0., pCrvBiArc2)) { if ( ! CalcBoundedLink( ptCloserOnChunk, ptCloser, vOffsFirstCurve, pCrvBiArc2)) continue ; } // aggiorno i nuovi punti e i nuovi parametri if ( ! pCrvBiArc2->GetStartPoint( ptMyPoint) || ! pCompoChunkBorder->GetParamAtPoint( ptMyPoint, dMyU)) continue ; // la curva a ricciolo ora diventa l'unione di pCrvBiArc1 - pCompoChunkBorder - pCrvBiArc2 pCrvCurl->Clear() ; // smusso la curva di bordo del Chunk ( è aperta) double dSmoothPar = PockParams.dRad / 8. ; ModifyCurveToSmoothed( pCompoChunkBorder, PockParams, dSmoothPar, dSmoothPar, false) ; if ( ! pCrvCurl->AddCurve( Release( pCrvBiArc1)) || ! pCrvCurl->AddCurve( Release( pCompoChunkBorder)) || ! pCrvCurl->AddCurve( Release( pCrvBiArc2))) continue ; } // attribuisco alla curva a ricciolo la Feed minima AssignMinFeed( pCrvCurl, PockParams) ; // aggiorno il nuovo Offset aggiungendo la curva a ricciolo PtrOwner pCrvTest( CreateCurveComposite()) ; if ( IsNull( pCrvTest)) return false ; // aggiungo tratto iniziale di Offset pCrvTest->AddCurve( ConvertCurveToComposite( pMyOffs->CopyParamRange( 0, dURef1))) ; // aggiungo la curva a ricciolo if ( ! pCrvTest->AddCurve( Release( pCrvCurl))) continue ; // aggiungo il tratto finale di Offset pCrvTest->AddCurve( ConvertCurveToComposite( pMyOffs->CopyParamRange( dURef2, pMyOffs->GetCurveCount()))) ; // provo a controllare che i punti inziali e finali coincidano, in caso aggiorno il nuovo Offset Point3d ptStart_Old ; vOffs[nInd]->GetStartPoint( ptStart_Old) ; Point3d ptEnd_Old ; vOffs[nInd]->GetEndPoint( ptEnd_Old) ; Point3d ptStart_New ; pCrvTest->GetStartPoint( ptStart_New) ; Point3d ptEnd_New ; pCrvTest->GetEndPoint( ptEnd_New) ; if ( AreSamePointApprox( ptStart_Old, ptStart_New) && AreSamePointApprox( ptEnd_Old, ptEnd_New)) { pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 0), 0) ; pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 1), 1) ; vOffs[nInd].Set( pCrvTest) ; } } return true ; } //---------------------------------------------------------------------------- static bool CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, const PocketParams& PockParams, const ICRVCOMPOPOVECTOR& vOffsFirstCurve) { // controllo dei parametri if ( vOffs.empty() || vOffsFirstCurve.empty()) return false ; vLinks.clear() ; vLinks.resize( int( vOffs.size())) ; // NB. Dato che le curve di Offset sono invertite a seconda di dove si trova il materiale, // Utilizzo il secondo TempParam per evere l'nSide // 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 ; // tra le curve successive cerco la curva interna e più vicina ad essa int nNextInd = -1 ; // indice della curva successiva double dMinDist = INFINITO ; // distanza tra questa curva e la successiva Point3d ptStartNext ; // punto iniziale della curva successiva bool bMinDistAtSmooth = false ; // se punto a minima distanza su curva di smusso 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 && int( vOffs[i]->GetTempParam( 1)) == MDS_RIGHT) || ( ccClass[0].nClass == CRVC_OUT && int( vOffs[i]->GetTempParam( 1)) == MDS_LEFT))) { // calcolo la distanza minima tra essa int nFlag ; double dPar = 0. ; Point3d ptClosest ; DistPointCurve DistPtCrv( ptS, *vOffs[j]) ; if ( DistPtCrv.GetParamAtMinDistPoint( EPS_SMALL, dPar, nFlag)) { // controllo se la curva ottenuta è di raccordo, in caso positivo considero come // punto più vicino il suo punto finale ( per avere poi successivamente Link tra // Offset in tangenza ) const ICurve* pCrv = vOffs[j]->GetCurve( static_cast( floor( dPar))) ; if ( pCrv != nullptr && pCrv->IsValid()) { if ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) pCrv->GetEndPoint( ptClosest) ; else DistPtCrv.GetMinDistPoint( EPS_SMALL, ptClosest, nFlag) ; double dCurrDist = SqDist( ptS, ptClosest) ; if ( dCurrDist < dMinDist) { dMinDist = dCurrDist ; nNextInd = j ; ptStartNext = ptClosest ; bMinDistAtSmooth = ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ; } } } } } // 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 ; double dPar = 0. ; Point3d ptClosest ; DistPointCurve DistPtCrv( ptS, *vOffs[j]) ; if ( DistPtCrv.GetParamAtMinDistPoint( EPS_SMALL, dPar, nFlag)) { const ICurve* pCrv = vOffs[j]->GetCurve( static_cast( floor( dPar))) ; if ( pCrv != nullptr && pCrv->IsValid()) { if ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) pCrv->GetEndPoint( ptClosest) ; else DistPtCrv.GetMinDistPoint( EPS_SMALL, ptClosest, nFlag) ; double dCurrDist = SqDist( ptS, ptClosest) ; if ( dCurrDist < dMinDist) { dMinDist = dCurrDist ; nNextInd = j ; ptStartNext = ptClosest ; bMinDistAtSmooth = ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ; } } } } } // 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) ; // se ho più di una curva di Offset 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 ; double dLenPercS = ( i == 0 ? 0. : 10 * EPS_SMALL) ; double dLenPercE = ( bMinDistAtSmooth ? 0. : 10 * EPS_SMALL) ; if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsFirstCurve, PockParams, dLenPercS, dLenPercE, pCrvLink) || ! pCrvLink->IsValid()) { // se non sono riuscito, cerco una strada più semplice ripristinando le curve pCrvLink->Clear() ; vOffs[i].Set( pCrv_i) ; vOffs[i+1].Set( pCrv_ii) ; // recupero i vettori tangenti 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 bool bSpecial = false ; if ( CalcBoundedSmoothedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink, bSpecial)) 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->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( CloneCurveComposite( vOffs[i])) ; 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 ISurfFlatRegion* pSfrAct, const ICurveComposite* pCrvOffs, double dOffs, double dOffsPrec, int nIter, 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 ; // se spiral out non vanno eliminati parti di percorso if ( PockParams.nType == POCKET_SPIRALOUT) return true ; if ( nIter != 0) { // controllo se sono almeno al secondo offset... // per ogni curva controllo se il suo Offset massimo non genera alcuna regione double dMaxOffs ; if ( ! CalcCurveLimitOffset( *pCrvOffs, dMaxOffs)) return false ; // controllo se l'Offset rimuove materiale if ( dMaxOffs + ( dOffs - dOffsPrec) - PockParams.dRad < EPS_SMALL) { bInsert = false ; return true ; } // controllo se l'Offset rimuove una quantità minima di materiale else if ( dMaxOffs + ( dOffs - dOffsPrec) - PockParams.dRad < TOL_REMOVE_OFFSET) { // inizializzo la curva di bordo della regione non svuotata PtrOwner pCrvBorder( CreateCurveComposite()) ; if ( IsNull( pCrvBorder)) return false ; // recupero la regione non svuotata e calcolo la curva di bordo PtrOwner pSfrA( pSfrAct->CreateOffsetSurf( - dOffsPrec - PockParams.dRad, ICurve::OFF_FILLET)) ; if ( IsNull( pSfrA)) return false ; if ( AreOppositeVectorApprox( pSfrA->GetNormVersor(), Z_AX)) pSfrA->Invert() ; for ( int nC = 0 ; nC < pSfrA->GetChunkCount() ; ++ nC) { PtrOwner pSfrChunk( pSfrA->CloneChunk( nC)) ; if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid()) return false ; PtrOwner pSfrB( CreateSurfFlatRegion()) ; if ( IsNull( pSfrB) || ! pSfrB->AddExtLoop( pCrvOffs->Clone())) return false ; if ( AreOppositeVectorApprox( pSfrB->GetNormVersor(), Z_AX)) pSfrB->Invert() ; if ( pSfrB->Intersect( *pSfrChunk) && pSfrB->IsValid()) { if ( ! pCrvBorder.Set( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0)))) return false ; break ; } } if ( pCrvBorder->IsValid()) { // controllo se l'utensile posizionato nel centroide rimuove la regione Point3d ptCentroid ; pCrvBorder->GetCentroid( ptCentroid) ; PtrOwner pCrvArc( CreateCurveArc()) ; if ( IsNull( pCrvArc)) return false ; pCrvArc->Set( ptCentroid, Z_AX, PockParams.dRad - 10 * EPS_SMALL) ; IntersCurveCurve ICC( *pCrvBorder, *pCrvArc) ; CRVCVECTOR ccClass ; bInsert = ( ! ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) || int( ccClass.size()) != 1 || ccClass[0].nClass != CRVC_IN) ; } } } return true ; } //---------------------------------------------------------------------------- static bool IsCompoMadeBy2DifferentHomogeneousParts( const ICurveComposite* pCompo, const PocketParams& PockParams, bool& bOk, ICRVCOMPOPOVECTOR& vpCrvs) { // controllo dei parametri if ( pCompo == nullptr || ! pCompo->IsValid()) return false ; bOk = false ; vpCrvs.clear() ; // clono la curva composita ( cambierà il suo punto iniziale) PtrOwner pCompoCL( CloneCurveComposite( pCompo)) ; if ( IsNull( pCompoCL) || ! pCompoCL->IsValid()) return false ; // recupero i tratti con proprietà uniformi GetHomogeneousParts( pCompoCL, PockParams, vpCrvs) ; if ( vpCrvs.size() > 1) { // unisco il primo e l'ultimo se estremi compatibili 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 che la curva abbia esattamente due tratti omogenei bOk = ( int( vpCrvs.size()) == 2) ; if ( ! bOk) vpCrvs.clear() ; return true ; } //---------------------------------------------------------------------------- static int CalcInversionForSpiralOffset( const ISurfFlatRegion* pSfrChunk) { // controllo validità della regione if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid()) return -1 ; // Caso 0 : Non esistono isole // Caso 1 : Loop Esterno Chiuso e, se esiste, Isola Chiusa // Caso 2 : Loop Esterno Chiuso e, se esiste, Isola Aperta // Caso 3 : Loop Esterno Aperto e, se esiste, Isola Chiusa // Caso 4 : Loop Esterno Aperto e, se esiste, Isola Aperta // se non esistono isole, allora Caso 0 if ( pSfrChunk->GetLoopCount( 0) == 1) return 0 ; // Il Loop esterno è considerato chiuso <=> esiste almeno una curva chiusa bool bExtClosed = false ; for ( int nU = 0 ; nU < pSfrChunk->GetLoopCurveCount( 0, 0) && ! bExtClosed ; ++ nU) { int nTempProp = TEMP_PROP_INVALID ; bExtClosed = ( pSfrChunk->GetCurveTempProp( 0, 0, nU, nTempProp) && nTempProp == TEMP_PROP_CLOSE_EDGE) ; } // Le isole vengono classificate come tutte chiuse <=> esiste almeno un isola chiusa bool bIntClosed = false ; for ( int nLoop = 1 ; nLoop < pSfrChunk->GetLoopCount( 0) && ! bIntClosed ; ++ nLoop) { for ( int nU = 0 ; nU < pSfrChunk->GetLoopCurveCount( 0, nLoop) && ! bIntClosed ; ++ nU) { int nTempProp = TEMP_PROP_INVALID ; bIntClosed = ( pSfrChunk->GetCurveTempProp( 0, nLoop, nU, nTempProp) && nTempProp == TEMP_PROP_CLOSE_EDGE) ; } } // restituisco il caso corrente if ( bExtClosed && bIntClosed) return 1 ; if ( bExtClosed && ! bIntClosed) return 2 ; if ( ! bExtClosed && bIntClosed) return 3 ; if ( ! bExtClosed && ! bIntClosed) return 4 ; return -1 ; } //---------------------------------------------------------------------------- static bool AddEpicycles( const PocketParams& PockParam, ICurveComposite* pCompo, ICurveComposite* pCrv, ICurveComposite* pCrvBound) { // verifico che la curva sia valida if ( pCompo == nullptr || ! pCompo->IsValid()) return false ; // calcolo l'Offset definito dal valore dell'raggio dell'epiciclo OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( pCompo, PockParam.dEpicyclesRad, ICurve::OFF_FILLET) || OffsCrv.GetCurveCount() > 1) return false ; PtrOwner pCrvOffs( GetCurveComposite( OffsCrv.GetCurve())) ; if ( IsNull( pCrvOffs)) return false ; // verifico se devo resitituire la curva offsettata if ( pCrvBound != nullptr) pCrvBound->AddCurve( pCrvOffs->Clone()) ; pCrv->Clear() ; double dParPrec = 0. ; for ( int i = 0 ; i < pCompo->GetCurveCount() ; ++ i) { // calcolo distanza epicili specifica per quel tratto double dLen ; pCompo->GetCurve( i)->GetLength( dLen) ; int nStep = max( 1, static_cast( ceil( ( dLen) / PockParam.dEpicyclesDist))) ; double dStep = 1.0 / nStep ; for ( int k = 1 ; k <= nStep ; ++ k) { // creo epiciclo Point3d ptCen ; Vector3d vtDir ; pCompo->GetCurve( i)->GetPointD1D2( k * dStep, ICurve::FROM_MINUS, ptCen, &vtDir) ; vtDir.Normalize() ; vtDir.Rotate( Z_AX, - 90.) ; Point3d pt = ptCen + vtDir * PockParam.dEpicyclesRad ; PtrOwner pCrvArc( CreateCurveArc()) ; pCrvArc->Set( ptCen, Z_AX, PockParam.dEpicyclesRad) ; double dU ; pCrvArc->GetParamAtPoint( pt, dU) ; pCrvArc->ChangeStartPoint( dU) ; // aggiungo tratto della curva offsettata double dPar ; pCrvOffs->GetParamAtPoint( pt, dPar) ; bool bAdd = ( pCrv->AddCurve( pCrvOffs->CopyParamRange( dParPrec, dPar))) ; // aggiungo epiciclo if ( ! pCrv->AddCurve( Release( pCrvArc))) { // se fallisco nell'aggiungere l'epiciclo tento nuovamente spostandolo di EPS_SMALL if ( bAdd) PtrOwner pCrvErased( pCrv->RemoveFirstOrLastCurve( true)) ; k -- ; dStep -= EPS_SMALL ; if ( dStep < EPS_SMALL) return false ; } else dParPrec = dPar ; } } // se necessario ripristino orientamento originale if ( PockParam.bInvert) pCrv->Invert() ; return true ; } //------------------------------------------------------------------------------ static bool CalcBoundedPolishingLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, const Vector3d& vtEnd, const ICurve* pCrvBound, ICurveComposite* pCrvLink) { double dAngStart, dAngEnd ; vtStart.GetAngleXY( X_AX, dAngStart) ; vtEnd.GetAngleXY( X_AX, dAngEnd) ; PtrOwner pBiArcLink( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, 0.5)) ; if ( IsNull( pBiArcLink)) return false ; // verifico se esce dalla svuotatura CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pBiArcLink, *pCrvBound) ; intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; // se nessuno o un solo tratto e interno, il biarco è il collegamento if ( ccClass.empty() || ( ssize( ccClass) == 1 && ccClass[0].nClass == CRVC_IN)) { pCrvLink->AddCurve( Release( pBiArcLink)) ; } // altrimenti creo un percorso con biarchi e opportuni tratti della curva di contenimento else { PtrOwner pCompo( CreateCurveComposite()) ; if ( IsNull( pCompo)) return false ; double dPar1, dPar2 ; Point3d ptMinDist1, ptMinDist2 ; Vector3d vtDir1, vtDir2 ; double dAng1, dAng2 ; int nFlag ; DistPointCurve distPtSCrv( ptStart, *pCrvBound) ; distPtSCrv.GetParamAtMinDistPoint( 0, dPar1, nFlag) ; pCrvBound->GetPointTang( dPar1, ICurve::FROM_MINUS, ptMinDist1, vtDir1) ; vtDir1.GetAngleXY( X_AX, dAng1) ; DistPointCurve distPtECrv( ptEnd, *pCrvBound) ; distPtECrv.GetParamAtMinDistPoint( 0, dPar2, nFlag) ; pCrvBound->GetPointTang( dPar2, ICurve::FROM_MINUS, ptMinDist2, vtDir2) ; vtDir2.GetAngleXY( X_AX, dAng2) ; pCompo->AddCurve( GetBiArc( ptStart, -dAngStart, ptMinDist1, -dAng1, 0.5)) ; // primo biarco pCompo->AddCurve( pCrvBound->CopyParamRange( dPar1, dPar2)) ; // tratto di pCrvBound pCompo->AddCurve( GetBiArc( ptMinDist2, -dAng2, ptEnd, -dAngEnd, 0.5)) ; // secondo biarco pCrvLink->AddCurve( Release( pCompo)) ; } return true ; } //---------------------------------------------------------------------------- static bool ComputePolishingPath( const PocketParams& PockParam, ICRVCOMPOPOVECTOR& vOffs, ICurveComposite* pMCrv) { // controllo dei parametri if ( vOffs.empty() || pMCrv == nullptr) return false ; pMCrv->Clear() ; // Offset con epicicli ICRVCOMPOPOVECTOR vpCrvsEp ; vpCrvsEp.reserve( vOffs.size()) ; // definisco la curva di bordo PtrOwner pCrvBound( CreateCurveComposite()) ; if ( IsNull( pCrvBound)) return false ; // recupero il punto iniziale Point3d ptStartRef ; GetPtStartOnGenericEdge( vOffs[0], PockParam, ptStartRef) ; // scorro le curve di Offset for ( int i = 0 ; i < ssize( vOffs) ; ++ i) { PtrOwner pCrvEp( CreateCurveComposite()) ; if ( IsNull( pCrvEp)) return false ; double dUStart = 0. ; int nFlag = 0 ; DistPointCurve( ptStartRef, *vOffs[i]).GetParamAtMinDistPoint( 0., dUStart, nFlag) ; vOffs[i]->ChangeStartPoint( dUStart) ; // la curva di bound è l'offset che calcolo in AddEpicycles per la prima curva compo trovata in pMCrv if ( ! AddEpicycles( PockParam, vOffs[i], pCrvEp, ( i == 0 ? pCrvBound : nullptr))) return false ; vpCrvsEp.emplace_back( Release( pCrvEp)) ; } // calcolo i collegamenti ICURVEPOVECTOR vLinks( vpCrvsEp.size()) ; for ( int i = 1 ; i < ssize( vpCrvsEp) ; ++ i) { // punti e direzioni di inizio e fine Point3d ptStart ; Vector3d vtStart ; vpCrvsEp[i-1]->GetEndPoint( ptStart) ; vpCrvsEp[i-1]->GetEndDir( vtStart) ; Point3d ptEnd ; Vector3d vtEnd ; vpCrvsEp[i]->GetStartPoint( ptEnd) ; vpCrvsEp[i]->GetStartDir( vtEnd) ; // calcolo il collegamento con biarchi (garantendo che non esca dalla svuotatura) PtrOwner pCrvLink( CreateCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; if ( ! CalcBoundedPolishingLink( ptStart, vtStart, ptEnd, vtEnd, pCrvBound, pCrvLink)) return false ; vLinks[i].Set( pCrvLink) ; } // creo il percorso di lavoro a partire dalla raccolta delle curve con epicicli e dei collegamenti for ( int i = 0 ; i < int( vpCrvsEp.size()) ; ++ i) { // se collegamento da aggiungere if ( ! IsNull( vLinks[i])) { // accodo nel percorso di lavorazione pMCrv->AddCurve( Release( vLinks[i])) ; } // aggiungo la curva pMCrv->AddCurve( Release( vpCrvsEp[i])) ; } return true ; } //---------------------------------------------------------------------------- static bool CalcSpiral( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, int& nReg, Point3d& ptStart, ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops, ICurveComposite* pMCrv, bool& bMidOut, Vector3d& vtMidOut) { // inizializzo il percorso come vuoto pMCrv->Clear() ; // Offset corrente ( il primo è definito dal raggio utensile) double dOffs = PockParams.dRad + PockParams.dRadialOffset ; // se lucidatura if ( PockParams.bPolishing && nReg == 0) dOffs += PockParams.dEpicyclesRad ; // 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 // tengo una copia della regione corrente da svuotare PtrOwner pSrfAct( CloneSurfFlatRegion( pSfrPock)) ; if ( IsNull( pSrfAct) || pSrfAct->GetChunkCount() == 0) return false ; // ricavo il tipo di relazione descritta tra Open/Close di bordo esterno e/o isole int nCase = CalcInversionForSpiralOffset( pSrfAct) ; if ( nCase == -1) return false ; // ricavo le regioni progressive double dOffsPrec = 0. ; int nCrvFirstOffs = 0 ; bool bLastNotValid = false ; while ( nIter < MAX_ITER) { // Offset della regione attuale PtrOwner pSfrOffsVR( pSrfAct->CreateOffsetSurf( - dOffs, ICurve::OFF_FILLET)) ; if ( IsNull( pSfrOffsVR)) return false ; // se la regione sparisce allora riprovo con un Offset leggermente più piccolo if ( ! pSfrOffsVR->IsValid()) { pSfrOffsVR.Set( pSrfAct->CreateOffsetSurf( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) ; if ( IsNull( pSfrOffsVR)) return false ; bLastNotValid = true ; } // se primo Offset if ( nIter == 0) { // aggiorno il nuovo valore delle regioni totali di primo Offset int my_nReg = nReg ; nReg = pSfrOffsVR->GetChunkCount() ; // gli Offset progressivi appartengono al Chunk nReg-esimo pSrfAct.Set( pSfrOffsVR->CloneChunk( my_nReg)) ; // se supero i chunk ottenuti if ( IsNull( pSrfAct)) return true ; // imposto la regione id svuotatura corrente pSfrOffsVR.Set( pSrfAct->Clone()) ; } // numero di Chunk e Loops 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 ; // controllo quali regioni di Offset possono essere sostituite bool bInsert = true ; if ( ! CheckIfOffsetIsNecessary( pSrfAct, pCrvCompoBorder, dOffs, dOffsPrec, nIter, PockParams, bInsert)) return false ; // per evitare di allacciare una curva di regione non svuotata alla prima curva di Offset // ( quindi avere un entrata nel pieno del grezzo) controllo di non eliminare il secondo Offset if ( ! bInsert && nChunks == 1 && bLastNotValid) bInsert = true ; if ( bInsert) { // imposto come secondo TempParam il Side di classificazione pCrvCompoBorder->SetTempParam( j == 0 ? MDS_RIGHT : MDS_LEFT, 1) ; // stabilisco l'orientamento della curva if ( ( nCase == 1 && nIter != 0 && j > 0) || ( nCase == 2 && j > 0) || ( nCase == 3 && j == 0) || ( nCase == 4 && j > 0)) { pCrvCompoBorder->Invert() ; SwapSideBySecondTempParam( pCrvCompoBorder) ; } 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)) ; } } } // ricavo il numero di curve ottenute di primo Offset if ( nIter == 0) nCrvFirstOffs = int( vOffs.size()) ; // 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 non ho Chunks e devo usare un Offset più piccolo... else if ( ! bSmallRad) { if ( PockParams.dRad < EPS_SMALL || PockParams.bPolishing) break ; 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 ; // se lucidatura if ( PockParams.bPolishing && nReg == 1) { // si suppone che gli offset siano tutti concentrici ( già ordinati dall'esterno all'interno) bMidOut = false ; // per definizione di lucidatura return ( ComputePolishingPath( PockParams, vOffs, pMCrv)) ; } // cambio il punto iniziale della prima Curva di Offset Point3d ptRef = PockParams.ptStart ; Point3d ptNewStart ; // Se ho come lavorazione uno SpiralIn posso poter entrare dalle isole aperte... int nIndexSwap = 0 ; if ( PockParams.nType == POCKET_SPIRALIN) { if ( SetAdvancedPtStartForPath( vOffsFirstCurve, PockParams, pSfrPock, ptRef, ptStart, vtMidOut, bMidOut, nIndexSwap, vCrvOrigChunkLoops)) { vOffsFirstCurve[nIndexSwap]->GetStartPoint( ptNewStart) ; } else return false ; } else { if ( SetPtStartForPath( vOffs[0], PockParams, pSfrPock, ptRef, ptStart, vtMidOut, bMidOut, vCrvOrigChunkLoops[0])) vOffs[0]->GetStartPoint( ptNewStart) ; else return false ; } // se richiesta inversione if ( PockParams.bInvert) { for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) { vOffs[i]->Invert() ; SwapSideBySecondTempParam( vOffs[i]) ; } } // smusso le curve di offset ( ad eccezione di quelle di primo Offset) double dSmoothPar = PockParams.dSmooth / SQRT2 ; for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) { if ( i >= nCrvFirstOffs) ModifyCurveToSmoothed( vOffs[i], PockParams, dSmoothPar, dSmoothPar, false) ; vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; } // setto il punto iniziale della svuotatura double dNewUS ; if ( nIndexSwap != 0) swap( vOffs[0], vOffs[nIndexSwap]) ; vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ; vOffs[0]->ChangeStartPoint( dNewUS) ; vOffs[0]->SetTempParam( 0., 0) ; // prima iterazione // riordino le curve e creo i collegamenti ICURVEPOVECTOR vLinks( vOffs.size()) ; if ( ! CreateSpiralPocketingPath( vOffs, vLinks, PockParams, vOffsFirstCurve)) return false ; // controllo eventuali parti non svuotate ( e setto la Feed degli Offset e dei Link)... PtrOwner pSfrUncleared( CreateSurfFlatRegion()) ; if ( GetUnclearedRegionAndSetFeed( vOffsFirstCurve, vOffs, vLinks, pSfrOrig, PockParams, pSfrUncleared)) { // modifico i percorsi if ( ! RemoveUnclearedRegions( pSfrUncleared, vOffs, vOffsFirstCurve, 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( Z_AX) ; 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) { // se interno if ( ccClass[j].nClass == CRVC_IN) { // recupero il tratto 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) ; // se troppo piccolo, lo scarto if ( SqDist( ptS, ptE) < 250 * 250 * SQ_EPS_SMALL) continue ; 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( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // ricavo il numero di Chunk da svuotare if ( pSrfPock == nullptr) return false ; int nChunk = pSrfPock->GetChunkCount() ; // scorro i Chunk for ( int nC = 0 ; nC < nChunk ; ++ nC) { // indice del Chunk da corrente da svuotare int nChunkInd = 0 ; if ( PockParams.ptStart.IsValid()) { // se il punto di riferimento è valido, il Chunk è quello ad esso più vicino DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ; int nMinLoop = 0 ; double dMinPar = 0. ; if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar)) return false ; } // Clono il Chunk attuale come regione FlatRegion PtrOwner pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ; if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid()) 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()) ; if ( IsNull( pMCrv)) return false ; int nRegTot = nReg ; Point3d ptStart ; // cerco le curve originali del chunk cc-esimo ( per casi ottimizzati) ICRVCOMPOPOVECTOR vCrvOrigChunkLoops ; if ( ! GetOptCrvIndex( pSfrOrig, pSfrChunk, PockParams, nReg, vCrvOrigChunkLoops)) return false ; // calcolo il percorso di svuotatura bool bSomeOpen ; Vector3d vtMidOut ; if ( ! CalcSpiral( pSfrChunk, pSfrOrig, PockParams, nRegTot, ptStart, vCrvOrigChunkLoops, pMCrv, bSomeOpen, vtMidOut)) return false ; // se terminate le regioni, esco if ( pMCrv->GetCurveCount() == 0) break ; // passo al chunk originale successivo bool bIsExtended = false ; if ( bSomeOpen) ExtendPath( pMCrv, pSfrOrig, PockParams, vtMidOut, false, GetExtendPathLen( PockParams), bIsExtended) ; // inserisco le curve nel vettore vCrvCompoRes.emplace_back( Release( pMCrv)) ; ++ nReg ; // incremento il numero di regione progressiva // aggiorno il punto di riferimento per il Chunk Successivo vCrvCompoRes.back()->GetEndPoint( PockParams.ptStart) ; } // rimuovo il Chunk dalla regione pSrfPock->EraseChunk( nChunkInd) ; } return true ; } //---------------------------------------------------------------------------- static bool AddSpiralOut( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // ricavo il numero di Chunk da svuotare if ( pSrfPock == nullptr) return false ; int nChunk = pSrfPock->GetChunkCount() ; // ciclo sui chunk della superficie da svuotare for ( int nC = 0 ; nC < nChunk ; ++ nC) { // indice del Chunk da corrente da svuotare int nChunkInd = 0 ; if ( PockParams.ptStart.IsValid()) { // se il punto di riferimento è valido, il Chunk è quello ad esso più vicino DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ; int nMinLoop = 0 ; double dMinPar = 0. ; if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar)) return false ; } // Clono il Chunk attuale come regione FlatRegion PtrOwner pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ; if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid()) 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()) ; if ( IsNull( pMCrv)) return false ; int nRegTot = nReg ; Point3d ptStart ; // cerco la curva originale del chunk cc-esimo ( per casi ottimizzati) ICRVCOMPOPOVECTOR vCrvOrig ; if ( ! GetOptCrvIndex( pSfrOrig, pSfrChunk, PockParams, nReg, vCrvOrig)) return false ; // calcolo il percorso di svuotatura bool bSomeOpen ; Vector3d vtMidOut ; if ( ! CalcSpiral( pSfrChunk, pSfrOrig, PockParams, nRegTot, ptStart, vCrvOrig, pMCrv, bSomeOpen, vtMidOut)) return false ; // se terminate le regioni, esco if ( pMCrv->GetCurveCount() == 0) break ; // passo al chunk originale successivo // inverto il percorso, perchè sono calcolati dall'esterno all'interno (solo nel caso non ottimizzato) pMCrv->Invert() ; // inserisco le curve nel vettore vCrvCompoRes.emplace_back( Release( pMCrv)) ; // incremento il numero di regione progressiva ++ nReg ; // aggiorno il punto di riferimento per il Chunk Successivo vCrvCompoRes.back()->GetEndPoint( PockParams.ptStart) ; } // rimuovo il Chunk dalla regione pSrfPock->EraseChunk( nChunkInd) ; } return true ; } //---------------------------------------------------------------------------- static bool GetZigZagOneWayBorderCrvs( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvRes) { /* Restituisce le curve di Primo Offset dei lati chiusi per i percorsi a ZigZag e OneWay. Vengono calcolate le Feed ed eventuali entrate da fuori per i percorsi ricavati */ // controllo validità della regione if ( pSfrPock == nullptr || ! pSfrPock->IsValid()) return false ; // recupero la superficie limite e calcolo Offset PtrOwner pSfrLimit( CreateSurfFlatRegion()) ; if ( IsNull( pSfrLimit)) return false ; if ( PockParams.SfrLimit.IsValid()) { pSfrLimit.Set( PockParams.SfrLimit.Clone()) ; if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid()) return false ; pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset - 50 * EPS_SMALL, ICurve::OFF_FILLET) ; } // vettore delle curve chiuse Offsettate ICRVCOMPOPOVECTOR vClosedOffs ; // scorro i Chunk e i Loop della regione ( se OneWay potrei avere più Chunks) for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) { for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) { ICRVCOMPOPOVECTOR vClosedOffs_nC ; // loop come curva composita PtrOwner pCrvLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ; if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid()) return false ; // recupero i tratti con proprietà uniformi ICRVCOMPOPOVECTOR vpCrvs ; GetHomogeneousParts( pCrvLoop, PockParams, vpCrvs) ; if ( vpCrvs.size() > 1) { // unisco il primo e l'ultimo se estremi compatibili 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) ; } } // le isole aperte vanno percorse bool bOpenIsland = ( nL > 0 && int( vpCrvs.size()) == 1 && vpCrvs[0]->GetTempProp() == TEMP_PROP_OPEN_EDGE) ; // scorro tutti i tratti omogenei for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { // se tratto chiuso o isola aperta if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE || bOpenIsland) { // Offset per le curve OffsetCurve OffsCrv ; double dOffs = - PockParams.dRad - PockParams.dRadialOffset ; // se isola aperta, allora l'offset è verso il suo interno if ( bOpenIsland) dOffs *= -1 ; if ( OffsCrv.Make( vpCrvs[i], dOffs, ICurve::OFF_FILLET)) { PtrOwner pOffLongestCrv( OffsCrv.GetLongerCurve()) ; while ( ! IsNull( pOffLongestCrv)) { // recupero solo i tratti di curva al di fuori della superficie limite if ( pSfrLimit->IsValid()) { CRVCVECTOR ccClass ; if ( pSfrLimit->GetCurveClassification( *pOffLongestCrv, EPS_SMALL, ccClass)) { for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { if ( ccClass[j].nClass == CRVC_OUT) { // recupero il tratto di curva PtrOwner pCrv( pOffLongestCrv->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ; if ( ! IsNull( pCrv) && pCrv->IsValid()) vClosedOffs_nC.emplace_back( ConvertCurveToComposite( Release( pCrv))) ; } } } } else vClosedOffs_nC.emplace_back( ConvertCurveToComposite( Release( pOffLongestCrv))) ; // passo alla successiva pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ; } } } } // se non ho ricavato curve dal Loop nL-esimo, passo al successivo if ( vClosedOffs_nC.empty()) continue ; // concateno eventuali percorsi if ( ! ChainCompoCurves( vClosedOffs_nC)) return false ; // miglioro le curve for ( auto& crv : vClosedOffs_nC) crv->MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG) ; // determino ora il punto di inizio, il tratto per il LeadIn bool bIsChunkClosed = false ; bool bIsChunkAllOpen = false ; if ( ! IsChunkAllHomogeneous( pSfrPock, nC, bIsChunkClosed, bIsChunkAllOpen)) return false ; if ( ! AdvanceExtendCurves( vClosedOffs_nC, pSfrOrig, PockParams, true)) return false ; // aggiungo le curve ottenute al vettore for ( int i = 0 ; i < int( vClosedOffs_nC.size()) ; ++ i) vClosedOffs.emplace_back( Release( vClosedOffs_nC[i])) ; } } // se non ho percorsi ho finito if ( vClosedOffs.empty()) return true ; // Calcolo ora le Feed e restituisco le curve ottenute for ( int i = 0 ; i < int( vClosedOffs.size()) ; ++ i) { // se ZigZag -> ho percorso i tratti a ZigZag, quindi posso andare a Feed massima if ( PockParams.nType == POCKET_ZIGZAG) AssignMaxFeed( vClosedOffs[i], PockParams) ; // se OneWay -> prima percorro queste curva, quindi la Feed è minima if ( PockParams.nType == POCKET_ONEWAY) AssignMinFeed( vClosedOffs[i], PockParams) ; // assegno la proprità alla curva composita vClosedOffs[i]->SetTempProp( TEMP_PROP_BORDER_CURVE, 0) ; // restituisco le curve ottenute vCrvRes.emplace_back( Release( vClosedOffs[i])) ; } return true ; } //---------------------------------------------------------------------------- static bool AddZigZag( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // ricavo il numero di Chunk da svuotare if ( pSrfPock == nullptr) return false ; int nChunk = pSrfPock->GetChunkCount() ; // offset della regione per curva ZigZag double dOffs = PockParams.dRad + PockParams.dRadialOffset ; if ( PockParams.bAllowZigZagOneWayBorders && ! PockParams.bPolishing) dOffs += PockParams.dOffsExtra ; // ciclo su tutti i chunks della superficie originale for ( int nC = 0 ; nC < nChunk ; ++ nC) { // indice del Chunk da corrente da svuotare int nChunkInd = 0 ; if ( PockParams.ptStart.IsValid()) { // se il punto di riferimento è valido, il Chunk è quello ad esso più vicino DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ; int nMinLoop = 0 ; double dMinPar = 0. ; if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar)) return false ; } // creo la regione per il percorso a ZigZag ( mediante primo Offset) PtrOwner pSrfZigZag( pSrfPock->CloneChunk( nChunkInd)) ; if ( ! pSrfZigZag->Offset( - dOffs, ICurve::OFF_FILLET)) return false ; // vettore con i percorsi a ZigZag relativi al Chunk attuale ICRVCOMPOPOVECTOR vpCrvs ; // ciclo sui Chunk ottenuti dal primo offset for ( int nC1 = 0 ; nC1 < pSrfZigZag->GetChunkCount() ; ++ nC1) { // recupero il Chunk attuale PtrOwner pSrfZigZagChunk( pSrfZigZag->CloneChunk( nC1)) ; if ( IsNull( pSrfZigZagChunk) || ! pSrfZigZagChunk->IsValid()) return false ; // calcolo i percorsi di ZigZag if ( ! CalcZigZag( pSrfZigZagChunk, PockParams, vpCrvs, false)) return false ; // controllo esistenza di lati aperti per il Chunk attuale bool bIsChunkClosed = true ; bool bIsChunkAllOpen = true ; if ( ! PockParams.bAllClosed && ! PockParams.bPolishing) { if ( ! IsChunkAllHomogeneous( pSrfPock, nChunkInd, bIsChunkClosed, bIsChunkAllOpen)) return false ; // se il Chunk originale aveva lati aperti, estendo il percorso a ZigZag for ( int nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU) { Vector3d vtRef ; vpCrvs[nU]->GetStartDir( vtRef) ; vtRef.Invert() ; bool bIsExtended = false ; if ( ! ExtendPath( vpCrvs[nU], pSfrOrig, PockParams, vtRef, false, GetExtendPathLen( PockParams), bIsExtended)) return false ; vpCrvs[nU]->GetEndDir( vtRef) ; if ( ! ExtendPath( vpCrvs[nU], pSfrOrig, PockParams, vtRef, true, GetExtendPathLen( PockParams), bIsExtended)) return false ; } } // se lucidatura if ( PockParams.bPolishing) { // ciclo sui percorsi for ( int k = 0 ; k < int( vpCrvs.size()) ; ++ k) { // se attacco a scivolo if ( PockParams.nLiType == LEAD_IN_GLIDE) { double dU ; vpCrvs[k]->GetParamAtLength( PockParams.dLiTang, dU) ; vpCrvs[k]->AddJoint( dU) ; Point3d ptStart ; vpCrvs[k]->GetStartPoint( ptStart) ; vpCrvs[k]->ModifyStart( ptStart + Z_AX * PockParams.dLiElev) ; } // se uscita a scivolo if ( PockParams.nLoType == LEAD_OUT_GLIDE) { double dLen, dU ; vpCrvs[k]->GetLength( dLen) ; vpCrvs[k]->GetParamAtLength( dLen - PockParams.dLoTang, dU) ; vpCrvs[k]->AddJoint( dU) ; Point3d ptEnd ; vpCrvs[k]->GetEndPoint( ptEnd) ; vpCrvs[k]->ModifyEnd( ptEnd + Z_AX * PockParams.dLiElev) ; } } } // inserisco le curve nel vettore risultante for ( int nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU) vCrvCompoRes.emplace_back( Release( vpCrvs[nU])) ; // libero il vettore di curve ZigZag per il chunk successivo vpCrvs.clear() ; } // se richiesto, aggiungo le curve chiuse di Bordo if ( PockParams.bAllowZigZagOneWayBorders && ! PockParams.bPolishing) { // recupero il Chunk nC-esimo PtrOwner pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ; if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid() || ! GetZigZagOneWayBorderCrvs( pSfrChunk, pSfrOrig, PockParams, vCrvCompoRes)) return false ; } // rimuovo il Chunk dalla regione pSrfPock->EraseChunk( nChunkInd) ; } return true ; } //---------------------------------------------------------------------------- static bool OptimizeChunkOneWay( const ISurfFlatRegion* pSfrOrig, ISURFFRPOVECTOR& vSrfIdeal) { // controllo parametri if ( pSfrOrig == nullptr || pSfrOrig->GetChunkCount() == 0) return false ; vSrfIdeal.clear() ; /* 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( pSfrOrig->GetChunkCount(), 1) ; for ( int nC = 0 ; nC < pSfrOrig->GetChunkCount() ; ++ nC) { PtrOwner pSrfIdeal( CreateSurfFlatRegion()) ; if ( IsNull( pSrfIdeal)) return false ; // se Chunk valido... if ( vChunksAvailable[nC] == 1) { // prendo la curva esterna PtrOwner pCrvExt( pSfrOrig->GetLoop( nC, 0)) ; if ( IsNull( pCrvExt)) return false ; // inserisco il chunk nC-esimo ( curva per curva, così non perdo le temp prop) pSrfIdeal->AddExtLoop( pCrvExt->Clone()) ; for ( int nL = 1 ; nL < pSfrOrig->GetLoopCount( nC) ; ++ nL) pSrfIdeal->AddIntLoop( pSfrOrig->GetLoop( nC, nL)) ; // il chunk nC-esimo non è più disponibile... vChunksAvailable[nC] = -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( pSfrOrig->GetLoop( i, 0)) ; if ( IsNull( pCrvExtC)) return false ; // classifico i bordi esterni ( se il bordo del chunk nC-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 (( int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) || ( int( ccClass1.size()) == 1 && ccClass1[0].nClass == CRVC_IN)) { // inserisco il chunk i-esimo ( curva per curva, così non perdo le temp prop) pSrfIdeal->AddExtLoop( pSfrOrig->GetLoop( i, 0)) ; for ( int nL = 1 ; nL < pSfrOrig->GetLoopCount( i) ; ++ nL) pSrfIdeal->AddIntLoop( pSfrOrig->GetLoop( i, nL)) ; 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) ; i = 0 ; } } } } } if ( pSrfIdeal->GetChunkCount() > 0) vSrfIdeal.emplace_back( Release( pSrfIdeal)) ; } return ( ! vSrfIdeal.empty()) ; } //---------------------------------------------------------------------------- static bool AddOneWay( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // offset della regione per segmenti a ZigZag double dOffs = PockParams.dRad + PockParams.dRadialOffset ; double dExtra = 0. ; if ( PockParams.bAllowZigZagOneWayBorders) dExtra = PockParams.dOffsExtra ; // vettore delle regioni ideali ISURFFRPOVECTOR vSrfFlat ; if ( ! OptimizeChunkOneWay( pSrfPock, vSrfFlat)) return false ; // scorro le regioni ideali for ( int nIs = 0 ; nIs < int( vSrfFlat.size()) ; ++ nIs) { // copio la superficie ideale ed effettuo il primo Offset PtrOwner pSrfIdeal( CloneSurfFlatRegion( vSrfFlat[nIs])) ; if ( IsNull( pSrfIdeal) || ! pSrfIdeal->Offset( - dOffs, ICurve::OFF_FILLET)) return false ; // se sono richieste le curve di Bordo, le aggiungo if ( PockParams.bAllowZigZagOneWayBorders) { if ( ! GetZigZagOneWayBorderCrvs( vSrfFlat[nIs], pSfrOrig, PockParams, vCrvCompoRes)) return false ; } // recupero il Box della superficie attuale da svuotare 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 ( per le Feed) 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 in tratti uniformi di Feed) 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) ; // estendo presso lati aperti se richiesto if ( ! PockParams.bAllClosed) { Vector3d vtRef ; pCrvSegCompo->GetStartDir( vtRef) ; vtRef.Invert() ; bool bIsExtended = false ; if ( ! ExtendPath( pCrvSegCompo, pSfrOrig, PockParams, vtRef, false, GetExtendPathLen( PockParams), bIsExtended)) return false ; pCrvSegCompo->GetEndDir( vtRef) ; if ( ! ExtendPath( pCrvSegCompo, pSfrOrig, PockParams, vtRef, true, GetExtendPathLen( PockParams), bIsExtended)) return false ; } 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 ; } //---------------------------------------------------------------------------- static bool SmoothLinkByOffs( ICurveComposite* pCrvOffs0, ICurveComposite* pCrvOffs1, ICurveComposite* pCrvLink, const PocketParams& PockParams, bool bFirstOffs0, bool bFirstOffs1, double dSmoothPar, double dTol) { // controllo dei parametri if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() || pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid() || pCrvLink == nullptr || ! pCrvLink->IsValid()) return false ; // definisco la lunghezza del segmento iniziale e finale sulle due curve di Offset double dRefLenSeg = 2. * dSmoothPar ; // --- Curva precedente --- // recupero la lunghezza e il dominio del primo Offset double dLen ; pCrvOffs0->GetLength( dLen) ; double dUStart, dUEnd ; pCrvOffs0->GetDomain( dUStart, dUEnd) ; // inizializzo la curva PtrOwner pCrvBef( CreateCurveComposite()) ; if ( IsNull( pCrvBef)) return false ; // se primo Offset di bordo o chiuso... if ( bFirstOffs0 && pCrvOffs0->IsClosed()) { double dU = dUEnd ; if ( dLen > dRefLenSeg - EPS_SMALL) pCrvOffs0->GetParamAtLength( dRefLenSeg, dU) ; PtrOwner pMyCrv( pCrvOffs0->CopyParamRange( dUStart, dU)) ; if ( IsNull( pMyCrv) || ! pMyCrv->IsValid()) return false ; pCrvBef->AddCurve( Release( pMyCrv)) ; } // altrimenti... else { double dU = dUStart ; if ( dLen > dRefLenSeg) pCrvOffs0->GetParamAtLength( dLen - dRefLenSeg, dU) ; PtrOwner pMyCrv( pCrvOffs0->CopyParamRange( dU, dUEnd)) ; if ( IsNull( pMyCrv) || ! pMyCrv->IsValid()) return false ; pCrvBef->AddCurve( Release( pMyCrv)) ; } // se non valida, errore if ( ! pCrvBef->IsValid()) return false ; // --- Curva successiva --- // recupero la lunghezza e il dominio del primo Offset pCrvOffs1->GetLength( dLen) ; pCrvOffs1->GetDomain( dUStart, dUEnd) ; // inizializzo la curva PtrOwner pCrvAft( CreateCurveComposite()) ; if ( IsNull( pCrvAft)) return false ; // calcolo il parametro di taglio double dU = dUEnd ; if ( dLen > dRefLenSeg - EPS_SMALL) pCrvOffs1->GetParamAtLength( dRefLenSeg, dU) ; PtrOwner pMyCrv( pCrvOffs1->CopyParamRange( dUStart, dU)) ; if ( IsNull( pMyCrv) || ! pMyCrv->IsValid()) return false ; pCrvAft->AddCurve( Release( pMyCrv)) ; // se non valida, errore if ( ! pCrvAft->IsValid()) return false ; // estendo il Link ( linea di ingresso, segmento Link, linea d'uscita) pCrvLink->AddCurve( Release( pCrvBef), false, dTol) ; pCrvLink->AddCurve( Release( pCrvAft), true, dTol) ; // miglioro la curva pCrvLink->MergeCurves( 100 * EPS_SMALL, ANG_TOL_STD_DEG) ; // smusso la curva ModifyCurveToSmoothed( pCrvLink, PockParams, dSmoothPar, dSmoothPar, false) ; // toglo gli estremi, il Link è in tangenza e le linee di ingresso e d'uscita sono più // estese dell'arco di smusso creato delete( pCrvLink->RemoveFirstOrLastCurve( false)) ; delete( pCrvLink->RemoveFirstOrLastCurve( true)) ; // modifico il primo Offset per raccordarlo al nuovo Link if ( bFirstOffs0 && pCrvOffs0->IsClosed()) { const ICurve* pCrvFirst = pCrvLink->GetFirstCurve() ; if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid()) return false ; PtrOwner pMyCrv( pCrvFirst->Clone()) ; if ( IsNull( pMyCrv) || ! pMyCrv->IsValid()) return false ; pCrvOffs0->AddCurve( Release( pMyCrv)) ; pCrvLink->RemoveFirstOrLastCurve( false) ; } else { Point3d ptS ; pCrvLink->GetStartPoint( ptS) ; double dUE ; pCrvOffs0->GetParamAtPoint( ptS, dUE, dTol) ; pCrvOffs0->TrimEndAtParam( dUE) ; } // modifico il secondo Offset per raccordarlo al nuovo Link Point3d ptE ; pCrvLink->GetEndPoint( ptE) ; double dUS ; pCrvOffs1->GetParamAtPoint( ptE, dUS, dTol) ; if ( bFirstOffs1 && pCrvOffs1->IsClosed()) pCrvOffs1->ChangeStartPoint( dUS) ; else pCrvOffs1->TrimStartAtParam( dUS) ; return true ; } static bool GetConformalLinkForOpenCrv( const ICurveComposite* pCrvOffs0, const ICurveComposite* pCrvOffs1, const ICRVCOMPOPOVECTOR& vCrvClassBorder, const PocketParams& PockParams, double dTol, ICurveComposite* pCrvLink) { // controllo dei parametri if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() || pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid()) return false ; pCrvLink->Clear() ; // ricavo gli estremi del Link Point3d ptLinkS ; pCrvOffs0->GetEndPoint( ptLinkS) ; Point3d ptLinkE ; pCrvOffs1->GetStartPoint( ptLinkE) ; // ricavo i parametri sulla curva di bordo che interessa i due estremi ( 1 sola !) for ( int i = 0 ; i < int( vCrvClassBorder.size()) ; ++ i) { if ( vCrvClassBorder[i]->IsPointOn( ptLinkS, dTol) && vCrvClassBorder[i]->IsPointOn( ptLinkE, dTol)) { double dPar0 ; vCrvClassBorder[i]->GetParamAtPoint( ptLinkS, dPar0, dTol) ; double dPar1 ; vCrvClassBorder[i]->GetParamAtPoint( ptLinkE, dPar1, dTol) ; // ricavo le due curve possibili di Link PtrOwner pCrvLinkA( ConvertCurveToComposite( vCrvClassBorder[i]->CopyParamRange( dPar0, dPar1))) ; PtrOwner pCrvLinkB( ConvertCurveToComposite( vCrvClassBorder[i]->CopyParamRange( dPar1, dPar0))) ; // scelgo quella migliore double dLenA = 0. ; if ( ! IsNull( pCrvLinkA) && pCrvLinkA->IsValid()) pCrvLinkA->GetLength( dLenA) ; double dLenB = 0. ; if ( ! IsNull( pCrvLinkB) && pCrvLinkB->IsValid()) { pCrvLinkB->Invert() ; pCrvLinkB->GetLength( dLenB) ; } if ( dLenA > dLenB) { if ( ! IsNull( pCrvLinkB) && pCrvLinkB->IsValid()) pCrvLink->CopyFrom( pCrvLinkB) ; } else { if ( ! IsNull( pCrvLinkA) && pCrvLinkA->IsValid()) pCrvLink->CopyFrom( pCrvLinkA) ; } break ; } } return true ; } //---------------------------------------------------------------------------- static bool CalcConformalLink( ICurveComposite* pCrvOffs0, ICurveComposite* pCrvOffs1, PocketParams& PockParams, const ICRVCOMPOPOVECTOR& vCrvClassBorder, ICurveComposite* pCrvLink) { // controllo validità delle due curve di Offset if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() || pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid()) return false ; pCrvLink->Clear() ; const double TOL = 150 * EPS_SMALL ; // flag per curve di Offset aperte o chiuse bool bOpen0 = ( ! pCrvOffs0->IsClosed()) ; bool bOpen1 = ( ! pCrvOffs1->IsClosed()) ; // se passo da una curva aperta ad un'altra curva aperta ( uso i bordi di pSfrClass) if ( bOpen0 && bOpen1) { // ricavo la curva di Link if ( ! GetConformalLinkForOpenCrv( pCrvOffs0, pCrvOffs1, vCrvClassBorder, PockParams, TOL, pCrvLink)) return false ; // smusso il Link raccordandolo if ( pCrvLink->IsValid()) SmoothLinkByOffs( pCrvOffs0, pCrvOffs1, pCrvLink, PockParams, false, false, PockParams.dRad / 8., TOL) ; } else { // clono le due curve di Offset PtrOwner pCrvOffs0_cl( CloneCurveComposite( pCrvOffs0)) ; PtrOwner pCrvOffs1_cl( CloneCurveComposite( pCrvOffs1)) ; if ( IsNull( pCrvOffs0_cl) || IsNull( pCrvOffs1_cl) || ! pCrvOffs0_cl->IsValid() || ! pCrvOffs1_cl->IsValid()) return false ; // controllo se gli Offset sono stati ricavati alla prima iterazione bool bFirstIterOffs0 = ( pCrvOffs0->GetTempProp( 0) == 0) ; bool bFirstIterOffs1 = ( pCrvOffs1->GetTempProp( 0) == 0) ; // definisco nuovo parametro di smusso bool bSmooth = PockParams.bSmooth ; PockParams.bSmooth = ( PockParams.nType == POCKET_CONFORMAL_ONEWAY && bSmooth) ; // cerco il chunk ipotetico che contiene le due curve di Offset INTVECTOR vIndOffs0, vIndOffs1 ; if ( ! GetIndOfHypoteticalChunk( vCrvClassBorder, pCrvOffs0, pCrvOffs1, vIndOffs0, vIndOffs1)) return false ; // se appartengono a due chunk diversi, allora il link non esiste if ( int( vIndOffs0.size()) != int( vIndOffs1.size())) return true ; for ( int i = 0 ; i < int( vIndOffs0.size()) ; ++ i) { if ( vIndOffs0[i] != vIndOffs1[i]) return true ; } // se vuoti, allora il link non esiste if ( vIndOffs0.empty() || vIndOffs1.empty()) // && return true ; // se appartenenti allo stesso chunk, allora creo link lineare ICRVCOMPOPOVECTOR vCrvChunk ; for ( int i = 0 ; i < int( vIndOffs0.size()) ; ++ i) vCrvChunk.emplace_back( vCrvClassBorder[vIndOffs0[i]]->Clone()) ; // se la curva successiva è chiusa, aggiorno il suo punto iniziale a quello a minima distanza if ( ! bOpen1) { Point3d ptE ; pCrvOffs0->GetEndPoint( ptE) ; double dPar ; int nFlag ; if ( DistPointCurve( ptE, *pCrvOffs1).GetParamAtMinDistPoint( 0., dPar, nFlag)) pCrvOffs1->ChangeStartPoint( dPar) ; } if ( ! CutCurveToConnect( pCrvOffs0, pCrvOffs1, vCrvChunk, PockParams, bOpen0 ? 0. : 10 * EPS_SMALL, bOpen1 ? 0. : 10 * EPS_SMALL, pCrvLink) || ! pCrvLink->IsValid()) { // se curva di Link non valida, cerco una strada più semplice pCrvLink->Clear() ; pCrvOffs0->CopyFrom( pCrvOffs0_cl) ; pCrvOffs1->CopyFrom( pCrvOffs1_cl) ; // recupero i vettori tangente iniziali ( le curve sono chiuse ) Vector3d vtS, vtE ; Point3d ptS, ptE ; if ( ! pCrvOffs0->GetStartDir( vtS) || ! pCrvOffs1->GetStartDir( vtE) || ! pCrvOffs0->GetStartPoint( ptS) || ! pCrvOffs1->GetStartPoint( ptE)) return false ; // creo il bi-arco tra esse bool bSpecial = false ; if ( ! CalcBoundedSmoothedLink( ptS, vtS, ptE, vtE, 0.5, vCrvChunk, PockParams, pCrvLink, bSpecial) || ! pCrvLink->IsValid()) return false ; } PockParams.bSmooth = bSmooth ; // nel caso di Conformal ZigZag if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) { // effettuo smusso del tratto lineare SmoothLinkByOffs( pCrvOffs0, pCrvOffs1, pCrvLink, PockParams, bFirstIterOffs0, bFirstIterOffs1, PockParams.dSmooth, TOL) ; // per sicurezza aggiorno i nuovi punti e i nuovi parametri double dUNewE ; Point3d ptStartNext ; pCrvLink->GetEndPoint( ptStartNext) ; pCrvOffs1->GetParamAtPoint( ptStartNext, dUNewE) ; pCrvOffs1->ChangeStartPoint( dUNewE) ; } // nel caso Conformal OneWay, taglio e unisco le curve ricavate else if ( PockParams.nType == POCKET_CONFORMAL_ONEWAY) { // per sicurezza aggiorno i nuovi punti e i nuovi parametri double dUNewS, dUNewE ; Point3d ptS, ptStartNext ; if ( ! pCrvLink->GetStartPoint( ptS) || ! pCrvLink->GetEndPoint( ptStartNext) || ! pCrvOffs0->GetParamAtPoint( ptS, dUNewS) || ! pCrvOffs1->GetParamAtPoint( ptStartNext, dUNewE)) return false ; // imposto il punto iniziale della curva successiva ( i+1 esima) pCrvOffs1->ChangeStartPoint( dUNewE) ; PtrOwner pCrvNewOffs( CloneCurveComposite( pCrvOffs0)) ; if ( dUNewS > EPS_SMALL) { pCrvNewOffs.Set( ConvertCurveToComposite( pCrvOffs0->CopyParamRange( 0, dUNewS))) ; // sostituisco la curva i esima con quella tagliata pCrvOffs0->CopyFrom( pCrvNewOffs) ; } } } return true ; } //---------------------------------------------------------------------------- static bool CheckConformalRetractLink( const ICurveComposite* pCrvOffs0, const ICurveComposite* pCrvOffs1, const ICRVCOMPOPOVECTOR& vCrvClassBorder, const PocketParams& PockParams, bool& bCalcLink) { // controllo dei parametri if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() || pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid()) return false ; bCalcLink = true ; // di base calcolo il Link // recupero gli estremi di un possibile collegamento tra i punti Point3d ptA ; pCrvOffs0->GetEndPoint( ptA) ; Point3d ptB ; pCrvOffs1->GetStartPoint( ptB) ; // se vicini -> no retroazione if ( SqDist( ptA, ptB) < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL) return true ; // se distanti else { // controllo se sono aperte bool bOpen_i = ( ! pCrvOffs0->IsClosed()) ; bool bOpen_ii = ( ! pCrvOffs1->IsClosed()) ; // se entrambe aperte if ( bOpen_i && bOpen_ii) { // calcolo il collegamento tra le due curve PtrOwner pCrvLink( CreateCurveComposite()) ; if ( IsNull( pCrvLink) || ! GetConformalLinkForOpenCrv( pCrvOffs0, pCrvOffs1, vCrvClassBorder, PockParams, 25 * EPS_SMALL, pCrvLink)) return false ; // se Link valido calcolo la sua lunghezza double dLinkLen = 0. ; if ( pCrvLink->IsValid()) pCrvLink->GetLength( dLinkLen) ; // minima distanza da ptA DistPointCurve DistPtACrv( ptA, *pCrvOffs1) ; double dMyDist ; if ( DistPtACrv.GetSqDist( dMyDist) && dMyDist < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL) { bCalcLink = ( dLinkLen < 4 * PockParams.dRad) ; return true ; } // minima distanza da ptB DistPointCurve DistPtBCrv( ptB, *pCrvOffs0) ; if ( DistPtBCrv.GetSqDist( dMyDist) && dMyDist < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL) { bCalcLink = ( dLinkLen < 4 * PockParams.dRad) ; return true ; } } // ... servono altri controlli ??? ( vedere dai casi) } bCalcLink = false ; return true ; } //---------------------------------------------------------------------------- static bool GetConformalIndices( const VICRVCOMPOPOVECTOR& vCrvOffs, int nMyInd0, int nMyInd1, const PocketParams& PockParams, const ICRVCOMPOPOVECTOR& vCrvClassBorder, int& nInd0, int& nInd1) { // controllo che l'indice sia sensato if ( vCrvOffs.empty() || nMyInd0 < 0 || nMyInd0 >= int( vCrvOffs.size()) || ( nMyInd1 < 0 || nMyInd1 >= int( vCrvOffs[nMyInd0].size()))) return false ; nInd0 = -1 ; nInd1 = -1 ; // il punto di riferimento è il punto finale della curva corrente Point3d ptRef ; vCrvOffs[nMyInd0][nMyInd1]->GetEndPoint( ptRef) ; // controllo se la curva è chiusa o aperta bool bIsClosed = ( vCrvOffs[nMyInd0][nMyInd1]->IsClosed()) ; // controllo se la curva è di primo Offset ( in questo caso non ho curve successive) bool bFistOffs = ( nMyInd0 == int( vCrvOffs.size() - 1)) ; // scorro le curve di indice successivo double dLimInfDist = INFINITO ; int nInd = -1 ; for ( int i = 0 ; ! bFistOffs && i < int( vCrvOffs[nMyInd0 + 1].size()) ; ++ i) { // se non attiva, passo alla successiva if ( vCrvOffs[nMyInd0 + 1][i]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE) continue ; // se di topologia differente, passo alla successiva if ( ( bIsClosed != vCrvOffs[nMyInd0 + 1][i]->IsClosed())) continue ; // se richiede retroazione, passo alla successiva bool bCalcLink = true ; if ( ! CheckConformalRetractLink( vCrvOffs[nMyInd0][nMyInd1], vCrvOffs[nMyInd0 + 1][i], vCrvClassBorder, PockParams, bCalcLink)) return false ; if ( ! bCalcLink) continue ; // se aperta if ( ! bIsClosed) { Point3d ptStart ; vCrvOffs[nMyInd0 + 1][i]->GetStartPoint( ptStart) ; double dDist = SqDist( ptRef, ptStart) ; if ( dDist < dLimInfDist) { dLimInfDist = dDist ; nInd = i ; } } // se chiusa else { DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nMyInd0 + 1][i]) ; MinDistPCInfo aInfo ; if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) { double dDist = SqDist( ptRef, aInfo.ptQ) ; if ( dDist < dLimInfDist) { dLimInfDist = dDist ; nInd = i ; vCrvOffs[nMyInd0 + 1][i]->ChangeStartPoint( aInfo.dPar) ; } } } } // se curva trovata, restituisco gli indici if ( nInd != -1) { nInd0 = nMyInd0 + 1 ; nInd1 = nInd ; return true ; } /* se non ho trovato alcuna curva, allora possono verificarsi le seguenti situazioni : - all'indice successivo ho solo curve di topologia differente - ho già percorso tutte le curve topologicamente uguali all'indice successivo - sono una curva di primo Offset - tutte le curve all'indice succesivo di topologia equivalente richiedono una retroazione => devo azzerare nInd0 e ricercare la curva di medesima topologia valida più vicina */ nInd0 = 0 ; nInd = -1 ; for ( ; nInd0 < int( vCrvOffs.size()) ; ++ nInd0) { nInd1 = 0 ; dLimInfDist = INFINITO ; for ( ; nInd1 < int( vCrvOffs[nInd0].size()) ; ++ nInd1) { // se non attiva, passo alla successiva if ( vCrvOffs[nInd0][nInd1]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE) continue ; // se di topologia differente, passo alla successiva if ( ( bIsClosed != vCrvOffs[nInd0][nInd1]->IsClosed())) continue ; // se aperta if ( ! bIsClosed) { Point3d ptStart ; vCrvOffs[nInd0][nInd1]->GetStartPoint( ptStart) ; double dDist = SqDist( ptRef, ptStart) ; if ( dDist < dLimInfDist) { dLimInfDist = dDist ; nInd = nInd1 ; } } // se chiusa else { DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nInd0][nInd1]) ; MinDistPCInfo aInfo ; if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) { double dDist = SqDist( ptRef, aInfo.ptQ) ; if ( dDist < dLimInfDist) { dLimInfDist = dDist ; nInd = nInd1 ; vCrvOffs[nInd0][nInd1]->ChangeStartPoint( aInfo.dPar) ; } } } } // se curva trovata, restituisco gli indici if ( nInd != -1) { nInd1 = nInd ; return true ; } } /* se non ho trovato alcuna curva, allora possono verificarsi le seguenti situazioni : - ho solo curve topologicamente differenti dalla mia - non ho curve attive => devo azzerare nInd0 e ricercare la curva valida più vicina */ nInd0 = 0 ; dLimInfDist = INFINITO ; nInd = -1 ; for ( ; nInd0 < int( vCrvOffs.size()) ; ++ nInd0) { nInd1 = 0 ; for ( ; nInd1 < int( vCrvOffs[nInd0].size()) ; ++ nInd1) { // se non attiva, passo alla successiva if ( vCrvOffs[nInd0][nInd1]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE) continue ; // se la curva successiva è aperta if ( ! vCrvOffs[nInd0][nInd1]->IsClosed()) { Point3d ptStart ; vCrvOffs[nInd0][nInd1]->GetStartPoint( ptStart) ; double dDist = SqDist( ptRef, ptStart) ; if ( dDist < dLimInfDist) { dLimInfDist = dDist ; nInd = nInd1 ; } } // se la curva successiva è chiusa else { DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nInd0][nInd1]) ; MinDistPCInfo aInfo ; if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) { double dDist = SqDist( ptRef, aInfo.ptQ) ; if ( dDist < dLimInfDist) { dLimInfDist = dDist ; nInd = nInd1 ; vCrvOffs[nInd0][nInd1]->ChangeStartPoint( aInfo.dPar) ; } } } } // se curva trovata, restituisco gli indici if ( nInd != -1) { nInd1 = nInd ; return true ; } } // non ho più curve disponibili nInd0 = -1 ; nInd1 = -1 ; return true ; } //---------------------------------------------------------------------------- static bool AdjustCloseEdgesForConformalGuide( ICurveComposite* pCrvCompo, const PocketParams& PockParams) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // se non ho una regione limite, allora non faccio nulla if ( ! PockParams.SfrLimit.IsValid()) return true ; // piccolo Offset per la superficie di classificazione PtrOwner pSfrLimit( CloneSurfFlatRegion( &PockParams.SfrLimit)) ; if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid() || ! pSfrLimit->Offset( 150 * EPS_SMALL, ICurve::OFF_FILLET)) return false ; // recupero le parti omogenee della curva ICRVCOMPOPOVECTOR vpCrvs ; GetHomogeneousParts( pCrvCompo, PockParams, vpCrvs) ; // i tratti aperti che fanno overlap con la regione limite diventano chiusi PtrOwner pCrvFinal( CreateCurveComposite()) ; if ( IsNull( pCrvFinal)) return false ; for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) { // nuovo curva PtrOwner pMyCompo( CreateCurveComposite()) ; if ( IsNull( pMyCompo)) return false ; CRVCVECTOR ccClass ; if ( pSfrLimit->GetCurveClassification( *vpCrvs[i], EPS_SMALL, ccClass)) { for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { // recupero il tratto di curva PtrOwner pMyCurve( ConvertCurveToComposite( vpCrvs[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ; if ( ! IsNull( pMyCurve) && pMyCurve->IsValid()) { // se non esterna alla regione limite, il sottotratto deve essere chiuso if ( ccClass[j].nClass != CRVC_OUT) { for ( int k = 0 ; k < pMyCurve->GetCurveCount() ; ++ k) pMyCurve->SetCurveTempProp( k, TEMP_PROP_CLOSE_EDGE, 0) ; } if ( ! pMyCompo->AddCurve( Release( pMyCurve))) return true ; // non faccio nulla... } } } vpCrvs[i].Set( pMyCompo) ; } if ( ! pCrvFinal->AddCurve( Release( vpCrvs[i]))) return true ; // non faccio nulla... } if ( pCrvFinal->IsValid()) pCrvCompo->CopyFrom( pCrvFinal) ; return ( pCrvCompo != nullptr && pCrvCompo->IsValid()) ; } //---------------------------------------------------------------------------- static bool ModifyConformalStartPoint( ICurveComposite* pCrvFirst, const PocketParams& PockParams, const ISurfFlatRegion* pSfrPock) { // controllo dei parametri if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid() || pSfrPock == nullptr || ! pSfrPock->IsValid()) return false ; // se la curva è aperta, allora non devo fare nulla if ( ! pCrvFirst->IsClosed()) return true ; // ricavo i lati aperti della superficie di classificazione ICRVCOMPOPOVECTOR vCrvOpenEdges ; for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) { for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) { // recupero il Loop PtrOwner pCrvLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ; if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid()) return false ; // recupero le parti omogenee ICRVCOMPOPOVECTOR vpCrvs ; GetHomogeneousParts( pCrvLoop, PockParams, vpCrvs) ; // recupero le parti interne for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) vCrvOpenEdges.emplace_back( Release( vpCrvs[i])) ; } } } // se non ho ricavato tratti aperti, errore if ( vCrvOpenEdges.empty()) return false ; /* NB. - Per i casi previsti dalle lavorazioni CONFORMAL devo trovare solo 1 tratto aperto, quindi considero quello di indice 0 - Tra la curva pCrvFirst e la curva di lato aperto, devo trovare i punti più vicino ( per ora l'algoritmo è euristico, bisogna aggiornarlo con VORONOI) */ // Algoritmo Euristico da sostituire con Voronoi : // 1) Cerco il punto più vicino al lato aperto dato il punto iniziale di pCrvFirst Point3d ptStart ; pCrvFirst->GetStartPoint( ptStart) ; DistPointCurve distCalculator0( ptStart, *vCrvOpenEdges[0]) ; Point3d ptMinDist ; int nFlag ; if ( ! distCalculator0.GetMinDistPoint( 0., ptMinDist, nFlag)) return false ; // 2) Cerco il punto più vicino su pCrvFirst da ptMinDist DistPointCurve distCalculator1( ptMinDist, *pCrvFirst) ; double dMinPar ; if ( ! distCalculator1.GetParamAtMinDistPoint( 0., dMinPar, nFlag)) return false ; // 3) Cambio il punto iniziale di pCrvFirst nel parametro ricavato pCrvFirst->ChangeStartPoint( dMinPar) ; return true ; } //---------------------------------------------------------------------------- static bool CalcConformalOffsAndLinks( VICRVCOMPOPOVECTOR& vvCrvOffs, const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrClass, PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvOffs, ICURVEPOVECTOR& vCrvLink) { // controllo dei parametri if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() || pSfrClass == nullptr || ! pSfrClass->IsValid()) return false ; vCrvOffs.clear() ; vCrvLink.clear() ; if ( vvCrvOffs.empty()) return true ; // rendo attive tutte le curve di Offset mediante prima TmpProp for ( int i = 0 ; i < int( vvCrvOffs.size()) ; ++ i) { for ( int j = 0 ; j < int( vvCrvOffs[i].size()) ; ++ j) vvCrvOffs[i][j]->SetTempProp( TEMP_PROP_CURVE_ACTIVE, 0) ; } /* NB. curve di vCrvOffs TempProp0 -> Se l'offset è attivo/non attivo TempProp1 -> Side {MDS_LEFT, MDS_RIGHT} per identificare l'interno della curva TempParam0 -> { vuoto } ; verrà utilizzato per la Feed TempParam1 -> { vuoto } */ // inverto il vettore degli Offset, in modo da partire dalle curve più distanti dai chiusi reverse( vvCrvOffs.begin(), vvCrvOffs.end()) ; // determino il punto iniziale della prima curva di Offset if ( ! ModifyConformalStartPoint( vvCrvOffs[0][0], PockParams, pSfrChunk)) return false ; /* NB. I link che collegano due curve di Offset aperte devono partire dalla prima e, seguendo il bordo della regione di classificazione ( pSfrClass) arrivare alla seconda. */ /* NB. La regione di classificazione ( pSfrClass) deve tenere conto della regione limite; nel caso di bordi chiusi, bisogna stare a distanza opportuna da essi, non raccordandoci i Links */ /* NB. I link che collegano due curve di Offset chiuse tra di loro vengono calcolati esattamente come per i percorsi SPIRAL, pertanto devo definire un insieme di curve di primo Offset sui quali smussare i Link per evitare di uscire dalla regione di svuotatura */ ICRVCOMPOPOVECTOR vCrvSfrClass ; PtrOwner pSfrClassReal( CloneSurfFlatRegion( pSfrClass)) ; if ( IsNull( pSfrClassReal) || ! pSfrClassReal->IsValid()) return false ; if ( PockParams.SfrLimit.IsValid()) { PtrOwner pSfrLimit( CloneSurfFlatRegion( &PockParams.SfrLimit)) ; if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid() || ! pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET) || ! pSfrClassReal->Subtract( *pSfrLimit)) return false ; } if ( ! GetSfrCrvCompoLoops( pSfrClassReal, vCrvSfrClass)) return false ; /* NB. Se POCKET_CONFORMAL_ZIGZAG : - vCrvCompoOffs contiene gli Offset ordinati - vCrvLinks contiene il link che collega l'Offset (i-1)-esimo con l'Offset (i)-esimo [ per definizione il primo Link è null] [ se tra due Offset non c'è Link, allora esso sarà null] Se POCKET_CONFORMAL_ONEWAY - vCrvCompoOffs contiene gli Offset ordinati - vCrvLinks è un vettore di null vCrvCompoOffs e vCrvLinks avranno la stessa dimensione */ // ------------ Ordino Gli Offset ------------ int nInd0 = 0 ; int nInd1 = 0 ; while ( nInd0 != -1 && nInd1 != -1) { // inserisco la curva attuale nel vettore degli Offset vCrvOffs.emplace_back( CloneCurveComposite( vvCrvOffs[nInd0][nInd1])) ; // salvo come prima TmpProp il numero di Offset che ha generato tale curva vCrvOffs.back()->SetTempProp( int( vvCrvOffs.size()) - nInd0 - 1, 0) ; // disattivo tale curva per la ricerca vvCrvOffs[nInd0][nInd1]->SetTempProp( TEMP_PROP_CURVE_INACTIVE, 0) ; // cerco la curva successiva per il percorso int nNextInd0 = -1 ; int nNextInd1 = -1 ; if ( ! GetConformalIndices( vvCrvOffs, nInd0, nInd1, PockParams, vCrvSfrClass, nNextInd0, nNextInd1)) return false ; // aggiorno gli indici nInd0 = nNextInd0 ; nInd1 = nNextInd1 ; } // se non ho ottenuto nulla, esco if ( vCrvOffs.empty()) return true ; // ------------ Definizione dei Link ------------ // NB. Il link i-esimo collega l'Offset (i-1)-esimo con l'i-esimo // per definizione il primo Link è nullo vCrvLink.resize( 1) ; for ( int i = 0 ; i < int( vCrvOffs.size()) - 1 ; ++ i) { // controllo se bisogna calcolare il Link tra i due Offset bool bCalcLink = true ; // se Conformal ZigZag if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) { // Criteri per creazione di un possibile collegamento if ( ! CheckConformalRetractLink( vCrvOffs[i], vCrvOffs[i+1], vCrvSfrClass, PockParams, bCalcLink)) return false ; } // se Conformal OneWay else { // gli unici link validi per il OneWay sono quelli che collegano due curve chiuse concentriche. // In questo caso si può generare un Biarco dato che l'orientamento degli Offset è lo stesso bCalcLink = ( vCrvOffs[i]->IsClosed() && vCrvOffs[i+1]->IsClosed()) ; if ( bCalcLink) { IntersCurveCurve ICC( *vCrvOffs[i], *vCrvOffs[i+1]) ; CRVCVECTOR ccClass ; bCalcLink = ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) && int( ccClass.size()) == 1) ; if ( bCalcLink) { if ( vCrvOffs[i]->GetTempProp( 1) == MDS_LEFT) bCalcLink = ( ccClass[0].nClass == CRVC_IN) ; else if ( vCrvOffs[i]->GetTempProp( 1) == MDS_RIGHT) bCalcLink = ( ccClass[0].nClass == CRVC_OUT) ; else bCalcLink = false ; } } } // se link da calcolare if ( bCalcLink) { // lo creo e cerco di calcolarlo PtrOwner pCrvLink( CreateCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; // se non calcolabile, allora retroazione, altrimenti lo memorizzo if ( ! CalcConformalLink( vCrvOffs[i], vCrvOffs[i+1], PockParams, vCrvSfrClass, pCrvLink) || IsNull( pCrvLink) || ! pCrvLink->IsValid() || pCrvLink->GetCurveCount() == 0) vCrvLink.resize( int( vCrvLink.size()) + 1) ; else vCrvLink.emplace_back( Release( pCrvLink)) ; } else vCrvLink.resize( int( vCrvLink.size()) + 1) ; } return true ; } //---------------------------------------------------------------------------- static bool ExtendConformalOffsAndSetFeed( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvOffs, ICURVEPOVECTOR& vCrvLinks) { // controllo dei parametri if ( pSfrPock == nullptr || ! pSfrPock->IsValid()) return false ; if ( vCrvOffs.empty()) return true ; // definisco la regione di ricerca delle aree non svuotate come la regione originale PtrOwner pSfrBorder( CloneSurfFlatRegion( pSfrPock)) ; if ( IsNull( pSfrBorder) || ! pSfrBorder->IsValid()) return false ; // dalla superficie originale, rimuovo le parti definite dai lati chiusi double dOffs = PockParams.dRad + PockParams.dRadialOffset - 2. * EPS_SMALL ; ICRVCOMPOPOVECTOR vpCrvs ; for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) { for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) { PtrOwner pCompoLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ; if ( IsNull( pCompoLoop) || ! pCompoLoop->IsValid()) return false ; PtrOwner pSfrToDiscard( GetSurfFlatRegionFromFatCurve( pCompoLoop->Clone(), dOffs, false, false)) ; if ( IsNull( pSfrToDiscard) || ! pSfrToDiscard->IsValid()) return false ; if ( ! pSfrBorder->Subtract( *pSfrToDiscard)) return false ; } } // se non ho nessuna superficie di bordo, allora vuol dire che la regione è più stretta del diametro utensile // ( capita soprattutto se ho un SideStep da rispettare presso gli aperti) // non controllo le regioni rimosse ( dovrebbero essere rimosse dalle passate) if ( pSfrBorder->IsValid()) { ICRVCOMPOPOVECTOR vCrvBorder ; if ( ! GetSfrCrvCompoLoops( pSfrBorder, vCrvBorder)) return false ; // determino eventuale regioni con parti non svuotate e imposto la Feed alle curve PtrOwner pSfrUncleared( CreateSurfFlatRegion()) ; if ( IsNull( pSfrUncleared)) return false ; if ( GetUnclearedRegionAndSetFeed( vCrvBorder, vCrvOffs, vCrvLinks, pSfrOrig, PockParams, pSfrUncleared)) { // estendo i percorsi di Offset se richiesto if ( ! RemoveUnclearedRegions( pSfrUncleared, vCrvOffs, vCrvBorder, PockParams)) return false ; } } else { for ( int i = 0 ; i < ssize( vCrvOffs) ; ++ i) { if ( ! IsNull( vCrvOffs[i]) && vCrvOffs[i]->IsValid()) AssignMaxFeed( vCrvOffs[i], PockParams) ; } for ( int i = 0 ; i < ssize( vCrvLinks) ; ++ i) { if ( ! IsNull( vCrvLinks[i]) && vCrvLinks[i]->IsValid()) { PtrOwner pCompoLink( CreateCurveComposite()) ; if ( IsNull( pCompoLink)) return false ; pCompoLink->AddCurve( Release( vCrvLinks[i])) ; AssignMaxFeed( pCompoLink, PockParams) ; vCrvLinks[i].Set( pCompoLink) ; } } } return true ; } //---------------------------------------------------------------------------- static bool AddLeadInLeadOutToCurveConformalPaths( const ISurfFlatRegion* pSfrOrig, const ISurfFlatRegion* pSfrPock, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvPaths) { // controllo dei parametri if ( pSfrOrig == nullptr || ! pSfrOrig->IsValid() || pSfrPock == nullptr || ! pSfrPock->IsValid()) return false ; for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) { if ( vCrvPaths[i] == nullptr || ! vCrvPaths[i]->IsValid()) return false ; } // ricavo le curve aperte della superficie di pocketing ICRVCOMPOPOVECTOR vCrvLoops ; if ( ! GetSfrCrvCompoLoops( pSfrPock, vCrvLoops)) return false ; ICRVCOMPOPOVECTOR vCrvOpenEdge ; for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) { ICRVCOMPOPOVECTOR vpCrvs ; GetHomogeneousParts( vCrvLoops[i], PockParams, vpCrvs) ; for ( int j = 0 ; j < int( vpCrvs.size()) ; ++ j) { if ( vpCrvs[j]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) vCrvOpenEdge.emplace_back( Release( vpCrvs[j])) ; } } // scorro i percorsi ricavati for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) { // recupero il punto iniziale del percorso Point3d ptStart ; vCrvPaths[i]->GetStartPoint( ptStart) ; // se il punto inziale è interno alla superficie originaria bool bIsInside ; if ( IsPointInsideSurfFr( ptStart, pSfrOrig, EPS_SMALL, bIsInside) && bIsInside) { // definizione del segmento per l'entrata PtrOwner pSeg( CreateCurveLine()) ; if ( IsNull( pSeg)) return false ; double dSqLenMin = INFINITO ; // ricavo il punto più vicino alle curve aperte della superficie di classificazione for ( int j = 0 ; j < int( vCrvOpenEdge.size()) ; ++ j) { DistPointCurve DistPtCrv( ptStart, *vCrvOpenEdge[j]) ; Point3d ptMinDist ; int nFlag ; // controllo se il segmento che congiunge i due estremi non rovina il grezzo bool bSafe = ( DistPtCrv.GetMinDistPoint( 0, ptMinDist, nFlag)) ; // se troppo piccolo, non faccio nulla double dSqSegLen = SqDist( ptStart, ptMinDist) ; if ( dSqSegLen < SQ_EPS_SMALL) { pSeg.Set( CreateCurveLine()) ; break ; } // creazione del segmento if ( bSafe) { PtrOwner pCurrSeg( CreateCurveLine()) ; if ( IsNull( pCurrSeg) || ! pCurrSeg->Set( ptMinDist, ptStart)) return false ; double dSqSegLen = SqDist( ptStart, ptMinDist) ; if ( PockParams.SfrLimit.IsValid()) { PtrOwner pSfrLimit( CloneSurfFlatRegion( &PockParams.SfrLimit)) ; if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid() || ! pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET)) return false ; // controllo distanza segmento CRVCVECTOR ccClass ; bSafe = ( pSfrLimit->GetCurveClassification( *pSeg, 25 * EPS_SMALL, ccClass) && int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) ; } if ( bSafe && dSqSegLen < dSqLenMin) { dSqLenMin = dSqSegLen ; pSeg.Set( pCurrSeg) ; } } } // se segmento trovato, lo aggiungo alla curva if ( pSeg->IsValid()) { if ( ! vCrvPaths[i]->AddCurve( Release( pSeg), false, 25 * EPS_SMALL)) return false ; vCrvPaths[i]->SetCurveTempParam( 0, GetMinFeed( PockParams), 0) ; } } // estendo il percorso sul tratto iniziale e finale ( se ammissibile) Vector3d vtTanS, vtTanE ; vCrvPaths[i]->GetStartDir( vtTanS) ; vtTanS.Invert() ; vCrvPaths[i]->GetEndDir( vtTanE) ; const int MAXTRY = 8 ; bool bOkStartExtension = false, bOkEndExtension = false ; for ( int j = 0 ; j <= MAXTRY ; ++ j) { if ( bOkStartExtension && bOkEndExtension) break ; double dDist = PockParams.dOpenMinSafe + j * ( GetExtendPathLen( PockParams) / ( 1. * MAXTRY)) ; if ( ! bOkStartExtension) { if ( ! ExtendPath( vCrvPaths[i], pSfrOrig, PockParams, vtTanS, false, dDist, bOkStartExtension)) return false ; } if ( ! bOkEndExtension) { if ( ! ExtendPath( vCrvPaths[i], pSfrOrig, PockParams, vtTanE, true, dDist, bOkEndExtension)) return false ; } } } return true ; } //---------------------------------------------------------------------------- static bool OrderAndExtendConformalPaths( ICRVCOMPOPOVECTOR& vCrvPaths, const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams) { // controllo dei parametri if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() || pSfrOrig == nullptr || ! pSfrOrig->IsValid()) return false ; // superficie di controllo per parti isolate PtrOwner pSfrToRemove( CloneSurfFlatRegion( pSfrChunk)) ; if ( IsNull( pSfrToRemove) || ! pSfrToRemove->IsValid() || ! pSfrToRemove->Intersect( *pSfrOrig)) { // riprovo con una leggera tolleranza pSfrToRemove->Offset( - 10 * EPS_SMALL, ICurve::OFF_FILLET) ; if ( ! pSfrToRemove->Intersect( *pSfrOrig)) return false ; } // creo le regioni piane di svuotatura dei percorsi ISURFFRPOVECTOR vSfrRemoved ; vSfrRemoved.resize( vCrvPaths.size()) ; ISURFFRPOVECTOR vSfrRemaining ; vSfrRemaining.reserve( vCrvPaths.size()) ; for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) { // creo la regione di svuotatura di tale percorso vSfrRemoved[i].Set( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vCrvPaths[i]), PockParams.dRad + 500 * EPS_SMALL, false, false)) ; if ( IsNull( vSfrRemoved[i]) || ! vSfrRemoved[i]->IsValid()) return false ; } /* ------------------------ Ordinamento dei percorsi ------------------------ */ // scorro i percorsi ricavati INTVECTOR vInds ; vInds.resize( vCrvPaths.size(), -1) ; int nChunkRef = pSfrToRemove->GetChunkCount() ; for ( int i = 0 ; i < int( vInds.size()) ; ++ i) { int j = 0 ; INTVECTOR vTempInds ; for ( ; j < int( vCrvPaths.size()) ; ++ j) { // se indice già presente if ( find( vInds.begin(), vInds.end(), j) != vInds.end()) continue ; // se sottraendo questa regione a quella originale ottengo più isole, allora non è il percorso ideale PtrOwner pSfrRemain( CloneSurfFlatRegion( pSfrToRemove)) ; if ( IsNull( pSfrRemain) || ! pSfrRemain->IsValid()) continue ; pSfrRemain->Subtract( *vSfrRemoved[j]) ; // se aumento il numero di Chunk, allora non è il percorso ideale if ( pSfrRemain->GetChunkCount() > nChunkRef) continue ; vTempInds.push_back( j) ; } // 1) se non ho trovato curve candidate, prendo l'indice disponibile più basso if ( vTempInds.empty()) { for ( int k = 0 ; k < int( vInds.size()) ; ++ k) { if ( find( vInds.begin(), vInds.end(), k) != vInds.end()) continue ; vInds[i] = k ; break ; } } // 2) se la candidata è singola, allora è lei else if ( int( vTempInds.size()) == 1) vInds[i] = vTempInds[0] ; // 3) se trovate più curve, allora effettuo algoritmi euristici else { // 3.1) cerco la curva che effettua meno overlap con la regione da limite double dMinLen = INFINITO ; double dMinArea = INFINITO ; for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) { // privilegio le aperte if ( vCrvPaths[vTempInds[k]]->IsClosed()) continue ; // se la regione di incidenza è valida if ( PockParams.SfrLimit.IsValid()) { dMinLen = 0. ; // non mi interessa in questo caso la lunghezza della curva PtrOwner pSfrTest( CloneSurfFlatRegion( vSfrRemoved[vTempInds[k]])) ; if ( IsNull( pSfrTest) || ! pSfrTest->IsValid()) return false ; pSfrTest->Intersect( PockParams.SfrLimit) ; double dArea ; pSfrTest->GetArea( dArea) ; if ( dArea < dMinArea) { dMinArea = dArea ; vInds[i] = vTempInds[k] ; if ( dArea < EPS_SMALL) break ; // è ottima... } } // 3.2) se la regione di incidenza non è valida, scelgo la curva più corta else { double dLen ; vCrvPaths[vTempInds[k]]->GetLength( dLen) ; if ( dLen < dMinLen) { dMinLen = dLen ; vInds[i] = vTempInds[k] ; } } } // 3.3) se ho solo curve adiacenti a regioni di incidenza, scelgo quella il cui bordo esterno // contiene più curve di bordo esterno if ( dMinArea > EPS_SMALL) { struct MyCompoClass { PtrOwner pCrvExtLoop ; int nIndex ; int nBorders ; } ; vector vCrvExtBorder( vTempInds.size() ) ; for ( int j = 0 ; j < int( vCrvExtBorder.size()) ; ++ j) { // recupero il bordo esterno (mi auguro sia un solo chunk...) PtrOwner pCrvExtLoop( ConvertCurveToComposite( vSfrRemoved[vTempInds[j]]->GetLoop( 0, 0))) ; if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() || ! vCrvExtBorder[j].pCrvExtLoop.Set( pCrvExtLoop)) return false ; vCrvExtBorder[j].nIndex = vTempInds[j] ; vCrvExtBorder[j].nBorders = 0 ; } for ( int j = 0 ; j < int( vCrvExtBorder.size()) - 1 ; ++ j) { for ( int k = j + 1 ; k < int( vCrvExtBorder.size()) ; ++ k) { IntersCurveCurve ICC( *vCrvExtBorder[j].pCrvExtLoop, *vCrvExtBorder[k].pCrvExtLoop) ; CRVCVECTOR ccClass ; if ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) && int( ccClass.size()) == 1) { // se i-esima interna a j-esima if ( ccClass[0].nClass == CRVC_IN) ++ vCrvExtBorder[k].nBorders ; else if ( ccClass[0].nClass == CRVC_OUT) ++ vCrvExtBorder[j].nBorders ; } } } // ordino il vettore delle curve sulla base di quante ne contiene sort( vCrvExtBorder.begin(), vCrvExtBorder.end(), [] ( const MyCompoClass& a, const MyCompoClass& b) { return a.nBorders > b.nBorders ; }) ; // recupero l'indice migliore if ( ! vCrvExtBorder.empty()) vInds[i] = vCrvExtBorder[0].nIndex ; } // cerco solo tra le chiuse se non ho aperte if ( dMinLen > INFINITO - 1) { for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) { if ( vCrvPaths[vTempInds[k]]->IsClosed()) { double dLen ; vCrvPaths[vTempInds[k]]->GetLength( dLen) ; if ( dLen < dMinLen) { dMinLen = dLen ; vInds[i] = vTempInds[k] ; } } } } } // salvo la regione rimanente ( è già in posizione corretta nel vettore) vSfrRemaining.emplace_back( CloneSurfFlatRegion( pSfrToRemove)) ; // aggiorno la regione da svuotare e il numero di Chunk che si sono formati pSfrToRemove->Subtract( *vSfrRemoved[vInds[i]]) ; nChunkRef = pSfrToRemove->GetChunkCount() ; } /* ------------------------ Estensione dei percorsi ------------------------ */ // ordino percorsi e superfici progressive per estendendere le curve presso gli aperti progressivi ICRVCOMPOPOVECTOR vCrvOrderedPaths ; vCrvOrderedPaths.resize( vCrvPaths.size()) ; ISURFFRPOVECTOR vSfrRemovedOrdered ; vSfrRemovedOrdered.resize( vCrvPaths.size()) ; for ( int i = 0 ; i < int( vInds.size()) ; ++ i) { // ordino la superficie rimossa dal percorso attuale vSfrRemovedOrdered[i].Set( vSfrRemoved[vInds[i]]) ; // ordino il percorso vCrvOrderedPaths[i].Set( vCrvPaths[vInds[i]]) ; } // controllo che i percorsi effettivamente rimuovano del materiale /* NB. Nel caso di lati aperti, può capitare che si formino delle piccole curve ( estese poi per il LeadIn/LeadOut ) che siano interne ad eventuali estensioni per i lati aperti ma che di fatto non svuotano materiale effettivo ( questo accade, perchè gli Offset non sono come per il caso degli Spiral fatti a partire dai bordi, ma sono calcolati tagliando gli offset dei lati chiusi su una superficie estesa di 1.05 * ( dRad + GetOffsR() ) */ int nMax = vCrvOrderedPaths.size() ; vCrvPaths.clear() ; int i = 0 ; for ( ; i < nMax ; ++ i) { // se percorso non valido, passo al successivo if ( IsNull( vCrvOrderedPaths[i]) || ! vCrvOrderedPaths[i]->IsValid()) continue ; // se con il percorso i-esimo ho già rimosso tutta la superficie, allora ho finito if ( IsNull( vSfrRemaining[i]) || ! vSfrRemaining[i]->IsValid()) break ; // se superficie rimossa dal percorso attuale non valida, errore if ( IsNull( vSfrRemovedOrdered[i]) || ! vSfrRemovedOrdered[i]->IsValid()) return false ; // controllo se la superficie rimossa ( + 500 * EPS_SMALL ) interseca la regione da svuotare if ( ! vSfrRemovedOrdered[i]->Intersect( *vSfrRemaining[i]) || IsNull( vSfrRemovedOrdered[i])) return false ; // se valida if ( vSfrRemovedOrdered[i]->IsValid() && vSfrRemovedOrdered[i]->GetChunkCount() > 0) { // il percorso rimuove materiale, aggiungo LeadIn e LeadOut ICRVCOMPOPOVECTOR vCurrPath ; vCurrPath.resize( 1) ; vCurrPath[0].Set( vCrvOrderedPaths[i]) ; AddLeadInLeadOutToCurveConformalPaths( vSfrRemaining[i], pSfrChunk, PockParams, vCurrPath) ; vCrvPaths.emplace_back( Release( vCurrPath[0])) ; } } return true ; } //---------------------------------------------------------------------------- static bool ChainConformalOffsWithLinks( ICRVCOMPOPOVECTOR& vCrvOffs, ICURVEPOVECTOR& vCrvLink, ICRVCOMPOPOVECTOR& vCrvPaths) { // se non ho Offset non devo concatenare nulla if ( vCrvOffs.empty()) return true ; // le curve di Offset devono essere definite e valide for ( int i = 0 ; i < int( vCrvOffs.size()) ; ++ i) { if ( vCrvOffs[i] == nullptr || ! vCrvOffs[i]->IsValid()) return false ; } vCrvPaths.clear() ; // percorso corrente PtrOwner pCrvPath( CreateCurveComposite()) ; if ( IsNull( pCrvPath)) return false ; for ( int i = 0 ; i < int( vCrvOffs.size()) - 1 ; ++ i) { // aggiungo l'Offset corrente if ( ! pCrvPath->AddCurve( Release( vCrvOffs[i]))) return false ; // se il Link per l'Offset successivo è valido, lo aggiungo if ( ! IsNull( vCrvLink[i+1]) && vCrvLink[i+1]->IsValid()) { if ( ! pCrvPath->AddCurve( Release( vCrvLink[i+1]))) { vCrvPaths.emplace_back( Release( pCrvPath)) ; pCrvPath.Set( CreateCurveComposite()) ; if ( IsNull( pCrvPath)) return false ; } } // se Link non valido, ho terminato il percorso corrente else { vCrvPaths.emplace_back( Release( pCrvPath)) ; pCrvPath.Set( CreateCurveComposite()) ; if ( IsNull( pCrvPath)) return false ; } } if ( ! pCrvPath->AddCurve( Release( vCrvOffs.back()))) return false ; vCrvPaths.emplace_back( Release( pCrvPath)) ; return true ; } //---------------------------------------------------------------------------- static bool ExtendGuideByIteration( ICurveComposite* pCompoTempGuide, const ICurveComposite* pCompoPerimeter, bool bInverted) { // controllo dei parametri if ( pCompoTempGuide == nullptr || ! pCompoTempGuide->IsValid() || pCompoPerimeter == nullptr || ! pCompoPerimeter->IsValid()) return false ; const double EXTENSION_VAL = 5000. ; // ricavo il punto finale della guida Point3d ptEndGuide ; pCompoTempGuide->GetEndPoint( ptEndGuide) ; // direzione finale della guida Vector3d vtEndGuide ; pCompoTempGuide->GetEndDir( vtEndGuide) ; // creo un segmento diretto come il chiuso PtrOwner pSeg( CreateCurveLine()) ; if ( IsNull( pSeg) || ! pSeg->Set( ptEndGuide, ptEndGuide + EXTENSION_VAL * vtEndGuide)) return false ; // calcolo l'intersezione tra questo segmento e la curva di perimetro aperta IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ; // se l'unica intersezione è il punto iniziale del segmento, allora non estendo la guida // ( vale anche nel caso di intersezione con Overlap) if ( ICC.GetIntersCount() == 1) { IntCrvCrvInfo aInfo ; if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) { if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide)) return true ; } } // passo alla PolyLine del perimetro PolyLine PL ; pCompoPerimeter->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, PL) ; // cerco il punto più esterno rispetto alla direzione del chiuso Point3d ptPoly ; PL.GetFirstPoint( ptPoly) ; while ( PL.GetNextPoint( ptPoly)) { Vector3d vtDir = ptPoly - ptEndGuide ; vtDir.Normalize() ; if ( ! pSeg->Set( ptEndGuide, ptEndGuide + vtDir * EXTENSION_VAL)) return false ; // calcolo l'intersezione tra questo segmento e la curva di perimetro aperta IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ; // se una intersezione, allora di Overlap con punto iniziale e finale definito if ( ICC.GetIntersCount() == 1) { IntCrvCrvInfo aInfo ; if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) { if ( aInfo.bOverlap) { if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide) && AreSamePointApprox( aInfo.IciB[1].ptI, ptPoly)) return ( pCompoTempGuide->AddLine( ptPoly)) ; } else { if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide)) return ( pCompoTempGuide->AddLine( ptPoly)) ; } } } // se più di una intersezione, allora il perimetro deve essere tutto Interno al segmento // ( Out nel caso in cui ho le curve invertite) IntCrvCrvInfo aInfo ; bool bOk = true ; for ( int i = 0 ; i < ICC.GetIntersCount() && bOk ; ++ i) { bOk = ( ICC.GetIntCrvCrvInfo( i, aInfo)) ; bOk = ( aInfo.bOverlap || ( aInfo.IciB[0].nPrevTy != ( ! bInverted ? CRVC_OUT : CRVC_IN) && aInfo.IciB[0].nNextTy != ( ! bInverted ? CRVC_OUT : CRVC_IN))) ; } if ( bOk) return ( pCompoTempGuide->AddLine( ptPoly)) ; } return true ; } //---------------------------------------------------------------------------- static bool CalcConformalGuide( const ICurveComposite* pCrvCloseEdge, const ICurveComposite* pCrvOpenEdge, const PocketParams& PockParams, ICurveComposite* pCrvGuide) { // controllo dei parametri if ( pCrvCloseEdge == nullptr || ! pCrvCloseEdge->IsValid() || pCrvOpenEdge == nullptr || ! pCrvOpenEdge->IsValid()) return false ; pCrvGuide->Clear() ; // creo una copia temporanea delle curve PtrOwner pCrvCL( CloneCurveComposite( pCrvCloseEdge)) ; PtrOwner pCrvOP( CloneCurveComposite( pCrvOpenEdge)) ; if ( IsNull( pCrvCL) || IsNull( pCrvOP) || ! pCrvCL->IsValid() || ! pCrvOP->IsValid()) return false ; // estendo la curva nel suo tratto finale ed iniziale for ( int i = 0 ; i < 2 ; ++ i) { if ( ! ExtendGuideByIteration( pCrvCL, pCrvOP, ( i == 1))) return false ; if ( ! pCrvCL->IsValid()) return true ; pCrvCL->Invert() ; pCrvOP->Invert() ; // se la curva diventa chiusa, allora ho la guida if ( pCrvCL->IsClosed()) { pCrvGuide->CopyFrom( pCrvCL) ; if ( i == 1) pCrvGuide->Invert() ; return true ; } } // tratto iniziale Vector3d vtStart ; pCrvCL->GetStartDir( vtStart) ; Point3d ptStart ; pCrvCL->GetStartPoint( ptStart) ; PtrOwner pLineStart( CreateCurveLine()) ; if ( IsNull( pLineStart) || ! pLineStart->Set( ptStart - 1000. * vtStart, ptStart)) return false ; // tratto finale ( potrebbe intersecare il tratto iniziale) Vector3d vtEnd ; pCrvCL->GetEndDir( vtEnd) ; Point3d ptEnd ; pCrvCL->GetEndPoint( ptEnd) ; PtrOwner pLineEnd( CreateCurveLine()) ; if ( IsNull( pLineEnd) || ! pLineEnd->Set( ptEnd, ptEnd + 1000. * vtEnd)) return false ; // controllo eventuale intersezione IntersCurveCurve ICC( *pLineStart, *pLineEnd) ; if ( ICC.GetIntersCount() > 0) { // se esiste allora spezzo sia il tratto iniziale che finale nel punto di intersezione IntCrvCrvInfo aInfo ; if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) { pLineStart->TrimStartAtParam( aInfo.IciA[0].dU) ; pLineEnd->TrimEndAtParam( aInfo.IciB[0].dU) ; } } // le due estensioni vanno tagliate presso i bordi dei lati aperti estesi; rischierei di non // riuscire a passare presso dei lati aperti vicini ai tratti lineari di estensione IntersCurveCurve ICCLS( *pLineStart, *pCrvOP) ; if ( ICCLS.GetIntersCount() > 0) { IntCrvCrvInfo aInfo ; for ( int i = 0 ; i < ICCLS.GetCrossIntersCount() ; ++ i) { if ( ICCLS.GetIntCrvCrvInfo( i, aInfo)) { if ( ! AreSamePointApprox( aInfo.IciA[ aInfo.bOverlap ? 1 : 0].ptI, ptStart)) pLineStart->ModifyStart( aInfo.IciA[aInfo.bOverlap ? 1 : 0].ptI) ; } } } IntersCurveCurve ICCLE( *pLineEnd, *pCrvOP) ; if ( ICCLE.GetIntersCount() > 0) { IntCrvCrvInfo aInfo ; for ( int i = 0 ; i < ICCLE.GetCrossIntersCount() ; ++ i) { if ( ICCLE.GetIntCrvCrvInfo( 0, aInfo)) { if ( ! AreSamePointApprox( aInfo.IciA[0].ptI, ptEnd)) pLineEnd->ModifyEnd( aInfo.IciA[0].ptI) ; } } } // aggiungo i tratti lineari di estensione della curva pCrvCL->AddCurve( Release( pLineStart), false) ; pCrvCL->AddCurve( Release( pLineEnd), true) ; // recupero la curva guida pCrvGuide->CopyFrom( pCrvCL) ; return true ; } //---------------------------------------------------------------------------- static bool CombineClosedEdgesForConformalOffsets( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParam, bool& bOk, ICRVCOMPOPOVECTOR& vpCrvs) { // controllo dei parametri if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid()) return false ; vpCrvs.clear() ; bOk = false ; // se la superficie non ha isole, allora non calcolo nulla if ( pSfrChunk->GetLoopCount( 0) == 1) return true ; // recupero il bordo esterno e l'eventuale guida associata ICRVCOMPOPOVECTOR vpCrvHomo ; PtrOwner pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ; if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() || ! AdjustCloseEdgesForConformalGuide( pCrvExtLoop, PockParam) || ! IsCompoMadeBy2DifferentHomogeneousParts( pCrvExtLoop, PockParam, bOk, vpCrvHomo)) return false ; if ( ! bOk) return true ; // recupero la guida ideale PtrOwner pCrvGuide( CreateCurveComposite()) ; if ( IsNull( pCrvGuide) || ! CalcConformalGuide( vpCrvHomo[0], vpCrvHomo[1], PockParam, pCrvGuide)) return false ; if ( ! pCrvGuide->IsValid()) return true ; // la guida definisce il tratto chiuso per gli Offsets vpCrvs.emplace_back( Release( pCrvGuide)) ; // recupero il bordo esterno della superficie originale pCrvExtLoop.Set( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ; if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid()) return false ; // considero questo bordo come tutto aperto for ( int i = 0 ; i < pCrvExtLoop->GetCurveCount() ; ++ i) pCrvExtLoop->SetCurveTempProp( i, 0, TEMP_PROP_OPEN_EDGE) ; // recupero solo le isole chiuse (di sicuro esistono isole) for ( int nL = 1 ; nL < pSfrChunk->GetLoopCount( 0) ; ++ nL) { // recupero la curva dell'isola PtrOwner pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, nL))) ; if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid()) return false ; // se curva tutta chiusa, allora la inserisco come isola bool bAllOpen, bAllClose ; if ( IsCurveCompoHomogeneous( pCrvIsl, bAllClose, bAllOpen) && bAllClose) vpCrvs.emplace_back( Release( pCrvIsl)) ; } return true ; } //---------------------------------------------------------------------------- static bool GetConformalOffsets( const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrClass, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParam, VICRVCOMPOPOVECTOR& vCrvOffs) { // controllo dei parametri if ( pSfrClass == nullptr || ! pSfrClass->IsValid() || pSfrChunk == nullptr || ! pSfrChunk->IsValid()) return false ; vCrvOffs.clear() ; /* NB vCrvOffs è un vettore di vettori di curve composite. Per ogni Offset progressivo i-esimo vengono salvate tutte le parti interne j-esime alla pSfrClass. ( la posizione del vettore ICRVCOMPOPOVECTOR in VICRVCOMPOPOVECTOR è il numero di iterazione progressiva di Offset) */ /* controllo se il chunk è svuotabile con lavorazione conformal Sono previsti due casi di lavorazioni conformal : 1) Non esistono Isole e il bordo esterno è formato da due tratti disomogenei 2) Esistono solo Isole chiuse e il bordo esterno è tutto aperto */ bool bOk = false ; ICRVCOMPOPOVECTOR vCrvCloseEdges ; // tratti chiusi da cui calcolare gli Offset // 1) Non esistono Isole e il bordo esterno è formato da due tratti disomogenei if ( pSfrChunk->GetLoopCount( 0) == 1) { // recupero la curva di bordo ICRVCOMPOPOVECTOR vpCrvs ; PtrOwner pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ; if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() || ! IsCompoMadeBy2DifferentHomogeneousParts( pCrvExtLoop, PockParam, bOk, vpCrvs)) return false ; if ( ! bOk) return true ; // recupero la guida ideale PtrOwner pCrvGuide( CreateCurveComposite()) ; if ( IsNull( pCrvGuide) || ! CalcConformalGuide( vpCrvs[0], vpCrvs[1], PockParam, pCrvGuide)) return false ; if ( ! pCrvGuide->IsValid()) return true ; // la guida definisce il tratto chiuso per gli Offsets vCrvCloseEdges.emplace_back( Release( pCrvGuide)) ; } // 2) Esistono solo Isole chiuse e il bordo esterno è tutto aperto else { if ( ! IsSfrChunkMadeOnlyByClosedIslands( pSfrChunk, PockParam, bOk, vCrvCloseEdges)) return false ; if ( ! bOk) { // 3) provo a combinare punto 1) e 2) if ( ! CombineClosedEdgesForConformalOffsets( pSfrChunk, PockParam, bOk, vCrvCloseEdges)) return false ; if ( ! bOk) return true ; // sarà in SpiralIn/Out... } } /* Calcolo degli Offsets */ // Oggetto Voronoi per curve chiuse Voronoi myVRONI ; for ( int i = 0 ; i < int( vCrvCloseEdges.size()) ; ++ i) { vCrvCloseEdges[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; myVRONI.AddCurve( vCrvCloseEdges[i]) ; } // determino la regione limite PtrOwner pSfrLimit( CreateSurfFlatRegion()) ; if ( IsNull( pSfrLimit)) return false ; if ( PockParam.SfrLimit.IsValid()) { if ( ! pSfrLimit.Set( PockParam.SfrLimit.Clone()) || ! pSfrLimit->IsValid() || ! pSfrLimit->Offset( PockParam.dRad + PockParam.dRadialOffset - 5 * EPS_SMALL, ICurve::OFF_FILLET)) return false ; } int MAX_ITER = 1000 ; int nIter = 0 ; double dOffs = PockParam.dRad + PockParam.dRadialOffset ; PtrOwner pSfrToRemove( CloneSurfFlatRegion( pSfrOrig)) ; if ( IsNull( pSfrToRemove) || ! pSfrToRemove->IsValid()) return false ; while ( nIter < MAX_ITER) { // determino se le curve vanno invertite o meno // essendo FatCurve, la parte interna alla regione di classificazione è invertita bool bInvert = false ; if ( PockParam.nType == POCKET_CONFORMAL_ZIGZAG) bInvert = ! ( ( ! IsEven( nIter) && ! PockParam.bInvert) || ( IsEven( nIter) && PockParam.bInvert)) ; else if ( PockParam.nType == POCKET_CONFORMAL_ONEWAY) bInvert = ! ( PockParam.bInvert) ; // recupero gli Offset dei tratti chiusi ICURVEPOVECTOR vFatCrv ; if ( ! myVRONI.CalcFatCurve( vFatCrv, dOffs, false, false)) return false ; // vettore di Curve dentro alla regione di classificazione ICRVCOMPOPOVECTOR vCrvOffsInside ; // per tutte le curve ottenute, tengo solo per le parti interne alla regione di classificazione bool bStop = true ; for ( int j = 0 ; j < int( vFatCrv.size()) ; ++ j) { CRVCVECTOR ccClass ; if ( ! pSfrClass->GetCurveClassification( *vFatCrv[j], EPS_SMALL, ccClass)) return false ; for ( int k = 0 ; k < int( ccClass.size()) ; ++ k) { if ( ccClass[k].nClass == CRVC_IN) { // recupero il tratto di curva PtrOwner pMyCrv( vFatCrv[j]->CopyParamRange( ccClass[k].dParS, ccClass[k].dParE)) ; if ( ! IsNull( pMyCrv) && pMyCrv->IsValid()) { if ( ! IsNull( pSfrLimit) && pSfrLimit->IsValid()) { // controllo che tale tratto sia esterno alla regione di incidenza CRVCVECTOR ccClass1 ; if ( ! pSfrLimit->GetCurveClassification( *pMyCrv, EPS_SMALL, ccClass1)) return false ; for ( int kk = 0 ; kk < int( ccClass1.size()) ; ++ kk) { if ( ccClass1[kk].nClass != CRVC_IN) { PtrOwner pMyCrv1( pMyCrv->CopyParamRange( ccClass1[kk].dParS, ccClass1[kk].dParE)) ; if ( ! IsNull( pMyCrv1) && pMyCrv1->IsValid()) { // almeno una curva in trovata bStop = false ; // inverto se necessario if ( bInvert) pMyCrv1->Invert() ; // salvo come seconda proprietà temporanea il lato interno pMyCrv1->SetTempProp( bInvert ? MDS_LEFT : MDS_RIGHT, 1) ; // memorizzo la curva nel vettore vCrvOffsInside.emplace_back( ConvertCurveToComposite( Release( pMyCrv1))) ; } } } } else { // almeno una curva in trovata bStop = false ; // inverto se necessario if ( bInvert) pMyCrv->Invert() ; // salvo come seconda proprietà temporanea il lato interno pMyCrv->SetTempProp( bInvert ? MDS_LEFT : MDS_RIGHT, 1) ; // memorizzo la curva nel vettore vCrvOffsInside.emplace_back( ConvertCurveToComposite( Release( pMyCrv))) ; } } } } } // concateno se necessario ( per tolleranza Offset) int nTempProp0 = ( ! vCrvOffsInside.empty() ? vCrvOffsInside[0]->GetTempProp( 0) : TEMP_PROP_INVALID) ; int nTempProp1 = ( ! vCrvOffsInside.empty() ? vCrvOffsInside[0]->GetTempProp( 1) : TEMP_PROP_INVALID) ; if ( ! ChainCompoCurves( vCrvOffsInside)) return false ; for ( auto& CrvCompo : vCrvOffsInside) { CrvCompo->SetTempProp( nTempProp0, 0) ; CrvCompo->SetTempProp( nTempProp1, 1) ; } // se ho trovato delle curve interne if ( ! bStop) { // inserisco le curva ricavate all'iterazione nIter nel vettore vCrvOffs.resize( ++ nIter) ; // incremento nIter for ( int i = 0 ; i < int( vCrvOffsInside.size()) ; ++ i) { vCrvOffsInside[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; // recupero la regione rimossa da tale curva PtrOwner pSfrFatCurve( GetSurfFlatRegionFromFatCurve( vCrvOffsInside[i]->Clone(), PockParam.dRad, false, false)) ; if ( IsNull( pSfrFatCurve) || ! pSfrFatCurve->IsValid()) return false ; if ( AreOppositeVectorApprox( pSfrFatCurve->GetNormVersor(), Z_AX)) pSfrFatCurve->Invert() ; // controllo se tale regione interserca quella da rimuovere PtrOwner pSfrFatCurveCL( CloneSurfFlatRegion( pSfrFatCurve)) ; if ( IsNull( pSfrFatCurveCL) || ! pSfrFatCurveCL->IsValid()) return false ; if ( pSfrToRemove->IsValid()) { pSfrFatCurveCL->Intersect( *pSfrToRemove) ; // se esiste l'intersezione if ( pSfrFatCurveCL->IsValid()) { // conservo la curva vCrvOffs.back().emplace_back( Release( vCrvOffsInside[i])) ; // aggiorno la regione da rimuovere pSfrToRemove->Subtract( *pSfrFatCurve) ; } } } // se non ho inserito nulla, allora ho finito if ( vCrvOffs.back().empty()) { vCrvOffs.pop_back() ; break ; } // aggiorno l'Offset progressivo dOffs += PockParam.dSideStep ; } // se non ho ricavato curve interne, allora esco else break ; } // smusso le curve di Offset ( ad eccezione di quelle generate alla prima iterazione) for ( int i = 1 ; i < int( vCrvOffs.size()) ; ++ i) { for ( int j = 0 ; j < int( vCrvOffs[i].size()) ; ++ j) ModifyCurveToSmoothed( vCrvOffs[i][j], PockParam, PockParam.dSmooth, PockParam.dSmooth, false) ; } return true ; } //---------------------------------------------------------------------------- static bool CalcSpiralPocketing( const ISurfFlatRegion* pSfr, int nType, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // verifica validità della superficie if ( pSfr == nullptr || ! pSfr->IsValid()) return false ; // il tipo può essere solo SpiralIn o SpiralOut if ( nType != POCKET_SPIRALIN && nType != POCKET_SPIRALOUT) return false ; PtrOwner pSfrLimit( PockParams.SfrLimit.IsValid() ? PockParams.SfrLimit.Clone() : CreateSurfFlatRegion()) ; // calcolo il percorso di svuotatura spiral return ( CalcPocketing( pSfr, PockParams.dRad, PockParams.dRadialOffset, PockParams.dSideStep, PockParams.dAngle, PockParams.dOpenMinSafe, nType, PockParams.bSmooth, PockParams.bCalcUnclearedRegs, PockParams.bInvert, PockParams.bAvoidOpt, PockParams.bConventionalMilling, PockParams.bAllowZigZagOneWayBorders, PockParams.bCalcFeed, PockParams.ptStart, pSfrLimit, PockParams.bAvoidOpt, PockParams.dMaxOptSize, PockParams.nLiType, PockParams.dLiTang, PockParams.dLiElev, PockParams.nLoType, PockParams.dLoTang, PockParams.bPolishing, PockParams.dEpicyclesRad, PockParams.dEpicyclesDist, vCrvCompoRes)) ; } //---------------------------------------------------------------------------- static bool GetOrigChunkForConformal( const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrOrig, set& setChunks) { // NB. funzione migliorabile... potrei calcolare tutto all'inizio senza ripetere le intersezioni // controllo validità delle regioni if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() || pSfrOrig == nullptr || ! pSfrOrig->IsValid()) return false ; // se la superficie originale ha un solo Chunk allora è lui stesso if ( pSfrOrig->GetChunkCount() == 1) { setChunks.insert( 0) ; return true ; } // essendo la pSfrChunk estesa, cerco quale chunk di pSfrOrig ha una intersezione valida for ( int nC = 0 ; nC < pSfrOrig->GetChunkCount() ; ++ nC) { // se il Chunk è già stato analizzato in precedenza, passo al successivo if ( setChunks.find( nC) != setChunks.end()) continue ; // recupero il Chunk corrente PtrOwner pSfrChunkOrig( pSfrOrig->CloneChunk( nC)) ; if ( IsNull( pSfrChunkOrig) || ! pSfrChunkOrig->IsValid()) return false ; // controllo se esiste l'intersezione if ( pSfrChunkOrig->Intersect( *pSfrChunk) && pSfrChunkOrig->IsValid()) setChunks.insert( nC) ; } return true ; } //---------------------------------------------------------------------------- static bool CreateSurfFromOtherChunks( ISurfFlatRegion* pSfr, const ISurfFlatRegion* pSfrOther, const set& setChunks) { // controllo della regione if ( pSfr == nullptr || pSfrOther == nullptr || ! pSfrOther->IsValid()) return false ; // se non ho indici di Chunk non faccio nulla if ( setChunks.empty()) return true ; // scorro gli indici dei Chunks for ( auto it = setChunks.begin() ; it != setChunks.end() ; ++ it) { // recupero l'indice del Chunk int nChunk = *it ; // recupero il Chunk come regione piana PtrOwner pSfrChunk( pSfrOther->CloneChunk( nChunk)) ; if ( ! IsNull( pSfrChunk) && pSfrChunk->IsValid()) { if ( ! pSfr->IsValid()) pSfr->CopyFrom( pSfrChunk) ; else if ( ! pSfr->Add( *pSfrChunk)) return false ; } } return true ; } //---------------------------------------------------------------------------- static bool AddConformal( ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // controllo dei parametri if ( pSfrPock == nullptr || ! pSfrPock->IsValid() || pSfrOrig == nullptr || ! pSfrOrig->IsValid()) return true ; // se superifice tutta aperta, lavoro in SPIRAL_IN if ( PockParams.bAllOpen) return ( CalcSpiralPocketing( pSfrOrig, POCKET_SPIRALIN, PockParams, vCrvCompoRes)) ; // se superficie tutta chiusa, lavoro in SPIRAL_OUT if ( PockParams.bAllClosed) return ( CalcSpiralPocketing( pSfrOrig, POCKET_SPIRALOUT, PockParams, vCrvCompoRes)) ; // definisco eventuali regioni da lavorare in SpiralIn e SpiralOut PtrOwner pSfrSpiralIn( CreateSurfFlatRegion()) ; PtrOwner pSfrSpiralOut( CreateSurfFlatRegion()) ; if ( IsNull( pSfrSpiralIn) || IsNull( pSfrSpiralOut)) return false ; // NB. La supercicie pSfrPock è estesa presso i lati aperti, quindi il suo numero di Chunk potrebbe // essere differente dal numero di Chunk della superficie originale di svuotatura. Tengo un insieme di // di indici dei Chunk che devono essere lavorati in SpiralIn e in SpiralOut set setIndChunkSpiralIn ; set setIndChunkSpiralOut ; // scorro i chunk della superficie da lavorare for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) { // controllo se il Chunk ha tutte proprietà omogenee tra loro bool bClose, bOpen ; if ( ! IsChunkAllHomogeneous( pSfrPock, nC, bClose, bOpen)) return false ; // recupero il Chunk come regione piana PtrOwner pSfrChunk( pSfrPock->CloneChunk( nC)) ; if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid()) return false ; // --- se Chunk tutto aperto, aggiorno i Chunk della superficie originale da lavorare in SpiralIn if ( bOpen) { if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn)) return false ; continue ; } // --- se Chunk tutto chiuso, lo lavoro in SPIRAL_OUT else if ( bClose) { if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralOut)) continue ; } // --- se chunk non omogoneo, ricavo gli Offset ( se possibili) dei tratti chiusi del Chunk VICRVCOMPOPOVECTOR vvCrvOffs ; if ( ! GetConformalOffsets( pSfrChunk, pSfrChunk, pSfrOrig, PockParams, vvCrvOffs)) return false ; // se non ottengo Curve di Offset, lavoro in SpiralIn ( il Chunk ha dei lati aperti) if ( vvCrvOffs.empty()) { if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn)) return false ; continue ; } // definisco i vettori ordinati degli Offset e dei Link ICRVCOMPOPOVECTOR vCrvOffs ; ICURVEPOVECTOR vCrvLink ; if ( ! CalcConformalOffsAndLinks( vvCrvOffs, pSfrChunk, pSfrPock, PockParams, vCrvOffs, vCrvLink)) { if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn)) return false ; continue ; } // flag per controllo bool bOk = true ; // estendo i percorsi per eventuali regioni non svuotate e calcolo le Feed bOk = bOk && ExtendConformalOffsAndSetFeed( pSfrPock, pSfrOrig, PockParams, vCrvOffs, vCrvLink) ; // concateno Offset e Links ICRVCOMPOPOVECTOR vCrvPaths ; bOk = bOk && ChainConformalOffsWithLinks( vCrvOffs, vCrvLink, vCrvPaths) ; // estendo per lati aperti ed ordino i percorsi trovati bOk = bOk && OrderAndExtendConformalPaths( vCrvPaths, pSfrChunk, pSfrOrig, PockParams) ; // se qualche passaggio restituisce errore, provo in SpiralIn if ( ! bOk) { if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn)) return false ; continue ; } // altrimenti aggiungo i percorsi ricavati for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) vCrvCompoRes.emplace_back( Release( vCrvPaths[i])) ; } // se ho superfici da lavorare in SpiralIn, aggiungo le curve if ( ! setIndChunkSpiralIn.empty()) { // costruisco la regione di SpiralIn PtrOwner pSfrSpiralIn( CreateSurfFlatRegion()) ; if ( IsNull( pSfrSpiralIn) || ! CreateSurfFromOtherChunks( pSfrSpiralIn, pSfrOrig, setIndChunkSpiralIn)) return false ; // recupero le curve di Pocketing SpiralIn ICRVCOMPOPOVECTOR vCrvSpiralIn ; if ( ! CalcSpiralPocketing( pSfrSpiralIn, POCKET_SPIRALIN, PockParams, vCrvSpiralIn)) return false ; for ( int i = 0 ; i < int( vCrvSpiralIn.size()) ; ++ i) { if ( vCrvSpiralIn[i] != nullptr && vCrvSpiralIn[i]->IsValid()) vCrvCompoRes.emplace_back( Release( vCrvSpiralIn[i])) ; } } // se ho superfici da lavorare in SpiralOut, aggiungo le curve if ( ! setIndChunkSpiralOut.empty()) { // costruisco la regione di SpiralOut PtrOwner pSfrSpiralOut( CreateSurfFlatRegion()) ; if ( IsNull( pSfrSpiralIn) || ! CreateSurfFromOtherChunks( pSfrSpiralOut, pSfrOrig, setIndChunkSpiralOut)) return false ; // recupero le curve di Pocketing SpiralOut ICRVCOMPOPOVECTOR vCrvSpiralOut ; if ( ! CalcSpiralPocketing( pSfrSpiralOut, POCKET_SPIRALOUT, PockParams, vCrvSpiralOut)) return false ; for ( int i = 0 ; i < int( vCrvSpiralOut.size()) ; ++ i) { if ( vCrvSpiralOut[i] != nullptr && vCrvSpiralOut[i]->IsValid()) vCrvCompoRes.emplace_back( Release( vCrvSpiralOut[i])) ; } } return true ; } //---------------------------------------------------------------------------- static bool SmoothExtensionLinesByIntersection( ICRVCOMPOPOVECTOR& vCrvPaths, const PocketParams& PockParams) { // estendendo i percorsi con tratti in ingresso e in uscita, può capitare che si creino autointersezioni // se ho meno di due percorsi non faccio nulla if ( int( vCrvPaths.size()) < 2) return true ; // cerco le autointersezioni tra i percorsi successivi tra loro double dTol = 50 * EPS_SMALL ; for ( int i = 0 ; i < int( vCrvPaths.size()) - 1 ; ++ i) { // se uno dei due percorsi presenta meno di 3 curve, non faccio nulla if ( ! vCrvPaths[i]->IsValid() || vCrvPaths[i]->GetCurveCount() < 3 || ! vCrvPaths[i+1]->IsValid() || vCrvPaths[i+1]->GetCurveCount() < 3) continue ; // se il percorso i-esimo non presenta una estensione in uscita e il percorso (i+1)-esimo // non presenta una estensione in entrata, non faccio nulla if ( vCrvPaths[i]->GetLastCurve()->GetTempProp() != TEMP_PROP_OUT_START || vCrvPaths[i+1]->GetFirstCurve()->GetTempProp() != TEMP_PROP_OUT_START) continue ; // recupero i due tratti lineari del percorso const ICurveLine* pLineA = GetCurveLine( vCrvPaths[i]->GetLastCurve()) ; const ICurveLine* pLineB = GetCurveLine( vCrvPaths[i+1]->GetFirstCurve()) ; if ( pLineA == nullptr || ! pLineA->IsValid() || pLineB == nullptr || ! pLineB->IsValid()) continue ; // se non esistono intersezioni tra i segmenti, non faccio nulla IntersCurveCurve ICC( *pLineA, *pLineB) ; if ( ICC.GetIntersCount() == 0) continue ; // definisco la curva di collegamento PtrOwner pCrvLink( CreateCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; Point3d ptS_A ; pLineA->GetStartPoint( ptS_A) ; pCrvLink->AddPoint( ptS_A) ; IntCrvCrvInfo aInfo ; if ( ! ICC.GetIntCrvCrvInfo( 0, aInfo)) continue ; pCrvLink->AddLine( aInfo.IciA[0].ptI) ; if ( aInfo.bOverlap) pCrvLink->AddLine( aInfo.IciA[1].ptI) ; Point3d ptE_B ; pLineB->GetEndPoint( ptE_B) ; pCrvLink->AddLine( ptE_B) ; // se tutti i punti sono coincidenti, allora non ho bisogno di entrambe le estensioni if ( ! pCrvLink->IsValid() || pCrvLink->GetCurveCount() == 0) { delete( vCrvPaths[i]->RemoveFirstOrLastCurve( true)) ; delete( vCrvPaths[i+1]->RemoveFirstOrLastCurve( false)) ; vCrvPaths[i]->AddCurve( Release( vCrvPaths[i+1]), dTol) ; vCrvPaths.erase( vCrvPaths.begin() + i + 1) ; -- i ; continue ; } // raccordo del Link con percorso attuale e successivo double dLen ; double dLenExtension ; pLineA->GetLength( dLenExtension) ; if ( vCrvPaths[i]->GetLength( dLen) && ( dLen - dLenExtension) > PockParams.dRad / 2.) { double dUS, dUE ; vCrvPaths[i]->GetDomain( dUS, dUE) ; double dU ; vCrvPaths[i]->GetParamAtLength( dLen - dLenExtension - ( PockParams.dRad / 2.), dU) ; pCrvLink->AddCurve( ConvertCurveToComposite( vCrvPaths[i]->CopyParamRange( dU, dUE - 1)), false, dTol) ; ; } pLineB->GetLength( dLenExtension) ; if ( vCrvPaths[i+1]->GetLength( dLen) && ( dLen - dLenExtension) > PockParams.dRad / 2.) { double dUS, dUE ; vCrvPaths[i+1]->GetDomain( dUS, dUE) ; double dU ; vCrvPaths[i+1]->GetParamAtLength( dLenExtension + ( PockParams.dRad / 2.), dU) ; pCrvLink->AddCurve( ConvertCurveToComposite( vCrvPaths[i+1]->CopyParamRange( dUS + 1, dU)), true, dTol) ; } pCrvLink->MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG) ; ModifyCurveToSmoothed( pCrvLink, PockParams, PockParams.dRad / 8., PockParams.dRad / 8., false) ; // assegno Feed massima AssignMaxFeed( pCrvLink, PockParams) ; // modifico i percorsi Point3d ptLinkS ; pCrvLink->GetStartPoint( ptLinkS) ; Point3d ptLinkE ; pCrvLink->GetEndPoint( ptLinkE) ; double dUE, dUS ; if ( ! vCrvPaths[i]->GetParamAtPoint( ptLinkS, dUE, dTol) || ! vCrvPaths[i]->TrimEndAtParam( dUE) || ! vCrvPaths[i+1]->GetParamAtPoint( ptLinkE, dUS, dTol) || ! vCrvPaths[i+1]->TrimStartAtParam( dUS)) continue ; vCrvPaths[i]->AddCurve( Release( pCrvLink), dTol) ; vCrvPaths[i]->AddCurve( Release( vCrvPaths[i+1]), dTol) ; vCrvPaths.erase( vCrvPaths.begin() + i + 1) ; -- i ; } return true ; } //---------------------------------------------------------------------------- bool CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double dStep, double dAngle, double dOpenMinSafe, int nType, bool bSmooth, bool bCalcUnclReg, bool bInvert, bool bAvoidOpt, bool bConventionalMilling, bool bAllowZigZagOneWayBorders, bool bCalcFeed, const Point3d& ptEndPrec, const ISurfFlatRegion* pSfrLimit, bool bAllOffs, double dMaxOptSize, int nLiType, double dLiTang, double dLiElev, int nLoType, double dLoTang, bool bPolishing, double dEpicyclesRad, double dEpicyclesDist, 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 && nType != POCKET_CONFORMAL_ZIGZAG && nType != POCKET_CONFORMAL_ONEWAY)) 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.dOpenMinSafe = dOpenMinSafe ; myParams.bCalcUnclearedRegs = bCalcUnclReg ; myParams.bInvert = bInvert ; myParams.bAvoidOpt = bAvoidOpt ; myParams.bCalcFeed = bCalcFeed ; myParams.bConventionalMilling = bConventionalMilling ; myParams.bAllowZigZagOneWayBorders = bAllowZigZagOneWayBorders ; myParams.bOptOffsets = ( ! bAllOffs) ; myParams.dMaxOptSize = dMaxOptSize ; myParams.nLiType = nLiType ; myParams.dLiTang = dLiTang ; myParams.dLiElev = dLiElev ; myParams.nLoType = nLoType ; myParams.dLoTang = dLoTang ; myParams.bPolishing = bPolishing ; myParams.dEpicyclesRad = dEpicyclesRad ; myParams.dEpicyclesDist = dEpicyclesDist ; if ( ptEndPrec.IsValid()) myParams.ptStart = ptEndPrec ; if ( pSfrLimit != nullptr && pSfrLimit->IsValid()) myParams.SfrLimit.CopyFrom( pSfrLimit) ; Point3d ptCenter ; pSfr->GetCentroid( ptCenter) ; if ( ! myParams.frLocXY.Set( ptCenter, pSfr->GetNormVersor()) || ! myParams.frLocXY.Rotate( ptCenter, pSfr->GetNormVersor() , myParams.dAngle)) return false ; // porto la superficie da svuotare nel sistema di riferimento lungo Z_AX PtrOwner pSfrAdj( pSfr->Clone()) ; if ( IsNull( pSfrAdj) || ! pSfrAdj->ToLoc( myParams.frLocXY)) return false ; // porto la superficie limite ( se valida) in tale sistema if ( myParams.SfrLimit.IsValid()) myParams.SfrLimit.ToLoc( myParams.frLocXY) ; // porto il punto iniziale di riferimento ( se valido) in tale sistema if ( myParams.ptStart.IsValid()) myParams.ptStart.ToLoc( myParams.frLocXY) ; // controllo se la superficie è tutta chiusa o tutta aperta if ( ! IsSfrAllHomogeneous( pSfr, myParams.bAllClosed, myParams.bAllOpen)) return false ; // ------------ casi ottimizzati ------------------------------------------ ICRVCOMPOPOVECTOR vCrvOptCurves ; if ( ! GetPocketingOptimizedCurves( pSfrAdj, myParams, vCrvOptCurves)) return false ; for ( int i = 0 ; i < int( vCrvOptCurves.size()) ; ++ i) { vCrvOptCurves[i]->ToGlob( myParams.frLocXY) ; vCrvCompoRes.emplace_back( Release( vCrvOptCurves[i])) ; } // se ho svuotato tutta la superficie, allora ho finito if ( IsNull( pSfrAdj) || ! pSfrAdj->IsValid() || pSfrAdj->GetChunkCount() == 0) return true ; // ------------ recupero le singole curve di svuotatura ------------------- ICRVCOMPOPOVECTOR vCrvSingleCurves ; if ( ! GetSinglePocketingCurves( pSfrAdj, myParams, vCrvSingleCurves)) return false ; for ( int i = 0 ; i < int( vCrvSingleCurves.size()) ; ++ i) { vCrvSingleCurves[i]->ToGlob( myParams.frLocXY) ; vCrvCompoRes.emplace_back( Release( vCrvSingleCurves[i])) ; } // se ho svuotato tutta la superficie, allora ho finito if ( IsNull( pSfrAdj) || ! pSfrAdj->IsValid() || pSfrAdj->GetChunkCount() == 0) return true ; // ------------ estensione lati aperti ------------------------------------ if ( ! ModifySurfByOpenEdges( pSfrAdj, myParams)) return false ; // porto una copia della superficie originale nel frame creato PtrOwner pSfr_Loc( CloneSurfFlatRegion( pSfr)) ; if ( IsNull( pSfr_Loc) || ! pSfr_Loc->IsValid() || ! pSfr_Loc->ToLoc( myParams.frLocXY)) return false ; // ------------ calcolo delle curve elementari della superficie ------------------- ICRVCOMPOPOVECTOR vCrvCompoPock ; switch ( nType) { case POCKET_ZIGZAG : if ( ! AddZigZag( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock)) return false ; break ; case POCKET_ONEWAY : if ( ! AddOneWay( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock)) return false ; break ; case POCKET_SPIRALIN : if ( ! AddSpiralIn( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock)) return false ; break ; case POCKET_SPIRALOUT : if ( ! AddSpiralOut( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock)) return false ; break ; case POCKET_CONFORMAL_ZIGZAG : case POCKET_CONFORMAL_ONEWAY : if ( ! AddConformal( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock)) return false ; break ; } // ------------ intersezione delle estensioni ---------------------------- if ( ! myParams.bAllClosed) { if ( ! SmoothExtensionLinesByIntersection( vCrvCompoPock, myParams)) return false ; } // riporto tutte le curve ottenute in globale for ( int i = 0 ; i < int( vCrvCompoPock.size()) ; ++ i) { vCrvCompoPock[i]->ToGlob( myParams.frLocXY) ; vCrvCompoRes.emplace_back( Release( vCrvCompoPock[i])) ; } return true ; } //---------------------------------------------------------------------------- bool IsPocketingHole( const ISurfFlatRegion* pSfr, double dToolRad, double dRadialOffs, double dSideStep, int nType, int nLiType, double dLiTang, double dRatioSfrTool) { // verifico validità della superficie if ( pSfr == nullptr || ! pSfr->IsValid()) return false ; // la superficie deve avere un solo Chunk e non presentare isole if ( pSfr->GetChunkCount() != 1 && pSfr->GetLoopCount( 0) != 1) return false ; // porto la superficie nel suo riferimento intrinseco, quindi lavoro nel piano XY Point3d ptC ; pSfr->GetCentroid( ptC) ; Frame3d frXY ; if ( ! frXY.Set( ptC, pSfr->GetNormVersor())) return false ; PtrOwner pSfrCL( CloneSurfFlatRegion( pSfr)) ; if ( IsNull( pSfrCL) || ! pSfrCL->IsValid() || ! pSfrCL->ToLoc( frXY)) return false ; // recupero l'unica curva di bordo che descrive la regione piana PtrOwner pCrvBorder( ConvertCurveToComposite( pSfrCL->GetLoop( 0, 0))) ; if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid()) return false ; pCrvBorder->MergeCurves( 10. * EPS_SMALL, 10. * EPS_ANG_SMALL, true, true) ; pCrvBorder->SetExtrusion( pSfrCL->GetNormVersor()) ; // Z_AX() // verifico che le proprietà di lato Aperto/Chiuso siano uniformi bool bOkSpiral = true ; const ICurve* pFirstCrv = pCrvBorder->GetCurve( 0) ; if ( pFirstCrv == nullptr || ! pFirstCrv->IsValid()) return false ; int nTmpPropRef = pFirstCrv->GetTempProp( 0) ; for ( int nU = 1 ; bOkSpiral && nU < pCrvBorder->GetCurveCount() ; ++ nU) { const ICurve* pCrv = pCrvBorder->GetCurve( nU) ; if ( pCrv == nullptr || ! pCrv->IsValid()) return false ; bOkSpiral = ( pCrv->GetTempProp( 0) == nTmpPropRef) ; } if ( ! bOkSpiral) return false ; // definisco i parametri di una lavorazione Standard ( solo quelli utili per una svuotatura di un foro) PocketParams PockParam ; PockParam.dRad = dToolRad ; PockParam.dRadialOffset = dRadialOffs ; PockParam.dSideStep = dSideStep ; PockParam.ptStart = P_INVALID ; PockParam.bCalcFeed = false ; PockParam.nType = nType ; PockParam.nLiType = nLiType ; PockParam.dLiTang = dLiTang ; // controllo che il Loop sia una circonferenza Point3d ptCen ; double dRad ; if ( ! OptimizedSpiralCircle( pCrvBorder, 50. * EPS_SMALL, dRad, ptCen, bOkSpiral) || ! bOkSpiral) return false ; double dOffs = PockParam.dRad + PockParam.dRadialOffset ; // se curva tutta Open, ingrandisco il raggio della circonferenza trovata if ( nTmpPropRef == TEMP_PROP_OPEN_EDGE) dRad += 1.05 * PockParam.dRad + PockParam.dRadialOffset ; // se curva chiusa, controllo che il raggio sia compatibile con il primo Offset else bOkSpiral = ( dRad - dOffs > 10. * EPS_SMALL) ; if ( ! bOkSpiral) return false ; // verifico di rispettare il Ratio if ( dRad > dRatioSfrTool * dToolRad + EPS_SMALL) return false ; // verifico se l'entrata è compatibile con la geometria e calcolo un eventuale percorso double dIntRad = 0 ; if ( PockParam.nType == POCKET_SPIRALOUT && PockParam.nLiType == LEAD_IN_HELIX) dIntRad = min( 0.5 * min( PockParam.dLiTang, 2. * PockParam.dRad), dRad - dOffs) ; PtrOwner pCrvRes( CreateCurveComposite()) ; if ( IsNull( pCrvRes)) return false ; bOkSpiral = CalcCircleSpiral( ptCen, pSfrCL->GetNormVersor(), dRad - dOffs, dIntRad, PockParam, pCrvRes) ; return ( bOkSpiral && pCrvRes->IsValid() && pCrvRes->GetCurveCount() > 0) ; } //---------------------------------------------------------------------------- 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 ; myParams.bCalcFeed = false ; myParams.dSideStep = dStep ; myParams.bInvert = false ; // 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 ; } //---------------------------------------------------------------------------- bool CalcSmoothCurve( ICurveComposite* pCrv, double dRightLen, double dLeftLen, bool bAsParam) { // controllo dei parametri if ( pCrv == nullptr || ! pCrv->IsValid()) return false ; // definisco i parametri PocketParams myPockParams ; myPockParams.bSmooth = true ; return ModifyCurveToSmoothed( pCrv, myPockParams, dRightLen, dLeftLen, bAsParam) ; }