b0f7cb93fd
- Aggiunto parametro in CalcPocketing.
9054 lines
374 KiB
C++
9054 lines
374 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2023-2023
|
|
//----------------------------------------------------------------------------
|
|
// File : EGkCalcPocketing.h Data : 16.11.23 Versione : 2.5j1
|
|
// Contenuto : Dichiarazione della funzione per calcolare le curve base di Pocketing
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 16.11.23 RE Creazione modulo.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "CurveLine.h"
|
|
#include "CurveArc.h"
|
|
#include "CurveComposite.h"
|
|
#include "BiArcs.h"
|
|
#include "SurfFlatRegion.h"
|
|
#include "GeoConst.h"
|
|
#include "Voronoi.h"
|
|
#include "/EgtDev/Include/EGkSfrCreate.h"
|
|
#include "/EgtDev/Include/EGkCurveAux.h"
|
|
#include "/EgtDev/Include/EGkCalcPocketing.h"
|
|
#include "/EgtDev/Include/EGkFilletChamfer.h"
|
|
#include "/EgtDev/Include/EGkDistPointCurve.h"
|
|
#include "/EgtDev/Include/EGkDistPointSurfFr.h"
|
|
#include "/EgtDev/Include/EGkCurveLocal.h"
|
|
#include "/EgtDev/Include/EGkMedialAxis.h"
|
|
#include "/EgtDev/Include/EGkLinePntTgCurve.h"
|
|
#include "/EgtDev/Include/EGkOffsetCurve.h"
|
|
#include "/EgtDev/Include/EGkIntervals.h"
|
|
#include "/EgtDev/Include/EGkChainCurves.h"
|
|
#include "/EgtDev/Include/EgtNumUtils.h"
|
|
#include <array>
|
|
|
|
using namespace std ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Calcolo delle curve elementari di Pocketing
|
|
//----------------------------------------------------------------------------
|
|
|
|
// variabili d'appoggio ( per non passare troppi parametri tra le funzioni secondarie)
|
|
struct PocketParams {
|
|
int nType = POCKET_SPIRALIN ;
|
|
double dRad = 0. ; // raggio utensile
|
|
double dRad_prec = -1. ; // raggio utensile della lavorazione precedente
|
|
double dSideStep = 0. ; // step
|
|
double dSideStep_prec = -1. ; // step della lavorazione precedente
|
|
double dRadialOffset = 0. ; // offset radiale
|
|
double dRadialOffset_prec = -1. ; // offset radiale precedente
|
|
double dMaxOpenEdgeRad = 0. ; // massimo raggio per estensione lati aperti
|
|
double dOpenMinSafe = 5. ; // estensione minima di sicurezza
|
|
double dMaxOptSize = 0. ; // dimensione per ottimizzazione
|
|
double dAngle = 0. ; // angolo per orientare le passate OneWay e ZigZag
|
|
bool bOptOffsets = true ; // flag per evitare Offset non necessari in SpiralIn/Out
|
|
bool bOptOffsetsAdv = false ; // flag per evitare Offset coperti da curve di MedialAxis in SpiralIn/Out
|
|
bool bAboveHead = true ; // flag per testa da sopra ( Z+)
|
|
bool bSmooth = false ; // curve smussate
|
|
bool bInvert = false ; // inversione dei percorsi
|
|
bool bAvoidOpt = false ; // flag per evitare casi ottimizzati
|
|
bool bAllowZigZagOneWayBorders = false ; // flag per abilitare le curve di bordo per ZigZag/OneWay
|
|
Point3d ptStart = P_INVALID ; // punto d'inizio
|
|
Point3d ptEnd = P_INVALID ; // punto di fine
|
|
SurfFlatRegion SfrLimit ; // superficie limite per estensione lati aperti
|
|
bool bCalcFeed = true ; // flag per calcolo della Feed
|
|
double dFeed = 1000 ; // feed di riferimento per frazione
|
|
double dToolFeed = 1000 ; // feed del tool
|
|
// -------------------------------------------------------
|
|
bool bAllClosed = true ; // flag per indicare se tutti i bordi della *pSfr da svuotare sono chiusi
|
|
bool bAllOpen = true ; // flag per indicare se tutti i bordi della *pSfr da svuotare sono aperti
|
|
double dOffsExtra = 2. ; // Offset aggiuntivo per percosi a ZigZag/OneWay se richieste le curve di bordo
|
|
Frame3d frLocXY ; // frame per conti nel piano XY
|
|
int nOffsType = ICurve::OFF_FILLET ; // tipologia di Offset per estensione degli aperti
|
|
double dOpenEdgeRad = 0. ; // raggio effettivo di estensione degli aperti
|
|
} ;
|
|
static double TOL_TRAPEZOID = 50 * EPS_SMALL ; // tolleranza per casi a trapezio SpiralPocket
|
|
typedef vector<ICRVCOMPOPOVECTOR> VICRVCOMPOPOVECTOR ;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// definizione varibaile di debug
|
|
#define ENABLE_DEBUG 0
|
|
#if ENABLE_DEBUG
|
|
#include "EgtDev/Include/EGkGeoObjSave.h"
|
|
#include "EgtDev/Include/EGkGeoPoint3d.h"
|
|
#include "EgtDev/Include/EGkGeoVector3d.h"
|
|
//----------------------------------------------------------------------------
|
|
// Debug Functions
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
DrawObjs( const ISURFFRPOVECTOR& vSfr, bool bSfrUniform, bool bAlphaCoverage,
|
|
const ICRVCOMPOPOVECTOR& vCompo, bool bCompoUniform, string sName)
|
|
{
|
|
// definisco i vettori per geometrie e colori
|
|
vector<IGeoObj*> VT ;
|
|
vector<Color> VC ;
|
|
|
|
// disegno le superfici
|
|
for ( int nS = 0 ; nS < int( vSfr.size()) ; ++ nS) {
|
|
if ( vSfr[nS] == nullptr || ! vSfr[nS]->IsValid())
|
|
return ;
|
|
VT.emplace_back( static_cast<IGeoObj*>( vSfr[nS]->Clone())) ;
|
|
VC.emplace_back( bAlphaCoverage ? Color( 0., 64., 0., .5) : Color( 0., 255., 0., .5)) ;
|
|
for ( int nC = 0 ; nC < vSfr[nS]->GetChunkCount() ; ++ nC) {
|
|
for ( int nL = 0 ; nL < vSfr[nS]->GetLoopCount( nC) ; ++ nL) {
|
|
PtrOwner<ICurveComposite> pCrvCompo( ConvertCurveToComposite( vSfr[nS]->GetLoop( nC, nL))) ;
|
|
for ( int nU = 0 ; nU < pCrvCompo->GetCurveCount() ; ++ nU) {
|
|
int nProp0 ; pCrvCompo->GetCurveTempProp( nU, nProp0, 0) ;
|
|
VT.emplace_back( static_cast<IGeoObj*>( pCrvCompo->GetCurve( nU)->Clone())) ;
|
|
VC.emplace_back( bSfrUniform ? WHITE : ( nProp0 == 0 ? ( bAlphaCoverage ? AQUA : BLUE) : ( bAlphaCoverage ? ORANGE : RED))) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// disegno le curve
|
|
for ( int nC = 0 ; nC < int( vCompo.size()) ; ++ nC) {
|
|
if ( vCompo[nC] == nullptr || ! vCompo[nC]->IsValid())
|
|
continue ;
|
|
VT.emplace_back( static_cast<IGeoObj*>( vCompo[nC]->Clone())) ;
|
|
VC.emplace_back( Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.)) ;
|
|
}
|
|
|
|
SaveGeoObj( VT, VC, sName) ;
|
|
return ;
|
|
}
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------------
|
|
static double
|
|
GetMaxFeed( const PocketParams& PockParams)
|
|
{
|
|
return PockParams.dFeed ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static double
|
|
GetMinFeed( const PocketParams& PockParams)
|
|
{
|
|
return PockParams.dFeed * PockParams.dSideStep / ( 2 * PockParams.dRad) ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static double
|
|
GetFeed( const PocketParams& PockParams)
|
|
{
|
|
return ( PockParams.dFeed > 0 ? PockParams.dFeed : PockParams.dToolFeed) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignMaxFeed( ICurveComposite* pCrv, const PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// assegno la Feed massima ad ogni sottocurva di pCrv
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
AssignMinFeed( ICurveComposite* pCrv, const PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// assegno la Feed minima ad ogni sottocurva di pCrv
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
AssignCustomFeed( ICurveComposite* pCrv, const PocketParams& PockParams, double dFeed)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
// La Feed deve essere compresa tra la minima e la massima
|
|
dFeed = Clamp( dFeed, GetMinFeed( PockParams), GetMaxFeed( PockParams)) ;
|
|
|
|
// assegno la Feed ad ogni sottocurva di pCrv
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, dFeed, 0) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
GetFeedForParam( double dPar, const PocketParams& PockParams, double& dFeed)
|
|
{
|
|
/*
|
|
Feed
|
|
^
|
|
|
|
|
GetFeed() + --------------\
|
|
| * \
|
|
| * \
|
|
| * \
|
|
| * \
|
|
| * \
|
|
GetFeed() * GetSideStep() / Diam + * *
|
|
| * *
|
|
0--------------+------+---------------> Length of working angle
|
|
GetSideStep() Diam
|
|
*/
|
|
|
|
if ( dPar > PockParams.dRad * 2 || dPar < 0 ) // dominio...
|
|
return false ;
|
|
|
|
if ( PockParams.dRad * 2 - PockParams.dSideStep < 50 * EPS_SMALL) { // se la funzione è costante...
|
|
dFeed = GetMaxFeed( PockParams) ; // non ho scelta ...
|
|
return true ;
|
|
}
|
|
else {
|
|
if ( PockParams.dSideStep < dPar + 50 * EPS_SMALL) { // se sono nel tratto lineare discendente ...
|
|
// d/2 su parte discendente
|
|
dFeed = GetFeed( PockParams) + ( GetFeed( PockParams) * ( 1 - ( PockParams.dSideStep / ( PockParams.dRad * 2)))) *
|
|
( dPar - PockParams.dSideStep) / ( PockParams.dSideStep - ( PockParams.dRad * 2)) ;
|
|
}
|
|
else
|
|
dFeed = GetMaxFeed( PockParams) ; // se sono nel tratto costante ...
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignFeedZigZagOneWay( ICurveComposite* pCompo, const bool bIsLink, const ICURVEPOVECTOR& vLAbove,
|
|
const ICURVEPOVECTOR& vLUnder, const ICRVCOMPOPOVECTOR& vAddedLinks,
|
|
const PocketParams& PockParams)
|
|
{
|
|
// controllo che la pCompoLine sia effettivamente una linea valida
|
|
if ( pCompo == nullptr || ! pCompo->IsValid())
|
|
return false ;
|
|
|
|
// controllo se il Flag per assegnare la Feed è attivo
|
|
if ( ! PockParams.bCalcFeed)
|
|
return AssignMaxFeed( pCompo, PockParams) ;
|
|
|
|
// inzialmente setto la feed Minima alla curva
|
|
AssignMinFeed( pCompo, PockParams) ;
|
|
|
|
// se è un link tra livelli diversi, allora esco ( con Feed Minima )
|
|
if ( bIsLink) {
|
|
Point3d ptS_link ; pCompo->GetStartPoint( ptS_link) ;
|
|
Point3d ptE_link ; pCompo->GetEndPoint( ptE_link) ;
|
|
if ( abs( ptE_link.y - ptS_link.y) > 500 * EPS_SMALL)
|
|
return true ;
|
|
}
|
|
|
|
// se la curva è piccola, allora esco ( con Feed Minima)
|
|
double dLen = EPS_SMALL ;
|
|
if ( ! pCompo->GetLength( dLen) || dLen < 0.6 * ( 2 * PockParams.dRad))
|
|
return true ;
|
|
|
|
Point3d ptS, ptE ;
|
|
pCompo->GetStartPoint( ptS) ;
|
|
pCompo->GetEndPoint( ptE) ;
|
|
|
|
// creo l'intervallo desiderato valutanto le coordinate x
|
|
Intervals IntMinFeed ; IntMinFeed.Set( ptS.x, ptE.x) ;
|
|
|
|
// creo un vettore contenente i tratti sopra e i tratti sotto attivi
|
|
ICURVEPOVECTOR vAllInt ; vAllInt.reserve( int( vLUnder.size()) + int( vLAbove.size())) ;
|
|
for ( int i = 0 ; i < int( vLUnder.size()) ; ++ i)
|
|
vAllInt.emplace_back( vLUnder[i]->Clone()) ;
|
|
for ( int i = 0 ; i < int( vLAbove.size()) ; ++ i)
|
|
vAllInt.emplace_back( vLAbove[i]->Clone()) ;
|
|
|
|
int nDim = int( vAllInt.size()) ;
|
|
|
|
// aggiungo le parti dei link interessate
|
|
double dCurrY = ptS.y ;
|
|
BBox3d bBoxLink ;
|
|
for ( int l = 0 ; l < int( vAddedLinks.size()) ; ++ l) {
|
|
vAddedLinks[l]->GetLocalBBox( bBoxLink) ;
|
|
if (( dCurrY - bBoxLink.GetMin().y) < PockParams.dSideStep + 50 * EPS_SMALL ||
|
|
( dCurrY - bBoxLink.GetMax().y) < PockParams.dSideStep + 50 * EPS_SMALL) {
|
|
|
|
// determino la direzione del Link
|
|
Vector3d vtLinkDir( - 25, 0, 0) ;
|
|
|
|
// recupero gli estremi
|
|
Point3d ptS_l ; vAddedLinks[l]->GetStartPoint( ptS_l) ;
|
|
Point3d ptE_l ; vAddedLinks[l]->GetEndPoint( ptE_l) ;
|
|
if ( ptE_l.x - ptS_l.x < EPS_SMALL)
|
|
vtLinkDir.x -= ptS_l.x - ptE_l.x ;
|
|
|
|
// superficie a rettangolo definita dal Box
|
|
PtrOwner<ISurfFlatRegion> pSfrRectUp( GetSurfFlatRegionRectangle( bBoxLink.GetDimX() + 50, PockParams.dSideStep)) ;
|
|
pSfrRectUp->Translate(( ptS_l - ORIG) + vtLinkDir) ;
|
|
|
|
// link sopra o sotto alla curva currente ?
|
|
bool bIsDown = true ;
|
|
if ( dCurrY - ptS_l.y < EPS_SMALL)
|
|
bIsDown = false ;
|
|
|
|
// classificazione con la superficie a rettangolo
|
|
CRVCVECTOR ccClass ;
|
|
pSfrRectUp->GetCurveClassification( *vAddedLinks[l], EPS_SMALL, ccClass) ;
|
|
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
|
|
if (( ccClass[i].nClass == CRVC_IN && bIsDown) ||
|
|
( ccClass[i].nClass == CRVC_OUT && ! bIsDown)) {
|
|
PtrOwner<ICurveComposite> pCrvInt( ConvertCurveToComposite( vAddedLinks[l]->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
|
|
if ( ! IsNull( pCrvInt) || pCrvInt->IsValid())
|
|
vAllInt.emplace_back( Release( pCrvInt)) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// scorro tutti i tratti Attivi
|
|
for ( int i = 0 ; i < int( vAllInt.size()) ; ++ i) {
|
|
|
|
// controllo che il tratto lineare sotto sia sufficientemente lungo
|
|
double dLen_iU = EPS_SMALL ;
|
|
if ( ! vAllInt[i]->GetLength( dLen_iU) || ( dLen_iU < 1.1 * ( 2 * PockParams.dRad) && i < nDim))
|
|
continue ;
|
|
|
|
// estremi dell'intervallo
|
|
Point3d ptS_iU, ptE_iU ;
|
|
vAllInt[i]->GetStartPoint( ptS_iU) ;
|
|
vAllInt[i]->GetEndPoint( ptE_iU) ;
|
|
|
|
// la vtDir per Above e Under è sempre invertita rispetto alla linea corrente
|
|
// sottraggo questo intervallo a quello originale
|
|
IntMinFeed.Subtract( ptS_iU.x, ptE_iU.x) ;
|
|
}
|
|
|
|
// l'intervallo orginale ora conterrà i sottointervalli con feed Minima, tolgo quelli troppo piccoli
|
|
Intervals IntMinFeed_noSmall ;
|
|
double dParS = EPS_SMALL ; double dParE = EPS_SMALL ;
|
|
bool bFound = IntMinFeed.GetFirst( dParS, dParE) ;
|
|
while ( bFound) {
|
|
if ( dParE - dParS > PockParams.dRad - 5 * EPS_SMALL)
|
|
IntMinFeed_noSmall.Add( dParS, dParE) ;
|
|
bFound = IntMinFeed.GetNext( dParS, dParE) ;
|
|
}
|
|
|
|
// ora che ho solo tratti abbastanza lunghi, creo l'intervallo complementare
|
|
// questo intervallo contiene tutte le feed Massime
|
|
Intervals IntMaxFeed ; IntMaxFeed.Set( ptS.x, ptE.x) ;
|
|
IntMaxFeed.Subtract( IntMinFeed_noSmall) ;
|
|
|
|
// tolgo le parti piccole
|
|
Intervals IntMaxFeed_noSmall ;
|
|
bFound = IntMaxFeed.GetFirst( dParS, dParE) ;
|
|
while ( bFound) {
|
|
if ( dParE - dParS > PockParams.dRad - 5 * EPS_SMALL)
|
|
IntMaxFeed_noSmall.Add( dParS, dParE) ;
|
|
bFound = IntMaxFeed.GetNext( dParS, dParE) ;
|
|
}
|
|
|
|
// recupero la direzione principale della curva corrente
|
|
Vector3d vtDir = X_AX ;
|
|
if ( ptE.x - ptS.x < EPS_SMALL)
|
|
vtDir = - X_AX ;
|
|
|
|
// trasformo questi intervalli nei parametri corrispondenti sulla linea originale
|
|
bool bFMax = false ;
|
|
bFound = IntMaxFeed_noSmall.GetFirst( dParS, dParE) ;
|
|
if ( bFound && IntMaxFeed_noSmall.IsInside( ptS.x + vtDir.x * 20 * EPS_SMALL))
|
|
bFMax = true ;
|
|
if ( ! bFound && bIsLink) // se non ho intervalli a Feed Massima, allora esco ( con Feed Minima)
|
|
return true ;
|
|
|
|
|
|
while ( bFound) {
|
|
if ( ! bIsLink) { // se segmento di ZigZag
|
|
double du_js = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParS, ptS.y, ptS.z), du_js) ;
|
|
pCompo->AddJoint( du_js) ;
|
|
double du_je = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParE, ptE.y, ptE.z), du_je) ;
|
|
pCompo->AddJoint( du_je) ;
|
|
}
|
|
bFound = IntMaxFeed_noSmall.GetNext( dParS, dParE) ;
|
|
}
|
|
|
|
// aggiorno le proprietà temporanee con la feed
|
|
if ( ! bIsLink) {
|
|
for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) {
|
|
if ( IsEven( u) == bFMax)
|
|
pCompo->SetCurveTempParam( u, GetMaxFeed( PockParams) , 0) ;
|
|
else
|
|
pCompo->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CheckSimpleOverlap( const ICurve* pCrv, const ICurveComposite* pCrvOri, int& nStat, double dToll)
|
|
{
|
|
// controllo dei parametri
|
|
if (( pCrv == nullptr || pCrvOri == nullptr || dToll < EPS_SMALL))
|
|
return false ;
|
|
nStat = 1 ; // 0 -> no overlap | 1 -> overlap
|
|
|
|
// controllo se una sottocurva della composita è abbastanza vicina a tutti i punti trovati
|
|
const double nMAX = 10.0 ;
|
|
for ( int i = 0 ; i <= nMAX ; ++i) {
|
|
double dPar = i / nMAX ;
|
|
Point3d ptC ; pCrv->GetPointD1D2( dPar, ICurve::FROM_PLUS, ptC) ;
|
|
if ( ! pCrvOri->IsPointOn( ptC, dToll)) {
|
|
nStat = 0 ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignFeedForOpenEdge( ICurveComposite* pCrv, const ICurveComposite* pCrvOF_orig,
|
|
const PocketParams& PockParams)
|
|
{
|
|
// se non rischiesto il calcolo della Feed, lascio quella standard
|
|
if ( ! PockParams.bCalcFeed)
|
|
return AssignMaxFeed( pCrv, PockParams) ;
|
|
|
|
// controllo se qualche curva passa sopra ad un lato aperto...
|
|
if ( pCrvOF_orig != nullptr) {
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
const ICurve* pCrv_u = pCrv->GetCurve( u) ;
|
|
if ( pCrv_u == nullptr)
|
|
return false ;
|
|
int nStat = -1 ;
|
|
if ( CheckSimpleOverlap( pCrv_u, pCrvOF_orig, nStat, 1500 * EPS_SMALL) && nStat == 1) {
|
|
double dFeed = GetMinFeed( PockParams) ;
|
|
GetFeedForParam( PockParams.dOpenEdgeRad, PockParams, dFeed) ;
|
|
pCrv->SetCurveTempParam( u, dFeed, 0) ;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
AssignMaxFeed( pCrv, PockParams) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SimplifyCurveByFeeds( ICurveComposite* pCrvCompo, const PocketParams& PockParam, double dToler)
|
|
{
|
|
// controllo la validità della curva
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// NB. Effettuo il Merge dei tratti di curva che presentano la stessa Feed ( mediante la tolleranza)
|
|
|
|
// curva semplificata
|
|
PtrOwner<ICurveComposite> pCrvSimple( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvSimple))
|
|
return false ;
|
|
|
|
/*
|
|
- effettuo il Merge dei tratti di curva che presentano la stessa Feed
|
|
- imposto la Feed dei tratti piccoli come quella dei tratti precedenti ( per evitare singolarità)
|
|
- effettuo un nuovo Merge per uniformare ulteriormente i nuovi tratti ottenuti
|
|
*/
|
|
|
|
double dCurrTempParam ;
|
|
int nParStart = 0 ;
|
|
double dTempParam ;
|
|
double dLen ;
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
pCrvCompo->GetCurveTempParam( i, dTempParam) ;
|
|
if ( i == 0) {
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
else if ( abs( dCurrTempParam - dTempParam) > dToler) { // se parametri diversi
|
|
// recupero il tratto di curva uniforme
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, i))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
// lo semplifico
|
|
// recupero la lunghezza del tratto e imposto associo la Feed corrente
|
|
// se curva corta -> il parametro di Feed è definito da quello precedente
|
|
pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
|
|
pCrv->GetLength( dLen) ;
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvSimple->AddCurve( Release( pCrv)) ;
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
}
|
|
// ultima parte di curva uniforme ...
|
|
PtrOwner<ICurveComposite> pCrvLast( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ;
|
|
if ( ! IsNull( pCrvLast)) {
|
|
pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
|
|
double dLen ; pCrvLast->GetLength( dLen) ;
|
|
for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u)
|
|
pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvSimple->AddCurve( Release( pCrvLast)) ;
|
|
}
|
|
|
|
// pulisco la curva originale
|
|
pCrvCompo->Clear() ;
|
|
|
|
// scorro i nuovi tratti ottenuti e ripeto il ragionamento
|
|
for ( int i = 0 ; i < pCrvSimple->GetCurveCount() ; ++ i) {
|
|
pCrvSimple->GetCurveTempParam( i, dTempParam) ;
|
|
if ( i == 0) {
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
else if ( abs( dCurrTempParam - dTempParam) > dToler) {
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, i))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
|
|
// recupero la lunghezza di tale curve
|
|
double dLen ; pCrv->GetLength( dLen) ;
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvCompo->AddCurve( Release( pCrv)) ; // aggiungo alla curva finale
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
}
|
|
// ultima parte di curva uniforme ...
|
|
pCrvLast.Set( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, pCrvSimple->GetCurveCount()))) ;
|
|
if ( ! IsNull( pCrvLast)) {
|
|
// semplifico
|
|
pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ;
|
|
// recupero la lunghezza di tale curva e imposto la Feed
|
|
pCrvLast->GetLength( dLen) ;
|
|
for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u)
|
|
pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvCompo->AddCurve( Release( pCrvLast)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved, bool bIsLink,
|
|
bool bFirstOffs, const ICurveComposite* pCrv_orig, const PocketParams& PockParams,
|
|
double dToll)
|
|
{
|
|
// controllo la validità della curva
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// controllo se il Flag per assegnare la Feed è attivo
|
|
if ( ! PockParams.bCalcFeed)
|
|
return AssignMaxFeed( pCrv, PockParams) ;
|
|
|
|
// Se link di conformal ZigZag
|
|
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG && bIsLink)
|
|
return AssignMaxFeed( pCrv, PockParams) ;
|
|
|
|
// imposto di Default la Feed minima per ogni sottocurva
|
|
AssignMinFeed( pCrv, PockParams) ;
|
|
|
|
// se non ho una superificie svuotata, allora esco ( con Feed Minima)
|
|
if ( pSrfRemoved == nullptr || ! pSrfRemoved->IsValid() || pSrfRemoved->GetChunkCount() == 0) {
|
|
// controllo eventuali sovrapposizioni con lati aperti
|
|
return AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ;
|
|
}
|
|
|
|
// parametro di Offset per regione svuotata ( restringo la superficie in maniera appropriata)
|
|
double dOffs = 0. ;
|
|
if ( bIsLink)
|
|
dOffs = - PockParams.dRad ;
|
|
else if ( PockParams.dRad < PockParams.dSideStep)
|
|
dOffs = PockParams.dSideStep - PockParams.dRad ;
|
|
|
|
// clono la superificie ( valida)
|
|
PtrOwner<ISurfFlatRegion> pSrfRemovedOffs( pSrfRemoved->CreateOffsetSurf( dOffs, ICurve::OFF_FILLET)) ;
|
|
if ( IsNull( pSrfRemovedOffs) || ! pSrfRemovedOffs->IsValid() || pSrfRemovedOffs->GetChunkCount() == 0)
|
|
return true ; // esco ( sempre con Feed Minima)
|
|
|
|
// classifico le parti interne alla superificie creata solo dagli Offset
|
|
CRVCVECTOR ccClass ;
|
|
if ( ! pSrfRemovedOffs->GetCurveClassification( *pCrv, EPS_SMALL, ccClass))
|
|
return true ; // esco ( sempre con Feed Minima)
|
|
|
|
// creo la nuova curva con le Feed regolate
|
|
PtrOwner<ICurveComposite> pCrv_new( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrv_new))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
|
|
// recupero il tratto di curva ricavato dalla classificazione
|
|
PtrOwner<ICurveComposite> pCrv_sez( ConvertCurveToComposite( pCrv->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
|
|
if ( IsNull( pCrv_sez))
|
|
continue ; // curva troppo piccola, passo alla successiva
|
|
// controllo se interno o fuori alla regione ( regolando quindi la Feed)
|
|
double dCurrFeed = GetMinFeed( PockParams) ;
|
|
if ( ccClass[i].nClass == CRVC_IN) // se interna alla regione rimossa...
|
|
dCurrFeed = GetMaxFeed( PockParams) ;
|
|
// assegno tale Feed ad ogni sottocurva ricavata dal tratto di classificazione
|
|
for ( int u = 0 ; u < pCrv_sez->GetCurveCount() ; ++ u)
|
|
pCrv_sez->SetCurveTempParam( u, dCurrFeed, 0) ;
|
|
// aggiungo la nuova curva con la Feed regolata a quella finale
|
|
pCrv_new->AddCurve( Release( pCrv_sez)) ;
|
|
}
|
|
|
|
// sotituisco con quanto calcolato
|
|
pCrv->Clear() ;
|
|
pCrv->AddCurve( Release( pCrv_new)) ;
|
|
|
|
if ( ! bIsLink) { // ---------------- NEL CASO DI OFFSET ----------------
|
|
// creo un intervallo con tutte le Feed Minime
|
|
Intervals IntMinFeed ;
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
double dParam ; pCrv->GetCurveTempParam( u, dParam, 0) ;
|
|
if ( abs( dParam - GetMinFeed( PockParams)) < 5 * EPS_SMALL)
|
|
IntMinFeed.Add( u, u + 1) ;
|
|
}
|
|
double dParS = EPS_SMALL ; double dParE = EPS_SMALL ;
|
|
// a questo intervallo tolgo tutti i sottotratti di lunghezza inferiore alla tolleranza richiesta
|
|
Intervals IntMinFeed_noSmall ;
|
|
bool bFound = IntMinFeed.GetFirst( dParS, dParE) ;
|
|
while ( bFound) {
|
|
PtrOwner<ICurve> pCrv_Crv( pCrv->CopyParamRange( dParS, dParE)) ;
|
|
double dLen = EPS_SMALL ; pCrv_Crv->GetLength( dLen) ;
|
|
if ( dLen > dToll + 5 * EPS_SMALL)
|
|
IntMinFeed_noSmall.Add( dParS, dParE) ;
|
|
bFound = IntMinFeed.GetNext( dParS, dParE) ;
|
|
}
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
if ( ! IntMinFeed_noSmall.IsInside( u + 0.5))
|
|
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
else { // ---------------- NEL CASO DI LINK ----------------
|
|
// le curve con lunghezza < dToll vanno modificate
|
|
for ( int j = 0 ; j < pCrv->GetCurveCount() ; ++ j) {
|
|
double dLen = EPS_SMALL ;
|
|
pCrv->GetCurve( j)->GetLength( dLen) ;
|
|
if ( dLen < dToll + 5 * EPS_SMALL) {
|
|
// se curva piccola, ricavo il tratto successivo lungo quanto il diametro
|
|
double dLenStart ;
|
|
pCrv->GetLengthAtParam( j, dLenStart) ;
|
|
double dLenEnd = dLenStart + dLen + 2 * PockParams.dRad ;
|
|
double dUE = pCrv->GetCurveCount() ;
|
|
pCrv->GetParamAtLength( dLenEnd, dUE) ;
|
|
bool bFound = false ;
|
|
for ( int k = j + 1 ; k < int( ceil( dUE)) ; ++ k) {
|
|
double dLenH = EPS_SMALL ;
|
|
pCrv->GetCurve( k)->GetLength( dLenH) ;
|
|
if ( dLenH < dToll + 5 * EPS_SMALL)
|
|
continue ;
|
|
// cerco tra le curve successive vicine se ne trovo una con Feed Minima
|
|
double dParam ;
|
|
pCrv->GetCurveTempParam( k, dParam, 0) ;
|
|
if ( abs( dParam - GetMinFeed( PockParams)) < 5 * EPS_SMALL) {
|
|
pCrv->SetCurveTempParam( k, dParam, 0) ;
|
|
bFound = true ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( ! bFound && j != 0) {
|
|
// arrivato qui, so che successivamente non ho curve con Feed Minima vicine
|
|
double dParam ; pCrv->GetCurveTempParam( j - 1, dParam, 0) ;
|
|
if ( abs( dParam - GetMaxFeed( PockParams)) < 5 * EPS_SMALL)
|
|
// se anche la precedente ha Feed Massima -> la curva u-esima è piccola ed Isolata
|
|
pCrv->SetCurveTempParam( j, dParam, 0) ;
|
|
}
|
|
else
|
|
pCrv->SetCurveTempParam( j, GetMinFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bFirstOffs)
|
|
AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ;
|
|
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignFeedSpiralOpt( const int nOptType, const PocketParams& PockParams, ICurveComposite* pCrv )
|
|
{
|
|
// controllo della curva corrente
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// controllo se il Flag per assegnare la Feed è attivo
|
|
if ( ! PockParams.bCalcFeed)
|
|
return AssignMaxFeed( pCrv, PockParams) ;
|
|
|
|
switch ( PockParams.nType) {
|
|
case POCKET_SPIRALIN :
|
|
if ( nOptType == 0) { // Spirale dall'Esterno
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
if ( u == 0) // prima circonferenza
|
|
pCrv->SetCurveTempParam( 0, GetMinFeed( PockParams), 0) ;
|
|
else // semi cerchi in tangenza
|
|
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
else if ( nOptType == 1) { // Trapezoidi
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
}
|
|
break ;
|
|
/* NB. Essendo la funzione CalcSpiral richiamata sia per lo SpiralIN che per lo SpiralOUT le curve sono sempre
|
|
orientate nello stesso modo, solamente alla fine viene invertita la curva finale per la svuotatura... */
|
|
case POCKET_SPIRALOUT :
|
|
if ( nOptType == 0) { // Spiral verso l'esterno
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
if ( u > pCrv->GetCurveCount() - 3) // prime semi circonferenze
|
|
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
else
|
|
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
else if ( nOptType == 1) { // Trapezoidi
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
}
|
|
break ;
|
|
default :
|
|
break ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ResetCurveTempProps( ICurveComposite* pCrvCompo)
|
|
{
|
|
// controllo parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// scorro le curve
|
|
for ( int nU = 0 ; nU < pCrvCompo->GetCurveCount() ; ++ nU) {
|
|
pCrvCompo->SetCurveTempProp( nU, 0, 0) ;
|
|
pCrvCompo->SetCurveTempProp( nU, 0, 1) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
IsCurveCompoHomogeneous( const ICurveComposite* pCrvCompo, bool& bAllClosed, bool& bAllOpen)
|
|
{
|
|
// controllo validità della curva
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
bAllClosed = true ;
|
|
bAllOpen = true ;
|
|
if ( pCrvCompo->GetCurveCount() == 0)
|
|
return true ;
|
|
|
|
// scorro le curve
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() && ( bAllOpen || bAllClosed) ; ++ i) {
|
|
// recupero la sottocurva i-esima
|
|
const ICurve* pCrv = pCrvCompo->GetCurve( i) ;
|
|
if ( pCrv == nullptr || ! pCrv->IsValid())
|
|
return false ;
|
|
int nTmpProp = pCrv->GetTempProp( 0) ;
|
|
bAllOpen = bAllOpen && ( nTmpProp == TEMP_PROP_OPEN_EDGE) ;
|
|
bAllClosed = bAllClosed && ( nTmpProp == TEMP_PROP_CLOSE_EDGE) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetSfrCrvCompoLoops( const ISurfFlatRegion* pSfr, ICRVCOMPOPOVECTOR& vCrvCompo)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
vCrvCompo.clear() ;
|
|
|
|
// scorro i Chunk della superficie
|
|
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
|
|
// scorro i loop del Chunk nC-esimo
|
|
for ( int nL = 0 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) {
|
|
// recupero il Loop come curva composita
|
|
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ;
|
|
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
|
|
return false ;
|
|
// inserisco la curva nel vettore
|
|
vCrvCompo.emplace_back( Release( pCrvLoop)) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
IsChunkAllHomogeneous( const ISurfFlatRegion* pSfr, int nChunk, bool& bAllClose, bool& bAllOpen)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid() || nChunk < 0 || nChunk > pSfr->GetChunkCount())
|
|
return false ;
|
|
bAllClose = true ;
|
|
bAllOpen = true ;
|
|
|
|
// scorro tutti i loop del Chunk
|
|
for ( int nL = 0 ; nL < pSfr->GetLoopCount( nChunk) ; ++ nL) {
|
|
// recupero il loop
|
|
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfr->GetLoop( nChunk, nL))) ;
|
|
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
|
|
return false ;
|
|
// scorro le curve semplici
|
|
int nCurrProp = TEMP_PROP_INVALID ;
|
|
for ( int nU = 0 ; nU < pCrvLoop->GetCurveCount() ; ++ nU) {
|
|
bAllClose = ( bAllClose && pCrvLoop->GetCurveTempProp( nU, nCurrProp, 0) &&
|
|
nCurrProp == TEMP_PROP_CLOSE_EDGE) ;
|
|
bAllOpen = ( bAllOpen && pCrvLoop->GetCurveTempProp( nU, nCurrProp, 0) &&
|
|
nCurrProp == TEMP_PROP_OPEN_EDGE) ;
|
|
if ( ! bAllClose && ! bAllOpen)
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
IsSfrAllHomogeneous( const ISurfFlatRegion* pSfr, bool& bAllClose, bool& bAllOpen)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
bAllClose = true ;
|
|
bAllOpen = true ;
|
|
|
|
// ciclo sui Chunk della regione
|
|
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
|
|
bool bChunkClose = true ;
|
|
bool bChunkOpen = true ;
|
|
if ( ! IsChunkAllHomogeneous( pSfr, nC, bChunkClose, bChunkOpen))
|
|
return false ;
|
|
bAllClose = ( bAllClose && bChunkClose) ;
|
|
bAllOpen = ( bAllOpen && bChunkOpen) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
IsSfrChunkMadeOnlyByClosedIslands( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParams,
|
|
bool& bOK, ICRVCOMPOPOVECTOR& vCrvIsl)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
vCrvIsl.clear() ;
|
|
bOK = false ;
|
|
|
|
// se non esistono isole
|
|
if ( pSfrChunk->GetLoopCount( 0) == 1)
|
|
return true ;
|
|
|
|
// controllo il loop esterno
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
for ( int i = 0 ; i < pCrvExtLoop->GetCurveCount() ; ++ i) {
|
|
int nTmpProp = TEMP_PROP_INVALID ;
|
|
if ( pCrvExtLoop->GetCurveTempProp( i, nTmpProp, 0) && nTmpProp == TEMP_PROP_CLOSE_EDGE)
|
|
return true ;
|
|
}
|
|
|
|
// controllo le Isole
|
|
bool bOkIsl = true ;
|
|
for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) && bOkIsl ; ++ i) {
|
|
// recupero l'isola
|
|
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ;
|
|
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
|
|
return false ;
|
|
// controllo le sue TmpProps
|
|
for ( int i = 0 ; i < pCrvIsl->GetCurveCount() && bOkIsl ; ++ i) {
|
|
int nTmpProp = TEMP_PROP_INVALID ;
|
|
bOkIsl = ( pCrvIsl->GetCurveTempProp( i, nTmpProp, 0) && nTmpProp == TEMP_PROP_CLOSE_EDGE) ;
|
|
}
|
|
if ( bOkIsl)
|
|
vCrvIsl.emplace_back( Release( pCrvIsl)) ;
|
|
}
|
|
|
|
if ( ! bOkIsl)
|
|
vCrvIsl.clear() ;
|
|
else
|
|
bOK = true ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetIndOfHypoteticalChunk( const ICRVCOMPOPOVECTOR& vCrv, const ICurveComposite* pCrvA, const ICurveComposite* pCrvB,
|
|
INTVECTOR& vIndA, INTVECTOR& vIndB)
|
|
{
|
|
/*
|
|
date due curve interne ad una regione piana descritta da vCrv, individuare i chunk in cui
|
|
sono rispettivamente contenute
|
|
*/
|
|
|
|
// controllo dei parametri
|
|
for ( int i = 0 ; i < int( vCrv.size()) ; ++ i) {
|
|
if ( vCrv[i] == nullptr || ! vCrv[i]->IsValid())
|
|
return false ;
|
|
}
|
|
if ( pCrvA == nullptr || ! pCrvA->IsValid() || pCrvB == nullptr || ! pCrvB->IsValid())
|
|
return false ;
|
|
vIndA.clear() ;
|
|
vIndB.clear() ;
|
|
|
|
// ricavo le polyLines dalle curve
|
|
POLYLINEVECTOR vPL ;
|
|
for ( int i = 0 ; i < int( vCrv.size()) ; ++ i) {
|
|
vPL.emplace_back( PolyLine()) ;
|
|
vCrv[i]->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, vPL.back()) ;
|
|
}
|
|
// calcolo la matrice dei Chunks e dei Loops
|
|
Vector3d vtN ;
|
|
INTMATRIX vnPLIndMat ;
|
|
BOOLVECTOR vbInvert ;
|
|
if ( ! CalcRegionPolyLines( vPL, vtN, vnPLIndMat, vbInvert))
|
|
return false ;
|
|
// scorro i Chunks
|
|
for ( int nC = 0 ; nC < int( vnPLIndMat.size()) ; ++ nC) {
|
|
PtrOwner<ISurfFlatRegion> pSfrTmp( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrTmp))
|
|
return false ;
|
|
// scorro i Loops
|
|
for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL) {
|
|
if ( nL == 0) {
|
|
if ( ! pSfrTmp->AddExtLoop( CloneCurveComposite( vCrv[vnPLIndMat[nC][0]])))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! pSfrTmp->AddIntLoop( CloneCurveComposite( vCrv[vnPLIndMat[nC][nL]])))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// piccolo Offset di correzione della superfice
|
|
pSfrTmp->Offset( 500 * EPS_SMALL, ICurve::OFF_FILLET) ;
|
|
// classifico le curve con il Chunk nC-esimo
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrTmp->GetCurveClassification( *pCrvA, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) {
|
|
for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL)
|
|
vIndA.emplace_back( vnPLIndMat[nC][nL]) ;
|
|
}
|
|
ccClass.clear() ;
|
|
if ( pSfrTmp->GetCurveClassification( *pCrvB, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) {
|
|
for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL)
|
|
vIndB.emplace_back( vnPLIndMat[nC][nL]) ;
|
|
}
|
|
// se entrambi indici trovati, esco
|
|
if ( ! vIndA.empty() && ! vIndB.empty())
|
|
return true ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ExtendPath( ICurveComposite* pCompo, const ISurfFlatRegion* pSfr, const PocketParams& PockParams,
|
|
const Vector3d& vtFirstOut, bool bEndOrStart, bool& bOkExtended)
|
|
{
|
|
/*
|
|
Estensione della curva pCompo con un segmento lineare.
|
|
bEndOrStart: definisce se la curva va estesa all'inizio o alla fine
|
|
vtFirstOut: prima direzione su cui tentare l'entrata, se questa non andasse bene
|
|
ruoto progressivamente di 90°
|
|
La curva viene estesa di un tratto lineare in direzione vtRotOut per una lunghezza
|
|
pari a Rad + OffsR + OpenMinSafe.
|
|
*/
|
|
|
|
// controllo dei parametri
|
|
if ( pCompo == nullptr || ! pCompo->IsValid() ||
|
|
pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
bOkExtended = false ;
|
|
|
|
// recupero il punto iniziale/finale della curva con versore tangente
|
|
Point3d pt ;
|
|
Vector3d vtTan ;
|
|
if ( bEndOrStart) {
|
|
if ( ! pCompo->GetEndPoint( pt) || ! pCompo->GetEndDir( vtTan))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! pCompo->GetStartPoint( pt) || ! pCompo->GetStartDir( vtTan))
|
|
return false ;
|
|
vtTan.Invert() ;
|
|
}
|
|
|
|
// ruoto il versore di uscita cercando un'entrata valida
|
|
Vector3d vtRotOut = vtFirstOut ;
|
|
double dMinDist = PockParams.dRad + PockParams.dRadialOffset ;
|
|
for ( int i = 0 ; i < 4 ; ++ i) {
|
|
// evito il 180deg ( se c'è tangenza )
|
|
if ( ! AreOppositeVectorEpsilon( vtTan, vtRotOut, 25. * EPS_SMALL)) {
|
|
// calcolo il punto di caduta dell'utensile
|
|
Point3d ptFall = pt + vtRotOut * ( PockParams.dRad + PockParams.dOpenMinSafe) ;
|
|
// controllo che sia sufficientemente distante dalla superficie limite
|
|
bool bInside = true ;
|
|
bool bOkOut = true ;
|
|
if ( PockParams.SfrLimit.IsValid())
|
|
bOkOut = ( IsPointInsideSurfFr( ptFall, &PockParams.SfrLimit, dMinDist, bInside) && ! bInside) ;
|
|
if ( bOkOut)
|
|
bOkOut = ( IsPointInsideSurfFr( ptFall, pSfr, dMinDist, bInside) && ! bInside) ;
|
|
if ( bOkOut) {
|
|
// aggiungo al ritorno l'uscita
|
|
pCompo->AddLine( ptFall, bEndOrStart) ;
|
|
pCompo->SetCurveTempProp( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0), TEMP_PROP_OUT_START, 0) ;
|
|
pCompo->SetCurveTempParam( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0),
|
|
( bEndOrStart ? GetMaxFeed( PockParams) : GetMinFeed( PockParams)), 0) ;
|
|
bOkExtended = true ;
|
|
return true ;
|
|
}
|
|
}
|
|
vtRotOut.Rotate( Z_AX, - ANG_RIGHT) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustContourStart( ICurveComposite* pCompo, const PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCompo == nullptr || ! pCompo->IsValid())
|
|
return false ;
|
|
|
|
// cerco il tratto lineare chiuso più lungo ...
|
|
int i = 0 ; // <--- indice della curva corrente
|
|
int nMax = - 1 ;
|
|
double dLenMax = 0 ;
|
|
const ICurve* pCrv = pCompo->GetFirstCurve() ;
|
|
while ( pCrv != nullptr) {
|
|
double dLen ;
|
|
if ( pCrv->GetType() == CRV_LINE && pCrv->GetTempProp() == TEMP_PROP_CLOSE_EDGE &&
|
|
pCrv->GetLength( dLen) && dLen > dLenMax) {
|
|
dLenMax = dLen ;
|
|
nMax = i ;
|
|
}
|
|
++ i ;
|
|
pCrv = pCompo->GetNextCurve() ;
|
|
}
|
|
// se non trovato o troppo corto, cerco il tratto chiuso più lungo in generale ( non lineare)
|
|
if ( nMax < 0 || dLenMax < 1.25 * PockParams.dRad) {
|
|
i = 0 ;
|
|
pCrv = pCompo->GetFirstCurve() ;
|
|
while ( pCrv != nullptr) {
|
|
double dLen ;
|
|
if ( pCrv->GetType() != CRV_LINE && pCrv->GetTempProp() == TEMP_PROP_CLOSE_EDGE &&
|
|
pCrv->GetLength( dLen) && dLen > dLenMax) {
|
|
dLenMax = dLen ;
|
|
nMax = i ;
|
|
}
|
|
++ i ;
|
|
pCrv = pCompo->GetNextCurve() ;
|
|
}
|
|
}
|
|
// se non trovato, imposto il punto iniziale a mentà del primo tratto
|
|
if ( nMax == -1) {
|
|
pCompo->ChangeStartPoint( 0.5) ;
|
|
return true ;
|
|
}
|
|
pCompo->ChangeStartPoint( nMax + 0.5) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetHomogeneousParts( ICurveComposite* pCrvCompo, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vpCrvs)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
vpCrvs.clear() ;
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0) // se curva non valida
|
|
return true ;
|
|
|
|
// la curva ha sia tratti aperti che tratti chiusi, quindi sposto l'inizio a metà del tratto più lungo ( se richiesto)...
|
|
AdjustContourStart( pCrvCompo, PockParams) ;
|
|
|
|
// estraggo parti con proprietà uniforme in un vettore ( vpCrvs)
|
|
int nCurrTempProp ;
|
|
int nParStart = 0 ;
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
int nTempProp ;
|
|
pCrvCompo->GetCurveTempProp( i, nTempProp) ;
|
|
if ( i == 0) {
|
|
nCurrTempProp = nTempProp ;
|
|
nParStart = i ;
|
|
}
|
|
else if ( nCurrTempProp != nTempProp) {
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, i))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
pCrv->SetTempProp( nCurrTempProp) ;
|
|
vpCrvs.emplace_back( Release( pCrv)) ;
|
|
nCurrTempProp = nTempProp ;
|
|
nParStart = i ;
|
|
}
|
|
}
|
|
// ultima curva...
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
pCrv->SetTempProp( nCurrTempProp) ;
|
|
vpCrvs.emplace_back( Release( pCrv)) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetFirstOffsCrvFromSfr( const ISurfFlatRegion* pSfr, double dOffs, ICRVCOMPOPOVECTOR& vCrvFirstOffs)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
vCrvFirstOffs.clear() ;
|
|
|
|
// creo la regione mediante Offset
|
|
PtrOwner<ISurfFlatRegion> pSfrOffs( pSfr->CreateOffsetSurf( dOffs, ICurve::OFF_FILLET)) ;
|
|
if ( IsNull( pSfrOffs) || ! pSfrOffs->IsValid())
|
|
return false ;
|
|
|
|
// inserisco le curve orientante
|
|
return ( GetSfrCrvCompoLoops( pSfrOffs, vCrvFirstOffs)) ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
CreateSurfFrIncidence( const ICurveComposite* pCrv, const PocketParams& PockParams,
|
|
const Vector3d& vtTanS_, const Vector3d& vtTanE_, const double dRad,
|
|
ISurfFlatRegion* pSfrInc)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid())
|
|
return false ;
|
|
pSfrInc->Clear() ;
|
|
|
|
// se la curva è chiusa, allora la regione di incidenza è già definita
|
|
if ( pCrv->IsClosed()) {
|
|
PtrOwner<ISurfFlatRegion> pSfrInc_tmp( GetSurfFlatRegionFromFatCurve( pCrv->Clone(), dRad + EPS_SMALL, false, false)) ;
|
|
if ( IsNull( pSfrInc_tmp) || ! pSfrInc_tmp->IsValid())
|
|
return false ;
|
|
pSfrInc->CopyFrom( pSfrInc_tmp) ;
|
|
return ( pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0) ;
|
|
}
|
|
|
|
// creo la curva di Offset esterna ( deve esistere ed essere valida)
|
|
OffsetCurve OffsCrv ;
|
|
PtrOwner<ICurve> pOffsExt( CreateCurveComposite()) ;
|
|
if ( IsNull( pOffsExt) ||
|
|
! OffsCrv.Make( pCrv, dRad, PockParams.nOffsType) ||
|
|
! pOffsExt.Set( OffsCrv.GetLongerCurve()) ||
|
|
IsNull( pOffsExt))
|
|
return false ;
|
|
|
|
// creo la curva di Offset interna ( se esiste e valida...)
|
|
PtrOwner<ICurve> pOffsInt( CreateCurveComposite()) ;
|
|
if ( IsNull( pOffsInt))
|
|
return false ;
|
|
if ( OffsCrv.Make( pCrv, - dRad, PockParams.nOffsType)) {
|
|
PtrOwner<ICurve> pMyCrv( OffsCrv.GetLongerCurve()) ;
|
|
while ( ! IsNull( pMyCrv) && pMyCrv->IsValid()) {
|
|
if ( ! pMyCrv->IsClosed()) {
|
|
pOffsInt.Set( pMyCrv) ;
|
|
break ;
|
|
}
|
|
else
|
|
pMyCrv.Set( OffsCrv.GetLongerCurve()) ;
|
|
}
|
|
}
|
|
|
|
// recupero gli estremi della curva aperta corrente
|
|
Point3d pt1 ; pCrv->GetEndPoint( pt1) ;
|
|
Point3d pt4 ; pCrv->GetStartPoint( pt4) ;
|
|
|
|
// verifico se una delle due curve esiste
|
|
bool bExistExt = ( ! IsNull( pOffsExt) && pOffsExt->IsValid()) ;
|
|
bool bExistInt = ( ! IsNull( pOffsInt) && pOffsInt->IsValid()) ;
|
|
|
|
PtrOwner<ICurveComposite> pCrvExtLoopSurfInc( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvExtLoopSurfInc))
|
|
return false ;
|
|
if ( bExistExt && bExistInt) {
|
|
Point3d pt2 ; pOffsInt->GetEndPoint( pt2) ;
|
|
Point3d pt3 ; pOffsInt->GetStartPoint( pt3) ;
|
|
Point3d pt5 ; pOffsExt->GetStartPoint( pt5) ;
|
|
pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt1) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt2) ;
|
|
pOffsInt->Invert() ;
|
|
pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt4) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt5) ;
|
|
}
|
|
else if ( bExistExt) {
|
|
Point3d pt5 ; pOffsExt->GetStartPoint( pt5) ;
|
|
pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt1) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt4) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt5) ;
|
|
}
|
|
else if ( bExistInt) {
|
|
Point3d pt5 ; pOffsInt->GetStartPoint( pt5) ;
|
|
pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt1) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt4) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt5) ;
|
|
}
|
|
else
|
|
return false ;
|
|
// per sicurezza...
|
|
pCrvExtLoopSurfInc->Close() ;
|
|
|
|
// creo la regione ( la curva potrebbe autointersecarsi)
|
|
SurfFlatRegionByContours SfrByC ;
|
|
if ( ! SfrByC.AddCurve( Release( pCrvExtLoopSurfInc)))
|
|
return false ;
|
|
|
|
PtrOwner<ISurfFlatRegion> pSfr( SfrByC.GetSurf()) ;
|
|
if ( pSfr->IsValid() && pSfr->GetChunkCount() > 0) {
|
|
if ( AreOppositeVectorApprox( pSfr->GetNormVersor(), Z_AX))
|
|
pSfr->Invert() ;
|
|
pSfrInc->CopyFrom( pSfr) ;
|
|
return true ;
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustOpenEdge( const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vCrvIsland,
|
|
const double dParS, const double dParE, const Vector3d& vtTanS,
|
|
const Vector3d& vtTanE, const double dRad, const double dDiamJ,
|
|
const PocketParams& PockParams, ICurveComposite* pCrvBorder)
|
|
{
|
|
/* parametri :
|
|
pCrvCompo -> curva originaria di bordo
|
|
vCrvIsland -> vettore delle isole all'interno di pCrvCompo
|
|
pCrvOpenOffs -> tratto aperto corrente già Offsettato verso l'esterno
|
|
dParS -> parametro sulla pCrvCompo per l'inizio del tratto aperto
|
|
dParE -> parametro sulla pCrvCompo per la fine del tratto aperto
|
|
vtTanS -> vettore di tangenza finale tratto chiuso precedente
|
|
vtTanE -> vettore di tangenza iniziale ( invertito ) del tratto chiuso successivo
|
|
dRad -> raggio di Offset per la regione di incidenza
|
|
dDiamJ -> ampiezza delle curve a fagiolo per estendere la regione di incidenza
|
|
pCrvRes -> curva da restituire ( inizialmente è il tratto aperto sulla pCrvCompo ;
|
|
questa curva sarà l'estensione del lato aperto, adattandosi alla geometria
|
|
dei chiusi ( non posso vedere solo i chiusi adiacenti all'aperto, devo considerare TUTTI i chiusi
|
|
della pCrvCompo
|
|
pStmVol -> Volume di svuotatura
|
|
pStm_Part -> Part corrente
|
|
*/
|
|
|
|
// controllo la validità dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 ||
|
|
pCrvBorder == nullptr || ! pCrvBorder->IsValid() || pCrvBorder->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// definisco la regione di incidenza
|
|
PtrOwner<ISurfFlatRegion> pSfrInc( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrInc))
|
|
return false ;
|
|
if ( ! CreateSurfFrIncidence( pCrvBorder, PockParams, vtTanS, vtTanE, dRad + 75 * EPS_SMALL, pSfrInc)) {
|
|
pSfrInc.Set( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( pCrvBorder), dRad + 75 * EPS_SMALL, false, false)) ;
|
|
if ( IsNull( pSfrInc) || ! pSfrInc->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
// creo un vettore con tutte le curve che potrebbero cadere, in parte, nella regione di incidenza
|
|
ICRVCOMPOPOVECTOR vCrvToCheck ;
|
|
for ( int i = 0 ; i < int( vCrvIsland.size()) ; ++ i)
|
|
vCrvToCheck.emplace_back( vCrvIsland[i]->Clone()) ; // aggiungo le isole
|
|
// se la curva originale non è tutta Aperta -> devo aggiungere anche essa nelle curve da controllare
|
|
bool bIsAllOpen = abs( abs( dParE - dParS) - pCrvCompo->GetCurveCount()) < EPS_SMALL ;
|
|
if ( ! bIsAllOpen) {
|
|
// recupero il tratto di curva prima e dopo dell'aperto corrente
|
|
PtrOwner<ICurveComposite> pCompoOther( ConvertCurveToComposite( pCrvCompo->CopyParamRange( dParE, dParS))) ;
|
|
if ( IsNull( pCompoOther) || ! pCompoOther->IsValid())
|
|
return false ;
|
|
vCrvToCheck.emplace_back( Release( pCompoOther)) ; // aggiungo il bordo
|
|
}
|
|
|
|
// controllo se la curva è un'isola ( normale del piano -Z_AX)
|
|
bool bIsIsland = false ;
|
|
double dArea ; Plane3d plCheck ;
|
|
if ( ! pCrvCompo->GetArea( plCheck, dArea))
|
|
return false ;
|
|
bIsIsland = AreSameVectorEpsilon( plCheck.GetVersN(), - Z_AX, 10 * EPS_SMALL) ;
|
|
|
|
// scorro il vettore creato...
|
|
for ( int c = 0 ; c < int( vCrvToCheck.size()) ; ++ c) {
|
|
// 1) recupero la curva corrente
|
|
PtrOwner<ICurveComposite> pCrvCurr( vCrvToCheck[c]->Clone()) ;
|
|
if ( IsNull( pCrvCurr) || ! pCrvCurr->IsValid())
|
|
return false ;
|
|
// 2) ricavo i tratti con proprietà uniformi ( Aperti/Chiusi )
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
if ( ! GetHomogeneousParts( pCrvCurr, PockParams, vpCrvs))
|
|
return false ;
|
|
// 3) considero solo i tratti chiusi
|
|
for ( int cl = 0 ; cl < int( vpCrvs.size()) ; ++ cl) {
|
|
if ( vpCrvs[cl]->GetTempProp() == TEMP_PROP_OPEN_EDGE)
|
|
continue ;
|
|
// 4) effettuo l'Offset della curva di metà dDiamJ
|
|
OffsetCurve OffsCrv ;
|
|
if ( ! OffsCrv.Make( vpCrvs[cl], - dDiamJ * 0.5 - 20 * EPS_SMALL, PockParams.nOffsType))
|
|
return false ;
|
|
// 5) scorro tutte le curve di Offset che si sono formate, prendendo sempre la più lunga tra le rimanenti
|
|
PtrOwner<ICurve> pOffLongestCrv( OffsCrv.GetLongerCurve()) ;
|
|
while ( ! IsNull( pOffLongestCrv)) {
|
|
// 6) creo la regione di incidenza di tale curva ( "Curva a fagiolo")
|
|
bool bSquareEnds = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ;
|
|
bool bSquareMids = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ;
|
|
PtrOwner<ISurfFlatRegion> pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ * 0.5, bSquareEnds, bSquareMids)) ;
|
|
if ( IsNull( pSfrBean) || ! pSfrBean->IsValid())
|
|
return false ;
|
|
// inverto se necessario
|
|
if ( AreOppositeVectorApprox( pSfrBean->GetNormVersor(), pSfrInc->GetNormVersor()))
|
|
pSfrBean->Invert() ;
|
|
// 7) se la "Regione a fagiolo" non influenza la regione di incidenza, la transcuro
|
|
bool bDiscard = false ;
|
|
if ( ! bIsIsland) { // se tratto un loop esterno
|
|
PtrOwner<ISurfFlatRegion> pSfrBean_test( CloneSurfFlatRegion( pSfrBean)) ;
|
|
if ( IsNull( pSfrBean_test) || ! pSfrBean_test->IsValid())
|
|
return false ;
|
|
pSfrBean_test->Intersect( *pSfrInc) ;
|
|
bDiscard = ( IsNull( pSfrBean_test) ||
|
|
! pSfrBean_test->IsValid() ||
|
|
pSfrBean_test->GetChunkCount() == 0) ;
|
|
}
|
|
else if ( bIsAllOpen && bIsIsland) { // se isola aperta
|
|
for ( int cI = 0 ; cI < pSfrInc->GetChunkCount() && ! bDiscard ; ++ cI) {
|
|
for ( int cB = 0 ; cB < pSfrBean->GetChunkCount() && ! bDiscard ; ++ cB) {
|
|
if ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrBean, cB) == REGC_IN1) {
|
|
for ( int l = 1 ; l < pSfrBean->GetLoopCount( cB) ; ++ l) {
|
|
PtrOwner<ISurfFlatRegion> pSfrTmp( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrTmp) || ! pSfrTmp->AddExtLoop( pSfrBean->GetLoop( cB, l)) ||
|
|
! pSfrTmp->Invert())
|
|
return false ;
|
|
bDiscard = ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrTmp, 0) == REGC_IN1) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( ! bDiscard) {
|
|
// 8) aggiorno la regione di incidenza
|
|
if ( ! pSfrInc->Add( *pSfrBean))
|
|
return false ;
|
|
}
|
|
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ; // passo al tratto offsettato successivo
|
|
}
|
|
}
|
|
}
|
|
|
|
// ATTENZIONE !
|
|
// L'algoritmo di allargamento presso i lati aperti è Euristico; io mi estendo a seconda della geometria del
|
|
// lato aperto al di fuori del volume di svuotatura... Devo controllare di non rovinare delle zone al di fuori di
|
|
// esso !
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
// recupero la superficie limite
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( PockParams.SfrLimit.Clone()) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
|
|
return false ;
|
|
// piccolo Offset per sicurezza
|
|
pSfrLimit->Offset( 50 * EPS_SMALL, ICurve::OFF_FILLET) ;
|
|
pSfrInc->Subtract( *pSfrLimit) ; // rimuovo la regione limite
|
|
|
|
// può capitare che la regione Limite mi crei più Chunk sulla pSfrInc
|
|
// I chunk da togliere sono tutti quelli che si sono separati dalla pCrvBorder
|
|
// NB. Il mio obiettivo è quello di avere un'unica curva con estremi i due tratti chiusi estremanti
|
|
|
|
// se curva originale non tutta aperta e multi-chunk
|
|
if ( ! bIsAllOpen && pSfrInc->GetChunkCount() > 1) { // se ottengo più chunks
|
|
PtrOwner<ISurfFlatRegion> pNewSfrInc( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pNewSfrInc))
|
|
return false ;
|
|
// ricavo i punti iniziali e finali della curva di bordo
|
|
Point3d ptS, ptE ;
|
|
if ( ! pCrvBorder->GetStartPoint( ptS) || ! pCrvBorder->GetEndPoint( ptE))
|
|
return false ;
|
|
// per ogni Chunk ( > 1)
|
|
bool bFound = false ;
|
|
for ( int i = 0 ; i < pSfrInc->GetChunkCount() ; ++ i) {
|
|
// bordo esterno
|
|
PtrOwner<ICurveComposite> pCrvEL( ConvertCurveToComposite( pSfrInc->GetLoop( i, 0))) ;
|
|
if ( IsNull( pCrvEL) || ! pCrvEL->IsValid())
|
|
return false ;
|
|
// se estremi in comune, la curva cercata è la seguente
|
|
if ( pCrvEL->IsPointOn( ptS, 300 * EPS_SMALL) && pCrvEL->IsPointOn( ptE, 300 * EPS_SMALL)) {
|
|
bFound = true ;
|
|
// se la nuova regione di incidenza è vuota, aggiorno
|
|
if ( ! pNewSfrInc->IsValid())
|
|
pNewSfrInc.Set( pSfrInc->CloneChunk( i)) ;
|
|
// se la nuova regione di incidenza non è vuota, aggiungo
|
|
else {
|
|
PtrOwner<ISurfFlatRegion> pSfrToAdd( pSfrInc->CloneChunk( i)) ;
|
|
if ( IsNull( pSfrToAdd) || ! pNewSfrInc->Add( *pSfrToAdd))
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
// se ho trovato qualcosa, allora aggiorno la superficie di incidenza
|
|
if ( bFound && pNewSfrInc->IsValid())
|
|
pSfrInc.Set( pNewSfrInc) ;
|
|
}
|
|
// se la regione è formata da più chunks, prendo quella con area di loop esterno maggiore
|
|
if ( ! IsNull( pSfrInc) && pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 1) {
|
|
double dMaxArea = - INFINITO ;
|
|
int nChunk = 0 ;
|
|
for ( int nC = 0 ; nC < pSfrInc->GetChunkCount() ; ++ nC) {
|
|
PtrOwner<ICurve> pExtLoop( pSfrInc->GetLoop( nC, 0)) ;
|
|
if ( IsNull( pExtLoop) || ! pExtLoop->IsValid())
|
|
return false ;
|
|
double dArea = EPS_SMALL ;
|
|
pExtLoop->GetAreaXY( dArea) ;
|
|
if ( dArea > dMaxArea) {
|
|
dMaxArea = dArea ;
|
|
nChunk = nC ;
|
|
}
|
|
}
|
|
pSfrInc.Set( pSfrInc->CloneChunk( nChunk)) ;
|
|
}
|
|
// se regione di incidenza nulla o non valida, errore
|
|
if ( IsNull( pSfrInc) || ! pSfrInc->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
// dalla regione di incidenza devo estrarre il tratto di bordo che ha per estremi i lati chiusi
|
|
// ( stando al di fuori, quindi estendendomi all'esterno, della regione da svuotare )
|
|
PtrOwner<ICurveComposite> pCrvNewBorder( ConvertCurveToComposite( pSfrInc->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid())
|
|
return false ;
|
|
// imposto la curva come tutta aperta
|
|
for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u)
|
|
pCrvNewBorder->SetCurveTempProp( u, TEMP_PROP_OPEN_EDGE, 0) ;
|
|
|
|
// se la curva originale era tutta aperta... ( il nuovo lato è il loop esterno della regione di incidenza)
|
|
if ( bIsAllOpen) {
|
|
// pulisco la curva originale
|
|
pCrvBorder->Clear() ;
|
|
if ( bIsIsland) { // se isola inserisco il loop interno corretto della regione
|
|
for ( int l = 1 ; l < pSfrInc->GetLoopCount( 0) ; ++ l) {
|
|
// recupero il Loop
|
|
PtrOwner<ICurveComposite> pCrvIntLoop( ConvertCurveToComposite( pSfrInc->GetLoop( 0, l))) ;
|
|
if ( IsNull( pCrvIntLoop) || ! pCrvIntLoop->IsValid())
|
|
return false ;
|
|
// controllo che sia interno alla curva di bordo corrente ( l'isola aperta)
|
|
IntersCurveCurve ICC( *pCrvIntLoop, *pCrvCompo) ;
|
|
CRVCVECTOR ccClass ;
|
|
if ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 &&
|
|
ccClass[0].nClass == CRVC_OUT) {
|
|
// il loop interno corretto va impostato come unico e nuovo loop interno della regione
|
|
pCrvNewBorder->CopyFrom( pCrvIntLoop) ;
|
|
if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid())
|
|
return false ;
|
|
// imposto la curva come tutta aperta ( gira come girava già l'isola)
|
|
for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u)
|
|
pCrvNewBorder->SetCurveTempProp( u, TEMP_PROP_OPEN_EDGE, 0) ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
// se bordo esterno inserisco il bordo esterno
|
|
return ( pCrvBorder->AddCurve( Release( pCrvNewBorder))) ;
|
|
}
|
|
|
|
// altrimenti la spezzo il loop della regione di incidenza nei punti iniziali e finali della curva aperta originale
|
|
Point3d ptStart ; pCrvBorder->GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; pCrvBorder->GetEndPoint( ptEnd) ;
|
|
double dUTrimS = -1 ; double dUTrimE = -1 ;
|
|
pCrvNewBorder->GetParamAtPoint( ptStart, dUTrimS, 10000 * EPS_SMALL) ;
|
|
pCrvNewBorder->GetParamAtPoint( ptEnd, dUTrimE, 10000 * EPS_SMALL) ;
|
|
|
|
// pulisco la curva originale
|
|
pCrvBorder->Clear() ;
|
|
if ( ! pCrvBorder->AddCurve( pCrvNewBorder->CopyParamRange( dUTrimS, dUTrimE)) ||
|
|
! pCrvBorder->IsValid()) {
|
|
// se la curva non originaria di lato aperto non era chiusa...
|
|
if ( ! pCrvBorder->IsClosed() && pCrvNewBorder->IsClosed()) {
|
|
// cerco il punto più vicino al ptStart del chiuso sulla nuova curva di bordo aperta
|
|
Point3d ptMinDist ;
|
|
int nFlag ;
|
|
if ( ! DistPointCurve( ptStart, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag))
|
|
return false ;
|
|
// cambio il punto iniziale della border in ptMinDist
|
|
double dUTmp ;
|
|
if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp) ||
|
|
! pCrvNewBorder->ChangeStartPoint( dUTmp))
|
|
return false ;
|
|
// cerco il punto più vicino al ptEnd del chiuso sulla curva di bordo aperta
|
|
if ( ! DistPointCurve( ptEnd, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag))
|
|
return false ;
|
|
// ricavo il parametro sulla curva
|
|
if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp))
|
|
return false ;
|
|
if ( dUTmp < EPS_SMALL)
|
|
dUTmp = pCrvNewBorder->GetCurveCount() - 5 * EPS_SMALL ;
|
|
// copio la curva tra questi due parametri
|
|
PtrOwner<ICurveComposite> pCrvTmp( ConvertCurveToComposite( pCrvNewBorder->CopyParamRange( 0., dUTmp))) ;
|
|
if ( IsNull( pCrvTmp) || ! pCrvTmp->IsValid())
|
|
return false ;
|
|
// raccordo con il chiuso mediante due tratto lineare
|
|
pCrvTmp->AddLine( ptStart, false) ;
|
|
pCrvTmp->AddLine( ptEnd, true) ;
|
|
// restituisco
|
|
pCrvBorder->CopyFrom( pCrvTmp) ;
|
|
return true ;
|
|
}
|
|
pCrvBorder->CopyFrom( pCrvCompo) ;
|
|
}
|
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustContourWithOpenEdges( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vCrvIsl, PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// recupero i parametri di lavorazione correnti
|
|
double dDiam = PockParams.dRad * 2 ;
|
|
double dOffR = PockParams.dRadialOffset ;
|
|
double dStep = PockParams.dSideStep ;
|
|
|
|
// raggio di riferimento per offset
|
|
double dOutEdge = 0.5 * dDiam ;
|
|
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_ZIGZAG)
|
|
dOutEdge = max( dOutEdge, dDiam - dStep) ;
|
|
double dRad = dOutEdge + dOffR ;
|
|
if ( abs( PockParams.dMaxOpenEdgeRad) > 0 && PockParams.dMaxOpenEdgeRad < dRad)
|
|
dRad = PockParams.dMaxOpenEdgeRad ;
|
|
// se lavorazione ZigZag/OneWay con richiesta di curve di Bordo, gli aperti vanno leggermente allargati
|
|
if ( PockParams.bAllowZigZagOneWayBorders &&
|
|
( PockParams.nType == POCKET_ZIGZAG || PockParams.nType == POCKET_ONEWAY))
|
|
dRad += PockParams.dOffsExtra ;
|
|
// se lavorazione a ZigZag con smusso, esco di Step/2 per pulire gli aperti
|
|
if ( PockParams.nType == POCKET_ZIGZAG && PockParams.bSmooth)
|
|
dRad += max( PockParams.dSideStep / 2, PockParams.dRad / 8) ;
|
|
|
|
// salvo il raggio trovato
|
|
PockParams.dOpenEdgeRad = dRad ;
|
|
|
|
// ricavo i tratti omogenei
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
if ( ! GetHomogeneousParts( pCrvCompo, PockParams, vpCrvs))
|
|
return false ;
|
|
// NB. Il primo tratto è la prima metà del tratto aperto più lungo ( se esiste )
|
|
|
|
// Offset esterno per CurveJ
|
|
double dDiamJ = 1.05 * ( dDiam) + 2 * dOffR ;
|
|
if ( abs( PockParams.dMaxOpenEdgeRad) > 0)
|
|
dDiamJ = 100 * EPS_SMALL ;
|
|
// NB. 1.05 serve per eccedere leggermente, in modo che il contro-offset della prima curva di svuotatura presenti
|
|
// dei piccoli archi tra i chiusi e gli aperti ( in modo da svuotare bene lungo il chiuso)
|
|
|
|
// curva finale da restituire
|
|
PtrOwner<ICurveComposite> pCrvCompo_final( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvCompo_final))
|
|
return false ;
|
|
|
|
// parametro iniziale del tratto corrente, sulla curva originale
|
|
double dParS = 0. ;
|
|
// scorro tutti i tratti omogenei nel vettore
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
// recupero la proprietà della curva composita
|
|
int nCurrTmpProp = vpCrvs[i]->GetTempProp() ;
|
|
// aggiorno i parametri del lato aperto corrente sulla curva originale
|
|
double dParE = dParS + 1. * vpCrvs[i]->GetCurveCount() ;
|
|
// se aperta
|
|
if ( nCurrTmpProp == TEMP_PROP_OPEN_EDGE) {
|
|
// ricavo la tangenze dei lati chiusi agli estremi di questa curva
|
|
Vector3d vtTanS = V_INVALID ;
|
|
Vector3d vtTanE = V_INVALID ;
|
|
if ( i != 0) {
|
|
if ( ! vpCrvs[i-1]->GetEndDir( vtTanS) || // tangente finale del chiuso precedente
|
|
! vpCrvs[( i + 1) % int( vpCrvs.size())]->GetStartDir( vtTanE)) // tangente iniziale del chiuso successivo...
|
|
return false ;
|
|
vtTanE.Invert() ; // invertita
|
|
}
|
|
if ( ! AdjustOpenEdge( pCrvCompo, vCrvIsl, dParS, dParE, vtTanS, vtTanE, dRad, dDiamJ, PockParams, vpCrvs[i]))
|
|
return false ;
|
|
}
|
|
// assegno le proprietà di lato Aperto/Chiuso per la curva corrente
|
|
for ( int u = 0 ; u < vpCrvs[i]->GetCurveCount() ; ++ u)
|
|
vpCrvs[i]->SetCurveTempProp( u, nCurrTmpProp, 0) ;
|
|
// aggiungo la curva ricavata ( se chiusa -> la copio, se aperta -> copio l'estesa)
|
|
if ( ! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone())) {
|
|
// per sicurezza, se gli estremi non coincidono, creo un piccolo raccordo lineare
|
|
Point3d ptH ; vpCrvs[i]->GetStartPoint( ptH) ;
|
|
if ( ! pCrvCompo_final->AddLine( ptH) ||
|
|
! pCrvCompo_final->SetCurveTempProp( pCrvCompo_final->GetCurveCount() - 1, 1, 0) ||
|
|
! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone()))
|
|
return false ;
|
|
}
|
|
// aggiorno
|
|
dParS = dParE ;
|
|
}
|
|
// non dovrebbe esserci un gap, ma meglio prevenire problemi
|
|
pCrvCompo_final->Close() ;
|
|
// sostituisco
|
|
pCrvCompo->Clear() ;
|
|
pCrvCompo->CopyFrom( pCrvCompo_final) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ChainCompoCurves( ICRVCOMPOPOVECTOR& vCrvCompo)
|
|
{
|
|
/* concatenamento delle curve composite */
|
|
if ( int( vCrvCompo.size() < 2))
|
|
return true ;
|
|
|
|
// controllo validità delle curve
|
|
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i)
|
|
if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid())
|
|
return false ;
|
|
|
|
// preparo i dati per il concatenamento
|
|
bool bFirst = true ;
|
|
Point3d ptNear = ORIG ;
|
|
double dToler = 500 * EPS_SMALL ; // leggermente maggiore della tolleranza sulla superficie limite
|
|
ChainCurves chainC ;
|
|
chainC.Init( false, dToler, int( vCrvCompo.size())) ;
|
|
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
|
|
// recupero i dati della curva necessari al concatenamento e li assegno
|
|
Point3d ptStart, ptEnd ;
|
|
Vector3d vtStart, vtEnd ;
|
|
if ( ! vCrvCompo[i]->GetStartPoint( ptStart) || ! vCrvCompo[i]->GetStartDir( vtStart) ||
|
|
! vCrvCompo[i]->GetEndPoint( ptEnd) || ! vCrvCompo[i]->GetEndDir( vtEnd))
|
|
return false ;
|
|
if ( ! chainC.AddCurve( int( i + 1), ptStart, vtStart, ptEnd, vtEnd))
|
|
return false ;
|
|
// se prima curva, assegno inizio della ricerca
|
|
if ( bFirst) {
|
|
ptNear = ptStart + 10 * EPS_SMALL * vtStart ;
|
|
bFirst = false ;
|
|
}
|
|
}
|
|
|
|
// vettore delle curve composite risultante
|
|
ICRVCOMPOPOVECTOR vCrvCompoChained ;
|
|
|
|
// recupero i percorsi concatenati
|
|
INTVECTOR vnInd ;
|
|
while ( chainC.GetChainFromNear( ptNear, true, vnInd)) {
|
|
// creo una curva composita
|
|
PtrOwner<ICurveComposite> pCrvCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvCompo))
|
|
return false ;
|
|
// recupero le curve semplici e le inserisco nella curva composita
|
|
for ( size_t i = 0 ; i < vnInd.size() ; ++ i) {
|
|
int nId = abs( vnInd[i]) - 1 ;
|
|
bool bInvert = ( vnInd[i] < 0) ;
|
|
// se necessario, la inverto
|
|
if ( bInvert)
|
|
vCrvCompo[nId]->Invert() ;
|
|
// la aggiungo alla curva composta
|
|
if ( ! pCrvCompo->AddCurve( ::Release( vCrvCompo[nId]), true, dToler))
|
|
return false ;
|
|
}
|
|
// aggiorno il nuovo punto vicino
|
|
if ( pCrvCompo->GetCurveCount() > 0) {
|
|
pCrvCompo->GetEndPoint( ptNear) ;
|
|
vCrvCompoChained.emplace_back( Release( pCrvCompo)) ;
|
|
}
|
|
}
|
|
|
|
swap( vCrvCompoChained, vCrvCompo) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ChangePtStartForSinglePocketCurve( ICurveComposite* pCrvCompo, const PocketParams& PockParams,
|
|
const Point3d& ptRef, bool bInvertOpenCrv)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// se la curva è aperta
|
|
if ( ! pCrvCompo->IsClosed() && bInvertOpenCrv) {
|
|
// se non ho un punto di riferimento, allora non faccio nulla
|
|
if ( ! ptRef.IsValid())
|
|
return true ;
|
|
// se ho un punto di riferimento controllo quale estremo è più vicino
|
|
Point3d ptStart ; pCrvCompo->GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; pCrvCompo->GetEndPoint( ptEnd) ;
|
|
if ( SqDist( ptRef, ptEnd) < SqDist( ptRef, ptStart))
|
|
pCrvCompo->Invert() ;
|
|
}
|
|
// se la curva è chiusa
|
|
else {
|
|
// se non ho un punto di riferimento, scelgo il tratto più lungo
|
|
if ( ! ptRef.IsValid()) {
|
|
ResetCurveTempProps( pCrvCompo) ;
|
|
AdjustContourStart( pCrvCompo, PockParams) ;
|
|
}
|
|
// se ho un punto di riferimento
|
|
else {
|
|
// cerco il punto più vicino
|
|
DistPointCurve DistPtCrv( ptRef, *pCrvCompo) ;
|
|
double dPar = 0. ;
|
|
int nFlag ;
|
|
if ( ! DistPtCrv.GetParamAtMinDistPoint( 0, dPar, nFlag))
|
|
return false ;
|
|
pCrvCompo->ChangeStartPoint( dPar) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdvanceExtendCurves( ICRVCOMPOPOVECTOR& vCrvCompo, const ISurfFlatRegion* pSfr,
|
|
const PocketParams& PockParams, bool bInvertOpenCrv)
|
|
{
|
|
|
|
// se non ho curve, allora non faccio nulla
|
|
if ( vCrvCompo.empty())
|
|
return true ;
|
|
|
|
// per ogni curva composita...
|
|
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
|
|
// controllo validità della curva
|
|
if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid())
|
|
return false ;
|
|
// cambio il punto iniziale della curva
|
|
if ( ! ChangePtStartForSinglePocketCurve( vCrvCompo[i], PockParams, PockParams.ptStart, bInvertOpenCrv))
|
|
return false ;
|
|
}
|
|
|
|
// se superficie tutta chiusa, allora ho finito
|
|
if ( PockParams.bAllClosed)
|
|
return true ;
|
|
|
|
/*
|
|
L'estensione della curva permettendo un'entrata da fuori è valida se il
|
|
nuovo punto di Inizio ( ptFall) :
|
|
- E' al di fuori della superficie Limite ( di almeno Rad + OffsR)
|
|
- E' al di fuori della superifice di svuotatura ( di almeno dRad + OffsR)
|
|
*/
|
|
|
|
// scorro ogni singola curva composita
|
|
const int MAX_ITER = 4 ;
|
|
for ( int i = 0 ; i < int( vCrvCompo.size()) ; ++ i) {
|
|
// controllo se la curva è chiusa o aperta
|
|
bool bIsClosed = vCrvCompo[i]->IsClosed() ;
|
|
// calcolo il numero massimo di punti in cui testare l'entrata da fuori
|
|
int nMaxIter = ( bIsClosed ? MAX_ITER : 1) ;
|
|
double dLen = EPS_SMALL ;
|
|
if ( nMaxIter == MAX_ITER)
|
|
vCrvCompo[i]->GetLength( dLen) ;
|
|
// scorro il numero di punti su cui tentare l'entrata da fuori
|
|
for ( int j = 0 ; j < nMaxIter ; ++ j) {
|
|
if ( j > 0) {
|
|
double dParAtLen = EPS_SMALL ;
|
|
vCrvCompo[i]->GetParamAtLength( dLen / nMaxIter, dParAtLen) ;
|
|
vCrvCompo[i]->ChangeStartPoint( dParAtLen) ;
|
|
}
|
|
// ricavo vettore tangente iniziale e provo ad estendere
|
|
Vector3d vtStart ; vCrvCompo[i]->GetStartDir( vtStart) ;
|
|
vtStart.Invert() ;
|
|
bool bIsStartExtended = false ;
|
|
if ( ! ExtendPath( vCrvCompo[i], pSfr, PockParams, vtStart, false, bIsStartExtended))
|
|
return false ;
|
|
// se aperta, controllo la fine
|
|
if ( ! bIsClosed) {
|
|
// ricavo vettore tangente finale e provo ad estendere
|
|
Vector3d vtEnd ; vCrvCompo[i]->GetEndDir( vtEnd) ;
|
|
bool bIsEndExtended = false ;
|
|
if ( ! ExtendPath( vCrvCompo[i], pSfr, PockParams, vtEnd, true, bIsEndExtended))
|
|
return false ;
|
|
if ( bIsEndExtended)
|
|
break ;
|
|
}
|
|
if ( bIsStartExtended)
|
|
break ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPocketCurvesByClosedEdges( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvCompoRes, bool& bAllRemoved)
|
|
{
|
|
// controllo parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
bAllRemoved = false ;
|
|
|
|
// controllo se la superficie si annulla con un controOffset del raggio utensile
|
|
double dMaxOffs ;
|
|
pSfrChunk->GetMaxOffset( dMaxOffs) ;
|
|
if ( dMaxOffs > PockParams.dRad + 200 * EPS_SMALL)
|
|
return true ;
|
|
|
|
/*
|
|
NB. Si poteva calcolare la Fat curve del raggio utensile dell'Offset di tutti i chiusi e vedere
|
|
se l'unione di queste regioni copriva interamente il Chunk attuale... così facendo rischio di
|
|
trascurare delle parti che spariscono facendo il primo Offset dei chiusi; il controllo va fatto
|
|
quindi con un contro-offset della regione complessiva
|
|
*/
|
|
|
|
// controllo se il Chunk ha delle isole
|
|
bool bHasIslands = ( pSfrChunk->GetLoopCount( 0) > 1) ;
|
|
|
|
// recupero il Loop esterno
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
// recupero i tratti con proprietà uniformi
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
GetHomogeneousParts( pCrvExtLoop, PockParams, vpCrvs) ;
|
|
if ( vpCrvs.size() > 1) { // unisco il primo e l'ultimo se estremi compatibili
|
|
// NB. GetHomogeneousParts() cambia il punto di inizio nel lato chiuso più lungo ( se presente)
|
|
Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ;
|
|
Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ;
|
|
if ( AreSamePointApprox( ptS, ptE)) {
|
|
vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ;
|
|
vpCrvs.erase( vpCrvs.end() - 1) ;
|
|
}
|
|
}
|
|
// controllo se il loop Esterno è uniforme ( quindi tutto chiuso o tutto aperto)
|
|
bool bExtAllClose = false ;
|
|
bool bExtAllOpen = false ;
|
|
if ( int( vpCrvs.size()) == 1) {
|
|
if ( vpCrvs[0]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
|
|
bExtAllClose = true ;
|
|
else
|
|
bExtAllOpen = true ;
|
|
}
|
|
// NB. Se regione estena tutta chiusa o aperta e senza isole... allora non faccio nulla ( es caso trapezi)
|
|
// Se tutta chiusa -> Chunk non svuotabile, il contro-offset del raggio utensile annulla la regione
|
|
// Se tutta aperta -> La regione è definita mediante Offset esterno del contorno aperto
|
|
if ( ( bExtAllClose || bExtAllOpen) && ! bHasIslands)
|
|
return true ;
|
|
// Se il contorno esterno è misto -> non devo avere isole
|
|
if ( ! bExtAllClose && ! bExtAllOpen && bHasIslands)
|
|
return true ;
|
|
// Se il contorno esterno è tutto chiuso e ho più di un'isola non faccio nulla
|
|
if ( bExtAllClose && pSfrChunk->GetLoopCount( 0) > 2)
|
|
return true ;
|
|
|
|
// scorro i chiusi e sottraggo la regione da loro creata mediante l'utensile
|
|
// NB. Queste curve sono orientate in maniera corretta
|
|
ICRVCOMPOPOVECTOR vCrvCompoRes_tmp ; // salvo il vettore di curve offsettate del raggio utensile
|
|
Voronoi myVRONI ;
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
// se tratto chiuso
|
|
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
|
|
myVRONI.AddCurve( vpCrvs[i]) ;
|
|
}
|
|
// offset delle parti chiuse
|
|
ICURVEPOVECTOR vCrvVroniOffs ;
|
|
myVRONI.CalcOffset( vCrvVroniOffs, - PockParams.dRad - PockParams.dRadialOffset + EPS_SMALL, ICurve::OFF_FILLET) ;
|
|
for ( int i = 0 ; i < int( vCrvVroniOffs.size()) ; ++ i)
|
|
vCrvCompoRes_tmp.emplace_back( ConvertCurveToComposite( Release( vCrvVroniOffs[i]))) ;
|
|
|
|
/*
|
|
Casi gestiti :
|
|
1) La curva esterna e mista e non ho isole ( ho già il percorso)
|
|
2) La curva esterna è tutta aperta e le isole sono tutte chiuse
|
|
3) La curva esterna è tutta chiusa e ho una sola isola aperta ( ho già il percorso)
|
|
*/
|
|
|
|
// 1)
|
|
if ( ! bExtAllClose && ! bExtAllOpen)
|
|
;
|
|
else {
|
|
// scorro tutte le isole
|
|
bool bValidIslands = true ; // flag per isole tutte uniformi
|
|
for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) && bValidIslands ; ++ i) {
|
|
// controllo uniformità dell'isola
|
|
int nCurrTmpProp = -1 ;
|
|
int nPrecTmpProp = -1 ;
|
|
bool bIsMixed = false ;
|
|
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ;
|
|
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
|
|
return false ;
|
|
for ( int u = 0 ; u < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ u) {
|
|
pCrvIsl->GetCurveTempProp( u, nCurrTmpProp, 0) ;
|
|
bIsMixed = ( u != 0 && nCurrTmpProp != nPrecTmpProp) ;
|
|
nPrecTmpProp = nCurrTmpProp ;
|
|
}
|
|
// se proprità non uniformi -> tutta chiusa ( isole non uniformi non sono definite)
|
|
if ( bIsMixed) {
|
|
for ( int u = 0 ; u < pCrvIsl->GetCurveCount() ; ++ u)
|
|
pCrvIsl->SetTempProp( u, TEMP_PROP_CLOSE_EDGE) ;
|
|
nCurrTmpProp = 0 ; // aggiorno il Flag
|
|
}
|
|
// 2) e 3)
|
|
bValidIslands = ( ( nCurrTmpProp == TEMP_PROP_CLOSE_EDGE && bExtAllOpen) ||
|
|
( nCurrTmpProp == TEMP_PROP_OPEN_EDGE && bExtAllClose)) ;
|
|
}
|
|
if ( ! bValidIslands)
|
|
return true ;
|
|
// nel caso 2) devo inserire le curve
|
|
if ( bExtAllOpen) {
|
|
// creo una regione formata solo dalla isole
|
|
PtrOwner<ISurfFlatRegion> pSrfIslands( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfIslands))
|
|
return false ;
|
|
for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) ; ++ i) {
|
|
pSrfIslands->AddExtLoop( pSfrChunk->GetLoop( 0, i)) ;
|
|
vpCrvs.emplace_back( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ;
|
|
}
|
|
pSrfIslands->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET) ;
|
|
for ( int nC = 0 ; nC < pSrfIslands->GetChunkCount() ; ++ nC)
|
|
for ( int nL = 0 ; nL < pSrfIslands->GetLoopCount( nL) ; ++ nL)
|
|
vCrvCompoRes_tmp.emplace_back( ConvertCurveToComposite( pSrfIslands->GetLoop( nC, nL))) ;
|
|
}
|
|
}
|
|
|
|
// se non ho ottenuto curve, allora ho finito
|
|
if ( vCrvCompoRes_tmp.empty())
|
|
return true ;
|
|
|
|
// controllo che effettivamente l'utensile svuoti la regione
|
|
PtrOwner<ISurfFlatRegion> pSfrNoRemoved( CloneSurfFlatRegion( pSfrChunk)) ;
|
|
if ( IsNull( pSfrNoRemoved) || ! pSfrNoRemoved->IsValid())
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) {
|
|
PtrOwner<ISurfFlatRegion> pSfrRemoved( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vCrvCompoRes_tmp[i]), PockParams.dRad + PockParams.dRadialOffset + 50 * EPS_SMALL, false, false)) ;
|
|
if ( ! IsNull( pSfrRemoved) && pSfrRemoved->IsValid()) {
|
|
if ( AreOppositeVectorEpsilon( pSfrRemoved->GetNormVersor(), pSfrNoRemoved->GetNormVersor(), 25. * EPS_SMALL))
|
|
pSfrRemoved->Invert() ;
|
|
pSfrNoRemoved->Subtract( *pSfrRemoved) ;
|
|
}
|
|
}
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) {
|
|
PtrOwner<ISurfFlatRegion> pSfrRemoved( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vpCrvs[i]), PockParams.dRad + PockParams.dRadialOffset + 50 * EPS_SMALL, false, false)) ;
|
|
if ( ! IsNull( pSfrRemoved) && pSfrRemoved->IsValid()) {
|
|
if ( AreOppositeVectorEpsilon( pSfrRemoved->GetNormVersor(), pSfrNoRemoved->GetNormVersor(), 25. * EPS_SMALL))
|
|
pSfrRemoved->Invert() ;
|
|
pSfrNoRemoved->Subtract( *pSfrRemoved) ;
|
|
}
|
|
}
|
|
}
|
|
if ( pSfrNoRemoved->IsValid() && pSfrNoRemoved->GetChunkCount() > 0)
|
|
return true ;
|
|
|
|
bAllRemoved = true ;
|
|
|
|
// recupero la superficie limite ( se esiste ed è valida)
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrLimit))
|
|
return false ;
|
|
|
|
// per tutte le curve inserite, devo tenere solamente quelle esterne alla superficie limite
|
|
if ( PockParams.SfrLimit.IsValid()) { // se esiste, allora classifico
|
|
// recupero la superficie limite
|
|
pSfrLimit.Set( PockParams.SfrLimit.Clone()) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
|
|
return false ;
|
|
// effettuo un Offset della regione, tutto ciò che dista più del raggio utensile non la rovina
|
|
pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset - 250 * EPS_SMALL, ICurve::OFF_FILLET) ; // c'è un offset radiale, quindi tengo tolleranza alta
|
|
ICRVCOMPOPOVECTOR vCrvCompoRes_tmp_Splitted ;
|
|
for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) {
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrLimit->GetCurveClassification( *vCrvCompoRes_tmp[i], EPS_SMALL, ccClass)) {
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
if ( ccClass[j].nClass == CRVC_OUT) {
|
|
PtrOwner<ICurveComposite> pCrvRes( ConvertCurveToComposite( vCrvCompoRes_tmp[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
|
|
if ( ! IsNull( pCrvRes) && pCrvRes->IsValid())
|
|
vCrvCompoRes_tmp_Splitted.emplace_back( Release( pCrvRes)) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se non ho curve, allora esco
|
|
if ( vCrvCompoRes_tmp_Splitted.empty())
|
|
return true ;
|
|
// altrimenti aggiorno il vettore di curve con quelle solo esterne alla regione limite
|
|
swap( vCrvCompoRes_tmp, vCrvCompoRes_tmp_Splitted) ;
|
|
}
|
|
|
|
// salvo come primo tempParam l'offset massimo della regione ( servirà per la Feed)
|
|
for ( int i = 0 ; i < int( vCrvCompoRes_tmp.size()) ; ++ i) {
|
|
vCrvCompoRes_tmp[i]->SetTempParam( dMaxOffs, 0) ;
|
|
// se richiesta inversione della curva, allora inverto
|
|
if ( PockParams.bInvert)
|
|
vCrvCompoRes_tmp[i]->Invert() ;
|
|
vCrvCompoRes.emplace_back( Release( vCrvCompoRes_tmp[i])) ; // aggiungo la curva al vettore
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetSinglePocketingCurves( ISurfFlatRegion* pSfr, const PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvSingleCurves)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
vCrvSingleCurves.clear() ;
|
|
|
|
// tengo una copia della superficie ( serve per l'estensione dei percorsi fuori dal grezzo)
|
|
PtrOwner<ISurfFlatRegion> pSfr_clone( CloneSurfFlatRegion( pSfr)) ;
|
|
if ( IsNull( pSfr_clone) || ! pSfr_clone->IsValid())
|
|
return false ;
|
|
|
|
// scorro i Chunk della superficie
|
|
int nChunks = pSfr->GetChunkCount() ;
|
|
int nCurrChunk = 0 ;
|
|
for ( int i = 0 ; i < nChunks ; ++ i) {
|
|
// recupero il Chunk corrente
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( nCurrChunk)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
// se il Chunk corrente si svuota con curva singola, allora lo rimuovo dalla superficie
|
|
bool bRemoved = false ;
|
|
GetPocketCurvesByClosedEdges( pSfrChunk, PockParams, vCrvSingleCurves, bRemoved) ;
|
|
if ( bRemoved)
|
|
pSfr->EraseChunk( nCurrChunk) ;
|
|
else
|
|
++ nCurrChunk ;
|
|
}
|
|
|
|
// se ho ottenuto delle curve singole, allora le provo a concatenare
|
|
// ( tenendo solo le curve al di fuori della superficie limite con Offset del raggio del tool)
|
|
// potrei doverle riconcatenare
|
|
if ( ! ChainCompoCurves( vCrvSingleCurves))
|
|
return false ;
|
|
// estendo le curve, definendo ptStart e calcolando le Feeds (non inverto le curve aperte)
|
|
if ( ! AdvanceExtendCurves( vCrvSingleCurves, pSfr_clone, PockParams, false))
|
|
return false ;
|
|
// imposto le Feed per tali curve se richiesto
|
|
for ( int i = 0 ; i < int( vCrvSingleCurves.size()) ; ++ i) {
|
|
/*
|
|
Idea : Feed proporzionale al minimo Offset per annullare la regione
|
|
-> Massimo parametro sui bisettori di VORONOI
|
|
se le curve risultano aperte, cambio il loro punto iniziale a seconda dell'estensione fatta
|
|
*/
|
|
if ( vCrvSingleCurves[i]->IsValid() && vCrvSingleCurves[i]->GetCurveCount() != 0 &&
|
|
! vCrvSingleCurves[i]->IsClosed()) {
|
|
int nTempProp_firstCrv = vCrvSingleCurves[i]->GetFirstCurve()->GetTempProp( 0) ;
|
|
int nTempProp_lastCrv = vCrvSingleCurves[i]->GetLastCurve()->GetTempProp( 0) ;
|
|
// se estremi entrambi estesi
|
|
if ( nTempProp_firstCrv == TEMP_PROP_OUT_START && nTempProp_lastCrv == TEMP_PROP_OUT_START) {
|
|
if ( PockParams.ptStart.IsValid()) {
|
|
Point3d ptStart ; vCrvSingleCurves[i]->GetStartPoint( ptStart) ;
|
|
double dSqDist_first = SqDist( PockParams.ptStart, ptStart) ;
|
|
Point3d ptEnd ; vCrvSingleCurves[i]->GetEndPoint( ptEnd) ;
|
|
double dSqDist_last = SqDist( PockParams.ptStart, ptEnd) ;
|
|
if ( dSqDist_last < dSqDist_first)
|
|
vCrvSingleCurves[i]->Invert() ;
|
|
}
|
|
}
|
|
// se invece l'estensione è alla fine
|
|
else if ( nTempProp_lastCrv == TEMP_PROP_OUT_START)
|
|
vCrvSingleCurves[i]->Invert() ;
|
|
}
|
|
// recupero il MaxOffset per la curva
|
|
double dMaxOffs = vCrvSingleCurves[i]->GetTempParam( 0) ;
|
|
// calcolo la Feed proporzionale a tale Offset
|
|
double dFeed ;
|
|
GetFeedForParam( 2 * dMaxOffs, PockParams, dFeed) ;
|
|
AssignCustomFeed( vCrvSingleCurves[i], PockParams, dFeed) ;
|
|
// imposto il flag di curva singola
|
|
vCrvSingleCurves[i]->SetTempProp( TEMP_PROP_SINGLE_CURVE, 0) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ModifySurfByOpenEdges( ISurfFlatRegion* pSfr, PocketParams& PockParams)
|
|
{
|
|
// controllo parametri :
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return true ; // <- se superficie non valida, allora non ho niente da impostare sui suoi lati
|
|
|
|
// se lati tutti chiusi, allora non devo fare nulla
|
|
if ( PockParams.bAllClosed)
|
|
return true ;
|
|
|
|
// NB. Tutti i Loop che presentano dei lati aperti possono essere estesi ; sia per loop esterni che per isole...
|
|
// I lati aperti vanno estesi seguendo lo geometria dei lati chiusi adiacenti e tenendo conto delle isole chiuse
|
|
// vicine ad essi
|
|
|
|
// creo la superficie da restituire... ( questa superficie sarà estesa presso i lati aperti)
|
|
PtrOwner<ISurfFlatRegion> pSrfFinal( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfFinal))
|
|
return false ;
|
|
|
|
// per ogni Chunck della superificie ottenuta...
|
|
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
|
|
|
|
// flag per indicare se il Chunk è stato modificato mediante estensione degli aperti
|
|
bool bIsChunkModified = true ;
|
|
|
|
// ricavo il Loop esterno ( External Loop)
|
|
PtrOwner<ICurveComposite> pCrvEL( ConvertCurveToComposite( pSfr->GetLoop( nC, 0))) ;
|
|
if ( IsNull( pCrvEL) || ! pCrvEL->IsValid())
|
|
return false ;
|
|
|
|
// creo un vettore di curve con le isole del Chunk ( Internal Loops)
|
|
ICRVCOMPOPOVECTOR vCrvIsl ;
|
|
for ( int nL = 1 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) {
|
|
PtrOwner<ICurveComposite> pCrvIL( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ;
|
|
if ( IsNull( pCrvIL) || ! pCrvIL->IsValid())
|
|
return false ;
|
|
vCrvIsl.emplace_back( Release( pCrvIL)) ;
|
|
}
|
|
|
|
// se la curva esterna presenta dei lati aperti -> devo modificarla
|
|
bool bSomeOpen = false ;
|
|
int nProp0 = TEMP_PROP_INVALID ;
|
|
for ( int u = 0 ; u < pCrvEL->GetCurveCount() && ! bSomeOpen ; ++ u)
|
|
bSomeOpen = ( pCrvEL->GetCurveTempProp( u, nProp0, 0) && nProp0 == TEMP_PROP_OPEN_EDGE) ;
|
|
if ( bSomeOpen) {
|
|
// allorargo il Loop esterno presso i lati aperti
|
|
if ( ! AdjustContourWithOpenEdges( pCrvEL, vCrvIsl, PockParams))
|
|
return false ;
|
|
bIsChunkModified = true ;
|
|
}
|
|
|
|
// controllo i bordi delle isole ottenute
|
|
// NB. L'isola può essere tutta aperta o tutta chiusa ( se non uniforme, la forzo chiusa)
|
|
// [ La definizione di isola con proprietà non uniformi non è definita ]
|
|
ICRVCOMPOPOVECTOR vCrvToTIsland ; // isole che considero valide
|
|
for ( int nI = 0 ; nI < int( vCrvIsl.size()) ; ++ nI) {
|
|
// controllo uniformità
|
|
int nCurrTmpProp = TEMP_PROP_INVALID ;
|
|
int nPrecTmpProp = TEMP_PROP_INVALID ;
|
|
bool bIsMixed = false ;
|
|
PtrOwner<ICurveComposite> pCrvIsl( CloneCurveComposite( vCrvIsl[nI])) ;
|
|
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
|
|
return false ;
|
|
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ nU) {
|
|
pCrvIsl->GetCurveTempProp( nU, nCurrTmpProp, 0) ;
|
|
bIsMixed = ( nU != 0 && nCurrTmpProp != nPrecTmpProp) ;
|
|
nPrecTmpProp = nCurrTmpProp ;
|
|
}
|
|
// se proprità non uniformi -> tutta chiusa
|
|
if ( bIsMixed) {
|
|
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() ; ++ nU)
|
|
pCrvIsl->SetTempProp( nU, TEMP_PROP_CLOSE_EDGE) ;
|
|
nCurrTmpProp = TEMP_PROP_CLOSE_EDGE ;
|
|
bIsChunkModified = true ;
|
|
}
|
|
// se curva tutta aperta, controllo se l'isola può essere trascurata
|
|
if ( nCurrTmpProp == TEMP_PROP_OPEN_EDGE) {
|
|
// calcolo il massimo Offset
|
|
double dMaxOffs ;
|
|
CalcCurveLimitOffset( *pCrvIsl, dMaxOffs) ;
|
|
// se l'isola è trascurabile passo alla successiva
|
|
if ( dMaxOffs < 2 * PockParams.dRad + 2 * PockParams.dRadialOffset)
|
|
continue ;
|
|
// altrimenti la restringo
|
|
ICRVCOMPOPOVECTOR vCrvOther ;
|
|
vCrvOther.emplace_back( pCrvEL->Clone()) ;
|
|
for ( int nII = 0 ; nII < int( vCrvIsl.size()) ; ++ nII) {
|
|
if ( nII != nI)
|
|
vCrvOther.emplace_back( vCrvIsl[nII]->Clone()) ;
|
|
}
|
|
if ( ! AdjustContourWithOpenEdges( pCrvIsl, vCrvOther, PockParams))
|
|
return false ;
|
|
bIsChunkModified = true ;
|
|
}
|
|
// conservo l'isola ( estesa o meno)
|
|
vCrvToTIsland.emplace_back( pCrvIsl->Clone()) ; // <-- valida
|
|
}
|
|
|
|
// se c'è stata almeno una modifica di lato aperto al Chunk ( nC-esimo), devo ricreare il Chunk
|
|
// con i nuovi loop ricavati ( Chunk dopo Chunk creo la superficie finale)
|
|
if ( bIsChunkModified) {
|
|
/*
|
|
l'estensione degli aperti, può unire il loop esterno con il loop delle sue isole.
|
|
Creo una nuova regione dai nuovi contorni ottenuti
|
|
*/
|
|
SurfFlatRegionByContours SfrBC ;
|
|
// loop Esterno
|
|
pCrvEL->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
SfrBC.AddCurve( Release( pCrvEL)) ;
|
|
// isole non trascurate
|
|
for ( int nI = 0 ; nI < int( vCrvToTIsland.size()) ; ++ nI) {
|
|
vCrvToTIsland[nI]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
SfrBC.AddCurve( Release( vCrvToTIsland[nI])) ;
|
|
}
|
|
// ricavo il nuovo Chunk nC-esimo esteso presso i lati aperti
|
|
PtrOwner<ISurfFlatRegion> pNewChunk( SfrBC.GetSurf()) ;
|
|
if ( IsNull( pNewChunk) || ! pNewChunk->IsValid())
|
|
return false ;
|
|
// aggiungo il Chunk nC-esimo alla superficie finale
|
|
if ( pSrfFinal->GetChunkCount() == 0)
|
|
pSrfFinal.Set( pNewChunk) ;
|
|
else if ( ! pSrfFinal->Add( *pNewChunk))
|
|
return false ;
|
|
}
|
|
// se il Chunk c-esimo non è mai stato modificato
|
|
else {
|
|
// aggiungo il Chunk alla superficie finale
|
|
if ( pSrfFinal->GetChunkCount() == 0)
|
|
pSrfFinal.Set( pSfr->CloneChunk( nC)) ;
|
|
else if ( ! pSrfFinal->Add( *pSfr->CloneChunk( nC)))
|
|
return false ;
|
|
}
|
|
}
|
|
// se la superficie finale estesa non è valida, errore
|
|
if ( ! pSrfFinal->IsValid())
|
|
return false ;
|
|
|
|
|
|
// chiudo le isole che risultano ambigue ( quindi non omgenee)
|
|
for ( int nC = 0 ; nC < pSrfFinal->GetChunkCount() ; ++ nC) {
|
|
for ( int nL = 1 ; nL < pSrfFinal->GetLoopCount( nC) ; ++ nL) {
|
|
// controllo uniformità
|
|
int nCurrTmpProp = TEMP_PROP_INVALID ;
|
|
int nPrecTmpProp = TEMP_PROP_INVALID ;
|
|
bool bIsMixed = false ;
|
|
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSrfFinal->GetLoop( nC, nL))) ;
|
|
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
|
|
return false ;
|
|
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ nU) {
|
|
pCrvIsl->GetCurveTempProp( nU, nCurrTmpProp, 0) ;
|
|
bIsMixed = ( nU != 0 && nCurrTmpProp != nPrecTmpProp) ;
|
|
nPrecTmpProp = nCurrTmpProp ;
|
|
}
|
|
// se proprità non uniformi -> tutta chiusa
|
|
if ( bIsMixed) {
|
|
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() ; ++ nU)
|
|
pSrfFinal->SetCurveTempProp( nC, nL, nU, TEMP_PROP_CLOSE_EDGE) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// restituisco la superficie estesa
|
|
pSfr->Clear() ;
|
|
pSfr->CopyFrom( pSrfFinal) ;
|
|
return ( pSfr->IsValid() && pSfr->GetChunkCount() > 0) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
OptimizedSpiralCircle( const ICurveComposite* pCrvCompo, const double dToll, double& dRad,
|
|
Point3d& ptC, bool& bIsCirlce)
|
|
{
|
|
/* restituisce il centro e il raggio di una circonfereza che approssima pCrvCompo */
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || dToll < EPS_SMALL)
|
|
return false ;
|
|
dRad = 0. ;
|
|
ptC = P_INVALID ;
|
|
bIsCirlce = false ;
|
|
|
|
// se aperta, non è un cerchio e non è piana, esco
|
|
Plane3d plPock ;
|
|
if ( ! pCrvCompo->IsClosed() || ! pCrvCompo->IsFlat( plPock))
|
|
return true ;
|
|
|
|
// creo un sistema di riferimento centrato sulla curva
|
|
Point3d ptCentroid ;
|
|
Vector3d vtN ;
|
|
Frame3d frCurr ;
|
|
if ( ! pCrvCompo->GetCentroid( ptCentroid) ||
|
|
! pCrvCompo->GetExtrusion( vtN) ||
|
|
! frCurr.Set( ptCentroid, vtN) ||
|
|
! frCurr.IsValid())
|
|
return false ;
|
|
|
|
// porto la curva nel sistema di riferimento corrente ( dopo averla clonata)
|
|
PtrOwner<ICurveComposite> pCrvLoc( pCrvCompo->Clone()) ;
|
|
if ( IsNull( pCrvLoc) || ! pCrvLoc->IsValid() || ! pCrvLoc->ToLoc( frCurr))
|
|
return false ;
|
|
|
|
// approssimo la curva locale con una PolyLine
|
|
PolyLine pL ;
|
|
if ( ! pCrvLoc->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, pL) ||
|
|
pL.GetPointNbr() == 0)
|
|
return false ;
|
|
|
|
// salvo i punti ottenuti ( il punto finale sarà contenuto due volte)
|
|
PNTVECTOR vPts ;
|
|
Point3d ptNext ;
|
|
pL.GetFirstPoint( ptNext) ;
|
|
vPts.push_back( ptNext) ;
|
|
while ( pL.GetNextPoint( ptNext)) {
|
|
vPts.push_back( Media( vPts.back(), ptNext)) ; // inserisco il punto medio
|
|
vPts.push_back( ptNext) ; // inserisco il punto successivo
|
|
}
|
|
|
|
// per ogni coppia di punti calcolo la distanza massima e minima dal centro del cerchio locale ( ORIG)
|
|
double dMaxDist = 0. ;
|
|
double dMinDist = INFINITO ;
|
|
for ( int i = 0 ; i < int( floor( 0.5 * int( vPts.size()))) ; i = i + 2) {
|
|
PtrOwner<ICurveLine> pSeg( CreateCurveLine()) ;
|
|
if ( IsNull( pSeg) || ! pSeg->Set( vPts[i], vPts[i+1]))
|
|
return false ;
|
|
DistPointCurve DPC( ORIG, *pSeg) ;
|
|
double dCurrDist = 0. ;
|
|
if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist)
|
|
dMinDist = dCurrDist ;
|
|
dCurrDist = Dist( ORIG, vPts[i]) ;
|
|
if ( dCurrDist > dMaxDist)
|
|
dMaxDist = dCurrDist ;
|
|
}
|
|
|
|
// controllo se la distanza tra minima e massima sono simili ( in base alla tolleranza)
|
|
if ( abs( dMaxDist - dMinDist) > dToll)
|
|
return true ;
|
|
|
|
// imposto i parametri per la circonferenza
|
|
dRad = 0.5 * ( dMaxDist + dMinDist) ;
|
|
ptC = ptCentroid ;
|
|
bIsCirlce = true ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcCircleSpiral( const Point3d& ptCen, const Vector3d& vtN, double dOutRad, double dIntRad,
|
|
const PocketParams& PockParams, ICurveComposite* pMCrv)
|
|
{
|
|
// raggio della circonferenza esterna
|
|
if ( dOutRad < 10 * EPS_SMALL)
|
|
return false ;
|
|
|
|
// imposto versore estrusione sulle curve composite
|
|
pMCrv->SetExtrusion( vtN) ;
|
|
|
|
// creo e inserisco la circonferenza esterna
|
|
PtrOwner<ICurveArc> pArc( CreateCurveArc()) ;
|
|
if ( IsNull( pArc) || ! pArc->Set( ptCen, vtN, dOutRad))
|
|
return false ;
|
|
|
|
// creo la superificie per la Feed
|
|
PtrOwner<ISurfFlatRegion> pSrfRemoved( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfRemoved))
|
|
return false ;
|
|
ICRVCOMPOPOVECTOR vLinksDone ;
|
|
|
|
Vector3d vtDir = pArc->GetStartVersor() ;
|
|
pMCrv->AddCurve( Release( pArc)) ;
|
|
|
|
// se richiesta percorrenza invertita
|
|
if ( PockParams.bInvert)
|
|
pMCrv->Invert() ;
|
|
|
|
// se raggio esterno maggiore dell'interno
|
|
if ( dOutRad > dIntRad + 10 * EPS_SMALL) {
|
|
|
|
// aggiungo le semicirconferenze della spirale ( devono essere in numero dispari)
|
|
int nStep = int( ceil( ( dOutRad - dIntRad) / ( 0.5 * PockParams.dSideStep))) ;
|
|
if ( nStep % 2 == 0)
|
|
nStep += 1 ;
|
|
double dStep = ( dOutRad - dIntRad) / nStep ;
|
|
for ( int i = 1 ; i <= nStep ; ++ i) {
|
|
if ( ! ( i % 2 == 0))
|
|
pMCrv->AddArcTg( ptCen - vtDir * ( dOutRad - i * dStep)) ;
|
|
else
|
|
pMCrv->AddArcTg( ptCen + vtDir * ( dOutRad - i * dStep)) ;
|
|
}
|
|
|
|
// aggiungo la circonferenza interna
|
|
pMCrv->AddArcTg( ptCen + vtDir * dIntRad) ;
|
|
pMCrv->AddArcTg( ptCen - vtDir * dIntRad) ;
|
|
}
|
|
|
|
// verifico il percorso di lavoro
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// assegno la Feed
|
|
AssignFeedSpiralOpt( 0, PockParams, pMCrv) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
CalcTrapezoidSpiralLocalFrame( ICurveComposite* pCrvTrap, const Vector3d& vtDir, Frame3d& frLoc)
|
|
{
|
|
// cerco i lati paralleli a vtDir
|
|
int nBaseId = -1 ;
|
|
for ( int i = 0 ; i < pCrvTrap->GetCurveCount() ; i ++) {
|
|
Vector3d vtEdge ;
|
|
pCrvTrap->GetCurve( i)->GetStartDir( vtEdge) ;
|
|
if ( AreSameOrOppositeVectorApprox( vtEdge, vtDir)) {
|
|
nBaseId = i ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( nBaseId != 0 && nBaseId != 1)
|
|
return false ;
|
|
|
|
// imposto come lato iniziale per la curva uno dei lati paralleli a vtDir
|
|
pCrvTrap->ChangeStartPoint( nBaseId) ;
|
|
Point3d ptOrig ; pCrvTrap->GetStartPoint( ptOrig) ;
|
|
Vector3d vtX ; pCrvTrap->GetStartDir( vtX) ;
|
|
return frLoc.Set( ptOrig, Z_AX, vtX) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetBoxCrvOptTrap( const int nCrv, const ICurveComposite* pCrvCompo, const double dDiam, int& nType,
|
|
ICurveComposite* pCrvBox)
|
|
{
|
|
nType = - 1 ;
|
|
// prendo la curva
|
|
const ICurve* pCrvCurr = pCrvCompo->GetCurve( nCrv) ;
|
|
if ( pCrvCurr == nullptr)
|
|
return false ;
|
|
// controllo se lineare, altrimenti passo alla successiva
|
|
if ( pCrvCurr->GetType() != CRV_LINE)
|
|
return true ;
|
|
// prendo la direzione del tratto lineare
|
|
Vector3d vtDir ; pCrvCurr->GetStartDir( vtDir) ;
|
|
// prendo il punto iniziale
|
|
Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ;
|
|
// creo il riferimento basato su questo tratto
|
|
Frame3d frTrap ; frTrap.Set( ptStart, Z_AX, vtDir) ;
|
|
if ( ! frTrap.IsValid())
|
|
return false ;
|
|
// porto la curva Compo ( clonandola) nel frame e calcolo il suo BBox
|
|
BBox3d BBox ;
|
|
PtrOwner<ICurveComposite> pCrvCompo_c( pCrvCompo->Clone()) ;
|
|
if ( IsNull( pCrvCompo_c) || ! pCrvCompo_c->IsValid() ||
|
|
! pCrvCompo_c->ToLoc( frTrap) ||
|
|
! pCrvCompo_c->GetLocalBBox( BBox))
|
|
return false ;
|
|
// controllo dimY e dimX per il box
|
|
bool bDimYOk = BBox.GetDimY() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ;
|
|
bool bDimXOk = BBox.GetDimX() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ;
|
|
// controllo dimensioni ammissibili
|
|
if ( ! bDimXOk && ! bDimYOk)
|
|
return true ; // se nessuna
|
|
else if ( ! bDimXOk)
|
|
nType = 1 ; // se dimY
|
|
else if ( ! bDimYOk)
|
|
nType = 0 ; // se dimX
|
|
else
|
|
nType = 2 ; // se entrambe
|
|
|
|
// creo il rettangolo del Box
|
|
pCrvBox->Clear() ;
|
|
pCrvBox->AddPoint( BBox.GetMin()) ;
|
|
pCrvBox->AddLine( BBox.GetMin() + BBox.GetDimX() * X_AX) ;
|
|
pCrvBox->AddLine( BBox.GetMax()) ;
|
|
pCrvBox->AddLine( BBox.GetMax() - BBox.GetDimX() * X_AX) ;
|
|
pCrvBox->Close() ;
|
|
|
|
// determino il punto inziale della curva
|
|
if ( nType == 0)
|
|
pCrvBox->ChangeStartPoint( 1.) ;
|
|
|
|
pCrvBox->ToGlob( frTrap) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
PreparareTrapezoidTwoBases( const ICurveComposite* pCrvCompo, const double dDiam, const int nType, int& nBase,
|
|
int& nSecondBase, bool& bOk, ICurveComposite* pCrvTrap)
|
|
{
|
|
// controllo parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() ||
|
|
nBase < 0 || nBase >= pCrvCompo->GetCurveCount() ||
|
|
nSecondBase < 0 || nSecondBase >= pCrvCompo->GetCurveCount())
|
|
return false ;
|
|
bOk = false ;
|
|
|
|
// le parti tra le due basi devono essere tutte omogenee ( o tutte aperte o tutte chiuse)
|
|
// pCrvTest0 sarà la parte destra, pCrvTest1 la parte sinistra
|
|
Point3d ptSB0, ptSB1 ,ptEB0, ptEB1 ;
|
|
PtrOwner<ICurveComposite> pCrvTest0( CloneCurveComposite( pCrvCompo)) ;
|
|
PtrOwner<ICurveComposite> pCrvTest1( CloneCurveComposite( pCrvCompo)) ;
|
|
pCrvCompo->GetCurve( nBase)->GetStartPoint( ptSB0) ;
|
|
pCrvCompo->GetCurve( nBase)->GetEndPoint( ptEB0) ;
|
|
pCrvCompo->GetCurve( nSecondBase)->GetStartPoint( ptSB1) ;
|
|
pCrvCompo->GetCurve( nSecondBase)->GetEndPoint( ptEB1) ;
|
|
pCrvTest0->ChangeStartPoint( nBase) ;
|
|
pCrvTest1->ChangeStartPoint( nSecondBase) ;
|
|
double dUTrim0, dUTrim1 ;
|
|
pCrvTest0->GetParamAtPoint( ptSB1, dUTrim0) ;
|
|
pCrvTest1->GetParamAtPoint( ptSB0, dUTrim1) ;
|
|
pCrvTest0->TrimStartEndAtParam( 1, dUTrim0) ;
|
|
pCrvTest1->TrimStartEndAtParam( 1, dUTrim1) ;
|
|
// controllo che la parte destra si uniforme per le TmpProp
|
|
for ( int u = 0 ; u < pCrvTest0->GetCurveCount() - 1 ; ++ u) {
|
|
int nPropAct, nPropSucc ;
|
|
if ( pCrvTest0->GetCurveTempProp( u, nPropAct, 0) &&
|
|
pCrvTest0->GetCurveTempProp( u + 1, nPropSucc, 0) &&
|
|
nPropAct != nPropSucc)
|
|
return true ; // se TmpProp diverse => non è un caso ottimizzato
|
|
}
|
|
// controllo che la parte sinistra sia uniforme per le TmpProp
|
|
for ( int u = 0 ; u < pCrvTest1->GetCurveCount() - 1 ; ++ u) {
|
|
int nPropAct, nPropSucc ;
|
|
if ( pCrvTest1->GetCurveTempProp( u, nPropAct, 0) &&
|
|
pCrvTest1->GetCurveTempProp( u + 1, nPropSucc, 0) &&
|
|
nPropAct != nPropSucc)
|
|
return true ; // se TmpProp diverse => non è un caso ottimizzato
|
|
}
|
|
|
|
// se lato destro aperto ( estendo il punto finale della base principale e il punto iniziale
|
|
// della base secondaria fino al lato destro del box)
|
|
bool bCopyRight = false ;
|
|
if ( pCrvTest0->GetCurve( 0)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
int nCrvRight_ref = ( nType == 0 ? 0 : 1) ;
|
|
pCrvTrap->GetCurve( nCrvRight_ref)->GetStartPoint( ptEB0) ;
|
|
pCrvTrap->GetCurve( nCrvRight_ref)->GetEndPoint( ptSB1) ;
|
|
}
|
|
// se lato destro chiuso
|
|
else
|
|
bCopyRight = true ;
|
|
|
|
// se lato sinistro aperto ( estendo il punto finale della base secondaria e il punto iniziale
|
|
// della base primaria dino al lato sinistro del box)
|
|
bool bCopyLeft = false ;
|
|
if ( pCrvTest1->GetCurve( 0)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
int nCrvLeft_ref = ( nType == 0 ? 2 : 3) ;
|
|
pCrvTrap->GetCurve( nCrvLeft_ref)->GetStartPoint( ptEB1) ;
|
|
pCrvTrap->GetCurve( nCrvLeft_ref)->GetEndPoint( ptSB0) ;
|
|
}
|
|
// se lato sinistro chiuso
|
|
else
|
|
bCopyLeft = true ;
|
|
|
|
// creo la curva da restituire
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddPoint( ptSB0) ;
|
|
pCrvTrap->AddLine( ptEB0) ;
|
|
nBase = 0 ;
|
|
if ( bCopyRight)
|
|
pCrvTrap->AddCurve( Release( pCrvTest0)) ;
|
|
else {
|
|
pCrvTrap->AddLine( ptSB1) ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, TEMP_PROP_OPEN_EDGE, 0) ; // aperta
|
|
}
|
|
nSecondBase = pCrvTrap->GetCurveCount() ;
|
|
pCrvTrap->AddLine( ptEB1) ;
|
|
if ( bCopyLeft)
|
|
pCrvTrap->AddCurve( Release( pCrvTest1)) ;
|
|
else {
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, TEMP_PROP_OPEN_EDGE, 0) ; // aperta
|
|
}
|
|
|
|
// verifico dimensione x della svuotatura nel caso tutto chiuso
|
|
if ( bCopyLeft && bCopyRight) {
|
|
double dLen0 = 0, dLen2 = 0 ;
|
|
pCrvTrap->GetCurve( nBase)->GetLength( dLen0) ;
|
|
pCrvTrap->GetCurve( nSecondBase)->GetLength( dLen2) ;
|
|
if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL) {
|
|
pCrvTrap->Clear() ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
bOk = true ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTrap,
|
|
Frame3d& frTrap, const PocketParams& PockParams, double& dPocketSize, int& nBase, int& nSecondBase)
|
|
{
|
|
// controllo parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// diametro reale da tenere in considerazione
|
|
double dDiam = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ;
|
|
|
|
pCrvTrap->Clear() ; // resterà vuota se il caso non è ottimizzato
|
|
nBase = -1 ;
|
|
nSecondBase = -1 ;
|
|
|
|
// se la curva è già un trapezio, non sempre devo adattarla...
|
|
Point3d pt ; Vector3d vtDir, vtB2, vtOtherDir ;
|
|
if ( pCrvCompo->IsATrapezoid( 100 * EPS_SMALL, pt, vtDir, vtOtherDir, vtB2)) {
|
|
// data la tolleranza, creo la curva a trapezio dai 4 punti
|
|
pCrvTrap->AddPoint( pt) ;
|
|
pCrvTrap->AddLine( pt + vtDir) ;
|
|
pCrvTrap->AddLine( pt + vtOtherDir + vtB2) ;
|
|
pCrvTrap->AddLine( pt + vtOtherDir) ;
|
|
pCrvTrap->Close() ;
|
|
// se parallelogramma scelgo come base i lati lunghi
|
|
Vector3d vtL2( - vtDir + vtOtherDir + vtB2) ;
|
|
if ( AreSameOrOppositeVectorApprox( vtOtherDir, vtL2)) {
|
|
if ( vtOtherDir.Len() > vtDir.Len())
|
|
swap( vtDir, vtOtherDir) ;
|
|
}
|
|
vtDir.Normalize() ;
|
|
Vector3d vtOrtho = OrthoCompo( vtOtherDir, vtDir) ;
|
|
dPocketSize = vtOrtho.Len() ;
|
|
if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap))
|
|
return false ;
|
|
// assegno le tempProp della curva trapezio
|
|
for ( int nU = 0 ; nU < 4 ; ++ nU) {
|
|
Vector3d vtCrvDir ; pCrvTrap->GetCurve( nU)->GetStartDir( vtCrvDir) ;
|
|
for ( int nI = 0 ; nI < pCrvCompo->GetCurveCount() ; ++ nI) {
|
|
Vector3d vtCurrCrvDir ; pCrvTrap->GetCurve( nI)->GetStartDir( vtCurrCrvDir) ;
|
|
if ( AreSameVectorEpsilon( vtCrvDir, vtCurrCrvDir, 150 * EPS_SMALL)) {
|
|
pCrvTrap->SetCurveTempProp( nU, pCrvCompo->GetCurve( nI)->GetTempProp( 0), 0) ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
bool bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == 1 || pCrvTrap->GetCurve( 2)->GetTempProp( 0) == 1) ;
|
|
// controllo dimensioni della svuotatura
|
|
if ( ! ( bBaseOpen && dPocketSize < dDiam + EPS_SMALL) &&
|
|
abs( dPocketSize - dDiam) > EPS_SMALL) {
|
|
pCrvTrap->Clear() ;
|
|
return true ;
|
|
}
|
|
// recupero flag aperto/chiuso dei lati
|
|
if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == 0 && pCrvTrap->GetCurve( 3)->GetTempProp( 0) == 0) {
|
|
double dLen0, dLen2 ;
|
|
pCrvTrap->GetCurve( 0)->GetLength( dLen0) ;
|
|
pCrvTrap->GetCurve( 2)->GetLength( dLen2) ;
|
|
if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL)
|
|
pCrvTrap->Clear() ;
|
|
}
|
|
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
return true ;
|
|
}
|
|
|
|
// controllo il numero di lati chiusi e salvo i loro indici
|
|
int nClosedSide = 0 ;
|
|
INTVECTOR vIndClosedSides ;
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
|
|
int nTmpProp ;
|
|
if ( pCrvCompo->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 0) {
|
|
++ nClosedSide ;
|
|
vIndClosedSides.push_back( u) ;
|
|
}
|
|
}
|
|
|
|
// se tutti lati aperti
|
|
switch ( nClosedSide) {
|
|
// TUTTI I LATI SONO APERTI
|
|
case 0 : {
|
|
// ricavo il box minimo della curva aperta ( passo dalla polyLine)
|
|
PolyLine PL ; Point3d ptCen ; double dWidth ;
|
|
if ( ! pCrvCompo->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) ||
|
|
! PL.GetMinAreaRectangleXY( ptCen, vtDir, dWidth, dPocketSize))
|
|
return false ;
|
|
// controllo dimY ( dHeight), se troppo estesa, non è un caso ottimizzato
|
|
if ( dPocketSize > dDiam + TOL_TRAPEZOID)
|
|
return true ;
|
|
// inverto il frame attuale
|
|
frTrap.Set( ptCen, Z_AX, vtDir) ;
|
|
if ( ! frTrap.IsValid())
|
|
return false ;
|
|
// creo il rettangolo del Box
|
|
pCrvTrap->AddPoint( Point3d( - 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->AddLine( Point3d( - 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->ToGlob( frTrap) ;
|
|
Point3d ptNewOrig ; pCrvTrap->GetStartPoint( ptNewOrig) ;
|
|
frTrap.Set( ptNewOrig, Z_AX, vtDir) ;
|
|
// imposto tutte le 4 curve come aperte
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
}
|
|
break ;
|
|
// 1 LATO CHIUSO ( LINEARE)
|
|
case 1 : {
|
|
int nType = -1 ;
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType != -1) {
|
|
// imposto tutte le curve come aperte tranne la prima ( estendo il solo lato chiuso come base del Box)
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u == 0 ? 0 : 1, 0) ;
|
|
// memorizzo la dimensione di svuotatura
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
}
|
|
}
|
|
break ;
|
|
// 2 LATI CHIUSI ( LINEARI, CONSECUTIVI O PARALLELI)
|
|
case 2 : {
|
|
// controllo se entrambi sono lineari
|
|
if ( pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE &&
|
|
pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE) {
|
|
// se lineari, ricavo i punti estremanti e le direzioni
|
|
Point3d ptS0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartPoint( ptS0) ;
|
|
Point3d ptS1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartPoint( ptS1) ;
|
|
Point3d ptE0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetEndPoint( ptE0) ;
|
|
Point3d ptE1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetEndPoint( ptE1) ;
|
|
Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ;
|
|
Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ;
|
|
// CASO PARALLELI ( la base è indifferente)
|
|
if ( AreOppositeVectorEpsilon( vtDir0, vtDir1, 5 * EPS_SMALL)) {
|
|
int nType = -1 ;
|
|
// prendo come base il primo chiuso
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType != -1) {
|
|
// memorizzo la dimensione di svuotatura
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// controllo che la distanza tra i due chiusi sia effettivamente circa il raggio
|
|
double dDist = 0. ;
|
|
DistPointCurve DPL( ptS0, *pCrvCompo->GetCurve( vIndClosedSides[1]), false) ;
|
|
if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) {
|
|
// imposto tutte le curve di indice dispari aperte
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u % 2 == 0 ? 0 : 1, 0) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
}
|
|
else
|
|
pCrvTrap->Clear() ;
|
|
}
|
|
}
|
|
// CASO CONSECUTIVI
|
|
bool bOk_0_1 = AreSamePointApprox( ptS1, ptE0) ; // chiuso1 . chiuso0
|
|
bool bOk_1_0 = AreSamePointApprox( ptS0, ptE1) ; // chiuso0 . chiuso1
|
|
if ( bOk_0_1 || bOk_1_0) {
|
|
// provo con il primo lato
|
|
int nType = -1 ;
|
|
// prendo come base il primo chiuso
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType == -1) {
|
|
// provo con l'altro
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType != -1) {
|
|
// spaw tra gli indici e flags
|
|
swap( vIndClosedSides[0], vIndClosedSides[1]) ;
|
|
swap( bOk_0_1, bOk_1_0) ;
|
|
}
|
|
}
|
|
Point3d ptH1, ptH2 ;
|
|
if ( nType == 1 || nType == 2) {
|
|
double dLen1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetLength( dLen1) ;
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// i lati inclinati chiusi definiscono il Box
|
|
if ( abs( dLen1 * ( vtDir0 ^ vtDir1).Len() - dPocketSize) < TOL_TRAPEZOID) {
|
|
// creo la curva a trapezio
|
|
if ( bOk_0_1) {
|
|
pCrvTrap->GetCurve( 2)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 3)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ;
|
|
}
|
|
else {
|
|
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->AddLine( ptS1) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
pCrvTrap->SetCurveTempProp( 0, 0, 0) ;
|
|
for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ;
|
|
}
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
else
|
|
pCrvTrap->Clear() ;
|
|
}
|
|
else if ( nType == 0) {
|
|
if ( bOk_0_1) {
|
|
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ;
|
|
pCrvTrap->ChangeStartPoint( 1.) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
else {
|
|
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->AddLine( ptS1) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
pCrvTrap->SetCurveTempProp( 0, 0, 0) ;
|
|
for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ;
|
|
pCrvTrap->ChangeStartPoint( 1.) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break ;
|
|
// 3 LATI CHIUSI ( LINEARI e CONSECUTIVI)
|
|
case 3 : {
|
|
// prendo i 3 lati chiusi
|
|
const ICurve* pCrv0 = pCrvCompo->GetCurve( vIndClosedSides[0]) ;
|
|
const ICurve* pCrv1 = pCrvCompo->GetCurve( vIndClosedSides[1]) ;
|
|
const ICurve* pCrv2 = pCrvCompo->GetCurve( vIndClosedSides[2]) ;
|
|
if ( pCrv0 == nullptr || pCrv1 == nullptr || pCrv2 == nullptr)
|
|
return false ;
|
|
// controllo che siano lineari
|
|
if ( pCrv0->GetType() == CRV_LINE && pCrv1->GetType() == CRV_LINE && pCrv2->GetType() == CRV_LINE) {
|
|
// prendo i punti iniziali e finali
|
|
Point3d ptStart0 ; pCrv0->GetStartPoint( ptStart0) ;
|
|
Point3d ptEnd0 ; pCrv0->GetEndPoint( ptEnd0) ;
|
|
Point3d ptStart1 ; pCrv1->GetStartPoint( ptStart1) ;
|
|
Point3d ptEnd1 ; pCrv1->GetEndPoint( ptEnd1) ;
|
|
Point3d ptStart2 ; pCrv2->GetStartPoint( ptStart2) ;
|
|
Point3d ptEnd2 ; pCrv2->GetEndPoint( ptEnd2) ;
|
|
|
|
// controllo che siano consecutivi e cambio l'ordine se necessario
|
|
bool bOk = true ;
|
|
if ( AreSamePointApprox( ptEnd0, ptStart1) && AreSamePointApprox( ptEnd1, ptStart2))
|
|
;
|
|
else if ( AreSamePointApprox( ptEnd1, ptStart2) && AreSamePointApprox( ptEnd2, ptStart0)) {
|
|
swap( vIndClosedSides[0], vIndClosedSides[1]) ;
|
|
swap( vIndClosedSides[1], vIndClosedSides[2]) ;
|
|
}
|
|
else if ( AreSamePointApprox( ptEnd2, ptStart0) && AreSamePointApprox( ptEnd0, ptStart1)) {
|
|
swap( vIndClosedSides[0], vIndClosedSides[2]) ;
|
|
swap( vIndClosedSides[2], vIndClosedSides[1]) ;
|
|
}
|
|
else // se non consecutivi
|
|
bOk = false ;
|
|
|
|
// se i tre lati sono ( mediante lo swap) consecutivi...
|
|
if ( bOk) {
|
|
// controllo se la dimensione Y del lato 1 è valida ( il resto è gestito di seguito)
|
|
int nType = -1 ;
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType == 1) {
|
|
// bisgna controllare la lunghezza dei chiusi
|
|
double dLen0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetLength( dLen0) ;
|
|
double dLen2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetLength( dLen2) ;
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ;
|
|
Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ;
|
|
Vector3d vtDir2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetStartDir( vtDir2) ;
|
|
// i lati inclinati chiusi definiscono il Box
|
|
if ( abs( dLen2 * ( vtDir1 ^ vtDir2).Len() - dPocketSize) < TOL_TRAPEZOID &&
|
|
abs( dLen0 * ( vtDir1 ^ vtDir0).Len() - dPocketSize) < TOL_TRAPEZOID) {
|
|
// creo la curva a trapezio
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[2])->Clone()) ;
|
|
pCrvTrap->Close() ;
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u < 3 ? 0 : 1 , 1) ;
|
|
pCrvTrap->ChangeStartPoint( 1.) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// se non ho trovato delle basi, allora cerco in maniera generale un trapezoide ( se esiste)
|
|
if ( nBase == -1) {
|
|
bool bOK = false ;
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() && !bOK ; ++ u) {
|
|
// cerco un lato chiuso ( esiste per forza)
|
|
if ( pCrvCompo->GetCurve( u)->GetTempProp( 0) == 0) {
|
|
// controllo se il lato corrente può essere una base
|
|
int nType = -1 ;
|
|
if ( ! GetBoxCrvOptTrap( u, pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
// se è una base valida
|
|
if ( nType != -1) {
|
|
// ricavo la dimensione di svuotatura
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// ricavo la sua direzione iniziale
|
|
Vector3d vtBaseDir ; pCrvCompo->GetCurve( u)->GetStartDir( vtBaseDir) ;
|
|
// ricavo il suo punto iniziale
|
|
Point3d ptBaseStart ; pCrvCompo->GetCurve( u)->GetStartPoint( ptBaseStart) ;
|
|
// ricavo il suo punto finale
|
|
Point3d ptBaseEnd ; pCrvCompo->GetCurve( u)->GetEndPoint( ptBaseEnd) ;
|
|
// cerco il lato chiuso lineare parallelo ad esso e distante circa dPocketSize
|
|
for ( int uu = 0 ; uu < pCrvCompo->GetCurveCount() && !bOK ; ++ uu) {
|
|
if ( uu == u ||
|
|
pCrvCompo->GetCurve( uu)->GetType() != CRV_LINE ||
|
|
pCrvCompo->GetCurve( uu)->GetTempProp() != 0)
|
|
continue ;
|
|
Vector3d vtSecondBaseDir ; pCrvCompo->GetCurve( uu)->GetStartDir( vtSecondBaseDir) ;
|
|
if ( ! AreOppositeVectorEpsilon( vtBaseDir, vtSecondBaseDir, 5 * EPS_SMALL))
|
|
continue ;
|
|
Point3d ptSecondBaseStart ; pCrvCompo->GetCurve( uu)->GetStartPoint( ptSecondBaseStart) ;
|
|
double dDist = 0. ;
|
|
DistPointCurve DPL( ptBaseStart, *pCrvCompo->GetCurve( uu), false) ;
|
|
if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) {
|
|
nBase = u ;
|
|
nSecondBase = uu ;
|
|
if ( ! PreparareTrapezoidTwoBases( pCrvCompo, dDiam, nType, nBase, nSecondBase, bOK, pCrvTrap))
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( ! bOK) {
|
|
pCrvTrap->Clear() ;
|
|
nBase = -1 ;
|
|
nSecondBase = -1 ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// la curva a trapezio deve evere almeno 4 lati
|
|
if ( pCrvTrap->GetCurveCount() < 4 || nBase == -1 || nSecondBase == -1) {
|
|
pCrvTrap->Clear() ;
|
|
return true ;
|
|
}
|
|
|
|
// ricostruisco il frame del trapezio
|
|
Point3d ptS ; pCrvTrap->GetStartPoint( ptS) ;
|
|
Vector3d vtS ; pCrvTrap->GetStartDir( vtS) ;
|
|
if ( ! frTrap.Set( ptS, Z_AX, vtS) || ! frTrap.IsValid())
|
|
return false ;
|
|
|
|
// se parametro MaxOptSize non compatibile => non è ottimizzato
|
|
if ( PockParams.dMaxOptSize > EPS_SMALL && dPocketSize > PockParams.dMaxOptSize)
|
|
pCrvTrap->Clear() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
SpecialAdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const bool bEvenClosed,
|
|
const ICurveComposite* pCrvPocket, const PocketParams& PockParams)
|
|
{
|
|
// parametri
|
|
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
|
|
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
|
|
double dRad = 0.5 * dDiam + dOffsR ;
|
|
|
|
// calcolo gli offset dei lati obliqui
|
|
PtrOwner<ICurveLine> pLineS( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 0 : 3)->Clone())) ;
|
|
pLineS->SimpleOffset( - dRad) ;
|
|
pLineS->Invert() ;
|
|
PtrOwner<ICurveLine> pLineE( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 2 : 1)->Clone())) ;
|
|
pLineE->SimpleOffset( - dRad) ;
|
|
pLineE->Invert() ;
|
|
|
|
Point3d ptS, ptE ;
|
|
pLineS->GetEndPoint( ptS) ;
|
|
pLineE->GetStartPoint( ptE) ;
|
|
Vector3d vtS, vtE ;
|
|
pLineS->GetStartDir( vtS) ;
|
|
pLineE->GetStartDir( vtE) ;
|
|
|
|
PtrOwner<ICurveLine> pLineLink( CreateCurveLine()) ;
|
|
pLineLink->Set( ptS, ptE) ;
|
|
|
|
pMCrv->Clear() ;
|
|
if ( ! pMCrv->AddCurve( Release( pLineS)))
|
|
return false ;
|
|
|
|
// creo raccordi
|
|
bool bUseBiArcs = false ;
|
|
Vector3d vtDir, vtDir2 ;
|
|
pLineLink->GetStartDir( vtDir) ;
|
|
pCrvPocket->GetCurve( bEvenClosed ? 1 : 0)->GetStartDir( vtDir2) ;
|
|
|
|
if ( Dist( ptS, ptE) > 500 * EPS_SMALL && AreSameOrOppositeVectorApprox( vtDir, vtDir2)) {
|
|
|
|
Point3d ptCrv1, ptCrv2 ;
|
|
pLineLink->GetPointD1D2( 0.3, ICurve::FROM_MINUS, ptCrv1) ;
|
|
pLineLink->GetPointD1D2( 0.7, ICurve::FROM_MINUS, ptCrv2) ;
|
|
|
|
// primo raccordo
|
|
double dAng ;
|
|
vtS.GetAngleXY( X_AX, dAng) ;
|
|
PtrOwner<ICurve> pBiArc1( GetBiArc( ptS, - dAng, ptCrv1, bEvenClosed ? 90 : 0, 0.5)) ;
|
|
|
|
// secondo raccordo
|
|
vtE.Invert() ;
|
|
vtE.GetAngleXY( X_AX, dAng) ;
|
|
PtrOwner<ICurve> pBiArc2( GetBiArc( ptE, - dAng, ptCrv2, bEvenClosed ? - 90 : 180, 0.5)) ;
|
|
pBiArc2->Invert() ;
|
|
|
|
if ( ! IsNull( pBiArc1) && ! IsNull( pBiArc2)) {
|
|
bUseBiArcs = true ;
|
|
pMCrv->AddCurve( Release( pBiArc1)) ;
|
|
pMCrv->AddLine( ptCrv2) ;
|
|
pMCrv->AddCurve( Release( pBiArc2)) ;
|
|
}
|
|
}
|
|
|
|
// se non è stato possibile creare raccordo, unisco linearmente
|
|
if ( ! bUseBiArcs)
|
|
pMCrv->AddCurve( Release( pLineLink)) ;
|
|
|
|
if ( ! pMCrv->AddCurve( Release( pLineE)))
|
|
return false ;
|
|
|
|
// setto temp prop per ricordare curve aggiuntive per pulire angoli
|
|
pMCrv->SetCurveTempProp( 0, 1) ;
|
|
pMCrv->SetCurveTempProp( pMCrv->GetCurveCount() - 1, 1) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------
|
|
static bool
|
|
CalcTrapezoidSpiralXCoord( const ICurveComposite* pCrvPocket, int nBase, int nSecondBase,
|
|
bool bStart, double dYCoord, double& dXCoord, double dPocketSize,
|
|
const PocketParams& PockParams)
|
|
{
|
|
// parametri
|
|
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
|
|
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
|
|
double dRad = 0.5 * dDiam + dOffsR ;
|
|
|
|
// recupero la curva di interesse
|
|
int nCrvId = ( bStart ? nSecondBase : nBase) + 1 ;
|
|
int nProp = - 1 ;
|
|
if ( ! pCrvPocket->GetCurveTempProp( nCrvId, nProp))
|
|
return false ;
|
|
// se open
|
|
if ( nProp == 1) {
|
|
Point3d pt1, pt2 ;
|
|
pCrvPocket->GetCurve( nCrvId)->GetStartPoint( pt1) ;
|
|
pCrvPocket->GetCurve( nCrvId)->GetEndPoint( pt2) ;
|
|
if ( bStart)
|
|
dXCoord = min( pt1.x, pt2.x) ;
|
|
else
|
|
dXCoord = max( pt1.x, pt2.x) ;
|
|
}
|
|
// se closed
|
|
else {
|
|
// creo la curva destra/sinistra
|
|
int nLast = bStart ? pCrvPocket->GetCurveCount() : nSecondBase ;
|
|
PtrOwner<ICurveComposite> pCrvSide( ConvertCurveToComposite( pCrvPocket->CopyParamRange( nCrvId, nLast))) ;
|
|
if ( IsNull( pCrvSide))
|
|
return false ;
|
|
// Offsetto la curva
|
|
OffsetCurve OffsCrv ;
|
|
if ( ! OffsCrv.Make( pCrvSide, - dRad, ICurve::OFF_FILLET))
|
|
return false ;
|
|
if ( OffsCrv.GetCurveCount() == 0) {
|
|
// controllo se avevo una circonferenza
|
|
if ( pCrvSide->GetCurveCount() == 1 && pCrvSide->GetFirstCurve()->GetType() == CRV_ARC) {
|
|
Point3d ptS ; pCrvSide->GetStartPoint( ptS) ;
|
|
dXCoord = ptS.x ;
|
|
}
|
|
else {
|
|
Point3d ptS ; pCrvSide->GetStartPoint( ptS) ;
|
|
Point3d ptE ; pCrvSide->GetEndPoint( ptE) ;
|
|
dXCoord = bStart ? max( ptS.x, ptE.x) + dRad : min( ptS.x, ptE.x) - dRad ;
|
|
}
|
|
}
|
|
else if ( OffsCrv.GetCurveCount() == 1) {
|
|
// controllo se la curva interseca la linea di svuotatura a YCoord
|
|
PtrOwner<ICurve> pCrvOffs( OffsCrv.GetLongerCurve()) ;
|
|
if ( IsNull( pCrvOffs))
|
|
return false ;
|
|
PtrOwner<ICurveLine> pLineMid( CreateCurveLine()) ;
|
|
if ( IsNull( pLineMid))
|
|
return false ;
|
|
pLineMid->Set( Point3d( - 3000, dYCoord, 0), Point3d( 3000, dYCoord, 0)) ;
|
|
IntersCurveCurve intCC( *pLineMid, *pCrvOffs) ;
|
|
IntCrvCrvInfo ccClass ;
|
|
if ( intCC.GetIntersCount() != 0) {
|
|
// se ho almeno una intersezione
|
|
if ( intCC.GetIntCrvCrvInfo( 0, ccClass))
|
|
dXCoord = ccClass.IciA[0].ptI.x ;
|
|
else
|
|
return false ;
|
|
}
|
|
else {
|
|
// se non ho intersezioni...
|
|
// prendo il box della curva
|
|
BBox3d Box3d ;
|
|
pCrvSide->GetLocalBBox( Box3d) ;
|
|
// creo la linea limitie verticale
|
|
PtrOwner<ICurveLine> pCrvVertLine( CreateCurveLine()) ;
|
|
if ( IsNull( pCrvVertLine))
|
|
return false ;
|
|
if ( bStart)
|
|
pCrvVertLine->SetPDL( Box3d.GetMax() + 5 * TOL_TRAPEZOID * Y_AX, - 90 , 2 * dPocketSize) ;
|
|
else
|
|
pCrvVertLine->SetPDL( Box3d.GetMin() - 5 * TOL_TRAPEZOID * Y_AX, 90, 2 * dPocketSize) ;
|
|
// intersechiamo
|
|
IntersCurveCurve intCC2( *pCrvVertLine, *pCrvSide) ;
|
|
if ( intCC2.GetOverlaps()) {
|
|
if ( bStart)
|
|
dXCoord = Box3d.GetMax().x + dRad ;
|
|
else
|
|
dXCoord = Box3d.GetMin().x - dRad ;
|
|
}
|
|
else {
|
|
dXCoord = bStart ? -INFINITO : INFINITO ;
|
|
for ( int i = 0 ; i < intCC2.GetIntersCount() ; ++ i) {
|
|
IntCrvCrvInfo ccClass2 ;
|
|
if ( intCC2.GetIntCrvCrvInfo( i, ccClass2)) {
|
|
if ( bStart)
|
|
dXCoord = max( dXCoord, Box3d.GetMax().x + sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ;
|
|
else
|
|
dXCoord = min( dXCoord, Box3d.GetMin().x - sqrt( dRad * dRad - pow(( ccClass2.IciA[0].ptI.y - dYCoord), 2))) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
AdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const ICurveComposite* pCrvPocket,
|
|
const PocketParams& PockParams, bool bStart)
|
|
{
|
|
// parametri
|
|
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
|
|
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
|
|
double dRad = 0.5 * dDiam + dOffsR ;
|
|
|
|
PtrOwner<ICurveComposite> pCompo( CreateCurveComposite()) ;
|
|
if ( ! bStart)
|
|
pMCrv->Invert() ;
|
|
|
|
Point3d ptTmp ;
|
|
pMCrv->GetStartPoint( ptTmp) ;
|
|
double dYCoord = ptTmp.y ; // quota verticale del percorso di svuotatura
|
|
pCrvPocket->GetCurve( 2)->GetStartPoint( ptTmp) ;
|
|
double dPocketSize = ptTmp.y ;
|
|
|
|
int nCrvId = ( bStart ? 3 : 1) ;
|
|
PtrOwner<ICurveLine> pLine( GetCurveLine( pCrvPocket->GetCurve( nCrvId)->Clone())) ;
|
|
pLine->SimpleOffset( - dRad) ;
|
|
|
|
Point3d ptP1, ptP2 ;
|
|
pLine->GetStartPoint( ptP1) ;
|
|
pLine->GetEndPoint( ptP2) ;
|
|
if ( ! bStart)
|
|
swap( ptP1, ptP2) ;
|
|
|
|
int nProp2 ;
|
|
// lato opposto a quello di riferimento aperto
|
|
if ( pCrvPocket->GetCurveTempProp( 2, nProp2) && nProp2 != 0) {
|
|
|
|
// caso 1 : pLine ha un estremo sopra e uno sotto il percorso di svuotatura pMCrv
|
|
if ( ptP2.y < dYCoord && dYCoord < ptP1.y) {
|
|
|
|
// creo tratto da ptP2 a ptP1
|
|
pCompo->AddPoint( ptP2) ;
|
|
pCompo->AddLine( ptP1) ;
|
|
|
|
// trovo il punto di pMCrv da cui ripartire per non lasciare aree residue
|
|
pLine->SimpleOffset( - 0.5 * dDiam) ;
|
|
pLine->ExtendStartByLen( EPS_SMALL) ;
|
|
pLine->ExtendEndByLen( EPS_SMALL) ;
|
|
IntersCurveCurve intCC( *pLine, *pCrvPocket) ;
|
|
if ( intCC.GetIntersCount() == 0)
|
|
return false ;
|
|
IntCrvCrvInfo aInfo ;
|
|
intCC.GetIntCrvCrvInfo( 0, aInfo) ;
|
|
double dDeltaX = sqrt( max( 0., dDiam * dDiam / 4 - dYCoord * dYCoord)) ;
|
|
if ( ! bStart)
|
|
dDeltaX *= -1 ;
|
|
Point3d ptCrv( aInfo.IciA[0].ptI.x + dDeltaX, dYCoord) ;
|
|
|
|
double dPar = 0 ;
|
|
if ( ! pMCrv->GetParamAtPoint( ptCrv, dPar)) {
|
|
dPar = 0.5 ;
|
|
pMCrv->GetPointD1D2( dPar, ICurve::FROM_MINUS, ptCrv) ;
|
|
}
|
|
if ( ! pMCrv->TrimStartAtParam( dPar))
|
|
pMCrv->Clear() ;
|
|
|
|
if ( ptP1.y > dPocketSize) {
|
|
// se ptP1 è esterno alla svuotatura scambio i punti in modo da usare ptP2 per il biarco ( così da non farlo fuoriuscire troppo)
|
|
swap( ptP1, ptP2) ;
|
|
pCompo->Invert() ;
|
|
}
|
|
|
|
// creo biarco fra ptP1 e ptCrv
|
|
Vector3d vtDir ;
|
|
pCompo->GetStartDir( vtDir) ;
|
|
double dAng ;
|
|
vtDir.GetAngleXY( X_AX, dAng) ;
|
|
PtrOwner<ICurve> pBiArc( GetBiArc( ptP1, - dAng, ptCrv, bStart ? 0 : 180, 0.8)) ;
|
|
|
|
bool bUseBiArc = false ;
|
|
if ( ! IsNull( pBiArc)) {
|
|
// verifico che con il biarco non si oltrepassi il lato obliquo chiuso della svuotatura
|
|
IntersCurveCurve intCC2( *pBiArc, *pCompo) ;
|
|
if ( intCC2.GetIntersCount() == 1)
|
|
bUseBiArc = true ;
|
|
}
|
|
if ( bUseBiArc)
|
|
pCompo->AddCurve( Release( pBiArc)) ;
|
|
else {
|
|
double dParLine = ( dYCoord - ptP2.y) / ( ptP1.y - ptP2.y) ;
|
|
Point3d ptOnLine = Media( ptP2, ptP1, dParLine) ;
|
|
pCompo->AddLine( ptOnLine) ;
|
|
pCompo->AddLine( ptCrv) ;
|
|
}
|
|
}
|
|
|
|
// caso 2 : pLine è completamente sopra/sotto la linea di svuotatura
|
|
else {
|
|
// se è sopra modifiche per ricondurmi al caso in cui è sotto
|
|
if ( ptP2.y > dYCoord) {
|
|
pLine->Invert() ;
|
|
swap( ptP1, ptP2) ;
|
|
}
|
|
|
|
// trovo l'intersezione fra il prolungamento della linea e pMCrv
|
|
if ( bStart)
|
|
pLine->ExtendStartByLen( 1000) ;
|
|
else
|
|
pLine->ExtendEndByLen( 1000) ;
|
|
IntersCurveCurve intCC ( *pLine, *pMCrv) ;
|
|
if ( intCC.GetIntersCount() == 0)
|
|
return false ;
|
|
IntCrvCrvInfo aInfo ;
|
|
intCC.GetIntCrvCrvInfo( 0, aInfo) ;
|
|
Point3d ptInt ;
|
|
ptInt = aInfo.IciA[0].ptI ;
|
|
double dPar = 0 ;
|
|
pMCrv->GetParamAtPoint( ptInt, dPar) ;
|
|
if ( ! pMCrv->TrimStartAtParam( dPar))
|
|
pMCrv->Clear() ;
|
|
|
|
pCompo->AddPoint( ptP2) ;
|
|
pCompo->AddLine( ptInt) ;
|
|
}
|
|
}
|
|
|
|
// se il lato opposto a quello di riferimento � chiuso bisogna distinguere i casi
|
|
else {
|
|
bool bStartPnt = false ;
|
|
if ( ptP2.y < dYCoord) {
|
|
bStartPnt = pCompo->AddPoint( ptP2) ;
|
|
}
|
|
else {
|
|
Vector3d vtDir ;
|
|
pLine->GetStartDir( vtDir) ;
|
|
if ( bStart)
|
|
vtDir.Invert() ;
|
|
Point3d ptP2N = ptP2 - dDiam / 2 * abs( vtDir.x / vtDir.y) * vtDir ;
|
|
if ( ptP2N.y < dYCoord)
|
|
bStartPnt = pCompo->AddPoint( ptP2N) ;
|
|
}
|
|
if ( bStartPnt) {
|
|
Point3d ptCrv ;
|
|
pMCrv->GetStartPoint( ptCrv) ;
|
|
pCompo->AddLine( ptCrv) ;
|
|
}
|
|
}
|
|
|
|
// setto temp prop per ricordare che è curva aggiuntiva per pulire angoli
|
|
for ( int i = 0 ; i < pCompo->GetCurveCount() ; i++)
|
|
pCompo->SetCurveTempProp( i, 1) ;
|
|
|
|
pMCrv->AddCurve( Release( pCompo), false) ;
|
|
if ( ! bStart)
|
|
pMCrv->Invert() ; // ripristino la direzione originaria
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double dPocketSize, int nBase,
|
|
int nSecondBase, const PocketParams& PockParams, ICurveComposite* pMCrv,
|
|
bool& bOptimizedTrap)
|
|
{
|
|
// parametri
|
|
double dDiam = 2 * PockParams.dRad ;
|
|
double dOffsR = PockParams.dRadialOffset ;
|
|
|
|
bOptimizedTrap = false ;
|
|
Vector3d vtExtr ; pCrvPocket->GetExtrusion( vtExtr) ;
|
|
|
|
// recupero le temp prop
|
|
INTVECTOR vnProp( 4, 0) ;
|
|
if ( pCrvPocket->GetCurveCount() == 4) {
|
|
for ( int i = 0 ; i < 4 ; i ++)
|
|
pCrvPocket->GetCurveTempProp( i, vnProp[i]) ;
|
|
}
|
|
else {
|
|
vnProp[1] = pCrvPocket->GetCurve( 1)->GetTempProp() ;
|
|
vnProp[3] = pCrvPocket->GetCurve( nSecondBase + 1)->GetTempProp() ;
|
|
}
|
|
|
|
// passo in un sistema di riferimento locale avente asse X allineato con uno dei due lati paralleli
|
|
// (possibilmente aperto) e centro nel punto iniziale del lato
|
|
pCrvPocket->ToLoc( frTrap) ;
|
|
|
|
// calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso)
|
|
double dLen0, dLen1, dLen2, dLen3, dMaxLarg = 0. ;
|
|
pCrvPocket->GetCurve( nBase)->GetLength( dLen0) ;
|
|
pCrvPocket->GetCurve( nSecondBase)->GetLength( dLen2) ;
|
|
bool bRealTrap = pCrvPocket->GetCurveCount() == 4 ;
|
|
for ( int i = 0 ; i < 4 && bRealTrap ; ++ i)
|
|
bRealTrap = pCrvPocket->GetCurve( i)->GetType() == CRV_LINE ;
|
|
if ( bRealTrap) {
|
|
pCrvPocket->GetCurve( nBase + 1)->GetLength( dLen1) ;
|
|
pCrvPocket->GetCurve( nSecondBase + 1)->GetLength( dLen3) ;
|
|
// calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso)
|
|
Point3d ptOrig ; pCrvPocket->GetCurve( 1)->GetStartPoint( ptOrig) ;
|
|
Vector3d vtX ; pCrvPocket->GetCurve( 1)->GetStartDir( vtX) ;
|
|
Frame3d frDim ; frDim.Set( ptOrig, Z_AX, vtX) ; frDim.Invert() ;
|
|
BBox3d b3Dim ; pCrvPocket->GetBBox( frDim, b3Dim, BBF_EXACT) ;
|
|
dMaxLarg = ( vnProp[0] != 0 ? b3Dim.GetDimY() : dPocketSize) ;
|
|
}
|
|
|
|
|
|
// calcolo percorso di svuotatura
|
|
// se lati obliqui sono entrambi chiusi e dimensione svuotatura è maggiore di diametro fresa e minore del doppio gestione speciale
|
|
if (( bRealTrap && dMaxLarg > PockParams.dRad * 2 + 10 * EPS_SMALL) &&
|
|
((( vnProp[0] != 0 && vnProp[2] != 0) && ( vnProp[3] == 0 && vnProp[1] == 0) && ( max( dLen0, dLen2) < 2 * dDiam + EPS_SMALL)) ||
|
|
(( vnProp[1] != 0 && vnProp[3] != 0) && ( vnProp[0] == 0 && vnProp[2] == 0) && ( max( dLen1, dLen3) < 2 * dDiam + EPS_SMALL)))) {
|
|
if ( ! SpecialAdjustTrapezoidSpiralForAngles( pMCrv, vnProp[0] == 0, pCrvPocket, PockParams)) {
|
|
pMCrv->Clear() ;
|
|
return false ;
|
|
}
|
|
}
|
|
else {
|
|
// trovo la quota Y per centro del Tool
|
|
double dYCoord ;
|
|
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) // se base principale chiusa
|
|
dYCoord = 0.5 * dDiam + dOffsR ;
|
|
else if ( pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 0) // se base principale aperta e secondaria chiusa
|
|
dYCoord = dPocketSize - 0.5 * dDiam - dOffsR ;
|
|
else // se entrambi i lati paralleli sono aperti mi posiziono a metà della svuotatura
|
|
dYCoord = 0.5 * dPocketSize ;
|
|
|
|
double dXCoordStart = -INFINITO ;
|
|
double dXCoordEnd = INFINITO ;
|
|
if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, true, dYCoord, dXCoordStart,
|
|
dPocketSize, PockParams) || dXCoordStart < - INFINITO + 1)
|
|
return false ;
|
|
if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, false, dYCoord, dXCoordEnd,
|
|
dPocketSize, PockParams) || dXCoordEnd > INFINITO - 1)
|
|
return false ;
|
|
if ( dXCoordStart > dXCoordEnd + 500 * EPS_SMALL)
|
|
return false ;
|
|
Point3d ptStart( dXCoordStart, dYCoord) ;
|
|
Point3d ptEnd( dXCoordEnd, dYCoord) ;
|
|
|
|
if ( bRealTrap && AreSamePointEpsilon( ptStart, ptEnd, 500 * EPS_SMALL) && ( vnProp[0] != 0 || vnProp[2] != 0)) {
|
|
Vector3d vtDir1, vtDir3 ;
|
|
pCrvPocket->GetCurve( 1)->GetStartDir( vtDir1) ;
|
|
pCrvPocket->GetCurve( 3)->GetStartDir( vtDir3) ;
|
|
// gestisco il caso speciale di un parallelogramma in cui anche l'altra dimensione della svuotatura è pari al diametro utensile
|
|
if ( AreOppositeVectorApprox( vtDir1, vtDir3)) {
|
|
PtrOwner<ICurveLine> pLine1( GetCurveLine( pCrvPocket->GetCurve( 1)->Clone())) ;
|
|
PtrOwner<ICurveLine> pLine3( GetCurveLine( pCrvPocket->GetCurve( 3)->Clone())) ;
|
|
if ( IsNull( pLine1) || IsNull( pLine3))
|
|
return false ;
|
|
if ( ! pLine1->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset) ||
|
|
! pLine3->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset))
|
|
return false ;
|
|
|
|
Point3d ptS, ptE ;
|
|
if ( vtDir3 * X_AX > EPS_SMALL) {
|
|
pLine1->GetStartPoint( ptS) ;
|
|
pLine3->GetStartPoint( ptE) ;
|
|
}
|
|
else {
|
|
pLine1->GetEndPoint( ptE) ;
|
|
pLine3->GetEndPoint( ptS) ;
|
|
}
|
|
|
|
if ( vnProp[0] != 0) {
|
|
pMCrv->AddPoint( ptS) ;
|
|
if ( vnProp[2] != 0)
|
|
pMCrv->AddLine( ptE) ;
|
|
else
|
|
pMCrv->AddLine( ptStart) ;
|
|
}
|
|
else {
|
|
pMCrv->AddPoint( ptE) ;
|
|
if ( vnProp[0] != 0)
|
|
pMCrv->AddLine( ptS) ;
|
|
else
|
|
pMCrv->AddLine( ptStart) ;
|
|
pMCrv->Invert() ;
|
|
}
|
|
|
|
pMCrv->SetCurveTempProp( 0, 1) ;
|
|
}
|
|
}
|
|
else {
|
|
if ( ! pMCrv->AddPoint( ptStart))
|
|
return true ;
|
|
if ( ! pMCrv->AddLine( ptEnd))
|
|
return true ;
|
|
|
|
// aggiustamenti al percorso per rimuovere materiale residuo negli angoli
|
|
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 1 ||
|
|
pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 1) {
|
|
|
|
Frame3d frOpen ;
|
|
bool bSwitch = false ;
|
|
// Base principale chiusa e base Secondaria aperta
|
|
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) {
|
|
pCrvPocket->ChangeStartPoint( nSecondBase) ;
|
|
// oriento il Frame ( ho sempre l'origine nell'estremo superiore del lato aperto)
|
|
Vector3d vtDir ; pCrvPocket->GetStartDir( vtDir) ;
|
|
Point3d ptORIG ;
|
|
if ( vtDir.y > 0)
|
|
pCrvPocket->GetCurve( nBase)->GetEndPoint( ptORIG) ;
|
|
else
|
|
pCrvPocket->GetCurve( nBase)->GetStartPoint( ptORIG) ;
|
|
frOpen.Set( ptORIG, Z_AX, -X_AX) ;
|
|
if ( ! frOpen.IsValid())
|
|
return false ;
|
|
pCrvPocket->ToLoc( frOpen) ;
|
|
pMCrv->ToLoc( frOpen) ;
|
|
pMCrv->Invert() ;
|
|
bSwitch = true ;
|
|
}
|
|
|
|
if ( pCrvPocket->GetCurve( 3)->GetTempProp( 0) == 0 &&
|
|
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, true)) {
|
|
pMCrv->Clear() ;
|
|
return false ;
|
|
}
|
|
if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == 0 &&
|
|
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, false)) {
|
|
pMCrv->Clear() ;
|
|
return false ;
|
|
}
|
|
|
|
if ( bSwitch) {
|
|
pCrvPocket->ToGlob( frOpen) ;
|
|
pMCrv->ToGlob( frOpen) ;
|
|
pMCrv->Invert() ;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
return true ;
|
|
|
|
pMCrv->ToGlob( frTrap) ;
|
|
|
|
if ( ! PockParams.bInvert) {
|
|
pMCrv->Invert() ;
|
|
// inverto le proprietà in modo che nProp3 sia sempre legata al punto iniziale e nProp1 a quello finale
|
|
swap( vnProp[1], vnProp[3]) ;
|
|
}
|
|
// segno i lati aperti come temp prop della curva
|
|
int nOpenEdges = vnProp[0] + vnProp[1] * 2 + vnProp[2] * 4 + vnProp[3] * 8 ;
|
|
pMCrv->SetTempProp( nOpenEdges, 0) ;
|
|
pMCrv->SetExtrusion( vtExtr) ;
|
|
|
|
bOptimizedTrap = true ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetSpiralOptimizedCurves( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParam,
|
|
ICurveComposite* pCrvRes)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
pCrvRes->Clear() ;
|
|
|
|
// ricavo la curva di bordo del chunk corrente
|
|
PtrOwner<ICurveComposite> pCrvBorder( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid())
|
|
return false ;
|
|
pCrvBorder->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
pCrvBorder->SetExtrusion( pSfrChunk->GetNormVersor()) ;
|
|
|
|
/* SPIRALE EUCLIDEA
|
|
- E' richiesto che la curva di bordo sia una circonferenza tutta OPEN o tutta CLOSED
|
|
*/
|
|
bool bOkSpiral = true ;
|
|
int nTempPropRef = pCrvBorder->GetCurve( 0)->GetTempProp( 0) ;
|
|
for ( int nU = 1 ; nU < pCrvBorder->GetCurveCount() && bOkSpiral ; ++ nU)
|
|
bOkSpiral = ( pCrvBorder->GetCurve( nU)->GetTempProp( 0) == nTempPropRef) ;
|
|
if ( bOkSpiral) {
|
|
// controllo che sia una circonferenza
|
|
Point3d ptCen ; double dRad ;
|
|
if ( ! OptimizedSpiralCircle( pCrvBorder, 50 * EPS_SMALL, dRad, ptCen, bOkSpiral))
|
|
return false ;
|
|
// se è una circonferenza
|
|
if ( bOkSpiral) {
|
|
double dOffs = PockParam.dRad + PockParam.dRadialOffset ;
|
|
// se curva tutta Open, allora ingrandisco il raggio
|
|
if ( nTempPropRef == TEMP_PROP_OPEN_EDGE)
|
|
dRad += 1.05 * PockParam.dRad + PockParam.dRadialOffset ;
|
|
// se curva chiusa, controllo che il raggio sia compatibile con il primo Offset
|
|
else
|
|
bOkSpiral = ( dRad - dOffs > 10 * EPS_SMALL) ;
|
|
if ( bOkSpiral) {
|
|
double dIntRad = 0 ;
|
|
bool bOkSpiral = CalcCircleSpiral( ptCen, pSfrChunk->GetNormVersor(), dRad - dOffs, dIntRad, PockParam, pCrvRes) ;
|
|
if ( bOkSpiral && pCrvRes->IsValid() && pCrvRes->GetCurveCount() > 0) {
|
|
// se curva di bordo OPEN, imposto i parametri per LeadIn
|
|
if ( nTempPropRef == TEMP_PROP_OPEN_EDGE) {
|
|
Vector3d vtMidOut ;
|
|
pCrvRes->GetStartDir( vtMidOut) ;
|
|
vtMidOut.Rotate( pSfrChunk->GetNormVersor(), PockParam.bInvert ? ANG_RIGHT : - ANG_RIGHT) ;
|
|
bool bIsExtended ;
|
|
ExtendPath( pCrvRes, pSfrChunk, PockParam, vtMidOut, false, bIsExtended) ;
|
|
}
|
|
return true ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* TRAPEZI
|
|
- E' richiesto che una dimensione del box della curva sia compatibile con il primo Offset, il
|
|
quale sarebbe una singola curva aperta
|
|
*/
|
|
PtrOwner<ICurveComposite> pCrvTrap( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvTrap))
|
|
return false ;
|
|
Frame3d frTrap ;
|
|
double dPocketSize ;
|
|
int nBase, nSecondBase ;
|
|
bool bOkTrap = ( GetTrapezoidFromShape( pCrvBorder, pCrvTrap, frTrap, PockParam, dPocketSize, nBase, nSecondBase)) ;
|
|
if ( bOkTrap && pCrvTrap->IsValid()) {
|
|
pCrvTrap->SetExtrusion( Z_AX) ;
|
|
CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParam, pCrvRes, bOkTrap) ;
|
|
if ( bOkTrap) {
|
|
// calcolo eventuali uscite e ingressi
|
|
if ( pCrvRes->GetTempProp( 0) > 0) {
|
|
Vector3d vtRef ; pCrvRes->GetStartDir( vtRef) ;
|
|
vtRef.Invert() ;
|
|
bool bIsStartExtended = false ;
|
|
if ( ! ExtendPath( pCrvRes, pSfrChunk, PockParam, vtRef, false, bIsStartExtended))
|
|
return false ;
|
|
pCrvRes->GetEndDir( vtRef) ;
|
|
bool bIsEndExtended = false ;
|
|
if ( ! ExtendPath( pCrvRes, pSfrChunk, PockParam, vtRef, true, bIsEndExtended))
|
|
return false ;
|
|
if ( bIsEndExtended && ! bIsStartExtended)
|
|
pCrvRes->Invert() ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPocketingOptimizedCurves( ISurfFlatRegion* pSfr, const PocketParams& PockParam,
|
|
ICRVCOMPOPOVECTOR& vCrvOptCurves)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
vCrvOptCurves.clear() ;
|
|
|
|
// se non sono richiesti i casi ottimizzati esco
|
|
if ( PockParam.bAvoidOpt)
|
|
return true ;
|
|
|
|
// scorro i Chunk della superficie
|
|
int nChunks = pSfr->GetChunkCount() ;
|
|
int nCurrChunk = 0 ;
|
|
for ( int i = 0 ; i < nChunks ; ++ i) {
|
|
|
|
// recupero il Chunk corrente
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( nCurrChunk)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
|
|
// se il Chunk ha isole, non ho casi ottimizzati
|
|
if ( pSfrChunk->GetLoopCount( 0) > 1) {
|
|
++ nCurrChunk ;
|
|
continue ;
|
|
}
|
|
|
|
// ricavo le curve ottimizzate a seconda della lavorazione richiesta
|
|
if ( PockParam.nType == POCKET_SPIRALIN || PockParam.nType == POCKET_SPIRALOUT) {
|
|
// curva da resituire
|
|
PtrOwner<ICurveComposite> pCrvOptSpiral( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvOptSpiral) ||
|
|
! GetSpiralOptimizedCurves( pSfrChunk, PockParam, pCrvOptSpiral))
|
|
return false ;
|
|
// se ho ricavato una curva ottimizzata
|
|
if ( ! IsNull( pCrvOptSpiral) && pCrvOptSpiral->IsValid() && pCrvOptSpiral->GetCurveCount() > 0) {
|
|
vCrvOptCurves.emplace_back( Release( pCrvOptSpiral)) ;
|
|
pSfr->EraseChunk( nCurrChunk) ;
|
|
}
|
|
else
|
|
++ nCurrChunk ;
|
|
}
|
|
// else if ( PockParam.nType == POCKET_ZIGZAG)
|
|
// ;
|
|
// else if ( PockParam.nType == POCKET_ONEWAY)
|
|
// ;
|
|
// else if ( PockParam.nType == POCKET_CONFORMAL_ONEWAY || PockParam.nType == POCKET_CONFORMAL_ZIGZAG)
|
|
// ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// ------------- SCELTA DEL PUNTO INIZIALE -----------------------------------
|
|
// ***************************************************************************
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetParamOnOpenCurve( const ICurveComposite* pCompo, const PocketParams& PockParams, bool& bOutStart,
|
|
Point3d& ptStart, Vector3d& vtOut)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCompo == nullptr || ! pCompo->IsValid())
|
|
return false ;
|
|
|
|
// verifico se tutti i lati sono aperti
|
|
bool bAllOpen = true ;
|
|
const ICurve* pMyCrv = pCompo->GetFirstCurve() ;
|
|
while ( pMyCrv != nullptr) {
|
|
if ( pMyCrv->GetTempProp() != TEMP_PROP_OPEN_EDGE) {
|
|
bAllOpen = false ;
|
|
break ;
|
|
}
|
|
pMyCrv = pCompo->GetNextCurve() ;
|
|
}
|
|
|
|
// richiedo lunghezza superiore a diametro utensile più doppio offset radiale
|
|
double dRefLen = ( bAllOpen ? 0. : ( 2 * PockParams.dRad) + 2 * PockParams.dRadialOffset - EPS_SMALL) ;
|
|
double dMaxLen = dRefLen ;
|
|
// ciclo sulle singole curve
|
|
bool bFound = false ;
|
|
const ICurve* pPrevCrv = pCompo->GetLastCurve() ;
|
|
double dLenPrev = 0. ;
|
|
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE)
|
|
pPrevCrv->GetLength( dLenPrev) ;
|
|
const ICurve* pCrv = pCompo->GetFirstCurve() ;
|
|
while ( pCrv != nullptr) {
|
|
// analizzo la curva successiva
|
|
const ICurve* pNextCrv = pCompo->GetNextCurve() ;
|
|
bool bNextOk = ( pNextCrv != nullptr) ;
|
|
if ( ! bNextOk)
|
|
pNextCrv = pCompo->GetFirstCurve() ;
|
|
double dLenNext = 0. ;
|
|
if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE)
|
|
pNextCrv->GetLength( dLenNext) ;
|
|
// verifico la curva corrente
|
|
if ( pCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
|
|
// contributo dalle entità adiacenti ( se non tutte aperte)
|
|
double dLenAgg = 0. ;
|
|
if ( ! bAllOpen) {
|
|
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
|
|
Vector3d vtPrevEnd ; pPrevCrv->GetEndDir( vtPrevEnd) ;
|
|
Vector3d vtStart ; pCrv->GetStartDir( vtStart) ;
|
|
dLenAgg += max( 0.4, vtPrevEnd * vtStart) * dLenPrev ;
|
|
}
|
|
if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
|
|
Vector3d vtEnd ; pCrv->GetEndDir( vtEnd) ;
|
|
Vector3d vtNextStart ; pNextCrv->GetStartDir( vtNextStart) ;
|
|
dLenAgg += max( 0.4, vtEnd * vtNextStart) * dLenNext ;
|
|
}
|
|
}
|
|
// entità corrente
|
|
double dLen = 0 ;
|
|
if ( pCrv->GetLength( dLen)) {
|
|
const double LEN_TOL = 1 ;
|
|
// se di lunghezza praticamente uguale
|
|
if ( bFound && dLen + dLenAgg > dRefLen && abs( dLen + dLenAgg - dMaxLen) < LEN_TOL) {
|
|
Point3d ptTest ;
|
|
pCrv->GetMidPoint( ptTest) ;
|
|
if ( ( PockParams.bAboveHead && ptTest.z > ptStart.z + 100 * EPS_SMALL) ||
|
|
( ! PockParams.bAboveHead && ptTest.z < ptStart.z - 100 * EPS_SMALL) ||
|
|
( abs( ptTest.z - ptStart.z) < 100 * EPS_SMALL && ptTest.y < ptStart.y - 100 * EPS_SMALL)) {
|
|
dMaxLen = max( dMaxLen, dLen + dLenAgg) ;
|
|
ptStart = ptTest ;
|
|
// versore ortogonale verso l'esterno
|
|
pCrv->GetMidDir( vtOut) ;
|
|
vtOut.Rotate( Z_AX, 0, -1) ;
|
|
}
|
|
}
|
|
// se più lunga ( o non già trovata)
|
|
else if ( dLen + dLenAgg > dMaxLen || ! bFound) {
|
|
dMaxLen = dLen + dLenAgg ;
|
|
if ( pCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart, &vtOut)) {
|
|
// versore ortogonale verso l'esterno
|
|
vtOut.Normalize() ;
|
|
vtOut.Rotate( Z_AX, 0, -1) ;
|
|
bFound = true ;
|
|
}
|
|
}
|
|
dLenPrev = dLen ;
|
|
}
|
|
}
|
|
else
|
|
dLenPrev = 0 ;
|
|
// vado alla successiva
|
|
pPrevCrv = pCrv ;
|
|
pCrv = ( bNextOk ? pNextCrv : nullptr) ;
|
|
}
|
|
|
|
return bFound ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPtStartOnOpenEdgeByOrigCurve( const ICurveComposite* pCrvOrig, const PocketParams& PockParams,
|
|
const ICurveComposite* pCrvCompo, Point3d& ptStart, Vector3d& vtMidOut,
|
|
bool& bMidOut)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvOrig == nullptr || ! pCrvOrig->IsValid() ||
|
|
pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
bMidOut = false ;
|
|
|
|
// cerco il lato aperto più lungo ( sufficientemente lungo)
|
|
double dLenRef = ( 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) - 50 * EPS_SMALL ;
|
|
for ( int i = 0 ; i < pCrvOrig->GetCurveCount() ; ++ i) {
|
|
// escludo le sottocurve chiuse
|
|
if ( pCrvOrig->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
|
|
continue ;
|
|
// recupero la curva i-esima aperta dalla curva originale
|
|
const ICurve* pCrvOpen = pCrvOrig->GetCurve( i) ;
|
|
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
|
|
return false ;
|
|
// ricavo la lunghezza di tale curva
|
|
double dLen = 0. ;
|
|
pCrvOpen->GetLength( dLen) ;
|
|
// se lunghezza accettabile o maggiore della massima trovata
|
|
if ( dLen > dLenRef) {
|
|
Point3d ptSTmp ;
|
|
Vector3d vtMidOutTmp ;
|
|
// ricavo il punto medio e il vettore tangente ad esso associato
|
|
if ( pCrvOpen->GetPointD1D2( 0.5, ICurve::FROM_MINUS, ptSTmp, &vtMidOutTmp)) {
|
|
// cerco la sottocurva più vicina a ptSTmp
|
|
int nFlag ;
|
|
double dMyPar ;
|
|
if ( DistPointCurve( ptSTmp, *pCrvCompo).GetParamAtMinDistPoint( 0, dMyPar, nFlag)) {
|
|
int nCrv = int( floor( dMyPar)) ;
|
|
// recupero tale sottocurva e controllo che anche essa sia OPEN
|
|
const ICurve* pMyCrv = pCrvCompo->GetCurve( nCrv) ;
|
|
if ( pMyCrv != nullptr && pMyCrv->IsValid() && pMyCrv->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
// recupero ptStart
|
|
pMyCrv->GetMidPoint( ptStart) ;
|
|
// versore d'uscita
|
|
vtMidOutTmp.Normalize() ;
|
|
vtMidOutTmp.Rotate( Z_AX, - ANG_RIGHT) ;
|
|
vtMidOut = vtMidOutTmp ;
|
|
// flag per possibile entrata da fuori
|
|
bMidOut = true ;
|
|
// aggiornamento della lunghezza di riferimento
|
|
dLenRef = dLen ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPtStartOnOpenEdgeByPtRef( const Point3d& ptRef, const PocketParams& PockParams,
|
|
const ICurveComposite* pCrvCompo, const ICurveComposite* pCrvOrig,
|
|
Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut)
|
|
{
|
|
// controllo dei parametri
|
|
if ( ! ptRef.IsValid() || pCrvCompo == nullptr || ! pCrvCompo->IsValid() ||
|
|
! ptRef.IsValid())
|
|
return false ;
|
|
bMidOut = false ;
|
|
|
|
// lunghezza minima di riferimento per un lato aperto valido
|
|
double dLenRef = ( 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) - 50 * EPS_SMALL ;
|
|
|
|
// cerco la curva OPEN a minima distanza da ptRef, sufficientemente lunga
|
|
int nStartCrv = -1 ;
|
|
double dMinDist = INFINITO ;
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
// considero solo le sottocurve OPEN
|
|
if ( pCrvCompo->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
// sufficientemente lunghe
|
|
double dLen ;
|
|
if ( pCrvCompo->GetCurve( i)->GetLength( dLen) && dLen > dLenRef) {
|
|
DistPointCurve DPC( ptRef, *pCrvCompo->GetCurve( i)) ;
|
|
double dCurrDist = INFINITO ;
|
|
// sufficiententemente vicine a ptRef
|
|
if ( DPC.GetSqDist( dCurrDist) && dCurrDist < dMinDist) {
|
|
dMinDist = dCurrDist ;
|
|
nStartCrv = i ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// se non ho trovato nulla, allora cerco un punto iniziale valido su una sottocurva OPEN
|
|
if ( nStartCrv == -1 && pCrvOrig != nullptr && pCrvOrig->IsValid()) {
|
|
return GetPtStartOnOpenEdgeByOrigCurve( pCrvOrig, PockParams, pCrvCompo, ptStart, vtMidOut, bMidOut) ;
|
|
}
|
|
// se ho trovato la curva più vicina, cerco un punto iniziale valido
|
|
else {
|
|
const ICurve* pMyCrv = pCrvCompo->GetCurve( nStartCrv) ;
|
|
if ( pMyCrv != nullptr && pMyCrv->IsValid()) {
|
|
// ricavo ptStart e vettore tangente
|
|
if ( pMyCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart, &vtMidOut)) {
|
|
// versore ortogonale verso l'esterno
|
|
vtMidOut.Normalize() ;
|
|
vtMidOut.Rotate( Z_AX, 0, -1) ;
|
|
// flag per possibile entrata da fuori
|
|
bMidOut = true ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPtStartOnHomogeousEdges( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
|
|
int nType, const Point3d& ptRef, bool& bMidOut, Point3d& ptStart,
|
|
Vector3d& vtMidOut)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
bMidOut = false ;
|
|
|
|
// analizzo i sottotratti omogenei
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
PtrOwner<ICurveComposite> pCrvCompoClone( CloneCurveComposite( pCrvCompo)) ;
|
|
if ( IsNull( pCrvCompoClone) || ! pCrvCompoClone->IsValid())
|
|
return false ;
|
|
GetHomogeneousParts( pCrvCompoClone, PockParams, vpCrvs) ;
|
|
|
|
// se ho un solo tratto omogeneo e coerente al tipo scelto ...
|
|
if ( int( vpCrvs.size()) == 1 && vpCrvs[0]->GetTempProp( 0) == nType) {
|
|
// ... e non ho un punto di riferimento valido
|
|
if ( ! ptRef.IsValid()) {
|
|
// il punto iniziale è già definito
|
|
pCrvCompoClone->GetStartPoint( ptStart) ;
|
|
}
|
|
// ho un punto di riferimento valido
|
|
else {
|
|
// cerco il punto più vicino
|
|
DistPointCurve DistPtCrv( ptRef, *pCrvCompoClone) ;
|
|
int nFlag ;
|
|
if ( ! DistPtCrv.GetMinDistPoint( 0, ptStart, nFlag))
|
|
return false ;
|
|
}
|
|
bMidOut = ( nType == TEMP_PROP_OPEN_EDGE) ;
|
|
return true ;
|
|
}
|
|
|
|
// scorro i tratti omogenei e cerco il tratto del tipo definito più lungo
|
|
int nMaxInd = -1 ;
|
|
double dMaxLen = 0. ;
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
// considero solo i tratti aperti
|
|
if ( vpCrvs[i]->GetTempProp( 0) == nType) {
|
|
// calcolo la lunghezza del tratto aperto
|
|
double dLen ;
|
|
vpCrvs[i]->GetLength( dLen) ;
|
|
if ( dLen > dMaxLen) {
|
|
dMaxLen = dLen ;
|
|
nMaxInd = i ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// recupero il tratto del tipo definito più lungo
|
|
if ( nMaxInd != -1) {
|
|
// recupero il parametro alla sua metà
|
|
double dParIn ;
|
|
vpCrvs[nMaxInd]->GetParamAtLength( 0.5 * dMaxLen, dParIn) ;
|
|
// se tratto OPEN
|
|
if ( nType == TEMP_PROP_OPEN_EDGE) {
|
|
bMidOut = true ;
|
|
vpCrvs[nMaxInd]->GetPointD1D2( dParIn, ICurve::FROM_MINUS, ptStart, &vtMidOut) ;
|
|
vtMidOut.Normalize() ;
|
|
vtMidOut.Rotate( Z_AX, 0, -1) ;
|
|
}
|
|
// se tratto CLOSED
|
|
else
|
|
vpCrvs[nMaxInd]->GetPointD1D2( dParIn, ICurve::FROM_MINUS, ptStart) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPtStartOnGenericEdge( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
|
|
Point3d& ptStart)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
pCrvCompo->GetStartPoint( ptStart) ;
|
|
|
|
// scorro tutte le curve
|
|
double dLenRef = 0. ;
|
|
bool bAllShort = true ; // flag per curve tutte piccole
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
// ricavo la curva i-esima dalla curva originale
|
|
const ICurve* pMyCrv = pCrvCompo->GetCurve( i) ;
|
|
if ( pMyCrv == nullptr || ! pMyCrv->IsValid())
|
|
return false ;
|
|
// ricavo la lunghezza di tale curva
|
|
double dLen = 0. ;
|
|
pMyCrv->GetLength( dLen) ;
|
|
// se lunghezza accettabile o maggiore della massima trovata, ricavo ptStart
|
|
if ( dLen > dLenRef) {
|
|
if ( pMyCrv->GetMidPoint( ptStart)) {
|
|
dLenRef = dLen ;
|
|
bAllShort = false ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// se curve tutte piccole
|
|
if ( bAllShort) {
|
|
bool bMidOutFake ;
|
|
Vector3d vtMidOutFake ;
|
|
if ( ! GetPtStartOnHomogeousEdges( pCrvCompo, PockParams, TEMP_PROP_CLOSE_EDGE, P_INVALID,
|
|
bMidOutFake, ptStart, vtMidOutFake))
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPtStartOnGenericEdgeByPtRef( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
|
|
const Point3d& ptRef, Point3d& ptStart)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 ||
|
|
! ptRef.IsValid())
|
|
return false ;
|
|
|
|
// ordino le curve di pCrvCompo in base alla vicinanza da ptRef
|
|
// <indice della curva, distanza da ptRef>
|
|
vector<pair<int, double>> vCrvInfoDist ; vCrvInfoDist.resize( pCrvCompo->GetCurveCount()) ;
|
|
// scorro le curve di pCrvCompo
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
vCrvInfoDist[i].first = i ;
|
|
// calcolo la distanza tra la curva i-esima e ptRef
|
|
double dDist = INFINITO ;
|
|
DistPointCurve DistPtCrv( ptRef, *pCrvCompo->GetCurve( i)) ;
|
|
DistPtCrv.GetSqDist( dDist) ;
|
|
vCrvInfoDist[i].second = dDist ;
|
|
}
|
|
// ordino il vettore in base alla distanza
|
|
sort( vCrvInfoDist.begin(), vCrvInfoDist.end(), [] ( const pair<int, double>& a, const pair<int, double>& b) {
|
|
return a.second < b.second ;
|
|
}) ;
|
|
|
|
// cerco un parametro iniziale valido per le sottocurve ordinate
|
|
double dLenRef = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ;
|
|
for ( int i = 0 ; i < int( vCrvInfoDist.size()) ; ++ i) {
|
|
// recupero la curva i-esima
|
|
const ICurve* pMyCrv = pCrvCompo->GetCurve( vCrvInfoDist[i].first) ;
|
|
if ( pMyCrv != nullptr && pMyCrv->IsValid()) {
|
|
// controllo che sia abbastanza lunga e aggiorno ptStart
|
|
double dLen = 0. ;
|
|
pMyCrv->GetLength( dLen) ;
|
|
if ( dLen > dLenRef) {
|
|
if ( pMyCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart))
|
|
return true ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// alla peggio...
|
|
bool bMidOutFake ;
|
|
Vector3d vtMidOutFake ;
|
|
return GetPtStartOnHomogeousEdges( pCrvCompo, PockParams, TEMP_PROP_CLOSE_EDGE, ptRef,
|
|
bMidOutFake, ptStart, vtMidOutFake) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignOpenCloseTmpPropToFirstOffsCurve( ICurveComposite* pCrv, const PocketParams& PockParams,
|
|
const ISurfFlatRegion* pSfrToWork, bool& bSomeOpen)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0 ||
|
|
pSfrToWork == nullptr || ! pSfrToWork->IsValid() || pSfrToWork->GetChunkCount() != 1)
|
|
return false ;
|
|
bSomeOpen = false ; // flag per presenza di lati aperti
|
|
int nInd = -1 ; // indice della prima curva che non è "raccordo" di Offset
|
|
|
|
// scorro tutte le curve presenti in pCrv ( curva da cui devo entrare)
|
|
for ( int i = 0 ; i < pCrv->GetCurveCount() ; ++ i) {
|
|
// nProp0 -> #curva il cui Offset ha generato la curva i-esima di pCrv
|
|
int nProp0 = 0 ; pCrv->GetCurveTempProp( i, nProp0, 0) ;
|
|
// nProp1 -> #loop che contiene la curva espressa in nProp0
|
|
int nProp1 = 0 ; pCrv->GetCurveTempProp( i, nProp1, 1) ;
|
|
|
|
// se questa curva non è un "raccordo" di Offset
|
|
if ( nProp0 > 0) {
|
|
// aggiorno la proprietà della curva da cui devo entrare
|
|
int nTempProp = TEMP_PROP_CLOSE_EDGE ;
|
|
pSfrToWork->GetCurveTempProp( 0, nProp1, nProp0 - 1, nTempProp, 0) ;
|
|
pCrv->SetCurveTempProp( i, nTempProp, 0) ;
|
|
// se la curva è aperta, aggiorno il Flag
|
|
if ( nTempProp == TEMP_PROP_OPEN_EDGE && ! bSomeOpen)
|
|
bSomeOpen = true ;
|
|
// salvo l'indice della prima curva trovata che non è un "raccordo" di Offset
|
|
if ( nInd == -1)
|
|
nInd = i ;
|
|
}
|
|
// se questa curva è un "raccordo" di Offset
|
|
else {
|
|
pCrv->SetCurveTempProp( i, TEMP_PROP_SMOOTH, 0) ;
|
|
pCrv->SetCurveTempProp( i, TEMP_PROP_SMOOTH, 1) ;
|
|
}
|
|
}
|
|
|
|
// cambio il punto iniziale all'inizio della prima curva che non deriva da un "raccordo"
|
|
Point3d ptStart ;
|
|
if ( nInd != -1) {
|
|
if ( ! pCrv->GetStartPoint( ptStart))
|
|
return false ;
|
|
pCrv->ChangeStartPoint( nInd) ; // la prima curva non è un "raccordo" di Offset
|
|
}
|
|
|
|
// scorro tutte le curve successive alla prima
|
|
for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++ i) {
|
|
int nTmpProp0, nTmpProp1 ;
|
|
// se "raccordo" copio la tempProp precedente
|
|
if (( pCrv->GetCurveTempProp( i, nTmpProp0, 0) && nTmpProp0 == TEMP_PROP_SMOOTH) &&
|
|
( pCrv->GetCurveTempProp( i, nTmpProp1, 1) && nTmpProp1 == TEMP_PROP_SMOOTH))
|
|
// copio la temp prop della curva precedente
|
|
pCrv->SetCurveTempProp( i, pCrv->GetCurve( i-1)->GetTempProp( 0), 0) ;
|
|
|
|
}
|
|
|
|
// lascio invariato il punto iniziale della curva originaria
|
|
if ( nInd != -1) {
|
|
double dUNewS ;
|
|
pCrv->GetParamAtPoint( ptStart, dUNewS) ;
|
|
pCrv->ChangeStartPoint( dUNewS) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SetPtStartForPath( ICurveComposite* pCrvOffs, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
|
|
const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut,
|
|
const ICurveComposite* pCrvOrig)
|
|
{
|
|
/*
|
|
Come prima cosa ricavo i lati aperti equivalenti dal primo Offset; Facendo l'Offset di una curva
|
|
chiusa ho all'interno delle temp prop delle sottcurve due informazioni :
|
|
// nTmpProp0 -> #curva il cui Offset ha generato la curva i-esima attuale
|
|
// nTmpProp1 -> #loop che contiene la curva espressa in nTmpProp0
|
|
|
|
Quindi dal contorno con i lati aperti impostati, posso settare i lati aperti "equivalenti"
|
|
anche sulla curva di primo Offset.
|
|
NB. Il tool entra dalla curva di primo Offset, non dal bordo della regione da svuotare; quindi
|
|
l'entrata va calcolata sul primo Offset, non sulla curva di bordo della FlatRegion di Pocketing.
|
|
*/
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0 ||
|
|
pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1)
|
|
return false ;
|
|
|
|
// assegno proprietà di OPEN/CLOSE alle sottocurve dell'offset corrente
|
|
bool bSomeOpen = false ;
|
|
if ( ! AssignOpenCloseTmpPropToFirstOffsCurve( pCrvOffs, PockParams, pSrfToWork, bSomeOpen))
|
|
return false ;
|
|
|
|
// punto valido per l'entrata ...
|
|
bMidOut = false ;
|
|
if ( bSomeOpen) {
|
|
// se esistono lati aperti...
|
|
if ( pCrvOrig != nullptr && pCrvOrig->IsValid()) {
|
|
// ... e non ho un punto di riferimento, lo cerco sulla curva originale
|
|
if ( ! ptEndPrec.IsValid()) {
|
|
if ( ! GetPtStartOnOpenEdgeByOrigCurve( pCrvOrig, PockParams, pCrvOffs, ptStart, vtMidOut, bMidOut))
|
|
return false ;
|
|
}
|
|
// ... e ho un punto di riferimento
|
|
else {
|
|
if ( ! GetPtStartOnOpenEdgeByPtRef( ptEndPrec, PockParams, pCrvOffs, pCrvOrig, ptStart, vtMidOut, bMidOut))
|
|
return false ;
|
|
}
|
|
}
|
|
// se punto non trovato precedentemente, tento con una sottocurva OPEN generica
|
|
if ( ! bMidOut) {
|
|
if ( ! GetParamOnOpenCurve( pCrvOffs, PockParams, bMidOut, ptStart, vtMidOut))
|
|
return false ;
|
|
}
|
|
// se queste curve sono troppo corte, analizzo le parti uniformi
|
|
if ( ! bMidOut) {
|
|
if ( ! GetPtStartOnHomogeousEdges( pCrvOffs, PockParams, TEMP_PROP_OPEN_EDGE, ptEndPrec,
|
|
bMidOut, ptStart, vtMidOut))
|
|
return false ;
|
|
}
|
|
}
|
|
// se sottocurve tutte CLOSED o un punto iniziale non valido, lo cerco su un lato generico
|
|
if ( ! bMidOut) {
|
|
if ( ! ptEndPrec.IsValid()) {
|
|
if ( ! GetPtStartOnGenericEdge( pCrvOffs, PockParams, ptStart))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! GetPtStartOnGenericEdgeByPtRef( pCrvOffs, PockParams, ptEndPrec, ptStart))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// imposto il punto iniziale scelto
|
|
double dPar = 0. ;
|
|
if ( ! pCrvOffs->GetParamAtPoint( ptStart, dPar) || ! pCrvOffs->ChangeStartPoint( dPar))
|
|
pCrvOffs->ChangeStartPoint( .5) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SetAdvancedPtStartForPath( ICRVCOMPOPOVECTOR& vCrvOffsAct, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
|
|
const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut,
|
|
bool& bMidOut, int& nIndex, ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops)
|
|
{
|
|
// controllo dei parametri
|
|
for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i)
|
|
if ( IsNull( vCrvOffsAct[i]) || ! vCrvOffsAct[i]->IsValid() || vCrvOffsAct[i]->GetCurveCount() == 0)
|
|
return false ;
|
|
if ( pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1)
|
|
return false ;
|
|
nIndex = 0 ;
|
|
|
|
// scorro le curve di primo Offset
|
|
// NB. in prima posizione ho il primo Offset del loop esterno, a seguire le isole
|
|
// NB. Se non riesco ad entrare in nessuna curva, allora tengo valida quella calcolata per il loop esterno
|
|
bool bMidOut_fist = false ;
|
|
Vector3d vtMidOut_first ;
|
|
Point3d ptStart_first ;
|
|
for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i) {
|
|
// cambio il suo punto d'inizio dell'Offset attuale
|
|
if ( SetPtStartForPath( vCrvOffsAct[i], PockParams, pSrfToWork, ptEndPrec, ptStart, vtMidOut,
|
|
bMidOut, i < ( int( vCrvOrigChunkLoops.size())) ? Get( vCrvOrigChunkLoops[i]) : nullptr))
|
|
vCrvOffsAct[i]->GetStartPoint( ptStart) ;
|
|
else
|
|
return false ;
|
|
// se ho trovato un'entrata valida da un lato aperto, ho finito
|
|
if ( bMidOut) {
|
|
nIndex = i ; // salvo l'indice del vettore
|
|
return true ;
|
|
}
|
|
// se sono nel loop esterno e non ho trovato un'entrata da un lato aperto, salvo i risultati ottenuti
|
|
else if ( i == 0) {
|
|
bMidOut_fist = bMidOut ;
|
|
vtMidOut_first = vtMidOut ;
|
|
ptStart_first = ptStart ;
|
|
}
|
|
}
|
|
|
|
// in questo caso non ho trovato un'entrata da un lato aperto in nessuna curva ( potrebbero essere tutte chiuse)
|
|
bMidOut = bMidOut_fist ;
|
|
vtMidOut = vtMidOut_first ;
|
|
ptStart = ptStart_first ;
|
|
return true ;
|
|
|
|
}
|
|
|
|
// ***************************************************************************
|
|
//---------------------------- CASI OTTIMIZZATI ------------------------------
|
|
// ***************************************************************************
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetOptCrvIndex( const ISurfFlatRegion* pSfrOrig, const ISurfFlatRegion* pSfrChunk,
|
|
const PocketParams& PockParams, int nReg, ICRVCOMPOPOVECTOR& vCrvOrig)
|
|
{
|
|
|
|
/*
|
|
NB. La superficie da lavorare è stata estesa presso i lati aperti; cerco il Chunk
|
|
di riferimento sulla superficie originale ed estraggo le sue curve ( in prima posizione
|
|
il loop esterno e a seguire le isole); queste curve verranno utilizzate per la scelta
|
|
dei casi ottimizzati ( trapezi, circonferenze, ecc ecc..)
|
|
*/
|
|
|
|
// controllo dei parametri
|
|
if ( pSfrOrig == nullptr || ! pSfrOrig->IsValid() ||
|
|
pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
|
|
// se la superficie originale ha un solo Chunk, allora le curve originali sono già trovate
|
|
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( 0) ; ++ l)
|
|
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( 0, l))) ;
|
|
if ( pSfrOrig->GetChunkCount() == 1)
|
|
return true ;
|
|
|
|
// vettore di indici dei chunk candidati
|
|
INTVECTOR vInds ;
|
|
|
|
// scorro tutti i Chunk della superficie orginaria
|
|
for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) {
|
|
// la classificazione può essere fatta semplicemente guardando il Loop esterno
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
// classifico il Chunk attuale con la curva in esame
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) {
|
|
// se il Chunk attuale è stato esteso presso presso i lati aperti mi aspetto che esso non sia più
|
|
// grande del chunk originale ( se non ci sono lati aperti, allora coincide )
|
|
// se il ccClass non contiene parti classificate come esterne allora il Chunk di riferimento è il c-esimo
|
|
bool bIsThis = true ;
|
|
for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) {
|
|
if ( ccClass[k].nClass == CRVC_OUT)
|
|
bIsThis = false ;
|
|
}
|
|
if ( bIsThis)
|
|
vInds.push_back( c) ;
|
|
}
|
|
}
|
|
// se ho un solo indice allora ho già identificato le curve originali
|
|
if ( int( vInds.size()) == 1) {
|
|
vCrvOrig.clear() ;
|
|
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( vInds[0]) ; ++ l)
|
|
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( vInds[0], l))) ;
|
|
return true ;
|
|
}
|
|
// altrimenti...
|
|
/*
|
|
NB. Come parametro alla funzione è stato passato "nReg"; Facendo il primo Offset del Chunk attuale
|
|
questo verrà diviso in n sottoregioni; devo considerare la nReg-esima
|
|
Situazione: Estendendo presso i lati aperti, due ( o più) chunk si sono uniti formandone uno unico
|
|
Quando effettuo il primo offset ( del diametro del materiale + offset radiale), questo Chunk si splitterà in n Chunk;
|
|
Uno ( o più) di questi nuovi chunk potrebbe corrispondere ad un caso ottimizzato.
|
|
Devo recuperare il Chunk della superficie originaria ( che non essendo estesa presso i lati aperti
|
|
ha la geomtria originale)
|
|
*/
|
|
|
|
// effettuo un Offset del Chunk per simulare la creazione degli n Chunks
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
PtrOwner<ISurfFlatRegion> pSfrRefChunk( pSfrChunk->Clone()) ;
|
|
if ( IsNull( pSfrRefChunk) ||
|
|
! pSfrRefChunk->Offset( - dOffs, ICurve::OFF_FILLET))
|
|
return false ;
|
|
|
|
// clono il Chunk nReg-esimo se esiste
|
|
pSfrRefChunk.Set( pSfrRefChunk->CloneChunk( nReg)) ;
|
|
if ( IsNull( pSfrRefChunk))
|
|
return true ; // bisogna passare al ( pSfrChunk + 1)-esimo se esiste
|
|
|
|
// scorro tutti i Chunk della superficie orginaria e ripeto la classificazione
|
|
// NB. Questa volta posso fermarmi al primo Chunk classificato come non esterno
|
|
for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) {
|
|
// come prima recupero il Loop esterno
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
// classificazione
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrRefChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) {
|
|
bool bIsThis = true ;
|
|
for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) {
|
|
if ( ccClass[k].nClass == CRVC_OUT)
|
|
bIsThis = false ;
|
|
}
|
|
if ( bIsThis) {
|
|
vCrvOrig.clear() ;
|
|
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( c) ; ++ l)
|
|
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( c, l))) ;
|
|
return true ; // esco
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ; // alla peggio non considero casi ottimizzati e svuoto normalmente...
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ModifyCurveToSmoothed( ICurveComposite* pCrv, const PocketParams& PockParams, double dRightLen, double dLeftLen, bool bAsParam)
|
|
{
|
|
// se non richiesto non faccio nulla
|
|
if ( ! PockParams.bSmooth)
|
|
return true ;
|
|
|
|
// controllo parametri
|
|
if ( pCrv == nullptr || dLeftLen < EPS_SMALL || dRightLen < EPS_SMALL)
|
|
return false ;
|
|
|
|
ICURVEPOVECTOR vCrvStepsToFill ; // vettore delle Curve da raccordare (curve della composita)
|
|
ICURVEPOVECTOR vCrvArcs ; // vettore contenente gli Archi (tra le curve della composita)
|
|
INTVECTOR vArcsToJump ; // vettore di indici per gli archi saltati (nel caso non si riesca a raccordare ...)
|
|
|
|
// se nella composita ho meno di due curve allora non c'è nulla da raccordare
|
|
const ICurve* pMyCrv = pCrv->GetFirstCurve() ;
|
|
while ( pMyCrv != nullptr) {
|
|
vCrvStepsToFill.emplace_back( pMyCrv->Clone()) ;
|
|
pMyCrv = pCrv->GetNextCurve() ;
|
|
}
|
|
if ( vCrvStepsToFill.size() < 2)
|
|
return true ;
|
|
|
|
// controllo se la curva è chiusa ( nel caso inserisco nel vettore la prima curva due volte, all'inizio e alla fine)
|
|
if ( pCrv->IsClosed()) {
|
|
const ICurve* pMyCrvFirst = pCrv->GetFirstCurve() ;
|
|
if ( pMyCrvFirst == nullptr)
|
|
return false ;
|
|
vCrvStepsToFill.emplace_back( pMyCrvFirst->Clone()) ;
|
|
}
|
|
|
|
double dUE_ref, dUS_ref, dRadius, dPar1, dPar2 ;
|
|
// riempio il vettore degli archi, scorro tutte le curve da raccordare ( la i-esima e la (i+1)-esima)
|
|
for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) {
|
|
|
|
// controllo che le curve non siano già tangenti
|
|
Vector3d vtS, vtE ;
|
|
if ( ! vCrvStepsToFill[i]->GetEndDir( vtS) || ! vCrvStepsToFill[i+1]->GetStartDir( vtE))
|
|
continue ;
|
|
if ( AreSameVectorApprox( vtS, vtE)) {
|
|
vCrvArcs.emplace_back( CreateCurveComposite()) ; // arco nullo
|
|
vArcsToJump.push_back( i) ; // da scartare
|
|
continue ;
|
|
}
|
|
|
|
// ricavo i valori parametrici associati
|
|
double dLen_act ; vCrvStepsToFill[i]->GetLength( dLen_act) ;
|
|
double dLen_succ ; vCrvStepsToFill[i+1]->GetLength( dLen_succ) ;
|
|
double dUE_ref = 1. ;
|
|
if ( dLen_act > dLeftLen)
|
|
dUE_ref -= dLeftLen / dLen_act ;
|
|
double dUS_ref = 0. ;
|
|
if ( dLen_succ > dRightLen)
|
|
dUS_ref = dRightLen / dLen_succ ;
|
|
|
|
// se valori trattati come parametri...
|
|
if ( bAsParam) {
|
|
// ... cerco i parametri corrispondenti sulle due curve
|
|
double dU_cm_S = 0 ; double dULast1 = 1 ; double dULast2 = 1 ;
|
|
vCrvStepsToFill[i]->GetDomain( dU_cm_S, dULast1) ;
|
|
dUE_ref = ( 1 - dRightLen) * dULast1 ;
|
|
vCrvStepsToFill[i+1]->GetDomain( dU_cm_S, dULast2) ;
|
|
dUS_ref = dLeftLen * dULast2 ;
|
|
}
|
|
|
|
// prendo i punti sulle due curve rispetto a tali parametri
|
|
Point3d ptS, ptE ;
|
|
if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) ||
|
|
! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE))
|
|
return false ;
|
|
|
|
dRadius = Dist( ptS, ptE) ; // uso come raggio la distanza tra i due punti ( limite superiore valido)
|
|
|
|
int nMaxTestForArcs = 3 ; // tentativi per creare l'arco
|
|
int nIterForArcs = 0 ; // numero di intersezioni con altre curve
|
|
bool IntersBTWArcs = false ; // caso particolare in cui due archi di raccordo si intersecano causa curva piccola
|
|
|
|
// creo l'arco di raccordo e controllo che non sia troppo piccolo
|
|
PtrOwner<ICurveArc> pCrvArc( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ;
|
|
double dArcLen ;
|
|
if ( ! IsNull( pCrvArc) && pCrvArc->IsValid() && ( ! pCrvArc->GetLength( dArcLen) || dArcLen < 5 * EPS_SMALL)) {
|
|
vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco nullo
|
|
vArcsToJump.push_back( i) ; // da scartare
|
|
continue ;
|
|
}
|
|
|
|
// dal secondo arco in poi controllo che non ci siano intersezioni tra essi
|
|
if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) {
|
|
IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ;
|
|
if ( intCCH.GetIntersCount() > 0 )
|
|
IntersBTWArcs = true ;
|
|
}
|
|
|
|
// se ho intersezioni tra archi o l'arco creato non è valido, allora provo altre nMaxTestForArcs volte a ricrearlo avvicinando i punti
|
|
while (( IsNull( pCrvArc) || IntersBTWArcs) && nIterForArcs < nMaxTestForArcs) {
|
|
|
|
dUE_ref = ( 1 + dUE_ref ) * 0.5 ;
|
|
dUS_ref = dUS_ref * 0.5 ;
|
|
|
|
if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) ||
|
|
! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE))
|
|
return false ;
|
|
|
|
dRadius = Dist( ptS, ptE) ;
|
|
|
|
pCrvArc.Set( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ;
|
|
if ( IsNull( pCrvArc) || ! pCrvArc->IsValid() || ! pCrvArc->GetLength( dArcLen) || dArcLen < 5 * EPS_SMALL)
|
|
break ;
|
|
++ nIterForArcs ;
|
|
|
|
IntersBTWArcs = false ;
|
|
if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) { // dal secondo arco in poi controllo che non ci siano intersezioni tra essi
|
|
IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ;
|
|
if ( intCCH.GetIntersCount() > 0)
|
|
IntersBTWArcs = true ;
|
|
}
|
|
|
|
}
|
|
if ( IsNull( pCrvArc) || ! pCrvArc->IsValid()) { // se ancora non riesco... salto l'arco
|
|
vCrvArcs.emplace_back( CreateCurveArc()) ; // arco nullo
|
|
vArcsToJump.push_back( i) ; // da scartare
|
|
continue ;
|
|
}
|
|
|
|
// se ho creato l'arco lo memorizzo
|
|
vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco valido
|
|
vArcsToJump.push_back( -1) ; // da considerare
|
|
}
|
|
|
|
// creo la curva che restituirò
|
|
PtrOwner<CurveComposite> pCrvCO_temp( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCrvCO_temp))
|
|
return false ;
|
|
Point3d ptArcHelp, ptFirstPoint ;
|
|
|
|
// unisco la curva i-esima con l'arco i-esimo (non guardo l'ultima curva nel vettore, controllo dopo il caso di curva chiusa)
|
|
for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) {
|
|
|
|
if ( vArcsToJump[i] == -1) { // se esiste l'arco ...
|
|
Point3d ptArcS, ptArcE ;
|
|
if ( ! vCrvArcs[i]->GetStartPoint( ptArcS) ||
|
|
! vCrvArcs[i]->GetEndPoint( ptArcE) ||
|
|
! vCrvStepsToFill[i]->GetParamAtPoint( ptArcS, dUE_ref) ||
|
|
! vCrvStepsToFill[i+1]->GetParamAtPoint( ptArcE, dUS_ref))
|
|
return false ;
|
|
|
|
if ( i == 0) { // ... e sono nella prima iterazione ...
|
|
if ( ! pCrv->IsClosed()) { // ... e se la curva è aperta -> la inserisco nel nuovo percorso
|
|
if ( ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref))))
|
|
return false ;
|
|
}
|
|
ptFirstPoint = ptArcS ;
|
|
// ... e se la curva è chiusa -> non la inserisco nel nuovo percorso
|
|
}
|
|
else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...)
|
|
if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref))))
|
|
return false ;
|
|
// aggiungo la curva 'tagliata per il raccordo'
|
|
}
|
|
|
|
// imposto la proprietà di raccord
|
|
vCrvArcs[i]->SetTempProp( TEMP_PROP_SMOOTH, 1) ;
|
|
if ( ! pCrvCO_temp->AddCurve( vCrvArcs[i]->Clone())) // aggiungo l'arco di raccordo
|
|
return false ;
|
|
ptArcHelp = ptArcE ;
|
|
}
|
|
else { // se non esiste l'arco ...
|
|
if ( i == 0 ) { // e sono nella prima iterazione...
|
|
if ( ! vCrvStepsToFill[0]->GetEndPoint( ptArcHelp))
|
|
return false ;
|
|
|
|
if ( ! pCrv->IsClosed()) { // ...e se aperta // aggiungo la prima curva per intero
|
|
if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref))))
|
|
return false ;
|
|
}
|
|
ptFirstPoint = ptArcHelp ;
|
|
}
|
|
else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...)
|
|
double dUS_cm ;
|
|
if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! vCrvStepsToFill[i]->GetDomain( dUS_cm, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref))) ||
|
|
! vCrvStepsToFill[i]->GetEndPoint( ptArcHelp))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// ultima curva...
|
|
if ( pCrv->IsClosed()) { // se curva chiusa...
|
|
if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! vCrvStepsToFill[0]->GetParamAtPoint( ptFirstPoint, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( dUS_ref, dUE_ref))))
|
|
return false ;
|
|
}
|
|
else { // se curva aperta... ( non ha importanza l'esistenza o meno degli archi...)
|
|
double dUS_cm ;
|
|
if ( ! vCrvStepsToFill.back()->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! vCrvStepsToFill.back()->GetDomain( dUS_cm, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill.back()->CopyParamRange( dUS_ref, dUE_ref))))
|
|
return false ;
|
|
}
|
|
|
|
// ripristino il punto inziale se la curva è chiusa
|
|
double dNewDU ;
|
|
if ( ! pCrv->IsClosed()) {
|
|
if ( ! pCrvCO_temp->GetParamAtPoint( ptArcHelp, dNewDU))
|
|
return false ;
|
|
pCrvCO_temp->ChangeStartPoint( dNewDU) ;
|
|
}
|
|
|
|
// restituisco
|
|
int nProp0 = pCrv->GetTempProp( 0) ;
|
|
int nProp1 = pCrv->GetTempProp( 1) ;
|
|
double dParam0 = pCrv->GetTempParam( 0) ;
|
|
double dParam1 = pCrv->GetTempParam( 1) ;
|
|
pCrv->Clear() ;
|
|
pCrv->AddCurve( Release( pCrvCO_temp)) ;
|
|
pCrv->SetTempProp( nProp0, 0) ;
|
|
pCrv->SetTempProp( nProp1, 1) ;
|
|
pCrv->SetTempParam( dParam0, 0) ;
|
|
pCrv->SetTempParam( dParam1, 1) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, const ICRVCOMPOPOVECTOR& vOffIslands,
|
|
ICurveComposite* pCrvLink)
|
|
{
|
|
// pulisco
|
|
pCrvLink->Clear() ;
|
|
|
|
// non serve collegare ( può capitare nel tagliare percorsi con isole complesse)
|
|
if ( AreSamePointApprox( ptStart, ptEnd))
|
|
return true ;
|
|
|
|
// creo la retta che li unisce
|
|
PtrOwner<CurveComposite> pCompoLine( CreateBasicCurveComposite()) ;
|
|
if ( ! pCompoLine->AddPoint( ptStart) || ! pCompoLine->AddLine( ptEnd))
|
|
return false ;
|
|
pCompoLine->SetExtrusion( Z_AX) ;
|
|
|
|
// creo la nuova curva formata dai tratti di linee INTERNI alle isole e dai tratti sul bordo degli offset
|
|
PtrOwner<CurveComposite> pCompo( pCompoLine->Clone()) ;
|
|
if ( IsNull( pCompo) )
|
|
return false ;
|
|
|
|
// memorizzo il tratto lineare nel caso qualche operazione fallisse
|
|
PtrOwner<CurveComposite> pCompoHelp( pCompoLine->Clone()) ;
|
|
if ( IsNull( pCompoHelp))
|
|
return false ;
|
|
|
|
// scorro il vettore degli indici degli offset delle isole intersecati
|
|
for ( int i = 0 ; i < int( vOffIslands.size()) ; ++ i) {
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pCompo, *vOffIslands[i]) ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
if ( ! pCompoHelp->Clear())
|
|
return false ;
|
|
// per ogni intersezione j con l'offset dell'isola i
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++j ) {
|
|
// se ho intersezione spezzo il segmento
|
|
if ( ccClass[j].nClass == CRVC_OUT) {
|
|
Point3d ptS ;
|
|
pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ;
|
|
double dOffS ;
|
|
vOffIslands[i]->GetParamAtPoint( ptS, dOffS) ;
|
|
Point3d ptE ;
|
|
pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ;
|
|
double dOffE ;
|
|
vOffIslands[i]->GetParamAtPoint( ptE, dOffE) ;
|
|
// recupero i due possibili percorsi e uso il più corto
|
|
PtrOwner<ICurve> pCrvA( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ;
|
|
PtrOwner<ICurve> pCrvB( vOffIslands[i]->CopyParamRange( dOffE, dOffS)) ;
|
|
if ( IsNull( pCrvA) || IsNull( pCrvB))
|
|
return false ;
|
|
double dLenA ; pCrvA->GetLength( dLenA) ;
|
|
double dLenB ; pCrvB->GetLength( dLenB) ;
|
|
if ( dLenA < dLenB)
|
|
pCompoHelp->AddCurve( Release( pCrvA)) ;
|
|
else {
|
|
pCrvB->Invert() ;
|
|
pCompoHelp->AddCurve( Release( pCrvB)) ;
|
|
}
|
|
}
|
|
// se non interseco
|
|
else {
|
|
pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
|
|
}
|
|
}
|
|
pCompo->Clear() ;
|
|
pCompo->AddCurve( pCompoHelp->Clone()) ;
|
|
}
|
|
|
|
pCrvLink->AddCurve( Release( pCompo)) ;
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static bool
|
|
ModifyBiArc( ICurve* pBiArcLink, double dToll, ICurveComposite* pNewBiArc)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
if ( pBiArcLink == nullptr)
|
|
return false ;
|
|
if ( dToll > 0.99 || dToll < 0.01)
|
|
return false ;
|
|
pNewBiArc->Clear() ;
|
|
|
|
// dominio
|
|
double dUS, dUE ;
|
|
pBiArcLink->GetDomain( dUS, dUE) ;
|
|
|
|
// prendo i due archi della curva BiArco
|
|
PtrOwner<ICurve> pArc1( pBiArcLink->CopyParamRange( dUS, ( dUS + dUE) / 2)) ;
|
|
PtrOwner<ICurve> pArc2( pBiArcLink->CopyParamRange(( dUS + dUE ) / 2, dUE)) ;
|
|
if ( IsNull( pArc1) || ! pArc1->IsValid() || IsNull( pArc2) || ! pArc2->IsValid())
|
|
return false ;
|
|
|
|
// primo pezzo
|
|
pArc1->GetDomain( dUS, dUE) ;
|
|
PtrOwner<ICurve> pArc1A( pArc1->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1
|
|
PtrOwner<ICurve> pArc1B( pArc1->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2
|
|
Point3d pt1A, pt1B ;
|
|
pArc1A->GetEndPoint( pt1A) ;
|
|
pArc1B->GetStartPoint( pt1B) ;
|
|
PtrOwner<CurveLine> pLine1( CreateBasicCurveLine()) ;
|
|
pLine1->Set( pt1A, pt1B) ;
|
|
|
|
// secondo pezzo
|
|
pArc2->GetDomain( dUS, dUE) ;
|
|
PtrOwner<ICurve> pArc2A( pArc2->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1
|
|
PtrOwner<ICurve> pArc2B( pArc2->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2
|
|
Point3d pt2A, pt2B ;
|
|
pArc2A->GetEndPoint( pt2A) ;
|
|
pArc2B->GetStartPoint( pt2B) ;
|
|
PtrOwner<CurveLine> pLine2( CreateBasicCurveLine()) ;
|
|
pLine2->Set( pt2A, pt2B) ;
|
|
|
|
// ricostruisco il nuovo link
|
|
if ( ! pNewBiArc->AddCurve( Release( pArc1A)) ||
|
|
! pNewBiArc->AddCurve( Release( pLine1)) ||
|
|
! pNewBiArc->AddCurve( Release( pArc1B)) ||
|
|
! pNewBiArc->AddCurve( Release( pArc2A)) ||
|
|
! pNewBiArc->AddCurve( Release( pLine2)) ||
|
|
! pNewBiArc->AddCurve( Release( pArc2B)))
|
|
return false ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static bool
|
|
CalcBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd,
|
|
const Vector3d& vtEnd, double dParMeet, const ICRVCOMPOPOVECTOR& vOffIslands,
|
|
const PocketParams& PockParams, ICurveComposite* pCrvLink)
|
|
{
|
|
// se senza smusso, ritorno tratto lineare
|
|
if ( ! PockParams.bSmooth)
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
|
|
// creo il BiArc che unisce i due punti
|
|
double dAngStart, dAngEnd ;
|
|
vtStart.GetAngleXY( X_AX, dAngStart) ;
|
|
vtEnd.GetAngleXY( X_AX, dAngEnd) ;
|
|
PtrOwner<ICurve> pBiArcLink ;
|
|
if ( dParMeet != 0)
|
|
pBiArcLink.Set( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, dParMeet)) ;
|
|
else {
|
|
PtrOwner<CurveArc> pCrvCir( CreateBasicCurveArc()) ;
|
|
if ( IsNull( pCrvCir))
|
|
return false ;
|
|
pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, PockParams.bInvert ? 360 : - 360, 0, Z_AX) ;
|
|
pBiArcLink.Set( pCrvCir->Clone()) ;
|
|
pCrvLink->AddCurve( pBiArcLink->Clone()) ;
|
|
return true ;
|
|
}
|
|
if ( IsNull( pBiArcLink))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
|
|
// se BiArco troppo grande allora lo modifico
|
|
double dLenBiArc ;
|
|
pBiArcLink->GetLength( dLenBiArc) ;
|
|
if ( dLenBiArc > 200) {
|
|
PtrOwner<CurveComposite> pCrvNewBiArcS( CreateBasicCurveComposite()) ;
|
|
ModifyBiArc( pBiArcLink, 0.2, pCrvNewBiArcS) ;
|
|
pBiArcLink.Set( pCrvNewBiArcS) ;
|
|
}
|
|
|
|
// creo la nuova curva formata inizialmente dai tratti di archi
|
|
PtrOwner<ICurveComposite> pCompo( GetCurveComposite( pBiArcLink->Clone())) ;
|
|
if ( IsNull( pCompo))
|
|
return false ;
|
|
PtrOwner<ICurveComposite> pCompoHelp( GetCurveComposite( pBiArcLink->Clone())) ;
|
|
if ( IsNull( pCompoHelp))
|
|
return false ;
|
|
|
|
// scorro tutte le curve per controllare le intersezioni ...
|
|
for ( int i = 0 ; i < int( vOffIslands.size()) ; ++ i) {
|
|
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pCompo, *vOffIslands[i]) ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
if ( ! pCompoHelp->Clear())
|
|
return false ;
|
|
|
|
// per ogni intersezione j con l'offset dell'isola i
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
// se ho intersezione spezzo il segmento
|
|
if ( ccClass[j].nClass == CRVC_OUT && int( ccClass.size()) > 1) {
|
|
Point3d ptS ;
|
|
pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ;
|
|
double dOffS ;
|
|
vOffIslands[i]->GetParamAtPoint( ptS, dOffS, 1500 * EPS_SMALL) ;
|
|
Point3d ptE ;
|
|
pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ;
|
|
double dOffE ;
|
|
vOffIslands[i]->GetParamAtPoint( ptE, dOffE, 1500 * EPS_SMALL) ;
|
|
// recupero i due possibili percorsi e uso il più corto
|
|
PtrOwner<ICurve> pCrvA( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ;
|
|
PtrOwner<ICurve> pCrvB( vOffIslands[i]->CopyParamRange( dOffE, dOffS)) ;
|
|
|
|
if ( IsNull( pCrvA) || IsNull( pCrvB))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
|
|
double dLenA ; pCrvA->GetLength( dLenA) ;
|
|
double dLenB ; pCrvB->GetLength( dLenB) ;
|
|
|
|
if ( j != 0) {
|
|
if ( dLenA < dLenB) {
|
|
if ( ! pCompoHelp->AddCurve( Release( pCrvA)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
}
|
|
else {
|
|
pCrvB->Invert() ;
|
|
if ( ! pCompoHelp->AddCurve( Release( pCrvB)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
}
|
|
}
|
|
else {
|
|
pCrvB->Invert() ;
|
|
if ( ! pCompoHelp->AddCurve( Release( pCrvB)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
}
|
|
}
|
|
// se non interseco
|
|
else {
|
|
if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ;
|
|
}
|
|
}
|
|
|
|
pCompo->Clear() ;
|
|
if ( ! pCompo->AddCurve( pCompoHelp->Clone()))
|
|
if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink))
|
|
return false ;
|
|
}
|
|
|
|
// controllo le dimensioni del BiArco
|
|
BBox3d bBox3 ;
|
|
if ( pCompo->GetLocalBBox( bBox3)) {
|
|
double dRadBB ;
|
|
// se troppo piccolo, lo approssimo con un segmento
|
|
if ( bBox3.GetRadius( dRadBB) && dRadBB < 500 * EPS_SMALL) {
|
|
if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink))
|
|
return false ;
|
|
return true ;
|
|
}
|
|
// se troppo grande, lo approssimo con un segmento
|
|
if ( bBox3.GetRadius( dRadBB) && dRadBB > 3 * PockParams.dRad) {
|
|
if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink))
|
|
return false ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
PtrOwner<ICurveComposite> pCrvCompo_noSmooth( pCompo->Clone()) ;
|
|
if ( ! ModifyCurveToSmoothed( pCompo, PockParams, 0.05, 0.05, true) ||
|
|
! pCompo->IsValid())
|
|
pCompo.Set( pCrvCompo_noSmooth) ;
|
|
pCrvLink->AddCurve( Release( pCompo)) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, const ICRVCOMPOPOVECTOR& vFirstOffset,
|
|
const PocketParams& PockParams, double dLenPercS, double dLenPercE,
|
|
ICurveComposite* pCrvLink)
|
|
{
|
|
// controllo i parametri
|
|
if ( pCrvS == nullptr || pCrvE == nullptr )
|
|
return false ;
|
|
pCrvLink->Clear() ;
|
|
|
|
// curva finale da restituire
|
|
PtrOwner<CurveComposite> ptCrvFinal( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( ptCrvFinal))
|
|
return false ;
|
|
|
|
// Prendo i punti, i vettori tangenti e i parametri di essi per le curve
|
|
Point3d ptSS, ptSE, ptES, ptEE ;
|
|
Vector3d vS, vE ;
|
|
double dUSS, dUSE, dUES, dUEE, dLenS, dLenE ;
|
|
dUSS = 0 ; dUSE = 0 ;
|
|
pCrvS->GetEndPoint( ptSE) ;
|
|
pCrvS->GetStartPoint( ptSS) ;
|
|
pCrvE->GetStartPoint( ptES) ;
|
|
pCrvE->GetEndPoint( ptEE) ;
|
|
pCrvS->GetEndDir( vS) ;
|
|
pCrvE->GetStartDir( vE) ;
|
|
pCrvS->GetDomain( dUSS, dUSE) ;
|
|
pCrvE->GetDomain( dUES, dUEE) ;
|
|
pCrvS->GetLength( dLenS) ;
|
|
pCrvE->GetLength( dLenE) ;
|
|
|
|
// se ho una curva di primo Offset allora non devo accorciarla ... ( si per bordi esterni che per isole)
|
|
for ( int i = 0 ; i < int( vFirstOffset.size()) ; ++ i) {
|
|
if ( vFirstOffset[i]->IsPointOn( ptSS) && vFirstOffset[i]->IsPointOn( ptSE))
|
|
dLenPercS = 0 ;
|
|
if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE))
|
|
dLenPercE = 0 ;
|
|
}
|
|
double dLStepS = ( dLenPercS < EPS_SMALL ? 0. : PockParams.dRad) ;
|
|
double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : PockParams.dRad) ;
|
|
dLenE = 0 ;
|
|
|
|
/*
|
|
calcolo il biArco tra il punto iniziale e finale dei due offset consecutivi
|
|
se richiesto lo smusso -> accorcio entrambi i tratti di Offset e, riuscendo, si sotituisce
|
|
il biArco calcolato prima con quest'ultimo
|
|
*/
|
|
int nMaxIter = 2 ;
|
|
if ( ! PockParams.bSmooth)
|
|
nMaxIter = 1 ;
|
|
|
|
// taglio la curva al massimo nMaxIter-volte e mi fermo al taglio più opportuno
|
|
int nIter = 0 ;
|
|
while ( nIter < nMaxIter) {
|
|
|
|
// calcolo il biArco
|
|
PtrOwner<CurveComposite> ptBiArc( CreateBasicCurveComposite()) ;
|
|
if ( ! CalcBoundedSmoothedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, PockParams, ptBiArc) ||
|
|
ptBiArc->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
ptCrvFinal->Clear() ;
|
|
ptCrvFinal.Set( ptBiArc->Clone()) ;
|
|
|
|
if ( dLenPercE == 0 && dLenPercS == 0 )
|
|
break ;
|
|
|
|
// se ho trovato un BiArco valido, allora accorcio le due curve di Offset
|
|
dLenS -= dLStepS ;
|
|
dLenE += dLStepE ;
|
|
|
|
// ricalcolo il punto iniziale e finale
|
|
pCrvS->GetParamAtLength( dLenS, dUSE) ;
|
|
pCrvS->GetPointD1D2( dUSE, ICurve::FROM_MINUS, ptSE) ;
|
|
pCrvE->GetParamAtLength( dLenE, dUES) ;
|
|
pCrvE->GetPointD1D2( dUES, ICurve::FROM_PLUS, ptES) ;
|
|
|
|
// ricalcolo il vettore tangente iniziale e finale
|
|
PtrOwner<ICurveComposite> pCrvS_clone( CloneCurveComposite( pCrvS)) ;
|
|
PtrOwner<ICurveComposite> pCrvE_clone( CloneCurveComposite( pCrvE)) ;
|
|
pCrvS_clone->ChangeStartPoint( dUSE) ;
|
|
pCrvE_clone->ChangeStartPoint( dUES) ;
|
|
pCrvS_clone->GetStartDir( vS) ;
|
|
pCrvE_clone->GetStartDir( vE) ;
|
|
|
|
++ nIter ;
|
|
}
|
|
|
|
pCrvLink->AddCurve( Release( ptCrvFinal)) ; // ultimo arco valido trovato
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
static bool
|
|
GetUnclearedRegionAndSetFeed( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks,
|
|
const ICurveComposite* pCrv_orig, const PocketParams& PockParams, ISurfFlatRegion* pSrfToCut)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
if ( vFirstOffs.empty() || int( vOffs.size()) < int( vFirstOffs.size()))
|
|
return false ;
|
|
pSrfToCut->Clear() ;
|
|
|
|
// 1) creo la regione esterna
|
|
PtrOwner<ISurfFlatRegion> pSrfExtern( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfExtern))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vFirstOffs.size()) ; ++ i) {
|
|
if ( i == 0)
|
|
pSrfExtern->AddExtLoop( vFirstOffs[i]->Clone()) ;
|
|
else {
|
|
PtrOwner<ICurve> pCrvIntLoop( vFirstOffs[i]->Clone()) ;
|
|
if ( IsNull( pCrvIntLoop) ||
|
|
! pCrvIntLoop->Invert() ||
|
|
! pSrfExtern->AddIntLoop( Release( pCrvIntLoop)))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// ---- le seguenti regioni sono distinte per calcolar emeglio la Feed ----
|
|
// Creo la regione svuotata dal Tool negli Offset, nei Links e sia dagli Offsets che dai Links
|
|
PtrOwner<ISurfFlatRegion> pSfrTool( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrTool))
|
|
return false ;
|
|
|
|
// creo un vettore che conterrà solamente i Link percorsi fino ad Ora
|
|
bool bFollowOrder = ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_CONFORMAL_ZIGZAG) ;
|
|
for ( int i = ( bFollowOrder ? 0 : int( vOffs.size()) - 1) ;
|
|
( bFollowOrder ? i < int( vOffs.size()) : i >= 0 ) ;
|
|
( bFollowOrder ? ++ i : -- i)) {
|
|
|
|
// ================== OFFSET =============================
|
|
// recupero le proprietà temporanee
|
|
int nProp0 = vOffs[i]->GetTempProp( 0) ;
|
|
int nProp1 = vOffs[i]->GetTempProp( 1) ;
|
|
|
|
// Feed
|
|
bool bFirstOffs = false ;
|
|
Point3d ptStart ; vOffs[i]->GetStartPoint( ptStart) ;
|
|
for ( int j = 0 ; j < int( vFirstOffs.size()) && ! bFirstOffs ; ++ j)
|
|
bFirstOffs = vFirstOffs[j]->IsPointOn( ptStart, 100 * EPS_SMALL) ;
|
|
// calcolo gli intervalli di Feed per la curva di Offset
|
|
if ( ! AssignFeedSpiral( vOffs[i], pSfrTool, false, bFirstOffs, pCrv_orig, PockParams, 2 * PockParams.dRad / 3))
|
|
return false ;
|
|
// aggiorno la superificie svuotata
|
|
PtrOwner<ISurfFlatRegion> pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( vOffs[i]->Clone() , PockParams.dRad + 5 * EPS_SMALL, false, false)) ;
|
|
if ( ! IsNull( pSrfToolRegOffi)) {
|
|
if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0)
|
|
pSfrTool.Set( pSrfToolRegOffi) ;
|
|
else
|
|
pSfrTool->Add( *pSrfToolRegOffi) ;
|
|
}
|
|
vOffs[i]->SetTempProp( nProp0, 0) ;
|
|
vOffs[i]->SetTempProp( nProp1, 1) ;
|
|
|
|
// ================= LINK ================================
|
|
int nLink_Ind = ( bFollowOrder ? i + 1 : i) ;
|
|
// ( Il primo link è nullo, infatti non vi è nessun raccordo per raggiungere il primo Offset)
|
|
if ( nLink_Ind < int( vLinks.size()) && ! IsNull( vLinks[nLink_Ind]) &&
|
|
vLinks[nLink_Ind]->IsValid()) {
|
|
|
|
// recupero le proprietà temporanee
|
|
int nProp0 = vLinks[nLink_Ind]->GetTempProp( 0) ;
|
|
int nProp1 = vLinks[nLink_Ind]->GetTempProp( 1) ;
|
|
// Feed
|
|
PtrOwner<ICurveComposite> pCrvLink( ConvertCurveToComposite( vLinks[nLink_Ind]->Clone())) ;
|
|
// calcolo gli intervalli di Feed per la curva di Link
|
|
if ( IsNull( pCrvLink) || ! pCrvLink->IsValid() ||
|
|
! AssignFeedSpiral( pCrvLink, pSfrTool, true, false, pCrv_orig, PockParams, 2 * PockParams.dRad / 3))
|
|
return false ;
|
|
// aggiorno la regione svuotata
|
|
PtrOwner<ISurfFlatRegion> pSrfToolRegLinki( GetSurfFlatRegionFromFatCurve( pCrvLink->Clone(), PockParams.dRad + 5 * EPS_SMALL, false, false)) ;
|
|
if ( ! IsNull( pSrfToolRegLinki)) {
|
|
if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0)
|
|
pSfrTool.Set( pSrfToolRegLinki) ;
|
|
else
|
|
pSfrTool->Add( *pSrfToolRegLinki) ;
|
|
}
|
|
// risetto il Link come curva composita ...
|
|
pCrvLink->SetTempProp( nProp0, 0) ;
|
|
pCrvLink->SetTempProp( nProp1, 1) ;
|
|
vLinks[nLink_Ind].Set( pCrvLink) ;
|
|
}
|
|
|
|
}
|
|
|
|
// 4) Creo la regione contenente tutte le parti non svuotate
|
|
pSrfToCut->CopyFrom( pSrfExtern) ;
|
|
if ( ! pSrfToCut->Subtract( *pSfrTool))
|
|
return false ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs,
|
|
ICRVCOMPOPOVECTOR& vOffsFirstCurve, const PocketParams& PockParams)
|
|
{
|
|
/*
|
|
E' richiesto che le curve di Offset presentino le seguenti TempProps :
|
|
- TempProp0 -> Offset progressivo ricavato ( 0, 1, 2, ... nMaxIter) [per CONFORMAL]
|
|
- TempProp1 -> parte esterna della curva ( MDS_LEFT o MDS_LEFT) [per CONFORMAL]
|
|
[per SPIRAL le proprietà sono ricavate automaticamente]
|
|
*/
|
|
|
|
// se non ho nessuna superficie valida da svuotare, allora esco
|
|
if ( pSfrUncleared == nullptr)
|
|
return false ;
|
|
if ( ! pSfrUncleared->IsValid() || pSfrUncleared->GetChunkCount() == 0)
|
|
return true ;
|
|
|
|
// scorro tutti i Chunk della regione
|
|
ICRVCOMPOPOVECTOR vCrvCurl ;
|
|
for ( int nC = 0 ; nC < pSfrUncleared->GetChunkCount() ; ++ nC) {
|
|
|
|
// recupero il centroide del Chunk da rimuovere
|
|
Point3d ptCentroid ; pSfrUncleared->GetChunkCentroid( nC, ptCentroid) ;
|
|
|
|
/*
|
|
Tra tutte le curve di Offset escludo le curve che :
|
|
- Sono di primo Offset
|
|
- Non contengono il centroide ( condizione già verificata per quelle di primo Offset)
|
|
... e cerco la più vicina tra le rimanenti
|
|
*/
|
|
int nInd = -1 ;
|
|
double dRefDist = INFINITO - 1 ;
|
|
Point3d ptCloser ; // serve nel caso non bastasse la curva a ricciolo per svuotare il chunk nC-esimo
|
|
int nFlag ;
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
|
|
// se la curva è di primo Offset, allora la salto
|
|
Point3d ptCheck ; vOffs[i]->GetStartPoint( ptCheck) ;
|
|
bool bSkip = false ;
|
|
for ( int j = 0 ; j < int( vOffsFirstCurve.size()) && ! bSkip ; ++ j) {
|
|
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_SPIRALOUT)
|
|
bSkip = ( vOffsFirstCurve[j]->IsPointOn( ptCheck, 100 * EPS_SMALL)) ;
|
|
else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG)
|
|
bSkip = ( vOffs[i]->GetTempProp( 0) == 0) ;
|
|
}
|
|
if ( bSkip)
|
|
continue ;
|
|
// se la curva contiene il centroide, allora la salto
|
|
int nSide ;
|
|
DistPointCurve DistPtCrv( ptCentroid, *vOffs[i]) ;
|
|
if ( DistPtCrv.GetSideAtMinDistPoint( EPS_SMALL, Z_AX, nSide)) {
|
|
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_SPIRALOUT) {
|
|
if ( nSide == ( ! PockParams.bInvert ? MDS_LEFT : MDS_RIGHT))
|
|
continue ;
|
|
}
|
|
else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) {
|
|
if ( nSide == vOffs[i]->GetTempProp( 1))
|
|
continue ;
|
|
}
|
|
}
|
|
// controllo se la distanza è ammissibile
|
|
double dCurrDist = INFINITO ;
|
|
if ( DistPtCrv.GetSqDist( dCurrDist) && dCurrDist < dRefDist) {
|
|
dRefDist = dCurrDist ;
|
|
nInd = i ;
|
|
DistPtCrv.GetMinDistPoint( EPS_SMALL, ptCloser, nFlag) ;
|
|
}
|
|
}
|
|
// se non ho trovato alcun indice, salto il Chunk nC-esimo
|
|
if ( nInd == -1)
|
|
continue ;
|
|
|
|
// cerco sulla curva di Offset nInd-esima la sottocurva dove cade il punto a minima distanza
|
|
double dURef = 0 ;
|
|
if ( ! vOffs[nInd]->GetParamAtPoint( ptCloser, dURef))
|
|
continue ;
|
|
int nCrv = int( floor( dURef)) ;
|
|
const ICurve* pCrv = vOffs[nInd]->GetCurve( nCrv) ;
|
|
if ( pCrv == nullptr || ! pCrv->IsValid())
|
|
continue ;
|
|
|
|
// determino se la curva è di raccordo o meno
|
|
bool bIsSmoothCrv = ( PockParams.bSmooth && pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ;
|
|
|
|
// clono la curva di Offset corrente ( in questo modo non modifico l'originale)
|
|
PtrOwner<ICurveComposite> pMyOffs( CloneCurveComposite( vOffs[nInd])) ;
|
|
if ( IsNull( pMyOffs) || ! pMyOffs->IsValid())
|
|
return false ;
|
|
|
|
// versore uscente ed entrante dall'Offset attuale per creazione della curva a ricciolo
|
|
Vector3d vt1, vt2, vtMain ;
|
|
// punto di uscita e di ingresso dall'Offset attuale per creazione della curva a ricciolo
|
|
Point3d pt1, pt2, ptMain ;
|
|
// paramtro di uscita e di ingresso dall'Offset attuale per creazione della curva a ricciolo
|
|
double dURef1, dURef2 ;
|
|
// inizializzo la curva a ricciolo
|
|
PtrOwner<ICurveComposite> pCrvCurl( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvCurl))
|
|
return false ;
|
|
|
|
// se è di raccordo, "ricostruisco lo spigolo vivo della curva originale di Offset"
|
|
if ( bIsSmoothCrv) {
|
|
// cerco la prima curva in precedenza e in successione non di raccordo
|
|
vt1 = V_INVALID ;
|
|
for ( int i = nCrv - 1 ; i >= 0 ; -- i) {
|
|
const ICurve* pCrvPrec = pMyOffs->GetCurve( i) ;
|
|
if ( pCrvPrec == nullptr || ! pCrvPrec->IsValid())
|
|
break ;
|
|
if ( pCrvPrec->GetTempProp( 1) != TEMP_PROP_SMOOTH) {
|
|
// definisco il versore uscente
|
|
pCrvPrec->GetEndDir( vt1) ;
|
|
// definisco il punto di uscita
|
|
pCrvPrec->GetEndPoint( pt1) ;
|
|
// definisco il parametro di uscita
|
|
dURef1 = i + 1 ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( ! vt1.IsValid())
|
|
continue ;
|
|
vt2 = V_INVALID ;
|
|
for ( int i = nCrv + 1 ; i < pMyOffs->GetCurveCount() ; ++ i) {
|
|
const ICurve* pCrvSucc = pMyOffs->GetCurve( i) ;
|
|
if ( pCrvSucc == nullptr || ! pCrvSucc->IsValid())
|
|
break ;
|
|
if ( pCrvSucc->GetTempProp( 1) != TEMP_PROP_SMOOTH) {
|
|
// definisco il versore entrante
|
|
pCrvSucc->GetStartDir( vt2) ;
|
|
// definisco il punto di entrata
|
|
pCrvSucc->GetStartPoint( pt2) ;
|
|
// definisco il parametro di entrata
|
|
dURef2 = i ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( ! vt2.IsValid())
|
|
continue ;
|
|
// trovo il punto di intersezione tra le due direzioni tangenti applicate
|
|
const double EXTENSION_LEN = 1000. ;
|
|
// segmento iniziale -------
|
|
PtrOwner<ICurveLine> pLineS( CreateCurveLine()) ;
|
|
if ( IsNull( pLineS) || ! pLineS->Set( pt1, pt1 + vt1 * EXTENSION_LEN))
|
|
continue ;
|
|
// segmento finale --------
|
|
PtrOwner<ICurveLine> pLineE( CreateCurveLine()) ;
|
|
if ( IsNull( pLineE) || ! pLineE->Set( pt2, pt2 - vt2 * EXTENSION_LEN))
|
|
continue ;
|
|
// intersezione
|
|
IntersCurveCurve ILL( *pLineS, *pLineE) ;
|
|
if ( ILL.GetCrossIntersCount() == 1) {
|
|
IntCrvCrvInfo aInfo ;
|
|
if ( ILL.GetIntCrvCrvInfo( 0, aInfo)) {
|
|
if ( ! pCrvCurl->AddPoint( pt1) ||
|
|
! pCrvCurl->AddLine( aInfo.IciA[0].ptI))
|
|
continue ;
|
|
ptMain = aInfo.IciA[0].ptI ;
|
|
}
|
|
}
|
|
else
|
|
continue ;
|
|
}
|
|
// se invece non è di raccordo, allora ho già lo spigolo vivo
|
|
else {
|
|
// definisco il versore entrante ed uscente
|
|
// definisco il punto di entrata e di uscita ( sono coincidenti)
|
|
// definisco il parametro di entrata e di uscita ( sono coincidenti)
|
|
dURef1 = dURef ;
|
|
dURef2 = dURef ;
|
|
if ( ! vOffs[nInd]->GetPointD1D2( dURef1, ICurve::FROM_MINUS, ptMain, &vt1) ||
|
|
! vOffs[nInd]->GetPointD1D2( dURef2, ICurve::FROM_PLUS, ptMain, &vt2))
|
|
continue ;
|
|
vt1.Normalize() ; vt2.Normalize() ;
|
|
}
|
|
// se i due vettori sono tra loro paralleli...
|
|
bool bSameDir = AreSameVectorEpsilon( vt1, vt2, 2 * EPS_SMALL) ;
|
|
if ( bSameDir) {
|
|
vt1 = ( ptCentroid + ( PockParams.dSideStep / 4 * vt1)) - ptCloser ;
|
|
vt2 = ( ptCentroid - ( PockParams.dSideStep / 4 * vt2)) - ptCloser ;
|
|
vt2.Invert() ;
|
|
}
|
|
|
|
// calcolo la direzione tra pt1 e ptCentroid
|
|
Vector3d vtDir = ( ptCentroid - ptMain) ; vtDir.Normalize() ;
|
|
vtDir.Rotate( Z_AX, ANG_RIGHT) ;
|
|
double dAngMain ; bool bDet ;
|
|
vt1.GetRotation( vt2, Z_AX, dAngMain, bDet) ;
|
|
if ( dAngMain > - EPS_ZERO)
|
|
vtDir.Invert() ;
|
|
// calcolo i due Biarchi di raccordo
|
|
PtrOwner<ICurveComposite> pCrvBiArc1( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvBiArc2( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvBiArc1) || IsNull( pCrvBiArc2) ||
|
|
! CalcBoundedSmoothedLink( ptMain, vt1, ptCentroid, vtDir, 0.5, vOffsFirstCurve, PockParams, pCrvBiArc1) ||
|
|
! CalcBoundedSmoothedLink( ptCentroid, vtDir, ptMain, vt2, 0.5, vOffsFirstCurve, PockParams, pCrvBiArc2))
|
|
continue ;
|
|
|
|
// aggiorno la curva a ricciolo
|
|
if ( ! pCrvCurl->AddCurve( Release( pCrvBiArc1)) ||
|
|
! pCrvCurl->AddCurve( Release( pCrvBiArc2)) ||
|
|
! pCrvCurl->IsValid())
|
|
continue ;
|
|
|
|
// controllo se devo raccordarmi con Offset
|
|
if ( bIsSmoothCrv)
|
|
pCrvCurl->AddLine( pt2) ;
|
|
|
|
// controllo se tale curva effettivamente rimuove il Chunk nC-esimo
|
|
PtrOwner<ISurfFlatRegion> pSfrFatCurl( GetSurfFlatRegionFromFatCurve( pCrvCurl->Clone(), PockParams.dRad, false, false)) ;
|
|
if ( IsNull( pSfrFatCurl) || ! pSfrFatCurl->IsValid())
|
|
continue ;
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfrUncleared->CloneChunk( nC)) ;
|
|
if ( IsNull( pSfrFatCurl) || ! pSfrFatCurl->IsValid())
|
|
return false ;
|
|
pSfrChunk->Subtract( *pSfrFatCurl) ;
|
|
|
|
// se la curva a ricciolo non svuota il Chunk nC-esimo -> percorro il bordo di tale Chunk
|
|
if ( pSfrChunk->IsValid() || pSfrChunk->GetChunkCount() > 0) {
|
|
// so che il punto più vicino alla curva è ptCloser
|
|
// ricavo dunque il punto più vicino sul bordo del Chunk nC-esimo
|
|
PtrOwner<ICurveComposite> pCompoChunkBorder( ConvertCurveToComposite( pSfrUncleared->GetLoop( nC, 0))) ;
|
|
if ( IsNull( pCompoChunkBorder) || ! pCompoChunkBorder->IsValid())
|
|
return false ;
|
|
if ( PockParams.bInvert)
|
|
pCompoChunkBorder->Invert() ;
|
|
DistPointCurve distPtChunkBorder( ptCloser, *pCompoChunkBorder) ;
|
|
Point3d ptCloserOnChunk ;
|
|
if ( ! distPtChunkBorder.GetMinDistPoint( EPS_SMALL, ptCloserOnChunk, nFlag))
|
|
continue ;
|
|
// cambio il punto di inizio della curva su tale punto
|
|
double dUS = 0. ;
|
|
pCompoChunkBorder->GetParamAtPoint( ptCloserOnChunk, dUS) ;
|
|
pCompoChunkBorder->ChangeStartPoint( dUS) ;
|
|
/*
|
|
Creo due curve composite rappresentanti un piccolo tratto tangente sulla curva di
|
|
Offset attuale; questi servono per la partenza e l'arrivo dei biArchi
|
|
*/
|
|
PtrOwner<ICurveComposite> pCompoHelpS( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoHelpS) || ! pCompoHelpS->AddPoint( pt1 - vt1 / 4) ||
|
|
! vt1.Normalize() || ! pCompoHelpS->AddLine( pt1))
|
|
continue ;
|
|
PtrOwner<ICurveComposite> pCompoHelpE( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoHelpE) || ! pCompoHelpE->AddPoint( pt2) ||
|
|
! vt2.Normalize() || ! pCompoHelpE->AddLine( pt2 + vt2 / 4))
|
|
continue ;
|
|
/*
|
|
creo il Biarco iniziale
|
|
( nel caso non si riuscisse diventa un segmento lineare)
|
|
*/
|
|
PtrOwner<ICurveComposite> pCrvBiArc1( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvBiArc1))
|
|
return false ;
|
|
if ( ! CutCurveToConnect( pCompoHelpS, pCompoChunkBorder, vOffsFirstCurve, PockParams,
|
|
0, 10 * EPS_SMALL, pCrvBiArc1)) {
|
|
if ( ! CalcBoundedLink( ptCloser, ptCloserOnChunk, vOffsFirstCurve, pCrvBiArc1))
|
|
continue ;
|
|
}
|
|
// aggiorno i nuovi punti e i nuovi parametri
|
|
double dMyU ;
|
|
Point3d ptMyPoint ;
|
|
if ( ! pCrvBiArc1->GetEndPoint( ptMyPoint) ||
|
|
! pCompoChunkBorder->GetParamAtPoint( ptMyPoint, dMyU))
|
|
continue ;
|
|
// accorgio il bordo dell'Offset in raccordo al BiArco iniziale
|
|
if ( dMyU > EPS_SMALL)
|
|
pCompoChunkBorder->TrimStartAtParam( dMyU) ;
|
|
/*
|
|
creo il Biarco finale
|
|
( nel caso non si riuscisse diventa un segmento lineare)
|
|
*/
|
|
// creo il Biarco iniziale ( nel caso non si riuscisse diventa un segmento lineare)
|
|
PtrOwner<ICurveComposite> pCrvBiArc2( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvBiArc2))
|
|
return false ;
|
|
if ( ! CutCurveToConnect( pCompoChunkBorder, pCompoHelpE, vOffsFirstCurve, PockParams,
|
|
0., 0., pCrvBiArc2)) {
|
|
if ( ! CalcBoundedLink( ptCloserOnChunk, ptCloser, vOffsFirstCurve, pCrvBiArc2))
|
|
continue ;
|
|
}
|
|
// aggiorno i nuovi punti e i nuovi parametri
|
|
if ( ! pCrvBiArc2->GetStartPoint( ptMyPoint) ||
|
|
! pCompoChunkBorder->GetParamAtPoint( ptMyPoint, dMyU))
|
|
continue ;
|
|
// la curva a ricciolo ora diventa l'unione di pCrvBiArc1 - pCompoChunkBorder - pCrvBiArc2
|
|
pCrvCurl->Clear() ;
|
|
// smusso la curva di bordo del Chunk ( è aperta)
|
|
double dSmoothPar = PockParams.dRad / 8. ;
|
|
ModifyCurveToSmoothed( pCompoChunkBorder, PockParams, dSmoothPar, dSmoothPar, false) ;
|
|
if ( ! pCrvCurl->AddCurve( Release( pCrvBiArc1)) ||
|
|
! pCrvCurl->AddCurve( Release( pCompoChunkBorder)) ||
|
|
! pCrvCurl->AddCurve( Release( pCrvBiArc2)))
|
|
continue ;
|
|
}
|
|
|
|
// attribuisco alla curva a ricciolo la Feed minima
|
|
AssignMinFeed( pCrvCurl, PockParams) ;
|
|
|
|
// aggiorno il nuovo Offset aggiungendo la curva a ricciolo
|
|
PtrOwner<ICurveComposite> pCrvTest( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvTest))
|
|
return false ;
|
|
// aggiungo tratto iniziale di Offset
|
|
pCrvTest->AddCurve( ConvertCurveToBasicComposite( pMyOffs->CopyParamRange( 0, dURef1))) ;
|
|
// aggiungo la curva a ricciolo
|
|
if ( ! pCrvTest->AddCurve( Release( pCrvCurl)))
|
|
continue ;
|
|
// aggiungo il tratto finale di Offset
|
|
pCrvTest->AddCurve( ConvertCurveToBasicComposite( pMyOffs->CopyParamRange( dURef2, pMyOffs->GetCurveCount()))) ;
|
|
// nel caso di direzioni parallele manca uno smusso
|
|
if ( bSameDir) {
|
|
double dSmoothPar = PockParams.dRad / 8. ;
|
|
ModifyCurveToSmoothed( pCrvTest, PockParams, dSmoothPar, dSmoothPar, false) ;
|
|
}
|
|
// provo a controllare che i punti inzili e finali coincidano, in caso aggiorno il nuovo Offset
|
|
Point3d ptStart_Old ; vOffs[nInd]->GetStartPoint( ptStart_Old) ;
|
|
Point3d ptEnd_Old ; vOffs[nInd]->GetEndPoint( ptEnd_Old) ;
|
|
Point3d ptStart_New ; pCrvTest->GetStartPoint( ptStart_New) ;
|
|
Point3d ptEnd_New ; pCrvTest->GetEndPoint( ptEnd_New) ;
|
|
if ( AreSamePointApprox( ptStart_Old, ptStart_New) && AreSamePointApprox( ptEnd_Old, ptEnd_New)) {
|
|
pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 0), 0) ;
|
|
pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 1), 1) ;
|
|
vOffs[nInd].Set( pCrvTest) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, const PocketParams& PockParams,
|
|
const ICRVCOMPOPOVECTOR& vOffsFirstCurve)
|
|
{
|
|
// controllo dei parametri
|
|
if ( vOffs.empty() || vOffsFirstCurve.empty())
|
|
return false ;
|
|
vLinks.clear() ;
|
|
vLinks.resize( int( vOffs.size())) ;
|
|
|
|
// scorro tutte le curva di Offset
|
|
for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) {
|
|
|
|
// ricavo il punto inziale della curva corrente
|
|
Point3d ptS ;
|
|
if ( ! vOffs[i]->GetStartPoint( ptS))
|
|
return false ;
|
|
|
|
int nNextInd = -1 ; // indice della curva successiva
|
|
double dMinDist = INFINITO ; // distanza tra questa curva e la successiva
|
|
Point3d ptStartNext ; // punto iniziale della curva successiva
|
|
|
|
// tra le curva successive cerco la curva interna e più vicina ad essa
|
|
for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) {
|
|
IntersCurveCurve IntCC( *vOffs[i], *vOffs[j]) ;
|
|
CRVCVECTOR ccClass ;
|
|
// se interna
|
|
if ( IntCC.GetCurveClassification( 1, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) {
|
|
// calcolo la distanza minima tra essa
|
|
int nFlag ;
|
|
Point3d ptClosest ;
|
|
if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) {
|
|
double dCurrDist = SqDist( ptS, ptClosest) ;
|
|
if ( dCurrDist < dMinDist) {
|
|
dMinDist = dCurrDist ;
|
|
nNextInd = j ;
|
|
ptStartNext = ptClosest ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se non ho trovato nessuna curva interna... cerco semplicemente la curva più vicina
|
|
if ( nNextInd == -1) {
|
|
for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) {
|
|
int nFlag ;
|
|
Point3d ptClosest ;
|
|
if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) {
|
|
double dCurrDist = SqDist( ptS, ptClosest) ;
|
|
if ( dCurrDist < dMinDist) {
|
|
dMinDist = dCurrDist ;
|
|
nNextInd = j ;
|
|
ptStartNext = ptClosest ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se non ho trovato nessuna curva, errore
|
|
if ( nNextInd == -1)
|
|
return false ;
|
|
|
|
// scambio la curva i+1 esima con la j-esima ( se non sono già in ordine)
|
|
if ( nNextInd != i + 1)
|
|
swap( vOffs[nNextInd], vOffs[i+1]) ;
|
|
|
|
// cambio il suo punto iniziale nel punto più vicino tovato
|
|
double dUS ;
|
|
if ( ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUS, EPS_SMALL))
|
|
return false ;
|
|
vOffs[i+1]->ChangeStartPoint( dUS) ;
|
|
|
|
// accorcio la curva per raccordarmi in tangenza con la successiva
|
|
if ( int( vOffs.size()) > 1) {
|
|
// clono le curve i ed i+1 esime ( nel caso non riuscissi ad accorciarle o raccordarle )
|
|
PtrOwner<ICurveComposite> pCrv_i( vOffs[i]->Clone()) ;
|
|
PtrOwner<ICurveComposite> pCrv_ii( vOffs[i+1]->Clone()) ;
|
|
if ( IsNull( pCrv_i) || IsNull( pCrv_ii) || ! pCrv_i->IsValid() || ! pCrv_ii->IsValid())
|
|
return false ;
|
|
// creo la curva che le collegherà
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
// NB. Le curve di Offset da tagliare non devono essere quelle di primo Offset
|
|
if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsFirstCurve, PockParams,
|
|
( i == 0 ? 0. : 10 * EPS_SMALL), 10 * EPS_SMALL, pCrvLink) ||
|
|
! pCrvLink->IsValid()) {
|
|
// se non sono riuscito, cerco una strada più semplice
|
|
// ripristino le curve
|
|
pCrvLink->Clear() ;
|
|
vOffs[i].Set( pCrv_i) ; // chiuso
|
|
vOffs[i+1].Set( pCrv_ii) ; // chiuso
|
|
// recupero i vettori tangente iniziali ( le curve sono chiuse )
|
|
Vector3d vtS, vtE ;
|
|
if ( ! vOffs[i]->GetStartDir( vtS) || ! vOffs[i+1]->GetStartDir( vtE))
|
|
return false ;
|
|
// creo il bi-arco tra esse
|
|
if ( CalcBoundedSmoothedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink))
|
|
vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento
|
|
else
|
|
return false ;
|
|
|
|
continue ; // passo alla curva i+1 esima successiva
|
|
}
|
|
|
|
// per sicurezza aggiorno i nuovi punti e i nuovi parametri
|
|
double dUNewS, dUNewE ;
|
|
if ( ! pCrvLink->GetStartPoint( ptS) ||
|
|
! pCrvLink->GetEndPoint( ptStartNext) ||
|
|
! vOffs[i]->GetParamAtPoint( ptS, dUNewS) ||
|
|
! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUNewE))
|
|
return false ;
|
|
|
|
// imposto il punto iniziale della curva successiva ( i+1 esima)
|
|
vOffs[i+1]->ChangeStartPoint( dUNewE) ;
|
|
PtrOwner<ICurveComposite> pCrvNewOffs( CloneCurveComposite( vOffs[i])) ;
|
|
if ( dUNewS > EPS_SMALL) {
|
|
pCrvNewOffs.Set( ConvertCurveToComposite( vOffs[i]->CopyParamRange( 0, dUNewS))) ;
|
|
// sostituisco la curva i esima con quella tagliata
|
|
vOffs[i].Set( pCrvNewOffs) ;
|
|
}
|
|
|
|
// aggiorno il collegamento
|
|
vLinks[i+1].Set( pCrvLink) ;
|
|
}
|
|
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs,
|
|
const int nIter, const PocketParams& PockParams, bool& bInsert)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0)
|
|
return false ;
|
|
bInsert = true ; // di base inserisco
|
|
|
|
// controllo se richiesta ottimizzazione sul numero di Offsets
|
|
if ( ! PockParams.bOptOffsets)
|
|
return true ;
|
|
|
|
if ( nIter != 0) { // controllo se sono almeno al secondo offset...
|
|
// per ogni curva controllo se il suo Offset massimo non genera alcuna regione
|
|
double dMaxOffs ;
|
|
if ( ! CalcCurveLimitOffset( *pCrvOffs, dMaxOffs))
|
|
return false ;
|
|
// se questa curva sparisce -> non serve inserirla come offset, non svuota parti aggiuntive
|
|
if ( dMaxOffs < PockParams.dRad - dOffs + 5 * EPS_SMALL) {
|
|
bInsert = false ;
|
|
return true ;
|
|
}
|
|
}
|
|
else
|
|
return true ;
|
|
|
|
// se non richiesto percorso avanzato
|
|
if ( ! PockParams.bOptOffsetsAdv)
|
|
return true ;
|
|
|
|
OffsetCurve OffsCrv ; // considero anche i casi in cui ho bSmallRad
|
|
if ( ! OffsCrv.Make( pCrvOffs, - PockParams.dRad + dOffs - 5 * EPS_SMALL , ICurve::OFF_FILLET))
|
|
return false ;
|
|
|
|
// controllo se richista ottimizzazione per Offsets mediante centroidi e medial Axis
|
|
if ( OffsCrv.GetCurveCount() > 1)
|
|
return true ;
|
|
|
|
PtrOwner<ICurve> pCrvOffLonger( OffsCrv.GetLongerCurve()) ;
|
|
if ( IsNull( pCrvOffLonger) || ! pCrvOffLonger->IsValid())
|
|
return true ;
|
|
|
|
// Immagine del tool con centro nel centroide della zona da svuotare
|
|
Point3d ptC ; pCrvOffLonger->GetCentroid( ptC) ;
|
|
PtrOwner<ICurveArc> pCrvTool( CreateCurveArc()) ;
|
|
if( IsNull( pCrvTool))
|
|
return false ;
|
|
pCrvTool->SetXY( ptC, PockParams.dRad - 5 * EPS_SMALL) ;
|
|
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pCrvOffLonger, *pCrvTool) ;
|
|
intCC.GetCurveClassification( 1, EPS_SMALL, ccClass) ;
|
|
if ( int(ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) {
|
|
// centroide
|
|
bInsert = false ;
|
|
return true ;
|
|
}
|
|
|
|
// recupero la PolyLine per altezza e lunghezza del Box2D ( rettangolo)
|
|
PolyLine pl ;
|
|
if ( ! pCrvOffLonger->ApproxWithLines( 100 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, pl))
|
|
return true ;
|
|
Vector3d vtX ;
|
|
double dLen = 0 ; double dHeight = 0 ;
|
|
if ( ! pl.GetMinAreaRectangleXY( ptC, vtX, dLen, dHeight))
|
|
return true ;
|
|
|
|
// frame locale, recupero DimX e DimY
|
|
Frame3d frLoc ; frLoc.Set( ptC, Z_AX, vtX) ;
|
|
if ( ! frLoc.IsValid())
|
|
return true ;
|
|
BBox3d bBox ;
|
|
frLoc.Invert() ;
|
|
pCrvOffLonger->GetBBox( frLoc, bBox) ;
|
|
double dDimX = bBox.GetDimX() ;
|
|
double dDimY = bBox.GetDimY() ;
|
|
|
|
// se dimensioni accettabili, inserisco
|
|
if ( dDimX < 2 * PockParams.dRad - 100 * EPS_SMALL ||
|
|
dDimY < 2 * 2 * PockParams.dRad - 100 * EPS_SMALL) {
|
|
bInsert = false ;
|
|
return true ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
IsCompoMadeBy2DifferentHomogeneousParts( const ICurveComposite* pCompo, const PocketParams& PockParams,
|
|
bool& bOk, ICRVCOMPOPOVECTOR& vpCrvs)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCompo == nullptr || ! pCompo->IsValid())
|
|
return false ;
|
|
bOk = false ;
|
|
vpCrvs.clear() ;
|
|
|
|
// clono la curva composita ( cambierà il suo punto iniziale)
|
|
PtrOwner<ICurveComposite> pCompoCL( CloneCurveComposite( pCompo)) ;
|
|
if ( IsNull( pCompoCL) || ! pCompoCL->IsValid())
|
|
return false ;
|
|
|
|
// recupero i tratti con proprietà uniformi
|
|
GetHomogeneousParts( pCompoCL, PockParams, vpCrvs) ;
|
|
if ( vpCrvs.size() > 1) {
|
|
// unisco il primo e l'ultimo se estremi compatibili
|
|
Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ;
|
|
Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ;
|
|
if ( AreSamePointApprox( ptS, ptE)) {
|
|
vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ;
|
|
vpCrvs.erase( vpCrvs.end() - 1) ;
|
|
}
|
|
}
|
|
|
|
// controllo che la curva abbia esattamente due tratti omogenei
|
|
bOk = ( int( vpCrvs.size()) == 2) ;
|
|
if ( ! bOk)
|
|
vpCrvs.clear() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcSpiral( const ISurfFlatRegion* pSrfPock, const PocketParams& PockParams, int& nReg, Point3d& ptStart,
|
|
ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops, ICurveComposite* pMCrv, bool& bOptimizedTrap,
|
|
bool& bMidOut, Vector3d& vtMidOut)
|
|
{
|
|
// inizializzo il percorso come vuoto
|
|
pMCrv->Clear() ;
|
|
|
|
// Offset corrente ( il primo è definito dal raggio utensile)
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
|
|
// recupero il versore normale della superficie, ruotandola nel piano XY
|
|
PtrOwner<ISurfFlatRegion> pSrfToWork( pSrfPock->Clone()) ;
|
|
if ( IsNull( pSrfToWork) || pSrfToWork->GetChunkCount() == 0)
|
|
return false;
|
|
|
|
// ciclo di offset verso l'interno
|
|
const int MAX_ITER = 1000 ;
|
|
int nIter = 0 ;
|
|
ICRVCOMPOPOVECTOR vOffs ; // vettore delle curve di offset
|
|
ICRVCOMPOPOVECTOR vOffsFirstCurve ; // curve di primo offset
|
|
|
|
PtrOwner<ISurfFlatRegion> pSrfAct( CloneSurfFlatRegion( pSrfToWork)) ; // regione attuale
|
|
if ( IsNull( pSrfAct) || pSrfAct->GetChunkCount() == 0)
|
|
return false ;
|
|
|
|
double dOffsPrec = 0. ;
|
|
while ( nIter < MAX_ITER) {
|
|
// ricavo la regione piana da VRONI
|
|
PtrOwner<ISurfFlatRegion> pSfrOffsVR( pSrfAct->CreateOffsetSurf( - dOffs, ICurve::OFF_FILLET)) ;
|
|
if ( IsNull( pSfrOffsVR))
|
|
return false ;
|
|
if ( ! pSfrOffsVR->IsValid()) {
|
|
pSfrOffsVR.Set( pSrfAct->CreateOffsetSurf( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) ;
|
|
if ( IsNull( pSfrOffsVR))
|
|
return false ;
|
|
}
|
|
|
|
// se primo Offset
|
|
if ( nIter == 0) {
|
|
int my_nReg = nReg ;
|
|
nReg = pSfrOffsVR->GetChunkCount() ; // aggiorno il nuovo valore delle regioni totali di primo Offset
|
|
// gli Offset progressivi appartengono al Chunk nReg-esimo
|
|
pSrfAct.Set( pSfrOffsVR->CloneChunk( my_nReg)) ;
|
|
if ( IsNull( pSrfAct)) // se supero i chunk ottenuti
|
|
return true ;
|
|
pSfrOffsVR.Set( pSrfAct->Clone()) ;
|
|
}
|
|
|
|
// numero di Chunk attuali ( alla prima iterazione è l'nReg-esimo)
|
|
int nChunks = pSfrOffsVR->GetChunkCount() ;
|
|
|
|
for ( int i = 0 ; i < nChunks ; ++ i) {
|
|
// per ogni chunk...
|
|
int nLoops = pSfrOffsVR->GetLoopCount( i) ;
|
|
for ( int j = 0 ; j < nLoops ; ++ j) {
|
|
// per ogni loop...
|
|
PtrOwner<ICurveComposite> pCrvCompoBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ;
|
|
if ( IsNull( pCrvCompoBorder) || ! pCrvCompoBorder->IsValid())
|
|
return false ;
|
|
/*
|
|
Il materiale deve sempre trovarsi alla destra ( se bInvert -> alla sinistra) del tool
|
|
soltanto sul primo bordo; Nel caso di isole aperte o nelle passate di Offset intermedio
|
|
l'orientamente non ha importanza.
|
|
*/
|
|
if ( nIter != 0 && j > 0) // inverto l'orientamento delle curve interne ( offset delle isole trovate)
|
|
pCrvCompoBorder->Invert() ;
|
|
// controllo quali regioni di Offset possono essere sostituite
|
|
bool bInsert = true ;
|
|
if ( ! CheckIfOffsetIsNecessary( pCrvCompoBorder, dOffs - dOffsPrec, nIter, PockParams, bInsert))
|
|
return false ;
|
|
if ( bInsert)
|
|
vOffs.emplace_back( Release( pCrvCompoBorder)) ;
|
|
if ( nIter == 0) { // salvo il bordo per i link ( non invertiti, devo sapere IN/OUT)
|
|
PtrOwner<ICurveComposite> pCrvCompoExtBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ;
|
|
vOffsFirstCurve.emplace_back( Release( pCrvCompoExtBorder)) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// controllo se serve un raggio più piccolo di svuotatura
|
|
bool bSmallRad = ( nIter == 0 ? dOffs < PockParams.dRad + EPS_ZERO : dOffs - dOffsPrec < PockParams.dRad + EPS_ZERO) ;
|
|
|
|
// se ho trovato dei chunk, allora aggiorno l'Offset per la passata successiva
|
|
if ( nChunks > 0) {
|
|
// memorizzo il valore di Offset per l'iterazione successiva
|
|
if ( nIter != 0) {
|
|
dOffsPrec = dOffs ;
|
|
dOffs += PockParams.dSideStep ;
|
|
}
|
|
else {
|
|
dOffsPrec = 0. ;
|
|
dOffs = PockParams.dSideStep ;
|
|
}
|
|
}
|
|
// se devo usare un Offset più piccolo...
|
|
else if ( ! bSmallRad)
|
|
dOffs = dOffsPrec + ( nIter == 0 ? PockParams.dRad + PockParams.dRadialOffset : PockParams.dRad) ;
|
|
// altrimenti ho finito la regione da svuotare...
|
|
else
|
|
break ;
|
|
|
|
++ nIter ; // aggiorno iterazione
|
|
}
|
|
|
|
// se non ho trovato curve di Offset allora esco
|
|
if ( vOffs.empty())
|
|
return true ;
|
|
|
|
// cambio il punto iniziale della prima Curva di Offset
|
|
Point3d ptRef = PockParams.ptStart ;
|
|
Point3d ptNewStart ;
|
|
|
|
// Se ho come lavorazione uno SpiralIn posso poter entrare dalle isole aperte...
|
|
int nIndexSwap = 0 ; // indice del vettore di curve per identificare la curva su cui entrare
|
|
if ( PockParams.nType == POCKET_SPIRALIN) {
|
|
if ( SetAdvancedPtStartForPath( vOffsFirstCurve, PockParams, pSrfToWork, ptRef, ptStart,
|
|
vtMidOut, bMidOut, nIndexSwap, vCrvOrigChunkLoops))
|
|
vOffsFirstCurve[nIndexSwap]->GetStartPoint( ptNewStart) ;
|
|
else
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( SetPtStartForPath( vOffs[0], PockParams, pSrfToWork, ptRef, ptStart, vtMidOut, bMidOut,
|
|
vCrvOrigChunkLoops[0]))
|
|
vOffs[0]->GetStartPoint( ptNewStart) ;
|
|
else
|
|
return false ;
|
|
}
|
|
|
|
// se richiesta inversione
|
|
for ( int i = 0 ; i < int( vOffs.size()) && PockParams.bInvert ; ++ i)
|
|
vOffs[i]->Invert() ;
|
|
|
|
// smusso le curve di offset ( ad eccezione della prima; le isole sono automaticamente smussate)
|
|
ICRVCOMPOPOVECTOR vOffsClosedCurves( vOffs.size()) ; // vettore con tutte le curve di Offset Chiuse
|
|
double dSmoothPar = PockParams.dRad / 8 ;
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
|
|
if ( i != 0)
|
|
ModifyCurveToSmoothed( vOffs[i], PockParams, dSmoothPar, dSmoothPar, false) ;
|
|
vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
vOffsClosedCurves[i].Set( vOffs[i]->Clone()) ;
|
|
}
|
|
|
|
// setto il punto iniziale della svuotatura
|
|
double dNewUS ;
|
|
if ( nIndexSwap != 0)
|
|
swap( vOffs[0], vOffs[nIndexSwap]) ; // se entro da un'isola aperta
|
|
vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ;
|
|
vOffs[0]->ChangeStartPoint( dNewUS) ;
|
|
vOffs[0]->SetTempParam( 0., 0) ; // prima iterazione
|
|
|
|
// riordino le curve e creo i collegamenti
|
|
/*
|
|
Se richiesto parametro di smusso, tutti gli Offset ( ad eccezione di quelli generati alla
|
|
prima iterazione vengono tagliati, quindi diventano aperti
|
|
*/
|
|
ICURVEPOVECTOR vLinks( vOffs.size()) ;
|
|
if ( ! CreateSpiralPocketingPath( vOffs, vLinks, PockParams, vOffsFirstCurve))
|
|
return false ;
|
|
|
|
// controllo eventuali parti non svuotate ( e setto la Feed degli Offset e dei Link)...
|
|
PtrOwner<ISurfFlatRegion> pSfrUncleared( CreateSurfFlatRegion()) ;
|
|
if ( GetUnclearedRegionAndSetFeed( vOffsFirstCurve, vOffs, vLinks, vCrvOrigChunkLoops[0], PockParams, pSfrUncleared)) {
|
|
// Modifico i percorsi
|
|
if ( ! RemoveExtraParts( pSfrUncleared, vOffs, vOffsFirstCurve, PockParams))
|
|
return false ;
|
|
}
|
|
|
|
// creo il percorso di lavoro a partire dalla raccolta degli offset e dei collegamenti
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++i) {
|
|
// se collegamento da aggiungere
|
|
if ( ! IsNull( vLinks[i])) {
|
|
// accodo nel percorso di lavorazione
|
|
if ( ! pMCrv->AddCurve( Release( vLinks[i]))) {
|
|
return false ;
|
|
}
|
|
}
|
|
pMCrv->AddCurve( Release( vOffs[i])) ;
|
|
}
|
|
|
|
// semplifico la curva ottenuta
|
|
SimplifyCurveByFeeds( pMCrv, PockParams, 10 * EPS_SMALL) ;
|
|
|
|
// verifico il percorso di lavoro
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// reset delle proprietà temporanee delle curve componenti
|
|
for ( int i = 0 ; i < pMCrv->GetCurveCount() ; ++ i) {
|
|
pMCrv->SetCurveTempProp( i, 0, 0) ;
|
|
pMCrv->SetCurveTempProp( i, 0, 1) ;
|
|
}
|
|
|
|
// setto estrusione
|
|
pMCrv->SetExtrusion( Z_AX) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
static bool
|
|
CalcZigZagLink( ICurveComposite* pCrv1, ICurveComposite* pCrv2, const PocketParams& PockParams,
|
|
ICurveComposite* pCrvLink)
|
|
{
|
|
// se non richiesto, esco
|
|
if ( ! PockParams.bSmooth)
|
|
return true ;
|
|
|
|
// controllo dei parametri
|
|
if ( pCrv1 == nullptr || pCrv2 == nullptr)
|
|
return false ;
|
|
pCrvLink->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
|
|
// prendo i parametri per il raccordo
|
|
Vector3d vtS, vtE, vtH ;
|
|
pCrv1->GetEndDir( vtS) ;
|
|
pCrv2->GetStartDir( vtE) ;
|
|
Point3d ptS, ptE, ptE1, ptS2 ;
|
|
pCrv1->GetEndPoint( ptS) ;
|
|
pCrv2->GetStartPoint( ptE) ;
|
|
vtH = ptE - ptS ;
|
|
double dLen1, dLen2 ;
|
|
pCrv1->GetLength( dLen1) ;
|
|
pCrv2->GetLength( dLen2) ;
|
|
double dUS1, dUE1, dUS2, dUE2 ;
|
|
pCrv1->GetDomain( dUS1, dUE1) ;
|
|
pCrv2->GetDomain( dUS2, dUE2) ;
|
|
double dAngle, dAngleH ;
|
|
double dDist = Dist( ptS, ptE) ;
|
|
|
|
// curve per controllo validità del collegamento
|
|
PtrOwner<ICurveComposite> pCrvH1( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvH2( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvTempLink( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvTest( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvH1) || IsNull( pCrvH2) || IsNull( pCrvTempLink) || IsNull( pCrvTest))
|
|
return false ;
|
|
|
|
if ( vtS.GetAngle( vtE, dAngle) &&
|
|
abs( dAngle) < ANG_STRAIGHT + 5 * EPS_SMALL && abs( dAngle) > ANG_STRAIGHT - 5 * EPS_SMALL &&
|
|
vtH.GetAngle( vtE, dAngleH) &&
|
|
abs( dAngleH) < ANG_RIGHT + 5 * EPS_SMALL && abs( dAngleH) > ANG_RIGHT - 5 * EPS_SMALL &&
|
|
dLen1 > dDist && dLen2 > dDist &&
|
|
pCrvLink->GetCurveCount() == 1 && pCrvLink->GetFirstCurve()->GetType() == CRV_LINE) {
|
|
|
|
// creo una semicirconferenza
|
|
double dRad = dDist / 2 ; // raggio
|
|
Point3d ptNS, ptNE ;
|
|
pCrv1->GetParamAtLength( dLen1 - dRad, dUE1) ;
|
|
pCrv2->GetParamAtLength( dRad, dUS2) ;
|
|
pCrv1->GetPointD1D2( dUE1, ICurve::FROM_MINUS, ptNS) ; // parametro di intersezione su curva 1
|
|
pCrv2->GetPointD1D2( dUS2, ICurve::FROM_PLUS, ptNE) ; // parametro di intersezione su curva 2
|
|
|
|
PtrOwner<ICurveArc> pSemiCir( CreateCurveArc()) ;
|
|
if ( pSemiCir->Set2PVN( ptNS, ptNE, vtS, Z_AX)) {
|
|
pCrvH1.Set( GetCurveComposite( pCrv1->CopyParamRange( dUS1, dUE1))) ;
|
|
pCrvH2.Set( GetCurveComposite( pCrv2->CopyParamRange( dUS2, dUE2))) ;
|
|
pCrvTempLink->AddCurve( pSemiCir->Clone()) ;
|
|
}
|
|
|
|
// controllo finale sui raccordi ammissibili ...
|
|
if ( pCrvTest->AddCurve( pCrvH1->Clone()) && pCrvTest->AddCurve( pCrvTempLink->Clone()) &&
|
|
pCrvTest->AddCurve( pCrvH2->Clone())) {
|
|
|
|
if ( ! pCrv1->TrimEndAtParam( dUE1))
|
|
pCrv1->Clear() ;
|
|
pCrvLink->Clear() ;
|
|
pCrvLink->AddCurve( Release( pCrvTempLink)) ;
|
|
if ( ! pCrv2->TrimStartAtParam( dUS2))
|
|
pCrv2->Clear() ;
|
|
return true ;
|
|
}
|
|
}
|
|
else {
|
|
// devo smussare la curva formata da pCrv1 - pCrvLink ( da creare) - pCrv2
|
|
pCrvH1->AddCurve( pCrv1->Clone()) ;
|
|
pCrvH2->AddCurve( pCrv2->Clone()) ;
|
|
pCrvTempLink->AddCurve( pCrvLink->Clone()) ;
|
|
|
|
// prendo le lunghezze necessarie
|
|
double dLenSeg1 = EPS_SMALL ; double dLenSeg2 = EPS_SMALL ;
|
|
double dLenI_s = EPS_SMALL ; // lunghezza prima curva del Link
|
|
double dLenI_e = EPS_SMALL ; // lunghezza ultima curva del Link
|
|
pCrvH1->GetLength( dLenSeg1) ;
|
|
pCrvH2->GetLength( dLenSeg2) ;
|
|
pCrvTempLink->GetFirstCurve()->GetLength( dLenI_s) ;
|
|
pCrvTempLink->GetLastCurve()->GetLength( dLenI_e) ;
|
|
|
|
// raccordo pSeg1 con pCrvLink
|
|
double dTollLeft = 1 - ( dLen1 - ( ( 2 * PockParams.dRad) / 16)) / dLen1 ; // % parametro sinistro per smusso
|
|
double dTollRight = ( ( 2 * PockParams.dRad) / 16) / dLenI_s ; // % parametro destro di smusso
|
|
PtrOwner<ICurveComposite> pCrv_Seg1_Isl( CreateCurveComposite()) ; // curva unione di pSeg1 e pCrvLink smussata
|
|
pCrv_Seg1_Isl->AddCurve( pCrvH1->Clone()) ;
|
|
pCrv_Seg1_Isl->AddCurve( pCrvTempLink->Clone()) ;
|
|
ModifyCurveToSmoothed( pCrv_Seg1_Isl, PockParams, dTollLeft, dTollRight, true) ;
|
|
|
|
// raccordo questa curva con pSeg2
|
|
double dUS_1IH, dUE_1IH, dToTLen ;
|
|
pCrv_Seg1_Isl->GetDomain( dUS_1IH, dUE_1IH) ; // dominio della curva smussata tra pSeg1 e PCrvLink
|
|
pCrv_Seg1_Isl->GetLength( dToTLen) ; // nuova lunghezza della curva smussata tra pSeg1 e pCrvLink
|
|
dTollLeft = 1 - ( dLenI_e - ( ( 2 * PockParams.dRad) / 16)) / dLenI_e ; // % paramtro sinistro per smusso
|
|
dTollRight = ( ( 2 * PockParams.dRad) / 16) / dLen2 ; // % parametro destro per smusso
|
|
PtrOwner<ICurveComposite> pCrv_smoothed( CreateCurveComposite()) ; // curva unione del primo smusso con pSeg2
|
|
pCrv_smoothed->AddCurve( pCrv_Seg1_Isl->Clone()) ;
|
|
pCrv_smoothed->AddCurve( pCrvH2->Clone()) ;
|
|
ModifyCurveToSmoothed( pCrv_smoothed, PockParams, dTollLeft, dTollRight, true) ;
|
|
|
|
// recupero i parametri del nuovo collegamento che si è creato dallo smusso
|
|
pCrvH1->Clear() ;
|
|
pCrvTempLink->Clear() ;
|
|
pCrvH2->Clear() ;
|
|
int nStat = -1 ;
|
|
for ( int u = 0 ; u < pCrv_smoothed->GetCurveCount() ; ++ u) {
|
|
// recupero la curva u-esima
|
|
const ICurve* pCrv = pCrv_smoothed->GetCurve( u) ;
|
|
if ( pCrv->GetType() == CRV_LINE) {
|
|
if ( CheckSimpleOverlap( pCrv, pCrv1, nStat, EPS_SMALL) && nStat == 1)
|
|
pCrvH1->AddCurve( pCrv->Clone()) ;
|
|
else if ( CheckSimpleOverlap( pCrv, pCrv2, nStat, EPS_SMALL) && nStat == 1)
|
|
pCrvH2->AddCurve( pCrv->Clone()) ;
|
|
}
|
|
}
|
|
|
|
Point3d ptCrv1_e, ptCrv2_s ;
|
|
double dULink_s = EPS_SMALL ;
|
|
double dULink_e = EPS_SMALL ;
|
|
|
|
pCrvH1->GetEndPoint( ptCrv1_e) ;
|
|
pCrv1->GetParamAtPoint( ptCrv1_e, dULink_s) ;
|
|
if ( ! pCrv1->TrimEndAtParam( dULink_s))
|
|
pCrv1->Clear() ;
|
|
|
|
pCrvH2->GetStartPoint( ptCrv2_s) ;
|
|
pCrv2->GetParamAtPoint( ptCrv2_s, dULink_e) ;
|
|
if ( ! pCrv2->TrimStartAtParam( dULink_e))
|
|
pCrv2->Clear() ;
|
|
|
|
pCrv_smoothed->GetParamAtPoint( ptCrv1_e, dULink_s) ;
|
|
pCrv_smoothed->GetParamAtPoint( ptCrv2_s, dULink_e) ;
|
|
pCrvLink->Clear() ;
|
|
pCrvLink->AddCurve( GetCurveComposite( pCrv_smoothed->CopyParamRange( dULink_s, dULink_e))) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcZigZag( const ISurfFlatRegion* pSrfZigZag, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vpCrvs,
|
|
bool bFromInfill)
|
|
{
|
|
|
|
// check parametri
|
|
if ( pSrfZigZag == nullptr || ! pSrfZigZag->IsValid())
|
|
return true ;
|
|
|
|
// creo il vettore dei contorni
|
|
ICRVCOMPOPOVECTOR vFirstOff ;
|
|
for ( int c = 0 ; c < pSrfZigZag->GetChunkCount() ; ++ c)
|
|
for ( int l = 0 ; l < pSrfZigZag->GetLoopCount( c) ; ++ l)
|
|
vFirstOff.emplace_back( GetCurveComposite( pSrfZigZag->GetLoop( c, l))) ;
|
|
|
|
// ingombro del contorno offsettato
|
|
BBox3d b3Pocket ;
|
|
vFirstOff[0]->GetLocalBBox( b3Pocket) ;
|
|
Point3d ptMin ; double dDimX, dDimY, dDimZ ;
|
|
b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ;
|
|
|
|
// lunghezza del contorno offsettato
|
|
double dLen ; vFirstOff[0]->GetLength( dLen) ;
|
|
|
|
// passi in Y
|
|
int nYStep ;
|
|
double dYStep ;
|
|
if ( ! bFromInfill) {
|
|
nYStep = static_cast< int >( ceil(( dDimY - 30 * EPS_SMALL) / PockParams.dSideStep)) ;
|
|
dYStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ;
|
|
}
|
|
else {
|
|
nYStep = static_cast< int >( floor(( dDimY + 30 * EPS_SMALL) / PockParams.dSideStep)) ;
|
|
dYStep = PockParams.dSideStep ;
|
|
}
|
|
|
|
int nRef = (( nYStep + ( PockParams.bInvert ? 0 : 1)) % 2) ;
|
|
|
|
// tratto valido
|
|
struct Section {
|
|
bool bActive ;
|
|
Point3d ptS ;
|
|
Point3d ptE ;
|
|
double dOs ;
|
|
double dOe ;
|
|
int nOffIndS ;
|
|
int nOffIndE ;
|
|
} ;
|
|
|
|
struct ParIsland {
|
|
double dU ;
|
|
int nInd ;
|
|
} ;
|
|
|
|
// raccolta di tratti
|
|
typedef vector<vector<Section>> VECVECSECT ; VECVECSECT vvSec ;
|
|
vvSec.resize( nYStep + 1) ;
|
|
|
|
// calcolo le linee di svuotatura
|
|
int nCount = 0 ;
|
|
for ( int i = 0 ; i <= nYStep ; ++ i) {
|
|
// determino senso
|
|
bool bPlus = (( i % 2) == nRef) ;
|
|
// definisco la linea
|
|
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
|
|
const double EXP_LEN = 1.0 ;
|
|
Point3d ptStart ;
|
|
if ( ! bFromInfill)
|
|
ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + 10 * EPS_SMALL + i * dYStep, ptMin.z + dDimZ) ;
|
|
else {
|
|
double dShift = ( i == 0 ? 10 * EPS_SMALL : ( i == nYStep ? - 10 * EPS_SMALL : 0.)) ;
|
|
ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + dShift + i * dYStep, ptMin.z + dDimZ) ;
|
|
}
|
|
if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN))
|
|
return false ;
|
|
|
|
if ( ! bPlus)
|
|
pLine->Invert() ;
|
|
|
|
// calcolo la classificazione della linea rispetto alla superficie
|
|
CRVCVECTOR ccClass ;
|
|
pSrfZigZag->GetCurveClassification( *pLine, EPS_SMALL, ccClass) ;
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
if ( ccClass[j].nClass == CRVC_IN) { // memorizzo il segmento
|
|
Section currSec ;
|
|
currSec.bActive = true ;
|
|
PtrOwner<ICurveLine> pSeg( GetCurveLine( pLine->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
|
|
if ( IsNull( pSeg))
|
|
continue ;
|
|
Point3d ptS, ptE ;
|
|
pSeg->GetStartPoint( ptS) ; pSeg->GetEndPoint( ptE) ;
|
|
currSec.ptS = ptS ; currSec.ptE = ptE ;
|
|
for ( int k = 0 ; k < int( vFirstOff.size()) ; ++ k) {
|
|
if ( vFirstOff[k]->IsPointOn( ptS)) {
|
|
currSec.nOffIndS = k ;
|
|
double dUOS; vFirstOff[k]->GetParamAtPoint( ptS, dUOS) ;
|
|
currSec.dOs = dUOS ;
|
|
}
|
|
if ( vFirstOff[k]->IsPointOn( ptE)){
|
|
currSec.nOffIndE = k ;
|
|
double dUOE; vFirstOff[k]->GetParamAtPoint( ptE, dUOE) ;
|
|
currSec.dOe = dUOE ;
|
|
}
|
|
}
|
|
vvSec[i].emplace_back( currSec) ;
|
|
++ nCount ; // numero di segmenti inseriti
|
|
}
|
|
}
|
|
}
|
|
|
|
int nStatus = 0 ; // 0 -> inizio oppure ho inserito una parte di isola | 2 -> sto inserendo un segmento
|
|
Point3d ptS_ref ;
|
|
PtrOwner<ICurveComposite> pLastSeg( CreateCurveComposite()) ; // ultimo segmento nel percorso
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ; // ultimo link aggiunto
|
|
// vettore dei link aggiunti ( per Feed )
|
|
ICRVCOMPOPOVECTOR vAddedLinks ;
|
|
bool bFirstLine = true ; // flag per prima linea di ogni percorso
|
|
|
|
// creo i percorsi di svuotatura
|
|
vpCrvs.reserve( nCount) ;
|
|
int nI = -1, nJ = -1 ;
|
|
while ( true) {
|
|
// se sezione non valida
|
|
if ( nI < 0 || nJ < 0) {
|
|
// ricerco la prima valida ( il primo segmento con bActive messo a true)
|
|
for ( int k = 0 ; k < int( vvSec.size()) && nI < 0 ; ++ k) {
|
|
for ( int l = 0 ; l < int( vvSec[k].size()) && nJ < 0 ; ++ l) {
|
|
if ( vvSec[k][l].bActive) {
|
|
nI = k ;
|
|
nJ = l ;
|
|
}
|
|
}
|
|
}
|
|
// se trovata, creo nuova curva composita
|
|
if ( nI >= 0 && nJ >= 0) {
|
|
// creo la curva
|
|
vpCrvs.emplace_back( CreateCurveComposite()) ;
|
|
nStatus = 0 ;
|
|
// aggiungo punto iniziale
|
|
vpCrvs.back()->AddPoint( vvSec[nI][nJ].ptS) ;
|
|
bFirstLine = true ;
|
|
}
|
|
// altrimenti, esco
|
|
else
|
|
break ;
|
|
}
|
|
// determino senso
|
|
bool bPlus = (( nI % 2) == nRef) ;
|
|
// aggiungo la sezione alla curva
|
|
Section& Sec = vvSec[nI][nJ] ;
|
|
Sec.bActive = false ;
|
|
// creo i vettori con le linee Under e Above nella struttura della Section
|
|
ICURVEPOVECTOR vLineAbove ;
|
|
if ( nI < nYStep) {
|
|
for ( int a = 0 ; a < ( int)vvSec[nI + 1].size() ; ++ a) {
|
|
if ( vvSec[nI + 1][a].bActive)
|
|
continue ;
|
|
PtrOwner<ICurveLine> pLA( CreateCurveLine()) ;
|
|
pLA->Set( vvSec[nI + 1][a].ptS, vvSec[nI + 1][a].ptE) ;
|
|
vLineAbove.emplace_back( Release( pLA)) ;
|
|
}
|
|
}
|
|
ICURVEPOVECTOR vLineUnder ;
|
|
if ( nI > 0) {
|
|
for ( int u = 0 ; u < int( vvSec[nI-1].size()) ; ++ u) {
|
|
if ( vvSec[nI-1][u].bActive)
|
|
continue ;
|
|
PtrOwner<ICurveLine> pLU( CreateCurveLine()) ;
|
|
pLU->Set( vvSec[nI-1][u].ptS, vvSec[nI-1][u].ptE) ;
|
|
vLineUnder.emplace_back( Release( pLU)) ;
|
|
}
|
|
}
|
|
// creo la linea come curva composita, aggiungerò dei Joint per ogni tratto con Feed differente
|
|
PtrOwner<ICurveComposite> pLineCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pLineCompo))
|
|
return false ;
|
|
|
|
Point3d ptE_l ;
|
|
if ( bFirstLine)
|
|
ptE_l = vvSec[nI][nJ].ptS ;
|
|
else
|
|
vpCrvs.back()->GetEndPoint( ptE_l) ;
|
|
|
|
pLineCompo->AddPoint( ptE_l) ;
|
|
pLineCompo->AddLine( vvSec[nI][nJ].ptE) ;
|
|
// assegno la feed al tratto lineare
|
|
if ( ! AssignFeedZigZagOneWay( pLineCompo, false, vLineAbove, vLineUnder, vAddedLinks, PockParams)) // Assegno la Feed
|
|
return false ;
|
|
|
|
bFirstLine = false ;
|
|
vpCrvs.back()->AddCurve( pLineCompo->Clone()) ; // aggiungo la curva al percorso
|
|
|
|
if ( nStatus == 0) { // primo segmento per il raccordo smussato
|
|
pLastSeg.Set( pLineCompo) ;
|
|
pLastSeg->GetStartPoint( ptS_ref) ;
|
|
}
|
|
if ( nStatus == 2) { // ho precedentemente aggiunto il bordo di un'isola, quindi mi salvo il secondo segmento
|
|
|
|
PtrOwner<ICurveComposite> pSeg1( pLastSeg->Clone()) ;
|
|
PtrOwner<ICurveComposite> pSeg2( pLineCompo->Clone()) ;
|
|
if ( IsNull( pSeg1) || IsNull( pSeg2))
|
|
return false ;
|
|
|
|
// aggiorno il link
|
|
if ( ! CalcZigZagLink( pSeg1, pSeg2, PockParams, pCrvLink))
|
|
return false ;
|
|
|
|
// aggiorno il vettore delle curve ZigZag
|
|
Point3d ptE_Seg1 ;
|
|
pSeg1->GetEndPoint( ptE_Seg1) ;
|
|
double dUS_Link = EPS_SMALL ;
|
|
vpCrvs.back()->GetParamAtPoint( ptE_Seg1, dUS_Link) ;
|
|
if ( ! vpCrvs.back()->TrimEndAtParam( dUS_Link))
|
|
vpCrvs.back()->Clear() ;
|
|
|
|
if ( bFromInfill)
|
|
for ( int u = 0 ; u < pCrvLink->GetCurveCount() ; ++ u)
|
|
pCrvLink->SetCurveTempProp( u, -1, 1) ;
|
|
|
|
if ( ! AssignFeedZigZagOneWay( pCrvLink, true, vLineAbove, vLineUnder, vAddedLinks, PockParams))
|
|
return false ;
|
|
|
|
vpCrvs.back()->AddCurve( pCrvLink->Clone()) ; // aggiungo il Link
|
|
vpCrvs.back()->AddCurve( pSeg2->Clone()) ; // aggiungo pSeg2
|
|
|
|
// aggiorno il vettore dei Link ( nel caso sia un link tra due segmenti sullo stesso livello )
|
|
Point3d ptS1 ;
|
|
pSeg1->GetStartPoint( ptS1) ;
|
|
Point3d ptS2 ;
|
|
pSeg2->GetStartPoint( ptS2) ;
|
|
if ( abs( ptS1.y - ptS2.y) < 50 * EPS_SMALL)
|
|
vAddedLinks.emplace_back( pCrvLink->Clone()) ;
|
|
|
|
// pSeg2 ora diventa pSeg1 e il procedimento si itera per tutto il percorso
|
|
pLastSeg.Set( pSeg2) ;
|
|
}
|
|
// cerco nella stessa fila o in quella successiva sezione successiva raccordabile tramite il contorno
|
|
double dUstart = Sec.dOe ;
|
|
int nIndexIslandE = Sec.nOffIndE ; // isola di arrivo
|
|
double dUmin, dUmax ;
|
|
vFirstOff[nIndexIslandE]->GetDomain( dUmin, dUmax) ;
|
|
double dUspan = dUmax - dUmin ;
|
|
double dUref = ( bPlus ? INFINITO : -INFINITO) ;
|
|
int nNextI = -1 ;
|
|
int nNextJ = -1 ;
|
|
int li = nJ + 1 ;
|
|
for ( int k = nI ; k <= nI + 1 && k < int( vvSec.size()) ; ++ k) {
|
|
for ( int l = li ; l < int( vvSec[k].size()) ; ++ l) {
|
|
if ( ! vvSec[k][l].bActive) // se sezione non attiva... non la considero
|
|
continue ;
|
|
if ( vvSec[k][l].nOffIndS != nIndexIslandE) // se isola diversa... non la considero
|
|
continue ;
|
|
double dU = vvSec[k][l].dOs ;
|
|
if ( bPlus) {
|
|
if ( dU < dUstart)
|
|
dU += dUspan ;
|
|
if ( dU < dUref) {
|
|
dUref = dU ;
|
|
nNextI = k ;
|
|
nNextJ = l ;
|
|
}
|
|
}
|
|
else {
|
|
if ( dU > dUstart)
|
|
dU -= dUspan ;
|
|
if ( dU > dUref) {
|
|
dUref = dU ;
|
|
nNextI = k ;
|
|
nNextJ = l ;
|
|
}
|
|
}
|
|
}
|
|
li = 0 ;
|
|
}
|
|
// se trovato, controllo il contorno dell'isola
|
|
if ( nNextI != -1) {
|
|
PtrOwner<ICurve> pCopy ;
|
|
if ( bPlus) {
|
|
if ( dUref > dUmax)
|
|
dUref -= dUspan ;
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ;
|
|
if ( ! IsNull( pCopy)) {
|
|
double dCLen ; pCopy->GetLength( dCLen) ;
|
|
if ( dCLen > 0.5 * dLen) {
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ;
|
|
if ( ! IsNull( pCopy))
|
|
pCopy->Invert() ;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ( dUref < dUmin)
|
|
dUref += dUspan ;
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ;
|
|
if ( ! IsNull( pCopy)) {
|
|
pCopy->Invert() ;
|
|
double dCLen; pCopy->GetLength( dCLen) ;
|
|
if ( dCLen > 0.5 * dLen)
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ;
|
|
}
|
|
}
|
|
// controllo che nel percorso scelto non ritorni indietro superando la precendete passata
|
|
BBox3d b3Copy ;
|
|
if ( ! IsNull( pCopy))
|
|
pCopy->GetLocalBBox( b3Copy) ;
|
|
if ( ! b3Copy.IsEmpty() && ( b3Copy.GetMax().y - b3Copy.GetMin().y) < dYStep + 10 * EPS_SMALL) { // tengo la curva (non ritorno indietro più dello step)
|
|
Point3d ptS, ptE ;
|
|
pCrvLink->Clear() ;
|
|
pCrvLink->AddCurve( pCopy->Clone()) ;
|
|
vpCrvs.back()->AddCurve( Release( pCopy)) ;
|
|
nStatus = 2 ; // aggiunta parte di isola
|
|
// aggiungo il Link
|
|
nI = nNextI ;
|
|
nJ = nNextJ ;
|
|
}
|
|
else {
|
|
nI = -1 ;
|
|
nJ = -1 ;
|
|
}
|
|
}
|
|
else {
|
|
nI = -1 ;
|
|
nJ = -1 ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddSpiralIn( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
|
|
PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// ricavo il numero di Chunk da svuotare
|
|
if ( pSrfPock == nullptr)
|
|
return false ;
|
|
int nChunk = pSrfPock->GetChunkCount() ;
|
|
|
|
// scorro i Chunk
|
|
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
|
|
|
|
// indice del Chunk da corrente da svuotare
|
|
int nChunkInd = 0 ;
|
|
if ( PockParams.ptStart.IsValid()) {
|
|
// se il punto di riferimento è valido, il Chunk è quello ad esso più vicino
|
|
DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ;
|
|
int nMinLoop = 0 ;
|
|
double dMinPar = 0. ;
|
|
if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar))
|
|
return false ;
|
|
}
|
|
|
|
// Clono il Chunk attuale come regione FlatRegion
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
|
|
const int MAX_REGS = 50 ; // massimo numero di regioni create dal primo offset
|
|
int nReg = 0 ; // chunk nuovo corrente da svuotare
|
|
|
|
// ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-esimo
|
|
while ( nReg < MAX_REGS) {
|
|
// calcolo la spirale dall'esterno all'interno e la curva che unisce inizio e fine
|
|
PtrOwner<ICurveComposite> pMCrv( CreateCurveComposite()) ;
|
|
if ( IsNull( pMCrv))
|
|
return false ;
|
|
|
|
int nRegTot = nReg ;
|
|
Point3d ptStart ;
|
|
bool bOptimizedTrap = false ;
|
|
|
|
// cerco le curve originali del chunk cc-esimo ( per casi ottimizzati)
|
|
ICRVCOMPOPOVECTOR vCrvOrigChunkLoops ;
|
|
if ( ! GetOptCrvIndex( pSfrOrig, pSfrChunk, PockParams, nReg, vCrvOrigChunkLoops))
|
|
return false ;
|
|
|
|
// calcolo il percorso di svuotatura
|
|
bool bSomeOpen ;
|
|
Vector3d vtMidOut ;
|
|
if ( ! CalcSpiral( pSfrChunk, PockParams, nRegTot, ptStart, vCrvOrigChunkLoops, pMCrv, bOptimizedTrap,
|
|
bSomeOpen, vtMidOut))
|
|
return false ;
|
|
|
|
// se terminate le regioni, esco
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
break ; // passo al chunk originale successivo
|
|
|
|
bool bIsExtended = false ;
|
|
// se caso a trapezio
|
|
if ( bOptimizedTrap) {
|
|
Vector3d vtRef ;
|
|
pMCrv->GetStartDir( vtRef) ;
|
|
vtRef.Invert() ;
|
|
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, false, bIsExtended))
|
|
return false ;
|
|
pMCrv->GetEndDir( vtRef) ;
|
|
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, true, bIsExtended))
|
|
return false ;
|
|
AssignFeedSpiralOpt( 1, PockParams, pMCrv) ;
|
|
pMCrv->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
|
|
}
|
|
// se esistenza di lati aperti validi
|
|
else if ( bSomeOpen)
|
|
ExtendPath( pMCrv, pSfrOrig, PockParams, vtMidOut, false, bIsExtended) ;
|
|
|
|
// inserisco le curve nel vettore
|
|
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
|
|
++ nReg ; // incremento il numero di regione progressiva
|
|
// aggiorno il punto di riferimento per il Chunk Successivo
|
|
vCrvCompoRes.back()->GetEndPoint( PockParams.ptStart) ;
|
|
}
|
|
|
|
// rimuovo il Chunk dalla regione
|
|
pSrfPock->EraseChunk( nChunkInd) ;
|
|
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddSpiralOut( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
|
|
PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// ricavo il numero di Chunk da svuotare
|
|
if ( pSrfPock == nullptr)
|
|
return false ;
|
|
int nChunk = pSrfPock->GetChunkCount() ;
|
|
|
|
// ciclo sui chunk della superficie da svuotare
|
|
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
|
|
|
|
// indice del Chunk da corrente da svuotare
|
|
int nChunkInd = 0 ;
|
|
if ( PockParams.ptStart.IsValid()) {
|
|
// se il punto di riferimento è valido, il Chunk è quello ad esso più vicino
|
|
DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ;
|
|
int nMinLoop = 0 ;
|
|
double dMinPar = 0. ;
|
|
if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar))
|
|
return false ;
|
|
}
|
|
|
|
// Clono il Chunk attuale come regione FlatRegion
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
|
|
const int MAX_REGS = 50 ; // massimo numero di regioni create dal primo offset
|
|
int nReg = 0 ; // chunk nuovo corrente da svuotare
|
|
|
|
// ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-esimo
|
|
while ( nReg < MAX_REGS) {
|
|
|
|
// calcolo la spirale dall'interno all'esterno
|
|
PtrOwner<ICurveComposite> pMCrv( CreateCurveComposite()) ;
|
|
if ( IsNull( pMCrv))
|
|
return false ;
|
|
|
|
int nRegTot = nReg ;
|
|
Point3d ptStart ;
|
|
bool bOptimizedTrap = false ;
|
|
|
|
// cerco la curva originale del chunk cc-esimo ( per casi ottimizzati)
|
|
ICRVCOMPOPOVECTOR vCrvOrig ;
|
|
if ( ! GetOptCrvIndex( pSfrOrig, pSfrChunk, PockParams, nReg, vCrvOrig))
|
|
return false ;
|
|
|
|
// calcolo il percorso di svuotatura
|
|
bool bSomeOpen ;
|
|
Vector3d vtMidOut ;
|
|
if ( ! CalcSpiral( pSfrChunk, PockParams, nRegTot, ptStart, vCrvOrig, pMCrv, bOptimizedTrap,
|
|
bSomeOpen, vtMidOut))
|
|
return false ;
|
|
|
|
// se terminate le regioni, esco
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
break ; // passo al chunk originale successivo
|
|
|
|
if ( bOptimizedTrap) { // se caso a trapezio
|
|
Vector3d vtRef ;
|
|
pMCrv->GetStartDir( vtRef) ;
|
|
vtRef.Invert() ;
|
|
bool bIsExtended = false ;
|
|
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, false, bIsExtended))
|
|
return false ;
|
|
pMCrv->GetEndDir( vtRef) ;
|
|
if ( ! ExtendPath( pMCrv, pSrfPock, PockParams, vtRef, true, bIsExtended))
|
|
return false ;
|
|
AssignFeedSpiralOpt( 1, PockParams, pMCrv) ;
|
|
pMCrv->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
|
|
}
|
|
|
|
// inverto il percorso, perchè sono calcolati dall'esterno all'interno (solo nel caso non ottimizzato)
|
|
pMCrv->Invert() ;
|
|
|
|
// inserisco le curve nel vettore
|
|
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
|
|
// incremento il numero di regione progressiva
|
|
++ nReg ;
|
|
// aggiorno il punto di riferimento per il Chunk Successivo
|
|
vCrvCompoRes.back()->GetEndPoint( PockParams.ptStart) ;
|
|
}
|
|
|
|
// rimuovo il Chunk dalla regione
|
|
pSrfPock->EraseChunk( nChunkInd) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetZigZagOneWayBorderCrvs( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig,
|
|
const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvRes)
|
|
{
|
|
/*
|
|
Restituisce le curve di Primo Offset dei lati chiusi per i percorsi a ZigZag e OneWay.
|
|
Vengono calcolate le Feed ed eventuali entrate da fuori per i percorsi ricavati
|
|
*/
|
|
|
|
// controllo validità della regione
|
|
if ( pSfrPock == nullptr || ! pSfrPock->IsValid())
|
|
return false ;
|
|
|
|
// recupero la superficie limite e calcolo Offset
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrLimit))
|
|
return false ;
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
pSfrLimit.Set( PockParams.SfrLimit.Clone()) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
|
|
return false ;
|
|
pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset - 50 * EPS_SMALL, ICurve::OFF_FILLET) ;
|
|
}
|
|
|
|
// le curve che ricavo devono essere sempre conenute nella regione che sto svuotando
|
|
/*
|
|
Se siamo in presenza di isole chiuse molto vicine a lati chiusi non posso estendere il bordo
|
|
dell'isola chiusa a piacere, devo limitarlo alla regione di svuotatura
|
|
*/
|
|
PtrOwner<ISurfFlatRegion> pSfrBucket( CloneSurfFlatRegion( pSfrPock)) ;
|
|
if ( IsNull( pSfrBucket) || ! pSfrBucket->IsValid() ||
|
|
! pSfrBucket->Offset( - PockParams.dRad - PockParams.dRadialOffset + 50 * EPS_SMALL, ICurve::OFF_FILLET))
|
|
return false ;
|
|
|
|
// vettore delle curve chiuse Offsettate
|
|
ICRVCOMPOPOVECTOR vClosedOffs ;
|
|
|
|
// scorro i Chunk e i Loop della regione ( se OneWay potrei avere più Chunks)
|
|
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
|
|
for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) {
|
|
ICRVCOMPOPOVECTOR vClosedOffs_nC ;
|
|
// loop come curva composita
|
|
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ;
|
|
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
|
|
return false ;
|
|
// recupero i tratti con proprietà uniformi
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
GetHomogeneousParts( pCrvLoop, PockParams, vpCrvs) ;
|
|
if ( vpCrvs.size() > 1) {
|
|
// unisco il primo e l'ultimo se estremi compatibili
|
|
Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ;
|
|
Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ;
|
|
if ( AreSamePointApprox( ptS, ptE)) {
|
|
vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ;
|
|
vpCrvs.erase( vpCrvs.end() - 1) ;
|
|
}
|
|
}
|
|
// le isole aperte vanno percorse
|
|
bool bOpenIsland = ( nL > 0 && int( vpCrvs.size()) == 1 &&
|
|
vpCrvs[0]->GetTempProp() == TEMP_PROP_OPEN_EDGE) ;
|
|
// scorro tutti i tratti omogenei
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
// se tratto chiuso o isola aperta
|
|
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE || bOpenIsland) {
|
|
// Offset per le curve
|
|
OffsetCurve OffsCrv ;
|
|
double dOffs = - PockParams.dRad - PockParams.dRadialOffset ;
|
|
// se isola aperta, allora l'offset è verso il suo interno
|
|
if ( bOpenIsland)
|
|
dOffs *= -1 ;
|
|
if ( OffsCrv.Make( vpCrvs[i], dOffs, ICurve::OFF_FILLET)) {
|
|
PtrOwner<ICurve> pOffLongestCrv( OffsCrv.GetLongerCurve()) ;
|
|
while ( ! IsNull( pOffLongestCrv)) {
|
|
// recupero solo i tratti di curva al di fuori della superficie limite
|
|
if ( pSfrLimit->IsValid()) {
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrLimit->GetCurveClassification( *pOffLongestCrv, EPS_SMALL, ccClass)) {
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
if ( ccClass[j].nClass == CRVC_OUT) {
|
|
// recupero il tratto di curva
|
|
PtrOwner<ICurve> pCrv( pOffLongestCrv->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
|
|
if ( ! IsNull( pCrv) && pCrv->IsValid())
|
|
vClosedOffs_nC.emplace_back( ConvertCurveToComposite( Release( pCrv))) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
vClosedOffs_nC.emplace_back( ConvertCurveToComposite( Release( pOffLongestCrv))) ;
|
|
|
|
// passo alla successiva
|
|
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// se non ho ricavato curve dal Chunk nC-esimo, passo al successivo
|
|
if ( vClosedOffs_nC.empty())
|
|
continue ;
|
|
|
|
// limito le curve ad essere contenute nella pSfrBucket
|
|
ICRVCOMPOPOVECTOR vClosedOffs1 ;
|
|
// scorro le curve di Offset chiuse ricavate
|
|
for ( int i = 0 ; i < int( vClosedOffs_nC.size()) ; ++ i) {
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrBucket->GetCurveClassification( *vClosedOffs_nC[i], EPS_SMALL, ccClass)) {
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
if ( ccClass[j].nClass == CRVC_IN) {
|
|
// recupero il tratto di curva
|
|
PtrOwner<ICurve> pCrv( vClosedOffs_nC[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
|
|
if ( ! IsNull( pCrv) && pCrv->IsValid())
|
|
vClosedOffs1.emplace_back( ConvertCurveToComposite( Release( pCrv))) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( vClosedOffs1.empty())
|
|
continue ;
|
|
swap( vClosedOffs_nC, vClosedOffs1) ;
|
|
|
|
// concateno eventuali percorsi
|
|
if ( ! ChainCompoCurves( vClosedOffs_nC))
|
|
return false ;
|
|
// determino ora il punto di inizio, il tratto per il LeadIn
|
|
bool bIsChunkClosed = false ;
|
|
bool bIsChunkAllOpen = false ;
|
|
if ( ! IsChunkAllHomogeneous( pSfrPock, nC, bIsChunkClosed, bIsChunkAllOpen))
|
|
return false ;
|
|
if ( ! bIsChunkClosed) {
|
|
if ( ! AdvanceExtendCurves( vClosedOffs_nC, pSfrOrig, PockParams, true))
|
|
return false ;
|
|
}
|
|
|
|
// aggiungo le curve ottenute al vettore
|
|
for ( int i = 0 ; i < int( vClosedOffs_nC.size()) ; ++ i)
|
|
vClosedOffs.emplace_back( Release( vClosedOffs_nC[i])) ;
|
|
}
|
|
}
|
|
// se non ho percorsi ho finito
|
|
if ( vClosedOffs.empty())
|
|
return true ;
|
|
|
|
// Calcolo ora le Feed e restituisco le curve ottenute
|
|
for ( int i = 0 ; i < int( vClosedOffs.size()) ; ++ i) {
|
|
// se ZigZag -> ho percorso i tratti a ZigZag, quindi posso andare a Feed massima
|
|
if ( PockParams.nType == POCKET_ZIGZAG)
|
|
AssignMaxFeed( vClosedOffs[i], PockParams) ;
|
|
// se OneWay -> prima percorro queste curva, quindi la Feed è minima
|
|
if ( PockParams.nType == POCKET_ONEWAY)
|
|
AssignMinFeed( vClosedOffs[i], PockParams) ;
|
|
// assegno la proprità alla curva composita
|
|
vClosedOffs[i]->SetTempProp( TEMP_PROP_BORDER_CURVE, 0) ;
|
|
// restituisco le curve ottenute
|
|
vCrvRes.emplace_back( Release( vClosedOffs[i])) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddZigZag( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// ricavo il numero di Chunk da svuotare
|
|
if ( pSrfPock == nullptr)
|
|
return false ;
|
|
int nChunk = pSrfPock->GetChunkCount() ;
|
|
|
|
// offset della regione per curva ZigZag
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
if ( PockParams.bAllowZigZagOneWayBorders)
|
|
dOffs += PockParams.dOffsExtra ;
|
|
|
|
// ciclo su tutti i chunks della superficie originale
|
|
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
|
|
|
|
// indice del Chunk da corrente da svuotare
|
|
int nChunkInd = 0 ;
|
|
if ( PockParams.ptStart.IsValid()) {
|
|
// se il punto di riferimento è valido, il Chunk è quello ad esso più vicino
|
|
DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ;
|
|
int nMinLoop = 0 ;
|
|
double dMinPar = 0. ;
|
|
if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar))
|
|
return false ;
|
|
}
|
|
|
|
// creo la regione per il percorso a ZigZag ( mediante primo Offset)
|
|
PtrOwner<ISurfFlatRegion> pSrfZigZag( pSrfPock->CloneChunk( nChunkInd)) ;
|
|
if ( ! pSrfZigZag->Offset( - dOffs, ICurve::OFF_FILLET))
|
|
return false ;
|
|
// vettore con i percorsi a ZigZag relativi al Chunk attuale
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
|
|
// ciclo sui Chunk ottenuti dal primo offset
|
|
for ( int nC1 = 0 ; nC1 < pSrfZigZag->GetChunkCount() ; ++ nC1) {
|
|
// recupero il Chunk attuale
|
|
PtrOwner<ISurfFlatRegion> pSrfZigZagChunk( pSrfZigZag->CloneChunk( nC1)) ;
|
|
if ( IsNull( pSrfZigZagChunk) || ! pSrfZigZagChunk->IsValid())
|
|
return false ;
|
|
|
|
// calcolo i percorsi di ZigZag
|
|
if ( ! CalcZigZag( pSrfZigZagChunk, PockParams, vpCrvs, false))
|
|
return false ;
|
|
|
|
// controllo esistenza di lati aperti per il Chunk attuale
|
|
bool bIsChunkClosed = true ;
|
|
bool bIsChunkAllOpen = true ;
|
|
if ( ! PockParams.bAllClosed) {
|
|
if ( ! IsChunkAllHomogeneous( pSrfPock, nChunkInd, bIsChunkClosed, bIsChunkAllOpen))
|
|
return false ;
|
|
// se il Chunk originale aveva lati aperti, estendo il percorso a ZigZag
|
|
for ( int nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU) {
|
|
Vector3d vtRef ; vpCrvs[nU]->GetStartDir( vtRef) ;
|
|
vtRef.Invert() ;
|
|
bool bIsExtended = false ;
|
|
if ( ! ExtendPath( vpCrvs[nU], pSfrOrig, PockParams, vtRef, false,bIsExtended))
|
|
return false ;
|
|
vpCrvs[nU]->GetEndDir( vtRef) ;
|
|
if ( ! ExtendPath( vpCrvs[nU], pSfrOrig, PockParams, vtRef, true, bIsExtended))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// inserisco le curve nel vettore risultante
|
|
for ( int nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU)
|
|
vCrvCompoRes.emplace_back( Release( vpCrvs[nU])) ;
|
|
|
|
// libero il vettore di curve ZigZag per il chunk successivo
|
|
vpCrvs.clear() ;
|
|
}
|
|
|
|
// se richiesto, aggiungo le curve chiuse di Bordo
|
|
if ( PockParams.bAllowZigZagOneWayBorders) {
|
|
// recupero il Chunk nC-esimo
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid() ||
|
|
! GetZigZagOneWayBorderCrvs( pSfrChunk, pSfrOrig, PockParams, vCrvCompoRes))
|
|
return false ;
|
|
}
|
|
|
|
// rimuovo il Chunk dalla regione
|
|
pSrfPock->EraseChunk( nChunkInd) ;
|
|
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
OptimizeChunkOneWay( const ISurfFlatRegion* pSfrOrig, ISURFFRPOVECTOR& vSrfIdeal)
|
|
{
|
|
// controllo parametri
|
|
if ( pSfrOrig == nullptr || pSfrOrig->GetChunkCount() == 0)
|
|
return false ;
|
|
vSrfIdeal.clear() ;
|
|
|
|
/*
|
|
classifico i chunks in modo da creare delle regioni ideali; una regione ideale è formata
|
|
da un chunk principale con tutti gli altri contenuti in esso ( in questo modo ottimizzo i
|
|
percorsi per i bordi)
|
|
*/
|
|
|
|
INTVECTOR vChunksAvailable( pSfrOrig->GetChunkCount(), 1) ;
|
|
for ( int nC = 0 ; nC < pSfrOrig->GetChunkCount() ; ++ nC) {
|
|
PtrOwner<ISurfFlatRegion> pSrfIdeal( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfIdeal))
|
|
return false ;
|
|
// se Chunk valido...
|
|
if ( vChunksAvailable[nC] == 1) {
|
|
// prendo la curva esterna
|
|
PtrOwner<ICurve> pCrvExt( pSfrOrig->GetLoop( nC, 0)) ;
|
|
if ( IsNull( pCrvExt))
|
|
return false ;
|
|
// inserisco il chunk nC-esimo ( curva per curva, così non perdo le temp prop)
|
|
pSrfIdeal->AddExtLoop( pCrvExt->Clone()) ;
|
|
for ( int nL = 1 ; nL < pSfrOrig->GetLoopCount( nC) ; ++ nL)
|
|
pSrfIdeal->AddIntLoop( pSfrOrig->GetLoop( nC, nL)) ;
|
|
// il chunk nC-esimo non è più disponibile...
|
|
vChunksAvailable[nC] = -1 ;
|
|
// scorro tutti gli altri chunk i-esimi ancora disponibili
|
|
for ( int i = 0 ; i < int( vChunksAvailable.size()) ; ++ i) {
|
|
if ( vChunksAvailable[i] == 1) {
|
|
// prendo la curva esterna del chunk i-esimo disponibile
|
|
PtrOwner<ICurve> pCrvExtC( pSfrOrig->GetLoop( i, 0)) ;
|
|
if ( IsNull( pCrvExtC))
|
|
return false ;
|
|
// classifico i bordi esterni ( se il bordo del chunk nC-esimo contiene o è contenuto nel loop
|
|
// esterno del chunk i-esimo -> sono chunks appartenenti alla stessa superficie ideale)
|
|
IntersCurveCurve intCC( *pCrvExtC, *pCrvExt) ;
|
|
CRVCVECTOR ccClass, ccClass1 ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
intCC.GetCurveClassification( 1, EPS_SMALL, ccClass1) ;
|
|
if (( int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) ||
|
|
( int( ccClass1.size()) == 1 && ccClass1[0].nClass == CRVC_IN)) {
|
|
// inserisco il chunk i-esimo ( curva per curva, così non perdo le temp prop)
|
|
pSrfIdeal->AddExtLoop( pSfrOrig->GetLoop( i, 0)) ;
|
|
for ( int nL = 1 ; nL < pSfrOrig->GetLoopCount( i) ; ++ nL)
|
|
pSrfIdeal->AddIntLoop( pSfrOrig->GetLoop( i, nL)) ;
|
|
vChunksAvailable[i] = -1 ; // chunk j-esimo non più disponibile
|
|
// NB: Se il chunk i-esimo contiene la pSrfIdeal ( quindi chunk i-esimo unito ad eventuali altri chunk
|
|
// j-esimi precedenti -> prendo come bordo esterno di riferimento il suo e ricontrollo dall'inizio i
|
|
// chunk disponibili
|
|
if ( int( ccClass1.size()) == 1 && ccClass1[0].nClass == CRVC_IN) {
|
|
pCrvExt.Set( pCrvExtC) ;
|
|
i = 0 ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( pSrfIdeal->GetChunkCount() > 0)
|
|
vSrfIdeal.emplace_back( Release( pSrfIdeal)) ;
|
|
}
|
|
|
|
return ( ! vSrfIdeal.empty()) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddOneWay( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// offset della regione per segmenti a ZigZag
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
double dExtra = 0. ;
|
|
if ( PockParams.bAllowZigZagOneWayBorders)
|
|
dExtra = PockParams.dOffsExtra ;
|
|
|
|
// vettore delle regioni ideali
|
|
ISURFFRPOVECTOR vSrfFlat ;
|
|
if ( ! OptimizeChunkOneWay( pSrfPock, vSrfFlat))
|
|
return false ;
|
|
|
|
// scorro le regioni ideali
|
|
for ( int nIs = 0 ; nIs < int( vSrfFlat.size()) ; ++ nIs) {
|
|
|
|
// copio la superficie ideale ed effettuo il primo Offset
|
|
PtrOwner<ISurfFlatRegion> pSrfIdeal( CloneSurfFlatRegion( vSrfFlat[nIs])) ;
|
|
if ( IsNull( pSrfIdeal) || ! pSrfIdeal->Offset( - dOffs, ICurve::OFF_FILLET))
|
|
return false ;
|
|
|
|
// se sono richieste le curve di Bordo, le aggiungo
|
|
if ( PockParams.bAllowZigZagOneWayBorders) {
|
|
if ( ! GetZigZagOneWayBorderCrvs( vSrfFlat[nIs], pSfrOrig, PockParams, vCrvCompoRes))
|
|
return false ;
|
|
}
|
|
|
|
// recupero il Box della superficie attuale da svuotare
|
|
BBox3d b3Pocket ; pSrfIdeal->GetLocalBBox( b3Pocket) ;
|
|
Point3d ptMin ; double dDimX, dDimY, dDimZ ;
|
|
b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ;
|
|
|
|
// passi in Y
|
|
int nYStep = static_cast<int>( ceil(( dDimY + 2 * dExtra) / PockParams.dSideStep)) ;
|
|
double dYStep = ( nYStep > 0 ? ( dDimY + 2 * dExtra) / nYStep : 0) ;
|
|
-- nYStep ;
|
|
|
|
// vettore dei segmenti al di sotto della linea corrente ( per le Feed)
|
|
ICURVEPOVECTOR vLineUnder ;
|
|
ICURVEPOVECTOR vCrvNull ;
|
|
ICRVCOMPOPOVECTOR vCrvCompoNull ;
|
|
|
|
// calcolo le linee di svuotatura
|
|
const double EXP_LEN = 1.0 ;
|
|
for ( int i = 1 ; i <= nYStep ; ++ i) {
|
|
|
|
// definisco la linea
|
|
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
|
|
Point3d ptStart( ptMin.x - EXP_LEN, ptMin.y + ( - dExtra + i * dYStep), ptMin.z + dDimZ) ;
|
|
if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN))
|
|
return false ;
|
|
|
|
// se richiesta inversione
|
|
if ( PockParams.bInvert)
|
|
pLine->Invert() ;
|
|
|
|
// linea come composita per Feed ( la dovrò spezzare in tratti uniformi di Feed)
|
|
PtrOwner<ICurveComposite> pCrvCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvCompo))
|
|
return false ;
|
|
|
|
// vettore di tutti i segmenti lineari che si formano nello step nYStep
|
|
ICURVEPOVECTOR vAddedLines ;
|
|
|
|
// riempio il vettore di segmenti
|
|
CRVCVECTOR ccClassSeg ;
|
|
pSrfIdeal->GetCurveClassification( *pLine, EPS_SMALL, ccClassSeg) ;
|
|
for ( int w = 0 ; w < int( ccClassSeg.size()) ; ++ w) {
|
|
if ( ccClassSeg[w].nClass == CRVC_IN) {
|
|
|
|
PtrOwner<ICurveLine> pCrvSeg( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ;
|
|
double duF, dLen ;
|
|
// accorcio leggermente il segmento per non toccare la prima curva di Offset
|
|
if ( ! pCrvSeg->GetLength( dLen) ||
|
|
dLen < 2 * dExtra ||
|
|
! pCrvSeg->GetParamAtLength( dLen - dExtra, duF) ||
|
|
! pCrvSeg->TrimStartAtLen( dExtra) ||
|
|
! pCrvSeg->TrimEndAtParam( duF))
|
|
pCrvSeg.Set( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ;
|
|
|
|
// aggiungo il segmento al vettore dei tratti correnti
|
|
vAddedLines.emplace_back( pCrvSeg->Clone()) ;
|
|
|
|
// trasformo il tratto lineare in curve composita per il calcolo della Feed
|
|
PtrOwner<ICurveComposite> pCrvSegCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvSegCompo))
|
|
return false ;
|
|
pCrvSegCompo->AddCurve( Release( pCrvSeg)) ;
|
|
// calcolo la Feed
|
|
AssignFeedZigZagOneWay( pCrvSegCompo, false, vCrvNull, vLineUnder, vCrvCompoNull, PockParams) ;
|
|
// estendo presso lati aperti se richiesto
|
|
if ( ! PockParams.bAllClosed) {
|
|
Vector3d vtRef ; pCrvSegCompo->GetStartDir( vtRef) ;
|
|
vtRef.Invert() ;
|
|
bool bIsExtended = false ;
|
|
if ( ! ExtendPath( pCrvSegCompo, pSfrOrig, PockParams, vtRef, false,bIsExtended))
|
|
return false ;
|
|
pCrvSegCompo->GetEndDir( vtRef) ;
|
|
if ( ! ExtendPath( pCrvSegCompo, pSfrOrig, PockParams, vtRef, true, bIsExtended))
|
|
return false ;
|
|
}
|
|
vCrvCompoRes.emplace_back( Release( pCrvSegCompo)) ;
|
|
}
|
|
}
|
|
// i tratti lineari correnti diventano i precedenti
|
|
vLineUnder.clear() ;
|
|
for ( int u = 0 ; u < int( vAddedLines.size()) ; ++ u)
|
|
vLineUnder.emplace_back( Release( vAddedLines[u])) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SmoothLinkByOffs( ICurveComposite* pCrvOffs0, ICurveComposite* pCrvOffs1, ICurveComposite* pCrvLink,
|
|
const PocketParams& PockParams, bool bFirstOffs0, bool bFirstOffs1, double dSmoothPar, double dTol)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
|
|
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid() ||
|
|
pCrvLink == nullptr || ! pCrvLink->IsValid())
|
|
return false ;
|
|
|
|
// curva precedente
|
|
PtrOwner<ICurveComposite> pCrvBef( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvBef))
|
|
return false ;
|
|
if ( bFirstOffs0 && pCrvOffs0->IsClosed()) {
|
|
const ICurve* pCrvFirst = pCrvOffs0->GetFirstCurve() ;
|
|
if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid())
|
|
return false ;
|
|
PtrOwner<ICurve> pMyCrv( pCrvFirst->Clone()) ;
|
|
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
|
|
return false ;
|
|
pCrvBef->AddCurve( Release( pMyCrv)) ;
|
|
}
|
|
else {
|
|
const ICurve* pCrvLast = pCrvOffs0->GetLastCurve() ;
|
|
if ( pCrvLast == nullptr || ! pCrvLast->IsValid())
|
|
return false ;
|
|
PtrOwner<ICurve> pMyCrv( pCrvLast->Clone()) ;
|
|
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
|
|
return false ;
|
|
pCrvBef->AddCurve( Release( pMyCrv)) ;
|
|
}
|
|
|
|
// curva successiva
|
|
PtrOwner<ICurveComposite> pCrvAft( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvAft))
|
|
return false ;
|
|
const ICurve* pCrvFirst = pCrvOffs1->GetFirstCurve() ;
|
|
if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid())
|
|
return false ;
|
|
PtrOwner<ICurve> pMyCrv( pCrvFirst->Clone()) ;
|
|
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
|
|
return false ;
|
|
pCrvAft->AddCurve( Release( pMyCrv)) ;
|
|
|
|
// controllo la validità delle curve precedenti e successive ricavate
|
|
if ( IsNull( pCrvBef) || IsNull( pCrvAft) || ! pCrvBef->IsValid() || ! pCrvAft->IsValid())
|
|
return false ;
|
|
// estendo il Link
|
|
pCrvLink->AddCurve( Release( pCrvBef), false, dTol) ;
|
|
pCrvLink->AddCurve( Release( pCrvAft), true, dTol) ;
|
|
// smusso
|
|
ModifyCurveToSmoothed( pCrvLink, PockParams, dSmoothPar, dSmoothPar, false) ;
|
|
// toglo gli estremi
|
|
delete( pCrvLink->RemoveFirstOrLastCurve( false)) ;
|
|
delete( pCrvLink->RemoveFirstOrLastCurve( true)) ;
|
|
// modifico Offset0 per raccordarlo al nuovo Link
|
|
if ( bFirstOffs0 && pCrvOffs0->IsClosed()) {
|
|
const ICurve* pCrvFirst = pCrvLink->GetFirstCurve() ;
|
|
if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid())
|
|
return false ;
|
|
PtrOwner<ICurve> pMyCrv( pCrvFirst->Clone()) ;
|
|
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
|
|
return false ;
|
|
pCrvOffs0->AddCurve( Release( pMyCrv)) ;
|
|
pCrvLink->RemoveFirstOrLastCurve( false) ;
|
|
}
|
|
else {
|
|
Point3d ptS ; pCrvLink->GetStartPoint( ptS) ;
|
|
double dUE ; pCrvOffs0->GetParamAtPoint( ptS, dUE, dTol) ;
|
|
pCrvOffs0->TrimEndAtParam( dUE) ;
|
|
}
|
|
// modifico Offset1 per raccordarlo al nuovo Link
|
|
Point3d ptE ; pCrvLink->GetEndPoint( ptE) ;
|
|
double dUS ; pCrvOffs1->GetParamAtPoint( ptE, dUS, dTol) ;
|
|
if ( bFirstOffs1 && pCrvOffs1->IsClosed())
|
|
pCrvOffs1->ChangeStartPoint( dUS) ;
|
|
else
|
|
pCrvOffs1->TrimStartAtParam( dUS) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
static bool
|
|
GetConformalLinkForOpenCrv( const ICurveComposite* pCrvOffs0, const ICurveComposite* pCrvOffs1,
|
|
const ICRVCOMPOPOVECTOR& vCrvClassBorder, const PocketParams& PockParams,
|
|
double dTol, ICurveComposite* pCrvLink)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
|
|
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid())
|
|
return false ;
|
|
pCrvLink->Clear() ;
|
|
|
|
// ricavo gli estremi del Link
|
|
Point3d ptLinkS ; pCrvOffs0->GetEndPoint( ptLinkS) ;
|
|
Point3d ptLinkE ; pCrvOffs1->GetStartPoint( ptLinkE) ;
|
|
// ricavo i parametri sulla curva di bordo che interessa i due estremi ( 1 sola !)
|
|
for ( int i = 0 ; i < int( vCrvClassBorder.size()) ; ++ i) {
|
|
if ( vCrvClassBorder[i]->IsPointOn( ptLinkS, dTol) &&
|
|
vCrvClassBorder[i]->IsPointOn( ptLinkE, dTol)) {
|
|
double dPar0 ; vCrvClassBorder[i]->GetParamAtPoint( ptLinkS, dPar0, dTol) ;
|
|
double dPar1 ; vCrvClassBorder[i]->GetParamAtPoint( ptLinkE, dPar1, dTol) ;
|
|
// ricavo le due curve possibili di Link
|
|
PtrOwner<ICurveComposite> pCrvLinkA( ConvertCurveToComposite( vCrvClassBorder[i]->CopyParamRange( dPar0, dPar1))) ;
|
|
PtrOwner<ICurveComposite> pCrvLinkB( ConvertCurveToComposite( vCrvClassBorder[i]->CopyParamRange( dPar1, dPar0))) ;
|
|
// scelgo quella migliore
|
|
double dLenA = 0. ;
|
|
if ( ! IsNull( pCrvLinkA) && pCrvLinkA->IsValid())
|
|
pCrvLinkA->GetLength( dLenA) ;
|
|
double dLenB = 0. ;
|
|
if ( ! IsNull( pCrvLinkB) && pCrvLinkB->IsValid()) {
|
|
pCrvLinkB->Invert() ;
|
|
pCrvLinkB->GetLength( dLenB) ;
|
|
}
|
|
if ( dLenA > dLenB) {
|
|
if ( ! IsNull( pCrvLinkB) && pCrvLinkB->IsValid())
|
|
pCrvLink->CopyFrom( pCrvLinkB) ;
|
|
}
|
|
else {
|
|
if ( ! IsNull( pCrvLinkA) && pCrvLinkA->IsValid())
|
|
pCrvLink->CopyFrom( pCrvLinkA) ;
|
|
}
|
|
break ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetConformalLink( ICurveComposite* pCrvOffs0, ICurveComposite* pCrvOffs1, PocketParams& PockParams,
|
|
const ICRVCOMPOPOVECTOR& vCrvClassBorder, ICurveComposite* pCrvLink)
|
|
{
|
|
// controllo validità delle due curve di Offset
|
|
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
|
|
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid())
|
|
return false ;
|
|
pCrvLink->Clear() ;
|
|
const double TOL = 150 * EPS_SMALL ;
|
|
|
|
// flag per curve di Offset aperte o chiuse
|
|
bool bOpen0 = ( ! pCrvOffs0->IsClosed()) ;
|
|
bool bOpen1 = ( ! pCrvOffs1->IsClosed()) ;
|
|
|
|
// se passo da una curva aperta ad un'altra curva aperta ( uso i bordi di pSfrClass)
|
|
if ( bOpen0 && bOpen1) {
|
|
// ricavo la curva di Link
|
|
if ( ! GetConformalLinkForOpenCrv( pCrvOffs0, pCrvOffs1, vCrvClassBorder, PockParams, TOL, pCrvLink))
|
|
return false ;
|
|
// smusso il Link raccordandolo
|
|
if ( pCrvLink->IsValid())
|
|
SmoothLinkByOffs( pCrvOffs0, pCrvOffs1, pCrvLink, PockParams, false, false, PockParams.dRad / 8., TOL) ;
|
|
}
|
|
else {
|
|
// clono le due curve di Offset
|
|
PtrOwner<ICurveComposite> pCrvOffs0_cl( CloneCurveComposite( pCrvOffs0)) ;
|
|
PtrOwner<ICurveComposite> pCrvOffs1_cl( CloneCurveComposite( pCrvOffs1)) ;
|
|
if ( IsNull( pCrvOffs0_cl) || IsNull( pCrvOffs1_cl) || ! pCrvOffs0_cl->IsValid() || ! pCrvOffs1_cl->IsValid())
|
|
return false ;
|
|
// controllo se gli Offset sono stati ricavati alla prima iterazione
|
|
bool bFirstIterOffs0 = ( pCrvOffs0->GetTempProp( 0) == 0) ;
|
|
bool bFirstIterOffs1 = ( pCrvOffs1->GetTempProp( 0) == 0) ;
|
|
// creo la curva che le collegherà
|
|
bool bSmooth = PockParams.bSmooth ;
|
|
PockParams.bSmooth = false ;
|
|
|
|
// cerco il chunk ipotetico che contiene le due curve di Offset
|
|
INTVECTOR vIndOffs0, vIndOffs1 ;
|
|
if ( ! GetIndOfHypoteticalChunk( vCrvClassBorder, pCrvOffs0, pCrvOffs1, vIndOffs0, vIndOffs1))
|
|
return false ;
|
|
// se appartengono a due chunk diversi, allora il link non esiste
|
|
if ( int( vIndOffs0.size()) != int( vIndOffs1.size()))
|
|
return true ;
|
|
for ( int i = 0 ; i < int( vIndOffs0.size()) ; ++ i) {
|
|
if ( vIndOffs0[i] != vIndOffs1[i])
|
|
return true ;
|
|
}
|
|
// se vuoti, allora il link non esiste
|
|
if ( vIndOffs0.empty() || vIndOffs1.empty()) // &&
|
|
return true ;
|
|
// se appartenenti allo stesso chunk, allora creo link lineare
|
|
ICRVCOMPOPOVECTOR vCrvChunk ;
|
|
for ( int i = 0 ; i < int( vIndOffs0.size()) ; ++ i)
|
|
vCrvChunk.emplace_back( vCrvClassBorder[vIndOffs0[i]]->Clone()) ;
|
|
if ( ! CutCurveToConnect( pCrvOffs0, pCrvOffs1, vCrvChunk, PockParams,
|
|
( ( bOpen0 || bFirstIterOffs0) ? 0. : 10 * EPS_SMALL),
|
|
( ( bOpen1 || bFirstIterOffs1) ? 0. : 10 * EPS_SMALL),
|
|
pCrvLink) ||
|
|
! pCrvLink->IsValid()) {
|
|
// se curva di Link non valida, cerco una strada più semplice
|
|
pCrvLink->Clear() ;
|
|
pCrvOffs0->CopyFrom( pCrvOffs0_cl) ;
|
|
pCrvOffs1->CopyFrom( pCrvOffs1_cl) ;
|
|
// recupero i vettori tangente iniziali ( le curve sono chiuse )
|
|
Vector3d vtS, vtE ;
|
|
Point3d ptS, ptE ;
|
|
if ( ! pCrvOffs0->GetStartDir( vtS) || ! pCrvOffs1->GetStartDir( vtE) ||
|
|
! pCrvOffs0->GetStartPoint( ptS) || ! pCrvOffs1->GetStartPoint( ptE))
|
|
return false ;
|
|
// creo il bi-arco tra esse
|
|
if ( ! CalcBoundedSmoothedLink( ptS, vtS, ptE, vtE, 0.5, vCrvChunk, PockParams, pCrvLink) ||
|
|
! pCrvLink->IsValid())
|
|
return false ;
|
|
}
|
|
PockParams.bSmooth = bSmooth ;
|
|
SmoothLinkByOffs( pCrvOffs0, pCrvOffs1, pCrvLink, PockParams, bFirstIterOffs0, bFirstIterOffs1,
|
|
PockParams.dRad / 16., TOL) ;
|
|
// per sicurezza aggiorno i nuovi punti e i nuovi parametri
|
|
double dUNewE ;
|
|
Point3d ptStartNext ;
|
|
pCrvLink->GetEndPoint( ptStartNext) ;
|
|
pCrvOffs1->GetParamAtPoint( ptStartNext, dUNewE) ;
|
|
pCrvOffs1->ChangeStartPoint( dUNewE) ;
|
|
}
|
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CheckConformalRetractLink( const ICurveComposite* pCrvOffs0, const ICurveComposite* pCrvOffs1,
|
|
const ICRVCOMPOPOVECTOR& vCrvClassBorder, const PocketParams& PockParams,
|
|
bool& bCalcLink)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
|
|
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid())
|
|
return false ;
|
|
bCalcLink = true ; // di base calcolo il Link
|
|
|
|
// recupero gli estremi di un possibile collegamento tra i punti
|
|
Point3d ptA ; pCrvOffs0->GetEndPoint( ptA) ;
|
|
Point3d ptB ; pCrvOffs1->GetStartPoint( ptB) ;
|
|
// se vicini -> no retroazione
|
|
if ( SqDist( ptA, ptB) < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL)
|
|
return true ;
|
|
// se distanti
|
|
else {
|
|
// controllo se sono aperte
|
|
bool bOpen_i = ( ! pCrvOffs0->IsClosed()) ;
|
|
bool bOpen_ii = ( ! pCrvOffs1->IsClosed()) ;
|
|
// se entrambe aperte
|
|
if ( bOpen_i && bOpen_ii) {
|
|
// calcolo il collegamento tra le due curve
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink) ||
|
|
! GetConformalLinkForOpenCrv( pCrvOffs0, pCrvOffs1, vCrvClassBorder, PockParams,
|
|
25 * EPS_SMALL, pCrvLink))
|
|
return false ;
|
|
// se Link valido calcolo la sua lunghezza
|
|
double dLinkLen = 0. ;
|
|
if ( pCrvLink->IsValid())
|
|
pCrvLink->GetLength( dLinkLen) ;
|
|
// minima distanza da ptA
|
|
DistPointCurve DistPtACrv( ptA, *pCrvOffs1) ;
|
|
double dMyDist ;
|
|
if ( DistPtACrv.GetSqDist( dMyDist) &&
|
|
dMyDist < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL) {
|
|
bCalcLink = ( dLinkLen < 4 * PockParams.dRad) ;
|
|
return true ;
|
|
}
|
|
// minima distanza da ptB
|
|
DistPointCurve DistPtBCrv( ptB, *pCrvOffs0) ;
|
|
if ( DistPtBCrv.GetSqDist( dMyDist) &&
|
|
dMyDist < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL) {
|
|
bCalcLink = ( dLinkLen < 4 * PockParams.dRad) ;
|
|
return true ;
|
|
}
|
|
}
|
|
// ... servono altri controlli ??? ( vedere dai casi)
|
|
}
|
|
|
|
bCalcLink = false ;
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetConformalIndices( const VICRVCOMPOPOVECTOR& vCrvOffs, int nMyInd0, int nMyInd1,
|
|
const PocketParams& PockParams, const ICRVCOMPOPOVECTOR& vCrvClassBorder,
|
|
int& nInd0, int& nInd1)
|
|
{
|
|
// controllo che l'indice sia sensato
|
|
if ( vCrvOffs.empty() || nMyInd0 < 0 || nMyInd0 >= int( vCrvOffs.size()) ||
|
|
( nMyInd1 < 0 || nMyInd1 >= int( vCrvOffs[nMyInd0].size())))
|
|
return false ;
|
|
nInd0 = -1 ;
|
|
nInd1 = -1 ;
|
|
|
|
// il punto di riferimento è il punto finale della curva corrente
|
|
Point3d ptRef ; vCrvOffs[nMyInd0][nMyInd1]->GetEndPoint( ptRef) ;
|
|
// controllo se la curva è chiusa o aperta
|
|
bool bIsClosed = ( vCrvOffs[nMyInd0][nMyInd1]->IsClosed()) ;
|
|
// controllo se la curva è di primo Offset ( in questo caso non ho curve successive)
|
|
bool bFistOffs = ( nMyInd0 == int( vCrvOffs.size() - 1)) ;
|
|
|
|
// scorro le curve di indice successivo
|
|
double dLimInfDist = INFINITO ;
|
|
int nInd = -1 ;
|
|
for ( int i = 0 ; ! bFistOffs && i < int( vCrvOffs[nMyInd0 + 1].size()) ; ++ i) {
|
|
// se non attiva, passo alla successiva
|
|
if ( vCrvOffs[nMyInd0 + 1][i]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE)
|
|
continue ;
|
|
// se di topologia differente, passo alla successiva
|
|
if ( ( bIsClosed != vCrvOffs[nMyInd0 + 1][i]->IsClosed()))
|
|
continue ;
|
|
// se richiede retroazione, passo alla successiva
|
|
bool bCalcLink = true ;
|
|
if ( ! CheckConformalRetractLink( vCrvOffs[nMyInd0][nMyInd1], vCrvOffs[nMyInd0 + 1][i],
|
|
vCrvClassBorder, PockParams, bCalcLink))
|
|
return false ;
|
|
if ( ! bCalcLink)
|
|
continue ;
|
|
// se aperta
|
|
if ( ! bIsClosed) {
|
|
Point3d ptStart ; vCrvOffs[nMyInd0 + 1][i]->GetStartPoint( ptStart) ;
|
|
double dDist = SqDist( ptRef, ptStart) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = i ;
|
|
}
|
|
}
|
|
// se chiusa
|
|
else {
|
|
DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nMyInd0 + 1][i]) ;
|
|
MinDistPCInfo aInfo ;
|
|
if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) {
|
|
double dDist = SqDist( ptRef, aInfo.ptQ) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = i ;
|
|
vCrvOffs[nMyInd0 + 1][i]->ChangeStartPoint( aInfo.dPar) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se curva trovata, restituisco gli indici
|
|
if ( nInd != -1) {
|
|
nInd0 = nMyInd0 + 1 ;
|
|
nInd1 = nInd ;
|
|
return true ;
|
|
}
|
|
/* se non ho trovato alcuna curva, allora possono verificarsi le seguenti situazioni :
|
|
- all'indice successivo ho solo curve di topologia differente
|
|
- ho già percorso tutte le curve topologicamente uguali all'indice successivo
|
|
- sono una curva di primo Offset
|
|
- tutte le curve all'indice succesivo di topologia equivalente richiedono una retroazione
|
|
=> devo azzerare nInd0 e ricercare la curva di medesima topologia valida più vicina
|
|
*/
|
|
nInd0 = 0 ;
|
|
nInd = -1 ;
|
|
for ( ; nInd0 < int( vCrvOffs.size()) ; ++ nInd0) {
|
|
nInd1 = 0 ;
|
|
dLimInfDist = INFINITO ;
|
|
for ( ; nInd1 < int( vCrvOffs[nInd0].size()) ; ++ nInd1) {
|
|
// se non attiva, passo alla successiva
|
|
if ( vCrvOffs[nInd0][nInd1]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE)
|
|
continue ;
|
|
// se di topologia differente, passo alla successiva
|
|
if ( ( bIsClosed != vCrvOffs[nInd0][nInd1]->IsClosed()))
|
|
continue ;
|
|
// se aperta
|
|
if ( ! bIsClosed) {
|
|
Point3d ptStart ; vCrvOffs[nInd0][nInd1]->GetStartPoint( ptStart) ;
|
|
double dDist = SqDist( ptRef, ptStart) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = nInd1 ;
|
|
}
|
|
}
|
|
// se chiusa
|
|
else {
|
|
DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nInd0][nInd1]) ;
|
|
MinDistPCInfo aInfo ;
|
|
if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) {
|
|
double dDist = SqDist( ptRef, aInfo.ptQ) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = nInd1 ;
|
|
vCrvOffs[nInd0][nInd1]->ChangeStartPoint( aInfo.dPar) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se curva trovata, restituisco gli indici
|
|
if ( nInd != -1) {
|
|
nInd1 = nInd ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
/* se non ho trovato alcuna curva, allora possono verificarsi le seguenti situazioni :
|
|
- ho solo curve topologicamente differenti dalla mia
|
|
- non ho curve attive
|
|
=> devo azzerare nInd0 e ricercare la curva valida più vicina
|
|
*/
|
|
nInd0 = 0 ;
|
|
dLimInfDist = INFINITO ;
|
|
nInd = -1 ;
|
|
for ( ; nInd0 < int( vCrvOffs.size()) ; ++ nInd0) {
|
|
nInd1 = 0 ;
|
|
for ( ; nInd1 < int( vCrvOffs[nInd0].size()) ; ++ nInd1) {
|
|
// se non attiva, passo alla successiva
|
|
if ( vCrvOffs[nInd0][nInd1]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE)
|
|
continue ;
|
|
// se la curva successiva è aperta
|
|
if ( ! vCrvOffs[nInd0][nInd1]->IsClosed()) {
|
|
Point3d ptStart ; vCrvOffs[nInd0][nInd1]->GetStartPoint( ptStart) ;
|
|
double dDist = SqDist( ptRef, ptStart) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = nInd1 ;
|
|
}
|
|
}
|
|
// se la curva successiva è chiusa
|
|
else {
|
|
DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nInd0][nInd1]) ;
|
|
MinDistPCInfo aInfo ;
|
|
if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) {
|
|
double dDist = SqDist( ptRef, aInfo.ptQ) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = nInd1 ;
|
|
vCrvOffs[nInd0][nInd1]->ChangeStartPoint( aInfo.dPar) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se curva trovata, restituisco gli indici
|
|
if ( nInd != -1) {
|
|
nInd1 = nInd ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// non ho più curve disponibili
|
|
nInd0 = -1 ;
|
|
nInd1 = -1 ;
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustCloseEsgesForConformalGuide( ICurveComposite* pCrvCompo, const PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// se non ho una regione limite, allora non faccio nulla
|
|
if ( ! PockParams.SfrLimit.IsValid())
|
|
return true ;
|
|
|
|
// piccolo Offset per la superficie di classificazione
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CloneSurfFlatRegion( &PockParams.SfrLimit)) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid() ||
|
|
! pSfrLimit->Offset( 100 * EPS_SMALL, ICurve::OFF_FILLET))
|
|
return false ;
|
|
|
|
// recupero le parti omogenee della curva
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
GetHomogeneousParts( pCrvCompo, PockParams, vpCrvs) ;
|
|
|
|
// i tratti aperti che fanno overlap con la regione limite diventano chiusi
|
|
PtrOwner<ICurveComposite> pCrvFinal( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvFinal))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
// nuovo curva
|
|
PtrOwner<ICurveComposite> pMyCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pMyCompo))
|
|
return false ;
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrLimit->GetCurveClassification( *vpCrvs[i], 125 * EPS_SMALL, ccClass)) {
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
// recupero il tratto di curva
|
|
PtrOwner<ICurveComposite> pMyCurve( ConvertCurveToComposite( vpCrvs[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
|
|
if ( ! IsNull( pMyCurve) && pMyCurve->IsValid()) {
|
|
// se non esterna alla regione limite, il sottotratto deve essere chiuso
|
|
if ( ccClass[j].nClass != CRVC_OUT) {
|
|
for ( int k = 0 ; k < pMyCurve->GetCurveCount() ; ++ k)
|
|
pMyCurve->SetCurveTempProp( k, TEMP_PROP_CLOSE_EDGE, 0) ;
|
|
}
|
|
if ( ! pMyCompo->AddCurve( Release( pMyCurve)))
|
|
return true ; // non faccio nulla...
|
|
}
|
|
}
|
|
}
|
|
vpCrvs[i].Set( pMyCompo) ;
|
|
}
|
|
if ( ! pCrvFinal->AddCurve( Release( vpCrvs[i])))
|
|
return true ; // non faccio nulla...
|
|
}
|
|
if ( pCrvFinal->IsValid())
|
|
pCrvCompo->CopyFrom( pCrvFinal) ;
|
|
|
|
return ( pCrvCompo != nullptr && pCrvCompo->IsValid()) ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ModifyConformalStartPoint( ICurveComposite* pCrvFirst, const PocketParams& PockParams,
|
|
const ISurfFlatRegion* pSfrPock)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid() ||
|
|
pSfrPock == nullptr || ! pSfrPock->IsValid())
|
|
return false ;
|
|
|
|
// se la curva è aperta, allora non devo fare nulla
|
|
if ( ! pCrvFirst->IsClosed())
|
|
return true ;
|
|
|
|
// ricavo i lati aperti della superficie di classificazione
|
|
ICRVCOMPOPOVECTOR vCrvOpenEdges ;
|
|
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
|
|
for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) {
|
|
// recupero il Loop
|
|
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ;
|
|
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
|
|
return false ;
|
|
// recupero le parti omogenee
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
GetHomogeneousParts( pCrvLoop, PockParams, vpCrvs) ;
|
|
// recupero le parti interne
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE)
|
|
vCrvOpenEdges.emplace_back( Release( vpCrvs[i])) ;
|
|
}
|
|
}
|
|
}
|
|
// se non ho ricavato tratti aperti, errore
|
|
if ( vCrvOpenEdges.empty())
|
|
return false ;
|
|
|
|
/* NB.
|
|
- Per i casi previsti dalle lavorazioni CONFORMAL devo trovare solo 1 tratto aperto, quindi
|
|
considero quello di indice 0
|
|
- Tra la curva pCrvFirst e la curva di lato aperto, devo trovare i punti più vicino
|
|
( per ora l'algoritmo è euristico, bisogna aggiornarlo con VORONOI)
|
|
*/
|
|
|
|
// Algoritmo Euristico da sostituire con Voronoi :
|
|
// 1) Cerco il punto più vicino al lato aperto dato il punto iniziale di pCrvFirst
|
|
Point3d ptStart ; pCrvFirst->GetStartPoint( ptStart) ;
|
|
DistPointCurve distCalculator0( ptStart, *vCrvOpenEdges[0]) ;
|
|
Point3d ptMinDist ;
|
|
int nFlag ;
|
|
if ( ! distCalculator0.GetMinDistPoint( 0., ptMinDist, nFlag))
|
|
return false ;
|
|
// 2) Cerco il punto più vicino su pCrvFirst da ptMinDist
|
|
DistPointCurve distCalculator1( ptMinDist, *pCrvFirst) ;
|
|
double dMinPar ;
|
|
if ( ! distCalculator1.GetParamAtMinDistPoint( 0., dMinPar, nFlag))
|
|
return false ;
|
|
// 3) Cambio il punto iniziale di pCrvFirst nel parametro ricavato
|
|
pCrvFirst->ChangeStartPoint( dMinPar) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcConformalOffsAndLinks( VICRVCOMPOPOVECTOR& vvCrvOffs, const ISurfFlatRegion* pSfrChunk,
|
|
const ISurfFlatRegion* pSfrClass, PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvOffs,
|
|
ICURVEPOVECTOR& vCrvLink)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() ||
|
|
pSfrClass == nullptr || ! pSfrClass->IsValid())
|
|
return false ;
|
|
vCrvOffs.clear() ;
|
|
vCrvLink.clear() ;
|
|
if ( vvCrvOffs.empty())
|
|
return true ;
|
|
|
|
// rendo attive tutte le curve di Offset mediante prima TmpProp
|
|
for ( int i = 0 ; i < int( vvCrvOffs.size()) ; ++ i) {
|
|
for ( int j = 0 ; j < int( vvCrvOffs[i].size()) ; ++ j)
|
|
vvCrvOffs[i][j]->SetTempProp( TEMP_PROP_CURVE_ACTIVE, 0) ;
|
|
}
|
|
|
|
/* NB. curve di vCrvOffs
|
|
TempProp0 -> Se l'offset è attivo/non attivo
|
|
TempProp1 -> Side {MDS_LEFT, MDS_RIGHT} per identificare l'interno della curva
|
|
TempParam0 -> { vuoto } ; verrà utilizzato per la Feed
|
|
TempParam1 -> { vuoto }
|
|
*/
|
|
|
|
// inverto il vettore degli Offset, in modo da partire dalle curve più distanti dai chiusi
|
|
reverse( vvCrvOffs.begin(), vvCrvOffs.end()) ;
|
|
|
|
// determino il punto iniziale della prima curva di Offset
|
|
if ( ! ModifyConformalStartPoint( vvCrvOffs[0][0], PockParams, pSfrChunk))
|
|
return false ;
|
|
|
|
/* NB.
|
|
I link che collegano due curve di Offset aperte devono partire dalla prima e, seguendo il
|
|
bordo della regione di classificazione ( pSfrClass) arrivare alla seconda.
|
|
*/
|
|
/* NB.
|
|
I link che collegano due curve di Offset chiuse tra di loro vengono calcolati esattamente
|
|
come per i percorsi SPIRAL, pertanto devo definire un insieme di curve di primo Offset sui
|
|
quali smussare i Link per evitare di uscire dalla regione di svuotatura
|
|
*/
|
|
ICRVCOMPOPOVECTOR vCrvSfrClass ;
|
|
if ( ! GetSfrCrvCompoLoops( pSfrClass, vCrvSfrClass))
|
|
return false ;
|
|
|
|
/* NB.
|
|
Se POCKET_CONFORMAL_ZIGZAG :
|
|
- vCrvCompoOffs contiene gli Offset ordinati
|
|
- vCrvLinks contiene il link che collega l'Offset (i-1)-esimo con l'Offset (i)-esimo
|
|
[ per definizione il primo Link è null]
|
|
[ se tra due Offset non c'è Link, allora esso sarà null]
|
|
Se POCKET_CONFORMAL_ONEWAY
|
|
- vCrvCompoOffs contiene gli Offset ordinati
|
|
- vCrvLinks è un vettore di null
|
|
|
|
vCrvCompoOffs e vCrvLinks avranno la stessa dimensione
|
|
*/
|
|
|
|
// ------------ Ordino Gli Offset ------------
|
|
int nInd0 = 0 ;
|
|
int nInd1 = 0 ;
|
|
while ( nInd0 != -1 && nInd1 != -1) {
|
|
// inserisco la curva attuale nel vettore degli Offset
|
|
vCrvOffs.emplace_back( CloneCurveComposite( vvCrvOffs[nInd0][nInd1])) ;
|
|
// salvo come prima TmpProp il numero di Offset che ha generato tale curva
|
|
vCrvOffs.back()->SetTempProp( int( vvCrvOffs.size()) - nInd0 - 1, 0) ;
|
|
// disattivo tale curva per la ricerca
|
|
vvCrvOffs[nInd0][nInd1]->SetTempProp( TEMP_PROP_CURVE_INACTIVE, 0) ;
|
|
// cerco la curva successiva per il percorso
|
|
int nNextInd0 = -1 ;
|
|
int nNextInd1 = -1 ;
|
|
if ( ! GetConformalIndices( vvCrvOffs, nInd0, nInd1, PockParams, vCrvSfrClass, nNextInd0, nNextInd1))
|
|
return false ;
|
|
// aggiorno gli indici
|
|
nInd0 = nNextInd0 ;
|
|
nInd1 = nNextInd1 ;
|
|
}
|
|
// se non ho ottenuto nulla, esco
|
|
if ( vCrvOffs.empty())
|
|
return true ;
|
|
|
|
// ------------ Definisco i Link ------------
|
|
if ( PockParams.nType == POCKET_CONFORMAL_ONEWAY)
|
|
vCrvLink.resize( vCrvOffs.size()) ;
|
|
else {
|
|
// per definizione il primo Link è nullo
|
|
vCrvLink.resize( 1) ;
|
|
// scorro le curve di Offset a coppie
|
|
for ( int i = 0 ; i < int( vCrvOffs.size()) - 1 ; ++ i) {
|
|
// controllo se bisogna calcolare il Link tra i due Offset
|
|
bool bCalcLink = true ;
|
|
if ( ! CheckConformalRetractLink( vCrvOffs[i], vCrvOffs[i+1], vCrvSfrClass, PockParams, bCalcLink))
|
|
return false ;
|
|
if ( bCalcLink) {
|
|
// se link da calcolare, lo ricavo
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
// se non calcolabile, allora retroazione, altrimenti lo memorizzo
|
|
if ( ! GetConformalLink( vCrvOffs[i], vCrvOffs[i+1], PockParams, vCrvSfrClass, pCrvLink) ||
|
|
IsNull( pCrvLink) || ! pCrvLink->IsValid() || pCrvLink->GetCurveCount() == 0)
|
|
vCrvLink.resize( int( vCrvLink.size()) + 1) ;
|
|
else
|
|
vCrvLink.emplace_back( Release( pCrvLink)) ;
|
|
}
|
|
else
|
|
vCrvLink.resize( int( vCrvLink.size()) + 1) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ExtendConformalOffsAndSetFeed( const ISurfFlatRegion* pSfrPock, const PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvOffs, ICURVEPOVECTOR& vCrvLinks)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrPock == nullptr || ! pSfrPock->IsValid())
|
|
return false ;
|
|
if ( vCrvOffs.empty())
|
|
return true ;
|
|
|
|
/*
|
|
dalla superificie estesa presso i lati aperti, effettuo un Offset interno pari alla somma del
|
|
raggio utensile e dell'offset radiale. La ricerca delle zone non svuotate avviene all'interno
|
|
di questa regione
|
|
*/
|
|
ICRVCOMPOPOVECTOR vCrvFirstOffs ;
|
|
if ( ! GetFirstOffsCrvFromSfr( pSfrPock, - PockParams.dRad - PockParams.dRadialOffset, vCrvFirstOffs))
|
|
return false ;
|
|
// determino eventuale regioni con parti non svuotate e imposto la Feed alle curve
|
|
PtrOwner<ISurfFlatRegion> pSfrUncleared( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrUncleared))
|
|
return false ;
|
|
if ( GetUnclearedRegionAndSetFeed( vCrvFirstOffs, vCrvOffs, vCrvLinks, nullptr, PockParams, pSfrUncleared)) {
|
|
// estendo i percorsi di Offset se richiesto
|
|
if ( ! RemoveExtraParts( pSfrUncleared, vCrvOffs, vCrvFirstOffs, PockParams))
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddLeadInToCurveConformalPaths( const ISurfFlatRegion* pSfrOrig, const ISurfFlatRegion* pSfrPock,
|
|
const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvPaths)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrOrig == nullptr || ! pSfrOrig->IsValid() ||
|
|
pSfrPock == nullptr || ! pSfrPock->IsValid())
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) {
|
|
if ( vCrvPaths[i] == nullptr || ! vCrvPaths[i]->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
// ricavo le curve aperte della superficie di pocketing
|
|
ICRVCOMPOPOVECTOR vCrvOpenEdge ;
|
|
{
|
|
ICRVCOMPOPOVECTOR vCrvLoops ;
|
|
if ( ! GetSfrCrvCompoLoops( pSfrPock, vCrvLoops))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) {
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
GetHomogeneousParts( vCrvLoops[i], PockParams, vpCrvs) ;
|
|
for ( int j = 0 ; j < int( vpCrvs.size()) ; ++ j) {
|
|
if ( vpCrvs[j]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE)
|
|
vCrvOpenEdge.emplace_back( Release( vpCrvs[j])) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// scorro i percorsi ricavati
|
|
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) {
|
|
// recupero il punto iniziale del percorso
|
|
Point3d ptStart ; vCrvPaths[i]->GetStartPoint( ptStart) ;
|
|
// se il punto inziale è interno alla superficie originaria
|
|
bool bIsInside ;
|
|
if ( IsPointInsideSurfFr( ptStart, pSfrOrig, EPS_SMALL, bIsInside) && bIsInside) {
|
|
// definizione del segmento per l'entrata
|
|
PtrOwner<ICurveLine> pSeg( CreateCurveLine()) ;
|
|
if ( IsNull( pSeg))
|
|
return false ;
|
|
double dSqLenMin = INFINITO ;
|
|
// ricavo il punto più vicino alle curve aperte della superficie di classificazione
|
|
for ( int j = 0 ; j < int( vCrvOpenEdge.size()) ; ++ j) {
|
|
DistPointCurve DistPtCrv( ptStart, *vCrvOpenEdge[j]) ;
|
|
Point3d ptMinDist ;
|
|
int nFlag ;
|
|
// controllo se il segmento che congiunge i due estremi non rovina il grezzo
|
|
bool bSafe = ( DistPtCrv.GetMinDistPoint( 0, ptMinDist, nFlag)) ;
|
|
// se troppo piccolo, non faccio nulla
|
|
double dSqSegLen = SqDist( ptStart, ptMinDist) ;
|
|
if ( dSqSegLen < SQ_EPS_SMALL) {
|
|
pSeg.Set( CreateCurveLine()) ;
|
|
break ;
|
|
}
|
|
// creazione del segmento
|
|
if ( bSafe) {
|
|
PtrOwner<ICurveLine> pCurrSeg( CreateCurveLine()) ;
|
|
if ( IsNull( pCurrSeg) || ! pCurrSeg->Set( ptMinDist, ptStart))
|
|
return false ;
|
|
double dSqSegLen = SqDist( ptStart, ptMinDist) ;
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CloneSurfFlatRegion( &PockParams.SfrLimit)) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid() ||
|
|
! pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET))
|
|
return false ;
|
|
// controllo distanza segmento
|
|
CRVCVECTOR ccClass ;
|
|
bSafe = ( pSfrLimit->GetCurveClassification( *pSeg, 25 * EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) ;
|
|
}
|
|
if ( bSafe && dSqSegLen < dSqLenMin) {
|
|
dSqLenMin = dSqSegLen ;
|
|
pSeg.Set( pCurrSeg) ;
|
|
}
|
|
}
|
|
}
|
|
// se segmento trovato, lo aggiungo alla curva
|
|
if ( pSeg->IsValid()) {
|
|
if ( ! vCrvPaths[i]->AddCurve( Release( pSeg), false, 25 * EPS_SMALL))
|
|
return false ;
|
|
vCrvPaths[i]->SetCurveTempParam( 0, GetMinFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
|
|
// estendo il percorso sul tratto iniziale e finale ( se ammissibile)
|
|
Vector3d vtTan ; vCrvPaths[i]->GetStartDir( vtTan) ;
|
|
vtTan.Invert() ;
|
|
bool bOkExtended ;
|
|
if ( ! ExtendPath( vCrvPaths[i], pSfrOrig, PockParams, vtTan, false, bOkExtended))
|
|
return false ;
|
|
vCrvPaths[i]->GetEndDir( vtTan) ;
|
|
if ( ! ExtendPath( vCrvPaths[i], pSfrOrig, PockParams, vtTan, true, bOkExtended))
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
OrderAndExtendConformalPaths( ICRVCOMPOPOVECTOR& vCrvPaths, const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrOrig,
|
|
PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() ||
|
|
pSfrOrig == nullptr || ! pSfrOrig->IsValid())
|
|
return false ;
|
|
if ( int( vCrvPaths.size()) < 2)
|
|
return true ;
|
|
|
|
// superficie di controllo per parti isolate
|
|
PtrOwner<ISurfFlatRegion> pSfrToRemove( CloneSurfFlatRegion( pSfrChunk)) ;
|
|
if ( IsNull( pSfrToRemove) || ! pSfrToRemove->IsValid() ||
|
|
! pSfrToRemove->Intersect( *pSfrOrig))
|
|
return false ;
|
|
|
|
// creo le regioni piane di svuotatura dei percorsi
|
|
ISURFFRPOVECTOR vSfrRemoved ; vSfrRemoved.resize( vCrvPaths.size()) ;
|
|
ISURFFRPOVECTOR vSfrRemaining ; vSfrRemaining.reserve( vCrvPaths.size()) ;
|
|
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) {
|
|
// creo la regione di svuotatura di tale percorso
|
|
vSfrRemoved[i].Set( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vCrvPaths[i]), PockParams.dRad + 500 * EPS_SMALL, false, false)) ;
|
|
if ( IsNull( vSfrRemoved[i]) || ! vSfrRemoved[i]->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
// scorro i percorsi ricavati
|
|
INTVECTOR vInds ; vInds.resize( vCrvPaths.size(), -1) ;
|
|
int nChunkRef = pSfrToRemove->GetChunkCount() ;
|
|
for ( int i = 0 ; i < int( vInds.size()) ; ++ i) {
|
|
int j = 0 ;
|
|
INTVECTOR vTempInds ;
|
|
for ( ; j < int( vCrvPaths.size()) ; ++ j) {
|
|
// se indice già presente
|
|
if ( find( vInds.begin(), vInds.end(), j) != vInds.end())
|
|
continue ;
|
|
// se sottraendo questa regione a quella originale ottengo più isole, allora non è il percorso ideale
|
|
PtrOwner<ISurfFlatRegion> pSfrRemain( CloneSurfFlatRegion( pSfrToRemove)) ;
|
|
if ( IsNull( pSfrRemain) || ! pSfrRemain->IsValid())
|
|
continue ;
|
|
pSfrRemain->Subtract( *vSfrRemoved[j]) ;
|
|
// se aumento il numero di Chunk, allora non è il percorso ideale
|
|
if ( pSfrRemain->GetChunkCount() > nChunkRef)
|
|
continue ;
|
|
vTempInds.push_back( j) ;
|
|
}
|
|
if ( vTempInds.empty()) {
|
|
// se non ho trovato curva candidate, prendo l'indice disponibile più basso
|
|
for ( int k = 0 ; k < int( vInds.size()) ; ++ k) {
|
|
if ( find( vInds.begin(), vInds.end(), k) != vInds.end())
|
|
continue ;
|
|
vInds[i] = k ;
|
|
break ;
|
|
}
|
|
}
|
|
else if ( int( vTempInds.size()) == 1) {
|
|
// se la candidata è singola, allora è lei
|
|
vInds[i] = vTempInds[0] ;
|
|
if ( PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
|
|
if ( vCrvPaths[vInds[i]]->IsClosed() && i > 0) {
|
|
Point3d ptS ; vCrvPaths[vInds[i-1]]->GetStartPoint( ptS) ;
|
|
DistPointCurve DistCalculator( ptS, *vCrvPaths[vInds[i]]) ;
|
|
int nFlag ; double dUS ;
|
|
DistCalculator.GetParamAtMinDistPoint( 0, dUS, nFlag) ;
|
|
vCrvPaths[vInds[i]]->ChangeStartPoint( dUS) ;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// se lavorazione conformal ZigZag
|
|
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) {
|
|
// se ne ho trovate di più, allora euristicamente prendo la curve che effettua
|
|
// meno overlap con la regione da limite
|
|
double dMinLen = INFINITO ;
|
|
double dMinArea = INFINITO ;
|
|
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
|
|
// privilegio le aperte
|
|
if ( vCrvPaths[vTempInds[k]]->IsClosed())
|
|
continue ;
|
|
// se la regione di incidenza è valida
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
dMinLen = 0. ; // non mi interessa in questo caso la lunghezza della curva
|
|
PtrOwner<ISurfFlatRegion> pSfrTest( CloneSurfFlatRegion( vSfrRemoved[vTempInds[k]])) ;
|
|
if ( IsNull( pSfrTest) || ! pSfrTest->IsValid())
|
|
return false ;
|
|
pSfrTest->Intersect( PockParams.SfrLimit) ;
|
|
double dArea ; pSfrTest->GetArea( dArea) ;
|
|
if ( dArea < dMinArea) {
|
|
dMinArea = dArea ;
|
|
vInds[i] = vTempInds[k] ;
|
|
if ( dArea < EPS_SMALL)
|
|
break ; // è ottima...
|
|
}
|
|
}
|
|
// se la regione di incidenza non è valida, scelgo erusiticamente la curva più corta
|
|
else {
|
|
double dLen ; vCrvPaths[vTempInds[k]]->GetLength( dLen) ;
|
|
if ( dLen < dMinLen) {
|
|
dMinLen = dLen ;
|
|
vInds[i] = vTempInds[k] ;
|
|
}
|
|
}
|
|
}
|
|
// se ho solo curve adiacenti a regioni di incidenza
|
|
if ( dMinArea > EPS_SMALL) {
|
|
struct MyCompoClass {
|
|
PtrOwner<ICurveComposite> pCrvExtLoop ;
|
|
int nIndex ;
|
|
int nBorders ;
|
|
} ;
|
|
// euristicamente scelgo quella il cui bordo esterno contiene più curve di bordo esterno
|
|
vector<MyCompoClass> vCrvExtBorder( vTempInds.size() ) ;
|
|
for ( int j = 0 ; j < int( vCrvExtBorder.size()) ; ++ j) {
|
|
// recupero il bordo esterno (mi auguro sia un solo chunk...)
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( vSfrRemoved[vTempInds[j]]->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() ||
|
|
! vCrvExtBorder[j].pCrvExtLoop.Set( pCrvExtLoop))
|
|
return false ;
|
|
vCrvExtBorder[j].nIndex = vTempInds[j] ;
|
|
vCrvExtBorder[j].nBorders = 0 ;
|
|
}
|
|
for ( int j = 0 ; j < int( vCrvExtBorder.size()) - 1 ; ++ j) {
|
|
for ( int k = j + 1 ; k < int( vCrvExtBorder.size()) ; ++ k) {
|
|
IntersCurveCurve ICC( *vCrvExtBorder[j].pCrvExtLoop, *vCrvExtBorder[k].pCrvExtLoop) ;
|
|
CRVCVECTOR ccClass ;
|
|
if ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1) {
|
|
// se i-esima interna a j-esima
|
|
if ( ccClass[0].nClass == CRVC_IN)
|
|
++ vCrvExtBorder[k].nBorders ;
|
|
else if ( ccClass[0].nClass == CRVC_OUT)
|
|
++ vCrvExtBorder[j].nBorders ;
|
|
}
|
|
}
|
|
}
|
|
// ordino il vettore delle curve sulla base di quante ne contiene
|
|
sort( vCrvExtBorder.begin(), vCrvExtBorder.end(),
|
|
[] ( const MyCompoClass& a, const MyCompoClass& b) {
|
|
return a.nBorders > b.nBorders ;
|
|
}) ;
|
|
// recupero l'indice migliore
|
|
if ( ! vCrvExtBorder.empty())
|
|
vInds[i] = vCrvExtBorder[0].nIndex ;
|
|
}
|
|
// cerco solo tra le chiuse se non ho aperte
|
|
if ( dMinLen > INFINITO - 1) {
|
|
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
|
|
if ( vCrvPaths[vTempInds[k]]->IsClosed()) {
|
|
double dLen ; vCrvPaths[vTempInds[k]]->GetLength( dLen) ;
|
|
if ( dLen < dMinLen) {
|
|
dMinLen = dLen ;
|
|
vInds[i] = vTempInds[k] ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se lavorazione conformal OneWay
|
|
else {
|
|
// se ne ho trovate di più, cerco euristicamente la più vicina al punto iniziale della curva
|
|
// se prima in assoluto, allora prendo la più corta
|
|
if ( i == 0) {
|
|
double dMinLen = INFINITO ;
|
|
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
|
|
double dLen ; vCrvPaths[vTempInds[k]]->GetLength( dLen) ;
|
|
if ( dLen < dMinLen) {
|
|
dMinLen = dLen ;
|
|
vInds[i] = vTempInds[k] ;
|
|
}
|
|
}
|
|
}
|
|
// altrimenti
|
|
else {
|
|
double dSqMinDist = INFINITO ;
|
|
Point3d ptS ; vCrvPaths[vInds[i-1]]->GetStartPoint( ptS) ;
|
|
Point3d ptTmpS ;
|
|
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
|
|
if ( vCrvPaths[vTempInds[k]]->IsClosed())
|
|
continue ;
|
|
vCrvPaths[vTempInds[k]]->GetStartPoint( ptTmpS) ;
|
|
double dMyDist = SqDist( ptS, ptTmpS) ;
|
|
if ( dMyDist < dSqMinDist) {
|
|
dSqMinDist = dMyDist ;
|
|
vInds[i] = vTempInds[k] ;
|
|
}
|
|
}
|
|
if ( dSqMinDist > INFINITO - 1) {
|
|
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
|
|
if ( vCrvPaths[vTempInds[k]]->IsClosed()) {
|
|
DistPointCurve DistCalculator( ptS, *vCrvPaths[vTempInds[k]]) ;
|
|
double dMySqDist ;
|
|
DistCalculator.GetSqDist( dMySqDist) ;
|
|
if ( dMySqDist < dSqMinDist) {
|
|
dSqMinDist = dMySqDist ;
|
|
vInds[i] = vTempInds[k] ;
|
|
double dUS ;
|
|
int nFlag ;
|
|
DistCalculator.GetParamAtMinDistPoint( 0, dUS, nFlag) ;
|
|
vCrvPaths[vTempInds[k]]->ChangeStartPoint( dUS) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// salvo la regione rimanente ( è già in posizione corretta nel vettore)
|
|
vSfrRemaining.emplace_back( CloneSurfFlatRegion( pSfrToRemove)) ;
|
|
// aggiorno la regione da svuotare e il numero di Chunk che si sono formati
|
|
pSfrToRemove->Subtract( *vSfrRemoved[vInds[i]]) ;
|
|
nChunkRef = pSfrToRemove->GetChunkCount() ;
|
|
}
|
|
|
|
// ordino percorsi e superfici progressive per estendendere le curve presso gli aperti progressivi
|
|
ICRVCOMPOPOVECTOR vCrvOrderedPaths ; vCrvOrderedPaths.resize( vCrvPaths.size()) ;
|
|
ISURFFRPOVECTOR vSfrRemovedOrdered ; vSfrRemovedOrdered.resize( vCrvPaths.size()) ;
|
|
for ( int i = 0 ; i < int( vInds.size()) ; ++ i) {
|
|
// ordino la superficie rimossa dal percorso attuale
|
|
vSfrRemovedOrdered[i].Set( vSfrRemoved[vInds[i]]) ;
|
|
// ordino il percorso
|
|
vCrvOrderedPaths[i].Set( vCrvPaths[vInds[i]]) ;
|
|
}
|
|
|
|
// controllo che i percorsi effettivamente rimuovano del materiale
|
|
/*
|
|
NB. Nel caso di lati aperti, può capitare che si formino delle piccole curve ( estese poi
|
|
per il LeadIn/LeadOut ) che siano interne ad eventuali estensioni per i lati aperti ma che
|
|
di fatto non svuotano materiale effettivo ( questo accade, perchè gli Offset non sono come
|
|
per il caso degli Spiral fatti a partire dai bordi, ma sono calcolati tagliando gli offset
|
|
dei lati chiusi su una superficie estesa di 1.05 * ( dRad + GetOffsR() )
|
|
*/
|
|
vCrvPaths.clear() ;
|
|
for ( int i = 0 ; i < int( vCrvOrderedPaths.size()) ; ++ i) {
|
|
// se percorso non valido, passo al successivo
|
|
if ( IsNull( vCrvOrderedPaths[i]) || ! vCrvOrderedPaths[i]->IsValid())
|
|
continue ;
|
|
// se con il percorso i-esimo ho già rimosso tutta la superficie, allora ho finito
|
|
if ( IsNull( vSfrRemaining[i]) || ! vSfrRemaining[i]->IsValid())
|
|
break ;
|
|
// se superficie rimossa dal percorso attuale non valida, errore
|
|
if ( IsNull( vSfrRemovedOrdered[i]) || ! vSfrRemovedOrdered[i]->IsValid())
|
|
return false ;
|
|
// controllo se la superficie rimossa ( + 500 * EPS_SMALL ) interseca la regione da svuotare
|
|
if ( ! vSfrRemovedOrdered[i]->Intersect( *vSfrRemaining[i]) || IsNull( vSfrRemovedOrdered[i]))
|
|
return false ;
|
|
|
|
if ( vSfrRemovedOrdered[i]->IsValid() && vSfrRemovedOrdered[i]->GetChunkCount() > 0) {
|
|
// il percorso rimuove materiale, aggiungo LeadIn e LeadOut
|
|
ICRVCOMPOPOVECTOR vCurrPath ; vCurrPath.resize( 1) ;
|
|
vCurrPath[0].Set( vCrvOrderedPaths[i]) ;
|
|
AddLeadInToCurveConformalPaths( vSfrRemaining[i], pSfrChunk, PockParams, vCurrPath) ;
|
|
vCrvPaths.emplace_back( Release( vCurrPath[0])) ;
|
|
}
|
|
}
|
|
|
|
// aggiungo leadIn e LeadOut per i percorsi rimasti
|
|
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) {
|
|
ICRVCOMPOPOVECTOR vCurrPath ; vCurrPath.resize( 1) ;
|
|
vCurrPath[0].Set( vCrvPaths[i]) ;
|
|
AddLeadInToCurveConformalPaths( vSfrRemaining[i], pSfrChunk, PockParams, vCurrPath) ;
|
|
vCrvPaths[i].Set( vCurrPath[0]) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ChainConformalOffsWithLinks( ICRVCOMPOPOVECTOR& vCrvOffs, ICURVEPOVECTOR& vCrvLink,
|
|
ICRVCOMPOPOVECTOR& vCrvPaths)
|
|
{
|
|
// se non ho Offset non devo concatenare nulla
|
|
if ( vCrvOffs.empty())
|
|
return true ;
|
|
// le curve di Offset devono essere definite e valide
|
|
for ( int i = 0 ; i < int( vCrvOffs.size()) ; ++ i) {
|
|
if ( vCrvOffs[i] == nullptr || ! vCrvOffs[i]->IsValid())
|
|
return false ;
|
|
}
|
|
vCrvPaths.clear() ;
|
|
|
|
// percorso corrente
|
|
PtrOwner<ICurveComposite> pCrvPath( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvPath))
|
|
return false ;
|
|
|
|
for ( int i = 0 ; i < int( vCrvOffs.size()) - 1 ; ++ i) {
|
|
// aggiungo l'Offset corrente
|
|
if ( ! pCrvPath->AddCurve( Release( vCrvOffs[i])))
|
|
return false ;
|
|
|
|
// se il Link per l'Offset successivo è valido, lo aggiungo
|
|
if ( ! IsNull( vCrvLink[i+1]) && vCrvLink[i+1]->IsValid()) {
|
|
if ( ! pCrvPath->AddCurve( Release( vCrvLink[i+1]))) {
|
|
vCrvPaths.emplace_back( Release( pCrvPath)) ;
|
|
pCrvPath.Set( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvPath))
|
|
return false ;
|
|
}
|
|
}
|
|
// se Link non valido, ho terminato il percorso corrente
|
|
else {
|
|
vCrvPaths.emplace_back( Release( pCrvPath)) ;
|
|
pCrvPath.Set( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvPath))
|
|
return false ;
|
|
}
|
|
}
|
|
if ( ! pCrvPath->AddCurve( Release( vCrvOffs.back())))
|
|
return false ;
|
|
vCrvPaths.emplace_back( Release( pCrvPath)) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ExtendGuideByIteration( ICurveComposite* pCompoTempGuide, const ICurveComposite* pCompoPerimeter,
|
|
bool bInverted)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCompoTempGuide == nullptr || ! pCompoTempGuide->IsValid() ||
|
|
pCompoPerimeter == nullptr || ! pCompoPerimeter->IsValid())
|
|
return false ;
|
|
|
|
const double EXTENSION_VAL = 1000. ;
|
|
|
|
// ricavo il punto finale della guida
|
|
Point3d ptEndGuide ; pCompoTempGuide->GetEndPoint( ptEndGuide) ;
|
|
// direzione finale della guida
|
|
Vector3d vtEndGuide ; pCompoTempGuide->GetEndDir( vtEndGuide) ;
|
|
// creo un segmento diretto come il chiuso
|
|
PtrOwner<ICurveLine> pSeg( CreateCurveLine()) ;
|
|
if ( IsNull( pSeg) ||
|
|
! pSeg->Set( ptEndGuide, ptEndGuide + EXTENSION_VAL * vtEndGuide))
|
|
return false ;
|
|
// calcolo l'intersezione tra questo segmento e la curva di perimetro aperta
|
|
IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ;
|
|
// se l'unica intersezione è il punto iniziale del segmento, allora non estendo la guida
|
|
// ( vale anche nel caso di intersezione con Overlap)
|
|
if ( ICC.GetIntersCount() == 1) {
|
|
IntCrvCrvInfo aInfo ;
|
|
if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) {
|
|
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide))
|
|
return true ;
|
|
}
|
|
}
|
|
// passo alla PolyLine del perimetro
|
|
PolyLine PL ;
|
|
pCompoPerimeter->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, PL) ;
|
|
// cerco il punto più esterno rispetto alla direzione del chiuso
|
|
Point3d ptPoly ;
|
|
PL.GetFirstPoint( ptPoly) ;
|
|
while ( PL.GetNextPoint( ptPoly)) {
|
|
if ( ! pSeg->Set( ptEndGuide, ptEndGuide + ( ptPoly - ptEndGuide) * EXTENSION_VAL))
|
|
return false ;
|
|
// calcolo l'intersezione tra questo segmento e la curva di perimetro aperta
|
|
IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ;
|
|
// se una intersezione, allora di Overlap con punto iniziale e finale definito
|
|
if ( ICC.GetIntersCount() == 1) {
|
|
IntCrvCrvInfo aInfo ;
|
|
if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) {
|
|
if ( aInfo.bOverlap) {
|
|
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide) &&
|
|
AreSamePointApprox( aInfo.IciB[1].ptI, ptPoly))
|
|
return ( pCompoTempGuide->AddLine( ptPoly)) ;
|
|
}
|
|
else {
|
|
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide))
|
|
return ( pCompoTempGuide->AddLine( ptPoly)) ;
|
|
}
|
|
}
|
|
}
|
|
// se più di una intersezione, allora il perimetro deve essere tutto Interno al segmento
|
|
// ( Out nel caso in cui ho le curve invertite)
|
|
IntCrvCrvInfo aInfo ;
|
|
bool bOk = true ;
|
|
for ( int i = 0 ; i < ICC.GetIntersCount() && bOk ; ++ i) {
|
|
bOk = ( ICC.GetIntCrvCrvInfo( i, aInfo)) ;
|
|
bOk = ( aInfo.bOverlap ||
|
|
( aInfo.IciB[0].nPrevTy != ( ! bInverted ? CRVC_OUT : CRVC_IN) &&
|
|
aInfo.IciB[0].nNextTy != ( ! bInverted ? CRVC_OUT : CRVC_IN))) ;
|
|
}
|
|
if ( bOk)
|
|
return ( pCompoTempGuide->AddLine( ptPoly)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcConformalGuide( const ICurveComposite* pCrvCloseEdge, const ICurveComposite* pCrvOpenEdge,
|
|
const PocketParams& PockParams, ICurveComposite* pCrvGuide)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCloseEdge == nullptr || ! pCrvCloseEdge->IsValid() ||
|
|
pCrvOpenEdge == nullptr || ! pCrvOpenEdge->IsValid())
|
|
return false ;
|
|
pCrvGuide->Clear() ;
|
|
|
|
// creo una copia temporanea delle curve
|
|
PtrOwner<ICurveComposite> pCrvCL( CloneCurveComposite( pCrvCloseEdge)) ;
|
|
PtrOwner<ICurveComposite> pCrvOP( CloneCurveComposite( pCrvOpenEdge)) ;
|
|
if ( IsNull( pCrvCL) || IsNull( pCrvOP) || ! pCrvCL->IsValid() || ! pCrvOP->IsValid())
|
|
return false ;
|
|
|
|
// estendo la curva nel suo tratto finale ed iniziale
|
|
for ( int i = 0 ; i < 2 ; ++ i) {
|
|
if ( ! ExtendGuideByIteration( pCrvCL, pCrvOP, ( i == 1)))
|
|
return false ;
|
|
if ( ! pCrvCL->IsValid())
|
|
return true ;
|
|
pCrvCL->Invert() ;
|
|
pCrvOP->Invert() ;
|
|
// se la curva diventa chiusa, allora ho la guida
|
|
if ( pCrvCL->IsClosed()) {
|
|
pCrvGuide->CopyFrom( pCrvCL) ;
|
|
if ( i == 1)
|
|
pCrvGuide->Invert() ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// tratto iniziale
|
|
Vector3d vtStart ; pCrvCL->GetStartDir( vtStart) ;
|
|
Point3d ptStart ; pCrvCL->GetStartPoint( ptStart) ;
|
|
PtrOwner<ICurveLine> pLineStart( CreateCurveLine()) ;
|
|
if ( IsNull( pLineStart) || ! pLineStart->Set( ptStart - 1000. * vtStart, ptStart))
|
|
return false ;
|
|
// tratto finale ( potrebbe intersecare il tratto iniziale)
|
|
Vector3d vtEnd ; pCrvCL->GetEndDir( vtEnd) ;
|
|
Point3d ptEnd ; pCrvCL->GetEndPoint( ptEnd) ;
|
|
PtrOwner<ICurveLine> pLineEnd( CreateCurveLine()) ;
|
|
if ( IsNull( pLineEnd) || ! pLineEnd->Set( ptEnd, ptEnd + 1000. * vtEnd))
|
|
return false ;
|
|
|
|
// controllo eventuale intersezione
|
|
IntersCurveCurve ICC( *pLineStart, *pLineEnd) ;
|
|
if ( ICC.GetIntersCount() > 0) {
|
|
// se esiste allora spezzo sia il tratto iniziale che finale nel punto di intersezione
|
|
IntCrvCrvInfo aInfo ;
|
|
if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) {
|
|
pLineStart->TrimStartAtParam( aInfo.IciA[0].dU) ;
|
|
pLineEnd->TrimEndAtParam( aInfo.IciB[0].dU) ;
|
|
}
|
|
}
|
|
|
|
// le due estensioni vanno tagliate presso i bordi dei lati aperti estesi; rischierei di non
|
|
// riuscire a passare presso dei lati aperti vicini ai tratti lineari di estensione
|
|
IntersCurveCurve ICCLS( *pLineStart, *pCrvOP) ;
|
|
if ( ICCLS.GetIntersCount() > 0) {
|
|
IntCrvCrvInfo aInfo ;
|
|
for ( int i = 0 ; i < ICCLS.GetCrossIntersCount() ; ++ i) {
|
|
if ( ICCLS.GetIntCrvCrvInfo( i, aInfo)) {
|
|
if ( ! AreSamePointApprox( aInfo.IciA[ aInfo.bOverlap ? 1 : 0].ptI, ptStart))
|
|
pLineStart->ModifyStart( aInfo.IciA[aInfo.bOverlap ? 1 : 0].ptI) ;
|
|
}
|
|
}
|
|
}
|
|
IntersCurveCurve ICCLE( *pLineEnd, *pCrvOP) ;
|
|
if ( ICCLE.GetIntersCount() > 0) {
|
|
IntCrvCrvInfo aInfo ;
|
|
for ( int i = 0 ; i < ICCLE.GetCrossIntersCount() ; ++ i) {
|
|
if ( ICCLE.GetIntCrvCrvInfo( 0, aInfo)) {
|
|
if ( ! AreSamePointApprox( aInfo.IciA[0].ptI, ptEnd))
|
|
pLineEnd->ModifyEnd( aInfo.IciA[0].ptI) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// aggiungo i tratti lineari di estensione della curva
|
|
pCrvCL->AddCurve( Release( pLineStart), false) ;
|
|
pCrvCL->AddCurve( Release( pLineEnd), true) ;
|
|
|
|
// recupero la curva guida
|
|
pCrvGuide->CopyFrom( pCrvCL) ;
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CombineClosedEdgesForConformalOffsets( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParam,
|
|
bool& bOk, ICRVCOMPOPOVECTOR& vpCrvs)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
vpCrvs.clear() ;
|
|
bOk = false ;
|
|
|
|
// se la superficie non ha isole, allora non calcolo nulla
|
|
if ( pSfrChunk->GetLoopCount( 0) == 1)
|
|
return true ;
|
|
|
|
// recupero il bordo esterno e l'eventuale guida associata
|
|
ICRVCOMPOPOVECTOR vpCrvHomo ;
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() ||
|
|
! AdjustCloseEsgesForConformalGuide( pCrvExtLoop, PockParam) ||
|
|
! IsCompoMadeBy2DifferentHomogeneousParts( pCrvExtLoop, PockParam, bOk, vpCrvHomo))
|
|
return false ;
|
|
if ( ! bOk)
|
|
return true ;
|
|
|
|
// recupero la guida ideale
|
|
PtrOwner<ICurveComposite> pCrvGuide( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvGuide) ||
|
|
! CalcConformalGuide( vpCrvHomo[0], vpCrvHomo[1], PockParam, pCrvGuide))
|
|
return false ;
|
|
if ( ! pCrvGuide->IsValid())
|
|
return true ;
|
|
// la guida definisce il tratto chiuso per gli Offsets
|
|
vpCrvs.emplace_back( Release( pCrvGuide)) ;
|
|
// recupero il bordo esterno della superficie originale
|
|
pCrvExtLoop.Set( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
// considero questo bordo come tutto aperto
|
|
for ( int i = 0 ; i < pCrvExtLoop->GetCurveCount() ; ++ i)
|
|
pCrvExtLoop->SetCurveTempProp( i, 0, TEMP_PROP_OPEN_EDGE) ;
|
|
// recupero solo le isole chiuse (di sicuro esistono isole)
|
|
for ( int nL = 1 ; nL < pSfrChunk->GetLoopCount( 0) ; ++ nL) {
|
|
// recupero la curva dell'isola
|
|
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, nL))) ;
|
|
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
|
|
return false ;
|
|
// se curva tutta chiusa, allora la inserisco come isola
|
|
bool bAllOpen, bAllClose ;
|
|
if ( IsCurveCompoHomogeneous( pCrvIsl, bAllClose, bAllOpen) && bAllClose)
|
|
vpCrvs.emplace_back( Release( pCrvIsl)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetConformalOffsets( const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrClass, const PocketParams& PockParam,
|
|
VICRVCOMPOPOVECTOR& vCrvOffs)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrClass == nullptr || ! pSfrClass->IsValid() ||
|
|
pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
vCrvOffs.clear() ;
|
|
|
|
/* NB
|
|
vCrvOffs è un vettore di vettori di curve composite. Per ogni Offset progressivo i-esimo
|
|
vengono salvate tutte le parti interne j-esime alla pSfrClass.
|
|
( la posizione del vettore ICRVCOMPOPOVECTOR int VICRVCOMPOPOVECTOR è il numero di iterazione
|
|
progressiva di Offset)
|
|
*/
|
|
|
|
/* controllo se il chunk è svuotabile con lavorazione conformal
|
|
Sono previsti due casi di lavorazioni conformal :
|
|
1) Non esistono Isole e il bordo esterno è formato da due tratti disomogenei
|
|
2) Esistono solo Isole chiuse e il bordo esterno è tutto aperto
|
|
*/
|
|
bool bOk = false ;
|
|
ICRVCOMPOPOVECTOR vCrvCloseEdges ; // tratti chiusi da cui calcolare gli Offset
|
|
|
|
// 1) Non esistono Isole e il bordo esterno è formato da due tratti disomogenei
|
|
if ( pSfrChunk->GetLoopCount( 0) == 1) {
|
|
// recupero la curva di bordo
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() ||
|
|
! AdjustCloseEsgesForConformalGuide( pCrvExtLoop, PockParam) ||
|
|
! IsCompoMadeBy2DifferentHomogeneousParts( pCrvExtLoop, PockParam, bOk, vpCrvs))
|
|
return false ;
|
|
if ( ! bOk)
|
|
return true ;
|
|
// recupero la guida ideale
|
|
PtrOwner<ICurveComposite> pCrvGuide( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvGuide) ||
|
|
! CalcConformalGuide( vpCrvs[0], vpCrvs[1], PockParam, pCrvGuide))
|
|
return false ;
|
|
if ( ! pCrvGuide->IsValid())
|
|
return true ;
|
|
// la guida definisce il tratto chiuso per gli Offsets
|
|
vCrvCloseEdges.emplace_back( Release( pCrvGuide)) ;
|
|
}
|
|
// 2) Esistono solo Isole chiuse e il bordo esterno è tutto aperto
|
|
else {
|
|
if ( ! IsSfrChunkMadeOnlyByClosedIslands( pSfrChunk, PockParam, bOk, vCrvCloseEdges))
|
|
return false ;
|
|
if ( ! bOk) {
|
|
// 3) provo a combinare punto 1) e 2)
|
|
if ( ! CombineClosedEdgesForConformalOffsets( pSfrChunk, PockParam, bOk, vCrvCloseEdges))
|
|
return false ;
|
|
if ( ! bOk)
|
|
return true ; // sarà in SpiralIn/Out...
|
|
}
|
|
}
|
|
|
|
/* Calcolo degli Offsets */
|
|
// Oggetto Voronoi per curve chiuse
|
|
Voronoi myVRONI ;
|
|
for ( int i = 0 ; i < int( vCrvCloseEdges.size()) ; ++ i) {
|
|
vCrvCloseEdges[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
myVRONI.AddCurve( vCrvCloseEdges[i]) ;
|
|
}
|
|
|
|
int MAX_ITER = 1000 ;
|
|
int nIter = 0 ;
|
|
double dOffs = PockParam.dRad + PockParam.dRadialOffset ;
|
|
double dOffsPrec = 0. ;
|
|
bool bStopSmallRad = false ;
|
|
while ( nIter < MAX_ITER) {
|
|
// determino se le curve vanno invertite o meno
|
|
// essendo FatCurve, la parte interna alla regione di classificazione è invertita
|
|
bool bInvert = false ;
|
|
if ( PockParam.nType == POCKET_CONFORMAL_ZIGZAG)
|
|
bInvert = ! ( ( ! IsEven( nIter) && ! PockParam.bInvert) || ( IsEven( nIter) && PockParam.bInvert)) ;
|
|
else if ( PockParam.nType == POCKET_CONFORMAL_ONEWAY)
|
|
bInvert = ! ( PockParam.bInvert) ;
|
|
// recupero gli Offset dei tratti chiusi
|
|
ICURVEPOVECTOR vFatCrv ;
|
|
if ( ! myVRONI.CalcFatCurve( vFatCrv, dOffs, false, false))
|
|
return false ;
|
|
// vettore di Curve dentro alla regione di classificazione
|
|
ICRVCOMPOPOVECTOR vCrvOffsInside ;
|
|
// per tutte le curve ottenute, tengo solo per le parti interne alla regione di classificazione
|
|
bool bStop = true ;
|
|
for ( int j = 0 ; j < int( vFatCrv.size()) ; ++ j) {
|
|
CRVCVECTOR ccClass ;
|
|
if ( ! pSfrClass->GetCurveClassification( *vFatCrv[j], EPS_SMALL, ccClass))
|
|
return false ;
|
|
for ( int k = 0 ; k < int( ccClass.size()) ; ++ k) {
|
|
if ( ccClass[k].nClass == CRVC_IN) {
|
|
// almeno una curva in trovata
|
|
bStop = false ;
|
|
// recupero il tratto di curva
|
|
PtrOwner<ICurve> pMyCrv( vFatCrv[j]->CopyParamRange( ccClass[k].dParS, ccClass[k].dParE)) ;
|
|
if ( ! IsNull( pMyCrv) && pMyCrv->IsValid()) {
|
|
// inverto se necessario
|
|
if ( bInvert)
|
|
pMyCrv->Invert() ;
|
|
// salvo come seconda proprietà temporanea il lato interno
|
|
pMyCrv->SetTempProp( bInvert ? MDS_LEFT : MDS_RIGHT, 1) ;
|
|
// memorizzo la curva nel vettore
|
|
vCrvOffsInside.emplace_back( ConvertCurveToComposite( Release( pMyCrv))) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// concateno se necessario ( per tolleranza Offset)
|
|
if ( ! ChainCompoCurves( vCrvOffsInside))
|
|
return false ;
|
|
// controllo se serve un raggio più piccolo di svuotatura
|
|
bool bSmallRad = ( nIter == 0 ? dOffs < PockParam.dRad + EPS_ZERO : dOffs - dOffsPrec < PockParam.dRad + EPS_ZERO) ;
|
|
// se ho trovato delle curve interne
|
|
if ( ! bStop) {
|
|
dOffsPrec = dOffs ;
|
|
dOffs += PockParam.dSideStep ;
|
|
// inserisco le curva ricavate all'iterazione nIter nel vettore
|
|
vCrvOffs.resize( ++ nIter) ; // incremento nIter
|
|
for ( int i = 0 ; i < int( vCrvOffsInside.size()) ; ++ i) {
|
|
vCrvOffsInside[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
vCrvOffs.back().emplace_back( Release( vCrvOffsInside[i])) ;
|
|
}
|
|
// se devo terminare, interrompo il loop
|
|
if ( bStopSmallRad)
|
|
break ;
|
|
}
|
|
// se non ho ricavato curve ma serve un offset del raggio utensile
|
|
else if ( ! bSmallRad) {
|
|
dOffs = dOffsPrec + ( nIter == 0 ? PockParam.dRad + PockParam.dRadialOffset : PockParam.dRad) ;
|
|
bStopSmallRad = true ;
|
|
}
|
|
else
|
|
break ;
|
|
}
|
|
|
|
// smusso le curve di Offset ( ad eccezione di quelle generate alla prima iterazione)
|
|
double dSmoothPar = PockParam.dRad / 8. ;
|
|
for ( int i = 1 ; i < int( vCrvOffs.size()) ; ++ i) {
|
|
for ( int j = 0 ; j < int( vCrvOffs[i].size()) ; ++ j)
|
|
ModifyCurveToSmoothed( vCrvOffs[i][j], PockParam, dSmoothPar, dSmoothPar, false) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetConformalSfrClassChunk( const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrClass,
|
|
INTVECTOR& vnChunk)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() ||
|
|
pSfrClass == nullptr || ! pSfrClass->IsValid())
|
|
return false ;
|
|
vnChunk.clear() ;
|
|
|
|
// se la superficie di classificazione ha un solo chunk, esco
|
|
if ( pSfrClass->GetChunkCount() == 1) {
|
|
vnChunk.emplace_back( 0) ;
|
|
return true ;
|
|
}
|
|
|
|
// controllo quali Chunk di pSfrClass intersecano la pSfrChunk
|
|
for ( int nC = 0 ; nC < pSfrClass->GetChunkCount() ; ++ nC) {
|
|
// recupero il Chunk nC-esimo
|
|
PtrOwner<ISurfFlatRegion> pSfrClassChunk( pSfrClass->CloneChunk( nC)) ;
|
|
if ( IsNull( pSfrClassChunk) || ! pSfrClass->IsValid())
|
|
return false ;
|
|
// controllo se l'intersezione non è vuota
|
|
pSfrClassChunk->Intersect( *pSfrChunk) ;
|
|
if ( ! IsNull( pSfrClassChunk) && pSfrClassChunk->IsValid())
|
|
vnChunk.push_back( nC) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetSfrClassConformal( const ISurfFlatRegion* pSfrPock, const PocketParams& PockParams, ISurfFlatRegion* pSfrClass)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrPock == nullptr || ! pSfrPock->IsValid())
|
|
return false ;
|
|
// dalla pSfrPock rimuovo i Chunk tutti chiusi o tutti aperti
|
|
pSfrClass->CopyFrom( pSfrPock) ;
|
|
// per ora tengo la superficie di classificazione esattamente identica alla superficie di svuotatura
|
|
#if 0
|
|
// Offset per definire la regione di taglio per le curve di Conformal
|
|
if ( ! pSfrClass->Offset( - ( 3. * PockParams.dRad / 4.) - PockParams.dRadialOffset, ICurve::OFF_FILLET))
|
|
return false ;
|
|
// rimuovo tutti i Chunk con massimo Offset più piccolo del raggio utensile
|
|
for ( int nC = 0 ; nC < pSfrClass->GetChunkCount() ; ++ nC) {
|
|
double dMaxOffs = EPS_SMALL ;
|
|
if ( ! pSfrClass->GetChunkMaxOffset( nC, dMaxOffs))
|
|
return false ;
|
|
if ( dMaxOffs < PockParams.dRad + PockParams.dRadialOffset + 500 * EPS_SMALL) {
|
|
pSfrClass->EraseChunk( nC) ;
|
|
-- nC ;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddConformal( ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig,
|
|
PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrPock == nullptr || ! pSfrPock->IsValid() ||
|
|
pSfrOrig == nullptr || ! pSfrOrig->IsValid())
|
|
return true ;
|
|
|
|
// se superifice tutta aperta, lavoro in SPIRAL_IN
|
|
if ( PockParams.bAllOpen)
|
|
return ( AddSpiralIn( pSfrPock, pSfrOrig, PockParams, vCrvCompoRes)) ;
|
|
// se superficie tutta chiusa, lavoro in SPIRAL_OUT
|
|
if ( PockParams.bAllClosed)
|
|
return ( AddSpiralOut( pSfrPock, pSfrOrig, PockParams, vCrvCompoRes)) ;
|
|
|
|
// Definisco la superficie di classificazione ( su essa vengono tagliate le curve progressive di Offset)
|
|
PtrOwner<ISurfFlatRegion> pSfrClass( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrClass) || ! GetSfrClassConformal( pSfrPock, PockParams, pSfrClass))
|
|
return false ;
|
|
if ( ! pSfrClass->IsValid() || pSfrClass->GetChunkCount() == 0)
|
|
return true ; // non posso svuotare nulla
|
|
|
|
// scorro i chunk della superficie da lavorare
|
|
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
|
|
// controllo se il Chunk ha tutte proprietà omogenee tra loro
|
|
bool bClose, bOpen ;
|
|
if ( ! IsChunkAllHomogeneous( pSfrPock, nC, bClose, bOpen))
|
|
return false ;
|
|
// recupero il Chunk come regione piana
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfrPock->CloneChunk( nC)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
// se Chunk tutto aperto, lo lavoro in SPIRAL_IN
|
|
if ( bOpen) {
|
|
if ( ! AddSpiralIn( pSfrChunk, pSfrOrig, PockParams, vCrvCompoRes))
|
|
return false ;
|
|
}
|
|
// se Chunk tutto chiuso, lo lavoro in SPIRAL_OUT
|
|
else if ( bClose) {
|
|
if ( ! AddSpiralOut( pSfrChunk, pSfrOrig, PockParams, vCrvCompoRes))
|
|
return false ;
|
|
}
|
|
// se Chunk non omogeneo
|
|
else {
|
|
// ricavo il Chunk della superficie di classificazione corrente
|
|
INTVECTOR vnChunk ;
|
|
if ( ! GetConformalSfrClassChunk( pSfrChunk, pSfrClass, vnChunk))
|
|
return false ;
|
|
// per tutti i Chunk classificati
|
|
for ( int i = 0 ; i < int( vnChunk.size()) ; ++ i) {
|
|
// ricavo il chunk di classificazione i-esimo
|
|
PtrOwner<ISurfFlatRegion> pSfrClassChunk( pSfrClass->CloneChunk( vnChunk[i])) ;
|
|
if ( IsNull( pSfrClassChunk) || ! pSfrClassChunk->IsValid())
|
|
return false ;
|
|
// ricavo gli Offset ( se possibili) dei tratti chiusi del Chunk
|
|
VICRVCOMPOPOVECTOR vvCrvOffs ;
|
|
if ( ! GetConformalOffsets( pSfrChunk, pSfrClassChunk, PockParams, vvCrvOffs))
|
|
return false ;
|
|
// se non ottengo Curve di Offset, lavoro in SpiralIn ( il Chunk ha dei lati aperti)
|
|
if ( vvCrvOffs.empty()) {
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk_cl( CloneSurfFlatRegion( pSfrChunk)) ;
|
|
if ( IsNull( pSfrChunk_cl) || ! pSfrChunk_cl->IsValid() ||
|
|
! AddSpiralIn( pSfrChunk_cl, pSfrOrig, PockParams, vCrvCompoRes))
|
|
return false ;
|
|
}
|
|
else {
|
|
// definisco vettore degli Offset e dei Link
|
|
ICRVCOMPOPOVECTOR vCrvOffs ;
|
|
ICURVEPOVECTOR vCrvLink ;
|
|
// ordino le curve di Offset e raccordo
|
|
if ( ! CalcConformalOffsAndLinks( vvCrvOffs, pSfrChunk, pSfrClassChunk, PockParams, vCrvOffs, vCrvLink)) {
|
|
// se calcolare i raccordi risulta troppo ambiguo o complesso, ritorno a SpiralIn
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk_cl( CloneSurfFlatRegion( pSfrChunk)) ;
|
|
if ( IsNull( pSfrChunk_cl) || ! pSfrChunk_cl->IsValid() ||
|
|
! AddSpiralIn( pSfrChunk_cl, pSfrOrig, PockParams, vCrvCompoRes))
|
|
return false ;
|
|
continue ;
|
|
}
|
|
// flag per controllo
|
|
bool bOk = true ;
|
|
// estendo i percorsi per eventuali regioni non svuotate e calcolo le Feed
|
|
bOk = bOk && ExtendConformalOffsAndSetFeed( pSfrPock, PockParams, vCrvOffs, vCrvLink) ;
|
|
// concateno Offset e Links
|
|
ICRVCOMPOPOVECTOR vCrvPaths ;
|
|
bOk = bOk && ChainConformalOffsWithLinks( vCrvOffs, vCrvLink, vCrvPaths) ;
|
|
// estendo per lati aperti ed ordino i percorsi trovati
|
|
bOk = bOk && OrderAndExtendConformalPaths( vCrvPaths, pSfrChunk, pSfrOrig, PockParams) ;
|
|
if ( ! bOk) {
|
|
// se qualche passaggio restituisce errore, provo in SpiralIn
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk_cl( CloneSurfFlatRegion( pSfrChunk)) ;
|
|
if ( IsNull( pSfrChunk_cl) || ! pSfrChunk_cl->IsValid() ||
|
|
! AddSpiralIn( pSfrChunk_cl, pSfrOrig, PockParams, vCrvCompoRes))
|
|
return false ;
|
|
}
|
|
else {
|
|
// aggiungo i percorsi ricavati
|
|
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i)
|
|
vCrvCompoRes.emplace_back( Release( vCrvPaths[i])) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double dStep, double dAngle,
|
|
int nType, bool bSmooth, bool bInvert, bool bAvoidOpt, bool bAllowZigZagOneWayBorders,
|
|
bool bCalcFeed, const Point3d& ptEndPrec, const ISurfFlatRegion* pSfrLimit, bool bAllOffs, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid() ||
|
|
dStep < 10 * EPS_SMALL ||
|
|
( nType != POCKET_ZIGZAG && nType != POCKET_ONEWAY && nType != POCKET_SPIRALIN &&
|
|
nType != POCKET_SPIRALOUT && nType != POCKET_CONFORMAL_ZIGZAG && nType != POCKET_CONFORMAL_ONEWAY))
|
|
return false ;
|
|
|
|
// pulizia vettore delle curve elementari
|
|
vCrvCompoRes.clear() ;
|
|
|
|
// assegno dati di modulo
|
|
PocketParams myParams ;
|
|
myParams.nType = nType ;
|
|
myParams.dRad = dRad ;
|
|
myParams.dRadialOffset = dRadOffs ;
|
|
myParams.bSmooth = bSmooth ;
|
|
myParams.dAngle = dAngle ;
|
|
myParams.dSideStep = dStep ;
|
|
myParams.bInvert = bInvert ;
|
|
myParams.bAvoidOpt = bAvoidOpt ;
|
|
myParams.bCalcFeed = bCalcFeed ;
|
|
myParams.bAllowZigZagOneWayBorders = bAllowZigZagOneWayBorders ;
|
|
myParams.bOptOffsets = ( ! bAllOffs) ;
|
|
if ( ptEndPrec.IsValid())
|
|
myParams.ptStart = ptEndPrec ;
|
|
if ( pSfrLimit != nullptr && pSfrLimit->IsValid())
|
|
myParams.SfrLimit.CopyFrom( pSfrLimit) ;
|
|
Point3d ptCenter ; pSfr->GetCentroid( ptCenter) ;
|
|
if ( ! myParams.frLocXY.Set( ptCenter, pSfr->GetNormVersor()) ||
|
|
! myParams.frLocXY.Rotate( ptCenter, pSfr->GetNormVersor() , myParams.dAngle))
|
|
return false ;
|
|
|
|
// porto la superficie da svuotare nel sistema di riferimento lungo Z_AX
|
|
PtrOwner<ISurfFlatRegion> pSfrAdj( pSfr->Clone()) ;
|
|
if ( IsNull( pSfrAdj) || ! pSfrAdj->ToLoc( myParams.frLocXY))
|
|
return false ;
|
|
// porto la superficie limite ( se valida) in tale sistema
|
|
if ( myParams.SfrLimit.IsValid())
|
|
myParams.SfrLimit.ToLoc( myParams.frLocXY) ;
|
|
// porto il punto iniziale di riferimento ( se valido) in tale sistema
|
|
if ( myParams.ptStart.IsValid())
|
|
myParams.ptStart.ToLoc( myParams.frLocXY) ;
|
|
// controllo se la superficie è tutta chiusa o tutta aperta
|
|
if ( ! IsSfrAllHomogeneous( pSfr, myParams.bAllClosed, myParams.bAllOpen))
|
|
return false ;
|
|
|
|
// ------------ casi ottimizzati ------------------------------------------
|
|
ICRVCOMPOPOVECTOR vCrvOptCurves ;
|
|
if ( ! GetPocketingOptimizedCurves( pSfrAdj, myParams, vCrvOptCurves))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vCrvOptCurves.size()) ; ++ i) {
|
|
vCrvOptCurves[i]->ToGlob( myParams.frLocXY) ;
|
|
vCrvCompoRes.emplace_back( Release( vCrvOptCurves[i])) ;
|
|
}
|
|
// se ho svuotato tutta la superficie, allora ho finito
|
|
if ( IsNull( pSfrAdj) || ! pSfrAdj->IsValid() || pSfrAdj->GetChunkCount() == 0)
|
|
return true ;
|
|
|
|
// ------------ recupero le singole curve di svuotatura -------------------
|
|
ICRVCOMPOPOVECTOR vCrvSingleCurves ;
|
|
if ( ! GetSinglePocketingCurves( pSfrAdj, myParams, vCrvSingleCurves))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vCrvSingleCurves.size()) ; ++ i) {
|
|
vCrvSingleCurves[i]->ToGlob( myParams.frLocXY) ;
|
|
vCrvCompoRes.emplace_back( Release( vCrvSingleCurves[i])) ;
|
|
}
|
|
|
|
// se ho svuotato tutta la superficie, allora ho finito
|
|
if ( IsNull( pSfrAdj) || ! pSfrAdj->IsValid() || pSfrAdj->GetChunkCount() == 0)
|
|
return true ;
|
|
|
|
// ------------ estensione lati aperti ------------------------------------
|
|
if ( ! ModifySurfByOpenEdges( pSfrAdj, myParams))
|
|
return false ;
|
|
|
|
// porto una copia della superficie originale nel frame creato
|
|
PtrOwner<ISurfFlatRegion> pSfr_Loc( CloneSurfFlatRegion( pSfr)) ;
|
|
if ( IsNull( pSfr_Loc) || ! pSfr_Loc->IsValid() || ! pSfr_Loc->ToLoc( myParams.frLocXY))
|
|
return false ;
|
|
|
|
// ------------ calcolo delle curve elementari della superficie -------------------
|
|
ICRVCOMPOPOVECTOR vCrvCompoPock ;
|
|
switch ( nType) {
|
|
case POCKET_ZIGZAG :
|
|
if ( ! AddZigZag( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
|
|
return false ;
|
|
break ;
|
|
case POCKET_ONEWAY :
|
|
if ( ! AddOneWay( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
|
|
return false ;
|
|
break ;
|
|
case POCKET_SPIRALIN :
|
|
if ( ! AddSpiralIn( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
|
|
return false ;
|
|
break ;
|
|
case POCKET_SPIRALOUT :
|
|
if ( ! AddSpiralOut( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
|
|
return false ;
|
|
break ;
|
|
case POCKET_CONFORMAL_ZIGZAG :
|
|
case POCKET_CONFORMAL_ONEWAY :
|
|
if ( ! AddConformal( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
|
|
return false ;
|
|
break ;
|
|
}
|
|
|
|
// riporto tutte le curve ottenute in globale
|
|
for ( int i = 0 ; i < int( vCrvCompoPock.size()) ; ++ i) {
|
|
vCrvCompoPock[i]->ToGlob( myParams.frLocXY) ;
|
|
vCrvCompoRes.emplace_back( Release( vCrvCompoPock[i])) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustZigZagPathTangentLinks( ICurveComposite* pCrvCompo)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// miglioro la curva
|
|
pCrvCompo->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
|
|
// scorro tutte le curve
|
|
int nCurrTmpProp = -1, nCurrTmpProp1 = -1 ;
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
|
|
if ( pCrvCompo->GetCurveTempProp( u, nCurrTmpProp, 1) && nCurrTmpProp == -1) {
|
|
// ricavo il link
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
pCrvLink->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ;
|
|
for ( int uu = u + 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) {
|
|
if ( pCrvCompo->GetCurveTempProp( uu, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
|
|
if ( ! pCrvLink->AddCurve( pCrvCompo->GetCurve( uu)->Clone()))
|
|
return false ;
|
|
}
|
|
else
|
|
break ;
|
|
}
|
|
if ( ! pCrvLink->IsValid())
|
|
return false ;
|
|
// se ho più di una curva nel link
|
|
if ( pCrvLink->GetCurveCount() > 1) {
|
|
// ricavo la curva precendente al Link ( se presente e se prima curva del Link è lineare)
|
|
if ( u != 0 && pCrvLink->GetFirstCurve()->GetType() == CRV_LINE) {
|
|
const ICurve* pCrvSegPrec = pCrvCompo->GetCurve( u - 1) ;
|
|
if ( pCrvSegPrec == nullptr || pCrvSegPrec->GetType() != CRV_LINE)
|
|
return false ;
|
|
// ricavo la sua direzione finale
|
|
Vector3d vtZigZagEndDir ; pCrvSegPrec->GetEndDir( vtZigZagEndDir) ;
|
|
// ricavo la direzione iniziale del link
|
|
Vector3d vtLinkStartDir ; pCrvLink->GetStartDir( vtLinkStartDir) ;
|
|
// se i vettori sono paralleli, considero la prima curva del link come parte del percorso a ZigZag
|
|
if ( AreSameVectorApprox( vtZigZagEndDir, vtLinkStartDir))
|
|
pCrvCompo->SetCurveTempProp( u, 0, 1) ;
|
|
}
|
|
// ricavo la curva successiva al Link ( se presente e se ultima curva del Link è lineare)
|
|
if ( u + pCrvLink->GetCurveCount() < pCrvCompo->GetCurveCount() && pCrvLink->GetLastCurve()->GetType() == CRV_LINE) {
|
|
const ICurve* pCrvSegSucc = pCrvCompo->GetCurve( u + pCrvLink->GetCurveCount()) ;
|
|
if ( pCrvSegSucc == nullptr || pCrvSegSucc->GetType() != CRV_LINE)
|
|
return false ;
|
|
// ricavo la sua direzione inziale
|
|
Vector3d vtZigZagStartDir ; pCrvSegSucc->GetStartDir( vtZigZagStartDir) ;
|
|
// ricavo la direzione finale del link
|
|
Vector3d vtLinkStartDir ; pCrvLink->GetEndDir( vtLinkStartDir) ;
|
|
// se i vettori sono paralleli, considero l'ultima curva del link come parte del percorso a ZigZag
|
|
if ( AreSameVectorApprox( vtZigZagStartDir, vtLinkStartDir))
|
|
pCrvCompo->SetCurveTempProp( u + pCrvLink->GetCurveCount() - 1, 0, 1) ;
|
|
}
|
|
}
|
|
// aggiorno
|
|
u += pCrvLink->GetCurveCount() ; // così la successiva è corretta nel ciclo
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustLinkSameY( ICurveComposite* pCrvLink, bool& bSplit)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvLink == nullptr || ! pCrvLink->IsValid())
|
|
return false ;
|
|
bSplit = false ;
|
|
|
|
// ricavo gli estremi del Link
|
|
Point3d ptS ; pCrvLink->GetStartPoint( ptS) ;
|
|
Point3d ptE ; pCrvLink->GetEndPoint( ptE) ;
|
|
|
|
// ricavo il versore uscente da ptS e diretto verso ptE
|
|
Vector3d vtDir = ptE - ptS ;
|
|
if ( vtDir.IsValid() && ! vtDir.IsSmall())
|
|
vtDir.Normalize() ;
|
|
else
|
|
return false ;
|
|
|
|
// creo un frame orientato come questo versore
|
|
Frame3d frLink ;
|
|
frLink.Set( ptS, Z_AX, vtDir) ;
|
|
if ( ! frLink.IsValid())
|
|
return false ;
|
|
|
|
// ricavo il box3d del Link in questo frame
|
|
BBox3d BBoxLink ;
|
|
pCrvLink->ToLoc( frLink) ;
|
|
if ( ! pCrvLink->GetLocalBBox( BBoxLink) || BBoxLink.IsEmpty())
|
|
return false ;
|
|
pCrvLink->ToGlob( frLink) ;
|
|
|
|
// controllo la dimensione del Box in Y
|
|
if ( abs( BBoxLink.GetDimY()) > 20 * EPS_SMALL)
|
|
bSplit = true ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustLinkDifferentY( const ICurve* pCrvSegPrec, const ICurve* pCrvSegSucc,
|
|
const ICurveComposite* pCrvLink, const PocketParams& PockParams, bool& bSplit)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvLink == nullptr || ! pCrvLink->IsValid())
|
|
return false ;
|
|
if ( pCrvSegPrec == nullptr && pCrvSegSucc == nullptr)
|
|
return true ;
|
|
bSplit = false ;
|
|
|
|
// creo un Trapezoide per regione di incidenza
|
|
Point3d ptH ;
|
|
PtrOwner<ISurfFlatRegion> pSfrTrap( CreateSurfFlatRegion()) ;
|
|
PtrOwner<ICurveComposite> pCrvTrapBorder( CreateCurveComposite()) ;
|
|
if ( IsNull( pSfrTrap) ||
|
|
IsNull( pCrvTrapBorder))
|
|
return false ;
|
|
|
|
// 0) primo tratto
|
|
if ( pCrvSegPrec != nullptr) {
|
|
// se presente lo aggiungo
|
|
if ( ! pCrvTrapBorder->AddCurve( pCrvSegPrec->Clone()))
|
|
return false ;
|
|
}
|
|
else {
|
|
// se non presente, inserisco il punto iniziale del Link
|
|
pCrvLink->GetStartPoint( ptH) ;
|
|
pCrvTrapBorder->AddPoint( ptH) ;
|
|
}
|
|
// 1) tratto lineare fino al punto finale del Link
|
|
if ( ! pCrvLink->GetEndPoint( ptH) ||
|
|
! pCrvTrapBorder->AddLine( ptH))
|
|
return false ;
|
|
// 2) terzo tratto
|
|
if ( pCrvSegSucc != nullptr) {
|
|
// se presente lo aggiungo
|
|
if ( ! pCrvTrapBorder->AddCurve( pCrvSegSucc->Clone()))
|
|
return false ;
|
|
}
|
|
// se non presente non aggiungo nulla, ho alla peggio il Link
|
|
// 4) ultimo tratto lineare per chiudere la curva
|
|
if ( ! pCrvTrapBorder->Close() || // ora creo la FlatRegion a trapezio
|
|
! pSfrTrap->AddExtLoop( Release( pCrvTrapBorder)) ||
|
|
! pSfrTrap->IsValid())
|
|
return false ;
|
|
|
|
// oriento
|
|
if ( AreOppositeVectorApprox( Z_AX, pSfrTrap->GetNormVersor()))
|
|
pSfrTrap->Invert() ;
|
|
|
|
// effettuo un piccolo Offset per una maggiore tolleranza ( se possibile)
|
|
PtrOwner<ISurfFlatRegion> pSfrTrap_c( pSfrTrap->Clone()) ;
|
|
if ( IsNull( pSfrTrap_c))
|
|
return false ;
|
|
if ( ! pSfrTrap->Offset( - PockParams.dRad / 5, ICurve::OFF_CHAMFER))
|
|
pSfrTrap.Set( pSfrTrap_c) ;
|
|
|
|
// se il Link interseca questa regione, allora va eliminato
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrTrap->GetCurveClassification( *pCrvLink, EPS_SMALL, ccClass))
|
|
bSplit = int( ccClass.size()) > 1 ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustLinkZigZagInfill( ICurveComposite* pCrvCompo, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vNewCrv)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
vNewCrv.clear() ;
|
|
|
|
// sistemo la curva per evitare che i tratti estremi dei link siano lineari ed in tangenza con percorsi ZigZag
|
|
if ( ! AdjustZigZagPathTangentLinks( pCrvCompo))
|
|
return false ;
|
|
|
|
// definisco un vettore di intervalli su cui spezzare la curva
|
|
struct CopyRange {
|
|
double dParS ;
|
|
double dParE ;
|
|
} ;
|
|
vector<CopyRange> vIntervals ;
|
|
|
|
// parametri
|
|
int nCurrTmpProp1 = 0 ;
|
|
int nCrvStartBreak = 0 ;
|
|
|
|
// NB. I tratti di ZigZag che seguono dei contorni ( i Links) hanno TmpProp1 impostata come -1
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
|
|
if ( pCrvCompo->GetCurveTempProp( u, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
|
|
// ricavo il link
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
pCrvLink->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ;
|
|
for ( int uu = u + 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) {
|
|
if ( pCrvCompo->GetCurveTempProp( uu, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
|
|
if ( ! pCrvLink->AddCurve( pCrvCompo->GetCurve( uu)->Clone()))
|
|
return false ;
|
|
}
|
|
else
|
|
break ;
|
|
}
|
|
if ( ! pCrvLink->IsValid())
|
|
return false ;
|
|
// ricavo il punto iniziale e finale del Link
|
|
Point3d ptS ; pCrvLink->GetStartPoint( ptS) ;
|
|
Point3d ptE ; pCrvLink->GetEndPoint( ptE) ;
|
|
// parametro per spezzare o meno la curva, togliendo il Link
|
|
bool bSplit = false ;
|
|
// se il Link collega due tratti alla stessa quota...
|
|
if ( abs( ptS.y - ptE.y) < 20 * EPS_SMALL) {
|
|
if ( ! AdjustLinkSameY( pCrvLink, bSplit))
|
|
return false ;
|
|
}
|
|
// se il Link collega due tratti a quote diverse
|
|
else {
|
|
if ( ! AdjustLinkDifferentY( pCrvCompo->GetCurve( u - 1), pCrvCompo->GetCurve( u + pCrvLink->GetCurveCount()),
|
|
pCrvLink, PockParams, bSplit))
|
|
return false ;
|
|
}
|
|
// aggiorno
|
|
u += pCrvLink->GetCurveCount() ; // così la successiva è corretta nel ciclo
|
|
if ( bSplit) {
|
|
// se dimensione sopra la tolleranza, allora spezzo la curva e rimuovo il link
|
|
CopyRange CR {
|
|
1. * nCrvStartBreak, // dParS
|
|
1. * ( u - pCrvLink->GetCurveCount()) // dParE
|
|
} ;
|
|
// se il primo tratto è un link non valido, non considero l'intervallo
|
|
if ( ! ( nCrvStartBreak == 0 && abs( CR.dParS - CR.dParE) < 20 * EPS_SMALL))
|
|
vIntervals.push_back( CR) ;
|
|
nCrvStartBreak = u ;
|
|
}
|
|
}
|
|
}
|
|
// se non ho mai trovato link non validi, lascio la curva invariata
|
|
if ( vIntervals.empty())
|
|
vNewCrv.emplace_back( pCrvCompo->Clone()) ;
|
|
else {
|
|
// per ogni intervallo...
|
|
for ( int i = 0 ; i < int( vIntervals.size()) ; ++ i)
|
|
vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( vIntervals[i].dParS, vIntervals[i].dParE))) ;
|
|
// ultimo intervallo ( se l'ultimo intervallo è un link non valido, non inserisco)
|
|
if ( abs( vIntervals.back().dParE - pCrvCompo->GetCurveCount()) > 20 * EPS_SMALL)
|
|
vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nCrvStartBreak, pCrvCompo->GetCurveCount()))) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CalcZigZagInfill( const ISurfFlatRegion* pSfr, double dStep, bool bSmooth, bool bRemoveOverlapLink, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid() || dStep < EPS_SMALL)
|
|
return false ;
|
|
vCrvCompoRes.clear() ;
|
|
|
|
PocketParams myParams ;
|
|
myParams.bSmooth = false ;
|
|
myParams.dRad = 0.5 * dStep ;
|
|
|
|
// vettore con i percorsi a ZigZag
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
|
|
// ciclo su tutti i chunk della regione
|
|
for ( int c = 0 ; c < pSfr->GetChunkCount() ; ++ c) {
|
|
// prendo il Chunk
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( c)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
// calcolo i percorsi a ZigZag
|
|
if ( ! CalcZigZag( pSfrChunk, myParams, vpCrvs, true))
|
|
return false ;
|
|
|
|
if ( bRemoveOverlapLink) {
|
|
ICRVCOMPOPOVECTOR vCrvFinal ;
|
|
for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u)
|
|
vCrvFinal.emplace_back( Release( vpCrvs[u])) ;
|
|
vpCrvs.clear() ;
|
|
// sistemo le curve ottenute, aggiustando i Links
|
|
for ( int u = 0 ; u < int( vCrvFinal.size()) ; ++ u) {
|
|
ICRVCOMPOPOVECTOR vNewCrv ;
|
|
if ( ! AdjustLinkZigZagInfill( vCrvFinal[u], myParams, vNewCrv))
|
|
return false ;
|
|
for ( int uu = 0 ; uu < int( vNewCrv.size()) ; ++ uu)
|
|
vpCrvs.emplace_back( Release( vNewCrv[uu])) ;
|
|
}
|
|
}
|
|
|
|
// inserisco i percorsi da ritornare
|
|
for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) {
|
|
if ( bSmooth) {
|
|
myParams.bSmooth = true ;
|
|
ModifyCurveToSmoothed( vpCrvs[u], myParams, myParams.dRad / 16, myParams.dRad / 16, false) ;
|
|
}
|
|
vCrvCompoRes.emplace_back( Release( vpCrvs[u])) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
} |