Files
EgtGeomKernel/CalcPocketing.cpp
T
Riccardo Elitropi ba7379e752 EgtGeomKernel :
- In CalcPocketing aggiunto flag per Conventional Milling.
2026-05-12 09:34:56 +02:00

10271 lines
431 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 "Voronoi.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/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 ; // tipo di lavorazione
int nLiType = LEAD_IN_NONE ; // tipo di ingresso (LeadIn)
int nLoType = LEAD_OUT_NONE ; // tipo di uscita (LeadOut)
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
double dSmooth = 5. ; // parametro di smusso per link (raccordo)
double dLiTang = INFINITO ; // valore tangente di LeadIn
double dLiElev = INFINITO ; // valore elevazione di LeadIn (usato anche per LeadOut)
double dLoTang = INFINITO ; // valore di LeadOut
bool bCalcUnclearedRegs = true ; // flag per calcolare o meno le regioni non svuotate
bool bOptOffsets = true ; // flag per evitare Offset non necessari
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 bConventionalMilling = true ; // flag per Conventional Milling Vs Climb Milling per svuotature a curva singola
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 della *pSfr da svuotare sono chiusi
bool bAllOpen = true ; // flag per indicare se tutti i bordi della *pSfr da svuotare sono aperti
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
// --------------------------------------------------------
bool bPolishing = false ; // flag per lucidatura
double dEpicyclesRad = 0. ; // raggio degli epicicli
double dEpicyclesDist = 0. ; // distanza degli epicicli
} ;
static double TOL_TRAPEZOID = 50 * EPS_SMALL ; // tolleranza per casi a trapezio SpiralPocket
static double TOL_REMOVE_OFFSET = 2. ; // tolleranza per controllo materiale lasciato da un Offset
typedef vector<ICRVCOMPOPOVECTOR> VICRVCOMPOPOVECTOR ;
//---------------------------------------------------------------------------
// definizione varibaile di debug
#define ENABLE_DEBUG 0
#if ENABLE_DEBUG
#include "EgtDev/Include/EGkGeoObjSave.h"
#include "EgtDev/Include/EGkGeoPoint3d.h"
#include "EgtDev/Include/EGkGeoVector3d.h"
// Varibili ausiliarie
vector<IGeoObj*> VT ;
vector<Color> VC ;
//---------------------------------------------------------------------------
inline Color GetRandomColor() {
return Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ;
}
//---------------------------------------------------------------------------
inline void DrawCurve( const ICurveComposite* pCrvCompo) {
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return ;
for ( int nU = 0 ; nU < pCrvCompo->GetCurveCount() ; ++ nU) {
int nProp0 ; pCrvCompo->GetCurveTempProp( nU, nProp0, 0) ;
VT.emplace_back( static_cast<IGeoObj*>( pCrvCompo->GetCurve( nU)->Clone())) ;
VC.emplace_back( nProp0 == 0 ? BLUE : RED) ;
}
return ;
}
//---------------------------------------------------------------------------
inline void DrawSfrAndLoops( const ISurfFlatRegion* pSfr, Color cCol = Color( 0., 255., 0., .5)) {
if ( pSfr == nullptr || ! pSfr->IsValid())
return ;
VT.emplace_back( static_cast<IGeoObj*>( pSfr->Clone())) ;
VC.emplace_back( cCol) ;
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
for ( int nL = 0 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) {
PtrOwner<ICurveComposite> pCrvCompo( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ;
DrawCurve( pCrvCompo->Clone()) ;
}
}
return ;
}
#endif
//---------------------------------------------------------------------------
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 nU = 0 ; nU < pCrv->GetCurveCount() ; ++ nU)
pCrv->SetCurveTempParam( nU, 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
*/
// dominio
dPar = Clamp( dPar, 0., 2. * PockParams.dRad) ;
// se la funzione si riduce ad un unico tratto costante
if ( 2. * PockParams.dRad - PockParams.dSideStep < 50. * EPS_SMALL) {
dFeed = GetMaxFeed( PockParams) ;
return true ;
}
// altrimenti controllo in quale tratto lineare cade il parametro
else {
if ( PockParams.dSideStep < dPar + 50. * EPS_SMALL) {
dFeed = GetFeed( PockParams) + ( GetFeed( PockParams) * ( 1. - ( PockParams.dSideStep / ( 2. * PockParams.dRad)))) *
( dPar - PockParams.dSideStep) / ( PockParams.dSideStep - ( 2. * PockParams.dRad)) ;
}
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
SwapSideBySecondTempParam( ICurveComposite* pCompo)
{
if ( pCompo == nullptr || ! pCompo->IsValid())
return false ;
pCompo->SetTempParam( int( pCompo->GetTempParam( 1)) == MDS_LEFT ? MDS_RIGHT : MDS_LEFT, 1) ;
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 ISurfFlatRegion* pSfrOpenEdges, const PocketParams& PockParams)
{
// NB. pSfrOpenEdges deve essere una regione che all'esterno contiene i lati considerati aperti
// Solo ai tratti esterni vengono assegnate le Feed
// se non rischiesto il calcolo della Feed, lascio quella standard
if ( ! PockParams.bCalcFeed)
return AssignMaxFeed( pCrv, PockParams) ;
// se non ho una regione di classificazione, non faccio nulla
if ( pSfrOpenEdges == nullptr || ! pSfrOpenEdges->IsValid())
return true ;
// classifico la curva in base alla regione
CRVCVECTOR ccClass ;
if ( pSfrOpenEdges->GetCurveClassification( *pCrv, EPS_SMALL, ccClass)) {
PtrOwner<ICurveComposite> pNewCrv( CreateCurveComposite()) ;
if ( IsNull( pNewCrv))
return false ;
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
// recupero il tratto di curva corrente
PtrOwner<ICurveComposite> pCrvRange( ConvertCurveToComposite( pCrv->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
if ( ! IsNull( pCrvRange) && pCrvRange->IsValid()) {
// se il tratto non è interno, calcolo la sua Feed
if ( ccClass[i].nClass != CRVC_IN) {
double dFeed = GetMinFeed( PockParams) ;
GetFeedForParam( 2. * PockParams.dRad - PockParams.dOpenEdgeRad, PockParams, dFeed) ;
AssignCustomFeed( pCrvRange, PockParams, dFeed) ;
}
// aggiungo il tratto alla sottocurva
if ( ! pNewCrv->AddCurve( Release( pCrvRange)))
return false ;
}
}
// assegno la curva calcolata
pNewCrv->SetTempProp( pCrv->GetTempProp( 0), 0) ;
pNewCrv->SetTempProp( pCrv->GetTempProp( 1), 1) ;
pNewCrv->SetTempParam( pCrv->GetTempParam( 0), 0) ;
pNewCrv->SetTempParam( pCrv->GetTempParam( 1), 1) ;
double dThick ; pCrv->GetThickness( dThick) ;
pNewCrv->SetThickness( dThick) ;
Vector3d vtExtr ; pCrv->GetExtrusion( vtExtr) ;
pNewCrv->SetExtrusion( vtExtr) ;
pCrv->CopyFrom( pNewCrv) ;
}
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, ANG_TOL_STD_DEG, false) ;
pCrv->GetLength( dLen) ;
for ( int nU = 0 ; nU < pCrv->GetCurveCount() ; ++ nU)
pCrv->SetCurveTempParam( nU, ( 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, ANG_TOL_STD_DEG, false) ;
double dLen ; pCrvLast->GetLength( dLen) ;
for ( int nU = 0 ; nU < pCrvLast->GetCurveCount() ; ++ nU)
pCrvLast->SetCurveTempParam( nU, ( 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, ANG_TOL_STD_DEG, 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, ANG_TOL_STD_DEG, false) ;
// recupero la lunghezza di tale curva e imposto la Feed
pCrvLast->GetLength( dLen) ;
for ( int nU = 0 ; nU < pCrvLast->GetCurveCount() ; ++ nU)
pCrvLast->SetCurveTempParam( nU, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
pCrvCompo->AddCurve( Release( pCrvLast)) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AssignFeedSpiral( ICurveComposite* pCompo, const ISurfFlatRegion* pSrfRemoved, bool bIsLink, bool bFirstOffs,
const ISurfFlatRegion* pSfrOpenEdges, const PocketParams& PockParams, double dTol)
{
// controllo la validità della curva
if ( pCompo == nullptr || ! pCompo->IsValid() || pCompo->GetCurveCount() == 0)
return false ;
// controllo se il Flag per assegnare la Feed è attivo
if ( ! PockParams.bCalcFeed)
return AssignMaxFeed( pCompo, PockParams) ;
// Se link di conformal ZigZag
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG && bIsLink)
return AssignMaxFeed( pCompo, PockParams) ;
// imposto di Default la Feed minima per ogni sottocurva
AssignMinFeed( pCompo, 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( pCompo, pSfrOpenEdges, 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( *pCompo, EPS_SMALL, ccClass))
return true ; // esco ( sempre con Feed Minima)
// creo la nuova curva con le Feed regolate
PtrOwner<ICurveComposite> pCompoNew( CreateCurveComposite()) ;
if ( IsNull( pCompoNew))
return false ;
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
// recupero il tratto di curva ricavato dalla classificazione
PtrOwner<ICurveComposite> pSubCompo( ConvertCurveToComposite( pCompo->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
if ( IsNull( pSubCompo))
continue ; // curva troppo piccola, passo alla successiva
// controllo se interno o fuori alla regione ( regolando quindi la Feed)
double dCurrFeed = ( ccClass[i].nClass == CRVC_IN ? GetMaxFeed( PockParams) :
GetMinFeed( PockParams)) ;
// assegno tale Feed ad ogni sottocurva ricavata dal tratto di classificazione
for ( int nU = 0 ; nU < pSubCompo->GetCurveCount() ; ++ nU)
pSubCompo->SetCurveTempParam( nU, dCurrFeed, 0) ;
// aggiungo la nuova curva con la Feed regolata a quella finale
pCompoNew->AddCurve( Release( pSubCompo)) ;
}
// sotituisco con quanto calcolato
pCompo->Clear() ;
pCompo->AddCurve( Release( pCompoNew)) ;
// cerco di uniformare i tratti ricavati
if ( ! bIsLink) { // ---------------- NEL CASO DI OFFSET ----------------
// creo un intervallo con tutte le Feed Minime
Intervals IntMinFeed ;
for ( int nU = 0 ; nU < pCompo->GetCurveCount() ; ++ nU) {
double dFeed ; pCompo->GetCurveTempParam( nU, dFeed, 0) ;
if ( abs( dFeed - GetMinFeed( PockParams)) < 5. * EPS_SMALL)
IntMinFeed.Add( nU, nU + 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( pCompo->CopyParamRange( dParS, dParE)) ;
double dLen = EPS_SMALL ; pCrv->GetLength( dLen) ;
if ( dLen > dTol + 5. * EPS_SMALL)
IntMinFeed_noSmall.Add( dParS, dParE) ;
bFound = IntMinFeed.GetNext( dParS, dParE) ;
}
for ( int nU = 0 ; nU < pCompo->GetCurveCount() ; ++ nU) {
if ( ! IntMinFeed_noSmall.IsInside( nU + 0.5))
pCompo->SetCurveTempParam( nU, GetMaxFeed( PockParams), 0) ;
}
}
else { // ---------------- NEL CASO DI LINK ----------------
// le curve con lunghezza inferiore alla tolleranza vanno modificate
for ( int j = 0 ; j < pCompo->GetCurveCount() ; ++ j) {
double dLen = EPS_SMALL ;
pCompo->GetCurve( j)->GetLength( dLen) ;
if ( dLen < dTol + 5. * EPS_SMALL) {
// se curva piccola, ricavo il tratto successivo lungo quanto il diametro
double dLenStart ;
pCompo->GetLengthAtParam( j, dLenStart) ;
double dLenEnd = dLenStart + dLen + 2 * PockParams.dRad ;
double dUE = pCompo->GetCurveCount() ;
pCompo->GetParamAtLength( dLenEnd, dUE) ;
bool bFound = false ;
for ( int k = j + 1 ; k < int( ceil( dUE)) ; ++ k) {
double dLenH = EPS_SMALL ;
pCompo->GetCurve( k)->GetLength( dLenH) ;
if ( dLenH < dTol + 5 * EPS_SMALL)
continue ;
// cerco tra le curve successive vicine se ne trovo una con Feed Minima
double dParam ;
pCompo->GetCurveTempParam( k, dParam, 0) ;
if ( abs( dParam - GetMinFeed( PockParams)) < 5. * EPS_SMALL) {
pCompo->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 ; pCompo->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
pCompo->SetCurveTempParam( j, dParam, 0) ;
}
else
pCompo->SetCurveTempParam( j, GetMinFeed( PockParams), 0) ;
}
}
}
if ( bFirstOffs)
AssignFeedForOpenEdge( pCompo, pSfrOpenEdges, PockParams) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
AssignFeedSpiralOpt( 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) ;
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_CONFORMAL_ZIGZAG ||
PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
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) ;
}
}
/* 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... */
else {
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) ;
}
}
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
IsCurveCompoHomogeneous( const ICurveComposite* pCrvCompo, bool& bAllClosed, bool& bAllOpen)
{
// controllo validità della curva
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
bAllClosed = true ;
bAllOpen = true ;
if ( pCrvCompo->GetCurveCount() == 0)
return true ;
// scorro le curve
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() && ( bAllOpen || bAllClosed) ; ++ i) {
// recupero la sottocurva i-esima
const ICurve* pCrv = pCrvCompo->GetCurve( i) ;
if ( pCrv == nullptr || ! pCrv->IsValid())
return false ;
int nTmpProp = pCrv->GetTempProp( 0) ;
bAllOpen = bAllOpen && ( nTmpProp == TEMP_PROP_OPEN_EDGE) ;
bAllClosed = bAllClosed && ( nTmpProp == TEMP_PROP_CLOSE_EDGE) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetSfrCrvCompoLoops( const ISurfFlatRegion* pSfr, ICRVCOMPOPOVECTOR& vCrvCompo)
{
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid())
return false ;
vCrvCompo.clear() ;
// scorro i Chunk della superficie
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
// scorro i loop del Chunk nC-esimo
for ( int nL = 0 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) {
// recupero il Loop come curva composita
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ;
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
return false ;
// inserisco la curva nel vettore
vCrvCompo.emplace_back( Release( pCrvLoop)) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
IsChunkAllHomogeneous( const ISurfFlatRegion* pSfr, int nChunk, bool& bAllClose, bool& bAllOpen)
{
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid() || nChunk < 0 || nChunk > pSfr->GetChunkCount())
return false ;
bAllClose = true ;
bAllOpen = 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 = ( bAllClose && pCrvLoop->GetCurveTempProp( nU, nCurrProp, 0) &&
nCurrProp == TEMP_PROP_CLOSE_EDGE) ;
bAllOpen = ( bAllOpen && pCrvLoop->GetCurveTempProp( nU, nCurrProp, 0) &&
nCurrProp == TEMP_PROP_OPEN_EDGE) ;
if ( ! bAllClose && ! bAllOpen)
return true ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
IsSfrAllHomogeneous( const ISurfFlatRegion* pSfr, bool& bAllClose, bool& bAllOpen)
{
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid())
return false ;
bAllClose = true ;
bAllOpen = true ;
// ciclo sui Chunk della regione
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
bool bChunkClose = true ;
bool bChunkOpen = true ;
if ( ! IsChunkAllHomogeneous( pSfr, nC, bChunkClose, bChunkOpen))
return false ;
bAllClose = ( bAllClose && bChunkClose) ;
bAllOpen = ( bAllOpen && bChunkOpen) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
IsSfrChunkMadeOnlyByClosedIslands( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParams,
bool& bOK, ICRVCOMPOPOVECTOR& vCrvIsl)
{
// controllo dei parametri
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
return false ;
vCrvIsl.clear() ;
bOK = false ;
// se non esistono isole
if ( pSfrChunk->GetLoopCount( 0) == 1)
return true ;
// controllo il loop esterno
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
return false ;
for ( int i = 0 ; i < pCrvExtLoop->GetCurveCount() ; ++ i) {
int nTmpProp = TEMP_PROP_INVALID ;
if ( pCrvExtLoop->GetCurveTempProp( i, nTmpProp, 0) && nTmpProp == TEMP_PROP_CLOSE_EDGE)
return true ;
}
// controllo le Isole
bool bOkIsl = true ;
for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) && bOkIsl ; ++ i) {
// recupero l'isola
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ;
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
return false ;
// controllo le sue TmpProps
for ( int i = 0 ; i < pCrvIsl->GetCurveCount() && bOkIsl ; ++ i) {
int nTmpProp = TEMP_PROP_INVALID ;
bOkIsl = ( pCrvIsl->GetCurveTempProp( i, nTmpProp, 0) && nTmpProp == TEMP_PROP_CLOSE_EDGE) ;
}
if ( bOkIsl)
vCrvIsl.emplace_back( Release( pCrvIsl)) ;
}
if ( ! bOkIsl)
vCrvIsl.clear() ;
else
bOK = true ;
return true ;
}
//----------------------------------------------------------------------------
static bool
GetIndOfHypoteticalChunk( const ICRVCOMPOPOVECTOR& vCrv, const ICurveComposite* pCrvA, const ICurveComposite* pCrvB,
INTVECTOR& vIndA, INTVECTOR& vIndB)
{
/*
date due curve interne ad una regione piana descritta da vCrv, individuare i chunk in cui
sono rispettivamente contenute
*/
// controllo dei parametri
for ( int i = 0 ; i < int( vCrv.size()) ; ++ i) {
if ( vCrv[i] == nullptr || ! vCrv[i]->IsValid())
return false ;
}
if ( pCrvA == nullptr || ! pCrvA->IsValid() || pCrvB == nullptr || ! pCrvB->IsValid())
return false ;
vIndA.clear() ;
vIndB.clear() ;
// ricavo le polyLines dalle curve
POLYLINEVECTOR vPL ;
for ( int i = 0 ; i < int( vCrv.size()) ; ++ i) {
vPL.emplace_back( PolyLine()) ;
vCrv[i]->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, vPL.back()) ;
}
// calcolo la matrice dei Chunks e dei Loops
Vector3d vtN ;
INTMATRIX vnPLIndMat ;
BOOLVECTOR vbInvert ;
if ( ! CalcRegionPolyLines( vPL, vtN, vnPLIndMat, vbInvert))
return false ;
// scorro i Chunks
for ( int nC = 0 ; nC < int( vnPLIndMat.size()) ; ++ nC) {
PtrOwner<ISurfFlatRegion> pSfrTmp( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrTmp))
return false ;
// scorro i Loops
for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL) {
if ( nL == 0) {
if ( ! pSfrTmp->AddExtLoop( CloneCurveComposite( vCrv[vnPLIndMat[nC][0]])))
return false ;
}
else {
if ( ! pSfrTmp->AddIntLoop( CloneCurveComposite( vCrv[vnPLIndMat[nC][nL]])))
return false ;
}
}
// piccolo Offset di correzione della superfice
pSfrTmp->Offset( 500 * EPS_SMALL, ICurve::OFF_FILLET) ;
// classifico le curve con il Chunk nC-esimo
CRVCVECTOR ccClass ;
if ( pSfrTmp->GetCurveClassification( *pCrvA, EPS_SMALL, ccClass) &&
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) {
for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL)
vIndA.emplace_back( vnPLIndMat[nC][nL]) ;
}
ccClass.clear() ;
if ( pSfrTmp->GetCurveClassification( *pCrvB, EPS_SMALL, ccClass) &&
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) {
for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL)
vIndB.emplace_back( vnPLIndMat[nC][nL]) ;
}
// se entrambi indici trovati, esco
if ( ! vIndA.empty() && ! vIndB.empty())
return true ;
}
return true ;
}
//----------------------------------------------------------------------------
static double
GetExtendPathLen( const PocketParams& PockParam)
{
return ( Clamp( 2. * PockParam.dRad - PockParam.dOpenEdgeRad, 0., PockParam.dRad) + PockParam.dOpenMinSafe) ;
}
//----------------------------------------------------------------------------
static bool
ExtendPath( ICurveComposite* pCompo, const ISurfFlatRegion* pSfr, const PocketParams& PockParams,
const Vector3d& vtFirstOut, bool bEndOrStart, double dExtension, 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 con versore tangente
Point3d pt ;
Vector3d vtTan ;
if ( bEndOrStart) {
if ( ! pCompo->GetEndPoint( pt) || ! pCompo->GetEndDir( vtTan))
return false ;
}
else {
if ( ! pCompo->GetStartPoint( pt) || ! pCompo->GetStartDir( vtTan))
return false ;
vtTan.Invert() ;
}
// angoli di rotazione ( potrebbero servirne altri)
const double ANG_45 = ANG_RIGHT / 2. ;
const array vAngles{ 0., ANG_RIGHT, 3 * ANG_RIGHT, ANG_45, 3 * ANG_45, 5 * ANG_45, 7 * ANG_45} ;
// ruoto il versore di uscita cercando un'entrata valida
double dMinDist = PockParams.dRad + PockParams.dRadialOffset ;
for ( int i = 0 ; i < ssize( vAngles) ; ++ i) {
// ruoto il versore d'uscita
Vector3d vtRotOut = GetRotate( vtFirstOut, Z_AX, - vAngles[i]) ;
// calcolo il punto di caduta dell'utensile
Point3d ptFall = pt + vtRotOut * dExtension ;
// controllo che sia sufficientemente distante dalla superficie limite
bool bInside = true ;
bool bOkOut = true ;
if ( PockParams.SfrLimit.IsValid()) {
bOkOut = ( IsPointInsideSurfFr( ptFall, &PockParams.SfrLimit, dMinDist - 10. * EPS_SMALL, bInside) && ! bInside) ;
if ( bOkOut)
bOkOut = ( IsPointInsideSurfFr( Media( ptFall, pt), &PockParams.SfrLimit, dMinDist - 10. * EPS_SMALL, 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 ;
}
}
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 < 1.25 * PockParams.dRad) {
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 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()) ;
while ( ! IsNull( pMyCrv) && pMyCrv->IsValid()) {
if ( ! pMyCrv->IsClosed()) {
pOffsInt.Set( pMyCrv) ;
break ;
}
else
pMyCrv.Set( OffsCrv.GetLongerCurve()) ;
}
}
// 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) ;
pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ;
pCrvExtLoopSurfInc->AddLine( pt1) ;
pCrvExtLoopSurfInc->AddLine( pt2) ;
pOffsInt->Invert() ;
pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ;
pCrvExtLoopSurfInc->AddLine( pt4) ;
pCrvExtLoopSurfInc->AddLine( pt5) ;
}
else if ( bExistExt) {
Point3d pt5 ; pOffsExt->GetStartPoint( pt5) ;
pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ;
pCrvExtLoopSurfInc->AddLine( pt1) ;
pCrvExtLoopSurfInc->AddLine( pt4) ;
pCrvExtLoopSurfInc->AddLine( pt5) ;
}
else if ( bExistInt) {
Point3d pt5 ; pOffsInt->GetStartPoint( pt5) ;
pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ;
pCrvExtLoopSurfInc->AddLine( pt1) ;
pCrvExtLoopSurfInc->AddLine( pt4) ;
pCrvExtLoopSurfInc->AddLine( pt5) ;
}
else
return false ;
// per sicurezza...
pCrvExtLoopSurfInc->Close() ;
// creo la regione ( la curva potrebbe autointersecarsi)
SurfFlatRegionByContours SfrByC ;
if ( ! SfrByC.AddCurve( Release( pCrvExtLoopSurfInc)))
return false ;
PtrOwner<ISurfFlatRegion> pSfr( SfrByC.GetSurf()) ;
if ( pSfr->IsValid() && pSfr->GetChunkCount() > 0) {
if ( AreOppositeVectorApprox( pSfr->GetNormVersor(), Z_AX))
pSfr->Invert() ;
pSfrInc->CopyFrom( pSfr) ;
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
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
*/
// 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 ( leggermente più grande )
PtrOwner<ISurfFlatRegion> pSfrInc( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrInc))
return false ;
if ( ! CreateSurfFrIncidence( pCrvBorder, PockParams, dRad + 75. * EPS_SMALL, pSfrInc)) {
pSfrInc.Set( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( pCrvBorder), dRad + 75 * EPS_SMALL, false, false)) ;
if ( IsNull( pSfrInc) || ! pSfrInc->IsValid())
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 i = 0 ; i < int( vCrvToCheck.size()) ; ++ i) {
// 1) recupero la curva corrente
PtrOwner<ICurveComposite> pCrvCurr( vCrvToCheck[i]->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 nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU) {
if ( vpCrvs[nU]->GetTempProp() == TEMP_PROP_OPEN_EDGE)
continue ;
// 4) effettuo l'Offset della curva di metà dDiamJ
OffsetCurve OffsCrv ;
if ( ! OffsCrv.Make( vpCrvs[nU], - dDiamJ / 2. - 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 / 2., 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
PtrOwner<ISurfFlatRegion> pSfrBean_test( CloneSurfFlatRegion( pSfrBean)) ;
if ( IsNull( pSfrBean_test) || ! pSfrBean_test->IsValid())
return false ;
pSfrBean_test->Intersect( *pSfrInc) ;
bDiscard = ( IsNull( pSfrBean_test) ||
! pSfrBean_test->IsValid() ||
pSfrBean_test->GetChunkCount() == 0) ;
}
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 ;
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
// se curva originale non tutta aperta e multi-chunk
if ( ! bIsAllOpen && 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)
bool bFound = false ;
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)) {
bFound = true ;
// 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 ;
}
}
}
// se ho trovato qualcosa, allora aggiorno la superficie di incidenza
if ( bFound && pNewSfrInc->IsValid())
pSfrInc.Set( pNewSfrInc) ;
}
// se la regione è formata da più chunks, prendo quella con area di loop esterno maggiore
if ( ! IsNull( pSfrInc) && pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 1) {
double dMaxArea = - INFINITO ;
int nChunk = 0 ;
for ( int nC = 0 ; nC < pSfrInc->GetChunkCount() ; ++ nC) {
PtrOwner<ICurve> pExtLoop( pSfrInc->GetLoop( nC, 0)) ;
if ( IsNull( pExtLoop) || ! pExtLoop->IsValid())
return false ;
double dArea = EPS_SMALL ;
pExtLoop->GetAreaXY( dArea) ;
if ( dArea > dMaxArea) {
dMaxArea = dArea ;
nChunk = nC ;
}
}
pSfrInc.Set( pSfrInc->CloneChunk( nChunk)) ;
}
// se regione di incidenza nulla o non valida, errore
if ( IsNull( pSfrInc) || ! pSfrInc->IsValid())
return false ;
}
// 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, TEMP_PROP_OPEN_EDGE, 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, TEMP_PROP_OPEN_EDGE, 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
if ( PockParams.nType == POCKET_ZIGZAG && PockParams.bSmooth)
dRad += PockParams.bSmooth ;
// se lavorazione Conformal
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
if ( PockParams.dRad - PockParams.dSideStep > 0)
dRad -= PockParams.dSideStep ;
}
// 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 ;
else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY)
dDiamJ = 1.05 * ( PockParams.dRad + dRad) + dOffR ;
// NB. 1.05 serve per eccedere leggermente, in modo che il contro-offset della prima curva di svuotatura presenti
// dei piccoli archi tra i chiusi e gli aperti ( in modo da svuotare bene lungo il chiuso)
// curva finale da restituire
PtrOwner<ICurveComposite> pCrvCompo_final( CreateCurveComposite()) ;
if ( IsNull( pCrvCompo_final))
return false ;
// parametro iniziale del tratto corrente, sulla curva originale
double dParS = 0. ;
// scorro tutti i tratti omogenei nel vettore
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
// recupero la proprietà della curva composita
int nCurrTmpProp = vpCrvs[i]->GetTempProp() ;
// aggiorno i parametri del lato aperto corrente sulla curva originale
double dParE = dParS + 1. * vpCrvs[i]->GetCurveCount() ;
// se aperta
if ( nCurrTmpProp == TEMP_PROP_OPEN_EDGE) {
// controllo che sia almeno lungo quanto il diametro utensile
double dLen = 0. ;
vpCrvs[i]->GetLength( dLen) ;
// 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, TEMP_PROP_CLOSE_EDGE) ;
// 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 ( ssize( vCrvCompo) < 2)
return true ;
// controllo validità delle curve
for ( int i = 0 ; i < ssize( vCrvCompo) ; ++ 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, ssize( vCrvCompo)) ; // evito inversioni, le curve devono essere coerenti
for ( int i = 0 ; i < ssize( vCrvCompo) ; ++ 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( 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 ( int i = 0 ; i < ssize( vnInd) ; ++ 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, bool bInvertOpenCrv)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// se la curva è aperta
if ( ! pCrvCompo->IsClosed()) {
// se richiesta inversione
if ( bInvertOpenCrv) {
// se non ho un punto di riferimento, 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, bool bInvertOpenCrv)
{
// se non ho curve, allora non faccio nulla
if ( vCrvCompo.empty())
return true ;
// per ogni curva
for ( int i = 0 ; i < ssize( vCrvCompo) ; ++ 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, bInvertOpenCrv))
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 < ssize( vCrvCompo) ; ++ 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, GetExtendPathLen( PockParams), 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, GetExtendPathLen( PockParams), 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 ;
// controllo che il MaxOptSize sia coerente
if ( 2. * dMaxOffs > PockParams.dMaxOptSize + 10. * 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) ;
// unisco il primo e l'ultimo se estremi compatibili
if ( ssize( vpCrvs) > 1) {
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) ;
}
}
// CASO PARTICOLARE : Tunnel ( 2 tratti open e due tratto chiusi) -> non uso le curve singole
if ( int( vpCrvs.size() == 4))
return true ;
// controllo se il loop Esterno è uniforme ( quindi tutto chiuso o tutto aperto)
bool bExtAllClose = false ;
bool bExtAllOpen = false ;
if ( ssize( vpCrvs) == 1) {
if ( vpCrvs[0]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
bExtAllClose = true ;
else
bExtAllOpen = true ;
}
// NB. Se regione estena tutta chiusa o aperta e senza isole, 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 vCrvCompoResTmp ; // salvo il vettore di curve offsettate del raggio utensile
Voronoi myVRONI ;
for ( int i = 0 ; i < ssize( vpCrvs) ; ++ i) {
// se tratto chiuso
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
myVRONI.AddCurve( vpCrvs[i]) ;
}
// offset delle parti chiuse
ICURVEPOVECTOR vCrvVroniOffs ;
myVRONI.CalcOffset( vCrvVroniOffs, - PockParams.dRad - PockParams.dRadialOffset + EPS_SMALL, ICurve::OFF_FILLET) ;
for ( int i = 0 ; i < ssize( vCrvVroniOffs) ; ++ i)
vCrvCompoResTmp.emplace_back( ConvertCurveToComposite( Release( vCrvVroniOffs[i]))) ;
/*
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 = TEMP_PROP_INVALID ;
int nPrecTmpProp = TEMP_PROP_INVALID ;
bool bIsMixed = false ;
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ;
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 ( isole non uniformi non sono definite)
if ( bIsMixed) {
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() ; ++ nU)
pCrvIsl->SetTempProp( nU, TEMP_PROP_CLOSE_EDGE) ;
nCurrTmpProp = TEMP_PROP_CLOSE_EDGE ; // 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 nL = 1 ; nL < pSfrChunk->GetLoopCount( 0) ; ++ nL) {
pSrfIslands->AddExtLoop( pSfrChunk->GetLoop( 0, nL)) ;
vpCrvs.emplace_back( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, nL))) ;
}
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)
vCrvCompoResTmp.emplace_back( ConvertCurveToComposite( pSrfIslands->GetLoop( nC, nL))) ;
}
}
}
// se non ho ottenuto curve, allora ho finito
if ( vCrvCompoResTmp.empty())
return true ;
// controllo che effettivamente l'utensile svuoti la regione
PtrOwner<ISurfFlatRegion> pSfrNotRemoved( CloneSurfFlatRegion( pSfrChunk)) ;
if ( IsNull( pSfrNotRemoved) || ! pSfrNotRemoved->IsValid())
return false ;
double dOffs = PockParams.dRad + PockParams.dRadialOffset + 50. * EPS_SMALL ;
double dTol = 25. * EPS_SMALL ;
for ( int i = 0 ; i < ssize( vCrvCompoResTmp) ; ++ i) {
PtrOwner<ISurfFlatRegion> pSfrRemoved( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vCrvCompoResTmp[i]), dOffs, false, false)) ;
if ( ! IsNull( pSfrRemoved) && pSfrRemoved->IsValid()) {
if ( AreOppositeVectorEpsilon( pSfrRemoved->GetNormVersor(), pSfrNotRemoved->GetNormVersor(), dTol))
pSfrRemoved->Invert() ;
pSfrNotRemoved->Subtract( *pSfrRemoved) ;
}
}
for ( int i = 0 ; i < ssize( vpCrvs) ; ++ i) {
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) {
PtrOwner<ISurfFlatRegion> pSfrRemoved( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vpCrvs[i]), dOffs, false, false)) ;
if ( ! IsNull( pSfrRemoved) && pSfrRemoved->IsValid()) {
if ( AreOppositeVectorEpsilon( pSfrRemoved->GetNormVersor(), pSfrNotRemoved->GetNormVersor(), dTol))
pSfrRemoved->Invert() ;
pSfrNotRemoved->Subtract( *pSfrRemoved) ;
}
}
}
if ( pSfrNotRemoved->IsValid() && pSfrNotRemoved->GetChunkCount() > 0)
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()) {
// 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 - dTol, ICurve::OFF_FILLET) ; // c'è un offset radiale, quindi tengo tolleranza alta
ICRVCOMPOPOVECTOR vCrvCompoResTmpSplitted ;
for ( int i = 0 ; i < ssize( vCrvCompoResTmp) ; ++ i) {
CRVCVECTOR ccClass ;
if ( pSfrLimit->GetCurveClassification( *vCrvCompoResTmp[i], EPS_SMALL, ccClass)) {
for ( int j = 0 ; j < ssize( ccClass) ; ++ j) {
if ( ccClass[j].nClass == CRVC_OUT) {
PtrOwner<ICurveComposite> pCrvRes( ConvertCurveToComposite( vCrvCompoResTmp[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
if ( ! IsNull( pCrvRes) && pCrvRes->IsValid())
vCrvCompoResTmpSplitted.emplace_back( Release( pCrvRes)) ;
}
}
}
}
// se non ho curve, allora esco
if ( vCrvCompoResTmpSplitted.empty())
return true ;
// altrimenti aggiorno il vettore di curve con quelle solo esterne alla regione limite
swap( vCrvCompoResTmp, vCrvCompoResTmpSplitted) ;
}
// salvo come primo tempParam l'offset massimo della regione ( servirà per la Feed)
for ( int i = 0 ; i < ssize( vCrvCompoResTmp) ; ++ i) {
vCrvCompoResTmp[i]->SetTempParam( dMaxOffs, 0) ;
// se richiesta inversione della curva, allora inverto
if ( PockParams.bInvert)
vCrvCompoResTmp[i]->Invert() ;
vCrvCompoRes.emplace_back( Release( vCrvCompoResTmp[i])) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetSinglePocketingCurves( ISurfFlatRegion* pSfr, PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvSingleCurves)
{
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid())
return false ;
vCrvSingleCurves.clear() ;
// tengo una copia della superficie ( serve per l'estensione dei percorsi fuori dal grezzo)
PtrOwner<ISurfFlatRegion> pSfrClone( CloneSurfFlatRegion( pSfr)) ;
if ( IsNull( pSfrClone) || ! pSfrClone->IsValid())
return false ;
// scorro i Chunk della superficie
int nChunks = pSfr->GetChunkCount() ;
int nCurrChunk = 0 ;
for ( int i = 0 ; i < nChunks ; ++ i) {
// recupero il Chunk corrente
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( nCurrChunk)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
return false ;
// se il Chunk corrente si svuota con curva singola, allora lo rimuovo dalla superficie
bool bRemoved = false ;
GetPocketCurvesByClosedEdges( pSfrChunk, PockParams, vCrvSingleCurves, bRemoved) ;
if ( bRemoved)
pSfr->EraseChunk( nCurrChunk) ;
else
++ nCurrChunk ;
}
// 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( vCrvSingleCurves))
return false ;
// estendo le curve, definendo ptStart e calcolando le Feeds
if ( ! AdvanceExtendCurves( vCrvSingleCurves, pSfrClone, PockParams, false))
return false ;
// imposto le Feed e inverto i percorsi per tali curve se richiesto
bool bAllowInvert = ( ! PockParams.bConventionalMilling) ;
for ( int i = 0 ; i < ssize( vCrvSingleCurves) ; ++ i) {
/*
Idea : Feed proporzionale al minimo Offset per annullare la regione
-> Massimo parametro sui bisettori di VORONOI
se le curve risultano aperte, cambio il loro punto iniziale a seconda dell'estensione fatta
*/
if ( vCrvSingleCurves[i]->IsValid() && vCrvSingleCurves[i]->GetCurveCount() > 0 && ! vCrvSingleCurves[i]->IsClosed()) {
int nFirstTempProp = vCrvSingleCurves[i]->GetFirstCurve()->GetTempProp( 0) ;
int nLastTempProp = vCrvSingleCurves[i]->GetLastCurve()->GetTempProp( 0) ;
// se estremi entrambi estesi
if ( nFirstTempProp == TEMP_PROP_OUT_START && nLastTempProp == TEMP_PROP_OUT_START) {
// se inversione delle curve accettata
if ( bAllowInvert) {
if ( PockParams.ptStart.IsValid()) {
Point3d ptStart ; vCrvSingleCurves[i]->GetStartPoint( ptStart) ;
double dSqStartDist = SqDist( PockParams.ptStart, ptStart) ;
Point3d ptEnd ; vCrvSingleCurves[i]->GetEndPoint( ptEnd) ;
double dSqEndDist = SqDist( PockParams.ptStart, ptEnd) ;
if ( dSqEndDist < dSqStartDist)
vCrvSingleCurves[i]->Invert() ;
}
}
vCrvSingleCurves[i]->GetEndPoint( PockParams.ptStart) ;
}
// se invece l'estensione è alla fine, sono forzato ad invertila per entrare presso il lato aperto
else if ( nLastTempProp == TEMP_PROP_OUT_START)
vCrvSingleCurves[i]->Invert() ;
}
// recupero il MaxOffset per la curva
double dMaxOffs = vCrvSingleCurves[i]->GetTempParam( 0) ;
// calcolo la Feed proporzionale a tale Offset
double dFeed ;
GetFeedForParam( 2 * dMaxOffs, PockParams, dFeed) ;
AssignCustomFeed( vCrvSingleCurves[i], PockParams, dFeed) ;
// imposto il flag di curva singola
vCrvSingleCurves[i]->SetTempProp( TEMP_PROP_SINGLE_CURVE, 0) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
ModifySurfByOpenEdges( ISurfFlatRegion* pSfr, PocketParams& PockParams)
{
// controllo parametri :
if ( pSfr == nullptr || ! pSfr->IsValid())
return true ; // <- se superficie non valida, allora non ho niente da impostare sui suoi lati
// 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 ;
// per ogni Chunck della superificie ottenuta...
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
// flag per indicare se il Chunk è stato modificato mediante estensione degli aperti
bool bIsChunkModified = true ;
// 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) ;
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, la forzo chiusa)
// [ La definizione di isola con proprietà non uniformi non è definita ]
ICRVCOMPOPOVECTOR vCrvToTIsland ; // isole che considero valide
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 la restringo
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()) ; // <-- valida
}
// 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( pSfr->CloneChunk( nC)) ;
else if ( ! pSrfFinal->Add( *pSfr->CloneChunk( nC)))
return false ;
}
}
// se la superficie finale estesa non è valida, errore
if ( ! pSrfFinal->IsValid())
return false ;
// chiudo le isole che risultano ambigue ( quindi non omgenee)
for ( int nC = 0 ; nC < pSrfFinal->GetChunkCount() ; ++ nC) {
for ( int nL = 1 ; nL < pSrfFinal->GetLoopCount( nC) ; ++ nL) {
// controllo uniformità
int nCurrTmpProp = TEMP_PROP_INVALID ;
int nPrecTmpProp = TEMP_PROP_INVALID ;
bool bIsMixed = false ;
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSrfFinal->GetLoop( nC, nL))) ;
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)
pSrfFinal->SetCurveTempProp( nC, nL, nU, TEMP_PROP_CLOSE_EDGE) ;
}
}
}
// restituisco la superficie estesa
pSfr->Clear() ;
pSfr->CopyFrom( pSrfFinal) ;
return ( pSfr->IsValid() && pSfr->GetChunkCount() > 0) ;
}
//----------------------------------------------------------------------------
static bool
OptimizedSpiralCircle( 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 ;
// cambio il punto iniziale se necessario
if ( PockParams.ptStart.IsValid()) {
double dUStart = 0. ;
int nFlag = 0 ;
DistPointCurve( PockParams.ptStart, *pArc).GetParamAtMinDistPoint( 0., dUStart, nFlag) ;
pArc->ChangeStartPoint( dUStart) ;
}
// 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) ;
// assegno proprietà di riconoscimento
pMCrv->SetTempProp( TEMP_PROP_OPT_CIRCLE, 0) ;
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 ;
if ( pCrvTrap->GetCurve( nBaseId)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE)
nBaseId += 2 ;
// 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 || ! pCrvCompo->IsValid() ||
nBase < 0 || nBase >= pCrvCompo->GetCurveCount() ||
nSecondBase < 0 || nSecondBase >= pCrvCompo->GetCurveCount())
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( CloneCurveComposite( pCrvCompo)) ;
PtrOwner<ICurveComposite> pCrvTest1( CloneCurveComposite( pCrvCompo)) ;
pCrvCompo->GetCurve( nBase)->GetStartPoint( ptSB0) ;
pCrvCompo->GetCurve( nBase)->GetEndPoint( ptEB0) ;
pCrvCompo->GetCurve( nSecondBase)->GetStartPoint( ptSB1) ;
pCrvCompo->GetCurve( nSecondBase)->GetEndPoint( ptEB1) ;
pCrvTest0->ChangeStartPoint( nBase) ;
pCrvTest1->ChangeStartPoint( nSecondBase) ;
double dUTrim0, dUTrim1 ;
pCrvTest0->GetParamAtPoint( ptSB1, dUTrim0) ;
pCrvTest1->GetParamAtPoint( ptSB0, dUTrim1) ;
pCrvTest0->TrimStartEndAtParam( 1, dUTrim0) ;
pCrvTest1->TrimStartEndAtParam( 1, dUTrim1) ;
// controllo che la parte destra si uniforme per le TmpProp
for ( int u = 0 ; u < pCrvTest0->GetCurveCount() - 1 ; ++ u) {
int nPropAct, nPropSucc ;
if ( pCrvTest0->GetCurveTempProp( u, nPropAct, 0) &&
pCrvTest0->GetCurveTempProp( u + 1, nPropSucc, 0) &&
nPropAct != nPropSucc)
return true ; // se TmpProp diverse => non è un caso ottimizzato
}
// controllo che la parte sinistra sia uniforme per le TmpProp
for ( int u = 0 ; u < pCrvTest1->GetCurveCount() - 1 ; ++ u) {
int nPropAct, nPropSucc ;
if ( pCrvTest1->GetCurveTempProp( u, nPropAct, 0) &&
pCrvTest1->GetCurveTempProp( u + 1, nPropSucc, 0) &&
nPropAct != nPropSucc)
return true ; // se TmpProp diverse => non è un caso ottimizzato
}
// se lato destro aperto ( estendo il punto finale della base principale e il punto iniziale
// della base secondaria fino al lato destro del box)
bool bCopyRight = false ;
if ( pCrvTest0->GetCurve( 0)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
int nCrvRight_ref = ( nType == 0 ? 0 : 1) ;
pCrvTrap->GetCurve( nCrvRight_ref)->GetStartPoint( ptEB0) ;
pCrvTrap->GetCurve( nCrvRight_ref)->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) == TEMP_PROP_OPEN_EDGE) {
int nCrvLeft_ref = ( nType == 0 ? 2 : 3) ;
pCrvTrap->GetCurve( nCrvLeft_ref)->GetStartPoint( ptEB1) ;
pCrvTrap->GetCurve( nCrvLeft_ref)->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, TEMP_PROP_OPEN_EDGE, 0) ; // aperta
}
nSecondBase = pCrvTrap->GetCurveCount() ;
pCrvTrap->AddLine( ptEB1) ;
if ( bCopyLeft)
pCrvTrap->AddCurve( Release( pCrvTest1)) ;
else {
pCrvTrap->Close() ;
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, TEMP_PROP_OPEN_EDGE, 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
IsForcedStepTrapezoid( const ICurveComposite* pCrvTrap, const PocketParams& PockParam,
int nBase, int nSecondBase, bool& bForced)
{
bForced = false ;
// se la curva non è valida, allora non può essere forzato
if ( pCrvTrap == nullptr || ! pCrvTrap->IsValid())
return false ;
// scorro la curva e ricavo le TempProps
array<int, 4> vnProps ;
int nClose = 0 ;
for ( int i = 0 ; i < 4 ; ++ i) {
if ( ! pCrvTrap->GetCurveTempProp( i, vnProps[i], 0))
return false ;
if ( vnProps[i] == TEMP_PROP_CLOSE_EDGE)
++ nClose ;
}
double dDiam = 2. * PockParam.dRad ;
switch ( nClose) {
// se trapezio tutto aperto, allora non è forzato
case 0 :
bForced = false ;
break ;
// se ho un lato chiuso, non è forzato
case 1 :
bForced = false ;
break ;
// se ho due lati chiusi
case 2 : {
if ( nBase < 0 || nBase > 4 || nSecondBase < 0 || nSecondBase > 4)
return false ;
// se entrambe le basi sono chiuse, è forzato
if ( vnProps[nBase] == TEMP_PROP_CLOSE_EDGE && vnProps[nSecondBase] == TEMP_PROP_CLOSE_EDGE)
bForced = true ;
// se entrambe le basi sono aperte
else if ( vnProps[nBase] == TEMP_PROP_OPEN_EDGE && vnProps[nSecondBase] == TEMP_PROP_OPEN_EDGE) {
double dLenOpen = 0. ;
for ( int i = 0 ; i < 4 ; ++ i) {
if ( i == nBase || i == nSecondBase) {
const ICurve* pCrvOpen = pCrvTrap->GetCurve( i) ;
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
return false ;
// essendo nei casi a trapezio, ho solo segmenti
pCrvOpen->GetLength( dLenOpen) ;
Vector3d vtDir ; pCrvOpen->GetStartDir( vtDir) ;
vtDir *= dLenOpen ;
const ICurve* pCrvClosePrev = pCrvTrap->GetCurve( ( i == 0 ? 3 : i - 1)) ;
if ( pCrvClosePrev == nullptr || ! pCrvClosePrev->IsValid())
return false ;
const ICurve* pCrvCloseAft = pCrvTrap->GetCurve( ( i == 3 ? 0 : i + 1)) ;
if ( pCrvCloseAft == nullptr || ! pCrvCloseAft->IsValid())
return false ;
// essendo un trapezio queste due direzioni me le aspetto parallele tra loro
Vector3d vtDirPrev ; pCrvClosePrev->GetEndDir( vtDirPrev) ;
Vector3d vtDirAft ; pCrvCloseAft->GetStartDir( vtDirAft) ;
dLenOpen = min( { OrthoCompo( vtDir, vtDirPrev).Len(), OrthoCompo( vtDir, vtDirAft).Len(), dLenOpen}) ;
break ;
}
}
bForced = ( dLenOpen < dDiam + TOL_TRAPEZOID) ;
}
// se alternate, non forzo
else
bForced = false ;
}
break ;
// se ho tre lati chiusi
case 3 : {
// diventa forzato se il lato aperto non ha una componente perpendicolare grande rispetto al chiuso precedente e successivo
double dLenOpen = 0. ;
for ( int i = 0 ; i < 4 ; ++ i) {
if ( vnProps[i] == TEMP_PROP_OPEN_EDGE) {
const ICurve* pCrvOpen = pCrvTrap->GetCurve( i) ;
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
return false ;
// essendo nei casi a trapezio, ho solo segmenti
pCrvOpen->GetLength( dLenOpen) ;
Vector3d vtDir ; pCrvOpen->GetStartDir( vtDir) ;
vtDir *= dLenOpen ;
const ICurve* pCrvClosePrev = pCrvTrap->GetCurve( ( i == 0 ? 3 : i - 1)) ;
if ( pCrvClosePrev == nullptr || ! pCrvClosePrev->IsValid())
return false ;
const ICurve* pCrvCloseAft = pCrvTrap->GetCurve( ( i == 3 ? 0 : i + 1)) ;
if ( pCrvCloseAft == nullptr || ! pCrvCloseAft->IsValid())
return false ;
// essendo un trapezio queste due direzioni me le aspetto parallele tra loro
Vector3d vtDirPrev ; pCrvClosePrev->GetEndDir( vtDirPrev) ;
Vector3d vtDirAft ; pCrvCloseAft->GetStartDir( vtDirAft) ;
dLenOpen = min( { OrthoCompo( vtDir, vtDirPrev).Len(), OrthoCompo( vtDir, vtDirAft).Len(), dLenOpen}) ;
break ;
}
}
bForced = ( dLenOpen < dDiam + TOL_TRAPEZOID) ;
}
break ;
// se tutto chiuso, è forzato
case 4 :
bForced = true ;
break ;
default :
return false ;
}
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() ;
// assegno le tempProp della curva trapezio
for ( int nU = 0 ; nU < 4 ; ++ nU) {
Vector3d vtCrvDir ; pCrvTrap->GetCurve( nU)->GetStartDir( vtCrvDir) ;
for ( int nI = 0 ; nI < pCrvCompo->GetCurveCount() ; ++ nI) {
Vector3d vtCurrCrvDir ; pCrvCompo->GetCurve( nI)->GetStartDir( vtCurrCrvDir) ;
if ( AreSameVectorEpsilon( vtCrvDir, vtCurrCrvDir, 150 * EPS_SMALL)) {
pCrvTrap->SetCurveTempProp( nU, pCrvCompo->GetCurve( nI)->GetTempProp( 0), 0) ;
break ;
}
}
}
if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap))
return false ;
bool bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE ||
pCrvTrap->GetCurve( 2)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) ;
// controllo dimensioni della svuotatura
if ( ! ( bBaseOpen && dPocketSize < dDiam + TOL_TRAPEZOID) &&
abs( dPocketSize - dDiam) > EPS_SMALL + TOL_TRAPEZOID) {
pCrvTrap->Clear() ;
return true ;
}
// recupero flag aperto/chiuso dei lati
if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE &&
pCrvTrap->GetCurve( 3)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) {
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, TEMP_PROP_OPEN_EDGE, 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 : TEMP_PROP_OPEN_EDGE, 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 ? TEMP_PROP_CLOSE_EDGE : TEMP_PROP_OPEN_EDGE, 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 nU = 0 ; nU < pCrvCompo->GetCurveCount() && ! bOK ; ++ nU) {
// cerco un lato chiuso ( esiste per forza)
if ( pCrvCompo->GetCurve( nU)->GetTempProp( 0) == 0) {
// controllo se il lato corrente può essere una base
int nType = -1 ;
if ( ! GetBoxCrvOptTrap( nU, 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( nU)->GetStartDir( vtBaseDir) ;
// ricavo il suo punto iniziale
Point3d ptBaseStart ; pCrvCompo->GetCurve( nU)->GetStartPoint( ptBaseStart) ;
// ricavo il suo punto finale
Point3d ptBaseEnd ; pCrvCompo->GetCurve( nU)->GetEndPoint( ptBaseEnd) ;
// cerco il lato chiuso lineare parallelo ad esso e distante circa dPocketSize
for ( int nOtherU = 0 ; nOtherU < pCrvCompo->GetCurveCount() && ! bOK ; ++ nOtherU) {
if ( nOtherU == nU ||
pCrvCompo->GetCurve( nOtherU)->GetType() != CRV_LINE ||
pCrvCompo->GetCurve( nOtherU)->GetTempProp() != 0)
continue ;
Vector3d vtSecondBaseDir ; pCrvCompo->GetCurve( nOtherU)->GetStartDir( vtSecondBaseDir) ;
if ( ! AreOppositeVectorEpsilon( vtBaseDir, vtSecondBaseDir, 5 * EPS_SMALL))
continue ;
Point3d ptSecondBaseStart ; pCrvCompo->GetCurve( nOtherU)->GetStartPoint( ptSecondBaseStart) ;
double dDist = 0. ;
DistPointCurve DPL( ptBaseStart, *pCrvCompo->GetCurve( nOtherU), false) ;
if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) {
nBase = nU ;
nSecondBase = nOtherU ;
if ( ! PreparareTrapezoidTwoBases( pCrvCompo, dDiam, nType, nBase, nSecondBase, bOK, pCrvTrap))
return false ;
}
}
}
}
}
if ( ! bOK) {
pCrvTrap->Clear() ;
nBase = -1 ;
nSecondBase = -1 ;
return true ;
}
}
// 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
bool bForced = false ;
IsForcedStepTrapezoid( pCrvTrap, PockParams, nBase, nSecondBase, bForced) ;
if ( ! bForced) {
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,
const Point3d& ptRef)
{
// 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) ;
PtrOwner<ICurveLine> pLineE( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 2 : 1)->Clone())) ;
pLineE->SimpleOffset( - dRad) ;
// verifico orientamento delle curve in base al punto di riferimento ( se valido)
double dSqDistS = 0., dSqDistE = 0. ;
if ( ! ptRef.IsValid() ||
( ( DistPointCurve( ptRef, *pCrvPocket->GetCurve( bEvenClosed ? 3 : 0)).GetSqDist( dSqDistS)) &&
( DistPointCurve( ptRef, *pCrvPocket->GetCurve( bEvenClosed ? 1 : 2)).GetSqDist( dSqDistE)) &&
dSqDistS < dSqDistE)) {
pLineS->Invert() ;
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 == TEMP_PROP_OPEN_EDGE) {
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, - ANG_RIGHT , 2 * dPocketSize) ;
else
pCrvVertLine->SetPDL( Box3d.GetMin() - 5 * TOL_TRAPEZOID * Y_AX, ANG_RIGHT, 2 * dPocketSize) ;
// intersezione
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)) {
double dDiffY = ccClass2.IciA[0].ptI.y - dYCoord ;
double dOffs = sqrt( dRad * dRad - dDiffY * dDiffY) ;
if ( bStart)
dXCoord = max( dXCoord, Box3d.GetMax().x + dOffs) ;
else
dXCoord = min( dXCoord, Box3d.GetMin().x - dOffs) ;
}
}
}
}
}
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 ;
if ( ! bStart)
pMCrv->Invert() ;
// recupero la coordinata Y del tratto lineare e la dimensione della tasca a trapezio
Point3d ptTmp ;
pMCrv->GetStartPoint( ptTmp) ;
double dYCoord = ptTmp.y ;
pCrvPocket->GetCurve( 2)->GetStartPoint( ptTmp) ;
double dPocketSize = ptTmp.y ;
// offset del lato chiuso del raggio utensile, verso l'interno della tasca a trapezio
int nCrvId = ( bStart ? 3 : 1) ;
PtrOwner<ICurveLine> pLine( GetCurveLine( pCrvPocket->GetCurve( nCrvId)->Clone())) ;
pLine->SimpleOffset( - dRad) ;
// inverto gli estremi se necessario
Point3d ptP1, ptP2 ;
pLine->GetStartPoint( ptP1) ;
pLine->GetEndPoint( ptP2) ;
if ( ! bStart)
swap( ptP1, ptP2) ;
int nProp2 ;
PtrOwner<ICurveComposite> pCompo( CreateCurveComposite()) ;
if ( IsNull( pCompo))
return false ;
// lato opposto a quello di riferimento aperto
if ( pCrvPocket->GetCurveTempProp( 2, nProp2) && nProp2 != TEMP_PROP_CLOSE_EDGE) {
// 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 + 100. * EPS_SMALL) {
// 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) ;
bool bUseBiArc = false ;
PtrOwner<ICurve> pBiArc( GetBiArc( ptP1, - dAng, ptCrv, bStart ? 0 : ANG_STRAIGHT, 0.8)) ;
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, TEMP_PROP_OPEN_EDGE) ;
pMCrv->AddCurve( Release( pCompo), false) ;
// se richiesto ripristino la direzione originaria
if ( ! bStart)
pMCrv->Invert() ;
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) ;
Point3d ptRef = P_INVALID ;
if ( PockParams.ptStart.IsValid())
ptRef = GetToLoc( PockParams.ptStart, 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] != TEMP_PROP_CLOSE_EDGE ? 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 > dDiam + TOL_TRAPEZOID) &&
( ( ( vnProp[0] != TEMP_PROP_CLOSE_EDGE && vnProp[2] != TEMP_PROP_CLOSE_EDGE) &&
( vnProp[3] == TEMP_PROP_CLOSE_EDGE && vnProp[1] == TEMP_PROP_CLOSE_EDGE) &&
( max( dLen0, dLen2) < 2. * dDiam + TOL_TRAPEZOID)) ||
( ( vnProp[1] != TEMP_PROP_CLOSE_EDGE && vnProp[3] != TEMP_PROP_CLOSE_EDGE) &&
( vnProp[0] == TEMP_PROP_CLOSE_EDGE && vnProp[2] == TEMP_PROP_CLOSE_EDGE) &&
( max( dLen0, dLen2) < 2. * dDiam + TOL_TRAPEZOID)))) {
if ( ! SpecialAdjustTrapezoidSpiralForAngles( pMCrv, ( vnProp[0] == TEMP_PROP_CLOSE_EDGE), pCrvPocket, PockParams, ptRef)) {
pMCrv->Clear() ;
return false ;
}
}
else {
// trovo la quota Y per centro del Tool
double dYCoord ;
// se base principale chiusa
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
dYCoord = 0.5 * dDiam + dOffsR ;
// se base principale aperta e secondaria chiusa
else if ( pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
dYCoord = dPocketSize - 0.5 * dDiam - dOffsR ;
// se entrambi i lati paralleli sono aperti mi posiziono a metà della svuotatura
else
dYCoord = 0.5 * dPocketSize ;
// determino le quote coordinate in X inziale e finale
double dXCoordStart = -INFINITO ;
double dXCoordEnd = INFINITO ;
if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, true, dYCoord, dXCoordStart, dPocketSize, PockParams) ||
dXCoordStart < - INFINITO + 1)
return false ;
if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, false, dYCoord, dXCoordEnd, dPocketSize, PockParams) ||
dXCoordEnd > INFINITO - 1)
return false ;
if ( dXCoordStart > dXCoordEnd + 500 * EPS_SMALL)
return false ;
// determino se devo invertire la curva dato il punto di riferimento
bool bInvertByRef = false ;
if ( ptRef.IsValid()) {
PtrOwner<ICurve> pCrvRight( pCrvPocket->CopyParamRange( nBase + 1, nSecondBase)) ;
PtrOwner<ICurve> pCrvLeft( pCrvPocket->CopyParamRange( nSecondBase + 1, nBase)) ;
if ( IsNull( pCrvRight) || IsNull( pCrvLeft) ||
! pCrvRight->IsValid() || ! pCrvLeft->IsValid())
return false ;
double dSqDistBase = INFINITO, dSqDistSecondBase = INFINITO, dSqDistLeft = INFINITO, dSqDistRight = INFINITO ;
DistPointCurve( ptRef, *pCrvPocket->GetCurve( nBase)).GetSqDist( dSqDistBase) ;
DistPointCurve( ptRef, *pCrvPocket->GetCurve( nSecondBase)).GetSqDist( dSqDistSecondBase) ;
DistPointCurve( ptRef, *pCrvRight).GetSqDist( dSqDistRight) ;
DistPointCurve( ptRef, *pCrvLeft).GetSqDist( dSqDistLeft) ;
bInvertByRef = ( dSqDistSecondBase < min( {dSqDistBase, dSqDistLeft, dSqDistRight}) ||
dSqDistRight < min( {dSqDistBase, dSqDistSecondBase, dSqDistLeft})) ;
}
// determino gli estremi del segmento di svuotatura
Point3d ptStart( dXCoordStart, dYCoord) ;
Point3d ptEnd( dXCoordEnd, dYCoord) ;
// verifico se il punto iniziale e finale coincidono
if ( bRealTrap && AreSamePointEpsilon( ptStart, ptEnd, 500 * EPS_SMALL) &&
( vnProp[0] != TEMP_PROP_CLOSE_EDGE || vnProp[2] != TEMP_PROP_CLOSE_EDGE)) {
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] != TEMP_PROP_CLOSE_EDGE) {
pMCrv->AddPoint( ptS) ;
if ( vnProp[2] != TEMP_PROP_CLOSE_EDGE)
pMCrv->AddLine( ptE) ;
else
pMCrv->AddLine( ptStart) ;
}
else {
pMCrv->AddPoint( ptE) ;
if ( vnProp[0] != TEMP_PROP_CLOSE_EDGE)
pMCrv->AddLine( ptS) ;
else
pMCrv->AddLine( ptStart) ;
pMCrv->Invert() ;
}
pMCrv->SetCurveTempProp( 0, TEMP_PROP_OPEN_EDGE) ;
// verifico se invertirla nel caso di un punto di riferimento
if ( bInvertByRef &&
vnProp[0] != TEMP_PROP_CLOSE_EDGE && vnProp[2] != TEMP_PROP_CLOSE_EDGE)
pMCrv->Invert() ;
}
}
// se estremi del segmento differenti, allora il percorso è definito
else {
// creo la linea
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) == TEMP_PROP_OPEN_EDGE ||
pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
Frame3d frOpen ;
bool bSwitch = false ;
// Base Principale chiusa e Base Secondaria aperta o inversione per punto di riferimento
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE ||
bInvertByRef) {
pCrvPocket->ChangeStartPoint( nSecondBase) ;
// oriento il Frame ( ho sempre l'origine nell'estremo superiore del lato aperto)
Vector3d vtDir ; pCrvPocket->GetStartDir( vtDir) ;
Point3d ptOpen ;
if ( vtDir.y > 0)
pCrvPocket->GetFirstCurve()->GetEndPoint( ptOpen) ;
else
pCrvPocket->GetFirstCurve()->GetStartPoint( ptOpen) ;
frOpen.Set( ptOpen, Z_AX, -X_AX) ;
if ( ! frOpen.IsValid())
return false ;
pCrvPocket->ToLoc( frOpen) ;
pMCrv->ToLoc( frOpen) ;
pMCrv->Invert() ;
bSwitch = true ;
}
// la Base principale è sempre aperta
if ( pCrvPocket->GetCurve( 3)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE &&
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, true)) {
pMCrv->Clear() ;
return false ;
}
if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE &&
! 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) ;
// 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
AdjustTrapeziodLeadIn( ICurveComposite* pCrvRes, const PocketParams& PockParam,
const ISurfFlatRegion* pSfrChunk)
{
// recupero la TempProp
int nTmpProp = pCrvRes->GetTempProp( 0) ;
// se esiste almeno un aperto
if ( nTmpProp > 0) {
// se solo lato3 aperto
if ( nTmpProp == 2)
pCrvRes->Invert() ; // entro dall'unico aperto
bool bCheckHead = ( nTmpProp != 8 && nTmpProp != 2) ;
if ( bCheckHead) {
// recupero gli estremi della curva corrente e la inverto in base alla Testa
Point3d ptS ; pCrvRes->GetStartPoint( ptS) ;
Point3d ptE ; pCrvRes->GetEndPoint( ptE) ;
Point3d ptSGlob = GetToGlob( ptS, PockParam.frLocXY) ;
Point3d ptEGlob = GetToGlob( ptE, PockParam.frLocXY) ;
if ( ( PockParam.bAboveHead && ptEGlob.z > ptSGlob.z) ||
( ! PockParam.bAboveHead && ptEGlob.z < ptSGlob.z))
pCrvRes->Invert() ;
}
}
else if ( ! PockParam.bConventionalMilling) { // per sgrossature
Point3d ptS ; pCrvRes->GetStartPoint( ptS) ;
Point3d ptE ; pCrvRes->GetEndPoint( ptE) ;
if ( SqDist( ptE, PockParam.ptEnd) < SqDist( ptE, PockParam.ptEnd) + EPS_SMALL)
pCrvRes->Invert() ;
}
// Assegno la Feed
AssignFeedSpiralOpt( 1, PockParam, pCrvRes) ;
// Se curva da invertire, inverto
if ( PockParam.bInvert)
pCrvRes->Invert() ;
// se esiste almeno un aperto, provo ad estendere il percorso
if ( nTmpProp > 0) {
// Calcolo eventuale entrata da fuori
Vector3d vtRef ; pCrvRes->GetStartDir( vtRef) ;
vtRef.Invert() ;
bool bIsStartExtended = false ;
if ( ! ExtendPath( pCrvRes, pSfrChunk, PockParam, vtRef, false, GetExtendPathLen( PockParam), bIsStartExtended))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetZigZagOptimizedCurves( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParam,
ICurveComposite* pCrvRes)
{
// controllo dei parametri
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
return false ;
pCrvRes->Clear() ;
// ricavo la curva di bordo del chunk corrente
PtrOwner<ICurveComposite> pCrvBorder( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid())
return false ;
pCrvBorder->MergeCurves( 10. * EPS_SMALL, 10. * EPS_ANG_SMALL, true, true) ;
pCrvBorder->SetExtrusion( pSfrChunk->GetNormVersor()) ;
/* TRAPEZI
- E' richiesto che una dimensione del box della curva sia compatibile con il primo Offset, il
quale sarebbe una singola curva aperta
*/
PtrOwner<ICurveComposite> pCrvTrap( CreateCurveComposite()) ;
if ( IsNull( pCrvTrap))
return false ;
Frame3d frTrap ;
double dPocketSize ;
int nBase, nSecondBase ;
bool bOkTrap = GetTrapezoidFromShape( pCrvBorder, pCrvTrap, frTrap, PockParam, dPocketSize, nBase, nSecondBase) ;
if ( bOkTrap && pCrvTrap->IsValid()) {
// verifico se il trapezio ottenuto deve o meno rispettare il SideStep
bool bForcedTrap = false ;
IsForcedStepTrapezoid( pCrvTrap, PockParam, nBase, nSecondBase, bForcedTrap) ;
if ( ! bForcedTrap)
bOkTrap = ( dPocketSize < PockParam.dMaxOptSize + 10. * EPS_SMALL) ;
}
if ( bOkTrap && pCrvTrap->IsValid()) {
pCrvTrap->SetExtrusion( Z_AX) ;
CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParam, pCrvRes, bOkTrap) ;
if ( bOkTrap) {
// verifico che tale curva non interferisca con la regione limite
if ( PockParam.SfrLimit.IsValid()) {
double dOffsCheck = PockParam.dRad + PockParam.dRadialOffset - 50. * EPS_SMALL ; // restrittivo per sicurezza
PtrOwner<ISurfFlatRegion> pSfrToolShape( GetSurfFlatRegionFromFatCurve( pCrvRes->Clone(), dOffsCheck, false, false)) ;
bOkTrap = ( ! IsNull( pSfrToolShape) && pSfrToolShape->IsValid()) ;
if ( bOkTrap) {
bOkTrap = ( pSfrToolShape->Intersect( PockParam.SfrLimit) &&
! pSfrToolShape->IsValid()) ;
if ( ! bOkTrap)
pCrvRes->Clear() ;
}
}
if ( bOkTrap) {
AdjustTrapeziodLeadIn( pCrvRes, PockParam, pSfrChunk) ;
// imposto il flag di curva singola
pCrvRes->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetSpiralOptimizedCurves( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParam,
ICurveComposite* pCrvRes)
{
// controllo dei parametri
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
return false ;
pCrvRes->Clear() ;
// ricavo la curva di bordo del chunk corrente
PtrOwner<ICurveComposite> pCrvBorder( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid())
return false ;
pCrvBorder->MergeCurves( 10. * EPS_SMALL, 10. * EPS_ANG_SMALL, true, true) ;
pCrvBorder->SetExtrusion( pSfrChunk->GetNormVersor()) ;
/* SPIRALE EUCLIDEA
- E' richiesto che la curva di bordo sia una circonferenza tutta OPEN o tutta CLOSED
*/
bool bOkSpiral = true ;
int nTempPropRef = pCrvBorder->GetCurve( 0)->GetTempProp( 0) ;
for ( int nU = 1 ; nU < pCrvBorder->GetCurveCount() && bOkSpiral ; ++ nU)
bOkSpiral = ( pCrvBorder->GetCurve( nU)->GetTempProp( 0) == nTempPropRef) ;
if ( bOkSpiral) {
// controllo che sia una circonferenza
Point3d ptCen ; double dRad ;
if ( ! OptimizedSpiralCircle( pCrvBorder, 50. * EPS_SMALL, dRad, ptCen, bOkSpiral))
return false ;
// se è una circonferenza
if ( bOkSpiral) {
double dOffs = PockParam.dRad + PockParam.dRadialOffset ;
// se curva tutta Open
if ( nTempPropRef == TEMP_PROP_OPEN_EDGE) {
// ingrandisco il raggio
dRad += 1.05 * PockParam.dRad + PockParam.dRadialOffset ;
// verifico che l'utensile ora non interferisca con la regione limite
if ( PockParam.SfrLimit.IsValid()) {
// definisco il nuovo bordo
PtrOwner<ICurveArc> pCrvArc( CreateCurveArc()) ;
bOkSpiral = ( ! IsNull( pCrvArc) &&
pCrvArc->Set( ptCen, Z_AX, dRad + PockParam.dRad)) ;
if ( bOkSpiral) {
CRVCVECTOR ccClass ;
bOkSpiral = ( PockParam.SfrLimit.GetCurveClassification( *pCrvArc, EPS_SMALL, ccClass) &&
ssize( ccClass) == 1 && ccClass[0].nClass == CRVC_OUT) ;
// NB. una versione più complessa dovrebbe verificare se la sottrazione tra la
// superficie dell'utensile e la regione limite non genera un'altra circonferenza...
// In questo caso la sottrazione potrebbe essere trattata come una circonferenza
// chiusa ed essere ancora svotata a spirale...
}
}
}
// se curva chiusa, controllo che il raggio sia compatibile con il primo Offset
else
bOkSpiral = ( dRad - dOffs > 10. * EPS_SMALL) ;
if ( bOkSpiral) {
double dIntRad = 0 ;
if ( PockParam.nType == POCKET_SPIRALOUT && PockParam.nLiType == LEAD_IN_HELIX)
dIntRad = min( 0.5 * min( PockParam.dLiTang, 2. * PockParam.dRad), dRad - dOffs) ;
bool bOkSpiral = CalcCircleSpiral( ptCen, pSfrChunk->GetNormVersor(), dRad - dOffs, dIntRad, PockParam, pCrvRes) ;
if ( bOkSpiral && pCrvRes->IsValid() && pCrvRes->GetCurveCount() > 0) {
// se curva di bordo OPEN e lavorazione SpiralIn, imposto i parametri per LeadIn
if ( nTempPropRef == TEMP_PROP_OPEN_EDGE && PockParam.nType == POCKET_SPIRALIN) {
Vector3d vtMidOut ;
pCrvRes->GetStartDir( vtMidOut) ;
vtMidOut.Rotate( pSfrChunk->GetNormVersor(), PockParam.bInvert ? ANG_RIGHT : - ANG_RIGHT) ;
bool bIsExtended ;
ExtendPath( pCrvRes, pSfrChunk, PockParam, vtMidOut, false, GetExtendPathLen( PockParam), bIsExtended) ;
}
// se invece lavorazione in SpiralOut, inverto il percorso
else if ( PockParam.nType == POCKET_SPIRALOUT)
pCrvRes->Invert() ;
return true ;
}
}
}
}
/* TRAPEZI
- E' richiesto che una dimensione del box della curva sia compatibile con il primo Offset, il
quale sarebbe una singola curva aperta
*/
PtrOwner<ICurveComposite> pCrvTrap( CreateCurveComposite()) ;
if ( IsNull( pCrvTrap))
return false ;
Frame3d frTrap ;
double dPocketSize ;
int nBase, nSecondBase ;
bool bOkTrap = GetTrapezoidFromShape( pCrvBorder, pCrvTrap, frTrap, PockParam, dPocketSize, nBase, nSecondBase) ;
if ( bOkTrap && pCrvTrap->IsValid()) {
// verifico se il trapezio ottenuto deve o meno rispettare il SideStep
bool bForcedTrap = false ;
IsForcedStepTrapezoid( pCrvTrap, PockParam, nBase, nSecondBase, bForcedTrap) ;
if ( ! bForcedTrap)
bOkTrap = ( dPocketSize < PockParam.dMaxOptSize + 10. * EPS_SMALL) ;
}
if ( bOkTrap && pCrvTrap->IsValid()) {
pCrvTrap->SetExtrusion( Z_AX) ;
CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParam, pCrvRes, bOkTrap) ;
if ( bOkTrap) {
// verifico che tale curva non interferisca con la regione limite
if ( PockParam.SfrLimit.IsValid()) {
double dOffsCheck = PockParam.dRad + PockParam.dRadialOffset - 50. * EPS_SMALL ; // restrittivo per sicurezza
PtrOwner<ISurfFlatRegion> pSfrToolShape( GetSurfFlatRegionFromFatCurve( pCrvRes->Clone(), dOffsCheck, false, false)) ;
bOkTrap = ( ! IsNull( pSfrToolShape) && pSfrToolShape->IsValid()) ;
if ( bOkTrap) {
bOkTrap = ( pSfrToolShape->Intersect( PockParam.SfrLimit) &&
! pSfrToolShape->IsValid()) ;
if ( ! bOkTrap)
pCrvRes->Clear() ;
}
}
if ( bOkTrap) {
AdjustTrapeziodLeadIn( pCrvRes, PockParam, pSfrChunk) ;
// imposto il flag di curva singola
pCrvRes->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPocketingOptimizedCurves( ISurfFlatRegion* pSfr, const PocketParams& PockParam,
ICRVCOMPOPOVECTOR& vCrvOptCurves)
{
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid())
return false ;
vCrvOptCurves.clear() ;
// se non sono richiesti i casi ottimizzati esco
if ( PockParam.bAvoidOpt)
return true ;
// scorro i Chunk della superficie
int nChunks = pSfr->GetChunkCount() ;
int nCurrChunk = 0 ;
for ( int i = 0 ; i < nChunks ; ++ i) {
// recupero il Chunk corrente
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( nCurrChunk)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
return false ;
// se il Chunk ha isole, non ho casi ottimizzati
if ( pSfrChunk->GetLoopCount( 0) > 1) {
++ nCurrChunk ;
continue ;
}
// ricavo le curve ottimizzate a seconda della lavorazione richiesta
if ( PockParam.nType == POCKET_SPIRALIN || PockParam.nType == POCKET_SPIRALOUT ||
PockParam.nType == POCKET_CONFORMAL_ZIGZAG || PockParam.nType == POCKET_CONFORMAL_ONEWAY) {
// curva da resituire
PtrOwner<ICurveComposite> pCrvOptSpiral( CreateCurveComposite()) ;
if ( IsNull( pCrvOptSpiral) ||
! GetSpiralOptimizedCurves( pSfrChunk, PockParam, pCrvOptSpiral))
return false ;
// se ho ricavato una curva ottimizzata
if ( ! IsNull( pCrvOptSpiral) && pCrvOptSpiral->IsValid() && pCrvOptSpiral->GetCurveCount() > 0) {
vCrvOptCurves.emplace_back( Release( pCrvOptSpiral)) ;
pSfr->EraseChunk( nCurrChunk) ;
}
else
++ nCurrChunk ;
}
else if ( PockParam.nType == POCKET_ZIGZAG || PockParam.nType == POCKET_ONEWAY) {
// curva da restituire
PtrOwner<ICurveComposite> pCrvOptZigZag( CreateCurveComposite()) ;
if ( IsNull( pCrvOptZigZag) ||
! GetZigZagOptimizedCurves( pSfrChunk, PockParam, pCrvOptZigZag))
return false ;
// se ho ricavato una curva ottimizzata
if ( ! IsNull( pCrvOptZigZag) && pCrvOptZigZag->IsValid() && pCrvOptZigZag->GetCurveCount() > 0) {
vCrvOptCurves.emplace_back( Release( pCrvOptZigZag)) ;
pSfr->EraseChunk( nCurrChunk) ;
}
else
++ nCurrChunk ;
}
}
return true ;
}
// ***************************************************************************
// ------------- SCELTA DEL PUNTO INIZIALE -----------------------------------
// ***************************************************************************
//----------------------------------------------------------------------------
static bool
GetParamOnOpenCurve( const ICurveComposite* pCompo, const PocketParams& PockParams, const Point3d& ptEndPrec, 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() ;
}
// se ho un punto di riferimento, cambio il punto iniziale della curva, partendo da questa
PtrOwner<ICurveComposite> pMyCompo( CloneCurveComposite( pCompo)) ;
if ( IsNull( pMyCompo) || ! pMyCompo->IsValid())
return false ;
if ( ptEndPrec.IsValid()) {
double dMinDistPar = 0. ;
int nFlag = 0 ;
DistPointCurve( ptEndPrec, *pMyCompo).GetParamAtMinDistPoint( 0., dMinDistPar, nFlag) ;
if ( dMinDistPar > EPS_PARAM)
pMyCompo->ChangeStartPoint( floor( dMinDistPar)) ;
}
// 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, bFirst = true ;
const ICurve* pPrevCrv = pMyCompo->GetLastCurve() ;
double dLenPrev = 0. ;
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE)
pPrevCrv->GetLength( dLenPrev) ;
const ICurve* pCrv = pMyCompo->GetFirstCurve() ;
while ( pCrv != nullptr) {
// analizzo la curva successiva
const ICurve* pNextCrv = pMyCompo->GetNextCurve() ;
bool bNextOk = ( pNextCrv != nullptr) ;
if ( ! bNextOk)
pNextCrv = pMyCompo->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 ;
}
}
if ( ptEndPrec.IsValid() && bFirst)
dLenAgg += 2. * ( dLenPrev + 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 ;
bOutStart = true ;
// 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) ;
bOutStart = true ;
bFound = true ;
}
}
dLenPrev = dLen ;
}
}
else
dLenPrev = 0 ;
// vado alla successiva
pPrevCrv = pCrv ;
pCrv = ( bNextOk ? pNextCrv : nullptr) ;
bFirst = false ;
}
return bFound ;
}
//----------------------------------------------------------------------------
static bool
GetPtStartOnOpenEdgeByOrigCurve( const Point3d& ptRef, 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 ;
double dLenRef = ( 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) - 50 * EPS_SMALL ;
// se ho un punto di riferimento, cerco il lato aperto sull'originale più vicino ad essa ( sufficientemente lungo)
if ( ptRef.IsValid()) {
double dSqMinDist = INFINITO ;
int nIndCrv = -1 ;
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 - 10. * EPS_SMALL) {
// verifico la distanza con il punto di riferimento
double dCurrSqDist = 0. ;
if ( DistPointCurve( ptRef, *pCrvOpen).GetSqDist( dCurrSqDist) && dCurrSqDist < dSqMinDist) {
dSqMinDist = dCurrSqDist ;
nIndCrv = i ;
}
}
}
// se ho un indice di curva valido, allora ricavo i valori
if ( nIndCrv != -1) {
// recupero la curva
const ICurve* pCrvOpen = pCrvOrig->GetCurve( nIndCrv) ;
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
return false ;
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)) {
// versore d'uscita
vtMidOutTmp.Normalize() ;
vtMidOutTmp.Rotate( Z_AX, 0, -1) ;
ptSTmp += vtMidOutTmp * max( PockParams.dRad - PockParams.dSideStep, 0.) ;
// 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) ;
// assegno direzione fuori
vtMidOut = vtMidOutTmp ;
// flag per possibile entrata da fuori
bMidOut = true ;
}
}
}
// esco
return true ;
}
}
// se non ho un punto di riferimento valido, o avendolo non si riesce ad entrare dal lato aperto più vicino
// cerco il lato aperto più lungo ( sufficientemente lungo)
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 - 10. * EPS_SMALL) {
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)) {
// versore d'uscita
vtMidOutTmp.Normalize() ;
vtMidOutTmp.Rotate( Z_AX, 0, -1) ;
ptSTmp += vtMidOutTmp * max( PockParams.dRad - PockParams.dSideStep, 0.) ;
// 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) ;
// assegno direzione fuori
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( ptRef, 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( P_INVALID, 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, ptEndPrec, 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
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)) ;
if ( IsNull( pCrvArc) || ! pCrvArc->IsValid() || ! pCrvArc->GetLength( dArcLen) || dArcLen < 5 * EPS_SMALL)
break ;
++ 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 raccordo
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() ;
// non serve collegare ( può capitare nel tagliare percorsi con isole complesse)
if ( AreSamePointApprox( ptStart, ptEnd))
return true ;
// creo la retta che li unisce
PtrOwner<CurveComposite> pCompoLine( CreateBasicCurveComposite()) ;
if ( ! pCompoLine->AddPoint( ptStart) || ! pCompoLine->AddLine( ptEnd))
return false ;
pCompoLine->SetExtrusion( Z_AX) ;
// 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 ;
// memorizzo il tratto lineare nel caso qualche operazione fallisse
PtrOwner<CurveComposite> pCompoHelp( pCompoLine->Clone()) ;
if ( IsNull( pCompoHelp))
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()) ;
}
pCrvLink->AddCurve( Release( pCompo)) ;
return true ;
}
//------------------------------------------------------------------------------
static bool
CalcSpecialBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd,
const Vector3d& vtEnd, const PocketParams& PockParams,
ICurveComposite* pCrvLink)
{
// controllo dei parametri
if ( pCrvLink == nullptr)
return false ;
pCrvLink->Clear() ;
// parametro di estensione in tangenza
double dLenExtension = PockParams.dRad / 2. ;
// determino i punti della curva
PNTVECTOR vPnts = { ptStart, ptStart + ( dLenExtension * vtStart),
ptEnd - ( dLenExtension * vtEnd), ptEnd} ;
int nInd = 1 ;
static const double COS_TOL = cos( 130 * DEGTORAD) ;
// se primo angolo minore della tolleranza, aggiungo un punto
Vector3d vtPrev = vPnts[nInd] - vPnts[nInd-1] ; vtPrev.Normalize() ;
Vector3d vtNext = vPnts[nInd+1] - vPnts[nInd] ; vtNext.Normalize() ;
double dCos = vtPrev * vtNext ;
if ( dCos < COS_TOL) {
Point3d ptA = vPnts[nInd] + dLenExtension * GetRotate( vtStart, Z_AX, ANG_RIGHT) ;
Point3d ptB = vPnts[nInd] - dLenExtension * GetRotate( vtStart, Z_AX, ANG_RIGHT) ;
if ( SqDist( ptA, vPnts[nInd+1]) < SqDist( ptB, vPnts[nInd+1]))
vPnts.insert( vPnts.begin() + nInd + 1, ptA) ;
else
vPnts.insert( vPnts.begin() + nInd + 1, ptB) ;
++ nInd ;
}
++ nInd ;
// se secondo angolo maggiore della tolleranza, aggiungo un punto
vtPrev = vPnts[nInd] - vPnts[nInd-1] ; vtPrev.Normalize() ;
vtNext = vPnts[nInd+1] - vPnts[nInd] ; vtNext.Normalize() ;
dCos = vtPrev * vtNext ;
if ( dCos < COS_TOL) {
Point3d ptA = vPnts[nInd] + dLenExtension * GetRotate( - vtEnd, Z_AX, ANG_RIGHT) ;
Point3d ptB = vPnts[nInd] - dLenExtension * GetRotate( - vtEnd, Z_AX, ANG_RIGHT) ;
if ( SqDist( ptA, vPnts[nInd-1]) < SqDist( ptB, vPnts[nInd-1]))
vPnts.insert( vPnts.begin() + nInd + 1, ptA) ;
else
vPnts.insert( vPnts.begin() + nInd + 1, ptB) ;
}
// definisco raccordo a ZigZag ( Lineare)
PtrOwner<ICurveComposite> pZigZagLink( CreateCurveComposite()) ;
if ( IsNull( pZigZagLink))
return false ;
bool bFirst = true ;
for ( const Point3d& ptNew : vPnts) {
if ( bFirst) {
bFirst = false ;
pZigZagLink->AddPoint( ptNew) ;
}
else
pZigZagLink->AddLine( ptNew) ;
}
// questa composita contiene al massimo 5 curve e, in generale, una curva molto più lunga
// delle altre ( quella che collega le due estensioni calcolate). Spezzo la composita ottenuta in curve
// la cui lunghezza massima è circa il raggio utensile
PolyLine PL ;
pZigZagLink->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL) ;
PL.AdjustForMaxSegmentLen( PockParams.dRad + 10 * EPS_SMALL) ;
pZigZagLink->Clear() ;
pZigZagLink->FromPolyLine( PL) ;
// Smusso il raccordo a ZigZag
ModifyCurveToSmoothed( pZigZagLink, PockParams, .4, .4, true) ;
pCrvLink->CopyFrom( pZigZagLink) ;
return ( pCrvLink != nullptr && pCrvLink->IsValid() && pCrvLink->GetCurveCount() > 0) ;
}
//------------------------------------------------------------------------------
static bool
CalcBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd,
const Vector3d& vtEnd, double dParMeet, const ICRVCOMPOPOVECTOR& vCrvBorders,
const PocketParams& PockParams, ICurveComposite* pCrvLink, bool& bSpecial)
{
// controllo parametri
if ( pCrvLink == nullptr)
return false ;
pCrvLink->Clear() ;
// se senza smusso (e non caso circonferenza), ritorno tratto lineare
bSpecial = false ;
if ( ! PockParams.bSmooth && dParMeet > EPS_ZERO)
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
// inizializzo la curva di Link da restituire
PtrOwner<ICurveComposite> pMyCrvLink( CreateCurveComposite()) ;
if ( IsNull( pMyCrvLink))
return false ;
// controllo se la distanza lineare tra i due punti è maggiore della tolleranza...
const double dSqTol = 4 * PockParams.dRad * PockParams.dRad + PockParams.dSideStep * PockParams.dSideStep ;
if ( SqDist( ptStart, ptEnd) > dSqTol)
bSpecial = ( CalcSpecialBoundedSmoothedLink( ptStart, vtStart, ptEnd, vtEnd, PockParams, pMyCrvLink)) ;
if ( ! pMyCrvLink->IsValid() || pMyCrvLink->GetCurveCount() == 0) {
// ...in caso negativo creo il Biarco
double dAngStart ; vtStart.GetAngleXY( X_AX, dAngStart) ;
double dAngEnd ; vtEnd.GetAngleXY( X_AX, dAngEnd) ;
if ( dParMeet > EPS_ZERO)
pMyCrvLink.Set( ConvertCurveToComposite( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, dParMeet))) ;
else {
// ... o una circonferenza, orientata in modo da poter essere classificata con le curve di bordo
PtrOwner<CurveArc> pCrvCir( CreateBasicCurveArc()) ;
if ( IsNull( pCrvCir))
return false ;
if ( pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, PockParams.bInvert ? 360 : -360, 0, Z_AX))
pMyCrvLink.Set( ConvertCurveToComposite( Release( pCrvCir))) ;
}
}
if ( ! pMyCrvLink->IsValid() || pMyCrvLink->GetCurveCount() == 0)
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
// se il BiArco creato è troppo piccolo, lo approssimo ad un tratto lineare
BBox3d bBox3 ;
if ( pMyCrvLink->GetLocalBBox( bBox3)) {
double dRadBB ;
if ( bBox3.GetRadius( dRadBB) && dRadBB < 500 * EPS_SMALL)
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
}
// il link ottenuto non deve uscire dalle curve di bordo ( Loop esterno ed Isole)
// curva di test
PtrOwner<ICurveComposite> pCompoTest( CloneCurveComposite( pMyCrvLink)) ;
if ( IsNull( pCompoTest) || ! pCompoTest->IsValid())
return false ;
// curva ausiliaria
PtrOwner<ICurveComposite> pCompoHelp( CloneCurveComposite( pMyCrvLink)) ;
if ( IsNull( pCompoHelp) || ! pCompoHelp->IsValid())
return false ;
// scorro tutte le curve le curve di bordo
for ( const ICurveComposite* pCrvBorder : vCrvBorders) {
// calcolo eventuali intersezioni
CRVCVECTOR ccClass ;
IntersCurveCurve intCC( *pCompoTest, *pCrvBorder) ;
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
// pulisco la curva ausiliaria
pCompoHelp->Clear() ;
// analizzo le intersezioni
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
// se ho intersezione spezzo il segmento
if ( ccClass[i].nClass == CRVC_OUT && int( ccClass.size()) > 1) {
// recupero eventuali punti di intersezioni e parametri
Point3d ptS ; pCompoTest->GetPointD1D2( ccClass[i].dParS, ICurve::FROM_PLUS, ptS) ;
double dOffS ; pCrvBorder->GetParamAtPoint( ptS, dOffS, 1500 * EPS_SMALL) ;
Point3d ptE ; pCompoTest->GetPointD1D2( ccClass[i].dParE, ICurve::FROM_MINUS, ptE) ;
double dOffE ; pCrvBorder->GetParamAtPoint( ptE, dOffE, 1500 * EPS_SMALL) ;
// recupero i due possibili percorsi e uso il più corto
PtrOwner<ICurve> pCrvA( pCrvBorder->CopyParamRange( dOffS, dOffE)) ;
PtrOwner<ICurve> pCrvB( pCrvBorder->CopyParamRange( dOffE, dOffS)) ;
if ( IsNull( pCrvA) || IsNull( pCrvB))
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
double dLenA ; pCrvA->GetLength( dLenA) ;
double dLenB ; pCrvB->GetLength( dLenB) ;
// definisco il nuovo bordo mediante la curva ausiliaria
if ( i != 0) {
if ( dLenA < dLenB) {
if ( ! pCompoHelp->AddCurve( Release( pCrvA)))
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
}
else {
pCrvB->Invert() ;
if ( ! pCompoHelp->AddCurve( Release( pCrvB)))
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
}
}
else {
pCrvB->Invert() ;
if ( ! pCompoHelp->AddCurve( Release( pCrvB)))
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
}
}
// se non interseco
else {
if ( ! pCompoHelp->AddCurve( pCompoTest->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE)))
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
}
}
// la curva di test diventa la curva ausiliaria
pCompoTest->Clear() ;
if ( ! pCompoTest->AddCurve( pCompoHelp->Clone()))
if ( ! CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink))
return false ;
}
// La curva necessita di un ulteriore smusso, in quanto tagliata su un bordo
ModifyCurveToSmoothed( pCompoTest, PockParams, PockParams.dSmooth, PockParams.dSmooth, false) ;
// nel caso speciale della circonferenza, devo impostare il punto iniziale
if ( dParMeet < EPS_ZERO) {
if ( pCompoTest->IsClosed()) { // sempre...
double dU = 0. ;
int nFlag = 0 ;
if ( DistPointCurve( ptStart, *pCompoTest).GetParamAtMinDistPoint( 0., dU, nFlag))
pCompoTest->ChangeStartPoint( dU) ;
}
}
// restituisco il Link
if ( IsNull( pCompoTest) || ! pCompoTest->IsValid() || pCompoTest->GetCurveCount() == 0)
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
pCrvLink->AddCurve( Release( pCompoTest)) ;
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> pCrvFinal( CreateBasicCurveComposite()) ;
if ( IsNull( pCrvFinal))
return false ;
// recupero gli estremi delle due curve ( S = Start, E = End)
Point3d ptSS ; pCrvS->GetStartPoint( ptSS) ;
Point3d ptSE ; pCrvS->GetEndPoint( ptSE) ;
Point3d ptES ; pCrvE->GetStartPoint( ptES) ;
Point3d ptEE ; pCrvE->GetEndPoint( ptEE) ;
// recupero i versori per il BiArco
Vector3d vS ; pCrvS->GetEndDir( vS) ;
Vector3d vE ; pCrvE->GetStartDir( vE) ;
// recupero i domini delle curve
double dUSS, dUSE ; pCrvS->GetDomain( dUSS, dUSE) ;
double dUES, dUEE ; pCrvE->GetDomain( dUES, dUEE) ;
// recupero le lunghezze delle curve
double dLenS ; pCrvS->GetLength( dLenS) ;
double dLenE ; pCrvE->GetLength( dLenE) ;
// azzero i parametri per curve di primo Offset
for ( int i = 0 ; i < int( vFirstOffset.size()) ; ++ i) {
if ( vFirstOffset[i]->IsPointOn( ptSS) && vFirstOffset[i]->IsPointOn( ptSE))
dLenPercS = 0. ;
#if 0
if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE))
dLenPercE = 0. ;
#endif
}
#if 0 // nel caso volessi estendere anche prima del punto finale
double dLStepS = ( dLenPercS < EPS_SMALL ? 0. : PockParams.dRad) ;
#endif
double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : PockParams.dRad) ;
dLenE = 0 ;
// recupero il numero massimo di iterazioni per il calcolo del BiArco
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
bool bSpecial = false ;
PtrOwner<CurveComposite> pCrvBiArc( CreateBasicCurveComposite()) ;
if ( IsNull( pCrvBiArc) ||
! CalcBoundedSmoothedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, PockParams, pCrvBiArc, bSpecial) ||
pCrvBiArc->GetCurveCount() == 0)
return false ;
pCrvFinal->Clear() ;
pCrvFinal.Set( pCrvBiArc->Clone()) ;
// se non devo cercarne altri, allora tengo quest'ultimo
if ( ( dLenPercE == 0 && dLenPercS == 0) || bSpecial)
break ;
// altrimenti, calcolo un nuovo BiArco, estendendo i suoi estremi
#if 0 // nel caso volessi estendere anche prima del punto finale
dLenS -= dLStepS ;
#endif
dLenE += dLStepE ;
// ricalcolo il punto iniziale e finale
pCrvS->GetParamAtLength( dLenS, dUSE) ;
if ( ! pCrvS->GetPointD1D2( dUSE, ICurve::FROM_MINUS, ptSE, &vS))
return false ;
vS.Normalize() ;
pCrvE->GetParamAtLength( dLenE, dUES) ;
if ( ! pCrvE->GetPointD1D2( dUES, ICurve::FROM_PLUS, ptES, &vE))
return false ;
vE.Normalize() ;
++ nIter ;
}
pCrvLink->AddCurve( Release( pCrvFinal)) ; // ultimo arco valido trovato
return true ;
}
//----------------------------------------------------------
static bool
GetUnclearedRegionAndSetFeed( const ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks,
const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ISurfFlatRegion* pSfrUncleared)
{
// se non devo nè calcolare la Feed nè calcolare le regioni svuotate, allora non faccio nulla
if ( ! PockParams.bCalcFeed && ! PockParams.bCalcUnclearedRegs)
return true ;
// controllo dei parametri
if ( vFirstOffs.empty() || ssize( vOffs) < ssize( vFirstOffs))
return false ;
pSfrUncleared->Clear() ;
// creo la regione esterna ( definita dalle curve di primo Offset)
// NB. Questa regione è diversa dalla pSfrOrig :
// - i tratti chiusi sono Offsettati di R + OffsR()
// - i tratti aperti sono stati estesi all'esterno di PockParams.dMaxOpenEdgeRad
PtrOwner<ISurfFlatRegion> pSrfExtern( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfExtern))
return false ;
for ( int i = 0 ; i < ssize( vFirstOffs) ; ++ 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 ;
}
}
// creo la regione che all'esterno contiene i lati aperti
// NB. Prendendo la superficie originale ( con lati Open/Close impostati), se effettuo un
// Offset di una piccola quantità, tutte le curve esterne a tale regione sono associate a lati aperti
const double OPEN_OFFS_TOL = 1000. * EPS_SMALL ;
PtrOwner<ISurfFlatRegion> pSfrForOpenEdges( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrForOpenEdges))
return false ;
if ( pSfrOrig != nullptr && pSfrOrig->IsValid()) {
if ( ! pSfrForOpenEdges.Set( CloneSurfFlatRegion( pSfrOrig)) ||
! pSfrForOpenEdges->Offset( OPEN_OFFS_TOL, ICurve::OFF_FILLET))
return false ;
}
// definisco la regione rimossa dall'utensile
PtrOwner<ISurfFlatRegion> pSfrTool( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrTool))
return false ;
// creo un vettore che conterrà solamente i Link percorsi fino ad ora
// NB. Per SpiralOut devo scorrere le curva di Offset in maniera inversa
bool bFollowOrder = ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_CONFORMAL_ZIGZAG) ;
for ( int i = ( bFollowOrder ? 0 : ssize( vOffs) - 1) ; ( bFollowOrder ? i < ssize( vOffs) : i >= 0) ; ( bFollowOrder ? ++ i : -- i)) {
// ================== OFFSET =============================
// recupero le proprietà temporanee
int nProp0 = vOffs[i]->GetTempProp( 0) ;
int nProp1 = vOffs[i]->GetTempProp( 1) ;
// il primo TempParam viene settato in proporzione alla Feed
// il secondo TempParam contiene l'nSide della curva
double dTempPar1 = vOffs[i]->GetTempParam( 1) ;
// Feed
bool bFirstOffs = false ;
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY)
bFirstOffs = true ;
else {
Point3d ptStart ; vOffs[i]->GetStartPoint( ptStart) ;
for ( int j = 0 ; j < ssize( vFirstOffs) && ! 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, pSfrForOpenEdges, PockParams, 2. * PockParams.dRad / 3.))
return false ;
// aggiorno la superificie svuotata
PtrOwner<ISurfFlatRegion> pSrfToolOffs( GetSurfFlatRegionFromFatCurve( vOffs[i]->Clone() , PockParams.dRad + 5. * EPS_SMALL,
false, false, 10. * EPS_SMALL, false)) ;
if ( ! IsNull( pSrfToolOffs)) {
if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0)
pSfrTool.Set( pSrfToolOffs) ;
else
pSfrTool->Add( *pSrfToolOffs) ;
}
vOffs[i]->SetTempProp( nProp0, 0) ;
vOffs[i]->SetTempProp( nProp1, 1) ;
vOffs[i]->SetTempParam( dTempPar1, 1) ;
// ================= LINK ================================
int nIndLink = ( bFollowOrder ? i + 1 : i) ;
// ( Il primo link è nullo, infatti non vi è nessun raccordo per raggiungere il primo Offset)
if ( nIndLink < int( vLinks.size()) && ! IsNull( vLinks[nIndLink]) && vLinks[nIndLink]->IsValid()) {
// recupero le proprietà temporanee
int nProp0 = vLinks[nIndLink]->GetTempProp( 0) ;
int nProp1 = vLinks[nIndLink]->GetTempProp( 1) ;
// Feed
PtrOwner<ICurveComposite> pCrvLink( ConvertCurveToComposite( vLinks[nIndLink]->Clone())) ;
// calcolo gli intervalli di Feed per la curva di Link
if ( IsNull( pCrvLink) || ! pCrvLink->IsValid() ||
! AssignFeedSpiral( pCrvLink, pSfrTool, true, false, pSfrForOpenEdges, PockParams, 2. * PockParams.dRad / 3.))
return false ;
// aggiorno la regione svuotata
PtrOwner<ISurfFlatRegion> pSfrToolLink( GetSurfFlatRegionFromFatCurve( pCrvLink->Clone(), PockParams.dRad + 10. * EPS_SMALL,
false, false, 10. * EPS_SMALL, false)) ;
if ( ! IsNull( pSfrToolLink)) {
if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0)
pSfrTool.Set( pSfrToolLink) ;
else
pSfrTool->Add( *pSfrToolLink) ;
}
// Imposto il Link come Curva Composita
pCrvLink->SetTempProp( nProp0, 0) ;
pCrvLink->SetTempProp( nProp1, 1) ;
vLinks[nIndLink].Set( pCrvLink) ;
}
}
// se rischiesto, creo la regione contenente tutte le parti non svuotate
if ( PockParams.bCalcUnclearedRegs) {
pSfrUncleared->CopyFrom( pSrfExtern) ;
if ( ! pSfrUncleared->Subtract( *pSfrTool))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
ChooseCurveForRemovingUnclearedRegion( const ICRVCOMPOPOVECTOR& vOffs, const ICRVCOMPOPOVECTOR& vOffsFirstCurve,
const Point3d& ptToGo, const PocketParams& PockParams,
int& nInd, Point3d& ptCloser, bool& bFirstOffs)
{
// E' richiesto che le curve di Offset presentino le seguenti proprietà :
// - CONFORMAL
// * TempProp0 -> Offset progressivo ricavato ( 0, 1, 2, ... nMaxIter)
// * TempProp1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT)
// - SPIRAL
// * vOffsFirstCurve -> per capire se l'Offset corrente è di primo Offset o meno
// * TempParam1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT)
// controllo dei parametri
nInd = -1 ;
if ( vOffs.empty())
return true ;
// scorro tutte le curve di Offset
int nFlag ;
struct OffsPtMinDist {
int nInd = 0 ;
double dSqDist = INFINITO - 1 ;
Point3d ptMinDist = P_INVALID ;
bool bInVsOut = true ;
bool bFirstOffs = false ;
} ;
vector<OffsPtMinDist> vOffsPtMinDist( vOffs.size()) ;
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
// indice dell'Offset
vOffsPtMinDist[i].nInd = i ;
// nel caso di Conformal, escludo le curve di primo Offset dalla ricerca
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
// prima iterazione
if ( vOffs[i]->GetTempProp( 0) == 0) {
vOffsPtMinDist[i].bFirstOffs = true ;
continue ;
}
}
// Controllo se il punto da raggiungere è interno o esterno alla curva
// NB. cerco di privilegiare le curve che presentano il punto da raggiungere al loro esterno;
// in questo modo riesco a creare le curve a ricciolo
int nSide ;
DistPointCurve DistPtCrv( ptToGo, *vOffs[i]) ;
if ( DistPtCrv.GetSideAtMinDistPoint( EPS_SMALL, Z_AX, nSide)) {
// nel caso Spiral
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_SPIRALOUT) {
// InVsOut
vOffsPtMinDist[i].bInVsOut = ( int( vOffs[i]->GetTempParam( 1) != nSide)) ;
// controllo se la curva in questione è di primo Offset
Point3d ptCheck ; vOffs[i]->GetStartPoint( ptCheck) ;
for ( int j = 0 ; j < int( vOffsFirstCurve.size()) ; ++ j) {
if ( vOffsFirstCurve[j]->IsPointOn( ptCheck, 100 * EPS_SMALL)) {
vOffsPtMinDist[i].bFirstOffs = true ;
break ;
}
}
}
// nel caso Conformal
else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
if ( nSide != vOffs[i]->GetTempProp( 1))
vOffsPtMinDist[i].bInVsOut = false ;
}
// recupero il punto a minima distanza e la distanza
DistPtCrv.GetMinDistPoint( EPS_SMALL, vOffsPtMinDist[i].ptMinDist, nFlag) ;
DistPtCrv.GetSqDist( vOffsPtMinDist[i].dSqDist) ;
// se il punto da raggiungere è interno alla curva, aggiungo una penalità ( euristica)
if ( vOffsPtMinDist[i].bInVsOut) {
vOffsPtMinDist[i].dSqDist += PockParams.dRad + PockParams.dRad +
PockParams.dSideStep + PockParams.dSideStep ;
}
// se curva di bordo esterno, aggiungo un'altra penalità ( euristica)
if ( vOffsPtMinDist[i].bFirstOffs)
vOffsPtMinDist[i].dSqDist *= 2. ;
}
}
// ordino il vettore in base alle distanze calcolate
sort( vOffsPtMinDist.begin(), vOffsPtMinDist.end(), []( const OffsPtMinDist& a, const OffsPtMinDist& b) {
return a.dSqDist < b.dSqDist ;
}) ;
// recupero la curva migliore
OffsPtMinDist& BestOffs = vOffsPtMinDist[0] ;
nInd = BestOffs.nInd ;
ptCloser = BestOffs.ptMinDist ;
bFirstOffs = BestOffs.bFirstOffs ;
return true ;
}
//----------------------------------------------------------------------------
static bool
RemoveUnclearedRegions( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs,
ICRVCOMPOPOVECTOR& vOffsFirstCurve, const PocketParams& PockParams)
{
// se non devo calcolare le regioni non svuotate, esco subito
if ( ! PockParams.bCalcUnclearedRegs)
return true ;
// E' richiesto che le curve di Offset presentino le seguenti proprietà :
// - CONFORMAL
// * TempProp0 -> Offset progressivo ricavato ( 0, 1, 2, ... nMaxIter)
// * TempProp1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT)
// - SPIRAL
// * vOffsFirstCurve -> per capire se l'Offset corrente è di primo Offset o meno
// * TempParam1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT)
// 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)
// ... cerco la più vicina tra le rimanenti
// recupero la curva migliore
int nInd = -1 ;
Point3d ptCloser ;
bool bFirstOffs = false ;
if ( ! ChooseCurveForRemovingUnclearedRegion( vOffs, vOffsFirstCurve, ptCentroid, PockParams,
nInd, ptCloser, bFirstOffs))
return false ;
// se nessun indice trovato, salto il chunk
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 = false ;
// se non di primo Offset allora leggo la proprietà
if ( ! bFirstOffs)
bIsSmoothCrv = ( PockParams.bSmooth && pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ;
else
bIsSmoothCrv = ( pCrv->GetTempProp( 1) > 0) ;
// 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 ;
// numero di curve dell'offset corrente
int nCrvNumber = ( pMyOffs->GetCurveCount()) ;
// 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) {
vt1 = V_INVALID ;
vt2 = V_INVALID ;
// cerco la prima sottocurva che non sia di raccordo in precedenza
for ( int i = ( nCrv + nCrvNumber - 1) % nCrvNumber ; ( i % nCrvNumber) == i ; -- i) {
// recupero la sottocurva
const ICurve* pCrvPrec = pMyOffs->GetCurve( i) ;
if ( pCrvPrec == nullptr || ! pCrvPrec->IsValid())
break ;
// se non di raccordo
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) % nCrvNumber ;
break ;
}
}
bIsSmoothCrv = ( vt1.IsValid()) ;
if ( bIsSmoothCrv) {
// cerco la prima sottocurva che non sia di raccordo in sucessione
for ( int i = ( nCrv + nCrvNumber + 1) % nCrvNumber ; ( i % nCrvNumber) == i ; ++ i) {
// recupero la sottocurva
const ICurve* pCrvSucc = pMyOffs->GetCurve( i) ;
if ( pCrvSucc == nullptr || ! pCrvSucc->IsValid())
break ;
// se non di raccordo
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 % nCrvNumber) ;
break ;
}
}
}
bIsSmoothCrv = bIsSmoothCrv && ( vt2.IsValid()) ;
if ( bIsSmoothCrv) {
// 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) ;
bIsSmoothCrv = ( ILL.GetCrossIntersCount() == 1) ;
if ( bIsSmoothCrv) {
IntCrvCrvInfo aInfo ;
bIsSmoothCrv = ( ( ILL.GetIntCrvCrvInfo( 0, aInfo)) &&
pCrvCurl->AddPoint( pt1) &&
pCrvCurl->AddLine( aInfo.IciA[0].ptI)) ;
if ( bIsSmoothCrv)
ptMain = aInfo.IciA[0].ptI ;
}
}
}
// se non è di raccordo, allora ho già lo spigolo vivo
if ( ! bIsSmoothCrv) {
pCrvCurl->Clear() ;
// definisco il versore entrante ed uscente
// definisco il punto 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() ;
}
// recupero le due direzioni per i Biarchi
bool bSameDir = AreSameVectorEpsilon( vt1, vt2, 2 * EPS_SMALL) ;
bool bSpecial = false ;
// se parallele, allora trasformo in una circonferenza
if ( bSameDir) {
if ( ! CalcBoundedSmoothedLink( ptMain, vt1, ptCentroid, vt2, 0., vOffsFirstCurve, PockParams, pCrvCurl, bSpecial))
continue ;
Vector3d vtStartCheck ; pCrvCurl->GetStartDir( vtStartCheck) ;
if ( AreOppositeVectorApprox( vtStartCheck, vt1))
pCrvCurl->Invert() ;
}
// se distinte...
else {
// 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, bSameDir ? 0. : 0.5, vOffsFirstCurve, PockParams, pCrvBiArc1, bSpecial) ||
! CalcBoundedSmoothedLink( ptCentroid, vtDir, ptMain, vt2, bSameDir ? 0. : 0.5, vOffsFirstCurve, PockParams, pCrvBiArc2, bSpecial))
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) ;
// controllo se la regione è tutta rimossa
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 ;
int nFlag ;
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
*/
if ( bSameDir) {
pt1 = ptMain ;
pt2 = ptMain ;
}
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( ConvertCurveToComposite( pMyOffs->CopyParamRange( 0, dURef1))) ;
// aggiungo la curva a ricciolo
if ( ! pCrvTest->AddCurve( Release( pCrvCurl)))
continue ;
// aggiungo il tratto finale di Offset
pCrvTest->AddCurve( ConvertCurveToComposite( pMyOffs->CopyParamRange( dURef2, pMyOffs->GetCurveCount()))) ;
// provo a controllare che i punti inziali 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())) ;
// NB. Dato che le curve di Offset sono invertite a seconda di dove si trova il materiale,
// Utilizzo il secondo TempParam per evere l'nSide
// 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 ;
// tra le curve successive cerco la curva interna e più vicina ad essa
int nNextInd = -1 ; // indice della curva successiva
double dMinDist = INFINITO ; // distanza tra questa curva e la successiva
Point3d ptStartNext ; // punto iniziale della curva successiva
bool bMinDistAtSmooth = false ; // se punto a minima distanza su curva di smusso
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 && int( vOffs[i]->GetTempParam( 1)) == MDS_RIGHT) ||
( ccClass[0].nClass == CRVC_OUT && int( vOffs[i]->GetTempParam( 1)) == MDS_LEFT))) {
// calcolo la distanza minima tra essa
int nFlag ;
double dPar = 0. ;
Point3d ptClosest ;
DistPointCurve DistPtCrv( ptS, *vOffs[j]) ;
if ( DistPtCrv.GetParamAtMinDistPoint( EPS_SMALL, dPar, nFlag)) {
// controllo se la curva ottenuta è di raccordo, in caso positivo considero come
// punto più vicino il suo punto finale ( per avere poi successivamente Link tra
// Offset in tangenza )
const ICurve* pCrv = vOffs[j]->GetCurve( static_cast<int>( floor( dPar))) ;
if ( pCrv != nullptr && pCrv->IsValid()) {
if ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH)
pCrv->GetEndPoint( ptClosest) ;
else
DistPtCrv.GetMinDistPoint( EPS_SMALL, ptClosest, nFlag) ;
double dCurrDist = SqDist( ptS, ptClosest) ;
if ( dCurrDist < dMinDist) {
dMinDist = dCurrDist ;
nNextInd = j ;
ptStartNext = ptClosest ;
bMinDistAtSmooth = ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ;
}
}
}
}
}
// 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 ;
double dPar = 0. ;
Point3d ptClosest ;
DistPointCurve DistPtCrv( ptS, *vOffs[j]) ;
if ( DistPtCrv.GetParamAtMinDistPoint( EPS_SMALL, dPar, nFlag)) {
const ICurve* pCrv = vOffs[j]->GetCurve( static_cast<int>( floor( dPar))) ;
if ( pCrv != nullptr && pCrv->IsValid()) {
if ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH)
pCrv->GetEndPoint( ptClosest) ;
else
DistPtCrv.GetMinDistPoint( EPS_SMALL, ptClosest, nFlag) ;
double dCurrDist = SqDist( ptS, ptClosest) ;
if ( dCurrDist < dMinDist) {
dMinDist = dCurrDist ;
nNextInd = j ;
ptStartNext = ptClosest ;
bMinDistAtSmooth = ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ;
}
}
}
}
}
// 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) ;
// se ho più di una curva di Offset
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 ;
double dLenPercS = ( i == 0 ? 0. : 10 * EPS_SMALL) ;
double dLenPercE = ( bMinDistAtSmooth ? 0. : 10 * EPS_SMALL) ;
if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsFirstCurve, PockParams, dLenPercS, dLenPercE, pCrvLink) ||
! pCrvLink->IsValid()) {
// se non sono riuscito, cerco una strada più semplice ripristinando le curve
pCrvLink->Clear() ;
vOffs[i].Set( pCrv_i) ;
vOffs[i+1].Set( pCrv_ii) ;
// recupero i vettori tangenti 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
bool bSpecial = false ;
if ( CalcBoundedSmoothedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink, bSpecial))
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->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 ISurfFlatRegion* pSfrAct, const ICurveComposite* pCrvOffs,
double dOffs, double dOffsPrec, 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 ;
// se spiral out non vanno eliminati parti di percorso
if ( PockParams.nType == POCKET_SPIRALOUT)
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 ;
// controllo se l'Offset rimuove materiale
if ( dMaxOffs + ( dOffs - dOffsPrec) - PockParams.dRad < EPS_SMALL) {
bInsert = false ;
return true ;
}
// controllo se l'Offset rimuove una quantità minima di materiale
else if ( dMaxOffs + ( dOffs - dOffsPrec) - PockParams.dRad < TOL_REMOVE_OFFSET) {
// inizializzo la curva di bordo della regione non svuotata
PtrOwner<ICurveComposite> pCrvBorder( CreateCurveComposite()) ;
if ( IsNull( pCrvBorder))
return false ;
// recupero la regione non svuotata e calcolo la curva di bordo
PtrOwner<ISurfFlatRegion> pSfrA( pSfrAct->CreateOffsetSurf( - dOffsPrec - PockParams.dRad, ICurve::OFF_FILLET)) ;
if ( IsNull( pSfrA))
return false ;
if ( AreOppositeVectorApprox( pSfrA->GetNormVersor(), Z_AX))
pSfrA->Invert() ;
for ( int nC = 0 ; nC < pSfrA->GetChunkCount() ; ++ nC) {
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfrA->CloneChunk( nC)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
return false ;
PtrOwner<ISurfFlatRegion> pSfrB( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrB) || ! pSfrB->AddExtLoop( pCrvOffs->Clone()))
return false ;
if ( AreOppositeVectorApprox( pSfrB->GetNormVersor(), Z_AX))
pSfrB->Invert() ;
if ( pSfrB->Intersect( *pSfrChunk) && pSfrB->IsValid()) {
if ( ! pCrvBorder.Set( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))))
return false ;
break ;
}
}
if ( pCrvBorder->IsValid()) {
// controllo se l'utensile posizionato nel centroide rimuove la regione
Point3d ptCentroid ; pCrvBorder->GetCentroid( ptCentroid) ;
PtrOwner<ICurveArc> pCrvArc( CreateCurveArc()) ;
if ( IsNull( pCrvArc))
return false ;
pCrvArc->Set( ptCentroid, Z_AX, PockParams.dRad - 10 * EPS_SMALL) ;
IntersCurveCurve ICC( *pCrvBorder, *pCrvArc) ;
CRVCVECTOR ccClass ;
bInsert = ( ! ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) ||
int( ccClass.size()) != 1 || ccClass[0].nClass != CRVC_IN) ;
}
}
}
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) ;
}
}
// controllo che la curva abbia esattamente due tratti omogenei
bOk = ( int( vpCrvs.size()) == 2) ;
if ( ! bOk)
vpCrvs.clear() ;
return true ;
}
//----------------------------------------------------------------------------
static int
CalcInversionForSpiralOffset( const ISurfFlatRegion* pSfrChunk)
{
// controllo validità della regione
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
return -1 ;
// Caso 0 : Non esistono isole
// Caso 1 : Loop Esterno Chiuso e, se esiste, Isola Chiusa
// Caso 2 : Loop Esterno Chiuso e, se esiste, Isola Aperta
// Caso 3 : Loop Esterno Aperto e, se esiste, Isola Chiusa
// Caso 4 : Loop Esterno Aperto e, se esiste, Isola Aperta
// se non esistono isole, allora Caso 0
if ( pSfrChunk->GetLoopCount( 0) == 1)
return 0 ;
// Il Loop esterno è considerato chiuso <=> esiste almeno una curva chiusa
bool bExtClosed = false ;
for ( int nU = 0 ; nU < pSfrChunk->GetLoopCurveCount( 0, 0) && ! bExtClosed ; ++ nU) {
int nTempProp = TEMP_PROP_INVALID ;
bExtClosed = ( pSfrChunk->GetCurveTempProp( 0, 0, nU, nTempProp) &&
nTempProp == TEMP_PROP_CLOSE_EDGE) ;
}
// Le isole vengono classificate come tutte chiuse <=> esiste almeno un isola chiusa
bool bIntClosed = false ;
for ( int nLoop = 1 ; nLoop < pSfrChunk->GetLoopCount( 0) && ! bIntClosed ; ++ nLoop) {
for ( int nU = 0 ; nU < pSfrChunk->GetLoopCurveCount( 0, nLoop) && ! bIntClosed ; ++ nU) {
int nTempProp = TEMP_PROP_INVALID ;
bIntClosed = ( pSfrChunk->GetCurveTempProp( 0, nLoop, nU, nTempProp) &&
nTempProp == TEMP_PROP_CLOSE_EDGE) ;
}
}
// restituisco il caso corrente
if ( bExtClosed && bIntClosed)
return 1 ;
if ( bExtClosed && ! bIntClosed)
return 2 ;
if ( ! bExtClosed && bIntClosed)
return 3 ;
if ( ! bExtClosed && ! bIntClosed)
return 4 ;
return -1 ;
}
//----------------------------------------------------------------------------
static bool
AddEpicycles( const PocketParams& PockParam, ICurveComposite* pCompo, ICurveComposite* pCrv, ICurveComposite* pCrvBound)
{
// verifico che la curva sia valida
if ( pCompo == nullptr || ! pCompo->IsValid())
return false ;
// calcolo l'Offset definito dal valore dell'raggio dell'epiciclo
OffsetCurve OffsCrv ;
if ( ! OffsCrv.Make( pCompo, PockParam.dEpicyclesRad, ICurve::OFF_FILLET) || OffsCrv.GetCurveCount() > 1)
return false ;
PtrOwner<ICurveComposite> pCrvOffs( GetCurveComposite( OffsCrv.GetCurve())) ;
if ( IsNull( pCrvOffs))
return false ;
// verifico se devo resitituire la curva offsettata
if ( pCrvBound != nullptr)
pCrvBound->AddCurve( pCrvOffs->Clone()) ;
pCrv->Clear() ;
double dParPrec = 0. ;
for ( int i = 0 ; i < pCompo->GetCurveCount() ; ++ i) {
// calcolo distanza epicili specifica per quel tratto
double dLen ;
pCompo->GetCurve( i)->GetLength( dLen) ;
int nStep = max( 1, static_cast<int>( ceil( ( dLen) / PockParam.dEpicyclesDist))) ;
double dStep = 1.0 / nStep ;
for ( int k = 1 ; k <= nStep ; ++ k) {
// creo epiciclo
Point3d ptCen ;
Vector3d vtDir ;
pCompo->GetCurve( i)->GetPointD1D2( k * dStep, ICurve::FROM_MINUS, ptCen, &vtDir) ;
vtDir.Normalize() ;
vtDir.Rotate( Z_AX, - 90.) ;
Point3d pt = ptCen + vtDir * PockParam.dEpicyclesRad ;
PtrOwner<ICurveArc> pCrvArc( CreateCurveArc()) ;
pCrvArc->Set( ptCen, Z_AX, PockParam.dEpicyclesRad) ;
double dU ;
pCrvArc->GetParamAtPoint( pt, dU) ;
pCrvArc->ChangeStartPoint( dU) ;
// aggiungo tratto della curva offsettata
double dPar ;
pCrvOffs->GetParamAtPoint( pt, dPar) ;
bool bAdd = ( pCrv->AddCurve( pCrvOffs->CopyParamRange( dParPrec, dPar))) ;
// aggiungo epiciclo
if ( ! pCrv->AddCurve( Release( pCrvArc))) {
// se fallisco nell'aggiungere l'epiciclo tento nuovamente spostandolo di EPS_SMALL
if ( bAdd)
PtrOwner<ICurve> pCrvErased( pCrv->RemoveFirstOrLastCurve( true)) ;
k -- ;
dStep -= EPS_SMALL ;
if ( dStep < EPS_SMALL)
return false ;
}
else
dParPrec = dPar ;
}
}
// se necessario ripristino orientamento originale
if ( PockParam.bInvert)
pCrv->Invert() ;
return true ;
}
//------------------------------------------------------------------------------
static bool
CalcBoundedPolishingLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, const Vector3d& vtEnd,
const ICurve* pCrvBound, ICurveComposite* pCrvLink)
{
double dAngStart, dAngEnd ;
vtStart.GetAngleXY( X_AX, dAngStart) ;
vtEnd.GetAngleXY( X_AX, dAngEnd) ;
PtrOwner<ICurve> pBiArcLink( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, 0.5)) ;
if ( IsNull( pBiArcLink))
return false ;
// verifico se esce dalla svuotatura
CRVCVECTOR ccClass ;
IntersCurveCurve intCC( *pBiArcLink, *pCrvBound) ;
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
// se nessuno o un solo tratto e interno, il biarco è il collegamento
if ( ccClass.empty() || ( ssize( ccClass) == 1 && ccClass[0].nClass == CRVC_IN)) {
pCrvLink->AddCurve( Release( pBiArcLink)) ;
}
// altrimenti creo un percorso con biarchi e opportuni tratti della curva di contenimento
else {
PtrOwner<ICurveComposite> pCompo( CreateCurveComposite()) ;
if ( IsNull( pCompo))
return false ;
double dPar1, dPar2 ;
Point3d ptMinDist1, ptMinDist2 ;
Vector3d vtDir1, vtDir2 ;
double dAng1, dAng2 ;
int nFlag ;
DistPointCurve distPtSCrv( ptStart, *pCrvBound) ;
distPtSCrv.GetParamAtMinDistPoint( 0, dPar1, nFlag) ;
pCrvBound->GetPointTang( dPar1, ICurve::FROM_MINUS, ptMinDist1, vtDir1) ;
vtDir1.GetAngleXY( X_AX, dAng1) ;
DistPointCurve distPtECrv( ptEnd, *pCrvBound) ;
distPtECrv.GetParamAtMinDistPoint( 0, dPar2, nFlag) ;
pCrvBound->GetPointTang( dPar2, ICurve::FROM_MINUS, ptMinDist2, vtDir2) ;
vtDir2.GetAngleXY( X_AX, dAng2) ;
pCompo->AddCurve( GetBiArc( ptStart, -dAngStart, ptMinDist1, -dAng1, 0.5)) ; // primo biarco
pCompo->AddCurve( pCrvBound->CopyParamRange( dPar1, dPar2)) ; // tratto di pCrvBound
pCompo->AddCurve( GetBiArc( ptMinDist2, -dAng2, ptEnd, -dAngEnd, 0.5)) ; // secondo biarco
pCrvLink->AddCurve( Release( pCompo)) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
ComputePolishingPath( const PocketParams& PockParam, ICRVCOMPOPOVECTOR& vOffs, ICurveComposite* pMCrv)
{
// controllo dei parametri
if ( vOffs.empty() || pMCrv == nullptr)
return false ;
pMCrv->Clear() ;
// Offset con epicicli
ICRVCOMPOPOVECTOR vpCrvsEp ; vpCrvsEp.reserve( vOffs.size()) ;
// definisco la curva di bordo
PtrOwner<ICurveComposite> pCrvBound( CreateCurveComposite()) ;
if ( IsNull( pCrvBound))
return false ;
// recupero il punto iniziale
Point3d ptStartRef ;
GetPtStartOnGenericEdge( vOffs[0], PockParam, ptStartRef) ;
// scorro le curve di Offset
for ( int i = 0 ; i < ssize( vOffs) ; ++ i) {
PtrOwner<ICurveComposite> pCrvEp( CreateCurveComposite()) ;
if ( IsNull( pCrvEp))
return false ;
double dUStart = 0. ;
int nFlag = 0 ;
DistPointCurve( ptStartRef, *vOffs[i]).GetParamAtMinDistPoint( 0., dUStart, nFlag) ;
vOffs[i]->ChangeStartPoint( dUStart) ;
// la curva di bound è l'offset che calcolo in AddEpicycles per la prima curva compo trovata in pMCrv
if ( ! AddEpicycles( PockParam, vOffs[i], pCrvEp, ( i == 0 ? pCrvBound : nullptr)))
return false ;
vpCrvsEp.emplace_back( Release( pCrvEp)) ;
}
// calcolo i collegamenti
ICURVEPOVECTOR vLinks( vpCrvsEp.size()) ;
for ( int i = 1 ; i < ssize( vpCrvsEp) ; ++ i) {
// punti e direzioni di inizio e fine
Point3d ptStart ; Vector3d vtStart ;
vpCrvsEp[i-1]->GetEndPoint( ptStart) ;
vpCrvsEp[i-1]->GetEndDir( vtStart) ;
Point3d ptEnd ; Vector3d vtEnd ;
vpCrvsEp[i]->GetStartPoint( ptEnd) ;
vpCrvsEp[i]->GetStartDir( vtEnd) ;
// calcolo il collegamento con biarchi (garantendo che non esca dalla svuotatura)
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
if ( IsNull( pCrvLink))
return false ;
if ( ! CalcBoundedPolishingLink( ptStart, vtStart, ptEnd, vtEnd, pCrvBound, pCrvLink))
return false ;
vLinks[i].Set( pCrvLink) ;
}
// creo il percorso di lavoro a partire dalla raccolta delle curve con epicicli e dei collegamenti
for ( int i = 0 ; i < int( vpCrvsEp.size()) ; ++ i) {
// se collegamento da aggiungere
if ( ! IsNull( vLinks[i])) {
// accodo nel percorso di lavorazione
pMCrv->AddCurve( Release( vLinks[i])) ;
}
// aggiungo la curva
pMCrv->AddCurve( Release( vpCrvsEp[i])) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CalcSpiral( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, int& nReg, Point3d& ptStart,
ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops, ICurveComposite* pMCrv, 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 ;
// se lucidatura
if ( PockParams.bPolishing && nReg == 0)
dOffs += PockParams.dEpicyclesRad ;
// 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
// tengo una copia della regione corrente da svuotare
PtrOwner<ISurfFlatRegion> pSrfAct( CloneSurfFlatRegion( pSfrPock)) ;
if ( IsNull( pSrfAct) || pSrfAct->GetChunkCount() == 0)
return false ;
// ricavo il tipo di relazione descritta tra Open/Close di bordo esterno e/o isole
int nCase = CalcInversionForSpiralOffset( pSrfAct) ;
if ( nCase == -1)
return false ;
// ricavo le regioni progressive
double dOffsPrec = 0. ;
int nCrvFirstOffs = 0 ;
bool bLastNotValid = false ;
while ( nIter < MAX_ITER) {
// Offset della regione attuale
PtrOwner<ISurfFlatRegion> pSfrOffsVR( pSrfAct->CreateOffsetSurf( - dOffs, ICurve::OFF_FILLET)) ;
if ( IsNull( pSfrOffsVR))
return false ;
// se la regione sparisce allora riprovo con un Offset leggermente più piccolo
if ( ! pSfrOffsVR->IsValid()) {
pSfrOffsVR.Set( pSrfAct->CreateOffsetSurf( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) ;
if ( IsNull( pSfrOffsVR))
return false ;
bLastNotValid = true ;
}
// se primo Offset
if ( nIter == 0) {
// aggiorno il nuovo valore delle regioni totali di primo Offset
int my_nReg = nReg ;
nReg = pSfrOffsVR->GetChunkCount() ;
// gli Offset progressivi appartengono al Chunk nReg-esimo
pSrfAct.Set( pSfrOffsVR->CloneChunk( my_nReg)) ;
// se supero i chunk ottenuti
if ( IsNull( pSrfAct))
return true ;
// imposto la regione id svuotatura corrente
pSfrOffsVR.Set( pSrfAct->Clone()) ;
}
// numero di Chunk e Loops 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 ;
// controllo quali regioni di Offset possono essere sostituite
bool bInsert = true ;
if ( ! CheckIfOffsetIsNecessary( pSrfAct, pCrvCompoBorder, dOffs, dOffsPrec, nIter, PockParams, bInsert))
return false ;
// per evitare di allacciare una curva di regione non svuotata alla prima curva di Offset
// ( quindi avere un entrata nel pieno del grezzo) controllo di non eliminare il secondo Offset
if ( ! bInsert && nChunks == 1 && bLastNotValid)
bInsert = true ;
if ( bInsert) {
// imposto come secondo TempParam il Side di classificazione
pCrvCompoBorder->SetTempParam( j == 0 ? MDS_RIGHT : MDS_LEFT, 1) ;
// stabilisco l'orientamento della curva
if ( ( nCase == 1 && nIter != 0 && j > 0) ||
( nCase == 2 && j > 0) ||
( nCase == 3 && j == 0) ||
( nCase == 4 && j > 0)) {
pCrvCompoBorder->Invert() ;
SwapSideBySecondTempParam( pCrvCompoBorder) ;
}
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)) ;
}
}
}
// ricavo il numero di curve ottenute di primo Offset
if ( nIter == 0)
nCrvFirstOffs = int( vOffs.size()) ;
// 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 non ho Chunks e devo usare un Offset più piccolo...
else if ( ! bSmallRad) {
if ( PockParams.dRad < EPS_SMALL || PockParams.bPolishing)
break ;
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 ;
// se lucidatura
if ( PockParams.bPolishing && nReg == 1) {
// si suppone che gli offset siano tutti concentrici ( già ordinati dall'esterno all'interno)
bMidOut = false ; // per definizione di lucidatura
return ( ComputePolishingPath( PockParams, vOffs, pMCrv)) ;
}
// 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 ;
if ( PockParams.nType == POCKET_SPIRALIN) {
if ( SetAdvancedPtStartForPath( vOffsFirstCurve, PockParams, pSfrPock, ptRef, ptStart,
vtMidOut, bMidOut, nIndexSwap, vCrvOrigChunkLoops)) {
vOffsFirstCurve[nIndexSwap]->GetStartPoint( ptNewStart) ;
}
else
return false ;
}
else {
if ( SetPtStartForPath( vOffs[0], PockParams, pSfrPock, ptRef, ptStart, vtMidOut, bMidOut,
vCrvOrigChunkLoops[0]))
vOffs[0]->GetStartPoint( ptNewStart) ;
else
return false ;
}
// se richiesta inversione
if ( PockParams.bInvert) {
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
vOffs[i]->Invert() ;
SwapSideBySecondTempParam( vOffs[i]) ;
}
}
// smusso le curve di offset ( ad eccezione di quelle di primo Offset)
double dSmoothPar = PockParams.dSmooth / SQRT2 ;
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
if ( i >= nCrvFirstOffs)
ModifyCurveToSmoothed( vOffs[i], PockParams, dSmoothPar, dSmoothPar, false) ;
vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
}
// setto il punto iniziale della svuotatura
double dNewUS ;
if ( nIndexSwap != 0)
swap( vOffs[0], vOffs[nIndexSwap]) ;
vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ;
vOffs[0]->ChangeStartPoint( dNewUS) ;
vOffs[0]->SetTempParam( 0., 0) ; // prima iterazione
// riordino le curve e creo i collegamenti
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, pSfrOrig, PockParams, pSfrUncleared)) {
// modifico i percorsi
if ( ! RemoveUnclearedRegions( 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) {
// se interno
if ( ccClass[j].nClass == CRVC_IN) {
// recupero il tratto
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) ;
// se troppo piccolo, lo scarto
if ( SqDist( ptS, ptE) < 250 * 250 * SQ_EPS_SMALL)
continue ;
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 ;
// 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, pSfrOrig, PockParams, nRegTot, ptStart, vCrvOrigChunkLoops, pMCrv, bSomeOpen, vtMidOut))
return false ;
// se terminate le regioni, esco
if ( pMCrv->GetCurveCount() == 0)
break ; // passo al chunk originale successivo
bool bIsExtended = false ;
if ( bSomeOpen)
ExtendPath( pMCrv, pSfrOrig, PockParams, vtMidOut, false, GetExtendPathLen( PockParams), 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 ;
// 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, pSfrOrig, PockParams, nRegTot, ptStart, vCrvOrig, pMCrv, bSomeOpen, vtMidOut))
return false ;
// se terminate le regioni, esco
if ( pMCrv->GetCurveCount() == 0)
break ; // passo al chunk originale successivo
// 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) ;
}
// 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 Loop nL-esimo, passo al successivo
if ( vClosedOffs_nC.empty())
continue ;
// concateno eventuali percorsi
if ( ! ChainCompoCurves( vClosedOffs_nC))
return false ;
// miglioro le curve
for ( auto& crv : vClosedOffs_nC)
crv->MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG) ;
// determino ora il punto di inizio, il tratto per il LeadIn
bool bIsChunkClosed = false ;
bool bIsChunkAllOpen = false ;
if ( ! IsChunkAllHomogeneous( pSfrPock, nC, bIsChunkClosed, bIsChunkAllOpen))
return false ;
if ( ! AdvanceExtendCurves( vClosedOffs_nC, pSfrOrig, PockParams, true))
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 && ! PockParams.bPolishing)
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 ;
bool bIsChunkAllOpen = true ;
if ( ! PockParams.bAllClosed && ! PockParams.bPolishing) {
if ( ! IsChunkAllHomogeneous( pSrfPock, nChunkInd, bIsChunkClosed, bIsChunkAllOpen))
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, GetExtendPathLen( PockParams), bIsExtended))
return false ;
vpCrvs[nU]->GetEndDir( vtRef) ;
if ( ! ExtendPath( vpCrvs[nU], pSfrOrig, PockParams, vtRef, true, GetExtendPathLen( PockParams), bIsExtended))
return false ;
}
}
// se lucidatura
if ( PockParams.bPolishing) {
// ciclo sui percorsi
for ( int k = 0 ; k < int( vpCrvs.size()) ; ++ k) {
// se attacco a scivolo
if ( PockParams.nLiType == LEAD_IN_GLIDE) {
double dU ;
vpCrvs[k]->GetParamAtLength( PockParams.dLiTang, dU) ;
vpCrvs[k]->AddJoint( dU) ;
Point3d ptStart ;
vpCrvs[k]->GetStartPoint( ptStart) ;
vpCrvs[k]->ModifyStart( ptStart + Z_AX * PockParams.dLiElev) ;
}
// se uscita a scivolo
if ( PockParams.nLoType == LEAD_OUT_GLIDE) {
double dLen, dU ;
vpCrvs[k]->GetLength( dLen) ;
vpCrvs[k]->GetParamAtLength( dLen - PockParams.dLoTang, dU) ;
vpCrvs[k]->AddJoint( dU) ;
Point3d ptEnd ;
vpCrvs[k]->GetEndPoint( ptEnd) ;
vpCrvs[k]->ModifyEnd( ptEnd + Z_AX * PockParams.dLiElev) ;
}
}
}
// 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 && ! PockParams.bPolishing) {
// 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) ;
// estendo presso lati aperti se richiesto
if ( ! PockParams.bAllClosed) {
Vector3d vtRef ; pCrvSegCompo->GetStartDir( vtRef) ;
vtRef.Invert() ;
bool bIsExtended = false ;
if ( ! ExtendPath( pCrvSegCompo, pSfrOrig, PockParams, vtRef, false, GetExtendPathLen( PockParams), bIsExtended))
return false ;
pCrvSegCompo->GetEndDir( vtRef) ;
if ( ! ExtendPath( pCrvSegCompo, pSfrOrig, PockParams, vtRef, true, GetExtendPathLen( PockParams), bIsExtended))
return false ;
}
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 ;
}
//----------------------------------------------------------------------------
static bool
SmoothLinkByOffs( ICurveComposite* pCrvOffs0, ICurveComposite* pCrvOffs1, ICurveComposite* pCrvLink,
const PocketParams& PockParams, bool bFirstOffs0, bool bFirstOffs1, double dSmoothPar, double dTol)
{
// controllo dei parametri
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid() ||
pCrvLink == nullptr || ! pCrvLink->IsValid())
return false ;
// definisco la lunghezza del segmento iniziale e finale sulle due curve di Offset
double dRefLenSeg = 2. * dSmoothPar ;
// --- Curva precedente ---
// recupero la lunghezza e il dominio del primo Offset
double dLen ;
pCrvOffs0->GetLength( dLen) ;
double dUStart, dUEnd ;
pCrvOffs0->GetDomain( dUStart, dUEnd) ;
// inizializzo la curva
PtrOwner<ICurveComposite> pCrvBef( CreateCurveComposite()) ;
if ( IsNull( pCrvBef))
return false ;
// se primo Offset di bordo o chiuso...
if ( bFirstOffs0 && pCrvOffs0->IsClosed()) {
double dU = dUEnd ;
if ( dLen > dRefLenSeg - EPS_SMALL)
pCrvOffs0->GetParamAtLength( dRefLenSeg, dU) ;
PtrOwner<ICurve> pMyCrv( pCrvOffs0->CopyParamRange( dUStart, dU)) ;
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
return false ;
pCrvBef->AddCurve( Release( pMyCrv)) ;
}
// altrimenti...
else {
double dU = dUStart ;
if ( dLen > dRefLenSeg)
pCrvOffs0->GetParamAtLength( dLen - dRefLenSeg, dU) ;
PtrOwner<ICurve> pMyCrv( pCrvOffs0->CopyParamRange( dU, dUEnd)) ;
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
return false ;
pCrvBef->AddCurve( Release( pMyCrv)) ;
}
// se non valida, errore
if ( ! pCrvBef->IsValid())
return false ;
// --- Curva successiva ---
// recupero la lunghezza e il dominio del primo Offset
pCrvOffs1->GetLength( dLen) ;
pCrvOffs1->GetDomain( dUStart, dUEnd) ;
// inizializzo la curva
PtrOwner<ICurveComposite> pCrvAft( CreateCurveComposite()) ;
if ( IsNull( pCrvAft))
return false ;
// calcolo il parametro di taglio
double dU = dUEnd ;
if ( dLen > dRefLenSeg - EPS_SMALL)
pCrvOffs1->GetParamAtLength( dRefLenSeg, dU) ;
PtrOwner<ICurve> pMyCrv( pCrvOffs1->CopyParamRange( dUStart, dU)) ;
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
return false ;
pCrvAft->AddCurve( Release( pMyCrv)) ;
// se non valida, errore
if ( ! pCrvAft->IsValid())
return false ;
// estendo il Link ( linea di ingresso, segmento Link, linea d'uscita)
pCrvLink->AddCurve( Release( pCrvBef), false, dTol) ;
pCrvLink->AddCurve( Release( pCrvAft), true, dTol) ;
// miglioro la curva
pCrvLink->MergeCurves( 100 * EPS_SMALL, ANG_TOL_STD_DEG) ;
// smusso la curva
ModifyCurveToSmoothed( pCrvLink, PockParams, dSmoothPar, dSmoothPar, false) ;
// toglo gli estremi, il Link è in tangenza e le linee di ingresso e d'uscita sono più
// estese dell'arco di smusso creato
delete( pCrvLink->RemoveFirstOrLastCurve( false)) ;
delete( pCrvLink->RemoveFirstOrLastCurve( true)) ;
// modifico il primo Offset per raccordarlo al nuovo Link
if ( bFirstOffs0 && pCrvOffs0->IsClosed()) {
const ICurve* pCrvFirst = pCrvLink->GetFirstCurve() ;
if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid())
return false ;
PtrOwner<ICurve> pMyCrv( pCrvFirst->Clone()) ;
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
return false ;
pCrvOffs0->AddCurve( Release( pMyCrv)) ;
pCrvLink->RemoveFirstOrLastCurve( false) ;
}
else {
Point3d ptS ; pCrvLink->GetStartPoint( ptS) ;
double dUE ; pCrvOffs0->GetParamAtPoint( ptS, dUE, dTol) ;
pCrvOffs0->TrimEndAtParam( dUE) ;
}
// modifico il secondo Offset per raccordarlo al nuovo Link
Point3d ptE ; pCrvLink->GetEndPoint( ptE) ;
double dUS ; pCrvOffs1->GetParamAtPoint( ptE, dUS, dTol) ;
if ( bFirstOffs1 && pCrvOffs1->IsClosed())
pCrvOffs1->ChangeStartPoint( dUS) ;
else
pCrvOffs1->TrimStartAtParam( dUS) ;
return true ;
}
static bool
GetConformalLinkForOpenCrv( const ICurveComposite* pCrvOffs0, const ICurveComposite* pCrvOffs1,
const ICRVCOMPOPOVECTOR& vCrvClassBorder, const PocketParams& PockParams,
double dTol, ICurveComposite* pCrvLink)
{
// controllo dei parametri
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid())
return false ;
pCrvLink->Clear() ;
// ricavo gli estremi del Link
Point3d ptLinkS ; pCrvOffs0->GetEndPoint( ptLinkS) ;
Point3d ptLinkE ; pCrvOffs1->GetStartPoint( ptLinkE) ;
// ricavo i parametri sulla curva di bordo che interessa i due estremi ( 1 sola !)
for ( int i = 0 ; i < int( vCrvClassBorder.size()) ; ++ i) {
if ( vCrvClassBorder[i]->IsPointOn( ptLinkS, dTol) &&
vCrvClassBorder[i]->IsPointOn( ptLinkE, dTol)) {
double dPar0 ; vCrvClassBorder[i]->GetParamAtPoint( ptLinkS, dPar0, dTol) ;
double dPar1 ; vCrvClassBorder[i]->GetParamAtPoint( ptLinkE, dPar1, dTol) ;
// ricavo le due curve possibili di Link
PtrOwner<ICurveComposite> pCrvLinkA( ConvertCurveToComposite( vCrvClassBorder[i]->CopyParamRange( dPar0, dPar1))) ;
PtrOwner<ICurveComposite> pCrvLinkB( ConvertCurveToComposite( vCrvClassBorder[i]->CopyParamRange( dPar1, dPar0))) ;
// scelgo quella migliore
double dLenA = 0. ;
if ( ! IsNull( pCrvLinkA) && pCrvLinkA->IsValid())
pCrvLinkA->GetLength( dLenA) ;
double dLenB = 0. ;
if ( ! IsNull( pCrvLinkB) && pCrvLinkB->IsValid()) {
pCrvLinkB->Invert() ;
pCrvLinkB->GetLength( dLenB) ;
}
if ( dLenA > dLenB) {
if ( ! IsNull( pCrvLinkB) && pCrvLinkB->IsValid())
pCrvLink->CopyFrom( pCrvLinkB) ;
}
else {
if ( ! IsNull( pCrvLinkA) && pCrvLinkA->IsValid())
pCrvLink->CopyFrom( pCrvLinkA) ;
}
break ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CalcConformalLink( ICurveComposite* pCrvOffs0, ICurveComposite* pCrvOffs1, PocketParams& PockParams,
const ICRVCOMPOPOVECTOR& vCrvClassBorder, ICurveComposite* pCrvLink)
{
// controllo validità delle due curve di Offset
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid())
return false ;
pCrvLink->Clear() ;
const double TOL = 150 * EPS_SMALL ;
// flag per curve di Offset aperte o chiuse
bool bOpen0 = ( ! pCrvOffs0->IsClosed()) ;
bool bOpen1 = ( ! pCrvOffs1->IsClosed()) ;
// se passo da una curva aperta ad un'altra curva aperta ( uso i bordi di pSfrClass)
if ( bOpen0 && bOpen1) {
// ricavo la curva di Link
if ( ! GetConformalLinkForOpenCrv( pCrvOffs0, pCrvOffs1, vCrvClassBorder, PockParams, TOL, pCrvLink))
return false ;
// smusso il Link raccordandolo
if ( pCrvLink->IsValid())
SmoothLinkByOffs( pCrvOffs0, pCrvOffs1, pCrvLink, PockParams, false, false, PockParams.dRad / 8., TOL) ;
}
else {
// clono le due curve di Offset
PtrOwner<ICurveComposite> pCrvOffs0_cl( CloneCurveComposite( pCrvOffs0)) ;
PtrOwner<ICurveComposite> pCrvOffs1_cl( CloneCurveComposite( pCrvOffs1)) ;
if ( IsNull( pCrvOffs0_cl) || IsNull( pCrvOffs1_cl) || ! pCrvOffs0_cl->IsValid() || ! pCrvOffs1_cl->IsValid())
return false ;
// controllo se gli Offset sono stati ricavati alla prima iterazione
bool bFirstIterOffs0 = ( pCrvOffs0->GetTempProp( 0) == 0) ;
bool bFirstIterOffs1 = ( pCrvOffs1->GetTempProp( 0) == 0) ;
// definisco nuovo parametro di smusso
bool bSmooth = PockParams.bSmooth ;
PockParams.bSmooth = ( PockParams.nType == POCKET_CONFORMAL_ONEWAY && bSmooth) ;
// cerco il chunk ipotetico che contiene le due curve di Offset
INTVECTOR vIndOffs0, vIndOffs1 ;
if ( ! GetIndOfHypoteticalChunk( vCrvClassBorder, pCrvOffs0, pCrvOffs1, vIndOffs0, vIndOffs1))
return false ;
// se appartengono a due chunk diversi, allora il link non esiste
if ( int( vIndOffs0.size()) != int( vIndOffs1.size()))
return true ;
for ( int i = 0 ; i < int( vIndOffs0.size()) ; ++ i) {
if ( vIndOffs0[i] != vIndOffs1[i])
return true ;
}
// se vuoti, allora il link non esiste
if ( vIndOffs0.empty() || vIndOffs1.empty()) // &&
return true ;
// se appartenenti allo stesso chunk, allora creo link lineare
ICRVCOMPOPOVECTOR vCrvChunk ;
for ( int i = 0 ; i < int( vIndOffs0.size()) ; ++ i)
vCrvChunk.emplace_back( vCrvClassBorder[vIndOffs0[i]]->Clone()) ;
// se la curva successiva è chiusa, aggiorno il suo punto iniziale a quello a minima distanza
if ( ! bOpen1) {
Point3d ptE ; pCrvOffs0->GetEndPoint( ptE) ;
double dPar ;
int nFlag ;
if ( DistPointCurve( ptE, *pCrvOffs1).GetParamAtMinDistPoint( 0., dPar, nFlag))
pCrvOffs1->ChangeStartPoint( dPar) ;
}
if ( ! CutCurveToConnect( pCrvOffs0, pCrvOffs1, vCrvChunk, PockParams,
bOpen0 ? 0. : 10 * EPS_SMALL, bOpen1 ? 0. : 10 * EPS_SMALL, pCrvLink) ||
! pCrvLink->IsValid()) {
// se curva di Link non valida, cerco una strada più semplice
pCrvLink->Clear() ;
pCrvOffs0->CopyFrom( pCrvOffs0_cl) ;
pCrvOffs1->CopyFrom( pCrvOffs1_cl) ;
// recupero i vettori tangente iniziali ( le curve sono chiuse )
Vector3d vtS, vtE ;
Point3d ptS, ptE ;
if ( ! pCrvOffs0->GetStartDir( vtS) || ! pCrvOffs1->GetStartDir( vtE) ||
! pCrvOffs0->GetStartPoint( ptS) || ! pCrvOffs1->GetStartPoint( ptE))
return false ;
// creo il bi-arco tra esse
bool bSpecial = false ;
if ( ! CalcBoundedSmoothedLink( ptS, vtS, ptE, vtE, 0.5, vCrvChunk, PockParams, pCrvLink, bSpecial) ||
! pCrvLink->IsValid())
return false ;
}
PockParams.bSmooth = bSmooth ;
// nel caso di Conformal ZigZag
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) {
// effettuo smusso del tratto lineare
SmoothLinkByOffs( pCrvOffs0, pCrvOffs1, pCrvLink, PockParams, bFirstIterOffs0, bFirstIterOffs1,
PockParams.dSmooth, TOL) ;
// per sicurezza aggiorno i nuovi punti e i nuovi parametri
double dUNewE ;
Point3d ptStartNext ;
pCrvLink->GetEndPoint( ptStartNext) ;
pCrvOffs1->GetParamAtPoint( ptStartNext, dUNewE) ;
pCrvOffs1->ChangeStartPoint( dUNewE) ;
}
// nel caso Conformal OneWay, taglio e unisco le curve ricavate
else if ( PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
// per sicurezza aggiorno i nuovi punti e i nuovi parametri
double dUNewS, dUNewE ;
Point3d ptS, ptStartNext ;
if ( ! pCrvLink->GetStartPoint( ptS) ||
! pCrvLink->GetEndPoint( ptStartNext) ||
! pCrvOffs0->GetParamAtPoint( ptS, dUNewS) ||
! pCrvOffs1->GetParamAtPoint( ptStartNext, dUNewE))
return false ;
// imposto il punto iniziale della curva successiva ( i+1 esima)
pCrvOffs1->ChangeStartPoint( dUNewE) ;
PtrOwner<ICurveComposite> pCrvNewOffs( CloneCurveComposite( pCrvOffs0)) ;
if ( dUNewS > EPS_SMALL) {
pCrvNewOffs.Set( ConvertCurveToComposite( pCrvOffs0->CopyParamRange( 0, dUNewS))) ;
// sostituisco la curva i esima con quella tagliata
pCrvOffs0->CopyFrom( pCrvNewOffs) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CheckConformalRetractLink( const ICurveComposite* pCrvOffs0, const ICurveComposite* pCrvOffs1,
const ICRVCOMPOPOVECTOR& vCrvClassBorder, const PocketParams& PockParams,
bool& bCalcLink)
{
// controllo dei parametri
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid())
return false ;
bCalcLink = true ; // di base calcolo il Link
// recupero gli estremi di un possibile collegamento tra i punti
Point3d ptA ; pCrvOffs0->GetEndPoint( ptA) ;
Point3d ptB ; pCrvOffs1->GetStartPoint( ptB) ;
// se vicini -> no retroazione
if ( SqDist( ptA, ptB) < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL)
return true ;
// se distanti
else {
// controllo se sono aperte
bool bOpen_i = ( ! pCrvOffs0->IsClosed()) ;
bool bOpen_ii = ( ! pCrvOffs1->IsClosed()) ;
// se entrambe aperte
if ( bOpen_i && bOpen_ii) {
// calcolo il collegamento tra le due curve
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
if ( IsNull( pCrvLink) ||
! GetConformalLinkForOpenCrv( pCrvOffs0, pCrvOffs1, vCrvClassBorder, PockParams,
25 * EPS_SMALL, pCrvLink))
return false ;
// se Link valido calcolo la sua lunghezza
double dLinkLen = 0. ;
if ( pCrvLink->IsValid())
pCrvLink->GetLength( dLinkLen) ;
// minima distanza da ptA
DistPointCurve DistPtACrv( ptA, *pCrvOffs1) ;
double dMyDist ;
if ( DistPtACrv.GetSqDist( dMyDist) &&
dMyDist < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL) {
bCalcLink = ( dLinkLen < 4 * PockParams.dRad) ;
return true ;
}
// minima distanza da ptB
DistPointCurve DistPtBCrv( ptB, *pCrvOffs0) ;
if ( DistPtBCrv.GetSqDist( dMyDist) &&
dMyDist < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL) {
bCalcLink = ( dLinkLen < 4 * PockParams.dRad) ;
return true ;
}
}
// ... servono altri controlli ??? ( vedere dai casi)
}
bCalcLink = false ;
return true ;
}
//----------------------------------------------------------------------------
static bool
GetConformalIndices( const VICRVCOMPOPOVECTOR& vCrvOffs, int nMyInd0, int nMyInd1,
const PocketParams& PockParams, const ICRVCOMPOPOVECTOR& vCrvClassBorder,
int& nInd0, int& nInd1)
{
// controllo che l'indice sia sensato
if ( vCrvOffs.empty() || nMyInd0 < 0 || nMyInd0 >= int( vCrvOffs.size()) ||
( nMyInd1 < 0 || nMyInd1 >= int( vCrvOffs[nMyInd0].size())))
return false ;
nInd0 = -1 ;
nInd1 = -1 ;
// il punto di riferimento è il punto finale della curva corrente
Point3d ptRef ; vCrvOffs[nMyInd0][nMyInd1]->GetEndPoint( ptRef) ;
// controllo se la curva è chiusa o aperta
bool bIsClosed = ( vCrvOffs[nMyInd0][nMyInd1]->IsClosed()) ;
// controllo se la curva è di primo Offset ( in questo caso non ho curve successive)
bool bFistOffs = ( nMyInd0 == int( vCrvOffs.size() - 1)) ;
// scorro le curve di indice successivo
double dLimInfDist = INFINITO ;
int nInd = -1 ;
for ( int i = 0 ; ! bFistOffs && i < int( vCrvOffs[nMyInd0 + 1].size()) ; ++ i) {
// se non attiva, passo alla successiva
if ( vCrvOffs[nMyInd0 + 1][i]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE)
continue ;
// se di topologia differente, passo alla successiva
if ( ( bIsClosed != vCrvOffs[nMyInd0 + 1][i]->IsClosed()))
continue ;
// se richiede retroazione, passo alla successiva
bool bCalcLink = true ;
if ( ! CheckConformalRetractLink( vCrvOffs[nMyInd0][nMyInd1], vCrvOffs[nMyInd0 + 1][i],
vCrvClassBorder, PockParams, bCalcLink))
return false ;
if ( ! bCalcLink)
continue ;
// se aperta
if ( ! bIsClosed) {
Point3d ptStart ; vCrvOffs[nMyInd0 + 1][i]->GetStartPoint( ptStart) ;
double dDist = SqDist( ptRef, ptStart) ;
if ( dDist < dLimInfDist) {
dLimInfDist = dDist ;
nInd = i ;
}
}
// se chiusa
else {
DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nMyInd0 + 1][i]) ;
MinDistPCInfo aInfo ;
if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) {
double dDist = SqDist( ptRef, aInfo.ptQ) ;
if ( dDist < dLimInfDist) {
dLimInfDist = dDist ;
nInd = i ;
vCrvOffs[nMyInd0 + 1][i]->ChangeStartPoint( aInfo.dPar) ;
}
}
}
}
// se curva trovata, restituisco gli indici
if ( nInd != -1) {
nInd0 = nMyInd0 + 1 ;
nInd1 = nInd ;
return true ;
}
/* se non ho trovato alcuna curva, allora possono verificarsi le seguenti situazioni :
- all'indice successivo ho solo curve di topologia differente
- ho già percorso tutte le curve topologicamente uguali all'indice successivo
- sono una curva di primo Offset
- tutte le curve all'indice succesivo di topologia equivalente richiedono una retroazione
=> devo azzerare nInd0 e ricercare la curva di medesima topologia valida più vicina
*/
nInd0 = 0 ;
nInd = -1 ;
for ( ; nInd0 < int( vCrvOffs.size()) ; ++ nInd0) {
nInd1 = 0 ;
dLimInfDist = INFINITO ;
for ( ; nInd1 < int( vCrvOffs[nInd0].size()) ; ++ nInd1) {
// se non attiva, passo alla successiva
if ( vCrvOffs[nInd0][nInd1]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE)
continue ;
// se di topologia differente, passo alla successiva
if ( ( bIsClosed != vCrvOffs[nInd0][nInd1]->IsClosed()))
continue ;
// se aperta
if ( ! bIsClosed) {
Point3d ptStart ; vCrvOffs[nInd0][nInd1]->GetStartPoint( ptStart) ;
double dDist = SqDist( ptRef, ptStart) ;
if ( dDist < dLimInfDist) {
dLimInfDist = dDist ;
nInd = nInd1 ;
}
}
// se chiusa
else {
DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nInd0][nInd1]) ;
MinDistPCInfo aInfo ;
if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) {
double dDist = SqDist( ptRef, aInfo.ptQ) ;
if ( dDist < dLimInfDist) {
dLimInfDist = dDist ;
nInd = nInd1 ;
vCrvOffs[nInd0][nInd1]->ChangeStartPoint( aInfo.dPar) ;
}
}
}
}
// se curva trovata, restituisco gli indici
if ( nInd != -1) {
nInd1 = nInd ;
return true ;
}
}
/* se non ho trovato alcuna curva, allora possono verificarsi le seguenti situazioni :
- ho solo curve topologicamente differenti dalla mia
- non ho curve attive
=> devo azzerare nInd0 e ricercare la curva valida più vicina
*/
nInd0 = 0 ;
dLimInfDist = INFINITO ;
nInd = -1 ;
for ( ; nInd0 < int( vCrvOffs.size()) ; ++ nInd0) {
nInd1 = 0 ;
for ( ; nInd1 < int( vCrvOffs[nInd0].size()) ; ++ nInd1) {
// se non attiva, passo alla successiva
if ( vCrvOffs[nInd0][nInd1]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE)
continue ;
// se la curva successiva è aperta
if ( ! vCrvOffs[nInd0][nInd1]->IsClosed()) {
Point3d ptStart ; vCrvOffs[nInd0][nInd1]->GetStartPoint( ptStart) ;
double dDist = SqDist( ptRef, ptStart) ;
if ( dDist < dLimInfDist) {
dLimInfDist = dDist ;
nInd = nInd1 ;
}
}
// se la curva successiva è chiusa
else {
DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nInd0][nInd1]) ;
MinDistPCInfo aInfo ;
if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) {
double dDist = SqDist( ptRef, aInfo.ptQ) ;
if ( dDist < dLimInfDist) {
dLimInfDist = dDist ;
nInd = nInd1 ;
vCrvOffs[nInd0][nInd1]->ChangeStartPoint( aInfo.dPar) ;
}
}
}
}
// se curva trovata, restituisco gli indici
if ( nInd != -1) {
nInd1 = nInd ;
return true ;
}
}
// non ho più curve disponibili
nInd0 = -1 ;
nInd1 = -1 ;
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustCloseEdgesForConformalGuide( ICurveComposite* pCrvCompo, const PocketParams& PockParams)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// se non ho una regione limite, allora non faccio nulla
if ( ! PockParams.SfrLimit.IsValid())
return true ;
// piccolo Offset per la superficie di classificazione
PtrOwner<ISurfFlatRegion> pSfrLimit( CloneSurfFlatRegion( &PockParams.SfrLimit)) ;
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid() ||
! pSfrLimit->Offset( 150 * EPS_SMALL, ICurve::OFF_FILLET))
return false ;
// recupero le parti omogenee della curva
ICRVCOMPOPOVECTOR vpCrvs ;
GetHomogeneousParts( pCrvCompo, PockParams, vpCrvs) ;
// i tratti aperti che fanno overlap con la regione limite diventano chiusi
PtrOwner<ICurveComposite> pCrvFinal( CreateCurveComposite()) ;
if ( IsNull( pCrvFinal))
return false ;
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
// nuovo curva
PtrOwner<ICurveComposite> pMyCompo( CreateCurveComposite()) ;
if ( IsNull( pMyCompo))
return false ;
CRVCVECTOR ccClass ;
if ( pSfrLimit->GetCurveClassification( *vpCrvs[i], EPS_SMALL, ccClass)) {
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
// recupero il tratto di curva
PtrOwner<ICurveComposite> pMyCurve( ConvertCurveToComposite( vpCrvs[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
if ( ! IsNull( pMyCurve) && pMyCurve->IsValid()) {
// se non esterna alla regione limite, il sottotratto deve essere chiuso
if ( ccClass[j].nClass != CRVC_OUT) {
for ( int k = 0 ; k < pMyCurve->GetCurveCount() ; ++ k)
pMyCurve->SetCurveTempProp( k, TEMP_PROP_CLOSE_EDGE, 0) ;
}
if ( ! pMyCompo->AddCurve( Release( pMyCurve)))
return true ; // non faccio nulla...
}
}
}
vpCrvs[i].Set( pMyCompo) ;
}
if ( ! pCrvFinal->AddCurve( Release( vpCrvs[i])))
return true ; // non faccio nulla...
}
if ( pCrvFinal->IsValid())
pCrvCompo->CopyFrom( pCrvFinal) ;
return ( pCrvCompo != nullptr && pCrvCompo->IsValid()) ;
}
//----------------------------------------------------------------------------
static bool
ModifyConformalStartPoint( ICurveComposite* pCrvFirst, const PocketParams& PockParams,
const ISurfFlatRegion* pSfrPock)
{
// controllo dei parametri
if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid() ||
pSfrPock == nullptr || ! pSfrPock->IsValid())
return false ;
// se la curva è aperta, allora non devo fare nulla
if ( ! pCrvFirst->IsClosed())
return true ;
// ricavo i lati aperti della superficie di classificazione
ICRVCOMPOPOVECTOR vCrvOpenEdges ;
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) {
// recupero il Loop
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ;
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
return false ;
// recupero le parti omogenee
ICRVCOMPOPOVECTOR vpCrvs ;
GetHomogeneousParts( pCrvLoop, PockParams, vpCrvs) ;
// recupero le parti interne
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE)
vCrvOpenEdges.emplace_back( Release( vpCrvs[i])) ;
}
}
}
// se non ho ricavato tratti aperti, errore
if ( vCrvOpenEdges.empty())
return false ;
/* NB.
- Per i casi previsti dalle lavorazioni CONFORMAL devo trovare solo 1 tratto aperto, quindi
considero quello di indice 0
- Tra la curva pCrvFirst e la curva di lato aperto, devo trovare i punti più vicino
( per ora l'algoritmo è euristico, bisogna aggiornarlo con VORONOI)
*/
// Algoritmo Euristico da sostituire con Voronoi :
// 1) Cerco il punto più vicino al lato aperto dato il punto iniziale di pCrvFirst
Point3d ptStart ; pCrvFirst->GetStartPoint( ptStart) ;
DistPointCurve distCalculator0( ptStart, *vCrvOpenEdges[0]) ;
Point3d ptMinDist ;
int nFlag ;
if ( ! distCalculator0.GetMinDistPoint( 0., ptMinDist, nFlag))
return false ;
// 2) Cerco il punto più vicino su pCrvFirst da ptMinDist
DistPointCurve distCalculator1( ptMinDist, *pCrvFirst) ;
double dMinPar ;
if ( ! distCalculator1.GetParamAtMinDistPoint( 0., dMinPar, nFlag))
return false ;
// 3) Cambio il punto iniziale di pCrvFirst nel parametro ricavato
pCrvFirst->ChangeStartPoint( dMinPar) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
CalcConformalOffsAndLinks( VICRVCOMPOPOVECTOR& vvCrvOffs, const ISurfFlatRegion* pSfrChunk,
const ISurfFlatRegion* pSfrClass, PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvOffs,
ICURVEPOVECTOR& vCrvLink)
{
// controllo dei parametri
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() ||
pSfrClass == nullptr || ! pSfrClass->IsValid())
return false ;
vCrvOffs.clear() ;
vCrvLink.clear() ;
if ( vvCrvOffs.empty())
return true ;
// rendo attive tutte le curve di Offset mediante prima TmpProp
for ( int i = 0 ; i < int( vvCrvOffs.size()) ; ++ i) {
for ( int j = 0 ; j < int( vvCrvOffs[i].size()) ; ++ j)
vvCrvOffs[i][j]->SetTempProp( TEMP_PROP_CURVE_ACTIVE, 0) ;
}
/* NB. curve di vCrvOffs
TempProp0 -> Se l'offset è attivo/non attivo
TempProp1 -> Side {MDS_LEFT, MDS_RIGHT} per identificare l'interno della curva
TempParam0 -> { vuoto } ; verrà utilizzato per la Feed
TempParam1 -> { vuoto }
*/
// inverto il vettore degli Offset, in modo da partire dalle curve più distanti dai chiusi
reverse( vvCrvOffs.begin(), vvCrvOffs.end()) ;
// determino il punto iniziale della prima curva di Offset
if ( ! ModifyConformalStartPoint( vvCrvOffs[0][0], PockParams, pSfrChunk))
return false ;
/* NB.
I link che collegano due curve di Offset aperte devono partire dalla prima e, seguendo il
bordo della regione di classificazione ( pSfrClass) arrivare alla seconda.
*/
/* NB.
La regione di classificazione ( pSfrClass) deve tenere conto della regione limite; nel caso di
bordi chiusi, bisogna stare a distanza opportuna da essi, non raccordandoci i Links
*/
/* NB.
I link che collegano due curve di Offset chiuse tra di loro vengono calcolati esattamente
come per i percorsi SPIRAL, pertanto devo definire un insieme di curve di primo Offset sui
quali smussare i Link per evitare di uscire dalla regione di svuotatura
*/
ICRVCOMPOPOVECTOR vCrvSfrClass ;
PtrOwner<ISurfFlatRegion> pSfrClassReal( CloneSurfFlatRegion( pSfrClass)) ;
if ( IsNull( pSfrClassReal) || ! pSfrClassReal->IsValid())
return false ;
if ( PockParams.SfrLimit.IsValid()) {
PtrOwner<ISurfFlatRegion> pSfrLimit( CloneSurfFlatRegion( &PockParams.SfrLimit)) ;
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid() ||
! pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET) ||
! pSfrClassReal->Subtract( *pSfrLimit))
return false ;
}
if ( ! GetSfrCrvCompoLoops( pSfrClassReal, vCrvSfrClass))
return false ;
/* NB.
Se POCKET_CONFORMAL_ZIGZAG :
- vCrvCompoOffs contiene gli Offset ordinati
- vCrvLinks contiene il link che collega l'Offset (i-1)-esimo con l'Offset (i)-esimo
[ per definizione il primo Link è null]
[ se tra due Offset non c'è Link, allora esso sarà null]
Se POCKET_CONFORMAL_ONEWAY
- vCrvCompoOffs contiene gli Offset ordinati
- vCrvLinks è un vettore di null
vCrvCompoOffs e vCrvLinks avranno la stessa dimensione
*/
// ------------ Ordino Gli Offset ------------
int nInd0 = 0 ;
int nInd1 = 0 ;
while ( nInd0 != -1 && nInd1 != -1) {
// inserisco la curva attuale nel vettore degli Offset
vCrvOffs.emplace_back( CloneCurveComposite( vvCrvOffs[nInd0][nInd1])) ;
// salvo come prima TmpProp il numero di Offset che ha generato tale curva
vCrvOffs.back()->SetTempProp( int( vvCrvOffs.size()) - nInd0 - 1, 0) ;
// disattivo tale curva per la ricerca
vvCrvOffs[nInd0][nInd1]->SetTempProp( TEMP_PROP_CURVE_INACTIVE, 0) ;
// cerco la curva successiva per il percorso
int nNextInd0 = -1 ;
int nNextInd1 = -1 ;
if ( ! GetConformalIndices( vvCrvOffs, nInd0, nInd1, PockParams, vCrvSfrClass, nNextInd0, nNextInd1))
return false ;
// aggiorno gli indici
nInd0 = nNextInd0 ;
nInd1 = nNextInd1 ;
}
// se non ho ottenuto nulla, esco
if ( vCrvOffs.empty())
return true ;
// ------------ Definizione dei Link ------------
// NB. Il link i-esimo collega l'Offset (i-1)-esimo con l'i-esimo
// per definizione il primo Link è nullo
vCrvLink.resize( 1) ;
for ( int i = 0 ; i < int( vCrvOffs.size()) - 1 ; ++ i) {
// controllo se bisogna calcolare il Link tra i due Offset
bool bCalcLink = true ;
// se Conformal ZigZag
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) {
// Criteri per creazione di un possibile collegamento
if ( ! CheckConformalRetractLink( vCrvOffs[i], vCrvOffs[i+1], vCrvSfrClass, PockParams, bCalcLink))
return false ;
}
// se Conformal OneWay
else {
// gli unici link validi per il OneWay sono quelli che collegano due curve chiuse concentriche.
// In questo caso si può generare un Biarco dato che l'orientamento degli Offset è lo stesso
bCalcLink = ( vCrvOffs[i]->IsClosed() && vCrvOffs[i+1]->IsClosed()) ;
if ( bCalcLink) {
IntersCurveCurve ICC( *vCrvOffs[i], *vCrvOffs[i+1]) ;
CRVCVECTOR ccClass ;
bCalcLink = ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) &&
int( ccClass.size()) == 1) ;
if ( bCalcLink) {
if ( vCrvOffs[i]->GetTempProp( 1) == MDS_LEFT)
bCalcLink = ( ccClass[0].nClass == CRVC_IN) ;
else if ( vCrvOffs[i]->GetTempProp( 1) == MDS_RIGHT)
bCalcLink = ( ccClass[0].nClass == CRVC_OUT) ;
else
bCalcLink = false ;
}
}
}
// se link da calcolare
if ( bCalcLink) {
// lo creo e cerco di calcolarlo
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
if ( IsNull( pCrvLink))
return false ;
// se non calcolabile, allora retroazione, altrimenti lo memorizzo
if ( ! CalcConformalLink( vCrvOffs[i], vCrvOffs[i+1], PockParams, vCrvSfrClass, pCrvLink) ||
IsNull( pCrvLink) || ! pCrvLink->IsValid() || pCrvLink->GetCurveCount() == 0)
vCrvLink.resize( int( vCrvLink.size()) + 1) ;
else
vCrvLink.emplace_back( Release( pCrvLink)) ;
}
else
vCrvLink.resize( int( vCrvLink.size()) + 1) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
ExtendConformalOffsAndSetFeed( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvOffs, ICURVEPOVECTOR& vCrvLinks)
{
// controllo dei parametri
if ( pSfrPock == nullptr || ! pSfrPock->IsValid())
return false ;
if ( vCrvOffs.empty())
return true ;
// definisco la regione di ricerca delle aree non svuotate come la regione originale
PtrOwner<ISurfFlatRegion> pSfrBorder( CloneSurfFlatRegion( pSfrPock)) ;
if ( IsNull( pSfrBorder) || ! pSfrBorder->IsValid())
return false ;
// dalla superficie originale, rimuovo le parti definite dai lati chiusi
double dOffs = PockParams.dRad + PockParams.dRadialOffset - 2. * EPS_SMALL ;
ICRVCOMPOPOVECTOR vpCrvs ;
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) {
PtrOwner<ICurveComposite> pCompoLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ;
if ( IsNull( pCompoLoop) || ! pCompoLoop->IsValid())
return false ;
PtrOwner<ISurfFlatRegion> pSfrToDiscard( GetSurfFlatRegionFromFatCurve( pCompoLoop->Clone(), dOffs, false, false)) ;
if ( IsNull( pSfrToDiscard) || ! pSfrToDiscard->IsValid())
return false ;
if ( ! pSfrBorder->Subtract( *pSfrToDiscard))
return false ;
}
}
// se non ho nessuna superficie di bordo, allora vuol dire che la regione è più stretta del diametro utensile
// ( capita soprattutto se ho un SideStep da rispettare presso gli aperti)
// non controllo le regioni rimosse ( dovrebbero essere rimosse dalle passate)
if ( pSfrBorder->IsValid()) {
ICRVCOMPOPOVECTOR vCrvBorder ;
if ( ! GetSfrCrvCompoLoops( pSfrBorder, vCrvBorder))
return false ;
// determino eventuale regioni con parti non svuotate e imposto la Feed alle curve
PtrOwner<ISurfFlatRegion> pSfrUncleared( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrUncleared))
return false ;
if ( GetUnclearedRegionAndSetFeed( vCrvBorder, vCrvOffs, vCrvLinks, pSfrOrig, PockParams, pSfrUncleared)) {
// estendo i percorsi di Offset se richiesto
if ( ! RemoveUnclearedRegions( pSfrUncleared, vCrvOffs, vCrvBorder, PockParams))
return false ;
}
}
else {
for ( int i = 0 ; i < ssize( vCrvOffs) ; ++ i) {
if ( ! IsNull( vCrvOffs[i]) && vCrvOffs[i]->IsValid())
AssignMaxFeed( vCrvOffs[i], PockParams) ;
}
for ( int i = 0 ; i < ssize( vCrvLinks) ; ++ i) {
if ( ! IsNull( vCrvLinks[i]) && vCrvLinks[i]->IsValid()) {
PtrOwner<ICurveComposite> pCompoLink( CreateCurveComposite()) ;
if ( IsNull( pCompoLink))
return false ;
pCompoLink->AddCurve( Release( vCrvLinks[i])) ;
AssignMaxFeed( pCompoLink, PockParams) ;
vCrvLinks[i].Set( pCompoLink) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AddLeadInLeadOutToCurveConformalPaths( const ISurfFlatRegion* pSfrOrig, const ISurfFlatRegion* pSfrPock,
const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvPaths)
{
// controllo dei parametri
if ( pSfrOrig == nullptr || ! pSfrOrig->IsValid() ||
pSfrPock == nullptr || ! pSfrPock->IsValid())
return false ;
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) {
if ( vCrvPaths[i] == nullptr || ! vCrvPaths[i]->IsValid())
return false ;
}
// ricavo le curve aperte della superficie di pocketing
ICRVCOMPOPOVECTOR vCrvLoops ;
if ( ! GetSfrCrvCompoLoops( pSfrPock, vCrvLoops))
return false ;
ICRVCOMPOPOVECTOR vCrvOpenEdge ;
for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) {
ICRVCOMPOPOVECTOR vpCrvs ;
GetHomogeneousParts( vCrvLoops[i], PockParams, vpCrvs) ;
for ( int j = 0 ; j < int( vpCrvs.size()) ; ++ j) {
if ( vpCrvs[j]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE)
vCrvOpenEdge.emplace_back( Release( vpCrvs[j])) ;
}
}
// scorro i percorsi ricavati
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) {
// recupero il punto iniziale del percorso
Point3d ptStart ; vCrvPaths[i]->GetStartPoint( ptStart) ;
// se il punto inziale è interno alla superficie originaria
bool bIsInside ;
if ( IsPointInsideSurfFr( ptStart, pSfrOrig, EPS_SMALL, bIsInside) && bIsInside) {
// definizione del segmento per l'entrata
PtrOwner<ICurveLine> pSeg( CreateCurveLine()) ;
if ( IsNull( pSeg))
return false ;
double dSqLenMin = INFINITO ;
// ricavo il punto più vicino alle curve aperte della superficie di classificazione
for ( int j = 0 ; j < int( vCrvOpenEdge.size()) ; ++ j) {
DistPointCurve DistPtCrv( ptStart, *vCrvOpenEdge[j]) ;
Point3d ptMinDist ;
int nFlag ;
// controllo se il segmento che congiunge i due estremi non rovina il grezzo
bool bSafe = ( DistPtCrv.GetMinDistPoint( 0, ptMinDist, nFlag)) ;
// se troppo piccolo, non faccio nulla
double dSqSegLen = SqDist( ptStart, ptMinDist) ;
if ( dSqSegLen < SQ_EPS_SMALL) {
pSeg.Set( CreateCurveLine()) ;
break ;
}
// creazione del segmento
if ( bSafe) {
PtrOwner<ICurveLine> pCurrSeg( CreateCurveLine()) ;
if ( IsNull( pCurrSeg) || ! pCurrSeg->Set( ptMinDist, ptStart))
return false ;
double dSqSegLen = SqDist( ptStart, ptMinDist) ;
if ( PockParams.SfrLimit.IsValid()) {
PtrOwner<ISurfFlatRegion> pSfrLimit( CloneSurfFlatRegion( &PockParams.SfrLimit)) ;
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid() ||
! pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET))
return false ;
// controllo distanza segmento
CRVCVECTOR ccClass ;
bSafe = ( pSfrLimit->GetCurveClassification( *pSeg, 25 * EPS_SMALL, ccClass) &&
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) ;
}
if ( bSafe && dSqSegLen < dSqLenMin) {
dSqLenMin = dSqSegLen ;
pSeg.Set( pCurrSeg) ;
}
}
}
// se segmento trovato, lo aggiungo alla curva
if ( pSeg->IsValid()) {
if ( ! vCrvPaths[i]->AddCurve( Release( pSeg), false, 25 * EPS_SMALL))
return false ;
vCrvPaths[i]->SetCurveTempParam( 0, GetMinFeed( PockParams), 0) ;
}
}
// estendo il percorso sul tratto iniziale e finale ( se ammissibile)
Vector3d vtTanS, vtTanE ;
vCrvPaths[i]->GetStartDir( vtTanS) ; vtTanS.Invert() ;
vCrvPaths[i]->GetEndDir( vtTanE) ;
const int MAXTRY = 8 ;
bool bOkStartExtension = false, bOkEndExtension = false ;
for ( int j = 0 ; j <= MAXTRY ; ++ j) {
if ( bOkStartExtension && bOkEndExtension)
break ;
double dDist = PockParams.dOpenMinSafe + j * ( GetExtendPathLen( PockParams) / ( 1. * MAXTRY)) ;
if ( ! bOkStartExtension) {
if ( ! ExtendPath( vCrvPaths[i], pSfrOrig, PockParams, vtTanS, false, dDist, bOkStartExtension))
return false ;
}
if ( ! bOkEndExtension) {
if ( ! ExtendPath( vCrvPaths[i], pSfrOrig, PockParams, vtTanE, true, dDist, bOkEndExtension))
return false ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
OrderAndExtendConformalPaths( ICRVCOMPOPOVECTOR& vCrvPaths, const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrOrig,
PocketParams& PockParams)
{
// controllo dei parametri
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() ||
pSfrOrig == nullptr || ! pSfrOrig->IsValid())
return false ;
// superficie di controllo per parti isolate
PtrOwner<ISurfFlatRegion> pSfrToRemove( CloneSurfFlatRegion( pSfrChunk)) ;
if ( IsNull( pSfrToRemove) || ! pSfrToRemove->IsValid() ||
! pSfrToRemove->Intersect( *pSfrOrig)) {
// riprovo con una leggera tolleranza
pSfrToRemove->Offset( - 10 * EPS_SMALL, ICurve::OFF_FILLET) ;
if ( ! pSfrToRemove->Intersect( *pSfrOrig))
return false ;
}
// creo le regioni piane di svuotatura dei percorsi
ISURFFRPOVECTOR vSfrRemoved ; vSfrRemoved.resize( vCrvPaths.size()) ;
ISURFFRPOVECTOR vSfrRemaining ; vSfrRemaining.reserve( vCrvPaths.size()) ;
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) {
// creo la regione di svuotatura di tale percorso
vSfrRemoved[i].Set( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vCrvPaths[i]), PockParams.dRad + 500 * EPS_SMALL, false, false)) ;
if ( IsNull( vSfrRemoved[i]) || ! vSfrRemoved[i]->IsValid())
return false ;
}
/* ------------------------ Ordinamento dei percorsi ------------------------ */
// scorro i percorsi ricavati
INTVECTOR vInds ; vInds.resize( vCrvPaths.size(), -1) ;
int nChunkRef = pSfrToRemove->GetChunkCount() ;
for ( int i = 0 ; i < int( vInds.size()) ; ++ i) {
int j = 0 ;
INTVECTOR vTempInds ;
for ( ; j < int( vCrvPaths.size()) ; ++ j) {
// se indice già presente
if ( find( vInds.begin(), vInds.end(), j) != vInds.end())
continue ;
// se sottraendo questa regione a quella originale ottengo più isole, allora non è il percorso ideale
PtrOwner<ISurfFlatRegion> pSfrRemain( CloneSurfFlatRegion( pSfrToRemove)) ;
if ( IsNull( pSfrRemain) || ! pSfrRemain->IsValid())
continue ;
pSfrRemain->Subtract( *vSfrRemoved[j]) ;
// se aumento il numero di Chunk, allora non è il percorso ideale
if ( pSfrRemain->GetChunkCount() > nChunkRef)
continue ;
vTempInds.push_back( j) ;
}
// 1) se non ho trovato curve candidate, prendo l'indice disponibile più basso
if ( vTempInds.empty()) {
for ( int k = 0 ; k < int( vInds.size()) ; ++ k) {
if ( find( vInds.begin(), vInds.end(), k) != vInds.end())
continue ;
vInds[i] = k ;
break ;
}
}
// 2) se la candidata è singola, allora è lei
else if ( int( vTempInds.size()) == 1)
vInds[i] = vTempInds[0] ;
// 3) se trovate più curve, allora effettuo algoritmi euristici
else {
// 3.1) cerco la curva che effettua meno overlap con la regione da limite
double dMinLen = INFINITO ;
double dMinArea = INFINITO ;
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
// privilegio le aperte
if ( vCrvPaths[vTempInds[k]]->IsClosed())
continue ;
// se la regione di incidenza è valida
if ( PockParams.SfrLimit.IsValid()) {
dMinLen = 0. ; // non mi interessa in questo caso la lunghezza della curva
PtrOwner<ISurfFlatRegion> pSfrTest( CloneSurfFlatRegion( vSfrRemoved[vTempInds[k]])) ;
if ( IsNull( pSfrTest) || ! pSfrTest->IsValid())
return false ;
pSfrTest->Intersect( PockParams.SfrLimit) ;
double dArea ; pSfrTest->GetArea( dArea) ;
if ( dArea < dMinArea) {
dMinArea = dArea ;
vInds[i] = vTempInds[k] ;
if ( dArea < EPS_SMALL)
break ; // è ottima...
}
}
// 3.2) se la regione di incidenza non è valida, scelgo la curva più corta
else {
double dLen ; vCrvPaths[vTempInds[k]]->GetLength( dLen) ;
if ( dLen < dMinLen) {
dMinLen = dLen ;
vInds[i] = vTempInds[k] ;
}
}
}
// 3.3) se ho solo curve adiacenti a regioni di incidenza, scelgo quella il cui bordo esterno
// contiene più curve di bordo esterno
if ( dMinArea > EPS_SMALL) {
struct MyCompoClass {
PtrOwner<ICurveComposite> pCrvExtLoop ;
int nIndex ;
int nBorders ;
} ;
vector<MyCompoClass> vCrvExtBorder( vTempInds.size() ) ;
for ( int j = 0 ; j < int( vCrvExtBorder.size()) ; ++ j) {
// recupero il bordo esterno (mi auguro sia un solo chunk...)
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( vSfrRemoved[vTempInds[j]]->GetLoop( 0, 0))) ;
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() ||
! vCrvExtBorder[j].pCrvExtLoop.Set( pCrvExtLoop))
return false ;
vCrvExtBorder[j].nIndex = vTempInds[j] ;
vCrvExtBorder[j].nBorders = 0 ;
}
for ( int j = 0 ; j < int( vCrvExtBorder.size()) - 1 ; ++ j) {
for ( int k = j + 1 ; k < int( vCrvExtBorder.size()) ; ++ k) {
IntersCurveCurve ICC( *vCrvExtBorder[j].pCrvExtLoop, *vCrvExtBorder[k].pCrvExtLoop) ;
CRVCVECTOR ccClass ;
if ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) &&
int( ccClass.size()) == 1) {
// se i-esima interna a j-esima
if ( ccClass[0].nClass == CRVC_IN)
++ vCrvExtBorder[k].nBorders ;
else if ( ccClass[0].nClass == CRVC_OUT)
++ vCrvExtBorder[j].nBorders ;
}
}
}
// ordino il vettore delle curve sulla base di quante ne contiene
sort( vCrvExtBorder.begin(), vCrvExtBorder.end(),
[] ( const MyCompoClass& a, const MyCompoClass& b) {
return a.nBorders > b.nBorders ;
}) ;
// recupero l'indice migliore
if ( ! vCrvExtBorder.empty())
vInds[i] = vCrvExtBorder[0].nIndex ;
}
// cerco solo tra le chiuse se non ho aperte
if ( dMinLen > INFINITO - 1) {
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
if ( vCrvPaths[vTempInds[k]]->IsClosed()) {
double dLen ; vCrvPaths[vTempInds[k]]->GetLength( dLen) ;
if ( dLen < dMinLen) {
dMinLen = dLen ;
vInds[i] = vTempInds[k] ;
}
}
}
}
}
// salvo la regione rimanente ( è già in posizione corretta nel vettore)
vSfrRemaining.emplace_back( CloneSurfFlatRegion( pSfrToRemove)) ;
// aggiorno la regione da svuotare e il numero di Chunk che si sono formati
pSfrToRemove->Subtract( *vSfrRemoved[vInds[i]]) ;
nChunkRef = pSfrToRemove->GetChunkCount() ;
}
/* ------------------------ Estensione dei percorsi ------------------------ */
// ordino percorsi e superfici progressive per estendendere le curve presso gli aperti progressivi
ICRVCOMPOPOVECTOR vCrvOrderedPaths ; vCrvOrderedPaths.resize( vCrvPaths.size()) ;
ISURFFRPOVECTOR vSfrRemovedOrdered ; vSfrRemovedOrdered.resize( vCrvPaths.size()) ;
for ( int i = 0 ; i < int( vInds.size()) ; ++ i) {
// ordino la superficie rimossa dal percorso attuale
vSfrRemovedOrdered[i].Set( vSfrRemoved[vInds[i]]) ;
// ordino il percorso
vCrvOrderedPaths[i].Set( vCrvPaths[vInds[i]]) ;
}
// controllo che i percorsi effettivamente rimuovano del materiale
/*
NB. Nel caso di lati aperti, può capitare che si formino delle piccole curve ( estese poi
per il LeadIn/LeadOut ) che siano interne ad eventuali estensioni per i lati aperti ma che
di fatto non svuotano materiale effettivo ( questo accade, perchè gli Offset non sono come
per il caso degli Spiral fatti a partire dai bordi, ma sono calcolati tagliando gli offset
dei lati chiusi su una superficie estesa di 1.05 * ( dRad + GetOffsR() )
*/
int nMax = vCrvOrderedPaths.size() ;
vCrvPaths.clear() ;
int i = 0 ;
for ( ; i < nMax ; ++ i) {
// se percorso non valido, passo al successivo
if ( IsNull( vCrvOrderedPaths[i]) || ! vCrvOrderedPaths[i]->IsValid())
continue ;
// se con il percorso i-esimo ho già rimosso tutta la superficie, allora ho finito
if ( IsNull( vSfrRemaining[i]) || ! vSfrRemaining[i]->IsValid())
break ;
// se superficie rimossa dal percorso attuale non valida, errore
if ( IsNull( vSfrRemovedOrdered[i]) || ! vSfrRemovedOrdered[i]->IsValid())
return false ;
// controllo se la superficie rimossa ( + 500 * EPS_SMALL ) interseca la regione da svuotare
if ( ! vSfrRemovedOrdered[i]->Intersect( *vSfrRemaining[i]) || IsNull( vSfrRemovedOrdered[i]))
return false ;
// se valida
if ( vSfrRemovedOrdered[i]->IsValid() && vSfrRemovedOrdered[i]->GetChunkCount() > 0) {
// il percorso rimuove materiale, aggiungo LeadIn e LeadOut
ICRVCOMPOPOVECTOR vCurrPath ; vCurrPath.resize( 1) ;
vCurrPath[0].Set( vCrvOrderedPaths[i]) ;
AddLeadInLeadOutToCurveConformalPaths( vSfrRemaining[i], pSfrChunk, PockParams, vCurrPath) ;
vCrvPaths.emplace_back( Release( vCurrPath[0])) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
ChainConformalOffsWithLinks( ICRVCOMPOPOVECTOR& vCrvOffs, ICURVEPOVECTOR& vCrvLink,
ICRVCOMPOPOVECTOR& vCrvPaths)
{
// se non ho Offset non devo concatenare nulla
if ( vCrvOffs.empty())
return true ;
// le curve di Offset devono essere definite e valide
for ( int i = 0 ; i < int( vCrvOffs.size()) ; ++ i) {
if ( vCrvOffs[i] == nullptr || ! vCrvOffs[i]->IsValid())
return false ;
}
vCrvPaths.clear() ;
// percorso corrente
PtrOwner<ICurveComposite> pCrvPath( CreateCurveComposite()) ;
if ( IsNull( pCrvPath))
return false ;
for ( int i = 0 ; i < int( vCrvOffs.size()) - 1 ; ++ i) {
// aggiungo l'Offset corrente
if ( ! pCrvPath->AddCurve( Release( vCrvOffs[i])))
return false ;
// se il Link per l'Offset successivo è valido, lo aggiungo
if ( ! IsNull( vCrvLink[i+1]) && vCrvLink[i+1]->IsValid()) {
if ( ! pCrvPath->AddCurve( Release( vCrvLink[i+1]))) {
vCrvPaths.emplace_back( Release( pCrvPath)) ;
pCrvPath.Set( CreateCurveComposite()) ;
if ( IsNull( pCrvPath))
return false ;
}
}
// se Link non valido, ho terminato il percorso corrente
else {
vCrvPaths.emplace_back( Release( pCrvPath)) ;
pCrvPath.Set( CreateCurveComposite()) ;
if ( IsNull( pCrvPath))
return false ;
}
}
if ( ! pCrvPath->AddCurve( Release( vCrvOffs.back())))
return false ;
vCrvPaths.emplace_back( Release( pCrvPath)) ;
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 = 5000. ;
// 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)) {
Vector3d vtDir = ptPoly - ptEndGuide ;
vtDir.Normalize() ;
if ( ! pSeg->Set( ptEndGuide, ptEndGuide + vtDir * 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
CalcConformalGuide( const ICurveComposite* pCrvCloseEdge, const ICurveComposite* pCrvOpenEdge,
const PocketParams& PockParams, ICurveComposite* pCrvGuide)
{
// controllo dei parametri
if ( pCrvCloseEdge == nullptr || ! pCrvCloseEdge->IsValid() ||
pCrvOpenEdge == nullptr || ! pCrvOpenEdge->IsValid())
return false ;
pCrvGuide->Clear() ;
// creo una copia temporanea delle curve
PtrOwner<ICurveComposite> pCrvCL( CloneCurveComposite( pCrvCloseEdge)) ;
PtrOwner<ICurveComposite> pCrvOP( CloneCurveComposite( pCrvOpenEdge)) ;
if ( IsNull( pCrvCL) || IsNull( pCrvOP) || ! pCrvCL->IsValid() || ! pCrvOP->IsValid())
return false ;
// estendo la curva nel suo tratto finale ed iniziale
for ( int i = 0 ; i < 2 ; ++ i) {
if ( ! ExtendGuideByIteration( pCrvCL, pCrvOP, ( i == 1)))
return false ;
if ( ! pCrvCL->IsValid())
return true ;
pCrvCL->Invert() ;
pCrvOP->Invert() ;
// se la curva diventa chiusa, allora ho la guida
if ( pCrvCL->IsClosed()) {
pCrvGuide->CopyFrom( pCrvCL) ;
if ( i == 1)
pCrvGuide->Invert() ;
return true ;
}
}
// tratto iniziale
Vector3d vtStart ; pCrvCL->GetStartDir( vtStart) ;
Point3d ptStart ; pCrvCL->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 ; pCrvCL->GetEndDir( vtEnd) ;
Point3d ptEnd ; pCrvCL->GetEndPoint( ptEnd) ;
PtrOwner<ICurveLine> pLineEnd( CreateCurveLine()) ;
if ( IsNull( pLineEnd) || ! pLineEnd->Set( ptEnd, ptEnd + 1000. * vtEnd))
return false ;
// controllo eventuale intersezione
IntersCurveCurve ICC( *pLineStart, *pLineEnd) ;
if ( ICC.GetIntersCount() > 0) {
// se esiste allora spezzo sia il tratto iniziale che finale nel punto di intersezione
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; rischierei di non
// riuscire a passare presso dei lati aperti vicini ai tratti lineari di estensione
IntersCurveCurve ICCLS( *pLineStart, *pCrvOP) ;
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, *pCrvOP) ;
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) ;
}
}
}
// aggiungo i tratti lineari di estensione della curva
pCrvCL->AddCurve( Release( pLineStart), false) ;
pCrvCL->AddCurve( Release( pLineEnd), true) ;
// recupero la curva guida
pCrvGuide->CopyFrom( pCrvCL) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
CombineClosedEdgesForConformalOffsets( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParam,
bool& bOk, ICRVCOMPOPOVECTOR& vpCrvs)
{
// controllo dei parametri
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
return false ;
vpCrvs.clear() ;
bOk = false ;
// se la superficie non ha isole, allora non calcolo nulla
if ( pSfrChunk->GetLoopCount( 0) == 1)
return true ;
// recupero il bordo esterno e l'eventuale guida associata
ICRVCOMPOPOVECTOR vpCrvHomo ;
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() ||
! AdjustCloseEdgesForConformalGuide( pCrvExtLoop, PockParam) ||
! IsCompoMadeBy2DifferentHomogeneousParts( pCrvExtLoop, PockParam, bOk, vpCrvHomo))
return false ;
if ( ! bOk)
return true ;
// recupero la guida ideale
PtrOwner<ICurveComposite> pCrvGuide( CreateCurveComposite()) ;
if ( IsNull( pCrvGuide) ||
! CalcConformalGuide( vpCrvHomo[0], vpCrvHomo[1], PockParam, pCrvGuide))
return false ;
if ( ! pCrvGuide->IsValid())
return true ;
// la guida definisce il tratto chiuso per gli Offsets
vpCrvs.emplace_back( Release( pCrvGuide)) ;
// recupero il bordo esterno della superficie originale
pCrvExtLoop.Set( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
return false ;
// considero questo bordo come tutto aperto
for ( int i = 0 ; i < pCrvExtLoop->GetCurveCount() ; ++ i)
pCrvExtLoop->SetCurveTempProp( i, 0, TEMP_PROP_OPEN_EDGE) ;
// recupero solo le isole chiuse (di sicuro esistono isole)
for ( int nL = 1 ; nL < pSfrChunk->GetLoopCount( 0) ; ++ nL) {
// recupero la curva dell'isola
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, nL))) ;
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
return false ;
// se curva tutta chiusa, allora la inserisco come isola
bool bAllOpen, bAllClose ;
if ( IsCurveCompoHomogeneous( pCrvIsl, bAllClose, bAllOpen) && bAllClose)
vpCrvs.emplace_back( Release( pCrvIsl)) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetConformalOffsets( const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrClass,
const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParam,
VICRVCOMPOPOVECTOR& vCrvOffs)
{
// controllo dei parametri
if ( pSfrClass == nullptr || ! pSfrClass->IsValid() ||
pSfrChunk == nullptr || ! pSfrChunk->IsValid())
return false ;
vCrvOffs.clear() ;
/* NB
vCrvOffs è un vettore di vettori di curve composite. Per ogni Offset progressivo i-esimo
vengono salvate tutte le parti interne j-esime alla pSfrClass.
( la posizione del vettore ICRVCOMPOPOVECTOR in VICRVCOMPOPOVECTOR è il numero di iterazione
progressiva di Offset)
*/
/* controllo se il chunk è svuotabile con lavorazione conformal
Sono previsti due casi di lavorazioni conformal :
1) Non esistono Isole e il bordo esterno è formato da due tratti disomogenei
2) Esistono solo Isole chiuse e il bordo esterno è tutto aperto
*/
bool bOk = false ;
ICRVCOMPOPOVECTOR vCrvCloseEdges ; // tratti chiusi da cui calcolare gli Offset
// 1) Non esistono Isole e il bordo esterno è formato da due tratti disomogenei
if ( pSfrChunk->GetLoopCount( 0) == 1) {
// recupero la curva di bordo
ICRVCOMPOPOVECTOR vpCrvs ;
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() ||
! IsCompoMadeBy2DifferentHomogeneousParts( pCrvExtLoop, PockParam, bOk, vpCrvs))
return false ;
if ( ! bOk)
return true ;
// recupero la guida ideale
PtrOwner<ICurveComposite> pCrvGuide( CreateCurveComposite()) ;
if ( IsNull( pCrvGuide) ||
! CalcConformalGuide( vpCrvs[0], vpCrvs[1], PockParam, pCrvGuide))
return false ;
if ( ! pCrvGuide->IsValid())
return true ;
// la guida definisce il tratto chiuso per gli Offsets
vCrvCloseEdges.emplace_back( Release( pCrvGuide)) ;
}
// 2) Esistono solo Isole chiuse e il bordo esterno è tutto aperto
else {
if ( ! IsSfrChunkMadeOnlyByClosedIslands( pSfrChunk, PockParam, bOk, vCrvCloseEdges))
return false ;
if ( ! bOk) {
// 3) provo a combinare punto 1) e 2)
if ( ! CombineClosedEdgesForConformalOffsets( pSfrChunk, PockParam, bOk, vCrvCloseEdges))
return false ;
if ( ! bOk)
return true ; // sarà in SpiralIn/Out...
}
}
/* Calcolo degli Offsets */
// Oggetto Voronoi per curve chiuse
Voronoi myVRONI ;
for ( int i = 0 ; i < int( vCrvCloseEdges.size()) ; ++ i) {
vCrvCloseEdges[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
myVRONI.AddCurve( vCrvCloseEdges[i]) ;
}
// determino la regione limite
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrLimit))
return false ;
if ( PockParam.SfrLimit.IsValid()) {
if ( ! pSfrLimit.Set( PockParam.SfrLimit.Clone()) ||
! pSfrLimit->IsValid() ||
! pSfrLimit->Offset( PockParam.dRad + PockParam.dRadialOffset - 5 * EPS_SMALL, ICurve::OFF_FILLET))
return false ;
}
int MAX_ITER = 1000 ;
int nIter = 0 ;
double dOffs = PockParam.dRad + PockParam.dRadialOffset ;
PtrOwner<ISurfFlatRegion> pSfrToRemove( CloneSurfFlatRegion( pSfrOrig)) ;
if ( IsNull( pSfrToRemove) || ! pSfrToRemove->IsValid())
return false ;
while ( nIter < MAX_ITER) {
// determino se le curve vanno invertite o meno
// essendo FatCurve, la parte interna alla regione di classificazione è invertita
bool bInvert = false ;
if ( PockParam.nType == POCKET_CONFORMAL_ZIGZAG)
bInvert = ! ( ( ! IsEven( nIter) && ! PockParam.bInvert) || ( IsEven( nIter) && PockParam.bInvert)) ;
else if ( PockParam.nType == POCKET_CONFORMAL_ONEWAY)
bInvert = ! ( PockParam.bInvert) ;
// recupero gli Offset dei tratti chiusi
ICURVEPOVECTOR vFatCrv ;
if ( ! myVRONI.CalcFatCurve( vFatCrv, dOffs, false, false))
return false ;
// vettore di Curve dentro alla regione di classificazione
ICRVCOMPOPOVECTOR vCrvOffsInside ;
// per tutte le curve ottenute, tengo solo per le parti interne alla regione di classificazione
bool bStop = true ;
for ( int j = 0 ; j < int( vFatCrv.size()) ; ++ j) {
CRVCVECTOR ccClass ;
if ( ! pSfrClass->GetCurveClassification( *vFatCrv[j], EPS_SMALL, ccClass))
return false ;
for ( int k = 0 ; k < int( ccClass.size()) ; ++ k) {
if ( ccClass[k].nClass == CRVC_IN) {
// recupero il tratto di curva
PtrOwner<ICurve> pMyCrv( vFatCrv[j]->CopyParamRange( ccClass[k].dParS, ccClass[k].dParE)) ;
if ( ! IsNull( pMyCrv) && pMyCrv->IsValid()) {
if ( ! IsNull( pSfrLimit) && pSfrLimit->IsValid()) {
// controllo che tale tratto sia esterno alla regione di incidenza
CRVCVECTOR ccClass1 ;
if ( ! pSfrLimit->GetCurveClassification( *pMyCrv, EPS_SMALL, ccClass1))
return false ;
for ( int kk = 0 ; kk < int( ccClass1.size()) ; ++ kk) {
if ( ccClass1[kk].nClass != CRVC_IN) {
PtrOwner<ICurve> pMyCrv1( pMyCrv->CopyParamRange( ccClass1[kk].dParS, ccClass1[kk].dParE)) ;
if ( ! IsNull( pMyCrv1) && pMyCrv1->IsValid()) {
// almeno una curva in trovata
bStop = false ;
// inverto se necessario
if ( bInvert)
pMyCrv1->Invert() ;
// salvo come seconda proprietà temporanea il lato interno
pMyCrv1->SetTempProp( bInvert ? MDS_LEFT : MDS_RIGHT, 1) ;
// memorizzo la curva nel vettore
vCrvOffsInside.emplace_back( ConvertCurveToComposite( Release( pMyCrv1))) ;
}
}
}
}
else {
// almeno una curva in trovata
bStop = false ;
// inverto se necessario
if ( bInvert)
pMyCrv->Invert() ;
// salvo come seconda proprietà temporanea il lato interno
pMyCrv->SetTempProp( bInvert ? MDS_LEFT : MDS_RIGHT, 1) ;
// memorizzo la curva nel vettore
vCrvOffsInside.emplace_back( ConvertCurveToComposite( Release( pMyCrv))) ;
}
}
}
}
}
// concateno se necessario ( per tolleranza Offset)
int nTempProp0 = ( ! vCrvOffsInside.empty() ? vCrvOffsInside[0]->GetTempProp( 0) : TEMP_PROP_INVALID) ;
int nTempProp1 = ( ! vCrvOffsInside.empty() ? vCrvOffsInside[0]->GetTempProp( 1) : TEMP_PROP_INVALID) ;
if ( ! ChainCompoCurves( vCrvOffsInside))
return false ;
for ( auto& CrvCompo : vCrvOffsInside) {
CrvCompo->SetTempProp( nTempProp0, 0) ;
CrvCompo->SetTempProp( nTempProp1, 1) ;
}
// se ho trovato delle curve interne
if ( ! bStop) {
// inserisco le curva ricavate all'iterazione nIter nel vettore
vCrvOffs.resize( ++ nIter) ; // incremento nIter
for ( int i = 0 ; i < int( vCrvOffsInside.size()) ; ++ i) {
vCrvOffsInside[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
// recupero la regione rimossa da tale curva
PtrOwner<ISurfFlatRegion> pSfrFatCurve( GetSurfFlatRegionFromFatCurve( vCrvOffsInside[i]->Clone(), PockParam.dRad, false, false)) ;
if ( IsNull( pSfrFatCurve) || ! pSfrFatCurve->IsValid())
return false ;
if ( AreOppositeVectorApprox( pSfrFatCurve->GetNormVersor(), Z_AX))
pSfrFatCurve->Invert() ;
// controllo se tale regione interserca quella da rimuovere
PtrOwner<ISurfFlatRegion> pSfrFatCurveCL( CloneSurfFlatRegion( pSfrFatCurve)) ;
if ( IsNull( pSfrFatCurveCL) || ! pSfrFatCurveCL->IsValid())
return false ;
if ( pSfrToRemove->IsValid()) {
pSfrFatCurveCL->Intersect( *pSfrToRemove) ;
// se esiste l'intersezione
if ( pSfrFatCurveCL->IsValid()) {
// conservo la curva
vCrvOffs.back().emplace_back( Release( vCrvOffsInside[i])) ;
// aggiorno la regione da rimuovere
pSfrToRemove->Subtract( *pSfrFatCurve) ;
}
}
}
// se non ho inserito nulla, allora ho finito
if ( vCrvOffs.back().empty()) {
vCrvOffs.pop_back() ;
break ;
}
// aggiorno l'Offset progressivo
dOffs += PockParam.dSideStep ;
}
// se non ho ricavato curve interne, allora esco
else
break ;
}
// smusso le curve di Offset ( ad eccezione di quelle generate alla prima iterazione)
for ( int i = 1 ; i < int( vCrvOffs.size()) ; ++ i) {
for ( int j = 0 ; j < int( vCrvOffs[i].size()) ; ++ j)
ModifyCurveToSmoothed( vCrvOffs[i][j], PockParam, PockParam.dSmooth, PockParam.dSmooth, false) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CalcSpiralPocketing( const ISurfFlatRegion* pSfr, int nType, const PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// verifica validità della superficie
if ( pSfr == nullptr || ! pSfr->IsValid())
return false ;
// il tipo può essere solo SpiralIn o SpiralOut
if ( nType != POCKET_SPIRALIN && nType != POCKET_SPIRALOUT)
return false ;
PtrOwner<ISurfFlatRegion> pSfrLimit( PockParams.SfrLimit.IsValid() ? PockParams.SfrLimit.Clone() : CreateSurfFlatRegion()) ;
// calcolo il percorso di svuotatura spiral
return ( CalcPocketing( pSfr, PockParams.dRad, PockParams.dRadialOffset, PockParams.dSideStep,
PockParams.dAngle, PockParams.dOpenMinSafe, nType, PockParams.bSmooth,
PockParams.bCalcUnclearedRegs, PockParams.bInvert, PockParams.bAvoidOpt,
PockParams.bConventionalMilling, PockParams.bAllowZigZagOneWayBorders, PockParams.bCalcFeed, PockParams.ptStart,
pSfrLimit, PockParams.bAvoidOpt, PockParams.dMaxOptSize,
PockParams.nLiType, PockParams.dLiTang, PockParams.dLiElev, PockParams.nLoType, PockParams.dLoTang,
PockParams.bPolishing, PockParams.dEpicyclesRad, PockParams.dEpicyclesDist, vCrvCompoRes)) ;
}
//----------------------------------------------------------------------------
static bool
GetOrigChunkForConformal( const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrOrig,
set<int>& setChunks)
{
// NB. funzione migliorabile... potrei calcolare tutto all'inizio senza ripetere le intersezioni
// controllo validità delle regioni
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() ||
pSfrOrig == nullptr || ! pSfrOrig->IsValid())
return false ;
// se la superficie originale ha un solo Chunk allora è lui stesso
if ( pSfrOrig->GetChunkCount() == 1) {
setChunks.insert( 0) ;
return true ;
}
// essendo la pSfrChunk estesa, cerco quale chunk di pSfrOrig ha una intersezione valida
for ( int nC = 0 ; nC < pSfrOrig->GetChunkCount() ; ++ nC) {
// se il Chunk è già stato analizzato in precedenza, passo al successivo
if ( setChunks.find( nC) != setChunks.end())
continue ;
// recupero il Chunk corrente
PtrOwner<ISurfFlatRegion> pSfrChunkOrig( pSfrOrig->CloneChunk( nC)) ;
if ( IsNull( pSfrChunkOrig) || ! pSfrChunkOrig->IsValid())
return false ;
// controllo se esiste l'intersezione
if ( pSfrChunkOrig->Intersect( *pSfrChunk) && pSfrChunkOrig->IsValid())
setChunks.insert( nC) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CreateSurfFromOtherChunks( ISurfFlatRegion* pSfr, const ISurfFlatRegion* pSfrOther, const set<int>& setChunks)
{
// controllo della regione
if ( pSfr == nullptr ||
pSfrOther == nullptr || ! pSfrOther->IsValid())
return false ;
// se non ho indici di Chunk non faccio nulla
if ( setChunks.empty())
return true ;
// scorro gli indici dei Chunks
for ( auto it = setChunks.begin() ; it != setChunks.end() ; ++ it) {
// recupero l'indice del Chunk
int nChunk = *it ;
// recupero il Chunk come regione piana
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfrOther->CloneChunk( nChunk)) ;
if ( ! IsNull( pSfrChunk) && pSfrChunk->IsValid()) {
if ( ! pSfr->IsValid())
pSfr->CopyFrom( pSfrChunk) ;
else if ( ! pSfr->Add( *pSfrChunk))
return false ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AddConformal( ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig,
PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// controllo dei parametri
if ( pSfrPock == nullptr || ! pSfrPock->IsValid() ||
pSfrOrig == nullptr || ! pSfrOrig->IsValid())
return true ;
// se superifice tutta aperta, lavoro in SPIRAL_IN
if ( PockParams.bAllOpen)
return ( CalcSpiralPocketing( pSfrOrig, POCKET_SPIRALIN, PockParams, vCrvCompoRes)) ;
// se superficie tutta chiusa, lavoro in SPIRAL_OUT
if ( PockParams.bAllClosed)
return ( CalcSpiralPocketing( pSfrOrig, POCKET_SPIRALOUT, PockParams, vCrvCompoRes)) ;
// definisco eventuali regioni da lavorare in SpiralIn e SpiralOut
PtrOwner<ISurfFlatRegion> pSfrSpiralIn( CreateSurfFlatRegion()) ;
PtrOwner<ISurfFlatRegion> pSfrSpiralOut( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrSpiralIn) || IsNull( pSfrSpiralOut))
return false ;
// NB. La supercicie pSfrPock è estesa presso i lati aperti, quindi il suo numero di Chunk potrebbe
// essere differente dal numero di Chunk della superficie originale di svuotatura. Tengo un insieme di
// di indici dei Chunk che devono essere lavorati in SpiralIn e in SpiralOut
set<int> setIndChunkSpiralIn ;
set<int> setIndChunkSpiralOut ;
// scorro i chunk della superficie da lavorare
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
// controllo se il Chunk ha tutte proprietà omogenee tra loro
bool bClose, bOpen ;
if ( ! IsChunkAllHomogeneous( pSfrPock, nC, bClose, bOpen))
return false ;
// recupero il Chunk come regione piana
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfrPock->CloneChunk( nC)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
return false ;
// --- se Chunk tutto aperto, aggiorno i Chunk della superficie originale da lavorare in SpiralIn
if ( bOpen) {
if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn))
return false ;
continue ;
}
// --- se Chunk tutto chiuso, lo lavoro in SPIRAL_OUT
else if ( bClose) {
if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralOut))
continue ;
}
// --- se chunk non omogoneo, ricavo gli Offset ( se possibili) dei tratti chiusi del Chunk
VICRVCOMPOPOVECTOR vvCrvOffs ;
if ( ! GetConformalOffsets( pSfrChunk, pSfrChunk, pSfrOrig, PockParams, vvCrvOffs))
return false ;
// se non ottengo Curve di Offset, lavoro in SpiralIn ( il Chunk ha dei lati aperti)
if ( vvCrvOffs.empty()) {
if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn))
return false ;
continue ;
}
// definisco i vettori ordinati degli Offset e dei Link
ICRVCOMPOPOVECTOR vCrvOffs ;
ICURVEPOVECTOR vCrvLink ;
if ( ! CalcConformalOffsAndLinks( vvCrvOffs, pSfrChunk, pSfrPock, PockParams, vCrvOffs, vCrvLink)) {
if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn))
return false ;
continue ;
}
// flag per controllo
bool bOk = true ;
// estendo i percorsi per eventuali regioni non svuotate e calcolo le Feed
bOk = bOk && ExtendConformalOffsAndSetFeed( pSfrPock, pSfrOrig, PockParams, vCrvOffs, vCrvLink) ;
// concateno Offset e Links
ICRVCOMPOPOVECTOR vCrvPaths ;
bOk = bOk && ChainConformalOffsWithLinks( vCrvOffs, vCrvLink, vCrvPaths) ;
// estendo per lati aperti ed ordino i percorsi trovati
bOk = bOk && OrderAndExtendConformalPaths( vCrvPaths, pSfrChunk, pSfrOrig, PockParams) ;
// se qualche passaggio restituisce errore, provo in SpiralIn
if ( ! bOk) {
if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn))
return false ;
continue ;
}
// altrimenti aggiungo i percorsi ricavati
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i)
vCrvCompoRes.emplace_back( Release( vCrvPaths[i])) ;
}
// se ho superfici da lavorare in SpiralIn, aggiungo le curve
if ( ! setIndChunkSpiralIn.empty()) {
// costruisco la regione di SpiralIn
PtrOwner<ISurfFlatRegion> pSfrSpiralIn( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrSpiralIn) ||
! CreateSurfFromOtherChunks( pSfrSpiralIn, pSfrOrig, setIndChunkSpiralIn))
return false ;
// recupero le curve di Pocketing SpiralIn
ICRVCOMPOPOVECTOR vCrvSpiralIn ;
if ( ! CalcSpiralPocketing( pSfrSpiralIn, POCKET_SPIRALIN, PockParams, vCrvSpiralIn))
return false ;
for ( int i = 0 ; i < int( vCrvSpiralIn.size()) ; ++ i) {
if ( vCrvSpiralIn[i] != nullptr && vCrvSpiralIn[i]->IsValid())
vCrvCompoRes.emplace_back( Release( vCrvSpiralIn[i])) ;
}
}
// se ho superfici da lavorare in SpiralOut, aggiungo le curve
if ( ! setIndChunkSpiralOut.empty()) {
// costruisco la regione di SpiralOut
PtrOwner<ISurfFlatRegion> pSfrSpiralOut( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrSpiralIn) ||
! CreateSurfFromOtherChunks( pSfrSpiralOut, pSfrOrig, setIndChunkSpiralOut))
return false ;
// recupero le curve di Pocketing SpiralOut
ICRVCOMPOPOVECTOR vCrvSpiralOut ;
if ( ! CalcSpiralPocketing( pSfrSpiralOut, POCKET_SPIRALOUT, PockParams, vCrvSpiralOut))
return false ;
for ( int i = 0 ; i < int( vCrvSpiralOut.size()) ; ++ i) {
if ( vCrvSpiralOut[i] != nullptr && vCrvSpiralOut[i]->IsValid())
vCrvCompoRes.emplace_back( Release( vCrvSpiralOut[i])) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
SmoothExtensionLinesByIntersection( ICRVCOMPOPOVECTOR& vCrvPaths, const PocketParams& PockParams)
{
// estendendo i percorsi con tratti in ingresso e in uscita, può capitare che si creino autointersezioni
// se ho meno di due percorsi non faccio nulla
if ( int( vCrvPaths.size()) < 2)
return true ;
// cerco le autointersezioni tra i percorsi successivi tra loro
double dTol = 50 * EPS_SMALL ;
for ( int i = 0 ; i < int( vCrvPaths.size()) - 1 ; ++ i) {
// se uno dei due percorsi presenta meno di 3 curve, non faccio nulla
if ( ! vCrvPaths[i]->IsValid() || vCrvPaths[i]->GetCurveCount() < 3 ||
! vCrvPaths[i+1]->IsValid() || vCrvPaths[i+1]->GetCurveCount() < 3)
continue ;
// se il percorso i-esimo non presenta una estensione in uscita e il percorso (i+1)-esimo
// non presenta una estensione in entrata, non faccio nulla
if ( vCrvPaths[i]->GetLastCurve()->GetTempProp() != TEMP_PROP_OUT_START ||
vCrvPaths[i+1]->GetFirstCurve()->GetTempProp() != TEMP_PROP_OUT_START)
continue ;
// recupero i due tratti lineari del percorso
const ICurveLine* pLineA = GetCurveLine( vCrvPaths[i]->GetLastCurve()) ;
const ICurveLine* pLineB = GetCurveLine( vCrvPaths[i+1]->GetFirstCurve()) ;
if ( pLineA == nullptr || ! pLineA->IsValid() ||
pLineB == nullptr || ! pLineB->IsValid())
continue ;
// se non esistono intersezioni tra i segmenti, non faccio nulla
IntersCurveCurve ICC( *pLineA, *pLineB) ;
if ( ICC.GetIntersCount() == 0)
continue ;
// definisco la curva di collegamento
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
if ( IsNull( pCrvLink))
return false ;
Point3d ptS_A ; pLineA->GetStartPoint( ptS_A) ;
pCrvLink->AddPoint( ptS_A) ;
IntCrvCrvInfo aInfo ;
if ( ! ICC.GetIntCrvCrvInfo( 0, aInfo))
continue ;
pCrvLink->AddLine( aInfo.IciA[0].ptI) ;
if ( aInfo.bOverlap)
pCrvLink->AddLine( aInfo.IciA[1].ptI) ;
Point3d ptE_B ; pLineB->GetEndPoint( ptE_B) ;
pCrvLink->AddLine( ptE_B) ;
// se tutti i punti sono coincidenti, allora non ho bisogno di entrambe le estensioni
if ( ! pCrvLink->IsValid() || pCrvLink->GetCurveCount() == 0) {
delete( vCrvPaths[i]->RemoveFirstOrLastCurve( true)) ;
delete( vCrvPaths[i+1]->RemoveFirstOrLastCurve( false)) ;
vCrvPaths[i]->AddCurve( Release( vCrvPaths[i+1]), dTol) ;
vCrvPaths.erase( vCrvPaths.begin() + i + 1) ;
-- i ;
continue ;
}
// raccordo del Link con percorso attuale e successivo
double dLen ;
double dLenExtension ;
pLineA->GetLength( dLenExtension) ;
if ( vCrvPaths[i]->GetLength( dLen) && ( dLen - dLenExtension) > PockParams.dRad / 2.) {
double dUS, dUE ;
vCrvPaths[i]->GetDomain( dUS, dUE) ;
double dU ;
vCrvPaths[i]->GetParamAtLength( dLen - dLenExtension - ( PockParams.dRad / 2.), dU) ;
pCrvLink->AddCurve( ConvertCurveToComposite( vCrvPaths[i]->CopyParamRange( dU, dUE - 1)), false, dTol) ; ;
}
pLineB->GetLength( dLenExtension) ;
if ( vCrvPaths[i+1]->GetLength( dLen) && ( dLen - dLenExtension) > PockParams.dRad / 2.) {
double dUS, dUE ;
vCrvPaths[i+1]->GetDomain( dUS, dUE) ;
double dU ;
vCrvPaths[i+1]->GetParamAtLength( dLenExtension + ( PockParams.dRad / 2.), dU) ;
pCrvLink->AddCurve( ConvertCurveToComposite( vCrvPaths[i+1]->CopyParamRange( dUS + 1, dU)), true, dTol) ;
}
pCrvLink->MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG) ;
ModifyCurveToSmoothed( pCrvLink, PockParams, PockParams.dRad / 8., PockParams.dRad / 8., false) ;
// assegno Feed massima
AssignMaxFeed( pCrvLink, PockParams) ;
// modifico i percorsi
Point3d ptLinkS ; pCrvLink->GetStartPoint( ptLinkS) ;
Point3d ptLinkE ; pCrvLink->GetEndPoint( ptLinkE) ;
double dUE, dUS ;
if ( ! vCrvPaths[i]->GetParamAtPoint( ptLinkS, dUE, dTol) ||
! vCrvPaths[i]->TrimEndAtParam( dUE) ||
! vCrvPaths[i+1]->GetParamAtPoint( ptLinkE, dUS, dTol) ||
! vCrvPaths[i+1]->TrimStartAtParam( dUS))
continue ;
vCrvPaths[i]->AddCurve( Release( pCrvLink), dTol) ;
vCrvPaths[i]->AddCurve( Release( vCrvPaths[i+1]), dTol) ;
vCrvPaths.erase( vCrvPaths.begin() + i + 1) ;
-- i ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double dStep, double dAngle,
double dOpenMinSafe, int nType, bool bSmooth, bool bCalcUnclReg, bool bInvert, bool bAvoidOpt,
bool bConventionalMilling, bool bAllowZigZagOneWayBorders, bool bCalcFeed, const Point3d& ptEndPrec,
const ISurfFlatRegion* pSfrLimit, bool bAllOffs, double dMaxOptSize,
int nLiType, double dLiTang, double dLiElev, int nLoType, double dLoTang,
bool bPolishing, double dEpicyclesRad, double dEpicyclesDist, 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 && nType != POCKET_CONFORMAL_ONEWAY))
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.dOpenMinSafe = dOpenMinSafe ;
myParams.bCalcUnclearedRegs = bCalcUnclReg ;
myParams.bInvert = bInvert ;
myParams.bAvoidOpt = bAvoidOpt ;
myParams.bCalcFeed = bCalcFeed ;
myParams.bConventionalMilling = bConventionalMilling ;
myParams.bAllowZigZagOneWayBorders = bAllowZigZagOneWayBorders ;
myParams.bOptOffsets = ( ! bAllOffs) ;
myParams.dMaxOptSize = dMaxOptSize ;
myParams.nLiType = nLiType ;
myParams.dLiTang = dLiTang ;
myParams.dLiElev = dLiElev ;
myParams.nLoType = nLoType ;
myParams.dLoTang = dLoTang ;
myParams.bPolishing = bPolishing ;
myParams.dEpicyclesRad = dEpicyclesRad ;
myParams.dEpicyclesDist = dEpicyclesDist ;
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 nel sistema di riferimento lungo Z_AX
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) ;
// controllo se la superficie è tutta chiusa o tutta aperta
if ( ! IsSfrAllHomogeneous( pSfr, myParams.bAllClosed, myParams.bAllOpen))
return false ;
// ------------ casi ottimizzati ------------------------------------------
ICRVCOMPOPOVECTOR vCrvOptCurves ;
if ( ! GetPocketingOptimizedCurves( pSfrAdj, myParams, vCrvOptCurves))
return false ;
for ( int i = 0 ; i < int( vCrvOptCurves.size()) ; ++ i) {
vCrvOptCurves[i]->ToGlob( myParams.frLocXY) ;
vCrvCompoRes.emplace_back( Release( vCrvOptCurves[i])) ;
}
// se ho svuotato tutta la superficie, allora ho finito
if ( IsNull( pSfrAdj) || ! pSfrAdj->IsValid() || pSfrAdj->GetChunkCount() == 0)
return true ;
// ------------ recupero le singole curve di svuotatura -------------------
ICRVCOMPOPOVECTOR vCrvSingleCurves ;
if ( ! GetSinglePocketingCurves( pSfrAdj, myParams, vCrvSingleCurves))
return false ;
for ( int i = 0 ; i < int( vCrvSingleCurves.size()) ; ++ i) {
vCrvSingleCurves[i]->ToGlob( myParams.frLocXY) ;
vCrvCompoRes.emplace_back( Release( vCrvSingleCurves[i])) ;
}
// se ho svuotato tutta la superficie, allora ho finito
if ( IsNull( pSfrAdj) || ! pSfrAdj->IsValid() || pSfrAdj->GetChunkCount() == 0)
return true ;
// ------------ estensione lati aperti ------------------------------------
if ( ! ModifySurfByOpenEdges( pSfrAdj, myParams))
return false ;
// 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 -------------------
ICRVCOMPOPOVECTOR vCrvCompoPock ;
switch ( nType) {
case POCKET_ZIGZAG :
if ( ! AddZigZag( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
return false ;
break ;
case POCKET_ONEWAY :
if ( ! AddOneWay( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
return false ;
break ;
case POCKET_SPIRALIN :
if ( ! AddSpiralIn( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
return false ;
break ;
case POCKET_SPIRALOUT :
if ( ! AddSpiralOut( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
return false ;
break ;
case POCKET_CONFORMAL_ZIGZAG :
case POCKET_CONFORMAL_ONEWAY :
if ( ! AddConformal( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
return false ;
break ;
}
// ------------ intersezione delle estensioni ----------------------------
if ( ! myParams.bAllClosed) {
if ( ! SmoothExtensionLinesByIntersection( vCrvCompoPock, myParams))
return false ;
}
// riporto tutte le curve ottenute in globale
for ( int i = 0 ; i < int( vCrvCompoPock.size()) ; ++ i) {
vCrvCompoPock[i]->ToGlob( myParams.frLocXY) ;
vCrvCompoRes.emplace_back( Release( vCrvCompoPock[i])) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
IsPocketingHole( const ISurfFlatRegion* pSfr, double dToolRad, double dRadialOffs, double dSideStep, int nType, int nLiType,
double dLiTang, double dRatioSfrTool)
{
// verifico validità della superficie
if ( pSfr == nullptr || ! pSfr->IsValid())
return false ;
// la superficie deve avere un solo Chunk e non presentare isole
if ( pSfr->GetChunkCount() != 1 && pSfr->GetLoopCount( 0) != 1)
return false ;
// porto la superficie nel suo riferimento intrinseco, quindi lavoro nel piano XY
Point3d ptC ; pSfr->GetCentroid( ptC) ;
Frame3d frXY ;
if ( ! frXY.Set( ptC, pSfr->GetNormVersor()))
return false ;
PtrOwner<ISurfFlatRegion> pSfrCL( CloneSurfFlatRegion( pSfr)) ;
if ( IsNull( pSfrCL) || ! pSfrCL->IsValid() || ! pSfrCL->ToLoc( frXY))
return false ;
// recupero l'unica curva di bordo che descrive la regione piana
PtrOwner<ICurveComposite> pCrvBorder( ConvertCurveToComposite( pSfrCL->GetLoop( 0, 0))) ;
if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid())
return false ;
pCrvBorder->MergeCurves( 10. * EPS_SMALL, 10. * EPS_ANG_SMALL, true, true) ;
pCrvBorder->SetExtrusion( pSfrCL->GetNormVersor()) ; // Z_AX()
// verifico che le proprietà di lato Aperto/Chiuso siano uniformi
bool bOkSpiral = true ;
const ICurve* pFirstCrv = pCrvBorder->GetCurve( 0) ;
if ( pFirstCrv == nullptr || ! pFirstCrv->IsValid())
return false ;
int nTmpPropRef = pFirstCrv->GetTempProp( 0) ;
for ( int nU = 1 ; bOkSpiral && nU < pCrvBorder->GetCurveCount() ; ++ nU) {
const ICurve* pCrv = pCrvBorder->GetCurve( nU) ;
if ( pCrv == nullptr || ! pCrv->IsValid())
return false ;
bOkSpiral = ( pCrv->GetTempProp( 0) == nTmpPropRef) ;
}
if ( ! bOkSpiral)
return false ;
// definisco i parametri di una lavorazione Standard ( solo quelli utili per una svuotatura di un foro)
PocketParams PockParam ;
PockParam.dRad = dToolRad ;
PockParam.dRadialOffset = dRadialOffs ;
PockParam.dSideStep = dSideStep ;
PockParam.ptStart = P_INVALID ;
PockParam.bCalcFeed = false ;
PockParam.nType = nType ;
PockParam.nLiType = nLiType ;
PockParam.dLiTang = dLiTang ;
// controllo che il Loop sia una circonferenza
Point3d ptCen ; double dRad ;
if ( ! OptimizedSpiralCircle( pCrvBorder, 50. * EPS_SMALL, dRad, ptCen, bOkSpiral) || ! bOkSpiral)
return false ;
double dOffs = PockParam.dRad + PockParam.dRadialOffset ;
// se curva tutta Open, ingrandisco il raggio della circonferenza trovata
if ( nTmpPropRef == TEMP_PROP_OPEN_EDGE)
dRad += 1.05 * PockParam.dRad + PockParam.dRadialOffset ;
// se curva chiusa, controllo che il raggio sia compatibile con il primo Offset
else
bOkSpiral = ( dRad - dOffs > 10. * EPS_SMALL) ;
if ( ! bOkSpiral)
return false ;
// verifico di rispettare il Ratio
if ( dRad > dRatioSfrTool * dToolRad + EPS_SMALL)
return false ;
// verifico se l'entrata è compatibile con la geometria e calcolo un eventuale percorso
double dIntRad = 0 ;
if ( PockParam.nType == POCKET_SPIRALOUT && PockParam.nLiType == LEAD_IN_HELIX)
dIntRad = min( 0.5 * min( PockParam.dLiTang, 2. * PockParam.dRad), dRad - dOffs) ;
PtrOwner<ICurveComposite> pCrvRes( CreateCurveComposite()) ;
if ( IsNull( pCrvRes))
return false ;
bOkSpiral = CalcCircleSpiral( ptCen, pSfrCL->GetNormVersor(), dRad - dOffs, dIntRad, PockParam, pCrvRes) ;
return ( bOkSpiral && pCrvRes->IsValid() && pCrvRes->GetCurveCount() > 0) ;
}
//----------------------------------------------------------------------------
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 ;
myParams.bCalcFeed = false ;
myParams.dSideStep = dStep ;
myParams.bInvert = false ;
// 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 ;
}
//----------------------------------------------------------------------------
bool
CalcSmoothCurve( ICurveComposite* pCrv, double dRightLen, double dLeftLen, bool bAsParam)
{
// controllo dei parametri
if ( pCrv == nullptr || ! pCrv->IsValid())
return false ;
// definisco i parametri
PocketParams myPockParams ;
myPockParams.bSmooth = true ;
return ModifyCurveToSmoothed( pCrv, myPockParams, dRightLen, dLeftLen, bAsParam) ;
}