Files
EgtMachKernel/Pocketing.cpp
T
Riccardo Elitropi ac8feb7a17 EgtMachKernel :
- utilizzo della funzione limite per LeadIn/Out.
2024-01-11 17:57:23 +01:00

13190 lines
548 KiB
C++
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//----------------------------------------------------------------------------
// 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 <algorithm>
using namespace std ;
//------------------------------ Errors --------------------------------------
// 2401 = "Error in Pocketing : UpdateToolData failed"
// 2402 = "Error in Pocketing : Open Contour"
// 2403 = "Error in Pocketing : Contour Not Flat"
// 2404 = "Error in Pocketing : Tool Not Perpendicular to Flat Area"
// 2405 = "Error in Pocketing : Empty RawBox"
// 2406 = "Error in Pocketing : Depth not computable"
// 2408 = "Error in Pocketing : Entity GetElevation"
// 2409 = "Error in Pocketing : missing aggregate from bottom"
// 2410 = "Error in Pocketing : path too far from part sides"
// 2411 = "Error in Pocketing : toolpath allocation failed"
// 2412 = "Error in Pocketing : Offset not computable"
// 2413 = "Error in Pocketing : Toolpath not computable"
// 2414 = "Error in Pocketing : Approach not computable"
// 2415 = "Error in Pocketing : LeadIn not computable"
// 2416 = "Error in Pocketing : LeadOut not computable"
// 2417 = "Error in Pocketing : Retract not computable"
// 2418 = "Error in Pocketing : Link not computable"
// 2419 = "Error in Pocketing : Linear Approx not computable"
// 2420 = "Error in Pocketing : Return toolpath not computable"
// 2421 = "Error in Pocketing : Chaining failed"
// 2422 = "Error in Pocketing : Tool MaxMaterial too small (xxx)"
// 2423 = "Error in Pocketing : axes values not calculable"
// 2424 = "Error in Pocketing : outstroke xxx"
// 2425 = "Error in Pocketing : link movements not calculable"
// 2426 = "Error in Pocketing : link outstroke xxx"
// 2427 = "Error in Pocketing : post apply not calculable"
// 2428 = "Error in Pocketing : Tool loading failed"
// 2429 = "Error in Pocketing : machining depth (xxx) bigger than MaxDepth (yyy)"
// 2430 = "Error in Pocketing : adjust open edges failed"
// 2431 = "Error in Pocketing : LeadIn with Mill NoTip in material"
// 2432 = "Error in Pocketing : Mirror for Double calculation failed"
// 2451 = "Warning in Pocketing : Skipped entity (xx)"
// 2452 = "Warning in Pocketing : No machinable pocket"
// 2453 = "Warning in Pocketing : Tool name changed (xx)"
// 2454 = "Warning in Pocketing : Tool data changed (xx)"
// 2455 = "Warning in Pocketing : skipped Path too short"
// 2456 = "Warning in Pocketing : machining step too small (xx)"
// 2457 = "Warning in Pocketing : machining step (xxx) bigger than MaxMaterial (yyy)"
// 2458 = "Warning in Pocketing : machining depth (xxx) bigger than MaxMaterial (yyy)"
//----------------------------------------------------------------------------
static string KEY_OPEN = "OPEN" ;
static string KEY_TOOL = "VTTOOL" ;
static int LINK_CURVE_PROP = -3 ;
static double FEED_DIVISOR = 1000.0 ;
static double TOLL_TRAPEZOID = 50 * EPS_SMALL ;
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 ;
}
catch( ...) {
delete pPock ;
return nullptr ;
}
}
// ritorno l'oggetto
return pPock ;
}
//----------------------------------------------------------------------------
bool
Pocketing::Dump( string& sOut, bool bMM, const char* szNewLine) const
{
sOut += GetClassName() + "[mm]" + szNewLine ;
sOut += KEY_PHASE + EQUAL + ToString( m_nPhase) + szNewLine ;
sOut += KEY_IDS + EQUAL + ToString( m_vId) + szNewLine ;
for ( int i = 0 ; i < m_Params.GetSize() ; ++ i)
sOut += m_Params.ToString( i) + szNewLine ;
for ( int i = 0 ; i < m_TParams.GetSize() ; ++ i)
sOut += m_TParams.ToString( i) + szNewLine ;
sOut += KEY_NUM + EQUAL + ToString( m_nPockets) + szNewLine ;
sOut += KEY_STAT + EQUAL + ToString( m_nStatus) + szNewLine ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::Save( int nBaseId, STRVECTOR& vString) const
{
try {
int nSize = 1 + m_Params.GetSize() + m_TParams.GetSize() + 3 ;
vString.insert( vString.begin(), nSize, "") ;
int k = - 1 ;
if ( ! SetVal( KEY_IDS, m_vId, vString[++k]))
return false ;
for ( int i = 0 ; i < m_Params.GetSize() ; ++ i) {
string sParam = m_Params.ToString( i) ;
if ( ! sParam.empty())
vString[++k] = sParam ;
}
for ( int i = 0 ; i < m_TParams.GetSize() ; ++ i)
vString[++k] = m_TParams.ToString( i) ;
if ( ! SetVal( KEY_PHASE, m_nPhase, vString[++k]))
return false ;
if ( ! SetVal( KEY_NUM, m_nPockets, vString[++k]))
return false ;
if ( ! SetVal( KEY_STAT, m_nStatus, vString[++k]))
return false ;
vString.resize( k + 1) ;
}
catch( ...) {
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::Load( const STRVECTOR& vString, int nBaseGdbId)
{
int nSize = int( vString.size()) ;
// lista identificativi geometrie da lavorare
int k = - 1 ;
if ( k >= nSize - 1 || ! GetVal( vString[++k], KEY_IDS, m_vId))
return false ;
for ( auto& Sel : m_vId)
Sel.nId += nBaseGdbId ;
// parametri lavorazione
for ( int i = 0 ; i < m_Params.GetSize() ; ++ i) {
int nKey ;
if ( k >= nSize - 1 || ! m_Params.FromString( vString[++k], nKey) || nKey != i) {
if ( m_Params.IsOptional( i))
-- k ;
else
return false ;
}
}
// parametri utensile
for ( int i = 0 ; i < m_TParams.GetSize() ; ++ i) {
int nKey ;
if ( k >= nSize - 1 || ! m_TParams.FromString( vString[++k], nKey) || nKey != i)
return false ;
}
// parametri di stato
while ( k < nSize - 1) {
// separo chiave da valore
string sKey, sVal ;
SplitFirst( vString[++k], "=", sKey, sVal) ;
// leggo
if ( sKey == KEY_PHASE) {
if ( ! FromString( sVal, m_nPhase))
return false ;
}
else if ( sKey == KEY_NUM) {
if ( ! FromString( sVal, m_nPockets))
return false ;
}
else if ( sKey == KEY_STAT) {
if ( ! FromString( sVal, m_nStatus))
return false ;
}
}
return true ;
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
Pocketing::Pocketing( void)
{
m_Params.m_sName = "*" ;
m_Params.m_sToolName = "*" ;
m_TParams.m_sName = "*" ;
m_TParams.m_sHead = "*" ;
m_dTHoldBase = 0 ;
m_dTHoldLen = 0 ;
m_dTHoldDiam = 0 ;
m_dMaxHelixRad = INFINITO ;
m_nStatus = MCH_ST_TO_VERIFY ;
m_nPockets = 0 ;
m_bTiltingTab = false ;
m_bAboveHead = true ;
m_bAggrBottom = false ;
m_bOpenOutRaw = false ;
m_dOpenMinSafe = 0 ;
}
//----------------------------------------------------------------------------
bool
Pocketing::Prepare( const string& sMillName)
{
// verifico il gestore lavorazioni
if ( m_pMchMgr == nullptr)
return false ;
// recupero il gestore DB utensili della macchina corrente
ToolsMgr* pTMgr = m_pMchMgr->GetCurrToolsMgr() ;
if ( pTMgr == nullptr)
return false ;
// recupero il gestore DB lavorazioni della macchina corrente
MachiningsMgr* pMMgr = m_pMchMgr->GetCurrMachiningsMgr() ;
if ( pMMgr == nullptr)
return false ;
// ricerca della lavorazione di libreria con il nome indicato
const PocketingData* pDdata = GetPocketingData( pMMgr->GetMachining( sMillName)) ;
if ( pDdata == nullptr)
return false ;
m_Params = *pDdata ;
// ricerca dell'utensile usato dalla lavorazione
const ToolData* pTdata = pTMgr->GetTool( m_Params.m_ToolUuid) ;
if ( pTdata == nullptr)
return false ;
m_TParams = *pTdata ;
m_Params.m_sToolName = m_TParams.m_sName ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::SetParam( int nType, bool bVal)
{
switch ( nType) {
case MPA_INVERT :
if ( bVal != m_Params.m_bInvert)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_bInvert = bVal ;
return true ;
case MPA_TOOLINVERT :
if ( bVal != m_Params.m_bToolInvert)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_bToolInvert = bVal ;
return true ;
}
return false ;
}
//----------------------------------------------------------------------------
bool
Pocketing::SetParam( int nType, int nVal)
{
switch ( nType) {
case MPA_LEADINTYPE :
if ( ! m_Params.VerifyLeadInType( nVal))
return false ;
if ( nVal != m_Params.m_nLeadInType)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_nLeadInType = nVal ;
return true ;
case MPA_LEADOUTTYPE :
if ( ! m_Params.VerifyLeadOutType( nVal))
return false ;
if ( nVal != m_Params.m_nLeadOutType)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_nLeadOutType = nVal ;
return true ;
case MPA_SCC :
if ( ! m_Params.VerifySolCh( nVal))
return false ;
if ( nVal != m_Params.m_nSolCh)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_nSolCh = nVal ;
return true ;
case MPA_SUBTYPE :
if ( ! m_Params.VerifySubType( nVal))
return false ;
if ( nVal != m_Params.m_nSubType)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_nSubType = nVal ;
return true ;
}
return false ;
}
//----------------------------------------------------------------------------
bool
Pocketing::SetParam( int nType, double dVal)
{
switch ( nType) {
case MPA_SPEED :
if ( ! m_TParams.VerifySpeed( dVal))
return false ;
if ( abs( m_TParams.m_dSpeed - dVal) < EPS_MACH_ANG_PAR)
dVal = 0 ;
if ( abs( dVal - m_Params.m_dSpeed) > EPS_MACH_ANG_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dSpeed = dVal ;
return true ;
case MPA_FEED :
if ( abs( m_TParams.m_dFeed - dVal) < EPS_MACH_LEN_PAR)
dVal = 0 ;
if ( abs( dVal - m_Params.m_dFeed) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dFeed = dVal ;
return true ;
case MPA_STARTFEED :
if ( abs( m_TParams.m_dStartFeed - dVal) < EPS_MACH_LEN_PAR)
dVal = 0 ;
if ( abs( dVal - m_Params.m_dStartFeed) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dStartFeed = dVal ;
return true ;
case MPA_ENDFEED :
if ( abs( m_TParams.m_dEndFeed - dVal) < EPS_MACH_LEN_PAR)
dVal = 0 ;
if ( abs( dVal - m_Params.m_dEndFeed) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dEndFeed = dVal ;
return true ;
case MPA_TIPFEED :
if ( abs( m_TParams.m_dTipFeed - dVal) < EPS_MACH_LEN_PAR)
dVal = 0 ;
if ( abs( dVal - m_Params.m_dTipFeed) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dTipFeed = dVal ;
return true ;
case MPA_OFFSR :
if ( abs( m_TParams.m_dOffsR - dVal) < EPS_MACH_LEN_PAR)
dVal = UNKNOWN_PAR ;
if ( abs( dVal - m_Params.m_dOffsR) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dOffsR = dVal ;
return true ;
case MPA_OFFSL :
if ( abs( m_TParams.m_dOffsL - dVal) < EPS_MACH_LEN_PAR)
dVal = UNKNOWN_PAR ;
if ( abs( dVal - m_Params.m_dOffsL) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dOffsL = dVal ;
return true ;
case MPA_DEPTH : {
string sVal = ToString( dVal) ;
if ( sVal != m_Params.m_sDepth)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_sDepth = sVal ;
} return true ;
case MPA_STARTPOS :
if ( abs( dVal - m_Params.m_dStartPos) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dStartPos = dVal ;
return true ;
case MPA_STEP :
if ( abs( dVal - m_Params.m_dStep) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dStep = dVal ;
return true ;
case MPA_SIDESTEP :
if ( abs( dVal - m_Params.m_dSideStep) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dSideStep = dVal ;
return true ;
case MPA_SIDEANGLE :
if ( abs( dVal - m_Params.m_dSideAngle) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dSideAngle = dVal ;
return true ;
case MPA_LITANG :
if ( abs( dVal - m_Params.m_dLiTang) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dLiTang = dVal ;
return true ;
case MPA_LIELEV :
if ( abs( dVal - m_Params.m_dLiElev) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dLiElev = dVal ;
return true ;
case MPA_LOTANG :
if ( abs( dVal - m_Params.m_dLoTang) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dLoTang = dVal ;
return true ;
case MPA_EPICYCLESRAD :
if ( abs( dVal - m_Params.m_dEpicyclesRad) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dEpicyclesRad = dVal ;
return true ;
case MPA_EPICYCLESDIST :
if ( abs( dVal - m_Params.m_dEpicyclesDist) > EPS_MACH_LEN_PAR)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_dEpicyclesDist = dVal ;
return true ;
}
return false ;
}
//----------------------------------------------------------------------------
bool
Pocketing::SetParam( int nType, const string& sVal)
{
switch ( nType) {
case MPA_TOOL : {
const ToolData* pTdata ;
if ( ! m_Params.VerifyTool( m_pMchMgr->GetCurrToolsMgr(), sVal, pTdata))
return false ;
if ( ! SameTool( m_TParams, *pTdata))
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_sToolName = sVal ;
m_Params.m_ToolUuid = pTdata->m_Uuid ;
m_TParams = *pTdata ;
} return true ;
case MPA_DEPTH_STR :
if ( sVal != m_Params.m_sDepth)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_sDepth = sVal ;
return true ;
case MPA_SYSNOTES :
if ( sVal != m_Params.m_sSysNotes)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_sSysNotes = sVal ;
return true ;
case MPA_USERNOTES :
if ( sVal != m_Params.m_sUserNotes)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_sUserNotes = sVal ;
return true ;
case MPA_INITANGS :
if ( sVal != m_Params.m_sInitAngs)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_sInitAngs = sVal ;
return true ;
case MPA_BLOCKEDAXIS :
if ( sVal != m_Params.m_sBlockedAxis)
m_nStatus |= MCH_ST_PARAM_MODIF ;
m_Params.m_sBlockedAxis = sVal ;
return true ;
}
return false ;
}
//----------------------------------------------------------------------------
bool
Pocketing::SetGeometry( const SELVECTOR& vIds)
{
// verifico validità gestore DB geometrico
if ( m_pGeomDB == nullptr)
return false ;
// reset della geometria corrente
m_vId.clear() ;
// verifico che gli identificativi rappresentino delle entità ammissibili (tutte curve o tutte facce)
int nType = GEO_NONE ;
for ( const auto& Id : vIds) {
// test sull'entità
int nSubs ;
if ( ! VerifyGeometry( Id, nSubs, nType)) {
string sInfo = "Warning in Pocketing : Skipped entity " + ToString( Id) ;
m_pMchMgr->SetWarning( 2451, sInfo) ;
continue ;
}
// posso aggiungere alla lista
m_vId.emplace_back( Id) ;
}
// aggiorno lo stato
m_nStatus |= MCH_ST_GEO_MODIF ;
// restituisco presenza geometria da lavorare
return ( ! m_vId.empty() || vIds.empty()) ;
}
//----------------------------------------------------------------------------
bool
Pocketing::Preview( bool bRecalc)
{
// reset numero percorsi di svuotatura generati
m_nPockets = 0 ;
// verifico validità gestore DB geometrico e Id del gruppo
if ( m_pGeomDB == nullptr || ! m_pGeomDB->ExistsObj( m_nOwnerId))
return false ;
// recupero gruppo per geometria ausiliaria
int nAuxId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_AUX) ;
bool bChain = false ;
// se non c'è, lo aggiungo
if ( nAuxId == GDB_ID_NULL) {
nAuxId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ;
if ( nAuxId == GDB_ID_NULL)
return false ;
m_pGeomDB->SetName( nAuxId, MCH_AUX) ;
m_pGeomDB->SetStatus( nAuxId, GDB_ST_OFF) ;
bChain = true ;
}
// altrimenti, se chiesto ricalcolo, lo svuoto
else if ( bRecalc) {
m_pGeomDB->EmptyGroup( nAuxId) ;
bChain = true ;
}
// aggiorno dati geometrici dell'utensile
if ( ! UpdateToolData()) {
m_pMchMgr->SetLastError( 2401, "Error in Pocketing : UpdateToolData failed") ;
return false ;
}
// rendo corrente l'utensile usato nella lavorazione
if ( ! m_pMchMgr->SetCalcTool( m_TParams.m_sName, m_TParams.m_sHead, m_TParams.m_nExit)) {
m_pMchMgr->SetLastError( 2428, "Error in Pocketing : Tool loading failed") ;
return false ;
}
// recupero i dati del portautensile
int nToolId = m_pMchMgr->GetCalcTool() ;
m_dTHoldBase = 0 ;
m_pGeomDB->GetInfo( nToolId, TTH_BASE, m_dTHoldBase) ;
m_dTHoldLen = 0 ;
m_pGeomDB->GetInfo( nToolId, TTH_LEN, m_dTHoldLen) ;
m_dTHoldDiam = 0 ;
m_pGeomDB->GetInfo( nToolId, TTH_DIAM, m_dTHoldDiam) ;
// se necessario, eseguo concatenamento ed inserisco i percorsi sotto la geometria ausiliaria
if ( bChain && ! Chain( nAuxId)) {
m_pMchMgr->SetLastError( 2421, "Error in Pocketing : Chaining failed") ;
return false ;
}
// recupero gruppo per geometria di Preview
int nPvId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_PV) ;
// se non c'è, lo aggiungo
if ( nPvId == GDB_ID_NULL) {
nPvId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ;
if ( nPvId == GDB_ID_NULL)
return false ;
m_pGeomDB->SetName( nPvId, MCH_PV) ;
}
// altrimenti lo svuoto
else
m_pGeomDB->EmptyGroup( nPvId) ;
// lavoro ogni singola catena
int nPathId = m_pGeomDB->GetFirstGroupInGroup( nAuxId) ;
while ( nPathId != GDB_ID_NULL) {
if ( ! ProcessPath( nPathId, nPvId, GDB_ID_NULL))
return false ;
nPathId = m_pGeomDB->GetNextGroup( nPathId) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::Apply( bool bRecalc, bool bPostApply)
{
// reset numero percorsi di svuotatura generati
int nCurrPockets = m_nPockets ;
m_nPockets = 0 ;
// reset raggio massimo attacco ad elica nel caso di cerchi
m_dMaxHelixRad = INFINITO ;
// verifico validità gestore DB geometrico e Id del gruppo
if ( m_pGeomDB == nullptr || ! m_pGeomDB->ExistsObj( m_nOwnerId))
return false ;
// aggiorno dati geometrici dell'utensile
bool bToolChanged = true ;
if ( ! UpdateToolData( &bToolChanged)) {
m_pMchMgr->SetLastError( 2401, "Error in Pocketing : UpdateToolData failed") ;
return false ;
}
// verifico se necessario continuare nell'aggiornamento
if ( !bRecalc && !bToolChanged &&
( m_nStatus == MCH_ST_OK || ( !bPostApply && m_nStatus == MCH_ST_NO_POSTAPPL))) {
// confermo i percorsi di lavorazione
m_nPockets = nCurrPockets ;
LOG_DBG_INFO( GetEMkLogger(), "Pocketing apply skipped : status already ok") ;
// eseguo aggiornamento assi macchina e collegamento con operazione precedente
if ( ! Update( bPostApply))
return false ;
LOG_DBG_INFO( GetEMkLogger(), "Update done") ;
// esco con successo
return true ;
}
m_nStatus = MCH_ST_TO_VERIFY ;
// recupero gruppo per geometria ausiliaria
int nAuxId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_AUX) ;
bool bChain = false ;
// se non c'è, lo aggiungo
if ( nAuxId == GDB_ID_NULL) {
nAuxId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ;
if ( nAuxId == GDB_ID_NULL)
return false ;
m_pGeomDB->SetName( nAuxId, MCH_AUX) ;
m_pGeomDB->SetStatus( nAuxId, GDB_ST_OFF) ;
bChain = true ;
}
// altrimenti, se chiesto ricalcolo, lo svuoto
else if ( bRecalc) {
m_pGeomDB->EmptyGroup( nAuxId) ;
bChain = true ;
}
// rendo corrente l'utensile usato nella lavorazione
if ( ! m_pMchMgr->SetCalcTool( m_TParams.m_sName, m_TParams.m_sHead, m_TParams.m_nExit)) {
m_pMchMgr->SetLastError( 2428, "Error in Pocketing : Tool loading failed") ;
return false ;
}
// recupero i dati del portautensile
int nToolId = m_pMchMgr->GetCalcTool() ;
m_dTHoldBase = 0 ;
m_pGeomDB->GetInfo( nToolId, TTH_BASE, m_dTHoldBase) ;
m_dTHoldLen = 0 ;
m_pGeomDB->GetInfo( nToolId, TTH_LEN, m_dTHoldLen) ;
m_dTHoldDiam = 0 ;
m_pGeomDB->GetInfo( nToolId, TTH_DIAM, m_dTHoldDiam) ;
// recupero gruppo per geometria di lavorazione (Cutter Location)
int nClId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_CL) ;
// se non c'è, lo aggiungo
if ( nClId == GDB_ID_NULL) {
nClId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ;
if ( nClId == GDB_ID_NULL)
return false ;
m_pGeomDB->SetName( nClId, MCH_CL) ;
}
// altrimenti lo svuoto
else
m_pGeomDB->EmptyGroup( nClId) ;
// elimino eventuale gruppo geometria simmetrica per lavorazione in doppio
int nDblId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_DBL) ;
if ( nDblId != GDB_ID_NULL) {
m_pGeomDB->Erase( nDblId) ;
nDblId = GDB_ID_NULL ;
}
// se necessario, eseguo concatenamento ed inserisco i percorsi sotto la geometria ausiliaria
if ( bChain && ! Chain( nAuxId)) {
m_pMchMgr->SetLastError( 2421, "Error in Pocketing : Chaining failed") ;
return false ;
}
// lavoro ogni singola catena
bool bOk = true ;
int nPathId = m_pGeomDB->GetFirstGroupInGroup( nAuxId) ;
while ( nPathId != GDB_ID_NULL) {
if ( ! ProcessPath( nPathId, GDB_ID_NULL, nClId))
bOk = false ;
nPathId = m_pGeomDB->GetNextGroup( nPathId) ;
}
if ( ! bOk)
return false ;
// assegno ingombri dei vari percorsi di lavorazione e della lavorazione nel suo complesso
CalcAndSetBBox( nClId) ;
// eseguo aggiornamento assi macchina e collegamento con operazione precedente
if ( ! Update( bPostApply))
return false ;
// se lavorazione in doppio, aggiungo geometria della parte simmetrica
if ( ! CalcMirrorByDouble( nClId, m_Params.m_sUserNotes)) {
m_pMchMgr->SetLastError( 2432, "Error in Pocketing : Mirror for Double calculation failed") ;
return false ;
}
// aggiorno stato della lavorazione
m_nStatus = ( bPostApply ? MCH_ST_OK : MCH_ST_NO_POSTAPPL) ;
// dichiaro successiva da aggiornare
UpdateFollowingOperationsStatus( MCH_ST_OTH_MODIF) ;
LOG_DBG_INFO( GetEMkLogger(), "Pocketing apply done") ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::Update( bool bPostApply)
{
// verifico validità gestore DB geometrico e Id del gruppo
if ( m_pGeomDB == nullptr || ! m_pGeomDB->ExistsObj( m_nOwnerId))
return false ;
// se lavorazione vuota, esco
if ( m_nPockets == 0) {
m_pMchMgr->SetWarning( 2452, "Warning in Pocketing : No machinable pocket") ;
return true ;
}
// elimino le entità CLIMB, RISE e HOME della lavorazione, potrebbero falsare i calcoli degli assi (in ogni casi vengono riaggiunte dopo)
RemoveClimbRiseHome() ;
// imposto eventuale asse bloccato da lavorazione
SetBlockedRotAxis( m_Params.m_sBlockedAxis) ;
// calcolo gli assi macchina
string sHint = ExtractHint( m_Params.m_sUserNotes) ;
if ( ! m_Params.m_sInitAngs.empty())
sHint = m_Params.m_sInitAngs ;
if ( ! CalculateAxesValues( sHint)) {
string sInfo = m_pMchMgr->GetOutstrokeInfo() ;
if ( sInfo.empty())
m_pMchMgr->SetLastError( 2423, "Error in Pocketing : axes values not calculable") ;
else
m_pMchMgr->SetLastError( 2424, "Error in Pocketing : outstroke ") ;
return false ;
}
// gestione movimenti all'inizio di ogni singolo percorso di lavorazione e alla fine della lavorazione
if ( ! AdjustStartEndMovements()) {
string sInfo = m_pMchMgr->GetOutstrokeInfo() ;
if ( sInfo.empty())
m_pMchMgr->SetLastError( 2425, "Error in Pocketing : link movements not calculable") ;
else
m_pMchMgr->SetLastError( 2426, "Error in Pocketing : link outstroke ") ;
return false ;
}
// assegno estremi degli assi dei vari percorsi di lavorazione e della lavorazione nel suo complesso
CalcAndSetAxesBBox() ;
// esecuzione eventuali personalizzazioni
string sErr ;
if ( bPostApply && ! PostApply( sErr)) {
if ( ! IsEmptyOrSpaces( sErr))
m_pMchMgr->SetLastError( 2427, sErr) ;
else
m_pMchMgr->SetLastError( 2427, "Error in Pocketing : post apply not calculable") ;
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::GetParam( int nType, bool& bVal) const
{
switch ( nType) {
case MPA_INVERT :
bVal = m_Params.m_bInvert ;
return true ;
case MPA_TOOLINVERT :
bVal = m_Params.m_bToolInvert ;
return true ;
}
bVal = false ;
return false ;
}
//----------------------------------------------------------------------------
bool
Pocketing::GetParam( int nType, int& nVal) const
{
switch ( nType) {
case MPA_TYPE :
nVal = MT_POCKETING ;
return true ;
case MPA_LEADINTYPE :
nVal = m_Params.m_nLeadInType ;
return true ;
case MPA_LEADOUTTYPE :
nVal = m_Params.m_nLeadOutType ;
return true ;
case MPA_SCC :
nVal = m_Params.m_nSolCh ;
return true ;
case MPA_SUBTYPE :
nVal = m_Params.m_nSubType ;
return true ;
}
nVal = 0 ;
return false ;
}
//----------------------------------------------------------------------------
bool
Pocketing::GetParam( int nType, double& dVal) const
{
switch ( nType) {
case MPA_SPEED :
dVal = GetSpeed() ;
return true ;
case MPA_FEED :
dVal = GetFeed() ;
return true ;
case MPA_STARTFEED :
dVal = GetStartFeed() ;
return true ;
case MPA_ENDFEED :
dVal = GetEndFeed() ;
return true ;
case MPA_TIPFEED :
dVal = GetTipFeed() ;
return true ;
case MPA_OFFSR :
dVal = GetOffsR() ;
return true ;
case MPA_OFFSL :
dVal = GetOffsL() ;
return true ;
case MPA_STARTPOS :
dVal = m_Params.m_dStartPos ;
return true ;
case MPA_STEP :
dVal = m_Params.m_dStep ;
return true ;
case MPA_SIDESTEP :
dVal = m_Params.m_dSideStep ;
return true ;
case MPA_SIDEANGLE :
dVal = m_Params.m_dSideAngle ;
return true ;
case MPA_LITANG :
dVal = m_Params.m_dLiTang ;
return true ;
case MPA_LIELEV :
dVal = m_Params.m_dLiElev ;
return true ;
case MPA_LOTANG :
dVal = m_Params.m_dLoTang ;
return true ;
case MPA_EPICYCLESRAD :
dVal = m_Params.m_dEpicyclesRad ;
return true ;
case MPA_EPICYCLESDIST :
dVal = m_Params.m_dEpicyclesDist ;
return true ;
}
dVal = 0 ;
return false ;
}
//----------------------------------------------------------------------------
bool
Pocketing::GetParam( int nType, string& sVal) const
{
switch ( nType) {
case MPA_NAME :
sVal = m_Params.m_sName ;
return true ;
case MPA_TOOL :
sVal = m_Params.m_sToolName ;
return true ;
case MPA_DEPTH_STR :
sVal = m_Params.m_sDepth ;
return true ;
case MPA_TUUID :
sVal = ToString( m_Params.m_ToolUuid) ;
return true ;
case MPA_UUID :
sVal = ToString( m_Params.m_Uuid) ;
return true ;
case MPA_SYSNOTES :
sVal = m_Params.m_sSysNotes ;
return true ;
case MPA_USERNOTES :
sVal = m_Params.m_sUserNotes ;
return true ;
case MPA_INITANGS :
sVal = m_Params.m_sInitAngs ;
return true ;
case MPA_BLOCKEDAXIS :
sVal = m_Params.m_sBlockedAxis ;
return true ;
}
sVal = "" ;
return false ;
}
//----------------------------------------------------------------------------
const ToolData&
Pocketing::GetToolData( void) const
{
return m_TParams ;
}
//----------------------------------------------------------------------------
bool
Pocketing::UpdateToolData( bool* pbChanged)
{
// recupero il gestore DB utensili della macchina corrente
ToolsMgr* pTMgr = m_pMchMgr->GetCurrToolsMgr() ;
if ( pTMgr == nullptr)
return false ;
// recupero l'utensile nel DB utensili
const ToolData* pTdata = pTMgr->GetTool( m_Params.m_ToolUuid) ;
if ( pTdata == nullptr)
return false ;
// salvo posizione TC, testa e uscita originali
string sOrigTcPos = m_TParams.m_sTcPos ;
string sOrigHead = m_TParams.m_sHead ;
int nOrigExit = m_TParams.m_nExit ;
// verifico se sono diversi (ad esclusione di nome, posizione TC, testa e uscita)
bool bChanged = ( ! SameTool( m_TParams, *pTdata, false)) ;
// aggiorno comunque i parametri
m_TParams = *pTdata ;
// se definito attrezzaggio, aggiorno i parametri che ne possono derivare
string sTcPos ; string sHead ; int nExit ;
if ( m_pMchMgr->GetCurrSetupMgr().GetToolData( m_TParams.m_sName, sTcPos, sHead, nExit)) {
if ( sOrigTcPos != sTcPos ||
sOrigHead != sHead ||
nOrigExit != nExit)
bChanged = true ;
m_TParams.m_sTcPos = sTcPos ;
m_TParams.m_sHead = sHead ;
m_TParams.m_nExit = nExit ;
}
else {
if ( sOrigTcPos != pTdata->m_sTcPos ||
sOrigHead != pTdata->m_sHead ||
nOrigExit != pTdata->m_nExit)
bChanged = true ;
}
// eventuali segnalazioni
if ( ! EqualNoCase( m_Params.m_sToolName, m_TParams.m_sName)) {
string sInfo = "Warning in Pocketing : tool name changed (" +
m_Params.m_sToolName + "->" + m_TParams.m_sName + ")" ;
m_pMchMgr->SetWarning( 2453, sInfo) ;
m_Params.m_sToolName = m_TParams.m_sName ;
}
if ( bChanged) {
string sInfo = "Warning in Pocketing : tool data changed (" +
m_Params.m_sToolName + ")" ;
m_pMchMgr->SetWarning( 2454, sInfo) ;
}
// se definito parametro di ritorno, lo assegno
if ( pbChanged != nullptr)
*pbChanged = bChanged ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::GetGeometry( SELVECTOR& vIds) const
{
// restituisco l'elenco delle entità
vIds = m_vId ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::VerifyGeometry( SelData Id, int& nSubs, int& nType)
{
// ammessi : curve, testi, facce di trimesh o regioni
const IGeoObj* pGObj = m_pGeomDB->GetGeoObj( Id.nId) ;
if ( pGObj == nullptr)
return false ;
// se ammesse curve ed è tale
if ( ( nType == GEO_NONE || nType == GEO_CURVE) && ( pGObj->GetType() & GEO_CURVE) != 0) {
nType = GEO_CURVE ;
const ICurve* pCurve = nullptr ;
// se direttamente la curva
if ( Id.nSub == SEL_SUB_ALL) {
pCurve = ::GetCurve( pGObj) ;
if ( pCurve == nullptr)
return false ;
if ( pCurve->GetType() == CRV_COMPO)
nSubs = ::GetCurveComposite( pCurve)->GetCurveCount() ;
else
nSubs = 0 ;
}
// altrimenti sottocurva di composita
else {
const ICurveComposite* pCompo = GetCurveComposite( pGObj) ;
pCurve = ( pCompo != nullptr ? pCompo->GetCurve( Id.nSub) : nullptr) ;
if ( pCurve == nullptr)
return false ;
nSubs = 0 ;
}
return true ;
}
// se altrimenti ammessi testi ed è tale
else if ( ( nType == GEO_NONE || nType == EXT_TEXT) && pGObj->GetType() == EXT_TEXT) {
nType = EXT_TEXT ;
const IExtText* pText = ::GetExtText( pGObj) ;
if ( pText == nullptr)
return false ;
nSubs = 0 ;
return true ;
}
// se altrimenti ammesse superfici trimesh ed è tale
else if ( ( nType == GEO_NONE || nType == SRF_TRIMESH) && pGObj->GetType() == SRF_TRIMESH) {
nType = SRF_TRIMESH ;
const ISurfTriMesh* pSurf = ::GetSurfTriMesh( pGObj) ;
if ( pSurf == nullptr)
return false ;
// se direttamente la superficie
if ( Id.nSub == SEL_SUB_ALL) {
// deve avere una sola faccia
if ( pSurf->GetFacetCount() != 1)
return false ;
nSubs = 1 ;
}
// altrimenti faccia di superficie trimesh
else {
// se faccia non esistente
if ( Id.nSub >= pSurf->GetFacetCount())
return false ;
nSubs = 0 ;
}
return true ;
}
// se altrimenti ammesse regioni ed è tale
else if ( ( nType == GEO_NONE || nType == SRF_FLATRGN) && pGObj->GetType() == SRF_FLATRGN) {
nType = SRF_FLATRGN ;
const ISurfFlatRegion* pReg = ::GetSurfFlatRegion( pGObj) ;
if ( pReg == nullptr)
return false ;
// se direttamente la regione
if ( Id.nSub == SEL_SUB_ALL) {
nSubs = pReg->GetChunkCount() ;
}
// altrimenti chunk di regione
else {
// se chunk non esistente
if ( Id.nSub >= pReg->GetChunkCount())
return false ;
// tutto bene
nSubs = 0 ;
}
return true ;
}
// altrimenti errore
else
return false ;
}
//----------------------------------------------------------------------------
bool
Pocketing::GetCurvesAndPartialVolume( SelData Id, ICURVEPLIST& lstPC, ISurfTriMesh* pStmTmp, Vector3d& vtN)
{
// ammessi : curve, testi, facce di trimesh o regioni
const IGeoObj* pGObj = m_pGeomDB->GetGeoObj( Id.nId) ;
if ( pGObj == nullptr)
return false ;
// ne recupero il riferimento globale
Frame3d frGlob ;
if ( ! m_pGeomDB->GetGlobFrame( Id.nId, frGlob))
return false ;
// se curva
if (( pGObj->GetType() & GEO_CURVE) != 0) {
// Thickness
double dThick = 0. ;
PtrOwner<ICurve> 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<ICurve> 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<ISurfTriMesh> pStm( CloneSurfTriMesh( pSurf)) ;
if ( IsNull( pStm))
return false ;
// porto in globale
pStm->ToGlob( frGlob) ;
// recupero l'indice della faccia
int nFacet = (( Id.nSub == SEL_SUB_ALL) ? 0 : Id.nSub) ;
// recupero la normale esterna della faccia e il suo punto centrale
Vector3d vtN_tm ;
Point3d ptCenter ;
if ( ! pStm->GetFacetCenter( nFacet, ptCenter, vtN_tm))
return false ;
if ( ! vtN.IsValid())
vtN = vtN_tm ;
else if ( ! AreSameVectorEpsilon( vtN, vtN_tm, 5 * EPS_SMALL))
return false ;
// inverto per definire il volume di pocketing
pStm->Invert() ;
// la superficie diventa il suo volume di svuotatura
if ( ! SetPocketingVolume( ptCenter, pStm))
return false ;
// ricavo la Depth
double dDepth = 0. ;
if ( ! CalcDepth( Id.nId, vtN_tm, 0., dDepth))
return false ;
// creo il piano di taglio alla quota della faccia
Plane3d plCut ; plCut.Set( ptCenter + vtN_tm * ( - dDepth) , - vtN_tm) ;
if ( ! plCut.IsValid())
return false ;
// taglio il volume alla quota di taglio
if ( ! pStm->Cut( plCut, true))
return false ;
// chiudo
if ( ! SewingMissingFacesOnPlanes( pStm, plCut))
return false ;
// aggiungo la Trimesh
if ( ! pStmTmp->Add( *pStm))
return false ;
return true ;
}
// se altrimenti regione
else if ( pGObj->GetType() == SRF_FLATRGN) {
// recupero la regione
const ISurfFlatRegion* pReg = ::GetSurfFlatRegion( pGObj) ;
if ( pReg == nullptr)
return false ;
PtrOwner<ISurfFlatRegion> 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<ICurveComposite> 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<ISurfTriMesh> pStm_tmp( CreateSurfTriMesh()) ;
if ( IsNull( pStm_tmp) || ! pStm_tmp->AdjustTopology())
return false ;
// estrusione e spessore
Vector3d vtExtr = V_INVALID ;
// scorro tutti gli id
for ( const auto& Id : m_vId) {
// vettore contente tutte le curve selezionate
ICURVEPLIST lstPC ;
// prendo le curve e/o aggiorno la TriMesh di pocketing originale
if ( ! GetCurvesAndPartialVolume( Id, lstPC, pStm_tmp, vtExtr)) {
string sInfo = "Warning in Pocketing : Skipped entity " + ToString( Id) ;
m_pMchMgr->SetWarning( 2451, sInfo) ;
}
for ( auto pCrv : lstPC) {
vpCrvs.emplace_back( pCrv) ;
vInds.emplace_back( Id) ;
}
}
// se inversione del Tool, allora inversione dell'estrusione
if ( m_Params.m_bToolInvert)
vtExtr.Invert() ;
// se ho trovato delle curve...
if ( int( vpCrvs.size()) > 0) {
// concateno le curve se necessario
ICRVCOMPOPOVECTOR vCrvCompo ;
if ( ! ChainCurveArray( vpCrvs, vCrvCompo))
return false ;
// verifico che siano curve chiuse ( devono delimitare l'area da svuotare)
for ( int u = 0 ; u < int( vCrvCompo.size()) ; ++ u) {
if ( ! vCrvCompo[u]->IsClosed()) {
m_pMchMgr->SetLastError( 2402, "Error in Pocketing : Open Contour") ;
return false ;
}
}
// ricavo il centro della prima curva
Point3d ptInside ;
if ( ! vCrvCompo[0]->GetCentroid( ptInside))
return false ;
// creazione della TriMesh di pocketing
PtrOwner<ISurfTriMesh> pStmCurve_Volume( CreateSurfTriMesh()) ;
if ( IsNull( pStmCurve_Volume) ||
! pStmCurve_Volume->AdjustTopology() ||
! CreateStmForIntersection( pStmCurve_Volume, vCrvCompo, vtExtr))
return false ;
// intersezione della Trimesh con il finito -> rivavo il volume di svuotatura
if ( ! SetPocketingVolume( ptInside, pStmCurve_Volume))
return false ;
if ( pStm_tmp->IsValid())
if ( ! pStm_tmp->Add( *pStmCurve_Volume))
return false ;
}
// per ogni part rivata dal volume di svuotatura...
for ( int p = 0 ; p < pStm_tmp->GetPartCount() ; ++ p) {
// creo nuovo gruppo
int nPathId = m_pGeomDB->AddGroup( GDB_ID_NULL, nGrpDestId, Frame3d()) ;
if ( nPathId == GDB_ID_NULL)
return false ;
m_pGeomDB->SetName( nPathId, MCH_PATH + ToString( p + 1)) ;
// inserisco la Part (p)-esima nel gruppo destinazione
int nNewId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nPathId, pStm_tmp->ClonePart( p)) ;
if ( nNewId == GDB_ID_NULL)
return false ;
// memorizzo la direzione di estrusione
m_pGeomDB->SetInfo( nPathId, KEY_TOOL, vtExtr) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::ChainCurveArray( ICURVEPOVECTOR& vpCrvs, ICRVCOMPOPOVECTOR& vCrvCompo)
{
// preparo i dati per il concatenamento
bool bFirst = true ;
Point3d ptNear = ORIG ;
double dToler = 10 * EPS_SMALL ;
ChainCurves chainC ;
chainC.Init( true, dToler, int( vpCrvs.size())) ;
for ( size_t i = 0 ; i < vpCrvs.size() ; ++ i) {
// recupero la curva e il suo riferimento
ICurve* pCrv = vpCrvs[i] ;
if ( pCrv == nullptr)
continue ;
// recupero i dati della curva necessari al concatenamento e li assegno
Point3d ptStart, ptEnd ;
Vector3d vtStart, vtEnd ;
if ( ! pCrv->GetStartPoint( ptStart) || ! pCrv->GetStartDir( vtStart) ||
! pCrv->GetEndPoint( ptEnd) || ! pCrv->GetEndDir( vtEnd))
return false ;
if ( ! chainC.AddCurve( int( i + 1), ptStart, vtStart, ptEnd, vtEnd))
return false ;
// se prima curva, assegno inizio della ricerca
if ( bFirst) {
ptNear = ptStart + 10 * EPS_SMALL * vtStart ;
bFirst = false ;
}
}
// recupero i percorsi concatenati
int nCount = 0 ;
INTVECTOR vnId2 ;
while ( chainC.GetChainFromNear( ptNear, false, vnId2)) {
// creo una curva composita
PtrOwner<ICurveComposite> 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<CurveQuotes> 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<ISurfFlatRegion> pSfrFinal( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrFinal))
return false ;
PtrOwner<ISurfFlatRegion> 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<ICurveComposite> 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<ISurfTriMesh> 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
Point3d ptC ;
if ( ! pCrvCompo->GetCentroid( ptC))
return false ;
// 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<ISurfTriMesh> pStmPart( CreateSurfTriMesh()) ;
if ( IsNull( pStmPart) ||
! GetCurrentPart( ptC, 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) ;
int nTmpProp0 = pCrvLoop->GetTempProp() ;
int nTmpProp1 = pCrvLoop->GetTempProp( 1) ;
// ricavo i tratti omogenei ( campio il punto iniziale della curva nel punto medio del chiuso più lungo se presente)
ICRVCOMPOPOVECTOR vpCrvs ;
if ( ! GetHomogeneousParts( pCrvLoop, vpCrvs))
return false ;
// se curva tutta omogonea
if ( int( vpCrvs.size()) == 1 ) {
// se tutta chiusa, non devo estendere nulla
if ( vpCrvs[0]->GetTempProp() == 0)
return true ;
// se tutta aperta -> Offset
OffsetCurve OffsCrv ;
if ( ! OffsCrv.Make( pCrvLoop, 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<ICurveLine> 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<ICurveLine> 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<ICurve> pCrvOffs( OffsCrv.GetLongerCurve()) ;
if ( IsNull( pCrvOffs) || ! pCrvOffs->IsValid())
return false ;
// estendo l'inizio e la fine
if ( ! pCrvOffs->ExtendStartByLen( 30000) ||
! pCrvOffs->ExtendEndByLen( 30000))
return false ;
// interseco con questa curva con le rette
IntersCurveCurve IntCC_S( *pLineS, *pCrvOffs) ;
IntersCurveCurve IntCC_E( *pLineE, *pCrvOffs) ;
Point3d ptInters_S, ptInters_E ;
if ( IntCC_S.GetIntersCount() == 1) {
IntCrvCrvInfo aInfoS ;
if ( ! IntCC_S.GetIntCrvCrvInfo( 0, aInfoS))
return false ;
ptInters_S = aInfoS.IciA[0].ptI ;
}
else
return false ;
if ( IntCC_E.GetCrossIntersCount() == 1) {
IntCrvCrvInfo aInfoE ;
if ( ! IntCC_E.GetIntCrvCrvInfo( 0, aInfoE))
return false ;
ptInters_E = aInfoE.IciA[0].ptI ;
}
else
return false ;
// trimmo la curva Offsettata nei punti di intersezione
double dUS, dUE ;
pCrvOffs->GetParamAtPoint( ptInters_S, dUS) ;
pCrvOffs->GetParamAtPoint( ptInters_E, dUE) ;
if ( ! pCrvOffs->TrimStartEndAtParam( dUS, dUE))
return false ;
// aggiungo il tratto nuovo
if ( ! pCrvLoop->AddLine( ptInters_S) ||
! pCrvLoop->AddCurve( Release( pCrvOffs)) ||
! pCrvLoop->AddLine( ptSL2))
return false ;
}
else // non definito
return false ;
}
// riporto nel frame originario
pCrvLoop->ToGlob( frZ) ;
return pCrvLoop->IsClosed() && pCrvLoop->IsValid() ;
}
//----------------------------------------------------------------------------
bool
Pocketing::GetCurrentPart( const Point3d& ptInside, ISurfTriMesh* pStmPart)
{
// controllo MachManager e database geometrico
if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr)
return false ;
// considero tutte le superfici dei pezzi nei grezzi attivi della fase
int nRawId = m_pMchMgr->GetFirstRawPart() ;
while ( nRawId != GDB_ID_NULL) {
if ( m_pMchMgr->VerifyRawPartPhase( nRawId, m_nPhase)) {
// ciclo sui pezzi del grezzo
int nPartId = m_pMchMgr->GetFirstPartInRawPart( nRawId) ;
while ( nPartId != GDB_ID_NULL) {
// ciclo sui layer dei pezzi
int nLayId = m_pGeomDB->GetFirstGroupInGroup( nPartId) ;
while ( nLayId != GDB_ID_NULL) {
// ciclo sulle entità del layer
int nEntId = m_pGeomDB->GetFirstInGroup( nLayId) ;
while ( nEntId != GDB_ID_NULL) {
// se entità superficie e visibile, la aggiungo
int nStat ;
if ( m_pGeomDB->GetGeoType( nEntId) == SRF_TRIMESH) {
// recupero l'ggetto dal databse con tale Id
const IGeoObj* pGObj = m_pGeomDB->GetGeoObj( nEntId) ;
// recupero il frame in cui si trova
Frame3d frPart ;
m_pGeomDB->GetGlobFrame( nEntId, frPart) ;
if ( pGObj == nullptr)
return false ;
// controllo che effettivamente sia una TriMesh
if ( pGObj->GetType() == SRF_TRIMESH) {
// Trimesh della Part
PtrOwner<ISurfTriMesh> pStmRawPart( CloneSurfTriMesh( pGObj)) ;
if ( IsNull( pStmRawPart))
return false ;
// porto la Trimesh in globale
pStmRawPart->LocToLoc( frPart, GLOB_FRM) ;
// controllo se il punto è interno a tale Part
double dElev = 0. ;
DistPointSurfTm DistPtStm( ptInside, *pStmRawPart) ;
double dDist ; DistPtStm.GetDist( dDist) ;
if ( DistPtStm.IsPointInside() || DistPtStm.IsSmall()) {
pStmPart->CopyFrom( pStmRawPart) ;
return pStmPart->IsValid() && pStmPart->GetTriangleCount() > 0 ;
}
}
}
// passo alla entità successiva
nEntId = m_pGeomDB->GetNext( nEntId) ;
}
nLayId = m_pGeomDB->GetNextGroup( nLayId) ;
}
nPartId = m_pMchMgr->GetNextPartInRawPart( nPartId) ;
}
}
nRawId = m_pMchMgr->GetNextRawPart( nRawId) ;
}
return false ;
}
//----------------------------------------------------------------------------
bool
Pocketing::SetPocketingVolume( const Point3d& ptInside, ISurfTriMesh* pStm)
{
// controllo parametro
if ( pStm == nullptr || ! pStm->IsValid())
return false ;
// recupero il Current Part
PtrOwner<ISurfTriMesh> pStmCurrRawPart( CreateSurfTriMesh()) ;
if ( IsNull( pStmCurrRawPart) ||
! GetCurrentPart( ptInside, pStmCurrRawPart))
return false ;
// interseco la TriMesh complessiva di pocketing con il finito, ottenendo il volume di svuotatura
if ( ! pStmCurrRawPart->Intersect( *pStm)) // intersezione
return false ;
// aggiorno la superficie
pStm->CopyFrom( pStmCurrRawPart) ;
return pStm->IsValid() && pStm->GetTriangleCount() > 0 ;
}
//---------------------------------------------------------------------------
bool
Pocketing::AdjustPocketingSideForVolumePart( ISurfTriMesh* pStmVolPart, const Vector3d& vtTool)
{
// controllo dei parametri
if ( pStmVolPart == nullptr || ! pStmVolPart->IsValid())
return false ;
// NB. Il controllo delle normali delle entitità che sono state selezionate presenta una laggera tolleranza;
// Questa tolleranza potrebbe rendere la superficie da svuotare non completamente piana ( ma come sovrapposizione
// di superfici piane leggermente inclinate tra loro;
const double TOLL = 500 * EPS_SMALL ;
// creo un frame centrato nel centroide della parte
Frame3d frElevation ;
Point3d ptC ; pStmVolPart->GetCentroid( ptC) ;
frElevation.Set( ptC, vtTool) ;
if ( ! frElevation.IsValid())
return false ;
// porto il la parte di volume in questo frame
pStmVolPart->ToLoc( frElevation) ;
// ricavo quindi il Box
BBox3d BBox ; pStmVolPart->GetLocalBBox( BBox) ;
// il su cui iniziare a svuotare è quello che si trova a Z minima
double dZ_min = BBox.GetMin().z ;
// riporto il volume nel suo frame Originario
pStmVolPart->ToGlob( frElevation) ;
// porto il frame alla minima Z ( sarà negativa) di svuotatura
frElevation.Set( ptC + vtTool * dZ_min, vtTool) ;
if ( ! frElevation.IsValid())
return false ;
// porto il volume in questo frame
pStmVolPart->ToLoc( frElevation) ;
// NB. ora il nostro volume è appoggiato al piano Z = 0 centrato sopra all'Origine
// devo controllare se ci sono delle facce contenute tra il piano Z = 0 e Z = TOLL
// 1) creo i due piani
Plane3d plZ0 ;
plZ0.Set( ORIG, -Z_AX) ;
if ( ! plZ0.IsValid())
return false ;
Plane3d plZTOLL ;
plZTOLL.Set( ORIG + Z_AX * TOLL, Z_AX) ;
if ( ! plZTOLL.IsValid())
return false ;
// 2) taglio una copia del volume tra i due piani
PtrOwner<ISurfTriMesh> pStmVolPart_between( CloneSurfTriMesh( pStmVolPart)) ;
if ( IsNull( pStmVolPart_between) || ! pStmVolPart_between->IsValid())
return false ;
if ( ! pStmVolPart_between->Cut( plZ0, false) || // tolgo la faccia a Z = 0
! pStmVolPart_between->Cut( plZTOLL, false))
return false ;
// 3) controllo tutte le facce che ho attenuto ; se ho ottenuto almeno una faccia con normale simile a Z_AX
// allora traslo il piano di svuotatura a Z = TOLL
bool bExistFaces = false ;
for ( int f = 0 ; f < pStmVolPart_between->GetFacetCount() && !bExistFaces ; ++ f) {
Point3d ptC ; Vector3d vtC ;
if ( ! pStmVolPart_between->GetFacetCenter( f, ptC, vtC))
return false ;
bExistFaces = AreSameVectorEpsilon( vtC, - Z_AX, 5 * EPS_SMALL) ;
}
// se non ho trovato nessuna faccia, allora il volume andava già bene
if ( ! bExistFaces) {
pStmVolPart->ToGlob( frElevation) ; // riportato in posizione originale
return true ;
}
// dato che ho trovato almeno una faccia, il mio nuovo volume si restringe leggermente su Z = TOLL
plZTOLL.Invert() ;
if ( ! pStmVolPart->Cut( plZTOLL, false) ||
! SewingMissingFacesOnPlanes( pStmVolPart, plZTOLL))
return false ;
pStmVolPart->ToGlob( frElevation) ; // riportato in posizione originale
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::ProcessPath( int nPathId, int nPvId, int nClId)
{
// recupero gruppo per geometria temporanea ( Gruppo Temp)
const string GRP_TEMP = "Temp" ;
int nTempId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, GRP_TEMP) ;
// se non c'è, lo aggiungo
if ( nTempId == GDB_ID_NULL) {
nTempId = m_pGeomDB->AddGroup( GDB_ID_NULL, m_nOwnerId, Frame3d()) ;
if ( nTempId == GDB_ID_NULL)
return false ;
m_pGeomDB->SetName( nTempId, GRP_TEMP) ;
}
// altrimenti lo svuoto
else
m_pGeomDB->EmptyGroup( nTempId) ;
// in ogni caso lo dichiaro temporaneo e non visibile
m_pGeomDB->SetLevel( nTempId, GDB_LV_TEMP) ;
m_pGeomDB->SetStatus( nTempId, GDB_ST_OFF) ;
// recupero l'entità ( Part del volume di svuotatura)
double dDepth = 0. ;
Vector3d vtTool ; m_pGeomDB->GetInfo( nPathId, KEY_TOOL, vtTool) ;
PtrOwner<ISurfTriMesh> pStm_PartVolume( CreateSurfTriMesh()) ;
if ( IsNull( pStm_PartVolume))
return false ;
int nId = m_pGeomDB->GetFirstInGroup( nPathId) ;
if ( m_pGeomDB->GetGeoType( nId) != SRF_TRIMESH)
return false ;
pStm_PartVolume.Set( CloneSurfTriMesh( m_pGeomDB->GetGeoObj( nId))) ;
if ( IsNull( pStm_PartVolume) || ! pStm_PartVolume->IsValid())
return false ;
// recupero nome del path
string sPathName ;
m_pGeomDB->GetName( nPathId, sPathName) ;
// recupero il Box del grezzo globale
BBox3d b3Raw ;
if ( ! GetRawGlobBox( m_nPhase, nPathId, 0.5 * m_TParams.m_dTDiam, b3Raw) || b3Raw.IsEmpty()) {
m_pMchMgr->SetLastError( 2405, "Error in Pocketing : Empty RawBox") ;
return false ;
}
// recupero la Part in cui è contenuto il volume
Point3d ptC ; pStm_PartVolume->GetCentroid( ptC) ;
PtrOwner<ISurfTriMesh> pStm_Part( CreateSurfTriMesh()) ;
if ( IsNull( pStm_Part) ||
! GetCurrentPart( ptC, pStm_Part))
return false ;
// rendo il volume accettabile per la svuotatura...
if ( ! AdjustPocketingSideForVolumePart( pStm_PartVolume, vtTool))
return false ;
// devo calcolare l'Elevazione... ( 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. ;
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 calcolo l'elevazione
for ( int j = 0 ; j < int( vPL.size()) ; ++ j) {
PtrOwner<ICurveComposite> pCompo( CreateCurveComposite()) ;
double dCurrElev = 0. ;
if ( IsNull( pCompo) ||
! pCompo->FromPolyLine( vPL[j]) ||
! pCompo->IsValid() ||
! CalcRegionElevation( pCompo, 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) ;
}
}
pStm_PartVolume->Invert() ; // riporto come in orginale
// eventuale imposizione massima elevazione da note utente
double dMaxElev ;
if ( FromString( ExtractInfo( m_Params.m_sUserNotes, "MaxElev="), dMaxElev) && dElev > dMaxElev)
dElev = dMaxElev ;
// verifico che lo step dell'utensile sia sensato
double dOkStep = ( m_Params.m_dStep > EPS_SMALL ? m_Params.m_dStep + EPS_SMALL : 0) ;
const double MIN_ZSTEP = 1.0 ;
if ( dOkStep >= EPS_SMALL && dOkStep < MIN_ZSTEP) {
dOkStep = MIN_ZSTEP + EPS_SMALL ;
string sInfo = "Warning in Pocketing : machining step too small (" + ToString( m_Params.m_dStep, 2) + ")" ;
m_pMchMgr->SetWarning( 2456, sInfo) ;
}
// verifico che il massimo materiale dell'utensile sia sensato
const double MIN_MAXMAT = 1.0 ;
if ( m_TParams.m_dMaxMat + 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 eventuale minima lunghezza di attacco su lato aperto
FromString( ExtractInfo( m_Params.m_sUserNotes, "OpenMinSafe="), m_dOpenMinSafe) ;
// se richiesta anteprima
if ( nPvId != GDB_ID_NULL) {
// creo gruppo per geometria di lavorazione del percorso
int nPxId = m_pGeomDB->AddGroup( GDB_ID_NULL, nPvId, Frame3d()) ;
if ( nPxId == GDB_ID_NULL)
return false ;
m_pGeomDB->SetName( nPxId, sPathName) ;
m_pGeomDB->SetMaterial( nPxId, GREEN) ;
// creo l'anteprima del percorso
if ( ! GeneratePocketingPv( nPxId, 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<int>( ceil( dElev / dOkStep))) ;
double dStep = dElev / nStep ;
// porto il frame alla minima Z ( sarà negativa) di svuotatura
frElevation.Set( ptC + vtTool * dZ_min, vtTool) ;
if ( ! frElevation.IsValid())
return false ;
// porto il volume e la Part che lo contiene in questo frame
pStm_PartVolume->ToLoc( frElevation) ;
pStm_Part->ToLoc( frElevation) ;
// 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<ICurveComposite> 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<ICRVCOMPOPOVECTOR> 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
// ricavo le superfici di svuotatura
if ( ! SliceVolume( pStm_PartVolume, pStm_Part, vSrfSliced, vSrfLimit, vCrvOEWithFlags,
vbChangedPrec, vVtTrasl, nStep, vtTool, dElev, dDepth, dStep))
return false ;
// riporto il volume nel frame globale
pStm_PartVolume->ToGlob( frElevation) ;
// riporto le superfici nel frame globale
for ( int s = 0 ; s < int( vSrfSliced.size()) ; ++ s)
vSrfSliced[s]->ToGlob( frElevation) ;
// riporto le superfici limite nel frame globale
for ( int s = 0 ; s < int( vSrfLimit.size()) ; ++ s)
vSrfLimit[s]->ToGlob( frElevation) ;
for ( int s = 0 ; s < int( vSrfLimit.size()) ; ++ s) {
if ( ! vSrfLimit[s]->IsValid())
continue ;
PtrOwner<ISurfFlatRegion> pSrf_toDraw( CloneSurfFlatRegion( vSrfLimit[s])) ;
int a = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pSrf_toDraw->Clone()) ;
m_pGeomDB->SetMaterial( a, Color( 0.0, 0.0, 1.0, 0.45)) ;
/*for ( int c = 0 ; c < pSrf_toDraw->GetChunkCount() ; ++ c) {
for ( int l = 0 ; l < pSrf_toDraw->GetLoopCount( c) ; ++ l) {
PtrOwner<ICurveComposite> pCrvCompo( GetCurveComposite( pSrf_toDraw->GetLoop( c, l))) ;
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
int nProp0 ; pCrvCompo->GetCurveTempProp( u, nProp0, 0) ;
int nProp1 ; pCrvCompo->GetCurveTempProp( u, nProp1, 1) ;
int aaa = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pCrvCompo->GetCurve( u)->Clone()) ;
m_pGeomDB->SetMaterial( aaa, nProp0 == 0 ? BLUE : RED) ;
}
}
}*/
}
// 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))
return false ;
break ;
case POCKET_SUB_SPIRALOUT :
if ( ! AddSpiralOut( vSrfSliced, vCrvOEWithFlags, vbChangedPrec, vVtTrasl, vtTool, vtTool, dDepth, dElev,
dMaxElev, dOkStep, bSplitArcs))
return false ;
break ;
}
}
// incremento numero di svuotature
++ m_nPockets ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::GetProjectionOfStmToNotPocket( const ISurfTriMesh* pStmPart, const ISurfTriMesh* pStmVol,
const Vector3d& vtTrasl, ISurfFlatRegion* pSfrProj)
{
// controllo i parametri
if ( pStmPart == nullptr || ! pStmPart->IsValid() ||
pStmVol == nullptr || ! pStmVol->IsValid())
return false ;
// ricavo la parte di grezzo da non rovinare
PtrOwner<ISurfTriMesh> pStmToNotRuin( CloneSurfTriMesh( pStmPart)) ;
if ( IsNull( pStmToNotRuin) ||
! pStmToNotRuin->IsValid() ||
! pStmToNotRuin->Subtract( *pStmVol) ||
! pStmToNotRuin->IsValid())
return false ;
// NB. La parte vera a propria di interesse è quella sopra la piano 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<ISurfFlatRegion> pSfrLimitH( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrLimitH) ||
! ProjectStmOnPlane( pStmToNotRuin, plZ, pSfrLimitH))
return false ;
// controllo per estrema sicurezza la coerenza con le normali
if ( AreOppositeVectorApprox( pSfrLimitH->GetNormVersor(), Z_AX))
pSfrLimitH->Invert() ;
// ritorno la superficie
pSfrProj->CopyFrom( pSfrLimitH) ;
return pSfrProj->IsValid() && pSfrProj->GetChunkCount() > 0 ;
}
//----------------------------------------------------------------------------
bool
Pocketing::SliceVolume( const ISurfTriMesh* pStmVol, const ISurfTriMesh* pStm_Part, ISURFFRPOVECTOR& vSrfSliced,
ISURFFRPOVECTOR& vSfrLimit, std::vector<ICRVCOMPOPOVECTOR>& vCrvOEWithFlags, BOOLVECTOR& vbChangedPrec,
VCT3DVECTOR& vVtTrasl, int& nStep, const Vector3d vtTool,
const double dElev, const double dDepth, const double dStep)
{
// controllo dei parametri
if ( pStmVol == nullptr || ! pStmVol->IsValid() ||
pStm_Part == nullptr || ! pStm_Part->IsValid())
return false ;
// 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))
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<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrLimit) ||
! GetProjectionOfStmToNotPocket( pStm_Part, pStmVol, vVtTrasl[j-1], pSfrLimit))
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<ISurfFlatRegion> pSfrCurr( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrCurr) ||
! CutVolumeByPlane( pStmVol, pStm_Part, vVtTrasl[j-1], bIsExtraStep, pSfrCurr))
return false ;
// tolgo/aggiungo eventuali proiezioni
if ( ! ProjectVolume( pStmVol, pStm_Part, vVtTrasl[j-1], j == 1 ? V_INVALID : vVtTrasl[j-2], pSfrLimit, pSfrCurr))
return false ;
// ricavo le proprietà di lato aperto/chiuso
if ( ! ChooseCloseOrOpenEdge( pSfrCurr, ( bIsExtraStep && ! m_bPocketPlane) ? vStmES_Edges_OC[j-1] : pStm_Part,
bIsExtraStep && ! m_bPocketPlane))
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<ISurfFlatRegion> pSfrNoExtension( CloneSurfFlatRegion( pSfrCurr)) ;
if ( IsNull( pSfrNoExtension) ||
! ModifySurfByOpenEdges( pSfrCurr, pSfrLimit))
return false ;
// controllo se la superficie deriva da una lavorazione precedente
if ( ! GetNewSfrByAnotherPocketing( pSfrCurr, pSfrNoExtension, pStmVol, pStm_Part, pSfrLimit))
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::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<ISurfTriMesh> 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<double> 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<STEP> 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<ISurfTriMesh> 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)) ;
}
}
}
// Offset L ?
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<ISurfFlatRegion> 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<ISurfFlatRegion> 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<ISurfFlatRegion> 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<ISurfTriMesh> 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<ICurveComposite> 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<ISurfFlatRegion> 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<ICurveComposite> pCrvLoop( CreateCurveComposite()) ;
if ( IsNull( pCrvLoop) ||
! pCrvLoop->FromPolyLine( vPL[p]) ||
! pCrvLoop->IsValid())
return false ;
// creo la TriMesh della faccia mancante
PtrOwner<ISurfTriMesh> 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<ISurfTriMesh> 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<ICurveComposite> pCrvCompo( CreateCurveComposite()) ;
pCrvCompo->FromPolyLine( vPL[i]) ;
if ( ! pCrvCompo->IsValid())
return false ;
// la proietto sul piano per maggiore sicurezza
PtrOwner<ICurve> 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<ISurfFlatRegion> pSfr_Final( SfrBC_final.GetSurf()) ;
while ( ! IsNull( pSfr_Final) && pSfr_Final->IsValid()) {
// controllo che sia orientata come Z_AX
if ( AreOppositeVectorApprox( Z_AX, pSfr_Final->GetNormVersor()))
pSfr_Final->Invert() ;
// prendo la supercicie e aggiorno quella da restituire
if ( ! pSfrResult->IsValid())
pSfrResult->CopyFrom( pSfr_Final) ;
else
if ( ! pSfrResult->Add( * pSfr_Final))
return false ;
pSfr_Final.Set( SfrBC_final.GetSurf()) ;
}
// restituisco
return ! ( pSfrResult == nullptr || ! pSfrResult->IsValid()) ;
}
//----------------------------------------------------------------------------
bool
Pocketing::ProjectVolume( const ISurfTriMesh* pStmVol, const ISurfTriMesh* pStm_Part, const Vector3d& vtTrasl,
const Vector3d& vtTrasl1, ISurfFlatRegion* pSfrLimit, ISurfFlatRegion* pSfr)
{
// controllo dei parametri
if ( pStmVol == nullptr || ! pStmVol->IsValid() ||
pStm_Part == nullptr || ! pStm_Part->IsValid() ||
pSfr == nullptr || ! pSfr->IsValid())
return false ;
// controllo se il tool ha un SideAngle
if ( m_TParams.m_dSideAng != 0) {
// ... controllo che gli angoli delle facce siano coerenti al tool selezionato
// ...
// ...
// ...
return true ;
}
/*
Vengono creati due piano di Taglio : plNear e plFar
1) plNear : piano di taglio allo step attuale con normale Z_AX
2) plFar : piano di taglio riferito allo step successivo ( se lo step è il primo allora è impostato ad una quota alta)
Ogni faccia del volume di svuotatura ( stm) è classificata come Aperta ( A) o Chiusa ( C)
A : questa faccia corrisponde ad una faccia della Part
C : questa faccia deriva dalla TriMesh selezionata ( o curve, vedi funzione GetCurvesAndPartialVolume())
* le facce C vanno proiettate sul piano di svuotatura attuale e sottratte alla *pSfr
-> si usa solo il plNear
* le facce A sono quelle che verranno utilizzate per la proiezione della Part sul piano di svuotatura attuale
-> si usa sia il plNear che il plFar
* il volume di svuotatura viene tagliato dal plNear e plFar, ottenendo la parte compresa tra i due piani
* ( in questo modo la proiezione sul piano di svuotatura considera svuotato tutto ciò che è sopra
allo step precedente, quindi con quota Z maggiore di esso )
* la proiezione consiste quindi nel proiettare le facce A di questa parte di Volume di Pocketing.
*/
// plNear
Point3d ptNear = ORIG + vtTrasl ;
Vector3d vtN = Z_AX ;
Plane3d plNear ; plNear.Set( ptNear, -vtN) ;
if ( ! plNear.IsValid())
return false ;
// plFar
Point3d ptFar = ORIG + ( vtTrasl1.IsValid() ? vtTrasl1 : 5000 * Z_AX) ;
Plane3d plFar ; plFar.Set( ptFar, vtN) ;
if ( ! plFar.IsValid())
return false ;
// ricavo il volume di svuotatura sopra al piano plNear
PtrOwner<ISurfTriMesh> 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<ISurfTriMesh> 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<ISurfFlatRegion> pSfr_ExtendProjection( CreateSurfFlatRegion()) ;
if ( IsNull( pSfr_ExtendProjection))
return false ;
if ( pStmBetween_Planes->IsValid() || pStmBetween_Planes->GetTriangleCount() > 0) {
// tutta questa superficie contiene solo facce A, le proietto sul piano plNear
if ( ! ProjectStmOnPlane( pStmBetween_Planes, plNear, pSfr_ExtendProjection))
return false ;
// controllo ( per estrema sicurezza) la coerenza con le normali
if ( AreOppositeVectorApprox( pSfr->GetNormVersor(), pSfr_ExtendProjection->GetNormVersor()))
pSfr_ExtendProjection->Invert() ;
// NB. Questa superficie esce dal volume definito, devo stare attento a non rovinare parti di grezzo
if ( pSfrLimit->IsValid() && pSfr_ExtendProjection->IsValid()) {
if ( ! pSfr_ExtendProjection->Subtract( *pSfrLimit))
return false ;
}
}
// -------------------------------------------------------------------------------------------------
// per pStmAbove_Near ricavo gli indici dei triangoli delle facce A ( tutti questi triangoli andranno rimossi
// lasciando quindi solo le facce C)
INTVECTOR vAllTria_A ;
for ( int f = 0 ; f < pStmAbove_Near->GetFacetCount() ; ++ f) {
// ricavo i parametri della faccia
Point3d ptC_f ; Vector3d vtN_f ;
if ( ! pStmAbove_Near->GetFacetCenter( f, ptC_f, vtN_f))
return false ;
// controllo se si tratta di una faccia interna alla Part
DistPointSurfTm distPtStm( ptC_f, *pStm_Part) ;
if ( ! distPtStm.IsPointInside()) {
// se faccia interna, prendo tutti i suoi tringoli ...
INTVECTOR vT ;
if ( ! pStmAbove_Near->GetAllTriaInFacet( f, vT))
return false ;
// ... e aggiungo i loro indici al vettore
for ( int t = 0 ; t < int( vT.size()) ; ++ t)
vAllTria_A.push_back( vT[t]) ;
}
}
// dalla superficie sopra al piano di svuotatura rimouovo i triangoli ottenuti
for ( int t = 0 ; t < int( vAllTria_A.size()) ; ++ t)
if ( ! pStmAbove_Near->RemoveTriangle( vAllTria_A[t]))
return false ;
pStmAbove_Near->DoCompacting() ;
// se non ho ricavato nulla, significa che il volume di svuotatura ( tagliato con il piano, e quindi senza le
// facce da svuotare) descrive la stessa geometria della Part ( quindi ogni faccia è sulla Part)
// ---> non c'è nulla da proiettare
PtrOwner<ISurfFlatRegion> pSfrProjection( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrProjection))
return false ;
if ( pStmAbove_Near->IsValid() && pStmAbove_Near->GetTriangleCount() > 0) {
// tutta questa superficie contiene solo facce C, le proietto sul piano plNear
if ( ! ProjectStmOnPlane( pStmAbove_Near, plNear, pSfrProjection))
return false ;
// controllo ( per estrema sicurezza) la coerenza con le normali
if ( AreOppositeVectorApprox( pSfr->GetNormVersor(), pSfrProjection->GetNormVersor()))
pSfrProjection->Invert() ;
}
// -------------------------------------------------------------------------------------------------
// 1) La superficie con le facce A tra i due piani plNear e plFar indentificano una proeizione che estende la
// superficie attuale da svuotare
// 2) La superficie con le facce C al di sopra del piano plNear identificano una proeizione che riduce la
// superficie attuale da svuotare
// 1) ricavo la superificie da svuotare come addizione tra la ( *Sfr) e la ( *pSfr_ExtendProjection)
if ( pSfr_ExtendProjection->IsValid())
if ( ! pSfr->Add( *pSfr_ExtendProjection))
return false ;
// 2) ricavo la superficie da svuotare come sottrazione tra la ( *pSfr) e la ( *pSfrProjection)
if ( pSfr->IsValid() && pSfrProjection->IsValid())
if ( ! pSfr->Subtract( *pSfrProjection))
return false ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::ChooseCloseOrOpenEdge( ISurfFlatRegion* pSfr, const ISurfTriMesh* pStm, 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<ISurfFlatRegion> 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<ICurveComposite> 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<ICurveComposite> 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<ISurfFlatRegion> 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<ICurveComposite> 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<ICurveComposite> 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)) {
for ( int u = 0 ; u < pCrvEL->GetCurveCount() ; ++ u) {
int nProp0 ; pCrvEL->GetCurveTempProp( u, nProp0, 0) ;
int nProp1 ; pCrvEL->GetCurveTempProp( u, nProp1, 1) ;
int aaa = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pCrvEL->GetCurve( u)->Clone()) ;
m_pGeomDB->SetMaterial( aaa, nProp0 == 0 ? BLUE : RED) ;
}
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<ICurveComposite> 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<ISurfFlatRegion> 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<ISurfTriMesh> 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<ICRVCOMPOPOVECTOR>& 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<ICurveComposite> 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<CrvInfo> vCrvInfo ;
vector<ChunkInfo> 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<ISurfFlatRegion> 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<ISurfFlatRegion> pSrfChunk(( vSfr[j-1]->CloneChunk( c))) ;
if ( IsNull( pSrfChunk))
return false ;
// cerco la curva originale del chunk (cc)-esimo ( per casi ottimizzati)
int nInd = 0 ; // indice del vettore delle curve esterne relativo al chunk (cc)-esimo
if ( ! GetOptCrvIndex( vCrvOrig, j, pSrfChunk, nInd))
return false ;
// determino il riferimento in base alla svuotatura ( per poter orientare il frame per m_dSideAngle)
Frame3d frPocket ;
Point3d ptCen ; pSrfChunk->GetCentroid( ptCen) ;
frPocket.Set( ptCen, vtExtr) ;
frPocket.Rotate( ptCen, vtExtr, m_Params.m_dSideAngle) ;
// porto la superificie nel nuovo sistema di riferimento
pSrfChunk->ToLoc( frPocket) ;
// verifico se si tratta di caso ottimizzato
bool bOptimizedZigZag = false ;
ICRVCOMPOPOVECTOR vpCrvs ; // vettore con i paths a ZigZag per il Chunk (cc)-esimo allo step (j)-esimo
ICRVCOMPOPOVECTOR vCrvIslMergeBorders ; // vettore delle possibili isole mergiate per il Chunk (cc)-esimo allo step (j)-esimo
double dOptZigZagOffs = 0. ; // Offset per uscita dal grezzo nel caso di ottimizzazione
int nClosedSides = 0 ; // se caso ottimizzato, numero di lati chiusi
bool bTwoOpposite = false ; // se caso otimizzato, flag per lati chiusi opposti
if ( ! OptimizedZigZag( pSrfChunk, vtTool, dDepth, dSafeZ, frPocket, vCrvOrig[j-1][nInd],
bOptimizedZigZag, vpCrvs, vCrvIslMergeBorders, dOptZigZagOffs, nClosedSides,
bTwoOpposite))
return false ;
// aggiorno le informazioni sul chunk attuale ( potrebbero essere usate per il successivo)
vChunkInfo[c].bOptZigZag = bOptimizedZigZag ;
vChunkInfo[c].nOptClosedSides = nClosedSides ;
vChunkInfo[c].bOptTwoOpposite = bTwoOpposite ;
vChunkInfo[c].frFrame = frPocket ;
vChunkInfo[c].dOptZigZagOffs = dOptZigZagOffs ;
// se CASO OTTIMIZZATO e vettore di curve ZigZag valido
if ( bOptimizedZigZag) {
vCrvInfo.resize( int( vpCrvs.size()) + int( vCrvIslMergeBorders.size())) ;
// aggiorno le curve per lo step attuale ( potrebbero essere usate per il successivo)
for ( int x = 0 ; x < int( vpCrvs.size()) ; ++ x) {
vCrvInfo[x].pCrvCompo.Set( vpCrvs[x]->Clone()) ;
Point3d ptS ;
vCrvInfo[x].pCrvCompo->GetStartPoint( ptS) ;
vCrvInfo[x].ptStart = ptS ;
vCrvInfo[x].nChunk = c ;
}
// creo localmente i parametri della funzione
VCT3DVECTOR vVtMidOut(( int)vCrvIslMergeBorders.size()) ;
BOOLVECTOR vbMidOut(( int)vCrvIslMergeBorders.size()) ;
PNTVECTOR vPtStart(( int)vCrvIslMergeBorders.size()) ;
// se la superficie è cambiata, allora ricalcolo i parametri
for ( int x = 0 ; x < int( vCrvIslMergeBorders.size()) ; ++ x) {
// se richisto, inverto
if ( m_Params.m_bInvert)
vCrvIslMergeBorders[x]->Invert() ;
// setto la Feed per la curva di contorno
//AssignFeedForEdgeCleaning( vCrvIslMergeBorders[x], vCrvOEWithFlags[j-1], nInd) ;
if ( vCrvIslMergeBorders[x]->IsClosed()) {
bool bOutTmp = false ;
if ( ! SetBetterPtStartForSubChunks( vCrvIslMergeBorders[x], pSrfChunk,
vPtStart[x], vVtMidOut[x], bOutTmp))
return false ;
vbMidOut[x] = bOutTmp ; // vector<bool>::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<ISurfFlatRegion> 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<ISurfFlatRegion> 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<ISurfFlatRegion> pSrfZigZagChunk( pSrfZigZag->CloneChunk( cfz)) ;
if ( ! CalcZigZag( pSrfZigZagChunk, vpCrvs))
return false ;
// memorizzo le curve ( per lo step successivo)
int nIndex_offs = int( vCrvInfo.size()) ;
vCrvInfo.resize( int( vpCrvs.size()) + nIndex_offs) ;
for ( int x = 0 ; x < int( vpCrvs.size()) ; ++ x) {
vCrvInfo[nIndex_offs + x].pCrvCompo.Set( Release( vpCrvs[x])) ;
vCrvInfo[nIndex_offs + x].nChunk = c ;
}
}
// se la regione non è dentro alla curva esterna allora la ignoro
// ( la ritroverò interna ad un'altra curva per i bordi successivamante o l'ho già considerata in precedenza)
else
continue ;
}
// se lucidatura non aggiungo contorno
if ( m_TParams.m_nType == TT_MILL_POLISHING)
continue ;
// Una volta percorse le curve di ZigZag devo aggiungere le curve per il bordo
// creo un vettore con tutti i loop formati, aggiustando i loro punti iniziali
// e portandoli nel frame della svuotatura
ICRVCOMPOPOVECTOR vAllCrv ;
VCT3DVECTOR vVtMidOut( pSrfForCrv->GetLoopCount( cfc)) ;
BOOLVECTOR vbMidOut( pSrfForCrv->GetLoopCount( cfc)) ;
PNTVECTOR vPtStart( pSrfForCrv->GetLoopCount( cfc)) ;
for ( int l = 0 ; l < pSrfForCrv->GetLoopCount( cfc) ; ++ l) {
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSrfForCrv->GetLoop( cfc, l))) ;
if ( IsNull( pCrvLoop))
return false ;
bool bOutTmp = false ;
if ( ! SetBetterPtStartForSubChunks( pCrvLoop, pSrfChunk, vPtStart[l], vVtMidOut[l], bOutTmp))
return false ;
vbMidOut[l] = bOutTmp ; // vector<bool>::reference da Bit a Bool
// se richiesto, la inverto
if ( m_Params.m_bInvert)
pCrvLoop->Invert() ;
// setto la Feed per la curva di contorno
//AssignFeedForEdgeCleaning( pCrvLoop, vCrvOEWithFlags[j-1], nInd) ;
// inserisco la curva nel vettore
vAllCrv.emplace_back( Release( pCrvLoop)) ;
}
// guardo il punto finale in cui si trova la fresa dopo al percorso a ZigZag
// e le riordino le curve ( e i relativi vettori ) in base alla vicinanza
// ( piccola ottimizzazione per l'ordine dei percorsi sui bordi )
Point3d ptEnd ;
if ( vpCrvs.size() > 0)
vCrvInfo.back().pCrvCompo->GetEndPoint( ptEnd) ;
// riordino le curve
if ( ! OrderCurvesByLastPntOfPath( vAllCrv, ptEnd, vPtStart, vVtMidOut, vbMidOut))
return false ;
int nIndex_offs = int( vCrvInfo.size()) ;
vCrvInfo.resize( int( vAllCrv.size()) + nIndex_offs) ;
for ( int x = 0 ; x < int( vAllCrv.size()) ; ++ x) {
vCrvInfo[nIndex_offs + x].pCrvCompo.Set( Release( vAllCrv[x])) ;
vCrvInfo[nIndex_offs + x].nCrvType = CRV_BORDER ;
vCrvInfo[nIndex_offs + x].bMidOut = vbMidOut[x] ;
vCrvInfo[nIndex_offs + x].ptStart = vPtStart[x] ;
vCrvInfo[nIndex_offs + x].vtMidOut = vVtMidOut[x] ;
vCrvInfo[nIndex_offs + x].nChunk = c ;
}
}
}
}
// disegno a seconda del tipo di curva che ho ricavato
for ( int u = 0 ; u < int( vCrvInfo.size()) ; ++ u) {
// prendo la curva corrente
PtrOwner<ICurveComposite> 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<ICurve> pCurve( pCrvC->Clone()) ;
if ( IsNull( pCurve))
return false;
// --- se PRIMA ENTITA' ---
if ( i == 0) {
// dati inizio entità
Point3d ptStart ;
pCurve->GetStartPoint( ptStart) ;
Vector3d vtStart ;
pCurve->GetStartDir( vtStart) ;
// determino inizio attacco
Point3d ptP1 ;
if ( ! CalcLeadInStart( ptStart, vtStart, vtExtr, nullptr, ptP1))
return false ;
// determino elevazione su inizio attacco
double dStElev = dSink ;
dStElev -= ( ptP1 - ptStart) * vtExtr ;
// se ottimizzata e attacco nel grezzo
if ( bOptZigZag && !( bOutRawLeadIn || m_bOpenOutRaw)) {
// se richiesto attacco a zigzag o a spirale, l'elevazione va nell'attacco
if ( GetLeadInType() == POCKET_LI_ZIGZAG || GetLeadInType() == POCKET_LI_HELIX) {
ptP1 += vtExtr * dStElev ;
dStElev = 0 ;
}
}
// se inizio, approccio globale al punto iniziale
if ( bStart) {
if ( ! AddApproach( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr, bOutRawLeadIn || m_bOpenOutRaw)) {
m_pMchMgr->SetLastError( 2414, "Error in Pocketing : Approach not computable") ;
return false ;
}
bStart = false ;
}
// altrimenti, approccio di collegamento
else {
if ( ! ( nStep > 1 && j > 1 && bOutRawLeadIn && bOptZigZag)) {
if ( ! AddLinkApproach( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr, bOutRawLeadIn || m_bOpenOutRaw)) {
m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ;
return false ;
}
}
}
// aggiungo attacco
SetFeed( GetStartFeed()) ;
if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, nullptr, !m_Params.m_bInvert,
bSplitArcs, bOutRawLeadIn || m_bOpenOutRaw)) {
m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ;
return false ;
}
}
// elaborazioni sulla curva corrente
//double dMinFeed = GetFeed() * GetSideStep() / m_TParams.m_dDiam ;
//double dFeed = pCurve->GetTempProp( 0) / FEED_DIVISOR < dMinFeed - 50 * EPS_SMALL ?
// GetFeed() : pCurve->GetTempProp( 0) / FEED_DIVISOR ;
//SetFeed( dFeed) ;
if ( pCurve->GetType() == CRV_LINE) {
ICurveLine* pLine = GetCurveLine( pCurve) ;
Point3d ptP3 = pLine->GetEnd() ;
SetFeed( GetFeed()) ;
if ( AddLinearMove( ptP3) == GDB_ID_NULL)
return false ;
}
else if ( pCurve->GetType() == CRV_ARC) {
ICurveArc* pArc = GetCurveArc( pCurve) ;
Point3d ptCen = pArc->GetCenter() ;
double dAngCen = pArc->GetAngCenter() ;
Vector3d vtN = pArc->GetNormVersor() ;
Point3d ptP3 ;
pArc->GetEndPoint( ptP3) ;
SetFeed( GetFeed()) ;
if ( AddArcMove( ptP3, ptCen, dAngCen, vtN) == GDB_ID_NULL)
return false ;
}
// --- se ULTIMA ENTITA' ---
if ( i == nMaxInd) {
// dati fine entità
Point3d ptEnd ;
pCurve->GetEndPoint( ptEnd) ;
Vector3d vtEnd ;
pCurve->GetEndDir( vtEnd) ;
// aggiungo uscita
double dEndElev = dSink ;
Point3d ptP1 ;
SetFeed( GetEndFeed()) ;
if ( ! AddLeadOut( ptEnd, vtEnd, vtExtr, nullptr, bSplitArcs, true, ptP1, dEndElev, false)) {
m_pMchMgr->SetLastError( 2416, "Error in Pocketing : LeadOut not computable") ;
return false ;
}
// se lucidatura o caso ottimizzato e ultimo percorso di ultimo step, aggiungo retrazione
if (( m_TParams.m_nType == TT_MILL_POLISHING || bOptZigZag) && j == nStep) {
if ( ! AddRetract( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) {
m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Retract not computable") ;
return false ;
}
}
// altrimenti, aggiungo retrazione di collegamento
else {
if ( ! AddLinkRetract( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) {
m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ;
return false ;
}
}
}
}
}
// =================== CURVA BORDO ESTERNO ==============================================
else {
bool bForcedOutStart = false ;
// se la curva è valida per entrata da fuori, aggiungo un piccolo tratto lineare
if ( vCrvInfo[u].bMidOut) {
// calcolo il punto fuori
Point3d ptOut = vCrvInfo[u].ptStart + vCrvInfo[u].vtMidOut * ( 0.5 * m_TParams.m_dDiam + max( dSafeZ, m_dOpenMinSafe)) ;
ptOut.ToGlob( frCurr) ;
ptOut.Translate( vVtTrasl[j-1]) ;
double dStElev ;
bool bOutStart = ( ! GetElevation( m_nPhase, ptOut, vtTool, 0.5 * m_TParams.m_dDiam, vtTool, dStElev) ||
dStElev < EPS_SMALL) ;
if ( bOutStart || m_bOpenOutRaw) {
// aggiungo alla curva il tratto lineare
ptOut.Translate( - vVtTrasl[j-1]) ;
pCrvCurr->AddLine( ptOut, false) ;
AssignFeedForLineInOut( pCrvCurr, true) ;
}
// verifico se ingresso da considerare fuori grezzo anche se dentro
bForcedOutStart = ( vCrvInfo[u].bMidOut && m_bOpenOutRaw) ;
// se utensile che non lavora di testa e ingresso non fuori dal pezzo, errore
if ( m_TParams.m_nType == TT_MILL_NOTIP && !bOutStart && ! bForcedOutStart) {
if ( ! LeadInRawIsOk()) {
m_pMchMgr->SetLastError( 2431, "Error in Pocketing : LeadIn with Mill NoTip in material") ;
return false;
}
}
}
// ----- disegno le curve di contorno -------------------
// aggiungo la lavorazione di questa curva
Point3d ptP1 ;
// ciclo sulle curve elementari
int nMaxInd = pCrvCurr->GetCurveCount() - 1 ;
for ( int i = 0 ; i <= nMaxInd ; ++i) {
// curva corrente
const ICurve* pCrvC = pCrvCurr->GetCurve( i) ;
// copio la curva
PtrOwner<ICurve> pCurve( pCrvC->Clone()) ;
if ( IsNull( pCurve))
return false ;
// --- se PRIMA ENTITA' ---
if ( i == 0) {
// dati inizio entità
Point3d ptStart ;
Vector3d vtStart ;
pCurve->GetStartPoint( ptStart) ;
pCurve->GetStartDir( vtStart) ;
// se primo step, approccio e affondo
// determino inizio attacco
if ( ! CalcLeadInStart( ptStart, vtStart, vtExtr, nullptr, ptP1))
return false ;
// determino elevazione su inizio attacco
double dStElev = dSink ;
dStElev -= ( ptP1 - ptStart) * vtExtr ;
// se attacco a zigzag o a spirale o a scivolo, l'elevazione va nell'attacco
if ( GetLeadInType() == POCKET_LI_ZIGZAG ||
GetLeadInType() == POCKET_LI_HELIX ||
GetLeadInType() == POCKET_LI_GLIDE) {
ptP1 += vtExtr * ( dStElev + LIO_ELEV_TOL) ;
dStElev = -LIO_ELEV_TOL ;
}
// approccio al punto iniziale
if ( ! AddLinkApproach( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr,
vCrvInfo[u].bMidOut || bForcedOutStart)) {
m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ;
return false ;
}
// aggiungo attacco
SetFeed( GetStartFeed()) ;
if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, nullptr, !m_Params.m_bInvert,
bSplitArcs, vCrvInfo[u].bMidOut || bForcedOutStart)) {
m_pMchMgr->SetLastError(2415, "Error in Pocketing : LeadIn not computable");
return false;
}
}
// elaborazioni sulla curva corrente
//double dMinFeed = GetFeed() * GetSideStep() / m_TParams.m_dDiam ;
//double dFeed = pCurve->GetTempProp( 0) / FEED_DIVISOR < dMinFeed - 50 * EPS_SMALL ?
// GetFeed() : pCurve->GetTempProp( 0) / FEED_DIVISOR ;
//SetFeed( dFeed) ;
if ( pCurve->GetType() == CRV_LINE) {
ICurveLine* pLine( GetCurveLine( pCurve)) ;
Point3d ptP3 = pLine->GetEnd() ;
SetFeed( GetFeed()) ;
if ( AddLinearMove( ptP3) == GDB_ID_NULL)
return false ;
}
else if ( pCurve->GetType() == CRV_ARC) {
ICurveArc* pArc( GetCurveArc( pCurve)) ;
Point3d ptCen = pArc->GetCenter() ;
double dAngCen = pArc->GetAngCenter() ;
Vector3d vtN = pArc->GetNormVersor() ;
Point3d ptP3 ;
pArc->GetEndPoint( ptP3) ;
SetFeed( GetFeed()) ;
if ( AddArcMove( ptP3, ptCen, dAngCen, vtN) == GDB_ID_NULL)
return false ;
}
// se ultima entità
if ( i == nMaxInd) {
// se ultimo step, uscita e retrazione di collegamento
// dati fine entità
Point3d ptEnd ;
pCurve->GetEndPoint( ptEnd) ;
Vector3d vtEnd ;
pCurve->GetEndDir( vtEnd) ;
// aggiungo uscita
double dEndElev = dSink ;
SetFeed( GetEndFeed()) ;
if ( ! AddLeadOut( ptEnd, vtEnd, vtExtr, nullptr, bSplitArcs, false, ptP1, dEndElev, ( u == ( int)vCrvInfo.size()))) {
m_pMchMgr->SetLastError( 2416, "Error in Pocketing : LeadOut not computable") ;
return false ;
}
// se ci sono ancora curve, aggiungo retrazione di collegamento
if ( pCrvCurr->GetCurveCount() > 0) {
if ( ! AddLinkRetract( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) {
m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ;
return false ;
}
}
// altrimenti retrazione finale
else {
if ( ! AddRetract( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) {
m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Retract not computable") ;
return false ;
}
}
}
}
}
}
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::CalcZigZag( const ISurfFlatRegion* pSrfZigZag, ICRVCOMPOPOVECTOR& vpCrvs)
{
// check parametri
if ( pSrfZigZag == nullptr || ! pSrfZigZag->IsValid())
return true ;
// creo il vettore dei contorni
ICRVCOMPOPOVECTOR vFirstOff ;
for ( int c = 0 ; c < pSrfZigZag->GetChunkCount() ; ++ c) { // sempre 1...
for ( int l = 0 ; l < pSrfZigZag->GetLoopCount( c) ;++ l) {
vFirstOff.emplace_back( GetCurveComposite( pSrfZigZag->GetLoop( c, l))) ;
}
}
// ingombro del contorno offsettato
BBox3d b3Pocket ;
vFirstOff[0]->GetLocalBBox( b3Pocket) ;
Point3d ptMin ; double dDimX, dDimY, dDimZ ;
b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ;
// lunghezza del contorno offsettato
double dLen ; vFirstOff[0]->GetLength( dLen) ;
// passi in Y
int nYStep = static_cast< int >( ceil(( dDimY - 30 * EPS_SMALL) / GetSideStep())) ;
double dYStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ;
int nRef = (( nYStep + ( m_Params.m_bInvert ? 0 : 1)) % 2) ;
// tratto valido
struct Section {
bool bActive ;
Point3d ptS ;
Point3d ptE ;
double dOs ;
double dOe ;
int nOffIndS ;
int nOffIndE ;
} ;
struct ParIsland {
double dU ;
int nInd ;
} ;
// raccolta di tratti
typedef vector<vector<Section>> 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<ICurveLine> 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<ICurveLine> 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<ICurveComposite> pLastSeg( CreateCurveComposite()) ; // ultimo segmento nel percorso
PtrOwner<ICurveComposite> 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<ICurveLine> 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<ICurveLine> 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<ICurveComposite> 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<ICurveComposite> pSeg1( CloneCurveComposite( pLastSeg)) ;
PtrOwner<ICurveComposite> 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<ICurve> 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<ISurfFlatRegion> 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<ICurve> 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<ISurfFlatRegion> pSrfTool_Offs( CreateSurfFlatRegion()) ;
PtrOwner<ISurfFlatRegion> pSrfTool_Links( CreateSurfFlatRegion()) ;
PtrOwner<ISurfFlatRegion> 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<ICurveComposite> 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<ICurve> pCrvLink_i( vLinks[i]->Clone()) ;
if ( IsNull( pCrvLink_i))
return false ;
PtrOwner<ISurfFlatRegion> 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<ICurveComposite> pCrvOffs_i( CloneCurveComposite( vCrvs[i])) ;
if ( IsNull( pCrvOffs_i))
return false ;
PtrOwner<ISurfFlatRegion> 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<ICurveComposite> pCrvH1( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pCrvH2( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pCrvTempLink( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> 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<ICurveArc> 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<ICurveComposite> 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<ICurveComposite> 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<ISurfFlatRegion> pSrfPocket( CloneSurfFlatRegion( pSrf)) ;
if ( IsNull( pSrfPocket))
return false ;
// recupero la curva dal bordo della superficie
PtrOwner<ICurveComposite> pCrvPocket( ConvertCurveToComposite( pSrfPocket->GetLoop( 0, 0))) ;
if ( IsNull( pCrvPocket))
return false ;
pCrvPocket->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
// sistemo senso antiorario visto dalla direzione di estrusione
Vector3d vtPocket; pCrvPocket->GetExtrusion( vtPocket) ;
Plane3d plPlane ; double dArea ;
pCrvPocket->GetArea( plPlane, dArea) ;
if ( plPlane.GetVersN() * vtPocket * dArea < 0)
pCrvPocket->Invert() ;
// recupero gli id dei lati chiusi
INTVECTOR vnInfoClosed ;
for ( int i = 0 ; i < pCrvPocket->GetCurveCount() ; i ++) {
int nProp ;
if ( pCrvPocket->GetCurveTempProp( i, nProp) && nProp == 0)
vnInfoClosed.push_back( i) ;
}
nClosedSides = vnInfoClosed.size() ;
// modifico pCrvPocket per poterla passare a CalcZigZag
Vector3d vtDir ; // direzione principale del segmento più lungo (linea)
switch ( nClosedSides) {
case 0 : // 0 lati CHIUSI -> tutti APERTI
ZigZagOptimizedNoClosedEdges( pCrvPocket, bOptimizedZigZag, vtDir, dOffs) ;
break ;
case 1 : // 1 lato CHIUSO
ZigZagOptimizedOneClosedEdge( pCrvPocket, vnInfoClosed[0], bOptimizedZigZag, vtDir, dOffs) ;
break ;
case 2 : // 2 lati CHIUSI
ZigZagOptimizedTwoClosedEdges( pCrvPocket, vnInfoClosed, bOptimizedZigZag, bTwoOpposite, vtDir, dOffs) ;
break ;
case 3 : // 3 lati CHIUSI
ZigZagOptimizedThreeClosedEdges( pCrvPocket, vnInfoClosed, bOptimizedZigZag, bTwoOpposite, vtDir, dOffs) ;
break ;
default : // nessuna ottimizzazione...
bOptimizedZigZag = false ;
break ;
}
// se non sono in un caso ottimizzato, allora esco
if ( ! bOptimizedZigZag)
return true ;
// frame per direzione più lunga per la svuotatura a ZigZag
if ( vtDir.IsZero())
pCrvPocket->GetStartDir( vtDir) ;
double dAng ;
vtDir.GetAngleXY( X_AX, dAng) ;
if ( nClosedSides == 0)
dAng += m_Params.m_dSideAngle ;
pSrfPocket->ToGlob( frPocket) ;
pCrvPocket->ToGlob( frPocket) ;
Frame3d frameH( frPocket) ;
Point3d ptCen = frPocket.Orig() ;
Vector3d vtExtr = frPocket.VersZ() ;
frPocket.Rotate( ptCen, vtExtr, -dAng) ;
pSrfPocket->ToLoc( frPocket) ;
pCrvPocket->ToLoc( frPocket) ;
// parametri per Offset
double dTRad = m_TParams.m_dDiam / 2 ;
double dMyOffs = dTRad + GetOffsR() ;
double dExtra = (( m_TParams.m_nType != TT_MILL_POLISHING) ? min( 0.1 * m_TParams.m_dDiam, 2.0) : 0) ;
// Offsetto la superficie per ricavare informazioni su eventuali isole e sulle loro possibili unioni
PtrOwner<ISurfFlatRegion> pSfrZigZag( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrZigZag) || ! pSfrZigZag->AddExtLoop( *pCrvPocket))
return false ;
PtrOwner<ISurfFlatRegion> pSfrForCrv( CloneSurfFlatRegion( pSfrZigZag)) ;
if ( IsNull( pSfrForCrv))
return false ;
for ( int l = 1 ; l < pSrfPocket->GetLoopCount( 0) ; ++ l) {
PtrOwner<ICurve> 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<ICurve> 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<ICurve> pCrvExtLoop( pSfrForCrv->GetLoop( 0, 0)) ;
if ( IsNull( pCrvExtLoop))
return false ;
Plane3d plH ;
PtrOwner<ISurfFlatRegion> 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<ISurfFlatRegion> 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<ICurveComposite> 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<ISurfFlatRegion> 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<ICurve> 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<ICurveComposite> 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<ICurveLine> 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<ICurveLine> pCrv1( CloneCurveLine( pCrvPocket->GetCurve( vnClosedIds[0]))) ;
PtrOwner<ICurveLine> 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<ICurveLine> pCrv1( CloneCurveLine( pCrvPocket->GetCurve( vnClosedIds[0]))) ;
PtrOwner<ICurveLine> pCrv2( CloneCurveLine( pCrvPocket->GetCurve( vnClosedIds[1]))) ;
PtrOwner<ICurveLine> 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<int>( 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<ICurveComposite> 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<ICRVCOMPOPOVECTOR>& 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<ISurfFlatRegion> pSrfChunk( GetSurfFlatRegion( vSfr[j-1]->CloneChunk( c))) ;
if ( IsNull( pSrfChunk))
return false ;
// per entrate ed uscite
PtrOwner<ISurfFlatRegion> 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<ISurfFlatRegion> 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<ICurveComposite> 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<ISurfFlatRegion> pSrfCurrChunk( vSfr[j-1]->CloneChunk( vInd[u])) ;
if ( IsNull( pSrfCurrChunk))
return false ;
// imposto un punto valido per l'entrata
if ( ! SetBetterPtStartForSubChunks( vAllCrv[u], pSrfCurrChunk,
vPtStart[u], vVtMidOut[u], bOutTmp))
return false ;
vbMidOut[u] = bOutTmp ; // vector<bool>::reference da Bit a Bool
// riporto i valori nel sistema di riferimento corretto
vPtStart[u].ToGlob( frLocI) ;
vVtMidOut[u].ToGlob( frLocI) ;
vAllCrv[u]->ToGlob( frLocI) ;
// se richiesto, la inverto
if ( m_Params.m_bInvert)
vAllCrv[u]->Invert() ;
// setto la Feed per la curva di contorno
//AssignFeedForEdgeCleaning( vAllCrv[u], vCrvOEWithFlags[j-1]) ;
// se la curva è valida per entrata da fuori, aggiungo un piccolo tratto lineare
if ( vbMidOut[u]) {
// calcolo il punto fuori
Point3d ptOut = vPtStart[u] + vVtMidOut[u] * ( 0.5 * m_TParams.m_dDiam + max( dSafeZ, m_dOpenMinSafe)) ;
ptOut.Translate( vVtTrasl[j-1]) ; // lo traslo allo step corrente
double dStElev ;
bool bOutStart = ( ! GetElevation( m_nPhase, ptOut, vtTool, 0.5 * m_TParams.m_dDiam, vtTool, dStElev) ||
dStElev < EPS_SMALL) ;
if ( bOutStart || m_bOpenOutRaw) {
// aggiungo alla curva il tratto lineare
ptOut.Translate( - vVtTrasl[j-1]) ;
vAllCrv[u]->AddLine( ptOut, false) ;
AssignFeedForLineInOut( vAllCrv[u], true) ;
}
// verifico se ingresso da considerare fuori grezzo anche se dentro
vbForcedOutStart[u] = ( vbMidOut[u] && m_bOpenOutRaw) ;
// se utensile che non lavora di testa e ingresso non fuori dal pezzo, errore
if ( m_TParams.m_nType == TT_MILL_NOTIP && !bOutStart && !vbForcedOutStart[u]) {
if ( ! LeadInRawIsOk()) {
m_pMchMgr->SetLastError( 2431, "Error in Pocketing : LeadIn with Mill NoTip in material") ;
return false ;
}
}
}
}
// riordino le curve ( e i relativi vettori ) in base alla vicinanza
// ( piccola ottimizzazione per l'ordine dei percorsi sui bordi)
Point3d ptStart ; vAllCrv[0]->GetStartPoint( ptStart) ;
if ( ! OrderCurvesByLastPntOfPath( vAllCrv, ptStart, vPtStart, vVtMidOut, vbMidOut))
return false ;
// determino l'affondamento attuale
double dSink = dElev - dDepth + dMaxElev - vVtTrasl[j-1].Len() ;
// ---------------------------- Disegno le curve di contorno ----------------------
for ( int u = 0 ; u < int( vAllCrv.size()) ; ++ u) { // percorro tutte le curve ( ordinate)
// recupero la prima curva di offset disponibile
PtrOwner<ICurveComposite> 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<ICurve> pCurve( pCrvC->Clone()) ;
if ( IsNull( pCurve))
return false ;
// SE PRIMA ENTITA'
if ( i == 0) {
// dati inizio entità
Point3d ptStart ;
Vector3d vtStart ;
pCurve->GetStartPoint( ptStart) ;
pCurve->GetStartDir( vtStart) ;
Point3d ptForElev = ptStart ;
if ( vbMidOut[u] || vbForcedOutStart[u])
pCurve->GetEndPoint( ptForElev) ;
// determino inizio attacco
if ( ! CalcLeadInStart( ptStart, vtStart, vtExtr, nullptr, ptP1))
return false ;
// determino elevazione su inizio attacco
double dStElev = dSink ;
dStElev -= ( ptP1 - ptStart) * vtExtr ;
// se attacco a zigzag o a spirale o a scivolo, l'elevazione va nell'attacco
if ( GetLeadInType() == POCKET_LI_ZIGZAG ||
GetLeadInType() == POCKET_LI_HELIX ||
GetLeadInType() == POCKET_LI_GLIDE) {
ptP1 += vtExtr * ( dStElev + LIO_ELEV_TOL) ;
dStElev = -LIO_ELEV_TOL ;
}
// approccio al punto iniziale
if ( ! AddApproach( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr, vbMidOut[u] || vbForcedOutStart[u])) {
m_pMchMgr->SetLastError( 2414, "Error in Pocketing : Approach not computable") ;
return false ;
}
// aggiungo attacco
SetFeed( GetStartFeed()) ;
if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, nullptr, !m_Params.m_bInvert, bSplitArcs, vbMidOut[u] || vbForcedOutStart[u])) {
m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ;
return false ;
}
}
// elaborazioni sulla curva corrente
//double dMinFeed = GetFeed() * GetSideStep() / m_TParams.m_dDiam ;
//double dFeed = pCurve->GetTempProp( 0) / FEED_DIVISOR < dMinFeed - 50 * EPS_SMALL ?
// GetFeed() : pCurve->GetTempProp( 0) / FEED_DIVISOR ;
//DrawColoredCrvForFeedTest( pCurve, dFeed) ;
//SetFeed( dFeed) ;
if ( pCurve->GetType() == CRV_LINE) {
ICurveLine* pLine = GetCurveLine( pCurve) ;
Point3d ptP3 = pLine->GetEnd() ;
SetFeed( dFeedRid * GetFeed()) ;
if ( AddLinearMove( ptP3) == GDB_ID_NULL)
return false ;
}
else if ( pCurve->GetType() == CRV_ARC) {
ICurveArc* pArc = GetCurveArc( pCurve) ;
Point3d ptCen = pArc->GetCenter() ;
double dAngCen = pArc->GetAngCenter() ;
Vector3d vtN = pArc->GetNormVersor() ;
Point3d ptP3 ;
pArc->GetEndPoint( ptP3) ;
SetFeed( dFeedRid * GetFeed()) ;
if ( AddArcMove( ptP3, ptCen, dAngCen, vtN) == GDB_ID_NULL)
return false ;
}
// SE ULTIMA ENTITA'
if ( i == nMaxInd) {
// se ultimo step, uscita e retrazione di collegamento
// dati fine entità
Point3d ptEnd ;
pCurve->GetEndPoint( ptEnd) ;
Vector3d vtEnd ;
pCurve->GetEndDir( vtEnd) ;
// aggiungo uscita
double dEndElev = dSink ;
SetFeed( GetEndFeed()) ;
if ( ! AddLeadOut( ptEnd, vtEnd, vtExtr, nullptr, bSplitArcs, false, ptP1, dEndElev, false)) {
m_pMchMgr->SetLastError( 2416, "Error in Pocketing : LeadOut not computable") ;
return false ;
}
// aggiungo retrazione
if ( ! AddLinkRetract( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) {
m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ;
return false ;
}
}
}
} // fine ciclo sulle curve di Offset i-esime
// ===== SEGMENTI =========
// determino il riferimento in base alla svuotatura ( Serve per Orientare i segmenti in base al m_dSideAngle)
BBox3d b3Pocket ; pSfrBorder->GetLocalBBox( b3Pocket) ;
Point3d ptMin ;
double dDimX, dDimY, dDimZ ;
b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ;
// passi in Y
int nYStep = static_cast<int>( 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<ICurveLine> 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<ICurveComposite> 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<ICurveLine> pCrvSeg( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ;
double duF, dLen ;
// accorcio leggermente il segmento per non toccare la prima curva di Offset ( di dExtra)
if ( ! pCrvSeg->GetLength( dLen) ||
dLen < 2 * dExtra ||
! pCrvSeg->GetParamAtLength( dLen - dExtra, duF) ||
! pCrvSeg->TrimStartAtLen( dExtra) ||
! pCrvSeg->TrimEndAtParam( duF))
pCrvSeg.Set( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ;
// punto iniziale
Point3d ptS ;
pCrvSeg->GetStartPoint( ptS) ;
// imposto la Feed
pCrvCompo->Clear() ;
pCrvCompo->AddCurve( pCrvSeg->Clone()) ;
vAddedLines.emplace_back( pCrvCompo->Clone()) ;
//if ( ! AssignFeedZigZagOneWay( pCrvCompo, false, vLineAbove, vLineUnder, vCrvLink))
// return false ;
// INIZIO
ptS.ToGlob( frLocI) ;
// determino inizio attacco
Point3d ptP ;
if ( ! CalcLeadInStart( ptS, frLocI.VersX(), vtExtr, nullptr, ptP))
return false ;
// determino elevazione su inizio attacco
double dStElev = dSink ;
dStElev -= ( ptP - ptS) * vtExtr ;
// sempre approccio di collegamento
if ( ! AddLinkApproach( ptP, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr)) {
m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ;
return false ;
}
// aggiungo attacco (forzato ad essere lineare)
SetFeed( GetStartFeed()) ;
if ( ! AddLeadIn( ptP, ptS, frLocI.VersX(), vtExtr, pSrfLeanInOut, nullptr, !m_Params.m_bInvert, bSplitArcs, true)) {
m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ;
return false ;
}
// punto finale
Point3d ptE ;
// per ogni sotto-tratto ( della feed) del tratto lineare ( per ora sempre 1)
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
// ricavo la singola entità curva
const ICurve* pCurve( pCrvCompo->GetCurve( u)) ;
pCurve->GetEndPoint( ptE) ;
ptE.ToGlob( frLocI) ;
// movimento al punto finale
// elaborazioni sulla curva corrente
//double dMinFeed = GetFeed() * GetSideStep() / m_TParams.m_dDiam ;
//double dFeed = pCurve->GetTempProp( 0) / FEED_DIVISOR < dMinFeed - 50 * EPS_SMALL ?
// GetFeed() : pCurve->GetTempProp( 0) / FEED_DIVISOR ;
//SetFeed( dFeed) ;
SetFeed( GetFeed()) ;
if ( AddLinearMove( ptE) == GDB_ID_NULL)
return false ;
}
// FINE
Point3d ptQ ;
double dEndElev = dSink ;
SetFeed( GetEndFeed()) ;
if ( ! AddLeadOut( ptE, frLocI.VersX(), vtExtr, nullptr, bSplitArcs, true, ptQ, dEndElev)) {
m_pMchMgr->SetLastError( 2416, "Error in Pocketing : LeadOut not computable") ;
return false ;
}
dEndElev -= ( ptE - ptQ) * vtExtr ;
// se ultimo movimento di ultima area, aggiungo retrazione globale
if ( j == nStep && i == nYStep && w == ( int)ccClassSeg.size() - 2) {
if ( ! AddRetract( ptQ, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) {
m_pMchMgr->SetLastError( 2417, "Error in Pocketing : Retract not computable") ;
return false ;
}
}
// altrimenti aggiungo retrazione di collegamento
else {
dEndElev = dSink ;
if ( ! AddLinkRetract( ptQ, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) {
m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ;
return false ;
}
}
}
}
vLineUnder.clear() ;
for ( int u = 0 ; u < ( int)vAddedLines.size() ; ++ u)
vLineUnder.emplace_back( vAddedLines[u]->Clone()) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AddSpiralIn( ISURFFRPOVECTOR& vSfr, const vector<ICRVCOMPOPOVECTOR>& vCrvOrig, BOOLVECTOR& vbChangedPrec,
VCT3DVECTOR& vVtTrasl, ISURFFRPOVECTOR& vSrfLimit, 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()) ;
// 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<Vector3d> 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 ;
} ;
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 ;
}
// se superficie non valida, passo alla prossima
if ( IsNull( vSfr[j-1]) || ! vSfr[j-1]->IsValid())
continue ;
// superifice per ingressi ed uscite
PtrOwner<ISurfFlatRegion> pSrfLeanInOut( CloneSurfFlatRegion( vSfr[j-1])) ;
if ( IsNull( pSrfLeanInOut))
return false ;
// 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<ISurfFlatRegion> 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<ICurveComposite> pMCrv( CreateCurveComposite()) ; // percorso di svuotatura
PtrOwner<ICurveComposite> 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, 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) ;
}
// controlli per entrate da fuori al grezzo
// NB. Anche se la superficie è rimasta invariata le entrate vanno controllate caso per caso
bOutStart = bMidOut ;
if ( bOutStart && ! bOptimizedTrap) {
// calcolo il punto fuori per il LeadIn
Point3d ptOut = ptStart + vtMidOut * ( 0.5 * m_TParams.m_dDiam + max( dSafeZ, m_dOpenMinSafe)) ;
ptOut.Translate( vVtTrasl[j-1]) ; // lo traslo allo step corrente
double dStElev ;
// controllo l'elevazione
bOutStart = ( ! GetElevation( m_nPhase, ptOut, vtTool, 0.5 * m_TParams.m_dDiam, vtTool, dStElev) || dStElev < EPS_SMALL) ;
if ( bOutStart || m_bOpenOutRaw) {
// aggiungo al ritorno l'uscita
if ( pRCrv->GetCurveCount() == 0) {
Point3d ptStart ; pMCrv->GetStartPoint( ptStart) ;
pRCrv->AddPoint( ptStart) ;
}
ptOut.Translate( -vVtTrasl[j-1]) ; // lo ritraslo sulla curva
// aggiungo un tratto lineare all'inizio del percorso di svuotatura
pMCrv->AddLine( ptOut, false) ;
// aggiungo un tratto lineare alla fine del percorso di svuotatura
//pRCrv->AddLine( ptOut, 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) ;
// 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<ICurve> pCurve( pCrvC->Clone()) ;
if ( IsNull( pCurve))
return false ;
//int b = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pCurve->Clone()) ;
//m_pGeomDB->SetMaterial( b, RED) ;
// 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( ptP1, ptStart, vtStart, 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<ICurveComposite> pRCrv( CreateCurveComposite()) ;
// ricavo il punto in cui sono
Point3d ptCurr ; GetCurrPos( ptCurr) ;
// ricavo il punto che devo raggiungere
Point3d ptDest ; vMCrv[i]->GetStartPoint( ptDest) ;
// calcolo il percorso di ritorno del percorso attuale
if ( IsNull( pRCrv) ||
! CalcLinkOnStep( ptCurr, ptDest, vSfr[j-2], vSrfLimit[j-2], pRCrv)) {
m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ;
return false ;
}
// emetto
//int b = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pRCrv->Clone()) ;
//m_pGeomDB->SetMaterial( b, BLUE) ;
SetFeed( GetEndFeed()) ;
if ( pRCrv->GetCurveCount() > 0 && AddCurveMove( pRCrv) == GDB_ID_NULL)
return false ;
// ricavo la posizione attuale
Point3d ptAbove ; GetCurrPos( ptAbove) ;
double dStElev = Dist( ptAbove, ptStart) ;
// aggiungo attacco ( solo collegamento )
SetFeed( GetStartFeed()) ;
if ( ! AddLeadIn( ptAbove, ptStart, vtStart, 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 ;
}
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, 10 * EPS_SMALL))
return true ;
// creo un Segmento tra l'ORIG ( ptS) e la proiezione del punto finale
PtrOwner<ICurveLine> 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<ISurfFlatRegion> pSfrDanger( pSfrLimit->CreateOffsetSurf( 0.5 * m_TParams.m_dDiam + 5 * 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 || ( 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<ISurfFlatRegion> pSfrSafe( pSfr->CreateOffsetSurf( - 0.5 * m_TParams.m_dDiam - GetOffsR() - 5 * EPS_SMALL,
ICurve::OFF_FILLET)) ;
if ( IsNull( pSfrSafe) || ! pSfrSafe->IsValid())
return false ;
Voronoi* pVRONI( pSfrSafe->GetVoronoiObject()) ;
if ( pVRONI == nullptr ||
! pVRONI->CalcMedialAxis( vCrvBisectors, Voronoi::WMAT_LEFT) ||
vCrvBisectors.empty())
return false ;
// porto i bisettori nel riferimento
for ( int i = 0 ; i < ( int)vCrvBisectors.size() ; ++ i)
vCrvBisectors[i]->ToLoc( frCurr) ;
// 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<ICurveComposite> pCrvPath( CreateCurveComposite()) ;
if ( IsNull( pCrvPath) ||
! ChooseBestBisectorPath( vCompoBisChain, ORIG, ptEProj, pCrvPath))
return false ;
double dMAxElev = 0. ;
// controllo che la curva si effettivamente valida e distante dal percorso, altrimenti scarico
bool bSkip = ! pCrvPath->IsValid() || pCrvPath->GetCurveCount() == 0 ;
if ( ! bSkip) {
// 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<ICurveLine> 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<ICurveLine> 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) ;
// aggiungo un tratto lineare per avere il percorso completo
pCrvPath->AddLine( ORIG, false) ;
pCrvPath->AddLine( ptEProj, true) ;
// calcolo la sua lunghezza
double dCurrLen ;
if ( ! pCrvPath->GetLength( dCurrLen))
return false ;
//pCrvPath->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL) ;
//PolyArc PA ;
//if ( ! pCrvPath->ApproxWithArcsEx( 500 * EPS_SMALL, ANG_TOL_STD_DEG, LIN_FEA_STD, PA))
// return false ;
//pCrvPath->Clear() ;
//pCrvPath->FromPolyArc( PA) ;
//ModifyCurveToSmoothed( pCrvPath, m_TParams.m_dDiam / 16, m_TParams.m_dDiam / 16, 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<ISurfFlatRegion> pSfrCurr( CloneSurfFlatRegion( pSfr)) ;
PtrOwner<ICurveLine> 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 ;
int r = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pSfr->Clone()) ;
m_pGeomDB->SetMaterial( r, Color( 0.0, 1., 0., 0.15)) ;
r = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pLine->Clone()) ;
m_pGeomDB->SetMaterial( r, BLACK) ;
// 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
ICurveLine* pLineX( CreateCurveLine()) ; pLineX->Set( ptS_i, ptE_i) ;
int r = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pLineX->Clone()) ;
m_pGeomDB->SetMaterial( r, RED) ;
if ( ! GetElevation( m_nPhase, ptS_i, ptE_i, pSfr->GetNormVersor(), 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<ICurveComposite> pCrvBef( ConvertCurveToComposite( vCrvCompo[nIndS]->CopyParamRange( 0, dUS))) ;
PtrOwner<ICurveComposite> pCrvAft( ConvertCurveToComposite( vCrvCompo[nIndS]->CopyParamRange( dUS,
vCrvCompo[nIndS]->GetCurveCount()))) ;
if ( IsNull( pCrvBef) || ! pCrvBef->IsValid() || IsNull( pCrvAft) || ! pCrvAft->IsValid())
return false ;
vCrvCompo[nIndS].Set( Release( pCrvBef)) ;
vCrvCompo.emplace_back( Release( pCrvAft)) ;
}
if ( dUE > 2 * EPS_SMALL && dUE < vCrvCompo[nIndE]->GetCurveCount() - 2 * EPS_SMALL) {
PtrOwner<ICurveComposite> pCrvBef( ConvertCurveToComposite( vCrvCompo[nIndE]->CopyParamRange( 0, dUE))) ;
PtrOwner<ICurveComposite> 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 {
// se la curva è la stessa
if ( abs( dUS - dUE) < 5 * EPS_SMALL) {
// se coincidenti
PtrOwner<ICurveComposite> pCrvBef( ConvertCurveToComposite( vCrvCompo[nIndS]->CopyParamRange( 0, 0.5 * ( dUS + dUE)))) ;
PtrOwner<ICurveComposite> pCrvAft( ConvertCurveToComposite( vCrvCompo[nIndS]->CopyParamRange( 0.5 * ( dUS + dUE),
vCrvCompo[nIndS]->GetCurveCount()))) ;
if ( IsNull( pCrvBef) || ! pCrvBef->IsValid() || IsNull( pCrvAft) || ! pCrvAft->IsValid())
return false ;
vCrvCompo[nIndS].Set( Release( pCrvBef)) ;
vCrvCompo.emplace_back( Release( pCrvAft)) ;
}
else {
// se distinti...
if ( dUS > dUE)
swap( dUS, dUE) ;
PtrOwner<ICurveComposite> pCrvBef( ConvertCurveToComposite( vCrvCompo[nIndS]->CopyParamRange( 0, dUS))) ;
PtrOwner<ICurveComposite> pCrvAft( ConvertCurveToComposite( vCrvCompo[nIndS]->CopyParamRange( dUE,
vCrvCompo[nIndS]->GetCurveCount()))) ;
PtrOwner<ICurveComposite> pCrvBet( ConvertCurveToComposite( vCrvCompo[nIndS]->CopyParamRange( dUS, dUE))) ;
if ( IsNull( pCrvBef) || ! pCrvBef->IsValid() || IsNull( pCrvAft) || ! pCrvAft->IsValid() ||
IsNull( pCrvBet) || ! pCrvBef->IsValid())
return false ;
vCrvCompo[nIndS].Set( Release( pCrvBef)) ;
vCrvCompo.emplace_back( Release( pCrvBet)) ;
vCrvCompo.emplace_back( Release( pCrvAft)) ;
}
}
//for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
// int b = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, vCrvCompo[i]->Clone()) ;
// m_pGeomDB->SetMaterial( b, BLUE) ;
//}
// definizioni per l'albero su cui calcolare i percorsi minimi
struct Link {
int nIndNodA ;
int nIndNodB ;
double dWeight ;
int nCrvIndex ;
} ;
vector<Point3d> Nodes ;
vector<Link> Links ;
// inserisco il nodo sorgente nel vettore dei nodi in posizione 0
Nodes.push_back( ptBisS) ;
// il nodo destinazione sarà all'indice nDestInd
int nDestInd = - 1 ;
// creo il vettore di nodi e di Archi
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
Point3d ptA, ptB ;
double dLen = 0.;
if ( ! vCrvCompo[i]->GetStartPoint( ptA) || ! vCrvCompo[i]->GetEndPoint( ptB) ||
! vCrvCompo[i]->GetLength( dLen))
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 ( AreSamePointEpsilon( ptA, Nodes[j], 5 * EPS_SMALL))
bInsertA = false ;
if ( AreSamePointEpsilon( ptB, Nodes[j], 5 * EPS_SMALL))
bInsertB = false ;
if ( nDestInd == -1)
if ( AreSamePointEpsilon( ptBisE, Nodes[j], 5 * EPS_SMALL))
nDestInd = j ;
}
if ( bInsertA)
Nodes.push_back( ptA) ;
if ( bInsertB)
Nodes.push_back( ptB) ;
// inserisco l'Arco
int nIndA = -1 ; int nIndB = -1 ;
for ( int j = 0 ; j < int( Nodes.size()) ; ++ j) {
if ( AreSamePointEpsilon( ptA, Nodes[j], 5 * EPS_SMALL))
nIndA = j ;
if ( AreSamePointEpsilon( ptB, Nodes[j], 5 * EPS_SMALL))
nIndB = j ;
}
if ( nIndA == -1 || nIndB == -1) // controllo...
return false ;
Link CurrLink ;
CurrLink.dWeight = dLen ;
CurrLink.nIndNodA = nIndA ;
CurrLink.nIndNodB = nIndB ;
CurrLink.nCrvIndex = i ;
Links.push_back( CurrLink) ;
}
if ( nDestInd == -1)
return false ;
// calcolo il percorso minimo (Dijkstra)
vector<double> vdDist ; vdDist.resize( int( Nodes.size())) ;
INTVECTOR vnIndPrec ; vnIndPrec.resize( int( Nodes.size())) ;
set<int> SetNode_to_check ;
for ( int i = 0 ; i < int( Nodes.size()) ; ++ i) {
vdDist[i] = INFINITO ;
vnIndPrec[i] = -1 ;
SetNode_to_check.insert( i) ;
}
vdDist[0] = 0. ;
while ( ! SetNode_to_check.empty()) {
// cerco il vertice in SetNode_to_check con distanza minima associata
int nMinInd = -1 ;
double dDistRef = INFINITO - 1 ;
for ( int j = 0 ; j < int( Nodes.size()) ; ++ j) {
set<int>::iterator iter = SetNode_to_check.find( j) ;
if ( iter == SetNode_to_check.end())
continue ;
if ( vdDist[j] < dDistRef) {
dDistRef = vdDist[j] ;
nMinInd = j ;
}
}
if ( nMinInd == -1 || nMinInd == nDestInd)
break ;
SetNode_to_check.erase( nMinInd) ;
// cerco i nodi collegati al nodo in esame
vector<INTINT> NeightBourInfo ;
for ( int j = 0 ; j < int( Links.size()) ; ++ j) {
if ( Links[j].nIndNodA == nMinInd || Links[j].nIndNodB == nMinInd) {
INTINT Info ;
Info.first = j ; // indice del link
Info.second = Links[j].nIndNodA == nMinInd ? Links[j].nIndNodB : Links[j].nIndNodA ; // indice dell'altro nodo
NeightBourInfo.push_back( Info) ;
}
}
for ( int j = 0 ; j < int( NeightBourInfo.size()) ; ++ j) {
double dCurrWeight = vdDist[nMinInd] + Links[NeightBourInfo[j].first].dWeight ;
if ( dCurrWeight < vdDist[NeightBourInfo[j].second]) {
vdDist[NeightBourInfo[j].second] = dCurrWeight ;
vnIndPrec[NeightBourInfo[j].second] = nMinInd ;
}
}
}
// inserisco il punto iniziale alla curva da restituire
pCrvPath->AddPoint( ptBisE) ;
// percorro a ritroso i Nodi indivisuati
int nIndCurrNode = nDestInd ;
int nIndPrecNode = -1 ;
while ( vnIndPrec[nIndCurrNode] != -1) {
nIndPrecNode = vnIndPrec[nIndCurrNode] ;
// cerco l'arco con estremanti tali nodi
for ( int i = 0 ; i < int( Links.size()) ; ++ i) {
if (( Links[i].nIndNodA == nIndCurrNode && Links[i].nIndNodB == nIndPrecNode) ||
( Links[i].nIndNodA == nIndPrecNode && Links[i].nIndNodB == nIndCurrNode)) {
if ( ! pCrvPath->AddCurve( vCrvCompo[i]->Clone())) {
vCrvCompo[i]->Invert() ;
if ( ! pCrvPath->AddCurve( vCrvCompo[i]->Clone())) {
m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pCrvPath->Clone()) ;
m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, vCrvCompo[i]->Clone()) ;
return false ;
}
}
// NB. Se la curva è la prima, il punto iniziale deve essere ptBisE !
if ( nIndCurrNode == nDestInd) {
Point3d ptSPath ;
if ( ! pCrvPath->GetStartPoint( ptSPath))
return false ;
if ( ! AreSamePointEpsilon( ptSPath, ptBisE, 5 * EPS_SMALL))
pCrvPath->Invert() ;
}
break ;
}
}
nIndCurrNode = nIndPrecNode ;
}
// 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
int nCount = 0 ;
INTVECTOR vnId2 ;
while ( chainC.GetChainFromNear( ptNear, true, vnId2)) {
// creo una curva composita
PtrOwner<ICurveComposite> 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<ICRVCOMPOPOVECTOR>& vCrvOrig, BOOLVECTOR& vbChangedPrec,
VCT3DVECTOR& vVtTrasl, const Vector3d& vtTool, const Vector3d& vtExtr,
double dDepth, double dElev, double dMaxElev, double dOkStep, bool bSplitArcs)
{
// recupero distanze di sicurezza
double dSafeZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeZ() ;
double dSafeAggrBottZ = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeAggrBottZ() ;
// lunghezza di approccio/retrazione
double dAppr = m_Params.m_dStartPos ;
// ricavo il numero di Step
int nStep = int( vSfr.size()) ;
// scorro il vettore delle FlatRegion ( calcolate per ogni step j-esimo)
for ( int j = 1 ; j <= nStep ; ++ j) {
// se superficie non valida, passo alla prossima
if ( IsNull( vSfr[j-1]) || ! vSfr[j-1]->IsValid())
continue ;
// ciclo sui chunk della superificie da svuotare
for ( int c = 0 ; c < vSfr[j-1]->GetChunkCount() ; ++ c) {
// copio il chunk c-esimo
PtrOwner<ISurfFlatRegion> pSrfChunk( GetSurfFlatRegion( vSfr[j-1]->CloneChunk( c))) ;
if ( IsNull( pSrfChunk))
return false ;
Point3d ptP1, ptStartPrec ; // per LeadIn attuale e precedente
bool bMidOutPrec ; // flag per entrata precedente da fuori dal grezzo
BOOLVECTOR vbMidOutPrec ; // flags per effettiva entrata precedente da fuori dal grezzo
ICRVCOMPOPOVECTOR vMCrv_step_prec ; // vettore delle curve dei percorsidi svuotatura dello step precedente
ICRVCOMPOPOVECTOR vRCrv_step_prec ; // vettore delle curve dei percorsi di ritorno dello step precedente
vector<Vector3d> vvtMidOutPrec ; // vettore dei versori delle direzioni di uscita dello step precedente
INTVECTOR vnRegTot_prec ; // vettore dei numeri delle regioni formate dal primo Offset nello step precedente
int nOffs_act = 0 ;
if ( vbChangedPrec[j-1]) {
// se lo step attuale è cambiato rispetto al precedente -> Flush info step precedente
vMCrv_step_prec.clear() ;
vRCrv_step_prec.clear() ;
vnRegTot_prec.clear() ;
vbMidOutPrec.clear() ;
vvtMidOutPrec.clear() ;
}
const int MAX_REGS = 50 ;
int nReg = 0 ; // chunk nuovo corrente da svuotare
// ciclo su tutte le regioni che posso ottenere col primo Offset del chunk cc-esimo
while ( nReg < MAX_REGS) {
// superificie per ingressi ed uscite
PtrOwner<ISurfFlatRegion> pSrfLeanInOut( CloneSurfFlatRegion( vSfr[j-1])) ;
if( IsNull( pSrfLeanInOut))
return false ;
// calcolo la spirale dall'interno all'esterno
PtrOwner<ICurveComposite> pMCrv( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pRCrv( CreateCurveComposite()) ;
if ( IsNull( pMCrv) || IsNull( pRCrv)) {
m_pMchMgr->SetLastError( 2411, "Error in Pocketing : toolpath allocation failed") ;
return false ;
}
int nRegTot ; // numero di regioni create dal primo Offset per lo step attuale
bool bOutStart ; // flag per entrata da fuori per lo step attuale
bool bForcedOutStart ; // flag per forzare l'entrata da fuori allo step attuale
Point3d ptStart ; // punto iniziale del percorso
Vector3d vtMidOut ; // vettore verso l'esterno nel caso di entrata da fuori ammissibile ( -> da CalcSpiral)
bool bMidOut ; // ammissibilità entrata da fuori ( da calcolare step per step)
bool bOptimizedTrap = false ; // se ho un caso spirale o trapezio
// NB. se la superficie è rimasta la stessa, utilizzo pMCrv e pRCrv dello step precedente
// ( non c'è bisogno di ricalcolare tutti i percorsi)
if ( ! vbChangedPrec[j-1] && j != 1) {
if ( nOffs_act > ( int)vMCrv_step_prec.size() - 1)
break ;
pMCrv.Set( vMCrv_step_prec[nOffs_act]->Clone()) ;
pRCrv.Set( vRCrv_step_prec[nOffs_act]->Clone()) ;
nRegTot = vnRegTot_prec[nOffs_act] ;
bMidOut = vbMidOutPrec[nOffs_act] ;
vMCrv_step_prec[nOffs_act]->GetStartPoint( ptStart) ;
vtMidOut = vvtMidOutPrec[nOffs_act] ;
}
else {
// se lucidatura con epicicli
if ( m_TParams.m_nType == TT_MILL_POLISHING && m_Params.m_dEpicyclesRad > EPS_SMALL) {
// verifico che parametri lucidatura siano sensati
if ( m_Params.m_dEpicyclesDist < 100 * EPS_SMALL) {
m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ;
return false ;
}
// modifico il diametro dell'utensile per tenere conto anche del raggio degli epicicli
m_TParams.m_dDiam += 2 * m_Params.m_dEpicyclesRad ;
}
bool bOptimizedTrap = false ;
int nInd = 0 ;
if ( ! GetOptCrvIndex( vCrvOrig, j, pSrfChunk, nInd))
return false ;
nRegTot = nReg ;
if ( ! CalcSpiral( pSrfChunk, nRegTot, ptStart, vtMidOut, bMidOut, bSplitArcs, pMCrv, pRCrv,
vCrvOrig[j-1][nInd], vbChangedPrec[j-1], bOptimizedTrap))
return false ;
// se terminate le regioni, esco
if ( pMCrv->GetCurveCount() == 0)
break ;
if ( m_TParams.m_nType == TT_MILL_POLISHING && m_Params.m_dEpicyclesRad > EPS_SMALL) {
// riporto il diametro dell'utensile al valore originale
m_TParams.m_dDiam -= 2 * m_Params.m_dEpicyclesRad ;
// aggiorno i percorsi di svuotatura con epicicli
if ( ! ComputePolishingPath( pMCrv, pRCrv, bSplitArcs)) {
m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ;
return false ;
}
}
// memorizzo le curve create e modificate per lo step successivo
vMCrv_step_prec.emplace_back( pMCrv->Clone()) ;
vRCrv_step_prec.emplace_back( pRCrv->Clone()) ;
// memorizzo i Flag per le entrate
vbMidOutPrec.push_back( bMidOut) ;
// memorizzo il numero delle regioni create dal primo Offset
vnRegTot_prec.push_back( nRegTot) ;
// memorizzo il versore di uscita calcolato
vvtMidOutPrec.push_back( vtMidOut) ;
}
// nel caso ottimizzato verifico se posso entrare e uscire fuori dal grezzo
bOutStart = false ;
int nOutsideRaw = 0 ;
if ( bOptimizedTrap) {
AdjustTrapezoidSpiralForLeadInLeadOut( pMCrv, pRCrv, vtTool, dDepth, nOutsideRaw) ;
bOutStart = ( nOutsideRaw > 0) ;
AssignFeedSpiralOpt( 1, pMCrv) ;
AssignFeedForReturnPath( pRCrv) ;
}
// se utensile che non lavora di testa e ingresso non fuori dal pezzo, errore
if ( m_TParams.m_nType == TT_MILL_NOTIP && ! bOutStart) {
if ( ! LeadInRawIsOk()) {
m_pMchMgr->SetLastError( 2431, "Error in Pocketing : LeadIn with Mill NoTip in material") ;
return false ;
}
}
// inverto i percorsi, perchè sono calcolati dall'esterno all'interno (solo nel caso non ottimizzato)
if ( ! bOptimizedTrap) {
pMCrv->Invert() ;
pRCrv->Invert() ;
}
// inversione per caso ottimizzato
if ( bOptimizedTrap && nOutsideRaw != 1 && j > 1)
pMCrv->Invert() ;
++ nReg ;
++ nOffs_act ;
int nMaxInd = pMCrv->GetCurveCount() - 1 ;
int nMaxRInd = pRCrv->GetCurveCount() - 1 ;
// determino l'affondamento attuale
double dSink = dElev - dDepth + dMaxElev - vVtTrasl[j-1].Len() ;
// ciclo sulle curve elementari
for ( int i = 0 ; i <= nMaxInd ; ++ i) {
// curva corrente
const ICurve* pCrvC = pMCrv->GetCurve( i) ;
// copio la curva
PtrOwner<ICurve> pCurve( pCrvC->Clone()) ;
if ( IsNull( pCurve))
return false ;
// se prima entità
if ( i == 0) {
// dati inizio entità
Point3d ptStart ;
pCurve->GetStartPoint( ptStart) ;
Vector3d vtStart ;
pCurve->GetStartDir( vtStart) ;
// se primo step, approccio e affondo
if ( j == 1 || vbChangedPrec[j-1] || vSfr[j-2]->GetChunkCount() > 1 || nRegTot > 1) {
// Ricalcolo il punto di attacco sopra al PtStart ( modificato in precedenza dal PtOut) quando :
// 1) sono nel primo Step o ...
// 2) la superificie è diversa da quello dello step precedente o ...
// 3) il numero di chunk allo step precedente è maggiore di 1 o ...
// 4) dal primo offset del chunk cc-esimo ottengo più regioni
if ( ! CalcLeadInStart( ptStart, vtStart, vtExtr, pRCrv, ptP1)) {
m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ;
return false ;
}
// determino elevazione su inizio attacco
double dStElev = dSink ;
dStElev -= ( ptP1 - ptStart) * vtExtr ;
// se attacco a zigzag o a spirale o a scivolo, l'elevazione va nell'attacco
if ( GetLeadInType() == POCKET_LI_ZIGZAG ||
GetLeadInType() == POCKET_LI_HELIX ||
GetLeadInType() == POCKET_LI_GLIDE) {
ptP1 += vtExtr * ( dStElev + LIO_ELEV_TOL) ;
dStElev = -LIO_ELEV_TOL ;
}
// approccio al punto iniziale
if ( ! AddApproach( ptP1, vtTool, dSafeZ, dSafeAggrBottZ, dStElev, dAppr, false)) {
m_pMchMgr->SetLastError( 2414, "Error in Pocketing : Approach not computable") ;
return false ;
}
// aggiungo attacco
SetFeed( GetStartFeed()) ;
if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, pRCrv, m_Params.m_bInvert, bSplitArcs, bOutStart)) {
m_pMchMgr->SetLastError( 2415, "Error in Pocketing : LeadIn not computable") ;
return false ;
}
}
// altrimenti solo collegamento
else {
SetFeed( GetStartFeed()) ;
GetCurrPos( ptP1) ;
if ( ! AddLeadIn( ptP1, ptStart, vtStart, vtExtr, pSrfLeanInOut, pRCrv, m_Params.m_bInvert, bSplitArcs, bOutStart)) {
m_pMchMgr->SetLastError( 2418, "Error in Pocketing : Link not computable") ;
return false ;
}
}
}
// elaborazioni sulla curva corrente
//double dMinFeed = GetFeed() * GetSideStep() / m_TParams.m_dDiam ;
//double dFeed = pCurve->GetTempProp( 0) / FEED_DIVISOR < dMinFeed - 50 * EPS_SMALL ?
// GetFeed() : pCurve->GetTempProp( 0) / FEED_DIVISOR ;
//DrawColoredCrvForFeedTest( pCurve, dFeed) ;
//SetFeed( dFeed) ;
if ( pCurve->GetType() == CRV_LINE) {
ICurveLine* pLine = GetCurveLine( pCurve) ;
Point3d ptP3 = pLine->GetEnd() ;
SetFeed( GetFeed()) ;
//SetFeed( pCurve->GetTempProp( 0) / FEED_DIVISOR) ;
if ( AddLinearMove( ptP3) == GDB_ID_NULL)
return false ;
}
else if ( pCurve->GetType() == CRV_ARC) {
ICurveArc* pArc = GetCurveArc( pCurve) ;
Point3d ptCen = pArc->GetCenter() ;
double dAngCen = pArc->GetAngCenter() ;
Vector3d vtN = pArc->GetNormVersor() ;
Point3d ptP3 ;
pArc->GetEndPoint( ptP3) ;
SetFeed( GetFeed()) ;
//SetFeed( pCurve->GetTempProp( 0 ) / FEED_DIVISOR) ;
if ( AddArcMove( ptP3, ptCen, dAngCen, vtN) == GDB_ID_NULL)
return false ;
}
// se ultima entità
if ( i == nMaxInd) {
// se step intermedio
if ( j < nStep && vSfr[j-1]->GetChunkCount() == 1 && !vbChangedPrec[j] && nRegTot == 1) {
// se necessario ritorno all'inizio
if ( nMaxRInd >= 0) {
// copio la curva di ritorno
PtrOwner<ICurveComposite> pRet( pRCrv->Clone()) ;
if ( IsNull( pRet))
return false ;
// aggiungo affondamento
pRet->Translate( vVtTrasl[j-1]) ;
// se attacco a scivolo, accorcio della lunghezza dell'attacco
if ( GetLeadInType() == POCKET_LI_GLIDE) {
double dLen ; pRet->GetLength( dLen) ;
if ( dLen > m_Params.m_dLiTang + 10 * EPS_SMALL)
if ( ! pRet->TrimEndAtLen( dLen - m_Params.m_dLiTang))
pRet->Clear() ;
else
pRet->Clear() ;
}
// emetto
SetFeed( GetFeed()) ;
if ( pRet->GetCurveCount() > 0 && AddCurveMove( pRet) == GDB_ID_NULL)
return false ;
}
}
// atrimenti ultimo step, uscita e retrazione
else {
// dati fine entità
Point3d ptEnd ;
pCurve->GetEndPoint( ptEnd) ;
Vector3d vtEnd ;
pCurve->GetEndDir( vtEnd) ;
// aggiungo uscita
Point3d ptQ ;
double dEndElev = dElev ;
SetFeed( GetEndFeed()) ;
if ( ! AddLeadOut( ptEnd, vtEnd, vtExtr, pRCrv, bSplitArcs, false, ptQ, dEndElev, false)) {
m_pMchMgr->SetLastError( 2416, "Error in Pocketing : LeadOut not computable") ;
return false ;
}
// aggiungo retrazione
if ( ! AddRetract( ptQ, vtTool, dSafeZ, dSafeAggrBottZ, dEndElev, dAppr)) {
m_pMchMgr->SetLastError( 2417, "Error in Pocketing : Retract not computable") ;
return false ;
}
}
}
}
}
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::CalcSpiral( const ISurfFlatRegion* pSrfPock, int& nReg, Point3d& ptStart, Vector3d& vtMidOut , bool& bMidOut,
bool bSplitArcs,ICurveComposite* pMCrv, ICurveComposite* pRCrv, ICurveComposite* pCrvOEWithFlags,
bool bChangedPrec, bool& bOptimizedTrap)
{
// inizializzo i risultati
pMCrv->Clear() ;
pRCrv->Clear() ;
// primo offset pari al raggio utensile + sovramateriale
double dTRad = 0.5 * m_TParams.m_dDiam ;
double dOffs = dTRad + GetOffsR() ;
// recupero il versore normale della superificie, ruotandola nel piano XY
PtrOwner<ISurfFlatRegion> 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<ICurveComposite> pCrvBorder( ConvertCurveToComposite( pSrfPock->GetLoop( 0, 0))) ;
if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid())
return false ;
pCrvBorder->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
Point3d ptCen ; Vector3d vtN ; double dRad ; bool bCCW ;
if ( pCrvBorder->IsACircle( 100 * EPS_SMALL, ptCen, vtN, dRad, bCCW)) {
double dIntRad = 0 ;
if ( m_Params.m_nSubType == POCKET_SUB_SPIRALOUT && GetLeadInType() == POCKET_LI_HELIX) {
dIntRad = min( 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam), dRad - dOffs) ;
m_dMaxHelixRad = dIntRad ;
}
if ( nReg == 0) {
bool bOk = CalcCircleSpiral( ptCen, vtN, dRad - dOffs, dIntRad, bSplitArcs, pMCrv, pRCrv) ;
if ( bOk) {
pMCrv->GetStartPoint( ptStart) ;
nReg = 1 ;
pMCrv->GetStartDir( vtMidOut) ;
Vector3d vtExtr ; pMCrv->GetExtrusion( vtExtr) ;
vtMidOut.Rotate( vtExtr, 0, m_Params.m_bInvert ? 1 : -1) ;
bMidOut = pCrvBorder->GetFirstCurve()->GetTempProp() == 1 ;
return true ;
}
else
return false ;
}
else
return true ;
}
// caso trapezoide
PtrOwner<ICurveComposite> 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<ISurfFlatRegion> 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<ISurfFlatRegion> pSfrOffsVR( pSrfAct->CreateOffsetSurf( - dOffs, ICurve::OFF_FILLET)) ;
if ( IsNull( pSfrOffsVR))
return false ;
if ( ! pSfrOffsVR->IsValid()) {
pSfrOffsVR.Set( pSrfAct->CreateOffsetSurf( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) ;
if ( IsNull( pSfrOffsVR))
return false ;
}
// alla prima iterazione svuoto la regione nReg-esima passata alla funzione CalcSpiral
if ( nIter == 0) {
PtrOwner<ISurfFlatRegion> 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<ICurveComposite> pCrvCompoBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ;
if ( IsNull( pCrvCompoBorder) || ! pCrvCompoBorder->IsValid())
return false ;
if ( j > 0) // inverto l'orientamento delle curve interne ( offset delle isole trovate)
pCrvCompoBorder->Invert() ;
// controllo quali regioni di Offset possono essere sostituite
bool bInsert = true ;
if ( ! CheckIfOffsetIsNecessary( pCrvCompoBorder, dOffs - dOffsPrec, ( int)vOffs.size(), nIter, vtExtr, bInsert))
return false ;
if ( bInsert)
vOffs.emplace_back( Release( pCrvCompoBorder)) ;
if ( nIter == 0) { // salvo il bordo per i link (non invertiti)
PtrOwner<ICurveComposite> pCrvCompoExtBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ;
vOffsFirstCurve.emplace_back( Release( pCrvCompoExtBorder)) ;
}
}
}
// controllo se serve un raggio più piccolo di svuotatura
bool bSmallRad = ( nIter == 0 ? dOffs < dTRad + GetOffsR() + EPS_ZERO : dOffs - dOffsPrec < dTRad + EPS_ZERO) ;
// se ho trovato dei chunk, allora aggiorno l'Offset per la passata successiva
if ( nChunks > 0) {
// memorizzo il valore di Offset per l'iterazione successiva
dOffsPrec = dOffs ;
dOffs += GetSideStep() ;
}
// se devo usare un Offset più piccolo...
else if ( ! bSmallRad)
dOffs = dOffsPrec + ( nIter == 0 ? dTRad + GetOffsR() : dTRad) ;
// altrimenti ho finito la regione da svuotare...
else
break ;
++ nIter ; // aggiorno l'iterazione
}
// se non ho trovato curve di Offset allora esco
if (( int)vOffs.size() == 0)
return true ;
// cambio il punto iniziale della prima Curva di Offset
Point3d ptNewStart ;
double dOffR ;
if (( int)vOffs.size() > 1)
dOffR = dTRad + GetOffsR() ;
else
dOffR = dOffs ;
if ( SetBetterPtStartForSubChunks( vOffs[0], pSrfToWork, ptStart, vtMidOut, bMidOut))
vOffs[0]->GetStartPoint( ptNewStart) ;
else
return false ;
// se richiesta inversione
for ( int i = 0 ; i < ( int)vOffs.size() && m_Params.m_bInvert ; ++ i)
vOffs[i]->Invert() ;
// smusso le curve di offset ( ad eccezione della prima)
ICRVCOMPOPOVECTOR vOffsClosedCurves( vOffs.size()) ; // vettore con tutte le curve di Offset Chiuse
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()) ;
}
for ( int i = 0 ; i < int( vOffsFirstCurve.size()) ; ++ i) {
if ( i != 0)
ModifyCurveToSmoothed( vOffsFirstCurve[i], dSmoothPar, dSmoothPar, false) ;
vOffsFirstCurve[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
}
// setto il punto iniziale della svuotatura
double dNewUS ;
vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ;
vOffs[0]->ChangeStartPoint( dNewUS) ;
// riordino le curve cambiando il loro punto di inizio e creando poi i collegamenti
int nClosestInd = -1 ; int nFlag ;
double dDist = INFINITO ;
Point3d ptHelp ;
ICURVEPOVECTOR vLinks( vOffs.size()) ;
for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) {
Point3d ptS ;
if ( ! vOffs[i]->GetStartPoint( ptS))
return false ;
// setto i default delle variabili
if ( ! DistPointCurve( ptS, *vOffs[i+1]).GetMinDistPoint( EPS_SMALL, ptHelp, nFlag))
return false ;
dDist = INFINITO ;
for ( int j = i + 1 ; j <= int( vOffs.size()) - 1 ; ++ j) {
// cerco il punto più vicino della curva
Point3d ptE ;
if ( ! DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptE, nFlag))
return false ;
if ( dDist > Dist( ptS, ptE) ) {
dDist = Dist( ptS, ptE) ;
nClosestInd = j ;
ptHelp.Set( ptE.x, ptE.y, ptE.z) ;
}
}
// avendo la curva più vicina ...
// 1) scambio la curva i con la curva nClosestInd
if ( nClosestInd != i + 1) {
PtrOwner<ICurveComposite> ptoCCHelp( Release( vOffs[i + 1])) ;
vOffs[i + 1].Set( vOffs[nClosestInd]) ;
vOffs[nClosestInd].Set( ptoCCHelp) ;
}
// 2) cambio il suo punto iniziale ...
double dU ;
Point3d ptNE( ptHelp.x, ptHelp.y, ptHelp.z) ;
if ( ! vOffs[i + 1]->GetParamAtPoint( ptNE, dU))
return false ;
vOffs[i + 1]->ChangeStartPoint( dU) ;
// 2.1) Accorcio la curva per velocizzare ...
if (( int)vOffs.size() > 1) { // accorcio se ho almeno due Offset
// copio le curve i e i+1 nel caso non riesca a tagliarle ...
double dUNS, dUNE ;
PtrOwner<ICurveComposite> pCrvTest( CreateCurveComposite()) ; // possibile collegamento tra la curva i ed i+1
PtrOwner<ICurveComposite> pOff_i0( CloneCurveComposite( vOffs[i])) ;
PtrOwner<ICurveComposite> pOff_i1( CloneCurveComposite( vOffs[i + 1])) ;
// cerco di tagliare le curve, ottenendo il collegamento
// NB. se la curva di offset è la prima, non devo tagliarla, accorcio solo le successive ...
if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsClosedCurves, vOffsFirstCurve, pCrvTest, i == 0 ? 0 : 0.01, 0.01, 2)) {
// se non sono riuscito, ritorno alla configurazione iniziale
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
Vector3d vS, vE ;
vOffs[i].Set( pOff_i0) ; vOffs[i+1].Set( pOff_i1) ;
if ( ! vOffs[i]->GetStartDir( vS) || ! vOffs[i+1]->GetStartDir( vE))
return false ;
if ( CalcBoundedSmootedLink( ptS, vS, ptNE, vE, 0.5, vOffsFirstCurve, pCrvLink))
vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento
else {
m_pMchMgr->SetLastError( 2413, "Error in Pocketing : Toolpath not computable") ;
return false ;
}
continue ;
}
if ( ! pCrvTest->GetStartPoint( ptS) ||
! pCrvTest->GetEndPoint( ptNE) ||
! vOffs[i]->GetParamAtPoint( ptS, dUNS) ||
! vOffs[i+1]->GetParamAtPoint( ptNE, dUNE))
return false ;
// imposto il nuovo punto inziale della curva successiva
vOffs[i+1]->ChangeStartPoint( dUNE) ;
PtrOwner<ICurveComposite> pCrvNewOffS( CloneCurveComposite( vOffs[i])) ;
if ( dUNS > EPS_SMALL) { // se parametro di trim sufficientemente grande
pCrvNewOffS.Set( GetCurveComposite( vOffs[i]->CopyParamRange( 0, dUNS))) ;
// sostituisco la curva i-esima con quella tagliata
vOffs[i]->Clear() ;
vOffs[i].Set( pCrvNewOffS) ;
}
// aggiorno il collegamento
vLinks[i + 1].Set( pCrvTest) ;
}
}
// copio il vettore degli Offset per settare poi la Feed
ICRVCOMPOPOVECTOR vOffsFeed ; vOffsFeed.reserve(( int)vOffs.size()) ;
for ( int i = 0 ; i < ( int)vOffs.size() ; ++ i)
vOffsFeed.emplace_back( vOffs[i]->Clone()) ;
// 3) controllo eventuali parti non svuotate...
pCrvOEWithFlags->ToLoc( frPocket) ;
PtrOwner<ISurfFlatRegion> pSrfToCut( CreateSurfFlatRegion()) ;
if ( GetUnclearedRegion( vOffsFirstCurve, vOffs, vLinks, pCrvOEWithFlags, pSrfToCut)) {
// 4) 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::ModifyCurveToSmoothed( ICurveComposite* pCrv, double dRightLen, double dLeftLen, bool bAsParam)
{
// controllo parametri
if ( pCrv == nullptr || dLeftLen < EPS_SMALL || dRightLen < EPS_SMALL)
return false ;
ICURVEPOVECTOR vCrvStepsToFill ; // vettore delle Curve da raccordare (curve della composita)
ICURVEPOVECTOR vCrvArcs ; // vettore contenente gli Archi (tra le curve della composita)
INTVECTOR vArcsToJump ; // vettore di indici per gli archi saltati (nel caso non si riesca a raccordare ...)
// se nella composita ho meno di due curve allora non c'è nulla da raccordare
const ICurve* pMyCrv = pCrv->GetFirstCurve() ;
while ( pMyCrv != nullptr) {
vCrvStepsToFill.emplace_back( pMyCrv->Clone()) ;
pMyCrv = pCrv->GetNextCurve() ;
}
if ( vCrvStepsToFill.size() < 2)
return false ;
// controllo se la curva è chiusa (nel caso inserisco nel vettore la prima curva due volte, all'inizio e alla fine)
if ( pCrv->IsClosed()) {
const ICurve* pMyCrvFirst = pCrv->GetFirstCurve() ;
if ( pMyCrvFirst == nullptr)
return false ;
vCrvStepsToFill.emplace_back( pMyCrvFirst->Clone()) ;
}
double dUE_ref, dUS_ref, dRadius, dPar1, dPar2 ;
// riempio il vettore degli archi, scorro tutte le curve da raccordare ( la i-esima e la (i+1)-esima )
for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) {
// controllo che le curve non siano già tangenti
Vector3d vtS, vtE ;
if ( ! vCrvStepsToFill[i]->GetEndDir( vtS) || ! vCrvStepsToFill[i+1]->GetStartDir( vtE))
continue ;
if ( AreSameVectorApprox( vtS, vtE)) {
vCrvArcs.emplace_back( CreateCurveComposite()) ; // arco nullo
vArcsToJump.push_back( i) ; // da scartare
continue ;
}
// ricavo i valori parametrici associati
double dLen_act ; vCrvStepsToFill[i]->GetLength( dLen_act) ;
double dLen_succ ; vCrvStepsToFill[i+1]->GetLength( dLen_succ) ;
double dUE_ref = 1. ;
if ( dLen_act > dLeftLen)
dUE_ref -= dLeftLen / dLen_act ;
double dUS_ref = 0. ;
if ( dLen_succ > dRightLen)
dUS_ref = dRightLen / dLen_succ ;
// se valori trattati come parametri...
if ( bAsParam) {
// ... cerco i parametri corrispondenti sulle due curve
double dU_cm_S = 0 ; double dULast1 = 1 ; double dULast2 = 1 ;
vCrvStepsToFill[i]->GetDomain( dU_cm_S, dULast1) ;
dUE_ref = ( 1 - dRightLen) * dULast1 ;
vCrvStepsToFill[i+1]->GetDomain( dU_cm_S, dULast2) ;
dUS_ref = dLeftLen * dULast2 ;
}
// prendo i punti sulle due curve rispetto a tali parametri
Point3d ptS, ptE ;
if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) ||
! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE))
return false ;
dRadius = Dist( ptS, ptE) ; // uso come raggio la distanza tra i due punti
int nMaxTestForArcs = 3 ; // tentativi per creare l'arco
int nIterForArcs = 0 ;
bool IntersBTWArcs = false ;
// creo l'arco di raccordo
PtrOwner<ICurveArc> pCrvArc( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ;
// controllo che l'arco creato non sia troppo piccolo
double dArcLen ;
if ( ! IsNull( pCrvArc) && pCrvArc->IsValid() && (! pCrvArc->GetLength( dArcLen) || dArcLen < 5 * EPS_SMALL)) {
vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco nullo
vArcsToJump.push_back( i) ; // da scartare
continue ;
}
if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) { // dal secondo arco in poi controllo che non ci siano intersezioni tra essi
IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ;
if ( intCCH.GetIntersCount() > 0 )
IntersBTWArcs = true ;
}
// se ho intersezioni tra archi o l'arco creato non è valido, allora provo altre nMaxTestForArcs volte a ricrearlo avvicinando i punti
while (( IsNull( pCrvArc) || IntersBTWArcs) && nIterForArcs < nMaxTestForArcs) {
dUE_ref = ( 1 + dUE_ref ) * 0.5 ;
dUS_ref = dUS_ref * 0.5 ;
if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) ||
! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE))
return false ;
dRadius = Dist( ptS, ptE) ;
pCrvArc.Set( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ;
nIterForArcs++ ;
IntersBTWArcs = false ;
if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) { // dal secondo arco in poi controllo che non ci siano intersezioni tra essi
IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ;
if ( intCCH.GetIntersCount() > 0)
IntersBTWArcs = true ;
}
}
if ( IsNull( pCrvArc) || ! pCrvArc->IsValid()) { // se ancora non riesco... salto l'arco
vCrvArcs.emplace_back( CreateCurveArc()) ; // arco nullo
vArcsToJump.push_back( i) ; // da scartare
continue ;
}
// se ho creato l'arco lo memorizzo
vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco valido
vArcsToJump.push_back( -1) ; // da considerare
}
// creo la curva che restituirò
PtrOwner<ICurveComposite> pCrvCO_temp( CreateCurveComposite()) ;
if ( IsNull( pCrvCO_temp))
return false ;
Point3d ptArcHelp, ptFirstPoint ;
// unisco la curva i-esima con l'arco i-esimo (non guardo l'ultima curva nel vettore, controllo dopo il caso di curva chiusa)
for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) {
if ( vArcsToJump[i] == -1) { // se esiste l'arco ...
Point3d ptArcS, ptArcE ;
if ( ! vCrvArcs[i]->GetStartPoint( ptArcS) ||
! vCrvArcs[i]->GetEndPoint( ptArcE) ||
! vCrvStepsToFill[i]->GetParamAtPoint( ptArcS, dUE_ref) ||
! vCrvStepsToFill[i+1]->GetParamAtPoint( ptArcE, dUS_ref))
return false ;
if ( i == 0) { // ... e sono nella prima iterazione ...
if ( ! pCrv->IsClosed()) { // ... e se la curva è aperta -> la inserisco nel nuovo percorso
if ( ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref))))
return false ;
}
ptFirstPoint = ptArcS ;
// ... e se la curva è chiusa -> non la inserisco nel nuovo percorso
}
else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...)
if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref))))
return false ;
// aggiungo la curva 'tagliata per il raccordo'
}
if ( ! pCrvCO_temp->AddCurve( vCrvArcs[i]->Clone())) // aggiungo l'arco di raccordo
return false ;
ptArcHelp = ptArcE ;
}
else { // se non esiste l'arco ...
if ( i == 0 ) { // e sono nella prima iterazione...
if ( ! vCrvStepsToFill[0]->GetEndPoint( ptArcHelp))
return false ;
if ( ! pCrv->IsClosed()) { // ...e se aperta // aggiungo la prima curva per intero
if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUE_ref) ||
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref))))
return false ;
}
ptFirstPoint = ptArcHelp ;
}
else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...)
double dUS_cm ;
if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
! vCrvStepsToFill[i]->GetDomain( dUS_cm, dUE_ref) ||
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref))) ||
! vCrvStepsToFill[i]->GetEndPoint( ptArcHelp))
return false ;
}
}
}
// ultima curva...
if ( pCrv->IsClosed()) { // se curva chiusa...
if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
! vCrvStepsToFill[0]->GetParamAtPoint( ptFirstPoint, dUE_ref) ||
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( dUS_ref, dUE_ref))))
return false ;
}
else { // se curva aperta... ( non ha importanza l'esistenza o meno degli archi...)
double dUS_cm ;
if ( ! vCrvStepsToFill.back()->GetParamAtPoint( ptArcHelp, dUS_ref) ||
! vCrvStepsToFill.back()->GetDomain( dUS_cm, dUE_ref) ||
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill.back()->CopyParamRange( dUS_ref, dUE_ref))))
return false ;
}
// ripristino il punto inziale se la curva è chiusa
double dNewDU ;
if ( ! pCrv->IsClosed()) {
if ( ! pCrvCO_temp->GetParamAtPoint( ptArcHelp, dNewDU))
return false ;
pCrvCO_temp->ChangeStartPoint( dNewDU) ;
}
pCrv->Clear() ;
pCrv->AddCurve( Release( pCrvCO_temp)) ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, ICRVCOMPOPOVECTOR& vOffsCL,
ICRVCOMPOPOVECTOR& vFirstOffset, ICurveComposite* pCrvLink, double dLenPercS,
double dLenPercE, int nMaxIter)
{
// controllo i parametri
if ( pCrvS == nullptr || pCrvE == nullptr )
return false ;
pCrvLink->Clear() ;
PtrOwner<ICurveComposite> 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<ICurveComposite> 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<ICurveComposite> pCrvS_clone( pCrvS->Clone()) ; // curva di Offset iniziale accorciata
PtrOwner<ICurveComposite> 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<ISurfFlatRegion> pSrfChunkToCut( pSrfToCut->CloneChunk( i)) ;
if ( IsNull( pSrfChunkToCut))
return false ;
// Curva nel caso la regione si svuoti con MedialAxis
PtrOwner<ICurveComposite> 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<ICurveComposite> pCrvH1( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pCrvH2( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pCrvH1I( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> 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<ICurveComposite> 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<ICurveComposite> pCrvPathI( pCrvPath->Clone()) ;
pCrvPathI->Invert() ;
PtrOwner<ICurveComposite> 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<ICurveComposite> 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<ICurveComposite> pCrvPathI( pCrvPath->Clone()) ;
pCrvPathI->Invert() ;
PtrOwner<ICurveComposite> 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<ICurveLine> 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<ICurveArc> 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<ICurveLine> 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<ICurveComposite> pPath1( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> 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<ICurveComposite> pPath1( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> 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<ICurveComposite> 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<ICurveComposite> 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<ICurveComposite> 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<ICurveLine> 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<ICurve> pCrvHelper(( pPath2->CopyParamRange( dUS, dUE - 0.5))) ;
pCrvHelper->GetEndPoint( ptNear) ;
PtrOwner<ICurveLine> 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<ICurveComposite> pNewPath2( GetCurveComposite( pPath2->CopyParamRange( dUS, dUE))) ;
// creo la curva formata dai due segmenti ma smussati tra loro...
PtrOwner<ICurveComposite> 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<ICurveComposite> 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<ICurveComposite> 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<ISurfFlatRegion> pSrfRes( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfRes))
return false ;
for ( int c = 1 ; c < pSrfOrig->GetChunkCount() ; ++ c) {
PtrOwner<ISurfFlatRegion> pSrfHelp( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfHelp))
return false ;
PtrOwner<ICurveComposite> 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<ICurveComposite> pCrvStepByStepPath( CreateCurveComposite()) ;
if ( IsNull( pCrvStepByStepPath))
return false ;
// copie del chunk che devo svuotare
PtrOwner<ISurfFlatRegion> 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<ISurfFlatRegion> 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<ICurveArc> pCrvToolShape( CreateCurveArc()) ;
if ( IsNull( pCrvToolShape))
return false ;
pCrvToolShape->SetXY( ptC, m_TParams.m_dDiam / 2 + 5 * EPS_SMALL) ;
PtrOwner<ISurfFlatRegion> pSrfTool( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfTool) || ! pSrfTool->AddExtLoop( Release( pCrvToolShape)) ||
! pSrfTool->IsValid())
break ;
PtrOwner<ISurfFlatRegion> 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<ICurve> 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<ICurveComposite> 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<ISurfFlatRegion> 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<ICurveComposite> 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<ICurveComposite> 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<ICurveComposite> pCrvA( GetCurveComposite( pCrvPath->CopyParamRange( 0, dU))) ;
if ( IsNull( pCrvA))
return false ;
PtrOwner<ICurveComposite> 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<ICurveComposite> pCrvPath1( CreateCurveComposite()) ;
if ( IsNull( pCrvPath1))
return false ;
if ( ! CalcBoundedSmootedLink( ptClosestOnPath, vtTanCpt, vPtCentroid[i], vtTanCpt, 0, vOffsFirstCurve, pCrvPath1))
if ( ! CalcBoundedLink( ptClosestOnPath, vPtCentroid[i], vOffsFirstCurve, pCrvPath1))
return false ;
pCrvPath->Clear() ;
pCrvPath->AddCurve( Release( pCrvA)) ;
if( ! pCrvPath->AddCurve( Release( pCrvPath1)))
return false ;
pCrvPath->AddCurve( Release( pCrvB)) ;
}
// la curva resitituita è una curva aperta che ha come primo punto il punto iniziale del primo medialAxis
// la curva restituita collega tutti i medial Axis tra di loro
// la curva restitutita collega tutti i centroidi con delle circonferenze
// la curva restituita ha come punto finale l'ultimo punto dell'ultimo medial Axis
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, ICRVCOMPOPOVECTOR& vOffIslands, ICurveComposite* pCrvLink)
{
pCrvLink->Clear() ;
// recupero il vettore estrusione dal bordo esterno offsettato della superficie
Vector3d vtExtr ;
vOffIslands[0]->GetExtrusion( vtExtr) ;
// determino il riferimento naturale della svuotatura (OCS con il vettore estrusione come asse Z)
Frame3d frLoc ;
frLoc.Set( ORIG, vtExtr) ;
// non serve collegare ( può capitare nel tagliare percorsi con isole complesse)
if ( AreSamePointApprox( ptStart, ptEnd))
return true ;
// porto la curva di contenimento in locale a questo riferimento
vector<CurveLocal> 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<ICurveComposite> 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<ICurveComposite> pCompo( pLine->Clone()) ;
if ( IsNull( pCompo) )
return false ;
if ( ! pCompo->LocToLoc( GLOB_FRM, frLoc) )
return false;
PtrOwner<ICurveComposite> 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<ICurve> pCrvA( GetCurve( vOffIslands[i]->CopyParamRange( dOffS, dOffE))) ;
PtrOwner<ICurve> pCrvB( GetCurve( vOffIslands[i]->CopyParamRange( dOffE, dOffS))) ;
if ( IsNull( pCrvA) || IsNull( pCrvB) )
return false ;
double dLenA ; pCrvA->GetLength( dLenA) ;
double dLenB ; pCrvB->GetLength( dLenB) ;
if ( dLenA < dLenB ) {
pCompoHelp->AddCurve( Release( pCrvA)) ;
}
else {
pCrvB->Invert() ;
pCompoHelp->AddCurve( Release( pCrvB)) ;
}
}
else { // se non interseco
pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
}
}
pCompo->Clear() ;
pCompo->AddCurve( pCompoHelp->Clone()) ;
}
// riporto pCompo nel sistema di riferimento originale
if ( ! pCompo->LocToLoc( frLoc, GLOB_FRM))
return false ;
pCrvLink->AddCurve( Release( pCompo)) ;
return true ;
}
//------------------------------------------------------------------------------
bool
Pocketing::CalcBoundedSmootedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd,
const Vector3d& vtEnd, double dParMeet, ICRVCOMPOPOVECTOR& vOffIslands,
ICurveComposite* pCrvLink)
{
// creo il BiArc che unisce i due punti
double dAngStart, dAngEnd ;
vtStart.GetAngleXY( X_AX, dAngStart) ;
vtEnd.GetAngleXY( X_AX, dAngEnd) ;
PtrOwner<ICurve> pBiArcLink ;
if ( dParMeet != 0)
pBiArcLink.Set( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, dParMeet)) ;
else {
PtrOwner<ICurveArc> 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 ;
if ( ! CalcBoundedLink( ptS_shift, ptE_shift, 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<ICurveComposite> pCrvNewBiArcS( CreateCurveComposite()) ;
ModifyBiArc( pBiArcLink, 0.2, pCrvNewBiArcS) ;
pBiArcLink.Set( pCrvNewBiArcS) ;
}
// creo la nuova curva formata inizialmente dai tratti di archi
PtrOwner<ICurveComposite> pCompo( GetCurveComposite( pBiArcLink->Clone())) ;
if ( IsNull( pCompo))
return false ;
PtrOwner<ICurveComposite> 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<ICurve> pCrvA( GetCurve( vOffIslands[i]->CopyParamRange( dOffS, dOffE))) ;
PtrOwner<ICurve> 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<ICurve> pArc1( GetCurve( pBiArcLink->CopyParamRange( dUS, (dUS + dUE) / 2))) ;
PtrOwner<ICurve> 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<ICurve> pArc1A( GetCurve( pArc1->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll))) ; // Arc1
PtrOwner<ICurve> pArc1B( GetCurve( pArc1->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE))) ; // Arc2
Point3d pt1A, pt1B ;
pArc1A->GetEndPoint( pt1A) ;
pArc1B->GetStartPoint( pt1B) ;
PtrOwner<ICurveLine> pLine1( CreateCurveLine()) ;
pLine1->Set( pt1A, pt1B) ; // Linea
// secondo pezzo
pArc2->GetDomain( dUS, dUE) ;
PtrOwner<ICurve> pArc2A( GetCurve( pArc2->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll))) ; // Arc1
PtrOwner<ICurve> pArc2B( GetCurve( pArc2->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE))) ; // Arc2
Point3d pt2A, pt2B ;
pArc2A->GetEndPoint( pt2A) ;
pArc2B->GetStartPoint( pt2B) ;
PtrOwner<ICurveLine> 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<ICurveComposite> pCompo( pCurve->Clone()) ;
PtrOwner<ICurveComposite> pCompoHelp( pCurve->Clone()) ;
PtrOwner<ICurveComposite> 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<ICurve> pCrvA( GetCurve( vOffOrig[i]->CopyParamRange( dOffS, dOffE))) ;
PtrOwner<ICurve> 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<ICurve> 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<ICurveComposite> 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<ICurveComposite> pCompo( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> 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<ICurveComposite> 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<ICurveComposite> 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<ICurveComposite> 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<ICurveComposite> 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<ICurveComposite> 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<int>( ceil( ( dLen) / m_Params.m_dEpicyclesDist))) ;
double dStep = 1.0 / nStep ;
for ( int k = 1 ; k <= nStep ; k ++) {
// creo epiciclo
PtrOwner<ICurveArc> 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<ICurve> 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 Point3d& ptP1, const Point3d& ptStart, const Vector3d& vtStart, const Vector3d& vtN,
ISurfFlatRegion* pSrfChunk, const ICurveComposite* pRCrv, bool bAtLeft, bool bSplitArcs,
bool bNoneForced, bool bSkipControl)
{
// Assegno il tipo
int nType = GetLeadInType() ;
if ( bNoneForced ||
AreSamePointEpsilon( ptP1, ptStart, 10 * EPS_SMALL) ||
( nType == POCKET_LI_GLIDE && ( pRCrv == nullptr || pRCrv->GetCurveCount() == 0)))
nType = POCKET_LI_NONE ;
// Se elica e fattibile lo creo
if ( nType == POCKET_LI_HELIX) {
// vettore dal punto al centro elica
Vector3d vtCen = vtStart ;
vtCen.Rotate( vtN, 0, ( bAtLeft ? 1 : - 1)) ;
// dati dell'elica
double dRad = min( 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam), m_dMaxHelixRad) ;
Point3d ptCen = ptP1 + vtCen * dRad ;
double dDeltaN = ( ptStart - ptP1) * vtN ;
double dAngCen = ceil( - dDeltaN / ( m_Params.m_dLiElev + 20 * EPS_SMALL)) * ( bAtLeft ? ANG_FULL : - ANG_FULL) ;
// verifico se fattibile
if ( bSkipControl || VerifyLeadInHelix( pSrfChunk, ptCen, dRad)) {
// creo l'elica
PtrOwner<ICurveArc> pArc( CreateCurveArc()) ;
if ( IsNull( pArc) || ! pArc->Set( ptCen, vtN, dRad, - vtCen, dAngCen, dDeltaN))
return false ;
// eventuale spezzatura
if ( bSplitArcs) {
PtrOwner<ICurveComposite> pCompo ;
if ( ! pCompo.Set( ConvertCurveToComposite( Release( pArc))) || ! ApproxWithLines( pCompo))
return false ;
return ( AddCurveMove( pCompo, MCH_CL_LEADIN) != GDB_ID_NULL) ;
}
else {
// emetto l'elica
return ( AddCurveMove( pArc, MCH_CL_LEADIN) != GDB_ID_NULL) ;
}
}
// altrimenti zigzag
else
nType = POCKET_LI_ZIGZAG ;
}
// Se zigzag e fattibile lo creo
if ( nType == POCKET_LI_ZIGZAG) {
// dati dello zigzag
double dDeltaN = ( ptStart - ptP1) * vtN ;
int nStep = int( ceil( - dDeltaN / ( m_Params.m_dLiElev + 20 * EPS_SMALL))) ;
double dStep = - dDeltaN / nStep ;
Point3d ptPa = ptP1 + vtStart * 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam) ;
Point3d ptPb = ptP1 - vtStart * 0.5 * min( m_Params.m_dLiTang, m_TParams.m_dDiam) ;
// verifico se fattibile
if ( bSkipControl || VerifyLeadInZigZag( pSrfChunk, ptPa, ptPb)) {
for ( int i = 1 ; i <= nStep ; ++ i) {
if ( AddLinearMove( ptPa - vtN * ( i - 0.75) * dStep, MCH_CL_LEADIN) == GDB_ID_NULL)
return false ;
if ( AddLinearMove( ptPb - vtN * ( i - 0.25) * dStep, MCH_CL_LEADIN) == GDB_ID_NULL)
return false ;
}
return ( AddLinearMove( ptStart, MCH_CL_LEADIN) != GDB_ID_NULL) ;
}
// altrimenti diretto
else {
nType = POCKET_LI_NONE ;
if ( m_TParams.m_nType == TT_MILL_NOTIP)
return false ;
}
}
// Se a scivolo e fattibile
if ( nType == POCKET_LI_GLIDE) {
if ( pRCrv != nullptr) {
// recupero la parte richiesta della curva di ritorno
PtrOwner<ICurveComposite> 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<ICurveComposite> 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,
Point3d& ptMid, Vector3d& vtMidOrt)
{
// recupero il vettore estrusione
Vector3d vtExtr = Z_AX ;
pCompo->GetExtrusion( vtExtr) ;
// verifico se tutti i lati sono aperti
bool bAllOpen = true ;
const ICurve* pMyCrv = pCompo->GetFirstCurve() ;
while ( pMyCrv != nullptr) {
if ( pMyCrv->GetTempProp() != 1) {
bAllOpen = false ;
break ;
}
pMyCrv = pCompo->GetNextCurve() ;
}
// richiedo lunghezza superiore a diametro utensile più doppio offset radiale
double dRefLen = ( bAllOpen ? 0 : m_TParams.m_dDiam + 2 * GetOffsR() - EPS_SMALL) ;
double dMaxLen = dRefLen ;
// ciclo sulle singole curve
bool bFound = false ;
const ICurve* pPrevCrv = pCompo->GetLastCurve() ;
double dLenPrev = 0 ;
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == 1)
pPrevCrv->GetLength( dLenPrev) ;
const ICurve* pCrv = pCompo->GetFirstCurve() ;
int nPriorityOpenEdge = -1 ;
while ( pCrv != nullptr) {
// analizzo la curva successiva
const ICurve* pNextCrv = pCompo->GetNextCurve() ;
bool bNextOk = ( pNextCrv != nullptr) ;
if ( ! bNextOk)
pNextCrv = pCompo->GetFirstCurve() ;
double dLenNext = 0 ;
if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == 1)
pNextCrv->GetLength( dLenNext) ;
++ nPriorityOpenEdge ;
// verifico la curva corrente
if ( pCrv->GetTempProp() == 1) {
// contributo dalle entità adiacenti (se non tutte aperte)
double dLenAgg = 0 ;
if ( ! bAllOpen) {
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == 1) {
Vector3d vtPrevEnd ; pPrevCrv->GetEndDir( vtPrevEnd) ;
Vector3d vtStart ; pCrv->GetStartDir( vtStart) ;
dLenAgg += max( 0.4, vtPrevEnd * vtStart) * dLenPrev ;
}
if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == 1) {
Vector3d vtEnd ; pCrv->GetEndDir( vtEnd) ;
Vector3d vtNextStart ; pNextCrv->GetStartDir( vtNextStart) ;
dLenAgg += max( 0.4, vtEnd * vtNextStart) * dLenNext ;
}
}
// entità corrente
double dLen = 0 ;
if ( pCrv->GetLength( dLen)) {
const double LEN_TOL = 1 ;
// se di lunghezza praticamente uguale
if ( bFound && dLen + dLenAgg > dRefLen && abs( dLen + dLenAgg - dMaxLen) < LEN_TOL) {
Point3d ptTest ;
pCrv->GetMidPoint( ptTest) ;
if (( m_bAboveHead && ptTest.z > ptMid.z + 100 * EPS_SMALL) ||
( ! m_bAboveHead && ptTest.z < ptMid.z - 100 * EPS_SMALL) ||
( abs( ptTest.z - ptMid.z) < 100 * EPS_SMALL && ptTest.y < ptMid.y - 100 * EPS_SMALL)) {
dMaxLen = max( dMaxLen, dLen + dLenAgg) ;
ptMid = ptTest ;
// vettore ortogonale verso l'esterno (ruotato -90deg attorno a estrusione)
pCrv->GetMidDir( vtMidOrt) ;
vtMidOrt.Rotate( vtExtr, 0, -1) ;
}
}
// se più lunga ( o non già trovata )
else if ( dLen + dLenAgg > dMaxLen || !bFound) {
dMaxLen = dLen + dLenAgg ;
double dParIn ;
// cerco il parametro di tale curva (0 < dParIn < 1) migliore per entrata
if ( GetParamForPtStartOnEdge( pCrv, pCompo, vOtherCrv, dParIn)) {
pCrv->GetPointD1D2( dParIn, ICurve::FROM_PLUS, ptMid) ;
PtrOwner<ICurveComposite> pCompoClone( CloneCurveComposite( pCompo)) ;
if( IsNull( pCompoClone))
return false ;
double dU ;
pCompoClone->GetParamAtPoint( ptMid, dU) ;
pCompoClone->ChangeStartPoint( dU) ;
pCompoClone->GetStartDir( vtMidOrt) ;
vtMidOrt.Rotate( vtExtr, 0, -1) ;
bFound = true ;
if ( nPriorityOpenEdge == 0)
return true ;
}
}
dLenPrev = dLen ;
}
}
else
dLenPrev = 0 ;
// vado alla successiva
pPrevCrv = pCrv ;
pCrv = ( bNextOk ? pNextCrv : nullptr) ;
}
return bFound ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AdjustContourWithOpenEdges( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vCrvIsl, const double dDiam,
const double dOffR, const double dStep, const ISurfFlatRegion* pSfrLimit)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// raggio di riferimento per offset
double dOutEdge = 0.5 * dDiam ;
if ( m_Params.m_nSubType == POCKET_SUB_SPIRALIN || m_Params.m_nSubType == POCKET_SUB_ZIGZAG)
dOutEdge = max( dOutEdge, dDiam - dStep) ;
double dRad = dOutEdge + dOffR ;
// ricavo i tratti omogenei
ICRVCOMPOPOVECTOR vpCrvs ;
if ( ! GetHomogeneousParts( pCrvCompo, vpCrvs))
return false ;
// Offset esterno per CurveJ
double dDiamJ = 1.05 * dDiam + 2 * dOffR ;
// NB. 1.05 serve per eccedere leggermente, in modo che il contro-offset della prima curva di svuotatura presenti
// dei piccoli archi tra i chiusi e gli aperti ( in modo da svuotare bene lungo il chiuso)
// curva finale da restituire
PtrOwner<ICurveComposite> pCrvCompo_final( CreateCurveComposite()) ;
if ( IsNull( pCrvCompo_final))
return false ;
// parametro iniziale del tratto corrente, sulla curva originale
double dParS = 0. ;
// scorro tutti i tratti omogenei nel vettore
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
// recupero la proprietà della curva composita
int nCurrTmpProp = vpCrvs[i]->GetTempProp() ;
// aggiorno i parametri del lato aperto corrente sulla curva originale
double dParE = dParS + 1. * vpCrvs[i]->GetCurveCount() ;
// se aperta
if ( nCurrTmpProp == 1) {
if ( ! AdjustOpenEdge( pCrvCompo, vCrvIsl, dParS, dParE, dRad, dDiamJ, pSfrLimit, vpCrvs[i]))
return false ;
}
// assegno le proprietà di lato Aperto/Chiuso per la curva corrente
for ( int u = 0 ; u < vpCrvs[i]->GetCurveCount() ; ++ u)
vpCrvs[i]->SetCurveTempProp( u, nCurrTmpProp, 0) ;
// aggiungo la curva ricavata
if ( ! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone())) {
Point3d ptH ; vpCrvs[i]->GetStartPoint( ptH) ;
if ( ! pCrvCompo_final->AddLine( ptH) ||
! pCrvCompo_final->SetCurveTempProp( pCrvCompo_final->GetCurveCount() - 1, 1, 0) ||
! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone()))
return false ;
}
// aggiorno
dParS = dParE ;
}
// non dovrebbe esserci un gap, ma meglio prevenire problemi
pCrvCompo_final->Close() ;
// sostituisco
pCrvCompo->Clear() ;
pCrvCompo->CopyFrom( pCrvCompo_final) ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AdjustOpenEdge( const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vCrvIsland,
const double dParS, const double dParE, const double dRad, const double dDiamJ,
const ISurfFlatRegion* pSfrLimit, ICurveComposite* pCrvBorder)
{
/* parametri :
pCrvCompo -> curva originaria di bordo
vCrvIsland -> vettore delle isole all'interno di pCrvCompo
pCrvOpenOffs -> tratto aperto corrente già Offsettato verso l'esterno
dParS -> parametro sulla pCrvCompo per l'inizio del tratto aperto
dParE -> parametro sulla pCrvCompo per la fine del tratto aperto
dRad -> raggio di Offset per la regione di incidenza
dDiamJ -> ampiezza delle curve a fagiolo per estendere la regione di incidenza
pCrvRes -> curva da restituire ( inizialmente è il tratto aperto sulla pCrvCompo ;
questa curva sarà l'estensione del lato aperto, adattandosi alla geometria
dei chiusi ( non posso vedere solo i chiusi adiacenti all'aperto, devo considerare TUTTI i chiusi
della pCrvCompo
pStmVol -> Volume di svuotatura
pStm_Part -> Part corrente
*/
// controllo la validtà dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 ||
pCrvBorder == nullptr || ! pCrvBorder->IsValid() || pCrvBorder->GetCurveCount() == 0)
return false ;
// definisco la regione di incidenza
PtrOwner<ISurfFlatRegion> 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<ICurveComposite> pCompoOther( ConvertCurveToComposite( pCrvCompo->CopyParamRange( dParE, dParS))) ;
if ( IsNull( pCompoOther) || ! pCompoOther->IsValid())
return false ;
vCrvToCheck.emplace_back( Release( pCompoOther)) ;
}
// scorro il vettore creato...
for ( int c = 0 ; c < ( int)vCrvToCheck.size() ; ++ c) {
// 1) recupero la curva corrente
PtrOwner<ICurveComposite> pCrvCurr( CloneCurveComposite( vCrvToCheck[c])) ;
if ( IsNull( pCrvCurr) || ! pCrvCurr->IsValid())
return false ;
// 2) classifico la curva con la superficie
CRVCVECTOR ccClass ;
if ( pSfrInc->GetCurveClassification( *pCrvCurr, EPS_SMALL, ccClass)) {
// 3) scorro tutte le classificazioni per cercare i tratti interni
for ( int i = 0 ; i < ( int)ccClass.size() ; ++ i) {
if ( ccClass[i].nClass == CRVC_IN) {
// 4) se il tratto è interno, recupero tutti i suoi tratti omogenei
PtrOwner<ICurveComposite> pCrvCurrPartIn( ConvertCurveToComposite( pCrvCurr->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
if ( IsNull( pCrvCurr) || ! pCrvCurr->IsValid())
return false ;
ICRVCOMPOPOVECTOR vpCrvs ;
if ( ! GetHomogeneousParts( pCrvCurrPartIn, vpCrvs))
return false ;
// 5) considero solo i tratti chiusi
for ( int cl = 0 ; cl < int( vpCrvs.size()) ; ++ cl) {
if ( vpCrvs[cl]->GetTempProp() == 1)
continue ;
// 6) effettuo l'Offset della curva di metà dDiamJ
OffsetCurve OffsCrv ;
if ( ! OffsCrv.Make( vpCrvs[cl], - dDiamJ * 0.5, ICurve::OFF_FILLET)) {
m_pMchMgr->SetLastError( 2412, "Error in Pocketing : Offset not computable") ;
return false ;
}
// 7) scorro tutte le curve di Offset che si sono formate, prendendo sempre la più lunga tra le rimanenti
PtrOwner<ICurve> pOffLongestCrv( OffsCrv.GetLongerCurve()) ;
while ( ! IsNull( pOffLongestCrv)) {
// 8) tengo solo le curve che non si chiudono su loro stesse
if ( ! pOffLongestCrv->IsClosed()) {
// 9) creo la regione di incidenza di tale curva ( "Curva a fagiolo")
PtrOwner<ISurfFlatRegion> pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ * 0.5, false, false)) ;
if ( IsNull( pSfrBean) || ! pSfrBean->IsValid())
return false ;
// inverto se necessario
if ( AreOppositeVectorApprox( pSfrBean->GetNormVersor(), pSfrInc->GetNormVersor()))
pSfrBean->Invert() ;
// 10) aggiorno la regione di incidenza
if ( ! pSfrInc->Add( *pSfrBean)) {
int o = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pSfrInc->Clone()) ;
m_pGeomDB->SetMaterial( o, ORANGE) ;
o = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pSfrBean->Clone()) ;
m_pGeomDB->SetMaterial( o, RED) ;
return false ;
}
}
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ;
}
}
}
}
}
else
return false ;
}
// ATTENZIONE !
// L'algoritmo di allargamento presso i lati aperti è Euristico; io mi estendo a seconda della geometria del
// lato aperto al di fuori del volume di svuotatura... Devo controllare di non rovinare delle zone al di fuori di
// esso !
if ( 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
if ( pSfrInc->GetChunkCount() > 1) {
PtrOwner<ISurfFlatRegion> pNewSfrInc( CreateSurfFlatRegion()) ;
if ( IsNull( pNewSfrInc))
return false ;
Point3d ptS, ptE ;
if ( ! pCrvBorder->GetStartPoint( ptS) || ! pCrvBorder->GetEndPoint( ptE))
return false ;
for ( int i = 0 ; i < pSfrInc->GetChunkCount() ; ++ i) {
PtrOwner<ICurveComposite> pCrvEL( ConvertCurveToComposite( pSfrInc->GetLoop( i, 0))) ;
if ( IsNull( pCrvEL) || ! pCrvEL->IsValid())
return false ;
if ( pCrvEL->IsPointOn( ptS, 300 * EPS_SMALL) && pCrvEL->IsPointOn( ptE, 300 * EPS_SMALL)) {
if ( ! pNewSfrInc->IsValid())
pNewSfrInc.Set( pSfrInc->CloneChunk( i)) ;
else
if ( ! pNewSfrInc->Add( * pSfrInc->CloneChunk( i))) ;
}
}
if ( ! pNewSfrInc->IsValid())
return false ;
pSfrInc.Set( Release( pNewSfrInc)) ;
}
}
// controllo se la curva è un'isola
bool bIsIsland = false ;
double dArea ; Plane3d plCheck ;
if ( ! pCrvCompo->GetArea( plCheck, dArea))
return false ;
bIsIsland = AreSameVectorEpsilon( plCheck.GetVersN(), - Z_AX, 10 * EPS_SMALL) ;
// recupero la curva di bordo del lato aperto corrente
PtrOwner<ICurveComposite> pCrvNewBorder( ConvertCurveToComposite( pSfrInc->GetLoop( 0, 0))) ;
if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid())
return false ;
// imposto la curva come tutta aperta
for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u)
pCrvNewBorder->SetCurveTempProp( u, 1, 0) ;
// se la curva originale era tutta aperta...
if ( bIsAllOpen) {
// pulisco la curva originale
pCrvBorder->Clear() ;
if ( bIsIsland) { // se isola inserisco il loop interno della regione
pCrvNewBorder.Set( ConvertCurveToComposite( pSfrInc->GetLoop( 0, 1))) ;
if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid())
return false ;
// imposto la curva come tutta aperta ( gira come girava già l'isola)
for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u)
pCrvNewBorder->SetCurveTempProp( u, 1, 0) ;
}
// se bordo esterno inserisco il bordo esterno
return pCrvBorder->AddCurve( Release( pCrvNewBorder)) ;
}
// altrimenti la spezzo il loop della regione di incidenza nei punti iniziali e finali della curva aperta originale
Point3d ptStart ; pCrvBorder->GetStartPoint( ptStart) ;
Point3d ptEnd ; pCrvBorder->GetEndPoint( ptEnd) ;
double dUTrimS, dUTrimE ;
pCrvNewBorder->GetParamAtPoint( ptStart, dUTrimS, 300 * EPS_SMALL) ;
pCrvNewBorder->GetParamAtPoint( ptEnd, dUTrimE, 300 * EPS_SMALL) ;
// pulisco la curva originale
pCrvBorder->Clear() ;
if ( ! pCrvBorder->AddCurve( pCrvNewBorder->CopyParamRange( dUTrimS, dUTrimE)) ||
! pCrvBorder->IsValid())
return false ;
return true ;
}
//---------------------------------------------------------------------------
bool
Pocketing::CreateSurfFrIncidence( const ICurveComposite* pCrv, const double dRad, ISurfFlatRegion* pSfrInc)
{
// controllo dei parametri
if ( pCrv == nullptr || ! pCrv->IsValid())
return false ;
pSfrInc->Clear() ;
// creo la Fat Curve dalla curva *pCrv
PtrOwner<ISurfFlatRegion> pSfrInc_tmp( GetSurfFlatRegionFromFatCurve( pCrv->Clone(), dRad + EPS_SMALL, false, false)) ;
if ( IsNull( pSfrInc_tmp) || ! pSfrInc_tmp->IsValid())
return false ;
pSfrInc->CopyFrom( pSfrInc_tmp) ; // pSfrInc ha sempre normale Z_AX !
// La regione di incidenza non deve avere un bordo distante dRad dagli estremi del tratto aperto
// per questo motivo, creo due FlatRegion a rettangolo che andrò a sottrarre alla pSfrInc_tmp
if ( pCrv->IsClosed()) {
pSfrInc->CopyFrom( pSfrInc_tmp) ;
return pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0 ;
}
// Rettangolo all'inizio
Vector3d vtTanS ; pCrv->GetStartDir( vtTanS) ;
Point3d ptS ; pCrv->GetStartPoint( ptS) ;
Vector3d vtOut = vtTanS ; vtOut.Rotate( Z_AX, -90) ;
PtrOwner<ICurveComposite> 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<ISurfFlatRegion> 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<ICurveComposite> 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<ISurfFlatRegion> pSfrRectEnd( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrRectEnd) ||
! pSfrRectEnd->AddExtLoop( Release( pCrvRectEBorder)) ||
! pSfrRectEnd->IsValid())
return false ;
if ( AreOppositeVectorApprox( pSfrInc->GetNormVersor(), pSfrRectEnd->GetNormVersor()))
pSfrRectEnd->Invert() ;
// alla superficie di incidenza, sottraggo i due rettangoli ricavati
if ( ! pSfrInc->Subtract( *pSfrRectStart) ||
! pSfrInc->Subtract( *pSfrRectEnd))
return false ;
return pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0 ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AdjustContourStart( ICurveComposite* pCompo, const ICRVCOMPOPOVECTOR& vCrvIsl, bool bOrder)
{
// se cerco semplicemente il tratto lineare chiuso più lungo ...
if ( ! bOrder) {
// la priorità è essagnata ai tratti lineari chiusi
int i = 0 ; // <--- indice della curva corrente
int nMax = - 1 ;
double dLenMax = 0 ;
const ICurve* pCrv = pCompo->GetFirstCurve() ;
while ( pCrv != nullptr) {
double dLen ;
if ( pCrv->GetType() == CRV_LINE && pCrv->GetTempProp() == 0 && pCrv->GetLength( dLen) && dLen > dLenMax) {
dLenMax = dLen ;
nMax = i ;
}
++ i ;
pCrv = pCompo->GetNextCurve() ;
}
// se non trovato o troppo corto, cerco il tratto chiuso più lungo in generale
if ( nMax < 0 || dLenMax < 2 * m_TParams.m_dDiam) {
i = 0 ;
pCrv = pCompo->GetFirstCurve() ;
while ( pCrv != nullptr) {
double dLen ;
if ( pCrv->GetType() != CRV_LINE && pCrv->GetTempProp() == 0 && pCrv->GetLength( dLen) && dLen > dLenMax) {
dLenMax = dLen ;
nMax = i ;
}
++ i ;
pCrv = pCompo->GetNextCurve() ;
}
}
// controllo che il tratto chiuso più lungo trovato sia sufficientemente lungo
if ( dLenMax < 10 * EPS_SMALL) {
// se troppo piccolo allora lo imposto aperto
pCompo->SetCurveTempProp( nMax, 0, 1) ;
// spezzo la curva nel primo tratto aperto sufficientemente lungo
for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) {
double dLen ; pCompo->GetCurve( u)->GetLength( dLen) ;
if ( dLen > 10 * EPS_SMALL) {
nMax = u ;
continue ;
}
}
}
// se non trovato, imposto il punto iniziale a mentà del primo tratto
if ( nMax == -1) {
pCompo->ChangeStartPoint( 0.5) ;
return true ;
}
pCompo->ChangeStartPoint( nMax + 0.5) ;
}
// se invece sto cercando di entrare da un lato chiuso per una svuotatura, allora riordino i lati
// chiusi a seconda della lunghezza, e a partire dal più lungo cerco un parametro su tale curva che mi consenta
// un'entrata sufficientemente distante da isole e da altre curve della curva stessa su cui cerco l'entrata ...
else {
// creo un vettore di indici che definisce l'ordine delle curve chiuse in base alla lunghezza
INTVECTOR vInd ; vInd.reserve( pCompo->GetCurveCount()) ;
// vettore di indici già utilizzati
INTVECTOR vIndUsed ; vIndUsed.reserve( pCompo->GetCurveCount()) ;
double dMaxLen = -INFINITO ;
int nCurrInd = 0 ;
bool bStop = false ;
for ( int c = 0 ; c < pCompo->GetCurveCount() && ! bStop ; ++ c) {
bStop = true ;
for ( int i = 0 ; i < pCompo->GetCurveCount() ; ++ i) {
int nProp_i ;
// se la curva i-esima non è già stata considerata ed è 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 (( int)vInd.size() == 0) {
// se questa condizione fosse vera allora non sono riuscito ad entrare da nessun lato aperto in precedenza e non
// ho nemmeno un lato chiuso disponibile per entrare...
pCompo->ChangeStartPoint( 0.5) ;
return true ;
}
// ora che ho riempito il vettore di indici lo scorro e cerco di entrare in questi lati chiusi secondo l'ordine
bool bOk = false ;
for ( int i = 0 ; i < ( int)vInd.size() && !bOk ; ++ i) {
const ICurve* pCrv( pCompo->GetCurve( vInd[i])) ;
double dPar ;
if ( GetParamForPtStartOnEdge( pCrv, pCompo, vCrvIsl, dPar)) {
pCompo->ChangeStartPoint( vInd[i] + dPar) ;
bOk = true ;
}
}
if ( ! bOk) {
// se non riesco ad entrare da nessun lato chiuso, considerando che in precedenza ho già provato ad
// entrare da tutti i lati aperti...
pCompo->ChangeStartPoint( 0.5) ;
return true ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::VerifyLeadInHelix( ISurfFlatRegion* pSrfChunk, const Point3d& ptCen, double dRad) const
{
// controllo della superifice
if ( pSrfChunk == nullptr)
return false ;
// vettore di tutte le curve della superficie
ICRVCOMPOPOVECTOR vCrv ;
for( int c = 0 ; c < pSrfChunk->GetChunkCount() ; c++) {
for ( int l = 0 ; l < pSrfChunk->GetLoopCount( c) ; l++) {
vCrv.emplace_back( ConvertCurveToComposite( pSrfChunk->GetLoop( c, l))) ;
}
}
if (( int)vCrv.size() == 0)
return false ;
// estraggo il bordo esterno
PtrOwner<ICurveComposite> 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<ICurveComposite> 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<ISurfTriMesh> pStmRef( CloneSurfTriMesh( pStmAbove)) ;
if ( IsNull( pStmRef) || ! pStmRef->IsValid())
return false ;
// 2.1) Per semplicità rimuovo dalla superficie tutti i triangoli che sono perpendicolari al piano di svuotatura
StmFromTriangleSoup TriaSoupEasy ; TriaSoupEasy.Start() ;
for ( int t = 0 ; t < pStmRef->GetTriangleCount() ; ++ t) {
Vector3d vtNTria ;
if ( pStmRef->GetFacetNormal( pStmRef->GetFacetFromTria( t), vtNTria) &&
abs( vtNTria * vtN) > EPS_SMALL) {
Triangle3d Tria ; pStmRef->GetTriangle( t, Tria) ;
TriaSoupEasy.AddTriangle( Tria) ;
}
}
TriaSoupEasy.End() ;
PtrOwner<ISurfTriMesh> pStmRef_Easy( TriaSoupEasy.GetSurf()) ;
if ( IsNull( pStmRef_Easy) || ! pStmRef_Easy->IsValid())
return true ; // non ho nulla da proiettare
pStmRef.Set( pStmRef_Easy->Clone()) ;
// 3) scalo la superficie rispetto al sistema di riferimento ( mettendo a 0 la componente vtN)
pStmRef->ToLoc( frPlane) ;
if ( ! pStmRef->Scale( GLOB_FRM, 1., 1., 0.))
return false ;
pStmRef->ToGlob( frPlane) ;
// 4) controllo la validità della superficie ottenuta
if ( IsNull( pStmRef) || ! pStmRef->IsValid() || pStmRef->GetTriangleCount() == 0)
return true ; // la proeizione sparisce
// 5) NB. A seconda di come sono orientate le facce, posso avere i Triangoli con normali vtN o -vtN...
// rendo le normali dei triangoli coerenti
StmFromTriangleSoup TriaSoupUp ; TriaSoupUp.Start() ;
StmFromTriangleSoup TriaSoupDown ; TriaSoupDown.Start() ;
for ( int t = 0 ; t < pStmRef->GetTriangleCount() ; ++ t) {
Triangle3d Tria ; pStmRef->GetTriangle( t, Tria) ;
if ( AreOppositeVectorApprox( Tria.GetN(), vtN))
TriaSoupDown.AddTriangle( Tria) ;
else
TriaSoupUp.AddTriangle( Tria) ;
}
TriaSoupUp.End() ;
TriaSoupDown.End() ;
PtrOwner<ISurfTriMesh> pStmUp( TriaSoupUp.GetSurf()) ; // superificie con i Triangoli Up
PtrOwner<ISurfTriMesh> 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<ISurfFlatRegion> pSfrUp( CreateSurfFlatRegion()) ;
PtrOwner<ISurfFlatRegion> 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<ICurveComposite> 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<ICurve> pCrv( pCrvCheck->Clone()) ;
PtrOwner<ICurveComposite> 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<ICurveArc> 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<ICurveComposite> 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<ICurveArc> 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<ICurveArc> pCrvTool( CreateCurveArc()) ;
pCrvTool->Set( ptMid, Z_AX, dDiam) ;
if ( ! pCrvTool->IsValid())
return false ;
IntersCurveCurve intCC( *pArc, *pCrvTool) ;
CRVCVECTOR ccClass ;
if( intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) && ( int)ccClass.size() == 3 && ccClass[1].nClass == CRVC_IN) {
Point3d ptS, ptE ;
pCrvTool->GetPointD1D2( ccClass[1].dParS, ICurve::FROM_MINUS, ptS) ;
pCrvTool->GetPointD1D2( ccClass[1].dParE, ICurve::FROM_PLUS, ptE) ;
Vector3d vtS = ptS - pArc->GetCenter() ;
Vector3d vtE = ptE - pArc->GetCenter() ;
double dTheta ;
vtS.GetAngle( vtE, dTheta) ;
dTheta = abs( dTheta) ;
dSubArc = pArc->GetRadius() * DEGTORAD * dTheta ;
}
else
return false ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::SetBetterPtStartForSubChunks( ICurveComposite* pCrvOffsAct, const ISurfFlatRegion* pSrfToWork,
Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut)
{
// ============================= INFO ==============================================================
// pCrvOffsAct -> Curva di Offset su cui cercare ptStart, vtMidOut, bMidOpen
// pSrfToWork -> Superificie originaria da lavorare
// ( questa superificie ha i flag di lati aperti/chiusi settati nei loops)
// =================================================================================================
// controllo dei parametri
if ( pCrvOffsAct == nullptr || ! pCrvOffsAct->IsValid() || pCrvOffsAct->GetCurveCount() == 0 ||
pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1)
return false ;
// clono le curva su cui devo entrare
PtrOwner<ICurveComposite> pCrv( CloneCurveComposite( pCrvOffsAct)) ;
if ( IsNull( pCrv))
return false ;
bool bSomeOpen = false ; // flag per presenza di lati aperti
// creo un vettore di Loops della superificie, ordinati
ICRVCOMPOPOVECTOR vCrvLoops ;
for ( int l = 0 ; l < pSrfToWork->GetLoopCount( 0) ; ++ l)
vCrvLoops.emplace_back( ConvertCurveToComposite( pSrfToWork->GetLoop( 0, l))) ;
// creo un vettore di indici. Questi indici si riferiscono alle posizioni di vCrvLoops i quali offset
// hanno generato delle curve sulla pCrv ( curva da cui devo entrare )
INTVECTOR vIndex ;
// scorro tutte le curve presenti in pCrv ( curva da cui devo entrare )
for ( int i = 0 ; i < ( int)pCrv->GetCurveCount() ; ++ i) {
// nProp0 -> #curva il cui Offset ha generato la curva i-esima di pCrv
int nProp0 ; pCrv->GetCurveTempProp( i, nProp0, 0) ;
// nProp1 -> #loop che contiene la curva espressa in nProp0
int nProp1 ; pCrv->GetCurveTempProp( i, nProp1, 1) ;
if ( nProp0 > 0) { // se questa curva non è un "raccordo" di Offset
// controllo per maggiore sicurezza che effettivamente nProp1 sia un indice valido per il vettore dei Loops e
// che nProp0 non sia maggiore del numero di curve del loop nProp1-esimo del vettore dei Loops della pSrfToWork
if ( nProp1 >= 0 && nProp1 < ( int)vCrvLoops.size() && nProp0 < vCrvLoops[nProp1]->GetCurveCount()) {
// aggiorno il vettore di indici ...
if ( find( vIndex.begin(), vIndex.end(), nProp1) == vIndex.end())
vIndex.push_back( nProp1) ;
// aggiorno la proprietà della curva da cui devo entrare
int nTempProp ; vCrvLoops[nProp1]->GetCurveTempProp( nProp0 - 1, nTempProp, 0) ;
pCrv->SetCurveTempProp( i, nTempProp, 0) ;
// se la curva è aperta, aggiorno il Flag
if ( nTempProp == 1 && ! bSomeOpen)
bSomeOpen = true ;
}
else
pCrv->SetCurveTempProp( i, 0, 0) ;
}
else
pCrv->SetCurveTempProp( i, 1, 0) ; // <- tmp prop = 0
}
// cambio il punto iniziale della curva all'inizio del lato aperto più lungo ( se presente)
double dLenMax = EPS_SMALL ;
int nCrvForMax = 0 ;
if ( bSomeOpen) {
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
int nTmpProp = 0 ;
double dLenAct = EPS_SMALL ;
if ( pCrv->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 1 &&
pCrv->GetCurve( u)->GetLength( dLenAct) && dLenAct > dLenMax) {
dLenMax = dLenAct ;
nCrvForMax = u ;
}
}
pCrv->ChangeStartPoint( nCrvForMax) ;
}
// creo un vettore con tutti i Loops della pSwfToWork per i quali, mediante l'Offset, non hanno
// generato alcuna curva presente nella pCrv ( quella da cui devo entrare)
ICRVCOMPOPOVECTOR vOtherCrv ;
for ( int i = 0 ; i < ( int)vCrvLoops.size() ; ++ i) {
bool bOk = true ;
for ( int j = 0 ; j < ( int)vIndex.size() && bOk ; ++ j) {
if ( i == vIndex[j])
bOk = false ;
}
if ( bOk)
vOtherCrv.emplace_back( ConvertCurveToComposite( vCrvLoops[i]->Clone())) ;
}
// cerchiamo un punto valido per l'entrata ...
bMidOut = false ;
if ( bSomeOpen) // se ho dei lati aperti, cerco il più lungo
bMidOut = GetParamOnOpenSide( pCrv, vOtherCrv, ptStart, vtMidOut) ;
if ( bMidOut) { // se ho trovato e valido, allora imposto il punto inziale trovato
const double LEN_OUT = 5 ;
double dPar ; int nFlag ;
bMidOut = ( DistPointCurve( ptStart + LEN_OUT * vtMidOut, *pCrv).GetParamAtMinDistPoint( 0, dPar, nFlag)
&& pCrv->ChangeStartPoint( dPar)) ;
}
if ( ! bMidOut) // alla peggio, ordino i lati lunghi per lunghezza e cerco un'entrata valida
AdjustContourStart( pCrv, vOtherCrv, true) ;
// ora che ho deciso quale sia il punto iniziale, lo imposto effettivamente sulla curva di Offset passata alla funzione
double dUS ;
pCrv->GetStartPoint( ptStart) ;
pCrvOffsAct->GetParamAtPoint( ptStart, dUS) ;
pCrvOffsAct->ChangeStartPoint( dUS) ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs, const int nOffsCount,
const int nIter, const Vector3d& vtN, bool& bInsert)
{
// controllo dei parametri
if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0 || dOffs < EPS_SMALL)
return false ;
bInsert = true ; // di base inserisco
// controllo se richiesta ottimizzazione sul numero di Offsets
if ( ! m_bOptOffset)
return true ;
if ( nIter != 0) { // controllo se sono almeno al secondo offset...
// per ogni curva controllo se il controOffset + 5 * EPS_SMALL genera una regione ...
OffsetCurve OffsCrv ; // considero anche i casi in cui ho bSmallRad
if ( OffsCrv.Make( pCrvOffs, - m_TParams.m_dDiam / 2 + dOffs - 5 * EPS_SMALL , ICurve::OFF_FILLET)) {
// se questa curva sparisce -> non serve inserirla come offset, non svuota parti aggiuntive
if ( OffsCrv.GetCurveCount() == 0) {
bInsert = false ;
return true ;
}
}
else
return true ;
// controllo se richista ottimizzazione per Offsets mediante centroidi e medial Axis
if ( ! m_bOptOffsetCM || OffsCrv.GetCurveCount() > 1)
return true ;
PtrOwner<ICurve> 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<ICurveArc> 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<ICurve> pCrv_i( pCrvPath->Clone()) ;
if ( IsNull( pCrv_i))
return false ;
PtrOwner<ISurfFlatRegion> 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<ICurveComposite> 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<ICurve> 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<ICurve> pCrv_proj( ProjectCurveOnPlane( *pCrv_c, plPock)) ;
double dAreaCheck = EPS_SMALL ; Plane3d plCheck ;
if ( ! IsNull( pCrv_proj) &&
pCrv_proj->IsClosed() &&
pCrv_proj->GetArea( plCheck, dAreaCheck) &&
dAreaCheck <= 50 * EPS_SMALL) {
continue ;
}
SrfChunkDef.AddCurve( Release( pCrv_proj)) ; // aggiungo le curve proiettate
}
// recupero la FlatRegion
PtrOwner<ISurfFlatRegion> 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<ISurfFlatRegion> 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<ICurve> 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<ICurveComposite> 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<ICurveComposite> 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<ICRVCOMPOPOVECTOR>& vCrvOEWithFlags, int nStep, ISurfFlatRegion* pSrfChunkFinal,
int& nIndex)
{
// controllo dei parametri
if ( int( vCrvOEWithFlags.size()) == 0 || pSrfChunkFinal == nullptr || ! pSrfChunkFinal->IsValid())
return false ;
// cerco la curva originale del chunk (cc)-esimo ( per casi ottimizzati)
if (( int)vCrvOEWithFlags[nStep-1].size() == 1)
nIndex = 0 ;
else {
for ( int k = 0 ; k < ( int)vCrvOEWithFlags[nStep-1].size() ; ++k) {
CRVCVECTOR ccClass ;
if ( pSrfChunkFinal->GetCurveClassification( *vCrvOEWithFlags[nStep-1][k], EPS_SMALL, ccClass)) {
bool bIsThis = true ;
for ( int kk = 0 ; kk < ( int)ccClass.size() && bIsThis ; ++kk) {
if ( ccClass[kk].nClass == CRVC_OUT)
bIsThis = false ;
}
if ( bIsThis) {
nIndex = k ;
break ;
}
}
}
}
return true ;
}
//----------------------------------------------------------------------------
//---------------------- CASI OTTIMIZZATI ------------------------------------
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
bool
Pocketing::GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTrap,
Frame3d& frTrap, double& dPocketSize, int& nBase, int& nSecondBase)
{
// controllo parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// diametro reale da tenere in considerazione
double dDiam = m_TParams.m_dDiam + GetOffsR() ;
pCrvTrap->Clear() ; // resterà vuota se il caso non è ottimizzato
nBase = -1 ;
nSecondBase = -1 ;
// se la curva è già un trapezio, non sempre devo adattarla...
Point3d pt ; Vector3d vtDir, vtB2, vtOtherDir ;
if ( pCrvCompo->IsATrapezoid( 100 * EPS_SMALL, pt, vtDir, vtOtherDir, vtB2)) {
pCrvTrap->AddCurve( pCrvCompo->Clone()) ;
// se parallelogramma scelgo come base i lati lunghi
Vector3d vtL2( - vtDir + vtOtherDir + vtB2) ;
if ( AreSameOrOppositeVectorApprox( vtOtherDir, vtL2)) {
if ( vtOtherDir.Len() > vtDir.Len())
swap( vtDir, vtOtherDir) ;
}
vtDir.Normalize() ;
Vector3d vtOrtho = OrthoCompo( vtOtherDir, vtDir) ;
dPocketSize = vtOrtho.Len() ;
// eventuale approssimazione della curva con polyline per ottenere la stessa curva calcolata in ICurveComposite::IsATrapezoid
if ( pCrvCompo->GetCurveCount() > 4) {
PolyLine PL ;
if ( ! pCrvCompo->ApproxWithLines( 100 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL))
return false ;
pCrvTrap->FromPolyLine( PL) ;
}
if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap))
return false ;
bool bBaseOpen = false ;
bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == 1 || pCrvTrap->GetCurve( 2)->GetTempProp( 0) == 1) ;
// controllo dimensioni della svuotatura
if ( ! ( bBaseOpen && dPocketSize < dDiam + EPS_SMALL) &&
abs( dPocketSize - dDiam) > EPS_SMALL) {
pCrvTrap->Clear() ;
return true ;
}
// recupero flag aperto/chiuso dei lati
if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == 0 && pCrvTrap->GetCurve( 3)->GetTempProp( 0) == 0) {
double dLen0, dLen2 ;
pCrvTrap->GetCurve( 0)->GetLength( dLen0) ;
pCrvTrap->GetCurve( 2)->GetLength( dLen2) ;
if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL)
pCrvTrap->Clear() ;
}
// imposto le basi
nBase = 0 ;
nSecondBase = 2 ;
return true ;
}
// controllo il numero di lati chiusi e salvo i loro indici
int nClosedSide = 0 ;
INTVECTOR vIndClosedSides ;
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
int nTmpProp ;
if ( pCrvCompo->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 0) {
++nClosedSide ;
vIndClosedSides.push_back( u) ;
}
}
// clono la curva Compo
PtrOwner<ICurveComposite> pCrvCompo_c( CloneCurveComposite( pCrvCompo)) ;
if ( IsNull( pCrvCompo_c))
return false ;
// tolleranza
const double TOLL = 50 * EPS_SMALL ;
// se tutti lati aperti
if ( nClosedSide == 0) {
// ricavo il box minimo della curva aperta ( passo dalla polyLine)
PolyLine PL ; Point3d ptCen ; double dWidth ;
if ( ! pCrvCompo->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) ||
! PL.GetMinAreaRectangleXY( ptCen, vtDir, dWidth, dPocketSize))
return false ;
// controllo dimY ( dHeight), se troppo estesa, non è un caso ottimizzato
if ( dPocketSize > dDiam + TOLL)
return true ;
// inverto il frame attuale
frTrap.Set( ptCen, Z_AX, vtDir) ;
if ( ! frTrap.IsValid())
return false ;
// creo il rettangolo del Box
pCrvTrap->AddPoint( Point3d( - 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
pCrvTrap->AddLine( Point3d( - 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
pCrvTrap->Close() ;
pCrvTrap->ToGlob( frTrap) ;
Point3d ptNewOrig ; pCrvTrap->GetStartPoint( ptNewOrig) ;
frTrap.Set( ptNewOrig, Z_AX, vtDir) ;
// imposto tutte le 4 curve come aperte
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
// imposto le basi
nBase = 0 ;
nSecondBase = 2 ;
}
// se un lato chiuso
else if ( nClosedSide == 1) {
// prendo l'unica curva chiusa
const ICurve* pCrvCurr = pCrvCompo->GetCurve( vIndClosedSides[0]) ;
if ( pCrvCurr == nullptr)
return false ;
// controllo se lineare ( altrimenti non è ottimizzato)
if ( pCrvCurr->GetType() != CRV_LINE)
return true ;
// prendo la direzione del tratto lineare
pCrvCurr->GetStartDir( vtDir) ;
// prendo il punto iniziale
Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ;
// creo il riferimento basato su questo tratto
frTrap.Set( ptStart, Z_AX, vtDir) ;
if ( ! frTrap.IsValid())
return false ;
// porto la curva Compo ( clonandola) nel frame
pCrvCompo_c->ToLoc( frTrap) ;
// ricavo il box complessivo in questo frame
BBox3d BBox ;
pCrvCompo_c->GetLocalBBox( BBox) ;
// controllo dimY ( se troppo grande o lato chiuso non sul bordo del box => caso non ottimizzato)
if (( BBox.GetDimY() < dDiam + TOLL || BBox.GetDimX() < dDiam + TOLL) && BBox.GetMin().y > - TOLL) {
// creo il rettangolo del Box
pCrvTrap->AddPoint( BBox.GetMin()) ;
pCrvTrap->AddLine( BBox.GetMin() + BBox.GetDimX() * X_AX) ;
pCrvTrap->AddLine( BBox.GetMax()) ;
pCrvTrap->AddLine( BBox.GetMax() - BBox.GetDimX() * X_AX) ;
pCrvTrap->Close() ;
// porto in globale
pCrvTrap->ToGlob( frTrap) ;
// imposto tutte le curve come aperte tranne la prima ( estendo il solo lato chiuso come base del Box)
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, u == 0 ? 0 : 1, 0) ;
// memorizzo la dimensione Y
dPocketSize = BBox.GetDimY() ;
// se il lato chiuso non può essere la base, allora sarà l'aperto ( del box)
if ( BBox.GetDimY() > dDiam + TOLL) {
dPocketSize = BBox.GetDimX() ;
pCrvTrap->ChangeStartPoint( 1.) ;
Point3d ptORIG ; pCrvTrap->GetStartPoint( ptORIG) ;
pCrvTrap->GetStartDir( vtDir) ;
frTrap.Set( ptORIG, Z_AX, vtDir) ;
}
}
// imposto le basi
nBase = 0 ;
nSecondBase = 2 ;
}
else {
// cerco la base
// Def di base :
// lato lineare chiuso, box nella sua direzione con DimY < 1.5 dDiam, box dei restanti chiusi ( almeno 1)
// con dimY < dDiam e lato lineare come lato del box ( è possibile trovare una seconda base, come altro lato chiuso
// parallelo alla base principale con distanza da essa circa il dDiam)
bool bBaseFound = false ; // flag per individuare base principale
BBox3d BBox ; // Box per base principale
// scorro i chiusi alla ricerca di una possibile base principale
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() && ! bBaseFound ; ++ i) {
nBase = i ; // aggiorno l'indice
// prendo la curva
const ICurve* pCrvCurr = pCrvCompo->GetCurve( i) ;
if ( pCrvCurr == nullptr)
return false ;
// controllo se lineare, altrimenti passo alla successiva
if ( pCrvCurr->GetType() != CRV_LINE)
continue ;
// prendo la direzione del tratto lineare
pCrvCurr->GetStartDir( vtDir) ;
// prendo il punto iniziale
Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ;
// creo il riferimento basato su questo tratto
frTrap.Set( ptStart, Z_AX, vtDir) ;
if ( ! frTrap.IsValid())
return false ;
// porto la curva Compo ( clonandola) nel frame
pCrvCompo_c.Set( pCrvCompo->Clone()) ;
pCrvCompo_c->ToLoc( frTrap) ;
// calcolo il box
pCrvCompo_c->GetLocalBBox( BBox) ;
// controllo dimY e dimX per il box
bool bDimYOk = BBox.GetDimY() < dDiam + TOLL && BBox.GetMin().y > - TOLL ;
bool bDimXOk = BBox.GetDimX() < dDiam + TOLL && BBox.GetMin().y > - TOLL ;
// se entrambe le dimensioni superano il diametro, questo lato non è la base, cerco il successivo
if ( !bDimXOk && !bDimYOk)
continue ;
// se dimY accettabile
if ( bDimYOk) {
bool bOk = false ;
if ( ! CheckTrapezoidClosedEdgePosition( pCrvCompo_c, i, BBox.GetDimY(), vIndClosedSides, bOk, pCrvTrap))
return false ;
if ( bOk) {
// pCrvTrap contiene il trapezio
nBase = 0 ;
nSecondBase = 2 ;
dPocketSize = BBox.GetDimY() ;
pCrvTrap->ToGlob( frTrap) ;
bBaseFound = true ;
}
else {
// cerco la seconda base
if ( ! CheckSecondBaseTrapezoid( pCrvCompo_c, i, nSecondBase))
return false ;
if ( nSecondBase != -1) {
// verifico che una delle due sia chiusa ( TO DO)
if ( pCrvCompo_c->GetCurve( nBase)->GetTempProp( 0) == 0 ||
pCrvCompo_c->GetCurve( nSecondBase)->GetTempProp( 0) == 0) {
// se ho trovato la seconda base
if ( ! PreparareTrapezoidTwoBases( pCrvCompo_c, BBox, i, nSecondBase, bOk, pCrvTrap))
return false ;
if ( bOk) {
// pCrvTrap contiene il trapezio
nBase = 0 ;
dPocketSize = BBox.GetDimY() ;
pCrvTrap->ToGlob( frTrap) ;
bBaseFound = true ;
}
}
}
}
}
// se dimX accettabile
if ( bDimYOk) {
// TODO :
}
}
// se non ho trovato una base principale => non è ottimizzato
if ( ! bBaseFound)
return true ;
}
// la curva a trapezio deve evere 4 lati
if ( pCrvTrap->GetCurveCount() != 4) {
pCrvTrap->Clear() ;
return true ;
}
// se parametro MaxOptSize non compatibile => non è ottimizzato
double dMaxOptSize ;
FromString( ExtractInfo( m_Params.m_sUserNotes, "MaxOptSize="), dMaxOptSize) ;
if ( FromString( ExtractInfo( m_Params.m_sUserNotes, "MaxOptSize="), dMaxOptSize) && dPocketSize > dMaxOptSize)
pCrvTrap->Clear() ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::CheckTrapezoidClosedEdgePosition( const ICurveComposite* pCrvCompo, const int nCrvInd, const double dDimBoxY,
INTVECTOR& vIndClosedSides, bool& bOk, ICurveComposite* pCrvTrap)
{
// controllo dei parametri
if ( pCrvCompo == nullptr)
return false ;
bOk = false ;
// controllo che ci siano due o tre lati chiusi
int nClosedEdge = int( vIndClosedSides.size()) ;
if ( nClosedEdge != 3)
return true ;
// TO DO se 2 chiusi consecutivi + gesitone archi
// prendo i 3 lati chiusi
const ICurve* pCrv0 = pCrvCompo->GetCurve( vIndClosedSides[0]) ;
const ICurve* pCrv1 = pCrvCompo->GetCurve( vIndClosedSides[1]) ;
const ICurve* pCrv2 = pCrvCompo->GetCurve( vIndClosedSides[2]) ;
if ( pCrv0 == nullptr || pCrv1 == nullptr || pCrv2 == nullptr)
return false ;
// controllo che siano lineari
if ( pCrv0->GetType() != CRV_LINE || pCrv1->GetType() != CRV_LINE || pCrv2->GetType() != CRV_LINE)
return true ;
// prendo i punti iniziali e finali
Point3d ptStart0 ; pCrv0->GetStartPoint( ptStart0) ;
Point3d ptEnd0 ; pCrv0->GetEndPoint( ptEnd0) ;
Point3d ptStart1 ; pCrv1->GetStartPoint( ptStart1) ;
Point3d ptEnd1 ; pCrv1->GetEndPoint( ptEnd1) ;
Point3d ptStart2 ; pCrv2->GetStartPoint( ptStart2) ;
Point3d ptEnd2 ; pCrv2->GetEndPoint( ptEnd2) ;
// controllo che siano consecutivi e cambio l'ordine se necessario
if ( AreSamePointApprox( ptEnd0, ptStart1) && AreSamePointApprox( ptEnd1, ptStart2))
;
else if ( AreSamePointApprox( ptEnd1, ptStart2) && AreSamePointApprox( ptEnd2, ptStart0)) {
swap( vIndClosedSides[0], vIndClosedSides[1]) ;
swap( vIndClosedSides[1], vIndClosedSides[2]) ;
}
else if ( AreSamePointApprox( ptEnd2, ptStart0) && AreSamePointApprox( ptEnd0, ptStart1)) {
swap( vIndClosedSides[0], vIndClosedSides[2]) ;
swap( vIndClosedSides[2], vIndClosedSides[1]) ;
}
else // se non consecutivi
return true ;
// se sono consecuti, ma la base non è il lato centrale, esco ( la ritroverò successivamente)
if ( vIndClosedSides[1] != nCrvInd)
return true ;
// parametri lavorazione
double dDiam = m_TParams.m_dDiam ;
double dOffsR = GetOffsR() ;
double dRad = 0.5 * dDiam + dOffsR ;
// -------------- creo il bordo del trapezio estendendo i lati obliqui se possibile ------------------------
// estendo
PtrOwner<ICurve> pEdge0( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
PtrOwner<ICurve> 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<ICurveComposite> 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<ICurve> pCrvOffs( OffsCrv.GetLongerCurve()) ;
if ( IsNull( pCrvOffs))
return false ;
IntersCurveCurve IntCC( *pCrvOffs, *pCrvCompo) ;
CRVCVECTOR ccClass ;
// se c'è intersezione, esco
if ( IntCC.GetRegionCurveClassification() != CCREGC_IN2)
return true ;
// costruisco il trapezio
pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartPoint( ptS0) ;
pCrvTrap->AddPoint( ptS0) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[2])->Clone()) ;
pCrvTrap->Close() ;
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ;
pCrvTrap->ChangeStartPoint( 1.) ; // la base è sempre la curva 0
bOk = true ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::CheckSecondBaseTrapezoid( const ICurveComposite* pCrvCompo, const int nCrvInd, int& nSecondBase)
{
// controllo parametri
if ( pCrvCompo == nullptr)
return false ;
// parametri lavorazione
double dDiam = m_TParams.m_dDiam ;
double dOffsR = GetOffsR() ;
double dRad = 0.5 * dDiam + dOffsR ;
nSecondBase = -1 ;
// scorro le altre curve lineari chiuse
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() && nSecondBase == -1 ; ++ u) {
if ( u == nCrvInd || pCrvCompo->GetCurve( u)->GetType() != CRV_LINE)
continue ;
// recupero la direzione del tratto
Vector3d vtCurr_dir ; pCrvCompo->GetCurve( u)->GetStartDir( vtCurr_dir) ;
// direzioni parallele ma opposte
if ( AreOppositeVectorApprox( vtCurr_dir, X_AX)) {
// se le direzioni sono compatibili, controllo che la distanza sia ammissibile
const ICurve* pCrvCurr = pCrvCompo->GetCurve( nCrvInd) ;
Point3d ptS0, ptS1 ;
pCrvCurr->GetStartPoint( ptS0) ;
pCrvCompo->GetCurve( u)->GetStartPoint( ptS1) ;
if ( abs( ptS1.y - dDiam) < TOLL_TRAPEZOID)
nSecondBase = u ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::PreparareTrapezoidTwoBases( const ICurveComposite* pCrvCompo, BBox3d BBox, const int nBase, int& nSecondBase,
bool& bOk, ICurveComposite* pCrvTrap)
{
// controllo parametri
if ( pCrvCompo == nullptr)
return false ;
bOk = false ;
// le parti tra le due basi devono essere tutte omogenee ( o tutte aperte o tutte chiuse)
// pCrvTest0 sarà la parte destra, pCrvTest1 la parte sinistra
Point3d ptSB0, ptSB1 ;
PtrOwner<ICurveComposite> pCrvTest0( CloneCurveComposite( pCrvCompo)) ;
PtrOwner<ICurveComposite> pCrvTest1( CloneCurveComposite( pCrvCompo)) ;
pCrvCompo->GetCurve( nBase)->GetStartPoint( ptSB0) ;
pCrvCompo->GetCurve( nSecondBase)->GetStartPoint( ptSB1) ;
pCrvTest0->ChangeStartPoint( nBase) ;
pCrvTest1->ChangeStartPoint( nSecondBase) ;
double dUTrim0, dUTrim1 ;
pCrvTest0->GetParamAtPoint( ptSB1, dUTrim0) ;
pCrvTest1->GetParamAtPoint( ptSB0, dUTrim1) ;
pCrvTest0->TrimStartEndAtParam( 1, dUTrim0) ;
pCrvTest1->TrimStartEndAtParam( 1, dUTrim1) ;
// controllo che la parte destra si uniforme per le TmpProp
for ( int u = 0 ; u < pCrvTest0->GetCurveCount() - 1 ; ++ u) {
int nPropAct, nPropSucc ;
if ( pCrvTest0->GetCurveTempProp( u, nPropAct, 0) &&
pCrvTest0->GetCurveTempProp( u + 1, nPropSucc, 0) &&
nPropAct != nPropSucc)
return true ; // se TmpProp diverse => non è un caso ottimizzato
}
// controllo che la parte sinistra sia uniforme per le TmpProp
for ( int u = 0 ; u < pCrvTest1->GetCurveCount() - 1 ; ++ u) {
int nPropAct, nPropSucc ;
if ( pCrvTest1->GetCurveTempProp( u, nPropAct, 0) &&
pCrvTest1->GetCurveTempProp( u + 1, nPropSucc, 0) &&
nPropAct != nPropSucc)
return true ; // se TmpProp diverse => non è un caso ottimizzato
}
// parametri lavorazione
double dDiam = m_TParams.m_dDiam ;
double dOffsR = GetOffsR() ;
double dRad = 0.5 * dDiam + dOffsR ;
// creo la base principale e secondaria
PtrOwner<ICurve> pCrvBase0( pCrvCompo->GetCurve( nBase)->Clone()) ;
PtrOwner<ICurve> pCrvBase1( pCrvCompo->GetCurve( nSecondBase)->Clone()) ;
// ricavo i nuovi estremi
Point3d ptEB0, ptEB1 ;
pCrvBase0->GetStartPoint( ptSB0) ;
pCrvBase0->GetEndPoint( ptEB0) ;
pCrvBase1->GetStartPoint( ptSB1) ;
pCrvBase1->GetEndPoint( ptEB1) ;
// se lato destro aperto ( estendo il punto finale della base principale e il punto iniziale
// della base secondaria fino al lato destro del box)
bool bCopyRight = false ;
if ( pCrvTest0->GetCurve( 0)->GetTempProp( 0) == 1) {
ptEB0 = BBox.GetMin() + X_AX * BBox.GetDimX() ;
ptSB1 = ptEB0 + dDiam * Y_AX ;
}
// se lato destro chiuso
else
bCopyRight = true ;
// se lato sinistro aperto ( estendo il punto finale della base secondaria e il punto iniziale
// della base primaria dino al lato sinistro del box)
bool bCopyLeft = false ;
if ( pCrvTest1->GetCurve( 0)->GetTempProp( 0) == 1) {
ptSB0 = BBox.GetMin() ;
ptEB1 = BBox.GetMin() + dDiam * Y_AX ;
}
// se lato sinistro chiuso
else
bCopyLeft = true ;
// creo la curva da restituire
pCrvTrap->AddPoint( ptSB0) ;
pCrvTrap->AddLine( ptEB0) ;
if ( bCopyRight)
pCrvTrap->AddCurve( Release( pCrvTest0)) ;
else {
pCrvTrap->AddLine( ptSB1) ;
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta
}
nSecondBase = pCrvTrap->GetCurveCount() ;
pCrvTrap->AddLine( ptEB1) ;
if ( bCopyLeft)
pCrvTrap->AddCurve( Release( pCrvTest1)) ;
else {
pCrvTrap->Close() ;
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta
}
// verifico dimensione x della svuotatura nel caso tutto chiuso
if ( bCopyLeft && bCopyRight) {
double dLen0 = 0, dLen1 = 0 ;
pCrvBase0->GetLength( dLen0) ;
pCrvBase1->GetLength( dLen1) ;
if ( dLen0 < dDiam - EPS_SMALL || dLen1 < dDiam - EPS_SMALL) {
pCrvTrap->Clear() ;
return true ;
}
}
bOk = true ;
return true ;
}
//----------------------------------------------------
bool
Pocketing::SpecialAdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const bool bEvenClosed, const ICurveComposite* pCrvPocket)
{
// parametri
double dDiam = m_dDiam_Prec > 0 ? m_dDiam_Prec : m_TParams.m_dDiam ;
double dOffsR = m_dDiam_Prec > 0 ? m_dOffsetR_Prec : GetOffsR() ;
double dRad = 0.5 * dDiam + dOffsR ;
// calcolo gli offset dei lati obliqui
PtrOwner<ICurveLine> pLineS( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 0 : 3)->Clone())) ;
pLineS->SimpleOffset( - dRad) ;
pLineS->Invert() ;
PtrOwner<ICurveLine> 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<ICurveLine> 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<ICurve> pBiArc1( GetBiArc( ptS, - dAng, ptCrv1, bEvenClosed ? 90 : 0, 0.5)) ;
// secondo raccordo
vtE.Invert() ;
vtE.GetAngleXY( X_AX, dAng) ;
PtrOwner<ICurve> 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<ICurveArc> 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<ISurfFlatRegion> 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<ICurveArc> pArc2( CreateCurveArc()) ;
if ( IsNull( pArc2) || ! pArc2->Set2PVN( ptStart, ptEnd, - vtStart, vtN)) {
m_pMchMgr->SetLastError( 2420, "Error in Pocketing : Return toolpath not computable") ;
return false ;
}
pRCrv->AddCurve( Release( pArc2)) ;
// inverto e eventualmente sistemo archi
pRCrv->Invert() ;
// setto la feed per il percorso di ritorno
AssignFeedForReturnPath( pRCrv) ;
VerifyArcs( pRCrv) ;
}
return true ;
}
//----------------------------------------------------
bool
Pocketing::CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double dPocketSize, int nBase,
int nSecondBase, ICurveComposite* pMCrv, ICurveComposite* pRCrv, bool& bOptimizedTrap)
{
// parametri
double dDiam = m_TParams.m_dDiam ;
double dOffsR = GetOffsR() ;
double dRad = 0.5 * dDiam + dOffsR ;
bOptimizedTrap = false ;
Vector3d vtExtr ; pCrvPocket->GetExtrusion( vtExtr) ;
// recupero le temp prop
INTVECTOR vnProp( 4, 0) ;
if ( pCrvPocket->GetCurveCount() == 4) {
for ( int i = 0 ; i < 4 ; i++)
pCrvPocket->GetCurveTempProp( i, vnProp[i]) ;
}
else {
vnProp[1] = pCrvPocket->GetCurve( 1)->GetTempProp() ;
vnProp[3] = pCrvPocket->GetCurve( nSecondBase + 1)->GetTempProp() ;
}
// passo in un sistema di riferimento locale avente asse X allineato con uno dei due lati paralleli (possibilmente aperto) e centro nel
// punto iniziale del lato
pCrvPocket->ToLoc( frTrap) ;
// calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso)
double dLen0, dLen1, dLen2, dLen3, dMaxLarg = 0. ;
pCrvPocket->GetCurve( nBase)->GetLength( dLen0) ;
pCrvPocket->GetCurve( nSecondBase)->GetLength( dLen2) ;
bool bRealTrap = pCrvPocket->GetCurveCount() == 4 ;
for ( int i = 0 ; i < 4 && bRealTrap ; ++ i)
bRealTrap = pCrvPocket->GetCurve( i)->GetType() == CRV_LINE ;
if ( bRealTrap) {
pCrvPocket->GetCurve( nBase + 1)->GetLength( dLen1) ;
pCrvPocket->GetCurve( nSecondBase + 1)->GetLength( dLen3) ;
// calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso)
Point3d ptOrig ; pCrvPocket->GetCurve( 1)->GetStartPoint( ptOrig) ;
Vector3d vtX ; pCrvPocket->GetCurve( 1)->GetStartDir( vtX) ;
Frame3d frDim ; frDim.Set( ptOrig, Z_AX, vtX) ; frDim.Invert() ;
BBox3d b3Dim ; pCrvPocket->GetBBox( frDim, b3Dim, BBF_EXACT) ;
dMaxLarg = ( vnProp[0] != 0 ? b3Dim.GetDimY() : dPocketSize) ;
}
// calcolo percorso di svuotatura
// se lati obliqui sono entrambi chiusi e dimensione svuotatura è maggiore di diametro fresa e minore del doppio gestione speciale
if ( bRealTrap && dMaxLarg > m_TParams.m_dDiam + 10 * EPS_SMALL &&
(( vnProp[0] != 0 && vnProp[2] != 0 && vnProp[3] == 0 && vnProp[1] == 0 && max( dLen0, dLen2) < 2 * dDiam + EPS_SMALL) ||
vnProp[1] != 0 && vnProp[3] != 0 && vnProp[0] == 0 && vnProp[2] == 0 && max( dLen1, dLen3) < 2 * dDiam + EPS_SMALL)) {
if ( ! SpecialAdjustTrapezoidSpiralForAngles( pMCrv, vnProp[0] == 0, pCrvPocket)) {
pMCrv->Clear() ;
return false ;
}
}
else {
// trovo la quota Y per centro del Tool
double dYCoord ;
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) // se base principale chiusa
dYCoord = 0.5 * dDiam + dOffsR ;
else if ( pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 0) // se base principale aperta e secondaria chiusa
dYCoord = dPocketSize - 0.5 * dDiam - dOffsR ;
else // se entrambi i lati paralleli sono aperti mi posiziono a metà della svuotatura
dYCoord = 0.5 * dPocketSize ;
double dXCoordStart, dXCoordEnd ;
if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, true, dYCoord, dXCoordStart, dPocketSize))
return false ;
if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, false, dYCoord, dXCoordEnd, dPocketSize))
return false ;
if ( dXCoordStart > dXCoordEnd + 500 * EPS_SMALL)
return false ;
Point3d ptStart( dXCoordStart, dYCoord) ;
Point3d ptEnd( dXCoordEnd, dYCoord) ;
if ( bRealTrap && AreSamePointEpsilon( ptStart, ptEnd, 500 * EPS_SMALL) && ( vnProp[0] != 0 || vnProp[2] != 0)) {
Vector3d vtDir1, vtDir3 ;
pCrvPocket->GetCurve( 1)->GetStartDir( vtDir1) ;
pCrvPocket->GetCurve( 3)->GetStartDir( vtDir3) ;
// gestisco il caso speciale di un parallelogramma in cui anche l'altra dimensione della svuotatura è pari al diametro utensile
if ( AreOppositeVectorApprox( vtDir1, vtDir3)) {
PtrOwner<ICurveLine> pLine1( GetCurveLine( pCrvPocket->GetCurve( 1)->Clone())) ;
PtrOwner<ICurveLine> pLine3( GetCurveLine( pCrvPocket->GetCurve( 3)->Clone())) ;
if ( IsNull( pLine1) || IsNull( pLine3))
return false ;
if ( ! pLine1->SimpleOffset( - 0.5 * m_TParams.m_dDiam - GetOffsR()) ||
! pLine3->SimpleOffset( - 0.5 * m_TParams.m_dDiam - GetOffsR()))
return false ;
Point3d ptS, ptE ;
if ( vtDir3 * X_AX > EPS_SMALL) {
pLine1->GetStartPoint( ptS) ;
pLine3->GetStartPoint( ptE) ;
}
else {
pLine1->GetEndPoint( ptE) ;
pLine3->GetEndPoint( ptS) ;
}
if ( vnProp[0] != 0) {
pMCrv->AddPoint( ptS) ;
if ( vnProp[2] != 0)
pMCrv->AddLine( ptE) ;
else
pMCrv->AddLine( ptStart) ;
}
else {
pMCrv->AddPoint( ptE) ;
if ( vnProp[0] != 0)
pMCrv->AddLine( ptS) ;
else
pMCrv->AddLine( ptStart) ;
pMCrv->Invert() ;
}
pMCrv->SetCurveTempProp( 0, 1) ;
}
}
else {
if ( ! pMCrv->AddPoint( ptStart))
return true ;
if ( ! pMCrv->AddLine( ptEnd))
return true ;
// aggiustamenti al percorso per rimuovere materiale residuo negli angoli
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 1 ||
pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 1) {
Frame3d frOpen ;
bool bSwitch = false ;
// Base principale chiusa e base Secondaria aperta
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) {
pCrvPocket->ChangeStartPoint( nSecondBase) ;
// oriento il Frame ( ho sempre l'origine nell'estremo superiore del lato aperto)
Vector3d vtDir ; pCrvPocket->GetStartDir( vtDir) ;
Point3d ptORIG ;
if ( vtDir.y > 0)
pCrvPocket->GetCurve( nBase)->GetEndPoint( ptORIG) ;
else
pCrvPocket->GetCurve( nBase)->GetStartPoint( ptORIG) ;
frOpen.Set( ptORIG, Z_AX, -X_AX) ;
if ( ! frOpen.IsValid())
return false ;
pCrvPocket->ToLoc( frOpen) ;
pMCrv->ToLoc( frOpen) ;
pMCrv->Invert() ;
bSwitch = true ;
}
if ( pCrvPocket->GetCurve( 3)->GetTempProp( 0) == 0 &&
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, true)) {
pMCrv->Clear() ;
return false ;
}
if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == 0 &&
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, false)) {
pMCrv->Clear() ;
return false ;
}
if ( bSwitch) {
pCrvPocket->ToGlob( frOpen) ;
pMCrv->ToGlob( frOpen) ;
pMCrv->Invert() ;
}
}
}
}
if ( pMCrv->GetCurveCount() == 0)
return true ;
pMCrv->ToGlob( frTrap) ;
if ( ! m_Params.m_bInvert) {
pMCrv->Invert() ;
// inverto le proprietà in modo che nProp3 sia sempre legata al punto iniziale e nProp1 a quello finale
swap( vnProp[1], vnProp[3]) ;
}
// segno i lati aperti come temp prop della curva
int nOpenEdges = vnProp[0] + vnProp[1] * 2 + vnProp[3] * 8 ;
pMCrv->SetTempProp( nOpenEdges, 0) ;
pMCrv->SetExtrusion( vtExtr) ;
bOptimizedTrap = true ;
return true ;
}
//----------------------------------------------------
bool
Pocketing::CalcTrapezoidSpiralLocalFrame( ICurveComposite* pCrvTrap, const Vector3d& vtDir, Frame3d& frLoc)
{
// cerco i lati paralleli a vtDir
int nBaseId = -1 ;
for ( int i = 0 ; i < pCrvTrap->GetCurveCount() ; i ++) {
Vector3d vtEdge ;
pCrvTrap->GetCurve( i)->GetStartDir( vtEdge) ;
if ( AreSameOrOppositeVectorApprox( vtEdge, vtDir)) {
nBaseId = i ;
break ;
}
}
if ( nBaseId != 0 && nBaseId != 1)
return false ;
// imposto come lato iniziale per la curva uno dei lati paralleli a vtDir
pCrvTrap->ChangeStartPoint( nBaseId) ;
Point3d ptOrig ; pCrvTrap->GetStartPoint( ptOrig) ;
Vector3d vtX ; pCrvTrap->GetStartDir( vtX) ;
return frLoc.Set( ptOrig, Z_AX, vtX) ;
}
//------------------------------------------------------
bool
Pocketing::CalcTrapezoidSpiralXCoord( const ICurveComposite* pCrvPocket, int nBase, int nSecondBase,
bool bStart, double dYCoord, double& dXCoord, double dPocketSize)
{
// parametri
double dDiam = m_dDiam_Prec > 0 ? m_dDiam_Prec : m_TParams.m_dDiam ;
double dOffsR = m_dDiam_Prec > 0 ? m_dOffsetR_Prec : GetOffsR() ;
double dRad = 0.5 * dDiam + dOffsR ;
const double TOLL = 50 * EPS_SMALL ;
// recupero la curva di interesse
int nCrvId = ( bStart ? nSecondBase : nBase) + 1 ;
int nProp = - 1 ;
if ( ! pCrvPocket->GetCurveTempProp( nCrvId, nProp))
return false ;
// se open
if ( nProp == 1) {
Point3d pt1, pt2 ;
pCrvPocket->GetCurve( nCrvId)->GetStartPoint( pt1) ;
pCrvPocket->GetCurve( nCrvId)->GetEndPoint( pt2) ;
if ( bStart)
dXCoord = min( pt1.x, pt2.x) ;
else
dXCoord = max( pt1.x, pt2.x) ;
}
// se closed
else {
// creo la curva destra/sinistra
int nLast = bStart ? pCrvPocket->GetCurveCount() : nSecondBase ;
PtrOwner<ICurveComposite> 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<ICurve> pCrvOffs( OffsCrv.GetLongerCurve()) ;
if ( IsNull( pCrvOffs))
return false ;
PtrOwner<ICurveLine> 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<ICurveLine> pCrvVertLine( CreateCurveLine()) ;
if ( IsNull( pCrvVertLine))
return false ;
if ( bStart)
pCrvVertLine->SetPDL( Box3d.GetMax() + 5 * TOLL * Y_AX, - 90 , 2 * dPocketSize) ;
else
pCrvVertLine->SetPDL( Box3d.GetMin() - 5 * TOLL * Y_AX, 90, 2 * dPocketSize) ;
// intersechiamo
IntersCurveCurve intCC2( *pCrvVertLine, *pCrvSide) ;
if ( intCC2.GetOverlaps()) {
if ( bStart)
dXCoord = Box3d.GetMax().x + dRad ;
else
dXCoord = Box3d.GetMin().x - dRad ;
}
else {
dXCoord = bStart ? -INFINITO : INFINITO ;
for ( int i = 0 ; i < intCC2.GetIntersCount() ; ++ i) {
IntCrvCrvInfo ccClass2 ;
if ( intCC2.GetIntCrvCrvInfo( i, ccClass2)) {
if ( bStart)
dXCoord = max( dXCoord, Box3d.GetMax().x + sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ;
else
dXCoord = min( dXCoord, Box3d.GetMin().x - sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ;
}
}
}
}
}
else
return false ;
}
return true ;
}
//----------------------------------------------------
bool
Pocketing::AdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const ICurveComposite* pCrvPocket,
bool bStart)
{
// parametri
double dDiam = m_dDiam_Prec > 0 ? m_dDiam_Prec : m_TParams.m_dDiam ;
double dOffsR = m_dDiam_Prec > 0 ? m_dOffsetR_Prec : GetOffsR() ;
double dRad = 0.5 * dDiam + dOffsR ;
const double TOLL = 50 * EPS_SMALL ;
PtrOwner<ICurveComposite> 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<ICurveLine> 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<ICurve> 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, FEED_DIVISOR * GetMinFeed(), 0) ;
else // semi cerchi in tangenza
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ;
}
}
else if ( nOptType == 1) { // Trapezoidi
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ;
}
break ;
/* NB. Essendo la funzione CalcSpiral richiamata sia per lo SpiralIN che per lo SpiralOUT le curve sono sempre
orientate nello stesso modo, solamente alla fine viene invertita la curva finale per la svuotatura... */
case POCKET_SUB_SPIRALOUT :
if ( nOptType == 0) { // Spiral verso l'esterno
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
if ( u > pCrv->GetCurveCount() - 3 ) // prime semi circonferenze
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ;
else
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ;
}
}
else if ( nOptType == 1) { // Trapezoidi
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ;
}
break ;
default :
break ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AssignFeedZigZagOneWay( ICurveComposite* pCompo, const bool bIsLink, const ICURVEPOVECTOR& vLAbove,
const ICURVEPOVECTOR& vLUnder, const ICRVCOMPOPOVECTOR& vAddedLinks)
{
// controllo che la pCompoLine sia effettivamente una linea valida
if ( pCompo == nullptr || ! pCompo->IsValid())
return false ;
// controllo se il Flag per assegnare la Feed è attivo
if ( !m_bAssignFeed)
return AssignDefaultFeed( pCompo) ;
// inzialmente setto la feed Minima alla curva
for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u)
pCompo->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ;
// se è un link tra livelli diversi, allora esco ( con Feed Minima )
if ( bIsLink) {
Point3d ptS_link ; pCompo->GetStartPoint( ptS_link) ;
Point3d ptE_link ; pCompo->GetEndPoint( ptE_link) ;
if ( abs( ptE_link.y - ptS_link.y) > 500 * EPS_SMALL)
return true ;
}
// se la curva è piccola, allora esco ( con Feed Minima)
double dLen = EPS_SMALL ;
if ( ! pCompo->GetLength( dLen) || dLen < 0.6 * m_TParams.m_dDiam)
return true ;
Point3d ptS, ptE ;
pCompo->GetStartPoint( ptS) ;
pCompo->GetEndPoint( ptE) ;
// creo l'intervallo desiderato valutanto le coordinate x
Intervals IntMinFeed ; IntMinFeed.Set( ptS.x, ptE.x) ;
// creo un vettore contenente i tratti sopra e i tratti sotto attivi
ICURVEPOVECTOR vAllInt ; vAllInt.reserve(( int)vLUnder.size() + ( int)vLAbove.size()) ;
for ( int i = 0 ; i < ( int)vLUnder.size() ; ++ i)
vAllInt.emplace_back( vLUnder[i]->Clone()) ;
for ( int i = 0 ; i < ( int)vLAbove.size() ; ++ i)
vAllInt.emplace_back( vLAbove[i]->Clone()) ;
int nDim = ( int)vAllInt.size() ;
// aggiungo le parti dei link interessate
double dCurrY = ptS.y ;
BBox3d bBoxLink ;
for ( int l = 0 ; l < ( int)vAddedLinks.size() ; ++ l) {
vAddedLinks[l]->GetLocalBBox( bBoxLink) ;
if (( dCurrY - bBoxLink.GetMin().y) < GetSideStep() + 50 * EPS_SMALL ||
( dCurrY - bBoxLink.GetMax().y) < GetSideStep() + 50 * EPS_SMALL) {
// determino la direzione del Link
Vector3d vtLinkDir( - 25, 0, 0) ;
Point3d ptS_l ;
vAddedLinks[l]->GetStartPoint( ptS_l) ;
Point3d ptE_l ;
vAddedLinks[l]->GetEndPoint( ptE_l) ;
if ( ptE_l.x - ptS_l.x < EPS_SMALL)
vtLinkDir.x -= ptS_l.x - ptE_l.x ;
PtrOwner<ISurfFlatRegion> pSfrRectUp( GetSurfFlatRegionRectangle( bBoxLink.GetDimX() + 50, GetSideStep())) ;
pSfrRectUp->Translate(( ptS_l - ORIG) + vtLinkDir) ;
// link sopra o sotto alla curva currente ?
bool bIsDown = true ;
if ( dCurrY - ptS_l.y < EPS_SMALL)
bIsDown = false ;
CRVCVECTOR ccClass ;
pSfrRectUp->GetCurveClassification( *vAddedLinks[l], EPS_SMALL, ccClass) ;
for ( int i = 0 ; i < ( int)ccClass.size() ; ++ i) {
if ( ccClass[i].nClass == CRVC_IN && bIsDown ||
ccClass[i].nClass == CRVC_OUT && ! bIsDown)
vAllInt.emplace_back( GetCurveComposite( vAddedLinks[l]->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
}
}
}
// scorro tutti i tratti Attivi
for ( int i = 0 ; i < ( int)vAllInt.size() ; ++ i) {
// controllo che il tratto lineare sotto sia sufficientemente lungo
double dLen_iU = EPS_SMALL ;
if ( ! vAllInt[i]->GetLength( dLen_iU) || ( dLen_iU < 1.1 * m_TParams.m_dDiam && i < nDim))
continue ;
Point3d ptS_iU, ptE_iU ;
vAllInt[i]->GetStartPoint( ptS_iU) ;
vAllInt[i]->GetEndPoint( ptE_iU) ;
// la vtDir per Above e Under è sempre invertita rispetto alla linea corrente
// sottraggo questo intervallo a quello originale
IntMinFeed.Subtract( ptS_iU.x, ptE_iU.x) ;
}
// l'intervallo orginale ora conterrà i sottointervalli con feed Minima, tolgo quelli troppo piccoli
Intervals IntMinFeed_noSmall ;
double dParS = EPS_SMALL ; double dParE = EPS_SMALL ;
bool bFound = IntMinFeed.GetFirst( dParS, dParE) ;
while ( bFound) {
if ( dParE - dParS > m_TParams.m_dDiam * 0.5 - 5 * EPS_SMALL)
IntMinFeed_noSmall.Add( dParS, dParE) ;
bFound = IntMinFeed.GetNext( dParS, dParE) ;
}
// ora che ho solo tratti abbastanza lunghi, creo l'intervallo complementare
// questo intervallo contiene tutte le feed Massime
Intervals IntMaxFeed ; IntMaxFeed.Set( ptS.x, ptE.x) ;
IntMaxFeed.Subtract( IntMinFeed_noSmall) ;
// tolgo le parti piccole
Intervals IntMaxFeed_noSmall ;
bFound = IntMaxFeed.GetFirst( dParS, dParE) ;
while ( bFound) {
if ( dParE - dParS > m_TParams.m_dDiam * 0.5 - 5 * EPS_SMALL)
IntMaxFeed_noSmall.Add( dParS, dParE) ;
bFound = IntMaxFeed.GetNext( dParS, dParE) ;
}
// recupero la direzione principale della curva corrente
Vector3d vtDir = X_AX ;
if ( ptE.x - ptS.x < EPS_SMALL)
vtDir = - X_AX ;
// trasformo questi intervalli nei parametri corrispondenti sulla linea originale
bool bFMax = false ;
bFound = IntMaxFeed_noSmall.GetFirst( dParS, dParE) ;
if ( bFound && IntMaxFeed_noSmall.IsInside( ptS.x + vtDir.x * 20 * EPS_SMALL))
bFMax = true ;
if ( ! bFound && bIsLink) // se non ho intervalli a Feed Massima, allora esco ( con Feed Minima)
return true ;
while ( bFound) {
if ( ! bIsLink) { // se segmento di ZigZag
double du_js = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParS, ptS.y, ptS.z), du_js) ;
pCompo->AddJoint( du_js) ;
double du_je = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParE, ptE.y, ptE.z), du_je) ;
pCompo->AddJoint( du_je) ;
}
bFound = IntMaxFeed_noSmall.GetNext( dParS, dParE) ;
}
// aggiorno le proprietà temporanee con la feed
if ( ! bIsLink) {
for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u)
{
if ( IsEven( u) == bFMax)
pCompo->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed() , 0) ;
else
pCompo->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed() , 0) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AssignFeedOnCorners( ICurveComposite* pCrv, const ICurveComposite* pCrv_orig, const double dLenToll)
{
// controllo sulla curva iniziale
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0 || ! pCrv->IsClosed())
return false ;
// controllo se Flag per Feed è attivo
if ( !m_bAssignFeed)
return AssignDefaultFeed( pCrv) ;
// nuova curva finale
PtrOwner<ICurveComposite> pCrv_new( CreateCurveComposite()) ;
if ( IsNull( pCrv_new))
return false ;
Intervals IntU ; IntU.Set( 0.0, 1.0 * pCrv->GetCurveCount()) ;
// scorro tutte le curve cercando i punti non C'
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u ) {
// curva u-esima
const ICurve* pCrv_u = pCrv->GetCurve( u) ;
// curva (u+1)-esima
int u_f = u + 1 ;
if ( u_f == pCrv->GetCurveCount())
u_f = 0 ;
const ICurve* pCrv_uu = pCrv->GetCurve( u_f) ;
// calcolo l'angolo tra le due curve
double dAng = EPS_ANG_ZERO ;
Vector3d dTan_u = V_NULL ; pCrv_u->GetEndDir( dTan_u) ;
Vector3d dTan_uu = V_NULL ; pCrv_uu->GetStartDir( dTan_uu) ;
double dU_S = 0.0 ;
double dU_E = 1.0 ;
if ( ! dTan_u.GetAngle( dTan_uu, dAng) || abs( dAng) > EPS_ANG_SMALL) { // se spigolo...
// ricavo la lunghezza delle due curve e controllo che non siano dei lati aperti
int nStat_u = 0 ;
if ( CheckSimpleOverlap( pCrv_u, pCrv_orig, nStat_u, 50 * EPS_SMALL) && nStat_u != 1) {
double dLen_u = EPS_SMALL ; pCrv_u->GetLength( dLen_u) ;
if ( dLen_u > dLenToll + 500 * EPS_SMALL)
pCrv_u->GetParamAtLength( dLen_u - dLenToll, dU_S) ;
}
int nStat_uu = 0 ;
if ( CheckSimpleOverlap( pCrv_uu, pCrv_orig, nStat_uu, 50 * EPS_SMALL) && nStat_uu != 1) {
double dLen_uu = EPS_SMALL ; pCrv_uu->GetLength( dLen_uu) ;
if ( dLen_uu > dLenToll + 500 * EPS_SMALL)
pCrv_uu->GetParamAtLength( dLenToll, dU_E) ;
}
if ( u_f != 0) {
IntU.Subtract( u + ( nStat_u == 0 ? dU_S : 1), u_f + ( nStat_uu == 0 ? dU_E : 0)) ;
}
else {
if ( nStat_u == 0)
IntU.Subtract( u + dU_S, pCrv->GetCurveCount()) ;
if ( nStat_uu == 0)
IntU.Subtract( 0.0, dU_E) ;
}
}
}
ICRVCOMPOPOVECTOR vCrvMinFeed ;
double dParS = EPS_SMALL ; double dParE = EPS_SMALL ;
Intervals IntMinFeed ; IntMinFeed.Set( 0.0, 1.0 * pCrv->GetCurveCount()) ;
IntMinFeed.Subtract( IntU) ;
bool bFound = IntMinFeed.GetFirst( dParS, dParE) ;
while ( bFound) {
Point3d ptS, ptE ;
vCrvMinFeed.emplace_back( GetCurveComposite( pCrv->CopyParamRange( dParS, dParE))) ;
for ( int u = 0 ; u < vCrvMinFeed.back()->GetCurveCount() ; ++ u)
vCrvMinFeed.back()->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ;
bFound = IntMinFeed.GetNext( dParS, dParE) ;
}
PtrOwner<ICurveComposite> pCrv_clone( pCrv->Clone()) ;
for ( int i = 0 ; i < ( int)vCrvMinFeed.size() ; ++ i) {
PtrOwner<ICurveComposite> pCrv_back( pCrv_clone->Clone()) ;
PtrOwner<ICurveComposite> pCrv_forward( pCrv_clone->Clone()) ;
Point3d ptS, ptE ;
vCrvMinFeed[i]->GetStartPoint( ptS) ;
vCrvMinFeed[i]->GetEndPoint( ptE) ;
double duS ; double duE ;
pCrv_clone->GetParamAtPoint( ptS, duS) ;
pCrv_clone->GetParamAtPoint( ptE, duE) ;
if ( ! pCrv_back->TrimEndAtParam( duS))
pCrv_back->Clear() ;
if ( ! pCrv_forward->TrimStartAtParam( duE))
pCrv_forward->Clear() ;
pCrv_clone->Clear() ;
pCrv_clone->AddCurve( Release( pCrv_back)) ;
pCrv_clone->AddCurve( vCrvMinFeed[i]->Clone()) ;
pCrv_clone->AddCurve( Release( pCrv_forward)) ;
}
pCrv->Clear() ;
pCrv->AddCurve( Release( pCrv_clone)) ;
return false ;
}
//----------------------------------------------------------------------------
bool
Pocketing::GetFeedForParam( double& dPar, double& dFeed)
{
/*
feed
^
|
GetFeed() + --------------\
| * \
| * \
| * \
| * \
| * \
GetFeed() * GetSideStep() / d + * *
| * *
0--------------+------+---------------> Working Arc
GetSideStep() d
*/
if ( dPar > m_TParams.m_dDiam || dPar < 0 ) // dominio...
return false ;
if ( m_TParams.m_dDiam - GetSideStep() < 50 * EPS_SMALL) { // se la funzione è costante...
dFeed = GetMaxFeed() ; // non ho scelta ...
return true ;
}
else {
if ( GetSideStep() < dPar + 50 * EPS_SMALL) { // se sono nel tratto lineare discendente ...
// d/2 su parte discendente
dFeed = GetFeed() + ( GetFeed() * ( 1 - ( GetSideStep() / m_TParams.m_dDiam))) *
( dPar - GetSideStep()) / ( GetSideStep() - m_TParams.m_dDiam) ;
}
else
dFeed = GetMaxFeed() ; // se sono nel tratto costante ...
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AssignFeedForOpenEdge( ICurveComposite* pCrv, const ICurveComposite* pCrvOF_orig)
{
if ( !m_bAssignFeed)
return AssignDefaultFeed( pCrv) ;
// controllo se qualche curva passa sopra ad un lato aperto...
if ( pCrvOF_orig != nullptr ) {
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
const ICurve* pCrv_u = pCrv->GetCurve( u) ;
if ( pCrv_u == nullptr)
return false ;
int nStat = -1 ;
if ( CheckSimpleOverlap( pCrv_u, pCrvOF_orig, nStat, 1500 * EPS_SMALL) && nStat == 1) {
double dFeed = GetMinFeed() ;
double dPar = m_TParams.m_dDiam / 2 ;
if ( ! GetFeedForParam( dPar, dFeed))
return false ;
pCrv->SetCurveTempProp( u, FEED_DIVISOR * dFeed, 0) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AssignFeedForLineInOut( ICurveComposite* pCrv, const bool bIsIn)
{
// controllo parametri
if ( pCrv == nullptr || pCrv->GetCurveCount() < 2)
return false ;
if ( !m_bAssignFeed)
return AssignDefaultFeed( pCrv) ;
if ( bIsIn) // Segmento in ingresso
pCrv->SetCurveTempProp( 0, FEED_DIVISOR * GetMinFeed(), 0) ;
else // Segmento in uscita
pCrv->SetCurveTempProp( pCrv->GetCurveCount() - 1, FEED_DIVISOR * GetMaxFeed(), 0) ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AssignFeedForEdgeCleaning( ICurveComposite *pCrv, const ICRVCOMPOPOVECTOR& vCrvOF_orig, int nInd) {
// controllo parametri
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
// controllo che la svuotatura sia ZigZag o OneWay
if ( m_Params.m_nSubType == POCKET_SUB_SPIRALIN || m_Params.m_nSubType == POCKET_SUB_SPIRALOUT)
return false ;
// controllo se il Flag per assegnare la Feed è attivo
if ( !m_bAssignFeed)
return AssignDefaultFeed( pCrv) ;
double dCurrFeed = GetMinFeed() ;
if ( m_Params.m_nSubType == POCKET_SUB_ZIGZAG) // se pulitura ZigZag -> posso andare alla FeedMassima
dCurrFeed = GetMaxFeed() ;
// se pulitura OneWay la feed rimane le minima
// assegno a tutte le sottocurve la Feed
for ( int u = 0 ; u < ( int)pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempProp( u, FEED_DIVISOR * dCurrFeed, 0) ;
// controllo se le curve di Bordo passano su una curva originale -> controllo dei lati aperti
if ( m_Params.m_nSubType == POCKET_SUB_ZIGZAG && nInd >= 0)
AssignFeedForOpenEdge( pCrv, vCrvOF_orig[nInd]) ;
else {
for ( int i = 0 ; i < ( int)vCrvOF_orig.size() ; ++ i) // se sono nel caso OneWay ho una regione con più chunk...
AssignFeedForOpenEdge( pCrv, vCrvOF_orig[i]) ;
}
// se sono in una svuotatura a ZigZag, controllo la Feed sugli Angoli
if ( m_Params.m_nSubType == POCKET_SUB_ZIGZAG)
AssignFeedOnCorners( pCrv, vCrvOF_orig[nInd], m_TParams.m_dTDiam) ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved_offs, const bool bIsLink,
const ICRVCOMPOPOVECTOR& vLinks_done, const ICurveComposite* pCrv_orig, double dToll)
{
// controllo la validità della curva
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
// controllo se il Flag per assegnare la Feed è attivo
if ( !m_bAssignFeed)
return AssignDefaultFeed( pCrv) ;
// imposto di Default la Feed minima per ogni sottocurva
for ( int u = 0 ; u < ( int)pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ;
// se non ho una superificie svuotata, allora esco ( con Feed Minima)
if ( pSrfRemoved_offs == nullptr || ! pSrfRemoved_offs->IsValid() || pSrfRemoved_offs->GetChunkCount() == 0) {
// controllo eventuali sovrapposizioni con lati aperti
AssignFeedForOpenEdge( pCrv, pCrv_orig) ;
return true ;
}
// clono la superificie ( valida)
PtrOwner<ISurfFlatRegion> 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<ICurveComposite> pCrv_new( CreateCurveComposite()) ;
if ( IsNull( pCrv_new))
return false ;
for ( int i = 0 ; i < ( int)ccClass.size() ; ++ i) {
double dCurrFeed = GetMinFeed() ;
PtrOwner<ICurveComposite> PCrv_sez( GetCurveComposite( pCrv->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
if ( IsNull( PCrv_sez))
continue ;
if ( ccClass[i].nClass == CRVC_IN)
dCurrFeed = GetMaxFeed() ;
for ( int u = 0 ; u < PCrv_sez->GetCurveCount() ; ++ u)
PCrv_sez->SetCurveTempProp( u, FEED_DIVISOR * dCurrFeed, 0) ;
pCrv_new->AddCurve( Release( PCrv_sez)) ;
}
pCrv->Clear() ;
pCrv->AddCurve( Release( pCrv_new)) ;
// controllo eventuali sovrapposizioni tra la curva attuale e i Link in precedenza percorsi
for ( int l = 0 ; l < ( int)vLinks_done.size() ; ++ l) {
IntersCurveCurve intCC( *pCrv, *vLinks_done[l]) ;
for ( int i = 0 ; i < intCC.GetIntersCount() ; ++ i) {
IntCrvCrvInfo aInfo ;
if ( ! intCC.GetIntCrvCrvInfo( i, aInfo) || ! aInfo.bOverlap ||
AreSamePointApprox( aInfo.IciA[0].ptI, aInfo.IciA[1].ptI))
continue ;
PtrOwner<ICurveComposite> pCrv_before( CloneCurveComposite( pCrv)) ;
if ( ! pCrv_before->TrimEndAtParam( aInfo.IciA[0].dU))
pCrv_before->Clear() ;
PtrOwner<ICurveComposite> pCrv_after( CloneCurveComposite( pCrv)) ;
if ( ! pCrv_after->TrimStartAtParam( aInfo.IciA[1].dU))
pCrv_after->Clear() ;
PtrOwner<ICurveComposite> pCrv_overlap( GetCurveComposite( pCrv->CopyParamRange( aInfo.IciA[0].dU, aInfo.IciA[1].dU))) ;
for ( int u = 0 ; u < pCrv_overlap->GetCurveCount() ; ++ u)
pCrv_overlap->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ;
pCrv->Clear() ;
pCrv->AddCurve( Release( pCrv_before)) ;
pCrv->AddCurve( Release( pCrv_overlap)) ;
pCrv->AddCurve( Release( pCrv_after)) ;
}
}
if ( ! bIsLink) { // NEL CASO DI OFFSET
// creo un intervallo con tutte le Feed Minime
Intervals IntMinFeed ;
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
int nProp ; pCrv->GetCurveTempProp( u, nProp, 0) ;
if ( abs ( nProp - int( FEED_DIVISOR * GetMinFeed()) < 5 * EPS_SMALL))
IntMinFeed.Add( u, u + 1) ;
}
double dParS = EPS_SMALL ; double dParE = EPS_SMALL ;
// a questo intervallo tolgo tutti i sottotratti di lunghezza inferiore alla tolleranza richiesta
Intervals IntMinFeed_noSmall ;
bool bFound = IntMinFeed.GetFirst( dParS, dParE) ;
while ( bFound) {
PtrOwner<ICurve> pCrv_Crv( pCrv->CopyParamRange( dParS, dParE)) ;
double dLen = EPS_SMALL ; pCrv_Crv->GetLength( dLen) ;
if ( dLen > dToll + 5 * EPS_SMALL)
IntMinFeed_noSmall.Add( dParS, dParE) ;
bFound = IntMinFeed.GetNext( dParS, dParE) ;
}
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
if ( IntMinFeed_noSmall.IsInside( u + 0.5))
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ;
else
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ;
}
}
else { // NEL CASO DI LINK
// le curve con lunghezza < dToll vanno modificate
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
double dLen = EPS_SMALL ;
pCrv->GetCurve( u)->GetLength( dLen) ;
if ( dLen < dToll + 5 * EPS_SMALL) {
PtrOwner<ICurveComposite> pCrvCompo( CloneCurveComposite( pCrv)) ;
if ( ! pCrvCompo->TrimStartAtParam( u))
pCrvCompo->Clear() ;
if ( ! pCrvCompo->TrimEndAtLen( dLen + m_TParams.m_dDiam))
pCrvCompo->Clear() ;
if ( pCrvCompo->IsValid()) {
bool bFound = false ;
for ( int uu = 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) {
double dLenH = EPS_SMALL ; pCrvCompo->GetCurve( uu)->GetLength( dLenH) ;
if ( dLenH < dToll + 5 * EPS_SMALL)
continue ;
// cerco tra le curve successive vicine se ne trovo una con Feed Minima
int nProp ; pCrvCompo->GetCurveTempProp( uu, nProp, 0) ;
if ( abs( nProp - int( FEED_DIVISOR * GetMinFeed()) < 5 * EPS_SMALL)) {
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ;
bFound = true ;
break ;
}
}
if ( ! bFound && u != 0) {
// arrivato qui, so che successivamente non ho curve con Feed Minima vicine
int nProp ; pCrv->GetCurveTempProp( u - 1, nProp, 0) ;
if ( abs( nProp - int( FEED_DIVISOR * GetMaxFeed()) < 5 * EPS_SMALL))
// se anche la precedente ha Feed Massima -> la curva u-esima è piccola ed Isolata
pCrv->SetCurveTempProp( u, nProp, 0) ;
}
}
else
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ;
}
}
}
// controllo eventuali sovrapposizioni con lati aperti
AssignFeedForOpenEdge( pCrv, pCrv_orig) ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AssignFeedForReturnPath( ICurveComposite* pCrv)
{
// controllo curva corrente
if ( pCrv == nullptr || !pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
// controllo se il Flag per assegnare la Feed è attivo
if ( !m_bAssignFeed)
return AssignDefaultFeed( pCrv) ;
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
for ( int u = 0 ; u < ( int)pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AssignFeedCrvOnUnclearedRegions( ICurveComposite* pCrv)
{
// controllo validità della curva
if ( pCrv == nullptr || !pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
// controllo se il Flag per assegnare la Feed è attivo
if ( !m_bAssignFeed)
return AssignDefaultFeed( pCrv) ;
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
for ( int u = 0 ; u < ( int)pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMinFeed(), 0) ;
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::AssignDefaultFeed( ICurveComposite* pCrv) {
if ( pCrv == nullptr || !pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempProp( u, FEED_DIVISOR * GetMaxFeed(), 0) ;
return true ;
}
bool
Pocketing::DrawColoredCrvForFeedTest( ICurve* pCurve, double dFeed)
{
#if 0
if ( abs( dFeed - GetMinFeed()) < EPS_SMALL) {
int rosso = m_pGeomDB->AddGeoObj(GDB_ID_NULL, GDB_ID_ROOT, pCurve->Clone()) ;
m_pGeomDB->SetMaterial( rosso, RED) ;
m_pGeomDB->SetStatus( rosso, ( m_Params.m_nSubType == POCKET_SUB_SPIRALIN ||
m_Params.m_nSubType == POCKET_SUB_SPIRALIN) ? GDB_ST_OFF : GDB_ST_ON) ;
} else if ( abs( dFeed - GetMaxFeed()) < EPS_SMALL) {
int verde = m_pGeomDB->AddGeoObj(GDB_ID_NULL, GDB_ID_ROOT, pCurve->Clone()) ;
m_pGeomDB->SetMaterial( verde, GREEN) ;
m_pGeomDB->SetStatus( verde, ( m_Params.m_nSubType == POCKET_SUB_SPIRALIN ||
m_Params.m_nSubType == POCKET_SUB_SPIRALIN) ? GDB_ST_OFF : GDB_ST_ON) ;
}
else {
int giallo = m_pGeomDB->AddGeoObj(GDB_ID_NULL, GDB_ID_ROOT, pCurve->Clone()) ;
m_pGeomDB->SetMaterial( giallo, YELLOW) ;
m_pGeomDB->SetStatus( giallo, ( m_Params.m_nSubType == POCKET_SUB_SPIRALIN ||
m_Params.m_nSubType == POCKET_SUB_SPIRALIN) ? GDB_ST_OFF : GDB_ST_ON) ;
}
#endif
return true ;
}
//----------------------------------------------------------------------------
bool
Pocketing::CalcAdaptedElevation( const Point3d& pt1, const Point3d& pt2)
{
PtrOwner<ICurveComposite> pLine( CreateCurveComposite()) ;
pLine->AddPoint( pt1) ;
pLine->AddLine( pt2) ;
int a = m_pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, pLine->Clone()) ;
m_pGeomDB->SetMaterial( a, PURPLE) ;
return true ;
}