//---------------------------------------------------------------------------- // EgalTech 2017-2022 //---------------------------------------------------------------------------- // File : Pocketing.cpp Data : 24.08.22 Versione : 2.4h2 // Contenuto : Implementazione gestione svuotature. // // // // Modifiche : 04.02.17 DS Creazione modulo. // 24.02.22 DS Corretta ed estesa VerifyPathFromBottom. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "MachMgr.h" #include "DllMain.h" #include "Pocketing.h" #include "OperationConst.h" #include "MachiningConst.h" #include "GeoConst.h" #include "/EgtDev/Include/EGkCurveLine.h" #include "/EgtDev/Include/EGkCurveArc.h" #include "/EgtDev/Include/EGkBiArcs.h" #include "/EgtDev/Include/EGkArcSpecial.h" #include "/EgtDev/Include/EGkChainCurves.h" #include "/EgtDev/Include/EGkOffsetCurve.h" #include "/EgtDev/Include/EGkCurveAux.h" #include "/EgtDev/Include/EGkSfrCreate.h" #include "/EgtDev/Include/EGkSurfTriMesh.h" #include "/EgtDev/Include/EGkExtText.h" #include "/EgtDev/Include/EGkCurveLocal.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EGkUserObjFactory.h" #include "/EgtDev/Include/EGkIntervals.h" #include "/EgtDev/Include/EGkStringUtils3d.h" #include "/EgtDev/Include/EGnStringKeyVal.h" #include "/EgtDev/Include/EgtPointerOwner.h" #include "/EgtDev/Include/EgtNumUtils.h" #include "/EgtDev/Include/EGkLinePntMinDistCurve.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EGkMedialAxis.h" #include "/EgtDev/Include/EGkFilletChamfer.h" #include "/EgtDev/Include/EGkCurveBezier.h" #include "/EgtDev/Include/EGkGeoPoint3d.h" #include "/EgtDev/Include/EGkGeoVector3d.h" #include "/EgtDev/Include/EGkIntersCurves.h" #include "/EgtDev/Include/EGkLinePntTgCurve.h" #include "/EgtDev/Include/EGkSfrCreate.h" #include "/EgtDev/Include/EGkStmStandard.h" #include "/EgtDev/Include/EGkIntersPlaneSurfTm.h" #include "/EgtDev/Include/EGkDistPointSurfTm.h" #include "/EgtDev/Include/EGkStmFromCurves.h" #include "/EgtDev/Include/EGkStmFromTriangleSoup.h" #include using namespace std ; //------------------------------ Errors -------------------------------------- // 2401 = "Error in Pocketing : UpdateToolData failed" // 2402 = "Error in Pocketing : Open Contour" // 2403 = "Error in Pocketing : Contour Not Flat" // 2404 = "Error in Pocketing : Tool Not Perpendicular to Flat Area" // 2405 = "Error in Pocketing : Empty RawBox" // 2406 = "Error in Pocketing : Depth not computable" // 2408 = "Error in Pocketing : Entity GetElevation" // 2409 = "Error in Pocketing : missing aggregate from bottom" // 2410 = "Error in Pocketing : path too far from part sides" // 2411 = "Error in Pocketing : toolpath allocation failed" // 2412 = "Error in Pocketing : Offset not computable" // 2413 = "Error in Pocketing : Toolpath not computable" // 2414 = "Error in Pocketing : Approach not computable" // 2415 = "Error in Pocketing : LeadIn not computable" // 2416 = "Error in Pocketing : LeadOut not computable" // 2417 = "Error in Pocketing : Retract not computable" // 2418 = "Error in Pocketing : Link not computable" // 2419 = "Error in Pocketing : Linear Approx not computable" // 2420 = "Error in Pocketing : Return toolpath not computable" // 2421 = "Error in Pocketing : Chaining failed" // 2422 = "Error in Pocketing : Tool MaxMaterial too small (xxx)" // 2423 = "Error in Pocketing : axes values not calculable" // 2424 = "Error in Pocketing : outstroke xxx" // 2425 = "Error in Pocketing : link movements not calculable" // 2426 = "Error in Pocketing : link outstroke xxx" // 2427 = "Error in Pocketing : post apply not calculable" // 2428 = "Error in Pocketing : Tool loading failed" // 2429 = "Error in Pocketing : machining depth (xxx) bigger than MaxDepth (yyy)" // 2430 = "Error in Pocketing : adjust open edges failed" // 2431 = "Error in Pocketing : LeadIn with Mill NoTip in material" // 2432 = "Error in Pocketing : Mirror for Double calculation failed" // 2451 = "Warning in Pocketing : Skipped entity (xx)" // 2452 = "Warning in Pocketing : No machinable pocket" // 2453 = "Warning in Pocketing : Tool name changed (xx)" // 2454 = "Warning in Pocketing : Tool data changed (xx)" // 2455 = "Warning in Pocketing : skipped Path too short" // 2456 = "Warning in Pocketing : machining step too small (xx)" // 2457 = "Warning in Pocketing : machining step (xxx) bigger than MaxMaterial (yyy)" // 2458 = "Warning in Pocketing : machining depth (xxx) bigger than MaxMaterial (yyy)" //---------------------------------------------------------------------------- static string KEY_OPEN = "OPEN" ; static string KEY_TOOL = "VTTOOL" ; static int LINK_CURVE_PROP = -3 ; static double FEED_DIVISOR = 1000.0 ; static double TOLL_TRAPEZOID = 50 * EPS_SMALL ; //---------------------------------------------------------------------------- USEROBJ_REGISTER( GetOperationClass( OPER_POCKETING), Pocketing) ; //---------------------------------------------------------------------------- const string& Pocketing::GetClassName( void) const { return USEROBJ_GETNAME( Pocketing) ; } //---------------------------------------------------------------------------- Pocketing* Pocketing::Clone( void) const { // alloco oggetto Pocketing* pPock = new(nothrow) Pocketing ; // eseguo copia dei dati if ( pPock != nullptr) { try { pPock->m_vId = m_vId ; pPock->m_pMchMgr = m_pMchMgr ; pPock->m_nPhase = m_nPhase ; pPock->m_Params = m_Params ; pPock->m_TParams = m_TParams ; pPock->m_dTHoldBase = m_dTHoldBase ; pPock->m_dTHoldLen = m_dTHoldLen ; pPock->m_dTHoldDiam = m_dTHoldDiam ; pPock->m_nStatus = m_nStatus ; pPock->m_nPockets = m_nPockets ; pPock->m_bTiltingTab = m_bTiltingTab ; pPock->m_vtTiltingAx = m_vtTiltingAx ; pPock->m_bAboveHead = m_bAboveHead ; pPock->m_bAggrBottom = m_bAggrBottom ; pPock->m_bOpenOutRaw = m_bOpenOutRaw ; pPock->m_dOpenMinSafe = m_dOpenMinSafe ; } catch( ...) { delete pPock ; return nullptr ; } } // ritorno l'oggetto return pPock ; } //---------------------------------------------------------------------------- bool Pocketing::Dump( string& sOut, bool bMM, const char* szNewLine) const { sOut += GetClassName() + "[mm]" + szNewLine ; sOut += KEY_PHASE + EQUAL + ToString( m_nPhase) + szNewLine ; sOut += KEY_IDS + EQUAL + ToString( m_vId) + szNewLine ; for ( int i = 0 ; i < m_Params.GetSize() ; ++ i) sOut += m_Params.ToString( i) + szNewLine ; for ( int i = 0 ; i < m_TParams.GetSize() ; ++ i) sOut += m_TParams.ToString( i) + szNewLine ; sOut += KEY_NUM + EQUAL + ToString( m_nPockets) + szNewLine ; sOut += KEY_STAT + EQUAL + ToString( m_nStatus) + szNewLine ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::Save( int nBaseId, STRVECTOR& vString) const { try { int nSize = 1 + m_Params.GetSize() + m_TParams.GetSize() + 3 ; vString.insert( vString.begin(), nSize, "") ; int k = - 1 ; if ( ! SetVal( KEY_IDS, m_vId, vString[++k])) return false ; for ( int i = 0 ; i < m_Params.GetSize() ; ++ i) { string sParam = m_Params.ToString( i) ; if ( ! sParam.empty()) vString[++k] = sParam ; } for ( int i = 0 ; i < m_TParams.GetSize() ; ++ i) vString[++k] = m_TParams.ToString( i) ; if ( ! SetVal( KEY_PHASE, m_nPhase, vString[++k])) return false ; if ( ! SetVal( KEY_NUM, m_nPockets, vString[++k])) return false ; if ( ! SetVal( KEY_STAT, m_nStatus, vString[++k])) return false ; vString.resize( k + 1) ; } catch( ...) { return false ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::Load( const STRVECTOR& vString, int nBaseGdbId) { int nSize = int( vString.size()) ; // lista identificativi geometrie da lavorare int k = - 1 ; if ( k >= nSize - 1 || ! GetVal( vString[++k], KEY_IDS, m_vId)) return false ; for ( auto& Sel : m_vId) Sel.nId += nBaseGdbId ; // parametri lavorazione for ( int i = 0 ; i < m_Params.GetSize() ; ++ i) { int nKey ; if ( k >= nSize - 1 || ! m_Params.FromString( vString[++k], nKey) || nKey != i) { if ( m_Params.IsOptional( i)) -- k ; else return false ; } } // parametri utensile for ( int i = 0 ; i < m_TParams.GetSize() ; ++ i) { int nKey ; if ( k >= nSize - 1 || ! m_TParams.FromString( vString[++k], nKey) || nKey != i) return false ; } // parametri di stato while ( k < nSize - 1) { // separo chiave da valore string sKey, sVal ; SplitFirst( vString[++k], "=", sKey, sVal) ; // leggo if ( sKey == KEY_PHASE) { if ( ! FromString( sVal, m_nPhase)) return false ; } else if ( sKey == KEY_NUM) { if ( ! FromString( sVal, m_nPockets)) return false ; } else if ( sKey == KEY_STAT) { if ( ! FromString( sVal, m_nStatus)) return false ; } } return true ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- Pocketing::Pocketing( void) { m_Params.m_sName = "*" ; m_Params.m_sToolName = "*" ; m_TParams.m_sName = "*" ; m_TParams.m_sHead = "*" ; m_dTHoldBase = 0 ; m_dTHoldLen = 0 ; m_dTHoldDiam = 0 ; m_dMaxHelixRad = INFINITO ; m_nStatus = MCH_ST_TO_VERIFY ; m_nPockets = 0 ; m_bTiltingTab = false ; m_bAboveHead = true ; m_bAggrBottom = false ; m_bOpenOutRaw = false ; m_dOpenMinSafe = 0 ; } //---------------------------------------------------------------------------- bool Pocketing::Prepare( const string& sMillName) { // verifico il gestore lavorazioni if ( m_pMchMgr == nullptr) return false ; // recupero il gestore DB utensili della macchina corrente ToolsMgr* pTMgr = m_pMchMgr->GetCurrToolsMgr() ; if ( pTMgr == nullptr) return false ; // recupero il gestore DB lavorazioni della macchina corrente MachiningsMgr* pMMgr = m_pMchMgr->GetCurrMachiningsMgr() ; if ( pMMgr == nullptr) return false ; // ricerca della lavorazione di libreria con il nome indicato const PocketingData* pDdata = GetPocketingData( pMMgr->GetMachining( sMillName)) ; if ( pDdata == nullptr) return false ; m_Params = *pDdata ; // ricerca dell'utensile usato dalla lavorazione const ToolData* pTdata = pTMgr->GetTool( m_Params.m_ToolUuid) ; if ( pTdata == nullptr) return false ; m_TParams = *pTdata ; m_Params.m_sToolName = m_TParams.m_sName ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::SetParam( int nType, bool bVal) { switch ( nType) { case MPA_INVERT : if ( bVal != m_Params.m_bInvert) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_bInvert = bVal ; return true ; case MPA_TOOLINVERT : if ( bVal != m_Params.m_bToolInvert) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_bToolInvert = bVal ; return true ; } return false ; } //---------------------------------------------------------------------------- bool Pocketing::SetParam( int nType, int nVal) { switch ( nType) { case MPA_LEADINTYPE : if ( ! m_Params.VerifyLeadInType( nVal)) return false ; if ( nVal != m_Params.m_nLeadInType) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_nLeadInType = nVal ; return true ; case MPA_LEADOUTTYPE : if ( ! m_Params.VerifyLeadOutType( nVal)) return false ; if ( nVal != m_Params.m_nLeadOutType) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_nLeadOutType = nVal ; return true ; case MPA_SCC : if ( ! m_Params.VerifySolCh( nVal)) return false ; if ( nVal != m_Params.m_nSolCh) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_nSolCh = nVal ; return true ; case MPA_SUBTYPE : if ( ! m_Params.VerifySubType( nVal)) return false ; if ( nVal != m_Params.m_nSubType) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_nSubType = nVal ; return true ; } return false ; } //---------------------------------------------------------------------------- bool Pocketing::SetParam( int nType, double dVal) { switch ( nType) { case MPA_SPEED : if ( ! m_TParams.VerifySpeed( dVal)) return false ; if ( abs( m_TParams.m_dSpeed - dVal) < EPS_MACH_ANG_PAR) dVal = 0 ; if ( abs( dVal - m_Params.m_dSpeed) > EPS_MACH_ANG_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dSpeed = dVal ; return true ; case MPA_FEED : if ( abs( m_TParams.m_dFeed - dVal) < EPS_MACH_LEN_PAR) dVal = 0 ; if ( abs( dVal - m_Params.m_dFeed) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dFeed = dVal ; return true ; case MPA_STARTFEED : if ( abs( m_TParams.m_dStartFeed - dVal) < EPS_MACH_LEN_PAR) dVal = 0 ; if ( abs( dVal - m_Params.m_dStartFeed) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dStartFeed = dVal ; return true ; case MPA_ENDFEED : if ( abs( m_TParams.m_dEndFeed - dVal) < EPS_MACH_LEN_PAR) dVal = 0 ; if ( abs( dVal - m_Params.m_dEndFeed) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dEndFeed = dVal ; return true ; case MPA_TIPFEED : if ( abs( m_TParams.m_dTipFeed - dVal) < EPS_MACH_LEN_PAR) dVal = 0 ; if ( abs( dVal - m_Params.m_dTipFeed) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dTipFeed = dVal ; return true ; case MPA_OFFSR : if ( abs( m_TParams.m_dOffsR - dVal) < EPS_MACH_LEN_PAR) dVal = UNKNOWN_PAR ; if ( abs( dVal - m_Params.m_dOffsR) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dOffsR = dVal ; return true ; case MPA_OFFSL : if ( abs( m_TParams.m_dOffsL - dVal) < EPS_MACH_LEN_PAR) dVal = UNKNOWN_PAR ; if ( abs( dVal - m_Params.m_dOffsL) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dOffsL = dVal ; return true ; case MPA_DEPTH : { string sVal = ToString( dVal) ; if ( sVal != m_Params.m_sDepth) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sDepth = sVal ; } return true ; case MPA_STARTPOS : if ( abs( dVal - m_Params.m_dStartPos) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dStartPos = dVal ; return true ; case MPA_STEP : if ( abs( dVal - m_Params.m_dStep) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dStep = dVal ; return true ; case MPA_SIDESTEP : if ( abs( dVal - m_Params.m_dSideStep) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dSideStep = dVal ; return true ; case MPA_SIDEANGLE : if ( abs( dVal - m_Params.m_dSideAngle) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dSideAngle = dVal ; return true ; case MPA_LITANG : if ( abs( dVal - m_Params.m_dLiTang) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dLiTang = dVal ; return true ; case MPA_LIELEV : if ( abs( dVal - m_Params.m_dLiElev) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dLiElev = dVal ; return true ; case MPA_LOTANG : if ( abs( dVal - m_Params.m_dLoTang) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dLoTang = dVal ; return true ; case MPA_EPICYCLESRAD : if ( abs( dVal - m_Params.m_dEpicyclesRad) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dEpicyclesRad = dVal ; return true ; case MPA_EPICYCLESDIST : if ( abs( dVal - m_Params.m_dEpicyclesDist) > EPS_MACH_LEN_PAR) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_dEpicyclesDist = dVal ; return true ; } return false ; } //---------------------------------------------------------------------------- bool Pocketing::SetParam( int nType, const string& sVal) { switch ( nType) { case MPA_TOOL : { const ToolData* pTdata ; if ( ! m_Params.VerifyTool( m_pMchMgr->GetCurrToolsMgr(), sVal, pTdata)) return false ; if ( ! SameTool( m_TParams, *pTdata)) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sToolName = sVal ; m_Params.m_ToolUuid = pTdata->m_Uuid ; m_TParams = *pTdata ; } return true ; case MPA_DEPTH_STR : if ( sVal != m_Params.m_sDepth) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sDepth = sVal ; return true ; case MPA_SYSNOTES : if ( sVal != m_Params.m_sSysNotes) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sSysNotes = sVal ; return true ; case MPA_USERNOTES : if ( sVal != m_Params.m_sUserNotes) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sUserNotes = sVal ; return true ; case MPA_INITANGS : if ( sVal != m_Params.m_sInitAngs) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sInitAngs = sVal ; return true ; case MPA_BLOCKEDAXIS : if ( sVal != m_Params.m_sBlockedAxis) m_nStatus |= MCH_ST_PARAM_MODIF ; m_Params.m_sBlockedAxis = sVal ; return true ; } return false ; } //---------------------------------------------------------------------------- bool Pocketing::SetGeometry( const SELVECTOR& vIds) { // verifico validità gestore DB geometrico if ( m_pGeomDB == nullptr) return false ; // reset della geometria corrente m_vId.clear() ; // verifico che gli identificativi rappresentino delle entità ammissibili (tutte curve o tutte facce) int nType = GEO_NONE ; for ( const auto& Id : vIds) { // test sull'entità int nSubs ; if ( ! VerifyGeometry( Id, nSubs, nType)) { string sInfo = "Warning in Pocketing : Skipped entity " + ToString( Id) ; m_pMchMgr->SetWarning( 2451, sInfo) ; continue ; } // posso aggiungere alla lista m_vId.emplace_back( Id) ; } // aggiorno lo stato m_nStatus |= MCH_ST_GEO_MODIF ; // restituisco presenza geometria da lavorare return ( ! m_vId.empty() || vIds.empty()) ; } //---------------------------------------------------------------------------- bool Pocketing::Preview( bool bRecalc) { // reset numero percorsi di svuotatura generati m_nPockets = 0 ; // verifico validità gestore DB geometrico e Id del gruppo if ( m_pGeomDB == nullptr || ! m_pGeomDB->ExistsObj( m_nOwnerId)) return false ; // recupero gruppo per geometria ausiliaria int nAuxId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_AUX) ; bool bChain = false ; // se non c'è, lo aggiungo if ( nAuxId == GDB_ID_NULL) { nAuxId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ; if ( nAuxId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nAuxId, MCH_AUX) ; m_pGeomDB->SetStatus( nAuxId, GDB_ST_OFF) ; bChain = true ; } // altrimenti, se chiesto ricalcolo, lo svuoto else if ( bRecalc) { m_pGeomDB->EmptyGroup( nAuxId) ; bChain = true ; } // aggiorno dati geometrici dell'utensile if ( ! UpdateToolData()) { m_pMchMgr->SetLastError( 2401, "Error in Pocketing : UpdateToolData failed") ; return false ; } // rendo corrente l'utensile usato nella lavorazione if ( ! m_pMchMgr->SetCalcTool( m_TParams.m_sName, m_TParams.m_sHead, m_TParams.m_nExit)) { m_pMchMgr->SetLastError( 2428, "Error in Pocketing : Tool loading failed") ; return false ; } // recupero i dati del portautensile int nToolId = m_pMchMgr->GetCalcTool() ; m_dTHoldBase = 0 ; m_pGeomDB->GetInfo( nToolId, TTH_BASE, m_dTHoldBase) ; m_dTHoldLen = 0 ; m_pGeomDB->GetInfo( nToolId, TTH_LEN, m_dTHoldLen) ; m_dTHoldDiam = 0 ; m_pGeomDB->GetInfo( nToolId, TTH_DIAM, m_dTHoldDiam) ; // se necessario, eseguo concatenamento ed inserisco i percorsi sotto la geometria ausiliaria if ( bChain && ! Chain( nAuxId)) { m_pMchMgr->SetLastError( 2421, "Error in Pocketing : Chaining failed") ; return false ; } // recupero gruppo per geometria di Preview int nPvId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_PV) ; // se non c'è, lo aggiungo if ( nPvId == GDB_ID_NULL) { nPvId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ; if ( nPvId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nPvId, MCH_PV) ; } // altrimenti lo svuoto else m_pGeomDB->EmptyGroup( nPvId) ; // lavoro ogni singola catena int nPathId = m_pGeomDB->GetFirstGroupInGroup( nAuxId) ; while ( nPathId != GDB_ID_NULL) { if ( ! ProcessPath( nPathId, nPvId, GDB_ID_NULL)) return false ; nPathId = m_pGeomDB->GetNextGroup( nPathId) ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::Apply( bool bRecalc, bool bPostApply) { // reset numero percorsi di svuotatura generati int nCurrPockets = m_nPockets ; m_nPockets = 0 ; // reset raggio massimo attacco ad elica nel caso di cerchi m_dMaxHelixRad = INFINITO ; // verifico validità gestore DB geometrico e Id del gruppo if ( m_pGeomDB == nullptr || ! m_pGeomDB->ExistsObj( m_nOwnerId)) return false ; // aggiorno dati geometrici dell'utensile bool bToolChanged = true ; if ( ! UpdateToolData( &bToolChanged)) { m_pMchMgr->SetLastError( 2401, "Error in Pocketing : UpdateToolData failed") ; return false ; } // verifico se necessario continuare nell'aggiornamento if ( !bRecalc && !bToolChanged && ( m_nStatus == MCH_ST_OK || ( !bPostApply && m_nStatus == MCH_ST_NO_POSTAPPL))) { // confermo i percorsi di lavorazione m_nPockets = nCurrPockets ; LOG_DBG_INFO( GetEMkLogger(), "Pocketing apply skipped : status already ok") ; // eseguo aggiornamento assi macchina e collegamento con operazione precedente if ( ! Update( bPostApply)) return false ; LOG_DBG_INFO( GetEMkLogger(), "Update done") ; // esco con successo return true ; } m_nStatus = MCH_ST_TO_VERIFY ; // recupero gruppo per geometria ausiliaria int nAuxId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_AUX) ; bool bChain = false ; // se non c'è, lo aggiungo if ( nAuxId == GDB_ID_NULL) { nAuxId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ; if ( nAuxId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nAuxId, MCH_AUX) ; m_pGeomDB->SetStatus( nAuxId, GDB_ST_OFF) ; bChain = true ; } // altrimenti, se chiesto ricalcolo, lo svuoto else if ( bRecalc) { m_pGeomDB->EmptyGroup( nAuxId) ; bChain = true ; } // rendo corrente l'utensile usato nella lavorazione if ( ! m_pMchMgr->SetCalcTool( m_TParams.m_sName, m_TParams.m_sHead, m_TParams.m_nExit)) { m_pMchMgr->SetLastError( 2428, "Error in Pocketing : Tool loading failed") ; return false ; } // recupero i dati del portautensile int nToolId = m_pMchMgr->GetCalcTool() ; m_dTHoldBase = 0 ; m_pGeomDB->GetInfo( nToolId, TTH_BASE, m_dTHoldBase) ; m_dTHoldLen = 0 ; m_pGeomDB->GetInfo( nToolId, TTH_LEN, m_dTHoldLen) ; m_dTHoldDiam = 0 ; m_pGeomDB->GetInfo( nToolId, TTH_DIAM, m_dTHoldDiam) ; // recupero gruppo per geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_CL) ; // se non c'è, lo aggiungo if ( nClId == GDB_ID_NULL) { nClId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ; if ( nClId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nClId, MCH_CL) ; } // altrimenti lo svuoto else m_pGeomDB->EmptyGroup( nClId) ; // elimino eventuale gruppo geometria simmetrica per lavorazione in doppio int nDblId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_DBL) ; if ( nDblId != GDB_ID_NULL) { m_pGeomDB->Erase( nDblId) ; nDblId = GDB_ID_NULL ; } // se necessario, eseguo concatenamento ed inserisco i percorsi sotto la geometria ausiliaria if ( bChain && ! Chain( nAuxId)) { m_pMchMgr->SetLastError( 2421, "Error in Pocketing : Chaining failed") ; return false ; } // lavoro ogni singola catena bool bOk = true ; int nPathId = m_pGeomDB->GetFirstGroupInGroup( nAuxId) ; while ( nPathId != GDB_ID_NULL) { if ( ! ProcessPath( nPathId, GDB_ID_NULL, nClId)) bOk = false ; nPathId = m_pGeomDB->GetNextGroup( nPathId) ; } if ( ! bOk) return false ; // assegno ingombri dei vari percorsi di lavorazione e della lavorazione nel suo complesso CalcAndSetBBox( nClId) ; // eseguo aggiornamento assi macchina e collegamento con operazione precedente if ( ! Update( bPostApply)) return false ; // se lavorazione in doppio, aggiungo geometria della parte simmetrica if ( ! CalcMirrorByDouble( nClId, m_Params.m_sUserNotes)) { m_pMchMgr->SetLastError( 2432, "Error in Pocketing : Mirror for Double calculation failed") ; return false ; } // aggiorno stato della lavorazione m_nStatus = ( bPostApply ? MCH_ST_OK : MCH_ST_NO_POSTAPPL) ; // dichiaro successiva da aggiornare UpdateFollowingOperationsStatus( MCH_ST_OTH_MODIF) ; LOG_DBG_INFO( GetEMkLogger(), "Pocketing apply done") ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::Update( bool bPostApply) { // verifico validità gestore DB geometrico e Id del gruppo if ( m_pGeomDB == nullptr || ! m_pGeomDB->ExistsObj( m_nOwnerId)) return false ; // se lavorazione vuota, esco if ( m_nPockets == 0) { m_pMchMgr->SetWarning( 2452, "Warning in Pocketing : No machinable pocket") ; return true ; } // elimino le entità CLIMB, RISE e HOME della lavorazione, potrebbero falsare i calcoli degli assi (in ogni casi vengono riaggiunte dopo) RemoveClimbRiseHome() ; // imposto eventuale asse bloccato da lavorazione SetBlockedRotAxis( m_Params.m_sBlockedAxis) ; // calcolo gli assi macchina string sHint = ExtractHint( m_Params.m_sUserNotes) ; if ( ! m_Params.m_sInitAngs.empty()) sHint = m_Params.m_sInitAngs ; if ( ! CalculateAxesValues( sHint)) { string sInfo = m_pMchMgr->GetOutstrokeInfo() ; if ( sInfo.empty()) m_pMchMgr->SetLastError( 2423, "Error in Pocketing : axes values not calculable") ; else m_pMchMgr->SetLastError( 2424, "Error in Pocketing : outstroke ") ; return false ; } // gestione movimenti all'inizio di ogni singolo percorso di lavorazione e alla fine della lavorazione if ( ! AdjustStartEndMovements()) { string sInfo = m_pMchMgr->GetOutstrokeInfo() ; if ( sInfo.empty()) m_pMchMgr->SetLastError( 2425, "Error in Pocketing : link movements not calculable") ; else m_pMchMgr->SetLastError( 2426, "Error in Pocketing : link outstroke ") ; return false ; } // assegno estremi degli assi dei vari percorsi di lavorazione e della lavorazione nel suo complesso CalcAndSetAxesBBox() ; // esecuzione eventuali personalizzazioni string sErr ; if ( bPostApply && ! PostApply( sErr)) { if ( ! IsEmptyOrSpaces( sErr)) m_pMchMgr->SetLastError( 2427, sErr) ; else m_pMchMgr->SetLastError( 2427, "Error in Pocketing : post apply not calculable") ; return false ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetParam( int nType, bool& bVal) const { switch ( nType) { case MPA_INVERT : bVal = m_Params.m_bInvert ; return true ; case MPA_TOOLINVERT : bVal = m_Params.m_bToolInvert ; return true ; } bVal = false ; return false ; } //---------------------------------------------------------------------------- bool Pocketing::GetParam( int nType, int& nVal) const { switch ( nType) { case MPA_TYPE : nVal = MT_POCKETING ; return true ; case MPA_LEADINTYPE : nVal = m_Params.m_nLeadInType ; return true ; case MPA_LEADOUTTYPE : nVal = m_Params.m_nLeadOutType ; return true ; case MPA_SCC : nVal = m_Params.m_nSolCh ; return true ; case MPA_SUBTYPE : nVal = m_Params.m_nSubType ; return true ; } nVal = 0 ; return false ; } //---------------------------------------------------------------------------- bool Pocketing::GetParam( int nType, double& dVal) const { switch ( nType) { case MPA_SPEED : dVal = GetSpeed() ; return true ; case MPA_FEED : dVal = GetFeed() ; return true ; case MPA_STARTFEED : dVal = GetStartFeed() ; return true ; case MPA_ENDFEED : dVal = GetEndFeed() ; return true ; case MPA_TIPFEED : dVal = GetTipFeed() ; return true ; case MPA_OFFSR : dVal = GetOffsR() ; return true ; case MPA_OFFSL : dVal = GetOffsL() ; return true ; case MPA_STARTPOS : dVal = m_Params.m_dStartPos ; return true ; case MPA_STEP : dVal = m_Params.m_dStep ; return true ; case MPA_SIDESTEP : dVal = m_Params.m_dSideStep ; return true ; case MPA_SIDEANGLE : dVal = m_Params.m_dSideAngle ; return true ; case MPA_LITANG : dVal = m_Params.m_dLiTang ; return true ; case MPA_LIELEV : dVal = m_Params.m_dLiElev ; return true ; case MPA_LOTANG : dVal = m_Params.m_dLoTang ; return true ; case MPA_EPICYCLESRAD : dVal = m_Params.m_dEpicyclesRad ; return true ; case MPA_EPICYCLESDIST : dVal = m_Params.m_dEpicyclesDist ; return true ; } dVal = 0 ; return false ; } //---------------------------------------------------------------------------- bool Pocketing::GetParam( int nType, string& sVal) const { switch ( nType) { case MPA_NAME : sVal = m_Params.m_sName ; return true ; case MPA_TOOL : sVal = m_Params.m_sToolName ; return true ; case MPA_DEPTH_STR : sVal = m_Params.m_sDepth ; return true ; case MPA_TUUID : sVal = ToString( m_Params.m_ToolUuid) ; return true ; case MPA_UUID : sVal = ToString( m_Params.m_Uuid) ; return true ; case MPA_SYSNOTES : sVal = m_Params.m_sSysNotes ; return true ; case MPA_USERNOTES : sVal = m_Params.m_sUserNotes ; return true ; case MPA_INITANGS : sVal = m_Params.m_sInitAngs ; return true ; case MPA_BLOCKEDAXIS : sVal = m_Params.m_sBlockedAxis ; return true ; } sVal = "" ; return false ; } //---------------------------------------------------------------------------- const ToolData& Pocketing::GetToolData( void) const { return m_TParams ; } //---------------------------------------------------------------------------- bool Pocketing::UpdateToolData( bool* pbChanged) { // recupero il gestore DB utensili della macchina corrente ToolsMgr* pTMgr = m_pMchMgr->GetCurrToolsMgr() ; if ( pTMgr == nullptr) return false ; // recupero l'utensile nel DB utensili const ToolData* pTdata = pTMgr->GetTool( m_Params.m_ToolUuid) ; if ( pTdata == nullptr) return false ; // salvo posizione TC, testa e uscita originali string sOrigTcPos = m_TParams.m_sTcPos ; string sOrigHead = m_TParams.m_sHead ; int nOrigExit = m_TParams.m_nExit ; // verifico se sono diversi (ad esclusione di nome, posizione TC, testa e uscita) bool bChanged = ( ! SameTool( m_TParams, *pTdata, false)) ; // aggiorno comunque i parametri m_TParams = *pTdata ; // se definito attrezzaggio, aggiorno i parametri che ne possono derivare string sTcPos ; string sHead ; int nExit ; if ( m_pMchMgr->GetCurrSetupMgr().GetToolData( m_TParams.m_sName, sTcPos, sHead, nExit)) { if ( sOrigTcPos != sTcPos || sOrigHead != sHead || nOrigExit != nExit) bChanged = true ; m_TParams.m_sTcPos = sTcPos ; m_TParams.m_sHead = sHead ; m_TParams.m_nExit = nExit ; } else { if ( sOrigTcPos != pTdata->m_sTcPos || sOrigHead != pTdata->m_sHead || nOrigExit != pTdata->m_nExit) bChanged = true ; } // eventuali segnalazioni if ( ! EqualNoCase( m_Params.m_sToolName, m_TParams.m_sName)) { string sInfo = "Warning in Pocketing : tool name changed (" + m_Params.m_sToolName + "->" + m_TParams.m_sName + ")" ; m_pMchMgr->SetWarning( 2453, sInfo) ; m_Params.m_sToolName = m_TParams.m_sName ; } if ( bChanged) { string sInfo = "Warning in Pocketing : tool data changed (" + m_Params.m_sToolName + ")" ; m_pMchMgr->SetWarning( 2454, sInfo) ; } // se definito parametro di ritorno, lo assegno if ( pbChanged != nullptr) *pbChanged = bChanged ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetGeometry( SELVECTOR& vIds) const { // restituisco l'elenco delle entità vIds = m_vId ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::VerifyGeometry( SelData Id, int& nSubs, int& nType) { // ammessi : curve, testi, facce di trimesh o regioni const IGeoObj* pGObj = m_pGeomDB->GetGeoObj( Id.nId) ; if ( pGObj == nullptr) return false ; // se ammesse curve ed è tale if ( ( nType == GEO_NONE || nType == GEO_CURVE) && ( pGObj->GetType() & GEO_CURVE) != 0) { nType = GEO_CURVE ; const ICurve* pCurve = nullptr ; // se direttamente la curva if ( Id.nSub == SEL_SUB_ALL) { pCurve = ::GetCurve( pGObj) ; if ( pCurve == nullptr) return false ; if ( pCurve->GetType() == CRV_COMPO) nSubs = ::GetCurveComposite( pCurve)->GetCurveCount() ; else nSubs = 0 ; } // altrimenti sottocurva di composita else { const ICurveComposite* pCompo = GetCurveComposite( pGObj) ; pCurve = ( pCompo != nullptr ? pCompo->GetCurve( Id.nSub) : nullptr) ; if ( pCurve == nullptr) return false ; nSubs = 0 ; } return true ; } // se altrimenti ammessi testi ed è tale else if ( ( nType == GEO_NONE || nType == EXT_TEXT) && pGObj->GetType() == EXT_TEXT) { nType = EXT_TEXT ; const IExtText* pText = ::GetExtText( pGObj) ; if ( pText == nullptr) return false ; nSubs = 0 ; return true ; } // se altrimenti ammesse superfici trimesh ed è tale else if ( ( nType == GEO_NONE || nType == SRF_TRIMESH) && pGObj->GetType() == SRF_TRIMESH) { nType = SRF_TRIMESH ; const ISurfTriMesh* pSurf = ::GetSurfTriMesh( pGObj) ; if ( pSurf == nullptr) return false ; // se direttamente la superficie if ( Id.nSub == SEL_SUB_ALL) { // deve avere una sola faccia if ( pSurf->GetFacetCount() != 1) return false ; nSubs = 1 ; } // altrimenti faccia di superficie trimesh else { // se faccia non esistente if ( Id.nSub >= pSurf->GetFacetCount()) return false ; nSubs = 0 ; } return true ; } // se altrimenti ammesse regioni ed è tale else if ( ( nType == GEO_NONE || nType == SRF_FLATRGN) && pGObj->GetType() == SRF_FLATRGN) { nType = SRF_FLATRGN ; const ISurfFlatRegion* pReg = ::GetSurfFlatRegion( pGObj) ; if ( pReg == nullptr) return false ; // se direttamente la regione if ( Id.nSub == SEL_SUB_ALL) { nSubs = pReg->GetChunkCount() ; } // altrimenti chunk di regione else { // se chunk non esistente if ( Id.nSub >= pReg->GetChunkCount()) return false ; // tutto bene nSubs = 0 ; } return true ; } // altrimenti errore else return false ; } //---------------------------------------------------------------------------- bool Pocketing::GetCurvesAndPartialVolume( SelData Id, ICURVEPLIST& lstPC, ISurfTriMesh* pStmTmp, Vector3d& vtN) { // ammessi : curve, testi, facce di trimesh o regioni const IGeoObj* pGObj = m_pGeomDB->GetGeoObj( Id.nId) ; if ( pGObj == nullptr) return false ; // ne recupero il riferimento globale Frame3d frGlob ; if ( ! m_pGeomDB->GetGlobFrame( Id.nId, frGlob)) return false ; // se curva if (( pGObj->GetType() & GEO_CURVE) != 0) { // Thickness double dThick = 0. ; PtrOwner pCurve ; // se direttamente curva if ( Id.nSub == SEL_SUB_ALL) { // recupero la curva const ICurve* pOriCurve = ::GetCurve( pGObj) ; if ( pOriCurve == nullptr) return false ; pOriCurve->GetThickness( dThick) ; pCurve.Set( pOriCurve->Clone()) ; } // altrimenti sottocurva di composita else { // recupero la composita const ICurveComposite* pCompo = GetCurveComposite( pGObj) ; if ( pCompo == nullptr) return false ; pCompo->GetThickness( dThick) ; // recupero la curva semplice const ICurve* pOriCurve = ::GetCurve( pCompo->GetCurve( Id.nSub)) ; if ( pOriCurve == nullptr) return false ; // la duplico pCurve.Set( pOriCurve->Clone()) ; } if ( IsNull( pCurve)) return false ; // porto in globale pCurve->ToGlob( frGlob) ; // recupero eventuali informazioni per lati aperti SetCurveAllTempProp( Id.nId, false, pCurve) ; // recupero estrusione Vector3d vtExtr ; // se estrusione mancante, imposto default if ( ! pCurve->GetExtrusion( vtExtr) || vtExtr.IsSmall()) pCurve->SetExtrusion( Z_AX) ; // corenza con vtTool if ( ! vtN.IsValid()) vtN = vtExtr ; else if ( ! AreSameVectorApprox( vtExtr, vtN)) return false ; // verifico sia piana e se necessario la appiattisco PtrOwner pFlatCrv( FlattenCurve( *pCurve, 50 * EPS_SMALL, 50 * EPS_ANG_SMALL, FLTCRV_USE_EXTR)) ; if ( IsNull( pFlatCrv)) { Plane3d plPlane ; if ( ! pCurve->IsFlat( plPlane, true, 50 * EPS_SMALL)) m_pMchMgr->SetLastError( 2403, "Error in Pocketing : Contour Not Flat") ; else m_pMchMgr->SetLastError( 2404, "Error in Pocketing : Tool Not Perpendicular to Flat Area") ; return false ; } // ricavo la Depth double dDepth = 0. ; if ( ! CalcDepth( Id.nId, vtExtr, dThick, dDepth)) return false ; // affondo pFlatCrv->Translate(( m_Params.m_bToolInvert ? 1. : -1.) * dDepth * vtExtr) ; // aggiungo alla lista lstPC.emplace_back( Release( pFlatCrv)) ; return true ; } // se altrimenti testo else if ( pGObj->GetType() == EXT_TEXT) { // recupero il testo const IExtText* pText = ::GetExtText( pGObj) ; if ( pText == nullptr) return false ; // recupero l'outline del testo ICURVEPLIST lstPC_tmp ; if ( ! pText->GetOutline( lstPC_tmp)) return false ; // porto le curve in globale e ritorno for ( auto pCrv : lstPC_tmp) { pCrv->ToGlob( frGlob) ; // recupero estrusione Vector3d vtExtr ; // se estrusione mancante, imposto default if ( ! pCrv->GetExtrusion( vtExtr) || vtExtr.IsSmall()) pCrv->SetExtrusion( Z_AX) ; // corenza con vtTool if ( ! vtN.IsValid()) vtN = vtExtr ; else if ( ! AreSameVectorApprox( vtExtr, vtN)) return false ; // tutte le curve sono chiuse ResetCurveAllTempProp( pCrv) ; // ricavo la Depth double dDepth = 0. ; if ( ! CalcDepth( Id.nId, vtExtr, 0., dDepth)) return false ; // affondo pCrv->Translate(( m_Params.m_bToolInvert ? 1. : -1.) * dDepth * vtExtr) ; lstPC.push_back( pCrv) ; } return true ; } // se altrimenti superficie else if ( pGObj->GetType() == SRF_TRIMESH) { // inversione del tool non ammessa if ( m_Params.m_bToolInvert) return false ; // recupero la trimesh const ISurfTriMesh* pSurf = ::GetSurfTriMesh( pGObj) ; if ( pSurf == nullptr) return false ; PtrOwner pStm( CloneSurfTriMesh( pSurf)) ; if ( IsNull( pStm)) return false ; // porto in globale pStm->ToGlob( frGlob) ; // recupero l'indice della faccia int nFacet = (( Id.nSub == SEL_SUB_ALL) ? 0 : Id.nSub) ; // recupero la normale esterna della faccia e il suo punto centrale Vector3d vtN_tm ; Point3d ptCenter ; if ( ! pStm->GetFacetCenter( nFacet, ptCenter, vtN_tm)) return false ; if ( ! vtN.IsValid()) vtN = vtN_tm ; else if ( ! AreSameVectorEpsilon( vtN, vtN_tm, 5 * EPS_SMALL)) return false ; // inverto per definire il volume di pocketing pStm->Invert() ; // la superficie diventa il suo volume di svuotatura if ( ! SetPocketingVolume( ptCenter, pStm)) return false ; // ricavo la Depth double dDepth = 0. ; if ( ! CalcDepth( Id.nId, vtN_tm, 0., dDepth)) return false ; // creo il piano di taglio alla quota della faccia Plane3d plCut ; plCut.Set( ptCenter + vtN_tm * ( - dDepth) , - vtN_tm) ; if ( ! plCut.IsValid()) return false ; // taglio il volume alla quota di taglio if ( ! pStm->Cut( plCut, true)) return false ; // chiudo if ( ! SewingMissingFacesOnPlanes( pStm, plCut)) return false ; // aggiungo la Trimesh if ( ! pStmTmp->Add( *pStm)) return false ; return true ; } // se altrimenti regione else if ( pGObj->GetType() == SRF_FLATRGN) { // recupero la regione const ISurfFlatRegion* pReg = ::GetSurfFlatRegion( pGObj) ; if ( pReg == nullptr) return false ; PtrOwner pSfr( CloneSurfFlatRegion( pReg)) ; if ( IsNull( pSfr)) return false ; // porto in globale pSfr->ToGlob( frGlob) ; // recupero la normale della regione Vector3d vtN_fr = pSfr->GetNormVersor() ; if ( vtN_fr.IsSmall()) return false ; if ( ! vtN.IsValid()) vtN = vtN_fr ; else if ( ! AreSameVectorApprox( vtN, vtN_fr)) return false ; // determino intervallo di chunk int nCstart = 0 ; int nCend = pSfr->GetChunkCount() ; if ( Id.nSub != SEL_SUB_ALL) { nCstart = Id.nSub ; nCend = nCstart + 1 ; } // ciclo sui chunk for ( int nC = nCstart ; nC < nCend ; ++ nC) { // recupero i contorni del chunk for ( int nL = 0 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) { PtrOwner pCrvLoop( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ; if ( IsNull( pCrvLoop)) return false ; // unisco le eventuali parti allineate pCrvLoop->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; // sistemazioni varie AdjustCurveFromSurf( pCrvLoop, TOOL_ORTHO, FACE_CONT, 0) ; // tutte le curve sono chiuse ResetCurveAllTempProp( pCrvLoop) ; // ricavo la Depth double dDepth = 0. ; if ( ! CalcDepth( Id.nId, vtN_fr, 0., dDepth)) return false ; // affondo pCrvLoop->Translate(( m_Params.m_bToolInvert ? 1. : -1.) * dDepth * vtN_fr) ; // aggiungo il loop al vettore di curve lstPC.emplace_back( Release( pCrvLoop)) ; } } return true ; } // altrimenti errore else return false ; } //---------------------------------------------------------------------------- bool Pocketing::SetCurveAllTempProp( int nCrvId, bool bForcedClose, ICurve* pCurve, bool* pbSomeOpen) { if ( pCurve == nullptr) return false ; if ( pbSomeOpen != nullptr) *pbSomeOpen = false ; // reset proprietà temporanee ResetCurveAllTempProp( pCurve) ; // se forzato chiuso o non presenti info per lati aperti, esco if ( bForcedClose || ! m_pGeomDB->ExistsInfo( nCrvId, KEY_OPEN)) return true ; // recupero info sui lati aperti INTVECTOR vOpen ; m_pGeomDB->GetInfo( nCrvId, KEY_OPEN, vOpen) ; // se curva composita ICurveComposite* pCC = GetCurveComposite( pCurve) ; if ( pCC != nullptr) { for ( int j : vOpen) { if ( pCC->SetCurveTempProp( j, 1)) { if ( pbSomeOpen != nullptr) *pbSomeOpen = true ; } } } // altrimenti else { if ( ! vOpen.empty() && vOpen[0] == 0) { pCurve->SetTempProp( 1) ; if ( pbSomeOpen != nullptr) *pbSomeOpen = true ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetForcedClosed( void) { int nOpen ; if ( FromString( ExtractInfo( m_Params.m_sUserNotes, "Open="), nOpen) && nOpen == 0) return true ; else return false ; } //---------------------------------------------------------------------------- bool Pocketing::ResetCurveAllTempProp( ICurve* pCurve) { if ( pCurve == nullptr) return false ; pCurve->SetTempProp( 0) ; ICurveComposite* pCC = GetCurveComposite( pCurve) ; if ( pCC != nullptr) for ( int i = 0 ; i < pCC->GetCurveCount() ; ++ i) pCC->SetCurveTempProp( i, 0, 0) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::CalcDepth( const int nId, const Vector3d& vtExtr, double dThick, double& dDepth) { // recupero distanza da fondo dei grezzi interessati dal percorso double dRbDist = 0. ; if ( AreSameVectorApprox( vtExtr, Z_AX)) { if ( ! GetDistanceFromRawBottom( m_nPhase, nId, m_TParams.m_dTDiam, dRbDist)) return false ; } // se inversione del Tool if ( m_Params.m_bToolInvert) dThick = - dThick ; // valuto l'espressione dell'affondamento ExeLuaSetGlobNumVar( "TH", abs( dThick)) ; ExeLuaSetGlobNumVar( "RB", dRbDist) ; string sMyDepth = m_Params.m_sDepth ; if ( ! ExeLuaEvalNumExpr( ToUpper( sMyDepth), &dDepth)) { m_pMchMgr->SetLastError( 2406, "Error in Pocketing : Depth not computable") ; return false ; } // se spessore positivo, lo sottraggo dal risultato if ( dThick > 0) dDepth -= dThick ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::Chain( int nGrpDestId) { // vettore puntatori alle curve ICURVEPOVECTOR vpCrvs ; vpCrvs.reserve( m_vId.size()) ; // vettore selettori delle curve originali SELVECTOR vInds ; // TriMesh complessiva di pocketing per intersezione con finito PtrOwner pStm_tmp( CreateSurfTriMesh()) ; if ( IsNull( pStm_tmp) || ! pStm_tmp->AdjustTopology()) return false ; // estrusione e spessore Vector3d vtExtr = V_INVALID ; // scorro tutti gli id for ( const auto& Id : m_vId) { // vettore contente tutte le curve selezionate ICURVEPLIST lstPC ; // prendo le curve e/o aggiorno la TriMesh di pocketing originale if ( ! GetCurvesAndPartialVolume( Id, lstPC, pStm_tmp, vtExtr)) { string sInfo = "Warning in Pocketing : Skipped entity " + ToString( Id) ; m_pMchMgr->SetWarning( 2451, sInfo) ; } for ( auto pCrv : lstPC) { vpCrvs.emplace_back( pCrv) ; vInds.emplace_back( Id) ; } } // se inversione del Tool, allora inversione dell'estrusione if ( m_Params.m_bToolInvert) vtExtr.Invert() ; // se ho trovato delle curve... if ( int( vpCrvs.size()) > 0) { // concateno le curve se necessario ICRVCOMPOPOVECTOR vCrvCompo ; if ( ! ChainCurveArray( vInds, vpCrvs, vCrvCompo)) return false ; // verifico che siano curve chiuse ( devono delimitare l'area da svuotare) for ( int u = 0 ; u < int( vCrvCompo.size()) ; ++ u) { if ( ! vCrvCompo[u]->IsClosed()) { m_pMchMgr->SetLastError( 2402, "Error in Pocketing : Open Contour") ; return false ; } } // ricavo il centro della prima curva Point3d ptInside ; if ( ! vCrvCompo[0]->GetCentroid( ptInside)) return false ; // creazione della TriMesh di pocketing PtrOwner pStmCurve_Volume( CreateSurfTriMesh()) ; if ( IsNull( pStmCurve_Volume) || ! pStmCurve_Volume->AdjustTopology() || ! CreateStmForIntersection( pStmCurve_Volume, vCrvCompo, vtExtr)) return false ; // intersezione della Trimesh con il finito -> rivavo il volume di svuotatura if ( ! SetPocketingVolume( ptInside, pStmCurve_Volume)) return false ; if ( pStm_tmp->IsValid()) if ( ! pStm_tmp->Add( *pStmCurve_Volume)) return false ; } // per ogni part rivata dal volume di svuotatura... for ( int p = 0 ; p < pStm_tmp->GetPartCount() ; ++ p) { // creo nuovo gruppo int nPathId = m_pGeomDB->AddGroup( GDB_ID_NULL, nGrpDestId, Frame3d()) ; if ( nPathId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nPathId, MCH_PATH + ToString( p + 1)) ; // inserisco la Part (p)-esima nel gruppo destinazione int nNewId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nPathId, pStm_tmp->ClonePart( p)) ; if ( nNewId == GDB_ID_NULL) return false ; // memorizzo la direzione di estrusione m_pGeomDB->SetInfo( nPathId, KEY_TOOL, vtExtr) ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::ChainCurveArray( const SELVECTOR vInds, ICURVEPOVECTOR& vpCrvs, ICRVCOMPOPOVECTOR& vCrvCompo) { // preparo i dati per il concatenamento bool bFirst = true ; Point3d ptNear = ORIG ; double dToler = 10 * EPS_SMALL ; ChainCurves chainC ; chainC.Init( true, dToler, int( vpCrvs.size())) ; for ( size_t i = 0 ; i < vpCrvs.size() ; ++ i) { // recupero la curva e il suo riferimento ICurve* pCrv = vpCrvs[i] ; if ( pCrv == nullptr) continue ; // recupero i dati della curva necessari al concatenamento e li assegno Point3d ptStart, ptEnd ; Vector3d vtStart, vtEnd ; if ( ! pCrv->GetStartPoint( ptStart) || ! pCrv->GetStartDir( vtStart) || ! pCrv->GetEndPoint( ptEnd) || ! pCrv->GetEndDir( vtEnd)) return false ; if ( ! chainC.AddCurve( int( i + 1), ptStart, vtStart, ptEnd, vtEnd)) return false ; // se prima curva, assegno inizio della ricerca if ( bFirst) { ptNear = ptStart + 10 * EPS_SMALL * vtStart ; bFirst = false ; } } // recupero i percorsi concatenati int nCount = 0 ; INTVECTOR vnId2 ; while ( chainC.GetChainFromNear( ptNear, false, vnId2)) { // creo una curva composita PtrOwner pCrvCompo( CreateCurveComposite()) ; if ( IsNull( pCrvCompo)) return false ; // estrusione e spessore Vector3d vtExtr = Z_AX ; double dThick = 0 ; // vettore Id originali SELVECTOR vId2 ; vId2.reserve( vnId2.size()) ; // recupero le curve semplici e le inserisco nella curva composita for ( size_t i = 0 ; i < vnId2.size() ; ++ i) { int nId = abs( vnId2[i]) - 1 ; bool bInvert = ( vnId2[i] < 0) ; vId2.emplace_back( vInds[nId]) ; // recupero la curva ICurve* pCrv = vpCrvs[nId] ; // se necessario, la inverto if ( bInvert) pCrv->Invert() ; // recupero eventuali estrusione e spessore Vector3d vtTemp ; if ( pCrv->GetExtrusion( vtTemp)) { vtExtr = vtTemp ; double dTemp ; if ( pCrv->GetThickness( dTemp) && abs( dTemp) > abs( dThick)) dThick = dTemp ; } // riporto le proprietà temporanee pCrvCompo->SetTempProp( vpCrvs[nId]->GetTempProp( 0), 0) ; pCrvCompo->SetTempProp( vpCrvs[nId]->GetTempProp( 1), 1) ; // la aggiungo alla curva composta if ( ! pCrvCompo->AddCurve( ::Release( vpCrvs[nId]), true, dToler)) return false ; } // se non sono state inserite curve, vado oltre if ( pCrvCompo->GetCurveCount() == 0) continue ; // imposto estrusione e spessore pCrvCompo->SetExtrusion( vtExtr) ; pCrvCompo->SetThickness( dThick) ; // aggiorno il nuovo punto vicino pCrvCompo->GetEndPoint( ptNear) ; // aggiungo la curva al vettore di curve composite concatenate vCrvCompo.emplace_back( Release( pCrvCompo)) ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::CreateStmForIntersection( ISurfTriMesh* pStm, ICRVCOMPOPOVECTOR& vCrvCompo, const Vector3d& vtN) { // se non ho entità esco if ( int( vCrvCompo.size()) == 0) return true ; // creo un frame centrato sulla prima curva con Z orientata come m_vtTool Point3d ptOrig ; if ( ! vCrvCompo[0]->GetCentroid( ptOrig)) if ( ! vCrvCompo[0]->GetStartPoint( ptOrig)) return false ; Frame3d frHeight ; frHeight.Set( ptOrig, vtN) ; if ( ! frHeight.IsValid()) return false ; struct CurveQuotes { int nCrvInd ; double dQuote ; } ; // scorro le curve e ricavo le quote vector vdCrvQuotes ; for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) { vCrvCompo[i]->ToLoc( frHeight) ; // porto in locale Point3d ptOnCurve ; if ( ! vCrvCompo[i]->GetCentroid( ptOnCurve)) if ( ! vCrvCompo[i]->GetStartPoint( ptOnCurve)) return false ; CurveQuotes CrvQt ; CrvQt.nCrvInd = i ; CrvQt.dQuote = ptOnCurve.z ; vdCrvQuotes.push_back( CrvQt) ; vCrvCompo[i]->ToGlob( frHeight) ; // porto in globale } // ordino le quote sort( vdCrvQuotes.begin(), vdCrvQuotes.end(), []( CurveQuotes &CrvQt0, CurveQuotes &CrvQt1) { return CrvQt0.dQuote < CrvQt1.dQuote ; }) ; // creo una FlatRegion con le curve alla stessa quota ISURFFRPOVECTOR vSfrByQuotes ; int nInd = 0 ; while ( nInd < int( vdCrvQuotes.size())) { // regione tempoeranea con la curva attuale SurfFlatRegionByContours sfrBC ; sfrBC.AddCurve( vCrvCompo[vdCrvQuotes[nInd].nCrvInd]->Clone()) ; for ( int j = nInd + 1 ; j < int( vdCrvQuotes.size()) ; ++ j) { // se curva successiva alla stessa quota -> aggiungo alla regione if ( abs( vdCrvQuotes[j].dQuote - vdCrvQuotes[j-1].dQuote) < EPS_SMALL) { sfrBC.AddCurve( vCrvCompo[vdCrvQuotes[j].nCrvInd]->Clone()) ; ++ nInd ; } else break ; } ++ nInd ; // creo la regione PtrOwner pSfrFinal( CreateSurfFlatRegion()) ; if ( IsNull( pSfrFinal)) return false ; PtrOwner pSfrCurr( sfrBC.GetSurf()) ; while ( ! IsNull( pSfrCurr) && pSfrCurr->IsValid()) { // inverto se necessario if ( AreOppositeVectorApprox( vtN, pSfrCurr->GetNormVersor())) pSfrCurr->Invert() ; if ( ! pSfrFinal->IsValid()) pSfrFinal.Set( Release( pSfrCurr)) ; else if ( ! pSfrFinal->Add( *pSfrCurr) || ! pSfrFinal->IsValid()) return false ; pSfrCurr.Set( sfrBC.GetSurf()) ; } // aggiungo al vettore vSfrByQuotes.emplace_back( Release( pSfrFinal)) ; } // devo allargare i lati aperti della curva esterna, così la superficie si adatterà al finito mediante l'intersezione for ( int s = 0 ; s < int( vSfrByQuotes.size()) ; ++ s) { // per ogni superficie ricavo il loop esterno e le sue isole for ( int c = 0 ; c < vSfrByQuotes[s]->GetChunkCount() ; ++ c) { // creo il vettore contenente il Loop esterno esteso e le isole CICURVEPVECTOR vCrv ; // loop esterno PtrOwner pCrvLoopExt( ConvertCurveToComposite( vSfrByQuotes[s]->GetLoop( c, 0))) ; if ( IsNull( pCrvLoopExt) || ! pCrvLoopExt->IsValid()) return false ; // allargo il loop esterno presso i chiusi if ( ! GetExtendedLoopToFitStmVolume( pCrvLoopExt, vtN)) return false ; // inserisco la curva nel vettore vCrv.emplace_back( Release( pCrvLoopExt)) ; // inserisco le isole for ( int l = 1 ; l < vSfrByQuotes[s]->GetLoopCount( c) ; ++ l) vCrv.emplace_back( vSfrByQuotes[s]->GetLoop( c, l)) ; // creo la TriMesh per l'intersezione con il finito PtrOwner pStmTmp( GetSurfTriMeshByRegionExtrusion( vCrv, 3000 * vtN)) ; for ( int m = 0 ; m < int( vCrv.size()) ; ++ m) delete( vCrv[m]) ; if ( IsNull( pStmTmp) || ! pStmTmp->IsValid()) return false ; // aggiungo questa regione a quella passata come parametro alla funzione if ( ! pStm->Add( *pStmTmp)) return false ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetExtendedLoopToFitStmVolume( ICurveComposite* pCrvLoop, const Vector3d& vtN) { // controllo del parametro if ( pCrvLoop == nullptr || ! pCrvLoop->IsValid()) return false ; // ricavo i parametri della curva double dThick ; pCrvLoop->GetThickness( dThick) ; int nTmpProp0 = pCrvLoop->GetTempProp() ; int nTmpProp1 = pCrvLoop->GetTempProp( 1) ; // ricavo i tratti omogenei ( campio il punto iniziale della curva nel punto medio del chiuso più lungo se presente) ICRVCOMPOPOVECTOR vpCrvs ; if ( ! GetHomogeneousParts( pCrvLoop, vpCrvs)) return false ; // se curva tutta omogonea if ( int( vpCrvs.size()) == 1 ) { // se tutta chiusa, non devo estendere nulla if ( vpCrvs[0]->GetTempProp() == 0) return true ; // se tutta aperta -> Offset OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( pCrvLoop, 3000, ICurve::OFF_EXTEND)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } pCrvLoop->CopyFrom( OffsCrv.GetLongerCurve()) ; return pCrvLoop->IsClosed() && pCrvLoop->IsValid() ; } // porto tutto nel piano XY ( per le intersezioni in futuro) Point3d ptOrig ; pCrvLoop->GetCentroid( ptOrig) ; Frame3d frZ ; frZ.Set( ptOrig, vtN) ; if ( ! frZ.IsValid()) return false ; for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) vpCrvs[i]->ToLoc( frZ) ; // pulisco la curva pCrvLoop->Clear() ; // scorro i tratti for ( int i = 0 ; i < int( vpCrvs.size()); ++ i) { // determino se il tratto è chiuso o aperto int nCurrTmpProp = vpCrvs[i]->GetTempProp() ; if ( nCurrTmpProp == 0) { pCrvLoop->AddCurve( vpCrvs[i]->Clone()) ; continue ; } // linea di estensione sulla parte finale Point3d ptSL1 ; vpCrvs[i-1]->GetEndPoint( ptSL1) ; Vector3d vtSL1 ; vpCrvs[i-1]->GetEndDir( vtSL1) ; PtrOwner pLineS( CreateCurveLine()) ; if ( IsNull( pLineS) || ! pLineS->SetPVL( ptSL1, vtSL1, 30000)) return false ; // linea di estensione sulla parte finale Point3d ptSL2 ; vpCrvs[i+1]->GetStartPoint( ptSL2) ; Vector3d vtSL2 ; vpCrvs[i+1]->GetStartDir( vtSL2) ; vtSL2.Invert() ; PtrOwner pLineE( CreateCurveLine()) ; if ( IsNull( pLineE) || ! pLineE->SetPVL( ptSL2, vtSL2, 30000)) return false ; // se le linee di intersecano IntersCurveCurve IntCC( *pLineE, *pLineS) ; if ( IntCC.GetIntersCount() == 1) { // ricavo il punto di intersezione IntCrvCrvInfo aInfo ; if ( ! IntCC.GetIntCrvCrvInfo( 0, aInfo)) return false ; Point3d ptInters( aInfo.IciA[0].ptI) ; // aggiungo il tratto nuovo if ( ! pCrvLoop->AddLine( ptInters) || ! pCrvLoop->AddLine( ptSL2)) return false ; } // se invece non ci sono intersezioni tra le linee else if ( IntCC.GetIntersCount() == 0) { // estendo il tratto aperto con un Offset OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( vpCrvs[i], 300, ICurve::OFF_EXTEND)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } PtrOwner pCrvOffs( OffsCrv.GetLongerCurve()) ; if ( IsNull( pCrvOffs) || ! pCrvOffs->IsValid()) return false ; // estendo l'inizio e la fine if ( ! pCrvOffs->ExtendStartByLen( 30000) || ! pCrvOffs->ExtendEndByLen( 30000)) return false ; // interseco con questa curva con le rette IntersCurveCurve IntCC_S( *pLineS, *pCrvOffs) ; IntersCurveCurve IntCC_E( *pLineE, *pCrvOffs) ; Point3d ptInters_S, ptInters_E ; if ( IntCC_S.GetIntersCount() == 1) { IntCrvCrvInfo aInfoS ; if ( ! IntCC_S.GetIntCrvCrvInfo( 0, aInfoS)) return false ; ptInters_S = aInfoS.IciA[0].ptI ; } else return false ; if ( IntCC_E.GetCrossIntersCount() == 1) { IntCrvCrvInfo aInfoE ; if ( ! IntCC_E.GetIntCrvCrvInfo( 0, aInfoE)) return false ; ptInters_E = aInfoE.IciA[0].ptI ; } else return false ; // trimmo la curva Offsettata nei punti di intersezione double dUS, dUE ; pCrvOffs->GetParamAtPoint( ptInters_S, dUS) ; pCrvOffs->GetParamAtPoint( ptInters_E, dUE) ; if ( ! pCrvOffs->TrimStartEndAtParam( dUS, dUE)) return false ; // aggiungo il tratto nuovo if ( ! pCrvLoop->AddLine( ptInters_S) || ! pCrvLoop->AddCurve( Release( pCrvOffs)) || ! pCrvLoop->AddLine( ptSL2)) return false ; } else // non definito return false ; } // riporto nel frame originario pCrvLoop->ToGlob( frZ) ; return pCrvLoop->IsClosed() && pCrvLoop->IsValid() ; } //---------------------------------------------------------------------------- bool Pocketing::GetCurrentPart( const Point3d& ptInside, ISurfTriMesh* pStmPart) { // controllo MachManager e database geometrico if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // considero tutte le superfici dei pezzi nei grezzi attivi della fase int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { if ( m_pMchMgr->VerifyRawPartPhase( nRawId, m_nPhase)) { // ciclo sui pezzi del grezzo int nPartId = m_pMchMgr->GetFirstPartInRawPart( nRawId) ; while ( nPartId != GDB_ID_NULL) { // ciclo sui layer dei pezzi int nLayId = m_pGeomDB->GetFirstGroupInGroup( nPartId) ; while ( nLayId != GDB_ID_NULL) { // ciclo sulle entità del layer int nEntId = m_pGeomDB->GetFirstInGroup( nLayId) ; while ( nEntId != GDB_ID_NULL) { // se entità superficie e visibile, la aggiungo int nStat ; if ( m_pGeomDB->GetGeoType( nEntId) == SRF_TRIMESH) { // recupero l'ggetto dal databse con tale Id const IGeoObj* pGObj = m_pGeomDB->GetGeoObj( nEntId) ; // recupero il frame in cui si trova Frame3d frPart ; m_pGeomDB->GetGlobFrame( nEntId, frPart) ; if ( pGObj == nullptr) return false ; // controllo che effettivamente sia una TriMesh if ( pGObj->GetType() == SRF_TRIMESH) { // Trimesh della Part PtrOwner pStmRawPart( CloneSurfTriMesh( pGObj)) ; if ( IsNull( pStmRawPart)) return false ; // porto la Trimesh in globale pStmRawPart->LocToLoc( frPart, GLOB_FRM) ; // controllo se il punto è interno a tale Part double dElev = 0. ; DistPointSurfTm DistPtStm( ptInside, *pStmRawPart) ; double dDist ; DistPtStm.GetDist( dDist) ; if ( DistPtStm.IsPointInside() || DistPtStm.IsSmall()) { pStmPart->CopyFrom( pStmRawPart) ; return pStmPart->IsValid() && pStmPart->GetTriangleCount() > 0 ; } } } // passo alla entità successiva nEntId = m_pGeomDB->GetNext( nEntId) ; } nLayId = m_pGeomDB->GetNextGroup( nLayId) ; } nPartId = m_pMchMgr->GetNextPartInRawPart( nPartId) ; } } nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } return false ; } //---------------------------------------------------------------------------- bool Pocketing::SetPocketingVolume( const Point3d& ptInside, ISurfTriMesh* pStm) { // controllo parametro if ( pStm == nullptr || ! pStm->IsValid()) return false ; // recupero il Current Part PtrOwner pStmCurrRawPart( CreateSurfTriMesh()) ; if ( IsNull( pStmCurrRawPart) || ! GetCurrentPart( ptInside, pStmCurrRawPart)) return false ; // interseco la TriMesh complessiva di pocketing con il finito, ottenendo il volume di svuotatura if ( ! pStmCurrRawPart->Intersect( *pStm)) // intersezione return false ; // aggiorno la superficie pStm->CopyFrom( pStmCurrRawPart) ; return pStm->IsValid() && pStm->GetTriangleCount() > 0 ; } //--------------------------------------------------------------------------- bool Pocketing::AdjustPocketingSideForVolumePart( ISurfTriMesh* pStmVolPart, const Vector3d& vtTool) { // controllo dei parametri if ( pStmVolPart == nullptr || ! pStmVolPart->IsValid()) return false ; // NB. Il controllo delle normali delle entitità che sono state selezionate presenta una laggera tolleranza; // Questa tolleranza potrebbe rendere la superficie da svuotare non completamente piana ( ma come sovrapposizione // di superfici piane leggermente inclinate tra loro; const double TOLL = 500 * EPS_SMALL ; // creo un frame centrato nel centroide della parte Frame3d frElevation ; Point3d ptC ; pStmVolPart->GetCentroid( ptC) ; frElevation.Set( ptC, vtTool) ; if ( ! frElevation.IsValid()) return false ; // porto il la parte di volume in questo frame pStmVolPart->ToLoc( frElevation) ; // ricavo quindi il Box BBox3d BBox ; pStmVolPart->GetLocalBBox( BBox) ; // il su cui iniziare a svuotare è quello che si trova a Z minima double dZ_min = BBox.GetMin().z ; // riporto il volume nel suo frame Originario pStmVolPart->ToGlob( frElevation) ; // porto il frame alla minima Z ( sarà negativa) di svuotatura frElevation.Set( ptC + vtTool * dZ_min, vtTool) ; if ( ! frElevation.IsValid()) return false ; // porto il volume in questo frame pStmVolPart->ToLoc( frElevation) ; // NB. ora il nostro volume è appoggiato al piano Z = 0 centrato sopra all'Origine // devo controllare se ci sono delle facce contenute tra il piano Z = 0 e Z = TOLL // 1) creo i due piani Plane3d plZ0 ; plZ0.Set( ORIG, -Z_AX) ; if ( ! plZ0.IsValid()) return false ; Plane3d plZTOLL ; plZTOLL.Set( ORIG + Z_AX * TOLL, Z_AX) ; if ( ! plZTOLL.IsValid()) return false ; // 2) taglio una copia del volume tra i due piani PtrOwner pStmVolPart_between( CloneSurfTriMesh( pStmVolPart)) ; if ( IsNull( pStmVolPart_between) || ! pStmVolPart_between->IsValid()) return false ; if ( ! pStmVolPart_between->Cut( plZ0, false) || // tolgo la faccia a Z = 0 ! pStmVolPart_between->Cut( plZTOLL, false)) return false ; // 3) controllo tutte le facce che ho attenuto ; se ho ottenuto almeno una faccia con normale simile a Z_AX // allora traslo il piano di svuotatura a Z = TOLL bool bExistFaces = false ; for ( int f = 0 ; f < pStmVolPart_between->GetFacetCount() && !bExistFaces ; ++ f) { Point3d ptC ; Vector3d vtC ; if ( ! pStmVolPart_between->GetFacetCenter( f, ptC, vtC)) return false ; bExistFaces = AreSameVectorEpsilon( vtC, - Z_AX, 5 * EPS_SMALL) ; } // se non ho trovato nessuna faccia, allora il volume andava già bene if ( ! bExistFaces) { pStmVolPart->ToGlob( frElevation) ; // riportato in posizione originale return true ; } // dato che ho trovato almeno una faccia, il mio nuovo volume si restringe leggermente su Z = TOLL plZTOLL.Invert() ; if ( ! pStmVolPart->Cut( plZTOLL, false) || ! SewingMissingFacesOnPlanes( pStmVolPart, plZTOLL)) return false ; pStmVolPart->ToGlob( frElevation) ; // riportato in posizione originale return true ; } //---------------------------------------------------------------------------- bool Pocketing::ProcessPath( int nPathId, int nPvId, int nClId) { // recupero gruppo per geometria temporanea ( Gruppo Temp) const string GRP_TEMP = "Temp" ; int nTempId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, GRP_TEMP) ; // se non c'è, lo aggiungo if ( nTempId == GDB_ID_NULL) { nTempId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ; if ( nTempId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nTempId, GRP_TEMP) ; } // altrimenti lo svuoto else m_pGeomDB->EmptyGroup( nTempId) ; // in ogni caso lo dichiaro temporaneo e non visibile m_pGeomDB->SetLevel( nTempId, GDB_LV_TEMP) ; m_pGeomDB->SetStatus( nTempId, GDB_ST_OFF) ; // recupero l'entità ( Part del volume di svuotatura) double dDepth = 0. ; Vector3d vtTool ; m_pGeomDB->GetInfo( nPathId, KEY_TOOL, vtTool) ; PtrOwner pStm_PartVolume( CreateSurfTriMesh()) ; if ( IsNull( pStm_PartVolume)) return false ; int nId = m_pGeomDB->GetFirstInGroup( nPathId) ; if ( m_pGeomDB->GetGeoType( nId) != SRF_TRIMESH) return false ; pStm_PartVolume.Set( CloneSurfTriMesh( m_pGeomDB->GetGeoObj( nId))) ; if ( IsNull( pStm_PartVolume) || ! pStm_PartVolume->IsValid()) return false ; // recupero nome del path string sPathName ; m_pGeomDB->GetName( nPathId, sPathName) ; // recupero il Box del grezzo globale BBox3d b3Raw ; if ( ! GetRawGlobBox( m_nPhase, nPathId, 0.5 * m_TParams.m_dTDiam, b3Raw) || b3Raw.IsEmpty()) { m_pMchMgr->SetLastError( 2405, "Error in Pocketing : Empty RawBox") ; return false ; } // recupero la Part in cui è contenuto il volume Point3d ptC ; pStm_PartVolume->GetCentroid( ptC) ; PtrOwner pStm_Part( CreateSurfTriMesh()) ; if ( IsNull( pStm_Part) || ! GetCurrentPart( ptC, pStm_Part)) return false ; // rendo il volume accettabile per la svuotatura... if ( ! AdjustPocketingSideForVolumePart( pStm_PartVolume, vtTool)) return false ; // devo calcolare l'Elevazione... Frame3d frElevation ; frElevation.Set( ptC, vtTool) ; if ( ! frElevation.IsValid()) return false ; pStm_PartVolume->ToLoc( frElevation) ; // ricavo quindi il Box BBox3d BBox ; pStm_PartVolume->GetLocalBBox( BBox) ; // il su cui iniziare a svuotare è quello che si trova a Z minima double dZ_min = BBox.GetMin().z ; pStm_PartVolume->ToGlob( frElevation) ; // calcolo l'elevazione massima ( è la lunghezza Z del Box con asse Z uguale a vtTool) double dElev = BBox.GetDimZ() ; // eventuale imposizione massima elevazione da note utente double dMaxElev ; if ( FromString( ExtractInfo( m_Params.m_sUserNotes, "MaxElev="), dMaxElev) && dElev > dMaxElev) dElev = dMaxElev ; // verifico che lo step dell'utensile sia sensato double dOkStep = ( m_Params.m_dStep > EPS_SMALL ? m_Params.m_dStep + EPS_SMALL : 0) ; const double MIN_ZSTEP = 1.0 ; if ( dOkStep >= EPS_SMALL && dOkStep < MIN_ZSTEP) { dOkStep = MIN_ZSTEP + EPS_SMALL ; string sInfo = "Warning in Pocketing : machining step too small (" + ToString( m_Params.m_dStep, 2) + ")" ; m_pMchMgr->SetWarning( 2456, sInfo) ; } // verifico che il massimo materiale dell'utensile sia sensato const double MIN_MAXMAT = 1.0 ; if ( m_TParams.m_dMaxMat < dElev && m_TParams.m_dMaxMat < MIN_MAXMAT) { string sInfo = "Error in Pocketing : Tool MaxMaterial too small (" + ToString( m_TParams.m_dMaxMat, 2) + ")" ; m_pMchMgr->SetLastError( 2422, sInfo) ; return false ; } // verifico di non superare il massimo materiale se lo step supera la capacità dell'utensile if ( m_Params.m_dStep > m_TParams.m_dMaxMat + EPS_SMALL) { dOkStep = m_TParams.m_dMaxMat + EPS_SMALL ; string sInfo = "Warning in Pocketing : machining step (" + ToString( m_Params.m_dStep, 1) + ") bigger than MaxMaterial (" + ToString( m_TParams.m_dMaxMat, 1) + ")" ; m_pMchMgr->SetWarning( 2457, sInfo) ; } // se lavorazione singola if ( dOkStep < EPS_SMALL || dOkStep > dElev) { // se l'elevazione supera la capacità dell'utensile if ( dElev > m_TParams.m_dMaxMat + EPS_SMALL) { string sInfo = "Warning in Pocketing : machining depth (" + ToString( dElev, 1) + ") bigger than MaxMaterial (" + ToString( m_TParams.m_dMaxMat, 1) + ")" ; m_pMchMgr->SetWarning( 2458, sInfo) ; dDepth -= dElev - m_TParams.m_dMaxMat ; dElev = m_TParams.m_dMaxMat ; } } // altrimenti lavorazione a step else { // se l'elevazione supera il massimo affondamento dell'utensile double dSafe = m_pMchMgr->GetCurrMachiningsMgr()->GetMaxDepthSafe() ; double dMaxDepth = m_TParams.m_dLen - ( m_TParams.m_dDiam > m_dTHoldDiam ? m_dTHoldBase : m_dTHoldLen) - dSafe ; if ( dElev > dMaxDepth + EPS_SMALL) { // segnalo, riduco e continuo string sInfo = "Warning in Pocketing : machining depth (" + ToString( dElev, 1) + ") bigger than MaxDepth (" + ToString( dMaxDepth, 1) + ")" ; m_pMchMgr->SetWarning( 2458, sInfo) ; dDepth -= dElev - dMaxDepth ; dElev = dMaxDepth ; } } // verifico se tavola basculante bool bTiltTab = false ; m_bTiltingTab = ( m_pMchMgr->GetCurrMachine()->GetCurrTableIsTilting( bTiltTab, m_vtTiltingAx) && bTiltTab) ; // verifico se testa da sopra (Z+) m_bAboveHead = m_pMchMgr->GetHeadAbove( m_TParams.m_sHead) ; // verifiche per svuotature dal basso m_bAggrBottom = false ; //if ( ! VerifyPathFromBottom( pCompo, vtTool)) { // return false ; //} // recupero eventuale flag di lato aperto forzato fuori dal grezzo int nOpenOutRaw ; m_bOpenOutRaw = ( FromString( ExtractInfo( m_Params.m_sUserNotes, "OpenOutRaw="), nOpenOutRaw) && nOpenOutRaw != 0) ; // recupero eventuale minima lunghezza di attacco su lato aperto FromString( ExtractInfo( m_Params.m_sUserNotes, "OpenMinSafe="), m_dOpenMinSafe) ; // se richiesta anteprima if ( nPvId != GDB_ID_NULL) { // creo gruppo per geometria di lavorazione del percorso int nPxId = m_pGeomDB->AddGroup( GDB_ID_NULL, nPvId, Frame3d()) ; if ( nPxId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nPxId, sPathName) ; m_pGeomDB->SetMaterial( nPxId, GREEN) ; // creo l'anteprima del percorso //if ( ! GeneratePocketingPv( nPxId, pCompo)) // return false ; } // se richiesta lavorazione if ( nClId != GDB_ID_NULL) { // creo gruppo per geometria di lavorazione del percorso int nPxId = m_pGeomDB->AddGroup( GDB_ID_NULL, nClId, Frame3d()) ; if ( nPxId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nPxId, sPathName) ; m_pGeomDB->SetMaterial( nPxId, BLUE) ; // verifico se archi vanno approssimati con segmenti di retta int nSplitArcs = m_pMchMgr->GetCurrMachiningsMgr()->GetSplitArcs() ; bool bSplitArcs = ( nSplitArcs == SPLAR_ALWAYS || ( nSplitArcs == SPLAR_NO_XY_PLANE && ! vtTool.IsZplus()) || ( nSplitArcs == SPLAR_GEN_PLANE && vtTool.IsGeneric())) ; // assegno il vettore estrazione al gruppo del percorso m_pGeomDB->SetInfo( nPxId, KEY_EXTR, vtTool) ; // assegno l'elevazione massima m_pGeomDB->SetInfo( nPxId, KEY_ELEV, dElev) ; // Imposto dati comuni SetPathId( nPxId) ; SetToolDir( vtTool) ; // determino numero e affondamento degli step int nStep = 1 ; nStep = max( 1, static_cast( ceil( dElev / dOkStep))) ; double dStep = dElev / nStep ; // porto il frame alla minima Z ( sarà negativa) di svuotatura frElevation.Set( ptC + vtTool * dZ_min, vtTool) ; if ( ! frElevation.IsValid()) return false ; // porto il volume e la Part che lo contiene in questo frame pStm_PartVolume->ToLoc( frElevation) ; pStm_Part->ToLoc( frElevation) ; // creo i parametri utili per la svuotatura ISURFFRPOVECTOR vSrfSliced ; // vettore delle superficie ricavate ( per ogni step) vector vCrvOEWithFlags; // vettore delle relative curve originali ( per ogni step) BOOLVECTOR vbChangedPrec( nStep, false) ; // vettore di Flag per superfici uguali tra steps consecutivi VCT3DVECTOR vVtTrasl( nStep, V_NULL) ; // vettore contenente le quote per le passate di svuotature // ricavo le superfici di svuotatura if ( ! SliceVolume( pStm_PartVolume, pStm_Part, vSrfSliced, vCrvOEWithFlags, vbChangedPrec, vVtTrasl, nStep, vtTool, dElev, dDepth, dStep)) return false ; // riporto il volume nel frame globale pStm_PartVolume->ToGlob( frElevation) ; // riporto le superfici nel frame globale for ( int s = 0 ; s < int( vSrfSliced.size()) ; ++ s) vSrfSliced[s]->ToGlob( frElevation) ; // riporto le curve per i casi ottimizzati nel frame globale for ( int u = 0 ; u < int( vCrvOEWithFlags.size()) ; ++ u) for ( int uu = 0 ; uu < int( vCrvOEWithFlags[u].size()) ; ++ uu) vCrvOEWithFlags[u][uu]->ToGlob( frElevation) ; // riporto i vettori di traslazione nel frame globale for ( int v = 0 ; v < int( vVtTrasl.size()) ; ++ v) vVtTrasl[v].ToGlob( frElevation) ; // Eseguo la lavorazione a seconda del tipo switch ( m_Params.m_nSubType) { case POCKET_SUB_ZIGZAG : if ( ! AddZigZag( vSrfSliced, vCrvOEWithFlags, vbChangedPrec, vVtTrasl, vtTool, vtTool, dDepth, dElev, dMaxElev, dOkStep, bSplitArcs)) return false ; break ; case POCKET_SUB_ONEWAY : if ( ! AddOneWay( vSrfSliced, vCrvOEWithFlags, vbChangedPrec, vVtTrasl, vtTool, vtTool, dDepth, dElev, dMaxElev, dOkStep, bSplitArcs)) return false ; break ; case POCKET_SUB_SPIRALIN : if ( ! AddSpiralIn( vSrfSliced, vCrvOEWithFlags, vbChangedPrec, vVtTrasl, vtTool, vtTool, dDepth, dElev, dMaxElev, dOkStep, bSplitArcs)) return false ; break ; case POCKET_SUB_SPIRALOUT : if ( ! AddSpiralOut( vSrfSliced, vCrvOEWithFlags, vbChangedPrec, vVtTrasl, vtTool, vtTool, dDepth, dElev, dMaxElev, dOkStep, bSplitArcs)) return false ; break ; } } // incremento numero di svuotature ++ m_nPockets ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetProjectionOfStmToNotPocket( const ISurfTriMesh* pStmPart, const ISurfTriMesh* pStmVol, ISurfFlatRegion* pSfrProj) { // controllo i parametri if ( pStmPart == nullptr || ! pStmPart->IsValid() || pStmVol == nullptr || ! pStmVol->IsValid()) return false ; // ricavo la parte di grezzo da non rovinare PtrOwner pStmToNotRuin( CloneSurfTriMesh( pStmPart)) ; if ( IsNull( pStmToNotRuin) || ! pStmToNotRuin->IsValid() || ! pStmToNotRuin->Subtract( *pStmVol) || ! pStmToNotRuin->IsValid()) return false ; // NB. La parte vera a propria di interesse è quella sopra la piano Z = 0 // creo il piano Z = 0 Plane3d plZ0 ; plZ0.Set( ORIG, - Z_AX) ; if ( ! plZ0.IsValid()) return false ; // taglio la supericie if ( ! pStmToNotRuin->Cut( plZ0, true)) return false ; // se non ho ottenuto nulla, esco if ( ! pStmToNotRuin->IsValid() || pStmToNotRuin->GetTriangleCount() == 0) return true ; // Proietto la TriMesh plZ0.Invert() ; PtrOwner pSfrLimitH( CreateSurfFlatRegion()) ; if ( IsNull( pSfrLimitH) || ! ProjectStmOnPlane( pStmToNotRuin, plZ0, pSfrLimitH)) return false ; // controllo per estrema sicurezza la coerenza con le normali if ( AreOppositeVectorApprox( pSfrLimitH->GetNormVersor(), Z_AX)) pSfrLimitH->Invert() ; // ritorno la superficie pSfrProj->CopyFrom( pSfrLimitH) ; return pSfrProj->IsValid() && pSfrProj->GetChunkCount() > 0 ; } //---------------------------------------------------------------------------- bool Pocketing::SliceVolume( const ISurfTriMesh* pStmVol, const ISurfTriMesh* pStm_Part, ISURFFRPOVECTOR& vSrfSliced, std::vector& vCrvOEWithFlags, BOOLVECTOR& vbChangedPrec, VCT3DVECTOR& vVtTrasl, int& nStep, const Vector3d vtTool, const double dElev, const double dDepth, const double dStep) { // controllo dei parametri if ( pStmVol == nullptr || ! pStmVol->IsValid() || pStm_Part == nullptr || ! pStm_Part->IsValid()) return false ; // NB. Ogni volta che esco dal volume di svuotura rischio con il Tool di rovinare delle parti al di fuori del volume // di svuotatura... // -> quando proeitto la parte di volume compresa tra lo step attuale e lo step precedente ( ProjectVolume) // -> quando mi estendo presso i lati aperti ( ModifySurfByOpenEdges) // Devo limitare la mia regione si svuotatura estesa mediante la proeizione della parte di grezzo che non voglio // svuotare PtrOwner pSfrLimit( CreateSurfFlatRegion()) ; if ( ! GetProjectionOfStmToNotPocket( pStm_Part, pStmVol, pSfrLimit)) return false ; vVtTrasl.clear() ; // creazione di un vettore con gli step ( caso base) for ( int j = 1 ; j <= nStep ; ++ j) vVtTrasl.push_back( -Z_AX * ( dDepth - dElev + j * dStep)) ; // calcolo di eventuali Step Extra // W.I.P // aggirno le dimensioni dei vettori da restituire in base agli step calcolati vSrfSliced.clear() ; vSrfSliced.resize( int( vVtTrasl.size())) ; vCrvOEWithFlags.clear() ; vCrvOEWithFlags.resize( int( vVtTrasl.size())) ; vbChangedPrec.clear() ; vbChangedPrec.resize( int( vVtTrasl.size())) ; // per ogni step ricavato... for ( int j = 1 ; j <= int( vVtTrasl.size()) ; ++ j) { // porto la regione limite allo step attuale if ( pSfrLimit->IsValid()) pSfrLimit->Translate( vVtTrasl[j-1]) ; // taglio il volume con il piano di svuotatura attuale PtrOwner pSfrCurr( CreateSurfFlatRegion()) ; if ( IsNull( pSfrCurr) || ! CutVolumeByPlane( pStmVol, pStm_Part, vVtTrasl[j-1], pSfrCurr)) return false ; // tolgo/aggiungo eventuali proiezioni if ( ! ProjectVolume( pStmVol, pStm_Part, vVtTrasl[j-1], j == 1 ? V_INVALID : vVtTrasl[j-2], pSfrLimit, pSfrCurr)) return false ; // ricavo le proprietà di lato aperto/chiuso if ( ! ChooseCloseOrOpenEdge( pSfrCurr, pStm_Part)) return false ; // salvo le curve originali per casi ottimizzati if ( ! GetCurvesForOptimizedPocketing( pSfrCurr, vCrvOEWithFlags[j-1])) return false ; // modifico la supericie in base alle proprietà di lato aperto/chiuso PtrOwner pSfrNoExtension( CloneSurfFlatRegion( pSfrCurr)) ; if ( IsNull( pSfrNoExtension) || ! ModifySurfByOpenEdges( pSfrCurr, pSfrLimit)) return false ; // controllo se la superficie deriva da una lavorazione precedente if ( ! GetNewSfrByAnotherPocketing( pSfrCurr, pSfrNoExtension, pStmVol, pSfrLimit)) return false ; // inserisco la superficie nel vettore vSrfSliced.emplace_back( Release( pSfrCurr)) ; // controllo se la superficie è uguale a quella inserita nello step precedente // ... W.I.P ... vbChangedPrec[j-1] = true ; // ... // porto la regione limite nel piano Z = 0 if ( pSfrLimit->IsValid()) pSfrLimit->Translate( -vVtTrasl[j-1]) ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetNewSfrByAnotherPocketing( ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrNoExtendedByOpenEdges, const ISurfTriMesh* pStmVol, const ISurfFlatRegion* pSfrLimit) { // se non ho una lavorazione precedente o non ho nulla da svuotare, esco if ( m_dDiam_Prec < EPS_SMALL || ! pSfrPock->IsValid() || ! pSfrNoExtendedByOpenEdges->IsValid()) return true ; // controllo effettivamente che il nuovo utensile possa svuotare qualcosa nella nuova lavorazione if ( 0.5 * m_TParams.m_dDiam + GetOffsR() > 0.5 * m_dDiam_Prec + m_dOffsetR_Prec) return true ; // effettuo un Offset verso l'interno ( curve esterne che percorre il centro del Tool) double dInsideOffs = - 0.5 * m_dDiam_Prec - 0.5 * m_dOffsetR_Prec - 5 * EPS_SMALL ; PtrOwner pSfrToolPath( pSfrPock->CreateOffsetSurf( dInsideOffs, ICurve::OFF_FILLET)) ; if ( IsNull( pSfrToolPath)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } if ( ! pSfrToolPath->IsValid()) return true ; // effettuo un Offset del raggio del tool precedente per ricavare la regione svuotata in precedenza double dOutSideOffset = 0.5 * m_dDiam_Prec + 20 * EPS_SMALL ; PtrOwner pSfrRemoved( pSfrToolPath->CreateOffsetSurf( dOutSideOffset, ICurve::OFF_FILLET)) ; if ( IsNull( pSfrRemoved)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } // definisco la regione vera a propria da svuotare con il tool attuale PtrOwner pSfrAct( CloneSurfFlatRegion( pSfrNoExtendedByOpenEdges)) ; if ( IsNull( pSfrAct) || ! pSfrAct->Subtract( *pSfrRemoved)) return false ; // Questa superficie presenterà diversi Chunks... per ognuno di essi ricavo le properità di lato aperto o chiuso // NB. Questa supericie non dovrebbe avere isole... // Posso richiamare la funzione ChooseCloseOrOpenEdge() tra la pSfrAct e il Volume di svuotatura // alzo leggermente la supericie per l'ultimi step pSfrAct->Translate( Z_AX * 50 * EPS_SMALL) ; if ( ! ChooseCloseOrOpenEdge( pSfrAct, pStmVol)) return false ; pSfrAct->Translate( -Z_AX * 50 * EPS_SMALL) ; // Questa funzione ha modificato le TmpProp dei Loops della pSfrAct e le temp prop sono invertite ; // I lati con tmp prop = 1 sono chiusi, mentre quelli con tmpProp = 0 sono aperti // modifico il Loop esterno di Ogni chunk della regione, estendendolo presso i lati aperti // creo quindi una nuova superficie con questi Loops SurfFlatRegionByContours sfrBC ; for ( int c = 0 ; c < pSfrAct->GetChunkCount() ; ++ c) { // ricavo il Loop esterno PtrOwner pCrvEL( ConvertCurveToComposite( pSfrAct->GetLoop( c, 0))) ; if ( IsNull( pCrvEL) || ! pCrvEL->IsValid()) return false ; // inverto le Tmp Props for ( int u = 0 ; u < pCrvEL->GetCurveCount() ; ++ u) pCrvEL->SetCurveTempProp( u, pCrvEL->GetCurve( u)->GetTempProp( 0) == 0 ? 1 : 0, 0) ; // ricavo le isole ICRVCOMPOPOVECTOR vCrvIsl ; for ( int l = 1 ; l < pSfrAct->GetLoopCount( c) ; ++ l) vCrvIsl.emplace_back( ConvertCurveToComposite( pSfrAct->GetLoop( c, l))) ; // inverto le temp props per le isole trovate for ( int i = 0 ; i < int( vCrvIsl.size()) ; ++ i) for ( int u = 0 ; u < vCrvIsl[i]->GetCurveCount() ; ++ u) vCrvIsl[i]->SetCurveTempProp( u, vCrvIsl[i]->GetCurve( u)->GetTempProp( 0) == 0 ? 1 : 0, 0) ; // estendo il loop esterno presso i nuovi lati aperti if ( ! AdjustContourWithOpenEdges( pCrvEL, vCrvIsl, m_TParams.m_dDiam, GetOffsR(), GetSideStep(), pSfrLimit)) { m_pMchMgr->SetLastError( 2430, "Error in Pocketing : adjust open edges failed") ; return false ; } // estendo eventuali isole presso i nuovi lati aperti ICRVCOMPOPOVECTOR vCrvNULL ; for ( int i = 0 ; i < int( vCrvIsl.size()) ; ++ i) { if ( ! AdjustContourWithOpenEdges( vCrvIsl[i], vCrvNULL, m_TParams.m_dDiam, GetOffsR(), GetSideStep(), pSfrLimit)) { m_pMchMgr->SetLastError( 2430, "Error in Pocketing : adjust open edges failed") ; return false ; } } // avendo i lati aperti, devo stare attento alle entrate... per sicurezza li imposto tutti come chiusi ( per ora..) for ( int u = 0 ; u < pCrvEL->GetCurveCount() ; ++ u) pCrvEL->SetCurveTempProp( u, 0, 0) ; for ( int i = 0 ; i < int( vCrvIsl.size()) ; ++ i) for ( int u = 0 ; u < vCrvIsl[i]->GetCurveCount() ; ++ u) vCrvIsl[i]->SetCurveTempProp( u, 0, 0) ; // aggiungo il Loop esterno e le isole sfrBC.AddCurve( Release( pCrvEL)) ; for ( int i = 0 ; i < int( vCrvIsl.size()) ; ++ i) sfrBC.AddCurve( Release( vCrvIsl[i])) ; } // ricavo la nuova superfcicie con i Loops modificati PtrOwner pSfrFinal( sfrBC.GetSurf()) ; if ( IsNull( pSfrFinal) || ! pSfrFinal->IsValid()) return false ; // sostituisco ed esco pSfrPock->Clear() ; pSfrPock->CopyFrom( pSfrFinal) ; return pSfrPock != nullptr && pSfrPock->IsValid() ; } //---------------------------------------------------------------------------- bool Pocketing::SewingMissingFacesOnPlanes( ISurfTriMesh* pStm, const Plane3d& plPlane) { // controllo dei parametri if ( pStm == nullptr || ! pStm->IsValid() || ! plPlane.IsValid()) return false ; // recupero tutti i Loop ( se presenti) POLYLINEVECTOR vPL ; pStm->GetLoops( vPL) ; for ( int p = 0 ; p < ( int)vPL.size() ; ++ p) { // controllo la PolyLine è contenuta nel piano dove svuoto, altrimenti non la considero Plane3d plCheck ; double dArea ; Point3d ptCheck ; if ( vPL[p].IsClosedAndFlat( plCheck, dArea)) { if ( vPL[p].GetPointNbr() > 0) { if ( vPL[p].GetFirstPoint( ptCheck)) { double dDist = DistPointPlane( ptCheck, plPlane) ; if ( abs( dDist) > 50 * EPS_SMALL) continue ; } } } // recupero la curva PtrOwner pCrvLoop( CreateCurveComposite()) ; if ( IsNull( pCrvLoop) || ! pCrvLoop->FromPolyLine( vPL[p]) || ! pCrvLoop->IsValid()) return false ; // creo la TriMesh della faccia mancante PtrOwner pStmMissingFace( GetSurfTriMeshByFlatContour( pCrvLoop)) ; if ( IsNull( pStmMissingFace) || ! pStmMissingFace->IsValid()) return false ; // controllo che sia orintata correttamente ( il volume deve essere interno) Vector3d vtN_f ; pStmMissingFace->GetFacetNormal( 0, vtN_f) ; if ( AreOppositeVectorApprox( plPlane.GetVersN(), vtN_f)) pStmMissingFace->Invert() ; // unisco if ( ! pStm->DoSewing( *pStmMissingFace)) return false ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::CutVolumeByPlane( const ISurfTriMesh* pStmVol, const ISurfTriMesh* pStm_Part, const Vector3d& vtTrasl, ISurfFlatRegion* pSfrResult) { // controllo dei parametri if ( pStmVol == nullptr || ! pStmVol->IsValid() || pStm_Part == nullptr || ! pStm_Part->IsValid()) return false ; pSfrResult->Clear() ; // creo un piano allo step attuale Point3d ptCenter = ORIG + vtTrasl ; Vector3d vtN = vtTrasl.IsSmall() ? Z_AX : vtTrasl ; vtN.Normalize() ; Plane3d plCut ; plCut.Set( ptCenter, -vtN) ; if ( ! plCut.IsValid()) return false ; // taglio il volume con il piano attuale PtrOwner pStmSlice( CloneSurfTriMesh( pStmVol)) ; if ( IsNull( pStmSlice) || ! pStmSlice->IsValid() || ! pStmSlice->Cut( plCut, false)) // non salvo quelli sullo stesso livello del piano ! return false ; // se non ho ricavato nulla, esco if ( ! pStmSlice->IsValid() || pStmSlice->GetTriangleCount() == 0) return true ; // aggiungo le facce mancanti dovute al taglio if ( ! SewingMissingFacesOnPlanes( pStmSlice, plCut)) return false ; // FlatRegion con lati flag di lato Aperto/Chiuso SurfFlatRegionByContours SfrBC_final ; // scorro tutte le facce della TriMesh ricavata for ( int f = 0 ; f < pStmSlice->GetFacetCount() ; ++ f) { // le facce che formeranno la FlatRegion di svuotatura sono quelle alla quota definita da vtTrasl Point3d ptCentroid ; Vector3d vtNormal ; if ( ! pStmSlice->GetFacetCenter( f, ptCentroid, vtNormal) || abs( ptCentroid.z - ptCenter.z) > 300 * EPS_SMALL || // se faccia non sul piano di taglio... ! AreOppositeVectorApprox( Z_AX, vtNormal)) // ... o con diversa normale.... continue ; // ... controllo la successiva // recupero i contorni della faccia POLYLINEVECTOR vPL ; pStmSlice->GetFacetLoops( f, vPL) ; if ( vPL.empty()) return false ; // scorro i loop trovati for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { // creo la curva a partire dalla PolyLine PtrOwner pCrvCompo( CreateCurveComposite()) ; pCrvCompo->FromPolyLine( vPL[i]) ; if ( ! pCrvCompo->IsValid()) return false ; // reset proprietà temporanee ResetCurveAllTempProp( pCrvCompo) ; // la proietto sul piano per maggiore sicurezza PtrOwner pCrvProj( ProjectCurveOnPlane( *pCrvCompo, plCut)) ; if ( IsNull( pCrvProj)) return false ; if ( pCrvProj->IsValid()) pCrvCompo.Set( ConvertCurveToComposite( Release( pCrvProj))) ; // abbellisco pCrvCompo->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; // aggiorno la curva alla FlatRegion finale SfrBC_final.AddCurve( Release( pCrvCompo)) ; } } // recupero la FlatRegion da restituire ( per approssimazione potrei avere piccoli sfalsamenti lungo Z_AX) PtrOwner pSfr_Final( SfrBC_final.GetSurf()) ; while( ! IsNull( pSfr_Final) && pSfr_Final->IsValid()) { // controllo che sia orientata come Z_AX if ( AreOppositeVectorApprox( Z_AX, pSfr_Final->GetNormVersor())) pSfr_Final->Invert() ; // prendo la supercicie e aggiorno quella da restituire if ( ! pSfrResult->IsValid()) pSfrResult->CopyFrom( pSfr_Final) ; else if ( ! pSfrResult->Add( * pSfr_Final)) return false ; pSfr_Final.Set( SfrBC_final.GetSurf()) ; } // restituisco return ! ( pSfrResult == nullptr || ! pSfrResult->IsValid()) ; } //---------------------------------------------------------------------------- bool Pocketing::ProjectVolume( const ISurfTriMesh* pStmVol, const ISurfTriMesh* pStm_Part, const Vector3d& vtTrasl, const Vector3d& vtTrasl1, ISurfFlatRegion* pSfrLimit, ISurfFlatRegion* pSfr) { // controllo dei parametri if ( pStmVol == nullptr || ! pStmVol->IsValid() || pStm_Part == nullptr || ! pStm_Part->IsValid() || pSfr == nullptr || ! pSfr->IsValid()) return false ; // controllo se il tool ha un SideAngle if ( m_TParams.m_dSideAng != 0) { // ... controllo che gli angoli delle facce siano coerenti al tool selezionato // ... // ... // ... return true ; } /* Vengono creati due piano di Taglio : plNear e plFar 1) plNear : piano di taglio allo step attuale con normale Z_AX 2) plFar : piano di taglio riferito allo step successivo ( se lo step è il primo allora è impostato ad una quota alta) Ogni faccia del volume di svuotatura ( stm) è classificata come Aperta ( A) o Chiusa ( C) A : questa faccia corrisponde ad una faccia della Part C : questa faccia deriva dalla TriMesh selezionata ( o curve, vedi funzione GetCurvesAndPartialVolume()) * le facce C vanno proiettate sul piano di svuotatura attuale e sottratte alla *pSfr -> si usa solo il plNear * le facce A sono quelle che verranno utilizzate per la proiezione della Part sul piano di svuotatura attuale -> si usa sia il plNear che il plFar * il volume di svuotatura viene tagliato dal plNear e plFar, ottenendo la parte compresa tra i due piani * ( in questo modo la proiezione sul piano di svuotatura considera svuotato tutto ciò che è sopra allo step precedente, quindi con quota Z maggiore di esso ) * la proiezione consiste quindi nel proiettare le facce A di questa parte di Volume di Pocketing. */ // plNear Point3d ptNear = ORIG + vtTrasl ; Vector3d vtN = Z_AX ; Plane3d plNear ; plNear.Set( ptNear, -vtN) ; if ( ! plNear.IsValid()) return false ; // plFar Point3d ptFar = ORIG + ( vtTrasl1.IsValid() ? vtTrasl1 : 5000 * Z_AX) ; Plane3d plFar ; plFar.Set( ptFar, vtN) ; if ( ! plFar.IsValid()) return false ; // ricavo il volume di svuotatura sopra al piano plNear PtrOwner pStmAbove_Near( CloneSurfTriMesh( pStmVol)) ; if ( IsNull( pStmAbove_Near) || ! pStmAbove_Near->IsValid() || ! pStmAbove_Near->Cut( plNear, false)) // non salvo quelli sullo stesso livello del piano ! return false ; // se non ho ricavato nulla, esco if ( ! pStmAbove_Near->IsValid() || pStmAbove_Near->GetTriangleCount() == 0) return true ; // ricavo il volume di svuotatura tra plNear e PlFar PtrOwner pStmBetween_Planes( CloneSurfTriMesh( pStmAbove_Near)) ; if ( IsNull( pStmBetween_Planes) || ! pStmBetween_Planes->IsValid() || ! pStmBetween_Planes->Cut( plFar, false)) return false ; // se non ho ricavato nulla, esco if ( ! pStmAbove_Near->IsValid() || pStmBetween_Planes->GetTriangleCount() == 0) return true ; // -------------------------------------------------------------------------------------------------- // per la superficie tra i due piani ricavo gli indici dei triangoli delle facce C ( tutti questi triangoli andranno // rimossi lasciando quindi solo le facce A) // NB. sul piano far la TriMesh non presenta facce, bisogna riaggiungerle if ( ! SewingMissingFacesOnPlanes( pStmBetween_Planes, plFar) || ! SewingMissingFacesOnPlanes( pStmBetween_Planes, plNear)) return false ; INTVECTOR vAllTria_C_between ; for ( int f = 0 ; f < pStmBetween_Planes->GetFacetCount() ; ++ f) { // ricavo i parametri della faccia Point3d ptC_f ; Vector3d vtN_f ; if ( ! pStmBetween_Planes->GetFacetCenter( f, ptC_f, vtN_f)) return false ; // controllo se si tratta di una faccia interna alla Part DistPointSurfTm distPtStm( ptC_f, *pStm_Part) ; if ( distPtStm.IsPointInside()) { // se faccia interna, prendo tutti i suoi tringoli ... INTVECTOR vT ; if ( ! pStmBetween_Planes->GetAllTriaInFacet( f, vT)) return false ; // ... e li aggiungo i loro indici al vettore for ( int t = 0 ; t < int( vT.size()) ; ++ t) vAllTria_C_between.push_back( vT[t]) ; } } // dalla superficie tra i due piani rimouovo i triangoli ottenuti for ( int t = 0 ; t < int( vAllTria_C_between.size()) ; ++ t) if ( ! pStmBetween_Planes->RemoveTriangle( vAllTria_C_between[t])) return false ; pStmBetween_Planes->DoCompacting() ; // se non ho ricavato nulla, allora significa che tutte le facce del volume di svuotatura comprese tra plNear e // plFar sono chiuse, quindi non ho nulla da proiettare PtrOwner pSfr_ExtendProjection( CreateSurfFlatRegion()) ; if ( IsNull( pSfr_ExtendProjection)) return false ; if ( pStmBetween_Planes->IsValid() || pStmBetween_Planes->GetTriangleCount() > 0) { // tutta questa superficie contiene solo facce A, le proietto sul piano plNear if ( ! ProjectStmOnPlane( pStmBetween_Planes, plNear, pSfr_ExtendProjection)) return false ; // controllo ( per estrema sicurezza) la coerenza con le normali if ( AreOppositeVectorApprox( pSfr->GetNormVersor(), pSfr_ExtendProjection->GetNormVersor())) pSfr_ExtendProjection->Invert() ; // NB. Questa superficie esce dal volume definito, devo stare attento a non rovinare parti di grezzo if ( pSfrLimit->IsValid() && pSfr_ExtendProjection->IsValid()) { if ( ! pSfr_ExtendProjection->Subtract( *pSfrLimit)) return false ; } } // ------------------------------------------------------------------------------------------------- // per pStmAbove_Near ricavo gli indici dei triangoli delle facce A ( tutti questi triangoli andranno rimossi // lasciando quindi solo le facce C) INTVECTOR vAllTria_A ; for ( int f = 0 ; f < pStmAbove_Near->GetFacetCount() ; ++ f) { // ricavo i parametri della faccia Point3d ptC_f ; Vector3d vtN_f ; if ( ! pStmAbove_Near->GetFacetCenter( f, ptC_f, vtN_f)) return false ; // controllo se si tratta di una faccia interna alla Part DistPointSurfTm distPtStm( ptC_f, *pStm_Part) ; if ( ! distPtStm.IsPointInside()) { // se faccia interna, prendo tutti i suoi tringoli ... INTVECTOR vT ; if ( ! pStmAbove_Near->GetAllTriaInFacet( f, vT)) return false ; // ... e li aggiungo i loro indici al vettore for ( int t = 0 ; t < int( vT.size()) ; ++ t) vAllTria_A.push_back( vT[t]) ; } } // dalla superficie sopra al piano di svuotatura rimouovo i triangoli ottenuti for ( int t = 0 ; t < int( vAllTria_A.size()) ; ++ t) if ( ! pStmAbove_Near->RemoveTriangle( vAllTria_A[t])) return false ; pStmAbove_Near->DoCompacting() ; // se non ho ricavato nulla, significa che il volume di svuotatura ( tagliato con il piano, e quindi senza le // facce da svuotare) descrive la stessa geometria della Part ( quindi ogni faccia è sulla Part) // ---> non c'è nulla da proiettare PtrOwner pSfrProjection( CreateSurfFlatRegion()) ; if ( IsNull( pSfrProjection)) return false ; if ( pStmAbove_Near->IsValid() && pStmAbove_Near->GetTriangleCount() > 0) { // tutta questa superficie contiene solo facce C, le proietto sul piano plNear if ( ! ProjectStmOnPlane( pStmAbove_Near, plNear, pSfrProjection)) return false ; // controllo ( per estrema sicurezza) la coerenza con le normali if ( AreOppositeVectorApprox( pSfr->GetNormVersor(), pSfrProjection->GetNormVersor())) pSfrProjection->Invert() ; } // ------------------------------------------------------------------------------------------------- // 1) La superficie con le facce A tra i due piani plNear e plFar indentificano una proeizione che estende la // superficie attuale da svuotare // 2) La superficie con le facce C al di sopra del piano plNear identificano una proeizione che riduce la // superficie attuale da svuotare // 1) ricavo la superificie da svuotare come addizione tra la ( *Sfr) e la ( *pSfr_ExtendProjection) if ( pSfr_ExtendProjection->IsValid()) if ( ! pSfr->Add( *pSfr_ExtendProjection)) return false ; // 2) ricavo la superficie da svuotare come sottrazione tra la ( *pSfr) e la ( *pSfrProjection) if ( pSfr->IsValid() && pSfrProjection->IsValid()) if ( ! pSfr->Subtract( *pSfrProjection)) return false ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::ChooseCloseOrOpenEdge( ISurfFlatRegion* pSfr, const ISurfTriMesh* pStm_Part) { // controllo parametri : if ( pSfr == nullptr || ! pSfr->IsValid()) return true ; // <- se superficie non valida, allora non ho niente da impostare sui suoi lati if ( pStm_Part == nullptr || ! pStm_Part->IsValid()) return false ; // per ogni curva dei Loop della FlatRegion vengono presi 4 punti di controllo equidistanti. // se tali punti risultano TUTTI non interni alla Part, allora il lato viene considerato aperto // " IL LATO E' APERTO <=> TUTTI I PUNTI DI CONTROLLO NON SONO DENTRO AL FINITO " // NB. Aggiungendo delle aree proiettando le parti aperte del volume di Pocketing, alcuni tratti possono risultare // in parte interni ed in parte esterni alla Part... ( questi lati rimangono chiusi, infatti lasciandoli aperti // rischierei di rovinare la part al di fuori del volume di svuotatura. const int NUM_POINTS = 4 ; PtrOwner pSfrWithTmpProps( CreateSurfFlatRegion()) ; if ( IsNull( pSfrWithTmpProps)) return false ; // scorro tutti i loop for ( int c = 0 ; c < pSfr->GetChunkCount() ; ++ c) { for ( int l = 0 ; l < pSfr->GetLoopCount( c) ; ++l) { // recupero la curva composita del Loop PtrOwner pCrvCompoLoop( ConvertCurveToComposite( pSfr->GetLoop( c, l))) ; if ( IsNull( pCrvCompoLoop) || ! pCrvCompoLoop->IsValid()) return false ; // abbellisco pCrvCompoLoop->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; // scorro ogni sua sottocurva for ( int u = 0 ; u < pCrvCompoLoop->GetCurveCount() ; ++ u) { // recupero la sottocurva const ICurve* pCrv = pCrvCompoLoop->GetCurve( u) ; if ( pCrv == nullptr) return false ; // recupero i 4 punti bool bIsClosed = false ; // <-- assumo che la curva sia Aperta for ( int p = 0 ; p < NUM_POINTS + 1 && ! bIsClosed ; ++ p) { double dPar = ( 1. / ( 1. * NUM_POINTS)) * p ; Point3d ptPar ; if ( ! pCrv->GetPointD1D2( dPar, ICurve::FROM_PLUS, ptPar)) return false ; DistPointSurfTm distPtStm( ptPar, *pStm_Part) ; bIsClosed = distPtStm.IsPointInside() ; } pCrvCompoLoop->SetCurveTempProp( u, int( ! bIsClosed), 0) ; } // aggiungo il Loop con Flag di lato Aperto/Chiuso alla superficie if ( l == 0) { if ( ! pSfrWithTmpProps->AddExtLoop( *pCrvCompoLoop)) return false ; } else if ( ! pSfrWithTmpProps->AddIntLoop( *pCrvCompoLoop)) return false ; } } // sostituisco la superficie da restituire if ( ! pSfrWithTmpProps->IsValid() || ! pSfr->Clear() || ! pSfr->CopyFrom( pSfrWithTmpProps)) return false ; return pSfr->IsValid() && pSfr->GetChunkCount() != 0 ; } //---------------------------------------------------------------------------- bool Pocketing::GetCurvesForOptimizedPocketing( 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 ; } //---------------------------------------------------------------------------- bool Pocketing::ModifySurfByOpenEdges( ISurfFlatRegion* pSfr, const ISurfFlatRegion* pSfrLimit) { // 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 superifcie 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, m_dDiam_Prec > 0 ? m_dDiam_Prec : m_TParams.m_dDiam, m_dDiam_Prec > 0 ? m_dOffsetR_Prec : GetOffsR(), m_dDiam_Prec > 0 ? m_dSideStep_Prec : GetSideStep(), pSfrLimit)) { m_pMchMgr->SetLastError( 2430, "Error in Pocketing : adjust open edges failed") ; return false ; } bIsChunkModified = true ; // la curva è stata modificata } // 4) Controllo i bordi delle isole ottenute e modifico quelli in cui sono presenti lati aperti for ( int i = 0 ; i < ( int)vCrvIsl.size() ; ++ i) { bSomeOpen = false ; nProp0 = -1 ; PtrOwner pCrvIsl( ConvertCurveToComposite( vCrvIsl[i]->Clone())) ; for ( int u = 0 ; u < pCrvIsl->GetCurveCount() && ! bSomeOpen ; ++ u) { if ( pCrvIsl->GetCurveTempProp( u, nProp0, 0) && nProp0 == 1) bSomeOpen = true ; } if ( bSomeOpen) { // se trovo dei lati aperti ... // 4.1) se l'isola è tutta aperta e "trascurabile" la tolgo bool bRemove = false ; if ( ! CheckForRemovingIsland( pCrvIsl, m_dDiam_Prec > 0 ? m_dDiam_Prec + m_dOffsetR_Prec : m_TParams.m_dDiam, bRemove)) return false ; if ( bRemove) // se si può trascurare passo alla successiva continue ; // 4.2) sistemo la superificie ICRVCOMPOPOVECTOR vCrvNULL ; if ( ! AdjustContourWithOpenEdges( pCrvIsl, vCrvNULL, m_dDiam_Prec > 0 ? m_dDiam_Prec : m_TParams.m_dDiam, m_dDiam_Prec > 0 ? m_dOffsetR_Prec : GetOffsR(), m_dDiam_Prec > 0 ? m_dSideStep_Prec : GetSideStep(), pSfrLimit)) { m_pMchMgr->SetLastError( 2430, "Error in Pocketing : adjust open edges failed") ; return false ; } bIsChunkModified = true ; // la curva è stata modificata } vCrvToTIsland.emplace_back( pCrvIsl->Clone()) ; } // 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 ; } //---------------------------------------------------------------------------- bool Pocketing::CalcRegionElevation( const ICurveComposite* pCompo, const Vector3d& vtTool, double dDepth, double dRad, double& dElev) const { // DA ADATTARE ! //// inizializzo l'elevazione // dElev = 0 ; //// affondamento come vettore // Vector3d vtDepth = vtTool * dDepth ; //// Offsetto la curva della quantità stabvilità per la ricerca di proiezioni // OffsetCurve OffsCrv ; // if ( ! OffsCrv.Make( pCompo, m_dMaxLenRawProj, ICurve::OFF_FILLET)) { // m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; // return false ; // } // PtrOwner pCompoExtended( ConvertCurveToComposite( OffsCrv.GetLongerCurve())) ; // if ( IsNull( pCompoExtended) || ! pCompoExtended->IsValid()) // return false ; //// Campiono il contorno // int nMaxInd = pCompoExtended->GetCurveCount() - 1 ; // for ( int i = 0 ; i <= nMaxInd ; ++ i) { // // curva corrente // const ICurve* pCrvC = pCompoExtended->GetCurve( i) ; // Point3d ptStart ; pCrvC->GetStartPoint( ptStart) ; // Point3d ptMid ; pCrvC->GetMidPoint( ptMid) ; // Point3d ptEnd ; pCrvC->GetEndPoint( ptEnd) ; // // elevazione della curva // double dCurrElev ; // if ( GetElevation( m_nPhase, ptStart - vtDepth, ptMid - vtDepth, ptEnd - vtDepth, vtTool, dCurrElev)) { // if ( dCurrElev > dElev) // dElev = dCurrElev ; // } // else { // m_pMchMgr->SetLastError( 2408, "Error in Pocketing : Entity GetElevation") ; // return false ; // } // } //// Campiono l'interno con una griglia (uso linee parallele a X) //// determino il riferimento di base // Frame3d frPocket ; // Point3d ptCen ; pCompoExtended->GetCentroid( ptCen) ; // frPocket.Set( ptCen, vtTool) ; //// copio il contorno e lo porto nel riferimento // PtrOwner pCompoL( pCompoExtended->Clone()) ; // if ( IsNull( pCompoL) || ! pCompoL->ToLoc( frPocket)) { // m_pMchMgr->SetLastError( 2408, "Error in Pocketing : Entity GetElevation") ; // return false ; // } //// ingombro del contorno in locale // BBox3d b3Pocket ; // pCompoL->GetLocalBBox( b3Pocket) ; // Point3d ptMin ; double dDimX, dDimY, dDimZ ; // b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ; //// passi in Y // const double STEP = m_TParams.m_dDiam ; // int nYStep = max( int( ceil( ( dDimY - 20 * EPS_SMALL) / STEP)), 2) ; // double dYStep = ( nYStep > 0 ? ( dDimY - 20 * EPS_SMALL) / nYStep : 0) ; //// calcolo le linee di svuotatura // for ( int i = 1 ; i < nYStep ; ++ i) { // // definisco la linea // PtrOwner pLine( CreateCurveLine()) ; // const double EXP_LEN = 1.0 ; // Point3d ptStart( ptMin.x - EXP_LEN, ptMin.y + 10 * EPS_SMALL + i * dYStep, ptMin.z + dDimZ) ; // if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN)) { // m_pMchMgr->SetLastError( 2408, "Error in Pocketing : Entity GetElevation") ; // return false ; // } // // calcolo la classificazione della curva rispetto al contorno // IntersCurveCurve intCC( *pLine, *pCompoL) ; // CRVCVECTOR ccClass ; // if ( intCC.GetCurveClassification( 0, EPS_SMALL, ccClass)) { // // determino gli intervalli di curva interni // Intervals inOk ; // for ( auto& ccOne : ccClass) { // if ( ccOne.nClass == CRVC_IN) { // // ricavo punto iniziale e finale // Point3d ptStart ; // pLine->GetPointD1D2( ccOne.dParS, ICurve::FROM_PLUS, ptStart) ; // Point3d ptEnd ; // pLine->GetPointD1D2( ccOne.dParE, ICurve::FROM_MINUS, ptEnd) ; // // calcolo la distanza tra questi due punti // double dDist = Dist( ptStart, ptEnd) ; // // il tratto interno lo campiono rispetto ad X // for ( int j = 0 ; j <= int( dDist / STEP) + 1 ; ++ j) { // Point3d ptCurr ; // pLine->GetPointD1D2( ccOne.dParS + j * ( ccOne.dParE - ccOne.dParS) / ( dDist / STEP + 1), ICurve::FROM_PLUS, ptCurr) ; // ptCurr.ToGlob( frPocket) ; // double dCurrElev ; // if ( GetElevation( m_nPhase, ptCurr - vtDepth, vtTool, dCurrElev)) { // if ( dCurrElev > dElev) // dElev = dCurrElev ; // } // else { // m_pMchMgr->SetLastError( 2408, "Error in Pocketing : Entity GetElevation") ; // return false ; // } // } // } // } // } // // altrimenti uso tutta la curva // else { // Point3d ptStart ; // pLine->GetStartPoint( ptStart) ; // ptStart.ToGlob( frPocket) ; // Point3d ptEnd ; // pLine->GetEndPoint( ptEnd) ; // ptEnd.ToGlob( frPocket) ; // // elevazione della curva // double dCurrElev ; // if ( GetElevation( m_nPhase, ptStart - vtDepth, ptEnd - vtDepth, vtTool, dCurrElev)) { // if ( dCurrElev > dElev) // dElev = dCurrElev ; // } // else { // m_pMchMgr->SetLastError( 2408, "Error in Pocketing : Entity GetElevation") ; // return false ; // } // } // } return true ; } //---------------------------------------------------------------------------- bool Pocketing::VerifyPathFromBottom( const ISurfFlatRegion* pSrfPock, const Vector3d& vtTool) { // se non è svuotatura dal basso in alto, esco if ( vtTool.z > MIN_ZDIR_TOP_TOOL) return true ; // se c'è testa non dall'alto o tavola basculante, esco if ( ! m_bAboveHead || m_bTiltingTab) return true ; // recupero dati di eventuale rinvio da sotto if ( ! GetAggrBottomData( m_TParams.m_sHead, m_AggrBottom) || m_AggrBottom.nType == 0) { m_pMchMgr->SetLastError( 2409, "Error in Pocketing : missing aggregate from bottom") ; return false ; } // calcolo il minimo della massima distanza del percorso dal contorno del grezzo double dMinDist = INFINITO ; Vector3d vtMinDir ; VCT3DVECTOR vDir ; for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; c++) { for ( int l = 0 ; l < pSrfPock->GetLoopCount( c) ; l++) { PtrOwner pCompo( GetCurveComposite( pSrfPock->GetLoop( c, l))) ; if ( IsNull( pCompo)) return false ; double dParS, dParE ; pCompo->GetDomain( dParS, dParE) ; for ( double dPar = dParS ; dPar < dParE + EPS_PARAM ; dPar += 0.5) { // distanza minima del punto e relativa direzione dal contorno del grezzo Point3d ptP ; double dCurrDist = INFINITO ; Vector3d vtCurrDir ; if ( pCompo->GetPointD1D2( dPar, ICurve::FROM_MINUS, ptP) && GetMinDistanceFromRawSide( m_nPhase, ptP, 0, m_AggrBottom.vtMDir, MCH_AGB_DELTAMAX_MDIR, dCurrDist, vtCurrDir) && ! vtCurrDir.IsSmallXY()) { if ( dCurrDist < dMinDist - 10 * EPS_SMALL && find_if( vDir.begin(), vDir.end(), [&]( const Vector3d& vtV) { return vtCurrDir * vtV > cos(15 * DEGTORAD); }) == vDir.end()) { // inserisco la direzione tra quelle già esplorate vDir.emplace_back( vtCurrDir) ; // determino la distanza di tutti gli altri punti dal contorno del grezzo lungo questa direzione for ( double dPar2 = dParS; dPar2 < dParE + EPS_PARAM; dPar2 += 0.5) { if ( abs( dPar2 - dPar) > EPS_PARAM) { Point3d ptQ ; double dQDist ; if ( pCompo->GetPointD1D2( dPar2, ICurve::FROM_MINUS, ptQ) && GetDistanceFromRawSide( m_nPhase, ptQ, vtCurrDir, dQDist) && dQDist > dCurrDist) dCurrDist = dQDist ; } } // se la massima distanza trovata è inferiore al minimo, lo aggiorno if ( dCurrDist < dMinDist) { dMinDist = dCurrDist ; vtMinDir = vtCurrDir ; } } } } } } // se supera il limite, errore if ( dMinDist > m_AggrBottom.dDMax) { m_pMchMgr->SetLastError( 2410, "Error in Pocketing : path too far from part sides") ; return false ; } // assegno direzione di accesso e segnalo utilizzo aggregato da sotto m_vtAggrBottom = vtMinDir ; m_bAggrBottom = true ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::GeneratePocketingPv( int nPathId, const ISurfFlatRegion* pSrfPock) { // creo copia della curva composita PtrOwner pSfr( CloneSurfFlatRegion( pSrfPock)) ; if ( IsNull( pSfr)) return false ; // ne recupero il contorno PtrOwner< ICurve> pCrv2 ; pCrv2.Set( pSfr->GetLoop( 0, 0)) ; if ( IsNull( pCrv2)) return false ; // inserisco la curva nel DB int nC2Id = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nPathId, Release( pCrv2)) ; if ( nC2Id == GDB_ID_NULL) return false ; // assegno nome e colore m_pGeomDB->SetName( nC2Id, MCH_PV_CUT) ; m_pGeomDB->SetMaterial( nC2Id, RED) ; // eventuali altri contorni ( interni di contornatura chiusa) const int MAX_INT_LOOP = 1000 ; for ( int i = 1 ; i <= MAX_INT_LOOP ; ++i) { PtrOwner< ICurve> pCrv3 ; pCrv3.Set( pSfr->GetLoop( 0, i)) ; if ( IsNull( pCrv3)) break ; // inserisco la curva nel DB int nC3Id = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nPathId, Release( pCrv3)) ; if ( nC3Id == GDB_ID_NULL) return false ; // assegno nome e colore m_pGeomDB->SetName( nC3Id, MCH_PV_CUT) ; m_pGeomDB->SetMaterial( nC3Id, RED) ; } // inserisco la regione nel DB int nRId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nPathId, Release( pSfr)) ; if ( nRId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nRId, MCH_PV_RCUT) ; m_pGeomDB->SetMaterial( nRId, Color( 255, 0, 0, 60)) ; // la copio anche come regione ridotta int nRrId = m_pGeomDB->Copy( nRId, GDB_ID_NULL, nPathId) ; if ( nRrId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nRrId, MCH_PV_RRCUT) ; m_pGeomDB->SetMaterial( nRrId, INVISIBLE) ; return true ; } //---------------------------------------------------------------------------- static double GetCurveRadius( const ICurve* pCrv) { if ( pCrv == nullptr) return 0.0 ; BBox3d b3Loc ; if ( ! pCrv->GetLocalBBox( b3Loc, BBF_EXACT)) return 0.0 ; double dRad ; if ( ! b3Loc.GetRadius( dRad)) return 0.0 ; return dRad ; } //---------------------------------------------------------------------------- bool Pocketing::AddZigZag( ISURFFRPOVECTOR& vSfr, const std::vector& vCrvOrig, BOOLVECTOR& vbChangedPrec, VCT3DVECTOR& vVtTrasl, const Vector3d& vtTool, const Vector3d& vtExtr, double dDepth, double dElev, double dMaxElev, double dOkStep, bool bSplitArcs) { // recupero distanze di sicurezza double dSafeZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeZ() ; double dSafeAggrBottZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeAggrBottZ() ; // lunghezza di approccio/retrazione double dAppr = m_Params.m_dStartPos ; // ricavo il numero degli step int nStep = int( vSfr.size()) ; // raggio utensile double dTRad = m_TParams.m_dDiam / 2 ; // Offset della regione per curva ZigZag double dOffs = dTRad + GetOffsR() ; double dExtra = (( m_TParams.m_nType != TT_MILL_POLISHING) ? min( 0.1 * m_TParams.m_dDiam, 2.0) : 0) ; // inizializzazione delle informazioni necessarie per eventuale superficie uguale per lo step successivo const int CRV_BORDER = 1 ; struct CrvInfo { PtrOwner pCrvCompo ; int nCrvType = 0 ; Point3d ptStart = ORIG ; bool bMidOut = false ; Vector3d vtMidOut = V_NULL ; int nChunk ; } ; struct ChunkInfo { bool bOptZigZag = false ; int nOptClosedSides = 0 ; bool bOptTwoOpposite = false ; Frame3d frFrame = GLOB_FRM ; double dOptZigZagOffs = 0. ; } ; // dichiarazione vettori info per steps vector vCrvInfo ; vector vChunkInfo ; // scorro il vettore delle FlatRegion ( calcolate per ogni step j-esimo) for ( int j = 1 ; j <= nStep ; ++ j) { // se superficie non valida, passo alla prossima if ( IsNull( vSfr[j-1]) || ! vSfr[j-1]->IsValid()) continue ; // superficie per ingressi ed uscite PtrOwner pSrfLeanInOut( CloneSurfFlatRegion( vSfr[j-1])) ; if ( IsNull( pSrfLeanInOut)) return false ; // se la superficie è cambiata ( o sono al primo step), reset dei parametri step precedente e ricalcolo if ( vbChangedPrec[j-1] || j == 1) { vCrvInfo.clear() ; vChunkInfo.clear() ; vChunkInfo.resize( vSfr[j-1]->GetChunkCount()) ; // ciclo sui chunk della superificie da svuotare for ( int c = 0 ; c < vSfr[j-1]->GetChunkCount() ; ++ c) { // copio il chunk c-esimo PtrOwner pSrfChunk(( vSfr[j-1]->CloneChunk( c))) ; if ( IsNull( pSrfChunk)) return 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, j, pSrfChunk, nInd)) 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, vtExtr) ; frPocket.Rotate( ptCen, vtExtr, m_Params.m_dSideAngle) ; // porto la superificie nel nuovo sistema di riferimento pSrfChunk->ToLoc( frPocket) ; // verifico se si tratta di caso ottimizzato bool bOptimizedZigZag = false ; ICRVCOMPOPOVECTOR vpCrvs ; // vettore con i paths a ZigZag per il Chunk (cc)-esimo allo step (j)-esimo ICRVCOMPOPOVECTOR vCrvIslMergeBorders ; // vettore delle possibili isole mergiate per il Chunk (cc)-esimo allo step (j)-esimo double dOptZigZagOffs = 0. ; // Offset per uscita dal grezzo nel caso di ottimizzazione int nClosedSides = 0 ; // se caso ottimizzato, numero di lati chiusi bool bTwoOpposite = false ; // se caso otimizzato, flag per lati chiusi opposti if ( ! OptimizedZigZag( pSrfChunk, vtTool, dDepth, dSafeZ, frPocket, vCrvOrig[j-1][nInd], bOptimizedZigZag, vpCrvs, vCrvIslMergeBorders, dOptZigZagOffs, nClosedSides, bTwoOpposite)) return false ; // aggiorno le informazioni sul chunk attuale ( potrebbero essere usate per il successivo) vChunkInfo[c].bOptZigZag = bOptimizedZigZag ; vChunkInfo[c].nOptClosedSides = nClosedSides ; vChunkInfo[c].bOptTwoOpposite = bTwoOpposite ; vChunkInfo[c].frFrame = frPocket ; vChunkInfo[c].dOptZigZagOffs = dOptZigZagOffs ; // se CASO OTTIMIZZATO e vettore di curve ZigZag valido if ( bOptimizedZigZag) { vCrvInfo.resize( int( vpCrvs.size()) + int( vCrvIslMergeBorders.size())) ; // aggiorno le curve per lo step attuale ( potrebbero essere usate per il successivo) for ( int x = 0 ; x < int( vpCrvs.size()) ; ++ x) { vCrvInfo[x].pCrvCompo.Set( vpCrvs[x]->Clone()) ; Point3d ptS ; vCrvInfo[x].pCrvCompo->GetStartPoint( ptS) ; vCrvInfo[x].ptStart = ptS ; vCrvInfo[x].nChunk = c ; } // creo localmente i parametri della funzione VCT3DVECTOR vVtMidOut(( int)vCrvIslMergeBorders.size()) ; BOOLVECTOR vbMidOut(( int)vCrvIslMergeBorders.size()) ; PNTVECTOR vPtStart(( int)vCrvIslMergeBorders.size()) ; // se la superficie è cambiata, allora ricalcolo i parametri for ( int x = 0 ; x < int( vCrvIslMergeBorders.size()) ; ++ x) { // se richisto, inverto if ( m_Params.m_bInvert) vCrvIslMergeBorders[x]->Invert() ; // setto la Feed per la curva di contorno //AssignFeedForEdgeCleaning( vCrvIslMergeBorders[x], vCrvOEWithFlags[j-1], nInd) ; if ( vCrvIslMergeBorders[x]->IsClosed()) { bool bOutTmp = false ; if ( ! SetBetterPtStartForSubChunks( vCrvIslMergeBorders[x], pSrfChunk, vPtStart[x], vVtMidOut[x], bOutTmp)) return false ; vbMidOut[x] = bOutTmp ; // vector::reference da Bit a Bool } else { vCrvIslMergeBorders[x]->GetStartPoint( vPtStart[x]) ; vbMidOut[x] = false ; // ... migliorabile vVtMidOut[x] = V_NULL ; // ... migliorabile } } // guardo il punto finale in cui si trova la fresa dopo al percorso a ZigZag // e riordino le curve ( e i relativi vettori) in base alla vicinanza // ( piccola ottimizzazione per l'ordine dei percorsi sui bordi ) Point3d ptEnd ; if ( vpCrvs.size() > 0) vpCrvs.back()->GetEndPoint( ptEnd) ; if ( int( vCrvIslMergeBorders.size()) != 0 && ! OrderCurvesByLastPntOfPath( vCrvIslMergeBorders, ptEnd, vPtStart, vVtMidOut, vbMidOut)) return false ; int nOff_index = int( vpCrvs.size()) ; for ( int x = 0 ; x < int( vCrvIslMergeBorders.size()) ; ++ x) { vCrvInfo[nOff_index + x].pCrvCompo.Set( Release( vCrvIslMergeBorders[x])) ; vCrvInfo[nOff_index + x].nCrvType = CRV_BORDER ; vCrvInfo[nOff_index + x].ptStart = vPtStart[x] ; vCrvInfo[nOff_index + x].bMidOut = vbMidOut[x] ; vCrvInfo[nOff_index + x].vtMidOut = vVtMidOut[x] ; vCrvInfo[nOff_index + x].nChunk = c ; } } // se CASO NON OTTIMIZZATO else { // effettuo il primo Offset della regione e controllo quanti Chunks ottengo... // l'Offset contiene una quantità Extra, per stare leggermente più staccato dal bordo PtrOwner pSrfZigZag( CloneSurfFlatRegion( pSrfChunk)) ; if ( ! pSrfZigZag->Offset( - dOffs - dExtra, ICurve::OFF_FILLET)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } // effettuo un secondo Offset per ottenere le curve di contorno per ripulire la svuotatura a ZigZag // Questo offset serve per ricavare le curve che l'utensile dovrà percorrere dopo aver svuotato a ZigZag // Curva esterna ( non percorsa nel caso a ZigZag ottimizzato) // Curve interne ( le isole, queste vanno percorse anche nei casi ottimizzati) // Per le curve interne, questo Offset viene sempre fatto ( a prescindere dai casi ottimizzati ) PtrOwner pSrfForCrv( CloneSurfFlatRegion( pSrfChunk)) ; if ( ! pSrfForCrv->Offset( - dOffs, ICurve::OFF_FILLET)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } if ( ! pSrfForCrv->IsValid()) { pSrfForCrv.Set( CloneSurfFlatRegion( pSrfChunk)) ; if ( ! pSrfForCrv->Offset( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } } // Avendo effettuato due Offsets ( -dOffs-dExtra e -dOffs ) non è detto che il numero di Chunks tra le nuove // superifici sia uguale... Inizio a scorre i chunks della superficie per le curve di contorno e, // per ognuna di esse, cerco il/i Chunck per il percorso a ZigZag contenuti. for ( int cfc = 0 ; cfc < pSrfForCrv->GetChunkCount() ; ++ cfc) { // scorro i chunk della superificie per i bordi for ( int cfz = 0 ; cfz < pSrfZigZag->GetChunkCount() ; ++ cfz) { // scorro i chunk della superificie per i percorsi ZigZag // e la regione è dentro al Chunk per i contorni if ( pSrfZigZag->GetChunkSimpleClassification( cfz, *pSrfForCrv, cfc) == REGC_IN1) { // svuoto il vettore delle curve ( potrebbe essere riempito dal percorso precedente vpCrvs.clear() ; // Calcolo il percorso a ZigZag PtrOwner pSrfZigZagChunk( pSrfZigZag->CloneChunk( cfz)) ; if ( ! CalcZigZag( pSrfZigZagChunk, vpCrvs)) return false ; // memorizzo le curve ( per lo step successivo) int nIndex_offs = int( vCrvInfo.size()) ; vCrvInfo.resize( int( vpCrvs.size()) + nIndex_offs) ; for ( int x = 0 ; x < int( vpCrvs.size()) ; ++ x) { vCrvInfo[nIndex_offs + x].pCrvCompo.Set( Release( vpCrvs[x])) ; vCrvInfo[nIndex_offs + x].nChunk = c ; } } // se la regione non è dentro alla curva esterna allora la ignoro // ( la ritroverò interna ad un'altra curva per i bordi successivamante o l'ho già considerata in precedenza) else continue ; } // se lucidatura non aggiungo contorno if ( m_TParams.m_nType == TT_MILL_POLISHING) continue ; // Una volta percorse le curve di ZigZag devo aggiungere le curve per il bordo // creo un vettore con tutti i loop formati, aggiustando i loro punti iniziali // e portandoli nel frame della svuotatura ICRVCOMPOPOVECTOR vAllCrv ; VCT3DVECTOR vVtMidOut( pSrfForCrv->GetLoopCount( cfc)) ; BOOLVECTOR vbMidOut( pSrfForCrv->GetLoopCount( cfc)) ; PNTVECTOR vPtStart( pSrfForCrv->GetLoopCount( cfc)) ; for ( int l = 0 ; l < pSrfForCrv->GetLoopCount( cfc) ; ++ l) { PtrOwner pCrvLoop( ConvertCurveToComposite( pSrfForCrv->GetLoop( cfc, l))) ; if ( IsNull( pCrvLoop)) return false ; bool bOutTmp = false ; if ( ! SetBetterPtStartForSubChunks( pCrvLoop, pSrfChunk, vPtStart[l], vVtMidOut[l], bOutTmp)) return false ; vbMidOut[l] = bOutTmp ; // vector::reference da Bit a Bool // se richiesto, la inverto if ( m_Params.m_bInvert) pCrvLoop->Invert() ; // setto la Feed per la curva di contorno //AssignFeedForEdgeCleaning( pCrvLoop, vCrvOEWithFlags[j-1], nInd) ; // inserisco la curva nel vettore vAllCrv.emplace_back( Release( pCrvLoop)) ; } // guardo il punto finale in cui si trova la fresa dopo al percorso a ZigZag // e le riordino le curve ( e i relativi vettori ) in base alla vicinanza // ( piccola ottimizzazione per l'ordine dei percorsi sui bordi ) Point3d ptEnd ; if ( vpCrvs.size() > 0) vCrvInfo.back().pCrvCompo->GetEndPoint( ptEnd) ; // riordino le curve if ( ! OrderCurvesByLastPntOfPath( vAllCrv, ptEnd, vPtStart, vVtMidOut, vbMidOut)) return false ; int nIndex_offs = int( vCrvInfo.size()) ; vCrvInfo.resize( int( vAllCrv.size()) + nIndex_offs) ; for ( int x = 0 ; x < int( vAllCrv.size()) ; ++ x) { vCrvInfo[nIndex_offs + x].pCrvCompo.Set( Release( vAllCrv[x])) ; vCrvInfo[nIndex_offs + x].nCrvType = CRV_BORDER ; vCrvInfo[nIndex_offs + x].bMidOut = vbMidOut[x] ; vCrvInfo[nIndex_offs + x].ptStart = vPtStart[x] ; vCrvInfo[nIndex_offs + x].vtMidOut = vVtMidOut[x] ; vCrvInfo[nIndex_offs + x].nChunk = c ; } } } } // disegno a seconda del tipo di curva che ho ricavato for ( int u = 0 ; u < int( vCrvInfo.size()) ; ++ u) { // prendo la curva corrente PtrOwner pCrvCurr( vCrvInfo[u].pCrvCompo->Clone()) ; if ( IsNull( pCrvCurr)) return false ; // recupero il frame attuale e porto la curva in globale Frame3d frCurr = vChunkInfo[vCrvInfo[u].nChunk].frFrame ; pCrvCurr->ToGlob( frCurr) ; // flag per LeadIn fuori/dentro al grezzo bool bOutRawLeadIn = false ; // determino l'affondamento attuale double dSink = dElev - dDepth + dMaxElev - vVtTrasl[j-1].Len() ; // =================== CURVA PERCORSO ZIGZAG ============================================== if ( vCrvInfo[u].nCrvType == 0) { // se caso ottimizzato bool bOptZigZag = vChunkInfo[vCrvInfo[u].nChunk].bOptZigZag ; if ( bOptZigZag) { // recupero l'Offset ottimizzato precedentemente calcolato double dOffsOpt = vChunkInfo[vCrvInfo[u].nChunk].dOptZigZagOffs ; // recupero il numero di lati chiusi int nClosedSide = vChunkInfo[vCrvInfo[u].nChunk].nOptClosedSides ; // recupero flat di due lati chiusi paralleli bool bOptTwoOpposite = vChunkInfo[vCrvInfo[u].nChunk].bOptTwoOpposite ; // se un lato chiuso if ( nClosedSide == 1) { // inverto il percorso ( parto dall'aperto) pCrvCurr->Invert() ; // verifico se attacco fuori dal grezzo ( allungandolo) Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ; Vector3d vtDir ; pCrvCurr->GetStartDir( vtDir) ; Point3d ptTest = ptStart + ( - vtDir) * ( m_TParams.m_dDiam / 2 - dOffsOpt + max( dSafeZ, m_dOpenMinSafe)) ; ptTest.Translate( vVtTrasl[j-1]) ; double dTestElev ; // se è nel grezzo provo a ruotare di 90 gradi if ( GetElevation( m_nPhase, ptTest, vtTool, m_TParams.m_dDiam / 2 - dOffsOpt, vtTool, dTestElev) && dTestElev > EPS_SMALL) { Vector3d vtDirO = vtDir ; vtDirO.Rotate( vtTool, ( m_Params.m_bInvert ? -90 : 90)) ; Point3d ptTestO = ptStart + vtDirO * ( m_TParams.m_dDiam / 2 - dOffsOpt + max( dSafeZ, m_dOpenMinSafe)) ; ptTestO += - vtTool * dDepth ; double dTestElevO ; // se è fuori dal grezzo uso inizio ruotato if ( ! GetElevation( m_nPhase, ptTestO, vtTool, m_TParams.m_dDiam / 2 - dOffsOpt, vtTool, dTestElevO) || dTestElevO < EPS_SMALL) { Point3d ptNewStart = ptStart + vtDirO ; pCrvCurr->AddLine( ptNewStart, false) ; } } } // se due lati chiusi consecutivi o tre lati chiusi... else if (( nClosedSide == 2 || nClosedSide == 3) && ! bOptTwoOpposite) { // inverto il percorso pCrvCurr->Invert() ; // allungo opportunamente inizio e fine Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ; Vector3d vtDir ; pCrvCurr->GetStartDir( vtDir) ; vtDir.Rotate( frCurr.VersZ(), ( m_Params.m_bInvert ? -90 : 90)) ; ptStart += vtDir ; Point3d ptEnd ; pCrvCurr->GetEndPoint( ptEnd) ; ptEnd += OrthoCompo( ptStart - ptEnd, frCurr.VersX()) ; pCrvCurr->AddLine( ptEnd, true) ; pCrvCurr->AddLine( ptStart, false) ; } // estendo la fine del percorso if (( nClosedSide == 2 && ! bOptTwoOpposite) || ( nClosedSide == 3 && ! bOptTwoOpposite)) pCrvCurr->ExtendEndByLen( max( m_TParams.m_dDiam / 4 - dOffsOpt, 0.0)) ; // aggiungo il tratto per l'entrata, verifico se attacco fuori dal grezzo Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ; Vector3d vtDir ; pCrvCurr->GetStartDir( vtDir) ; ptStart += -vtDir * ( m_TParams.m_dDiam / 2 - dOffsOpt + max( dSafeZ, m_dOpenMinSafe)) ; ptStart += -vtTool * dDepth ; // controllo l'elevazione double dTestElev ; if ( ! GetElevation( m_nPhase, ptStart, vtTool, m_TParams.m_dDiam / 2, vtTool, dTestElev) || dTestElev < EPS_SMALL) bOutRawLeadIn = true ; // sistemo attacco ( per essere completamente fuori dal grezzo) if ( bOutRawLeadIn || m_bOpenOutRaw) pCrvCurr->ExtendStartByLen( m_TParams.m_dDiam / 2 - dOffsOpt + max( dSafeZ, m_dOpenMinSafe)) ; } // se lucidatura if ( m_TParams.m_nType == TT_MILL_POLISHING) { // se attacco a scivolo if ( GetLeadInType() == POCKET_LI_GLIDE) { double dU; pCrvCurr->GetParamAtLength( m_Params.m_dLiTang, dU) ; pCrvCurr->AddJoint( dU) ; Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ; pCrvCurr->ModifyStart( ptStart + vtTool * m_Params.m_dLiElev) ; } // se uscita a scivolo if ( GetLeadOutType() == POCKET_LO_GLIDE) { double dLen, dU ; pCrvCurr->GetLength( dLen) ; pCrvCurr->GetParamAtLength( dLen - m_Params.m_dLoTang, dU) ; pCrvCurr->AddJoint( dU) ; Point3d ptEnd ; pCrvCurr->GetEndPoint( ptEnd) ; pCrvCurr->ModifyEnd( ptEnd + vtTool * m_Params.m_dLiElev) ; } } // ---------------------------- Disegno le curve a ZigZag (vpCrv) ------------------------- bool bStart = true ; // ciclo sulle curve elementari int nMaxInd = pCrvCurr->GetCurveCount() - 1 ; for ( int i = 0 ; i <= nMaxInd ; ++ i) { // curva corrente const ICurve* pCrvC = pCrvCurr->GetCurve( i) ; // copio la curva e la traslo allo step richisto PtrOwner pCurve( pCrvC->Clone()) ; if ( IsNull( pCurve)) return false; // --- se PRIMA ENTITA' --- if ( i == 0) { // dati inizio entità Point3d ptStart ; pCurve->GetStartPoint( ptStart) ; Vector3d vtStart ; pCurve->GetStartDir( vtStart) ; // determino inizio attacco Point3d ptP1 ; if ( ! CalcLeadInStart( ptStart, vtStart, vtExtr, nullptr, ptP1)) return false ; // determino elevazione su inizio attacco double dStElev = dSink ; dStElev -= ( ptP1 - ptStart) * vtExtr ; // se ottimizzata e attacco nel grezzo if ( bOptZigZag && !( bOutRawLeadIn || m_bOpenOutRaw)) { // se richiesto attacco a zigzag o a spirale, l'elevazione va nell'attacco if ( GetLeadInType() == POCKET_LI_ZIGZAG || GetLeadInType() == POCKET_LI_HELIX) { ptP1 += vtExtr * dStElev ; dStElev = 0 ; } } // se inizio, approccio globale al punto iniziale if ( bStart) { if ( ! AddApproach( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr, bOutRawLeadIn || m_bOpenOutRaw)) { m_pMchMgr->SetLastError( 2414, "Error in Pocketing : Approach not computable") ; return false ; } bStart = false ; } // altrimenti, approccio di collegamento else { if ( ! ( nStep > 1 && j > 1 && bOutRawLeadIn && bOptZigZag)) { if ( ! AddLinkApproach( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr, bOutRawLeadIn || m_bOpenOutRaw)) { m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ; return false ; } } } // aggiungo attacco SetFeed( GetStartFeed()) ; if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, nullptr, !m_Params.m_bInvert, bSplitArcs, bOutRawLeadIn || m_bOpenOutRaw)) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } } // elaborazioni sulla curva corrente //double dMinFeed = GetFeed() * GetSideStep() / m_TParams.m_dDiam ; //double dFeed = pCurve->GetTempProp( 0) / FEED_DIVISOR < dMinFeed - 50 * EPS_SMALL ? // GetFeed() : pCurve->GetTempProp( 0) / FEED_DIVISOR ; //SetFeed( dFeed) ; if ( pCurve->GetType() == CRV_LINE) { ICurveLine* pLine = GetCurveLine( pCurve) ; Point3d ptP3 = pLine->GetEnd() ; SetFeed( GetFeed()) ; if ( AddLinearMove( ptP3) == GDB_ID_NULL) return false ; } else if ( pCurve->GetType() == CRV_ARC) { ICurveArc* pArc = GetCurveArc( pCurve) ; Point3d ptCen = pArc->GetCenter() ; double dAngCen = pArc->GetAngCenter() ; Vector3d vtN = pArc->GetNormVersor() ; Point3d ptP3 ; pArc->GetEndPoint( ptP3) ; SetFeed( GetFeed()) ; if ( AddArcMove( ptP3, ptCen, dAngCen, vtN) == GDB_ID_NULL) return false ; } // --- se ULTIMA ENTITA' --- if ( i == nMaxInd) { // dati fine entità Point3d ptEnd ; pCurve->GetEndPoint( ptEnd) ; Vector3d vtEnd ; pCurve->GetEndDir( vtEnd) ; // aggiungo uscita double dEndElev = dSink ; Point3d ptP1 ; SetFeed( GetEndFeed()) ; if ( ! AddLeadOut( ptEnd, vtEnd, vtExtr, nullptr, bSplitArcs, true, ptP1, dEndElev, false)) { m_pMchMgr->SetLastError( 2416, "Error in Pocketing : LeadOut not computable") ; return false ; } // se lucidatura o caso ottimizzato e ultimo percorso di ultimo step, aggiungo retrazione if (( m_TParams.m_nType == TT_MILL_POLISHING || bOptZigZag) && j == nStep) { if ( ! AddRetract( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) { m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Retract not computable") ; return false ; } } // altrimenti, aggiungo retrazione di collegamento else { if ( ! AddLinkRetract( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) { m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ; return false ; } } } } } // =================== CURVA BORDO ESTERNO ============================================== else { bool bForcedOutStart = false ; // se la curva è valida per entrata da fuori, aggiungo un piccolo tratto lineare if ( vCrvInfo[u].bMidOut) { // calcolo il punto fuori Point3d ptOut = vCrvInfo[u].ptStart + vCrvInfo[u].vtMidOut * ( 0.5 * m_TParams.m_dDiam + max( dSafeZ, m_dOpenMinSafe)) ; ptOut.ToGlob( frCurr) ; ptOut.Translate( vVtTrasl[j-1]) ; double dStElev ; bool bOutStart = ( ! GetElevation( m_nPhase, ptOut, vtTool, 0.5 * m_TParams.m_dDiam, vtTool, dStElev) || dStElev < EPS_SMALL) ; if ( bOutStart || m_bOpenOutRaw) { // aggiungo alla curva il tratto lineare ptOut.Translate( - vVtTrasl[j-1]) ; pCrvCurr->AddLine( ptOut, false) ; AssignFeedForLineInOut( pCrvCurr, true) ; } // verifico se ingresso da considerare fuori grezzo anche se dentro bForcedOutStart = ( vCrvInfo[u].bMidOut && m_bOpenOutRaw) ; // se utensile che non lavora di testa e ingresso non fuori dal pezzo, errore if ( m_TParams.m_nType == TT_MILL_NOTIP && !bOutStart && ! bForcedOutStart) { if ( ! LeadInRawIsOk()) { m_pMchMgr->SetLastError( 2431, "Error in Pocketing : LeadIn with Mill NoTip in material") ; return false; } } } // ----- disegno le curve di contorno ------------------- // aggiungo la lavorazione di questa curva Point3d ptP1 ; // ciclo sulle curve elementari int nMaxInd = pCrvCurr->GetCurveCount() - 1 ; for ( int i = 0 ; i <= nMaxInd ; ++i) { // curva corrente const ICurve* pCrvC = pCrvCurr->GetCurve( i) ; // copio la curva PtrOwner pCurve( pCrvC->Clone()) ; if ( IsNull( pCurve)) return false ; // --- se PRIMA ENTITA' --- if ( i == 0) { // dati inizio entità Point3d ptStart ; Vector3d vtStart ; pCurve->GetStartPoint( ptStart) ; pCurve->GetStartDir( vtStart) ; // se primo step, approccio e affondo // determino inizio attacco if ( ! CalcLeadInStart( ptStart, vtStart, vtExtr, nullptr, ptP1)) return false ; // determino elevazione su inizio attacco double dStElev = dSink ; dStElev -= ( ptP1 - ptStart) * vtExtr ; // se attacco a zigzag o a spirale o a scivolo, l'elevazione va nell'attacco if ( GetLeadInType() == POCKET_LI_ZIGZAG || GetLeadInType() == POCKET_LI_HELIX || GetLeadInType() == POCKET_LI_GLIDE) { ptP1 += vtExtr * ( dStElev + LIO_ELEV_TOL) ; dStElev = -LIO_ELEV_TOL ; } // approccio al punto iniziale if ( ! AddLinkApproach( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr, vCrvInfo[u].bMidOut || bForcedOutStart)) { m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ; return false ; } // aggiungo attacco SetFeed( GetStartFeed()) ; if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, nullptr, !m_Params.m_bInvert, bSplitArcs, vCrvInfo[u].bMidOut || bForcedOutStart)) { m_pMchMgr->SetLastError(2415, "Error in Pocketing : LeadIn not computable"); return false; } } // elaborazioni sulla curva corrente //double dMinFeed = GetFeed() * GetSideStep() / m_TParams.m_dDiam ; //double dFeed = pCurve->GetTempProp( 0) / FEED_DIVISOR < dMinFeed - 50 * EPS_SMALL ? // GetFeed() : pCurve->GetTempProp( 0) / FEED_DIVISOR ; //SetFeed( dFeed) ; if ( pCurve->GetType() == CRV_LINE) { ICurveLine* pLine( GetCurveLine( pCurve)) ; Point3d ptP3 = pLine->GetEnd() ; SetFeed( GetFeed()) ; if ( AddLinearMove( ptP3) == GDB_ID_NULL) return false ; } else if ( pCurve->GetType() == CRV_ARC) { ICurveArc* pArc( GetCurveArc( pCurve)) ; Point3d ptCen = pArc->GetCenter() ; double dAngCen = pArc->GetAngCenter() ; Vector3d vtN = pArc->GetNormVersor() ; Point3d ptP3 ; pArc->GetEndPoint( ptP3) ; SetFeed( GetFeed()) ; if ( AddArcMove( ptP3, ptCen, dAngCen, vtN) == GDB_ID_NULL) return false ; } // se ultima entità if ( i == nMaxInd) { // se ultimo step, uscita e retrazione di collegamento // dati fine entità Point3d ptEnd ; pCurve->GetEndPoint( ptEnd) ; Vector3d vtEnd ; pCurve->GetEndDir( vtEnd) ; // aggiungo uscita double dEndElev = dSink ; SetFeed( GetEndFeed()) ; if ( ! AddLeadOut( ptEnd, vtEnd, vtExtr, nullptr, bSplitArcs, false, ptP1, dEndElev, ( u == ( int)vCrvInfo.size()))) { m_pMchMgr->SetLastError( 2416, "Error in Pocketing : LeadOut not computable") ; return false ; } // se ci sono ancora curve, aggiungo retrazione di collegamento if ( pCrvCurr->GetCurveCount() > 0) { if ( ! AddLinkRetract( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) { m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ; return false ; } } // altrimenti retrazione finale else { if ( ! AddRetract( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) { m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Retract not computable") ; return false ; } } } } } } } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::CalcZigZag( const ISurfFlatRegion* pSrfZigZag, ICRVCOMPOPOVECTOR& vpCrvs) { // check parametri if ( pSrfZigZag == nullptr || ! pSrfZigZag->IsValid()) return true ; // creo il vettore dei contorni ICRVCOMPOPOVECTOR vFirstOff ; for ( int c = 0 ; c < pSrfZigZag->GetChunkCount() ; ++ c) { // sempre 1... 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 = static_cast< int >( ceil(( dDimY - 30 * EPS_SMALL) / GetSideStep())) ; double dYStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ; int nRef = (( nYStep + ( m_Params.m_bInvert ? 0 : 1)) % 2) ; // tratto valido struct Section { bool bActive ; Point3d ptS ; Point3d ptE ; double dOs ; double dOe ; int nOffIndS ; int nOffIndE ; } ; struct ParIsland { double dU ; int nInd ; } ; // raccolta di tratti typedef vector> VECVECSECT ; VECVECSECT vvSec ; vvSec.resize( nYStep + 1) ; // calcolo le linee di svuotatura int nCount = 0 ; for ( int i = 0 ; i <= nYStep ; ++ i) { // determino senso bool bPlus = (( i % 2) == nRef) ; // definisco la linea PtrOwner pLine( CreateCurveLine()) ; const double EXP_LEN = 1.0 ; Point3d ptStart( ptMin.x - EXP_LEN, ptMin.y + 10 * EPS_SMALL + i * dYStep, ptMin.z + dDimZ) ; if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN)) { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } if ( ! bPlus) pLine->Invert() ; // calcolo la classificazione della linea rispetto alla superificie 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) ; if ( ! AssignFeedZigZagOneWay( pLineCompo, false, vLineAbove, vLineUnder, vAddedLinks)) // Assegno la Feed return false ; bFirstLine = false ; vpCrvs.back()->AddCurve( pLineCompo->Clone()) ; // aggiungo la curva al percorso if ( nStatus == 0) { // primo segmento per il raccordo smussato pLastSeg.Set( pLineCompo) ; pLastSeg->GetStartPoint( ptS_ref) ; } if ( nStatus == 2) { // ho precedentemente aggiunto il bordo di un'isola, quindi mi salvo il secondo segmento PtrOwner pSeg1( CloneCurveComposite( pLastSeg)) ; PtrOwner pSeg2( CloneCurveComposite( pLineCompo)) ; if ( ! CalcZigZagLink( pSeg1, pSeg2, 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 ( ! AssignFeedZigZagOneWay( pCrvLink, true, vLineAbove, vLineUnder, vAddedLinks)) return false ; vpCrvs.back()->AddCurve( pCrvLink->Clone()) ; // aggiungo il Link vpCrvs.back()->AddCurve( pSeg2->Clone()) ; // aggiungo pSeg2 // aggiorno il vettore dei Link ( nel caso sia un link tra due segmenti sullo stesso livello ) Point3d ptS1 ; pSeg1->GetStartPoint( ptS1) ; Point3d ptS2 ; pSeg2->GetStartPoint( ptS2) ; if ( abs( ptS1.y - ptS2.y) < 50 * EPS_SMALL) vAddedLinks.emplace_back( pCrvLink->Clone()) ; // pSeg2 ora diventa pSeg1 e il procedimento si itera per tutto il percorso pLastSeg.Set( Release( 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 ; } //---------------------------------------------------------- bool Pocketing::GetUnclearedRegion( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vCrvs, ICURVEPOVECTOR& vLinks, const ICurveComposite* pCrv_orig, 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 = m_Params.m_nSubType == POCKET_SUB_SPIRALIN ? 0 : ( int)vCrvs.size() - 1 ; m_Params.m_nSubType == POCKET_SUB_SPIRALIN ? i < int( vCrvs.size()) : i >= 0 ; m_Params.m_nSubType == POCKET_SUB_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()) ; if ( ! AssignFeedSpiral( pCompoLink_i, pSrfTool_Offs, true, vLinks_done, pCrv_orig, m_TParams.m_dDiam / 3)) return false ; // per Link ... PtrOwner pCrvLink_i( vLinks[i]->Clone()) ; if ( IsNull( pCrvLink_i)) return false ; PtrOwner pSrfToolRegLinki( GetSurfFlatRegionFromFatCurve( Release( pCrvLink_i), m_TParams.m_dDiam / 2 + 5 * EPS_SMALL, false, false)) ; if ( ! IsNull( pSrfToolRegLinki)) { if ( ! pSrfTool_Links->IsValid() || pSrfTool_Links->GetChunkCount() == 0) pSrfTool_Links.Set( pSrfToolRegLinki) ; else pSrfTool_Links->Add( *pSrfToolRegLinki) ; } // risetto il Link come curva composita ... vLinks[i].Set( pCompoLink_i->Clone()) ; // inserisco il Link nel vettore dei Link percorsi vLinks_done.emplace_back( Release( pCompoLink_i)) ; } // ================== OFFSET ============================= // Feed if ( ! AssignFeedSpiral( vCrvs[i], pSrfTool_Offs, false, vLinks_done, pCrv_orig, m_TParams.m_dDiam / 3)) return false ; // aggiorno la superificie svuotata PtrOwner pCrvOffs_i( CloneCurveComposite( vCrvs[i])) ; if ( IsNull( pCrvOffs_i)) return false ; PtrOwner pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( Release( pCrvOffs_i) , m_TParams.m_dDiam / 2 + 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 ; } //---------------------------------------------------------------------------- bool Pocketing::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 ; } //---------------------------------------------------------- bool Pocketing::CalcZigZagLink( ICurveComposite* pCrv1, ICurveComposite* pCrv2, ICurveComposite* pCrvLink) { // 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 - ( m_TParams.m_dTDiam / 16)) / dLen1 ; // % parametro sinistro per smusso double dTollRight = ( m_TParams.m_dTDiam / 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, 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 - ( m_TParams.m_dTDiam / 16)) / dLenI_e ; // % paramtro sinistro per smusso dTollRight = ( m_TParams.m_dTDiam / 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, 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 ; } //---------------------------------------------------------- bool Pocketing::OptimizedZigZag( ISurfFlatRegion* pSrf, const Vector3d& vtTool, double dDepth, double dSafeZ, Frame3d& frPocket, ICurveComposite* pCrvOrig, bool& bOptimizedZigZag, ICRVCOMPOPOVECTOR& vpCrvs, ICRVCOMPOPOVECTOR& vCrvIslMergeBorders, double& dOffs, int& nClosedSides, bool& bTwoOpposite) { // clono la superficie (chunk c-esimo dell'originale) PtrOwner pSrfPocket( CloneSurfFlatRegion( pSrf)) ; if ( IsNull( pSrfPocket)) return false ; // recupero la curva dal bordo della superficie PtrOwner pCrvPocket( ConvertCurveToComposite( pSrfPocket->GetLoop( 0, 0))) ; if ( IsNull( pCrvPocket)) return false ; pCrvPocket->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; // sistemo senso antiorario visto dalla direzione di estrusione Vector3d vtPocket; pCrvPocket->GetExtrusion( vtPocket) ; Plane3d plPlane ; double dArea ; pCrvPocket->GetArea( plPlane, dArea) ; if ( plPlane.GetVersN() * vtPocket * dArea < 0) pCrvPocket->Invert() ; // recupero gli id dei lati chiusi INTVECTOR vnInfoClosed ; for ( int i = 0 ; i < pCrvPocket->GetCurveCount() ; i ++) { int nProp ; if ( pCrvPocket->GetCurveTempProp( i, nProp) && nProp == 0) vnInfoClosed.push_back( i) ; } nClosedSides = vnInfoClosed.size() ; // modifico pCrvPocket per poterla passare a CalcZigZag Vector3d vtDir ; // direzione principale del segmento più lungo (linea) switch ( nClosedSides) { case 0 : // 0 lati CHIUSI -> tutti APERTI ZigZagOptimizedNoClosedEdges( pCrvPocket, bOptimizedZigZag, vtDir, dOffs) ; break ; case 1 : // 1 lato CHIUSO ZigZagOptimizedOneClosedEdge( pCrvPocket, vnInfoClosed[0], bOptimizedZigZag, vtDir, dOffs) ; break ; case 2 : // 2 lati CHIUSI ZigZagOptimizedTwoClosedEdges( pCrvPocket, vnInfoClosed, bOptimizedZigZag, bTwoOpposite, vtDir, dOffs) ; break ; case 3 : // 3 lati CHIUSI ZigZagOptimizedThreeClosedEdges( pCrvPocket, vnInfoClosed, bOptimizedZigZag, bTwoOpposite, vtDir, dOffs) ; break ; default : // nessuna ottimizzazione... bOptimizedZigZag = false ; break ; } // se non sono in un caso ottimizzato, allora esco if ( ! bOptimizedZigZag) return true ; // frame per direzione più lunga per la svuotatura a ZigZag if ( vtDir.IsZero()) pCrvPocket->GetStartDir( vtDir) ; double dAng ; vtDir.GetAngleXY( X_AX, dAng) ; if ( nClosedSides == 0) dAng += m_Params.m_dSideAngle ; pSrfPocket->ToGlob( frPocket) ; pCrvPocket->ToGlob( frPocket) ; Frame3d frameH( frPocket) ; Point3d ptCen = frPocket.Orig() ; Vector3d vtExtr = frPocket.VersZ() ; frPocket.Rotate( ptCen, vtExtr, -dAng) ; pSrfPocket->ToLoc( frPocket) ; pCrvPocket->ToLoc( frPocket) ; // parametri per Offset double dTRad = m_TParams.m_dDiam / 2 ; double dMyOffs = dTRad + GetOffsR() ; double dExtra = (( m_TParams.m_nType != TT_MILL_POLISHING) ? min( 0.1 * m_TParams.m_dDiam, 2.0) : 0) ; // Offsetto la superficie per ricavare informazioni su eventuali isole e sulle loro possibili unioni PtrOwner pSfrZigZag( CreateSurfFlatRegion()) ; if ( IsNull( pSfrZigZag) || ! pSfrZigZag->AddExtLoop( *pCrvPocket)) return false ; PtrOwner pSfrForCrv( CloneSurfFlatRegion( pSfrZigZag)) ; if ( IsNull( pSfrForCrv)) return false ; for ( int l = 1 ; l < pSrfPocket->GetLoopCount( 0) ; ++ l) { PtrOwner pCrvLoopIsl( pSrfPocket->GetLoop( 0, l)) ; if ( IsNull( pCrvLoopIsl)) return false ; // aggiungo alla pSrfForCurves ( così non considero il dExtra) pSfrForCrv->AddIntLoop( *pCrvLoopIsl) ; // effettuo l'offset di dExtra ( cos' considero il dExtra come distanza dal bordo delle isole) pCrvLoopIsl->Invert() ; OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( pCrvLoopIsl, dExtra, ICurve::OFF_CHAMFER)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } PtrOwner pCrvBorderIsl( OffsCrv.GetLongerCurve()) ; if ( ! pSfrZigZag->AddIntLoop( *pCrvBorderIsl)) return false ; } // Offset per i bordi e percorsi a ZigZag if ( ! pSfrZigZag->Offset( - dMyOffs, ICurve::OFF_FILLET)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } if ( ! pSfrForCrv->Offset( - dMyOffs , ICurve::OFF_FILLET)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } if ( ! pSfrForCrv->IsValid()) { bOptimizedZigZag = false ; return true ; } // calcolo il percorso di svuotatura pSfrZigZag->ToGlob( frPocket) ; pSfrZigZag->ToLoc( frPocket) ; if ( ! CalcZigZag( pSfrZigZag, vpCrvs)) return false ; // controllo se il Loop esterno ha cambiato area ( in questo caso si è unito con un'isola e tale // bordo deve essere percorso double dAreaPrec, dAreaAft ; PtrOwner pCrvExtLoop( pSfrForCrv->GetLoop( 0, 0)) ; if ( IsNull( pCrvExtLoop)) return false ; Plane3d plH ; PtrOwner pSfrByCrvPocket( CreateSurfFlatRegion()) ; if ( IsNull( pSfrByCrvPocket) || ! pSfrByCrvPocket->AddExtLoop( *pCrvPocket)) return false ; if ( ! pSfrByCrvPocket->Offset( - dMyOffs, ICurve::OFF_CHAMFER)) return false ; if ( ! pSfrByCrvPocket->GetArea( dAreaPrec) || ! pCrvExtLoop->GetArea( plH, dAreaAft)) return false ; // se l'area è cambiata... if ( abs( dAreaPrec - dAreaAft) > 5000 * EPS_SMALL) { ICURVEPOVECTOR vCrv ; ChainCurves ChainC ; // creo una superficie come somma delle isole PtrOwner pSfrToTIsl( CreateSurfFlatRegion()) ; if ( IsNull( pSfrToTIsl)) return false ; // prendo i loop interni dalla superficie originale ! for ( int c = 0 ; c < pSrfPocket->GetChunkCount() ; ++ c) { // sempre 1... (?) for ( int l = 1 ; l < pSrfPocket->GetLoopCount( c) ; ++ l) { PtrOwner pCrvIslLoop( ConvertCurveToComposite( pSrfPocket->GetLoop( c, l))) ; if ( IsNull( pCrvIslLoop) || ! pCrvIslLoop->IsValid()) return false ; // creo la superficie dal bordo dell'isola // ( NB. facendo l'offset di un'isola non è detto che ottengo una sola curva) PtrOwner pSfrIslOff1( CreateSurfFlatRegion()) ; if ( IsNull( pSfrIslOff1) || ! pSfrIslOff1->AddExtLoop( *pCrvIslLoop)) return false ; // per sottrarre devono essere coerenti if ( AreOppositeVectorApprox( pSfrIslOff1->GetNormVersor(), pSfrZigZag->GetNormVersor())) pSfrIslOff1->Invert() ; // Offset 1 ed Offset 2 if ( ! pSfrIslOff1->Offset( dMyOffs + 50 * EPS_SMALL, ICurve::OFF_FILLET)) return false ; // aggiorno la regione totale delle isole if ( ! pSfrToTIsl->IsValid() || pSfrToTIsl->GetChunkCount() == 0) pSfrToTIsl.Set( pSfrIslOff1->Clone()) ; else pSfrToTIsl->Add( *pSfrIslOff1) ; } } // il bordo esterno si è unito all'isola => devo percorrere la parte di bordo interna all'isola for ( int c = 0 ; c < pSfrForCrv->GetChunkCount() ; ++ c) { const ICurve* pCrvExt = pSfrForCrv->GetLoop( c, 0) ; CRVCVECTOR ccClass ; if ( ! pSfrToTIsl->GetCurveClassification( *pCrvExt, EPS_SMALL, ccClass)) return false ; for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { if ( ccClass[j].nClass == CRVC_IN) { // recupero il tratto interno PtrOwner pCrv_In( pCrvExt->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ; if ( ! IsNull( pCrv_In)) vCrv.emplace_back( Release( pCrv_In)) ; } } if ( int( vCrv.size()) == 0) continue ; ChainC.Init( true, 10 * EPS_SMALL, int( vCrv.size())) ; for ( int k = 0 ; k < int( vCrv.size()) ; ++ k) { // recupero i dati della curva necessari al concatenamento e li assegno Point3d ptStart, ptEnd ; Vector3d vtStart, vtEnd ; if ( ! vCrv[k]->GetStartPoint( ptStart) || ! vCrv[k]->GetStartDir( vtStart) || ! vCrv[k]->GetEndPoint( ptEnd) || ! vCrv[k]->GetEndDir( vtEnd)) return false ; ChainC.AddCurve( k + 1, ptStart, vtStart, ptEnd, vtEnd) ; } Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ; INTVECTOR vInds ; while ( ChainC.GetChainFromNear( ptE, false, vInds)) { PtrOwner pCrvCompo_Curr( CreateCurveComposite()) ; if ( IsNull( pCrvCompo_Curr)) return false ; for ( int i = 0 ; i < int( vInds.size()) ; ++ i) { if ( vInds[i] < 0) vCrv[abs( vInds[i]) - 1]->Invert() ; pCrvCompo_Curr->AddCurve( Release( vCrv[abs( vInds[i]) - 1])) ; } if ( ! IsNull( pCrvCompo_Curr) && pCrvCompo_Curr->IsValid()) vCrvIslMergeBorders.emplace_back( Release( pCrvCompo_Curr)) ; } } } // inserisco a prescindere tutte le isole for ( int c = 0 ; c < pSfrForCrv->GetChunkCount() ; ++ c) for ( int l = 1 ; l < pSfrForCrv->GetLoopCount( c) ; ++ l) vCrvIslMergeBorders.emplace_back( ConvertCurveToComposite( pSfrForCrv->GetLoop( c, l))) ; return true ; } //------------------------------------------------------------------ bool Pocketing::ZigZagOptimizedNoClosedEdges( ICurveComposite* pCrvPocket, bool& bOptimizedZigZag, Vector3d& vtDir, double& dOffs) { // individuo il segmento di retta più lungo int nMax = -1 ; double dMaxLen = 0 ; for ( int i = 0 ; i < int( pCrvPocket->GetCurveCount()) ; ++ i) { const ICurve* pCrv = pCrvPocket->GetCurve( i) ; if ( pCrv->GetType() == CRV_LINE) { double dLen = 0 ; pCrv->GetLength( dLen) ; if ( dLen > dMaxLen) { dMaxLen = dLen ; nMax = i ; } } } if ( nMax == -1) return true ; pCrvPocket->GetCurve( nMax)->GetStartDir( vtDir) ; // aggiorno pCrvPocket con eventuale offset per regioni residue //if ( ! ZigZagOptimizedComputeOffset( pCrvPocket, vtDir, 0, {}, dOffs)) // return true ; OffsetCurve CrvOffs ; if ( ! CrvOffs.Make( pCrvPocket, 0.5 * m_TParams.m_dDiam, ICurve::OFF_CHAMFER)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } pCrvPocket->Clear() ; pCrvPocket->AddCurve( CrvOffs.GetLongerCurve()) ; if ( ! pCrvPocket->IsValid()) return false ; bOptimizedZigZag = true ; return true ; } //------------------------------------------------------------------ bool Pocketing::ZigZagOptimizedOneClosedEdge( ICurveComposite* pCrvPocket, int nClosedId, bool& bOptimizedZigZag, Vector3d& vtDir, double& dOffs) { // verifico che il lato chiuso sia una linea PtrOwner pCrv( CloneCurveLine( pCrvPocket->GetCurve( nClosedId))) ; if ( IsNull( pCrv)) return true ; // aggiorno pCrvPocket con eventuale offset per regioni residue pCrv->GetStartDir( vtDir) ; //if ( ! ZigZagOptimizedComputeOffset( pCrvPocket, vtDir, 1, {nClosedId}, dOffs)) // return true ; // Offset OffsetCurve CrvOffs ; if ( ! CrvOffs.Make( pCrvPocket, 0.5 * m_TParams.m_dDiam, ICurve::OFF_CHAMFER)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } pCrv->ExtendStartByLen( 300) ; pCrv->ExtendEndByLen( 300) ; pCrvPocket->Clear() ; pCrvPocket->AddCurve( CrvOffs.GetLongerCurve()) ; if ( ! pCrvPocket->IsValid() || ! CutCurveWithLine( pCrvPocket, pCrv)) return false ; pCrvPocket->ChangeStartPoint( nClosedId) ; // setto il vettore estrusione per eseguire correttamente offset // Vector3d vtExtr ; // pCrvPocket->GetExtrusion( vtExtr) ; // pCrv->SetExtrusion( vtExtr) ; // pCrv->SimpleOffset( -m_TParams.m_dDiam / 2 - GetOffsR() + 10 * EPS_SMALL) ; // pCrv->ExtendStartByLen( 300) ; // pCrv->ExtendEndByLen( 300) ; //// sarà la prima curva del percorso // if ( ! CutCurveWithLine( pCrvPocket, pCrv)) // return false ; bOptimizedZigZag = true ; return true ; } //------------------------------------------------------------------ bool Pocketing::ZigZagOptimizedTwoClosedEdges( ICurveComposite* pCrvPocket, const INTVECTOR& vnClosedIds, bool& bOptimizedZigZag, bool& bOpposite, Vector3d& vtDir, double& dOffs) { // verifico che i lati chiusi siano linee PtrOwner pCrv1( CloneCurveLine( pCrvPocket->GetCurve( vnClosedIds[0]))) ; PtrOwner pCrv2( CloneCurveLine( pCrvPocket->GetCurve( vnClosedIds[1]))) ; if ( IsNull( pCrv1) || IsNull( pCrv2)) return true ; // verifico abbiano direzioni opposte Vector3d vtDir1, vtDir2 ; pCrv1->GetStartDir( vtDir1) ; pCrv2->GetStartDir( vtDir2) ; if ( AreOppositeVectorApprox( vtDir1, vtDir2)) bOpposite = true ; else { // verifico siano consecutive Point3d ptStart1 ; pCrv1->GetStartPoint( ptStart1) ; Point3d ptEnd1 ; pCrv1->GetEndPoint( ptEnd1) ; Point3d ptStart2 ; pCrv2->GetStartPoint( ptStart2) ; Point3d ptEnd2 ; pCrv2->GetEndPoint( ptEnd2) ; if ( AreSamePointApprox( ptEnd1, ptStart2) || AreSamePointApprox( ptEnd2, ptStart1)) bOpposite = false ; else return true ; } // setto il vettore estrusione per eseguire correttamente offset //Vector3d vtExtr ; //pCrvPocket->GetExtrusion( vtExtr) ; //pCrv1->SetExtrusion( vtExtr) ; //pCrv2->SetExtrusion( vtExtr) ; // determino la curva chiusa più lunga double dLen1 ; pCrv1->GetLength( dLen1) ; double dLen2 ; pCrv2->GetLength( dLen2) ; // se non opposti, verifico che almeno una delle due sia corta if ( ! bOpposite && dLen1 > /*1.5 **/ m_TParams.m_dDiam && dLen2 > /*1.5 **/ m_TParams.m_dDiam) return true ; // taglio per ultima la curva più lunga if ( dLen1 > dLen2) { swap( pCrv1, pCrv2) ; swap( vtDir1, vtDir2) ; } vtDir = vtDir2 ; //if ( ! ZigZagOptimizedComputeOffset( pCrvPocket, vtDir, bOpposite ? 2 : 1, vnClosedIds, dOffs)) // return true ; // aggiorno pCrvPocket con eventuale offset per regioni residue OffsetCurve CrvOffs ; if ( ! CrvOffs.Make( pCrvPocket, 0.5 * m_TParams.m_dDiam, ICurve::OFF_CHAMFER)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } pCrvPocket->Clear() ; pCrvPocket->AddCurve( CrvOffs.GetLongerCurve()) ; pCrv1->ExtendStartByLen( 300) ; pCrv1->ExtendEndByLen( 300) ; pCrv2->ExtendStartByLen( 300) ; pCrv2->ExtendEndByLen( 300) ; if ( ! pCrvPocket->IsValid() || ! CutCurveWithLine( pCrvPocket, pCrv1) || ! CutCurveWithLine( pCrvPocket, pCrv2)) return false ; bOptimizedZigZag = true ; return true ; } //------------------------------------------------------------------ bool Pocketing::ZigZagOptimizedThreeClosedEdges( ICurveComposite* pCrvPocket, const INTVECTOR& vnClosedIds, bool& bOptimizedZigZag, bool& bOpposite, Vector3d& vtDir,double& dOffs) { // verifico che i lati chiusi siano linee PtrOwner pCrv1( CloneCurveLine( pCrvPocket->GetCurve( vnClosedIds[0]))) ; PtrOwner pCrv2( CloneCurveLine( pCrvPocket->GetCurve( vnClosedIds[1]))) ; PtrOwner pCrv3( CloneCurveLine( pCrvPocket->GetCurve( vnClosedIds[2]))) ; if ( IsNull( pCrv1) || IsNull( pCrv2) || IsNull( pCrv3)) return true ; // verifico siano consecutivi Point3d ptStart1 ; pCrv1->GetStartPoint( ptStart1) ; Point3d ptEnd1 ; pCrv1->GetEndPoint( ptEnd1) ; Point3d ptStart2 ; pCrv2->GetStartPoint( ptStart2) ; Point3d ptEnd2 ; pCrv2->GetEndPoint( ptEnd2) ; Point3d ptStart3 ; pCrv3->GetStartPoint( ptStart3) ; Point3d ptEnd3 ; pCrv3->GetEndPoint( ptEnd3) ; if ( AreSamePointApprox( ptEnd1, ptStart2) && AreSamePointApprox( ptEnd2, ptStart3)) ; else if ( AreSamePointApprox( ptEnd2, ptStart3) && AreSamePointApprox( ptEnd3, ptStart1)) { swap( pCrv1, pCrv2) ; swap( pCrv2, pCrv3) ; } else if ( AreSamePointApprox( ptEnd3, ptStart1) && AreSamePointApprox( ptEnd1, ptStart2)) { swap( pCrv1, pCrv3) ; swap( pCrv3, pCrv2) ; } else return true ; // calcolo la direzione della svuotatura e verifico che lati non siano troppo lunghi double dLen1 ; pCrv1->GetLength( dLen1) ; double dLen2 ; pCrv2->GetLength( dLen2) ; double dLen3 ; pCrv3->GetLength( dLen3) ; if ( dLen2 > dLen1 && dLen2 > dLen3) { if ( dLen1 > m_TParams.m_dDiam * 0.5 + 5 * EPS_SMALL || dLen3 > m_TParams.m_dDiam * 0.5 + 5 * EPS_SMALL) return true ; pCrv2->GetStartDir( vtDir) ; bOpposite = false ; } else { return true ; //if ( dLen2 > m_TParams.m_dDiam * 0.5 + 5 * EPS_SMALL) // return true ; //pCrv3->GetStartDir( vtDir) ; //bOpposite = true ; } // aggiorno pCrvPocket con eventuale offset per regioni residue //if ( ! ZigZagOptimizedComputeOffset( pCrvPocket, vtDir, 1, vnClosedIds, dOffs)) // return true ; OffsetCurve CrvOffs ; if ( ! CrvOffs.Make( pCrvPocket, 0.5 * m_TParams.m_dDiam, ICurve::OFF_CHAMFER)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } pCrvPocket->Clear() ; pCrvPocket->AddCurve( CrvOffs.GetLongerCurve()) ; pCrv1->ExtendStartByLen( 300) ; pCrv1->ExtendEndByLen( 300) ; if ( ! CutCurveWithLine( pCrvPocket, pCrv1)) return false ; pCrv3->ExtendStartByLen( 300) ; pCrv3->ExtendEndByLen( 300) ; if ( ! CutCurveWithLine( pCrvPocket, pCrv3)) return false ; pCrv2->ExtendStartByLen( 300) ; pCrv2->ExtendEndByLen( 300) ; if ( ! CutCurveWithLine( pCrvPocket, pCrv2)) return false ; bOptimizedZigZag = true ; return true ; } //------------------------------------------------------------------ bool Pocketing::ZigZagOptimizedComputeOffset( ICurveComposite* pCrvPocket, const Vector3d& vtMainDir, int nOffsettedEdgesOnY, const INTVECTOR& vnClosedIds, double& dOffs) { // calcolo il side step che verrà utilizzato in CalcZigZag Frame3d frLoc ; if ( ! frLoc.Set( ORIG, Z_AX, vtMainDir)) return true ; BBox3d b3Loc ; pCrvPocket->ToLoc( frLoc) ; pCrvPocket->GetLocalBBox( b3Loc) ; pCrvPocket->ToGlob( frLoc) ; Point3d pt ; double dDimX, dDimY, dDimZ ; b3Loc.GetMinDim( pt, dDimX, dDimY, dDimZ) ; // riduco la DimY della svuotatura in base al numero di lati chiusi che saranno offsettati lungo quella direzione dDimY -= nOffsettedEdgesOnY * ( 0.5 * m_TParams.m_dDiam + GetOffsR()) ; int nYStep = static_cast( ceil( ( dDimY - 30 * EPS_SMALL) / GetSideStep())) ; double dYSideStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ; dOffs = dYSideStep * 0.5 ; return true ; // ---- check // individuo i lati vicini a quelli closed for ( int i = 0 ; i < ( int)vnClosedIds.size() ; i ++) { int nNext = vnClosedIds[i] == pCrvPocket->GetCurveCount() - 1 ? 0 : vnClosedIds[i] + 1 ; int nPrev = vnClosedIds[i] == 0 ? pCrvPocket->GetCurveCount() - 1 : vnClosedIds[i] - 1 ; pCrvPocket->SetCurveTempProp( nNext, 1, 1) ; pCrvPocket->SetCurveTempProp( nPrev, 1, 1) ; } // verifico se resteranno aree residue e calcolo eventuale offset per pCrvPocket dOffs = 0 ; for ( int i = 0 ; i < pCrvPocket->GetCurveCount() ; i ++) { double dOffsTmp = 0 ; double dVal = 0 ; if ( pCrvPocket->GetCurve(i)->GetType() == CRV_LINE) { Vector3d vtDir ; if ( ! pCrvPocket->GetCurve( i)->GetStartDir( vtDir)) return false ; int nProp ; pCrvPocket->GetCurveTempProp( i, nProp, 1) ; double dLen ; pCrvPocket->GetCurve( i)->GetLength( dLen) ; if ( nProp == 1 && dLen > 1000 * EPS_SMALL) { // gestione speciale se vicino al lato closed double dCosAlpha = vtMainDir * vtDir ; if ( abs( dCosAlpha) > EPS_SMALL && abs( dCosAlpha) < 1 - EPS_SMALL) dOffsTmp = abs( 0.5 * m_TParams.m_dDiam * dCosAlpha) ; } else { double dSinAlpha = ( vtMainDir ^ vtDir).Len() ; if ( abs( dSinAlpha) > EPS_SMALL) dVal = ( dYSideStep - 0.5 * m_TParams.m_dDiam) / dSinAlpha - 0.5 * m_TParams.m_dDiam ; if ( dVal > EPS_SMALL) { double dCosAlpha = vtMainDir * vtDir ; dOffsTmp = abs( ( dYSideStep - 0.5 * m_TParams.m_dDiam) * dCosAlpha) ; } } } if ( dOffsTmp > dOffs) dOffs = dOffsTmp ; } // aggiusto offset double dMinOffs = max( 0., 0.5 * m_TParams.m_dDiam - m_Params.m_dSideStep) ; dOffs = max( dOffs, dMinOffs) ; if ( dOffs > EPS_SMALL) { // calcolo offset OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( pCrvPocket, dOffs, ICurve::OFF_EXTEND)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } if ( OffsCrv.GetCurveCount() > 1) return false ; // aggiorno pCrvPocket pCrvPocket->Clear() ; pCrvPocket->AddCurve( OffsCrv.GetCurve()) ; } return true ; } //------------------------------------------------------------------ bool Pocketing::CutCurveWithLine( ICurveComposite* pCrvA, const ICurveLine* pCrvB) { IntersCurveCurve IntersCC( *pCrvA, *pCrvB) ; CRVCVECTOR ccClass ; IntersCC.GetCurveClassification( 1, EPS_SMALL, ccClass) ; if ( ccClass.size() != 3 || ccClass[0].nClass != CRVC_OUT || ccClass[1].nClass == CRVC_OUT || ccClass[2].nClass != CRVC_OUT) return false ; Point3d ptS, ptE ; pCrvB->GetPointD1D2( ccClass[1].dParS, ICurve::FROM_MINUS, ptS) ; pCrvB->GetPointD1D2( ccClass[1].dParE, ICurve::FROM_MINUS, ptE) ; double dParS, dParE ; pCrvA->GetParamAtPoint( ptS, dParS) ; pCrvA->GetParamAtPoint( ptE, dParE) ; PtrOwner pCrvTmp( CloneCurveComposite( pCrvA)) ; if ( IsNull( pCrvTmp)) return false ; pCrvA->Clear() ; pCrvA->AddCurve( pCrvB->CopyParamRange( ccClass[1].dParS, ccClass[1].dParE)) ; pCrvA->AddCurve( pCrvTmp->CopyParamRange( dParE, dParS)) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::AddOneWay( ISURFFRPOVECTOR& vSfr, const std::vector& vCrvOrig, BOOLVECTOR& vbChangedPrec, VCT3DVECTOR& vVtTrasl, const Vector3d& vtTool, const Vector3d& vtExtr, double dDepth, double dElev, double dMaxElev, double dOkStep, bool bSplitArcs) { // recupero distanze di sicurezza double dSafeZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeZ() ; double dSafeAggrBottZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeAggrBottZ() ; // lunghezza di approccio/retrazione double dAppr = m_Params.m_dStartPos ; // ricavo il numero di Step int nStep = int( vSfr.size()) ; // coefficiente di riduzione feed di lavorazione di questa curva double dFeedRid = min( GetSideStep() / m_TParams.m_dDiam, 1.0) ; // se utensile che non lavora di testa poichè ingresso non fuori dal pezzo, errore if ( m_TParams.m_nType == TT_MILL_NOTIP) { if ( ! LeadInRawIsOk()) { m_pMchMgr->SetLastError( 2431, "Error in Pocketing : LeadIn with Mill NoTip in material") ; return false ; } } // determino i paramtri per la lavorazione double dTRad = 0.5 * m_TParams.m_dDiam ; double dOffs = dTRad + GetOffsR() ; double dExtra = min( 0.1 * m_TParams.m_dDiam, 1.0) ; // scorro il vettore delle FlatRegion ( calcolate per ogni step j-esimo) for ( int j = 1 ; j <= nStep ; ++ j) { // se superficie non valida, passo alla prossima if ( IsNull( vSfr[j-1]) || ! vSfr[j-1]->IsValid()) continue ; // ciclo sui chunk della superificie da svuotare for ( int c = 0 ; c < vSfr[j-1]->GetChunkCount() ; ++ c) { // copio il chunk c-esimo PtrOwner pSrfChunk( GetSurfFlatRegion( vSfr[j-1]->CloneChunk( c))) ; if ( IsNull( pSrfChunk)) return false ; // per entrate ed uscite PtrOwner pSrfLeanInOut( CloneSurfFlatRegion( vSfr[j-1])) ; if ( IsNull( pSrfLeanInOut) || pSrfLeanInOut->GetChunkCount() == 0) return false ; // creo un frame di riferimento per ogni superificie da svuotare Frame3d frLocI ; Point3d ptC ; pSrfChunk->GetCentroid( ptC) ; Vector3d vtN = pSrfChunk->GetNormVersor() ; frLocI.Set( ptC, vtN) ; pSrfChunk->ToLoc( frLocI) ; // salvo tutte le curve di Offset in un vettore per poi ordinarle e sistemare i punti iniziali ICRVCOMPOPOVECTOR vAllCrv ; // vettore di indici, mi dice a quale chunk la curva vAllCrv-u-esima appartiene INTVECTOR vInd ; // NB. Questa Superificie la userò solamente dopo, quando andrò creare le traettorie rettilinee per la OneWay // A questa superificie non andrò ad effettuare un ulteriore Offset con -dExtra, in quando potrei lasciare delle // regioni non svuotate ( come soluzione interseco ogni segmento di OneWay con questa superificie Offsettata di // -dOffs e tagliando ogni segmento, lo accorcio sia all'inizio che alla fine della quantità dExtra PtrOwner pSfrBorder( pSrfChunk->CreateOffsetSurf( - dOffs, ICurve::OFF_FILLET)) ; if ( IsNull( pSfrBorder)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } for ( int cc = 0 ; cc < pSfrBorder->GetChunkCount() ; ++ cc) { for ( int l = 0 ; l < pSfrBorder->GetLoopCount( cc) ; ++ l) { PtrOwner pCrvCompoLoop( ConvertCurveToComposite( pSfrBorder->GetLoop( cc, l))) ; if ( IsNull( pCrvCompoLoop) || ! pCrvCompoLoop->IsValid()) return false ; vAllCrv.emplace_back( Release( pCrvCompoLoop)) ; vInd.emplace_back( c) ; } } // se non ho curve di bordo, salto allo step succevo, non avrà nemmeno i segmenti OneWay if (( int)vAllCrv.size() == 0) continue ; // passo allo step successivo // Sistemo i punti iniziali per i nuovi Chunks ------------------------------------------ VCT3DVECTOR vVtMidOut(( int)vAllCrv.size(), V_NULL) ; BOOLVECTOR vbMidOut(( int)vAllCrv.size(), false) ; PNTVECTOR vPtStart(( int)vAllCrv.size(), Point3d( 0,0,0)) ; BOOLVECTOR vbForcedOutStart(( int)vAllCrv.size(), false) ; // le curve ottenute andranno percorse dall'utensile for ( int u = 0 ; u < ( int)vAllCrv.size() ; ++ u) { // per ogni curva di bordo... bool bOutTmp = false ; // ricavo il chunk da vui deriva -> vInd PtrOwner pSrfCurrChunk( vSfr[j-1]->CloneChunk( vInd[u])) ; if ( IsNull( pSrfCurrChunk)) return false ; // imposto un punto valido per l'entrata if ( ! SetBetterPtStartForSubChunks( vAllCrv[u], pSrfCurrChunk, vPtStart[u], vVtMidOut[u], bOutTmp)) return false ; vbMidOut[u] = bOutTmp ; // vector::reference da Bit a Bool // riporto i valori nel sistema di riferimento corretto vPtStart[u].ToGlob( frLocI) ; vVtMidOut[u].ToGlob( frLocI) ; vAllCrv[u]->ToGlob( frLocI) ; // se richiesto, la inverto if ( m_Params.m_bInvert) vAllCrv[u]->Invert() ; // setto la Feed per la curva di contorno //AssignFeedForEdgeCleaning( vAllCrv[u], vCrvOEWithFlags[j-1]) ; // se la curva è valida per entrata da fuori, aggiungo un piccolo tratto lineare if ( vbMidOut[u]) { // calcolo il punto fuori Point3d ptOut = vPtStart[u] + vVtMidOut[u] * ( 0.5 * m_TParams.m_dDiam + max( dSafeZ, m_dOpenMinSafe)) ; ptOut.Translate( vVtTrasl[j-1]) ; // lo traslo allo step corrente double dStElev ; bool bOutStart = ( ! GetElevation( m_nPhase, ptOut, vtTool, 0.5 * m_TParams.m_dDiam, vtTool, dStElev) || dStElev < EPS_SMALL) ; if ( bOutStart || m_bOpenOutRaw) { // aggiungo alla curva il tratto lineare ptOut.Translate( - vVtTrasl[j-1]) ; vAllCrv[u]->AddLine( ptOut, false) ; AssignFeedForLineInOut( vAllCrv[u], true) ; } // verifico se ingresso da considerare fuori grezzo anche se dentro vbForcedOutStart[u] = ( vbMidOut[u] && m_bOpenOutRaw) ; // se utensile che non lavora di testa e ingresso non fuori dal pezzo, errore if ( m_TParams.m_nType == TT_MILL_NOTIP && !bOutStart && !vbForcedOutStart[u]) { if ( ! LeadInRawIsOk()) { m_pMchMgr->SetLastError( 2431, "Error in Pocketing : LeadIn with Mill NoTip in material") ; return false ; } } } } // riordino le curve ( e i relativi vettori ) in base alla vicinanza // ( piccola ottimizzazione per l'ordine dei percorsi sui bordi) Point3d ptStart ; vAllCrv[0]->GetStartPoint( ptStart) ; if ( ! OrderCurvesByLastPntOfPath( vAllCrv, ptStart, vPtStart, vVtMidOut, vbMidOut)) return false ; // determino l'affondamento attuale double dSink = dElev - dDepth + dMaxElev - vVtTrasl[j-1].Len() ; // ---------------------------- Disegno le curve di contorno ---------------------- for ( int u = 0 ; u < int( vAllCrv.size()) ; ++ u) { // percorro tutte le curve ( ordinate) // recupero la prima curva di offset disponibile PtrOwner pOffs( CreateCurveComposite()); if ( IsNull( pOffs) || ! pOffs->AddCurve( vAllCrv[u]->Clone())) { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false; } // aggiungo la lavorazione di questa curva Point3d ptP1 ; // ciclo sulle curve elementari int nMaxInd = pOffs->GetCurveCount() - 1 ; for ( int i = 0 ; i <= nMaxInd ; ++i) { // curva corrente const ICurve* pCrvC = pOffs->GetCurve( i) ; // copio la curva PtrOwner pCurve( pCrvC->Clone()) ; if ( IsNull( pCurve)) return false ; // SE PRIMA ENTITA' if ( i == 0) { // dati inizio entità Point3d ptStart ; Vector3d vtStart ; pCurve->GetStartPoint( ptStart) ; pCurve->GetStartDir( vtStart) ; Point3d ptForElev = ptStart ; if ( vbMidOut[u] || vbForcedOutStart[u]) pCurve->GetEndPoint( ptForElev) ; // determino inizio attacco if ( ! CalcLeadInStart( ptStart, vtStart, vtExtr, nullptr, ptP1)) return false ; // determino elevazione su inizio attacco double dStElev = dSink ; dStElev -= ( ptP1 - ptStart) * vtExtr ; // se attacco a zigzag o a spirale o a scivolo, l'elevazione va nell'attacco if ( GetLeadInType() == POCKET_LI_ZIGZAG || GetLeadInType() == POCKET_LI_HELIX || GetLeadInType() == POCKET_LI_GLIDE) { ptP1 += vtExtr * ( dStElev + LIO_ELEV_TOL) ; dStElev = -LIO_ELEV_TOL ; } // approccio al punto iniziale if ( ! AddApproach( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr, vbMidOut[u] || vbForcedOutStart[u])) { m_pMchMgr->SetLastError( 2414, "Error in Pocketing : Approach not computable") ; return false ; } // aggiungo attacco SetFeed( GetStartFeed()) ; if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, nullptr, !m_Params.m_bInvert, bSplitArcs, vbMidOut[u] || vbForcedOutStart[u])) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } } // elaborazioni sulla curva corrente //double dMinFeed = GetFeed() * GetSideStep() / m_TParams.m_dDiam ; //double dFeed = pCurve->GetTempProp( 0) / FEED_DIVISOR < dMinFeed - 50 * EPS_SMALL ? // GetFeed() : pCurve->GetTempProp( 0) / FEED_DIVISOR ; //DrawColoredCrvForFeedTest( pCurve, dFeed) ; //SetFeed( dFeed) ; if ( pCurve->GetType() == CRV_LINE) { ICurveLine* pLine = GetCurveLine( pCurve) ; Point3d ptP3 = pLine->GetEnd() ; SetFeed( dFeedRid * GetFeed()) ; if ( AddLinearMove( ptP3) == GDB_ID_NULL) return false ; } else if ( pCurve->GetType() == CRV_ARC) { ICurveArc* pArc = GetCurveArc( pCurve) ; Point3d ptCen = pArc->GetCenter() ; double dAngCen = pArc->GetAngCenter() ; Vector3d vtN = pArc->GetNormVersor() ; Point3d ptP3 ; pArc->GetEndPoint( ptP3) ; SetFeed( dFeedRid * GetFeed()) ; if ( AddArcMove( ptP3, ptCen, dAngCen, vtN) == GDB_ID_NULL) return false ; } // SE ULTIMA ENTITA' if ( i == nMaxInd) { // se ultimo step, uscita e retrazione di collegamento // dati fine entità Point3d ptEnd ; pCurve->GetEndPoint( ptEnd) ; Vector3d vtEnd ; pCurve->GetEndDir( vtEnd) ; // aggiungo uscita double dEndElev = dSink ; SetFeed( GetEndFeed()) ; if ( ! AddLeadOut( ptEnd, vtEnd, vtExtr, nullptr, bSplitArcs, false, ptP1, dEndElev, false)) { m_pMchMgr->SetLastError( 2416, "Error in Pocketing : LeadOut not computable") ; return false ; } // aggiungo retrazione if ( ! AddLinkRetract( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) { m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ; return false ; } } } } // fine ciclo sulle curve di Offset i-esime // ===== SEGMENTI ========= // determino il riferimento in base alla svuotatura ( Serve per Orientare i segmenti in base al m_dSideAngle) BBox3d b3Pocket ; pSfrBorder->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) / GetSideStep())) ; double dYStep = ( nYStep > 0 ? ( dDimY + 2 * dExtra) / nYStep : 0) ; -- nYStep ; // vettore dei segmenti al di sotto della linea corrente ( per la Feed) ICURVEPOVECTOR vLineUnder ; ICURVEPOVECTOR vLineAbove ; // in questo caso sempre vuoto ICRVCOMPOPOVECTOR vCrvLink ; // in questo caso sempre vuoto // 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)) { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } // linea come composita per Feed ( la dovrò spezzare a seconda dei tratti di Feed differenti) PtrOwner pCrvCompo( CreateCurveComposite()) ; if ( IsNull( pCrvCompo)) return false ; // vettore di tutti i segmenti lineari che si formano nello step nYStep ICURVEPOVECTOR vAddedLines ; // riempio il vettore di segmenti CRVCVECTOR ccClassSeg ; pSfrBorder->GetCurveClassification( *pLine, EPS_SMALL, ccClassSeg) ; for ( int w = 0 ; w < int( ccClassSeg.size()) ; ++w) { if ( ccClassSeg[w].nClass == CRVC_IN) { // creo la Linea tra i bordi 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 ( di dExtra) 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))) ; // punto iniziale Point3d ptS ; pCrvSeg->GetStartPoint( ptS) ; // imposto la Feed pCrvCompo->Clear() ; pCrvCompo->AddCurve( pCrvSeg->Clone()) ; vAddedLines.emplace_back( pCrvCompo->Clone()) ; //if ( ! AssignFeedZigZagOneWay( pCrvCompo, false, vLineAbove, vLineUnder, vCrvLink)) // return false ; // INIZIO ptS.ToGlob( frLocI) ; // determino inizio attacco Point3d ptP ; if ( ! CalcLeadInStart( ptS, frLocI.VersX(), vtExtr, nullptr, ptP)) return false ; // determino elevazione su inizio attacco double dStElev = dSink ; dStElev -= ( ptP - ptS) * vtExtr ; // sempre approccio di collegamento if ( ! AddLinkApproach( ptP, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr)) { m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ; return false ; } // aggiungo attacco (forzato ad essere lineare) SetFeed( GetStartFeed()) ; if ( ! AddLeadIn( ptP, ptS, frLocI.VersX(), vtExtr, pSrfLeanInOut, nullptr, !m_Params.m_bInvert, bSplitArcs, true)) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } // punto finale Point3d ptE ; // per ogni sotto-tratto ( della feed) del tratto lineare ( per ora sempre 1) for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) { // ricavo la singola entità curva const ICurve* pCurve( pCrvCompo->GetCurve( u)) ; pCurve->GetEndPoint( ptE) ; ptE.ToGlob( frLocI) ; // movimento al punto finale // elaborazioni sulla curva corrente //double dMinFeed = GetFeed() * GetSideStep() / m_TParams.m_dDiam ; //double dFeed = pCurve->GetTempProp( 0) / FEED_DIVISOR < dMinFeed - 50 * EPS_SMALL ? // GetFeed() : pCurve->GetTempProp( 0) / FEED_DIVISOR ; //SetFeed( dFeed) ; SetFeed( GetFeed()) ; if ( AddLinearMove( ptE) == GDB_ID_NULL) return false ; } // FINE Point3d ptQ ; double dEndElev = dSink ; SetFeed( GetEndFeed()) ; if ( ! AddLeadOut( ptE, frLocI.VersX(), vtExtr, nullptr, bSplitArcs, true, ptQ, dEndElev)) { m_pMchMgr->SetLastError( 2416, "Error in Pocketing : LeadOut not computable") ; return false ; } dEndElev -= ( ptE - ptQ) * vtExtr ; // se ultimo movimento di ultima area, aggiungo retrazione globale if ( j == nStep && i == nYStep && w == ( int)ccClassSeg.size() - 2) { if ( ! AddRetract( ptQ, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) { m_pMchMgr->SetLastError( 2417, "Error in Pocketing : Retract not computable") ; return false ; } } // altrimenti aggiungo retrazione di collegamento else { dEndElev = dSink ; if ( ! AddLinkRetract( ptQ, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) { m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ; return false ; } } } } vLineUnder.clear() ; for ( int u = 0 ; u < ( int)vAddedLines.size() ; ++ u) vLineUnder.emplace_back( vAddedLines[u]->Clone()) ; } } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::AddSpiralIn( ISURFFRPOVECTOR& vSfr, const vector& vCrvOrig, BOOLVECTOR& vbChangedPrec, VCT3DVECTOR& vVtTrasl, const Vector3d& vtTool, const Vector3d& vtExtr, double dDepth, double dElev, double dMaxElev, double dOkStep, bool bSplitArcs) { // recupero distanze di sicurezza double dSafeZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeZ() ; double dSafeAggrBottZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeAggrBottZ() ; // lunghezza di approccio/retrazione double dAppr = m_Params.m_dStartPos ; // ricavo il numero di Step int nStep = int( vSfr.size()) ; // scorro il vettore delle FlatRegion ( calcolate per ogni step j-esimo) for ( int j = 1 ; j <= nStep ; ++ j) { // se superficie non valida, passo alla prossima if ( IsNull( vSfr[j-1]) || ! vSfr[j-1]->IsValid()) continue ; // ciclo sui chunk della superificie da svuotare for ( int c = 0 ; c < vSfr[j-1]->GetChunkCount() ; ++ c) { // copio il chunk c-esimo PtrOwner pSrfChunk( GetSurfFlatRegion( vSfr[j-1]->CloneChunk( c))) ; if ( IsNull( pSrfChunk)) return false ; Point3d ptP1, ptStartPrec ; // per LeadIn attuale e precedente BOOLVECTOR vbMidOutPrec ; // flags per effettiva entrata precedente da fuori dal grezzo ICRVCOMPOPOVECTOR vMCrv_step_prec ; // vettore delle curve dei percorsidi svuotatura dello step precedente ICRVCOMPOPOVECTOR vRCrv_step_prec ; // vettore delle curve dei percorsi di ritorno dello step precedente vector vvtMidOutPrec ; // vettore dei versori delle direzioni di uscita dello step precedente INTVECTOR vnRegTot_prec ; // vettore dei numeri delle regioni formate dal primo Offset nello step precedente BOOLVECTOR vbOptTrap_prec ; // flags per caso ottimizzato svuotratura dello step precedente int nOffs_act = 0 ; // Shift indice dei vettori causato da nReg nOffs_act = 0 ; // reset indice dei vettori per step precedenti if ( vbChangedPrec[j-1]) { // se lo step attuale è cambiato rispetto al precedente -> Flush info step precedente vMCrv_step_prec.clear() ; vRCrv_step_prec.clear() ; vnRegTot_prec.clear() ; vbMidOutPrec.clear() ; vvtMidOutPrec.clear() ; vbOptTrap_prec.clear() ; } const int MAX_REGS = 50 ; int nReg = 0 ; // chunk nuovo corrente da svuotare // ciclo su tutte le regioni che posso ottenere col primo Offset del chunk cc-esimo while ( nReg < MAX_REGS) { // superifice per ingressi ed uscite PtrOwner pSrfLeanInOut( CloneSurfFlatRegion( vSfr[j-1])) ; if ( IsNull( pSrfLeanInOut)) return false ; // 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)) { m_pMchMgr->SetLastError( 2411, "Error in Pocketing : toolpath allocation failed") ; return false ; } int nRegTot ; // numero di regioni create dal primo Offset per lo step attuale bool bOutStart ; // flag per entrata da fuori per lo step attuale bool bForcedOutStart ; // flag per forzare l'entrata da fuori allo step attuale Point3d ptStart ; // punto iniziale del percorso Vector3d vtMidOut ; // vettore verso l'esterno nel caso di entrata da fuori ammissibile ( -> da CalcSpiral) bool bMidOut ; // ammissibilità entrata da fuori ( da calcolare step per step) bool bOptimizedTrap = false ; // se ho un caso spirale o trapezio // NB. se la superficie è rimasta la stessa, utilizzo pMCrv e pRCrv dello step precedente // ( non c'è bisogno di ricalcolare tutti i percorsi) if ( ! vbChangedPrec[j-1] && j != 1) { if ( nOffs_act > int( vMCrv_step_prec.size()) - 1) break ; pMCrv.Set( vMCrv_step_prec[nOffs_act]->Clone()) ; pRCrv.Set( vRCrv_step_prec[nOffs_act]->Clone()) ; nRegTot = vnRegTot_prec[nOffs_act] ; bMidOut = vbMidOutPrec[nOffs_act] ; vMCrv_step_prec[nOffs_act]->GetStartPoint( ptStart) ; vtMidOut = vvtMidOutPrec[nOffs_act] ; bOptimizedTrap = vbOptTrap_prec[nOffs_act] ; } else { // se lucidatura con epicicli if ( m_TParams.m_nType == TT_MILL_POLISHING && m_Params.m_dEpicyclesRad > EPS_SMALL) { // verifico che i parametri lucidatura siano sensati if ( m_Params.m_dEpicyclesDist < 100 * EPS_SMALL) { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } // modifico il diametro dell'utensile per tenere conto anche del diametro degli epicicli m_TParams.m_dDiam += 2 * m_Params.m_dEpicyclesRad ; } // 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, j, pSrfChunk, nInd)) return false ; nRegTot = nReg ; // calcolo il percorso di svuotatura if ( ! CalcSpiral( pSrfChunk, nRegTot, ptStart, vtMidOut, bMidOut, bSplitArcs, pMCrv, pRCrv, vCrvOrig[j-1][nInd], j == 1 ? true : vbChangedPrec[j-1], bOptimizedTrap)) return false ; // se terminate le regioni, esco if ( pMCrv->GetCurveCount() == 0) break ; // passo al chunk originale successivo if ( m_TParams.m_nType == TT_MILL_POLISHING && m_Params.m_dEpicyclesRad > EPS_SMALL) { // riporto il diametro dell'utensile al valore originale m_TParams.m_dDiam -= 2 * m_Params.m_dEpicyclesRad ; // aggiorno i percorsi di svuotatura con epicicli if ( ! ComputePolishingPath( pMCrv, pRCrv, bSplitArcs)) { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } } // memorizzo le curve create e modificate per lo step successivo vMCrv_step_prec.emplace_back( pMCrv->Clone()) ; vRCrv_step_prec.emplace_back( pRCrv->Clone()) ; // memorizzo i Flag per le entrate vbMidOutPrec.push_back( bMidOut) ; // memorizzo il numero delle regioni create dal primo Offset vnRegTot_prec.push_back( nRegTot) ; // memorizzo il versore di uscita calcolato vvtMidOutPrec.push_back( vtMidOut) ; // memorizzo caso ottimizzato vbOptTrap_prec.push_back( bOptimizedTrap) ; } // controlli per entrate da fuori al grezzo // NB. Anche se la superficie è rimasta invariata le entrate vanno controllate caso per caso bOutStart = bMidOut ; if ( bOutStart && ! bOptimizedTrap) { // calcolo il punto fuori per il LeadIn Point3d ptOut = ptStart + vtMidOut * ( 0.5 * m_TParams.m_dDiam + max( dSafeZ, m_dOpenMinSafe)) ; ptOut.Translate( vVtTrasl[j-1]) ; // lo traslo allo step corrente double dStElev ; // controllo l'elevazione bOutStart = ( ! GetElevation( m_nPhase, ptOut, vtTool, 0.5 * m_TParams.m_dDiam, vtTool, dStElev) || dStElev < EPS_SMALL) ; if ( bOutStart || m_bOpenOutRaw) { // aggiungo al ritorno l'uscita if ( pRCrv->GetCurveCount() == 0) { Point3d ptStart ; pMCrv->GetStartPoint( ptStart) ; pRCrv->AddPoint( ptStart) ; } ptOut.Translate( -vVtTrasl[j-1]) ; // lo ritraslo sulla curva // aggiungo un tratto lineare all'inizio del percorso di svuotatura pMCrv->AddLine( ptOut, false) ; // assegno la sua Feed //AssignFeedForLineInOut( pMCrv, true) ; // aggiungo un tratto lineare alla fine del percorso di svuotatura pRCrv->AddLine( ptOut, true) ; // assegno la sua Feed //AssignFeedForLineInOut( pRCrv, false) ; } } // calcolo gli eventuali punti fuori dal grezzo nel caso ottimizzato int nOutsideRaw = 0 ; if ( bOptimizedTrap) { AdjustTrapezoidSpiralForLeadInLeadOut( pMCrv, pRCrv, vtTool, dDepth, nOutsideRaw) ; bOutStart = ( nOutsideRaw > 0) ; // imposto la Feed //AssignFeedSpiralOpt( 1, pMCrv) ; //AssignFeedForReturnPath( pRCrv) ; } // verifico se l'ingresso è da considerare fuori dal grezzo anche se dentro bForcedOutStart = ( bMidOut && m_bOpenOutRaw) ; // se utensile che non lavora di testa e ingresso non fuori dal pezzo, errore if ( m_TParams.m_nType == TT_MILL_NOTIP && ! ( bOutStart || bForcedOutStart)) { if ( ! LeadInRawIsOk()) { m_pMchMgr->SetLastError( 2431, "Error in Pocketing : LeadIn with Mill NoTip in material") ; return false ; } } // se sono nel caso ottimizzato per Trapezi e ho attacco e uscita entrambi dentro/fuori dal grezzo, // ad ogni step pari inverto la direzione della curva solamente se ho un solo chunk e la superificie successiva // non è cambiata if ( bOptimizedTrap && nOutsideRaw != 1 && j > 1 && vSfr[j-2]->GetChunkCount() == 1 && ! vbChangedPrec[j-1] && ( j % 2 == 0)) pMCrv->Invert() ; ++nReg ; // incremento del numero di regioni trovate ++ nOffs_act ; // shift indice int nMaxInd = pMCrv->GetCurveCount() - 1 ; int nMaxRInd = pRCrv->GetCurveCount() - 1 ; // determino l'affondamento attuale double dSink = dElev - dDepth + dMaxElev - vVtTrasl[j-1].Len() ; // ciclo sulle curve elementari for ( int i = 0 ; i <= nMaxInd ; ++ i) { // curva corrente const ICurve* pCrvC = pMCrv->GetCurve( i) ; // copio la curva PtrOwner pCurve( pCrvC->Clone()) ; if ( IsNull( pCurve)) return false ; // aggiungo affondamento //pCurve->Translate( vVtTrasl[j-1]) ; // se prima entità if ( i == 0) { // dati inizio entità Point3d ptStart ; pCurve->GetStartPoint( ptStart) ; Vector3d vtStart ; pCurve->GetStartDir( vtStart) ; // Entrata ... if ( j == 1 || vbChangedPrec[j-1] || vSfr[j-2]->GetChunkCount() > 1 || nRegTot > 1) { // Ricalcolo il punto di attacco sopra al PtStart ( modificato in precedenza dal PtOut) quando : // 1) sono nel primo Step o ... // 2) la superificie è diversa da quello dello step precedente o ... // 3) il numero di chunk allo step precedente è maggiore di 1 o ... // 4) dal primo offset del chunk cc-esimo ottengo più regioni if ( ! CalcLeadInStart( ptStart, vtStart, vtExtr, pRCrv, ptP1)) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } // determino elevazione su inizio attacco double dStElev = dSink ; dStElev -= ( ptP1 - ptStart) * vtExtr ; // se attacco a zigzag o a spirale o a scivolo, l'elevazione va nell'attacco if ( GetLeadInType() == POCKET_LI_ZIGZAG || GetLeadInType() == POCKET_LI_HELIX || GetLeadInType() == POCKET_LI_GLIDE) { ptP1 += vtExtr * ( dStElev + LIO_ELEV_TOL) ; dStElev = -LIO_ELEV_TOL ; } // approccio al punto iniziale if ( ! AddApproach( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr, bOutStart || bForcedOutStart)) { m_pMchMgr->SetLastError( 2414, "Error in Pocketing : Approach not computable") ; return false ; } // aggiungo attacco SetFeed( GetStartFeed()) ; if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, pRCrv, !m_Params.m_bInvert, bSplitArcs, bOutStart || bForcedOutStart)) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } } // altrimenti solo collegamento else { SetFeed( GetStartFeed()) ; GetCurrPos( ptP1) ; if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, pRCrv, !m_Params.m_bInvert, bSplitArcs, bOutStart || bForcedOutStart)) { m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ; return false ; } } } // elaborazioni sulla curva corrente //double dMinFeed = GetFeed() * GetSideStep() / m_TParams.m_dDiam ; //double dFeed = pCurve->GetTempProp( 0) / FEED_DIVISOR < dMinFeed - 50 * EPS_SMALL ? // GetFeed() : pCurve->GetTempProp( 0) / FEED_DIVISOR ; // DrawColoredCrvForFeedTest( pCurve, dFeed) ; //SetFeed( dFeed) ; // <--- if ( pCurve->GetType() == CRV_LINE) { ICurveLine* pLine = GetCurveLine( pCurve) ; Point3d ptP3 = pLine->GetEnd() ; // SetFeed( pCurve->GetTempProp( 0) / FEED_DIVISOR) ; // <---- SetFeed( GetFeed()) ; if ( AddLinearMove( ptP3) == GDB_ID_NULL) return false ; } else if ( pCurve->GetType() == CRV_ARC) { ICurveArc* pArc = GetCurveArc( pCurve) ; Point3d ptCen = pArc->GetCenter() ; double dAngCen = pArc->GetAngCenter() ; Vector3d vtN = pArc->GetNormVersor() ; Point3d ptP3 ; pArc->GetEndPoint( ptP3) ; //SetFeed( pCurve->GetTempProp( 0) / FEED_DIVISOR) ; SetFeed( GetFeed()) ; if ( AddArcMove( ptP3, ptCen, dAngCen, vtN) == GDB_ID_NULL) return false ; } // se ultima entità -> passo al chunk cc successivo o allo step sotto if ( i == nMaxInd) { // Uscita ... if ( j < nStep && vSfr[j-1]->GetChunkCount() == 1 && ! vbChangedPrec[j] && nRegTot == 1) { // Effettuo l'uscita quando : // 1) non sono nello step intermedio e ... // 2) il numero di Chunk allo step attuale è 1 e ... // 3) la superificie non è cambiata dalla precedente e ... // 4) dal primo offset del chunk cc-esimo non si sono create altre regioni da svuotare // se necessario ritorno all'inizio if ( nMaxRInd >= 0) { // copio la curva di ritorno PtrOwner pRet( pRCrv->Clone()) ; if ( IsNull( pRet)) return false ; // aggiungo affondamento pRet->Translate( vVtTrasl[j-1]) ; // se attacco a scivolo, accorcio della lunghezza dell'attacco if ( GetLeadInType() == POCKET_LI_GLIDE) { double dLen ; pRet->GetLength( dLen) ; if ( dLen > m_Params.m_dLiTang + 10 * EPS_SMALL) { if ( ! pRet->TrimEndAtLen( dLen - m_Params.m_dLiTang)) pRet->Clear() ; } else pRet->Clear() ; } // emetto SetFeed( GetFeed()) ; if ( pRet->GetCurveCount() > 0 && AddCurveMove( pRet) == GDB_ID_NULL) return false ; } } // atrimenti ultimo step, uscita e retrazione else { // dati fine entità Point3d ptEnd ; pCurve->GetEndPoint( ptEnd) ; Vector3d vtEnd ; pCurve->GetEndDir( vtEnd) ; // aggiungo uscita Point3d ptP1 ; double dEndElev = dSink ; SetFeed( GetEndFeed()) ; if ( ! AddLeadOut( ptEnd, vtEnd, vtExtr, pRCrv, bSplitArcs, false, ptP1, dEndElev, true)) { m_pMchMgr->SetLastError( 2416, "Error in Pocketing : LeadOut not computable") ; return false ; } // aggiungo retrazione if ( ! AddRetract( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) { m_pMchMgr->SetLastError( 2417, "Error in Pocketing : Retract not computable") ; return false ; } } } } } } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::AddSpiralOut( ISURFFRPOVECTOR& vSfr, const std::vector& vCrvOrig, BOOLVECTOR& vbChangedPrec, VCT3DVECTOR& vVtTrasl, const Vector3d& vtTool, const Vector3d& vtExtr, double dDepth, double dElev, double dMaxElev, double dOkStep, bool bSplitArcs) { // recupero distanze di sicurezza double dSafeZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeZ() ; double dSafeAggrBottZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeAggrBottZ() ; // lunghezza di approccio/retrazione double dAppr = m_Params.m_dStartPos ; // ricavo il numero di Step int nStep = int( vSfr.size()) ; // scorro il vettore delle FlatRegion ( calcolate per ogni step j-esimo) for ( int j = 1 ; j <= nStep ; ++ j) { // se superficie non valida, passo alla prossima if ( IsNull( vSfr[j-1]) || ! vSfr[j-1]->IsValid()) continue ; // ciclo sui chunk della superificie da svuotare for ( int c = 0 ; c < vSfr[j-1]->GetChunkCount() ; ++ c) { // copio il chunk c-esimo PtrOwner pSrfChunk( GetSurfFlatRegion( vSfr[j-1]->CloneChunk( c))) ; if ( IsNull( pSrfChunk)) return false ; Point3d ptP1, ptStartPrec ; // per LeadIn attuale e precedente bool bMidOutPrec ; // flag per entrata precedente da fuori dal grezzo BOOLVECTOR vbMidOutPrec ; // flags per effettiva entrata precedente da fuori dal grezzo ICRVCOMPOPOVECTOR vMCrv_step_prec ; // vettore delle curve dei percorsidi svuotatura dello step precedente ICRVCOMPOPOVECTOR vRCrv_step_prec ; // vettore delle curve dei percorsi di ritorno dello step precedente vector vvtMidOutPrec ; // vettore dei versori delle direzioni di uscita dello step precedente INTVECTOR vnRegTot_prec ; // vettore dei numeri delle regioni formate dal primo Offset nello step precedente int nOffs_act = 0 ; if ( vbChangedPrec[j-1]) { // se lo step attuale è cambiato rispetto al precedente -> Flush info step precedente vMCrv_step_prec.clear() ; vRCrv_step_prec.clear() ; vnRegTot_prec.clear() ; vbMidOutPrec.clear() ; vvtMidOutPrec.clear() ; } const int MAX_REGS = 50 ; int nReg = 0 ; // chunk nuovo corrente da svuotare // ciclo su tutte le regioni che posso ottenere col primo Offset del chunk cc-esimo while ( nReg < MAX_REGS) { // superificie per ingressi ed uscite PtrOwner pSrfLeanInOut( CloneSurfFlatRegion( vSfr[j-1])) ; if( IsNull( pSrfLeanInOut)) return false ; // calcolo la spirale dall'interno all'esterno PtrOwner pMCrv( CreateCurveComposite()) ; PtrOwner pRCrv( CreateCurveComposite()) ; if ( IsNull( pMCrv) || IsNull( pRCrv)) { m_pMchMgr->SetLastError( 2411, "Error in Pocketing : toolpath allocation failed") ; return false ; } int nRegTot ; // numero di regioni create dal primo Offset per lo step attuale bool bOutStart ; // flag per entrata da fuori per lo step attuale bool bForcedOutStart ; // flag per forzare l'entrata da fuori allo step attuale Point3d ptStart ; // punto iniziale del percorso Vector3d vtMidOut ; // vettore verso l'esterno nel caso di entrata da fuori ammissibile ( -> da CalcSpiral) bool bMidOut ; // ammissibilità entrata da fuori ( da calcolare step per step) bool bOptimizedTrap = false ; // se ho un caso spirale o trapezio // NB. se la superficie è rimasta la stessa, utilizzo pMCrv e pRCrv dello step precedente // ( non c'è bisogno di ricalcolare tutti i percorsi) if ( ! vbChangedPrec[j-1] && j != 1) { if ( nOffs_act > ( int)vMCrv_step_prec.size() - 1) break ; pMCrv.Set( vMCrv_step_prec[nOffs_act]->Clone()) ; pRCrv.Set( vRCrv_step_prec[nOffs_act]->Clone()) ; nRegTot = vnRegTot_prec[nOffs_act] ; bMidOut = vbMidOutPrec[nOffs_act] ; vMCrv_step_prec[nOffs_act]->GetStartPoint( ptStart) ; vtMidOut = vvtMidOutPrec[nOffs_act] ; } else { // se lucidatura con epicicli if ( m_TParams.m_nType == TT_MILL_POLISHING && m_Params.m_dEpicyclesRad > EPS_SMALL) { // verifico che parametri lucidatura siano sensati if ( m_Params.m_dEpicyclesDist < 100 * EPS_SMALL) { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } // modifico il diametro dell'utensile per tenere conto anche del raggio degli epicicli m_TParams.m_dDiam += 2 * m_Params.m_dEpicyclesRad ; } bool bOptimizedTrap = false ; int nInd = 0 ; if ( ! GetOptCrvIndex( vCrvOrig, j, pSrfChunk, nInd)) return false ; nRegTot = nReg ; if ( ! CalcSpiral( pSrfChunk, nRegTot, ptStart, vtMidOut, bMidOut, bSplitArcs, pMCrv, pRCrv, vCrvOrig[j-1][nInd], vbChangedPrec[j-1], bOptimizedTrap)) return false ; // se terminate le regioni, esco if ( pMCrv->GetCurveCount() == 0) break ; if ( m_TParams.m_nType == TT_MILL_POLISHING && m_Params.m_dEpicyclesRad > EPS_SMALL) { // riporto il diametro dell'utensile al valore originale m_TParams.m_dDiam -= 2 * m_Params.m_dEpicyclesRad ; // aggiorno i percorsi di svuotatura con epicicli if ( ! ComputePolishingPath( pMCrv, pRCrv, bSplitArcs)) { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } } // memorizzo le curve create e modificate per lo step successivo vMCrv_step_prec.emplace_back( pMCrv->Clone()) ; vRCrv_step_prec.emplace_back( pRCrv->Clone()) ; // memorizzo i Flag per le entrate vbMidOutPrec.push_back( bMidOut) ; // memorizzo il numero delle regioni create dal primo Offset vnRegTot_prec.push_back( nRegTot) ; // memorizzo il versore di uscita calcolato vvtMidOutPrec.push_back( vtMidOut) ; } // nel caso ottimizzato verifico se posso entrare e uscire fuori dal grezzo bOutStart = false ; int nOutsideRaw = 0 ; if ( bOptimizedTrap) { AdjustTrapezoidSpiralForLeadInLeadOut( pMCrv, pRCrv, vtTool, dDepth, nOutsideRaw) ; bOutStart = ( nOutsideRaw > 0) ; AssignFeedSpiralOpt( 1, pMCrv) ; AssignFeedForReturnPath( pRCrv) ; } // se utensile che non lavora di testa e ingresso non fuori dal pezzo, errore if ( m_TParams.m_nType == TT_MILL_NOTIP && ! bOutStart) { if ( ! LeadInRawIsOk()) { m_pMchMgr->SetLastError( 2431, "Error in Pocketing : LeadIn with Mill NoTip in material") ; return false ; } } // inverto i percorsi, perchè sono calcolati dall'esterno all'interno (solo nel caso non ottimizzato) if ( ! bOptimizedTrap) { pMCrv->Invert() ; pRCrv->Invert() ; } // inversione per caso ottimizzato if ( bOptimizedTrap && nOutsideRaw != 1 && j > 1) pMCrv->Invert() ; ++ nReg ; ++ nOffs_act ; int nMaxInd = pMCrv->GetCurveCount() - 1 ; int nMaxRInd = pRCrv->GetCurveCount() - 1 ; // determino l'affondamento attuale double dSink = dElev - dDepth + dMaxElev - vVtTrasl[j-1].Len() ; // ciclo sulle curve elementari for ( int i = 0 ; i <= nMaxInd ; ++ i) { // curva corrente const ICurve* pCrvC = pMCrv->GetCurve( i) ; // copio la curva PtrOwner pCurve( pCrvC->Clone()) ; if ( IsNull( pCurve)) return false ; // se prima entità if ( i == 0) { // dati inizio entità Point3d ptStart ; pCurve->GetStartPoint( ptStart) ; Vector3d vtStart ; pCurve->GetStartDir( vtStart) ; // se primo step, approccio e affondo if ( j == 1 || vbChangedPrec[j-1] || vSfr[j-2]->GetChunkCount() > 1 || nRegTot > 1) { // Ricalcolo il punto di attacco sopra al PtStart ( modificato in precedenza dal PtOut) quando : // 1) sono nel primo Step o ... // 2) la superificie è diversa da quello dello step precedente o ... // 3) il numero di chunk allo step precedente è maggiore di 1 o ... // 4) dal primo offset del chunk cc-esimo ottengo più regioni if ( ! CalcLeadInStart( ptStart, vtStart, vtExtr, pRCrv, ptP1)) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } // determino elevazione su inizio attacco double dStElev = dSink ; dStElev -= ( ptP1 - ptStart) * vtExtr ; // se attacco a zigzag o a spirale o a scivolo, l'elevazione va nell'attacco if ( GetLeadInType() == POCKET_LI_ZIGZAG || GetLeadInType() == POCKET_LI_HELIX || GetLeadInType() == POCKET_LI_GLIDE) { ptP1 += vtExtr * ( dStElev + LIO_ELEV_TOL) ; dStElev = -LIO_ELEV_TOL ; } // approccio al punto iniziale if ( ! AddApproach( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr, false)) { m_pMchMgr->SetLastError( 2414, "Error in Pocketing : Approach not computable") ; return false ; } // aggiungo attacco SetFeed( GetStartFeed()) ; if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, pRCrv, m_Params.m_bInvert, bSplitArcs, bOutStart)) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } } // altrimenti solo collegamento else { SetFeed( GetStartFeed()) ; GetCurrPos( ptP1) ; if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, pRCrv, m_Params.m_bInvert, bSplitArcs, bOutStart)) { m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ; return false ; } } } // elaborazioni sulla curva corrente //double dMinFeed = GetFeed() * GetSideStep() / m_TParams.m_dDiam ; //double dFeed = pCurve->GetTempProp( 0) / FEED_DIVISOR < dMinFeed - 50 * EPS_SMALL ? // GetFeed() : pCurve->GetTempProp( 0) / FEED_DIVISOR ; //DrawColoredCrvForFeedTest( pCurve, dFeed) ; //SetFeed( dFeed) ; if ( pCurve->GetType() == CRV_LINE) { ICurveLine* pLine = GetCurveLine( pCurve) ; Point3d ptP3 = pLine->GetEnd() ; SetFeed( GetFeed()) ; //SetFeed( pCurve->GetTempProp( 0) / FEED_DIVISOR) ; if ( AddLinearMove( ptP3) == GDB_ID_NULL) return false ; } else if ( pCurve->GetType() == CRV_ARC) { ICurveArc* pArc = GetCurveArc( pCurve) ; Point3d ptCen = pArc->GetCenter() ; double dAngCen = pArc->GetAngCenter() ; Vector3d vtN = pArc->GetNormVersor() ; Point3d ptP3 ; pArc->GetEndPoint( ptP3) ; SetFeed( GetFeed()) ; //SetFeed( pCurve->GetTempProp( 0 ) / FEED_DIVISOR) ; if ( AddArcMove( ptP3, ptCen, dAngCen, vtN) == GDB_ID_NULL) return false ; } // se ultima entità if ( i == nMaxInd) { // se step intermedio if ( j < nStep && vSfr[j-1]->GetChunkCount() == 1 && !vbChangedPrec[j] && nRegTot == 1) { // se necessario ritorno all'inizio if ( nMaxRInd >= 0) { // copio la curva di ritorno PtrOwner pRet( pRCrv->Clone()) ; if ( IsNull( pRet)) return false ; // aggiungo affondamento pRet->Translate( vVtTrasl[j-1]) ; // se attacco a scivolo, accorcio della lunghezza dell'attacco if ( GetLeadInType() == POCKET_LI_GLIDE) { double dLen ; pRet->GetLength( dLen) ; if ( dLen > m_Params.m_dLiTang + 10 * EPS_SMALL) if ( ! pRet->TrimEndAtLen( dLen - m_Params.m_dLiTang)) pRet->Clear() ; else pRet->Clear() ; } // emetto SetFeed( GetFeed()) ; if ( pRet->GetCurveCount() > 0 && AddCurveMove( pRet) == GDB_ID_NULL) return false ; } } // atrimenti ultimo step, uscita e retrazione else { // dati fine entità Point3d ptEnd ; pCurve->GetEndPoint( ptEnd) ; Vector3d vtEnd ; pCurve->GetEndDir( vtEnd) ; // aggiungo uscita Point3d ptQ ; double dEndElev = dElev ; SetFeed( GetEndFeed()) ; if ( ! AddLeadOut( ptEnd, vtEnd, vtExtr, pRCrv, bSplitArcs, false, ptQ, dEndElev, false)) { m_pMchMgr->SetLastError( 2416, "Error in Pocketing : LeadOut not computable") ; return false ; } // aggiungo retrazione if ( ! AddRetract( ptQ, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) { m_pMchMgr->SetLastError( 2417, "Error in Pocketing : Retract not computable") ; return false ; } } } } } } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::CalcSpiral( const ISurfFlatRegion* pSrfPock, int& nReg, Point3d& ptStart, Vector3d& vtMidOut , bool& bMidOut, bool bSplitArcs,ICurveComposite* pMCrv, ICurveComposite* pRCrv, ICurveComposite* pCrvOEWithFlags, bool bChangedPrec, bool& bOptimizedTrap) { // inizializzo i risultati pMCrv->Clear() ; pRCrv->Clear() ; // primo offset pari al raggio utensile + sovramateriale double dTRad = 0.5 * m_TParams.m_dDiam ; double dOffs = dTRad + GetOffsR() ; // recupero il versore normale della superificie, 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 se ho isole per i 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) ; Point3d ptCen ; Vector3d vtN ; double dRad ; bool bCCW ; if ( pCrvBorder->IsACircle( 100 * EPS_SMALL, ptCen, vtN, dRad, bCCW)) { double dIntRad = 0 ; if ( m_Params.m_nSubType == POCKET_SUB_SPIRALOUT && GetLeadInType() == POCKET_LI_HELIX) { dIntRad = min( 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam), dRad - dOffs) ; m_dMaxHelixRad = dIntRad ; } if ( nReg == 0) { bool bOk = CalcCircleSpiral( ptCen, vtN, dRad - dOffs, dIntRad, bSplitArcs, pMCrv, pRCrv) ; if ( bOk) { pMCrv->GetStartPoint( ptStart) ; nReg = 1 ; pMCrv->GetStartDir( vtMidOut) ; Vector3d vtExtr ; pMCrv->GetExtrusion( vtExtr) ; vtMidOut.Rotate( vtExtr, 0, m_Params.m_bInvert ? 1 : -1) ; bMidOut = pCrvBorder->GetFirstCurve()->GetTempProp() == 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, dPocketSize, nBase, nSecondBase)) return false ; pCrvOEWithFlags->ToGlob( frPocket) ; if ( pCrvTrap->IsValid()) { pCrvTrap->SetExtrusion( vtExtr) ; if ( nReg == 0) { CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, pMCrv, pRCrv, bOptimizedTrap) ; if ( bOptimizedTrap) { pMCrv->ToGlob( frPocket) ; pRCrv->ToGlob( frPocket) ; nReg = 1 ; return true ; } } else return true ; } } // porto la superficie nel frame della svuotatura 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 ; } // ricavo il numero di Chunk ottenuti dall'Offset int nChunks = pSfrOffsVR->GetChunkCount() ; // ciclo sui Chunks 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, bInsert)) return false ; if ( bInsert) vOffs.emplace_back( Release( pCrvCompoBorder)) ; if ( nIter == 0) { // salvo il bordo per i link (non invertiti) 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 < dTRad + GetOffsR() + EPS_ZERO : dOffs - dOffsPrec < dTRad + 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 += GetSideStep() ; } // se devo usare un Offset più piccolo... else if ( ! bSmallRad) dOffs = dOffsPrec + ( nIter == 0 ? dTRad + GetOffsR() : dTRad) ; // altrimenti ho finito la regione da svuotare... else break ; ++ nIter ; // aggiorno l'iterazione } // se non ho trovato curve di Offset allora esco if (( int)vOffs.size() == 0) return true ; // cambio il punto iniziale della prima Curva di Offset Point3d ptNewStart ; double dOffR ; if (( int)vOffs.size() > 1) dOffR = dTRad + GetOffsR() ; else dOffR = dOffs ; if ( SetBetterPtStartForSubChunks( vOffs[0], pSrfToWork, ptStart, vtMidOut, bMidOut)) vOffs[0]->GetStartPoint( ptNewStart) ; else return false ; // se richiesta inversione for ( int i = 0 ; i < ( int)vOffs.size() && m_Params.m_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 for ( int i = 0 ; i < int( vOffs.size()) ; ++i ) { if ( i != 0) ModifyCurveToSmoothed( vOffs[i], m_TParams.m_dDiam / 16, m_TParams.m_dDiam / 16, false) ; vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; vOffsClosedCurves[i].Set( vOffs[i]->Clone()) ; } for ( int i = 0 ; i < int( vOffsFirstCurve.size()) ; ++ i) { if ( i != 0) ModifyCurveToSmoothed( vOffsFirstCurve[i], m_TParams.m_dDiam / 16, m_TParams.m_dDiam / 16, false) ; vOffsFirstCurve[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; } // setto il punto iniziale della svuotatura double dNewUS ; vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ; vOffs[0]->ChangeStartPoint( dNewUS) ; // riordino le curve cambiando il loro punto di inizio e creando poi i collegamenti int nClosestInd = -1 ; int nFlag ; double dDist = INFINITO ; Point3d ptHelp ; ICURVEPOVECTOR vLinks( vOffs.size()) ; for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) { Point3d ptS ; if ( ! vOffs[i]->GetStartPoint( ptS)) return false ; // setto i default delle variabili if ( ! DistPointCurve( ptS, *vOffs[i+1]).GetMinDistPoint( EPS_SMALL, ptHelp, nFlag)) return false ; dDist = INFINITO ; for ( int j = i + 1 ; j <= int( vOffs.size()) - 1 ; ++ j) { // cerco il punto più vicino della curva Point3d ptE ; if ( ! DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptE, nFlag)) return false ; if ( dDist > Dist( ptS, ptE) ) { dDist = Dist( ptS, ptE) ; nClosestInd = j ; ptHelp.Set( ptE.x, ptE.y, ptE.z) ; } } // avendo la curva più vicina ... // 1) scambio la curva i con la curva nClosestInd if ( nClosestInd != i + 1) { PtrOwner ptoCCHelp( Release( vOffs[i + 1])) ; vOffs[i + 1].Set( vOffs[nClosestInd]) ; vOffs[nClosestInd].Set( ptoCCHelp) ; } // 2) cambio il suo punto iniziale ... double dU ; Point3d ptNE( ptHelp.x, ptHelp.y, ptHelp.z) ; if ( ! vOffs[i + 1]->GetParamAtPoint( ptNE, dU)) return false ; vOffs[i + 1]->ChangeStartPoint( dU) ; // 2.1) Accorcio la curva per velocizzare ... if (( int)vOffs.size() > 1) { // accorcio se ho almeno due Offset // copio le curve i e i+1 nel caso non riesca a tagliarle ... double dUNS, dUNE ; PtrOwner pCrvTest( CreateCurveComposite()) ; // possibile collegamento tra la curva i ed i+1 PtrOwner pOff_i0( CloneCurveComposite( vOffs[i])) ; PtrOwner pOff_i1( CloneCurveComposite( vOffs[i + 1])) ; // cerco di tagliare le curve, ottenendo il collegamento // NB. se la curva di offset è la prima, non devo tagliarla, accorcio solo le successive ... if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsClosedCurves, vOffsFirstCurve, pCrvTest, i == 0 ? 0 : 0.01, 0.01, 2)) { // se non sono riuscito, ritorno alla configurazione iniziale PtrOwner pCrvLink( CreateCurveComposite()) ; Vector3d vS, vE ; vOffs[i].Set( pOff_i0) ; vOffs[i+1].Set( pOff_i1) ; if ( ! vOffs[i]->GetStartDir( vS) || ! vOffs[i+1]->GetStartDir( vE)) return false ; if ( CalcBoundedSmootedLink( ptS, vS, ptNE, vE, 0.5, vOffsFirstCurve, pCrvLink)) vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento else { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } continue ; } if ( ! pCrvTest->GetStartPoint( ptS) || ! pCrvTest->GetEndPoint( ptNE) || ! vOffs[i]->GetParamAtPoint( ptS, dUNS) || ! vOffs[i+1]->GetParamAtPoint( ptNE, dUNE)) return false ; // imposto il nuovo punto inziale della curva successiva vOffs[i+1]->ChangeStartPoint( dUNE) ; PtrOwner pCrvNewOffS( CloneCurveComposite( vOffs[i])) ; if ( dUNS > EPS_SMALL) { // se parametro di trim sufficientemente grande pCrvNewOffS.Set( GetCurveComposite( vOffs[i]->CopyParamRange( 0, dUNS))) ; // sostituisco la curva i-esima con quella tagliata vOffs[i]->Clear() ; vOffs[i].Set( pCrvNewOffS) ; } // aggiorno il collegamento vLinks[i + 1].Set( pCrvTest) ; } } // copio il vettore degli Offset per settare poi la Feed ICRVCOMPOPOVECTOR vOffsFeed ; vOffsFeed.reserve(( int)vOffs.size()) ; for ( int i = 0 ; i < ( int)vOffs.size() ; ++ i) vOffsFeed.emplace_back( vOffs[i]->Clone()) ; // 3) controllo eventuali parti non svuotate... pCrvOEWithFlags->ToLoc( frPocket) ; PtrOwner pSrfToCut( CreateSurfFlatRegion()) ; if ( GetUnclearedRegion( vOffsFirstCurve, vOffs, vLinks, pCrvOEWithFlags, pSrfToCut)) { // 4) Modifico i percorsi if ( ! RemoveExtraParts( pSrfToCut, vOffs, vOffsClosedCurves, vOffsFirstCurve, vLinks)) return false ; } // calcolo il percorso di ritorno if (( int)vOffs.size() >= 2) { pRCrv->Clear() ; // punto inziale e finale | vettore iniziale e finale Point3d ptStart ; vOffs.back()->GetEndPoint( ptStart) ; Point3d ptEnd ; vOffs.front()->GetStartPoint( ptEnd) ; Vector3d vtStart ; vOffs.back()->GetEndDir( vtStart) ; Vector3d vtEnd ; vOffs.front()->GetStartDir( vtEnd) ; // calcolo il ritorno (garantendo che non esca dalla svuotatura) PtrOwner pCrvLink( CreateCurveComposite()) ; if ( CalcBoundedSmootedLink( ptStart, vtStart, ptEnd, vtEnd, 0.5, vOffsFirstCurve, pCrvLink)) { pRCrv->AddCurve( Release( pCrvLink)) ; pRCrv->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, false) ; // se necessario, approssimo archi con rette if ( bSplitArcs && ! ApproxWithLines( pRCrv)) { m_pMchMgr->SetLastError( 2421, "Error in Pocketing : Linear Approx not computable") ; return false ; } VerifyArcs( pRCrv) ; } else { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } // Feed per percorso di ritorno AssignFeedForReturnPath( pRCrv) ; } // 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 ; // nel caso di lucidatura setto proprietà alle curve di collegamento per poterle identificare if ( m_TParams.m_nType == TT_MILL_POLISHING) { for ( int j = nCrvsCount0 ; j < pMCrv->GetCurveCount() ; ++ j) pMCrv->SetCurveTempProp( j, LINK_CURVE_PROP) ; } } pMCrv->AddCurve( vOffs[i]->Clone()) ; } // verifico il percorso di lavoro if ( pMCrv->GetCurveCount() == 0) { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } // se necessario, approssimo archi con rette if ( bSplitArcs && ! ApproxWithLines( pMCrv)) { m_pMchMgr->SetLastError( 2421, "Error in Pocketing : Linear Approx not computable") ; return false ; } // eventuale sistemazione archi VerifyArcs( pMCrv) ; // setto estrusione pMCrv->SetExtrusion( vtExtr) ; // riporto tutto nel sistema di riferimento originale pMCrv->ToGlob( frPocket) ; pRCrv->ToGlob( frPocket) ; ptStart.ToGlob( frPocket) ; vtMidOut.ToGlob( frPocket) ; pCrvOEWithFlags->ToGlob( frPocket) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::ModifyCurveToSmoothed( ICurveComposite* pCrv, double dRightLen, double dLeftLen, bool bAsParam) { // 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 false ; // 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 int nMaxTestForArcs = 3 ; // tentativi per creare l'arco int nIterForArcs = 0 ; bool IntersBTWArcs = false ; // creo l'arco di raccordo PtrOwner pCrvArc( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ; // controllo che l'arco creato non sia troppo piccolo 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 ; } 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 ; } // 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( CreateCurveComposite()) ; 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) ; } pCrv->Clear() ; pCrv->AddCurve( Release( pCrvCO_temp)) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, ICRVCOMPOPOVECTOR& vOffsCL, ICRVCOMPOPOVECTOR& vFirstOffset, ICurveComposite* pCrvLink, double dLenPercS, double dLenPercE, int nMaxIter) { // controllo i parametri if ( pCrvS == nullptr || pCrvE == nullptr ) return false ; pCrvLink->Clear() ; PtrOwner ptCrvFinal( CreateCurveComposite()) ; 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 ... for ( int i = 0 ; i < int( vFirstOffset.size()) ; ++ i) { if ( vFirstOffset[i]->IsPointOn( ptSS) && vFirstOffset[i]->IsPointOn( ptSE)) dLenPercS = EPS_SMALL * 0.5 ; if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE)) dLenPercE = EPS_SMALL * 0.5 ; } double dLStepS = dLenPercS < EPS_SMALL ? 0. : m_TParams.m_dDiam * 0.5 ; double dLStepE = dLenPercE < EPS_SMALL ? 0. : m_TParams.m_dDiam * 0.5 ; dLenE = 0 ; // calcolo i possibili BiArchi tra le due curve int nIter = 0 ; while ( nIter < nMaxIter) { // calcolo il BiArco PtrOwner ptBiArc( CreateCurveComposite()) ; if ( ! CalcBoundedSmootedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, ptBiArc) || ptBiArc->GetCurveCount() == 0) return false ; ptCrvFinal->Clear() ; ptCrvFinal.Set( ptBiArc->Clone()) ; if ( dLenPercE < EPS_SMALL && dLenPercS < EPS_SMALL) 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 ; } //---------------------------------------------------------------------------- bool Pocketing::RemoveExtraParts( ISurfFlatRegion* pSrfToCut, ICRVCOMPOPOVECTOR& vOffs, ICRVCOMPOPOVECTOR& vOffsClosedCurves, ICRVCOMPOPOVECTOR& vOffsFirstCurve, ICURVEPOVECTOR& vLinks) { if ( pSrfToCut == nullptr || ( int)vOffs.size() == 0) 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, 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, 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, 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, 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, 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, ptCrvNewOffs)) bSucc = false ; PtrOwner pCrvPathI( pCrvPath->Clone()) ; pCrvPathI->Invert() ; PtrOwner ptCrvNewOffsI( CreateCurveComposite()) ; if ( IsNull( ptCrvNewOffsI)) return false ; if ( ! GetNewCurvetWithPath( pCrvH1I, pCrvH2I, pCrvPathI, vOffsFirstCurve, vOffsClosedCurves, 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 ; } //--------------------------------------------------------------------------- bool Pocketing::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 ; } //--------------------------------------------------------------------------- bool Pocketing::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 ; } //--------------------------------------------------------------------------- bool Pocketing::CutOffsetToClosestPoint( ICRVCOMPOPOVECTOR& vCurves, const Point3d& ptFocus, ICurveComposite* pCrv1, ICurveComposite* pCrv2, int& nIndex) { // controllo di avere almeno un offset... if (( int)vCurves.size() == 0) 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 ; } //--------------------------------------------------------------------------- bool Pocketing::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 ; } //---------------------------------------------------------------------------- bool Pocketing::GetNewCurvetWithCentroid( const ICurveComposite* pCrvH1, const ICurveComposite* pCrvH2, Point3d& ptC, bool bCir, ICRVCOMPOPOVECTOR& VFirstOff, ICurveComposite* pCrvNewCurve) { PtrOwner pPath1( CreateCurveComposite()) ; PtrOwner pPath2( CreateCurveComposite()) ; if ( IsNull( pPath1) || IsNull( pPath2)) return false ; pCrvNewCurve->Clear() ; Point3d ptS, ptE = ptC ; Vector3d vtTanS, vtTanE ; if ( bCir) { // creo una circonferenza // 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, pPath1)) return false ; Vector3d vtS_cir ; pPath1->GetStartDir( vtS_cir) ; Vector3d vtS_CrvH1 ; pCrvH1->GetEndDir( vtS_CrvH1) ; if ( ! AreSameVectorApprox( vtS_cir, vtS_CrvH1)) pPath1->Invert() ; // Assegno la Feed if ( ! AssignFeedCrvOnUnclearedRegions( pPath1)) return false ; // 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, 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 ; } else { // creo un BiArco // 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, pPath1) || ! CalcBoundedSmootedLink( ptE, vtTanS, ptS, vtTanS, 0.5, VFirstOff, pPath2)) return false ; // Assegno la Feed a queste nuove curve if ( ! AssignFeedCrvOnUnclearedRegions( pPath1) || ! AssignFeedCrvOnUnclearedRegions( 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 ; } //---------------------------------------------------------------------------- bool Pocketing::GetNewCurvetWithPath( const ICurveComposite* pCrvH1, const ICurveComposite* pCrvH2, ICurveComposite* pCrvPath, ICRVCOMPOPOVECTOR& VFirstOff, ICRVCOMPOPOVECTOR& VoffsCl, ICurveComposite* pCrvNewCurve) { 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, pPath1)) if ( ! CalcBoundedLink( ptS, ptE, VFirstOff, pPath1)) return false ; if ( ! CalcBoundedSmootedLink( ptH, vtH, ptS, vtTanS, 0.5, VFirstOff, 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, pPath2, VoffsCl)) return false ; // alla curva ottenuta setto la Feed if ( ! AssignFeedCrvOnUnclearedRegions( pCrvToAdd)) 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, pCrvBiArcHelper)) if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper)) return false ; // assegno la Feed ... if ( ! AssignFeedCrvOnUnclearedRegions( 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, pCrvBiArcHelper)) if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper) ) return false ; // assegno la Feed ... if ( ! AssignFeedCrvOnUnclearedRegions( pCrvBiArcHelper)) return false ; if ( ! pCrvNewCurve->AddCurve( pCrvBiArcHelper->Clone()) || ! pCrvNewCurve->AddCurve( pCrvH2->Clone())) { ++nTry ; } else { bFound = true ; } } if ( ! bFound) return false ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::ManageSmoothAndAutoInters( ICurveComposite* pCrv, ICurveComposite* pCrvPath, ICurveComposite* pPath1, 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 ; while ( ! bFound && nIter < 45) { vTanE.Rotate( Z_AX, 90 - nIter) ; // vettore perpendicolare PtrOwner pLine( CreateCurveLine()) ; pLine->SetPVL( ptHS, vTanE, m_TParams.m_dDiam / 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, m_TParams.m_dDiam / 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, 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 ; } //---------------------------------------------------------------------------- bool Pocketing::RemoveFirstLoopFromSfr( ISurfFlatRegion* pSrfOrig) { if ( pSrfOrig == nullptr) return true ; PtrOwner pSrfRes( CreateSurfFlatRegion()) ; if ( IsNull( pSrfRes)) return false ; for ( int c = 1 ; c < pSrfOrig->GetChunkCount() ; ++ c) { PtrOwner pSrfHelp( CreateSurfFlatRegion()) ; if ( IsNull( pSrfHelp)) return false ; PtrOwner pCrvMiniChunkBorder( GetCurveComposite( pSrfOrig->GetLoop( c, 0))) ; pSrfHelp->AddExtLoop( pCrvMiniChunkBorder->Clone()) ; if ( c == 1) pSrfRes.Set( pSrfHelp->Clone()) ; else pSrfRes->Add( *( pSrfHelp)) ; } pSrfOrig->Clear() ; pSrfOrig->CopyFrom( pSrfRes) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::RemoveExtraPartByMedialAxis( const ISurfFlatRegion* pChunkToCut, ICRVCOMPOPOVECTOR& vOffsFirstCurve, 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( CreateCurveComposite()) ; 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 superificie che racchiude il mio tool PtrOwner pCrvToolShape( CreateCurveArc()) ; if ( IsNull( pCrvToolShape)) return false ; pCrvToolShape->SetXY( ptC, m_TParams.m_dDiam / 2 + 5 * EPS_SMALL) ; PtrOwner pSrfTool( CreateSurfFlatRegion()) ; if ( IsNull( pSrfTool) || ! pSrfTool->AddExtLoop( Release( pCrvToolShape)) || ! pSrfTool->IsValid()) break ; PtrOwner pSrfTest( CloneSurfFlatRegion( pSrfBiggerChunk)) ; if( IsNull( pSrfTest)) return false ; pSrfTest->Subtract( *pSrfTool) ; if( IsNull( pSrfTest) || ! pSrfTest->IsValid() || ! pSrfTest->GetArea( dArea) || dArea < 10 * EPS_SMALL || bForceCentroid) { if ( nIter == 1) { // se prima iterazione -> ritorno il centroide com punto nOptFlag = 1 ; ptCentroid = ptC ; return true ; } else { // se non sono alla prima iterazione -> memorizzo il centroide nel vettore // 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( CreateCurveComposite()) ; 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 ; } pCrvMedAx->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ; PolyArc PlMedAxArc ; pCrvMedAx->ApproxWithArcsEx( 500 * EPS_SMALL, ANG_TOL_STD_DEG, LIN_FEA_STD, PlMedAxArc) ; pCrvMedAx->Clear() ; if( ! pCrvMedAx->FromPolyArc( PlMedAxArc)) { bForceCentroid = true ; -- nIter ; continue ; } // smusso la curva ModifyCurveToSmoothed( pCrvMedAx, 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), m_TParams.m_dDiam / 2 + 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 (( int)vCrvCoMedAxi.size() == 0 && ( int)vPtCentroid.size() == 0) { nOptFlag = 0 ; return true ; } // ora collego la varie curve medial Axis trovate tra loro (ottenendo quindi una curva che svuota tutta la regione) PtrOwner pCrvCoBackLink( CreateCurveComposite()) ; // curva che collega primo e ultimo medial Axis 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( CreateCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; if ( ! CalcBoundedSmootedLink( ptS, vtS, ptE, vtE, 0.5, vOffsFirstCurve, 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)) if ( ! CalcBoundedSmootedLink( ptEOriginal, vtEOriginal, ptSOriginal, vtSOriginal, 0.5, vOffsFirstCurve, pCrvCoBackLink)) if ( ! 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( GetCurveComposite( pCrvPath->CopyParamRange( 0, dU))) ; if ( IsNull( pCrvA)) return false ; PtrOwner pCrvB( GetCurveComposite( 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( CreateCurveComposite()) ; if ( IsNull( pCrvPath1)) return false ; if ( ! CalcBoundedSmootedLink( ptClosestOnPath, vtTanCpt, vPtCentroid[i], vtTanCpt, 0, vOffsFirstCurve, pCrvPath1)) if ( ! 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 ; } //---------------------------------------------------------------------------- bool Pocketing::CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, ICRVCOMPOPOVECTOR& vOffIslands, ICurveComposite* pCrvLink) { 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 pLine( CreateCurveComposite()) ; if ( ! pLine->AddPoint( ptStart) || ! pLine->AddLine( ptEnd)) return false ; pLine->SetExtrusion( vtExtr) ; // la porto in locale al riferimento della svuotatura CurveLocal LineLoc( pLine, 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( pLine->Clone()) ; if ( IsNull( pCompo) ) return false ; if ( ! pCompo->LocToLoc( GLOB_FRM, frLoc) ) return false; PtrOwner pCompoHelp( pLine->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 ; for ( int j = 0 ; j < int( ccClass.size()) ; ++j ) { // per ogni intersezione j con l'offset dell'isola i if ( ccClass[j].nClass == CRVC_OUT) { // se ho intersezione spezzo il segmento 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( GetCurve( vOffIslands[i]->CopyParamRange( dOffS, dOffE))) ; PtrOwner pCrvB( GetCurve( 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)) ; } } else { // se non interseco 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 ; } //------------------------------------------------------------------------------ bool Pocketing::CalcBoundedSmootedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, const Vector3d& vtEnd, double dParMeet, ICRVCOMPOPOVECTOR& vOffIslands, ICurveComposite* 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( CreateCurveArc()) ; if ( IsNull( pCrvCir)) return false ; pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, m_Params.m_bInvert ? 360 : - 360, 0, Z_AX) ; pBiArcLink.Set( pCrvCir->Clone()) ; pCrvLink->AddCurve( pBiArcLink->Clone()) ; return true ; } if ( IsNull( pBiArcLink)) return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; // se BiArco troppo grande allora lo modifico double dLenBiArc ; pBiArcLink->GetLength( dLenBiArc) ; if ( dLenBiArc > 200 * 1000 * EPS_SMALL) { PtrOwner pCrvNewBiArcS( CreateCurveComposite()) ; 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 ; for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { // per ogni intersezione j con l'offset dell'isola i if ( ccClass[j].nClass == CRVC_OUT && ccClass.size() > 1) { // se ho intersezione spezzo il segmento 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( GetCurve( vOffIslands[i]->CopyParamRange( dOffS, dOffE))) ; PtrOwner pCrvB( GetCurve( 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) ; } } else { // se non interseco 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 ; } } ModifyCurveToSmoothed( pCompo, 0.05, 0.05, true) ; pCrvLink->AddCurve( Release( pCompo)) ; return true ; } //----------------------------------------------------------------------------- bool Pocketing::ModifyBiArc( ICurve* pBiArcLink, double dToll, ICurveComposite* pNewBiArc) { if ( pBiArcLink == nullptr) return false ; if ( dToll > 0.99 || dToll < 0.01) return false ; pNewBiArc->Clear() ; double dUS, dUE ; pBiArcLink->GetDomain( dUS, dUE) ; PtrOwner pArc1( GetCurve( pBiArcLink->CopyParamRange( dUS, (dUS + dUE) / 2))) ; PtrOwner pArc2( GetCurve( 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( GetCurve( pArc1->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll))) ; // Arc1 PtrOwner pArc1B( GetCurve( pArc1->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE))) ; // Arc2 Point3d pt1A, pt1B ; pArc1A->GetEndPoint( pt1A) ; pArc1B->GetStartPoint( pt1B) ; PtrOwner pLine1( CreateCurveLine()) ; pLine1->Set( pt1A, pt1B) ; // Linea // secondo pezzo pArc2->GetDomain( dUS, dUE) ; PtrOwner pArc2A( GetCurve( pArc2->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll))) ; // Arc1 PtrOwner pArc2B( GetCurve( pArc2->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE))) ; // Arc2 Point3d pt2A, pt2B ; pArc2A->GetEndPoint( pt2A) ; pArc2B->GetStartPoint( pt2B) ; PtrOwner pLine2( CreateCurveLine()) ; pLine2->Set( pt2A, pt2B) ; // Linea 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 ; } //------------------------------------------------------------------------------ bool Pocketing::CutCurveByOffsets( ICurveComposite* pCurve, ICRVCOMPOPOVECTOR& vOffs) { // controllo parametri ingresso if (( int)vOffs.size() == 0) return true ; ICRVCOMPOPOVECTOR vOffOrig( vOffs.size()) ; for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) { vOffOrig[i].Set( vOffs[i]->Clone()) ; } Point3d ptStart ; if ( ! pCurve->GetStartPoint( ptStart)) return false ; PtrOwner pCompo( pCurve->Clone()) ; PtrOwner pCompoHelp( pCurve->Clone()) ; PtrOwner pOffCheck( CreateCurveComposite()) ; if ( IsNull( pCompoHelp) || IsNull( pCompo) || IsNull( pOffCheck)) return false ; int nTypeOfCut = -1 ; for ( int i = 0; i < int( vOffOrig.size()) ; ++ i) { if ( vOffOrig[i]->IsPointOn( ptStart)) pOffCheck.Set( vOffOrig[i]->Clone()) ; CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCompo, *vOffOrig[i]) ; intCC.GetCurveClassification( 0, 10 * EPS_SMALL, ccClass) ; if ( ! pCompoHelp->Clear()) return false ; if ( ccClass.size() > 1) { if ( ccClass[0].nClass == CRVC_IN) nTypeOfCut = CRVC_OUT ; else nTypeOfCut = CRVC_IN ; 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( GetCurve( vOffOrig[i]->CopyParamRange( dOffS, dOffE))) ; PtrOwner pCrvB( GetCurve( 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()) ; } } pCurve->Clear() ; pCurve->AddCurve( Release( pCompo)) ; return true ; } //------------------------------------------------------------------------------ bool Pocketing::CalcBoundedLinkWithBiArcs( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, const Vector3d& vtEnd, const ICurve* pCrvBound, ICurveComposite* pCrvLink) { double dAngStart, dAngEnd ; vtStart.GetAngleXY( X_AX, dAngStart) ; vtEnd.GetAngleXY( X_AX, dAngEnd) ; PtrOwner pBiArcLink( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, 0.5)) ; if ( IsNull( pBiArcLink)) return false ; // verifico se esce dalla svuotatura CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pBiArcLink, *pCrvBound) ; intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; // se nessuno o un solo tratto e interno, il biarco è il collegamento if ( ccClass.empty() || ( ccClass.size() == 1 && ccClass[0].nClass == CRVC_IN)) { pCrvLink->AddCurve( Release( pBiArcLink)) ; } // altrimenti creo un percorso con biarchi e opportuni tratti della curva di contenimento else { PtrOwner pCompo( CreateCurveComposite()) ; if ( IsNull( pCompo)) return false ; double dPar1, dPar2 ; Point3d ptMinDist1, ptMinDist2 ; Vector3d vtDir1, vtDir2 ; double dAng1, dAng2 ; int nFlag ; DistPointCurve distPtSCrv( ptStart, *pCrvBound) ; distPtSCrv.GetParamAtMinDistPoint( 0, dPar1, nFlag) ; pCrvBound->GetPointTang( dPar1, ICurve::FROM_MINUS, ptMinDist1, vtDir1) ; vtDir1.GetAngleXY( X_AX, dAng1) ; DistPointCurve distPtECrv( ptEnd, *pCrvBound) ; distPtECrv.GetParamAtMinDistPoint( 0, dPar2, nFlag) ; pCrvBound->GetPointTang( dPar2, ICurve::FROM_MINUS, ptMinDist2, vtDir2) ; vtDir2.GetAngleXY( X_AX, dAng2) ; pCompo->AddCurve( GetBiArc( ptStart, -dAngStart, ptMinDist1, -dAng1, 0.5)) ; // primo biarco pCompo->AddCurve( pCrvBound->CopyParamRange( dPar1, dPar2)) ; // tratto di pCrvBound pCompo->AddCurve( GetBiArc( ptMinDist2, -dAng2, ptEnd, -dAngEnd, 0.5)) ; // secondo biarco pCrvLink->AddCurve( Release( pCompo)) ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetSignedDistFromRealDirection( const Point3d ptP, const Vector3d vtDir, double& dDist, Vector3d& vtNorm) { // calcolo la distanza lungo vtDir... if ( ! GetSignedDistFromStmRaw( m_nPhase, ptP, vtDir, dDist, vtNorm)) return false ; // se risulta infinita, allora sono fuori dal grezzo, quindi inverto vtDir e ricalcolo la distanza if ( dDist > INFINITO - 1) { if ( ! GetSignedDistFromStmRaw( m_nPhase, ptP, -vtDir, dDist, vtNorm)) return false ; // se ancora infinita, errore else if ( dDist > INFINITO - 1) return false ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::ComputePolishingPath( ICurveComposite* pMCrv, ICurveComposite* pRCrv, bool bSplitArcs) { PtrOwner pCompo( CreateCurveComposite()) ; PtrOwner pCrvBound( CreateCurveComposite()) ; // curva bound da usare per CalcBoundedLink ICRVCOMPOPOVECTOR vpCrvsEp ; Frame3d frLoc ; Vector3d vtExtr ; pMCrv->GetExtrusion( vtExtr) ; frLoc.Set( ORIG, vtExtr) ; pMCrv->ToLoc( frLoc) ; for ( int i = 0 ; i < pMCrv->GetCurveCount() ; i ++) { int nProp = 0 ; if ( ! pMCrv->GetCurveTempProp( i, nProp)) return false ; // se è un tratto di collegamento ho concluso percorso su cui aggiungere epicicli if ( nProp == LINK_CURVE_PROP) { if ( pCompo->IsValid()) { PtrOwner pCrvEp( CreateCurveComposite()) ; // la curva di bound è l'offset che calcolo in AddEpicycles per la prima curva compo trovata in pMCrv bool bAddEp = ( ! pCrvBound->IsValid()) ? AddEpicycles( pCompo, pCrvEp, pCrvBound) : AddEpicycles( pCompo, pCrvEp) ; if ( ! bAddEp) return false ; vpCrvsEp.emplace_back( Release( pCrvEp)) ; pCompo.Set( CreateCurveComposite()) ; } } // se non è tratto di collegamento lo aggiungo alla curva else { if ( ! pCompo->AddCurve( pMCrv->GetCurve(i)->Clone())) return false ; } } // ultima curva if ( ! IsNull( pCompo)) { PtrOwner pCrvEp( CreateCurveComposite()) ; if ( ! AddEpicycles( pCompo, pCrvEp)) return false ; vpCrvsEp.emplace_back( Release( pCrvEp)) ; } // calcolo i collegamenti ICURVEPOVECTOR vLinks( vpCrvsEp.size()) ; for ( int i = 1 ; i < int( vpCrvsEp.size()) ; ++ i) { // punti e direzioni di inizio e fine Point3d ptStart ; Vector3d vtStart ; vpCrvsEp[i-1]->GetEndPoint( ptStart) ; vpCrvsEp[i-1]->GetEndDir( vtStart) ; Point3d ptEnd ; Vector3d vtEnd ; vpCrvsEp[i]->GetStartPoint( ptEnd) ; vpCrvsEp[i]->GetStartDir( vtEnd) ; // calcolo il collegamento con biarchi (garantendo che non esca dalla svuotatura) PtrOwner pCrvLink( CreateCurveComposite()) ; if ( CalcBoundedLinkWithBiArcs( ptStart, vtStart, ptEnd, vtEnd, pCrvBound, pCrvLink)) { vLinks[i].Set( pCrvLink) ; } else { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } } // calcolo il percorso di ritorno pRCrv->Clear() ; if ( vpCrvsEp.size() >= 2) { // punti di inizio e fine Point3d ptStart ; Vector3d vtStart ; vpCrvsEp.back()->GetEndPoint( ptStart) ; vpCrvsEp.back()->GetEndDir( vtStart) ; Point3d ptEnd ; Vector3d vtEnd ; vpCrvsEp.front()->GetStartPoint( ptEnd) ; vpCrvsEp.front()->GetStartDir( vtEnd) ; // calcolo il ritorno con biarchi (garantendo che non esca dalla svuotatura) PtrOwner pCrvLink( CreateCurveComposite()) ; if ( CalcBoundedLinkWithBiArcs( ptStart, vtStart, ptEnd, vtEnd, pCrvBound, pCrvLink)) { pRCrv->AddCurve( Release( pCrvLink)) ; pRCrv->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, false) ; // se necessario, approssimo archi con rette if ( bSplitArcs && ! ApproxWithLines( pRCrv)) { m_pMchMgr->SetLastError( 2421, "Error in Pocketing : Linear Approx not computable") ; return false ; } VerifyArcs( pRCrv) ; } else { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } } // creo il percorso di lavoro a partire dalla raccolta delle curve con epicicli e dei collegamenti pMCrv->Clear() ; for ( int i = 0 ; i < int( vpCrvsEp.size()) ; ++ i) { // se collegamento da aggiungere if ( ! IsNull( vLinks[i])) { // accodo nel percorso di lavorazione pMCrv->AddCurve( Release( vLinks[i])) ; } // aggiungo la curva pMCrv->AddCurve( Release( vpCrvsEp[i])) ; } // se necessario, approssimo archi con rette if ( bSplitArcs && ! ApproxWithLines( pMCrv)) { m_pMchMgr->SetLastError( 2421, "Error in Pocketing : Linear Approx not computable") ; return false ; } VerifyArcs( pMCrv) ; pMCrv->ToGlob( frLoc) ; pRCrv->ToGlob( frLoc) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::AddEpicycles( ICurveComposite* pCompo, ICurveComposite * pCrv, ICurveComposite * pCrvBound) { if ( m_Params.m_bInvert) pCompo->Invert() ; // oriento la curva in senso antiorario OffsetCurve OffsCrv ; double dOffs = m_Params.m_dEpicyclesRad ; if ( ! OffsCrv.Make( pCompo, dOffs, ICurve::OFF_FILLET)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } if ( OffsCrv.GetCurveCount() > 1) return false ; PtrOwner pCrvOffs( GetCurveComposite( OffsCrv.GetCurve())) ; if ( IsNull( pCrvOffs)) return false ; // verifico se devo resitituire la curva offsettata if ( pCrvBound) pCrvBound->AddCurve( pCrvOffs->Clone()) ; pCrv->Clear() ; double dParPrec = 0 ; for ( int i = 0 ; i < pCompo->GetCurveCount() ; i++) { // calcolo distanza epicili specifica per quel tratto double dLen ; pCompo->GetCurve( i)->GetLength( dLen) ; int nStep = max( 1, static_cast( ceil( ( dLen) / m_Params.m_dEpicyclesDist))) ; double dStep = 1.0 / nStep ; for ( int k = 1 ; k <= nStep ; k ++) { // creo epiciclo PtrOwner pCrvArc( CreateCurveArc()) ; Point3d ptCen ; Vector3d vtDir ; pCompo->GetCurve( i)->GetPointD1D2( k * dStep, ICurve::FROM_MINUS, ptCen, &vtDir) ; vtDir.Normalize() ; vtDir.Rotate( Z_AX, - 90) ; Point3d pt = ptCen + vtDir * m_Params.m_dEpicyclesRad ; pCrvArc->Set( ptCen, Z_AX, m_Params.m_dEpicyclesRad) ; double dU ; pCrvArc->GetParamAtPoint( pt, dU) ; pCrvArc->ChangeStartPoint( dU) ; // aggiungo tratto della curva offsettata double dPar ; pCrvOffs->GetParamAtPoint( pt, dPar) ; bool bAdd = pCrv->AddCurve( pCrvOffs->CopyParamRange( dParPrec, dPar)) ; // aggiungo epiciclo if ( ! pCrv->AddCurve( Release( pCrvArc))) { // se fallisco nell'aggiungere l'epiciclo tento nuovamente spostandolo di EPS_SMALL if ( bAdd) PtrOwner pCrvErased( pCrv->RemoveFirstOrLastCurve( true)) ; k -- ; dStep -= EPS_SMALL ; if ( dStep < EPS_SMALL) return false ; } else dParPrec = dPar ; } } // se necessario ripristino orientamento originale if ( m_Params.m_bInvert) pCrv->Invert() ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::AddApproach( const Point3d& ptP, const Vector3d& vtTool, double dSafeZ, double dSafeAggrBottZ, double dElev, double dAppr, bool bOutStart) { SetFlag( 1) ; // se con aggregato da sotto o equivalente (rinvio a 90 gradi su testa 5 assi) bool bBottomOutStart = false ; if ( m_bAggrBottom) { // distanza dal bordo del pezzo (se negativa il punto � fuori dal grezzo) double dDistBottom ; if ( ! GetDistanceFromRawSide( m_nPhase, ptP, m_vtAggrBottom, dDistBottom)) dDistBottom = 0 ; bBottomOutStart = ( dDistBottom < - 10 * EPS_SMALL) ; // aggiuntivo in Z double dAggZ = ( bBottomOutStart ? 0. : max( dElev + max( dSafeAggrBottZ, dAppr), 0.)) ; // pre-approccio Point3d ptP0 = ptP - Z_AX * dAggZ + m_vtAggrBottom * ( dDistBottom + m_AggrBottom.dEncH + dSafeZ) ; Point3d ptP00 = ptP0 + Z_AX * ( m_AggrBottom.dEncV + m_TParams.m_dLen + dAggZ - dElev) ; // se rinvio da sotto che richiede speciale rotazione if ( m_AggrBottom.nType == 1) { Vector3d vtAux = m_vtAggrBottom ; vtAux.Rotate( Z_AX, 0, 1) ; SetAuxDir( vtAux) ; if ( AddRapidStart( ptP00, MCH_CL_AGB_DWN) == GDB_ID_NULL) return false ; SetAuxDir( m_vtAggrBottom) ; SetFlag( 0) ; if ( AddRapidMove( ptP0, MCH_CL_AGB_IN) == GDB_ID_NULL) return false ; } // altrimenti rinvio normale else { SetAuxDir( m_vtAggrBottom) ; if ( AddRapidStart( ptP0, MCH_CL_AGB_IN) == GDB_ID_NULL) return false ; SetFlag( 0) ; } } // se sopra attacco c'� spazio per sicurezza o approccio double dSafeDist = ( m_bAggrBottom ? dSafeAggrBottZ : dSafeZ) ; if ( ! bBottomOutStart && dElev + max( dSafeDist, dAppr) > 10 * EPS_SMALL) { Point3d ptP1 = ptP + vtTool * ( dElev + dAppr) ; // se distanza di sicurezza minore di distanza di inizio if ( dSafeDist < dAppr + 10 * EPS_SMALL) { // 1 -> punto sopra inizio if ( ( ! m_bAggrBottom && AddRapidStart( ptP1) == GDB_ID_NULL) || ( m_bAggrBottom && AddRapidMove( ptP1) == GDB_ID_NULL)) return false ; } else { // 1a -> punto molto sopra inizio Point3d ptP1a = ptP1 + vtTool * ( dSafeDist - dAppr) ; if ( ( ! m_bAggrBottom && AddRapidStart( ptP1a) == GDB_ID_NULL) || ( m_bAggrBottom && AddRapidMove( ptP1a) == GDB_ID_NULL)) return false ; // 1 -> punto sopra inizio if ( ( dElev + dAppr) > 10 * EPS_SMALL || (( dElev + dAppr) > -EPS_ZERO && dAppr > EPS_SMALL)) { SetFlag( 0) ; if ( AddRapidMove( ptP1) == GDB_ID_NULL) return false ; } else ptP1 = ptP1a ; } // affondo al punto iniziale SetFlag( 0) ; bool bStartFeed = ( bOutStart || m_TParams.m_nType == TT_MILL_NOTIP) ; SetFeed( bStartFeed ? GetStartFeed() : GetTipFeed()) ; if ( ! AreSamePointApprox( ptP1, ptP) && AddLinearMove( ptP) == GDB_ID_NULL) return false ; } else { // affondo diretto al punto iniziale SetFlag( 0) ; if ( ( ! m_bAggrBottom && AddRapidStart( ptP) == GDB_ID_NULL) || ( m_bAggrBottom && AddRapidMove( ptP) == GDB_ID_NULL)) return false ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::AddLinkApproach( const Point3d& ptP, const Vector3d& vtTool, double dSafeZ, double dSafeAggrBottZ, double dElev, double dAppr, bool bOutStart) { // se sopra attacco c'è spazio per approccio if ( ( dElev + dAppr) > 10 * EPS_SMALL) { // 1b -> punto appena sopra inizio Point3d ptP1b = ptP + vtTool * ( dElev + dAppr) ; if ( ( dElev + dAppr) > EPS_SMALL) { SetFlag( 0) ; if ( AddRapidMove( ptP1b) == GDB_ID_NULL) return false ; } // affondo al punto iniziale SetFlag( 0) ; SetFeed( bOutStart ? GetStartFeed() : GetTipFeed()) ; if ( AddLinearMove( ptP) == GDB_ID_NULL) return false ; } else { // affondo diretto al punto iniziale SetFlag( 0) ; if ( AddRapidMove( ptP) == GDB_ID_NULL) return false ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::AddLinkRetract( const Point3d& ptP, const Vector3d& vtTool, double dSafeZ, double dSafeAggrBottZ, double dElev, double dAppr) { // se sopra uscita c'è spazio per approccio if ( ( dElev + dAppr) > 10 * EPS_SMALL) { // 4 -> movimento di risalita sopra il punto finale SetFeed( GetEndFeed()) ; Point3d ptP4 = ptP + vtTool * ( dElev + dAppr) ; if ( AddLinearMove( ptP4) == GDB_ID_NULL) return false ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::AddRetract( const Point3d& ptP, const Vector3d& vtTool, double dSafeZ, double dSafeAggrBottZ, double dElev, double dAppr) { // se con aggregato da sotto o equivalente (rinvio a 90 gradi su testa 5 assi) bool bBottomOutStart = false ; double dDistBottom ; if ( m_bAggrBottom) { // distanza dal bordo del pezzo if ( ! GetDistanceFromRawSide( m_nPhase, ptP, m_vtAggrBottom, dDistBottom)) dDistBottom = 0 ; bBottomOutStart = ( dDistBottom < - 10 * EPS_SMALL) ; } // se sopra uscita c'è spazio per sicurezza o approccio double dSafeDist = ( m_bAggrBottom ? dSafeAggrBottZ : dSafeZ) ; if ( ! bBottomOutStart && dElev + max( dSafeDist, dAppr) > 10 * EPS_SMALL) { if ( dSafeDist < dAppr + 10 * EPS_SMALL) { // 4 -> movimento di risalita sopra il punto finale SetFeed( GetEndFeed()) ; Point3d ptP4 = ptP + vtTool * ( dElev + dAppr) ; if ( AddLinearMove( ptP4) == GDB_ID_NULL) return false ; } else { // 4a -> movimento di risalita appena sopra il punto finale Point3d ptP4a = ptP + vtTool * ( dElev + dAppr) ; if ( dElev + dAppr > EPS_SMALL) { SetFeed( GetEndFeed()) ; if ( AddLinearMove( ptP4a) == GDB_ID_NULL) return false ; } // 4b -> movimento di risalita sopra il punto finale Point3d ptP4b = ptP4a + vtTool * ( dSafeDist - dAppr) ; if ( AddRapidMove( ptP4b) == GDB_ID_NULL) return false ; } } // se con aggregato da sotto o equivalente (rinvio a 90 gradi su testa 5 assi) if ( m_bAggrBottom) { // aggiuntivo in Z double dAggZ = ( bBottomOutStart ? 0. : max( dElev + max( dSafeAggrBottZ, dAppr), 0.)) ; // post-retract Point3d ptP0 = ptP - Z_AX * dAggZ + m_vtAggrBottom * ( dDistBottom + m_AggrBottom.dEncH + dSafeZ) ; Point3d ptP00 = ptP0 + Z_AX * ( m_AggrBottom.dEncV + m_TParams.m_dLen + dAggZ - dElev) ; if ( AddRapidMove( ptP0, MCH_CL_AGB_OUT) == GDB_ID_NULL) return false ; // se rinvio da sotto che richiede speciale rotazione if ( m_AggrBottom.nType == 1) { Vector3d vtAux = m_vtAggrBottom ; vtAux.Rotate( Z_AX, 0, 1) ; SetAuxDir( vtAux) ; if ( AddRapidMove( ptP00, MCH_CL_AGB_UP) == GDB_ID_NULL) return false ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::CalcLeadInStart( const Point3d& ptStart, const Vector3d& vtStart, const Vector3d& vtN, const ICurveComposite* pRCrv, Point3d& ptP1) const { // Assegno tipo e parametri int nType = GetLeadInType() ; if ( nType == POCKET_LI_GLIDE && ( pRCrv == nullptr || pRCrv->GetCurveCount() == 0)) nType = POCKET_LI_NONE ; // Calcolo punto iniziale switch ( nType) { case POCKET_LI_NONE : case POCKET_LI_ZIGZAG : case POCKET_LI_HELIX : ptP1 = ptStart ; return true ; case POCKET_LI_GLIDE : { double dLen, dU ; if ( ! pRCrv->GetLength( dLen) || ! pRCrv->GetParamAtLength( dLen - m_Params.m_dLiTang, dU) || ! pRCrv->GetPointD1D2( dU, ICurve::FROM_MINUS, ptP1)) { if ( ! pRCrv->GetStartPoint( ptP1)) return false ; } ptP1 += vtN * ( vtN * ( ptStart - ptP1)) ; return true ; } default : return false ; } } //---------------------------------------------------------------------------- bool Pocketing::AddLeadIn( const Point3d& ptP1, const Point3d& ptStart, const Vector3d& vtStart, const Vector3d& vtN, ISurfFlatRegion* pSrfChunk, const ICurveComposite* pRCrv, bool bAtLeft, bool bSplitArcs, bool bNoneForced, bool bSkipControl) { // Assegno il tipo int nType = GetLeadInType() ; if ( bNoneForced || AreSamePointEpsilon( ptP1, ptStart, 10 * EPS_SMALL) || ( nType == POCKET_LI_GLIDE && ( pRCrv == nullptr || pRCrv->GetCurveCount() == 0))) nType = POCKET_LI_NONE ; // Se elica e fattibile lo creo if ( nType == POCKET_LI_HELIX) { // vettore dal punto al centro elica Vector3d vtCen = vtStart ; vtCen.Rotate( vtN, 0, ( bAtLeft ? 1 : - 1)) ; // dati dell'elica double dRad = min( 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam), m_dMaxHelixRad) ; Point3d ptCen = ptP1 + vtCen * dRad ; double dDeltaN = ( ptStart - ptP1) * vtN ; double dAngCen = ceil( - dDeltaN / ( m_Params.m_dLiElev + 20 * EPS_SMALL)) * ( bAtLeft ? ANG_FULL : - ANG_FULL) ; // verifico se fattibile if ( bSkipControl || VerifyLeadInHelix( pSrfChunk, ptCen, dRad)) { // creo l'elica PtrOwner pArc( CreateCurveArc()) ; if ( IsNull( pArc) || ! pArc->Set( ptCen, vtN, dRad, - vtCen, dAngCen, dDeltaN)) return false ; // eventuale spezzatura if ( bSplitArcs) { PtrOwner pCompo ; if ( ! pCompo.Set( ConvertCurveToComposite( Release( pArc))) || ! ApproxWithLines( pCompo)) return false ; return ( AddCurveMove( pCompo, MCH_CL_LEADIN) != GDB_ID_NULL) ; } else { // emetto l'elica return ( AddCurveMove( pArc, MCH_CL_LEADIN) != GDB_ID_NULL) ; } } // altrimenti zigzag else nType = POCKET_LI_ZIGZAG ; } // Se zigzag e fattibile lo creo if ( nType == POCKET_LI_ZIGZAG) { // dati dello zigzag double dDeltaN = ( ptStart - ptP1) * vtN ; int nStep = int( ceil( - dDeltaN / ( m_Params.m_dLiElev + 20 * EPS_SMALL))) ; double dStep = - dDeltaN / nStep ; Point3d ptPa = ptP1 + vtStart * 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam) ; Point3d ptPb = ptP1 - vtStart * 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam) ; // verifico se fattibile if ( bSkipControl || VerifyLeadInZigZag( pSrfChunk, ptPa, ptPb)) { for ( int i = 1 ; i <= nStep ; ++ i) { if ( AddLinearMove( ptPa - vtN * ( i - 0.75) * dStep, MCH_CL_LEADIN) == GDB_ID_NULL) return false ; if ( AddLinearMove( ptPb - vtN * ( i - 0.25) * dStep, MCH_CL_LEADIN) == GDB_ID_NULL) return false ; } return ( AddLinearMove( ptStart, MCH_CL_LEADIN) != GDB_ID_NULL) ; } // altrimenti diretto else { nType = POCKET_LI_NONE ; if ( m_TParams.m_nType == TT_MILL_NOTIP) return false ; } } // Se a scivolo e fattibile if ( nType == POCKET_LI_GLIDE) { if ( pRCrv != nullptr) { // recupero la parte richiesta della curva di ritorno PtrOwner pCrv ; double dLen, dU ; if ( pRCrv->GetLength( dLen) && pRCrv->GetParamAtLength( dLen - m_Params.m_dLiTang, dU)) { double dParS, dParE ; pRCrv->GetDomain( dParS, dParE) ; if ( ! pCrv.Set( ConvertCurveToComposite( pRCrv->CopyParamRange( dU, dParE)))) return false ; } else { if ( ! pCrv.Set( pRCrv->Clone())) return false ; } pCrv->SetExtrusion( vtN) ; // la porto alla giusta quota Point3d ptFin ; pCrv->GetEndPoint( ptFin) ; Vector3d vtMove = ptStart - ptFin ; pCrv->Translate( vtMove) ; // assegno la corretta pendenza double dNini = ( ptP1 - ORIG) * vtN ; double dNfin = ( ptStart - ORIG) * vtN ; AdjustCurveSlope( pCrv, dNini, dNfin) ; // eventuale spezzatura if ( bSplitArcs && ! ApproxWithLines( pCrv)) return false ; // emetto return ( AddCurveMove( pCrv) != GDB_ID_NULL) ; } // altrimenti diretto else nType = POCKET_LI_NONE ; } // Se diretto if ( nType == POCKET_LI_NONE) { Point3d ptCurr = ptP1 ; GetCurrPos( ptCurr) ; if ( ! AreSamePointApprox( ptCurr, ptStart)) { if ( AddLinearMove( ptStart, MCH_CL_LEADIN) == GDB_ID_NULL) return false ; } return true ; } // Altrimenti errore return false ; } //---------------------------------------------------------------------------- bool Pocketing::AddLeadOut( const Point3d& ptEnd, const Vector3d& vtEnd, const Vector3d& vtN, const ICurveComposite* pRCrv, bool bSplitArcs, bool bNoneForced, Point3d& ptP1, double& dElev, bool bRecalcElev) { bool bOppositeHome ; return AddLeadOut( ptEnd, vtEnd, vtN, pRCrv, bSplitArcs, bNoneForced, ptP1, dElev, bOppositeHome, bRecalcElev) ; } //---------------------------------------------------------------------------- bool Pocketing::AddLeadOut( const Point3d& ptEnd, const Vector3d& vtEnd, const Vector3d& vtN, const ICurveComposite* pRCrv, bool bSplitArcs, bool bNoneForced, Point3d& ptP1, double& dElev, bool& bOppositeHome, bool bRecalcElev) { // assegno i parametri int nType = GetLeadOutType() ; if ( bNoneForced || ( nType == POCKET_LO_GLIDE && ( pRCrv == nullptr || pRCrv->GetCurveCount() == 0))) nType = POCKET_LO_NONE ; // eseguo a seconda del tipo switch ( nType) { case POCKET_LO_NONE : { // nessuna uscita ptP1 = ptEnd ; // determino elevazione su fine uscita if ( bRecalcElev) { double dEndElev ; if ( GetElevation( m_nPhase, ptP1 - 10 * EPS_SMALL * vtN, vtN, GetRadiusForStartEndElevation(), vtN, dEndElev)) dElev = dEndElev ; } // correzione per punto sotto il grezzo con testa normale da sopra double dSafeZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeZ() ; bool bAhUnderRaw = m_bAboveHead && ! m_bAggrBottom && ! m_bTiltingTab && GetAhPointUnderRaw( ptP1, vtN, 0, GetRadiusForStartEndElevation(), m_TParams.m_dLen, false, dSafeZ, vtN, dElev) ; bool bUhAboveRaw = ! m_bAboveHead && GetUhPointAboveRaw( ptP1, vtN, 0, GetRadiusForStartEndElevation(), m_TParams.m_dLen, false, dSafeZ, vtN, dElev) ; bOppositeHome = ( bAhUnderRaw || bUhAboveRaw) ; return true ; } case POCKET_LO_GLIDE : { // recupero la parte richiesta della curva di ritorno PtrOwner pCrv ; double dU ; if ( pRCrv->GetParamAtLength( m_Params.m_dLoTang, dU)) { if ( ! pCrv.Set( ConvertCurveToComposite( pRCrv->CopyParamRange( 0, dU)))) return false ; } else { if ( ! pCrv.Set( pRCrv->Clone())) return false ; } // la porto alla giusta quota Point3d ptIni ; pCrv->GetStartPoint( ptIni) ; Vector3d vtMove = ptEnd - ptIni ; pCrv->Translate( vtMove) ; Point3d ptFin ; pCrv->GetEndPoint( ptFin) ; ptFin += vtN * 1.0 ; pCrv->ModifyEnd( ptFin) ; // eventuale spezzatura if ( bSplitArcs && ! ApproxWithLines( pCrv)) return false ; // emetto AddCurveMove( pCrv) ; // determino elevazione su fine uscita ptP1 = ptFin ; if( bRecalcElev) { double dEndElev ; if ( GetElevation( m_nPhase, ptP1 - 10 * EPS_SMALL * vtN, vtN, GetRadiusForStartEndElevation(), vtN, dEndElev)) dElev = dEndElev ; } // correzione per punto sotto il grezzo con testa normale da sopra double dSafeZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeZ() ; bool bAhUnderRaw = m_bAboveHead && ! m_bAggrBottom && ! m_bTiltingTab && GetAhPointUnderRaw( ptP1, vtN, 0, GetRadiusForStartEndElevation(), m_TParams.m_dLen, false, dSafeZ, vtN, dElev) ; bool bUhAboveRaw = ! m_bAboveHead && GetUhPointAboveRaw( ptP1, vtN, 0, GetRadiusForStartEndElevation(), m_TParams.m_dLen, false, dSafeZ, vtN, dElev) ; bOppositeHome = ( bAhUnderRaw || bUhAboveRaw) ; return true ; } default : bOppositeHome = false ; return false ; } } //---------------------------------------------------------------------------- double Pocketing::GetRadiusForStartEndElevation( void) const { const double DELTA_ELEV_RAD = 4.0 ; double dDeltaRad = min( DELTA_ELEV_RAD, 0.5 * m_TParams.m_dTDiam) ; return ( 0.5 * m_TParams.m_dTDiam + dDeltaRad) ; } //---------------------------------------------------------------------------- bool Pocketing::GetParamOnOpenSide( const ICurveComposite* pCompo, const ICRVCOMPOPOVECTOR& vOtherCrv, 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() ; } // richiedo lunghezza superiore a diametro utensile più doppio offset radiale double dRefLen = ( bAllOpen ? 0 : m_TParams.m_dDiam + 2 * GetOffsR() - 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 (( m_bAboveHead && ptTest.z > ptMid.z + 100 * EPS_SMALL) || ( ! m_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 entrata if ( GetParamForPtStartOnEdge( pCrv, pCompo, vOtherCrv, 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) ; bFound = true ; if ( nPriorityOpenEdge == 0) return true ; } } dLenPrev = dLen ; } } else dLenPrev = 0 ; // vado alla successiva pPrevCrv = pCrv ; pCrv = ( bNextOk ? pNextCrv : nullptr) ; } return bFound ; } //---------------------------------------------------------------------------- bool Pocketing::AdjustContourWithOpenEdges( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vCrvIsl, const double dDiam, const double dOffR, const double dStep, const ISurfFlatRegion* pSfrLimit) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // raggio di riferimento per offset double dOutEdge = 0.5 * dDiam ; if ( m_Params.m_nSubType == POCKET_SUB_SPIRALIN || m_Params.m_nSubType == POCKET_SUB_ZIGZAG) dOutEdge = max( dOutEdge, dDiam - dStep) ; double dRad = dOutEdge + dOffR ; // ricavo i tratti omogenei ICRVCOMPOPOVECTOR vpCrvs ; if ( ! GetHomogeneousParts( pCrvCompo, vpCrvs)) return false ; // Offset esterno per CurveJ double dDiamJ = 1.05 * dDiam + 2 * dOffR ; // NB. 1.05 serve per eccedere leggermente, in modo che il contro-offset della prima curva di svuotatura presenti // dei piccoli archi tra i chiusi e gli aperti ( in modo da svuotare bene lungo il chiuso) // curva finale da restituire PtrOwner pCrvCompo_final( CreateCurveComposite()) ; if ( IsNull( pCrvCompo_final)) return false ; // parametro iniziale del tratto corrente, sulla curva originale double dParS = 0. ; // scorro tutti i tratti omogenei nel vettore for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { // recupero la proprietà della curva composita int nCurrTmpProp = vpCrvs[i]->GetTempProp() ; // aggiorno i parametri del lato aperto corrente sulla curva originale double dParE = dParS + 1. * vpCrvs[i]->GetCurveCount() ; // se aperta if ( nCurrTmpProp == 1) { if ( ! AdjustOpenEdge( pCrvCompo, vCrvIsl, dParS, dParE, dRad, dDiamJ, pSfrLimit, 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 if ( ! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone())) { 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 ; } //---------------------------------------------------------------------------- bool Pocketing::AdjustOpenEdge( const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vCrvIsland, const double dParS, const double dParE, const double dRad, const double dDiamJ, const ISurfFlatRegion* pSfrLimit, 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 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 validtà 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, dRad, pSfrLimit, 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)) ; } // 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) classifico la curva con la superficie CRVCVECTOR ccClass ; if ( pSfrInc->GetCurveClassification( *pCrvCurr, EPS_SMALL, ccClass)) { // 3) scorro tutte le classificazioni per cercare i tratti interni for ( int i = 0 ; i < ( int)ccClass.size() ; ++ i) { if ( ccClass[i].nClass == CRVC_IN) { // 4) se il tratto è interno, recupero tutti i suoi tratti omogenei PtrOwner pCrvCurrPartIn( ConvertCurveToComposite( pCrvCurr->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ; if ( IsNull( pCrvCurr) || ! pCrvCurr->IsValid()) return false ; ICRVCOMPOPOVECTOR vpCrvs ; if ( ! GetHomogeneousParts( pCrvCurrPartIn, vpCrvs)) return false ; // 5) considero solo i tratti chiusi for ( int cl = 0 ; cl < int( vpCrvs.size()) ; ++ cl) { if ( vpCrvs[cl]->GetTempProp() == 1) continue ; // 6) effettuo l'Offset della curva di metà dDiamJ OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( vpCrvs[cl], - dDiamJ * 0.5, ICurve::OFF_FILLET)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } // 7) 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)) { // 8) tengo solo le curve che non si chiudono su loro stesse if ( ! pOffLongestCrv->IsClosed()) { // 9) 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() ; // 10) aggiorno la regione di incidenza if ( ! pSfrInc->Add( *pSfrBean)) return false ; } pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ; } } } } } else return false ; } // 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 ( ! pSfrInc->Subtract( *pSfrLimit)) return false ; // controllo se la curva è un'isola bool bIsIsland = false ; double dArea ; Plane3d plCheck ; if ( ! pCrvCompo->GetArea( plCheck, dArea)) return false ; bIsIsland = AreSameVectorEpsilon( plCheck.GetVersN(), - Z_AX, 10 * EPS_SMALL) ; // recupero la curva di bordo del lato aperto corrente 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... 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, dUTrimE ; pCrvNewBorder->GetParamAtPoint( ptStart, dUTrimS, 300 * EPS_SMALL) ; pCrvNewBorder->GetParamAtPoint( ptEnd, dUTrimE, 300 * EPS_SMALL) ; // pulisco la curva originale pCrvBorder->Clear() ; if ( ! pCrvBorder->AddCurve( pCrvNewBorder->CopyParamRange( dUTrimS, dUTrimE)) || ! pCrvBorder->IsValid()) return false ; return true ; } //--------------------------------------------------------------------------- bool Pocketing::CreateSurfFrIncidence( const ICurveComposite* pCrv, const double dRad, const ISurfFlatRegion* pSfrLimit, 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 ! // 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 if ( pCrv->IsClosed()) { pSfrInc->CopyFrom( pSfrInc_tmp) ; return pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0 ; } // 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() ; // alla superficie di incidenza, sottraggo i due rettangoli ricavati if ( ! pSfrInc->Subtract( *pSfrRectStart) || ! pSfrInc->Subtract( *pSfrRectEnd)) return false ; return pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0 ; } //---------------------------------------------------------------------------- bool Pocketing::AdjustContourStart( ICurveComposite* pCompo, const ICRVCOMPOPOVECTOR& vCrvIsl, bool bOrder) { // 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 * m_TParams.m_dDiam) { 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 { // 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 è aperta... 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 (( int)vInd.size() == 0) { // 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... 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, 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( 0.5) ; return true ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::VerifyLeadInHelix( ISurfFlatRegion* pSrfChunk, const Point3d& ptCen, double dRad) const { // controllo della superifice if ( pSrfChunk == nullptr) return false ; // vettore di tutte le curve della superficie ICRVCOMPOPOVECTOR vCrv ; for( int c = 0 ; c < pSrfChunk->GetChunkCount() ; c++) { for ( int l = 0 ; l < pSrfChunk->GetLoopCount( c) ; l++) { vCrv.emplace_back( ConvertCurveToComposite( pSrfChunk->GetLoop( c, l))) ; } } if (( int)vCrv.size() == 0) return false ; // estraggo il bordo esterno PtrOwner pCompo( CloneCurveComposite( vCrv[0])) ; if ( IsNull( pCompo)) return false ; // recupero il piano della curva di contorno Point3d ptStart ; Vector3d vtN ; if ( ! pCompo->GetStartPoint( ptStart) || ! pCompo->GetExtrusion( vtN)) return false ; // porto il centro sullo stesso piano del contorno Point3d ptCenL = ptCen - ( ptCen - ptStart) * vtN * vtN ; // calcolo la distanza del centro dal contorno double dMinDist ; bool bOk = DistPointCurve( ptCenL, *pCompo).GetDist( dMinDist) && dMinDist > dRad + 0.5 * m_TParams.m_dDiam - 10 * EPS_SMALL ; // se la curva per l'attacco è valida per tale, controllo che l'elica non intersechi eventuali isole for ( int i = 1 ; bOk && i < int( vCrv.size()) ; i++) bOk = DistPointCurve( ptCenL, *vCrv[i]).GetDist( dMinDist) && dMinDist > dRad + 0.5 * m_TParams.m_dDiam - 10 * EPS_SMALL ; return bOk ; } //---------------------------------------------------------------------------- bool Pocketing::VerifyLeadInZigZag( ISurfFlatRegion* pSrfChunk, const Point3d& ptPa, const Point3d& ptPb) const { // controllo della superificie if ( pSrfChunk == nullptr) return false ; // vettore di tutte le curve della superficie ICRVCOMPOPOVECTOR vCrv ; for ( int c = 0 ; c < pSrfChunk->GetChunkCount() ; c++) { for ( int l = 0 ; l < pSrfChunk->GetLoopCount( c) ; l++) { vCrv.emplace_back( GetCurveComposite( pSrfChunk->GetLoop( c, l))) ; } } if ( vCrv.size() == 0) return false ; // estraggo il bordo esterno PtrOwner pCompo( CloneCurveComposite( vCrv[0])) ; if ( IsNull( pCompo)) return false ; // recupero il piano della curva di contorno Point3d ptStart ; Vector3d vtN ; if ( ! pCompo->GetStartPoint( ptStart) || ! pCompo->GetExtrusion( vtN)) return false ; // porto i punti sullo stesso piano del contorno Point3d ptPaL = ptPa - ( ptPa - ptStart) * vtN * vtN ; Point3d ptPbL = ptPb - ( ptPb - ptStart) * vtN * vtN ; // calcolo la distanza dei due punti dal contorno double dMinDistPa ; if ( ! DistPointCurve( ptPaL, *pCompo).GetDist( dMinDistPa)) return false ; double dMinDistPb ; if ( ! DistPointCurve( ptPbL, *pCompo).GetDist( dMinDistPb)) return false ; bool bOk = dMinDistPa > 0.5 * m_TParams.m_dDiam - 10 * EPS_SMALL && dMinDistPb > 0.5 * m_TParams.m_dDiam - 10 * EPS_SMALL ; // se la curva per l'attacco è valida per tale, controllo che lo ZigZag non intersechi eventuali isole for ( int i = 1 ; bOk && i < int( vCrv.size()) ; i++) { if ( ! DistPointCurve( ptPaL, *vCrv[i]).GetDist( dMinDistPa)) return false ; if ( ! DistPointCurve( ptPbL, *vCrv[i]).GetDist( dMinDistPb)) return false ; bOk = dMinDistPa > 0.5 * m_TParams.m_dDiam - 10 * EPS_SMALL && dMinDistPb > 0.5 * m_TParams.m_dDiam - 10 * EPS_SMALL ; } return bOk ; } //---------------------------------------------------------------------------- bool Pocketing::ProjectStmOnPlane( const ISurfTriMesh* pStmAbove, const Plane3d plPock, ISurfFlatRegion* pSfrProj) { // controllo parametri if ( pStmAbove == nullptr || ! pStmAbove->IsValid() || pStmAbove->GetTriangleCount() == 0) return true ; if ( ! plPock.IsValid()) return false ; pSfrProj->Clear() ; // 1) creo il sistema di riferimento ( inerente al piano di svuotatura) Point3d ptORIG = plPock.GetPoint() ; Vector3d vtN = -plPock.GetVersN() ; Frame3d frPlane ; frPlane.Set( ptORIG, vtN) ; if ( ! frPlane.IsValid()) return false ; // 2) creo una copia della superficie nel nuovo sistema di riferimento PtrOwner pStmRef( CloneSurfTriMesh( pStmAbove)) ; if ( IsNull( pStmRef) || ! pStmRef->IsValid()) return false ; // 2.1) Per semplicità rimuovo dalla superficie tutti i triangoli che sono perpendicolari al piano di svuotatura StmFromTriangleSoup TriaSoupEasy ; TriaSoupEasy.Start() ; for ( int t = 0 ; t < pStmRef->GetTriangleCount() ; ++ t) { Vector3d vtNTria ; if ( pStmRef->GetFacetNormal( pStmRef->GetFacetFromTria( t), vtNTria) && abs( vtNTria * vtN) > EPS_SMALL) { Triangle3d Tria ; pStmRef->GetTriangle( t, Tria) ; TriaSoupEasy.AddTriangle( Tria) ; } } TriaSoupEasy.End() ; PtrOwner pStmRef_Easy( TriaSoupEasy.GetSurf()) ; if ( IsNull( pStmRef_Easy) || ! pStmRef_Easy->IsValid()) return true ; // non ho nulla da proiettare pStmRef.Set( pStmRef_Easy->Clone()) ; // 3) scalo la superficie rispetto al sistema di riferimento ( mettendo a 0 la componente vtN) pStmRef->ToLoc( frPlane) ; if ( ! pStmRef->Scale( GLOB_FRM, 1., 1., 0.)) return false ; pStmRef->ToGlob( frPlane) ; // 4) controllo la validità della superficie ottenuta if ( IsNull( pStmRef) || ! pStmRef->IsValid() || pStmRef->GetTriangleCount() == 0) return true ; // la proeizione sparisce // 5) NB. A seconda di come sono orientate le facce, posso avere i Triangoli con normali vtN o -vtN... // rendo le normali dei triangoli coerenti StmFromTriangleSoup TriaSoupUp ; TriaSoupUp.Start() ; StmFromTriangleSoup TriaSoupDown ; TriaSoupDown.Start() ; for ( int t = 0 ; t < pStmRef->GetTriangleCount() ; ++ t) { Triangle3d Tria ; pStmRef->GetTriangle( t, Tria) ; if ( AreOppositeVectorApprox( Tria.GetN(), vtN)) TriaSoupDown.AddTriangle( Tria) ; else TriaSoupUp.AddTriangle( Tria) ; } TriaSoupUp.End() ; TriaSoupDown.End() ; PtrOwner pStmUp( TriaSoupUp.GetSurf()) ; // superificie con i Triangoli Up PtrOwner pStmDown( TriaSoupDown.GetSurf()) ; // superificie con i Triangoli Down // 5.1) Controllo ausiliario... ( per possibili approssimazioni con triangoli particolari) if ( ! IsNull( pStmUp) && pStmUp->IsValid() && pStmUp->GetTriangleCount() == 0 && ! IsNull( pStmDown) && pStmDown->IsValid() && pStmDown->GetTriangleCount() == 0) return true ; pStmUp->Repair() ; pStmDown->Repair() ; // 6) Essendo due Trimesh piane e sullo stesso piano, converto in FlatRegion prima di unirle PtrOwner pSfrUp( CreateSurfFlatRegion()) ; PtrOwner pSfrUp1( CreateSurfFlatRegion()) ; if ( IsNull( pSfrUp) || IsNull( pSfrUp1)) return false ; if ( ! IsNull( pStmUp) && pStmUp->IsValid() && pStmUp->GetTriangleCount() > 0) if ( ! GetSfrByStm( pStmUp, pSfrUp, plPock, V_NULL, 0., 0.)) return false ; if ( ! IsNull( pStmDown) && pStmDown->IsValid() && pStmDown->GetTriangleCount() > 0) if ( pStmDown->Invert()) if ( ! GetSfrByStm( pStmDown, pSfrUp1, plPock, V_NULL, 0., 0.)) return false ; // 7) Controllo validità e creo la FlatRegion da restituire if ( ! IsNull( pSfrUp) && pSfrUp->IsValid()) { // se Up valida, allora inizio a sostituire if ( AreOppositeVectorApprox( pSfrUp->GetNormVersor(), plPock.GetVersN())) pSfrUp->Invert() ; pSfrProj->CopyFrom( pSfrUp) ; } if ( ! IsNull( pSfrUp1) && pSfrUp1->IsValid()) { // se Down valida... if ( AreOppositeVectorApprox( pSfrUp1->GetNormVersor(), plPock.GetVersN())) pSfrUp1->Invert() ; if ( pSfrProj != nullptr && pSfrProj->IsValid()) { // e Up valida -> aggiungo if ( ! pSfrProj->Add( *pSfrUp1)) return false ; } else // se Up non valida -> copio pSfrProj->CopyFrom( pSfrUp1) ; } // se nessuna delle due è valida, non ritorno nulla if ( ! pSfrUp->IsValid() && ! pSfrUp1->IsValid()) return true ; return pSfrProj != nullptr && pSfrProj->IsValid() && pSfrProj->GetChunkCount() != 0 ; } //---------------------------------------------------------------------------- bool Pocketing::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)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } // nel caso l'Offset sparisca, all'ora l'isola non è necessaria if ( OffsCrv.GetCurveCount() == 0) bRemove = true ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::OrderCurvesByLastPntOfPath( ICRVCOMPOPOVECTOR& vCrv, Point3d ptEnd, PNTVECTOR& vPtStart, VCT3DVECTOR& vVtOut, BOOLVECTOR& vbMidOut) { if (( int)vCrv.size() == 0) return false ; Point3d ptRef = ptEnd ; double dMinDist = INFINITO ; int nIndexSwitch = -1 ; for ( int i = -1 ; i < int( vCrv.size()) - 1 ; ++ i) { for ( int j = i + 1 ; j < int( vCrv.size()) ; ++ j) { Point3d ptS ; vCrv[j]->GetStartPoint( ptS) ; if ( Dist( ptS, ptRef) < dMinDist) { dMinDist = Dist( ptS, ptRef) ; nIndexSwitch = j ; } } if ( nIndexSwitch != i + 1) { // scambio le curve PtrOwner pCrvClosest( vCrv[nIndexSwitch]->Clone()) ; vCrv[nIndexSwitch].Set( vCrv[i+1]) ; vCrv[i+1].Set( pCrvClosest) ; // scambio i punti Point3d ptStart = vPtStart[nIndexSwitch] ; vPtStart[nIndexSwitch] = vPtStart[i+1] ; vPtStart[i+1] = ptStart ; // scambio i vettori Vector3d vtOut = vVtOut[nIndexSwitch] ; vVtOut[nIndexSwitch] = vVtOut[i+1] ; vVtOut[i+1] = vtOut ; // scambio i booleani bool bOut = vbMidOut[nIndexSwitch] ; vbMidOut[nIndexSwitch] = vbMidOut[i+1] ; vbMidOut[i+1] = bOut ; } vCrv[i+1]->GetEndPoint( ptRef) ; dMinDist = INFINITO ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetParamForPtStartOnEdge( const ICurve* pCrvCheck, const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vOtherCrv, 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 = m_TParams.m_dDiam + 2 * GetOffsR() ; 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) ; //if ( dMaxInt < EPS_SMALL) // return false ; 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) ; 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 *= (( m_TParams.m_dDiam + GetOffsR() + 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 ; } } ++nDen ; } return false ; } //---------------------------------------------------------------------------- bool Pocketing::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 ; 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 ; } //---------------------------------------------------------------------------- bool Pocketing::SetBetterPtStartForSubChunks( ICurveComposite* pCrvOffsAct, const ISurfFlatRegion* pSrfToWork, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut) { // ============================= 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 ; // scorro tutte le curve presenti in pCrv ( curva da cui devo entrare ) for ( int i = 0 ; i < ( int)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 ; } else pCrv->SetCurveTempProp( i, 0, 0) ; } else pCrv->SetCurveTempProp( i, 1, 0) ; // <- tmp prop = 0 } // cambio il punto iniziale della curva all'inizio del lato aperto più lungo ( se presente) double dLenMax = EPS_SMALL ; int nCrvForMax = 0 ; if ( bSomeOpen) { 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 ; } } pCrv->ChangeStartPoint( nCrvForMax) ; } // 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 il più lungo bMidOut = GetParamOnOpenSide( pCrv, vOtherCrv, 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, vOtherCrv, true) ; // ora che ho deciso quale sia il punto iniziale, lo imposto effettivamente sulla curva di Offset passata alla funzione double dUS ; pCrv->GetStartPoint( ptStart) ; pCrvOffsAct->GetParamAtPoint( ptStart, dUS) ; pCrvOffsAct->ChangeStartPoint( dUS) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs, const int nOffsCount, const int nIter, const Vector3d& vtN, 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 ( ! m_bOptOffset) 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, - m_TParams.m_dDiam / 2 + 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 ( ! m_bOptOffsetCM || 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, m_TParams.m_dDiam / 2 - 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 < m_TParams.m_dDiam - 100 * EPS_SMALL || dDimY < m_TParams.m_dDiam - 100 * EPS_SMALL) { bInsert = false ; return true ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetDynamicClearedRegion( ISurfFlatRegion* pSrfPrec, const ICurveComposite* pCrv) { for ( int i = 0 ; i < pCrv->GetCurveCount() ; ++ i) { const ICurve* pCrvPath = pCrv->GetCurve( i) ; if ( pCrvPath != nullptr) { PtrOwner pCrv_i( pCrvPath->Clone()) ; if ( IsNull( pCrv_i)) return false ; PtrOwner PSrfRemoved( GetSurfFlatRegionFromFatCurve( Release( pCrv_i), m_TParams.m_dDiam / 2, false, false)) ; if ( pSrfPrec->GetChunkCount() == 0) pSrfPrec->CopyFrom( PSrfRemoved) ; else pSrfPrec->Add( *PSrfRemoved) ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetSfrByStm( const ISurfTriMesh* pStm, ISurfFlatRegion* pSfr, const Plane3d plPock, const Vector3d& vtExtr, const double dThick, double dToll) { // controllo validità dei parametri if ( pStm == nullptr || ! pStm->IsValid() || ! plPock.IsValid()) return false ; // creo un Frame per il sistema di Riferimento Locale Frame3d frPock ; frPock.Set( plPock.GetPoint(), plPock.GetVersN()) ; if ( ! frPock.IsValid()) return false ; // creo la FlatRegion da resitutiure SurfFlatRegionByContours SrfChunkDef ; // recupero i Loop della TriMesh salvandoli in un vettore di PolyLine POLYLINEVECTOR vPl ; pStm->GetLoops( vPl) ; // per ogni PolyLine... for ( int i = 0 ; i < ( int)vPl.size() ; ++ i) { // recupero la curva composita PolyLine PL = vPl[i] ; PtrOwner pCrv( CreateCurveComposite()) ; pCrv->FromPolyLine( PL) ; // 0) Controllo se l'area ha senso double dAreaStart = 0. ; double dLenStart = 0. ; Plane3d plUseless ; pCrv->GetArea( plUseless, dAreaStart) ; pCrv->GetLength( dLenStart) ; if ( dAreaStart < 50 * EPS_SMALL || dLenStart < 50 * EPS_SMALL) continue ; // essendo una curva derivante da un'intersezione di Trimesh ho una composita derivante da una polyLine, // devo recuperare gli archi PolyArc PA ; // 1) Mergiamo per uniformità pCrv->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; // 2) Rimuoviamo Spikes o Curve a Z if ( ! pCrv->RemoveSmallDefects( 150 * EPS_SMALL, 2 * ANG_TOL_STD_DEG, true)) return false ; // 3) Interpoliamo mediante Linee ed Archi pCrv->ToLoc( frPock) ; pCrv->ApproxWithArcsEx( 500 * EPS_SMALL, ANG_TOL_STD_DEG, LIN_FEA_STD, PA) ; if ( ! pCrv->IsValid()) continue ; pCrv->Clear() ; pCrv->FromPolyArc( PA) ; if ( ! pCrv->IsValid()) continue ; pCrv->ToGlob( frPock) ; // 4) Estrusione e Thickness della curva pCrv->SetExtrusion( vtExtr) ; pCrv->SetThickness( dThick) ; // la converto in una semplice cruva per proiettarla su piano della svuotatura // dato che le curve sono prese da una trimesh, le proietto sul piano per sicurezza e per evitare approssimazioni double dS, dE ; pCrv->GetDomain( dS, dE) ; PtrOwner pCrv_c( pCrv->CopyParamRange( dS, dE)) ; if ( IsNull( pCrv_c) || ! pCrv_c->IsValid()) return false ; // se la curva è "stretta" la trascuro, rende solo difficili i conti senza dare valore aggiunto PtrOwner pCrv_proj( ProjectCurveOnPlane( *pCrv_c, plPock)) ; double dAreaCheck = EPS_SMALL ; Plane3d plCheck ; if ( ! IsNull( pCrv_proj) && pCrv_proj->IsClosed() && pCrv_proj->GetArea( plCheck, dAreaCheck) && dAreaCheck <= 50 * EPS_SMALL) { continue ; } SrfChunkDef.AddCurve( Release( pCrv_proj)) ; // aggiungo le curve proiettate } // recupero la FlatRegion PtrOwner pSrfByCurves( SrfChunkDef.GetSurf()) ; if ( IsNull( pSrfByCurves) || pSrfByCurves->GetChunkCount() == 0) // controllo validità della FlatRegion return true ; // non ho ricavato nulla, la TriMesh è troppo sottile o piccola // controllo quante facce ottengo per uniformità con la proeizione PtrOwner pSrfByAdd( CreateSurfFlatRegion()) ; if ( IsNull( pSrfByAdd)) return false ; while ( ! IsNull( pSrfByCurves)) { if ( ! pSrfByAdd->IsValid() || pSrfByAdd->GetChunkCount() == 0) pSrfByAdd.Set( Release( pSrfByCurves)) ; else if ( ! pSrfByAdd->Add( *pSrfByCurves)) return false ; pSrfByCurves.Set( SrfChunkDef.GetSurf()) ; } // superificie regolare senza Chunk piccoli aggiuntivi SurfFlatRegionByContours SFRFINAL ; for ( int c = 0 ; c < pSrfByAdd->GetChunkCount() ; ++ c) { for ( int l = 0 ; l < pSrfByAdd->GetLoopCount( c) ; ++ l) { PtrOwner pCrvLoop( ConvertCurveToComposite( pSrfByAdd->GetLoop( c, l))) ; if ( IsNull( pCrvLoop)) return false ; double dArea = EPS_SMALL ; double dLen = EPS_SMALL ; Plane3d plCheck ; if ( pCrvLoop->GetArea( plCheck, dArea) && dArea > 500 * EPS_SMALL && pCrvLoop->GetLength( dLen) && dLen > 500 * EPS_SMALL) SFRFINAL.AddCurve( Release( pCrvLoop)) ; } } pSrfByCurves.Set( SFRFINAL.GetSurf()) ; if ( IsNull( pSrfByCurves) || pSrfByCurves->GetChunkCount() == 0) // controllo ancora validità della FlatRegion return true ; // non ho ricavato nulla, la TriMesh è troppo sottile o piccola // restituisco la FlatRegion pSfr->Clear() ; pSfr->CopyFrom( pSrfByCurves) ; return pSfr != nullptr && pSfr->IsValid() ; } //---------------------------------------------------------------------------- bool Pocketing::GetHomogeneousParts( ICurveComposite* pCrvCompo, 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, vCrvNULL) ; // 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 ; } //---------------------------------------------------------------------------- bool Pocketing::GetOptCrvIndex( const vector& vCrvOEWithFlags, int nStep, 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[nStep-1].size() == 1) nIndex = 0 ; else { for ( int k = 0 ; k < ( int)vCrvOEWithFlags[nStep-1].size() ; ++k) { CRVCVECTOR ccClass ; if ( pSrfChunkFinal->GetCurveClassification( *vCrvOEWithFlags[nStep-1][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 ; } //---------------------------------------------------------------------------- //---------------------- CASI OTTIMIZZATI ------------------------------------ //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Pocketing::GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTrap, Frame3d& frTrap, double& dPocketSize, int& nBase, int& nSecondBase) { // controllo parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) return false ; // diametro reale da tenere in considerazione double dDiam = m_TParams.m_dDiam + GetOffsR() ; 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) ; } } // clono la curva Compo PtrOwner pCrvCompo_c( CloneCurveComposite( pCrvCompo)) ; if ( IsNull( pCrvCompo_c)) return false ; // tolleranza const double TOLL = 50 * EPS_SMALL ; // se tutti lati aperti if ( nClosedSide == 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 + TOLL) 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 ; } // se un lato chiuso else if ( nClosedSide == 1) { // prendo l'unica curva chiusa const ICurve* pCrvCurr = pCrvCompo->GetCurve( vIndClosedSides[0]) ; if ( pCrvCurr == nullptr) return false ; // controllo se lineare ( altrimenti non è ottimizzato) if ( pCrvCurr->GetType() != CRV_LINE) return true ; // prendo la direzione del tratto lineare pCrvCurr->GetStartDir( vtDir) ; // prendo il punto iniziale Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ; // creo il riferimento basato su questo tratto frTrap.Set( ptStart, Z_AX, vtDir) ; if ( ! frTrap.IsValid()) return false ; // porto la curva Compo ( clonandola) nel frame pCrvCompo_c->ToLoc( frTrap) ; // ricavo il box complessivo in questo frame BBox3d BBox ; pCrvCompo_c->GetLocalBBox( BBox) ; // controllo dimY ( se troppo grande o lato chiuso non sul bordo del box => caso non ottimizzato) if (( BBox.GetDimY() < dDiam + TOLL || BBox.GetDimX() < dDiam + TOLL) && BBox.GetMin().y > - TOLL) { // creo il rettangolo del Box pCrvTrap->AddPoint( BBox.GetMin()) ; pCrvTrap->AddLine( BBox.GetMin() + BBox.GetDimX() * X_AX) ; pCrvTrap->AddLine( BBox.GetMax()) ; pCrvTrap->AddLine( BBox.GetMax() - BBox.GetDimX() * X_AX) ; pCrvTrap->Close() ; // porto in globale pCrvTrap->ToGlob( frTrap) ; // 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 Y dPocketSize = BBox.GetDimY() ; // se il lato chiuso non può essere la base, allora sarà l'aperto ( del box) if ( BBox.GetDimY() > dDiam + TOLL) { dPocketSize = BBox.GetDimX() ; pCrvTrap->ChangeStartPoint( 1.) ; Point3d ptORIG ; pCrvTrap->GetStartPoint( ptORIG) ; pCrvTrap->GetStartDir( vtDir) ; frTrap.Set( ptORIG, Z_AX, vtDir) ; } } // imposto le basi nBase = 0 ; nSecondBase = 2 ; } else { // cerco la base // Def di base : // lato lineare chiuso, box nella sua direzione con DimY < 1.5 dDiam, box dei restanti chiusi ( almeno 1) // con dimY < dDiam e lato lineare come lato del box ( è possibile trovare una seconda base, come altro lato chiuso // parallelo alla base principale con distanza da essa circa il dDiam) bool bBaseFound = false ; // flag per individuare base principale BBox3d BBox ; // Box per base principale // scorro i chiusi alla ricerca di una possibile base principale for ( int i = 0 ; i < pCrvCompo->GetCurveCount() && ! bBaseFound ; ++ i) { nBase = i ; // aggiorno l'indice // prendo la curva const ICurve* pCrvCurr = pCrvCompo->GetCurve( i) ; if ( pCrvCurr == nullptr) return false ; // controllo se lineare, altrimenti passo alla successiva if ( pCrvCurr->GetType() != CRV_LINE) continue ; // prendo la direzione del tratto lineare pCrvCurr->GetStartDir( vtDir) ; // prendo il punto iniziale Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ; // creo il riferimento basato su questo tratto frTrap.Set( ptStart, Z_AX, vtDir) ; if ( ! frTrap.IsValid()) return false ; // porto la curva Compo ( clonandola) nel frame pCrvCompo_c.Set( pCrvCompo->Clone()) ; pCrvCompo_c->ToLoc( frTrap) ; // calcolo il box pCrvCompo_c->GetLocalBBox( BBox) ; // controllo dimY e dimX per il box bool bDimYOk = BBox.GetDimY() < dDiam + TOLL && BBox.GetMin().y > - TOLL ; bool bDimXOk = BBox.GetDimX() < dDiam + TOLL && BBox.GetMin().y > - TOLL ; // se entrambe le dimensioni superano il diametro, questo lato non è la base, cerco il successivo if ( !bDimXOk && !bDimYOk) continue ; // se dimY accettabile if ( bDimYOk) { bool bOk = false ; if ( ! CheckTrapezoidClosedEdgePosition( pCrvCompo_c, i, BBox.GetDimY(), vIndClosedSides, bOk, pCrvTrap)) return false ; if ( bOk) { // pCrvTrap contiene il trapezio nBase = 0 ; nSecondBase = 2 ; dPocketSize = BBox.GetDimY() ; pCrvTrap->ToGlob( frTrap) ; bBaseFound = true ; } else { // cerco la seconda base if ( ! CheckSecondBaseTrapezoid( pCrvCompo_c, i, nSecondBase)) return false ; if ( nSecondBase != -1) { // verifico che una delle due sia chiusa ( TO DO) if ( pCrvCompo_c->GetCurve( nBase)->GetTempProp( 0) == 0 || pCrvCompo_c->GetCurve( nSecondBase)->GetTempProp( 0) == 0) { // se ho trovato la seconda base if ( ! PreparareTrapezoidTwoBases( pCrvCompo_c, BBox, i, nSecondBase, bOk, pCrvTrap)) return false ; if ( bOk) { // pCrvTrap contiene il trapezio nBase = 0 ; dPocketSize = BBox.GetDimY() ; pCrvTrap->ToGlob( frTrap) ; bBaseFound = true ; } } } } } // se dimX accettabile if ( bDimYOk) { // TODO : } } // se non ho trovato una base principale => non è ottimizzato if ( ! bBaseFound) return true ; } // se parametro MaxOptSize non compatibile => non è ottimizzato double dMaxOptSize ; FromString( ExtractInfo( m_Params.m_sUserNotes, "MaxOptSize="), dMaxOptSize) ; if ( FromString( ExtractInfo( m_Params.m_sUserNotes, "MaxOptSize="), dMaxOptSize) && dPocketSize > dMaxOptSize) pCrvTrap->Clear() ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::CheckTrapezoidClosedEdgePosition( const ICurveComposite* pCrvCompo, const int nCrvInd, const double dDimBoxY, INTVECTOR& vIndClosedSides, bool& bOk, ICurveComposite* pCrvTrap) { // controllo dei parametri if ( pCrvCompo == nullptr) return false ; bOk = false ; // controllo che ci siano due o tre lati chiusi int nClosedEdge = int( vIndClosedSides.size()) ; if ( nClosedEdge != 3) return true ; // TO DO se 2 chiusi consecutivi + gesitone archi // 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) return true ; // 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 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 return true ; // se sono consecuti, ma la base non è il lato centrale, esco ( la ritroverò successivamente) if ( vIndClosedSides[1] != nCrvInd) return true ; // parametri lavorazione double dDiam = m_TParams.m_dDiam ; double dOffsR = GetOffsR() ; double dRad = 0.5 * dDiam + dOffsR ; // -------------- creo il bordo del trapezio estendendo i lati obliqui se possibile ------------------------ // estendo PtrOwner pEdge0( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; PtrOwner pEdge2( pCrvCompo->GetCurve( vIndClosedSides[2])->Clone()) ; Point3d ptS0 ; pEdge0->GetStartPoint( ptS0) ; Point3d ptE2 ; pEdge2->GetEndPoint( ptE2) ; if ( abs( ptS0.y - dDimBoxY) > TOLL_TRAPEZOID) { Vector3d vtDir ; pEdge0->GetStartDir( vtDir) ; double dSinT = sqrt( 1 - vtDir.x * vtDir.x) ; if ( dSinT > TOLL_TRAPEZOID) { pEdge0->ExtendStartByLen(( dDimBoxY - ptS0.y) / dSinT) ; pEdge0->GetStartPoint( ptS0) ; } } if ( abs( ptE2.y - dDimBoxY) > TOLL_TRAPEZOID) { Vector3d vtDir ; pEdge2->GetStartDir( vtDir) ; double dSinT = sqrt( 1 - vtDir.x * vtDir.x) ; if ( dSinT > TOLL_TRAPEZOID) { pEdge2->ExtendEndByLen(( dDimBoxY - ptE2.y) / dSinT) ; pEdge2->GetEndPoint( ptE2) ; } } // creo il bordo del trapezio esteso PtrOwner pCrvTrapBorder( CreateCurveComposite()) ; if ( IsNull( pCrvTrapBorder)) return false ; pCrvTrapBorder->AddPoint( ptS0) ; pCrvTrapBorder->AddCurve( Release( pEdge0)) ; pCrvTrapBorder->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrapBorder->AddCurve( Release( pEdge2)) ; pCrvTrapBorder->Close() ; // ---------------------------------------------------------------------------------------------------------- // verifico dimensione x della svuotatura double dLen0 = 0, dLen2 = 0 ; pCrvTrapBorder->GetCurve( 0)->GetLength( dLen0) ; pCrvTrapBorder->GetCurve( 2)->GetLength( dLen2) ; if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL) return true ; // creo un piccolo Offset e controllo che il trapezio non intersechi la curva Compo OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( pCrvTrapBorder, TOLL_TRAPEZOID, ICurve::OFF_EXTEND)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } PtrOwner pCrvOffs( OffsCrv.GetLongerCurve()) ; if ( IsNull( pCrvOffs)) return false ; IntersCurveCurve IntCC( *pCrvOffs, *pCrvCompo) ; CRVCVECTOR ccClass ; // se c'è intersezione, esco if ( IntCC.GetRegionCurveClassification() != CCREGC_IN2) return true ; // costruisco il trapezio pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartPoint( ptS0) ; pCrvTrap->AddPoint( ptS0) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[2])->Clone()) ; pCrvTrap->Close() ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; pCrvTrap->ChangeStartPoint( 1.) ; // la base è sempre la curva 0 bOk = true ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::CheckSecondBaseTrapezoid( const ICurveComposite* pCrvCompo, const int nCrvInd, int& nSecondBase) { // controllo parametri if ( pCrvCompo == nullptr) return false ; // parametri lavorazione double dDiam = m_TParams.m_dDiam ; double dOffsR = GetOffsR() ; double dRad = 0.5 * dDiam + dOffsR ; nSecondBase = -1 ; // scorro le altre curve lineari chiuse for ( int u = 0 ; u < pCrvCompo->GetCurveCount() && nSecondBase == -1 ; ++ u) { if ( u == nCrvInd || pCrvCompo->GetCurve( u)->GetType() != CRV_LINE) continue ; // recupero la direzione del tratto Vector3d vtCurr_dir ; pCrvCompo->GetCurve( u)->GetStartDir( vtCurr_dir) ; // direzioni parallele ma opposte if ( AreOppositeVectorApprox( vtCurr_dir, X_AX)) { // se le direzioni sono compatibili, controllo che la distanza sia ammissibile const ICurve* pCrvCurr = pCrvCompo->GetCurve( nCrvInd) ; Point3d ptS0, ptS1 ; pCrvCurr->GetStartPoint( ptS0) ; pCrvCompo->GetCurve( u)->GetStartPoint( ptS1) ; if ( abs( ptS1.y - dDiam) < TOLL_TRAPEZOID) nSecondBase = u ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::PreparareTrapezoidTwoBases( const ICurveComposite* pCrvCompo, BBox3d BBox, const 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 ; PtrOwner pCrvTest0( CloneCurveComposite( pCrvCompo)) ; PtrOwner pCrvTest1( CloneCurveComposite( pCrvCompo)) ; pCrvCompo->GetCurve( nBase)->GetStartPoint( ptSB0) ; pCrvCompo->GetCurve( nSecondBase)->GetStartPoint( ptSB1) ; 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 } // parametri lavorazione double dDiam = m_TParams.m_dDiam ; double dOffsR = GetOffsR() ; double dRad = 0.5 * dDiam + dOffsR ; // creo la base principale e secondaria PtrOwner pCrvBase0( pCrvCompo->GetCurve( nBase)->Clone()) ; PtrOwner pCrvBase1( pCrvCompo->GetCurve( nSecondBase)->Clone()) ; // ricavo i nuovi estremi Point3d ptEB0, ptEB1 ; pCrvBase0->GetStartPoint( ptSB0) ; pCrvBase0->GetEndPoint( ptEB0) ; pCrvBase1->GetStartPoint( ptSB1) ; pCrvBase1->GetEndPoint( ptEB1) ; // 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) { ptEB0 = BBox.GetMin() + X_AX * BBox.GetDimX() ; ptSB1 = ptEB0 + dDiam * Y_AX ; } // 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) { ptSB0 = BBox.GetMin() ; ptEB1 = BBox.GetMin() + dDiam * Y_AX ; } // se lato sinistro chiuso else bCopyLeft = true ; // creo la curva da restituire pCrvTrap->AddPoint( ptSB0) ; pCrvTrap->AddLine( ptEB0) ; 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, dLen1 = 0 ; pCrvBase0->GetLength( dLen0) ; pCrvBase1->GetLength( dLen1) ; if ( dLen0 < dDiam - EPS_SMALL || dLen1 < dDiam - EPS_SMALL) { pCrvTrap->Clear() ; return true ; } } bOk = true ; return true ; } //---------------------------------------------------- bool Pocketing::SpecialAdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const bool bEvenClosed, const ICurveComposite* pCrvPocket) { // parametri double dDiam = m_dDiam_Prec > 0 ? m_dDiam_Prec : m_TParams.m_dDiam ; double dOffsR = m_dDiam_Prec > 0 ? m_dOffsetR_Prec : GetOffsR() ; 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 ; } //---------------------------------------------------------------------------- bool Pocketing::CalcCircleSpiral( const Point3d& ptCen, const Vector3d& vtN, double dOutRad, double dIntRad, bool bSplitArcs, ICurveComposite* pMCrv, ICurveComposite* pRCrv) { // raggio della circonferenza esterna if ( dOutRad < 10 * EPS_SMALL) { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; 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)) { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; 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 ( m_Params.m_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 * GetSideStep()))) ; if ( IsEven( nStep)) nStep += 1 ; double dStep = ( dOutRad - dIntRad) / nStep ; for ( int i = 1 ; i <= nStep ; ++ i) { if ( ! IsEven( i)) 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) { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } // se necessario, approssimo con rette if ( bSplitArcs && ! ApproxWithLines( pMCrv)) { m_pMchMgr->SetLastError( 2421, "Error in Pocketing : Linear Approx not computable") ; return false ; } // Assegno la Feed al percorso AssignFeedSpiralOpt( 0, pMCrv) ; // eventuale sistemazione archi VerifyArcs( pMCrv) ; // calcolo l'eventuale percorso di ritorno Point3d ptStart ; pMCrv->GetStartPoint( ptStart) ; Point3d ptEnd ; pMCrv->GetEndPoint( ptEnd) ; Vector3d vtStart ; pMCrv->GetStartDir( vtStart) ; if ( ! AreSamePointApprox( ptStart, ptEnd)) { PtrOwner pArc2( CreateCurveArc()) ; if ( IsNull( pArc2) || ! pArc2->Set2PVN( ptStart, ptEnd, - vtStart, vtN)) { m_pMchMgr->SetLastError( 2420, "Error in Pocketing : Return toolpath not computable") ; return false ; } pRCrv->AddCurve( Release( pArc2)) ; // inverto e eventualmente sistemo archi pRCrv->Invert() ; // setto la feed per il percorso di ritorno AssignFeedForReturnPath( pRCrv) ; VerifyArcs( pRCrv) ; } return true ; } //---------------------------------------------------- bool Pocketing::CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double dPocketSize, int nBase, int nSecondBase, ICurveComposite* pMCrv, ICurveComposite* pRCrv, bool& bOptimizedTrap) { // parametri double dDiam = m_TParams.m_dDiam ; double dOffsR = GetOffsR() ; double dRad = 0.5 * dDiam + dOffsR ; 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 > m_TParams.m_dDiam + 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)) { 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)) return false ; if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, false, dYCoord, dXCoordEnd, dPocketSize)) 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( - 0.5 * m_TParams.m_dDiam - GetOffsR()) || ! pLine3->SimpleOffset( - 0.5 * m_TParams.m_dDiam - GetOffsR())) 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, true)) { pMCrv->Clear() ; return false ; } if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == 0 && ! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, 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 ( ! m_Params.m_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[3] * 8 ; pMCrv->SetTempProp( nOpenEdges, 0) ; pMCrv->SetExtrusion( vtExtr) ; bOptimizedTrap = true ; return true ; } //---------------------------------------------------- bool Pocketing::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) ; } //------------------------------------------------------ bool Pocketing::CalcTrapezoidSpiralXCoord( const ICurveComposite* pCrvPocket, int nBase, int nSecondBase, bool bStart, double dYCoord, double& dXCoord, double dPocketSize) { // parametri double dDiam = m_dDiam_Prec > 0 ? m_dDiam_Prec : m_TParams.m_dDiam ; double dOffsR = m_dDiam_Prec > 0 ? m_dOffsetR_Prec : GetOffsR() ; double dRad = 0.5 * dDiam + dOffsR ; const double TOLL = 50 * EPS_SMALL ; // 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)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; 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 * TOLL * Y_AX, - 90 , 2 * dPocketSize) ; else pCrvVertLine->SetPDL( Box3d.GetMin() - 5 * TOLL * 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 ; } //---------------------------------------------------- bool Pocketing::AdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const ICurveComposite* pCrvPocket, bool bStart) { // parametri double dDiam = m_dDiam_Prec > 0 ? m_dDiam_Prec : m_TParams.m_dDiam ; double dOffsR = m_dDiam_Prec > 0 ? m_dOffsetR_Prec : GetOffsR() ; double dRad = 0.5 * dDiam + dOffsR ; const double TOLL = 50 * EPS_SMALL ; 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 ; } //---------------------------------------------------- bool Pocketing::AdjustTrapezoidSpiralForLeadInLeadOut( ICurveComposite* pCompo, ICurveComposite* pRCrv, const Vector3d& vtTool, double dDepth, int& nOutsideRaw) { // recupero la direzione principale della svuotatura Vector3d vtMainDir ; for ( int i = 0 ; i < pCompo->GetCurveCount() ; i++) { int nProp ; if ( pCompo->GetCurveTempProp( i, nProp) && nProp == 0) { // se non è lato aggiuntivo per la pulitura angoli recupero la sua direzione pCompo->GetCurve( i)->GetStartDir( vtMainDir) ; break ; } } // start point bool bStartOutside = false ; ComputeTrapezoidSpiralLeadInLeadOut( pCompo, vtMainDir, true, vtTool, dDepth, bStartOutside) ; // end point bool bEndOutside = false ; ComputeTrapezoidSpiralLeadInLeadOut( pCompo, vtMainDir, false, vtTool, dDepth, bEndOutside) ; // eventuale inversione della curva per partire sempre dall'esterno del grezzo if ( bEndOutside && ! bStartOutside) pCompo->Invert() ; nOutsideRaw = 0 ; if ( bStartOutside && bEndOutside) nOutsideRaw = 2 ; else if ( bStartOutside || bEndOutside) { nOutsideRaw = 1 ; // calcolo percorso di ritorno pRCrv->Clear() ; pRCrv->AddCurve( pCompo->Clone()) ; pRCrv->Invert() ; } return true ; } //---------------------------------------------------- bool Pocketing::ComputeTrapezoidSpiralLeadInLeadOut( ICurveComposite* pCompo, const Vector3d& vtMainDir, bool bLeadIn, const Vector3d& vtTool, double dDepth, bool& bIsOutsideRaw) { bIsOutsideRaw = false ; Point3d ptP ; Vector3d vtDir ; if ( bLeadIn) { pCompo->GetStartPoint( ptP) ; pCompo->GetStartDir( vtDir) ; } else { pCompo->GetEndPoint( ptP) ; pCompo->GetEndDir( vtDir) ; } // per non farlo coincedere esttamente con la faccia del grezzo ( per CalcElev) //ptP += 20 * EPS_SMALL *vtDir ; Vector3d vtExtr ; pCompo->GetExtrusion( vtExtr) ; // recupero info sui lati aperti int nPropOpen = pCompo->GetTempProp( 0) ; bool bEdgeOpen = (( nPropOpen & ( bLeadIn ? 8 : 2)) > 0) ; bool bBaseOpen = (( nPropOpen & 1) > 0) ; // recupero info per capire se sto considerando un lato aggiuntivo per pulire angoli int nIdCrv = ( bLeadIn ? 0 : pCompo->GetCurveCount() - 1) ; int nExtraEdge ; pCompo->GetCurveTempProp( nIdCrv, nExtraEdge) ; double dSafeZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeZ() ; // tento con allungamento se lato inclinato è aperto oppure se sto considerando un lato aggiuntivo per pulire angoli if ( bEdgeOpen || nExtraEdge == 1) { Vector3d vtDirP = ( bLeadIn ? -vtDir : vtDir) ; // se forzato come fuori dal grezzo if ( m_bOpenOutRaw) { Point3d ptNewStart = ptP + vtDirP * ( m_TParams.m_dDiam / 2 + max( dSafeZ, m_dOpenMinSafe)) ; pCompo->AddLine( ptNewStart, ! bLeadIn) ; bIsOutsideRaw = true ; return true ; } // recupero la distanza dal bordo del grezzo lungo la direzione di allungamento double dDist ; Vector3d vtNorm ; if ( ! GetSignedDistFromRealDirection( ptP, vtDirP, dDist, vtNorm)) return false ; // calcolo eventuali fattori correttivi (se uscita approx. con fianco utensile) double dCorr = 1 ; double dDistRef = dDist ; double dDistMin ; Vector3d vtNormMin ; if ( abs( vtTool * vtNorm) < 0.5 && GetSignedDistFromRealDirection( ptP, vtNorm, dDistMin, vtNormMin)) { if ( abs( dDistMin) < abs( dDist) && abs( dDistMin) > EPS_SMALL) { dDistRef = dDistMin ; dCorr = dDist / dDistMin ; } } // ( se sono dentro al grezzo e se sono vicino al bordo del grezzo) oppure sono fuori ( circa sul bordo) if (( dDistRef < EPS_SMALL && abs( dDistRef) < m_TParams.m_dDiam / 2 + EPS_SMALL ) || dDistRef > - EPS_SMALL) { Point3d ptTest = ptP + vtDirP * ( - dDist + ( m_TParams.m_dDiam / 2 + max( dSafeZ, m_dOpenMinSafe)) * dCorr) ; ptTest += - vtTool * dDepth ; double dTestElev ; // se è fuori dal grezzo if ( ! GetElevation( m_nPhase, ptTest, vtTool, m_TParams.m_dDiam / 2, vtTool, dTestElev) || dTestElev < EPS_SMALL) { Point3d ptNewStart = ptP + vtDirP * ( - dDist + ( m_TParams.m_dDiam / 2 + max( dSafeZ, m_dOpenMinSafe)) * dCorr) ; pCompo->AddLine( ptNewStart, ! bLeadIn) ; bIsOutsideRaw = true ; } } } // tento con attacco ruotato di 90° se non sto considerando un tratto aggiuntivo per pulire angoli if ( bBaseOpen && ! bIsOutsideRaw && nExtraEdge == 0) { Vector3d vtDirO = vtDir ; vtDirO.Rotate( vtExtr, ( m_Params.m_bInvert ? -90 : 90)) ; // calcolo distanza dal bordo del grezzo lungo vtDirO double dDist ; Vector3d vtNorm ; if ( ! GetSignedDistFromRealDirection( ptP, vtDirO, dDist, vtNorm)) return false ; // se vicino al bordo del grezzo if ( abs( dDist) < m_TParams.m_dDiam / 2 + EPS_SMALL) { Point3d ptTestO = ptP + vtDirO * ( - dDist + m_TParams.m_dDiam / 2 + max( dSafeZ, m_dOpenMinSafe)) ; ptTestO += - vtTool * dDepth ; double dTestElevO ; // se è fuori dal grezzo uso inizio ruotato if ( ! GetElevation( m_nPhase, ptTestO, vtTool, m_TParams.m_dDiam / 2, vtTool, dTestElevO) || dTestElevO < EPS_SMALL) { Point3d ptNewStart = ptP + vtDirO * ( - dDist + m_TParams.m_dDiam / 2 + max( dSafeZ, m_dOpenMinSafe)) ; pCompo->AddLine( ptNewStart, ! bLeadIn) ; bIsOutsideRaw = true ; } } } return true ; } // pizza //---------------------------------------------------------------------------- //-------------------------- FEED -------------------------------------------- //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Pocketing::AssignFeedSpiralOpt( const int nOptType, ICurveComposite* pCrv ) { // controllo della curva corrente if ( pCrv == nullptr || !pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; // controllo se il Flag per assegnare la Feed è attivo if ( !m_bAssignFeed) return AssignDefaultFeed( pCrv) ; switch ( m_Params.m_nSubType ) { case POCKET_SUB_SPIRALIN : if ( nOptType == 0) { // Spirale dall'Esterno for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { if ( u == 0) // prima circonferenza pCrv->SetCurveTempProp( 0, FEED_DIVISOR * GetMinFeed(), 0) ; else // semi cerchi in tangenza pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ; } } else if ( nOptType == 1) { // Trapezoidi for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ; } break ; /* NB. Essendo la funzione CalcSpiral richiamata sia per lo SpiralIN che per lo SpiralOUT le curve sono sempre orientate nello stesso modo, solamente alla fine viene invertita la curva finale per la svuotatura... */ case POCKET_SUB_SPIRALOUT : if ( nOptType == 0) { // Spiral verso l'esterno for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { if ( u > pCrv->GetCurveCount() - 3 ) // prime semi circonferenze pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ; else pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ; } } else if ( nOptType == 1) { // Trapezoidi for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ; } break ; default : break ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::AssignFeedZigZagOneWay( ICurveComposite* pCompo, const bool bIsLink, const ICURVEPOVECTOR& vLAbove, const ICURVEPOVECTOR& vLUnder, const ICRVCOMPOPOVECTOR& vAddedLinks) { // controllo che la pCompoLine sia effettivamente una linea valida if ( pCompo == nullptr || ! pCompo->IsValid()) return false ; // controllo se il Flag per assegnare la Feed è attivo if ( !m_bAssignFeed) return AssignDefaultFeed( pCompo) ; // inzialmente setto la feed Minima alla curva for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) pCompo->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ; // se è un link tra livelli diversi, allora esco ( con Feed Minima ) if ( bIsLink) { Point3d ptS_link ; pCompo->GetStartPoint( ptS_link) ; Point3d ptE_link ; pCompo->GetEndPoint( ptE_link) ; if ( abs( ptE_link.y - ptS_link.y) > 500 * EPS_SMALL) return true ; } // se la curva è piccola, allora esco ( con Feed Minima) double dLen = EPS_SMALL ; if ( ! pCompo->GetLength( dLen) || dLen < 0.6 * m_TParams.m_dDiam) return true ; Point3d ptS, ptE ; pCompo->GetStartPoint( ptS) ; pCompo->GetEndPoint( ptE) ; // creo l'intervallo desiderato valutanto le coordinate x Intervals IntMinFeed ; IntMinFeed.Set( ptS.x, ptE.x) ; // creo un vettore contenente i tratti sopra e i tratti sotto attivi ICURVEPOVECTOR vAllInt ; vAllInt.reserve(( int)vLUnder.size() + ( int)vLAbove.size()) ; for ( int i = 0 ; i < ( int)vLUnder.size() ; ++ i) vAllInt.emplace_back( vLUnder[i]->Clone()) ; for ( int i = 0 ; i < ( int)vLAbove.size() ; ++ i) vAllInt.emplace_back( vLAbove[i]->Clone()) ; int nDim = ( int)vAllInt.size() ; // aggiungo le parti dei link interessate double dCurrY = ptS.y ; BBox3d bBoxLink ; for ( int l = 0 ; l < ( int)vAddedLinks.size() ; ++ l) { vAddedLinks[l]->GetLocalBBox( bBoxLink) ; if (( dCurrY - bBoxLink.GetMin().y) < GetSideStep() + 50 * EPS_SMALL || ( dCurrY - bBoxLink.GetMax().y) < GetSideStep() + 50 * EPS_SMALL) { // determino la direzione del Link Vector3d vtLinkDir( - 25, 0, 0) ; Point3d ptS_l ; vAddedLinks[l]->GetStartPoint( ptS_l) ; Point3d ptE_l ; vAddedLinks[l]->GetEndPoint( ptE_l) ; if ( ptE_l.x - ptS_l.x < EPS_SMALL) vtLinkDir.x -= ptS_l.x - ptE_l.x ; PtrOwner pSfrRectUp( GetSurfFlatRegionRectangle( bBoxLink.GetDimX() + 50, GetSideStep())) ; pSfrRectUp->Translate(( ptS_l - ORIG) + vtLinkDir) ; // link sopra o sotto alla curva currente ? bool bIsDown = true ; if ( dCurrY - ptS_l.y < EPS_SMALL) bIsDown = false ; CRVCVECTOR ccClass ; pSfrRectUp->GetCurveClassification( *vAddedLinks[l], EPS_SMALL, ccClass) ; for ( int i = 0 ; i < ( int)ccClass.size() ; ++ i) { if ( ccClass[i].nClass == CRVC_IN && bIsDown || ccClass[i].nClass == CRVC_OUT && ! bIsDown) vAllInt.emplace_back( GetCurveComposite( vAddedLinks[l]->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ; } } } // scorro tutti i tratti Attivi for ( int i = 0 ; i < ( int)vAllInt.size() ; ++ i) { // controllo che il tratto lineare sotto sia sufficientemente lungo double dLen_iU = EPS_SMALL ; if ( ! vAllInt[i]->GetLength( dLen_iU) || ( dLen_iU < 1.1 * m_TParams.m_dDiam && i < nDim)) continue ; Point3d ptS_iU, ptE_iU ; vAllInt[i]->GetStartPoint( ptS_iU) ; vAllInt[i]->GetEndPoint( ptE_iU) ; // la vtDir per Above e Under è sempre invertita rispetto alla linea corrente // sottraggo questo intervallo a quello originale IntMinFeed.Subtract( ptS_iU.x, ptE_iU.x) ; } // l'intervallo orginale ora conterrà i sottointervalli con feed Minima, tolgo quelli troppo piccoli Intervals IntMinFeed_noSmall ; double dParS = EPS_SMALL ; double dParE = EPS_SMALL ; bool bFound = IntMinFeed.GetFirst( dParS, dParE) ; while ( bFound) { if ( dParE - dParS > m_TParams.m_dDiam * 0.5 - 5 * EPS_SMALL) IntMinFeed_noSmall.Add( dParS, dParE) ; bFound = IntMinFeed.GetNext( dParS, dParE) ; } // ora che ho solo tratti abbastanza lunghi, creo l'intervallo complementare // questo intervallo contiene tutte le feed Massime Intervals IntMaxFeed ; IntMaxFeed.Set( ptS.x, ptE.x) ; IntMaxFeed.Subtract( IntMinFeed_noSmall) ; // tolgo le parti piccole Intervals IntMaxFeed_noSmall ; bFound = IntMaxFeed.GetFirst( dParS, dParE) ; while ( bFound) { if ( dParE - dParS > m_TParams.m_dDiam * 0.5 - 5 * EPS_SMALL) IntMaxFeed_noSmall.Add( dParS, dParE) ; bFound = IntMaxFeed.GetNext( dParS, dParE) ; } // recupero la direzione principale della curva corrente Vector3d vtDir = X_AX ; if ( ptE.x - ptS.x < EPS_SMALL) vtDir = - X_AX ; // trasformo questi intervalli nei parametri corrispondenti sulla linea originale bool bFMax = false ; bFound = IntMaxFeed_noSmall.GetFirst( dParS, dParE) ; if ( bFound && IntMaxFeed_noSmall.IsInside( ptS.x + vtDir.x * 20 * EPS_SMALL)) bFMax = true ; if ( ! bFound && bIsLink) // se non ho intervalli a Feed Massima, allora esco ( con Feed Minima) return true ; while ( bFound) { if ( ! bIsLink) { // se segmento di ZigZag double du_js = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParS, ptS.y, ptS.z), du_js) ; pCompo->AddJoint( du_js) ; double du_je = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParE, ptE.y, ptE.z), du_je) ; pCompo->AddJoint( du_je) ; } bFound = IntMaxFeed_noSmall.GetNext( dParS, dParE) ; } // aggiorno le proprietà temporanee con la feed if ( ! bIsLink) { for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) { if ( IsEven( u) == bFMax) pCompo->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed() , 0) ; else pCompo->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed() , 0) ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::AssignFeedOnCorners( ICurveComposite* pCrv, const ICurveComposite* pCrv_orig, const double dLenToll) { // controllo sulla curva iniziale if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0 || ! pCrv->IsClosed()) return false ; // controllo se Flag per Feed è attivo if ( !m_bAssignFeed) return AssignDefaultFeed( pCrv) ; // nuova curva finale PtrOwner pCrv_new( CreateCurveComposite()) ; if ( IsNull( pCrv_new)) return false ; Intervals IntU ; IntU.Set( 0.0, 1.0 * pCrv->GetCurveCount()) ; // scorro tutte le curve cercando i punti non C' for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u ) { // curva u-esima const ICurve* pCrv_u = pCrv->GetCurve( u) ; // curva (u+1)-esima int u_f = u + 1 ; if ( u_f == pCrv->GetCurveCount()) u_f = 0 ; const ICurve* pCrv_uu = pCrv->GetCurve( u_f) ; // calcolo l'angolo tra le due curve double dAng = EPS_ANG_ZERO ; Vector3d dTan_u = V_NULL ; pCrv_u->GetEndDir( dTan_u) ; Vector3d dTan_uu = V_NULL ; pCrv_uu->GetStartDir( dTan_uu) ; double dU_S = 0.0 ; double dU_E = 1.0 ; if ( ! dTan_u.GetAngle( dTan_uu, dAng) || abs( dAng) > EPS_ANG_SMALL) { // se spigolo... // ricavo la lunghezza delle due curve e controllo che non siano dei lati aperti int nStat_u = 0 ; if ( CheckSimpleOverlap( pCrv_u, pCrv_orig, nStat_u, 50 * EPS_SMALL) && nStat_u != 1) { double dLen_u = EPS_SMALL ; pCrv_u->GetLength( dLen_u) ; if ( dLen_u > dLenToll + 500 * EPS_SMALL) pCrv_u->GetParamAtLength( dLen_u - dLenToll, dU_S) ; } int nStat_uu = 0 ; if ( CheckSimpleOverlap( pCrv_uu, pCrv_orig, nStat_uu, 50 * EPS_SMALL) && nStat_uu != 1) { double dLen_uu = EPS_SMALL ; pCrv_uu->GetLength( dLen_uu) ; if ( dLen_uu > dLenToll + 500 * EPS_SMALL) pCrv_uu->GetParamAtLength( dLenToll, dU_E) ; } if ( u_f != 0) { IntU.Subtract( u + ( nStat_u == 0 ? dU_S : 1), u_f + ( nStat_uu == 0 ? dU_E : 0)) ; } else { if ( nStat_u == 0) IntU.Subtract( u + dU_S, pCrv->GetCurveCount()) ; if ( nStat_uu == 0) IntU.Subtract( 0.0, dU_E) ; } } } ICRVCOMPOPOVECTOR vCrvMinFeed ; double dParS = EPS_SMALL ; double dParE = EPS_SMALL ; Intervals IntMinFeed ; IntMinFeed.Set( 0.0, 1.0 * pCrv->GetCurveCount()) ; IntMinFeed.Subtract( IntU) ; bool bFound = IntMinFeed.GetFirst( dParS, dParE) ; while ( bFound) { Point3d ptS, ptE ; vCrvMinFeed.emplace_back( GetCurveComposite( pCrv->CopyParamRange( dParS, dParE))) ; for ( int u = 0 ; u < vCrvMinFeed.back()->GetCurveCount() ; ++ u) vCrvMinFeed.back()->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ; bFound = IntMinFeed.GetNext( dParS, dParE) ; } PtrOwner pCrv_clone( pCrv->Clone()) ; for ( int i = 0 ; i < ( int)vCrvMinFeed.size() ; ++ i) { PtrOwner pCrv_back( pCrv_clone->Clone()) ; PtrOwner pCrv_forward( pCrv_clone->Clone()) ; Point3d ptS, ptE ; vCrvMinFeed[i]->GetStartPoint( ptS) ; vCrvMinFeed[i]->GetEndPoint( ptE) ; double duS ; double duE ; pCrv_clone->GetParamAtPoint( ptS, duS) ; pCrv_clone->GetParamAtPoint( ptE, duE) ; if ( ! pCrv_back->TrimEndAtParam( duS)) pCrv_back->Clear() ; if ( ! pCrv_forward->TrimStartAtParam( duE)) pCrv_forward->Clear() ; pCrv_clone->Clear() ; pCrv_clone->AddCurve( Release( pCrv_back)) ; pCrv_clone->AddCurve( vCrvMinFeed[i]->Clone()) ; pCrv_clone->AddCurve( Release( pCrv_forward)) ; } pCrv->Clear() ; pCrv->AddCurve( Release( pCrv_clone)) ; return false ; } //---------------------------------------------------------------------------- bool Pocketing::GetFeedForParam( double& dPar, double& dFeed) { /* feed ^ | GetFeed() + --------------\ | * \ | * \ | * \ | * \ | * \ GetFeed() * GetSideStep() / d + * * | * * 0--------------+------+---------------> Working Arc GetSideStep() d */ if ( dPar > m_TParams.m_dDiam || dPar < 0 ) // dominio... return false ; if ( m_TParams.m_dDiam - GetSideStep() < 50 * EPS_SMALL) { // se la funzione è costante... dFeed = GetMaxFeed() ; // non ho scelta ... return true ; } else { if ( GetSideStep() < dPar + 50 * EPS_SMALL) { // se sono nel tratto lineare discendente ... // d/2 su parte discendente dFeed = GetFeed() + ( GetFeed() * ( 1 - ( GetSideStep() / m_TParams.m_dDiam))) * ( dPar - GetSideStep()) / ( GetSideStep() - m_TParams.m_dDiam) ; } else dFeed = GetMaxFeed() ; // se sono nel tratto costante ... } return true ; } //---------------------------------------------------------------------------- bool Pocketing::AssignFeedForOpenEdge( ICurveComposite* pCrv, const ICurveComposite* pCrvOF_orig) { if ( !m_bAssignFeed) return AssignDefaultFeed( pCrv) ; // controllo se qualche curva passa sopra ad un lato aperto... if ( pCrvOF_orig != nullptr ) { for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { const ICurve* pCrv_u = pCrv->GetCurve( u) ; if ( pCrv_u == nullptr) return false ; int nStat = -1 ; if ( CheckSimpleOverlap( pCrv_u, pCrvOF_orig, nStat, 1500 * EPS_SMALL) && nStat == 1) { double dFeed = GetMinFeed() ; double dPar = m_TParams.m_dDiam / 2 ; if ( ! GetFeedForParam( dPar, dFeed)) return false ; pCrv->SetCurveTempProp( u, FEED_DIVISOR * dFeed, 0) ; } } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::AssignFeedForLineInOut( ICurveComposite* pCrv, const bool bIsIn) { // controllo parametri if ( pCrv == nullptr || pCrv->GetCurveCount() < 2) return false ; if ( !m_bAssignFeed) return AssignDefaultFeed( pCrv) ; if ( bIsIn) // Segmento in ingresso pCrv->SetCurveTempProp( 0, FEED_DIVISOR * GetMinFeed(), 0) ; else // Segmento in uscita pCrv->SetCurveTempProp( pCrv->GetCurveCount() - 1, FEED_DIVISOR * GetMaxFeed(), 0) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::AssignFeedForEdgeCleaning( ICurveComposite *pCrv, const ICRVCOMPOPOVECTOR& vCrvOF_orig, int nInd) { // controllo parametri if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; // controllo che la svuotatura sia ZigZag o OneWay if ( m_Params.m_nSubType == POCKET_SUB_SPIRALIN || m_Params.m_nSubType == POCKET_SUB_SPIRALOUT) return false ; // controllo se il Flag per assegnare la Feed è attivo if ( !m_bAssignFeed) return AssignDefaultFeed( pCrv) ; double dCurrFeed = GetMinFeed() ; if ( m_Params.m_nSubType == POCKET_SUB_ZIGZAG) // se pulitura ZigZag -> posso andare alla FeedMassima dCurrFeed = GetMaxFeed() ; // se pulitura OneWay la feed rimane le minima // assegno a tutte le sottocurve la Feed for ( int u = 0 ; u < ( int)pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempProp( u, FEED_DIVISOR * dCurrFeed, 0) ; // controllo se le curve di Bordo passano su una curva originale -> controllo dei lati aperti if ( m_Params.m_nSubType == POCKET_SUB_ZIGZAG && nInd >= 0) AssignFeedForOpenEdge( pCrv, vCrvOF_orig[nInd]) ; else { for ( int i = 0 ; i < ( int)vCrvOF_orig.size() ; ++ i) // se sono nel caso OneWay ho una regione con più chunk... AssignFeedForOpenEdge( pCrv, vCrvOF_orig[i]) ; } // se sono in una svuotatura a ZigZag, controllo la Feed sugli Angoli if ( m_Params.m_nSubType == POCKET_SUB_ZIGZAG) AssignFeedOnCorners( pCrv, vCrvOF_orig[nInd], m_TParams.m_dTDiam) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved_offs, const bool bIsLink, const ICRVCOMPOPOVECTOR& vLinks_done, const ICurveComposite* pCrv_orig, double dToll) { // controllo la validità della curva if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; // controllo se il Flag per assegnare la Feed è attivo if ( !m_bAssignFeed) return AssignDefaultFeed( pCrv) ; // imposto di Default la Feed minima per ogni sottocurva for ( int u = 0 ; u < ( int)pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ; // se non ho una superificie svuotata, allora esco ( con Feed Minima) if ( pSrfRemoved_offs == nullptr || ! pSrfRemoved_offs->IsValid() || pSrfRemoved_offs->GetChunkCount() == 0) { // controllo eventuali sovrapposizioni con lati aperti AssignFeedForOpenEdge( pCrv, pCrv_orig) ; return true ; } // clono la superificie ( valida) PtrOwner pSrf_Removed_offs_clone( CloneSurfFlatRegion( pSrfRemoved_offs)) ; if ( IsNull( pSrf_Removed_offs_clone) || ! pSrf_Removed_offs_clone->IsValid() || pSrf_Removed_offs_clone->GetChunkCount() == 0) return true ; // esco ( sempre con Feed Minima) // restringo la superificie in maniera appropriata if ( bIsLink) { // se curva di Link if ( ! pSrf_Removed_offs_clone->Offset( - m_TParams.m_dDiam / 2 + 1500 * EPS_SMALL, ICurve::OFF_CHAMFER) || ! pSrf_Removed_offs_clone->IsValid() || pSrf_Removed_offs_clone->GetChunkCount() == 0) return true ; // esco ( sempre con Feed Minima) } else if ( m_TParams.m_dDiam / 2 < GetSideStep()) { // se curva di Offset e raggio utensile < Side step if ( ! pSrf_Removed_offs_clone->Offset( GetSideStep() - m_TParams.m_dDiam / 2, ICurve::OFF_CHAMFER) || ! pSrf_Removed_offs_clone->IsValid() || pSrf_Removed_offs_clone->GetChunkCount() == 0) return true ; // esco ( sempre con Feed Minima) } // classifico le parti interne alla superificie creata solo dagli Offset CRVCVECTOR ccClass ; if ( ! pSrf_Removed_offs_clone->GetCurveClassification( *pCrv, EPS_SMALL, ccClass)) return true ; // esco ( sempre con Feed Minima) // creo la nuova curva con le Feed regolate PtrOwner pCrv_new( CreateCurveComposite()) ; if ( IsNull( pCrv_new)) return false ; for ( int i = 0 ; i < ( int)ccClass.size() ; ++ i) { double dCurrFeed = GetMinFeed() ; PtrOwner PCrv_sez( GetCurveComposite( pCrv->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ; if ( IsNull( PCrv_sez)) continue ; if ( ccClass[i].nClass == CRVC_IN) dCurrFeed = GetMaxFeed() ; for ( int u = 0 ; u < PCrv_sez->GetCurveCount() ; ++ u) PCrv_sez->SetCurveTempProp( u, FEED_DIVISOR * dCurrFeed, 0) ; pCrv_new->AddCurve( Release( PCrv_sez)) ; } pCrv->Clear() ; pCrv->AddCurve( Release( pCrv_new)) ; // controllo eventuali sovrapposizioni tra la curva attuale e i Link in precedenza percorsi for ( int l = 0 ; l < ( int)vLinks_done.size() ; ++ l) { IntersCurveCurve intCC( *pCrv, *vLinks_done[l]) ; for ( int i = 0 ; i < intCC.GetIntersCount() ; ++ i) { IntCrvCrvInfo aInfo ; if ( ! intCC.GetIntCrvCrvInfo( i, aInfo) || ! aInfo.bOverlap || AreSamePointApprox( aInfo.IciA[0].ptI, aInfo.IciA[1].ptI)) continue ; PtrOwner pCrv_before( CloneCurveComposite( pCrv)) ; if ( ! pCrv_before->TrimEndAtParam( aInfo.IciA[0].dU)) pCrv_before->Clear() ; PtrOwner pCrv_after( CloneCurveComposite( pCrv)) ; if ( ! pCrv_after->TrimStartAtParam( aInfo.IciA[1].dU)) pCrv_after->Clear() ; PtrOwner pCrv_overlap( GetCurveComposite( pCrv->CopyParamRange( aInfo.IciA[0].dU, aInfo.IciA[1].dU))) ; for ( int u = 0 ; u < pCrv_overlap->GetCurveCount() ; ++ u) pCrv_overlap->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ; pCrv->Clear() ; pCrv->AddCurve( Release( pCrv_before)) ; pCrv->AddCurve( Release( pCrv_overlap)) ; pCrv->AddCurve( Release( pCrv_after)) ; } } if ( ! bIsLink) { // NEL CASO DI OFFSET // creo un intervallo con tutte le Feed Minime Intervals IntMinFeed ; for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { int nProp ; pCrv->GetCurveTempProp( u, nProp, 0) ; if ( abs ( nProp - int( FEED_DIVISOR * GetMinFeed()) < 5 * EPS_SMALL)) IntMinFeed.Add( u, u + 1) ; } double dParS = EPS_SMALL ; double dParE = EPS_SMALL ; // a questo intervallo tolgo tutti i sottotratti di lunghezza inferiore alla tolleranza richiesta Intervals IntMinFeed_noSmall ; bool bFound = IntMinFeed.GetFirst( dParS, dParE) ; while ( bFound) { PtrOwner pCrv_Crv( pCrv->CopyParamRange( dParS, dParE)) ; double dLen = EPS_SMALL ; pCrv_Crv->GetLength( dLen) ; if ( dLen > dToll + 5 * EPS_SMALL) IntMinFeed_noSmall.Add( dParS, dParE) ; bFound = IntMinFeed.GetNext( dParS, dParE) ; } for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { if ( IntMinFeed_noSmall.IsInside( u + 0.5)) pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ; else pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ; } } else { // NEL CASO DI LINK // le curve con lunghezza < dToll vanno modificate for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { double dLen = EPS_SMALL ; pCrv->GetCurve( u)->GetLength( dLen) ; if ( dLen < dToll + 5 * EPS_SMALL) { PtrOwner pCrvCompo( CloneCurveComposite( pCrv)) ; if ( ! pCrvCompo->TrimStartAtParam( u)) pCrvCompo->Clear() ; if ( ! pCrvCompo->TrimEndAtLen( dLen + m_TParams.m_dDiam)) pCrvCompo->Clear() ; if ( pCrvCompo->IsValid()) { bool bFound = false ; for ( int uu = 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) { double dLenH = EPS_SMALL ; pCrvCompo->GetCurve( uu)->GetLength( dLenH) ; if ( dLenH < dToll + 5 * EPS_SMALL) continue ; // cerco tra le curve successive vicine se ne trovo una con Feed Minima int nProp ; pCrvCompo->GetCurveTempProp( uu, nProp, 0) ; if ( abs( nProp - int( FEED_DIVISOR * GetMinFeed()) < 5 * EPS_SMALL)) { pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ; bFound = true ; break ; } } if ( ! bFound && u != 0) { // arrivato qui, so che successivamente non ho curve con Feed Minima vicine int nProp ; pCrv->GetCurveTempProp( u - 1, nProp, 0) ; if ( abs( nProp - int( FEED_DIVISOR * GetMaxFeed()) < 5 * EPS_SMALL)) // se anche la precedente ha Feed Massima -> la curva u-esima è piccola ed Isolata pCrv->SetCurveTempProp( u, nProp, 0) ; } } else pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ; } } } // controllo eventuali sovrapposizioni con lati aperti AssignFeedForOpenEdge( pCrv, pCrv_orig) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::AssignFeedForReturnPath( ICurveComposite* pCrv) { // controllo curva corrente if ( pCrv == nullptr || !pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; // controllo se il Flag per assegnare la Feed è attivo if ( !m_bAssignFeed) return AssignDefaultFeed( pCrv) ; if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; for ( int u = 0 ; u < ( int)pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::AssignFeedCrvOnUnclearedRegions( ICurveComposite* pCrv) { // controllo validità della curva if ( pCrv == nullptr || !pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; // controllo se il Flag per assegnare la Feed è attivo if ( !m_bAssignFeed) return AssignDefaultFeed( pCrv) ; if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; for ( int u = 0 ; u < ( int)pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::AssignDefaultFeed( ICurveComposite* pCrv) { if ( pCrv == nullptr || !pCrv->IsValid() || pCrv->GetCurveCount() == 0) return false ; for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ; return true ; } bool Pocketing::DrawColoredCrvForFeedTest( ICurve* pCurve, double dFeed) { #if 0 if ( abs( dFeed - GetMinFeed()) < EPS_SMALL) { int rosso = m_pGeomDB->AddGeoObj(GDB_ID_NULL, GDB_ID_ROOT, pCurve->Clone()) ; m_pGeomDB->SetMaterial( rosso, RED) ; m_pGeomDB->SetStatus( rosso, ( m_Params.m_nSubType == POCKET_SUB_SPIRALIN || m_Params.m_nSubType == POCKET_SUB_SPIRALIN) ? GDB_ST_OFF : GDB_ST_ON) ; } else if ( abs( dFeed - GetMaxFeed()) < EPS_SMALL) { int verde = m_pGeomDB->AddGeoObj(GDB_ID_NULL, GDB_ID_ROOT, pCurve->Clone()) ; m_pGeomDB->SetMaterial( verde, GREEN) ; m_pGeomDB->SetStatus( verde, ( m_Params.m_nSubType == POCKET_SUB_SPIRALIN || m_Params.m_nSubType == POCKET_SUB_SPIRALIN) ? GDB_ST_OFF : GDB_ST_ON) ; } else { int giallo = m_pGeomDB->AddGeoObj(GDB_ID_NULL, GDB_ID_ROOT, pCurve->Clone()) ; m_pGeomDB->SetMaterial( giallo, YELLOW) ; m_pGeomDB->SetStatus( giallo, ( m_Params.m_nSubType == POCKET_SUB_SPIRALIN || m_Params.m_nSubType == POCKET_SUB_SPIRALIN) ? GDB_ST_OFF : GDB_ST_ON) ; } #endif return true ; }