Files
EgtGeomKernel/CalcPocketing.cpp
T
Riccardo Elitropi 5375752609 EgtGeomKernel :
- in CalcPocketing aggiunto un frame Locale XY per semplificazione conti e migliorata la gestione della svuotature a trapezio.
2024-07-23 11:52:43 +02:00

7758 lines
318 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2023-2023
//----------------------------------------------------------------------------
// File : EGkCalcPocketing.h Data : 16.11.23 Versione : 2.5j1
// Contenuto : Dichiarazione della funzione per calcolare le curve base di Pocketing
//
//
//
// Modifiche : 16.11.23 RE Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "CurveLine.h"
#include "CurveArc.h"
#include "CurveComposite.h"
#include "BiArcs.h"
#include "SurfFlatRegion.h"
#include "GeoConst.h"
#include "/EgtDev/Include/EGkSfrCreate.h"
#include "/EgtDev/Include/EGkCurveAux.h"
#include "/EgtDev/Include/EGkCalcPocketing.h"
#include "/EgtDev/Include/EGkFilletChamfer.h"
#include "/EgtDev/Include/EGkDistPointCurve.h"
#include "/EgtDev/Include/EGkDistPointCurve.h"
#include "/EgtDev/Include/EGkDistPointSurfFr.h"
#include "/EgtDev/Include/EGkCurveLocal.h"
#include "/EgtDev/Include/EGkMedialAxis.h"
#include "/EgtDev/Include/EGkLinePntTgCurve.h"
#include "/EgtDev/Include/EGkOffsetCurve.h"
#include "/EgtDev/Include/EGkIntervals.h"
#include "/EgtDev/Include/EGkChainCurves.h"
#include "/EgtDev/Include/EgtNumUtils.h"
#include <array>
using namespace std ;
//----------------------------------------------------------------------------
// Calcolo delle curve elementari di Pocketing
//----------------------------------------------------------------------------
// variabili d'appoggio ( per non passare troppi parametri tra le funzioni secondarie)
struct PocketParams {
int nType = POCKET_SPIRALIN ;
double dRad = 0. ; // raggio utensile
double dRad_prec = -1. ; // raggio utensile della lavorazione precedente
double dSideStep = 0. ; // step
double dSideStep_prec = -1. ; // step della lavorazione precedente
double dRadialOffset = 0. ; // offset radiale
double dRadialOffset_prec = -1. ; // offset radiale precedente
double dOpenEdgeRad = 0. ; // massimo raggio per estensione lati aperti
double dOpenMinSafe = 5. ; // estensione minima di sicurezza
double dMaxOptSize = 0. ; // dimensione per ottimizzazione
double dAngle = 0. ; // angolo per orientare le passate OneWay e ZigZag
bool bOptOffsets = true ; // flag per evitare Offset non necessari in SpiralIn/Out
bool bOptOffsetsAdv = false ; // flag per evitare Offset coperti da curve di MedialAxis in SpiralIn/Out
bool bAboveHead = true ; // flag per testa da sopra ( Z+)
bool bSmooth = false ; // curve smussate
bool bInvert = false ; // inversione dei percorsi
bool bAvoidOpt = false ; // flag per evitare casi ottimizzati
bool bAllowZigZagOneWayBorders = false ; // flag per abilitare le curve di bordo per ZigZag/OneWay
Point3d ptStart = P_INVALID ; // punto d'inizio
Point3d ptEnd = P_INVALID ; // punto di fine
SurfFlatRegion SfrLimit ; // superficie limite per estensione lati aperti
bool bCalcFeed = true ; // flag per calcolo della Feed
double dFeed = 1000 ; // feed di riferimento per frazione
double dToolFeed = 1000 ; // feed del tool
// -------------------------------------------------------
bool bAllClosed = true ; // flag per indicare se tutti i bordi sono chiusi
Frame3d frLocXY ; // frame per conti nel piano XY
} ;
static double TOL_TRAPEZOID = 50 * EPS_SMALL ; // tolleranza per casi a trapezio SpiralPocket
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
static double
GetMaxFeed( const PocketParams& PockParams)
{
return PockParams.dFeed ;
}
//---------------------------------------------------------------------------
static double
GetMinFeed( const PocketParams& PockParams)
{
return PockParams.dFeed * PockParams.dSideStep / ( 2 * PockParams.dRad) ;
}
//---------------------------------------------------------------------------
static double
GetFeed( const PocketParams& PockParams)
{
return ( PockParams.dFeed > 0 ? PockParams.dFeed : PockParams.dToolFeed) ;
}
//----------------------------------------------------------------------------
static bool
AssignMaxFeed( ICurveComposite* pCrv, const PocketParams& PockParams)
{
// controllo dei parametri
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
// assegno la Feed massima ad ogni sottocurva di pCrv
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
return true ;
}
//---------------------------------------------------------------------------
static bool
AssignMinFeed( ICurveComposite* pCrv, const PocketParams& PockParams)
{
// controllo dei parametri
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
// assegno la Feed minima ad ogni sottocurva di pCrv
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
return true ;
}
//---------------------------------------------------------------------------
static bool
AssignCustomFeed( ICurveComposite* pCrv, const PocketParams& PockParams, double dFeed)
{
// controllo dei parametri
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
// La Feed deve essere compresa tra la minima e la massima
dFeed = Clamp( dFeed, GetMinFeed( PockParams), GetMaxFeed( PockParams)) ;
// assegno la Feed ad ogni sottocurva di pCrv
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, dFeed, 0) ;
return true ;
}
//---------------------------------------------------------------------------
static bool
GetFeedForParam( double dPar, const PocketParams& PockParams, double& dFeed)
{
/*
Feed
^
|
GetFeed() + --------------\
| * \
| * \
| * \
| * \
| * \
GetFeed() * GetSideStep() / Diam + * *
| * *
0--------------+------+---------------> Length of working angle
GetSideStep() Diam
*/
if ( dPar > PockParams.dRad * 2 || dPar < 0 ) // dominio...
return false ;
if ( PockParams.dRad * 2 - PockParams.dSideStep < 50 * EPS_SMALL) { // se la funzione è costante...
dFeed = GetMaxFeed( PockParams) ; // non ho scelta ...
return true ;
}
else {
if ( PockParams.dSideStep < dPar + 50 * EPS_SMALL) { // se sono nel tratto lineare discendente ...
// d/2 su parte discendente
dFeed = GetFeed( PockParams) + ( GetFeed( PockParams) * ( 1 - ( PockParams.dSideStep / ( PockParams.dRad * 2)))) *
( dPar - PockParams.dSideStep) / ( PockParams.dSideStep - ( PockParams.dRad * 2)) ;
}
else
dFeed = GetMaxFeed( PockParams) ; // se sono nel tratto costante ...
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AssignFeedZigZagOneWay( ICurveComposite* pCompo, const bool bIsLink, const ICURVEPOVECTOR& vLAbove,
const ICURVEPOVECTOR& vLUnder, const ICRVCOMPOPOVECTOR& vAddedLinks,
const PocketParams& PockParams)
{
// 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 ( ! PockParams.bCalcFeed)
return AssignMaxFeed( pCompo, PockParams) ;
// inzialmente setto la feed Minima alla curva
AssignMinFeed( pCompo, PockParams) ;
// 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 * ( 2 * PockParams.dRad))
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) < PockParams.dSideStep + 50 * EPS_SMALL ||
( dCurrY - bBoxLink.GetMax().y) < PockParams.dSideStep + 50 * EPS_SMALL) {
// determino la direzione del Link
Vector3d vtLinkDir( - 25, 0, 0) ;
// recupero gli estremi
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 ;
// superficie a rettangolo definita dal Box
PtrOwner<ISurfFlatRegion> pSfrRectUp( GetSurfFlatRegionRectangle( bBoxLink.GetDimX() + 50, PockParams.dSideStep)) ;
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 ;
// classificazione con la superficie a rettangolo
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)) {
PtrOwner<ICurveComposite> pCrvInt( ConvertCurveToComposite( vAddedLinks[l]->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
if ( ! IsNull( pCrvInt) || pCrvInt->IsValid())
vAllInt.emplace_back( Release( pCrvInt)) ;
}
}
}
}
// 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 * ( 2 * PockParams.dRad) && i < nDim))
continue ;
// estremi dell'intervallo
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 > PockParams.dRad - 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 > PockParams.dRad - 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->SetCurveTempParam( u, GetMaxFeed( PockParams) , 0) ;
else
pCompo->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
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 ;
}
//----------------------------------------------------------------------------
static bool
AssignFeedForOpenEdge( ICurveComposite* pCrv, const ICurveComposite* pCrvOF_orig,
const PocketParams& PockParams, bool bAllowOverWrite)
{
// se non rischiesto il calcolo della Feed, lascio quella standard
if ( ! PockParams.bCalcFeed && bAllowOverWrite)
return AssignMaxFeed( pCrv, PockParams) ;
// 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( PockParams) ;
double dPar = PockParams.dRad ;
if ( ! GetFeedForParam( dPar, PockParams, dFeed))
return false ;
pCrv->SetCurveTempParam( u, dFeed, 0) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
SimplifyCurveByFeeds( ICurveComposite* pCrvCompo, const PocketParams& PockParam, double dToler)
{
// controllo la validità della curva
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0)
return false ;
// NB. Effettuo il Merge dei tratti di curva che presentano la stessa Feed ( mediante la tolleranza)
// curva semplificata
PtrOwner<ICurveComposite> pCrvSimple( CreateCurveComposite()) ;
if ( IsNull( pCrvSimple))
return false ;
/*
- effettuo il Merge dei tratti di curva che presentano la stessa Feed
- imposto la Feed dei tratti piccoli come quella dei tratti precedenti ( per evitare singolarità)
- effettuo un nuovo Merge per uniformare ulteriormente i nuovi tratti ottenuti
*/
double dCurrTempParam ;
int nParStart = 0 ;
double dTempParam ;
double dLen ;
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
pCrvCompo->GetCurveTempParam( i, dTempParam) ;
if ( i == 0) {
dCurrTempParam = dTempParam ;
nParStart = i ;
}
else if ( abs( dCurrTempParam - dTempParam) > dToler) { // se parametri diversi
// recupero il tratto di curva uniforme
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, i))) ;
if ( IsNull( pCrv))
return false ;
// lo semplifico
// recupero la lunghezza del tratto e imposto associo la Feed corrente
// se curva corta -> il parametro di Feed è definito da quello precedente
pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
pCrv->GetLength( dLen) ;
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
pCrvSimple->AddCurve( Release( pCrv)) ;
dCurrTempParam = dTempParam ;
nParStart = i ;
}
}
// ultima parte di curva uniforme ...
PtrOwner<ICurveComposite> pCrvLast( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ;
if ( ! IsNull( pCrvLast)) {
pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
double dLen ; pCrvLast->GetLength( dLen) ;
for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u)
pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
pCrvSimple->AddCurve( Release( pCrvLast)) ;
}
// pulisco la curva originale
pCrvCompo->Clear() ;
// scorro i nuovi tratti ottenuti e ripeto il ragionamento
for ( int i = 0 ; i < pCrvSimple->GetCurveCount() ; ++ i) {
pCrvSimple->GetCurveTempParam( i, dTempParam) ;
if ( i == 0) {
dCurrTempParam = dTempParam ;
nParStart = i ;
}
else if ( abs( dCurrTempParam - dTempParam) > dToler) {
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, i))) ;
if ( IsNull( pCrv))
return false ;
pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
// recupero la lunghezza di tale curve
double dLen ; pCrv->GetLength( dLen) ;
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
pCrvCompo->AddCurve( Release( pCrv)) ; // aggiungo alla curva finale
dCurrTempParam = dTempParam ;
nParStart = i ;
}
}
// ultima parte di curva uniforme ...
pCrvLast.Set( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, pCrvSimple->GetCurveCount()))) ;
if ( ! IsNull( pCrvLast)) {
// semplifico
pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
// recupero la lunghezza di tale curva e imposto la Feed
pCrvLast->GetLength( dLen) ;
for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u)
pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
pCrvCompo->AddCurve( Release( pCrvLast)) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved_offs, bool bIsLink,
const ICRVCOMPOPOVECTOR& vLinks_done, const ICurveComposite* pCrv_orig,
const PocketParams& PockParams, 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 ( ! PockParams.bCalcFeed)
return AssignMaxFeed( pCrv, PockParams) ;
// imposto di Default la Feed minima per ogni sottocurva
AssignMinFeed( pCrv, PockParams) ;
// 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, PockParams, true) ;
return true ;
}
// clono la superificie ( valida)
PtrOwner<ISurfFlatRegion> pSrf_Removed_offs_clone( pSrfRemoved_offs->Clone()) ;
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( - PockParams.dRad, ICurve::OFF_CHAMFER) ||
! pSrf_Removed_offs_clone->IsValid() || pSrf_Removed_offs_clone->GetChunkCount() == 0)
return true ; // esco ( sempre con Feed Minima)
}
else if ( PockParams.dRad < PockParams.dSideStep) { // se curva di Offset e raggio utensile < Side step
if ( ! pSrf_Removed_offs_clone->Offset( PockParams.dSideStep - PockParams.dRad, 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) {
// recupero il tratto di curva ricavato dalla classificazione
double dCurrFeed = GetMinFeed( PockParams) ;
PtrOwner<ICurveComposite> pCrv_sez( ConvertCurveToComposite( pCrv->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
if ( IsNull( pCrv_sez))
continue ; // curva troppo piccola, passo alla successiva
// controllo se interno o fuori alla regione ( regolando quindi la Feed)
if ( ccClass[i].nClass == CRVC_IN) // se interna alla regione rimossa...
dCurrFeed = GetMaxFeed( PockParams) ;
// assegno tale Feed ad ogni sottocurva ricavata dal tratto di classificazione
for ( int u = 0 ; u < pCrv_sez->GetCurveCount() ; ++ u)
pCrv_sez->SetCurveTempParam( u, dCurrFeed, 0) ;
// aggiungo la nuova curva con la Feed regolata a quella finale
pCrv_new->AddCurve( Release( pCrv_sez)) ;
}
// sotituisco con quanto calcolato
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) {
// classificazione
IntersCurveCurve intCC( *pCrv, *vLinks_done[l]) ;
for ( int i = 0 ; i < intCC.GetIntersCount() ; ++ i) { // per ogni tratto ricavato
IntCrvCrvInfo aInfo ;
if ( ! intCC.GetIntCrvCrvInfo( i, aInfo) || ! aInfo.bOverlap ||
AreSamePointApprox( aInfo.IciA[0].ptI, aInfo.IciA[1].ptI))
continue ; // passo al successivo se non c'è overlap o se i punti coicidono
// tratto precedente
PtrOwner<ICurveComposite> pCrv_before( pCrv->Clone()) ;
if ( ! pCrv_before->TrimEndAtParam( aInfo.IciA[0].dU))
pCrv_before->Clear() ; // se troppo piccolo, elimino
// tratto successivo
PtrOwner<ICurveComposite> pCrv_after( pCrv->Clone()) ;
if ( ! pCrv_after->TrimStartAtParam( aInfo.IciA[1].dU))
pCrv_after->Clear() ; // se troppo piccolo, elimino
// tratto di overlap
PtrOwner<ICurveComposite> pCrv_overlap( ConvertCurveToComposite( pCrv->CopyParamRange( aInfo.IciA[0].dU, aInfo.IciA[1].dU))) ;
if ( ! IsNull( pCrv_overlap) && pCrv_overlap->IsValid()) {
for ( int u = 0 ; u < pCrv_overlap->GetCurveCount() ; ++ u)
pCrv_overlap->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
// sostituisco unendo i 3 tratti
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) {
double dParam ; pCrv->GetCurveTempParam( u, dParam, 0) ;
if ( abs( dParam - GetMinFeed( PockParams)) < 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->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
else
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 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( pCrv->Clone()) ;
if ( ! pCrvCompo->TrimStartAtParam( u))
pCrvCompo->Clear() ;
if ( ! pCrvCompo->TrimEndAtLen( dLen + PockParams.dRad * 2))
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
double dParam ; pCrvCompo->GetCurveTempParam( uu, dParam, 0) ;
if ( abs( dParam - GetMinFeed( PockParams)) < 5 * EPS_SMALL) {
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
bFound = true ;
break ;
}
}
if ( ! bFound && u != 0) {
// arrivato qui, so che successivamente non ho curve con Feed Minima vicine
double dParam ; pCrv->GetCurveTempParam( u - 1, dParam, 0) ;
if ( abs( dParam - GetMaxFeed( PockParams)) < 5 * EPS_SMALL)
// se anche la precedente ha Feed Massima -> la curva u-esima è piccola ed Isolata
pCrv->SetCurveTempParam( u, dParam, 0) ;
}
}
else
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
}
}
}
// controllo eventuali sovrapposizioni con lati aperti
AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams, true) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
AssignFeedSpiralOpt( const int nOptType, const PocketParams& PockParams, 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 ( ! PockParams.bCalcFeed)
return AssignMaxFeed( pCrv, PockParams) ;
switch ( PockParams.nType) {
case POCKET_SPIRALIN :
if ( nOptType == 0) { // Spirale dall'Esterno
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
if ( u == 0) // prima circonferenza
pCrv->SetCurveTempParam( 0, GetMinFeed( PockParams), 0) ;
else // semi cerchi in tangenza
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
}
}
else if ( nOptType == 1) { // Trapezoidi
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 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_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->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
else
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
}
}
else if ( nOptType == 1) { // Trapezoidi
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
}
break ;
default :
break ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
ResetCurveTempProps( ICurveComposite* pCrvCompo)
{
// controllo parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// scorro le curve
for ( int nU = 0 ; nU < pCrvCompo->GetCurveCount() ; ++ nU) {
pCrvCompo->SetCurveTempProp( nU, 0, 0) ;
pCrvCompo->SetCurveTempProp( nU, 0, 1) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
IsChunkAllClosed( const ISurfFlatRegion* pSfr, int nChunk, bool& bAllClose)
{
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid() || nChunk < 0 || nChunk > pSfr->GetChunkCount())
return false ;
bAllClose = true ;
// scorro tutti i loop del Chunk
for ( int nL = 0 ; nL < pSfr->GetLoopCount( nChunk) ; ++ nL) {
// recupero il loop
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfr->GetLoop( nChunk, nL))) ;
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
return false ;
// scorro le curve semplici
int nCurrProp = TEMP_PROP_INVALID ;
for ( int nU = 0 ; nU < pCrvLoop->GetCurveCount() ; ++ nU) {
bAllClose = ( pCrvLoop->GetCurveTempProp( nU, nCurrProp, 0) &&
nCurrProp == TEMP_PROP_CLOSE_EDGE) ;
if ( ! bAllClose)
return true ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
IsSfrAllClosed( const ISurfFlatRegion* pSfr, bool& bAllClose)
{
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid())
return false ;
bAllClose = true ;
// ciclo sui Chunk della regione
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
if ( ! IsChunkAllClosed( pSfr, nC, bAllClose))
return false ;
if ( ! bAllClose)
return true ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetCoeffLinArc( const ICurveArc* pArc, double dDiam, double& dSubArc)
{
// controllo parametri
if ( pArc == nullptr || ! pArc->IsValid())
return false ;
// recupero il punto medio
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 / 2) ;
if ( ! pCrvTool->IsValid())
return false ;
// effettuo la classificazione
IntersCurveCurve intCC( *pArc, *pCrvTool) ;
CRVCVECTOR ccClass ;
if ( intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) && int( ccClass.size()) == 3 &&
ccClass[1].nClass == CRVC_IN) {
// recupero i due punti di intersezione
Point3d ptS, ptE ;
pCrvTool->GetPointD1D2( ccClass[1].dParS, ICurve::FROM_MINUS, ptS) ;
pCrvTool->GetPointD1D2( ccClass[1].dParE, ICurve::FROM_PLUS, ptE) ;
// vettori uscenti dal centro dell'arco e diretti verso i punti di intersezione
Vector3d vtS = ptS - pArc->GetCenter() ;
Vector3d vtE = ptE - pArc->GetCenter() ;
// angolo tra i due vettori
double dTheta ;
vtS.GetAngle( vtE, dTheta) ;
dTheta = abs( dTheta) ;
// lunghezza del tratto di arco compreso
dSubArc = pArc->GetRadius() * DEGTORAD * dTheta ;
}
else
return false ;
return true ;
}
//----------------------------------------------------------------------------
static bool
GetParamForPtStartOnEdge( const ICurve* pCrvCheck, const ICurveComposite* pCrvCompo,
const ICRVCOMPOPOVECTOR& vOtherCrv, const PocketParams& PockParams, 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( pCrvCompo->Clone()) ;
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 = ( 2 * PockParams.dRad ) + 2 * PockParams.dRadialOffset ;
double dLen ;
if ( ! pCrv->GetLength( dLen))
return false ;
double dSubArc = -1 ;
if ( pCrv->GetType() == CRV_ARC)
GetCoeffLinArc( GetCurveArc( pCrv), dDiam, dSubArc) ;
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( pCompo->Clone()) ;
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 *= (( PockParams.dRad + PockParams.dRadialOffset - 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 ;
}
//----------------------------------------------------
static bool
ComputeTrapezoidSpiralLeadInLeadOut( ICurveComposite* pCompo, const Vector3d& vtMainDir, bool bLeadIn,
const PocketParams& PockParams, bool& bIsOutsideRaw)
{
// inizializzazione come interno al grezzo
bIsOutsideRaw = false ;
Point3d ptP ;
Vector3d vtDir ;
if ( bLeadIn) {
pCompo->GetStartPoint( ptP) ;
pCompo->GetStartDir( vtDir) ;
}
else {
pCompo->GetEndPoint( ptP) ;
pCompo->GetEndDir( vtDir) ;
}
// recupero estrusione della curva
Vector3d vtExtr ; pCompo->GetExtrusion( vtExtr) ;
// recupero info sui lati aperti
int nPropOpen = pCompo->GetTempProp( 0) ;
bool bEdgeOpen = (( nPropOpen & ( bLeadIn ? 8 : 2)) > 0) ;
bool bBase0Open = (( nPropOpen & 1) > 0) ;
bool bBase1Open = (( nPropOpen & 4) > 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) ;
// 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) ;
Point3d ptNewStart = ptP + vtDirP * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
pCompo->AddLine( ptNewStart, ! bLeadIn) ;
pCompo->SetCurveTempProp( bLeadIn ? 0 : pCompo->GetCurveCount() - 1, 2) ;
bIsOutsideRaw = true ;
}
// tento con attacco ruotato di 90° se non sto considerando un tratto aggiuntivo per pulire angoli
else if (( bBase0Open || bBase1Open) && ! bIsOutsideRaw && nExtraEdge == 0) {
Vector3d vtDirO = bBase0Open ? vtDir : - vtDir ;
vtDirO.Rotate( vtExtr, ( PockParams.bInvert ? -90 : 90)) ;
// se vicino al bordo del grezzo
Point3d ptNewStart = ptP + vtDirO * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
pCompo->AddLine( ptNewStart, ! bLeadIn) ;
pCompo->SetCurveTempProp( bLeadIn ? 0 : pCompo->GetCurveCount() - 1, 2) ;
bIsOutsideRaw = true ;
}
return true ;
}
//----------------------------------------------------
static bool
AdjustTrapezoidSpiralForLeadInLeadOut( ICurveComposite* pCompo, const PocketParams& PockParams)
{
// 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, PockParams, bStartOutside) ;
// end point
bool bEndOutside = false ;
ComputeTrapezoidSpiralLeadInLeadOut( pCompo, vtMainDir, false, PockParams, bEndOutside) ;
// eventuale inversione della curva per partire sempre dall'esterno del grezzo
if ( bEndOutside && ! bStartOutside)
pCompo->Invert() ;
return true ;
}
//----------------------------------------------------------------------------
static bool
ExtendPathOnOpenEdge( ICurveComposite* pCrvPath, const PocketParams& PockParams, Vector3d& vtMidOut,
bool bEndOrStart)
{
// controllo dei parametri
if ( pCrvPath == nullptr || ! pCrvPath->IsValid())
return false ;
// recupero il punto iniziale/finale della curva
Point3d ptMidOpen ;
if ( bEndOrStart) {
if ( ! pCrvPath->GetEndPoint( ptMidOpen))
return false ;
}
else {
if ( ! pCrvPath->GetStartPoint( ptMidOpen))
return false ;
}
// calcolo il punto di caduta dell'utensile
Point3d ptOut = ptMidOpen + vtMidOut * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
// se non ho una superficie limite, allora aggiungo il tratto
if ( ! PockParams.SfrLimit.IsValid()) {
// aggiungo al ritorno l'uscita
pCrvPath->AddLine( ptOut, bEndOrStart) ;
pCrvPath->SetCurveTempProp( ( bEndOrStart ? pCrvPath->GetCurveCount() - 1 : 0), TEMP_PROP_OUT_START, 0) ; // nProp 2 per le curve LeadIn/Out
pCrvPath->SetCurveTempParam( ( bEndOrStart ? pCrvPath->GetCurveCount() - 1 : 0), GetMinFeed( PockParams) , 0) ; // Feed minima
return true ;
}
// recupero la regione limite
PtrOwner<ISurfFlatRegion> pSfrLimit( PockParams.SfrLimit.Clone()) ;
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
return false ;
// Offset della regione
pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET) ;
// ruoto il versore di uscita cercando un'entrata valida
Vector3d vtRotMidOut = vtMidOut ;
for ( int i = 0 ; i < 4 ; ++ i) {
// creo l'ombra del Tool centrato in ptOut
PtrOwner<ICurveArc> pCrvToolShape( CreateCurveArc()) ;
if ( IsNull( pCrvToolShape) || ! pCrvToolShape->Set( ptOut, Z_AX, PockParams.dRad))
return false ;
// controllo l'intersezione ( pSfrLimit e pCrvToolShape sono nel piano XY)
CRVCVECTOR ccClass ;
if ( pSfrLimit->GetCurveClassification( *pCrvToolShape, EPS_SMALL, ccClass) &&
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) {
// aggiungo al ritorno l'uscita
pCrvPath->AddLine( ptOut, bEndOrStart) ;
pCrvPath->SetCurveTempProp( ( bEndOrStart ? pCrvPath->GetCurveCount() - 1 : 0), TEMP_PROP_OUT_START, 0) ; // nProp 2 per le curve LeadIn/Out
pCrvPath->SetCurveTempParam( ( bEndOrStart ? pCrvPath->GetCurveCount() - 1 : 0), GetMinFeed( PockParams) , 0) ; // Feed minima
return true ;
}
vtRotMidOut.Rotate( Z_AX, - ANG_RIGHT) ;
ptOut = ptMidOpen + vtRotMidOut * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustContourStart( ICurveComposite* pCompo, const PocketParams& PockParams, const ICRVCOMPOPOVECTOR& vCrvIsl,
bool bOrder, Point3d ptRef)
{
// se cerco semplicemente il tratto lineare chiuso più lungo ...
if ( ! bOrder) {
// la priorità è essagnata ai tratti lineari chiusi
int i = 0 ; // <--- indice della curva corrente
int nMax = - 1 ;
double dLenMax = 0 ;
const ICurve* pCrv = pCompo->GetFirstCurve() ;
while ( pCrv != nullptr) {
double dLen ;
if ( pCrv->GetType() == CRV_LINE && pCrv->GetTempProp() == 0 && pCrv->GetLength( dLen) && dLen > dLenMax) {
dLenMax = dLen ;
nMax = i ;
}
++ i ;
pCrv = pCompo->GetNextCurve() ;
}
// se non trovato o troppo corto, cerco il tratto chiuso più lungo in generale
if ( nMax < 0 || dLenMax < 2 * ( 2 * PockParams.dRad)) {
i = 0 ;
pCrv = pCompo->GetFirstCurve() ;
while ( pCrv != nullptr) {
double dLen ;
if ( pCrv->GetType() != CRV_LINE && pCrv->GetTempProp() == 0 && pCrv->GetLength( dLen) && dLen > dLenMax) {
dLenMax = dLen ;
nMax = i ;
}
++ i ;
pCrv = pCompo->GetNextCurve() ;
}
}
// controllo che il tratto chiuso più lungo trovato sia sufficientemente lungo
if ( dLenMax < 10 * EPS_SMALL) {
// se troppo piccolo allora lo imposto aperto
pCompo->SetCurveTempProp( nMax, 0, 1) ;
// spezzo la curva nel primo tratto aperto sufficientemente lungo
for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) {
double dLen ; pCompo->GetCurve( u)->GetLength( dLen) ;
if ( dLen > 10 * EPS_SMALL) {
nMax = u ;
continue ;
}
}
}
// se non trovato, imposto il punto iniziale a mentà del primo tratto
if ( nMax == -1) {
pCompo->ChangeStartPoint( 0.5) ;
return true ;
}
pCompo->ChangeStartPoint( nMax + 0.5) ;
}
// se invece sto cercando di entrare da un lato chiuso per una svuotatura, allora riordino i lati
// chiusi a seconda della lunghezza, e a partire dal più lungo cerco un parametro su tale curva che mi consenta
// un'entrata sufficientemente distante da isole e da altre curve della curva stessa su cui cerco l'entrata ...
else {
// se ho un punto di riferimento, allora come prima cosa controllo il lato chiuso più vicino
if ( ptRef.IsValid()) {
double dMinDist = INFINITO ;
int nIndCrvMinDist = - 1 ;
for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) {
DistPointCurve DPC( ptRef, *pCompo->GetCurve( u)) ;
double dCurrDist = INFINITO ;
if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) {
nIndCrvMinDist = u ;
dMinDist = dCurrDist ;
}
}
// se avessi curve alla stessa distanza ( circa) non controllo le altre, mi accontento della
// prima curva trovata
if ( nIndCrvMinDist != -1) {
// controllo che il lato chiuso sia sufficientemente lungo
double dLen = 0. ;
pCompo->GetCurve( nIndCrvMinDist)->GetLength( dLen) ;
if ( dLen > 2 * PockParams.dRad + 2 * PockParams.dRadialOffset + 500 * EPS_SMALL) {
pCompo->ChangeStartPoint( nIndCrvMinDist + 0.5) ;
return true ;
}
}
}
// creo un vettore di indici che definisce l'ordine delle curve chiuse in base alla lunghezza
INTVECTOR vInd ; vInd.reserve( pCompo->GetCurveCount()) ;
// vettore di indici già utilizzati
INTVECTOR vIndUsed ; vIndUsed.reserve( pCompo->GetCurveCount()) ;
double dMaxLen = -INFINITO ;
int nCurrInd = 0 ;
bool bStop = false ;
for ( int c = 0 ; c < pCompo->GetCurveCount() && ! bStop ; ++ c) {
bStop = true ;
for ( int i = 0 ; i < pCompo->GetCurveCount() ; ++ i) {
int nProp_i ;
// se la curva i-esima non è già stata considerata ed è chiusa...
if ( find( vIndUsed.begin(), vIndUsed.end(), i) == vIndUsed.end() &&
pCompo->GetCurveTempProp( i, nProp_i, 0) && nProp_i == 0) {
// creo la curva i-esima
const ICurve* pCrv_i( pCompo->GetCurve( i)) ;
double dLen_i ;
if ( pCrv_i->GetLength( dLen_i) && dLen_i > dMaxLen) { // se di lunghezza maggiore alla soglia...
dMaxLen = dLen_i ;
nCurrInd = i ;
bStop = false ;
}
}
}
vIndUsed.push_back( nCurrInd) ;
vInd.push_back( nCurrInd) ;
dMaxLen = -INFINITO ;
}
if ( vInd.empty()) {
// se questa condizione fosse vera allora non sono riuscito ad entrare da nessun lato aperto in precedenza e non
// ho nemmeno un lato chiuso disponibile per entrare... ( forzo l'entrata a metà del primo lato)
pCompo->ChangeStartPoint( 0.5) ;
return true ;
}
// ora che ho riempito il vettore di indici lo scorro e cerco di entrare in questi lati chiusi secondo l'ordine
bool bOk = false ;
for ( int i = 0 ; i < int( vInd.size()) && !bOk ; ++ i) {
const ICurve* pCrv( pCompo->GetCurve( vInd[i])) ;
double dPar ;
if ( GetParamForPtStartOnEdge( pCrv, pCompo, vCrvIsl, PockParams, dPar)) {
pCompo->ChangeStartPoint( vInd[i] + dPar) ;
bOk = true ;
}
}
if ( ! bOk) {
// se non riesco ad entrare da nessun lato chiuso, considerando che in precedenza ho già provato ad
// entrare da tutti i lati aperti...
pCompo->ChangeStartPoint( vInd[0] + 0.5) ;
return true ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetHomogeneousParts( ICurveComposite* pCrvCompo, const PocketParams& PockParams, 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 richiesto)...
ICRVCOMPOPOVECTOR vCrvNULL ;
AdjustContourStart( pCrvCompo, PockParams, vCrvNULL, false, P_INVALID) ;
// 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 ;
}
//---------------------------------------------------------------------------
static bool
CreateSurfFrIncidence( const ICurveComposite* pCrv, const Vector3d& vtTanS_, const Vector3d& vtTanE_,
const double dRad, ISurfFlatRegion* pSfrInc)
{
// controllo dei parametri
if ( pCrv == nullptr || ! pCrv->IsValid())
return false ;
pSfrInc->Clear() ;
// se la curva è chiusa, allora la regione di incidenza è già definita
if ( pCrv->IsClosed()) {
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) ;
return ( pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0) ;
}
// creo la curva di Offset esterna ( deve esistere ed essere valida)
OffsetCurve OffsCrv ;
PtrOwner<ICurve> pOffsExt( CreateCurveComposite()) ;
if ( IsNull( pOffsExt) ||
! OffsCrv.Make( pCrv, dRad, ICurve::OFF_FILLET) ||
! pOffsExt.Set( OffsCrv.GetLongerCurve()) ||
IsNull( pOffsExt))
return false ;
// creo la curva di Offset interna ( se esiste e valida...)
PtrOwner<ICurve> pOffsInt( CreateCurveComposite()) ;
if ( IsNull( pOffsInt))
return false ;
if ( OffsCrv.Make( pCrv, - dRad, ICurve::OFF_FILLET)) {
PtrOwner<ICurve> pMyCrv( OffsCrv.GetLongerCurve()) ;
if ( ! IsNull( pMyCrv) && pMyCrv->IsValid())
pOffsInt.Set( pMyCrv) ;
}
// recupero gli estremi della curva aperta corrente
Point3d pt1 ; pCrv->GetEndPoint( pt1) ;
Point3d pt4 ; pCrv->GetStartPoint( pt4) ;
// creo la regione dalle curve
PtrOwner<ICurveComposite> pCrvExtLoopSurfInc( CreateCurveComposite()) ;
if ( IsNull( pCrvExtLoopSurfInc) ||
! pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ||
! pCrvExtLoopSurfInc->AddLine( pt1))
return false ;
Point3d pt5 ; pCrvExtLoopSurfInc->GetStartPoint( pt5) ;
if ( ! IsNull( pOffsInt) && pOffsInt->IsValid()) {
Point3d pt2 ; pOffsInt->GetEndPoint( pt2) ;
Point3d pt3 ; pOffsInt->GetStartPoint( pt3) ;
if ( ! pCrvExtLoopSurfInc->AddLine( pt2) ||
! pOffsInt->Invert() ||
! pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ||
! pCrvExtLoopSurfInc->AddLine( pt4) ||
! pCrvExtLoopSurfInc->AddLine( pt5))
return false ;
}
else {
if ( ! pCrvExtLoopSurfInc->AddLine( pt4) ||
! pCrvExtLoopSurfInc->AddLine( pt5))
return false ;
}
// per sicurezza...
pCrvExtLoopSurfInc->Close() ;
// creo la regione
if ( ! pSfrInc->AddExtLoop( Release( pCrvExtLoopSurfInc)))
return false ;
return ( pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0) ;
}
//----------------------------------------------------------------------------
static bool
AdjustOpenEdge( const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vCrvIsland,
const double dParS, const double dParE, const Vector3d& vtTanS,
const Vector3d& vtTanE, const double dRad, const double dDiamJ,
const PocketParams& PockParams, 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
vtTanS -> vettore di tangenza finale tratto chiuso precedente
vtTanE -> vettore di tangenza iniziale ( invertito ) del tratto chiuso successivo
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 validità 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, vtTanS, vtTanE, dRad + 75 * EPS_SMALL, pSfrInc))
return false ;
// creo un vettore con tutte le curve che potrebbero cadere, in parte, nella regione di incidenza
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)) ; // aggiungo il bordo
}
// controllo se la curva è un'isola ( normale del piano -Z_AX)
bool bIsIsland = false ;
double dArea ; Plane3d plCheck ;
if ( ! pCrvCompo->GetArea( plCheck, dArea))
return false ;
bIsIsland = AreSameVectorEpsilon( plCheck.GetVersN(), - Z_AX, 10 * EPS_SMALL) ;
// scorro il vettore creato...
for ( int c = 0 ; c < int( vCrvToCheck.size()) ; ++ c) {
// 1) recupero la curva corrente
PtrOwner<ICurveComposite> pCrvCurr( vCrvToCheck[c]->Clone()) ;
if ( IsNull( pCrvCurr) || ! pCrvCurr->IsValid())
return false ;
// 2) ricavo i tratti con proprietà uniformi ( Aperti/Chiusi )
ICRVCOMPOPOVECTOR vpCrvs ;
if ( ! GetHomogeneousParts( pCrvCurr, PockParams, vpCrvs))
return false ;
// 3) considero solo i tratti chiusi
for ( int cl = 0 ; cl < int( vpCrvs.size()) ; ++ cl) {
if ( vpCrvs[cl]->GetTempProp() == 1)
continue ;
// 4) effettuo l'Offset della curva di metà dDiamJ
OffsetCurve OffsCrv ;
if ( ! OffsCrv.Make( vpCrvs[cl], - dDiamJ * 0.5 - 20 * EPS_SMALL, ICurve::OFF_FILLET))
return false ;
// 5) 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)) {
// 6) 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() ;
// 7) se la "Regione a fagiolo" non influenza la regione di incidenza, la transcuro
bool bDiscard = false ;
if ( ! bIsIsland) { // se tratto un loop esterno
for ( int cI = 0 ; cI < pSfrInc->GetChunkCount() && ! bDiscard ; ++ cI)
for ( int cB = 0 ; cB < pSfrBean->GetChunkCount() && ! bDiscard ; ++ cB)
bDiscard = ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrBean, cB) != REGC_INTERS) ;
}
else if ( bIsAllOpen && bIsIsland) { // se isola aperta
for ( int cI = 0 ; cI < pSfrInc->GetChunkCount() && ! bDiscard ; ++ cI) {
for ( int cB = 0 ; cB < pSfrBean->GetChunkCount() && ! bDiscard ; ++ cB) {
if ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrBean, cB) == REGC_IN1) {
for ( int l = 1 ; l < pSfrBean->GetLoopCount( cB) ; ++ l) {
PtrOwner<ISurfFlatRegion> pSfrTmp( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrTmp) || ! pSfrTmp->AddExtLoop( pSfrBean->GetLoop( cB, l)) ||
! pSfrTmp->Invert())
return false ;
bDiscard = ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrTmp, 0) == REGC_IN1) ;
}
}
}
}
}
if ( ! bDiscard) {
// 8) aggiorno la regione di incidenza
if ( ! pSfrInc->Add( *pSfrBean))
return false ;
}
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ; // passo al tratto offsettato successivo
}
}
}
// ATTENZIONE !
// L'algoritmo di allargamento presso i lati aperti è Euristico; io mi estendo a seconda della geometria del
// lato aperto al di fuori del volume di svuotatura... Devo controllare di non rovinare delle zone al di fuori di
// esso !
if ( PockParams.SfrLimit.IsValid()) {
// recupero la superficie limite
PtrOwner<ISurfFlatRegion> pSfrLimit( PockParams.SfrLimit.Clone()) ;
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
return false ;
// piccolo Offset per sicurezza
pSfrLimit->Offset( 50 * EPS_SMALL, ICurve::OFF_FILLET) ;
pSfrInc->Subtract( *pSfrLimit) ; // rimuovo la regione limite
// può capitare che la regione Limite mi crei più Chunk sulla pSfrInc
// I chunk da togliere sono tutti quelli che si sono separati dalla pCrvBorder
// NB. Il mio obiettivo è quello di avere un'unica curva con estremi i due tratti chiusi estremanti
if ( pSfrInc->GetChunkCount() > 1) { // se ottengo più chunks
PtrOwner<ISurfFlatRegion> pNewSfrInc( CreateSurfFlatRegion()) ;
if ( IsNull( pNewSfrInc))
return false ;
// ricavo i punti iniziali e finali della curva di bordo
Point3d ptS, ptE ;
if ( ! pCrvBorder->GetStartPoint( ptS) || ! pCrvBorder->GetEndPoint( ptE))
return false ;
// per ogni Chunk ( > 1 )
for ( int i = 0 ; i < pSfrInc->GetChunkCount() ; ++ i) {
// bordo esterno
PtrOwner<ICurveComposite> pCrvEL( ConvertCurveToComposite( pSfrInc->GetLoop( i, 0))) ;
if ( IsNull( pCrvEL) || ! pCrvEL->IsValid())
return false ;
// se estremi in comune, la curva cercata è la seguente
if ( pCrvEL->IsPointOn( ptS, 300 * EPS_SMALL) && pCrvEL->IsPointOn( ptE, 300 * EPS_SMALL)) {
// se la nuova regione di incidenza è vuota, aggiorno
if ( ! pNewSfrInc->IsValid())
pNewSfrInc.Set( pSfrInc->CloneChunk( i)) ;
// se la nuova regione di incidenza non è vuota, aggiungo
else {
PtrOwner<ISurfFlatRegion> pSfrToAdd( pSfrInc->CloneChunk( i)) ;
if ( IsNull( pSfrToAdd) || ! pNewSfrInc->Add( *pSfrToAdd))
return false ;
}
}
}
// definisco quindi la nuova regione di incidenza
if ( ! pNewSfrInc->IsValid())
return false ;
pSfrInc.Set( pNewSfrInc) ;
}
}
// dalla regione di incidenza devo estrarre il tratto di bordo che ha per estremi i lati chiusi
// ( stando al di fuori, quindi estendendomi all'esterno, della regione da svuotare )
PtrOwner<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... ( il nuovo lato è il loop esterno della regione di incidenza)
if ( bIsAllOpen) {
// pulisco la curva originale
pCrvBorder->Clear() ;
if ( bIsIsland) { // se isola inserisco il loop interno corretto della regione
for ( int l = 1 ; l < pSfrInc->GetLoopCount( 0) ; ++ l) {
// recupero il Loop
PtrOwner<ICurveComposite> pCrvIntLoop( ConvertCurveToComposite( pSfrInc->GetLoop( 0, l))) ;
if ( IsNull( pCrvIntLoop) || ! pCrvIntLoop->IsValid())
return false ;
// controllo che sia interno alla curva di bordo corrente ( l'isola aperta)
IntersCurveCurve ICC( *pCrvIntLoop, *pCrvCompo) ;
CRVCVECTOR ccClass ;
if ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) &&
int( ccClass.size()) == 1 &&
ccClass[0].nClass == CRVC_OUT) {
// il loop interno corretto va impostato come unico e nuovo loop interno della regione
pCrvNewBorder->CopyFrom( pCrvIntLoop) ;
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) ;
break ;
}
}
}
// 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 = -1 ; double dUTrimE = -1 ;
pCrvNewBorder->GetParamAtPoint( ptStart, dUTrimS, 10000 * EPS_SMALL) ;
pCrvNewBorder->GetParamAtPoint( ptEnd, dUTrimE, 10000 * EPS_SMALL) ;
// pulisco la curva originale
pCrvBorder->Clear() ;
if ( ! pCrvBorder->AddCurve( pCrvNewBorder->CopyParamRange( dUTrimS, dUTrimE)) ||
! pCrvBorder->IsValid()) {
// se la curva non originaria di lato aperto non era chiusa...
if ( ! pCrvBorder->IsClosed() && pCrvNewBorder->IsClosed()) {
// cerco il punto più vicino al ptStart del chiuso sulla nuova curva di bordo aperta
Point3d ptMinDist ;
int nFlag ;
if ( ! DistPointCurve( ptStart, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag))
return false ;
// cambio il punto iniziale della border in ptMinDist
double dUTmp ;
if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp) ||
! pCrvNewBorder->ChangeStartPoint( dUTmp))
return false ;
// cerco il punto più vicino al ptEnd del chiuso sulla curva di bordo aperta
if ( ! DistPointCurve( ptEnd, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag))
return false ;
// ricavo il parametro sulla curva
if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp))
return false ;
if ( dUTmp < EPS_SMALL)
dUTmp = pCrvNewBorder->GetCurveCount() - 5 * EPS_SMALL ;
// copio la curva tra questi due parametri
PtrOwner<ICurveComposite> pCrvTmp( ConvertCurveToComposite( pCrvNewBorder->CopyParamRange( 0., dUTmp))) ;
if ( IsNull( pCrvTmp) || ! pCrvTmp->IsValid())
return false ;
// raccordo con il chiuso mediante due tratto lineare
pCrvTmp->AddLine( ptStart, false) ;
pCrvTmp->AddLine( ptEnd, true) ;
// restituisco
pCrvBorder->CopyFrom( pCrvTmp) ;
return true ;
}
pCrvBorder->CopyFrom( pCrvCompo) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustContourWithOpenEdges( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vCrvIsl, const PocketParams& PockParams)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// recupero i parametri di lavorazione correnti
double dDiam = PockParams.dRad * 2 ;
double dOffR = PockParams.dRadialOffset ;
double dStep = PockParams.dSideStep ;
// raggio di riferimento per offset
double dOutEdge = 0.5 * dDiam ;
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_ZIGZAG)
dOutEdge = max( dOutEdge, dDiam - dStep) ;
double dRad = dOutEdge + dOffR ;
if ( abs( PockParams.dOpenEdgeRad) > 0 && PockParams.dOpenEdgeRad < dRad)
dRad = PockParams.dOpenEdgeRad ;
// ricavo i tratti omogenei
ICRVCOMPOPOVECTOR vpCrvs ;
if ( ! GetHomogeneousParts( pCrvCompo, PockParams, vpCrvs))
return false ;
// NB. Il primo tratto è la prima metà del tratto aperto più lungo ( se esiste )
// Offset esterno per CurveJ
double dDiamJ = 1.05 * ( dDiam) + 2 * dOffR ;
if ( abs( PockParams.dOpenEdgeRad) > 0)
dDiamJ = 100 * EPS_SMALL ;
// NB. 1.05 serve per eccedere leggermente, in modo che il contro-offset della prima curva di svuotatura presenti
// dei piccoli archi tra i chiusi e gli aperti ( in modo da svuotare bene lungo il chiuso)
// curva finale da restituire
PtrOwner<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) {
// ricavo la tangenze dei lati chiusi agli estremi di questa curva
Vector3d vtTanS = V_INVALID ;
Vector3d vtTanE = V_INVALID ;
if ( i != 0) {
if ( ! vpCrvs[i-1]->GetEndDir( vtTanS) || // tangente finale del chiuso precedente
! vpCrvs[( i + 1) % int( vpCrvs.size())]->GetStartDir( vtTanE)) // tangente iniziale del chiuso successivo...
return false ;
vtTanE.Invert() ; // invertita
}
if ( ! AdjustOpenEdge( pCrvCompo, vCrvIsl, dParS, dParE, vtTanS, vtTanE, dRad, dDiamJ, PockParams, vpCrvs[i]))
return false ;
}
// assegno le proprietà di lato Aperto/Chiuso per la curva corrente
for ( int u = 0 ; u < vpCrvs[i]->GetCurveCount() ; ++ u)
vpCrvs[i]->SetCurveTempProp( u, nCurrTmpProp, 0) ;
// aggiungo la curva ricavata ( se chiusa -> la copio, se aperta -> copio l'estesa)
if ( ! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone())) {
// per sicurezza, se gli estremi non coincidono, creo un piccolo raccordo lineare
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 ;
}
//----------------------------------------------------------------------------
static bool
ChainCompoCurves( ICRVCOMPOPOVECTOR& vCrvCompo)
{
/* concatenamento delle curve composite */
if ( int( vCrvCompo.size() < 2))
return true ;
// controllo validità delle curve
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i)
if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid())
return false ;
// preparo i dati per il concatenamento
bool bFirst = true ;
Point3d ptNear = ORIG ;
double dToler = 500 * EPS_SMALL ; // leggermente maggiore della tolleranza sulla superficie limite
ChainCurves chainC ;
chainC.Init( false, dToler, int( vCrvCompo.size())) ;
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
// recupero i dati della curva necessari al concatenamento e li assegno
Point3d ptStart, ptEnd ;
Vector3d vtStart, vtEnd ;
if ( ! vCrvCompo[i]->GetStartPoint( ptStart) || ! vCrvCompo[i]->GetStartDir( vtStart) ||
! vCrvCompo[i]->GetEndPoint( ptEnd) || ! vCrvCompo[i]->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 ;
}
}
// vettore delle curve composite risultante
ICRVCOMPOPOVECTOR vCrvCompoChained ;
// recupero i percorsi concatenati
INTVECTOR vnInd ;
while ( chainC.GetChainFromNear( ptNear, true, vnInd)) {
// 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 < vnInd.size() ; ++ i) {
int nId = abs( vnInd[i]) - 1 ;
bool bInvert = ( vnInd[i] < 0) ;
// se necessario, la inverto
if ( bInvert)
vCrvCompo[nId]->Invert() ;
// la aggiungo alla curva composta
if ( ! pCrvCompo->AddCurve( ::Release( vCrvCompo[nId]), true, dToler))
return false ;
}
// aggiorno il nuovo punto vicino
if ( pCrvCompo->GetCurveCount() > 0) {
pCrvCompo->GetEndPoint( ptNear) ;
vCrvCompoChained.emplace_back( Release( pCrvCompo)) ;
}
}
swap( vCrvCompoChained, vCrvCompo) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustBorderCurves( ICRVCOMPOPOVECTOR& vCrvCompo, const PocketParams& PockParams, bool bCalcFeed)
{
/*
sistemazione punto di inizio, segmento per LeadIn, e Feed
( Se la curva è chiusa, allora si può cambiare il punto d'inizio)
*/
// se non ho curve, allora non faccio nulla
if ( vCrvCompo.empty())
return true ;
// controllo validità delle curve
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i)
if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid())
return false ;
// recupero la superficie limite ( se esiste ed è valida)
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrLimit))
return false ;
if ( PockParams.SfrLimit.IsValid()) { // se esiste, allora classifico
// recupero la superficie limite
pSfrLimit.Set( PockParams.SfrLimit.Clone()) ;
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
return false ;
}
// per ogni curva signola, aggiungo l'entrata da fuori se ammissibile
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
int nMaxIter = ( vCrvCompo[i]->IsClosed() ? 4 : 1) ;
double dLen = EPS_SMALL ;
if ( nMaxIter == 4)
vCrvCompo[i]->GetLength( dLen) ;
if ( PockParams.bInvert)
vCrvCompo[i]->Invert() ;
for ( int j = 0 ; j < nMaxIter ; ++ j) {
if ( j > 0)
vCrvCompo[i]->ChangeStartPoint( dLen / nMaxIter) ;
// ricavo punto di inizio e vettore tangente
Point3d ptStart ; vCrvCompo[i]->GetStartPoint( ptStart) ;
Vector3d vtStart ; vCrvCompo[i]->GetStartDir( vtStart) ;
// recupero il punto di caduta dell'utensile
Point3d ptFall = ptStart - vtStart * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
// controllo che il tool non sia interno alla regione limite
bool bAllowLeadIn = ( ! pSfrLimit->IsValid()) ;
if ( ! bAllowLeadIn) {
// calcolo l'ombra del tool centrata in ptFall
PtrOwner<ICurveArc> pCrvToolShape( CreateCurveArc()) ;
if ( IsNull( pCrvToolShape) || ! pCrvToolShape->Set( ptFall, Z_AX, PockParams.dRad - 100 * EPS_SMALL))
return false ;
CRVCVECTOR ccClass ;
bAllowLeadIn = ( pSfrLimit->GetCurveClassification( *pCrvToolShape, EPS_SMALL, ccClass) &&
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) ;
// se esiste intersezione con la limite Offsettata, provo a ruotare di 90° il tratto lineare
if ( ! bAllowLeadIn) {
vtStart.Rotate( Z_AX, PockParams.bInvert ? - ANG_RIGHT : ANG_RIGHT) ;
ptFall = ptStart + vtStart * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
if ( ! pCrvToolShape->Set( ptFall, Z_AX, PockParams.dRad - 100 * EPS_SMALL))
return false ;
ccClass.clear() ;
bAllowLeadIn = ( pSfrLimit->GetCurveClassification( *pCrvToolShape, EPS_SMALL, ccClass) &&
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) ;
}
}
if ( bAllowLeadIn) { // se ammessa entrata da fuori
vCrvCompo[i]->AddLine( ptFall, false) ;
vCrvCompo[i]->SetCurveTempProp( 0, TEMP_PROP_OUT_START, 0) ; // nProp 2 per le curve LeadIn/Out
vCrvCompo[i]->SetCurveTempParam( 0, GetMinFeed( PockParams) , 0) ; // Feed massima
break ; // interrompo a prescindere l'iterazione per i punti iniziali
}
}
}
// imposto le Feed per tali curve se richiesto
if ( bCalcFeed) {
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
/*
Idea : Feed proporzionale al minimo Offset per annullare la regione
-> Massimo parametro sui bisettori di VORONOI
*/
// recupero il MaxOffset per la curva
double dMaxOffs = vCrvCompo[i]->GetTempParam( 0) ;
// calcolo la Feed proporzionale a tale Offset
double dFeed ;
GetFeedForParam( 2 * dMaxOffs, PockParams, dFeed) ;
AssignCustomFeed( vCrvCompo[i], PockParams, dFeed) ;
// imposto il flag di curva singola
vCrvCompo[i]->SetTempProp( TEMP_PROP_SINGLE_CURVE, 0) ; //nProp per curva singola
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPocketCurvesByCloseEdges( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvCompoRes, bool& bAllRemoved)
{
// controllo parametri
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
return false ;
bAllRemoved = false ;
// controllo se la superficie si annulla con un controOffset del raggio utensile
double dMaxOffs ;
pSfrChunk->GetMaxOffset( dMaxOffs) ;
if ( dMaxOffs > PockParams.dRad + 200 * EPS_SMALL)
return true ;
/*
NB. Si poteva calcolare la Fat curve del raggio utensile dell'Offset di tutti i chiusi e vedere
se l'unione di queste regioni copriva interamente il Chunk attuale... così facendo rischio di
trascurare delle parti che spariscono facendo il primo Offset dei chiusi; il controllo va fatto
quindi con un contro-offset della regione complessiva
*/
// controllo se il Chunk ha delle isole
bool bHasIslands = ( pSfrChunk->GetLoopCount( 0) > 1) ;
// recupero il Loop esterno
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
return false ;
// recupero i tratti con proprietà uniformi
ICRVCOMPOPOVECTOR vpCrvs ;
GetHomogeneousParts( pCrvExtLoop, PockParams, vpCrvs) ;
if ( vpCrvs.size() > 1) { // unisco il primo e l'ultimo se estremi compatibili
// NB. GetHomogeneousParts() cambia il punto di inizio nel lato chiuso più lungo ( se presente)
Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ;
Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ;
if ( AreSamePointApprox( ptS, ptE)) {
vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ;
vpCrvs.erase( vpCrvs.end() - 1) ;
}
}
// controllo se il loop Esterno è uniforme ( quindi tutto chiuso o tutto aperto)
bool bExtAllClose = false ;
bool bExtAllOpen = false ;
if ( int( vpCrvs.size()) == 1) {
if ( vpCrvs[0]->GetTempProp( 0) == 0)
bExtAllClose = true ;
else
bExtAllOpen = true ;
}
// NB. Se regione estena tutta chiusa o aperta e senza isole... allora non faccio nulla ( es caso trapezi)
// Se tutta chiusa -> Chunk non svuotabile, il contro-offset del raggio utensile annulla la regione
// Se tutta aperta -> La regione è definita mediante Offset esterno del contorno aperto
if ( ( bExtAllClose || bExtAllOpen) && ! bHasIslands)
return true ;
// Se il contorno esterno è misto -> non devo avere isole
if ( ! bExtAllClose && ! bExtAllOpen && bHasIslands)
return true ;
// Se il contorno esterno è tutto chiuso e ho più di un'isola non faccio nulla
if ( bExtAllClose && pSfrChunk->GetLoopCount( 0) > 2)
return true ;
// scorro i chiusi e sottraggo la regione da loro creata mediante l'utensile
// NB. Queste curve sono orientate in maniera corretta
ICRVCOMPOPOVECTOR vCrvCompoRes_tmp ; // salvo il vettore di curve offsettate del raggio utensile
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
if ( vpCrvs[i]->GetTempProp( 0) == 0) { // se tratto chiuso...
OffsetCurve OffsCrv ; // con offset verso l'interno
if ( OffsCrv.Make( vpCrvs[i], - PockParams.dRad - PockParams.dRadialOffset, ICurve::OFF_FILLET)) {
PtrOwner<ICurve> pOffLongestCrv( OffsCrv.GetLongerCurve()) ;
while ( ! IsNull( pOffLongestCrv)) {
// salvo la curva corrente
vCrvCompoRes_tmp.emplace_back( ConvertCurveToComposite( Release( pOffLongestCrv))) ;
// passo alla successiva
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ;
}
}
}
}
/*
Casi gestiti :
1) La curva esterna e mista e non ho isole ( ho già il percorso)
2) La curva esterna è tutta aperta e le isole sono tutte chiuse
3) La curva esterna è tutta chiusa e ho una sola isola aperta ( ho già il percorso)
*/
// 1)
if ( ! bExtAllClose && ! bExtAllOpen)
;
else {
// scorro tutte le isole
bool bValidIslands = true ; // flag per isole tutte uniformi
for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) && bValidIslands ; ++ i) {
// controllo uniformità dell'isola
int nCurrTmpProp = -1 ;
int nPrecTmpProp = -1 ;
bool bIsMixed = false ;
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ;
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
return false ;
for ( int u = 0 ; u < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ u) {
pCrvIsl->GetCurveTempProp( u, nCurrTmpProp, 0) ;
bIsMixed = ( u != 0 && nCurrTmpProp != nPrecTmpProp) ;
nPrecTmpProp = nCurrTmpProp ;
}
// se proprità non uniformi -> tutta chiusa ( isole non uniformi non sono definite)
if ( bIsMixed) {
for ( int u = 0 ; u < pCrvIsl->GetCurveCount() ; ++ u)
pCrvIsl->SetTempProp( u, 0) ;
nCurrTmpProp = 0 ; // aggiorno il Flag
}
// 2) e 3)
bValidIslands = ( ( nCurrTmpProp == 0 && bExtAllOpen) ||
( nCurrTmpProp == 1 && bExtAllClose)) ;
}
if ( ! bValidIslands)
return true ;
// nel caso 2) devo inserire le curve
// creo una regione formata solo dalla isole
PtrOwner<ISurfFlatRegion> pSrfIslands( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfIslands))
return false ;
for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) ; ++ i)
pSrfIslands->AddExtLoop( pSfrChunk->GetLoop( 0, i)) ;
pSrfIslands->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET) ;
for ( int c = 0 ; c < pSrfIslands->GetChunkCount() ; ++ c)
for ( int l = 0 ; l < pSrfIslands->GetLoopCount( l) ; ++ l)
vCrvCompoRes_tmp.emplace_back( ConvertCurveToComposite( pSrfIslands->GetLoop( c, l))) ;
}
// se non ho ottenuto curve, allora ho finito
if ( vCrvCompoRes_tmp.empty())
return true ;
bAllRemoved = true ;
// recupero la superficie limite ( se esiste ed è valida)
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrLimit))
return false ;
// per tutte le curve inserite, devo tenere solamente quelle esterne alla superficie limite
if ( PockParams.SfrLimit.IsValid()) { // se esiste, allora classifico
// recupero la superficie limite
pSfrLimit.Set( PockParams.SfrLimit.Clone()) ;
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
return false ;
// effettuo un Offset della regione, tutto ciò che dista più del raggio utensile non la rovina
pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset - 250 * EPS_SMALL, ICurve::OFF_FILLET) ; // c'è un offset radiale, quindi tengo tolleranza alta
ICRVCOMPOPOVECTOR vCrvCompoRes_tmp_Splitted ;
for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) {
CRVCVECTOR ccClass ;
if ( pSfrLimit->GetCurveClassification( *vCrvCompoRes_tmp[i], EPS_SMALL, ccClass)) {
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
if ( ccClass[j].nClass == CRVC_OUT) {
PtrOwner<ICurveComposite> pCrvRes( ConvertCurveToComposite( vCrvCompoRes_tmp[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
if ( ! IsNull( pCrvRes) && pCrvRes->IsValid())
vCrvCompoRes_tmp_Splitted.emplace_back( Release( pCrvRes)) ;
}
}
}
}
// se non ho curve, allora esco
if ( vCrvCompoRes_tmp_Splitted.empty())
return true ;
// altrimenti aggiorno il vettore di curve con quelle solo esterne alla regione limite
swap( vCrvCompoRes_tmp, vCrvCompoRes_tmp_Splitted) ;
}
// salvo come primo tempParam l'offset massimo della regione ( servirà per la Feed)
for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) {
vCrvCompoRes_tmp[i]->SetTempParam( dMaxOffs, 0) ;
vCrvCompoRes.emplace_back( Release( vCrvCompoRes_tmp[i])) ; // aggiungo la curva al vettore
}
return true ;
}
//----------------------------------------------------------------------------
static bool
ModifySurfByOpenEdges( ISurfFlatRegion* pSfr, PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes,
bool& bSkipPocket)
{
// controllo parametri :
if ( pSfr == nullptr || ! pSfr->IsValid())
return true ; // <- se superficie non valida, allora non ho niente da impostare sui suoi lati
bSkipPocket = false ;
// controllo se esistono dei lati aperti
if ( ! IsSfrAllClosed( pSfr, PockParams.bAllClosed))
return false ;
// se lati tutti chiusi, allora non devo fare nulla
if ( PockParams.bAllClosed)
return true ;
// 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
// creo la superficie da restituire... ( questa superficie sarà estesa presso i lati aperti)
PtrOwner<ISurfFlatRegion> pSrfFinal( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfFinal))
return false ;
// numero di chunk rimovibili mediante una sola curva di Offset seguente il tratto chiuso
int nChunkOneCurve = 0 ;
// numero complessivo di Chunk da svuotare
int nChunk = pSfr->GetChunkCount() ;
// per ogni Chunck della superificie ottenuta...
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
// controllo se seguendo i chiusi posso svuotare l'interno chunk
/*
nel caso in cui il chunk c-esimo può essere svuotato da una o più curve che seguono
il/i tratto/i chiuso/i, queste vengono salvate direttamente nei percorsi di svuotatura.
Il chuck c-esimo verrà poi ignorato ( quindi nessuna estensione presso gli aperti)
*/
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( nC)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
return false ;
bool bIsAllRemoved = false ;
GetPocketCurvesByCloseEdges( pSfrChunk, PockParams, vCrvCompoRes, bIsAllRemoved) ;
if ( bIsAllRemoved) {
// se ho rimosso tutto, allora passo la chunk successivo
++ nChunkOneCurve ;
continue ;
}
// 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 del Chunk nC-esimo
bool bIsChunkModified = false ;
// ricavo il Loop esterno ( External Loop)
PtrOwner<ICurveComposite> pCrvEL( ConvertCurveToComposite( pSfr->GetLoop( nC, 0))) ;
if ( IsNull( pCrvEL) || ! pCrvEL->IsValid())
return false ;
// creo un vettore di curve con le isole del Chunk ( Internal Loops)
ICRVCOMPOPOVECTOR vCrvIsl ;
for ( int nL = 1 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) {
PtrOwner<ICurveComposite> pCrvIL( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ;
if ( IsNull( pCrvIL) || ! pCrvIL->IsValid())
return false ;
vCrvIsl.emplace_back( Release( pCrvIL)) ;
}
// se la curva esterna presenta dei lati aperti -> devo modificarla
bool bSomeOpen = false ;
int nProp0 = TEMP_PROP_INVALID ;
for ( int u = 0 ; u < pCrvEL->GetCurveCount() && ! bSomeOpen ; ++ u)
bSomeOpen = ( pCrvEL->GetCurveTempProp( u, nProp0, 0) && nProp0 == TEMP_PROP_OPEN_EDGE) ;
// se trovo dei lati aperti
if ( bSomeOpen) {
// allorargo il Loop esterno presso i lati aperti
if ( ! AdjustContourWithOpenEdges( pCrvEL, vCrvIsl, PockParams))
return false ;
bIsChunkModified = true ;
}
// controllo i bordi delle isole ottenute
// NB. L'isola può essere tutta aperta o tutta chiusa ( se non uniforme, passo a tutta chiusa)
// La definizione di isola con proprietà non uniformi non è definita...
for ( int nI = 0 ; nI < int( vCrvIsl.size()) ; ++ nI) {
// controllo uniformità
int nCurrTmpProp = TEMP_PROP_INVALID ;
int nPrecTmpProp = TEMP_PROP_INVALID ;
bool bIsMixed = false ;
PtrOwner<ICurveComposite> pCrvIsl( CloneCurveComposite( vCrvIsl[nI])) ;
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
return false ;
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ nU) {
pCrvIsl->GetCurveTempProp( nU, nCurrTmpProp, 0) ;
bIsMixed = ( nU != 0 && nCurrTmpProp != nPrecTmpProp) ;
nPrecTmpProp = nCurrTmpProp ;
}
// se proprità non uniformi -> tutta chiusa
if ( bIsMixed) {
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() ; ++ nU)
pCrvIsl->SetTempProp( nU, TEMP_PROP_CLOSE_EDGE) ;
nCurrTmpProp = TEMP_PROP_CLOSE_EDGE ;
bIsChunkModified = true ;
}
// se curva tutta aperta, controllo se l'isola può essere trascurata
if ( nCurrTmpProp == TEMP_PROP_OPEN_EDGE) {
// calcolo il massimo Offset
double dMaxOffs ;
CalcCurveLimitOffset( *pCrvIsl, dMaxOffs) ;
// se l'isola è trascurabile passo alla successiva
if ( dMaxOffs < 2 * PockParams.dRad + 2 * PockParams.dRadialOffset)
continue ;
// altrimenti, estendo l'isola aperta
ICRVCOMPOPOVECTOR vCrvOther ;
vCrvOther.emplace_back( pCrvEL->Clone()) ;
for ( int nII = 0 ; nII < int( vCrvIsl.size()) ; ++ nII) {
if ( nII != nI)
vCrvOther.emplace_back( vCrvIsl[nII]->Clone()) ;
}
if ( ! AdjustContourWithOpenEdges( pCrvIsl, vCrvOther, PockParams))
return false ;
bIsChunkModified = true ;
}
// conservo l'isola ( estesa o meno)
vCrvToTIsland.emplace_back( pCrvIsl->Clone()) ;
}
// se c'è stata almeno una modifica di lato aperto al Chunk (nC-esimo), devo ricreare il Chunk
// con i nuovi loop ricavati ( Chunk dopo Chunk creo la superficie finale)
if ( bIsChunkModified) {
/*
l'estensione degli aperti, può unire il loop esterno con il loop delle sue isole.
Creo una nuova regione dai nuovi contorni ottenuti
*/
SurfFlatRegionByContours SfrBC ;
// loop Esterno
pCrvEL->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
SfrBC.AddCurve( Release( pCrvEL)) ;
// isole non trascurate
for ( int nI = 0 ; nI < int( vCrvToTIsland.size()) ; ++ nI) {
vCrvToTIsland[nI]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
SfrBC.AddCurve( Release( vCrvToTIsland[nI])) ;
}
// ricavo il nuovo Chunk nC-esimo esteso presso i lati aperti
PtrOwner<ISurfFlatRegion> pNewChunk( SfrBC.GetSurf()) ;
if ( IsNull( pNewChunk) || ! pNewChunk->IsValid())
return false ;
// aggiungo il Chunk nC-esimo 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( pSfrChunk) ;
else if ( ! pSrfFinal->Add( *pSfrChunk))
return false ;
}
}
// se ho ottenuto delle curve singole, allora le provo a concatenare
// ( tenendo solo le curve al di fuori della superficie limite ( con Offset del raggio del tool)
// potrei doverle riconcatenare
if ( ! ChainCompoCurves( vCrvCompoRes))
return false ;
// determino ora il punto di inizio, il tratto per il LeadIn e la Feed delle curve singole
if ( ! AdjustBorderCurves( vCrvCompoRes, PockParams, true))
return false ;
// se ho rimosso tutti i Chunk con una singola curva, allora ho terminato la svuotatura
bSkipPocket = ( nChunk == nChunkOneCurve) ;
if ( bSkipPocket)
return true ;
// restituisco la superficie aggiornata ricavata
if ( ! pSrfFinal->IsValid())
return false ;
pSfr->Clear() ;
pSfr->CopyFrom( pSrfFinal) ;
return ( pSrfFinal->IsValid() && pSrfFinal->GetChunkCount() > 0) ;
}
// ***************************************************************************
// ------------- SCELTA DEL PUNTO INIZIALE -----------------------------------
// ***************************************************************************
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
static bool
GetParamOnOpenSide( const ICurveComposite* pCompo, const ICRVCOMPOPOVECTOR& vOtherCrv,
const PocketParams& PockParams, Point3d& ptMid, Vector3d& vtMidOrt)
{
// recupero il vettore estrusione
Vector3d vtExtr = Z_AX ;
pCompo->GetExtrusion( vtExtr) ;
// verifico se tutti i lati sono aperti
bool bAllOpen = true ;
const ICurve* pMyCrv = pCompo->GetFirstCurve() ;
while ( pMyCrv != nullptr) {
if ( pMyCrv->GetTempProp() != 1) {
bAllOpen = false ;
break ;
}
pMyCrv = pCompo->GetNextCurve() ;
}
// salvo il punto iniziale e la direzione d'uscita del primo lato aperto valido che trovo
// NB. Il primo valido non significa il migliore, potrebbe infatti essere il lato aperto più lungo
// ma essere all'interno del grezzo... cerco di dare priorità ai lati sul grezzo
// flag per la presenza di un primo lato aperto valido per entrare
bool bFirstOpenValid = false ;
Point3d ptFirstOpenValid = P_INVALID ;
Vector3d vtFirstOpenValid = V_INVALID ;
// richiedo lunghezza superiore a diametro utensile più doppio offset radiale
double dRefLen = ( bAllOpen ? 0 : ( 2 * PockParams.dRad) + 2 * PockParams.dRadialOffset - 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() ;
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) ;
// 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 ( ( PockParams.bAboveHead && ptTest.z > ptMid.z + 100 * EPS_SMALL) ||
( ! PockParams.bAboveHead && ptTest.z < ptMid.z - 100 * EPS_SMALL) ||
( abs( ptTest.z - ptMid.z) < 100 * EPS_SMALL && ptTest.y < ptMid.y - 100 * EPS_SMALL)) {
dMaxLen = max( dMaxLen, dLen + dLenAgg) ;
ptMid = ptTest ;
// vettore ortogonale verso l'esterno (ruotato -90deg attorno a estrusione)
pCrv->GetMidDir( vtMidOrt) ;
vtMidOrt.Rotate( vtExtr, 0, -1) ;
}
}
// se più lunga ( o non già trovata)
else if ( dLen + dLenAgg > dMaxLen || !bFound) {
dMaxLen = dLen + dLenAgg ;
double dParIn ;
// cerco il parametro di tale curva (0 < dParIn < 1) migliore per l'entrata
if ( GetParamForPtStartOnEdge( pCrv, pCompo, vOtherCrv, PockParams, dParIn)) {
pCrv->GetPointD1D2( dParIn, ICurve::FROM_PLUS, ptMid) ;
PtrOwner<ICurveComposite> pCompoClone( pCompo->Clone()) ;
if ( IsNull( pCompoClone))
return false ;
double dU ;
pCompoClone->GetParamAtPoint( ptMid, dU) ;
pCompoClone->ChangeStartPoint( dU) ;
pCompoClone->GetStartDir( vtMidOrt) ;
vtMidOrt.Rotate( vtExtr, 0, -1) ;
// salvo l'indice di questo lato aperto se si tratta del primo valido
if ( ! bFirstOpenValid) {
bFirstOpenValid = true ;
ptFirstOpenValid = ptMid ;
vtFirstOpenValid = vtMidOrt ;
}
}
}
dLenPrev = dLen ;
}
}
else
dLenPrev = 0 ;
// vado alla successiva
pPrevCrv = pCrv ;
pCrv = ( bNextOk ? pNextCrv : nullptr) ;
}
// se non ho trovato un lato aperto valido fuori dal grezzo, ma ho trovato un lato aperto
// generico accettabile
if ( ! bFound && bFirstOpenValid) {
ptMid = ptFirstOpenValid ;
vtMidOrt = vtFirstOpenValid ;
bFound = true ;
}
return bFound ;
}
//----------------------------------------------------------------------------
static bool
SetSpecialPtStartForOpenEdges( const ICurveComposite* pCrvOrig, const PocketParams& PockParams,
ICurveComposite* pCrvCompo, Point3d& ptStart,
Vector3d& vtMidOut, bool& bMidOut)
{
// controllo dei parametri
if ( pCrvOrig == nullptr || ! pCrvOrig->IsValid() ||
pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// cerco il lato aperto più lungo
double dLenRef = ( 2 * PockParams.dRad) - 50 * EPS_SMALL ; // almeno di questa lunghezza
for ( int u = 0 ; u < pCrvOrig->GetCurveCount() ; ++ u) {
if ( pCrvOrig->GetCurve( u)->GetTempProp( 0) == 0)
continue ; // escludo le chiuse
// ricavo la curva u-esima aperta
const ICurve* pCrvOpen = pCrvOrig->GetCurve( u) ;
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
return false ;
// ricavo la lunghezza di tale curva
double dLen = 0. ;
pCrvOpen->GetLength( dLen) ;
if ( dLen > dLenRef) { // se lunghezza accettabile o maggiore della massima trovata
Point3d ptSTmp ;
Vector3d vtMidOutTmp ;
// ricavo il punto medio e il versore tangente associato
if ( pCrvOpen->GetPointD1D2( 0.5, ICurve::FROM_MINUS, ptSTmp, &vtMidOutTmp)) {
int nFlag ;
if ( DistPointCurve( ptSTmp, *pCrvCompo).GetMinDistPoint( EPS_SMALL, ptStart, nFlag)) {
// controllo se il punto iniziale sta effettivaente su un lato aperto della curva attuale
// di primo Offset
double dU_check ;
if ( pCrvCompo->GetParamAtPoint( ptStart, dU_check)) {
// se il punto trovato è a cavallo tra due curve...
if ( abs( dU_check - floor( dU_check)) < EPS_PARAM ||
abs( dU_check - ceil( dU_check)) < EPS_PARAM) {
const ICurve* pCrvA = pCrvCompo->GetCurve( int( floor( dU_check))) ;
if ( pCrvA == nullptr)
return false ;
const ICurve* pCrvB = pCrvCompo->GetCurve( int( ceil( dU_check))) ;
if ( pCrvB == nullptr)
return false ;
if ( pCrvA->GetTempProp( 0) == 0 || pCrvB->GetTempProp( 0) == 1)
continue ; // se una delle due è chiusa, salto tale punto
}
// se invece tocca una sola curva, controllo che sia aperta
else {
const ICurve* pCrvA = pCrvCompo->GetCurve( int( floor( dU_check))) ;
if ( pCrvA == nullptr)
return false ;
if ( pCrvA->GetTempProp( 0) == 0)
continue ; // se chiusa, salto tale punto
}
}
vtMidOutTmp.Normalize() ;
vtMidOutTmp.Rotate( Z_AX, - 90) ;
vtMidOut = vtMidOutTmp ;
bMidOut = true ;
}
}
}
}
return bMidOut ;
}
//----------------------------------------------------------------------------
static bool
AssignOpenCloseTmpPropToFirstOffsCurve( ICurveComposite* pCrv, const PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvLoops, INTVECTOR& vIndex, bool& bSomeOpen)
{
// controllo dei parametri
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i)
if ( IsNull( vCrvLoops[i]) || ! vCrvLoops[i]->IsValid() || vCrvLoops[i]->GetCurveCount() == 0)
return false ;
bSomeOpen = false ; // flag per presenza di lati aperti
int nInd = -1 ; // indice della prima curva che non è "raccordo" di Offset
// scorro tutte le curve presenti in pCrv ( curva da cui devo entrare)
for ( int i = 0 ; i < pCrv->GetCurveCount() ; ++ i) {
// nProp0 -> #curva il cui Offset ha generato la curva i-esima di pCrv
int nProp0 ; pCrv->GetCurveTempProp( i, nProp0, 0) ;
// nProp1 -> #loop che contiene la curva espressa in nProp0
int nProp1 ; pCrv->GetCurveTempProp( i, nProp1, 1) ;
if ( nProp0 > 0) { // se questa curva non è un "raccordo" di Offset
// controllo per maggiore sicurezza che effettivamente nProp1 sia un indice valido per il vettore dei Loops e
// che nProp0 non sia maggiore del numero di curve del loop nProp1-esimo del vettore dei Loops della pSrfToWork
if ( nProp1 >= 0 && nProp1 < int( vCrvLoops.size()) && nProp0 < vCrvLoops[nProp1]->GetCurveCount()) {
// aggiorno il vettore di indici ...
if ( find( vIndex.begin(), vIndex.end(), nProp1) == vIndex.end())
vIndex.push_back( nProp1) ;
// aggiorno la proprietà della curva da cui devo entrare
int nTempProp ; vCrvLoops[nProp1]->GetCurveTempProp( nProp0 - 1, nTempProp, 0) ;
pCrv->SetCurveTempProp( i, nTempProp, 0) ;
// se la curva è aperta, aggiorno il Flag
if ( nTempProp == 1 && ! bSomeOpen)
bSomeOpen = true ;
// salvo l'indice della prima curva trovata che non è un "raccordo" di Offset
if ( nInd == -1)
nInd = i ;
}
else
pCrv->SetCurveTempProp( i, 0, 0) ;
}
else {
pCrv->SetCurveTempProp( i, -2, 0) ;
pCrv->SetCurveTempProp( i, -2, 1) ;
}
}
// scorro tutte le curve incerte ( che derivano da raccordi )
if ( nInd != -1)
pCrv->ChangeStartPoint( nInd) ; // la prima curva non è un "raccordo" di Offset
for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++ i) {
double dLen = 0. ;
if ( pCrv->GetCurve( i)->GetLength( dLen) && dLen < 0.025 * ( 2 * PockParams.dRad))
pCrv->SetCurveTempProp( i, 0, 0) ;
else {
int nTmpProp0, nTmpProp1 ;
if (( pCrv->GetCurveTempProp( i, nTmpProp0, 0) && nTmpProp0 == -2) &&
( pCrv->GetCurveTempProp( i, nTmpProp1, 1) && nTmpProp1 == -2)) {
// copio la temp prop della curva precedente
pCrv->SetCurveTempProp( i, pCrv->GetCurve( i-1)->GetTempProp(), 0) ;
// se troppo corta, allora chiusa
if ( pCrv->GetCurve( i)->GetLength( dLen) && dLen < 0.01 * ( 2 * PockParams.dRad))
pCrv->SetCurveTempProp( i, 0, 0) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
SetPtStartForPath( ICurveComposite* pCrvOffsAct, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut,
int nOffs, const ICurveComposite* pCrvOrig)
{
// ============================= 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)
// =================================================================================================
/*
1) Come prima cosa ricavo i lati aperti equivalenti dal primo Offset ; Facendo l'Offset di una curva
chiusa ho all'interno delle temp prop delle sottcurve due informazioni :
// nTmpProp0 -> #curva il cui Offset ha generato la curva i-esima attuale
// nTmpProp1 -> #loop che contiene la curva espressa in nTmpProp0
Quindi dal mio contorno con i lati aperti impostati, posso settare i lati aperti "equivalenti"
anche sulla curva di primo Offset.
NB. Il tool entra dalla curva di primo Offset, non dal bordo della regione da svuotare; quindi
l'entrata va calcolata sul primo Offset, non sulla curva di bordo della FlatRegion di Pocketing.
2) Verifico l'esistenza di lati aperti :
- Se esistono lati aperti -> cerco il lato più sensato su cui entrare
- 2.1) Guardo la curva originale ( senza estensione degli aperti) che delimita la regione di
svuotatura e cerco i lati aperti presenti in essa ; se mi posiziono a metà del lato aperto
e cerco il punto più vicino sulla curva di primo offset. Se quest'ultima è anch'essa aperta
e di lunghezza adeguata, l'entrata è valida
- 2.2) ( se 2.1 non accettabile ) Scorro tutte le curve di lato aperto sulla curva di primo
Offset e per ogni curva di lunghezza accettabile controllo la precedente e la successiva.
Ad ogni curva aperta viene assegnato uno "score", in base alla sua Lenght, alle Lenghts
delle curve adiacenti ( se aperte ) e ai loro angoli di adiacenza.
Se la curva aperta corrente ha uno "score" sufficiente, viene scelta come curva per l'entrata
- 2.3) ( se 2.2 non accettabile ) Considero le curve come tutte chiuse ( vedi sotto )
- Se non esistono dei lati aperti -> entro presso il lato chiuso più lungo
- 2.4)
*/
// 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( pCrvOffsAct->Clone()) ;
if ( IsNull( pCrv))
return false ;
// 1) *********************************************************************
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 ;
if ( ! AssignOpenCloseTmpPropToFirstOffsCurve( pCrv, PockParams, vCrvLoops, vIndex, bSomeOpen))
return false ;
// *********************************************************************
// se ho dei lati aperti...
double dLenMax = EPS_SMALL ;
int nCrvForMax = 0 ;
if ( bSomeOpen) {
// ... e sono al primo Step, cerco la curva Aperta più lunga ( scelgo la prima che trovo) ...
if ( ! ptEndPrec.IsValid()) {
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
int nTmpProp = 0 ;
double dLenAct = EPS_SMALL ;
if ( pCrv->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 1 &&
pCrv->GetCurve( u)->GetLength( dLenAct) && dLenAct > dLenMax) {
dLenMax = dLenAct ;
nCrvForMax = u ;
}
}
// il punto iniziale della curva sarà il punto iniziale della sottocurva trovata
pCrv->ChangeStartPoint( nCrvForMax) ;
}
// ... se invece non sono al primo Step, cerco la prima curva aperta più vicina al punto finale
else {
int nIndClosesOpenCrv = -1 ;
double dMinDist = INFINITO ;
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
if ( pCrv->GetCurve( u)->GetTempProp( 0) == 1) {
DistPointCurve DPC( ptEndPrec, *pCrv->GetCurve( u)) ;
double dCurrDist = INFINITO ;
if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) {
dMinDist = dCurrDist ;
nIndClosesOpenCrv = u ;
}
}
}
// il punto iniziale della curva sarà il punto iniziale della sottocurva trovata
pCrv->ChangeStartPoint( nIndClosesOpenCrv) ;
}
}
// creo un vettore con tutti i Loops della pSwfToWork per i quali, mediante l'Offset, non hanno
// generato alcuna curva presente nella pCrv ( quella da cui devo entrare)
ICRVCOMPOPOVECTOR vOtherCrv ;
for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) {
bool bOk = true ;
for ( int j = 0 ; j < int( vIndex.size()) && bOk ; ++ j) {
if ( i == vIndex[j])
bOk = false ;
}
if ( bOk)
vOtherCrv.emplace_back( vCrvLoops[i]->Clone()) ;
}
// cerchiamo un punto valido per l'entrata ...
bMidOut = false ;
if ( bSomeOpen) { // se ho dei lati aperti, cerco un parametro ideale per entrare
/* ( 2.1 ) */
bool bOK = false ;
if ( nOffs == 1 && pCrvOrig != nullptr && pCrvOrig->IsValid()) {
// in questo caso le curve a fagiolo dei lati aperti a sinistra e a destra potrebbero
// intersecarsi tra loro -> risulta difficile calcolare il versore direzione d'uscita
bOK = SetSpecialPtStartForOpenEdges( pCrvOrig, PockParams, pCrv, ptStart, vtMidOut, bMidOut) ;
}
if ( ! bOK) /* ( 2.2 ) */
bMidOut = GetParamOnOpenSide( pCrv, vOtherCrv, PockParams, 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)) ;
}
/* ( 2.3 | 2.4 ) */
if ( ! bMidOut) // alla peggio, ordino i lati lunghi per lunghezza e cerco un'entrata valida
AdjustContourStart( pCrv, PockParams, vOtherCrv, true, ptEndPrec) ;
// ora che ho deciso quale sia il punto iniziale, lo imposto effettivamente sulla curva di Offset passata alla funzione
pCrv->GetStartPoint( ptStart) ;
pCrvOffsAct->Clear() ;
pCrvOffsAct->CopyFrom( pCrv) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
SetAdvancedPtStartForPath( ICRVCOMPOPOVECTOR& vCrvOffsAct, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut,
bool& bMidOut, int& nIndex, int nOffs, ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops)
{
// controllo dei parametri
for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i)
if ( IsNull( vCrvOffsAct[i]) || ! vCrvOffsAct[i]->IsValid() || vCrvOffsAct[i]->GetCurveCount() == 0)
return false ;
if ( pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1)
return false ;
nIndex = 0 ;
// scorro le curve di primo Offset
// NB. in prima posizione ho il primo Offset del loop esterno, a seguire le isole
// NB. Se non riesco ad entrare in nessuna curva, allora tengo valida quella calcolata per il loop esterno
bool bMidOut_fist = false ;
Vector3d vtMidOut_first ;
Point3d ptStart_first ;
for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i) {
// cambio il suo punto d'inizio dell'Offset attuale
if ( SetPtStartForPath( vCrvOffsAct[i], PockParams, pSrfToWork, ptEndPrec, ptStart, vtMidOut,
bMidOut, nOffs, ( int( vCrvOrigChunkLoops.size()) < i) ? Get( vCrvOrigChunkLoops[i]) : nullptr))
vCrvOffsAct[i]->GetStartPoint( ptStart) ;
else
return false ;
// se ho trovato un'entrata valida da un lato aperto, ho finito
if ( bMidOut) {
nIndex = i ; // salvo l'indice del vettore
return true ;
}
// se sono nel loop esterno e non ho trovato un'entrata da un lato aperto, salvo i risultati ottenuti
else if ( i == 0) {
bMidOut_fist = bMidOut ;
vtMidOut_first = vtMidOut ;
ptStart_first = ptStart ;
}
}
// in questo caso non ho trovato un'entrata da un lato aperto in nessuna curva ( potrebbero essere tutte chiuse)
bMidOut = bMidOut_fist ;
vtMidOut = vtMidOut_first ;
ptStart = ptStart_first ;
return true ;
}
// ***************************************************************************
//---------------------------- CASI OTTIMIZZATI ------------------------------
// ***************************************************************************
//----------------------------------------------------------------------------
static bool
GetOptCrvIndex( const ISurfFlatRegion* pSfrOrig, const ISurfFlatRegion* pSfrChunk,
const PocketParams& PockParams, int nReg, ICRVCOMPOPOVECTOR& vCrvOrig)
{
/*
NB. La superficie da lavorare è stata estesa presso i lati aperti; cerco il Chunk
di riferimento sulla superficie originale ed estraggo le sue curve ( in prima posizione
il loop esterno e a seguire le isole); queste curve verranno utilizzate per la scelta
dei casi ottimizzati ( trapezi, circonferenze, ecc ecc..)
*/
// controllo dei parametri
if ( pSfrOrig == nullptr || ! pSfrOrig->IsValid() ||
pSfrChunk == nullptr || ! pSfrChunk->IsValid())
return false ;
// se la superficie originale ha un solo Chunk, allora le curve originali sono già trovate
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( 0) ; ++ l)
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( 0, l))) ;
if ( pSfrOrig->GetChunkCount() == 1)
return true ;
// vettore di indici dei chunk candidati
INTVECTOR vInds ;
// scorro tutti i Chunk della superficie orginaria
for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) {
// la classificazione può essere fatta semplicemente guardando il Loop esterno
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ;
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
return false ;
// classifico il Chunk attuale con la curva in esame
CRVCVECTOR ccClass ;
if ( pSfrChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) {
// se il Chunk attuale è stato esteso presso presso i lati aperti mi aspetto che esso non sia più
// grande del chunk originale ( se non ci sono lati aperti, allora coincide )
// se il ccClass non contiene parti classificate come esterne allora il Chunk di riferimento è il c-esimo
bool bIsThis = true ;
for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) {
if ( ccClass[k].nClass == CRVC_OUT)
bIsThis = false ;
}
if ( bIsThis)
vInds.push_back( c) ;
}
}
// se ho un solo indice allora ho già identificato le curve originali
if ( int( vInds.size()) == 1) {
vCrvOrig.clear() ;
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( vInds[0]) ; ++ l)
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( vInds[0], l))) ;
return true ;
}
// altrimenti...
/*
NB. Come parametro alla funzione è stato passato "nReg"; Facendo il primo Offset del Chunk attuale
questo verrà diviso in n sottoregioni; devo considerare la nReg-esima
Situazione: Estendendo presso i lati aperti, due ( o più) chunk si sono uniti formandone uno unico
Quando effettuo il primo offset ( del diametro del materiale + offset radiale), questo Chunk si splitterà in n Chunk;
Uno ( o più) di questi nuovi chunk potrebbe corrispondere ad un caso ottimizzato.
Devo recuperare il Chunk della superficie originaria ( che non essendo estesa presso i lati aperti
ha la geomtria originale)
*/
// effettuo un Offset del Chunk per simulare la creazione degli n Chunks
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
PtrOwner<ISurfFlatRegion> pSfrRefChunk( pSfrChunk->Clone()) ;
if ( IsNull( pSfrRefChunk) ||
! pSfrRefChunk->Offset( - dOffs, ICurve::OFF_FILLET))
return false ;
// clono il Chunk nReg-esimo se esiste
pSfrRefChunk.Set( pSfrRefChunk->CloneChunk( nReg)) ;
if ( IsNull( pSfrRefChunk))
return true ; // bisogna passare al ( pSfrChunk + 1)-esimo se esiste
// scorro tutti i Chunk della superficie orginaria e ripeto la classificazione
// NB. Questa volta posso fermarmi al primo Chunk classificato come non esterno
for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) {
// come prima recupero il Loop esterno
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ;
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
return false ;
// classificazione
CRVCVECTOR ccClass ;
if ( pSfrRefChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) {
bool bIsThis = true ;
for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) {
if ( ccClass[k].nClass == CRVC_OUT)
bIsThis = false ;
}
if ( bIsThis) {
vCrvOrig.clear() ;
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( c) ; ++ l)
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( c, l))) ;
return true ; // esco
}
}
}
return true ; // alla peggio non considero casi ottimizzati e svuoto normalmente...
}
//----------------------------------------------------------------------------
static bool
OptimizedSpiralCirle( const ICurveComposite* pCrvCompo, const double dToll, double& dRad,
Point3d& ptC, bool& bIsCirlce)
{
/* restituisce il centro e il raggio di una circonfereza che approssima pCrvCompo */
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || dToll < EPS_SMALL)
return false ;
dRad = 0. ;
ptC = P_INVALID ;
bIsCirlce = false ;
// se aperta, non è un cerchio e non è piana, esco
Plane3d plPock ;
if ( ! pCrvCompo->IsClosed() || ! pCrvCompo->IsFlat( plPock))
return true ;
// creo un sistema di riferimento centrato sulla curva
Point3d ptCentroid ;
Vector3d vtN ;
Frame3d frCurr ;
if ( ! pCrvCompo->GetCentroid( ptCentroid) ||
! pCrvCompo->GetExtrusion( vtN) ||
! frCurr.Set( ptCentroid, vtN) ||
! frCurr.IsValid())
return false ;
// porto la curva nel sistema di riferimento corrente ( dopo averla clonata)
PtrOwner<ICurveComposite> pCrvLoc( pCrvCompo->Clone()) ;
if ( IsNull( pCrvLoc) || ! pCrvLoc->IsValid() || ! pCrvLoc->ToLoc( frCurr))
return false ;
// approssimo la curva locale con una PolyLine
PolyLine pL ;
if ( ! pCrvLoc->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, pL) ||
pL.GetPointNbr() == 0)
return false ;
// salvo i punti ottenuti ( il punto finale sarà contenuto due volte)
PNTVECTOR vPts ;
Point3d ptNext ;
pL.GetFirstPoint( ptNext) ;
vPts.push_back( ptNext) ;
while ( pL.GetNextPoint( ptNext)) {
vPts.push_back( Media( vPts.back(), ptNext)) ; // inserisco il punto medio
vPts.push_back( ptNext) ; // inserisco il punto successivo
}
// per ogni coppia di punti calcolo la distanza massima e minima dal centro del cerchio locale ( ORIG)
double dMaxDist = 0. ;
double dMinDist = INFINITO ;
for ( int i = 0 ; i < int( floor( 0.5 * int( vPts.size()))) ; i = i + 2) {
PtrOwner<ICurveLine> pSeg( CreateCurveLine()) ;
if ( IsNull( pSeg) || ! pSeg->Set( vPts[i], vPts[i+1]))
return false ;
DistPointCurve DPC( ORIG, *pSeg) ;
double dCurrDist = 0. ;
if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist)
dMinDist = dCurrDist ;
dCurrDist = Dist( ORIG, vPts[i]) ;
if ( dCurrDist > dMaxDist)
dMaxDist = dCurrDist ;
}
// controllo se la distanza tra minima e massima sono simili ( in base alla tolleranza)
if ( abs( dMaxDist - dMinDist) > dToll)
return true ;
// imposto i parametri per la circonferenza
dRad = 0.5 * ( dMaxDist + dMinDist) ;
ptC = ptCentroid ;
bIsCirlce = true ;
return true ;
}
//----------------------------------------------------------------------------
static bool
CalcCircleSpiral( const Point3d& ptCen, const Vector3d& vtN, double dOutRad, double dIntRad,
const PocketParams& PockParams, ICurveComposite* pMCrv, ICurveComposite* pRCrv)
{
/* calcola il percorso di a spirale assieme al percorso di ritorno */
// raggio della circonferenza esterna
if ( dOutRad < 10 * EPS_SMALL)
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))
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 ( PockParams.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 * PockParams.dSideStep))) ;
if ( nStep % 2 == 0)
nStep += 1 ;
double dStep = ( dOutRad - dIntRad) / nStep ;
for ( int i = 1 ; i <= nStep ; ++ i) {
if ( ! ( i % 2 == 0))
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)
return false ;
// assegno la Feed
AssignFeedSpiralOpt( 0, PockParams, 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))
return false ;
pRCrv->AddCurve( Release( pArc2)) ;
// inverto e eventualmente sistemo archi
pRCrv->Invert() ;
}
return true ;
}
//----------------------------------------------------
static bool
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) ;
}
//----------------------------------------------------------------------------
static bool
GetBoxCrvOptTrap( const int nCrv, const ICurveComposite* pCrvCompo, const double dDiam, int& nType,
ICurveComposite* pCrvBox)
{
nType = - 1 ;
// prendo la curva
const ICurve* pCrvCurr = pCrvCompo->GetCurve( nCrv) ;
if ( pCrvCurr == nullptr)
return false ;
// controllo se lineare, altrimenti passo alla successiva
if ( pCrvCurr->GetType() != CRV_LINE)
return true ;
// prendo la direzione del tratto lineare
Vector3d vtDir ; pCrvCurr->GetStartDir( vtDir) ;
// prendo il punto iniziale
Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ;
// creo il riferimento basato su questo tratto
Frame3d frTrap ; frTrap.Set( ptStart, Z_AX, vtDir) ;
if ( ! frTrap.IsValid())
return false ;
// porto la curva Compo ( clonandola) nel frame e calcolo il suo BBox
BBox3d BBox ;
PtrOwner<ICurveComposite> pCrvCompo_c( pCrvCompo->Clone()) ;
if ( IsNull( pCrvCompo_c) || ! pCrvCompo_c->IsValid() ||
! pCrvCompo_c->ToLoc( frTrap) ||
! pCrvCompo_c->GetLocalBBox( BBox))
return false ;
// controllo dimY e dimX per il box
bool bDimYOk = BBox.GetDimY() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ;
bool bDimXOk = BBox.GetDimX() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ;
// controllo dimensioni ammissibili
if ( ! bDimXOk && ! bDimYOk)
return true ; // se nessuna
else if ( ! bDimXOk)
nType = 1 ; // se dimY
else if ( ! bDimYOk)
nType = 0 ; // se dimX
else
nType = 2 ; // se entrambe
// creo il rettangolo del Box
pCrvBox->Clear() ;
pCrvBox->AddPoint( BBox.GetMin()) ;
pCrvBox->AddLine( BBox.GetMin() + BBox.GetDimX() * X_AX) ;
pCrvBox->AddLine( BBox.GetMax()) ;
pCrvBox->AddLine( BBox.GetMax() - BBox.GetDimX() * X_AX) ;
pCrvBox->Close() ;
// determino il punto inziale della curva
if ( nType == 0)
pCrvBox->ChangeStartPoint( 1.) ;
pCrvBox->ToGlob( frTrap) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
PreparareTrapezoidTwoBases( const ICurveComposite* pCrvCompo, const double dDiam, const int nType, int& nBase,
int& nSecondBase, bool& bOk, ICurveComposite* pCrvTrap)
{
// controllo parametri
if ( pCrvCompo == nullptr)
return false ;
bOk = false ;
// le parti tra le due basi devono essere tutte omogenee ( o tutte aperte o tutte chiuse)
// pCrvTest0 sarà la parte destra, pCrvTest1 la parte sinistra
Point3d ptSB0, ptSB1 ,ptEB0, ptEB1 ;
PtrOwner<ICurveComposite> pCrvTest0( pCrvCompo->Clone()) ;
PtrOwner<ICurveComposite> pCrvTest1( pCrvCompo->Clone()) ;
pCrvCompo->GetCurve( nBase)->GetStartPoint( ptSB0) ;
pCrvCompo->GetCurve( nBase)->GetEndPoint( ptEB0) ;
pCrvCompo->GetCurve( nSecondBase)->GetStartPoint( ptSB1) ;
pCrvCompo->GetCurve( nSecondBase)->GetEndPoint( ptEB1) ;
pCrvTest0->ChangeStartPoint( nBase) ;
pCrvTest1->ChangeStartPoint( nSecondBase) ;
double dUTrim0, dUTrim1 ;
pCrvTest0->GetParamAtPoint( ptSB1, dUTrim0) ;
pCrvTest1->GetParamAtPoint( ptSB0, dUTrim1) ;
pCrvTest0->TrimStartEndAtParam( 1, dUTrim0) ;
pCrvTest1->TrimStartEndAtParam( 1, dUTrim1) ;
// controllo che la parte destra si uniforme per le TmpProp
for ( int u = 0 ; u < pCrvTest0->GetCurveCount() - 1 ; ++ u) {
int nPropAct, nPropSucc ;
if ( pCrvTest0->GetCurveTempProp( u, nPropAct, 0) &&
pCrvTest0->GetCurveTempProp( u + 1, nPropSucc, 0) &&
nPropAct != nPropSucc)
return true ; // se TmpProp diverse => non è un caso ottimizzato
}
// controllo che la parte sinistra sia uniforme per le TmpProp
for ( int u = 0 ; u < pCrvTest1->GetCurveCount() - 1 ; ++ u) {
int nPropAct, nPropSucc ;
if ( pCrvTest1->GetCurveTempProp( u, nPropAct, 0) &&
pCrvTest1->GetCurveTempProp( u + 1, nPropSucc, 0) &&
nPropAct != nPropSucc)
return true ; // se TmpProp diverse => non è un caso ottimizzato
}
// se lato destro aperto ( estendo il punto finale della base principale e il punto iniziale
// della base secondaria fino al lato destro del box)
bool bCopyRight = false ;
if ( pCrvTest0->GetCurve( 0)->GetTempProp( 0) == 1) {
if ( nType == 0) {
pCrvTrap->GetCurve( 0)->GetStartPoint( ptEB0) ;
pCrvTrap->GetCurve( 1)->GetStartPoint( ptSB1) ;
}
else {
pCrvTrap->GetCurve( 0)->GetEndPoint( ptEB0) ;
pCrvTrap->GetCurve( 1)->GetEndPoint( ptSB1) ;
}
}
// se lato destro chiuso
else
bCopyRight = true ;
// se lato sinistro aperto ( estendo il punto finale della base secondaria e il punto iniziale
// della base primaria dino al lato sinistro del box)
bool bCopyLeft = false ;
if ( pCrvTest1->GetCurve( 0)->GetTempProp( 0) == 1) {
if ( nType == 0) {
pCrvTrap->GetCurve( 2)->GetEndPoint( ptEB1) ;
pCrvTrap->GetCurve( 0)->GetStartPoint( ptSB0) ;
}
else {
pCrvTrap->GetCurve( 1)->GetEndPoint( ptEB1) ;
pCrvTrap->GetCurve( 2)->GetEndPoint( ptSB0) ;
}
}
// se lato sinistro chiuso
else
bCopyLeft = true ;
// creo la curva da restituire
pCrvTrap->Clear() ;
pCrvTrap->AddPoint( ptSB0) ;
pCrvTrap->AddLine( ptEB0) ;
nBase = 0 ;
if ( bCopyRight)
pCrvTrap->AddCurve( Release( pCrvTest0)) ;
else {
pCrvTrap->AddLine( ptSB1) ;
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta
}
nSecondBase = pCrvTrap->GetCurveCount() ;
pCrvTrap->AddLine( ptEB1) ;
if ( bCopyLeft)
pCrvTrap->AddCurve( Release( pCrvTest1)) ;
else {
pCrvTrap->Close() ;
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta
}
// verifico dimensione x della svuotatura nel caso tutto chiuso
if ( bCopyLeft && bCopyRight) {
double dLen0 = 0, dLen2 = 0 ;
pCrvTrap->GetCurve( nBase)->GetLength( dLen0) ;
pCrvTrap->GetCurve( nSecondBase)->GetLength( dLen2) ;
if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL) {
pCrvTrap->Clear() ;
return true ;
}
}
bOk = true ;
return true ;
}
//----------------------------------------------------------------------------
static bool
GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTrap,
Frame3d& frTrap, const PocketParams& PockParams, double& dPocketSize, int& nBase, int& nSecondBase)
{
// controllo parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// diametro reale da tenere in considerazione
double dDiam = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ;
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)) {
// data la tolleranza, creo la curva a trapezio dai 4 punti
pCrvTrap->AddPoint( pt) ;
pCrvTrap->AddLine( pt + vtDir) ;
pCrvTrap->AddLine( pt + vtOtherDir + vtB2) ;
pCrvTrap->AddLine( pt + vtOtherDir) ;
pCrvTrap->Close() ;
// 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() ;
if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap))
return false ;
bool bBaseOpen = false ;
bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == 1 || pCrvTrap->GetCurve( 2)->GetTempProp( 0) == 1) ;
// controllo dimensioni della svuotatura
if ( ! ( bBaseOpen && dPocketSize < dDiam + EPS_SMALL) &&
abs( dPocketSize - dDiam) > EPS_SMALL) {
pCrvTrap->Clear() ;
return true ;
}
// recupero flag aperto/chiuso dei lati
if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == 0 && pCrvTrap->GetCurve( 3)->GetTempProp( 0) == 0) {
double dLen0, dLen2 ;
pCrvTrap->GetCurve( 0)->GetLength( dLen0) ;
pCrvTrap->GetCurve( 2)->GetLength( dLen2) ;
if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL)
pCrvTrap->Clear() ;
}
// imposto le basi
nBase = 0 ;
nSecondBase = 2 ;
return true ;
}
// controllo il numero di lati chiusi e salvo i loro indici
int nClosedSide = 0 ;
INTVECTOR vIndClosedSides ;
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
int nTmpProp ;
if ( pCrvCompo->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 0) {
++ nClosedSide ;
vIndClosedSides.push_back( u) ;
}
}
// se tutti lati aperti
switch ( nClosedSide) {
// TUTTI I LATI SONO APERTI
case 0 : {
// ricavo il box minimo della curva aperta ( passo dalla polyLine)
PolyLine PL ; Point3d ptCen ; double dWidth ;
if ( ! pCrvCompo->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) ||
! PL.GetMinAreaRectangleXY( ptCen, vtDir, dWidth, dPocketSize))
return false ;
// controllo dimY ( dHeight), se troppo estesa, non è un caso ottimizzato
if ( dPocketSize > dDiam + TOL_TRAPEZOID)
return true ;
// inverto il frame attuale
frTrap.Set( ptCen, Z_AX, vtDir) ;
if ( ! frTrap.IsValid())
return false ;
// creo il rettangolo del Box
pCrvTrap->AddPoint( Point3d( - 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
pCrvTrap->AddLine( Point3d( - 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
pCrvTrap->Close() ;
pCrvTrap->ToGlob( frTrap) ;
Point3d ptNewOrig ; pCrvTrap->GetStartPoint( ptNewOrig) ;
frTrap.Set( ptNewOrig, Z_AX, vtDir) ;
// imposto tutte le 4 curve come aperte
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
// imposto le basi
nBase = 0 ;
nSecondBase = 2 ;
}
break ;
// 1 LATO CHIUSO ( LINEARE)
case 1 : {
int nType = -1 ;
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
if ( nType != -1) {
// imposto tutte le curve come aperte tranne la prima ( estendo il solo lato chiuso come base del Box)
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, u == 0 ? 0 : 1, 0) ;
// memorizzo la dimensione di svuotatura
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
// imposto le basi
nBase = 0 ;
nSecondBase = 2 ;
}
}
break ;
// 2 LATI CHIUSI ( LINEARI, CONSECUTIVI O PARALLELI)
case 2 : {
// controllo se entrambi sono lineari
if ( pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE &&
pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE) {
// se lineari, ricavo i punti estremanti e le direzioni
Point3d ptS0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartPoint( ptS0) ;
Point3d ptS1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartPoint( ptS1) ;
Point3d ptE0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetEndPoint( ptE0) ;
Point3d ptE1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetEndPoint( ptE1) ;
Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ;
Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ;
// CASO PARALLELI ( la base è indifferente)
if ( AreOppositeVectorEpsilon( vtDir0, vtDir1, 5 * EPS_SMALL)) {
int nType = -1 ;
// prendo come base il primo chiuso
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
if ( nType != -1) {
// memorizzo la dimensione di svuotatura
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
// controllo che la distanza tra i due chiusi sia effettivamente circa il raggio
double dDist = 0. ;
DistPointCurve DPL( ptS0, *pCrvCompo->GetCurve( vIndClosedSides[1]), false) ;
if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) {
// imposto tutte le curve di indice dispari aperte
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, u % 2 == 0 ? 0 : 1, 0) ;
// imposto le basi
nBase = 0 ;
nSecondBase = 2 ;
}
else
pCrvTrap->Clear() ;
}
}
// CASO CONSECUTIVI
bool bOk_0_1 = AreSamePointApprox( ptS1, ptE0) ; // chiuso1 . chiuso0
bool bOk_1_0 = AreSamePointApprox( ptS0, ptE1) ; // chiuso0 . chiuso1
if ( bOk_0_1 || bOk_1_0) {
// provo con il primo lato
int nType = -1 ;
// prendo come base il primo chiuso
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
if ( nType == -1) {
// provo con l'altro
if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
if ( nType != -1) {
// spaw tra gli indici e flags
swap( vIndClosedSides[0], vIndClosedSides[1]) ;
swap( bOk_0_1, bOk_1_0) ;
}
}
Point3d ptH1, ptH2 ;
if ( nType == 1 || nType == 2) {
double dLen1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetLength( dLen1) ;
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
// i lati inclinati chiusi definiscono il Box
if ( abs( dLen1 * ( vtDir0 ^ vtDir1).Len() - dPocketSize) < TOL_TRAPEZOID) {
// creo la curva a trapezio
if ( bOk_0_1) {
pCrvTrap->GetCurve( 2)->GetEndPoint( ptH1) ;
pCrvTrap->GetCurve( 3)->GetEndPoint( ptH2) ;
pCrvTrap->Clear() ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
pCrvTrap->AddLine( ptH1) ;
pCrvTrap->AddLine( ptH2) ;
pCrvTrap->Close() ;
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ;
}
else {
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
pCrvTrap->Clear() ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
pCrvTrap->AddLine( ptH1) ;
pCrvTrap->AddLine( ptH2) ;
pCrvTrap->AddLine( ptS1) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
pCrvTrap->SetCurveTempProp( 0, 0, 0) ;
for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u)
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ;
}
// imposto le basi
nBase = 0 ;
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
}
else
pCrvTrap->Clear() ;
}
else if ( nType == 0) {
if ( bOk_0_1) {
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
pCrvTrap->Clear() ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
pCrvTrap->AddLine( ptH1) ;
pCrvTrap->AddLine( ptH2) ;
pCrvTrap->Close() ;
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ;
pCrvTrap->ChangeStartPoint( 1.) ;
// imposto le basi
nBase = 0 ;
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
}
else {
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
pCrvTrap->AddLine( ptH1) ;
pCrvTrap->AddLine( ptH2) ;
pCrvTrap->AddLine( ptS1) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
pCrvTrap->SetCurveTempProp( 0, 0, 0) ;
for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u)
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ;
pCrvTrap->ChangeStartPoint( 1.) ;
// imposto le basi
nBase = 0 ;
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
}
}
}
}
}
break ;
// 3 LATI CHIUSI ( LINEARI e CONSECUTIVI)
case 3 : {
// prendo i 3 lati chiusi
const ICurve* pCrv0 = pCrvCompo->GetCurve( vIndClosedSides[0]) ;
const ICurve* pCrv1 = pCrvCompo->GetCurve( vIndClosedSides[1]) ;
const ICurve* pCrv2 = pCrvCompo->GetCurve( vIndClosedSides[2]) ;
if ( pCrv0 == nullptr || pCrv1 == nullptr || pCrv2 == nullptr)
return false ;
// controllo che siano lineari
if ( pCrv0->GetType() == CRV_LINE && pCrv1->GetType() == CRV_LINE && pCrv2->GetType() == CRV_LINE) {
// prendo i punti iniziali e finali
Point3d ptStart0 ; pCrv0->GetStartPoint( ptStart0) ;
Point3d ptEnd0 ; pCrv0->GetEndPoint( ptEnd0) ;
Point3d ptStart1 ; pCrv1->GetStartPoint( ptStart1) ;
Point3d ptEnd1 ; pCrv1->GetEndPoint( ptEnd1) ;
Point3d ptStart2 ; pCrv2->GetStartPoint( ptStart2) ;
Point3d ptEnd2 ; pCrv2->GetEndPoint( ptEnd2) ;
// controllo che siano consecutivi e cambio l'ordine se necessario
bool bOk = true ;
if ( AreSamePointApprox( ptEnd0, ptStart1) && AreSamePointApprox( ptEnd1, ptStart2))
;
else if ( AreSamePointApprox( ptEnd1, ptStart2) && AreSamePointApprox( ptEnd2, ptStart0)) {
swap( vIndClosedSides[0], vIndClosedSides[1]) ;
swap( vIndClosedSides[1], vIndClosedSides[2]) ;
}
else if ( AreSamePointApprox( ptEnd2, ptStart0) && AreSamePointApprox( ptEnd0, ptStart1)) {
swap( vIndClosedSides[0], vIndClosedSides[2]) ;
swap( vIndClosedSides[2], vIndClosedSides[1]) ;
}
else // se non consecutivi
bOk = false ;
// se i tre lati sono ( mediante lo swap) consecutivi...
if ( bOk) {
// controllo se la dimensione Y del lato 1 è valida ( il resto è gestito di seguito)
int nType = -1 ;
if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
if ( nType == 1) {
// bisgna controllare la lunghezza dei chiusi
double dLen0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetLength( dLen0) ;
double dLen2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetLength( dLen2) ;
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ;
Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ;
Vector3d vtDir2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetStartDir( vtDir2) ;
// i lati inclinati chiusi definiscono il Box
if ( abs( dLen2 * ( vtDir1 ^ vtDir2).Len() - dPocketSize) < TOL_TRAPEZOID &&
abs( dLen0 * ( vtDir1 ^ vtDir0).Len() - dPocketSize) < TOL_TRAPEZOID) {
// creo la curva a trapezio
pCrvTrap->Clear() ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[2])->Clone()) ;
pCrvTrap->Close() ;
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, u < 3 ? 0 : 1 , 1) ;
pCrvTrap->ChangeStartPoint( 1.) ;
// imposto le basi
nBase = 0 ;
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
}
}
}
}
}
}
// se non ho trovato delle basi, allora cerco in maniera generale un trapezoide ( se esiste)
if ( nBase == -1) {
bool bOK = false ;
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() && !bOK ; ++ u) {
// cerco un lato chiuso ( esiste per forza)
if ( pCrvCompo->GetCurve( u)->GetTempProp( 0) == 0) {
// controllo se il lato corrente può essere una base
int nType = -1 ;
if ( ! GetBoxCrvOptTrap( u, pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
// se è una base valida
if ( nType != -1) {
// ricavo la dimensione di svuotatura
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
// ricavo la sua direzione iniziale
Vector3d vtBaseDir ; pCrvCompo->GetCurve( u)->GetStartDir( vtBaseDir) ;
// ricavo il suo punto iniziale
Point3d ptBaseStart ; pCrvCompo->GetCurve( u)->GetStartPoint( ptBaseStart) ;
// ricavo il suo punto finale
Point3d ptBaseEnd ; pCrvCompo->GetCurve( u)->GetEndPoint( ptBaseEnd) ;
// cerco il lato chiuso lineare parallelo ad esso e distante circa dPocketSize
for ( int uu = 0 ; uu < pCrvCompo->GetCurveCount() && !bOK ; ++ uu) {
if ( uu == u ||
pCrvCompo->GetCurve( uu)->GetType() != CRV_LINE ||
pCrvCompo->GetCurve( uu)->GetTempProp() != 0)
continue ;
Vector3d vtSecondBaseDir ; pCrvCompo->GetCurve( uu)->GetStartDir( vtSecondBaseDir) ;
if ( ! AreOppositeVectorEpsilon( vtBaseDir, vtSecondBaseDir, 5 * EPS_SMALL))
continue ;
Point3d ptSecondBaseStart ; pCrvCompo->GetCurve( uu)->GetStartPoint( ptSecondBaseStart) ;
double dDist = 0. ;
DistPointCurve DPL( ptBaseStart, *pCrvCompo->GetCurve( uu), false) ;
if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) {
nBase = u ;
nSecondBase = uu ;
if ( ! PreparareTrapezoidTwoBases( pCrvCompo, dDiam, nType, nBase, nSecondBase, bOK, pCrvTrap))
return false ;
}
}
}
}
}
}
// la curva a trapezio deve evere almeno 4 lati
if ( pCrvTrap->GetCurveCount() < 4 || nBase == -1 || nSecondBase == -1) {
pCrvTrap->Clear() ;
return true ;
}
// ricostruisco il frame del trapezio
Point3d ptS ; pCrvTrap->GetStartPoint( ptS) ;
Vector3d vtS ; pCrvTrap->GetStartDir( vtS) ;
if ( ! frTrap.Set( ptS, Z_AX, vtS) || ! frTrap.IsValid())
return false ;
// se parametro MaxOptSize non compatibile => non è ottimizzato
if ( PockParams.dMaxOptSize > EPS_SMALL && dPocketSize > PockParams.dMaxOptSize)
pCrvTrap->Clear() ;
return true ;
}
//----------------------------------------------------
static bool
SpecialAdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const bool bEvenClosed,
const ICurveComposite* pCrvPocket, const PocketParams& PockParams)
{
// parametri
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
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 ;
}
//------------------------------------------------------
static bool
CalcTrapezoidSpiralXCoord( const ICurveComposite* pCrvPocket, int nBase, int nSecondBase,
bool bStart, double dYCoord, double& dXCoord, double dPocketSize,
const PocketParams& PockParams)
{
// parametri
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
double dRad = 0.5 * dDiam + dOffsR ;
// 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))
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 * TOL_TRAPEZOID * Y_AX, - 90 , 2 * dPocketSize) ;
else
pCrvVertLine->SetPDL( Box3d.GetMin() - 5 * TOL_TRAPEZOID * 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 ;
}
//----------------------------------------------------
static bool
AdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const ICurveComposite* pCrvPocket,
const PocketParams& PockParams, bool bStart)
{
// parametri
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
double dRad = 0.5 * dDiam + dOffsR ;
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 ;
}
//----------------------------------------------------
static bool
CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double dPocketSize, int nBase,
int nSecondBase, const PocketParams& PockParams, ICurveComposite* pMCrv, ICurveComposite* pRCrv, bool& bOptimizedTrap)
{
// parametri
double dDiam = 2 * PockParams.dRad ;
double dOffsR = PockParams.dRadialOffset ;
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 > PockParams.dRad * 2 + 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, PockParams)) {
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, PockParams))
return false ;
if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, false, dYCoord, dXCoordEnd,
dPocketSize, PockParams))
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( - PockParams.dRad - PockParams.dRadialOffset) ||
! pLine3->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset))
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, PockParams, true)) {
pMCrv->Clear() ;
return false ;
}
if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == 0 &&
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, 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 ( ! PockParams.bInvert) {
pMCrv->Invert() ;
// inverto le proprietà in modo che nProp3 sia sempre legata al punto iniziale e nProp1 a quello finale
swap( vnProp[1], vnProp[3]) ;
}
// segno i lati aperti come temp prop della curva
int nOpenEdges = vnProp[0] + vnProp[1] * 2 + vnProp[2] * 4 + vnProp[3] * 8 ;
pMCrv->SetTempProp( nOpenEdges, 0) ;
pMCrv->SetExtrusion( vtExtr) ;
bOptimizedTrap = true ;
return true ;
}
//----------------------------------------------------------------------------
static bool
ModifyCurveToSmoothed( ICurveComposite* pCrv, const PocketParams& PockParams, double dRightLen, double dLeftLen, bool bAsParam)
{
// se non richiesto non faccio nulla
if ( ! PockParams.bSmooth)
return true ;
// 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 true ;
// 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 ( limite superiore valido)
int nMaxTestForArcs = 3 ; // tentativi per creare l'arco
int nIterForArcs = 0 ; // numero di intersezioni con altre curve
bool IntersBTWArcs = false ; // caso particolare in cui due archi di raccordo si intersecano causa curva piccola
// creo l'arco di raccordo e controllo che non sia troppo piccolo
PtrOwner<ICurveArc> pCrvArc( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ;
double dArcLen ;
if ( ! IsNull( pCrvArc) && pCrvArc->IsValid() && ( ! pCrvArc->GetLength( dArcLen) || dArcLen < 5 * EPS_SMALL)) {
vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco nullo
vArcsToJump.push_back( i) ; // da scartare
continue ;
}
// dal secondo arco in poi controllo che non ci siano intersezioni tra essi
if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) {
IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ;
if ( intCCH.GetIntersCount() > 0 )
IntersBTWArcs = true ;
}
// se ho intersezioni tra archi o l'arco creato non è valido, allora provo altre nMaxTestForArcs volte a ricrearlo avvicinando i punti
while (( IsNull( pCrvArc) || IntersBTWArcs) && nIterForArcs < nMaxTestForArcs) {
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<CurveComposite> pCrvCO_temp( CreateBasicCurveComposite()) ;
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) ;
}
// restituisco
pCrv->Clear() ;
pCrv->AddCurve( Release( pCrvCO_temp)) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, const ICRVCOMPOPOVECTOR& vOffIslands,
ICurveComposite* pCrvLink)
{
// pulisco
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<CurveComposite> pCompoLine( CreateBasicCurveComposite()) ;
if ( ! pCompoLine->AddPoint( ptStart) || ! pCompoLine->AddLine( ptEnd))
return false ;
pCompoLine->SetExtrusion( vtExtr) ;
// la porto in locale al riferimento della svuotatura
CurveLocal LineLoc( pCompoLine, 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<CurveComposite> pCompo( pCompoLine->Clone()) ;
if ( IsNull( pCompo) )
return false ;
if ( ! pCompo->LocToLoc( GLOB_FRM, frLoc) )
return false;
// memorizzo il tratto lineare nel caso qualche operazione fallisse
PtrOwner<CurveComposite> pCompoHelp( pCompoLine->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 ;
// per ogni intersezione j con l'offset dell'isola i
for ( int j = 0 ; j < int( ccClass.size()) ; ++j ) {
// se ho intersezione spezzo il segmento
if ( ccClass[j].nClass == CRVC_OUT) {
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( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ;
PtrOwner<ICurve> pCrvB( 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)) ;
}
}
// se non interseco
else {
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 ;
}
//-----------------------------------------------------------------------------
static bool
ModifyBiArc( ICurve* pBiArcLink, double dToll, ICurveComposite* pNewBiArc)
{
// controllo dei parametri
if ( pBiArcLink == nullptr)
return false ;
if ( dToll > 0.99 || dToll < 0.01)
return false ;
pNewBiArc->Clear() ;
// dominio
double dUS, dUE ;
pBiArcLink->GetDomain( dUS, dUE) ;
// prendo i due archi della curva BiArco
PtrOwner<ICurve> pArc1( pBiArcLink->CopyParamRange( dUS, ( dUS + dUE) / 2)) ;
PtrOwner<ICurve> pArc2( 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( pArc1->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1
PtrOwner<ICurve> pArc1B( pArc1->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2
Point3d pt1A, pt1B ;
pArc1A->GetEndPoint( pt1A) ;
pArc1B->GetStartPoint( pt1B) ;
PtrOwner<CurveLine> pLine1( CreateBasicCurveLine()) ;
pLine1->Set( pt1A, pt1B) ;
// secondo pezzo
pArc2->GetDomain( dUS, dUE) ;
PtrOwner<ICurve> pArc2A( pArc2->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1
PtrOwner<ICurve> pArc2B( pArc2->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2
Point3d pt2A, pt2B ;
pArc2A->GetEndPoint( pt2A) ;
pArc2B->GetStartPoint( pt2B) ;
PtrOwner<CurveLine> pLine2( CreateBasicCurveLine()) ;
pLine2->Set( pt2A, pt2B) ;
// ricostruisco il nuovo link
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 ;
}
//------------------------------------------------------------------------------
static bool
CalcBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd,
const Vector3d& vtEnd, double dParMeet, const ICRVCOMPOPOVECTOR& vOffIslands,
const PocketParams& PockParams, ICurveComposite* pCrvLink)
{
// se senza smusso, ritorno tratto lineare
if ( ! PockParams.bSmooth)
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, 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<CurveArc> pCrvCir( CreateBasicCurveArc()) ;
if ( IsNull( pCrvCir))
return false ;
pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, PockParams.bInvert ? 360 : - 360, 0, Z_AX) ;
pBiArcLink.Set( pCrvCir->Clone()) ;
pCrvLink->AddCurve( pBiArcLink->Clone()) ;
return true ;
}
if ( IsNull( pBiArcLink))
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
// se BiArco troppo grande allora lo modifico
double dLenBiArc ;
pBiArcLink->GetLength( dLenBiArc) ;
if ( dLenBiArc > 200 * 1000 * EPS_SMALL) {
PtrOwner<CurveComposite> pCrvNewBiArcS( CreateBasicCurveComposite()) ;
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 ;
// per ogni intersezione j con l'offset dell'isola i
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
// se ho intersezione spezzo il segmento
if ( ccClass[j].nClass == CRVC_OUT && int( ccClass.size()) > 1) {
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( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ;
PtrOwner<ICurve> pCrvB( 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) ;
}
}
// se non interseco
else {
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 ;
}
}
PtrOwner<ICurveComposite> pCrvCompo_noSmooth( pCompo->Clone()) ;
if ( ! ModifyCurveToSmoothed( pCompo, PockParams, 0.05, 0.05, true) ||
! pCompo->IsValid())
pCompo.Set( pCrvCompo_noSmooth) ;
pCrvLink->AddCurve( Release( pCompo)) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, const ICRVCOMPOPOVECTOR& vOffsCL,
const ICRVCOMPOPOVECTOR& vFirstOffset, const PocketParams& PockParams, ICurveComposite* pCrvLink,
double dLenPercS, double dLenPercE, int nMaxIter)
{
// controllo i parametri
if ( pCrvS == nullptr || pCrvE == nullptr )
return false ;
pCrvLink->Clear() ;
// curva finale da restituire
PtrOwner<CurveComposite> ptCrvFinal( CreateBasicCurveComposite()) ;
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 ... ( si per bordi esterni che per isole)
for ( int i = 0 ; i < int( vFirstOffset.size()) ; ++ i) {
if ( vFirstOffset[i]->IsPointOn( ptSS) && vFirstOffset[i]->IsPointOn( ptSE))
dLenPercS = 0 ;
if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE))
dLenPercE = 0 ;
}
double dLStepS = ( dLenPercS < EPS_SMALL ? 0. : PockParams.dRad) ;
double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : PockParams.dRad) ;
dLenE = 0 ;
// calcolo i possibili BiArchi tra le due curve
int nIter = 0 ;
if ( ! PockParams.bSmooth)
nMaxIter = 1 ;
// taglio la curva al massimo nMaxIter-volte e mi fermo al taglio più opportuno
while ( nIter < nMaxIter) {
// calcolo il BiArco
PtrOwner<CurveComposite> ptBiArc( CreateBasicCurveComposite()) ;
if ( ! CalcBoundedSmoothedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, PockParams, ptBiArc) ||
ptBiArc->GetCurveCount() == 0)
return false ;
ptCrvFinal->Clear() ;
ptCrvFinal.Set( ptBiArc->Clone()) ;
if ( dLenPercE == 0 && dLenPercS == 0 )
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( Release( ptCrvFinal)) ; // ultimo arco valido trovato
return true ;
}
//----------------------------------------------------------
static bool
GetUnclearedRegion( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vCrvs, ICURVEPOVECTOR& vLinks,
const ICurveComposite* pCrv_orig, const PocketParams& PockParams, ISurfFlatRegion* pSrfToCut)
{
// controllo dei parametri
if ( vFirstOffs.empty() || 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( Release( pCrvIntLoop)))
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 = ( PockParams.nType == POCKET_SPIRALIN ? 0 : int( vCrvs.size()) - 1) ;
( PockParams.nType == POCKET_SPIRALIN ? i < int( vCrvs.size()) : i >= 0 ) ;
( PockParams.nType == POCKET_SPIRALIN ? ++ i : -- i)) {
// ================= LINK ================================
// ( Il primo link è nullo, inatti non vi è nessun raccordo per raggiungere il primo Offset)
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, PockParams, 2 * PockParams.dRad / 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), PockParams.dRad + 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, PockParams, 2 * PockParams.dRad / 3))
return false ;
// aggiorno la superificie svuotata
PtrOwner<ICurveComposite> pCrvOffs_i( vCrvs[i]->Clone()) ;
if ( IsNull( pCrvOffs_i))
return false ;
PtrOwner<ISurfFlatRegion> pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( Release( pCrvOffs_i) , PockParams.dRad + 5 * EPS_SMALL, false, false)) ;
if ( ! IsNull( pSrfToolRegOffi)) {
if ( ! pSrfTool_Offs->IsValid() || pSrfTool_Offs->GetChunkCount() == 0)
pSrfTool_Offs.Set( 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 ;
}
//----------------------------------------------------------------------------
static bool
RemoveFirstLoopFromSfr( ISurfFlatRegion* pSrfOrig)
{
// controllo dei parametri
if ( pSrfOrig == nullptr)
return true ;
// superficie da restituire
PtrOwner<SurfFlatRegion> pSrfRes( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSrfRes))
return false ;
// scorro tutti i chunk tranne il primo ( escludo quindi il primo chunk)
for ( int c = 1 ; c < pSrfOrig->GetChunkCount() ; ++ c) {
PtrOwner<SurfFlatRegion> pSrfHelp( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSrfHelp))
return false ;
pSrfHelp->AddExtLoop( pSrfOrig->GetLoop( c, 0)) ;
if ( c == 1)
pSrfRes.Set( pSrfHelp) ;
else
pSrfRes->Add( *pSrfHelp) ;
}
// restituisco
pSrfOrig->Clear() ;
pSrfOrig->CopyFrom( pSrfRes) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
RemoveExtraPartByMedialAxis( const ISurfFlatRegion* pChunkToCut, ICRVCOMPOPOVECTOR& vOffsFirstCurve,
const PocketParams& PockParams, 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<CurveComposite> pCrvStepByStepPath( CreateBasicCurveComposite()) ;
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 superficie che racchiude il mio tool
PtrOwner<CurveArc> pCrvToolShape( CreateBasicCurveArc()) ;
if ( IsNull( pCrvToolShape))
return false ;
pCrvToolShape->SetXY( ptC, PockParams.dRad + 5 * EPS_SMALL) ;
PtrOwner<SurfFlatRegion> pSrfTool( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSrfTool) || ! pSrfTool->AddExtLoop( Release( pCrvToolShape)) ||
! pSrfTool->IsValid())
break ;
PtrOwner<SurfFlatRegion> pSrfTest( CloneBasicSurfFlatRegion( pSrfBiggerChunk)) ;
if ( IsNull( pSrfTest))
return false ;
pSrfTest->Subtract( *pSrfTool) ;
if ( IsNull( pSrfTest) || ! pSrfTest->IsValid() || ! pSrfTest->GetArea( dArea) || dArea < 10 * EPS_SMALL ||
bForceCentroid) {
// se prima iterazione -> ritorno il centroide con punto
if ( nIter == 1) {
nOptFlag = 1 ;
ptCentroid = ptC ;
return true ;
}
// se non sono alla prima iterazione -> memorizzo il centroide nel vettore
else {
// 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<CurveComposite> pCrvMedAx( CreateBasicCurveComposite()) ;
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 ;
}
// abbellisco la curva
pCrvMedAx->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
PolyArc PlMedAxArc ;
pCrvMedAx->ApproxWithArcsEx( 500 * EPS_SMALL, ANG_TOL_STD_DEG, 20.0, PlMedAxArc) ;
pCrvMedAx->Clear() ;
if( ! pCrvMedAx->FromPolyArc( PlMedAxArc)) {
bForceCentroid = true ;
-- nIter ;
continue ;
}
// smusso la curva
ModifyCurveToSmoothed( pCrvMedAx, PockParams, 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), PockParams.dRad + 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 ( vCrvCoMedAxi.empty() && vPtCentroid.empty()) {
nOptFlag = 0 ;
return true ;
}
// ora collego la varie curve medial Axis trovate tra loro (ottenendo quindi una curva che svuota tutta la regione)
// curva che collega primo e ultimo medial Axis
PtrOwner<CurveComposite> pCrvCoBackLink( CreateBasicCurveComposite()) ;
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<CurveComposite> pCrvLink( CreateBasicCurveComposite()) ;
if ( IsNull( pCrvLink))
return false ;
if ( ! CalcBoundedSmoothedLink( ptS, vtS, ptE, vtE, 0.5, vOffsFirstCurve, PockParams, 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) &&
! CalcBoundedSmoothedLink( ptEOriginal, vtEOriginal, ptSOriginal, vtSOriginal, 0.5, vOffsFirstCurve,
PockParams, pCrvCoBackLink) &&
! 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<CurveComposite> pCrvA( GetBasicCurveComposite( pCrvPath->CopyParamRange( 0, dU))) ;
if ( IsNull( pCrvA))
return false ;
PtrOwner<CurveComposite> pCrvB( GetBasicCurveComposite( 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<CurveComposite> pCrvPath1( CreateBasicCurveComposite()) ;
if ( IsNull( pCrvPath1))
return false ;
if ( ! CalcBoundedSmoothedLink( ptClosestOnPath, vtTanCpt, vPtCentroid[i], vtTanCpt, 0,
vOffsFirstCurve, PockParams, pCrvPath1) &&
! 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 ;
}
//---------------------------------------------------------------------------
static bool
CutOffsetToClosestPoint( ICRVCOMPOPOVECTOR& vCurves, const Point3d& ptFocus,
ICurveComposite* pCrv1, ICurveComposite* pCrv2, int& nIndex)
{
// controllo di avere almeno un offset...
if ( vCurves.empty())
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 ;
}
//---------------------------------------------------------------------------
static bool
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 ;
}
//------------------------------------------------------------------------------
static bool
CutCurveByOffsets( ICurveComposite* pCurve, ICRVCOMPOPOVECTOR& vOffs)
{
// controllo parametri ingresso
if ( vOffs.empty())
return true ;
// vettore di curve ausiliario
ICRVCOMPOPOVECTOR vOffOrig( vOffs.size()) ;
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i)
vOffOrig[i].Set( vOffs[i]->Clone()) ;
// punto iniziale corrente
Point3d ptStart ;
if ( ! pCurve->GetStartPoint( ptStart))
return false ;
// curve d'appoggio
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 ;
// scorro tutte le curve
for ( int i = 0; i < int( vOffOrig.size()) ; ++ i) {
// controllo se la curva è quella attuale
if ( vOffOrig[i]->IsPointOn( ptStart))
pOffCheck.Set( vOffOrig[i]->Clone()) ;
// classificazione
CRVCVECTOR ccClass ;
IntersCurveCurve intCC( *pCompo, *vOffOrig[i]) ;
intCC.GetCurveClassification( 0, 10 * EPS_SMALL, ccClass) ;
if ( ! pCompoHelp->Clear())
return false ;
// se In/Out
if ( ccClass.size() > 1) {
if ( ccClass[0].nClass == CRVC_IN)
nTypeOfCut = CRVC_OUT ;
else
nTypeOfCut = CRVC_IN ;
// scorro le classificazioni ottenute
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( vOffOrig[i]->CopyParamRange( dOffS, dOffE)) ;
PtrOwner<ICurve> pCrvB( 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()) ;
}
}
// restituisco la nuova curva
pCurve->Clear() ;
pCurve->AddCurve( Release( pCompo)) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
GetNewCurvetWithCentroid( const ICurveComposite* pCrvH1, const ICurveComposite* pCrvH2,
const PocketParams& PockParams, Point3d& ptC,
bool bCir, ICRVCOMPOPOVECTOR& VFirstOff, ICurveComposite* pCrvNewCurve)
{
// creazione dei due possibili percorsi per raggiungere il centroide
PtrOwner<ICurveComposite> pPath1( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pPath2( CreateCurveComposite()) ;
if ( IsNull( pPath1) || IsNull( pPath2))
return false ;
pCrvNewCurve->Clear() ;
Point3d ptS, ptE = ptC ;
Vector3d vtTanS, vtTanE ;
// creo una circonferenza
if ( bCir) {
// 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 ( ! CalcBoundedSmoothedLink( ptS, vtTanS, ptE, vtTanE, 0, VFirstOff, PockParams, 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
AssignMinFeed( pPath1, PockParams) ;
// 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, PockParams, 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 ;
}
// creo un BiArco
else {
// 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 ( ! CalcBoundedSmoothedLink( ptS, vtTanS, ptE, vtTanS, 0.5, VFirstOff, PockParams, pPath1) ||
! CalcBoundedSmoothedLink( ptE, vtTanS, ptS, vtTanS, 0.5, VFirstOff, PockParams, pPath2))
return false ;
// assegno la Feed a queste due nuove curve
AssignMinFeed( pPath1, PockParams) ;
AssignMinFeed( pPath2, PockParams) ;
// 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 ;
}
//----------------------------------------------------------------------------
static bool
ManageSmoothAndAutoInters( ICurveComposite* pCrv, ICurveComposite* pCrvPath, ICurveComposite* pPath1,
const PocketParams& PockParams, 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 ; // angolo incrementale nel caso di autointersezioni
while ( ! bFound && nIter < 45) {
vTanE.Rotate( Z_AX, 90 - nIter) ; // vettore perpendicolare
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
pLine->SetPVL( ptHS, vTanE, ( 2 * PockParams.dRad) / 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, ( 2 * PockParams.dRad) / 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, PockParams, 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 ;
}
//----------------------------------------------------------------------------
static bool
GetNewCurvetWithPath( const ICurveComposite* pCrvH1, const ICurveComposite* pCrvH2, ICurveComposite* pCrvPath,
ICRVCOMPOPOVECTOR& VFirstOff, ICRVCOMPOPOVECTOR& VoffsCl, const PocketParams& PockParams,
ICurveComposite* pCrvNewCurve)
{
// inizializzo i due possibili percorsi
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 ( ! CalcBoundedSmoothedLink( ptS, vtTanS, ptE, vtTanE, 0.5, VFirstOff, PockParams, pPath1))
if ( ! CalcBoundedLink( ptS, ptE, VFirstOff, pPath1))
return false ;
if ( ! CalcBoundedSmoothedLink( ptH, vtH, ptS, vtTanS, 0.5, VFirstOff, PockParams, 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, PockParams, pPath2, VoffsCl))
return false ;
// imposto la Feed
AssignMinFeed( pCrvToAdd, PockParams) ;
// 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 ( ! CalcBoundedSmoothedLink( ptHS, vtHS, ptHE, vtHE, 0.5, VFirstOff, PockParams, pCrvBiArcHelper))
if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper))
return false ;
// assegno la Feed
AssignMinFeed( pCrvBiArcHelper, PockParams) ;
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 ( ! CalcBoundedSmoothedLink( ptHS, vtHS, ptHE, vtHE, 0.5, VFirstOff, PockParams, pCrvBiArcHelper))
if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper) )
return false ;
// assegno la Feed
AssignMinFeed( pCrvBiArcHelper, PockParams) ;
if ( ! pCrvNewCurve->AddCurve( pCrvBiArcHelper->Clone()) ||
! pCrvNewCurve->AddCurve( pCrvH2->Clone())) {
++nTry ;
}
else {
bFound = true ;
}
}
if ( ! bFound)
return false ;
}
}
return true ;
}
//---------------------------------------------------------------------------
static bool
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 ;
}
//---------------------------------------------------------------------------
static bool
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 ;
}
//----------------------------------------------------------------------------
static bool
RemoveExtraParts( ISurfFlatRegion* pSrfToCut, ICRVCOMPOPOVECTOR& vOffs, ICRVCOMPOPOVECTOR& vOffsClosedCurves,
ICRVCOMPOPOVECTOR& vOffsFirstCurve, ICURVEPOVECTOR& vLinks, const PocketParams& PockParams)
{
// controllo parametri
if ( pSrfToCut == nullptr || vOffs.empty())
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, PockParams, 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, PockParams, 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,
PockParams, 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,
PockParams, 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, PockParams, 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,
PockParams, 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,
PockParams, 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 ;
}
//----------------------------------------------------------------------------
static bool
CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, const PocketParams& PockParams,
const ICRVCOMPOPOVECTOR& vOffsClosedCurves, const ICRVCOMPOPOVECTOR& vOffsFirstCurve)
{
// controllo dei parametri
if ( vOffs.empty() || vOffsFirstCurve.empty())
return false ;
vLinks.clear() ;
vLinks.resize( int( vOffs.size())) ;
// scorro tutte le curva di Offset
for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) {
// ricavo il punto inziale della curva corrente
Point3d ptS ;
if ( ! vOffs[i]->GetStartPoint( ptS))
return false ;
int nNextInd = -1 ; // indice della curva successiva
double dMinDist = INFINITO ; // distanza tra questa curva e la successiva
Point3d ptStartNext ; // punto iniziale della curva successiva
// tra le curva successive cerco la curva interna e più vicina ad essa
for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) {
IntersCurveCurve IntCC( *vOffs[i], *vOffs[j]) ;
CRVCVECTOR ccClass ;
// se interna
if ( IntCC.GetCurveClassification( 1, EPS_SMALL, ccClass) &&
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) {
// calcolo la distanza minima tra essa
int nFlag ;
Point3d ptClosest ;
if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) {
double dCurrDist = SqDist( ptS, ptClosest) ;
if ( dCurrDist < dMinDist) {
dMinDist = dCurrDist ;
nNextInd = j ;
ptStartNext = ptClosest ;
}
}
}
}
// se non ho trovato nessuna curva interna... cerco semplicemente la curva più vicina
if ( nNextInd == -1) {
for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) {
int nFlag ;
Point3d ptClosest ;
if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) {
double dCurrDist = SqDist( ptS, ptClosest) ;
if ( dCurrDist < dMinDist) {
dMinDist = dCurrDist ;
nNextInd = j ;
ptStartNext = ptClosest ;
}
}
}
}
// se non ho trovato nessuna curva, errore
if ( nNextInd == -1)
return false ;
// scambio la curva i+1 esima con la j-esima ( se non sono già in ordine)
if ( nNextInd != i + 1)
swap( vOffs[nNextInd], vOffs[i+1]) ;
// cambio il suo punto iniziale nel punto più vicino tovato
double dUS ;
if ( ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUS, EPS_SMALL))
return false ;
vOffs[i+1]->ChangeStartPoint( dUS) ;
// accorcio la curva per raccordarmi in tangenza con la successiva
if ( int( vOffs.size()) > 1) {
// clono le curve i ed i+1 esime ( nel caso non riuscissi ad accorciarle o raccordarle )
PtrOwner<ICurveComposite> pCrv_i( vOffs[i]->Clone()) ;
PtrOwner<ICurveComposite> pCrv_ii( vOffs[i+1]->Clone()) ;
if ( IsNull( pCrv_i) || IsNull( pCrv_ii) || ! pCrv_i->IsValid() || ! pCrv_ii->IsValid())
return false ;
// creo la curva che le collegherà
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
if ( IsNull( pCrvLink))
return false ;
// NB. Le curve di Offset da tagliare non devono essere quelle di primo Offset
if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsClosedCurves, vOffsFirstCurve, PockParams, pCrvLink,
i == 0 ? 0 : 0.01, 0.01, 2) || ! pCrvLink->IsValid()) {
// se non sono riuscito, cerco una strada più semplice
// ripristino le curve
pCrvLink->Clear() ;
vOffs[i].Set( pCrv_i) ; // chiuso
vOffs[i+1].Set( pCrv_ii) ; // chiuso
// recupero i vettori tangente iniziali ( le curve sono chiuse )
Vector3d vtS, vtE ;
if ( ! vOffs[i]->GetStartDir( vtS) || ! vOffs[i+1]->GetStartDir( vtE))
return false ;
// creo il bi-arco tra esse
if ( CalcBoundedSmoothedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink))
vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento
else
return false ;
continue ; // passo alla curva i+1 esima successiva
}
// per sicurezza aggiorno i nuovi punti e i nuovi parametri
double dUNewS, dUNewE ;
if ( ! pCrvLink->GetStartPoint( ptS) ||
! pCrvLink->GetEndPoint( ptStartNext) ||
! vOffs[i]->GetParamAtPoint( ptS, dUNewS) ||
! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUNewE))
return false ;
// imposto il punto iniziale della curva successiva ( i+1 esima)
vOffs[i+1]->ChangeStartPoint( dUNewE) ;
PtrOwner<ICurveComposite> pCrvNewOffs( vOffs[i]->Clone()) ;
if ( dUNewS > EPS_SMALL) {
pCrvNewOffs.Set( ConvertCurveToComposite( vOffs[i]->CopyParamRange( 0, dUNewS))) ;
// sostituisco la curva i esima con quella tagliata
vOffs[i].Set( pCrvNewOffs) ;
}
// aggiorno il collegamento
vLinks[i+1].Set( pCrvLink) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs, const int nOffsCount,
const int nIter, const PocketParams& PockParams, bool& bInsert)
{
// controllo dei parametri
if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0)
return false ;
bInsert = true ; // di base inserisco
// controllo se richiesta ottimizzazione sul numero di Offsets
if ( ! PockParams.bOptOffsets)
return true ;
if ( nIter != 0) { // controllo se sono almeno al secondo offset...
// per ogni curva controllo se il controOffset + 5 * EPS_SMALL genera una regione ...
OffsetCurve OffsCrv ; // considero anche i casi in cui ho bSmallRad
if ( OffsCrv.Make( pCrvOffs, - PockParams.dRad + dOffs - 5 * EPS_SMALL , ICurve::OFF_FILLET)) {
// se questa curva sparisce -> non serve inserirla come offset, non svuota parti aggiuntive
if ( OffsCrv.GetCurveCount() == 0) {
bInsert = false ;
return true ;
}
}
else
return true ;
// controllo se richista ottimizzazione per Offsets mediante centroidi e medial Axis
if ( ! PockParams.bOptOffsetsAdv || OffsCrv.GetCurveCount() > 1)
return true ;
PtrOwner<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, PockParams.dRad - 5 * EPS_SMALL) ;
CRVCVECTOR ccClass ;
IntersCurveCurve intCC( *pCrvOffLonger, *pCrvTool) ;
intCC.GetCurveClassification( 1, EPS_SMALL, ccClass) ;
if ( int(ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) {
// centroide
bInsert = false ;
return true ;
}
// recupero la PolyLine per altezza e lunghezza del Box2D ( rettangolo)
PolyLine pl ;
if ( ! pCrvOffLonger->ApproxWithLines( 100 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, pl))
return true ;
Vector3d vtX ;
double dLen = 0 ; double dHeight = 0 ;
if ( ! pl.GetMinAreaRectangleXY( ptC, vtX, dLen, dHeight))
return true ;
// frame locale, recupero DimX e DimY
Frame3d frLoc ; frLoc.Set( ptC, Z_AX, vtX) ;
if ( ! frLoc.IsValid())
return true ;
BBox3d bBox ;
frLoc.Invert() ;
pCrvOffLonger->GetBBox( frLoc, bBox) ;
double dDimX = bBox.GetDimX() ;
double dDimY = bBox.GetDimY() ;
// se dimensioni accettabili, inserisco
if ( dDimX < 2 * PockParams.dRad - 100 * EPS_SMALL ||
dDimY < 2 * 2 * PockParams.dRad - 100 * EPS_SMALL) {
bInsert = false ;
return true ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CalcSpiral( const ISurfFlatRegion* pSrfPock, const PocketParams& PockParams, int& nReg, Point3d& ptStart,
ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops, ICurveComposite* pMCrv, ICurveComposite* pRCrv,
bool& bOptimizedTrap, bool& bMidOut, Vector3d& vtMidOut)
{
// inizializzo i risultati
pMCrv->Clear() ;
pRCrv->Clear() ;
// Offset corrente ( il primo è definito dal raggio utensile)
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
// recupero il versore normale della superficie, ruotandola nel piano XY
PtrOwner<ISurfFlatRegion> pSrfToWork( pSrfPock->Clone()) ;
if ( IsNull( pSrfToWork) || pSrfToWork->GetChunkCount() == 0)
return false;
// ---------------------------------- Casi ottimizzati ----------------------------------
bool bHasIsland = ( pSrfPock->GetLoopCount( 0) > 1) ;
// se non ho isole e curva valida allora controllo casi ottimizzati
if ( ! PockParams.bAvoidOpt && ! bHasIsland &&
( ! vCrvOrigChunkLoops.empty()) && ! IsNull( vCrvOrigChunkLoops[0])) {
// --------------------------- 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) ;
pCrvBorder->SetExtrusion( pSrfPock->GetNormVersor()) ; // setto l'estrusione della curva
Vector3d vtN = pSrfPock->GetNormVersor() ; // vettore normale per caso circonferenza
Point3d ptCen ; double dRad ;
bool bIsCircle = false ;
if ( ! OptimizedSpiralCirle( pCrvBorder, 50 * EPS_SMALL, dRad, ptCen, bIsCircle))
return false ;
if ( bIsCircle && dRad - dOffs > 10 * EPS_SMALL) {
double dIntRad = 0 ;
if ( nReg == 0) {
bool bOk = CalcCircleSpiral( ptCen, vtN, dRad - dOffs, dIntRad, PockParams, pMCrv, pRCrv) ;
if ( bOk) {
pMCrv->GetStartPoint( ptStart) ;
for ( int u = 0 ; u < pCrvBorder->GetCurveCount() ; ++ u) {
if ( pCrvBorder->GetCurve( u)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
bMidOut = true ;
pMCrv->GetStartDir( vtMidOut) ;
vtMidOut.Rotate( pSrfPock->GetNormVersor(), PockParams.bInvert ? ANG_RIGHT : - ANG_RIGHT) ;
break ;
}
}
nReg = 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 ;
if ( ! GetTrapezoidFromShape( vCrvOrigChunkLoops[0], pCrvTrap, frTrap, PockParams, dPocketSize, nBase, nSecondBase))
return false ;
if ( pCrvTrap->IsValid()) {
pCrvTrap->SetExtrusion( Z_AX) ;
if ( nReg == 0) {
CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParams, pMCrv, pRCrv, bOptimizedTrap) ;
if ( bOptimizedTrap) {
nReg = 1 ;
return true ;
}
}
else
return true ;
}
}
// 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( pSrfToWork->Clone()) ; // 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 ;
}
// se primo Offset
if ( nIter == 0) {
int my_nReg = nReg ;
nReg = pSfrOffsVR->GetChunkCount() ; // aggiorno il nuovo valore delle regioni totali di primo Offset
// gli Offset progressivi appartengono al Chunk nReg-esimo
pSrfAct.Set( pSfrOffsVR->CloneChunk( my_nReg)) ;
if ( IsNull( pSrfAct)) // se supero i chunk ottenuti
return true ;
pSfrOffsVR.Set( pSrfAct->Clone()) ;
}
// numero di Chunk attuali ( alla prima iterazione è l'nReg-esimo)
int nChunks = pSfrOffsVR->GetChunkCount() ;
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 ;
/*
Il materiale deve sempre trovarsi alla destra ( se bInvert -> alla sinistra) del tool
soltanto sul primo bordo; Nel caso di isole aperte o nelle passate di Offset intermedio
l'orientamente non ha importanza.
*/
if ( nIter != 0 && 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, PockParams, bInsert))
return false ;
if ( bInsert)
vOffs.emplace_back( Release( pCrvCompoBorder)) ;
if ( nIter == 0) { // salvo il bordo per i link ( non invertiti, devo sapere IN/OUT)
PtrOwner<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 < PockParams.dRad + EPS_ZERO : dOffs - dOffsPrec < PockParams.dRad + 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
if ( nIter != 0) {
dOffsPrec = dOffs ;
dOffs += PockParams.dSideStep ;
}
else {
dOffsPrec = 0. ;
dOffs = PockParams.dSideStep ;
}
}
// se devo usare un Offset più piccolo...
else if ( ! bSmallRad)
dOffs = dOffsPrec + ( nIter == 0 ? PockParams.dRad + PockParams.dRadialOffset : PockParams.dRad) ;
// altrimenti ho finito la regione da svuotare...
else
break ;
++ nIter ; // aggiorno iterazione
}
// se non ho trovato curve di Offset allora esco
if ( vOffs.empty())
return true ;
// cambio il punto iniziale della prima Curva di Offset
Point3d ptRef = PockParams.ptEnd ;
Point3d ptNewStart ;
// Se ho come lavorazione uno SpiralIn posso poter entrare dalle isole aperte...
int nIndexSwap = 0 ; // indice del vettore di curve per identificare la curva su cui entrare
if ( PockParams.nType == POCKET_SPIRALIN) {
if ( SetAdvancedPtStartForPath( vOffsFirstCurve, PockParams, pSrfToWork, ptRef, ptStart,
vtMidOut, bMidOut, nIndexSwap, int( vOffs.size()), vCrvOrigChunkLoops))
vOffsFirstCurve[nIndexSwap]->GetStartPoint( ptNewStart) ;
else
return false ;
}
else {
if ( SetPtStartForPath( vOffs[0], PockParams, pSrfToWork, ptRef, ptStart, vtMidOut, bMidOut,
int( vOffs.size()), vCrvOrigChunkLoops[0]))
vOffs[0]->GetStartPoint( ptNewStart) ;
else
return false ;
}
// se richiesta inversione
for ( int i = 0 ; i < int( vOffs.size()) && PockParams.bInvert ; ++ i)
vOffs[i]->Invert() ;
// smusso le curve di offset ( ad eccezione della prima; le isole sono automaticamente smussate)
ICRVCOMPOPOVECTOR vOffsClosedCurves( vOffs.size()) ; // vettore con tutte le curve di Offset Chiuse
double dSmoothPar = PockParams.dRad / 8 ;
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
if ( i != 0)
ModifyCurveToSmoothed( vOffs[i], PockParams, dSmoothPar, dSmoothPar, false) ;
vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
vOffsClosedCurves[i].Set( vOffs[i]->Clone()) ;
}
// setto il punto iniziale della svuotatura
double dNewUS ;
if ( nIndexSwap != 0)
swap( vOffs[0], vOffs[nIndexSwap]) ; // se entro da un'isola aperta
vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ;
vOffs[0]->ChangeStartPoint( dNewUS) ;
vOffs[0]->SetTempParam( 0., 0) ; // prima iterazione
// riordino le curve cambiando e creo i collegamenti
ICURVEPOVECTOR vLinks( vOffs.size()) ;
if ( ! CreateSpiralPocketingPath( vOffs, vLinks, PockParams, vOffsClosedCurves, vOffsFirstCurve))
return false ;
// controllo eventuali parti non svuotate...
PtrOwner<ISurfFlatRegion> pSrfToCut( CreateSurfFlatRegion()) ;
if ( GetUnclearedRegion( vOffsFirstCurve, vOffs, vLinks, vCrvOrigChunkLoops[0], PockParams, pSrfToCut)) {
// Modifico i percorsi
if ( ! RemoveExtraParts( pSrfToCut, vOffs, vOffsClosedCurves, vOffsFirstCurve, vLinks, PockParams))
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])) {
// accodo nel percorso di lavorazione
if ( ! pMCrv->AddCurve( Release( vLinks[i]))) {
return false ;
}
}
pMCrv->AddCurve( Release( vOffs[i])) ;
}
// semplifico la curva ottenuta
SimplifyCurveByFeeds( pMCrv, PockParams, 10 * EPS_SMALL) ;
// verifico il percorso di lavoro
if ( pMCrv->GetCurveCount() == 0)
return false ;
// reset delle proprietà temporanee delle curve componenti
for ( int i = 0 ; i < pMCrv->GetCurveCount() ; ++ i) {
pMCrv->SetCurveTempProp( i, 0, 0) ;
pMCrv->SetCurveTempProp( i, 0, 1) ;
}
// setto estrusione
pMCrv->SetExtrusion( Z_AX) ;
return true ;
}
//----------------------------------------------------------
static bool
CalcZigZagLink( ICurveComposite* pCrv1, ICurveComposite* pCrv2, const PocketParams& PockParams,
ICurveComposite* pCrvLink)
{
// se non richiesto, esco
if ( ! PockParams.bSmooth)
return true ;
// 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 - ( ( 2 * PockParams.dRad) / 16)) / dLen1 ; // % parametro sinistro per smusso
double dTollRight = ( ( 2 * PockParams.dRad) / 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, PockParams, 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 - ( ( 2 * PockParams.dRad) / 16)) / dLenI_e ; // % paramtro sinistro per smusso
dTollRight = ( ( 2 * PockParams.dRad) / 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, PockParams, 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 ;
}
//----------------------------------------------------------------------------
static bool
CalcZigZag( const ISurfFlatRegion* pSrfZigZag, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vpCrvs,
bool bFromInfill)
{
// check parametri
if ( pSrfZigZag == nullptr || ! pSrfZigZag->IsValid())
return true ;
// creo il vettore dei contorni
ICRVCOMPOPOVECTOR vFirstOff ;
for ( int c = 0 ; c < pSrfZigZag->GetChunkCount() ; ++ c)
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 ;
double dYStep ;
if ( ! bFromInfill) {
nYStep = static_cast< int >( ceil(( dDimY - 30 * EPS_SMALL) / PockParams.dSideStep)) ;
dYStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ;
}
else {
nYStep = static_cast< int >( floor(( dDimY + 30 * EPS_SMALL) / PockParams.dSideStep)) ;
dYStep = PockParams.dSideStep ;
}
int nRef = (( nYStep + ( PockParams.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 ;
if ( ! bFromInfill)
ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + 10 * EPS_SMALL + i * dYStep, ptMin.z + dDimZ) ;
else {
double dShift = ( i == 0 ? 10 * EPS_SMALL : ( i == nYStep ? - 10 * EPS_SMALL : 0.)) ;
ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + dShift + i * dYStep, ptMin.z + dDimZ) ;
}
if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN))
return false ;
if ( ! bPlus)
pLine->Invert() ;
// calcolo la classificazione della linea rispetto alla superficie
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))) ;
if ( IsNull( pSeg))
continue ;
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) ;
// assegno la feed al tratto lineare
if ( ! AssignFeedZigZagOneWay( pLineCompo, false, vLineAbove, vLineUnder, vAddedLinks, PockParams)) // 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( pLastSeg->Clone()) ;
PtrOwner<ICurveComposite> pSeg2( pLineCompo->Clone()) ;
if ( IsNull( pSeg1) || IsNull( pSeg2))
return false ;
// aggiorno il link
if ( ! CalcZigZagLink( pSeg1, pSeg2, PockParams, 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 ( bFromInfill)
for ( int u = 0 ; u < pCrvLink->GetCurveCount() ; ++ u)
pCrvLink->SetCurveTempProp( u, -1, 1) ;
if ( ! AssignFeedZigZagOneWay( pCrvLink, true, vLineAbove, vLineUnder, vAddedLinks, PockParams))
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( 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 ;
}
//----------------------------------------------------------------------------
static bool
AddSpiralIn( const ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// ciclo sui chunk della superficie da svuotare
for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; ++ c) {
// copio il chunk c-esimo
PtrOwner<ISurfFlatRegion> pSrfChunk( pSrfPock->CloneChunk( c)) ;
if ( IsNull( pSrfChunk))
return false ;
const int MAX_REGS = 50 ; // massimo numero di regioni create dal primo offset
int nReg = 0 ; // chunk nuovo corrente da svuotare
// ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-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))
return false ;
int nRegTot = nReg ;
Point3d ptStart ;
bool bOptimizedTrap = false ;
// cerco le curve originali del chunk cc-esimo ( per casi ottimizzati)
ICRVCOMPOPOVECTOR vCrvOrigChunkLoops ;
if ( ! GetOptCrvIndex( pSfrOrig, pSrfChunk, PockParams, nReg, vCrvOrigChunkLoops))
return false ;
// calcolo il percorso di svuotatura
bool bSomeOpen ;
Vector3d vtMidOut ;
if ( ! CalcSpiral( pSrfChunk, PockParams, nRegTot, ptStart, vCrvOrigChunkLoops, pMCrv, pRCrv, bOptimizedTrap,
bSomeOpen, vtMidOut))
return false ;
// se terminate le regioni, esco
if ( pMCrv->GetCurveCount() == 0)
break ; // passo al chunk originale successivo
if ( bOptimizedTrap) { // se caso a trapezio
AdjustTrapezoidSpiralForLeadInLeadOut( pMCrv, PockParams) ;
AssignFeedSpiralOpt( 1, PockParams, pMCrv) ;
pMCrv->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
}
else if ( bSomeOpen) { // se presenza di lati aperti validi
if ( ! ExtendPathOnOpenEdge( pMCrv, PockParams, vtMidOut, false))
return false ;
}
// inserisco le curve nel vettore
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
++ nReg ; // incremento il numero di regione progressiva
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AddSpiralOut( const ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// ciclo sui chunk della superficie da svuotare
for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; ++ c) {
// copio il chunk c-esimo
PtrOwner<ISurfFlatRegion> pSrfChunk( pSrfPock->CloneChunk( c)) ;
if ( IsNull( pSrfChunk))
return false ;
const int MAX_REGS = 50 ; // massimo numero di regioni create dal primo offset
int nReg = 0 ; // chunk nuovo corrente da svuotare
// ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-esimo
while ( nReg < MAX_REGS) {
// calcolo la spirale dall'interno all'esterno
PtrOwner<ICurveComposite> pMCrv( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pRCrv( CreateCurveComposite()) ;
if ( IsNull( pMCrv) || IsNull( pRCrv))
return false ;
int nRegTot = nReg ;
Point3d ptStart ;
bool bOptimizedTrap = false ;
// cerco la curva originale del chunk cc-esimo ( per casi ottimizzati)
ICRVCOMPOPOVECTOR vCrvOrig ;
if ( ! GetOptCrvIndex( pSfrOrig, pSrfChunk, PockParams, nReg, vCrvOrig))
return false ;
// calcolo il percorso di svuotatura
bool bSomeOpen ;
Vector3d vtMidOut ;
if ( ! CalcSpiral( pSrfChunk, PockParams, nRegTot, ptStart, vCrvOrig, pMCrv, pRCrv, bOptimizedTrap,
bSomeOpen, vtMidOut))
return false ;
// se terminate le regioni, esco
if ( pMCrv->GetCurveCount() == 0)
break ; // passo al chunk originale successivo
if ( bOptimizedTrap) { // se caso a trapezio
AdjustTrapezoidSpiralForLeadInLeadOut( pMCrv, PockParams) ;
AssignFeedSpiralOpt( 1, PockParams, pMCrv) ;
pMCrv->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
}
// inverto i percorsi, perchè sono calcolati dall'esterno all'interno (solo nel caso non ottimizzato)
pMCrv->Invert() ;
pRCrv->Invert() ;
// inserisco le curve nel vettore
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
++ nReg ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetZigZagOneWayBorderCrvs( const ISurfFlatRegion* pSfrPock, const PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvRes)
{
/*
Restituisce le curve di Primo Offset dei lati chiusi per i percorsi a ZigZag e OneWay.
Vengono calcolate le Feed ed eventuali entrate da fuori per i percorsi ricavati
*/
// controllo validità della regione
if ( pSfrPock == nullptr || ! pSfrPock->IsValid())
return false ;
// recupero la superficie limite e calcolo Offset
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrLimit))
return false ;
if ( PockParams.SfrLimit.IsValid()) {
pSfrLimit.Set( PockParams.SfrLimit.Clone()) ;
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
return false ;
pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset - 50 * EPS_SMALL, ICurve::OFF_FILLET) ;
}
// vettore delle curve chiuse Offsettate
ICRVCOMPOPOVECTOR vClosedOffs ;
// scorro i Chunk e i Loop della regione
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) {
// Loop come curva composita
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ;
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
return false ;
// recupero i tratti con proprietà uniformi
ICRVCOMPOPOVECTOR vpCrvs ;
GetHomogeneousParts( pCrvLoop, PockParams, vpCrvs) ;
if ( vpCrvs.size() > 1) {
// unisco il primo e l'ultimo se estremi compatibili
Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ;
Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ;
if ( AreSamePointApprox( ptS, ptE)) {
vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ;
vpCrvs.erase( vpCrvs.end() - 1) ;
}
}
// le isole aperte vanno percorse
bool bOpenIsland = ( nL > 0 && int( vpCrvs.size()) == 1 &&
vpCrvs[0]->GetTempProp() == TEMP_PROP_OPEN_EDGE) ;
// scorro tutti i tratti chiusi e calcolo l'Offset
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
// se tratto chiuso o isola aperta
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE || bOpenIsland) {
// Offset per le curve
OffsetCurve OffsCrv ;
double dOffs = - PockParams.dRad - PockParams.dRadialOffset ;
// se isole, l'Offset è di segno opposto
if ( nL > 0)
dOffs *= -1 ;
if ( OffsCrv.Make( vpCrvs[i], dOffs, ICurve::OFF_FILLET)) {
PtrOwner<ICurve> pOffLongestCrv( OffsCrv.GetLongerCurve()) ;
while ( ! IsNull( pOffLongestCrv)) {
// recupero solo i tratti di curva al di fuori della superficie limite
if ( pSfrLimit->IsValid()) {
CRVCVECTOR ccClass ;
if ( pSfrLimit->GetCurveClassification( *pOffLongestCrv, EPS_SMALL, ccClass)) {
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
if ( ccClass[j].nClass == CRVC_OUT) {
// recupero il tratto di curva
PtrOwner<ICurve> pCrv( pOffLongestCrv->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
if ( ! IsNull( pCrv) && pCrv->IsValid())
vClosedOffs.emplace_back( ConvertCurveToComposite( Release( pCrv))) ;
}
}
}
}
else
vClosedOffs.emplace_back( ConvertCurveToComposite( Release( pOffLongestCrv))) ;
// passo alla successiva
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ;
}
}
}
}
}
}
// se non ho percorsi ho finito
if ( vClosedOffs.empty())
return true ;
// concateno eventuali percorsi
if ( ! ChainCompoCurves( vClosedOffs))
return false ;
// determino ora il punto di inizio, il tratto per il LeadIn
if ( ! AdjustBorderCurves( vClosedOffs, PockParams, false))
return false ;
// Calcolo ora le Feed e restituisco le curve ottenute
for ( int i = 0 ; i < int( vClosedOffs.size()) ; ++ i) {
// se ZigZag -> ho percorso i tratti a ZigZag, quindi posso andare a Feed massima
if ( PockParams.nType == POCKET_ZIGZAG)
AssignMaxFeed( vClosedOffs[i], PockParams) ;
// se OneWay -> prima percorro queste curva, quindi la Feed è minima
if ( PockParams.nType == POCKET_ONEWAY)
AssignMinFeed( vClosedOffs[i], PockParams) ;
// restituisco le curve ottenute
vCrvRes.emplace_back( Release( vClosedOffs[i])) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AddZigZag( const ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// Offset della regione per curva ZigZag
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
// ciclo su tutti i chunks della superficie originale
for ( int nC = 0 ; nC < pSrfPock->GetChunkCount() ; ++ nC) {
// creo la regione per il percorso a ZigZag ( mediante primo Offset) portandola
// nel sistema di riferimento creato
PtrOwner<ISurfFlatRegion> pSrfZigZag( pSrfPock->CloneChunk( nC)) ;
if ( ! pSrfZigZag->Offset( - dOffs, ICurve::OFF_FILLET))
return false ;
// vettore con i percorsi a ZigZag relativi al Chunk nC-esimo
ICRVCOMPOPOVECTOR vpCrvs ;
// ciclo sui Chunk ottenuti dal primo offset
for ( int nC1 = 0 ; nC1 < pSrfZigZag->GetChunkCount() ; ++ nC1) {
// recupero il Chunk attuale
PtrOwner<ISurfFlatRegion> pSrfZigZagChunk( pSrfZigZag->CloneChunk( nC1)) ;
if ( IsNull( pSrfZigZagChunk) || ! pSrfZigZagChunk->IsValid())
return false ;
// calcolo i percorsi di ZigZag
if ( ! CalcZigZag( pSrfZigZagChunk, PockParams, vpCrvs, false))
return false ;
// Flag per esistenza di lati aperti per il Chunk nC-esimo
bool bIsChunkClosed = true ;
if ( ! PockParams.bAllClosed) {
if ( ! IsChunkAllClosed( pSrfPock, nC, bIsChunkClosed))
return false ;
}
// aggiorno il vettore delle curve risultati
for ( int nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU) {
// pulisco le tempProp
ResetCurveTempProps( vpCrvs[nU]) ;
// se il Chunk originale aveva lati aperti, estendo il percorso a ZigZag
if ( ! bIsChunkClosed) {
bool bIsInside = false ;
// controllo se il punto iniziale è interno alla superficie originale
Point3d ptStart ; vpCrvs[nU]->GetStartPoint( ptStart) ;
Vector3d vtStart ; vpCrvs[nU]->GetStartDir( vtStart) ;
vtStart.Invert() ;
ptStart.Translate( 0.25 * PockParams.dRad * vtStart) ;
double dMinDist = max( 0., - PockParams.dRadialOffset) ;
if ( IsPointInsideSurfFr( ptStart, pSfrOrig, dMinDist, bIsInside) && ! bIsInside)
ExtendPathOnOpenEdge( vpCrvs[nU], PockParams, vtStart, false) ;
// controllo se il punto finale è esterno alla superficie originale
Point3d ptEnd ; vpCrvs[nU]->GetEndPoint( ptEnd) ;
Vector3d vtEnd ; vpCrvs[nU]->GetEndDir( vtEnd) ;
ptEnd.Translate( 0.25 * PockParams.dRad * vtEnd) ;
if ( IsPointInsideSurfFr( ptEnd, pSfrOrig, dMinDist, bIsInside) && ! bIsInside)
ExtendPathOnOpenEdge( vpCrvs[nU], PockParams, vtEnd, true) ;
}
// inserisco le curve nel vettore risultante
vCrvCompoRes.emplace_back( Release( vpCrvs[nU])) ;
}
// libero il vettore di curve ZigZag per il chunk successivo
vpCrvs.clear() ;
}
// se richiesto, aggiungo le curve chiuse di Bordo
if ( PockParams.bAllowZigZagOneWayBorders) {
// recupero il Chunk nC-esimo
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nC)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid() ||
! GetZigZagOneWayBorderCrvs( pSfrChunk, PockParams, vCrvCompoRes))
return false ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
OptimizeChunkOneWay( ISurfFlatRegion* pSrfOrig, ISURFFRPOVECTOR& vSrfIdeal)
{
// controllo parametri
if ( pSrfOrig == nullptr || pSrfOrig->GetChunkCount() == 0)
return false ;
vSrfIdeal.clear() ;
// clono la superficie passata alla funzione
PtrOwner<ISurfFlatRegion> pSrfToPock( pSrfOrig->Clone()) ;
if ( IsNull( pSrfToPock) || pSrfToPock->GetChunkCount() == 0)
return false ;
// classifico i chunks in modo da creare delle regioni ideali; una regione ideale è formata da un chunk principale
// con tutti gli altri contenuti in esso ( in questo modo ottimizzo i percorsi per i bordi)
INTVECTOR vChunksAvailable( pSrfToPock->GetChunkCount(), 1) ;
for ( int c = 0 ; c < pSrfToPock->GetChunkCount() ; ++ c) {
PtrOwner<ISurfFlatRegion> pSrfIdeal( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfIdeal))
return false ;
if ( vChunksAvailable[c] == 1) { // se Chunk valido...
// prendo la curva esterna
PtrOwner<ICurve> pCrvExt( pSrfToPock->GetLoop( c, 0)) ;
if ( IsNull( pCrvExt))
return false ;
// inserisco il chunk c-esimo ( curva per curva, così non perdo le temp prop)
pSrfIdeal->AddExtLoop( pCrvExt->Clone()) ; // ... inizio a creare la regione ideale da esso
for ( int l = 1 ; l < pSrfToPock->GetLoopCount( c) ; ++ l)
pSrfIdeal->AddIntLoop( pSrfToPock->GetLoop( c, l)) ;
// il chunk c-esimo non è più disponibile...
vChunksAvailable[c] = -1 ;
// scorro tutti gli altri chunk i-esimi ancora disponibili
for ( int i = 0 ; i < int( vChunksAvailable.size()) ; ++ i) {
if ( vChunksAvailable[i] == 1) {
// prendo la curva esterna del chunk i-esimo disponibile
PtrOwner<ICurve> pCrvExtC( pSrfToPock->GetLoop( i, 0)) ;
if ( IsNull( pCrvExtC))
return false ;
// classifico i bordi esterni ( se il bordo del chunk c-esimo contiene o è contenuto nel loop
// esterno del chunk i-esimo -> sono chunks appartenenti alla stessa superficie ideale)
IntersCurveCurve intCC( *pCrvExtC, *pCrvExt) ;
CRVCVECTOR ccClass, ccClass1 ;
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
intCC.GetCurveClassification( 1, EPS_SMALL, ccClass1) ;
if (( ccClass.size() == 1 && ccClass[0].nClass == CRVC_IN) ||
( ccClass1.size() == 1 && ccClass1[0].nClass == CRVC_IN)) {
// inserisco il chunk i-esimo ( curva per curva, così non perdo le temp prop)
pSrfIdeal->AddExtLoop( pSrfToPock->GetLoop( i, 0)) ;
for ( int l = 1 ; l < pSrfToPock->GetLoopCount( i) ; ++ l)
pSrfIdeal->AddIntLoop( pSrfToPock->GetLoop( i, l)) ;
vChunksAvailable[i] = -1 ; // chunk j-esimo non più disponibile
// NB: Se il chunk i-esimo contiene la pSrfIdeal ( quindi chunk i-esimo unito ad eventuali altri chunk
// j-esimi precedenti -> prendo come bordo esterno di riferimento il suo e ricontrollo dall'inizio i
// chunk disponibili
if (( int)ccClass1.size() == 1 && ccClass1[0].nClass == CRVC_IN) {
pCrvExt.Set( pCrvExtC->Clone()) ;
i = 0 ;
}
}
}
}
}
if ( pSrfIdeal->GetChunkCount() > 0)
vSrfIdeal.emplace_back( Release( pSrfIdeal)) ;
}
return ( ! vSrfIdeal.empty()) ;
}
//----------------------------------------------------------------------------
static bool
AddOneWay( const ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
double dExtra = 0. ;
// copio la regione da svuotare
PtrOwner<ISurfFlatRegion> pSrfToPock( pSrfPock->Clone()) ;
if ( IsNull( pSrfToPock))
return false ;
// vettore delle regioni ideali
ISURFFRPOVECTOR vSrfFlat ;
if ( ! OptimizeChunkOneWay( pSrfToPock, vSrfFlat))
return false ;
// scorro le regioni ideali
for ( int nIs = 0 ; nIs < int( vSrfFlat.size()) ; ++ nIs) {
// copio la superficie ideale ed effettuo il primo Offset
PtrOwner<ISurfFlatRegion> pSrfIdeal( CloneSurfFlatRegion( vSrfFlat[nIs])) ;
if ( IsNull( pSrfIdeal) || ! pSrfIdeal->Offset( - dOffs, ICurve::OFF_FILLET))
return false ;
// recupero il Box della superficie attuale da svuotare
BBox3d b3Pocket ; pSrfIdeal->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) / PockParams.dSideStep)) ;
double dYStep = ( nYStep > 0 ? ( dDimY + 2 * dExtra) / nYStep : 0) ;
-- nYStep ;
// vettore dei segmenti al di sotto della linea corrente
ICURVEPOVECTOR vLineUnder ;
ICURVEPOVECTOR vCrvNull ;
ICRVCOMPOPOVECTOR vCrvCompoNull ;
// 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))
return false ;
// se richiesta inversione
if ( PockParams.bInvert)
pLine->Invert() ;
// linea come composita per Feed ( la dovrò spezzare)
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 ;
pSrfIdeal->GetCurveClassification( *pLine, EPS_SMALL, ccClassSeg) ;
for ( int w = 0 ; w < int( ccClassSeg.size()) ; ++ w) {
if ( ccClassSeg[w].nClass == CRVC_IN) {
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
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))) ;
// aggiungo il segmento al vettore dei tratti correnti
vAddedLines.emplace_back( pCrvSeg->Clone()) ;
// trasformo il tratto lineare in curve composita per il calcolo della Feed
PtrOwner<ICurveComposite> pCrvSegCompo( CreateCurveComposite()) ;
if ( IsNull( pCrvSegCompo))
return false ;
pCrvSegCompo->AddCurve( Release( pCrvSeg)) ;
// calcolo la Feed
AssignFeedZigZagOneWay( pCrvSegCompo, false, vCrvNull, vLineUnder, vCrvCompoNull, PockParams) ;
vCrvCompoRes.emplace_back( Release( pCrvSegCompo)) ;
}
}
// i tratti lineari correnti diventano i precedenti
vLineUnder.clear() ;
for ( int u = 0 ; u < int( vAddedLines.size()) ; ++ u)
vLineUnder.emplace_back( Release( vAddedLines[u])) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double dStep, double dAngle,
int nType, bool bSmooth, bool bInvert, bool bAvoidOpt, ISurfFlatRegion* pSfrLimit, ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid() ||
dStep < 10 * EPS_SMALL ||
( nType != POCKET_ZIGZAG && nType != POCKET_ONEWAY && nType != POCKET_SPIRALIN && nType != POCKET_SPIRALOUT))
return false ;
// pulizia vettore delle curve elementari
vCrvCompoRes.clear() ;
// assegno dati di modulo
PocketParams myParams ;
myParams.nType = nType ;
myParams.dRad = dRad ;
myParams.dRadialOffset = dRadOffs ;
myParams.bSmooth = bSmooth ;
myParams.dAngle = dAngle ;
myParams.dSideStep = dStep ;
myParams.bInvert = bInvert ;
myParams.bAvoidOpt = bAvoidOpt ;
if ( pSfrLimit != nullptr && pSfrLimit->IsValid())
myParams.SfrLimit.CopyFrom( pSfrLimit) ;
Point3d ptCenter ; pSfr->GetCentroid( ptCenter) ;
if ( ! myParams.frLocXY.Set( ptCenter, pSfr->GetNormVersor()) ||
! myParams.frLocXY.Rotate( ptCenter, pSfr->GetNormVersor() , myParams.dAngle))
return false ;
// porto la superficie da svuotare in tale sistema
PtrOwner<ISurfFlatRegion> pSfrAdj( pSfr->Clone()) ;
if ( IsNull( pSfrAdj) || ! pSfrAdj->ToLoc( myParams.frLocXY))
return false ;
// porto la superficie limite ( se valida) in tale sistema
if ( myParams.SfrLimit.IsValid())
myParams.SfrLimit.ToLoc( myParams.frLocXY) ;
// ------------ gestione dei lati aperti -------------------
// modifico la superficie da svuotare estendendo i lati aperti
bool bSkipPocket = false ; // se la superficie si svuota mediante curve singole di primo Offset dei chiusi
if ( ! ModifySurfByOpenEdges( pSfrAdj, myParams, vCrvCompoRes, bSkipPocket))
return false ;
// se le curve di primo offset che ho ottenuto, svuotano tutta la superficie, allora ho finito
if ( bSkipPocket) {
// riporto le curve in Globale
for ( int i = 0 ; i < int( vCrvCompoRes.size()) ; ++ i)
vCrvCompoRes[i]->ToGlob( myParams.frLocXY) ;
return true ;
}
// porto una copia della superficie originale nel frame creato
PtrOwner<ISurfFlatRegion> pSfr_Loc( CloneSurfFlatRegion( pSfr)) ;
if ( IsNull( pSfr_Loc) || ! pSfr_Loc->IsValid() || ! pSfr_Loc->ToLoc( myParams.frLocXY))
return false ;
// ------------ calcolo delle curve elementari della superficie -------------------
switch ( nType) {
case POCKET_ZIGZAG :
if ( ! AddZigZag( pSfrAdj, pSfr_Loc, myParams, vCrvCompoRes))
return false ;
break ;
case POCKET_ONEWAY :
if ( ! AddOneWay( pSfrAdj, pSfr_Loc, myParams, vCrvCompoRes))
return false ;
break ;
case POCKET_SPIRALIN :
if ( ! AddSpiralIn( pSfrAdj, pSfr_Loc, myParams, vCrvCompoRes))
return false ;
break ;
case POCKET_SPIRALOUT :
if ( ! AddSpiralOut( pSfrAdj, pSfr, myParams, vCrvCompoRes))
return false ;
break ;
}
// riporto tutte le curve ottenute in globale
for ( int i = 0 ; i < int( vCrvCompoRes.size()) ; ++ i)
vCrvCompoRes[i]->ToGlob( myParams.frLocXY) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustZigZagPathTangentLinks( ICurveComposite* pCrvCompo)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// miglioro la curva
pCrvCompo->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
// scorro tutte le curve
int nCurrTmpProp = -1, nCurrTmpProp1 = -1 ;
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
if ( pCrvCompo->GetCurveTempProp( u, nCurrTmpProp, 1) && nCurrTmpProp == -1) {
// ricavo il link
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
if ( IsNull( pCrvLink))
return false ;
pCrvLink->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ;
for ( int uu = u + 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) {
if ( pCrvCompo->GetCurveTempProp( uu, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
if ( ! pCrvLink->AddCurve( pCrvCompo->GetCurve( uu)->Clone()))
return false ;
}
else
break ;
}
if ( ! pCrvLink->IsValid())
return false ;
// se ho più di una curva nel link
if ( pCrvLink->GetCurveCount() > 1) {
// ricavo la curva precendente al Link ( se presente e se prima curva del Link è lineare)
if ( u != 0 && pCrvLink->GetFirstCurve()->GetType() == CRV_LINE) {
const ICurve* pCrvSegPrec = pCrvCompo->GetCurve( u - 1) ;
if ( pCrvSegPrec == nullptr || pCrvSegPrec->GetType() != CRV_LINE)
return false ;
// ricavo la sua direzione finale
Vector3d vtZigZagEndDir ; pCrvSegPrec->GetEndDir( vtZigZagEndDir) ;
// ricavo la direzione iniziale del link
Vector3d vtLinkStartDir ; pCrvLink->GetStartDir( vtLinkStartDir) ;
// se i vettori sono paralleli, considero la prima curva del link come parte del percorso a ZigZag
if ( AreSameVectorApprox( vtZigZagEndDir, vtLinkStartDir))
pCrvCompo->SetCurveTempProp( u, 0, 1) ;
}
// ricavo la curva successiva al Link ( se presente e se ultima curva del Link è lineare)
if ( u + pCrvLink->GetCurveCount() < pCrvCompo->GetCurveCount() && pCrvLink->GetLastCurve()->GetType() == CRV_LINE) {
const ICurve* pCrvSegSucc = pCrvCompo->GetCurve( u + pCrvLink->GetCurveCount()) ;
if ( pCrvSegSucc == nullptr || pCrvSegSucc->GetType() != CRV_LINE)
return false ;
// ricavo la sua direzione inziale
Vector3d vtZigZagStartDir ; pCrvSegSucc->GetStartDir( vtZigZagStartDir) ;
// ricavo la direzione finale del link
Vector3d vtLinkStartDir ; pCrvLink->GetEndDir( vtLinkStartDir) ;
// se i vettori sono paralleli, considero l'ultima curva del link come parte del percorso a ZigZag
if ( AreSameVectorApprox( vtZigZagStartDir, vtLinkStartDir))
pCrvCompo->SetCurveTempProp( u + pCrvLink->GetCurveCount() - 1, 0, 1) ;
}
}
// aggiorno
u += pCrvLink->GetCurveCount() ; // così la successiva è corretta nel ciclo
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustLinkSameY( ICurveComposite* pCrvLink, bool& bSplit)
{
// controllo dei parametri
if ( pCrvLink == nullptr || ! pCrvLink->IsValid())
return false ;
bSplit = false ;
// ricavo gli estremi del Link
Point3d ptS ; pCrvLink->GetStartPoint( ptS) ;
Point3d ptE ; pCrvLink->GetEndPoint( ptE) ;
// ricavo il versore uscente da ptS e diretto verso ptE
Vector3d vtDir = ptE - ptS ;
if ( vtDir.IsValid() && ! vtDir.IsSmall())
vtDir.Normalize() ;
else
return false ;
// creo un frame orientato come questo versore
Frame3d frLink ;
frLink.Set( ptS, Z_AX, vtDir) ;
if ( ! frLink.IsValid())
return false ;
// ricavo il box3d del Link in questo frame
BBox3d BBoxLink ;
pCrvLink->ToLoc( frLink) ;
if ( ! pCrvLink->GetLocalBBox( BBoxLink) || BBoxLink.IsEmpty())
return false ;
pCrvLink->ToGlob( frLink) ;
// controllo la dimensione del Box in Y
if ( abs( BBoxLink.GetDimY()) > 20 * EPS_SMALL)
bSplit = true ;
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustLinkDifferentY( const ICurve* pCrvSegPrec, const ICurve* pCrvSegSucc,
const ICurveComposite* pCrvLink, const PocketParams& PockParams, bool& bSplit)
{
// controllo dei parametri
if ( pCrvLink == nullptr || ! pCrvLink->IsValid())
return false ;
if ( pCrvSegPrec == nullptr && pCrvSegSucc == nullptr)
return true ;
bSplit = false ;
// creo un Trapezoide per regione di incidenza
Point3d ptH ;
PtrOwner<ISurfFlatRegion> pSfrTrap( CreateSurfFlatRegion()) ;
PtrOwner<ICurveComposite> pCrvTrapBorder( CreateCurveComposite()) ;
if ( IsNull( pSfrTrap) ||
IsNull( pCrvTrapBorder))
return false ;
// 0) primo tratto
if ( pCrvSegPrec != nullptr) {
// se presente lo aggiungo
if ( ! pCrvTrapBorder->AddCurve( pCrvSegPrec->Clone()))
return false ;
}
else {
// se non presente, inserisco il punto iniziale del Link
pCrvLink->GetStartPoint( ptH) ;
pCrvTrapBorder->AddPoint( ptH) ;
}
// 1) tratto lineare fino al punto finale del Link
if ( ! pCrvLink->GetEndPoint( ptH) ||
! pCrvTrapBorder->AddLine( ptH))
return false ;
// 2) terzo tratto
if ( pCrvSegSucc != nullptr) {
// se presente lo aggiungo
if ( ! pCrvTrapBorder->AddCurve( pCrvSegSucc->Clone()))
return false ;
}
// se non presente non aggiungo nulla, ho alla peggio il Link
// 4) ultimo tratto lineare per chiudere la curva
if ( ! pCrvTrapBorder->Close() || // ora creo la FlatRegion a trapezio
! pSfrTrap->AddExtLoop( Release( pCrvTrapBorder)) ||
! pSfrTrap->IsValid())
return false ;
// oriento
if ( AreOppositeVectorApprox( Z_AX, pSfrTrap->GetNormVersor()))
pSfrTrap->Invert() ;
// effettuo un piccolo Offset per una maggiore tolleranza ( se possibile)
PtrOwner<ISurfFlatRegion> pSfrTrap_c( pSfrTrap->Clone()) ;
if ( IsNull( pSfrTrap_c))
return false ;
if ( ! pSfrTrap->Offset( - PockParams.dRad / 5, ICurve::OFF_CHAMFER))
pSfrTrap.Set( pSfrTrap_c) ;
// se il Link interseca questa regione, allora va eliminato
CRVCVECTOR ccClass ;
if ( pSfrTrap->GetCurveClassification( *pCrvLink, EPS_SMALL, ccClass))
bSplit = int( ccClass.size()) > 1 ;
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustLinkZigZagInfill( ICurveComposite* pCrvCompo, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vNewCrv)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
vNewCrv.clear() ;
// sistemo la curva per evitare che i tratti estremi dei link siano lineari ed in tangenza con percorsi ZigZag
if ( ! AdjustZigZagPathTangentLinks( pCrvCompo))
return false ;
// definisco un vettore di intervalli su cui spezzare la curva
struct CopyRange {
double dParS ;
double dParE ;
} ;
vector<CopyRange> vIntervals ;
// parametri
int nCurrTmpProp1 = 0 ;
int nCrvStartBreak = 0 ;
// NB. I tratti di ZigZag che seguono dei contorni ( i Links) hanno TmpProp1 impostata come -1
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
if ( pCrvCompo->GetCurveTempProp( u, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
// ricavo il link
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
if ( IsNull( pCrvLink))
return false ;
pCrvLink->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ;
for ( int uu = u + 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) {
if ( pCrvCompo->GetCurveTempProp( uu, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
if ( ! pCrvLink->AddCurve( pCrvCompo->GetCurve( uu)->Clone()))
return false ;
}
else
break ;
}
if ( ! pCrvLink->IsValid())
return false ;
// ricavo il punto iniziale e finale del Link
Point3d ptS ; pCrvLink->GetStartPoint( ptS) ;
Point3d ptE ; pCrvLink->GetEndPoint( ptE) ;
// parametro per spezzare o meno la curva, togliendo il Link
bool bSplit = false ;
// se il Link collega due tratti alla stessa quota...
if ( abs( ptS.y - ptE.y) < 20 * EPS_SMALL) {
if ( ! AdjustLinkSameY( pCrvLink, bSplit))
return false ;
}
// se il Link collega due tratti a quote diverse
else {
if ( ! AdjustLinkDifferentY( pCrvCompo->GetCurve( u - 1), pCrvCompo->GetCurve( u + pCrvLink->GetCurveCount()),
pCrvLink, PockParams, bSplit))
return false ;
}
// aggiorno
u += pCrvLink->GetCurveCount() ; // così la successiva è corretta nel ciclo
if ( bSplit) {
// se dimensione sopra la tolleranza, allora spezzo la curva e rimuovo il link
CopyRange CR {
1. * nCrvStartBreak, // dParS
1. * ( u - pCrvLink->GetCurveCount()) // dParE
} ;
// se il primo tratto è un link non valido, non considero l'intervallo
if ( ! ( nCrvStartBreak == 0 && abs( CR.dParS - CR.dParE) < 20 * EPS_SMALL))
vIntervals.push_back( CR) ;
nCrvStartBreak = u ;
}
}
}
// se non ho mai trovato link non validi, lascio la curva invariata
if ( vIntervals.empty())
vNewCrv.emplace_back( pCrvCompo->Clone()) ;
else {
// per ogni intervallo...
for ( int i = 0 ; i < int( vIntervals.size()) ; ++ i)
vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( vIntervals[i].dParS, vIntervals[i].dParE))) ;
// ultimo intervallo ( se l'ultimo intervallo è un link non valido, non inserisco)
if ( abs( vIntervals.back().dParE - pCrvCompo->GetCurveCount()) > 20 * EPS_SMALL)
vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nCrvStartBreak, pCrvCompo->GetCurveCount()))) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CalcZigZagInfill( const ISurfFlatRegion* pSfr, double dStep, bool bSmooth, bool bRemoveOverlapLink, ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid() || dStep < EPS_SMALL)
return false ;
vCrvCompoRes.clear() ;
PocketParams myParams ;
myParams.bSmooth = false ;
myParams.dRad = 0.5 * dStep ;
// vettore con i percorsi a ZigZag
ICRVCOMPOPOVECTOR vpCrvs ;
// ciclo su tutti i chunk della regione
for ( int c = 0 ; c < pSfr->GetChunkCount() ; ++ c) {
// prendo il Chunk
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( c)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
return false ;
// calcolo i percorsi a ZigZag
if ( ! CalcZigZag( pSfrChunk, myParams, vpCrvs, true))
return false ;
if ( bRemoveOverlapLink) {
ICRVCOMPOPOVECTOR vCrvFinal ;
for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u)
vCrvFinal.emplace_back( Release( vpCrvs[u])) ;
vpCrvs.clear() ;
// sistemo le curve ottenute, aggiustando i Links
for ( int u = 0 ; u < int( vCrvFinal.size()) ; ++ u) {
ICRVCOMPOPOVECTOR vNewCrv ;
if ( ! AdjustLinkZigZagInfill( vCrvFinal[u], myParams, vNewCrv))
return false ;
for ( int uu = 0 ; uu < int( vNewCrv.size()) ; ++ uu)
vpCrvs.emplace_back( Release( vNewCrv[uu])) ;
}
}
// inserisco i percorsi da ritornare
for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) {
if ( bSmooth) {
myParams.bSmooth = true ;
ModifyCurveToSmoothed( vpCrvs[u], myParams, myParams.dRad / 16, myParams.dRad / 16, false) ;
}
vCrvCompoRes.emplace_back( Release( vpCrvs[u])) ;
}
}
return true ;
}