//---------------------------------------------------------------------------- // EgalTech 2023-2023 //---------------------------------------------------------------------------- // File : EGkCalcPocketing.h Data : 16.11.23 Versione : 2.5j1 // Contenuto : Dichiarazione della funzione per calcolare le curve base di Pocketing // // // // Modifiche : 16.11.23 RE Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "CurveLine.h" #include "CurveArc.h" #include "CurveComposite.h" #include "BiArcs.h" #include "SurfFlatRegion.h" #include "GeoConst.h" #include "/EgtDev/Include/EGkSfrCreate.h" #include "/EgtDev/Include/EGkCurveAux.h" #include "/EgtDev/Include/EGkCalcPocketing.h" #include "/EgtDev/Include/EGkFilletChamfer.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EGkCurveLocal.h" #include "/EgtDev/Include/EGkMedialAxis.h" #include "/EgtDev/Include/EGkLinePntTgCurve.h" #include "/EgtDev/Include/EGkOffsetCurve.h" #include using namespace std ; //---------------------------------------------------------------------------- // Calcolo delle curve elementari di Pocketing //---------------------------------------------------------------------------- // variabili d'appoggio ( per non passare troppi parametri tra le funzioni secondarie) struct PocketParams { int nType = POCKET_SPIRALIN ; double dRad = 0. ; // raggio utensile double dRad_prec = -1. ; // raggio utensile della lavorazione precedente double dSideStep = 0. ; // step double dSideStep_prec = -1. ; // step della lavorazione precedente double dRadialOffset = 0. ; // offset radiale double dRadialOffset_prec = -1. ; // offset radiale precedente double dOpenEdgeRad = 0. ; // massimo raggio per estensione lati aperti double dOpenMinSafe = 0. ; // estensione minima di sicurezza double dMaxOptSize = 0. ; // dimensione per ottimizzazione double dAngle = 0. ; // angolo per orientare le passate OneWay e ZigZag bool bOptOffsets = true ; // flag per evitare Offset non necessari in SpiralIn/Out bool bOptOffsetsAdv = false ; // flag per evitare Offset coperti da curve di MedialAxis in SpiralIn/Out bool bAboveHead = false ; // flag per testa da sopra ( Z+) double dSafeZ = 0. ; // safe Z bool bSmooth = false ; // curve smussate bool bInvert = false ; // inversione dei percorsi Point3d ptStart = P_INVALID ; // punto d'inizio Point3d ptEnd = P_INVALID ; // punto di fine SurfFlatRegion SrfLimit ; // superficie limite per estensione lati aperti bool bCalcFeed = false ; // flag per calcolo della Feed double dFeed = 1000 ; // feed di riferimento per frazione } ; static double TOL_TRAPEZOID = 50 * EPS_SMALL ; // tolleranza per casi a trapezio SpiralPocket //---------------------------------------------------------------------------- static bool GetCurvesForOptimizedPocketing( const ISurfFlatRegion* pSfr, ICRVCOMPOPOVECTOR& vCrvOEWithFlags) { vCrvOEWithFlags.clear() ; // controllo parametri : if ( pSfr == nullptr || ! pSfr->IsValid()) { vCrvOEWithFlags.emplace_back( CreateCurveComposite()) ; return true ; // <- se superficie non valida, allora non ho curve da salvare } // ricavo tutti i Loop esterni della superficie con i lati Aperti/Chiusi for ( int c = 0 ; c < pSfr->GetChunkCount() ; ++ c) { PtrOwner pCrvCompoLoop( ConvertCurveToComposite( pSfr->GetLoop( c, 0))) ; if ( IsNull( pCrvCompoLoop) || ! pCrvCompoLoop->IsValid()) return false ; vCrvOEWithFlags.emplace_back( Release( pCrvCompoLoop)) ; } return true ; } //---------------------------------------------------------------------------- static bool GetCoeffLinArc( const ICurveArc* pArc, double dDiam, double& dSubArc) { // controllo parametri if ( pArc == nullptr || ! pArc->IsValid()) return false ; Point3d ptMid ; if ( ! pArc->GetMidPoint( ptMid)) return false ; // creo l'ombra del tool nel punto medio PtrOwner pCrvTool( CreateCurveArc()) ; pCrvTool->Set( ptMid, Z_AX, dDiam) ; if ( ! pCrvTool->IsValid()) return false ; // effettuo la classificazione IntersCurveCurve intCC( *pArc, *pCrvTool) ; CRVCVECTOR ccClass ; if ( intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) && int( ccClass.size()) == 3 && ccClass[1].nClass == CRVC_IN) { Point3d ptS, ptE ; pCrvTool->GetPointD1D2( ccClass[1].dParS, ICurve::FROM_MINUS, ptS) ; pCrvTool->GetPointD1D2( ccClass[1].dParE, ICurve::FROM_PLUS, ptE) ; Vector3d vtS = ptS - pArc->GetCenter() ; Vector3d vtE = ptE - pArc->GetCenter() ; double dTheta ; vtS.GetAngle( vtE, dTheta) ; dTheta = abs( dTheta) ; dSubArc = pArc->GetRadius() * DEGTORAD * dTheta ; } else return false ; return true ; } //---------------------------------------------------------------------------- static bool GetParamForPtStartOnEdge( const ICurve* pCrvCheck, const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vOtherCrv, PocketParams PockParams, double& dPar) { // ==================== INFO ================================================================ // pCrvCheck -> sottocurva di pCrvCompo su cui cercare il parametro ideale per ingresso // vOtherCrv -> tutte le altre curve che non devono essere intersecate durante l'ingresso // ( questo vettore è stato riempito con tutti loop della superificie da // lavorare, i quali Offset non hanno generato alcuna curva di pCrvCheck ) // ========================================================================================== // controllo dei parametri if ( pCrvCheck == nullptr || ! pCrvCheck->IsValid() || pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0) return false ; // clono le curve PtrOwner pCrv( pCrvCheck->Clone()) ; PtrOwner pCompo( CloneCurveComposite( pCrvCompo)) ; if ( IsNull( pCrv) || IsNull( pCompo)) return false ; ICRVCOMPOPOVECTOR vCrvNoInters ; for ( int i = 0 ; i < ( int)vOtherCrv.size() ; ++ i) vCrvNoInters.emplace_back( ConvertCurveToComposite( vOtherCrv[i]->Clone())) ; // ricavo l'estrusione della curva composita Vector3d vtExtr ; if ( ! pCompo->GetExtrusion( vtExtr)) vtExtr = Z_AX ; // ricavo il centroide o il punto iniziale della curva composita Point3d ptCen ; if ( ! pCompo->GetCentroid( ptCen)) if ( ! pCompo->GetStartPoint( ptCen)) return false ; // creo un frame locale per creare l'ombra del tool e intersecarla con la curva Frame3d frLoc ; frLoc.Set( ptCen, vtExtr) ; // porto le curve in questo sistema di riferimento pCrv->ToLoc( frLoc) ; pCompo->ToLoc( frLoc) ; for ( int i = 0 ; i < ( int)vCrvNoInters.size() ; ++i) vCrvNoInters[i]->ToLoc( frLoc) ; // fisso il diametro del tool ( più grande per sicurezza ) double dDiam = ( 2 * PockParams.dRad ) + 2 * PockParams.dRadialOffset ; double dLen ; if ( ! pCrv->GetLength( dLen)) return false ; double dSubArc = -1 ; if ( pCrv->GetType() == CRV_ARC) { PtrOwner pCrvArc( GetCurveArc( pCrv->Clone())) ; if ( IsNull( pCrvArc) || ! GetCoeffLinArc( pCrvArc, dDiam, dSubArc)) return false ; } double dDiv = dSubArc < 0 ? dDiam : dSubArc ; double dMaxInt = floor( dLen / dDiv) ; int nDen = 2 ; // intervalli in cui suddivido la curva dMaxInt = max( 2.0, dMaxInt) ; // così se entra una volta controllo sempre dParT = 0.5 while ( nDen < dMaxInt + EPS_SMALL) { // EPS_SMALL per comprendere l'uguaglianza for ( int i = 1 ; i < nDen ; ++i) { if ( int( i % nDen) == 0) continue ; double dNum = 1.0 * i ; double dParT = dNum / nDen ; Point3d ptTest ; Vector3d vtPerpIn ; if ( pCrv->GetPointD1D2( dParT, ICurve::FROM_PLUS, ptTest, &vtPerpIn)) { PtrOwner pCompoClone( CloneCurveComposite( pCompo)) ; if ( IsNull( pCompoClone)) return false ; double dU ; pCompoClone->GetParamAtPoint( ptTest, dU) ; pCompoClone->ChangeStartPoint( dU) ; // se chiuso if ( pCrv->GetTempProp() == 0) { // se la curva è chiusa sposto il centro verso l'interno. // Devo essere sicuro di essere su una curva di Offset interno, altrimenti potrebbe essere valido // il punto trovato, però facendo poi il primo offeset tale punto potrebbe diventare un punto // di convergenza tra più chunks... vtPerpIn.Normalize() ; vtPerpIn.Rotate( Z_AX, 0, 1) ; // cos( pi/2) = 0, sin( pi/2) = 1 vtPerpIn *= (( PockParams.dRad + PockParams.dRadialOffset - 50 * EPS_SMALL)) ; ptTest = ptTest + vtPerpIn ; // creo una circonferenza ( ombra del tool ) centrata su ptTest PtrOwner pCrvToolShape( CreateCurveArc()) ; pCrvToolShape->Set( ptTest, Z_AX, 0.5 * dDiam) ; if ( ! pCrvToolShape->IsValid()) return false ; // interseco la curva composita con l'ombra del tool CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCompoClone, *pCrvToolShape) ; if ( intCC.GetCurveClassification( 0, EPS_SMALL, ccClass)) { // se ho solo due intersezioni, controllo di non intersecare isole ( In-Out-In) if ( int( ccClass.size() == 3)) { bool bOk = true ; for ( int j = 0 ; j < ( int)vCrvNoInters.size() && bOk ; ++ j) { IntersCurveCurve intCCI( *vCrvNoInters[j], *pCrvToolShape) ; if ( intCCI.GetCurveClassification( 0, EPS_SMALL, ccClass)) { if (( int)ccClass.size() > 1) bOk = false ; } else return false ; } if ( bOk) { dPar = dParT ; return true ; } } } else return false ; } // se aperto else { dPar = dParT ; return true ; } } } ++nDen ; } return false ; } //---------------------------------------------------------------------------- static bool AdjustContourStart( ICurveComposite* pCompo, PocketParams PockParams, const ICRVCOMPOPOVECTOR& vCrvIsl, bool bOrder, Point3d ptRef) { // se cerco semplicemente il tratto lineare chiuso più lungo ... if ( ! bOrder) { // la priorità è essagnata ai tratti lineari chiusi int i = 0 ; // <--- indice della curva corrente int nMax = - 1 ; double dLenMax = 0 ; const ICurve* pCrv = pCompo->GetFirstCurve() ; while ( pCrv != nullptr) { double dLen ; if ( pCrv->GetType() == CRV_LINE && pCrv->GetTempProp() == 0 && pCrv->GetLength( dLen) && dLen > dLenMax) { dLenMax = dLen ; nMax = i ; } ++ i ; pCrv = pCompo->GetNextCurve() ; } // se non trovato o troppo corto, cerco il tratto chiuso più lungo in generale if ( nMax < 0 || dLenMax < 2 * ( 2 * PockParams.dRad)) { i = 0 ; pCrv = pCompo->GetFirstCurve() ; while ( pCrv != nullptr) { double dLen ; if ( pCrv->GetType() != CRV_LINE && pCrv->GetTempProp() == 0 && pCrv->GetLength( dLen) && dLen > dLenMax) { dLenMax = dLen ; nMax = i ; } ++ i ; pCrv = pCompo->GetNextCurve() ; } } // controllo che il tratto chiuso più lungo trovato sia sufficientemente lungo if ( dLenMax < 10 * EPS_SMALL) { // se troppo piccolo allora lo imposto aperto pCompo->SetCurveTempProp( nMax, 0, 1) ; // spezzo la curva nel primo tratto aperto sufficientemente lungo for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) { double dLen ; pCompo->GetCurve( u)->GetLength( dLen) ; if ( dLen > 10 * EPS_SMALL) { nMax = u ; continue ; } } } // se non trovato, imposto il punto iniziale a mentà del primo tratto if ( nMax == -1) { pCompo->ChangeStartPoint( 0.5) ; return true ; } pCompo->ChangeStartPoint( nMax + 0.5) ; } // se invece sto cercando di entrare da un lato chiuso per una svuotatura, allora riordino i lati // chiusi a seconda della lunghezza, e a partire dal più lungo cerco un parametro su tale curva che mi consenta // un'entrata sufficientemente distante da isole e da altre curve della curva stessa su cui cerco l'entrata ... else { // se ho un punto di riferimento, allora come prima cosa controllo il lato chiuso più vicino if ( ptRef.IsValid()) { double dMinDist = INFINITO ; int nIndCrvMinDist = - 1 ; for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) { DistPointCurve DPC( ptRef, *pCompo->GetCurve( u)) ; double dCurrDist = INFINITO ; if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) { nIndCrvMinDist = u ; dMinDist = dCurrDist ; } } // se avessi curve alla stessa distanza ( circa) non controllo le altre, mi accontento della // prima curva trovata if ( nIndCrvMinDist != -1) { // controllo che il lato chiuso sia sufficientemente lungo double dLen = 0. ; pCompo->GetCurve( nIndCrvMinDist)->GetLength( dLen) ; if ( dLen > 2 * PockParams.dRad + 2 * PockParams.dRadialOffset + 500 * EPS_SMALL) { pCompo->ChangeStartPoint( nIndCrvMinDist + 0.5) ; return true ; } } } // creo un vettore di indici che definisce l'ordine delle curve chiuse in base alla lunghezza INTVECTOR vInd ; vInd.reserve( pCompo->GetCurveCount()) ; // vettore di indici già utilizzati INTVECTOR vIndUsed ; vIndUsed.reserve( pCompo->GetCurveCount()) ; double dMaxLen = -INFINITO ; int nCurrInd = 0 ; bool bStop = false ; for ( int c = 0 ; c < pCompo->GetCurveCount() && ! bStop ; ++ c) { bStop = true ; for ( int i = 0 ; i < pCompo->GetCurveCount() ; ++ i) { int nProp_i ; // se la curva i-esima non è già stata considerata ed è chiusa... if ( find( vIndUsed.begin(), vIndUsed.end(), i) == vIndUsed.end() && pCompo->GetCurveTempProp( i, nProp_i, 0) && nProp_i == 0) { // creo la curva i-esima const ICurve* pCrv_i( pCompo->GetCurve( i)) ; double dLen_i ; if ( pCrv_i->GetLength( dLen_i) && dLen_i > dMaxLen) { // se di lunghezza maggiore alla soglia... dMaxLen = dLen_i ; nCurrInd = i ; bStop = false ; } } } vIndUsed.push_back( nCurrInd) ; vInd.push_back( nCurrInd) ; dMaxLen = -INFINITO ; } if ( vInd.empty()) { // se questa condizione fosse vera allora non sono riuscito ad entrare da nessun lato aperto in precedenza e non // ho nemmeno un lato chiuso disponibile per entrare... ( forzo l'entrata a metà del primo lato) pCompo->ChangeStartPoint( 0.5) ; return true ; } // ora che ho riempito il vettore di indici lo scorro e cerco di entrare in questi lati chiusi secondo l'ordine bool bOk = false ; for ( int i = 0 ; i < int( vInd.size()) && !bOk ; ++ i) { const ICurve* pCrv( pCompo->GetCurve( vInd[i])) ; double dPar ; if ( GetParamForPtStartOnEdge( pCrv, pCompo, vCrvIsl, PockParams, dPar)) { pCompo->ChangeStartPoint( vInd[i] + dPar) ; bOk = true ; } } if ( ! bOk) { // se non riesco ad entrare da nessun lato chiuso, considerando che in precedenza ho già provato ad // entrare da tutti i lati aperti... pCompo->ChangeStartPoint( vInd[0] + 0.5) ; return true ; } } return true ; } //---------------------------------------------------------------------------- static bool GetHomogeneousParts( ICurveComposite* pCrvCompo, 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 richisto)... ICRVCOMPOPOVECTOR vCrvNULL ; AdjustContourStart( pCrvCompo, PockParams, vCrvNULL, false, P_INVALID) ; // estraggo parti con proprietà uniforme in un vettore ( vpCrvs) int nCurrTempProp ; int nParStart = 0 ; for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) { int nTempProp ; pCrvCompo->GetCurveTempProp( i, nTempProp) ; if ( i == 0) { nCurrTempProp = nTempProp ; nParStart = i ; } else if ( nCurrTempProp != nTempProp) { PtrOwner pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, i))) ; if ( IsNull( pCrv)) return false ; pCrv->SetTempProp( nCurrTempProp) ; vpCrvs.emplace_back( Release( pCrv)) ; nCurrTempProp = nTempProp ; nParStart = i ; } } // ultima curva... PtrOwner pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ; if ( IsNull( pCrv)) return false ; pCrv->SetTempProp( nCurrTempProp) ; vpCrvs.emplace_back( Release( pCrv)) ; return true ; } //--------------------------------------------------------------------------- static bool CreateSurfFrIncidence( const ICurveComposite* pCrv, const Vector3d& vtTanS_, const Vector3d& vtTanE_, const double dRad, ISurfFlatRegion* pSfrInc) { // controllo dei parametri if ( pCrv == nullptr || ! pCrv->IsValid()) return false ; pSfrInc->Clear() ; // creo la Fat Curve dalla curva *pCrv PtrOwner pSfrInc_tmp( GetSurfFlatRegionFromFatCurve( pCrv->Clone(), dRad + EPS_SMALL, false, false)) ; if ( IsNull( pSfrInc_tmp) || ! pSfrInc_tmp->IsValid()) return false ; pSfrInc->CopyFrom( pSfrInc_tmp) ; // pSfrInc ha sempre normale Z_AX ! // se la curva è chiusa, allora tutto il bordo è aperto if ( pCrv->IsClosed()) { pSfrInc->CopyFrom( pSfrInc_tmp) ; return pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0 ; } // 1) *** // La regione di incidenza non deve avere un bordo distante dRad dagli estremi del tratto aperto // per questo motivo, creo due FlatRegion a rettangolo che andrò a sottrarre alla pSfrInc_tmp // ( tolgo i semi-dischi agli estremi ) // 2) *** // La regione di incidenza deve seguire seguire in tangenza il percorso agli estremi del tratto // aperto corrente, creo due FlatRegion a rettangolo, orientate come le tangenza dei chiusi che andrò // a sottrarre alla pSfrInc_tmp // 1) *** // Rettangolo all'inizio Vector3d vtTanS ; pCrv->GetStartDir( vtTanS) ; Point3d ptS ; pCrv->GetStartPoint( ptS) ; Vector3d vtOut = vtTanS ; vtOut.Rotate( Z_AX, - 90) ; PtrOwner pCrvRectSBorder( CreateCurveComposite()) ; if ( IsNull( pCrvRectSBorder)) return false ; pCrvRectSBorder->AddPoint( ptS + ( dRad + 500 * EPS_SMALL) * vtOut) ; pCrvRectSBorder->AddLine( ptS - ( dRad + 500 * EPS_SMALL) * vtOut) ; pCrvRectSBorder->AddLine( ptS - ( dRad + 500 * EPS_SMALL) * vtOut - vtTanS * ( dRad + 500 * EPS_SMALL)) ; pCrvRectSBorder->AddLine( ptS + ( dRad + 500 * EPS_SMALL) * vtOut - vtTanS * ( dRad + 500 * EPS_SMALL)) ; pCrvRectSBorder->Close() ; PtrOwner pSfrRectStart( CreateSurfFlatRegion()) ; if ( IsNull( pSfrRectStart) || ! pSfrRectStart->AddExtLoop( Release( pCrvRectSBorder)) || ! pSfrRectStart->IsValid()) return false ; if ( AreOppositeVectorApprox( pSfrInc->GetNormVersor(), pSfrRectStart->GetNormVersor())) pSfrRectStart->Invert() ; // Rettangolo alla fine Vector3d vtTanE ; pCrv->GetEndDir( vtTanE) ; Point3d ptE ; pCrv->GetEndPoint( ptE) ; vtOut = vtTanE ; vtOut.Rotate( Z_AX, - 90) ; PtrOwner pCrvRectEBorder( CreateCurveComposite()) ; if ( IsNull( pCrvRectEBorder)) return false ; pCrvRectEBorder->AddPoint( ptE + ( dRad + 500 * EPS_SMALL) * vtOut) ; pCrvRectEBorder->AddLine( ptE - ( dRad + 500 * EPS_SMALL) * vtOut) ; pCrvRectEBorder->AddLine( ptE - ( dRad + 500 * EPS_SMALL) * vtOut + vtTanE * ( dRad + 500 * EPS_SMALL)) ; pCrvRectEBorder->AddLine( ptE + ( dRad + 500 * EPS_SMALL) * vtOut + vtTanE * ( dRad + 500 * EPS_SMALL)) ; pCrvRectEBorder->Close() ; PtrOwner pSfrRectEnd( CreateSurfFlatRegion()) ; if ( IsNull( pSfrRectEnd) || ! pSfrRectEnd->AddExtLoop( Release( pCrvRectEBorder)) || ! pSfrRectEnd->IsValid()) return false ; if ( AreOppositeVectorApprox( pSfrInc->GetNormVersor(), pSfrRectEnd->GetNormVersor())) pSfrRectEnd->Invert() ; // 2) *** // Rettangolo all'inizio Vector3d vtTanCS = vtTanS_ ; Vector3d vtTanCE = vtTanE_ ; PtrOwner pCrvRect_TanCLS_Border( CreateCurveComposite()) ; if ( IsNull( pCrvRect_TanCLS_Border)) return false ; pCrvRect_TanCLS_Border->AddPoint( ptS) ; pCrvRect_TanCLS_Border->AddLine( ptS + ( 1.5 * dRad + 500 * EPS_SMALL) * vtTanCS) ; vtOut = vtTanCS ; vtOut.Rotate( Z_AX, - 90) ; pCrvRect_TanCLS_Border->AddLine( ptS + ( 1.5 * dRad + 500 * EPS_SMALL) * vtTanCS + ( dRad + 500 * EPS_SMALL) * vtOut) ; pCrvRect_TanCLS_Border->AddLine( ptS + ( dRad + 500 * EPS_SMALL) * vtOut) ; pCrvRect_TanCLS_Border->Close() ; PtrOwner pSfrRectCLStart( CreateSurfFlatRegion()) ; if ( IsNull( pSfrRectCLStart) || ! pSfrRectCLStart->AddExtLoop( Release( pCrvRect_TanCLS_Border)) || ! pSfrRectCLStart->IsValid()) return false ; if ( AreOppositeVectorApprox( pSfrInc->GetNormVersor(), pSfrRectCLStart->GetNormVersor())) pSfrRectCLStart->Invert() ; // Rettangolo alla fine PtrOwner pCrvRect_TanCLE_Border( CreateCurveComposite()) ; if ( IsNull( pCrvRect_TanCLE_Border)) return false ; pCrvRect_TanCLE_Border->AddPoint( ptE) ; pCrvRect_TanCLE_Border->AddLine( ptE + ( 1.5 * dRad + 500 * EPS_SMALL) * vtTanCE) ; vtOut = vtTanCE ; vtOut.Rotate( Z_AX, 90) ; pCrvRect_TanCLE_Border->AddLine( ptE + ( 1.5 * dRad + 500 * EPS_SMALL) * vtTanCE + ( dRad + 500 * EPS_SMALL) * vtOut) ; pCrvRect_TanCLE_Border->AddLine( ptE + ( dRad + 500 * EPS_SMALL) * vtOut) ; pCrvRect_TanCLE_Border->Close() ; PtrOwner pSfrRectCLEEnd( CreateSurfFlatRegion()) ; if ( IsNull( pSfrRectCLEEnd) || ! pSfrRectCLEEnd->AddExtLoop( Release( pCrvRect_TanCLE_Border)) || ! pSfrRectCLEEnd->IsValid()) return false ; if ( AreOppositeVectorApprox( pSfrInc->GetNormVersor(), pSfrRectCLStart->GetNormVersor())) pSfrRectCLStart->Invert() ; // alla superficie di incidenza, sottraggo i due rettangoli ricavati if ( ! pSfrInc->Subtract( *pSfrRectStart) || ! pSfrInc->Subtract( *pSfrRectEnd)) return false ; // tolgo ora i rettangoli per le tangenze ai chiusi pSfrInc->Subtract( *pSfrRectCLStart) ; pSfrInc->Subtract( *pSfrRectCLEEnd) ; return pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0 ; } //---------------------------------------------------------------------------- static bool AdjustOpenEdge( const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vCrvIsland, const double dParS, const double dParE, const Vector3d& vtTanS, const Vector3d& vtTanE, const double dRad, const double dDiamJ, PocketParams PockParams, ICurveComposite* pCrvBorder) { /* parametri : pCrvCompo -> curva originaria di bordo vCrvIsland -> vettore delle isole all'interno di pCrvCompo pCrvOpenOffs -> tratto aperto corrente già Offsettato verso l'esterno dParS -> parametro sulla pCrvCompo per l'inizio del tratto aperto dParE -> parametro sulla pCrvCompo per la fine del tratto aperto vtTanS -> vettore di tangenza finale tratto chiuso precedente vtTanE -> vettore di tangenza iniziale ( invertito ) del tratto chiuso successivo dRad -> raggio di Offset per la regione di incidenza dDiamJ -> ampiezza delle curve a fagiolo per estendere la regione di incidenza pCrvRes -> curva da restituire ( inizialmente è il tratto aperto sulla pCrvCompo ; questa curva sarà l'estensione del lato aperto, adattandosi alla geometria dei chiusi ( non posso vedere solo i chiusi adiacenti all'aperto, devo considerare TUTTI i chiusi della pCrvCompo pStmVol -> Volume di svuotatura pStm_Part -> Part corrente */ // controllo la validità dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 || pCrvBorder == nullptr || ! pCrvBorder->IsValid() || pCrvBorder->GetCurveCount() == 0) return false ; // definisco la regione di incidenza PtrOwner pSfrInc( CreateSurfFlatRegion()) ; if ( IsNull( pSfrInc) || ! CreateSurfFrIncidence( pCrvBorder, vtTanS, vtTanE, dRad + 75 * EPS_SMALL, pSfrInc)) return false ; // creo un vettore con tutte le curve che potrebbero cadere, in parte, nella regione di incidenza ICRVCOMPOPOVECTOR vCrvToCheck ; for ( int i = 0 ; i < int( vCrvIsland.size()) ; ++ i) vCrvToCheck.emplace_back( vCrvIsland[i]->Clone()) ; // aggiungo le isole // se la curva originale non è tutta Aperta -> devo aggiungere anche essa nelle curve da controllare bool bIsAllOpen = abs( abs( dParE - dParS) - pCrvCompo->GetCurveCount()) < EPS_SMALL ; if ( ! bIsAllOpen) { // recupero il tratto di curva prima e dopo dell'aperto corrente PtrOwner pCompoOther( ConvertCurveToComposite( pCrvCompo->CopyParamRange( dParE, dParS))) ; if ( IsNull( pCompoOther) || ! pCompoOther->IsValid()) return false ; vCrvToCheck.emplace_back( Release( pCompoOther)) ; // aggiungo il bordo } // controllo se la curva è un'isola ( normale del piano -Z_AX) bool bIsIsland = false ; double dArea ; Plane3d plCheck ; if ( ! pCrvCompo->GetArea( plCheck, dArea)) return false ; bIsIsland = AreSameVectorEpsilon( plCheck.GetVersN(), - Z_AX, 10 * EPS_SMALL) ; // scorro il vettore creato... for ( int c = 0 ; c < int( vCrvToCheck.size()) ; ++ c) { // 1) recupero la curva corrente PtrOwner pCrvCurr( CloneCurveComposite( vCrvToCheck[c])) ; if ( IsNull( pCrvCurr) || ! pCrvCurr->IsValid()) return false ; // 2) ricavo i tratti con proprietà uniformi ( Aperti/Chiusi ) ICRVCOMPOPOVECTOR vpCrvs ; if ( ! GetHomogeneousParts( pCrvCurr, PockParams, vpCrvs)) return false ; // 3) considero solo i tratti chiusi for ( int cl = 0 ; cl < int( vpCrvs.size()) ; ++ cl) { if ( vpCrvs[cl]->GetTempProp() == 1) continue ; // 4) effettuo l'Offset della curva di metà dDiamJ OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( vpCrvs[cl], - dDiamJ * 0.5 - 20 * EPS_SMALL, ICurve::OFF_FILLET)) return false ; // 5) scorro tutte le curve di Offset che si sono formate, prendendo sempre la più lunga tra le rimanenti PtrOwner pOffLongestCrv( OffsCrv.GetLongerCurve()) ; while ( ! IsNull( pOffLongestCrv)) { // 6) creo la regione di incidenza di tale curva ( "Curva a fagiolo") PtrOwner pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ * 0.5, false, false)) ; if ( IsNull( pSfrBean) || ! pSfrBean->IsValid()) return false ; // inverto se necessario if ( AreOppositeVectorApprox( pSfrBean->GetNormVersor(), pSfrInc->GetNormVersor())) pSfrBean->Invert() ; // 7) se la "Regione a fagiolo" non influenza la regione di incidenza, la transcuro bool bDiscard = true ; for ( int cI = 0 ; cI < pSfrInc->GetChunkCount() && bDiscard ; ++ cI) for ( int cB = 0 ; cB < pSfrBean->GetChunkCount() && bDiscard ; ++ cB) bDiscard = ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrBean, cB) != REGC_INTERS) ; 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.SrfLimit.IsValid()) { pSfrInc->Offset( 50 * EPS_SMALL, ICurve::OFF_FILLET) ; if ( ! pSfrInc->Subtract( PockParams.SrfLimit)) return false ; // 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 aperti estremanti if ( pSfrInc->GetChunkCount() > 1) { // se ottengo più chunks PtrOwner pNewSfrInc( CreateSurfFlatRegion()) ; if ( IsNull( pNewSfrInc)) return false ; // ricavo i punti iniziali e finali della curva di bordo Point3d ptS, ptE ; if ( ! pCrvBorder->GetStartPoint( ptS) || ! pCrvBorder->GetEndPoint( ptE)) return false ; // per ogni Chunk ( > 1 ) for ( int i = 0 ; i < pSfrInc->GetChunkCount() ; ++ i) { // bordo esterno PtrOwner pCrvEL( ConvertCurveToComposite( pSfrInc->GetLoop( i, 0))) ; if ( IsNull( pCrvEL) || ! pCrvEL->IsValid()) return false ; // se estremi in comune, la curva cercata è la seguente if ( pCrvEL->IsPointOn( ptS, 300 * EPS_SMALL) && pCrvEL->IsPointOn( ptE, 300 * EPS_SMALL)) { if ( ! pNewSfrInc->IsValid()) // se la nuova regione di incidenza è vuota... pNewSfrInc.Set( pSfrInc->CloneChunk( i)) ; // aggiorno... else // se la nuova regione di incidenza non è vuota... if ( ! pNewSfrInc->Add( * pSfrInc->CloneChunk( i))) // aggiungo... return false ; } } // definisco quindi la nuova regione di incidenza if ( ! pNewSfrInc->IsValid()) return false ; pSfrInc.Set( Release( pNewSfrInc)) ; } } // dalla regione di incidenza devo estrarre il tratto di bordo che ha per estremi i lati chiusi // ( stando al di fuori, quindi estendendomi all'esterno, della regione da svuotare ) PtrOwner pCrvNewBorder( ConvertCurveToComposite( pSfrInc->GetLoop( 0, 0))) ; if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid()) return false ; // imposto la curva come tutta aperta for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u) pCrvNewBorder->SetCurveTempProp( u, 1, 0) ; // se la curva originale era tutta aperta... ( il nuovo lato è il loop esterno della regione di incidenza) if ( bIsAllOpen) { // pulisco la curva originale pCrvBorder->Clear() ; if ( bIsIsland) { // se isola inserisco il loop interno della regione pCrvNewBorder.Set( ConvertCurveToComposite( pSfrInc->GetLoop( 0, 1))) ; if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid()) return false ; // imposto la curva come tutta aperta ( gira come girava già l'isola) for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u) pCrvNewBorder->SetCurveTempProp( u, 1, 0) ; } // se bordo esterno inserisco il bordo esterno return pCrvBorder->AddCurve( Release( pCrvNewBorder)) ; } // altrimenti la spezzo il loop della regione di incidenza nei punti iniziali e finali della curva aperta originale Point3d ptStart ; pCrvBorder->GetStartPoint( ptStart) ; Point3d ptEnd ; pCrvBorder->GetEndPoint( ptEnd) ; double dUTrimS = -1 ; double dUTrimE = -1 ; pCrvNewBorder->GetParamAtPoint( ptStart, dUTrimS, 10000 * EPS_SMALL) ; pCrvNewBorder->GetParamAtPoint( ptEnd, dUTrimE, 10000 * EPS_SMALL) ; // pulisco la curva originale pCrvBorder->Clear() ; if ( ! pCrvBorder->AddCurve( pCrvNewBorder->CopyParamRange( dUTrimS, dUTrimE)) || ! pCrvBorder->IsValid()) { // se la curva non originaria di lato aperto non era chiusa... if ( ! pCrvBorder->IsClosed() && pCrvNewBorder->IsClosed()) { // cerco il punto più vicino al ptStart del chiuso sulla nuova curva di bordo aperta Point3d ptMinDist ; int nFlag ; if ( ! DistPointCurve( ptStart, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag)) return false ; // cambio il punto iniziale della border in ptMinDist double dUTmp ; if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp) || ! pCrvNewBorder->ChangeStartPoint( dUTmp)) return false ; // cerco il punto più vicino al ptEnd del chiuso sulla curva di bordo aperta if ( ! DistPointCurve( ptEnd, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag)) return false ; // ricavo il parametro sulla curva if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp)) return false ; if ( dUTmp < EPS_SMALL) dUTmp = pCrvNewBorder->GetCurveCount() - 5 * EPS_SMALL ; // copio la curva tra questi due parametri PtrOwner pCrvTmp( ConvertCurveToComposite( pCrvNewBorder->CopyParamRange( 0., dUTmp))) ; if ( IsNull( pCrvTmp) || ! pCrvTmp->IsValid()) return false ; // raccordo con il chiuso mediante due tratto lineare pCrvTmp->AddLine( ptStart, false) ; pCrvTmp->AddLine( ptEnd, true) ; // restituisco pCrvBorder->CopyFrom( pCrvTmp) ; return true ; } pCrvBorder->CopyFrom( pCrvCompo) ; } return true ; } //---------------------------------------------------------------------------- static bool AdjustContourWithOpenEdges( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vCrvIsl, const double dDiam, const double dOffR, const double dStep, PocketParams PockParams) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // raggio di riferimento per offset double dOutEdge = 0.5 * dDiam ; if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_ZIGZAG) dOutEdge = max( dOutEdge, dDiam - dStep) ; double dRad = dOutEdge + dOffR ; if ( abs( PockParams.dOpenEdgeRad) > 0 && PockParams.dOpenEdgeRad < dRad) dRad = PockParams.dOpenEdgeRad ; // ricavo i tratti omogenei ICRVCOMPOPOVECTOR vpCrvs ; if ( ! GetHomogeneousParts( pCrvCompo, PockParams, vpCrvs)) return false ; // NB. Il primo tratto è la prima metà del tratto aperto più lungo ( se esiste ) // Offset esterno per CurveJ double dDiamJ = 1.05 * ( dDiam) + 2 * dOffR ; if ( abs( PockParams.dOpenEdgeRad) > 0) dDiamJ = 100 * EPS_SMALL ; // NB. 1.05 serve per eccedere leggermente, in modo che il contro-offset della prima curva di svuotatura presenti // dei piccoli archi tra i chiusi e gli aperti ( in modo da svuotare bene lungo il chiuso) // curva finale da restituire PtrOwner pCrvCompo_final( CreateCurveComposite()) ; if ( IsNull( pCrvCompo_final)) return false ; // parametro iniziale del tratto corrente, sulla curva originale double dParS = 0. ; // scorro tutti i tratti omogenei nel vettore for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { // recupero la proprietà della curva composita int nCurrTmpProp = vpCrvs[i]->GetTempProp() ; // aggiorno i parametri del lato aperto corrente sulla curva originale double dParE = dParS + 1. * vpCrvs[i]->GetCurveCount() ; // se aperta if ( nCurrTmpProp == 1) { // ricavo la tangenze dei lati chiusi agli estremi di questa curva Vector3d vtTanS = V_INVALID ; Vector3d vtTanE = V_INVALID ; if ( i != 0) { if ( ! vpCrvs[i-1]->GetEndDir( vtTanS) || // tangente finale del chiuso precedente ! vpCrvs[( i + 1) % int( vpCrvs.size())]->GetStartDir( vtTanE)) // tangente iniziale del chiuso successivo... return false ; vtTanE.Invert() ; // invertita } if ( ! AdjustOpenEdge( pCrvCompo, vCrvIsl, dParS, dParE, vtTanS, vtTanE, dRad, dDiamJ, PockParams, vpCrvs[i])) return false ; } // assegno le proprietà di lato Aperto/Chiuso per la curva corrente for ( int u = 0 ; u < vpCrvs[i]->GetCurveCount() ; ++ u) vpCrvs[i]->SetCurveTempProp( u, nCurrTmpProp, 0) ; // aggiungo la curva ricavata ( se chiusa -> la copio, se aperta -> copio l'estesa) if ( ! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone())) { // per sicurezza, se gli estremi non coincidono, creo un piccolo raccordo lineare Point3d ptH ; vpCrvs[i]->GetStartPoint( ptH) ; if ( ! pCrvCompo_final->AddLine( ptH) || ! pCrvCompo_final->SetCurveTempProp( pCrvCompo_final->GetCurveCount() - 1, 1, 0) || ! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone())) return false ; } // aggiorno dParS = dParE ; } // non dovrebbe esserci un gap, ma meglio prevenire problemi pCrvCompo_final->Close() ; // sostituisco pCrvCompo->Clear() ; pCrvCompo->CopyFrom( pCrvCompo_final) ; return true ; } //---------------------------------------------------------------------------- static bool CheckForRemovingIsland( const ICurveComposite* pCrvIslandBorder, double dOffs, bool bRemove) { // controllo dei parametri // NB. La curva dell'isola gira in senso orario, quindi l'offset va fatto in positivo if ( pCrvIslandBorder == nullptr || ! pCrvIslandBorder->IsValid() || dOffs < EPS_SMALL) return false ; bRemove = false ; // controllo se l'isola è tutta aperta for ( int u = 0 ; u < pCrvIslandBorder->GetCurveCount() ; ++ u) { const ICurve* pCrv_u = pCrvIslandBorder->GetCurve( u) ; if ( pCrv_u == nullptr) return false ; int nTmpProp0 = pCrv_u->GetTempProp( 0) ; // se trovo una sottocurva che non è aperta, esco if ( nTmpProp0 != 1) return true ; } // tutte le curva sono aperte, effettuo un offset per vedere se l'isola è da tenere OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( pCrvIslandBorder, dOffs, ICurve::OFF_FILLET)) return false ; // nel caso l'Offset sparisca, all'ora l'isola non è necessaria if ( OffsCrv.GetCurveCount() == 0) bRemove = true ; 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 // 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 // NB. Se ho una lavorazione precedente, allora estendo i lati aperti delle quantità relative alla lavorazione // precedente ( l'attuale infatti svuoterà quello che rimane da svuotare proprio da questa lavorazione) // Vedi GetNewSfrByAnotherPocketing() // 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 c = 0 ; c < pSfr->GetChunkCount() ; ++ c) { // vettore delle isole che userò ( le isole aperte piccole sono trascurate) ICRVCOMPOPOVECTOR vCrvToTIsland ; // Flag per sapere se c'è stata almeno una modifica in un loop bool bIsChunkModified = false ; // 1) ricavo il Loop esterno PtrOwner pCrvEL( ConvertCurveToComposite( pSfr->GetLoop( c, 0))) ; if ( IsNull( pCrvEL) || ! pCrvEL->IsValid()) return false ; // 2) creo un vettore di curve con le isole del Chunk ICRVCOMPOPOVECTOR vCrvIsl ; for ( int l = 1 ; l < pSfr->GetLoopCount( c) ; ++ l) { PtrOwner pCrvIL( ConvertCurveToComposite( pSfr->GetLoop( c, l))) ; if ( IsNull( pCrvIL) || ! pCrvIL->IsValid()) return false ; vCrvIsl.emplace_back( Release( pCrvIL)) ; } // 3) se la curva esterna presenta dei lati aperti -> devo modificarla bool bSomeOpen = false ; int nProp0 = -1 ; for ( int u = 0 ; u < pCrvEL->GetCurveCount() && ! bSomeOpen ; ++ u) if ( pCrvEL->GetCurveTempProp( u, nProp0, 0) && nProp0 == 1) bSomeOpen = true ; if ( bSomeOpen) { // se trovo dei lati aperti // 3.1) sistemo la superificie if ( ! AdjustContourWithOpenEdges( pCrvEL, vCrvIsl, PockParams.dRad_prec > 0 ? 2 * PockParams.dRadialOffset_prec : 2 * PockParams.dRad, PockParams.dRadialOffset_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset, PockParams.dRad_prec > 0 ? PockParams.dSideStep_prec : PockParams.dSideStep, PockParams)) return false ; bIsChunkModified = true ; // la curva è stata modificata } // 4) Controllo i bordi delle isole ottenute // NB. L'isola può essere tutta aperta o tutta chiusa ( se non uniforme, passo a tutta chiusa ) // La definizione di isola con proprietà non uniformi non è definita... for ( int i = 0 ; i < int( vCrvIsl.size()) ; ++ i) { // controllo uniformità int nCurrTmpProp = -1 ; int nPrecTmpProp = -1 ; bool bIsMixed = false ; PtrOwner pCrvIsl( ConvertCurveToComposite( vCrvIsl[i]->Clone())) ; if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid()) return false ; for ( int u = 0 ; u < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ u) { pCrvIsl->GetCurveTempProp( u, nCurrTmpProp, 0) ; bIsMixed = ( u != 0 && nCurrTmpProp != nPrecTmpProp) ; nPrecTmpProp = nCurrTmpProp ; } // se proprità non uniformi -> tutta chiusa if ( bIsMixed) { for ( int u = 0 ; u < pCrvIsl->GetCurveCount() ; ++ u) pCrvIsl->SetTempProp( u, 0) ; nCurrTmpProp = 0 ; // aggiorno il Flag bIsChunkModified = true ; // la curva è stata modificata } // se curva tutta aperta, controllo se l'isola può essere trascurata if ( nCurrTmpProp == 1) { bool bRemove = false ; if ( ! CheckForRemovingIsland( pCrvIsl, PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec + PockParams.dRadialOffset_prec : 2 * PockParams.dRad, bRemove)) return false ; if ( bRemove) continue ; } for ( int u = 0 ; u < pCrvIsl->GetCurveCount() ; ++ u) pCrvIsl->SetCurveTempProp( u, 0, 0) ; // lascio tutto chiuso, ho già esteso vCrvToTIsland.emplace_back( pCrvIsl->Clone()) ; // entra nelle curve della regione da svuotare } // 6) Se c'è stata almeno una modifica di lato aperto al chunk (c-esimo), devo ricreare il Chunk // nuovi loop ricavati ( Chunk dopo Chunk creo la superficie finale) if ( bIsChunkModified) { SurfFlatRegionByContours SfrBC ; // per sicurezza analizzo ancora i Loop pCrvEL->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; SfrBC.AddCurve( Release( pCrvEL)) ; // <--- Loop esterno for ( int i = 0 ; i < ( int)vCrvToTIsland.size() ; ++ i) { vCrvToTIsland[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; SfrBC.AddCurve( Release( vCrvToTIsland[i])) ; // <--- isole "valide" } // ricavo il nuovo Chunk PtrOwner pNewChunk( SfrBC.GetSurf()) ; if ( IsNull( pNewChunk) || ! pNewChunk->IsValid()) return false ; // aggiungo il Chunk alla superficie finale if ( pSrfFinal->GetChunkCount() == 0) pSrfFinal.Set( pNewChunk) ; else if ( ! pSrfFinal->Add( *pNewChunk)) return false ; } // se il Chunk c-esimo non è mai stato modificato... else { // aggiungo il Chunk alla superficie finale if ( pSrfFinal->GetChunkCount() == 0) pSrfFinal.Set( pSfr->CloneChunk( c)) ; else if ( ! pSrfFinal->Add( *pSfr->CloneChunk( c))) return false ; } } // restituisco la superficie aggiornata ricavata if ( ! pSrfFinal->IsValid()) return false ; pSfr->Clear() ; pSfr->CopyFrom( pSrfFinal) ; return pSrfFinal->IsValid() && pSrfFinal->GetChunkCount() > 0 ; } // *************************************************************************** // ------------- SCELTA DEL PUNTO INIZIALE ----------------------------------- // *************************************************************************** //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- static bool GetParamOnOpenSide( const ICurveComposite* pCompo, const ICRVCOMPOPOVECTOR& vOtherCrv, const Frame3d& frPocket, PocketParams PockParams, Point3d& ptMid, Vector3d& vtMidOrt) { // recupero il vettore estrusione Vector3d vtExtr = Z_AX ; pCompo->GetExtrusion( vtExtr) ; // verifico se tutti i lati sono aperti bool bAllOpen = true ; const ICurve* pMyCrv = pCompo->GetFirstCurve() ; while ( pMyCrv != nullptr) { if ( pMyCrv->GetTempProp() != 1) { bAllOpen = false ; break ; } pMyCrv = pCompo->GetNextCurve() ; } // salvo il punto iniziale e la direzione d'uscita del primo lato aperto valido che trovo // NB. Il primo valido non significa il migliore, potrebbe infatti essere il lato aperto più lungo // ma essere all'interno del grezzo... cerco di dare priorità ai lati sul grezzo // flag per la presenza di un primo lato aperto valido per entrare bool bFirstOpenValid = false ; Point3d ptFirstOpenValid = P_INVALID ; Vector3d vtFirstOpenValid = V_INVALID ; // richiedo lunghezza superiore a diametro utensile più doppio offset radiale double dRefLen = ( bAllOpen ? 0 : ( 2 * PockParams.dRad) + 2 * PockParams.dRadialOffset - EPS_SMALL) ; double dMaxLen = dRefLen ; // ciclo sulle singole curve bool bFound = false ; const ICurve* pPrevCrv = pCompo->GetLastCurve() ; double dLenPrev = 0 ; if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == 1) pPrevCrv->GetLength( dLenPrev) ; const ICurve* pCrv = pCompo->GetFirstCurve() ; int nPriorityOpenEdge = -1 ; while ( pCrv != nullptr) { // analizzo la curva successiva const ICurve* pNextCrv = pCompo->GetNextCurve() ; bool bNextOk = ( pNextCrv != nullptr) ; if ( ! bNextOk) pNextCrv = pCompo->GetFirstCurve() ; double dLenNext = 0 ; if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == 1) pNextCrv->GetLength( dLenNext) ; ++ nPriorityOpenEdge ; // verifico la curva corrente if ( pCrv->GetTempProp() == 1) { // contributo dalle entità adiacenti (se non tutte aperte) double dLenAgg = 0 ; if ( ! bAllOpen) { if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == 1) { Vector3d vtPrevEnd ; pPrevCrv->GetEndDir( vtPrevEnd) ; Vector3d vtStart ; pCrv->GetStartDir( vtStart) ; dLenAgg += max( 0.4, vtPrevEnd * vtStart) * dLenPrev ; } if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == 1) { Vector3d vtEnd ; pCrv->GetEndDir( vtEnd) ; Vector3d vtNextStart ; pNextCrv->GetStartDir( vtNextStart) ; dLenAgg += max( 0.4, vtEnd * vtNextStart) * dLenNext ; } } // entità corrente double dLen = 0 ; if ( pCrv->GetLength( dLen)) { const double LEN_TOL = 1 ; // se di lunghezza praticamente uguale if ( bFound && dLen + dLenAgg > dRefLen && abs( dLen + dLenAgg - dMaxLen) < LEN_TOL) { Point3d ptTest ; pCrv->GetMidPoint( ptTest) ; if (( PockParams.bAboveHead && ptTest.z > ptMid.z + 100 * EPS_SMALL) || ( ! PockParams.bAboveHead && ptTest.z < ptMid.z - 100 * EPS_SMALL) || ( abs( ptTest.z - ptMid.z) < 100 * EPS_SMALL && ptTest.y < ptMid.y - 100 * EPS_SMALL)) { dMaxLen = max( dMaxLen, dLen + dLenAgg) ; ptMid = ptTest ; // vettore ortogonale verso l'esterno (ruotato -90deg attorno a estrusione) pCrv->GetMidDir( vtMidOrt) ; vtMidOrt.Rotate( vtExtr, 0, -1) ; } } // se più lunga ( o non già trovata) else if ( dLen + dLenAgg > dMaxLen || !bFound) { dMaxLen = dLen + dLenAgg ; double dParIn ; // cerco il parametro di tale curva (0 < dParIn < 1) migliore per l'entrata if ( GetParamForPtStartOnEdge( pCrv, pCompo, vOtherCrv, PockParams, dParIn)) { pCrv->GetPointD1D2( dParIn, ICurve::FROM_PLUS, ptMid) ; PtrOwner pCompoClone( CloneCurveComposite( pCompo)) ; if ( IsNull( pCompoClone)) return false ; double dU ; pCompoClone->GetParamAtPoint( ptMid, dU) ; pCompoClone->ChangeStartPoint( dU) ; pCompoClone->GetStartDir( vtMidOrt) ; vtMidOrt.Rotate( vtExtr, 0, -1) ; // salvo l'indice di questo lato aperto se si tratta del primo valido if ( ! bFirstOpenValid) { bFirstOpenValid = true ; ptFirstOpenValid = ptMid ; vtFirstOpenValid = vtMidOrt ; } // controllo se questa quantità è effettivamente fuori dal grezzo // ( privilegio i lati aperti che sono effettivamente sul bordo del grezzo) // punto iniziale nel sistema di riferimento globale Point3d ptStart ; pCompoClone->GetStartPoint( ptStart) ; ptStart.ToGlob( frPocket) ; // vettore d'uscita nel sistema di riferimento globale Vector3d vtMidOut = vtMidOrt ; vtMidOut.ToGlob( frPocket) ; // ricavo la SafeZ double dSafeZ = PockParams.dSafeZ ; Point3d ptOut = ptStart + vtMidOut * ( PockParams.dRad + PockParams.dRadialOffset + ( PockParams.dRad + max( dSafeZ, PockParams.dOpenMinSafe))) ; // controllo l'elevazione sopra a quel punto ( qui è un bel mistero...) // double dElev = 0. ; // if ( ! GetElevation( m_nPhase, ptOut, frPocket.VersZ(), m_TParams.m_dDiam, m_TParams.m_dLen, // frPocket.VersZ(), dElev)) // return false ; // se non vi è elevazione, il lato aperto trovato è il privilegiato //if ( dElev < EPS_SMALL) { // bFound = true ; // if ( nPriorityOpenEdge > -1) // return true ; //} } } dLenPrev = dLen ; } } else dLenPrev = 0 ; // vado alla successiva pPrevCrv = pCrv ; pCrv = ( bNextOk ? pNextCrv : nullptr) ; } // se non ho trovato un lato aperto valido fuori dal grezzo, ma ho trovato un lato aperto // generico accettabile if ( ! bFound && bFirstOpenValid) { ptMid = ptFirstOpenValid ; vtMidOrt = vtFirstOpenValid ; bFound = true ; } return bFound ; } //---------------------------------------------------------------------------- static bool SetSpecialPtStartForOpenEdges( const ICurveComposite* pCrvOrig, const Frame3d& frPocket, PocketParams PockParams, ICurveComposite* pCrvCompo, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut) { // controllo dei parametri if ( pCrvOrig == nullptr || ! pCrvOrig->IsValid() || ! frPocket.IsValid() || pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // cerco il lato aperto più lungo double dLenRef = ( 2 * PockParams.dRad) - 50 * EPS_SMALL ; for ( int u = 0 ; u < pCrvOrig->GetCurveCount() ; ++ u) { if ( pCrvOrig->GetCurve( u)->GetTempProp( 0) == 0) continue ; // escludo le chiuse // ricavo la curva u-esima aperta const ICurve* pCrvOpen = pCrvOrig->GetCurve( u) ; if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid()) return false ; // ricavo la lunghezza di tale curva double dLen = 0. ; pCrvOpen->GetLength( dLen) ; if ( dLen > dLenRef) { Point3d ptSTmp ; Vector3d vtMidOutTmp ; if ( pCrvOpen->GetPointD1D2( 0.5, ICurve::FROM_MINUS, ptSTmp, &vtMidOutTmp)) { int nFlag ; if ( DistPointCurve( ptSTmp, *pCrvCompo).GetMinDistPoint( EPS_SMALL, ptStart, nFlag)) { vtMidOutTmp.Normalize() ; vtMidOutTmp.Rotate( Z_AX, - 90) ; vtMidOut = vtMidOutTmp ; bMidOut = true ; } } } } return bMidOut ; } //---------------------------------------------------------------------------- static bool SetPtStartForPath( ICurveComposite* pCrvOffsAct, PocketParams PockParams, const ISurfFlatRegion* pSrfToWork, const Point3d& ptEndPrec, const Frame3d& frPocket, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut, int nOffs, ICurveComposite* pCrvOrig) { // ============================= INFO ============================================================== // pCrvOffsAct -> Curva di Offset su cui cercare ptStart, vtMidOut, bMidOpen // pSrfToWork -> Superificie originaria da lavorare // ( questa superificie ha i flag di lati aperti/chiusi settati nei loops) // ================================================================================================= // controllo dei parametri if ( pCrvOffsAct == nullptr || ! pCrvOffsAct->IsValid() || pCrvOffsAct->GetCurveCount() == 0 || pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1) return false ; // clono le curva su cui devo entrare PtrOwner pCrv( CloneCurveComposite( pCrvOffsAct)) ; if ( IsNull( pCrv)) return false ; bool bSomeOpen = false ; // flag per presenza di lati aperti // creo un vettore di Loops della superificie, ordinati ICRVCOMPOPOVECTOR vCrvLoops ; for ( int l = 0 ; l < pSrfToWork->GetLoopCount( 0) ; ++ l) vCrvLoops.emplace_back( ConvertCurveToComposite( pSrfToWork->GetLoop( 0, l))) ; // creo un vettore di indici. Questi indici si riferiscono alle posizioni di vCrvLoops i quali offset // hanno generato delle curve sulla pCrv ( curva da cui devo entrare ) INTVECTOR vIndex ; int nInd = -1 ; // indice della prima curva che non è "raccordo" di Offset // scorro tutte le curve presenti in pCrv ( curva da cui devo entrare) for ( int i = 0 ; i < pCrv->GetCurveCount() ; ++ i) { // nProp0 -> #curva il cui Offset ha generato la curva i-esima di pCrv int nProp0 ; pCrv->GetCurveTempProp( i, nProp0, 0) ; // nProp1 -> #loop che contiene la curva espressa in nProp0 int nProp1 ; pCrv->GetCurveTempProp( i, nProp1, 1) ; if ( nProp0 > 0) { // se questa curva non è un "raccordo" di Offset // controllo per maggiore sicurezza che effettivamente nProp1 sia un indice valido per il vettore dei Loops e // che nProp0 non sia maggiore del numero di curve del loop nProp1-esimo del vettore dei Loops della pSrfToWork if ( nProp1 >= 0 && nProp1 < int( vCrvLoops.size()) && nProp0 < vCrvLoops[nProp1]->GetCurveCount()) { // aggiorno il vettore di indici ... if ( find( vIndex.begin(), vIndex.end(), nProp1) == vIndex.end()) vIndex.push_back( nProp1) ; // aggiorno la proprietà della curva da cui devo entrare int nTempProp ; vCrvLoops[nProp1]->GetCurveTempProp( nProp0 - 1, nTempProp, 0) ; pCrv->SetCurveTempProp( i, nTempProp, 0) ; // se la curva è aperta, aggiorno il Flag if ( nTempProp == 1 && ! bSomeOpen) bSomeOpen = true ; // salvo l'indice della prima curva trovata che non è un "raccordo" di Offset if ( nInd == -1) nInd = i ; } else pCrv->SetCurveTempProp( i, 0, 0) ; } else { pCrv->SetCurveTempProp( i, -2, 0) ; pCrv->SetCurveTempProp( i, -2, 1) ; } } // scorro tutte le curve incerte ( che derivano da raccordi ) if ( nInd != -1) pCrv->ChangeStartPoint( nInd) ; // la prima curva non è un "raccordo" di Offset for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++ i) { double dLen = 0. ; if ( pCrv->GetCurve( i)->GetLength( dLen) && dLen < 0.025 * ( 2 * PockParams.dRad)) pCrv->SetCurveTempProp( i, 0, 0) ; else { int nTmpProp0, nTmpProp1 ; if (( pCrv->GetCurveTempProp( i, nTmpProp0, 0) && nTmpProp0 == -2) && ( pCrv->GetCurveTempProp( i, nTmpProp1, 1) && nTmpProp1 == -2)) { // copio la temp prop della curva precedente pCrv->SetCurveTempProp( i, pCrv->GetCurve( i-1)->GetTempProp(), 0) ; // se troppo corta, allora chiusa if ( pCrv->GetCurve( i)->GetLength( dLen) && dLen < 0.01 * ( 2 * PockParams.dRad)) pCrv->SetCurveTempProp( i, 0, 0) ; } } } // se ho dei lati aperti... double dLenMax = EPS_SMALL ; int nCrvForMax = 0 ; if ( bSomeOpen) { // ... e sono al primo Step, cerco la curva Aperta più lunga ( scelgo la prima che trovo) ... if ( ! ptEndPrec.IsValid()) { for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { int nTmpProp = 0 ; double dLenAct = EPS_SMALL ; if ( pCrv->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 1 && pCrv->GetCurve( u)->GetLength( dLenAct) && dLenAct > dLenMax) { dLenMax = dLenAct ; nCrvForMax = u ; } } // il punto iniziale della curva sarà il punto iniziale della sottocurva trovata pCrv->ChangeStartPoint( nCrvForMax) ; } // ... se invece non sono al primo Step, cerco la prima curva aperta più vicina al punto finale else { int nIndClosesOpenCrv = -1 ; double dMinDist = INFINITO ; for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { if ( pCrv->GetCurve( u)->GetTempProp( 0) == 1) { DistPointCurve DPC( ptEndPrec, *pCrv->GetCurve( u)) ; double dCurrDist = INFINITO ; if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) { dMinDist = dCurrDist ; nIndClosesOpenCrv = u ; } } } // il punto iniziale della curva sarà il punto iniziale della sottocurva trovata pCrv->ChangeStartPoint( nIndClosesOpenCrv) ; } } // creo un vettore con tutti i Loops della pSwfToWork per i quali, mediante l'Offset, non hanno // generato alcuna curva presente nella pCrv ( quella da cui devo entrare) ICRVCOMPOPOVECTOR vOtherCrv ; for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) { bool bOk = true ; for ( int j = 0 ; j < int( vIndex.size()) && bOk ; ++ j) { if ( i == vIndex[j]) bOk = false ; } if ( bOk) vOtherCrv.emplace_back( ConvertCurveToComposite( vCrvLoops[i]->Clone())) ; } // cerchiamo un punto valido per l'entrata ... bMidOut = false ; if ( bSomeOpen) { // se ho dei lati aperti, cerco un parametro ideale per entrare bool bOK = false ; if ( nOffs == 1 && pCrvOrig != nullptr && pCrvOrig->IsValid()) { // in questo caso le curve a fagiolo dei lati aperti a sinistra e a destra potrebbero // intersecarsi tra loro -> risulta difficile calcolare il versore direzione d'uscita bOK = SetSpecialPtStartForOpenEdges( pCrvOrig, frPocket, PockParams, pCrvOffsAct, ptStart, vtMidOut, bMidOut) ; } if ( ! bOK) bMidOut = GetParamOnOpenSide( pCrv, vOtherCrv, frPocket, PockParams, ptStart, vtMidOut) ; } if ( bMidOut) { // se ho trovato e valido, allora imposto il punto inziale trovato const double LEN_OUT = 5 ; double dPar ; int nFlag ; bMidOut = ( DistPointCurve( ptStart + LEN_OUT * vtMidOut, *pCrv).GetParamAtMinDistPoint( 0, dPar, nFlag) && pCrv->ChangeStartPoint( dPar)) ; } if ( ! bMidOut) // alla peggio, ordino i lati lunghi per lunghezza e cerco un'entrata valida AdjustContourStart( pCrv, PockParams, vOtherCrv, true, ptEndPrec) ; // ora che ho deciso quale sia il punto iniziale, lo imposto effettivamente sulla curva di Offset passata alla funzione pCrv->GetStartPoint( ptStart) ; pCrvOffsAct->Clear() ; pCrvOffsAct->CopyFrom( pCrv) ; return true ; } // *************************************************************************** //---------------------------- CASI OTTIMIZZATI ------------------------------ // *************************************************************************** //---------------------------------------------------------------------------- static bool GetOptCrvIndex( const ICRVCOMPOPOVECTOR& vCrvOEWithFlags, ISurfFlatRegion* pSrfChunkFinal, int& nIndex) { // controllo dei parametri if ( int( vCrvOEWithFlags.size()) == 0 || pSrfChunkFinal == nullptr || ! pSrfChunkFinal->IsValid()) return false ; // cerco la curva originale del chunk (cc)-esimo ( per casi ottimizzati) if ( int( vCrvOEWithFlags.size()) == 1) nIndex = 0 ; else { for ( int k = 0 ; k < ( int)vCrvOEWithFlags.size() ; ++k) { CRVCVECTOR ccClass ; if ( pSrfChunkFinal->GetCurveClassification( *vCrvOEWithFlags[k], EPS_SMALL, ccClass)) { bool bIsThis = true ; for ( int kk = 0 ; kk < ( int)ccClass.size() && bIsThis ; ++kk) { if ( ccClass[kk].nClass == CRVC_OUT) bIsThis = false ; } if ( bIsThis) { nIndex = k ; break ; } } } } return true ; } //---------------------------------------------------------------------------- static bool OptimizedSpiralCirle( const ICurveComposite* pCrvCompo, const double dToll, double& dRad, Point3d& ptC, bool& bIsCirlce) { /* restituisce il centro e il raggio di una circonfereza che approssima pCrvCompo */ // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || dToll < EPS_SMALL) return false ; dRad = 0. ; ptC = P_INVALID ; bIsCirlce = false ; // se aperta, non è un cerchio e non è piana, esco Plane3d plPock ; if ( ! pCrvCompo->IsClosed() || ! pCrvCompo->IsFlat( plPock)) return true ; // creo un sistema di riferimento centrato sulla curva Point3d ptCentroid ; Vector3d vtN ; Frame3d frCurr ; if ( ! pCrvCompo->GetCentroid( ptCentroid) || ! pCrvCompo->GetExtrusion( vtN) || ! frCurr.Set( ptCentroid, vtN) || ! frCurr.IsValid()) return false ; // porto la curva nel sistema di riferimento corrente ( dopo averla clonata) PtrOwner pCrvLoc( CloneCurveComposite( pCrvCompo)) ; 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, PocketParams PockParams, ICurveComposite* pMCrv, ICurveComposite* pRCrv) { /* calcola il percorso di a spirale assieme al percorso di ritorno */ // raggio della circonferenza esterna if ( dOutRad < 10 * EPS_SMALL) return false ; // imposto versore estrusione sulle curve composite pMCrv->SetExtrusion( vtN) ; pRCrv->SetExtrusion( vtN) ; // creo e inserisco la circonferenza esterna PtrOwner pArc( CreateCurveArc()) ; if ( IsNull( pArc) || ! pArc->Set( ptCen, vtN, dOutRad)) return false ; // creo la superificie per la Feed PtrOwner pSrfRemoved( CreateSurfFlatRegion()) ; if ( IsNull( pSrfRemoved)) return false ; ICRVCOMPOPOVECTOR vLinksDone ; Vector3d vtDir = pArc->GetStartVersor() ; pMCrv->AddCurve( Release( pArc)) ; // se richiesta percorrenza invertita if ( PockParams.bInvert) pMCrv->Invert() ; // se raggio esterno maggiore dell'interno if ( dOutRad > dIntRad + 10 * EPS_SMALL) { // aggiungo le semicirconferenze della spirale ( devono essere in numero dispari) int nStep = int( ceil( ( dOutRad - dIntRad) / ( 0.5 * PockParams.dSideStep))) ; if ( nStep % 2 == 0) nStep += 1 ; double dStep = ( dOutRad - dIntRad) / nStep ; for ( int i = 1 ; i <= nStep ; ++ i) { if ( ! ( i % 2 == 0)) pMCrv->AddArcTg( ptCen - vtDir * ( dOutRad - i * dStep)) ; else pMCrv->AddArcTg( ptCen + vtDir * ( dOutRad - i * dStep)) ; } // aggiungo la circonferenza interna pMCrv->AddArcTg( ptCen + vtDir * dIntRad) ; pMCrv->AddArcTg( ptCen - vtDir * dIntRad) ; } // verifico il percorso di lavoro if ( pMCrv->GetCurveCount() == 0) return false ; // calcolo l'eventuale percorso di ritorno Point3d ptStart ; pMCrv->GetStartPoint( ptStart) ; Point3d ptEnd ; pMCrv->GetEndPoint( ptEnd) ; Vector3d vtStart ; pMCrv->GetStartDir( vtStart) ; if ( ! AreSamePointApprox( ptStart, ptEnd)) { PtrOwner pArc2( CreateCurveArc()) ; if ( IsNull( pArc2) || ! pArc2->Set2PVN( ptStart, ptEnd, - vtStart, vtN)) return false ; pRCrv->AddCurve( Release( pArc2)) ; // inverto e eventualmente sistemo archi pRCrv->Invert() ; } return true ; } //---------------------------------------------------- static bool CalcTrapezoidSpiralLocalFrame( ICurveComposite* pCrvTrap, const Vector3d& vtDir, Frame3d& frLoc) { // cerco i lati paralleli a vtDir int nBaseId = -1 ; for ( int i = 0 ; i < pCrvTrap->GetCurveCount() ; i ++) { Vector3d vtEdge ; pCrvTrap->GetCurve( i)->GetStartDir( vtEdge) ; if ( AreSameOrOppositeVectorApprox( vtEdge, vtDir)) { nBaseId = i ; break ; } } if ( nBaseId != 0 && nBaseId != 1) return false ; // imposto come lato iniziale per la curva uno dei lati paralleli a vtDir pCrvTrap->ChangeStartPoint( nBaseId) ; Point3d ptOrig ; pCrvTrap->GetStartPoint( ptOrig) ; Vector3d vtX ; pCrvTrap->GetStartDir( vtX) ; return frLoc.Set( ptOrig, Z_AX, vtX) ; } //---------------------------------------------------------------------------- static bool GetBoxCrvOptTrap( const int nCrv, const ICurveComposite* pCrvCompo, const double dDiam, int& nType, ICurveComposite* pCrvBox) { nType = - 1 ; // prendo la curva const ICurve* pCrvCurr = pCrvCompo->GetCurve( nCrv) ; if ( pCrvCurr == nullptr) return false ; // controllo se lineare, altrimenti passo alla successiva if ( pCrvCurr->GetType() != CRV_LINE) return true ; // prendo la direzione del tratto lineare Vector3d vtDir ; pCrvCurr->GetStartDir( vtDir) ; // prendo il punto iniziale Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ; // creo il riferimento basato su questo tratto Frame3d frTrap ; frTrap.Set( ptStart, Z_AX, vtDir) ; if ( ! frTrap.IsValid()) return false ; // porto la curva Compo ( clonandola) nel frame e calcolo il suo BBox BBox3d BBox ; PtrOwner pCrvCompo_c( CloneCurveComposite( pCrvCompo)) ; if ( IsNull( pCrvCompo_c) || ! pCrvCompo_c->IsValid() || ! pCrvCompo_c->ToLoc( frTrap) || ! pCrvCompo_c->GetLocalBBox( BBox)) return false ; // controllo dimY e dimX per il box bool bDimYOk = BBox.GetDimY() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ; bool bDimXOk = BBox.GetDimX() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ; // controllo dimensioni ammissibili if ( ! bDimXOk && ! bDimYOk) return true ; // se nessuna else if ( ! bDimXOk) nType = 1 ; // se dimY else if ( ! bDimYOk) nType = 0 ; // se dimX else nType = 2 ; // se entrambe // creo il rettangolo del Box pCrvBox->Clear() ; pCrvBox->AddPoint( BBox.GetMin()) ; pCrvBox->AddLine( BBox.GetMin() + BBox.GetDimX() * X_AX) ; pCrvBox->AddLine( BBox.GetMax()) ; pCrvBox->AddLine( BBox.GetMax() - BBox.GetDimX() * X_AX) ; pCrvBox->Close() ; // determino il punto inziale della curva if ( nType == 0) pCrvBox->ChangeStartPoint( 1.) ; pCrvBox->ToGlob( frTrap) ; return true ; } //---------------------------------------------------------------------------- static bool PreparareTrapezoidTwoBases( const ICurveComposite* pCrvCompo, const double dDiam, const int nType, int& nBase, int& nSecondBase, bool& bOk, ICurveComposite* pCrvTrap) { // controllo parametri if ( pCrvCompo == nullptr) return false ; bOk = false ; // le parti tra le due basi devono essere tutte omogenee ( o tutte aperte o tutte chiuse) // pCrvTest0 sarà la parte destra, pCrvTest1 la parte sinistra Point3d ptSB0, ptSB1 ,ptEB0, ptEB1 ; PtrOwner pCrvTest0( 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) == 1) { if ( nType == 0) { pCrvTrap->GetCurve( 0)->GetStartPoint( ptEB0) ; pCrvTrap->GetCurve( 1)->GetStartPoint( ptSB1) ; } else { pCrvTrap->GetCurve( 0)->GetEndPoint( ptEB0) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptSB1) ; } } // se lato destro chiuso else bCopyRight = true ; // se lato sinistro aperto ( estendo il punto finale della base secondaria e il punto iniziale // della base primaria dino al lato sinistro del box) bool bCopyLeft = false ; if ( pCrvTest1->GetCurve( 0)->GetTempProp( 0) == 1) { if ( nType == 0) { pCrvTrap->GetCurve( 2)->GetEndPoint( ptEB1) ; pCrvTrap->GetCurve( 0)->GetStartPoint( ptSB0) ; } else { pCrvTrap->GetCurve( 1)->GetEndPoint( ptEB1) ; pCrvTrap->GetCurve( 2)->GetEndPoint( ptSB0) ; } } // se lato sinistro chiuso else bCopyLeft = true ; // creo la curva da restituire pCrvTrap->Clear() ; pCrvTrap->AddPoint( ptSB0) ; pCrvTrap->AddLine( ptEB0) ; nBase = 0 ; if ( bCopyRight) pCrvTrap->AddCurve( Release( pCrvTest0)) ; else { pCrvTrap->AddLine( ptSB1) ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta } nSecondBase = pCrvTrap->GetCurveCount() ; pCrvTrap->AddLine( ptEB1) ; if ( bCopyLeft) pCrvTrap->AddCurve( Release( pCrvTest1)) ; else { pCrvTrap->Close() ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta } // verifico dimensione x della svuotatura nel caso tutto chiuso if ( bCopyLeft && bCopyRight) { double dLen0 = 0, dLen2 = 0 ; pCrvTrap->GetCurve( nBase)->GetLength( dLen0) ; pCrvTrap->GetCurve( nSecondBase)->GetLength( dLen2) ; if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL) { pCrvTrap->Clear() ; return true ; } } bOk = true ; return true ; } //---------------------------------------------------------------------------- static bool GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTrap, Frame3d& frTrap, PocketParams PockParams, double& dPocketSize, int& nBase, int& nSecondBase) { // controllo parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // diametro reale da tenere in considerazione double dDiam = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ; pCrvTrap->Clear() ; // resterà vuota se il caso non è ottimizzato nBase = -1 ; nSecondBase = -1 ; // se la curva è già un trapezio, non sempre devo adattarla... Point3d pt ; Vector3d vtDir, vtB2, vtOtherDir ; if ( pCrvCompo->IsATrapezoid( 100 * EPS_SMALL, pt, vtDir, vtOtherDir, vtB2)) { pCrvTrap->AddCurve( pCrvCompo->Clone()) ; // se parallelogramma scelgo come base i lati lunghi Vector3d vtL2( - vtDir + vtOtherDir + vtB2) ; if ( AreSameOrOppositeVectorApprox( vtOtherDir, vtL2)) { if ( vtOtherDir.Len() > vtDir.Len()) swap( vtDir, vtOtherDir) ; } vtDir.Normalize() ; Vector3d vtOrtho = OrthoCompo( vtOtherDir, vtDir) ; dPocketSize = vtOrtho.Len() ; // eventuale approssimazione della curva con polyline per ottenere la stessa curva calcolata in ICurveComposite::IsATrapezoid if ( pCrvCompo->GetCurveCount() > 4) { PolyLine PL ; if ( ! pCrvCompo->ApproxWithLines( 100 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL)) return false ; pCrvTrap->FromPolyLine( PL) ; } if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap)) return false ; bool bBaseOpen = false ; bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == 1 || pCrvTrap->GetCurve( 2)->GetTempProp( 0) == 1) ; // controllo dimensioni della svuotatura if ( ! ( bBaseOpen && dPocketSize < dDiam + EPS_SMALL) && abs( dPocketSize - dDiam) > EPS_SMALL) { pCrvTrap->Clear() ; return true ; } // recupero flag aperto/chiuso dei lati if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == 0 && pCrvTrap->GetCurve( 3)->GetTempProp( 0) == 0) { double dLen0, dLen2 ; pCrvTrap->GetCurve( 0)->GetLength( dLen0) ; pCrvTrap->GetCurve( 2)->GetLength( dLen2) ; if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL) pCrvTrap->Clear() ; } // imposto le basi nBase = 0 ; nSecondBase = 2 ; return true ; } // controllo il numero di lati chiusi e salvo i loro indici int nClosedSide = 0 ; INTVECTOR vIndClosedSides ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) { int nTmpProp ; if ( pCrvCompo->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 0) { ++ nClosedSide ; vIndClosedSides.push_back( u) ; } } // se tutti lati aperti switch ( nClosedSide) { // TUTTI I LATI SONO APERTI case 0 : { // ricavo il box minimo della curva aperta ( passo dalla polyLine) PolyLine PL ; Point3d ptCen ; double dWidth ; if ( ! pCrvCompo->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) || ! PL.GetMinAreaRectangleXY( ptCen, vtDir, dWidth, dPocketSize)) return false ; // controllo dimY ( dHeight), se troppo estesa, non è un caso ottimizzato if ( dPocketSize > dDiam + TOL_TRAPEZOID) return true ; // inverto il frame attuale frTrap.Set( ptCen, Z_AX, vtDir) ; if ( ! frTrap.IsValid()) return false ; // creo il rettangolo del Box pCrvTrap->AddPoint( Point3d( - 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ; pCrvTrap->AddLine( Point3d( 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ; pCrvTrap->AddLine( Point3d( 0.5 * dWidth, 0.5 * dPocketSize, 0)) ; pCrvTrap->AddLine( Point3d( - 0.5 * dWidth, 0.5 * dPocketSize, 0)) ; pCrvTrap->Close() ; pCrvTrap->ToGlob( frTrap) ; Point3d ptNewOrig ; pCrvTrap->GetStartPoint( ptNewOrig) ; frTrap.Set( ptNewOrig, Z_AX, vtDir) ; // imposto tutte le 4 curve come aperte for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, 1, 0) ; // imposto le basi nBase = 0 ; nSecondBase = 2 ; } break ; // 1 LATO CHIUSO ( LINEARE) case 1 : { int nType = -1 ; if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType != -1) { // imposto tutte le curve come aperte tranne la prima ( estendo il solo lato chiuso come base del Box) for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u == 0 ? 0 : 1, 0) ; // memorizzo la dimensione di svuotatura pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // imposto le basi nBase = 0 ; nSecondBase = 2 ; } } break ; // 2 LATI CHIUSI ( LINEARI, CONSECUTIVI O PARALLELI) case 2 : { // controllo se entrambi sono lineari if ( pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE && pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE) { // se lineari, ricavo i punti estremanti e le direzioni Point3d ptS0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartPoint( ptS0) ; Point3d ptS1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartPoint( ptS1) ; Point3d ptE0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetEndPoint( ptE0) ; Point3d ptE1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetEndPoint( ptE1) ; Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ; Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ; // CASO PARALLELI ( la base è indifferente) if ( AreOppositeVectorEpsilon( vtDir0, vtDir1, 5 * EPS_SMALL)) { int nType = -1 ; // prendo come base il primo chiuso if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType != -1) { // memorizzo la dimensione di svuotatura pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // controllo che la distanza tra i due chiusi sia effettivamente circa il raggio double dDist = 0. ; DistPointCurve DPL( ptS0, *pCrvCompo->GetCurve( vIndClosedSides[1]), false) ; if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) { // imposto tutte le curve di indice dispari aperte for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u % 2 == 0 ? 0 : 1, 0) ; // imposto le basi nBase = 0 ; nSecondBase = 2 ; } else pCrvTrap->Clear() ; } } // CASO CONSECUTIVI bool bOk_0_1 = AreSamePointApprox( ptS1, ptE0) ; // chiuso1 . chiuso0 bool bOk_1_0 = AreSamePointApprox( ptS0, ptE1) ; // chiuso0 . chiuso1 if ( bOk_0_1 || bOk_1_0) { // provo con il primo lato int nType = -1 ; // prendo come base il primo chiuso if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType == -1) { // provo con l'altro if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType != -1) { // spaw tra gli indici e flags swap( vIndClosedSides[0], vIndClosedSides[1]) ; swap( bOk_0_1, bOk_1_0) ; } } Point3d ptH1, ptH2 ; if ( nType == 1 || nType == 2) { double dLen1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetLength( dLen1) ; pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // i lati inclinati chiusi definiscono il Box if ( abs( dLen1 * ( vtDir0 ^ vtDir1).Len() - dPocketSize) < TOL_TRAPEZOID) { // creo la curva a trapezio if ( bOk_0_1) { pCrvTrap->GetCurve( 2)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 3)->GetEndPoint( ptH2) ; pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->Close() ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ; } else { pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ; pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->AddLine( ptS1) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; pCrvTrap->SetCurveTempProp( 0, 0, 0) ; for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u) pCrvTrap->SetCurveTempProp( u, 1, 0) ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ; } // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } else pCrvTrap->Clear() ; } else if ( nType == 0) { if ( bOk_0_1) { pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ; pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->Close() ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ; pCrvTrap->ChangeStartPoint( 1.) ; // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } else { pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->AddLine( ptS1) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; pCrvTrap->SetCurveTempProp( 0, 0, 0) ; for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u) pCrvTrap->SetCurveTempProp( u, 1, 0) ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ; pCrvTrap->ChangeStartPoint( 1.) ; // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } } } } } break ; // 3 LATI CHIUSI ( LINEARI e CONSECUTIVI) case 3 : { // prendo i 3 lati chiusi const ICurve* pCrv0 = pCrvCompo->GetCurve( vIndClosedSides[0]) ; const ICurve* pCrv1 = pCrvCompo->GetCurve( vIndClosedSides[1]) ; const ICurve* pCrv2 = pCrvCompo->GetCurve( vIndClosedSides[2]) ; if ( pCrv0 == nullptr || pCrv1 == nullptr || pCrv2 == nullptr) return false ; // controllo che siano lineari if ( pCrv0->GetType() == CRV_LINE && pCrv1->GetType() == CRV_LINE && pCrv2->GetType() == CRV_LINE) { // prendo i punti iniziali e finali Point3d ptStart0 ; pCrv0->GetStartPoint( ptStart0) ; Point3d ptEnd0 ; pCrv0->GetEndPoint( ptEnd0) ; Point3d ptStart1 ; pCrv1->GetStartPoint( ptStart1) ; Point3d ptEnd1 ; pCrv1->GetEndPoint( ptEnd1) ; Point3d ptStart2 ; pCrv2->GetStartPoint( ptStart2) ; Point3d ptEnd2 ; pCrv2->GetEndPoint( ptEnd2) ; // controllo che siano consecutivi e cambio l'ordine se necessario bool bOk = true ; if ( AreSamePointApprox( ptEnd0, ptStart1) && AreSamePointApprox( ptEnd1, ptStart2)) ; else if ( AreSamePointApprox( ptEnd1, ptStart2) && AreSamePointApprox( ptEnd2, ptStart0)) { swap( vIndClosedSides[0], vIndClosedSides[1]) ; swap( vIndClosedSides[1], vIndClosedSides[2]) ; } else if ( AreSamePointApprox( ptEnd2, ptStart0) && AreSamePointApprox( ptEnd0, ptStart1)) { swap( vIndClosedSides[0], vIndClosedSides[2]) ; swap( vIndClosedSides[2], vIndClosedSides[1]) ; } else // se non consecutivi bOk = false ; // se i tre lati sono ( mediante lo swap) consecutivi... if ( bOk) { // controllo se la dimensione Y del lato 1 è valida ( il resto è gestito di seguito) int nType = -1 ; if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType == 1) { // bisgna controllare la lunghezza dei chiusi double dLen0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetLength( dLen0) ; double dLen2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetLength( dLen2) ; pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ; Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ; Vector3d vtDir2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetStartDir( vtDir2) ; // i lati inclinati chiusi definiscono il Box if ( abs( dLen2 * ( vtDir1 ^ vtDir2).Len() - dPocketSize) < TOL_TRAPEZOID && abs( dLen0 * ( vtDir1 ^ vtDir0).Len() - dPocketSize) < TOL_TRAPEZOID) { // creo la curva a trapezio pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[2])->Clone()) ; pCrvTrap->Close() ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u < 3 ? 0 : 1 , 1) ; pCrvTrap->ChangeStartPoint( 1.) ; // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } } } } } } // se non ho trovato delle basi, allora cerco in maniera generale un trapezoide ( se esiste) if ( nBase == -1) { bool bOK = false ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() && !bOK ; ++ u) { // cerco un lato chiuso ( esiste per forza) if ( pCrvCompo->GetCurve( u)->GetTempProp( 0) == 0) { // controllo se il lato corrente può essere una base int nType = -1 ; if ( ! GetBoxCrvOptTrap( u, pCrvCompo, dDiam, nType, pCrvTrap)) return false ; // se è una base valida if ( nType != -1) { // ricavo la dimensione di svuotatura pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // ricavo la sua direzione iniziale Vector3d vtBaseDir ; pCrvCompo->GetCurve( u)->GetStartDir( vtBaseDir) ; // ricavo il suo punto iniziale Point3d ptBaseStart ; pCrvCompo->GetCurve( u)->GetStartPoint( ptBaseStart) ; // ricavo il suo punto finale Point3d ptBaseEnd ; pCrvCompo->GetCurve( u)->GetEndPoint( ptBaseEnd) ; // cerco il lato chiuso lineare parallelo ad esso e distante circa dPocketSize for ( int uu = 0 ; uu < pCrvCompo->GetCurveCount() && !bOK ; ++ uu) { if ( uu == u || pCrvCompo->GetCurve( uu)->GetType() != CRV_LINE || pCrvCompo->GetCurve( uu)->GetTempProp() != 0) continue ; Vector3d vtSecondBaseDir ; pCrvCompo->GetCurve( uu)->GetStartDir( vtSecondBaseDir) ; if ( ! AreOppositeVectorEpsilon( vtBaseDir, vtSecondBaseDir, 5 * EPS_SMALL)) continue ; Point3d ptSecondBaseStart ; pCrvCompo->GetCurve( uu)->GetStartPoint( ptSecondBaseStart) ; double dDist = 0. ; DistPointCurve DPL( ptBaseStart, *pCrvCompo->GetCurve( uu), false) ; if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) { nBase = u ; nSecondBase = uu ; if ( ! PreparareTrapezoidTwoBases( pCrvCompo, dDiam, nType, nBase, nSecondBase, bOK, pCrvTrap)) return false ; } } } } } } // la curva a trapezio deve evere almeno 4 lati if ( pCrvTrap->GetCurveCount() < 4 || nBase == -1 || nSecondBase == -1) { pCrvTrap->Clear() ; return true ; } // ricostruisco il frame del trapezio Point3d ptS ; pCrvTrap->GetStartPoint( ptS) ; Vector3d vtS ; pCrvTrap->GetStartDir( vtS) ; if ( ! frTrap.Set( ptS, Z_AX, vtS) || ! frTrap.IsValid()) return false ; // se parametro MaxOptSize non compatibile => non è ottimizzato if ( PockParams.dMaxOptSize > EPS_SMALL && dPocketSize > PockParams.dMaxOptSize) pCrvTrap->Clear() ; return true ; } //---------------------------------------------------- static bool SpecialAdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const bool bEvenClosed, const ICurveComposite* pCrvPocket, PocketParams PockParams) { // parametri double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ; double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ; double dRad = 0.5 * dDiam + dOffsR ; // calcolo gli offset dei lati obliqui PtrOwner pLineS( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 0 : 3)->Clone())) ; pLineS->SimpleOffset( - dRad) ; pLineS->Invert() ; PtrOwner pLineE( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 2 : 1)->Clone())) ; pLineE->SimpleOffset( - dRad) ; pLineE->Invert() ; Point3d ptS, ptE ; pLineS->GetEndPoint( ptS) ; pLineE->GetStartPoint( ptE) ; Vector3d vtS, vtE ; pLineS->GetStartDir( vtS) ; pLineE->GetStartDir( vtE) ; PtrOwner pLineLink( CreateCurveLine()) ; pLineLink->Set( ptS, ptE) ; pMCrv->Clear() ; if ( ! pMCrv->AddCurve( Release( pLineS))) return false ; // creo raccordi bool bUseBiArcs = false ; Vector3d vtDir, vtDir2 ; pLineLink->GetStartDir( vtDir) ; pCrvPocket->GetCurve( bEvenClosed ? 1 : 0)->GetStartDir( vtDir2) ; if ( Dist( ptS, ptE) > 500 * EPS_SMALL && AreSameOrOppositeVectorApprox( vtDir, vtDir2)) { Point3d ptCrv1, ptCrv2 ; pLineLink->GetPointD1D2( 0.3, ICurve::FROM_MINUS, ptCrv1) ; pLineLink->GetPointD1D2( 0.7, ICurve::FROM_MINUS, ptCrv2) ; // primo raccordo double dAng ; vtS.GetAngleXY( X_AX, dAng) ; PtrOwner pBiArc1( GetBiArc( ptS, - dAng, ptCrv1, bEvenClosed ? 90 : 0, 0.5)) ; // secondo raccordo vtE.Invert() ; vtE.GetAngleXY( X_AX, dAng) ; PtrOwner pBiArc2( GetBiArc( ptE, - dAng, ptCrv2, bEvenClosed ? - 90 : 180, 0.5)) ; pBiArc2->Invert() ; if ( ! IsNull( pBiArc1) && ! IsNull( pBiArc2)) { bUseBiArcs = true ; pMCrv->AddCurve( Release( pBiArc1)) ; pMCrv->AddLine( ptCrv2) ; pMCrv->AddCurve( Release( pBiArc2)) ; } } // se non è stato possibile creare raccordo, unisco linearmente if ( ! bUseBiArcs) pMCrv->AddCurve( Release( pLineLink)) ; if ( ! pMCrv->AddCurve( Release( pLineE))) return false ; // setto temp prop per ricordare curve aggiuntive per pulire angoli pMCrv->SetCurveTempProp( 0, 1) ; pMCrv->SetCurveTempProp( pMCrv->GetCurveCount() - 1, 1) ; return true ; } //------------------------------------------------------ static bool CalcTrapezoidSpiralXCoord( const ICurveComposite* pCrvPocket, int nBase, int nSecondBase, bool bStart, double dYCoord, double& dXCoord, double dPocketSize, PocketParams PockParams) { // parametri double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ; double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ; double dRad = 0.5 * dDiam + dOffsR ; // recupero la curva di interesse int nCrvId = ( bStart ? nSecondBase : nBase) + 1 ; int nProp = - 1 ; if ( ! pCrvPocket->GetCurveTempProp( nCrvId, nProp)) return false ; // se open if ( nProp == 1) { Point3d pt1, pt2 ; pCrvPocket->GetCurve( nCrvId)->GetStartPoint( pt1) ; pCrvPocket->GetCurve( nCrvId)->GetEndPoint( pt2) ; if ( bStart) dXCoord = min( pt1.x, pt2.x) ; else dXCoord = max( pt1.x, pt2.x) ; } // se closed else { // creo la curva destra/sinistra int nLast = bStart ? pCrvPocket->GetCurveCount() : nSecondBase ; PtrOwner pCrvSide( ConvertCurveToComposite( pCrvPocket->CopyParamRange( nCrvId, nLast))) ; if ( IsNull( pCrvSide)) return false ; // Offsetto la curva OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( pCrvSide, - dRad, ICurve::OFF_FILLET)) return false ; if ( OffsCrv.GetCurveCount() == 0) { // controllo se avevo una circonferenza if ( pCrvSide->GetCurveCount() == 1 && pCrvSide->GetFirstCurve()->GetType() == CRV_ARC) { Point3d ptS ; pCrvSide->GetStartPoint( ptS) ; dXCoord = ptS.x ; } else { Point3d ptS ; pCrvSide->GetStartPoint( ptS) ; Point3d ptE ; pCrvSide->GetEndPoint( ptE) ; dXCoord = bStart ? max( ptS.x, ptE.x) + dRad : min( ptS.x, ptE.x) - dRad ; } } else if ( OffsCrv.GetCurveCount() == 1) { // controllo se la curva interseca la linea di svuotatura a YCoord PtrOwner pCrvOffs( OffsCrv.GetLongerCurve()) ; if ( IsNull( pCrvOffs)) return false ; PtrOwner pLineMid( CreateCurveLine()) ; if ( IsNull( pLineMid)) return false ; pLineMid->Set( Point3d( - 3000, dYCoord, 0), Point3d( 3000, dYCoord, 0)) ; IntersCurveCurve intCC( *pLineMid, *pCrvOffs) ; IntCrvCrvInfo ccClass ; if ( intCC.GetIntersCount() != 0) { // se ho almeno una intersezione if ( intCC.GetIntCrvCrvInfo( 0, ccClass)) dXCoord = ccClass.IciA[0].ptI.x ; else return false ; } else { // se non ho intersezioni... // prendo il box della curva BBox3d Box3d ; pCrvSide->GetLocalBBox( Box3d) ; // creo la linea limitie verticale PtrOwner pCrvVertLine( CreateCurveLine()) ; if ( IsNull( pCrvVertLine)) return false ; if ( bStart) pCrvVertLine->SetPDL( Box3d.GetMax() + 5 * TOL_TRAPEZOID * Y_AX, - 90 , 2 * dPocketSize) ; else pCrvVertLine->SetPDL( Box3d.GetMin() - 5 * TOL_TRAPEZOID * Y_AX, 90, 2 * dPocketSize) ; // intersechiamo IntersCurveCurve intCC2( *pCrvVertLine, *pCrvSide) ; if ( intCC2.GetOverlaps()) { if ( bStart) dXCoord = Box3d.GetMax().x + dRad ; else dXCoord = Box3d.GetMin().x - dRad ; } else { dXCoord = bStart ? -INFINITO : INFINITO ; for ( int i = 0 ; i < intCC2.GetIntersCount() ; ++ i) { IntCrvCrvInfo ccClass2 ; if ( intCC2.GetIntCrvCrvInfo( i, ccClass2)) { if ( bStart) dXCoord = max( dXCoord, Box3d.GetMax().x + sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ; else dXCoord = min( dXCoord, Box3d.GetMin().x - sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ; } } } } } else return false ; } return true ; } //---------------------------------------------------- static bool SpecialAdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const bool bEvenClosed, PocketParams PockParams, const ICurveComposite* pCrvPocket) { // parametri double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ; double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ; double dRad = 0.5 * dDiam + dOffsR ; // calcolo gli offset dei lati obliqui PtrOwner pLineS( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 0 : 3)->Clone())) ; pLineS->SimpleOffset( - dRad) ; pLineS->Invert() ; PtrOwner pLineE( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 2 : 1)->Clone())) ; pLineE->SimpleOffset( - dRad) ; pLineE->Invert() ; Point3d ptS, ptE ; pLineS->GetEndPoint( ptS) ; pLineE->GetStartPoint( ptE) ; Vector3d vtS, vtE ; pLineS->GetStartDir( vtS) ; pLineE->GetStartDir( vtE) ; PtrOwner pLineLink( CreateCurveLine()) ; pLineLink->Set( ptS, ptE) ; pMCrv->Clear() ; if ( ! pMCrv->AddCurve( Release( pLineS))) return false ; // creo raccordi bool bUseBiArcs = false ; Vector3d vtDir, vtDir2 ; pLineLink->GetStartDir( vtDir) ; pCrvPocket->GetCurve( bEvenClosed ? 1 : 0)->GetStartDir( vtDir2) ; if ( Dist( ptS, ptE) > 500 * EPS_SMALL && AreSameOrOppositeVectorApprox( vtDir, vtDir2)) { Point3d ptCrv1, ptCrv2 ; pLineLink->GetPointD1D2( 0.3, ICurve::FROM_MINUS, ptCrv1) ; pLineLink->GetPointD1D2( 0.7, ICurve::FROM_MINUS, ptCrv2) ; // primo raccordo double dAng ; vtS.GetAngleXY( X_AX, dAng) ; PtrOwner pBiArc1( GetBiArc( ptS, - dAng, ptCrv1, bEvenClosed ? 90 : 0, 0.5)) ; // secondo raccordo vtE.Invert() ; vtE.GetAngleXY( X_AX, dAng) ; PtrOwner pBiArc2( GetBiArc( ptE, - dAng, ptCrv2, bEvenClosed ? - 90 : 180, 0.5)) ; pBiArc2->Invert() ; if ( ! IsNull( pBiArc1) && ! IsNull( pBiArc2)) { bUseBiArcs = true ; pMCrv->AddCurve( Release( pBiArc1)) ; pMCrv->AddLine( ptCrv2) ; pMCrv->AddCurve( Release( pBiArc2)) ; } } // se non è stato possibile creare raccordo, unisco linearmente if ( ! bUseBiArcs) pMCrv->AddCurve( Release( pLineLink)) ; if ( ! pMCrv->AddCurve( Release( pLineE))) return false ; // setto temp prop per ricordare curve aggiuntive per pulire angoli pMCrv->SetCurveTempProp( 0, 1) ; pMCrv->SetCurveTempProp( pMCrv->GetCurveCount() - 1, 1) ; return true ; } //---------------------------------------------------- static bool AdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const ICurveComposite* pCrvPocket, PocketParams PockParams, bool bStart) { // parametri double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ; double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ; double dRad = 0.5 * dDiam + dOffsR ; PtrOwner pCompo( CreateCurveComposite()) ; if ( ! bStart) pMCrv->Invert() ; Point3d ptTmp ; pMCrv->GetStartPoint( ptTmp) ; double dYCoord = ptTmp.y ; // quota verticale del percorso di svuotatura pCrvPocket->GetCurve( 2)->GetStartPoint( ptTmp) ; double dPocketSize = ptTmp.y ; int nCrvId = ( bStart ? 3 : 1) ; PtrOwner pLine( GetCurveLine( pCrvPocket->GetCurve( nCrvId)->Clone())) ; pLine->SimpleOffset( - dRad) ; Point3d ptP1, ptP2 ; pLine->GetStartPoint( ptP1) ; pLine->GetEndPoint( ptP2) ; if ( ! bStart) swap( ptP1, ptP2) ; int nProp2 ; // lato opposto a quello di riferimento aperto if ( pCrvPocket->GetCurveTempProp( 2, nProp2) && nProp2 != 0) { // caso 1 : pLine ha un estremo sopra e uno sotto il percorso di svuotatura pMCrv if ( ptP2.y < dYCoord && dYCoord < ptP1.y) { // creo tratto da ptP2 a ptP1 pCompo->AddPoint( ptP2) ; pCompo->AddLine( ptP1) ; // trovo il punto di pMCrv da cui ripartire per non lasciare aree residue pLine->SimpleOffset( - 0.5 * dDiam) ; pLine->ExtendStartByLen( EPS_SMALL) ; pLine->ExtendEndByLen( EPS_SMALL) ; IntersCurveCurve intCC( *pLine, *pCrvPocket) ; if ( intCC.GetIntersCount() == 0) return false ; IntCrvCrvInfo aInfo ; intCC.GetIntCrvCrvInfo( 0, aInfo) ; double dDeltaX = sqrt( max( 0., dDiam * dDiam / 4 - dYCoord * dYCoord)) ; if ( ! bStart) dDeltaX *= -1 ; Point3d ptCrv( aInfo.IciA[0].ptI.x + dDeltaX, dYCoord) ; double dPar = 0 ; if ( ! pMCrv->GetParamAtPoint( ptCrv, dPar)) { dPar = 0.5 ; pMCrv->GetPointD1D2( dPar, ICurve::FROM_MINUS, ptCrv) ; } if ( ! pMCrv->TrimStartAtParam( dPar)) pMCrv->Clear() ; if ( ptP1.y > dPocketSize) { // se ptP1 è esterno alla svuotatura scambio i punti in modo da usare ptP2 per il biarco ( così da non farlo fuoriuscire troppo) swap( ptP1, ptP2) ; pCompo->Invert() ; } // creo biarco fra ptP1 e ptCrv Vector3d vtDir ; pCompo->GetStartDir( vtDir) ; double dAng ; vtDir.GetAngleXY( X_AX, dAng) ; PtrOwner pBiArc( GetBiArc( ptP1, - dAng, ptCrv, bStart ? 0 : 180, 0.8)) ; bool bUseBiArc = false ; if ( ! IsNull( pBiArc)) { // verifico che con il biarco non si oltrepassi il lato obliquo chiuso della svuotatura IntersCurveCurve intCC2( *pBiArc, *pCompo) ; if ( intCC2.GetIntersCount() == 1) bUseBiArc = true ; } if ( bUseBiArc) pCompo->AddCurve( Release( pBiArc)) ; else { double dParLine = ( dYCoord - ptP2.y) / ( ptP1.y - ptP2.y) ; Point3d ptOnLine = Media( ptP2, ptP1, dParLine) ; pCompo->AddLine( ptOnLine) ; pCompo->AddLine( ptCrv) ; } } // caso 2 : pLine è completamente sopra/sotto la linea di svuotatura else { // se è sopra modifiche per ricondurmi al caso in cui è sotto if ( ptP2.y > dYCoord) { pLine->Invert() ; swap( ptP1, ptP2) ; } // trovo l'intersezione fra il prolungamento della linea e pMCrv if ( bStart) pLine->ExtendStartByLen( 1000) ; else pLine->ExtendEndByLen( 1000) ; IntersCurveCurve intCC ( *pLine, *pMCrv) ; if ( intCC.GetIntersCount() == 0) return false ; IntCrvCrvInfo aInfo ; intCC.GetIntCrvCrvInfo( 0, aInfo) ; Point3d ptInt ; ptInt = aInfo.IciA[0].ptI ; double dPar = 0 ; pMCrv->GetParamAtPoint( ptInt, dPar) ; if ( ! pMCrv->TrimStartAtParam( dPar)) pMCrv->Clear() ; pCompo->AddPoint( ptP2) ; pCompo->AddLine( ptInt) ; } } // se il lato opposto a quello di riferimento � chiuso bisogna distinguere i casi else { bool bStartPnt = false ; if ( ptP2.y < dYCoord) { bStartPnt = pCompo->AddPoint( ptP2) ; } else { Vector3d vtDir ; pLine->GetStartDir( vtDir) ; if ( bStart) vtDir.Invert() ; Point3d ptP2N = ptP2 - dDiam / 2 * abs( vtDir.x / vtDir.y) * vtDir ; if ( ptP2N.y < dYCoord) bStartPnt = pCompo->AddPoint( ptP2N) ; } if ( bStartPnt) { Point3d ptCrv ; pMCrv->GetStartPoint( ptCrv) ; pCompo->AddLine( ptCrv) ; } } // setto temp prop per ricordare che è curva aggiuntiva per pulire angoli for ( int i = 0 ; i < pCompo->GetCurveCount() ; i++) pCompo->SetCurveTempProp( i, 1) ; pMCrv->AddCurve( Release( pCompo), false) ; if ( ! bStart) pMCrv->Invert() ; // ripristino la direzione originaria return true ; } //---------------------------------------------------- static bool CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double dPocketSize, int nBase, int nSecondBase, PocketParams PockParams, ICurveComposite* pMCrv, ICurveComposite* pRCrv, bool& bOptimizedTrap) { // parametri double dDiam = 2 * PockParams.dRad ; double dOffsR = PockParams.dRadialOffset ; bOptimizedTrap = false ; Vector3d vtExtr ; pCrvPocket->GetExtrusion( vtExtr) ; // recupero le temp prop INTVECTOR vnProp( 4, 0) ; if ( pCrvPocket->GetCurveCount() == 4) { for ( int i = 0 ; i < 4 ; i ++) pCrvPocket->GetCurveTempProp( i, vnProp[i]) ; } else { vnProp[1] = pCrvPocket->GetCurve( 1)->GetTempProp() ; vnProp[3] = pCrvPocket->GetCurve( nSecondBase + 1)->GetTempProp() ; } // passo in un sistema di riferimento locale avente asse X allineato con uno dei due lati paralleli // (possibilmente aperto) e centro nel punto iniziale del lato pCrvPocket->ToLoc( frTrap) ; // calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso) double dLen0, dLen1, dLen2, dLen3, dMaxLarg = 0. ; pCrvPocket->GetCurve( nBase)->GetLength( dLen0) ; pCrvPocket->GetCurve( nSecondBase)->GetLength( dLen2) ; bool bRealTrap = pCrvPocket->GetCurveCount() == 4 ; for ( int i = 0 ; i < 4 && bRealTrap ; ++ i) bRealTrap = pCrvPocket->GetCurve( i)->GetType() == CRV_LINE ; if ( bRealTrap) { pCrvPocket->GetCurve( nBase + 1)->GetLength( dLen1) ; pCrvPocket->GetCurve( nSecondBase + 1)->GetLength( dLen3) ; // calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso) Point3d ptOrig ; pCrvPocket->GetCurve( 1)->GetStartPoint( ptOrig) ; Vector3d vtX ; pCrvPocket->GetCurve( 1)->GetStartDir( vtX) ; Frame3d frDim ; frDim.Set( ptOrig, Z_AX, vtX) ; frDim.Invert() ; BBox3d b3Dim ; pCrvPocket->GetBBox( frDim, b3Dim, BBF_EXACT) ; dMaxLarg = ( vnProp[0] != 0 ? b3Dim.GetDimY() : dPocketSize) ; } // calcolo percorso di svuotatura // se lati obliqui sono entrambi chiusi e dimensione svuotatura è maggiore di diametro fresa e minore del doppio gestione speciale if (( bRealTrap && dMaxLarg > PockParams.dRad * 2 + 10 * EPS_SMALL) && ((( vnProp[0] != 0 && vnProp[2] != 0) && ( vnProp[3] == 0 && vnProp[1] == 0) && ( max( dLen0, dLen2) < 2 * dDiam + EPS_SMALL)) || (( vnProp[1] != 0 && vnProp[3] != 0) && ( vnProp[0] == 0 && vnProp[2] == 0) && ( max( dLen1, dLen3) < 2 * dDiam + EPS_SMALL)))) { if ( ! SpecialAdjustTrapezoidSpiralForAngles( pMCrv, vnProp[0] == 0, pCrvPocket, PockParams)) { pMCrv->Clear() ; return false ; } } else { // trovo la quota Y per centro del Tool double dYCoord ; if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) // se base principale chiusa dYCoord = 0.5 * dDiam + dOffsR ; else if ( pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 0) // se base principale aperta e secondaria chiusa dYCoord = dPocketSize - 0.5 * dDiam - dOffsR ; else // se entrambi i lati paralleli sono aperti mi posiziono a metà della svuotatura dYCoord = 0.5 * dPocketSize ; double dXCoordStart, dXCoordEnd ; if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, true, dYCoord, dXCoordStart, dPocketSize, PockParams)) return false ; if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, false, dYCoord, dXCoordEnd, dPocketSize, PockParams)) return false ; if ( dXCoordStart > dXCoordEnd + 500 * EPS_SMALL) return false ; Point3d ptStart( dXCoordStart, dYCoord) ; Point3d ptEnd( dXCoordEnd, dYCoord) ; if ( bRealTrap && AreSamePointEpsilon( ptStart, ptEnd, 500 * EPS_SMALL) && ( vnProp[0] != 0 || vnProp[2] != 0)) { Vector3d vtDir1, vtDir3 ; pCrvPocket->GetCurve( 1)->GetStartDir( vtDir1) ; pCrvPocket->GetCurve( 3)->GetStartDir( vtDir3) ; // gestisco il caso speciale di un parallelogramma in cui anche l'altra dimensione della svuotatura è pari al diametro utensile if ( AreOppositeVectorApprox( vtDir1, vtDir3)) { PtrOwner pLine1( GetCurveLine( pCrvPocket->GetCurve( 1)->Clone())) ; PtrOwner pLine3( GetCurveLine( pCrvPocket->GetCurve( 3)->Clone())) ; if ( IsNull( pLine1) || IsNull( pLine3)) return false ; if ( ! pLine1->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset) || ! pLine3->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset)) return false ; Point3d ptS, ptE ; if ( vtDir3 * X_AX > EPS_SMALL) { pLine1->GetStartPoint( ptS) ; pLine3->GetStartPoint( ptE) ; } else { pLine1->GetEndPoint( ptE) ; pLine3->GetEndPoint( ptS) ; } if ( vnProp[0] != 0) { pMCrv->AddPoint( ptS) ; if ( vnProp[2] != 0) pMCrv->AddLine( ptE) ; else pMCrv->AddLine( ptStart) ; } else { pMCrv->AddPoint( ptE) ; if ( vnProp[0] != 0) pMCrv->AddLine( ptS) ; else pMCrv->AddLine( ptStart) ; pMCrv->Invert() ; } pMCrv->SetCurveTempProp( 0, 1) ; } } else { if ( ! pMCrv->AddPoint( ptStart)) return true ; if ( ! pMCrv->AddLine( ptEnd)) return true ; // aggiustamenti al percorso per rimuovere materiale residuo negli angoli if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 1 || pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 1) { Frame3d frOpen ; bool bSwitch = false ; // Base principale chiusa e base Secondaria aperta if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) { pCrvPocket->ChangeStartPoint( nSecondBase) ; // oriento il Frame ( ho sempre l'origine nell'estremo superiore del lato aperto) Vector3d vtDir ; pCrvPocket->GetStartDir( vtDir) ; Point3d ptORIG ; if ( vtDir.y > 0) pCrvPocket->GetCurve( nBase)->GetEndPoint( ptORIG) ; else pCrvPocket->GetCurve( nBase)->GetStartPoint( ptORIG) ; frOpen.Set( ptORIG, Z_AX, -X_AX) ; if ( ! frOpen.IsValid()) return false ; pCrvPocket->ToLoc( frOpen) ; pMCrv->ToLoc( frOpen) ; pMCrv->Invert() ; bSwitch = true ; } if ( pCrvPocket->GetCurve( 3)->GetTempProp( 0) == 0 && ! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, true)) { pMCrv->Clear() ; return false ; } if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == 0 && ! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, false)) { pMCrv->Clear() ; return false ; } if ( bSwitch) { pCrvPocket->ToGlob( frOpen) ; pMCrv->ToGlob( frOpen) ; pMCrv->Invert() ; } } } } if ( pMCrv->GetCurveCount() == 0) return true ; pMCrv->ToGlob( frTrap) ; if ( ! PockParams.bInvert) { pMCrv->Invert() ; // inverto le proprietà in modo che nProp3 sia sempre legata al punto iniziale e nProp1 a quello finale swap( vnProp[1], vnProp[3]) ; } // segno i lati aperti come temp prop della curva int nOpenEdges = vnProp[0] + vnProp[1] * 2 + vnProp[2] * 4 + vnProp[3] * 8 ; pMCrv->SetTempProp( nOpenEdges, 0) ; pMCrv->SetExtrusion( vtExtr) ; bOptimizedTrap = true ; return true ; } //---------------------------------------------------------------------------- static bool ModifyCurveToSmoothed( ICurveComposite* pCrv, PocketParams PockParams, double dRightLen, double dLeftLen, bool bAsParam) { // se non richiesto non faccio nulla if ( ! PockParams.bSmooth) return true ; // controllo parametri if ( pCrv == nullptr || dLeftLen < EPS_SMALL || dRightLen < EPS_SMALL) return false ; ICURVEPOVECTOR vCrvStepsToFill ; // vettore delle Curve da raccordare (curve della composita) ICURVEPOVECTOR vCrvArcs ; // vettore contenente gli Archi (tra le curve della composita) INTVECTOR vArcsToJump ; // vettore di indici per gli archi saltati (nel caso non si riesca a raccordare ...) // se nella composita ho meno di due curve allora non c'è nulla da raccordare const ICurve* pMyCrv = pCrv->GetFirstCurve() ; while ( pMyCrv != nullptr) { vCrvStepsToFill.emplace_back( pMyCrv->Clone()) ; pMyCrv = pCrv->GetNextCurve() ; } if ( vCrvStepsToFill.size() < 2) return true ; // controllo se la curva è chiusa ( nel caso inserisco nel vettore la prima curva due volte, all'inizio e alla fine) if ( pCrv->IsClosed()) { const ICurve* pMyCrvFirst = pCrv->GetFirstCurve() ; if ( pMyCrvFirst == nullptr) return false ; vCrvStepsToFill.emplace_back( pMyCrvFirst->Clone()) ; } double dUE_ref, dUS_ref, dRadius, dPar1, dPar2 ; // riempio il vettore degli archi, scorro tutte le curve da raccordare ( la i-esima e la (i+1)-esima) for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) { // controllo che le curve non siano già tangenti Vector3d vtS, vtE ; if ( ! vCrvStepsToFill[i]->GetEndDir( vtS) || ! vCrvStepsToFill[i+1]->GetStartDir( vtE)) continue ; if ( AreSameVectorApprox( vtS, vtE)) { vCrvArcs.emplace_back( CreateCurveComposite()) ; // arco nullo vArcsToJump.push_back( i) ; // da scartare continue ; } // ricavo i valori parametrici associati double dLen_act ; vCrvStepsToFill[i]->GetLength( dLen_act) ; double dLen_succ ; vCrvStepsToFill[i+1]->GetLength( dLen_succ) ; double dUE_ref = 1. ; if ( dLen_act > dLeftLen) dUE_ref -= dLeftLen / dLen_act ; double dUS_ref = 0. ; if ( dLen_succ > dRightLen) dUS_ref = dRightLen / dLen_succ ; // se valori trattati come parametri... if ( bAsParam) { // ... cerco i parametri corrispondenti sulle due curve double dU_cm_S = 0 ; double dULast1 = 1 ; double dULast2 = 1 ; vCrvStepsToFill[i]->GetDomain( dU_cm_S, dULast1) ; dUE_ref = ( 1 - dRightLen) * dULast1 ; vCrvStepsToFill[i+1]->GetDomain( dU_cm_S, dULast2) ; dUS_ref = dLeftLen * dULast2 ; } // prendo i punti sulle due curve rispetto a tali parametri Point3d ptS, ptE ; if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) || ! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE)) return false ; dRadius = Dist( ptS, ptE) ; // uso come raggio la distanza tra i due punti ( limite superiore valido) int nMaxTestForArcs = 3 ; // tentativi per creare l'arco int nIterForArcs = 0 ; // numero di intersezioni con altre curve bool IntersBTWArcs = false ; // caso particolare in cui due archi di raccordo si intersecano causa curva piccola // creo l'arco di raccordo e controllo che non sia troppo piccolo PtrOwner pCrvArc( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ; double dArcLen ; if ( ! IsNull( pCrvArc) && pCrvArc->IsValid() && ( ! pCrvArc->GetLength( dArcLen) || dArcLen < 5 * EPS_SMALL)) { vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco nullo vArcsToJump.push_back( i) ; // da scartare continue ; } // dal secondo arco in poi controllo che non ci siano intersezioni tra essi if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) { IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ; if ( intCCH.GetIntersCount() > 0 ) IntersBTWArcs = true ; } // se ho intersezioni tra archi o l'arco creato non è valido, allora provo altre nMaxTestForArcs volte a ricrearlo avvicinando i punti while (( IsNull( pCrvArc) || IntersBTWArcs) && nIterForArcs < nMaxTestForArcs) { dUE_ref = ( 1 + dUE_ref ) * 0.5 ; dUS_ref = dUS_ref * 0.5 ; if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) || ! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE)) return false ; dRadius = Dist( ptS, ptE) ; pCrvArc.Set( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ; nIterForArcs++ ; IntersBTWArcs = false ; if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) { // dal secondo arco in poi controllo che non ci siano intersezioni tra essi IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ; if ( intCCH.GetIntersCount() > 0) IntersBTWArcs = true ; } } if ( IsNull( pCrvArc) || ! pCrvArc->IsValid()) { // se ancora non riesco... salto l'arco vCrvArcs.emplace_back( CreateCurveArc()) ; // arco nullo vArcsToJump.push_back( i) ; // da scartare continue ; } // se ho creato l'arco lo memorizzo vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco valido vArcsToJump.push_back( -1) ; // da considerare } // creo la curva che restituirò PtrOwner pCrvCO_temp( CreateBasicCurveComposite()) ; if ( IsNull( pCrvCO_temp)) return false ; Point3d ptArcHelp, ptFirstPoint ; // unisco la curva i-esima con l'arco i-esimo (non guardo l'ultima curva nel vettore, controllo dopo il caso di curva chiusa) for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) { if ( vArcsToJump[i] == -1) { // se esiste l'arco ... Point3d ptArcS, ptArcE ; if ( ! vCrvArcs[i]->GetStartPoint( ptArcS) || ! vCrvArcs[i]->GetEndPoint( ptArcE) || ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcS, dUE_ref) || ! vCrvStepsToFill[i+1]->GetParamAtPoint( ptArcE, dUS_ref)) return false ; if ( i == 0) { // ... e sono nella prima iterazione ... if ( ! pCrv->IsClosed()) { // ... e se la curva è aperta -> la inserisco nel nuovo percorso if ( ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref)))) return false ; } ptFirstPoint = ptArcS ; // ... e se la curva è chiusa -> non la inserisco nel nuovo percorso } else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...) if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) || ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref)))) return false ; // aggiungo la curva 'tagliata per il raccordo' } if ( ! pCrvCO_temp->AddCurve( vCrvArcs[i]->Clone())) // aggiungo l'arco di raccordo return false ; ptArcHelp = ptArcE ; } else { // se non esiste l'arco ... if ( i == 0 ) { // e sono nella prima iterazione... if ( ! vCrvStepsToFill[0]->GetEndPoint( ptArcHelp)) return false ; if ( ! pCrv->IsClosed()) { // ...e se aperta // aggiungo la prima curva per intero if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUE_ref) || ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref)))) return false ; } ptFirstPoint = ptArcHelp ; } else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...) double dUS_cm ; if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) || ! vCrvStepsToFill[i]->GetDomain( dUS_cm, dUE_ref) || ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref))) || ! vCrvStepsToFill[i]->GetEndPoint( ptArcHelp)) return false ; } } } // ultima curva... if ( pCrv->IsClosed()) { // se curva chiusa... if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUS_ref) || ! vCrvStepsToFill[0]->GetParamAtPoint( ptFirstPoint, dUE_ref) || ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( dUS_ref, dUE_ref)))) return false ; } else { // se curva aperta... ( non ha importanza l'esistenza o meno degli archi...) double dUS_cm ; if ( ! vCrvStepsToFill.back()->GetParamAtPoint( ptArcHelp, dUS_ref) || ! vCrvStepsToFill.back()->GetDomain( dUS_cm, dUE_ref) || ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill.back()->CopyParamRange( dUS_ref, dUE_ref)))) return false ; } // ripristino il punto inziale se la curva è chiusa double dNewDU ; if ( ! pCrv->IsClosed()) { if ( ! pCrvCO_temp->GetParamAtPoint( ptArcHelp, dNewDU)) return false ; pCrvCO_temp->ChangeStartPoint( dNewDU) ; } // restituisco pCrv->Clear() ; pCrv->AddCurve( Release( pCrvCO_temp)) ; return true ; } //---------------------------------------------------------------------------- static bool CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, const ICRVCOMPOPOVECTOR& vOffIslands, ICurveComposite* pCrvLink) { // pulisco pCrvLink->Clear() ; // recupero il vettore estrusione dal bordo esterno offsettato della superficie Vector3d vtExtr ; vOffIslands[0]->GetExtrusion( vtExtr) ; // determino il riferimento naturale della svuotatura ( OCS con il vettore estrusione come asse Z) Frame3d frLoc ; frLoc.Set( ORIG, vtExtr) ; // non serve collegare ( può capitare nel tagliare percorsi con isole complesse) if ( AreSamePointApprox( ptStart, ptEnd)) return true ; // porto la curva di contenimento in locale a questo riferimento vector vOffsExtr ; for ( int i = 0 ; i < int( vOffIslands.size()) ; ++ i) { CurveLocal CrvOutLoc( vOffIslands[i], GLOB_FRM, frLoc) ; vOffsExtr.push_back( CrvOutLoc) ; } // creo la retta che li unisce PtrOwner pCompoLine( CreateBasicCurveComposite()) ; if ( ! pCompoLine->AddPoint( ptStart) || ! pCompoLine->AddLine( ptEnd)) return false ; pCompoLine->SetExtrusion( vtExtr) ; // la porto in locale al riferimento della svuotatura CurveLocal LineLoc( pCompoLine, GLOB_FRM, frLoc) ; // ... per le intersezioni // creo la nuova curva formata dai tratti di linee INTERNI alle isole e dai tratti sul bordo degli offset PtrOwner pCompo( pCompoLine->Clone()) ; if ( IsNull( pCompo) ) return false ; if ( ! pCompo->LocToLoc( GLOB_FRM, frLoc) ) return false; // memorizzo il tratto lineare nel caso qualche operazione fallisse PtrOwner pCompoHelp( pCompoLine->Clone()) ; if ( IsNull( pCompoHelp)) return false ; if ( ! pCompoHelp->LocToLoc( GLOB_FRM, frLoc)) return false ; // scorro il vettore degli indici degli offset delle isole intersecati for ( int i = 0 ; i < int( vOffIslands.size()) ; i++) { CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCompo, *vOffIslands[i]) ; intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; if ( ! pCompoHelp->Clear()) return false ; // per ogni intersezione j con l'offset dell'isola i for ( int j = 0 ; j < int( ccClass.size()) ; ++j ) { // se ho intersezione spezzo il segmento if ( ccClass[j].nClass == CRVC_OUT) { Point3d ptS ; pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ; double dOffS ; vOffIslands[i]->GetParamAtPoint( ptS, dOffS) ; Point3d ptE ; pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ; double dOffE ; vOffIslands[i]->GetParamAtPoint( ptE, dOffE) ; // recupero i due possibili percorsi e uso il più corto PtrOwner pCrvA( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ; PtrOwner pCrvB( vOffIslands[i]->CopyParamRange( dOffE, dOffS)) ; if ( IsNull( pCrvA) || IsNull( pCrvB)) return false ; double dLenA ; pCrvA->GetLength( dLenA) ; double dLenB ; pCrvB->GetLength( dLenB) ; if ( dLenA < dLenB) { pCompoHelp->AddCurve( Release( pCrvA)) ; } else { pCrvB->Invert() ; pCompoHelp->AddCurve( Release( pCrvB)) ; } } // se non interseco else { pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ; } } pCompo->Clear() ; pCompo->AddCurve( pCompoHelp->Clone()) ; } // riporto pCompo nel sistema di riferimento originale if ( ! pCompo->LocToLoc( frLoc, GLOB_FRM)) return false ; pCrvLink->AddCurve( Release( pCompo)) ; return true ; } //----------------------------------------------------------------------------- static bool ModifyBiArc( ICurve* pBiArcLink, double dToll, ICurveComposite* pNewBiArc) { // controllo dei parametri if ( pBiArcLink == nullptr) return false ; if ( dToll > 0.99 || dToll < 0.01) return false ; pNewBiArc->Clear() ; // dominio double dUS, dUE ; pBiArcLink->GetDomain( dUS, dUE) ; // prendo i due archi della curva BiArco PtrOwner pArc1( pBiArcLink->CopyParamRange( dUS, ( dUS + dUE) / 2)) ; PtrOwner pArc2( pBiArcLink->CopyParamRange(( dUS + dUE ) / 2, dUE)) ; if ( IsNull( pArc1) || ! pArc1->IsValid() || IsNull( pArc2) || ! pArc2->IsValid()) return false ; // primo pezzo pArc1->GetDomain( dUS, dUE) ; PtrOwner pArc1A( pArc1->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1 PtrOwner pArc1B( pArc1->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2 Point3d pt1A, pt1B ; pArc1A->GetEndPoint( pt1A) ; pArc1B->GetStartPoint( pt1B) ; PtrOwner pLine1( CreateBasicCurveLine()) ; pLine1->Set( pt1A, pt1B) ; // secondo pezzo pArc2->GetDomain( dUS, dUE) ; PtrOwner pArc2A( pArc2->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1 PtrOwner pArc2B( pArc2->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2 Point3d pt2A, pt2B ; pArc2A->GetEndPoint( pt2A) ; pArc2B->GetStartPoint( pt2B) ; PtrOwner pLine2( CreateBasicCurveLine()) ; pLine2->Set( pt2A, pt2B) ; // ricostruisco il nuovo link if ( ! pNewBiArc->AddCurve( Release( pArc1A)) || ! pNewBiArc->AddCurve( Release( pLine1)) || ! pNewBiArc->AddCurve( Release( pArc1B)) || ! pNewBiArc->AddCurve( Release( pArc2A)) || ! pNewBiArc->AddCurve( Release( pLine2)) || ! pNewBiArc->AddCurve( Release( pArc2B))) return false ; return true ; } //------------------------------------------------------------------------------ static bool CalcBoundedSmootedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, const Vector3d& vtEnd, double dParMeet, const ICRVCOMPOPOVECTOR& vOffIslands, PocketParams PockParams, ICurveComposite* pCrvLink) { // se senza smusso, ritorno tratto lineare if ( ! PockParams.bSmooth) return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; // creo il BiArc che unisce i due punti double dAngStart, dAngEnd ; vtStart.GetAngleXY( X_AX, dAngStart) ; vtEnd.GetAngleXY( X_AX, dAngEnd) ; PtrOwner pBiArcLink ; if ( dParMeet != 0) pBiArcLink.Set( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, dParMeet)) ; else { PtrOwner pCrvCir( CreateBasicCurveArc()) ; if ( IsNull( pCrvCir)) return false ; pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, - 360, 0, Z_AX) ; pBiArcLink.Set( pCrvCir->Clone()) ; pCrvLink->AddCurve( pBiArcLink->Clone()) ; return true ; } if ( IsNull( pBiArcLink)) return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; // se BiArco troppo grande allora lo modifico double dLenBiArc ; pBiArcLink->GetLength( dLenBiArc) ; if ( dLenBiArc > 200 * 1000 * EPS_SMALL) { PtrOwner pCrvNewBiArcS( CreateBasicCurveComposite()) ; ModifyBiArc( pBiArcLink, 0.2, pCrvNewBiArcS) ; pBiArcLink.Set( pCrvNewBiArcS) ; } // creo la nuova curva formata inizialmente dai tratti di archi PtrOwner pCompo( GetCurveComposite( pBiArcLink->Clone())) ; if ( IsNull( pCompo)) return false ; PtrOwner pCompoHelp( GetCurveComposite( pBiArcLink->Clone())) ; if ( IsNull( pCompoHelp)) return false ; // scorro tutte le curve per controllare le intersezioni ... for ( int i = 0 ; i < int( vOffIslands.size()) ; ++ i) { CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCompo, *vOffIslands[i]) ; intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; if ( ! pCompoHelp->Clear()) return false ; // per ogni intersezione j con l'offset dell'isola i for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { // se ho intersezione spezzo il segmento if ( ccClass[j].nClass == CRVC_OUT && ccClass.size() > 1) { Point3d ptS ; pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ; double dOffS ; vOffIslands[i]->GetParamAtPoint( ptS, dOffS, 1500 * EPS_SMALL) ; Point3d ptE ; pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ; double dOffE ; vOffIslands[i]->GetParamAtPoint( ptE, dOffE, 1500 * EPS_SMALL) ; // recupero i due possibili percorsi e uso il più corto PtrOwner pCrvA( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ; PtrOwner pCrvB( vOffIslands[i]->CopyParamRange( dOffE, dOffS)) ; if ( IsNull( pCrvA) || IsNull( pCrvB)) return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; double dLenA ; pCrvA->GetLength( dLenA) ; double dLenB ; pCrvB->GetLength( dLenB) ; if ( j != 0) { if ( dLenA < dLenB) { if ( ! pCompoHelp->AddCurve( Release( pCrvA))) return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; } else { pCrvB->Invert() ; if ( ! pCompoHelp->AddCurve( Release( pCrvB))) return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; } } else { pCrvB->Invert() ; if ( ! pCompoHelp->AddCurve( Release( pCrvB))) return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; } } // se non interseco else { if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; } } pCompo->Clear() ; if ( ! pCompo->AddCurve( pCompoHelp->Clone())) if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink)) return false ; } // se il BiArco è troppo piccolo allora lo approssimo con un segmento BBox3d bBox3 ; if ( pCompo->GetLocalBBox( bBox3)) { double dRadBB ; if ( bBox3.GetRadius( dRadBB) && dRadBB < 500 * EPS_SMALL) { if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink)) return false ; return true ; } } // smusso e restituisco ModifyCurveToSmoothed( pCompo, PockParams, 0.05, 0.05, true) ; pCrvLink->AddCurve( Release( pCompo)) ; return true ; } //---------------------------------------------------------------------------- static bool CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, const ICRVCOMPOPOVECTOR& vOffsCL, const ICRVCOMPOPOVECTOR& vFirstOffset, PocketParams PockParams, ICurveComposite* pCrvLink, double dLenPercS, double dLenPercE, int nMaxIter) { // controllo i parametri if ( pCrvS == nullptr || pCrvE == nullptr ) return false ; pCrvLink->Clear() ; // curva finale da restituire PtrOwner ptCrvFinal( CreateBasicCurveComposite()) ; if ( IsNull( ptCrvFinal)) return false ; // Prendo i punti, i vettori tangenti e i parametri di essi per le curve Point3d ptSS, ptSE, ptES, ptEE ; Vector3d vS, vE ; double dUSS, dUSE, dUES, dUEE, dLenS, dLenE ; dUSS = 0 ; dUSE = 0 ; pCrvS->GetEndPoint( ptSE) ; pCrvS->GetStartPoint( ptSS) ; pCrvE->GetStartPoint( ptES) ; pCrvE->GetEndPoint( ptEE) ; pCrvS->GetEndDir( vS) ; pCrvE->GetStartDir( vE) ; pCrvS->GetDomain( dUSS, dUSE) ; pCrvE->GetDomain( dUES, dUEE) ; pCrvS->GetLength( dLenS) ; pCrvE->GetLength( dLenE) ; // se ho una curva di primo Offset allora non devo accorciarla ... ( si per bordi esterni che per isole) for ( int i = 0 ; i < int( vFirstOffset.size()) ; ++ i) { if ( vFirstOffset[i]->IsPointOn( ptSS) && vFirstOffset[i]->IsPointOn( ptSE)) dLenPercS = 0 ; if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE)) dLenPercE = 0 ; } double dLStepS = ( dLenPercS < EPS_SMALL ? 0. : PockParams.dRad) ; double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : PockParams.dRad) ; dLenE = 0 ; // calcolo i possibili BiArchi tra le due curve int nIter = 0 ; if ( ! PockParams.bSmooth) nMaxIter = 1 ; // taglio la curva al massimo nMaxIter-volte e mi fermo al taglio più opportuno while ( nIter < nMaxIter) { // calcolo il BiArco PtrOwner ptBiArc( CreateBasicCurveComposite()) ; if ( ! CalcBoundedSmootedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, PockParams, ptBiArc) || ptBiArc->GetCurveCount() == 0) return false ; ptCrvFinal->Clear() ; ptCrvFinal.Set( ptBiArc->Clone()) ; if ( dLenPercE == 0 && dLenPercS == 0 ) break ; // se il BiArco creato interseca le altre curve di Offset allora mi fermo... bool bInterr = false ; for ( int i = 0 ; i < int( vOffsCL.size()) && ! bInterr ; ++ i) { if ( vOffsCL[i]->IsPointOn( ptSE) || vOffsCL[i]->IsPointOn( ptES)) continue ; CRVCVECTOR ccClass ; IntersCurveCurve intCC( *ptBiArc, *vOffsCL[i]) ; intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; if ( ccClass.size() > 1) // se intersezione bInterr = true ; } if ( bInterr) break ; // così come ultimo arco ho quello precedente (valido) // se ho trovato un BiArco valido, allora accorcio le due curve di Offset dLenS -= dLStepS ; dLenE += dLStepE ; // ricalcolo il punto iniziale e finale pCrvS->GetParamAtLength( dLenS, dUSE) ; pCrvS->GetPointD1D2( dUSE, ICurve::FROM_MINUS, ptSE) ; pCrvE->GetParamAtLength( dLenE, dUES) ; pCrvE->GetPointD1D2( dUES, ICurve::FROM_PLUS, ptES) ; // ricalcolo il vettore tangente iniziale e finale PtrOwner pCrvS_clone( pCrvS->Clone()) ; // curva di Offset iniziale accorciata PtrOwner pCrvE_clone( pCrvE->Clone()) ; // curva di Offset finale accorciata pCrvS_clone->ChangeStartPoint( dUSE) ; pCrvE_clone->ChangeStartPoint( dUES) ; pCrvS_clone->GetStartDir( vS) ; pCrvE_clone->GetStartDir( vE) ; ++ nIter ; } pCrvLink->AddCurve( ptCrvFinal->Clone()) ; // ultimo arco valido trovato return true ; } //---------------------------------------------------------- static bool GetUnclearedRegion( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vCrvs, ICURVEPOVECTOR& vLinks, const ICurveComposite* pCrv_orig, PocketParams PockParams, ISurfFlatRegion* pSrfToCut) { // controllo dei parametri if ( vFirstOffs.size() == 0 || ( int)vCrvs.size() < ( int)vFirstOffs.size()) return false ; pSrfToCut->Clear() ; // 1) creo la regione esterna PtrOwner pSrfExtern( CreateSurfFlatRegion()) ; if ( IsNull( pSrfExtern)) return false ; for ( int i = 0 ; i < int( vFirstOffs.size()) ; ++ i) { if ( i == 0) pSrfExtern->AddExtLoop( vFirstOffs[i]->Clone()) ; else { PtrOwner pCrvIntLoop( vFirstOffs[i]->Clone()) ; if( IsNull( pCrvIntLoop) || ! pCrvIntLoop->Invert() || ! pSrfExtern->AddIntLoop( pCrvIntLoop->Clone())) return false ; } } // ---- le seguenti regioni sono distinte per calcolar emeglio la Feed ---- // 2) Creo la regione svuotata dal Tool negli Offset, nei Links e sia dagli Offsets che dai Links PtrOwner pSrfTool_Offs( CreateSurfFlatRegion()) ; PtrOwner pSrfTool_Links( CreateSurfFlatRegion()) ; PtrOwner pSrfTool( CreateSurfFlatRegion()) ; if ( IsNull( pSrfTool_Offs) || IsNull( pSrfTool_Links) || IsNull( pSrfTool)) return false ; // creo un vettore che conterrà solamente i Link percorsi fino ad Ora ICRVCOMPOPOVECTOR vLinks_done ; for ( int i = PockParams.nType == POCKET_SPIRALIN ? 0 : ( int)vCrvs.size() - 1 ; PockParams.nType == POCKET_SPIRALIN ? i < int( vCrvs.size()) : i >= 0 ; PockParams.nType == POCKET_SPIRALIN ? ++ i : -- i) { // ================= LINK ================================ if ( i <= ( int)vLinks.size() && ! IsNull( vLinks[i]) && vLinks[i]->IsValid()) { // Feed PtrOwner pCompoLink_i( CreateCurveComposite()) ; if ( IsNull( pCompoLink_i)) return false ; pCompoLink_i->AddCurve( vLinks[i]->Clone()) ; // per Link ... PtrOwner pCrvLink_i( vLinks[i]->Clone()) ; if ( IsNull( pCrvLink_i)) return false ; PtrOwner pSrfToolRegLinki( GetSurfFlatRegionFromFatCurve( Release( pCrvLink_i), PockParams.dRad + 5 * EPS_SMALL, false, false)) ; if ( ! IsNull( pSrfToolRegLinki)) { if ( ! pSrfTool_Links->IsValid() || pSrfTool_Links->GetChunkCount() == 0) pSrfTool_Links.Set( GetBasicSurfFlatRegion( Release( pSrfToolRegLinki))) ; else pSrfTool_Links->Add( *pSrfToolRegLinki) ; } // risetto il Link come curva composita ... vLinks[i].Set( pCompoLink_i->Clone()) ; // inserisco il Link nel vettore dei Link percorsi vLinks_done.emplace_back( Release( pCompoLink_i)) ; } // ================== OFFSET ============================= // aggiorno la superificie svuotata PtrOwner pCrvOffs_i( CloneCurveComposite( vCrvs[i])) ; if ( IsNull( pCrvOffs_i)) return false ; PtrOwner pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( Release( pCrvOffs_i) , PockParams.dRad + 5 * EPS_SMALL, false, false)) ; if ( ! IsNull( pSrfToolRegOffi)) { if ( ! pSrfTool_Offs->IsValid() || pSrfTool_Offs->GetChunkCount() == 0) pSrfTool_Offs.Set( Release( pSrfToolRegOffi)) ; else pSrfTool_Offs->Add( *pSrfToolRegOffi) ; } } // 3) Calcolo la superificie svuotata pSrfTool.Set( pSrfTool_Offs) ; pSrfTool->Add( *pSrfTool_Links) ; // 4) Creo la regione contenente tutte le parti non svuotate pSrfToCut->CopyFrom( pSrfExtern) ; if ( ! pSrfToCut->Subtract( *pSrfTool)) return false ; return true ; } //---------------------------------------------------------------------------- static bool RemoveFirstLoopFromSfr( ISurfFlatRegion* pSrfOrig) { // controllo dei parametri if ( pSrfOrig == nullptr) return true ; // superficie da restituire PtrOwner pSrfRes( CreateBasicSurfFlatRegion()) ; if ( IsNull( pSrfRes)) return false ; // scorro tutti i chunk tranne il primo ( escludo quindi il primo chunk) for ( int c = 1 ; c < pSrfOrig->GetChunkCount() ; ++ c) { PtrOwner pSrfHelp( CreateBasicSurfFlatRegion()) ; if ( IsNull( pSrfHelp)) return false ; PtrOwner pCrvMiniChunkBorder( GetBasicCurveComposite( pSrfOrig->GetLoop( c, 0))) ; pSrfHelp->AddExtLoop( pCrvMiniChunkBorder->Clone()) ; if ( c == 1) pSrfRes.Set( pSrfHelp->Clone()) ; else pSrfRes->Add( *( pSrfHelp)) ; } // restituisco pSrfOrig->Clear() ; pSrfOrig->CopyFrom( pSrfRes) ; return true ; } //---------------------------------------------------------------------------- static bool RemoveExtraPartByMedialAxis( const ISurfFlatRegion* pChunkToCut, ICRVCOMPOPOVECTOR& vOffsFirstCurve, PocketParams PockParams, int& nOptFlag, Point3d& ptCentroid, ICurveComposite* pCrvPath) { // nOptFlag : 0 -> non faccio nulla | 1 -> svuoto con un centroide | 2 -> svuoto con un percorso // variabili iniziali nOptFlag = 2 ; bool bForceCentroid = false ; if ( pChunkToCut == nullptr) return false ; // curva che l'utensile dovrà seguire per svuotare la regione e curva di ingombro del tool PtrOwner pCrvStepByStepPath( CreateBasicCurveComposite()) ; if ( IsNull( pCrvStepByStepPath)) return false ; // copie del chunk che devo svuotare PtrOwner pSrfChunkToCutClone( pChunkToCut->Clone()) ; double dArea = INFINITO ; ICRVCOMPOPOVECTOR vCrvCoMedAxi ; // vettore dei medial Axis PNTVECTOR vPtCentroid ; // vettore di centroidi int nIter = 0 ; // numero di iterazioni while ( pSrfChunkToCutClone->IsValid() && pSrfChunkToCutClone->GetChunkCount() != 0 && pSrfChunkToCutClone->GetArea( dArea) && dArea > 10 * EPS_SMALL) { // finchè restano parti non svuotate la cui area totale è suffcientemente grande... if ( nIter > 25) // troppi tentativi... break ; nIter++ ; PtrOwner pSrfBiggerChunk( pSrfChunkToCutClone->CloneChunk( 0)) ; if ( IsNull( pSrfBiggerChunk)) return false ; if ( ! pSrfBiggerChunk->IsValid()) break ; double dAreaExt ; // se l'area del chunk è piccola... rimuovo il chunk dalla superficie da svuotare if ( pSrfBiggerChunk->GetArea( dAreaExt) && dAreaExt < 10 * EPS_SMALL) { if ( ! RemoveFirstLoopFromSfr( pSrfChunkToCutClone)) return false ; if ( IsNull( pSrfChunkToCutClone) || ! pSrfChunkToCutClone->IsValid()) break ; continue ; } // prendo il centroide del chunk Point3d ptC ; if ( ! pSrfBiggerChunk->GetCentroid( ptC)) break ; // creo la superficie che racchiude il mio tool PtrOwner pCrvToolShape( CreateBasicCurveArc()) ; if ( IsNull( pCrvToolShape)) return false ; pCrvToolShape->SetXY( ptC, PockParams.dRad + 5 * EPS_SMALL) ; PtrOwner pSrfTool( CreateBasicSurfFlatRegion()) ; if ( IsNull( pSrfTool) || ! pSrfTool->AddExtLoop( Release( pCrvToolShape)) || ! pSrfTool->IsValid()) break ; PtrOwner pSrfTest( CloneBasicSurfFlatRegion( pSrfBiggerChunk)) ; if ( IsNull( pSrfTest)) return false ; pSrfTest->Subtract( *pSrfTool) ; if ( IsNull( pSrfTest) || ! pSrfTest->IsValid() || ! pSrfTest->GetArea( dArea) || dArea < 10 * EPS_SMALL || bForceCentroid) { // se prima iterazione -> ritorno il centroide con punto if ( nIter == 1) { nOptFlag = 1 ; ptCentroid = ptC ; return true ; } // se non sono alla prima iterazione -> memorizzo il centroide nel vettore else { // controllo di non aver trovato un centroide già inserito ( per simmetria del chunk) for ( int cen = 0 ; cen < int( vPtCentroid.size()) ; ++ cen) { if ( AreSamePointApprox( vPtCentroid[cen], ptC)) { pSrfChunkToCutClone->Clear() ; break ; } } vPtCentroid.push_back( ptC) ; pSrfChunkToCutClone->Subtract( *pSrfTool) ; if ( IsNull( pSrfChunkToCutClone) || ! pSrfChunkToCutClone->IsValid()) break ; continue ; } } bForceCentroid = false ; // 1) ricavo il bordo della regione PtrOwner pCrvBorder( pSrfBiggerChunk->GetLoop( 0, 0)) ; if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid()) return false ; // 2) ricavo il medial Axis del bordo PolyLine PlMedAx ; if ( ! CurveSimpleMedialAxis( pCrvBorder, PlMedAx)) { bForceCentroid = true ; -- nIter ; continue ; } PtrOwner pCrvMedAx( CreateBasicCurveComposite()) ; if ( IsNull( pCrvMedAx)) return false ; if ( ! pCrvMedAx->FromPolyLine( PlMedAx)) { // se questo medial Axis è troppo piccolo e la polyLine non è convertitibile in curva composita ... bForceCentroid = true ; // 2a) forzo ad andare nel centroide -- nIter ; // 2b) scalo una iterzione continue ; } // abbellisco la curva pCrvMedAx->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ; PolyArc PlMedAxArc ; pCrvMedAx->ApproxWithArcsEx( 500 * EPS_SMALL, ANG_TOL_STD_DEG, 20.0, PlMedAxArc) ; pCrvMedAx->Clear() ; if( ! pCrvMedAx->FromPolyArc( PlMedAxArc)) { bForceCentroid = true ; -- nIter ; continue ; } // smusso la curva ModifyCurveToSmoothed( pCrvMedAx, PockParams, 0.025, 0.025, true) ; // se curva valida la inserisco nel vettore, altrimenti forzo il centroide if ( pCrvMedAx->IsValid()) vCrvCoMedAxi.emplace_back( pCrvMedAx->Clone()) ; else { bForceCentroid = true ; -- nIter ; continue ; } // 3) guardo quale regione svuoto con questa curva PtrOwner pSrfRemoved( GetSurfFlatRegionFromFatCurve( Release( pCrvMedAx), PockParams.dRad + 5 * EPS_SMALL, false, false)) ; if ( IsNull( pSrfRemoved) || ! pSrfRemoved->IsValid()) break ; // 4) aggiorno la regione togliendo la parte percorsa dal tool if ( ! pSrfChunkToCutClone->Subtract( *pSrfRemoved)) break ; if ( IsNull( pSrfChunkToCutClone) || ! pSrfChunkToCutClone->IsValid()) break ; } // se entrambi i vettori sono vuoti l'area originaria era più piccola di 10 * EPS_SMALL -> non faccio nulla if ( vCrvCoMedAxi.empty() && vPtCentroid.empty()) { nOptFlag = 0 ; return true ; } // ora collego la varie curve medial Axis trovate tra loro (ottenendo quindi una curva che svuota tutta la regione) // curva che collega primo e ultimo medial Axis PtrOwner pCrvCoBackLink( CreateBasicCurveComposite()) ; if ( IsNull( pCrvCoBackLink)) return false ; Point3d ptSOriginal, ptEOriginal ; Vector3d vtSOriginal, vtEOriginal ; // parametri iniziali del primo e ultimo medial Axis trovato if ( ! vCrvCoMedAxi[0]->GetStartPoint( ptSOriginal) || ! vCrvCoMedAxi.back()->GetEndPoint( ptEOriginal) || ! vCrvCoMedAxi[0]->GetStartDir( vtSOriginal) || ! vCrvCoMedAxi.back()->GetEndDir( vtEOriginal)) return false ; pCrvPath->AddCurve( vCrvCoMedAxi[0]->Clone()) ; // inserisco il primo medial Axis nel percorso finale for ( int i = 1 ; i < int( vCrvCoMedAxi.size()) ; ++ i) { // scorro i restanti Point3d ptS, ptE ; Vector3d vtS, vtE ; // parametri iniziali del medialAxis i-esimo e (i-1)esimo if ( ! vCrvCoMedAxi[i]->GetStartPoint( ptE) || ! vCrvCoMedAxi[i-1]->GetEndPoint( ptS) || ! vCrvCoMedAxi[i]->GetStartDir( vtE) || ! vCrvCoMedAxi[i-1]->GetEndDir( vtS)) return false ; PtrOwner pCrvLink( CreateBasicCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; if ( ! CalcBoundedSmootedLink( ptS, vtS, ptE, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink)) if ( ! CalcBoundedLink( ptS, ptE, vOffsFirstCurve, pCrvLink)) return false ; if ( ! pCrvPath->AddCurve( pCrvLink->Clone()) || ! pCrvPath->AddCurve( vCrvCoMedAxi[i]->Clone())) return false ; } if ( ! AreSamePointEpsilon( ptEOriginal, ptSOriginal, EPS_SMALL) && ! CalcBoundedSmootedLink( ptEOriginal, vtEOriginal, ptSOriginal, vtSOriginal, 0.5, vOffsFirstCurve, PockParams, pCrvCoBackLink) && ! CalcBoundedLink( ptEOriginal, ptSOriginal, vOffsFirstCurve, pCrvCoBackLink)) return false ; // se ho trovato dei centroidi li unisco nei punti più vicini a questo percorso for ( int i = 0 ; i < int( vPtCentroid.size()) ; ++ i) { // 1) cerco il punto sulla curva più vicino al centroide i Point3d ptClosestOnPath ; int nFlag ; if ( ! DistPointCurve( vPtCentroid[i], *pCrvPath).GetMinDistPoint( EPS_SMALL, ptClosestOnPath, nFlag)) return false ; // 2) spezzo la curva al paramtro del punto più vicino double dU ; if ( ! pCrvPath->GetParamAtPoint( ptClosestOnPath, dU)) return false ; PtrOwner pCrvA( GetBasicCurveComposite( pCrvPath->CopyParamRange( 0, dU))) ; if ( IsNull( pCrvA)) return false ; PtrOwner pCrvB( GetBasicCurveComposite( pCrvPath->CopyParamRange( dU, pCrvPath->GetCurveCount()))) ; if ( IsNull( pCrvB)) return false ; // 3) prendo il vettore tangente al percorso nel punto trovato nel pCrvPath Vector3d vtTanCpt ; Point3d ptH ; if ( ! pCrvPath->GetPointTang( dU, ICurve::FROM_MINUS, ptH, vtTanCpt)) return false ; // 4) collego i due punti (centroide e punto più vicino alla curva) PtrOwner pCrvPath1( CreateBasicCurveComposite()) ; if ( IsNull( pCrvPath1)) return false ; if ( ! CalcBoundedSmootedLink( ptClosestOnPath, vtTanCpt, vPtCentroid[i], vtTanCpt, 0, vOffsFirstCurve, PockParams, pCrvPath1) && ! CalcBoundedLink( ptClosestOnPath, vPtCentroid[i], vOffsFirstCurve, pCrvPath1)) return false ; pCrvPath->Clear() ; pCrvPath->AddCurve( Release( pCrvA)) ; if ( ! pCrvPath->AddCurve( Release( pCrvPath1))) return false ; pCrvPath->AddCurve( Release( pCrvB)) ; } // la curva resitituita è una curva aperta che ha come primo punto il punto iniziale del primo medialAxis // la curva restituita collega tutti i medial Axis tra di loro // la curva restitutita collega tutti i centroidi con delle circonferenze // la curva restituita ha come punto finale l'ultimo punto dell'ultimo medial Axis return true ; } //--------------------------------------------------------------------------- static bool CutOffsetToClosestPoint( ICRVCOMPOPOVECTOR& vCurves, const Point3d& ptFocus, ICurveComposite* pCrv1, ICurveComposite* pCrv2, int& nIndex) { // controllo di avere almeno un offset... if ( vCurves.empty()) return false ; // variabili iniziali Point3d ptCl_H ; double dDistance = INFINITO ; int nFlag ; pCrv1->Clear() ; pCrv2->Clear() ; nIndex = -1 ; Point3d ptCL ; // scorro tutti gli offset ad eccezione del primo ( a meno di averne uno solo ) for ( int j = ( int( vCurves.size()) == 1 ? 0 : 1) ; j < int( vCurves.size()) ; ++ j) { // non ho offset nulli, però controllo... if ( IsNull( vCurves[j])) continue ; // prendo il punto più vicino if ( ! DistPointCurve( ptFocus, *vCurves[j]).GetMinDistPoint( EPS_SMALL, ptCl_H, nFlag)) continue ; // cerco il punto in assoluto più vicino if ( dDistance > Dist( ptCl_H, ptFocus)) { dDistance = Dist( ptCl_H, ptFocus) ; ptCL = ptCl_H ; nIndex = j ; } } // se non ho trovato nulla, esco if ( nIndex < 0) return false ; // ricavo le due curve ( l'Offset viene spezzato in due sottocurve mediante il punto trovato) double dUTan ; vCurves[nIndex]->GetParamAtPoint( ptCL, dUTan) ; pCrv1->AddCurve( vCurves[nIndex]->Clone()) ; if ( ! pCrv1->TrimEndAtParam( dUTan)) pCrv1->Clear() ; pCrv2->AddCurve( vCurves[nIndex]->Clone()) ; if ( ! pCrv2->TrimStartAtParam( dUTan)) pCrv2->Clear() ; return true ; } //--------------------------------------------------------------------------- static bool CutLinkToClosestPoint( ICURVEPOVECTOR& vCurves, ICRVCOMPOPOVECTOR& vOffs, const Point3d& ptFocus, ICurveComposite* pCrv1, ICurveComposite* pCrv2, bool& bFound, int& nIndex) { // variabili iniziali double dDistance = INFINITO ; Point3d ptCl_H ; int nFlag ; pCrv1->Clear() ; pCrv2->Clear() ; Point3d ptCL ; nIndex = - 1 ; bFound = false ; // scorro tutti i link cercando il più vicino for ( int j = 0 ; j < int( vCurves.size()) ; ++ j) { // escludo i links nulli ... if ( IsNull( vCurves[j])) continue ; // distanza minima tra link e curva if ( ! DistPointCurve( ptFocus, *vCurves[j]).GetMinDistPoint( EPS_SMALL, ptCl_H, nFlag)) continue ; // cerco la distanza minimia assoluta ( non deve essere sopra un Offset ) if ( dDistance > Dist( ptCl_H, ptFocus) && ! ( vOffs[j-1]->IsPointOn( ptCl_H) || vOffs[j]->IsPointOn( ptCl_H))) { dDistance = Dist( ptCl_H, ptFocus) ; nIndex = j ; bFound = true ; } } // se non ho trovato nulla, esco if ( nIndex < 0) return false ; // ricavo le due curve ( il link viene spezzato in due sottocurve mediante il punto trovato ) double dUTan ; vCurves[nIndex]->GetParamAtPoint( ptCL, dUTan) ; pCrv1->AddCurve( vCurves[nIndex]->Clone()) ; if ( ! pCrv1->TrimEndAtParam( dUTan)) pCrv1->Clear( ) ; pCrv2->AddCurve( vCurves[nIndex]->Clone()) ; if ( ! pCrv2->TrimStartAtParam( dUTan)) pCrv2->Clear() ; return true ; } //------------------------------------------------------------------------------ static bool CutCurveByOffsets( ICurveComposite* pCurve, ICRVCOMPOPOVECTOR& vOffs) { // controllo parametri ingresso if ( vOffs.empty()) return true ; // vettore di curve ausiliario ICRVCOMPOPOVECTOR vOffOrig( vOffs.size()) ; for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) vOffOrig[i].Set( vOffs[i]->Clone()) ; // punto iniziale corrente Point3d ptStart ; if ( ! pCurve->GetStartPoint( ptStart)) return false ; // curve d'appoggio PtrOwner pCompo( pCurve->Clone()) ; PtrOwner pCompoHelp( pCurve->Clone()) ; PtrOwner pOffCheck( CreateCurveComposite()) ; if ( IsNull( pCompoHelp) || IsNull( pCompo) || IsNull( pOffCheck)) return false ; int nTypeOfCut = -1 ; // scorro tutte le curve for ( int i = 0; i < int( vOffOrig.size()) ; ++ i) { // controllo se la curva è quella attuale if ( vOffOrig[i]->IsPointOn( ptStart)) pOffCheck.Set( vOffOrig[i]->Clone()) ; // classificazione CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCompo, *vOffOrig[i]) ; intCC.GetCurveClassification( 0, 10 * EPS_SMALL, ccClass) ; if ( ! pCompoHelp->Clear()) return false ; // se In/Out if ( ccClass.size() > 1) { if ( ccClass[0].nClass == CRVC_IN) nTypeOfCut = CRVC_OUT ; else nTypeOfCut = CRVC_IN ; // scorro le classificazioni ottenute for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { if ( ccClass[j].nClass == nTypeOfCut) { Point3d ptS ; pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ; double dOffS ; vOffOrig[i]->GetParamAtPoint( ptS, dOffS) ; Point3d ptE ; pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ; double dOffE ; vOffOrig[i]->GetParamAtPoint( ptE, dOffE) ; // recupero i due possibili percorsi e uso il più corto PtrOwner pCrvA( vOffOrig[i]->CopyParamRange( dOffS, dOffE)) ; PtrOwner pCrvB( vOffOrig[i]->CopyParamRange( dOffE, dOffS)) ; if ( IsNull( pCrvA) || IsNull( pCrvB)) return false ; double dLenA ; pCrvA->GetLength( dLenA) ; double dLenB ; pCrvB->GetLength( dLenB) ; if ( dLenA < dLenB) { CRVCVECTOR ccClassA ; IntersCurveCurve intCCA( *pCrvA, *pOffCheck) ; intCCA.GetCurveClassification( 0, 10 * EPS_SMALL, ccClassA) ; if ( ccClassA.size() > 0 && i == vOffOrig.size() - 1 && ccClassA[0].nClass == CRVC_ON_M) { if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) return false ; continue ; } if ( ! pCompoHelp->AddCurve( Release( pCrvA))) return false ; } else { pCrvB->Invert() ; CRVCVECTOR ccClassB ; IntersCurveCurve intCCB( *pCrvB, *pOffCheck) ; intCCB.GetCurveClassification( 0, 10 * EPS_SMALL, ccClassB) ; if ( ccClassB.size() > 0 && i == vOffOrig.size() - 1 && ccClassB[0].nClass == CRVC_ON_M) { if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) return false ; continue ; } if ( ! pCompoHelp->AddCurve( Release( pCrvB))) return false ; } } else { if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) return false ; } } pCompo->Clear() ; pCompo->AddCurve( pCompoHelp->Clone()) ; } } // restituisco la nuova curva pCurve->Clear() ; pCurve->AddCurve( Release( pCompo)) ; return true ; } //---------------------------------------------------------------------------- static bool GetNewCurvetWithCentroid( const ICurveComposite* pCrvH1, const ICurveComposite* pCrvH2, PocketParams PockParams, Point3d& ptC, bool bCir, ICRVCOMPOPOVECTOR& VFirstOff, ICurveComposite* pCrvNewCurve) { // creazione dei due possibili percorsi per raggiungere il centroide PtrOwner pPath1( CreateCurveComposite()) ; PtrOwner pPath2( CreateCurveComposite()) ; if ( IsNull( pPath1) || IsNull( pPath2)) return false ; pCrvNewCurve->Clear() ; Point3d ptS, ptE = ptC ; Vector3d vtTanS, vtTanE ; // creo una circonferenza if ( bCir) { // prendo il punto iniziale if ( pCrvH1 == nullptr || pCrvH1->GetFirstCurve() == nullptr) { if ( ! pCrvH2->GetStartPoint( ptS)) return false ; } else { if ( ! pCrvH1->GetEndPoint( ptS)) return false ; } // creo la circonferenza if ( ! CalcBoundedSmootedLink( ptS, vtTanS, ptE, vtTanE, 0, VFirstOff, PockParams, pPath1)) return false ; Vector3d vtS_cir ; pPath1->GetStartDir( vtS_cir) ; Vector3d vtS_CrvH1 ; pCrvH1->GetEndDir( vtS_CrvH1) ; if ( ! AreSameVectorApprox( vtS_cir, vtS_CrvH1)) pPath1->Invert() ; // controllo che la circonferenza sia ammissibile ( solo che non esca dalle prime curve di Offset) Point3d ptCrvS ; pPath1->GetStartPoint( ptCrvS) ; if ( ! CutCurveByOffsets( pPath1, VFirstOff)) // taglio la curva sugli Offsets return false ; double dUCrvS ; ModifyCurveToSmoothed( pPath1, PockParams, 0.075, 0.075, true) ; if ( pPath1->IsClosed()) { pPath1->GetParamAtPoint( ptCrvS, dUCrvS) ; pPath1->ChangeStartPoint( dUCrvS) ; } // creo la nuova curva con la circonferenza if ( pCrvH1 != nullptr && pCrvH1->GetFirstCurve() != nullptr) if ( ! pCrvNewCurve->AddCurve( pCrvH1->Clone())) // aggiungo inizio return false ; if ( ! pCrvNewCurve->AddCurve( pPath1->Clone())) // aggiungo la circonferenza return false ; if ( pCrvH2 != nullptr && pCrvH2->GetFirstCurve() != nullptr) if ( ! pCrvNewCurve->AddCurve(( pCrvH2->Clone()))) // aggiungo la fine return false ; } // creo un BiArco else { // prendo il vettore tangente e il punto iniziale if ( pCrvH1 == nullptr || pCrvH1->GetFirstCurve() == nullptr) { if ( ! pCrvH2->GetStartDir( vtTanS) || ! pCrvH2->GetStartPoint( ptS)) return false ; } else { if ( ! pCrvH1->GetEndDir( vtTanS) || ! pCrvH1->GetEndPoint( ptS)) return false ; } // creo i due Biarchi if ( ! CalcBoundedSmootedLink( ptS, vtTanS, ptE, vtTanS, 0.5, VFirstOff, PockParams, pPath1) || ! CalcBoundedSmootedLink( ptE, vtTanS, ptS, vtTanS, 0.5, VFirstOff, PockParams, pPath2)) return false ; // creo la nuova curva con il doppio Biarco if ( pCrvH1 != nullptr && pCrvH1->GetFirstCurve() != nullptr) if ( ! pCrvNewCurve->AddCurve( pCrvH1->Clone())) // aggiungo inizio return false ; if ( ! pCrvNewCurve->AddCurve( pPath1->Clone()) || ! pCrvNewCurve->AddCurve( pPath2->Clone())) // aggiungo i due BiArchi return false ; if ( pCrvH2 != nullptr && pCrvH2->GetFirstCurve() != nullptr) if ( ! pCrvNewCurve->AddCurve(( pCrvH2->Clone()))) // aggiungo la fine return false ; } return true ; } //---------------------------------------------------------------------------- static bool ManageSmoothAndAutoInters( ICurveComposite* pCrv, ICurveComposite* pCrvPath, ICurveComposite* pPath1, PocketParams PockParams, ICurveComposite* pPath2, ICRVCOMPOPOVECTOR& vOffsCL) { // controllo parametri if ( pCrv == nullptr) return false ; // check AutoIntersezioni... SelfIntersCurve SICrv( *pCrv) ; if ( SICrv.GetCrossOrOverlapIntersCount() > 0) { // se ci sono autointersezioni Vector3d vTanE ; Point3d ptHS ; pCrvPath->GetEndDir( vTanE) ; pCrvPath->GetEndPoint( ptHS) ; bool bFound = false ; int nIter = - 45 ; // angolo incrementale nel caso di autointersezioni while ( ! bFound && nIter < 45) { vTanE.Rotate( Z_AX, 90 - nIter) ; // vettore perpendicolare PtrOwner pLine( CreateCurveLine()) ; pLine->SetPVL( ptHS, vTanE, ( 2 * PockParams.dRad) / 3 - 5 * EPS_SMALL) ; // segmento uscente CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pLine, *pCrv) ; intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; if (( int)ccClass.size() > 1) // se intersezione con parte precedente inverto la direzione pLine->SetPVL( ptHS, - vTanE, ( 2 * PockParams.dRad) / 3 - 5 * EPS_SMALL) ; if ( IsNull( pLine) || ! pLine->IsValid()) break ; // punto finale segmento uscente Point3d ptEndLine ; pLine->GetEndPoint( ptEndLine) ; Point3d ptNear ; double dUS, dUE ; pPath2->GetDomain( dUS, dUE) ; PtrOwner pCrvHelper(( pPath2->CopyParamRange( dUS, dUE - 0.5))) ; pCrvHelper->GetEndPoint( ptNear) ; PtrOwner pLine1( GetLinePointTgCurve( ptEndLine, *pPath2, ptNear)) ; // segmento tangente if ( IsNull( pLine1) || ! pLine1->IsValid()) break ; // creo la nuova curva formata da BiArco 1 - Path - Segmento uscente - Segmento tangente - parte restante di Biarco 2 Point3d ptELine1 ; pLine1->GetEndPoint( ptELine1) ; pPath2->GetParamAtPoint( ptELine1, dUS) ; PtrOwner pNewPath2( GetCurveComposite( pPath2->CopyParamRange( dUS, dUE))) ; // creo la curva formata dai due segmenti ma smussati tra loro... PtrOwner pCrvTwoSeg( CreateCurveComposite()) ; if ( ! pCrvTwoSeg->AddCurve( pCrvPath->Clone()) || ! pCrvTwoSeg->AddCurve( pLine->Clone()) || ! pCrvTwoSeg->AddCurve( pLine1->Clone())) return false ; ModifyCurveToSmoothed( pCrvTwoSeg, PockParams, 0.2, 0.2, true) ; PtrOwner ptNewCrv( CreateCurveComposite()) ; if ( ptNewCrv->AddCurve( pPath1->Clone()) && ptNewCrv->AddCurve( pCrvTwoSeg->Clone()) && ptNewCrv->AddCurve( pNewPath2->Clone())) { SelfIntersCurve SICrvLoop( *ptNewCrv) ; if ( SICrvLoop.GetCrossOrOverlapIntersCount() == 0) { pCrv->Clear() ; pCrv->AddCurve( Release( ptNewCrv)) ; bFound = true ; } else { // se trovo autointersezioni, ripeto cambiando l'angolo del segmento uscente ++nIter ; } } else break ; } } // smusso questa curva sulle varie curve di offsets Point3d ptCheck1, ptCheck2 ; PtrOwner pCrvH( pCrv->Clone()) ; // copia della curva if ( ! CutCurveByOffsets( pCrv, vOffsCL)) { // taglio la curva sugli Offsets pCrv->Clear() ; pCrv->AddCurve( Release( pCrvH)) ; } else { // controllo che i punti iniziali coincidano pCrv->GetStartPoint( ptCheck1) ; pCrvH->GetStartPoint( ptCheck2) ; if ( ! AreSamePointApprox( ptCheck1, ptCheck2)) { // se i punti iniziali della curva prima e dopo lo smusso non coincidono ... pCrv->Clear() ; pCrv->AddCurve( Release( pCrvH)) ; } } return true ; } //---------------------------------------------------------------------------- static bool GetNewCurvetWithPath( const ICurveComposite* pCrvH1, const ICurveComposite* pCrvH2, ICurveComposite* pCrvPath, ICRVCOMPOPOVECTOR& VFirstOff, ICRVCOMPOPOVECTOR& VoffsCl, PocketParams PockParams, ICurveComposite* pCrvNewCurve) { // inizializzo i due possibili percorsi PtrOwner pPath1( CreateCurveComposite()) ; PtrOwner pPath2( CreateCurveComposite()) ; if ( IsNull( pPath1) || IsNull( pPath2) || pCrvPath == nullptr) return false ; pCrvNewCurve->Clear() ; Point3d ptS, ptE, ptH ; Vector3d vtTanS, vtTanE, vtH ; if ( ! pCrvPath->GetStartPoint( ptE) || ! pCrvPath->GetStartDir( vtTanE) || ! pCrvPath->GetEndPoint( ptH) || ! pCrvPath->GetEndDir( vtH)) return false ; if ( pCrvH1 == nullptr || pCrvH1->GetFirstCurve() == nullptr) { if ( ! pCrvH2->GetStartPoint( ptS) || ! pCrvH2->GetStartDir( vtTanS)) return false ; } else { if ( ! pCrvH1->GetEndPoint( ptS) || ! pCrvH1->GetEndDir( vtTanS)) return false ; } // creo i due Biarchi if ( ! CalcBoundedSmootedLink( ptS, vtTanS, ptE, vtTanE, 0.5, VFirstOff, PockParams, pPath1)) if ( ! CalcBoundedLink( ptS, ptE, VFirstOff, pPath1)) return false ; if ( ! CalcBoundedSmootedLink( ptH, vtH, ptS, vtTanS, 0.5, VFirstOff, PockParams, pPath2)) if ( ! CalcBoundedLink( ptH, ptS, VFirstOff, pPath2)) return false ; // creo la curva formata da BiArco1 - Path - BiArco 2 PtrOwner pCrvToAdd( CreateCurveComposite()) ; if ( IsNull( pCrvToAdd) || ! pCrvToAdd->AddCurve( pPath1->Clone()) || ! pCrvToAdd->AddCurve( pCrvPath->Clone()) || ! pCrvToAdd->AddCurve( pPath2->Clone())) return false ; // cerco di togliere le auto intersezioni tra i biarchi e le curve di Medial Axis if ( ! ManageSmoothAndAutoInters( pCrvToAdd, pCrvPath, pPath1, PockParams, pPath2, VoffsCl)) return false ; // curva finale if ( pCrvH1 != nullptr && pCrvH1->GetFirstCurve() != nullptr) if ( ! pCrvNewCurve->AddCurve( pCrvH1->Clone())) // aggiungo inizio return false ; if ( ! pCrvNewCurve->AddCurve( pCrvToAdd->Clone())) { // aggiungo BiArco - Path - BiArco // nel caso non funzioni questo raccordo, recupero un biarco valido e uso questo ... Vector3d vtHS, vtHE ; Point3d ptHS, ptHE ; pCrvNewCurve->GetEndPoint( ptHS) ; pCrvNewCurve->GetEndDir( vtHS) ; pCrvToAdd->GetStartPoint( ptHE) ; pCrvToAdd->GetStartDir( vtHE) ; PtrOwner pCrvBiArcHelper( CreateCurveComposite()) ; if ( ! CalcBoundedSmootedLink( ptHS, vtHS, ptHE, vtHE, 0.5, VFirstOff, PockParams, pCrvBiArcHelper)) if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper)) return false ; if ( ! pCrvNewCurve->AddCurve( Release( pCrvBiArcHelper)) || ! pCrvNewCurve->AddCurve( pCrvToAdd->Clone())) return false ; } if ( pCrvH2 != nullptr && pCrvH2->GetFirstCurve() != nullptr) { if ( ! pCrvNewCurve->AddCurve( pCrvH2->Clone())) { // aggiungo fine // nel caso non funzioni questo raccordo, recupero un biarco valido e uso questo ... int nTry = 1 ; int nMaxTry = 10 ; bool bFound = false ; while ( nTry < nMaxTry && ! bFound) { Vector3d vtHS, vtHE ; Point3d ptHS, ptHE ; pCrvNewCurve->GetEndPoint( ptHS) ; pCrvNewCurve->GetEndDir( vtHS) ; pCrvH2->GetStartPoint( ptHE) ; pCrvH2->GetStartDir( vtHE) ; PtrOwner pCrvBiArcHelper( CreateCurveComposite()) ; if ( ! CalcBoundedSmootedLink( ptHS, vtHS, ptHE, vtHE, 0.5, VFirstOff, PockParams, pCrvBiArcHelper)) if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper) ) return false ; if ( ! pCrvNewCurve->AddCurve( pCrvBiArcHelper->Clone()) || ! pCrvNewCurve->AddCurve( pCrvH2->Clone())) { ++nTry ; } else { bFound = true ; } } if ( ! bFound) return false ; } } return true ; } //--------------------------------------------------------------------------- static bool GetCurveWeightInfo( const ICurveComposite* pCrvCompo, double dMaxLen, double& dToTRot, int& nSmallArcs, int& nSmallLines) { dToTRot = 0 ; nSmallArcs = 0 ; nSmallLines = 0 ; if ( pCrvCompo == nullptr) return true ; int nLines = 0 ; PtrOwner pCrvLineOld( CreateCurveLine()) ; if ( IsNull( pCrvLineOld)) return false ; // scorro tutte le curve della composita const ICurve* pMyCrv = pCrvCompo->GetFirstCurve() ; while ( pMyCrv != nullptr) { if ( pMyCrv->GetType() == CRV_ARC) { // nel caso di archi ... nLines = 0 ; PtrOwner pCrvArc( GetCurveArc( pMyCrv->Clone())) ; double dAngCenter = abs( pCrvArc->GetAngCenter()) ; dToTRot += abs( dAngCenter) ; double dLen = 0 ; if ( pCrvArc->GetLength( dLen) && dLen < dMaxLen) ++ nSmallArcs ; } else if ( pMyCrv->GetType() == CRV_LINE) { // nel caso di linee ... ++ nLines ; if ( nLines == 1) pCrvLineOld.Set( GetCurveLine( pMyCrv->Clone())) ; else { PtrOwner pNewMyCrv( GetCurveLine( pMyCrv->Clone())) ; Vector3d vtNew, vtOld ; pCrvLineOld->GetEndDir( vtOld) ; pNewMyCrv->GetStartDir( vtNew) ; double dAngCorner ; vtOld.GetAngle( vtNew, dAngCorner) ; dToTRot += abs( dAngCorner) ; pCrvLineOld.Set( pNewMyCrv) ; nLines = 0 ; } double dLen = 0 ; if ( pMyCrv->GetLength( dLen) && dLen < dMaxLen) ++ nSmallLines ; } pMyCrv = pCrvCompo->GetNextCurve() ; } return true ; } //--------------------------------------------------------------------------- static bool ChoosePath( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, int nP, double dPerP, double dMaxLen, int& nC) { // controllo dei parametri if ( pCrv1 == nullptr || pCrv2 == nullptr || ! ( nP == 0 || nP == 1) || dPerP > 1 || dPerP < 0 || dMaxLen < EPS_SMALL) return false ; // # di archi piccoli tra le due curve int dSmallArcs1 = 0 ; int dSmallArcs2 = 0 ; // # di segmenti piccoli tra le due curve int dSmallLines1 = 0 ; int dSmallLines2 = 0 ; // ------------- lunghezze -------------- double dLen1 = 0 ; double dLen2 = 0 ; pCrv1->GetLength( dLen1) ; pCrv2->GetLength( dLen2) ; double CL1, CL2 ; // rapporto tra lunghezza corrente e massima if ( dLen1 < EPS_SMALL && dLen2 < EPS_SMALL) { CL1 = 0 ; CL2 = 0 ; } else { CL1 = dLen1 / max( dLen1, dLen2) ; CL2 = dLen2 / max( dLen1, dLen2) ; } // ------------- rotazioni -------------- double dRot1 = 0 ; double dRot2 = 0 ; if ( ! GetCurveWeightInfo( pCrv1, dMaxLen, dRot1, dSmallArcs1, dSmallLines1) || ! GetCurveWeightInfo( pCrv2, dMaxLen, dRot2, dSmallArcs2, dSmallLines2)) return false ; double CR1, CR2 ; // rapporto tra la somma de rotazione degli angoli correnti e quella massima if ( dRot1 == 0 && dRot2 == 0) { CR1 = 0 ; CR2 = 0 ; } else { CR1 = dRot1 / max( dRot1, dRot2) ; CR2 = dRot2 / max( dRot1, dRot2) ; } // funzione peso ( bilancio tra lunghezze e rotazioni complessive pesate) double Fp1 = ( nP == 0 ? dPerP : ( 1 - dPerP)) * ( CL1 + dSmallLines1) + ( nP == 0 ? ( 1 - dPerP) : dPerP) * ( CR1 + dSmallArcs1) ; double Fp2 = ( nP == 0 ? dPerP : ( 1 - dPerP)) * ( CL2 + dSmallLines2) + ( nP == 0 ? ( 1 - dPerP) : dPerP) * ( CR2 + dSmallArcs2) ; // scelta della prima o della seconda curva nC = ( Fp1 > Fp2 ? 1 : 0) ; return true ; } //---------------------------------------------------------------------------- static bool RemoveExtraParts( ISurfFlatRegion* pSrfToCut, ICRVCOMPOPOVECTOR& vOffs, ICRVCOMPOPOVECTOR& vOffsClosedCurves, ICRVCOMPOPOVECTOR& vOffsFirstCurve, ICURVEPOVECTOR& vLinks, PocketParams PockParams) { // controllo parametri if ( pSrfToCut == nullptr || vOffs.empty()) return true ; // ciclo tutti i chunk della regione da tagliare for ( int i = 0 ; i < pSrfToCut->GetChunkCount() ; ++ i) { // regione i-esima da rimuovere ( chunk i-esimo ) PtrOwner pSrfChunkToCut( pSrfToCut->CloneChunk( i)) ; if ( IsNull( pSrfChunkToCut)) return false ; // Curva nel caso la regione si svuoti con MedialAxis PtrOwner pCrvPath( CreateCurveComposite()) ; if ( IsNull( pCrvPath)) return false ; // nel caso la regione si svuoti con un centroide -> ptGoTo è il centroide // nel caso la regione si svuoti con un medial Axis -> ptGoTo è il punto iniziale // -> ptGoTo è il punto finale Point3d ptGoTo, ptGoToI ; // flag : 0 -> non faccio nulla | 1 -> svuoto con centroide | 2 -> svuoto con un percorso int nOptFlag = 0 ; // ricavo il flag // ptGoTo è il centroide, pCrvPath è la curva da seguire per svuotare la regione ( nel caso non bastasse il centroide) if ( ! RemoveExtraPartByMedialAxis( pSrfChunkToCut, vOffsFirstCurve, PockParams, nOptFlag, ptGoTo, pCrvPath) || nOptFlag == 0) continue ; if ( nOptFlag == 2 ) // se ho una curva di Medial Axis, ricavo il punto iniziale e finale if ( ! pCrvPath->GetStartPoint( ptGoTo) || ! pCrvPath->GetEndPoint( ptGoToI)) continue ; // una volta trovata la curva di medial Axis ( se la regione non si svuota semplicemente passando per il // centroide, controllo in quale direzione è più conveniente percorrerla ... ( I = Inverso) PtrOwner pCrvH1( CreateCurveComposite()) ; PtrOwner pCrvH2( CreateCurveComposite()) ; PtrOwner pCrvH1I( CreateCurveComposite()) ; PtrOwner pCrvH2I( CreateCurveComposite()) ; if ( IsNull( pCrvH1) || IsNull( pCrvH2) || IsNull( pCrvH2) || IsNull( pCrvH2I)) return false ; int nIndexO = 0 ; int nIndexOI = 0 ; // cerco il punto pià vicino a ptGoTo sugli tra i vari Offset ( escludendo il primo ) // NB. PtGoTo è il centroide o il punto iniziale del percorso di medial Axis non invertito if ( ! CutOffsetToClosestPoint( vOffs, ptGoTo, pCrvH1, pCrvH2, nIndexO)) continue ; if ( nOptFlag == 2) // se ho un percorso di Medial Axis, lo cerco anche per PtGoToI if ( ! CutOffsetToClosestPoint( vOffs, ptGoToI, pCrvH1I, pCrvH2I, nIndexOI)) continue ; // A) SE IL PUNTO PIU' VICINO E' SU UN ESTREMO DELL'OFFSET... -> Cerco un punto sui collegamenti // ( controllo solo i punti trovati sugli offset mediante Medial Axis non invertiti) if ( IsNull( pCrvH1) || IsNull( pCrvH2) || pCrvH1->GetCurveCount() == 0 || pCrvH2->GetCurveCount() == 0) { bool bFound, bFoundI ; int nIndexL, nIndexLI ; if ( ! CutLinkToClosestPoint( vLinks, vOffs, ptGoTo, pCrvH1, pCrvH2, bFound, nIndexL)) continue ; if ( nOptFlag == 2) { // nel caso curva di Medial Axis, controllo anche nel caso invertito if ( ! CutLinkToClosestPoint( vLinks, vOffs, ptGoToI, pCrvH1I, pCrvH2I, bFoundI, nIndexLI)) continue ; } // link finale scelto... PtrOwner ptCrvNewLink( CreateCurveComposite()) ; if ( IsNull( ptCrvNewLink)) return false ; if ( nOptFlag == 1) { // se svuoto con centroide, aggiusto il link con una circonferenza ( BiArco alla peggio) if ( ! GetNewCurvetWithCentroid( pCrvH1, pCrvH2, PockParams, ptGoTo, bFound, vOffsFirstCurve, ptCrvNewLink)) continue ; } else if ( nOptFlag == 2) { // se svuoto con curva di medial Axis... bool bSucc = true ; bool bSuccI = true ; // ricavo il nuovo Offset con la curva di medial Axis aggiunta if ( ! GetNewCurvetWithPath( pCrvH1, pCrvH2, pCrvPath, vOffsFirstCurve, vOffsClosedCurves, PockParams, ptCrvNewLink)) bSucc = false ; PtrOwner pCrvPathI( pCrvPath->Clone()) ; pCrvPathI->Invert() ; PtrOwner ptCrvNewLinkI( CreateCurveComposite()) ; if ( IsNull( ptCrvNewLinkI)) return false ; // ricavo il nuovo Offset con la curva di medial Axis Invertita aggiunta if ( ! GetNewCurvetWithPath( pCrvH1I, pCrvH2I, pCrvPathI, vOffsFirstCurve, vOffsClosedCurves, PockParams, ptCrvNewLinkI)) bSuccI = false ; // scelgo il miglior percorso ottenuto ( sempre verificando che siano validi...) int nC = 0 ; if ( bSucc && bSuccI) { // se entrambi i percorsi sono validi allora li confronto if ( ChoosePath( ptCrvNewLink, ptCrvNewLinkI, 0, 0.25, 5 * 1000 * EPS_SMALL, nC) && nC == 1) { ptCrvNewLink.Set( ptCrvNewLinkI) ; nIndexL = nIndexLI ; } } else if ( ! bSucc) { // altrimenti tengo l'unico valido ptCrvNewLink.Set( ptCrvNewLinkI) ; nIndexL = nIndexLI ; } } vLinks[nIndexL].Set( ptCrvNewLink) ; // setto il nuovo Link } else { // B) SE NON SONO AD UN ESTREMO DELLA CURVA DI OFFSET // nuovo Offset da restituire PtrOwner ptCrvNewOffs( CreateCurveComposite()) ; if ( IsNull( ptCrvNewOffs)) return false ; if ( nOptFlag == 1) { // se svuoto con centroide, aggiusto il link con una circonferenza ( BiArco alla peggio) if ( ! GetNewCurvetWithCentroid( pCrvH1, pCrvH2, PockParams, ptGoTo, true, vOffsFirstCurve, ptCrvNewOffs)) continue ; } else if ( nOptFlag == 2) { // nel caso aggiungo un medial Axis non agli estremi di un Offset bool bSucc = true ; bool bSuccI = true ; // come prima controllo il percorso normal ed invertito if ( ! GetNewCurvetWithPath( pCrvH1, pCrvH2, pCrvPath, vOffsFirstCurve, vOffsClosedCurves, PockParams, ptCrvNewOffs)) bSucc = false ; PtrOwner pCrvPathI( pCrvPath->Clone()) ; pCrvPathI->Invert() ; PtrOwner ptCrvNewOffsI( CreateCurveComposite()) ; if ( IsNull( ptCrvNewOffsI)) return false ; if ( ! GetNewCurvetWithPath( pCrvH1I, pCrvH2I, pCrvPathI, vOffsFirstCurve, vOffsClosedCurves, PockParams, ptCrvNewOffsI)) bSuccI = false ; int nC = 0 ; // se entrambi i percorsi sono validi allora li confronto if ( bSucc && bSuccI) { if ( ChoosePath( ptCrvNewOffs, ptCrvNewOffsI, 0, 0.5, 5 * 1000 * EPS_SMALL, nC) && nC == 1) { ptCrvNewOffs.Set( ptCrvNewOffsI) ; nIndexO = nIndexOI ; } } else if ( ! bSucc) { // altrimenti tengo l'unico valido ptCrvNewOffs.Set( ptCrvNewOffsI) ; nIndexO = nIndexOI ; } } vOffs[nIndexO].Set( ptCrvNewOffs) ; // setto il nuovo Offset } } return true ; } //---------------------------------------------------------------------------- static bool CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, PocketParams PockParams, const ICRVCOMPOPOVECTOR& vOffsClosedCurves, const ICRVCOMPOPOVECTOR& vOffsFirstCurve) { // controllo dei parametri if ( vOffs.empty() || vOffsFirstCurve.empty()) return false ; vLinks.clear() ; vLinks.resize( int( vOffs.size())) ; // scorro tutte le curva di Offset for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) { // ricavo il punto inziale della curva corrente Point3d ptS ; if ( ! vOffs[i]->GetStartPoint( ptS)) return false ; int nNextInd = -1 ; // indice della curva successiva double dMinDist = INFINITO ; // distanza tra questa curva e la successiva Point3d ptStartNext ; // punto iniziale della curva successiva // tra le curva successive cerco la curva interna e più vicina ad essa for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) { IntersCurveCurve IntCC( *vOffs[i], *vOffs[j]) ; CRVCVECTOR ccClass ; // se interna if ( IntCC.GetCurveClassification( 1, EPS_SMALL, ccClass) && int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) { // calcolo la distanza minima tra essa int nFlag ; Point3d ptClosest ; if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) { double dCurrDist = SqDist( ptS, ptClosest) ; if ( dCurrDist < dMinDist) { dMinDist = dCurrDist ; nNextInd = j ; ptStartNext = ptClosest ; } } } } // se non ho trovato nessuna curva interna... cerco semplicemente la curva più vicina if ( nNextInd == -1) { for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) { int nFlag ; Point3d ptClosest ; if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) { double dCurrDist = SqDist( ptS, ptClosest) ; if ( dCurrDist < dMinDist) { dMinDist = dCurrDist ; nNextInd = j ; ptStartNext = ptClosest ; } } } } // se non ho trovato nessuna curva, errore if ( nNextInd == -1) return false ; // scambio la curva i+1 esima con la j-esima ( se non sono già in ordine) if ( nNextInd != i + 1) swap( vOffs[nNextInd], vOffs[i+1]) ; // cambio il suo punto iniziale nel punto più vicino tovato double dUS ; if ( ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUS, EPS_SMALL)) return false ; vOffs[i+1]->ChangeStartPoint( dUS) ; // accorcio la curva per raccordarmi in tangenza con la successiva if ( int( vOffs.size()) > 1) { // clono le curve i ed i+1 esime ( nel caso non riuscissi ad accorciarle o raccordarle ) PtrOwner pCrv_i( CloneCurveComposite( vOffs[i])) ; PtrOwner pCrv_ii( CloneCurveComposite( vOffs[i+1])) ; if ( IsNull( pCrv_i) || IsNull( pCrv_ii) || ! pCrv_i->IsValid() || ! pCrv_ii->IsValid()) return false ; // creo la curva che le collegherà PtrOwner pCrvLink( CreateCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; // NB. Le curve di Offset da tagliare non devono essere quelle di primo Offset if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsClosedCurves, vOffsFirstCurve, PockParams, pCrvLink, i == 0 ? 0 : 0.01, 0.01, 2)) { // se non sono riuscito, cerco una strada più semplice // ripristino le curve pCrvLink->Clear() ; vOffs[i].Set( Release( pCrv_i)) ; // chiuso vOffs[i+1].Set( Release( pCrv_ii)) ; // chiuso // recupero i vettori tangente iniziali ( le curve sono chiuse ) Vector3d vtS, vtE ; if ( ! vOffs[i]->GetStartDir( vtS) || ! vOffs[i+1]->GetStartDir( vtE)) return false ; // creo il bi-arco tra esse if ( CalcBoundedSmootedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink)) vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento else return false ; continue ; // passo alla curva i+1 esima successiva } // per sicurezza aggiorno i nuovi punti e i nuovi parametri double dUNewS, dUNewE ; if ( ! pCrvLink->GetStartPoint( ptS) || ! pCrvLink->GetEndPoint( ptStartNext) || ! vOffs[i]->GetParamAtPoint( ptS, dUNewS) || ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUNewE)) return false ; // imposto il punto iniziale della curva successiva ( i+1 esima) vOffs[i+1]->ChangeStartPoint( dUNewE) ; PtrOwner pCrvNewOffs( 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( Release( pCrvLink)) ; } } return true ; } //---------------------------------------------------------------------------- static bool CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs, const int nOffsCount, const int nIter, const Vector3d& vtN, PocketParams PockParams, bool& bInsert) { // controllo dei parametri if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0 || dOffs < EPS_SMALL) return false ; bInsert = true ; // di base inserisco // controllo se richiesta ottimizzazione sul numero di Offsets if ( ! PockParams.bOptOffsets) return true ; if ( nIter != 0) { // controllo se sono almeno al secondo offset... // per ogni curva controllo se il controOffset + 5 * EPS_SMALL genera una regione ... OffsetCurve OffsCrv ; // considero anche i casi in cui ho bSmallRad if ( OffsCrv.Make( pCrvOffs, - PockParams.dRad + dOffs - 5 * EPS_SMALL , ICurve::OFF_FILLET)) { // se questa curva sparisce -> non serve inserirla come offset, non svuota parti aggiuntive if ( OffsCrv.GetCurveCount() == 0) { bInsert = false ; return true ; } } else return true ; // controllo se richista ottimizzazione per Offsets mediante centroidi e medial Axis if ( ! PockParams.bOptOffsetsAdv || OffsCrv.GetCurveCount() > 1) return true ; PtrOwner pCrvOffLonger( OffsCrv.GetLongerCurve()) ; if ( IsNull( pCrvOffLonger) || ! pCrvOffLonger->IsValid()) return true ; // Immagine del tool con centro nel centroide della zona da svuotare Point3d ptC ; pCrvOffLonger->GetCentroid( ptC) ; PtrOwner pCrvTool( CreateCurveArc()) ; if( IsNull( pCrvTool)) return false ; pCrvTool->SetXY( ptC, PockParams.dRad - 5 * EPS_SMALL) ; CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCrvOffLonger, *pCrvTool) ; intCC.GetCurveClassification( 1, EPS_SMALL, ccClass) ; if ( int(ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) { // centroide bInsert = false ; return true ; } // recupero la PolyLine per altezza e lunghezza del Box2D ( rettangolo) PolyLine pl ; if ( ! pCrvOffLonger->ApproxWithLines( 100 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, pl)) return true ; Vector3d vtX ; double dLen = 0 ; double dHeight = 0 ; if ( ! pl.GetMinAreaRectangleXY( ptC, vtX, dLen, dHeight)) return true ; // frame locale, recupero DimX e DimY Frame3d frLoc ; frLoc.Set( ptC, vtN, vtX) ; if ( ! frLoc.IsValid()) return true ; BBox3d bBox ; frLoc.Invert() ; pCrvOffLonger->GetBBox( frLoc, bBox) ; double dDimX = bBox.GetDimX() ; double dDimY = bBox.GetDimY() ; // se dimensioni accettabili, inserisco if ( dDimX < 2 * PockParams.dRad - 100 * EPS_SMALL || dDimY < 2 * 2 * PockParams.dRad - 100 * EPS_SMALL) { bInsert = false ; return true ; } } return true ; } //---------------------------------------------------------------------------- static bool CalcSpiral( const ISurfFlatRegion* pSrfPock, PocketParams PockParams, int& nReg, Point3d& ptStart, ICurveComposite* pCrvOEWithFlags, ICurveComposite* pMCrv, ICurveComposite* pRCrv, bool& bOptimizedTrap) { // inizializzo i risultati pMCrv->Clear() ; pRCrv->Clear() ; // Offset corrente ( il primo è definito dal raggio utensile) double dOffs = PockParams.dRad ; // recupero il versore normale della superficie, ruotandola nel piano XY PtrOwner pSrfToWork( pSrfPock->Clone()) ; if ( IsNull( pSrfToWork) || pSrfToWork->GetChunkCount() == 0) return false; // porto il Chunk c-esimo nel sistema di riferimento locale Vector3d vtExtr = pSrfToWork->GetNormVersor() ; Point3d ptCen ; pSrfToWork->GetCentroid( ptCen) ; Frame3d frPocket ; frPocket.Set( ptCen, vtExtr) ; // CONTROLLO PER CASI OTTIMIZZATI bool bHasIsland = ( pSrfPock->GetLoopCount( 0) > 1) ; // se non ho isole e curva valida allora controllo casi ottimizzati if ( ! bHasIsland && pCrvOEWithFlags != nullptr) { // --------------------------- caso spirale --------------------------------------- PtrOwner pCrvBorder( ConvertCurveToComposite( pSrfPock->GetLoop( 0, 0))) ; if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid()) return false ; pCrvBorder->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; pCrvBorder->SetExtrusion( pSrfPock->GetNormVersor()) ; // setto l'estrusione della curva Vector3d vtN = pSrfPock->GetNormVersor() ; // vettore normale per caso circonferenza Point3d ptCen ; double dRad ; bool bIsCircle = false ; if ( ! OptimizedSpiralCirle( pCrvBorder, 50 * EPS_SMALL, dRad, ptCen, bIsCircle)) return false ; if ( bIsCircle) { double dIntRad = 0 ; if ( nReg == 0) { bool bOk = CalcCircleSpiral( ptCen, vtN, dRad - dOffs, dIntRad, PockParams, pMCrv, pRCrv) ; if ( bOk) { pMCrv->GetStartPoint( ptStart) ; nReg = 1 ; return true ; } else return false ; } else return true ; } // -------------------------- caso trapezoide --------------------------------------- PtrOwner pCrvTrap( CreateCurveComposite()) ; if ( IsNull( pCrvTrap)) return false ; Frame3d frTrap ; double dPocketSize ; int nBase, nSecondBase ; pCrvOEWithFlags->ToLoc( frPocket) ; if ( ! GetTrapezoidFromShape( pCrvOEWithFlags, pCrvTrap, frTrap, PockParams, dPocketSize, nBase, nSecondBase)) return false ; pCrvOEWithFlags->ToGlob( frPocket) ; if ( pCrvTrap->IsValid()) { pCrvTrap->SetExtrusion( vtExtr) ; if ( nReg == 0) { CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParams, pMCrv, pRCrv, bOptimizedTrap) ; if ( bOptimizedTrap) { pMCrv->ToGlob( frPocket) ; pRCrv->ToGlob( frPocket) ; nReg = 1 ; return true ; } } else return true ; } } // porto la superficie nel frame corrente pSrfToWork->ToLoc( frPocket) ; // ciclo di offset verso l'interno const int MAX_ITER = 1000 ; int nIter = 0 ; ICRVCOMPOPOVECTOR vOffs ; // vettore delle curve di offset ICRVCOMPOPOVECTOR vOffsFirstCurve ; // curve di primo offset PtrOwner pSrfAct( CloneSurfFlatRegion( pSrfToWork)) ; // regione attuale if ( IsNull( pSrfAct) || pSrfAct->GetChunkCount() == 0) return false ; double dOffsPrec = 0. ; while ( nIter < MAX_ITER) { // ricavo la regione piana da VRONI PtrOwner pSfrOffsVR( pSrfAct->CreateOffsetSurf( - dOffs, ICurve::OFF_FILLET)) ; if ( IsNull( pSfrOffsVR)) return false ; if ( ! pSfrOffsVR->IsValid()) { pSfrOffsVR.Set( pSrfAct->CreateOffsetSurf( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) ; if ( IsNull( pSfrOffsVR)) return false ; } // alla prima iterazione svuoto la regione nReg-esima passata alla funzione CalcSpiral if ( nIter == 0) { PtrOwner pSrfChunknReg( pSfrOffsVR->CloneChunk( nReg)) ; nReg = pSfrOffsVR->GetChunkCount() ; // aggiorno il nuovo valore delle regioni if ( IsNull( pSrfChunknReg)) // se supero i chunk ottenuti return true ; } // numero di Chunk attuali int nChunks = pSfrOffsVR->GetChunkCount() ; for ( int i = 0 ; i < nChunks ; ++ i) { // per ogni chunk... int nLoops = pSfrOffsVR->GetLoopCount( i) ; for ( int j = 0 ; j < nLoops ; ++ j) { // per ogni loop... PtrOwner pCrvCompoBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ; if ( IsNull( pCrvCompoBorder) || ! pCrvCompoBorder->IsValid()) return false ; if ( j > 0) // inverto l'orientamento delle curve interne ( offset delle isole trovate) pCrvCompoBorder->Invert() ; // controllo quali regioni di Offset possono essere sostituite bool bInsert = true ; if ( ! CheckIfOffsetIsNecessary( pCrvCompoBorder, dOffs - dOffsPrec, int( vOffs.size()), nIter, vtExtr, PockParams, bInsert)) return false ; if ( bInsert) vOffs.emplace_back( Release( pCrvCompoBorder)) ; if ( nIter == 0) { // salvo il bordo per i link ( non invertiti, devo sapere IN/OUT) PtrOwner pCrvCompoExtBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ; vOffsFirstCurve.emplace_back( Release( pCrvCompoExtBorder)) ; } } } // controllo se serve un raggio più piccolo di svuotatura bool bSmallRad = ( nIter == 0 ? dOffs < PockParams.dRad + EPS_ZERO : dOffs - dOffsPrec < PockParams.dRad + EPS_ZERO) ; // se ho trovato dei chunk, allora aggiorno l'Offset per la passata successiva if ( nChunks > 0) { // memorizzo il valore di Offset per l'iterazione successiva dOffsPrec = dOffs ; dOffs += PockParams.dSideStep ; } // se devo usare un Offset più piccolo... else if ( ! bSmallRad) dOffs = dOffsPrec + ( nIter == 0 ? PockParams.dRad + PockParams.dRadialOffset : PockParams.dRad) ; // superfluo, effettivamente non ho l'offset radiale // altrimenti ho finito la regione da svuotare... else break ; ++ nIter ; // aggiorno iterazione } // se non ho trovato curve di Offset allora esco if ( vOffs.empty()) return true ; // cambio il punto iniziale della prima Curva di Offset Point3d ptRef = PockParams.ptEnd ; ptRef.ToLoc( frPocket) ; Point3d ptNewStart ; Vector3d vtMidOut ; bool bMidOut ; pCrvOEWithFlags->ToLoc( frPocket) ; if ( SetPtStartForPath( vOffs[0], PockParams, pSrfToWork, ptRef, frPocket, ptStart, vtMidOut, bMidOut, int( vOffs.size()), pCrvOEWithFlags)) vOffs[0]->GetStartPoint( ptNewStart) ; else return false ; pCrvOEWithFlags->ToGlob( frPocket) ; // se richiesta inversione for ( int i = 0 ; i < int( vOffs.size()) && PockParams.bInvert ; ++ i) vOffs[i]->Invert() ; // smusso le curve di offset ( ad eccezione della prima) ICRVCOMPOPOVECTOR vOffsClosedCurves( vOffs.size()) ; // vettore con tutte le curve di Offset Chiuse double dSmoothPar = PockParams.dRad / 8 ; for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) { if ( i != 0) ModifyCurveToSmoothed( vOffs[i], PockParams, dSmoothPar, dSmoothPar, false) ; vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; vOffsClosedCurves[i].Set( vOffs[i]->Clone()) ; } // setto il punto iniziale della svuotatura double dNewUS ; vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ; vOffs[0]->ChangeStartPoint( dNewUS) ; vOffs[0]->SetTempParam( 0., 0) ; // prima iterazione // riordino le curve cambiando e creo i collegamenti ICURVEPOVECTOR vLinks( vOffs.size()) ; if ( ! CreateSpiralPocketingPath( vOffs, vLinks, PockParams, vOffsClosedCurves, vOffsFirstCurve)) return false ; // controllo eventuali parti non svuotate... pCrvOEWithFlags->ToLoc( frPocket) ; PtrOwner pSrfToCut( CreateSurfFlatRegion()) ; if ( GetUnclearedRegion( vOffsFirstCurve, vOffs, vLinks, pCrvOEWithFlags, PockParams, pSrfToCut)) { // Modifico i percorsi if ( ! RemoveExtraParts( pSrfToCut, vOffs, vOffsClosedCurves, vOffsFirstCurve, vLinks, PockParams)) return false ; } // creo il percorso di lavoro a partire dalla raccolta degli offset e dei collegamenti for ( int i = 0 ; i < int( vOffs.size()) ; ++i) { // se collegamento da aggiungere if ( ! IsNull( vLinks[i])) { int nCrvsCount0 = pMCrv->GetCurveCount() ; // accodo nel percorso di lavorazione if ( ! pMCrv->AddCurve( vLinks[i]->Clone())) return false ; } pMCrv->AddCurve( vOffs[i]->Clone()) ; } // verifico il percorso di lavoro if ( pMCrv->GetCurveCount() == 0) return false ; // setto estrusione pMCrv->SetExtrusion( vtExtr) ; // riporto tutto nel sistema di riferimento originale pMCrv->ToGlob( frPocket) ; ptStart.ToGlob( frPocket) ; vtMidOut.ToGlob( frPocket) ; pCrvOEWithFlags->ToGlob( frPocket) ; return true ; } //---------------------------------------------------------------------------- static bool CheckSimpleOverlap( const ICurve* pCrv, const ICurveComposite* pCrvOri, int& nStat, double dToll) { // controllo dei parametri if (( pCrv == nullptr || pCrvOri == nullptr || dToll < EPS_SMALL)) return false ; nStat = 1 ; // 0 -> no overlap | 1 -> overlap // controllo se una sottocurva della composita è abbastanza vicina a tutti i punti trovati const double nMAX = 10.0 ; for ( int i = 0 ; i <= nMAX ; ++i) { double dPar = i / nMAX ; Point3d ptC ; pCrv->GetPointD1D2( dPar, ICurve::FROM_PLUS, ptC) ; if ( ! pCrvOri->IsPointOn( ptC, dToll)) { nStat = 0 ; return true ; } } return true ; } //---------------------------------------------------------- static bool CalcZigZagLink( ICurveComposite* pCrv1, ICurveComposite* pCrv2, 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, 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 + ( 1)) % 2) ; // tratto valido struct Section { bool bActive ; Point3d ptS ; Point3d ptE ; double dOs ; double dOe ; int nOffIndS ; int nOffIndE ; } ; struct ParIsland { double dU ; int nInd ; } ; // raccolta di tratti typedef vector> VECVECSECT ; VECVECSECT vvSec ; vvSec.resize( nYStep + 1) ; // calcolo le linee di svuotatura int nCount = 0 ; for ( int i = 0 ; i <= nYStep ; ++ i) { // determino senso bool bPlus = (( i % 2) == nRef) ; // definisco la linea PtrOwner pLine( CreateCurveLine()) ; const double EXP_LEN = 1.0 ; Point3d ptStart ; if ( ! bFromInfill) ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + 10 * EPS_SMALL + i * dYStep, ptMin.z + dDimZ) ; else { double dShift = ( i == 0 ? 10 * EPS_SMALL : ( i == nYStep ? - 10 * EPS_SMALL : 0.)) ; ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + dShift + i * dYStep, ptMin.z + dDimZ) ; } if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN)) return false ; if ( ! bPlus) pLine->Invert() ; // calcolo la classificazione della linea rispetto alla superficie CRVCVECTOR ccClass ; pSrfZigZag->GetCurveClassification( *pLine, EPS_SMALL, ccClass) ; for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { if ( ccClass[j].nClass == CRVC_IN) { // memorizzo il segmento Section currSec; currSec.bActive = true; PtrOwner pSeg( GetCurveLine( pLine->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ; Point3d ptS, ptE ; pSeg->GetStartPoint( ptS) ; pSeg->GetEndPoint( ptE) ; currSec.ptS = ptS ; currSec.ptE = ptE ; for ( int k = 0 ; k < int( vFirstOff.size()) ; ++ k) { if ( vFirstOff[k]->IsPointOn( ptS)) { currSec.nOffIndS = k ; double dUOS; vFirstOff[k]->GetParamAtPoint( ptS, dUOS) ; currSec.dOs = dUOS ; } if ( vFirstOff[k]->IsPointOn( ptE)){ currSec.nOffIndE = k ; double dUOE; vFirstOff[k]->GetParamAtPoint( ptE, dUOE) ; currSec.dOe = dUOE ; } } vvSec[i].emplace_back( currSec) ; ++nCount ; // numero di segmenti inseriti } } } int nStatus = 0 ; // 0 -> inizio oppure ho inserito una parte di isola | 2 -> sto inserendo un segmento Point3d ptS_ref ; PtrOwner pLastSeg( CreateCurveComposite()) ; // ultimo segmento nel percorso PtrOwner pCrvLink( CreateCurveComposite()) ; // ultimo link aggiunto // vettore dei link aggiunti ( per Feed ) ICRVCOMPOPOVECTOR vAddedLinks ; bool bFirstLine = true ; // flag per prima linea di ogni percorso // creo i percorsi di svuotatura vpCrvs.reserve( nCount) ; int nI = -1, nJ = -1 ; while ( true) { // se sezione non valida if ( nI < 0 || nJ < 0) { // ricerco la prima valida ( il primo segmento con bActive messo a true) for ( int k = 0 ; k < int( vvSec.size()) && nI < 0 ; ++ k) { for ( int l = 0 ; l < int( vvSec[k].size()) && nJ < 0 ; ++ l) { if ( vvSec[k][l].bActive) { nI = k ; nJ = l ; } } } // se trovata, creo nuova curva composita if ( nI >= 0 && nJ >= 0) { // creo la curva vpCrvs.emplace_back( CreateCurveComposite()) ; nStatus = 0 ; // aggiungo punto iniziale vpCrvs.back()->AddPoint( vvSec[nI][nJ].ptS) ; bFirstLine = true ; } // altrimenti, esco else break ; } // determino senso bool bPlus = (( nI % 2) == nRef) ; // aggiungo la sezione alla curva Section& Sec = vvSec[nI][nJ] ; Sec.bActive = false ; // creo i vettori con le linee Under e Above nella struttura della Section ICURVEPOVECTOR vLineAbove ; if ( nI < nYStep) { for ( int a = 0 ; a < ( int)vvSec[nI + 1].size() ; ++ a) { if ( vvSec[nI + 1][a].bActive) continue ; PtrOwner pLA( CreateCurveLine()) ; pLA->Set( vvSec[nI + 1][a].ptS, vvSec[nI + 1][a].ptE) ; vLineAbove.emplace_back( Release( pLA)) ; } } ICURVEPOVECTOR vLineUnder ; if ( nI > 0) { for ( int u = 0 ; u < ( int)vvSec[nI - 1].size() ; ++ u) { if ( vvSec[ nI - 1][u].bActive) continue ; PtrOwner pLU( CreateCurveLine()) ; pLU->Set( vvSec[nI - 1][u].ptS, vvSec[nI - 1][u].ptE) ; vLineUnder.emplace_back( Release( pLU)) ; } } // creo la linea come curva composita, aggiungerò dei Joint per ogni tratto con Feed differente PtrOwner pLineCompo( CreateCurveComposite()) ; if ( IsNull( pLineCompo)) return false ; Point3d ptE_l ; if ( bFirstLine) ptE_l = vvSec[nI][nJ].ptS ; else vpCrvs.back()->GetEndPoint( ptE_l) ; pLineCompo->AddPoint( ptE_l) ; pLineCompo->AddLine( vvSec[nI][nJ].ptE) ; bFirstLine = false ; vpCrvs.back()->AddCurve( pLineCompo->Clone()) ; // aggiungo la curva al percorso if ( nStatus == 0) { // primo segmento per il raccordo smussato pLastSeg.Set( pLineCompo) ; pLastSeg->GetStartPoint( ptS_ref) ; } if ( nStatus == 2) { // ho precedentemente aggiunto il bordo di un'isola, quindi mi salvo il secondo segmento PtrOwner pSeg1( CloneCurveComposite( pLastSeg)) ; PtrOwner pSeg2( CloneCurveComposite( pLineCompo)) ; if ( IsNull( pSeg1) || IsNull( pSeg2)) return false ; // aggiorno il link if ( ! CalcZigZagLink( pSeg1, pSeg2, 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) ; vpCrvs.back()->AddCurve( pCrvLink->Clone()) ; // aggiungo il Link vpCrvs.back()->AddCurve( pSeg2->Clone()) ; // aggiungo pSeg2 // aggiorno il vettore dei Link ( nel caso sia un link tra due segmenti sullo stesso livello ) Point3d ptS1 ; pSeg1->GetStartPoint( ptS1) ; Point3d ptS2 ; pSeg2->GetStartPoint( ptS2) ; if ( abs( ptS1.y - ptS2.y) < 50 * EPS_SMALL) vAddedLinks.emplace_back( pCrvLink->Clone()) ; // pSeg2 ora diventa pSeg1 e il procedimento si itera per tutto il percorso pLastSeg.Set( pSeg2) ; } // cerco nella stessa fila o in quella successiva sezione successiva raccordabile tramite il contorno double dUstart = Sec.dOe ; int nIndexIslandE = Sec.nOffIndE ; // isola di arrivo double dUmin, dUmax ; vFirstOff[nIndexIslandE]->GetDomain( dUmin, dUmax) ; double dUspan = dUmax - dUmin ; double dUref = ( bPlus ? INFINITO : -INFINITO) ; int nNextI = -1 ; int nNextJ = -1 ; int li = nJ + 1 ; for ( int k = nI ; k <= nI + 1 && k < int( vvSec.size()) ; ++ k) { for ( int l = li ; l < int( vvSec[k].size()) ; ++ l) { if ( ! vvSec[k][l].bActive) // se sezione non attiva... non la considero continue ; if ( vvSec[k][l].nOffIndS != nIndexIslandE) // se isola diversa... non la considero continue ; double dU = vvSec[k][l].dOs ; if ( bPlus) { if ( dU < dUstart) dU += dUspan ; if ( dU < dUref) { dUref = dU ; nNextI = k ; nNextJ = l ; } } else { if ( dU > dUstart) dU -= dUspan ; if ( dU > dUref) { dUref = dU ; nNextI = k ; nNextJ = l ; } } } li = 0 ; } // se trovato, controllo il contorno dell'isola if ( nNextI != -1) { PtrOwner pCopy ; if ( bPlus) { if ( dUref > dUmax) dUref -= dUspan ; pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ; if ( ! IsNull( pCopy)) { double dCLen ; pCopy->GetLength( dCLen) ; if ( dCLen > 0.5 * dLen) { pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ; if ( ! IsNull( pCopy)) pCopy->Invert() ; } } } else { if ( dUref < dUmin) dUref += dUspan ; pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ; if ( ! IsNull( pCopy)) { pCopy->Invert() ; double dCLen; pCopy->GetLength( dCLen) ; if ( dCLen > 0.5 * dLen) pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ; } } // controllo che nel percorso scelto non ritorni indietro superando la precendete passata BBox3d b3Copy ; if ( ! IsNull( pCopy)) pCopy->GetLocalBBox( b3Copy) ; if ( ! b3Copy.IsEmpty() && ( b3Copy.GetMax().y - b3Copy.GetMin().y) < dYStep + 10 * EPS_SMALL) { // tengo la curva (non ritorno indietro più dello step) Point3d ptS, ptE ; pCrvLink->Clear() ; pCrvLink->AddCurve( pCopy->Clone()) ; vpCrvs.back()->AddCurve( Release( pCopy)) ; nStatus = 2 ; // aggiunta parte di isola // aggiungo il Link nI = nNextI ; nJ = nNextJ ; } else { nI = -1 ; nJ = -1 ; } } else { nI = -1 ; nJ = -1 ; } } return true ; } //---------------------------------------------------------------------------- static bool AddSpiralIn( const ISurfFlatRegion* pSrfPock, PocketParams PockParams, const ICRVCOMPOPOVECTOR& vCrvOrig, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // ciclo sui chunk della superficie da svuotare for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; ++ c) { // copio il chunk c-esimo PtrOwner pSrfChunk( pSrfPock->CloneChunk( c)) ; if ( IsNull( pSrfChunk)) return false ; const int MAX_REGS = 50 ; // massimo numero di regioni create dal primo offset int nReg = 0 ; // chunk nuovo corrente da svuotare // ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-esimo while ( nReg < MAX_REGS) { // calcolo la spirale dall'esterno all'interno e la curva che unisce inizio e fine PtrOwner pMCrv( CreateCurveComposite()) ; // percorso di svuotatura PtrOwner pRCrv( CreateCurveComposite()) ; // percorso di ritorno if ( IsNull( pMCrv) || IsNull( pRCrv)) return false ; int nRegTot = nReg ; Point3d ptStart ; bool bOptimizedTrap = false ; // cerco la curva originale del chunk cc-esimo ( per casi ottimizzati) int nInd = 0 ; // indice del vettore delle curve esterne relativo al chunk cc-esimo if ( ! GetOptCrvIndex( vCrvOrig, pSrfChunk, nInd)) return false ; // calcolo il percorso di svuotatura if ( ! CalcSpiral( pSrfChunk, PockParams, nRegTot, ptStart, vCrvOrig[nInd], pMCrv, pRCrv, bOptimizedTrap)) return false ; // se terminate le regioni, esco if ( pMCrv->GetCurveCount() == 0) break ; // passo al chunk originale successivo // inserisco le curve nel vettore vCrvCompoRes.emplace_back( Release( pMCrv)) ; ++ nReg ; // incremento il numero di regione progressiva } } return true ; } //---------------------------------------------------------------------------- static bool AddSpiralOut( const ISurfFlatRegion* pSrfPock, PocketParams PockParams, const ICRVCOMPOPOVECTOR& vCrvOrig, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // ciclo sui chunk della superficie da svuotare for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; ++ c) { // copio il chunk c-esimo PtrOwner pSrfChunk( GetSurfFlatRegion( pSrfPock->CloneChunk( c))) ; if ( IsNull( pSrfChunk)) return false ; const int MAX_REGS = 50 ; int nReg = 0 ; while ( nReg < MAX_REGS) { // calcolo la spirale dall'interno all'esterno PtrOwner pMCrv( CreateCurveComposite()) ; PtrOwner pRCrv( CreateCurveComposite()) ; if ( IsNull( pMCrv) || IsNull( pRCrv)) return false ; int nRegTot = nReg ; Point3d ptStart ; bool bOptimizedTrap = false ; // cerco la curva originale del chunk cc-esimo ( per casi ottimizzati) int nInd = 0 ; // indice del vettore delle curve esterne relativo al chunk cc-esimo if ( ! GetOptCrvIndex( vCrvOrig, pSrfChunk, nInd)) return false ; // calcolo il percorso di svuotatura if ( ! CalcSpiral( pSrfChunk, PockParams, nRegTot, ptStart, vCrvOrig[nInd], pMCrv, pRCrv, bOptimizedTrap)) return false ; // se terminate le regioni, esco if ( pMCrv->GetCurveCount() == 0) break ; // passo al chunk originale successivo // inverto i percorsi, perchè sono calcolati dall'esterno all'interno (solo nel caso non ottimizzato) pMCrv->Invert() ; pRCrv->Invert() ; // inserisco le curve nel vettore vCrvCompoRes.emplace_back( Release( pMCrv)) ; ++ nReg ; } } return true ; } //---------------------------------------------------------------------------- static bool AddZigZag( const ISurfFlatRegion* pSrfPock, PocketParams PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // Offset della regione per curva ZigZag double dOffs = PockParams.dRad ; // ciclo su tutti i chunks della superficie originale for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; ++ c) { // copio il chunk c-esimo PtrOwner pSrfChunk(( pSrfPock->CloneChunk( c))) ; if ( IsNull( pSrfChunk)) return false ; // determino il riferimento in base alla svuotatura ( per poter orientare il frame per m_dSideAngle) Frame3d frPocket ; Point3d ptCen ; pSrfChunk->GetCentroid( ptCen) ; frPocket.Set( ptCen, pSrfChunk->GetNormVersor()) ; frPocket.Rotate( ptCen, pSrfChunk->GetNormVersor(), PockParams.dAngle) ; // porto la superficie nel nuovo sistema di riferimento pSrfChunk->ToLoc( frPocket) ; // creo la regione per il percorso a ZigZag PtrOwner pSrfZigZag( CloneSurfFlatRegion( pSrfChunk)) ; if ( ! pSrfZigZag->Offset( - dOffs, ICurve::OFF_FILLET)) return false ; // vettore con i percorsi a ZigZag ICRVCOMPOPOVECTOR vpCrvs ; // ciclo sui Chunk ottenuti... for ( int cfz = 0 ; cfz < pSrfZigZag->GetChunkCount() ; ++ cfz) { // considero un chunk per volta... PtrOwner pSrfZigZagChunk( pSrfZigZag->CloneChunk( cfz)) ; if ( IsNull( pSrfZigZagChunk)) return false ; // calcolo i percorsi di ZigZag if ( ! CalcZigZag( pSrfZigZagChunk, PockParams, vpCrvs, false)) return false ; // aggiorno il vettore delle curve risultati for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) { vpCrvs[u]->ToGlob( frPocket) ; // riportando prima in Globale vCrvCompoRes.emplace_back( Release( vpCrvs[u])) ; } // libero il vettore di curve ZigZag per il chunk successivo vpCrvs.clear() ; } } return true ; } //---------------------------------------------------------------------------- static bool OptimizeChunkOneWay( ISurfFlatRegion* pSrfOrig, ISURFFRPOVECTOR& vSrfIdeal) { // controllo parametri if ( pSrfOrig == nullptr || pSrfOrig->GetChunkCount() == 0) return false ; vSrfIdeal.clear() ; // clono la superficie passata alla funzione PtrOwner pSrfToPock( CloneSurfFlatRegion( pSrfOrig)) ; if ( IsNull( pSrfToPock) || pSrfToPock->GetChunkCount() == 0) return false ; // classifico i chunks in modo da creare delle regioni ideali; una regione ideale è formata da un chunk principale // con tutti gli altri contenuti in esso ( in questo modo ottimizzo i percorsi per i bordi) INTVECTOR vChunksAvailable( pSrfToPock->GetChunkCount(), 1) ; for ( int c = 0 ; c < pSrfToPock->GetChunkCount() ; ++ c) { PtrOwner pSrfIdeal( CreateSurfFlatRegion()) ; if ( IsNull( pSrfIdeal)) return false ; if ( vChunksAvailable[c] == 1) { // se Chunk valido... // prendo la curva esterna PtrOwner pCrvExt( ConvertCurveToComposite( pSrfToPock->GetLoop( c, 0))) ; if ( IsNull( pCrvExt)) return false ; // inserisco il chunk c-esimo ( curva per curva, così non perdo le temp prop) pSrfIdeal->AddExtLoop( pCrvExt->Clone()) ; // ... inizio a creare la regione ideale da esso for ( int l = 1 ; l < pSrfToPock->GetLoopCount( c) ; ++ l) pSrfIdeal->AddIntLoop( ConvertCurveToComposite( pSrfToPock->GetLoop( c, l))) ; // il chunk c-esimo non è più disponibile... vChunksAvailable[c] = -1 ; // scorro tutti gli altri chunk i-esimi ancora disponibili for ( int i = 0 ; i < int( vChunksAvailable.size()) ; ++ i) { if ( vChunksAvailable[i] == 1) { // prendo la curva esterna del chunk i-esimo disponibile PtrOwner pCrvExtC( ConvertCurveToComposite( pSrfToPock->GetLoop( i, 0))) ; if ( IsNull( pCrvExtC)) return false ; // classifico i bordi esterni ( se il bordo del chunk c-esimo contiene o è contenuto nel loop // esterno del chunk i-esimo -> sono chunks appartenenti alla stessa superficie ideale) IntersCurveCurve intCC( *pCrvExtC, *pCrvExt) ; CRVCVECTOR ccClass, ccClass1 ; intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; intCC.GetCurveClassification( 1, EPS_SMALL, ccClass1) ; if (( ccClass.size() == 1 && ccClass[0].nClass == CRVC_IN) || ( ccClass1.size() == 1 && ccClass1[0].nClass == CRVC_IN)) { // inserisco il chunk i-esimo ( curva per curva, così non perdo le temp prop) pSrfIdeal->AddExtLoop( ConvertCurveToComposite( pSrfToPock->GetLoop( i, 0))) ; for ( int l = 1 ; l < pSrfToPock->GetLoopCount( i) ; ++ l) pSrfIdeal->AddIntLoop( ConvertCurveToComposite( pSrfToPock->GetLoop( i, l))) ; vChunksAvailable[i] = -1 ; // chunk j-esimo non più disponibile // NB: Se il chunk i-esimo contiene la pSrfIdeal ( quindi chunk i-esimo unito ad eventuali altri chunk // j-esimi precedenti -> prendo come bordo esterno di riferimento il suo e ricontrollo dall'inizio i // chunk disponibili if (( int)ccClass1.size() == 1 && ccClass1[0].nClass == CRVC_IN) { pCrvExt.Set( pCrvExtC->Clone()) ; i = 0 ; } } } } } if ( pSrfIdeal->GetChunkCount() > 0) vSrfIdeal.emplace_back( Release( pSrfIdeal)) ; } return ( int( vSrfIdeal.size() != 0)) ; } //---------------------------------------------------------------------------- static bool AddOneWay( const ISurfFlatRegion* pSrfPock, PocketParams PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes) { double dOffs = PockParams.dRad ; double dExtra = 0. ; // copio la regione da svuotare PtrOwner pSrfToPock( pSrfPock->Clone()) ; if ( IsNull( pSrfToPock)) return false ; // creo le regioni ideali -------------------------------------------- // creo un frame di riferimento della svuotatura Frame3d frLoc ; Point3d ptC ; pSrfToPock->GetCentroid( ptC) ; Vector3d vtN = pSrfToPock->GetNormVersor() ; frLoc.Set( ptC, vtN) ; pSrfToPock->ToLoc( frLoc) ; ISURFFRPOVECTOR vSrfFlat ; // vettore delle regioni ideali if ( ! OptimizeChunkOneWay( pSrfToPock, vSrfFlat)) return false ; // riporto le superfici ideali nel sistema di riferimento globale ( Ogni chunk verrà poi messo nel suo frame) for ( int i = 0 ; i < ( int)vSrfFlat.size() ; ++ i) vSrfFlat[i]->ToGlob( frLoc) ; // ------------------------------------------------------------------ // scorro le regioni ideali for ( int nIs = 0 ; nIs < int( vSrfFlat.size()) ; ++ nIs) { // copio la superficie ideale PtrOwner pSrfIdeal( CloneSurfFlatRegion( vSrfFlat[nIs])) ; if ( IsNull( pSrfIdeal)) return false ; // effettuo Offset interno if ( ! pSrfIdeal->Offset( - dOffs, ICurve::OFF_FILLET)) return false ; // creo un frame coerente con i parametri Frame3d frPocket ; Point3d ptCen ; pSrfIdeal->GetCentroid( ptCen) ; frPocket.Set( ptCen, pSrfIdeal->GetNormVersor()) ; frPocket.Rotate ( ptCen, pSrfIdeal->GetNormVersor(), PockParams.dAngle) ; pSrfIdeal->ToLoc( frPocket) ; BBox3d b3Pocket ; pSrfIdeal->GetLocalBBox( b3Pocket) ; Point3d ptMin ; double dDimX, dDimY, dDimZ ; b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ; // passi in Y int nYStep = static_cast( ceil(( dDimY + 2 * dExtra) / PockParams.dSideStep)) ; double dYStep = ( nYStep > 0 ? ( dDimY + 2 * dExtra) / nYStep : 0) ; --nYStep ; // vettore dei segmenti al di sotto della linea corrente ICURVEPOVECTOR vLineUnder ; ICURVEPOVECTOR vLineAbove ; ICRVCOMPOPOVECTOR vCrvLink ; // calcolo le linee di svuotatura const double EXP_LEN = 1.0 ; for ( int i = 1 ; i <= nYStep ; ++ i) { // definisco la linea PtrOwner pLine( CreateCurveLine()) ; Point3d ptStart( ptMin.x - EXP_LEN, ptMin.y + ( - dExtra + i * dYStep), ptMin.z + dDimZ) ; if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN)) return false ; // vettore di tutti i segmenti lineari che si formano nello step nYStep ICURVEPOVECTOR vAddedLines ; // riempio il vettore di segmenti CRVCVECTOR ccClassSeg ; pSrfIdeal->GetCurveClassification( *pLine, EPS_SMALL, ccClassSeg) ; for ( int w = 0 ; w < int( ccClassSeg.size()) ; ++w) { if ( ccClassSeg[w].nClass == CRVC_IN) { PtrOwner pCrvSeg( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ; double duF, dLen ; // accorcio leggermente il segmento per non toccare la prima curva di Offset if ( ! pCrvSeg->GetLength( dLen) || dLen < 2 * dExtra || ! pCrvSeg->GetParamAtLength( dLen - dExtra, duF) || ! pCrvSeg->TrimStartAtLen( dExtra) || ! pCrvSeg->TrimEndAtParam( duF)) pCrvSeg.Set( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ; PtrOwner pCrvSegCompo( CreateCurveComposite()) ; if ( IsNull( pCrvSegCompo)) return false ; pCrvSegCompo->AddCurve( Release( pCrvSeg)) ; pCrvSegCompo->ToGlob( frPocket) ; vCrvCompoRes.emplace_back( Release( pCrvSegCompo)) ; } } } } return true ; } //---------------------------------------------------------------------------- bool CalcPocketing( const ISurfFlatRegion *pSfr, double dRad, double dStep, double dAngle, int nType, bool bSmooth, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid() || dStep < EPS_SMALL || ( nType != POCKET_ZIGZAG && nType != POCKET_ONEWAY && nType != POCKET_SPIRALIN && nType != POCKET_SPIRALOUT)) return false ; // pulizia vettore delle curve elementari vCrvCompoRes.clear() ; // assegno dati di modulo PocketParams myParams ; myParams.nType = nType ; myParams.dRad = dRad ; myParams.bSmooth = bSmooth ; myParams.dAngle = dAngle ; myParams.dSideStep = dStep ; // ------------ gestione dei lati aperti ------------------- // 1) salvo le curve originali per casi ottimizzati ICRVCOMPOPOVECTOR vCrvOEWithFlags ; if ( ! GetCurvesForOptimizedPocketing( pSfr, vCrvOEWithFlags)) return false ; // 2) modifico la supericie in base alle proprietà di lato aperto/chiuso Point3d ptCenter ; pSfr->GetCentroid( ptCenter) ; Vector3d vtN = pSfr->GetNormVersor() ; Frame3d frLoc ; if ( ! frLoc.Set( ptCenter, vtN)) return false ; PtrOwner pSfrAdj( CloneSurfFlatRegion( pSfr)) ; if ( IsNull( pSfrAdj) || ! pSfrAdj->ToLoc( frLoc) || ! ModifySurfByOpenEdges( pSfrAdj, myParams) || ! pSfrAdj->ToGlob( frLoc)) return false ; // calcolo delle curve elementari della superficie switch ( nType) { case POCKET_ZIGZAG : if ( ! AddZigZag( pSfr, myParams, vCrvCompoRes)) return false ; break ; case POCKET_ONEWAY : if ( ! AddOneWay( pSfr, myParams, vCrvCompoRes)) return false ; break ; case POCKET_SPIRALIN : if ( ! AddSpiralIn( pSfrAdj, myParams, vCrvOEWithFlags, vCrvCompoRes)) return false ; break ; case POCKET_SPIRALOUT : if ( ! AddSpiralOut( pSfrAdj, myParams, vCrvOEWithFlags, vCrvCompoRes)) return true ; break ; } return true ; } //---------------------------------------------------------------------------- static bool AdjustZigZagPathTangentLinks( ICurveComposite* pCrvCompo) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // miglioro la curva pCrvCompo->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; // scorro tutte le curve int nCurrTmpProp = -1, nCurrTmpProp1 = -1 ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) { if ( pCrvCompo->GetCurveTempProp( u, nCurrTmpProp, 1) && nCurrTmpProp == -1) { // ricavo il link PtrOwner pCrvLink( CreateCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; pCrvLink->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ; for ( int uu = u + 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) { if ( pCrvCompo->GetCurveTempProp( uu, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) { if ( ! pCrvLink->AddCurve( pCrvCompo->GetCurve( uu)->Clone())) return false ; } else break ; } if ( ! pCrvLink->IsValid()) return false ; // se ho più di una curva nel link if ( pCrvLink->GetCurveCount() > 1) { // ricavo la curva precendente al Link ( se presente e se prima curva del Link è lineare) if ( u != 0 && pCrvLink->GetFirstCurve()->GetType() == CRV_LINE) { const ICurve* pCrvSegPrec = pCrvCompo->GetCurve( u - 1) ; if ( pCrvSegPrec == nullptr || pCrvSegPrec->GetType() != CRV_LINE) return false ; // ricavo la sua direzione finale Vector3d vtZigZagEndDir ; pCrvSegPrec->GetEndDir( vtZigZagEndDir) ; // ricavo la direzione iniziale del link Vector3d vtLinkStartDir ; pCrvLink->GetStartDir( vtLinkStartDir) ; // se i vettori sono paralleli, considero la prima curva del link come parte del percorso a ZigZag if ( AreSameVectorApprox( vtZigZagEndDir, vtLinkStartDir)) pCrvCompo->SetCurveTempProp( u, 0, 1) ; } // ricavo la curva successiva al Link ( se presente e se ultima curva del Link è lineare) if ( u + pCrvLink->GetCurveCount() < pCrvCompo->GetCurveCount() && pCrvLink->GetLastCurve()->GetType() == CRV_LINE) { const ICurve* pCrvSegSucc = pCrvCompo->GetCurve( u + pCrvLink->GetCurveCount()) ; if ( pCrvSegSucc == nullptr || pCrvSegSucc->GetType() != CRV_LINE) return false ; // ricavo la sua direzione inziale Vector3d vtZigZagStartDir ; pCrvSegSucc->GetStartDir( vtZigZagStartDir) ; // ricavo la direzione finale del link Vector3d vtLinkStartDir ; pCrvLink->GetEndDir( vtLinkStartDir) ; // se i vettori sono paralleli, considero l'ultima curva del link come parte del percorso a ZigZag if ( AreSameVectorApprox( vtZigZagStartDir, vtLinkStartDir)) pCrvCompo->SetCurveTempProp( u + pCrvLink->GetCurveCount() - 1, 0, 1) ; } } // aggiorno u += pCrvLink->GetCurveCount() ; // così la successiva è corretta nel ciclo } } return true ; } //---------------------------------------------------------------------------- static bool AdjustLinkSameY( ICurveComposite* pCrvLink, bool& bSplit) { // controllo dei parametri if ( pCrvLink == nullptr || ! pCrvLink->IsValid()) return false ; bSplit = false ; // ricavo gli estremi del Link Point3d ptS ; pCrvLink->GetStartPoint( ptS) ; Point3d ptE ; pCrvLink->GetEndPoint( ptE) ; // ricavo il versore uscente da ptS e diretto verso ptE Vector3d vtDir = ptE - ptS ; if ( vtDir.IsValid() && ! vtDir.IsSmall()) vtDir.Normalize() ; else return false ; // creo un frame orientato come questo versore Frame3d frLink ; frLink.Set( ptS, Z_AX, vtDir) ; if ( ! frLink.IsValid()) return false ; // ricavo il box3d del Link in questo frame BBox3d BBoxLink ; pCrvLink->ToLoc( frLink) ; if ( ! pCrvLink->GetLocalBBox( BBoxLink) || BBoxLink.IsEmpty()) return false ; pCrvLink->ToGlob( frLink) ; // controllo la dimensione del Box in Y if ( abs( BBoxLink.GetDimY()) > 20 * EPS_SMALL) bSplit = true ; return true ; } //---------------------------------------------------------------------------- static bool AdjustLinkDifferentY( const ICurve* pCrvSegPrec, const ICurve* pCrvSegSucc, const ICurveComposite* pCrvLink, 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( CloneSurfFlatRegion( pSfrTrap)) ; 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, 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( CloneCurveComposite( pCrvCompo)) ; else { // per ogni intervallo... for ( int i = 0 ; i < int( vIntervals.size()) ; ++ i) vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( vIntervals[i].dParS, vIntervals[i].dParE))) ; // ultimo intervallo ( se l'ultimo intervallo è un link non valido, non inserisco) if ( abs( vIntervals.back().dParE - pCrvCompo->GetCurveCount()) > 20 * EPS_SMALL) vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nCrvStartBreak, pCrvCompo->GetCurveCount()))) ; } return true ; } //---------------------------------------------------------------------------- bool CalcZigZagInfill( const ISurfFlatRegion* pSfr, double dStep, bool bSmooth, bool bRemoveOverlapLink, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid() || dStep < EPS_SMALL) return false ; vCrvCompoRes.clear() ; PocketParams myParams ; myParams.bSmooth = false ; myParams.dRad = 0.5 * dStep ; // vettore con i percorsi a ZigZag ICRVCOMPOPOVECTOR vpCrvs ; // ciclo su tutti i chunk della regione for ( int c = 0 ; c < pSfr->GetChunkCount() ; ++ c) { // prendo il Chunk PtrOwner pSfrChunk( pSfr->CloneChunk( c)) ; if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid()) return false ; // calcolo i percorsi a ZigZag if ( ! CalcZigZag( pSfrChunk, myParams, vpCrvs, true)) return false ; if ( bRemoveOverlapLink) { ICRVCOMPOPOVECTOR vCrvFinal ; for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) vCrvFinal.emplace_back( Release( vpCrvs[u])) ; vpCrvs.clear() ; // sistemo le curve ottenute, aggiustando i Links for ( int u = 0 ; u < int( vCrvFinal.size()) ; ++ u) { ICRVCOMPOPOVECTOR vNewCrv ; if ( ! AdjustLinkZigZagInfill( vCrvFinal[u], myParams, vNewCrv)) return false ; for ( int uu = 0 ; uu < int( vNewCrv.size()) ; ++ uu) vpCrvs.emplace_back( Release( vNewCrv[uu])) ; } } // inserisco i percorsi da ritornare for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) { if ( bSmooth) { myParams.bSmooth = true ; ModifyCurveToSmoothed( vpCrvs[u], myParams, myParams.dRad / 16, myParams.dRad / 16, false) ; } vCrvCompoRes.emplace_back( Release( vpCrvs[u])) ; } } return true ; }