Files
EgtGeomKernel/CalcPocketing.cpp
T
Riccardo Elitropi b0f7cb93fd EgtGeomKernel :
- Aggiunto parametro in CalcPocketing.
2025-03-04 17:42:53 +01:00

9054 lines
374 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 ;
double dRad = 0. ; // raggio utensile
double dRad_prec = -1. ; // raggio utensile della lavorazione precedente
double dSideStep = 0. ; // step
double dSideStep_prec = -1. ; // step della lavorazione precedente
double dRadialOffset = 0. ; // offset radiale
double dRadialOffset_prec = -1. ; // offset radiale precedente
double dMaxOpenEdgeRad = 0. ; // massimo raggio per estensione lati aperti
double dOpenMinSafe = 5. ; // estensione minima di sicurezza
double dMaxOptSize = 0. ; // dimensione per ottimizzazione
double dAngle = 0. ; // angolo per orientare le passate OneWay e ZigZag
bool bOptOffsets = true ; // flag per evitare Offset non necessari in SpiralIn/Out
bool bOptOffsetsAdv = false ; // flag per evitare Offset coperti da curve di MedialAxis in SpiralIn/Out
bool bAboveHead = true ; // flag per testa da sopra ( Z+)
bool bSmooth = false ; // curve smussate
bool bInvert = false ; // inversione dei percorsi
bool bAvoidOpt = false ; // flag per evitare casi ottimizzati
bool bAllowZigZagOneWayBorders = false ; // flag per abilitare le curve di bordo per ZigZag/OneWay
Point3d ptStart = P_INVALID ; // punto d'inizio
Point3d ptEnd = P_INVALID ; // punto di fine
SurfFlatRegion SfrLimit ; // superficie limite per estensione lati aperti
bool bCalcFeed = true ; // flag per calcolo della Feed
double dFeed = 1000 ; // feed di riferimento per frazione
double dToolFeed = 1000 ; // feed del tool
// -------------------------------------------------------
bool bAllClosed = true ; // flag per indicare se tutti i bordi 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
} ;
static double TOL_TRAPEZOID = 50 * EPS_SMALL ; // tolleranza per casi a trapezio SpiralPocket
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"
//----------------------------------------------------------------------------
// Debug Functions
//----------------------------------------------------------------------------
void
DrawObjs( const ISURFFRPOVECTOR& vSfr, bool bSfrUniform, bool bAlphaCoverage,
const ICRVCOMPOPOVECTOR& vCompo, bool bCompoUniform, string sName)
{
// definisco i vettori per geometrie e colori
vector<IGeoObj*> VT ;
vector<Color> VC ;
// disegno le superfici
for ( int nS = 0 ; nS < int( vSfr.size()) ; ++ nS) {
if ( vSfr[nS] == nullptr || ! vSfr[nS]->IsValid())
return ;
VT.emplace_back( static_cast<IGeoObj*>( vSfr[nS]->Clone())) ;
VC.emplace_back( bAlphaCoverage ? Color( 0., 64., 0., .5) : Color( 0., 255., 0., .5)) ;
for ( int nC = 0 ; nC < vSfr[nS]->GetChunkCount() ; ++ nC) {
for ( int nL = 0 ; nL < vSfr[nS]->GetLoopCount( nC) ; ++ nL) {
PtrOwner<ICurveComposite> pCrvCompo( ConvertCurveToComposite( vSfr[nS]->GetLoop( nC, nL))) ;
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( bSfrUniform ? WHITE : ( nProp0 == 0 ? ( bAlphaCoverage ? AQUA : BLUE) : ( bAlphaCoverage ? ORANGE : RED))) ;
}
}
}
}
// disegno le curve
for ( int nC = 0 ; nC < int( vCompo.size()) ; ++ nC) {
if ( vCompo[nC] == nullptr || ! vCompo[nC]->IsValid())
continue ;
VT.emplace_back( static_cast<IGeoObj*>( vCompo[nC]->Clone())) ;
VC.emplace_back( Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.)) ;
}
SaveGeoObj( VT, VC, sName) ;
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 u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
return true ;
}
//---------------------------------------------------------------------------
static bool
AssignCustomFeed( ICurveComposite* pCrv, const PocketParams& PockParams, double dFeed)
{
// controllo dei parametri
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
// La Feed deve essere compresa tra la minima e la massima
dFeed = Clamp( dFeed, GetMinFeed( PockParams), GetMaxFeed( PockParams)) ;
// assegno la Feed ad ogni sottocurva di pCrv
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, dFeed, 0) ;
return true ;
}
//---------------------------------------------------------------------------
static bool
GetFeedForParam( double dPar, const PocketParams& PockParams, double& dFeed)
{
/*
Feed
^
|
GetFeed() + --------------\
| * \
| * \
| * \
| * \
| * \
GetFeed() * GetSideStep() / Diam + * *
| * *
0--------------+------+---------------> Length of working angle
GetSideStep() Diam
*/
if ( dPar > PockParams.dRad * 2 || dPar < 0 ) // dominio...
return false ;
if ( PockParams.dRad * 2 - PockParams.dSideStep < 50 * EPS_SMALL) { // se la funzione è costante...
dFeed = GetMaxFeed( PockParams) ; // non ho scelta ...
return true ;
}
else {
if ( PockParams.dSideStep < dPar + 50 * EPS_SMALL) { // se sono nel tratto lineare discendente ...
// d/2 su parte discendente
dFeed = GetFeed( PockParams) + ( GetFeed( PockParams) * ( 1 - ( PockParams.dSideStep / ( PockParams.dRad * 2)))) *
( dPar - PockParams.dSideStep) / ( PockParams.dSideStep - ( PockParams.dRad * 2)) ;
}
else
dFeed = GetMaxFeed( PockParams) ; // se sono nel tratto costante ...
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AssignFeedZigZagOneWay( ICurveComposite* pCompo, const bool bIsLink, const ICURVEPOVECTOR& vLAbove,
const ICURVEPOVECTOR& vLUnder, const ICRVCOMPOPOVECTOR& vAddedLinks,
const PocketParams& PockParams)
{
// controllo che la pCompoLine sia effettivamente una linea valida
if ( pCompo == nullptr || ! pCompo->IsValid())
return false ;
// controllo se il Flag per assegnare la Feed è attivo
if ( ! PockParams.bCalcFeed)
return AssignMaxFeed( pCompo, PockParams) ;
// inzialmente setto la feed Minima alla curva
AssignMinFeed( pCompo, PockParams) ;
// se è un link tra livelli diversi, allora esco ( con Feed Minima )
if ( bIsLink) {
Point3d ptS_link ; pCompo->GetStartPoint( ptS_link) ;
Point3d ptE_link ; pCompo->GetEndPoint( ptE_link) ;
if ( abs( ptE_link.y - ptS_link.y) > 500 * EPS_SMALL)
return true ;
}
// se la curva è piccola, allora esco ( con Feed Minima)
double dLen = EPS_SMALL ;
if ( ! pCompo->GetLength( dLen) || dLen < 0.6 * ( 2 * PockParams.dRad))
return true ;
Point3d ptS, ptE ;
pCompo->GetStartPoint( ptS) ;
pCompo->GetEndPoint( ptE) ;
// creo l'intervallo desiderato valutanto le coordinate x
Intervals IntMinFeed ; IntMinFeed.Set( ptS.x, ptE.x) ;
// creo un vettore contenente i tratti sopra e i tratti sotto attivi
ICURVEPOVECTOR vAllInt ; vAllInt.reserve( int( vLUnder.size()) + int( vLAbove.size())) ;
for ( int i = 0 ; i < int( vLUnder.size()) ; ++ i)
vAllInt.emplace_back( vLUnder[i]->Clone()) ;
for ( int i = 0 ; i < int( vLAbove.size()) ; ++ i)
vAllInt.emplace_back( vLAbove[i]->Clone()) ;
int nDim = int( vAllInt.size()) ;
// aggiungo le parti dei link interessate
double dCurrY = ptS.y ;
BBox3d bBoxLink ;
for ( int l = 0 ; l < int( vAddedLinks.size()) ; ++ l) {
vAddedLinks[l]->GetLocalBBox( bBoxLink) ;
if (( dCurrY - bBoxLink.GetMin().y) < PockParams.dSideStep + 50 * EPS_SMALL ||
( dCurrY - bBoxLink.GetMax().y) < PockParams.dSideStep + 50 * EPS_SMALL) {
// determino la direzione del Link
Vector3d vtLinkDir( - 25, 0, 0) ;
// recupero gli estremi
Point3d ptS_l ; vAddedLinks[l]->GetStartPoint( ptS_l) ;
Point3d ptE_l ; vAddedLinks[l]->GetEndPoint( ptE_l) ;
if ( ptE_l.x - ptS_l.x < EPS_SMALL)
vtLinkDir.x -= ptS_l.x - ptE_l.x ;
// superficie a rettangolo definita dal Box
PtrOwner<ISurfFlatRegion> pSfrRectUp( GetSurfFlatRegionRectangle( bBoxLink.GetDimX() + 50, PockParams.dSideStep)) ;
pSfrRectUp->Translate(( ptS_l - ORIG) + vtLinkDir) ;
// link sopra o sotto alla curva currente ?
bool bIsDown = true ;
if ( dCurrY - ptS_l.y < EPS_SMALL)
bIsDown = false ;
// classificazione con la superficie a rettangolo
CRVCVECTOR ccClass ;
pSfrRectUp->GetCurveClassification( *vAddedLinks[l], EPS_SMALL, ccClass) ;
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
if (( ccClass[i].nClass == CRVC_IN && bIsDown) ||
( ccClass[i].nClass == CRVC_OUT && ! bIsDown)) {
PtrOwner<ICurveComposite> pCrvInt( ConvertCurveToComposite( vAddedLinks[l]->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
if ( ! IsNull( pCrvInt) || pCrvInt->IsValid())
vAllInt.emplace_back( Release( pCrvInt)) ;
}
}
}
}
// scorro tutti i tratti Attivi
for ( int i = 0 ; i < int( vAllInt.size()) ; ++ i) {
// controllo che il tratto lineare sotto sia sufficientemente lungo
double dLen_iU = EPS_SMALL ;
if ( ! vAllInt[i]->GetLength( dLen_iU) || ( dLen_iU < 1.1 * ( 2 * PockParams.dRad) && i < nDim))
continue ;
// estremi dell'intervallo
Point3d ptS_iU, ptE_iU ;
vAllInt[i]->GetStartPoint( ptS_iU) ;
vAllInt[i]->GetEndPoint( ptE_iU) ;
// la vtDir per Above e Under è sempre invertita rispetto alla linea corrente
// sottraggo questo intervallo a quello originale
IntMinFeed.Subtract( ptS_iU.x, ptE_iU.x) ;
}
// l'intervallo orginale ora conterrà i sottointervalli con feed Minima, tolgo quelli troppo piccoli
Intervals IntMinFeed_noSmall ;
double dParS = EPS_SMALL ; double dParE = EPS_SMALL ;
bool bFound = IntMinFeed.GetFirst( dParS, dParE) ;
while ( bFound) {
if ( dParE - dParS > PockParams.dRad - 5 * EPS_SMALL)
IntMinFeed_noSmall.Add( dParS, dParE) ;
bFound = IntMinFeed.GetNext( dParS, dParE) ;
}
// ora che ho solo tratti abbastanza lunghi, creo l'intervallo complementare
// questo intervallo contiene tutte le feed Massime
Intervals IntMaxFeed ; IntMaxFeed.Set( ptS.x, ptE.x) ;
IntMaxFeed.Subtract( IntMinFeed_noSmall) ;
// tolgo le parti piccole
Intervals IntMaxFeed_noSmall ;
bFound = IntMaxFeed.GetFirst( dParS, dParE) ;
while ( bFound) {
if ( dParE - dParS > PockParams.dRad - 5 * EPS_SMALL)
IntMaxFeed_noSmall.Add( dParS, dParE) ;
bFound = IntMaxFeed.GetNext( dParS, dParE) ;
}
// recupero la direzione principale della curva corrente
Vector3d vtDir = X_AX ;
if ( ptE.x - ptS.x < EPS_SMALL)
vtDir = - X_AX ;
// trasformo questi intervalli nei parametri corrispondenti sulla linea originale
bool bFMax = false ;
bFound = IntMaxFeed_noSmall.GetFirst( dParS, dParE) ;
if ( bFound && IntMaxFeed_noSmall.IsInside( ptS.x + vtDir.x * 20 * EPS_SMALL))
bFMax = true ;
if ( ! bFound && bIsLink) // se non ho intervalli a Feed Massima, allora esco ( con Feed Minima)
return true ;
while ( bFound) {
if ( ! bIsLink) { // se segmento di ZigZag
double du_js = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParS, ptS.y, ptS.z), du_js) ;
pCompo->AddJoint( du_js) ;
double du_je = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParE, ptE.y, ptE.z), du_je) ;
pCompo->AddJoint( du_je) ;
}
bFound = IntMaxFeed_noSmall.GetNext( dParS, dParE) ;
}
// aggiorno le proprietà temporanee con la feed
if ( ! bIsLink) {
for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) {
if ( IsEven( u) == bFMax)
pCompo->SetCurveTempParam( u, GetMaxFeed( PockParams) , 0) ;
else
pCompo->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CheckSimpleOverlap( const ICurve* pCrv, const ICurveComposite* pCrvOri, int& nStat, double dToll)
{
// controllo dei parametri
if (( pCrv == nullptr || pCrvOri == nullptr || dToll < EPS_SMALL))
return false ;
nStat = 1 ; // 0 -> no overlap | 1 -> overlap
// controllo se una sottocurva della composita è abbastanza vicina a tutti i punti trovati
const double nMAX = 10.0 ;
for ( int i = 0 ; i <= nMAX ; ++i) {
double dPar = i / nMAX ;
Point3d ptC ; pCrv->GetPointD1D2( dPar, ICurve::FROM_PLUS, ptC) ;
if ( ! pCrvOri->IsPointOn( ptC, dToll)) {
nStat = 0 ;
return true ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AssignFeedForOpenEdge( ICurveComposite* pCrv, const ICurveComposite* pCrvOF_orig,
const PocketParams& PockParams)
{
// se non rischiesto il calcolo della Feed, lascio quella standard
if ( ! PockParams.bCalcFeed)
return AssignMaxFeed( pCrv, PockParams) ;
// controllo se qualche curva passa sopra ad un lato aperto...
if ( pCrvOF_orig != nullptr) {
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
const ICurve* pCrv_u = pCrv->GetCurve( u) ;
if ( pCrv_u == nullptr)
return false ;
int nStat = -1 ;
if ( CheckSimpleOverlap( pCrv_u, pCrvOF_orig, nStat, 1500 * EPS_SMALL) && nStat == 1) {
double dFeed = GetMinFeed( PockParams) ;
GetFeedForParam( PockParams.dOpenEdgeRad, PockParams, dFeed) ;
pCrv->SetCurveTempParam( u, dFeed, 0) ;
}
}
}
else
AssignMaxFeed( pCrv, PockParams) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
SimplifyCurveByFeeds( ICurveComposite* pCrvCompo, const PocketParams& PockParam, double dToler)
{
// controllo la validità della curva
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0)
return false ;
// NB. Effettuo il Merge dei tratti di curva che presentano la stessa Feed ( mediante la tolleranza)
// curva semplificata
PtrOwner<ICurveComposite> pCrvSimple( CreateCurveComposite()) ;
if ( IsNull( pCrvSimple))
return false ;
/*
- effettuo il Merge dei tratti di curva che presentano la stessa Feed
- imposto la Feed dei tratti piccoli come quella dei tratti precedenti ( per evitare singolarità)
- effettuo un nuovo Merge per uniformare ulteriormente i nuovi tratti ottenuti
*/
double dCurrTempParam ;
int nParStart = 0 ;
double dTempParam ;
double dLen ;
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
pCrvCompo->GetCurveTempParam( i, dTempParam) ;
if ( i == 0) {
dCurrTempParam = dTempParam ;
nParStart = i ;
}
else if ( abs( dCurrTempParam - dTempParam) > dToler) { // se parametri diversi
// recupero il tratto di curva uniforme
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, i))) ;
if ( IsNull( pCrv))
return false ;
// lo semplifico
// recupero la lunghezza del tratto e imposto associo la Feed corrente
// se curva corta -> il parametro di Feed è definito da quello precedente
pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
pCrv->GetLength( dLen) ;
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
pCrvSimple->AddCurve( Release( pCrv)) ;
dCurrTempParam = dTempParam ;
nParStart = i ;
}
}
// ultima parte di curva uniforme ...
PtrOwner<ICurveComposite> pCrvLast( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ;
if ( ! IsNull( pCrvLast)) {
pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
double dLen ; pCrvLast->GetLength( dLen) ;
for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u)
pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
pCrvSimple->AddCurve( Release( pCrvLast)) ;
}
// pulisco la curva originale
pCrvCompo->Clear() ;
// scorro i nuovi tratti ottenuti e ripeto il ragionamento
for ( int i = 0 ; i < pCrvSimple->GetCurveCount() ; ++ i) {
pCrvSimple->GetCurveTempParam( i, dTempParam) ;
if ( i == 0) {
dCurrTempParam = dTempParam ;
nParStart = i ;
}
else if ( abs( dCurrTempParam - dTempParam) > dToler) {
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, i))) ;
if ( IsNull( pCrv))
return false ;
pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
// recupero la lunghezza di tale curve
double dLen ; pCrv->GetLength( dLen) ;
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
pCrvCompo->AddCurve( Release( pCrv)) ; // aggiungo alla curva finale
dCurrTempParam = dTempParam ;
nParStart = i ;
}
}
// ultima parte di curva uniforme ...
pCrvLast.Set( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, pCrvSimple->GetCurveCount()))) ;
if ( ! IsNull( pCrvLast)) {
// semplifico
pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
// recupero la lunghezza di tale curva e imposto la Feed
pCrvLast->GetLength( dLen) ;
for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u)
pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
pCrvCompo->AddCurve( Release( pCrvLast)) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved, bool bIsLink,
bool bFirstOffs, const ICurveComposite* pCrv_orig, const PocketParams& PockParams,
double dToll)
{
// controllo la validità della curva
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
// controllo se il Flag per assegnare la Feed è attivo
if ( ! PockParams.bCalcFeed)
return AssignMaxFeed( pCrv, PockParams) ;
// Se link di conformal ZigZag
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG && bIsLink)
return AssignMaxFeed( pCrv, PockParams) ;
// imposto di Default la Feed minima per ogni sottocurva
AssignMinFeed( pCrv, PockParams) ;
// se non ho una superificie svuotata, allora esco ( con Feed Minima)
if ( pSrfRemoved == nullptr || ! pSrfRemoved->IsValid() || pSrfRemoved->GetChunkCount() == 0) {
// controllo eventuali sovrapposizioni con lati aperti
return AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ;
}
// parametro di Offset per regione svuotata ( restringo la superficie in maniera appropriata)
double dOffs = 0. ;
if ( bIsLink)
dOffs = - PockParams.dRad ;
else if ( PockParams.dRad < PockParams.dSideStep)
dOffs = PockParams.dSideStep - PockParams.dRad ;
// clono la superificie ( valida)
PtrOwner<ISurfFlatRegion> pSrfRemovedOffs( pSrfRemoved->CreateOffsetSurf( dOffs, ICurve::OFF_FILLET)) ;
if ( IsNull( pSrfRemovedOffs) || ! pSrfRemovedOffs->IsValid() || pSrfRemovedOffs->GetChunkCount() == 0)
return true ; // esco ( sempre con Feed Minima)
// classifico le parti interne alla superificie creata solo dagli Offset
CRVCVECTOR ccClass ;
if ( ! pSrfRemovedOffs->GetCurveClassification( *pCrv, EPS_SMALL, ccClass))
return true ; // esco ( sempre con Feed Minima)
// creo la nuova curva con le Feed regolate
PtrOwner<ICurveComposite> pCrv_new( CreateCurveComposite()) ;
if ( IsNull( pCrv_new))
return false ;
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
// recupero il tratto di curva ricavato dalla classificazione
PtrOwner<ICurveComposite> pCrv_sez( ConvertCurveToComposite( pCrv->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
if ( IsNull( pCrv_sez))
continue ; // curva troppo piccola, passo alla successiva
// controllo se interno o fuori alla regione ( regolando quindi la Feed)
double dCurrFeed = GetMinFeed( PockParams) ;
if ( ccClass[i].nClass == CRVC_IN) // se interna alla regione rimossa...
dCurrFeed = GetMaxFeed( PockParams) ;
// assegno tale Feed ad ogni sottocurva ricavata dal tratto di classificazione
for ( int u = 0 ; u < pCrv_sez->GetCurveCount() ; ++ u)
pCrv_sez->SetCurveTempParam( u, dCurrFeed, 0) ;
// aggiungo la nuova curva con la Feed regolata a quella finale
pCrv_new->AddCurve( Release( pCrv_sez)) ;
}
// sotituisco con quanto calcolato
pCrv->Clear() ;
pCrv->AddCurve( Release( pCrv_new)) ;
if ( ! bIsLink) { // ---------------- NEL CASO DI OFFSET ----------------
// creo un intervallo con tutte le Feed Minime
Intervals IntMinFeed ;
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
double dParam ; pCrv->GetCurveTempParam( u, dParam, 0) ;
if ( abs( dParam - GetMinFeed( PockParams)) < 5 * EPS_SMALL)
IntMinFeed.Add( u, u + 1) ;
}
double dParS = EPS_SMALL ; double dParE = EPS_SMALL ;
// a questo intervallo tolgo tutti i sottotratti di lunghezza inferiore alla tolleranza richiesta
Intervals IntMinFeed_noSmall ;
bool bFound = IntMinFeed.GetFirst( dParS, dParE) ;
while ( bFound) {
PtrOwner<ICurve> pCrv_Crv( pCrv->CopyParamRange( dParS, dParE)) ;
double dLen = EPS_SMALL ; pCrv_Crv->GetLength( dLen) ;
if ( dLen > dToll + 5 * EPS_SMALL)
IntMinFeed_noSmall.Add( dParS, dParE) ;
bFound = IntMinFeed.GetNext( dParS, dParE) ;
}
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
if ( ! IntMinFeed_noSmall.IsInside( u + 0.5))
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
}
}
else { // ---------------- NEL CASO DI LINK ----------------
// le curve con lunghezza < dToll vanno modificate
for ( int j = 0 ; j < pCrv->GetCurveCount() ; ++ j) {
double dLen = EPS_SMALL ;
pCrv->GetCurve( j)->GetLength( dLen) ;
if ( dLen < dToll + 5 * EPS_SMALL) {
// se curva piccola, ricavo il tratto successivo lungo quanto il diametro
double dLenStart ;
pCrv->GetLengthAtParam( j, dLenStart) ;
double dLenEnd = dLenStart + dLen + 2 * PockParams.dRad ;
double dUE = pCrv->GetCurveCount() ;
pCrv->GetParamAtLength( dLenEnd, dUE) ;
bool bFound = false ;
for ( int k = j + 1 ; k < int( ceil( dUE)) ; ++ k) {
double dLenH = EPS_SMALL ;
pCrv->GetCurve( k)->GetLength( dLenH) ;
if ( dLenH < dToll + 5 * EPS_SMALL)
continue ;
// cerco tra le curve successive vicine se ne trovo una con Feed Minima
double dParam ;
pCrv->GetCurveTempParam( k, dParam, 0) ;
if ( abs( dParam - GetMinFeed( PockParams)) < 5 * EPS_SMALL) {
pCrv->SetCurveTempParam( k, dParam, 0) ;
bFound = true ;
break ;
}
}
if ( ! bFound && j != 0) {
// arrivato qui, so che successivamente non ho curve con Feed Minima vicine
double dParam ; pCrv->GetCurveTempParam( j - 1, dParam, 0) ;
if ( abs( dParam - GetMaxFeed( PockParams)) < 5 * EPS_SMALL)
// se anche la precedente ha Feed Massima -> la curva u-esima è piccola ed Isolata
pCrv->SetCurveTempParam( j, dParam, 0) ;
}
else
pCrv->SetCurveTempParam( j, GetMinFeed( PockParams), 0) ;
}
}
}
if ( bFirstOffs)
AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
AssignFeedSpiralOpt( const int nOptType, const PocketParams& PockParams, ICurveComposite* pCrv )
{
// controllo della curva corrente
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
return false ;
// controllo se il Flag per assegnare la Feed è attivo
if ( ! PockParams.bCalcFeed)
return AssignMaxFeed( pCrv, PockParams) ;
switch ( PockParams.nType) {
case POCKET_SPIRALIN :
if ( nOptType == 0) { // Spirale dall'Esterno
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
if ( u == 0) // prima circonferenza
pCrv->SetCurveTempParam( 0, GetMinFeed( PockParams), 0) ;
else // semi cerchi in tangenza
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
}
}
else if ( nOptType == 1) { // Trapezoidi
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
}
break ;
/* NB. Essendo la funzione CalcSpiral richiamata sia per lo SpiralIN che per lo SpiralOUT le curve sono sempre
orientate nello stesso modo, solamente alla fine viene invertita la curva finale per la svuotatura... */
case POCKET_SPIRALOUT :
if ( nOptType == 0) { // Spiral verso l'esterno
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
if ( u > pCrv->GetCurveCount() - 3) // prime semi circonferenze
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
else
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
}
}
else if ( nOptType == 1) { // Trapezoidi
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
}
break ;
default :
break ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
ResetCurveTempProps( ICurveComposite* pCrvCompo)
{
// controllo parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// scorro le curve
for ( int nU = 0 ; nU < pCrvCompo->GetCurveCount() ; ++ nU) {
pCrvCompo->SetCurveTempProp( nU, 0, 0) ;
pCrvCompo->SetCurveTempProp( nU, 0, 1) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
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 bool
ExtendPath( ICurveComposite* pCompo, const ISurfFlatRegion* pSfr, const PocketParams& PockParams,
const Vector3d& vtFirstOut, bool bEndOrStart, bool& bOkExtended)
{
/*
Estensione della curva pCompo con un segmento lineare.
bEndOrStart: definisce se la curva va estesa all'inizio o alla fine
vtFirstOut: prima direzione su cui tentare l'entrata, se questa non andasse bene
ruoto progressivamente di 90°
La curva viene estesa di un tratto lineare in direzione vtRotOut per una lunghezza
pari a Rad + OffsR + OpenMinSafe.
*/
// controllo dei parametri
if ( pCompo == nullptr || ! pCompo->IsValid() ||
pSfr == nullptr || ! pSfr->IsValid())
return false ;
bOkExtended = false ;
// recupero il punto iniziale/finale della curva 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() ;
}
// ruoto il versore di uscita cercando un'entrata valida
Vector3d vtRotOut = vtFirstOut ;
double dMinDist = PockParams.dRad + PockParams.dRadialOffset ;
for ( int i = 0 ; i < 4 ; ++ i) {
// evito il 180deg ( se c'è tangenza )
if ( ! AreOppositeVectorEpsilon( vtTan, vtRotOut, 25. * EPS_SMALL)) {
// calcolo il punto di caduta dell'utensile
Point3d ptFall = pt + vtRotOut * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
// controllo che sia sufficientemente distante dalla superficie limite
bool bInside = true ;
bool bOkOut = true ;
if ( PockParams.SfrLimit.IsValid())
bOkOut = ( IsPointInsideSurfFr( ptFall, &PockParams.SfrLimit, dMinDist, bInside) && ! bInside) ;
if ( bOkOut)
bOkOut = ( IsPointInsideSurfFr( ptFall, pSfr, dMinDist, bInside) && ! bInside) ;
if ( bOkOut) {
// aggiungo al ritorno l'uscita
pCompo->AddLine( ptFall, bEndOrStart) ;
pCompo->SetCurveTempProp( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0), TEMP_PROP_OUT_START, 0) ;
pCompo->SetCurveTempParam( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0),
( bEndOrStart ? GetMaxFeed( PockParams) : GetMinFeed( PockParams)), 0) ;
bOkExtended = true ;
return true ;
}
}
vtRotOut.Rotate( Z_AX, - ANG_RIGHT) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustContourStart( ICurveComposite* pCompo, const PocketParams& PockParams)
{
// controllo dei parametri
if ( pCompo == nullptr || ! pCompo->IsValid())
return false ;
// cerco il tratto lineare chiuso più lungo ...
int i = 0 ; // <--- indice della curva corrente
int nMax = - 1 ;
double dLenMax = 0 ;
const ICurve* pCrv = pCompo->GetFirstCurve() ;
while ( pCrv != nullptr) {
double dLen ;
if ( pCrv->GetType() == CRV_LINE && pCrv->GetTempProp() == TEMP_PROP_CLOSE_EDGE &&
pCrv->GetLength( dLen) && dLen > dLenMax) {
dLenMax = dLen ;
nMax = i ;
}
++ i ;
pCrv = pCompo->GetNextCurve() ;
}
// se non trovato o troppo corto, cerco il tratto chiuso più lungo in generale ( non lineare)
if ( nMax < 0 || dLenMax < 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
GetFirstOffsCrvFromSfr( const ISurfFlatRegion* pSfr, double dOffs, ICRVCOMPOPOVECTOR& vCrvFirstOffs)
{
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid())
return false ;
vCrvFirstOffs.clear() ;
// creo la regione mediante Offset
PtrOwner<ISurfFlatRegion> pSfrOffs( pSfr->CreateOffsetSurf( dOffs, ICurve::OFF_FILLET)) ;
if ( IsNull( pSfrOffs) || ! pSfrOffs->IsValid())
return false ;
// inserisco le curve orientante
return ( GetSfrCrvCompoLoops( pSfrOffs, vCrvFirstOffs)) ;
}
//---------------------------------------------------------------------------
static bool
CreateSurfFrIncidence( const ICurveComposite* pCrv, const PocketParams& PockParams,
const Vector3d& vtTanS_, const Vector3d& vtTanE_, const double dRad,
ISurfFlatRegion* pSfrInc)
{
// controllo dei parametri
if ( pCrv == nullptr || ! pCrv->IsValid())
return false ;
pSfrInc->Clear() ;
// se la curva è chiusa, allora la regione di incidenza è già definita
if ( pCrv->IsClosed()) {
PtrOwner<ISurfFlatRegion> pSfrInc_tmp( GetSurfFlatRegionFromFatCurve( pCrv->Clone(), dRad + EPS_SMALL, false, false)) ;
if ( IsNull( pSfrInc_tmp) || ! pSfrInc_tmp->IsValid())
return false ;
pSfrInc->CopyFrom( pSfrInc_tmp) ;
return ( pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0) ;
}
// creo la curva di Offset esterna ( deve esistere ed essere valida)
OffsetCurve OffsCrv ;
PtrOwner<ICurve> pOffsExt( CreateCurveComposite()) ;
if ( IsNull( pOffsExt) ||
! OffsCrv.Make( pCrv, dRad, PockParams.nOffsType) ||
! pOffsExt.Set( OffsCrv.GetLongerCurve()) ||
IsNull( pOffsExt))
return false ;
// creo la curva di Offset interna ( se esiste e valida...)
PtrOwner<ICurve> pOffsInt( CreateCurveComposite()) ;
if ( IsNull( pOffsInt))
return false ;
if ( OffsCrv.Make( pCrv, - dRad, PockParams.nOffsType)) {
PtrOwner<ICurve> pMyCrv( OffsCrv.GetLongerCurve()) ;
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
pCrvOpenOffs -> tratto aperto corrente già Offsettato verso l'esterno
dParS -> parametro sulla pCrvCompo per l'inizio del tratto aperto
dParE -> parametro sulla pCrvCompo per la fine del tratto aperto
vtTanS -> vettore di tangenza finale tratto chiuso precedente
vtTanE -> vettore di tangenza iniziale ( invertito ) del tratto chiuso successivo
dRad -> raggio di Offset per la regione di incidenza
dDiamJ -> ampiezza delle curve a fagiolo per estendere la regione di incidenza
pCrvRes -> curva da restituire ( inizialmente è il tratto aperto sulla pCrvCompo ;
questa curva sarà l'estensione del lato aperto, adattandosi alla geometria
dei chiusi ( non posso vedere solo i chiusi adiacenti all'aperto, devo considerare TUTTI i chiusi
della pCrvCompo
pStmVol -> Volume di svuotatura
pStm_Part -> Part corrente
*/
// controllo la validità dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 ||
pCrvBorder == nullptr || ! pCrvBorder->IsValid() || pCrvBorder->GetCurveCount() == 0)
return false ;
// definisco la regione di incidenza
PtrOwner<ISurfFlatRegion> pSfrInc( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrInc))
return false ;
if ( ! CreateSurfFrIncidence( pCrvBorder, PockParams, vtTanS, vtTanE, 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 c = 0 ; c < int( vCrvToCheck.size()) ; ++ c) {
// 1) recupero la curva corrente
PtrOwner<ICurveComposite> pCrvCurr( vCrvToCheck[c]->Clone()) ;
if ( IsNull( pCrvCurr) || ! pCrvCurr->IsValid())
return false ;
// 2) ricavo i tratti con proprietà uniformi ( Aperti/Chiusi )
ICRVCOMPOPOVECTOR vpCrvs ;
if ( ! GetHomogeneousParts( pCrvCurr, PockParams, vpCrvs))
return false ;
// 3) considero solo i tratti chiusi
for ( int cl = 0 ; cl < int( vpCrvs.size()) ; ++ cl) {
if ( vpCrvs[cl]->GetTempProp() == TEMP_PROP_OPEN_EDGE)
continue ;
// 4) effettuo l'Offset della curva di metà dDiamJ
OffsetCurve OffsCrv ;
if ( ! OffsCrv.Make( vpCrvs[cl], - dDiamJ * 0.5 - 20 * EPS_SMALL, PockParams.nOffsType))
return false ;
// 5) scorro tutte le curve di Offset che si sono formate, prendendo sempre la più lunga tra le rimanenti
PtrOwner<ICurve> pOffLongestCrv( OffsCrv.GetLongerCurve()) ;
while ( ! IsNull( pOffLongestCrv)) {
// 6) creo la regione di incidenza di tale curva ( "Curva a fagiolo")
bool bSquareEnds = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ;
bool bSquareMids = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ;
PtrOwner<ISurfFlatRegion> pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ * 0.5, bSquareEnds, bSquareMids)) ;
if ( IsNull( pSfrBean) || ! pSfrBean->IsValid())
return false ;
// inverto se necessario
if ( AreOppositeVectorApprox( pSfrBean->GetNormVersor(), pSfrInc->GetNormVersor()))
pSfrBean->Invert() ;
// 7) se la "Regione a fagiolo" non influenza la regione di incidenza, la transcuro
bool bDiscard = false ;
if ( ! bIsIsland) { // se tratto un loop esterno
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 ;
// piccolo Offset per sicurezza
pSfrLimit->Offset( 50 * EPS_SMALL, ICurve::OFF_FILLET) ;
pSfrInc->Subtract( *pSfrLimit) ; // rimuovo la regione limite
// può capitare che la regione Limite mi crei più Chunk sulla pSfrInc
// I chunk da togliere sono tutti quelli che si sono separati dalla pCrvBorder
// NB. Il mio obiettivo è quello di avere un'unica curva con estremi i due tratti chiusi estremanti
// 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, esco di Step/2 per pulire gli aperti
if ( PockParams.nType == POCKET_ZIGZAG && PockParams.bSmooth)
dRad += max( PockParams.dSideStep / 2, PockParams.dRad / 8) ;
// salvo il raggio trovato
PockParams.dOpenEdgeRad = dRad ;
// ricavo i tratti omogenei
ICRVCOMPOPOVECTOR vpCrvs ;
if ( ! GetHomogeneousParts( pCrvCompo, PockParams, vpCrvs))
return false ;
// NB. Il primo tratto è la prima metà del tratto aperto più lungo ( se esiste )
// Offset esterno per CurveJ
double dDiamJ = 1.05 * ( dDiam) + 2 * dOffR ;
if ( abs( PockParams.dMaxOpenEdgeRad) > 0)
dDiamJ = 100 * EPS_SMALL ;
// NB. 1.05 serve per eccedere leggermente, in modo che il contro-offset della prima curva di svuotatura presenti
// dei piccoli archi tra i chiusi e gli aperti ( in modo da svuotare bene lungo il chiuso)
// curva finale da restituire
PtrOwner<ICurveComposite> pCrvCompo_final( CreateCurveComposite()) ;
if ( IsNull( pCrvCompo_final))
return false ;
// parametro iniziale del tratto corrente, sulla curva originale
double dParS = 0. ;
// scorro tutti i tratti omogenei nel vettore
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
// recupero la proprietà della curva composita
int nCurrTmpProp = vpCrvs[i]->GetTempProp() ;
// aggiorno i parametri del lato aperto corrente sulla curva originale
double dParE = dParS + 1. * vpCrvs[i]->GetCurveCount() ;
// se aperta
if ( nCurrTmpProp == TEMP_PROP_OPEN_EDGE) {
// ricavo la tangenze dei lati chiusi agli estremi di questa curva
Vector3d vtTanS = V_INVALID ;
Vector3d vtTanE = V_INVALID ;
if ( i != 0) {
if ( ! vpCrvs[i-1]->GetEndDir( vtTanS) || // tangente finale del chiuso precedente
! vpCrvs[( i + 1) % int( vpCrvs.size())]->GetStartDir( vtTanE)) // tangente iniziale del chiuso successivo...
return false ;
vtTanE.Invert() ; // invertita
}
if ( ! AdjustOpenEdge( pCrvCompo, vCrvIsl, dParS, dParE, vtTanS, vtTanE, dRad, dDiamJ, PockParams, vpCrvs[i]))
return false ;
}
// assegno le proprietà di lato Aperto/Chiuso per la curva corrente
for ( int u = 0 ; u < vpCrvs[i]->GetCurveCount() ; ++ u)
vpCrvs[i]->SetCurveTempProp( u, nCurrTmpProp, 0) ;
// aggiungo la curva ricavata ( se chiusa -> la copio, se aperta -> copio l'estesa)
if ( ! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone())) {
// per sicurezza, se gli estremi non coincidono, creo un piccolo raccordo lineare
Point3d ptH ; vpCrvs[i]->GetStartPoint( ptH) ;
if ( ! pCrvCompo_final->AddLine( ptH) ||
! pCrvCompo_final->SetCurveTempProp( pCrvCompo_final->GetCurveCount() - 1, 1, 0) ||
! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone()))
return false ;
}
// aggiorno
dParS = dParE ;
}
// non dovrebbe esserci un gap, ma meglio prevenire problemi
pCrvCompo_final->Close() ;
// sostituisco
pCrvCompo->Clear() ;
pCrvCompo->CopyFrom( pCrvCompo_final) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
ChainCompoCurves( ICRVCOMPOPOVECTOR& vCrvCompo)
{
/* concatenamento delle curve composite */
if ( int( vCrvCompo.size() < 2))
return true ;
// controllo validità delle curve
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i)
if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid())
return false ;
// preparo i dati per il concatenamento
bool bFirst = true ;
Point3d ptNear = ORIG ;
double dToler = 500 * EPS_SMALL ; // leggermente maggiore della tolleranza sulla superficie limite
ChainCurves chainC ;
chainC.Init( false, dToler, int( vCrvCompo.size())) ;
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
// recupero i dati della curva necessari al concatenamento e li assegno
Point3d ptStart, ptEnd ;
Vector3d vtStart, vtEnd ;
if ( ! vCrvCompo[i]->GetStartPoint( ptStart) || ! vCrvCompo[i]->GetStartDir( vtStart) ||
! vCrvCompo[i]->GetEndPoint( ptEnd) || ! vCrvCompo[i]->GetEndDir( vtEnd))
return false ;
if ( ! chainC.AddCurve( int( i + 1), ptStart, vtStart, ptEnd, vtEnd))
return false ;
// se prima curva, assegno inizio della ricerca
if ( bFirst) {
ptNear = ptStart + 10 * EPS_SMALL * vtStart ;
bFirst = false ;
}
}
// vettore delle curve composite risultante
ICRVCOMPOPOVECTOR vCrvCompoChained ;
// recupero i percorsi concatenati
INTVECTOR vnInd ;
while ( chainC.GetChainFromNear( ptNear, true, vnInd)) {
// creo una curva composita
PtrOwner<ICurveComposite> pCrvCompo( CreateCurveComposite()) ;
if ( IsNull( pCrvCompo))
return false ;
// recupero le curve semplici e le inserisco nella curva composita
for ( size_t i = 0 ; i < vnInd.size() ; ++ i) {
int nId = abs( vnInd[i]) - 1 ;
bool bInvert = ( vnInd[i] < 0) ;
// se necessario, la inverto
if ( bInvert)
vCrvCompo[nId]->Invert() ;
// la aggiungo alla curva composta
if ( ! pCrvCompo->AddCurve( ::Release( vCrvCompo[nId]), true, dToler))
return false ;
}
// aggiorno il nuovo punto vicino
if ( pCrvCompo->GetCurveCount() > 0) {
pCrvCompo->GetEndPoint( ptNear) ;
vCrvCompoChained.emplace_back( Release( pCrvCompo)) ;
}
}
swap( vCrvCompoChained, vCrvCompo) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
ChangePtStartForSinglePocketCurve( ICurveComposite* pCrvCompo, const PocketParams& PockParams,
const Point3d& ptRef, bool bInvertOpenCrv)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// se la curva è aperta
if ( ! pCrvCompo->IsClosed() && bInvertOpenCrv) {
// se non ho un punto di riferimento, allora non faccio nulla
if ( ! ptRef.IsValid())
return true ;
// se ho un punto di riferimento controllo quale estremo è più vicino
Point3d ptStart ; pCrvCompo->GetStartPoint( ptStart) ;
Point3d ptEnd ; pCrvCompo->GetEndPoint( ptEnd) ;
if ( SqDist( ptRef, ptEnd) < SqDist( ptRef, ptStart))
pCrvCompo->Invert() ;
}
// se la curva è chiusa
else {
// se non ho un punto di riferimento, scelgo il tratto più lungo
if ( ! ptRef.IsValid()) {
ResetCurveTempProps( pCrvCompo) ;
AdjustContourStart( pCrvCompo, PockParams) ;
}
// se ho un punto di riferimento
else {
// cerco il punto più vicino
DistPointCurve DistPtCrv( ptRef, *pCrvCompo) ;
double dPar = 0. ;
int nFlag ;
if ( ! DistPtCrv.GetParamAtMinDistPoint( 0, dPar, nFlag))
return false ;
pCrvCompo->ChangeStartPoint( dPar) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AdvanceExtendCurves( ICRVCOMPOPOVECTOR& vCrvCompo, const ISurfFlatRegion* pSfr,
const PocketParams& PockParams, bool bInvertOpenCrv)
{
// se non ho curve, allora non faccio nulla
if ( vCrvCompo.empty())
return true ;
// per ogni curva composita...
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
// controllo validità della curva
if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid())
return false ;
// cambio il punto iniziale della curva
if ( ! ChangePtStartForSinglePocketCurve( vCrvCompo[i], PockParams, PockParams.ptStart, 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 < int( vCrvCompo.size()) ; ++ i) {
// controllo se la curva è chiusa o aperta
bool bIsClosed = vCrvCompo[i]->IsClosed() ;
// calcolo il numero massimo di punti in cui testare l'entrata da fuori
int nMaxIter = ( bIsClosed ? MAX_ITER : 1) ;
double dLen = EPS_SMALL ;
if ( nMaxIter == MAX_ITER)
vCrvCompo[i]->GetLength( dLen) ;
// scorro il numero di punti su cui tentare l'entrata da fuori
for ( int j = 0 ; j < nMaxIter ; ++ j) {
if ( j > 0) {
double dParAtLen = EPS_SMALL ;
vCrvCompo[i]->GetParamAtLength( dLen / nMaxIter, dParAtLen) ;
vCrvCompo[i]->ChangeStartPoint( dParAtLen) ;
}
// ricavo vettore tangente iniziale e provo ad estendere
Vector3d vtStart ; vCrvCompo[i]->GetStartDir( vtStart) ;
vtStart.Invert() ;
bool bIsStartExtended = false ;
if ( ! ExtendPath( vCrvCompo[i], pSfr, PockParams, vtStart, false, bIsStartExtended))
return false ;
// se aperta, controllo la fine
if ( ! bIsClosed) {
// ricavo vettore tangente finale e provo ad estendere
Vector3d vtEnd ; vCrvCompo[i]->GetEndDir( vtEnd) ;
bool bIsEndExtended = false ;
if ( ! ExtendPath( vCrvCompo[i], pSfr, PockParams, vtEnd, true, bIsEndExtended))
return false ;
if ( bIsEndExtended)
break ;
}
if ( bIsStartExtended)
break ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPocketCurvesByClosedEdges( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvCompoRes, bool& bAllRemoved)
{
// controllo parametri
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
return false ;
bAllRemoved = false ;
// controllo se la superficie si annulla con un controOffset del raggio utensile
double dMaxOffs ;
pSfrChunk->GetMaxOffset( dMaxOffs) ;
if ( dMaxOffs > PockParams.dRad + 200 * EPS_SMALL)
return true ;
/*
NB. Si poteva calcolare la Fat curve del raggio utensile dell'Offset di tutti i chiusi e vedere
se l'unione di queste regioni copriva interamente il Chunk attuale... così facendo rischio di
trascurare delle parti che spariscono facendo il primo Offset dei chiusi; il controllo va fatto
quindi con un contro-offset della regione complessiva
*/
// controllo se il Chunk ha delle isole
bool bHasIslands = ( pSfrChunk->GetLoopCount( 0) > 1) ;
// recupero il Loop esterno
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
return false ;
// recupero i tratti con proprietà uniformi
ICRVCOMPOPOVECTOR vpCrvs ;
GetHomogeneousParts( pCrvExtLoop, PockParams, vpCrvs) ;
if ( vpCrvs.size() > 1) { // unisco il primo e l'ultimo se estremi compatibili
// NB. GetHomogeneousParts() cambia il punto di inizio nel lato chiuso più lungo ( se presente)
Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ;
Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ;
if ( AreSamePointApprox( ptS, ptE)) {
vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ;
vpCrvs.erase( vpCrvs.end() - 1) ;
}
}
// controllo se il loop Esterno è uniforme ( quindi tutto chiuso o tutto aperto)
bool bExtAllClose = false ;
bool bExtAllOpen = false ;
if ( int( vpCrvs.size()) == 1) {
if ( vpCrvs[0]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
bExtAllClose = true ;
else
bExtAllOpen = true ;
}
// NB. Se regione estena tutta chiusa o aperta e senza isole... allora non faccio nulla ( es caso trapezi)
// Se tutta chiusa -> Chunk non svuotabile, il contro-offset del raggio utensile annulla la regione
// Se tutta aperta -> La regione è definita mediante Offset esterno del contorno aperto
if ( ( bExtAllClose || bExtAllOpen) && ! bHasIslands)
return true ;
// Se il contorno esterno è misto -> non devo avere isole
if ( ! bExtAllClose && ! bExtAllOpen && bHasIslands)
return true ;
// Se il contorno esterno è tutto chiuso e ho più di un'isola non faccio nulla
if ( bExtAllClose && pSfrChunk->GetLoopCount( 0) > 2)
return true ;
// scorro i chiusi e sottraggo la regione da loro creata mediante l'utensile
// NB. Queste curve sono orientate in maniera corretta
ICRVCOMPOPOVECTOR vCrvCompoRes_tmp ; // salvo il vettore di curve offsettate del raggio utensile
Voronoi myVRONI ;
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ 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 < int( vCrvVroniOffs.size()) ; ++ i)
vCrvCompoRes_tmp.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 = -1 ;
int nPrecTmpProp = -1 ;
bool bIsMixed = false ;
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ;
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
return false ;
for ( int u = 0 ; u < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ u) {
pCrvIsl->GetCurveTempProp( u, nCurrTmpProp, 0) ;
bIsMixed = ( u != 0 && nCurrTmpProp != nPrecTmpProp) ;
nPrecTmpProp = nCurrTmpProp ;
}
// se proprità non uniformi -> tutta chiusa ( isole non uniformi non sono definite)
if ( bIsMixed) {
for ( int u = 0 ; u < pCrvIsl->GetCurveCount() ; ++ u)
pCrvIsl->SetTempProp( u, TEMP_PROP_CLOSE_EDGE) ;
nCurrTmpProp = 0 ; // aggiorno il Flag
}
// 2) e 3)
bValidIslands = ( ( nCurrTmpProp == TEMP_PROP_CLOSE_EDGE && bExtAllOpen) ||
( nCurrTmpProp == TEMP_PROP_OPEN_EDGE && bExtAllClose)) ;
}
if ( ! bValidIslands)
return true ;
// nel caso 2) devo inserire le curve
if ( bExtAllOpen) {
// creo una regione formata solo dalla isole
PtrOwner<ISurfFlatRegion> pSrfIslands( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfIslands))
return false ;
for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) ; ++ i) {
pSrfIslands->AddExtLoop( pSfrChunk->GetLoop( 0, i)) ;
vpCrvs.emplace_back( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ;
}
pSrfIslands->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET) ;
for ( int nC = 0 ; nC < pSrfIslands->GetChunkCount() ; ++ nC)
for ( int nL = 0 ; nL < pSrfIslands->GetLoopCount( nL) ; ++ nL)
vCrvCompoRes_tmp.emplace_back( ConvertCurveToComposite( pSrfIslands->GetLoop( nC, nL))) ;
}
}
// se non ho ottenuto curve, allora ho finito
if ( vCrvCompoRes_tmp.empty())
return true ;
// controllo che effettivamente l'utensile svuoti la regione
PtrOwner<ISurfFlatRegion> pSfrNoRemoved( CloneSurfFlatRegion( pSfrChunk)) ;
if ( IsNull( pSfrNoRemoved) || ! pSfrNoRemoved->IsValid())
return false ;
for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) {
PtrOwner<ISurfFlatRegion> pSfrRemoved( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vCrvCompoRes_tmp[i]), PockParams.dRad + PockParams.dRadialOffset + 50 * EPS_SMALL, false, false)) ;
if ( ! IsNull( pSfrRemoved) && pSfrRemoved->IsValid()) {
if ( AreOppositeVectorEpsilon( pSfrRemoved->GetNormVersor(), pSfrNoRemoved->GetNormVersor(), 25. * EPS_SMALL))
pSfrRemoved->Invert() ;
pSfrNoRemoved->Subtract( *pSfrRemoved) ;
}
}
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) {
PtrOwner<ISurfFlatRegion> pSfrRemoved( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vpCrvs[i]), PockParams.dRad + PockParams.dRadialOffset + 50 * EPS_SMALL, false, false)) ;
if ( ! IsNull( pSfrRemoved) && pSfrRemoved->IsValid()) {
if ( AreOppositeVectorEpsilon( pSfrRemoved->GetNormVersor(), pSfrNoRemoved->GetNormVersor(), 25. * EPS_SMALL))
pSfrRemoved->Invert() ;
pSfrNoRemoved->Subtract( *pSfrRemoved) ;
}
}
}
if ( pSfrNoRemoved->IsValid() && pSfrNoRemoved->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()) { // se esiste, allora classifico
// recupero la superficie limite
pSfrLimit.Set( PockParams.SfrLimit.Clone()) ;
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
return false ;
// effettuo un Offset della regione, tutto ciò che dista più del raggio utensile non la rovina
pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset - 250 * EPS_SMALL, ICurve::OFF_FILLET) ; // c'è un offset radiale, quindi tengo tolleranza alta
ICRVCOMPOPOVECTOR vCrvCompoRes_tmp_Splitted ;
for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) {
CRVCVECTOR ccClass ;
if ( pSfrLimit->GetCurveClassification( *vCrvCompoRes_tmp[i], EPS_SMALL, ccClass)) {
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
if ( ccClass[j].nClass == CRVC_OUT) {
PtrOwner<ICurveComposite> pCrvRes( ConvertCurveToComposite( vCrvCompoRes_tmp[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
if ( ! IsNull( pCrvRes) && pCrvRes->IsValid())
vCrvCompoRes_tmp_Splitted.emplace_back( Release( pCrvRes)) ;
}
}
}
}
// se non ho curve, allora esco
if ( vCrvCompoRes_tmp_Splitted.empty())
return true ;
// altrimenti aggiorno il vettore di curve con quelle solo esterne alla regione limite
swap( vCrvCompoRes_tmp, vCrvCompoRes_tmp_Splitted) ;
}
// salvo come primo tempParam l'offset massimo della regione ( servirà per la Feed)
for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) {
vCrvCompoRes_tmp[i]->SetTempParam( dMaxOffs, 0) ;
// se richiesta inversione della curva, allora inverto
if ( PockParams.bInvert)
vCrvCompoRes_tmp[i]->Invert() ;
vCrvCompoRes.emplace_back( Release( vCrvCompoRes_tmp[i])) ; // aggiungo la curva al vettore
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetSinglePocketingCurves( ISurfFlatRegion* pSfr, const 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> pSfr_clone( CloneSurfFlatRegion( pSfr)) ;
if ( IsNull( pSfr_clone) || ! pSfr_clone->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 (non inverto le curve aperte)
if ( ! AdvanceExtendCurves( vCrvSingleCurves, pSfr_clone, PockParams, false))
return false ;
// imposto le Feed per tali curve se richiesto
for ( int i = 0 ; i < int( vCrvSingleCurves.size()) ; ++ 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 nTempProp_firstCrv = vCrvSingleCurves[i]->GetFirstCurve()->GetTempProp( 0) ;
int nTempProp_lastCrv = vCrvSingleCurves[i]->GetLastCurve()->GetTempProp( 0) ;
// se estremi entrambi estesi
if ( nTempProp_firstCrv == TEMP_PROP_OUT_START && nTempProp_lastCrv == TEMP_PROP_OUT_START) {
if ( PockParams.ptStart.IsValid()) {
Point3d ptStart ; vCrvSingleCurves[i]->GetStartPoint( ptStart) ;
double dSqDist_first = SqDist( PockParams.ptStart, ptStart) ;
Point3d ptEnd ; vCrvSingleCurves[i]->GetEndPoint( ptEnd) ;
double dSqDist_last = SqDist( PockParams.ptStart, ptEnd) ;
if ( dSqDist_last < dSqDist_first)
vCrvSingleCurves[i]->Invert() ;
}
}
// se invece l'estensione è alla fine
else if ( nTempProp_lastCrv == 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 ;
// creo la superificie per la Feed
PtrOwner<ISurfFlatRegion> pSrfRemoved( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfRemoved))
return false ;
ICRVCOMPOPOVECTOR vLinksDone ;
Vector3d vtDir = pArc->GetStartVersor() ;
pMCrv->AddCurve( Release( pArc)) ;
// se richiesta percorrenza invertita
if ( PockParams.bInvert)
pMCrv->Invert() ;
// se raggio esterno maggiore dell'interno
if ( dOutRad > dIntRad + 10 * EPS_SMALL) {
// aggiungo le semicirconferenze della spirale ( devono essere in numero dispari)
int nStep = int( ceil( ( dOutRad - dIntRad) / ( 0.5 * PockParams.dSideStep))) ;
if ( nStep % 2 == 0)
nStep += 1 ;
double dStep = ( dOutRad - dIntRad) / nStep ;
for ( int i = 1 ; i <= nStep ; ++ i) {
if ( ! ( i % 2 == 0))
pMCrv->AddArcTg( ptCen - vtDir * ( dOutRad - i * dStep)) ;
else
pMCrv->AddArcTg( ptCen + vtDir * ( dOutRad - i * dStep)) ;
}
// aggiungo la circonferenza interna
pMCrv->AddArcTg( ptCen + vtDir * dIntRad) ;
pMCrv->AddArcTg( ptCen - vtDir * dIntRad) ;
}
// verifico il percorso di lavoro
if ( pMCrv->GetCurveCount() == 0)
return false ;
// assegno la Feed
AssignFeedSpiralOpt( 0, PockParams, pMCrv) ;
return true ;
}
//----------------------------------------------------
static bool
CalcTrapezoidSpiralLocalFrame( ICurveComposite* pCrvTrap, const Vector3d& vtDir, Frame3d& frLoc)
{
// cerco i lati paralleli a vtDir
int nBaseId = -1 ;
for ( int i = 0 ; i < pCrvTrap->GetCurveCount() ; i ++) {
Vector3d vtEdge ;
pCrvTrap->GetCurve( i)->GetStartDir( vtEdge) ;
if ( AreSameOrOppositeVectorApprox( vtEdge, vtDir)) {
nBaseId = i ;
break ;
}
}
if ( nBaseId != 0 && nBaseId != 1)
return false ;
// imposto come lato iniziale per la curva uno dei lati paralleli a vtDir
pCrvTrap->ChangeStartPoint( nBaseId) ;
Point3d ptOrig ; pCrvTrap->GetStartPoint( ptOrig) ;
Vector3d vtX ; pCrvTrap->GetStartDir( vtX) ;
return frLoc.Set( ptOrig, Z_AX, vtX) ;
}
//----------------------------------------------------------------------------
static bool
GetBoxCrvOptTrap( const int nCrv, const ICurveComposite* pCrvCompo, const double dDiam, int& nType,
ICurveComposite* pCrvBox)
{
nType = - 1 ;
// prendo la curva
const ICurve* pCrvCurr = pCrvCompo->GetCurve( nCrv) ;
if ( pCrvCurr == nullptr)
return false ;
// controllo se lineare, altrimenti passo alla successiva
if ( pCrvCurr->GetType() != CRV_LINE)
return true ;
// prendo la direzione del tratto lineare
Vector3d vtDir ; pCrvCurr->GetStartDir( vtDir) ;
// prendo il punto iniziale
Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ;
// creo il riferimento basato su questo tratto
Frame3d frTrap ; frTrap.Set( ptStart, Z_AX, vtDir) ;
if ( ! frTrap.IsValid())
return false ;
// porto la curva Compo ( clonandola) nel frame e calcolo il suo BBox
BBox3d BBox ;
PtrOwner<ICurveComposite> pCrvCompo_c( pCrvCompo->Clone()) ;
if ( IsNull( pCrvCompo_c) || ! pCrvCompo_c->IsValid() ||
! pCrvCompo_c->ToLoc( frTrap) ||
! pCrvCompo_c->GetLocalBBox( BBox))
return false ;
// controllo dimY e dimX per il box
bool bDimYOk = BBox.GetDimY() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ;
bool bDimXOk = BBox.GetDimX() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ;
// controllo dimensioni ammissibili
if ( ! bDimXOk && ! bDimYOk)
return true ; // se nessuna
else if ( ! bDimXOk)
nType = 1 ; // se dimY
else if ( ! bDimYOk)
nType = 0 ; // se dimX
else
nType = 2 ; // se entrambe
// creo il rettangolo del Box
pCrvBox->Clear() ;
pCrvBox->AddPoint( BBox.GetMin()) ;
pCrvBox->AddLine( BBox.GetMin() + BBox.GetDimX() * X_AX) ;
pCrvBox->AddLine( BBox.GetMax()) ;
pCrvBox->AddLine( BBox.GetMax() - BBox.GetDimX() * X_AX) ;
pCrvBox->Close() ;
// determino il punto inziale della curva
if ( nType == 0)
pCrvBox->ChangeStartPoint( 1.) ;
pCrvBox->ToGlob( frTrap) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
PreparareTrapezoidTwoBases( const ICurveComposite* pCrvCompo, const double dDiam, const int nType, int& nBase,
int& nSecondBase, bool& bOk, ICurveComposite* pCrvTrap)
{
// controllo parametri
if ( pCrvCompo == nullptr || ! 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
GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTrap,
Frame3d& frTrap, const PocketParams& PockParams, double& dPocketSize, int& nBase, int& nSecondBase)
{
// controllo parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// diametro reale da tenere in considerazione
double dDiam = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ;
pCrvTrap->Clear() ; // resterà vuota se il caso non è ottimizzato
nBase = -1 ;
nSecondBase = -1 ;
// se la curva è già un trapezio, non sempre devo adattarla...
Point3d pt ; Vector3d vtDir, vtB2, vtOtherDir ;
if ( pCrvCompo->IsATrapezoid( 100 * EPS_SMALL, pt, vtDir, vtOtherDir, vtB2)) {
// data la tolleranza, creo la curva a trapezio dai 4 punti
pCrvTrap->AddPoint( pt) ;
pCrvTrap->AddLine( pt + vtDir) ;
pCrvTrap->AddLine( pt + vtOtherDir + vtB2) ;
pCrvTrap->AddLine( pt + vtOtherDir) ;
pCrvTrap->Close() ;
// se parallelogramma scelgo come base i lati lunghi
Vector3d vtL2( - vtDir + vtOtherDir + vtB2) ;
if ( AreSameOrOppositeVectorApprox( vtOtherDir, vtL2)) {
if ( vtOtherDir.Len() > vtDir.Len())
swap( vtDir, vtOtherDir) ;
}
vtDir.Normalize() ;
Vector3d vtOrtho = OrthoCompo( vtOtherDir, vtDir) ;
dPocketSize = vtOrtho.Len() ;
if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap))
return false ;
// 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 ; pCrvTrap->GetCurve( nI)->GetStartDir( vtCurrCrvDir) ;
if ( AreSameVectorEpsilon( vtCrvDir, vtCurrCrvDir, 150 * EPS_SMALL)) {
pCrvTrap->SetCurveTempProp( nU, pCrvCompo->GetCurve( nI)->GetTempProp( 0), 0) ;
break ;
}
}
}
bool bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == 1 || pCrvTrap->GetCurve( 2)->GetTempProp( 0) == 1) ;
// controllo dimensioni della svuotatura
if ( ! ( bBaseOpen && dPocketSize < dDiam + EPS_SMALL) &&
abs( dPocketSize - dDiam) > EPS_SMALL) {
pCrvTrap->Clear() ;
return true ;
}
// recupero flag aperto/chiuso dei lati
if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == 0 && pCrvTrap->GetCurve( 3)->GetTempProp( 0) == 0) {
double dLen0, dLen2 ;
pCrvTrap->GetCurve( 0)->GetLength( dLen0) ;
pCrvTrap->GetCurve( 2)->GetLength( dLen2) ;
if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL)
pCrvTrap->Clear() ;
}
// imposto le basi
nBase = 0 ;
nSecondBase = 2 ;
return true ;
}
// controllo il numero di lati chiusi e salvo i loro indici
int nClosedSide = 0 ;
INTVECTOR vIndClosedSides ;
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
int nTmpProp ;
if ( pCrvCompo->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 0) {
++ nClosedSide ;
vIndClosedSides.push_back( u) ;
}
}
// se tutti lati aperti
switch ( nClosedSide) {
// TUTTI I LATI SONO APERTI
case 0 : {
// ricavo il box minimo della curva aperta ( passo dalla polyLine)
PolyLine PL ; Point3d ptCen ; double dWidth ;
if ( ! pCrvCompo->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) ||
! PL.GetMinAreaRectangleXY( ptCen, vtDir, dWidth, dPocketSize))
return false ;
// controllo dimY ( dHeight), se troppo estesa, non è un caso ottimizzato
if ( dPocketSize > dDiam + TOL_TRAPEZOID)
return true ;
// inverto il frame attuale
frTrap.Set( ptCen, Z_AX, vtDir) ;
if ( ! frTrap.IsValid())
return false ;
// creo il rettangolo del Box
pCrvTrap->AddPoint( Point3d( - 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
pCrvTrap->AddLine( Point3d( - 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
pCrvTrap->Close() ;
pCrvTrap->ToGlob( frTrap) ;
Point3d ptNewOrig ; pCrvTrap->GetStartPoint( ptNewOrig) ;
frTrap.Set( ptNewOrig, Z_AX, vtDir) ;
// imposto tutte le 4 curve come aperte
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
// imposto le basi
nBase = 0 ;
nSecondBase = 2 ;
}
break ;
// 1 LATO CHIUSO ( LINEARE)
case 1 : {
int nType = -1 ;
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
if ( nType != -1) {
// imposto tutte le curve come aperte tranne la prima ( estendo il solo lato chiuso come base del Box)
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, u == 0 ? 0 : 1, 0) ;
// memorizzo la dimensione di svuotatura
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
// imposto le basi
nBase = 0 ;
nSecondBase = 2 ;
}
}
break ;
// 2 LATI CHIUSI ( LINEARI, CONSECUTIVI O PARALLELI)
case 2 : {
// controllo se entrambi sono lineari
if ( pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE &&
pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE) {
// se lineari, ricavo i punti estremanti e le direzioni
Point3d ptS0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartPoint( ptS0) ;
Point3d ptS1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartPoint( ptS1) ;
Point3d ptE0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetEndPoint( ptE0) ;
Point3d ptE1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetEndPoint( ptE1) ;
Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ;
Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ;
// CASO PARALLELI ( la base è indifferente)
if ( AreOppositeVectorEpsilon( vtDir0, vtDir1, 5 * EPS_SMALL)) {
int nType = -1 ;
// prendo come base il primo chiuso
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
if ( nType != -1) {
// memorizzo la dimensione di svuotatura
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
// controllo che la distanza tra i due chiusi sia effettivamente circa il raggio
double dDist = 0. ;
DistPointCurve DPL( ptS0, *pCrvCompo->GetCurve( vIndClosedSides[1]), false) ;
if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) {
// imposto tutte le curve di indice dispari aperte
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, u % 2 == 0 ? 0 : 1, 0) ;
// imposto le basi
nBase = 0 ;
nSecondBase = 2 ;
}
else
pCrvTrap->Clear() ;
}
}
// CASO CONSECUTIVI
bool bOk_0_1 = AreSamePointApprox( ptS1, ptE0) ; // chiuso1 . chiuso0
bool bOk_1_0 = AreSamePointApprox( ptS0, ptE1) ; // chiuso0 . chiuso1
if ( bOk_0_1 || bOk_1_0) {
// provo con il primo lato
int nType = -1 ;
// prendo come base il primo chiuso
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
if ( nType == -1) {
// provo con l'altro
if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
if ( nType != -1) {
// spaw tra gli indici e flags
swap( vIndClosedSides[0], vIndClosedSides[1]) ;
swap( bOk_0_1, bOk_1_0) ;
}
}
Point3d ptH1, ptH2 ;
if ( nType == 1 || nType == 2) {
double dLen1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetLength( dLen1) ;
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
// i lati inclinati chiusi definiscono il Box
if ( abs( dLen1 * ( vtDir0 ^ vtDir1).Len() - dPocketSize) < TOL_TRAPEZOID) {
// creo la curva a trapezio
if ( bOk_0_1) {
pCrvTrap->GetCurve( 2)->GetEndPoint( ptH1) ;
pCrvTrap->GetCurve( 3)->GetEndPoint( ptH2) ;
pCrvTrap->Clear() ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
pCrvTrap->AddLine( ptH1) ;
pCrvTrap->AddLine( ptH2) ;
pCrvTrap->Close() ;
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ;
}
else {
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
pCrvTrap->Clear() ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
pCrvTrap->AddLine( ptH1) ;
pCrvTrap->AddLine( ptH2) ;
pCrvTrap->AddLine( ptS1) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
pCrvTrap->SetCurveTempProp( 0, 0, 0) ;
for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u)
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ;
}
// imposto le basi
nBase = 0 ;
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
}
else
pCrvTrap->Clear() ;
}
else if ( nType == 0) {
if ( bOk_0_1) {
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
pCrvTrap->Clear() ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
pCrvTrap->AddLine( ptH1) ;
pCrvTrap->AddLine( ptH2) ;
pCrvTrap->Close() ;
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ;
pCrvTrap->ChangeStartPoint( 1.) ;
// imposto le basi
nBase = 0 ;
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
}
else {
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
pCrvTrap->AddLine( ptH1) ;
pCrvTrap->AddLine( ptH2) ;
pCrvTrap->AddLine( ptS1) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
pCrvTrap->SetCurveTempProp( 0, 0, 0) ;
for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u)
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ;
pCrvTrap->ChangeStartPoint( 1.) ;
// imposto le basi
nBase = 0 ;
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
}
}
}
}
}
break ;
// 3 LATI CHIUSI ( LINEARI e CONSECUTIVI)
case 3 : {
// prendo i 3 lati chiusi
const ICurve* pCrv0 = pCrvCompo->GetCurve( vIndClosedSides[0]) ;
const ICurve* pCrv1 = pCrvCompo->GetCurve( vIndClosedSides[1]) ;
const ICurve* pCrv2 = pCrvCompo->GetCurve( vIndClosedSides[2]) ;
if ( pCrv0 == nullptr || pCrv1 == nullptr || pCrv2 == nullptr)
return false ;
// controllo che siano lineari
if ( pCrv0->GetType() == CRV_LINE && pCrv1->GetType() == CRV_LINE && pCrv2->GetType() == CRV_LINE) {
// prendo i punti iniziali e finali
Point3d ptStart0 ; pCrv0->GetStartPoint( ptStart0) ;
Point3d ptEnd0 ; pCrv0->GetEndPoint( ptEnd0) ;
Point3d ptStart1 ; pCrv1->GetStartPoint( ptStart1) ;
Point3d ptEnd1 ; pCrv1->GetEndPoint( ptEnd1) ;
Point3d ptStart2 ; pCrv2->GetStartPoint( ptStart2) ;
Point3d ptEnd2 ; pCrv2->GetEndPoint( ptEnd2) ;
// controllo che siano consecutivi e cambio l'ordine se necessario
bool bOk = true ;
if ( AreSamePointApprox( ptEnd0, ptStart1) && AreSamePointApprox( ptEnd1, ptStart2))
;
else if ( AreSamePointApprox( ptEnd1, ptStart2) && AreSamePointApprox( ptEnd2, ptStart0)) {
swap( vIndClosedSides[0], vIndClosedSides[1]) ;
swap( vIndClosedSides[1], vIndClosedSides[2]) ;
}
else if ( AreSamePointApprox( ptEnd2, ptStart0) && AreSamePointApprox( ptEnd0, ptStart1)) {
swap( vIndClosedSides[0], vIndClosedSides[2]) ;
swap( vIndClosedSides[2], vIndClosedSides[1]) ;
}
else // se non consecutivi
bOk = false ;
// se i tre lati sono ( mediante lo swap) consecutivi...
if ( bOk) {
// controllo se la dimensione Y del lato 1 è valida ( il resto è gestito di seguito)
int nType = -1 ;
if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
if ( nType == 1) {
// bisgna controllare la lunghezza dei chiusi
double dLen0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetLength( dLen0) ;
double dLen2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetLength( dLen2) ;
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ;
Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ;
Vector3d vtDir2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetStartDir( vtDir2) ;
// i lati inclinati chiusi definiscono il Box
if ( abs( dLen2 * ( vtDir1 ^ vtDir2).Len() - dPocketSize) < TOL_TRAPEZOID &&
abs( dLen0 * ( vtDir1 ^ vtDir0).Len() - dPocketSize) < TOL_TRAPEZOID) {
// creo la curva a trapezio
pCrvTrap->Clear() ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[2])->Clone()) ;
pCrvTrap->Close() ;
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u)
pCrvTrap->SetCurveTempProp( u, u < 3 ? 0 : 1 , 1) ;
pCrvTrap->ChangeStartPoint( 1.) ;
// imposto le basi
nBase = 0 ;
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
}
}
}
}
}
}
// se non ho trovato delle basi, allora cerco in maniera generale un trapezoide ( se esiste)
if ( nBase == -1) {
bool bOK = false ;
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() && !bOK ; ++ u) {
// cerco un lato chiuso ( esiste per forza)
if ( pCrvCompo->GetCurve( u)->GetTempProp( 0) == 0) {
// controllo se il lato corrente può essere una base
int nType = -1 ;
if ( ! GetBoxCrvOptTrap( u, pCrvCompo, dDiam, nType, pCrvTrap))
return false ;
// se è una base valida
if ( nType != -1) {
// ricavo la dimensione di svuotatura
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
// ricavo la sua direzione iniziale
Vector3d vtBaseDir ; pCrvCompo->GetCurve( u)->GetStartDir( vtBaseDir) ;
// ricavo il suo punto iniziale
Point3d ptBaseStart ; pCrvCompo->GetCurve( u)->GetStartPoint( ptBaseStart) ;
// ricavo il suo punto finale
Point3d ptBaseEnd ; pCrvCompo->GetCurve( u)->GetEndPoint( ptBaseEnd) ;
// cerco il lato chiuso lineare parallelo ad esso e distante circa dPocketSize
for ( int uu = 0 ; uu < pCrvCompo->GetCurveCount() && !bOK ; ++ uu) {
if ( uu == u ||
pCrvCompo->GetCurve( uu)->GetType() != CRV_LINE ||
pCrvCompo->GetCurve( uu)->GetTempProp() != 0)
continue ;
Vector3d vtSecondBaseDir ; pCrvCompo->GetCurve( uu)->GetStartDir( vtSecondBaseDir) ;
if ( ! AreOppositeVectorEpsilon( vtBaseDir, vtSecondBaseDir, 5 * EPS_SMALL))
continue ;
Point3d ptSecondBaseStart ; pCrvCompo->GetCurve( uu)->GetStartPoint( ptSecondBaseStart) ;
double dDist = 0. ;
DistPointCurve DPL( ptBaseStart, *pCrvCompo->GetCurve( uu), false) ;
if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) {
nBase = u ;
nSecondBase = uu ;
if ( ! PreparareTrapezoidTwoBases( pCrvCompo, dDiam, nType, nBase, nSecondBase, bOK, pCrvTrap))
return false ;
}
}
}
}
}
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
if ( PockParams.dMaxOptSize > EPS_SMALL && dPocketSize > PockParams.dMaxOptSize)
pCrvTrap->Clear() ;
return true ;
}
//----------------------------------------------------
static bool
SpecialAdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const bool bEvenClosed,
const ICurveComposite* pCrvPocket, const PocketParams& PockParams)
{
// parametri
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
double dRad = 0.5 * dDiam + dOffsR ;
// calcolo gli offset dei lati obliqui
PtrOwner<ICurveLine> pLineS( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 0 : 3)->Clone())) ;
pLineS->SimpleOffset( - dRad) ;
pLineS->Invert() ;
PtrOwner<ICurveLine> pLineE( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 2 : 1)->Clone())) ;
pLineE->SimpleOffset( - dRad) ;
pLineE->Invert() ;
Point3d ptS, ptE ;
pLineS->GetEndPoint( ptS) ;
pLineE->GetStartPoint( ptE) ;
Vector3d vtS, vtE ;
pLineS->GetStartDir( vtS) ;
pLineE->GetStartDir( vtE) ;
PtrOwner<ICurveLine> pLineLink( CreateCurveLine()) ;
pLineLink->Set( ptS, ptE) ;
pMCrv->Clear() ;
if ( ! pMCrv->AddCurve( Release( pLineS)))
return false ;
// creo raccordi
bool bUseBiArcs = false ;
Vector3d vtDir, vtDir2 ;
pLineLink->GetStartDir( vtDir) ;
pCrvPocket->GetCurve( bEvenClosed ? 1 : 0)->GetStartDir( vtDir2) ;
if ( Dist( ptS, ptE) > 500 * EPS_SMALL && AreSameOrOppositeVectorApprox( vtDir, vtDir2)) {
Point3d ptCrv1, ptCrv2 ;
pLineLink->GetPointD1D2( 0.3, ICurve::FROM_MINUS, ptCrv1) ;
pLineLink->GetPointD1D2( 0.7, ICurve::FROM_MINUS, ptCrv2) ;
// primo raccordo
double dAng ;
vtS.GetAngleXY( X_AX, dAng) ;
PtrOwner<ICurve> pBiArc1( GetBiArc( ptS, - dAng, ptCrv1, bEvenClosed ? 90 : 0, 0.5)) ;
// secondo raccordo
vtE.Invert() ;
vtE.GetAngleXY( X_AX, dAng) ;
PtrOwner<ICurve> pBiArc2( GetBiArc( ptE, - dAng, ptCrv2, bEvenClosed ? - 90 : 180, 0.5)) ;
pBiArc2->Invert() ;
if ( ! IsNull( pBiArc1) && ! IsNull( pBiArc2)) {
bUseBiArcs = true ;
pMCrv->AddCurve( Release( pBiArc1)) ;
pMCrv->AddLine( ptCrv2) ;
pMCrv->AddCurve( Release( pBiArc2)) ;
}
}
// se non è stato possibile creare raccordo, unisco linearmente
if ( ! bUseBiArcs)
pMCrv->AddCurve( Release( pLineLink)) ;
if ( ! pMCrv->AddCurve( Release( pLineE)))
return false ;
// setto temp prop per ricordare curve aggiuntive per pulire angoli
pMCrv->SetCurveTempProp( 0, 1) ;
pMCrv->SetCurveTempProp( pMCrv->GetCurveCount() - 1, 1) ;
return true ;
}
//------------------------------------------------------
static bool
CalcTrapezoidSpiralXCoord( const ICurveComposite* pCrvPocket, int nBase, int nSecondBase,
bool bStart, double dYCoord, double& dXCoord, double dPocketSize,
const PocketParams& PockParams)
{
// parametri
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
double dRad = 0.5 * dDiam + dOffsR ;
// recupero la curva di interesse
int nCrvId = ( bStart ? nSecondBase : nBase) + 1 ;
int nProp = - 1 ;
if ( ! pCrvPocket->GetCurveTempProp( nCrvId, nProp))
return false ;
// se open
if ( nProp == 1) {
Point3d pt1, pt2 ;
pCrvPocket->GetCurve( nCrvId)->GetStartPoint( pt1) ;
pCrvPocket->GetCurve( nCrvId)->GetEndPoint( pt2) ;
if ( bStart)
dXCoord = min( pt1.x, pt2.x) ;
else
dXCoord = max( pt1.x, pt2.x) ;
}
// se closed
else {
// creo la curva destra/sinistra
int nLast = bStart ? pCrvPocket->GetCurveCount() : nSecondBase ;
PtrOwner<ICurveComposite> pCrvSide( ConvertCurveToComposite( pCrvPocket->CopyParamRange( nCrvId, nLast))) ;
if ( IsNull( pCrvSide))
return false ;
// Offsetto la curva
OffsetCurve OffsCrv ;
if ( ! OffsCrv.Make( pCrvSide, - dRad, ICurve::OFF_FILLET))
return false ;
if ( OffsCrv.GetCurveCount() == 0) {
// controllo se avevo una circonferenza
if ( pCrvSide->GetCurveCount() == 1 && pCrvSide->GetFirstCurve()->GetType() == CRV_ARC) {
Point3d ptS ; pCrvSide->GetStartPoint( ptS) ;
dXCoord = ptS.x ;
}
else {
Point3d ptS ; pCrvSide->GetStartPoint( ptS) ;
Point3d ptE ; pCrvSide->GetEndPoint( ptE) ;
dXCoord = bStart ? max( ptS.x, ptE.x) + dRad : min( ptS.x, ptE.x) - dRad ;
}
}
else if ( OffsCrv.GetCurveCount() == 1) {
// controllo se la curva interseca la linea di svuotatura a YCoord
PtrOwner<ICurve> pCrvOffs( OffsCrv.GetLongerCurve()) ;
if ( IsNull( pCrvOffs))
return false ;
PtrOwner<ICurveLine> pLineMid( CreateCurveLine()) ;
if ( IsNull( pLineMid))
return false ;
pLineMid->Set( Point3d( - 3000, dYCoord, 0), Point3d( 3000, dYCoord, 0)) ;
IntersCurveCurve intCC( *pLineMid, *pCrvOffs) ;
IntCrvCrvInfo ccClass ;
if ( intCC.GetIntersCount() != 0) {
// se ho almeno una intersezione
if ( intCC.GetIntCrvCrvInfo( 0, ccClass))
dXCoord = ccClass.IciA[0].ptI.x ;
else
return false ;
}
else {
// se non ho intersezioni...
// prendo il box della curva
BBox3d Box3d ;
pCrvSide->GetLocalBBox( Box3d) ;
// creo la linea limitie verticale
PtrOwner<ICurveLine> pCrvVertLine( CreateCurveLine()) ;
if ( IsNull( pCrvVertLine))
return false ;
if ( bStart)
pCrvVertLine->SetPDL( Box3d.GetMax() + 5 * TOL_TRAPEZOID * Y_AX, - 90 , 2 * dPocketSize) ;
else
pCrvVertLine->SetPDL( Box3d.GetMin() - 5 * TOL_TRAPEZOID * Y_AX, 90, 2 * dPocketSize) ;
// intersechiamo
IntersCurveCurve intCC2( *pCrvVertLine, *pCrvSide) ;
if ( intCC2.GetOverlaps()) {
if ( bStart)
dXCoord = Box3d.GetMax().x + dRad ;
else
dXCoord = Box3d.GetMin().x - dRad ;
}
else {
dXCoord = bStart ? -INFINITO : INFINITO ;
for ( int i = 0 ; i < intCC2.GetIntersCount() ; ++ i) {
IntCrvCrvInfo ccClass2 ;
if ( intCC2.GetIntCrvCrvInfo( i, ccClass2)) {
if ( bStart)
dXCoord = max( dXCoord, Box3d.GetMax().x + sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ;
else
dXCoord = min( dXCoord, Box3d.GetMin().x - sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ;
}
}
}
}
}
else
return false ;
}
return true ;
}
//----------------------------------------------------
static bool
AdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const ICurveComposite* pCrvPocket,
const PocketParams& PockParams, bool bStart)
{
// parametri
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
double dRad = 0.5 * dDiam + dOffsR ;
PtrOwner<ICurveComposite> pCompo( CreateCurveComposite()) ;
if ( ! bStart)
pMCrv->Invert() ;
Point3d ptTmp ;
pMCrv->GetStartPoint( ptTmp) ;
double dYCoord = ptTmp.y ; // quota verticale del percorso di svuotatura
pCrvPocket->GetCurve( 2)->GetStartPoint( ptTmp) ;
double dPocketSize = ptTmp.y ;
int nCrvId = ( bStart ? 3 : 1) ;
PtrOwner<ICurveLine> pLine( GetCurveLine( pCrvPocket->GetCurve( nCrvId)->Clone())) ;
pLine->SimpleOffset( - dRad) ;
Point3d ptP1, ptP2 ;
pLine->GetStartPoint( ptP1) ;
pLine->GetEndPoint( ptP2) ;
if ( ! bStart)
swap( ptP1, ptP2) ;
int nProp2 ;
// lato opposto a quello di riferimento aperto
if ( pCrvPocket->GetCurveTempProp( 2, nProp2) && nProp2 != 0) {
// caso 1 : pLine ha un estremo sopra e uno sotto il percorso di svuotatura pMCrv
if ( ptP2.y < dYCoord && dYCoord < ptP1.y) {
// creo tratto da ptP2 a ptP1
pCompo->AddPoint( ptP2) ;
pCompo->AddLine( ptP1) ;
// trovo il punto di pMCrv da cui ripartire per non lasciare aree residue
pLine->SimpleOffset( - 0.5 * dDiam) ;
pLine->ExtendStartByLen( EPS_SMALL) ;
pLine->ExtendEndByLen( EPS_SMALL) ;
IntersCurveCurve intCC( *pLine, *pCrvPocket) ;
if ( intCC.GetIntersCount() == 0)
return false ;
IntCrvCrvInfo aInfo ;
intCC.GetIntCrvCrvInfo( 0, aInfo) ;
double dDeltaX = sqrt( max( 0., dDiam * dDiam / 4 - dYCoord * dYCoord)) ;
if ( ! bStart)
dDeltaX *= -1 ;
Point3d ptCrv( aInfo.IciA[0].ptI.x + dDeltaX, dYCoord) ;
double dPar = 0 ;
if ( ! pMCrv->GetParamAtPoint( ptCrv, dPar)) {
dPar = 0.5 ;
pMCrv->GetPointD1D2( dPar, ICurve::FROM_MINUS, ptCrv) ;
}
if ( ! pMCrv->TrimStartAtParam( dPar))
pMCrv->Clear() ;
if ( ptP1.y > dPocketSize) {
// se ptP1 è esterno alla svuotatura scambio i punti in modo da usare ptP2 per il biarco ( così da non farlo fuoriuscire troppo)
swap( ptP1, ptP2) ;
pCompo->Invert() ;
}
// creo biarco fra ptP1 e ptCrv
Vector3d vtDir ;
pCompo->GetStartDir( vtDir) ;
double dAng ;
vtDir.GetAngleXY( X_AX, dAng) ;
PtrOwner<ICurve> pBiArc( GetBiArc( ptP1, - dAng, ptCrv, bStart ? 0 : 180, 0.8)) ;
bool bUseBiArc = false ;
if ( ! IsNull( pBiArc)) {
// verifico che con il biarco non si oltrepassi il lato obliquo chiuso della svuotatura
IntersCurveCurve intCC2( *pBiArc, *pCompo) ;
if ( intCC2.GetIntersCount() == 1)
bUseBiArc = true ;
}
if ( bUseBiArc)
pCompo->AddCurve( Release( pBiArc)) ;
else {
double dParLine = ( dYCoord - ptP2.y) / ( ptP1.y - ptP2.y) ;
Point3d ptOnLine = Media( ptP2, ptP1, dParLine) ;
pCompo->AddLine( ptOnLine) ;
pCompo->AddLine( ptCrv) ;
}
}
// caso 2 : pLine è completamente sopra/sotto la linea di svuotatura
else {
// se è sopra modifiche per ricondurmi al caso in cui è sotto
if ( ptP2.y > dYCoord) {
pLine->Invert() ;
swap( ptP1, ptP2) ;
}
// trovo l'intersezione fra il prolungamento della linea e pMCrv
if ( bStart)
pLine->ExtendStartByLen( 1000) ;
else
pLine->ExtendEndByLen( 1000) ;
IntersCurveCurve intCC ( *pLine, *pMCrv) ;
if ( intCC.GetIntersCount() == 0)
return false ;
IntCrvCrvInfo aInfo ;
intCC.GetIntCrvCrvInfo( 0, aInfo) ;
Point3d ptInt ;
ptInt = aInfo.IciA[0].ptI ;
double dPar = 0 ;
pMCrv->GetParamAtPoint( ptInt, dPar) ;
if ( ! pMCrv->TrimStartAtParam( dPar))
pMCrv->Clear() ;
pCompo->AddPoint( ptP2) ;
pCompo->AddLine( ptInt) ;
}
}
// se il lato opposto a quello di riferimento chiuso bisogna distinguere i casi
else {
bool bStartPnt = false ;
if ( ptP2.y < dYCoord) {
bStartPnt = pCompo->AddPoint( ptP2) ;
}
else {
Vector3d vtDir ;
pLine->GetStartDir( vtDir) ;
if ( bStart)
vtDir.Invert() ;
Point3d ptP2N = ptP2 - dDiam / 2 * abs( vtDir.x / vtDir.y) * vtDir ;
if ( ptP2N.y < dYCoord)
bStartPnt = pCompo->AddPoint( ptP2N) ;
}
if ( bStartPnt) {
Point3d ptCrv ;
pMCrv->GetStartPoint( ptCrv) ;
pCompo->AddLine( ptCrv) ;
}
}
// setto temp prop per ricordare che è curva aggiuntiva per pulire angoli
for ( int i = 0 ; i < pCompo->GetCurveCount() ; i++)
pCompo->SetCurveTempProp( i, 1) ;
pMCrv->AddCurve( Release( pCompo), false) ;
if ( ! bStart)
pMCrv->Invert() ; // ripristino la direzione originaria
return true ;
}
//----------------------------------------------------
static bool
CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double dPocketSize, int nBase,
int nSecondBase, const PocketParams& PockParams, ICurveComposite* pMCrv,
bool& bOptimizedTrap)
{
// parametri
double dDiam = 2 * PockParams.dRad ;
double dOffsR = PockParams.dRadialOffset ;
bOptimizedTrap = false ;
Vector3d vtExtr ; pCrvPocket->GetExtrusion( vtExtr) ;
// recupero le temp prop
INTVECTOR vnProp( 4, 0) ;
if ( pCrvPocket->GetCurveCount() == 4) {
for ( int i = 0 ; i < 4 ; i ++)
pCrvPocket->GetCurveTempProp( i, vnProp[i]) ;
}
else {
vnProp[1] = pCrvPocket->GetCurve( 1)->GetTempProp() ;
vnProp[3] = pCrvPocket->GetCurve( nSecondBase + 1)->GetTempProp() ;
}
// passo in un sistema di riferimento locale avente asse X allineato con uno dei due lati paralleli
// (possibilmente aperto) e centro nel punto iniziale del lato
pCrvPocket->ToLoc( frTrap) ;
// calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso)
double dLen0, dLen1, dLen2, dLen3, dMaxLarg = 0. ;
pCrvPocket->GetCurve( nBase)->GetLength( dLen0) ;
pCrvPocket->GetCurve( nSecondBase)->GetLength( dLen2) ;
bool bRealTrap = pCrvPocket->GetCurveCount() == 4 ;
for ( int i = 0 ; i < 4 && bRealTrap ; ++ i)
bRealTrap = pCrvPocket->GetCurve( i)->GetType() == CRV_LINE ;
if ( bRealTrap) {
pCrvPocket->GetCurve( nBase + 1)->GetLength( dLen1) ;
pCrvPocket->GetCurve( nSecondBase + 1)->GetLength( dLen3) ;
// calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso)
Point3d ptOrig ; pCrvPocket->GetCurve( 1)->GetStartPoint( ptOrig) ;
Vector3d vtX ; pCrvPocket->GetCurve( 1)->GetStartDir( vtX) ;
Frame3d frDim ; frDim.Set( ptOrig, Z_AX, vtX) ; frDim.Invert() ;
BBox3d b3Dim ; pCrvPocket->GetBBox( frDim, b3Dim, BBF_EXACT) ;
dMaxLarg = ( vnProp[0] != 0 ? b3Dim.GetDimY() : dPocketSize) ;
}
// calcolo percorso di svuotatura
// se lati obliqui sono entrambi chiusi e dimensione svuotatura è maggiore di diametro fresa e minore del doppio gestione speciale
if (( bRealTrap && dMaxLarg > PockParams.dRad * 2 + 10 * EPS_SMALL) &&
((( vnProp[0] != 0 && vnProp[2] != 0) && ( vnProp[3] == 0 && vnProp[1] == 0) && ( max( dLen0, dLen2) < 2 * dDiam + EPS_SMALL)) ||
(( vnProp[1] != 0 && vnProp[3] != 0) && ( vnProp[0] == 0 && vnProp[2] == 0) && ( max( dLen1, dLen3) < 2 * dDiam + EPS_SMALL)))) {
if ( ! SpecialAdjustTrapezoidSpiralForAngles( pMCrv, vnProp[0] == 0, pCrvPocket, PockParams)) {
pMCrv->Clear() ;
return false ;
}
}
else {
// trovo la quota Y per centro del Tool
double dYCoord ;
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) // se base principale chiusa
dYCoord = 0.5 * dDiam + dOffsR ;
else if ( pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 0) // se base principale aperta e secondaria chiusa
dYCoord = dPocketSize - 0.5 * dDiam - dOffsR ;
else // se entrambi i lati paralleli sono aperti mi posiziono a metà della svuotatura
dYCoord = 0.5 * dPocketSize ;
double dXCoordStart = -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 ;
Point3d ptStart( dXCoordStart, dYCoord) ;
Point3d ptEnd( dXCoordEnd, dYCoord) ;
if ( bRealTrap && AreSamePointEpsilon( ptStart, ptEnd, 500 * EPS_SMALL) && ( vnProp[0] != 0 || vnProp[2] != 0)) {
Vector3d vtDir1, vtDir3 ;
pCrvPocket->GetCurve( 1)->GetStartDir( vtDir1) ;
pCrvPocket->GetCurve( 3)->GetStartDir( vtDir3) ;
// gestisco il caso speciale di un parallelogramma in cui anche l'altra dimensione della svuotatura è pari al diametro utensile
if ( AreOppositeVectorApprox( vtDir1, vtDir3)) {
PtrOwner<ICurveLine> pLine1( GetCurveLine( pCrvPocket->GetCurve( 1)->Clone())) ;
PtrOwner<ICurveLine> pLine3( GetCurveLine( pCrvPocket->GetCurve( 3)->Clone())) ;
if ( IsNull( pLine1) || IsNull( pLine3))
return false ;
if ( ! pLine1->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset) ||
! pLine3->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset))
return false ;
Point3d ptS, ptE ;
if ( vtDir3 * X_AX > EPS_SMALL) {
pLine1->GetStartPoint( ptS) ;
pLine3->GetStartPoint( ptE) ;
}
else {
pLine1->GetEndPoint( ptE) ;
pLine3->GetEndPoint( ptS) ;
}
if ( vnProp[0] != 0) {
pMCrv->AddPoint( ptS) ;
if ( vnProp[2] != 0)
pMCrv->AddLine( ptE) ;
else
pMCrv->AddLine( ptStart) ;
}
else {
pMCrv->AddPoint( ptE) ;
if ( vnProp[0] != 0)
pMCrv->AddLine( ptS) ;
else
pMCrv->AddLine( ptStart) ;
pMCrv->Invert() ;
}
pMCrv->SetCurveTempProp( 0, 1) ;
}
}
else {
if ( ! pMCrv->AddPoint( ptStart))
return true ;
if ( ! pMCrv->AddLine( ptEnd))
return true ;
// aggiustamenti al percorso per rimuovere materiale residuo negli angoli
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 1 ||
pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 1) {
Frame3d frOpen ;
bool bSwitch = false ;
// Base principale chiusa e base Secondaria aperta
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) {
pCrvPocket->ChangeStartPoint( nSecondBase) ;
// oriento il Frame ( ho sempre l'origine nell'estremo superiore del lato aperto)
Vector3d vtDir ; pCrvPocket->GetStartDir( vtDir) ;
Point3d ptORIG ;
if ( vtDir.y > 0)
pCrvPocket->GetCurve( nBase)->GetEndPoint( ptORIG) ;
else
pCrvPocket->GetCurve( nBase)->GetStartPoint( ptORIG) ;
frOpen.Set( ptORIG, Z_AX, -X_AX) ;
if ( ! frOpen.IsValid())
return false ;
pCrvPocket->ToLoc( frOpen) ;
pMCrv->ToLoc( frOpen) ;
pMCrv->Invert() ;
bSwitch = true ;
}
if ( pCrvPocket->GetCurve( 3)->GetTempProp( 0) == 0 &&
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, true)) {
pMCrv->Clear() ;
return false ;
}
if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == 0 &&
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, false)) {
pMCrv->Clear() ;
return false ;
}
if ( bSwitch) {
pCrvPocket->ToGlob( frOpen) ;
pMCrv->ToGlob( frOpen) ;
pMCrv->Invert() ;
}
}
}
}
if ( pMCrv->GetCurveCount() == 0)
return true ;
pMCrv->ToGlob( frTrap) ;
if ( ! PockParams.bInvert) {
pMCrv->Invert() ;
// inverto le proprietà in modo che nProp3 sia sempre legata al punto iniziale e nProp1 a quello finale
swap( vnProp[1], vnProp[3]) ;
}
// segno i lati aperti come temp prop della curva
int nOpenEdges = vnProp[0] + vnProp[1] * 2 + vnProp[2] * 4 + vnProp[3] * 8 ;
pMCrv->SetTempProp( nOpenEdges, 0) ;
pMCrv->SetExtrusion( vtExtr) ;
bOptimizedTrap = true ;
return true ;
}
//----------------------------------------------------------------------------
static bool
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, allora ingrandisco il raggio
if ( nTempPropRef == 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) {
double dIntRad = 0 ;
bool bOkSpiral = CalcCircleSpiral( ptCen, pSfrChunk->GetNormVersor(), dRad - dOffs, dIntRad, PockParam, pCrvRes) ;
if ( bOkSpiral && pCrvRes->IsValid() && pCrvRes->GetCurveCount() > 0) {
// se curva di bordo OPEN, imposto i parametri per LeadIn
if ( nTempPropRef == TEMP_PROP_OPEN_EDGE) {
Vector3d vtMidOut ;
pCrvRes->GetStartDir( vtMidOut) ;
vtMidOut.Rotate( pSfrChunk->GetNormVersor(), PockParam.bInvert ? ANG_RIGHT : - ANG_RIGHT) ;
bool bIsExtended ;
ExtendPath( pCrvRes, pSfrChunk, PockParam, vtMidOut, false, bIsExtended) ;
}
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()) {
pCrvTrap->SetExtrusion( Z_AX) ;
CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParam, pCrvRes, bOkTrap) ;
if ( bOkTrap) {
// calcolo eventuali uscite e ingressi
if ( pCrvRes->GetTempProp( 0) > 0) {
Vector3d vtRef ; pCrvRes->GetStartDir( vtRef) ;
vtRef.Invert() ;
bool bIsStartExtended = false ;
if ( ! ExtendPath( pCrvRes, pSfrChunk, PockParam, vtRef, false, bIsStartExtended))
return false ;
pCrvRes->GetEndDir( vtRef) ;
bool bIsEndExtended = false ;
if ( ! ExtendPath( pCrvRes, pSfrChunk, PockParam, vtRef, true, bIsEndExtended))
return false ;
if ( bIsEndExtended && ! bIsStartExtended)
pCrvRes->Invert() ;
}
}
}
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) {
// 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)
// ;
// else if ( PockParam.nType == POCKET_ONEWAY)
// ;
// else if ( PockParam.nType == POCKET_CONFORMAL_ONEWAY || PockParam.nType == POCKET_CONFORMAL_ZIGZAG)
// ;
}
return true ;
}
// ***************************************************************************
// ------------- SCELTA DEL PUNTO INIZIALE -----------------------------------
// ***************************************************************************
//----------------------------------------------------------------------------
static bool
GetParamOnOpenCurve( const ICurveComposite* pCompo, const PocketParams& PockParams, bool& bOutStart,
Point3d& ptStart, Vector3d& vtOut)
{
// controllo dei parametri
if ( pCompo == nullptr || ! pCompo->IsValid())
return false ;
// verifico se tutti i lati sono aperti
bool bAllOpen = true ;
const ICurve* pMyCrv = pCompo->GetFirstCurve() ;
while ( pMyCrv != nullptr) {
if ( pMyCrv->GetTempProp() != TEMP_PROP_OPEN_EDGE) {
bAllOpen = false ;
break ;
}
pMyCrv = pCompo->GetNextCurve() ;
}
// richiedo lunghezza superiore a diametro utensile più doppio offset radiale
double dRefLen = ( bAllOpen ? 0. : ( 2 * PockParams.dRad) + 2 * PockParams.dRadialOffset - EPS_SMALL) ;
double dMaxLen = dRefLen ;
// ciclo sulle singole curve
bool bFound = false ;
const ICurve* pPrevCrv = pCompo->GetLastCurve() ;
double dLenPrev = 0. ;
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE)
pPrevCrv->GetLength( dLenPrev) ;
const ICurve* pCrv = pCompo->GetFirstCurve() ;
while ( pCrv != nullptr) {
// analizzo la curva successiva
const ICurve* pNextCrv = pCompo->GetNextCurve() ;
bool bNextOk = ( pNextCrv != nullptr) ;
if ( ! bNextOk)
pNextCrv = pCompo->GetFirstCurve() ;
double dLenNext = 0. ;
if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE)
pNextCrv->GetLength( dLenNext) ;
// verifico la curva corrente
if ( pCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
// contributo dalle entità adiacenti ( se non tutte aperte)
double dLenAgg = 0. ;
if ( ! bAllOpen) {
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
Vector3d vtPrevEnd ; pPrevCrv->GetEndDir( vtPrevEnd) ;
Vector3d vtStart ; pCrv->GetStartDir( vtStart) ;
dLenAgg += max( 0.4, vtPrevEnd * vtStart) * dLenPrev ;
}
if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
Vector3d vtEnd ; pCrv->GetEndDir( vtEnd) ;
Vector3d vtNextStart ; pNextCrv->GetStartDir( vtNextStart) ;
dLenAgg += max( 0.4, vtEnd * vtNextStart) * dLenNext ;
}
}
// entità corrente
double dLen = 0 ;
if ( pCrv->GetLength( dLen)) {
const double LEN_TOL = 1 ;
// se di lunghezza praticamente uguale
if ( bFound && dLen + dLenAgg > dRefLen && abs( dLen + dLenAgg - dMaxLen) < LEN_TOL) {
Point3d ptTest ;
pCrv->GetMidPoint( ptTest) ;
if ( ( PockParams.bAboveHead && ptTest.z > ptStart.z + 100 * EPS_SMALL) ||
( ! PockParams.bAboveHead && ptTest.z < ptStart.z - 100 * EPS_SMALL) ||
( abs( ptTest.z - ptStart.z) < 100 * EPS_SMALL && ptTest.y < ptStart.y - 100 * EPS_SMALL)) {
dMaxLen = max( dMaxLen, dLen + dLenAgg) ;
ptStart = ptTest ;
// versore ortogonale verso l'esterno
pCrv->GetMidDir( vtOut) ;
vtOut.Rotate( Z_AX, 0, -1) ;
}
}
// se più lunga ( o non già trovata)
else if ( dLen + dLenAgg > dMaxLen || ! bFound) {
dMaxLen = dLen + dLenAgg ;
if ( pCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart, &vtOut)) {
// versore ortogonale verso l'esterno
vtOut.Normalize() ;
vtOut.Rotate( Z_AX, 0, -1) ;
bFound = true ;
}
}
dLenPrev = dLen ;
}
}
else
dLenPrev = 0 ;
// vado alla successiva
pPrevCrv = pCrv ;
pCrv = ( bNextOk ? pNextCrv : nullptr) ;
}
return bFound ;
}
//----------------------------------------------------------------------------
static bool
GetPtStartOnOpenEdgeByOrigCurve( const ICurveComposite* pCrvOrig, const PocketParams& PockParams,
const ICurveComposite* pCrvCompo, Point3d& ptStart, Vector3d& vtMidOut,
bool& bMidOut)
{
// controllo dei parametri
if ( pCrvOrig == nullptr || ! pCrvOrig->IsValid() ||
pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
bMidOut = false ;
// cerco il lato aperto più lungo ( sufficientemente lungo)
double dLenRef = ( 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) - 50 * EPS_SMALL ;
for ( int i = 0 ; i < pCrvOrig->GetCurveCount() ; ++ i) {
// escludo le sottocurve chiuse
if ( pCrvOrig->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
continue ;
// recupero la curva i-esima aperta dalla curva originale
const ICurve* pCrvOpen = pCrvOrig->GetCurve( i) ;
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
return false ;
// ricavo la lunghezza di tale curva
double dLen = 0. ;
pCrvOpen->GetLength( dLen) ;
// se lunghezza accettabile o maggiore della massima trovata
if ( dLen > dLenRef) {
Point3d ptSTmp ;
Vector3d vtMidOutTmp ;
// ricavo il punto medio e il vettore tangente ad esso associato
if ( pCrvOpen->GetPointD1D2( 0.5, ICurve::FROM_MINUS, ptSTmp, &vtMidOutTmp)) {
// cerco la sottocurva più vicina a ptSTmp
int nFlag ;
double dMyPar ;
if ( DistPointCurve( ptSTmp, *pCrvCompo).GetParamAtMinDistPoint( 0, dMyPar, nFlag)) {
int nCrv = int( floor( dMyPar)) ;
// recupero tale sottocurva e controllo che anche essa sia OPEN
const ICurve* pMyCrv = pCrvCompo->GetCurve( nCrv) ;
if ( pMyCrv != nullptr && pMyCrv->IsValid() && pMyCrv->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
// recupero ptStart
pMyCrv->GetMidPoint( ptStart) ;
// versore d'uscita
vtMidOutTmp.Normalize() ;
vtMidOutTmp.Rotate( Z_AX, - ANG_RIGHT) ;
vtMidOut = vtMidOutTmp ;
// flag per possibile entrata da fuori
bMidOut = true ;
// aggiornamento della lunghezza di riferimento
dLenRef = dLen ;
}
}
}
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPtStartOnOpenEdgeByPtRef( const Point3d& ptRef, const PocketParams& PockParams,
const ICurveComposite* pCrvCompo, const ICurveComposite* pCrvOrig,
Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut)
{
// controllo dei parametri
if ( ! ptRef.IsValid() || pCrvCompo == nullptr || ! pCrvCompo->IsValid() ||
! ptRef.IsValid())
return false ;
bMidOut = false ;
// lunghezza minima di riferimento per un lato aperto valido
double dLenRef = ( 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) - 50 * EPS_SMALL ;
// cerco la curva OPEN a minima distanza da ptRef, sufficientemente lunga
int nStartCrv = -1 ;
double dMinDist = INFINITO ;
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
// considero solo le sottocurve OPEN
if ( pCrvCompo->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
// sufficientemente lunghe
double dLen ;
if ( pCrvCompo->GetCurve( i)->GetLength( dLen) && dLen > dLenRef) {
DistPointCurve DPC( ptRef, *pCrvCompo->GetCurve( i)) ;
double dCurrDist = INFINITO ;
// sufficiententemente vicine a ptRef
if ( DPC.GetSqDist( dCurrDist) && dCurrDist < dMinDist) {
dMinDist = dCurrDist ;
nStartCrv = i ;
}
}
}
}
// se non ho trovato nulla, allora cerco un punto iniziale valido su una sottocurva OPEN
if ( nStartCrv == -1 && pCrvOrig != nullptr && pCrvOrig->IsValid()) {
return GetPtStartOnOpenEdgeByOrigCurve( pCrvOrig, PockParams, pCrvCompo, ptStart, vtMidOut, bMidOut) ;
}
// se ho trovato la curva più vicina, cerco un punto iniziale valido
else {
const ICurve* pMyCrv = pCrvCompo->GetCurve( nStartCrv) ;
if ( pMyCrv != nullptr && pMyCrv->IsValid()) {
// ricavo ptStart e vettore tangente
if ( pMyCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart, &vtMidOut)) {
// versore ortogonale verso l'esterno
vtMidOut.Normalize() ;
vtMidOut.Rotate( Z_AX, 0, -1) ;
// flag per possibile entrata da fuori
bMidOut = true ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPtStartOnHomogeousEdges( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
int nType, const Point3d& ptRef, bool& bMidOut, Point3d& ptStart,
Vector3d& vtMidOut)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
bMidOut = false ;
// analizzo i sottotratti omogenei
ICRVCOMPOPOVECTOR vpCrvs ;
PtrOwner<ICurveComposite> pCrvCompoClone( CloneCurveComposite( pCrvCompo)) ;
if ( IsNull( pCrvCompoClone) || ! pCrvCompoClone->IsValid())
return false ;
GetHomogeneousParts( pCrvCompoClone, PockParams, vpCrvs) ;
// se ho un solo tratto omogeneo e coerente al tipo scelto ...
if ( int( vpCrvs.size()) == 1 && vpCrvs[0]->GetTempProp( 0) == nType) {
// ... e non ho un punto di riferimento valido
if ( ! ptRef.IsValid()) {
// il punto iniziale è già definito
pCrvCompoClone->GetStartPoint( ptStart) ;
}
// ho un punto di riferimento valido
else {
// cerco il punto più vicino
DistPointCurve DistPtCrv( ptRef, *pCrvCompoClone) ;
int nFlag ;
if ( ! DistPtCrv.GetMinDistPoint( 0, ptStart, nFlag))
return false ;
}
bMidOut = ( nType == TEMP_PROP_OPEN_EDGE) ;
return true ;
}
// scorro i tratti omogenei e cerco il tratto del tipo definito più lungo
int nMaxInd = -1 ;
double dMaxLen = 0. ;
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
// considero solo i tratti aperti
if ( vpCrvs[i]->GetTempProp( 0) == nType) {
// calcolo la lunghezza del tratto aperto
double dLen ;
vpCrvs[i]->GetLength( dLen) ;
if ( dLen > dMaxLen) {
dMaxLen = dLen ;
nMaxInd = i ;
}
}
}
// recupero il tratto del tipo definito più lungo
if ( nMaxInd != -1) {
// recupero il parametro alla sua metà
double dParIn ;
vpCrvs[nMaxInd]->GetParamAtLength( 0.5 * dMaxLen, dParIn) ;
// se tratto OPEN
if ( nType == TEMP_PROP_OPEN_EDGE) {
bMidOut = true ;
vpCrvs[nMaxInd]->GetPointD1D2( dParIn, ICurve::FROM_MINUS, ptStart, &vtMidOut) ;
vtMidOut.Normalize() ;
vtMidOut.Rotate( Z_AX, 0, -1) ;
}
// se tratto CLOSED
else
vpCrvs[nMaxInd]->GetPointD1D2( dParIn, ICurve::FROM_MINUS, ptStart) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPtStartOnGenericEdge( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
Point3d& ptStart)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
pCrvCompo->GetStartPoint( ptStart) ;
// scorro tutte le curve
double dLenRef = 0. ;
bool bAllShort = true ; // flag per curve tutte piccole
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
// ricavo la curva i-esima dalla curva originale
const ICurve* pMyCrv = pCrvCompo->GetCurve( i) ;
if ( pMyCrv == nullptr || ! pMyCrv->IsValid())
return false ;
// ricavo la lunghezza di tale curva
double dLen = 0. ;
pMyCrv->GetLength( dLen) ;
// se lunghezza accettabile o maggiore della massima trovata, ricavo ptStart
if ( dLen > dLenRef) {
if ( pMyCrv->GetMidPoint( ptStart)) {
dLenRef = dLen ;
bAllShort = false ;
}
}
}
// se curve tutte piccole
if ( bAllShort) {
bool bMidOutFake ;
Vector3d vtMidOutFake ;
if ( ! GetPtStartOnHomogeousEdges( pCrvCompo, PockParams, TEMP_PROP_CLOSE_EDGE, P_INVALID,
bMidOutFake, ptStart, vtMidOutFake))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetPtStartOnGenericEdgeByPtRef( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
const Point3d& ptRef, Point3d& ptStart)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 ||
! ptRef.IsValid())
return false ;
// ordino le curve di pCrvCompo in base alla vicinanza da ptRef
// <indice della curva, distanza da ptRef>
vector<pair<int, double>> vCrvInfoDist ; vCrvInfoDist.resize( pCrvCompo->GetCurveCount()) ;
// scorro le curve di pCrvCompo
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
vCrvInfoDist[i].first = i ;
// calcolo la distanza tra la curva i-esima e ptRef
double dDist = INFINITO ;
DistPointCurve DistPtCrv( ptRef, *pCrvCompo->GetCurve( i)) ;
DistPtCrv.GetSqDist( dDist) ;
vCrvInfoDist[i].second = dDist ;
}
// ordino il vettore in base alla distanza
sort( vCrvInfoDist.begin(), vCrvInfoDist.end(), [] ( const pair<int, double>& a, const pair<int, double>& b) {
return a.second < b.second ;
}) ;
// cerco un parametro iniziale valido per le sottocurve ordinate
double dLenRef = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ;
for ( int i = 0 ; i < int( vCrvInfoDist.size()) ; ++ i) {
// recupero la curva i-esima
const ICurve* pMyCrv = pCrvCompo->GetCurve( vCrvInfoDist[i].first) ;
if ( pMyCrv != nullptr && pMyCrv->IsValid()) {
// controllo che sia abbastanza lunga e aggiorno ptStart
double dLen = 0. ;
pMyCrv->GetLength( dLen) ;
if ( dLen > dLenRef) {
if ( pMyCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart))
return true ;
}
}
}
// alla peggio...
bool bMidOutFake ;
Vector3d vtMidOutFake ;
return GetPtStartOnHomogeousEdges( pCrvCompo, PockParams, TEMP_PROP_CLOSE_EDGE, ptRef,
bMidOutFake, ptStart, vtMidOutFake) ;
}
//----------------------------------------------------------------------------
static bool
AssignOpenCloseTmpPropToFirstOffsCurve( ICurveComposite* pCrv, const PocketParams& PockParams,
const ISurfFlatRegion* pSfrToWork, bool& bSomeOpen)
{
// controllo dei parametri
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0 ||
pSfrToWork == nullptr || ! pSfrToWork->IsValid() || pSfrToWork->GetChunkCount() != 1)
return false ;
bSomeOpen = false ; // flag per presenza di lati aperti
int nInd = -1 ; // indice della prima curva che non è "raccordo" di Offset
// scorro tutte le curve presenti in pCrv ( curva da cui devo entrare)
for ( int i = 0 ; i < pCrv->GetCurveCount() ; ++ i) {
// nProp0 -> #curva il cui Offset ha generato la curva i-esima di pCrv
int nProp0 = 0 ; pCrv->GetCurveTempProp( i, nProp0, 0) ;
// nProp1 -> #loop che contiene la curva espressa in nProp0
int nProp1 = 0 ; pCrv->GetCurveTempProp( i, nProp1, 1) ;
// se questa curva non è un "raccordo" di Offset
if ( nProp0 > 0) {
// aggiorno la proprietà della curva da cui devo entrare
int nTempProp = TEMP_PROP_CLOSE_EDGE ;
pSfrToWork->GetCurveTempProp( 0, nProp1, nProp0 - 1, nTempProp, 0) ;
pCrv->SetCurveTempProp( i, nTempProp, 0) ;
// se la curva è aperta, aggiorno il Flag
if ( nTempProp == TEMP_PROP_OPEN_EDGE && ! bSomeOpen)
bSomeOpen = true ;
// salvo l'indice della prima curva trovata che non è un "raccordo" di Offset
if ( nInd == -1)
nInd = i ;
}
// se questa curva è un "raccordo" di Offset
else {
pCrv->SetCurveTempProp( i, TEMP_PROP_SMOOTH, 0) ;
pCrv->SetCurveTempProp( i, TEMP_PROP_SMOOTH, 1) ;
}
}
// cambio il punto iniziale all'inizio della prima curva che non deriva da un "raccordo"
Point3d ptStart ;
if ( nInd != -1) {
if ( ! pCrv->GetStartPoint( ptStart))
return false ;
pCrv->ChangeStartPoint( nInd) ; // la prima curva non è un "raccordo" di Offset
}
// scorro tutte le curve successive alla prima
for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++ i) {
int nTmpProp0, nTmpProp1 ;
// se "raccordo" copio la tempProp precedente
if (( pCrv->GetCurveTempProp( i, nTmpProp0, 0) && nTmpProp0 == TEMP_PROP_SMOOTH) &&
( pCrv->GetCurveTempProp( i, nTmpProp1, 1) && nTmpProp1 == TEMP_PROP_SMOOTH))
// copio la temp prop della curva precedente
pCrv->SetCurveTempProp( i, pCrv->GetCurve( i-1)->GetTempProp( 0), 0) ;
}
// lascio invariato il punto iniziale della curva originaria
if ( nInd != -1) {
double dUNewS ;
pCrv->GetParamAtPoint( ptStart, dUNewS) ;
pCrv->ChangeStartPoint( dUNewS) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
SetPtStartForPath( ICurveComposite* pCrvOffs, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut,
const ICurveComposite* pCrvOrig)
{
/*
Come prima cosa ricavo i lati aperti equivalenti dal primo Offset; Facendo l'Offset di una curva
chiusa ho all'interno delle temp prop delle sottcurve due informazioni :
// nTmpProp0 -> #curva il cui Offset ha generato la curva i-esima attuale
// nTmpProp1 -> #loop che contiene la curva espressa in nTmpProp0
Quindi dal contorno con i lati aperti impostati, posso settare i lati aperti "equivalenti"
anche sulla curva di primo Offset.
NB. Il tool entra dalla curva di primo Offset, non dal bordo della regione da svuotare; quindi
l'entrata va calcolata sul primo Offset, non sulla curva di bordo della FlatRegion di Pocketing.
*/
// controllo dei parametri
if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0 ||
pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1)
return false ;
// assegno proprietà di OPEN/CLOSE alle sottocurve dell'offset corrente
bool bSomeOpen = false ;
if ( ! AssignOpenCloseTmpPropToFirstOffsCurve( pCrvOffs, PockParams, pSrfToWork, bSomeOpen))
return false ;
// punto valido per l'entrata ...
bMidOut = false ;
if ( bSomeOpen) {
// se esistono lati aperti...
if ( pCrvOrig != nullptr && pCrvOrig->IsValid()) {
// ... e non ho un punto di riferimento, lo cerco sulla curva originale
if ( ! ptEndPrec.IsValid()) {
if ( ! GetPtStartOnOpenEdgeByOrigCurve( pCrvOrig, PockParams, pCrvOffs, ptStart, vtMidOut, bMidOut))
return false ;
}
// ... e ho un punto di riferimento
else {
if ( ! GetPtStartOnOpenEdgeByPtRef( ptEndPrec, PockParams, pCrvOffs, pCrvOrig, ptStart, vtMidOut, bMidOut))
return false ;
}
}
// se punto non trovato precedentemente, tento con una sottocurva OPEN generica
if ( ! bMidOut) {
if ( ! GetParamOnOpenCurve( pCrvOffs, PockParams, bMidOut, ptStart, vtMidOut))
return false ;
}
// se queste curve sono troppo corte, analizzo le parti uniformi
if ( ! bMidOut) {
if ( ! GetPtStartOnHomogeousEdges( pCrvOffs, PockParams, TEMP_PROP_OPEN_EDGE, ptEndPrec,
bMidOut, ptStart, vtMidOut))
return false ;
}
}
// se sottocurve tutte CLOSED o un punto iniziale non valido, lo cerco su un lato generico
if ( ! bMidOut) {
if ( ! ptEndPrec.IsValid()) {
if ( ! GetPtStartOnGenericEdge( pCrvOffs, PockParams, ptStart))
return false ;
}
else {
if ( ! GetPtStartOnGenericEdgeByPtRef( pCrvOffs, PockParams, ptEndPrec, ptStart))
return false ;
}
}
// imposto il punto iniziale scelto
double dPar = 0. ;
if ( ! pCrvOffs->GetParamAtPoint( ptStart, dPar) || ! pCrvOffs->ChangeStartPoint( dPar))
pCrvOffs->ChangeStartPoint( .5) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
SetAdvancedPtStartForPath( ICRVCOMPOPOVECTOR& vCrvOffsAct, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut,
bool& bMidOut, int& nIndex, ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops)
{
// controllo dei parametri
for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i)
if ( IsNull( vCrvOffsAct[i]) || ! vCrvOffsAct[i]->IsValid() || vCrvOffsAct[i]->GetCurveCount() == 0)
return false ;
if ( pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1)
return false ;
nIndex = 0 ;
// scorro le curve di primo Offset
// NB. in prima posizione ho il primo Offset del loop esterno, a seguire le isole
// NB. Se non riesco ad entrare in nessuna curva, allora tengo valida quella calcolata per il loop esterno
bool bMidOut_fist = false ;
Vector3d vtMidOut_first ;
Point3d ptStart_first ;
for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i) {
// cambio il suo punto d'inizio dell'Offset attuale
if ( SetPtStartForPath( vCrvOffsAct[i], PockParams, pSrfToWork, ptEndPrec, ptStart, vtMidOut,
bMidOut, i < ( int( vCrvOrigChunkLoops.size())) ? Get( vCrvOrigChunkLoops[i]) : nullptr))
vCrvOffsAct[i]->GetStartPoint( ptStart) ;
else
return false ;
// se ho trovato un'entrata valida da un lato aperto, ho finito
if ( bMidOut) {
nIndex = i ; // salvo l'indice del vettore
return true ;
}
// se sono nel loop esterno e non ho trovato un'entrata da un lato aperto, salvo i risultati ottenuti
else if ( i == 0) {
bMidOut_fist = bMidOut ;
vtMidOut_first = vtMidOut ;
ptStart_first = ptStart ;
}
}
// in questo caso non ho trovato un'entrata da un lato aperto in nessuna curva ( potrebbero essere tutte chiuse)
bMidOut = bMidOut_fist ;
vtMidOut = vtMidOut_first ;
ptStart = ptStart_first ;
return true ;
}
// ***************************************************************************
//---------------------------- CASI OTTIMIZZATI ------------------------------
// ***************************************************************************
//----------------------------------------------------------------------------
static bool
GetOptCrvIndex( const ISurfFlatRegion* pSfrOrig, const ISurfFlatRegion* pSfrChunk,
const PocketParams& PockParams, int nReg, ICRVCOMPOPOVECTOR& vCrvOrig)
{
/*
NB. La superficie da lavorare è stata estesa presso i lati aperti; cerco il Chunk
di riferimento sulla superficie originale ed estraggo le sue curve ( in prima posizione
il loop esterno e a seguire le isole); queste curve verranno utilizzate per la scelta
dei casi ottimizzati ( trapezi, circonferenze, ecc ecc..)
*/
// controllo dei parametri
if ( pSfrOrig == nullptr || ! pSfrOrig->IsValid() ||
pSfrChunk == nullptr || ! pSfrChunk->IsValid())
return false ;
// se la superficie originale ha un solo Chunk, allora le curve originali sono già trovate
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( 0) ; ++ l)
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( 0, l))) ;
if ( pSfrOrig->GetChunkCount() == 1)
return true ;
// vettore di indici dei chunk candidati
INTVECTOR vInds ;
// scorro tutti i Chunk della superficie orginaria
for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) {
// la classificazione può essere fatta semplicemente guardando il Loop esterno
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ;
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
return false ;
// classifico il Chunk attuale con la curva in esame
CRVCVECTOR ccClass ;
if ( pSfrChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) {
// se il Chunk attuale è stato esteso presso presso i lati aperti mi aspetto che esso non sia più
// grande del chunk originale ( se non ci sono lati aperti, allora coincide )
// se il ccClass non contiene parti classificate come esterne allora il Chunk di riferimento è il c-esimo
bool bIsThis = true ;
for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) {
if ( ccClass[k].nClass == CRVC_OUT)
bIsThis = false ;
}
if ( bIsThis)
vInds.push_back( c) ;
}
}
// se ho un solo indice allora ho già identificato le curve originali
if ( int( vInds.size()) == 1) {
vCrvOrig.clear() ;
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( vInds[0]) ; ++ l)
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( vInds[0], l))) ;
return true ;
}
// altrimenti...
/*
NB. Come parametro alla funzione è stato passato "nReg"; Facendo il primo Offset del Chunk attuale
questo verrà diviso in n sottoregioni; devo considerare la nReg-esima
Situazione: Estendendo presso i lati aperti, due ( o più) chunk si sono uniti formandone uno unico
Quando effettuo il primo offset ( del diametro del materiale + offset radiale), questo Chunk si splitterà in n Chunk;
Uno ( o più) di questi nuovi chunk potrebbe corrispondere ad un caso ottimizzato.
Devo recuperare il Chunk della superficie originaria ( che non essendo estesa presso i lati aperti
ha la geomtria originale)
*/
// effettuo un Offset del Chunk per simulare la creazione degli n Chunks
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
PtrOwner<ISurfFlatRegion> pSfrRefChunk( pSfrChunk->Clone()) ;
if ( IsNull( pSfrRefChunk) ||
! pSfrRefChunk->Offset( - dOffs, ICurve::OFF_FILLET))
return false ;
// clono il Chunk nReg-esimo se esiste
pSfrRefChunk.Set( pSfrRefChunk->CloneChunk( nReg)) ;
if ( IsNull( pSfrRefChunk))
return true ; // bisogna passare al ( pSfrChunk + 1)-esimo se esiste
// scorro tutti i Chunk della superficie orginaria e ripeto la classificazione
// NB. Questa volta posso fermarmi al primo Chunk classificato come non esterno
for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) {
// come prima recupero il Loop esterno
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ;
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
return false ;
// classificazione
CRVCVECTOR ccClass ;
if ( pSfrRefChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) {
bool bIsThis = true ;
for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) {
if ( ccClass[k].nClass == CRVC_OUT)
bIsThis = false ;
}
if ( bIsThis) {
vCrvOrig.clear() ;
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( c) ; ++ l)
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( c, l))) ;
return true ; // esco
}
}
}
return true ; // alla peggio non considero casi ottimizzati e svuoto normalmente...
}
//----------------------------------------------------------------------------
static bool
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 raccord
vCrvArcs[i]->SetTempProp( TEMP_PROP_SMOOTH, 1) ;
if ( ! pCrvCO_temp->AddCurve( vCrvArcs[i]->Clone())) // aggiungo l'arco di raccordo
return false ;
ptArcHelp = ptArcE ;
}
else { // se non esiste l'arco ...
if ( i == 0 ) { // e sono nella prima iterazione...
if ( ! vCrvStepsToFill[0]->GetEndPoint( ptArcHelp))
return false ;
if ( ! pCrv->IsClosed()) { // ...e se aperta // aggiungo la prima curva per intero
if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUE_ref) ||
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref))))
return false ;
}
ptFirstPoint = ptArcHelp ;
}
else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...)
double dUS_cm ;
if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
! vCrvStepsToFill[i]->GetDomain( dUS_cm, dUE_ref) ||
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref))) ||
! vCrvStepsToFill[i]->GetEndPoint( ptArcHelp))
return false ;
}
}
}
// ultima curva...
if ( pCrv->IsClosed()) { // se curva chiusa...
if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
! vCrvStepsToFill[0]->GetParamAtPoint( ptFirstPoint, dUE_ref) ||
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( dUS_ref, dUE_ref))))
return false ;
}
else { // se curva aperta... ( non ha importanza l'esistenza o meno degli archi...)
double dUS_cm ;
if ( ! vCrvStepsToFill.back()->GetParamAtPoint( ptArcHelp, dUS_ref) ||
! vCrvStepsToFill.back()->GetDomain( dUS_cm, dUE_ref) ||
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill.back()->CopyParamRange( dUS_ref, dUE_ref))))
return false ;
}
// ripristino il punto inziale se la curva è chiusa
double dNewDU ;
if ( ! pCrv->IsClosed()) {
if ( ! pCrvCO_temp->GetParamAtPoint( ptArcHelp, dNewDU))
return false ;
pCrvCO_temp->ChangeStartPoint( dNewDU) ;
}
// restituisco
int nProp0 = pCrv->GetTempProp( 0) ;
int nProp1 = pCrv->GetTempProp( 1) ;
double dParam0 = pCrv->GetTempParam( 0) ;
double dParam1 = pCrv->GetTempParam( 1) ;
pCrv->Clear() ;
pCrv->AddCurve( Release( pCrvCO_temp)) ;
pCrv->SetTempProp( nProp0, 0) ;
pCrv->SetTempProp( nProp1, 1) ;
pCrv->SetTempParam( dParam0, 0) ;
pCrv->SetTempParam( dParam1, 1) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, const ICRVCOMPOPOVECTOR& vOffIslands,
ICurveComposite* pCrvLink)
{
// pulisco
pCrvLink->Clear() ;
// 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
ModifyBiArc( ICurve* pBiArcLink, double dToll, ICurveComposite* pNewBiArc)
{
// controllo dei parametri
if ( pBiArcLink == nullptr)
return false ;
if ( dToll > 0.99 || dToll < 0.01)
return false ;
pNewBiArc->Clear() ;
// dominio
double dUS, dUE ;
pBiArcLink->GetDomain( dUS, dUE) ;
// prendo i due archi della curva BiArco
PtrOwner<ICurve> pArc1( pBiArcLink->CopyParamRange( dUS, ( dUS + dUE) / 2)) ;
PtrOwner<ICurve> pArc2( pBiArcLink->CopyParamRange(( dUS + dUE ) / 2, dUE)) ;
if ( IsNull( pArc1) || ! pArc1->IsValid() || IsNull( pArc2) || ! pArc2->IsValid())
return false ;
// primo pezzo
pArc1->GetDomain( dUS, dUE) ;
PtrOwner<ICurve> pArc1A( pArc1->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1
PtrOwner<ICurve> pArc1B( pArc1->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2
Point3d pt1A, pt1B ;
pArc1A->GetEndPoint( pt1A) ;
pArc1B->GetStartPoint( pt1B) ;
PtrOwner<CurveLine> pLine1( CreateBasicCurveLine()) ;
pLine1->Set( pt1A, pt1B) ;
// secondo pezzo
pArc2->GetDomain( dUS, dUE) ;
PtrOwner<ICurve> pArc2A( pArc2->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1
PtrOwner<ICurve> pArc2B( pArc2->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2
Point3d pt2A, pt2B ;
pArc2A->GetEndPoint( pt2A) ;
pArc2B->GetStartPoint( pt2B) ;
PtrOwner<CurveLine> pLine2( CreateBasicCurveLine()) ;
pLine2->Set( pt2A, pt2B) ;
// ricostruisco il nuovo link
if ( ! pNewBiArc->AddCurve( Release( pArc1A)) ||
! pNewBiArc->AddCurve( Release( pLine1)) ||
! pNewBiArc->AddCurve( Release( pArc1B)) ||
! pNewBiArc->AddCurve( Release( pArc2A)) ||
! pNewBiArc->AddCurve( Release( pLine2)) ||
! pNewBiArc->AddCurve( Release( pArc2B)))
return false ;
return true ;
}
//------------------------------------------------------------------------------
static bool
CalcBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd,
const Vector3d& vtEnd, double dParMeet, const ICRVCOMPOPOVECTOR& vOffIslands,
const PocketParams& PockParams, ICurveComposite* pCrvLink)
{
// se senza smusso, ritorno tratto lineare
if ( ! PockParams.bSmooth)
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
// creo il BiArc che unisce i due punti
double dAngStart, dAngEnd ;
vtStart.GetAngleXY( X_AX, dAngStart) ;
vtEnd.GetAngleXY( X_AX, dAngEnd) ;
PtrOwner<ICurve> pBiArcLink ;
if ( dParMeet != 0)
pBiArcLink.Set( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, dParMeet)) ;
else {
PtrOwner<CurveArc> pCrvCir( CreateBasicCurveArc()) ;
if ( IsNull( pCrvCir))
return false ;
pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, PockParams.bInvert ? 360 : - 360, 0, Z_AX) ;
pBiArcLink.Set( pCrvCir->Clone()) ;
pCrvLink->AddCurve( pBiArcLink->Clone()) ;
return true ;
}
if ( IsNull( pBiArcLink))
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
// se BiArco troppo grande allora lo modifico
double dLenBiArc ;
pBiArcLink->GetLength( dLenBiArc) ;
if ( dLenBiArc > 200) {
PtrOwner<CurveComposite> pCrvNewBiArcS( CreateBasicCurveComposite()) ;
ModifyBiArc( pBiArcLink, 0.2, pCrvNewBiArcS) ;
pBiArcLink.Set( pCrvNewBiArcS) ;
}
// creo la nuova curva formata inizialmente dai tratti di archi
PtrOwner<ICurveComposite> pCompo( GetCurveComposite( pBiArcLink->Clone())) ;
if ( IsNull( pCompo))
return false ;
PtrOwner<ICurveComposite> pCompoHelp( GetCurveComposite( pBiArcLink->Clone())) ;
if ( IsNull( pCompoHelp))
return false ;
// scorro tutte le curve per controllare le intersezioni ...
for ( int i = 0 ; i < int( vOffIslands.size()) ; ++ i) {
CRVCVECTOR ccClass ;
IntersCurveCurve intCC( *pCompo, *vOffIslands[i]) ;
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
if ( ! pCompoHelp->Clear())
return false ;
// per ogni intersezione j con l'offset dell'isola i
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
// se ho intersezione spezzo il segmento
if ( ccClass[j].nClass == CRVC_OUT && int( ccClass.size()) > 1) {
Point3d ptS ;
pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ;
double dOffS ;
vOffIslands[i]->GetParamAtPoint( ptS, dOffS, 1500 * EPS_SMALL) ;
Point3d ptE ;
pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ;
double dOffE ;
vOffIslands[i]->GetParamAtPoint( ptE, dOffE, 1500 * EPS_SMALL) ;
// recupero i due possibili percorsi e uso il più corto
PtrOwner<ICurve> pCrvA( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ;
PtrOwner<ICurve> pCrvB( vOffIslands[i]->CopyParamRange( dOffE, dOffS)) ;
if ( IsNull( pCrvA) || IsNull( pCrvB))
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
double dLenA ; pCrvA->GetLength( dLenA) ;
double dLenB ; pCrvB->GetLength( dLenB) ;
if ( j != 0) {
if ( dLenA < dLenB) {
if ( ! pCompoHelp->AddCurve( Release( pCrvA)))
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
}
else {
pCrvB->Invert() ;
if ( ! pCompoHelp->AddCurve( Release( pCrvB)))
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
}
}
else {
pCrvB->Invert() ;
if ( ! pCompoHelp->AddCurve( Release( pCrvB)))
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
}
}
// se non interseco
else {
if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)))
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
}
}
pCompo->Clear() ;
if ( ! pCompo->AddCurve( pCompoHelp->Clone()))
if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink))
return false ;
}
// controllo le dimensioni del BiArco
BBox3d bBox3 ;
if ( pCompo->GetLocalBBox( bBox3)) {
double dRadBB ;
// se troppo piccolo, lo approssimo con un segmento
if ( bBox3.GetRadius( dRadBB) && dRadBB < 500 * EPS_SMALL) {
if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink))
return false ;
return true ;
}
// se troppo grande, lo approssimo con un segmento
if ( bBox3.GetRadius( dRadBB) && dRadBB > 3 * PockParams.dRad) {
if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink))
return false ;
return true ;
}
}
PtrOwner<ICurveComposite> pCrvCompo_noSmooth( pCompo->Clone()) ;
if ( ! ModifyCurveToSmoothed( pCompo, PockParams, 0.05, 0.05, true) ||
! pCompo->IsValid())
pCompo.Set( pCrvCompo_noSmooth) ;
pCrvLink->AddCurve( Release( pCompo)) ;
return true ;
}
//----------------------------------------------------------------------------
static bool
CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, const ICRVCOMPOPOVECTOR& vFirstOffset,
const PocketParams& PockParams, double dLenPercS, double dLenPercE,
ICurveComposite* pCrvLink)
{
// controllo i parametri
if ( pCrvS == nullptr || pCrvE == nullptr )
return false ;
pCrvLink->Clear() ;
// curva finale da restituire
PtrOwner<CurveComposite> ptCrvFinal( CreateBasicCurveComposite()) ;
if ( IsNull( ptCrvFinal))
return false ;
// Prendo i punti, i vettori tangenti e i parametri di essi per le curve
Point3d ptSS, ptSE, ptES, ptEE ;
Vector3d vS, vE ;
double dUSS, dUSE, dUES, dUEE, dLenS, dLenE ;
dUSS = 0 ; dUSE = 0 ;
pCrvS->GetEndPoint( ptSE) ;
pCrvS->GetStartPoint( ptSS) ;
pCrvE->GetStartPoint( ptES) ;
pCrvE->GetEndPoint( ptEE) ;
pCrvS->GetEndDir( vS) ;
pCrvE->GetStartDir( vE) ;
pCrvS->GetDomain( dUSS, dUSE) ;
pCrvE->GetDomain( dUES, dUEE) ;
pCrvS->GetLength( dLenS) ;
pCrvE->GetLength( dLenE) ;
// se ho una curva di primo Offset allora non devo accorciarla ... ( si per bordi esterni che per isole)
for ( int i = 0 ; i < int( vFirstOffset.size()) ; ++ i) {
if ( vFirstOffset[i]->IsPointOn( ptSS) && vFirstOffset[i]->IsPointOn( ptSE))
dLenPercS = 0 ;
if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE))
dLenPercE = 0 ;
}
double dLStepS = ( dLenPercS < EPS_SMALL ? 0. : PockParams.dRad) ;
double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : PockParams.dRad) ;
dLenE = 0 ;
/*
calcolo il biArco tra il punto iniziale e finale dei due offset consecutivi
se richiesto lo smusso -> accorcio entrambi i tratti di Offset e, riuscendo, si sotituisce
il biArco calcolato prima con quest'ultimo
*/
int nMaxIter = 2 ;
if ( ! PockParams.bSmooth)
nMaxIter = 1 ;
// taglio la curva al massimo nMaxIter-volte e mi fermo al taglio più opportuno
int nIter = 0 ;
while ( nIter < nMaxIter) {
// calcolo il biArco
PtrOwner<CurveComposite> ptBiArc( CreateBasicCurveComposite()) ;
if ( ! CalcBoundedSmoothedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, PockParams, ptBiArc) ||
ptBiArc->GetCurveCount() == 0)
return false ;
ptCrvFinal->Clear() ;
ptCrvFinal.Set( ptBiArc->Clone()) ;
if ( dLenPercE == 0 && dLenPercS == 0 )
break ;
// se ho trovato un BiArco valido, allora accorcio le due curve di Offset
dLenS -= dLStepS ;
dLenE += dLStepE ;
// ricalcolo il punto iniziale e finale
pCrvS->GetParamAtLength( dLenS, dUSE) ;
pCrvS->GetPointD1D2( dUSE, ICurve::FROM_MINUS, ptSE) ;
pCrvE->GetParamAtLength( dLenE, dUES) ;
pCrvE->GetPointD1D2( dUES, ICurve::FROM_PLUS, ptES) ;
// ricalcolo il vettore tangente iniziale e finale
PtrOwner<ICurveComposite> pCrvS_clone( CloneCurveComposite( pCrvS)) ;
PtrOwner<ICurveComposite> pCrvE_clone( CloneCurveComposite( pCrvE)) ;
pCrvS_clone->ChangeStartPoint( dUSE) ;
pCrvE_clone->ChangeStartPoint( dUES) ;
pCrvS_clone->GetStartDir( vS) ;
pCrvE_clone->GetStartDir( vE) ;
++ nIter ;
}
pCrvLink->AddCurve( Release( ptCrvFinal)) ; // ultimo arco valido trovato
return true ;
}
//----------------------------------------------------------
static bool
GetUnclearedRegionAndSetFeed( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks,
const ICurveComposite* pCrv_orig, const PocketParams& PockParams, ISurfFlatRegion* pSrfToCut)
{
// controllo dei parametri
if ( vFirstOffs.empty() || int( vOffs.size()) < int( vFirstOffs.size()))
return false ;
pSrfToCut->Clear() ;
// 1) creo la regione esterna
PtrOwner<ISurfFlatRegion> pSrfExtern( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfExtern))
return false ;
for ( int i = 0 ; i < int( vFirstOffs.size()) ; ++ i) {
if ( i == 0)
pSrfExtern->AddExtLoop( vFirstOffs[i]->Clone()) ;
else {
PtrOwner<ICurve> pCrvIntLoop( vFirstOffs[i]->Clone()) ;
if ( IsNull( pCrvIntLoop) ||
! pCrvIntLoop->Invert() ||
! pSrfExtern->AddIntLoop( Release( pCrvIntLoop)))
return false ;
}
}
// ---- le seguenti regioni sono distinte per calcolar emeglio la Feed ----
// Creo la regione svuotata dal Tool negli Offset, nei Links e sia dagli Offsets che dai Links
PtrOwner<ISurfFlatRegion> pSfrTool( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrTool))
return false ;
// creo un vettore che conterrà solamente i Link percorsi fino ad Ora
bool bFollowOrder = ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_CONFORMAL_ZIGZAG) ;
for ( int i = ( bFollowOrder ? 0 : int( vOffs.size()) - 1) ;
( bFollowOrder ? i < int( vOffs.size()) : i >= 0 ) ;
( bFollowOrder ? ++ i : -- i)) {
// ================== OFFSET =============================
// recupero le proprietà temporanee
int nProp0 = vOffs[i]->GetTempProp( 0) ;
int nProp1 = vOffs[i]->GetTempProp( 1) ;
// Feed
bool bFirstOffs = false ;
Point3d ptStart ; vOffs[i]->GetStartPoint( ptStart) ;
for ( int j = 0 ; j < int( vFirstOffs.size()) && ! bFirstOffs ; ++ j)
bFirstOffs = vFirstOffs[j]->IsPointOn( ptStart, 100 * EPS_SMALL) ;
// calcolo gli intervalli di Feed per la curva di Offset
if ( ! AssignFeedSpiral( vOffs[i], pSfrTool, false, bFirstOffs, pCrv_orig, PockParams, 2 * PockParams.dRad / 3))
return false ;
// aggiorno la superificie svuotata
PtrOwner<ISurfFlatRegion> pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( vOffs[i]->Clone() , PockParams.dRad + 5 * EPS_SMALL, false, false)) ;
if ( ! IsNull( pSrfToolRegOffi)) {
if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0)
pSfrTool.Set( pSrfToolRegOffi) ;
else
pSfrTool->Add( *pSrfToolRegOffi) ;
}
vOffs[i]->SetTempProp( nProp0, 0) ;
vOffs[i]->SetTempProp( nProp1, 1) ;
// ================= LINK ================================
int nLink_Ind = ( bFollowOrder ? i + 1 : i) ;
// ( Il primo link è nullo, infatti non vi è nessun raccordo per raggiungere il primo Offset)
if ( nLink_Ind < int( vLinks.size()) && ! IsNull( vLinks[nLink_Ind]) &&
vLinks[nLink_Ind]->IsValid()) {
// recupero le proprietà temporanee
int nProp0 = vLinks[nLink_Ind]->GetTempProp( 0) ;
int nProp1 = vLinks[nLink_Ind]->GetTempProp( 1) ;
// Feed
PtrOwner<ICurveComposite> pCrvLink( ConvertCurveToComposite( vLinks[nLink_Ind]->Clone())) ;
// calcolo gli intervalli di Feed per la curva di Link
if ( IsNull( pCrvLink) || ! pCrvLink->IsValid() ||
! AssignFeedSpiral( pCrvLink, pSfrTool, true, false, pCrv_orig, PockParams, 2 * PockParams.dRad / 3))
return false ;
// aggiorno la regione svuotata
PtrOwner<ISurfFlatRegion> pSrfToolRegLinki( GetSurfFlatRegionFromFatCurve( pCrvLink->Clone(), PockParams.dRad + 5 * EPS_SMALL, false, false)) ;
if ( ! IsNull( pSrfToolRegLinki)) {
if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0)
pSfrTool.Set( pSrfToolRegLinki) ;
else
pSfrTool->Add( *pSrfToolRegLinki) ;
}
// risetto il Link come curva composita ...
pCrvLink->SetTempProp( nProp0, 0) ;
pCrvLink->SetTempProp( nProp1, 1) ;
vLinks[nLink_Ind].Set( pCrvLink) ;
}
}
// 4) Creo la regione contenente tutte le parti non svuotate
pSrfToCut->CopyFrom( pSrfExtern) ;
if ( ! pSrfToCut->Subtract( *pSfrTool))
return false ;
return true ;
}
//----------------------------------------------------------------------------
static bool
RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs,
ICRVCOMPOPOVECTOR& vOffsFirstCurve, const PocketParams& PockParams)
{
/*
E' richiesto che le curve di Offset presentino le seguenti TempProps :
- TempProp0 -> Offset progressivo ricavato ( 0, 1, 2, ... nMaxIter) [per CONFORMAL]
- TempProp1 -> parte esterna della curva ( MDS_LEFT o MDS_LEFT) [per CONFORMAL]
[per SPIRAL le proprietà sono ricavate automaticamente]
*/
// se non ho nessuna superficie valida da svuotare, allora esco
if ( pSfrUncleared == nullptr)
return false ;
if ( ! pSfrUncleared->IsValid() || pSfrUncleared->GetChunkCount() == 0)
return true ;
// scorro tutti i Chunk della regione
ICRVCOMPOPOVECTOR vCrvCurl ;
for ( int nC = 0 ; nC < pSfrUncleared->GetChunkCount() ; ++ nC) {
// recupero il centroide del Chunk da rimuovere
Point3d ptCentroid ; pSfrUncleared->GetChunkCentroid( nC, ptCentroid) ;
/*
Tra tutte le curve di Offset escludo le curve che :
- Sono di primo Offset
- Non contengono il centroide ( condizione già verificata per quelle di primo Offset)
... e cerco la più vicina tra le rimanenti
*/
int nInd = -1 ;
double dRefDist = INFINITO - 1 ;
Point3d ptCloser ; // serve nel caso non bastasse la curva a ricciolo per svuotare il chunk nC-esimo
int nFlag ;
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
// se la curva è di primo Offset, allora la salto
Point3d ptCheck ; vOffs[i]->GetStartPoint( ptCheck) ;
bool bSkip = false ;
for ( int j = 0 ; j < int( vOffsFirstCurve.size()) && ! bSkip ; ++ j) {
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_SPIRALOUT)
bSkip = ( vOffsFirstCurve[j]->IsPointOn( ptCheck, 100 * EPS_SMALL)) ;
else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG)
bSkip = ( vOffs[i]->GetTempProp( 0) == 0) ;
}
if ( bSkip)
continue ;
// se la curva contiene il centroide, allora la salto
int nSide ;
DistPointCurve DistPtCrv( ptCentroid, *vOffs[i]) ;
if ( DistPtCrv.GetSideAtMinDistPoint( EPS_SMALL, Z_AX, nSide)) {
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_SPIRALOUT) {
if ( nSide == ( ! PockParams.bInvert ? MDS_LEFT : MDS_RIGHT))
continue ;
}
else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) {
if ( nSide == vOffs[i]->GetTempProp( 1))
continue ;
}
}
// controllo se la distanza è ammissibile
double dCurrDist = INFINITO ;
if ( DistPtCrv.GetSqDist( dCurrDist) && dCurrDist < dRefDist) {
dRefDist = dCurrDist ;
nInd = i ;
DistPtCrv.GetMinDistPoint( EPS_SMALL, ptCloser, nFlag) ;
}
}
// se non ho trovato alcun indice, salto il Chunk nC-esimo
if ( nInd == -1)
continue ;
// cerco sulla curva di Offset nInd-esima la sottocurva dove cade il punto a minima distanza
double dURef = 0 ;
if ( ! vOffs[nInd]->GetParamAtPoint( ptCloser, dURef))
continue ;
int nCrv = int( floor( dURef)) ;
const ICurve* pCrv = vOffs[nInd]->GetCurve( nCrv) ;
if ( pCrv == nullptr || ! pCrv->IsValid())
continue ;
// determino se la curva è di raccordo o meno
bool bIsSmoothCrv = ( PockParams.bSmooth && pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ;
// clono la curva di Offset corrente ( in questo modo non modifico l'originale)
PtrOwner<ICurveComposite> pMyOffs( CloneCurveComposite( vOffs[nInd])) ;
if ( IsNull( pMyOffs) || ! pMyOffs->IsValid())
return false ;
// versore uscente ed entrante dall'Offset attuale per creazione della curva a ricciolo
Vector3d vt1, vt2, vtMain ;
// punto di uscita e di ingresso dall'Offset attuale per creazione della curva a ricciolo
Point3d pt1, pt2, ptMain ;
// paramtro di uscita e di ingresso dall'Offset attuale per creazione della curva a ricciolo
double dURef1, dURef2 ;
// inizializzo la curva a ricciolo
PtrOwner<ICurveComposite> pCrvCurl( CreateCurveComposite()) ;
if ( IsNull( pCrvCurl))
return false ;
// se è di raccordo, "ricostruisco lo spigolo vivo della curva originale di Offset"
if ( bIsSmoothCrv) {
// cerco la prima curva in precedenza e in successione non di raccordo
vt1 = V_INVALID ;
for ( int i = nCrv - 1 ; i >= 0 ; -- i) {
const ICurve* pCrvPrec = pMyOffs->GetCurve( i) ;
if ( pCrvPrec == nullptr || ! pCrvPrec->IsValid())
break ;
if ( pCrvPrec->GetTempProp( 1) != TEMP_PROP_SMOOTH) {
// definisco il versore uscente
pCrvPrec->GetEndDir( vt1) ;
// definisco il punto di uscita
pCrvPrec->GetEndPoint( pt1) ;
// definisco il parametro di uscita
dURef1 = i + 1 ;
break ;
}
}
if ( ! vt1.IsValid())
continue ;
vt2 = V_INVALID ;
for ( int i = nCrv + 1 ; i < pMyOffs->GetCurveCount() ; ++ i) {
const ICurve* pCrvSucc = pMyOffs->GetCurve( i) ;
if ( pCrvSucc == nullptr || ! pCrvSucc->IsValid())
break ;
if ( pCrvSucc->GetTempProp( 1) != TEMP_PROP_SMOOTH) {
// definisco il versore entrante
pCrvSucc->GetStartDir( vt2) ;
// definisco il punto di entrata
pCrvSucc->GetStartPoint( pt2) ;
// definisco il parametro di entrata
dURef2 = i ;
break ;
}
}
if ( ! vt2.IsValid())
continue ;
// trovo il punto di intersezione tra le due direzioni tangenti applicate
const double EXTENSION_LEN = 1000. ;
// segmento iniziale -------
PtrOwner<ICurveLine> pLineS( CreateCurveLine()) ;
if ( IsNull( pLineS) || ! pLineS->Set( pt1, pt1 + vt1 * EXTENSION_LEN))
continue ;
// segmento finale --------
PtrOwner<ICurveLine> pLineE( CreateCurveLine()) ;
if ( IsNull( pLineE) || ! pLineE->Set( pt2, pt2 - vt2 * EXTENSION_LEN))
continue ;
// intersezione
IntersCurveCurve ILL( *pLineS, *pLineE) ;
if ( ILL.GetCrossIntersCount() == 1) {
IntCrvCrvInfo aInfo ;
if ( ILL.GetIntCrvCrvInfo( 0, aInfo)) {
if ( ! pCrvCurl->AddPoint( pt1) ||
! pCrvCurl->AddLine( aInfo.IciA[0].ptI))
continue ;
ptMain = aInfo.IciA[0].ptI ;
}
}
else
continue ;
}
// se invece non è di raccordo, allora ho già lo spigolo vivo
else {
// definisco il versore entrante ed uscente
// definisco il punto di entrata e di uscita ( sono coincidenti)
// definisco il parametro di entrata e di uscita ( sono coincidenti)
dURef1 = dURef ;
dURef2 = dURef ;
if ( ! vOffs[nInd]->GetPointD1D2( dURef1, ICurve::FROM_MINUS, ptMain, &vt1) ||
! vOffs[nInd]->GetPointD1D2( dURef2, ICurve::FROM_PLUS, ptMain, &vt2))
continue ;
vt1.Normalize() ; vt2.Normalize() ;
}
// se i due vettori sono tra loro paralleli...
bool bSameDir = AreSameVectorEpsilon( vt1, vt2, 2 * EPS_SMALL) ;
if ( bSameDir) {
vt1 = ( ptCentroid + ( PockParams.dSideStep / 4 * vt1)) - ptCloser ;
vt2 = ( ptCentroid - ( PockParams.dSideStep / 4 * vt2)) - ptCloser ;
vt2.Invert() ;
}
// calcolo la direzione tra pt1 e ptCentroid
Vector3d vtDir = ( ptCentroid - ptMain) ; vtDir.Normalize() ;
vtDir.Rotate( Z_AX, ANG_RIGHT) ;
double dAngMain ; bool bDet ;
vt1.GetRotation( vt2, Z_AX, dAngMain, bDet) ;
if ( dAngMain > - EPS_ZERO)
vtDir.Invert() ;
// calcolo i due Biarchi di raccordo
PtrOwner<ICurveComposite> pCrvBiArc1( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pCrvBiArc2( CreateCurveComposite()) ;
if ( IsNull( pCrvBiArc1) || IsNull( pCrvBiArc2) ||
! CalcBoundedSmoothedLink( ptMain, vt1, ptCentroid, vtDir, 0.5, vOffsFirstCurve, PockParams, pCrvBiArc1) ||
! CalcBoundedSmoothedLink( ptCentroid, vtDir, ptMain, vt2, 0.5, vOffsFirstCurve, PockParams, pCrvBiArc2))
continue ;
// aggiorno la curva a ricciolo
if ( ! pCrvCurl->AddCurve( Release( pCrvBiArc1)) ||
! pCrvCurl->AddCurve( Release( pCrvBiArc2)) ||
! pCrvCurl->IsValid())
continue ;
// controllo se devo raccordarmi con Offset
if ( bIsSmoothCrv)
pCrvCurl->AddLine( pt2) ;
// controllo se tale curva effettivamente rimuove il Chunk nC-esimo
PtrOwner<ISurfFlatRegion> pSfrFatCurl( GetSurfFlatRegionFromFatCurve( pCrvCurl->Clone(), PockParams.dRad, false, false)) ;
if ( IsNull( pSfrFatCurl) || ! pSfrFatCurl->IsValid())
continue ;
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfrUncleared->CloneChunk( nC)) ;
if ( IsNull( pSfrFatCurl) || ! pSfrFatCurl->IsValid())
return false ;
pSfrChunk->Subtract( *pSfrFatCurl) ;
// se la curva a ricciolo non svuota il Chunk nC-esimo -> percorro il bordo di tale Chunk
if ( pSfrChunk->IsValid() || pSfrChunk->GetChunkCount() > 0) {
// so che il punto più vicino alla curva è ptCloser
// ricavo dunque il punto più vicino sul bordo del Chunk nC-esimo
PtrOwner<ICurveComposite> pCompoChunkBorder( ConvertCurveToComposite( pSfrUncleared->GetLoop( nC, 0))) ;
if ( IsNull( pCompoChunkBorder) || ! pCompoChunkBorder->IsValid())
return false ;
if ( PockParams.bInvert)
pCompoChunkBorder->Invert() ;
DistPointCurve distPtChunkBorder( ptCloser, *pCompoChunkBorder) ;
Point3d ptCloserOnChunk ;
if ( ! distPtChunkBorder.GetMinDistPoint( EPS_SMALL, ptCloserOnChunk, nFlag))
continue ;
// cambio il punto di inizio della curva su tale punto
double dUS = 0. ;
pCompoChunkBorder->GetParamAtPoint( ptCloserOnChunk, dUS) ;
pCompoChunkBorder->ChangeStartPoint( dUS) ;
/*
Creo due curve composite rappresentanti un piccolo tratto tangente sulla curva di
Offset attuale; questi servono per la partenza e l'arrivo dei biArchi
*/
PtrOwner<ICurveComposite> pCompoHelpS( CreateCurveComposite()) ;
if ( IsNull( pCompoHelpS) || ! pCompoHelpS->AddPoint( pt1 - vt1 / 4) ||
! vt1.Normalize() || ! pCompoHelpS->AddLine( pt1))
continue ;
PtrOwner<ICurveComposite> pCompoHelpE( CreateCurveComposite()) ;
if ( IsNull( pCompoHelpE) || ! pCompoHelpE->AddPoint( pt2) ||
! vt2.Normalize() || ! pCompoHelpE->AddLine( pt2 + vt2 / 4))
continue ;
/*
creo il Biarco iniziale
( nel caso non si riuscisse diventa un segmento lineare)
*/
PtrOwner<ICurveComposite> pCrvBiArc1( CreateCurveComposite()) ;
if ( IsNull( pCrvBiArc1))
return false ;
if ( ! CutCurveToConnect( pCompoHelpS, pCompoChunkBorder, vOffsFirstCurve, PockParams,
0, 10 * EPS_SMALL, pCrvBiArc1)) {
if ( ! CalcBoundedLink( ptCloser, ptCloserOnChunk, vOffsFirstCurve, pCrvBiArc1))
continue ;
}
// aggiorno i nuovi punti e i nuovi parametri
double dMyU ;
Point3d ptMyPoint ;
if ( ! pCrvBiArc1->GetEndPoint( ptMyPoint) ||
! pCompoChunkBorder->GetParamAtPoint( ptMyPoint, dMyU))
continue ;
// accorgio il bordo dell'Offset in raccordo al BiArco iniziale
if ( dMyU > EPS_SMALL)
pCompoChunkBorder->TrimStartAtParam( dMyU) ;
/*
creo il Biarco finale
( nel caso non si riuscisse diventa un segmento lineare)
*/
// creo il Biarco iniziale ( nel caso non si riuscisse diventa un segmento lineare)
PtrOwner<ICurveComposite> pCrvBiArc2( CreateCurveComposite()) ;
if ( IsNull( pCrvBiArc2))
return false ;
if ( ! CutCurveToConnect( pCompoChunkBorder, pCompoHelpE, vOffsFirstCurve, PockParams,
0., 0., pCrvBiArc2)) {
if ( ! CalcBoundedLink( ptCloserOnChunk, ptCloser, vOffsFirstCurve, pCrvBiArc2))
continue ;
}
// aggiorno i nuovi punti e i nuovi parametri
if ( ! pCrvBiArc2->GetStartPoint( ptMyPoint) ||
! pCompoChunkBorder->GetParamAtPoint( ptMyPoint, dMyU))
continue ;
// la curva a ricciolo ora diventa l'unione di pCrvBiArc1 - pCompoChunkBorder - pCrvBiArc2
pCrvCurl->Clear() ;
// smusso la curva di bordo del Chunk ( è aperta)
double dSmoothPar = PockParams.dRad / 8. ;
ModifyCurveToSmoothed( pCompoChunkBorder, PockParams, dSmoothPar, dSmoothPar, false) ;
if ( ! pCrvCurl->AddCurve( Release( pCrvBiArc1)) ||
! pCrvCurl->AddCurve( Release( pCompoChunkBorder)) ||
! pCrvCurl->AddCurve( Release( pCrvBiArc2)))
continue ;
}
// attribuisco alla curva a ricciolo la Feed minima
AssignMinFeed( pCrvCurl, PockParams) ;
// aggiorno il nuovo Offset aggiungendo la curva a ricciolo
PtrOwner<ICurveComposite> pCrvTest( CreateCurveComposite()) ;
if ( IsNull( pCrvTest))
return false ;
// aggiungo tratto iniziale di Offset
pCrvTest->AddCurve( ConvertCurveToBasicComposite( pMyOffs->CopyParamRange( 0, dURef1))) ;
// aggiungo la curva a ricciolo
if ( ! pCrvTest->AddCurve( Release( pCrvCurl)))
continue ;
// aggiungo il tratto finale di Offset
pCrvTest->AddCurve( ConvertCurveToBasicComposite( pMyOffs->CopyParamRange( dURef2, pMyOffs->GetCurveCount()))) ;
// nel caso di direzioni parallele manca uno smusso
if ( bSameDir) {
double dSmoothPar = PockParams.dRad / 8. ;
ModifyCurveToSmoothed( pCrvTest, PockParams, dSmoothPar, dSmoothPar, false) ;
}
// provo a controllare che i punti inzili e finali coincidano, in caso aggiorno il nuovo Offset
Point3d ptStart_Old ; vOffs[nInd]->GetStartPoint( ptStart_Old) ;
Point3d ptEnd_Old ; vOffs[nInd]->GetEndPoint( ptEnd_Old) ;
Point3d ptStart_New ; pCrvTest->GetStartPoint( ptStart_New) ;
Point3d ptEnd_New ; pCrvTest->GetEndPoint( ptEnd_New) ;
if ( AreSamePointApprox( ptStart_Old, ptStart_New) && AreSamePointApprox( ptEnd_Old, ptEnd_New)) {
pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 0), 0) ;
pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 1), 1) ;
vOffs[nInd].Set( pCrvTest) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, const PocketParams& PockParams,
const ICRVCOMPOPOVECTOR& vOffsFirstCurve)
{
// controllo dei parametri
if ( vOffs.empty() || vOffsFirstCurve.empty())
return false ;
vLinks.clear() ;
vLinks.resize( int( vOffs.size())) ;
// scorro tutte le curva di Offset
for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) {
// ricavo il punto inziale della curva corrente
Point3d ptS ;
if ( ! vOffs[i]->GetStartPoint( ptS))
return false ;
int nNextInd = -1 ; // indice della curva successiva
double dMinDist = INFINITO ; // distanza tra questa curva e la successiva
Point3d ptStartNext ; // punto iniziale della curva successiva
// tra le curva successive cerco la curva interna e più vicina ad essa
for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) {
IntersCurveCurve IntCC( *vOffs[i], *vOffs[j]) ;
CRVCVECTOR ccClass ;
// se interna
if ( IntCC.GetCurveClassification( 1, EPS_SMALL, ccClass) &&
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) {
// calcolo la distanza minima tra essa
int nFlag ;
Point3d ptClosest ;
if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) {
double dCurrDist = SqDist( ptS, ptClosest) ;
if ( dCurrDist < dMinDist) {
dMinDist = dCurrDist ;
nNextInd = j ;
ptStartNext = ptClosest ;
}
}
}
}
// se non ho trovato nessuna curva interna... cerco semplicemente la curva più vicina
if ( nNextInd == -1) {
for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) {
int nFlag ;
Point3d ptClosest ;
if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) {
double dCurrDist = SqDist( ptS, ptClosest) ;
if ( dCurrDist < dMinDist) {
dMinDist = dCurrDist ;
nNextInd = j ;
ptStartNext = ptClosest ;
}
}
}
}
// se non ho trovato nessuna curva, errore
if ( nNextInd == -1)
return false ;
// scambio la curva i+1 esima con la j-esima ( se non sono già in ordine)
if ( nNextInd != i + 1)
swap( vOffs[nNextInd], vOffs[i+1]) ;
// cambio il suo punto iniziale nel punto più vicino tovato
double dUS ;
if ( ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUS, EPS_SMALL))
return false ;
vOffs[i+1]->ChangeStartPoint( dUS) ;
// accorcio la curva per raccordarmi in tangenza con la successiva
if ( int( vOffs.size()) > 1) {
// clono le curve i ed i+1 esime ( nel caso non riuscissi ad accorciarle o raccordarle )
PtrOwner<ICurveComposite> pCrv_i( vOffs[i]->Clone()) ;
PtrOwner<ICurveComposite> pCrv_ii( vOffs[i+1]->Clone()) ;
if ( IsNull( pCrv_i) || IsNull( pCrv_ii) || ! pCrv_i->IsValid() || ! pCrv_ii->IsValid())
return false ;
// creo la curva che le collegherà
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
if ( IsNull( pCrvLink))
return false ;
// NB. Le curve di Offset da tagliare non devono essere quelle di primo Offset
if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsFirstCurve, PockParams,
( i == 0 ? 0. : 10 * EPS_SMALL), 10 * EPS_SMALL, pCrvLink) ||
! pCrvLink->IsValid()) {
// se non sono riuscito, cerco una strada più semplice
// ripristino le curve
pCrvLink->Clear() ;
vOffs[i].Set( pCrv_i) ; // chiuso
vOffs[i+1].Set( pCrv_ii) ; // chiuso
// recupero i vettori tangente iniziali ( le curve sono chiuse )
Vector3d vtS, vtE ;
if ( ! vOffs[i]->GetStartDir( vtS) || ! vOffs[i+1]->GetStartDir( vtE))
return false ;
// creo il bi-arco tra esse
if ( CalcBoundedSmoothedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink))
vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento
else
return false ;
continue ; // passo alla curva i+1 esima successiva
}
// per sicurezza aggiorno i nuovi punti e i nuovi parametri
double dUNewS, dUNewE ;
if ( ! pCrvLink->GetStartPoint( ptS) ||
! pCrvLink->GetEndPoint( ptStartNext) ||
! vOffs[i]->GetParamAtPoint( ptS, dUNewS) ||
! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUNewE))
return false ;
// imposto il punto iniziale della curva successiva ( i+1 esima)
vOffs[i+1]->ChangeStartPoint( dUNewE) ;
PtrOwner<ICurveComposite> pCrvNewOffs( CloneCurveComposite( vOffs[i])) ;
if ( dUNewS > EPS_SMALL) {
pCrvNewOffs.Set( ConvertCurveToComposite( vOffs[i]->CopyParamRange( 0, dUNewS))) ;
// sostituisco la curva i esima con quella tagliata
vOffs[i].Set( pCrvNewOffs) ;
}
// aggiorno il collegamento
vLinks[i+1].Set( pCrvLink) ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs,
const int nIter, const PocketParams& PockParams, bool& bInsert)
{
// controllo dei parametri
if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0)
return false ;
bInsert = true ; // di base inserisco
// controllo se richiesta ottimizzazione sul numero di Offsets
if ( ! PockParams.bOptOffsets)
return true ;
if ( nIter != 0) { // controllo se sono almeno al secondo offset...
// per ogni curva controllo se il suo Offset massimo non genera alcuna regione
double dMaxOffs ;
if ( ! CalcCurveLimitOffset( *pCrvOffs, dMaxOffs))
return false ;
// se questa curva sparisce -> non serve inserirla come offset, non svuota parti aggiuntive
if ( dMaxOffs < PockParams.dRad - dOffs + 5 * EPS_SMALL) {
bInsert = false ;
return true ;
}
}
else
return true ;
// se non richiesto percorso avanzato
if ( ! PockParams.bOptOffsetsAdv)
return true ;
OffsetCurve OffsCrv ; // considero anche i casi in cui ho bSmallRad
if ( ! OffsCrv.Make( pCrvOffs, - PockParams.dRad + dOffs - 5 * EPS_SMALL , ICurve::OFF_FILLET))
return false ;
// controllo se richista ottimizzazione per Offsets mediante centroidi e medial Axis
if ( OffsCrv.GetCurveCount() > 1)
return true ;
PtrOwner<ICurve> pCrvOffLonger( OffsCrv.GetLongerCurve()) ;
if ( IsNull( pCrvOffLonger) || ! pCrvOffLonger->IsValid())
return true ;
// Immagine del tool con centro nel centroide della zona da svuotare
Point3d ptC ; pCrvOffLonger->GetCentroid( ptC) ;
PtrOwner<ICurveArc> pCrvTool( CreateCurveArc()) ;
if( IsNull( pCrvTool))
return false ;
pCrvTool->SetXY( ptC, PockParams.dRad - 5 * EPS_SMALL) ;
CRVCVECTOR ccClass ;
IntersCurveCurve intCC( *pCrvOffLonger, *pCrvTool) ;
intCC.GetCurveClassification( 1, EPS_SMALL, ccClass) ;
if ( int(ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) {
// centroide
bInsert = false ;
return true ;
}
// recupero la PolyLine per altezza e lunghezza del Box2D ( rettangolo)
PolyLine pl ;
if ( ! pCrvOffLonger->ApproxWithLines( 100 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, pl))
return true ;
Vector3d vtX ;
double dLen = 0 ; double dHeight = 0 ;
if ( ! pl.GetMinAreaRectangleXY( ptC, vtX, dLen, dHeight))
return true ;
// frame locale, recupero DimX e DimY
Frame3d frLoc ; frLoc.Set( ptC, Z_AX, vtX) ;
if ( ! frLoc.IsValid())
return true ;
BBox3d bBox ;
frLoc.Invert() ;
pCrvOffLonger->GetBBox( frLoc, bBox) ;
double dDimX = bBox.GetDimX() ;
double dDimY = bBox.GetDimY() ;
// se dimensioni accettabili, inserisco
if ( dDimX < 2 * PockParams.dRad - 100 * EPS_SMALL ||
dDimY < 2 * 2 * PockParams.dRad - 100 * EPS_SMALL) {
bInsert = false ;
return true ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
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 bool
CalcSpiral( const ISurfFlatRegion* pSrfPock, const PocketParams& PockParams, int& nReg, Point3d& ptStart,
ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops, ICurveComposite* pMCrv, bool& bOptimizedTrap,
bool& bMidOut, Vector3d& vtMidOut)
{
// inizializzo il percorso come vuoto
pMCrv->Clear() ;
// Offset corrente ( il primo è definito dal raggio utensile)
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
// recupero il versore normale della superficie, ruotandola nel piano XY
PtrOwner<ISurfFlatRegion> pSrfToWork( pSrfPock->Clone()) ;
if ( IsNull( pSrfToWork) || pSrfToWork->GetChunkCount() == 0)
return false;
// ciclo di offset verso l'interno
const int MAX_ITER = 1000 ;
int nIter = 0 ;
ICRVCOMPOPOVECTOR vOffs ; // vettore delle curve di offset
ICRVCOMPOPOVECTOR vOffsFirstCurve ; // curve di primo offset
PtrOwner<ISurfFlatRegion> pSrfAct( CloneSurfFlatRegion( pSrfToWork)) ; // regione attuale
if ( IsNull( pSrfAct) || pSrfAct->GetChunkCount() == 0)
return false ;
double dOffsPrec = 0. ;
while ( nIter < MAX_ITER) {
// ricavo la regione piana da VRONI
PtrOwner<ISurfFlatRegion> pSfrOffsVR( pSrfAct->CreateOffsetSurf( - dOffs, ICurve::OFF_FILLET)) ;
if ( IsNull( pSfrOffsVR))
return false ;
if ( ! pSfrOffsVR->IsValid()) {
pSfrOffsVR.Set( pSrfAct->CreateOffsetSurf( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) ;
if ( IsNull( pSfrOffsVR))
return false ;
}
// se primo Offset
if ( nIter == 0) {
int my_nReg = nReg ;
nReg = pSfrOffsVR->GetChunkCount() ; // aggiorno il nuovo valore delle regioni totali di primo Offset
// gli Offset progressivi appartengono al Chunk nReg-esimo
pSrfAct.Set( pSfrOffsVR->CloneChunk( my_nReg)) ;
if ( IsNull( pSrfAct)) // se supero i chunk ottenuti
return true ;
pSfrOffsVR.Set( pSrfAct->Clone()) ;
}
// numero di Chunk attuali ( alla prima iterazione è l'nReg-esimo)
int nChunks = pSfrOffsVR->GetChunkCount() ;
for ( int i = 0 ; i < nChunks ; ++ i) {
// per ogni chunk...
int nLoops = pSfrOffsVR->GetLoopCount( i) ;
for ( int j = 0 ; j < nLoops ; ++ j) {
// per ogni loop...
PtrOwner<ICurveComposite> pCrvCompoBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ;
if ( IsNull( pCrvCompoBorder) || ! pCrvCompoBorder->IsValid())
return false ;
/*
Il materiale deve sempre trovarsi alla destra ( se bInvert -> alla sinistra) del tool
soltanto sul primo bordo; Nel caso di isole aperte o nelle passate di Offset intermedio
l'orientamente non ha importanza.
*/
if ( nIter != 0 && j > 0) // inverto l'orientamento delle curve interne ( offset delle isole trovate)
pCrvCompoBorder->Invert() ;
// controllo quali regioni di Offset possono essere sostituite
bool bInsert = true ;
if ( ! CheckIfOffsetIsNecessary( pCrvCompoBorder, dOffs - dOffsPrec, nIter, PockParams, bInsert))
return false ;
if ( bInsert)
vOffs.emplace_back( Release( pCrvCompoBorder)) ;
if ( nIter == 0) { // salvo il bordo per i link ( non invertiti, devo sapere IN/OUT)
PtrOwner<ICurveComposite> pCrvCompoExtBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ;
vOffsFirstCurve.emplace_back( Release( pCrvCompoExtBorder)) ;
}
}
}
// controllo se serve un raggio più piccolo di svuotatura
bool bSmallRad = ( nIter == 0 ? dOffs < PockParams.dRad + EPS_ZERO : dOffs - dOffsPrec < PockParams.dRad + EPS_ZERO) ;
// se ho trovato dei chunk, allora aggiorno l'Offset per la passata successiva
if ( nChunks > 0) {
// memorizzo il valore di Offset per l'iterazione successiva
if ( nIter != 0) {
dOffsPrec = dOffs ;
dOffs += PockParams.dSideStep ;
}
else {
dOffsPrec = 0. ;
dOffs = PockParams.dSideStep ;
}
}
// se devo usare un Offset più piccolo...
else if ( ! bSmallRad)
dOffs = dOffsPrec + ( nIter == 0 ? PockParams.dRad + PockParams.dRadialOffset : PockParams.dRad) ;
// altrimenti ho finito la regione da svuotare...
else
break ;
++ nIter ; // aggiorno iterazione
}
// se non ho trovato curve di Offset allora esco
if ( vOffs.empty())
return true ;
// cambio il punto iniziale della prima Curva di Offset
Point3d ptRef = PockParams.ptStart ;
Point3d ptNewStart ;
// Se ho come lavorazione uno SpiralIn posso poter entrare dalle isole aperte...
int nIndexSwap = 0 ; // indice del vettore di curve per identificare la curva su cui entrare
if ( PockParams.nType == POCKET_SPIRALIN) {
if ( SetAdvancedPtStartForPath( vOffsFirstCurve, PockParams, pSrfToWork, ptRef, ptStart,
vtMidOut, bMidOut, nIndexSwap, vCrvOrigChunkLoops))
vOffsFirstCurve[nIndexSwap]->GetStartPoint( ptNewStart) ;
else
return false ;
}
else {
if ( SetPtStartForPath( vOffs[0], PockParams, pSrfToWork, ptRef, ptStart, vtMidOut, bMidOut,
vCrvOrigChunkLoops[0]))
vOffs[0]->GetStartPoint( ptNewStart) ;
else
return false ;
}
// se richiesta inversione
for ( int i = 0 ; i < int( vOffs.size()) && PockParams.bInvert ; ++ i)
vOffs[i]->Invert() ;
// smusso le curve di offset ( ad eccezione della prima; le isole sono automaticamente smussate)
ICRVCOMPOPOVECTOR vOffsClosedCurves( vOffs.size()) ; // vettore con tutte le curve di Offset Chiuse
double dSmoothPar = PockParams.dRad / 8 ;
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
if ( i != 0)
ModifyCurveToSmoothed( vOffs[i], PockParams, dSmoothPar, dSmoothPar, false) ;
vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
vOffsClosedCurves[i].Set( vOffs[i]->Clone()) ;
}
// setto il punto iniziale della svuotatura
double dNewUS ;
if ( nIndexSwap != 0)
swap( vOffs[0], vOffs[nIndexSwap]) ; // se entro da un'isola aperta
vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ;
vOffs[0]->ChangeStartPoint( dNewUS) ;
vOffs[0]->SetTempParam( 0., 0) ; // prima iterazione
// riordino le curve e creo i collegamenti
/*
Se richiesto parametro di smusso, tutti gli Offset ( ad eccezione di quelli generati alla
prima iterazione vengono tagliati, quindi diventano aperti
*/
ICURVEPOVECTOR vLinks( vOffs.size()) ;
if ( ! CreateSpiralPocketingPath( vOffs, vLinks, PockParams, vOffsFirstCurve))
return false ;
// controllo eventuali parti non svuotate ( e setto la Feed degli Offset e dei Link)...
PtrOwner<ISurfFlatRegion> pSfrUncleared( CreateSurfFlatRegion()) ;
if ( GetUnclearedRegionAndSetFeed( vOffsFirstCurve, vOffs, vLinks, vCrvOrigChunkLoops[0], PockParams, pSfrUncleared)) {
// Modifico i percorsi
if ( ! RemoveExtraParts( pSfrUncleared, vOffs, vOffsFirstCurve, PockParams))
return false ;
}
// creo il percorso di lavoro a partire dalla raccolta degli offset e dei collegamenti
for ( int i = 0 ; i < int( vOffs.size()) ; ++i) {
// se collegamento da aggiungere
if ( ! IsNull( vLinks[i])) {
// accodo nel percorso di lavorazione
if ( ! pMCrv->AddCurve( Release( vLinks[i]))) {
return false ;
}
}
pMCrv->AddCurve( Release( vOffs[i])) ;
}
// semplifico la curva ottenuta
SimplifyCurveByFeeds( pMCrv, PockParams, 10 * EPS_SMALL) ;
// verifico il percorso di lavoro
if ( pMCrv->GetCurveCount() == 0)
return false ;
// reset delle proprietà temporanee delle curve componenti
for ( int i = 0 ; i < pMCrv->GetCurveCount() ; ++ i) {
pMCrv->SetCurveTempProp( i, 0, 0) ;
pMCrv->SetCurveTempProp( i, 0, 1) ;
}
// setto estrusione
pMCrv->SetExtrusion( Z_AX) ;
return true ;
}
//----------------------------------------------------------
static bool
CalcZigZagLink( ICurveComposite* pCrv1, ICurveComposite* pCrv2, const PocketParams& PockParams,
ICurveComposite* pCrvLink)
{
// se non richiesto, esco
if ( ! PockParams.bSmooth)
return true ;
// controllo dei parametri
if ( pCrv1 == nullptr || pCrv2 == nullptr)
return false ;
pCrvLink->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
// prendo i parametri per il raccordo
Vector3d vtS, vtE, vtH ;
pCrv1->GetEndDir( vtS) ;
pCrv2->GetStartDir( vtE) ;
Point3d ptS, ptE, ptE1, ptS2 ;
pCrv1->GetEndPoint( ptS) ;
pCrv2->GetStartPoint( ptE) ;
vtH = ptE - ptS ;
double dLen1, dLen2 ;
pCrv1->GetLength( dLen1) ;
pCrv2->GetLength( dLen2) ;
double dUS1, dUE1, dUS2, dUE2 ;
pCrv1->GetDomain( dUS1, dUE1) ;
pCrv2->GetDomain( dUS2, dUE2) ;
double dAngle, dAngleH ;
double dDist = Dist( ptS, ptE) ;
// curve per controllo validità del collegamento
PtrOwner<ICurveComposite> pCrvH1( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pCrvH2( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pCrvTempLink( CreateCurveComposite()) ;
PtrOwner<ICurveComposite> pCrvTest( CreateCurveComposite()) ;
if ( IsNull( pCrvH1) || IsNull( pCrvH2) || IsNull( pCrvTempLink) || IsNull( pCrvTest))
return false ;
if ( vtS.GetAngle( vtE, dAngle) &&
abs( dAngle) < ANG_STRAIGHT + 5 * EPS_SMALL && abs( dAngle) > ANG_STRAIGHT - 5 * EPS_SMALL &&
vtH.GetAngle( vtE, dAngleH) &&
abs( dAngleH) < ANG_RIGHT + 5 * EPS_SMALL && abs( dAngleH) > ANG_RIGHT - 5 * EPS_SMALL &&
dLen1 > dDist && dLen2 > dDist &&
pCrvLink->GetCurveCount() == 1 && pCrvLink->GetFirstCurve()->GetType() == CRV_LINE) {
// creo una semicirconferenza
double dRad = dDist / 2 ; // raggio
Point3d ptNS, ptNE ;
pCrv1->GetParamAtLength( dLen1 - dRad, dUE1) ;
pCrv2->GetParamAtLength( dRad, dUS2) ;
pCrv1->GetPointD1D2( dUE1, ICurve::FROM_MINUS, ptNS) ; // parametro di intersezione su curva 1
pCrv2->GetPointD1D2( dUS2, ICurve::FROM_PLUS, ptNE) ; // parametro di intersezione su curva 2
PtrOwner<ICurveArc> pSemiCir( CreateCurveArc()) ;
if ( pSemiCir->Set2PVN( ptNS, ptNE, vtS, Z_AX)) {
pCrvH1.Set( GetCurveComposite( pCrv1->CopyParamRange( dUS1, dUE1))) ;
pCrvH2.Set( GetCurveComposite( pCrv2->CopyParamRange( dUS2, dUE2))) ;
pCrvTempLink->AddCurve( pSemiCir->Clone()) ;
}
// controllo finale sui raccordi ammissibili ...
if ( pCrvTest->AddCurve( pCrvH1->Clone()) && pCrvTest->AddCurve( pCrvTempLink->Clone()) &&
pCrvTest->AddCurve( pCrvH2->Clone())) {
if ( ! pCrv1->TrimEndAtParam( dUE1))
pCrv1->Clear() ;
pCrvLink->Clear() ;
pCrvLink->AddCurve( Release( pCrvTempLink)) ;
if ( ! pCrv2->TrimStartAtParam( dUS2))
pCrv2->Clear() ;
return true ;
}
}
else {
// devo smussare la curva formata da pCrv1 - pCrvLink ( da creare) - pCrv2
pCrvH1->AddCurve( pCrv1->Clone()) ;
pCrvH2->AddCurve( pCrv2->Clone()) ;
pCrvTempLink->AddCurve( pCrvLink->Clone()) ;
// prendo le lunghezze necessarie
double dLenSeg1 = EPS_SMALL ; double dLenSeg2 = EPS_SMALL ;
double dLenI_s = EPS_SMALL ; // lunghezza prima curva del Link
double dLenI_e = EPS_SMALL ; // lunghezza ultima curva del Link
pCrvH1->GetLength( dLenSeg1) ;
pCrvH2->GetLength( dLenSeg2) ;
pCrvTempLink->GetFirstCurve()->GetLength( dLenI_s) ;
pCrvTempLink->GetLastCurve()->GetLength( dLenI_e) ;
// raccordo pSeg1 con pCrvLink
double dTollLeft = 1 - ( dLen1 - ( ( 2 * PockParams.dRad) / 16)) / dLen1 ; // % parametro sinistro per smusso
double dTollRight = ( ( 2 * PockParams.dRad) / 16) / dLenI_s ; // % parametro destro di smusso
PtrOwner<ICurveComposite> pCrv_Seg1_Isl( CreateCurveComposite()) ; // curva unione di pSeg1 e pCrvLink smussata
pCrv_Seg1_Isl->AddCurve( pCrvH1->Clone()) ;
pCrv_Seg1_Isl->AddCurve( pCrvTempLink->Clone()) ;
ModifyCurveToSmoothed( pCrv_Seg1_Isl, PockParams, dTollLeft, dTollRight, true) ;
// raccordo questa curva con pSeg2
double dUS_1IH, dUE_1IH, dToTLen ;
pCrv_Seg1_Isl->GetDomain( dUS_1IH, dUE_1IH) ; // dominio della curva smussata tra pSeg1 e PCrvLink
pCrv_Seg1_Isl->GetLength( dToTLen) ; // nuova lunghezza della curva smussata tra pSeg1 e pCrvLink
dTollLeft = 1 - ( dLenI_e - ( ( 2 * PockParams.dRad) / 16)) / dLenI_e ; // % paramtro sinistro per smusso
dTollRight = ( ( 2 * PockParams.dRad) / 16) / dLen2 ; // % parametro destro per smusso
PtrOwner<ICurveComposite> pCrv_smoothed( CreateCurveComposite()) ; // curva unione del primo smusso con pSeg2
pCrv_smoothed->AddCurve( pCrv_Seg1_Isl->Clone()) ;
pCrv_smoothed->AddCurve( pCrvH2->Clone()) ;
ModifyCurveToSmoothed( pCrv_smoothed, PockParams, dTollLeft, dTollRight, true) ;
// recupero i parametri del nuovo collegamento che si è creato dallo smusso
pCrvH1->Clear() ;
pCrvTempLink->Clear() ;
pCrvH2->Clear() ;
int nStat = -1 ;
for ( int u = 0 ; u < pCrv_smoothed->GetCurveCount() ; ++ u) {
// recupero la curva u-esima
const ICurve* pCrv = pCrv_smoothed->GetCurve( u) ;
if ( pCrv->GetType() == CRV_LINE) {
if ( CheckSimpleOverlap( pCrv, pCrv1, nStat, EPS_SMALL) && nStat == 1)
pCrvH1->AddCurve( pCrv->Clone()) ;
else if ( CheckSimpleOverlap( pCrv, pCrv2, nStat, EPS_SMALL) && nStat == 1)
pCrvH2->AddCurve( pCrv->Clone()) ;
}
}
Point3d ptCrv1_e, ptCrv2_s ;
double dULink_s = EPS_SMALL ;
double dULink_e = EPS_SMALL ;
pCrvH1->GetEndPoint( ptCrv1_e) ;
pCrv1->GetParamAtPoint( ptCrv1_e, dULink_s) ;
if ( ! pCrv1->TrimEndAtParam( dULink_s))
pCrv1->Clear() ;
pCrvH2->GetStartPoint( ptCrv2_s) ;
pCrv2->GetParamAtPoint( ptCrv2_s, dULink_e) ;
if ( ! pCrv2->TrimStartAtParam( dULink_e))
pCrv2->Clear() ;
pCrv_smoothed->GetParamAtPoint( ptCrv1_e, dULink_s) ;
pCrv_smoothed->GetParamAtPoint( ptCrv2_s, dULink_e) ;
pCrvLink->Clear() ;
pCrvLink->AddCurve( GetCurveComposite( pCrv_smoothed->CopyParamRange( dULink_s, dULink_e))) ;
return true ;
}
return false ;
}
//----------------------------------------------------------------------------
static bool
CalcZigZag( const ISurfFlatRegion* pSrfZigZag, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vpCrvs,
bool bFromInfill)
{
// check parametri
if ( pSrfZigZag == nullptr || ! pSrfZigZag->IsValid())
return true ;
// creo il vettore dei contorni
ICRVCOMPOPOVECTOR vFirstOff ;
for ( int c = 0 ; c < pSrfZigZag->GetChunkCount() ; ++ c)
for ( int l = 0 ; l < pSrfZigZag->GetLoopCount( c) ; ++ l)
vFirstOff.emplace_back( GetCurveComposite( pSrfZigZag->GetLoop( c, l))) ;
// ingombro del contorno offsettato
BBox3d b3Pocket ;
vFirstOff[0]->GetLocalBBox( b3Pocket) ;
Point3d ptMin ; double dDimX, dDimY, dDimZ ;
b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ;
// lunghezza del contorno offsettato
double dLen ; vFirstOff[0]->GetLength( dLen) ;
// passi in Y
int nYStep ;
double dYStep ;
if ( ! bFromInfill) {
nYStep = static_cast< int >( ceil(( dDimY - 30 * EPS_SMALL) / PockParams.dSideStep)) ;
dYStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ;
}
else {
nYStep = static_cast< int >( floor(( dDimY + 30 * EPS_SMALL) / PockParams.dSideStep)) ;
dYStep = PockParams.dSideStep ;
}
int nRef = (( nYStep + ( PockParams.bInvert ? 0 : 1)) % 2) ;
// tratto valido
struct Section {
bool bActive ;
Point3d ptS ;
Point3d ptE ;
double dOs ;
double dOe ;
int nOffIndS ;
int nOffIndE ;
} ;
struct ParIsland {
double dU ;
int nInd ;
} ;
// raccolta di tratti
typedef vector<vector<Section>> VECVECSECT ; VECVECSECT vvSec ;
vvSec.resize( nYStep + 1) ;
// calcolo le linee di svuotatura
int nCount = 0 ;
for ( int i = 0 ; i <= nYStep ; ++ i) {
// determino senso
bool bPlus = (( i % 2) == nRef) ;
// definisco la linea
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
const double EXP_LEN = 1.0 ;
Point3d ptStart ;
if ( ! bFromInfill)
ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + 10 * EPS_SMALL + i * dYStep, ptMin.z + dDimZ) ;
else {
double dShift = ( i == 0 ? 10 * EPS_SMALL : ( i == nYStep ? - 10 * EPS_SMALL : 0.)) ;
ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + dShift + i * dYStep, ptMin.z + dDimZ) ;
}
if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN))
return false ;
if ( ! bPlus)
pLine->Invert() ;
// calcolo la classificazione della linea rispetto alla superficie
CRVCVECTOR ccClass ;
pSrfZigZag->GetCurveClassification( *pLine, EPS_SMALL, ccClass) ;
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
if ( ccClass[j].nClass == CRVC_IN) { // memorizzo il segmento
Section currSec ;
currSec.bActive = true ;
PtrOwner<ICurveLine> pSeg( GetCurveLine( pLine->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
if ( IsNull( pSeg))
continue ;
Point3d ptS, ptE ;
pSeg->GetStartPoint( ptS) ; pSeg->GetEndPoint( ptE) ;
currSec.ptS = ptS ; currSec.ptE = ptE ;
for ( int k = 0 ; k < int( vFirstOff.size()) ; ++ k) {
if ( vFirstOff[k]->IsPointOn( ptS)) {
currSec.nOffIndS = k ;
double dUOS; vFirstOff[k]->GetParamAtPoint( ptS, dUOS) ;
currSec.dOs = dUOS ;
}
if ( vFirstOff[k]->IsPointOn( ptE)){
currSec.nOffIndE = k ;
double dUOE; vFirstOff[k]->GetParamAtPoint( ptE, dUOE) ;
currSec.dOe = dUOE ;
}
}
vvSec[i].emplace_back( currSec) ;
++ nCount ; // numero di segmenti inseriti
}
}
}
int nStatus = 0 ; // 0 -> inizio oppure ho inserito una parte di isola | 2 -> sto inserendo un segmento
Point3d ptS_ref ;
PtrOwner<ICurveComposite> pLastSeg( CreateCurveComposite()) ; // ultimo segmento nel percorso
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ; // ultimo link aggiunto
// vettore dei link aggiunti ( per Feed )
ICRVCOMPOPOVECTOR vAddedLinks ;
bool bFirstLine = true ; // flag per prima linea di ogni percorso
// creo i percorsi di svuotatura
vpCrvs.reserve( nCount) ;
int nI = -1, nJ = -1 ;
while ( true) {
// se sezione non valida
if ( nI < 0 || nJ < 0) {
// ricerco la prima valida ( il primo segmento con bActive messo a true)
for ( int k = 0 ; k < int( vvSec.size()) && nI < 0 ; ++ k) {
for ( int l = 0 ; l < int( vvSec[k].size()) && nJ < 0 ; ++ l) {
if ( vvSec[k][l].bActive) {
nI = k ;
nJ = l ;
}
}
}
// se trovata, creo nuova curva composita
if ( nI >= 0 && nJ >= 0) {
// creo la curva
vpCrvs.emplace_back( CreateCurveComposite()) ;
nStatus = 0 ;
// aggiungo punto iniziale
vpCrvs.back()->AddPoint( vvSec[nI][nJ].ptS) ;
bFirstLine = true ;
}
// altrimenti, esco
else
break ;
}
// determino senso
bool bPlus = (( nI % 2) == nRef) ;
// aggiungo la sezione alla curva
Section& Sec = vvSec[nI][nJ] ;
Sec.bActive = false ;
// creo i vettori con le linee Under e Above nella struttura della Section
ICURVEPOVECTOR vLineAbove ;
if ( nI < nYStep) {
for ( int a = 0 ; a < ( int)vvSec[nI + 1].size() ; ++ a) {
if ( vvSec[nI + 1][a].bActive)
continue ;
PtrOwner<ICurveLine> pLA( CreateCurveLine()) ;
pLA->Set( vvSec[nI + 1][a].ptS, vvSec[nI + 1][a].ptE) ;
vLineAbove.emplace_back( Release( pLA)) ;
}
}
ICURVEPOVECTOR vLineUnder ;
if ( nI > 0) {
for ( int u = 0 ; u < int( vvSec[nI-1].size()) ; ++ u) {
if ( vvSec[nI-1][u].bActive)
continue ;
PtrOwner<ICurveLine> pLU( CreateCurveLine()) ;
pLU->Set( vvSec[nI-1][u].ptS, vvSec[nI-1][u].ptE) ;
vLineUnder.emplace_back( Release( pLU)) ;
}
}
// creo la linea come curva composita, aggiungerò dei Joint per ogni tratto con Feed differente
PtrOwner<ICurveComposite> pLineCompo( CreateCurveComposite()) ;
if ( IsNull( pLineCompo))
return false ;
Point3d ptE_l ;
if ( bFirstLine)
ptE_l = vvSec[nI][nJ].ptS ;
else
vpCrvs.back()->GetEndPoint( ptE_l) ;
pLineCompo->AddPoint( ptE_l) ;
pLineCompo->AddLine( vvSec[nI][nJ].ptE) ;
// assegno la feed al tratto lineare
if ( ! AssignFeedZigZagOneWay( pLineCompo, false, vLineAbove, vLineUnder, vAddedLinks, PockParams)) // Assegno la Feed
return false ;
bFirstLine = false ;
vpCrvs.back()->AddCurve( pLineCompo->Clone()) ; // aggiungo la curva al percorso
if ( nStatus == 0) { // primo segmento per il raccordo smussato
pLastSeg.Set( pLineCompo) ;
pLastSeg->GetStartPoint( ptS_ref) ;
}
if ( nStatus == 2) { // ho precedentemente aggiunto il bordo di un'isola, quindi mi salvo il secondo segmento
PtrOwner<ICurveComposite> pSeg1( pLastSeg->Clone()) ;
PtrOwner<ICurveComposite> pSeg2( pLineCompo->Clone()) ;
if ( IsNull( pSeg1) || IsNull( pSeg2))
return false ;
// aggiorno il link
if ( ! CalcZigZagLink( pSeg1, pSeg2, PockParams, pCrvLink))
return false ;
// aggiorno il vettore delle curve ZigZag
Point3d ptE_Seg1 ;
pSeg1->GetEndPoint( ptE_Seg1) ;
double dUS_Link = EPS_SMALL ;
vpCrvs.back()->GetParamAtPoint( ptE_Seg1, dUS_Link) ;
if ( ! vpCrvs.back()->TrimEndAtParam( dUS_Link))
vpCrvs.back()->Clear() ;
if ( bFromInfill)
for ( int u = 0 ; u < pCrvLink->GetCurveCount() ; ++ u)
pCrvLink->SetCurveTempProp( u, -1, 1) ;
if ( ! AssignFeedZigZagOneWay( pCrvLink, true, vLineAbove, vLineUnder, vAddedLinks, PockParams))
return false ;
vpCrvs.back()->AddCurve( pCrvLink->Clone()) ; // aggiungo il Link
vpCrvs.back()->AddCurve( pSeg2->Clone()) ; // aggiungo pSeg2
// aggiorno il vettore dei Link ( nel caso sia un link tra due segmenti sullo stesso livello )
Point3d ptS1 ;
pSeg1->GetStartPoint( ptS1) ;
Point3d ptS2 ;
pSeg2->GetStartPoint( ptS2) ;
if ( abs( ptS1.y - ptS2.y) < 50 * EPS_SMALL)
vAddedLinks.emplace_back( pCrvLink->Clone()) ;
// pSeg2 ora diventa pSeg1 e il procedimento si itera per tutto il percorso
pLastSeg.Set( pSeg2) ;
}
// cerco nella stessa fila o in quella successiva sezione successiva raccordabile tramite il contorno
double dUstart = Sec.dOe ;
int nIndexIslandE = Sec.nOffIndE ; // isola di arrivo
double dUmin, dUmax ;
vFirstOff[nIndexIslandE]->GetDomain( dUmin, dUmax) ;
double dUspan = dUmax - dUmin ;
double dUref = ( bPlus ? INFINITO : -INFINITO) ;
int nNextI = -1 ;
int nNextJ = -1 ;
int li = nJ + 1 ;
for ( int k = nI ; k <= nI + 1 && k < int( vvSec.size()) ; ++ k) {
for ( int l = li ; l < int( vvSec[k].size()) ; ++ l) {
if ( ! vvSec[k][l].bActive) // se sezione non attiva... non la considero
continue ;
if ( vvSec[k][l].nOffIndS != nIndexIslandE) // se isola diversa... non la considero
continue ;
double dU = vvSec[k][l].dOs ;
if ( bPlus) {
if ( dU < dUstart)
dU += dUspan ;
if ( dU < dUref) {
dUref = dU ;
nNextI = k ;
nNextJ = l ;
}
}
else {
if ( dU > dUstart)
dU -= dUspan ;
if ( dU > dUref) {
dUref = dU ;
nNextI = k ;
nNextJ = l ;
}
}
}
li = 0 ;
}
// se trovato, controllo il contorno dell'isola
if ( nNextI != -1) {
PtrOwner<ICurve> pCopy ;
if ( bPlus) {
if ( dUref > dUmax)
dUref -= dUspan ;
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ;
if ( ! IsNull( pCopy)) {
double dCLen ; pCopy->GetLength( dCLen) ;
if ( dCLen > 0.5 * dLen) {
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ;
if ( ! IsNull( pCopy))
pCopy->Invert() ;
}
}
}
else {
if ( dUref < dUmin)
dUref += dUspan ;
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ;
if ( ! IsNull( pCopy)) {
pCopy->Invert() ;
double dCLen; pCopy->GetLength( dCLen) ;
if ( dCLen > 0.5 * dLen)
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ;
}
}
// controllo che nel percorso scelto non ritorni indietro superando la precendete passata
BBox3d b3Copy ;
if ( ! IsNull( pCopy))
pCopy->GetLocalBBox( b3Copy) ;
if ( ! b3Copy.IsEmpty() && ( b3Copy.GetMax().y - b3Copy.GetMin().y) < dYStep + 10 * EPS_SMALL) { // tengo la curva (non ritorno indietro più dello step)
Point3d ptS, ptE ;
pCrvLink->Clear() ;
pCrvLink->AddCurve( pCopy->Clone()) ;
vpCrvs.back()->AddCurve( Release( pCopy)) ;
nStatus = 2 ; // aggiunta parte di isola
// aggiungo il Link
nI = nNextI ;
nJ = nNextJ ;
}
else {
nI = -1 ;
nJ = -1 ;
}
}
else {
nI = -1 ;
nJ = -1 ;
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AddSpiralIn( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// ricavo il numero di Chunk da svuotare
if ( pSrfPock == nullptr)
return false ;
int nChunk = pSrfPock->GetChunkCount() ;
// scorro i Chunk
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
// indice del Chunk da corrente da svuotare
int nChunkInd = 0 ;
if ( PockParams.ptStart.IsValid()) {
// se il punto di riferimento è valido, il Chunk è quello ad esso più vicino
DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ;
int nMinLoop = 0 ;
double dMinPar = 0. ;
if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar))
return false ;
}
// Clono il Chunk attuale come regione FlatRegion
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
return false ;
const int MAX_REGS = 50 ; // massimo numero di regioni create dal primo offset
int nReg = 0 ; // chunk nuovo corrente da svuotare
// ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-esimo
while ( nReg < MAX_REGS) {
// calcolo la spirale dall'esterno all'interno e la curva che unisce inizio e fine
PtrOwner<ICurveComposite> pMCrv( CreateCurveComposite()) ;
if ( IsNull( pMCrv))
return false ;
int nRegTot = nReg ;
Point3d ptStart ;
bool bOptimizedTrap = false ;
// cerco le curve originali del chunk cc-esimo ( per casi ottimizzati)
ICRVCOMPOPOVECTOR vCrvOrigChunkLoops ;
if ( ! GetOptCrvIndex( pSfrOrig, pSfrChunk, PockParams, nReg, vCrvOrigChunkLoops))
return false ;
// calcolo il percorso di svuotatura
bool bSomeOpen ;
Vector3d vtMidOut ;
if ( ! CalcSpiral( pSfrChunk, PockParams, nRegTot, ptStart, vCrvOrigChunkLoops, pMCrv, bOptimizedTrap,
bSomeOpen, vtMidOut))
return false ;
// se terminate le regioni, esco
if ( pMCrv->GetCurveCount() == 0)
break ; // passo al chunk originale successivo
bool bIsExtended = false ;
// se caso a trapezio
if ( bOptimizedTrap) {
Vector3d vtRef ;
pMCrv->GetStartDir( vtRef) ;
vtRef.Invert() ;
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, false, bIsExtended))
return false ;
pMCrv->GetEndDir( vtRef) ;
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, true, bIsExtended))
return false ;
AssignFeedSpiralOpt( 1, PockParams, pMCrv) ;
pMCrv->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
}
// se esistenza di lati aperti validi
else if ( bSomeOpen)
ExtendPath( pMCrv, pSfrOrig, PockParams, vtMidOut, false, bIsExtended) ;
// inserisco le curve nel vettore
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
++ nReg ; // incremento il numero di regione progressiva
// aggiorno il punto di riferimento per il Chunk Successivo
vCrvCompoRes.back()->GetEndPoint( PockParams.ptStart) ;
}
// rimuovo il Chunk dalla regione
pSrfPock->EraseChunk( nChunkInd) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AddSpiralOut( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// ricavo il numero di Chunk da svuotare
if ( pSrfPock == nullptr)
return false ;
int nChunk = pSrfPock->GetChunkCount() ;
// ciclo sui chunk della superficie da svuotare
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
// indice del Chunk da corrente da svuotare
int nChunkInd = 0 ;
if ( PockParams.ptStart.IsValid()) {
// se il punto di riferimento è valido, il Chunk è quello ad esso più vicino
DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ;
int nMinLoop = 0 ;
double dMinPar = 0. ;
if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar))
return false ;
}
// Clono il Chunk attuale come regione FlatRegion
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
return false ;
const int MAX_REGS = 50 ; // massimo numero di regioni create dal primo offset
int nReg = 0 ; // chunk nuovo corrente da svuotare
// ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-esimo
while ( nReg < MAX_REGS) {
// calcolo la spirale dall'interno all'esterno
PtrOwner<ICurveComposite> pMCrv( CreateCurveComposite()) ;
if ( IsNull( pMCrv))
return false ;
int nRegTot = nReg ;
Point3d ptStart ;
bool bOptimizedTrap = false ;
// cerco la curva originale del chunk cc-esimo ( per casi ottimizzati)
ICRVCOMPOPOVECTOR vCrvOrig ;
if ( ! GetOptCrvIndex( pSfrOrig, pSfrChunk, PockParams, nReg, vCrvOrig))
return false ;
// calcolo il percorso di svuotatura
bool bSomeOpen ;
Vector3d vtMidOut ;
if ( ! CalcSpiral( pSfrChunk, PockParams, nRegTot, ptStart, vCrvOrig, pMCrv, bOptimizedTrap,
bSomeOpen, vtMidOut))
return false ;
// se terminate le regioni, esco
if ( pMCrv->GetCurveCount() == 0)
break ; // passo al chunk originale successivo
if ( bOptimizedTrap) { // se caso a trapezio
Vector3d vtRef ;
pMCrv->GetStartDir( vtRef) ;
vtRef.Invert() ;
bool bIsExtended = false ;
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, false, bIsExtended))
return false ;
pMCrv->GetEndDir( vtRef) ;
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, true, bIsExtended))
return false ;
AssignFeedSpiralOpt( 1, PockParams, pMCrv) ;
pMCrv->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
}
// inverto il percorso, perchè sono calcolati dall'esterno all'interno (solo nel caso non ottimizzato)
pMCrv->Invert() ;
// inserisco le curve nel vettore
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
// incremento il numero di regione progressiva
++ nReg ;
// aggiorno il punto di riferimento per il Chunk Successivo
vCrvCompoRes.back()->GetEndPoint( PockParams.ptStart) ;
}
// rimuovo il Chunk dalla regione
pSrfPock->EraseChunk( nChunkInd) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetZigZagOneWayBorderCrvs( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig,
const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvRes)
{
/*
Restituisce le curve di Primo Offset dei lati chiusi per i percorsi a ZigZag e OneWay.
Vengono calcolate le Feed ed eventuali entrate da fuori per i percorsi ricavati
*/
// controllo validità della regione
if ( pSfrPock == nullptr || ! pSfrPock->IsValid())
return false ;
// recupero la superficie limite e calcolo Offset
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrLimit))
return false ;
if ( PockParams.SfrLimit.IsValid()) {
pSfrLimit.Set( PockParams.SfrLimit.Clone()) ;
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
return false ;
pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset - 50 * EPS_SMALL, ICurve::OFF_FILLET) ;
}
// le curve che ricavo devono essere sempre conenute nella regione che sto svuotando
/*
Se siamo in presenza di isole chiuse molto vicine a lati chiusi non posso estendere il bordo
dell'isola chiusa a piacere, devo limitarlo alla regione di svuotatura
*/
PtrOwner<ISurfFlatRegion> pSfrBucket( CloneSurfFlatRegion( pSfrPock)) ;
if ( IsNull( pSfrBucket) || ! pSfrBucket->IsValid() ||
! pSfrBucket->Offset( - PockParams.dRad - PockParams.dRadialOffset + 50 * EPS_SMALL, ICurve::OFF_FILLET))
return false ;
// vettore delle curve chiuse Offsettate
ICRVCOMPOPOVECTOR vClosedOffs ;
// scorro i Chunk e i Loop della regione ( se OneWay potrei avere più Chunks)
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) {
ICRVCOMPOPOVECTOR vClosedOffs_nC ;
// loop come curva composita
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ;
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
return false ;
// recupero i tratti con proprietà uniformi
ICRVCOMPOPOVECTOR vpCrvs ;
GetHomogeneousParts( pCrvLoop, PockParams, vpCrvs) ;
if ( vpCrvs.size() > 1) {
// unisco il primo e l'ultimo se estremi compatibili
Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ;
Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ;
if ( AreSamePointApprox( ptS, ptE)) {
vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ;
vpCrvs.erase( vpCrvs.end() - 1) ;
}
}
// le isole aperte vanno percorse
bool bOpenIsland = ( nL > 0 && int( vpCrvs.size()) == 1 &&
vpCrvs[0]->GetTempProp() == TEMP_PROP_OPEN_EDGE) ;
// scorro tutti i tratti omogenei
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
// se tratto chiuso o isola aperta
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE || bOpenIsland) {
// Offset per le curve
OffsetCurve OffsCrv ;
double dOffs = - PockParams.dRad - PockParams.dRadialOffset ;
// se isola aperta, allora l'offset è verso il suo interno
if ( bOpenIsland)
dOffs *= -1 ;
if ( OffsCrv.Make( vpCrvs[i], dOffs, ICurve::OFF_FILLET)) {
PtrOwner<ICurve> pOffLongestCrv( OffsCrv.GetLongerCurve()) ;
while ( ! IsNull( pOffLongestCrv)) {
// recupero solo i tratti di curva al di fuori della superficie limite
if ( pSfrLimit->IsValid()) {
CRVCVECTOR ccClass ;
if ( pSfrLimit->GetCurveClassification( *pOffLongestCrv, EPS_SMALL, ccClass)) {
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
if ( ccClass[j].nClass == CRVC_OUT) {
// recupero il tratto di curva
PtrOwner<ICurve> pCrv( pOffLongestCrv->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
if ( ! IsNull( pCrv) && pCrv->IsValid())
vClosedOffs_nC.emplace_back( ConvertCurveToComposite( Release( pCrv))) ;
}
}
}
}
else
vClosedOffs_nC.emplace_back( ConvertCurveToComposite( Release( pOffLongestCrv))) ;
// passo alla successiva
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ;
}
}
}
}
// se non ho ricavato curve dal Chunk nC-esimo, passo al successivo
if ( vClosedOffs_nC.empty())
continue ;
// limito le curve ad essere contenute nella pSfrBucket
ICRVCOMPOPOVECTOR vClosedOffs1 ;
// scorro le curve di Offset chiuse ricavate
for ( int i = 0 ; i < int( vClosedOffs_nC.size()) ; ++ i) {
CRVCVECTOR ccClass ;
if ( pSfrBucket->GetCurveClassification( *vClosedOffs_nC[i], EPS_SMALL, ccClass)) {
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
if ( ccClass[j].nClass == CRVC_IN) {
// recupero il tratto di curva
PtrOwner<ICurve> pCrv( vClosedOffs_nC[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
if ( ! IsNull( pCrv) && pCrv->IsValid())
vClosedOffs1.emplace_back( ConvertCurveToComposite( Release( pCrv))) ;
}
}
}
}
if ( vClosedOffs1.empty())
continue ;
swap( vClosedOffs_nC, vClosedOffs1) ;
// concateno eventuali percorsi
if ( ! ChainCompoCurves( vClosedOffs_nC))
return false ;
// determino ora il punto di inizio, il tratto per il LeadIn
bool bIsChunkClosed = false ;
bool bIsChunkAllOpen = false ;
if ( ! IsChunkAllHomogeneous( pSfrPock, nC, bIsChunkClosed, bIsChunkAllOpen))
return false ;
if ( ! bIsChunkClosed) {
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)
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) {
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,bIsExtended))
return false ;
vpCrvs[nU]->GetEndDir( vtRef) ;
if ( ! ExtendPath( vpCrvs[nU], pSfrOrig, PockParams, vtRef, true, bIsExtended))
return false ;
}
}
// inserisco le curve nel vettore risultante
for ( int nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU)
vCrvCompoRes.emplace_back( Release( vpCrvs[nU])) ;
// libero il vettore di curve ZigZag per il chunk successivo
vpCrvs.clear() ;
}
// se richiesto, aggiungo le curve chiuse di Bordo
if ( PockParams.bAllowZigZagOneWayBorders) {
// recupero il Chunk nC-esimo
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid() ||
! GetZigZagOneWayBorderCrvs( pSfrChunk, pSfrOrig, PockParams, vCrvCompoRes))
return false ;
}
// rimuovo il Chunk dalla regione
pSrfPock->EraseChunk( nChunkInd) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
OptimizeChunkOneWay( const ISurfFlatRegion* pSfrOrig, ISURFFRPOVECTOR& vSrfIdeal)
{
// controllo parametri
if ( pSfrOrig == nullptr || pSfrOrig->GetChunkCount() == 0)
return false ;
vSrfIdeal.clear() ;
/*
classifico i chunks in modo da creare delle regioni ideali; una regione ideale è formata
da un chunk principale con tutti gli altri contenuti in esso ( in questo modo ottimizzo i
percorsi per i bordi)
*/
INTVECTOR vChunksAvailable( pSfrOrig->GetChunkCount(), 1) ;
for ( int nC = 0 ; nC < pSfrOrig->GetChunkCount() ; ++ nC) {
PtrOwner<ISurfFlatRegion> pSrfIdeal( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfIdeal))
return false ;
// se Chunk valido...
if ( vChunksAvailable[nC] == 1) {
// prendo la curva esterna
PtrOwner<ICurve> pCrvExt( pSfrOrig->GetLoop( nC, 0)) ;
if ( IsNull( pCrvExt))
return false ;
// inserisco il chunk nC-esimo ( curva per curva, così non perdo le temp prop)
pSrfIdeal->AddExtLoop( pCrvExt->Clone()) ;
for ( int nL = 1 ; nL < pSfrOrig->GetLoopCount( nC) ; ++ nL)
pSrfIdeal->AddIntLoop( pSfrOrig->GetLoop( nC, nL)) ;
// il chunk nC-esimo non è più disponibile...
vChunksAvailable[nC] = -1 ;
// scorro tutti gli altri chunk i-esimi ancora disponibili
for ( int i = 0 ; i < int( vChunksAvailable.size()) ; ++ i) {
if ( vChunksAvailable[i] == 1) {
// prendo la curva esterna del chunk i-esimo disponibile
PtrOwner<ICurve> pCrvExtC( pSfrOrig->GetLoop( i, 0)) ;
if ( IsNull( pCrvExtC))
return false ;
// classifico i bordi esterni ( se il bordo del chunk nC-esimo contiene o è contenuto nel loop
// esterno del chunk i-esimo -> sono chunks appartenenti alla stessa superficie ideale)
IntersCurveCurve intCC( *pCrvExtC, *pCrvExt) ;
CRVCVECTOR ccClass, ccClass1 ;
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
intCC.GetCurveClassification( 1, EPS_SMALL, ccClass1) ;
if (( int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) ||
( int( ccClass1.size()) == 1 && ccClass1[0].nClass == CRVC_IN)) {
// inserisco il chunk i-esimo ( curva per curva, così non perdo le temp prop)
pSrfIdeal->AddExtLoop( pSfrOrig->GetLoop( i, 0)) ;
for ( int nL = 1 ; nL < pSfrOrig->GetLoopCount( i) ; ++ nL)
pSrfIdeal->AddIntLoop( pSfrOrig->GetLoop( i, nL)) ;
vChunksAvailable[i] = -1 ; // chunk j-esimo non più disponibile
// NB: Se il chunk i-esimo contiene la pSrfIdeal ( quindi chunk i-esimo unito ad eventuali altri chunk
// j-esimi precedenti -> prendo come bordo esterno di riferimento il suo e ricontrollo dall'inizio i
// chunk disponibili
if ( int( ccClass1.size()) == 1 && ccClass1[0].nClass == CRVC_IN) {
pCrvExt.Set( pCrvExtC) ;
i = 0 ;
}
}
}
}
}
if ( pSrfIdeal->GetChunkCount() > 0)
vSrfIdeal.emplace_back( Release( pSrfIdeal)) ;
}
return ( ! vSrfIdeal.empty()) ;
}
//----------------------------------------------------------------------------
static bool
AddOneWay( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// offset della regione per segmenti a ZigZag
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
double dExtra = 0. ;
if ( PockParams.bAllowZigZagOneWayBorders)
dExtra = PockParams.dOffsExtra ;
// vettore delle regioni ideali
ISURFFRPOVECTOR vSrfFlat ;
if ( ! OptimizeChunkOneWay( pSrfPock, vSrfFlat))
return false ;
// scorro le regioni ideali
for ( int nIs = 0 ; nIs < int( vSrfFlat.size()) ; ++ nIs) {
// copio la superficie ideale ed effettuo il primo Offset
PtrOwner<ISurfFlatRegion> pSrfIdeal( CloneSurfFlatRegion( vSrfFlat[nIs])) ;
if ( IsNull( pSrfIdeal) || ! pSrfIdeal->Offset( - dOffs, ICurve::OFF_FILLET))
return false ;
// se sono richieste le curve di Bordo, le aggiungo
if ( PockParams.bAllowZigZagOneWayBorders) {
if ( ! GetZigZagOneWayBorderCrvs( vSrfFlat[nIs], pSfrOrig, PockParams, vCrvCompoRes))
return false ;
}
// recupero il Box della superficie attuale da svuotare
BBox3d b3Pocket ; pSrfIdeal->GetLocalBBox( b3Pocket) ;
Point3d ptMin ; double dDimX, dDimY, dDimZ ;
b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ;
// passi in Y
int nYStep = static_cast<int>( ceil(( dDimY + 2 * dExtra) / PockParams.dSideStep)) ;
double dYStep = ( nYStep > 0 ? ( dDimY + 2 * dExtra) / nYStep : 0) ;
-- nYStep ;
// vettore dei segmenti al di sotto della linea corrente ( per le Feed)
ICURVEPOVECTOR vLineUnder ;
ICURVEPOVECTOR vCrvNull ;
ICRVCOMPOPOVECTOR vCrvCompoNull ;
// calcolo le linee di svuotatura
const double EXP_LEN = 1.0 ;
for ( int i = 1 ; i <= nYStep ; ++ i) {
// definisco la linea
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
Point3d ptStart( ptMin.x - EXP_LEN, ptMin.y + ( - dExtra + i * dYStep), ptMin.z + dDimZ) ;
if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN))
return false ;
// se richiesta inversione
if ( PockParams.bInvert)
pLine->Invert() ;
// linea come composita per Feed ( la dovrò spezzare in tratti uniformi di Feed)
PtrOwner<ICurveComposite> pCrvCompo( CreateCurveComposite()) ;
if ( IsNull( pCrvCompo))
return false ;
// vettore di tutti i segmenti lineari che si formano nello step nYStep
ICURVEPOVECTOR vAddedLines ;
// riempio il vettore di segmenti
CRVCVECTOR ccClassSeg ;
pSrfIdeal->GetCurveClassification( *pLine, EPS_SMALL, ccClassSeg) ;
for ( int w = 0 ; w < int( ccClassSeg.size()) ; ++ w) {
if ( ccClassSeg[w].nClass == CRVC_IN) {
PtrOwner<ICurveLine> pCrvSeg( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ;
double duF, dLen ;
// accorcio leggermente il segmento per non toccare la prima curva di Offset
if ( ! pCrvSeg->GetLength( dLen) ||
dLen < 2 * dExtra ||
! pCrvSeg->GetParamAtLength( dLen - dExtra, duF) ||
! pCrvSeg->TrimStartAtLen( dExtra) ||
! pCrvSeg->TrimEndAtParam( duF))
pCrvSeg.Set( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ;
// aggiungo il segmento al vettore dei tratti correnti
vAddedLines.emplace_back( pCrvSeg->Clone()) ;
// trasformo il tratto lineare in curve composita per il calcolo della Feed
PtrOwner<ICurveComposite> pCrvSegCompo( CreateCurveComposite()) ;
if ( IsNull( pCrvSegCompo))
return false ;
pCrvSegCompo->AddCurve( Release( pCrvSeg)) ;
// calcolo la Feed
AssignFeedZigZagOneWay( pCrvSegCompo, false, vCrvNull, vLineUnder, vCrvCompoNull, PockParams) ;
// 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,bIsExtended))
return false ;
pCrvSegCompo->GetEndDir( vtRef) ;
if ( ! ExtendPath( pCrvSegCompo, pSfrOrig, PockParams, vtRef, true, 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 ;
// curva precedente
PtrOwner<ICurveComposite> pCrvBef( CreateCurveComposite()) ;
if ( IsNull( pCrvBef))
return false ;
if ( bFirstOffs0 && pCrvOffs0->IsClosed()) {
const ICurve* pCrvFirst = pCrvOffs0->GetFirstCurve() ;
if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid())
return false ;
PtrOwner<ICurve> pMyCrv( pCrvFirst->Clone()) ;
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
return false ;
pCrvBef->AddCurve( Release( pMyCrv)) ;
}
else {
const ICurve* pCrvLast = pCrvOffs0->GetLastCurve() ;
if ( pCrvLast == nullptr || ! pCrvLast->IsValid())
return false ;
PtrOwner<ICurve> pMyCrv( pCrvLast->Clone()) ;
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
return false ;
pCrvBef->AddCurve( Release( pMyCrv)) ;
}
// curva successiva
PtrOwner<ICurveComposite> pCrvAft( CreateCurveComposite()) ;
if ( IsNull( pCrvAft))
return false ;
const ICurve* pCrvFirst = pCrvOffs1->GetFirstCurve() ;
if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid())
return false ;
PtrOwner<ICurve> pMyCrv( pCrvFirst->Clone()) ;
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
return false ;
pCrvAft->AddCurve( Release( pMyCrv)) ;
// controllo la validità delle curve precedenti e successive ricavate
if ( IsNull( pCrvBef) || IsNull( pCrvAft) || ! pCrvBef->IsValid() || ! pCrvAft->IsValid())
return false ;
// estendo il Link
pCrvLink->AddCurve( Release( pCrvBef), false, dTol) ;
pCrvLink->AddCurve( Release( pCrvAft), true, dTol) ;
// smusso
ModifyCurveToSmoothed( pCrvLink, PockParams, dSmoothPar, dSmoothPar, false) ;
// toglo gli estremi
delete( pCrvLink->RemoveFirstOrLastCurve( false)) ;
delete( pCrvLink->RemoveFirstOrLastCurve( true)) ;
// modifico Offset0 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 Offset1 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
GetConformalLink( 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) ;
// creo la curva che le collegherà
bool bSmooth = PockParams.bSmooth ;
PockParams.bSmooth = false ;
// 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()) ;
if ( ! CutCurveToConnect( pCrvOffs0, pCrvOffs1, vCrvChunk, PockParams,
( ( bOpen0 || bFirstIterOffs0) ? 0. : 10 * EPS_SMALL),
( ( bOpen1 || bFirstIterOffs1) ? 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
if ( ! CalcBoundedSmoothedLink( ptS, vtS, ptE, vtE, 0.5, vCrvChunk, PockParams, pCrvLink) ||
! pCrvLink->IsValid())
return false ;
}
PockParams.bSmooth = bSmooth ;
SmoothLinkByOffs( pCrvOffs0, pCrvOffs1, pCrvLink, PockParams, bFirstIterOffs0, bFirstIterOffs1,
PockParams.dRad / 16., 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) ;
}
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
AdjustCloseEsgesForConformalGuide( 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( 100 * 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], 125 * 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.
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 ;
if ( ! GetSfrCrvCompoLoops( pSfrClass, 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 ;
// ------------ Definisco i Link ------------
if ( PockParams.nType == POCKET_CONFORMAL_ONEWAY)
vCrvLink.resize( vCrvOffs.size()) ;
else {
// per definizione il primo Link è nullo
vCrvLink.resize( 1) ;
// scorro le curve di Offset a coppie
for ( int i = 0 ; i < int( vCrvOffs.size()) - 1 ; ++ i) {
// controllo se bisogna calcolare il Link tra i due Offset
bool bCalcLink = true ;
if ( ! CheckConformalRetractLink( vCrvOffs[i], vCrvOffs[i+1], vCrvSfrClass, PockParams, bCalcLink))
return false ;
if ( bCalcLink) {
// se link da calcolare, lo ricavo
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
if ( IsNull( pCrvLink))
return false ;
// se non calcolabile, allora retroazione, altrimenti lo memorizzo
if ( ! GetConformalLink( 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 PocketParams& PockParams,
ICRVCOMPOPOVECTOR& vCrvOffs, ICURVEPOVECTOR& vCrvLinks)
{
// controllo dei parametri
if ( pSfrPock == nullptr || ! pSfrPock->IsValid())
return false ;
if ( vCrvOffs.empty())
return true ;
/*
dalla superificie estesa presso i lati aperti, effettuo un Offset interno pari alla somma del
raggio utensile e dell'offset radiale. La ricerca delle zone non svuotate avviene all'interno
di questa regione
*/
ICRVCOMPOPOVECTOR vCrvFirstOffs ;
if ( ! GetFirstOffsCrvFromSfr( pSfrPock, - PockParams.dRad - PockParams.dRadialOffset, vCrvFirstOffs))
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( vCrvFirstOffs, vCrvOffs, vCrvLinks, nullptr, PockParams, pSfrUncleared)) {
// estendo i percorsi di Offset se richiesto
if ( ! RemoveExtraParts( pSfrUncleared, vCrvOffs, vCrvFirstOffs, PockParams))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AddLeadInToCurveConformalPaths( 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 vCrvOpenEdge ;
{
ICRVCOMPOPOVECTOR vCrvLoops ;
if ( ! GetSfrCrvCompoLoops( pSfrPock, vCrvLoops))
return false ;
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 vtTan ; vCrvPaths[i]->GetStartDir( vtTan) ;
vtTan.Invert() ;
bool bOkExtended ;
if ( ! ExtendPath( vCrvPaths[i], pSfrOrig, PockParams, vtTan, false, bOkExtended))
return false ;
vCrvPaths[i]->GetEndDir( vtTan) ;
if ( ! ExtendPath( vCrvPaths[i], pSfrOrig, PockParams, vtTan, true, bOkExtended))
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 ;
if ( int( vCrvPaths.size()) < 2)
return true ;
// superficie di controllo per parti isolate
PtrOwner<ISurfFlatRegion> pSfrToRemove( CloneSurfFlatRegion( pSfrChunk)) ;
if ( IsNull( pSfrToRemove) || ! pSfrToRemove->IsValid() ||
! 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 ;
}
// 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) ;
}
if ( vTempInds.empty()) {
// se non ho trovato curva candidate, prendo l'indice disponibile più basso
for ( int k = 0 ; k < int( vInds.size()) ; ++ k) {
if ( find( vInds.begin(), vInds.end(), k) != vInds.end())
continue ;
vInds[i] = k ;
break ;
}
}
else if ( int( vTempInds.size()) == 1) {
// se la candidata è singola, allora è lei
vInds[i] = vTempInds[0] ;
if ( PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
if ( vCrvPaths[vInds[i]]->IsClosed() && i > 0) {
Point3d ptS ; vCrvPaths[vInds[i-1]]->GetStartPoint( ptS) ;
DistPointCurve DistCalculator( ptS, *vCrvPaths[vInds[i]]) ;
int nFlag ; double dUS ;
DistCalculator.GetParamAtMinDistPoint( 0, dUS, nFlag) ;
vCrvPaths[vInds[i]]->ChangeStartPoint( dUS) ;
}
}
}
else {
// se lavorazione conformal ZigZag
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) {
// se ne ho trovate di più, allora euristicamente prendo la curve 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...
}
}
// se la regione di incidenza non è valida, scelgo erusiticamente la curva più corta
else {
double dLen ; vCrvPaths[vTempInds[k]]->GetLength( dLen) ;
if ( dLen < dMinLen) {
dMinLen = dLen ;
vInds[i] = vTempInds[k] ;
}
}
}
// se ho solo curve adiacenti a regioni di incidenza
if ( dMinArea > EPS_SMALL) {
struct MyCompoClass {
PtrOwner<ICurveComposite> pCrvExtLoop ;
int nIndex ;
int nBorders ;
} ;
// euristicamente scelgo quella il cui bordo esterno contiene più curve di bordo esterno
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] ;
}
}
}
}
}
// se lavorazione conformal OneWay
else {
// se ne ho trovate di più, cerco euristicamente la più vicina al punto iniziale della curva
// se prima in assoluto, allora prendo la più corta
if ( i == 0) {
double dMinLen = INFINITO ;
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
double dLen ; vCrvPaths[vTempInds[k]]->GetLength( dLen) ;
if ( dLen < dMinLen) {
dMinLen = dLen ;
vInds[i] = vTempInds[k] ;
}
}
}
// altrimenti
else {
double dSqMinDist = INFINITO ;
Point3d ptS ; vCrvPaths[vInds[i-1]]->GetStartPoint( ptS) ;
Point3d ptTmpS ;
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
if ( vCrvPaths[vTempInds[k]]->IsClosed())
continue ;
vCrvPaths[vTempInds[k]]->GetStartPoint( ptTmpS) ;
double dMyDist = SqDist( ptS, ptTmpS) ;
if ( dMyDist < dSqMinDist) {
dSqMinDist = dMyDist ;
vInds[i] = vTempInds[k] ;
}
}
if ( dSqMinDist > INFINITO - 1) {
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
if ( vCrvPaths[vTempInds[k]]->IsClosed()) {
DistPointCurve DistCalculator( ptS, *vCrvPaths[vTempInds[k]]) ;
double dMySqDist ;
DistCalculator.GetSqDist( dMySqDist) ;
if ( dMySqDist < dSqMinDist) {
dSqMinDist = dMySqDist ;
vInds[i] = vTempInds[k] ;
double dUS ;
int nFlag ;
DistCalculator.GetParamAtMinDistPoint( 0, dUS, nFlag) ;
vCrvPaths[vTempInds[k]]->ChangeStartPoint( dUS) ;
}
}
}
}
}
}
}
// 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() ;
}
// 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() )
*/
vCrvPaths.clear() ;
for ( int i = 0 ; i < int( vCrvOrderedPaths.size()) ; ++ 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 ;
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]) ;
AddLeadInToCurveConformalPaths( vSfrRemaining[i], pSfrChunk, PockParams, vCurrPath) ;
vCrvPaths.emplace_back( Release( vCurrPath[0])) ;
}
}
// aggiungo leadIn e LeadOut per i percorsi rimasti
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) {
ICRVCOMPOPOVECTOR vCurrPath ; vCurrPath.resize( 1) ;
vCurrPath[0].Set( vCrvPaths[i]) ;
AddLeadInToCurveConformalPaths( vSfrRemaining[i], pSfrChunk, PockParams, vCurrPath) ;
vCrvPaths[i].Set( 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 = 1000. ;
// ricavo il punto finale della guida
Point3d ptEndGuide ; pCompoTempGuide->GetEndPoint( ptEndGuide) ;
// direzione finale della guida
Vector3d vtEndGuide ; pCompoTempGuide->GetEndDir( vtEndGuide) ;
// creo un segmento diretto come il chiuso
PtrOwner<ICurveLine> pSeg( CreateCurveLine()) ;
if ( IsNull( pSeg) ||
! pSeg->Set( ptEndGuide, ptEndGuide + EXTENSION_VAL * vtEndGuide))
return false ;
// calcolo l'intersezione tra questo segmento e la curva di perimetro aperta
IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ;
// se l'unica intersezione è il punto iniziale del segmento, allora non estendo la guida
// ( vale anche nel caso di intersezione con Overlap)
if ( ICC.GetIntersCount() == 1) {
IntCrvCrvInfo aInfo ;
if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) {
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide))
return true ;
}
}
// passo alla PolyLine del perimetro
PolyLine PL ;
pCompoPerimeter->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, PL) ;
// cerco il punto più esterno rispetto alla direzione del chiuso
Point3d ptPoly ;
PL.GetFirstPoint( ptPoly) ;
while ( PL.GetNextPoint( ptPoly)) {
if ( ! pSeg->Set( ptEndGuide, ptEndGuide + ( ptPoly - ptEndGuide) * EXTENSION_VAL))
return false ;
// calcolo l'intersezione tra questo segmento e la curva di perimetro aperta
IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ;
// se una intersezione, allora di Overlap con punto iniziale e finale definito
if ( ICC.GetIntersCount() == 1) {
IntCrvCrvInfo aInfo ;
if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) {
if ( aInfo.bOverlap) {
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide) &&
AreSamePointApprox( aInfo.IciB[1].ptI, ptPoly))
return ( pCompoTempGuide->AddLine( ptPoly)) ;
}
else {
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide))
return ( pCompoTempGuide->AddLine( ptPoly)) ;
}
}
}
// se più di una intersezione, allora il perimetro deve essere tutto Interno al segmento
// ( Out nel caso in cui ho le curve invertite)
IntCrvCrvInfo aInfo ;
bool bOk = true ;
for ( int i = 0 ; i < ICC.GetIntersCount() && bOk ; ++ i) {
bOk = ( ICC.GetIntCrvCrvInfo( i, aInfo)) ;
bOk = ( aInfo.bOverlap ||
( aInfo.IciB[0].nPrevTy != ( ! bInverted ? CRVC_OUT : CRVC_IN) &&
aInfo.IciB[0].nNextTy != ( ! bInverted ? CRVC_OUT : CRVC_IN))) ;
}
if ( bOk)
return ( pCompoTempGuide->AddLine( ptPoly)) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
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() ||
! AdjustCloseEsgesForConformalGuide( 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 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 int 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() ||
! AdjustCloseEsgesForConformalGuide( pCrvExtLoop, PockParam) ||
! 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]) ;
}
int MAX_ITER = 1000 ;
int nIter = 0 ;
double dOffs = PockParam.dRad + PockParam.dRadialOffset ;
double dOffsPrec = 0. ;
bool bStopSmallRad = 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) {
// almeno una curva in trovata
bStop = false ;
// recupero il tratto di curva
PtrOwner<ICurve> pMyCrv( vFatCrv[j]->CopyParamRange( ccClass[k].dParS, ccClass[k].dParE)) ;
if ( ! IsNull( pMyCrv) && pMyCrv->IsValid()) {
// 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)
if ( ! ChainCompoCurves( vCrvOffsInside))
return false ;
// controllo se serve un raggio più piccolo di svuotatura
bool bSmallRad = ( nIter == 0 ? dOffs < PockParam.dRad + EPS_ZERO : dOffs - dOffsPrec < PockParam.dRad + EPS_ZERO) ;
// se ho trovato delle curve interne
if ( ! bStop) {
dOffsPrec = dOffs ;
dOffs += PockParam.dSideStep ;
// 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) ;
vCrvOffs.back().emplace_back( Release( vCrvOffsInside[i])) ;
}
// se devo terminare, interrompo il loop
if ( bStopSmallRad)
break ;
}
// se non ho ricavato curve ma serve un offset del raggio utensile
else if ( ! bSmallRad) {
dOffs = dOffsPrec + ( nIter == 0 ? PockParam.dRad + PockParam.dRadialOffset : PockParam.dRad) ;
bStopSmallRad = true ;
}
else
break ;
}
// smusso le curve di Offset ( ad eccezione di quelle generate alla prima iterazione)
double dSmoothPar = PockParam.dRad / 8. ;
for ( int i = 1 ; i < int( vCrvOffs.size()) ; ++ i) {
for ( int j = 0 ; j < int( vCrvOffs[i].size()) ; ++ j)
ModifyCurveToSmoothed( vCrvOffs[i][j], PockParam, dSmoothPar, dSmoothPar, false) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetConformalSfrClassChunk( const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrClass,
INTVECTOR& vnChunk)
{
// controllo dei parametri
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() ||
pSfrClass == nullptr || ! pSfrClass->IsValid())
return false ;
vnChunk.clear() ;
// se la superficie di classificazione ha un solo chunk, esco
if ( pSfrClass->GetChunkCount() == 1) {
vnChunk.emplace_back( 0) ;
return true ;
}
// controllo quali Chunk di pSfrClass intersecano la pSfrChunk
for ( int nC = 0 ; nC < pSfrClass->GetChunkCount() ; ++ nC) {
// recupero il Chunk nC-esimo
PtrOwner<ISurfFlatRegion> pSfrClassChunk( pSfrClass->CloneChunk( nC)) ;
if ( IsNull( pSfrClassChunk) || ! pSfrClass->IsValid())
return false ;
// controllo se l'intersezione non è vuota
pSfrClassChunk->Intersect( *pSfrChunk) ;
if ( ! IsNull( pSfrClassChunk) && pSfrClassChunk->IsValid())
vnChunk.push_back( nC) ;
}
return true ;
}
//----------------------------------------------------------------------------
static bool
GetSfrClassConformal( const ISurfFlatRegion* pSfrPock, const PocketParams& PockParams, ISurfFlatRegion* pSfrClass)
{
// controllo dei parametri
if ( pSfrPock == nullptr || ! pSfrPock->IsValid())
return false ;
// dalla pSfrPock rimuovo i Chunk tutti chiusi o tutti aperti
pSfrClass->CopyFrom( pSfrPock) ;
// per ora tengo la superficie di classificazione esattamente identica alla superficie di svuotatura
#if 0
// Offset per definire la regione di taglio per le curve di Conformal
if ( ! pSfrClass->Offset( - ( 3. * PockParams.dRad / 4.) - PockParams.dRadialOffset, ICurve::OFF_FILLET))
return false ;
// rimuovo tutti i Chunk con massimo Offset più piccolo del raggio utensile
for ( int nC = 0 ; nC < pSfrClass->GetChunkCount() ; ++ nC) {
double dMaxOffs = EPS_SMALL ;
if ( ! pSfrClass->GetChunkMaxOffset( nC, dMaxOffs))
return false ;
if ( dMaxOffs < PockParams.dRad + PockParams.dRadialOffset + 500 * EPS_SMALL) {
pSfrClass->EraseChunk( nC) ;
-- nC ;
}
}
#endif
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 ( AddSpiralIn( pSfrPock, pSfrOrig, PockParams, vCrvCompoRes)) ;
// se superficie tutta chiusa, lavoro in SPIRAL_OUT
if ( PockParams.bAllClosed)
return ( AddSpiralOut( pSfrPock, pSfrOrig, PockParams, vCrvCompoRes)) ;
// Definisco la superficie di classificazione ( su essa vengono tagliate le curve progressive di Offset)
PtrOwner<ISurfFlatRegion> pSfrClass( CreateSurfFlatRegion()) ;
if ( IsNull( pSfrClass) || ! GetSfrClassConformal( pSfrPock, PockParams, pSfrClass))
return false ;
if ( ! pSfrClass->IsValid() || pSfrClass->GetChunkCount() == 0)
return true ; // non posso svuotare nulla
// 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, lo lavoro in SPIRAL_IN
if ( bOpen) {
if ( ! AddSpiralIn( pSfrChunk, pSfrOrig, PockParams, vCrvCompoRes))
return false ;
}
// se Chunk tutto chiuso, lo lavoro in SPIRAL_OUT
else if ( bClose) {
if ( ! AddSpiralOut( pSfrChunk, pSfrOrig, PockParams, vCrvCompoRes))
return false ;
}
// se Chunk non omogeneo
else {
// ricavo il Chunk della superficie di classificazione corrente
INTVECTOR vnChunk ;
if ( ! GetConformalSfrClassChunk( pSfrChunk, pSfrClass, vnChunk))
return false ;
// per tutti i Chunk classificati
for ( int i = 0 ; i < int( vnChunk.size()) ; ++ i) {
// ricavo il chunk di classificazione i-esimo
PtrOwner<ISurfFlatRegion> pSfrClassChunk( pSfrClass->CloneChunk( vnChunk[i])) ;
if ( IsNull( pSfrClassChunk) || ! pSfrClassChunk->IsValid())
return false ;
// ricavo gli Offset ( se possibili) dei tratti chiusi del Chunk
VICRVCOMPOPOVECTOR vvCrvOffs ;
if ( ! GetConformalOffsets( pSfrChunk, pSfrClassChunk, PockParams, vvCrvOffs))
return false ;
// se non ottengo Curve di Offset, lavoro in SpiralIn ( il Chunk ha dei lati aperti)
if ( vvCrvOffs.empty()) {
PtrOwner<ISurfFlatRegion> pSfrChunk_cl( CloneSurfFlatRegion( pSfrChunk)) ;
if ( IsNull( pSfrChunk_cl) || ! pSfrChunk_cl->IsValid() ||
! AddSpiralIn( pSfrChunk_cl, pSfrOrig, PockParams, vCrvCompoRes))
return false ;
}
else {
// definisco vettore degli Offset e dei Link
ICRVCOMPOPOVECTOR vCrvOffs ;
ICURVEPOVECTOR vCrvLink ;
// ordino le curve di Offset e raccordo
if ( ! CalcConformalOffsAndLinks( vvCrvOffs, pSfrChunk, pSfrClassChunk, PockParams, vCrvOffs, vCrvLink)) {
// se calcolare i raccordi risulta troppo ambiguo o complesso, ritorno a SpiralIn
PtrOwner<ISurfFlatRegion> pSfrChunk_cl( CloneSurfFlatRegion( pSfrChunk)) ;
if ( IsNull( pSfrChunk_cl) || ! pSfrChunk_cl->IsValid() ||
! AddSpiralIn( pSfrChunk_cl, pSfrOrig, PockParams, vCrvCompoRes))
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, 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) ;
if ( ! bOk) {
// se qualche passaggio restituisce errore, provo in SpiralIn
PtrOwner<ISurfFlatRegion> pSfrChunk_cl( CloneSurfFlatRegion( pSfrChunk)) ;
if ( IsNull( pSfrChunk_cl) || ! pSfrChunk_cl->IsValid() ||
! AddSpiralIn( pSfrChunk_cl, pSfrOrig, PockParams, vCrvCompoRes))
return false ;
}
else {
// aggiungo i percorsi ricavati
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i)
vCrvCompoRes.emplace_back( Release( vCrvPaths[i])) ;
}
}
}
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double dStep, double dAngle,
int nType, bool bSmooth, bool bInvert, bool bAvoidOpt, bool bAllowZigZagOneWayBorders,
bool bCalcFeed, const Point3d& ptEndPrec, const ISurfFlatRegion* pSfrLimit, bool bAllOffs, 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.bInvert = bInvert ;
myParams.bAvoidOpt = bAvoidOpt ;
myParams.bCalcFeed = bCalcFeed ;
myParams.bAllowZigZagOneWayBorders = bAllowZigZagOneWayBorders ;
myParams.bOptOffsets = ( ! bAllOffs) ;
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 ;
}
// 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 ;
}
//----------------------------------------------------------------------------
static bool
AdjustZigZagPathTangentLinks( ICurveComposite* pCrvCompo)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
// miglioro la curva
pCrvCompo->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
// scorro tutte le curve
int nCurrTmpProp = -1, nCurrTmpProp1 = -1 ;
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
if ( pCrvCompo->GetCurveTempProp( u, nCurrTmpProp, 1) && nCurrTmpProp == -1) {
// ricavo il link
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
if ( IsNull( pCrvLink))
return false ;
pCrvLink->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ;
for ( int uu = u + 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) {
if ( pCrvCompo->GetCurveTempProp( uu, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
if ( ! pCrvLink->AddCurve( pCrvCompo->GetCurve( uu)->Clone()))
return false ;
}
else
break ;
}
if ( ! pCrvLink->IsValid())
return false ;
// se ho più di una curva nel link
if ( pCrvLink->GetCurveCount() > 1) {
// ricavo la curva precendente al Link ( se presente e se prima curva del Link è lineare)
if ( u != 0 && pCrvLink->GetFirstCurve()->GetType() == CRV_LINE) {
const ICurve* pCrvSegPrec = pCrvCompo->GetCurve( u - 1) ;
if ( pCrvSegPrec == nullptr || pCrvSegPrec->GetType() != CRV_LINE)
return false ;
// ricavo la sua direzione finale
Vector3d vtZigZagEndDir ; pCrvSegPrec->GetEndDir( vtZigZagEndDir) ;
// ricavo la direzione iniziale del link
Vector3d vtLinkStartDir ; pCrvLink->GetStartDir( vtLinkStartDir) ;
// se i vettori sono paralleli, considero la prima curva del link come parte del percorso a ZigZag
if ( AreSameVectorApprox( vtZigZagEndDir, vtLinkStartDir))
pCrvCompo->SetCurveTempProp( u, 0, 1) ;
}
// ricavo la curva successiva al Link ( se presente e se ultima curva del Link è lineare)
if ( u + pCrvLink->GetCurveCount() < pCrvCompo->GetCurveCount() && pCrvLink->GetLastCurve()->GetType() == CRV_LINE) {
const ICurve* pCrvSegSucc = pCrvCompo->GetCurve( u + pCrvLink->GetCurveCount()) ;
if ( pCrvSegSucc == nullptr || pCrvSegSucc->GetType() != CRV_LINE)
return false ;
// ricavo la sua direzione inziale
Vector3d vtZigZagStartDir ; pCrvSegSucc->GetStartDir( vtZigZagStartDir) ;
// ricavo la direzione finale del link
Vector3d vtLinkStartDir ; pCrvLink->GetEndDir( vtLinkStartDir) ;
// se i vettori sono paralleli, considero l'ultima curva del link come parte del percorso a ZigZag
if ( AreSameVectorApprox( vtZigZagStartDir, vtLinkStartDir))
pCrvCompo->SetCurveTempProp( u + pCrvLink->GetCurveCount() - 1, 0, 1) ;
}
}
// aggiorno
u += pCrvLink->GetCurveCount() ; // così la successiva è corretta nel ciclo
}
}
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustLinkSameY( ICurveComposite* pCrvLink, bool& bSplit)
{
// controllo dei parametri
if ( pCrvLink == nullptr || ! pCrvLink->IsValid())
return false ;
bSplit = false ;
// ricavo gli estremi del Link
Point3d ptS ; pCrvLink->GetStartPoint( ptS) ;
Point3d ptE ; pCrvLink->GetEndPoint( ptE) ;
// ricavo il versore uscente da ptS e diretto verso ptE
Vector3d vtDir = ptE - ptS ;
if ( vtDir.IsValid() && ! vtDir.IsSmall())
vtDir.Normalize() ;
else
return false ;
// creo un frame orientato come questo versore
Frame3d frLink ;
frLink.Set( ptS, Z_AX, vtDir) ;
if ( ! frLink.IsValid())
return false ;
// ricavo il box3d del Link in questo frame
BBox3d BBoxLink ;
pCrvLink->ToLoc( frLink) ;
if ( ! pCrvLink->GetLocalBBox( BBoxLink) || BBoxLink.IsEmpty())
return false ;
pCrvLink->ToGlob( frLink) ;
// controllo la dimensione del Box in Y
if ( abs( BBoxLink.GetDimY()) > 20 * EPS_SMALL)
bSplit = true ;
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustLinkDifferentY( const ICurve* pCrvSegPrec, const ICurve* pCrvSegSucc,
const ICurveComposite* pCrvLink, const PocketParams& PockParams, bool& bSplit)
{
// controllo dei parametri
if ( pCrvLink == nullptr || ! pCrvLink->IsValid())
return false ;
if ( pCrvSegPrec == nullptr && pCrvSegSucc == nullptr)
return true ;
bSplit = false ;
// creo un Trapezoide per regione di incidenza
Point3d ptH ;
PtrOwner<ISurfFlatRegion> pSfrTrap( CreateSurfFlatRegion()) ;
PtrOwner<ICurveComposite> pCrvTrapBorder( CreateCurveComposite()) ;
if ( IsNull( pSfrTrap) ||
IsNull( pCrvTrapBorder))
return false ;
// 0) primo tratto
if ( pCrvSegPrec != nullptr) {
// se presente lo aggiungo
if ( ! pCrvTrapBorder->AddCurve( pCrvSegPrec->Clone()))
return false ;
}
else {
// se non presente, inserisco il punto iniziale del Link
pCrvLink->GetStartPoint( ptH) ;
pCrvTrapBorder->AddPoint( ptH) ;
}
// 1) tratto lineare fino al punto finale del Link
if ( ! pCrvLink->GetEndPoint( ptH) ||
! pCrvTrapBorder->AddLine( ptH))
return false ;
// 2) terzo tratto
if ( pCrvSegSucc != nullptr) {
// se presente lo aggiungo
if ( ! pCrvTrapBorder->AddCurve( pCrvSegSucc->Clone()))
return false ;
}
// se non presente non aggiungo nulla, ho alla peggio il Link
// 4) ultimo tratto lineare per chiudere la curva
if ( ! pCrvTrapBorder->Close() || // ora creo la FlatRegion a trapezio
! pSfrTrap->AddExtLoop( Release( pCrvTrapBorder)) ||
! pSfrTrap->IsValid())
return false ;
// oriento
if ( AreOppositeVectorApprox( Z_AX, pSfrTrap->GetNormVersor()))
pSfrTrap->Invert() ;
// effettuo un piccolo Offset per una maggiore tolleranza ( se possibile)
PtrOwner<ISurfFlatRegion> pSfrTrap_c( pSfrTrap->Clone()) ;
if ( IsNull( pSfrTrap_c))
return false ;
if ( ! pSfrTrap->Offset( - PockParams.dRad / 5, ICurve::OFF_CHAMFER))
pSfrTrap.Set( pSfrTrap_c) ;
// se il Link interseca questa regione, allora va eliminato
CRVCVECTOR ccClass ;
if ( pSfrTrap->GetCurveClassification( *pCrvLink, EPS_SMALL, ccClass))
bSplit = int( ccClass.size()) > 1 ;
return true ;
}
//----------------------------------------------------------------------------
static bool
AdjustLinkZigZagInfill( ICurveComposite* pCrvCompo, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vNewCrv)
{
// controllo dei parametri
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
return false ;
vNewCrv.clear() ;
// sistemo la curva per evitare che i tratti estremi dei link siano lineari ed in tangenza con percorsi ZigZag
if ( ! AdjustZigZagPathTangentLinks( pCrvCompo))
return false ;
// definisco un vettore di intervalli su cui spezzare la curva
struct CopyRange {
double dParS ;
double dParE ;
} ;
vector<CopyRange> vIntervals ;
// parametri
int nCurrTmpProp1 = 0 ;
int nCrvStartBreak = 0 ;
// NB. I tratti di ZigZag che seguono dei contorni ( i Links) hanno TmpProp1 impostata come -1
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
if ( pCrvCompo->GetCurveTempProp( u, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
// ricavo il link
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
if ( IsNull( pCrvLink))
return false ;
pCrvLink->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ;
for ( int uu = u + 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) {
if ( pCrvCompo->GetCurveTempProp( uu, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
if ( ! pCrvLink->AddCurve( pCrvCompo->GetCurve( uu)->Clone()))
return false ;
}
else
break ;
}
if ( ! pCrvLink->IsValid())
return false ;
// ricavo il punto iniziale e finale del Link
Point3d ptS ; pCrvLink->GetStartPoint( ptS) ;
Point3d ptE ; pCrvLink->GetEndPoint( ptE) ;
// parametro per spezzare o meno la curva, togliendo il Link
bool bSplit = false ;
// se il Link collega due tratti alla stessa quota...
if ( abs( ptS.y - ptE.y) < 20 * EPS_SMALL) {
if ( ! AdjustLinkSameY( pCrvLink, bSplit))
return false ;
}
// se il Link collega due tratti a quote diverse
else {
if ( ! AdjustLinkDifferentY( pCrvCompo->GetCurve( u - 1), pCrvCompo->GetCurve( u + pCrvLink->GetCurveCount()),
pCrvLink, PockParams, bSplit))
return false ;
}
// aggiorno
u += pCrvLink->GetCurveCount() ; // così la successiva è corretta nel ciclo
if ( bSplit) {
// se dimensione sopra la tolleranza, allora spezzo la curva e rimuovo il link
CopyRange CR {
1. * nCrvStartBreak, // dParS
1. * ( u - pCrvLink->GetCurveCount()) // dParE
} ;
// se il primo tratto è un link non valido, non considero l'intervallo
if ( ! ( nCrvStartBreak == 0 && abs( CR.dParS - CR.dParE) < 20 * EPS_SMALL))
vIntervals.push_back( CR) ;
nCrvStartBreak = u ;
}
}
}
// se non ho mai trovato link non validi, lascio la curva invariata
if ( vIntervals.empty())
vNewCrv.emplace_back( pCrvCompo->Clone()) ;
else {
// per ogni intervallo...
for ( int i = 0 ; i < int( vIntervals.size()) ; ++ i)
vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( vIntervals[i].dParS, vIntervals[i].dParE))) ;
// ultimo intervallo ( se l'ultimo intervallo è un link non valido, non inserisco)
if ( abs( vIntervals.back().dParE - pCrvCompo->GetCurveCount()) > 20 * EPS_SMALL)
vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nCrvStartBreak, pCrvCompo->GetCurveCount()))) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CalcZigZagInfill( const ISurfFlatRegion* pSfr, double dStep, bool bSmooth, bool bRemoveOverlapLink, ICRVCOMPOPOVECTOR& vCrvCompoRes)
{
// controllo dei parametri
if ( pSfr == nullptr || ! pSfr->IsValid() || dStep < EPS_SMALL)
return false ;
vCrvCompoRes.clear() ;
PocketParams myParams ;
myParams.bSmooth = false ;
myParams.dRad = 0.5 * dStep ;
// vettore con i percorsi a ZigZag
ICRVCOMPOPOVECTOR vpCrvs ;
// ciclo su tutti i chunk della regione
for ( int c = 0 ; c < pSfr->GetChunkCount() ; ++ c) {
// prendo il Chunk
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( c)) ;
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
return false ;
// calcolo i percorsi a ZigZag
if ( ! CalcZigZag( pSfrChunk, myParams, vpCrvs, true))
return false ;
if ( bRemoveOverlapLink) {
ICRVCOMPOPOVECTOR vCrvFinal ;
for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u)
vCrvFinal.emplace_back( Release( vpCrvs[u])) ;
vpCrvs.clear() ;
// sistemo le curve ottenute, aggiustando i Links
for ( int u = 0 ; u < int( vCrvFinal.size()) ; ++ u) {
ICRVCOMPOPOVECTOR vNewCrv ;
if ( ! AdjustLinkZigZagInfill( vCrvFinal[u], myParams, vNewCrv))
return false ;
for ( int uu = 0 ; uu < int( vNewCrv.size()) ; ++ uu)
vpCrvs.emplace_back( Release( vNewCrv[uu])) ;
}
}
// inserisco i percorsi da ritornare
for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) {
if ( bSmooth) {
myParams.bSmooth = true ;
ModifyCurveToSmoothed( vpCrvs[u], myParams, myParams.dRad / 16, myParams.dRad / 16, false) ;
}
vCrvCompoRes.emplace_back( Release( vpCrvs[u])) ;
}
}
return true ;
}