From df10b45c6b8ce5b2aa7776da80412d7ff3a565a2 Mon Sep 17 00:00:00 2001 From: Riccardo Elitropi Date: Tue, 30 Jul 2024 14:01:17 +0200 Subject: [PATCH] EgtGeomKernel : - in CalcPocketing aggiunta lavorazione CONFORMAL_ZIGZAG - migliorie varie. --- CalcPocketing.cpp | 667 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 600 insertions(+), 67 deletions(-) diff --git a/CalcPocketing.cpp b/CalcPocketing.cpp index 70e0aed..60dcfd1 100644 --- a/CalcPocketing.cpp +++ b/CalcPocketing.cpp @@ -71,6 +71,7 @@ struct PocketParams { bool bAllClosed = true ; // flag per indicare se tutti i bordi sono chiusi double dOffsExtra = 2. ; // Offset aggiuntivo per percosi a ZigZag/OneWay se richieste le curve di bordo Frame3d frLocXY ; // frame per conti nel piano XY + int nOffsType = ICurve::OFF_FILLET ; // tipologia di Offset per estensione degli aperti } ; static double TOL_TRAPEZOID = 50 * EPS_SMALL ; // tolleranza per casi a trapezio SpiralPocket @@ -724,6 +725,62 @@ AssignFeedSpiralOpt( const int nOptType, const PocketParams& PockParams, ICurveC return true ; } +//---------------------------------------------------------------------------- +static bool +AssignFeedConformalZigZag( ICurveComposite* pCompoPath, const ICurveComposite* pCrvLoop, + const PocketParams& PockParams) +{ + // controllo dei parametri + if ( pCompoPath == nullptr || ! pCompoPath->IsValid()) + return false ; + + // controllo se il Flag per assegnare la Feed è attivo + if ( ! PockParams.bCalcFeed) + return AssignMaxFeed( pCompoPath, PockParams) ; + + // imposto di Default la Feed minima per ogni sottocurva + AssignMinFeed( pCompoPath, PockParams) ; + + // creo la superficie rimossa ( creata con il raggio utensile) + // creo la superficie di classificazione ( creata mediante raggio utensile e SideStep) + // creo la curva da restituire con le Feed Assegnate + PtrOwner pSfrRemoved( CreateSurfFlatRegion()) ; + PtrOwner pSfrClass( CreateSurfFlatRegion()) ; + PtrOwner pCompoPathWithFeed( CreateCurveComposite()) ; + if ( IsNull( pSfrRemoved) || IsNull( pSfrClass) || IsNull( pCompoPathWithFeed)) + return false ; + ICRVCOMPOPOVECTOR vCrvEmpty ; + + // scorro tutte le curve del percorso + for ( int i = 0 ; i < pCompoPath->GetCurveCount() ; ++ i) { + // recupero la singola curva come composita + PtrOwner pSingleCompo( ConvertCurveToComposite( pCompoPath->GetCurve( i)->Clone())) ; + if ( IsNull( pSingleCompo) || ! pSingleCompo->IsValid()) + return false ; + + // calcolo la Feed per tale curva + AssignFeedSpiral( pSingleCompo, pSfrClass, false, vCrvEmpty, pCrvLoop, PockParams, 2 * PockParams.dRad / 3) ; + + // aggiorno la regione svuotata dal Tool + PtrOwner pSfrSingleCompo( GetSurfFlatRegionFromFatCurve( pSingleCompo->Clone(), PockParams.dRad + 5 * EPS_SMALL, false, false)) ; + if ( ! IsNull( pSfrSingleCompo) || ! pSfrSingleCompo->IsValid()) { + if ( ! pSfrClass->IsValid()) + pSfrClass->CopyFrom( pSfrSingleCompo) ; + else + pSfrClass->Add( *pSfrSingleCompo) ; + } + + if ( ! pCompoPathWithFeed->AddCurve( Release( pSingleCompo))) + return false ; + } + + // restituisco la curva se valida + if ( pCompoPathWithFeed->IsValid()) + pCompoPath->CopyFrom( pCompoPathWithFeed) ; + + return true ; +} + //---------------------------------------------------------------------------- static bool ResetCurveTempProps( ICurveComposite* pCrvCompo) @@ -1026,7 +1083,7 @@ ExtendPath( ICurveComposite* pCompo, const ISurfFlatRegion* pSfr, const PocketPa pCompo->AddLine( ptFall, bEndOrStart) ; pCompo->SetCurveTempProp( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0), TEMP_PROP_OUT_START, 0) ; pCompo->SetCurveTempParam( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0), - ( bEndOrStart ? GetMaxFeed( PockParams) : GetMinFeed( PockParams), 0)) ; + ( bEndOrStart ? GetMaxFeed( PockParams) : GetMinFeed( PockParams)), 0) ; bOkExtended = true ; return true ; } @@ -1231,8 +1288,9 @@ GetHomogeneousParts( ICurveComposite* pCrvCompo, const PocketParams& PockParams, //--------------------------------------------------------------------------- static bool -CreateSurfFrIncidence( const ICurveComposite* pCrv, const Vector3d& vtTanS_, const Vector3d& vtTanE_, - const double dRad, ISurfFlatRegion* pSfrInc) +CreateSurfFrIncidence( const ICurveComposite* pCrv, const PocketParams& PockParams, + const Vector3d& vtTanS_, const Vector3d& vtTanE_, const double dRad, + ISurfFlatRegion* pSfrInc) { // controllo dei parametri if ( pCrv == nullptr || ! pCrv->IsValid()) @@ -1252,7 +1310,7 @@ CreateSurfFrIncidence( const ICurveComposite* pCrv, const Vector3d& vtTanS_, con OffsetCurve OffsCrv ; PtrOwner pOffsExt( CreateCurveComposite()) ; if ( IsNull( pOffsExt) || - ! OffsCrv.Make( pCrv, dRad, ICurve::OFF_FILLET) || + ! OffsCrv.Make( pCrv, dRad, PockParams.nOffsType) || ! pOffsExt.Set( OffsCrv.GetLongerCurve()) || IsNull( pOffsExt)) return false ; @@ -1261,7 +1319,7 @@ CreateSurfFrIncidence( const ICurveComposite* pCrv, const Vector3d& vtTanS_, con PtrOwner pOffsInt( CreateCurveComposite()) ; if ( IsNull( pOffsInt)) return false ; - if ( OffsCrv.Make( pCrv, - dRad, ICurve::OFF_FILLET)) { + if ( OffsCrv.Make( pCrv, - dRad, PockParams.nOffsType)) { PtrOwner pMyCrv( OffsCrv.GetLongerCurve()) ; if ( ! IsNull( pMyCrv) && pMyCrv->IsValid()) pOffsInt.Set( pMyCrv) ; @@ -1336,7 +1394,7 @@ AdjustOpenEdge( const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vCrvI // definisco la regione di incidenza PtrOwner pSfrInc( CreateSurfFlatRegion()) ; if ( IsNull( pSfrInc) || - ! CreateSurfFrIncidence( pCrvBorder, vtTanS, vtTanE, dRad + 75 * EPS_SMALL, pSfrInc)) + ! CreateSurfFrIncidence( pCrvBorder, PockParams, 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 @@ -1376,13 +1434,15 @@ AdjustOpenEdge( const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vCrvI 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)) + if ( ! OffsCrv.Make( vpCrvs[cl], - dDiamJ * 0.5 - 20 * EPS_SMALL, PockParams.nOffsType)) return false ; // 5) scorro tutte le curve di Offset che si sono formate, prendendo sempre la più lunga tra le rimanenti PtrOwner pOffLongestCrv( OffsCrv.GetLongerCurve()) ; while ( ! IsNull( pOffLongestCrv)) { // 6) creo la regione di incidenza di tale curva ( "Curva a fagiolo") - PtrOwner pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ * 0.5, false, false)) ; + bool bSquareEnds = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ; + bool bSquareMids = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ; + PtrOwner pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ * 0.5, bSquareEnds, bSquareMids)) ; if ( IsNull( pSfrBean) || ! pSfrBean->IsValid()) return false ; // inverto se necessario @@ -1585,6 +1645,10 @@ AdjustContourWithOpenEdges( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vCrvI if ( PockParams.bAllowZigZagOneWayBorders && ( PockParams.nType == POCKET_ZIGZAG || PockParams.nType == POCKET_ONEWAY)) dRad += PockParams.dOffsExtra ; + // se lavorazione a ZigZag con smusso, esco di Step/2 per pulire gli aperti + if ( PockParams.nType == POCKET_ZIGZAG && PockParams.bSmooth) + dRad += max( PockParams.dSideStep / 2, PockParams.dRad / 8) ; + // ricavo i tratti omogenei ICRVCOMPOPOVECTOR vpCrvs ; @@ -2026,6 +2090,9 @@ ModifySurfByOpenEdges( ISurfFlatRegion* pSfr, PocketParams& PockParams, ICRVCOMP ++ nChunkOneCurve ; continue ; } + // controllo se la lavorazione ammatte estensione da aperti + if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) + continue ; // vettore delle isole che userò ( le isole aperte piccole sono trascurate) ICRVCOMPOPOVECTOR vCrvToTIsland ; @@ -5950,7 +6017,7 @@ CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, con //---------------------------------------------------------------------------- static bool -CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs, const int nOffsCount, +CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs, const int nIter, const PocketParams& PockParams, bool& bInsert) { @@ -5964,73 +6031,534 @@ CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs, c 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)) + // per ogni curva controllo se il suo Offset massimo non genera alcuna regione + double dMaxOffs ; + if ( ! CalcCurveLimitOffset( *pCrvOffs, dMaxOffs)) 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 + // se questa curva sparisce -> non serve inserirla come offset, non svuota parti aggiuntive + if ( dMaxOffs < PockParams.dRad - dOffs + 5 * EPS_SMALL) { bInsert = false ; return true ; } + } + else + 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 ; + // se non richiesto percorso avanzato + if ( ! PockParams.bOptOffsetsAdv) + return true ; - // frame locale, recupero DimX e DimY - Frame3d frLoc ; frLoc.Set( ptC, Z_AX, vtX) ; - if ( ! frLoc.IsValid()) - return true ; - BBox3d bBox ; - frLoc.Invert() ; - pCrvOffLonger->GetBBox( frLoc, bBox) ; - double dDimX = bBox.GetDimX() ; - double dDimY = bBox.GetDimY() ; + OffsetCurve OffsCrv ; // considero anche i casi in cui ho bSmallRad + if ( ! OffsCrv.Make( pCrvOffs, - PockParams.dRad + dOffs - 5 * EPS_SMALL , ICurve::OFF_FILLET)) + return false ; - // se dimensioni accettabili, inserisco - if ( dDimX < 2 * PockParams.dRad - 100 * EPS_SMALL || - dDimY < 2 * 2 * PockParams.dRad - 100 * EPS_SMALL) { - bInsert = false ; - return true ; - } + // controllo se richista ottimizzazione per Offsets mediante centroidi e medial Axis + if ( 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, Z_AX, 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 +ExtendGuideByIteration( ICurveComposite* pCompoTempGuide, const ICurveComposite* pCompoPerimeter, + bool bInverted) +{ + // controllo dei parametri + if ( pCompoTempGuide == nullptr || ! pCompoTempGuide->IsValid() || + pCompoPerimeter == nullptr || ! pCompoPerimeter->IsValid()) + return false ; + + const double EXTENSION_VAL = 1000. ; + + // ricavo il punto finale della guida + Point3d ptEndGuide ; pCompoTempGuide->GetEndPoint( ptEndGuide) ; + // direzione finale della guida + Vector3d vtEndGuide ; pCompoTempGuide->GetEndDir( vtEndGuide) ; + // creo un segmento diretto come il chiuso + PtrOwner pSeg( CreateCurveLine()) ; + if ( IsNull( pSeg) || + ! pSeg->Set( ptEndGuide, ptEndGuide + EXTENSION_VAL * vtEndGuide)) + return false ; + // calcolo l'intersezione tra questo segmento e la curva di perimetro aperta + IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ; + // se l'unica intersezione è il punto iniziale del segmento, allora non estendo la guida + // ( vale anche nel caso di intersezione con Overlap) + if ( ICC.GetIntersCount() == 1) { + IntCrvCrvInfo aInfo ; + if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) { + if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide)) + return true ; + } + } + // passo alla PolyLine del perimetro + PolyLine PL ; + pCompoPerimeter->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, PL) ; + // cerco il punto più esterno rispetto alla direzione del chiuso + Point3d ptPoly ; + PL.GetFirstPoint( ptPoly) ; + while ( PL.GetNextPoint( ptPoly)) { + if ( ! pSeg->Set( ptEndGuide, ptEndGuide + ( ptPoly - ptEndGuide) * EXTENSION_VAL)) + return false ; + // calcolo l'intersezione tra questo segmento e la curva di perimetro aperta + IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ; + // se una intersezione, allora di Overlap con punto iniziale e finale definito + if ( ICC.GetIntersCount() == 1) { + IntCrvCrvInfo aInfo ; + if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) { + if ( aInfo.bOverlap) { + if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide) && + AreSamePointApprox( aInfo.IciB[1].ptI, ptPoly)) + return ( pCompoTempGuide->AddLine( ptPoly)) ; + } + else { + if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide)) + return ( pCompoTempGuide->AddLine( ptPoly)) ; + } + } + } + // se più di una intersezione, allora il perimetro deve essere tutto Interno al segmento + // ( Out nel caso in cui ho le curve invertite) + IntCrvCrvInfo aInfo ; + bool bOk = true ; + for ( int i = 0 ; i < ICC.GetIntersCount() && bOk ; ++ i) { + bOk = ( ICC.GetIntCrvCrvInfo( i, aInfo)) ; + bOk = ( aInfo.bOverlap || + ( aInfo.IciB[0].nPrevTy != ( ! bInverted ? CRVC_OUT : CRVC_IN) && + aInfo.IciB[0].nNextTy != ( ! bInverted ? CRVC_OUT : CRVC_IN))) ; + } + if ( bOk) + return ( pCompoTempGuide->AddLine( ptPoly)) ; + } + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +IsCompoMadeBy2DifferentHomogeneousParts( const ICurveComposite* pCompo, const PocketParams& PockParams, + bool& bOk, ICRVCOMPOPOVECTOR& vpCrvs) +{ + // controllo dei parametri + if ( pCompo == nullptr || ! pCompo->IsValid()) + return false ; + bOk = false ; + vpCrvs.clear() ; + + // clono la curva composita ( cambierà il suo punto iniziale) + PtrOwner pCompoCL( CloneCurveComposite( pCompo)) ; + if ( IsNull( pCompoCL) || ! pCompoCL->IsValid()) + return false ; + + // recupero i tratti con proprietà uniformi + GetHomogeneousParts( pCompoCL, PockParams, vpCrvs) ; + if ( vpCrvs.size() > 1) { + // unisco il primo e l'ultimo se estremi compatibili + Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ; + Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ; + if ( AreSamePointApprox( ptS, ptE)) { + vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ; + vpCrvs.erase( vpCrvs.end() - 1) ; + } + } + + // controlloo che la curva abbia esattamente due tratti omogenei + bOk = ( int( vpCrvs.size()) == 2) ; + if ( ! bOk) + vpCrvs.clear() ; + return true ; + +} + +//---------------------------------------------------------------------------- +static bool +CalcCrvGuideConformalZigZag( const ISurfFlatRegion* pSfrChunk, PocketParams& PockParams, ICurveComposite* pCompoGuide, + ICurveComposite* pCompoClass) +{ + // controllo dei parametri + if ( pCompoGuide == nullptr || pCompoClass == nullptr || + pSfrChunk == nullptr || ! pSfrChunk->IsValid()) + return false ; + pCompoGuide->Clear() ; + pCompoClass->Clear() ; + + // la superficie deve avere un Chunk e non avere isole + if ( pSfrChunk->GetChunkCount() > 1 || pSfrChunk->GetLoopCount( 0) > 1) + return true ; + + // ricavo la curva di bordo ( la curva che definisce il perimetro) + PtrOwner pCompoPerimeter( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ; + if ( IsNull( pCompoPerimeter) || ! pCompoPerimeter->IsValid()) + return false ; + // controllo che sia formata da esattamente due tratti omogenei + bool b2HP = false ; + ICRVCOMPOPOVECTOR vpCrvs ; + if ( ! IsCompoMadeBy2DifferentHomogeneousParts( pCompoPerimeter, PockParams, b2HP, vpCrvs)) + return false ; + if ( ! b2HP) + return true ; + + // allargo curva di Bordo secondo le specifiche di una lavorazione a SpiralIn + double dRad = PockParams.dRad ; + int nOffsType = PockParams.nOffsType ; + PockParams.dRad = max( dRad / 8, PockParams.dSideStep / 2) ; + PockParams.nOffsType = ICurve::OFF_CHAMFER ; + ICRVCOMPOPOVECTOR vCrvEmpty ; + if ( ! AdjustContourWithOpenEdges( pCompoPerimeter, vCrvEmpty, PockParams)) + return false ; + PockParams.dRad = dRad ; + PockParams.nOffsType = nOffsType ; + + // reucupero ora i due tratti uniformi ( della curva allargata) + if ( ! IsCompoMadeBy2DifferentHomogeneousParts( pCompoPerimeter, PockParams, b2HP, vpCrvs) || + ! b2HP) + return false ; + + // la guida inizialmente è data dal tratto chiuso + // il perimetro coincide con il tratto aperto + PtrOwner pCompoTempGuide( Release( vpCrvs[0])) ; + if ( IsNull( pCompoTempGuide) || ! pCompoTempGuide->IsValid()) + return false ; + + // estendo la curva nel suo tratto finale ed iniziale + for ( int i = 0 ; i < 2 ; ++ i) { + if ( ! ExtendGuideByIteration( pCompoTempGuide, vpCrvs[1], ( i == 1))) + return false ; + // se la guida diventa diventa chiusa, allora esco + if ( ! pCompoTempGuide->IsValid() || pCompoTempGuide->IsClosed()) + return true ; + pCompoTempGuide->Invert() ; + vpCrvs[1]->Invert() ; + } + + // tratto iniziale + Vector3d vtStart ; pCompoTempGuide->GetStartDir( vtStart) ; + Point3d ptStart ; pCompoTempGuide->GetStartPoint( ptStart) ; + PtrOwner pLineStart( CreateCurveLine()) ; + if ( IsNull( pLineStart) || ! pLineStart->Set( ptStart - 1000. * vtStart, ptStart)) + return false ; + // tratto finale ( potrebbe intersecare il tratto iniziale) + Vector3d vtEnd ; pCompoTempGuide->GetEndDir( vtEnd) ; + Point3d ptEnd ; pCompoTempGuide->GetEndPoint( ptEnd) ; + PtrOwner pLineEnd( CreateCurveLine()) ; + if ( IsNull( pLineEnd) || ! pLineEnd->Set( ptEnd, ptEnd + 1000. * vtEnd)) + return false ; + IntersCurveCurve ICC( *pLineStart, *pLineEnd) ; + if ( ICC.GetIntersCount() > 0) { + IntCrvCrvInfo aInfo ; + if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) { + pLineStart->TrimStartAtParam( aInfo.IciA[0].dU) ; + pLineEnd->TrimEndAtParam( aInfo.IciB[0].dU) ; + } + } + // le due estensioni vanno tagliate presso i bordi dei lati aperti estesi + IntersCurveCurve ICCLS( *pLineStart, *vpCrvs[1]) ; + if ( ICCLS.GetIntersCount() > 0) { + IntCrvCrvInfo aInfo ; + for ( int i = 0 ; i < ICCLS.GetCrossIntersCount() ; ++ i) { + if ( ICCLS.GetIntCrvCrvInfo( i, aInfo)) { + if ( ! AreSamePointApprox( aInfo.IciA[ aInfo.bOverlap ? 1 : 0].ptI, ptStart)) + pLineStart->ModifyStart( aInfo.IciA[aInfo.bOverlap ? 1 : 0].ptI) ; + } + } + } + IntersCurveCurve ICCLE( *pLineEnd, *vpCrvs[1]) ; + if ( ICCLE.GetIntersCount() > 0) { + IntCrvCrvInfo aInfo ; + for ( int i = 0 ; i < ICCLE.GetCrossIntersCount() ; ++ i) { + if ( ICCLE.GetIntCrvCrvInfo( 0, aInfo)) { + if ( ! AreSamePointApprox( aInfo.IciA[0].ptI, ptEnd)) + pLineEnd->ModifyEnd( aInfo.IciA[0].ptI) ; + } + } + } + pCompoTempGuide->AddCurve( Release( pLineStart), false) ; + pCompoTempGuide->AddCurve( Release( pLineEnd), true) ; + + // recupero la curva guida + pCompoGuide->CopyFrom( pCompoTempGuide) ; + + // recupero la curva di classificazione ( abballendola) + PolyLine PL ; + pCompoPerimeter->ApproxWithLines( 10 * LIN_TOL_MIN, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) ; + PL.RemoveAlignedPoints( 500 * EPS_SMALL) ; + pCompoPerimeter->Clear() ; + pCompoPerimeter->FromPolyLine( PL) ; + pCompoPerimeter->RemoveSmallDefects( 10 * LIN_TOL_MIN, ANG_TOL_STD_DEG, true) ; + pCompoPerimeter->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG) ; + pCompoClass->CopyFrom( pCompoPerimeter) ; + ResetCurveTempProps( pCompoClass) ; + + return true ; +} + +//---------------------------------------------------------------------------- +static bool +AddAddConformalZigZag( const ISurfFlatRegion* pSfr, PocketParams& PockParams, + ICRVCOMPOPOVECTOR& vCrvCompoRes) +{ + /* + Calcolo ottimizzato di svuotatura mediante Offset progressivi di una guida, raccordati + rispetto al bordo della superficie di Pocketing mediante logica ZigZag + */ + + // controllo dei parametri + if ( pSfr == nullptr || ! pSfr->IsValid()) + return false ; + vCrvCompoRes.clear() ; + + // ciclo sui chunk della regione + for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) { + // recupero il Chunk nC-esimo + PtrOwner pSfrChunk( pSfr->CloneChunk( nC)) ; + if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid()) + return false ; + + // recupero il massimo Offset del Chunk nC-esimo + double dMaxOffs ; + pSfrChunk->GetMaxOffset( dMaxOffs) ; + // se inferiore del Tool ( ho già svuotato tale Chunk in precedenza) + if ( dMaxOffs < PockParams.dRad + 200 * EPS_SMALL) + continue ; + + // ricavo la guida ideale e la curva su cui tagliare gli Offset a partire dal Chunk nC-esimo + PtrOwner pCrvGuideExt( CreateCurveComposite()) ; + PtrOwner pCrvClass( CreateCurveComposite()) ; + if ( IsNull( pCrvGuideExt) || IsNull( pCrvClass) || + ! CalcCrvGuideConformalZigZag( pSfr, PockParams, pCrvGuideExt, pCrvClass)) + return false ; + // se non è stata trovata una guida ideale, allora passo al Chunk successivo + if ( ! pCrvGuideExt->IsValid() || pCrvGuideExt->GetCurveCount() == 0 || + ! pCrvClass->IsValid() || pCrvClass->GetCurveCount() == 0) { + return true ; + } + + // memorizzo le curve di Offset tagliate a seconda dalla geometria del perimetro + ICRVCOMPOPOVECTOR vCompoInternalOffs ; + const int MAX_ITER = 100 ; + double dOffs = PockParams.dRad + PockParams.dRadialOffset ; + for ( int i = 0 ; i < MAX_ITER ; ++ i) { + OffsetCurve OffsCrv ; + if ( ! OffsCrv.Make( pCrvGuideExt, - dOffs, ICurve::OFF_FILLET)) + return false ; + // se invece ne ottengo più di una, controllo se è necessaria + if ( OffsCrv.GetCurveCount() > 1) { + // recupero le curve extra ( tutte tranne la più lunga) + for ( int j = 0 ; j < OffsCrv.GetCurveCount() - 1 ; ++ j) { + // recupero la curva più corta + PtrOwner pCrvShorter( ConvertCurveToComposite( OffsCrv.GetShorterCurve())) ; + if ( ! IsNull( pCrvShorter) && pCrvShorter->IsValid()) { + // controllo se è davvero necessaria per non lasciare materiale + bool bSkip = false ; + if ( pCrvShorter->IsClosed()) { + CheckIfOffsetIsNecessary( pCrvShorter, ( i == 0 ? dOffs : PockParams.dSideStep), + i, PockParams, bSkip) ; + if ( bSkip) + return true ; + } + } + } + } + // se non ottengo nessuna curva, allora ho finito + PtrOwner pCrvOffs( OffsCrv.GetLongerCurve()) ; + if ( IsNull( pCrvOffs) || ! pCrvOffs->IsValid()) + break ; + // se ottengo una curva chiusa, lavorazione non ammessa + if ( pCrvOffs->IsClosed()) + return true ; + // la inverto se numero di Offset progressivo dispari + if ( ( ! IsEven( i) && ! PockParams.bInvert) || + ( IsEven( i) && PockParams.bInvert)) + pCrvOffs->Invert() ; + // classifico la curva rispetto al perimetro, devo tenere solo le parti interne + CRVCVECTOR ccClass ; + IntersCurveCurve ICC( *pCrvOffs, *pCrvClass) ; + if ( ! ICC.GetCurveClassification( 0, EPS_SMALL, ccClass)) + return true ; + // scorro tutte le classificazioni ottenute + for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { + // tengo quelle strettamente interne + if ( ccClass[j].nClass == CRVC_IN) { + // recupero la porzione di Offset + PtrOwner pCrvOffs_Int( pCrvOffs->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ; + if ( ! IsNull( pCrvOffs_Int) && pCrvOffs_Int->IsValid()) { + // salvo la curva + vCompoInternalOffs.emplace_back( ConvertCurveToComposite( Release( pCrvOffs_Int))) ; + // salvo come prima propeità temporanea, il numero di Offset progressivo + vCompoInternalOffs.back()->SetTempProp( i, 0) ; + } + } + } + dOffs += PockParams.dSideStep ; + } + // se non ho ottenuto Offset, passo al Chunk successivo + if ( vCompoInternalOffs.empty()) + return true ; + + /* + Creo la curva di Svuotatura come unione di due curve : + - La curva che non deve essere smussata ( curve di primo Offset e raccordi) + - La Curva che necessita di smusso ( le altre) + NB. Nel caso non sia richiesto lo smusso, allora la seconda curva non viene smussata, ma la + logica rimane la stessa. + */ + + PtrOwner pCompoNoSmooth( CreateCurveComposite()) ; + PtrOwner pCompoSmooth( CreateCurveComposite()) ; + if ( IsNull( pCompoNoSmooth) || IsNull( pCompoSmooth)) + return false ; + + + // scorro i tratti di Offset interni e aggiungo i raccordi sul bordo esterno + // raccordo il tratto di Offset interno corrente al successivo + const double TOL_JOINT = 25. * EPS_SMALL ; + double dHalfLenPerimeter ; + pCrvClass->GetLength( dHalfLenPerimeter) ; + dHalfLenPerimeter /= 2. ; + for ( int i = 0 ; i < int( vCompoInternalOffs.size()) - 1 ; ++ i) { + // recupero il parametro iniziale del raccordo sulla curva di perimetro + Point3d ptS ; vCompoInternalOffs[i]->GetEndPoint( ptS) ; + double dUS ; pCrvClass->GetParamAtPoint( ptS, dUS) ; + // recupero il parametro finale del raccordo sulla curva di perimetro + Point3d ptE ; vCompoInternalOffs[i+1]->GetStartPoint( ptE) ; + double dUE ; pCrvClass->GetParamAtPoint( ptE, dUE) ; + // aggiungo il tratto di Offset interno corrente alla curva di svuotatura + if ( vCompoInternalOffs[i]->GetTempProp() == 0) { + // se deriva dal primo Offset + if ( ! pCompoNoSmooth->AddCurve( Release( vCompoInternalOffs[i]), true, TOL_JOINT)) + return false ; + } + else { + // se deriva da Offset successivi + if ( ! pCompoSmooth->AddCurve( Release( vCompoInternalOffs[i]), true, TOL_JOINT)) + return false ; + } + // recupero il tratto di curva di interesse + PtrOwner pCrvJoint( pCrvClass->CopyParamRange( dUS, dUE)) ; + if ( ! IsNull( pCrvJoint) && pCrvJoint->IsValid()) { + double dLen ; + pCrvJoint->GetLength( dLen) ; + if ( dLen > dHalfLenPerimeter) { + pCrvJoint.Set( pCrvClass->CopyParamRange( dUE, dUS)) ; + pCrvJoint->Invert() ; + } + // aggiungo il tratto di percorso + if ( vCompoInternalOffs[i+1]->GetTempProp() == 0) { + // se l'offset successivo deriva ancora dal primo + if ( ! pCompoNoSmooth->AddCurve( Release( pCrvJoint), true, TOL_JOINT)) + return false ; + } + else { + // se l'Offset successivo non deriva dal primo Offset + if ( ! pCompoSmooth->AddCurve( Release( pCrvJoint), true, TOL_JOINT)) + return false ; + } + } + } + // aggiungo l'ultimo tratto di Offset + if ( vCompoInternalOffs.back()->GetTempProp() == 0) { + if ( ! pCompoNoSmooth->AddCurve( Release( vCompoInternalOffs.back()), true, TOL_JOINT)) + return false ; + } + else { + if ( ! pCompoSmooth->AddCurve( vCompoInternalOffs.back()->Clone(), true, TOL_JOINT)) + return false ; + } + // porto l'ultima curva del percorso NoSmooth nel percorso Smooth per avere un raccordo anche + // sul tratto di giunzione tra esse + if ( pCompoSmooth->IsValid() && pCompoNoSmooth->IsValid()) { + PtrOwner pCrv( pCompoNoSmooth->GetLastCurve()->Clone()) ; + if ( IsNull( pCrv) || ! pCrv->IsValid() || + ! pCompoSmooth->AddCurve( Release( pCrv), false, TOL_JOINT)) + return false ; + delete( pCompoNoSmooth->RemoveFirstOrLastCurve( true)) ; + } + + // smusso dove necessario + ModifyCurveToSmoothed( pCompoSmooth, PockParams, PockParams.dRad / 8, PockParams.dRad / 8, false) ; + // creo il percorso di svuotatura + PtrOwner pCompoTempPath( CreateCurveComposite()) ; + if ( IsNull( pCompoTempPath)) + return false ; + if ( pCompoNoSmooth->IsValid() && pCompoNoSmooth->GetCurveCount() > 0) { + if ( ! pCompoTempPath.Set( pCompoNoSmooth)) + return false ; + } + if ( pCompoSmooth->IsValid()) { + if ( ! pCompoTempPath->AddCurve( Release( pCompoSmooth), true, TOL_JOINT)) + return false ; + } + // se la curva finale di Pocketing è valida, allora la resituisco + if ( pCompoTempPath->IsValid()) { + // inverto l'ordine delle curve di Offset ( in modo da partire sempre ad eseguire il percorso + // più lontano dal dai lati chiusi) + pCompoTempPath->Invert() ; + // calcolo la Feed del percorso trovato + if ( ! AssignFeedConformalZigZag( pCompoTempPath, pCrvClass, PockParams)) + return false ; + // aggiugno il tratto iniziale e finale per entrate da fuori + Vector3d vtStart ; pCompoTempPath->GetStartDir( vtStart) ; + vtStart.Invert() ; + bool bExt = false ; + if ( ! ExtendPath( pCompoTempPath, pSfr, PockParams, vtStart, false, bExt)) + return false ; + Vector3d vtEnd ; pCompoTempPath->GetEndDir( vtEnd) ; + if ( ! ExtendPath( pCompoTempPath, pSfr, PockParams, vtEnd, true, bExt)) + return false ; + // sostituisco la curva + if ( pCompoTempPath->IsValid()) + vCrvCompoRes.emplace_back( Release( pCompoTempPath)) ; + } + } + + return true ; +} //---------------------------------------------------------------------------- static bool @@ -6166,7 +6694,7 @@ CalcSpiral( const ISurfFlatRegion* pSrfPock, const PocketParams& PockParams, int pCrvCompoBorder->Invert() ; // controllo quali regioni di Offset possono essere sostituite bool bInsert = true ; - if ( ! CheckIfOffsetIsNecessary( pCrvCompoBorder, dOffs - dOffsPrec, int( vOffs.size()), nIter, PockParams, bInsert)) + if ( ! CheckIfOffsetIsNecessary( pCrvCompoBorder, dOffs - dOffsPrec, nIter, PockParams, bInsert)) return false ; if ( bInsert) vOffs.emplace_back( Release( pCrvCompoBorder)) ; @@ -7317,7 +7845,8 @@ CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid() || dStep < 10 * EPS_SMALL || - ( nType != POCKET_ZIGZAG && nType != POCKET_ONEWAY && nType != POCKET_SPIRALIN && nType != POCKET_SPIRALOUT)) + ( nType != POCKET_ZIGZAG && nType != POCKET_ONEWAY && nType != POCKET_SPIRALIN && + nType != POCKET_SPIRALOUT && nType != POCKET_CONFORMAL_ZIGZAG)) return false ; // pulizia vettore delle curve elementari @@ -7381,7 +7910,11 @@ CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double return false ; break ; case POCKET_SPIRALOUT : - if ( ! AddSpiralOut( pSfrAdj, pSfr, myParams, vCrvCompoRes)) + if ( ! AddSpiralOut( pSfrAdj, pSfr_Loc, myParams, vCrvCompoRes)) + return false ; + break ; + case POCKET_CONFORMAL_ZIGZAG : + if ( ! AddAddConformalZigZag( pSfr_Loc, myParams, vCrvCompoRes)) return false ; break ; }