4748bd5ac8
- In CalcPocketing corretta la gestione delle isole aperte mediante MaxOffset.
7597 lines
312 KiB
C++
7597 lines
312 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2023-2023
|
|
//----------------------------------------------------------------------------
|
|
// File : EGkCalcPocketing.h Data : 16.11.23 Versione : 2.5j1
|
|
// Contenuto : Dichiarazione della funzione per calcolare le curve base di Pocketing
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 16.11.23 RE Creazione modulo.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "CurveLine.h"
|
|
#include "CurveArc.h"
|
|
#include "CurveComposite.h"
|
|
#include "BiArcs.h"
|
|
#include "SurfFlatRegion.h"
|
|
#include "GeoConst.h"
|
|
#include "/EgtDev/Include/EGkSfrCreate.h"
|
|
#include "/EgtDev/Include/EGkCurveAux.h"
|
|
#include "/EgtDev/Include/EGkCalcPocketing.h"
|
|
#include "/EgtDev/Include/EGkFilletChamfer.h"
|
|
#include "/EgtDev/Include/EGkDistPointCurve.h"
|
|
#include "/EgtDev/Include/EGkDistPointCurve.h"
|
|
#include "/EgtDev/Include/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/EgtNumUtils.h"
|
|
#include "/EgtDev/Include/EGkChainCurves.h"
|
|
#include <array>
|
|
|
|
using namespace std ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Calcolo delle curve elementari di Pocketing
|
|
//----------------------------------------------------------------------------
|
|
|
|
// variabili d'appoggio ( per non passare troppi parametri tra le funzioni secondarie)
|
|
struct PocketParams {
|
|
int nType = POCKET_SPIRALIN ;
|
|
double dRad = 0. ; // raggio utensile
|
|
double dRad_prec = -1. ; // raggio utensile della lavorazione precedente
|
|
double dSideStep = 0. ; // step
|
|
double dSideStep_prec = -1. ; // step della lavorazione precedente
|
|
double dRadialOffset = 0. ; // offset radiale
|
|
double dRadialOffset_prec = -1. ; // offset radiale precedente
|
|
double dOpenEdgeRad = 0. ; // massimo raggio per estensione lati aperti
|
|
double dOpenMinSafe = 2. ; // 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
|
|
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
|
|
} ;
|
|
|
|
static double TOL_TRAPEZOID = 50 * EPS_SMALL ; // tolleranza per casi a trapezio SpiralPocket
|
|
static int TMP_PROP_SINGLE_CURVE = 3 ; // percorso singola curva aperta ( seguendo i chiusi)
|
|
static int TMP_PROP_CRV_OPEN_SIDE = 2 ; // segmento per entrata da fuori
|
|
static int TMP_PROP_OPT_TRAPEZOID = 4 ; // caso ottimizzato Trapezio
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------------------
|
|
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) ;
|
|
double dPar = PockParams.dRad ;
|
|
if ( ! GetFeedForParam( dPar, PockParams, dFeed))
|
|
return false ;
|
|
pCrv->SetCurveTempParam( u, dFeed, 0) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SimplifyCurveByFeeds( ICurveComposite* pCrvCompo, const PocketParams& PockParam, double dToler)
|
|
{
|
|
// controllo la validità della curva
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// NB. Effettuo il Merge dei tratti di curva che presentano la stessa Feed ( mediante la tolleranza)
|
|
|
|
// curva semplificata
|
|
PtrOwner<ICurveComposite> pCrvSimple( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvSimple))
|
|
return false ;
|
|
|
|
/*
|
|
- effettuo il Merge dei tratti di curva che presentano la stessa Feed
|
|
- imposto la Feed dei tratti piccoli come quella dei tratti precedenti ( per evitare singolarità)
|
|
- effettuo un nuovo Merge per uniformare ulteriormente i nuovi tratti ottenuti
|
|
*/
|
|
|
|
double dCurrTempParam ;
|
|
int nParStart = 0 ;
|
|
double dTempParam ;
|
|
double dLen ;
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
pCrvCompo->GetCurveTempParam( i, dTempParam) ;
|
|
if ( i == 0) {
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
else if ( abs( dCurrTempParam - dTempParam) > dToler) { // se parametri diversi
|
|
// recupero il tratto di curva uniforme
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, i))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
// lo semplifico
|
|
// recupero la lunghezza del tratto e imposto associo la Feed corrente
|
|
// se curva corta -> il parametro di Feed è definito da quello precedente
|
|
pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
|
|
pCrv->GetLength( dLen) ;
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvSimple->AddCurve( Release( pCrv)) ;
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
}
|
|
// ultima parte di curva uniforme ...
|
|
PtrOwner<ICurveComposite> pCrvLast( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ;
|
|
if ( ! IsNull( pCrvLast)) {
|
|
pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
|
|
double dLen ; pCrvLast->GetLength( dLen) ;
|
|
for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u)
|
|
pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvSimple->AddCurve( Release( pCrvLast)) ;
|
|
}
|
|
|
|
// pulisco la curva originale
|
|
pCrvCompo->Clear() ;
|
|
|
|
// scorro i nuovi tratti ottenuti e ripeto il ragionamento
|
|
for ( int i = 0 ; i < pCrvSimple->GetCurveCount() ; ++ i) {
|
|
pCrvSimple->GetCurveTempParam( i, dTempParam) ;
|
|
if ( i == 0) {
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
else if ( abs( dCurrTempParam - dTempParam) > dToler) {
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, i))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
|
|
// recupero la lunghezza di tale curve
|
|
double dLen ; pCrv->GetLength( dLen) ;
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvCompo->AddCurve( Release( pCrv)) ; // aggiungo alla curva finale
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
}
|
|
// ultima parte di curva uniforme ...
|
|
pCrvLast.Set( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, pCrvSimple->GetCurveCount()))) ;
|
|
if ( ! IsNull( pCrvLast)) {
|
|
// semplifico
|
|
pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
|
|
// recupero la lunghezza di tale curva e imposto la Feed
|
|
pCrvLast->GetLength( dLen) ;
|
|
for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u)
|
|
pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvCompo->AddCurve( Release( pCrvLast)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved_offs, bool bIsLink,
|
|
const ICRVCOMPOPOVECTOR& vLinks_done, const ICurveComposite* pCrv_orig,
|
|
const PocketParams& PockParams, double dToll)
|
|
{
|
|
// controllo la validità della curva
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// controllo se il Flag per assegnare la Feed è attivo
|
|
if ( ! PockParams.bCalcFeed)
|
|
return AssignMaxFeed( pCrv, PockParams) ;
|
|
|
|
// imposto di Default la Feed minima per ogni sottocurva
|
|
AssignMinFeed( pCrv, PockParams) ;
|
|
|
|
// se non ho una superificie svuotata, allora esco ( con Feed Minima)
|
|
if ( pSrfRemoved_offs == nullptr || ! pSrfRemoved_offs->IsValid() || pSrfRemoved_offs->GetChunkCount() == 0) {
|
|
// controllo eventuali sovrapposizioni con lati aperti
|
|
AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ;
|
|
return true ;
|
|
}
|
|
|
|
// clono la superificie ( valida)
|
|
PtrOwner<ISurfFlatRegion> pSrf_Removed_offs_clone( pSrfRemoved_offs->Clone()) ;
|
|
if ( IsNull( pSrf_Removed_offs_clone) || ! pSrf_Removed_offs_clone->IsValid() ||
|
|
pSrf_Removed_offs_clone->GetChunkCount() == 0)
|
|
return true ; // esco ( sempre con Feed Minima)
|
|
|
|
// restringo la superificie in maniera appropriata
|
|
if ( bIsLink) { // se curva di Link
|
|
if ( ! pSrf_Removed_offs_clone->Offset( - PockParams.dRad, ICurve::OFF_CHAMFER) ||
|
|
! pSrf_Removed_offs_clone->IsValid() || pSrf_Removed_offs_clone->GetChunkCount() == 0)
|
|
return true ; // esco ( sempre con Feed Minima)
|
|
}
|
|
else if ( PockParams.dRad < PockParams.dSideStep) { // se curva di Offset e raggio utensile < Side step
|
|
if ( ! pSrf_Removed_offs_clone->Offset( PockParams.dSideStep - PockParams.dRad, ICurve::OFF_CHAMFER) ||
|
|
! pSrf_Removed_offs_clone->IsValid() || pSrf_Removed_offs_clone->GetChunkCount() == 0)
|
|
return true ; // esco ( sempre con Feed Minima)
|
|
}
|
|
|
|
// classifico le parti interne alla superificie creata solo dagli Offset
|
|
CRVCVECTOR ccClass ;
|
|
if ( ! pSrf_Removed_offs_clone->GetCurveClassification( *pCrv, EPS_SMALL, ccClass))
|
|
return true ; // esco ( sempre con Feed Minima)
|
|
|
|
// creo la nuova curva con le Feed regolate
|
|
PtrOwner<ICurveComposite> pCrv_new( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrv_new))
|
|
return false ;
|
|
|
|
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
|
|
// recupero il tratto di curva ricavato dalla classificazione
|
|
double dCurrFeed = GetMinFeed( PockParams) ;
|
|
PtrOwner<ICurveComposite> pCrv_sez( ConvertCurveToComposite( pCrv->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
|
|
if ( IsNull( pCrv_sez))
|
|
continue ; // curva troppo piccola, passo alla successiva
|
|
// controllo se interno o fuori alla regione ( regolando quindi la Feed)
|
|
if ( ccClass[i].nClass == CRVC_IN) // se interna alla regione rimossa...
|
|
dCurrFeed = GetMaxFeed( PockParams) ;
|
|
// assegno tale Feed ad ogni sottocurva ricavata dal tratto di classificazione
|
|
for ( int u = 0 ; u < pCrv_sez->GetCurveCount() ; ++ u)
|
|
pCrv_sez->SetCurveTempParam( u, dCurrFeed, 0) ;
|
|
// aggiungo la nuova curva con la Feed regolata a quella finale
|
|
pCrv_new->AddCurve( Release( pCrv_sez)) ;
|
|
}
|
|
|
|
// sotituisco con quanto calcolato
|
|
pCrv->Clear() ;
|
|
pCrv->AddCurve( Release( pCrv_new)) ;
|
|
|
|
// controllo eventuali sovrapposizioni tra la curva attuale e i Link in precedenza percorsi
|
|
for ( int l = 0 ; l < int( vLinks_done.size()) ; ++ l) {
|
|
// classificazione
|
|
IntersCurveCurve intCC( *pCrv, *vLinks_done[l]) ;
|
|
for ( int i = 0 ; i < intCC.GetIntersCount() ; ++ i) { // per ogni tratto ricavato
|
|
IntCrvCrvInfo aInfo ;
|
|
if ( ! intCC.GetIntCrvCrvInfo( i, aInfo) || ! aInfo.bOverlap ||
|
|
AreSamePointApprox( aInfo.IciA[0].ptI, aInfo.IciA[1].ptI))
|
|
continue ; // passo al successivo se non c'è overlap o se i punti coicidono
|
|
// tratto precedente
|
|
PtrOwner<ICurveComposite> pCrv_before( pCrv->Clone()) ;
|
|
if ( ! pCrv_before->TrimEndAtParam( aInfo.IciA[0].dU))
|
|
pCrv_before->Clear() ; // se troppo piccolo, elimino
|
|
// tratto successivo
|
|
PtrOwner<ICurveComposite> pCrv_after( pCrv->Clone()) ;
|
|
if ( ! pCrv_after->TrimStartAtParam( aInfo.IciA[1].dU))
|
|
pCrv_after->Clear() ; // se troppo piccolo, elimino
|
|
// tratto di overlap
|
|
PtrOwner<ICurveComposite> pCrv_overlap( ConvertCurveToComposite( pCrv->CopyParamRange( aInfo.IciA[0].dU, aInfo.IciA[1].dU))) ;
|
|
if ( ! IsNull( pCrv_overlap) && pCrv_overlap->IsValid()) {
|
|
for ( int u = 0 ; u < pCrv_overlap->GetCurveCount() ; ++ u)
|
|
pCrv_overlap->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
|
|
// sostituisco unendo i 3 tratti
|
|
pCrv->Clear() ;
|
|
pCrv->AddCurve( Release( pCrv_before)) ;
|
|
pCrv->AddCurve( Release( pCrv_overlap)) ;
|
|
pCrv->AddCurve( Release( pCrv_after)) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ! bIsLink) { // ---------------- NEL CASO DI OFFSET ----------------
|
|
// creo un intervallo con tutte le Feed Minime
|
|
Intervals IntMinFeed ;
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
double dParam ; pCrv->GetCurveTempParam( u, dParam, 0) ;
|
|
if ( abs( dParam - GetMinFeed( PockParams)) < 5 * EPS_SMALL)
|
|
IntMinFeed.Add( u, u + 1) ;
|
|
}
|
|
double dParS = EPS_SMALL ; double dParE = EPS_SMALL ;
|
|
|
|
// a questo intervallo tolgo tutti i sottotratti di lunghezza inferiore alla tolleranza richiesta
|
|
Intervals IntMinFeed_noSmall ;
|
|
bool bFound = IntMinFeed.GetFirst( dParS, dParE) ;
|
|
while ( bFound) {
|
|
PtrOwner<ICurve> pCrv_Crv( pCrv->CopyParamRange( dParS, dParE)) ;
|
|
double dLen = EPS_SMALL ; pCrv_Crv->GetLength( dLen) ;
|
|
if ( dLen > dToll + 5 * EPS_SMALL)
|
|
IntMinFeed_noSmall.Add( dParS, dParE) ;
|
|
bFound = IntMinFeed.GetNext( dParS, dParE) ;
|
|
}
|
|
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
if ( IntMinFeed_noSmall.IsInside( u + 0.5))
|
|
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
else
|
|
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
else { // ---------------- NEL CASO DI LINK ----------------
|
|
// le curve con lunghezza < dToll vanno modificate
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
double dLen = EPS_SMALL ;
|
|
pCrv->GetCurve( u)->GetLength( dLen) ;
|
|
if ( dLen < dToll + 5 * EPS_SMALL) {
|
|
PtrOwner<ICurveComposite> pCrvCompo( pCrv->Clone()) ;
|
|
if ( ! pCrvCompo->TrimStartAtParam( u))
|
|
pCrvCompo->Clear() ;
|
|
if ( ! pCrvCompo->TrimEndAtLen( dLen + PockParams.dRad * 2))
|
|
pCrvCompo->Clear() ;
|
|
if ( pCrvCompo->IsValid()) {
|
|
bool bFound = false ;
|
|
for ( int uu = 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) {
|
|
double dLenH = EPS_SMALL ; pCrvCompo->GetCurve( uu)->GetLength( dLenH) ;
|
|
if ( dLenH < dToll + 5 * EPS_SMALL)
|
|
continue ;
|
|
// cerco tra le curve successive vicine se ne trovo una con Feed Minima
|
|
double dParam ; pCrvCompo->GetCurveTempParam( uu, dParam, 0) ;
|
|
if ( abs( dParam - GetMinFeed( PockParams)) < 5 * EPS_SMALL) {
|
|
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
bFound = true ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( ! bFound && u != 0) {
|
|
// arrivato qui, so che successivamente non ho curve con Feed Minima vicine
|
|
double dParam ; pCrv->GetCurveTempParam( u - 1, dParam, 0) ;
|
|
if ( abs( dParam - GetMaxFeed( PockParams)) < 5 * EPS_SMALL)
|
|
// se anche la precedente ha Feed Massima -> la curva u-esima è piccola ed Isolata
|
|
pCrv->SetCurveTempParam( u, dParam, 0) ;
|
|
}
|
|
}
|
|
else
|
|
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// controllo eventuali sovrapposizioni con lati aperti
|
|
AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ;
|
|
|
|
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
|
|
IsPointInsideSfr( const ISurfFlatRegion* pSfr, const Point3d& pt, bool& bIsInside)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid() || ! pt.IsValid())
|
|
return false ;
|
|
bIsInside = false ;
|
|
|
|
// ciclo sui chunk della FlatRegion
|
|
for ( int i = 0 ; i < pSfr->GetChunkCount() ; i ++) {
|
|
// verifico se è contenuto nel loop esterno
|
|
PtrOwner<ICurve> pCrv( pSfr->GetLoop( i, 0)) ;
|
|
PolyLine PL ;
|
|
pCrv->ApproxWithLines( 10 * EPS_SMALL, 15, ICurve::APL_STD, PL) ;
|
|
if ( IsPointInsidePolyLine( pt, PL, EPS_SMALL)) {
|
|
bool bInsideHole = false ;
|
|
// verifico se è contenuto in un loop interno
|
|
for ( int j = 1 ; j < pSfr->GetLoopCount( i) ; j ++) {
|
|
PtrOwner<ICurve> pCrv( pSfr->GetLoop( i, j)) ;
|
|
pCrv->ApproxWithLines( 10 * EPS_SMALL, 15, ICurve::APL_STD, PL) ;
|
|
PL.Invert() ;
|
|
if ( IsPointInsidePolyLine( pt, PL, EPS_SMALL))
|
|
bInsideHole = true ;
|
|
}
|
|
// se è contenuto nel loop esterno ma non è contenuto in nessuno dei loop interni allora il punto è interno
|
|
if ( ! bInsideHole) {
|
|
bIsInside = true ;
|
|
return true ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetCoeffLinArc( const ICurveArc* pArc, double dDiam, double& dSubArc)
|
|
{
|
|
|
|
// controllo parametri
|
|
if ( pArc == nullptr || ! pArc->IsValid())
|
|
return false ;
|
|
|
|
// recupero il punto medio
|
|
Point3d ptMid ;
|
|
if ( ! pArc->GetMidPoint( ptMid))
|
|
return false ;
|
|
|
|
// creo l'ombra del tool nel punto medio
|
|
PtrOwner<ICurveArc> pCrvTool( CreateCurveArc()) ;
|
|
pCrvTool->Set( ptMid, Z_AX, dDiam / 2) ;
|
|
if ( ! pCrvTool->IsValid())
|
|
return false ;
|
|
|
|
// effettuo la classificazione
|
|
IntersCurveCurve intCC( *pArc, *pCrvTool) ;
|
|
CRVCVECTOR ccClass ;
|
|
if ( intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) && int( ccClass.size()) == 3 &&
|
|
ccClass[1].nClass == CRVC_IN) {
|
|
// recupero i due punti di intersezione
|
|
Point3d ptS, ptE ;
|
|
pCrvTool->GetPointD1D2( ccClass[1].dParS, ICurve::FROM_MINUS, ptS) ;
|
|
pCrvTool->GetPointD1D2( ccClass[1].dParE, ICurve::FROM_PLUS, ptE) ;
|
|
// vettori uscenti dal centro dell'arco e diretti verso i punti di intersezione
|
|
Vector3d vtS = ptS - pArc->GetCenter() ;
|
|
Vector3d vtE = ptE - pArc->GetCenter() ;
|
|
// angolo tra i due vettori
|
|
double dTheta ;
|
|
vtS.GetAngle( vtE, dTheta) ;
|
|
dTheta = abs( dTheta) ;
|
|
// lunghezza del tratto di arco compreso
|
|
dSubArc = pArc->GetRadius() * DEGTORAD * dTheta ;
|
|
}
|
|
else
|
|
return false ;
|
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetParamForPtStartOnEdge( const ICurve* pCrvCheck, const ICurveComposite* pCrvCompo,
|
|
const ICRVCOMPOPOVECTOR& vOtherCrv, const PocketParams& PockParams, double& dPar)
|
|
{
|
|
|
|
// ==================== INFO ================================================================
|
|
// pCrvCheck -> sottocurva di pCrvCompo su cui cercare il parametro ideale per ingresso
|
|
// vOtherCrv -> tutte le altre curve che non devono essere intersecate durante l'ingresso
|
|
// ( questo vettore è stato riempito con tutti loop della superificie da
|
|
// lavorare, i quali Offset non hanno generato alcuna curva di pCrvCheck )
|
|
// ==========================================================================================
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvCheck == nullptr || ! pCrvCheck->IsValid() ||
|
|
pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// clono le curve
|
|
PtrOwner<ICurve> pCrv( pCrvCheck->Clone()) ;
|
|
PtrOwner<ICurveComposite> pCompo( pCrvCompo->Clone()) ;
|
|
if ( IsNull( pCrv) || IsNull( pCompo))
|
|
return false ;
|
|
ICRVCOMPOPOVECTOR vCrvNoInters ;
|
|
for ( int i = 0 ; i < int( vOtherCrv.size()) ; ++ i)
|
|
vCrvNoInters.emplace_back( ConvertCurveToComposite( vOtherCrv[i]->Clone())) ;
|
|
|
|
// ricavo l'estrusione della curva composita
|
|
Vector3d vtExtr ;
|
|
if ( ! pCompo->GetExtrusion( vtExtr))
|
|
vtExtr = Z_AX ;
|
|
// ricavo il centroide o il punto iniziale della curva composita
|
|
Point3d ptCen ;
|
|
if ( ! pCompo->GetCentroid( ptCen))
|
|
if ( ! pCompo->GetStartPoint( ptCen))
|
|
return false ;
|
|
|
|
// creo un frame locale per creare l'ombra del tool e intersecarla con la curva
|
|
Frame3d frLoc ;
|
|
frLoc.Set( ptCen, vtExtr) ;
|
|
|
|
// porto le curve in questo sistema di riferimento
|
|
pCrv->ToLoc( frLoc) ;
|
|
pCompo->ToLoc( frLoc) ;
|
|
for ( int i = 0 ; i < ( int)vCrvNoInters.size() ; ++i)
|
|
vCrvNoInters[i]->ToLoc( frLoc) ;
|
|
|
|
// fisso il diametro del tool ( più grande per sicurezza )
|
|
double dDiam = ( 2 * PockParams.dRad ) + 2 * PockParams.dRadialOffset ;
|
|
double dLen ;
|
|
if ( ! pCrv->GetLength( dLen))
|
|
return false ;
|
|
double dSubArc = -1 ;
|
|
if ( pCrv->GetType() == CRV_ARC) {
|
|
if ( ! GetCoeffLinArc( GetCurveArc( pCrv), dDiam, dSubArc))
|
|
return false ;
|
|
}
|
|
double dDiv = dSubArc < 0 ? dDiam : dSubArc ;
|
|
double dMaxInt = floor( dLen / dDiv) ;
|
|
|
|
int nDen = 2 ; // intervalli in cui suddivido la curva
|
|
dMaxInt = max( 2.0, dMaxInt) ; // così se entra una volta controllo sempre dParT = 0.5
|
|
|
|
while ( nDen < dMaxInt + EPS_SMALL) { // EPS_SMALL per comprendere l'uguaglianza
|
|
|
|
for ( int i = 1 ; i < nDen ; ++i) {
|
|
|
|
if ( int( i % nDen) == 0)
|
|
continue ;
|
|
|
|
double dNum = 1.0 * i ;
|
|
double dParT = dNum / nDen ;
|
|
Point3d ptTest ;
|
|
Vector3d vtPerpIn ;
|
|
|
|
if ( pCrv->GetPointD1D2( dParT, ICurve::FROM_PLUS, ptTest, &vtPerpIn)) {
|
|
|
|
PtrOwner<ICurveComposite> pCompoClone( pCompo->Clone()) ;
|
|
if ( IsNull( pCompoClone))
|
|
return false ;
|
|
double dU ;
|
|
pCompoClone->GetParamAtPoint( ptTest, dU) ;
|
|
pCompoClone->ChangeStartPoint( dU) ;
|
|
|
|
// se chiuso
|
|
if ( pCrv->GetTempProp() == 0) {
|
|
// se la curva è chiusa sposto il centro verso l'interno.
|
|
// Devo essere sicuro di essere su una curva di Offset interno, altrimenti potrebbe essere valido
|
|
// il punto trovato, però facendo poi il primo offeset tale punto potrebbe diventare un punto
|
|
// di convergenza tra più chunks...
|
|
vtPerpIn.Normalize() ;
|
|
vtPerpIn.Rotate( Z_AX, 0, 1) ; // cos( pi/2) = 0, sin( pi/2) = 1
|
|
vtPerpIn *= (( PockParams.dRad + PockParams.dRadialOffset - 50 * EPS_SMALL)) ;
|
|
ptTest = ptTest + vtPerpIn ;
|
|
|
|
// creo una circonferenza ( ombra del tool ) centrata su ptTest
|
|
PtrOwner<ICurveArc> pCrvToolShape( CreateCurveArc()) ;
|
|
pCrvToolShape->Set( ptTest, Z_AX, 0.5 * dDiam) ;
|
|
if ( ! pCrvToolShape->IsValid())
|
|
return false ;
|
|
|
|
// interseco la curva composita con l'ombra del tool
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pCompoClone, *pCrvToolShape) ;
|
|
|
|
if ( intCC.GetCurveClassification( 0, EPS_SMALL, ccClass)) {
|
|
// se ho solo due intersezioni, controllo di non intersecare isole ( In-Out-In)
|
|
if ( int( ccClass.size() == 3)) {
|
|
bool bOk = true ;
|
|
for ( int j = 0 ; j < ( int)vCrvNoInters.size() && bOk ; ++ j) {
|
|
|
|
IntersCurveCurve intCCI( *vCrvNoInters[j], *pCrvToolShape) ;
|
|
if ( intCCI.GetCurveClassification( 0, EPS_SMALL, ccClass)) {
|
|
|
|
if (( int)ccClass.size() > 1)
|
|
bOk = false ;
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
if ( bOk) {
|
|
dPar = dParT ;
|
|
return true ;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
// se aperto
|
|
else {
|
|
dPar = dParT ;
|
|
return true ;
|
|
}
|
|
}
|
|
}
|
|
|
|
++nDen ;
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
ComputeTrapezoidSpiralLeadInLeadOut( ICurveComposite* pCompo, const Vector3d& vtMainDir, bool bLeadIn,
|
|
const PocketParams& PockParams, bool& bIsOutsideRaw)
|
|
{
|
|
// inizializzazione come interno al grezzo
|
|
bIsOutsideRaw = false ;
|
|
Point3d ptP ;
|
|
Vector3d vtDir ;
|
|
if ( bLeadIn) {
|
|
pCompo->GetStartPoint( ptP) ;
|
|
pCompo->GetStartDir( vtDir) ;
|
|
}
|
|
else {
|
|
pCompo->GetEndPoint( ptP) ;
|
|
pCompo->GetEndDir( vtDir) ;
|
|
}
|
|
|
|
// recupero estrusione della curva
|
|
Vector3d vtExtr ; pCompo->GetExtrusion( vtExtr) ;
|
|
|
|
// recupero info sui lati aperti
|
|
int nPropOpen = pCompo->GetTempProp( 0) ;
|
|
bool bEdgeOpen = (( nPropOpen & ( bLeadIn ? 8 : 2)) > 0) ;
|
|
bool bBase0Open = (( nPropOpen & 1) > 0) ;
|
|
bool bBase1Open = (( nPropOpen & 4) > 0) ;
|
|
|
|
// recupero info per capire se sto considerando un lato aggiuntivo per pulire angoli
|
|
int nIdCrv = ( bLeadIn ? 0 : pCompo->GetCurveCount() - 1) ;
|
|
int nExtraEdge ;
|
|
pCompo->GetCurveTempProp( nIdCrv, nExtraEdge) ;
|
|
|
|
// tento con allungamento se lato inclinato è aperto oppure se sto considerando un lato aggiuntivo per pulire angoli
|
|
if ( bEdgeOpen || nExtraEdge == 1) {
|
|
|
|
Vector3d vtDirP = ( bLeadIn ? -vtDir : vtDir) ;
|
|
Point3d ptNewStart = ptP + vtDirP * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
|
|
pCompo->AddLine( ptNewStart, ! bLeadIn) ;
|
|
pCompo->SetCurveTempProp( bLeadIn ? 0 : pCompo->GetCurveCount() - 1, 2) ;
|
|
bIsOutsideRaw = true ;
|
|
}
|
|
// tento con attacco ruotato di 90° se non sto considerando un tratto aggiuntivo per pulire angoli
|
|
else if (( bBase0Open || bBase1Open) && ! bIsOutsideRaw && nExtraEdge == 0) {
|
|
|
|
Vector3d vtDirO = bBase0Open ? vtDir : - vtDir ;
|
|
vtDirO.Rotate( vtExtr, ( PockParams.bInvert ? -90 : 90)) ;
|
|
|
|
// se vicino al bordo del grezzo
|
|
Point3d ptNewStart = ptP + vtDirO * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
|
|
pCompo->AddLine( ptNewStart, ! bLeadIn) ;
|
|
pCompo->SetCurveTempProp( bLeadIn ? 0 : pCompo->GetCurveCount() - 1, 2) ;
|
|
bIsOutsideRaw = true ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
AdjustTrapezoidSpiralForLeadInLeadOut( ICurveComposite* pCompo, const PocketParams& PockParams)
|
|
{
|
|
// recupero la direzione principale della svuotatura
|
|
Vector3d vtMainDir ;
|
|
for ( int i = 0 ; i < pCompo->GetCurveCount() ; i++) {
|
|
int nProp ;
|
|
if ( pCompo->GetCurveTempProp( i, nProp) && nProp == 0) {
|
|
// se non è lato aggiuntivo per la pulitura angoli recupero la sua direzione
|
|
pCompo->GetCurve( i)->GetStartDir( vtMainDir) ;
|
|
break ;
|
|
}
|
|
}
|
|
|
|
// start point
|
|
bool bStartOutside = false ;
|
|
ComputeTrapezoidSpiralLeadInLeadOut( pCompo, vtMainDir, true, PockParams, bStartOutside) ;
|
|
// end point
|
|
bool bEndOutside = false ;
|
|
ComputeTrapezoidSpiralLeadInLeadOut( pCompo, vtMainDir, false, PockParams, bEndOutside) ;
|
|
|
|
// eventuale inversione della curva per partire sempre dall'esterno del grezzo
|
|
if ( bEndOutside && ! bStartOutside)
|
|
pCompo->Invert() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ExtendPathOnOpenEdge( ICurveComposite* pCrvPath, const PocketParams& PockParams, const Vector3d& vtN,
|
|
Vector3d& vtMidOut, bool bEndOrStart)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvPath == nullptr || ! pCrvPath->IsValid())
|
|
return false ;
|
|
// recupero il punto iniziale della curva
|
|
Point3d ptMidOpen ;
|
|
if ( bEndOrStart) {
|
|
if ( ! pCrvPath->GetEndPoint( ptMidOpen))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! pCrvPath->GetStartPoint( ptMidOpen))
|
|
return false ;
|
|
}
|
|
// calcolo il punto fuori
|
|
Point3d ptOut = ptMidOpen + vtMidOut * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
|
|
// aggiungo al ritorno l'uscita
|
|
pCrvPath->AddLine( ptOut, bEndOrStart) ;
|
|
pCrvPath->SetCurveTempProp( ( bEndOrStart ? pCrvPath->GetCurveCount() - 1 : 0), TMP_PROP_CRV_OPEN_SIDE, 0) ; // nProp 2 per le curve LeadIn/Out
|
|
pCrvPath->SetCurveTempParam( ( bEndOrStart ? pCrvPath->GetCurveCount() - 1 : 0), GetMinFeed( PockParams) , 0) ; // Feed minima
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustContourStart( ICurveComposite* pCompo, const PocketParams& PockParams, const ICRVCOMPOPOVECTOR& vCrvIsl,
|
|
bool bOrder, Point3d ptRef)
|
|
{
|
|
// se cerco semplicemente il tratto lineare chiuso più lungo ...
|
|
if ( ! bOrder) {
|
|
// la priorità è essagnata ai tratti lineari chiusi
|
|
int i = 0 ; // <--- indice della curva corrente
|
|
int nMax = - 1 ;
|
|
double dLenMax = 0 ;
|
|
const ICurve* pCrv = pCompo->GetFirstCurve() ;
|
|
while ( pCrv != nullptr) {
|
|
double dLen ;
|
|
if ( pCrv->GetType() == CRV_LINE && pCrv->GetTempProp() == 0 && pCrv->GetLength( dLen) && dLen > dLenMax) {
|
|
dLenMax = dLen ;
|
|
nMax = i ;
|
|
}
|
|
++ i ;
|
|
pCrv = pCompo->GetNextCurve() ;
|
|
}
|
|
// se non trovato o troppo corto, cerco il tratto chiuso più lungo in generale
|
|
if ( nMax < 0 || dLenMax < 2 * ( 2 * PockParams.dRad)) {
|
|
i = 0 ;
|
|
pCrv = pCompo->GetFirstCurve() ;
|
|
while ( pCrv != nullptr) {
|
|
double dLen ;
|
|
if ( pCrv->GetType() != CRV_LINE && pCrv->GetTempProp() == 0 && pCrv->GetLength( dLen) && dLen > dLenMax) {
|
|
dLenMax = dLen ;
|
|
nMax = i ;
|
|
}
|
|
++ i ;
|
|
pCrv = pCompo->GetNextCurve() ;
|
|
}
|
|
}
|
|
// controllo che il tratto chiuso più lungo trovato sia sufficientemente lungo
|
|
if ( dLenMax < 10 * EPS_SMALL) {
|
|
// se troppo piccolo allora lo imposto aperto
|
|
pCompo->SetCurveTempProp( nMax, 0, 1) ;
|
|
// spezzo la curva nel primo tratto aperto sufficientemente lungo
|
|
for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) {
|
|
double dLen ; pCompo->GetCurve( u)->GetLength( dLen) ;
|
|
if ( dLen > 10 * EPS_SMALL) {
|
|
nMax = u ;
|
|
continue ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// se non trovato, imposto il punto iniziale a mentà del primo tratto
|
|
if ( nMax == -1) {
|
|
pCompo->ChangeStartPoint( 0.5) ;
|
|
return true ;
|
|
}
|
|
pCompo->ChangeStartPoint( nMax + 0.5) ;
|
|
}
|
|
// se invece sto cercando di entrare da un lato chiuso per una svuotatura, allora riordino i lati
|
|
// chiusi a seconda della lunghezza, e a partire dal più lungo cerco un parametro su tale curva che mi consenta
|
|
// un'entrata sufficientemente distante da isole e da altre curve della curva stessa su cui cerco l'entrata ...
|
|
else {
|
|
|
|
// se ho un punto di riferimento, allora come prima cosa controllo il lato chiuso più vicino
|
|
if ( ptRef.IsValid()) {
|
|
double dMinDist = INFINITO ;
|
|
int nIndCrvMinDist = - 1 ;
|
|
for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) {
|
|
DistPointCurve DPC( ptRef, *pCompo->GetCurve( u)) ;
|
|
double dCurrDist = INFINITO ;
|
|
if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) {
|
|
nIndCrvMinDist = u ;
|
|
dMinDist = dCurrDist ;
|
|
}
|
|
}
|
|
// se avessi curve alla stessa distanza ( circa) non controllo le altre, mi accontento della
|
|
// prima curva trovata
|
|
if ( nIndCrvMinDist != -1) {
|
|
// controllo che il lato chiuso sia sufficientemente lungo
|
|
double dLen = 0. ;
|
|
pCompo->GetCurve( nIndCrvMinDist)->GetLength( dLen) ;
|
|
if ( dLen > 2 * PockParams.dRad + 2 * PockParams.dRadialOffset + 500 * EPS_SMALL) {
|
|
pCompo->ChangeStartPoint( nIndCrvMinDist + 0.5) ;
|
|
return true ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// creo un vettore di indici che definisce l'ordine delle curve chiuse in base alla lunghezza
|
|
INTVECTOR vInd ; vInd.reserve( pCompo->GetCurveCount()) ;
|
|
|
|
// vettore di indici già utilizzati
|
|
INTVECTOR vIndUsed ; vIndUsed.reserve( pCompo->GetCurveCount()) ;
|
|
|
|
double dMaxLen = -INFINITO ;
|
|
int nCurrInd = 0 ;
|
|
bool bStop = false ;
|
|
|
|
for ( int c = 0 ; c < pCompo->GetCurveCount() && ! bStop ; ++ c) {
|
|
bStop = true ;
|
|
for ( int i = 0 ; i < pCompo->GetCurveCount() ; ++ i) {
|
|
|
|
int nProp_i ;
|
|
// se la curva i-esima non è già stata considerata ed è chiusa...
|
|
if ( find( vIndUsed.begin(), vIndUsed.end(), i) == vIndUsed.end() &&
|
|
pCompo->GetCurveTempProp( i, nProp_i, 0) && nProp_i == 0) {
|
|
|
|
// creo la curva i-esima
|
|
const ICurve* pCrv_i( pCompo->GetCurve( i)) ;
|
|
double dLen_i ;
|
|
|
|
if ( pCrv_i->GetLength( dLen_i) && dLen_i > dMaxLen) { // se di lunghezza maggiore alla soglia...
|
|
dMaxLen = dLen_i ;
|
|
nCurrInd = i ;
|
|
bStop = false ;
|
|
}
|
|
}
|
|
}
|
|
vIndUsed.push_back( nCurrInd) ;
|
|
vInd.push_back( nCurrInd) ;
|
|
dMaxLen = -INFINITO ;
|
|
}
|
|
|
|
if ( vInd.empty()) {
|
|
// se questa condizione fosse vera allora non sono riuscito ad entrare da nessun lato aperto in precedenza e non
|
|
// ho nemmeno un lato chiuso disponibile per entrare... ( forzo l'entrata a metà del primo lato)
|
|
pCompo->ChangeStartPoint( 0.5) ;
|
|
return true ;
|
|
}
|
|
|
|
// ora che ho riempito il vettore di indici lo scorro e cerco di entrare in questi lati chiusi secondo l'ordine
|
|
bool bOk = false ;
|
|
for ( int i = 0 ; i < int( vInd.size()) && !bOk ; ++ i) {
|
|
const ICurve* pCrv( pCompo->GetCurve( vInd[i])) ;
|
|
double dPar ;
|
|
if ( GetParamForPtStartOnEdge( pCrv, pCompo, vCrvIsl, PockParams, dPar)) {
|
|
pCompo->ChangeStartPoint( vInd[i] + dPar) ;
|
|
bOk = true ;
|
|
}
|
|
}
|
|
|
|
if ( ! bOk) {
|
|
// se non riesco ad entrare da nessun lato chiuso, considerando che in precedenza ho già provato ad
|
|
// entrare da tutti i lati aperti...
|
|
pCompo->ChangeStartPoint( vInd[0] + 0.5) ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetHomogeneousParts( ICurveComposite* pCrvCompo, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vpCrvs)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
vpCrvs.clear() ;
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0) // se curva non valida
|
|
return true ;
|
|
|
|
// la curva ha sia tratti aperti che tratti chiusi, quindi sposto l'inizio a metà del tratto più lungo ( se richiesto)...
|
|
ICRVCOMPOPOVECTOR vCrvNULL ;
|
|
AdjustContourStart( pCrvCompo, PockParams, vCrvNULL, false, P_INVALID) ;
|
|
|
|
// estraggo parti con proprietà uniforme in un vettore ( vpCrvs)
|
|
int nCurrTempProp ;
|
|
int nParStart = 0 ;
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
int nTempProp ;
|
|
pCrvCompo->GetCurveTempProp( i, nTempProp) ;
|
|
if ( i == 0) {
|
|
nCurrTempProp = nTempProp ;
|
|
nParStart = i ;
|
|
}
|
|
else if ( nCurrTempProp != nTempProp) {
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, i))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
pCrv->SetTempProp( nCurrTempProp) ;
|
|
vpCrvs.emplace_back( Release( pCrv)) ;
|
|
nCurrTempProp = nTempProp ;
|
|
nParStart = i ;
|
|
}
|
|
}
|
|
// ultima curva...
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
pCrv->SetTempProp( nCurrTempProp) ;
|
|
vpCrvs.emplace_back( Release( pCrv)) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
CreateSurfFrIncidence( const ICurveComposite* pCrv, const Vector3d& vtTanS_, const Vector3d& vtTanE_,
|
|
const double dRad, ISurfFlatRegion* pSfrInc)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid())
|
|
return false ;
|
|
pSfrInc->Clear() ;
|
|
|
|
// se la curva è chiusa, allora la regione di incidenza è già definita
|
|
if ( pCrv->IsClosed()) {
|
|
PtrOwner<ISurfFlatRegion> pSfrInc_tmp( GetSurfFlatRegionFromFatCurve( pCrv->Clone(), dRad + EPS_SMALL, false, false)) ;
|
|
if ( IsNull( pSfrInc_tmp) || ! pSfrInc_tmp->IsValid())
|
|
return false ;
|
|
pSfrInc->CopyFrom( pSfrInc_tmp) ;
|
|
return ( pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0) ;
|
|
}
|
|
|
|
// creo la curva di Offset esterna ( deve esistere ed essere valida)
|
|
OffsetCurve OffsCrv ;
|
|
PtrOwner<ICurve> pOffsExt( CreateCurveComposite()) ;
|
|
if ( IsNull( pOffsExt) ||
|
|
! OffsCrv.Make( pCrv, dRad, ICurve::OFF_FILLET) ||
|
|
! pOffsExt.Set( OffsCrv.GetLongerCurve()) ||
|
|
IsNull( pOffsExt))
|
|
return false ;
|
|
|
|
// creo la curva di Offset interna ( se esiste e valida...)
|
|
PtrOwner<ICurve> pOffsInt( CreateCurveComposite()) ;
|
|
if ( IsNull( pOffsInt))
|
|
return false ;
|
|
if ( OffsCrv.Make( pCrv, - dRad, ICurve::OFF_FILLET)) {
|
|
PtrOwner<ICurve> pMyCrv( OffsCrv.GetLongerCurve()) ;
|
|
if ( ! IsNull( pMyCrv) && pMyCrv->IsValid())
|
|
pOffsInt.Set( pMyCrv) ;
|
|
}
|
|
|
|
// recupero gli estremi della curva aperta corrente
|
|
Point3d pt1 ; pCrv->GetEndPoint( pt1) ;
|
|
Point3d pt4 ; pCrv->GetStartPoint( pt4) ;
|
|
|
|
// creo la regione dalle curve
|
|
PtrOwner<ICurveComposite> pCrvExtLoopSurfInc( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvExtLoopSurfInc) ||
|
|
! pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ||
|
|
! pCrvExtLoopSurfInc->AddLine( pt1))
|
|
return false ;
|
|
Point3d pt5 ; pCrvExtLoopSurfInc->GetStartPoint( pt5) ;
|
|
if ( ! IsNull( pOffsInt) && pOffsInt->IsValid()) {
|
|
Point3d pt2 ; pOffsInt->GetEndPoint( pt2) ;
|
|
Point3d pt3 ; pOffsInt->GetStartPoint( pt3) ;
|
|
if ( ! pCrvExtLoopSurfInc->AddLine( pt2) ||
|
|
! pOffsInt->Invert() ||
|
|
! pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ||
|
|
! pCrvExtLoopSurfInc->AddLine( pt4) ||
|
|
! pCrvExtLoopSurfInc->AddLine( pt5))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! pCrvExtLoopSurfInc->AddLine( pt4) ||
|
|
! pCrvExtLoopSurfInc->AddLine( pt5))
|
|
return false ;
|
|
}
|
|
// per sicurezza...
|
|
pCrvExtLoopSurfInc->Close() ;
|
|
|
|
// creo la regione
|
|
if ( ! pSfrInc->AddExtLoop( Release( pCrvExtLoopSurfInc)))
|
|
return false ;
|
|
|
|
return ( pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustOpenEdge( const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vCrvIsland,
|
|
const double dParS, const double dParE, const Vector3d& vtTanS,
|
|
const Vector3d& vtTanE, const double dRad, const double dDiamJ,
|
|
const PocketParams& PockParams, ICurveComposite* pCrvBorder)
|
|
{
|
|
/* parametri :
|
|
pCrvCompo -> curva originaria di bordo
|
|
vCrvIsland -> vettore delle isole all'interno di pCrvCompo
|
|
pCrvOpenOffs -> tratto aperto corrente già Offsettato verso l'esterno
|
|
dParS -> parametro sulla pCrvCompo per l'inizio del tratto aperto
|
|
dParE -> parametro sulla pCrvCompo per la fine del tratto aperto
|
|
vtTanS -> vettore di tangenza finale tratto chiuso precedente
|
|
vtTanE -> vettore di tangenza iniziale ( invertito ) del tratto chiuso successivo
|
|
dRad -> raggio di Offset per la regione di incidenza
|
|
dDiamJ -> ampiezza delle curve a fagiolo per estendere la regione di incidenza
|
|
pCrvRes -> curva da restituire ( inizialmente è il tratto aperto sulla pCrvCompo ;
|
|
questa curva sarà l'estensione del lato aperto, adattandosi alla geometria
|
|
dei chiusi ( non posso vedere solo i chiusi adiacenti all'aperto, devo considerare TUTTI i chiusi
|
|
della pCrvCompo
|
|
pStmVol -> Volume di svuotatura
|
|
pStm_Part -> Part corrente
|
|
*/
|
|
|
|
// controllo la validità dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 ||
|
|
pCrvBorder == nullptr || ! pCrvBorder->IsValid() || pCrvBorder->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// definisco la regione di incidenza
|
|
PtrOwner<ISurfFlatRegion> pSfrInc( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrInc) ||
|
|
! CreateSurfFrIncidence( pCrvBorder, vtTanS, vtTanE, dRad + 75 * EPS_SMALL, pSfrInc))
|
|
return false ;
|
|
|
|
// creo un vettore con tutte le curve che potrebbero cadere, in parte, nella regione di incidenza
|
|
ICRVCOMPOPOVECTOR vCrvToCheck ;
|
|
for ( int i = 0 ; i < int( vCrvIsland.size()) ; ++ i)
|
|
vCrvToCheck.emplace_back( vCrvIsland[i]->Clone()) ; // aggiungo le isole
|
|
// se la curva originale non è tutta Aperta -> devo aggiungere anche essa nelle curve da controllare
|
|
bool bIsAllOpen = abs( abs( dParE - dParS) - pCrvCompo->GetCurveCount()) < EPS_SMALL ;
|
|
if ( ! bIsAllOpen) {
|
|
// recupero il tratto di curva prima e dopo dell'aperto corrente
|
|
PtrOwner<ICurveComposite> pCompoOther( ConvertCurveToComposite( pCrvCompo->CopyParamRange( dParE, dParS))) ;
|
|
if ( IsNull( pCompoOther) || ! pCompoOther->IsValid())
|
|
return false ;
|
|
vCrvToCheck.emplace_back( Release( pCompoOther)) ; // aggiungo il bordo
|
|
}
|
|
|
|
// controllo se la curva è un'isola ( normale del piano -Z_AX)
|
|
bool bIsIsland = false ;
|
|
double dArea ; Plane3d plCheck ;
|
|
if ( ! pCrvCompo->GetArea( plCheck, dArea))
|
|
return false ;
|
|
bIsIsland = AreSameVectorEpsilon( plCheck.GetVersN(), - Z_AX, 10 * EPS_SMALL) ;
|
|
|
|
// scorro il vettore creato...
|
|
for ( int c = 0 ; c < int( vCrvToCheck.size()) ; ++ c) {
|
|
// 1) recupero la curva corrente
|
|
PtrOwner<ICurveComposite> pCrvCurr( vCrvToCheck[c]->Clone()) ;
|
|
if ( IsNull( pCrvCurr) || ! pCrvCurr->IsValid())
|
|
return false ;
|
|
// 2) ricavo i tratti con proprietà uniformi ( Aperti/Chiusi )
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
if ( ! GetHomogeneousParts( pCrvCurr, PockParams, vpCrvs))
|
|
return false ;
|
|
// 3) considero solo i tratti chiusi
|
|
for ( int cl = 0 ; cl < int( vpCrvs.size()) ; ++ cl) {
|
|
if ( vpCrvs[cl]->GetTempProp() == 1)
|
|
continue ;
|
|
// 4) effettuo l'Offset della curva di metà dDiamJ
|
|
OffsetCurve OffsCrv ;
|
|
if ( ! OffsCrv.Make( vpCrvs[cl], - dDiamJ * 0.5 - 20 * EPS_SMALL, ICurve::OFF_FILLET))
|
|
return false ;
|
|
// 5) scorro tutte le curve di Offset che si sono formate, prendendo sempre la più lunga tra le rimanenti
|
|
PtrOwner<ICurve> pOffLongestCrv( OffsCrv.GetLongerCurve()) ;
|
|
while ( ! IsNull( pOffLongestCrv)) {
|
|
// 6) creo la regione di incidenza di tale curva ( "Curva a fagiolo")
|
|
PtrOwner<ISurfFlatRegion> pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ * 0.5, false, false)) ;
|
|
if ( IsNull( pSfrBean) || ! pSfrBean->IsValid())
|
|
return false ;
|
|
// inverto se necessario
|
|
if ( AreOppositeVectorApprox( pSfrBean->GetNormVersor(), pSfrInc->GetNormVersor()))
|
|
pSfrBean->Invert() ;
|
|
|
|
// 7) se la "Regione a fagiolo" non influenza la regione di incidenza, la transcuro
|
|
bool bDiscard = false ;
|
|
if ( ! bIsIsland) { // se tratto un loop esterno
|
|
for ( int cI = 0 ; cI < pSfrInc->GetChunkCount() && ! bDiscard ; ++ cI)
|
|
for ( int cB = 0 ; cB < pSfrBean->GetChunkCount() && ! bDiscard ; ++ cB)
|
|
bDiscard = ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrBean, cB) != REGC_INTERS) ;
|
|
}
|
|
else if ( bIsAllOpen && bIsIsland) { // se isola aperta
|
|
for ( int cI = 0 ; cI < pSfrInc->GetChunkCount() && ! bDiscard ; ++ cI) {
|
|
for ( int cB = 0 ; cB < pSfrBean->GetChunkCount() && ! bDiscard ; ++ cB) {
|
|
if ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrBean, cB) == REGC_IN1) {
|
|
for ( int l = 1 ; l < pSfrBean->GetLoopCount( cB) ; ++ l) {
|
|
PtrOwner<ISurfFlatRegion> pSfrTmp( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrTmp) || ! pSfrTmp->AddExtLoop( pSfrBean->GetLoop( cB, l)) ||
|
|
! pSfrTmp->Invert())
|
|
return false ;
|
|
|
|
bDiscard = ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrTmp, 0) == REGC_IN1) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( ! bDiscard) {
|
|
// 8) aggiorno la regione di incidenza
|
|
if ( ! pSfrInc->Add( *pSfrBean))
|
|
return false ;
|
|
}
|
|
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ; // passo al tratto offsettato successivo
|
|
}
|
|
}
|
|
}
|
|
|
|
// ATTENZIONE !
|
|
// L'algoritmo di allargamento presso i lati aperti è Euristico; io mi estendo a seconda della geometria del
|
|
// lato aperto al di fuori del volume di svuotatura... Devo controllare di non rovinare delle zone al di fuori di
|
|
// esso !
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
// recupero la superficie limite
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( PockParams.SfrLimit.Clone()) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
|
|
return false ;
|
|
// piccolo Offset per sicurezza
|
|
pSfrLimit->Offset( 50 * EPS_SMALL, ICurve::OFF_FILLET) ;
|
|
pSfrInc->Subtract( *pSfrLimit) ; // rimuovo la regione limite
|
|
|
|
// può capitare che la regione Limite mi crei più Chunk sulla pSfrInc
|
|
// I chunk da togliere sono tutti quelli che si sono separati dalla pCrvBorder
|
|
// NB. Il mio obiettivo è quello di avere un'unica curva con estremi i due tratti chiusi estremanti
|
|
if ( pSfrInc->GetChunkCount() > 1) { // se ottengo più chunks
|
|
PtrOwner<ISurfFlatRegion> pNewSfrInc( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pNewSfrInc))
|
|
return false ;
|
|
// ricavo i punti iniziali e finali della curva di bordo
|
|
Point3d ptS, ptE ;
|
|
if ( ! pCrvBorder->GetStartPoint( ptS) || ! pCrvBorder->GetEndPoint( ptE))
|
|
return false ;
|
|
// per ogni Chunk ( > 1 )
|
|
for ( int i = 0 ; i < pSfrInc->GetChunkCount() ; ++ i) {
|
|
// bordo esterno
|
|
PtrOwner<ICurveComposite> pCrvEL( ConvertCurveToComposite( pSfrInc->GetLoop( i, 0))) ;
|
|
if ( IsNull( pCrvEL) || ! pCrvEL->IsValid())
|
|
return false ;
|
|
// se estremi in comune, la curva cercata è la seguente
|
|
if ( pCrvEL->IsPointOn( ptS, 300 * EPS_SMALL) && pCrvEL->IsPointOn( ptE, 300 * EPS_SMALL)) {
|
|
// se la nuova regione di incidenza è vuota, aggiorno
|
|
if ( ! pNewSfrInc->IsValid())
|
|
pNewSfrInc.Set( pSfrInc->CloneChunk( i)) ;
|
|
// se la nuova regione di incidenza non è vuota, aggiungo
|
|
else {
|
|
PtrOwner<ISurfFlatRegion> pSfrToAdd( pSfrInc->CloneChunk( i)) ;
|
|
if ( IsNull( pSfrToAdd) || ! pNewSfrInc->Add( *pSfrToAdd))
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
// definisco quindi la nuova regione di incidenza
|
|
if ( ! pNewSfrInc->IsValid())
|
|
return false ;
|
|
pSfrInc.Set( pNewSfrInc) ;
|
|
}
|
|
}
|
|
|
|
// dalla regione di incidenza devo estrarre il tratto di bordo che ha per estremi i lati chiusi
|
|
// ( stando al di fuori, quindi estendendomi all'esterno, della regione da svuotare )
|
|
PtrOwner<ICurveComposite> pCrvNewBorder( ConvertCurveToComposite( pSfrInc->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid())
|
|
return false ;
|
|
// imposto la curva come tutta aperta
|
|
for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u)
|
|
pCrvNewBorder->SetCurveTempProp( u, 1, 0) ;
|
|
|
|
// se la curva originale era tutta aperta... ( il nuovo lato è il loop esterno della regione di incidenza)
|
|
if ( bIsAllOpen) {
|
|
// pulisco la curva originale
|
|
pCrvBorder->Clear() ;
|
|
if ( bIsIsland) { // se isola inserisco il loop interno corretto della regione
|
|
for ( int l = 1 ; l < pSfrInc->GetLoopCount( 0) ; ++ l) {
|
|
// recupero il Loop
|
|
PtrOwner<ICurveComposite> pCrvIntLoop( ConvertCurveToComposite( pSfrInc->GetLoop( 0, l))) ;
|
|
if ( IsNull( pCrvIntLoop) || ! pCrvIntLoop->IsValid())
|
|
return false ;
|
|
// controllo che sia interno alla curva di bordo corrente ( l'isola aperta)
|
|
IntersCurveCurve ICC( *pCrvIntLoop, *pCrvCompo) ;
|
|
CRVCVECTOR ccClass ;
|
|
if ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 &&
|
|
ccClass[0].nClass == CRVC_OUT) {
|
|
// il loop interno corretto va impostato come unico e nuovo loop interno della regione
|
|
pCrvNewBorder->CopyFrom( pCrvIntLoop) ;
|
|
if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid())
|
|
return false ;
|
|
// imposto la curva come tutta aperta ( gira come girava già l'isola)
|
|
for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u)
|
|
pCrvNewBorder->SetCurveTempProp( u, 1, 0) ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
// se bordo esterno inserisco il bordo esterno
|
|
return ( pCrvBorder->AddCurve( Release( pCrvNewBorder))) ;
|
|
}
|
|
|
|
// altrimenti la spezzo il loop della regione di incidenza nei punti iniziali e finali della curva aperta originale
|
|
Point3d ptStart ; pCrvBorder->GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; pCrvBorder->GetEndPoint( ptEnd) ;
|
|
double dUTrimS = -1 ; double dUTrimE = -1 ;
|
|
pCrvNewBorder->GetParamAtPoint( ptStart, dUTrimS, 10000 * EPS_SMALL) ;
|
|
pCrvNewBorder->GetParamAtPoint( ptEnd, dUTrimE, 10000 * EPS_SMALL) ;
|
|
|
|
// pulisco la curva originale
|
|
pCrvBorder->Clear() ;
|
|
if ( ! pCrvBorder->AddCurve( pCrvNewBorder->CopyParamRange( dUTrimS, dUTrimE)) ||
|
|
! pCrvBorder->IsValid()) {
|
|
// se la curva non originaria di lato aperto non era chiusa...
|
|
if ( ! pCrvBorder->IsClosed() && pCrvNewBorder->IsClosed()) {
|
|
// cerco il punto più vicino al ptStart del chiuso sulla nuova curva di bordo aperta
|
|
Point3d ptMinDist ;
|
|
int nFlag ;
|
|
if ( ! DistPointCurve( ptStart, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag))
|
|
return false ;
|
|
// cambio il punto iniziale della border in ptMinDist
|
|
double dUTmp ;
|
|
if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp) ||
|
|
! pCrvNewBorder->ChangeStartPoint( dUTmp))
|
|
return false ;
|
|
// cerco il punto più vicino al ptEnd del chiuso sulla curva di bordo aperta
|
|
if ( ! DistPointCurve( ptEnd, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag))
|
|
return false ;
|
|
// ricavo il parametro sulla curva
|
|
if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp))
|
|
return false ;
|
|
if ( dUTmp < EPS_SMALL)
|
|
dUTmp = pCrvNewBorder->GetCurveCount() - 5 * EPS_SMALL ;
|
|
// copio la curva tra questi due parametri
|
|
PtrOwner<ICurveComposite> pCrvTmp( ConvertCurveToComposite( pCrvNewBorder->CopyParamRange( 0., dUTmp))) ;
|
|
if ( IsNull( pCrvTmp) || ! pCrvTmp->IsValid())
|
|
return false ;
|
|
// raccordo con il chiuso mediante due tratto lineare
|
|
pCrvTmp->AddLine( ptStart, false) ;
|
|
pCrvTmp->AddLine( ptEnd, true) ;
|
|
// restituisco
|
|
pCrvBorder->CopyFrom( pCrvTmp) ;
|
|
return true ;
|
|
}
|
|
pCrvBorder->CopyFrom( pCrvCompo) ;
|
|
}
|
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustContourWithOpenEdges( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vCrvIsl, const PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// recupero i parametri di lavorazione correnti
|
|
double dDiam = PockParams.dRad * 2 ;
|
|
double dOffR = PockParams.dRadialOffset ;
|
|
double dStep = PockParams.dSideStep ;
|
|
|
|
// raggio di riferimento per offset
|
|
double dOutEdge = 0.5 * dDiam ;
|
|
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_ZIGZAG)
|
|
dOutEdge = max( dOutEdge, dDiam - dStep) ;
|
|
double dRad = dOutEdge + dOffR ;
|
|
if ( abs( PockParams.dOpenEdgeRad) > 0 && PockParams.dOpenEdgeRad < dRad)
|
|
dRad = PockParams.dOpenEdgeRad ;
|
|
|
|
// ricavo i tratti omogenei
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
if ( ! GetHomogeneousParts( pCrvCompo, PockParams, vpCrvs))
|
|
return false ;
|
|
// NB. Il primo tratto è la prima metà del tratto aperto più lungo ( se esiste )
|
|
|
|
// Offset esterno per CurveJ
|
|
double dDiamJ = 1.05 * ( dDiam) + 2 * dOffR ;
|
|
if ( abs( PockParams.dOpenEdgeRad) > 0)
|
|
dDiamJ = 100 * EPS_SMALL ;
|
|
// NB. 1.05 serve per eccedere leggermente, in modo che il contro-offset della prima curva di svuotatura presenti
|
|
// dei piccoli archi tra i chiusi e gli aperti ( in modo da svuotare bene lungo il chiuso)
|
|
|
|
// curva finale da restituire
|
|
PtrOwner<ICurveComposite> pCrvCompo_final( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvCompo_final))
|
|
return false ;
|
|
|
|
// parametro iniziale del tratto corrente, sulla curva originale
|
|
double dParS = 0. ;
|
|
// scorro tutti i tratti omogenei nel vettore
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
// recupero la proprietà della curva composita
|
|
int nCurrTmpProp = vpCrvs[i]->GetTempProp() ;
|
|
// aggiorno i parametri del lato aperto corrente sulla curva originale
|
|
double dParE = dParS + 1. * vpCrvs[i]->GetCurveCount() ;
|
|
// se aperta
|
|
if ( nCurrTmpProp == 1) {
|
|
// ricavo la tangenze dei lati chiusi agli estremi di questa curva
|
|
Vector3d vtTanS = V_INVALID ;
|
|
Vector3d vtTanE = V_INVALID ;
|
|
if ( i != 0) {
|
|
if ( ! vpCrvs[i-1]->GetEndDir( vtTanS) || // tangente finale del chiuso precedente
|
|
! vpCrvs[( i + 1) % int( vpCrvs.size())]->GetStartDir( vtTanE)) // tangente iniziale del chiuso successivo...
|
|
return false ;
|
|
vtTanE.Invert() ; // invertita
|
|
}
|
|
if ( ! AdjustOpenEdge( pCrvCompo, vCrvIsl, dParS, dParE, vtTanS, vtTanE, dRad, dDiamJ, PockParams, vpCrvs[i]))
|
|
return false ;
|
|
}
|
|
// assegno le proprietà di lato Aperto/Chiuso per la curva corrente
|
|
for ( int u = 0 ; u < vpCrvs[i]->GetCurveCount() ; ++ u)
|
|
vpCrvs[i]->SetCurveTempProp( u, nCurrTmpProp, 0) ;
|
|
// aggiungo la curva ricavata ( se chiusa -> la copio, se aperta -> copio l'estesa)
|
|
if ( ! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone())) {
|
|
// per sicurezza, se gli estremi non coincidono, creo un piccolo raccordo lineare
|
|
Point3d ptH ; vpCrvs[i]->GetStartPoint( ptH) ;
|
|
if ( ! pCrvCompo_final->AddLine( ptH) ||
|
|
! pCrvCompo_final->SetCurveTempProp( pCrvCompo_final->GetCurveCount() - 1, 1, 0) ||
|
|
! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone()))
|
|
return false ;
|
|
}
|
|
// aggiorno
|
|
dParS = dParE ;
|
|
}
|
|
// non dovrebbe esserci un gap, ma meglio prevenire problemi
|
|
pCrvCompo_final->Close() ;
|
|
// sostituisco
|
|
pCrvCompo->Clear() ;
|
|
pCrvCompo->CopyFrom( pCrvCompo_final) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ChainCompoCurves( ICRVCOMPOPOVECTOR& vCrvCompo)
|
|
{
|
|
/* concatenamento delle curve composite */
|
|
if ( int( vCrvCompo.size() < 2))
|
|
return true ;
|
|
|
|
// controllo validità delle curve
|
|
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i)
|
|
if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid())
|
|
return false ;
|
|
|
|
// preparo i dati per il concatenamento
|
|
bool bFirst = true ;
|
|
Point3d ptNear = ORIG ;
|
|
double dToler = 500 * EPS_SMALL ; // leggermente maggiore della tolleranza sulla superficie limite
|
|
ChainCurves chainC ;
|
|
chainC.Init( false, dToler, int( vCrvCompo.size())) ;
|
|
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
|
|
// recupero i dati della curva necessari al concatenamento e li assegno
|
|
Point3d ptStart, ptEnd ;
|
|
Vector3d vtStart, vtEnd ;
|
|
if ( ! vCrvCompo[i]->GetStartPoint( ptStart) || ! vCrvCompo[i]->GetStartDir( vtStart) ||
|
|
! vCrvCompo[i]->GetEndPoint( ptEnd) || ! vCrvCompo[i]->GetEndDir( vtEnd))
|
|
return false ;
|
|
if ( ! chainC.AddCurve( int( i + 1), ptStart, vtStart, ptEnd, vtEnd))
|
|
return false ;
|
|
// se prima curva, assegno inizio della ricerca
|
|
if ( bFirst) {
|
|
ptNear = ptStart + 10 * EPS_SMALL * vtStart ;
|
|
bFirst = false ;
|
|
}
|
|
}
|
|
|
|
// vettore delle curve composite risultante
|
|
ICRVCOMPOPOVECTOR vCrvCompoChained ;
|
|
|
|
// recupero i percorsi concatenati
|
|
INTVECTOR vnInd ;
|
|
while ( chainC.GetChainFromNear( ptNear, true, vnInd)) {
|
|
// creo una curva composita
|
|
PtrOwner<ICurveComposite> pCrvCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvCompo))
|
|
return false ;
|
|
// recupero le curve semplici e le inserisco nella curva composita
|
|
for ( size_t i = 0 ; i < vnInd.size() ; ++ i) {
|
|
int nId = abs( vnInd[i]) - 1 ;
|
|
bool bInvert = ( vnInd[i] < 0) ;
|
|
// se necessario, la inverto
|
|
if ( bInvert)
|
|
vCrvCompo[nId]->Invert() ;
|
|
// la aggiungo alla curva composta
|
|
if ( ! pCrvCompo->AddCurve( ::Release( vCrvCompo[nId]), true, dToler))
|
|
return false ;
|
|
}
|
|
// aggiorno il nuovo punto vicino
|
|
if ( pCrvCompo->GetCurveCount() > 0) {
|
|
pCrvCompo->GetEndPoint( ptNear) ;
|
|
vCrvCompoChained.emplace_back( Release( pCrvCompo)) ;
|
|
}
|
|
}
|
|
|
|
swap( vCrvCompoChained, vCrvCompo) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustOpenCurves( ICRVCOMPOPOVECTOR& vCrvCompo, const PocketParams& PockParams)
|
|
{
|
|
/*
|
|
sistemazione punto di inizio, segmento per LeadIn, e Feed
|
|
( Se la curva è chiusa, allora si può cambiare il punto d'inizio)
|
|
*/
|
|
|
|
// controllo validità delle curve
|
|
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i)
|
|
if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid())
|
|
return false ;
|
|
|
|
// recupero la superficie limite ( se esiste ed è valida)
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrLimit))
|
|
return false ;
|
|
if ( PockParams.SfrLimit.IsValid()) { // se esiste, allora classifico
|
|
// recupero la superficie limite
|
|
pSfrLimit.Set( PockParams.SfrLimit.Clone()) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
|
|
return false ;
|
|
// effettuo un Offset della regione, tutto ciò che dista più del raggio utensile non la rovina
|
|
pSfrLimit->Offset( PockParams.dRad - 250 * EPS_SMALL, ICurve::OFF_FILLET) ; // c'è un offset radiale, quindi tengo tolleranza alta
|
|
}
|
|
|
|
// per ogni curva signola, aggiungo l'entrata da fuori se ammissibile
|
|
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
|
|
|
|
int nMaxIter = ( vCrvCompo[i]->IsClosed() ? 4 : 1) ;
|
|
double dLen = EPS_SMALL ;
|
|
if ( nMaxIter == 4)
|
|
vCrvCompo[i]->GetLength( dLen) ;
|
|
|
|
if ( PockParams.bInvert)
|
|
vCrvCompo[i]->Invert() ;
|
|
|
|
for ( int j = 0 ; j < nMaxIter ; ++ j) {
|
|
if ( j > 0)
|
|
vCrvCompo[i]->ChangeStartPoint( dLen / nMaxIter) ;
|
|
|
|
// ricavo punto di inizio e vettore tangente
|
|
Point3d ptStart ; vCrvCompo[i]->GetStartPoint( ptStart) ;
|
|
Vector3d vtStart ; vCrvCompo[i]->GetStartDir( vtStart) ;
|
|
// recupero il punto di caduta dell'utensile
|
|
Point3d ptFall = ptStart - vtStart * ( 2 * PockParams.dRad + PockParams.dOpenMinSafe) ;
|
|
// controllo che il tool non sia interno alla regione limite
|
|
bool bAllowLeadIn = ( ! pSfrLimit->IsValid()) ;
|
|
if ( ! bAllowLeadIn) {
|
|
// calcolo l'ombra del tool centrata in ptFall
|
|
PtrOwner<ICurveArc> pCrvToolShape( CreateCurveArc()) ;
|
|
if ( IsNull( pCrvToolShape) || ! pCrvToolShape->Set( ptFall, Z_AX, PockParams.dRad - 100 * EPS_SMALL))
|
|
return false ;
|
|
CRVCVECTOR ccClass ;
|
|
bAllowLeadIn = ( pSfrLimit->GetCurveClassification( *pCrvToolShape, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) ;
|
|
// se esiste intersezione con la limite Offsettata, provo a ruotare di 90° il tratto lineare
|
|
if ( ! bAllowLeadIn) {
|
|
vtStart.Rotate( Z_AX, PockParams.bInvert ? - ANG_RIGHT : ANG_RIGHT) ;
|
|
ptFall = ptStart + vtStart * ( 2 * PockParams.dRad + PockParams.dOpenMinSafe) ;
|
|
if ( ! pCrvToolShape->Set( ptFall, Z_AX, PockParams.dRad - 100 * EPS_SMALL))
|
|
return false ;
|
|
ccClass.clear() ;
|
|
bAllowLeadIn = ( pSfrLimit->GetCurveClassification( *pCrvToolShape, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) ;
|
|
}
|
|
}
|
|
if ( bAllowLeadIn) { // se ammessa entrata da fuori
|
|
vCrvCompo[i]->AddLine( ptFall, false) ;
|
|
vCrvCompo[i]->SetCurveTempProp( 0, TMP_PROP_CRV_OPEN_SIDE, 0) ; // nProp 2 per le curve LeadIn/Out
|
|
vCrvCompo[i]->SetCurveTempParam( 0, GetMinFeed( PockParams) , 0) ; // Feed massima
|
|
break ; // interrompo a prescindere l'iterazione per i punti iniziali
|
|
}
|
|
}
|
|
}
|
|
|
|
// imposto le Feed per tali curve
|
|
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
|
|
/*
|
|
Idea : Feed proporzionale al minimo Offset per annullare la regione
|
|
-> Massimo parametro sui bisettori di VORONOI
|
|
*/
|
|
|
|
// recupero il MaxOffset per la curva
|
|
double dMaxOffs = vCrvCompo[i]->GetTempParam( 0) ;
|
|
// calcolo la Feed proporzionale a tale Offset
|
|
double dFeed ;
|
|
GetFeedForParam( 2 * dMaxOffs, PockParams, dFeed) ;
|
|
AssignCustomFeed( vCrvCompo[i], PockParams, dFeed) ;
|
|
// imposto il flag di curva singola
|
|
vCrvCompo[i]->SetTempProp( TMP_PROP_SINGLE_CURVE, 0) ; //nProp per curva singola
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPocketCurvesByCloseEdges( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvCompoRes, bool& bAllRemoved)
|
|
{
|
|
// controllo parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
bAllRemoved = false ;
|
|
|
|
// controllo se la superficie si annulla con un controOffset del raggio utensile
|
|
double dMaxOffs ;
|
|
pSfrChunk->GetMaxOffset( dMaxOffs) ;
|
|
if ( dMaxOffs > PockParams.dRad + 200 * EPS_SMALL)
|
|
return true ;
|
|
|
|
/*
|
|
NB. Si poteva calcolare la Fat curve del raggio utensile dell'Offset di tutti i chiusi e vedere
|
|
se l'unione di queste regioni copriva interamente il Chunk attuale... così facendo rischio di
|
|
trascurare delle parti che spariscono facendo il primo Offset dei chiusi; il controllo va fatto
|
|
quindi con un contro-offset della regione complessiva
|
|
*/
|
|
|
|
// controllo se il Chunk ha delle isole
|
|
bool bHasIslands = ( pSfrChunk->GetLoopCount( 0) > 1) ;
|
|
|
|
// recupero il Loop esterno
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
// recupero i tratti con proprietà uniformi
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
GetHomogeneousParts( pCrvExtLoop, PockParams, vpCrvs) ;
|
|
if ( vpCrvs.size() > 1) { // unisco il primo e l'ultimo se estremi compatibili
|
|
// NB. GetHomogeneousParts() cambia il punto di inizio nel lato chiuso più lungo ( se presente)
|
|
Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ;
|
|
Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ;
|
|
if ( AreSamePointApprox( ptS, ptE)) {
|
|
vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ;
|
|
vpCrvs.erase( vpCrvs.end() - 1) ;
|
|
}
|
|
}
|
|
// controllo se il loop Esterno è uniforme ( quindi tutto chiuso o tutto aperto)
|
|
bool bExtAllClose = false ;
|
|
bool bExtAllOpen = false ;
|
|
if ( int( vpCrvs.size()) == 1) {
|
|
if ( vpCrvs[0]->GetTempProp( 0) == 0)
|
|
bExtAllClose = true ;
|
|
else
|
|
bExtAllOpen = true ;
|
|
}
|
|
// NB. Se regione estena tutta chiusa o aperta e senza isole... allora non faccio nulla ( es caso trapezi)
|
|
// Se tutta chiusa -> Chunk non svuotabile, il contro-offset del raggio utensile annulla la regione
|
|
// Se tutta aperta -> La regione è definita mediante Offset esterno del contorno aperto
|
|
if ( ( bExtAllClose || bExtAllOpen) && ! bHasIslands)
|
|
return true ;
|
|
// Se il contorno esterno è misto -> non devo avere isole
|
|
if ( ! bExtAllClose && ! bExtAllOpen && bHasIslands)
|
|
return true ;
|
|
// Se il contorno esterno è tutto chiuso e ho più di un'isola non faccio nulla
|
|
if ( bExtAllClose && pSfrChunk->GetLoopCount( 0) > 2)
|
|
return true ;
|
|
|
|
// scorro i chiusi e sottraggo la regione da loro creata mediante l'utensile
|
|
// NB. Queste curve sono orientate in maniera corretta
|
|
ICRVCOMPOPOVECTOR vCrvCompoRes_tmp ; // salvo il vettore di curve offsettate del raggio utensile
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
if ( vpCrvs[i]->GetTempProp( 0) == 0) { // se tratto chiuso...
|
|
OffsetCurve OffsCrv ; // con offset verso l'interno
|
|
if ( OffsCrv.Make( vpCrvs[i], - PockParams.dRad - PockParams.dRadialOffset, ICurve::OFF_FILLET)) {
|
|
PtrOwner<ICurve> pOffLongestCrv( OffsCrv.GetLongerCurve()) ;
|
|
while ( ! IsNull( pOffLongestCrv)) {
|
|
// salvo la curva corrente
|
|
vCrvCompoRes_tmp.emplace_back( ConvertCurveToComposite( Release( pOffLongestCrv))) ;
|
|
// passo alla successiva
|
|
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Casi gestiti :
|
|
1) La curva esterna e mista e non ho isole ( ho già il percorso)
|
|
2) La curva esterna è tutta aperta e le isole sono tutte chiuse
|
|
3) La curva esterna è tutta chiusa e ho una sola isola aperta ( ho già il percorso)
|
|
|
|
*/
|
|
|
|
// 1)
|
|
if ( ! bExtAllClose && ! bExtAllOpen)
|
|
;
|
|
else {
|
|
// scorro tutte le isole
|
|
bool bValidIslands = true ; // flag per isole tutte uniformi
|
|
for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) && bValidIslands ; ++ i) {
|
|
// controllo uniformità dell'isola
|
|
int nCurrTmpProp = -1 ;
|
|
int nPrecTmpProp = -1 ;
|
|
bool bIsMixed = false ;
|
|
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ;
|
|
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
|
|
return false ;
|
|
for ( int u = 0 ; u < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ u) {
|
|
pCrvIsl->GetCurveTempProp( u, nCurrTmpProp, 0) ;
|
|
bIsMixed = ( u != 0 && nCurrTmpProp != nPrecTmpProp) ;
|
|
nPrecTmpProp = nCurrTmpProp ;
|
|
}
|
|
// se proprità non uniformi -> tutta chiusa ( isole non uniformi non sono definite)
|
|
if ( bIsMixed) {
|
|
for ( int u = 0 ; u < pCrvIsl->GetCurveCount() ; ++ u)
|
|
pCrvIsl->SetTempProp( u, 0) ;
|
|
nCurrTmpProp = 0 ; // aggiorno il Flag
|
|
}
|
|
// 2) e 3)
|
|
bValidIslands = ( ( nCurrTmpProp == 0 && bExtAllOpen) ||
|
|
( nCurrTmpProp == 1 && bExtAllClose)) ;
|
|
}
|
|
if ( ! bValidIslands)
|
|
return true ;
|
|
// nel caso 2) devo inserire le curve
|
|
// creo una regione formata solo dalla isole
|
|
PtrOwner<ISurfFlatRegion> pSrfIslands( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfIslands))
|
|
return false ;
|
|
for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) ; ++ i)
|
|
pSrfIslands->AddExtLoop( pSfrChunk->GetLoop( 0, i)) ;
|
|
pSrfIslands->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET) ;
|
|
for ( int c = 0 ; c < pSrfIslands->GetChunkCount() ; ++ c)
|
|
for ( int l = 0 ; l < pSrfIslands->GetLoopCount( l) ; ++ l)
|
|
vCrvCompoRes_tmp.emplace_back( ConvertCurveToComposite( pSrfIslands->GetLoop( c, l))) ;
|
|
}
|
|
|
|
// se non ho ottenuto curve, allora ho finito
|
|
if ( vCrvCompoRes_tmp.empty())
|
|
return true ;
|
|
|
|
bAllRemoved = true ;
|
|
|
|
// recupero la superficie limite ( se esiste ed è valida)
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrLimit))
|
|
return false ;
|
|
|
|
// per tutte le curve inserite, devo tenere solamente quelle esterne alla superficie limite
|
|
if ( PockParams.SfrLimit.IsValid()) { // se esiste, allora classifico
|
|
// recupero la superficie limite
|
|
pSfrLimit.Set( PockParams.SfrLimit.Clone()) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
|
|
return false ;
|
|
// effettuo un Offset della regione, tutto ciò che dista più del raggio utensile non la rovina
|
|
pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset - 250 * EPS_SMALL, ICurve::OFF_FILLET) ; // c'è un offset radiale, quindi tengo tolleranza alta
|
|
ICRVCOMPOPOVECTOR vCrvCompoRes_tmp_Splitted ;
|
|
for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) {
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrLimit->GetCurveClassification( *vCrvCompoRes_tmp[i], EPS_SMALL, ccClass)) {
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
if ( ccClass[j].nClass == CRVC_OUT) {
|
|
PtrOwner<ICurveComposite> pCrvRes( ConvertCurveToComposite( vCrvCompoRes_tmp[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
|
|
if ( ! IsNull( pCrvRes) && pCrvRes->IsValid())
|
|
vCrvCompoRes_tmp_Splitted.emplace_back( Release( pCrvRes)) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se non ho curve, allora esco
|
|
if ( vCrvCompoRes_tmp_Splitted.empty())
|
|
return true ;
|
|
// altrimenti aggiorno il vettore di curve con quelle solo esterne alla regione limite
|
|
swap( vCrvCompoRes_tmp, vCrvCompoRes_tmp_Splitted) ;
|
|
}
|
|
|
|
// salvo come primo tempParam l'offset massimo della regione ( servirà per la Feed)
|
|
for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) {
|
|
vCrvCompoRes_tmp[i]->SetTempParam( dMaxOffs, 0) ;
|
|
vCrvCompoRes.emplace_back( Release( vCrvCompoRes_tmp[i])) ; // aggiungo la curva al vettore
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ModifySurfByOpenEdges( ISurfFlatRegion* pSfr, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes,
|
|
bool& bSkipPocket)
|
|
{
|
|
// controllo parametri :
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return true ; // <- se superficie non valida, allora non ho niente da impostare sui suoi lati
|
|
bSkipPocket = false ;
|
|
|
|
// 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 ;
|
|
|
|
int nChunkOneCurve = 0 ; // numero di chunk rimovibili mediante una sola curva di Offset
|
|
int nChunk = pSfr->GetChunkCount() ; // numero complessivo di Chunk da svuotare
|
|
|
|
// per ogni Chunck della superificie ottenuta...
|
|
for ( int c = 0 ; c < nChunk ; ++ c) {
|
|
|
|
// 0) controllo se seguendo i chiusi posso svuotare l'interno chunk
|
|
/*
|
|
nel caso in cui il chunk c-esimo può essere svuotata da una o più curve che seguono
|
|
il/i tratto/i chiuso/i, queste vengono salvate direttamente nei percorsi di svuotatura.
|
|
Il chuck c-esimo verrà poi ignorato ( quindi nessuna estensione presso gli aperti)
|
|
*/
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( c)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
bool bIsAllRemoved = false ;
|
|
GetPocketCurvesByCloseEdges( pSfrChunk, PockParams, vCrvCompoRes, bIsAllRemoved) ;
|
|
if ( bIsAllRemoved) { // se ho rimosso tutto, allora passo la chunk successivo
|
|
++ nChunkOneCurve ; // aumento il contatore
|
|
continue ; // ignoro il chunk c-esimo
|
|
}
|
|
|
|
// vettore delle isole che userò ( le isole aperte piccole sono trascurate)
|
|
ICRVCOMPOPOVECTOR vCrvToTIsland ;
|
|
// Flag per sapere se c'è stata almeno una modifica in un loop
|
|
bool bIsChunkModified = false ;
|
|
|
|
// 1) ricavo il Loop esterno
|
|
PtrOwner<ICurveComposite> pCrvEL( ConvertCurveToComposite( pSfr->GetLoop( c, 0))) ;
|
|
if ( IsNull( pCrvEL) || ! pCrvEL->IsValid())
|
|
return false ;
|
|
|
|
// 2) creo un vettore di curve con le isole del Chunk
|
|
ICRVCOMPOPOVECTOR vCrvIsl ;
|
|
for ( int l = 1 ; l < pSfr->GetLoopCount( c) ; ++ l) {
|
|
PtrOwner<ICurveComposite> pCrvIL( ConvertCurveToComposite( pSfr->GetLoop( c, l))) ;
|
|
if ( IsNull( pCrvIL) || ! pCrvIL->IsValid())
|
|
return false ;
|
|
vCrvIsl.emplace_back( Release( pCrvIL)) ;
|
|
}
|
|
|
|
// 3) se la curva esterna presenta dei lati aperti -> devo modificarla
|
|
bool bSomeOpen = false ;
|
|
int nProp0 = -1 ;
|
|
for ( int u = 0 ; u < pCrvEL->GetCurveCount() && ! bSomeOpen ; ++ u)
|
|
if ( pCrvEL->GetCurveTempProp( u, nProp0, 0) && nProp0 == 1)
|
|
bSomeOpen = true ;
|
|
if ( bSomeOpen) { // se trovo dei lati aperti
|
|
// 3.1) sistemo la superificie
|
|
if ( ! AdjustContourWithOpenEdges( pCrvEL, vCrvIsl, PockParams))
|
|
return false ;
|
|
bIsChunkModified = true ; // la curva è stata modificata
|
|
}
|
|
|
|
// 4) Controllo i bordi delle isole ottenute
|
|
// NB. L'isola può essere tutta aperta o tutta chiusa ( se non uniforme, passo a tutta chiusa )
|
|
// La definizione di isola con proprietà non uniformi non è definita...
|
|
for ( int i = 0 ; i < int( vCrvIsl.size()) ; ++ i) {
|
|
// controllo uniformità
|
|
int nCurrTmpProp = -1 ;
|
|
int nPrecTmpProp = -1 ;
|
|
bool bIsMixed = false ;
|
|
PtrOwner<ICurveComposite> pCrvIsl( vCrvIsl[i]->Clone()) ;
|
|
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
|
|
if ( bIsMixed) {
|
|
for ( int u = 0 ; u < pCrvIsl->GetCurveCount() ; ++ u)
|
|
pCrvIsl->SetTempProp( u, 0) ;
|
|
nCurrTmpProp = 0 ; // aggiorno il Flag
|
|
bIsChunkModified = true ; // la curva è stata modificata
|
|
}
|
|
// se curva tutta aperta, controllo se l'isola può essere trascurata
|
|
if ( nCurrTmpProp == 1) {
|
|
// calcolo il massimo Offset
|
|
double dMaxOffs ;
|
|
CalcCurveLimitOffset( *pCrvIsl, dMaxOffs) ;
|
|
if ( dMaxOffs < 2 * PockParams.dRad + 2 * PockParams.dRadialOffset)
|
|
continue ;
|
|
// altrimenti, estendo l'isola aperta ( considero il loop esterno come "isola" per l'estensione)
|
|
ICRVCOMPOPOVECTOR vCrvExtLoop ; vCrvExtLoop.emplace_back( pCrvEL->Clone()) ;
|
|
if ( ! AdjustContourWithOpenEdges( pCrvIsl, vCrvExtLoop, PockParams))
|
|
return false ;
|
|
bIsChunkModified = true ;
|
|
}
|
|
vCrvToTIsland.emplace_back( pCrvIsl->Clone()) ; // entra nelle curve della regione da svuotare
|
|
}
|
|
|
|
// 6) Se c'è stata almeno una modifica di lato aperto al chunk (c-esimo), devo ricreare il Chunk
|
|
// nuovi loop ricavati ( Chunk dopo Chunk creo la superficie finale)
|
|
if ( bIsChunkModified) {
|
|
|
|
SurfFlatRegionByContours SfrBC ; // per sicurezza analizzo ancora i Loop
|
|
pCrvEL->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
SfrBC.AddCurve( Release( pCrvEL)) ; // <--- Loop esterno
|
|
for ( int i = 0 ; i < int( vCrvToTIsland.size()) ; ++ i) {
|
|
vCrvToTIsland[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
SfrBC.AddCurve( Release( vCrvToTIsland[i])) ; // <--- isole "valide"
|
|
}
|
|
|
|
// ricavo il nuovo Chunk
|
|
PtrOwner<ISurfFlatRegion> pNewChunk( SfrBC.GetSurf()) ;
|
|
if ( IsNull( pNewChunk) || ! pNewChunk->IsValid())
|
|
return false ;
|
|
|
|
// aggiungo il Chunk alla superficie finale
|
|
if ( pSrfFinal->GetChunkCount() == 0)
|
|
pSrfFinal.Set( pNewChunk) ;
|
|
else
|
|
if ( ! pSrfFinal->Add( *pNewChunk))
|
|
return false ;
|
|
}
|
|
// se il Chunk c-esimo non è mai stato modificato...
|
|
else {
|
|
// aggiungo il Chunk alla superficie finale
|
|
if ( pSrfFinal->GetChunkCount() == 0)
|
|
pSrfFinal.Set( pSfr->CloneChunk( c)) ;
|
|
else {
|
|
PtrOwner<ISurfFlatRegion> pSfrToAdd( pSfr->CloneChunk( c)) ;
|
|
if ( IsNull( pSfrToAdd) || ! pSrfFinal->Add( *pSfrToAdd))
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// se ho ottenuto delle curve singole, allora le provo a concatenare
|
|
// ( tenendo solo le curve al di fuori della superficie limite ( con Offset del raggio del tool)
|
|
// potrei doverle riconcatenare
|
|
if ( ! ChainCompoCurves( vCrvCompoRes))
|
|
return false ;
|
|
// determino ora il punto di inizio, il tratto per il LeadIn e la Feed delle curve singole
|
|
if ( ! AdjustOpenCurves( vCrvCompoRes, PockParams))
|
|
return false ;
|
|
|
|
// se ho rimosso tutti i Chunk con una singola curva...
|
|
bSkipPocket = ( nChunk == nChunkOneCurve) ;
|
|
if ( bSkipPocket)
|
|
return true ;
|
|
|
|
// restituisco la superficie aggiornata ricavata
|
|
if ( ! pSrfFinal->IsValid())
|
|
return false ;
|
|
pSfr->Clear() ;
|
|
pSfr->CopyFrom( pSrfFinal) ;
|
|
|
|
return ( pSrfFinal->IsValid() && pSrfFinal->GetChunkCount() > 0) ;
|
|
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// ------------- SCELTA DEL PUNTO INIZIALE -----------------------------------
|
|
// ***************************************************************************
|
|
//----------------------------------------------------------------------------
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetParamOnOpenSide( const ICurveComposite* pCompo, const ICRVCOMPOPOVECTOR& vOtherCrv,
|
|
const Frame3d& frPocket, const PocketParams& PockParams, Point3d& ptMid, Vector3d& vtMidOrt)
|
|
{
|
|
// recupero il vettore estrusione
|
|
Vector3d vtExtr = Z_AX ;
|
|
pCompo->GetExtrusion( vtExtr) ;
|
|
// verifico se tutti i lati sono aperti
|
|
bool bAllOpen = true ;
|
|
const ICurve* pMyCrv = pCompo->GetFirstCurve() ;
|
|
while ( pMyCrv != nullptr) {
|
|
if ( pMyCrv->GetTempProp() != 1) {
|
|
bAllOpen = false ;
|
|
break ;
|
|
}
|
|
pMyCrv = pCompo->GetNextCurve() ;
|
|
}
|
|
// salvo il punto iniziale e la direzione d'uscita del primo lato aperto valido che trovo
|
|
// NB. Il primo valido non significa il migliore, potrebbe infatti essere il lato aperto più lungo
|
|
// ma essere all'interno del grezzo... cerco di dare priorità ai lati sul grezzo
|
|
|
|
// flag per la presenza di un primo lato aperto valido per entrare
|
|
bool bFirstOpenValid = false ;
|
|
Point3d ptFirstOpenValid = P_INVALID ;
|
|
Vector3d vtFirstOpenValid = V_INVALID ;
|
|
// richiedo lunghezza superiore a diametro utensile più doppio offset radiale
|
|
double dRefLen = ( bAllOpen ? 0 : ( 2 * PockParams.dRad) + 2 * PockParams.dRadialOffset - EPS_SMALL) ;
|
|
double dMaxLen = dRefLen ;
|
|
// ciclo sulle singole curve
|
|
bool bFound = false ;
|
|
const ICurve* pPrevCrv = pCompo->GetLastCurve() ;
|
|
double dLenPrev = 0 ;
|
|
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == 1)
|
|
pPrevCrv->GetLength( dLenPrev) ;
|
|
const ICurve* pCrv = pCompo->GetFirstCurve() ;
|
|
while ( pCrv != nullptr) {
|
|
// analizzo la curva successiva
|
|
const ICurve* pNextCrv = pCompo->GetNextCurve() ;
|
|
bool bNextOk = ( pNextCrv != nullptr) ;
|
|
if ( ! bNextOk)
|
|
pNextCrv = pCompo->GetFirstCurve() ;
|
|
double dLenNext = 0 ;
|
|
if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == 1)
|
|
pNextCrv->GetLength( dLenNext) ;
|
|
// verifico la curva corrente
|
|
if ( pCrv->GetTempProp() == 1) {
|
|
// contributo dalle entità adiacenti (se non tutte aperte)
|
|
double dLenAgg = 0 ;
|
|
if ( ! bAllOpen) {
|
|
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == 1) {
|
|
Vector3d vtPrevEnd ; pPrevCrv->GetEndDir( vtPrevEnd) ;
|
|
Vector3d vtStart ; pCrv->GetStartDir( vtStart) ;
|
|
dLenAgg += max( 0.4, vtPrevEnd * vtStart) * dLenPrev ;
|
|
}
|
|
if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == 1) {
|
|
Vector3d vtEnd ; pCrv->GetEndDir( vtEnd) ;
|
|
Vector3d vtNextStart ; pNextCrv->GetStartDir( vtNextStart) ;
|
|
dLenAgg += max( 0.4, vtEnd * vtNextStart) * dLenNext ;
|
|
}
|
|
}
|
|
// entità corrente
|
|
double dLen = 0 ;
|
|
if ( pCrv->GetLength( dLen)) {
|
|
const double LEN_TOL = 1 ;
|
|
// se di lunghezza praticamente uguale
|
|
if ( bFound && dLen + dLenAgg > dRefLen && abs( dLen + dLenAgg - dMaxLen) < LEN_TOL) {
|
|
Point3d ptTest ;
|
|
pCrv->GetMidPoint( ptTest) ;
|
|
if ( ( PockParams.bAboveHead && ptTest.z > ptMid.z + 100 * EPS_SMALL) ||
|
|
( ! PockParams.bAboveHead && ptTest.z < ptMid.z - 100 * EPS_SMALL) ||
|
|
( abs( ptTest.z - ptMid.z) < 100 * EPS_SMALL && ptTest.y < ptMid.y - 100 * EPS_SMALL)) {
|
|
dMaxLen = max( dMaxLen, dLen + dLenAgg) ;
|
|
ptMid = ptTest ;
|
|
// vettore ortogonale verso l'esterno (ruotato -90deg attorno a estrusione)
|
|
pCrv->GetMidDir( vtMidOrt) ;
|
|
vtMidOrt.Rotate( vtExtr, 0, -1) ;
|
|
}
|
|
}
|
|
// se più lunga ( o non già trovata)
|
|
else if ( dLen + dLenAgg > dMaxLen || !bFound) {
|
|
dMaxLen = dLen + dLenAgg ;
|
|
double dParIn ;
|
|
// cerco il parametro di tale curva (0 < dParIn < 1) migliore per l'entrata
|
|
if ( GetParamForPtStartOnEdge( pCrv, pCompo, vOtherCrv, PockParams, dParIn)) {
|
|
pCrv->GetPointD1D2( dParIn, ICurve::FROM_PLUS, ptMid) ;
|
|
PtrOwner<ICurveComposite> pCompoClone( pCompo->Clone()) ;
|
|
if ( IsNull( pCompoClone))
|
|
return false ;
|
|
double dU ;
|
|
pCompoClone->GetParamAtPoint( ptMid, dU) ;
|
|
pCompoClone->ChangeStartPoint( dU) ;
|
|
pCompoClone->GetStartDir( vtMidOrt) ;
|
|
vtMidOrt.Rotate( vtExtr, 0, -1) ;
|
|
// salvo l'indice di questo lato aperto se si tratta del primo valido
|
|
if ( ! bFirstOpenValid) {
|
|
bFirstOpenValid = true ;
|
|
ptFirstOpenValid = ptMid ;
|
|
vtFirstOpenValid = vtMidOrt ;
|
|
}
|
|
// controllo se questa quantità è effettivamente fuori dal grezzo
|
|
// ( privilegio i lati aperti che sono effettivamente sul bordo del grezzo)
|
|
// punto iniziale nel sistema di riferimento globale
|
|
Point3d ptStart ; pCompoClone->GetStartPoint( ptStart) ;
|
|
ptStart.ToGlob( frPocket) ;
|
|
// vettore d'uscita nel sistema di riferimento globale
|
|
Vector3d vtMidOut = vtMidOrt ;
|
|
vtMidOut.ToGlob( frPocket) ;
|
|
}
|
|
}
|
|
dLenPrev = dLen ;
|
|
}
|
|
}
|
|
else
|
|
dLenPrev = 0 ;
|
|
// vado alla successiva
|
|
pPrevCrv = pCrv ;
|
|
pCrv = ( bNextOk ? pNextCrv : nullptr) ;
|
|
}
|
|
|
|
// se non ho trovato un lato aperto valido fuori dal grezzo, ma ho trovato un lato aperto
|
|
// generico accettabile
|
|
if ( ! bFound && bFirstOpenValid) {
|
|
ptMid = ptFirstOpenValid ;
|
|
vtMidOrt = vtFirstOpenValid ;
|
|
bFound = true ;
|
|
}
|
|
|
|
return bFound ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SetSpecialPtStartForOpenEdges( const ICurveComposite* pCrvOrig, const Frame3d& frPocket,
|
|
const PocketParams& PockParams, ICurveComposite* pCrvCompo, Point3d& ptStart,
|
|
Vector3d& vtMidOut, bool& bMidOut)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvOrig == nullptr || ! pCrvOrig->IsValid() ||
|
|
! frPocket.IsValid() ||
|
|
pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// cerco il lato aperto più lungo
|
|
double dLenRef = ( 2 * PockParams.dRad) - 50 * EPS_SMALL ; // almeno di questa lunghezza
|
|
for ( int u = 0 ; u < pCrvOrig->GetCurveCount() ; ++ u) {
|
|
if ( pCrvOrig->GetCurve( u)->GetTempProp( 0) == 0)
|
|
continue ; // escludo le chiuse
|
|
// ricavo la curva u-esima aperta
|
|
const ICurve* pCrvOpen = pCrvOrig->GetCurve( u) ;
|
|
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
|
|
return false ;
|
|
// ricavo la lunghezza di tale curva
|
|
double dLen = 0. ;
|
|
pCrvOpen->GetLength( dLen) ;
|
|
if ( dLen > dLenRef) { // se lunghezza accettabile o maggiore della massima trovata
|
|
Point3d ptSTmp ;
|
|
Vector3d vtMidOutTmp ;
|
|
// ricavo il punto medio e il versore tangente associato
|
|
if ( pCrvOpen->GetPointD1D2( 0.5, ICurve::FROM_MINUS, ptSTmp, &vtMidOutTmp)) {
|
|
int nFlag ;
|
|
if ( DistPointCurve( ptSTmp, *pCrvCompo).GetMinDistPoint( EPS_SMALL, ptStart, nFlag)) {
|
|
// controllo se il punto iniziale sta effettivaente su un lato aperto della curva attuale
|
|
// di primo Offset
|
|
double dU_check ;
|
|
if ( pCrvCompo->GetParamAtPoint( ptStart, dU_check)) {
|
|
// se il punto trovato è a cavallo tra due curve...
|
|
if ( abs( dU_check - floor( dU_check)) < EPS_PARAM ||
|
|
abs( dU_check - ceil( dU_check)) < EPS_PARAM) {
|
|
const ICurve* pCrvA = pCrvCompo->GetCurve( int( floor( dU_check))) ;
|
|
if ( pCrvA == nullptr)
|
|
return false ;
|
|
const ICurve* pCrvB = pCrvCompo->GetCurve( int( ceil( dU_check))) ;
|
|
if ( pCrvB == nullptr)
|
|
return false ;
|
|
if ( pCrvA->GetTempProp( 0) == 0 || pCrvB->GetTempProp( 0) == 1)
|
|
continue ; // se una delle due è chiusa, salto tale punto
|
|
}
|
|
// se invece tocca una sola curva, controllo che sia aperta
|
|
else {
|
|
const ICurve* pCrvA = pCrvCompo->GetCurve( int( floor( dU_check))) ;
|
|
if ( pCrvA == nullptr)
|
|
return false ;
|
|
if ( pCrvA->GetTempProp( 0) == 0)
|
|
continue ; // se chiusa, salto tale punto
|
|
}
|
|
}
|
|
vtMidOutTmp.Normalize() ;
|
|
vtMidOutTmp.Rotate( Z_AX, - 90) ;
|
|
vtMidOut = vtMidOutTmp ;
|
|
bMidOut = true ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bMidOut ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignOpenCloseTmpPropToFirstOffsCurve( ICurveComposite* pCrv, const PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvLoops, INTVECTOR& vIndex, bool& bSomeOpen)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i)
|
|
if ( IsNull( vCrvLoops[i]) || ! vCrvLoops[i]->IsValid() || vCrvLoops[i]->GetCurveCount() == 0)
|
|
return false ;
|
|
bSomeOpen = false ; // flag per presenza di lati aperti
|
|
int nInd = -1 ; // indice della prima curva che non è "raccordo" di Offset
|
|
|
|
// scorro tutte le curve presenti in pCrv ( curva da cui devo entrare)
|
|
for ( int i = 0 ; i < pCrv->GetCurveCount() ; ++ i) {
|
|
|
|
// nProp0 -> #curva il cui Offset ha generato la curva i-esima di pCrv
|
|
int nProp0 ; pCrv->GetCurveTempProp( i, nProp0, 0) ;
|
|
// nProp1 -> #loop che contiene la curva espressa in nProp0
|
|
int nProp1 ; pCrv->GetCurveTempProp( i, nProp1, 1) ;
|
|
|
|
if ( nProp0 > 0) { // se questa curva non è un "raccordo" di Offset
|
|
// controllo per maggiore sicurezza che effettivamente nProp1 sia un indice valido per il vettore dei Loops e
|
|
// che nProp0 non sia maggiore del numero di curve del loop nProp1-esimo del vettore dei Loops della pSrfToWork
|
|
if ( nProp1 >= 0 && nProp1 < int( vCrvLoops.size()) && nProp0 < vCrvLoops[nProp1]->GetCurveCount()) {
|
|
// aggiorno il vettore di indici ...
|
|
if ( find( vIndex.begin(), vIndex.end(), nProp1) == vIndex.end())
|
|
vIndex.push_back( nProp1) ;
|
|
// aggiorno la proprietà della curva da cui devo entrare
|
|
int nTempProp ; vCrvLoops[nProp1]->GetCurveTempProp( nProp0 - 1, nTempProp, 0) ;
|
|
pCrv->SetCurveTempProp( i, nTempProp, 0) ;
|
|
// se la curva è aperta, aggiorno il Flag
|
|
if ( nTempProp == 1 && ! bSomeOpen)
|
|
bSomeOpen = true ;
|
|
// salvo l'indice della prima curva trovata che non è un "raccordo" di Offset
|
|
if ( nInd == -1)
|
|
nInd = i ;
|
|
}
|
|
else
|
|
pCrv->SetCurveTempProp( i, 0, 0) ;
|
|
}
|
|
else {
|
|
pCrv->SetCurveTempProp( i, -2, 0) ;
|
|
pCrv->SetCurveTempProp( i, -2, 1) ;
|
|
}
|
|
}
|
|
|
|
// scorro tutte le curve incerte ( che derivano da raccordi )
|
|
if ( nInd != -1)
|
|
pCrv->ChangeStartPoint( nInd) ; // la prima curva non è un "raccordo" di Offset
|
|
|
|
for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++ i) {
|
|
double dLen = 0. ;
|
|
if ( pCrv->GetCurve( i)->GetLength( dLen) && dLen < 0.025 * ( 2 * PockParams.dRad))
|
|
pCrv->SetCurveTempProp( i, 0, 0) ;
|
|
else {
|
|
int nTmpProp0, nTmpProp1 ;
|
|
if (( pCrv->GetCurveTempProp( i, nTmpProp0, 0) && nTmpProp0 == -2) &&
|
|
( pCrv->GetCurveTempProp( i, nTmpProp1, 1) && nTmpProp1 == -2)) {
|
|
// copio la temp prop della curva precedente
|
|
pCrv->SetCurveTempProp( i, pCrv->GetCurve( i-1)->GetTempProp(), 0) ;
|
|
// se troppo corta, allora chiusa
|
|
if ( pCrv->GetCurve( i)->GetLength( dLen) && dLen < 0.01 * ( 2 * PockParams.dRad))
|
|
pCrv->SetCurveTempProp( i, 0, 0) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SetPtStartForPath( ICurveComposite* pCrvOffsAct, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
|
|
const Point3d& ptEndPrec, const Frame3d& frPocket, Point3d& ptStart,
|
|
Vector3d& vtMidOut, bool& bMidOut, int nOffs, const ICurveComposite* pCrvOrig)
|
|
{
|
|
// ============================= INFO ==============================================================
|
|
// pCrvOffsAct -> Curva di Offset su cui cercare ptStart, vtMidOut, bMidOpen
|
|
// pSrfToWork -> Superificie originaria da lavorare
|
|
// ( questa superificie ha i flag di lati aperti/chiusi settati nei loops)
|
|
// =================================================================================================
|
|
|
|
/*
|
|
1) Come prima cosa ricavo i lati aperti equivalenti dal primo Offset ; Facendo l'Offset di una curva
|
|
chiusa ho all'interno delle temp prop delle sottcurve due informazioni :
|
|
// nTmpProp0 -> #curva il cui Offset ha generato la curva i-esima attuale
|
|
// nTmpProp1 -> #loop che contiene la curva espressa in nTmpProp0
|
|
|
|
Quindi dal mio contorno con i lati aperti impostati, posso settare i lati aperti "equivalenti"
|
|
anche sulla curva di primo Offset.
|
|
NB. Il tool entra dalla curva di primo Offset, non dal bordo della regione da svuotare; quindi
|
|
l'entrata va calcolata sul primo Offset, non sulla curva di bordo della FlatRegion di Pocketing.
|
|
|
|
2) Verifico l'esistenza di lati aperti :
|
|
- Se esistono lati aperti -> cerco il lato più sensato su cui entrare
|
|
- 2.1) Guardo la curva originale ( senza estensione degli aperti) che delimita la regione di
|
|
svuotatura e cerco i lati aperti presenti in essa ; se mi posiziono a metà del lato aperto
|
|
e cerco il punto più vicino sulla curva di primo offset. Se quest'ultima è anch'essa aperta
|
|
e di lunghezza adeguata, l'entrata è valida
|
|
- 2.2) ( se 2.1 non accettabile ) Scorro tutte le curve di lato aperto sulla curva di primo
|
|
Offset e per ogni curva di lunghezza accettabile controllo la precedente e la successiva.
|
|
Ad ogni curva aperta viene assegnato uno "score", in base alla sua Lenght, alle Lenghts
|
|
delle curve adiacenti ( se aperte ) e ai loro angoli di adiacenza.
|
|
Se la curva aperta corrente ha uno "score" sufficiente, viene scelta come curva per l'entrata
|
|
- 2.3) ( se 2.2 non accettabile ) Considero le curve come tutte chiuse ( vedi sotto )
|
|
- Se non esistono dei lati aperti -> entro presso il lato chiuso più lungo
|
|
- 2.4)
|
|
*/
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvOffsAct == nullptr || ! pCrvOffsAct->IsValid() || pCrvOffsAct->GetCurveCount() == 0 ||
|
|
pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1)
|
|
return false ;
|
|
|
|
// clono le curva su cui devo entrare
|
|
PtrOwner<ICurveComposite> pCrv( pCrvOffsAct->Clone()) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
|
|
// 1) *********************************************************************
|
|
|
|
bool bSomeOpen = false ; // flag per presenza di lati aperti
|
|
// creo un vettore di Loops della superificie, ordinati
|
|
ICRVCOMPOPOVECTOR vCrvLoops ;
|
|
for ( int l = 0 ; l < pSrfToWork->GetLoopCount( 0) ; ++ l)
|
|
vCrvLoops.emplace_back( ConvertCurveToComposite( pSrfToWork->GetLoop( 0, l))) ;
|
|
// creo un vettore di indici. Questi indici si riferiscono alle posizioni di vCrvLoops i quali offset
|
|
// hanno generato delle curve sulla pCrv ( curva da cui devo entrare )
|
|
INTVECTOR vIndex ;
|
|
if ( ! AssignOpenCloseTmpPropToFirstOffsCurve( pCrv, PockParams, vCrvLoops, vIndex, bSomeOpen))
|
|
return false ;
|
|
|
|
// *********************************************************************
|
|
|
|
// se ho dei lati aperti...
|
|
double dLenMax = EPS_SMALL ;
|
|
int nCrvForMax = 0 ;
|
|
if ( bSomeOpen) {
|
|
// ... e sono al primo Step, cerco la curva Aperta più lunga ( scelgo la prima che trovo) ...
|
|
if ( ! ptEndPrec.IsValid()) {
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
int nTmpProp = 0 ;
|
|
double dLenAct = EPS_SMALL ;
|
|
if ( pCrv->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 1 &&
|
|
pCrv->GetCurve( u)->GetLength( dLenAct) && dLenAct > dLenMax) {
|
|
dLenMax = dLenAct ;
|
|
nCrvForMax = u ;
|
|
}
|
|
}
|
|
// il punto iniziale della curva sarà il punto iniziale della sottocurva trovata
|
|
pCrv->ChangeStartPoint( nCrvForMax) ;
|
|
}
|
|
// ... se invece non sono al primo Step, cerco la prima curva aperta più vicina al punto finale
|
|
else {
|
|
int nIndClosesOpenCrv = -1 ;
|
|
double dMinDist = INFINITO ;
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
if ( pCrv->GetCurve( u)->GetTempProp( 0) == 1) {
|
|
DistPointCurve DPC( ptEndPrec, *pCrv->GetCurve( u)) ;
|
|
double dCurrDist = INFINITO ;
|
|
if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist) {
|
|
dMinDist = dCurrDist ;
|
|
nIndClosesOpenCrv = u ;
|
|
}
|
|
}
|
|
}
|
|
// il punto iniziale della curva sarà il punto iniziale della sottocurva trovata
|
|
pCrv->ChangeStartPoint( nIndClosesOpenCrv) ;
|
|
}
|
|
}
|
|
|
|
// creo un vettore con tutti i Loops della pSwfToWork per i quali, mediante l'Offset, non hanno
|
|
// generato alcuna curva presente nella pCrv ( quella da cui devo entrare)
|
|
ICRVCOMPOPOVECTOR vOtherCrv ;
|
|
for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) {
|
|
bool bOk = true ;
|
|
for ( int j = 0 ; j < int( vIndex.size()) && bOk ; ++ j) {
|
|
if ( i == vIndex[j])
|
|
bOk = false ;
|
|
}
|
|
if ( bOk)
|
|
vOtherCrv.emplace_back( vCrvLoops[i]->Clone()) ;
|
|
}
|
|
|
|
// cerchiamo un punto valido per l'entrata ...
|
|
bMidOut = false ;
|
|
if ( bSomeOpen) { // se ho dei lati aperti, cerco un parametro ideale per entrare
|
|
/* ( 2.1 ) */
|
|
bool bOK = false ;
|
|
if ( nOffs == 1 && pCrvOrig != nullptr && pCrvOrig->IsValid()) {
|
|
// in questo caso le curve a fagiolo dei lati aperti a sinistra e a destra potrebbero
|
|
// intersecarsi tra loro -> risulta difficile calcolare il versore direzione d'uscita
|
|
bOK = SetSpecialPtStartForOpenEdges( pCrvOrig, frPocket, PockParams, pCrv, ptStart, vtMidOut, bMidOut) ;
|
|
}
|
|
if ( ! bOK) /* ( 2.2 ) */
|
|
bMidOut = GetParamOnOpenSide( pCrv, vOtherCrv, frPocket, PockParams, ptStart, vtMidOut) ;
|
|
}
|
|
if ( bMidOut) { // se ho trovato e valido, allora imposto il punto inziale trovato
|
|
const double LEN_OUT = 5 ;
|
|
double dPar ; int nFlag ;
|
|
bMidOut = ( DistPointCurve( ptStart + LEN_OUT * vtMidOut, *pCrv).GetParamAtMinDistPoint( 0, dPar, nFlag)
|
|
&& pCrv->ChangeStartPoint( dPar)) ;
|
|
}
|
|
|
|
/* ( 2.3 | 2.4 ) */
|
|
if ( ! bMidOut) // alla peggio, ordino i lati lunghi per lunghezza e cerco un'entrata valida
|
|
AdjustContourStart( pCrv, PockParams, vOtherCrv, true, ptEndPrec) ;
|
|
|
|
// ora che ho deciso quale sia il punto iniziale, lo imposto effettivamente sulla curva di Offset passata alla funzione
|
|
pCrv->GetStartPoint( ptStart) ;
|
|
pCrvOffsAct->Clear() ;
|
|
pCrvOffsAct->CopyFrom( pCrv) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SetAdvancedPtStartForPath( ICRVCOMPOPOVECTOR& vCrvOffsAct, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
|
|
const Point3d& ptEndPrec, const Frame3d& frPocket, Point3d& ptStart,
|
|
Vector3d& vtMidOut, bool& bMidOut, int& nIndex, int nOffs,
|
|
ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops)
|
|
{
|
|
// controllo dei parametri
|
|
for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i)
|
|
if ( IsNull( vCrvOffsAct[i]) || ! vCrvOffsAct[i]->IsValid() || vCrvOffsAct[i]->GetCurveCount() == 0)
|
|
return false ;
|
|
if ( pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1)
|
|
return false ;
|
|
nIndex = 0 ;
|
|
|
|
// scorro le curve di primo Offset
|
|
// NB. in prima posizione ho il primo Offset del loop esterno, a seguire le isole
|
|
// NB. Se non riesco ad entrare in nessuna curva, allora tengo valida quella calcolata per il loop esterno
|
|
bool bMidOut_fist = false ;
|
|
Vector3d vtMidOut_first ;
|
|
Point3d ptStart_first ;
|
|
for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i) {
|
|
// cambio il suo punto d'inizio dell'Offset attuale
|
|
if ( SetPtStartForPath( vCrvOffsAct[i], PockParams, pSrfToWork, ptEndPrec, frPocket, ptStart,
|
|
vtMidOut, bMidOut, nOffs,
|
|
( int( vCrvOrigChunkLoops.size()) < i) ? Get( vCrvOrigChunkLoops[i]) : nullptr))
|
|
vCrvOffsAct[i]->GetStartPoint( ptStart) ;
|
|
else
|
|
return false ;
|
|
// se ho trovato un'entrata valida da un lato aperto, ho finito
|
|
if ( bMidOut) {
|
|
nIndex = i ; // salvo l'indice del vettore
|
|
return true ;
|
|
}
|
|
// se sono nel loop esterno e non ho trovato un'entrata da un lato aperto, salvo i risultati ottenuti
|
|
else if ( i == 0) {
|
|
bMidOut_fist = bMidOut ;
|
|
vtMidOut_first = vtMidOut ;
|
|
ptStart_first = ptStart ;
|
|
}
|
|
}
|
|
|
|
// in questo caso non ho trovato un'entrata da un lato aperto in nessuna curva ( potrebbero essere tutte chiuse)
|
|
bMidOut = bMidOut_fist ;
|
|
vtMidOut = vtMidOut_first ;
|
|
ptStart = ptStart_first ;
|
|
return true ;
|
|
|
|
}
|
|
|
|
// ***************************************************************************
|
|
//---------------------------- CASI OTTIMIZZATI ------------------------------
|
|
// ***************************************************************************
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetOptCrvIndex( const ISurfFlatRegion* pSfrOrig, const ISurfFlatRegion* pSfrChunk,
|
|
const PocketParams& PockParams, int nReg, ICRVCOMPOPOVECTOR& vCrvOrig)
|
|
{
|
|
|
|
/*
|
|
NB. La superficie da lavorare è stata estesa presso i lati aperti; cerco il Chunk
|
|
di riferimento sulla superficie originale ed estraggo le sue curve ( in prima posizione
|
|
il loop esterno e a seguire le isole); queste curve verranno utilizzate per la scelta
|
|
dei casi ottimizzati ( trapezi, circonferenze, ecc ecc..)
|
|
*/
|
|
|
|
// controllo dei parametri
|
|
if ( pSfrOrig == nullptr || ! pSfrOrig->IsValid() ||
|
|
pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
|
|
// se la superficie originale ha un solo Chunk, allora le curve originali sono già trovate
|
|
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( 0) ; ++ l)
|
|
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( 0, l))) ; // sono inizializzate...
|
|
if ( pSfrOrig->GetChunkCount() == 1)
|
|
return true ;
|
|
|
|
// vettore di indici dei chunk candidati
|
|
INTVECTOR vInds ;
|
|
|
|
// scorro tutti i Chunk della superficie orginaria
|
|
for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) {
|
|
// la classificazione può essere fatta semplicemente guardando il Loop esterno
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
// classifico il Chunk attuale con la curva in esame
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) {
|
|
// se il Chunk attuale è stato esteso presso presso i lati aperti mi aspetto che esso non sia più
|
|
// grande del chunk originale ( se non ci sono lati aperti, allora coincide )
|
|
// se il ccClass non contiene parti classificate come esterne allora il Chunk di riferimento è il c-esimo
|
|
bool bIsThis = true ;
|
|
for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) {
|
|
if ( ccClass[k].nClass == CRVC_OUT)
|
|
bIsThis = false ;
|
|
}
|
|
if ( bIsThis)
|
|
vInds.push_back( c) ;
|
|
}
|
|
}
|
|
// se ho un solo indice allora ho già identificato le curve originali
|
|
if ( int( vInds.size()) == 1) {
|
|
vCrvOrig.clear() ;
|
|
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( vInds[0]) ; ++ l)
|
|
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( vInds[0], l))) ;
|
|
return true ;
|
|
}
|
|
// altrimenti...
|
|
/*
|
|
NB. Come parametro alla funzione è stato passato "nReg"; Facendo il primo Offset del Chunk attuale
|
|
questo verrà diviso in n sottoregioni; devo considerare la nReg-esima
|
|
Situazione: Estendendo presso i lati aperti, due ( o più) chunk si sono uniti formandone uno unico
|
|
Quando effettuo il primo offset ( del diametro del materiale + offset radiale), questo Chunk si splitterà in n Chunk;
|
|
Uno ( o più) di questi nuovi chunk potrebbe corrispondere ad un caso ottimizzato.
|
|
Devo recuperare il Chunk della superficie originaria ( che non essendo estesa presso i lati aperti
|
|
ha la geomtria originale)
|
|
*/
|
|
|
|
// effettuo un Offset del Chunk per simulare la creazione degli n Chunks
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
PtrOwner<ISurfFlatRegion> pSfrRefChunk( pSfrChunk->Clone()) ;
|
|
if ( IsNull( pSfrRefChunk) ||
|
|
! pSfrRefChunk->Offset( - dOffs, ICurve::OFF_FILLET))
|
|
return false ;
|
|
|
|
// clono il Chunk nReg-esimo se esiste
|
|
pSfrRefChunk.Set( pSfrRefChunk->CloneChunk( nReg)) ;
|
|
if ( IsNull( pSfrRefChunk))
|
|
return true ; // bisogna passare al ( pSfrChunk + 1)-esimo se esiste
|
|
|
|
// scorro tutti i Chunk della superficie orginaria e ripeto la classificazione
|
|
// NB. Questa volta posso fermarmi al primo Chunk classificato come non esterno
|
|
for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) {
|
|
// come prima recupero il Loop esterno
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
// classificazione
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrRefChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) {
|
|
bool bIsThis = true ;
|
|
for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) {
|
|
if ( ccClass[k].nClass == CRVC_OUT)
|
|
bIsThis = false ;
|
|
}
|
|
if ( bIsThis) {
|
|
vCrvOrig.clear() ;
|
|
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( c) ; ++ l)
|
|
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( c, l))) ;
|
|
return true ; // esco
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ; // alla peggio non considero casi ottimizzati e svuoto normalmente...
|
|
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
OptimizedSpiralCirle( const ICurveComposite* pCrvCompo, const double dToll, double& dRad,
|
|
Point3d& ptC, bool& bIsCirlce)
|
|
{
|
|
/* restituisce il centro e il raggio di una circonfereza che approssima pCrvCompo */
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || dToll < EPS_SMALL)
|
|
return false ;
|
|
dRad = 0. ;
|
|
ptC = P_INVALID ;
|
|
bIsCirlce = false ;
|
|
|
|
// se aperta, non è un cerchio e non è piana, esco
|
|
Plane3d plPock ;
|
|
if ( ! pCrvCompo->IsClosed() || ! pCrvCompo->IsFlat( plPock))
|
|
return true ;
|
|
|
|
// creo un sistema di riferimento centrato sulla curva
|
|
Point3d ptCentroid ;
|
|
Vector3d vtN ;
|
|
Frame3d frCurr ;
|
|
if ( ! pCrvCompo->GetCentroid( ptCentroid) ||
|
|
! pCrvCompo->GetExtrusion( vtN) ||
|
|
! frCurr.Set( ptCentroid, vtN) ||
|
|
! frCurr.IsValid())
|
|
return false ;
|
|
|
|
// porto la curva nel sistema di riferimento corrente ( dopo averla clonata)
|
|
PtrOwner<ICurveComposite> pCrvLoc( pCrvCompo->Clone()) ;
|
|
if ( IsNull( pCrvLoc) || ! pCrvLoc->IsValid() || ! pCrvLoc->ToLoc( frCurr))
|
|
return false ;
|
|
|
|
// approssimo la curva locale con una PolyLine
|
|
PolyLine pL ;
|
|
if ( ! pCrvLoc->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, pL) ||
|
|
pL.GetPointNbr() == 0)
|
|
return false ;
|
|
|
|
// salvo i punti ottenuti ( il punto finale sarà contenuto due volte)
|
|
PNTVECTOR vPts ;
|
|
Point3d ptNext ;
|
|
pL.GetFirstPoint( ptNext) ;
|
|
vPts.push_back( ptNext) ;
|
|
while ( pL.GetNextPoint( ptNext)) {
|
|
vPts.push_back( Media( vPts.back(), ptNext)) ; // inserisco il punto medio
|
|
vPts.push_back( ptNext) ; // inserisco il punto successivo
|
|
}
|
|
|
|
// per ogni coppia di punti calcolo la distanza massima e minima dal centro del cerchio locale ( ORIG)
|
|
double dMaxDist = 0. ;
|
|
double dMinDist = INFINITO ;
|
|
for ( int i = 0 ; i < int( floor( 0.5 * int( vPts.size()))) ; i = i + 2) {
|
|
PtrOwner<ICurveLine> pSeg( CreateCurveLine()) ;
|
|
if ( IsNull( pSeg) || ! pSeg->Set( vPts[i], vPts[i+1]))
|
|
return false ;
|
|
DistPointCurve DPC( ORIG, *pSeg) ;
|
|
double dCurrDist = 0. ;
|
|
if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist)
|
|
dMinDist = dCurrDist ;
|
|
dCurrDist = Dist( ORIG, vPts[i]) ;
|
|
if ( dCurrDist > dMaxDist)
|
|
dMaxDist = dCurrDist ;
|
|
}
|
|
|
|
// controllo se la distanza tra minima e massima sono simili ( in base alla tolleranza)
|
|
if ( abs( dMaxDist - dMinDist) > dToll)
|
|
return true ;
|
|
|
|
// imposto i parametri per la circonferenza
|
|
dRad = 0.5 * ( dMaxDist + dMinDist) ;
|
|
ptC = ptCentroid ;
|
|
bIsCirlce = true ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcCircleSpiral( const Point3d& ptCen, const Vector3d& vtN, double dOutRad, double dIntRad,
|
|
const PocketParams& PockParams, ICurveComposite* pMCrv, ICurveComposite* pRCrv)
|
|
{
|
|
/* calcola il percorso di a spirale assieme al percorso di ritorno */
|
|
|
|
// raggio della circonferenza esterna
|
|
if ( dOutRad < 10 * EPS_SMALL)
|
|
return false ;
|
|
|
|
// imposto versore estrusione sulle curve composite
|
|
pMCrv->SetExtrusion( vtN) ;
|
|
pRCrv->SetExtrusion( vtN) ;
|
|
|
|
// creo e inserisco la circonferenza esterna
|
|
PtrOwner<ICurveArc> pArc( CreateCurveArc()) ;
|
|
if ( IsNull( pArc) || ! pArc->Set( ptCen, vtN, dOutRad))
|
|
return false ;
|
|
|
|
// creo la superificie per la Feed
|
|
PtrOwner<ISurfFlatRegion> pSrfRemoved( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfRemoved))
|
|
return false ;
|
|
ICRVCOMPOPOVECTOR vLinksDone ;
|
|
|
|
Vector3d vtDir = pArc->GetStartVersor() ;
|
|
pMCrv->AddCurve( Release( pArc)) ;
|
|
|
|
// se richiesta percorrenza invertita
|
|
if ( PockParams.bInvert)
|
|
pMCrv->Invert() ;
|
|
|
|
// se raggio esterno maggiore dell'interno
|
|
if ( dOutRad > dIntRad + 10 * EPS_SMALL) {
|
|
|
|
// aggiungo le semicirconferenze della spirale ( devono essere in numero dispari)
|
|
int nStep = int( ceil( ( dOutRad - dIntRad) / ( 0.5 * PockParams.dSideStep))) ;
|
|
if ( nStep % 2 == 0)
|
|
nStep += 1 ;
|
|
double dStep = ( dOutRad - dIntRad) / nStep ;
|
|
for ( int i = 1 ; i <= nStep ; ++ i) {
|
|
if ( ! ( i % 2 == 0))
|
|
pMCrv->AddArcTg( ptCen - vtDir * ( dOutRad - i * dStep)) ;
|
|
else
|
|
pMCrv->AddArcTg( ptCen + vtDir * ( dOutRad - i * dStep)) ;
|
|
}
|
|
|
|
// aggiungo la circonferenza interna
|
|
pMCrv->AddArcTg( ptCen + vtDir * dIntRad) ;
|
|
pMCrv->AddArcTg( ptCen - vtDir * dIntRad) ;
|
|
}
|
|
|
|
// verifico il percorso di lavoro
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// assegno la Feed
|
|
AssignFeedSpiralOpt( 0, PockParams, pMCrv) ;
|
|
|
|
// calcolo l'eventuale percorso di ritorno
|
|
Point3d ptStart ; pMCrv->GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; pMCrv->GetEndPoint( ptEnd) ;
|
|
Vector3d vtStart ; pMCrv->GetStartDir( vtStart) ;
|
|
if ( ! AreSamePointApprox( ptStart, ptEnd)) {
|
|
PtrOwner<ICurveArc> pArc2( CreateCurveArc()) ;
|
|
if ( IsNull( pArc2) || ! pArc2->Set2PVN( ptStart, ptEnd, - vtStart, vtN))
|
|
return false ;
|
|
pRCrv->AddCurve( Release( pArc2)) ;
|
|
// inverto e eventualmente sistemo archi
|
|
pRCrv->Invert() ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
CalcTrapezoidSpiralLocalFrame( ICurveComposite* pCrvTrap, const Vector3d& vtDir, Frame3d& frLoc)
|
|
{
|
|
// cerco i lati paralleli a vtDir
|
|
int nBaseId = -1 ;
|
|
for ( int i = 0 ; i < pCrvTrap->GetCurveCount() ; i ++) {
|
|
Vector3d vtEdge ;
|
|
pCrvTrap->GetCurve( i)->GetStartDir( vtEdge) ;
|
|
if ( AreSameOrOppositeVectorApprox( vtEdge, vtDir)) {
|
|
nBaseId = i ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( nBaseId != 0 && nBaseId != 1)
|
|
return false ;
|
|
|
|
// imposto come lato iniziale per la curva uno dei lati paralleli a vtDir
|
|
pCrvTrap->ChangeStartPoint( nBaseId) ;
|
|
Point3d ptOrig ; pCrvTrap->GetStartPoint( ptOrig) ;
|
|
Vector3d vtX ; pCrvTrap->GetStartDir( vtX) ;
|
|
return frLoc.Set( ptOrig, Z_AX, vtX) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetBoxCrvOptTrap( const int nCrv, const ICurveComposite* pCrvCompo, const double dDiam, int& nType,
|
|
ICurveComposite* pCrvBox)
|
|
{
|
|
nType = - 1 ;
|
|
// prendo la curva
|
|
const ICurve* pCrvCurr = pCrvCompo->GetCurve( nCrv) ;
|
|
if ( pCrvCurr == nullptr)
|
|
return false ;
|
|
// controllo se lineare, altrimenti passo alla successiva
|
|
if ( pCrvCurr->GetType() != CRV_LINE)
|
|
return true ;
|
|
// prendo la direzione del tratto lineare
|
|
Vector3d vtDir ; pCrvCurr->GetStartDir( vtDir) ;
|
|
// prendo il punto iniziale
|
|
Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ;
|
|
// creo il riferimento basato su questo tratto
|
|
Frame3d frTrap ; frTrap.Set( ptStart, Z_AX, vtDir) ;
|
|
if ( ! frTrap.IsValid())
|
|
return false ;
|
|
// porto la curva Compo ( clonandola) nel frame e calcolo il suo BBox
|
|
BBox3d BBox ;
|
|
PtrOwner<ICurveComposite> pCrvCompo_c( pCrvCompo->Clone()) ;
|
|
if ( IsNull( pCrvCompo_c) || ! pCrvCompo_c->IsValid() ||
|
|
! pCrvCompo_c->ToLoc( frTrap) ||
|
|
! pCrvCompo_c->GetLocalBBox( BBox))
|
|
return false ;
|
|
// controllo dimY e dimX per il box
|
|
bool bDimYOk = BBox.GetDimY() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ;
|
|
bool bDimXOk = BBox.GetDimX() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ;
|
|
// controllo dimensioni ammissibili
|
|
if ( ! bDimXOk && ! bDimYOk)
|
|
return true ; // se nessuna
|
|
else if ( ! bDimXOk)
|
|
nType = 1 ; // se dimY
|
|
else if ( ! bDimYOk)
|
|
nType = 0 ; // se dimX
|
|
else
|
|
nType = 2 ; // se entrambe
|
|
|
|
// creo il rettangolo del Box
|
|
pCrvBox->Clear() ;
|
|
pCrvBox->AddPoint( BBox.GetMin()) ;
|
|
pCrvBox->AddLine( BBox.GetMin() + BBox.GetDimX() * X_AX) ;
|
|
pCrvBox->AddLine( BBox.GetMax()) ;
|
|
pCrvBox->AddLine( BBox.GetMax() - BBox.GetDimX() * X_AX) ;
|
|
pCrvBox->Close() ;
|
|
|
|
// determino il punto inziale della curva
|
|
if ( nType == 0)
|
|
pCrvBox->ChangeStartPoint( 1.) ;
|
|
|
|
pCrvBox->ToGlob( frTrap) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
PreparareTrapezoidTwoBases( const ICurveComposite* pCrvCompo, const double dDiam, const int nType, int& nBase,
|
|
int& nSecondBase, bool& bOk, ICurveComposite* pCrvTrap)
|
|
{
|
|
// controllo parametri
|
|
if ( pCrvCompo == nullptr)
|
|
return false ;
|
|
bOk = false ;
|
|
|
|
// le parti tra le due basi devono essere tutte omogenee ( o tutte aperte o tutte chiuse)
|
|
// pCrvTest0 sarà la parte destra, pCrvTest1 la parte sinistra
|
|
Point3d ptSB0, ptSB1 ,ptEB0, ptEB1 ;
|
|
PtrOwner<ICurveComposite> pCrvTest0( pCrvCompo->Clone()) ;
|
|
PtrOwner<ICurveComposite> pCrvTest1( pCrvCompo->Clone()) ;
|
|
pCrvCompo->GetCurve( nBase)->GetStartPoint( ptSB0) ;
|
|
pCrvCompo->GetCurve( nBase)->GetEndPoint( ptEB0) ;
|
|
pCrvCompo->GetCurve( nSecondBase)->GetStartPoint( ptSB1) ;
|
|
pCrvCompo->GetCurve( nSecondBase)->GetEndPoint( ptEB1) ;
|
|
pCrvTest0->ChangeStartPoint( nBase) ;
|
|
pCrvTest1->ChangeStartPoint( nSecondBase) ;
|
|
double dUTrim0, dUTrim1 ;
|
|
pCrvTest0->GetParamAtPoint( ptSB1, dUTrim0) ;
|
|
pCrvTest1->GetParamAtPoint( ptSB0, dUTrim1) ;
|
|
pCrvTest0->TrimStartEndAtParam( 1, dUTrim0) ;
|
|
pCrvTest1->TrimStartEndAtParam( 1, dUTrim1) ;
|
|
// controllo che la parte destra si uniforme per le TmpProp
|
|
for ( int u = 0 ; u < pCrvTest0->GetCurveCount() - 1 ; ++ u) {
|
|
int nPropAct, nPropSucc ;
|
|
if ( pCrvTest0->GetCurveTempProp( u, nPropAct, 0) &&
|
|
pCrvTest0->GetCurveTempProp( u + 1, nPropSucc, 0) &&
|
|
nPropAct != nPropSucc)
|
|
return true ; // se TmpProp diverse => non è un caso ottimizzato
|
|
}
|
|
// controllo che la parte sinistra sia uniforme per le TmpProp
|
|
for ( int u = 0 ; u < pCrvTest1->GetCurveCount() - 1 ; ++ u) {
|
|
int nPropAct, nPropSucc ;
|
|
if ( pCrvTest1->GetCurveTempProp( u, nPropAct, 0) &&
|
|
pCrvTest1->GetCurveTempProp( u + 1, nPropSucc, 0) &&
|
|
nPropAct != nPropSucc)
|
|
return true ; // se TmpProp diverse => non è un caso ottimizzato
|
|
}
|
|
|
|
// se lato destro aperto ( estendo il punto finale della base principale e il punto iniziale
|
|
// della base secondaria fino al lato destro del box)
|
|
bool bCopyRight = false ;
|
|
if ( pCrvTest0->GetCurve( 0)->GetTempProp( 0) == 1) {
|
|
if ( nType == 0) {
|
|
pCrvTrap->GetCurve( 0)->GetStartPoint( ptEB0) ;
|
|
pCrvTrap->GetCurve( 1)->GetStartPoint( ptSB1) ;
|
|
}
|
|
else {
|
|
pCrvTrap->GetCurve( 0)->GetEndPoint( ptEB0) ;
|
|
pCrvTrap->GetCurve( 1)->GetEndPoint( ptSB1) ;
|
|
}
|
|
}
|
|
// se lato destro chiuso
|
|
else
|
|
bCopyRight = true ;
|
|
// se lato sinistro aperto ( estendo il punto finale della base secondaria e il punto iniziale
|
|
// della base primaria dino al lato sinistro del box)
|
|
bool bCopyLeft = false ;
|
|
if ( pCrvTest1->GetCurve( 0)->GetTempProp( 0) == 1) {
|
|
if ( nType == 0) {
|
|
pCrvTrap->GetCurve( 2)->GetEndPoint( ptEB1) ;
|
|
pCrvTrap->GetCurve( 0)->GetStartPoint( ptSB0) ;
|
|
}
|
|
else {
|
|
pCrvTrap->GetCurve( 1)->GetEndPoint( ptEB1) ;
|
|
pCrvTrap->GetCurve( 2)->GetEndPoint( ptSB0) ;
|
|
}
|
|
}
|
|
// se lato sinistro chiuso
|
|
else
|
|
bCopyLeft = true ;
|
|
|
|
// creo la curva da restituire
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddPoint( ptSB0) ;
|
|
pCrvTrap->AddLine( ptEB0) ;
|
|
nBase = 0 ;
|
|
if ( bCopyRight)
|
|
pCrvTrap->AddCurve( Release( pCrvTest0)) ;
|
|
else {
|
|
pCrvTrap->AddLine( ptSB1) ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta
|
|
}
|
|
nSecondBase = pCrvTrap->GetCurveCount() ;
|
|
pCrvTrap->AddLine( ptEB1) ;
|
|
if ( bCopyLeft)
|
|
pCrvTrap->AddCurve( Release( pCrvTest1)) ;
|
|
else {
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, 1, 0) ; // aperta
|
|
}
|
|
|
|
// verifico dimensione x della svuotatura nel caso tutto chiuso
|
|
if ( bCopyLeft && bCopyRight) {
|
|
double dLen0 = 0, dLen2 = 0 ;
|
|
pCrvTrap->GetCurve( nBase)->GetLength( dLen0) ;
|
|
pCrvTrap->GetCurve( nSecondBase)->GetLength( dLen2) ;
|
|
if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL) {
|
|
pCrvTrap->Clear() ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
bOk = true ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTrap,
|
|
Frame3d& frTrap, const PocketParams& PockParams, double& dPocketSize, int& nBase, int& nSecondBase)
|
|
{
|
|
// controllo parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// diametro reale da tenere in considerazione
|
|
double dDiam = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ;
|
|
|
|
pCrvTrap->Clear() ; // resterà vuota se il caso non è ottimizzato
|
|
nBase = -1 ;
|
|
nSecondBase = -1 ;
|
|
|
|
// se la curva è già un trapezio, non sempre devo adattarla...
|
|
Point3d pt ; Vector3d vtDir, vtB2, vtOtherDir ;
|
|
if ( pCrvCompo->IsATrapezoid( 100 * EPS_SMALL, pt, vtDir, vtOtherDir, vtB2)) {
|
|
pCrvTrap->AddCurve( pCrvCompo->Clone()) ;
|
|
// se parallelogramma scelgo come base i lati lunghi
|
|
Vector3d vtL2( - vtDir + vtOtherDir + vtB2) ;
|
|
if ( AreSameOrOppositeVectorApprox( vtOtherDir, vtL2)) {
|
|
if ( vtOtherDir.Len() > vtDir.Len())
|
|
swap( vtDir, vtOtherDir) ;
|
|
}
|
|
vtDir.Normalize() ;
|
|
Vector3d vtOrtho = OrthoCompo( vtOtherDir, vtDir) ;
|
|
dPocketSize = vtOrtho.Len() ;
|
|
// eventuale approssimazione della curva con polyline per ottenere la stessa curva calcolata in ICurveComposite::IsATrapezoid
|
|
if ( pCrvCompo->GetCurveCount() > 4) {
|
|
PolyLine PL ;
|
|
if ( ! pCrvCompo->ApproxWithLines( 100 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL))
|
|
return false ;
|
|
pCrvTrap->FromPolyLine( PL) ;
|
|
}
|
|
if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap))
|
|
return false ;
|
|
bool bBaseOpen = false ;
|
|
bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == 1 || pCrvTrap->GetCurve( 2)->GetTempProp( 0) == 1) ;
|
|
// controllo dimensioni della svuotatura
|
|
if ( ! ( bBaseOpen && dPocketSize < dDiam + EPS_SMALL) &&
|
|
abs( dPocketSize - dDiam) > EPS_SMALL) {
|
|
pCrvTrap->Clear() ;
|
|
return true ;
|
|
}
|
|
// recupero flag aperto/chiuso dei lati
|
|
if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == 0 && pCrvTrap->GetCurve( 3)->GetTempProp( 0) == 0) {
|
|
double dLen0, dLen2 ;
|
|
pCrvTrap->GetCurve( 0)->GetLength( dLen0) ;
|
|
pCrvTrap->GetCurve( 2)->GetLength( dLen2) ;
|
|
if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL)
|
|
pCrvTrap->Clear() ;
|
|
}
|
|
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
return true ;
|
|
}
|
|
|
|
// controllo il numero di lati chiusi e salvo i loro indici
|
|
int nClosedSide = 0 ;
|
|
INTVECTOR vIndClosedSides ;
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
|
|
int nTmpProp ;
|
|
if ( pCrvCompo->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 0) {
|
|
++ nClosedSide ;
|
|
vIndClosedSides.push_back( u) ;
|
|
}
|
|
}
|
|
|
|
// se tutti lati aperti
|
|
switch ( nClosedSide) {
|
|
// TUTTI I LATI SONO APERTI
|
|
case 0 : {
|
|
// ricavo il box minimo della curva aperta ( passo dalla polyLine)
|
|
PolyLine PL ; Point3d ptCen ; double dWidth ;
|
|
if ( ! pCrvCompo->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) ||
|
|
! PL.GetMinAreaRectangleXY( ptCen, vtDir, dWidth, dPocketSize))
|
|
return false ;
|
|
// controllo dimY ( dHeight), se troppo estesa, non è un caso ottimizzato
|
|
if ( dPocketSize > dDiam + TOL_TRAPEZOID)
|
|
return true ;
|
|
// inverto il frame attuale
|
|
frTrap.Set( ptCen, Z_AX, vtDir) ;
|
|
if ( ! frTrap.IsValid())
|
|
return false ;
|
|
// creo il rettangolo del Box
|
|
pCrvTrap->AddPoint( Point3d( - 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->AddLine( Point3d( - 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->ToGlob( frTrap) ;
|
|
Point3d ptNewOrig ; pCrvTrap->GetStartPoint( ptNewOrig) ;
|
|
frTrap.Set( ptNewOrig, Z_AX, vtDir) ;
|
|
// imposto tutte le 4 curve come aperte
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
}
|
|
break ;
|
|
// 1 LATO CHIUSO ( LINEARE)
|
|
case 1 : {
|
|
int nType = -1 ;
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType != -1) {
|
|
// imposto tutte le curve come aperte tranne la prima ( estendo il solo lato chiuso come base del Box)
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u == 0 ? 0 : 1, 0) ;
|
|
// memorizzo la dimensione di svuotatura
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
}
|
|
}
|
|
break ;
|
|
// 2 LATI CHIUSI ( LINEARI, CONSECUTIVI O PARALLELI)
|
|
case 2 : {
|
|
// controllo se entrambi sono lineari
|
|
if ( pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE &&
|
|
pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE) {
|
|
// se lineari, ricavo i punti estremanti e le direzioni
|
|
Point3d ptS0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartPoint( ptS0) ;
|
|
Point3d ptS1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartPoint( ptS1) ;
|
|
Point3d ptE0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetEndPoint( ptE0) ;
|
|
Point3d ptE1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetEndPoint( ptE1) ;
|
|
Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ;
|
|
Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ;
|
|
// CASO PARALLELI ( la base è indifferente)
|
|
if ( AreOppositeVectorEpsilon( vtDir0, vtDir1, 5 * EPS_SMALL)) {
|
|
int nType = -1 ;
|
|
// prendo come base il primo chiuso
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType != -1) {
|
|
// memorizzo la dimensione di svuotatura
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// controllo che la distanza tra i due chiusi sia effettivamente circa il raggio
|
|
double dDist = 0. ;
|
|
DistPointCurve DPL( ptS0, *pCrvCompo->GetCurve( vIndClosedSides[1]), false) ;
|
|
if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) {
|
|
// imposto tutte le curve di indice dispari aperte
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u % 2 == 0 ? 0 : 1, 0) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
}
|
|
else
|
|
pCrvTrap->Clear() ;
|
|
}
|
|
}
|
|
// CASO CONSECUTIVI
|
|
bool bOk_0_1 = AreSamePointApprox( ptS1, ptE0) ; // chiuso1 . chiuso0
|
|
bool bOk_1_0 = AreSamePointApprox( ptS0, ptE1) ; // chiuso0 . chiuso1
|
|
if ( bOk_0_1 || bOk_1_0) {
|
|
// provo con il primo lato
|
|
int nType = -1 ;
|
|
// prendo come base il primo chiuso
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType == -1) {
|
|
// provo con l'altro
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType != -1) {
|
|
// spaw tra gli indici e flags
|
|
swap( vIndClosedSides[0], vIndClosedSides[1]) ;
|
|
swap( bOk_0_1, bOk_1_0) ;
|
|
}
|
|
}
|
|
Point3d ptH1, ptH2 ;
|
|
if ( nType == 1 || nType == 2) {
|
|
double dLen1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetLength( dLen1) ;
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// i lati inclinati chiusi definiscono il Box
|
|
if ( abs( dLen1 * ( vtDir0 ^ vtDir1).Len() - dPocketSize) < TOL_TRAPEZOID) {
|
|
// creo la curva a trapezio
|
|
if ( bOk_0_1) {
|
|
pCrvTrap->GetCurve( 2)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 3)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ;
|
|
}
|
|
else {
|
|
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->AddLine( ptS1) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
pCrvTrap->SetCurveTempProp( 0, 0, 0) ;
|
|
for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ;
|
|
}
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
else
|
|
pCrvTrap->Clear() ;
|
|
}
|
|
else if ( nType == 0) {
|
|
if ( bOk_0_1) {
|
|
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ;
|
|
pCrvTrap->ChangeStartPoint( 1.) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
else {
|
|
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->AddLine( ptS1) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
pCrvTrap->SetCurveTempProp( 0, 0, 0) ;
|
|
for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ;
|
|
pCrvTrap->ChangeStartPoint( 1.) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break ;
|
|
// 3 LATI CHIUSI ( LINEARI e CONSECUTIVI)
|
|
case 3 : {
|
|
// prendo i 3 lati chiusi
|
|
const ICurve* pCrv0 = pCrvCompo->GetCurve( vIndClosedSides[0]) ;
|
|
const ICurve* pCrv1 = pCrvCompo->GetCurve( vIndClosedSides[1]) ;
|
|
const ICurve* pCrv2 = pCrvCompo->GetCurve( vIndClosedSides[2]) ;
|
|
if ( pCrv0 == nullptr || pCrv1 == nullptr || pCrv2 == nullptr)
|
|
return false ;
|
|
// controllo che siano lineari
|
|
if ( pCrv0->GetType() == CRV_LINE && pCrv1->GetType() == CRV_LINE && pCrv2->GetType() == CRV_LINE) {
|
|
// prendo i punti iniziali e finali
|
|
Point3d ptStart0 ; pCrv0->GetStartPoint( ptStart0) ;
|
|
Point3d ptEnd0 ; pCrv0->GetEndPoint( ptEnd0) ;
|
|
Point3d ptStart1 ; pCrv1->GetStartPoint( ptStart1) ;
|
|
Point3d ptEnd1 ; pCrv1->GetEndPoint( ptEnd1) ;
|
|
Point3d ptStart2 ; pCrv2->GetStartPoint( ptStart2) ;
|
|
Point3d ptEnd2 ; pCrv2->GetEndPoint( ptEnd2) ;
|
|
|
|
// controllo che siano consecutivi e cambio l'ordine se necessario
|
|
bool bOk = true ;
|
|
if ( AreSamePointApprox( ptEnd0, ptStart1) && AreSamePointApprox( ptEnd1, ptStart2))
|
|
;
|
|
else if ( AreSamePointApprox( ptEnd1, ptStart2) && AreSamePointApprox( ptEnd2, ptStart0)) {
|
|
swap( vIndClosedSides[0], vIndClosedSides[1]) ;
|
|
swap( vIndClosedSides[1], vIndClosedSides[2]) ;
|
|
}
|
|
else if ( AreSamePointApprox( ptEnd2, ptStart0) && AreSamePointApprox( ptEnd0, ptStart1)) {
|
|
swap( vIndClosedSides[0], vIndClosedSides[2]) ;
|
|
swap( vIndClosedSides[2], vIndClosedSides[1]) ;
|
|
}
|
|
else // se non consecutivi
|
|
bOk = false ;
|
|
|
|
// se i tre lati sono ( mediante lo swap) consecutivi...
|
|
if ( bOk) {
|
|
// controllo se la dimensione Y del lato 1 è valida ( il resto è gestito di seguito)
|
|
int nType = -1 ;
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType == 1) {
|
|
// bisgna controllare la lunghezza dei chiusi
|
|
double dLen0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetLength( dLen0) ;
|
|
double dLen2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetLength( dLen2) ;
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ;
|
|
Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ;
|
|
Vector3d vtDir2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetStartDir( vtDir2) ;
|
|
// i lati inclinati chiusi definiscono il Box
|
|
if ( abs( dLen2 * ( vtDir1 ^ vtDir2).Len() - dPocketSize) < TOL_TRAPEZOID &&
|
|
abs( dLen0 * ( vtDir1 ^ vtDir0).Len() - dPocketSize) < TOL_TRAPEZOID) {
|
|
// creo la curva a trapezio
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[2])->Clone()) ;
|
|
pCrvTrap->Close() ;
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u < 3 ? 0 : 1 , 1) ;
|
|
pCrvTrap->ChangeStartPoint( 1.) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// se non ho trovato delle basi, allora cerco in maniera generale un trapezoide ( se esiste)
|
|
if ( nBase == -1) {
|
|
bool bOK = false ;
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() && !bOK ; ++ u) {
|
|
// cerco un lato chiuso ( esiste per forza)
|
|
if ( pCrvCompo->GetCurve( u)->GetTempProp( 0) == 0) {
|
|
// controllo se il lato corrente può essere una base
|
|
int nType = -1 ;
|
|
if ( ! GetBoxCrvOptTrap( u, pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
// se è una base valida
|
|
if ( nType != -1) {
|
|
// ricavo la dimensione di svuotatura
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// ricavo la sua direzione iniziale
|
|
Vector3d vtBaseDir ; pCrvCompo->GetCurve( u)->GetStartDir( vtBaseDir) ;
|
|
// ricavo il suo punto iniziale
|
|
Point3d ptBaseStart ; pCrvCompo->GetCurve( u)->GetStartPoint( ptBaseStart) ;
|
|
// ricavo il suo punto finale
|
|
Point3d ptBaseEnd ; pCrvCompo->GetCurve( u)->GetEndPoint( ptBaseEnd) ;
|
|
// cerco il lato chiuso lineare parallelo ad esso e distante circa dPocketSize
|
|
for ( int uu = 0 ; uu < pCrvCompo->GetCurveCount() && !bOK ; ++ uu) {
|
|
if ( uu == u ||
|
|
pCrvCompo->GetCurve( uu)->GetType() != CRV_LINE ||
|
|
pCrvCompo->GetCurve( uu)->GetTempProp() != 0)
|
|
continue ;
|
|
Vector3d vtSecondBaseDir ; pCrvCompo->GetCurve( uu)->GetStartDir( vtSecondBaseDir) ;
|
|
if ( ! AreOppositeVectorEpsilon( vtBaseDir, vtSecondBaseDir, 5 * EPS_SMALL))
|
|
continue ;
|
|
Point3d ptSecondBaseStart ; pCrvCompo->GetCurve( uu)->GetStartPoint( ptSecondBaseStart) ;
|
|
double dDist = 0. ;
|
|
DistPointCurve DPL( ptBaseStart, *pCrvCompo->GetCurve( uu), false) ;
|
|
if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) {
|
|
nBase = u ;
|
|
nSecondBase = uu ;
|
|
if ( ! PreparareTrapezoidTwoBases( pCrvCompo, dDiam, nType, nBase, nSecondBase, bOK, pCrvTrap))
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// la curva a trapezio deve evere almeno 4 lati
|
|
if ( pCrvTrap->GetCurveCount() < 4 || nBase == -1 || nSecondBase == -1) {
|
|
pCrvTrap->Clear() ;
|
|
return true ;
|
|
}
|
|
|
|
// ricostruisco il frame del trapezio
|
|
Point3d ptS ; pCrvTrap->GetStartPoint( ptS) ;
|
|
Vector3d vtS ; pCrvTrap->GetStartDir( vtS) ;
|
|
if ( ! frTrap.Set( ptS, Z_AX, vtS) || ! frTrap.IsValid())
|
|
return false ;
|
|
|
|
// se parametro MaxOptSize non compatibile => non è ottimizzato
|
|
if ( PockParams.dMaxOptSize > EPS_SMALL && dPocketSize > PockParams.dMaxOptSize)
|
|
pCrvTrap->Clear() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
SpecialAdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const bool bEvenClosed,
|
|
const ICurveComposite* pCrvPocket, const PocketParams& PockParams)
|
|
{
|
|
// parametri
|
|
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
|
|
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
|
|
double dRad = 0.5 * dDiam + dOffsR ;
|
|
|
|
// calcolo gli offset dei lati obliqui
|
|
PtrOwner<ICurveLine> pLineS( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 0 : 3)->Clone())) ;
|
|
pLineS->SimpleOffset( - dRad) ;
|
|
pLineS->Invert() ;
|
|
PtrOwner<ICurveLine> pLineE( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 2 : 1)->Clone())) ;
|
|
pLineE->SimpleOffset( - dRad) ;
|
|
pLineE->Invert() ;
|
|
|
|
Point3d ptS, ptE ;
|
|
pLineS->GetEndPoint( ptS) ;
|
|
pLineE->GetStartPoint( ptE) ;
|
|
Vector3d vtS, vtE ;
|
|
pLineS->GetStartDir( vtS) ;
|
|
pLineE->GetStartDir( vtE) ;
|
|
|
|
PtrOwner<ICurveLine> pLineLink( CreateCurveLine()) ;
|
|
pLineLink->Set( ptS, ptE) ;
|
|
|
|
pMCrv->Clear() ;
|
|
if ( ! pMCrv->AddCurve( Release( pLineS)))
|
|
return false ;
|
|
|
|
// creo raccordi
|
|
bool bUseBiArcs = false ;
|
|
Vector3d vtDir, vtDir2 ;
|
|
pLineLink->GetStartDir( vtDir) ;
|
|
pCrvPocket->GetCurve( bEvenClosed ? 1 : 0)->GetStartDir( vtDir2) ;
|
|
|
|
if ( Dist( ptS, ptE) > 500 * EPS_SMALL && AreSameOrOppositeVectorApprox( vtDir, vtDir2)) {
|
|
|
|
Point3d ptCrv1, ptCrv2 ;
|
|
pLineLink->GetPointD1D2( 0.3, ICurve::FROM_MINUS, ptCrv1) ;
|
|
pLineLink->GetPointD1D2( 0.7, ICurve::FROM_MINUS, ptCrv2) ;
|
|
|
|
// primo raccordo
|
|
double dAng ;
|
|
vtS.GetAngleXY( X_AX, dAng) ;
|
|
PtrOwner<ICurve> pBiArc1( GetBiArc( ptS, - dAng, ptCrv1, bEvenClosed ? 90 : 0, 0.5)) ;
|
|
|
|
// secondo raccordo
|
|
vtE.Invert() ;
|
|
vtE.GetAngleXY( X_AX, dAng) ;
|
|
PtrOwner<ICurve> pBiArc2( GetBiArc( ptE, - dAng, ptCrv2, bEvenClosed ? - 90 : 180, 0.5)) ;
|
|
pBiArc2->Invert() ;
|
|
|
|
if ( ! IsNull( pBiArc1) && ! IsNull( pBiArc2)) {
|
|
bUseBiArcs = true ;
|
|
pMCrv->AddCurve( Release( pBiArc1)) ;
|
|
pMCrv->AddLine( ptCrv2) ;
|
|
pMCrv->AddCurve( Release( pBiArc2)) ;
|
|
}
|
|
}
|
|
|
|
// se non è stato possibile creare raccordo, unisco linearmente
|
|
if ( ! bUseBiArcs)
|
|
pMCrv->AddCurve( Release( pLineLink)) ;
|
|
|
|
if ( ! pMCrv->AddCurve( Release( pLineE)))
|
|
return false ;
|
|
|
|
// setto temp prop per ricordare curve aggiuntive per pulire angoli
|
|
pMCrv->SetCurveTempProp( 0, 1) ;
|
|
pMCrv->SetCurveTempProp( pMCrv->GetCurveCount() - 1, 1) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------
|
|
static bool
|
|
CalcTrapezoidSpiralXCoord( const ICurveComposite* pCrvPocket, int nBase, int nSecondBase,
|
|
bool bStart, double dYCoord, double& dXCoord, double dPocketSize,
|
|
const PocketParams& PockParams)
|
|
{
|
|
// parametri
|
|
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
|
|
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
|
|
double dRad = 0.5 * dDiam + dOffsR ;
|
|
|
|
// recupero la curva di interesse
|
|
int nCrvId = ( bStart ? nSecondBase : nBase) + 1 ;
|
|
int nProp = - 1 ;
|
|
if ( ! pCrvPocket->GetCurveTempProp( nCrvId, nProp))
|
|
return false ;
|
|
// se open
|
|
if ( nProp == 1) {
|
|
Point3d pt1, pt2 ;
|
|
pCrvPocket->GetCurve( nCrvId)->GetStartPoint( pt1) ;
|
|
pCrvPocket->GetCurve( nCrvId)->GetEndPoint( pt2) ;
|
|
if ( bStart)
|
|
dXCoord = min( pt1.x, pt2.x) ;
|
|
else
|
|
dXCoord = max( pt1.x, pt2.x) ;
|
|
}
|
|
// se closed
|
|
else {
|
|
// creo la curva destra/sinistra
|
|
int nLast = bStart ? pCrvPocket->GetCurveCount() : nSecondBase ;
|
|
PtrOwner<ICurveComposite> pCrvSide( ConvertCurveToComposite( pCrvPocket->CopyParamRange( nCrvId, nLast))) ;
|
|
if ( IsNull( pCrvSide))
|
|
return false ;
|
|
// Offsetto la curva
|
|
OffsetCurve OffsCrv ;
|
|
if ( ! OffsCrv.Make( pCrvSide, - dRad, ICurve::OFF_FILLET))
|
|
return false ;
|
|
if ( OffsCrv.GetCurveCount() == 0) {
|
|
// controllo se avevo una circonferenza
|
|
if ( pCrvSide->GetCurveCount() == 1 && pCrvSide->GetFirstCurve()->GetType() == CRV_ARC) {
|
|
Point3d ptS ; pCrvSide->GetStartPoint( ptS) ;
|
|
dXCoord = ptS.x ;
|
|
}
|
|
else {
|
|
Point3d ptS ; pCrvSide->GetStartPoint( ptS) ;
|
|
Point3d ptE ; pCrvSide->GetEndPoint( ptE) ;
|
|
dXCoord = bStart ? max( ptS.x, ptE.x) + dRad : min( ptS.x, ptE.x) - dRad ;
|
|
}
|
|
}
|
|
else if ( OffsCrv.GetCurveCount() == 1) {
|
|
// controllo se la curva interseca la linea di svuotatura a YCoord
|
|
PtrOwner<ICurve> pCrvOffs( OffsCrv.GetLongerCurve()) ;
|
|
if ( IsNull( pCrvOffs))
|
|
return false ;
|
|
PtrOwner<ICurveLine> pLineMid( CreateCurveLine()) ;
|
|
if ( IsNull( pLineMid))
|
|
return false ;
|
|
pLineMid->Set( Point3d( - 3000, dYCoord, 0), Point3d( 3000, dYCoord, 0)) ;
|
|
IntersCurveCurve intCC( *pLineMid, *pCrvOffs) ;
|
|
IntCrvCrvInfo ccClass ;
|
|
if ( intCC.GetIntersCount() != 0) {
|
|
// se ho almeno una intersezione
|
|
if ( intCC.GetIntCrvCrvInfo( 0, ccClass))
|
|
dXCoord = ccClass.IciA[0].ptI.x ;
|
|
else
|
|
return false ;
|
|
}
|
|
else {
|
|
// se non ho intersezioni...
|
|
// prendo il box della curva
|
|
BBox3d Box3d ;
|
|
pCrvSide->GetLocalBBox( Box3d) ;
|
|
// creo la linea limitie verticale
|
|
PtrOwner<ICurveLine> pCrvVertLine( CreateCurveLine()) ;
|
|
if ( IsNull( pCrvVertLine))
|
|
return false ;
|
|
if ( bStart)
|
|
pCrvVertLine->SetPDL( Box3d.GetMax() + 5 * TOL_TRAPEZOID * Y_AX, - 90 , 2 * dPocketSize) ;
|
|
else
|
|
pCrvVertLine->SetPDL( Box3d.GetMin() - 5 * TOL_TRAPEZOID * Y_AX, 90, 2 * dPocketSize) ;
|
|
// intersechiamo
|
|
IntersCurveCurve intCC2( *pCrvVertLine, *pCrvSide) ;
|
|
if ( intCC2.GetOverlaps()) {
|
|
if ( bStart)
|
|
dXCoord = Box3d.GetMax().x + dRad ;
|
|
else
|
|
dXCoord = Box3d.GetMin().x - dRad ;
|
|
}
|
|
else {
|
|
dXCoord = bStart ? -INFINITO : INFINITO ;
|
|
for ( int i = 0 ; i < intCC2.GetIntersCount() ; ++ i) {
|
|
IntCrvCrvInfo ccClass2 ;
|
|
if ( intCC2.GetIntCrvCrvInfo( i, ccClass2)) {
|
|
if ( bStart)
|
|
dXCoord = max( dXCoord, Box3d.GetMax().x + sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ;
|
|
else
|
|
dXCoord = min( dXCoord, Box3d.GetMin().x - sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
AdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const ICurveComposite* pCrvPocket,
|
|
const PocketParams& PockParams, bool bStart)
|
|
{
|
|
// parametri
|
|
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
|
|
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
|
|
double dRad = 0.5 * dDiam + dOffsR ;
|
|
|
|
PtrOwner<ICurveComposite> pCompo( CreateCurveComposite()) ;
|
|
if ( ! bStart)
|
|
pMCrv->Invert() ;
|
|
|
|
Point3d ptTmp ;
|
|
pMCrv->GetStartPoint( ptTmp) ;
|
|
double dYCoord = ptTmp.y ; // quota verticale del percorso di svuotatura
|
|
pCrvPocket->GetCurve( 2)->GetStartPoint( ptTmp) ;
|
|
double dPocketSize = ptTmp.y ;
|
|
|
|
int nCrvId = ( bStart ? 3 : 1) ;
|
|
PtrOwner<ICurveLine> pLine( GetCurveLine( pCrvPocket->GetCurve( nCrvId)->Clone())) ;
|
|
pLine->SimpleOffset( - dRad) ;
|
|
|
|
Point3d ptP1, ptP2 ;
|
|
pLine->GetStartPoint( ptP1) ;
|
|
pLine->GetEndPoint( ptP2) ;
|
|
if ( ! bStart)
|
|
swap( ptP1, ptP2) ;
|
|
|
|
int nProp2 ;
|
|
// lato opposto a quello di riferimento aperto
|
|
if ( pCrvPocket->GetCurveTempProp( 2, nProp2) && nProp2 != 0) {
|
|
|
|
// caso 1 : pLine ha un estremo sopra e uno sotto il percorso di svuotatura pMCrv
|
|
if ( ptP2.y < dYCoord && dYCoord < ptP1.y) {
|
|
|
|
// creo tratto da ptP2 a ptP1
|
|
pCompo->AddPoint( ptP2) ;
|
|
pCompo->AddLine( ptP1) ;
|
|
|
|
// trovo il punto di pMCrv da cui ripartire per non lasciare aree residue
|
|
pLine->SimpleOffset( - 0.5 * dDiam) ;
|
|
pLine->ExtendStartByLen( EPS_SMALL) ;
|
|
pLine->ExtendEndByLen( EPS_SMALL) ;
|
|
IntersCurveCurve intCC( *pLine, *pCrvPocket) ;
|
|
if ( intCC.GetIntersCount() == 0)
|
|
return false ;
|
|
IntCrvCrvInfo aInfo ;
|
|
intCC.GetIntCrvCrvInfo( 0, aInfo) ;
|
|
double dDeltaX = sqrt( max( 0., dDiam * dDiam / 4 - dYCoord * dYCoord)) ;
|
|
if ( ! bStart)
|
|
dDeltaX *= -1 ;
|
|
Point3d ptCrv( aInfo.IciA[0].ptI.x + dDeltaX, dYCoord) ;
|
|
|
|
double dPar = 0 ;
|
|
if ( ! pMCrv->GetParamAtPoint( ptCrv, dPar)) {
|
|
dPar = 0.5 ;
|
|
pMCrv->GetPointD1D2( dPar, ICurve::FROM_MINUS, ptCrv) ;
|
|
}
|
|
if ( ! pMCrv->TrimStartAtParam( dPar))
|
|
pMCrv->Clear() ;
|
|
|
|
if ( ptP1.y > dPocketSize) {
|
|
// se ptP1 è esterno alla svuotatura scambio i punti in modo da usare ptP2 per il biarco ( così da non farlo fuoriuscire troppo)
|
|
swap( ptP1, ptP2) ;
|
|
pCompo->Invert() ;
|
|
}
|
|
|
|
// creo biarco fra ptP1 e ptCrv
|
|
Vector3d vtDir ;
|
|
pCompo->GetStartDir( vtDir) ;
|
|
double dAng ;
|
|
vtDir.GetAngleXY( X_AX, dAng) ;
|
|
PtrOwner<ICurve> pBiArc( GetBiArc( ptP1, - dAng, ptCrv, bStart ? 0 : 180, 0.8)) ;
|
|
|
|
bool bUseBiArc = false ;
|
|
if ( ! IsNull( pBiArc)) {
|
|
// verifico che con il biarco non si oltrepassi il lato obliquo chiuso della svuotatura
|
|
IntersCurveCurve intCC2( *pBiArc, *pCompo) ;
|
|
if ( intCC2.GetIntersCount() == 1)
|
|
bUseBiArc = true ;
|
|
}
|
|
if ( bUseBiArc)
|
|
pCompo->AddCurve( Release( pBiArc)) ;
|
|
else {
|
|
double dParLine = ( dYCoord - ptP2.y) / ( ptP1.y - ptP2.y) ;
|
|
Point3d ptOnLine = Media( ptP2, ptP1, dParLine) ;
|
|
pCompo->AddLine( ptOnLine) ;
|
|
pCompo->AddLine( ptCrv) ;
|
|
}
|
|
}
|
|
|
|
// caso 2 : pLine è completamente sopra/sotto la linea di svuotatura
|
|
else {
|
|
// se è sopra modifiche per ricondurmi al caso in cui è sotto
|
|
if ( ptP2.y > dYCoord) {
|
|
pLine->Invert() ;
|
|
swap( ptP1, ptP2) ;
|
|
}
|
|
|
|
// trovo l'intersezione fra il prolungamento della linea e pMCrv
|
|
if ( bStart)
|
|
pLine->ExtendStartByLen( 1000) ;
|
|
else
|
|
pLine->ExtendEndByLen( 1000) ;
|
|
IntersCurveCurve intCC ( *pLine, *pMCrv) ;
|
|
if ( intCC.GetIntersCount() == 0)
|
|
return false ;
|
|
IntCrvCrvInfo aInfo ;
|
|
intCC.GetIntCrvCrvInfo( 0, aInfo) ;
|
|
Point3d ptInt ;
|
|
ptInt = aInfo.IciA[0].ptI ;
|
|
double dPar = 0 ;
|
|
pMCrv->GetParamAtPoint( ptInt, dPar) ;
|
|
if ( ! pMCrv->TrimStartAtParam( dPar))
|
|
pMCrv->Clear() ;
|
|
|
|
pCompo->AddPoint( ptP2) ;
|
|
pCompo->AddLine( ptInt) ;
|
|
}
|
|
}
|
|
|
|
// se il lato opposto a quello di riferimento � chiuso bisogna distinguere i casi
|
|
else {
|
|
bool bStartPnt = false ;
|
|
if ( ptP2.y < dYCoord) {
|
|
bStartPnt = pCompo->AddPoint( ptP2) ;
|
|
}
|
|
else {
|
|
Vector3d vtDir ;
|
|
pLine->GetStartDir( vtDir) ;
|
|
if ( bStart)
|
|
vtDir.Invert() ;
|
|
Point3d ptP2N = ptP2 - dDiam / 2 * abs( vtDir.x / vtDir.y) * vtDir ;
|
|
if ( ptP2N.y < dYCoord)
|
|
bStartPnt = pCompo->AddPoint( ptP2N) ;
|
|
}
|
|
if ( bStartPnt) {
|
|
Point3d ptCrv ;
|
|
pMCrv->GetStartPoint( ptCrv) ;
|
|
pCompo->AddLine( ptCrv) ;
|
|
}
|
|
}
|
|
|
|
// setto temp prop per ricordare che è curva aggiuntiva per pulire angoli
|
|
for ( int i = 0 ; i < pCompo->GetCurveCount() ; i++)
|
|
pCompo->SetCurveTempProp( i, 1) ;
|
|
|
|
pMCrv->AddCurve( Release( pCompo), false) ;
|
|
if ( ! bStart)
|
|
pMCrv->Invert() ; // ripristino la direzione originaria
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double dPocketSize, int nBase,
|
|
int nSecondBase, const PocketParams& PockParams, ICurveComposite* pMCrv, ICurveComposite* pRCrv, bool& bOptimizedTrap)
|
|
{
|
|
// parametri
|
|
double dDiam = 2 * PockParams.dRad ;
|
|
double dOffsR = PockParams.dRadialOffset ;
|
|
|
|
bOptimizedTrap = false ;
|
|
Vector3d vtExtr ; pCrvPocket->GetExtrusion( vtExtr) ;
|
|
|
|
// recupero le temp prop
|
|
INTVECTOR vnProp( 4, 0) ;
|
|
if ( pCrvPocket->GetCurveCount() == 4) {
|
|
for ( int i = 0 ; i < 4 ; i ++)
|
|
pCrvPocket->GetCurveTempProp( i, vnProp[i]) ;
|
|
}
|
|
else {
|
|
vnProp[1] = pCrvPocket->GetCurve( 1)->GetTempProp() ;
|
|
vnProp[3] = pCrvPocket->GetCurve( nSecondBase + 1)->GetTempProp() ;
|
|
}
|
|
|
|
// passo in un sistema di riferimento locale avente asse X allineato con uno dei due lati paralleli
|
|
// (possibilmente aperto) e centro nel punto iniziale del lato
|
|
pCrvPocket->ToLoc( frTrap) ;
|
|
|
|
// calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso)
|
|
double dLen0, dLen1, dLen2, dLen3, dMaxLarg = 0. ;
|
|
pCrvPocket->GetCurve( nBase)->GetLength( dLen0) ;
|
|
pCrvPocket->GetCurve( nSecondBase)->GetLength( dLen2) ;
|
|
bool bRealTrap = pCrvPocket->GetCurveCount() == 4 ;
|
|
for ( int i = 0 ; i < 4 && bRealTrap ; ++ i)
|
|
bRealTrap = pCrvPocket->GetCurve( i)->GetType() == CRV_LINE ;
|
|
if ( bRealTrap) {
|
|
pCrvPocket->GetCurve( nBase + 1)->GetLength( dLen1) ;
|
|
pCrvPocket->GetCurve( nSecondBase + 1)->GetLength( dLen3) ;
|
|
// calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso)
|
|
Point3d ptOrig ; pCrvPocket->GetCurve( 1)->GetStartPoint( ptOrig) ;
|
|
Vector3d vtX ; pCrvPocket->GetCurve( 1)->GetStartDir( vtX) ;
|
|
Frame3d frDim ; frDim.Set( ptOrig, Z_AX, vtX) ; frDim.Invert() ;
|
|
BBox3d b3Dim ; pCrvPocket->GetBBox( frDim, b3Dim, BBF_EXACT) ;
|
|
dMaxLarg = ( vnProp[0] != 0 ? b3Dim.GetDimY() : dPocketSize) ;
|
|
}
|
|
|
|
|
|
// calcolo percorso di svuotatura
|
|
// se lati obliqui sono entrambi chiusi e dimensione svuotatura è maggiore di diametro fresa e minore del doppio gestione speciale
|
|
if (( bRealTrap && dMaxLarg > PockParams.dRad * 2 + 10 * EPS_SMALL) &&
|
|
((( vnProp[0] != 0 && vnProp[2] != 0) && ( vnProp[3] == 0 && vnProp[1] == 0) && ( max( dLen0, dLen2) < 2 * dDiam + EPS_SMALL)) ||
|
|
(( vnProp[1] != 0 && vnProp[3] != 0) && ( vnProp[0] == 0 && vnProp[2] == 0) && ( max( dLen1, dLen3) < 2 * dDiam + EPS_SMALL)))) {
|
|
if ( ! SpecialAdjustTrapezoidSpiralForAngles( pMCrv, vnProp[0] == 0, pCrvPocket, PockParams)) {
|
|
pMCrv->Clear() ;
|
|
return false ;
|
|
}
|
|
}
|
|
else {
|
|
// trovo la quota Y per centro del Tool
|
|
double dYCoord ;
|
|
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) // se base principale chiusa
|
|
dYCoord = 0.5 * dDiam + dOffsR ;
|
|
else if ( pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 0) // se base principale aperta e secondaria chiusa
|
|
dYCoord = dPocketSize - 0.5 * dDiam - dOffsR ;
|
|
else // se entrambi i lati paralleli sono aperti mi posiziono a metà della svuotatura
|
|
dYCoord = 0.5 * dPocketSize ;
|
|
|
|
double dXCoordStart, dXCoordEnd ;
|
|
if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, true, dYCoord, dXCoordStart,
|
|
dPocketSize, PockParams))
|
|
return false ;
|
|
if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, false, dYCoord, dXCoordEnd,
|
|
dPocketSize, PockParams))
|
|
return false ;
|
|
if ( dXCoordStart > dXCoordEnd + 500 * EPS_SMALL)
|
|
return false ;
|
|
Point3d ptStart( dXCoordStart, dYCoord) ;
|
|
Point3d ptEnd( dXCoordEnd, dYCoord) ;
|
|
|
|
if ( bRealTrap && AreSamePointEpsilon( ptStart, ptEnd, 500 * EPS_SMALL) && ( vnProp[0] != 0 || vnProp[2] != 0)) {
|
|
Vector3d vtDir1, vtDir3 ;
|
|
pCrvPocket->GetCurve( 1)->GetStartDir( vtDir1) ;
|
|
pCrvPocket->GetCurve( 3)->GetStartDir( vtDir3) ;
|
|
// gestisco il caso speciale di un parallelogramma in cui anche l'altra dimensione della svuotatura è pari al diametro utensile
|
|
if ( AreOppositeVectorApprox( vtDir1, vtDir3)) {
|
|
PtrOwner<ICurveLine> pLine1( GetCurveLine( pCrvPocket->GetCurve( 1)->Clone())) ;
|
|
PtrOwner<ICurveLine> pLine3( GetCurveLine( pCrvPocket->GetCurve( 3)->Clone())) ;
|
|
if ( IsNull( pLine1) || IsNull( pLine3))
|
|
return false ;
|
|
if ( ! pLine1->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset) ||
|
|
! pLine3->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset))
|
|
return false ;
|
|
|
|
Point3d ptS, ptE ;
|
|
if ( vtDir3 * X_AX > EPS_SMALL) {
|
|
pLine1->GetStartPoint( ptS) ;
|
|
pLine3->GetStartPoint( ptE) ;
|
|
}
|
|
else {
|
|
pLine1->GetEndPoint( ptE) ;
|
|
pLine3->GetEndPoint( ptS) ;
|
|
}
|
|
|
|
if ( vnProp[0] != 0) {
|
|
pMCrv->AddPoint( ptS) ;
|
|
if ( vnProp[2] != 0)
|
|
pMCrv->AddLine( ptE) ;
|
|
else
|
|
pMCrv->AddLine( ptStart) ;
|
|
}
|
|
else {
|
|
pMCrv->AddPoint( ptE) ;
|
|
if ( vnProp[0] != 0)
|
|
pMCrv->AddLine( ptS) ;
|
|
else
|
|
pMCrv->AddLine( ptStart) ;
|
|
pMCrv->Invert() ;
|
|
}
|
|
|
|
pMCrv->SetCurveTempProp( 0, 1) ;
|
|
}
|
|
}
|
|
else {
|
|
if ( ! pMCrv->AddPoint( ptStart))
|
|
return true ;
|
|
if ( ! pMCrv->AddLine( ptEnd))
|
|
return true ;
|
|
|
|
// aggiustamenti al percorso per rimuovere materiale residuo negli angoli
|
|
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 1 ||
|
|
pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 1) {
|
|
|
|
Frame3d frOpen ;
|
|
bool bSwitch = false ;
|
|
// Base principale chiusa e base Secondaria aperta
|
|
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) {
|
|
pCrvPocket->ChangeStartPoint( nSecondBase) ;
|
|
// oriento il Frame ( ho sempre l'origine nell'estremo superiore del lato aperto)
|
|
Vector3d vtDir ; pCrvPocket->GetStartDir( vtDir) ;
|
|
Point3d ptORIG ;
|
|
if ( vtDir.y > 0)
|
|
pCrvPocket->GetCurve( nBase)->GetEndPoint( ptORIG) ;
|
|
else
|
|
pCrvPocket->GetCurve( nBase)->GetStartPoint( ptORIG) ;
|
|
frOpen.Set( ptORIG, Z_AX, -X_AX) ;
|
|
if ( ! frOpen.IsValid())
|
|
return false ;
|
|
pCrvPocket->ToLoc( frOpen) ;
|
|
pMCrv->ToLoc( frOpen) ;
|
|
pMCrv->Invert() ;
|
|
bSwitch = true ;
|
|
}
|
|
|
|
if ( pCrvPocket->GetCurve( 3)->GetTempProp( 0) == 0 &&
|
|
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, true)) {
|
|
pMCrv->Clear() ;
|
|
return false ;
|
|
}
|
|
if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == 0 &&
|
|
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, false)) {
|
|
pMCrv->Clear() ;
|
|
return false ;
|
|
}
|
|
|
|
if ( bSwitch) {
|
|
pCrvPocket->ToGlob( frOpen) ;
|
|
pMCrv->ToGlob( frOpen) ;
|
|
pMCrv->Invert() ;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
return true ;
|
|
|
|
pMCrv->ToGlob( frTrap) ;
|
|
|
|
if ( ! PockParams.bInvert) {
|
|
pMCrv->Invert() ;
|
|
// inverto le proprietà in modo che nProp3 sia sempre legata al punto iniziale e nProp1 a quello finale
|
|
swap( vnProp[1], vnProp[3]) ;
|
|
}
|
|
// segno i lati aperti come temp prop della curva
|
|
int nOpenEdges = vnProp[0] + vnProp[1] * 2 + vnProp[2] * 4 + vnProp[3] * 8 ;
|
|
pMCrv->SetTempProp( nOpenEdges, 0) ;
|
|
pMCrv->SetExtrusion( vtExtr) ;
|
|
|
|
bOptimizedTrap = true ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ModifyCurveToSmoothed( ICurveComposite* pCrv, const PocketParams& PockParams, double dRightLen, double dLeftLen, bool bAsParam)
|
|
{
|
|
// se non richiesto non faccio nulla
|
|
if ( ! PockParams.bSmooth)
|
|
return true ;
|
|
|
|
// controllo parametri
|
|
if ( pCrv == nullptr || dLeftLen < EPS_SMALL || dRightLen < EPS_SMALL)
|
|
return false ;
|
|
|
|
ICURVEPOVECTOR vCrvStepsToFill ; // vettore delle Curve da raccordare (curve della composita)
|
|
ICURVEPOVECTOR vCrvArcs ; // vettore contenente gli Archi (tra le curve della composita)
|
|
INTVECTOR vArcsToJump ; // vettore di indici per gli archi saltati (nel caso non si riesca a raccordare ...)
|
|
|
|
// se nella composita ho meno di due curve allora non c'è nulla da raccordare
|
|
const ICurve* pMyCrv = pCrv->GetFirstCurve() ;
|
|
while ( pMyCrv != nullptr) {
|
|
vCrvStepsToFill.emplace_back( pMyCrv->Clone()) ;
|
|
pMyCrv = pCrv->GetNextCurve() ;
|
|
}
|
|
if ( vCrvStepsToFill.size() < 2)
|
|
return true ;
|
|
|
|
// controllo se la curva è chiusa ( nel caso inserisco nel vettore la prima curva due volte, all'inizio e alla fine)
|
|
if ( pCrv->IsClosed()) {
|
|
const ICurve* pMyCrvFirst = pCrv->GetFirstCurve() ;
|
|
if ( pMyCrvFirst == nullptr)
|
|
return false ;
|
|
vCrvStepsToFill.emplace_back( pMyCrvFirst->Clone()) ;
|
|
}
|
|
|
|
double dUE_ref, dUS_ref, dRadius, dPar1, dPar2 ;
|
|
// riempio il vettore degli archi, scorro tutte le curve da raccordare ( la i-esima e la (i+1)-esima)
|
|
for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) {
|
|
|
|
// controllo che le curve non siano già tangenti
|
|
Vector3d vtS, vtE ;
|
|
if ( ! vCrvStepsToFill[i]->GetEndDir( vtS) || ! vCrvStepsToFill[i+1]->GetStartDir( vtE))
|
|
continue ;
|
|
if ( AreSameVectorApprox( vtS, vtE)) {
|
|
vCrvArcs.emplace_back( CreateCurveComposite()) ; // arco nullo
|
|
vArcsToJump.push_back( i) ; // da scartare
|
|
continue ;
|
|
}
|
|
|
|
// ricavo i valori parametrici associati
|
|
double dLen_act ; vCrvStepsToFill[i]->GetLength( dLen_act) ;
|
|
double dLen_succ ; vCrvStepsToFill[i+1]->GetLength( dLen_succ) ;
|
|
double dUE_ref = 1. ;
|
|
if ( dLen_act > dLeftLen)
|
|
dUE_ref -= dLeftLen / dLen_act ;
|
|
double dUS_ref = 0. ;
|
|
if ( dLen_succ > dRightLen)
|
|
dUS_ref = dRightLen / dLen_succ ;
|
|
|
|
// se valori trattati come parametri...
|
|
if ( bAsParam) {
|
|
// ... cerco i parametri corrispondenti sulle due curve
|
|
double dU_cm_S = 0 ; double dULast1 = 1 ; double dULast2 = 1 ;
|
|
vCrvStepsToFill[i]->GetDomain( dU_cm_S, dULast1) ;
|
|
dUE_ref = ( 1 - dRightLen) * dULast1 ;
|
|
vCrvStepsToFill[i+1]->GetDomain( dU_cm_S, dULast2) ;
|
|
dUS_ref = dLeftLen * dULast2 ;
|
|
}
|
|
|
|
|
|
// prendo i punti sulle due curve rispetto a tali parametri
|
|
Point3d ptS, ptE ;
|
|
if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) ||
|
|
! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE))
|
|
return false ;
|
|
|
|
dRadius = Dist( ptS, ptE) ; // uso come raggio la distanza tra i due punti ( limite superiore valido)
|
|
|
|
int nMaxTestForArcs = 3 ; // tentativi per creare l'arco
|
|
int nIterForArcs = 0 ; // numero di intersezioni con altre curve
|
|
bool IntersBTWArcs = false ; // caso particolare in cui due archi di raccordo si intersecano causa curva piccola
|
|
|
|
// creo l'arco di raccordo e controllo che non sia troppo piccolo
|
|
PtrOwner<ICurveArc> pCrvArc( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ;
|
|
double dArcLen ;
|
|
if ( ! IsNull( pCrvArc) && pCrvArc->IsValid() && ( ! pCrvArc->GetLength( dArcLen) || dArcLen < 5 * EPS_SMALL)) {
|
|
vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco nullo
|
|
vArcsToJump.push_back( i) ; // da scartare
|
|
continue ;
|
|
}
|
|
|
|
// dal secondo arco in poi controllo che non ci siano intersezioni tra essi
|
|
if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) {
|
|
IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ;
|
|
if ( intCCH.GetIntersCount() > 0 )
|
|
IntersBTWArcs = true ;
|
|
}
|
|
|
|
// se ho intersezioni tra archi o l'arco creato non è valido, allora provo altre nMaxTestForArcs volte a ricrearlo avvicinando i punti
|
|
while (( IsNull( pCrvArc) || IntersBTWArcs) && nIterForArcs < nMaxTestForArcs) {
|
|
|
|
dUE_ref = ( 1 + dUE_ref ) * 0.5 ;
|
|
dUS_ref = dUS_ref * 0.5 ;
|
|
|
|
if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) ||
|
|
! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE))
|
|
return false ;
|
|
|
|
dRadius = Dist( ptS, ptE) ;
|
|
|
|
pCrvArc.Set( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ;
|
|
nIterForArcs++ ;
|
|
|
|
IntersBTWArcs = false ;
|
|
if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) { // dal secondo arco in poi controllo che non ci siano intersezioni tra essi
|
|
IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ;
|
|
if ( intCCH.GetIntersCount() > 0)
|
|
IntersBTWArcs = true ;
|
|
}
|
|
|
|
}
|
|
if ( IsNull( pCrvArc) || ! pCrvArc->IsValid()) { // se ancora non riesco... salto l'arco
|
|
vCrvArcs.emplace_back( CreateCurveArc()) ; // arco nullo
|
|
vArcsToJump.push_back( i) ; // da scartare
|
|
continue ;
|
|
}
|
|
|
|
// se ho creato l'arco lo memorizzo
|
|
vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco valido
|
|
vArcsToJump.push_back( -1) ; // da considerare
|
|
}
|
|
|
|
// creo la curva che restituirò
|
|
PtrOwner<CurveComposite> pCrvCO_temp( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCrvCO_temp))
|
|
return false ;
|
|
Point3d ptArcHelp, ptFirstPoint ;
|
|
|
|
// unisco la curva i-esima con l'arco i-esimo (non guardo l'ultima curva nel vettore, controllo dopo il caso di curva chiusa)
|
|
for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) {
|
|
|
|
if ( vArcsToJump[i] == -1) { // se esiste l'arco ...
|
|
Point3d ptArcS, ptArcE ;
|
|
if ( ! vCrvArcs[i]->GetStartPoint( ptArcS) ||
|
|
! vCrvArcs[i]->GetEndPoint( ptArcE) ||
|
|
! vCrvStepsToFill[i]->GetParamAtPoint( ptArcS, dUE_ref) ||
|
|
! vCrvStepsToFill[i+1]->GetParamAtPoint( ptArcE, dUS_ref))
|
|
return false ;
|
|
|
|
if ( i == 0) { // ... e sono nella prima iterazione ...
|
|
if ( ! pCrv->IsClosed()) { // ... e se la curva è aperta -> la inserisco nel nuovo percorso
|
|
if ( ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref))))
|
|
return false ;
|
|
}
|
|
ptFirstPoint = ptArcS ;
|
|
// ... e se la curva è chiusa -> non la inserisco nel nuovo percorso
|
|
}
|
|
else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...)
|
|
if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref))))
|
|
return false ;
|
|
// aggiungo la curva 'tagliata per il raccordo'
|
|
}
|
|
|
|
if ( ! pCrvCO_temp->AddCurve( vCrvArcs[i]->Clone())) // aggiungo l'arco di raccordo
|
|
return false ;
|
|
ptArcHelp = ptArcE ;
|
|
}
|
|
else { // se non esiste l'arco ...
|
|
if ( i == 0 ) { // e sono nella prima iterazione...
|
|
if ( ! vCrvStepsToFill[0]->GetEndPoint( ptArcHelp))
|
|
return false ;
|
|
|
|
if ( ! pCrv->IsClosed()) { // ...e se aperta // aggiungo la prima curva per intero
|
|
if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref))))
|
|
return false ;
|
|
}
|
|
ptFirstPoint = ptArcHelp ;
|
|
}
|
|
else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...)
|
|
double dUS_cm ;
|
|
if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! vCrvStepsToFill[i]->GetDomain( dUS_cm, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref))) ||
|
|
! vCrvStepsToFill[i]->GetEndPoint( ptArcHelp))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// ultima curva...
|
|
if ( pCrv->IsClosed()) { // se curva chiusa...
|
|
if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! vCrvStepsToFill[0]->GetParamAtPoint( ptFirstPoint, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( dUS_ref, dUE_ref))))
|
|
return false ;
|
|
}
|
|
else { // se curva aperta... ( non ha importanza l'esistenza o meno degli archi...)
|
|
double dUS_cm ;
|
|
if ( ! vCrvStepsToFill.back()->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! vCrvStepsToFill.back()->GetDomain( dUS_cm, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill.back()->CopyParamRange( dUS_ref, dUE_ref))))
|
|
return false ;
|
|
}
|
|
|
|
// ripristino il punto inziale se la curva è chiusa
|
|
double dNewDU ;
|
|
if ( ! pCrv->IsClosed()) {
|
|
if ( ! pCrvCO_temp->GetParamAtPoint( ptArcHelp, dNewDU))
|
|
return false ;
|
|
pCrvCO_temp->ChangeStartPoint( dNewDU) ;
|
|
}
|
|
|
|
// restituisco
|
|
pCrv->Clear() ;
|
|
pCrv->AddCurve( Release( pCrvCO_temp)) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, const ICRVCOMPOPOVECTOR& vOffIslands,
|
|
ICurveComposite* pCrvLink)
|
|
{
|
|
// pulisco
|
|
pCrvLink->Clear() ;
|
|
|
|
// recupero il vettore estrusione dal bordo esterno offsettato della superficie
|
|
Vector3d vtExtr ;
|
|
vOffIslands[0]->GetExtrusion( vtExtr) ;
|
|
|
|
// determino il riferimento naturale della svuotatura ( OCS con il vettore estrusione come asse Z)
|
|
Frame3d frLoc ;
|
|
frLoc.Set( ORIG, vtExtr) ;
|
|
|
|
// non serve collegare ( può capitare nel tagliare percorsi con isole complesse)
|
|
if ( AreSamePointApprox( ptStart, ptEnd))
|
|
return true ;
|
|
|
|
// porto la curva di contenimento in locale a questo riferimento
|
|
vector<CurveLocal> vOffsExtr ;
|
|
for ( int i = 0 ; i < int( vOffIslands.size()) ; ++ i) {
|
|
CurveLocal CrvOutLoc( vOffIslands[i], GLOB_FRM, frLoc) ;
|
|
vOffsExtr.push_back( CrvOutLoc) ;
|
|
}
|
|
|
|
// creo la retta che li unisce
|
|
PtrOwner<CurveComposite> pCompoLine( CreateBasicCurveComposite()) ;
|
|
if ( ! pCompoLine->AddPoint( ptStart) || ! pCompoLine->AddLine( ptEnd))
|
|
return false ;
|
|
pCompoLine->SetExtrusion( vtExtr) ;
|
|
// la porto in locale al riferimento della svuotatura
|
|
CurveLocal LineLoc( pCompoLine, GLOB_FRM, frLoc) ; // ... per le intersezioni
|
|
|
|
// creo la nuova curva formata dai tratti di linee INTERNI alle isole e dai tratti sul bordo degli offset
|
|
PtrOwner<CurveComposite> pCompo( pCompoLine->Clone()) ;
|
|
if ( IsNull( pCompo) )
|
|
return false ;
|
|
if ( ! pCompo->LocToLoc( GLOB_FRM, frLoc) )
|
|
return false;
|
|
|
|
// memorizzo il tratto lineare nel caso qualche operazione fallisse
|
|
PtrOwner<CurveComposite> pCompoHelp( pCompoLine->Clone()) ;
|
|
if ( IsNull( pCompoHelp))
|
|
return false ;
|
|
if ( ! pCompoHelp->LocToLoc( GLOB_FRM, frLoc))
|
|
return false ;
|
|
|
|
// scorro il vettore degli indici degli offset delle isole intersecati
|
|
for ( int i = 0 ; i < int( vOffIslands.size()) ; i++) {
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pCompo, *vOffIslands[i]) ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
if ( ! pCompoHelp->Clear())
|
|
return false ;
|
|
// per ogni intersezione j con l'offset dell'isola i
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++j ) {
|
|
// se ho intersezione spezzo il segmento
|
|
if ( ccClass[j].nClass == CRVC_OUT) {
|
|
Point3d ptS ;
|
|
pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ;
|
|
double dOffS ;
|
|
vOffIslands[i]->GetParamAtPoint( ptS, dOffS) ;
|
|
Point3d ptE ;
|
|
pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ;
|
|
double dOffE ;
|
|
vOffIslands[i]->GetParamAtPoint( ptE, dOffE) ;
|
|
// recupero i due possibili percorsi e uso il più corto
|
|
PtrOwner<ICurve> pCrvA( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ;
|
|
PtrOwner<ICurve> pCrvB( vOffIslands[i]->CopyParamRange( dOffE, dOffS)) ;
|
|
if ( IsNull( pCrvA) || IsNull( pCrvB))
|
|
return false ;
|
|
double dLenA ; pCrvA->GetLength( dLenA) ;
|
|
double dLenB ; pCrvB->GetLength( dLenB) ;
|
|
if ( dLenA < dLenB) {
|
|
pCompoHelp->AddCurve( Release( pCrvA)) ;
|
|
}
|
|
else {
|
|
pCrvB->Invert() ;
|
|
pCompoHelp->AddCurve( Release( pCrvB)) ;
|
|
}
|
|
}
|
|
// se non interseco
|
|
else {
|
|
pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
|
|
}
|
|
}
|
|
|
|
pCompo->Clear() ;
|
|
pCompo->AddCurve( pCompoHelp->Clone()) ;
|
|
}
|
|
|
|
// riporto pCompo nel sistema di riferimento originale
|
|
if ( ! pCompo->LocToLoc( frLoc, GLOB_FRM))
|
|
return false ;
|
|
pCrvLink->AddCurve( Release( pCompo)) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static bool
|
|
ModifyBiArc( ICurve* pBiArcLink, double dToll, ICurveComposite* pNewBiArc)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
if ( pBiArcLink == nullptr)
|
|
return false ;
|
|
if ( dToll > 0.99 || dToll < 0.01)
|
|
return false ;
|
|
pNewBiArc->Clear() ;
|
|
|
|
// dominio
|
|
double dUS, dUE ;
|
|
pBiArcLink->GetDomain( dUS, dUE) ;
|
|
|
|
// prendo i due archi della curva BiArco
|
|
PtrOwner<ICurve> pArc1( pBiArcLink->CopyParamRange( dUS, ( dUS + dUE) / 2)) ;
|
|
PtrOwner<ICurve> pArc2( pBiArcLink->CopyParamRange(( dUS + dUE ) / 2, dUE)) ;
|
|
if ( IsNull( pArc1) || ! pArc1->IsValid() || IsNull( pArc2) || ! pArc2->IsValid())
|
|
return false ;
|
|
|
|
// primo pezzo
|
|
pArc1->GetDomain( dUS, dUE) ;
|
|
PtrOwner<ICurve> pArc1A( pArc1->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1
|
|
PtrOwner<ICurve> pArc1B( pArc1->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2
|
|
Point3d pt1A, pt1B ;
|
|
pArc1A->GetEndPoint( pt1A) ;
|
|
pArc1B->GetStartPoint( pt1B) ;
|
|
PtrOwner<CurveLine> pLine1( CreateBasicCurveLine()) ;
|
|
pLine1->Set( pt1A, pt1B) ;
|
|
|
|
// secondo pezzo
|
|
pArc2->GetDomain( dUS, dUE) ;
|
|
PtrOwner<ICurve> pArc2A( pArc2->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1
|
|
PtrOwner<ICurve> pArc2B( pArc2->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2
|
|
Point3d pt2A, pt2B ;
|
|
pArc2A->GetEndPoint( pt2A) ;
|
|
pArc2B->GetStartPoint( pt2B) ;
|
|
PtrOwner<CurveLine> pLine2( CreateBasicCurveLine()) ;
|
|
pLine2->Set( pt2A, pt2B) ;
|
|
|
|
// ricostruisco il nuovo link
|
|
if ( ! pNewBiArc->AddCurve( Release( pArc1A)) ||
|
|
! pNewBiArc->AddCurve( Release( pLine1)) ||
|
|
! pNewBiArc->AddCurve( Release( pArc1B)) ||
|
|
! pNewBiArc->AddCurve( Release( pArc2A)) ||
|
|
! pNewBiArc->AddCurve( Release( pLine2)) ||
|
|
! pNewBiArc->AddCurve( Release( pArc2B)))
|
|
return false ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static bool
|
|
CalcBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd,
|
|
const Vector3d& vtEnd, double dParMeet, const ICRVCOMPOPOVECTOR& vOffIslands,
|
|
const PocketParams& PockParams, ICurveComposite* pCrvLink)
|
|
{
|
|
// se senza smusso, ritorno tratto lineare
|
|
if ( ! PockParams.bSmooth)
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
|
|
// creo il BiArc che unisce i due punti
|
|
double dAngStart, dAngEnd ;
|
|
vtStart.GetAngleXY( X_AX, dAngStart) ;
|
|
vtEnd.GetAngleXY( X_AX, dAngEnd) ;
|
|
PtrOwner<ICurve> pBiArcLink ;
|
|
if ( dParMeet != 0)
|
|
pBiArcLink.Set( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, dParMeet)) ;
|
|
else {
|
|
PtrOwner<CurveArc> pCrvCir( CreateBasicCurveArc()) ;
|
|
if ( IsNull( pCrvCir))
|
|
return false ;
|
|
pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, PockParams.bInvert ? 360 : - 360, 0, Z_AX) ;
|
|
pBiArcLink.Set( pCrvCir->Clone()) ;
|
|
pCrvLink->AddCurve( pBiArcLink->Clone()) ;
|
|
return true ;
|
|
}
|
|
if ( IsNull( pBiArcLink))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
|
|
// se BiArco troppo grande allora lo modifico
|
|
double dLenBiArc ;
|
|
pBiArcLink->GetLength( dLenBiArc) ;
|
|
if ( dLenBiArc > 200 * 1000 * EPS_SMALL) {
|
|
PtrOwner<CurveComposite> pCrvNewBiArcS( CreateBasicCurveComposite()) ;
|
|
ModifyBiArc( pBiArcLink, 0.2, pCrvNewBiArcS) ;
|
|
pBiArcLink.Set( pCrvNewBiArcS) ;
|
|
}
|
|
|
|
// creo la nuova curva formata inizialmente dai tratti di archi
|
|
PtrOwner<ICurveComposite> pCompo( GetCurveComposite( pBiArcLink->Clone())) ;
|
|
if ( IsNull( pCompo))
|
|
return false ;
|
|
PtrOwner<ICurveComposite> pCompoHelp( GetCurveComposite( pBiArcLink->Clone())) ;
|
|
if ( IsNull( pCompoHelp))
|
|
return false ;
|
|
|
|
// scorro tutte le curve per controllare le intersezioni ...
|
|
for ( int i = 0 ; i < int( vOffIslands.size()) ; ++ i) {
|
|
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pCompo, *vOffIslands[i]) ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
if ( ! pCompoHelp->Clear())
|
|
return false ;
|
|
|
|
// per ogni intersezione j con l'offset dell'isola i
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
// se ho intersezione spezzo il segmento
|
|
if ( ccClass[j].nClass == CRVC_OUT && int( ccClass.size()) > 1) {
|
|
Point3d ptS ;
|
|
pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ;
|
|
double dOffS ;
|
|
vOffIslands[i]->GetParamAtPoint( ptS, dOffS, 1500 * EPS_SMALL) ;
|
|
Point3d ptE ;
|
|
pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ;
|
|
double dOffE ;
|
|
vOffIslands[i]->GetParamAtPoint( ptE, dOffE, 1500 * EPS_SMALL) ;
|
|
// recupero i due possibili percorsi e uso il più corto
|
|
PtrOwner<ICurve> pCrvA( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ;
|
|
PtrOwner<ICurve> pCrvB( vOffIslands[i]->CopyParamRange( dOffE, dOffS)) ;
|
|
|
|
if ( IsNull( pCrvA) || IsNull( pCrvB))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
|
|
double dLenA ; pCrvA->GetLength( dLenA) ;
|
|
double dLenB ; pCrvB->GetLength( dLenB) ;
|
|
|
|
if ( j != 0) {
|
|
if ( dLenA < dLenB) {
|
|
if ( ! pCompoHelp->AddCurve( Release( pCrvA)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
}
|
|
else {
|
|
pCrvB->Invert() ;
|
|
if ( ! pCompoHelp->AddCurve( Release( pCrvB)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
}
|
|
}
|
|
else {
|
|
pCrvB->Invert() ;
|
|
if ( ! pCompoHelp->AddCurve( Release( pCrvB)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
}
|
|
}
|
|
// se non interseco
|
|
else {
|
|
if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
}
|
|
}
|
|
|
|
pCompo->Clear() ;
|
|
if ( ! pCompo->AddCurve( pCompoHelp->Clone()))
|
|
if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink))
|
|
return false ;
|
|
}
|
|
|
|
// se il BiArco è troppo piccolo allora lo approssimo con un segmento
|
|
BBox3d bBox3 ;
|
|
if ( pCompo->GetLocalBBox( bBox3)) {
|
|
double dRadBB ;
|
|
if ( bBox3.GetRadius( dRadBB) && dRadBB < 500 * EPS_SMALL) {
|
|
if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink))
|
|
return false ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
PtrOwner<ICurveComposite> pCrvCompo_noSmooth( pCompo->Clone()) ;
|
|
if ( ! ModifyCurveToSmoothed( pCompo, PockParams, 0.05, 0.05, true) ||
|
|
! pCompo->IsValid())
|
|
pCompo.Set( pCrvCompo_noSmooth) ;
|
|
pCrvLink->AddCurve( Release( pCompo)) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, const ICRVCOMPOPOVECTOR& vOffsCL,
|
|
const ICRVCOMPOPOVECTOR& vFirstOffset, const PocketParams& PockParams, ICurveComposite* pCrvLink,
|
|
double dLenPercS, double dLenPercE, int nMaxIter)
|
|
{
|
|
// controllo i parametri
|
|
if ( pCrvS == nullptr || pCrvE == nullptr )
|
|
return false ;
|
|
pCrvLink->Clear() ;
|
|
|
|
// curva finale da restituire
|
|
PtrOwner<CurveComposite> ptCrvFinal( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( ptCrvFinal))
|
|
return false ;
|
|
|
|
// Prendo i punti, i vettori tangenti e i parametri di essi per le curve
|
|
Point3d ptSS, ptSE, ptES, ptEE ;
|
|
Vector3d vS, vE ;
|
|
double dUSS, dUSE, dUES, dUEE, dLenS, dLenE ;
|
|
dUSS = 0 ; dUSE = 0 ;
|
|
pCrvS->GetEndPoint( ptSE) ;
|
|
pCrvS->GetStartPoint( ptSS) ;
|
|
pCrvE->GetStartPoint( ptES) ;
|
|
pCrvE->GetEndPoint( ptEE) ;
|
|
pCrvS->GetEndDir( vS) ;
|
|
pCrvE->GetStartDir( vE) ;
|
|
pCrvS->GetDomain( dUSS, dUSE) ;
|
|
pCrvE->GetDomain( dUES, dUEE) ;
|
|
pCrvS->GetLength( dLenS) ;
|
|
pCrvE->GetLength( dLenE) ;
|
|
|
|
// se ho una curva di primo Offset allora non devo accorciarla ... ( si per bordi esterni che per isole)
|
|
for ( int i = 0 ; i < int( vFirstOffset.size()) ; ++ i) {
|
|
if ( vFirstOffset[i]->IsPointOn( ptSS) && vFirstOffset[i]->IsPointOn( ptSE))
|
|
dLenPercS = 0 ;
|
|
if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE))
|
|
dLenPercE = 0 ;
|
|
}
|
|
double dLStepS = ( dLenPercS < EPS_SMALL ? 0. : PockParams.dRad) ;
|
|
double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : PockParams.dRad) ;
|
|
dLenE = 0 ;
|
|
|
|
// calcolo i possibili BiArchi tra le due curve
|
|
int nIter = 0 ;
|
|
|
|
if ( ! PockParams.bSmooth)
|
|
nMaxIter = 1 ;
|
|
|
|
// taglio la curva al massimo nMaxIter-volte e mi fermo al taglio più opportuno
|
|
while ( nIter < nMaxIter) {
|
|
|
|
// calcolo il BiArco
|
|
PtrOwner<CurveComposite> ptBiArc( CreateBasicCurveComposite()) ;
|
|
if ( ! CalcBoundedSmoothedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, PockParams, ptBiArc) ||
|
|
ptBiArc->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
ptCrvFinal->Clear() ;
|
|
ptCrvFinal.Set( ptBiArc->Clone()) ;
|
|
|
|
if ( dLenPercE == 0 && dLenPercS == 0 )
|
|
break ;
|
|
|
|
// se il BiArco creato interseca le altre curve di Offset allora mi fermo...
|
|
bool bInterr = false ;
|
|
for ( int i = 0 ; i < int( vOffsCL.size()) && ! bInterr ; ++ i) {
|
|
if ( vOffsCL[i]->IsPointOn( ptSE) || vOffsCL[i]->IsPointOn( ptES))
|
|
continue ;
|
|
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *ptBiArc, *vOffsCL[i]) ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
if ( ccClass.size() > 1) // se intersezione
|
|
bInterr = true ;
|
|
}
|
|
if ( bInterr)
|
|
break ; // così come ultimo arco ho quello precedente (valido)
|
|
|
|
// se ho trovato un BiArco valido, allora accorcio le due curve di Offset
|
|
dLenS -= dLStepS ;
|
|
dLenE += dLStepE ;
|
|
|
|
// ricalcolo il punto iniziale e finale
|
|
pCrvS->GetParamAtLength( dLenS, dUSE) ;
|
|
pCrvS->GetPointD1D2( dUSE, ICurve::FROM_MINUS, ptSE) ;
|
|
pCrvE->GetParamAtLength( dLenE, dUES) ;
|
|
pCrvE->GetPointD1D2( dUES, ICurve::FROM_PLUS, ptES) ;
|
|
|
|
// ricalcolo il vettore tangente iniziale e finale
|
|
PtrOwner<ICurveComposite> pCrvS_clone( pCrvS->Clone()) ; // curva di Offset iniziale accorciata
|
|
PtrOwner<ICurveComposite> pCrvE_clone( pCrvE->Clone()) ; // curva di Offset finale accorciata
|
|
pCrvS_clone->ChangeStartPoint( dUSE) ;
|
|
pCrvE_clone->ChangeStartPoint( dUES) ;
|
|
pCrvS_clone->GetStartDir( vS) ;
|
|
pCrvE_clone->GetStartDir( vE) ;
|
|
|
|
++ nIter ;
|
|
}
|
|
|
|
pCrvLink->AddCurve( Release( ptCrvFinal)) ; // ultimo arco valido trovato
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
static bool
|
|
GetUnclearedRegion( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vCrvs, ICURVEPOVECTOR& vLinks,
|
|
const ICurveComposite* pCrv_orig, const PocketParams& PockParams, ISurfFlatRegion* pSrfToCut)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
if ( vFirstOffs.empty() || int( vCrvs.size()) < int( vFirstOffs.size()))
|
|
return false ;
|
|
pSrfToCut->Clear() ;
|
|
|
|
// 1) creo la regione esterna
|
|
PtrOwner<ISurfFlatRegion> pSrfExtern( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfExtern))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vFirstOffs.size()) ; ++ i) {
|
|
if ( i == 0)
|
|
pSrfExtern->AddExtLoop( vFirstOffs[i]->Clone()) ;
|
|
else {
|
|
PtrOwner<ICurve> pCrvIntLoop( vFirstOffs[i]->Clone()) ;
|
|
if ( IsNull( pCrvIntLoop) ||
|
|
! pCrvIntLoop->Invert() ||
|
|
! pSrfExtern->AddIntLoop( Release( pCrvIntLoop)))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// ---- le seguenti regioni sono distinte per calcolar emeglio la Feed ----
|
|
// 2) Creo la regione svuotata dal Tool negli Offset, nei Links e sia dagli Offsets che dai Links
|
|
PtrOwner<ISurfFlatRegion> pSrfTool_Offs( CreateSurfFlatRegion()) ;
|
|
PtrOwner<ISurfFlatRegion> pSrfTool_Links( CreateSurfFlatRegion()) ;
|
|
PtrOwner<ISurfFlatRegion> pSrfTool( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfTool_Offs) || IsNull( pSrfTool_Links) || IsNull( pSrfTool))
|
|
return false ;
|
|
|
|
// creo un vettore che conterrà solamente i Link percorsi fino ad Ora
|
|
ICRVCOMPOPOVECTOR vLinks_done ;
|
|
|
|
for ( int i = ( PockParams.nType == POCKET_SPIRALIN ? 0 : int( vCrvs.size()) - 1) ;
|
|
( PockParams.nType == POCKET_SPIRALIN ? i < int( vCrvs.size()) : i >= 0 ) ;
|
|
( PockParams.nType == POCKET_SPIRALIN ? ++ i : -- i)) {
|
|
|
|
// ================= LINK ================================
|
|
// ( Il primo link è nullo, inatti non vi è nessun raccordo per raggiungere il primo Offset)
|
|
if ( i <= int( vLinks.size()) && ! IsNull( vLinks[i]) && vLinks[i]->IsValid()) {
|
|
// Feed
|
|
PtrOwner<ICurveComposite> pCompoLink_i( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoLink_i))
|
|
return false ;
|
|
pCompoLink_i->AddCurve( vLinks[i]->Clone()) ;
|
|
if ( ! AssignFeedSpiral( pCompoLink_i, pSrfTool_Offs, true, vLinks_done, pCrv_orig, PockParams, 2 * PockParams.dRad / 3))
|
|
return false ;
|
|
|
|
// per Link ...
|
|
PtrOwner<ICurve> pCrvLink_i( vLinks[i]->Clone()) ;
|
|
if ( IsNull( pCrvLink_i))
|
|
return false ;
|
|
PtrOwner<ISurfFlatRegion> pSrfToolRegLinki( GetSurfFlatRegionFromFatCurve( Release( pCrvLink_i), PockParams.dRad + 5 * EPS_SMALL, false, false)) ;
|
|
if ( ! IsNull( pSrfToolRegLinki)) {
|
|
if ( ! pSrfTool_Links->IsValid() || pSrfTool_Links->GetChunkCount() == 0)
|
|
pSrfTool_Links.Set( pSrfToolRegLinki) ;
|
|
else
|
|
pSrfTool_Links->Add( *pSrfToolRegLinki) ;
|
|
}
|
|
|
|
// risetto il Link come curva composita ...
|
|
vLinks[i].Set( pCompoLink_i->Clone()) ;
|
|
|
|
// inserisco il Link nel vettore dei Link percorsi
|
|
vLinks_done.emplace_back( Release( pCompoLink_i)) ;
|
|
}
|
|
|
|
// ================== OFFSET =============================
|
|
// Feed
|
|
if ( ! AssignFeedSpiral( vCrvs[i], pSrfTool_Offs, false, vLinks_done, pCrv_orig, PockParams, 2 * PockParams.dRad / 3))
|
|
return false ;
|
|
|
|
// aggiorno la superificie svuotata
|
|
PtrOwner<ICurveComposite> pCrvOffs_i( vCrvs[i]->Clone()) ;
|
|
if ( IsNull( pCrvOffs_i))
|
|
return false ;
|
|
PtrOwner<ISurfFlatRegion> pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( Release( pCrvOffs_i) , PockParams.dRad + 5 * EPS_SMALL, false, false)) ;
|
|
if ( ! IsNull( pSrfToolRegOffi)) {
|
|
if ( ! pSrfTool_Offs->IsValid() || pSrfTool_Offs->GetChunkCount() == 0)
|
|
pSrfTool_Offs.Set( pSrfToolRegOffi) ;
|
|
else
|
|
pSrfTool_Offs->Add( *pSrfToolRegOffi) ;
|
|
}
|
|
}
|
|
|
|
// 3) Calcolo la superificie svuotata
|
|
pSrfTool.Set( pSrfTool_Offs) ;
|
|
pSrfTool->Add( *pSrfTool_Links) ;
|
|
|
|
// 4) Creo la regione contenente tutte le parti non svuotate
|
|
pSrfToCut->CopyFrom( pSrfExtern) ;
|
|
if ( ! pSrfToCut->Subtract( *pSrfTool))
|
|
return false ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
RemoveFirstLoopFromSfr( ISurfFlatRegion* pSrfOrig)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSrfOrig == nullptr)
|
|
return true ;
|
|
// superficie da restituire
|
|
PtrOwner<SurfFlatRegion> pSrfRes( CreateBasicSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfRes))
|
|
return false ;
|
|
// scorro tutti i chunk tranne il primo ( escludo quindi il primo chunk)
|
|
for ( int c = 1 ; c < pSrfOrig->GetChunkCount() ; ++ c) {
|
|
PtrOwner<SurfFlatRegion> pSrfHelp( CreateBasicSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfHelp))
|
|
return false ;
|
|
pSrfHelp->AddExtLoop( pSrfOrig->GetLoop( c, 0)) ;
|
|
if ( c == 1)
|
|
pSrfRes.Set( pSrfHelp) ;
|
|
else
|
|
pSrfRes->Add( *pSrfHelp) ;
|
|
}
|
|
// restituisco
|
|
pSrfOrig->Clear() ;
|
|
pSrfOrig->CopyFrom( pSrfRes) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
RemoveExtraPartByMedialAxis( const ISurfFlatRegion* pChunkToCut, ICRVCOMPOPOVECTOR& vOffsFirstCurve,
|
|
const PocketParams& PockParams, int& nOptFlag, Point3d& ptCentroid, ICurveComposite* pCrvPath)
|
|
{
|
|
|
|
// nOptFlag : 0 -> non faccio nulla | 1 -> svuoto con un centroide | 2 -> svuoto con un percorso
|
|
|
|
// variabili iniziali
|
|
nOptFlag = 2 ;
|
|
bool bForceCentroid = false ;
|
|
if ( pChunkToCut == nullptr)
|
|
return false ;
|
|
|
|
// curva che l'utensile dovrà seguire per svuotare la regione e curva di ingombro del tool
|
|
PtrOwner<CurveComposite> pCrvStepByStepPath( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCrvStepByStepPath))
|
|
return false ;
|
|
|
|
// copie del chunk che devo svuotare
|
|
PtrOwner<ISurfFlatRegion> pSrfChunkToCutClone( pChunkToCut->Clone()) ;
|
|
double dArea = INFINITO ;
|
|
ICRVCOMPOPOVECTOR vCrvCoMedAxi ; // vettore dei medial Axis
|
|
PNTVECTOR vPtCentroid ; // vettore di centroidi
|
|
int nIter = 0 ; // numero di iterazioni
|
|
|
|
while ( pSrfChunkToCutClone->IsValid() && pSrfChunkToCutClone->GetChunkCount() != 0 &&
|
|
pSrfChunkToCutClone->GetArea( dArea) && dArea > 10 * EPS_SMALL) {
|
|
// finchè restano parti non svuotate la cui area totale è suffcientemente grande...
|
|
|
|
if ( nIter > 25) // troppi tentativi...
|
|
break ;
|
|
|
|
nIter++ ;
|
|
PtrOwner<ISurfFlatRegion> pSrfBiggerChunk( pSrfChunkToCutClone->CloneChunk( 0)) ;
|
|
if ( IsNull( pSrfBiggerChunk))
|
|
return false ;
|
|
if ( ! pSrfBiggerChunk->IsValid())
|
|
break ;
|
|
|
|
double dAreaExt ;
|
|
// se l'area del chunk è piccola... rimuovo il chunk dalla superficie da svuotare
|
|
if ( pSrfBiggerChunk->GetArea( dAreaExt) && dAreaExt < 10 * EPS_SMALL) {
|
|
if ( ! RemoveFirstLoopFromSfr( pSrfChunkToCutClone))
|
|
return false ;
|
|
if ( IsNull( pSrfChunkToCutClone) || ! pSrfChunkToCutClone->IsValid())
|
|
break ;
|
|
continue ;
|
|
}
|
|
|
|
// prendo il centroide del chunk
|
|
Point3d ptC ;
|
|
if ( ! pSrfBiggerChunk->GetCentroid( ptC))
|
|
break ;
|
|
|
|
// creo la superficie che racchiude il mio tool
|
|
PtrOwner<CurveArc> pCrvToolShape( CreateBasicCurveArc()) ;
|
|
if ( IsNull( pCrvToolShape))
|
|
return false ;
|
|
pCrvToolShape->SetXY( ptC, PockParams.dRad + 5 * EPS_SMALL) ;
|
|
PtrOwner<SurfFlatRegion> pSrfTool( CreateBasicSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfTool) || ! pSrfTool->AddExtLoop( Release( pCrvToolShape)) ||
|
|
! pSrfTool->IsValid())
|
|
break ;
|
|
|
|
PtrOwner<SurfFlatRegion> pSrfTest( CloneBasicSurfFlatRegion( pSrfBiggerChunk)) ;
|
|
if ( IsNull( pSrfTest))
|
|
return false ;
|
|
|
|
pSrfTest->Subtract( *pSrfTool) ;
|
|
if ( IsNull( pSrfTest) || ! pSrfTest->IsValid() || ! pSrfTest->GetArea( dArea) || dArea < 10 * EPS_SMALL ||
|
|
bForceCentroid) {
|
|
// se prima iterazione -> ritorno il centroide con punto
|
|
if ( nIter == 1) {
|
|
nOptFlag = 1 ;
|
|
ptCentroid = ptC ;
|
|
return true ;
|
|
}
|
|
// se non sono alla prima iterazione -> memorizzo il centroide nel vettore
|
|
else {
|
|
// controllo di non aver trovato un centroide già inserito ( per simmetria del chunk)
|
|
for ( int cen = 0 ; cen < int( vPtCentroid.size()) ; ++ cen) {
|
|
if ( AreSamePointApprox( vPtCentroid[cen], ptC)) {
|
|
pSrfChunkToCutClone->Clear() ;
|
|
break ;
|
|
}
|
|
}
|
|
vPtCentroid.push_back( ptC) ;
|
|
pSrfChunkToCutClone->Subtract( *pSrfTool) ;
|
|
if ( IsNull( pSrfChunkToCutClone) || ! pSrfChunkToCutClone->IsValid())
|
|
break ;
|
|
continue ;
|
|
}
|
|
}
|
|
bForceCentroid = false ;
|
|
|
|
// 1) ricavo il bordo della regione
|
|
PtrOwner<ICurve> pCrvBorder( pSrfBiggerChunk->GetLoop( 0, 0)) ;
|
|
if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid())
|
|
return false ;
|
|
|
|
// 2) ricavo il medial Axis del bordo
|
|
PolyLine PlMedAx ;
|
|
if ( ! CurveSimpleMedialAxis( pCrvBorder, PlMedAx)) {
|
|
bForceCentroid = true ;
|
|
-- nIter ;
|
|
continue ;
|
|
}
|
|
|
|
PtrOwner<CurveComposite> pCrvMedAx( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCrvMedAx))
|
|
return false ;
|
|
if ( ! pCrvMedAx->FromPolyLine( PlMedAx)) {
|
|
// se questo medial Axis è troppo piccolo e la polyLine non è convertitibile in curva composita ...
|
|
bForceCentroid = true ; // 2a) forzo ad andare nel centroide
|
|
-- nIter ; // 2b) scalo una iterzione
|
|
continue ;
|
|
}
|
|
|
|
// abbellisco la curva
|
|
pCrvMedAx->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
|
|
PolyArc PlMedAxArc ;
|
|
pCrvMedAx->ApproxWithArcsEx( 500 * EPS_SMALL, ANG_TOL_STD_DEG, 20.0, PlMedAxArc) ;
|
|
pCrvMedAx->Clear() ;
|
|
if( ! pCrvMedAx->FromPolyArc( PlMedAxArc)) {
|
|
bForceCentroid = true ;
|
|
-- nIter ;
|
|
continue ;
|
|
}
|
|
// smusso la curva
|
|
ModifyCurveToSmoothed( pCrvMedAx, PockParams, 0.025, 0.025, true) ;
|
|
// se curva valida la inserisco nel vettore, altrimenti forzo il centroide
|
|
if ( pCrvMedAx->IsValid())
|
|
vCrvCoMedAxi.emplace_back( pCrvMedAx->Clone()) ;
|
|
else {
|
|
bForceCentroid = true ;
|
|
-- nIter ;
|
|
continue ;
|
|
}
|
|
|
|
// 3) guardo quale regione svuoto con questa curva
|
|
PtrOwner<ISurfFlatRegion> pSrfRemoved( GetSurfFlatRegionFromFatCurve( Release( pCrvMedAx), PockParams.dRad + 5 * EPS_SMALL, false, false)) ;
|
|
if ( IsNull( pSrfRemoved) || ! pSrfRemoved->IsValid())
|
|
break ;
|
|
|
|
// 4) aggiorno la regione togliendo la parte percorsa dal tool
|
|
if ( ! pSrfChunkToCutClone->Subtract( *pSrfRemoved))
|
|
break ;
|
|
if ( IsNull( pSrfChunkToCutClone) || ! pSrfChunkToCutClone->IsValid())
|
|
break ;
|
|
|
|
}
|
|
|
|
// se entrambi i vettori sono vuoti l'area originaria era più piccola di 10 * EPS_SMALL -> non faccio nulla
|
|
if ( vCrvCoMedAxi.empty() && vPtCentroid.empty()) {
|
|
nOptFlag = 0 ;
|
|
return true ;
|
|
}
|
|
|
|
// ora collego la varie curve medial Axis trovate tra loro (ottenendo quindi una curva che svuota tutta la regione)
|
|
// curva che collega primo e ultimo medial Axis
|
|
PtrOwner<CurveComposite> pCrvCoBackLink( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCrvCoBackLink))
|
|
return false ;
|
|
Point3d ptSOriginal, ptEOriginal ;
|
|
Vector3d vtSOriginal, vtEOriginal ;
|
|
|
|
// parametri iniziali del primo e ultimo medial Axis trovato
|
|
if ( ! vCrvCoMedAxi[0]->GetStartPoint( ptSOriginal) ||
|
|
! vCrvCoMedAxi.back()->GetEndPoint( ptEOriginal) ||
|
|
! vCrvCoMedAxi[0]->GetStartDir( vtSOriginal) ||
|
|
! vCrvCoMedAxi.back()->GetEndDir( vtEOriginal))
|
|
return false ;
|
|
|
|
pCrvPath->AddCurve( vCrvCoMedAxi[0]->Clone()) ; // inserisco il primo medial Axis nel percorso finale
|
|
|
|
for ( int i = 1 ; i < int( vCrvCoMedAxi.size()) ; ++ i) { // scorro i restanti
|
|
Point3d ptS, ptE ; Vector3d vtS, vtE ;
|
|
// parametri iniziali del medialAxis i-esimo e (i-1)esimo
|
|
if ( ! vCrvCoMedAxi[i]->GetStartPoint( ptE) ||
|
|
! vCrvCoMedAxi[i-1]->GetEndPoint( ptS) ||
|
|
! vCrvCoMedAxi[i]->GetStartDir( vtE) ||
|
|
! vCrvCoMedAxi[i-1]->GetEndDir( vtS))
|
|
return false ;
|
|
|
|
PtrOwner<CurveComposite> pCrvLink( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
|
|
if ( ! CalcBoundedSmoothedLink( ptS, vtS, ptE, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink))
|
|
if ( ! CalcBoundedLink( ptS, ptE, vOffsFirstCurve, pCrvLink))
|
|
return false ;
|
|
|
|
if ( ! pCrvPath->AddCurve( pCrvLink->Clone()) || ! pCrvPath->AddCurve( vCrvCoMedAxi[i]->Clone()))
|
|
return false ;
|
|
}
|
|
|
|
if ( ! AreSamePointEpsilon( ptEOriginal, ptSOriginal, EPS_SMALL) &&
|
|
! CalcBoundedSmoothedLink( ptEOriginal, vtEOriginal, ptSOriginal, vtSOriginal, 0.5, vOffsFirstCurve,
|
|
PockParams, pCrvCoBackLink) &&
|
|
! CalcBoundedLink( ptEOriginal, ptSOriginal, vOffsFirstCurve, pCrvCoBackLink))
|
|
return false ;
|
|
|
|
// se ho trovato dei centroidi li unisco nei punti più vicini a questo percorso
|
|
for ( int i = 0 ; i < int( vPtCentroid.size()) ; ++ i) {
|
|
|
|
// 1) cerco il punto sulla curva più vicino al centroide i
|
|
Point3d ptClosestOnPath ; int nFlag ;
|
|
if ( ! DistPointCurve( vPtCentroid[i], *pCrvPath).GetMinDistPoint( EPS_SMALL, ptClosestOnPath, nFlag))
|
|
return false ;
|
|
// 2) spezzo la curva al paramtro del punto più vicino
|
|
double dU ;
|
|
if ( ! pCrvPath->GetParamAtPoint( ptClosestOnPath, dU))
|
|
return false ;
|
|
|
|
PtrOwner<CurveComposite> pCrvA( GetBasicCurveComposite( pCrvPath->CopyParamRange( 0, dU))) ;
|
|
if ( IsNull( pCrvA))
|
|
return false ;
|
|
PtrOwner<CurveComposite> pCrvB( GetBasicCurveComposite( pCrvPath->CopyParamRange( dU, pCrvPath->GetCurveCount()))) ;
|
|
if ( IsNull( pCrvB))
|
|
return false ;
|
|
|
|
// 3) prendo il vettore tangente al percorso nel punto trovato nel pCrvPath
|
|
Vector3d vtTanCpt ; Point3d ptH ;
|
|
if ( ! pCrvPath->GetPointTang( dU, ICurve::FROM_MINUS, ptH, vtTanCpt))
|
|
return false ;
|
|
|
|
// 4) collego i due punti (centroide e punto più vicino alla curva)
|
|
PtrOwner<CurveComposite> pCrvPath1( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCrvPath1))
|
|
return false ;
|
|
|
|
if ( ! CalcBoundedSmoothedLink( ptClosestOnPath, vtTanCpt, vPtCentroid[i], vtTanCpt, 0,
|
|
vOffsFirstCurve, PockParams, pCrvPath1) &&
|
|
! CalcBoundedLink( ptClosestOnPath, vPtCentroid[i], vOffsFirstCurve, pCrvPath1))
|
|
return false ;
|
|
|
|
pCrvPath->Clear() ;
|
|
pCrvPath->AddCurve( Release( pCrvA)) ;
|
|
if ( ! pCrvPath->AddCurve( Release( pCrvPath1)))
|
|
return false ;
|
|
pCrvPath->AddCurve( Release( pCrvB)) ;
|
|
}
|
|
|
|
// la curva resitituita è una curva aperta che ha come primo punto il punto iniziale del primo medialAxis
|
|
// la curva restituita collega tutti i medial Axis tra di loro
|
|
// la curva restitutita collega tutti i centroidi con delle circonferenze
|
|
// la curva restituita ha come punto finale l'ultimo punto dell'ultimo medial Axis
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
CutOffsetToClosestPoint( ICRVCOMPOPOVECTOR& vCurves, const Point3d& ptFocus,
|
|
ICurveComposite* pCrv1, ICurveComposite* pCrv2, int& nIndex)
|
|
{
|
|
// controllo di avere almeno un offset...
|
|
if ( vCurves.empty())
|
|
return false ;
|
|
|
|
// variabili iniziali
|
|
Point3d ptCl_H ;
|
|
double dDistance = INFINITO ;
|
|
int nFlag ;
|
|
pCrv1->Clear() ;
|
|
pCrv2->Clear() ;
|
|
nIndex = -1 ;
|
|
Point3d ptCL ;
|
|
|
|
// scorro tutti gli offset ad eccezione del primo ( a meno di averne uno solo )
|
|
for ( int j = ( int( vCurves.size()) == 1 ? 0 : 1) ; j < int( vCurves.size()) ; ++ j) {
|
|
|
|
// non ho offset nulli, però controllo...
|
|
if ( IsNull( vCurves[j]))
|
|
continue ;
|
|
|
|
// prendo il punto più vicino
|
|
if ( ! DistPointCurve( ptFocus, *vCurves[j]).GetMinDistPoint( EPS_SMALL, ptCl_H, nFlag))
|
|
continue ;
|
|
|
|
// cerco il punto in assoluto più vicino
|
|
if ( dDistance > Dist( ptCl_H, ptFocus)) {
|
|
dDistance = Dist( ptCl_H, ptFocus) ;
|
|
ptCL = ptCl_H ;
|
|
nIndex = j ;
|
|
}
|
|
}
|
|
|
|
// se non ho trovato nulla, esco
|
|
if ( nIndex < 0)
|
|
return false ;
|
|
|
|
// ricavo le due curve ( l'Offset viene spezzato in due sottocurve mediante il punto trovato)
|
|
double dUTan ;
|
|
vCurves[nIndex]->GetParamAtPoint( ptCL, dUTan) ;
|
|
pCrv1->AddCurve( vCurves[nIndex]->Clone()) ;
|
|
if ( ! pCrv1->TrimEndAtParam( dUTan))
|
|
pCrv1->Clear() ;
|
|
pCrv2->AddCurve( vCurves[nIndex]->Clone()) ;
|
|
if ( ! pCrv2->TrimStartAtParam( dUTan))
|
|
pCrv2->Clear() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
CutLinkToClosestPoint( ICURVEPOVECTOR& vCurves, ICRVCOMPOPOVECTOR& vOffs, const Point3d& ptFocus,
|
|
ICurveComposite* pCrv1, ICurveComposite* pCrv2, bool& bFound, int& nIndex)
|
|
{
|
|
|
|
// variabili iniziali
|
|
double dDistance = INFINITO ;
|
|
Point3d ptCl_H ;
|
|
int nFlag ;
|
|
pCrv1->Clear() ;
|
|
pCrv2->Clear() ;
|
|
Point3d ptCL ;
|
|
nIndex = - 1 ;
|
|
bFound = false ;
|
|
|
|
// scorro tutti i link cercando il più vicino
|
|
for ( int j = 0 ; j < int( vCurves.size()) ; ++ j) {
|
|
|
|
// escludo i links nulli ...
|
|
if ( IsNull( vCurves[j]))
|
|
continue ;
|
|
|
|
// distanza minima tra link e curva
|
|
if ( ! DistPointCurve( ptFocus, *vCurves[j]).GetMinDistPoint( EPS_SMALL, ptCl_H, nFlag))
|
|
continue ;
|
|
|
|
// cerco la distanza minimia assoluta ( non deve essere sopra un Offset )
|
|
if ( dDistance > Dist( ptCl_H, ptFocus) &&
|
|
! ( vOffs[j-1]->IsPointOn( ptCl_H) || vOffs[j]->IsPointOn( ptCl_H))) {
|
|
dDistance = Dist( ptCl_H, ptFocus) ;
|
|
nIndex = j ;
|
|
bFound = true ;
|
|
}
|
|
}
|
|
|
|
// se non ho trovato nulla, esco
|
|
if ( nIndex < 0)
|
|
return false ;
|
|
|
|
// ricavo le due curve ( il link viene spezzato in due sottocurve mediante il punto trovato )
|
|
double dUTan ;
|
|
vCurves[nIndex]->GetParamAtPoint( ptCL, dUTan) ;
|
|
pCrv1->AddCurve( vCurves[nIndex]->Clone()) ;
|
|
if ( ! pCrv1->TrimEndAtParam( dUTan))
|
|
pCrv1->Clear( ) ;
|
|
pCrv2->AddCurve( vCurves[nIndex]->Clone()) ;
|
|
if ( ! pCrv2->TrimStartAtParam( dUTan))
|
|
pCrv2->Clear() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static bool
|
|
CutCurveByOffsets( ICurveComposite* pCurve, ICRVCOMPOPOVECTOR& vOffs)
|
|
{
|
|
|
|
// controllo parametri ingresso
|
|
if ( vOffs.empty())
|
|
return true ;
|
|
|
|
// vettore di curve ausiliario
|
|
ICRVCOMPOPOVECTOR vOffOrig( vOffs.size()) ;
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i)
|
|
vOffOrig[i].Set( vOffs[i]->Clone()) ;
|
|
|
|
// punto iniziale corrente
|
|
Point3d ptStart ;
|
|
if ( ! pCurve->GetStartPoint( ptStart))
|
|
return false ;
|
|
|
|
// curve d'appoggio
|
|
PtrOwner<ICurveComposite> pCompo( pCurve->Clone()) ;
|
|
PtrOwner<ICurveComposite> pCompoHelp( pCurve->Clone()) ;
|
|
PtrOwner<ICurveComposite> pOffCheck( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoHelp) || IsNull( pCompo) || IsNull( pOffCheck))
|
|
return false ;
|
|
|
|
int nTypeOfCut = -1 ;
|
|
|
|
// scorro tutte le curve
|
|
for ( int i = 0; i < int( vOffOrig.size()) ; ++ i) {
|
|
|
|
// controllo se la curva è quella attuale
|
|
if ( vOffOrig[i]->IsPointOn( ptStart))
|
|
pOffCheck.Set( vOffOrig[i]->Clone()) ;
|
|
// classificazione
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pCompo, *vOffOrig[i]) ;
|
|
intCC.GetCurveClassification( 0, 10 * EPS_SMALL, ccClass) ;
|
|
if ( ! pCompoHelp->Clear())
|
|
return false ;
|
|
// se In/Out
|
|
if ( ccClass.size() > 1) {
|
|
if ( ccClass[0].nClass == CRVC_IN)
|
|
nTypeOfCut = CRVC_OUT ;
|
|
else
|
|
nTypeOfCut = CRVC_IN ;
|
|
// scorro le classificazioni ottenute
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
if ( ccClass[j].nClass == nTypeOfCut) {
|
|
Point3d ptS ; pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ;
|
|
double dOffS ; vOffOrig[i]->GetParamAtPoint( ptS, dOffS) ;
|
|
Point3d ptE ; pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ;
|
|
double dOffE ; vOffOrig[i]->GetParamAtPoint( ptE, dOffE) ;
|
|
// recupero i due possibili percorsi e uso il più corto
|
|
PtrOwner<ICurve> pCrvA( vOffOrig[i]->CopyParamRange( dOffS, dOffE)) ;
|
|
PtrOwner<ICurve> pCrvB( vOffOrig[i]->CopyParamRange( dOffE, dOffS)) ;
|
|
|
|
if ( IsNull( pCrvA) || IsNull( pCrvB))
|
|
return false ;
|
|
|
|
double dLenA ; pCrvA->GetLength( dLenA) ;
|
|
double dLenB ; pCrvB->GetLength( dLenB) ;
|
|
|
|
if ( dLenA < dLenB) {
|
|
|
|
CRVCVECTOR ccClassA ;
|
|
IntersCurveCurve intCCA( *pCrvA, *pOffCheck) ;
|
|
intCCA.GetCurveClassification( 0, 10 * EPS_SMALL, ccClassA) ;
|
|
|
|
if ( ccClassA.size() > 0 && i == vOffOrig.size() - 1 && ccClassA[0].nClass == CRVC_ON_M) {
|
|
if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)))
|
|
return false ;
|
|
continue ;
|
|
}
|
|
|
|
if ( ! pCompoHelp->AddCurve( Release( pCrvA)))
|
|
return false ;
|
|
}
|
|
else {
|
|
pCrvB->Invert() ;
|
|
|
|
CRVCVECTOR ccClassB ;
|
|
IntersCurveCurve intCCB( *pCrvB, *pOffCheck) ;
|
|
intCCB.GetCurveClassification( 0, 10 * EPS_SMALL, ccClassB) ;
|
|
|
|
if ( ccClassB.size() > 0 && i == vOffOrig.size() - 1 && ccClassB[0].nClass == CRVC_ON_M) {
|
|
if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)))
|
|
return false ;
|
|
continue ;
|
|
}
|
|
|
|
if ( ! pCompoHelp->AddCurve( Release( pCrvB)))
|
|
return false ;
|
|
}
|
|
}
|
|
else {
|
|
if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
pCompo->Clear() ;
|
|
pCompo->AddCurve( pCompoHelp->Clone()) ;
|
|
}
|
|
}
|
|
// restituisco la nuova curva
|
|
pCurve->Clear() ;
|
|
pCurve->AddCurve( Release( pCompo)) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetNewCurvetWithCentroid( const ICurveComposite* pCrvH1, const ICurveComposite* pCrvH2,
|
|
const PocketParams& PockParams, Point3d& ptC,
|
|
bool bCir, ICRVCOMPOPOVECTOR& VFirstOff, ICurveComposite* pCrvNewCurve)
|
|
{
|
|
// creazione dei due possibili percorsi per raggiungere il centroide
|
|
PtrOwner<ICurveComposite> pPath1( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pPath2( CreateCurveComposite()) ;
|
|
if ( IsNull( pPath1) || IsNull( pPath2))
|
|
return false ;
|
|
pCrvNewCurve->Clear() ;
|
|
Point3d ptS, ptE = ptC ;
|
|
Vector3d vtTanS, vtTanE ;
|
|
|
|
// creo una circonferenza
|
|
if ( bCir) {
|
|
// prendo il punto iniziale
|
|
if ( pCrvH1 == nullptr || pCrvH1->GetFirstCurve() == nullptr) {
|
|
if ( ! pCrvH2->GetStartPoint( ptS))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! pCrvH1->GetEndPoint( ptS))
|
|
return false ;
|
|
}
|
|
|
|
// creo la circonferenza
|
|
if ( ! CalcBoundedSmoothedLink( ptS, vtTanS, ptE, vtTanE, 0, VFirstOff, PockParams, pPath1))
|
|
return false ;
|
|
Vector3d vtS_cir ; pPath1->GetStartDir( vtS_cir) ;
|
|
Vector3d vtS_CrvH1 ; pCrvH1->GetEndDir( vtS_CrvH1) ;
|
|
if ( ! AreSameVectorApprox( vtS_cir, vtS_CrvH1))
|
|
pPath1->Invert() ;
|
|
|
|
// assegno la Feed
|
|
AssignMinFeed( pPath1, PockParams) ;
|
|
|
|
// controllo che la circonferenza sia ammissibile ( solo che non esca dalle prime curve di Offset)
|
|
Point3d ptCrvS ; pPath1->GetStartPoint( ptCrvS) ;
|
|
if ( ! CutCurveByOffsets( pPath1, VFirstOff)) // taglio la curva sugli Offsets
|
|
return false ;
|
|
|
|
double dUCrvS ;
|
|
ModifyCurveToSmoothed( pPath1, PockParams, 0.075, 0.075, true) ;
|
|
if ( pPath1->IsClosed()) {
|
|
pPath1->GetParamAtPoint( ptCrvS, dUCrvS) ;
|
|
pPath1->ChangeStartPoint( dUCrvS) ;
|
|
}
|
|
|
|
// creo la nuova curva con la circonferenza
|
|
if ( pCrvH1 != nullptr && pCrvH1->GetFirstCurve() != nullptr)
|
|
if ( ! pCrvNewCurve->AddCurve( pCrvH1->Clone())) // aggiungo inizio
|
|
return false ;
|
|
if ( ! pCrvNewCurve->AddCurve( pPath1->Clone())) // aggiungo la circonferenza
|
|
return false ;
|
|
if ( pCrvH2 != nullptr && pCrvH2->GetFirstCurve() != nullptr)
|
|
if ( ! pCrvNewCurve->AddCurve(( pCrvH2->Clone()))) // aggiungo la fine
|
|
return false ;
|
|
}
|
|
// creo un BiArco
|
|
else {
|
|
|
|
// prendo il vettore tangente e il punto iniziale
|
|
if ( pCrvH1 == nullptr || pCrvH1->GetFirstCurve() == nullptr) {
|
|
if ( ! pCrvH2->GetStartDir( vtTanS) ||
|
|
! pCrvH2->GetStartPoint( ptS))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! pCrvH1->GetEndDir( vtTanS) ||
|
|
! pCrvH1->GetEndPoint( ptS))
|
|
return false ;
|
|
}
|
|
|
|
// creo i due Biarchi
|
|
if ( ! CalcBoundedSmoothedLink( ptS, vtTanS, ptE, vtTanS, 0.5, VFirstOff, PockParams, pPath1) ||
|
|
! CalcBoundedSmoothedLink( ptE, vtTanS, ptS, vtTanS, 0.5, VFirstOff, PockParams, pPath2))
|
|
return false ;
|
|
|
|
// assegno la Feed a queste due nuove curve
|
|
AssignMinFeed( pPath1, PockParams) ;
|
|
AssignMinFeed( pPath2, PockParams) ;
|
|
|
|
// creo la nuova curva con il doppio Biarco
|
|
if ( pCrvH1 != nullptr && pCrvH1->GetFirstCurve() != nullptr)
|
|
if ( ! pCrvNewCurve->AddCurve( pCrvH1->Clone())) // aggiungo inizio
|
|
return false ;
|
|
if ( ! pCrvNewCurve->AddCurve( pPath1->Clone()) || ! pCrvNewCurve->AddCurve( pPath2->Clone())) // aggiungo i due BiArchi
|
|
return false ;
|
|
if ( pCrvH2 != nullptr && pCrvH2->GetFirstCurve() != nullptr)
|
|
if ( ! pCrvNewCurve->AddCurve(( pCrvH2->Clone()))) // aggiungo la fine
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ManageSmoothAndAutoInters( ICurveComposite* pCrv, ICurveComposite* pCrvPath, ICurveComposite* pPath1,
|
|
const PocketParams& PockParams, ICurveComposite* pPath2, ICRVCOMPOPOVECTOR& vOffsCL)
|
|
{
|
|
// controllo parametri
|
|
if ( pCrv == nullptr)
|
|
return false ;
|
|
|
|
// check AutoIntersezioni...
|
|
SelfIntersCurve SICrv( *pCrv) ;
|
|
if ( SICrv.GetCrossOrOverlapIntersCount() > 0) { // se ci sono autointersezioni
|
|
|
|
Vector3d vTanE ; Point3d ptHS ;
|
|
pCrvPath->GetEndDir( vTanE) ;
|
|
pCrvPath->GetEndPoint( ptHS) ;
|
|
|
|
bool bFound = false ;
|
|
int nIter = - 45 ; // angolo incrementale nel caso di autointersezioni
|
|
|
|
while ( ! bFound && nIter < 45) {
|
|
vTanE.Rotate( Z_AX, 90 - nIter) ; // vettore perpendicolare
|
|
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
|
|
pLine->SetPVL( ptHS, vTanE, ( 2 * PockParams.dRad) / 3 - 5 * EPS_SMALL) ; // segmento uscente
|
|
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pLine, *pCrv) ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
if ( int( ccClass.size()) > 1) // se intersezione con parte precedente inverto la direzione
|
|
pLine->SetPVL( ptHS, - vTanE, ( 2 * PockParams.dRad) / 3 - 5 * EPS_SMALL) ;
|
|
|
|
if ( IsNull( pLine) || ! pLine->IsValid())
|
|
break ;
|
|
|
|
// punto finale segmento uscente
|
|
Point3d ptEndLine ;
|
|
pLine->GetEndPoint( ptEndLine) ;
|
|
Point3d ptNear ;
|
|
double dUS, dUE ;
|
|
pPath2->GetDomain( dUS, dUE) ;
|
|
PtrOwner<ICurve> pCrvHelper(( pPath2->CopyParamRange( dUS, dUE - 0.5))) ;
|
|
pCrvHelper->GetEndPoint( ptNear) ;
|
|
PtrOwner<ICurveLine> pLine1( GetLinePointTgCurve( ptEndLine, *pPath2, ptNear)) ; // segmento tangente
|
|
|
|
if ( IsNull( pLine1) || ! pLine1->IsValid())
|
|
break ;
|
|
|
|
// creo la nuova curva formata da BiArco 1 - Path - Segmento uscente - Segmento tangente - parte restante di Biarco 2
|
|
Point3d ptELine1 ;
|
|
pLine1->GetEndPoint( ptELine1) ;
|
|
pPath2->GetParamAtPoint( ptELine1, dUS) ;
|
|
PtrOwner<ICurveComposite> pNewPath2( GetCurveComposite( pPath2->CopyParamRange( dUS, dUE))) ;
|
|
|
|
// creo la curva formata dai due segmenti ma smussati tra loro...
|
|
PtrOwner<ICurveComposite> pCrvTwoSeg( CreateCurveComposite()) ;
|
|
if ( ! pCrvTwoSeg->AddCurve( pCrvPath->Clone()) ||
|
|
! pCrvTwoSeg->AddCurve( pLine->Clone()) ||
|
|
! pCrvTwoSeg->AddCurve( pLine1->Clone()))
|
|
return false ;
|
|
|
|
ModifyCurveToSmoothed( pCrvTwoSeg, PockParams, 0.2, 0.2, true) ;
|
|
|
|
PtrOwner<ICurveComposite> ptNewCrv( CreateCurveComposite()) ;
|
|
if ( ptNewCrv->AddCurve( pPath1->Clone()) &&
|
|
ptNewCrv->AddCurve( pCrvTwoSeg->Clone()) &&
|
|
ptNewCrv->AddCurve( pNewPath2->Clone())) {
|
|
|
|
SelfIntersCurve SICrvLoop( *ptNewCrv) ;
|
|
|
|
if ( SICrvLoop.GetCrossOrOverlapIntersCount() == 0) {
|
|
pCrv->Clear() ;
|
|
pCrv->AddCurve( Release( ptNewCrv)) ;
|
|
bFound = true ;
|
|
}
|
|
else { // se trovo autointersezioni, ripeto cambiando l'angolo del segmento uscente
|
|
++nIter ;
|
|
}
|
|
}
|
|
else
|
|
break ;
|
|
}
|
|
}
|
|
|
|
// smusso questa curva sulle varie curve di offsets
|
|
Point3d ptCheck1, ptCheck2 ;
|
|
PtrOwner<ICurveComposite> pCrvH( pCrv->Clone()) ; // copia della curva
|
|
|
|
if ( ! CutCurveByOffsets( pCrv, vOffsCL)) { // taglio la curva sugli Offsets
|
|
pCrv->Clear() ;
|
|
pCrv->AddCurve( Release( pCrvH)) ;
|
|
}
|
|
else { // controllo che i punti iniziali coincidano
|
|
pCrv->GetStartPoint( ptCheck1) ;
|
|
pCrvH->GetStartPoint( ptCheck2) ;
|
|
if ( ! AreSamePointApprox( ptCheck1, ptCheck2)) { // se i punti iniziali della curva prima e dopo lo smusso non coincidono ...
|
|
pCrv->Clear() ;
|
|
pCrv->AddCurve( Release( pCrvH)) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetNewCurvetWithPath( const ICurveComposite* pCrvH1, const ICurveComposite* pCrvH2, ICurveComposite* pCrvPath,
|
|
ICRVCOMPOPOVECTOR& VFirstOff, ICRVCOMPOPOVECTOR& VoffsCl, const PocketParams& PockParams,
|
|
ICurveComposite* pCrvNewCurve)
|
|
{
|
|
// inizializzo i due possibili percorsi
|
|
PtrOwner<ICurveComposite> pPath1( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pPath2( CreateCurveComposite()) ;
|
|
if ( IsNull( pPath1) || IsNull( pPath2) || pCrvPath == nullptr)
|
|
return false ;
|
|
pCrvNewCurve->Clear() ;
|
|
Point3d ptS, ptE, ptH ;
|
|
Vector3d vtTanS, vtTanE, vtH ;
|
|
if ( ! pCrvPath->GetStartPoint( ptE) ||
|
|
! pCrvPath->GetStartDir( vtTanE) ||
|
|
! pCrvPath->GetEndPoint( ptH) ||
|
|
! pCrvPath->GetEndDir( vtH))
|
|
return false ;
|
|
|
|
if ( pCrvH1 == nullptr || pCrvH1->GetFirstCurve() == nullptr) {
|
|
if ( ! pCrvH2->GetStartPoint( ptS) ||
|
|
! pCrvH2->GetStartDir( vtTanS))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! pCrvH1->GetEndPoint( ptS) ||
|
|
! pCrvH1->GetEndDir( vtTanS))
|
|
return false ;
|
|
}
|
|
|
|
// creo i due Biarchi
|
|
if ( ! CalcBoundedSmoothedLink( ptS, vtTanS, ptE, vtTanE, 0.5, VFirstOff, PockParams, pPath1))
|
|
if ( ! CalcBoundedLink( ptS, ptE, VFirstOff, pPath1))
|
|
return false ;
|
|
if ( ! CalcBoundedSmoothedLink( ptH, vtH, ptS, vtTanS, 0.5, VFirstOff, PockParams, pPath2))
|
|
if ( ! CalcBoundedLink( ptH, ptS, VFirstOff, pPath2))
|
|
return false ;
|
|
|
|
// creo la curva formata da BiArco1 - Path - BiArco 2
|
|
PtrOwner<ICurveComposite> pCrvToAdd( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvToAdd) ||
|
|
! pCrvToAdd->AddCurve( pPath1->Clone()) ||
|
|
! pCrvToAdd->AddCurve( pCrvPath->Clone()) ||
|
|
! pCrvToAdd->AddCurve( pPath2->Clone()))
|
|
return false ;
|
|
|
|
// cerco di togliere le auto intersezioni tra i biarchi e le curve di Medial Axis
|
|
if ( ! ManageSmoothAndAutoInters( pCrvToAdd, pCrvPath, pPath1, PockParams, pPath2, VoffsCl))
|
|
return false ;
|
|
|
|
// imposto la Feed
|
|
AssignMinFeed( pCrvToAdd, PockParams) ;
|
|
|
|
// curva finale
|
|
if ( pCrvH1 != nullptr && pCrvH1->GetFirstCurve() != nullptr)
|
|
if ( ! pCrvNewCurve->AddCurve( pCrvH1->Clone())) // aggiungo inizio
|
|
return false ;
|
|
|
|
if ( ! pCrvNewCurve->AddCurve( pCrvToAdd->Clone())) { // aggiungo BiArco - Path - BiArco
|
|
// nel caso non funzioni questo raccordo, recupero un biarco valido e uso questo ...
|
|
Vector3d vtHS, vtHE ;
|
|
Point3d ptHS, ptHE ;
|
|
pCrvNewCurve->GetEndPoint( ptHS) ;
|
|
pCrvNewCurve->GetEndDir( vtHS) ;
|
|
pCrvToAdd->GetStartPoint( ptHE) ;
|
|
pCrvToAdd->GetStartDir( vtHE) ;
|
|
PtrOwner<ICurveComposite> pCrvBiArcHelper( CreateCurveComposite()) ;
|
|
if ( ! CalcBoundedSmoothedLink( ptHS, vtHS, ptHE, vtHE, 0.5, VFirstOff, PockParams, pCrvBiArcHelper))
|
|
if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper))
|
|
return false ;
|
|
|
|
// assegno la Feed
|
|
AssignMinFeed( pCrvBiArcHelper, PockParams) ;
|
|
|
|
if ( ! pCrvNewCurve->AddCurve( Release( pCrvBiArcHelper)) ||
|
|
! pCrvNewCurve->AddCurve( pCrvToAdd->Clone()))
|
|
return false ;
|
|
}
|
|
if ( pCrvH2 != nullptr && pCrvH2->GetFirstCurve() != nullptr) {
|
|
if ( ! pCrvNewCurve->AddCurve( pCrvH2->Clone())) { // aggiungo fine
|
|
// nel caso non funzioni questo raccordo, recupero un biarco valido e uso questo ...
|
|
int nTry = 1 ; int nMaxTry = 10 ; bool bFound = false ;
|
|
while ( nTry < nMaxTry && ! bFound) {
|
|
Vector3d vtHS, vtHE ;
|
|
Point3d ptHS, ptHE ;
|
|
pCrvNewCurve->GetEndPoint( ptHS) ;
|
|
pCrvNewCurve->GetEndDir( vtHS) ;
|
|
pCrvH2->GetStartPoint( ptHE) ;
|
|
pCrvH2->GetStartDir( vtHE) ;
|
|
PtrOwner<ICurveComposite> pCrvBiArcHelper( CreateCurveComposite()) ;
|
|
if ( ! CalcBoundedSmoothedLink( ptHS, vtHS, ptHE, vtHE, 0.5, VFirstOff, PockParams, pCrvBiArcHelper))
|
|
if ( ! CalcBoundedLink( ptHS, ptHE, VFirstOff, pCrvBiArcHelper) )
|
|
return false ;
|
|
|
|
// assegno la Feed
|
|
AssignMinFeed( pCrvBiArcHelper, PockParams) ;
|
|
|
|
if ( ! pCrvNewCurve->AddCurve( pCrvBiArcHelper->Clone()) ||
|
|
! pCrvNewCurve->AddCurve( pCrvH2->Clone())) {
|
|
++nTry ;
|
|
}
|
|
else {
|
|
bFound = true ;
|
|
}
|
|
}
|
|
if ( ! bFound)
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
GetCurveWeightInfo( const ICurveComposite* pCrvCompo, double dMaxLen, double& dToTRot, int& nSmallArcs, int& nSmallLines)
|
|
{
|
|
|
|
dToTRot = 0 ; nSmallArcs = 0 ; nSmallLines = 0 ;
|
|
if ( pCrvCompo == nullptr)
|
|
return true ;
|
|
|
|
int nLines = 0 ;
|
|
PtrOwner<ICurveLine> pCrvLineOld( CreateCurveLine()) ;
|
|
if ( IsNull( pCrvLineOld))
|
|
return false ;
|
|
|
|
// scorro tutte le curve della composita
|
|
const ICurve* pMyCrv = pCrvCompo->GetFirstCurve() ;
|
|
while ( pMyCrv != nullptr) {
|
|
if ( pMyCrv->GetType() == CRV_ARC) { // nel caso di archi ...
|
|
nLines = 0 ;
|
|
PtrOwner<ICurveArc> pCrvArc( GetCurveArc( pMyCrv->Clone())) ;
|
|
double dAngCenter = abs( pCrvArc->GetAngCenter()) ;
|
|
dToTRot += abs( dAngCenter) ;
|
|
double dLen = 0 ;
|
|
if ( pCrvArc->GetLength( dLen) && dLen < dMaxLen)
|
|
++ nSmallArcs ;
|
|
}
|
|
else if ( pMyCrv->GetType() == CRV_LINE) { // nel caso di linee ...
|
|
++ nLines ;
|
|
if ( nLines == 1)
|
|
pCrvLineOld.Set( GetCurveLine( pMyCrv->Clone())) ;
|
|
else {
|
|
PtrOwner<ICurveLine> pNewMyCrv( GetCurveLine( pMyCrv->Clone())) ;
|
|
Vector3d vtNew, vtOld ;
|
|
pCrvLineOld->GetEndDir( vtOld) ;
|
|
pNewMyCrv->GetStartDir( vtNew) ;
|
|
double dAngCorner ; vtOld.GetAngle( vtNew, dAngCorner) ;
|
|
dToTRot += abs( dAngCorner) ;
|
|
pCrvLineOld.Set( pNewMyCrv) ;
|
|
nLines = 0 ;
|
|
}
|
|
|
|
double dLen = 0 ;
|
|
if ( pMyCrv->GetLength( dLen) && dLen < dMaxLen)
|
|
++ nSmallLines ;
|
|
}
|
|
pMyCrv = pCrvCompo->GetNextCurve() ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
ChoosePath( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, int nP, double dPerP, double dMaxLen, int& nC)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
if ( pCrv1 == nullptr || pCrv2 == nullptr ||
|
|
! ( nP == 0 || nP == 1) || dPerP > 1 || dPerP < 0 || dMaxLen < EPS_SMALL)
|
|
return false ;
|
|
|
|
// # di archi piccoli tra le due curve
|
|
int dSmallArcs1 = 0 ; int dSmallArcs2 = 0 ;
|
|
// # di segmenti piccoli tra le due curve
|
|
int dSmallLines1 = 0 ; int dSmallLines2 = 0 ;
|
|
|
|
// ------------- lunghezze --------------
|
|
double dLen1 = 0 ; double dLen2 = 0 ;
|
|
pCrv1->GetLength( dLen1) ;
|
|
pCrv2->GetLength( dLen2) ;
|
|
|
|
double CL1, CL2 ; // rapporto tra lunghezza corrente e massima
|
|
|
|
if ( dLen1 < EPS_SMALL && dLen2 < EPS_SMALL) {
|
|
CL1 = 0 ;
|
|
CL2 = 0 ;
|
|
}
|
|
else {
|
|
CL1 = dLen1 / max( dLen1, dLen2) ;
|
|
CL2 = dLen2 / max( dLen1, dLen2) ;
|
|
}
|
|
|
|
// ------------- rotazioni --------------
|
|
double dRot1 = 0 ; double dRot2 = 0 ;
|
|
if ( ! GetCurveWeightInfo( pCrv1, dMaxLen, dRot1, dSmallArcs1, dSmallLines1) ||
|
|
! GetCurveWeightInfo( pCrv2, dMaxLen, dRot2, dSmallArcs2, dSmallLines2))
|
|
return false ;
|
|
|
|
double CR1, CR2 ; // rapporto tra la somma de rotazione degli angoli correnti e quella massima
|
|
|
|
if ( dRot1 == 0 && dRot2 == 0) {
|
|
CR1 = 0 ;
|
|
CR2 = 0 ;
|
|
}
|
|
else {
|
|
CR1 = dRot1 / max( dRot1, dRot2) ;
|
|
CR2 = dRot2 / max( dRot1, dRot2) ;
|
|
}
|
|
|
|
// funzione peso ( bilancio tra lunghezze e rotazioni complessive pesate)
|
|
double Fp1 = ( nP == 0 ? dPerP : ( 1 - dPerP)) * ( CL1 + dSmallLines1) + ( nP == 0 ? ( 1 - dPerP) : dPerP) * ( CR1 + dSmallArcs1) ;
|
|
double Fp2 = ( nP == 0 ? dPerP : ( 1 - dPerP)) * ( CL2 + dSmallLines2) + ( nP == 0 ? ( 1 - dPerP) : dPerP) * ( CR2 + dSmallArcs2) ;
|
|
|
|
// scelta della prima o della seconda curva
|
|
nC = ( Fp1 > Fp2 ? 1 : 0) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
RemoveExtraParts( ISurfFlatRegion* pSrfToCut, ICRVCOMPOPOVECTOR& vOffs, ICRVCOMPOPOVECTOR& vOffsClosedCurves,
|
|
ICRVCOMPOPOVECTOR& vOffsFirstCurve, ICURVEPOVECTOR& vLinks, const PocketParams& PockParams)
|
|
{
|
|
|
|
// controllo parametri
|
|
if ( pSrfToCut == nullptr || vOffs.empty())
|
|
return true ;
|
|
|
|
// ciclo tutti i chunk della regione da tagliare
|
|
for ( int i = 0 ; i < pSrfToCut->GetChunkCount() ; ++ i) {
|
|
|
|
// regione i-esima da rimuovere ( chunk i-esimo )
|
|
PtrOwner<ISurfFlatRegion> pSrfChunkToCut( pSrfToCut->CloneChunk( i)) ;
|
|
if ( IsNull( pSrfChunkToCut))
|
|
return false ;
|
|
|
|
// Curva nel caso la regione si svuoti con MedialAxis
|
|
PtrOwner<ICurveComposite> pCrvPath( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvPath))
|
|
return false ;
|
|
|
|
// nel caso la regione si svuoti con un centroide -> ptGoTo è il centroide
|
|
// nel caso la regione si svuoti con un medial Axis -> ptGoTo è il punto iniziale
|
|
// -> ptGoTo è il punto finale
|
|
Point3d ptGoTo, ptGoToI ;
|
|
// flag : 0 -> non faccio nulla | 1 -> svuoto con centroide | 2 -> svuoto con un percorso
|
|
int nOptFlag = 0 ;
|
|
|
|
// ricavo il flag
|
|
// ptGoTo è il centroide, pCrvPath è la curva da seguire per svuotare la regione ( nel caso non bastasse il centroide)
|
|
if ( ! RemoveExtraPartByMedialAxis( pSrfChunkToCut, vOffsFirstCurve, PockParams, nOptFlag, ptGoTo, pCrvPath) ||
|
|
nOptFlag == 0)
|
|
continue ;
|
|
|
|
if ( nOptFlag == 2 ) // se ho una curva di Medial Axis, ricavo il punto iniziale e finale
|
|
if ( ! pCrvPath->GetStartPoint( ptGoTo) ||
|
|
! pCrvPath->GetEndPoint( ptGoToI))
|
|
continue ;
|
|
|
|
// una volta trovata la curva di medial Axis ( se la regione non si svuota semplicemente passando per il
|
|
// centroide, controllo in quale direzione è più conveniente percorrerla ... ( I = Inverso)
|
|
PtrOwner<ICurveComposite> pCrvH1( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvH2( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvH1I( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvH2I( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvH1) || IsNull( pCrvH2) || IsNull( pCrvH2) || IsNull( pCrvH2I))
|
|
return false ;
|
|
int nIndexO = 0 ;
|
|
int nIndexOI = 0 ;
|
|
|
|
// cerco il punto pià vicino a ptGoTo sugli tra i vari Offset ( escludendo il primo )
|
|
// NB. PtGoTo è il centroide o il punto iniziale del percorso di medial Axis non invertito
|
|
if ( ! CutOffsetToClosestPoint( vOffs, ptGoTo, pCrvH1, pCrvH2, nIndexO))
|
|
continue ;
|
|
if ( nOptFlag == 2) // se ho un percorso di Medial Axis, lo cerco anche per PtGoToI
|
|
if ( ! CutOffsetToClosestPoint( vOffs, ptGoToI, pCrvH1I, pCrvH2I, nIndexOI))
|
|
continue ;
|
|
|
|
// A) SE IL PUNTO PIU' VICINO E' SU UN ESTREMO DELL'OFFSET... -> Cerco un punto sui collegamenti
|
|
// ( controllo solo i punti trovati sugli offset mediante Medial Axis non invertiti)
|
|
if ( IsNull( pCrvH1) || IsNull( pCrvH2) || pCrvH1->GetCurveCount() == 0 || pCrvH2->GetCurveCount() == 0) {
|
|
bool bFound, bFoundI ; int nIndexL, nIndexLI ;
|
|
if ( ! CutLinkToClosestPoint( vLinks, vOffs, ptGoTo, pCrvH1, pCrvH2, bFound, nIndexL))
|
|
continue ;
|
|
if ( nOptFlag == 2) { // nel caso curva di Medial Axis, controllo anche nel caso invertito
|
|
if ( ! CutLinkToClosestPoint( vLinks, vOffs, ptGoToI, pCrvH1I, pCrvH2I, bFoundI, nIndexLI))
|
|
continue ;
|
|
}
|
|
|
|
// link finale scelto...
|
|
PtrOwner<ICurveComposite> ptCrvNewLink( CreateCurveComposite()) ;
|
|
if ( IsNull( ptCrvNewLink))
|
|
return false ;
|
|
|
|
if ( nOptFlag == 1) { // se svuoto con centroide, aggiusto il link con una circonferenza ( BiArco alla peggio)
|
|
if ( ! GetNewCurvetWithCentroid( pCrvH1, pCrvH2, PockParams, ptGoTo, bFound, vOffsFirstCurve, ptCrvNewLink))
|
|
continue ;
|
|
}
|
|
else if ( nOptFlag == 2) { // se svuoto con curva di medial Axis...
|
|
|
|
bool bSucc = true ; bool bSuccI = true ;
|
|
|
|
// ricavo il nuovo Offset con la curva di medial Axis aggiunta
|
|
if ( ! GetNewCurvetWithPath( pCrvH1, pCrvH2, pCrvPath, vOffsFirstCurve, vOffsClosedCurves,
|
|
PockParams, ptCrvNewLink))
|
|
bSucc = false ;
|
|
|
|
PtrOwner<ICurveComposite> pCrvPathI( pCrvPath->Clone()) ;
|
|
pCrvPathI->Invert() ;
|
|
PtrOwner<ICurveComposite> ptCrvNewLinkI( CreateCurveComposite()) ;
|
|
if ( IsNull( ptCrvNewLinkI))
|
|
return false ;
|
|
|
|
// ricavo il nuovo Offset con la curva di medial Axis Invertita aggiunta
|
|
if ( ! GetNewCurvetWithPath( pCrvH1I, pCrvH2I, pCrvPathI, vOffsFirstCurve, vOffsClosedCurves,
|
|
PockParams, ptCrvNewLinkI))
|
|
bSuccI = false ;
|
|
|
|
// scelgo il miglior percorso ottenuto ( sempre verificando che siano validi...)
|
|
int nC = 0 ;
|
|
if ( bSucc && bSuccI) { // se entrambi i percorsi sono validi allora li confronto
|
|
if ( ChoosePath( ptCrvNewLink, ptCrvNewLinkI, 0, 0.25, 5 * 1000 * EPS_SMALL, nC) && nC == 1) {
|
|
ptCrvNewLink.Set( ptCrvNewLinkI) ;
|
|
nIndexL = nIndexLI ;
|
|
}
|
|
}
|
|
else if ( ! bSucc) { // altrimenti tengo l'unico valido
|
|
ptCrvNewLink.Set( ptCrvNewLinkI) ;
|
|
nIndexL = nIndexLI ;
|
|
}
|
|
}
|
|
vLinks[nIndexL].Set( ptCrvNewLink) ; // setto il nuovo Link
|
|
}
|
|
else { // B) SE NON SONO AD UN ESTREMO DELLA CURVA DI OFFSET
|
|
|
|
// nuovo Offset da restituire
|
|
PtrOwner<ICurveComposite> ptCrvNewOffs( CreateCurveComposite()) ;
|
|
if ( IsNull( ptCrvNewOffs))
|
|
return false ;
|
|
|
|
if ( nOptFlag == 1) { // se svuoto con centroide, aggiusto il link con una circonferenza ( BiArco alla peggio)
|
|
if ( ! GetNewCurvetWithCentroid( pCrvH1, pCrvH2, PockParams, ptGoTo, true, vOffsFirstCurve, ptCrvNewOffs))
|
|
continue ;
|
|
}
|
|
else if ( nOptFlag == 2) { // nel caso aggiungo un medial Axis non agli estremi di un Offset
|
|
|
|
bool bSucc = true ; bool bSuccI = true ;
|
|
|
|
// come prima controllo il percorso normal ed invertito
|
|
if ( ! GetNewCurvetWithPath( pCrvH1, pCrvH2, pCrvPath, vOffsFirstCurve, vOffsClosedCurves,
|
|
PockParams, ptCrvNewOffs))
|
|
bSucc = false ;
|
|
|
|
PtrOwner<ICurveComposite> pCrvPathI( pCrvPath->Clone()) ;
|
|
pCrvPathI->Invert() ;
|
|
PtrOwner<ICurveComposite> ptCrvNewOffsI( CreateCurveComposite()) ;
|
|
if ( IsNull( ptCrvNewOffsI))
|
|
return false ;
|
|
|
|
if ( ! GetNewCurvetWithPath( pCrvH1I, pCrvH2I, pCrvPathI, vOffsFirstCurve, vOffsClosedCurves,
|
|
PockParams, ptCrvNewOffsI))
|
|
bSuccI = false ;
|
|
|
|
int nC = 0 ; // se entrambi i percorsi sono validi allora li confronto
|
|
if ( bSucc && bSuccI) {
|
|
if ( ChoosePath( ptCrvNewOffs, ptCrvNewOffsI, 0, 0.5, 5 * 1000 * EPS_SMALL, nC) && nC == 1) {
|
|
ptCrvNewOffs.Set( ptCrvNewOffsI) ;
|
|
nIndexO = nIndexOI ;
|
|
}
|
|
}
|
|
else if ( ! bSucc) { // altrimenti tengo l'unico valido
|
|
ptCrvNewOffs.Set( ptCrvNewOffsI) ;
|
|
nIndexO = nIndexOI ;
|
|
}
|
|
}
|
|
vOffs[nIndexO].Set( ptCrvNewOffs) ; // setto il nuovo Offset
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, const PocketParams& PockParams,
|
|
const ICRVCOMPOPOVECTOR& vOffsClosedCurves, const ICRVCOMPOPOVECTOR& vOffsFirstCurve)
|
|
{
|
|
// controllo dei parametri
|
|
if ( vOffs.empty() || vOffsFirstCurve.empty())
|
|
return false ;
|
|
vLinks.clear() ;
|
|
vLinks.resize( int( vOffs.size())) ;
|
|
|
|
// scorro tutte le curva di Offset
|
|
for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) {
|
|
|
|
// ricavo il punto inziale della curva corrente
|
|
Point3d ptS ;
|
|
if ( ! vOffs[i]->GetStartPoint( ptS))
|
|
return false ;
|
|
|
|
int nNextInd = -1 ; // indice della curva successiva
|
|
double dMinDist = INFINITO ; // distanza tra questa curva e la successiva
|
|
Point3d ptStartNext ; // punto iniziale della curva successiva
|
|
|
|
// tra le curva successive cerco la curva interna e più vicina ad essa
|
|
for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) {
|
|
IntersCurveCurve IntCC( *vOffs[i], *vOffs[j]) ;
|
|
CRVCVECTOR ccClass ;
|
|
// se interna
|
|
if ( IntCC.GetCurveClassification( 1, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) {
|
|
// calcolo la distanza minima tra essa
|
|
int nFlag ;
|
|
Point3d ptClosest ;
|
|
if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) {
|
|
double dCurrDist = SqDist( ptS, ptClosest) ;
|
|
if ( dCurrDist < dMinDist) {
|
|
dMinDist = dCurrDist ;
|
|
nNextInd = j ;
|
|
ptStartNext = ptClosest ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se non ho trovato nessuna curva interna... cerco semplicemente la curva più vicina
|
|
if ( nNextInd == -1) {
|
|
for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) {
|
|
int nFlag ;
|
|
Point3d ptClosest ;
|
|
if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) {
|
|
double dCurrDist = SqDist( ptS, ptClosest) ;
|
|
if ( dCurrDist < dMinDist) {
|
|
dMinDist = dCurrDist ;
|
|
nNextInd = j ;
|
|
ptStartNext = ptClosest ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se non ho trovato nessuna curva, errore
|
|
if ( nNextInd == -1)
|
|
return false ;
|
|
|
|
// scambio la curva i+1 esima con la j-esima ( se non sono già in ordine)
|
|
if ( nNextInd != i + 1)
|
|
swap( vOffs[nNextInd], vOffs[i+1]) ;
|
|
|
|
// cambio il suo punto iniziale nel punto più vicino tovato
|
|
double dUS ;
|
|
if ( ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUS, EPS_SMALL))
|
|
return false ;
|
|
vOffs[i+1]->ChangeStartPoint( dUS) ;
|
|
|
|
// accorcio la curva per raccordarmi in tangenza con la successiva
|
|
if ( int( vOffs.size()) > 1) {
|
|
// clono le curve i ed i+1 esime ( nel caso non riuscissi ad accorciarle o raccordarle )
|
|
PtrOwner<ICurveComposite> pCrv_i( vOffs[i]->Clone()) ;
|
|
PtrOwner<ICurveComposite> pCrv_ii( vOffs[i+1]->Clone()) ;
|
|
if ( IsNull( pCrv_i) || IsNull( pCrv_ii) || ! pCrv_i->IsValid() || ! pCrv_ii->IsValid())
|
|
return false ;
|
|
// creo la curva che le collegherà
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
// NB. Le curve di Offset da tagliare non devono essere quelle di primo Offset
|
|
if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsClosedCurves, vOffsFirstCurve, PockParams, pCrvLink,
|
|
i == 0 ? 0 : 0.01, 0.01, 2) || ! pCrvLink->IsValid()) {
|
|
// se non sono riuscito, cerco una strada più semplice
|
|
// ripristino le curve
|
|
pCrvLink->Clear() ;
|
|
vOffs[i].Set( pCrv_i) ; // chiuso
|
|
vOffs[i+1].Set( pCrv_ii) ; // chiuso
|
|
// recupero i vettori tangente iniziali ( le curve sono chiuse )
|
|
Vector3d vtS, vtE ;
|
|
if ( ! vOffs[i]->GetStartDir( vtS) || ! vOffs[i+1]->GetStartDir( vtE))
|
|
return false ;
|
|
// creo il bi-arco tra esse
|
|
if ( CalcBoundedSmoothedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink))
|
|
vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento
|
|
else
|
|
return false ;
|
|
|
|
continue ; // passo alla curva i+1 esima successiva
|
|
}
|
|
|
|
// per sicurezza aggiorno i nuovi punti e i nuovi parametri
|
|
double dUNewS, dUNewE ;
|
|
if ( ! pCrvLink->GetStartPoint( ptS) ||
|
|
! pCrvLink->GetEndPoint( ptStartNext) ||
|
|
! vOffs[i]->GetParamAtPoint( ptS, dUNewS) ||
|
|
! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUNewE))
|
|
return false ;
|
|
|
|
// imposto il punto iniziale della curva successiva ( i+1 esima)
|
|
vOffs[i+1]->ChangeStartPoint( dUNewE) ;
|
|
PtrOwner<ICurveComposite> pCrvNewOffs( vOffs[i]->Clone()) ;
|
|
if ( dUNewS > EPS_SMALL) {
|
|
pCrvNewOffs.Set( ConvertCurveToComposite( vOffs[i]->CopyParamRange( 0, dUNewS))) ;
|
|
// sostituisco la curva i esima con quella tagliata
|
|
vOffs[i].Set( pCrvNewOffs) ;
|
|
}
|
|
|
|
// aggiorno il collegamento
|
|
vLinks[i+1].Set( pCrvLink) ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs, const int nOffsCount,
|
|
const int nIter, const Vector3d& vtN, const PocketParams& PockParams, bool& bInsert)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0)
|
|
return false ;
|
|
bInsert = true ; // di base inserisco
|
|
|
|
// controllo se richiesta ottimizzazione sul numero di Offsets
|
|
if ( ! PockParams.bOptOffsets)
|
|
return true ;
|
|
|
|
if ( nIter != 0) { // controllo se sono almeno al secondo offset...
|
|
|
|
// per ogni curva controllo se il controOffset + 5 * EPS_SMALL genera una regione ...
|
|
OffsetCurve OffsCrv ; // considero anche i casi in cui ho bSmallRad
|
|
if ( OffsCrv.Make( pCrvOffs, - PockParams.dRad + dOffs - 5 * EPS_SMALL , ICurve::OFF_FILLET)) {
|
|
// se questa curva sparisce -> non serve inserirla come offset, non svuota parti aggiuntive
|
|
if ( OffsCrv.GetCurveCount() == 0) {
|
|
bInsert = false ;
|
|
return true ;
|
|
}
|
|
}
|
|
else
|
|
return true ;
|
|
|
|
// controllo se richista ottimizzazione per Offsets mediante centroidi e medial Axis
|
|
if ( ! PockParams.bOptOffsetsAdv || OffsCrv.GetCurveCount() > 1)
|
|
return true ;
|
|
|
|
PtrOwner<ICurve> pCrvOffLonger( OffsCrv.GetLongerCurve()) ;
|
|
if ( IsNull( pCrvOffLonger) || ! pCrvOffLonger->IsValid())
|
|
return true ;
|
|
|
|
// Immagine del tool con centro nel centroide della zona da svuotare
|
|
Point3d ptC ; pCrvOffLonger->GetCentroid( ptC) ;
|
|
PtrOwner<ICurveArc> pCrvTool( CreateCurveArc()) ;
|
|
if( IsNull( pCrvTool))
|
|
return false ;
|
|
pCrvTool->SetXY( ptC, PockParams.dRad - 5 * EPS_SMALL) ;
|
|
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pCrvOffLonger, *pCrvTool) ;
|
|
intCC.GetCurveClassification( 1, EPS_SMALL, ccClass) ;
|
|
if ( int(ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) {
|
|
// centroide
|
|
bInsert = false ;
|
|
return true ;
|
|
}
|
|
|
|
// recupero la PolyLine per altezza e lunghezza del Box2D ( rettangolo)
|
|
PolyLine pl ;
|
|
if ( ! pCrvOffLonger->ApproxWithLines( 100 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, pl))
|
|
return true ;
|
|
Vector3d vtX ;
|
|
double dLen = 0 ; double dHeight = 0 ;
|
|
if ( ! pl.GetMinAreaRectangleXY( ptC, vtX, dLen, dHeight))
|
|
return true ;
|
|
|
|
// frame locale, recupero DimX e DimY
|
|
Frame3d frLoc ; frLoc.Set( ptC, vtN, vtX) ;
|
|
if ( ! frLoc.IsValid())
|
|
return true ;
|
|
BBox3d bBox ;
|
|
frLoc.Invert() ;
|
|
pCrvOffLonger->GetBBox( frLoc, bBox) ;
|
|
double dDimX = bBox.GetDimX() ;
|
|
double dDimY = bBox.GetDimY() ;
|
|
|
|
// se dimensioni accettabili, inserisco
|
|
if ( dDimX < 2 * PockParams.dRad - 100 * EPS_SMALL ||
|
|
dDimY < 2 * 2 * PockParams.dRad - 100 * EPS_SMALL) {
|
|
bInsert = false ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcSpiral( const ISurfFlatRegion* pSrfPock, const PocketParams& PockParams, int& nReg, Point3d& ptStart,
|
|
ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops, ICurveComposite* pMCrv, ICurveComposite* pRCrv,
|
|
bool& bOptimizedTrap, bool& bMidOut, Vector3d& vtMidOut)
|
|
{
|
|
// inizializzo i risultati
|
|
pMCrv->Clear() ;
|
|
pRCrv->Clear() ;
|
|
|
|
// Offset corrente ( il primo è definito dal raggio utensile)
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
|
|
// recupero il versore normale della superficie, ruotandola nel piano XY
|
|
PtrOwner<ISurfFlatRegion> pSrfToWork( pSrfPock->Clone()) ;
|
|
if ( IsNull( pSrfToWork) || pSrfToWork->GetChunkCount() == 0)
|
|
return false;
|
|
|
|
// porto il Chunk c-esimo nel sistema di riferimento locale
|
|
Vector3d vtExtr = pSrfToWork->GetNormVersor() ;
|
|
Point3d ptCen ; pSrfToWork->GetCentroid( ptCen) ;
|
|
Frame3d frPocket ; frPocket.Set( ptCen, vtExtr) ;
|
|
|
|
|
|
// CONTROLLO PER CASI OTTIMIZZATI
|
|
bool bHasIsland = ( pSrfPock->GetLoopCount( 0) > 1) ;
|
|
// se non ho isole e curva valida allora controllo casi ottimizzati
|
|
if ( ! PockParams.bAvoidOpt && ! bHasIsland &&
|
|
( ! vCrvOrigChunkLoops.empty()) && ! IsNull( vCrvOrigChunkLoops[0])) {
|
|
|
|
// --------------------------- caso spirale ---------------------------------------
|
|
PtrOwner<ICurveComposite> pCrvBorder( ConvertCurveToComposite( pSrfPock->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid())
|
|
return false ;
|
|
pCrvBorder->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
pCrvBorder->SetExtrusion( pSrfPock->GetNormVersor()) ; // setto l'estrusione della curva
|
|
Vector3d vtN = pSrfPock->GetNormVersor() ; // vettore normale per caso circonferenza
|
|
Point3d ptCen ; double dRad ;
|
|
bool bIsCircle = false ;
|
|
if ( ! OptimizedSpiralCirle( pCrvBorder, 50 * EPS_SMALL, dRad, ptCen, bIsCircle))
|
|
return false ;
|
|
if ( bIsCircle && dRad - dOffs > 10 * EPS_SMALL) {
|
|
double dIntRad = 0 ;
|
|
if ( nReg == 0) {
|
|
bool bOk = CalcCircleSpiral( ptCen, vtN, dRad - dOffs, dIntRad, PockParams, pMCrv, pRCrv) ;
|
|
if ( bOk) {
|
|
pMCrv->GetStartPoint( ptStart) ;
|
|
nReg = 1 ;
|
|
return true ;
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
else
|
|
return true ;
|
|
}
|
|
|
|
// -------------------------- caso trapezoide ---------------------------------------
|
|
PtrOwner<ICurveComposite> pCrvTrap( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvTrap))
|
|
return false ;
|
|
Frame3d frTrap ;
|
|
double dPocketSize ;
|
|
int nBase, nSecondBase ;
|
|
vCrvOrigChunkLoops[0]->ToLoc( frPocket) ;
|
|
if ( ! GetTrapezoidFromShape( vCrvOrigChunkLoops[0], pCrvTrap, frTrap, PockParams, dPocketSize, nBase, nSecondBase))
|
|
return false ;
|
|
vCrvOrigChunkLoops[0]->ToGlob( frPocket) ;
|
|
if ( pCrvTrap->IsValid()) {
|
|
pCrvTrap->SetExtrusion( vtExtr) ;
|
|
if ( nReg == 0) {
|
|
CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParams, pMCrv, pRCrv, bOptimizedTrap) ;
|
|
if ( bOptimizedTrap) {
|
|
pMCrv->ToGlob( frPocket) ;
|
|
pRCrv->ToGlob( frPocket) ;
|
|
nReg = 1 ;
|
|
return true ;
|
|
}
|
|
}
|
|
else
|
|
return true ;
|
|
}
|
|
|
|
}
|
|
|
|
// porto la superficie nel frame corrente
|
|
pSrfToWork->ToLoc( frPocket) ;
|
|
|
|
// ciclo di offset verso l'interno
|
|
const int MAX_ITER = 1000 ;
|
|
int nIter = 0 ;
|
|
ICRVCOMPOPOVECTOR vOffs ; // vettore delle curve di offset
|
|
ICRVCOMPOPOVECTOR vOffsFirstCurve ; // curve di primo offset
|
|
|
|
PtrOwner<ISurfFlatRegion> pSrfAct( pSrfToWork->Clone()) ; // regione attuale
|
|
if ( IsNull( pSrfAct) || pSrfAct->GetChunkCount() == 0)
|
|
return false ;
|
|
|
|
double dOffsPrec = 0. ;
|
|
while ( nIter < MAX_ITER) {
|
|
// ricavo la regione piana da VRONI
|
|
PtrOwner<ISurfFlatRegion> pSfrOffsVR( pSrfAct->CreateOffsetSurf( - dOffs, ICurve::OFF_FILLET)) ;
|
|
if ( IsNull( pSfrOffsVR))
|
|
return false ;
|
|
if ( ! pSfrOffsVR->IsValid()) {
|
|
pSfrOffsVR.Set( pSrfAct->CreateOffsetSurf( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) ;
|
|
if ( IsNull( pSfrOffsVR))
|
|
return false ;
|
|
}
|
|
|
|
// se primo Offset
|
|
if ( nIter == 0) {
|
|
int my_nReg = nReg ;
|
|
nReg = pSfrOffsVR->GetChunkCount() ; // aggiorno il nuovo valore delle regioni totali di primo Offset
|
|
// gli Offset progressivi appartengono al Chunk nReg-esimo
|
|
pSrfAct.Set( pSfrOffsVR->CloneChunk( my_nReg)) ;
|
|
if ( IsNull( pSrfAct)) // se supero i chunk ottenuti
|
|
return true ;
|
|
pSfrOffsVR.Set( pSrfAct->Clone()) ;
|
|
}
|
|
|
|
// numero di Chunk attuali ( alla prima iterazione è l'nReg-esimo)
|
|
int nChunks = pSfrOffsVR->GetChunkCount() ;
|
|
|
|
for ( int i = 0 ; i < nChunks ; ++ i) {
|
|
// per ogni chunk...
|
|
int nLoops = pSfrOffsVR->GetLoopCount( i) ;
|
|
for ( int j = 0 ; j < nLoops ; ++ j) {
|
|
// per ogni loop...
|
|
PtrOwner<ICurveComposite> pCrvCompoBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ;
|
|
if ( IsNull( pCrvCompoBorder) || ! pCrvCompoBorder->IsValid())
|
|
return false ;
|
|
/*
|
|
Il materiale deve sempre trovarsi alla destra ( se bInvert -> alla sinistra) del tool
|
|
soltanto sul primo bordo; Nel caso di isole aperte o nelle passate di Offset intermedio
|
|
l'orientamente non ha importanza.
|
|
*/
|
|
if ( nIter != 0 && j > 0) // inverto l'orientamento delle curve interne ( offset delle isole trovate)
|
|
pCrvCompoBorder->Invert() ;
|
|
// controllo quali regioni di Offset possono essere sostituite
|
|
bool bInsert = true ;
|
|
if ( ! CheckIfOffsetIsNecessary( pCrvCompoBorder, dOffs - dOffsPrec, int( vOffs.size()), nIter, vtExtr, PockParams, bInsert))
|
|
return false ;
|
|
if ( bInsert)
|
|
vOffs.emplace_back( Release( pCrvCompoBorder)) ;
|
|
if ( nIter == 0) { // salvo il bordo per i link ( non invertiti, devo sapere IN/OUT)
|
|
PtrOwner<ICurveComposite> pCrvCompoExtBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ;
|
|
vOffsFirstCurve.emplace_back( Release( pCrvCompoExtBorder)) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// controllo se serve un raggio più piccolo di svuotatura
|
|
bool bSmallRad = ( nIter == 0 ? dOffs < PockParams.dRad + EPS_ZERO : dOffs - dOffsPrec < PockParams.dRad + EPS_ZERO) ;
|
|
|
|
// se ho trovato dei chunk, allora aggiorno l'Offset per la passata successiva
|
|
if ( nChunks > 0) {
|
|
// memorizzo il valore di Offset per l'iterazione successiva
|
|
if ( nIter != 0) {
|
|
dOffsPrec = dOffs ;
|
|
dOffs += PockParams.dSideStep ;
|
|
}
|
|
else {
|
|
dOffsPrec = 0. ;
|
|
dOffs = PockParams.dSideStep ;
|
|
}
|
|
}
|
|
// se devo usare un Offset più piccolo...
|
|
else if ( ! bSmallRad)
|
|
dOffs = dOffsPrec + ( nIter == 0 ? PockParams.dRad + PockParams.dRadialOffset : PockParams.dRad) ;
|
|
// altrimenti ho finito la regione da svuotare...
|
|
else
|
|
break ;
|
|
|
|
++ nIter ; // aggiorno iterazione
|
|
}
|
|
|
|
// se non ho trovato curve di Offset allora esco
|
|
if ( vOffs.empty())
|
|
return true ;
|
|
|
|
// cambio il punto iniziale della prima Curva di Offset
|
|
Point3d ptRef = PockParams.ptEnd ; ptRef.ToLoc( frPocket) ;
|
|
Point3d ptNewStart ;
|
|
if ( ! IsNull( vCrvOrigChunkLoops[0]))
|
|
vCrvOrigChunkLoops[0]->ToLoc( frPocket) ;
|
|
|
|
// 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, frPocket,
|
|
ptStart, vtMidOut, bMidOut, nIndexSwap, int( vOffs.size()), vCrvOrigChunkLoops))
|
|
vOffsFirstCurve[nIndexSwap]->GetStartPoint( ptNewStart) ;
|
|
else
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( SetPtStartForPath( vOffs[0], PockParams, pSrfToWork, ptRef, frPocket, ptStart, vtMidOut, bMidOut,
|
|
int( vOffs.size()), vCrvOrigChunkLoops[0]))
|
|
vOffs[0]->GetStartPoint( ptNewStart) ;
|
|
else
|
|
return false ;
|
|
}
|
|
if ( ! IsNull( vCrvOrigChunkLoops[0]))
|
|
vCrvOrigChunkLoops[0]->ToGlob( frPocket) ;
|
|
|
|
// se richiesta inversione
|
|
for ( int i = 0 ; i < int( vOffs.size()) && PockParams.bInvert ; ++ i)
|
|
vOffs[i]->Invert() ;
|
|
|
|
// smusso le curve di offset ( ad eccezione della prima; le isole sono automaticamente smussate)
|
|
ICRVCOMPOPOVECTOR vOffsClosedCurves( vOffs.size()) ; // vettore con tutte le curve di Offset Chiuse
|
|
double dSmoothPar = PockParams.dRad / 8 ;
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
|
|
if ( i != 0)
|
|
ModifyCurveToSmoothed( vOffs[i], PockParams, dSmoothPar, dSmoothPar, false) ;
|
|
vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
vOffsClosedCurves[i].Set( vOffs[i]->Clone()) ;
|
|
}
|
|
|
|
// setto il punto iniziale della svuotatura
|
|
double dNewUS ;
|
|
if ( nIndexSwap != 0)
|
|
swap( vOffs[0], vOffs[nIndexSwap]) ; // se entro da un'isola aperta
|
|
vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ;
|
|
vOffs[0]->ChangeStartPoint( dNewUS) ;
|
|
vOffs[0]->SetTempParam( 0., 0) ; // prima iterazione
|
|
|
|
// riordino le curve cambiando e creo i collegamenti
|
|
ICURVEPOVECTOR vLinks( vOffs.size()) ;
|
|
if ( ! CreateSpiralPocketingPath( vOffs, vLinks, PockParams, vOffsClosedCurves, vOffsFirstCurve))
|
|
return false ;
|
|
|
|
// controllo eventuali parti non svuotate...
|
|
if ( ! IsNull( vCrvOrigChunkLoops[0]))
|
|
vCrvOrigChunkLoops[0]->ToLoc( frPocket) ;
|
|
PtrOwner<ISurfFlatRegion> pSrfToCut( CreateSurfFlatRegion()) ;
|
|
if ( GetUnclearedRegion( vOffsFirstCurve, vOffs, vLinks, vCrvOrigChunkLoops[0], PockParams, pSrfToCut)) {
|
|
// Modifico i percorsi
|
|
if ( ! RemoveExtraParts( pSrfToCut, vOffs, vOffsClosedCurves, vOffsFirstCurve, vLinks, PockParams))
|
|
return false ;
|
|
}
|
|
|
|
// creo il percorso di lavoro a partire dalla raccolta degli offset e dei collegamenti
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++i) {
|
|
// se collegamento da aggiungere
|
|
if ( ! IsNull( vLinks[i])) {
|
|
// accodo nel percorso di lavorazione
|
|
if ( ! pMCrv->AddCurve( Release( vLinks[i]))) {
|
|
return false ;
|
|
}
|
|
}
|
|
pMCrv->AddCurve( Release( vOffs[i])) ;
|
|
}
|
|
|
|
// semplifico la curva ottenuta
|
|
SimplifyCurveByFeeds( pMCrv, PockParams, 10 * EPS_SMALL) ;
|
|
|
|
// verifico il percorso di lavoro
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// reset delle proprietà temporanee delle curve componenti
|
|
for ( int i = 0 ; i < pMCrv->GetCurveCount() ; ++ i) {
|
|
pMCrv->SetCurveTempProp( i, 0, 0) ;
|
|
pMCrv->SetCurveTempProp( i, 0, 1) ;
|
|
}
|
|
|
|
// setto estrusione
|
|
pMCrv->SetExtrusion( vtExtr) ;
|
|
|
|
// riporto tutto nel sistema di riferimento originale
|
|
pMCrv->ToGlob( frPocket) ;
|
|
ptStart.ToGlob( frPocket) ;
|
|
vtMidOut.ToGlob( frPocket) ;
|
|
if ( ! IsNull( vCrvOrigChunkLoops[0]))
|
|
vCrvOrigChunkLoops[0]->ToGlob( frPocket) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
static bool
|
|
CalcZigZagLink( ICurveComposite* pCrv1, ICurveComposite* pCrv2, const PocketParams& PockParams,
|
|
ICurveComposite* pCrvLink)
|
|
{
|
|
// se non richiesto, esco
|
|
if ( ! PockParams.bSmooth)
|
|
return true ;
|
|
|
|
// controllo dei parametri
|
|
if ( pCrv1 == nullptr || pCrv2 == nullptr)
|
|
return false ;
|
|
pCrvLink->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
|
|
// prendo i parametri per il raccordo
|
|
Vector3d vtS, vtE, vtH ;
|
|
pCrv1->GetEndDir( vtS) ;
|
|
pCrv2->GetStartDir( vtE) ;
|
|
Point3d ptS, ptE, ptE1, ptS2 ;
|
|
pCrv1->GetEndPoint( ptS) ;
|
|
pCrv2->GetStartPoint( ptE) ;
|
|
vtH = ptE - ptS ;
|
|
double dLen1, dLen2 ;
|
|
pCrv1->GetLength( dLen1) ;
|
|
pCrv2->GetLength( dLen2) ;
|
|
double dUS1, dUE1, dUS2, dUE2 ;
|
|
pCrv1->GetDomain( dUS1, dUE1) ;
|
|
pCrv2->GetDomain( dUS2, dUE2) ;
|
|
double dAngle, dAngleH ;
|
|
double dDist = Dist( ptS, ptE) ;
|
|
|
|
// curve per controllo validità del collegamento
|
|
PtrOwner<ICurveComposite> pCrvH1( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvH2( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvTempLink( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvTest( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvH1) || IsNull( pCrvH2) || IsNull( pCrvTempLink) || IsNull( pCrvTest))
|
|
return false ;
|
|
|
|
if ( vtS.GetAngle( vtE, dAngle) &&
|
|
abs( dAngle) < ANG_STRAIGHT + 5 * EPS_SMALL && abs( dAngle) > ANG_STRAIGHT - 5 * EPS_SMALL &&
|
|
vtH.GetAngle( vtE, dAngleH) &&
|
|
abs( dAngleH) < ANG_RIGHT + 5 * EPS_SMALL && abs( dAngleH) > ANG_RIGHT - 5 * EPS_SMALL &&
|
|
dLen1 > dDist && dLen2 > dDist &&
|
|
pCrvLink->GetCurveCount() == 1 && pCrvLink->GetFirstCurve()->GetType() == CRV_LINE) {
|
|
|
|
// creo una semicirconferenza
|
|
double dRad = dDist / 2 ; // raggio
|
|
Point3d ptNS, ptNE ;
|
|
pCrv1->GetParamAtLength( dLen1 - dRad, dUE1) ;
|
|
pCrv2->GetParamAtLength( dRad, dUS2) ;
|
|
pCrv1->GetPointD1D2( dUE1, ICurve::FROM_MINUS, ptNS) ; // parametro di intersezione su curva 1
|
|
pCrv2->GetPointD1D2( dUS2, ICurve::FROM_PLUS, ptNE) ; // parametro di intersezione su curva 2
|
|
|
|
PtrOwner<ICurveArc> pSemiCir( CreateCurveArc()) ;
|
|
if ( pSemiCir->Set2PVN( ptNS, ptNE, vtS, Z_AX)) {
|
|
pCrvH1.Set( GetCurveComposite( pCrv1->CopyParamRange( dUS1, dUE1))) ;
|
|
pCrvH2.Set( GetCurveComposite( pCrv2->CopyParamRange( dUS2, dUE2))) ;
|
|
pCrvTempLink->AddCurve( pSemiCir->Clone()) ;
|
|
}
|
|
|
|
// controllo finale sui raccordi ammissibili ...
|
|
if ( pCrvTest->AddCurve( pCrvH1->Clone()) && pCrvTest->AddCurve( pCrvTempLink->Clone()) &&
|
|
pCrvTest->AddCurve( pCrvH2->Clone())) {
|
|
|
|
if ( ! pCrv1->TrimEndAtParam( dUE1))
|
|
pCrv1->Clear() ;
|
|
pCrvLink->Clear() ;
|
|
pCrvLink->AddCurve( Release( pCrvTempLink)) ;
|
|
if ( ! pCrv2->TrimStartAtParam( dUS2))
|
|
pCrv2->Clear() ;
|
|
return true ;
|
|
}
|
|
}
|
|
else {
|
|
// devo smussare la curva formata da pCrv1 - pCrvLink ( da creare) - pCrv2
|
|
pCrvH1->AddCurve( pCrv1->Clone()) ;
|
|
pCrvH2->AddCurve( pCrv2->Clone()) ;
|
|
pCrvTempLink->AddCurve( pCrvLink->Clone()) ;
|
|
|
|
// prendo le lunghezze necessarie
|
|
double dLenSeg1 = EPS_SMALL ; double dLenSeg2 = EPS_SMALL ;
|
|
double dLenI_s = EPS_SMALL ; // lunghezza prima curva del Link
|
|
double dLenI_e = EPS_SMALL ; // lunghezza ultima curva del Link
|
|
pCrvH1->GetLength( dLenSeg1) ;
|
|
pCrvH2->GetLength( dLenSeg2) ;
|
|
pCrvTempLink->GetFirstCurve()->GetLength( dLenI_s) ;
|
|
pCrvTempLink->GetLastCurve()->GetLength( dLenI_e) ;
|
|
|
|
// raccordo pSeg1 con pCrvLink
|
|
double dTollLeft = 1 - ( dLen1 - ( ( 2 * PockParams.dRad) / 16)) / dLen1 ; // % parametro sinistro per smusso
|
|
double dTollRight = ( ( 2 * PockParams.dRad) / 16) / dLenI_s ; // % parametro destro di smusso
|
|
PtrOwner<ICurveComposite> pCrv_Seg1_Isl( CreateCurveComposite()) ; // curva unione di pSeg1 e pCrvLink smussata
|
|
pCrv_Seg1_Isl->AddCurve( pCrvH1->Clone()) ;
|
|
pCrv_Seg1_Isl->AddCurve( pCrvTempLink->Clone()) ;
|
|
ModifyCurveToSmoothed( pCrv_Seg1_Isl, PockParams, dTollLeft, dTollRight, true) ;
|
|
|
|
// raccordo questa curva con pSeg2
|
|
double dUS_1IH, dUE_1IH, dToTLen ;
|
|
pCrv_Seg1_Isl->GetDomain( dUS_1IH, dUE_1IH) ; // dominio della curva smussata tra pSeg1 e PCrvLink
|
|
pCrv_Seg1_Isl->GetLength( dToTLen) ; // nuova lunghezza della curva smussata tra pSeg1 e pCrvLink
|
|
dTollLeft = 1 - ( dLenI_e - ( ( 2 * PockParams.dRad) / 16)) / dLenI_e ; // % paramtro sinistro per smusso
|
|
dTollRight = ( ( 2 * PockParams.dRad) / 16) / dLen2 ; // % parametro destro per smusso
|
|
PtrOwner<ICurveComposite> pCrv_smoothed( CreateCurveComposite()) ; // curva unione del primo smusso con pSeg2
|
|
pCrv_smoothed->AddCurve( pCrv_Seg1_Isl->Clone()) ;
|
|
pCrv_smoothed->AddCurve( pCrvH2->Clone()) ;
|
|
ModifyCurveToSmoothed( pCrv_smoothed, PockParams, dTollLeft, dTollRight, true) ;
|
|
|
|
// recupero i parametri del nuovo collegamento che si è creato dallo smusso
|
|
pCrvH1->Clear() ;
|
|
pCrvTempLink->Clear() ;
|
|
pCrvH2->Clear() ;
|
|
int nStat = -1 ;
|
|
for ( int u = 0 ; u < pCrv_smoothed->GetCurveCount() ; ++ u) {
|
|
// recupero la curva u-esima
|
|
const ICurve* pCrv = pCrv_smoothed->GetCurve( u) ;
|
|
if ( pCrv->GetType() == CRV_LINE) {
|
|
if ( CheckSimpleOverlap( pCrv, pCrv1, nStat, EPS_SMALL) && nStat == 1)
|
|
pCrvH1->AddCurve( pCrv->Clone()) ;
|
|
else if ( CheckSimpleOverlap( pCrv, pCrv2, nStat, EPS_SMALL) && nStat == 1)
|
|
pCrvH2->AddCurve( pCrv->Clone()) ;
|
|
}
|
|
}
|
|
|
|
Point3d ptCrv1_e, ptCrv2_s ;
|
|
double dULink_s = EPS_SMALL ;
|
|
double dULink_e = EPS_SMALL ;
|
|
|
|
pCrvH1->GetEndPoint( ptCrv1_e) ;
|
|
pCrv1->GetParamAtPoint( ptCrv1_e, dULink_s) ;
|
|
if ( ! pCrv1->TrimEndAtParam( dULink_s))
|
|
pCrv1->Clear() ;
|
|
|
|
pCrvH2->GetStartPoint( ptCrv2_s) ;
|
|
pCrv2->GetParamAtPoint( ptCrv2_s, dULink_e) ;
|
|
if ( ! pCrv2->TrimStartAtParam( dULink_e))
|
|
pCrv2->Clear() ;
|
|
|
|
pCrv_smoothed->GetParamAtPoint( ptCrv1_e, dULink_s) ;
|
|
pCrv_smoothed->GetParamAtPoint( ptCrv2_s, dULink_e) ;
|
|
pCrvLink->Clear() ;
|
|
pCrvLink->AddCurve( GetCurveComposite( pCrv_smoothed->CopyParamRange( dULink_s, dULink_e))) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcZigZag( const ISurfFlatRegion* pSrfZigZag, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vpCrvs,
|
|
bool bFromInfill)
|
|
{
|
|
|
|
// check parametri
|
|
if ( pSrfZigZag == nullptr || ! pSrfZigZag->IsValid())
|
|
return true ;
|
|
|
|
// creo il vettore dei contorni
|
|
ICRVCOMPOPOVECTOR vFirstOff ;
|
|
for ( int c = 0 ; c < pSrfZigZag->GetChunkCount() ; ++ c)
|
|
for ( int l = 0 ; l < pSrfZigZag->GetLoopCount( c) ; ++ l)
|
|
vFirstOff.emplace_back( GetCurveComposite( pSrfZigZag->GetLoop( c, l))) ;
|
|
|
|
// ingombro del contorno offsettato
|
|
BBox3d b3Pocket ;
|
|
vFirstOff[0]->GetLocalBBox( b3Pocket) ;
|
|
Point3d ptMin ; double dDimX, dDimY, dDimZ ;
|
|
b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ;
|
|
|
|
// lunghezza del contorno offsettato
|
|
double dLen ; vFirstOff[0]->GetLength( dLen) ;
|
|
|
|
// passi in Y
|
|
int nYStep ;
|
|
double dYStep ;
|
|
if ( ! bFromInfill) {
|
|
nYStep = static_cast< int >( ceil(( dDimY - 30 * EPS_SMALL) / PockParams.dSideStep)) ;
|
|
dYStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ;
|
|
}
|
|
else {
|
|
nYStep = static_cast< int >( floor(( dDimY + 30 * EPS_SMALL) / PockParams.dSideStep)) ;
|
|
dYStep = PockParams.dSideStep ;
|
|
}
|
|
|
|
int nRef = (( nYStep + ( PockParams.bInvert ? 0 : 1)) % 2) ;
|
|
|
|
// tratto valido
|
|
struct Section {
|
|
bool bActive ;
|
|
Point3d ptS ;
|
|
Point3d ptE ;
|
|
double dOs ;
|
|
double dOe ;
|
|
int nOffIndS ;
|
|
int nOffIndE ;
|
|
} ;
|
|
|
|
struct ParIsland {
|
|
double dU ;
|
|
int nInd ;
|
|
} ;
|
|
|
|
// raccolta di tratti
|
|
typedef vector<vector<Section>> VECVECSECT ; VECVECSECT vvSec ;
|
|
vvSec.resize( nYStep + 1) ;
|
|
|
|
// calcolo le linee di svuotatura
|
|
int nCount = 0 ;
|
|
for ( int i = 0 ; i <= nYStep ; ++ i) {
|
|
// determino senso
|
|
bool bPlus = (( i % 2) == nRef) ;
|
|
// definisco la linea
|
|
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
|
|
const double EXP_LEN = 1.0 ;
|
|
Point3d ptStart ;
|
|
if ( ! bFromInfill)
|
|
ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + 10 * EPS_SMALL + i * dYStep, ptMin.z + dDimZ) ;
|
|
else {
|
|
double dShift = ( i == 0 ? 10 * EPS_SMALL : ( i == nYStep ? - 10 * EPS_SMALL : 0.)) ;
|
|
ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + dShift + i * dYStep, ptMin.z + dDimZ) ;
|
|
}
|
|
if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN))
|
|
return false ;
|
|
|
|
if ( ! bPlus)
|
|
pLine->Invert() ;
|
|
|
|
// calcolo la classificazione della linea rispetto alla superficie
|
|
CRVCVECTOR ccClass ;
|
|
pSrfZigZag->GetCurveClassification( *pLine, EPS_SMALL, ccClass) ;
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
if ( ccClass[j].nClass == CRVC_IN) { // memorizzo il segmento
|
|
Section currSec ;
|
|
currSec.bActive = true ;
|
|
PtrOwner<ICurveLine> pSeg( GetCurveLine( pLine->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
|
|
if ( IsNull( pSeg))
|
|
continue ;
|
|
Point3d ptS, ptE ;
|
|
pSeg->GetStartPoint( ptS) ; pSeg->GetEndPoint( ptE) ;
|
|
currSec.ptS = ptS ; currSec.ptE = ptE ;
|
|
for ( int k = 0 ; k < int( vFirstOff.size()) ; ++ k) {
|
|
if ( vFirstOff[k]->IsPointOn( ptS)) {
|
|
currSec.nOffIndS = k ;
|
|
double dUOS; vFirstOff[k]->GetParamAtPoint( ptS, dUOS) ;
|
|
currSec.dOs = dUOS ;
|
|
}
|
|
if ( vFirstOff[k]->IsPointOn( ptE)){
|
|
currSec.nOffIndE = k ;
|
|
double dUOE; vFirstOff[k]->GetParamAtPoint( ptE, dUOE) ;
|
|
currSec.dOe = dUOE ;
|
|
}
|
|
}
|
|
vvSec[i].emplace_back( currSec) ;
|
|
++ nCount ; // numero di segmenti inseriti
|
|
}
|
|
}
|
|
}
|
|
|
|
int nStatus = 0 ; // 0 -> inizio oppure ho inserito una parte di isola | 2 -> sto inserendo un segmento
|
|
Point3d ptS_ref ;
|
|
PtrOwner<ICurveComposite> pLastSeg( CreateCurveComposite()) ; // ultimo segmento nel percorso
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ; // ultimo link aggiunto
|
|
// vettore dei link aggiunti ( per Feed )
|
|
ICRVCOMPOPOVECTOR vAddedLinks ;
|
|
bool bFirstLine = true ; // flag per prima linea di ogni percorso
|
|
|
|
// creo i percorsi di svuotatura
|
|
vpCrvs.reserve( nCount) ;
|
|
int nI = -1, nJ = -1 ;
|
|
while ( true) {
|
|
// se sezione non valida
|
|
if ( nI < 0 || nJ < 0) {
|
|
// ricerco la prima valida ( il primo segmento con bActive messo a true)
|
|
for ( int k = 0 ; k < int( vvSec.size()) && nI < 0 ; ++ k) {
|
|
for ( int l = 0 ; l < int( vvSec[k].size()) && nJ < 0 ; ++ l) {
|
|
if ( vvSec[k][l].bActive) {
|
|
nI = k ;
|
|
nJ = l ;
|
|
}
|
|
}
|
|
}
|
|
// se trovata, creo nuova curva composita
|
|
if ( nI >= 0 && nJ >= 0) {
|
|
// creo la curva
|
|
vpCrvs.emplace_back( CreateCurveComposite()) ;
|
|
nStatus = 0 ;
|
|
// aggiungo punto iniziale
|
|
vpCrvs.back()->AddPoint( vvSec[nI][nJ].ptS) ;
|
|
bFirstLine = true ;
|
|
}
|
|
// altrimenti, esco
|
|
else
|
|
break ;
|
|
}
|
|
// determino senso
|
|
bool bPlus = (( nI % 2) == nRef) ;
|
|
// aggiungo la sezione alla curva
|
|
Section& Sec = vvSec[nI][nJ] ;
|
|
Sec.bActive = false ;
|
|
// creo i vettori con le linee Under e Above nella struttura della Section
|
|
ICURVEPOVECTOR vLineAbove ;
|
|
if ( nI < nYStep) {
|
|
for ( int a = 0 ; a < ( int)vvSec[nI + 1].size() ; ++ a) {
|
|
if ( vvSec[nI + 1][a].bActive)
|
|
continue ;
|
|
PtrOwner<ICurveLine> pLA( CreateCurveLine()) ;
|
|
pLA->Set( vvSec[nI + 1][a].ptS, vvSec[nI + 1][a].ptE) ;
|
|
vLineAbove.emplace_back( Release( pLA)) ;
|
|
}
|
|
}
|
|
ICURVEPOVECTOR vLineUnder ;
|
|
if ( nI > 0) {
|
|
for ( int u = 0 ; u < int( vvSec[nI-1].size()) ; ++ u) {
|
|
if ( vvSec[nI-1][u].bActive)
|
|
continue ;
|
|
PtrOwner<ICurveLine> pLU( CreateCurveLine()) ;
|
|
pLU->Set( vvSec[nI-1][u].ptS, vvSec[nI-1][u].ptE) ;
|
|
vLineUnder.emplace_back( Release( pLU)) ;
|
|
}
|
|
}
|
|
// creo la linea come curva composita, aggiungerò dei Joint per ogni tratto con Feed differente
|
|
PtrOwner<ICurveComposite> pLineCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pLineCompo))
|
|
return false ;
|
|
|
|
Point3d ptE_l ;
|
|
if ( bFirstLine)
|
|
ptE_l = vvSec[nI][nJ].ptS ;
|
|
else
|
|
vpCrvs.back()->GetEndPoint( ptE_l) ;
|
|
|
|
pLineCompo->AddPoint( ptE_l) ;
|
|
pLineCompo->AddLine( vvSec[nI][nJ].ptE) ;
|
|
// assegno la feed al tratto lineare
|
|
if ( ! AssignFeedZigZagOneWay( pLineCompo, false, vLineAbove, vLineUnder, vAddedLinks, PockParams)) // Assegno la Feed
|
|
return false ;
|
|
|
|
bFirstLine = false ;
|
|
vpCrvs.back()->AddCurve( pLineCompo->Clone()) ; // aggiungo la curva al percorso
|
|
|
|
if ( nStatus == 0) { // primo segmento per il raccordo smussato
|
|
pLastSeg.Set( pLineCompo) ;
|
|
pLastSeg->GetStartPoint( ptS_ref) ;
|
|
}
|
|
if ( nStatus == 2) { // ho precedentemente aggiunto il bordo di un'isola, quindi mi salvo il secondo segmento
|
|
|
|
PtrOwner<ICurveComposite> pSeg1( pLastSeg->Clone()) ;
|
|
PtrOwner<ICurveComposite> pSeg2( pLineCompo->Clone()) ;
|
|
if ( IsNull( pSeg1) || IsNull( pSeg2))
|
|
return false ;
|
|
|
|
// aggiorno il link
|
|
if ( ! CalcZigZagLink( pSeg1, pSeg2, PockParams, pCrvLink))
|
|
return false ;
|
|
|
|
// aggiorno il vettore delle curve ZigZag
|
|
Point3d ptE_Seg1 ;
|
|
pSeg1->GetEndPoint( ptE_Seg1) ;
|
|
double dUS_Link = EPS_SMALL ;
|
|
vpCrvs.back()->GetParamAtPoint( ptE_Seg1, dUS_Link) ;
|
|
if ( ! vpCrvs.back()->TrimEndAtParam( dUS_Link))
|
|
vpCrvs.back()->Clear() ;
|
|
|
|
if ( bFromInfill)
|
|
for ( int u = 0 ; u < pCrvLink->GetCurveCount() ; ++ u)
|
|
pCrvLink->SetCurveTempProp( u, -1, 1) ;
|
|
|
|
if ( ! AssignFeedZigZagOneWay( pCrvLink, true, vLineAbove, vLineUnder, vAddedLinks, PockParams))
|
|
return false ;
|
|
|
|
vpCrvs.back()->AddCurve( pCrvLink->Clone()) ; // aggiungo il Link
|
|
vpCrvs.back()->AddCurve( pSeg2->Clone()) ; // aggiungo pSeg2
|
|
|
|
// aggiorno il vettore dei Link ( nel caso sia un link tra due segmenti sullo stesso livello )
|
|
Point3d ptS1 ;
|
|
pSeg1->GetStartPoint( ptS1) ;
|
|
Point3d ptS2 ;
|
|
pSeg2->GetStartPoint( ptS2) ;
|
|
if ( abs( ptS1.y - ptS2.y) < 50 * EPS_SMALL)
|
|
vAddedLinks.emplace_back( pCrvLink->Clone()) ;
|
|
|
|
// pSeg2 ora diventa pSeg1 e il procedimento si itera per tutto il percorso
|
|
pLastSeg.Set( pSeg2) ;
|
|
}
|
|
// cerco nella stessa fila o in quella successiva sezione successiva raccordabile tramite il contorno
|
|
double dUstart = Sec.dOe ;
|
|
int nIndexIslandE = Sec.nOffIndE ; // isola di arrivo
|
|
double dUmin, dUmax ;
|
|
vFirstOff[nIndexIslandE]->GetDomain( dUmin, dUmax) ;
|
|
double dUspan = dUmax - dUmin ;
|
|
double dUref = ( bPlus ? INFINITO : -INFINITO) ;
|
|
int nNextI = -1 ;
|
|
int nNextJ = -1 ;
|
|
int li = nJ + 1 ;
|
|
for ( int k = nI ; k <= nI + 1 && k < int( vvSec.size()) ; ++ k) {
|
|
for ( int l = li ; l < int( vvSec[k].size()) ; ++ l) {
|
|
if ( ! vvSec[k][l].bActive) // se sezione non attiva... non la considero
|
|
continue ;
|
|
if ( vvSec[k][l].nOffIndS != nIndexIslandE) // se isola diversa... non la considero
|
|
continue ;
|
|
double dU = vvSec[k][l].dOs ;
|
|
if ( bPlus) {
|
|
if ( dU < dUstart)
|
|
dU += dUspan ;
|
|
if ( dU < dUref) {
|
|
dUref = dU ;
|
|
nNextI = k ;
|
|
nNextJ = l ;
|
|
}
|
|
}
|
|
else {
|
|
if ( dU > dUstart)
|
|
dU -= dUspan ;
|
|
if ( dU > dUref) {
|
|
dUref = dU ;
|
|
nNextI = k ;
|
|
nNextJ = l ;
|
|
}
|
|
}
|
|
}
|
|
li = 0 ;
|
|
}
|
|
// se trovato, controllo il contorno dell'isola
|
|
if ( nNextI != -1) {
|
|
PtrOwner<ICurve> pCopy ;
|
|
if ( bPlus) {
|
|
if ( dUref > dUmax)
|
|
dUref -= dUspan ;
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ;
|
|
if ( ! IsNull( pCopy)) {
|
|
double dCLen ; pCopy->GetLength( dCLen) ;
|
|
if ( dCLen > 0.5 * dLen) {
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ;
|
|
if ( ! IsNull( pCopy))
|
|
pCopy->Invert() ;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ( dUref < dUmin)
|
|
dUref += dUspan ;
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ;
|
|
if ( ! IsNull( pCopy)) {
|
|
pCopy->Invert() ;
|
|
double dCLen; pCopy->GetLength( dCLen) ;
|
|
if ( dCLen > 0.5 * dLen)
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ;
|
|
}
|
|
}
|
|
// controllo che nel percorso scelto non ritorni indietro superando la precendete passata
|
|
BBox3d b3Copy ;
|
|
if ( ! IsNull( pCopy))
|
|
pCopy->GetLocalBBox( b3Copy) ;
|
|
if ( ! b3Copy.IsEmpty() && ( b3Copy.GetMax().y - b3Copy.GetMin().y) < dYStep + 10 * EPS_SMALL) { // tengo la curva (non ritorno indietro più dello step)
|
|
Point3d ptS, ptE ;
|
|
pCrvLink->Clear() ;
|
|
pCrvLink->AddCurve( pCopy->Clone()) ;
|
|
vpCrvs.back()->AddCurve( Release( pCopy)) ;
|
|
nStatus = 2 ; // aggiunta parte di isola
|
|
// aggiungo il Link
|
|
nI = nNextI ;
|
|
nJ = nNextJ ;
|
|
}
|
|
else {
|
|
nI = -1 ;
|
|
nJ = -1 ;
|
|
}
|
|
}
|
|
else {
|
|
nI = -1 ;
|
|
nJ = -1 ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddSpiralIn( const ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
|
|
const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// ciclo sui chunk della superficie da svuotare
|
|
for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; ++ c) {
|
|
|
|
// copio il chunk c-esimo
|
|
PtrOwner<ISurfFlatRegion> pSrfChunk( pSrfPock->CloneChunk( c)) ;
|
|
if ( IsNull( pSrfChunk))
|
|
return false ;
|
|
|
|
const int MAX_REGS = 50 ; // massimo numero di regioni create dal primo offset
|
|
int nReg = 0 ; // chunk nuovo corrente da svuotare
|
|
|
|
// ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-esimo
|
|
while ( nReg < MAX_REGS) {
|
|
// calcolo la spirale dall'esterno all'interno e la curva che unisce inizio e fine
|
|
PtrOwner<ICurveComposite> pMCrv( CreateCurveComposite()) ; // percorso di svuotatura
|
|
PtrOwner<ICurveComposite> pRCrv( CreateCurveComposite()) ; // percorso di ritorno
|
|
if ( IsNull( pMCrv) || IsNull( pRCrv))
|
|
return false ;
|
|
|
|
int nRegTot = nReg ;
|
|
Point3d ptStart ;
|
|
bool bOptimizedTrap = false ;
|
|
|
|
// cerco le curve originali del chunk cc-esimo ( per casi ottimizzati)
|
|
ICRVCOMPOPOVECTOR vCrvOrigChunkLoops ;
|
|
if ( ! GetOptCrvIndex( pSfrOrig, pSrfChunk, PockParams, nReg, vCrvOrigChunkLoops))
|
|
return false ;
|
|
|
|
// calcolo il percorso di svuotatura
|
|
bool bSomeOpen ;
|
|
Vector3d vtMidOut ;
|
|
if ( ! CalcSpiral( pSrfChunk, PockParams, nRegTot, ptStart, vCrvOrigChunkLoops, pMCrv, pRCrv, bOptimizedTrap,
|
|
bSomeOpen, vtMidOut))
|
|
return false ;
|
|
|
|
// se terminate le regioni, esco
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
break ; // passo al chunk originale successivo
|
|
|
|
if ( bOptimizedTrap) { // se caso a trapezio
|
|
AdjustTrapezoidSpiralForLeadInLeadOut( pMCrv, PockParams) ;
|
|
AssignFeedSpiralOpt( 1, PockParams, pMCrv) ;
|
|
pMCrv->SetTempProp( TMP_PROP_OPT_TRAPEZOID, 0) ;
|
|
}
|
|
else if ( bSomeOpen) { // se presenza di lati aperti
|
|
if ( ! ExtendPathOnOpenEdge( pMCrv, PockParams, pSrfChunk->GetNormVersor(), vtMidOut, false))
|
|
return false ;
|
|
}
|
|
|
|
// inserisco le curve nel vettore
|
|
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
|
|
++ nReg ; // incremento il numero di regione progressiva
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddSpiralOut( const ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
|
|
const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// ciclo sui chunk della superficie da svuotare
|
|
for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; ++ c) {
|
|
|
|
// copio il chunk c-esimo
|
|
PtrOwner<ISurfFlatRegion> pSrfChunk( pSrfPock->CloneChunk( c)) ;
|
|
if ( IsNull( pSrfChunk))
|
|
return false ;
|
|
|
|
const int MAX_REGS = 50 ; // massimo numero di regioni create dal primo offset
|
|
int nReg = 0 ; // chunk nuovo corrente da svuotare
|
|
|
|
// ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-esimo
|
|
while ( nReg < MAX_REGS) {
|
|
|
|
// calcolo la spirale dall'interno all'esterno
|
|
PtrOwner<ICurveComposite> pMCrv( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pRCrv( CreateCurveComposite()) ;
|
|
if ( IsNull( pMCrv) || IsNull( pRCrv))
|
|
return false ;
|
|
|
|
int nRegTot = nReg ;
|
|
Point3d ptStart ;
|
|
bool bOptimizedTrap = false ;
|
|
|
|
// cerco la curva originale del chunk cc-esimo ( per casi ottimizzati)
|
|
ICRVCOMPOPOVECTOR vCrvOrig ;
|
|
if ( ! GetOptCrvIndex( pSfrOrig, pSrfChunk, PockParams, nReg, vCrvOrig))
|
|
return false ;
|
|
|
|
// calcolo il percorso di svuotatura
|
|
bool bSomeOpen ;
|
|
Vector3d vtMidOut ;
|
|
if ( ! CalcSpiral( pSrfChunk, PockParams, nRegTot, ptStart, vCrvOrig, pMCrv, pRCrv, bOptimizedTrap,
|
|
bSomeOpen, vtMidOut))
|
|
return false ;
|
|
|
|
// se terminate le regioni, esco
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
break ; // passo al chunk originale successivo
|
|
|
|
if ( bOptimizedTrap) { // se caso a trapezio
|
|
AdjustTrapezoidSpiralForLeadInLeadOut( pMCrv, PockParams) ;
|
|
AssignFeedSpiralOpt( 1, PockParams, pMCrv) ;
|
|
pMCrv->SetTempProp( TMP_PROP_OPT_TRAPEZOID, 0) ;
|
|
}
|
|
|
|
// inverto i percorsi, perchè sono calcolati dall'esterno all'interno (solo nel caso non ottimizzato)
|
|
pMCrv->Invert() ;
|
|
pRCrv->Invert() ;
|
|
|
|
// inserisco le curve nel vettore
|
|
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
|
|
++ nReg ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddZigZag( const ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// Offset della regione per curva ZigZag
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
|
|
// ciclo su tutti i chunks della superficie originale
|
|
for ( int c = 0 ; c < pSrfPock->GetChunkCount() ; ++ c) {
|
|
|
|
// copio il chunk c-esimo
|
|
PtrOwner<ISurfFlatRegion> pSrfChunk( pSrfPock->CloneChunk( c)) ;
|
|
if ( IsNull( pSrfChunk))
|
|
return false ;
|
|
|
|
// determino il riferimento in base alla svuotatura ( per poter orientare il frame per m_dSideAngle)
|
|
Frame3d frPocket ;
|
|
Point3d ptCen ; pSrfChunk->GetCentroid( ptCen) ;
|
|
frPocket.Set( ptCen, pSrfChunk->GetNormVersor()) ;
|
|
frPocket.Rotate( ptCen, pSrfChunk->GetNormVersor(), PockParams.dAngle) ;
|
|
|
|
// porto la superficie nel nuovo sistema di riferimento
|
|
pSrfChunk->ToLoc( frPocket) ;
|
|
|
|
// creo la regione per il percorso a ZigZag
|
|
PtrOwner<ISurfFlatRegion> pSrfZigZag( pSrfChunk->Clone()) ;
|
|
if ( ! pSrfZigZag->Offset( - dOffs, ICurve::OFF_FILLET))
|
|
return false ;
|
|
|
|
// vettore con i percorsi a ZigZag
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
|
|
// ciclo sui Chunk ottenuti...
|
|
for ( int cfz = 0 ; cfz < pSrfZigZag->GetChunkCount() ; ++ cfz) {
|
|
// considero un chunk per volta...
|
|
PtrOwner<ISurfFlatRegion> pSrfZigZagChunk( pSrfZigZag->CloneChunk( cfz)) ;
|
|
if ( IsNull( pSrfZigZagChunk))
|
|
return false ;
|
|
// calcolo i percorsi di ZigZag
|
|
if ( ! CalcZigZag( pSrfZigZagChunk, PockParams, vpCrvs, false))
|
|
return false ;
|
|
// aggiorno il vettore delle curve risultati
|
|
for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) {
|
|
|
|
// riportando prima in Globale
|
|
vpCrvs[u]->ToGlob( frPocket) ;
|
|
|
|
// controllo se il punto iniziale e finale sono esterni alla superficie originale
|
|
Point3d ptStart ; vpCrvs[u]->GetStartPoint( ptStart) ;
|
|
bool bIsInside = false ;
|
|
if ( IsPointInsideSfr( pSfrOrig, ptStart, bIsInside) && ! bIsInside) {
|
|
Vector3d vtMidOut ; vpCrvs[u]->GetStartDir( vtMidOut) ;
|
|
vtMidOut.Invert() ;
|
|
ExtendPathOnOpenEdge( vpCrvs[u], PockParams, pSrfChunk->GetNormVersor(), vtMidOut, false) ;
|
|
}
|
|
Point3d ptEnd ; vpCrvs[u]->GetEndPoint( ptEnd) ;
|
|
if ( IsPointInsideSfr( pSfrOrig, ptEnd, bIsInside) && ! bIsInside) {
|
|
Vector3d vtMidOut ; vpCrvs[u]->GetEndDir( vtMidOut) ;
|
|
ExtendPathOnOpenEdge( vpCrvs[u], PockParams, pSrfChunk->GetNormVersor(), vtMidOut, true) ;
|
|
}
|
|
|
|
// inserisco le curve nel vettore risultante
|
|
vCrvCompoRes.emplace_back( Release( vpCrvs[u])) ;
|
|
}
|
|
// libero il vettore di curve ZigZag per il chunk successivo
|
|
vpCrvs.clear() ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
OptimizeChunkOneWay( ISurfFlatRegion* pSrfOrig, ISURFFRPOVECTOR& vSrfIdeal)
|
|
{
|
|
// controllo parametri
|
|
if ( pSrfOrig == nullptr || pSrfOrig->GetChunkCount() == 0)
|
|
return false ;
|
|
vSrfIdeal.clear() ;
|
|
|
|
// clono la superficie passata alla funzione
|
|
PtrOwner<ISurfFlatRegion> pSrfToPock( pSrfOrig->Clone()) ;
|
|
if ( IsNull( pSrfToPock) || pSrfToPock->GetChunkCount() == 0)
|
|
return false ;
|
|
|
|
// classifico i chunks in modo da creare delle regioni ideali; una regione ideale è formata da un chunk principale
|
|
// con tutti gli altri contenuti in esso ( in questo modo ottimizzo i percorsi per i bordi)
|
|
|
|
INTVECTOR vChunksAvailable( pSrfToPock->GetChunkCount(), 1) ;
|
|
|
|
for ( int c = 0 ; c < pSrfToPock->GetChunkCount() ; ++ c) {
|
|
PtrOwner<ISurfFlatRegion> pSrfIdeal( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfIdeal))
|
|
return false ;
|
|
if ( vChunksAvailable[c] == 1) { // se Chunk valido...
|
|
// prendo la curva esterna
|
|
PtrOwner<ICurve> pCrvExt( pSrfToPock->GetLoop( c, 0)) ;
|
|
if ( IsNull( pCrvExt))
|
|
return false ;
|
|
// inserisco il chunk c-esimo ( curva per curva, così non perdo le temp prop)
|
|
pSrfIdeal->AddExtLoop( pCrvExt->Clone()) ; // ... inizio a creare la regione ideale da esso
|
|
for ( int l = 1 ; l < pSrfToPock->GetLoopCount( c) ; ++ l)
|
|
pSrfIdeal->AddIntLoop( pSrfToPock->GetLoop( c, l)) ;
|
|
// il chunk c-esimo non è più disponibile...
|
|
vChunksAvailable[c] = -1 ;
|
|
// scorro tutti gli altri chunk i-esimi ancora disponibili
|
|
for ( int i = 0 ; i < int( vChunksAvailable.size()) ; ++ i) {
|
|
if ( vChunksAvailable[i] == 1) {
|
|
// prendo la curva esterna del chunk i-esimo disponibile
|
|
PtrOwner<ICurve> pCrvExtC( pSrfToPock->GetLoop( i, 0)) ;
|
|
if ( IsNull( pCrvExtC))
|
|
return false ;
|
|
// classifico i bordi esterni ( se il bordo del chunk c-esimo contiene o è contenuto nel loop
|
|
// esterno del chunk i-esimo -> sono chunks appartenenti alla stessa superficie ideale)
|
|
IntersCurveCurve intCC( *pCrvExtC, *pCrvExt) ;
|
|
CRVCVECTOR ccClass, ccClass1 ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
intCC.GetCurveClassification( 1, EPS_SMALL, ccClass1) ;
|
|
if (( ccClass.size() == 1 && ccClass[0].nClass == CRVC_IN) ||
|
|
( ccClass1.size() == 1 && ccClass1[0].nClass == CRVC_IN)) {
|
|
// inserisco il chunk i-esimo ( curva per curva, così non perdo le temp prop)
|
|
pSrfIdeal->AddExtLoop( pSrfToPock->GetLoop( i, 0)) ;
|
|
for ( int l = 1 ; l < pSrfToPock->GetLoopCount( i) ; ++ l)
|
|
pSrfIdeal->AddIntLoop( pSrfToPock->GetLoop( i, l)) ;
|
|
vChunksAvailable[i] = -1 ; // chunk j-esimo non più disponibile
|
|
// NB: Se il chunk i-esimo contiene la pSrfIdeal ( quindi chunk i-esimo unito ad eventuali altri chunk
|
|
// j-esimi precedenti -> prendo come bordo esterno di riferimento il suo e ricontrollo dall'inizio i
|
|
// chunk disponibili
|
|
if (( int)ccClass1.size() == 1 && ccClass1[0].nClass == CRVC_IN) {
|
|
pCrvExt.Set( pCrvExtC->Clone()) ;
|
|
i = 0 ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pSrfIdeal->GetChunkCount() > 0)
|
|
vSrfIdeal.emplace_back( Release( pSrfIdeal)) ;
|
|
|
|
}
|
|
|
|
return ( ! vSrfIdeal.empty()) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddOneWay( const ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
double dExtra = 0. ;
|
|
|
|
// copio la regione da svuotare
|
|
PtrOwner<ISurfFlatRegion> pSrfToPock( pSrfPock->Clone()) ;
|
|
if ( IsNull( pSrfToPock))
|
|
return false ;
|
|
|
|
// creo le regioni ideali --------------------------------------------
|
|
// creo un frame di riferimento della svuotatura
|
|
Frame3d frLoc ;
|
|
Point3d ptC ; pSrfToPock->GetCentroid( ptC) ;
|
|
Vector3d vtN = pSrfToPock->GetNormVersor() ;
|
|
frLoc.Set( ptC, vtN) ;
|
|
pSrfToPock->ToLoc( frLoc) ;
|
|
|
|
ISURFFRPOVECTOR vSrfFlat ; // vettore delle regioni ideali
|
|
if ( ! OptimizeChunkOneWay( pSrfToPock, vSrfFlat))
|
|
return false ;
|
|
|
|
// riporto le superfici ideali nel sistema di riferimento globale ( Ogni chunk verrà poi messo nel suo frame)
|
|
for ( int i = 0 ; i < int( vSrfFlat.size()) ; ++ i)
|
|
vSrfFlat[i]->ToGlob( frLoc) ;
|
|
// ------------------------------------------------------------------
|
|
|
|
// scorro le regioni ideali
|
|
for ( int nIs = 0 ; nIs < int( vSrfFlat.size()) ; ++ nIs) {
|
|
|
|
// copio la superficie ideale
|
|
PtrOwner<ISurfFlatRegion> pSrfIdeal( vSrfFlat[nIs]->Clone()) ;
|
|
if ( IsNull( pSrfIdeal))
|
|
return false ;
|
|
|
|
// effettuo Offset interno
|
|
if ( ! pSrfIdeal->Offset( - dOffs, ICurve::OFF_FILLET))
|
|
return false ;
|
|
|
|
// creo un frame coerente con i parametri
|
|
Frame3d frPocket ;
|
|
Point3d ptCen ; pSrfIdeal->GetCentroid( ptCen) ;
|
|
frPocket.Set( ptCen, pSrfIdeal->GetNormVersor()) ;
|
|
frPocket.Rotate ( ptCen, pSrfIdeal->GetNormVersor(), PockParams.dAngle) ;
|
|
pSrfIdeal->ToLoc( frPocket) ;
|
|
BBox3d b3Pocket ; pSrfIdeal->GetLocalBBox( b3Pocket) ;
|
|
Point3d ptMin ; double dDimX, dDimY, dDimZ ;
|
|
b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ;
|
|
|
|
// passi in Y
|
|
int nYStep = static_cast<int>( ceil(( dDimY + 2 * dExtra) / PockParams.dSideStep)) ;
|
|
double dYStep = ( nYStep > 0 ? ( dDimY + 2 * dExtra) / nYStep : 0) ;
|
|
-- nYStep ;
|
|
|
|
// vettore dei segmenti al di sotto della linea corrente
|
|
ICURVEPOVECTOR vLineUnder ;
|
|
ICURVEPOVECTOR vCrvNull ;
|
|
ICRVCOMPOPOVECTOR vCrvCompoNull ;
|
|
|
|
// calcolo le linee di svuotatura
|
|
const double EXP_LEN = 1.0 ;
|
|
for ( int i = 1 ; i <= nYStep ; ++ i) {
|
|
|
|
// definisco la linea
|
|
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
|
|
Point3d ptStart( ptMin.x - EXP_LEN, ptMin.y + ( - dExtra + i * dYStep), ptMin.z + dDimZ) ;
|
|
if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN))
|
|
return false ;
|
|
|
|
// se richiesta inversione
|
|
if ( PockParams.bInvert)
|
|
pLine->Invert() ;
|
|
|
|
// linea come composita per Feed ( la dovrò spezzare)
|
|
PtrOwner<ICurveComposite> pCrvCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvCompo))
|
|
return false ;
|
|
|
|
// vettore di tutti i segmenti lineari che si formano nello step nYStep
|
|
ICURVEPOVECTOR vAddedLines ;
|
|
|
|
// riempio il vettore di segmenti
|
|
CRVCVECTOR ccClassSeg ;
|
|
pSrfIdeal->GetCurveClassification( *pLine, EPS_SMALL, ccClassSeg) ;
|
|
for ( int w = 0 ; w < int( ccClassSeg.size()) ; ++ w) {
|
|
if ( ccClassSeg[w].nClass == CRVC_IN) {
|
|
|
|
PtrOwner<ICurveLine> pCrvSeg( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ;
|
|
double duF, dLen ;
|
|
// accorcio leggermente il segmento per non toccare la prima curva di Offset
|
|
if ( ! pCrvSeg->GetLength( dLen) ||
|
|
dLen < 2 * dExtra ||
|
|
! pCrvSeg->GetParamAtLength( dLen - dExtra, duF) ||
|
|
! pCrvSeg->TrimStartAtLen( dExtra) ||
|
|
! pCrvSeg->TrimEndAtParam( duF))
|
|
pCrvSeg.Set( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ;
|
|
|
|
// aggiungo il segmento al vettore dei tratti correnti
|
|
vAddedLines.emplace_back( pCrvSeg->Clone()) ;
|
|
|
|
// trasformo il tratto lineare in curve composita per il calcolo della Feed
|
|
PtrOwner<ICurveComposite> pCrvSegCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvSegCompo))
|
|
return false ;
|
|
pCrvSegCompo->AddCurve( Release( pCrvSeg)) ;
|
|
// calcolo la Feed
|
|
AssignFeedZigZagOneWay( pCrvSegCompo, false, vCrvNull, vLineUnder, vCrvCompoNull, PockParams) ;
|
|
pCrvSegCompo->ToGlob( frPocket) ;
|
|
vCrvCompoRes.emplace_back( Release( pCrvSegCompo)) ;
|
|
}
|
|
}
|
|
// i tratti lineari correnti diventano i precedenti
|
|
vLineUnder.clear() ;
|
|
for ( int u = 0 ; u < int( vAddedLines.size()) ; ++ u)
|
|
vLineUnder.emplace_back( Release( vAddedLines[u])) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double dStep, double dAngle,
|
|
int nType, bool bSmooth, bool bInvert, bool bAvoidOpt, ISurfFlatRegion* pSfrLimit, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid() ||
|
|
dStep < 10 * EPS_SMALL ||
|
|
( nType != POCKET_ZIGZAG && nType != POCKET_ONEWAY && nType != POCKET_SPIRALIN && nType != POCKET_SPIRALOUT))
|
|
return false ;
|
|
|
|
// pulizia vettore delle curve elementari
|
|
vCrvCompoRes.clear() ;
|
|
|
|
// assegno dati di modulo
|
|
PocketParams myParams ;
|
|
myParams.nType = nType ;
|
|
myParams.dRad = dRad ;
|
|
myParams.dRadialOffset = dRadOffs ;
|
|
myParams.bSmooth = bSmooth ;
|
|
myParams.dAngle = dAngle ;
|
|
myParams.dSideStep = dStep ;
|
|
myParams.bInvert = bInvert ;
|
|
myParams.bAvoidOpt = bAvoidOpt ;
|
|
if ( pSfrLimit != nullptr && pSfrLimit->IsValid())
|
|
myParams.SfrLimit.CopyFrom( pSfrLimit) ;
|
|
|
|
// ------------ gestione dei lati aperti -------------------
|
|
// creo un sistema di riferimento locale
|
|
Point3d ptCenter ; pSfr->GetCentroid( ptCenter) ;
|
|
Vector3d vtN = pSfr->GetNormVersor() ;
|
|
Frame3d frLoc ;
|
|
if ( ! frLoc.Set( ptCenter, vtN))
|
|
return false ;
|
|
// porto la superficie da svuotare in tale sistema
|
|
PtrOwner<ISurfFlatRegion> pSfrAdj( pSfr->Clone()) ;
|
|
if ( IsNull( pSfrAdj) || ! pSfrAdj->ToLoc( frLoc))
|
|
return false ;
|
|
// porto la superficie limite ( se valida) in tale sistema
|
|
if ( myParams.SfrLimit.IsValid())
|
|
myParams.SfrLimit.ToLoc( frLoc) ;
|
|
// modifico la superficie da svuotare estendendo i lati aperti
|
|
bool bSkipPocket = false ; // se la superficie si svuota mediante curve singole di primo Offset dei chiusi
|
|
if ( ! ModifySurfByOpenEdges( pSfrAdj, myParams, vCrvCompoRes, bSkipPocket))
|
|
return false ;
|
|
// se ottengo delle curve si svuotatura di primo Offset, le riporto in globale
|
|
for ( int i = 0 ; i < int( vCrvCompoRes.size()) ; ++ i)
|
|
vCrvCompoRes[i]->ToGlob( frLoc) ;
|
|
if ( bSkipPocket) // se tali curva rimuovono tutta la regione, allora non faccio nulla
|
|
return true ;
|
|
// riporto tutto nel frame originale
|
|
pSfrAdj->ToGlob( frLoc) ;
|
|
if ( myParams.SfrLimit.IsValid())
|
|
myParams.SfrLimit.ToGlob( frLoc) ;
|
|
|
|
// ------------ calcolo delle curve elementari della superficie -------------------
|
|
switch ( nType) {
|
|
case POCKET_ZIGZAG :
|
|
return AddZigZag( pSfrAdj, pSfr, myParams, vCrvCompoRes) ;
|
|
case POCKET_ONEWAY :
|
|
return AddOneWay( pSfrAdj, pSfr, myParams, vCrvCompoRes) ;
|
|
case POCKET_SPIRALIN :
|
|
return AddSpiralIn( pSfrAdj, pSfr, myParams, vCrvCompoRes) ;
|
|
case POCKET_SPIRALOUT :
|
|
return AddSpiralOut( pSfrAdj, pSfr, myParams, vCrvCompoRes) ;
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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 ;
|
|
}
|