Files
EgtGeomKernel/CalcPocketing.cpp
T
Riccardo Elitropi 4f3fe68283 EgtGeomKernel :
- aggiunta in CalcPocketing la gestione dei punti iniziali
- migliorie varie.
2024-08-22 12:54:25 +02:00

7426 lines
307 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 dMaxOpenEdgeRad = 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
double dOffsExtra = 2. ; // Offset aggiuntivo per percosi a ZigZag/OneWay se richieste le curve di bordo
Frame3d frLocXY ; // frame per conti nel piano XY
int nOffsType = ICurve::OFF_FILLET ; // tipologia di Offset per estensione degli aperti
double dOpenEdgeRad = 0. ; // raggio effettivo di estensione degli aperti
} ;
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)
{
// se non rischiesto il calcolo della Feed, lascio quella standard
if ( ! PockParams.bCalcFeed)
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) ;
GetFeedForParam( PockParams.dOpenEdgeRad, PockParams, dFeed) ;
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, bool bIsLink,
bool bFirstOffs, 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) ;
// Se link di conformal ZigZag
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG && bIsLink)
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 == nullptr || ! pSrfRemoved->IsValid() || pSrfRemoved->GetChunkCount() == 0) {
// controllo eventuali sovrapposizioni con lati aperti
return AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ;
}
// parametro di Offset per regione svuotata ( restringo la superficie in maniera appropriata)
double dOffs = 0. ;
if ( bIsLink)
dOffs = - PockParams.dRad ;
else if ( PockParams.dRad < PockParams.dSideStep)
dOffs = PockParams.dSideStep - PockParams.dRad ;
// clono la superificie ( valida)
PtrOwner<ISurfFlatRegion> pSrfRemovedOffs( pSrfRemoved->CreateOffsetSurf( dOffs, ICurve::OFF_FILLET)) ;
if ( IsNull( pSrfRemovedOffs) || ! pSrfRemovedOffs->IsValid() || pSrfRemovedOffs->GetChunkCount() == 0)
return true ; // esco ( sempre con Feed Minima)
// classifico le parti interne alla superificie creata solo dagli Offset
CRVCVECTOR ccClass ;
if ( ! pSrfRemovedOffs->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
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)
double dCurrFeed = GetMinFeed( PockParams) ;
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)) ;
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, GetMaxFeed( PockParams), 0) ;
}
}
else { // ---------------- NEL CASO DI LINK ----------------
// le curve con lunghezza < dToll vanno modificate
for ( int j = 0 ; j < pCrv->GetCurveCount() ; ++ j) {
double dLen = EPS_SMALL ;
pCrv->GetCurve( j)->GetLength( dLen) ;
if ( dLen < dToll + 5 * EPS_SMALL) {
// se curva piccola, ricavo il tratto successivo lungo quanto il diametro
double dLenStart ;
pCrv->GetLengthAtParam( j, dLenStart) ;
double dLenEnd = dLenStart + dLen + 2 * PockParams.dRad ;
double dUE = pCrv->GetCurveCount() ;
pCrv->GetParamAtLength( dLenEnd, dUE) ;
bool bFound = false ;
for ( int k = j + 1 ; k < int( ceil( dUE)) ; ++ k) {
double dLenH = EPS_SMALL ;
pCrv->GetCurve( k)->GetLength( dLenH) ;
if ( dLenH < dToll + 5 * EPS_SMALL)
continue ;
// cerco tra le curve successive vicine se ne trovo una con Feed Minima
double dParam ;
pCrv->GetCurveTempParam( k, dParam, 0) ;
if ( abs( dParam - GetMinFeed( PockParams)) < 5 * EPS_SMALL) {
pCrv->SetCurveTempParam( k, dParam, 0) ;
bFound = true ;
break ;
}
}
if ( ! bFound && j != 0) {
// arrivato qui, so che successivamente non ho curve con Feed Minima vicine
double dParam ; pCrv->GetCurveTempParam( j - 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( j, dParam, 0) ;
}
else
pCrv->SetCurveTempParam( j, GetMinFeed( PockParams), 0) ;
}
}
}
if ( bFirstOffs)
AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ;
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
ExtendPath( ICurveComposite* pCompo, const ISurfFlatRegion* pSfr, const PocketParams& PockParams,
Vector3d& vtFirstOut, bool bEndOrStart, bool& bOkExtended)
{
/*
Estensione della curva pCompo con un segmento lineare.
bEndOrStart: definisce se la curva va estesa all'inizio o alla fine
vtFirstOut: prima direzione su cui tentare l'entrata, se questa non andasse bene
ruoto progressivamente di 90°
La curva viene estesa di un tratto lineare in direzione vtRotOut per una lunghezza
pari a Rad + OffsR + OpenMinSafe.
*/
// controllo dei parametri
if ( pCompo == nullptr || ! pCompo->IsValid() ||
pSfr == nullptr || ! pSfr->IsValid())
return false ;
bOkExtended = false ;
// recupero il punto iniziale/finale della curva
Point3d pt ;
if ( bEndOrStart) {
if ( ! pCompo->GetEndPoint( pt))
return false ;
}
else {
if ( ! pCompo->GetStartPoint( pt))
return false ;
}
// ruoto il versore di uscita cercando un'entrata valida
Vector3d vtRotOut = vtFirstOut ;
double dMinDist = PockParams.dRad + PockParams.dRadialOffset ;
for ( int i = 0 ; i < 4 ; ++ i) {
// evito il 180deg
if ( i != 2) {
// calcolo il punto di caduta dell'utensile
Point3d ptFall = pt + vtRotOut * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
// controllo che sia sufficientemente distante dalla superficie limite
bool bInside = true ;
bool bOkOut = true ;
if ( PockParams.SfrLimit.IsValid())
bOkOut = ( IsPointInsideSurfFr( ptFall, &PockParams.SfrLimit, dMinDist, bInside) && ! bInside) ;
if ( bOkOut)
bOkOut = ( IsPointInsideSurfFr( ptFall, pSfr, dMinDist, bInside) && ! bInside) ;
if ( bOkOut) {
// aggiungo al ritorno l'uscita
pCompo->AddLine( ptFall, bEndOrStart) ;
pCompo->SetCurveTempProp( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0), TEMP_PROP_OUT_START, 0) ;
pCompo->SetCurveTempParam( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0),
( bEndOrStart ? GetMaxFeed( PockParams) : GetMinFeed( PockParams)), 0) ;
bOkExtended = true ;
return true ;
}
}
vtRotOut.Rotate( Z_AX, - ANG_RIGHT) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustContourStart( ICurveComposite* pCompo, const PocketParams& PockParams)
{
// controllo dei parametri
if ( pCompo == nullptr || ! pCompo->IsValid())
return false ;
// cerco il tratto lineare chiuso più lungo ...
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() == TEMP_PROP_CLOSE_EDGE &&
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 ( non lineare)
if ( nMax < 0 || dLenMax < 2. * PockParams.dRad + 2 * PockParams.dRadialOffset) {
i = 0 ;
pCrv = pCompo->GetFirstCurve() ;
while ( pCrv != nullptr) {
double dLen ;
if ( pCrv->GetType() != CRV_LINE && pCrv->GetTempProp() == TEMP_PROP_CLOSE_EDGE &&
pCrv->GetLength( dLen) && dLen > dLenMax) {
dLenMax = dLen ;
nMax = i ;
}
++ i ;
pCrv = pCompo->GetNextCurve() ;
}
}
// 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) ;
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)...
AdjustContourStart( pCrvCompo, PockParams) ;
// 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 PocketParams& PockParams,
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, PockParams.nOffsType) ||
! 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, PockParams.nOffsType)) {
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) ;
// verifico se una delle due curve esiste
bool bExistExt = ( ! IsNull( pOffsExt) && pOffsExt->IsValid()) ;
bool bExistInt = ( ! IsNull( pOffsInt) && pOffsInt->IsValid()) ;
PtrOwner<ICurveComposite> pCrvExtLoopSurfInc( CreateCurveComposite()) ;
if ( IsNull( pCrvExtLoopSurfInc))
return false ;
if ( bExistExt && bExistInt) {
Point3d pt2 ; pOffsInt->GetEndPoint( pt2) ;
Point3d pt3 ; pOffsInt->GetStartPoint( pt3) ;
Point3d pt5 ; pOffsExt->GetStartPoint( pt5) ;
if ( ! pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ||
! pCrvExtLoopSurfInc->AddLine( pt1) ||
! pCrvExtLoopSurfInc->AddLine( pt2) ||
! pOffsInt->Invert() ||
! pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ||
! pCrvExtLoopSurfInc->AddLine( pt4) ||
! pCrvExtLoopSurfInc->AddLine( pt5))
return false ;
}
else if ( bExistExt) {
Point3d pt5 ; pOffsExt->GetStartPoint( pt5) ;
if ( ! pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ||
! pCrvExtLoopSurfInc->AddLine( pt1) ||
! pCrvExtLoopSurfInc->AddLine( pt4) ||
! pCrvExtLoopSurfInc->AddLine( pt5))
return false ;
}
else if ( bExistInt) {
Point3d pt5 ; pOffsInt->GetStartPoint( pt5) ;
if ( ! pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ||
! pCrvExtLoopSurfInc->AddLine( pt1) ||
! pCrvExtLoopSurfInc->AddLine( pt4) ||
! pCrvExtLoopSurfInc->AddLine( pt5))
return false ;
}
else
return false ;
// per sicurezza...
pCrvExtLoopSurfInc->Close() ;
// creo la regione
if ( ! pSfrInc->AddExtLoop( Release( pCrvExtLoopSurfInc)))
return false ;
// guarda Z_AX come normale
if ( pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0) {
Vector3d vtN = pSfrInc->GetNormVersor() ;
if ( AreOppositeVectorApprox( vtN, Z_AX))
pSfrInc->Invert() ;
return true ;
}
return false ;
}
//----------------------------------------------------------------------------
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, PockParams, 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, PockParams.nOffsType))
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")
bool bSquareEnds = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ;
bool bSquareMids = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ;
PtrOwner<ISurfFlatRegion> pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ * 0.5, bSquareEnds, bSquareMids)) ;
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, 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.dMaxOpenEdgeRad) > 0 && PockParams.dMaxOpenEdgeRad < dRad)
dRad = PockParams.dMaxOpenEdgeRad ;
// se lavorazione ZigZag/OneWay con richiesta di curve di Bordo, gli aperti vanno leggermente allargati
if ( PockParams.bAllowZigZagOneWayBorders &&
( PockParams.nType == POCKET_ZIGZAG || PockParams.nType == POCKET_ONEWAY))
dRad += PockParams.dOffsExtra ;
// se lavorazione a ZigZag con smusso, esco di Step/2 per pulire gli aperti
if ( PockParams.nType == POCKET_ZIGZAG && PockParams.bSmooth)
dRad += max( PockParams.dSideStep / 2, PockParams.dRad / 8) ;
// salvo il raggio trovato
PockParams.dOpenEdgeRad = dRad ;
// 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.dMaxOpenEdgeRad) > 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
ChangePtStartForSinglePocketCurve( ICurveComposite* pCrvCompo, const PocketParams& PockParams,
const Point3d& ptRef)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// se la curva è aperta
if ( ! pCrvCompo->IsClosed()) {
// se non ho un punto di riferimento, allora non faccio nulla
if ( ! ptRef.IsValid())
return true ;
// se ho un punto di riferimento controllo quale estremo è più vicino
Point3d ptStart ; pCrvCompo->GetStartPoint( ptStart) ;
Point3d ptEnd ; pCrvCompo->GetEndPoint( ptEnd) ;
if ( SqDist( ptRef, ptEnd) < SqDist( ptRef, ptStart))
pCrvCompo->Invert() ;
}
// se la curva è chiusa
else {
// se non ho un punto di riferimento, scelgo il tratto più lungo
if ( ! ptRef.IsValid()) {
ResetCurveTempProps( pCrvCompo) ;
AdjustContourStart( pCrvCompo, PockParams) ;
}
// se ho un punto di riferimento
else {
// cerco il punto più vicino
DistPointCurve DistPtCrv( ptRef, *pCrvCompo) ;
double dPar = 0. ;
int nFlag ;
if ( ! DistPtCrv.GetParamAtMinDistPoint( 0, dPar, nFlag))
return false ;
pCrvCompo->ChangeStartPoint( dPar) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AdvanceExtendCurves( ICRVCOMPOPOVECTOR& vCrvCompo, const ISurfFlatRegion* pSfr, const PocketParams& PockParams)
{
// se non ho curve, allora non faccio nulla
if ( vCrvCompo.empty())
return true ;
// per ogni curva composita...
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
// controllo validità della curva
if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid())
return false ;
// cambio il punto iniziale della curva
if ( ! ChangePtStartForSinglePocketCurve( vCrvCompo[i], PockParams, PockParams.ptStart))
return false ;
}
// se superficie tutta chiusa, allora ho finito
if ( PockParams.bAllClosed)
return true ;
/*
L'estensione della curva permettendo un'entrata da fuori è valida se il
nuovo punto di Inizio ( ptFall) :
- E' al di fuori della superficie Limite ( di almeno Rad + OffsR)
- E' al di fuori della superifice di svuotatura ( di almeno dRad + OffsR)
*/
// scorro ogni singola curva composita
const int MAX_ITER = 4 ;
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
// controllo se la curva è chiusa o aperta
bool bIsClosed = vCrvCompo[i]->IsClosed() ;
// calcolo il numero massimo di punti in cui testare l'entrata da fuori
int nMaxIter = ( bIsClosed ? MAX_ITER : 1) ;
double dLen = EPS_SMALL ;
if ( nMaxIter == MAX_ITER)
vCrvCompo[i]->GetLength( dLen) ;
// scorro il numero di punti su cui tentare l'entrata da fuori
for ( int j = 0 ; j < nMaxIter ; ++ j) {
if ( j > 0) {
double dParAtLen = EPS_SMALL ;
vCrvCompo[i]->GetParamAtLength( dLen / nMaxIter, dParAtLen) ;
vCrvCompo[i]->ChangeStartPoint( dParAtLen) ;
}
// ricavo vettore tangente iniziale e provo ad estendere
Vector3d vtStart ; vCrvCompo[i]->GetStartDir( vtStart) ;
vtStart.Invert() ;
bool bIsStartExtended = false ;
if ( ! ExtendPath( vCrvCompo[i], pSfr, PockParams, vtStart, false, bIsStartExtended))
return false ;
// se aperta, controllo la fine
if ( ! bIsClosed) {
// ricavo vettore tangente finale e provo ad estendere
Vector3d vtEnd ; vCrvCompo[i]->GetEndDir( vtEnd) ;
bool bIsEndExtended = false ;
if ( ! ExtendPath( vCrvCompo[i], pSfr, PockParams, vtEnd, true, bIsEndExtended))
return false ;
if ( bIsEndExtended)
break ;
}
if ( bIsStartExtended)
break ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPocketCurvesByClosedEdges( 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) {
// se tratto chiuso
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) {
OffsetCurve OffsCrv ; // con offset verso l'interno ( è il loop esterno)
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, TEMP_PROP_CLOSE_EDGE) ;
nCurrTmpProp = 0 ; // aggiorno il Flag
}
// 2) e 3)
bValidIslands = ( ( nCurrTmpProp == TEMP_PROP_CLOSE_EDGE && bExtAllOpen) ||
( nCurrTmpProp == TEMP_PROP_OPEN_EDGE && bExtAllClose)) ;
}
if ( ! bValidIslands)
return true ;
// nel caso 2) devo inserire le curve
if ( bExtAllOpen) {
// 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 nC = 0 ; nC < pSrfIslands->GetChunkCount() ; ++ nC)
for ( int nL = 0 ; nL < pSrfIslands->GetLoopCount( nL) ; ++ nL)
vCrvCompoRes_tmp.emplace_back( ConvertCurveToComposite( pSrfIslands->GetLoop( nC, nL))) ;
}
}
// 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) ;
// se richiesta inversione della curva, allora inverto se chiusa
if ( PockParams.bInvert)
vCrvCompoRes_tmp[i]->Invert() ;
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 ;
GetPocketCurvesByClosedEdges( pSfrChunk, PockParams, vCrvCompoRes, bIsAllRemoved) ;
if ( bIsAllRemoved) {
// se ho rimosso tutto, allora passo la chunk successivo
++ nChunkOneCurve ;
continue ;
}
// controllo se la lavorazione ammatte estensione da aperti
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG)
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 ;
// estendo le curve, definendo ptStart e calcolando le Feeds
if ( ! AdvanceExtendCurves( vCrvCompoRes, pSfr, PockParams))
return false ;
// imposto le Feed per tali curve se richiesto
for ( int i = 0 ; i < int( vCrvCompoRes.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 = vCrvCompoRes[i]->GetTempParam( 0) ;
// calcolo la Feed proporzionale a tale Offset
double dFeed ;
GetFeedForParam( 2 * dMaxOffs, PockParams, dFeed) ;
AssignCustomFeed( vCrvCompoRes[i], PockParams, dFeed) ;
// imposto il flag di curva singola
vCrvCompoRes[i]->SetTempProp( TEMP_PROP_SINGLE_CURVE, 0) ;
}
// 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 ( PockParams.nType != POCKET_CONFORMAL_ZIGZAG) {
if ( ! pSrfFinal->IsValid())
return false ;
pSfr->Clear() ;
pSfr->CopyFrom( pSrfFinal) ;
}
return ( pSfr->IsValid() && pSfr->GetChunkCount() > 0) ;
}
// ***************************************************************************
// ------------- SCELTA DEL PUNTO INIZIALE -----------------------------------
// ***************************************************************************
//----------------------------------------------------------------------------
static bool
GetParamOnOpenCurve( const ICurveComposite* pCompo, const PocketParams& PockParams, bool& bOutStart,
Point3d& ptStart, Vector3d& vtOut)
{
// controllo dei parametri
if ( pCompo == nullptr || ! pCompo->IsValid())
return false ;
// verifico se tutti i lati sono aperti
bool bAllOpen = true ;
const ICurve* pMyCrv = pCompo->GetFirstCurve() ;
while ( pMyCrv != nullptr) {
if ( pMyCrv->GetTempProp() != TEMP_PROP_OPEN_EDGE) {
bAllOpen = false ;
break ;
}
pMyCrv = pCompo->GetNextCurve() ;
}
// 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() == TEMP_PROP_OPEN_EDGE)
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() == TEMP_PROP_OPEN_EDGE)
pNextCrv->GetLength( dLenNext) ;
// verifico la curva corrente
if ( pCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
// contributo dalle entità adiacenti ( se non tutte aperte)
double dLenAgg = 0. ;
if ( ! bAllOpen) {
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
Vector3d vtPrevEnd ; pPrevCrv->GetEndDir( vtPrevEnd) ;
Vector3d vtStart ; pCrv->GetStartDir( vtStart) ;
dLenAgg += max( 0.4, vtPrevEnd * vtStart) * dLenPrev ;
}
if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
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 > ptStart.z + 100 * EPS_SMALL) ||
( ! PockParams.bAboveHead && ptTest.z < ptStart.z - 100 * EPS_SMALL) ||
( abs( ptTest.z - ptStart.z) < 100 * EPS_SMALL && ptTest.y < ptStart.y - 100 * EPS_SMALL)) {
dMaxLen = max( dMaxLen, dLen + dLenAgg) ;
ptStart = ptTest ;
// versore ortogonale verso l'esterno
pCrv->GetMidDir( vtOut) ;
vtOut.Rotate( Z_AX, 0, -1) ;
}
}
// se più lunga ( o non già trovata)
else if ( dLen + dLenAgg > dMaxLen || ! bFound) {
dMaxLen = dLen + dLenAgg ;
if ( pCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart, &vtOut)) {
// versore ortogonale verso l'esterno
vtOut.Normalize() ;
vtOut.Rotate( Z_AX, 0, -1) ;
bFound = true ;
}
}
dLenPrev = dLen ;
}
}
else
dLenPrev = 0 ;
// vado alla successiva
pPrevCrv = pCrv ;
pCrv = ( bNextOk ? pNextCrv : nullptr) ;
}
return bFound ;
}
//----------------------------------------------------------------------------
static bool
GetPtStartOnOpenEdgeByOrigCurve( const ICurveComposite* pCrvOrig, const PocketParams& PockParams,
const ICurveComposite* pCrvCompo, Point3d& ptStart, Vector3d& vtMidOut,
bool& bMidOut)
{
// controllo dei parametri
if ( pCrvOrig == nullptr || ! pCrvOrig->IsValid() ||
pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
bMidOut = false ;
// cerco il lato aperto più lungo ( sufficientemente lungo)
double dLenRef = ( 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) - 50 * EPS_SMALL ;
for ( int i = 0 ; i < pCrvOrig->GetCurveCount() ; ++ i) {
// escludo le sottocurve chiuse
if ( pCrvOrig->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
continue ;
// recupero la curva i-esima aperta dalla curva originale
const ICurve* pCrvOpen = pCrvOrig->GetCurve( i) ;
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
return false ;
// ricavo la lunghezza di tale curva
double dLen = 0. ;
pCrvOpen->GetLength( dLen) ;
// se lunghezza accettabile o maggiore della massima trovata
if ( dLen > dLenRef) {
Point3d ptSTmp ;
Vector3d vtMidOutTmp ;
// ricavo il punto medio e il vettore tangente ad esso associato
if ( pCrvOpen->GetPointD1D2( 0.5, ICurve::FROM_MINUS, ptSTmp, &vtMidOutTmp)) {
// cerco la sottocurva più vicina a ptSTmp
int nFlag ;
double dMyPar ;
if ( DistPointCurve( ptSTmp, *pCrvCompo).GetParamAtMinDistPoint( 0, dMyPar, nFlag)) {
int nCrv = int( floor( dMyPar)) ;
// recupero tale sottocurva e controllo che anche essa sia OPEN
const ICurve* pMyCrv = pCrvCompo->GetCurve( nCrv) ;
if ( pMyCrv != nullptr && pMyCrv->IsValid() && pMyCrv->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
// recupero ptStart
pMyCrv->GetMidPoint( ptStart) ;
// versore d'uscita
vtMidOutTmp.Normalize() ;
vtMidOutTmp.Rotate( Z_AX, - ANG_RIGHT) ;
vtMidOut = vtMidOutTmp ;
// flag per possibile entrata da fuori
bMidOut = true ;
// aggiornamento della lunghezza di riferimento
dLenRef = dLen ;
}
}
}
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPtStartOnOpenEdgeByPtRef( const Point3d& ptRef, const PocketParams& PockParams,
const ICurveComposite* pCrvCompo, const ICurveComposite* pCrvOrig,
Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut)
{
// controllo dei parametri
if ( ! ptRef.IsValid() || pCrvCompo == nullptr || ! pCrvCompo->IsValid() ||
! ptRef.IsValid())
return false ;
bMidOut = false ;
// lunghezza minima di riferimento per un lato aperto valido
double dLenRef = ( 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) - 50 * EPS_SMALL ;
// cerco la curva OPEN a minima distanza da ptRef, sufficientemente lunga
int nStartCrv = -1 ;
double dMinDist = INFINITO ;
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
// considero solo le sottocurve OPEN
if ( pCrvCompo->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
// sufficientemente lunghe
double dLen ;
if ( pCrvCompo->GetCurve( i)->GetLength( dLen) && dLen > dLenRef) {
DistPointCurve DPC( ptRef, *pCrvCompo->GetCurve( i)) ;
double dCurrDist = INFINITO ;
// sufficiententemente vicine a ptRef
if ( DPC.GetSqDist( dCurrDist) && dCurrDist < dMinDist) {
dMinDist = dCurrDist ;
nStartCrv = i ;
}
}
}
}
// se non ho trovato nulla, allora cerco un punto iniziale valido su una sottocurva OPEN
if ( nStartCrv == -1 && pCrvOrig != nullptr && pCrvOrig->IsValid()) {
return GetPtStartOnOpenEdgeByOrigCurve( pCrvOrig, PockParams, pCrvCompo, ptStart, vtMidOut, bMidOut) ;
}
// se ho trovato la curva più vicina, cerco un punto iniziale valido
else {
const ICurve* pMyCrv = pCrvCompo->GetCurve( nStartCrv) ;
if ( pMyCrv != nullptr && pMyCrv->IsValid()) {
// ricavo ptStart e vettore tangente
if ( pMyCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart, &vtMidOut)) {
// versore ortogonale verso l'esterno
vtMidOut.Normalize() ;
vtMidOut.Rotate( Z_AX, 0, -1) ;
// flag per possibile entrata da fuori
bMidOut = true ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPtStartOnHomogeousEdges( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
int nType, const Point3d& ptRef, bool& bMidOut, Point3d& ptStart,
Vector3d& vtMidOut)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
bMidOut = false ;
// analizzo i sottotratti omogenei
ICRVCOMPOPOVECTOR vpCrvs ;
PtrOwner<ICurveComposite> pCrvCompoClone( CloneCurveComposite( pCrvCompo)) ;
if ( IsNull( pCrvCompoClone) || ! pCrvCompoClone->IsValid())
return false ;
GetHomogeneousParts( pCrvCompoClone, PockParams, vpCrvs) ;
// se ho un solo tratto omogeneo e coerente al tipo scelto ...
if ( int( vpCrvs.size()) == 1 && vpCrvs[0]->GetTempProp( 0) == nType) {
// ... e non ho un punto di riferimento valido
if ( ! ptRef.IsValid()) {
// il punto iniziale è già definito
pCrvCompoClone->GetStartPoint( ptStart) ;
}
// ho un punto di riferimento valido
else {
// cerco il punto più vicino
DistPointCurve DistPtCrv( ptRef, *pCrvCompoClone) ;
int nFlag ;
if ( ! DistPtCrv.GetMinDistPoint( 0, ptStart, nFlag))
return false ;
}
bMidOut = ( nType == TEMP_PROP_OPEN_EDGE) ;
return true ;
}
// scorro i tratti omogenei e cerco il tratto del tipo definito più lungo
int nMaxInd = -1 ;
double dMaxLen = 0. ;
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
// considero solo i tratti aperti
if ( vpCrvs[i]->GetTempProp( 0) == nType) {
// calcolo la lunghezza del tratto aperto
double dLen ;
vpCrvs[i]->GetLength( dLen) ;
if ( dLen > dMaxLen) {
dMaxLen = dLen ;
nMaxInd = i ;
}
}
}
// recupero il tratto del tipo definito più lungo
if ( nMaxInd != -1) {
// recupero il parametro alla sua metà
double dParIn ;
vpCrvs[nMaxInd]->GetParamAtLength( 0.5 * dMaxLen, dParIn) ;
// se tratto OPEN
if ( nType == TEMP_PROP_OPEN_EDGE) {
bMidOut = true ;
vpCrvs[nMaxInd]->GetPointD1D2( dParIn, ICurve::FROM_MINUS, ptStart, &vtMidOut) ;
vtMidOut.Normalize() ;
vtMidOut.Rotate( Z_AX, 0, -1) ;
}
// se tratto CLOSED
else
vpCrvs[nMaxInd]->GetPointD1D2( dParIn, ICurve::FROM_MINUS, ptStart) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPtStartOnGenericEdge( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
Point3d& ptStart)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
pCrvCompo->GetStartPoint( ptStart) ;
// scorro tutte le curve
double dLenRef = 0. ;
bool bAllShort = true ; // flag per curve tutte piccole
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
// ricavo la curva i-esima dalla curva originale
const ICurve* pMyCrv = pCrvCompo->GetCurve( i) ;
if ( pMyCrv == nullptr || ! pMyCrv->IsValid())
return false ;
// ricavo la lunghezza di tale curva
double dLen = 0. ;
pMyCrv->GetLength( dLen) ;
// se lunghezza accettabile o maggiore della massima trovata, ricavo ptStart
if ( dLen > dLenRef) {
if ( pMyCrv->GetMidPoint( ptStart)) {
dLenRef = dLen ;
bAllShort = false ;
}
}
}
// se curve tutte piccole
if ( bAllShort) {
bool bMidOutFake ;
Vector3d vtMidOutFake ;
if ( ! GetPtStartOnHomogeousEdges( pCrvCompo, PockParams, TEMP_PROP_CLOSE_EDGE, P_INVALID,
bMidOutFake, ptStart, vtMidOutFake))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPtStartOnGenericEdgeByPtRef( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
const Point3d& ptRef, Point3d& ptStart)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 ||
! ptRef.IsValid())
return false ;
// ordino le curve di pCrvCompo in base alla vicinanza da ptRef
// <indice della curva, distanza da ptRef>
vector<pair<int, double>> vCrvInfoDist ; vCrvInfoDist.resize( pCrvCompo->GetCurveCount()) ;
// scorro le curve di pCrvCompo
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
vCrvInfoDist[i].first = i ;
// calcolo la distanza tra la curva i-esima e ptRef
double dDist = INFINITO ;
DistPointCurve DistPtCrv( ptRef, *pCrvCompo->GetCurve( i)) ;
DistPtCrv.GetSqDist( dDist) ;
vCrvInfoDist[i].second = dDist ;
}
// ordino il vettore in base alla distanza
sort( vCrvInfoDist.begin(), vCrvInfoDist.end(), [] ( const pair<int, double>& a, const pair<int, double>& b) {
return a.second < b.second ;
}) ;
// cerco un parametro iniziale valido per le sottocurve ordinate
double dLenRef = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ;
for ( int i = 0 ; i < int( vCrvInfoDist.size()) ; ++ i) {
// recupero la curva i-esima
const ICurve* pMyCrv = pCrvCompo->GetCurve( vCrvInfoDist[i].first) ;
if ( pMyCrv != nullptr && pMyCrv->IsValid()) {
// controllo che sia abbastanza lunga e aggiorno ptStart
double dLen = 0. ;
pMyCrv->GetLength( dLen) ;
if ( dLen > dLenRef) {
if ( pMyCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart))
return true ;
}
}
}
// alla peggio...
bool bMidOutFake ;
Vector3d vtMidOutFake ;
return GetPtStartOnHomogeousEdges( pCrvCompo, PockParams, TEMP_PROP_CLOSE_EDGE, ptRef,
bMidOutFake, ptStart, vtMidOutFake) ;
}
//----------------------------------------------------------------------------
static bool
AssignOpenCloseTmpPropToFirstOffsCurve( ICurveComposite* pCrv, const PocketParams& PockParams,
const ISurfFlatRegion* pSfrToWork, bool& bSomeOpen)
{
// controllo dei parametri
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0 ||
pSfrToWork == nullptr || ! pSfrToWork->IsValid() || pSfrToWork->GetChunkCount() != 1)
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 = 0 ; pCrv->GetCurveTempProp( i, nProp0, 0) ;
// nProp1 -> #loop che contiene la curva espressa in nProp0
int nProp1 = 0 ; pCrv->GetCurveTempProp( i, nProp1, 1) ;
// se questa curva non è un "raccordo" di Offset
if ( nProp0 > 0) {
// aggiorno la proprietà della curva da cui devo entrare
int nTempProp = TEMP_PROP_CLOSE_EDGE ;
pSfrToWork->GetCurveTempProp( 0, nProp1, nProp0 - 1, nTempProp, 0) ;
pCrv->SetCurveTempProp( i, nTempProp, 0) ;
// se la curva è aperta, aggiorno il Flag
if ( nTempProp == TEMP_PROP_OPEN_EDGE && ! bSomeOpen)
bSomeOpen = true ;
// salvo l'indice della prima curva trovata che non è un "raccordo" di Offset
if ( nInd == -1)
nInd = i ;
}
// se questa curva è un "raccordo" di Offset
else {
pCrv->SetCurveTempProp( i, TEMP_PROP_SMOOTH, 0) ;
pCrv->SetCurveTempProp( i, TEMP_PROP_SMOOTH, 1) ;
}
}
// cambio il punto iniziale all'inizio della prima curva che non deriva da un "raccordo"
Point3d ptStart ;
if ( nInd != -1) {
if ( ! pCrv->GetStartPoint( ptStart))
return false ;
pCrv->ChangeStartPoint( nInd) ; // la prima curva non è un "raccordo" di Offset
}
// scorro tutte le curve successive alla prima
for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++ i) {
int nTmpProp0, nTmpProp1 ;
// se "raccordo" copio la tempProp precedente
if (( pCrv->GetCurveTempProp( i, nTmpProp0, 0) && nTmpProp0 == TEMP_PROP_SMOOTH) &&
( pCrv->GetCurveTempProp( i, nTmpProp1, 1) && nTmpProp1 == TEMP_PROP_SMOOTH))
// copio la temp prop della curva precedente
pCrv->SetCurveTempProp( i, pCrv->GetCurve( i-1)->GetTempProp( 0), 0) ;
}
// lascio invariato il punto iniziale della curva originaria
if ( nInd != -1) {
double dUNewS ;
pCrv->GetParamAtPoint( ptStart, dUNewS) ;
pCrv->ChangeStartPoint( dUNewS) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
SetPtStartForPath( ICurveComposite* pCrvOffs, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut,
const ICurveComposite* pCrvOrig)
{
/*
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 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.
*/
// controllo dei parametri
if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0 ||
pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1)
return false ;
// assegno proprietà di OPEN/CLOSE alle sottocurve dell'offset corrente
bool bSomeOpen = false ;
if ( ! AssignOpenCloseTmpPropToFirstOffsCurve( pCrvOffs, PockParams, pSrfToWork, bSomeOpen))
return false ;
// punto valido per l'entrata ...
bMidOut = false ;
if ( bSomeOpen) {
// se esistono lati aperti...
if ( pCrvOrig != nullptr && pCrvOrig->IsValid()) {
// ... e non ho un punto di riferimento, lo cerco sulla curva originale
if ( ! ptEndPrec.IsValid()) {
if ( ! GetPtStartOnOpenEdgeByOrigCurve( pCrvOrig, PockParams, pCrvOffs, ptStart, vtMidOut, bMidOut))
return false ;
}
// ... e ho un punto di riferimento
else {
if ( ! GetPtStartOnOpenEdgeByPtRef( ptEndPrec, PockParams, pCrvOffs, pCrvOrig, ptStart, vtMidOut, bMidOut))
return false ;
}
}
// se punto non trovato precedentemente, tento con una sottocurva OPEN generica
if ( ! bMidOut) {
if ( ! GetParamOnOpenCurve( pCrvOffs, PockParams, bMidOut, ptStart, vtMidOut))
return false ;
}
// se queste curve sono troppo corte, analizzo le parti uniformi
if ( ! bMidOut) {
if ( ! GetPtStartOnHomogeousEdges( pCrvOffs, PockParams, TEMP_PROP_OPEN_EDGE, ptEndPrec,
bMidOut, ptStart, vtMidOut))
return false ;
}
}
// se sottocurve tutte CLOSED o un punto iniziale non valido, lo cerco su un lato generico
if ( ! bMidOut) {
if ( ! ptEndPrec.IsValid()) {
if ( ! GetPtStartOnGenericEdge( pCrvOffs, PockParams, ptStart))
return false ;
}
else {
if ( ! GetPtStartOnGenericEdgeByPtRef( pCrvOffs, PockParams, ptEndPrec, ptStart))
return false ;
}
}
// imposto il punto iniziale scelto
double dPar = 0. ;
if ( ! pCrvOffs->GetParamAtPoint( ptStart, dPar) || ! pCrvOffs->ChangeStartPoint( dPar))
pCrvOffs->ChangeStartPoint( .5) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
SetAdvancedPtStartForPath( ICRVCOMPOPOVECTOR& vCrvOffsAct, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut,
bool& bMidOut, int& nIndex, 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, i < ( int( vCrvOrigChunkLoops.size())) ? 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)
{
// raggio della circonferenza esterna
if ( dOutRad < 10 * EPS_SMALL)
return false ;
// imposto versore estrusione sulle curve composite
pMCrv->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) ;
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,
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'
}
// imposto la proprietà di raccord
vCrvArcs[i]->SetTempProp( TEMP_PROP_SMOOTH, 1) ;
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
int nProp0 = pCrv->GetTempProp( 0) ;
int nProp1 = pCrv->GetTempProp( 1) ;
double dParam0 = pCrv->GetTempParam( 0) ;
double dParam1 = pCrv->GetTempParam( 1) ;
pCrv->Clear() ;
pCrv->AddCurve( Release( pCrvCO_temp)) ;
pCrv->SetTempProp( nProp0, 0) ;
pCrv->SetTempProp( nProp1, 1) ;
pCrv->SetTempParam( dParam0, 0) ;
pCrv->SetTempParam( dParam1, 1) ;
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& vFirstOffset,
const PocketParams& PockParams, double dLenPercS, double dLenPercE,
ICurveComposite* pCrvLink)
{
// 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 il biArco tra il punto iniziale e finale dei due offset consecutivi
se richiesto lo smusso -> accorcio entrambi i tratti di Offset e, riuscendo, si sotituisce
il biArco calcolato prima con quest'ultimo
*/
int nMaxIter = 2 ;
if ( ! PockParams.bSmooth)
nMaxIter = 1 ;
// taglio la curva al massimo nMaxIter-volte e mi fermo al taglio più opportuno
int nIter = 0 ;
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 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( CloneCurveComposite( pCrvS)) ;
PtrOwner<ICurveComposite> pCrvE_clone( CloneCurveComposite( pCrvE)) ;
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
GetUnclearedRegionAndSetFeed( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks,
const ICurveComposite* pCrv_orig, const PocketParams& PockParams, ISurfFlatRegion* pSrfToCut)
{
// controllo dei parametri
if ( vFirstOffs.empty() || int( vOffs.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 ----
// Creo la regione svuotata dal Tool negli Offset, nei Links e sia dagli Offsets che dai Links
PtrOwner<ISurfFlatRegion> pSfrTool( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrTool))
return false ;
// creo un vettore che conterrà solamente i Link percorsi fino ad Ora
bool bFollowOrder = ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_CONFORMAL_ZIGZAG) ;
for ( int i = ( bFollowOrder ? 0 : int( vOffs.size()) - 1) ;
( bFollowOrder ? i < int( vOffs.size()) : i >= 0 ) ;
( bFollowOrder ? ++ i : -- i)) {
// ================== OFFSET =============================
// recupero le proprietà temporanee
int nProp0 = vOffs[i]->GetTempProp( 0) ;
int nProp1 = vOffs[i]->GetTempProp( 1) ;
// Feed
bool bFirstOffs = false ;
Point3d ptStart ; vOffs[i]->GetStartPoint( ptStart) ;
for ( int j = 0 ; j < int( vFirstOffs.size()) && ! bFirstOffs ; ++ j)
bFirstOffs = vFirstOffs[j]->IsPointOn( ptStart, 100 * EPS_SMALL) ;
// calcolo gli intervalli di Feed per la curva di Offset
if ( ! AssignFeedSpiral( vOffs[i], pSfrTool, false, bFirstOffs, pCrv_orig, PockParams, 2 * PockParams.dRad / 3))
return false ;
// aggiorno la superificie svuotata
PtrOwner<ISurfFlatRegion> pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( vOffs[i]->Clone() , PockParams.dRad + 5 * EPS_SMALL, false, false)) ;
if ( ! IsNull( pSrfToolRegOffi)) {
if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0)
pSfrTool.Set( pSrfToolRegOffi) ;
else
pSfrTool->Add( *pSrfToolRegOffi) ;
}
vOffs[i]->SetTempProp( nProp0, 0) ;
vOffs[i]->SetTempProp( nProp1, 1) ;
// ================= LINK ================================
int nLink_Ind = ( bFollowOrder ? i + 1 : i) ;
// ( Il primo link è nullo, inatti non vi è nessun raccordo per raggiungere il primo Offset)
if ( nLink_Ind < int( vLinks.size()) && ! IsNull( vLinks[nLink_Ind]) &&
vLinks[nLink_Ind]->IsValid()) {
// recupero le proprietà temporanee
int nProp0 = vLinks[nLink_Ind]->GetTempProp( 0) ;
int nProp1 = vLinks[nLink_Ind]->GetTempProp( 1) ;
// Feed
PtrOwner<ICurveComposite> pCrvLink( ConvertCurveToComposite( vLinks[nLink_Ind]->Clone())) ;
// calcolo gli intervalli di Feed per la curva di Link
if ( IsNull( pCrvLink) || ! pCrvLink->IsValid() ||
! AssignFeedSpiral( pCrvLink, pSfrTool, true, false, pCrv_orig, PockParams, 2 * PockParams.dRad / 3))
return false ;
// aggiorno la regione svuotata
PtrOwner<ISurfFlatRegion> pSrfToolRegLinki( GetSurfFlatRegionFromFatCurve( pCrvLink->Clone(), PockParams.dRad + 5 * EPS_SMALL, false, false)) ;
if ( ! IsNull( pSrfToolRegLinki)) {
if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0)
pSfrTool.Set( pSrfToolRegLinki) ;
else
pSfrTool->Add( *pSrfToolRegLinki) ;
}
// risetto il Link come curva composita ...
pCrvLink->SetTempProp( nProp0, 0) ;
pCrvLink->SetTempProp( nProp1, 1) ;
vLinks[nLink_Ind].Set( pCrvLink) ;
}
}
// 4) Creo la regione contenente tutte le parti non svuotate
pSrfToCut->CopyFrom( pSrfExtern) ;
if ( ! pSrfToCut->Subtract( *pSfrTool))
return false ;
return true ;
}
//----------------------------------------------------------------------------
static bool
RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs,
ICRVCOMPOPOVECTOR& vOffsFirstCurve, const PocketParams& PockParams)
{
// se non ho nessuna superficie valida da svuotare, allora esco
if ( pSfrUncleared == nullptr)
return false ;
if ( ! pSfrUncleared->IsValid() || pSfrUncleared->GetChunkCount() == 0)
return true ;
// scorro tutti i Chunk della regione
ICRVCOMPOPOVECTOR vCrvCurl ;
for ( int nC = 0 ; nC < pSfrUncleared->GetChunkCount() ; ++ nC) {
// recupero il centroide del Chunk da rimuovere
Point3d ptCentroid ; pSfrUncleared->GetChunkCentroid( nC, ptCentroid) ;
/*
Tra tutte le curve di Offset escludo le curve che :
- Sono di primo Offset
- Non contengono il centroide ( condizione già verificata per quelle di primo Offset)
... e cerco la più vicina tra le rimanenti
*/
int nInd = -1 ;
double dRefDist = INFINITO - 1 ;
Point3d ptCloser ; // serve nel caso non bastasse la curva a ricciolo per svuotare il chunk nC-esimo
int nFlag ;
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
// se la curva è di primo Offset, allora la salto
Point3d ptCheck ; vOffs[i]->GetStartPoint( ptCheck) ;
bool bSkip = false ;
for ( int j = 0 ; j < int( vOffsFirstCurve.size()) && ! bSkip ; ++ j) {
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_SPIRALOUT)
bSkip = ( vOffsFirstCurve[j]->IsPointOn( ptCheck, 100 * EPS_SMALL)) ;
else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG)
bSkip = vOffs[i]->GetTempProp( 0) == 0 ;
}
if ( bSkip)
continue ;
// se la curva contiene il centroide, allora la salto
int nSide ;
DistPointCurve DistPtCrv( ptCentroid, *vOffs[i]) ;
if ( DistPtCrv.GetSideAtMinDistPoint( EPS_SMALL, Z_AX, nSide)) {
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_SPIRALOUT) {
if ( nSide == ( ! PockParams.bInvert ? MDS_LEFT : MDS_RIGHT))
continue ;
}
else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) {
if ( nSide != vOffs[i]->GetTempProp( 1))
continue ;
}
}
// controllo se la distanza è ammissibile
double dCurrDist = INFINITO ;
if ( DistPtCrv.GetSqDist( dCurrDist) && dCurrDist < dRefDist) {
dRefDist = dCurrDist ;
nInd = i ;
DistPtCrv.GetMinDistPoint( EPS_SMALL, ptCloser, nFlag) ;
}
}
// se non ho trovato alcun indice, salto il Chunk nC-esimo
if ( nInd == -1)
continue ;
// cerco sulla curva di Offset nInd-esima la sottocurva dove cade il punto a minima distanza
double dURef = 0 ;
if ( ! vOffs[nInd]->GetParamAtPoint( ptCloser, dURef))
continue ;
int nCrv = int( floor( dURef)) ;
const ICurve* pCrv = vOffs[nInd]->GetCurve( nCrv) ;
if ( pCrv == nullptr || ! pCrv->IsValid())
continue ;
// determino se la curva è di raccordo o meno
bool bIsSmoothCrv = ( PockParams.bSmooth && pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ;
// clono la curva di Offset corrente ( in questo modo non modifico l'originale)
PtrOwner<ICurveComposite> pMyOffs( CloneCurveComposite( vOffs[nInd])) ;
if ( IsNull( pMyOffs) || ! pMyOffs->IsValid())
return false ;
// versore uscente ed entrante dall'Offset attuale per creazione della curva a ricciolo
Vector3d vt1, vt2, vtMain ;
// punto di uscita e di ingresso dall'Offset attuale per creazione della curva a ricciolo
Point3d pt1, pt2, ptMain ;
// paramtro di uscita e di ingresso dall'Offset attuale per creazione della curva a ricciolo
double dURef1, dURef2 ;
// inizializzo la curva a ricciolo
PtrOwner<ICurveComposite> pCrvCurl( CreateCurveComposite()) ;
if ( IsNull( pCrvCurl))
return false ;
// se è di raccordo, "ricostruisco lo spigolo vivo della curva originale di Offset"
if ( bIsSmoothCrv) {
// cerco la prima curva in precedenza e in successione non di raccordo
vt1 = V_INVALID ;
for ( int i = nCrv - 1 ; i >= 0 ; -- i) {
const ICurve* pCrvPrec = pMyOffs->GetCurve( i) ;
if ( pCrvPrec == nullptr || ! pCrvPrec->IsValid())
break ;
if ( pCrvPrec->GetTempProp( 1) != TEMP_PROP_SMOOTH) {
// definisco il versore uscente
pCrvPrec->GetEndDir( vt1) ;
// definisco il punto di uscita
pCrvPrec->GetEndPoint( pt1) ;
// definisco il parametro di uscita
dURef1 = i + 1 ;
break ;
}
}
if ( ! vt1.IsValid())
continue ;
vt2 = V_INVALID ;
for ( int i = nCrv + 1 ; i < pMyOffs->GetCurveCount() ; ++ i) {
const ICurve* pCrvSucc = pMyOffs->GetCurve( i) ;
if ( pCrvSucc == nullptr || ! pCrvSucc->IsValid())
break ;
if ( pCrvSucc->GetTempProp( 1) != TEMP_PROP_SMOOTH) {
// definisco il versore entrante
pCrvSucc->GetStartDir( vt2) ;
// definisco il punto di entrata
pCrvSucc->GetStartPoint( pt2) ;
// definisco il parametro di entrata
dURef2 = i ;
break ;
}
}
if ( ! vt2.IsValid())
continue ;
// trovo il punto di intersezione tra le due direzioni tangenti applicate
const double EXTENSION_LEN = 1000. ;
// segmento iniziale -------
PtrOwner<ICurveLine> pLineS( CreateCurveLine()) ;
if ( IsNull( pLineS) || ! pLineS->Set( pt1, pt1 + vt1 * EXTENSION_LEN))
continue ;
// segmento finale --------
PtrOwner<ICurveLine> pLineE( CreateCurveLine()) ;
if ( IsNull( pLineE) || ! pLineE->Set( pt2, pt2 - vt2 * EXTENSION_LEN))
continue ;
// intersezione
IntersCurveCurve ILL( *pLineS, *pLineE) ;
if ( ILL.GetCrossIntersCount() == 1) {
IntCrvCrvInfo aInfo ;
if ( ILL.GetIntCrvCrvInfo( 0, aInfo)) {
if ( ! pCrvCurl->AddPoint( pt1) ||
! pCrvCurl->AddLine( aInfo.IciA[0].ptI))
continue ;
ptMain = aInfo.IciA[0].ptI ;
}
}
else
continue ;
}
// se invece non è di raccordo, allora ho già lo spigolo vivo
else {
// definisco il versore entrante ed uscente
// definisco il punto di entrata e di uscita ( sono coincidenti)
// definisco il parametro di entrata e di uscita ( sono coincidenti)
dURef1 = dURef ;
dURef2 = dURef ;
if ( ! vOffs[nInd]->GetPointD1D2( dURef1, ICurve::FROM_MINUS, ptMain, &vt1) ||
! vOffs[nInd]->GetPointD1D2( dURef2, ICurve::FROM_PLUS, ptMain, &vt2))
continue ;
vt1.Normalize() ; vt2.Normalize() ;
}
// se i due vettori sono tra loro paralleli...
bool bSameDir = AreSameVectorEpsilon( vt1, vt2, 2 * EPS_SMALL) ;
if ( bSameDir) {
vt1 = ( ptCentroid + ( PockParams.dSideStep / 4 * vt1)) - ptCloser ;
vt2 = ( ptCentroid - ( PockParams.dSideStep / 4 * vt2)) - ptCloser ;
vt2.Invert() ;
}
// calcolo la direzione tra pt1 e ptCentroid
Vector3d vtDir = ( ptCentroid - ptMain) ; vtDir.Normalize() ;
vtDir.Rotate( Z_AX, ANG_RIGHT) ;
double dAngMain ; bool bDet ;
vt1.GetRotation( vt2, Z_AX, dAngMain, bDet) ;
if ( dAngMain > - EPS_ZERO)
vtDir.Invert() ;
// calcolo i due Biarchi di raccordo
PtrOwner<ICurveComposite> pCrvBiArc1( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pCrvBiArc2( CreateCurveComposite()) ;
if ( IsNull( pCrvBiArc1) || IsNull( pCrvBiArc2) ||
! CalcBoundedSmoothedLink( ptMain, vt1, ptCentroid, vtDir, 0.5, vOffsFirstCurve, PockParams, pCrvBiArc1) ||
! CalcBoundedSmoothedLink( ptCentroid, vtDir, ptMain, vt2, 0.5, vOffsFirstCurve, PockParams, pCrvBiArc2))
continue ;
// aggiorno la curva a ricciolo
if ( ! pCrvCurl->AddCurve( Release( pCrvBiArc1)) ||
! pCrvCurl->AddCurve( Release( pCrvBiArc2)) ||
! pCrvCurl->IsValid())
continue ;
// controllo se devo raccordarmi con Offset
if ( bIsSmoothCrv)
pCrvCurl->AddLine( pt2) ;
// controllo se tale curva effettivamente rimuove il Chunk nC-esimo
PtrOwner<ISurfFlatRegion> pSfrFatCurl( GetSurfFlatRegionFromFatCurve( pCrvCurl->Clone(), PockParams.dRad, false, false)) ;
if ( IsNull( pSfrFatCurl) || ! pSfrFatCurl->IsValid())
continue ;
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfrUncleared->CloneChunk( nC)) ;
if ( IsNull( pSfrFatCurl) || ! pSfrFatCurl->IsValid())
return false ;
pSfrChunk->Subtract( *pSfrFatCurl) ;
// se la curva a ricciolo non svuota il Chunk nC-esimo -> percorro il bordo di tale Chunk
if ( pSfrChunk->IsValid() || pSfrChunk->GetChunkCount() > 0) {
// so che il punto più vicino alla curva è ptCloser
// ricavo dunque il punto più vicino sul bordo del Chunk nC-esimo
PtrOwner<ICurveComposite> pCompoChunkBorder( ConvertCurveToComposite( pSfrUncleared->GetLoop( nC, 0))) ;
if ( IsNull( pCompoChunkBorder) || ! pCompoChunkBorder->IsValid())
return false ;
if ( PockParams.bInvert)
pCompoChunkBorder->Invert() ;
DistPointCurve distPtChunkBorder( ptCloser, *pCompoChunkBorder) ;
Point3d ptCloserOnChunk ;
if ( ! distPtChunkBorder.GetMinDistPoint( EPS_SMALL, ptCloserOnChunk, nFlag))
continue ;
// cambio il punto di inizio della curva su tale punto
double dUS = 0. ;
pCompoChunkBorder->GetParamAtPoint( ptCloserOnChunk, dUS) ;
pCompoChunkBorder->ChangeStartPoint( dUS) ;
/*
Creo due curve composite rappresentanti un piccolo tratto tangente sulla curva di
Offset attuale; questi servono per la partenza e l'arrivo dei biArchi
*/
PtrOwner<ICurveComposite> pCompoHelpS( CreateCurveComposite()) ;
if ( IsNull( pCompoHelpS) || ! pCompoHelpS->AddPoint( pt1 - vt1 / 4) ||
! vt1.Normalize() || ! pCompoHelpS->AddLine( pt1))
continue ;
PtrOwner<ICurveComposite> pCompoHelpE( CreateCurveComposite()) ;
if ( IsNull( pCompoHelpE) || ! pCompoHelpE->AddPoint( pt2) ||
! vt2.Normalize() || ! pCompoHelpE->AddLine( pt2 + vt2 / 4))
continue ;
/*
creo il Biarco iniziale
( nel caso non si riuscisse diventa un segmento lineare)
*/
PtrOwner<ICurveComposite> pCrvBiArc1( CreateCurveComposite()) ;
if ( IsNull( pCrvBiArc1))
return false ;
if ( ! CutCurveToConnect( pCompoHelpS, pCompoChunkBorder, vOffsFirstCurve, PockParams,
0, 10 * EPS_SMALL, pCrvBiArc1)) {
if ( ! CalcBoundedLink( ptCloser, ptCloserOnChunk, vOffsFirstCurve, pCrvBiArc1))
continue ;
}
// aggiorno i nuovi punti e i nuovi parametri
double dMyU ;
Point3d ptMyPoint ;
if ( ! pCrvBiArc1->GetEndPoint( ptMyPoint) ||
! pCompoChunkBorder->GetParamAtPoint( ptMyPoint, dMyU))
continue ;
// accorgio il bordo dell'Offset in raccordo al BiArco iniziale
if ( dMyU > EPS_SMALL)
pCompoChunkBorder->TrimStartAtParam( dMyU) ;
/*
creo il Biarco finale
( nel caso non si riuscisse diventa un segmento lineare)
*/
// creo il Biarco iniziale ( nel caso non si riuscisse diventa un segmento lineare)
PtrOwner<ICurveComposite> pCrvBiArc2( CreateCurveComposite()) ;
if ( IsNull( pCrvBiArc2))
return false ;
if ( ! CutCurveToConnect( pCompoChunkBorder, pCompoHelpE, vOffsFirstCurve, PockParams,
0., 0., pCrvBiArc2)) {
if ( ! CalcBoundedLink( ptCloserOnChunk, ptCloser, vOffsFirstCurve, pCrvBiArc2))
continue ;
}
// aggiorno i nuovi punti e i nuovi parametri
if ( ! pCrvBiArc2->GetStartPoint( ptMyPoint) ||
! pCompoChunkBorder->GetParamAtPoint( ptMyPoint, dMyU))
continue ;
// la curva a ricciolo ora diventa l'unione di pCrvBiArc1 - pCompoChunkBorder - pCrvBiArc2
pCrvCurl->Clear() ;
// smusso la curva di bordo del Chunk ( è aperta)
double dSmoothPar = PockParams.dRad / 8. ;
ModifyCurveToSmoothed( pCompoChunkBorder, PockParams, dSmoothPar, dSmoothPar, false) ;
if ( ! pCrvCurl->AddCurve( Release( pCrvBiArc1)) ||
! pCrvCurl->AddCurve( Release( pCompoChunkBorder)) ||
! pCrvCurl->AddCurve( Release( pCrvBiArc2)))
continue ;
}
// attribuisco alla curva a ricciolo la Feed minima
AssignMinFeed( pCrvCurl, PockParams) ;
// aggiorno il nuovo Offset aggiungendo la curva a ricciolo
PtrOwner<ICurveComposite> pCrvTest( CreateCurveComposite()) ;
if ( IsNull( pCrvTest))
return false ;
// aggiungo tratto iniziale di Offset
pCrvTest->AddCurve( ConvertCurveToBasicComposite( pMyOffs->CopyParamRange( 0, dURef1))) ;
// aggiungo la curva a ricciolo
if ( ! pCrvTest->AddCurve( Release( pCrvCurl)))
continue ;
// aggiungo il tratto finale di Offset
pCrvTest->AddCurve( ConvertCurveToBasicComposite( pMyOffs->CopyParamRange( dURef2, pMyOffs->GetCurveCount()))) ;
// nel caso di direzioni parallele manca uno smusso
if ( bSameDir) {
double dSmoothPar = PockParams.dRad / 8. ;
ModifyCurveToSmoothed( pCrvTest, PockParams, dSmoothPar, dSmoothPar, false) ;
}
// provo a controllare che i punti inzili e finali coincidano, in caso aggiorno il nuovo Offset
Point3d ptStart_Old ; vOffs[nInd]->GetStartPoint( ptStart_Old) ;
Point3d ptEnd_Old ; vOffs[nInd]->GetEndPoint( ptEnd_Old) ;
Point3d ptStart_New ; pCrvTest->GetStartPoint( ptStart_New) ;
Point3d ptEnd_New ; pCrvTest->GetEndPoint( ptEnd_New) ;
if ( AreSamePointApprox( ptStart_Old, ptStart_New) && AreSamePointApprox( ptEnd_Old, ptEnd_New)) {
pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 0), 0) ;
pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 1), 1) ;
vOffs[nInd].Set( pCrvTest) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, const PocketParams& PockParams,
const ICRVCOMPOPOVECTOR& vOffsFirstCurve)
{
// controllo dei parametri
if ( vOffs.empty() || vOffsFirstCurve.empty())
return false ;
vLinks.clear() ;
vLinks.resize( int( vOffs.size())) ;
vector<IGeoObj*> VT ;
// 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], vOffsFirstCurve, PockParams,
( i == 0 ? 0. : 10 * EPS_SMALL), 10 * EPS_SMALL, pCrvLink) ||
! 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( CloneCurveComposite( vOffs[i])) ;
if ( dUNewS > EPS_SMALL) {
pCrvNewOffs.Set( ConvertCurveToComposite( vOffs[i]->CopyParamRange( 0, dUNewS))) ;
// sostituisco la curva i esima con quella tagliata
vOffs[i].Set( pCrvNewOffs) ;
}
// aggiorno il collegamento
vLinks[i+1].Set( pCrvLink) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs,
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 suo Offset massimo non genera alcuna regione
double dMaxOffs ;
if ( ! CalcCurveLimitOffset( *pCrvOffs, dMaxOffs))
return false ;
// se questa curva sparisce -> non serve inserirla come offset, non svuota parti aggiuntive
if ( dMaxOffs < PockParams.dRad - dOffs + 5 * EPS_SMALL) {
bInsert = false ;
return true ;
}
}
else
return true ;
// se non richiesto percorso avanzato
if ( ! PockParams.bOptOffsetsAdv)
return true ;
OffsetCurve OffsCrv ; // considero anche i casi in cui ho bSmallRad
if ( ! OffsCrv.Make( pCrvOffs, - PockParams.dRad + dOffs - 5 * EPS_SMALL , ICurve::OFF_FILLET))
return false ;
// controllo se richista ottimizzazione per Offsets mediante centroidi e medial Axis
if ( 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
ExtendGuideByIteration( ICurveComposite* pCompoTempGuide, const ICurveComposite* pCompoPerimeter,
bool bInverted)
{
// controllo dei parametri
if ( pCompoTempGuide == nullptr || ! pCompoTempGuide->IsValid() ||
pCompoPerimeter == nullptr || ! pCompoPerimeter->IsValid())
return false ;
const double EXTENSION_VAL = 1000. ;
// ricavo il punto finale della guida
Point3d ptEndGuide ; pCompoTempGuide->GetEndPoint( ptEndGuide) ;
// direzione finale della guida
Vector3d vtEndGuide ; pCompoTempGuide->GetEndDir( vtEndGuide) ;
// creo un segmento diretto come il chiuso
PtrOwner<ICurveLine> pSeg( CreateCurveLine()) ;
if ( IsNull( pSeg) ||
! pSeg->Set( ptEndGuide, ptEndGuide + EXTENSION_VAL * vtEndGuide))
return false ;
// calcolo l'intersezione tra questo segmento e la curva di perimetro aperta
IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ;
// se l'unica intersezione è il punto iniziale del segmento, allora non estendo la guida
// ( vale anche nel caso di intersezione con Overlap)
if ( ICC.GetIntersCount() == 1) {
IntCrvCrvInfo aInfo ;
if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) {
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide))
return true ;
}
}
// passo alla PolyLine del perimetro
PolyLine PL ;
pCompoPerimeter->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, PL) ;
// cerco il punto più esterno rispetto alla direzione del chiuso
Point3d ptPoly ;
PL.GetFirstPoint( ptPoly) ;
while ( PL.GetNextPoint( ptPoly)) {
if ( ! pSeg->Set( ptEndGuide, ptEndGuide + ( ptPoly - ptEndGuide) * EXTENSION_VAL))
return false ;
// calcolo l'intersezione tra questo segmento e la curva di perimetro aperta
IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ;
// se una intersezione, allora di Overlap con punto iniziale e finale definito
if ( ICC.GetIntersCount() == 1) {
IntCrvCrvInfo aInfo ;
if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) {
if ( aInfo.bOverlap) {
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide) &&
AreSamePointApprox( aInfo.IciB[1].ptI, ptPoly))
return ( pCompoTempGuide->AddLine( ptPoly)) ;
}
else {
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide))
return ( pCompoTempGuide->AddLine( ptPoly)) ;
}
}
}
// se più di una intersezione, allora il perimetro deve essere tutto Interno al segmento
// ( Out nel caso in cui ho le curve invertite)
IntCrvCrvInfo aInfo ;
bool bOk = true ;
for ( int i = 0 ; i < ICC.GetIntersCount() && bOk ; ++ i) {
bOk = ( ICC.GetIntCrvCrvInfo( i, aInfo)) ;
bOk = ( aInfo.bOverlap ||
( aInfo.IciB[0].nPrevTy != ( ! bInverted ? CRVC_OUT : CRVC_IN) &&
aInfo.IciB[0].nNextTy != ( ! bInverted ? CRVC_OUT : CRVC_IN))) ;
}
if ( bOk)
return ( pCompoTempGuide->AddLine( ptPoly)) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
IsCompoMadeBy2DifferentHomogeneousParts( const ICurveComposite* pCompo, const PocketParams& PockParams,
bool& bOk, ICRVCOMPOPOVECTOR& vpCrvs)
{
// controllo dei parametri
if ( pCompo == nullptr || ! pCompo->IsValid())
return false ;
bOk = false ;
vpCrvs.clear() ;
// clono la curva composita ( cambierà il suo punto iniziale)
PtrOwner<ICurveComposite> pCompoCL( CloneCurveComposite( pCompo)) ;
if ( IsNull( pCompoCL) || ! pCompoCL->IsValid())
return false ;
// recupero i tratti con proprietà uniformi
GetHomogeneousParts( pCompoCL, 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) ;
}
}
// controlloo che la curva abbia esattamente due tratti omogenei
bOk = ( int( vpCrvs.size()) == 2) ;
if ( ! bOk)
vpCrvs.clear() ;
return true ;
}
//----------------------------------------------------------------------------
static bool
CalcCrvGuideConformalZigZag( const ISurfFlatRegion* pSfrChunk, PocketParams& PockParams, ICurveComposite* pCompoGuide,
ICurveComposite* pCompoClass, ICurveComposite* pCompoClass_UR)
{
/*
NB. La curva di classificazione pCompoClass è una curva chiusa che deriva dall'estensione
presso i lati aperti con un tool di ragggio a max( dRad / 8, PockParams.dSideStep / 2).
NB. La curva di classificazione pCompoClass_UR è la curva mediante la quale i Biarchi di
raccordo ( curve a racciolo non devono uscire) ; in questo caso l'estensione della
curva è paragonabile a quella di un tool con raggio
max( dRad / 8, PockParams.dSideStep / 2) + PockParams.Rad
in questo modo il controOffset della regione definisce correttamente la curva per la classificazione
*/
// controllo dei parametri
if ( pCompoGuide == nullptr || pCompoClass == nullptr || pCompoClass_UR == nullptr ||
pSfrChunk == nullptr || ! pSfrChunk->IsValid())
return false ;
pCompoGuide->Clear() ;
pCompoClass->Clear() ;
pCompoClass_UR->Clear() ;
// la superficie deve avere un Chunk e non avere isole
if ( pSfrChunk->GetChunkCount() > 1 || pSfrChunk->GetLoopCount( 0) > 1)
return true ;
// ricavo la curva di bordo ( la curva che definisce il perimetro)
PtrOwner<ICurveComposite> pCompoPerimeter( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
if ( IsNull( pCompoPerimeter) || ! pCompoPerimeter->IsValid())
return false ;
// controllo che sia formata da esattamente due tratti omogenei
bool b2HP = false ;
ICRVCOMPOPOVECTOR vpCrvs ;
if ( ! IsCompoMadeBy2DifferentHomogeneousParts( pCompoPerimeter, PockParams, b2HP, vpCrvs))
return false ;
if ( ! b2HP)
return true ;
// allargo curva di Bordo secondo le specifiche di una lavorazione a SpiralIn
double dRad = PockParams.dRad ;
int nOffsType = PockParams.nOffsType ;
PockParams.dRad = max( dRad / 8, PockParams.dSideStep / 2) ;
PockParams.nOffsType = ICurve::OFF_CHAMFER ;
ICRVCOMPOPOVECTOR vCrvEmpty ;
if ( ! AdjustContourWithOpenEdges( pCompoPerimeter, vCrvEmpty, PockParams))
return false ;
PockParams.dRad = dRad ;
PockParams.nOffsType = nOffsType ;
// reucupero ora i due tratti uniformi ( della curva allargata)
if ( ! IsCompoMadeBy2DifferentHomogeneousParts( pCompoPerimeter, PockParams, b2HP, vpCrvs) ||
! b2HP)
return false ;
// la guida inizialmente è data dal tratto chiuso
// il perimetro coincide con il tratto aperto
PtrOwner<ICurveComposite> pCompoTempGuide( Release( vpCrvs[0])) ;
if ( IsNull( pCompoTempGuide) || ! pCompoTempGuide->IsValid())
return false ;
// estendo la curva nel suo tratto finale ed iniziale
for ( int i = 0 ; i < 2 ; ++ i) {
if ( ! ExtendGuideByIteration( pCompoTempGuide, vpCrvs[1], ( i == 1)))
return false ;
// se la guida diventa diventa chiusa, allora esco
if ( ! pCompoTempGuide->IsValid() || pCompoTempGuide->IsClosed())
return true ;
pCompoTempGuide->Invert() ;
vpCrvs[1]->Invert() ;
}
// tratto iniziale
Vector3d vtStart ; pCompoTempGuide->GetStartDir( vtStart) ;
Point3d ptStart ; pCompoTempGuide->GetStartPoint( ptStart) ;
PtrOwner<ICurveLine> pLineStart( CreateCurveLine()) ;
if ( IsNull( pLineStart) || ! pLineStart->Set( ptStart - 1000. * vtStart, ptStart))
return false ;
// tratto finale ( potrebbe intersecare il tratto iniziale)
Vector3d vtEnd ; pCompoTempGuide->GetEndDir( vtEnd) ;
Point3d ptEnd ; pCompoTempGuide->GetEndPoint( ptEnd) ;
PtrOwner<ICurveLine> pLineEnd( CreateCurveLine()) ;
if ( IsNull( pLineEnd) || ! pLineEnd->Set( ptEnd, ptEnd + 1000. * vtEnd))
return false ;
IntersCurveCurve ICC( *pLineStart, *pLineEnd) ;
if ( ICC.GetIntersCount() > 0) {
IntCrvCrvInfo aInfo ;
if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) {
pLineStart->TrimStartAtParam( aInfo.IciA[0].dU) ;
pLineEnd->TrimEndAtParam( aInfo.IciB[0].dU) ;
}
}
// le due estensioni vanno tagliate presso i bordi dei lati aperti estesi
IntersCurveCurve ICCLS( *pLineStart, *vpCrvs[1]) ;
if ( ICCLS.GetIntersCount() > 0) {
IntCrvCrvInfo aInfo ;
for ( int i = 0 ; i < ICCLS.GetCrossIntersCount() ; ++ i) {
if ( ICCLS.GetIntCrvCrvInfo( i, aInfo)) {
if ( ! AreSamePointApprox( aInfo.IciA[ aInfo.bOverlap ? 1 : 0].ptI, ptStart))
pLineStart->ModifyStart( aInfo.IciA[aInfo.bOverlap ? 1 : 0].ptI) ;
}
}
}
IntersCurveCurve ICCLE( *pLineEnd, *vpCrvs[1]) ;
if ( ICCLE.GetIntersCount() > 0) {
IntCrvCrvInfo aInfo ;
for ( int i = 0 ; i < ICCLE.GetCrossIntersCount() ; ++ i) {
if ( ICCLE.GetIntCrvCrvInfo( 0, aInfo)) {
if ( ! AreSamePointApprox( aInfo.IciA[0].ptI, ptEnd))
pLineEnd->ModifyEnd( aInfo.IciA[0].ptI) ;
}
}
}
pCompoTempGuide->AddCurve( Release( pLineStart), false) ;
pCompoTempGuide->AddCurve( Release( pLineEnd), true) ;
// recupero la curva guida
pCompoGuide->CopyFrom( pCompoTempGuide) ;
// recupero la curva di classificazione ( abballendola)
PolyLine PL ;
pCompoPerimeter->ApproxWithLines( 10 * LIN_TOL_MIN, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) ;
PL.RemoveAlignedPoints( 500 * EPS_SMALL) ;
pCompoPerimeter->Clear() ;
pCompoPerimeter->FromPolyLine( PL) ;
pCompoPerimeter->RemoveSmallDefects( 10 * LIN_TOL_MIN, ANG_TOL_STD_DEG, true) ;
pCompoPerimeter->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG) ;
pCompoClass->CopyFrom( pCompoPerimeter) ;
ResetCurveTempProps( pCompoClass) ;
// creo la curva di classificazione per le regioni non svuotate
dRad = PockParams.dRad ;
PockParams.dRad = max( dRad / 8, PockParams.dSideStep / 2) + dRad ;
PtrOwner<ICurveComposite> pCompoClassUnclearedRegion( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
if ( IsNull( pCompoClassUnclearedRegion) || ! pCompoClassUnclearedRegion->IsValid() ||
! AdjustContourWithOpenEdges( pCompoClassUnclearedRegion, vCrvEmpty, PockParams))
return false ;
PockParams.dRad = dRad ;
if ( pCompoClassUnclearedRegion->IsValid()) {
OffsetCurve OffsCrv ;
if ( ! OffsCrv.Make( pCompoClassUnclearedRegion, - PockParams.dRad - PockParams.dRadialOffset, ICurve::OFF_FILLET))
return false ;
PtrOwner<ICurveComposite> pMyCrv( ConvertCurveToComposite( OffsCrv.GetLongerCurve())) ;
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
return false ;
pCompoClass_UR->CopyFrom( pMyCrv) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AdapthLinkToOffsBySmooth( ICurveComposite* pCrvLink, ICurveComposite* pCrvOffsPrec,
ICurveComposite* pCrvOffsSucc, const PocketParams& PockParams)
{
// controllo dei parametri
if ( pCrvLink == nullptr || pCrvOffsPrec == nullptr || pCrvOffsSucc == nullptr ||
! pCrvLink->IsValid() || ! pCrvOffsPrec->IsValid() || ! pCrvOffsSucc->IsValid())
return false ;
// clono la curva di Link ( in modo da non rovinare l'originale)
PtrOwner<ICurveComposite> pCrvMyLink( CloneCurveComposite( pCrvLink)) ;
if ( IsNull( pCrvMyLink) || ! pCrvMyLink->IsValid())
return false ;
// aggiungo la curva finale dell'Offset precedente e la curva iniziale dell'Offset successivo
// al tratto di Link
PtrOwner<ICurve> pCrvPrec( pCrvOffsPrec->GetLastCurve()->Clone()) ;
PtrOwner<ICurve> pCrvSucc( pCrvOffsSucc->GetFirstCurve()->Clone()) ;
if ( IsNull( pCrvPrec) || IsNull( pCrvSucc) ||
! pCrvPrec->IsValid() || ! pCrvSucc->IsValid())
return false ;
// assegno proprietà di invalidità delle curve
pCrvPrec->SetTempProp( TEMP_PROP_INVALID, 1) ;
pCrvSucc->SetTempProp( TEMP_PROP_INVALID, 1) ;
// aggiungo
if ( ! pCrvMyLink->AddCurve( Release( pCrvPrec), false) ||
! pCrvMyLink->AddCurve( Release( pCrvSucc), true))
return false ;
// estendo la curva, in modo da non perdere i tratti estremi
const double EXT_LEN = 1000. ;
if ( ! pCrvMyLink->AddLineTg( EXT_LEN, false) ||
! pCrvMyLink->AddLineTg( EXT_LEN, true))
return false ;
// smusso il Link
double dSmoothParam = PockParams.dRad / 8. ;
ModifyCurveToSmoothed( pCrvMyLink, PockParams, dSmoothParam, dSmoothParam, false) ;
// rimuovo la prima e l'ultima curva ( e quella invalida)
delete( pCrvMyLink->RemoveFirstOrLastCurve( false)) ; // tratto lineare
delete( pCrvMyLink->RemoveFirstOrLastCurve( true)) ; // tratto lineare
const ICurve* pCrvFirst = pCrvMyLink->GetFirstCurve() ;
if ( pCrvFirst != nullptr && pCrvFirst->GetTempProp( 1) == TEMP_PROP_INVALID) // sottocurva di Offs
delete( pCrvMyLink->RemoveFirstOrLastCurve( false)) ;
const ICurve* pCrvLast = pCrvMyLink->GetLastCurve() ;
if ( pCrvLast != nullptr && pCrvLast->GetTempProp( 1) == TEMP_PROP_INVALID) // sottocurva di Offs
delete( pCrvMyLink->RemoveFirstOrLastCurve( true)) ;
// recupero punto iniziale e finale del Link
Point3d ptS ; pCrvMyLink->GetStartPoint( ptS) ;
Point3d ptE ; pCrvMyLink->GetEndPoint( ptE) ;
// recupero i paramtri sulle curve di Offset
double dUTrim ;
if ( ! pCrvOffsPrec->GetParamAtPoint( ptS, dUTrim, 500 * EPS_SMALL) ||
! pCrvOffsPrec->TrimEndAtParam( dUTrim))
return false ;
if ( ! pCrvOffsSucc->GetParamAtPoint( ptE, dUTrim, 500 * EPS_SMALL) ||
! pCrvOffsSucc->TrimStartAtParam( dUTrim))
return false ;
pCrvLink->CopyFrom( pCrvMyLink) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
AddConformalZigZag( const ISurfFlatRegion* pSfr, PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
/*
Calcolo ottimizzato di svuotatura mediante Offset progressivi di una guida, raccordati
rispetto al bordo della superficie di Pocketing mediante logica ZigZag
*/
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid())
return false ;
vCrvCompoRes.clear() ;
// ciclo sui chunk della regione
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
// recupero il Chunk nC-esimo
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( nC)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
return false ;
// recupero il massimo Offset del Chunk nC-esimo
double dMaxOffs ;
pSfrChunk->GetMaxOffset( dMaxOffs) ;
// se inferiore del Tool ( ho già svuotato tale Chunk in precedenza)
if ( dMaxOffs < PockParams.dRad + 200 * EPS_SMALL)
continue ;
// ricavo la guida ideale e la curva su cui tagliare gli Offset a partire dal Chunk nC-esimo
PtrOwner<ICurveComposite> pCrvGuideExt( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pCrvClass( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pCrvClassUR( CreateCurveComposite()) ;
if ( IsNull( pCrvGuideExt) || IsNull( pCrvClass) ||
! CalcCrvGuideConformalZigZag( pSfr, PockParams, pCrvGuideExt, pCrvClass, pCrvClassUR))
return false ;
// se non è stata trovata una guida ideale, allora passo al Chunk successivo
if ( ! pCrvGuideExt->IsValid() || pCrvGuideExt->GetCurveCount() == 0 ||
! pCrvClass->IsValid() || pCrvClass->GetCurveCount() == 0 ||
! pCrvClassUR->IsValid() || pCrvClassUR->GetCurveCount() == 0) {
return true ;
}
// memorizzo le curve di Offset tagliate a seconda dalla geometria del perimetro
ICRVCOMPOPOVECTOR vOffs ;
const int MAX_ITER = 100 ;
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
for ( int i = 0 ; i < MAX_ITER ; ++ i) {
OffsetCurve OffsCrv ;
if ( ! OffsCrv.Make( pCrvGuideExt, - dOffs, ICurve::OFF_FILLET))
return false ;
// se invece ne ottengo più di una, controllo se è necessaria
if ( OffsCrv.GetCurveCount() > 1) {
// recupero le curve extra ( tutte tranne la più lunga)
for ( int j = 0 ; j < OffsCrv.GetCurveCount() - 1 ; ++ j) {
// recupero la curva più corta
PtrOwner<ICurveComposite> pCrvShorter( ConvertCurveToComposite( OffsCrv.GetShorterCurve())) ;
if ( ! IsNull( pCrvShorter) && pCrvShorter->IsValid()) {
// controllo se è davvero necessaria per non lasciare materiale
bool bSkip = false ;
if ( pCrvShorter->IsClosed()) {
CheckIfOffsetIsNecessary( pCrvShorter, ( i == 0 ? dOffs : PockParams.dSideStep),
i, PockParams, bSkip) ;
if ( bSkip)
return true ;
}
}
}
}
// se non ottengo nessuna curva, allora ho finito
PtrOwner<ICurve> pCrvOffs( OffsCrv.GetLongerCurve()) ;
if ( IsNull( pCrvOffs) || ! pCrvOffs->IsValid())
break ;
// se ottengo una curva chiusa, lavorazione non ammessa
if ( pCrvOffs->IsClosed())
return true ;
// la inverto se numero di Offset progressivo dispari
if ( ( ! IsEven( i) && ! PockParams.bInvert) ||
( IsEven( i) && PockParams.bInvert))
pCrvOffs->Invert() ;
// classifico la curva rispetto al perimetro, devo tenere solo le parti interne
CRVCVECTOR ccClass ;
IntersCurveCurve ICC( *pCrvOffs, *pCrvClass) ;
if ( ! ICC.GetCurveClassification( 0, EPS_SMALL, ccClass))
return true ;
// se curva tutta esterna ho finito
if ( int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT)
break ;
// scorro tutte le classificazioni ottenute
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
// tengo quelle strettamente interne
if ( ccClass[j].nClass == CRVC_IN) {
// recupero la porzione di Offset
PtrOwner<ICurve> pCrvOffs_Int( pCrvOffs->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
if ( ! IsNull( pCrvOffs_Int) && pCrvOffs_Int->IsValid()) {
// salvo come prima proprietà temporanea, il numero di Offset progressivo
pCrvOffs_Int->SetTempProp( i, 0) ;
// salvo come seconda proprietà temporanea il lato esterno
pCrvOffs_Int->SetTempProp( ( ! IsEven( i) && ! PockParams.bInvert) ||
( IsEven( i) && PockParams.bInvert) ?
MDS_LEFT : MDS_RIGHT, 1) ;
// salvo la curva
vOffs.emplace_back( ConvertCurveToComposite( Release( pCrvOffs_Int))) ;
}
}
}
dOffs += PockParams.dSideStep ;
}
// se non ho ottenuto Offset, passo al Chunk successivo
if ( vOffs.empty())
return true ;
// inverto il vettore degli Offset, in modo da partire dal più esterno fino ad arrivare al
// più interno
reverse( vOffs.begin(), vOffs.end()) ;
// calcolo la lunghezza del semiperimetro della curva di classificazione
// ( serve per decidere il tratto di curva di raccordo)
double dHalfLenPerimeter ;
pCrvClass->GetLength( dHalfLenPerimeter) ;
dHalfLenPerimeter /= 2. ;
// creo il vettore dei Link e definisco la tolleranza di Join tra Offset e Link
ICURVEPOVECTOR vLinks( int( vOffs.size())) ;
Point3d ptSplit ; // punto dal quale smussare tutti i percorsi
for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) {
// recupero il parametro iniziale del raccordo sulla curva di perimetro
Point3d ptS ; vOffs[i]->GetEndPoint( ptS) ;
double dUS ; pCrvClass->GetParamAtPoint( ptS, dUS) ;
if ( vOffs[i]->GetTempProp( 0) == 0)
ptSplit = ptS ;
// recupero il parametro finale del raccordo sulla curva di perimetro
Point3d ptE ; vOffs[i+1]->GetStartPoint( ptE) ;
double dUE ; pCrvClass->GetParamAtPoint( ptE, dUE) ;
// recupero il tratto di curva di interesse
PtrOwner<ICurveComposite> pCrvJoint( ConvertCurveToComposite( pCrvClass->CopyParamRange( dUS, dUE))) ;
if ( ! IsNull( pCrvJoint) && pCrvJoint->IsValid()) {
double dLen ;
pCrvJoint->GetLength( dLen) ;
if ( dLen > dHalfLenPerimeter) {
pCrvJoint.Set( ConvertCurveToComposite( pCrvClass->CopyParamRange( dUE, dUS))) ;
pCrvJoint->Invert() ;
}
// solo se non siamo in presenza di curve di primo Offset, aggiungo al Link l'ultima
// curva dell'Offset precedente e la prima curva dell'Offset successivo
if ( vOffs[i]->GetTempProp( 0) > 0 || vOffs[i+1]->GetTempProp( 0) > 0) {
if ( ! AdapthLinkToOffsBySmooth( pCrvJoint, vOffs[i], vOffs[i+1], PockParams))
return false ;
}
vLinks[i+1].Set( Release( pCrvJoint)) ;
}
}
// smusso tutte le curve di Offset ( tranne quelle generate alla prima iterazione)
double dSmoothParam = PockParams.dRad / 8. ;
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
if ( vOffs[i]->GetTempProp( 0) > 0)
ModifyCurveToSmoothed( vOffs[i], PockParams, dSmoothParam, dSmoothParam, false) ;
}
// determino la regione piana dove sono contenute le zone non svuotate ( se esistono)
PtrOwner<ISurfFlatRegion> pSfrFirstOffs( CloneSurfFlatRegion( pSfrChunk)) ;
if ( IsNull( pSfrFirstOffs) || ! pSfrFirstOffs->IsValid() ||
! pSfrFirstOffs->Offset( - PockParams.dRad - PockParams.dRadialOffset, ICurve::OFF_FILLET))
return false ;
ICRVCOMPOPOVECTOR vCrvFirstOffs ;
for ( int nC_ = 0 ; nC_ < pSfrFirstOffs->GetChunkCount() ; ++ nC_)
vCrvFirstOffs.emplace_back( ConvertCurveToComposite( pSfrFirstOffs->GetLoop( nC_, 0))) ;
// inizializzo la regione contenente le parti non svuotate
PtrOwner<ISurfFlatRegion> pSfrUncleared( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrUncleared))
return false ;
// calcolo la Feed e la regione contenente le parti non svuotate
if ( GetUnclearedRegionAndSetFeed( vCrvFirstOffs, vOffs, vLinks, pCrvClass, PockParams, pSfrUncleared)) {
// Modifico i percorsi
vCrvFirstOffs.clear() ;
vCrvFirstOffs.emplace_back( Release( pCrvClassUR)) ;
if ( ! RemoveExtraParts( pSfrUncleared, vOffs, vCrvFirstOffs, PockParams))
return false ;
}
// creo il percorso di svuotatura
PtrOwner<ICurveComposite> pCompoTempPath( CreateCurveComposite()) ;
if ( IsNull( pCompoTempPath))
return false ;
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
if ( ! IsNull( vLinks[i]) && vLinks[i]->IsValid()) {
if ( ! pCompoTempPath->AddCurve( Release( vLinks[i])))
return false ;
}
if ( ! IsNull( vOffs[i]) && vOffs[i]->IsValid()) {
if ( ! pCompoTempPath->AddCurve( Release( vOffs[i])))
return false ;
}
}
// se la curva finale di Pocketing è valida, allora la resituisco
if ( pCompoTempPath->IsValid()) {
// semplifico le curve mediante uniformità delle Feeds
SimplifyCurveByFeeds( pCompoTempPath, PockParams, 10 * EPS_SMALL) ;
// aggiugno il tratto iniziale e finale per entrate da fuori
Vector3d vtStart ; pCompoTempPath->GetStartDir( vtStart) ;
vtStart.Invert() ;
bool bExt = false ;
if ( ! ExtendPath( pCompoTempPath, pSfr, PockParams, vtStart, false, bExt))
return false ;
Vector3d vtEnd ; pCompoTempPath->GetEndDir( vtEnd) ;
if ( ! ExtendPath( pCompoTempPath, pSfr, PockParams, vtEnd, true, bExt))
return false ;
// sostituisco la curva
if ( pCompoTempPath->IsValid())
vCrvCompoRes.emplace_back( Release( pCompoTempPath)) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CalcSpiral( const ISurfFlatRegion* pSrfPock, const PocketParams& PockParams, int& nReg, Point3d& ptStart,
ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops, ICurveComposite* pMCrv, bool& bOptimizedTrap,
bool& bMidOut, Vector3d& vtMidOut)
{
// inizializzo il percorso come vuoto
pMCrv->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) ;
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, 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( CloneSurfFlatRegion( pSrfToWork)) ; // regione attuale
if ( IsNull( pSrfAct) || pSrfAct->GetChunkCount() == 0)
return false ;
double dOffsPrec = 0. ;
while ( nIter < MAX_ITER) {
// ricavo la regione piana da VRONI
PtrOwner<ISurfFlatRegion> pSfrOffsVR( pSrfAct->CreateOffsetSurf( - dOffs, ICurve::OFF_FILLET)) ;
if ( IsNull( pSfrOffsVR))
return false ;
if ( ! pSfrOffsVR->IsValid()) {
pSfrOffsVR.Set( pSrfAct->CreateOffsetSurf( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) ;
if ( IsNull( pSfrOffsVR))
return false ;
}
// 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, 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.ptStart ;
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, vCrvOrigChunkLoops))
vOffsFirstCurve[nIndexSwap]->GetStartPoint( ptNewStart) ;
else
return false ;
}
else {
if ( SetPtStartForPath( vOffs[0], PockParams, pSrfToWork, ptRef, ptStart, vtMidOut, bMidOut,
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 e creo i collegamenti
/*
Se richiesto parametro di smusso, tutti gli Offset ( ad eccezione di quelli generati alla
prima iterazione vengono tagliati, quindi diventano aperti
*/
ICURVEPOVECTOR vLinks( vOffs.size()) ;
if ( ! CreateSpiralPocketingPath( vOffs, vLinks, PockParams, vOffsFirstCurve))
return false ;
// controllo eventuali parti non svuotate ( e setto la Feed degli Offset e dei Link)...
PtrOwner<ISurfFlatRegion> pSfrUncleared( CreateSurfFlatRegion()) ;
if ( GetUnclearedRegionAndSetFeed( vOffsFirstCurve, vOffs, vLinks, vCrvOrigChunkLoops[0], PockParams, pSfrUncleared)) {
// Modifico i percorsi
if ( ! RemoveExtraParts( pSfrUncleared, vOffs, vOffsFirstCurve, 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( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// ricavo il numero di Chunk da svuotare
if ( pSrfPock == nullptr)
return false ;
int nChunk = pSrfPock->GetChunkCount() ;
// scorro i Chunk
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
// indice del Chunk da corrente da svuotare
int nChunkInd = 0 ;
if ( PockParams.ptStart.IsValid()) {
// se il punto di riferimento è valido, il Chunk è quello ad esso più vicino
DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ;
int nMinLoop = 0 ;
double dMinPar = 0. ;
if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar))
return false ;
}
// Clono il Chunk attuale come regione FlatRegion
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
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()) ;
if ( IsNull( pMCrv))
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, pSfrChunk, PockParams, nReg, vCrvOrigChunkLoops))
return false ;
// calcolo il percorso di svuotatura
bool bSomeOpen ;
Vector3d vtMidOut ;
if ( ! CalcSpiral( pSfrChunk, PockParams, nRegTot, ptStart, vCrvOrigChunkLoops, pMCrv, bOptimizedTrap,
bSomeOpen, vtMidOut))
return false ;
// se terminate le regioni, esco
if ( pMCrv->GetCurveCount() == 0)
break ; // passo al chunk originale successivo
bool bIsExtended = false ;
// se caso a trapezio
if ( bOptimizedTrap) {
Vector3d vtRef ;
pMCrv->GetStartDir( vtRef) ;
vtRef.Invert() ;
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, false, bIsExtended))
return false ;
pMCrv->GetEndDir( vtRef) ;
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, true, bIsExtended))
return false ;
AssignFeedSpiralOpt( 1, PockParams, pMCrv) ;
pMCrv->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
}
// se esistenza di lati aperti validi
else if ( bSomeOpen)
ExtendPath( pMCrv, pSfrOrig, PockParams, vtMidOut, false, bIsExtended) ;
// inserisco le curve nel vettore
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
++ nReg ; // incremento il numero di regione progressiva
// aggiorno il punto di riferimento per il Chunk Successivo
vCrvCompoRes.back()->GetEndPoint( PockParams.ptStart) ;
}
// rimuovo il Chunk dalla regione
pSrfPock->EraseChunk( nChunkInd) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AddSpiralOut( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// ricavo il numero di Chunk da svuotare
if ( pSrfPock == nullptr)
return false ;
int nChunk = pSrfPock->GetChunkCount() ;
// ciclo sui chunk della superficie da svuotare
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
// indice del Chunk da corrente da svuotare
int nChunkInd = 0 ;
if ( PockParams.ptStart.IsValid()) {
// se il punto di riferimento è valido, il Chunk è quello ad esso più vicino
DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ;
int nMinLoop = 0 ;
double dMinPar = 0. ;
if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar))
return false ;
}
// Clono il Chunk attuale come regione FlatRegion
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
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()) ;
if ( IsNull( pMCrv))
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, pSfrChunk, PockParams, nReg, vCrvOrig))
return false ;
// calcolo il percorso di svuotatura
bool bSomeOpen ;
Vector3d vtMidOut ;
if ( ! CalcSpiral( pSfrChunk, PockParams, nRegTot, ptStart, vCrvOrig, pMCrv, 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
Vector3d vtRef ;
pMCrv->GetStartDir( vtRef) ;
vtRef.Invert() ;
bool bIsExtended = false ;
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, false, bIsExtended))
return false ;
pMCrv->GetEndDir( vtRef) ;
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, true, bIsExtended))
return false ;
AssignFeedSpiralOpt( 1, PockParams, pMCrv) ;
pMCrv->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
}
// inverto il percorso, perchè sono calcolati dall'esterno all'interno (solo nel caso non ottimizzato)
pMCrv->Invert() ;
// inserisco le curve nel vettore
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
// incremento il numero di regione progressiva
++ nReg ;
// aggiorno il punto di riferimento per il Chunk Successivo
vCrvCompoRes.back()->GetEndPoint( PockParams.ptStart) ;
}
// rimuovo il Chunk dalla regione
pSrfPock->EraseChunk( nChunkInd) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetZigZagOneWayBorderCrvs( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig,
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) ;
}
// le curve che ricavo devono essere sempre conenute nella regione che sto svuotando
/*
Se siamo in presenza di isole chiuse molto vicine a lati chiusi non posso estendere il bordo
dell'isola chiusa a piacere, devo limitarlo alla regione di svuotatura
*/
PtrOwner<ISurfFlatRegion> pSfrBucket( CloneSurfFlatRegion( pSfrPock)) ;
if ( IsNull( pSfrBucket) || ! pSfrBucket->IsValid() ||
! pSfrBucket->Offset( - PockParams.dRad - PockParams.dRadialOffset + 50 * EPS_SMALL, ICurve::OFF_FILLET))
return false ;
// vettore delle curve chiuse Offsettate
ICRVCOMPOPOVECTOR vClosedOffs ;
// scorro i Chunk e i Loop della regione ( se OneWay potrei avere più Chunks)
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) {
ICRVCOMPOPOVECTOR vClosedOffs_nC ;
// 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 omogenei
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 isola aperta, allora l'offset è verso il suo interno
if ( bOpenIsland)
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_nC.emplace_back( ConvertCurveToComposite( Release( pCrv))) ;
}
}
}
}
else
vClosedOffs_nC.emplace_back( ConvertCurveToComposite( Release( pOffLongestCrv))) ;
// passo alla successiva
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ;
}
}
}
}
// se non ho ricavato curve dal Chunk nC-esimo, passo al successivo
if ( vClosedOffs_nC.empty())
continue ;
// limito le curve ad essere contenute nella pSfrBucket
ICRVCOMPOPOVECTOR vClosedOffs1 ;
// scorro le curve di Offset chiuse ricavate
for ( int i = 0 ; i < int( vClosedOffs_nC.size()) ; ++ i) {
CRVCVECTOR ccClass ;
if ( pSfrBucket->GetCurveClassification( *vClosedOffs_nC[i], EPS_SMALL, ccClass)) {
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
if ( ccClass[j].nClass == CRVC_IN) {
// recupero il tratto di curva
PtrOwner<ICurve> pCrv( vClosedOffs_nC[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
if ( ! IsNull( pCrv) && pCrv->IsValid())
vClosedOffs1.emplace_back( ConvertCurveToComposite( Release( pCrv))) ;
}
}
}
}
if ( vClosedOffs1.empty())
continue ;
swap( vClosedOffs_nC, vClosedOffs1) ;
// concateno eventuali percorsi
if ( ! ChainCompoCurves( vClosedOffs_nC))
return false ;
// determino ora il punto di inizio, il tratto per il LeadIn
bool bIsChunkClosed = false ;
if ( ! IsChunkAllClosed( pSfrPock, nC, bIsChunkClosed))
return false ;
if ( ! bIsChunkClosed) {
if ( ! AdvanceExtendCurves( vClosedOffs_nC, pSfrOrig, PockParams))
return false ;
}
// aggiungo le curve ottenute al vettore
for ( int i = 0 ; i < int( vClosedOffs_nC.size()) ; ++ i)
vClosedOffs.emplace_back( Release( vClosedOffs_nC[i])) ;
}
}
// se non ho percorsi ho finito
if ( vClosedOffs.empty())
return true ;
// 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) ;
// assegno la proprità alla curva composita
vClosedOffs[i]->SetTempProp( TEMP_PROP_BORDER_CURVE, 0) ;
// restituisco le curve ottenute
vCrvRes.emplace_back( Release( vClosedOffs[i])) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AddZigZag( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// ricavo il numero di Chunk da svuotare
if ( pSrfPock == nullptr)
return false ;
int nChunk = pSrfPock->GetChunkCount() ;
// offset della regione per curva ZigZag
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
if ( PockParams.bAllowZigZagOneWayBorders)
dOffs += PockParams.dOffsExtra ;
// ciclo su tutti i chunks della superficie originale
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
// indice del Chunk da corrente da svuotare
int nChunkInd = 0 ;
if ( PockParams.ptStart.IsValid()) {
// se il punto di riferimento è valido, il Chunk è quello ad esso più vicino
DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ;
int nMinLoop = 0 ;
double dMinPar = 0. ;
if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar))
return false ;
}
// creo la regione per il percorso a ZigZag ( mediante primo Offset)
PtrOwner<ISurfFlatRegion> pSrfZigZag( pSrfPock->CloneChunk( nChunkInd)) ;
if ( ! pSrfZigZag->Offset( - dOffs, ICurve::OFF_FILLET))
return false ;
// vettore con i percorsi a ZigZag relativi al Chunk attuale
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 ;
// controllo esistenza di lati aperti per il Chunk attuale
bool bIsChunkClosed = true ;
if ( ! PockParams.bAllClosed) {
if ( ! IsChunkAllClosed( pSrfPock, nChunkInd, bIsChunkClosed))
return false ;
// se il Chunk originale aveva lati aperti, estendo il percorso a ZigZag
for ( int nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU) {
Vector3d vtRef ; vpCrvs[nU]->GetStartDir( vtRef) ;
vtRef.Invert() ;
bool bIsExtended = false ;
if ( ! ExtendPath( vpCrvs[nU], pSfrOrig, PockParams, vtRef, false,bIsExtended))
return false ;
vpCrvs[nU]->GetEndDir( vtRef) ;
if ( ! ExtendPath( vpCrvs[nU], pSfrOrig, PockParams, vtRef, true, bIsExtended))
return false ;
}
}
// inserisco le curve nel vettore risultante
for ( int nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU)
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( nChunkInd)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid() ||
! GetZigZagOneWayBorderCrvs( pSfrChunk, pSfrOrig, PockParams, vCrvCompoRes))
return false ;
}
// rimuovo il Chunk dalla regione
pSrfPock->EraseChunk( nChunkInd) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
OptimizeChunkOneWay( const ISurfFlatRegion* pSfrOrig, ISURFFRPOVECTOR& vSrfIdeal)
{
// controllo parametri
if ( pSfrOrig == nullptr || pSfrOrig->GetChunkCount() == 0)
return false ;
vSrfIdeal.clear() ;
/*
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( pSfrOrig->GetChunkCount(), 1) ;
for ( int nC = 0 ; nC < pSfrOrig->GetChunkCount() ; ++ nC) {
PtrOwner<ISurfFlatRegion> pSrfIdeal( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfIdeal))
return false ;
// se Chunk valido...
if ( vChunksAvailable[nC] == 1) {
// prendo la curva esterna
PtrOwner<ICurve> pCrvExt( pSfrOrig->GetLoop( nC, 0)) ;
if ( IsNull( pCrvExt))
return false ;
// inserisco il chunk nC-esimo ( curva per curva, così non perdo le temp prop)
pSrfIdeal->AddExtLoop( pCrvExt->Clone()) ;
for ( int nL = 1 ; nL < pSfrOrig->GetLoopCount( nC) ; ++ nL)
pSrfIdeal->AddIntLoop( pSfrOrig->GetLoop( nC, nL)) ;
// il chunk nC-esimo non è più disponibile...
vChunksAvailable[nC] = -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( pSfrOrig->GetLoop( i, 0)) ;
if ( IsNull( pCrvExtC))
return false ;
// classifico i bordi esterni ( se il bordo del chunk nC-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 (( int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) ||
( int( ccClass1.size()) == 1 && ccClass1[0].nClass == CRVC_IN)) {
// inserisco il chunk i-esimo ( curva per curva, così non perdo le temp prop)
pSrfIdeal->AddExtLoop( pSfrOrig->GetLoop( i, 0)) ;
for ( int nL = 1 ; nL < pSfrOrig->GetLoopCount( i) ; ++ nL)
pSrfIdeal->AddIntLoop( pSfrOrig->GetLoop( i, nL)) ;
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) ;
i = 0 ;
}
}
}
}
}
if ( pSrfIdeal->GetChunkCount() > 0)
vSrfIdeal.emplace_back( Release( pSrfIdeal)) ;
}
return ( ! vSrfIdeal.empty()) ;
}
//----------------------------------------------------------------------------
static bool
AddOneWay( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// offset della regione per segmenti a ZigZag
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
double dExtra = 0. ;
if ( PockParams.bAllowZigZagOneWayBorders)
dExtra = PockParams.dOffsExtra ;
// vettore delle regioni ideali
ISURFFRPOVECTOR vSrfFlat ;
if ( ! OptimizeChunkOneWay( pSrfPock, 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 ;
// se sono richieste le curve di Bordo, le aggiungo
if ( PockParams.bAllowZigZagOneWayBorders) {
if ( ! GetZigZagOneWayBorderCrvs( vSrfFlat[nIs], pSfrOrig, PockParams, vCrvCompoRes))
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 ( per le Feed)
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 in tratti uniformi di Feed)
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, bool bAllowZigZagOneWayBorders,
const Point3d& ptEndPrec, const 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 && nType != POCKET_CONFORMAL_ZIGZAG))
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 ;
myParams.bAllowZigZagOneWayBorders = bAllowZigZagOneWayBorders ;
if ( ptEndPrec.IsValid())
myParams.ptStart = ptEndPrec ;
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) ;
// porto il punto iniziale di riferimento ( se valido) in tale sistema
if ( myParams.ptStart.IsValid())
myParams.ptStart.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_Loc, myParams, vCrvCompoRes))
return false ;
break ;
case POCKET_CONFORMAL_ZIGZAG :
if ( ! AddConformalZigZag( pSfr_Loc, 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 ;
}