//---------------------------------------------------------------------------- // EgalTech 2017-2022 //---------------------------------------------------------------------------- // File : Pocketing.cpp Data : 17.12.23 Versione : 2.5l3 // 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/EGkPolygonElevation.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 "/EgtDev/Include/ENkDijkstra.h" #include #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" // 2433 = "Error in Pocketing : Volume not found" // 2434 = "Error in Pocketing : Part not found" // 2435 = "Error in Pocketing : Mergin volume failed" // 2436 = "Error in Pocketing : Computing extra step failed" // 2437 = "Error in Pocketing : Slicing volume failed" // 2438 = "Error in Pocketing : Projecting volume failed" // 2439 = "Error in Pocketing : Detecting open edges failed" // 2440 = "Error in Pocketing : Computing second pocket failed" // 2441 = "Error in Pocketing : Return path not computable" // 2442 = "Error in Pocketing : Defining volume's faces failed" // 2443 = "Error in Pocketing : Failed region simplification" // 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 ; static double SAFE_Z_RET = 5. ; //---------------------------------------------------------------------------- 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 ; pPock->m_bOptOffset = m_bOptOffset ; pPock->m_bOptOffsetCM = m_bOptOffsetCM ; pPock->m_bAssignFeed = m_bAssignFeed ; pPock->m_dDiam_Prec = m_dDiam_Prec ; pPock->m_dOffsetR_Prec = m_dOffsetR_Prec ; pPock->m_dSideStep_Prec = m_dSideStep_Prec ; pPock->m_dLen_Prec = m_dLen_Prec ; pPock->m_bOrderStepZ = m_bOrderStepZ ; pPock->m_bPocketPlane = m_bPocketPlane ; pPock->m_dOpenEdgeRad = m_dOpenEdgeRad ; } 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 ; m_bOptOffset = true ; m_bOptOffsetCM = false ; m_bAssignFeed = false ; m_dDiam_Prec = 0. ; m_dOffsetR_Prec = 0. ; m_dOffsetR_Prec = 0. ; m_dSideStep_Prec = 0. ; m_dLen_Prec = 0. ; m_bOrderStepZ = true ; m_bPocketPlane = false ; m_dOpenEdgeRad = 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)) 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 PNTVECTOR vPts ; vPts.push_back( ptCenter) ; for ( int f = 0 ; f < pStm->GetFacetCount() ; ++ f) { Point3d ptC ; Vector3d vtN ; if ( ! pStm->GetFacetCenter( f, ptC, vtN)) return false ; vPts.push_back( ptC) ; } if ( ! SetPocketingVolume( vPts, 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 ( ! vpCrvs.empty()) { // concateno le curve se necessario ICRVCOMPOPOVECTOR vCrvCompo ; if ( ! ChainCurveArray( vpCrvs, vCrvCompo)) return false ; // verifico che siano curve chiuse ( devono delimitare l'area da svuotare) PNTVECTOR vPts ; for ( int u = 0 ; u < int( vCrvCompo.size()) ; ++ u) { if ( ! vCrvCompo[u]->IsClosed()) { m_pMchMgr->SetLastError( 2402, "Error in Pocketing : Open Contour") ; return false ; } Point3d ptInside ; if ( ! vCrvCompo[u]->GetCentroid( ptInside)) return false ; vPts.push_back( ptInside) ; for ( int uu = 0 ; uu < vCrvCompo[u]->GetCurveCount() ; ++ uu) { if ( ! vCrvCompo[u]->GetCurve( uu)->GetMidPoint( ptInside)) return false ; vPts.push_back( ptInside) ; } } // creazione della TriMesh di pocketing PtrOwner pStmCurve_Volume( CreateSurfTriMesh()) ; if ( IsNull( pStmCurve_Volume) || ! pStmCurve_Volume->AdjustTopology() || ! CreateStmForIntersection( pStmCurve_Volume, vCrvCompo, vtExtr)) { // se non ho curve e non ho un volume valido, errore m_pMchMgr->SetLastError( 2433, "Error in Pocketing : Volume not defined") ; return false ; } // intersezione della Trimesh con il finito -> rivavo il volume di svuotatura if ( ! SetPocketingVolume( vPts, pStmCurve_Volume)) { m_pMchMgr->SetLastError( 2433, "Error in Pocketing : Volume not defined") ; return false ; } if ( pStm_tmp->IsValid()) { if ( ! pStm_tmp->Add( *pStmCurve_Volume)) { m_pMchMgr->SetLastError( 2433, "Error in Pocketing : Volume not defined") ; return false ; } } } // se non ho un volume valido, errore if ( ! pStm_tmp->IsValid() || pStm_tmp->GetTriangleCount() == 0) { m_pMchMgr->SetLastError( 2433, "Error in Pocketing : Volume not defined") ; 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( 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 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 ; // 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) ; // 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 ; // ricavo l'estensione massima possibile double dExt ; if ( ! CalcOffsExtensionsForCurves( pCrvLoopExt, vtN, dExt)) return false ; // allargo il loop esterno presso i chiusi if ( ! GetExtendedLoopToFitStmVolume( pCrvLoopExt, dExt, 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, dExt * 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::CalcOffsExtensionsForCurves( const ICurveComposite* pCrvCompo, const Vector3d& vtN, double& dExt) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || ! vtN.IsValid() || vtN.IsSmall()) return false ; // ricavo il centroide della curva e i punti inziali delle sottocurve PNTVECTOR vPts ; Point3d ptC ; if ( ! pCrvCompo->GetCentroid( ptC)) return false ; vPts.push_back( ptC) ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) { if ( ! pCrvCompo->GetCurve( u)->GetStartPoint( ptC)) return false ; vPts.push_back( ptC) ; } // creo un frame coerente con la curva e la direzione di svuotatura Frame3d frCurr ; frCurr.Set( ptC, vtN) ; if ( ! frCurr.IsValid()) return false ; // recupero la part corrente PtrOwner pStmPart( CreateSurfTriMesh()) ; if ( IsNull( pStmPart) || ! GetCurrentPart( vPts, pStmPart)) return false ; // taglio la part con il piano definito dalla curva Plane3d plCut ; plCut.Set( ptC, - vtN) ; if ( ! plCut.IsValid() || ! pStmPart->Cut( plCut, true)) return false ; // porto la Part nel frame corrente if ( ! pStmPart->ToLoc( frCurr)) return false ; // l'estensione dei lati aperti può avvernire lungo X ed Y, la Z serve per calcolare di quanto estrudere la curva // al fine di ricavare un volume di svuotatura. Cerco quindi la dimensione massima del box lungo i tre assi BBox3d BBox ; if ( ! pStmPart->GetLocalBBox( BBox)) return false ; dExt = max( max( BBox.GetDimX(), BBox.GetDimY()), BBox.GetDimZ()) ; dExt *= 1.05 ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetExtendedLoopToFitStmVolume( ICurveComposite* pCrvLoop, const double& dExt, const Vector3d& vtN) { // controllo del parametro if ( pCrvLoop == nullptr || ! pCrvLoop->IsValid()) return false ; // ricavo i parametri della curva double dThick ; pCrvLoop->GetThickness( dThick) ; // 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, dExt, 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], dExt, 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 PNTVECTOR& vPtInside, 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 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 esiste un punto interno ad essa for ( int p = 0 ; p < int( vPtInside.size()) ; ++ p) { DistPointSurfTm DistPtStm( vPtInside[p], *pStmRawPart) ; double dDist ; DistPtStm.GetDist( dDist) ; if ( DistPtStm.IsPointInside() || DistPtStm.IsSmall()) { // se interno, allora prendo restituisco la Part 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 PNTVECTOR& vPtInside, ISurfTriMesh* pStm) { // controllo parametro if ( pStm == nullptr || ! pStm->IsValid()) return false ; // recupero il Current Part PtrOwner pStmCurrRawPart( CreateSurfTriMesh()) ; if ( IsNull( pStmCurrRawPart) || ! GetCurrentPart( vPtInside, 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 il grezzo PtrOwner pStm_Raw( CreateSurfTriMesh()) ; if ( IsNull( pStm_Raw) || ! GetRaw( pStm_Raw)) { m_pMchMgr->SetLastError( 2405, "Error in Pocketing : Empty RawBox") ; return false ; } // 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 PNTVECTOR vPts ; Point3d ptC ; pStm_PartVolume->GetCentroid( ptC) ; vPts.push_back( ptC) ; for ( int f = 0 ; f < pStm_PartVolume->GetFacetCount() ; ++ f) { Vector3d vtN ; if ( ! pStm_PartVolume->GetFacetCenter( f, ptC, vtN)) return false ; vPts.push_back( ptC) ; } PtrOwner pStm_Part( CreateSurfTriMesh()) ; if ( IsNull( pStm_Part) || ! GetCurrentPart( vPts, pStm_Part)) { m_pMchMgr->SetLastError( 2434, "Error in Pocketing : Part not Found") ; return false ; } // rendo il volume accettabile per la svuotatura... if ( ! AdjustPocketingSideForVolumePart( pStm_PartVolume, vtTool)) { m_pMchMgr->SetLastError( 2435, "Error in Pocketing : Mergin volume failed") ; return false ; } // devo calcolare l'Elevazione... ( devo tenere conto del grezzo, non solo della parte) 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 piano su cui iniziare a svuotare è quello che si trova a Z minima double dZ_min = BBox.GetMin().z ; // riporto in globale pStm_PartVolume->ToGlob( frElevation) ; // calcolo l'elevazione massima su ogni faccia del volume con normale circa - vtTool ( il volume è interno) double dElev = 0. ; // Flat Region per regione di calcolo dell'elevazione SurfFlatRegionByContours SfrBC ; pStm_PartVolume->Invert() ; // ho curve e normali orientate correttamente for ( int i = 0 ; i < pStm_PartVolume->GetFacetCount() ; ++ i) { Vector3d vtN_f ; if ( ! pStm_PartVolume->GetFacetNormal( i, vtN_f)) return false ; if ( ! AreSameVectorEpsilon( vtTool, vtN_f, 5 * EPS_SMALL)) continue ; // ricavo le polyLine di bordo POLYLINEVECTOR vPL ; if ( ! pStm_PartVolume->GetFacetLoops( i, vPL) || vPL.empty()) return false ; // ricavo le curve Composite associate e creo la FlatRegion assocoiata per l'orientamento // NB. CalcRegionElevation calcola l'elevazione nella regione definita dalle curve, quindi serve che siano orientate for ( int j = 0 ; j < int( vPL.size()) ; ++ j) { PtrOwner pCompo( CreateCurveComposite()) ; if ( IsNull( pCompo) || ! pCompo->FromPolyLine( vPL[j]) || ! pCompo->IsValid() || ! SfrBC.AddCurve( Release( pCompo))) return false ; } } // ricavo la Flat region per il calcolo dell'elevazione PtrOwner pSfrElevation( SfrBC.GetSurf()) ; if ( IsNull( pSfrElevation) || ! pSfrElevation->IsValid()) return false ; if ( AreOppositeVectorApprox( pSfrElevation->GetNormVersor(), vtTool)) pSfrElevation->Invert() ; // calcolo l'elevazione for ( int c = 0 ; c < pSfrElevation->GetChunkCount() ; ++ c) { for ( int l = 0 ; l < pSfrElevation->GetLoopCount( c) ; ++ l) { PtrOwner pCrvLoop( ConvertCurveToComposite( pSfrElevation->GetLoop( c, l))) ; if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid()) return false ; if ( l > 0) pCrvLoop->Invert() ; double dCurrElev = 0. ; if ( ! CalcRegionElevation( pCrvLoop, vtTool, 0., 0.5 * ( m_dDiam_Prec > 0 ? m_dDiam_Prec : m_TParams.m_dDiam), m_dLen_Prec > 0 ? m_dLen_Prec : m_TParams.m_dLen, dCurrElev)) return false ; dElev = max( dElev, dCurrElev) ; // l'elevazione è la massima tra quelle trovate } } pStm_PartVolume->Invert() ; // riporto come in orginale // eventuale imposizione massima elevazione da note utente double dMaxElev = 0. ; 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 + GetOffsL() < dElev && m_TParams.m_dMaxMat + GetOffsL() < MIN_MAXMAT) { string sInfo = "Error in Pocketing : Tool MaxMaterial too small (" + ToString( m_TParams.m_dMaxMat + GetOffsL(), 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 + GetOffsL() + EPS_SMALL) { dOkStep = m_TParams.m_dMaxMat + GetOffsL() + EPS_SMALL ; string sInfo = "Warning in Pocketing : machining step (" + ToString( m_Params.m_dStep, 1) + ") bigger than MaxMaterial (" + ToString( m_TParams.m_dMaxMat + GetOffsL(), 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 + GetOffsL() + EPS_SMALL) { string sInfo = "Warning in Pocketing : machining depth (" + ToString( dElev, 1) + ") bigger than MaxMaterial (" + ToString( m_TParams.m_dMaxMat + GetOffsL(), 1) + ")" ; m_pMchMgr->SetWarning( 2458, sInfo) ; dDepth -= dElev - m_TParams.m_dMaxMat - GetOffsL() ; dElev = m_TParams.m_dMaxMat + GetOffsL() ; } } // 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 + GetOffsL() - ( 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) ; // 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 flag per minimo raggio di uscita per lato aperto FromString( ExtractInfo( m_Params.m_sUserNotes, "OpenEdgeRad="), m_dOpenEdgeRad) ; // recupero eventuale minima lunghezza di attacco su lato aperto FromString( ExtractInfo( m_Params.m_sUserNotes, "OpenMinSafe="), m_dOpenMinSafe) ; // recupero flag per ottimizzazioni Offset int nOptOffs ; m_bOptOffset = ( FromString( ExtractInfo( m_Params.m_sUserNotes, "OptOffs="), nOptOffs) && nOptOffs != 0) ; // 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, pStm_PartVolume)) 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 grezzo, il Volume e la Part che lo contiene in questo frame pStm_PartVolume->ToLoc( frElevation) ; pStm_Part->ToLoc( frElevation) ; pStm_Raw->ToLoc( frElevation) ; // verifiche per svuotature dal basso m_bAggrBottom = false ; // scorro tutte le facce con centroide in Z = 0 for ( int f = 0 ; f < pStm_PartVolume->GetFacetCount() ; ++ f) { Point3d ptC ; Vector3d vtN ; if ( ! pStm_PartVolume->GetFacetCenter( f, ptC, vtN)) return false ; if ( abs( ptC.z) > 5 * EPS_SMALL) continue ; // recupero il loop della faccia POLYLINEVECTOR vPL ; if ( ! pStm_PartVolume->GetFacetLoops( f, vPL)) return false ; for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { // recupero la curva composita PtrOwner pCompo( CreateCurveComposite()) ; if ( IsNull( pCompo) || ! pCompo->FromPolyLine( vPL[i]) || ! pCompo->IsValid()) return false ; // porto in globale pCompo->ToGlob( frElevation) ; // verifico svuotatura dal basso if ( ! VerifyPathFromBottom( pCompo, vtTool)) return false ; } } // 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 ISURFFRPOVECTOR vSrfLimit ; // vettore delle superfici limite double dExtraLenInOut = 0. ; // Lunghezza extra per Tool non cilindrici per LeadIn/Out // ricavo le superfici di svuotatura if ( ! SliceVolume( pStm_PartVolume, pStm_Part, pStm_Raw, vSrfSliced, vSrfLimit, vCrvOEWithFlags, vbChangedPrec, vVtTrasl, nStep, vtTool, dElev, dDepth, dStep, dExtraLenInOut)) 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 superfici limite nel frame globale for ( int s = 0 ; s < int( vSrfLimit.size()) ; ++ s) vSrfLimit[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, vSrfLimit, vtTool, vtTool, dDepth, dElev, dMaxElev, dOkStep, bSplitArcs, dExtraLenInOut)) return false ; break ; case POCKET_SUB_SPIRALOUT : if ( ! AddSpiralOut( vSrfSliced, vCrvOEWithFlags, vbChangedPrec, vVtTrasl, vSrfLimit, vtTool, vtTool, dDepth, dElev, dMaxElev, dOkStep, bSplitArcs, dExtraLenInOut)) return false ; break ; } } // incremento numero di svuotature ++ m_nPockets ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetProjectionOfStmToNotPocket( const ISurfTriMesh* pStm, const ISurfTriMesh* pStmVol, const Vector3d& vtTrasl, ISurfFlatRegion* pSfrProj) { // controllo i parametri if ( pStm == nullptr || ! pStm->IsValid() || pStmVol == nullptr || ! pStmVol->IsValid()) return false ; // ricavo la parte di grezzo da non rovinare PtrOwner pStmToNotRuin( CloneSurfTriMesh( pStm)) ; if ( IsNull( pStmToNotRuin) || ! pStmToNotRuin->IsValid() || ! pStmToNotRuin->Subtract( *pStmVol) || ! pStmToNotRuin->IsValid()) return false ; // NB. La parte vera a propria di interesse è quella sopra la piano attuale Plane3d plZ ; plZ.Set( ORIG + vtTrasl, - Z_AX) ; if ( ! plZ.IsValid()) return false ; // taglio la supericie if ( ! pStmToNotRuin->Cut( plZ, true)) return false ; // se non ho ottenuto nulla, esco if ( ! pStmToNotRuin->IsValid() || pStmToNotRuin->GetTriangleCount() == 0) return true ; // Proietto la TriMesh plZ.Invert() ; PtrOwner pSfrLimitH( CreateSurfFlatRegion()) ; if ( IsNull( pSfrLimitH) || ! ProjectStmOnPlane( pStmToNotRuin, plZ, pSfrLimitH)) return false ; // se la limite non è valida, allora per approssimazione non la considero if ( ! pSfrLimitH->IsValid()) return true ; // 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, const ISurfTriMesh* pStm_Raw, ISURFFRPOVECTOR& vSrfSliced, ISURFFRPOVECTOR& vSfrLimit, std::vector& vCrvOEWithFlags, BOOLVECTOR& vbChangedPrec, VCT3DVECTOR& vVtTrasl, int& nStep, const Vector3d vtTool, const double dElev, const double dDepth, const double dStep, double& dExtraLenInOut) { // controllo dei parametri if ( pStmVol == nullptr || ! pStmVol->IsValid() || pStm_Part == nullptr || ! pStm_Part->IsValid()) return false ; // calcolo di eventuali Step Extra // NB. Per gli step Extra la decisione dei lati aperti i chiusi non avviene guardando la Part, ma una superficie // ricavata in maniera opportuna ( per ogni step, tale superficie è memorizzata in vStmES_Edges_OC) vVtTrasl.clear() ; INTVECTOR vIndExtraSteps ; ISURFTMPOVECTOR vStmES_Edges_OC ; if ( ! CalcExtraSteps( pStmVol, vVtTrasl, nStep, dElev, dDepth, dStep, vIndExtraSteps, vStmES_Edges_OC)) { m_pMchMgr->SetLastError( 2436, "Error in Pocketing : Computing Extra Step failed") ; return false ; } // aggirno le dimensioni dei vettori da restituire in base agli step calcolati vSrfSliced.clear() ; vSrfSliced.resize( int( vVtTrasl.size())) ; vSfrLimit.clear() ; vSfrLimit.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) { // 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 allo step corrente PtrOwner pSfrLimit( CreateSurfFlatRegion()) ; PtrOwner pSfrLimitElevation( CreateSurfFlatRegion()) ; if ( IsNull( pSfrLimit) || IsNull( pSfrLimitElevation) || ! GetProjectionOfStmToNotPocket( pStm_Part, pStmVol, vVtTrasl[j-1], pSfrLimit) || ! GetProjectionOfStmToNotPocket( pStm_Raw, pStmVol, vVtTrasl[j-1], pSfrLimitElevation)) { m_pMchMgr->SetLastError( 2442, "Error in Pocketing : Defining volume's faces failed") ; return false ; } // determino se è uno step base o extra bool bIsExtraStep = ! vIndExtraSteps.empty() && ( find( vIndExtraSteps.begin(), vIndExtraSteps.end(), j-1) != vIndExtraSteps.end()) ; // taglio il volume con il piano di svuotatura attuale PtrOwner pSfrCurr( CreateSurfFlatRegion()) ; if ( IsNull( pSfrCurr) || ! CutVolumeByPlane( pStmVol, pStm_Part, vVtTrasl[j-1], bIsExtraStep, pSfrCurr)) { m_pMchMgr->SetLastError( 2437, "Error in Pocketing : Slicing volume failed") ; return false ; } // tolgo/aggiungo eventuali proiezioni if ( ! ProjectVolume( pStmVol, pStm_Part, vVtTrasl[j-1], j == 1 ? V_INVALID : vVtTrasl[j-2], pSfrLimit, pSfrCurr, int( vVtTrasl.size()) == 1, dExtraLenInOut)) { m_pMchMgr->SetLastError( 2438, "Error in Pocketing : Projecting volume failed") ; return false ; } // ricavo le proprietà di lato aperto/chiuso if ( ! ChooseCloseOrOpenEdge( pSfrCurr, ( bIsExtraStep && ! m_bPocketPlane) ? vStmES_Edges_OC[j-1] : pStm_Part, bIsExtraStep && ! m_bPocketPlane)) { m_pMchMgr->SetLastError( 2439, "Error in Pocketing : Detecting open edges failed") ; return false ; } // dato che la FlatRegion è ricavata a partire da una Trimesh, semplifico i suoi loops if ( ! SimplifySfrLoops( pSfrCurr)) { m_pMchMgr->SetLastError( 2443, "Error in Pocketing : Failed region simplification") ; 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)) { m_pMchMgr->SetLastError( 2430, "Error in Pocketing : adjust open edges failed") ; return false ; } // controllo se la superficie deriva da una lavorazione precedente if ( ! GetNewSfrByAnotherPocketing( pSfrCurr, pSfrNoExtension, pStmVol, pStm_Part, pSfrLimit)) { m_pMchMgr->SetLastError( 2440, "Error in Pocketing : Computing second pocket failed") ; return false ; } // inserisco la superficie nel vettore vSrfSliced[j-1].Set( Release( pSfrCurr)) ; // inserisco la superficie limite nel vettore vSfrLimit[j-1].Set( Release( pSfrLimit)) ; // controllo se la superficie è uguale a quella inserita nello step precedente // ... W.I.P ... vbChangedPrec[j-1] = true ; // ... } return true ; } //---------------------------------------------------------------------------- bool Pocketing::SimplifySfrLoops( ISurfFlatRegion* pSfr) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid()) return false ; // superficie semplificata PtrOwner pSfrSimple( CreateSurfFlatRegion()) ; if ( IsNull( pSfrSimple)) return false ; // scorro tutti i loop della superficie da semplificare for ( int c = 0 ; c < pSfr->GetChunkCount() ; ++ c) { for ( int l = 0 ; l < pSfr->GetLoopCount( c) ; ++ l) { PtrOwner pCrvLoop( ConvertCurveToComposite( pSfr->GetLoop( c, l))) ; if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid()) return false ; // ricavo i tratti con proprità omogenee, quindi aperti e chiusi ICRVCOMPOPOVECTOR vCrvSameProps ; if ( ! GetHomogeneousParts( pCrvLoop, vCrvSameProps)) return false ; // curva semplificata che restituirà il loop PtrOwner pCrvSimpleLoop( CreateCurveComposite()) ; if ( IsNull( pCrvSimpleLoop)) return false ; // per ogni tratto omogeneo trovato, cerco di semplificare for ( int t = 0 ; t < int( vCrvSameProps.size()) ; ++ t) { PtrOwner pCrv_part( CloneCurveComposite( vCrvSameProps[t])) ; if ( IsNull( pCrv_part) || ! pCrv_part->IsValid()) return false ; // tmp prop ( Aperto/Chiuso) int nTmpProp = pCrv_part->GetCurve( 0)->GetTempProp( 0) ; // estremi Point3d ptS ; pCrv_part->GetStartPoint( ptS) ; Point3d ptE ; pCrv_part->GetEndPoint( ptE) ; PolyArc PA ; Point3d ptS_c, ptE_c ; // 1) Mergiamo per uniformità if ( pCrv_part->MergeCurves( 200 * EPS_SMALL, 200 * EPS_ANG_SMALL) && // 2) Rimozione Spikes o Curve a Z pCrv_part->RemoveSmallDefects( 150 * EPS_SMALL, 2 * ANG_TOL_STD_DEG, true) && // 3) Interpoliamo mediante Linee ed Archi pCrv_part->ApproxWithArcsEx( 150 * EPS_SMALL, ANG_TOL_STD_DEG, LIN_FEA_STD, PA) && pCrv_part->Clear() && pCrv_part->FromPolyArc( PA) && // 4) Controllo che i punti inziali e finali siano gli stessi...( per estrema sicurezza) pCrv_part->GetStartPoint( ptS_c) && pCrv_part->GetEndPoint( ptE_c) && ( pCrv_part->IsClosed() || ( AreSamePointApprox( ptS, ptS_c) && AreSamePointApprox( ptE, ptE_c)))) { // assegno la temp prop a tutte le sue sottocurve for ( int u = 0 ; u < pCrv_part->GetCurveCount() ; ++ u) pCrv_part->SetCurveTempProp( u, nTmpProp, 0) ; } // inserisco il tratto di curva semplificata nel loop semplificato if ( ! pCrvSimpleLoop->AddCurve( Release( pCrv_part))) return false ; } // ora che ho il loop semplificato lo inserisco nella flatRegion semplificata if ( l == 0) { if ( ! pSfrSimple->AddExtLoop( Release( pCrvSimpleLoop))) return false ; } else if ( ! pSfrSimple->AddIntLoop( Release( pCrvSimpleLoop))) return false ; } } // sostituisco la superficie semplificata ricavata if ( pSfrSimple->IsValid()) { pSfr->Clear() ; pSfr->CopyFrom( pSfrSimple) ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetRaw( ISurfTriMesh* pStmRaw) { // controllo parametri, MachManager e database geometrico if ( pStmRaw == nullptr || m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; pStmRaw->AdjustTopology() ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, m_nPhase)) { // recupero l'oggetto dal database con tale Id int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; const IGeoObj* pGObj = m_pGeomDB->GetGeoObj( nRawSolidId) ; // recupero il frame in cui si trova Frame3d frRaw ; m_pGeomDB->GetGlobFrame( nRawSolidId, frRaw) ; if ( pGObj == nullptr) return false ; // controllo che sia una Trimesh if ( pGObj->GetType() == SRF_TRIMESH) { // Trimesh della parte p-esima PtrOwner pStmRawPart( CloneSurfTriMesh( pGObj)) ; if ( IsNull( pStmRawPart)) return false ; // porto la trimesh in globale pStmRawPart->LocToLoc( frRaw, GLOB_FRM) ; // lo aggiungo alla Trimesh complessiva pStmRaw->Add( *pStmRawPart) ; } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } return pStmRaw->IsValid() && pStmRaw->GetTriangleCount() > 0 ; } //---------------------------------------------------------------------------- bool Pocketing::CalcExtraSteps( const ISurfTriMesh* pStmVol, VCT3DVECTOR& vVtTrasl, int& nStep, const double dElev, const double dDepth, const double dStep, INTVECTOR& vIndExtraSteps, ISURFTMPOVECTOR& vStmES_Edges_OC) { // controllo dei parametri if ( pStmVol == nullptr || ! pStmVol->IsValid()) return false ; // pulisco il vettore degli step, degli indici per l'Extra e delle superfici per i flag Open/Close vVtTrasl.clear() ; vIndExtraSteps.clear() ; vStmES_Edges_OC.clear() ; // creo un vettore per gli step calcolati e un vettore per gli step Extra VCT3DVECTOR vtExtraSteps ; // scorro tutti gli step calcolati originariamente for ( int j = 1 ; j <= nStep ; ++ j) { // ricavo il vettore di traslazione attuale Vector3d vtCurrTrasl = - Z_AX * ( dDepth - dElev + j * dStep) ; // lo inserisco negli step base vVtTrasl.push_back( vtCurrTrasl) ; // ricavo il vettore dello step successivo ( serve per verificare se tra i due step ne devo inserire altri intermedi) Vector3d vtPrecTrasl = - Z_AX * ( dDepth - dElev + ( j-1) * dStep) ; // taglio il volume di svuotatura tra due piani definiti da questi vettori PtrOwner pStmVol_c( CloneSurfTriMesh( pStmVol)) ; if ( IsNull( pStmVol_c) || ! pStmVol_c->IsValid()) return false ; Plane3d plCurr ; plCurr.Set( ORIG + vtCurrTrasl, - Z_AX) ; Plane3d plSucc ; plSucc.Set( ORIG + vtPrecTrasl, Z_AX) ; if ( ! plCurr.IsValid() || ! plSucc.IsValid() || ! pStmVol_c->Cut( plCurr, true) || ! pStmVol_c->Cut( plSucc, true)) return false ; // se non ricavo niente allora passo allo step successivo if ( ! pStmVol_c->IsValid()) continue ; // creo un vettore di quote Z per le facce che mi generano step extra... vector vdDists ; // cerco se ci sono delle facce che presentano normale parallela a ( -Z_AX)... for ( int f = 0 ; f < pStmVol_c->GetFacetCount() ; ++ f) { Vector3d vtN ; Point3d ptC ; if ( ! pStmVol_c->GetFacetCenter( f, ptC, vtN)) return false ; if ( ! AreSameOrOppositeVectorEpsilon( Z_AX, vtN, 5 * EPS_SMALL)) continue ; // ... calcolo la distanza che c'è con il piano attuale ( plCurr) double dDist = abs( DistPointPlane( ptC, plCurr)) ; // se circa lo step o circa 0, allora gli step base svuotano già queste facce... if ( dDist < 100 * EPS_SMALL || abs( dDist - dStep) < 100 * EPS_SMALL) continue ; // NB. Se ho più facce con normale ( - Z_AZ) tra i due step, voglio inserire un unico step Extra... bool bInsert = true ; for ( int d = 0 ; d < int( vdDists.size()) && bInsert ; ++ d) bInsert = ( abs( dDist - vdDists[d]) > 100 * EPS_SMALL * dStep) ; if ( bInsert) { vdDists.push_back( dDist) ; // aggiorno il vettore degli step extra vtExtraSteps.push_back( vtCurrTrasl + Z_AX * dDist) ; } } } // se non ho step extra allora esco if ( int( vtExtraSteps.size() == 0)) return true ; // in presenza di step extra creo una struttura adatta alla classificazione typedef pair< Vector3d, bool> STEP ; // copia vettore traslazione e flag per step extra vector vSTEP ; // inserisco gli STEP base for ( int j = 1 ; j <= int( vVtTrasl.size()) ; ++ j) { STEP currSTEP( vVtTrasl[j-1], false) ; vSTEP.push_back( currSTEP) ; } // inserisco gli STEP extra sort( vtExtraSteps.begin(), vtExtraSteps.end(), []( Vector3d &a, Vector3d&b){ return a.z > b.z ; }) ; for ( int j = 1 ; j <= int( vtExtraSteps.size()) ; ++ j) { STEP currSTEP( vtExtraSteps[j-1], true) ; vSTEP.push_back( currSTEP) ; } // se devo ordinarli per Z decrescente... if ( m_bOrderStepZ) sort( vSTEP.begin(), vSTEP.end(), []( STEP &a, STEP &b){ return a.first.z > b.first.z ; }) ; vVtTrasl.clear() ; // aggiorno il vettore degli step e degli indici e ricavo la TriMesh per la gestione degli Open/Close for ( int j = 1 ; j <= int( vSTEP.size()) ; ++ j) { vVtTrasl.push_back( vSTEP[j-1].first) ; vStmES_Edges_OC.emplace_back( CreateSurfTriMesh()) ; if ( vSTEP[j-1].second) { vIndExtraSteps.push_back( j-1) ; // se svuotatura extra Step non completa non sul piano... if ( ! m_bPocketPlane) { // definisco il piano di taglio alla quota corrente Plane3d plExtra ; plExtra.Set( ORIG + vVtTrasl[j-1], - Z_AX) ; if ( ! plExtra.IsValid()) return false ; // taglio la superficie allo step corrente, eliminando le facce Extra da svuotare PtrOwner pStm_Vol_OC( CloneSurfTriMesh( pStmVol)) ; if ( IsNull( pStm_Vol_OC) || ! pStm_Vol_OC->IsValid() || ! pStm_Vol_OC->Cut( plExtra, false) || ! pStm_Vol_OC->IsValid()) return false ; // memorizzo la faccia vStmES_Edges_OC.back().Set( Release( pStm_Vol_OC)) ; } } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetNewSfrByAnotherPocketing( ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrNoExtendedByOpenEdges, const ISurfTriMesh* pStmVol, const ISurfTriMesh* pStm_Part, 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 ; // considero esistenti i lati aperti m_bOpenOutRaw = true ; // <--- verificare se è una assunzione lecita // 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 a cui tolgo le facce // aperte ( non è garantito che il Tool precedente sia riuscito a passare presso un lato aperto data la sua // dimensione... INTVECTOR vAllTria_A ; PtrOwner pStmVol_A( CloneSurfTriMesh( pStmVol)) ; if ( IsNull( pStmVol_A) || ! pStmVol_A->IsValid()) return false ; Plane3d plZ0 ; plZ0.Set( ORIG, - Z_AX) ; // la faccia a Z = 0 è chiusa ( va rimossa) if ( ! plZ0.IsValid() || ! pStmVol_A->Cut( plZ0, false)) return false ; for ( int f = 0 ; f < pStmVol_A->GetFacetCount() ; ++ f) { // ricavo i parametri della faccia Point3d ptC_f ; Vector3d vtN_f ; if ( ! pStmVol_A->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 ( ! pStmVol_A->GetAllTriaInFacet( f, vT)) return false ; // ... e aggiungo i loro indici al vettore for ( int t = 0 ; t < int( vT.size()) ; ++ t) vAllTria_A.push_back( vT[t]) ; } } // dalla superficie tra i due piani rimouovo i triangoli ottenuti for ( int t = 0 ; t < int( vAllTria_A.size()) ; ++ t) if ( ! pStmVol_A->RemoveTriangle( vAllTria_A[t])) return false ; pStmVol_A->DoCompacting() ; // Open/Close... // alzo leggermente la supericie per l'ultimi step pSfrAct->Translate( Z_AX * 50 * EPS_SMALL) ; // per sicurezza if ( ! ChooseCloseOrOpenEdge( pSfrAct, pStmVol_A, true)) return false ; pSfrAct->Translate( -Z_AX * 50 * EPS_SMALL) ; // riporto in posizione originale // 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 ; // 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 ; } } // 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, const bool bIsExtraStep, 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 ; // FlatRegion della regione piana da svuotare SurfFlatRegionByContours SfrBC_final ; // vettore di PolyLines dalle quali construirò la superficie piana da svuotare POLYLINEVECTOR vPL ; // se si tratta di uno step extra e non devo svuotare tutto sul piano di pocketing... if ( bIsExtraStep && ! m_bPocketPlane) { // la superficie da svuotare è formata da tutte le facce del volume di svuotatura alla quota attuale for ( int f = 0 ; f < pStmVol->GetFacetCount() ; ++ f) { Point3d ptCentroid ; Vector3d vtNormal ; if ( ! pStmVol->GetFacetCenter( f, ptCentroid, vtNormal) || abs( ptCentroid.z - vtTrasl.z) > 100 * EPS_SMALL || // se faccia non allo step attuale ! AreOppositeVectorApprox( Z_AX, vtNormal)) // ... o con normale non coerente.... continue ; // memorizzo il contorno della faccia POLYLINEVECTOR vPL_f ; pStmVol->GetFacetLoops( f, vPL_f) ; if ( vPL_f.empty()) return false ; for ( int i = 0 ; i < int( vPL_f.size()) ; ++ i) vPL.push_back( vPL_f[i]) ; } } else { // 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 ; // vettore delle PolyLine ricavate dal taglio ( i Loops) if ( ! pStmSlice->GetLoops( vPL)) 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 ; // 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::CheckSideAngleForVolume( const ISurfTriMesh* pStmVol_Above, const ISurfTriMesh* pStm_Part, ISurfFlatRegion* pSfr_Projection, double& dExtraLenInOut, ISurfFlatRegion* pSfr, ISurfFlatRegion* pSfrExtension) { // NB. Questa funzione è chimata solo in caso ci sia uno Step e il tool non sia cilindrico // controllo dei parametri if ( pStmVol_Above == nullptr || ! pStmVol_Above->IsValid() || pStm_Part == nullptr || ! pStm_Part->IsValid()) return false ; // se non ho un volume da svuotare, allora non faccio nulla if ( ! pStmVol_Above->IsValid()) return true ; // chiudo il volume con il piano a Z minima ( quindi posso vedere semplicemente i Loop presenti) POLYLINEVECTOR vPL_tmp ; Point3d ptC_tmp ; Plane3d pl_noFace ; PtrOwner pStmVol( CloneSurfTriMesh( pStmVol_Above)) ; if ( IsNull( pStmVol) || ! pStmVol->IsValid() || ! pStmVol->GetLoops( vPL_tmp) || vPL_tmp.empty() || ! vPL_tmp[0].GetFirstPoint( ptC_tmp) || ! pl_noFace.Set( ptC_tmp, -Z_AX) || ! pl_noFace.IsValid() || ! SewingMissingFacesOnPlanes( pStmVol, pl_noFace)) return false ; // seleziono le facce a Z_minima con normale -Z_AX ( è un volume) INTVECTOR vnFaces ; Point3d ptC ; for ( int f = 0 ; f < pStmVol->GetFacetCount() ; ++ f) { Vector3d vtN ; if ( ! pStmVol->GetFacetCenter( f, ptC, vtN)) return false ; if ( AreSameVectorEpsilon( vtN, -Z_AX, 10 * EPS_SMALL) && abs( ptC.z - ptC_tmp.z) < 20 * EPS_SMALL) vnFaces.push_back( f) ; } if ( vnFaces.empty()) return false ; // controllo gli angoli che la faccia da svuotare con le facce Chiuse adiacenti // Se il SideAngle del tool è positivo, vuol dire che si stringe, in caso contrario si allarga // Se SideAngle > 0 -> proietto tutte le facce con angolo < SideAngle // Se SideAngle < 0 -> proietto tutte le facce con angolo < 180° + |SideAngle| ( ovvero 180° - SideAngle) // NB. Anche la lunghezza del Tool va ad influire... non è detto che un angolo ammissibile presenti // una faccia sufficientemente corta lungo Z da non intersecare il Tool ( per SideAngle < 0) // vettore dei triangoli da proiettare INTVECTOR vInd_TriaToProj ; // per ogni faccia da controllare for ( int f = 0 ; f < int( vnFaces.size()) ; ++ f) { // recupero il bordo della faccia POLYLINEVECTOR vPL ; pStmVol->GetFacetLoops( vnFaces[f], vPL) ; if ( vPL.empty()) return false ; // per ogni bordo che trovo... ( potrebbero esserci isole) for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { // creo la curva a partire da quello esterno PtrOwner pCrvCompo( CreateCurveComposite()) ; pCrvCompo->FromPolyLine( vPL[i]) ; if ( ! pCrvCompo->IsValid()) return false ; // ricavo il parametro sulla PolyLine coincidente all'indice della faccia adiacente double dPar ; bool bFound = vPL[i].GetFirstU( dPar, true) ; while ( bFound) { // recupero il flag int nFlag = int( dPar) ; // se non c'è nulla di adiacente, errore if ( nFlag == SVT_NULL) return false ; // ricavo il centroide di questa faccia Point3d ptC_Adj ; Vector3d vtN_Adj ; if ( ! pStmVol->GetFacetCenter( nFlag, ptC_Adj, vtN_Adj)) return false ; // controllo se si tratta di una faccia interna alla Part ( quindi Chiusa) DistPointSurfTm distPtStm( ptC_Adj, *pStm_Part) ; if ( distPtStm.IsPointInside()) { // è una faccia Chiusa, ricavo l'angolo bool bAdjac ; Point3d ptP1, ptP2 ; double dAng ; if ( ! pStmVol->GetFacetsContact( vnFaces[f], nFlag, bAdjac, ptP1, ptP2, dAng)) return false ; bool bProj = false ; // flag di proiezione della faccia // se la lunghezza del lato lungo Z supera quella del Tool, allora proietto BBox3d BBox ; if ( ! pStmVol->GetFacetBBox( nFlag, GLOB_FRM, BBox)) return false ; // se il SideAngle è negativo... if ( m_TParams.m_dSideAng < 0) { bProj = BBox.GetDimZ() > m_TParams.m_dLen ; if ( ! bProj) // se la lunghezza della faccia è valida, allora controllo la relazione con gli angoli bProj = dAng > ANG_RIGHT - m_TParams.m_dSideAng + 2500 * EPS_ANG_SMALL ; // determino di quanto devo uscire if ( ! bProj && dAng > ANG_RIGHT) if ( abs( m_TParams.m_dSideAng + ANG_RIGHT) > 100 * EPS_ANG_SMALL) dExtraLenInOut = max( dExtraLenInOut, BBox.GetDimZ() * tan((dAng - ANG_RIGHT) * DEGTORAD)) ; } // se il SideAngle è positivo... else if ( dAng > 0 && dAng > ANG_RIGHT - m_TParams.m_dSideAng - 2500 * EPS_ANG_SMALL) { bProj = dAng > ANG_RIGHT ; if ( ! bProj) { double dLenOut = BBox.GetDimZ() * tan(( ANG_RIGHT - dAng) * DEGTORAD) ; dExtraLenInOut = max( dExtraLenInOut, BBox.GetDimZ() * tan( m_TParams.m_dSideAng * DEGTORAD) - dLenOut) ; } else dExtraLenInOut = max( dExtraLenInOut, BBox.GetDimZ() * tan( m_TParams.m_dSideAng * DEGTORAD)) ; bProj = true ; } // se la faccia è da proiettare if ( bProj) { // recupero i triangoli della faccia INTVECTOR vT ; if ( ! pStmVol->GetAllTriaInFacet( nFlag, vT)) return false ; // aggiungo i loro indici al vettore generale dei triangoli da proeittare for ( int t = 0 ; t < int( vT.size()) ; ++ t) vInd_TriaToProj.push_back( vT[t]) ; } } // passo al successivo bFound = vPL[0].GetNextU( dPar, true) ; } } } // se tutti i triangoli non vanno proiettati, allora non faccio nulla if ( int( vInd_TriaToProj.size()) == 0) return true ; // creo una TriMesh con i triangoli da proiettare StmFromTriangleSoup StmSoup ; StmSoup.Start() ; for ( int t = 0 ; t < int( vInd_TriaToProj.size()) ; ++ t) { Triangle3dEx Tria ; if ( ! pStmVol->GetTriangle( vInd_TriaToProj[t], Tria) || ! StmSoup.AddTriangle( Tria)) return false ; } StmSoup.End() ; PtrOwner pStmToProj( StmSoup.GetSurf()) ; if ( IsNull( pStmToProj) || ! pStmToProj->IsValid() || ! pStmToProj->DoCompacting()) return false ; // proietto questa superficie sul piano Z = Z_min Plane3d plProj ; plProj.Set( ptC, Z_AX) ; if ( ! plProj.IsValid() || ! ProjectStmOnPlane( pStmToProj, plProj, pSfr_Projection)) return false ; // controllo ( per estrema sicurezza) la coerenza con le normali ( se la proiezione esiste) if ( pSfr_Projection->IsValid() && AreOppositeVectorApprox( Z_AX, pSfr_Projection->GetNormVersor())) pSfr_Projection->Invert() ; // in questo caso if ( m_TParams.m_dSideAng > 0 && dExtraLenInOut > 0) { // se la superficie di proeizione non è valida, allora le facce Chiuse sono perpendicolari al piano di // svuotatura ; if ( ! pSfr_Projection->IsValid()) { // ricavo la nuova superficie di proeizione if ( ! GetDangerSfrForSideStep( pSfr, pStm_Part, dExtraLenInOut, pSfr_Projection)) return false ; } // se invece la regione di proeizione è valida, la estendo della quantità richiesta else if ( ! pSfr_Projection->Offset( dExtraLenInOut, ICurve::OFF_FILLET)) return false ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::GetDangerSfrForSideStep( const ISurfFlatRegion* pSfr, const ISurfTriMesh* pStm_Part, const double dExtraLenInOut, ISurfFlatRegion* pSfr_Projection) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid() || pStm_Part == nullptr || ! pStm_Part->IsValid() || pSfr_Projection == nullptr) return false ; // se non ci sono problemi, allora non faccio nulla pSfr_Projection->Clear() ; if ( dExtraLenInOut < EPS_ANG_SMALL) return true ; // clono la supercicie da svuotare e ricavo i suoi lati aperti e chiusi PtrOwner pSfr_c( CloneSurfFlatRegion( pSfr)) ; if ( IsNull( pSfr_c) || ! pSfr_c->IsValid() || ! ChooseCloseOrOpenEdge( pSfr_c, pStm_Part)) return false ; // tengo solo in considerazioni i lati chiusi... for ( int c = 0 ; c < pSfr_c->GetChunkCount() ; ++ c) { for ( int l = 0 ; l < pSfr_c->GetLoopCount( c) ; ++ l) { // ricavo tutti i loop dalla regione PtrOwner pCrvLoop( ConvertCurveToComposite( pSfr_c->GetLoop( c, l))) ; if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid()) return false ; // spezzo il loop in tratti aperti e chiusi ICRVCOMPOPOVECTOR vCrv ; if ( ! GetHomogeneousParts( pCrvLoop, vCrv)) return false ; // tengo in considerazione solo i lati chiusi for ( int i = 0 ; i < int( vCrv.size()) ; ++ i) { if ( vCrv[i]->IsValid() && vCrv[i]->GetTempProp( 0) == 0) { PtrOwner pCrvClose( CloneCurveComposite( vCrv[i])) ; if ( IsNull( pCrvClose) || ! pCrvClose->IsValid()) return false ; // creo la FatRegion della quantità richista PtrOwner pSfrFat( GetSurfFlatRegionFromFatCurve( pCrvClose->Clone(), dExtraLenInOut, false, false)) ; if ( IsNull( pSfrFat) || ! pSfrFat->IsValid()) return false ; // aggiungo questa regione alla FlatRegion Danger da restitutire if ( ! pSfr_Projection->IsValid()) pSfr_Projection->CopyFrom( pSfrFat) ; else if ( ! pSfr_Projection->Add( *pSfrFat)) return false ; } } } } return pSfr_Projection->IsValid() && pSfr_Projection->GetChunkCount() > 0 ; } //---------------------------------------------------------------------------- bool Pocketing::ProjectVolume( const ISurfTriMesh* pStmVol, const ISurfTriMesh* pStm_Part, const Vector3d& vtTrasl, const Vector3d& vtTrasl1, ISurfFlatRegion* pSfrLimit, ISurfFlatRegion* pSfr, const bool bOneStep, double& dExtraLenInOut) { // controllo dei parametri if ( pStmVol == nullptr || ! pStmVol->IsValid() || pStm_Part == nullptr || ! pStm_Part->IsValid() || pSfr == nullptr) return false ; // se non ho una regione da svuotare, non faccio nulla // NB. Il volume da svuotare è definito dalla Part, mentre l'elevazione è calcolata sul grezzo // potrebbe capitare che che il piano di svuotura sia al di fuori della Part ma sempre dentro al grezzo // in questo caso non ho volume da svuotare, quindi non considero il seguente piano if ( ! pSfr->IsValid()) 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 Vector3d vtMaxTrasl = 5000 * Z_AX ; if ( ! vtTrasl1.IsValid()) { BBox3d BBox ; if ( ! pStmVol->GetLocalBBox( BBox)) return false ; vtMaxTrasl = vtN * ( BBox.GetDimZ() - 10 * EPS_SMALL) ; } Point3d ptFar = ORIG + ( vtTrasl1.IsValid() ? vtTrasl1 : vtMaxTrasl) ; 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 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) PtrOwner pSfrProjection( CreateSurfFlatRegion()) ; // superficie di proiezione if ( IsNull( pSfrProjection)) return false ; if ( m_TParams.m_dSideAng != 0 && bOneStep) { if ( ! CheckSideAngleForVolume( pStmAbove_Near, pStm_Part, pSfrProjection, dExtraLenInOut, pSfr, pSfr_ExtendProjection)) return false ; } else { 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 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 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 ( *pSfr) 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, const bool bOnIsClosed) { // controllo parametri : if ( pSfr == nullptr || ! pSfr->IsValid()) return true ; // <- se superficie non valida, allora non ho niente da impostare sui suoi lati if ( pStm == nullptr || ! pStm->IsValid()) return false ; bool bStmClosed = pStm->IsClosed() ; // per ogni curva dei Loop della FlatRegion vengono presi 4 punti di controllo equidistanti. // " IL LATO E' APERTO <=> TUTTI I PUNTI DI CONTROLLO NON SONO DENTRO AL FINITO " ( bInVsOnFace = T) // " IL LATO E' APERTO <=> TUTTI I PUNTI DI CONTROLLO DISTANO MENO DI 50 * EPS_SMALL DALLE FACCE ( bInVsOnFace = F) // 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) ; // resetto tutte le temp Prop ResetCurveAllTempProp( pCrvCompoLoop) ; // -> tutto chiuso ( 0) // 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 bIsOn = true ; for ( int p = 0 ; p < NUM_POINTS + 1 && bIsOn ; ++ p) { double dPar = ( 1. / ( 1. * NUM_POINTS)) * p ; Point3d ptPar ; if ( ! pCrv->GetPointD1D2( dPar, ICurve::FROM_PLUS, ptPar)) return false ; DistPointSurfTm distPtStm( ptPar, *pStm) ; if ( bStmClosed) bIsOn = ! distPtStm.IsPointInside() ; else { double dDist = 0. ; bIsOn = distPtStm.GetDist( dDist) && dDist < 50 * EPS_SMALL ; } } if (( bIsOn && ! bOnIsClosed) || ( ! bIsOn && bOnIsClosed)) pCrvCompoLoop->SetCurveTempProp( u, 1, 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 dLen, double& dElev) const { // inizializzo l'elevazione dElev = 0 ; // approssimo la curva con una polilinea che uso per creare il poligono equivalente PolyLine PL ; if ( ! pCompo->ApproxWithLines( LIN_TOL_RAW, ANG_TOL_MAX_DEG, ICurve::APL_SPECIAL, PL)) return false ; Polygon3d pgFacet ; if ( ! pgFacet.FromPolyLine( PL)) return false ; // aggiungo l'affondamento pgFacet.Translate( -dDepth * vtTool) ; // inizializzo elevazioni per ogni grezzo INTDBLVECTOR vRawElev ; // ciclo sui grezzi della fase int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, m_nPhase)) { // recupero la trimesh del grezzo int nStmId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; const ISurfTriMesh* pStm = GetSurfTriMesh( m_pGeomDB->GetGeoObj( nStmId)) ; if ( pStm != nullptr) { // recupero il riferimento della trimesh Frame3d frStm ; m_pGeomDB->GetGlobFrame( nStmId, frStm) ; // porto il poligono in questo riferimento Polygon3d pgFacetL = pgFacet ; pgFacetL.ToLoc( frStm) ; // calcolo l'elevazione double dCurrElev ; if ( ! PolygonElevationInClosedSurfTm( pgFacetL, *pStm, true, dCurrElev)) return false ; if ( dCurrElev > EPS_SMALL) vRawElev.emplace_back( nStmId, dCurrElev) ; } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } // se trovate elevazioni if ( ! vRawElev.empty()) { // ordino il vettore secondo l'elevazione crescente sort( vRawElev.begin(), vRawElev.end(), []( const INTDBL& a, const INTDBL& b) { return a.second < b.second ; }) ; // box dell'insieme delle posizioni utensile all'inizio const double MAX_DIST_RAW = 200.0 ; BBox3d b3Tool ; pgFacet.GetLocalBBox( b3Tool) ; b3Tool.Add( b3Tool.GetMin() + dLen * vtTool) ; b3Tool.Add( b3Tool.GetMax() + dLen * vtTool) ; if ( vtTool.IsX()) b3Tool.Expand( 0, dRad, dRad) ; else if ( vtTool.IsY()) b3Tool.Expand( dRad, 0, dRad) ; else if ( vtTool.IsZ()) b3Tool.Expand( dRad, dRad, 0) ; else { double dExpandX = dRad * sqrt( 1 - vtTool.x * vtTool.x) ; double dExpandY = dRad * sqrt( 1 - vtTool.y * vtTool.y) ; double dExpandZ = dRad * sqrt( 1 - vtTool.z * vtTool.z) ; b3Tool.Expand( dExpandX, dExpandY, dExpandZ) ; } b3Tool.Expand( MAX_DIST_RAW) ; // verifico la reale interferenza dell'utensile con i diversi grezzi for ( int i = 0 ; i < int( vRawElev.size()) ; ++ i) { // box del grezzo BBox3d b3Raw ; m_pGeomDB->GetGlobalBBox( vRawElev[i].first, b3Raw) ; // confronto con il box dell'utensile nella posizione precedente BBox3d b3CurrTool = b3Tool ; b3CurrTool.Translate( dElev * vtTool) ; if ( b3Raw.Overlaps( b3CurrTool)) dElev = vRawElev[i].second ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::VerifyPathFromBottom( const ICurveComposite* pCompo, 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 ; 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 ISurfTriMesh* pStm_PartVolume) { // creo copia della part del volume di svuotatura PtrOwner pStm( CloneSurfTriMesh( pStm_PartVolume)) ; if ( IsNull( pStm)) return false ; // inserisco il volume nel DB int nRId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nPathId, Release( pStm)) ; 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 le isole si sono mergiate, ricavolo il nuovo punto iniziale for ( int x = 0 ; x < int( vCrvIslMergeBorders.size()) ; ++ x) { // se richisto, inverto if ( m_Params.m_bInvert) vCrvIslMergeBorders[x]->Invert() ; // cambio il punto inziale alla curva di bordo if ( vCrvIslMergeBorders[x]->IsClosed()) { bool bOutTmp = false ; Point3d ptEnd_prec = P_INVALID ; if ( ! SetBetterPtStartForSubChunks( vCrvIslMergeBorders[x], pSrfChunk, ptEnd_prec, frPocket, 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) { // ricavo il loop PtrOwner pCrvLoop( ConvertCurveToComposite( pSrfForCrv->GetLoop( cfc, l))) ; if ( IsNull( pCrvLoop)) return false ; // assegno il punto di inizio bool bOutTmp = false ; Point3d ptEndPrec = P_INVALID ; if ( ! SetBetterPtStartForSubChunks( pCrvLoop, pSrfChunk, ptEndPrec, frPocket, 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() ; // 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( pCrvCurr, ptP1, ptStart, vtStart, V_INVALID, 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 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 ; if ( j > 1) { ptP1.Translate( - vtTool * ( vVtTrasl[j-1] - vVtTrasl[j-2]).Len()) ; dStElev += ( vVtTrasl[j-1] - vVtTrasl[j-2]).Len() ; } } // 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( pCrvCurr, ptP1, ptStart, vtStart, V_INVALID, 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 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) ; 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, P_INVALID, frLocI, 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() ; // 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 ; if ( j > 1) { ptP1.Translate( - vtTool * ( vVtTrasl[j-1] - vVtTrasl[j-2]).Len()) ; dStElev += ( vVtTrasl[j-1] - vVtTrasl[j-2]).Len() ; } } // 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( pOffs, ptP1, ptStart, vtStart, V_INVALID, 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 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()) ; // 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( pCrvCompo, ptP, ptS, frLocI.VersX(), V_INVALID, 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 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::AdjustTrapezoidSpiralLeadInLeadOutForSideAngle( ICurveComposite* pMCrv, const double dExtraLenInOut, const int nOutsideRaw, const Vector3d& vtN) { int nTmpProp = pMCrv->GetTempProp( 0) ; double dExtension = 0. ; if ( GetLeadInType() == POCKET_LI_HELIX) dExtension = min( 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam), m_dMaxHelixRad) ; else if ( GetLeadInType() == POCKET_LI_ZIGZAG) dExtension = 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam) ; else if ( GetLeadInType() == POCKET_LI_GLIDE) dExtension = m_Params.m_dLiTang ; dExtension += dExtraLenInOut + m_TParams.m_dDiam * 0.5 ; if ( m_TParams.m_dSideAng < 0) { // se tutto chiuso if ( nTmpProp == 0) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } // se un lato aperto else if ( nTmpProp & 1 || nTmpProp & 4 || nTmpProp == 10) { // almeno 1 base aperta o entrambi i lati obliqui aperti if ( nOutsideRaw == 2) return true ; if ( nOutsideRaw == 0) { if ( pMCrv->GetCurve( 0)->GetTempProp( 0) != 1 && nTmpProp < 8) { Vector3d vtS ; pMCrv->GetStartDir( vtS) ; Point3d ptS ; pMCrv->GetStartPoint( ptS) ; vtS.Rotate( vtN, ( nTmpProp & 1) && m_Params.m_bInvert ? - ANG_RIGHT : ANG_RIGHT) ; vtS *= dExtension ; pMCrv->AddLine( ptS + vtS, false) ; } else pMCrv->ExtendStartByLen( dExtension) ; } if ( pMCrv->GetLastCurve()->GetTempProp( 0) != 1 && ( nTmpProp & 2) == 0) { Vector3d vtE ; pMCrv->GetEndDir( vtE) ; Point3d ptE ; pMCrv->GetEndPoint( ptE) ; vtE.Rotate( vtN, ( nTmpProp & 1) && m_Params.m_bInvert ? - ANG_RIGHT : ANG_RIGHT) ; vtE *= dExtension ; pMCrv->AddLine( ptE + vtE, true) ; } else pMCrv->ExtendEndByLen( dExtension) ; return true ; } else if ( nTmpProp == 2 || nTmpProp == 8) { // 1 lato obliquo aperto e tutti gli altri chiusi if ( nTmpProp == 2 && nOutsideRaw == 0) pMCrv->Invert() ; pMCrv->Close() ; if ( nOutsideRaw == 0) { pMCrv->ExtendStartByLen( dExtension) ; pMCrv->ExtendEndByLen( dExtension) ; } return true ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::AddSpiralIn( ISURFFRPOVECTOR& vSfr, const vector& vCrvOrig, BOOLVECTOR& vbChangedPrec, VCT3DVECTOR& vVtTrasl, ISURFFRPOVECTOR& vSrfLimit, const Vector3d& vtTool, const Vector3d& vtExtr, const double dDepth, const double dElev, const double dMaxElev, const double dOkStep, const bool bSplitArcs, const double dExtraLenInOut) { // 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()) ; // determino lo step classico nel caso di errori ( senza considerare gli step extra) double dStep = dElev / nStep ; // LeadIn/leadout Point3d ptP1 ; struct SpiralData { BOOLVECTOR vbMidOut ; // flags per effettiva entrata precedente da fuori dal grezzo ICRVCOMPOPOVECTOR vMCrv ; // vettore delle curve dei percorsi di svuotatura dello step precedente ICRVCOMPOPOVECTOR vRCrv ; // vettore delle curve dei percorsi di ritorno dello step precedente vector vvtMidOut ; // vettore dei versori delle direzioni di uscita dello step precedente INTVECTOR vnRegTot ; // vettore dei numeri delle regioni formate dal primo Offset nello step precedente BOOLVECTOR vbOptTrap ; // vettore di flag per casi ottimizzati Point3d ptEndPath ; // punto finale del percorso di svuotatura attuale } ; SpiralData SDStepPrec ; // informazioni Spiral attuali int nOffs_act = 0 ; // Shift indice dei vettori causato da nReg // scorro il vettore delle FlatRegion ( calcolate per ogni step j-esimo) for ( int j = 1 ; j <= nStep ; ++ j) { SpiralData SDStepCurr ; // informazioni Spiral attuali if ( ! vbChangedPrec[j-1]) { for ( int i = 0 ; i < int( SDStepPrec.vMCrv.size()) ; ++ i) SDStepCurr.vMCrv.emplace_back( SDStepPrec.vMCrv[i]->Clone()) ; SDStepCurr.vbMidOut = SDStepPrec.vbMidOut ; SDStepCurr.vnRegTot = SDStepPrec.vnRegTot ; SDStepCurr.vvtMidOut = SDStepPrec.vvtMidOut ; SDStepCurr.vbOptTrap = SDStepPrec.vbOptTrap ; SDStepCurr.ptEndPath = SDStepPrec.ptEndPath ; } // se superficie non valida, passo alla prossima if ( IsNull( vSfr[j-1]) || ! vSfr[j-1]->IsValid()) continue ; // superifice per ingressi ed uscite PtrOwner pSrfLeanInOut( CloneSurfFlatRegion( vSfr[j-1])) ; if ( IsNull( pSrfLeanInOut)) return false ; // punto finale dello step precedente Point3d ptEndPrec = ( j == 1 ? P_INVALID : SDStepPrec.ptEndPath) ; // creo le entità per lo step corrente ICRVCOMPOPOVECTOR vMCrv, vRCrv ; BOOLVECTOR vbOut ; INTVECTOR vnRegTot ; // ciclo sui chunk della superficie 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 ; 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) { // 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( SDStepPrec.vMCrv.size()) - 1) break ; pMCrv.Set( SDStepPrec.vMCrv[nOffs_act]->Clone()) ; pRCrv.Set( SDStepPrec.vRCrv[nOffs_act]->Clone()) ; nRegTot = SDStepPrec.vnRegTot[nOffs_act] ; bMidOut = SDStepPrec.vbMidOut[nOffs_act] ; vtMidOut = SDStepPrec.vvtMidOut[nOffs_act] ; bOptimizedTrap = SDStepPrec.vbOptTrap[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, ptEndPrec, 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 SDStepCurr.vMCrv.emplace_back( pMCrv->Clone()) ; SDStepCurr.vRCrv.emplace_back( pRCrv->Clone()) ; // memorizzo i Flag per le entrate SDStepCurr.vbMidOut.push_back( bMidOut) ; // memorizzo il numero delle regioni create dal primo Offset SDStepCurr.vnRegTot.push_back( nRegTot) ; // memorizzo il versore di uscita calcolato SDStepCurr.vvtMidOut.push_back( vtMidOut) ; // memorizzo caso ottimizzato SDStepCurr.vbOptTrap.push_back( bOptimizedTrap) ; // sovrascrivo sempre il punto finale ( lo aggiorno in caso di più chunks) Point3d ptEnd ; pMCrv->GetEndPoint( ptEnd) ; SDStepCurr.ptEndPath = ptEnd ; } // 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)) ; 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) ; } // aggiungo un tratto lineare all'inizio del percorso di svuotatura pMCrv->AddLine( ptOut, false) ; // aggiungo un tratto lineare alla fine del percorso di svuotatura //pRCrv->AddLine( ptOut, true) ; } } // se non ho un'entrata da fuori e il tool non è cilindrico... if ( ! ( bOutStart || m_bOpenOutRaw) && ! bOptimizedTrap && m_TParams.m_dSideAng < 0 && dExtraLenInOut > EPS_SMALL) { Vector3d vtDir ; Point3d ptS ; Vector3d vtIn ; double dLenCheck = 0.; // aggiungo al percorso un tratto interno if ( pMCrv->GetCurveCount() > 0) { pMCrv->GetStartDir( vtDir) ; pMCrv->GetStartPoint( ptS) ; vtIn = vtDir ; vtIn.Rotate( vtTool, m_Params.m_bInvert ? -ANG_RIGHT : ANG_RIGHT) ; if ( GetLeadInType() == POCKET_LI_HELIX) dLenCheck = min( 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam), m_dMaxHelixRad) ; else if ( GetLeadInType() == POCKET_LI_ZIGZAG) dLenCheck = 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam) ; else if ( GetLeadInType() == POCKET_LI_GLIDE) dLenCheck = m_Params.m_dLiTang ; pMCrv->AddLine( ptS + ( dExtraLenInOut + dLenCheck) * vtIn, false) ; // aggiorno la superficie limite di controllo PtrOwner pNewSfrLeadInOut( CloneSurfFlatRegion( vSfr[j-1])) ; if ( IsNull( pNewSfrLeadInOut) || ! pNewSfrLeadInOut->Offset( - dExtraLenInOut, ICurve::OFF_CHAMFER) || ! pNewSfrLeadInOut->IsValid() || ! pSrfLeanInOut.Set( Release( pNewSfrLeadInOut))) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } // se ho un solo Offset dalla svuotatura, devo controllare anche l'uscita... Point3d ptE ; pMCrv->GetEndPoint( ptE) ; if ( AreSamePointEpsilon( ptS, ptE, 500 * EPS_SMALL)) pMCrv->AddLine( ptS + ( dExtraLenInOut + dLenCheck) * vtIn, true) ; } } // calcolo gli eventuali punti fuori dal grezzo nel caso ottimizzato int nOutsideRaw = 0 ; if ( bOptimizedTrap) { AdjustTrapezoidSpiralForLeadInLeadOut( pMCrv, pRCrv, vtTool, dDepth, nOutsideRaw) ; bOutStart = ( nOutsideRaw > 0) ; // controllo geometria del tool if ( m_TParams.m_dSideAng != 0) { if ( ! AdjustTrapezoidSpiralLeadInLeadOutForSideAngle( pMCrv, dExtraLenInOut, nOutsideRaw, vtTool)) return false ; } // aggiorno la curva per eventuali inversioni SDStepCurr.vMCrv.back().Set( pMCrv->Clone()) ; } // 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 (( j > 1 && vSfr[j-1]->GetChunkCount() == 1 && int( SDStepPrec.vMCrv.size()) == 1 && bOptimizedTrap && SDStepPrec.vbOptTrap[0])) { Point3d ptA, ptB, ptC ; pMCrv->GetStartPoint( ptA) ; SDStepPrec.vMCrv[0]->GetStartPoint( ptB) ; SDStepPrec.vMCrv[0]->GetEndPoint( ptC) ; ptA.Translate( vVtTrasl[j-2] - vVtTrasl[j-1]) ; bool bOpposite = ( Dist( ptA, ptB) > Dist( ptA, ptC) + EPS_SMALL) ; switch ( nOutsideRaw) { case 0 : case 2 : if ( ! bOpposite) { pMCrv->Invert() ; SDStepCurr.vMCrv[0]->Invert() ; } break ; case 1 : if ( bOpposite) { pMCrv->Invert() ; SDStepCurr.vMCrv[0]->Invert() ; } break ; } } ++ nReg ; // incremento del numero di regioni trovate ++ nOffs_act ; // shift indice // salvo i dati Spiral correnti vMCrv.emplace_back( Release( pMCrv)) ; vRCrv.emplace_back( Release( pRCrv)) ; vbOut.push_back( bOutStart || bForcedOutStart) ; vnRegTot.push_back( nRegTot) ; } } // per ogni percorso trovato for ( int i = 0 ; i < int( vMCrv.size()) ; ++ i) { // recupero l'indice massimo delle sottocurve int nMaxInd = vMCrv[i]->GetCurveCount() - 1 ; // ciclo sulle curve elementari for ( int u = 0 ; u <= nMaxInd ; ++ u) { // curva corrente const ICurve* pCrvC = vMCrv[i]->GetCurve( u) ; // copio la curva PtrOwner pCurve( pCrvC->Clone()) ; if ( IsNull( pCurve)) return false ; // se prima curva del percorso if ( u == 0) { // ricavo il punto inziale Point3d ptStart ; pCurve->GetStartPoint( ptStart) ; // vettore tangente iniziale Vector3d vtStart ; pCurve->GetStartDir( vtStart) ; // se primo Step e primo percorso, allora semplice LeadIn... if ( j == 1 && i == 0) { // inizio leadIn if ( ! CalcLeadInStart( ptStart, vtStart, vtExtr, vRCrv[i], ptP1)) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } // determino elevazione su inizio attacco ( se non trovata, l'elevazione è nStep) double dStElev ; if ( ! GetElevation( m_nPhase, ptStart - 10 * EPS_SMALL * vtTool, vtTool, GetRadiusForStartEndElevation(), vtTool, dStElev)) dStElev = dStep ; bool bAhUnderRaw = m_bAboveHead && ! m_bAggrBottom && ! m_bTiltingTab && GetAhPointUnderRaw( ptP1, vtTool, 0, GetRadiusForStartEndElevation(), m_TParams.m_dLen, false, dSafeZ, vtTool, dStElev) ; bool bUhAboveRaw = ! m_bAboveHead && GetUhPointAboveRaw( ptP1, vtTool, 0, GetRadiusForStartEndElevation(), m_TParams.m_dLen, false, dSafeZ, vtTool, dStElev) ; if ( bAhUnderRaw || bUhAboveRaw || m_bTiltingTab) dStElev = max( dStElev, dStep) ; 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, vbOut[i])) { m_pMchMgr->SetLastError( 2414, "Error in Pocketing : Approach not computable") ; return false ; } // aggiungo attacco SetFeed( GetStartFeed()) ; if ( ! AddLeadIn( vMCrv[i], ptP1, ptStart, vtStart, V_INVALID, vtExtr, pSrfLeanInOut, vRCrv[i], !m_Params.m_bInvert, bSplitArcs, vbOut[i])) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } } // ... se devo aggiungere l'uscita del percorso precedente e calcolare l'entrata // per il percorso attuale else { PtrOwner pRCrv( CreateCurveComposite()) ; // ricavo il punto in cui sono Point3d ptCurr ; GetCurrPos( ptCurr) ; // ricavo il punto che devo raggiungere Point3d ptDest ; vMCrv[i]->GetStartPoint( ptDest) ; // modifico il punto di destinazione a seconda del tipo di attacco per tangenza Vector3d vtEnd = vtStart ; if ( ! vbOut[i]) { if ( ! SetLeadInPointStart( ptCurr, vtTool, !m_Params.m_bInvert, vtStart, ptDest)) return false ; } // calcolo il percorso di ritorno del percorso attuale if ( IsNull( pRCrv) || ! CalcLinkOnStep( ptCurr, ptDest, vSfr[j-2], ! SDStepCurr.vbOptTrap[i] ? vSrfLimit[j-2] : CreateSurfFlatRegion(), pRCrv)) { m_pMchMgr->SetLastError( 2441, "Error in Pocketing : Return path not computable") ; return false ; } // emetto SetFeed( GetEndFeed()) ; if ( pRCrv->GetCurveCount() > 0 && AddCurveMove( pRCrv) == GDB_ID_NULL) return false ; // ricavo la posizione attuale Point3d ptAbove ; GetCurrPos( ptAbove) ; // aggiungo attacco ( solo collegamento ) SetFeed( GetStartFeed()) ; if ( ! AddLeadIn( vMCrv[i], ptAbove, ptStart, vtStart, vtEnd, vtExtr, pSrfLeanInOut, vRCrv[i], !m_Params.m_bInvert, bSplitArcs, vbOut[i])) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } } } // elaborazioni sulla curva corrente 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à dell'ultimo percorso if ( u == nMaxInd && j == int( vSfr.size()) && i == int( vMCrv.size()) - 1) { // dati fine entità Point3d ptEnd ; pCurve->GetEndPoint( ptEnd) ; Vector3d vtEnd ; pCurve->GetEndDir( vtEnd) ; // aggiungo uscita, LeadOut Point3d ptP1 ; double dEndElev = dElev ; SetFeed( GetEndFeed()) ; if ( ! AddLeadOut( ptEnd, vtEnd, vtExtr, vRCrv[i], 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 ; } } } } // aggiorno i dati Spiral se regioni cambiate tra step SDStepPrec.vMCrv.clear() ; for ( int i = 0 ; i < int( SDStepCurr.vMCrv.size()) ; ++ i) SDStepPrec.vMCrv.emplace_back( Release( SDStepCurr.vMCrv[i])) ; SDStepPrec.vbMidOut = SDStepCurr.vbMidOut ; SDStepPrec.vnRegTot = SDStepCurr.vnRegTot ; SDStepPrec.vvtMidOut = SDStepCurr.vvtMidOut ; SDStepPrec.vbOptTrap = SDStepCurr.vbOptTrap ; SDStepPrec.ptEndPath = SDStepCurr.ptEndPath ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::SetLeadInPointStart( const Point3d& ptCurr, const Vector3d vtN, const bool& bAtLeft, Vector3d& vtStart, Point3d& ptDest) { /* ptCurr -> punto iniziale in cui si trova l'utensile ( e' il punto finale del percoso di svuotatura allo step attuale) vtN -> versore direzione del tool bAtLeft -> flag per indicare se il percorso è invertito o meno vtStart -> versore direzione iniziale del successivo percorso di svuotatura ( questo versore viene modificato per il collegamento in tangenza ) ptDest -> punto tangente all'elica */ switch ( GetLeadInType()) { case POCKET_LI_HELIX : { // raggio dell'elica double dRad = min( 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam), m_dMaxHelixRad) ; if ( dRad < - EPS_SMALL) return false ; // porto tutto nel piano XY Frame3d frXY ; frXY.Set( ptCurr, vtN) ; // ricavo il centro dell'elica Vector3d vtCen = vtStart ; vtCen.Rotate( vtN, 0, ( bAtLeft ? 1 : - 1)) ; Point3d ptCen = ptDest + vtCen * dRad ; // porto il centro nel frame locale e traslo allo step corrente ptCen.ToLoc( frXY) ; ptCen.z = 0 ; // calcolo il vettore diretto verso ptCen Vector3d vtOC = ptCen - ORIG ; // lunghezza di tale vettore double dDist = vtOC.Len() ; // rendo tale vettore un versore vtOC.Normalize() ; // se la distanza è inferiore o uguale al raggio ritorno if ( dDist < dRad + EPS_SMALL) return true ; // lunghezza delle due tangenti double dLen = sqrt( dDist * dDist - dRad * dRad) ; if ( dLen < 100 * EPS_SMALL) return true ; // ricavo l'angolo in base al triangolo rettangolo creato double dAngRot = acos( dLen / dDist) * RADTODEG ; // vettore direzione per tangente destra Vector3d vtDirR = vtOC ; vtDirR.Rotate( Z_AX, dAngRot) ; // vettore direzione per tangente sinistra Vector3d vtDirL = vtOC ; vtDirL.Rotate( Z_AX, - dAngRot) ; // punto tangenza a destra Point3d ptTR = ORIG + vtDirL * dLen ; // punto tangenza a sinistra Point3d ptTL = ORIG + vtDirR * dLen ; // devo scegliere il punto che segue in direzione tangente l'arco ptDest = ptTL ; if ( bAtLeft) ptDest = ptTR ; // aggiorno il nuovo vettore direzione iniziale e il nuovo punto di destinazione vtStart = ptDest - ORIG ; vtStart.ToGlob( frXY) ; vtStart.Normalize() ; ptDest.ToGlob( frXY) ; } break ; default: break ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::CalcLinkOnStep( const Point3d& ptS, const Point3d& ptE, const ISurfFlatRegion* pSfr, const ISurfFlatRegion* pSfrLimit, ICurveComposite* pCrvStepLink) { // ptS -> punto finale del percorso attuale di pocketing // ptE -> punto iniziale del successivo percorso di pocketing // pSfr -> superficie da svuotare // pCrvStepLink -> curva di collegamento tra Chunks o Steps diversi // controllo dei parametri if ( ! ptS.IsValid() || ! ptE.IsValid() || pSfr == nullptr || ! pSfr->IsValid() || pCrvStepLink == nullptr) return false ; pCrvStepLink->Clear() ; // creo un sistema di riferimento centrato nella regione Frame3d frCurr ; frCurr.Set( ptS, pSfr->GetNormVersor()) ; if ( ! frCurr.IsValid()) return false ; // ricavo il punto finale proiettato sul piano Z = 0 ( quello di svuotatura attuale) Point3d ptEProj = ptE ; ptEProj.ToLoc( frCurr) ; ptEProj.z = 0 ; // Se il punto proiettato coincide con l'ORIG ( ptS) allora ho già finito ( il collegamento non serve) if ( AreSamePointEpsilon( ORIG, ptEProj, 200 * EPS_SMALL)) return true ; // creo un Segmento tra l'ORIG ( ptS) e la proiezione del punto finale PtrOwner pSeg( CreateCurveLine()) ; if ( IsNull( pSeg) || ! pSeg->Set( ORIG, ptEProj) || ! pSeg->IsValid()) return false ; // controllo se tale tratto lineare è ammissibile, potrei uscire presso un lato aperto // o attraversare la regione di svuotatura in parti che non vanno svuotate ( ad esempio isole o // rientranze di contorni chiusi ) -> controllo la superficie limite ( offsettata del raggio del tool) bool bIsAllSecure = ! pSfrLimit->IsValid() ; CRVCVECTOR ccClass ; PtrOwner pSfrDanger( pSfrLimit->CreateOffsetSurf( 0.5 * m_TParams.m_dDiam - 10 * EPS_SMALL, ICurve::OFF_FILLET)) ; if ( ! bIsAllSecure && ( IsNull( pSfrDanger) || ! pSfrDanger->IsValid() || ! pSfrDanger->ToLoc( frCurr) || ! pSfrDanger->GetCurveClassification( *pSeg, EPS_SMALL, ccClass) || ccClass.empty() || ! pSfrDanger->ToGlob( frCurr))) return false ; if ( bIsAllSecure || ( int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT)) return pCrvStepLink->AddPoint( ORIG) && pCrvStepLink->AddLine( ORIG + SAFE_Z_RET * Z_AX) && pCrvStepLink->AddLine( ptEProj + SAFE_Z_RET * Z_AX) && pCrvStepLink->IsValid() && pCrvStepLink->ToGlob( frCurr) ; // in questo caso sto rovinando delle parti di grezzo : le scelte ora sono 2 // 1) cerco un percorso ammissibile con i bisettori ( MedialAxis da Voroni ) // 2) Scarico // 1) ricavo i bisettori dell'Offset interno del raggio della regione da svuotare ( ed eventuale offset radiale) // ( I Bisettori sono quindi accorciati presso i loro estremi ) ICURVEPOVECTOR vCrvBisectors ; PtrOwner pSfrSafe( pSfr->CreateOffsetSurf( - 0.5 * m_TParams.m_dDiam - GetOffsR() + 5 * EPS_SMALL, ICurve::OFF_FILLET)) ; if ( IsNull( pSfrSafe) || ! pSfrSafe->IsValid() || ! pSfrSafe->CalcMedialAxis( vCrvBisectors, WMAT_IN) || vCrvBisectors.empty()) return false ; // porto i bisettori nel riferimento e rimuovo quelli con un parametro a zero ICURVEPOVECTOR vCrvBisectors_tmp ; for ( int i = 0 ; i < ( int)vCrvBisectors.size() ; ++ i) { // tolgo tutti i Bisettori che presentano un parametro nullo if ( vCrvBisectors[i]->GetTempParam( 0) > EPS_SMALL && vCrvBisectors[i]->GetTempParam( 1) > EPS_SMALL) { vCrvBisectors[i]->ToLoc( frCurr) ; vCrvBisectors_tmp.emplace_back( Release( vCrvBisectors[i])) ; } } swap( vCrvBisectors, vCrvBisectors_tmp) ; vCrvBisectors_tmp.clear() ; // trasformo in composite concatenando le curve ( bloccandomi alle biforcazioni) ICRVCOMPOPOVECTOR vCompoBisChain ; if ( ! ChainBisectors( vCrvBisectors, vCompoBisChain)) return false ; // pulisco vCrvBisectors.clear() ; // calcolo il percorso ottimale ( Dijkstra) PtrOwner pCrvPath( CreateCurveComposite()) ; if ( IsNull( pCrvPath)) return false ; ChooseBestBisectorPath( vCompoBisChain, ORIG, ptEProj, pCrvPath) ; double dMAxElev = 0. ; // controllo che la curva si effettivamente valida e distante dal percorso, altrimenti scarico bool bSkip = ! pCrvPath->IsValid() || pCrvPath->GetCurveCount() == 0 ; // se non valida, privilegio lo scarico if ( bSkip) { Point3d ptS = ORIG ; ptS.ToGlob( frCurr) ; Point3d ptE = ptEProj ; ptE.ToGlob( frCurr) ; if ( ! GetElevation( m_nPhase, ptS, ptE, pSfr->GetNormVersor(), m_TParams.m_dDiam, m_TParams.m_dLen, pSfr->GetNormVersor(), dMAxElev)) return false ; } else { // calcolo i tratti lineari di raccordo tra il percorso trovato e i punti iniziali e finali effettivi Point3d ptS_path, ptE_path ; if ( ! pCrvPath->GetStartPoint( ptS_path) || ! pCrvPath->GetEndPoint( ptE_path)) return false ; if ( ! AreSamePointEpsilon( ptS_path, ORIG, 5 * EPS_SMALL)) { PtrOwner pSegStart( CreateCurveLine()) ; if ( IsNull( pSegStart) || ! pSegStart->Set( ORIG, ptS_path) || ! pSegStart->IsValid() || ! pSegStart->ToGlob( frCurr) || ! GetSpecialElevation( pSfrDanger, pSegStart, true, dMAxElev)) return false ; bSkip = ( dMAxElev > EPS_SMALL) ; } if ( ! bSkip) { if ( ! AreSamePointEpsilon( ptE_path, ptEProj, 5 * EPS_SMALL)) { PtrOwner pSegEnd( CreateCurveLine()) ; if ( IsNull( pSegEnd) || ! pSegEnd->Set( ptEProj, ptE_path) || ! pSegEnd->IsValid() || ! pSegEnd->ToGlob( frCurr) || ! GetSpecialElevation( pSfrDanger, pSegEnd, true, dMAxElev)) return false ; bSkip = ( dMAxElev > EPS_SMALL) ; } } } // se la curva non è valida, ovvero non è sufficientemente lontana dal bordo della regione Safe o i raccordi // lineari rovinano il grezzo, scarico if ( bSkip) return pCrvStepLink->AddPoint( ORIG) && pCrvStepLink->AddLine( ORIG + ( SAFE_Z_RET + dMAxElev) * Z_AX) && pCrvStepLink->AddLine( ptEProj + ( SAFE_Z_RET + dMAxElev) * Z_AX) && ( dMAxElev - SAFE_Z_RET > 0 ? pCrvStepLink->AddLine( ptEProj + SAFE_Z_RET * Z_AX) : true) && pCrvStepLink->IsValid() && pCrvStepLink->ToGlob( frCurr) ; // ricavo il parametro massimo sui bisettori da VRONI double dMinPar = INFINITO ; for ( int i = 1 ; i < pCrvPath->GetCurveCount() - 1; ++ i) { double dParS = pCrvPath->GetCurve( i)->GetTempParam( 0) ; if ( dParS < dMinPar) dMinPar = dParS ; } // approssimo il percorso PolyLine PL ; pCrvPath->ApproxWithLines( dMinPar, EPS_ANG_SMALL, ICurve::APL_STD, PL) ; if ( PL.GetLineNbr() > 0) { PtrOwner pCrvPathTmp( CreateCurveComposite()) ; if ( ! IsNull( pCrvPathTmp) && pCrvPathTmp->FromPolyLine( PL)) pCrvPath.Set( Release( pCrvPathTmp)) ; } // smusso mediante parametro di VRONI ModifyCurveToSmoothed( pCrvPath, dMinPar, dMinPar, false) ; // aggiungo un tratto lineare per avere il percorso completo pCrvPath->AddLine( ORIG, false) ; pCrvPath->AddLine( ptEProj, true) ; // smusso di poco i nuovi raccordi ModifyCurveToSmoothed( pCrvPath, m_TParams.m_dDiam / 16, m_TParams.m_dDiam / 16, false) ; // calcolo la sua lunghezza double dCurrLen ; if ( ! pCrvPath->GetLength( dCurrLen)) return false ; // 2) valuto se invece è più conveniente scaricare ( quindi alzandomi dell'elevazione sopra alla linea) // la lunghezza del tratto in salita e in discesa è dMaxElev // la lunghezza del tratto orizzontale è data dalla distanza tra ORIG e ptEProj if ( ! pSeg->ToGlob( frCurr) || ! GetSpecialElevation( pSfrDanger, pSeg, false, dMAxElev)) return false ; if ( dCurrLen < 2 * dMAxElev + Dist( ORIG, ptEProj)) // privilegio il percorso return pCrvStepLink->AddPoint( ORIG) && pCrvStepLink->AddLine( ORIG + SAFE_Z_RET * Z_AX) && pCrvPath->Translate( SAFE_Z_RET * Z_AX) && pCrvStepLink->AddCurve( Release( pCrvPath)) && pCrvStepLink->IsValid() && pCrvStepLink->ToGlob( frCurr) ; // privilegio lo scarico return pCrvStepLink->AddPoint( ORIG) && pCrvStepLink->AddLine( ORIG + ( SAFE_Z_RET + dMAxElev) * Z_AX) && pCrvStepLink->AddLine( ptEProj + ( SAFE_Z_RET + dMAxElev) * Z_AX) && ( dMAxElev - SAFE_Z_RET > 0 ? pCrvStepLink->AddLine( ptEProj + SAFE_Z_RET * Z_AX) : true) && pCrvStepLink->IsValid() && pCrvStepLink->ToGlob( frCurr) ; } //---------------------------------------------------------------------------- bool Pocketing::GetSpecialElevation( const ISurfFlatRegion* pSfr, const ICurveLine* pLine, const bool bInVsOut, double& dElev) { // calcolo dell'elevazione sopra alle parti di pLine che si trovano all'interno dalla pSfr // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid() || pLine == nullptr || ! pLine->IsValid()) return false ; dElev = 0. ; // creo il frame per classificare le curve nel piano XY Frame3d frCurr ; Point3d ptC ; pLine->GetStartPoint( ptC) ; frCurr.Set( ptC, pSfr->GetNormVersor()) ; if ( ! frCurr.IsValid()) return false ; // classificazione CRVCVECTOR ccClass ; PtrOwner pSfrCurr( CloneSurfFlatRegion( pSfr)) ; PtrOwner pSeg( CloneCurveLine( pLine)) ; if ( IsNull( pSfrCurr) || ! pSfrCurr->IsValid() || IsNull( pSeg) || ! pSeg->IsValid() || ! pSfrCurr->ToLoc( frCurr) || ! pSeg->ToLoc( frCurr) || ! pSfrCurr->GetCurveClassification( *pSeg, EPS_SMALL, ccClass)) return false ; // per ogni tratto non esterno, calcolo la sua elevazione for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) { if (( bInVsOut && ccClass[i].nClass != CRVC_OUT) || ( ! bInVsOut && ccClass[i].nClass != CRVC_IN)) { Point3d ptS_i, ptE_i ; if ( ! pSeg->GetPointD1D2( ccClass[i].dParS, ICurve::FROM_MINUS, ptS_i) || ! pSeg->GetPointD1D2( ccClass[i].dParE, ICurve::FROM_PLUS, ptE_i)) return false ; double dCurrElev = 0. ; ptS_i.ToGlob( frCurr) ; // in globale ptE_i.ToGlob( frCurr) ; // in globale if ( ! GetElevation( m_nPhase, ptS_i, ptE_i, pSfr->GetNormVersor(), 1.16 * m_TParams.m_dDiam * 0.5, m_TParams.m_dLen, pSfr->GetNormVersor(), dCurrElev)) return false ; dElev = max( dCurrElev, dElev) ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::ChooseBestBisectorPath( ICRVCOMPOPOVECTOR& vCrvCompo, const Point3d& ptS, const Point3d& ptE, ICurveComposite* pCrvPath) { // controllo dei parametri if ( vCrvCompo.empty() || ! ptS.IsValid() || ! ptE.IsValid()) return false ; pCrvPath->Clear() ; // cerco gli indici delle curve più vicine a ptS e ptE double dMinDistORIG = INFINITO, dMinDistEnd = INFINITO ; int nIndS, nIndE ; for ( int i = 0 ; i < ( int)vCrvCompo.size() ; ++ i) { double dCurrDistORIG, dCurrDistEND ; if ( ! DistPointCurve( ptS, *vCrvCompo[i]).GetDist( dCurrDistORIG) || ! DistPointCurve( ptE, *vCrvCompo[i]).GetDist( dCurrDistEND)) return false ; if ( dCurrDistORIG < dMinDistORIG) { dMinDistORIG = dCurrDistORIG ; nIndS = i ; } if ( dCurrDistEND < dMinDistEnd) { dMinDistEnd = dCurrDistEND ; nIndE = i ; } } // cerco il punto più vicino al bisettore double dUS, dUE ; Point3d ptBisS, ptBisE ; int nFlag ; if ( ! DistPointCurve( ptS, *vCrvCompo[nIndS]).GetMinDistPoint( EPS_SMALL, ptBisS, nFlag) || ! DistPointCurve( ptE, *vCrvCompo[nIndE]).GetMinDistPoint( EPS_SMALL, ptBisE, nFlag) || ! vCrvCompo[nIndS]->GetParamAtPoint( ptBisS, dUS, 10 * EPS_SMALL) || ! vCrvCompo[nIndE]->GetParamAtPoint( ptBisE, dUE, 10 * EPS_SMALL)) return false ; // splitto le curve più vicine sui rispettivi punti ( se non coincidenti a degli estremi) if ( nIndS != nIndE) { // ...se le curve sono distinte if ( dUS > 2 * EPS_SMALL && dUS < vCrvCompo[nIndS]->GetCurveCount() - 2 * EPS_SMALL) { PtrOwner pCrvBef( ConvertCurveToComposite( vCrvCompo[nIndS]->CopyParamRange( 0, dUS))) ; PtrOwner pCrvAft( ConvertCurveToComposite( vCrvCompo[nIndS]->CopyParamRange( dUS, vCrvCompo[nIndS]->GetCurveCount()))) ; if ( IsNull( pCrvBef) || ! pCrvBef->IsValid() || IsNull( pCrvAft) || ! pCrvAft->IsValid()) return false ; vCrvCompo[nIndS].Set( Release( pCrvAft)) ; vCrvCompo.emplace_back( Release( pCrvBef)) ; } else if ( dUS > vCrvCompo[nIndS]->GetCurveCount() - 2 * EPS_SMALL) vCrvCompo[nIndS]->Invert() ; // NB. Esiste sempre una curva che ha come punto iniziale ptBisS if ( dUE > 2 * EPS_SMALL && dUE < vCrvCompo[nIndE]->GetCurveCount() - 2 * EPS_SMALL) { PtrOwner pCrvBef( ConvertCurveToComposite( vCrvCompo[nIndE]->CopyParamRange( 0, dUE))) ; PtrOwner pCrvAft( ConvertCurveToComposite( vCrvCompo[nIndE]->CopyParamRange( dUE, vCrvCompo[nIndE]->GetCurveCount()))) ; if ( IsNull( pCrvBef) || ! pCrvBef->IsValid() || IsNull( pCrvAft) || ! pCrvAft->IsValid()) return false ; vCrvCompo[nIndE].Set( Release( pCrvBef)) ; vCrvCompo.emplace_back( Release( pCrvAft)) ; } else if ( dUE > 2 * EPS_SMALL) vCrvCompo[nIndE]->Invert() ; // NB. Esiste sempre una curva che ha coe punto finale ptBiSE } else { // se la curva è la stessa... if ( abs( dUS - dUE) < 5 * EPS_SMALL) // ... e i punti sono coincidenti ; else { // ... se invece distinti... if ( dUS > dUE) swap( dUS, dUE) ; pCrvPath->AddCurve( vCrvCompo[ nIndS]->CopyParamRange( dUS, dUE)) ; } return true ; } // creo un vettore di nodi ( in questo caso coincidono con i punti) vector Nodes ; Nodes.push_back( ptBisS) ; // in prima posizione inerente al punto di inizio // la curve più vicina a ptBisS va in posizione 0 for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) { Point3d ptS ; if ( ! vCrvCompo[i]->GetStartPoint( ptS)) return false ; if ( AreSamePointApprox( ptS, ptBisS)) { swap( vCrvCompo[i], vCrvCompo[0]) ; if ( nIndE == 0) // se inverto proprio con la curva finale nIndE = i ; break ; } } // il nodo destinazione sarà all'indice nDestInd int nDestInd = - 1 ; // riempio il vettore dei nodi con gli altri estremanti for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) { Point3d ptA, ptB ; if ( ! vCrvCompo[i]->GetStartPoint( ptA) || ! vCrvCompo[i]->GetEndPoint( ptB)) return false ; // inserisco i due possibili nodi se non già presenti rispettivamente bool bInsertA = true ; bool bInsertB = true ; for ( int j = 0 ; j < int( Nodes.size()) ; ++ j) { if ( AreSamePointApprox( ptA, Nodes[j])) bInsertA = false ; if ( AreSamePointApprox( ptB, Nodes[j])) bInsertB = false ; if ( nDestInd == -1) if ( AreSamePointApprox( ptBisE, Nodes[j])) nDestInd = j ; } if ( bInsertA) Nodes.push_back( ptA) ; if ( bInsertB) Nodes.push_back( ptB) ; } if ( nDestInd == -1) return false ; // creo la matrice di incidenza con tutte distanze massime DBLMATRIX Adj_Matrix( int( Nodes.size())) ; for ( int i = 0 ; i < int( Nodes.size()) ; ++ i) for ( int j = 0 ; j < int( Nodes.size()) ; ++ j) Adj_Matrix[i].push_back( WEIGHT_NO_ADJ) ; // per di coppie per percorsi a ritroso vector vPathsCouples ; // aggiorno la matrice di incidenza for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) { Point3d ptS ; Point3d ptE ; vCrvCompo[i]->GetStartPoint( ptS) ; vCrvCompo[i]->GetEndPoint( ptE) ; double dLen = 0. ; if ( ! vCrvCompo[i]->GetLength( dLen)) return false ; int nIndS = -1 ; int nIndE = -1 ; for ( int j = 0 ; j < int( Nodes.size()) && ( nIndS == -1 || nIndE == -1) ; ++ j) { if ( nIndS == -1) { if ( AreSamePointApprox( ptS, Nodes[j])) nIndS = j ; } if ( nIndE == -1) { if ( AreSamePointApprox( ptE, Nodes[j])) nIndE = j ; } } if ( nIndS == -1 || nIndE == -1) return false ; // inserisco la coppia INTINT couple ; couple.first = nIndS ; couple.second = nIndE ; vPathsCouples.push_back( couple) ; // inserisco le lunghezze come pesi della matrice delle adiancenze ( è simmetrica ) Adj_Matrix[nIndS][nIndE] = dLen ; Adj_Matrix[nIndE][nIndS] = dLen ; } // calcolo l'albero di copertura minima con Dijkstra INTVECTOR vPath ; PtrOwner DijkstraCalc( CreateDijkstra()) ; if ( IsNull( DijkstraCalc) || ! DijkstraCalc->SetGraph( Adj_Matrix, nDestInd) || ! DijkstraCalc->GetPath( vPath) || int( vPath.size()) != int( Nodes.size())) return false ; // ricostruisco il percorso a ritroso a partire dal nodo finale pCrvPath->AddPoint( ptBisE) ; int nCrvIndex = nIndE ; // indice della curva int nIndNodeS = nDestInd ; // nodo di partenza ( a ritroso ) int nIndNodeE = vPath[nDestInd] ; // nodo di destinazione ( a ritroso ) const int MAXITER = 500 ; int nIter = 0 ; while ( nIndNodeE != -1 && nIter < MAXITER) { if ( nIndNodeS == nDestInd) { // se prima curva da inserire devo controllare il verso ( avendo inserito solo un punto in // pCrvPath ) ... Point3d ptS_first ; vCrvCompo[nCrvIndex]->GetStartPoint( ptS_first) ; if ( ! AreSamePointApprox( ptS_first, ptBisE)) vCrvCompo[nCrvIndex]->Invert() ; } else { // ... altrimenti recupero la curva tra i due nodi correnti for ( int i = 0 ; i < int( vPathsCouples.size()) ; ++ i) { if (( vPathsCouples[i].first == nIndNodeS && vPathsCouples[i].second == nIndNodeE) || ( vPathsCouples[i].second == nIndNodeS && vPathsCouples[i].first == nIndNodeE)) { nCrvIndex = i ; break ; } } } // aggiungo la curva ( invertendola eventualmente ) if ( ! pCrvPath->AddCurve( vCrvCompo[ nCrvIndex]->Clone())) { vCrvCompo[ nCrvIndex]->Invert() ; if ( ! pCrvPath->AddCurve( vCrvCompo[nCrvIndex]->Clone())) return false ; } nIndNodeS = nIndNodeE ; nIndNodeE = vPath[nIndNodeE] ; ++ nIter ; } // controllo se il percorso trovato è ammissibile e che il punto iniziale e finale che coincidano // a quelli ricavati Point3d ptSCheck, ptECheck ; bool bIsValid = pCrvPath->IsValid() && pCrvPath->Invert() && pCrvPath->GetStartPoint( ptSCheck) && AreSamePointEpsilon( ptBisS, ptSCheck, 5 * EPS_SMALL) && pCrvPath->GetEndPoint( ptECheck) && AreSamePointEpsilon( ptBisE, ptECheck, 5 * EPS_SMALL) ; // se non valida, potrebbe effettivamente non esistere un percorso if ( ! bIsValid) pCrvPath->Clear() ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::ChainBisectors( ICURVEPOVECTOR& vpCrvs, ICRVCOMPOPOVECTOR& vCrvCompo) { // controllo dei parametri if ( int( vpCrvs.size()) == 0) return false ; vCrvCompo.clear() ; // 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 INTVECTOR vnId2 ; while ( chainC.GetChainFromNear( ptNear, true, vnId2)) { // creo una curva composita PtrOwner pCrvCompo( CreateCurveComposite()) ; if ( IsNull( pCrvCompo)) return false ; // 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) ; // recupero la curva ICurve* pCrv = vpCrvs[nId] ; // se necessario, la inverto if ( bInvert) pCrv->Invert() ; // 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 ; // aggiungo la curva al vettore di curve composite concatenate vCrvCompo.emplace_back( Release( pCrvCompo)) ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::AddSpiralOut( ISURFFRPOVECTOR& vSfr, const std::vector& vCrvOrig, BOOLVECTOR& vbChangedPrec, VCT3DVECTOR& vVtTrasl, ISURFFRPOVECTOR& vSrfLimit, const Vector3d& vtTool, const Vector3d& vtExtr, double dDepth, double dElev, double dMaxElev, double dOkStep, bool bSplitArcs, const double dExtraLenInOut) { // 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()) ; // determino lo step classico ( senza considerare gli step extra) double dStep = dElev / nStep ; // LeadIn/leadOut Point3d ptP1 ; struct SpiralData { BOOLVECTOR vbMidOut ; // flags per effettiva entrata precedente da fuori dal grezzo ICRVCOMPOPOVECTOR vMCrv ; // vettore delle curve dei percorsi di svuotatura dello step precedente ICRVCOMPOPOVECTOR vRCrv ; // vettore delle curve dei percorsi di ritorno dello step precedente vector vvtMidOut ; // vettore dei versori delle direzioni di uscita dello step precedente INTVECTOR vnRegTot ; // vettore dei numeri delle regioni formate dal primo Offset nello step precedente BOOLVECTOR vbOptTrap ; // vettore di flag per casi ottimizzati Point3d ptEndPath ; // punto finale del percorso di svuotatura attuale } ; SpiralData SDStepPrec ; // informazioni Spiral attuali int nOffs_act = 0 ; // Shift indice dei vettori causato da nReg // scorro il vettore delle FlatRegion ( calcolate per ogni step j-esimo) for ( int j = 1 ; j <= nStep ; ++ j) { SpiralData SDStepCurr ; // informazioni Spiral attuali if ( ! vbChangedPrec[j-1]) { for ( int i = 0 ; i < int( SDStepPrec.vMCrv.size()) ; ++ i) SDStepCurr.vMCrv.emplace_back( SDStepPrec.vMCrv[i]->Clone()) ; SDStepCurr.vbMidOut = SDStepPrec.vbMidOut ; SDStepCurr.vnRegTot = SDStepPrec.vnRegTot ; SDStepCurr.vvtMidOut = SDStepPrec.vvtMidOut ; SDStepCurr.vbOptTrap = SDStepPrec.vbOptTrap ; SDStepCurr.ptEndPath = SDStepPrec.ptEndPath ; } // se superficie non valida, passo alla prossima if ( IsNull( vSfr[j-1]) || ! vSfr[j-1]->IsValid()) continue ; // superificie per ingressi ed uscite PtrOwner pSrfLeanInOut( CloneSurfFlatRegion( vSfr[j-1])) ; if( IsNull( pSrfLeanInOut)) return false ; // punto finale dello step precedente Point3d ptEndPrec = ( j == 1 ? P_INVALID : SDStepPrec.ptEndPath) ; // creo le entità per lo step corrente ICRVCOMPOPOVECTOR vMCrv, vRCrv ; BOOLVECTOR vbOut ; INTVECTOR vnRegTot ; // 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 ; 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) { // 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 = false ; // 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( SDStepPrec.vMCrv.size()) - 1) break ; pMCrv.Set( SDStepPrec.vMCrv[nOffs_act]->Clone()) ; pRCrv.Set( SDStepPrec.vRCrv[nOffs_act]->Clone()) ; nRegTot = SDStepPrec.vnRegTot[nOffs_act] ; bMidOut = SDStepPrec.vbMidOut[nOffs_act] ; vtMidOut = SDStepPrec.vvtMidOut[nOffs_act] ; bOptimizedTrap = SDStepPrec.vbOptTrap[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 ; } // 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, ptEndPrec, vtMidOut, bMidOut, bSplitArcs, pMCrv, pRCrv, vCrvOrig[j-1][nInd], j == 1 ? true : vbChangedPrec[j-1], bOptimizedTrap)) return false ; // se terminate le regioni... 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 SDStepCurr.vMCrv.emplace_back( pMCrv->Clone()) ; SDStepCurr.vRCrv.emplace_back( pRCrv->Clone()) ; // memorizzo i Flag per le entrate SDStepCurr.vbMidOut.push_back( bMidOut) ; // memorizzo il numero delle regioni create dal primo Offset SDStepCurr.vnRegTot.push_back( nRegTot) ; // memorizzo il versore di uscita calcolato SDStepCurr.vvtMidOut.push_back( vtMidOut) ; // memorizzo caso ottimizzato SDStepCurr.vbOptTrap.push_back( bOptimizedTrap) ; // sovrascrivo sempre il punto finale ( lo aggiorno in caso di più chunks) Point3d ptEnd ; pMCrv->GetEndPoint( ptEnd) ; SDStepCurr.ptEndPath = ptEnd ; } // se il tool non è cilindrico... if ( ! bOptimizedTrap && m_TParams.m_dSideAng < 0 && dExtraLenInOut > EPS_SMALL) { Vector3d vtDir ; Point3d ptS ; Vector3d vtIn ; double dLenCheck = 0.; // aggiungo al percorso un tratto interno if ( pMCrv->GetCurveCount() > 0) { pMCrv->GetStartDir( vtDir) ; pMCrv->GetStartPoint( ptS) ; vtIn = vtDir ; vtIn.Rotate( vtTool, m_Params.m_bInvert ? -ANG_RIGHT : ANG_RIGHT) ; if ( GetLeadInType() == POCKET_LI_HELIX) dLenCheck = min( 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam), m_dMaxHelixRad) ; else if ( GetLeadInType() == POCKET_LI_ZIGZAG) dLenCheck = 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam) ; else if ( GetLeadInType() == POCKET_LI_GLIDE) dLenCheck = m_Params.m_dLiTang ; pMCrv->AddLine( ptS + ( dExtraLenInOut + dLenCheck) * vtIn, false) ; // aggiorno la superficie limite di controllo PtrOwner pNewSfrLeadInOut( CloneSurfFlatRegion( vSfr[j-1])) ; if ( IsNull( pNewSfrLeadInOut) || ! pNewSfrLeadInOut->Offset( - dExtraLenInOut, ICurve::OFF_CHAMFER) || ! pNewSfrLeadInOut->IsValid() || ! pSrfLeanInOut.Set( Release( pNewSfrLeadInOut))) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } // se ho un solo Offset dalla svuotatura, devo controllare anche l'uscita... Point3d ptE ; pMCrv->GetEndPoint( ptE) ; if ( AreSamePointEpsilon( ptS, ptE, 500 * EPS_SMALL)) pMCrv->AddLine( ptS + ( dExtraLenInOut + dLenCheck) * vtIn, true) ; } } // 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) ; // controllo geometria del tool if ( m_TParams.m_dSideAng != 0) { if ( ! AdjustTrapezoidSpiralLeadInLeadOutForSideAngle( pMCrv, dExtraLenInOut, nOutsideRaw, vtTool)) return false ; } // aggiorno la curva per eventuali inversioni SDStepCurr.vMCrv.back().Set( pMCrv->Clone()) ; } // 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() ; } // 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 (( j > 1 && vSfr[j-1]->GetChunkCount() == 1 && int( SDStepPrec.vMCrv.size()) == 1 && bOptimizedTrap && SDStepPrec.vbOptTrap[0])) { Point3d ptA, ptB, ptC ; pMCrv->GetStartPoint( ptA) ; SDStepPrec.vMCrv[0]->GetStartPoint( ptB) ; SDStepPrec.vMCrv[0]->GetEndPoint( ptC) ; ptA.Translate( vVtTrasl[j-2] - vVtTrasl[j-1]) ; bool bOpposite = ( Dist( ptA, ptB) > Dist( ptA, ptC) + EPS_SMALL) ; switch ( nOutsideRaw) { case 0 : case 2 : if ( ! bOpposite) { pMCrv->Invert() ; SDStepCurr.vMCrv[0]->Invert() ; } break ; case 1 : if ( bOpposite) { pMCrv->Invert() ; SDStepCurr.vMCrv[0]->Invert() ; } break ; } } ++ nReg ; // incremento del numero di regioni trovate ++ nOffs_act ; // shift indice // salvo i dati Spiral correnti vMCrv.emplace_back( Release( pMCrv)) ; vRCrv.emplace_back( Release( pRCrv)) ; vbOut.push_back( bOutStart || bForcedOutStart) ; vnRegTot.push_back( nRegTot) ; } } // per ogni percorso trovato for ( int i = 0 ; i < int( vMCrv.size()) ; ++ i) { // recupero l'indice massimo delle sottocurve int nMaxInd = vMCrv[i]->GetCurveCount() - 1 ; // ciclo sulle curve elementari for ( int u = 0 ; u <= nMaxInd ; ++ u) { // curva corrente const ICurve* pCrvC = vMCrv[i]->GetCurve( u) ; // copio la curva PtrOwner pCurve( pCrvC->Clone()) ; if ( IsNull( pCurve)) return false ; // se prima curva del percorso if ( u == 0) { // ricavo il punto inziale Point3d ptStart ; pCurve->GetStartPoint( ptStart) ; // vettore tangente iniziale Vector3d vtStart ; pCurve->GetStartDir( vtStart) ; // se primo Step e primo percorso, allora semplice LeadIn... if ( j == 1 && i == 0) { // inizio leadIn if ( ! CalcLeadInStart( ptStart, vtStart, vtExtr, vRCrv[i], ptP1)) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } // determino elevazione su inizio attacco ( se non trovata, l'elevazione è nStep) double dStElev ; if ( ! GetElevation( m_nPhase, ptStart - 10 * EPS_SMALL * vtTool, vtTool, GetRadiusForStartEndElevation(), vtTool, dStElev)) dStElev = dStep ; bool bAhUnderRaw = m_bAboveHead && ! m_bAggrBottom && ! m_bTiltingTab && GetAhPointUnderRaw( ptP1, vtTool, 0, GetRadiusForStartEndElevation(), m_TParams.m_dLen, false, dSafeZ, vtTool, dStElev) ; bool bUhAboveRaw = ! m_bAboveHead && GetUhPointAboveRaw( ptP1, vtTool, 0, GetRadiusForStartEndElevation(), m_TParams.m_dLen, false, dSafeZ, vtTool, dStElev) ; if ( bAhUnderRaw || bUhAboveRaw || m_bTiltingTab) dStElev = max( dStElev, dStep) ; 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, vbOut[i])) { m_pMchMgr->SetLastError( 2414, "Error in Pocketing : Approach not computable") ; return false ; } // aggiungo attacco SetFeed( GetStartFeed()) ; if ( ! AddLeadIn( vMCrv[i], ptP1, ptStart, vtStart, V_INVALID, vtExtr, pSrfLeanInOut, vRCrv[i], !m_Params.m_bInvert, bSplitArcs, vbOut[i])) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } } // ... se devo aggiungere l'uscita del percorso precedente e calcolare l'entrata // per il percorso attuale else { PtrOwner pRCrv( CreateCurveComposite()) ; // ricavo il punto in cui sono Point3d ptCurr ; GetCurrPos( ptCurr) ; // ricavo il punto che devo raggiungere Point3d ptDest ; vMCrv[i]->GetStartPoint( ptDest) ; // modifico il punto di destinazione a seconda del tipo di attacco per tangenza Vector3d vtEnd = vtStart ; if ( ! vbOut[i]) { if ( ! SetLeadInPointStart( ptCurr, vtTool, !m_Params.m_bInvert, vtStart, ptDest)) return false ; } // calcolo il percorso di ritorno del percorso attuale if ( IsNull( pRCrv) || ! CalcLinkOnStep( ptCurr, ptDest, vSfr[j-2], ! SDStepCurr.vbOptTrap[i] ? vSrfLimit[j-2] : CreateSurfFlatRegion(), pRCrv)) { m_pMchMgr->SetLastError( 2441, "Error in Pocketing : Return path not computable") ; return false ; } // emetto SetFeed( GetEndFeed()) ; if ( pRCrv->GetCurveCount() > 0 && AddCurveMove( pRCrv) == GDB_ID_NULL) return false ; // ricavo la posizione attuale Point3d ptAbove ; GetCurrPos( ptAbove) ; // aggiungo attacco ( solo collegamento ) SetFeed( GetStartFeed()) ; if ( ! AddLeadIn( vMCrv[i], ptAbove, ptStart, vtStart, vtEnd, vtExtr, pSrfLeanInOut, vRCrv[i], !m_Params.m_bInvert, bSplitArcs, vbOut[i])) { m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ; return false ; } } } // elaborazioni sulla curva corrente 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à dell'ultimo percorso if ( u == nMaxInd && j == int( vSfr.size()) && i == int( vMCrv.size()) - 1) { // dati fine entità Point3d ptEnd ; pCurve->GetEndPoint( ptEnd) ; Vector3d vtEnd ; pCurve->GetEndDir( vtEnd) ; // aggiungo uscita, LeadOut Point3d ptP1 ; double dEndElev = dElev ; SetFeed( GetEndFeed()) ; if ( ! AddLeadOut( ptEnd, vtEnd, vtExtr, vRCrv[i], 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 ; } } } } // aggiorno i dati Spiral se regioni cambiate tra step SDStepPrec.vMCrv.clear() ; for ( int i = 0 ; i < int( SDStepCurr.vMCrv.size()) ; ++ i) SDStepPrec.vMCrv.emplace_back( Release( SDStepCurr.vMCrv[i])) ; SDStepPrec.vbMidOut = SDStepCurr.vbMidOut ; SDStepPrec.vnRegTot = SDStepCurr.vnRegTot ; SDStepPrec.vvtMidOut = SDStepCurr.vvtMidOut ; SDStepPrec.vbOptTrap = SDStepCurr.vbOptTrap ; SDStepPrec.ptEndPath = SDStepCurr.ptEndPath ; } return true ; } //---------------------------------------------------------------------------- bool Pocketing::CalcSpiral( const ISurfFlatRegion* pSrfPock, int& nReg, Point3d& ptStart, const Point3d& ptEndPrec, 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) ; pCrvBorder->SetExtrusion( pSrfPock->GetNormVersor()) ; // setto l'estrusione della curva Vector3d vtN = pSrfPock->GetNormVersor() ; // vettore normale per caso circonferenza Point3d ptCen ; double dRad ; bool bIsCircle = false ; if ( ! OptimizedSpiralCirle( pCrvBorder, 50 * EPS_SMALL, dRad, ptCen, bIsCircle)) return false ; if ( bIsCircle) { double dIntRad = 0 ; if ( 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 ; // se l'offset della regione la la annulla, allora ritento con una piccola tolleranza 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 ; // assegno come if ( j > 0) // inverto l'orientamento delle curve interne ( offset delle isole trovate) pCrvCompoBorder->Invert() ; // salvo l'iterazione come primo parametro della curva pCrvCompoBorder->SetTempParam( nIter * 1., 0) ; // 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, devo sapere IN/OUT) PtrOwner pCrvCompoExtBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ; vOffsFirstCurve.emplace_back( Release( pCrvCompoExtBorder)) ; } } } // controllo se serve un raggio più piccolo di svuotatura bool bSmallRad = ( nIter == 0 ? dOffs < 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 ( vOffs.empty()) return true ; // cambio il punto iniziale della prima Curva di Offset Point3d ptRef = ptEndPrec ; ptRef.ToLoc( frPocket) ; Point3d ptNewStart ; if ( SetBetterPtStartForSubChunks( vOffs[0], pSrfToWork, ptRef, frPocket, 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 double dSmoothPar = m_TParams.m_dDiam / 16 ; for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) { if ( i != 0) ModifyCurveToSmoothed( vOffs[i], dSmoothPar, dSmoothPar, false) ; vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ; vOffsClosedCurves[i].Set( vOffs[i]->Clone()) ; } // setto il punto iniziale della svuotatura double dNewUS ; vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ; vOffs[0]->ChangeStartPoint( dNewUS) ; vOffs[0]->SetTempParam( 0., 0) ; // prima iterazione // riordino le curve cambiando e creo i collegamenti ICURVEPOVECTOR vLinks( vOffs.size()) ; if ( ! CreateSpiralPocketingPath( vOffs, vLinks, vOffsClosedCurves, vOffsFirstCurve)) return false ; // 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()) ; // controllo eventuali parti non svuotate... pCrvOEWithFlags->ToLoc( frPocket) ; PtrOwner pSrfToCut( CreateSurfFlatRegion()) ; if ( GetUnclearedRegion( vOffsFirstCurve, vOffs, vLinks, pCrvOEWithFlags, pSrfToCut)) { // Modifico i percorsi if ( ! RemoveExtraParts( pSrfToCut, vOffs, vOffsClosedCurves, vOffsFirstCurve, vLinks)) return false ; } // creo il percorso di lavoro a partire dalla raccolta degli offset e dei collegamenti for ( int i = 0 ; i < int( vOffs.size()) ; ++i) { // se collegamento da aggiungere if ( ! IsNull( vLinks[i])) { int nCrvsCount0 = pMCrv->GetCurveCount() ; // accodo nel percorso di lavorazione if ( ! pMCrv->AddCurve( vLinks[i]->Clone())) return false ; // 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) ; ptStart.ToGlob( frPocket) ; vtMidOut.ToGlob( frPocket) ; pCrvOEWithFlags->ToGlob( frPocket) ; return true ; } //---------------------------------------------------------------------------- bool Pocketing::CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, const ICRVCOMPOPOVECTOR& vOffsClosedCurves, const ICRVCOMPOPOVECTOR& vOffsFirstCurve) { // controllo dei parametri if ( vOffs.empty() || vOffsFirstCurve.empty()) return false ; vLinks.clear() ; vLinks.resize( int( vOffs.size())) ; // scorro tutte le curva di Offset for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) { // ricavo il punto inziale della curva corrente Point3d ptS ; if ( ! vOffs[i]->GetStartPoint( ptS)) return false ; // ricavo l'iterazione corrente int nCurrIter = int( vOffs[i]->GetTempParam( 0)) ; int nNextInd = -1 ; // indice della curva successiva double dMinDist = INFINITO ; // distanza tra questa curva e la successiva Point3d ptStartNext ; // punto iniziale della curva successiva // tra le curva successive cerco la curva interna e più vicina ad essa for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) { IntersCurveCurve IntCC( *vOffs[i], *vOffs[j]) ; CRVCVECTOR ccClass ; // se interna if ( IntCC.GetCurveClassification( 1, EPS_SMALL, ccClass) && int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) { // calcolo la distanza minima tra essa int nFlag ; Point3d ptClosest ; if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) { double dCurrDist = SqDist( ptS, ptClosest) ; if ( dCurrDist < dMinDist) { dMinDist = dCurrDist ; nNextInd = j ; ptStartNext = ptClosest ; } } } } // se non ho trovato nessuna curva interna... cerco semplicemente la curva più vicina if ( nNextInd == -1) { for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) { int nFlag ; Point3d ptClosest ; if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) { double dCurrDist = SqDist( ptS, ptClosest) ; if ( dCurrDist < dMinDist) { dMinDist = dCurrDist ; nNextInd = j ; ptStartNext = ptClosest ; } } } } // se non ho trovato nessuna curva, errore if ( nNextInd == -1) return false ; // scambio la curva i+1 esima con la j-esima ( se non sono già in ordine) if ( nNextInd != i + 1) swap( vOffs[nNextInd], vOffs[i+1]) ; // cambio il suo punto iniziale nel punto più vicino tovato double dUS ; if ( ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUS, EPS_SMALL)) return false ; vOffs[i+1]->ChangeStartPoint( dUS) ; // accorcio la curva per raccordarmi in tangenza con la successiva if ( int( vOffs.size()) > 1) { // clono le curve i ed i+1 esime ( nel caso non riuscissi ad accorciarle o raccordarle ) PtrOwner pCrv_i( CloneCurveComposite( vOffs[i])) ; PtrOwner pCrv_ii( CloneCurveComposite( vOffs[i+1])) ; if ( IsNull( pCrv_i) || IsNull( pCrv_ii) || ! pCrv_i->IsValid() || ! pCrv_ii->IsValid()) return false ; // creo la curva che le collegherà PtrOwner pCrvLink( CreateCurveComposite()) ; if ( IsNull( pCrvLink)) return false ; // NB. Le curve di Offset da tagliare non devono essere quelle di primo Offset if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsClosedCurves, vOffsFirstCurve, pCrvLink, i == 0 ? 0 : 0.01, 0.01, 2)) { // se non sono riuscito, cerco una strada più semplice // ripristino le curve pCrvLink->Clear() ; vOffs[i].Set( Release( pCrv_i)) ; // chiuso vOffs[i+1].Set( Release( pCrv_ii)) ; // chiuso // recupero i vettori tangente iniziali ( le curve sono chiuse ) Vector3d vtS, vtE ; if ( ! vOffs[i]->GetStartDir( vtS) || ! vOffs[i+1]->GetStartDir( vtE)) return false ; // creo il bi-arco tra esse if ( CalcBoundedSmootedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, pCrvLink)) vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento else { m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ; return false ; } continue ; // passo alla curva i+1 esima successiva } // per sicurezza aggiorno i nuovi punti e i nuovi parametri double dUNewS, dUNewE ; if ( ! pCrvLink->GetStartPoint( ptS) || ! pCrvLink->GetEndPoint( ptStartNext) || ! vOffs[i]->GetParamAtPoint( ptS, dUNewS) || ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUNewE)) return false ; // imposto il punto iniziale della curva successiva ( i+1 esima) vOffs[i+1]->ChangeStartPoint( dUNewE) ; PtrOwner pCrvNewOffs( CloneCurveComposite( vOffs[i])) ; if ( dUNewS > EPS_SMALL) { pCrvNewOffs.Set( ConvertCurveToComposite( vOffs[i]->CopyParamRange( 0, dUNewS))) ; // sostituisco la curva i esima con quella tagliata vOffs[i].Set( pCrvNewOffs) ; } // aggiorno il collegamento vLinks[i+1].Set( Release( pCrvLink)) ; } } return true ; } //---------------------------------------------------------------------------- bool Pocketing::OptimizedSpiralCirle( const ICurveComposite* pCrvCompo, const double dToll, double& dRad, Point3d& ptC, bool& bIsCirlce) { // controllo dei parametri if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || dToll < EPS_SMALL) return false ; dRad = 0. ; ptC = P_INVALID ; bIsCirlce = false ; // se aperta, non è un cerchio e non è piana, esco Plane3d plPock ; if ( ! pCrvCompo->IsClosed() || ! pCrvCompo->IsFlat( plPock)) return true ; // creo un sistema di riferimento centrato sulla curva Point3d ptCentroid ; Vector3d vtN ; Frame3d frCurr ; if ( ! pCrvCompo->GetCentroid( ptCentroid) || ! pCrvCompo->GetExtrusion( vtN) || ! frCurr.Set( ptCentroid, vtN) || ! frCurr.IsValid()) return false ; // porto la curva nel sistema di riferimento corrente ( dopo averla clonata) PtrOwner pCrvLoc( CloneCurveComposite( pCrvCompo)) ; if ( IsNull( pCrvLoc) || ! pCrvLoc->IsValid() || ! pCrvLoc->ToLoc( frCurr)) return false ; // approssimo la curva locale con una PolyLine PolyLine pL ; if ( ! pCrvLoc->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, pL) || pL.GetPointNbr() == 0) return false ; // salvo i punti ottenuti ( il punto finale sarà contenuto due volte) PNTVECTOR vPts ; Point3d ptNext ; pL.GetFirstPoint( ptNext) ; vPts.push_back( ptNext) ; while ( pL.GetNextPoint( ptNext)) { vPts.push_back( Media( vPts.back(), ptNext)) ; // inserisco il punto medio vPts.push_back( ptNext) ; // inserisco il punto successivo } // per ogni coppia di punti calcolo la distanza massima e minima dal centro del cerchio locale ( ORIG) double dMaxDist = 0. ; double dMinDist = INFINITO ; for ( int i = 0 ; i < int( floor( 0.5 * int( vPts.size()))) ; i = i + 2) { PtrOwner pSeg( CreateCurveLine()) ; if ( IsNull( pSeg) || ! pSeg->Set( vPts[i], vPts[i+1])) return false ; DistPointCurve DPC( ORIG, *pSeg) ; double dCurrDist = 0. ; if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) dMinDist = dCurrDist ; dCurrDist = Dist( ORIG, vPts[i]) ; if ( dCurrDist > dMaxDist) dMaxDist = dCurrDist ; } // controllo se la distanza tra minima e massima sono simili ( in base alla tolleranza) if ( abs( dMaxDist - dMinDist) > dToll) return true ; // imposto i parametri per la circonferenza dRad = 0.5 * ( dMaxDist + dMinDist) ; ptC = ptCentroid ; bIsCirlce = true ; return true ; } //---------------------------------------------------------------------------- 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 ( AreSameVectorEpsilon( vtS, vtE, 5 * EPS_SMALL)) { 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 // NB. Due curve non è detto che siano raccordabili con una circonferenza ai parametri stabiliti // -> cerco di stare vicino a tale quantità 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 ; } // dal secondo arco in poi di raccordo controllo che non ci siano intersezioni tra essi if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) { IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ; if ( intCCH.GetIntersCount() > 0 ) IntersBTWArcs = true ; } // se ho intersezioni tra archi o l'arco creato non è valido, allora provo altre // nMaxTestForArcs volte a ricrearlo avvicinando i punti while (( IsNull( pCrvArc) || IntersBTWArcs) && nIterForArcs < nMaxTestForArcs) { // aggiorno i parametri di riferimento ( mi avvicino sempre della metà) 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; // controllo ancora lunghezza ed intersezioni 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 ; } 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 ; } } // se ancora non valido... salto l'arco if ( IsNull( pCrvArc) || ! pCrvArc->IsValid()) { 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, const ICRVCOMPOPOVECTOR& vOffsCL, const 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, const 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, const double dParMeet, const 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) ; // controllo la distanza tra i due punti ( utilizzo raccordo lineare se grande) if ( SqDist( ptStart, ptEnd) > 25 * ( GetSideStep() + m_TParams.m_dDiam * m_TParams.m_dDiam)) { Point3d ptS_shift = ptStart + ( m_TParams.m_dDiam / 2) * vtStart ; Point3d ptE_shift = ptEnd - ( m_TParams.m_dDiam / 2) * vtEnd ; // controllo che tali punti siano effettivamente interni alla regione da svuotare bool bOk = true ; for ( int i = 0 ; i < int( vOffIslands.size()) && bOk ; ++ i) { PolyLine PL ; vOffIslands[i]->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, PL) ; bOk = IsPointInsidePolyLine( ptS_shift, PL, EPS_SMALL) || IsPointInsidePolyLine( ptE_shift, PL, EPS_SMALL) ; } if ( ! CalcBoundedLink( bOk ? ptS_shift : ptStart, bOk ? ptE_shift : ptEnd, vOffIslands, pCrvLink)) return false ; pCrvLink->AddLine( ptStart, false) ; pCrvLink->AddLine( ptEnd, true) ; ModifyCurveToSmoothed( pCrvLink, 2.5, 2.5, false) ; return true ; } // 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 + GetOffsL() + 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 + GetOffsL() + 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 ICurveComposite* pCrvCompo, const Point3d& ptP1, const Point3d& ptStart, const Vector3d& vtStart, const Vector3d& vtEnd, 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 dExtraAng = 0. ; if ( vtEnd.IsValid()) { bool bDet ; vtStart.GetRotation( vtEnd, vtN, dExtraAng, bDet) ; if ( dExtraAng < 0 && bAtLeft) dExtraAng = ANG_FULL + dExtraAng ; } double dAngCen = dExtraAng + 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 ; double dLenRef = 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam) ; // se sono in un caso ottimizzato dei trapezi ( tutti i lati chiusi) PtrOwner pSingleCrv( CloneCurveComposite( pCrvCompo)) ; if ( IsNull( pSingleCrv) || ! pSingleCrv->IsValid()) return false ; pSingleCrv->MergeCurves( 100 * EPS_ANG_SMALL, 100 * EPS_ANG_SMALL) ; // per sicurezza // ricavo la lunghezza della curva double dLen ; if ( ! pSingleCrv->GetLength( dLen)) return false ; // se la curva è singola e permette l'entrata a zigzag... if ( pSingleCrv->GetCurveCount() == 1 && dLen > dLenRef) { int nStep = int( ceil( - dDeltaN / ( m_Params.m_dLiElev + 20 * EPS_SMALL))) + 1 ; double dStep = - dDeltaN / nStep ; double dUTrap ; Point3d ptTrap ; if ( ! pSingleCrv->GetParamAtLength( dLenRef, dUTrap) || ! pSingleCrv->GetPointD1D2( dUTrap, ICurve::FROM_MINUS, ptTrap)) return false ; // traslo il punto trovato alla quota di ptP1 ptTrap.Translate( - dDeltaN * vtN) ; // estremi del percorso a ZigZag Point3d ptPa = ptTrap + vtStart * dLenRef ; Point3d ptPb = ptTrap - vtStart * dLenRef ; // 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 ; } if ( AddLinearMove( ptPa - vtN * ( nStep - 0.75) * dStep, MCH_CL_LEADIN) == GDB_ID_NULL) return false ; return ( AddLinearMove( ptStart, MCH_CL_LEADIN) != GDB_ID_NULL) ; } } else { int nStep = int( ceil( - dDeltaN / ( m_Params.m_dLiElev + 20 * EPS_SMALL))) ; double dStep = - dDeltaN / nStep ; // se la svuotatuta è SpiralOut, l'entrata a ZigZag è più conveniente farla lineare if ( m_Params.m_nSubType == POCKET_SUB_SPIRALOUT) { Point3d ptPa = ptP1 + vtStart * dLenRef ; Point3d ptPb = ptP1 - vtStart * dLenRef ; // 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) ; } } else { // ricavo la curva di primo Offset dal percorso PtrOwner pCrvCompoFirstOffs( CreateCurveComposite()) ; if ( IsNull( pCrvCompoFirstOffs)) return false ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) { Point3d ptEnd ; pCrvCompo->GetCurve( u)->GetEndPoint( ptEnd) ; pCrvCompoFirstOffs->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ; if ( AreSamePointApprox( ptEnd, ptStart)) break ; } // per sicurezza... pCrvCompoFirstOffs->Close() ; // la traslo della quantità richiesta ( il percorso a ZigZag incomincia sopra al piano di pocketing) pCrvCompoFirstOffs->Translate( - vtN * dDeltaN) ; double dLen, dUA, dUB ; if ( pCrvCompoFirstOffs->GetParamAtLength( dLenRef, dUA) && pCrvCompoFirstOffs->GetLength( dLen) && pCrvCompoFirstOffs->GetParamAtLength( dLen - dLenRef, dUB)) { // recupero le due sottocurve per definire il percorso a ZigZag PtrOwner pCrvA( pCrvCompoFirstOffs->CopyParamRange( 0., dUA)) ; PtrOwner pCrvB( pCrvCompoFirstOffs->CopyParamRange( dUB, pCrvCompoFirstOffs->GetCurveCount())) ; if ( IsNull( pCrvA) || IsNull( pCrvB) || ! pCrvA->IsValid() || ! pCrvB->IsValid()) return false ; // inverto la curva sinistra pCrvB->Invert() ; // traslo MAX_NUM_PT punti della curva lungo -vtN e creo quindi le curve per lo ZigZag const int MAX_NUM_PT = 100 ; PtrOwner pCrvA_ZigZag( CreateCurveComposite()) ; PtrOwner pCrvB_ZigZag( CreateCurveComposite()) ; PtrOwner pCrvA_ZigZag_I( CreateCurveComposite()) ; // Invertita PtrOwner pCrvB_ZigZag_I( CreateCurveComposite()) ; // Invertita if ( IsNull( pCrvA_ZigZag) || IsNull( pCrvB_ZigZag) || IsNull( pCrvA_ZigZag_I) || IsNull( pCrvB_ZigZag_I)) return false ; // ricavo, rispettivamente, i valori al 0%, 25%, 50%, 75%, 100% delle quote per la discesa // ( il percorso a ZigZag sarà formato da crvA, crvA_invetered, crvB, crvB_inverted) Point3d ptS ; pCrvA->GetStartPoint( ptS) ; pCrvA_ZigZag->AddPoint( ptS) ; pCrvB_ZigZag->AddPoint( ptS) ; ptS.Translate( - vtN * 0.5 * dStep) ; pCrvA_ZigZag_I->AddPoint( ptS) ; pCrvB_ZigZag->AddPoint( ptS) ; ptS.Translate( - vtN * 0.5 * dStep) ; pCrvB_ZigZag_I->AddPoint( ptS) ; for ( int j = 1 ; j <= MAX_NUM_PT ; ++ j) { // j parte da 1, ho già inserito i punti inziali // tra lo 0% e il 50% della discesa, si considera la curva A double dParA ; pCrvA->GetParamAtLength( j * ( dLenRef / MAX_NUM_PT), dParA) ; Point3d ptA ; pCrvA->GetPointD1D2( dParA, ICurve::FROM_MINUS, ptA) ; // tra 0% e 25% Point3d ptA_z = ptA ; ptA_z.Translate( - vtN * j * ( 0.25 * dStep / MAX_NUM_PT)) ; pCrvA_ZigZag->AddLine( ptA_z) ; // tra 25% e 50% Point3d ptA_z_I = ptA ; ptA_z_I.Translate( - vtN * ( 0.25 * dStep) * ( 2.0 - ( 1. * j) / MAX_NUM_PT)) ; pCrvA_ZigZag_I->AddLine( ptA_z_I) ; // tra il 50% e il 100% della discesa, si considera la curva B double dParB ; pCrvB->GetParamAtLength( j * ( dLenRef / MAX_NUM_PT), dParB) ; Point3d ptB ; pCrvB->GetPointD1D2( dParB, ICurve::FROM_MINUS, ptB) ; // tra il 50% e 75% Point3d ptB_z = ptB ; ptB_z.Translate( - vtN * ( 0.25 * dStep) * ( 2.0 + ( 1. * j) / MAX_NUM_PT)) ; pCrvB_ZigZag->AddLine( ptB_z) ; // tra 75% e 100% Point3d ptB_z_I = ptB ; ptB_z_I.Translate( - vtN * ( 0.25 * dStep) * ( 4 - ( 1. * j) / MAX_NUM_PT)) ; pCrvB_ZigZag_I->AddLine( ptB_z_I) ; } // semplifico le curve PolyArc PA ; pCrvA_ZigZag->MergeCurves( 500 * EPS_SMALL, 500 * EPS_ANG_SMALL) ; pCrvA_ZigZag->ApproxWithArcsEx( 500 * EPS_SMALL, ANG_TOL_STD_DEG, LIN_FEA_STD, PA) ; pCrvA_ZigZag->Clear() ; pCrvA_ZigZag->FromPolyArc( PA) ; PA.Clear() ; pCrvA_ZigZag_I->MergeCurves( 500 * EPS_SMALL, 500 * EPS_ANG_SMALL) ; pCrvA_ZigZag_I->Invert() ; // altrimenti la curva salirebbe lungo vtN pCrvA_ZigZag_I->ApproxWithArcsEx( 500 * EPS_SMALL, ANG_TOL_STD_DEG, LIN_FEA_STD, PA) ; pCrvA_ZigZag_I->Clear() ; pCrvA_ZigZag_I->FromPolyArc( PA) ; PA.Clear() ; pCrvB_ZigZag->MergeCurves( 500 * EPS_SMALL, 500 * EPS_ANG_SMALL) ; pCrvB_ZigZag->ApproxWithArcsEx( 500 * EPS_SMALL, ANG_TOL_STD_DEG, LIN_FEA_STD, PA) ; pCrvB_ZigZag->Clear() ; pCrvB_ZigZag->FromPolyArc( PA) ; PA.Clear() ; pCrvB_ZigZag_I->MergeCurves( 500 * EPS_SMALL, 500 * EPS_ANG_SMALL) ; pCrvB_ZigZag_I->Invert() ; // altrimenti la curva salirebbe lungo vtN pCrvB_ZigZag_I->ApproxWithArcsEx( 500 * EPS_SMALL, ANG_TOL_STD_DEG, LIN_FEA_STD, PA) ; pCrvB_ZigZag_I->Clear() ; pCrvB_ZigZag_I->FromPolyArc( PA) ; // per ogni step copio le curve for ( int i = 1 ; i <= nStep ; ++ i) { if ( AddCurveMove( pCrvA_ZigZag, MCH_CL_LEADIN) == GDB_ID_NULL || AddCurveMove( pCrvA_ZigZag_I, MCH_CL_LEADIN) == GDB_ID_NULL || AddCurveMove( pCrvB_ZigZag, MCH_CL_LEADIN) == GDB_ID_NULL || AddCurveMove( pCrvB_ZigZag_I, MCH_CL_LEADIN) == GDB_ID_NULL) return false ; // traslo per il percorso a ZigZag sottostante ( se ne ho uno) if ( i < nStep) { pCrvA_ZigZag->Translate( - vtN * dStep) ; pCrvA_ZigZag_I->Translate( - vtN * dStep) ; pCrvB_ZigZag->Translate( - vtN * dStep) ; pCrvB_ZigZag_I->Translate( - vtN * dStep) ; } } return true ; } } } 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 + GetOffsL(), false, dSafeZ, vtN, dElev) ; bool bUhAboveRaw = ! m_bAboveHead && GetUhPointAboveRaw( ptP1, vtN, 0, GetRadiusForStartEndElevation(), m_TParams.m_dLen + GetOffsL(), 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 + GetOffsL(), false, dSafeZ, vtN, dElev) ; bool bUhAboveRaw = ! m_bAboveHead && GetUhPointAboveRaw( ptP1, vtN, 0, GetRadiusForStartEndElevation(), m_TParams.m_dLen + GetOffsL(), 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, const Frame3d& frPocket, Point3d& ptMid, Vector3d& vtMidOrt) { // recupero il vettore estrusione Vector3d vtExtr = Z_AX ; pCompo->GetExtrusion( vtExtr) ; // verifico se tutti i lati sono aperti bool bAllOpen = true ; const ICurve* pMyCrv = pCompo->GetFirstCurve() ; while ( pMyCrv != nullptr) { if ( pMyCrv->GetTempProp() != 1) { bAllOpen = false ; break ; } pMyCrv = pCompo->GetNextCurve() ; } // salvo il punto iniziale e la direzione d'uscita del primo lato aperto valido che trovo // NB. Il primo valido non significa il migliore, potrebbe infatti essere il lato aperto più lungo // ma essere all'interno del grezzo... cerco di dare priorità ai lati sul grezzo // flag per la presenza di un primo lato aperto valido per entrare bool bFirstOpenValid = false ; Point3d ptFirstOpenValid = P_INVALID ; Vector3d vtFirstOpenValid = V_INVALID ; // richiedo lunghezza superiore a diametro utensile più doppio offset radiale double dRefLen = ( bAllOpen ? 0 : 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 l'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) ; // salvo l'indice di questo lato aperto se si tratta del primo valido if ( ! bFirstOpenValid) { bFirstOpenValid = true ; ptFirstOpenValid = ptMid ; vtFirstOpenValid = vtMidOrt ; } // controllo se questa quantità è effettivamente fuori dal grezzo // ( privilegio i lati aperti che sono effettivamente sul bordo del grezzo) // punto iniziale nel sistema di riferimento globale Point3d ptStart ; pCompoClone->GetStartPoint( ptStart) ; ptStart.ToGlob( frPocket) ; // vettore d'uscita nel sistema di riferimento globale Vector3d vtMidOut = vtMidOrt ; vtMidOut.ToGlob( frPocket) ; // ricavo la SafeZ double dSafeZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeZ() ; Point3d ptOut = ptStart + vtMidOut * ( 0.5 * m_TParams.m_dDiam + GetOffsR() + ( 0.5 * m_TParams.m_dDiam + max( dSafeZ, m_dOpenMinSafe))) ; // controllo l'elevazione sopra a quel punto double dElev = 0. ; if ( ! GetElevation( m_nPhase, ptOut, frPocket.VersZ(), m_TParams.m_dDiam, m_TParams.m_dLen, frPocket.VersZ(), dElev)) return false ; // se non vi è elevazione, il lato aperto trovato è il privilegiato if ( dElev < EPS_SMALL) { bFound = true ; if ( nPriorityOpenEdge > -1) return true ; } } } dLenPrev = dLen ; } } else dLenPrev = 0 ; // vado alla successiva pPrevCrv = pCrv ; pCrv = ( bNextOk ? pNextCrv : nullptr) ; } // se non ho trovato un lato aperto valido fuori dal grezzo, ma ho trovato un lato aperto // generico accettabile if ( ! bFound && bFirstOpenValid) { ptMid = ptFirstOpenValid ; vtMidOrt = vtFirstOpenValid ; bFound = true ; } return bFound ; } //---------------------------------------------------------------------------- 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 ; if ( abs( m_dOpenEdgeRad) > 0 && m_dOpenEdgeRad < dRad) dRad = m_dOpenEdgeRad ; // ricavo i tratti omogenei ICRVCOMPOPOVECTOR vpCrvs ; if ( ! GetHomogeneousParts( pCrvCompo, vpCrvs)) return false ; // Offset esterno per CurveJ double dDiamJ = 1.05 * ( dDiam) + 2 * dOffR ; if ( abs( m_dOpenEdgeRad) > 0) dDiamJ = 100 * EPS_SMALL ; // NB. 1.05 serve per eccedere leggermente, in modo che il contro-offset della prima curva di svuotatura presenti // dei piccoli archi tra i chiusi e gli aperti ( in modo da svuotare bene lungo il chiuso) // curva finale da restituire PtrOwner pCrvCompo_final( CreateCurveComposite()) ; if ( IsNull( pCrvCompo_final)) return false ; // parametro iniziale del tratto corrente, sulla curva originale double dParS = 0. ; // scorro tutti i tratti omogenei nel vettore for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) { // recupero la proprietà della curva composita int nCurrTmpProp = vpCrvs[i]->GetTempProp() ; // aggiorno i parametri del lato aperto corrente sulla curva originale double dParE = dParS + 1. * vpCrvs[i]->GetCurveCount() ; // se aperta if ( nCurrTmpProp == 1) { 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 ( se chiusa -> la copio, se aperta -> copio l'estesa) 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, pSfrInc)) return false ; // creo un vettore con tutte le curve che potrebbero cadere, in parte, nella regione di incidenza ICRVCOMPOPOVECTOR vCrvToCheck ; for ( int i = 0 ; i < ( int)vCrvIsland.size() ; ++ i) vCrvToCheck.emplace_back( vCrvIsland[i]->Clone()) ; // aggiungo le isole // se la curva originale non è tutta Aperta -> devo aggiungere anche essa nelle curve da controllare bool bIsAllOpen = abs( abs( dParE - dParS) - pCrvCompo->GetCurveCount()) < EPS_SMALL ; if ( ! bIsAllOpen) { // recupero il tratto di curva prima e dopo dell'aperto corrente PtrOwner pCompoOther( ConvertCurveToComposite( pCrvCompo->CopyParamRange( dParE, dParS))) ; if ( IsNull( pCompoOther) || ! pCompoOther->IsValid()) return false ; vCrvToCheck.emplace_back( Release( pCompoOther)) ; // aggiungo il bordo } // scorro il vettore creato... for ( int c = 0 ; c < int( vCrvToCheck.size()) ; ++ c) { // 1) recupero la curva corrente PtrOwner pCrvCurr( CloneCurveComposite( vCrvToCheck[c])) ; if ( IsNull( pCrvCurr) || ! pCrvCurr->IsValid()) return false ; // 2) ricavo i tratti con proprietà uniformi ( Aperti/Chiusi ) ICRVCOMPOPOVECTOR vpCrvs ; if ( ! GetHomogeneousParts( pCrvCurr, vpCrvs)) return false ; // 3) considero solo i tratti chiusi for ( int cl = 0 ; cl < int( vpCrvs.size()) ; ++ cl) { if ( vpCrvs[cl]->GetTempProp() == 1) continue ; // 4) effettuo l'Offset della curva di metà dDiamJ OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( vpCrvs[cl], - dDiamJ * 0.5, ICurve::OFF_FILLET)) { m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ; return false ; } // 5) scorro tutte le curve di Offset che si sono formate, prendendo sempre la più lunga tra le rimanenti PtrOwner pOffLongestCrv( OffsCrv.GetLongerCurve()) ; while ( ! IsNull( pOffLongestCrv)) { // 6) creo la regione di incidenza di tale curva ( "Curva a fagiolo") PtrOwner pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ * 0.5, false, false)) ; if ( IsNull( pSfrBean) || ! pSfrBean->IsValid()) return false ; // inverto se necessario if ( AreOppositeVectorApprox( pSfrBean->GetNormVersor(), pSfrInc->GetNormVersor())) pSfrBean->Invert() ; // 7) se la "Regione a fagiolo" non influenza la regione di incidenza, la transcuro bool bDiscard = true ; for ( int cI = 0 ; cI < pSfrInc->GetChunkCount() && bDiscard ; ++ cI) for ( int cB = 0 ; cB < pSfrBean->GetChunkCount() && bDiscard ; ++ cB) bDiscard = ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrBean, cB) != REGC_INTERS) ; if ( ! bDiscard) { // 8) aggiorno la regione di incidenza if ( ! pSfrInc->Add( *pSfrBean)) return false ; } pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ; // passo al tratto offsettato successivo } } } // ATTENZIONE ! // L'algoritmo di allargamento presso i lati aperti è Euristico; io mi estendo a seconda della geometria del // lato aperto al di fuori del volume di svuotatura... Devo controllare di non rovinare delle zone al di fuori di // esso ! if ( pSfrLimit->IsValid()) { if ( ! pSfrInc->Subtract( *pSfrLimit)) return false ; // può capitare che la regione Limite mi crei più Chunk sulla pSfrInc // I chunk da togliere sono tutti quelli che si sono separati dalla pCrvBorder // NB. Il mio obiettivo è quello di avere un'unica curva con estremi i due tratti aperti estremanti if ( pSfrInc->GetChunkCount() > 1) { // se ottengo più chunks PtrOwner pNewSfrInc( CreateSurfFlatRegion()) ; if ( IsNull( pNewSfrInc)) return false ; // ricavo i punti iniziali e finali della curva di bordo Point3d ptS, ptE ; if ( ! pCrvBorder->GetStartPoint( ptS) || ! pCrvBorder->GetEndPoint( ptE)) return false ; // per ogni Chunk ( > 1 ) for ( int i = 0 ; i < pSfrInc->GetChunkCount() ; ++ i) { // bordo esterno PtrOwner pCrvEL( ConvertCurveToComposite( pSfrInc->GetLoop( i, 0))) ; if ( IsNull( pCrvEL) || ! pCrvEL->IsValid()) return false ; // se estremi in comune, la curva cercata è la seguente if ( pCrvEL->IsPointOn( ptS, 300 * EPS_SMALL) && pCrvEL->IsPointOn( ptE, 300 * EPS_SMALL)) { if ( ! pNewSfrInc->IsValid()) // se la nuova regione di incidenza è vuota... pNewSfrInc.Set( pSfrInc->CloneChunk( i)) ; // aggiorno.... else // se la nuova regione di incidenza non è vuota... if ( ! pNewSfrInc->Add( * pSfrInc->CloneChunk( i))) // aggiungo... return false ; } } // definisco quinfi la nuova regione di incidenza if ( ! pNewSfrInc->IsValid()) return false ; pSfrInc.Set( Release( pNewSfrInc)) ; } } // controllo se la curva è un'isola ( dall'area ) bool bIsIsland = false ; double dArea ; Plane3d plCheck ; if ( ! pCrvCompo->GetArea( plCheck, dArea)) return false ; bIsIsland = AreSameVectorEpsilon( plCheck.GetVersN(), - Z_AX, 10 * EPS_SMALL) ; // dalla regione di incidenza devo estrarre il tratto di bordo che ha per estremi i lati chiusi // ( stando al di fuori, quindi estendendomi all'esterno, della regione da svuotare ) PtrOwner pCrvNewBorder( ConvertCurveToComposite( pSfrInc->GetLoop( 0, 0))) ; if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid()) return false ; // imposto la curva come tutta aperta for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u) pCrvNewBorder->SetCurveTempProp( u, 1, 0) ; // se la curva originale era tutta aperta... ( il nuovo lato è il loop esterno della regione di incidenza) if ( bIsAllOpen) { // pulisco la curva originale pCrvBorder->Clear() ; if ( bIsIsland) { // se isola inserisco il loop interno della regione pCrvNewBorder.Set( ConvertCurveToComposite( pSfrInc->GetLoop( 0, 1))) ; if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid()) return false ; // imposto la curva come tutta aperta ( gira come girava già l'isola) for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u) pCrvNewBorder->SetCurveTempProp( u, 1, 0) ; } // se bordo esterno inserisco il bordo esterno return pCrvBorder->AddCurve( Release( pCrvNewBorder)) ; } // altrimenti la spezzo il loop della regione di incidenza nei punti iniziali e finali della curva aperta originale Point3d ptStart ; pCrvBorder->GetStartPoint( ptStart) ; Point3d ptEnd ; pCrvBorder->GetEndPoint( ptEnd) ; double dUTrimS, dUTrimE ; pCrvNewBorder->GetParamAtPoint( ptStart, dUTrimS, 6000 * EPS_SMALL) ; pCrvNewBorder->GetParamAtPoint( ptEnd, dUTrimE, 6000 * EPS_SMALL) ; // pulisco la curva originale pCrvBorder->Clear() ; if ( ! pCrvBorder->AddCurve( pCrvNewBorder->CopyParamRange( dUTrimS, dUTrimE)) || ! pCrvBorder->IsValid()) { // il lato aperto è troppo corto e FlatRegion bean si sono mergiate pCrvBorder->CopyFrom( pCrvCompo) ; } return true ; } //--------------------------------------------------------------------------- bool Pocketing::CreateSurfFrIncidence( const ICurveComposite* pCrv, const double dRad, ISurfFlatRegion* pSfrInc) { // controllo dei parametri if ( pCrv == nullptr || ! pCrv->IsValid()) return false ; pSfrInc->Clear() ; // creo la Fat Curve dalla curva *pCrv PtrOwner pSfrInc_tmp( GetSurfFlatRegionFromFatCurve( pCrv->Clone(), dRad + EPS_SMALL, false, false)) ; if ( IsNull( pSfrInc_tmp) || ! pSfrInc_tmp->IsValid()) return false ; pSfrInc->CopyFrom( pSfrInc_tmp) ; // pSfrInc ha sempre normale Z_AX ! // se la curva è chiusa, allora tutto il bordo è aperto if ( pCrv->IsClosed()) { pSfrInc->CopyFrom( pSfrInc_tmp) ; return pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0 ; } // La regione di incidenza non deve avere un bordo distante dRad dagli estremi del tratto aperto // per questo motivo, creo due FlatRegion a rettangolo che andrò a sottrarre alla pSfrInc_tmp // ( tolgo i semi-dischi agli estremi ) // 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, Point3d ptRef) { // se cerco semplicemente il tratto lineare chiuso più lungo ... if ( ! bOrder) { // la priorità è essagnata ai tratti lineari chiusi int i = 0 ; // <--- indice della curva corrente int nMax = - 1 ; double dLenMax = 0 ; const ICurve* pCrv = pCompo->GetFirstCurve() ; while ( pCrv != nullptr) { double dLen ; if ( pCrv->GetType() == CRV_LINE && pCrv->GetTempProp() == 0 && pCrv->GetLength( dLen) && dLen > dLenMax) { dLenMax = dLen ; nMax = i ; } ++ i ; pCrv = pCompo->GetNextCurve() ; } // se non trovato o troppo corto, cerco il tratto chiuso più lungo in generale if ( nMax < 0 || dLenMax < 2 * 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 { // se ho un punto di riferimento, allora come prima cosa controllo il lato chiuso più vicino if ( ptRef.IsValid()) { double dMinDist = INFINITO ; int nIndCrvMinDist = - 1 ; for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) { DistPointCurve DPC( ptRef, *pCompo->GetCurve( u)) ; double dCurrDist = INFINITO ; if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) { nIndCrvMinDist = u ; dMinDist = dCurrDist ; } } // se avessi curve alla stessa distanza ( circa) non controllo le altre, mi accontento della // prima curva trovata if ( nIndCrvMinDist != -1) { // controllo che il lato chiuso sia sufficientemente lungo double dLen = 0. ; pCompo->GetCurve( nIndCrvMinDist)->GetLength( dLen) ; if ( dLen > m_TParams.m_dDiam + 2 * GetOffsR() + 500 * EPS_SMALL) { pCompo->ChangeStartPoint( nIndCrvMinDist + 0.5) ; return true ; } } } // creo un vettore di indici che definisce l'ordine delle curve chiuse in base alla lunghezza INTVECTOR vInd ; vInd.reserve( pCompo->GetCurveCount()) ; // vettore di indici già utilizzati INTVECTOR vIndUsed ; vIndUsed.reserve( pCompo->GetCurveCount()) ; double dMaxLen = -INFINITO ; int nCurrInd = 0 ; bool bStop = false ; for ( int c = 0 ; c < pCompo->GetCurveCount() && ! bStop ; ++ c) { bStop = true ; for ( int i = 0 ; i < pCompo->GetCurveCount() ; ++ i) { int nProp_i ; // se la curva i-esima non è già stata considerata ed è chiusa... if ( find( vIndUsed.begin(), vIndUsed.end(), i) == vIndUsed.end() && pCompo->GetCurveTempProp( i, nProp_i, 0) && nProp_i == 0) { // creo la curva i-esima const ICurve* pCrv_i( pCompo->GetCurve( i)) ; double dLen_i ; if ( pCrv_i->GetLength( dLen_i) && dLen_i > dMaxLen) { // se di lunghezza maggiore alla soglia... dMaxLen = dLen_i ; nCurrInd = i ; bStop = false ; } } } vIndUsed.push_back( nCurrInd) ; vInd.push_back( nCurrInd) ; dMaxLen = -INFINITO ; } if ( vInd.empty()) { // se questa condizione fosse vera allora non sono riuscito ad entrare da nessun lato aperto in precedenza e non // ho nemmeno un lato chiuso disponibile per entrare... ( forzo l'entrata a metà del primo lato) pCompo->ChangeStartPoint( 0.5) ; return true ; } // ora che ho riempito il vettore di indici lo scorro e cerco di entrare in questi lati chiusi secondo l'ordine bool bOk = false ; for ( int i = 0 ; i < ( int)vInd.size() && !bOk ; ++ i) { const ICurve* pCrv( pCompo->GetCurve( vInd[i])) ; double dPar ; if ( GetParamForPtStartOnEdge( pCrv, pCompo, vCrvIsl, dPar)) { pCompo->ChangeStartPoint( vInd[i] + dPar) ; bOk = true ; } } if ( ! bOk) { // se non riesco ad entrare da nessun lato chiuso, considerando che in precedenza ho già provato ad // entrare da tutti i lati aperti... pCompo->ChangeStartPoint( vInd[0] + 0.5) ; return true ; } } return true ; } //---------------------------------------------------------------------------- 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) > 20 * 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() || pStmRef_Easy->GetTriangleCount() == 0) 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) ; int nDen = 2 ; // intervalli in cui suddivido la curva dMaxInt = max( 2.0, dMaxInt) ; // così se entra una volta controllo sempre dParT = 0.5 while ( nDen < dMaxInt + EPS_SMALL) { // EPS_SMALL per comprendere l'uguaglianza for ( int i = 1 ; i < nDen ; ++i) { if ( int( i % nDen) == 0) continue ; double dNum = 1.0 * i ; double dParT = dNum / nDen ; Point3d ptTest ; Vector3d vtPerpIn ; if ( pCrv->GetPointD1D2( dParT, ICurve::FROM_PLUS, ptTest, &vtPerpIn)) { PtrOwner pCompoClone( CloneCurveComposite( pCompo)) ; if ( IsNull( pCompoClone)) return false ; double dU ; pCompoClone->GetParamAtPoint( ptTest, dU) ; pCompoClone->ChangeStartPoint( dU) ; // se chiuso if ( pCrv->GetTempProp() == 0) { // se la curva è chiusa sposto il centro verso l'interno. // Devo essere sicuro di essere su una curva di Offset interno, altrimenti potrebbe essere valido // il punto trovato, però facendo poi il primo offeset tale punto potrebbe diventare un punto // di convergenza tra più chunks... vtPerpIn.Normalize() ; vtPerpIn.Rotate( Z_AX, 0, 1) ; // cos( pi/2) = 0, sin( pi/2) = 1 vtPerpIn *= (( 0.5 * 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 ; } // se aperto else { dPar = dParT ; return true ; } } } ++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, const Point3d& ptEndPrec, const Frame3d& frPocket, 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 ; int nInd = -1 ; // indice della prima curva che non è "raccordo" di Offset // scorro tutte le curve presenti in pCrv ( curva da cui devo entrare) for ( int i = 0 ; i < pCrv->GetCurveCount() ; ++ i) { // nProp0 -> #curva il cui Offset ha generato la curva i-esima di pCrv int nProp0 ; pCrv->GetCurveTempProp( i, nProp0, 0) ; // nProp1 -> #loop che contiene la curva espressa in nProp0 int nProp1 ; pCrv->GetCurveTempProp( i, nProp1, 1) ; if ( nProp0 > 0) { // se questa curva non è un "raccordo" di Offset // controllo per maggiore sicurezza che effettivamente nProp1 sia un indice valido per il vettore dei Loops e // che nProp0 non sia maggiore del numero di curve del loop nProp1-esimo del vettore dei Loops della pSrfToWork if ( nProp1 >= 0 && nProp1 < int( vCrvLoops.size()) && nProp0 < vCrvLoops[nProp1]->GetCurveCount()) { // aggiorno il vettore di indici ... if ( find( vIndex.begin(), vIndex.end(), nProp1) == vIndex.end()) vIndex.push_back( nProp1) ; // aggiorno la proprietà della curva da cui devo entrare int nTempProp ; vCrvLoops[nProp1]->GetCurveTempProp( nProp0 - 1, nTempProp, 0) ; pCrv->SetCurveTempProp( i, nTempProp, 0) ; // se la curva è aperta, aggiorno il Flag if ( nTempProp == 1 && ! bSomeOpen) bSomeOpen = true ; // salvo l'indice della prima curva trovata che non è un "raccordo" di Offset if ( nInd == -1) nInd = i ; } else pCrv->SetCurveTempProp( i, 0, 0) ; } else { pCrv->SetCurveTempProp( i, -2, 0) ; pCrv->SetCurveTempProp( i, -2, 1) ; } } // scorro tutte le curve incerte ( che derivano da raccordi ) if ( nInd != -1) pCrv->ChangeStartPoint( nInd) ; // la prima curva non è un "raccordo" di Offset for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++ i) { double dLen = 0. ; if ( pCrv->GetCurve( i)->GetLength( dLen) && dLen < 0.5 * m_TParams.m_dDiam) pCrv->SetCurveTempProp( i, 0, 0) ; else { int nTmpProp0, nTmpProp1 ; if (( pCrv->GetCurveTempProp( i, nTmpProp0, 0) && nTmpProp0 == -2) && ( pCrv->GetCurveTempProp( i, nTmpProp1, 1) && nTmpProp1 == -2)) { // copio la temp prop della curva precedente pCrv->SetCurveTempProp( i, pCrv->GetCurve( i-1)->GetTempProp(), 0) ; // se troppo corta, allora chiusa if ( pCrv->GetCurve( i)->GetLength( dLen) && dLen < 0.2 * m_TParams.m_dDiam) pCrv->SetCurveTempProp( i, 0, 0) ; } } } // se ho dei lati aperti... double dLenMax = EPS_SMALL ; int nCrvForMax = 0 ; if ( bSomeOpen) { // ... e sono al primo Step, cerco la curva Aperta più lunga ( scelgo la prima che trovo) ... if ( ! ptEndPrec.IsValid()) { for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { int nTmpProp = 0 ; double dLenAct = EPS_SMALL ; if ( pCrv->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 1 && pCrv->GetCurve( u)->GetLength( dLenAct) && dLenAct > dLenMax) { dLenMax = dLenAct ; nCrvForMax = u ; } } // il punto iniziale della curva sarà il punto iniziale della sottocurva trovata pCrv->ChangeStartPoint( nCrvForMax) ; } // ... se invece non sono al primo Step, cerco la prima curva aperta più vicina al punto finale else { int nIndClosesOpenCrv = -1 ; double dMinDist = INFINITO ; for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { if ( pCrv->GetCurve( u)->GetTempProp( 0) == 1) { DistPointCurve DPC( ptEndPrec, *pCrv->GetCurve( u)) ; double dCurrDist = INFINITO ; if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) { dMinDist = dCurrDist ; nIndClosesOpenCrv = u ; } } } // il punto iniziale della curva sarà il punto iniziale della sottocurva trovata pCrv->ChangeStartPoint( nIndClosesOpenCrv) ; } } // creo un vettore con tutti i Loops della pSwfToWork per i quali, mediante l'Offset, non hanno // generato alcuna curva presente nella pCrv ( quella da cui devo entrare) ICRVCOMPOPOVECTOR vOtherCrv ; for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) { bool bOk = true ; for ( int j = 0 ; j < int( vIndex.size()) && bOk ; ++ j) { if ( i == vIndex[j]) bOk = false ; } if ( bOk) vOtherCrv.emplace_back( ConvertCurveToComposite( vCrvLoops[i]->Clone())) ; } // cerchiamo un punto valido per l'entrata ... bMidOut = false ; if ( bSomeOpen) // se ho dei lati aperti, cerco un parametro ideale per entrare bMidOut = GetParamOnOpenSide( pCrv, vOtherCrv, frPocket, 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, ptEndPrec) ; // 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 ; } // per sicurezza cambio il suo punto iniziale a metà del lato più lungo ICRVCOMPOPOVECTOR VNULL ; PtrOwner pCrv_projCompo( ConvertCurveToComposite( pCrv_proj->Clone())) ; if ( IsNull( pCrv_projCompo)) return false ; for ( int u = 0 ; u < pCrv_projCompo->GetCurveCount() ; ++ u) pCrv_projCompo->SetCurveTempProp( u, 0, 0) ; AdjustContourStart( pCrv_projCompo, VNULL) ; SrfChunkDef.AddCurve( Release( pCrv_projCompo)) ; // 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 + 2 * 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) ; } } // se tutti lati aperti switch ( nClosedSide) { // TUTTI I LATI SONO APERTI case 0 : { // ricavo il box minimo della curva aperta ( passo dalla polyLine) PolyLine PL ; Point3d ptCen ; double dWidth ; if ( ! pCrvCompo->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) || ! PL.GetMinAreaRectangleXY( ptCen, vtDir, dWidth, dPocketSize)) return false ; // controllo dimY ( dHeight), se troppo estesa, non è un caso ottimizzato if ( dPocketSize > dDiam + TOLL_TRAPEZOID) return true ; // inverto il frame attuale frTrap.Set( ptCen, Z_AX, vtDir) ; if ( ! frTrap.IsValid()) return false ; // creo il rettangolo del Box pCrvTrap->AddPoint( Point3d( - 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ; pCrvTrap->AddLine( Point3d( 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ; pCrvTrap->AddLine( Point3d( 0.5 * dWidth, 0.5 * dPocketSize, 0)) ; pCrvTrap->AddLine( Point3d( - 0.5 * dWidth, 0.5 * dPocketSize, 0)) ; pCrvTrap->Close() ; pCrvTrap->ToGlob( frTrap) ; Point3d ptNewOrig ; pCrvTrap->GetStartPoint( ptNewOrig) ; frTrap.Set( ptNewOrig, Z_AX, vtDir) ; // imposto tutte le 4 curve come aperte for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, 1, 0) ; // imposto le basi nBase = 0 ; nSecondBase = 2 ; } break ; // 1 LATO CHIUSO ( LINEARE) case 1 : { int nType = -1 ; if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType != -1) { // imposto tutte le curve come aperte tranne la prima ( estendo il solo lato chiuso come base del Box) for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u == 0 ? 0 : 1, 0) ; // memorizzo la dimensione di svuotatura pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // imposto le basi nBase = 0 ; nSecondBase = 2 ; } } break ; // 2 LATI CHIUSI ( LINEARI, CONSECUTIVI O PARALLELI) case 2 : { // controllo se entrambi sono lineari if ( pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE && pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE) { // se lineari, ricavo i punti estremanti e le direzioni Point3d ptS0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartPoint( ptS0) ; Point3d ptS1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartPoint( ptS1) ; Point3d ptE0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetEndPoint( ptE0) ; Point3d ptE1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetEndPoint( ptE1) ; Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ; Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ; // CASO PARALLELI ( la base è indifferente) if ( AreOppositeVectorEpsilon( vtDir0, vtDir1, 5 * EPS_SMALL)) { int nType = -1 ; // prendo come base il primo chiuso if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType != -1) { // memorizzo la dimensione di svuotatura pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // controllo che la distanza tra i due chiusi sia effettivamente circa il raggio double dDist = 0. ; DistPointCurve DPL( ptS0, *pCrvCompo->GetCurve( vIndClosedSides[1]), false) ; if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOLL_TRAPEZOID) { // imposto tutte le curve di indice dispari aperte for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u % 2 == 0 ? 0 : 1, 0) ; // imposto le basi nBase = 0 ; nSecondBase = 2 ; } else pCrvTrap->Clear() ; } } // CASO CONSECUTIVI bool bOk_0_1 = AreSamePointApprox( ptS1, ptE0) ; // chiuso1 . chiuso0 bool bOk_1_0 = AreSamePointApprox( ptS0, ptE1) ; // chiuso0 . chiuso1 if ( bOk_0_1 || bOk_1_0) { // provo con il primo lato int nType = -1 ; // prendo come base il primo chiuso if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType == -1) { // provo con l'altro if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType != -1) { // spaw tra gli indici e flags swap( vIndClosedSides[0], vIndClosedSides[1]) ; swap( bOk_0_1, bOk_1_0) ; } } Point3d ptH1, ptH2 ; if ( nType == 1 || nType == 2) { double dLen1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetLength( dLen1) ; pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // i lati inclinati chiusi definiscono il Box if ( abs( dLen1 * ( vtDir0 ^ vtDir1).Len() - dPocketSize) < TOLL_TRAPEZOID) { // creo la curva a trapezio if ( bOk_0_1) { pCrvTrap->GetCurve( 2)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 3)->GetEndPoint( ptH2) ; pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->Close() ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ; } else { pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ; pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->AddLine( ptS1) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; pCrvTrap->SetCurveTempProp( 0, 0, 0) ; for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u) pCrvTrap->SetCurveTempProp( u, 1, 0) ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ; } // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } else pCrvTrap->Clear() ; } else if ( nType == 0) { if ( bOk_0_1) { pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ; pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->Close() ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ; pCrvTrap->ChangeStartPoint( 1.) ; // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } else { pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddLine( ptH1) ; pCrvTrap->AddLine( ptH2) ; pCrvTrap->AddLine( ptS1) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ; pCrvTrap->SetCurveTempProp( 0, 0, 0) ; for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u) pCrvTrap->SetCurveTempProp( u, 1, 0) ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ; pCrvTrap->ChangeStartPoint( 1.) ; // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } } } } } break ; // 3 LATI CHIUSI ( LINEARI e CONSECUTIVI) case 3 : { // prendo i 3 lati chiusi const ICurve* pCrv0 = pCrvCompo->GetCurve( vIndClosedSides[0]) ; const ICurve* pCrv1 = pCrvCompo->GetCurve( vIndClosedSides[1]) ; const ICurve* pCrv2 = pCrvCompo->GetCurve( vIndClosedSides[2]) ; if ( pCrv0 == nullptr || pCrv1 == nullptr || pCrv2 == nullptr) return false ; // controllo che siano lineari if ( pCrv0->GetType() == CRV_LINE && pCrv1->GetType() == CRV_LINE && pCrv2->GetType() == CRV_LINE) { // prendo i punti iniziali e finali Point3d ptStart0 ; pCrv0->GetStartPoint( ptStart0) ; Point3d ptEnd0 ; pCrv0->GetEndPoint( ptEnd0) ; Point3d ptStart1 ; pCrv1->GetStartPoint( ptStart1) ; Point3d ptEnd1 ; pCrv1->GetEndPoint( ptEnd1) ; Point3d ptStart2 ; pCrv2->GetStartPoint( ptStart2) ; Point3d ptEnd2 ; pCrv2->GetEndPoint( ptEnd2) ; // controllo che siano consecutivi e cambio l'ordine se necessario bool bOk = true ; if ( AreSamePointApprox( ptEnd0, ptStart1) && AreSamePointApprox( ptEnd1, ptStart2)) ; else if ( AreSamePointApprox( ptEnd1, ptStart2) && AreSamePointApprox( ptEnd2, ptStart0)) { swap( vIndClosedSides[0], vIndClosedSides[1]) ; swap( vIndClosedSides[1], vIndClosedSides[2]) ; } else if ( AreSamePointApprox( ptEnd2, ptStart0) && AreSamePointApprox( ptEnd0, ptStart1)) { swap( vIndClosedSides[0], vIndClosedSides[2]) ; swap( vIndClosedSides[2], vIndClosedSides[1]) ; } else // se non consecutivi bOk = false ; // se i tre lati sono ( mediante lo swap) consecutivi... if ( bOk) { // controllo se la dimensione Y del lato 1 è valida ( il resto è gestito di seguito) int nType = -1 ; if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap)) return false ; if ( nType == 1) { // bisgna controllare la lunghezza dei chiusi double dLen0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetLength( dLen0) ; double dLen2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetLength( dLen2) ; pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ; Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ; Vector3d vtDir2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetStartDir( vtDir2) ; // i lati inclinati chiusi definiscono il Box if ( abs( dLen2 * ( vtDir1 ^ vtDir2).Len() - dPocketSize) < TOLL_TRAPEZOID && abs( dLen0 * ( vtDir1 ^ vtDir0).Len() - dPocketSize) < TOLL_TRAPEZOID) { // creo la curva a trapezio pCrvTrap->Clear() ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ; pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[2])->Clone()) ; pCrvTrap->Close() ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) pCrvTrap->SetCurveTempProp( u, u < 3 ? 0 : 1 , 1) ; pCrvTrap->ChangeStartPoint( 1.) ; // imposto le basi nBase = 0 ; nSecondBase = pCrvTrap->GetCurveCount() - 2 ; } } } } } } // se non ho trovato delle basi, allora cerco in maniera generale un trapezoide ( se esiste) if ( nBase == -1) { bool bOK = false ; for ( int u = 0 ; u < pCrvCompo->GetCurveCount() && !bOK ; ++ u) { // cerco un lato chiuso ( esiste per forza) if ( pCrvCompo->GetCurve( u)->GetTempProp( 0) == 0) { // controllo se il lato corrente può essere una base int nType = -1 ; if ( ! GetBoxCrvOptTrap( u, pCrvCompo, dDiam, nType, pCrvTrap)) return false ; // se è una base valida if ( nType != -1) { // ricavo la dimensione di svuotatura pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // ricavo la sua direzione iniziale Vector3d vtBaseDir ; pCrvCompo->GetCurve( u)->GetStartDir( vtBaseDir) ; // ricavo il suo punto iniziale Point3d ptBaseStart ; pCrvCompo->GetCurve( u)->GetStartPoint( ptBaseStart) ; // ricavo il suo punto finale Point3d ptBaseEnd ; pCrvCompo->GetCurve( u)->GetEndPoint( ptBaseEnd) ; // cerco il lato chiuso lineare parallelo ad esso e distante circa dPocketSize for ( int uu = 0 ; uu < pCrvCompo->GetCurveCount() && !bOK ; ++ uu) { if ( uu == u || pCrvCompo->GetCurve( uu)->GetType() != CRV_LINE || pCrvCompo->GetCurve( uu)->GetTempProp() != 0) continue ; Vector3d vtSecondBaseDir ; pCrvCompo->GetCurve( uu)->GetStartDir( vtSecondBaseDir) ; if ( ! AreOppositeVectorEpsilon( vtBaseDir, vtSecondBaseDir, 5 * EPS_SMALL)) continue ; Point3d ptSecondBaseStart ; pCrvCompo->GetCurve( uu)->GetStartPoint( ptSecondBaseStart) ; double dDist = 0. ; DistPointCurve DPL( ptBaseStart, *pCrvCompo->GetCurve( uu), false) ; if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOLL_TRAPEZOID) { nBase = u ; nSecondBase = uu ; if ( ! PreparareTrapezoidTwoBases( pCrvCompo, dDiam, nType, nBase, nSecondBase, bOK, pCrvTrap)) return false ; } } } } } } // la curva a trapezio deve evere almeno 4 lati if ( pCrvTrap->GetCurveCount() < 4 || nBase == -1 || nSecondBase == -1) { pCrvTrap->Clear() ; return true ; } // ricostruisco il frame del trapezio Point3d ptS ; pCrvTrap->GetStartPoint( ptS) ; Vector3d vtS ; pCrvTrap->GetStartDir( vtS) ; if ( ! frTrap.Set( ptS, Z_AX, vtS) || ! frTrap.IsValid()) return false ; // se parametro MaxOptSize non compatibile => non è ottimizzato 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::GetBoxCrvOptTrap( const int nCrv, const ICurveComposite* pCrvCompo, const double dDiam, int& nType, ICurveComposite* pCrvBox) { nType = - 1 ; // prendo la curva const ICurve* pCrvCurr = pCrvCompo->GetCurve( nCrv) ; if ( pCrvCurr == nullptr) return false ; // controllo se lineare, altrimenti passo alla successiva if ( pCrvCurr->GetType() != CRV_LINE) return true ; // prendo la direzione del tratto lineare Vector3d vtDir ; pCrvCurr->GetStartDir( vtDir) ; // prendo il punto iniziale Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ; // creo il riferimento basato su questo tratto Frame3d frTrap ; frTrap.Set( ptStart, Z_AX, vtDir) ; if ( ! frTrap.IsValid()) return false ; // porto la curva Compo ( clonandola) nel frame e calcolo il suo BBox BBox3d BBox ; PtrOwner pCrvCompo_c( CloneCurveComposite( pCrvCompo)) ; if ( IsNull( pCrvCompo_c) || ! pCrvCompo_c->IsValid() || ! pCrvCompo_c->ToLoc( frTrap) || ! pCrvCompo_c->GetLocalBBox( BBox)) return false ; // controllo dimY e dimX per il box bool bDimYOk = BBox.GetDimY() < dDiam + TOLL_TRAPEZOID && BBox.GetMin().y > - TOLL_TRAPEZOID ; bool bDimXOk = BBox.GetDimX() < dDiam + TOLL_TRAPEZOID && BBox.GetMin().y > - TOLL_TRAPEZOID ; // controllo dimensioni ammissibili if ( ! bDimXOk && ! bDimYOk) return true ; // se nessuna else if ( ! bDimXOk) nType = 1 ; // se dimY else if ( ! bDimYOk) nType = 0 ; // se dimX else nType = 2 ; // se entrambe // creo il rettangolo del Box pCrvBox->Clear() ; pCrvBox->AddPoint( BBox.GetMin()) ; pCrvBox->AddLine( BBox.GetMin() + BBox.GetDimX() * X_AX) ; pCrvBox->AddLine( BBox.GetMax()) ; pCrvBox->AddLine( BBox.GetMax() - BBox.GetDimX() * X_AX) ; pCrvBox->Close() ; // determino il punto inziale della curva if ( nType == 0) pCrvBox->ChangeStartPoint( 1.) ; pCrvBox->ToGlob( frTrap) ; return true ; } //---------------------------------------------------------------------------- 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 && nClosedEdge != 2) return true ; // se sono 2 if ( nClosedEdge == 2) { // ricavo direzioni Vector3d vtStart0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtStart0) ; Vector3d vtStart1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtStart1) ; // se opposte if ( AreOppositeVectorEpsilon( vtStart0, vtStart1, 5 * EPS_SMALL)) { } } else { // se sono 3 // prendo i 3 lati chiusi const ICurve* pCrv0 = pCrvCompo->GetCurve( vIndClosedSides[0]) ; const ICurve* pCrv1 = pCrvCompo->GetCurve( vIndClosedSides[1]) ; const ICurve* pCrv2 = pCrvCompo->GetCurve( vIndClosedSides[2]) ; if ( pCrv0 == nullptr || pCrv1 == nullptr || pCrv2 == nullptr) return false ; // controllo che siano lineari if ( pCrv0->GetType() != CRV_LINE || pCrv1->GetType() != CRV_LINE || pCrv2->GetType() != CRV_LINE) 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 ; // -------------- 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 ; 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, const double dDiam, const int nType, int& nBase, int& nSecondBase, bool& bOk, ICurveComposite* pCrvTrap) { // controllo parametri if ( pCrvCompo == nullptr) return false ; bOk = false ; // le parti tra le due basi devono essere tutte omogenee ( o tutte aperte o tutte chiuse) // pCrvTest0 sarà la parte destra, pCrvTest1 la parte sinistra Point3d ptSB0, ptSB1 ,ptEB0, ptEB1 ; PtrOwner pCrvTest0( CloneCurveComposite( pCrvCompo)) ; PtrOwner pCrvTest1( CloneCurveComposite( pCrvCompo)) ; pCrvCompo->GetCurve( nBase)->GetStartPoint( ptSB0) ; pCrvCompo->GetCurve( nBase)->GetEndPoint( ptEB0) ; pCrvCompo->GetCurve( nSecondBase)->GetStartPoint( ptSB1) ; pCrvCompo->GetCurve( nSecondBase)->GetEndPoint( ptEB1) ; pCrvTest0->ChangeStartPoint( nBase) ; pCrvTest1->ChangeStartPoint( nSecondBase) ; double dUTrim0, dUTrim1 ; pCrvTest0->GetParamAtPoint( ptSB1, dUTrim0) ; pCrvTest1->GetParamAtPoint( ptSB0, dUTrim1) ; pCrvTest0->TrimStartEndAtParam( 1, dUTrim0) ; pCrvTest1->TrimStartEndAtParam( 1, dUTrim1) ; // controllo che la parte destra si uniforme per le TmpProp for ( int u = 0 ; u < pCrvTest0->GetCurveCount() - 1 ; ++ u) { int nPropAct, nPropSucc ; if ( pCrvTest0->GetCurveTempProp( u, nPropAct, 0) && pCrvTest0->GetCurveTempProp( u + 1, nPropSucc, 0) && nPropAct != nPropSucc) return true ; // se TmpProp diverse => non è un caso ottimizzato } // controllo che la parte sinistra sia uniforme per le TmpProp for ( int u = 0 ; u < pCrvTest1->GetCurveCount() - 1 ; ++ u) { int nPropAct, nPropSucc ; if ( pCrvTest1->GetCurveTempProp( u, nPropAct, 0) && pCrvTest1->GetCurveTempProp( u + 1, nPropSucc, 0) && nPropAct != nPropSucc) return true ; // se TmpProp diverse => non è un caso ottimizzato } // se lato destro aperto ( estendo il punto finale della base principale e il punto iniziale // della base secondaria fino al lato destro del box) bool bCopyRight = false ; if ( pCrvTest0->GetCurve( 0)->GetTempProp( 0) == 1) { if ( nType == 0) { pCrvTrap->GetCurve( 0)->GetStartPoint( ptEB0) ; pCrvTrap->GetCurve( 1)->GetStartPoint( ptSB1) ; } else { pCrvTrap->GetCurve( 0)->GetEndPoint( ptEB0) ; pCrvTrap->GetCurve( 1)->GetEndPoint( ptSB1) ; } } // se lato destro chiuso else bCopyRight = true ; // se lato sinistro aperto ( estendo il punto finale della base secondaria e il punto iniziale // della base primaria dino al lato sinistro del box) bool bCopyLeft = false ; if ( pCrvTest1->GetCurve( 0)->GetTempProp( 0) == 1) { if ( nType == 0) { pCrvTrap->GetCurve( 2)->GetEndPoint( ptEB1) ; pCrvTrap->GetCurve( 0)->GetStartPoint( ptSB0) ; } else { pCrvTrap->GetCurve( 1)->GetEndPoint( ptEB1) ; pCrvTrap->GetCurve( 2)->GetEndPoint( ptSB0) ; } } // se lato sinistro chiuso else bCopyLeft = true ; // creo la curva da restituire pCrvTrap->Clear() ; pCrvTrap->AddPoint( ptSB0) ; pCrvTrap->AddLine( ptEB0) ; nBase = 0 ; if ( bCopyRight) pCrvTrap->AddCurve( Release( pCrvTest0)) ; else { pCrvTrap->AddLine( ptSB1) ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta } nSecondBase = pCrvTrap->GetCurveCount() ; pCrvTrap->AddLine( ptEB1) ; if ( bCopyLeft) pCrvTrap->AddCurve( Release( pCrvTest1)) ; else { pCrvTrap->Close() ; pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta } // verifico dimensione x della svuotatura nel caso tutto chiuso if ( bCopyLeft && bCopyRight) { double dLen0 = 0, dLen2 = 0 ; pCrvTrap->GetCurve( nBase)->GetLength( dLen0) ; pCrvTrap->GetCurve( nSecondBase)->GetLength( dLen2) ; if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL) { pCrvTrap->Clear() ; return true ; } } bOk = true ; return true ; } //---------------------------------------------------- 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() ; 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[2] * 4 + 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 ; 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 ; } //---------------------------------------------------------------------------- //-------------------------- 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, int( FEED_DIVISOR * GetMinFeed()), 0) ; else // semi cerchi in tangenza pCrv->SetCurveTempProp( u, int( FEED_DIVISOR * GetMaxFeed()), 0) ; } } else if ( nOptType == 1) { // Trapezoidi for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempProp( u, int( 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, int( FEED_DIVISOR * GetMinFeed()), 0) ; else pCrv->SetCurveTempProp( u, int( FEED_DIVISOR * GetMaxFeed()), 0) ; } } else if ( nOptType == 1) { // Trapezoidi for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) pCrv->SetCurveTempProp( u, int( 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, int ( 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, int( FEED_DIVISOR * GetMaxFeed()) , 0) ; else pCompo->SetCurveTempProp( u, int( 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, int( 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, int( 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, int( FEED_DIVISOR * GetMinFeed()), 0) ; else // Segmento in uscita pCrv->SetCurveTempProp( pCrv->GetCurveCount() - 1, int( 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, int( 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, int( 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, int( 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, int( 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, int( FEED_DIVISOR * GetMinFeed()), 0) ; else pCrv->SetCurveTempProp( u, int( 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, int( 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, int( 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, int( 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, int( 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, int( 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 ; }