From 74bf6c65e883dc2008644d8d2d672f6310c719e6 Mon Sep 17 00:00:00 2001 From: Riccardo Elitropi Date: Fri, 10 Jul 2026 17:22:19 +0200 Subject: [PATCH] EgtMachKernel : - in SawFinishing aggiunto sottotipo "Along5A" - in SawFinishing correzioni varie. --- Operation.cpp | 4 +- SawFinishing.cpp | 687 +++++++++++++++++++++++++++++++++++++++++-- SawFinishing.h | 27 +- SawFinishingData.cpp | 3 +- 4 files changed, 688 insertions(+), 33 deletions(-) diff --git a/Operation.cpp b/Operation.cpp index 0def19e..5f6a6c0 100644 --- a/Operation.cpp +++ b/Operation.cpp @@ -2525,8 +2525,8 @@ Operation::VerifyMcentLineMidPoint( const Point3d& ptPrec, const Vector3d& vtDir bAxError = false ; Machine* pMachine = m_pMchMgr->GetCurrMachine() ; - // se disposizione non vanno fatti controlli - if ( GetType() == OPER_DISP) + // se disposizione o finitura di cornici con lama non vanno fatti controlli + if ( GetType() == OPER_DISP || GetType() == OPER_SAWFINISHING) return true ; // se superato il limite di ricursioni, non devo fare alcunché diff --git a/SawFinishing.cpp b/SawFinishing.cpp index c62b64b..9e64a67 100644 --- a/SawFinishing.cpp +++ b/SawFinishing.cpp @@ -24,6 +24,7 @@ #include "/EgtDev/Include/EGkCurveLine.h" #include "/EgtDev/Include/EGkCurveArc.h" #include "/EgtDev/Include/EGkCurveComposite.h" +#include "/EgtDev/Include/EGkCurveAux.h" #include "/EgtDev/Include/EGkSurfLocal.h" #include "/EgtDev/Include/EGkSurfTriMesh.h" #include "/EgtDev/Include/EGkOffsetCurve.h" @@ -59,6 +60,8 @@ using namespace std ; // 2715 = "Error in SawFinishing : post apply not calculable" // 2716 = "Error in SawFinishing : special apply not calculable" // 2717 = "Error in SawFinishing : CalculateSectionToolPath failed" +// 2718 = "Error in SawFinishing : CalculateStraightAlong5AToolPath failed" +// 2719 = "Error in SawFinishing : Splitting Section failed" // 2751 = "Warning in SawFinishing : Skipped entity (xx)" // 2752 = "Warning in SawFinishing : No machinable path" // 2753 = "Warning in SawFinishing : Tool name changed (xx)" @@ -565,35 +568,45 @@ SawFinishing::Apply( bool bRecalc, bool bPostApply) if ( m_Params.m_nSubType == SAWFIN_SUB_ALONG) { if ( m_bStraight) { if ( ! CalculateStraightAlongToolPath( nAuxId, nClId)) { - m_pMchMgr->SetLastError( 2709, "Error in SawFinishing : CalculateAlongToolPath failed") ; - return false ; + m_pMchMgr->SetLastError( 2709, "Error in SawFinishing : CalculateAlongToolPath failed") ; + return false ; } } else { if ( ! CalculateCurvedAlongToolPath( nAuxId, nClId)) { - m_pMchMgr->SetLastError( 2709, "Error in SawFinishing : CalculateAlongToolPath failed") ; - return false ; + m_pMchMgr->SetLastError( 2709, "Error in SawFinishing : CalculateAlongToolPath failed") ; + return false ; } } } else if ( m_Params.m_nSubType == SAWFIN_SUB_ACROSS) { if ( m_bStraight) { if ( ! CalculateStraightAcrossToolPath( nAuxId, nClId)) { - m_pMchMgr->SetLastError( 2710, "Error in SawFinishing : CalculateAcrossToolPath failed") ; + m_pMchMgr->SetLastError( 2710, "Error in SawFinishing : CalculateAcrossToolPath failed") ; return false ; } } else { if ( ! CalculateCurvedAcrossToolPath( nAuxId, nClId)) { - m_pMchMgr->SetLastError( 2710, "Error in SawFinishing : CalculateAcrossToolPath failed") ; + m_pMchMgr->SetLastError( 2710, "Error in SawFinishing : CalculateAcrossToolPath failed") ; return false ; } } } else if ( m_Params.m_nSubType == SAWFIN_SUB_SECTION) { - if ( ! CalculateStraightSectionToolPath( nAuxId, nClId)) { - m_pMchMgr->SetLastError( 2717, "Error in SawFinishing : CalculateSectionToolPath failed") ; - return false ; + if ( m_bStraight) { + if ( ! CalculateStraightSectionToolPath( nAuxId, nClId)) { + m_pMchMgr->SetLastError( 2717, "Error in SawFinishing : CalculateSectionToolPath failed") ; + return false ; + } + } + } + else if ( m_Params.m_nSubType == SAWFIN_SUB_ALONG_5A) { + if ( m_bStraight) { + if ( ! CalculateStraightAlong5AToolPath( nAuxId, nClId)) { + m_pMchMgr->SetLastError( 2718, "Error in SawFinishing : CalculateStraightAlong5AToolPath failed") ; + return false ; + } } } @@ -1100,7 +1113,7 @@ SawFinishing::CalculateStraightAlongToolPath( int nAuxId, int nClId) Point3d ptGdStart, ptGdEnd ; Vector3d vtGdDir ; int nGuideId = m_pGeomDB->GetFirstInGroup( m_pGeomDB->GetFirstNameInGroup( nAuxId, MCH_GUIDE)) ; - if ( ! CalculateGuideLine( nGuideId, pSect, ptGdStart, ptGdEnd, vtGdDir, nSectGrpId, false)) + if ( ! CalculateGuideLine( nGuideId, ptGdStart, ptGdEnd, vtGdDir, nSectGrpId, false)) return false ; if ( m_Params.m_bInvert) { swap( ptGdStart, ptGdEnd) ; @@ -1295,7 +1308,7 @@ SawFinishing::CalculateStraightAcrossToolPath( int nAuxId, int nClId) Point3d ptGdStart, ptGdEnd ; Vector3d vtGdDir ; int nGuideId = m_pGeomDB->GetFirstInGroup( m_pGeomDB->GetFirstNameInGroup( nAuxId, MCH_GUIDE)) ; - if ( ! CalculateGuideLine( nGuideId, pSect, ptGdStart, ptGdEnd, vtGdDir, nSectGrpId, bExtend)) + if ( ! CalculateGuideLine( nGuideId, ptGdStart, ptGdEnd, vtGdDir, nSectGrpId, bExtend)) return false ; Vector3d vtDeltaSect = vtGdDir * ( vtGdDir * ( ptGdStart - frSect.Orig())) ; if ( m_Params.m_bInvert) { @@ -1811,7 +1824,7 @@ SawFinishing::CalculateStraightSectionToolPath( int nAuxId, int nClId) Point3d ptGdStart, ptGdEnd ; Vector3d vtGdDir ; int nGuideId = m_pGeomDB->GetFirstInGroup( m_pGeomDB->GetFirstNameInGroup( nAuxId, MCH_GUIDE)) ; - if ( ! CalculateGuideLine( nGuideId, pSect, ptGdStart, ptGdEnd, vtGdDir, nSectGrpId, bExtend)) + if ( ! CalculateGuideLine( nGuideId, ptGdStart, ptGdEnd, vtGdDir, nSectGrpId, bExtend)) return false ; if ( m_Params.m_bInvert) { swap( ptGdStart, ptGdEnd) ; @@ -1964,6 +1977,117 @@ SawFinishing::CalculateStraightSectionToolPath( int nAuxId, int nClId) return true ; } +//---------------------------------------------------------------------------- +bool +SawFinishing::CalculateStraightAlong5AToolPath( int nAuxId, int nClId) +{ + // Recupero ed elaboro le sezioni per portarle ad essere la curva del centro in basso della lama + int nSectGrpId = m_pGeomDB->GetFirstNameInGroup( nAuxId, MCH_SECTION) ; + SECTIONVECTOR vSections ; + if ( ! Split5ASection( nSectGrpId, vSections)) { + m_pMchMgr->SetLastError( 2719, "Error in SawFinishing : Splitting Section failed") ; + return false ; + } + // recupero il riferimento globale delle sezioni + Frame3d frSect ; + m_pGeomDB->GetGroupGlobFrame( nSectGrpId, frSect) ; + // recupero il box globale delle sezioni + BBox3d b3Sect ; + m_pGeomDB->GetGlobalBBox( nSectGrpId, b3Sect) ; + + // Recupero da UserNotes il flag di estensione al bordo del grezzo + bool bExtend ; + if ( ! GetValInNotes( m_Params.m_sUserNotes, UN_EXTEND, bExtend)) + bExtend = true ; + // se estensione non presente e Link di tipo Extended, lo forzo come Center + if ( ! bExtend && m_Params.m_nLeadLinkType == SAWFIN_LL_OUT) { + m_pMchMgr->SetWarning( 2655, "Warning in SawRoughing : Link not compatible with extension") ; + m_Params.m_nLeadLinkType = SAWFIN_LL_CENT ; + } + // se link di tipo Standard, questa viene convertita in Center, per la lavorazione corrente si effettuano le rotazioni della lama + // sempre al di fuori del grezzo + if ( m_Params.m_nLeadLinkType == SAWFIN_LL_STD) { + m_pMchMgr->SetWarning( 2655, "Warning in SawRoughing : Link not compatible with extension") ; + m_Params.m_nLeadLinkType = SAWFIN_LL_CENT ; + } + + // Recupero ed elaboro la linea guida + Point3d ptGdStart, ptGdEnd ; + Vector3d vtGdDir ; + int nGuideId = m_pGeomDB->GetFirstInGroup( m_pGeomDB->GetFirstNameInGroup( nAuxId, MCH_GUIDE)) ; + if ( ! CalculateGuideLine( nGuideId, ptGdStart, ptGdEnd, vtGdDir, nSectGrpId, false)) + return false ; + if ( m_Params.m_bInvert) { + swap( ptGdStart, ptGdEnd) ; + vtGdDir.Invert() ; + } + // recupero il box globale della linea guida + BBox3d b3Guide ; + m_pGeomDB->GetGlobalBBox( nGuideId, b3Guide) ; + // recupero il box del grezzo in globale + BBox3d b3Test ; + b3Test.Add( b3Sect) ; + b3Test.Add( b3Guide) ; + BBox3d b3Raw ; + if ( ! GetRawGlobBox( m_nPhase, b3Test, 10 * EPS_SMALL, b3Raw) || b3Raw.IsEmpty()) { + m_pMchMgr->SetLastError( 2703, "Error in SawFinishing : Empty RawBox") ; + return false ; + } + + // valuto l'espressione dell'affondamento + ExeLuaSetGlobNumVar( "TH", 0) ; + ExeLuaSetGlobNumVar( "RB", b3Raw.GetMax().z - b3Raw.GetMin().z) ; + double dDepth ; + if ( ! ExeLuaEvalNumExpr( m_Params.m_sDepth, &dDepth)) { + m_pMchMgr->SetLastError( 2704, "Error in SawFinishing : Depth not computable") ; + return false ; + } + + // creo gruppo per geometria di lavorazione + int nPxId = m_pGeomDB->AddGroup( GDB_ID_NULL, nClId, Frame3d()) ; + if ( nPxId == GDB_ID_NULL) + return false ; + m_pGeomDB->SetName( nPxId, MCH_PATH + "1_1") ; + m_pGeomDB->SetMaterial( nPxId, GREEN) ; + + // Imposto dati comuni + SetPathId( nPxId) ; + + // recupero la TriMesh del grezzo in globale + PtrOwner pStmRaw( CreateSurfTriMesh()) ; + if ( IsNull( pStmRaw)) + return false ; + pStmRaw->AdjustTopology() ; + int nRawId = m_pMchMgr->GetFirstRawPart() ; + while ( nRawId != GDB_ID_NULL) { + // verifico che il grezzo compaia nella fase + if ( m_pMchMgr->VerifyRawPartPhase( nRawId, m_nPhase)) { + // recupero l'oggetto dal database con tale Id + int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; + const IGeoObj* pGObj = m_pGeomDB->GetGeoObj( nRawSolidId) ; + if ( pGObj != nullptr) { + // recupero il frame in cui si trova + Frame3d frRaw ; + m_pGeomDB->GetGlobFrame( nRawSolidId, frRaw) ; + // controllo che sia una Trimesh + if ( pGObj->GetType() == SRF_TRIMESH) { + SurfLocal StmRawPart( GetSurfTriMesh( pGObj), frRaw, GLOB_FRM) ; + // lo aggiungo alla Trimesh complessiva + pStmRaw->Add( *GetSurfTriMesh( StmRawPart)) ; + } + } + } + // passo al grezzo successivo + nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; + } + + // elaboro le Section/SubSections + if ( ! CalcAlong5ACuts( vSections, frSect, bExtend, ptGdStart, ptGdEnd, vtGdDir, dDepth, b3Raw, pStmRaw)) + return false ; + + return true ; +} + //---------------------------------------------------------------------------- bool SawFinishing::ExtendPathToRaw( PolyLine& PL, const ISurfTriMesh* pStmRaw, double dExtraOut) const @@ -2312,21 +2436,20 @@ SawFinishing::CalculateSection( int nSectGrpId, ICurve*& pSect) const typedef vector INDASCVECTOR ; // vettore di coppie indice, ascissa INDASCVECTOR vIAsc ; vIAsc.reserve( vpNucCrvs.size()) ; - for ( int i = 0 ; i < int( vpNucCrvs.size()) ; ++ i) { + for ( int i = 0 ; i < ssize( vpNucCrvs) ; ++ i) { Point3d ptStart ; if ( ! vpNucCrvs[i]->GetStartPoint( ptStart)) return false ; vIAsc.emplace_back( i, ptStart.x) ; } - sort( vIAsc.begin(), vIAsc.end(), []( const INDASC& a, const INDASC& b) - { return a.second > b.second ; }) ; + sort( vIAsc.begin(), vIAsc.end(), []( const INDASC& a, const INDASC& b) { return a.second > b.second ; }) ; // unisco le diverse curve (ora aperte) in un unico profilo PtrOwner pCurve( CreateCurveComposite()) ; if ( IsNull( pCurve)) return false ; bool bFirst = true ; - for ( int i = 0 ; i < int( vpNucCrvs.size()) ; ++ i) { + for ( int i = 0 ; i < ssize( vpNucCrvs) ; ++ i) { if ( ! bFirst) { Point3d ptStart ; if ( ! vpNucCrvs[vIAsc[i].first]->GetStartPoint( ptStart) || @@ -2340,20 +2463,23 @@ SawFinishing::CalculateSection( int nSectGrpId, ICurve*& pSect) const // offset radiale delle sezioni per il sovramateriale double dOffsR = GetOffsR() ; - PtrOwner pOvrCrv ; OffsetCurve OffsCrv ; OffsCrv.Make( pCurve, dOffsR, ICurve::OFF_FILLET) ; - pOvrCrv.Set( OffsCrv.GetLongerCurve()) ; + PtrOwner pOvrCrv( OffsCrv.GetLongerCurve()) ; + if ( IsNull( pOvrCrv) || ! pOvrCrv->IsValid()) + return false ; // ricavo dalla sezione la curva per centro in basso della lama double dOffsX = 0.5 * m_TParams.m_dThick ; - PtrOwner pCrv ; OffsetCurveOnX OffsCrvX ; - OffsCrvX.Make( pOvrCrv, dOffsX) ; - pCrv.Set( OffsCrvX.GetLongerCurve()) ; + if ( ! OffsCrvX.Make( pOvrCrv, dOffsX)) + return false ; + PtrOwner pCrv( OffsCrvX.GetLongerCurve()) ; + if ( IsNull( pCrv) || ! pCrv->IsValid()) + return false ; // eseguo trim (se richiesto) - if ( IsNull( pCrv) || ! TrimSection( pCrv)) + if ( ! TrimSection( pCrv)) return false ; pSect = Release( pCrv) ; @@ -2366,7 +2492,7 @@ bool SawFinishing::TrimSection( ICurve* pCrv) const { // se parametri di accorciamento iniziale e finale sono nulli, non faccio alcunché - if ( m_Params.m_dStartAddLen > - EPS_SMALL && m_Params.m_dEndAddLen > -EPS_SMALL) + if ( m_Params.m_dStartAddLen > -EPS_SMALL && m_Params.m_dEndAddLen > -EPS_SMALL) return true ; // box della sezione in locale @@ -2379,13 +2505,13 @@ SawFinishing::TrimSection( ICurve* pCrv) const ptMin.x -= m_Params.m_dStartAddLen ; ptMax.x += m_Params.m_dEndAddLen ; double dDimX = ptMax.x - ptMin.x ; - double dDimY = ptMax.y - ptMin.y + 100 * EPS_SMALL ; + double dDimY = ptMax.y - ptMin.y + 100. * EPS_SMALL ; // regione del box nel piano XY PtrOwner pSfr( GetSurfFlatRegionRectangle( dDimX, dDimY)) ; if ( IsNull( pSfr)) return false ; - pSfr->Translate( Vector3d( ptMin.x, ptMin.y - 50 * EPS_SMALL, ptMin.z)) ; + pSfr->Translate( Vector3d( ptMin.x, ptMin.y - 50. * EPS_SMALL, ptMin.z)) ; // calcolo la classificazione della curva rispetto alla regione CRVCVECTOR ccClass ; @@ -2394,7 +2520,7 @@ SawFinishing::TrimSection( ICurve* pCrv) const // determino l'intervallo di curva da conservare Intervals inOk ; - for ( auto& ccOne : ccClass) { + for ( const CrvClass& ccOne : ccClass) { if ( ccOne.nClass == CRVC_IN || ccOne.nClass == CRVC_ON_P || ccOne.nClass == CRVC_ON_M) inOk.Add( ccOne.dParS, ccOne.dParE) ; } @@ -2406,6 +2532,289 @@ SawFinishing::TrimSection( ICurve* pCrv) const return ( pCrv->TrimEndAtParam( dUmax) && pCrv->TrimStartAtParam( dUmin)) ; } +//---------------------------------------------------------------------------- +bool +SawFinishing::Split5ASection( int nSectGrpId, SECTIONVECTOR& vSections) const +{ + vSections.clear() ; + + // recupero le sezioni + ICURVEPVECTOR vpSects ; + int nSectId = m_pGeomDB->GetFirstInGroup( nSectGrpId) ; + while ( nSectId != GDB_ID_NULL) { + // recupero il puntatore alla curva + vpSects.emplace_back( ::GetCurve( m_pGeomDB->GetGeoObj( nSectId))) ; + if ( vpSects.back() == nullptr) + return false ; + // passo alla successiva + nSectId = m_pGeomDB->GetNext( nSectId) ; + } + + // elimino eventuali sottosquadra + ICRVCOMPOPOVECTOR vpNucCrvs ; vpNucCrvs.reserve( vpSects.size()) ; + for ( const auto& pSect : vpSects) { + PtrOwner pCrvCompo( CreateCurveComposite()) ; + if ( IsNull( pCrvCompo) || + ! pCrvCompo->AddCurve( *pSect) || + ! pCrvCompo->RemoveUndercutOnY( LIN_TOL_STD, ANG_TOL_STD_DEG)) + return false ; + vpNucCrvs.emplace_back( Release( pCrvCompo)) ; + } + + // per ogni sezione vengono rimossi i tratti verticali + ICRVCOMPOPOVECTOR vSimpleSections ; + for ( int i = 0 ; i < ssize( vpNucCrvs) ; ++ i) { + // se tratto non valido, non lo considero + if ( IsNull( vpNucCrvs[i]) || ! vpNucCrvs[i]->IsValid()) + return false ; + + // oriento la curva corrente per X decrescenti + Point3d ptStart ; vpNucCrvs[i]->GetStartPoint( ptStart) ; + Point3d ptEnd ; vpNucCrvs[i]->GetEndPoint( ptEnd) ; + if ( ptEnd.x > ptStart.x) + vpNucCrvs[i]->Invert() ; + + // approssimo per la presenza di archi e classifico i tratti + PolyLine PL ; + vpNucCrvs[i]->ApproxWithLines( LIN_TOL_MID, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL) ; + vpNucCrvs[i]->Clear() ; + vpNucCrvs[i]->FromPolyLine( PL) ; + INTVECTOR vnClass ; + if ( ! ClassifySection( vpNucCrvs[i], vnClass)) + return false ; + + // vettore delle parti di sezione validi senza tratti verticiali (e sottosquadra) + ICRVCOMPOPOVECTOR vpNucCrvsNoVert ; vpNucCrvsNoVert.reserve( vnClass.size()) ; + PtrOwner pCompoTmp( nullptr) ; + for ( int j = 0 ; j < ssize( vnClass) ; ++ j) { + if ( vnClass[j] == CCL_VERT) { + if ( ! IsNull( pCompoTmp) && pCompoTmp->IsValid()) + vpNucCrvsNoVert.emplace_back( ::Release( pCompoTmp)) ; + } + else { + if ( IsNull( pCompoTmp)) + pCompoTmp.Set( CreateCurveComposite()) ; + const ICurve* pCrv = vpNucCrvs[i]->GetCurve( j) ; + if ( pCrv == nullptr || ! pCrv->IsValid()) + return false ; + pCompoTmp->AddCurve( pCrv->Clone()) ; + } + } + erase_if( vpNucCrvsNoVert, []( const PtrOwner& pCompo) { return ( pCompo == nullptr || ! pCompo->IsValid()) ; }) ; + for ( int j = 0 ; j < ssize( vpNucCrvsNoVert) ; ++ j) { + if ( ! vSimpleSections.emplace_back( ::Release( vpNucCrvsNoVert[j]))) + return false ; + } + } + + // dalle sezioni semplici recuperate eseguo l'Offset Radiale complessivo ( evito così interferenze con sezioni differenti) + ICURVEPVECTOR vMySimpleSections ; vMySimpleSections.resize( vSimpleSections.size()) ; + for ( int i = 0 ; i < ssize( vSimpleSections) ; ++ i) + vMySimpleSections[i] = vSimpleSections[i] ; + ICURVEPOVECTOR vMySections ; + if ( ! CalcOffsetCurves( vMySimpleSections, vMySections, GetOffsR())) + return false ; + for ( int i = 0 ; i < ssize( vMySections) ; ++ i) { + if ( vMySections[i] != nullptr && vMySections[i]->IsValid()) { + vSections.resize( vSections.size() + 1) ; + if ( ! vSections.back().pSection.Set( CreateCurveComposite()) || ! vSections.back().pSection->AddCurve( vMySections[i]->Clone())) + return false ; + } + } + erase_if( vSections, []( const SectionInfo& SectionInfo) { + return ( SectionInfo.pSection == nullptr || ! SectionInfo.pSection->IsValid()) ; + }) ; + + // calcolo ordinamento delle curve secondo la X decrescente + typedef pair INDASC ; // coppia indice, ascissa + typedef vector INDASCVECTOR ; // vettore di coppie indice, ascissa + INDASCVECTOR vIAsc ; + vIAsc.reserve( vSections.size()) ; + for ( int i = 0 ; i < ssize( vSections) ; ++ i) { + Point3d ptStart ; + if ( ! vSections[i].pSection->GetStartPoint( ptStart)) + return false ; + vIAsc.emplace_back( i, ptStart.x) ; + } + sort( vIAsc.begin(), vIAsc.end(), []( const INDASC& a, const INDASC& b) { return a.second > b.second ; }) ; + for ( int nNewPos = 0 ; nNewPos < ssize( vIAsc) ; ++ nNewPos) { + int nOldPos = vIAsc[nNewPos].first ; + if ( nOldPos == nNewPos) + continue ; + swap( vSections[nNewPos], vSections[nOldPos]) ; + swap( vIAsc[nNewPos].first, vIAsc[nOldPos].first) ; + } + + // !<-- NB: + // - Una Section semplice non presente tratti verticali e non presenta tratti in sottosquadra + // - Una Section semplice è orientata per X decrescente + // - Le sue SubSections sono anch'esse orientate per X decrescente + + // recupero angolo di Split iniziale e finale per i tratti + double dAngDegSplit = 60. ; + double dAngUN = 0. ; + if ( GetValInNotes( m_Params.m_sUserNotes, UN_LIMIT_ANG, dAngUN)) + dAngDegSplit = max( 15. - EPS_ANG_SMALL, dAngUN) ; + + // ---------------------------- funzioni ausiliarie semplici ------------------------------------- + auto AddSubSection = [&]( SUBSECTIONVECTOR& vSubSections, PtrOwner& pCompo, int nHeadSide) { + if ( IsNull( pCompo) || ! pCompo->IsValid()) + return false ; + vSubSections.resize( vSubSections.size() + 1) ; + vSubSections.back().pSubSection.Set( ::Release( pCompo)) ; + vSubSections.back().nHeadSide = nHeadSide ; + return true ; + } ; + auto GetHeadSide = [&]( int nClass) { + if ( nClass == CCL_FALL) + return int( SAWFIN_HS_RIGHT) ; + else if ( nClass == CCL_RISE) + return int( SAWFIN_HS_LEFT) ; + else + return m_Params.m_nHeadSide ; + } ; + + // per ogni sezione semplice + for ( int i = 0 ; i < ssize( vSections) ; ++ i) { + + // riferimenti + SectionInfo& CurrSection = vSections[i] ; + SUBSECTIONVECTOR& vCurrSubSections = CurrSection.vSubSections ; + + // elimino tutti i tratti all'inizio e alla fine della sezione con tangenza sotto i 60deg + if ( ! TrimVerticalSectionBounds( CurrSection.pSection, dAngDegSplit)) + return false ; + // eseguo trim della parte iniziale e finale (se richiesto) + if ( ! TrimSection( CurrSection.pSection)) + return false ; + // Classifico i tratti a seconda della pendenza (salita, discesa, verticale, orizzontale) + INTVECTOR vnClass ; + if ( ! ClassifySection( CurrSection.pSection, vnClass)) + return false ; + + // se una sola classificazione presente + if ( ssize( vnClass) == 1) { + PtrOwner pSubSection( CloneCurveComposite( CurrSection.pSection)) ; + return AddSubSection( vCurrSubSections, pSubSection, vnClass[0]) ; + } + + // scrorro i tratti correnti + bool bCanSwapHead = true ; + for ( int i = 1 ; i < ssize( vnClass) ; ++ i) { + if ( i == 1) { + // inserisco il primo elemento + PtrOwner pCompoSubSect( ConvertCurveToComposite( CurrSection.pSection->CopyParamRange( double( i - 1), double( i)))) ; + if ( ! AddSubSection( vCurrSubSections, pCompoSubSect, GetHeadSide( vnClass[0]))) + return false ; + } + // classificazione corrente + const int& nCurrClass = vnClass[i] ; + // classificazione precedente + const int& nPrevClass = vnClass[i - 1] ; + // parametri per copia sotto-tratto corrente + double dUS = double( i) ; + double dUE = double( i + 1) ; + PtrOwner pCompoSub( ConvertCurveToComposite( CurrSection.pSection->CopyParamRange( dUS, dUE))) ; + if ( IsNull( pCompoSub) || ! pCompoSub->IsValid()) + return false ; + switch ( nCurrClass) { + // --- classificazione corrente Flat o Small + case CCL_FLAT : + case CCL_SMALL : + { + // a prescindere dai valori precedenti si aggiunge semplicemente il tratto di sezione + if ( ! vCurrSubSections.back().pSubSection->AddCurve( ::Release( pCompoSub))) + return false ; + bCanSwapHead = ( bCanSwapHead && ( nPrevClass != CCL_RISE && nPrevClass != CCL_FALL)) ; + break ; + } + // --- classificazione corrente Fall (la testa deve essere a destra) + case CCL_FALL : { + // se la precedente è flat, veticale o small + if ( nPrevClass == CCL_FLAT || nPrevClass == CCL_SMALL) { + if ( vCurrSubSections.back().nHeadSide == SAWFIN_HS_RIGHT) { + // allungo il percorso precedente + if ( ! vCurrSubSections.back().pSubSection->AddCurve( ::Release( pCompoSub))) + return false ; + // non posso swappare la testa + bCanSwapHead = false ; + } + else if ( bCanSwapHead) { + // inverto la testa del percorso precedente e mi collego + vCurrSubSections.back().nHeadSide = SAWFIN_HS_RIGHT ; + if ( ! vCurrSubSections.back().pSubSection->AddCurve( ::Release( pCompoSub))) + return false ; + bCanSwapHead = false ; + } + else { + // nuovo percorso + PtrOwner pCompoSubSect( ::Release( pCompoSub)) ; + if ( ! AddSubSection( vCurrSubSections, pCompoSubSect, SAWFIN_HS_RIGHT)) + return false ; + } + } + // se la precedente è fall + else if ( nPrevClass == CCL_FALL) { + // allungo il percorso precedente + if ( ! vCurrSubSections.back().pSubSection->AddCurve( ::Release( pCompoSub))) + return false ; + } + // se la precedente è Rise + else if ( nPrevClass == CCL_RISE) { + // definisco un nuovo percorso + PtrOwner pCompoSubSect( ::Release( pCompoSub)) ; + if ( ! AddSubSection( vCurrSubSections, pCompoSubSect, SAWFIN_HS_RIGHT)) + return false ; + } + break ; + } + // --- classificazione corrente Rise (la testa deve essere a sinistra) + case CCL_RISE : { + // se la precedente è flat, veticale o small + if ( nPrevClass == CCL_FLAT || nPrevClass == CCL_SMALL) { + if ( vCurrSubSections.back().nHeadSide == SAWFIN_HS_LEFT) { + // allungo il percorso precedente + if ( ! vCurrSubSections.back().pSubSection->AddCurve( ::Release( pCompoSub))) + return false ; + // non posso swappare la testa + bCanSwapHead = false ; + } + else if ( bCanSwapHead) { + // inverto la testa del percorso precedente e mi collego + vCurrSubSections.back().nHeadSide = SAWFIN_HS_LEFT ; + if ( ! vCurrSubSections.back().pSubSection->AddCurve( ::Release( pCompoSub))) + return false ; + bCanSwapHead = false ; + } + else { + // nuovo percorso + PtrOwner pCompoSubSect( ::Release( pCompoSub)) ; + if ( ! AddSubSection( vCurrSubSections, pCompoSubSect, SAWFIN_HS_LEFT)) + return false ; + } + } + // se la precedente è fall + else if ( nPrevClass == CCL_FALL) { + // definisco un nuovo percorso + PtrOwner pCompoSubSect( ::Release( pCompoSub)) ; + if ( ! AddSubSection( vCurrSubSections, pCompoSubSect, SAWFIN_HS_LEFT)) + return false ; + } + // se la precedente è Rise + else if ( nPrevClass == CCL_RISE) { + // allungo il percorso precedente + if ( ! vCurrSubSections.back().pSubSection->AddCurve( ::Release( pCompoSub))) + return false ; + } + break ; + } + } + } + } + + return true ; +} + //---------------------------------------------------------------------------- bool SawFinishing::AdaptGuideLineToRaw( Point3d& ptGdStart, Point3d& ptGdEnd, const Vector3d& vtGdDir, bool bExtend, const ISurfTriMesh* pStmRaw) const @@ -2573,7 +2982,7 @@ SawFinishing::CalculateSectElevation( const Polygon3d& pgFacet, double& dElev) c //---------------------------------------------------------------------------- bool -SawFinishing::CalculateGuideLine( int nGuideId, const ICurve* pSect, Point3d& ptGdStart, Point3d& ptGdEnd, Vector3d& vtGdDir, +SawFinishing::CalculateGuideLine( int nGuideId, Point3d& ptGdStart, Point3d& ptGdEnd, Vector3d& vtGdDir, int nSectGrpId, bool bExtendToRawBox) const { // recupero la linea guida @@ -2649,7 +3058,7 @@ SawFinishing::CalculateGuideLine( int nGuideId, const ICurve* pSect, Point3d& pt //---------------------------------------------------------------------------- bool -SawFinishing::ClassifySection( ICurve* pCrv, INTVECTOR& vnClass) +SawFinishing::ClassifySection( ICurve* pCrv, INTVECTOR& vnClass) const { const ICurveComposite* pCompo = GetCurveComposite( pCrv) ; if ( pCompo != nullptr) { @@ -2872,6 +3281,228 @@ SawFinishing::CalcAlongStdCuts( ICurve* pSect, double dUmin, double dUmax, return true ; } +//---------------------------------------------------------------------------- +bool +SawFinishing::CalcCAvSect5Ax( const ICurveComposite* pSubSect, const SECTIONVECTOR& vSections, const Vector3d& vtTool, const Vector3d& vtCorr, + const Point3d& ptP, double& dElev, bool& bOtherCollision) const +{ + // verifico la validità della curva + if ( pSubSect == nullptr || ! pSubSect->IsValid()) + return false ; + bOtherCollision = false ; + + // definisco il numero di punti di campionamento progressivi + const int MAX_NUM = 3 ; + const double LINE_LEN = 1e4 ; + double dSqMaxElev = EPS_ZERO ; + PtrOwner pLineInters( CreateCurveLine()) ; + if ( IsNull( pLineInters) || ! pLineInters->Set( ptP, ptP + vtCorr * LINE_LEN)) + return false ; + for ( int i = 0 ; i < MAX_NUM ; ++ i) { + if ( i > 0) + pLineInters->Translate( ( m_TParams.m_dThick) / ( MAX_NUM - 1) * vtTool) ; + Point3d ptLineS ; pLineInters->GetStartPoint( ptLineS) ; + IntersCurveCurve ICC( *pLineInters, *pSubSect) ; + for ( int j = ICC.GetIntersCount() - 1 ; j >= 0 ; -- j) { + IntCrvCrvInfo aInfo ; + if ( ICC.GetIntCrvCrvInfo( j, aInfo)) + dSqMaxElev = max( dSqMaxElev, ( SqDist( ptLineS, ( aInfo.bOverlap ? aInfo.IciA[1].ptI : aInfo.IciA[0].ptI)))) ; + } + + // controllo se la linea ricavata interseca le sezioni presenti + double dSafeShift = 1. + sqrt( dSqMaxElev) ; + pLineInters->Translate( dSafeShift * vtCorr) ; + for ( int j = 0 ; j < ssize( vSections) ; ++ j) { + if ( vSections[j].pSection != nullptr && vSections[j].pSection->IsValid()) { + IntersCurveCurve ICC( *pLineInters, *vSections[j].pSection) ; + if ( ICC.GetIntersCount() > 0) { + bOtherCollision = true ; + return true ; + } + } + } + pLineInters->Translate( - dSafeShift * vtCorr) ; // ritorno in posizione originale + } + + dElev = sqrt( dSqMaxElev) ; + return true ; +} + +//---------------------------------------------------------------------------- +bool +SawFinishing::CalcAlong5ACuts( const SECTIONVECTOR& vSections, const Frame3d& frSect, bool bExtend, + const Point3d& ptGdStart, const Point3d& ptGdEnd, const Vector3d& vtGdDir, double dDepth, + const BBox3d& b3Raw, const ISurfTriMesh* pStmRaw) +{ + // se non ho sezioni da lavorare, non faccio nulla + if ( vSections.empty()) { + m_pMchMgr->SetWarning( 2752, "Warning in SawFinishing : No machinable path") ; + return true ; + } + + // determino angolo limite di inclinazione dell'utensile + double dAngDegSplit = 60. ; + double dAngUN = 0. ; + if ( GetValInNotes( m_Params.m_sUserNotes, UN_LIMIT_ANG, dAngUN)) + dAngDegSplit = max( 15. - EPS_ANG_SMALL, dAngUN) ; + double dCosToolLimit = cos( dAngDegSplit * DEGTORAD) ; + + // determino quota Zmassima dal Box del grezzo in globale + double dRawMaxZ = b3Raw.GetMax().z ; + + // scorro le sezioni + for ( int nSect = 0 ; nSect < ssize( vSections) ; ++ nSect) { + + // recupero la sezione + const SectionInfo& Section = vSections[nSect] ; + if ( Section.pSection == nullptr || ! Section.pSection->IsValid()) + continue ; + + // scorro le SubSection ottenute + for ( int nSubSect = 0 ; nSubSect < ssize( Section.vSubSections) ; ++ nSubSect) { + + // recupero la SubSection corrente, se non valida, passo alla successiva + const SubSectionInfo& SubSection = Section.vSubSections[nSubSect] ; + PtrOwner pSubSect( CloneCurveComposite( SubSection.pSubSection)) ; + if ( pSubSect == nullptr || ! pSubSect->IsValid()) + continue ; + // essendo l'utensile inclinato e la testa con orientamento forzato è meglio estendere la sezione per evitare lavorazioni + // a filo dei suoi bordi + pSubSect->ExtendStartByLen( 50. * EPS_SMALL) ; + pSubSect->ExtendEndByLen( 50. * EPS_SMALL) ; + + // calcolo della lunghezza dello step e del numero di step complessivi + double dStep = max( m_Params.m_dSideStep, 10. * EPS_SMALL) ; + double dLen ; + if ( ! pSubSect->GetLength( dLen)) + return false ; + double dL = dLen ; + if ( dL < 10. * EPS_SMALL) + return true ; + int nStep = static_cast( ceil( dL / dStep)) ; + dStep = dL / nStep ; + nStep ++ ; + + // Ciclo sulle passate + double dLastElev ; + Point3d ptStartPrev ; + for ( int i = 0 ; i <= nStep ; ++ i) { + // coordinate nel punto, del versore correzione e del versore utensile + double dLcurr = i * dStep ; + double dU ; pSubSect->GetParamAtLength( dLcurr, dU) ; + Point3d ptP ; + Vector3d vtTan, vtPerp ; + if ( ! pSubSect->GetPointD1D2( dU, ICurve::FROM_MINUS, ptP, &vtTan) || ! vtTan.Normalize()) + return false ; + Vector3d vtCorr = GetToGlob( vtTan, frSect) ; + vtCorr.Rotate( vtGdDir, m_Params.m_bInvert ? - ANG_RIGHT : ANG_RIGHT) ; + Vector3d vtTool = GetToGlob( vtTan, frSect) ; + // determino la posizione della testa + if ( vtTool.z < - EPS_ZERO) + vtTool.Invert() ; // NB. vtTool deve sempre avere la componente Z come positiva + + // se lama più inclinata della soglia, passo allo step successivo + if ( vtCorr * Z_AX < dCosToolLimit) + continue ; + + // verifico posizionando la lama nel punto corrente se devo sposarmi per evitare collisione con la sezione + double dSectElev = 0. ; + bool bOtherColl = false ; + if ( ! CalcCAvSect5Ax( pSubSect, vSections, GetToLoc( vtTool, frSect), GetToLoc( vtCorr, frSect), ptP, dSectElev, bOtherColl)) { + m_pMchMgr->SetLastError( 2708, "Error in SawFinishing : GetElevation") ; + return false ; + } + // se collisione con altra sezione, passo allo step successivo ( non inserisco la passata corrente) + if ( bOtherColl) + continue ; + + // definisco gli estremi del tratto rettilineo in globale + Point3d ptPCAvSect = GetToGlob( ptP, frSect) + vtCorr * dSectElev ; + Point3d ptStart = ptPCAvSect ; + Point3d ptEnd = ptPCAvSect + vtGdDir * ( Dist( ptGdStart, ptGdEnd)) ; + + // verifico eventuale estensione degli estremi sul grezzo + if ( bExtend) { + if ( ! AdaptGuideLineToRaw( ptStart, ptEnd, vtGdDir, bExtend, pStmRaw)) + return false ; + } + + // determino l'elevazione del taglio (considerando l'elevazione verticiale) + Vector3d vtThick = vtTool * m_TParams.m_dThick ; + double dElev, dElev2 ; + if ( ! GetElevation( m_nPhase, ptStart, ptEnd, Z_AX, dElev) || + ! GetElevation( m_nPhase, ptStart + vtThick, ptEnd + vtThick, Z_AX, dElev2)) { + m_pMchMgr->SetLastError( 2708, "Error in SawFinishing : GetElevation") ; + return false ; + } + dElev = max( dElev, dElev2) ; + if ( dElev < EPS_SMALL) + dElev = max( max( dRawMaxZ - ptStart.z, dRawMaxZ - ptEnd.z), 0.) ; + // la confronto con il massimo affondamento e con la massima lavorazione della lama. Se sotto alla massima Depth allora riporto + // l'utensile in verticale e ricalcolo il punto più adatto in z per il tratto lineare + double dMaxDepth = min( dDepth, m_TParams.m_dMaxMat) ; + if ( dElev > dMaxDepth) { + // modifica versore correzione e direzione utensile + vtTool.z = 0. ; + if ( ! vtTool.Normalize()) + return false ; + vtCorr = Z_AX ; + double dElevTmp = 0. ; + if ( ! CalcCAvSect5Ax( pSubSect, vSections, GetToLoc( vtTool, frSect), GetToLoc( vtCorr, frSect), ptP, dElevTmp, bOtherColl)) { + m_pMchMgr->SetLastError( 2708, "Error in SawFinishing : GetElevation") ; + return false ; + } + if ( bOtherColl) + continue ; + ptPCAvSect = GetToGlob( ptP, frSect) + vtCorr * dElevTmp ; + double dElev3 ; + if ( ! GetElevation( m_nPhase, ptPCAvSect, vtTool, m_TParams.m_dDiam / 2., m_TParams.m_dLen, vtCorr, dElev3)) { + m_pMchMgr->SetLastError( 2708, "Error in SawFinishing : GetElevation") ; + return false ; + } + Point3d ptPTopRaw = ptPCAvSect + ( dElev3 - m_TParams.m_dDiam / 2.) * vtCorr ; + ptStart.z = max( ptPTopRaw.z - dMaxDepth, ptPCAvSect.z) ; + ptEnd.z = max( ptPTopRaw.z - dMaxDepth, ptPCAvSect.z) ; + dElev = dMaxDepth ; + } + + // Imposto dati comune + SetToolDir( vtTool) ; + SetCorrAuxDir( vtCorr) ; + // se diverso da precedente, eseguo il taglio + bool bFirst = ( i == 0) ; + dElev = dElev / max( EPS_SMALL, ( vtCorr * Z_AX)) ; // non serve fare il max, ma meglio prevenire... + if ( bFirst || ! AreSamePointEpsilon( ptStart, ptStartPrev, 10. * EPS_SMALL)) { + if ( m_Params.m_nStepType != SAWFIN_ST_ZIGZAG) { + if ( ! CalcAlongOneWayCut( ptStart, ptEnd, vtGdDir, vtTool, vtCorr, dElev, bExtend, false)) + return false ; + } + else { + if ( ! CalcAlongZigZagCut( ptStart, ptEnd, vtGdDir, vtTool, vtCorr, dElev, bExtend, false, bFirst)) + return false ; + } + // salvo dati correnti + dLastElev = dElev ; + ptStartPrev = ptStart ; + } + } + // se ZigZag e non centrato aggiungo risalita + if ( m_Params.m_nStepType == SAWFIN_ST_ZIGZAG && m_Params.m_nLeadLinkType != SAWFIN_LL_CENT) { + // recupero distanza di sicurezza + double dSafeZ = GetSafeZ() ; + // lunghezza di approccio/retrazione + double dAppr = m_Params.m_dStartPos ; + // aggiungo retrazione + Point3d ptNewEnd ; + if ( ! GetCurrPos( ptNewEnd) || ! AddRetract( ptNewEnd, Z_AX, dSafeZ, dLastElev, dAppr)) + return false ; + } + } + } + + return true ; +} + //---------------------------------------------------------------------------- bool SawFinishing::CalcAlongOneWayCut( const Point3d& ptStart, const Point3d& ptEnd, const Vector3d& vtDir, diff --git a/SawFinishing.h b/SawFinishing.h index 931a9ea..fd65fc6 100644 --- a/SawFinishing.h +++ b/SawFinishing.h @@ -25,6 +25,22 @@ class ICurveComposite ; class ISurfTriMesh ; class Polygon3d ; +// Definizione strutture per Section/SubSection ------------------------------ +struct SubSectionInfo { + PtrOwner pSubSection ; + int nHeadSide ; + SubSectionInfo() + : pSubSection( nullptr), nHeadSide( -1) {} ; +} ; +typedef std::vector SUBSECTIONVECTOR ; +struct SectionInfo { + PtrOwner pSection ; + SUBSECTIONVECTOR vSubSections ; + SectionInfo() + : pSection( nullptr), vSubSections( SUBSECTIONVECTOR{}) {} ; +} ; +typedef std::vector SECTIONVECTOR ; + //---------------------------------------------------------------------------- class SawFinishing : public Machining { @@ -86,22 +102,29 @@ class SawFinishing : public Machining bool CalculateCurvedAlongToolPath( int nAuxId, int nClId) ; bool CalculateCurvedAcrossToolPath( int nAuxId, int nClId) ; bool CalculateStraightSectionToolPath( int nAuxId, int nClId) ; + bool CalculateStraightAlong5AToolPath( int nAuxId, int nClId) ; bool TrimVerticalSectionBounds( ICurveComposite* pSect, double dAngDeg) const ; bool CalculateOffsetSection( int nSectGrpId, const Frame3d& frSec, ICurve*& pSect) const ; bool CalculateSection( int nSectGrpId, ICurve*& pSect) const ; bool TrimSection( ICurve* pCrv) const ; + bool Split5ASection( int nSectGrpId, SECTIONVECTOR& vSubSections) const ; bool AdaptGuideLineToRaw( Point3d& ptGdStart, Point3d& ptGdEnd, const Vector3d& vtGdDir, bool bExtend, const ISurfTriMesh* pStmRaw) const ; bool ExtendPathToRaw( PolyLine& PL, const ISurfTriMesh* pStmRaw, double dExtraOut) const ; bool CalculateSectElevation( const Polygon3d& pgFacet, double& dElev) const ; - bool CalculateGuideLine( int nGuideId, const ICurve* pSect, Point3d& ptGdStart, Point3d& ptGdEnd, Vector3d& vtGdDir, + bool CalculateGuideLine( int nGuideId, Point3d& ptGdStart, Point3d& ptGdEnd, Vector3d& vtGdDir, int nSectGrpId, bool bExtendToRawBox) const ; - bool ClassifySection( ICurve* pCrv, INTVECTOR& vnClass) ; + bool ClassifySection( ICurve* pCrv, INTVECTOR& vnClass) const ; + bool CalcCAvSect5Ax( const ICurveComposite* pSubSect, const SECTIONVECTOR& vSections, const Vector3d& vtTool, const Vector3d& vtCorr, const Point3d& ptP, + double& dElev, bool& bOtherCollision) const ; bool CalcAlongVerticalCuts( ICurve* pSect, int nUmin, int nUmax, const Frame3d& frSect, bool bExtend, const Point3d& ptGdStart, const Point3d& ptGdEnd, const Vector3d& vtGdDir, double dDepth, double dRaxMaxZ, const Vector3d& vtTool, const Vector3d& vtCorr, const ISurfTriMesh* pStmRaw) ; bool CalcAlongStdCuts( ICurve* pSect, double dUmin, double dUmax, bool bSkipMin, bool bSkipMax, bool bInvert, const Frame3d& frSect, bool bExtend, const Point3d& ptGdStart, const Point3d& ptGdEnd, const Vector3d& vtGdDir, double dDepth, double dRawMaxZ, const Vector3d& vtTool, const Vector3d& vtCorr, const ISurfTriMesh* pStmRaw) ; + bool CalcAlong5ACuts( const SECTIONVECTOR& vSections, const Frame3d& frSect, bool bExtend, + const Point3d& ptGdStart, const Point3d& ptGdEnd, const Vector3d& vtGdDir, double dDepth, + const BBox3d& b3Raw, const ISurfTriMesh* pStmRaw) ; bool CalcAlongOneWayCut( const Point3d& ptStart, const Point3d& ptEnd, const Vector3d& vtDir, const Vector3d& vtTool, const Vector3d& vtCorr, double dElev, bool bExtend, bool bVert) ; bool CalcAlongZigZagCut( const Point3d& ptStart, const Point3d& ptEnd, const Vector3d& vtDir, diff --git a/SawFinishingData.cpp b/SawFinishingData.cpp index 7ac6cbb..bef1002 100644 --- a/SawFinishingData.cpp +++ b/SawFinishingData.cpp @@ -391,7 +391,8 @@ SawFinishingData::VerifyStepType( int nVal) const bool SawFinishingData::VerifySubType( int nVal) const { - return ( nVal == SAWFIN_SUB_ALONG || nVal == SAWFIN_SUB_ACROSS) ; + return ( nVal == SAWFIN_SUB_ALONG || nVal == SAWFIN_SUB_ACROSS || nVal == SAWFIN_SUB_SECTION || + nVal == SAWFIN_SUB_ALONG_5A) ; } //----------------------------------------------------------------------------