ba7379e752
- In CalcPocketing aggiunto flag per Conventional Milling.
10271 lines
431 KiB
C++
10271 lines
431 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2023-2023
|
|
//----------------------------------------------------------------------------
|
|
// File : EGkCalcPocketing.h Data : 16.11.23 Versione : 2.5j1
|
|
// Contenuto : Dichiarazione della funzione per calcolare le curve base di Pocketing
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 16.11.23 RE Creazione modulo.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "CurveLine.h"
|
|
#include "CurveArc.h"
|
|
#include "CurveComposite.h"
|
|
#include "BiArcs.h"
|
|
#include "SurfFlatRegion.h"
|
|
#include "GeoConst.h"
|
|
#include "Voronoi.h"
|
|
#include "/EgtDev/Include/EGkSfrCreate.h"
|
|
#include "/EgtDev/Include/EGkCurveAux.h"
|
|
#include "/EgtDev/Include/EGkCalcPocketing.h"
|
|
#include "/EgtDev/Include/EGkFilletChamfer.h"
|
|
#include "/EgtDev/Include/EGkDistPointCurve.h"
|
|
#include "/EgtDev/Include/EGkDistPointSurfFr.h"
|
|
#include "/EgtDev/Include/EGkCurveLocal.h"
|
|
#include "/EgtDev/Include/EGkMedialAxis.h"
|
|
#include "/EgtDev/Include/EGkLinePntTgCurve.h"
|
|
#include "/EgtDev/Include/EGkOffsetCurve.h"
|
|
#include "/EgtDev/Include/EGkIntervals.h"
|
|
#include "/EgtDev/Include/EGkChainCurves.h"
|
|
#include "/EgtDev/Include/EgtNumUtils.h"
|
|
#include <array>
|
|
|
|
using namespace std ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Calcolo delle curve elementari di Pocketing
|
|
//----------------------------------------------------------------------------
|
|
|
|
// variabili d'appoggio ( per non passare troppi parametri tra le funzioni secondarie)
|
|
struct PocketParams {
|
|
int nType = POCKET_SPIRALIN ; // tipo di lavorazione
|
|
int nLiType = LEAD_IN_NONE ; // tipo di ingresso (LeadIn)
|
|
int nLoType = LEAD_OUT_NONE ; // tipo di uscita (LeadOut)
|
|
double dRad = 0. ; // raggio utensile
|
|
double dRad_prec = -1. ; // raggio utensile della lavorazione precedente
|
|
double dSideStep = 0. ; // step
|
|
double dSideStep_prec = -1. ; // step della lavorazione precedente
|
|
double dRadialOffset = 0. ; // offset radiale
|
|
double dRadialOffset_prec = -1. ; // offset radiale precedente
|
|
double dMaxOpenEdgeRad = 0. ; // massimo raggio per estensione lati aperti
|
|
double dOpenMinSafe = 5. ; // estensione minima di sicurezza
|
|
double dMaxOptSize = 0. ; // dimensione per ottimizzazione
|
|
double dAngle = 0. ; // angolo per orientare le passate OneWay e ZigZag
|
|
double dSmooth = 5. ; // parametro di smusso per link (raccordo)
|
|
double dLiTang = INFINITO ; // valore tangente di LeadIn
|
|
double dLiElev = INFINITO ; // valore elevazione di LeadIn (usato anche per LeadOut)
|
|
double dLoTang = INFINITO ; // valore di LeadOut
|
|
bool bCalcUnclearedRegs = true ; // flag per calcolare o meno le regioni non svuotate
|
|
bool bOptOffsets = true ; // flag per evitare Offset non necessari
|
|
bool bAboveHead = true ; // flag per testa da sopra ( Z+)
|
|
bool bSmooth = false ; // curve smussate
|
|
bool bInvert = false ; // inversione dei percorsi
|
|
bool bAvoidOpt = false ; // flag per evitare casi ottimizzati
|
|
bool bConventionalMilling = true ; // flag per Conventional Milling Vs Climb Milling per svuotature a curva singola
|
|
bool bAllowZigZagOneWayBorders = false ; // flag per abilitare le curve di bordo per ZigZag/OneWay
|
|
Point3d ptStart = P_INVALID ; // punto d'inizio
|
|
Point3d ptEnd = P_INVALID ; // punto di fine
|
|
SurfFlatRegion SfrLimit ; // superficie limite per estensione lati aperti
|
|
bool bCalcFeed = true ; // flag per calcolo della Feed
|
|
double dFeed = 1000 ; // feed di riferimento per frazione
|
|
double dToolFeed = 1000 ; // feed del tool
|
|
// -------------------------------------------------------
|
|
bool bAllClosed = true ; // flag per indicare se tutti i bordi della *pSfr da svuotare sono chiusi
|
|
bool bAllOpen = true ; // flag per indicare se tutti i bordi della *pSfr da svuotare sono aperti
|
|
double dOffsExtra = 2. ; // Offset aggiuntivo per percosi a ZigZag/OneWay se richieste le curve di bordo
|
|
Frame3d frLocXY ; // frame per conti nel piano XY
|
|
int nOffsType = ICurve::OFF_FILLET ; // tipologia di Offset per estensione degli aperti
|
|
double dOpenEdgeRad = 0. ; // raggio effettivo di estensione degli aperti
|
|
// --------------------------------------------------------
|
|
bool bPolishing = false ; // flag per lucidatura
|
|
double dEpicyclesRad = 0. ; // raggio degli epicicli
|
|
double dEpicyclesDist = 0. ; // distanza degli epicicli
|
|
} ;
|
|
static double TOL_TRAPEZOID = 50 * EPS_SMALL ; // tolleranza per casi a trapezio SpiralPocket
|
|
static double TOL_REMOVE_OFFSET = 2. ; // tolleranza per controllo materiale lasciato da un Offset
|
|
typedef vector<ICRVCOMPOPOVECTOR> VICRVCOMPOPOVECTOR ;
|
|
|
|
//---------------------------------------------------------------------------
|
|
// definizione varibaile di debug
|
|
#define ENABLE_DEBUG 0
|
|
#if ENABLE_DEBUG
|
|
#include "EgtDev/Include/EGkGeoObjSave.h"
|
|
#include "EgtDev/Include/EGkGeoPoint3d.h"
|
|
#include "EgtDev/Include/EGkGeoVector3d.h"
|
|
|
|
// Varibili ausiliarie
|
|
vector<IGeoObj*> VT ;
|
|
vector<Color> VC ;
|
|
|
|
//---------------------------------------------------------------------------
|
|
inline Color GetRandomColor() {
|
|
return Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
inline void DrawCurve( const ICurveComposite* pCrvCompo) {
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return ;
|
|
for ( int nU = 0 ; nU < pCrvCompo->GetCurveCount() ; ++ nU) {
|
|
int nProp0 ; pCrvCompo->GetCurveTempProp( nU, nProp0, 0) ;
|
|
VT.emplace_back( static_cast<IGeoObj*>( pCrvCompo->GetCurve( nU)->Clone())) ;
|
|
VC.emplace_back( nProp0 == 0 ? BLUE : RED) ;
|
|
}
|
|
return ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
inline void DrawSfrAndLoops( const ISurfFlatRegion* pSfr, Color cCol = Color( 0., 255., 0., .5)) {
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return ;
|
|
VT.emplace_back( static_cast<IGeoObj*>( pSfr->Clone())) ;
|
|
VC.emplace_back( cCol) ;
|
|
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
|
|
for ( int nL = 0 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) {
|
|
PtrOwner<ICurveComposite> pCrvCompo( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ;
|
|
DrawCurve( pCrvCompo->Clone()) ;
|
|
}
|
|
}
|
|
return ;
|
|
}
|
|
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------------
|
|
static double
|
|
GetMaxFeed( const PocketParams& PockParams)
|
|
{
|
|
return PockParams.dFeed ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static double
|
|
GetMinFeed( const PocketParams& PockParams)
|
|
{
|
|
return PockParams.dFeed * PockParams.dSideStep / ( 2. * PockParams.dRad) ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static double
|
|
GetFeed( const PocketParams& PockParams)
|
|
{
|
|
return ( PockParams.dFeed > 0 ? PockParams.dFeed : PockParams.dToolFeed) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignMaxFeed( ICurveComposite* pCrv, const PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// assegno la Feed massima ad ogni sottocurva di pCrv
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
AssignMinFeed( ICurveComposite* pCrv, const PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// assegno la Feed minima ad ogni sottocurva di pCrv
|
|
for ( int nU = 0 ; nU < pCrv->GetCurveCount() ; ++ nU)
|
|
pCrv->SetCurveTempParam( nU, GetMinFeed( PockParams), 0) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
AssignCustomFeed( ICurveComposite* pCrv, const PocketParams& PockParams, double dFeed)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
// La Feed deve essere compresa tra la minima e la massima
|
|
dFeed = Clamp( dFeed, GetMinFeed( PockParams), GetMaxFeed( PockParams)) ;
|
|
|
|
// assegno la Feed ad ogni sottocurva di pCrv
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, dFeed, 0) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
GetFeedForParam( double dPar, const PocketParams& PockParams, double& dFeed)
|
|
{
|
|
/*
|
|
Feed
|
|
^
|
|
|
|
|
GetFeed() + --------------\
|
|
| * \
|
|
| * \
|
|
| * \
|
|
| * \
|
|
| * \
|
|
GetFeed() * GetSideStep() / Diam + * *
|
|
| * *
|
|
0--------------+------+---------------> Length of working angle
|
|
GetSideStep() Diam
|
|
*/
|
|
|
|
// dominio
|
|
dPar = Clamp( dPar, 0., 2. * PockParams.dRad) ;
|
|
|
|
// se la funzione si riduce ad un unico tratto costante
|
|
if ( 2. * PockParams.dRad - PockParams.dSideStep < 50. * EPS_SMALL) {
|
|
dFeed = GetMaxFeed( PockParams) ;
|
|
return true ;
|
|
}
|
|
// altrimenti controllo in quale tratto lineare cade il parametro
|
|
else {
|
|
if ( PockParams.dSideStep < dPar + 50. * EPS_SMALL) {
|
|
dFeed = GetFeed( PockParams) + ( GetFeed( PockParams) * ( 1. - ( PockParams.dSideStep / ( 2. * PockParams.dRad)))) *
|
|
( dPar - PockParams.dSideStep) / ( PockParams.dSideStep - ( 2. * PockParams.dRad)) ;
|
|
}
|
|
else
|
|
dFeed = GetMaxFeed( PockParams) ; // se sono nel tratto costante ...
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignFeedZigZagOneWay( ICurveComposite* pCompo, const bool bIsLink, const ICURVEPOVECTOR& vLAbove,
|
|
const ICURVEPOVECTOR& vLUnder, const ICRVCOMPOPOVECTOR& vAddedLinks,
|
|
const PocketParams& PockParams)
|
|
{
|
|
// controllo che la pCompoLine sia effettivamente una linea valida
|
|
if ( pCompo == nullptr || ! pCompo->IsValid())
|
|
return false ;
|
|
|
|
// controllo se il Flag per assegnare la Feed è attivo
|
|
if ( ! PockParams.bCalcFeed)
|
|
return AssignMaxFeed( pCompo, PockParams) ;
|
|
|
|
// inzialmente setto la feed Minima alla curva
|
|
AssignMinFeed( pCompo, PockParams) ;
|
|
|
|
// se è un link tra livelli diversi, allora esco ( con Feed Minima )
|
|
if ( bIsLink) {
|
|
Point3d ptS_link ; pCompo->GetStartPoint( ptS_link) ;
|
|
Point3d ptE_link ; pCompo->GetEndPoint( ptE_link) ;
|
|
if ( abs( ptE_link.y - ptS_link.y) > 500 * EPS_SMALL)
|
|
return true ;
|
|
}
|
|
|
|
// se la curva è piccola, allora esco ( con Feed Minima)
|
|
double dLen = EPS_SMALL ;
|
|
if ( ! pCompo->GetLength( dLen) || dLen < 0.6 * ( 2 * PockParams.dRad))
|
|
return true ;
|
|
|
|
Point3d ptS, ptE ;
|
|
pCompo->GetStartPoint( ptS) ;
|
|
pCompo->GetEndPoint( ptE) ;
|
|
|
|
// creo l'intervallo desiderato valutanto le coordinate x
|
|
Intervals IntMinFeed ; IntMinFeed.Set( ptS.x, ptE.x) ;
|
|
|
|
// creo un vettore contenente i tratti sopra e i tratti sotto attivi
|
|
ICURVEPOVECTOR vAllInt ; vAllInt.reserve( int( vLUnder.size()) + int( vLAbove.size())) ;
|
|
for ( int i = 0 ; i < int( vLUnder.size()) ; ++ i)
|
|
vAllInt.emplace_back( vLUnder[i]->Clone()) ;
|
|
for ( int i = 0 ; i < int( vLAbove.size()) ; ++ i)
|
|
vAllInt.emplace_back( vLAbove[i]->Clone()) ;
|
|
|
|
int nDim = int( vAllInt.size()) ;
|
|
|
|
// aggiungo le parti dei link interessate
|
|
double dCurrY = ptS.y ;
|
|
BBox3d bBoxLink ;
|
|
for ( int l = 0 ; l < int( vAddedLinks.size()) ; ++ l) {
|
|
vAddedLinks[l]->GetLocalBBox( bBoxLink) ;
|
|
if (( dCurrY - bBoxLink.GetMin().y) < PockParams.dSideStep + 50 * EPS_SMALL ||
|
|
( dCurrY - bBoxLink.GetMax().y) < PockParams.dSideStep + 50 * EPS_SMALL) {
|
|
|
|
// determino la direzione del Link
|
|
Vector3d vtLinkDir( - 25, 0, 0) ;
|
|
|
|
// recupero gli estremi
|
|
Point3d ptS_l ; vAddedLinks[l]->GetStartPoint( ptS_l) ;
|
|
Point3d ptE_l ; vAddedLinks[l]->GetEndPoint( ptE_l) ;
|
|
if ( ptE_l.x - ptS_l.x < EPS_SMALL)
|
|
vtLinkDir.x -= ptS_l.x - ptE_l.x ;
|
|
|
|
// superficie a rettangolo definita dal Box
|
|
PtrOwner<ISurfFlatRegion> pSfrRectUp( GetSurfFlatRegionRectangle( bBoxLink.GetDimX() + 50, PockParams.dSideStep)) ;
|
|
pSfrRectUp->Translate(( ptS_l - ORIG) + vtLinkDir) ;
|
|
|
|
// link sopra o sotto alla curva currente ?
|
|
bool bIsDown = true ;
|
|
if ( dCurrY - ptS_l.y < EPS_SMALL)
|
|
bIsDown = false ;
|
|
|
|
// classificazione con la superficie a rettangolo
|
|
CRVCVECTOR ccClass ;
|
|
pSfrRectUp->GetCurveClassification( *vAddedLinks[l], EPS_SMALL, ccClass) ;
|
|
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
|
|
if (( ccClass[i].nClass == CRVC_IN && bIsDown) ||
|
|
( ccClass[i].nClass == CRVC_OUT && ! bIsDown)) {
|
|
PtrOwner<ICurveComposite> pCrvInt( ConvertCurveToComposite( vAddedLinks[l]->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
|
|
if ( ! IsNull( pCrvInt) || pCrvInt->IsValid())
|
|
vAllInt.emplace_back( Release( pCrvInt)) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// scorro tutti i tratti Attivi
|
|
for ( int i = 0 ; i < int( vAllInt.size()) ; ++ i) {
|
|
|
|
// controllo che il tratto lineare sotto sia sufficientemente lungo
|
|
double dLen_iU = EPS_SMALL ;
|
|
if ( ! vAllInt[i]->GetLength( dLen_iU) || ( dLen_iU < 1.1 * ( 2 * PockParams.dRad) && i < nDim))
|
|
continue ;
|
|
|
|
// estremi dell'intervallo
|
|
Point3d ptS_iU, ptE_iU ;
|
|
vAllInt[i]->GetStartPoint( ptS_iU) ;
|
|
vAllInt[i]->GetEndPoint( ptE_iU) ;
|
|
|
|
// la vtDir per Above e Under è sempre invertita rispetto alla linea corrente
|
|
// sottraggo questo intervallo a quello originale
|
|
IntMinFeed.Subtract( ptS_iU.x, ptE_iU.x) ;
|
|
}
|
|
|
|
// l'intervallo orginale ora conterrà i sottointervalli con feed Minima, tolgo quelli troppo piccoli
|
|
Intervals IntMinFeed_noSmall ;
|
|
double dParS = EPS_SMALL ; double dParE = EPS_SMALL ;
|
|
bool bFound = IntMinFeed.GetFirst( dParS, dParE) ;
|
|
while ( bFound) {
|
|
if ( dParE - dParS > PockParams.dRad - 5 * EPS_SMALL)
|
|
IntMinFeed_noSmall.Add( dParS, dParE) ;
|
|
bFound = IntMinFeed.GetNext( dParS, dParE) ;
|
|
}
|
|
|
|
// ora che ho solo tratti abbastanza lunghi, creo l'intervallo complementare
|
|
// questo intervallo contiene tutte le feed Massime
|
|
Intervals IntMaxFeed ; IntMaxFeed.Set( ptS.x, ptE.x) ;
|
|
IntMaxFeed.Subtract( IntMinFeed_noSmall) ;
|
|
|
|
// tolgo le parti piccole
|
|
Intervals IntMaxFeed_noSmall ;
|
|
bFound = IntMaxFeed.GetFirst( dParS, dParE) ;
|
|
while ( bFound) {
|
|
if ( dParE - dParS > PockParams.dRad - 5 * EPS_SMALL)
|
|
IntMaxFeed_noSmall.Add( dParS, dParE) ;
|
|
bFound = IntMaxFeed.GetNext( dParS, dParE) ;
|
|
}
|
|
|
|
// recupero la direzione principale della curva corrente
|
|
Vector3d vtDir = X_AX ;
|
|
if ( ptE.x - ptS.x < EPS_SMALL)
|
|
vtDir = - X_AX ;
|
|
|
|
// trasformo questi intervalli nei parametri corrispondenti sulla linea originale
|
|
bool bFMax = false ;
|
|
bFound = IntMaxFeed_noSmall.GetFirst( dParS, dParE) ;
|
|
if ( bFound && IntMaxFeed_noSmall.IsInside( ptS.x + vtDir.x * 20 * EPS_SMALL))
|
|
bFMax = true ;
|
|
if ( ! bFound && bIsLink) // se non ho intervalli a Feed Massima, allora esco ( con Feed Minima)
|
|
return true ;
|
|
|
|
|
|
while ( bFound) {
|
|
if ( ! bIsLink) { // se segmento di ZigZag
|
|
double du_js = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParS, ptS.y, ptS.z), du_js) ;
|
|
pCompo->AddJoint( du_js) ;
|
|
double du_je = EPS_SMALL ; pCompo->GetParamAtPoint( Point3d( dParE, ptE.y, ptE.z), du_je) ;
|
|
pCompo->AddJoint( du_je) ;
|
|
}
|
|
bFound = IntMaxFeed_noSmall.GetNext( dParS, dParE) ;
|
|
}
|
|
|
|
// aggiorno le proprietà temporanee con la feed
|
|
if ( ! bIsLink) {
|
|
for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) {
|
|
if ( IsEven( u) == bFMax)
|
|
pCompo->SetCurveTempParam( u, GetMaxFeed( PockParams) , 0) ;
|
|
else
|
|
pCompo->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SwapSideBySecondTempParam( ICurveComposite* pCompo)
|
|
{
|
|
if ( pCompo == nullptr || ! pCompo->IsValid())
|
|
return false ;
|
|
pCompo->SetTempParam( int( pCompo->GetTempParam( 1)) == MDS_LEFT ? MDS_RIGHT : MDS_LEFT, 1) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CheckSimpleOverlap( const ICurve* pCrv, const ICurveComposite* pCrvOri, int& nStat, double dToll)
|
|
{
|
|
// controllo dei parametri
|
|
if (( pCrv == nullptr || pCrvOri == nullptr || dToll < EPS_SMALL))
|
|
return false ;
|
|
nStat = 1 ; // 0 -> no overlap | 1 -> overlap
|
|
|
|
// controllo se una sottocurva della composita è abbastanza vicina a tutti i punti trovati
|
|
const double nMAX = 10.0 ;
|
|
for ( int i = 0 ; i <= nMAX ; ++i) {
|
|
double dPar = i / nMAX ;
|
|
Point3d ptC ; pCrv->GetPointD1D2( dPar, ICurve::FROM_PLUS, ptC) ;
|
|
if ( ! pCrvOri->IsPointOn( ptC, dToll)) {
|
|
nStat = 0 ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignFeedForOpenEdge( ICurveComposite* pCrv, const ISurfFlatRegion* pSfrOpenEdges, const PocketParams& PockParams)
|
|
{
|
|
// NB. pSfrOpenEdges deve essere una regione che all'esterno contiene i lati considerati aperti
|
|
// Solo ai tratti esterni vengono assegnate le Feed
|
|
|
|
// se non rischiesto il calcolo della Feed, lascio quella standard
|
|
if ( ! PockParams.bCalcFeed)
|
|
return AssignMaxFeed( pCrv, PockParams) ;
|
|
|
|
// se non ho una regione di classificazione, non faccio nulla
|
|
if ( pSfrOpenEdges == nullptr || ! pSfrOpenEdges->IsValid())
|
|
return true ;
|
|
|
|
// classifico la curva in base alla regione
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrOpenEdges->GetCurveClassification( *pCrv, EPS_SMALL, ccClass)) {
|
|
PtrOwner<ICurveComposite> pNewCrv( CreateCurveComposite()) ;
|
|
if ( IsNull( pNewCrv))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
|
|
// recupero il tratto di curva corrente
|
|
PtrOwner<ICurveComposite> pCrvRange( ConvertCurveToComposite( pCrv->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
|
|
if ( ! IsNull( pCrvRange) && pCrvRange->IsValid()) {
|
|
// se il tratto non è interno, calcolo la sua Feed
|
|
if ( ccClass[i].nClass != CRVC_IN) {
|
|
double dFeed = GetMinFeed( PockParams) ;
|
|
GetFeedForParam( 2. * PockParams.dRad - PockParams.dOpenEdgeRad, PockParams, dFeed) ;
|
|
AssignCustomFeed( pCrvRange, PockParams, dFeed) ;
|
|
}
|
|
// aggiungo il tratto alla sottocurva
|
|
if ( ! pNewCrv->AddCurve( Release( pCrvRange)))
|
|
return false ;
|
|
}
|
|
}
|
|
// assegno la curva calcolata
|
|
pNewCrv->SetTempProp( pCrv->GetTempProp( 0), 0) ;
|
|
pNewCrv->SetTempProp( pCrv->GetTempProp( 1), 1) ;
|
|
pNewCrv->SetTempParam( pCrv->GetTempParam( 0), 0) ;
|
|
pNewCrv->SetTempParam( pCrv->GetTempParam( 1), 1) ;
|
|
double dThick ; pCrv->GetThickness( dThick) ;
|
|
pNewCrv->SetThickness( dThick) ;
|
|
Vector3d vtExtr ; pCrv->GetExtrusion( vtExtr) ;
|
|
pNewCrv->SetExtrusion( vtExtr) ;
|
|
pCrv->CopyFrom( pNewCrv) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SimplifyCurveByFeeds( ICurveComposite* pCrvCompo, const PocketParams& PockParam, double dToler)
|
|
{
|
|
// controllo la validità della curva
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// NB. Effettuo il Merge dei tratti di curva che presentano la stessa Feed ( mediante la tolleranza)
|
|
|
|
// curva semplificata
|
|
PtrOwner<ICurveComposite> pCrvSimple( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvSimple))
|
|
return false ;
|
|
|
|
/*
|
|
- effettuo il Merge dei tratti di curva che presentano la stessa Feed
|
|
- imposto la Feed dei tratti piccoli come quella dei tratti precedenti ( per evitare singolarità)
|
|
- effettuo un nuovo Merge per uniformare ulteriormente i nuovi tratti ottenuti
|
|
*/
|
|
|
|
double dCurrTempParam ;
|
|
int nParStart = 0 ;
|
|
double dTempParam ;
|
|
double dLen ;
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
pCrvCompo->GetCurveTempParam( i, dTempParam) ;
|
|
if ( i == 0) {
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
else if ( abs( dCurrTempParam - dTempParam) > dToler) { // se parametri diversi
|
|
// recupero il tratto di curva uniforme
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, i))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
// lo semplifico
|
|
// recupero la lunghezza del tratto e imposto associo la Feed corrente
|
|
// se curva corta -> il parametro di Feed è definito da quello precedente
|
|
pCrv->MergeCurves( 100 * EPS_SMALL, ANG_TOL_STD_DEG, false) ;
|
|
pCrv->GetLength( dLen) ;
|
|
for ( int nU = 0 ; nU < pCrv->GetCurveCount() ; ++ nU)
|
|
pCrv->SetCurveTempParam( nU, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvSimple->AddCurve( Release( pCrv)) ;
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
}
|
|
// ultima parte di curva uniforme ...
|
|
PtrOwner<ICurveComposite> pCrvLast( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ;
|
|
if ( ! IsNull( pCrvLast)) {
|
|
pCrvLast->MergeCurves( 100 * EPS_SMALL, ANG_TOL_STD_DEG, false) ;
|
|
double dLen ; pCrvLast->GetLength( dLen) ;
|
|
for ( int nU = 0 ; nU < pCrvLast->GetCurveCount() ; ++ nU)
|
|
pCrvLast->SetCurveTempParam( nU, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvSimple->AddCurve( Release( pCrvLast)) ;
|
|
}
|
|
|
|
// pulisco la curva originale
|
|
pCrvCompo->Clear() ;
|
|
|
|
// scorro i nuovi tratti ottenuti e ripeto il ragionamento
|
|
for ( int i = 0 ; i < pCrvSimple->GetCurveCount() ; ++ i) {
|
|
pCrvSimple->GetCurveTempParam( i, dTempParam) ;
|
|
if ( i == 0) {
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
else if ( abs( dCurrTempParam - dTempParam) > dToler) {
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, i))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
pCrv->MergeCurves( 100 * EPS_SMALL, ANG_TOL_STD_DEG, false) ;
|
|
// recupero la lunghezza di tale curve
|
|
double dLen ; pCrv->GetLength( dLen) ;
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvCompo->AddCurve( Release( pCrv)) ; // aggiungo alla curva finale
|
|
dCurrTempParam = dTempParam ;
|
|
nParStart = i ;
|
|
}
|
|
}
|
|
// ultima parte di curva uniforme ...
|
|
pCrvLast.Set( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, pCrvSimple->GetCurveCount()))) ;
|
|
if ( ! IsNull( pCrvLast)) {
|
|
// semplifico
|
|
pCrvLast->MergeCurves( 100 * EPS_SMALL, ANG_TOL_STD_DEG, false) ;
|
|
// recupero la lunghezza di tale curva e imposto la Feed
|
|
pCrvLast->GetLength( dLen) ;
|
|
for ( int nU = 0 ; nU < pCrvLast->GetCurveCount() ; ++ nU)
|
|
pCrvLast->SetCurveTempParam( nU, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ;
|
|
pCrvCompo->AddCurve( Release( pCrvLast)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignFeedSpiral( ICurveComposite* pCompo, const ISurfFlatRegion* pSrfRemoved, bool bIsLink, bool bFirstOffs,
|
|
const ISurfFlatRegion* pSfrOpenEdges, const PocketParams& PockParams, double dTol)
|
|
{
|
|
// controllo la validità della curva
|
|
if ( pCompo == nullptr || ! pCompo->IsValid() || pCompo->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// controllo se il Flag per assegnare la Feed è attivo
|
|
if ( ! PockParams.bCalcFeed)
|
|
return AssignMaxFeed( pCompo, PockParams) ;
|
|
|
|
// Se link di conformal ZigZag
|
|
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG && bIsLink)
|
|
return AssignMaxFeed( pCompo, PockParams) ;
|
|
|
|
// imposto di Default la Feed minima per ogni sottocurva
|
|
AssignMinFeed( pCompo, PockParams) ;
|
|
|
|
// se non ho una superificie svuotata, allora esco ( con Feed Minima)
|
|
if ( pSrfRemoved == nullptr || ! pSrfRemoved->IsValid() || pSrfRemoved->GetChunkCount() == 0) {
|
|
// controllo eventuali sovrapposizioni con lati aperti
|
|
return AssignFeedForOpenEdge( pCompo, pSfrOpenEdges, PockParams) ;
|
|
}
|
|
|
|
// parametro di Offset per regione svuotata ( restringo la superficie in maniera appropriata)
|
|
double dOffs = 0. ;
|
|
if ( bIsLink)
|
|
dOffs = - PockParams.dRad ;
|
|
else if ( PockParams.dRad < PockParams.dSideStep)
|
|
dOffs = PockParams.dSideStep - PockParams.dRad ;
|
|
|
|
// clono la superificie ( valida)
|
|
PtrOwner<ISurfFlatRegion> pSrfRemovedOffs( pSrfRemoved->CreateOffsetSurf( dOffs, ICurve::OFF_FILLET)) ;
|
|
if ( IsNull( pSrfRemovedOffs) || ! pSrfRemovedOffs->IsValid() || pSrfRemovedOffs->GetChunkCount() == 0)
|
|
return true ; // esco ( sempre con Feed Minima)
|
|
|
|
// classifico le parti interne alla superificie creata solo dagli Offset
|
|
CRVCVECTOR ccClass ;
|
|
if ( ! pSrfRemovedOffs->GetCurveClassification( *pCompo, EPS_SMALL, ccClass))
|
|
return true ; // esco ( sempre con Feed Minima)
|
|
|
|
// creo la nuova curva con le Feed regolate
|
|
PtrOwner<ICurveComposite> pCompoNew( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoNew))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
|
|
// recupero il tratto di curva ricavato dalla classificazione
|
|
PtrOwner<ICurveComposite> pSubCompo( ConvertCurveToComposite( pCompo->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ;
|
|
if ( IsNull( pSubCompo))
|
|
continue ; // curva troppo piccola, passo alla successiva
|
|
// controllo se interno o fuori alla regione ( regolando quindi la Feed)
|
|
double dCurrFeed = ( ccClass[i].nClass == CRVC_IN ? GetMaxFeed( PockParams) :
|
|
GetMinFeed( PockParams)) ;
|
|
// assegno tale Feed ad ogni sottocurva ricavata dal tratto di classificazione
|
|
for ( int nU = 0 ; nU < pSubCompo->GetCurveCount() ; ++ nU)
|
|
pSubCompo->SetCurveTempParam( nU, dCurrFeed, 0) ;
|
|
// aggiungo la nuova curva con la Feed regolata a quella finale
|
|
pCompoNew->AddCurve( Release( pSubCompo)) ;
|
|
}
|
|
|
|
// sotituisco con quanto calcolato
|
|
pCompo->Clear() ;
|
|
pCompo->AddCurve( Release( pCompoNew)) ;
|
|
|
|
// cerco di uniformare i tratti ricavati
|
|
if ( ! bIsLink) { // ---------------- NEL CASO DI OFFSET ----------------
|
|
// creo un intervallo con tutte le Feed Minime
|
|
Intervals IntMinFeed ;
|
|
for ( int nU = 0 ; nU < pCompo->GetCurveCount() ; ++ nU) {
|
|
double dFeed ; pCompo->GetCurveTempParam( nU, dFeed, 0) ;
|
|
if ( abs( dFeed - GetMinFeed( PockParams)) < 5. * EPS_SMALL)
|
|
IntMinFeed.Add( nU, nU + 1) ;
|
|
}
|
|
double dParS = EPS_SMALL ; double dParE = EPS_SMALL ;
|
|
// a questo intervallo tolgo tutti i sottotratti di lunghezza inferiore alla tolleranza richiesta
|
|
Intervals IntMinFeed_noSmall ;
|
|
bool bFound = IntMinFeed.GetFirst( dParS, dParE) ;
|
|
while ( bFound) {
|
|
PtrOwner<ICurve> pCrv( pCompo->CopyParamRange( dParS, dParE)) ;
|
|
double dLen = EPS_SMALL ; pCrv->GetLength( dLen) ;
|
|
if ( dLen > dTol + 5. * EPS_SMALL)
|
|
IntMinFeed_noSmall.Add( dParS, dParE) ;
|
|
bFound = IntMinFeed.GetNext( dParS, dParE) ;
|
|
}
|
|
for ( int nU = 0 ; nU < pCompo->GetCurveCount() ; ++ nU) {
|
|
if ( ! IntMinFeed_noSmall.IsInside( nU + 0.5))
|
|
pCompo->SetCurveTempParam( nU, GetMaxFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
else { // ---------------- NEL CASO DI LINK ----------------
|
|
// le curve con lunghezza inferiore alla tolleranza vanno modificate
|
|
for ( int j = 0 ; j < pCompo->GetCurveCount() ; ++ j) {
|
|
double dLen = EPS_SMALL ;
|
|
pCompo->GetCurve( j)->GetLength( dLen) ;
|
|
if ( dLen < dTol + 5. * EPS_SMALL) {
|
|
// se curva piccola, ricavo il tratto successivo lungo quanto il diametro
|
|
double dLenStart ;
|
|
pCompo->GetLengthAtParam( j, dLenStart) ;
|
|
double dLenEnd = dLenStart + dLen + 2 * PockParams.dRad ;
|
|
double dUE = pCompo->GetCurveCount() ;
|
|
pCompo->GetParamAtLength( dLenEnd, dUE) ;
|
|
bool bFound = false ;
|
|
for ( int k = j + 1 ; k < int( ceil( dUE)) ; ++ k) {
|
|
double dLenH = EPS_SMALL ;
|
|
pCompo->GetCurve( k)->GetLength( dLenH) ;
|
|
if ( dLenH < dTol + 5 * EPS_SMALL)
|
|
continue ;
|
|
// cerco tra le curve successive vicine se ne trovo una con Feed Minima
|
|
double dParam ;
|
|
pCompo->GetCurveTempParam( k, dParam, 0) ;
|
|
if ( abs( dParam - GetMinFeed( PockParams)) < 5. * EPS_SMALL) {
|
|
pCompo->SetCurveTempParam( k, dParam, 0) ;
|
|
bFound = true ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( ! bFound && j != 0) {
|
|
// arrivato qui, so che successivamente non ho curve con Feed Minima vicine
|
|
double dParam ; pCompo->GetCurveTempParam( j - 1, dParam, 0) ;
|
|
if ( abs( dParam - GetMaxFeed( PockParams)) < 5. * EPS_SMALL)
|
|
// se anche la precedente ha Feed Massima -> la curva u-esima è piccola ed Isolata
|
|
pCompo->SetCurveTempParam( j, dParam, 0) ;
|
|
}
|
|
else
|
|
pCompo->SetCurveTempParam( j, GetMinFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bFirstOffs)
|
|
AssignFeedForOpenEdge( pCompo, pSfrOpenEdges, PockParams) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignFeedSpiralOpt( int nOptType, const PocketParams& PockParams, ICurveComposite* pCrv )
|
|
{
|
|
// controllo della curva corrente
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// controllo se il Flag per assegnare la Feed è attivo
|
|
if ( ! PockParams.bCalcFeed)
|
|
return AssignMaxFeed( pCrv, PockParams) ;
|
|
|
|
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_CONFORMAL_ZIGZAG ||
|
|
PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
|
|
if ( nOptType == 0) { // Spirale dall'Esterno
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
if ( u == 0) // prima circonferenza
|
|
pCrv->SetCurveTempParam( 0, GetMinFeed( PockParams), 0) ;
|
|
else // semi cerchi in tangenza
|
|
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
else if ( nOptType == 1) { // Trapezoidi
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
/* NB. Essendo la funzione CalcSpiral richiamata sia per lo SpiralIN che per lo SpiralOUT le curve sono sempre
|
|
orientate nello stesso modo, solamente alla fine viene invertita la curva finale per la svuotatura... */
|
|
else {
|
|
if ( nOptType == 0) { // Spiral verso l'esterno
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) {
|
|
if ( u > pCrv->GetCurveCount() - 3) // prime semi circonferenze
|
|
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
else
|
|
pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
else if ( nOptType == 1) { // Trapezoidi
|
|
for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u)
|
|
pCrv->SetCurveTempParam( u, GetMinFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ResetCurveTempProps( ICurveComposite* pCrvCompo)
|
|
{
|
|
// controllo parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// scorro le curve
|
|
for ( int nU = 0 ; nU < pCrvCompo->GetCurveCount() ; ++ nU) {
|
|
pCrvCompo->SetCurveTempProp( nU, 0, 0) ;
|
|
pCrvCompo->SetCurveTempProp( nU, 0, 1) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
IsCurveCompoHomogeneous( const ICurveComposite* pCrvCompo, bool& bAllClosed, bool& bAllOpen)
|
|
{
|
|
// controllo validità della curva
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
bAllClosed = true ;
|
|
bAllOpen = true ;
|
|
if ( pCrvCompo->GetCurveCount() == 0)
|
|
return true ;
|
|
|
|
// scorro le curve
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() && ( bAllOpen || bAllClosed) ; ++ i) {
|
|
// recupero la sottocurva i-esima
|
|
const ICurve* pCrv = pCrvCompo->GetCurve( i) ;
|
|
if ( pCrv == nullptr || ! pCrv->IsValid())
|
|
return false ;
|
|
int nTmpProp = pCrv->GetTempProp( 0) ;
|
|
bAllOpen = bAllOpen && ( nTmpProp == TEMP_PROP_OPEN_EDGE) ;
|
|
bAllClosed = bAllClosed && ( nTmpProp == TEMP_PROP_CLOSE_EDGE) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetSfrCrvCompoLoops( const ISurfFlatRegion* pSfr, ICRVCOMPOPOVECTOR& vCrvCompo)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
vCrvCompo.clear() ;
|
|
|
|
// scorro i Chunk della superficie
|
|
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
|
|
// scorro i loop del Chunk nC-esimo
|
|
for ( int nL = 0 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) {
|
|
// recupero il Loop come curva composita
|
|
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ;
|
|
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
|
|
return false ;
|
|
// inserisco la curva nel vettore
|
|
vCrvCompo.emplace_back( Release( pCrvLoop)) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
IsChunkAllHomogeneous( const ISurfFlatRegion* pSfr, int nChunk, bool& bAllClose, bool& bAllOpen)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid() || nChunk < 0 || nChunk > pSfr->GetChunkCount())
|
|
return false ;
|
|
bAllClose = true ;
|
|
bAllOpen = true ;
|
|
|
|
// scorro tutti i loop del Chunk
|
|
for ( int nL = 0 ; nL < pSfr->GetLoopCount( nChunk) ; ++ nL) {
|
|
// recupero il loop
|
|
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfr->GetLoop( nChunk, nL))) ;
|
|
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
|
|
return false ;
|
|
// scorro le curve semplici
|
|
int nCurrProp = TEMP_PROP_INVALID ;
|
|
for ( int nU = 0 ; nU < pCrvLoop->GetCurveCount() ; ++ nU) {
|
|
bAllClose = ( bAllClose && pCrvLoop->GetCurveTempProp( nU, nCurrProp, 0) &&
|
|
nCurrProp == TEMP_PROP_CLOSE_EDGE) ;
|
|
bAllOpen = ( bAllOpen && pCrvLoop->GetCurveTempProp( nU, nCurrProp, 0) &&
|
|
nCurrProp == TEMP_PROP_OPEN_EDGE) ;
|
|
if ( ! bAllClose && ! bAllOpen)
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
IsSfrAllHomogeneous( const ISurfFlatRegion* pSfr, bool& bAllClose, bool& bAllOpen)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
bAllClose = true ;
|
|
bAllOpen = true ;
|
|
|
|
// ciclo sui Chunk della regione
|
|
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
|
|
bool bChunkClose = true ;
|
|
bool bChunkOpen = true ;
|
|
if ( ! IsChunkAllHomogeneous( pSfr, nC, bChunkClose, bChunkOpen))
|
|
return false ;
|
|
bAllClose = ( bAllClose && bChunkClose) ;
|
|
bAllOpen = ( bAllOpen && bChunkOpen) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
IsSfrChunkMadeOnlyByClosedIslands( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParams,
|
|
bool& bOK, ICRVCOMPOPOVECTOR& vCrvIsl)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
vCrvIsl.clear() ;
|
|
bOK = false ;
|
|
|
|
// se non esistono isole
|
|
if ( pSfrChunk->GetLoopCount( 0) == 1)
|
|
return true ;
|
|
|
|
// controllo il loop esterno
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
for ( int i = 0 ; i < pCrvExtLoop->GetCurveCount() ; ++ i) {
|
|
int nTmpProp = TEMP_PROP_INVALID ;
|
|
if ( pCrvExtLoop->GetCurveTempProp( i, nTmpProp, 0) && nTmpProp == TEMP_PROP_CLOSE_EDGE)
|
|
return true ;
|
|
}
|
|
|
|
// controllo le Isole
|
|
bool bOkIsl = true ;
|
|
for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) && bOkIsl ; ++ i) {
|
|
// recupero l'isola
|
|
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ;
|
|
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
|
|
return false ;
|
|
// controllo le sue TmpProps
|
|
for ( int i = 0 ; i < pCrvIsl->GetCurveCount() && bOkIsl ; ++ i) {
|
|
int nTmpProp = TEMP_PROP_INVALID ;
|
|
bOkIsl = ( pCrvIsl->GetCurveTempProp( i, nTmpProp, 0) && nTmpProp == TEMP_PROP_CLOSE_EDGE) ;
|
|
}
|
|
if ( bOkIsl)
|
|
vCrvIsl.emplace_back( Release( pCrvIsl)) ;
|
|
}
|
|
|
|
if ( ! bOkIsl)
|
|
vCrvIsl.clear() ;
|
|
else
|
|
bOK = true ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetIndOfHypoteticalChunk( const ICRVCOMPOPOVECTOR& vCrv, const ICurveComposite* pCrvA, const ICurveComposite* pCrvB,
|
|
INTVECTOR& vIndA, INTVECTOR& vIndB)
|
|
{
|
|
/*
|
|
date due curve interne ad una regione piana descritta da vCrv, individuare i chunk in cui
|
|
sono rispettivamente contenute
|
|
*/
|
|
|
|
// controllo dei parametri
|
|
for ( int i = 0 ; i < int( vCrv.size()) ; ++ i) {
|
|
if ( vCrv[i] == nullptr || ! vCrv[i]->IsValid())
|
|
return false ;
|
|
}
|
|
if ( pCrvA == nullptr || ! pCrvA->IsValid() || pCrvB == nullptr || ! pCrvB->IsValid())
|
|
return false ;
|
|
vIndA.clear() ;
|
|
vIndB.clear() ;
|
|
|
|
// ricavo le polyLines dalle curve
|
|
POLYLINEVECTOR vPL ;
|
|
for ( int i = 0 ; i < int( vCrv.size()) ; ++ i) {
|
|
vPL.emplace_back( PolyLine()) ;
|
|
vCrv[i]->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, vPL.back()) ;
|
|
}
|
|
// calcolo la matrice dei Chunks e dei Loops
|
|
Vector3d vtN ;
|
|
INTMATRIX vnPLIndMat ;
|
|
BOOLVECTOR vbInvert ;
|
|
if ( ! CalcRegionPolyLines( vPL, vtN, vnPLIndMat, vbInvert))
|
|
return false ;
|
|
// scorro i Chunks
|
|
for ( int nC = 0 ; nC < int( vnPLIndMat.size()) ; ++ nC) {
|
|
PtrOwner<ISurfFlatRegion> pSfrTmp( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrTmp))
|
|
return false ;
|
|
// scorro i Loops
|
|
for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL) {
|
|
if ( nL == 0) {
|
|
if ( ! pSfrTmp->AddExtLoop( CloneCurveComposite( vCrv[vnPLIndMat[nC][0]])))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! pSfrTmp->AddIntLoop( CloneCurveComposite( vCrv[vnPLIndMat[nC][nL]])))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// piccolo Offset di correzione della superfice
|
|
pSfrTmp->Offset( 500 * EPS_SMALL, ICurve::OFF_FILLET) ;
|
|
// classifico le curve con il Chunk nC-esimo
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrTmp->GetCurveClassification( *pCrvA, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) {
|
|
for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL)
|
|
vIndA.emplace_back( vnPLIndMat[nC][nL]) ;
|
|
}
|
|
ccClass.clear() ;
|
|
if ( pSfrTmp->GetCurveClassification( *pCrvB, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) {
|
|
for ( int nL = 0 ; nL < int( vnPLIndMat[nC].size()) ; ++ nL)
|
|
vIndB.emplace_back( vnPLIndMat[nC][nL]) ;
|
|
}
|
|
// se entrambi indici trovati, esco
|
|
if ( ! vIndA.empty() && ! vIndB.empty())
|
|
return true ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static double
|
|
GetExtendPathLen( const PocketParams& PockParam)
|
|
{
|
|
return ( Clamp( 2. * PockParam.dRad - PockParam.dOpenEdgeRad, 0., PockParam.dRad) + PockParam.dOpenMinSafe) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ExtendPath( ICurveComposite* pCompo, const ISurfFlatRegion* pSfr, const PocketParams& PockParams,
|
|
const Vector3d& vtFirstOut, bool bEndOrStart, double dExtension, bool& bOkExtended)
|
|
{
|
|
/*
|
|
Estensione della curva pCompo con un segmento lineare.
|
|
bEndOrStart: definisce se la curva va estesa all'inizio o alla fine
|
|
vtFirstOut: prima direzione su cui tentare l'entrata, se questa non andasse bene
|
|
ruoto progressivamente di 90°
|
|
La curva viene estesa di un tratto lineare in direzione vtRotOut per una lunghezza
|
|
pari a Rad + OffsR + OpenMinSafe.
|
|
*/
|
|
|
|
// controllo dei parametri
|
|
if ( pCompo == nullptr || ! pCompo->IsValid() ||
|
|
pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
bOkExtended = false ;
|
|
|
|
// recupero il punto iniziale/finale della curva con versore tangente
|
|
Point3d pt ;
|
|
Vector3d vtTan ;
|
|
if ( bEndOrStart) {
|
|
if ( ! pCompo->GetEndPoint( pt) || ! pCompo->GetEndDir( vtTan))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! pCompo->GetStartPoint( pt) || ! pCompo->GetStartDir( vtTan))
|
|
return false ;
|
|
vtTan.Invert() ;
|
|
}
|
|
|
|
// angoli di rotazione ( potrebbero servirne altri)
|
|
const double ANG_45 = ANG_RIGHT / 2. ;
|
|
const array vAngles{ 0., ANG_RIGHT, 3 * ANG_RIGHT, ANG_45, 3 * ANG_45, 5 * ANG_45, 7 * ANG_45} ;
|
|
|
|
// ruoto il versore di uscita cercando un'entrata valida
|
|
double dMinDist = PockParams.dRad + PockParams.dRadialOffset ;
|
|
for ( int i = 0 ; i < ssize( vAngles) ; ++ i) {
|
|
// ruoto il versore d'uscita
|
|
Vector3d vtRotOut = GetRotate( vtFirstOut, Z_AX, - vAngles[i]) ;
|
|
// calcolo il punto di caduta dell'utensile
|
|
Point3d ptFall = pt + vtRotOut * dExtension ;
|
|
// controllo che sia sufficientemente distante dalla superficie limite
|
|
bool bInside = true ;
|
|
bool bOkOut = true ;
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
bOkOut = ( IsPointInsideSurfFr( ptFall, &PockParams.SfrLimit, dMinDist - 10. * EPS_SMALL, bInside) && ! bInside) ;
|
|
if ( bOkOut)
|
|
bOkOut = ( IsPointInsideSurfFr( Media( ptFall, pt), &PockParams.SfrLimit, dMinDist - 10. * EPS_SMALL, bInside) && ! bInside) ;
|
|
}
|
|
if ( bOkOut)
|
|
bOkOut = ( IsPointInsideSurfFr( ptFall, pSfr, dMinDist, bInside) && ! bInside) ;
|
|
if ( bOkOut) {
|
|
// aggiungo al ritorno l'uscita
|
|
pCompo->AddLine( ptFall, bEndOrStart) ;
|
|
pCompo->SetCurveTempProp( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0), TEMP_PROP_OUT_START, 0) ;
|
|
pCompo->SetCurveTempParam( ( bEndOrStart ? pCompo->GetCurveCount() - 1 : 0),
|
|
( bEndOrStart ? GetMaxFeed( PockParams) : GetMinFeed( PockParams)), 0) ;
|
|
bOkExtended = true ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustContourStart( ICurveComposite* pCompo, const PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCompo == nullptr || ! pCompo->IsValid())
|
|
return false ;
|
|
|
|
// cerco il tratto lineare chiuso più lungo ...
|
|
int i = 0 ; // <--- indice della curva corrente
|
|
int nMax = - 1 ;
|
|
double dLenMax = 0 ;
|
|
const ICurve* pCrv = pCompo->GetFirstCurve() ;
|
|
while ( pCrv != nullptr) {
|
|
double dLen ;
|
|
if ( pCrv->GetType() == CRV_LINE && pCrv->GetTempProp() == TEMP_PROP_CLOSE_EDGE &&
|
|
pCrv->GetLength( dLen) && dLen > dLenMax) {
|
|
dLenMax = dLen ;
|
|
nMax = i ;
|
|
}
|
|
++ i ;
|
|
pCrv = pCompo->GetNextCurve() ;
|
|
}
|
|
// se non trovato o troppo corto, cerco il tratto chiuso più lungo in generale ( non lineare)
|
|
if ( nMax < 0 || dLenMax < 1.25 * PockParams.dRad) {
|
|
i = 0 ;
|
|
pCrv = pCompo->GetFirstCurve() ;
|
|
while ( pCrv != nullptr) {
|
|
double dLen ;
|
|
if ( pCrv->GetType() != CRV_LINE && pCrv->GetTempProp() == TEMP_PROP_CLOSE_EDGE &&
|
|
pCrv->GetLength( dLen) && dLen > dLenMax) {
|
|
dLenMax = dLen ;
|
|
nMax = i ;
|
|
}
|
|
++ i ;
|
|
pCrv = pCompo->GetNextCurve() ;
|
|
}
|
|
}
|
|
// se non trovato, imposto il punto iniziale a mentà del primo tratto
|
|
if ( nMax == -1) {
|
|
pCompo->ChangeStartPoint( 0.5) ;
|
|
return true ;
|
|
}
|
|
pCompo->ChangeStartPoint( nMax + 0.5) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetHomogeneousParts( ICurveComposite* pCrvCompo, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vpCrvs)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
vpCrvs.clear() ;
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0) // se curva non valida
|
|
return true ;
|
|
|
|
// la curva ha sia tratti aperti che tratti chiusi, quindi sposto l'inizio a metà del tratto più lungo ( se richiesto)...
|
|
AdjustContourStart( pCrvCompo, PockParams) ;
|
|
|
|
// estraggo parti con proprietà uniforme in un vettore ( vpCrvs)
|
|
int nCurrTempProp ;
|
|
int nParStart = 0 ;
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
int nTempProp ;
|
|
pCrvCompo->GetCurveTempProp( i, nTempProp) ;
|
|
if ( i == 0) {
|
|
nCurrTempProp = nTempProp ;
|
|
nParStart = i ;
|
|
}
|
|
else if ( nCurrTempProp != nTempProp) {
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, i))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
pCrv->SetTempProp( nCurrTempProp) ;
|
|
vpCrvs.emplace_back( Release( pCrv)) ;
|
|
nCurrTempProp = nTempProp ;
|
|
nParStart = i ;
|
|
}
|
|
}
|
|
// ultima curva...
|
|
PtrOwner<ICurveComposite> pCrv( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
pCrv->SetTempProp( nCurrTempProp) ;
|
|
vpCrvs.emplace_back( Release( pCrv)) ;
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool
|
|
CreateSurfFrIncidence( const ICurveComposite* pCrv, const PocketParams& PockParams,
|
|
const double dRad, ISurfFlatRegion* pSfrInc)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid())
|
|
return false ;
|
|
pSfrInc->Clear() ;
|
|
|
|
// se la curva è chiusa, allora la regione di incidenza è già definita
|
|
if ( pCrv->IsClosed()) {
|
|
PtrOwner<ISurfFlatRegion> pSfrInc_tmp( GetSurfFlatRegionFromFatCurve( pCrv->Clone(), dRad + EPS_SMALL, false, false)) ;
|
|
if ( IsNull( pSfrInc_tmp) || ! pSfrInc_tmp->IsValid())
|
|
return false ;
|
|
pSfrInc->CopyFrom( pSfrInc_tmp) ;
|
|
return ( pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 0) ;
|
|
}
|
|
|
|
// creo la curva di Offset esterna ( deve esistere ed essere valida)
|
|
OffsetCurve OffsCrv ;
|
|
PtrOwner<ICurve> pOffsExt( CreateCurveComposite()) ;
|
|
if ( IsNull( pOffsExt) ||
|
|
! OffsCrv.Make( pCrv, dRad, PockParams.nOffsType) ||
|
|
! pOffsExt.Set( OffsCrv.GetLongerCurve()) ||
|
|
IsNull( pOffsExt))
|
|
return false ;
|
|
|
|
// creo la curva di Offset interna ( se esiste e valida...)
|
|
PtrOwner<ICurve> pOffsInt( CreateCurveComposite()) ;
|
|
if ( IsNull( pOffsInt))
|
|
return false ;
|
|
if ( OffsCrv.Make( pCrv, - dRad, PockParams.nOffsType)) {
|
|
PtrOwner<ICurve> pMyCrv( OffsCrv.GetLongerCurve()) ;
|
|
while ( ! IsNull( pMyCrv) && pMyCrv->IsValid()) {
|
|
if ( ! pMyCrv->IsClosed()) {
|
|
pOffsInt.Set( pMyCrv) ;
|
|
break ;
|
|
}
|
|
else
|
|
pMyCrv.Set( OffsCrv.GetLongerCurve()) ;
|
|
}
|
|
}
|
|
|
|
// recupero gli estremi della curva aperta corrente
|
|
Point3d pt1 ; pCrv->GetEndPoint( pt1) ;
|
|
Point3d pt4 ; pCrv->GetStartPoint( pt4) ;
|
|
|
|
// verifico se una delle due curve esiste
|
|
bool bExistExt = ( ! IsNull( pOffsExt) && pOffsExt->IsValid()) ;
|
|
bool bExistInt = ( ! IsNull( pOffsInt) && pOffsInt->IsValid()) ;
|
|
|
|
PtrOwner<ICurveComposite> pCrvExtLoopSurfInc( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvExtLoopSurfInc))
|
|
return false ;
|
|
if ( bExistExt && bExistInt) {
|
|
Point3d pt2 ; pOffsInt->GetEndPoint( pt2) ;
|
|
Point3d pt3 ; pOffsInt->GetStartPoint( pt3) ;
|
|
Point3d pt5 ; pOffsExt->GetStartPoint( pt5) ;
|
|
pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt1) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt2) ;
|
|
pOffsInt->Invert() ;
|
|
pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt4) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt5) ;
|
|
}
|
|
else if ( bExistExt) {
|
|
Point3d pt5 ; pOffsExt->GetStartPoint( pt5) ;
|
|
pCrvExtLoopSurfInc->AddCurve( Release( pOffsExt)) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt1) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt4) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt5) ;
|
|
}
|
|
else if ( bExistInt) {
|
|
Point3d pt5 ; pOffsInt->GetStartPoint( pt5) ;
|
|
pCrvExtLoopSurfInc->AddCurve( Release( pOffsInt)) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt1) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt4) ;
|
|
pCrvExtLoopSurfInc->AddLine( pt5) ;
|
|
}
|
|
else
|
|
return false ;
|
|
// per sicurezza...
|
|
pCrvExtLoopSurfInc->Close() ;
|
|
|
|
// creo la regione ( la curva potrebbe autointersecarsi)
|
|
SurfFlatRegionByContours SfrByC ;
|
|
if ( ! SfrByC.AddCurve( Release( pCrvExtLoopSurfInc)))
|
|
return false ;
|
|
|
|
PtrOwner<ISurfFlatRegion> pSfr( SfrByC.GetSurf()) ;
|
|
if ( pSfr->IsValid() && pSfr->GetChunkCount() > 0) {
|
|
if ( AreOppositeVectorApprox( pSfr->GetNormVersor(), Z_AX))
|
|
pSfr->Invert() ;
|
|
pSfrInc->CopyFrom( pSfr) ;
|
|
return true ;
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustOpenEdge( const ICurveComposite* pCrvCompo, const ICRVCOMPOPOVECTOR& vCrvIsland,
|
|
const double dParS, const double dParE, const Vector3d& vtTanS,
|
|
const Vector3d& vtTanE, const double dRad, const double dDiamJ,
|
|
const PocketParams& PockParams, ICurveComposite* pCrvBorder)
|
|
{
|
|
/* parametri :
|
|
pCrvCompo -> curva originaria di bordo
|
|
vCrvIsland -> vettore delle isole all'interno di pCrvCompo
|
|
dParS -> parametro sulla pCrvCompo per l'inizio del tratto aperto
|
|
dParE -> parametro sulla pCrvCompo per la fine del tratto aperto
|
|
vtTanS -> vettore di tangenza finale tratto chiuso precedente
|
|
vtTanE -> vettore di tangenza iniziale ( invertito ) del tratto chiuso successivo
|
|
dRad -> raggio di Offset per la regione di incidenza
|
|
dDiamJ -> ampiezza delle curve a fagiolo per estendere la regione di incidenza
|
|
pCrvRes -> curva da restituire ( inizialmente è il tratto aperto sulla pCrvCompo ;
|
|
questa curva sarà l'estensione del lato aperto, adattandosi alla geometria
|
|
dei chiusi ( non posso vedere solo i chiusi adiacenti all'aperto, devo considerare TUTTI i chiusi
|
|
della pCrvCompo
|
|
*/
|
|
|
|
// controllo la validità dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 ||
|
|
pCrvBorder == nullptr || ! pCrvBorder->IsValid() || pCrvBorder->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// definisco la regione di incidenza ( leggermente più grande )
|
|
PtrOwner<ISurfFlatRegion> pSfrInc( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrInc))
|
|
return false ;
|
|
if ( ! CreateSurfFrIncidence( pCrvBorder, PockParams, dRad + 75. * EPS_SMALL, pSfrInc)) {
|
|
pSfrInc.Set( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( pCrvBorder), dRad + 75 * EPS_SMALL, false, false)) ;
|
|
if ( IsNull( pSfrInc) || ! pSfrInc->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
// creo un vettore con tutte le curve che potrebbero cadere, in parte, nella regione di incidenza
|
|
ICRVCOMPOPOVECTOR vCrvToCheck ;
|
|
for ( int i = 0 ; i < int( vCrvIsland.size()) ; ++ i)
|
|
vCrvToCheck.emplace_back( vCrvIsland[i]->Clone()) ; // aggiungo le isole
|
|
// se la curva originale non è tutta Aperta -> devo aggiungere anche essa nelle curve da controllare
|
|
bool bIsAllOpen = abs( abs( dParE - dParS) - pCrvCompo->GetCurveCount()) < EPS_SMALL ;
|
|
if ( ! bIsAllOpen) {
|
|
// recupero il tratto di curva prima e dopo dell'aperto corrente
|
|
PtrOwner<ICurveComposite> pCompoOther( ConvertCurveToComposite( pCrvCompo->CopyParamRange( dParE, dParS))) ;
|
|
if ( IsNull( pCompoOther) || ! pCompoOther->IsValid())
|
|
return false ;
|
|
vCrvToCheck.emplace_back( Release( pCompoOther)) ; // aggiungo il bordo
|
|
}
|
|
|
|
// controllo se la curva è un'isola ( normale del piano -Z_AX)
|
|
bool bIsIsland = false ;
|
|
double dArea ; Plane3d plCheck ;
|
|
if ( ! pCrvCompo->GetArea( plCheck, dArea))
|
|
return false ;
|
|
bIsIsland = AreSameVectorEpsilon( plCheck.GetVersN(), - Z_AX, 10. * EPS_SMALL) ;
|
|
|
|
// scorro il vettore creato...
|
|
for ( int i = 0 ; i < int( vCrvToCheck.size()) ; ++ i) {
|
|
// 1) recupero la curva corrente
|
|
PtrOwner<ICurveComposite> pCrvCurr( vCrvToCheck[i]->Clone()) ;
|
|
if ( IsNull( pCrvCurr) || ! pCrvCurr->IsValid())
|
|
return false ;
|
|
// 2) ricavo i tratti con proprietà uniformi ( Aperti/Chiusi )
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
if ( ! GetHomogeneousParts( pCrvCurr, PockParams, vpCrvs))
|
|
return false ;
|
|
// 3) considero solo i tratti chiusi
|
|
for ( int nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU) {
|
|
if ( vpCrvs[nU]->GetTempProp() == TEMP_PROP_OPEN_EDGE)
|
|
continue ;
|
|
// 4) effettuo l'Offset della curva di metà dDiamJ
|
|
OffsetCurve OffsCrv ;
|
|
if ( ! OffsCrv.Make( vpCrvs[nU], - dDiamJ / 2. - 20. * EPS_SMALL, PockParams.nOffsType))
|
|
return false ;
|
|
// 5) scorro tutte le curve di Offset che si sono formate, prendendo sempre la più lunga tra le rimanenti
|
|
PtrOwner<ICurve> pOffLongestCrv( OffsCrv.GetLongerCurve()) ;
|
|
while ( ! IsNull( pOffLongestCrv)) {
|
|
// 6) creo la regione di incidenza di tale curva ( "Curva a fagiolo")
|
|
bool bSquareEnds = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ;
|
|
bool bSquareMids = ( PockParams.nOffsType == ICurve::OFF_CHAMFER) ;
|
|
PtrOwner<ISurfFlatRegion> pSfrBean( GetSurfFlatRegionFromFatCurve( Release( pOffLongestCrv), dDiamJ / 2., bSquareEnds, bSquareMids)) ;
|
|
if ( IsNull( pSfrBean) || ! pSfrBean->IsValid())
|
|
return false ;
|
|
// inverto se necessario
|
|
if ( AreOppositeVectorApprox( pSfrBean->GetNormVersor(), pSfrInc->GetNormVersor()))
|
|
pSfrBean->Invert() ;
|
|
// 7) se la "Regione a fagiolo" non influenza la regione di incidenza, la transcuro
|
|
bool bDiscard = false ;
|
|
if ( ! bIsIsland) {
|
|
// se tratto un loop esterno
|
|
PtrOwner<ISurfFlatRegion> pSfrBean_test( CloneSurfFlatRegion( pSfrBean)) ;
|
|
if ( IsNull( pSfrBean_test) || ! pSfrBean_test->IsValid())
|
|
return false ;
|
|
pSfrBean_test->Intersect( *pSfrInc) ;
|
|
bDiscard = ( IsNull( pSfrBean_test) ||
|
|
! pSfrBean_test->IsValid() ||
|
|
pSfrBean_test->GetChunkCount() == 0) ;
|
|
}
|
|
else if ( bIsAllOpen && bIsIsland) {
|
|
// se isola aperta
|
|
for ( int cI = 0 ; cI < pSfrInc->GetChunkCount() && ! bDiscard ; ++ cI) {
|
|
for ( int cB = 0 ; cB < pSfrBean->GetChunkCount() && ! bDiscard ; ++ cB) {
|
|
if ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrBean, cB) == REGC_IN1) {
|
|
for ( int l = 1 ; l < pSfrBean->GetLoopCount( cB) ; ++ l) {
|
|
PtrOwner<ISurfFlatRegion> pSfrTmp( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrTmp) || ! pSfrTmp->AddExtLoop( pSfrBean->GetLoop( cB, l)) ||
|
|
! pSfrTmp->Invert())
|
|
return false ;
|
|
bDiscard = ( pSfrInc->GetChunkSimpleClassification( cI, *pSfrTmp, 0) == REGC_IN1) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( ! bDiscard) {
|
|
// 8) aggiorno la regione di incidenza
|
|
if ( ! pSfrInc->Add( *pSfrBean))
|
|
return false ;
|
|
}
|
|
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ; // passo al tratto offsettato successivo
|
|
}
|
|
}
|
|
}
|
|
|
|
// ATTENZIONE !
|
|
// L'algoritmo di allargamento presso i lati aperti è Euristico; io mi estendo a seconda della geometria del
|
|
// lato aperto al di fuori del volume di svuotatura... Devo controllare di non rovinare delle zone al di fuori di
|
|
// esso !
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
// recupero la superficie limite
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( PockParams.SfrLimit.Clone()) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
|
|
return false ;
|
|
pSfrLimit->Offset( 50. * EPS_SMALL, ICurve::OFF_FILLET) ;
|
|
pSfrInc->Subtract( *pSfrLimit) ; // rimuovo la regione limite
|
|
|
|
// può capitare che la regione Limite mi crei più Chunk sulla pSfrInc
|
|
// I chunk da togliere sono tutti quelli che si sono separati dalla pCrvBorder
|
|
// NB. Il mio obiettivo è quello di avere un'unica curva con estremi i due tratti chiusi estremanti
|
|
|
|
// se curva originale non tutta aperta e multi-chunk
|
|
if ( ! bIsAllOpen && pSfrInc->GetChunkCount() > 1) { // se ottengo più chunks
|
|
PtrOwner<ISurfFlatRegion> pNewSfrInc( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pNewSfrInc))
|
|
return false ;
|
|
// ricavo i punti iniziali e finali della curva di bordo
|
|
Point3d ptS, ptE ;
|
|
if ( ! pCrvBorder->GetStartPoint( ptS) || ! pCrvBorder->GetEndPoint( ptE))
|
|
return false ;
|
|
// per ogni Chunk ( > 1)
|
|
bool bFound = false ;
|
|
for ( int i = 0 ; i < pSfrInc->GetChunkCount() ; ++ i) {
|
|
// bordo esterno
|
|
PtrOwner<ICurveComposite> pCrvEL( ConvertCurveToComposite( pSfrInc->GetLoop( i, 0))) ;
|
|
if ( IsNull( pCrvEL) || ! pCrvEL->IsValid())
|
|
return false ;
|
|
// se estremi in comune, la curva cercata è la seguente
|
|
if ( pCrvEL->IsPointOn( ptS, 300 * EPS_SMALL) && pCrvEL->IsPointOn( ptE, 300 * EPS_SMALL)) {
|
|
bFound = true ;
|
|
// se la nuova regione di incidenza è vuota, aggiorno
|
|
if ( ! pNewSfrInc->IsValid())
|
|
pNewSfrInc.Set( pSfrInc->CloneChunk( i)) ;
|
|
// se la nuova regione di incidenza non è vuota, aggiungo
|
|
else {
|
|
PtrOwner<ISurfFlatRegion> pSfrToAdd( pSfrInc->CloneChunk( i)) ;
|
|
if ( IsNull( pSfrToAdd) || ! pNewSfrInc->Add( *pSfrToAdd))
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
// se ho trovato qualcosa, allora aggiorno la superficie di incidenza
|
|
if ( bFound && pNewSfrInc->IsValid())
|
|
pSfrInc.Set( pNewSfrInc) ;
|
|
}
|
|
// se la regione è formata da più chunks, prendo quella con area di loop esterno maggiore
|
|
if ( ! IsNull( pSfrInc) && pSfrInc->IsValid() && pSfrInc->GetChunkCount() > 1) {
|
|
double dMaxArea = - INFINITO ;
|
|
int nChunk = 0 ;
|
|
for ( int nC = 0 ; nC < pSfrInc->GetChunkCount() ; ++ nC) {
|
|
PtrOwner<ICurve> pExtLoop( pSfrInc->GetLoop( nC, 0)) ;
|
|
if ( IsNull( pExtLoop) || ! pExtLoop->IsValid())
|
|
return false ;
|
|
double dArea = EPS_SMALL ;
|
|
pExtLoop->GetAreaXY( dArea) ;
|
|
if ( dArea > dMaxArea) {
|
|
dMaxArea = dArea ;
|
|
nChunk = nC ;
|
|
}
|
|
}
|
|
pSfrInc.Set( pSfrInc->CloneChunk( nChunk)) ;
|
|
}
|
|
// se regione di incidenza nulla o non valida, errore
|
|
if ( IsNull( pSfrInc) || ! pSfrInc->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
// dalla regione di incidenza devo estrarre il tratto di bordo che ha per estremi i lati chiusi
|
|
// ( stando al di fuori, quindi estendendomi all'esterno, della regione da svuotare )
|
|
PtrOwner<ICurveComposite> pCrvNewBorder( ConvertCurveToComposite( pSfrInc->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid())
|
|
return false ;
|
|
// imposto la curva come tutta aperta
|
|
for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u)
|
|
pCrvNewBorder->SetCurveTempProp( u, TEMP_PROP_OPEN_EDGE, 0) ;
|
|
|
|
// se la curva originale era tutta aperta... ( il nuovo lato è il loop esterno della regione di incidenza)
|
|
if ( bIsAllOpen) {
|
|
// pulisco la curva originale
|
|
pCrvBorder->Clear() ;
|
|
if ( bIsIsland) { // se isola inserisco il loop interno corretto della regione
|
|
for ( int l = 1 ; l < pSfrInc->GetLoopCount( 0) ; ++ l) {
|
|
// recupero il Loop
|
|
PtrOwner<ICurveComposite> pCrvIntLoop( ConvertCurveToComposite( pSfrInc->GetLoop( 0, l))) ;
|
|
if ( IsNull( pCrvIntLoop) || ! pCrvIntLoop->IsValid())
|
|
return false ;
|
|
// controllo che sia interno alla curva di bordo corrente ( l'isola aperta)
|
|
IntersCurveCurve ICC( *pCrvIntLoop, *pCrvCompo) ;
|
|
CRVCVECTOR ccClass ;
|
|
if ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 &&
|
|
ccClass[0].nClass == CRVC_OUT) {
|
|
// il loop interno corretto va impostato come unico e nuovo loop interno della regione
|
|
pCrvNewBorder->CopyFrom( pCrvIntLoop) ;
|
|
if ( IsNull( pCrvNewBorder) || ! pCrvNewBorder->IsValid())
|
|
return false ;
|
|
// imposto la curva come tutta aperta ( gira come girava già l'isola)
|
|
for ( int u = 0 ; u < pCrvNewBorder->GetCurveCount() ; ++ u)
|
|
pCrvNewBorder->SetCurveTempProp( u, TEMP_PROP_OPEN_EDGE, 0) ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
// se bordo esterno inserisco il bordo esterno
|
|
return ( pCrvBorder->AddCurve( Release( pCrvNewBorder))) ;
|
|
}
|
|
|
|
// altrimenti la spezzo il loop della regione di incidenza nei punti iniziali e finali della curva aperta originale
|
|
Point3d ptStart ; pCrvBorder->GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; pCrvBorder->GetEndPoint( ptEnd) ;
|
|
double dUTrimS = -1 ; double dUTrimE = -1 ;
|
|
pCrvNewBorder->GetParamAtPoint( ptStart, dUTrimS, 10000 * EPS_SMALL) ;
|
|
pCrvNewBorder->GetParamAtPoint( ptEnd, dUTrimE, 10000 * EPS_SMALL) ;
|
|
|
|
// pulisco la curva originale
|
|
pCrvBorder->Clear() ;
|
|
if ( ! pCrvBorder->AddCurve( pCrvNewBorder->CopyParamRange( dUTrimS, dUTrimE)) ||
|
|
! pCrvBorder->IsValid()) {
|
|
// se la curva non originaria di lato aperto non era chiusa...
|
|
if ( ! pCrvBorder->IsClosed() && pCrvNewBorder->IsClosed()) {
|
|
// cerco il punto più vicino al ptStart del chiuso sulla nuova curva di bordo aperta
|
|
Point3d ptMinDist ;
|
|
int nFlag ;
|
|
if ( ! DistPointCurve( ptStart, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag))
|
|
return false ;
|
|
// cambio il punto iniziale della border in ptMinDist
|
|
double dUTmp ;
|
|
if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp) ||
|
|
! pCrvNewBorder->ChangeStartPoint( dUTmp))
|
|
return false ;
|
|
// cerco il punto più vicino al ptEnd del chiuso sulla curva di bordo aperta
|
|
if ( ! DistPointCurve( ptEnd, *pCrvNewBorder).GetMinDistPoint( EPS_SMALL, ptMinDist, nFlag))
|
|
return false ;
|
|
// ricavo il parametro sulla curva
|
|
if ( ! pCrvNewBorder->GetParamAtPoint( ptMinDist, dUTmp))
|
|
return false ;
|
|
if ( dUTmp < EPS_SMALL)
|
|
dUTmp = pCrvNewBorder->GetCurveCount() - 5 * EPS_SMALL ;
|
|
// copio la curva tra questi due parametri
|
|
PtrOwner<ICurveComposite> pCrvTmp( ConvertCurveToComposite( pCrvNewBorder->CopyParamRange( 0., dUTmp))) ;
|
|
if ( IsNull( pCrvTmp) || ! pCrvTmp->IsValid())
|
|
return false ;
|
|
// raccordo con il chiuso mediante due tratto lineare
|
|
pCrvTmp->AddLine( ptStart, false) ;
|
|
pCrvTmp->AddLine( ptEnd, true) ;
|
|
// restituisco
|
|
pCrvBorder->CopyFrom( pCrvTmp) ;
|
|
return true ;
|
|
}
|
|
pCrvBorder->CopyFrom( pCrvCompo) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustContourWithOpenEdges( ICurveComposite* pCrvCompo, ICRVCOMPOPOVECTOR& vCrvIsl, PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// recupero i parametri di lavorazione correnti
|
|
double dDiam = PockParams.dRad * 2 ;
|
|
double dOffR = PockParams.dRadialOffset ;
|
|
double dStep = PockParams.dSideStep ;
|
|
|
|
// raggio di riferimento per offset
|
|
double dOutEdge = 0.5 * dDiam ;
|
|
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_ZIGZAG)
|
|
dOutEdge = max( dOutEdge, dDiam - dStep) ;
|
|
double dRad = dOutEdge + dOffR ;
|
|
if ( abs( PockParams.dMaxOpenEdgeRad) > 0 && PockParams.dMaxOpenEdgeRad < dRad)
|
|
dRad = PockParams.dMaxOpenEdgeRad ;
|
|
// se lavorazione ZigZag/OneWay con richiesta di curve di Bordo, gli aperti vanno leggermente allargati
|
|
if ( PockParams.bAllowZigZagOneWayBorders &&
|
|
( PockParams.nType == POCKET_ZIGZAG || PockParams.nType == POCKET_ONEWAY))
|
|
dRad += PockParams.dOffsExtra ;
|
|
// se lavorazione a ZigZag con smusso
|
|
if ( PockParams.nType == POCKET_ZIGZAG && PockParams.bSmooth)
|
|
dRad += PockParams.bSmooth ;
|
|
// se lavorazione Conformal
|
|
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
|
|
if ( PockParams.dRad - PockParams.dSideStep > 0)
|
|
dRad -= PockParams.dSideStep ;
|
|
}
|
|
|
|
// salvo il raggio trovato
|
|
PockParams.dOpenEdgeRad = dRad ;
|
|
|
|
// ricavo i tratti omogenei
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
if ( ! GetHomogeneousParts( pCrvCompo, PockParams, vpCrvs))
|
|
return false ;
|
|
// NB. Il primo tratto è la prima metà del tratto aperto più lungo ( se esiste )
|
|
|
|
// Offset esterno per CurveJ
|
|
double dDiamJ = 1.05 * ( dDiam) + 2 * dOffR ;
|
|
if ( abs( PockParams.dMaxOpenEdgeRad) > 0)
|
|
dDiamJ = 100 * EPS_SMALL ;
|
|
else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY)
|
|
dDiamJ = 1.05 * ( PockParams.dRad + dRad) + dOffR ;
|
|
// NB. 1.05 serve per eccedere leggermente, in modo che il contro-offset della prima curva di svuotatura presenti
|
|
// dei piccoli archi tra i chiusi e gli aperti ( in modo da svuotare bene lungo il chiuso)
|
|
|
|
// curva finale da restituire
|
|
PtrOwner<ICurveComposite> pCrvCompo_final( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvCompo_final))
|
|
return false ;
|
|
|
|
// parametro iniziale del tratto corrente, sulla curva originale
|
|
double dParS = 0. ;
|
|
// scorro tutti i tratti omogenei nel vettore
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
// recupero la proprietà della curva composita
|
|
int nCurrTmpProp = vpCrvs[i]->GetTempProp() ;
|
|
// aggiorno i parametri del lato aperto corrente sulla curva originale
|
|
double dParE = dParS + 1. * vpCrvs[i]->GetCurveCount() ;
|
|
// se aperta
|
|
if ( nCurrTmpProp == TEMP_PROP_OPEN_EDGE) {
|
|
// controllo che sia almeno lungo quanto il diametro utensile
|
|
double dLen = 0. ;
|
|
vpCrvs[i]->GetLength( dLen) ;
|
|
// ricavo la tangenze dei lati chiusi agli estremi di questa curva
|
|
Vector3d vtTanS = V_INVALID ;
|
|
Vector3d vtTanE = V_INVALID ;
|
|
if ( i != 0) {
|
|
if ( ! vpCrvs[i-1]->GetEndDir( vtTanS) || // tangente finale del chiuso precedente
|
|
! vpCrvs[( i + 1) % int( vpCrvs.size())]->GetStartDir( vtTanE)) // tangente iniziale del chiuso successivo...
|
|
return false ;
|
|
vtTanE.Invert() ; // invertita
|
|
}
|
|
if ( ! AdjustOpenEdge( pCrvCompo, vCrvIsl, dParS, dParE, vtTanS, vtTanE, dRad, dDiamJ, PockParams, vpCrvs[i]))
|
|
return false ;
|
|
}
|
|
// assegno le proprietà di lato Aperto/Chiuso per la curva corrente
|
|
for ( int u = 0 ; u < vpCrvs[i]->GetCurveCount() ; ++ u)
|
|
vpCrvs[i]->SetCurveTempProp( u, nCurrTmpProp, TEMP_PROP_CLOSE_EDGE) ;
|
|
// aggiungo la curva ricavata ( se chiusa -> la copio, se aperta -> copio l'estesa)
|
|
if ( ! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone())) {
|
|
// per sicurezza, se gli estremi non coincidono, creo un piccolo raccordo lineare
|
|
Point3d ptH ; vpCrvs[i]->GetStartPoint( ptH) ;
|
|
if ( ! pCrvCompo_final->AddLine( ptH) ||
|
|
! pCrvCompo_final->SetCurveTempProp( pCrvCompo_final->GetCurveCount() - 1, 1, 0) ||
|
|
! pCrvCompo_final->AddCurve( vpCrvs[i]->Clone()))
|
|
return false ;
|
|
}
|
|
// aggiorno
|
|
dParS = dParE ;
|
|
}
|
|
// non dovrebbe esserci un gap, ma meglio prevenire problemi
|
|
pCrvCompo_final->Close() ;
|
|
// sostituisco
|
|
pCrvCompo->Clear() ;
|
|
pCrvCompo->CopyFrom( pCrvCompo_final) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ChainCompoCurves( ICRVCOMPOPOVECTOR& vCrvCompo)
|
|
{
|
|
// concatenamento delle curve composite
|
|
if ( ssize( vCrvCompo) < 2)
|
|
return true ;
|
|
|
|
// controllo validità delle curve
|
|
for ( int i = 0 ; i < ssize( vCrvCompo) ; ++ i) {
|
|
if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
// preparo i dati per il concatenamento
|
|
bool bFirst = true ;
|
|
Point3d ptNear = ORIG ;
|
|
double dToler = 500. * EPS_SMALL ; // leggermente maggiore della tolleranza sulla superficie limite
|
|
ChainCurves chainC ;
|
|
chainC.Init( false, dToler, ssize( vCrvCompo)) ; // evito inversioni, le curve devono essere coerenti
|
|
for ( int i = 0 ; i < ssize( vCrvCompo) ; ++ i) {
|
|
// recupero i dati della curva necessari al concatenamento e li assegno
|
|
Point3d ptStart, ptEnd ;
|
|
Vector3d vtStart, vtEnd ;
|
|
if ( ! vCrvCompo[i]->GetStartPoint( ptStart) || ! vCrvCompo[i]->GetStartDir( vtStart) ||
|
|
! vCrvCompo[i]->GetEndPoint( ptEnd) || ! vCrvCompo[i]->GetEndDir( vtEnd))
|
|
return false ;
|
|
if ( ! chainC.AddCurve( i + 1, ptStart, vtStart, ptEnd, vtEnd))
|
|
return false ;
|
|
// se prima curva, assegno inizio della ricerca
|
|
if ( bFirst) {
|
|
ptNear = ptStart + 10. * EPS_SMALL * vtStart ;
|
|
bFirst = false ;
|
|
}
|
|
}
|
|
|
|
// vettore delle curve composite risultante
|
|
ICRVCOMPOPOVECTOR vCrvCompoChained ;
|
|
|
|
// recupero i percorsi concatenati
|
|
INTVECTOR vnInd ;
|
|
while ( chainC.GetChainFromNear( ptNear, true, vnInd)) {
|
|
// creo una curva composita
|
|
PtrOwner<ICurveComposite> pCrvCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvCompo))
|
|
return false ;
|
|
// recupero le curve semplici e le inserisco nella curva composita
|
|
for ( int i = 0 ; i < ssize( vnInd) ; ++ i) {
|
|
int nId = abs( vnInd[i]) - 1 ;
|
|
bool bInvert = ( vnInd[i] < 0) ;
|
|
// se necessario, la inverto
|
|
if ( bInvert)
|
|
vCrvCompo[nId]->Invert() ;
|
|
// la aggiungo alla curva composta
|
|
if ( ! pCrvCompo->AddCurve( Release( vCrvCompo[nId]), true, dToler))
|
|
return false ;
|
|
}
|
|
// aggiorno il nuovo punto vicino
|
|
if ( pCrvCompo->GetCurveCount() > 0) {
|
|
pCrvCompo->GetEndPoint( ptNear) ;
|
|
vCrvCompoChained.emplace_back( Release( pCrvCompo)) ;
|
|
}
|
|
}
|
|
|
|
swap( vCrvCompoChained, vCrvCompo) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ChangePtStartForSinglePocketCurve( ICurveComposite* pCrvCompo, const PocketParams& PockParams,
|
|
const Point3d& ptRef, bool bInvertOpenCrv)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// se la curva è aperta
|
|
if ( ! pCrvCompo->IsClosed()) {
|
|
// se richiesta inversione
|
|
if ( bInvertOpenCrv) {
|
|
// se non ho un punto di riferimento, non faccio nulla
|
|
if ( ! ptRef.IsValid())
|
|
return true ;
|
|
// se ho un punto di riferimento controllo quale estremo è più vicino
|
|
Point3d ptStart ; pCrvCompo->GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; pCrvCompo->GetEndPoint( ptEnd) ;
|
|
if ( SqDist( ptRef, ptEnd) < SqDist( ptRef, ptStart))
|
|
pCrvCompo->Invert() ;
|
|
}
|
|
}
|
|
// se la curva è chiusa
|
|
else {
|
|
// se non ho un punto di riferimento, scelgo il tratto più lungo
|
|
if ( ! ptRef.IsValid()) {
|
|
ResetCurveTempProps( pCrvCompo) ;
|
|
AdjustContourStart( pCrvCompo, PockParams) ;
|
|
}
|
|
// se ho un punto di riferimento
|
|
else {
|
|
// cerco il punto più vicino
|
|
DistPointCurve DistPtCrv( ptRef, *pCrvCompo) ;
|
|
double dPar = 0. ;
|
|
int nFlag ;
|
|
if ( ! DistPtCrv.GetParamAtMinDistPoint( 0, dPar, nFlag))
|
|
return false ;
|
|
pCrvCompo->ChangeStartPoint( dPar) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdvanceExtendCurves( ICRVCOMPOPOVECTOR& vCrvCompo, const ISurfFlatRegion* pSfr,
|
|
const PocketParams& PockParams, bool bInvertOpenCrv)
|
|
{
|
|
// se non ho curve, allora non faccio nulla
|
|
if ( vCrvCompo.empty())
|
|
return true ;
|
|
|
|
// per ogni curva
|
|
for ( int i = 0 ; i < ssize( vCrvCompo) ; ++ i) {
|
|
// controllo validità della curva
|
|
if ( IsNull( vCrvCompo[i]) || ! vCrvCompo[i]->IsValid())
|
|
return false ;
|
|
// cambio il punto iniziale della curva
|
|
if ( ! ChangePtStartForSinglePocketCurve( vCrvCompo[i], PockParams, PockParams.ptStart, bInvertOpenCrv))
|
|
return false ;
|
|
}
|
|
|
|
// se superficie tutta chiusa, allora ho finito
|
|
if ( PockParams.bAllClosed)
|
|
return true ;
|
|
|
|
/*
|
|
L'estensione della curva permettendo un'entrata da fuori è valida se il
|
|
nuovo punto di Inizio ( ptFall) :
|
|
- E' al di fuori della superficie Limite ( di almeno Rad + OffsR)
|
|
- E' al di fuori della superifice di svuotatura ( di almeno dRad + OffsR)
|
|
*/
|
|
|
|
// scorro ogni singola curva composita
|
|
const int MAX_ITER = 4 ;
|
|
for ( int i = 0 ; i < ssize( vCrvCompo) ; ++ i) {
|
|
// controllo se la curva è chiusa o aperta
|
|
bool bIsClosed = vCrvCompo[i]->IsClosed() ;
|
|
// calcolo il numero massimo di punti in cui testare l'entrata da fuori
|
|
int nMaxIter = ( bIsClosed ? MAX_ITER : 1) ;
|
|
double dLen = EPS_SMALL ;
|
|
if ( nMaxIter == MAX_ITER)
|
|
vCrvCompo[i]->GetLength( dLen) ;
|
|
// scorro il numero di punti su cui tentare l'entrata da fuori
|
|
for ( int j = 0 ; j < nMaxIter ; ++ j) {
|
|
if ( j > 0) {
|
|
double dParAtLen = EPS_SMALL ;
|
|
vCrvCompo[i]->GetParamAtLength( dLen / nMaxIter, dParAtLen) ;
|
|
vCrvCompo[i]->ChangeStartPoint( dParAtLen) ;
|
|
}
|
|
// ricavo vettore tangente iniziale e provo ad estendere
|
|
Vector3d vtStart ; vCrvCompo[i]->GetStartDir( vtStart) ;
|
|
vtStart.Invert() ;
|
|
bool bIsStartExtended = false ;
|
|
if ( ! ExtendPath( vCrvCompo[i], pSfr, PockParams, vtStart, false, GetExtendPathLen( PockParams), bIsStartExtended))
|
|
return false ;
|
|
// se aperta, controllo la fine
|
|
if ( ! bIsClosed) {
|
|
// ricavo vettore tangente finale e provo ad estendere
|
|
Vector3d vtEnd ; vCrvCompo[i]->GetEndDir( vtEnd) ;
|
|
bool bIsEndExtended = false ;
|
|
if ( ! ExtendPath( vCrvCompo[i], pSfr, PockParams, vtEnd, true, GetExtendPathLen( PockParams), bIsEndExtended))
|
|
return false ;
|
|
if ( bIsEndExtended)
|
|
break ;
|
|
}
|
|
if ( bIsStartExtended)
|
|
break ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPocketCurvesByClosedEdges( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvCompoRes, bool& bAllRemoved)
|
|
{
|
|
// controllo parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
bAllRemoved = false ;
|
|
|
|
// controllo se la superficie si annulla con un controOffset del raggio utensile
|
|
double dMaxOffs ;
|
|
pSfrChunk->GetMaxOffset( dMaxOffs) ;
|
|
if ( dMaxOffs > PockParams.dRad + 200. * EPS_SMALL)
|
|
return true ;
|
|
// controllo che il MaxOptSize sia coerente
|
|
if ( 2. * dMaxOffs > PockParams.dMaxOptSize + 10. * EPS_SMALL)
|
|
return true ;
|
|
|
|
/*
|
|
NB. Si poteva calcolare la Fat curve del raggio utensile dell'Offset di tutti i chiusi e vedere
|
|
se l'unione di queste regioni copriva interamente il Chunk attuale... così facendo rischio di
|
|
trascurare delle parti che spariscono facendo il primo Offset dei chiusi; il controllo va fatto
|
|
quindi con un contro-offset della regione complessiva
|
|
*/
|
|
|
|
// controllo se il Chunk ha delle isole
|
|
bool bHasIslands = ( pSfrChunk->GetLoopCount( 0) > 1) ;
|
|
|
|
// recupero il Loop esterno
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
// recupero i tratti con proprietà uniformi
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
GetHomogeneousParts( pCrvExtLoop, PockParams, vpCrvs) ;
|
|
// unisco il primo e l'ultimo se estremi compatibili
|
|
if ( ssize( vpCrvs) > 1) {
|
|
Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ;
|
|
Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ;
|
|
if ( AreSamePointApprox( ptS, ptE)) {
|
|
vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ;
|
|
vpCrvs.erase( vpCrvs.end() - 1) ;
|
|
}
|
|
}
|
|
// CASO PARTICOLARE : Tunnel ( 2 tratti open e due tratto chiusi) -> non uso le curve singole
|
|
if ( int( vpCrvs.size() == 4))
|
|
return true ;
|
|
// controllo se il loop Esterno è uniforme ( quindi tutto chiuso o tutto aperto)
|
|
bool bExtAllClose = false ;
|
|
bool bExtAllOpen = false ;
|
|
if ( ssize( vpCrvs) == 1) {
|
|
if ( vpCrvs[0]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
|
|
bExtAllClose = true ;
|
|
else
|
|
bExtAllOpen = true ;
|
|
}
|
|
// NB. Se regione estena tutta chiusa o aperta e senza isole, non faccio nulla ( es. caso trapezi)
|
|
// Se tutta chiusa -> Chunk non svuotabile, il contro-offset del raggio utensile annulla la regione
|
|
// Se tutta aperta -> La regione è definita mediante Offset esterno del contorno aperto
|
|
if ( ( bExtAllClose || bExtAllOpen) && ! bHasIslands)
|
|
return true ;
|
|
// Se il contorno esterno è misto -> non devo avere isole
|
|
if ( ! bExtAllClose && ! bExtAllOpen && bHasIslands)
|
|
return true ;
|
|
// Se il contorno esterno è tutto chiuso e ho più di un'isola non faccio nulla
|
|
if ( bExtAllClose && pSfrChunk->GetLoopCount( 0) > 2)
|
|
return true ;
|
|
|
|
// scorro i chiusi e sottraggo la regione da loro creata mediante l'utensile
|
|
// NB. Queste curve sono orientate in maniera corretta
|
|
ICRVCOMPOPOVECTOR vCrvCompoResTmp ; // salvo il vettore di curve offsettate del raggio utensile
|
|
Voronoi myVRONI ;
|
|
for ( int i = 0 ; i < ssize( vpCrvs) ; ++ i) {
|
|
// se tratto chiuso
|
|
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
|
|
myVRONI.AddCurve( vpCrvs[i]) ;
|
|
}
|
|
// offset delle parti chiuse
|
|
ICURVEPOVECTOR vCrvVroniOffs ;
|
|
myVRONI.CalcOffset( vCrvVroniOffs, - PockParams.dRad - PockParams.dRadialOffset + EPS_SMALL, ICurve::OFF_FILLET) ;
|
|
for ( int i = 0 ; i < ssize( vCrvVroniOffs) ; ++ i)
|
|
vCrvCompoResTmp.emplace_back( ConvertCurveToComposite( Release( vCrvVroniOffs[i]))) ;
|
|
|
|
/*
|
|
Casi gestiti :
|
|
1) La curva esterna e mista e non ho isole ( ho già il percorso)
|
|
2) La curva esterna è tutta aperta e le isole sono tutte chiuse
|
|
3) La curva esterna è tutta chiusa e ho una sola isola aperta ( ho già il percorso)
|
|
*/
|
|
|
|
// 1)
|
|
if ( ! bExtAllClose && ! bExtAllOpen)
|
|
;
|
|
else {
|
|
// scorro tutte le isole
|
|
bool bValidIslands = true ; // flag per isole tutte uniformi
|
|
for ( int i = 1 ; i < pSfrChunk->GetLoopCount( 0) && bValidIslands ; ++ i) {
|
|
// controllo uniformità dell'isola
|
|
int nCurrTmpProp = TEMP_PROP_INVALID ;
|
|
int nPrecTmpProp = TEMP_PROP_INVALID ;
|
|
bool bIsMixed = false ;
|
|
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, i))) ;
|
|
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
|
|
return false ;
|
|
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ nU) {
|
|
pCrvIsl->GetCurveTempProp( nU, nCurrTmpProp, 0) ;
|
|
bIsMixed = ( nU != 0 && nCurrTmpProp != nPrecTmpProp) ;
|
|
nPrecTmpProp = nCurrTmpProp ;
|
|
}
|
|
// se proprità non uniformi -> tutta chiusa ( isole non uniformi non sono definite)
|
|
if ( bIsMixed) {
|
|
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() ; ++ nU)
|
|
pCrvIsl->SetTempProp( nU, TEMP_PROP_CLOSE_EDGE) ;
|
|
nCurrTmpProp = TEMP_PROP_CLOSE_EDGE ; // aggiorno il Flag
|
|
}
|
|
// 2) e 3)
|
|
bValidIslands = ( ( nCurrTmpProp == TEMP_PROP_CLOSE_EDGE && bExtAllOpen) ||
|
|
( nCurrTmpProp == TEMP_PROP_OPEN_EDGE && bExtAllClose)) ;
|
|
}
|
|
if ( ! bValidIslands)
|
|
return true ;
|
|
// nel caso 2) devo inserire le curve
|
|
if ( bExtAllOpen) {
|
|
// creo una regione formata solo dalla isole
|
|
PtrOwner<ISurfFlatRegion> pSrfIslands( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfIslands))
|
|
return false ;
|
|
for ( int nL = 1 ; nL < pSfrChunk->GetLoopCount( 0) ; ++ nL) {
|
|
pSrfIslands->AddExtLoop( pSfrChunk->GetLoop( 0, nL)) ;
|
|
vpCrvs.emplace_back( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, nL))) ;
|
|
}
|
|
pSrfIslands->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET) ;
|
|
for ( int nC = 0 ; nC < pSrfIslands->GetChunkCount() ; ++ nC) {
|
|
for ( int nL = 0 ; nL < pSrfIslands->GetLoopCount( nL) ; ++ nL)
|
|
vCrvCompoResTmp.emplace_back( ConvertCurveToComposite( pSrfIslands->GetLoop( nC, nL))) ;
|
|
}
|
|
}
|
|
}
|
|
// se non ho ottenuto curve, allora ho finito
|
|
if ( vCrvCompoResTmp.empty())
|
|
return true ;
|
|
|
|
// controllo che effettivamente l'utensile svuoti la regione
|
|
PtrOwner<ISurfFlatRegion> pSfrNotRemoved( CloneSurfFlatRegion( pSfrChunk)) ;
|
|
if ( IsNull( pSfrNotRemoved) || ! pSfrNotRemoved->IsValid())
|
|
return false ;
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset + 50. * EPS_SMALL ;
|
|
double dTol = 25. * EPS_SMALL ;
|
|
for ( int i = 0 ; i < ssize( vCrvCompoResTmp) ; ++ i) {
|
|
PtrOwner<ISurfFlatRegion> pSfrRemoved( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vCrvCompoResTmp[i]), dOffs, false, false)) ;
|
|
if ( ! IsNull( pSfrRemoved) && pSfrRemoved->IsValid()) {
|
|
if ( AreOppositeVectorEpsilon( pSfrRemoved->GetNormVersor(), pSfrNotRemoved->GetNormVersor(), dTol))
|
|
pSfrRemoved->Invert() ;
|
|
pSfrNotRemoved->Subtract( *pSfrRemoved) ;
|
|
}
|
|
}
|
|
for ( int i = 0 ; i < ssize( vpCrvs) ; ++ i) {
|
|
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) {
|
|
PtrOwner<ISurfFlatRegion> pSfrRemoved( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vpCrvs[i]), dOffs, false, false)) ;
|
|
if ( ! IsNull( pSfrRemoved) && pSfrRemoved->IsValid()) {
|
|
if ( AreOppositeVectorEpsilon( pSfrRemoved->GetNormVersor(), pSfrNotRemoved->GetNormVersor(), dTol))
|
|
pSfrRemoved->Invert() ;
|
|
pSfrNotRemoved->Subtract( *pSfrRemoved) ;
|
|
}
|
|
}
|
|
}
|
|
if ( pSfrNotRemoved->IsValid() && pSfrNotRemoved->GetChunkCount() > 0)
|
|
return true ;
|
|
|
|
bAllRemoved = true ;
|
|
|
|
// recupero la superficie limite ( se esiste ed è valida)
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrLimit))
|
|
return false ;
|
|
// per tutte le curve inserite, devo tenere solamente quelle esterne alla superficie limite
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
// recupero la superficie limite
|
|
pSfrLimit.Set( PockParams.SfrLimit.Clone()) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
|
|
return false ;
|
|
// effettuo un Offset della regione, tutto ciò che dista più del raggio utensile non la rovina
|
|
pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset - dTol, ICurve::OFF_FILLET) ; // c'è un offset radiale, quindi tengo tolleranza alta
|
|
ICRVCOMPOPOVECTOR vCrvCompoResTmpSplitted ;
|
|
for ( int i = 0 ; i < ssize( vCrvCompoResTmp) ; ++ i) {
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrLimit->GetCurveClassification( *vCrvCompoResTmp[i], EPS_SMALL, ccClass)) {
|
|
for ( int j = 0 ; j < ssize( ccClass) ; ++ j) {
|
|
if ( ccClass[j].nClass == CRVC_OUT) {
|
|
PtrOwner<ICurveComposite> pCrvRes( ConvertCurveToComposite( vCrvCompoResTmp[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
|
|
if ( ! IsNull( pCrvRes) && pCrvRes->IsValid())
|
|
vCrvCompoResTmpSplitted.emplace_back( Release( pCrvRes)) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se non ho curve, allora esco
|
|
if ( vCrvCompoResTmpSplitted.empty())
|
|
return true ;
|
|
// altrimenti aggiorno il vettore di curve con quelle solo esterne alla regione limite
|
|
swap( vCrvCompoResTmp, vCrvCompoResTmpSplitted) ;
|
|
}
|
|
|
|
// salvo come primo tempParam l'offset massimo della regione ( servirà per la Feed)
|
|
for ( int i = 0 ; i < ssize( vCrvCompoResTmp) ; ++ i) {
|
|
vCrvCompoResTmp[i]->SetTempParam( dMaxOffs, 0) ;
|
|
// se richiesta inversione della curva, allora inverto
|
|
if ( PockParams.bInvert)
|
|
vCrvCompoResTmp[i]->Invert() ;
|
|
vCrvCompoRes.emplace_back( Release( vCrvCompoResTmp[i])) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetSinglePocketingCurves( ISurfFlatRegion* pSfr, PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvSingleCurves)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
vCrvSingleCurves.clear() ;
|
|
|
|
// tengo una copia della superficie ( serve per l'estensione dei percorsi fuori dal grezzo)
|
|
PtrOwner<ISurfFlatRegion> pSfrClone( CloneSurfFlatRegion( pSfr)) ;
|
|
if ( IsNull( pSfrClone) || ! pSfrClone->IsValid())
|
|
return false ;
|
|
|
|
// scorro i Chunk della superficie
|
|
int nChunks = pSfr->GetChunkCount() ;
|
|
int nCurrChunk = 0 ;
|
|
for ( int i = 0 ; i < nChunks ; ++ i) {
|
|
// recupero il Chunk corrente
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( nCurrChunk)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
// se il Chunk corrente si svuota con curva singola, allora lo rimuovo dalla superficie
|
|
bool bRemoved = false ;
|
|
GetPocketCurvesByClosedEdges( pSfrChunk, PockParams, vCrvSingleCurves, bRemoved) ;
|
|
if ( bRemoved)
|
|
pSfr->EraseChunk( nCurrChunk) ;
|
|
else
|
|
++ nCurrChunk ;
|
|
}
|
|
|
|
// se ho ottenuto delle curve singole, allora le provo a concatenare
|
|
// ( tenendo solo le curve al di fuori della superficie limite con Offset del raggio del tool)
|
|
// potrei doverle riconcatenare
|
|
if ( ! ChainCompoCurves( vCrvSingleCurves))
|
|
return false ;
|
|
// estendo le curve, definendo ptStart e calcolando le Feeds
|
|
if ( ! AdvanceExtendCurves( vCrvSingleCurves, pSfrClone, PockParams, false))
|
|
return false ;
|
|
|
|
// imposto le Feed e inverto i percorsi per tali curve se richiesto
|
|
bool bAllowInvert = ( ! PockParams.bConventionalMilling) ;
|
|
for ( int i = 0 ; i < ssize( vCrvSingleCurves) ; ++ i) {
|
|
/*
|
|
Idea : Feed proporzionale al minimo Offset per annullare la regione
|
|
-> Massimo parametro sui bisettori di VORONOI
|
|
se le curve risultano aperte, cambio il loro punto iniziale a seconda dell'estensione fatta
|
|
*/
|
|
if ( vCrvSingleCurves[i]->IsValid() && vCrvSingleCurves[i]->GetCurveCount() > 0 && ! vCrvSingleCurves[i]->IsClosed()) {
|
|
int nFirstTempProp = vCrvSingleCurves[i]->GetFirstCurve()->GetTempProp( 0) ;
|
|
int nLastTempProp = vCrvSingleCurves[i]->GetLastCurve()->GetTempProp( 0) ;
|
|
// se estremi entrambi estesi
|
|
if ( nFirstTempProp == TEMP_PROP_OUT_START && nLastTempProp == TEMP_PROP_OUT_START) {
|
|
// se inversione delle curve accettata
|
|
if ( bAllowInvert) {
|
|
if ( PockParams.ptStart.IsValid()) {
|
|
Point3d ptStart ; vCrvSingleCurves[i]->GetStartPoint( ptStart) ;
|
|
double dSqStartDist = SqDist( PockParams.ptStart, ptStart) ;
|
|
Point3d ptEnd ; vCrvSingleCurves[i]->GetEndPoint( ptEnd) ;
|
|
double dSqEndDist = SqDist( PockParams.ptStart, ptEnd) ;
|
|
if ( dSqEndDist < dSqStartDist)
|
|
vCrvSingleCurves[i]->Invert() ;
|
|
}
|
|
}
|
|
vCrvSingleCurves[i]->GetEndPoint( PockParams.ptStart) ;
|
|
}
|
|
// se invece l'estensione è alla fine, sono forzato ad invertila per entrare presso il lato aperto
|
|
else if ( nLastTempProp == TEMP_PROP_OUT_START)
|
|
vCrvSingleCurves[i]->Invert() ;
|
|
}
|
|
// recupero il MaxOffset per la curva
|
|
double dMaxOffs = vCrvSingleCurves[i]->GetTempParam( 0) ;
|
|
// calcolo la Feed proporzionale a tale Offset
|
|
double dFeed ;
|
|
GetFeedForParam( 2 * dMaxOffs, PockParams, dFeed) ;
|
|
AssignCustomFeed( vCrvSingleCurves[i], PockParams, dFeed) ;
|
|
// imposto il flag di curva singola
|
|
vCrvSingleCurves[i]->SetTempProp( TEMP_PROP_SINGLE_CURVE, 0) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ModifySurfByOpenEdges( ISurfFlatRegion* pSfr, PocketParams& PockParams)
|
|
{
|
|
// controllo parametri :
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return true ; // <- se superficie non valida, allora non ho niente da impostare sui suoi lati
|
|
|
|
// se lati tutti chiusi, allora non devo fare nulla
|
|
if ( PockParams.bAllClosed)
|
|
return true ;
|
|
|
|
// NB. Tutti i Loop che presentano dei lati aperti possono essere estesi ; sia per loop esterni che per isole...
|
|
// I lati aperti vanno estesi seguendo lo geometria dei lati chiusi adiacenti e tenendo conto delle isole chiuse
|
|
// vicine ad essi
|
|
|
|
// creo la superficie da restituire... ( questa superficie sarà estesa presso i lati aperti)
|
|
PtrOwner<ISurfFlatRegion> pSrfFinal( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfFinal))
|
|
return false ;
|
|
|
|
// per ogni Chunck della superificie ottenuta...
|
|
for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) {
|
|
|
|
// flag per indicare se il Chunk è stato modificato mediante estensione degli aperti
|
|
bool bIsChunkModified = true ;
|
|
|
|
// ricavo il Loop esterno ( External Loop)
|
|
PtrOwner<ICurveComposite> pCrvEL( ConvertCurveToComposite( pSfr->GetLoop( nC, 0))) ;
|
|
if ( IsNull( pCrvEL) || ! pCrvEL->IsValid())
|
|
return false ;
|
|
|
|
// creo un vettore di curve con le isole del Chunk ( Internal Loops)
|
|
ICRVCOMPOPOVECTOR vCrvIsl ;
|
|
for ( int nL = 1 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) {
|
|
PtrOwner<ICurveComposite> pCrvIL( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ;
|
|
if ( IsNull( pCrvIL) || ! pCrvIL->IsValid())
|
|
return false ;
|
|
vCrvIsl.emplace_back( Release( pCrvIL)) ;
|
|
}
|
|
|
|
// se la curva esterna presenta dei lati aperti -> devo modificarla
|
|
bool bSomeOpen = false ;
|
|
int nProp0 = TEMP_PROP_INVALID ;
|
|
for ( int u = 0 ; u < pCrvEL->GetCurveCount() && ! bSomeOpen ; ++ u)
|
|
bSomeOpen = ( pCrvEL->GetCurveTempProp( u, nProp0, 0) && nProp0 == TEMP_PROP_OPEN_EDGE) ;
|
|
if ( bSomeOpen) {
|
|
// allorargo il Loop esterno presso i lati aperti
|
|
if ( ! AdjustContourWithOpenEdges( pCrvEL, vCrvIsl, PockParams))
|
|
return false ;
|
|
bIsChunkModified = true ;
|
|
}
|
|
|
|
// controllo i bordi delle isole ottenute
|
|
// NB. L'isola può essere tutta aperta o tutta chiusa ( se non uniforme, la forzo chiusa)
|
|
// [ La definizione di isola con proprietà non uniformi non è definita ]
|
|
ICRVCOMPOPOVECTOR vCrvToTIsland ; // isole che considero valide
|
|
for ( int nI = 0 ; nI < int( vCrvIsl.size()) ; ++ nI) {
|
|
// controllo uniformità
|
|
int nCurrTmpProp = TEMP_PROP_INVALID ;
|
|
int nPrecTmpProp = TEMP_PROP_INVALID ;
|
|
bool bIsMixed = false ;
|
|
PtrOwner<ICurveComposite> pCrvIsl( CloneCurveComposite( vCrvIsl[nI])) ;
|
|
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
|
|
return false ;
|
|
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ nU) {
|
|
pCrvIsl->GetCurveTempProp( nU, nCurrTmpProp, 0) ;
|
|
bIsMixed = ( nU != 0 && nCurrTmpProp != nPrecTmpProp) ;
|
|
nPrecTmpProp = nCurrTmpProp ;
|
|
}
|
|
// se proprità non uniformi -> tutta chiusa
|
|
if ( bIsMixed) {
|
|
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() ; ++ nU)
|
|
pCrvIsl->SetTempProp( nU, TEMP_PROP_CLOSE_EDGE) ;
|
|
nCurrTmpProp = TEMP_PROP_CLOSE_EDGE ;
|
|
bIsChunkModified = true ;
|
|
}
|
|
// se curva tutta aperta, controllo se l'isola può essere trascurata
|
|
if ( nCurrTmpProp == TEMP_PROP_OPEN_EDGE) {
|
|
// calcolo il massimo Offset
|
|
double dMaxOffs ;
|
|
CalcCurveLimitOffset( *pCrvIsl, dMaxOffs) ;
|
|
// se l'isola è trascurabile passo alla successiva
|
|
if ( dMaxOffs < 2 * PockParams.dRad + 2 * PockParams.dRadialOffset)
|
|
continue ;
|
|
// altrimenti la restringo
|
|
ICRVCOMPOPOVECTOR vCrvOther ;
|
|
vCrvOther.emplace_back( pCrvEL->Clone()) ;
|
|
for ( int nII = 0 ; nII < int( vCrvIsl.size()) ; ++ nII) {
|
|
if ( nII != nI)
|
|
vCrvOther.emplace_back( vCrvIsl[nII]->Clone()) ;
|
|
}
|
|
if ( ! AdjustContourWithOpenEdges( pCrvIsl, vCrvOther, PockParams))
|
|
return false ;
|
|
bIsChunkModified = true ;
|
|
}
|
|
// conservo l'isola ( estesa o meno)
|
|
vCrvToTIsland.emplace_back( pCrvIsl->Clone()) ; // <-- valida
|
|
}
|
|
|
|
// se c'è stata almeno una modifica di lato aperto al Chunk ( nC-esimo), devo ricreare il Chunk
|
|
// con i nuovi loop ricavati ( Chunk dopo Chunk creo la superficie finale)
|
|
if ( bIsChunkModified) {
|
|
/*
|
|
l'estensione degli aperti, può unire il loop esterno con il loop delle sue isole.
|
|
Creo una nuova regione dai nuovi contorni ottenuti
|
|
*/
|
|
SurfFlatRegionByContours SfrBC ;
|
|
// loop Esterno
|
|
pCrvEL->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
SfrBC.AddCurve( Release( pCrvEL)) ;
|
|
// isole non trascurate
|
|
for ( int nI = 0 ; nI < int( vCrvToTIsland.size()) ; ++ nI) {
|
|
vCrvToTIsland[nI]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
SfrBC.AddCurve( Release( vCrvToTIsland[nI])) ;
|
|
}
|
|
// ricavo il nuovo Chunk nC-esimo esteso presso i lati aperti
|
|
PtrOwner<ISurfFlatRegion> pNewChunk( SfrBC.GetSurf()) ;
|
|
if ( IsNull( pNewChunk) || ! pNewChunk->IsValid())
|
|
return false ;
|
|
// aggiungo il Chunk nC-esimo alla superficie finale
|
|
if ( pSrfFinal->GetChunkCount() == 0)
|
|
pSrfFinal.Set( pNewChunk) ;
|
|
else if ( ! pSrfFinal->Add( *pNewChunk))
|
|
return false ;
|
|
}
|
|
// se il Chunk c-esimo non è mai stato modificato
|
|
else {
|
|
// aggiungo il Chunk alla superficie finale
|
|
if ( pSrfFinal->GetChunkCount() == 0)
|
|
pSrfFinal.Set( pSfr->CloneChunk( nC)) ;
|
|
else if ( ! pSrfFinal->Add( *pSfr->CloneChunk( nC)))
|
|
return false ;
|
|
}
|
|
}
|
|
// se la superficie finale estesa non è valida, errore
|
|
if ( ! pSrfFinal->IsValid())
|
|
return false ;
|
|
|
|
// chiudo le isole che risultano ambigue ( quindi non omgenee)
|
|
for ( int nC = 0 ; nC < pSrfFinal->GetChunkCount() ; ++ nC) {
|
|
for ( int nL = 1 ; nL < pSrfFinal->GetLoopCount( nC) ; ++ nL) {
|
|
// controllo uniformità
|
|
int nCurrTmpProp = TEMP_PROP_INVALID ;
|
|
int nPrecTmpProp = TEMP_PROP_INVALID ;
|
|
bool bIsMixed = false ;
|
|
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSrfFinal->GetLoop( nC, nL))) ;
|
|
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
|
|
return false ;
|
|
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() && ! bIsMixed ; ++ nU) {
|
|
pCrvIsl->GetCurveTempProp( nU, nCurrTmpProp, 0) ;
|
|
bIsMixed = ( nU != 0 && nCurrTmpProp != nPrecTmpProp) ;
|
|
nPrecTmpProp = nCurrTmpProp ;
|
|
}
|
|
// se proprità non uniformi -> tutta chiusa
|
|
if ( bIsMixed) {
|
|
for ( int nU = 0 ; nU < pCrvIsl->GetCurveCount() ; ++ nU)
|
|
pSrfFinal->SetCurveTempProp( nC, nL, nU, TEMP_PROP_CLOSE_EDGE) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// restituisco la superficie estesa
|
|
pSfr->Clear() ;
|
|
pSfr->CopyFrom( pSrfFinal) ;
|
|
return ( pSfr->IsValid() && pSfr->GetChunkCount() > 0) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
OptimizedSpiralCircle( const ICurveComposite* pCrvCompo, const double dToll, double& dRad,
|
|
Point3d& ptC, bool& bIsCirlce)
|
|
{
|
|
/* restituisce il centro e il raggio di una circonfereza che approssima pCrvCompo */
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || dToll < EPS_SMALL)
|
|
return false ;
|
|
dRad = 0. ;
|
|
ptC = P_INVALID ;
|
|
bIsCirlce = false ;
|
|
|
|
// se aperta, non è un cerchio e non è piana, esco
|
|
Plane3d plPock ;
|
|
if ( ! pCrvCompo->IsClosed() || ! pCrvCompo->IsFlat( plPock))
|
|
return true ;
|
|
|
|
// creo un sistema di riferimento centrato sulla curva
|
|
Point3d ptCentroid ;
|
|
Vector3d vtN ;
|
|
Frame3d frCurr ;
|
|
if ( ! pCrvCompo->GetCentroid( ptCentroid) ||
|
|
! pCrvCompo->GetExtrusion( vtN) ||
|
|
! frCurr.Set( ptCentroid, vtN) ||
|
|
! frCurr.IsValid())
|
|
return false ;
|
|
|
|
// porto la curva nel sistema di riferimento corrente ( dopo averla clonata)
|
|
PtrOwner<ICurveComposite> pCrvLoc( pCrvCompo->Clone()) ;
|
|
if ( IsNull( pCrvLoc) || ! pCrvLoc->IsValid() || ! pCrvLoc->ToLoc( frCurr))
|
|
return false ;
|
|
|
|
// approssimo la curva locale con una PolyLine
|
|
PolyLine pL ;
|
|
if ( ! pCrvLoc->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, pL) ||
|
|
pL.GetPointNbr() == 0)
|
|
return false ;
|
|
|
|
// salvo i punti ottenuti ( il punto finale sarà contenuto due volte)
|
|
PNTVECTOR vPts ;
|
|
Point3d ptNext ;
|
|
pL.GetFirstPoint( ptNext) ;
|
|
vPts.push_back( ptNext) ;
|
|
while ( pL.GetNextPoint( ptNext)) {
|
|
vPts.push_back( Media( vPts.back(), ptNext)) ; // inserisco il punto medio
|
|
vPts.push_back( ptNext) ; // inserisco il punto successivo
|
|
}
|
|
|
|
// per ogni coppia di punti calcolo la distanza massima e minima dal centro del cerchio locale ( ORIG)
|
|
double dMaxDist = 0. ;
|
|
double dMinDist = INFINITO ;
|
|
for ( int i = 0 ; i < int( floor( 0.5 * int( vPts.size()))) ; i = i + 2) {
|
|
PtrOwner<ICurveLine> pSeg( CreateCurveLine()) ;
|
|
if ( IsNull( pSeg) || ! pSeg->Set( vPts[i], vPts[i+1]))
|
|
return false ;
|
|
DistPointCurve DPC( ORIG, *pSeg) ;
|
|
double dCurrDist = 0. ;
|
|
if ( DPC.GetDist( dCurrDist) && dCurrDist < dMinDist)
|
|
dMinDist = dCurrDist ;
|
|
dCurrDist = Dist( ORIG, vPts[i]) ;
|
|
if ( dCurrDist > dMaxDist)
|
|
dMaxDist = dCurrDist ;
|
|
}
|
|
|
|
// controllo se la distanza tra minima e massima sono simili ( in base alla tolleranza)
|
|
if ( abs( dMaxDist - dMinDist) > dToll)
|
|
return true ;
|
|
|
|
// imposto i parametri per la circonferenza
|
|
dRad = 0.5 * ( dMaxDist + dMinDist) ;
|
|
ptC = ptCentroid ;
|
|
bIsCirlce = true ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcCircleSpiral( const Point3d& ptCen, const Vector3d& vtN, double dOutRad, double dIntRad,
|
|
const PocketParams& PockParams, ICurveComposite* pMCrv)
|
|
{
|
|
// raggio della circonferenza esterna
|
|
if ( dOutRad < 10 * EPS_SMALL)
|
|
return false ;
|
|
|
|
// imposto versore estrusione sulle curve composite
|
|
pMCrv->SetExtrusion( vtN) ;
|
|
|
|
// creo e inserisco la circonferenza esterna
|
|
PtrOwner<ICurveArc> pArc( CreateCurveArc()) ;
|
|
if ( IsNull( pArc) || ! pArc->Set( ptCen, vtN, dOutRad))
|
|
return false ;
|
|
// cambio il punto iniziale se necessario
|
|
if ( PockParams.ptStart.IsValid()) {
|
|
double dUStart = 0. ;
|
|
int nFlag = 0 ;
|
|
DistPointCurve( PockParams.ptStart, *pArc).GetParamAtMinDistPoint( 0., dUStart, nFlag) ;
|
|
pArc->ChangeStartPoint( dUStart) ;
|
|
}
|
|
|
|
// creo la superificie per la Feed
|
|
PtrOwner<ISurfFlatRegion> pSrfRemoved( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfRemoved))
|
|
return false ;
|
|
ICRVCOMPOPOVECTOR vLinksDone ;
|
|
|
|
Vector3d vtDir = pArc->GetStartVersor() ;
|
|
pMCrv->AddCurve( Release( pArc)) ;
|
|
|
|
// se richiesta percorrenza invertita
|
|
if ( PockParams.bInvert)
|
|
pMCrv->Invert() ;
|
|
|
|
// se raggio esterno maggiore dell'interno
|
|
if ( dOutRad > dIntRad + 10 * EPS_SMALL) {
|
|
|
|
// aggiungo le semicirconferenze della spirale ( devono essere in numero dispari)
|
|
int nStep = int( ceil( ( dOutRad - dIntRad) / ( 0.5 * PockParams.dSideStep))) ;
|
|
if ( nStep % 2 == 0)
|
|
nStep += 1 ;
|
|
double dStep = ( dOutRad - dIntRad) / nStep ;
|
|
for ( int i = 1 ; i <= nStep ; ++ i) {
|
|
if ( ! ( i % 2 == 0))
|
|
pMCrv->AddArcTg( ptCen - vtDir * ( dOutRad - i * dStep)) ;
|
|
else
|
|
pMCrv->AddArcTg( ptCen + vtDir * ( dOutRad - i * dStep)) ;
|
|
}
|
|
|
|
// aggiungo la circonferenza interna
|
|
pMCrv->AddArcTg( ptCen + vtDir * dIntRad) ;
|
|
pMCrv->AddArcTg( ptCen - vtDir * dIntRad) ;
|
|
}
|
|
|
|
// verifico il percorso di lavoro
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// assegno la Feed
|
|
AssignFeedSpiralOpt( 0, PockParams, pMCrv) ;
|
|
|
|
// assegno proprietà di riconoscimento
|
|
pMCrv->SetTempProp( TEMP_PROP_OPT_CIRCLE, 0) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
CalcTrapezoidSpiralLocalFrame( ICurveComposite* pCrvTrap, const Vector3d& vtDir, Frame3d& frLoc)
|
|
{
|
|
// cerco i lati paralleli a vtDir
|
|
int nBaseId = -1 ;
|
|
for ( int i = 0 ; i < pCrvTrap->GetCurveCount() ; i ++) {
|
|
Vector3d vtEdge ;
|
|
pCrvTrap->GetCurve( i)->GetStartDir( vtEdge) ;
|
|
if ( AreSameOrOppositeVectorApprox( vtEdge, vtDir)) {
|
|
nBaseId = i ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( nBaseId != 0 && nBaseId != 1)
|
|
return false ;
|
|
if ( pCrvTrap->GetCurve( nBaseId)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE)
|
|
nBaseId += 2 ;
|
|
|
|
// imposto come lato iniziale per la curva uno dei lati paralleli a vtDir
|
|
pCrvTrap->ChangeStartPoint( nBaseId) ;
|
|
Point3d ptOrig ; pCrvTrap->GetStartPoint( ptOrig) ;
|
|
Vector3d vtX ; pCrvTrap->GetStartDir( vtX) ;
|
|
return frLoc.Set( ptOrig, Z_AX, vtX) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetBoxCrvOptTrap( const int nCrv, const ICurveComposite* pCrvCompo, const double dDiam, int& nType,
|
|
ICurveComposite* pCrvBox)
|
|
{
|
|
nType = - 1 ;
|
|
// prendo la curva
|
|
const ICurve* pCrvCurr = pCrvCompo->GetCurve( nCrv) ;
|
|
if ( pCrvCurr == nullptr)
|
|
return false ;
|
|
// controllo se lineare, altrimenti passo alla successiva
|
|
if ( pCrvCurr->GetType() != CRV_LINE)
|
|
return true ;
|
|
// prendo la direzione del tratto lineare
|
|
Vector3d vtDir ; pCrvCurr->GetStartDir( vtDir) ;
|
|
// prendo il punto iniziale
|
|
Point3d ptStart ; pCrvCurr->GetStartPoint( ptStart) ;
|
|
// creo il riferimento basato su questo tratto
|
|
Frame3d frTrap ; frTrap.Set( ptStart, Z_AX, vtDir) ;
|
|
if ( ! frTrap.IsValid())
|
|
return false ;
|
|
// porto la curva Compo ( clonandola) nel frame e calcolo il suo BBox
|
|
BBox3d BBox ;
|
|
PtrOwner<ICurveComposite> pCrvCompo_c( pCrvCompo->Clone()) ;
|
|
if ( IsNull( pCrvCompo_c) || ! pCrvCompo_c->IsValid() ||
|
|
! pCrvCompo_c->ToLoc( frTrap) ||
|
|
! pCrvCompo_c->GetLocalBBox( BBox))
|
|
return false ;
|
|
// controllo dimY e dimX per il box
|
|
bool bDimYOk = BBox.GetDimY() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ;
|
|
bool bDimXOk = BBox.GetDimX() < dDiam + TOL_TRAPEZOID && BBox.GetMin().y > - TOL_TRAPEZOID ;
|
|
// controllo dimensioni ammissibili
|
|
if ( ! bDimXOk && ! bDimYOk)
|
|
return true ; // se nessuna
|
|
else if ( ! bDimXOk)
|
|
nType = 1 ; // se dimY
|
|
else if ( ! bDimYOk)
|
|
nType = 0 ; // se dimX
|
|
else
|
|
nType = 2 ; // se entrambe
|
|
|
|
// creo il rettangolo del Box
|
|
pCrvBox->Clear() ;
|
|
pCrvBox->AddPoint( BBox.GetMin()) ;
|
|
pCrvBox->AddLine( BBox.GetMin() + BBox.GetDimX() * X_AX) ;
|
|
pCrvBox->AddLine( BBox.GetMax()) ;
|
|
pCrvBox->AddLine( BBox.GetMax() - BBox.GetDimX() * X_AX) ;
|
|
pCrvBox->Close() ;
|
|
|
|
// determino il punto inziale della curva
|
|
if ( nType == 0)
|
|
pCrvBox->ChangeStartPoint( 1.) ;
|
|
|
|
pCrvBox->ToGlob( frTrap) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
PreparareTrapezoidTwoBases( const ICurveComposite* pCrvCompo, const double dDiam, const int nType, int& nBase,
|
|
int& nSecondBase, bool& bOk, ICurveComposite* pCrvTrap)
|
|
{
|
|
// controllo parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() ||
|
|
nBase < 0 || nBase >= pCrvCompo->GetCurveCount() ||
|
|
nSecondBase < 0 || nSecondBase >= pCrvCompo->GetCurveCount())
|
|
return false ;
|
|
bOk = false ;
|
|
|
|
// le parti tra le due basi devono essere tutte omogenee ( o tutte aperte o tutte chiuse)
|
|
// pCrvTest0 sarà la parte destra, pCrvTest1 la parte sinistra
|
|
Point3d ptSB0, ptSB1 ,ptEB0, ptEB1 ;
|
|
PtrOwner<ICurveComposite> pCrvTest0( CloneCurveComposite( pCrvCompo)) ;
|
|
PtrOwner<ICurveComposite> pCrvTest1( CloneCurveComposite( pCrvCompo)) ;
|
|
pCrvCompo->GetCurve( nBase)->GetStartPoint( ptSB0) ;
|
|
pCrvCompo->GetCurve( nBase)->GetEndPoint( ptEB0) ;
|
|
pCrvCompo->GetCurve( nSecondBase)->GetStartPoint( ptSB1) ;
|
|
pCrvCompo->GetCurve( nSecondBase)->GetEndPoint( ptEB1) ;
|
|
pCrvTest0->ChangeStartPoint( nBase) ;
|
|
pCrvTest1->ChangeStartPoint( nSecondBase) ;
|
|
double dUTrim0, dUTrim1 ;
|
|
pCrvTest0->GetParamAtPoint( ptSB1, dUTrim0) ;
|
|
pCrvTest1->GetParamAtPoint( ptSB0, dUTrim1) ;
|
|
pCrvTest0->TrimStartEndAtParam( 1, dUTrim0) ;
|
|
pCrvTest1->TrimStartEndAtParam( 1, dUTrim1) ;
|
|
// controllo che la parte destra si uniforme per le TmpProp
|
|
for ( int u = 0 ; u < pCrvTest0->GetCurveCount() - 1 ; ++ u) {
|
|
int nPropAct, nPropSucc ;
|
|
if ( pCrvTest0->GetCurveTempProp( u, nPropAct, 0) &&
|
|
pCrvTest0->GetCurveTempProp( u + 1, nPropSucc, 0) &&
|
|
nPropAct != nPropSucc)
|
|
return true ; // se TmpProp diverse => non è un caso ottimizzato
|
|
}
|
|
// controllo che la parte sinistra sia uniforme per le TmpProp
|
|
for ( int u = 0 ; u < pCrvTest1->GetCurveCount() - 1 ; ++ u) {
|
|
int nPropAct, nPropSucc ;
|
|
if ( pCrvTest1->GetCurveTempProp( u, nPropAct, 0) &&
|
|
pCrvTest1->GetCurveTempProp( u + 1, nPropSucc, 0) &&
|
|
nPropAct != nPropSucc)
|
|
return true ; // se TmpProp diverse => non è un caso ottimizzato
|
|
}
|
|
|
|
// se lato destro aperto ( estendo il punto finale della base principale e il punto iniziale
|
|
// della base secondaria fino al lato destro del box)
|
|
bool bCopyRight = false ;
|
|
if ( pCrvTest0->GetCurve( 0)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
int nCrvRight_ref = ( nType == 0 ? 0 : 1) ;
|
|
pCrvTrap->GetCurve( nCrvRight_ref)->GetStartPoint( ptEB0) ;
|
|
pCrvTrap->GetCurve( nCrvRight_ref)->GetEndPoint( ptSB1) ;
|
|
}
|
|
// se lato destro chiuso
|
|
else
|
|
bCopyRight = true ;
|
|
|
|
// se lato sinistro aperto ( estendo il punto finale della base secondaria e il punto iniziale
|
|
// della base primaria dino al lato sinistro del box)
|
|
bool bCopyLeft = false ;
|
|
if ( pCrvTest1->GetCurve( 0)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
int nCrvLeft_ref = ( nType == 0 ? 2 : 3) ;
|
|
pCrvTrap->GetCurve( nCrvLeft_ref)->GetStartPoint( ptEB1) ;
|
|
pCrvTrap->GetCurve( nCrvLeft_ref)->GetEndPoint( ptSB0) ;
|
|
}
|
|
// se lato sinistro chiuso
|
|
else
|
|
bCopyLeft = true ;
|
|
|
|
// creo la curva da restituire
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddPoint( ptSB0) ;
|
|
pCrvTrap->AddLine( ptEB0) ;
|
|
nBase = 0 ;
|
|
if ( bCopyRight)
|
|
pCrvTrap->AddCurve( Release( pCrvTest0)) ;
|
|
else {
|
|
pCrvTrap->AddLine( ptSB1) ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, TEMP_PROP_OPEN_EDGE, 0) ; // aperta
|
|
}
|
|
nSecondBase = pCrvTrap->GetCurveCount() ;
|
|
pCrvTrap->AddLine( ptEB1) ;
|
|
if ( bCopyLeft)
|
|
pCrvTrap->AddCurve( Release( pCrvTest1)) ;
|
|
else {
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() - 1, TEMP_PROP_OPEN_EDGE, 0) ; // aperta
|
|
}
|
|
|
|
// verifico dimensione x della svuotatura nel caso tutto chiuso
|
|
if ( bCopyLeft && bCopyRight) {
|
|
double dLen0 = 0, dLen2 = 0 ;
|
|
pCrvTrap->GetCurve( nBase)->GetLength( dLen0) ;
|
|
pCrvTrap->GetCurve( nSecondBase)->GetLength( dLen2) ;
|
|
if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL) {
|
|
pCrvTrap->Clear() ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
bOk = true ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
IsForcedStepTrapezoid( const ICurveComposite* pCrvTrap, const PocketParams& PockParam,
|
|
int nBase, int nSecondBase, bool& bForced)
|
|
{
|
|
bForced = false ;
|
|
// se la curva non è valida, allora non può essere forzato
|
|
if ( pCrvTrap == nullptr || ! pCrvTrap->IsValid())
|
|
return false ;
|
|
|
|
// scorro la curva e ricavo le TempProps
|
|
array<int, 4> vnProps ;
|
|
int nClose = 0 ;
|
|
for ( int i = 0 ; i < 4 ; ++ i) {
|
|
if ( ! pCrvTrap->GetCurveTempProp( i, vnProps[i], 0))
|
|
return false ;
|
|
if ( vnProps[i] == TEMP_PROP_CLOSE_EDGE)
|
|
++ nClose ;
|
|
}
|
|
|
|
double dDiam = 2. * PockParam.dRad ;
|
|
switch ( nClose) {
|
|
// se trapezio tutto aperto, allora non è forzato
|
|
case 0 :
|
|
bForced = false ;
|
|
break ;
|
|
// se ho un lato chiuso, non è forzato
|
|
case 1 :
|
|
bForced = false ;
|
|
break ;
|
|
// se ho due lati chiusi
|
|
case 2 : {
|
|
if ( nBase < 0 || nBase > 4 || nSecondBase < 0 || nSecondBase > 4)
|
|
return false ;
|
|
// se entrambe le basi sono chiuse, è forzato
|
|
if ( vnProps[nBase] == TEMP_PROP_CLOSE_EDGE && vnProps[nSecondBase] == TEMP_PROP_CLOSE_EDGE)
|
|
bForced = true ;
|
|
// se entrambe le basi sono aperte
|
|
else if ( vnProps[nBase] == TEMP_PROP_OPEN_EDGE && vnProps[nSecondBase] == TEMP_PROP_OPEN_EDGE) {
|
|
double dLenOpen = 0. ;
|
|
for ( int i = 0 ; i < 4 ; ++ i) {
|
|
if ( i == nBase || i == nSecondBase) {
|
|
const ICurve* pCrvOpen = pCrvTrap->GetCurve( i) ;
|
|
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
|
|
return false ;
|
|
// essendo nei casi a trapezio, ho solo segmenti
|
|
pCrvOpen->GetLength( dLenOpen) ;
|
|
Vector3d vtDir ; pCrvOpen->GetStartDir( vtDir) ;
|
|
vtDir *= dLenOpen ;
|
|
const ICurve* pCrvClosePrev = pCrvTrap->GetCurve( ( i == 0 ? 3 : i - 1)) ;
|
|
if ( pCrvClosePrev == nullptr || ! pCrvClosePrev->IsValid())
|
|
return false ;
|
|
const ICurve* pCrvCloseAft = pCrvTrap->GetCurve( ( i == 3 ? 0 : i + 1)) ;
|
|
if ( pCrvCloseAft == nullptr || ! pCrvCloseAft->IsValid())
|
|
return false ;
|
|
// essendo un trapezio queste due direzioni me le aspetto parallele tra loro
|
|
Vector3d vtDirPrev ; pCrvClosePrev->GetEndDir( vtDirPrev) ;
|
|
Vector3d vtDirAft ; pCrvCloseAft->GetStartDir( vtDirAft) ;
|
|
dLenOpen = min( { OrthoCompo( vtDir, vtDirPrev).Len(), OrthoCompo( vtDir, vtDirAft).Len(), dLenOpen}) ;
|
|
break ;
|
|
}
|
|
}
|
|
bForced = ( dLenOpen < dDiam + TOL_TRAPEZOID) ;
|
|
}
|
|
// se alternate, non forzo
|
|
else
|
|
bForced = false ;
|
|
}
|
|
break ;
|
|
// se ho tre lati chiusi
|
|
case 3 : {
|
|
// diventa forzato se il lato aperto non ha una componente perpendicolare grande rispetto al chiuso precedente e successivo
|
|
double dLenOpen = 0. ;
|
|
for ( int i = 0 ; i < 4 ; ++ i) {
|
|
if ( vnProps[i] == TEMP_PROP_OPEN_EDGE) {
|
|
const ICurve* pCrvOpen = pCrvTrap->GetCurve( i) ;
|
|
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
|
|
return false ;
|
|
// essendo nei casi a trapezio, ho solo segmenti
|
|
pCrvOpen->GetLength( dLenOpen) ;
|
|
Vector3d vtDir ; pCrvOpen->GetStartDir( vtDir) ;
|
|
vtDir *= dLenOpen ;
|
|
const ICurve* pCrvClosePrev = pCrvTrap->GetCurve( ( i == 0 ? 3 : i - 1)) ;
|
|
if ( pCrvClosePrev == nullptr || ! pCrvClosePrev->IsValid())
|
|
return false ;
|
|
const ICurve* pCrvCloseAft = pCrvTrap->GetCurve( ( i == 3 ? 0 : i + 1)) ;
|
|
if ( pCrvCloseAft == nullptr || ! pCrvCloseAft->IsValid())
|
|
return false ;
|
|
// essendo un trapezio queste due direzioni me le aspetto parallele tra loro
|
|
Vector3d vtDirPrev ; pCrvClosePrev->GetEndDir( vtDirPrev) ;
|
|
Vector3d vtDirAft ; pCrvCloseAft->GetStartDir( vtDirAft) ;
|
|
dLenOpen = min( { OrthoCompo( vtDir, vtDirPrev).Len(), OrthoCompo( vtDir, vtDirAft).Len(), dLenOpen}) ;
|
|
break ;
|
|
}
|
|
}
|
|
bForced = ( dLenOpen < dDiam + TOL_TRAPEZOID) ;
|
|
}
|
|
break ;
|
|
// se tutto chiuso, è forzato
|
|
case 4 :
|
|
bForced = true ;
|
|
break ;
|
|
default :
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTrap,
|
|
Frame3d& frTrap, const PocketParams& PockParams, double& dPocketSize, int& nBase, int& nSecondBase)
|
|
{
|
|
// controllo parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// diametro reale da tenere in considerazione
|
|
double dDiam = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ;
|
|
|
|
pCrvTrap->Clear() ; // resterà vuota se il caso non è ottimizzato
|
|
nBase = -1 ;
|
|
nSecondBase = -1 ;
|
|
|
|
// se la curva è già un trapezio, non sempre devo adattarla...
|
|
Point3d pt ; Vector3d vtDir, vtB2, vtOtherDir ;
|
|
if ( pCrvCompo->IsATrapezoid( 100 * EPS_SMALL, pt, vtDir, vtOtherDir, vtB2)) {
|
|
// data la tolleranza, creo la curva a trapezio dai 4 punti
|
|
pCrvTrap->AddPoint( pt) ;
|
|
pCrvTrap->AddLine( pt + vtDir) ;
|
|
pCrvTrap->AddLine( pt + vtOtherDir + vtB2) ;
|
|
pCrvTrap->AddLine( pt + vtOtherDir) ;
|
|
pCrvTrap->Close() ;
|
|
// se parallelogramma scelgo come base i lati lunghi
|
|
Vector3d vtL2( - vtDir + vtOtherDir + vtB2) ;
|
|
if ( AreSameOrOppositeVectorApprox( vtOtherDir, vtL2)) {
|
|
if ( vtOtherDir.Len() > vtDir.Len())
|
|
swap( vtDir, vtOtherDir) ;
|
|
}
|
|
vtDir.Normalize() ;
|
|
Vector3d vtOrtho = OrthoCompo( vtOtherDir, vtDir) ;
|
|
dPocketSize = vtOrtho.Len() ;
|
|
// assegno le tempProp della curva trapezio
|
|
for ( int nU = 0 ; nU < 4 ; ++ nU) {
|
|
Vector3d vtCrvDir ; pCrvTrap->GetCurve( nU)->GetStartDir( vtCrvDir) ;
|
|
for ( int nI = 0 ; nI < pCrvCompo->GetCurveCount() ; ++ nI) {
|
|
Vector3d vtCurrCrvDir ; pCrvCompo->GetCurve( nI)->GetStartDir( vtCurrCrvDir) ;
|
|
if ( AreSameVectorEpsilon( vtCrvDir, vtCurrCrvDir, 150 * EPS_SMALL)) {
|
|
pCrvTrap->SetCurveTempProp( nU, pCrvCompo->GetCurve( nI)->GetTempProp( 0), 0) ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap))
|
|
return false ;
|
|
bool bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE ||
|
|
pCrvTrap->GetCurve( 2)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) ;
|
|
// controllo dimensioni della svuotatura
|
|
if ( ! ( bBaseOpen && dPocketSize < dDiam + TOL_TRAPEZOID) &&
|
|
abs( dPocketSize - dDiam) > EPS_SMALL + TOL_TRAPEZOID) {
|
|
pCrvTrap->Clear() ;
|
|
return true ;
|
|
}
|
|
// recupero flag aperto/chiuso dei lati
|
|
if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE &&
|
|
pCrvTrap->GetCurve( 3)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) {
|
|
double dLen0, dLen2 ;
|
|
pCrvTrap->GetCurve( 0)->GetLength( dLen0) ;
|
|
pCrvTrap->GetCurve( 2)->GetLength( dLen2) ;
|
|
if ( dLen0 < dDiam - EPS_SMALL || dLen2 < dDiam - EPS_SMALL)
|
|
pCrvTrap->Clear() ;
|
|
}
|
|
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
return true ;
|
|
}
|
|
|
|
// controllo il numero di lati chiusi e salvo i loro indici
|
|
int nClosedSide = 0 ;
|
|
INTVECTOR vIndClosedSides ;
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
|
|
int nTmpProp ;
|
|
if ( pCrvCompo->GetCurveTempProp( u, nTmpProp, 0) && nTmpProp == 0) {
|
|
++ nClosedSide ;
|
|
vIndClosedSides.push_back( u) ;
|
|
}
|
|
}
|
|
|
|
// se tutti lati aperti
|
|
switch ( nClosedSide) {
|
|
// TUTTI I LATI SONO APERTI
|
|
case 0 : {
|
|
// ricavo il box minimo della curva aperta ( passo dalla polyLine)
|
|
PolyLine PL ; Point3d ptCen ; double dWidth ;
|
|
if ( ! pCrvCompo->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, PL) ||
|
|
! PL.GetMinAreaRectangleXY( ptCen, vtDir, dWidth, dPocketSize))
|
|
return false ;
|
|
// controllo dimY ( dHeight), se troppo estesa, non è un caso ottimizzato
|
|
if ( dPocketSize > dDiam + TOL_TRAPEZOID)
|
|
return true ;
|
|
// inverto il frame attuale
|
|
frTrap.Set( ptCen, Z_AX, vtDir) ;
|
|
if ( ! frTrap.IsValid())
|
|
return false ;
|
|
// creo il rettangolo del Box
|
|
pCrvTrap->AddPoint( Point3d( - 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, - 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->AddLine( Point3d( 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->AddLine( Point3d( - 0.5 * dWidth, 0.5 * dPocketSize, 0)) ;
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->ToGlob( frTrap) ;
|
|
Point3d ptNewOrig ; pCrvTrap->GetStartPoint( ptNewOrig) ;
|
|
frTrap.Set( ptNewOrig, Z_AX, vtDir) ;
|
|
// imposto tutte le 4 curve come aperte
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, TEMP_PROP_OPEN_EDGE, 0) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
}
|
|
break ;
|
|
// 1 LATO CHIUSO ( LINEARE)
|
|
case 1 : {
|
|
int nType = -1 ;
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType != -1) {
|
|
// imposto tutte le curve come aperte tranne la prima ( estendo il solo lato chiuso come base del Box)
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u == 0 ? 0 : TEMP_PROP_OPEN_EDGE, 0) ;
|
|
// memorizzo la dimensione di svuotatura
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
}
|
|
}
|
|
break ;
|
|
// 2 LATI CHIUSI ( LINEARI, CONSECUTIVI O PARALLELI)
|
|
case 2 : {
|
|
// controllo se entrambi sono lineari
|
|
if ( pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE &&
|
|
pCrvCompo->GetCurve( vIndClosedSides[0])->GetType() == CRV_LINE) {
|
|
// se lineari, ricavo i punti estremanti e le direzioni
|
|
Point3d ptS0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartPoint( ptS0) ;
|
|
Point3d ptS1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartPoint( ptS1) ;
|
|
Point3d ptE0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetEndPoint( ptE0) ;
|
|
Point3d ptE1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetEndPoint( ptE1) ;
|
|
Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ;
|
|
Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ;
|
|
// CASO PARALLELI ( la base è indifferente)
|
|
if ( AreOppositeVectorEpsilon( vtDir0, vtDir1, 5 * EPS_SMALL)) {
|
|
int nType = -1 ;
|
|
// prendo come base il primo chiuso
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType != -1) {
|
|
// memorizzo la dimensione di svuotatura
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// controllo che la distanza tra i due chiusi sia effettivamente circa il raggio
|
|
double dDist = 0. ;
|
|
DistPointCurve DPL( ptS0, *pCrvCompo->GetCurve( vIndClosedSides[1]), false) ;
|
|
if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) {
|
|
// imposto tutte le curve di indice dispari aperte
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u % 2 == 0 ? TEMP_PROP_CLOSE_EDGE : TEMP_PROP_OPEN_EDGE, 0) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = 2 ;
|
|
}
|
|
else
|
|
pCrvTrap->Clear() ;
|
|
}
|
|
}
|
|
// CASO CONSECUTIVI
|
|
bool bOk_0_1 = AreSamePointApprox( ptS1, ptE0) ; // chiuso1 . chiuso0
|
|
bool bOk_1_0 = AreSamePointApprox( ptS0, ptE1) ; // chiuso0 . chiuso1
|
|
if ( bOk_0_1 || bOk_1_0) {
|
|
// provo con il primo lato
|
|
int nType = -1 ;
|
|
// prendo come base il primo chiuso
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[0], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType == -1) {
|
|
// provo con l'altro
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType != -1) {
|
|
// spaw tra gli indici e flags
|
|
swap( vIndClosedSides[0], vIndClosedSides[1]) ;
|
|
swap( bOk_0_1, bOk_1_0) ;
|
|
}
|
|
}
|
|
Point3d ptH1, ptH2 ;
|
|
if ( nType == 1 || nType == 2) {
|
|
double dLen1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetLength( dLen1) ;
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// i lati inclinati chiusi definiscono il Box
|
|
if ( abs( dLen1 * ( vtDir0 ^ vtDir1).Len() - dPocketSize) < TOL_TRAPEZOID) {
|
|
// creo la curva a trapezio
|
|
if ( bOk_0_1) {
|
|
pCrvTrap->GetCurve( 2)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 3)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ;
|
|
}
|
|
else {
|
|
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->AddLine( ptS1) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
pCrvTrap->SetCurveTempProp( 0, 0, 0) ;
|
|
for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ;
|
|
}
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
else
|
|
pCrvTrap->Clear() ;
|
|
}
|
|
else if ( nType == 0) {
|
|
if ( bOk_0_1) {
|
|
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->Close() ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u < 2 == 0 ? 0 : 1, 0) ;
|
|
pCrvTrap->ChangeStartPoint( 1.) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
else {
|
|
pCrvTrap->GetCurve( 0)->GetEndPoint( ptH1) ;
|
|
pCrvTrap->GetCurve( 1)->GetEndPoint( ptH2) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddLine( ptH1) ;
|
|
pCrvTrap->AddLine( ptH2) ;
|
|
pCrvTrap->AddLine( ptS1) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
pCrvTrap->SetCurveTempProp( 0, 0, 0) ;
|
|
for ( int u = 1 ; u < pCrvTrap->GetCurveCount() -1 ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, 1, 0) ;
|
|
pCrvTrap->SetCurveTempProp( pCrvTrap->GetCurveCount() -1, 0, 0) ;
|
|
pCrvTrap->ChangeStartPoint( 1.) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break ;
|
|
// 3 LATI CHIUSI ( LINEARI e CONSECUTIVI)
|
|
case 3 : {
|
|
// prendo i 3 lati chiusi
|
|
const ICurve* pCrv0 = pCrvCompo->GetCurve( vIndClosedSides[0]) ;
|
|
const ICurve* pCrv1 = pCrvCompo->GetCurve( vIndClosedSides[1]) ;
|
|
const ICurve* pCrv2 = pCrvCompo->GetCurve( vIndClosedSides[2]) ;
|
|
if ( pCrv0 == nullptr || pCrv1 == nullptr || pCrv2 == nullptr)
|
|
return false ;
|
|
// controllo che siano lineari
|
|
if ( pCrv0->GetType() == CRV_LINE && pCrv1->GetType() == CRV_LINE && pCrv2->GetType() == CRV_LINE) {
|
|
// prendo i punti iniziali e finali
|
|
Point3d ptStart0 ; pCrv0->GetStartPoint( ptStart0) ;
|
|
Point3d ptEnd0 ; pCrv0->GetEndPoint( ptEnd0) ;
|
|
Point3d ptStart1 ; pCrv1->GetStartPoint( ptStart1) ;
|
|
Point3d ptEnd1 ; pCrv1->GetEndPoint( ptEnd1) ;
|
|
Point3d ptStart2 ; pCrv2->GetStartPoint( ptStart2) ;
|
|
Point3d ptEnd2 ; pCrv2->GetEndPoint( ptEnd2) ;
|
|
|
|
// controllo che siano consecutivi e cambio l'ordine se necessario
|
|
bool bOk = true ;
|
|
if ( AreSamePointApprox( ptEnd0, ptStart1) && AreSamePointApprox( ptEnd1, ptStart2))
|
|
;
|
|
else if ( AreSamePointApprox( ptEnd1, ptStart2) && AreSamePointApprox( ptEnd2, ptStart0)) {
|
|
swap( vIndClosedSides[0], vIndClosedSides[1]) ;
|
|
swap( vIndClosedSides[1], vIndClosedSides[2]) ;
|
|
}
|
|
else if ( AreSamePointApprox( ptEnd2, ptStart0) && AreSamePointApprox( ptEnd0, ptStart1)) {
|
|
swap( vIndClosedSides[0], vIndClosedSides[2]) ;
|
|
swap( vIndClosedSides[2], vIndClosedSides[1]) ;
|
|
}
|
|
else // se non consecutivi
|
|
bOk = false ;
|
|
|
|
// se i tre lati sono ( mediante lo swap) consecutivi...
|
|
if ( bOk) {
|
|
// controllo se la dimensione Y del lato 1 è valida ( il resto è gestito di seguito)
|
|
int nType = -1 ;
|
|
if ( ! GetBoxCrvOptTrap( vIndClosedSides[1], pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
if ( nType == 1) {
|
|
// bisgna controllare la lunghezza dei chiusi
|
|
double dLen0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetLength( dLen0) ;
|
|
double dLen2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetLength( dLen2) ;
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
Vector3d vtDir0 ; pCrvCompo->GetCurve( vIndClosedSides[0])->GetStartDir( vtDir0) ;
|
|
Vector3d vtDir1 ; pCrvCompo->GetCurve( vIndClosedSides[1])->GetStartDir( vtDir1) ;
|
|
Vector3d vtDir2 ; pCrvCompo->GetCurve( vIndClosedSides[2])->GetStartDir( vtDir2) ;
|
|
// i lati inclinati chiusi definiscono il Box
|
|
if ( abs( dLen2 * ( vtDir1 ^ vtDir2).Len() - dPocketSize) < TOL_TRAPEZOID &&
|
|
abs( dLen0 * ( vtDir1 ^ vtDir0).Len() - dPocketSize) < TOL_TRAPEZOID) {
|
|
// creo la curva a trapezio
|
|
pCrvTrap->Clear() ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[0])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[1])->Clone()) ;
|
|
pCrvTrap->AddCurve( pCrvCompo->GetCurve( vIndClosedSides[2])->Clone()) ;
|
|
pCrvTrap->Close() ;
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u)
|
|
pCrvTrap->SetCurveTempProp( u, u < 3 ? 0 : 1 , 1) ;
|
|
pCrvTrap->ChangeStartPoint( 1.) ;
|
|
// imposto le basi
|
|
nBase = 0 ;
|
|
nSecondBase = pCrvTrap->GetCurveCount() - 2 ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// se non ho trovato delle basi, allora cerco in maniera generale un trapezoide ( se esiste)
|
|
if ( nBase == -1) {
|
|
bool bOK = false ;
|
|
for ( int nU = 0 ; nU < pCrvCompo->GetCurveCount() && ! bOK ; ++ nU) {
|
|
// cerco un lato chiuso ( esiste per forza)
|
|
if ( pCrvCompo->GetCurve( nU)->GetTempProp( 0) == 0) {
|
|
// controllo se il lato corrente può essere una base
|
|
int nType = -1 ;
|
|
if ( ! GetBoxCrvOptTrap( nU, pCrvCompo, dDiam, nType, pCrvTrap))
|
|
return false ;
|
|
// se è una base valida
|
|
if ( nType != -1) {
|
|
// ricavo la dimensione di svuotatura
|
|
pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ;
|
|
// ricavo la sua direzione iniziale
|
|
Vector3d vtBaseDir ; pCrvCompo->GetCurve( nU)->GetStartDir( vtBaseDir) ;
|
|
// ricavo il suo punto iniziale
|
|
Point3d ptBaseStart ; pCrvCompo->GetCurve( nU)->GetStartPoint( ptBaseStart) ;
|
|
// ricavo il suo punto finale
|
|
Point3d ptBaseEnd ; pCrvCompo->GetCurve( nU)->GetEndPoint( ptBaseEnd) ;
|
|
// cerco il lato chiuso lineare parallelo ad esso e distante circa dPocketSize
|
|
for ( int nOtherU = 0 ; nOtherU < pCrvCompo->GetCurveCount() && ! bOK ; ++ nOtherU) {
|
|
if ( nOtherU == nU ||
|
|
pCrvCompo->GetCurve( nOtherU)->GetType() != CRV_LINE ||
|
|
pCrvCompo->GetCurve( nOtherU)->GetTempProp() != 0)
|
|
continue ;
|
|
Vector3d vtSecondBaseDir ; pCrvCompo->GetCurve( nOtherU)->GetStartDir( vtSecondBaseDir) ;
|
|
if ( ! AreOppositeVectorEpsilon( vtBaseDir, vtSecondBaseDir, 5 * EPS_SMALL))
|
|
continue ;
|
|
Point3d ptSecondBaseStart ; pCrvCompo->GetCurve( nOtherU)->GetStartPoint( ptSecondBaseStart) ;
|
|
double dDist = 0. ;
|
|
DistPointCurve DPL( ptBaseStart, *pCrvCompo->GetCurve( nOtherU), false) ;
|
|
if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) {
|
|
nBase = nU ;
|
|
nSecondBase = nOtherU ;
|
|
if ( ! PreparareTrapezoidTwoBases( pCrvCompo, dDiam, nType, nBase, nSecondBase, bOK, pCrvTrap))
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( ! bOK) {
|
|
pCrvTrap->Clear() ;
|
|
nBase = -1 ;
|
|
nSecondBase = -1 ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// la curva a trapezio deve evere almeno 4 lati
|
|
if ( pCrvTrap->GetCurveCount() < 4 || nBase == -1 || nSecondBase == -1) {
|
|
pCrvTrap->Clear() ;
|
|
return true ;
|
|
}
|
|
|
|
// ricostruisco il frame del trapezio
|
|
Point3d ptS ; pCrvTrap->GetStartPoint( ptS) ;
|
|
Vector3d vtS ; pCrvTrap->GetStartDir( vtS) ;
|
|
if ( ! frTrap.Set( ptS, Z_AX, vtS) || ! frTrap.IsValid())
|
|
return false ;
|
|
|
|
// se parametro MaxOptSize non compatibile => non è ottimizzato
|
|
bool bForced = false ;
|
|
IsForcedStepTrapezoid( pCrvTrap, PockParams, nBase, nSecondBase, bForced) ;
|
|
if ( ! bForced) {
|
|
if ( PockParams.dMaxOptSize > EPS_SMALL && dPocketSize > PockParams.dMaxOptSize)
|
|
pCrvTrap->Clear() ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
SpecialAdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const bool bEvenClosed,
|
|
const ICurveComposite* pCrvPocket, const PocketParams& PockParams,
|
|
const Point3d& ptRef)
|
|
{
|
|
// parametri
|
|
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
|
|
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
|
|
double dRad = 0.5 * dDiam + dOffsR ;
|
|
|
|
// calcolo gli offset dei lati obliqui
|
|
PtrOwner<ICurveLine> pLineS( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 0 : 3)->Clone())) ;
|
|
pLineS->SimpleOffset( - dRad) ;
|
|
PtrOwner<ICurveLine> pLineE( GetCurveLine( pCrvPocket->GetCurve( bEvenClosed ? 2 : 1)->Clone())) ;
|
|
pLineE->SimpleOffset( - dRad) ;
|
|
|
|
// verifico orientamento delle curve in base al punto di riferimento ( se valido)
|
|
double dSqDistS = 0., dSqDistE = 0. ;
|
|
if ( ! ptRef.IsValid() ||
|
|
( ( DistPointCurve( ptRef, *pCrvPocket->GetCurve( bEvenClosed ? 3 : 0)).GetSqDist( dSqDistS)) &&
|
|
( DistPointCurve( ptRef, *pCrvPocket->GetCurve( bEvenClosed ? 1 : 2)).GetSqDist( dSqDistE)) &&
|
|
dSqDistS < dSqDistE)) {
|
|
pLineS->Invert() ;
|
|
pLineE->Invert() ;
|
|
}
|
|
|
|
Point3d ptS, ptE ;
|
|
pLineS->GetEndPoint( ptS) ;
|
|
pLineE->GetStartPoint( ptE) ;
|
|
Vector3d vtS, vtE ;
|
|
pLineS->GetStartDir( vtS) ;
|
|
pLineE->GetStartDir( vtE) ;
|
|
|
|
PtrOwner<ICurveLine> pLineLink( CreateCurveLine()) ;
|
|
pLineLink->Set( ptS, ptE) ;
|
|
|
|
pMCrv->Clear() ;
|
|
if ( ! pMCrv->AddCurve( Release( pLineS)))
|
|
return false ;
|
|
|
|
// creo raccordi
|
|
bool bUseBiArcs = false ;
|
|
Vector3d vtDir, vtDir2 ;
|
|
pLineLink->GetStartDir( vtDir) ;
|
|
pCrvPocket->GetCurve( bEvenClosed ? 1 : 0)->GetStartDir( vtDir2) ;
|
|
|
|
if ( Dist( ptS, ptE) > 500 * EPS_SMALL && AreSameOrOppositeVectorApprox( vtDir, vtDir2)) {
|
|
|
|
Point3d ptCrv1, ptCrv2 ;
|
|
pLineLink->GetPointD1D2( 0.3, ICurve::FROM_MINUS, ptCrv1) ;
|
|
pLineLink->GetPointD1D2( 0.7, ICurve::FROM_MINUS, ptCrv2) ;
|
|
|
|
// primo raccordo
|
|
double dAng ;
|
|
vtS.GetAngleXY( X_AX, dAng) ;
|
|
PtrOwner<ICurve> pBiArc1( GetBiArc( ptS, - dAng, ptCrv1, bEvenClosed ? 90 : 0, 0.5)) ;
|
|
|
|
// secondo raccordo
|
|
vtE.Invert() ;
|
|
vtE.GetAngleXY( X_AX, dAng) ;
|
|
PtrOwner<ICurve> pBiArc2( GetBiArc( ptE, - dAng, ptCrv2, bEvenClosed ? - 90 : 180, 0.5)) ;
|
|
pBiArc2->Invert() ;
|
|
|
|
if ( ! IsNull( pBiArc1) && ! IsNull( pBiArc2)) {
|
|
bUseBiArcs = true ;
|
|
pMCrv->AddCurve( Release( pBiArc1)) ;
|
|
pMCrv->AddLine( ptCrv2) ;
|
|
pMCrv->AddCurve( Release( pBiArc2)) ;
|
|
}
|
|
}
|
|
|
|
// se non è stato possibile creare raccordo, unisco linearmente
|
|
if ( ! bUseBiArcs)
|
|
pMCrv->AddCurve( Release( pLineLink)) ;
|
|
|
|
if ( ! pMCrv->AddCurve( Release( pLineE)))
|
|
return false ;
|
|
|
|
// setto temp prop per ricordare curve aggiuntive per pulire angoli
|
|
pMCrv->SetCurveTempProp( 0, 1) ;
|
|
pMCrv->SetCurveTempProp( pMCrv->GetCurveCount() - 1, 1) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------
|
|
static bool
|
|
CalcTrapezoidSpiralXCoord( const ICurveComposite* pCrvPocket, int nBase, int nSecondBase,
|
|
bool bStart, double dYCoord, double& dXCoord, double dPocketSize,
|
|
const PocketParams& PockParams)
|
|
{
|
|
// parametri
|
|
double dDiam = PockParams.dRad_prec > 0 ? 2 * PockParams.dRad_prec : 2 * PockParams.dRad ;
|
|
double dOffsR = PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset ;
|
|
double dRad = 0.5 * dDiam + dOffsR ;
|
|
|
|
// recupero la curva di interesse
|
|
int nCrvId = ( bStart ? nSecondBase : nBase) + 1 ;
|
|
int nProp = -1 ;
|
|
if ( ! pCrvPocket->GetCurveTempProp( nCrvId, nProp))
|
|
return false ;
|
|
// se Open
|
|
if ( nProp == TEMP_PROP_OPEN_EDGE) {
|
|
Point3d pt1, pt2 ;
|
|
pCrvPocket->GetCurve( nCrvId)->GetStartPoint( pt1) ;
|
|
pCrvPocket->GetCurve( nCrvId)->GetEndPoint( pt2) ;
|
|
if ( bStart)
|
|
dXCoord = min( pt1.x, pt2.x) ;
|
|
else
|
|
dXCoord = max( pt1.x, pt2.x) ;
|
|
}
|
|
// se Closed
|
|
else {
|
|
// creo la curva destra/sinistra
|
|
int nLast = ( bStart ? pCrvPocket->GetCurveCount() : nSecondBase) ;
|
|
PtrOwner<ICurveComposite> pCrvSide( ConvertCurveToComposite( pCrvPocket->CopyParamRange( nCrvId, nLast))) ;
|
|
if ( IsNull( pCrvSide))
|
|
return false ;
|
|
// Offsetto la curva
|
|
OffsetCurve OffsCrv ;
|
|
if ( ! OffsCrv.Make( pCrvSide, - dRad, ICurve::OFF_FILLET))
|
|
return false ;
|
|
if ( OffsCrv.GetCurveCount() == 0) {
|
|
// controllo se avevo una circonferenza
|
|
if ( pCrvSide->GetCurveCount() == 1 && pCrvSide->GetFirstCurve()->GetType() == CRV_ARC) {
|
|
Point3d ptS ; pCrvSide->GetStartPoint( ptS) ;
|
|
dXCoord = ptS.x ;
|
|
}
|
|
else {
|
|
Point3d ptS ; pCrvSide->GetStartPoint( ptS) ;
|
|
Point3d ptE ; pCrvSide->GetEndPoint( ptE) ;
|
|
dXCoord = bStart ? max( ptS.x, ptE.x) + dRad : min( ptS.x, ptE.x) - dRad ;
|
|
}
|
|
}
|
|
else if ( OffsCrv.GetCurveCount() == 1) {
|
|
// controllo se la curva interseca la linea di svuotatura a YCoord
|
|
PtrOwner<ICurve> pCrvOffs( OffsCrv.GetLongerCurve()) ;
|
|
if ( IsNull( pCrvOffs))
|
|
return false ;
|
|
PtrOwner<ICurveLine> pLineMid( CreateCurveLine()) ;
|
|
if ( IsNull( pLineMid))
|
|
return false ;
|
|
pLineMid->Set( Point3d( - 3000., dYCoord, 0.), Point3d( 3000., dYCoord, 0.)) ;
|
|
IntersCurveCurve intCC( *pLineMid, *pCrvOffs) ;
|
|
IntCrvCrvInfo ccClass ;
|
|
if ( intCC.GetIntersCount() != 0) {
|
|
// se ho almeno una intersezione
|
|
if ( intCC.GetIntCrvCrvInfo( 0, ccClass))
|
|
dXCoord = ccClass.IciA[0].ptI.x ;
|
|
else
|
|
return false ;
|
|
}
|
|
else {
|
|
// se non ho intersezioni, prendo il box della curva
|
|
BBox3d Box3d ;
|
|
pCrvSide->GetLocalBBox( Box3d) ;
|
|
// creo la linea limitie verticale
|
|
PtrOwner<ICurveLine> pCrvVertLine( CreateCurveLine()) ;
|
|
if ( IsNull( pCrvVertLine))
|
|
return false ;
|
|
if ( bStart)
|
|
pCrvVertLine->SetPDL( Box3d.GetMax() + 5 * TOL_TRAPEZOID * Y_AX, - ANG_RIGHT , 2 * dPocketSize) ;
|
|
else
|
|
pCrvVertLine->SetPDL( Box3d.GetMin() - 5 * TOL_TRAPEZOID * Y_AX, ANG_RIGHT, 2 * dPocketSize) ;
|
|
// intersezione
|
|
IntersCurveCurve intCC2( *pCrvVertLine, *pCrvSide) ;
|
|
if ( intCC2.GetOverlaps()) {
|
|
if ( bStart)
|
|
dXCoord = Box3d.GetMax().x + dRad ;
|
|
else
|
|
dXCoord = Box3d.GetMin().x - dRad ;
|
|
}
|
|
else {
|
|
dXCoord = ( bStart ? -INFINITO : INFINITO) ;
|
|
for ( int i = 0 ; i < intCC2.GetIntersCount() ; ++ i) {
|
|
IntCrvCrvInfo ccClass2 ;
|
|
if ( intCC2.GetIntCrvCrvInfo( i, ccClass2)) {
|
|
double dDiffY = ccClass2.IciA[0].ptI.y - dYCoord ;
|
|
double dOffs = sqrt( dRad * dRad - dDiffY * dDiffY) ;
|
|
if ( bStart)
|
|
dXCoord = max( dXCoord, Box3d.GetMax().x + dOffs) ;
|
|
else
|
|
dXCoord = min( dXCoord, Box3d.GetMin().x - dOffs) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
AdjustTrapezoidSpiralForAngles( ICurveComposite* pMCrv, const ICurveComposite* pCrvPocket,
|
|
const PocketParams& PockParams, bool bStart)
|
|
{
|
|
// parametri
|
|
double dDiam = ( PockParams.dRad_prec > 0 ? 2. * PockParams.dRad_prec : 2. * PockParams.dRad) ;
|
|
double dOffsR = ( PockParams.dRad_prec > 0 ? PockParams.dRadialOffset_prec : PockParams.dRadialOffset) ;
|
|
double dRad = 0.5 * dDiam + dOffsR ;
|
|
if ( ! bStart)
|
|
pMCrv->Invert() ;
|
|
|
|
// recupero la coordinata Y del tratto lineare e la dimensione della tasca a trapezio
|
|
Point3d ptTmp ;
|
|
pMCrv->GetStartPoint( ptTmp) ;
|
|
double dYCoord = ptTmp.y ;
|
|
pCrvPocket->GetCurve( 2)->GetStartPoint( ptTmp) ;
|
|
double dPocketSize = ptTmp.y ;
|
|
|
|
// offset del lato chiuso del raggio utensile, verso l'interno della tasca a trapezio
|
|
int nCrvId = ( bStart ? 3 : 1) ;
|
|
PtrOwner<ICurveLine> pLine( GetCurveLine( pCrvPocket->GetCurve( nCrvId)->Clone())) ;
|
|
pLine->SimpleOffset( - dRad) ;
|
|
|
|
// inverto gli estremi se necessario
|
|
Point3d ptP1, ptP2 ;
|
|
pLine->GetStartPoint( ptP1) ;
|
|
pLine->GetEndPoint( ptP2) ;
|
|
if ( ! bStart)
|
|
swap( ptP1, ptP2) ;
|
|
|
|
int nProp2 ;
|
|
PtrOwner<ICurveComposite> pCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompo))
|
|
return false ;
|
|
// lato opposto a quello di riferimento aperto
|
|
if ( pCrvPocket->GetCurveTempProp( 2, nProp2) && nProp2 != TEMP_PROP_CLOSE_EDGE) {
|
|
|
|
// caso 1 : pLine ha un estremo sopra e uno sotto il percorso di svuotatura pMCrv
|
|
if ( ptP2.y < dYCoord && dYCoord < ptP1.y) {
|
|
|
|
// creo tratto da ptP2 a ptP1
|
|
pCompo->AddPoint( ptP2) ;
|
|
pCompo->AddLine( ptP1) ;
|
|
|
|
// trovo il punto di pMCrv da cui ripartire per non lasciare aree residue
|
|
pLine->SimpleOffset( - 0.5 * dDiam) ;
|
|
pLine->ExtendStartByLen( EPS_SMALL) ;
|
|
pLine->ExtendEndByLen( EPS_SMALL) ;
|
|
IntersCurveCurve intCC( *pLine, *pCrvPocket) ;
|
|
if ( intCC.GetIntersCount() == 0)
|
|
return false ;
|
|
IntCrvCrvInfo aInfo ;
|
|
intCC.GetIntCrvCrvInfo( 0, aInfo) ;
|
|
double dDeltaX = sqrt( max( 0., dDiam * dDiam / 4 - dYCoord * dYCoord)) ;
|
|
if ( ! bStart)
|
|
dDeltaX *= -1 ;
|
|
Point3d ptCrv( aInfo.IciA[0].ptI.x + dDeltaX, dYCoord) ;
|
|
|
|
double dPar = 0 ;
|
|
if ( ! pMCrv->GetParamAtPoint( ptCrv, dPar)) {
|
|
dPar = 0.5 ;
|
|
pMCrv->GetPointD1D2( dPar, ICurve::FROM_MINUS, ptCrv) ;
|
|
}
|
|
if ( ! pMCrv->TrimStartAtParam( dPar))
|
|
pMCrv->Clear() ;
|
|
|
|
if ( ptP1.y > dPocketSize + 100. * EPS_SMALL) {
|
|
// se ptP1 è esterno alla svuotatura scambio i punti in modo da usare ptP2 per il biarco ( così da non farlo fuoriuscire troppo)
|
|
swap( ptP1, ptP2) ;
|
|
pCompo->Invert() ;
|
|
}
|
|
|
|
// creo biarco fra ptP1 e ptCrv
|
|
Vector3d vtDir ; pCompo->GetStartDir( vtDir) ;
|
|
double dAng ; vtDir.GetAngleXY( X_AX, dAng) ;
|
|
bool bUseBiArc = false ;
|
|
PtrOwner<ICurve> pBiArc( GetBiArc( ptP1, - dAng, ptCrv, bStart ? 0 : ANG_STRAIGHT, 0.8)) ;
|
|
if ( ! IsNull( pBiArc)) {
|
|
// verifico che con il biarco non si oltrepassi il lato obliquo chiuso della svuotatura
|
|
IntersCurveCurve intCC2( *pBiArc, *pCompo) ;
|
|
if ( intCC2.GetIntersCount() == 1)
|
|
bUseBiArc = true ;
|
|
}
|
|
if ( bUseBiArc)
|
|
pCompo->AddCurve( Release( pBiArc)) ;
|
|
else {
|
|
double dParLine = ( dYCoord - ptP2.y) / ( ptP1.y - ptP2.y) ;
|
|
Point3d ptOnLine = Media( ptP2, ptP1, dParLine) ;
|
|
pCompo->AddLine( ptOnLine) ;
|
|
pCompo->AddLine( ptCrv) ;
|
|
}
|
|
}
|
|
|
|
// caso 2 : pLine è completamente sopra/sotto la linea di svuotatura
|
|
else {
|
|
// se è sopra modifiche per ricondurmi al caso in cui è sotto
|
|
if ( ptP2.y > dYCoord) {
|
|
pLine->Invert() ;
|
|
swap( ptP1, ptP2) ;
|
|
}
|
|
|
|
// trovo l'intersezione fra il prolungamento della linea e pMCrv
|
|
if ( bStart)
|
|
pLine->ExtendStartByLen( 1000.) ;
|
|
else
|
|
pLine->ExtendEndByLen( 1000.) ;
|
|
IntersCurveCurve intCC ( *pLine, *pMCrv) ;
|
|
if ( intCC.GetIntersCount() == 0)
|
|
return false ;
|
|
IntCrvCrvInfo aInfo ;
|
|
intCC.GetIntCrvCrvInfo( 0, aInfo) ;
|
|
Point3d ptInt ;
|
|
ptInt = aInfo.IciA[0].ptI ;
|
|
double dPar = 0 ;
|
|
pMCrv->GetParamAtPoint( ptInt, dPar) ;
|
|
if ( ! pMCrv->TrimStartAtParam( dPar))
|
|
pMCrv->Clear() ;
|
|
|
|
pCompo->AddPoint( ptP2) ;
|
|
pCompo->AddLine( ptInt) ;
|
|
}
|
|
}
|
|
|
|
// se il lato opposto a quello di riferimento è chiuso bisogna distinguere i casi
|
|
else {
|
|
bool bStartPnt = false ;
|
|
if ( ptP2.y < dYCoord) {
|
|
bStartPnt = pCompo->AddPoint( ptP2) ;
|
|
}
|
|
else {
|
|
Vector3d vtDir ;
|
|
pLine->GetStartDir( vtDir) ;
|
|
if ( bStart)
|
|
vtDir.Invert() ;
|
|
Point3d ptP2N = ptP2 - dDiam / 2. * abs( vtDir.x / vtDir.y) * vtDir ;
|
|
if ( ptP2N.y < dYCoord)
|
|
bStartPnt = pCompo->AddPoint( ptP2N) ;
|
|
}
|
|
if ( bStartPnt) {
|
|
Point3d ptCrv ;
|
|
pMCrv->GetStartPoint( ptCrv) ;
|
|
pCompo->AddLine( ptCrv) ;
|
|
}
|
|
}
|
|
|
|
// setto temp prop per ricordare che è curva aggiuntiva per pulire angoli
|
|
for ( int i = 0 ; i < pCompo->GetCurveCount() ; ++ i)
|
|
pCompo->SetCurveTempProp( i, TEMP_PROP_OPEN_EDGE) ;
|
|
|
|
pMCrv->AddCurve( Release( pCompo), false) ;
|
|
// se richiesto ripristino la direzione originaria
|
|
if ( ! bStart)
|
|
pMCrv->Invert() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
static bool
|
|
CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double dPocketSize, int nBase,
|
|
int nSecondBase, const PocketParams& PockParams, ICurveComposite* pMCrv,
|
|
bool& bOptimizedTrap)
|
|
{
|
|
// parametri
|
|
double dDiam = 2 * PockParams.dRad ;
|
|
double dOffsR = PockParams.dRadialOffset ;
|
|
bOptimizedTrap = false ;
|
|
Vector3d vtExtr ; pCrvPocket->GetExtrusion( vtExtr) ;
|
|
|
|
// recupero le temp prop
|
|
INTVECTOR vnProp( 4, 0) ;
|
|
if ( pCrvPocket->GetCurveCount() == 4) {
|
|
for ( int i = 0 ; i < 4 ; i ++)
|
|
pCrvPocket->GetCurveTempProp( i, vnProp[i]) ;
|
|
}
|
|
else {
|
|
vnProp[1] = pCrvPocket->GetCurve( 1)->GetTempProp() ;
|
|
vnProp[3] = pCrvPocket->GetCurve( nSecondBase + 1)->GetTempProp() ;
|
|
}
|
|
|
|
// passo in un sistema di riferimento locale avente asse X allineato con uno dei due lati paralleli
|
|
// (possibilmente aperto) e centro nel punto iniziale del lato
|
|
pCrvPocket->ToLoc( frTrap) ;
|
|
Point3d ptRef = P_INVALID ;
|
|
if ( PockParams.ptStart.IsValid())
|
|
ptRef = GetToLoc( PockParams.ptStart, frTrap) ;
|
|
|
|
// calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso)
|
|
double dLen0, dLen1, dLen2, dLen3, dMaxLarg = 0. ;
|
|
pCrvPocket->GetCurve( nBase)->GetLength( dLen0) ;
|
|
pCrvPocket->GetCurve( nSecondBase)->GetLength( dLen2) ;
|
|
bool bRealTrap = ( pCrvPocket->GetCurveCount() == 4) ;
|
|
for ( int i = 0 ; i < 4 && bRealTrap ; ++ i)
|
|
bRealTrap = ( pCrvPocket->GetCurve( i)->GetType() == CRV_LINE) ;
|
|
if ( bRealTrap) {
|
|
pCrvPocket->GetCurve( nBase + 1)->GetLength( dLen1) ;
|
|
pCrvPocket->GetCurve( nSecondBase + 1)->GetLength( dLen3) ;
|
|
// calcolo la larghezza massima della svuotatura (riferimento con X parallelo a primo lato chiuso)
|
|
Point3d ptOrig ; pCrvPocket->GetCurve( 1)->GetStartPoint( ptOrig) ;
|
|
Vector3d vtX ; pCrvPocket->GetCurve( 1)->GetStartDir( vtX) ;
|
|
Frame3d frDim ; frDim.Set( ptOrig, Z_AX, vtX) ; frDim.Invert() ;
|
|
BBox3d b3Dim ; pCrvPocket->GetBBox( frDim, b3Dim, BBF_EXACT) ;
|
|
dMaxLarg = ( vnProp[0] != TEMP_PROP_CLOSE_EDGE ? b3Dim.GetDimY() : dPocketSize) ;
|
|
}
|
|
|
|
// calcolo percorso di svuotatura
|
|
// se lati obliqui sono entrambi chiusi e dimensione svuotatura è maggiore di diametro fresa e minore del doppio gestione speciale
|
|
if ( ( bRealTrap && dMaxLarg > dDiam + TOL_TRAPEZOID) &&
|
|
( ( ( vnProp[0] != TEMP_PROP_CLOSE_EDGE && vnProp[2] != TEMP_PROP_CLOSE_EDGE) &&
|
|
( vnProp[3] == TEMP_PROP_CLOSE_EDGE && vnProp[1] == TEMP_PROP_CLOSE_EDGE) &&
|
|
( max( dLen0, dLen2) < 2. * dDiam + TOL_TRAPEZOID)) ||
|
|
( ( vnProp[1] != TEMP_PROP_CLOSE_EDGE && vnProp[3] != TEMP_PROP_CLOSE_EDGE) &&
|
|
( vnProp[0] == TEMP_PROP_CLOSE_EDGE && vnProp[2] == TEMP_PROP_CLOSE_EDGE) &&
|
|
( max( dLen0, dLen2) < 2. * dDiam + TOL_TRAPEZOID)))) {
|
|
if ( ! SpecialAdjustTrapezoidSpiralForAngles( pMCrv, ( vnProp[0] == TEMP_PROP_CLOSE_EDGE), pCrvPocket, PockParams, ptRef)) {
|
|
pMCrv->Clear() ;
|
|
return false ;
|
|
}
|
|
}
|
|
else {
|
|
// trovo la quota Y per centro del Tool
|
|
double dYCoord ;
|
|
// se base principale chiusa
|
|
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
|
|
dYCoord = 0.5 * dDiam + dOffsR ;
|
|
// se base principale aperta e secondaria chiusa
|
|
else if ( pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
|
|
dYCoord = dPocketSize - 0.5 * dDiam - dOffsR ;
|
|
// se entrambi i lati paralleli sono aperti mi posiziono a metà della svuotatura
|
|
else
|
|
dYCoord = 0.5 * dPocketSize ;
|
|
|
|
// determino le quote coordinate in X inziale e finale
|
|
double dXCoordStart = -INFINITO ;
|
|
double dXCoordEnd = INFINITO ;
|
|
if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, true, dYCoord, dXCoordStart, dPocketSize, PockParams) ||
|
|
dXCoordStart < - INFINITO + 1)
|
|
return false ;
|
|
if ( ! CalcTrapezoidSpiralXCoord( pCrvPocket, nBase, nSecondBase, false, dYCoord, dXCoordEnd, dPocketSize, PockParams) ||
|
|
dXCoordEnd > INFINITO - 1)
|
|
return false ;
|
|
if ( dXCoordStart > dXCoordEnd + 500 * EPS_SMALL)
|
|
return false ;
|
|
|
|
// determino se devo invertire la curva dato il punto di riferimento
|
|
bool bInvertByRef = false ;
|
|
if ( ptRef.IsValid()) {
|
|
PtrOwner<ICurve> pCrvRight( pCrvPocket->CopyParamRange( nBase + 1, nSecondBase)) ;
|
|
PtrOwner<ICurve> pCrvLeft( pCrvPocket->CopyParamRange( nSecondBase + 1, nBase)) ;
|
|
if ( IsNull( pCrvRight) || IsNull( pCrvLeft) ||
|
|
! pCrvRight->IsValid() || ! pCrvLeft->IsValid())
|
|
return false ;
|
|
double dSqDistBase = INFINITO, dSqDistSecondBase = INFINITO, dSqDistLeft = INFINITO, dSqDistRight = INFINITO ;
|
|
DistPointCurve( ptRef, *pCrvPocket->GetCurve( nBase)).GetSqDist( dSqDistBase) ;
|
|
DistPointCurve( ptRef, *pCrvPocket->GetCurve( nSecondBase)).GetSqDist( dSqDistSecondBase) ;
|
|
DistPointCurve( ptRef, *pCrvRight).GetSqDist( dSqDistRight) ;
|
|
DistPointCurve( ptRef, *pCrvLeft).GetSqDist( dSqDistLeft) ;
|
|
bInvertByRef = ( dSqDistSecondBase < min( {dSqDistBase, dSqDistLeft, dSqDistRight}) ||
|
|
dSqDistRight < min( {dSqDistBase, dSqDistSecondBase, dSqDistLeft})) ;
|
|
}
|
|
|
|
// determino gli estremi del segmento di svuotatura
|
|
Point3d ptStart( dXCoordStart, dYCoord) ;
|
|
Point3d ptEnd( dXCoordEnd, dYCoord) ;
|
|
|
|
// verifico se il punto iniziale e finale coincidono
|
|
if ( bRealTrap && AreSamePointEpsilon( ptStart, ptEnd, 500 * EPS_SMALL) &&
|
|
( vnProp[0] != TEMP_PROP_CLOSE_EDGE || vnProp[2] != TEMP_PROP_CLOSE_EDGE)) {
|
|
Vector3d vtDir1, vtDir3 ;
|
|
pCrvPocket->GetCurve( 1)->GetStartDir( vtDir1) ;
|
|
pCrvPocket->GetCurve( 3)->GetStartDir( vtDir3) ;
|
|
// gestisco il caso speciale di un parallelogramma in cui anche l'altra dimensione della svuotatura è pari al diametro utensile
|
|
if ( AreOppositeVectorApprox( vtDir1, vtDir3)) {
|
|
PtrOwner<ICurveLine> pLine1( GetCurveLine( pCrvPocket->GetCurve( 1)->Clone())) ;
|
|
PtrOwner<ICurveLine> pLine3( GetCurveLine( pCrvPocket->GetCurve( 3)->Clone())) ;
|
|
if ( IsNull( pLine1) || IsNull( pLine3))
|
|
return false ;
|
|
if ( ! pLine1->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset) ||
|
|
! pLine3->SimpleOffset( - PockParams.dRad - PockParams.dRadialOffset))
|
|
return false ;
|
|
Point3d ptS, ptE ;
|
|
if ( vtDir3 * X_AX > EPS_SMALL) {
|
|
pLine1->GetStartPoint( ptS) ;
|
|
pLine3->GetStartPoint( ptE) ;
|
|
}
|
|
else {
|
|
pLine1->GetEndPoint( ptE) ;
|
|
pLine3->GetEndPoint( ptS) ;
|
|
}
|
|
if ( vnProp[0] != TEMP_PROP_CLOSE_EDGE) {
|
|
pMCrv->AddPoint( ptS) ;
|
|
if ( vnProp[2] != TEMP_PROP_CLOSE_EDGE)
|
|
pMCrv->AddLine( ptE) ;
|
|
else
|
|
pMCrv->AddLine( ptStart) ;
|
|
}
|
|
else {
|
|
pMCrv->AddPoint( ptE) ;
|
|
if ( vnProp[0] != TEMP_PROP_CLOSE_EDGE)
|
|
pMCrv->AddLine( ptS) ;
|
|
else
|
|
pMCrv->AddLine( ptStart) ;
|
|
pMCrv->Invert() ;
|
|
}
|
|
pMCrv->SetCurveTempProp( 0, TEMP_PROP_OPEN_EDGE) ;
|
|
// verifico se invertirla nel caso di un punto di riferimento
|
|
if ( bInvertByRef &&
|
|
vnProp[0] != TEMP_PROP_CLOSE_EDGE && vnProp[2] != TEMP_PROP_CLOSE_EDGE)
|
|
pMCrv->Invert() ;
|
|
}
|
|
}
|
|
// se estremi del segmento differenti, allora il percorso è definito
|
|
else {
|
|
// creo la linea
|
|
if ( ! pMCrv->AddPoint( ptStart))
|
|
return true ;
|
|
if ( ! pMCrv->AddLine( ptEnd))
|
|
return true ;
|
|
// aggiustamenti al percorso per rimuovere materiale residuo negli angoli
|
|
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE ||
|
|
pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
Frame3d frOpen ;
|
|
bool bSwitch = false ;
|
|
// Base Principale chiusa e Base Secondaria aperta o inversione per punto di riferimento
|
|
if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE ||
|
|
bInvertByRef) {
|
|
pCrvPocket->ChangeStartPoint( nSecondBase) ;
|
|
// oriento il Frame ( ho sempre l'origine nell'estremo superiore del lato aperto)
|
|
Vector3d vtDir ; pCrvPocket->GetStartDir( vtDir) ;
|
|
Point3d ptOpen ;
|
|
if ( vtDir.y > 0)
|
|
pCrvPocket->GetFirstCurve()->GetEndPoint( ptOpen) ;
|
|
else
|
|
pCrvPocket->GetFirstCurve()->GetStartPoint( ptOpen) ;
|
|
frOpen.Set( ptOpen, Z_AX, -X_AX) ;
|
|
if ( ! frOpen.IsValid())
|
|
return false ;
|
|
pCrvPocket->ToLoc( frOpen) ;
|
|
pMCrv->ToLoc( frOpen) ;
|
|
pMCrv->Invert() ;
|
|
bSwitch = true ;
|
|
}
|
|
// la Base principale è sempre aperta
|
|
if ( pCrvPocket->GetCurve( 3)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE &&
|
|
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, true)) {
|
|
pMCrv->Clear() ;
|
|
return false ;
|
|
}
|
|
if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE &&
|
|
! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, false)) {
|
|
pMCrv->Clear() ;
|
|
return false ;
|
|
}
|
|
|
|
if ( bSwitch) {
|
|
pCrvPocket->ToGlob( frOpen) ;
|
|
pMCrv->ToGlob( frOpen) ;
|
|
pMCrv->Invert() ;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
return true ;
|
|
pMCrv->ToGlob( frTrap) ;
|
|
|
|
// segno i lati aperti come temp prop della curva
|
|
int nOpenEdges = vnProp[0] + vnProp[1] * 2 + vnProp[2] * 4 + vnProp[3] * 8 ;
|
|
pMCrv->SetTempProp( nOpenEdges, 0) ;
|
|
pMCrv->SetExtrusion( vtExtr) ;
|
|
|
|
bOptimizedTrap = true ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustTrapeziodLeadIn( ICurveComposite* pCrvRes, const PocketParams& PockParam,
|
|
const ISurfFlatRegion* pSfrChunk)
|
|
{
|
|
// recupero la TempProp
|
|
int nTmpProp = pCrvRes->GetTempProp( 0) ;
|
|
// se esiste almeno un aperto
|
|
if ( nTmpProp > 0) {
|
|
// se solo lato3 aperto
|
|
if ( nTmpProp == 2)
|
|
pCrvRes->Invert() ; // entro dall'unico aperto
|
|
bool bCheckHead = ( nTmpProp != 8 && nTmpProp != 2) ;
|
|
if ( bCheckHead) {
|
|
// recupero gli estremi della curva corrente e la inverto in base alla Testa
|
|
Point3d ptS ; pCrvRes->GetStartPoint( ptS) ;
|
|
Point3d ptE ; pCrvRes->GetEndPoint( ptE) ;
|
|
Point3d ptSGlob = GetToGlob( ptS, PockParam.frLocXY) ;
|
|
Point3d ptEGlob = GetToGlob( ptE, PockParam.frLocXY) ;
|
|
if ( ( PockParam.bAboveHead && ptEGlob.z > ptSGlob.z) ||
|
|
( ! PockParam.bAboveHead && ptEGlob.z < ptSGlob.z))
|
|
pCrvRes->Invert() ;
|
|
}
|
|
}
|
|
else if ( ! PockParam.bConventionalMilling) { // per sgrossature
|
|
Point3d ptS ; pCrvRes->GetStartPoint( ptS) ;
|
|
Point3d ptE ; pCrvRes->GetEndPoint( ptE) ;
|
|
if ( SqDist( ptE, PockParam.ptEnd) < SqDist( ptE, PockParam.ptEnd) + EPS_SMALL)
|
|
pCrvRes->Invert() ;
|
|
}
|
|
|
|
// Assegno la Feed
|
|
AssignFeedSpiralOpt( 1, PockParam, pCrvRes) ;
|
|
// Se curva da invertire, inverto
|
|
if ( PockParam.bInvert)
|
|
pCrvRes->Invert() ;
|
|
|
|
// se esiste almeno un aperto, provo ad estendere il percorso
|
|
if ( nTmpProp > 0) {
|
|
// Calcolo eventuale entrata da fuori
|
|
Vector3d vtRef ; pCrvRes->GetStartDir( vtRef) ;
|
|
vtRef.Invert() ;
|
|
bool bIsStartExtended = false ;
|
|
if ( ! ExtendPath( pCrvRes, pSfrChunk, PockParam, vtRef, false, GetExtendPathLen( PockParam), bIsStartExtended))
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetZigZagOptimizedCurves( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParam,
|
|
ICurveComposite* pCrvRes)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
pCrvRes->Clear() ;
|
|
|
|
// ricavo la curva di bordo del chunk corrente
|
|
PtrOwner<ICurveComposite> pCrvBorder( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid())
|
|
return false ;
|
|
pCrvBorder->MergeCurves( 10. * EPS_SMALL, 10. * EPS_ANG_SMALL, true, true) ;
|
|
pCrvBorder->SetExtrusion( pSfrChunk->GetNormVersor()) ;
|
|
|
|
/* TRAPEZI
|
|
- E' richiesto che una dimensione del box della curva sia compatibile con il primo Offset, il
|
|
quale sarebbe una singola curva aperta
|
|
*/
|
|
PtrOwner<ICurveComposite> pCrvTrap( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvTrap))
|
|
return false ;
|
|
Frame3d frTrap ;
|
|
double dPocketSize ;
|
|
int nBase, nSecondBase ;
|
|
bool bOkTrap = GetTrapezoidFromShape( pCrvBorder, pCrvTrap, frTrap, PockParam, dPocketSize, nBase, nSecondBase) ;
|
|
if ( bOkTrap && pCrvTrap->IsValid()) {
|
|
// verifico se il trapezio ottenuto deve o meno rispettare il SideStep
|
|
bool bForcedTrap = false ;
|
|
IsForcedStepTrapezoid( pCrvTrap, PockParam, nBase, nSecondBase, bForcedTrap) ;
|
|
if ( ! bForcedTrap)
|
|
bOkTrap = ( dPocketSize < PockParam.dMaxOptSize + 10. * EPS_SMALL) ;
|
|
}
|
|
if ( bOkTrap && pCrvTrap->IsValid()) {
|
|
pCrvTrap->SetExtrusion( Z_AX) ;
|
|
CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParam, pCrvRes, bOkTrap) ;
|
|
if ( bOkTrap) {
|
|
// verifico che tale curva non interferisca con la regione limite
|
|
if ( PockParam.SfrLimit.IsValid()) {
|
|
double dOffsCheck = PockParam.dRad + PockParam.dRadialOffset - 50. * EPS_SMALL ; // restrittivo per sicurezza
|
|
PtrOwner<ISurfFlatRegion> pSfrToolShape( GetSurfFlatRegionFromFatCurve( pCrvRes->Clone(), dOffsCheck, false, false)) ;
|
|
bOkTrap = ( ! IsNull( pSfrToolShape) && pSfrToolShape->IsValid()) ;
|
|
if ( bOkTrap) {
|
|
bOkTrap = ( pSfrToolShape->Intersect( PockParam.SfrLimit) &&
|
|
! pSfrToolShape->IsValid()) ;
|
|
if ( ! bOkTrap)
|
|
pCrvRes->Clear() ;
|
|
}
|
|
}
|
|
if ( bOkTrap) {
|
|
AdjustTrapeziodLeadIn( pCrvRes, PockParam, pSfrChunk) ;
|
|
// imposto il flag di curva singola
|
|
pCrvRes->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetSpiralOptimizedCurves( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParam,
|
|
ICurveComposite* pCrvRes)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
pCrvRes->Clear() ;
|
|
|
|
// ricavo la curva di bordo del chunk corrente
|
|
PtrOwner<ICurveComposite> pCrvBorder( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid())
|
|
return false ;
|
|
pCrvBorder->MergeCurves( 10. * EPS_SMALL, 10. * EPS_ANG_SMALL, true, true) ;
|
|
pCrvBorder->SetExtrusion( pSfrChunk->GetNormVersor()) ;
|
|
|
|
/* SPIRALE EUCLIDEA
|
|
- E' richiesto che la curva di bordo sia una circonferenza tutta OPEN o tutta CLOSED
|
|
*/
|
|
bool bOkSpiral = true ;
|
|
int nTempPropRef = pCrvBorder->GetCurve( 0)->GetTempProp( 0) ;
|
|
for ( int nU = 1 ; nU < pCrvBorder->GetCurveCount() && bOkSpiral ; ++ nU)
|
|
bOkSpiral = ( pCrvBorder->GetCurve( nU)->GetTempProp( 0) == nTempPropRef) ;
|
|
if ( bOkSpiral) {
|
|
// controllo che sia una circonferenza
|
|
Point3d ptCen ; double dRad ;
|
|
if ( ! OptimizedSpiralCircle( pCrvBorder, 50. * EPS_SMALL, dRad, ptCen, bOkSpiral))
|
|
return false ;
|
|
// se è una circonferenza
|
|
if ( bOkSpiral) {
|
|
double dOffs = PockParam.dRad + PockParam.dRadialOffset ;
|
|
// se curva tutta Open
|
|
if ( nTempPropRef == TEMP_PROP_OPEN_EDGE) {
|
|
// ingrandisco il raggio
|
|
dRad += 1.05 * PockParam.dRad + PockParam.dRadialOffset ;
|
|
// verifico che l'utensile ora non interferisca con la regione limite
|
|
if ( PockParam.SfrLimit.IsValid()) {
|
|
// definisco il nuovo bordo
|
|
PtrOwner<ICurveArc> pCrvArc( CreateCurveArc()) ;
|
|
bOkSpiral = ( ! IsNull( pCrvArc) &&
|
|
pCrvArc->Set( ptCen, Z_AX, dRad + PockParam.dRad)) ;
|
|
if ( bOkSpiral) {
|
|
CRVCVECTOR ccClass ;
|
|
bOkSpiral = ( PockParam.SfrLimit.GetCurveClassification( *pCrvArc, EPS_SMALL, ccClass) &&
|
|
ssize( ccClass) == 1 && ccClass[0].nClass == CRVC_OUT) ;
|
|
// NB. una versione più complessa dovrebbe verificare se la sottrazione tra la
|
|
// superficie dell'utensile e la regione limite non genera un'altra circonferenza...
|
|
// In questo caso la sottrazione potrebbe essere trattata come una circonferenza
|
|
// chiusa ed essere ancora svotata a spirale...
|
|
}
|
|
}
|
|
}
|
|
// se curva chiusa, controllo che il raggio sia compatibile con il primo Offset
|
|
else
|
|
bOkSpiral = ( dRad - dOffs > 10. * EPS_SMALL) ;
|
|
if ( bOkSpiral) {
|
|
double dIntRad = 0 ;
|
|
if ( PockParam.nType == POCKET_SPIRALOUT && PockParam.nLiType == LEAD_IN_HELIX)
|
|
dIntRad = min( 0.5 * min( PockParam.dLiTang, 2. * PockParam.dRad), dRad - dOffs) ;
|
|
bool bOkSpiral = CalcCircleSpiral( ptCen, pSfrChunk->GetNormVersor(), dRad - dOffs, dIntRad, PockParam, pCrvRes) ;
|
|
if ( bOkSpiral && pCrvRes->IsValid() && pCrvRes->GetCurveCount() > 0) {
|
|
// se curva di bordo OPEN e lavorazione SpiralIn, imposto i parametri per LeadIn
|
|
if ( nTempPropRef == TEMP_PROP_OPEN_EDGE && PockParam.nType == POCKET_SPIRALIN) {
|
|
Vector3d vtMidOut ;
|
|
pCrvRes->GetStartDir( vtMidOut) ;
|
|
vtMidOut.Rotate( pSfrChunk->GetNormVersor(), PockParam.bInvert ? ANG_RIGHT : - ANG_RIGHT) ;
|
|
bool bIsExtended ;
|
|
ExtendPath( pCrvRes, pSfrChunk, PockParam, vtMidOut, false, GetExtendPathLen( PockParam), bIsExtended) ;
|
|
}
|
|
// se invece lavorazione in SpiralOut, inverto il percorso
|
|
else if ( PockParam.nType == POCKET_SPIRALOUT)
|
|
pCrvRes->Invert() ;
|
|
return true ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* TRAPEZI
|
|
- E' richiesto che una dimensione del box della curva sia compatibile con il primo Offset, il
|
|
quale sarebbe una singola curva aperta
|
|
*/
|
|
PtrOwner<ICurveComposite> pCrvTrap( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvTrap))
|
|
return false ;
|
|
Frame3d frTrap ;
|
|
double dPocketSize ;
|
|
int nBase, nSecondBase ;
|
|
bool bOkTrap = GetTrapezoidFromShape( pCrvBorder, pCrvTrap, frTrap, PockParam, dPocketSize, nBase, nSecondBase) ;
|
|
if ( bOkTrap && pCrvTrap->IsValid()) {
|
|
// verifico se il trapezio ottenuto deve o meno rispettare il SideStep
|
|
bool bForcedTrap = false ;
|
|
IsForcedStepTrapezoid( pCrvTrap, PockParam, nBase, nSecondBase, bForcedTrap) ;
|
|
if ( ! bForcedTrap)
|
|
bOkTrap = ( dPocketSize < PockParam.dMaxOptSize + 10. * EPS_SMALL) ;
|
|
}
|
|
if ( bOkTrap && pCrvTrap->IsValid()) {
|
|
pCrvTrap->SetExtrusion( Z_AX) ;
|
|
CalcTrapezoidSpiral( pCrvTrap, frTrap, dPocketSize, nBase, nSecondBase, PockParam, pCrvRes, bOkTrap) ;
|
|
if ( bOkTrap) {
|
|
// verifico che tale curva non interferisca con la regione limite
|
|
if ( PockParam.SfrLimit.IsValid()) {
|
|
double dOffsCheck = PockParam.dRad + PockParam.dRadialOffset - 50. * EPS_SMALL ; // restrittivo per sicurezza
|
|
PtrOwner<ISurfFlatRegion> pSfrToolShape( GetSurfFlatRegionFromFatCurve( pCrvRes->Clone(), dOffsCheck, false, false)) ;
|
|
bOkTrap = ( ! IsNull( pSfrToolShape) && pSfrToolShape->IsValid()) ;
|
|
if ( bOkTrap) {
|
|
bOkTrap = ( pSfrToolShape->Intersect( PockParam.SfrLimit) &&
|
|
! pSfrToolShape->IsValid()) ;
|
|
if ( ! bOkTrap)
|
|
pCrvRes->Clear() ;
|
|
}
|
|
}
|
|
if ( bOkTrap) {
|
|
AdjustTrapeziodLeadIn( pCrvRes, PockParam, pSfrChunk) ;
|
|
// imposto il flag di curva singola
|
|
pCrvRes->SetTempProp( TEMP_PROP_OPT_TRAPEZOID, 0) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPocketingOptimizedCurves( ISurfFlatRegion* pSfr, const PocketParams& PockParam,
|
|
ICRVCOMPOPOVECTOR& vCrvOptCurves)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
vCrvOptCurves.clear() ;
|
|
|
|
// se non sono richiesti i casi ottimizzati esco
|
|
if ( PockParam.bAvoidOpt)
|
|
return true ;
|
|
|
|
// scorro i Chunk della superficie
|
|
int nChunks = pSfr->GetChunkCount() ;
|
|
int nCurrChunk = 0 ;
|
|
for ( int i = 0 ; i < nChunks ; ++ i) {
|
|
|
|
// recupero il Chunk corrente
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( nCurrChunk)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
|
|
// se il Chunk ha isole, non ho casi ottimizzati
|
|
if ( pSfrChunk->GetLoopCount( 0) > 1) {
|
|
++ nCurrChunk ;
|
|
continue ;
|
|
}
|
|
|
|
// ricavo le curve ottimizzate a seconda della lavorazione richiesta
|
|
if ( PockParam.nType == POCKET_SPIRALIN || PockParam.nType == POCKET_SPIRALOUT ||
|
|
PockParam.nType == POCKET_CONFORMAL_ZIGZAG || PockParam.nType == POCKET_CONFORMAL_ONEWAY) {
|
|
// curva da resituire
|
|
PtrOwner<ICurveComposite> pCrvOptSpiral( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvOptSpiral) ||
|
|
! GetSpiralOptimizedCurves( pSfrChunk, PockParam, pCrvOptSpiral))
|
|
return false ;
|
|
// se ho ricavato una curva ottimizzata
|
|
if ( ! IsNull( pCrvOptSpiral) && pCrvOptSpiral->IsValid() && pCrvOptSpiral->GetCurveCount() > 0) {
|
|
vCrvOptCurves.emplace_back( Release( pCrvOptSpiral)) ;
|
|
pSfr->EraseChunk( nCurrChunk) ;
|
|
}
|
|
else
|
|
++ nCurrChunk ;
|
|
}
|
|
else if ( PockParam.nType == POCKET_ZIGZAG || PockParam.nType == POCKET_ONEWAY) {
|
|
// curva da restituire
|
|
PtrOwner<ICurveComposite> pCrvOptZigZag( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvOptZigZag) ||
|
|
! GetZigZagOptimizedCurves( pSfrChunk, PockParam, pCrvOptZigZag))
|
|
return false ;
|
|
// se ho ricavato una curva ottimizzata
|
|
if ( ! IsNull( pCrvOptZigZag) && pCrvOptZigZag->IsValid() && pCrvOptZigZag->GetCurveCount() > 0) {
|
|
vCrvOptCurves.emplace_back( Release( pCrvOptZigZag)) ;
|
|
pSfr->EraseChunk( nCurrChunk) ;
|
|
}
|
|
else
|
|
++ nCurrChunk ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
// ------------- SCELTA DEL PUNTO INIZIALE -----------------------------------
|
|
// ***************************************************************************
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetParamOnOpenCurve( const ICurveComposite* pCompo, const PocketParams& PockParams, const Point3d& ptEndPrec, bool& bOutStart,
|
|
Point3d& ptStart, Vector3d& vtOut)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCompo == nullptr || ! pCompo->IsValid())
|
|
return false ;
|
|
|
|
// verifico se tutti i lati sono aperti
|
|
bool bAllOpen = true ;
|
|
const ICurve* pMyCrv = pCompo->GetFirstCurve() ;
|
|
while ( pMyCrv != nullptr) {
|
|
if ( pMyCrv->GetTempProp() != TEMP_PROP_OPEN_EDGE) {
|
|
bAllOpen = false ;
|
|
break ;
|
|
}
|
|
pMyCrv = pCompo->GetNextCurve() ;
|
|
}
|
|
|
|
// se ho un punto di riferimento, cambio il punto iniziale della curva, partendo da questa
|
|
PtrOwner<ICurveComposite> pMyCompo( CloneCurveComposite( pCompo)) ;
|
|
if ( IsNull( pMyCompo) || ! pMyCompo->IsValid())
|
|
return false ;
|
|
if ( ptEndPrec.IsValid()) {
|
|
double dMinDistPar = 0. ;
|
|
int nFlag = 0 ;
|
|
DistPointCurve( ptEndPrec, *pMyCompo).GetParamAtMinDistPoint( 0., dMinDistPar, nFlag) ;
|
|
if ( dMinDistPar > EPS_PARAM)
|
|
pMyCompo->ChangeStartPoint( floor( dMinDistPar)) ;
|
|
}
|
|
|
|
// richiedo lunghezza superiore a diametro utensile più doppio offset radiale
|
|
double dRefLen = ( bAllOpen ? 0. : ( 2 * PockParams.dRad) + 2 * PockParams.dRadialOffset - EPS_SMALL) ;
|
|
double dMaxLen = dRefLen ;
|
|
// ciclo sulle singole curve
|
|
bool bFound = false, bFirst = true ;
|
|
const ICurve* pPrevCrv = pMyCompo->GetLastCurve() ;
|
|
double dLenPrev = 0. ;
|
|
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE)
|
|
pPrevCrv->GetLength( dLenPrev) ;
|
|
const ICurve* pCrv = pMyCompo->GetFirstCurve() ;
|
|
while ( pCrv != nullptr) {
|
|
// analizzo la curva successiva
|
|
const ICurve* pNextCrv = pMyCompo->GetNextCurve() ;
|
|
bool bNextOk = ( pNextCrv != nullptr) ;
|
|
if ( ! bNextOk)
|
|
pNextCrv = pMyCompo->GetFirstCurve() ;
|
|
double dLenNext = 0. ;
|
|
if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE)
|
|
pNextCrv->GetLength( dLenNext) ;
|
|
// verifico la curva corrente
|
|
if ( pCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
|
|
// contributo dalle entità adiacenti ( se non tutte aperte)
|
|
double dLenAgg = 0. ;
|
|
if ( ! bAllOpen) {
|
|
if ( pPrevCrv != nullptr && pPrevCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
|
|
Vector3d vtPrevEnd ; pPrevCrv->GetEndDir( vtPrevEnd) ;
|
|
Vector3d vtStart ; pCrv->GetStartDir( vtStart) ;
|
|
dLenAgg += max( 0.4, vtPrevEnd * vtStart) * dLenPrev ;
|
|
}
|
|
if ( pNextCrv != nullptr && pNextCrv->GetTempProp() == TEMP_PROP_OPEN_EDGE) {
|
|
Vector3d vtEnd ; pCrv->GetEndDir( vtEnd) ;
|
|
Vector3d vtNextStart ; pNextCrv->GetStartDir( vtNextStart) ;
|
|
dLenAgg += max( 0.4, vtEnd * vtNextStart) * dLenNext ;
|
|
}
|
|
}
|
|
if ( ptEndPrec.IsValid() && bFirst)
|
|
dLenAgg += 2. * ( dLenPrev + dLenNext) ;
|
|
// entità corrente
|
|
double dLen = 0 ;
|
|
if ( pCrv->GetLength( dLen)) {
|
|
const double LEN_TOL = 1 ;
|
|
// se di lunghezza praticamente uguale
|
|
if ( bFound && dLen + dLenAgg > dRefLen && abs( dLen + dLenAgg - dMaxLen) < LEN_TOL) {
|
|
Point3d ptTest ;
|
|
pCrv->GetMidPoint( ptTest) ;
|
|
if ( ( PockParams.bAboveHead && ptTest.z > ptStart.z + 100 * EPS_SMALL) ||
|
|
( ! PockParams.bAboveHead && ptTest.z < ptStart.z - 100 * EPS_SMALL) ||
|
|
( abs( ptTest.z - ptStart.z) < 100 * EPS_SMALL && ptTest.y < ptStart.y - 100 * EPS_SMALL)) {
|
|
dMaxLen = max( dMaxLen, dLen + dLenAgg) ;
|
|
ptStart = ptTest ;
|
|
bOutStart = true ;
|
|
// versore ortogonale verso l'esterno
|
|
pCrv->GetMidDir( vtOut) ;
|
|
vtOut.Rotate( Z_AX, 0, -1) ;
|
|
}
|
|
}
|
|
// se più lunga ( o non già trovata)
|
|
else if ( dLen + dLenAgg > dMaxLen || ! bFound) {
|
|
dMaxLen = dLen + dLenAgg ;
|
|
if ( pCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart, &vtOut)) {
|
|
// versore ortogonale verso l'esterno
|
|
vtOut.Normalize() ;
|
|
vtOut.Rotate( Z_AX, 0, -1) ;
|
|
bOutStart = true ;
|
|
bFound = true ;
|
|
}
|
|
}
|
|
dLenPrev = dLen ;
|
|
}
|
|
}
|
|
else
|
|
dLenPrev = 0 ;
|
|
// vado alla successiva
|
|
pPrevCrv = pCrv ;
|
|
pCrv = ( bNextOk ? pNextCrv : nullptr) ;
|
|
bFirst = false ;
|
|
}
|
|
|
|
return bFound ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPtStartOnOpenEdgeByOrigCurve( const Point3d& ptRef, const ICurveComposite* pCrvOrig, const PocketParams& PockParams,
|
|
const ICurveComposite* pCrvCompo, Point3d& ptStart, Vector3d& vtMidOut,
|
|
bool& bMidOut)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvOrig == nullptr || ! pCrvOrig->IsValid() ||
|
|
pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
bMidOut = false ;
|
|
|
|
double dLenRef = ( 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) - 50 * EPS_SMALL ;
|
|
|
|
// se ho un punto di riferimento, cerco il lato aperto sull'originale più vicino ad essa ( sufficientemente lungo)
|
|
if ( ptRef.IsValid()) {
|
|
double dSqMinDist = INFINITO ;
|
|
int nIndCrv = -1 ;
|
|
for ( int i = 0 ; i < pCrvOrig->GetCurveCount() ; ++ i) {
|
|
// escludo le sottocurve chiuse
|
|
if ( pCrvOrig->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
|
|
continue ;
|
|
// recupero la curva i-esima aperta dalla curva originale
|
|
const ICurve* pCrvOpen = pCrvOrig->GetCurve( i) ;
|
|
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
|
|
return false ;
|
|
// ricavo la lunghezza di tale curva
|
|
double dLen = 0. ;
|
|
pCrvOpen->GetLength( dLen) ;
|
|
// se lunghezza accettabile o maggiore della massima trovata
|
|
if ( dLen > dLenRef - 10. * EPS_SMALL) {
|
|
// verifico la distanza con il punto di riferimento
|
|
double dCurrSqDist = 0. ;
|
|
if ( DistPointCurve( ptRef, *pCrvOpen).GetSqDist( dCurrSqDist) && dCurrSqDist < dSqMinDist) {
|
|
dSqMinDist = dCurrSqDist ;
|
|
nIndCrv = i ;
|
|
}
|
|
}
|
|
}
|
|
// se ho un indice di curva valido, allora ricavo i valori
|
|
if ( nIndCrv != -1) {
|
|
// recupero la curva
|
|
const ICurve* pCrvOpen = pCrvOrig->GetCurve( nIndCrv) ;
|
|
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
|
|
return false ;
|
|
Point3d ptSTmp ;
|
|
Vector3d vtMidOutTmp ;
|
|
// ricavo il punto medio e il vettore tangente ad esso associato
|
|
if ( pCrvOpen->GetPointD1D2( 0.5, ICurve::FROM_MINUS, ptSTmp, &vtMidOutTmp)) {
|
|
// versore d'uscita
|
|
vtMidOutTmp.Normalize() ;
|
|
vtMidOutTmp.Rotate( Z_AX, 0, -1) ;
|
|
ptSTmp += vtMidOutTmp * max( PockParams.dRad - PockParams.dSideStep, 0.) ;
|
|
// cerco la sottocurva più vicina a ptSTmp
|
|
int nFlag ;
|
|
double dMyPar ;
|
|
if ( DistPointCurve( ptSTmp, *pCrvCompo).GetParamAtMinDistPoint( 0, dMyPar, nFlag)) {
|
|
int nCrv = int( floor( dMyPar)) ;
|
|
// recupero tale sottocurva e controllo che anche essa sia OPEN
|
|
const ICurve* pMyCrv = pCrvCompo->GetCurve( nCrv) ;
|
|
if ( pMyCrv != nullptr && pMyCrv->IsValid() && pMyCrv->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
// recupero ptStart
|
|
pMyCrv->GetMidPoint( ptStart) ;
|
|
// assegno direzione fuori
|
|
vtMidOut = vtMidOutTmp ;
|
|
// flag per possibile entrata da fuori
|
|
bMidOut = true ;
|
|
}
|
|
}
|
|
}
|
|
// esco
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// se non ho un punto di riferimento valido, o avendolo non si riesce ad entrare dal lato aperto più vicino
|
|
// cerco il lato aperto più lungo ( sufficientemente lungo)
|
|
for ( int i = 0 ; i < pCrvOrig->GetCurveCount() ; ++ i) {
|
|
// escludo le sottocurve chiuse
|
|
if ( pCrvOrig->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE)
|
|
continue ;
|
|
// recupero la curva i-esima aperta dalla curva originale
|
|
const ICurve* pCrvOpen = pCrvOrig->GetCurve( i) ;
|
|
if ( pCrvOpen == nullptr || ! pCrvOpen->IsValid())
|
|
return false ;
|
|
// ricavo la lunghezza di tale curva
|
|
double dLen = 0. ;
|
|
pCrvOpen->GetLength( dLen) ;
|
|
// se lunghezza accettabile o maggiore della massima trovata
|
|
if ( dLen > dLenRef - 10. * EPS_SMALL) {
|
|
Point3d ptSTmp ;
|
|
Vector3d vtMidOutTmp ;
|
|
// ricavo il punto medio e il vettore tangente ad esso associato
|
|
if ( pCrvOpen->GetPointD1D2( 0.5, ICurve::FROM_MINUS, ptSTmp, &vtMidOutTmp)) {
|
|
// versore d'uscita
|
|
vtMidOutTmp.Normalize() ;
|
|
vtMidOutTmp.Rotate( Z_AX, 0, -1) ;
|
|
ptSTmp += vtMidOutTmp * max( PockParams.dRad - PockParams.dSideStep, 0.) ;
|
|
// cerco la sottocurva più vicina a ptSTmp
|
|
int nFlag ;
|
|
double dMyPar ;
|
|
if ( DistPointCurve( ptSTmp, *pCrvCompo).GetParamAtMinDistPoint( 0, dMyPar, nFlag)) {
|
|
int nCrv = int( floor( dMyPar)) ;
|
|
// recupero tale sottocurva e controllo che anche essa sia OPEN
|
|
const ICurve* pMyCrv = pCrvCompo->GetCurve( nCrv) ;
|
|
if ( pMyCrv != nullptr && pMyCrv->IsValid() && pMyCrv->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
// recupero ptStart
|
|
pMyCrv->GetMidPoint( ptStart) ;
|
|
// assegno direzione fuori
|
|
vtMidOut = vtMidOutTmp ;
|
|
// flag per possibile entrata da fuori
|
|
bMidOut = true ;
|
|
// aggiornamento della lunghezza di riferimento
|
|
dLenRef = dLen ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPtStartOnOpenEdgeByPtRef( const Point3d& ptRef, const PocketParams& PockParams,
|
|
const ICurveComposite* pCrvCompo, const ICurveComposite* pCrvOrig,
|
|
Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut)
|
|
{
|
|
// controllo dei parametri
|
|
if ( ! ptRef.IsValid() || pCrvCompo == nullptr || ! pCrvCompo->IsValid() ||
|
|
! ptRef.IsValid())
|
|
return false ;
|
|
bMidOut = false ;
|
|
|
|
// lunghezza minima di riferimento per un lato aperto valido
|
|
double dLenRef = ( 2 * PockParams.dRad + 2 * PockParams.dRadialOffset) - 50 * EPS_SMALL ;
|
|
|
|
// cerco la curva OPEN a minima distanza da ptRef, sufficientemente lunga
|
|
int nStartCrv = -1 ;
|
|
double dMinDist = INFINITO ;
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
// considero solo le sottocurve OPEN
|
|
if ( pCrvCompo->GetCurve( i)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
// sufficientemente lunghe
|
|
double dLen ;
|
|
if ( pCrvCompo->GetCurve( i)->GetLength( dLen) && dLen > dLenRef) {
|
|
DistPointCurve DPC( ptRef, *pCrvCompo->GetCurve( i)) ;
|
|
double dCurrDist = INFINITO ;
|
|
// sufficiententemente vicine a ptRef
|
|
if ( DPC.GetSqDist( dCurrDist) && dCurrDist < dMinDist) {
|
|
dMinDist = dCurrDist ;
|
|
nStartCrv = i ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// se non ho trovato nulla, allora cerco un punto iniziale valido su una sottocurva OPEN
|
|
if ( nStartCrv == -1 && pCrvOrig != nullptr && pCrvOrig->IsValid()) {
|
|
return GetPtStartOnOpenEdgeByOrigCurve( ptRef, pCrvOrig, PockParams, pCrvCompo, ptStart, vtMidOut, bMidOut) ;
|
|
}
|
|
// se ho trovato la curva più vicina, cerco un punto iniziale valido
|
|
else {
|
|
const ICurve* pMyCrv = pCrvCompo->GetCurve( nStartCrv) ;
|
|
if ( pMyCrv != nullptr && pMyCrv->IsValid()) {
|
|
// ricavo ptStart e vettore tangente
|
|
if ( pMyCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart, &vtMidOut)) {
|
|
// versore ortogonale verso l'esterno
|
|
vtMidOut.Normalize() ;
|
|
vtMidOut.Rotate( Z_AX, 0, -1) ;
|
|
// flag per possibile entrata da fuori
|
|
bMidOut = true ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPtStartOnHomogeousEdges( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
|
|
int nType, const Point3d& ptRef, bool& bMidOut, Point3d& ptStart,
|
|
Vector3d& vtMidOut)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
bMidOut = false ;
|
|
|
|
// analizzo i sottotratti omogenei
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
PtrOwner<ICurveComposite> pCrvCompoClone( CloneCurveComposite( pCrvCompo)) ;
|
|
if ( IsNull( pCrvCompoClone) || ! pCrvCompoClone->IsValid())
|
|
return false ;
|
|
GetHomogeneousParts( pCrvCompoClone, PockParams, vpCrvs) ;
|
|
|
|
// se ho un solo tratto omogeneo e coerente al tipo scelto ...
|
|
if ( int( vpCrvs.size()) == 1 && vpCrvs[0]->GetTempProp( 0) == nType) {
|
|
// ... e non ho un punto di riferimento valido
|
|
if ( ! ptRef.IsValid()) {
|
|
// il punto iniziale è già definito
|
|
pCrvCompoClone->GetStartPoint( ptStart) ;
|
|
}
|
|
// ho un punto di riferimento valido
|
|
else {
|
|
// cerco il punto più vicino
|
|
DistPointCurve DistPtCrv( ptRef, *pCrvCompoClone) ;
|
|
int nFlag ;
|
|
if ( ! DistPtCrv.GetMinDistPoint( 0, ptStart, nFlag))
|
|
return false ;
|
|
}
|
|
bMidOut = ( nType == TEMP_PROP_OPEN_EDGE) ;
|
|
return true ;
|
|
}
|
|
|
|
// scorro i tratti omogenei e cerco il tratto del tipo definito più lungo
|
|
int nMaxInd = -1 ;
|
|
double dMaxLen = 0. ;
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
// considero solo i tratti aperti
|
|
if ( vpCrvs[i]->GetTempProp( 0) == nType) {
|
|
// calcolo la lunghezza del tratto aperto
|
|
double dLen ;
|
|
vpCrvs[i]->GetLength( dLen) ;
|
|
if ( dLen > dMaxLen) {
|
|
dMaxLen = dLen ;
|
|
nMaxInd = i ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// recupero il tratto del tipo definito più lungo
|
|
if ( nMaxInd != -1) {
|
|
// recupero il parametro alla sua metà
|
|
double dParIn ;
|
|
vpCrvs[nMaxInd]->GetParamAtLength( 0.5 * dMaxLen, dParIn) ;
|
|
// se tratto OPEN
|
|
if ( nType == TEMP_PROP_OPEN_EDGE) {
|
|
bMidOut = true ;
|
|
vpCrvs[nMaxInd]->GetPointD1D2( dParIn, ICurve::FROM_MINUS, ptStart, &vtMidOut) ;
|
|
vtMidOut.Normalize() ;
|
|
vtMidOut.Rotate( Z_AX, 0, -1) ;
|
|
}
|
|
// se tratto CLOSED
|
|
else
|
|
vpCrvs[nMaxInd]->GetPointD1D2( dParIn, ICurve::FROM_MINUS, ptStart) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPtStartOnGenericEdge( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
|
|
Point3d& ptStart)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
pCrvCompo->GetStartPoint( ptStart) ;
|
|
|
|
// scorro tutte le curve
|
|
double dLenRef = 0. ;
|
|
bool bAllShort = true ; // flag per curve tutte piccole
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
// ricavo la curva i-esima dalla curva originale
|
|
const ICurve* pMyCrv = pCrvCompo->GetCurve( i) ;
|
|
if ( pMyCrv == nullptr || ! pMyCrv->IsValid())
|
|
return false ;
|
|
// ricavo la lunghezza di tale curva
|
|
double dLen = 0. ;
|
|
pMyCrv->GetLength( dLen) ;
|
|
// se lunghezza accettabile o maggiore della massima trovata, ricavo ptStart
|
|
if ( dLen > dLenRef) {
|
|
if ( pMyCrv->GetMidPoint( ptStart)) {
|
|
dLenRef = dLen ;
|
|
bAllShort = false ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// se curve tutte piccole
|
|
if ( bAllShort) {
|
|
bool bMidOutFake ;
|
|
Vector3d vtMidOutFake ;
|
|
if ( ! GetPtStartOnHomogeousEdges( pCrvCompo, PockParams, TEMP_PROP_CLOSE_EDGE, P_INVALID,
|
|
bMidOutFake, ptStart, vtMidOutFake))
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetPtStartOnGenericEdgeByPtRef( const ICurveComposite* pCrvCompo, const PocketParams& PockParams,
|
|
const Point3d& ptRef, Point3d& ptStart)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid() || pCrvCompo->GetCurveCount() == 0 ||
|
|
! ptRef.IsValid())
|
|
return false ;
|
|
|
|
// ordino le curve di pCrvCompo in base alla vicinanza da ptRef
|
|
// <indice della curva, distanza da ptRef>
|
|
vector<pair<int, double>> vCrvInfoDist ; vCrvInfoDist.resize( pCrvCompo->GetCurveCount()) ;
|
|
// scorro le curve di pCrvCompo
|
|
for ( int i = 0 ; i < pCrvCompo->GetCurveCount() ; ++ i) {
|
|
vCrvInfoDist[i].first = i ;
|
|
// calcolo la distanza tra la curva i-esima e ptRef
|
|
double dDist = INFINITO ;
|
|
DistPointCurve DistPtCrv( ptRef, *pCrvCompo->GetCurve( i)) ;
|
|
DistPtCrv.GetSqDist( dDist) ;
|
|
vCrvInfoDist[i].second = dDist ;
|
|
}
|
|
// ordino il vettore in base alla distanza
|
|
sort( vCrvInfoDist.begin(), vCrvInfoDist.end(), [] ( const pair<int, double>& a, const pair<int, double>& b) {
|
|
return a.second < b.second ;
|
|
}) ;
|
|
|
|
// cerco un parametro iniziale valido per le sottocurve ordinate
|
|
double dLenRef = 2 * PockParams.dRad + 2 * PockParams.dRadialOffset ;
|
|
for ( int i = 0 ; i < int( vCrvInfoDist.size()) ; ++ i) {
|
|
// recupero la curva i-esima
|
|
const ICurve* pMyCrv = pCrvCompo->GetCurve( vCrvInfoDist[i].first) ;
|
|
if ( pMyCrv != nullptr && pMyCrv->IsValid()) {
|
|
// controllo che sia abbastanza lunga e aggiorno ptStart
|
|
double dLen = 0. ;
|
|
pMyCrv->GetLength( dLen) ;
|
|
if ( dLen > dLenRef) {
|
|
if ( pMyCrv->GetPointD1D2( .5, ICurve::FROM_MINUS, ptStart))
|
|
return true ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// alla peggio...
|
|
bool bMidOutFake ;
|
|
Vector3d vtMidOutFake ;
|
|
return GetPtStartOnHomogeousEdges( pCrvCompo, PockParams, TEMP_PROP_CLOSE_EDGE, ptRef,
|
|
bMidOutFake, ptStart, vtMidOutFake) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AssignOpenCloseTmpPropToFirstOffsCurve( ICurveComposite* pCrv, const PocketParams& PockParams,
|
|
const ISurfFlatRegion* pSfrToWork, bool& bSomeOpen)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid() || pCrv->GetCurveCount() == 0 ||
|
|
pSfrToWork == nullptr || ! pSfrToWork->IsValid() || pSfrToWork->GetChunkCount() != 1)
|
|
return false ;
|
|
bSomeOpen = false ; // flag per presenza di lati aperti
|
|
int nInd = -1 ; // indice della prima curva che non è "raccordo" di Offset
|
|
|
|
// scorro tutte le curve presenti in pCrv ( curva da cui devo entrare)
|
|
for ( int i = 0 ; i < pCrv->GetCurveCount() ; ++ i) {
|
|
// nProp0 -> #curva il cui Offset ha generato la curva i-esima di pCrv
|
|
int nProp0 = 0 ; pCrv->GetCurveTempProp( i, nProp0, 0) ;
|
|
// nProp1 -> #loop che contiene la curva espressa in nProp0
|
|
int nProp1 = 0 ; pCrv->GetCurveTempProp( i, nProp1, 1) ;
|
|
|
|
// se questa curva non è un "raccordo" di Offset
|
|
if ( nProp0 > 0) {
|
|
// aggiorno la proprietà della curva da cui devo entrare
|
|
int nTempProp = TEMP_PROP_CLOSE_EDGE ;
|
|
pSfrToWork->GetCurveTempProp( 0, nProp1, nProp0 - 1, nTempProp, 0) ;
|
|
pCrv->SetCurveTempProp( i, nTempProp, 0) ;
|
|
// se la curva è aperta, aggiorno il Flag
|
|
if ( nTempProp == TEMP_PROP_OPEN_EDGE && ! bSomeOpen)
|
|
bSomeOpen = true ;
|
|
// salvo l'indice della prima curva trovata che non è un "raccordo" di Offset
|
|
if ( nInd == -1)
|
|
nInd = i ;
|
|
}
|
|
// se questa curva è un "raccordo" di Offset
|
|
else {
|
|
pCrv->SetCurveTempProp( i, TEMP_PROP_SMOOTH, 0) ;
|
|
pCrv->SetCurveTempProp( i, TEMP_PROP_SMOOTH, 1) ;
|
|
}
|
|
}
|
|
|
|
// cambio il punto iniziale all'inizio della prima curva che non deriva da un "raccordo"
|
|
Point3d ptStart ;
|
|
if ( nInd != -1) {
|
|
if ( ! pCrv->GetStartPoint( ptStart))
|
|
return false ;
|
|
pCrv->ChangeStartPoint( nInd) ; // la prima curva non è un "raccordo" di Offset
|
|
}
|
|
|
|
// scorro tutte le curve successive alla prima
|
|
for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++ i) {
|
|
int nTmpProp0, nTmpProp1 ;
|
|
// se "raccordo" copio la tempProp precedente
|
|
if (( pCrv->GetCurveTempProp( i, nTmpProp0, 0) && nTmpProp0 == TEMP_PROP_SMOOTH) &&
|
|
( pCrv->GetCurveTempProp( i, nTmpProp1, 1) && nTmpProp1 == TEMP_PROP_SMOOTH))
|
|
// copio la temp prop della curva precedente
|
|
pCrv->SetCurveTempProp( i, pCrv->GetCurve( i-1)->GetTempProp( 0), 0) ;
|
|
|
|
}
|
|
|
|
// lascio invariato il punto iniziale della curva originaria
|
|
if ( nInd != -1) {
|
|
double dUNewS ;
|
|
pCrv->GetParamAtPoint( ptStart, dUNewS) ;
|
|
pCrv->ChangeStartPoint( dUNewS) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SetPtStartForPath( ICurveComposite* pCrvOffs, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
|
|
const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut, bool& bMidOut,
|
|
const ICurveComposite* pCrvOrig)
|
|
{
|
|
/*
|
|
Come prima cosa ricavo i lati aperti equivalenti dal primo Offset; Facendo l'Offset di una curva
|
|
chiusa ho all'interno delle temp prop delle sottcurve due informazioni :
|
|
// nTmpProp0 -> #curva il cui Offset ha generato la curva i-esima attuale
|
|
// nTmpProp1 -> #loop che contiene la curva espressa in nTmpProp0
|
|
|
|
Quindi dal contorno con i lati aperti impostati, posso settare i lati aperti "equivalenti"
|
|
anche sulla curva di primo Offset.
|
|
NB. Il tool entra dalla curva di primo Offset, non dal bordo della regione da svuotare; quindi
|
|
l'entrata va calcolata sul primo Offset, non sulla curva di bordo della FlatRegion di Pocketing.
|
|
*/
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0 ||
|
|
pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1)
|
|
return false ;
|
|
|
|
// assegno proprietà di OPEN/CLOSE alle sottocurve dell'offset corrente
|
|
bool bSomeOpen = false ;
|
|
if ( ! AssignOpenCloseTmpPropToFirstOffsCurve( pCrvOffs, PockParams, pSrfToWork, bSomeOpen))
|
|
return false ;
|
|
|
|
// punto valido per l'entrata ...
|
|
bMidOut = false ;
|
|
if ( bSomeOpen) {
|
|
// se esistono lati aperti...
|
|
if ( pCrvOrig != nullptr && pCrvOrig->IsValid()) {
|
|
// ... e non ho un punto di riferimento, lo cerco sulla curva originale
|
|
if ( ! ptEndPrec.IsValid()) {
|
|
if ( ! GetPtStartOnOpenEdgeByOrigCurve( P_INVALID, pCrvOrig, PockParams, pCrvOffs, ptStart, vtMidOut, bMidOut))
|
|
return false ;
|
|
}
|
|
// ... e ho un punto di riferimento
|
|
else {
|
|
if ( ! GetPtStartOnOpenEdgeByPtRef( ptEndPrec, PockParams, pCrvOffs, pCrvOrig, ptStart, vtMidOut, bMidOut))
|
|
return false ;
|
|
}
|
|
}
|
|
// se punto non trovato precedentemente, tento con una sottocurva OPEN generica
|
|
if ( ! bMidOut) {
|
|
if ( ! GetParamOnOpenCurve( pCrvOffs, PockParams, ptEndPrec, bMidOut, ptStart, vtMidOut))
|
|
return false ;
|
|
}
|
|
// se queste curve sono troppo corte, analizzo le parti uniformi
|
|
if ( ! bMidOut) {
|
|
if ( ! GetPtStartOnHomogeousEdges( pCrvOffs, PockParams, TEMP_PROP_OPEN_EDGE, ptEndPrec,
|
|
bMidOut, ptStart, vtMidOut))
|
|
return false ;
|
|
}
|
|
}
|
|
// se sottocurve tutte CLOSED o un punto iniziale non valido, lo cerco su un lato generico
|
|
if ( ! bMidOut) {
|
|
if ( ! ptEndPrec.IsValid()) {
|
|
if ( ! GetPtStartOnGenericEdge( pCrvOffs, PockParams, ptStart))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! GetPtStartOnGenericEdgeByPtRef( pCrvOffs, PockParams, ptEndPrec, ptStart))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// imposto il punto iniziale scelto
|
|
double dPar = 0. ;
|
|
if ( ! pCrvOffs->GetParamAtPoint( ptStart, dPar) || ! pCrvOffs->ChangeStartPoint( dPar))
|
|
pCrvOffs->ChangeStartPoint( .5) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SetAdvancedPtStartForPath( ICRVCOMPOPOVECTOR& vCrvOffsAct, const PocketParams& PockParams, const ISurfFlatRegion* pSrfToWork,
|
|
const Point3d& ptEndPrec, Point3d& ptStart, Vector3d& vtMidOut,
|
|
bool& bMidOut, int& nIndex, ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops)
|
|
{
|
|
// controllo dei parametri
|
|
for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i) {
|
|
if ( IsNull( vCrvOffsAct[i]) || ! vCrvOffsAct[i]->IsValid() || vCrvOffsAct[i]->GetCurveCount() == 0)
|
|
return false ;
|
|
}
|
|
if ( pSrfToWork == nullptr || ! pSrfToWork->IsValid() || pSrfToWork->GetChunkCount() != 1)
|
|
return false ;
|
|
nIndex = 0 ;
|
|
|
|
// scorro le curve di primo Offset
|
|
// NB. in prima posizione ho il primo Offset del loop esterno, a seguire le isole
|
|
// NB. Se non riesco ad entrare in nessuna curva, allora tengo valida quella calcolata per il loop esterno
|
|
bool bMidOut_fist = false ;
|
|
Vector3d vtMidOut_first ;
|
|
Point3d ptStart_first ;
|
|
for ( int i = 0 ; i < int( vCrvOffsAct.size()) ; ++ i) {
|
|
// cambio il suo punto d'inizio dell'Offset attuale
|
|
if ( SetPtStartForPath( vCrvOffsAct[i], PockParams, pSrfToWork, ptEndPrec, ptStart, vtMidOut,
|
|
bMidOut, i < ( int( vCrvOrigChunkLoops.size())) ? Get( vCrvOrigChunkLoops[i]) : nullptr))
|
|
vCrvOffsAct[i]->GetStartPoint( ptStart) ;
|
|
else
|
|
return false ;
|
|
// se ho trovato un'entrata valida da un lato aperto, ho finito
|
|
if ( bMidOut) {
|
|
nIndex = i ; // salvo l'indice del vettore
|
|
return true ;
|
|
}
|
|
// se sono nel loop esterno e non ho trovato un'entrata da un lato aperto, salvo i risultati ottenuti
|
|
else if ( i == 0) {
|
|
bMidOut_fist = bMidOut ;
|
|
vtMidOut_first = vtMidOut ;
|
|
ptStart_first = ptStart ;
|
|
}
|
|
}
|
|
|
|
// in questo caso non ho trovato un'entrata da un lato aperto in nessuna curva ( potrebbero essere tutte chiuse)
|
|
bMidOut = bMidOut_fist ;
|
|
vtMidOut = vtMidOut_first ;
|
|
ptStart = ptStart_first ;
|
|
return true ;
|
|
|
|
}
|
|
|
|
// ***************************************************************************
|
|
//---------------------------- CASI OTTIMIZZATI ------------------------------
|
|
// ***************************************************************************
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetOptCrvIndex( const ISurfFlatRegion* pSfrOrig, const ISurfFlatRegion* pSfrChunk,
|
|
const PocketParams& PockParams, int nReg, ICRVCOMPOPOVECTOR& vCrvOrig)
|
|
{
|
|
|
|
/*
|
|
NB. La superficie da lavorare è stata estesa presso i lati aperti; cerco il Chunk
|
|
di riferimento sulla superficie originale ed estraggo le sue curve ( in prima posizione
|
|
il loop esterno e a seguire le isole); queste curve verranno utilizzate per la scelta
|
|
dei casi ottimizzati ( trapezi, circonferenze, ecc ecc..)
|
|
*/
|
|
|
|
// controllo dei parametri
|
|
if ( pSfrOrig == nullptr || ! pSfrOrig->IsValid() ||
|
|
pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
|
|
// se la superficie originale ha un solo Chunk, allora le curve originali sono già trovate
|
|
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( 0) ; ++ l)
|
|
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( 0, l))) ;
|
|
if ( pSfrOrig->GetChunkCount() == 1)
|
|
return true ;
|
|
|
|
// vettore di indici dei chunk candidati
|
|
INTVECTOR vInds ;
|
|
|
|
// scorro tutti i Chunk della superficie orginaria
|
|
for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) {
|
|
// la classificazione può essere fatta semplicemente guardando il Loop esterno
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
// classifico il Chunk attuale con la curva in esame
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) {
|
|
// se il Chunk attuale è stato esteso presso presso i lati aperti mi aspetto che esso non sia più
|
|
// grande del chunk originale ( se non ci sono lati aperti, allora coincide )
|
|
// se il ccClass non contiene parti classificate come esterne allora il Chunk di riferimento è il c-esimo
|
|
bool bIsThis = true ;
|
|
for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) {
|
|
if ( ccClass[k].nClass == CRVC_OUT)
|
|
bIsThis = false ;
|
|
}
|
|
if ( bIsThis)
|
|
vInds.push_back( c) ;
|
|
}
|
|
}
|
|
// se ho un solo indice allora ho già identificato le curve originali
|
|
if ( int( vInds.size()) == 1) {
|
|
vCrvOrig.clear() ;
|
|
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( vInds[0]) ; ++ l)
|
|
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( vInds[0], l))) ;
|
|
return true ;
|
|
}
|
|
// altrimenti...
|
|
/*
|
|
NB. Come parametro alla funzione è stato passato "nReg"; Facendo il primo Offset del Chunk attuale
|
|
questo verrà diviso in n sottoregioni; devo considerare la nReg-esima
|
|
Situazione: Estendendo presso i lati aperti, due ( o più) chunk si sono uniti formandone uno unico
|
|
Quando effettuo il primo offset ( del diametro del materiale + offset radiale), questo Chunk si splitterà in n Chunk;
|
|
Uno ( o più) di questi nuovi chunk potrebbe corrispondere ad un caso ottimizzato.
|
|
Devo recuperare il Chunk della superficie originaria ( che non essendo estesa presso i lati aperti
|
|
ha la geomtria originale)
|
|
*/
|
|
|
|
// effettuo un Offset del Chunk per simulare la creazione degli n Chunks
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
PtrOwner<ISurfFlatRegion> pSfrRefChunk( pSfrChunk->Clone()) ;
|
|
if ( IsNull( pSfrRefChunk) ||
|
|
! pSfrRefChunk->Offset( - dOffs, ICurve::OFF_FILLET))
|
|
return false ;
|
|
|
|
// clono il Chunk nReg-esimo se esiste
|
|
pSfrRefChunk.Set( pSfrRefChunk->CloneChunk( nReg)) ;
|
|
if ( IsNull( pSfrRefChunk))
|
|
return true ; // bisogna passare al ( pSfrChunk + 1)-esimo se esiste
|
|
|
|
// scorro tutti i Chunk della superficie orginaria e ripeto la classificazione
|
|
// NB. Questa volta posso fermarmi al primo Chunk classificato come non esterno
|
|
for ( int c = 0 ; c < int( pSfrOrig->GetChunkCount()) ; ++ c) {
|
|
// come prima recupero il Loop esterno
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrOrig->GetLoop( c, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
// classificazione
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrRefChunk->GetCurveClassification( *pCrvExtLoop, EPS_SMALL, ccClass)) {
|
|
bool bIsThis = true ;
|
|
for ( int k = 0 ; k < int( ccClass.size()) && bIsThis ; ++ k) {
|
|
if ( ccClass[k].nClass == CRVC_OUT)
|
|
bIsThis = false ;
|
|
}
|
|
if ( bIsThis) {
|
|
vCrvOrig.clear() ;
|
|
for ( int l = 0 ; l < pSfrOrig->GetLoopCount( c) ; ++ l)
|
|
vCrvOrig.emplace_back( ConvertCurveToComposite( pSfrOrig->GetLoop( c, l))) ;
|
|
return true ; // esco
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ; // alla peggio non considero casi ottimizzati e svuoto normalmente...
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ModifyCurveToSmoothed( ICurveComposite* pCrv, const PocketParams& PockParams, double dRightLen, double dLeftLen, bool bAsParam)
|
|
{
|
|
// se non richiesto non faccio nulla
|
|
if ( ! PockParams.bSmooth)
|
|
return true ;
|
|
|
|
// controllo parametri
|
|
if ( pCrv == nullptr || dLeftLen < EPS_SMALL || dRightLen < EPS_SMALL)
|
|
return false ;
|
|
|
|
ICURVEPOVECTOR vCrvStepsToFill ; // vettore delle Curve da raccordare (curve della composita)
|
|
ICURVEPOVECTOR vCrvArcs ; // vettore contenente gli Archi (tra le curve della composita)
|
|
INTVECTOR vArcsToJump ; // vettore di indici per gli archi saltati (nel caso non si riesca a raccordare ...)
|
|
|
|
// se nella composita ho meno di due curve allora non c'è nulla da raccordare
|
|
const ICurve* pMyCrv = pCrv->GetFirstCurve() ;
|
|
while ( pMyCrv != nullptr) {
|
|
vCrvStepsToFill.emplace_back( pMyCrv->Clone()) ;
|
|
pMyCrv = pCrv->GetNextCurve() ;
|
|
}
|
|
if ( vCrvStepsToFill.size() < 2)
|
|
return true ;
|
|
|
|
// controllo se la curva è chiusa ( nel caso inserisco nel vettore la prima curva due volte, all'inizio e alla fine)
|
|
if ( pCrv->IsClosed()) {
|
|
const ICurve* pMyCrvFirst = pCrv->GetFirstCurve() ;
|
|
if ( pMyCrvFirst == nullptr)
|
|
return false ;
|
|
vCrvStepsToFill.emplace_back( pMyCrvFirst->Clone()) ;
|
|
}
|
|
|
|
double dUE_ref, dUS_ref, dRadius, dPar1, dPar2 ;
|
|
// riempio il vettore degli archi, scorro tutte le curve da raccordare ( la i-esima e la (i+1)-esima)
|
|
for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) {
|
|
|
|
// controllo che le curve non siano già tangenti
|
|
Vector3d vtS, vtE ;
|
|
if ( ! vCrvStepsToFill[i]->GetEndDir( vtS) || ! vCrvStepsToFill[i+1]->GetStartDir( vtE))
|
|
continue ;
|
|
if ( AreSameVectorApprox( vtS, vtE)) {
|
|
vCrvArcs.emplace_back( CreateCurveComposite()) ; // arco nullo
|
|
vArcsToJump.push_back( i) ; // da scartare
|
|
continue ;
|
|
}
|
|
|
|
// ricavo i valori parametrici associati
|
|
double dLen_act ; vCrvStepsToFill[i]->GetLength( dLen_act) ;
|
|
double dLen_succ ; vCrvStepsToFill[i+1]->GetLength( dLen_succ) ;
|
|
double dUE_ref = 1. ;
|
|
if ( dLen_act > dLeftLen)
|
|
dUE_ref -= dLeftLen / dLen_act ;
|
|
double dUS_ref = 0. ;
|
|
if ( dLen_succ > dRightLen)
|
|
dUS_ref = dRightLen / dLen_succ ;
|
|
|
|
// se valori trattati come parametri...
|
|
if ( bAsParam) {
|
|
// ... cerco i parametri corrispondenti sulle due curve
|
|
double dU_cm_S = 0 ; double dULast1 = 1 ; double dULast2 = 1 ;
|
|
vCrvStepsToFill[i]->GetDomain( dU_cm_S, dULast1) ;
|
|
dUE_ref = ( 1 - dRightLen) * dULast1 ;
|
|
vCrvStepsToFill[i+1]->GetDomain( dU_cm_S, dULast2) ;
|
|
dUS_ref = dLeftLen * dULast2 ;
|
|
}
|
|
|
|
// prendo i punti sulle due curve rispetto a tali parametri
|
|
Point3d ptS, ptE ;
|
|
if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) ||
|
|
! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE))
|
|
return false ;
|
|
|
|
dRadius = Dist( ptS, ptE) ; // uso come raggio la distanza tra i due punti ( limite superiore valido)
|
|
|
|
int nMaxTestForArcs = 3 ; // tentativi per creare l'arco
|
|
int nIterForArcs = 0 ; // numero di intersezioni con altre curve
|
|
bool IntersBTWArcs = false ; // caso particolare in cui due archi di raccordo si intersecano causa curva piccola
|
|
|
|
// creo l'arco di raccordo e controllo che non sia troppo piccolo
|
|
PtrOwner<ICurveArc> pCrvArc( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ;
|
|
double dArcLen ;
|
|
if ( ! IsNull( pCrvArc) && pCrvArc->IsValid() && ( ! pCrvArc->GetLength( dArcLen) || dArcLen < 5 * EPS_SMALL)) {
|
|
vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco nullo
|
|
vArcsToJump.push_back( i) ; // da scartare
|
|
continue ;
|
|
}
|
|
|
|
// dal secondo arco in poi controllo che non ci siano intersezioni tra essi
|
|
if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) {
|
|
IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ;
|
|
if ( intCCH.GetIntersCount() > 0 )
|
|
IntersBTWArcs = true ;
|
|
}
|
|
|
|
// se ho intersezioni tra archi o l'arco creato non è valido, allora provo altre nMaxTestForArcs volte a ricrearlo avvicinando i punti
|
|
while (( IsNull( pCrvArc) || IntersBTWArcs) && nIterForArcs < nMaxTestForArcs) {
|
|
|
|
dUE_ref = ( 1 + dUE_ref ) * 0.5 ;
|
|
dUS_ref = dUS_ref * 0.5 ;
|
|
|
|
if ( ! vCrvStepsToFill[i]->GetPointD1D2( dUE_ref, ICurve::FROM_PLUS, ptS) ||
|
|
! vCrvStepsToFill[i+1]->GetPointD1D2( dUS_ref, ICurve::FROM_MINUS, ptE))
|
|
return false ;
|
|
|
|
dRadius = Dist( ptS, ptE) ;
|
|
|
|
pCrvArc.Set( CreateFillet( *vCrvStepsToFill[i], ptS, *vCrvStepsToFill[i+1], ptE, Z_AX, dRadius, dPar1, dPar2)) ;
|
|
if ( IsNull( pCrvArc) || ! pCrvArc->IsValid() || ! pCrvArc->GetLength( dArcLen) || dArcLen < 5 * EPS_SMALL)
|
|
break ;
|
|
++ nIterForArcs ;
|
|
|
|
IntersBTWArcs = false ;
|
|
if ( i != 0 && vArcsToJump[i-1] == -1 && ! IsNull( pCrvArc) && pCrvArc->IsValid()) { // dal secondo arco in poi controllo che non ci siano intersezioni tra essi
|
|
IntersCurveCurve intCCH( *pCrvArc, *vCrvArcs[i-1]) ;
|
|
if ( intCCH.GetIntersCount() > 0)
|
|
IntersBTWArcs = true ;
|
|
}
|
|
|
|
}
|
|
if ( IsNull( pCrvArc) || ! pCrvArc->IsValid()) { // se ancora non riesco... salto l'arco
|
|
vCrvArcs.emplace_back( CreateCurveArc()) ; // arco nullo
|
|
vArcsToJump.push_back( i) ; // da scartare
|
|
continue ;
|
|
}
|
|
|
|
// se ho creato l'arco lo memorizzo
|
|
vCrvArcs.emplace_back( Release( pCrvArc)) ; // arco valido
|
|
vArcsToJump.push_back( -1) ; // da considerare
|
|
}
|
|
|
|
// creo la curva che restituirò
|
|
PtrOwner<CurveComposite> pCrvCO_temp( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCrvCO_temp))
|
|
return false ;
|
|
Point3d ptArcHelp, ptFirstPoint ;
|
|
|
|
// unisco la curva i-esima con l'arco i-esimo (non guardo l'ultima curva nel vettore, controllo dopo il caso di curva chiusa)
|
|
for ( int i = 0 ; i < int( vCrvStepsToFill.size()) - 1 ; ++ i) {
|
|
|
|
if ( vArcsToJump[i] == -1) { // se esiste l'arco ...
|
|
Point3d ptArcS, ptArcE ;
|
|
if ( ! vCrvArcs[i]->GetStartPoint( ptArcS) ||
|
|
! vCrvArcs[i]->GetEndPoint( ptArcE) ||
|
|
! vCrvStepsToFill[i]->GetParamAtPoint( ptArcS, dUE_ref) ||
|
|
! vCrvStepsToFill[i+1]->GetParamAtPoint( ptArcE, dUS_ref))
|
|
return false ;
|
|
|
|
if ( i == 0) { // ... e sono nella prima iterazione ...
|
|
if ( ! pCrv->IsClosed()) { // ... e se la curva è aperta -> la inserisco nel nuovo percorso
|
|
if ( ! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref))))
|
|
return false ;
|
|
}
|
|
ptFirstPoint = ptArcS ;
|
|
// ... e se la curva è chiusa -> non la inserisco nel nuovo percorso
|
|
}
|
|
else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...)
|
|
if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref))))
|
|
return false ;
|
|
// aggiungo la curva 'tagliata per il raccordo'
|
|
}
|
|
|
|
// imposto la proprietà di raccordo
|
|
vCrvArcs[i]->SetTempProp( TEMP_PROP_SMOOTH, 1) ;
|
|
if ( ! pCrvCO_temp->AddCurve( vCrvArcs[i]->Clone())) // aggiungo l'arco di raccordo
|
|
return false ;
|
|
ptArcHelp = ptArcE ;
|
|
}
|
|
else { // se non esiste l'arco ...
|
|
if ( i == 0 ) { // e sono nella prima iterazione...
|
|
if ( ! vCrvStepsToFill[0]->GetEndPoint( ptArcHelp))
|
|
return false ;
|
|
|
|
if ( ! pCrv->IsClosed()) { // ...e se aperta // aggiungo la prima curva per intero
|
|
if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( 0, dUE_ref))))
|
|
return false ;
|
|
}
|
|
ptFirstPoint = ptArcHelp ;
|
|
}
|
|
else { // ... e non sono nella prima iterazione (non ha importanza se la curva è chiusa o aperta...)
|
|
double dUS_cm ;
|
|
if ( ! vCrvStepsToFill[i]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! vCrvStepsToFill[i]->GetDomain( dUS_cm, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[i]->CopyParamRange( dUS_ref, dUE_ref))) ||
|
|
! vCrvStepsToFill[i]->GetEndPoint( ptArcHelp))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// ultima curva...
|
|
if ( pCrv->IsClosed()) { // se curva chiusa...
|
|
if ( ! vCrvStepsToFill[0]->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! vCrvStepsToFill[0]->GetParamAtPoint( ptFirstPoint, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill[0]->CopyParamRange( dUS_ref, dUE_ref))))
|
|
return false ;
|
|
}
|
|
else { // se curva aperta... ( non ha importanza l'esistenza o meno degli archi...)
|
|
double dUS_cm ;
|
|
if ( ! vCrvStepsToFill.back()->GetParamAtPoint( ptArcHelp, dUS_ref) ||
|
|
! vCrvStepsToFill.back()->GetDomain( dUS_cm, dUE_ref) ||
|
|
! pCrvCO_temp->AddCurve( GetCurve( vCrvStepsToFill.back()->CopyParamRange( dUS_ref, dUE_ref))))
|
|
return false ;
|
|
}
|
|
|
|
// ripristino il punto inziale se la curva è chiusa
|
|
double dNewDU ;
|
|
if ( ! pCrv->IsClosed()) {
|
|
if ( ! pCrvCO_temp->GetParamAtPoint( ptArcHelp, dNewDU))
|
|
return false ;
|
|
pCrvCO_temp->ChangeStartPoint( dNewDU) ;
|
|
}
|
|
|
|
// restituisco
|
|
int nProp0 = pCrv->GetTempProp( 0) ;
|
|
int nProp1 = pCrv->GetTempProp( 1) ;
|
|
double dParam0 = pCrv->GetTempParam( 0) ;
|
|
double dParam1 = pCrv->GetTempParam( 1) ;
|
|
pCrv->Clear() ;
|
|
pCrv->AddCurve( Release( pCrvCO_temp)) ;
|
|
pCrv->SetTempProp( nProp0, 0) ;
|
|
pCrv->SetTempProp( nProp1, 1) ;
|
|
pCrv->SetTempParam( dParam0, 0) ;
|
|
pCrv->SetTempParam( dParam1, 1) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, const ICRVCOMPOPOVECTOR& vOffIslands,
|
|
ICurveComposite* pCrvLink)
|
|
{
|
|
// pulisco
|
|
pCrvLink->Clear() ;
|
|
|
|
// non serve collegare ( può capitare nel tagliare percorsi con isole complesse)
|
|
if ( AreSamePointApprox( ptStart, ptEnd))
|
|
return true ;
|
|
|
|
// creo la retta che li unisce
|
|
PtrOwner<CurveComposite> pCompoLine( CreateBasicCurveComposite()) ;
|
|
if ( ! pCompoLine->AddPoint( ptStart) || ! pCompoLine->AddLine( ptEnd))
|
|
return false ;
|
|
pCompoLine->SetExtrusion( Z_AX) ;
|
|
|
|
// creo la nuova curva formata dai tratti di linee INTERNI alle isole e dai tratti sul bordo degli offset
|
|
PtrOwner<CurveComposite> pCompo( pCompoLine->Clone()) ;
|
|
if ( IsNull( pCompo) )
|
|
return false ;
|
|
|
|
// memorizzo il tratto lineare nel caso qualche operazione fallisse
|
|
PtrOwner<CurveComposite> pCompoHelp( pCompoLine->Clone()) ;
|
|
if ( IsNull( pCompoHelp))
|
|
return false ;
|
|
|
|
// scorro il vettore degli indici degli offset delle isole intersecati
|
|
for ( int i = 0 ; i < int( vOffIslands.size()) ; ++ i) {
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pCompo, *vOffIslands[i]) ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
if ( ! pCompoHelp->Clear())
|
|
return false ;
|
|
// per ogni intersezione j con l'offset dell'isola i
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++j ) {
|
|
// se ho intersezione spezzo il segmento
|
|
if ( ccClass[j].nClass == CRVC_OUT) {
|
|
Point3d ptS ; pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ;
|
|
double dOffS ; vOffIslands[i]->GetParamAtPoint( ptS, dOffS) ;
|
|
Point3d ptE ; pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ;
|
|
double dOffE ; vOffIslands[i]->GetParamAtPoint( ptE, dOffE) ;
|
|
// recupero i due possibili percorsi e uso il più corto
|
|
PtrOwner<ICurve> pCrvA( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ;
|
|
PtrOwner<ICurve> pCrvB( vOffIslands[i]->CopyParamRange( dOffE, dOffS)) ;
|
|
if ( IsNull( pCrvA) || IsNull( pCrvB))
|
|
return false ;
|
|
double dLenA ; pCrvA->GetLength( dLenA) ;
|
|
double dLenB ; pCrvB->GetLength( dLenB) ;
|
|
if ( dLenA < dLenB)
|
|
pCompoHelp->AddCurve( Release( pCrvA)) ;
|
|
else {
|
|
pCrvB->Invert() ;
|
|
pCompoHelp->AddCurve( Release( pCrvB)) ;
|
|
}
|
|
}
|
|
// se non interseco
|
|
else {
|
|
pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
|
|
}
|
|
}
|
|
pCompo->Clear() ;
|
|
pCompo->AddCurve( pCompoHelp->Clone()) ;
|
|
}
|
|
|
|
pCrvLink->AddCurve( Release( pCompo)) ;
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static bool
|
|
CalcSpecialBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd,
|
|
const Vector3d& vtEnd, const PocketParams& PockParams,
|
|
ICurveComposite* pCrvLink)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvLink == nullptr)
|
|
return false ;
|
|
pCrvLink->Clear() ;
|
|
|
|
// parametro di estensione in tangenza
|
|
double dLenExtension = PockParams.dRad / 2. ;
|
|
|
|
// determino i punti della curva
|
|
PNTVECTOR vPnts = { ptStart, ptStart + ( dLenExtension * vtStart),
|
|
ptEnd - ( dLenExtension * vtEnd), ptEnd} ;
|
|
int nInd = 1 ;
|
|
static const double COS_TOL = cos( 130 * DEGTORAD) ;
|
|
// se primo angolo minore della tolleranza, aggiungo un punto
|
|
Vector3d vtPrev = vPnts[nInd] - vPnts[nInd-1] ; vtPrev.Normalize() ;
|
|
Vector3d vtNext = vPnts[nInd+1] - vPnts[nInd] ; vtNext.Normalize() ;
|
|
double dCos = vtPrev * vtNext ;
|
|
if ( dCos < COS_TOL) {
|
|
Point3d ptA = vPnts[nInd] + dLenExtension * GetRotate( vtStart, Z_AX, ANG_RIGHT) ;
|
|
Point3d ptB = vPnts[nInd] - dLenExtension * GetRotate( vtStart, Z_AX, ANG_RIGHT) ;
|
|
if ( SqDist( ptA, vPnts[nInd+1]) < SqDist( ptB, vPnts[nInd+1]))
|
|
vPnts.insert( vPnts.begin() + nInd + 1, ptA) ;
|
|
else
|
|
vPnts.insert( vPnts.begin() + nInd + 1, ptB) ;
|
|
++ nInd ;
|
|
}
|
|
++ nInd ;
|
|
// se secondo angolo maggiore della tolleranza, aggiungo un punto
|
|
vtPrev = vPnts[nInd] - vPnts[nInd-1] ; vtPrev.Normalize() ;
|
|
vtNext = vPnts[nInd+1] - vPnts[nInd] ; vtNext.Normalize() ;
|
|
dCos = vtPrev * vtNext ;
|
|
if ( dCos < COS_TOL) {
|
|
Point3d ptA = vPnts[nInd] + dLenExtension * GetRotate( - vtEnd, Z_AX, ANG_RIGHT) ;
|
|
Point3d ptB = vPnts[nInd] - dLenExtension * GetRotate( - vtEnd, Z_AX, ANG_RIGHT) ;
|
|
if ( SqDist( ptA, vPnts[nInd-1]) < SqDist( ptB, vPnts[nInd-1]))
|
|
vPnts.insert( vPnts.begin() + nInd + 1, ptA) ;
|
|
else
|
|
vPnts.insert( vPnts.begin() + nInd + 1, ptB) ;
|
|
}
|
|
|
|
// definisco raccordo a ZigZag ( Lineare)
|
|
PtrOwner<ICurveComposite> pZigZagLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pZigZagLink))
|
|
return false ;
|
|
bool bFirst = true ;
|
|
for ( const Point3d& ptNew : vPnts) {
|
|
if ( bFirst) {
|
|
bFirst = false ;
|
|
pZigZagLink->AddPoint( ptNew) ;
|
|
}
|
|
else
|
|
pZigZagLink->AddLine( ptNew) ;
|
|
}
|
|
|
|
// questa composita contiene al massimo 5 curve e, in generale, una curva molto più lunga
|
|
// delle altre ( quella che collega le due estensioni calcolate). Spezzo la composita ottenuta in curve
|
|
// la cui lunghezza massima è circa il raggio utensile
|
|
PolyLine PL ;
|
|
pZigZagLink->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL) ;
|
|
PL.AdjustForMaxSegmentLen( PockParams.dRad + 10 * EPS_SMALL) ;
|
|
pZigZagLink->Clear() ;
|
|
pZigZagLink->FromPolyLine( PL) ;
|
|
// Smusso il raccordo a ZigZag
|
|
ModifyCurveToSmoothed( pZigZagLink, PockParams, .4, .4, true) ;
|
|
pCrvLink->CopyFrom( pZigZagLink) ;
|
|
return ( pCrvLink != nullptr && pCrvLink->IsValid() && pCrvLink->GetCurveCount() > 0) ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static bool
|
|
CalcBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd,
|
|
const Vector3d& vtEnd, double dParMeet, const ICRVCOMPOPOVECTOR& vCrvBorders,
|
|
const PocketParams& PockParams, ICurveComposite* pCrvLink, bool& bSpecial)
|
|
{
|
|
// controllo parametri
|
|
if ( pCrvLink == nullptr)
|
|
return false ;
|
|
pCrvLink->Clear() ;
|
|
|
|
// se senza smusso (e non caso circonferenza), ritorno tratto lineare
|
|
bSpecial = false ;
|
|
if ( ! PockParams.bSmooth && dParMeet > EPS_ZERO)
|
|
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
|
|
|
|
// inizializzo la curva di Link da restituire
|
|
PtrOwner<ICurveComposite> pMyCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pMyCrvLink))
|
|
return false ;
|
|
|
|
// controllo se la distanza lineare tra i due punti è maggiore della tolleranza...
|
|
const double dSqTol = 4 * PockParams.dRad * PockParams.dRad + PockParams.dSideStep * PockParams.dSideStep ;
|
|
if ( SqDist( ptStart, ptEnd) > dSqTol)
|
|
bSpecial = ( CalcSpecialBoundedSmoothedLink( ptStart, vtStart, ptEnd, vtEnd, PockParams, pMyCrvLink)) ;
|
|
if ( ! pMyCrvLink->IsValid() || pMyCrvLink->GetCurveCount() == 0) {
|
|
// ...in caso negativo creo il Biarco
|
|
double dAngStart ; vtStart.GetAngleXY( X_AX, dAngStart) ;
|
|
double dAngEnd ; vtEnd.GetAngleXY( X_AX, dAngEnd) ;
|
|
if ( dParMeet > EPS_ZERO)
|
|
pMyCrvLink.Set( ConvertCurveToComposite( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, dParMeet))) ;
|
|
else {
|
|
// ... o una circonferenza, orientata in modo da poter essere classificata con le curve di bordo
|
|
PtrOwner<CurveArc> pCrvCir( CreateBasicCurveArc()) ;
|
|
if ( IsNull( pCrvCir))
|
|
return false ;
|
|
if ( pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, PockParams.bInvert ? 360 : -360, 0, Z_AX))
|
|
pMyCrvLink.Set( ConvertCurveToComposite( Release( pCrvCir))) ;
|
|
}
|
|
}
|
|
if ( ! pMyCrvLink->IsValid() || pMyCrvLink->GetCurveCount() == 0)
|
|
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
|
|
|
|
// se il BiArco creato è troppo piccolo, lo approssimo ad un tratto lineare
|
|
BBox3d bBox3 ;
|
|
if ( pMyCrvLink->GetLocalBBox( bBox3)) {
|
|
double dRadBB ;
|
|
if ( bBox3.GetRadius( dRadBB) && dRadBB < 500 * EPS_SMALL)
|
|
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
|
|
}
|
|
|
|
// il link ottenuto non deve uscire dalle curve di bordo ( Loop esterno ed Isole)
|
|
// curva di test
|
|
PtrOwner<ICurveComposite> pCompoTest( CloneCurveComposite( pMyCrvLink)) ;
|
|
if ( IsNull( pCompoTest) || ! pCompoTest->IsValid())
|
|
return false ;
|
|
// curva ausiliaria
|
|
PtrOwner<ICurveComposite> pCompoHelp( CloneCurveComposite( pMyCrvLink)) ;
|
|
if ( IsNull( pCompoHelp) || ! pCompoHelp->IsValid())
|
|
return false ;
|
|
|
|
// scorro tutte le curve le curve di bordo
|
|
for ( const ICurveComposite* pCrvBorder : vCrvBorders) {
|
|
// calcolo eventuali intersezioni
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pCompoTest, *pCrvBorder) ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
|
|
// pulisco la curva ausiliaria
|
|
pCompoHelp->Clear() ;
|
|
|
|
// analizzo le intersezioni
|
|
for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) {
|
|
// se ho intersezione spezzo il segmento
|
|
if ( ccClass[i].nClass == CRVC_OUT && int( ccClass.size()) > 1) {
|
|
// recupero eventuali punti di intersezioni e parametri
|
|
Point3d ptS ; pCompoTest->GetPointD1D2( ccClass[i].dParS, ICurve::FROM_PLUS, ptS) ;
|
|
double dOffS ; pCrvBorder->GetParamAtPoint( ptS, dOffS, 1500 * EPS_SMALL) ;
|
|
Point3d ptE ; pCompoTest->GetPointD1D2( ccClass[i].dParE, ICurve::FROM_MINUS, ptE) ;
|
|
double dOffE ; pCrvBorder->GetParamAtPoint( ptE, dOffE, 1500 * EPS_SMALL) ;
|
|
// recupero i due possibili percorsi e uso il più corto
|
|
PtrOwner<ICurve> pCrvA( pCrvBorder->CopyParamRange( dOffS, dOffE)) ;
|
|
PtrOwner<ICurve> pCrvB( pCrvBorder->CopyParamRange( dOffE, dOffS)) ;
|
|
if ( IsNull( pCrvA) || IsNull( pCrvB))
|
|
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
|
|
double dLenA ; pCrvA->GetLength( dLenA) ;
|
|
double dLenB ; pCrvB->GetLength( dLenB) ;
|
|
// definisco il nuovo bordo mediante la curva ausiliaria
|
|
if ( i != 0) {
|
|
if ( dLenA < dLenB) {
|
|
if ( ! pCompoHelp->AddCurve( Release( pCrvA)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
|
|
}
|
|
else {
|
|
pCrvB->Invert() ;
|
|
if ( ! pCompoHelp->AddCurve( Release( pCrvB)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
|
|
}
|
|
}
|
|
else {
|
|
pCrvB->Invert() ;
|
|
if ( ! pCompoHelp->AddCurve( Release( pCrvB)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
|
|
}
|
|
}
|
|
// se non interseco
|
|
else {
|
|
if ( ! pCompoHelp->AddCurve( pCompoTest->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE)))
|
|
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
|
|
}
|
|
}
|
|
// la curva di test diventa la curva ausiliaria
|
|
pCompoTest->Clear() ;
|
|
if ( ! pCompoTest->AddCurve( pCompoHelp->Clone()))
|
|
if ( ! CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink))
|
|
return false ;
|
|
}
|
|
// La curva necessita di un ulteriore smusso, in quanto tagliata su un bordo
|
|
ModifyCurveToSmoothed( pCompoTest, PockParams, PockParams.dSmooth, PockParams.dSmooth, false) ;
|
|
// nel caso speciale della circonferenza, devo impostare il punto iniziale
|
|
if ( dParMeet < EPS_ZERO) {
|
|
if ( pCompoTest->IsClosed()) { // sempre...
|
|
double dU = 0. ;
|
|
int nFlag = 0 ;
|
|
if ( DistPointCurve( ptStart, *pCompoTest).GetParamAtMinDistPoint( 0., dU, nFlag))
|
|
pCompoTest->ChangeStartPoint( dU) ;
|
|
}
|
|
}
|
|
|
|
// restituisco il Link
|
|
if ( IsNull( pCompoTest) || ! pCompoTest->IsValid() || pCompoTest->GetCurveCount() == 0)
|
|
return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ;
|
|
pCrvLink->AddCurve( Release( pCompoTest)) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, const ICRVCOMPOPOVECTOR& vFirstOffset,
|
|
const PocketParams& PockParams, double dLenPercS, double dLenPercE,
|
|
ICurveComposite* pCrvLink)
|
|
{
|
|
// controllo i parametri
|
|
if ( pCrvS == nullptr || pCrvE == nullptr )
|
|
return false ;
|
|
pCrvLink->Clear() ;
|
|
|
|
// curva finale da restituire
|
|
PtrOwner<CurveComposite> pCrvFinal( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCrvFinal))
|
|
return false ;
|
|
|
|
// recupero gli estremi delle due curve ( S = Start, E = End)
|
|
Point3d ptSS ; pCrvS->GetStartPoint( ptSS) ;
|
|
Point3d ptSE ; pCrvS->GetEndPoint( ptSE) ;
|
|
Point3d ptES ; pCrvE->GetStartPoint( ptES) ;
|
|
Point3d ptEE ; pCrvE->GetEndPoint( ptEE) ;
|
|
// recupero i versori per il BiArco
|
|
Vector3d vS ; pCrvS->GetEndDir( vS) ;
|
|
Vector3d vE ; pCrvE->GetStartDir( vE) ;
|
|
// recupero i domini delle curve
|
|
double dUSS, dUSE ; pCrvS->GetDomain( dUSS, dUSE) ;
|
|
double dUES, dUEE ; pCrvE->GetDomain( dUES, dUEE) ;
|
|
// recupero le lunghezze delle curve
|
|
double dLenS ; pCrvS->GetLength( dLenS) ;
|
|
double dLenE ; pCrvE->GetLength( dLenE) ;
|
|
|
|
// azzero i parametri per curve di primo Offset
|
|
for ( int i = 0 ; i < int( vFirstOffset.size()) ; ++ i) {
|
|
if ( vFirstOffset[i]->IsPointOn( ptSS) && vFirstOffset[i]->IsPointOn( ptSE))
|
|
dLenPercS = 0. ;
|
|
#if 0
|
|
if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE))
|
|
dLenPercE = 0. ;
|
|
#endif
|
|
}
|
|
#if 0 // nel caso volessi estendere anche prima del punto finale
|
|
double dLStepS = ( dLenPercS < EPS_SMALL ? 0. : PockParams.dRad) ;
|
|
#endif
|
|
double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : PockParams.dRad) ;
|
|
dLenE = 0 ;
|
|
|
|
// recupero il numero massimo di iterazioni per il calcolo del BiArco
|
|
int nMaxIter = 2 ;
|
|
if ( ! PockParams.bSmooth)
|
|
nMaxIter = 1 ;
|
|
|
|
// taglio la curva al massimo nMaxIter-volte e mi fermo al taglio più opportuno
|
|
int nIter = 0 ;
|
|
while ( nIter < nMaxIter) {
|
|
|
|
// calcolo il biArco
|
|
bool bSpecial = false ;
|
|
PtrOwner<CurveComposite> pCrvBiArc( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCrvBiArc) ||
|
|
! CalcBoundedSmoothedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, PockParams, pCrvBiArc, bSpecial) ||
|
|
pCrvBiArc->GetCurveCount() == 0)
|
|
return false ;
|
|
pCrvFinal->Clear() ;
|
|
pCrvFinal.Set( pCrvBiArc->Clone()) ;
|
|
|
|
// se non devo cercarne altri, allora tengo quest'ultimo
|
|
if ( ( dLenPercE == 0 && dLenPercS == 0) || bSpecial)
|
|
break ;
|
|
// altrimenti, calcolo un nuovo BiArco, estendendo i suoi estremi
|
|
#if 0 // nel caso volessi estendere anche prima del punto finale
|
|
dLenS -= dLStepS ;
|
|
#endif
|
|
dLenE += dLStepE ;
|
|
|
|
// ricalcolo il punto iniziale e finale
|
|
pCrvS->GetParamAtLength( dLenS, dUSE) ;
|
|
if ( ! pCrvS->GetPointD1D2( dUSE, ICurve::FROM_MINUS, ptSE, &vS))
|
|
return false ;
|
|
vS.Normalize() ;
|
|
pCrvE->GetParamAtLength( dLenE, dUES) ;
|
|
if ( ! pCrvE->GetPointD1D2( dUES, ICurve::FROM_PLUS, ptES, &vE))
|
|
return false ;
|
|
vE.Normalize() ;
|
|
|
|
++ nIter ;
|
|
}
|
|
|
|
pCrvLink->AddCurve( Release( pCrvFinal)) ; // ultimo arco valido trovato
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
static bool
|
|
GetUnclearedRegionAndSetFeed( const ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks,
|
|
const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ISurfFlatRegion* pSfrUncleared)
|
|
{
|
|
// se non devo nè calcolare la Feed nè calcolare le regioni svuotate, allora non faccio nulla
|
|
if ( ! PockParams.bCalcFeed && ! PockParams.bCalcUnclearedRegs)
|
|
return true ;
|
|
|
|
// controllo dei parametri
|
|
if ( vFirstOffs.empty() || ssize( vOffs) < ssize( vFirstOffs))
|
|
return false ;
|
|
pSfrUncleared->Clear() ;
|
|
|
|
// creo la regione esterna ( definita dalle curve di primo Offset)
|
|
// NB. Questa regione è diversa dalla pSfrOrig :
|
|
// - i tratti chiusi sono Offsettati di R + OffsR()
|
|
// - i tratti aperti sono stati estesi all'esterno di PockParams.dMaxOpenEdgeRad
|
|
PtrOwner<ISurfFlatRegion> pSrfExtern( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfExtern))
|
|
return false ;
|
|
for ( int i = 0 ; i < ssize( vFirstOffs) ; ++ i) {
|
|
if ( i == 0)
|
|
pSrfExtern->AddExtLoop( vFirstOffs[i]->Clone()) ;
|
|
else {
|
|
PtrOwner<ICurve> pCrvIntLoop( vFirstOffs[i]->Clone()) ;
|
|
if ( IsNull( pCrvIntLoop) || ! pCrvIntLoop->Invert() || ! pSrfExtern->AddIntLoop( Release( pCrvIntLoop)))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// creo la regione che all'esterno contiene i lati aperti
|
|
// NB. Prendendo la superficie originale ( con lati Open/Close impostati), se effettuo un
|
|
// Offset di una piccola quantità, tutte le curve esterne a tale regione sono associate a lati aperti
|
|
const double OPEN_OFFS_TOL = 1000. * EPS_SMALL ;
|
|
PtrOwner<ISurfFlatRegion> pSfrForOpenEdges( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrForOpenEdges))
|
|
return false ;
|
|
if ( pSfrOrig != nullptr && pSfrOrig->IsValid()) {
|
|
if ( ! pSfrForOpenEdges.Set( CloneSurfFlatRegion( pSfrOrig)) ||
|
|
! pSfrForOpenEdges->Offset( OPEN_OFFS_TOL, ICurve::OFF_FILLET))
|
|
return false ;
|
|
}
|
|
|
|
// definisco la regione rimossa dall'utensile
|
|
PtrOwner<ISurfFlatRegion> pSfrTool( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrTool))
|
|
return false ;
|
|
|
|
// creo un vettore che conterrà solamente i Link percorsi fino ad ora
|
|
// NB. Per SpiralOut devo scorrere le curva di Offset in maniera inversa
|
|
bool bFollowOrder = ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_CONFORMAL_ZIGZAG) ;
|
|
for ( int i = ( bFollowOrder ? 0 : ssize( vOffs) - 1) ; ( bFollowOrder ? i < ssize( vOffs) : i >= 0) ; ( bFollowOrder ? ++ i : -- i)) {
|
|
|
|
// ================== OFFSET =============================
|
|
// recupero le proprietà temporanee
|
|
int nProp0 = vOffs[i]->GetTempProp( 0) ;
|
|
int nProp1 = vOffs[i]->GetTempProp( 1) ;
|
|
// il primo TempParam viene settato in proporzione alla Feed
|
|
// il secondo TempParam contiene l'nSide della curva
|
|
double dTempPar1 = vOffs[i]->GetTempParam( 1) ;
|
|
|
|
// Feed
|
|
bool bFirstOffs = false ;
|
|
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY)
|
|
bFirstOffs = true ;
|
|
else {
|
|
Point3d ptStart ; vOffs[i]->GetStartPoint( ptStart) ;
|
|
for ( int j = 0 ; j < ssize( vFirstOffs) && ! bFirstOffs ; ++ j)
|
|
bFirstOffs = vFirstOffs[j]->IsPointOn( ptStart, 100. * EPS_SMALL) ;
|
|
}
|
|
// calcolo gli intervalli di Feed per la curva di Offset
|
|
if ( ! AssignFeedSpiral( vOffs[i], pSfrTool, false, bFirstOffs, pSfrForOpenEdges, PockParams, 2. * PockParams.dRad / 3.))
|
|
return false ;
|
|
// aggiorno la superificie svuotata
|
|
PtrOwner<ISurfFlatRegion> pSrfToolOffs( GetSurfFlatRegionFromFatCurve( vOffs[i]->Clone() , PockParams.dRad + 5. * EPS_SMALL,
|
|
false, false, 10. * EPS_SMALL, false)) ;
|
|
if ( ! IsNull( pSrfToolOffs)) {
|
|
if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0)
|
|
pSfrTool.Set( pSrfToolOffs) ;
|
|
else
|
|
pSfrTool->Add( *pSrfToolOffs) ;
|
|
}
|
|
vOffs[i]->SetTempProp( nProp0, 0) ;
|
|
vOffs[i]->SetTempProp( nProp1, 1) ;
|
|
vOffs[i]->SetTempParam( dTempPar1, 1) ;
|
|
|
|
// ================= LINK ================================
|
|
int nIndLink = ( bFollowOrder ? i + 1 : i) ;
|
|
// ( Il primo link è nullo, infatti non vi è nessun raccordo per raggiungere il primo Offset)
|
|
if ( nIndLink < int( vLinks.size()) && ! IsNull( vLinks[nIndLink]) && vLinks[nIndLink]->IsValid()) {
|
|
// recupero le proprietà temporanee
|
|
int nProp0 = vLinks[nIndLink]->GetTempProp( 0) ;
|
|
int nProp1 = vLinks[nIndLink]->GetTempProp( 1) ;
|
|
// Feed
|
|
PtrOwner<ICurveComposite> pCrvLink( ConvertCurveToComposite( vLinks[nIndLink]->Clone())) ;
|
|
// calcolo gli intervalli di Feed per la curva di Link
|
|
if ( IsNull( pCrvLink) || ! pCrvLink->IsValid() ||
|
|
! AssignFeedSpiral( pCrvLink, pSfrTool, true, false, pSfrForOpenEdges, PockParams, 2. * PockParams.dRad / 3.))
|
|
return false ;
|
|
// aggiorno la regione svuotata
|
|
PtrOwner<ISurfFlatRegion> pSfrToolLink( GetSurfFlatRegionFromFatCurve( pCrvLink->Clone(), PockParams.dRad + 10. * EPS_SMALL,
|
|
false, false, 10. * EPS_SMALL, false)) ;
|
|
if ( ! IsNull( pSfrToolLink)) {
|
|
if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0)
|
|
pSfrTool.Set( pSfrToolLink) ;
|
|
else
|
|
pSfrTool->Add( *pSfrToolLink) ;
|
|
}
|
|
// Imposto il Link come Curva Composita
|
|
pCrvLink->SetTempProp( nProp0, 0) ;
|
|
pCrvLink->SetTempProp( nProp1, 1) ;
|
|
vLinks[nIndLink].Set( pCrvLink) ;
|
|
}
|
|
|
|
}
|
|
|
|
// se rischiesto, creo la regione contenente tutte le parti non svuotate
|
|
if ( PockParams.bCalcUnclearedRegs) {
|
|
pSfrUncleared->CopyFrom( pSrfExtern) ;
|
|
if ( ! pSfrUncleared->Subtract( *pSfrTool))
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ChooseCurveForRemovingUnclearedRegion( const ICRVCOMPOPOVECTOR& vOffs, const ICRVCOMPOPOVECTOR& vOffsFirstCurve,
|
|
const Point3d& ptToGo, const PocketParams& PockParams,
|
|
int& nInd, Point3d& ptCloser, bool& bFirstOffs)
|
|
{
|
|
// E' richiesto che le curve di Offset presentino le seguenti proprietà :
|
|
// - CONFORMAL
|
|
// * TempProp0 -> Offset progressivo ricavato ( 0, 1, 2, ... nMaxIter)
|
|
// * TempProp1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT)
|
|
// - SPIRAL
|
|
// * vOffsFirstCurve -> per capire se l'Offset corrente è di primo Offset o meno
|
|
// * TempParam1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT)
|
|
|
|
// controllo dei parametri
|
|
nInd = -1 ;
|
|
if ( vOffs.empty())
|
|
return true ;
|
|
|
|
// scorro tutte le curve di Offset
|
|
int nFlag ;
|
|
struct OffsPtMinDist {
|
|
int nInd = 0 ;
|
|
double dSqDist = INFINITO - 1 ;
|
|
Point3d ptMinDist = P_INVALID ;
|
|
bool bInVsOut = true ;
|
|
bool bFirstOffs = false ;
|
|
} ;
|
|
vector<OffsPtMinDist> vOffsPtMinDist( vOffs.size()) ;
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
|
|
// indice dell'Offset
|
|
vOffsPtMinDist[i].nInd = i ;
|
|
// nel caso di Conformal, escludo le curve di primo Offset dalla ricerca
|
|
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
|
|
// prima iterazione
|
|
if ( vOffs[i]->GetTempProp( 0) == 0) {
|
|
vOffsPtMinDist[i].bFirstOffs = true ;
|
|
continue ;
|
|
}
|
|
}
|
|
// Controllo se il punto da raggiungere è interno o esterno alla curva
|
|
// NB. cerco di privilegiare le curve che presentano il punto da raggiungere al loro esterno;
|
|
// in questo modo riesco a creare le curve a ricciolo
|
|
int nSide ;
|
|
DistPointCurve DistPtCrv( ptToGo, *vOffs[i]) ;
|
|
if ( DistPtCrv.GetSideAtMinDistPoint( EPS_SMALL, Z_AX, nSide)) {
|
|
// nel caso Spiral
|
|
if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_SPIRALOUT) {
|
|
// InVsOut
|
|
vOffsPtMinDist[i].bInVsOut = ( int( vOffs[i]->GetTempParam( 1) != nSide)) ;
|
|
// controllo se la curva in questione è di primo Offset
|
|
Point3d ptCheck ; vOffs[i]->GetStartPoint( ptCheck) ;
|
|
for ( int j = 0 ; j < int( vOffsFirstCurve.size()) ; ++ j) {
|
|
if ( vOffsFirstCurve[j]->IsPointOn( ptCheck, 100 * EPS_SMALL)) {
|
|
vOffsPtMinDist[i].bFirstOffs = true ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
// nel caso Conformal
|
|
else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
|
|
if ( nSide != vOffs[i]->GetTempProp( 1))
|
|
vOffsPtMinDist[i].bInVsOut = false ;
|
|
}
|
|
// recupero il punto a minima distanza e la distanza
|
|
DistPtCrv.GetMinDistPoint( EPS_SMALL, vOffsPtMinDist[i].ptMinDist, nFlag) ;
|
|
DistPtCrv.GetSqDist( vOffsPtMinDist[i].dSqDist) ;
|
|
// se il punto da raggiungere è interno alla curva, aggiungo una penalità ( euristica)
|
|
if ( vOffsPtMinDist[i].bInVsOut) {
|
|
vOffsPtMinDist[i].dSqDist += PockParams.dRad + PockParams.dRad +
|
|
PockParams.dSideStep + PockParams.dSideStep ;
|
|
}
|
|
// se curva di bordo esterno, aggiungo un'altra penalità ( euristica)
|
|
if ( vOffsPtMinDist[i].bFirstOffs)
|
|
vOffsPtMinDist[i].dSqDist *= 2. ;
|
|
}
|
|
}
|
|
// ordino il vettore in base alle distanze calcolate
|
|
sort( vOffsPtMinDist.begin(), vOffsPtMinDist.end(), []( const OffsPtMinDist& a, const OffsPtMinDist& b) {
|
|
return a.dSqDist < b.dSqDist ;
|
|
}) ;
|
|
// recupero la curva migliore
|
|
OffsPtMinDist& BestOffs = vOffsPtMinDist[0] ;
|
|
nInd = BestOffs.nInd ;
|
|
ptCloser = BestOffs.ptMinDist ;
|
|
bFirstOffs = BestOffs.bFirstOffs ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
RemoveUnclearedRegions( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs,
|
|
ICRVCOMPOPOVECTOR& vOffsFirstCurve, const PocketParams& PockParams)
|
|
{
|
|
// se non devo calcolare le regioni non svuotate, esco subito
|
|
if ( ! PockParams.bCalcUnclearedRegs)
|
|
return true ;
|
|
|
|
// E' richiesto che le curve di Offset presentino le seguenti proprietà :
|
|
// - CONFORMAL
|
|
// * TempProp0 -> Offset progressivo ricavato ( 0, 1, 2, ... nMaxIter)
|
|
// * TempProp1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT)
|
|
// - SPIRAL
|
|
// * vOffsFirstCurve -> per capire se l'Offset corrente è di primo Offset o meno
|
|
// * TempParam1 -> parte esterna della curva ( MDS_LEFT o MDS_RIGHT)
|
|
|
|
// se non ho nessuna superficie valida da svuotare, allora esco
|
|
if ( pSfrUncleared == nullptr)
|
|
return false ;
|
|
if ( ! pSfrUncleared->IsValid() || pSfrUncleared->GetChunkCount() == 0)
|
|
return true ;
|
|
|
|
// scorro tutti i Chunk della regione
|
|
ICRVCOMPOPOVECTOR vCrvCurl ;
|
|
for ( int nC = 0 ; nC < pSfrUncleared->GetChunkCount() ; ++ nC) {
|
|
// recupero il centroide del Chunk da rimuovere
|
|
Point3d ptCentroid ; pSfrUncleared->GetChunkCentroid( nC, ptCentroid) ;
|
|
// Tra tutte le curve di Offset escludo le curve che :
|
|
// - Sono di primo Offset
|
|
// - Non contengono il centroide ( condizione già verificata per quelle di primo Offset)
|
|
// ... cerco la più vicina tra le rimanenti
|
|
// recupero la curva migliore
|
|
int nInd = -1 ;
|
|
Point3d ptCloser ;
|
|
bool bFirstOffs = false ;
|
|
if ( ! ChooseCurveForRemovingUnclearedRegion( vOffs, vOffsFirstCurve, ptCentroid, PockParams,
|
|
nInd, ptCloser, bFirstOffs))
|
|
return false ;
|
|
// se nessun indice trovato, salto il chunk
|
|
if ( nInd == -1)
|
|
continue ;
|
|
|
|
// cerco sulla curva di Offset nInd-esima la sottocurva dove cade il punto a minima distanza
|
|
double dURef = 0 ;
|
|
if ( ! vOffs[nInd]->GetParamAtPoint( ptCloser, dURef))
|
|
continue ;
|
|
int nCrv = int( floor( dURef)) ;
|
|
const ICurve* pCrv = vOffs[nInd]->GetCurve( nCrv) ;
|
|
if ( pCrv == nullptr || ! pCrv->IsValid())
|
|
continue ;
|
|
|
|
// determino se la curva è di raccordo o meno
|
|
bool bIsSmoothCrv = false ;
|
|
// se non di primo Offset allora leggo la proprietà
|
|
if ( ! bFirstOffs)
|
|
bIsSmoothCrv = ( PockParams.bSmooth && pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ;
|
|
else
|
|
bIsSmoothCrv = ( pCrv->GetTempProp( 1) > 0) ;
|
|
|
|
// clono la curva di Offset corrente ( in questo modo non modifico l'originale)
|
|
PtrOwner<ICurveComposite> pMyOffs( CloneCurveComposite( vOffs[nInd])) ;
|
|
if ( IsNull( pMyOffs) || ! pMyOffs->IsValid())
|
|
return false ;
|
|
|
|
// versore uscente ed entrante dall'Offset attuale per creazione della curva a ricciolo
|
|
Vector3d vt1, vt2, vtMain ;
|
|
// punto di uscita e di ingresso dall'Offset attuale per creazione della curva a ricciolo
|
|
Point3d pt1, pt2, ptMain ;
|
|
// paramtro di uscita e di ingresso dall'Offset attuale per creazione della curva a ricciolo
|
|
double dURef1, dURef2 ;
|
|
// numero di curve dell'offset corrente
|
|
int nCrvNumber = ( pMyOffs->GetCurveCount()) ;
|
|
// inizializzo la curva a ricciolo
|
|
PtrOwner<ICurveComposite> pCrvCurl( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvCurl))
|
|
return false ;
|
|
|
|
// se è di raccordo, ricostruisco lo spigolo vivo della curva originale di Offset
|
|
if ( bIsSmoothCrv) {
|
|
vt1 = V_INVALID ;
|
|
vt2 = V_INVALID ;
|
|
// cerco la prima sottocurva che non sia di raccordo in precedenza
|
|
for ( int i = ( nCrv + nCrvNumber - 1) % nCrvNumber ; ( i % nCrvNumber) == i ; -- i) {
|
|
// recupero la sottocurva
|
|
const ICurve* pCrvPrec = pMyOffs->GetCurve( i) ;
|
|
if ( pCrvPrec == nullptr || ! pCrvPrec->IsValid())
|
|
break ;
|
|
// se non di raccordo
|
|
if ( pCrvPrec->GetTempProp( 1) != TEMP_PROP_SMOOTH) {
|
|
// definisco il versore uscente
|
|
pCrvPrec->GetEndDir( vt1) ;
|
|
// definisco il punto di uscita
|
|
pCrvPrec->GetEndPoint( pt1) ;
|
|
// definisco il parametro di uscita
|
|
dURef1 = ( i + 1) % nCrvNumber ;
|
|
break ;
|
|
}
|
|
}
|
|
bIsSmoothCrv = ( vt1.IsValid()) ;
|
|
if ( bIsSmoothCrv) {
|
|
// cerco la prima sottocurva che non sia di raccordo in sucessione
|
|
for ( int i = ( nCrv + nCrvNumber + 1) % nCrvNumber ; ( i % nCrvNumber) == i ; ++ i) {
|
|
// recupero la sottocurva
|
|
const ICurve* pCrvSucc = pMyOffs->GetCurve( i) ;
|
|
if ( pCrvSucc == nullptr || ! pCrvSucc->IsValid())
|
|
break ;
|
|
// se non di raccordo
|
|
if ( pCrvSucc->GetTempProp( 1) != TEMP_PROP_SMOOTH) {
|
|
// definisco il versore entrante
|
|
pCrvSucc->GetStartDir( vt2) ;
|
|
// definisco il punto di entrata
|
|
pCrvSucc->GetStartPoint( pt2) ;
|
|
// definisco il parametro di entrata
|
|
dURef2 = ( i % nCrvNumber) ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
bIsSmoothCrv = bIsSmoothCrv && ( vt2.IsValid()) ;
|
|
if ( bIsSmoothCrv) {
|
|
// trovo il punto di intersezione tra le due direzioni tangenti applicate
|
|
const double EXTENSION_LEN = 1000. ;
|
|
// segmento iniziale -------
|
|
PtrOwner<ICurveLine> pLineS( CreateCurveLine()) ;
|
|
if ( IsNull( pLineS) || ! pLineS->Set( pt1, pt1 + vt1 * EXTENSION_LEN))
|
|
continue ;
|
|
// segmento finale --------
|
|
PtrOwner<ICurveLine> pLineE( CreateCurveLine()) ;
|
|
if ( IsNull( pLineE) || ! pLineE->Set( pt2, pt2 - vt2 * EXTENSION_LEN))
|
|
continue ;
|
|
// intersezione
|
|
IntersCurveCurve ILL( *pLineS, *pLineE) ;
|
|
bIsSmoothCrv = ( ILL.GetCrossIntersCount() == 1) ;
|
|
if ( bIsSmoothCrv) {
|
|
IntCrvCrvInfo aInfo ;
|
|
bIsSmoothCrv = ( ( ILL.GetIntCrvCrvInfo( 0, aInfo)) &&
|
|
pCrvCurl->AddPoint( pt1) &&
|
|
pCrvCurl->AddLine( aInfo.IciA[0].ptI)) ;
|
|
if ( bIsSmoothCrv)
|
|
ptMain = aInfo.IciA[0].ptI ;
|
|
}
|
|
}
|
|
}
|
|
// se non è di raccordo, allora ho già lo spigolo vivo
|
|
if ( ! bIsSmoothCrv) {
|
|
pCrvCurl->Clear() ;
|
|
// definisco il versore entrante ed uscente
|
|
// definisco il punto di entrata e di uscita ( sono coincidenti)
|
|
dURef1 = dURef ;
|
|
dURef2 = dURef ;
|
|
if ( ! vOffs[nInd]->GetPointD1D2( dURef1, ICurve::FROM_MINUS, ptMain, &vt1) ||
|
|
! vOffs[nInd]->GetPointD1D2( dURef2, ICurve::FROM_PLUS, ptMain, &vt2))
|
|
continue ;
|
|
vt1.Normalize() ; vt2.Normalize() ;
|
|
}
|
|
// recupero le due direzioni per i Biarchi
|
|
bool bSameDir = AreSameVectorEpsilon( vt1, vt2, 2 * EPS_SMALL) ;
|
|
bool bSpecial = false ;
|
|
// se parallele, allora trasformo in una circonferenza
|
|
if ( bSameDir) {
|
|
if ( ! CalcBoundedSmoothedLink( ptMain, vt1, ptCentroid, vt2, 0., vOffsFirstCurve, PockParams, pCrvCurl, bSpecial))
|
|
continue ;
|
|
Vector3d vtStartCheck ; pCrvCurl->GetStartDir( vtStartCheck) ;
|
|
if ( AreOppositeVectorApprox( vtStartCheck, vt1))
|
|
pCrvCurl->Invert() ;
|
|
}
|
|
// se distinte...
|
|
else {
|
|
// calcolo la direzione tra pt1 e ptCentroid
|
|
Vector3d vtDir = ( ptCentroid - ptMain) ; vtDir.Normalize() ;
|
|
vtDir.Rotate( Z_AX, ANG_RIGHT) ;
|
|
double dAngMain ; bool bDet ;
|
|
vt1.GetRotation( vt2, Z_AX, dAngMain, bDet) ;
|
|
if ( dAngMain > - EPS_ZERO)
|
|
vtDir.Invert() ;
|
|
// calcolo i due Biarchi di raccordo
|
|
PtrOwner<ICurveComposite> pCrvBiArc1( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvBiArc2( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvBiArc1) || IsNull( pCrvBiArc2) ||
|
|
! CalcBoundedSmoothedLink( ptMain, vt1, ptCentroid, vtDir, bSameDir ? 0. : 0.5, vOffsFirstCurve, PockParams, pCrvBiArc1, bSpecial) ||
|
|
! CalcBoundedSmoothedLink( ptCentroid, vtDir, ptMain, vt2, bSameDir ? 0. : 0.5, vOffsFirstCurve, PockParams, pCrvBiArc2, bSpecial))
|
|
continue ;
|
|
// aggiorno la curva a ricciolo
|
|
if ( ! pCrvCurl->AddCurve( Release( pCrvBiArc1)) ||
|
|
! pCrvCurl->AddCurve( Release( pCrvBiArc2)) ||
|
|
! pCrvCurl->IsValid())
|
|
continue ;
|
|
}
|
|
// controllo se devo raccordarmi con Offset
|
|
if ( bIsSmoothCrv)
|
|
pCrvCurl->AddLine( pt2) ;
|
|
|
|
// controllo se tale curva effettivamente rimuove il Chunk nC-esimo
|
|
PtrOwner<ISurfFlatRegion> pSfrFatCurl( GetSurfFlatRegionFromFatCurve( pCrvCurl->Clone(), PockParams.dRad, false, false)) ;
|
|
if ( IsNull( pSfrFatCurl) || ! pSfrFatCurl->IsValid())
|
|
continue ;
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfrUncleared->CloneChunk( nC)) ;
|
|
if ( IsNull( pSfrFatCurl) || ! pSfrFatCurl->IsValid())
|
|
return false ;
|
|
pSfrChunk->Subtract( *pSfrFatCurl) ;
|
|
|
|
// controllo se la regione è tutta rimossa
|
|
if ( pSfrChunk->IsValid() || pSfrChunk->GetChunkCount() > 0) {
|
|
// so che il punto più vicino alla curva è ptCloser
|
|
// ricavo dunque il punto più vicino sul bordo del Chunk nC-esimo
|
|
PtrOwner<ICurveComposite> pCompoChunkBorder( ConvertCurveToComposite( pSfrUncleared->GetLoop( nC, 0))) ;
|
|
if ( IsNull( pCompoChunkBorder) || ! pCompoChunkBorder->IsValid())
|
|
return false ;
|
|
if ( PockParams.bInvert)
|
|
pCompoChunkBorder->Invert() ;
|
|
DistPointCurve distPtChunkBorder( ptCloser, *pCompoChunkBorder) ;
|
|
Point3d ptCloserOnChunk ;
|
|
int nFlag ;
|
|
if ( ! distPtChunkBorder.GetMinDistPoint( EPS_SMALL, ptCloserOnChunk, nFlag))
|
|
continue ;
|
|
// cambio il punto di inizio della curva su tale punto
|
|
double dUS = 0. ;
|
|
pCompoChunkBorder->GetParamAtPoint( ptCloserOnChunk, dUS) ;
|
|
pCompoChunkBorder->ChangeStartPoint( dUS) ;
|
|
|
|
/*
|
|
Creo due curve composite rappresentanti un piccolo tratto tangente sulla curva di
|
|
Offset attuale; questi servono per la partenza e l'arrivo dei biArchi
|
|
*/
|
|
if ( bSameDir) {
|
|
pt1 = ptMain ;
|
|
pt2 = ptMain ;
|
|
}
|
|
PtrOwner<ICurveComposite> pCompoHelpS( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoHelpS) || ! pCompoHelpS->AddPoint( pt1 - vt1 / 4) ||
|
|
! vt1.Normalize() || ! pCompoHelpS->AddLine( pt1))
|
|
continue ;
|
|
PtrOwner<ICurveComposite> pCompoHelpE( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoHelpE) || ! pCompoHelpE->AddPoint( pt2) ||
|
|
! vt2.Normalize() || ! pCompoHelpE->AddLine( pt2 + vt2 / 4))
|
|
continue ;
|
|
|
|
/*
|
|
creo il Biarco iniziale
|
|
( nel caso non si riuscisse diventa un segmento lineare)
|
|
*/
|
|
PtrOwner<ICurveComposite> pCrvBiArc1( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvBiArc1))
|
|
return false ;
|
|
if ( ! CutCurveToConnect( pCompoHelpS, pCompoChunkBorder, vOffsFirstCurve, PockParams,
|
|
0, 10 * EPS_SMALL, pCrvBiArc1)) {
|
|
if ( ! CalcBoundedLink( ptCloser, ptCloserOnChunk, vOffsFirstCurve, pCrvBiArc1))
|
|
continue ;
|
|
}
|
|
// aggiorno i nuovi punti e i nuovi parametri
|
|
double dMyU ;
|
|
Point3d ptMyPoint ;
|
|
if ( ! pCrvBiArc1->GetEndPoint( ptMyPoint) ||
|
|
! pCompoChunkBorder->GetParamAtPoint( ptMyPoint, dMyU))
|
|
continue ;
|
|
// accorgio il bordo dell'Offset in raccordo al BiArco iniziale
|
|
if ( dMyU > EPS_SMALL)
|
|
pCompoChunkBorder->TrimStartAtParam( dMyU) ;
|
|
|
|
/*
|
|
creo il Biarco finale
|
|
( nel caso non si riuscisse diventa un segmento lineare)
|
|
*/
|
|
// creo il Biarco iniziale ( nel caso non si riuscisse diventa un segmento lineare)
|
|
PtrOwner<ICurveComposite> pCrvBiArc2( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvBiArc2))
|
|
return false ;
|
|
if ( ! CutCurveToConnect( pCompoChunkBorder, pCompoHelpE, vOffsFirstCurve, PockParams,
|
|
0., 0., pCrvBiArc2)) {
|
|
if ( ! CalcBoundedLink( ptCloserOnChunk, ptCloser, vOffsFirstCurve, pCrvBiArc2))
|
|
continue ;
|
|
}
|
|
// aggiorno i nuovi punti e i nuovi parametri
|
|
if ( ! pCrvBiArc2->GetStartPoint( ptMyPoint) ||
|
|
! pCompoChunkBorder->GetParamAtPoint( ptMyPoint, dMyU))
|
|
continue ;
|
|
|
|
// la curva a ricciolo ora diventa l'unione di pCrvBiArc1 - pCompoChunkBorder - pCrvBiArc2
|
|
pCrvCurl->Clear() ;
|
|
// smusso la curva di bordo del Chunk ( è aperta)
|
|
double dSmoothPar = PockParams.dRad / 8. ;
|
|
ModifyCurveToSmoothed( pCompoChunkBorder, PockParams, dSmoothPar, dSmoothPar, false) ;
|
|
if ( ! pCrvCurl->AddCurve( Release( pCrvBiArc1)) ||
|
|
! pCrvCurl->AddCurve( Release( pCompoChunkBorder)) ||
|
|
! pCrvCurl->AddCurve( Release( pCrvBiArc2)))
|
|
continue ;
|
|
}
|
|
|
|
// attribuisco alla curva a ricciolo la Feed minima
|
|
AssignMinFeed( pCrvCurl, PockParams) ;
|
|
|
|
// aggiorno il nuovo Offset aggiungendo la curva a ricciolo
|
|
PtrOwner<ICurveComposite> pCrvTest( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvTest))
|
|
return false ;
|
|
// aggiungo tratto iniziale di Offset
|
|
pCrvTest->AddCurve( ConvertCurveToComposite( pMyOffs->CopyParamRange( 0, dURef1))) ;
|
|
// aggiungo la curva a ricciolo
|
|
if ( ! pCrvTest->AddCurve( Release( pCrvCurl)))
|
|
continue ;
|
|
// aggiungo il tratto finale di Offset
|
|
pCrvTest->AddCurve( ConvertCurveToComposite( pMyOffs->CopyParamRange( dURef2, pMyOffs->GetCurveCount()))) ;
|
|
// provo a controllare che i punti inziali e finali coincidano, in caso aggiorno il nuovo Offset
|
|
Point3d ptStart_Old ; vOffs[nInd]->GetStartPoint( ptStart_Old) ;
|
|
Point3d ptEnd_Old ; vOffs[nInd]->GetEndPoint( ptEnd_Old) ;
|
|
Point3d ptStart_New ; pCrvTest->GetStartPoint( ptStart_New) ;
|
|
Point3d ptEnd_New ; pCrvTest->GetEndPoint( ptEnd_New) ;
|
|
if ( AreSamePointApprox( ptStart_Old, ptStart_New) && AreSamePointApprox( ptEnd_Old, ptEnd_New)) {
|
|
pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 0), 0) ;
|
|
pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 1), 1) ;
|
|
vOffs[nInd].Set( pCrvTest) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, const PocketParams& PockParams,
|
|
const ICRVCOMPOPOVECTOR& vOffsFirstCurve)
|
|
{
|
|
// controllo dei parametri
|
|
if ( vOffs.empty() || vOffsFirstCurve.empty())
|
|
return false ;
|
|
vLinks.clear() ;
|
|
vLinks.resize( int( vOffs.size())) ;
|
|
|
|
// NB. Dato che le curve di Offset sono invertite a seconda di dove si trova il materiale,
|
|
// Utilizzo il secondo TempParam per evere l'nSide
|
|
|
|
// scorro tutte le curva di Offset
|
|
for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) {
|
|
|
|
// ricavo il punto inziale della curva corrente
|
|
Point3d ptS ;
|
|
if ( ! vOffs[i]->GetStartPoint( ptS))
|
|
return false ;
|
|
|
|
// tra le curve successive cerco la curva interna e più vicina ad essa
|
|
int nNextInd = -1 ; // indice della curva successiva
|
|
double dMinDist = INFINITO ; // distanza tra questa curva e la successiva
|
|
Point3d ptStartNext ; // punto iniziale della curva successiva
|
|
bool bMinDistAtSmooth = false ; // se punto a minima distanza su curva di smusso
|
|
for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) {
|
|
IntersCurveCurve IntCC( *vOffs[i], *vOffs[j]) ;
|
|
CRVCVECTOR ccClass ;
|
|
// se interna
|
|
if ( IntCC.GetCurveClassification( 1, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 &&
|
|
( ( ccClass[0].nClass == CRVC_IN && int( vOffs[i]->GetTempParam( 1)) == MDS_RIGHT) ||
|
|
( ccClass[0].nClass == CRVC_OUT && int( vOffs[i]->GetTempParam( 1)) == MDS_LEFT))) {
|
|
// calcolo la distanza minima tra essa
|
|
int nFlag ;
|
|
double dPar = 0. ;
|
|
Point3d ptClosest ;
|
|
DistPointCurve DistPtCrv( ptS, *vOffs[j]) ;
|
|
if ( DistPtCrv.GetParamAtMinDistPoint( EPS_SMALL, dPar, nFlag)) {
|
|
// controllo se la curva ottenuta è di raccordo, in caso positivo considero come
|
|
// punto più vicino il suo punto finale ( per avere poi successivamente Link tra
|
|
// Offset in tangenza )
|
|
const ICurve* pCrv = vOffs[j]->GetCurve( static_cast<int>( floor( dPar))) ;
|
|
if ( pCrv != nullptr && pCrv->IsValid()) {
|
|
if ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH)
|
|
pCrv->GetEndPoint( ptClosest) ;
|
|
else
|
|
DistPtCrv.GetMinDistPoint( EPS_SMALL, ptClosest, nFlag) ;
|
|
double dCurrDist = SqDist( ptS, ptClosest) ;
|
|
if ( dCurrDist < dMinDist) {
|
|
dMinDist = dCurrDist ;
|
|
nNextInd = j ;
|
|
ptStartNext = ptClosest ;
|
|
bMinDistAtSmooth = ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// se non ho trovato nessuna curva interna... cerco semplicemente la curva più vicina
|
|
if ( nNextInd == -1) {
|
|
for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) {
|
|
int nFlag ;
|
|
double dPar = 0. ;
|
|
Point3d ptClosest ;
|
|
DistPointCurve DistPtCrv( ptS, *vOffs[j]) ;
|
|
if ( DistPtCrv.GetParamAtMinDistPoint( EPS_SMALL, dPar, nFlag)) {
|
|
const ICurve* pCrv = vOffs[j]->GetCurve( static_cast<int>( floor( dPar))) ;
|
|
if ( pCrv != nullptr && pCrv->IsValid()) {
|
|
if ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH)
|
|
pCrv->GetEndPoint( ptClosest) ;
|
|
else
|
|
DistPtCrv.GetMinDistPoint( EPS_SMALL, ptClosest, nFlag) ;
|
|
double dCurrDist = SqDist( ptS, ptClosest) ;
|
|
if ( dCurrDist < dMinDist) {
|
|
dMinDist = dCurrDist ;
|
|
nNextInd = j ;
|
|
ptStartNext = ptClosest ;
|
|
bMinDistAtSmooth = ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se non ho trovato nessuna curva, errore
|
|
if ( nNextInd == -1)
|
|
return false ;
|
|
|
|
// scambio la curva i+1 esima con la j-esima ( se non sono già in ordine)
|
|
if ( nNextInd != i + 1)
|
|
swap( vOffs[nNextInd], vOffs[i+1]) ;
|
|
|
|
// cambio il suo punto iniziale nel punto più vicino tovato
|
|
double dUS ;
|
|
if ( ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUS, EPS_SMALL))
|
|
return false ;
|
|
vOffs[i+1]->ChangeStartPoint( dUS) ;
|
|
|
|
// se ho più di una curva di Offset
|
|
if ( int( vOffs.size()) > 1) {
|
|
// clono le curve i ed i+1 esime ( nel caso non riuscissi ad accorciarle o raccordarle )
|
|
PtrOwner<ICurveComposite> pCrv_i( vOffs[i]->Clone()) ;
|
|
PtrOwner<ICurveComposite> pCrv_ii( vOffs[i+1]->Clone()) ;
|
|
if ( IsNull( pCrv_i) || IsNull( pCrv_ii) || ! pCrv_i->IsValid() || ! pCrv_ii->IsValid())
|
|
return false ;
|
|
// creo la curva che le collegherà
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
double dLenPercS = ( i == 0 ? 0. : 10 * EPS_SMALL) ;
|
|
double dLenPercE = ( bMinDistAtSmooth ? 0. : 10 * EPS_SMALL) ;
|
|
if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsFirstCurve, PockParams, dLenPercS, dLenPercE, pCrvLink) ||
|
|
! pCrvLink->IsValid()) {
|
|
// se non sono riuscito, cerco una strada più semplice ripristinando le curve
|
|
pCrvLink->Clear() ;
|
|
vOffs[i].Set( pCrv_i) ;
|
|
vOffs[i+1].Set( pCrv_ii) ;
|
|
// recupero i vettori tangenti iniziali ( le curve sono chiuse )
|
|
Vector3d vtS, vtE ;
|
|
if ( ! vOffs[i]->GetStartDir( vtS) || ! vOffs[i+1]->GetStartDir( vtE))
|
|
return false ;
|
|
// creo il bi-arco tra esse
|
|
bool bSpecial = false ;
|
|
if ( CalcBoundedSmoothedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink, bSpecial))
|
|
vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento
|
|
else
|
|
return false ;
|
|
continue ; // passo alla curva i+1 esima successiva
|
|
}
|
|
|
|
// per sicurezza aggiorno i nuovi punti e i nuovi parametri
|
|
double dUNewS, dUNewE ;
|
|
if ( ! pCrvLink->GetEndPoint( ptStartNext) ||
|
|
! vOffs[i]->GetParamAtPoint( ptS, dUNewS) ||
|
|
! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUNewE))
|
|
return false ;
|
|
|
|
// imposto il punto iniziale della curva successiva ( i+1 esima)
|
|
vOffs[i+1]->ChangeStartPoint( dUNewE) ;
|
|
PtrOwner<ICurveComposite> pCrvNewOffs( CloneCurveComposite( vOffs[i])) ;
|
|
if ( dUNewS > EPS_SMALL) {
|
|
pCrvNewOffs.Set( ConvertCurveToComposite( vOffs[i]->CopyParamRange( 0, dUNewS))) ;
|
|
// sostituisco la curva i esima con quella tagliata
|
|
vOffs[i].Set( pCrvNewOffs) ;
|
|
}
|
|
|
|
// aggiorno il collegamento
|
|
vLinks[i+1].Set( pCrvLink) ;
|
|
}
|
|
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CheckIfOffsetIsNecessary( const ISurfFlatRegion* pSfrAct, const ICurveComposite* pCrvOffs,
|
|
double dOffs, double dOffsPrec, int nIter, const PocketParams& PockParams, bool& bInsert)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvOffs == nullptr || ! pCrvOffs->IsValid() || pCrvOffs->GetCurveCount() == 0)
|
|
return false ;
|
|
bInsert = true ; // di base inserisco
|
|
|
|
// controllo se richiesta ottimizzazione sul numero di Offsets
|
|
if ( ! PockParams.bOptOffsets)
|
|
return true ;
|
|
|
|
// se spiral out non vanno eliminati parti di percorso
|
|
if ( PockParams.nType == POCKET_SPIRALOUT)
|
|
return true ;
|
|
|
|
if ( nIter != 0) { // controllo se sono almeno al secondo offset...
|
|
// per ogni curva controllo se il suo Offset massimo non genera alcuna regione
|
|
double dMaxOffs ;
|
|
if ( ! CalcCurveLimitOffset( *pCrvOffs, dMaxOffs))
|
|
return false ;
|
|
// controllo se l'Offset rimuove materiale
|
|
if ( dMaxOffs + ( dOffs - dOffsPrec) - PockParams.dRad < EPS_SMALL) {
|
|
bInsert = false ;
|
|
return true ;
|
|
}
|
|
// controllo se l'Offset rimuove una quantità minima di materiale
|
|
else if ( dMaxOffs + ( dOffs - dOffsPrec) - PockParams.dRad < TOL_REMOVE_OFFSET) {
|
|
// inizializzo la curva di bordo della regione non svuotata
|
|
PtrOwner<ICurveComposite> pCrvBorder( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvBorder))
|
|
return false ;
|
|
// recupero la regione non svuotata e calcolo la curva di bordo
|
|
PtrOwner<ISurfFlatRegion> pSfrA( pSfrAct->CreateOffsetSurf( - dOffsPrec - PockParams.dRad, ICurve::OFF_FILLET)) ;
|
|
if ( IsNull( pSfrA))
|
|
return false ;
|
|
if ( AreOppositeVectorApprox( pSfrA->GetNormVersor(), Z_AX))
|
|
pSfrA->Invert() ;
|
|
for ( int nC = 0 ; nC < pSfrA->GetChunkCount() ; ++ nC) {
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfrA->CloneChunk( nC)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
PtrOwner<ISurfFlatRegion> pSfrB( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrB) || ! pSfrB->AddExtLoop( pCrvOffs->Clone()))
|
|
return false ;
|
|
if ( AreOppositeVectorApprox( pSfrB->GetNormVersor(), Z_AX))
|
|
pSfrB->Invert() ;
|
|
if ( pSfrB->Intersect( *pSfrChunk) && pSfrB->IsValid()) {
|
|
if ( ! pCrvBorder.Set( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))))
|
|
return false ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( pCrvBorder->IsValid()) {
|
|
// controllo se l'utensile posizionato nel centroide rimuove la regione
|
|
Point3d ptCentroid ; pCrvBorder->GetCentroid( ptCentroid) ;
|
|
PtrOwner<ICurveArc> pCrvArc( CreateCurveArc()) ;
|
|
if ( IsNull( pCrvArc))
|
|
return false ;
|
|
pCrvArc->Set( ptCentroid, Z_AX, PockParams.dRad - 10 * EPS_SMALL) ;
|
|
IntersCurveCurve ICC( *pCrvBorder, *pCrvArc) ;
|
|
CRVCVECTOR ccClass ;
|
|
bInsert = ( ! ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) ||
|
|
int( ccClass.size()) != 1 || ccClass[0].nClass != CRVC_IN) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
IsCompoMadeBy2DifferentHomogeneousParts( const ICurveComposite* pCompo, const PocketParams& PockParams,
|
|
bool& bOk, ICRVCOMPOPOVECTOR& vpCrvs)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCompo == nullptr || ! pCompo->IsValid())
|
|
return false ;
|
|
bOk = false ;
|
|
vpCrvs.clear() ;
|
|
|
|
// clono la curva composita ( cambierà il suo punto iniziale)
|
|
PtrOwner<ICurveComposite> pCompoCL( CloneCurveComposite( pCompo)) ;
|
|
if ( IsNull( pCompoCL) || ! pCompoCL->IsValid())
|
|
return false ;
|
|
|
|
// recupero i tratti con proprietà uniformi
|
|
GetHomogeneousParts( pCompoCL, PockParams, vpCrvs) ;
|
|
if ( vpCrvs.size() > 1) {
|
|
// unisco il primo e l'ultimo se estremi compatibili
|
|
Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ;
|
|
Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ;
|
|
if ( AreSamePointApprox( ptS, ptE)) {
|
|
vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ;
|
|
vpCrvs.erase( vpCrvs.end() - 1) ;
|
|
}
|
|
}
|
|
|
|
// controllo che la curva abbia esattamente due tratti omogenei
|
|
bOk = ( int( vpCrvs.size()) == 2) ;
|
|
if ( ! bOk)
|
|
vpCrvs.clear() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static int
|
|
CalcInversionForSpiralOffset( const ISurfFlatRegion* pSfrChunk)
|
|
{
|
|
// controllo validità della regione
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return -1 ;
|
|
|
|
// Caso 0 : Non esistono isole
|
|
// Caso 1 : Loop Esterno Chiuso e, se esiste, Isola Chiusa
|
|
// Caso 2 : Loop Esterno Chiuso e, se esiste, Isola Aperta
|
|
// Caso 3 : Loop Esterno Aperto e, se esiste, Isola Chiusa
|
|
// Caso 4 : Loop Esterno Aperto e, se esiste, Isola Aperta
|
|
|
|
// se non esistono isole, allora Caso 0
|
|
if ( pSfrChunk->GetLoopCount( 0) == 1)
|
|
return 0 ;
|
|
|
|
// Il Loop esterno è considerato chiuso <=> esiste almeno una curva chiusa
|
|
bool bExtClosed = false ;
|
|
for ( int nU = 0 ; nU < pSfrChunk->GetLoopCurveCount( 0, 0) && ! bExtClosed ; ++ nU) {
|
|
int nTempProp = TEMP_PROP_INVALID ;
|
|
bExtClosed = ( pSfrChunk->GetCurveTempProp( 0, 0, nU, nTempProp) &&
|
|
nTempProp == TEMP_PROP_CLOSE_EDGE) ;
|
|
}
|
|
|
|
// Le isole vengono classificate come tutte chiuse <=> esiste almeno un isola chiusa
|
|
bool bIntClosed = false ;
|
|
for ( int nLoop = 1 ; nLoop < pSfrChunk->GetLoopCount( 0) && ! bIntClosed ; ++ nLoop) {
|
|
for ( int nU = 0 ; nU < pSfrChunk->GetLoopCurveCount( 0, nLoop) && ! bIntClosed ; ++ nU) {
|
|
int nTempProp = TEMP_PROP_INVALID ;
|
|
bIntClosed = ( pSfrChunk->GetCurveTempProp( 0, nLoop, nU, nTempProp) &&
|
|
nTempProp == TEMP_PROP_CLOSE_EDGE) ;
|
|
}
|
|
}
|
|
|
|
// restituisco il caso corrente
|
|
if ( bExtClosed && bIntClosed)
|
|
return 1 ;
|
|
if ( bExtClosed && ! bIntClosed)
|
|
return 2 ;
|
|
if ( ! bExtClosed && bIntClosed)
|
|
return 3 ;
|
|
if ( ! bExtClosed && ! bIntClosed)
|
|
return 4 ;
|
|
|
|
return -1 ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddEpicycles( const PocketParams& PockParam, ICurveComposite* pCompo, ICurveComposite* pCrv, ICurveComposite* pCrvBound)
|
|
{
|
|
// verifico che la curva sia valida
|
|
if ( pCompo == nullptr || ! pCompo->IsValid())
|
|
return false ;
|
|
|
|
// calcolo l'Offset definito dal valore dell'raggio dell'epiciclo
|
|
OffsetCurve OffsCrv ;
|
|
if ( ! OffsCrv.Make( pCompo, PockParam.dEpicyclesRad, ICurve::OFF_FILLET) || OffsCrv.GetCurveCount() > 1)
|
|
return false ;
|
|
PtrOwner<ICurveComposite> pCrvOffs( GetCurveComposite( OffsCrv.GetCurve())) ;
|
|
if ( IsNull( pCrvOffs))
|
|
return false ;
|
|
|
|
// verifico se devo resitituire la curva offsettata
|
|
if ( pCrvBound != nullptr)
|
|
pCrvBound->AddCurve( pCrvOffs->Clone()) ;
|
|
|
|
pCrv->Clear() ;
|
|
double dParPrec = 0. ;
|
|
for ( int i = 0 ; i < pCompo->GetCurveCount() ; ++ i) {
|
|
|
|
// calcolo distanza epicili specifica per quel tratto
|
|
double dLen ;
|
|
pCompo->GetCurve( i)->GetLength( dLen) ;
|
|
int nStep = max( 1, static_cast<int>( ceil( ( dLen) / PockParam.dEpicyclesDist))) ;
|
|
double dStep = 1.0 / nStep ;
|
|
|
|
for ( int k = 1 ; k <= nStep ; ++ k) {
|
|
// creo epiciclo
|
|
Point3d ptCen ;
|
|
Vector3d vtDir ;
|
|
pCompo->GetCurve( i)->GetPointD1D2( k * dStep, ICurve::FROM_MINUS, ptCen, &vtDir) ;
|
|
vtDir.Normalize() ;
|
|
vtDir.Rotate( Z_AX, - 90.) ;
|
|
Point3d pt = ptCen + vtDir * PockParam.dEpicyclesRad ;
|
|
PtrOwner<ICurveArc> pCrvArc( CreateCurveArc()) ;
|
|
pCrvArc->Set( ptCen, Z_AX, PockParam.dEpicyclesRad) ;
|
|
double dU ;
|
|
pCrvArc->GetParamAtPoint( pt, dU) ;
|
|
pCrvArc->ChangeStartPoint( dU) ;
|
|
|
|
// aggiungo tratto della curva offsettata
|
|
double dPar ;
|
|
pCrvOffs->GetParamAtPoint( pt, dPar) ;
|
|
bool bAdd = ( pCrv->AddCurve( pCrvOffs->CopyParamRange( dParPrec, dPar))) ;
|
|
|
|
// aggiungo epiciclo
|
|
if ( ! pCrv->AddCurve( Release( pCrvArc))) {
|
|
// se fallisco nell'aggiungere l'epiciclo tento nuovamente spostandolo di EPS_SMALL
|
|
if ( bAdd)
|
|
PtrOwner<ICurve> pCrvErased( pCrv->RemoveFirstOrLastCurve( true)) ;
|
|
k -- ;
|
|
dStep -= EPS_SMALL ;
|
|
if ( dStep < EPS_SMALL)
|
|
return false ;
|
|
}
|
|
else
|
|
dParPrec = dPar ;
|
|
}
|
|
}
|
|
// se necessario ripristino orientamento originale
|
|
if ( PockParam.bInvert)
|
|
pCrv->Invert() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static bool
|
|
CalcBoundedPolishingLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, const Vector3d& vtEnd,
|
|
const ICurve* pCrvBound, ICurveComposite* pCrvLink)
|
|
{
|
|
double dAngStart, dAngEnd ;
|
|
vtStart.GetAngleXY( X_AX, dAngStart) ;
|
|
vtEnd.GetAngleXY( X_AX, dAngEnd) ;
|
|
PtrOwner<ICurve> pBiArcLink( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, 0.5)) ;
|
|
if ( IsNull( pBiArcLink))
|
|
return false ;
|
|
|
|
// verifico se esce dalla svuotatura
|
|
CRVCVECTOR ccClass ;
|
|
IntersCurveCurve intCC( *pBiArcLink, *pCrvBound) ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
// se nessuno o un solo tratto e interno, il biarco è il collegamento
|
|
if ( ccClass.empty() || ( ssize( ccClass) == 1 && ccClass[0].nClass == CRVC_IN)) {
|
|
pCrvLink->AddCurve( Release( pBiArcLink)) ;
|
|
}
|
|
// altrimenti creo un percorso con biarchi e opportuni tratti della curva di contenimento
|
|
else {
|
|
PtrOwner<ICurveComposite> pCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompo))
|
|
return false ;
|
|
|
|
double dPar1, dPar2 ;
|
|
Point3d ptMinDist1, ptMinDist2 ;
|
|
Vector3d vtDir1, vtDir2 ;
|
|
double dAng1, dAng2 ;
|
|
int nFlag ;
|
|
|
|
DistPointCurve distPtSCrv( ptStart, *pCrvBound) ;
|
|
distPtSCrv.GetParamAtMinDistPoint( 0, dPar1, nFlag) ;
|
|
pCrvBound->GetPointTang( dPar1, ICurve::FROM_MINUS, ptMinDist1, vtDir1) ;
|
|
vtDir1.GetAngleXY( X_AX, dAng1) ;
|
|
|
|
DistPointCurve distPtECrv( ptEnd, *pCrvBound) ;
|
|
distPtECrv.GetParamAtMinDistPoint( 0, dPar2, nFlag) ;
|
|
pCrvBound->GetPointTang( dPar2, ICurve::FROM_MINUS, ptMinDist2, vtDir2) ;
|
|
vtDir2.GetAngleXY( X_AX, dAng2) ;
|
|
|
|
pCompo->AddCurve( GetBiArc( ptStart, -dAngStart, ptMinDist1, -dAng1, 0.5)) ; // primo biarco
|
|
pCompo->AddCurve( pCrvBound->CopyParamRange( dPar1, dPar2)) ; // tratto di pCrvBound
|
|
pCompo->AddCurve( GetBiArc( ptMinDist2, -dAng2, ptEnd, -dAngEnd, 0.5)) ; // secondo biarco
|
|
|
|
pCrvLink->AddCurve( Release( pCompo)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ComputePolishingPath( const PocketParams& PockParam, ICRVCOMPOPOVECTOR& vOffs, ICurveComposite* pMCrv)
|
|
{
|
|
// controllo dei parametri
|
|
if ( vOffs.empty() || pMCrv == nullptr)
|
|
return false ;
|
|
pMCrv->Clear() ;
|
|
|
|
// Offset con epicicli
|
|
ICRVCOMPOPOVECTOR vpCrvsEp ; vpCrvsEp.reserve( vOffs.size()) ;
|
|
|
|
// definisco la curva di bordo
|
|
PtrOwner<ICurveComposite> pCrvBound( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvBound))
|
|
return false ;
|
|
|
|
// recupero il punto iniziale
|
|
Point3d ptStartRef ;
|
|
GetPtStartOnGenericEdge( vOffs[0], PockParam, ptStartRef) ;
|
|
|
|
// scorro le curve di Offset
|
|
for ( int i = 0 ; i < ssize( vOffs) ; ++ i) {
|
|
PtrOwner<ICurveComposite> pCrvEp( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvEp))
|
|
return false ;
|
|
double dUStart = 0. ;
|
|
int nFlag = 0 ;
|
|
DistPointCurve( ptStartRef, *vOffs[i]).GetParamAtMinDistPoint( 0., dUStart, nFlag) ;
|
|
vOffs[i]->ChangeStartPoint( dUStart) ;
|
|
// la curva di bound è l'offset che calcolo in AddEpicycles per la prima curva compo trovata in pMCrv
|
|
if ( ! AddEpicycles( PockParam, vOffs[i], pCrvEp, ( i == 0 ? pCrvBound : nullptr)))
|
|
return false ;
|
|
vpCrvsEp.emplace_back( Release( pCrvEp)) ;
|
|
}
|
|
|
|
// calcolo i collegamenti
|
|
ICURVEPOVECTOR vLinks( vpCrvsEp.size()) ;
|
|
for ( int i = 1 ; i < ssize( vpCrvsEp) ; ++ i) {
|
|
// punti e direzioni di inizio e fine
|
|
Point3d ptStart ; Vector3d vtStart ;
|
|
vpCrvsEp[i-1]->GetEndPoint( ptStart) ;
|
|
vpCrvsEp[i-1]->GetEndDir( vtStart) ;
|
|
Point3d ptEnd ; Vector3d vtEnd ;
|
|
vpCrvsEp[i]->GetStartPoint( ptEnd) ;
|
|
vpCrvsEp[i]->GetStartDir( vtEnd) ;
|
|
|
|
// calcolo il collegamento con biarchi (garantendo che non esca dalla svuotatura)
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
if ( ! CalcBoundedPolishingLink( ptStart, vtStart, ptEnd, vtEnd, pCrvBound, pCrvLink))
|
|
return false ;
|
|
vLinks[i].Set( pCrvLink) ;
|
|
}
|
|
|
|
// creo il percorso di lavoro a partire dalla raccolta delle curve con epicicli e dei collegamenti
|
|
for ( int i = 0 ; i < int( vpCrvsEp.size()) ; ++ i) {
|
|
// se collegamento da aggiungere
|
|
if ( ! IsNull( vLinks[i])) {
|
|
// accodo nel percorso di lavorazione
|
|
pMCrv->AddCurve( Release( vLinks[i])) ;
|
|
}
|
|
// aggiungo la curva
|
|
pMCrv->AddCurve( Release( vpCrvsEp[i])) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcSpiral( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, int& nReg, Point3d& ptStart,
|
|
ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops, ICurveComposite* pMCrv, bool& bMidOut, Vector3d& vtMidOut)
|
|
{
|
|
// inizializzo il percorso come vuoto
|
|
pMCrv->Clear() ;
|
|
|
|
// Offset corrente ( il primo è definito dal raggio utensile)
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
// se lucidatura
|
|
if ( PockParams.bPolishing && nReg == 0)
|
|
dOffs += PockParams.dEpicyclesRad ;
|
|
|
|
// ciclo di offset verso l'interno
|
|
const int MAX_ITER = 1000 ;
|
|
int nIter = 0 ;
|
|
ICRVCOMPOPOVECTOR vOffs ; // vettore delle curve di offset
|
|
ICRVCOMPOPOVECTOR vOffsFirstCurve ; // curve di primo offset
|
|
|
|
// tengo una copia della regione corrente da svuotare
|
|
PtrOwner<ISurfFlatRegion> pSrfAct( CloneSurfFlatRegion( pSfrPock)) ;
|
|
if ( IsNull( pSrfAct) || pSrfAct->GetChunkCount() == 0)
|
|
return false ;
|
|
|
|
// ricavo il tipo di relazione descritta tra Open/Close di bordo esterno e/o isole
|
|
int nCase = CalcInversionForSpiralOffset( pSrfAct) ;
|
|
if ( nCase == -1)
|
|
return false ;
|
|
|
|
// ricavo le regioni progressive
|
|
double dOffsPrec = 0. ;
|
|
int nCrvFirstOffs = 0 ;
|
|
bool bLastNotValid = false ;
|
|
while ( nIter < MAX_ITER) {
|
|
// Offset della regione attuale
|
|
PtrOwner<ISurfFlatRegion> pSfrOffsVR( pSrfAct->CreateOffsetSurf( - dOffs, ICurve::OFF_FILLET)) ;
|
|
if ( IsNull( pSfrOffsVR))
|
|
return false ;
|
|
// se la regione sparisce allora riprovo con un Offset leggermente più piccolo
|
|
if ( ! pSfrOffsVR->IsValid()) {
|
|
pSfrOffsVR.Set( pSrfAct->CreateOffsetSurf( - dOffs + 5 * EPS_SMALL, ICurve::OFF_FILLET)) ;
|
|
if ( IsNull( pSfrOffsVR))
|
|
return false ;
|
|
bLastNotValid = true ;
|
|
}
|
|
|
|
// se primo Offset
|
|
if ( nIter == 0) {
|
|
// aggiorno il nuovo valore delle regioni totali di primo Offset
|
|
int my_nReg = nReg ;
|
|
nReg = pSfrOffsVR->GetChunkCount() ;
|
|
// gli Offset progressivi appartengono al Chunk nReg-esimo
|
|
pSrfAct.Set( pSfrOffsVR->CloneChunk( my_nReg)) ;
|
|
// se supero i chunk ottenuti
|
|
if ( IsNull( pSrfAct))
|
|
return true ;
|
|
// imposto la regione id svuotatura corrente
|
|
pSfrOffsVR.Set( pSrfAct->Clone()) ;
|
|
}
|
|
|
|
// numero di Chunk e Loops attuali ( alla prima iterazione è l'nReg-esimo)
|
|
int nChunks = pSfrOffsVR->GetChunkCount() ;
|
|
|
|
for ( int i = 0 ; i < nChunks ; ++ i) {
|
|
// per ogni chunk...
|
|
int nLoops = pSfrOffsVR->GetLoopCount( i) ;
|
|
for ( int j = 0 ; j < nLoops ; ++ j) {
|
|
// per ogni loop...
|
|
PtrOwner<ICurveComposite> pCrvCompoBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ;
|
|
if ( IsNull( pCrvCompoBorder) || ! pCrvCompoBorder->IsValid())
|
|
return false ;
|
|
// controllo quali regioni di Offset possono essere sostituite
|
|
bool bInsert = true ;
|
|
if ( ! CheckIfOffsetIsNecessary( pSrfAct, pCrvCompoBorder, dOffs, dOffsPrec, nIter, PockParams, bInsert))
|
|
return false ;
|
|
// per evitare di allacciare una curva di regione non svuotata alla prima curva di Offset
|
|
// ( quindi avere un entrata nel pieno del grezzo) controllo di non eliminare il secondo Offset
|
|
if ( ! bInsert && nChunks == 1 && bLastNotValid)
|
|
bInsert = true ;
|
|
if ( bInsert) {
|
|
// imposto come secondo TempParam il Side di classificazione
|
|
pCrvCompoBorder->SetTempParam( j == 0 ? MDS_RIGHT : MDS_LEFT, 1) ;
|
|
// stabilisco l'orientamento della curva
|
|
if ( ( nCase == 1 && nIter != 0 && j > 0) ||
|
|
( nCase == 2 && j > 0) ||
|
|
( nCase == 3 && j == 0) ||
|
|
( nCase == 4 && j > 0)) {
|
|
pCrvCompoBorder->Invert() ;
|
|
SwapSideBySecondTempParam( pCrvCompoBorder) ;
|
|
}
|
|
vOffs.emplace_back( Release( pCrvCompoBorder)) ;
|
|
}
|
|
if ( nIter == 0) { // salvo il bordo per i link ( non invertiti, devo sapere IN/OUT)
|
|
PtrOwner<ICurveComposite> pCrvCompoExtBorder( ConvertCurveToComposite( pSfrOffsVR->GetLoop( i, j))) ;
|
|
vOffsFirstCurve.emplace_back( Release( pCrvCompoExtBorder)) ;
|
|
}
|
|
}
|
|
}
|
|
// ricavo il numero di curve ottenute di primo Offset
|
|
if ( nIter == 0)
|
|
nCrvFirstOffs = int( vOffs.size()) ;
|
|
|
|
// controllo se serve un raggio più piccolo di svuotatura
|
|
bool bSmallRad = ( nIter == 0 ? ( dOffs < PockParams.dRad + EPS_ZERO) :
|
|
( dOffs - dOffsPrec < PockParams.dRad + EPS_ZERO)) ;
|
|
|
|
// se ho trovato dei chunk, allora aggiorno l'Offset per la passata successiva
|
|
if ( nChunks > 0) {
|
|
// memorizzo il valore di Offset per l'iterazione successiva
|
|
if ( nIter != 0) {
|
|
dOffsPrec = dOffs ;
|
|
dOffs += PockParams.dSideStep ;
|
|
}
|
|
else {
|
|
dOffsPrec = 0. ;
|
|
dOffs = PockParams.dSideStep ;
|
|
}
|
|
}
|
|
// se non ho Chunks e devo usare un Offset più piccolo...
|
|
else if ( ! bSmallRad) {
|
|
if ( PockParams.dRad < EPS_SMALL || PockParams.bPolishing)
|
|
break ;
|
|
dOffs = dOffsPrec + ( nIter == 0 ? PockParams.dRad + PockParams.dRadialOffset : PockParams.dRad) ;
|
|
}
|
|
// altrimenti ho finito la regione da svuotare...
|
|
else
|
|
break ;
|
|
|
|
++ nIter ; // aggiorno iterazione
|
|
}
|
|
|
|
// se non ho trovato curve di Offset allora esco
|
|
if ( vOffs.empty())
|
|
return true ;
|
|
|
|
// se lucidatura
|
|
if ( PockParams.bPolishing && nReg == 1) {
|
|
// si suppone che gli offset siano tutti concentrici ( già ordinati dall'esterno all'interno)
|
|
bMidOut = false ; // per definizione di lucidatura
|
|
return ( ComputePolishingPath( PockParams, vOffs, pMCrv)) ;
|
|
}
|
|
|
|
// cambio il punto iniziale della prima Curva di Offset
|
|
Point3d ptRef = PockParams.ptStart ;
|
|
Point3d ptNewStart ;
|
|
|
|
// Se ho come lavorazione uno SpiralIn posso poter entrare dalle isole aperte...
|
|
int nIndexSwap = 0 ;
|
|
if ( PockParams.nType == POCKET_SPIRALIN) {
|
|
if ( SetAdvancedPtStartForPath( vOffsFirstCurve, PockParams, pSfrPock, ptRef, ptStart,
|
|
vtMidOut, bMidOut, nIndexSwap, vCrvOrigChunkLoops)) {
|
|
vOffsFirstCurve[nIndexSwap]->GetStartPoint( ptNewStart) ;
|
|
}
|
|
else
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( SetPtStartForPath( vOffs[0], PockParams, pSfrPock, ptRef, ptStart, vtMidOut, bMidOut,
|
|
vCrvOrigChunkLoops[0]))
|
|
vOffs[0]->GetStartPoint( ptNewStart) ;
|
|
else
|
|
return false ;
|
|
}
|
|
|
|
// se richiesta inversione
|
|
if ( PockParams.bInvert) {
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
|
|
vOffs[i]->Invert() ;
|
|
SwapSideBySecondTempParam( vOffs[i]) ;
|
|
}
|
|
}
|
|
|
|
// smusso le curve di offset ( ad eccezione di quelle di primo Offset)
|
|
double dSmoothPar = PockParams.dSmooth / SQRT2 ;
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
|
|
if ( i >= nCrvFirstOffs)
|
|
ModifyCurveToSmoothed( vOffs[i], PockParams, dSmoothPar, dSmoothPar, false) ;
|
|
vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
}
|
|
|
|
// setto il punto iniziale della svuotatura
|
|
double dNewUS ;
|
|
if ( nIndexSwap != 0)
|
|
swap( vOffs[0], vOffs[nIndexSwap]) ;
|
|
vOffs[0]->GetParamAtPoint( ptNewStart, dNewUS) ;
|
|
vOffs[0]->ChangeStartPoint( dNewUS) ;
|
|
vOffs[0]->SetTempParam( 0., 0) ; // prima iterazione
|
|
|
|
// riordino le curve e creo i collegamenti
|
|
ICURVEPOVECTOR vLinks( vOffs.size()) ;
|
|
if ( ! CreateSpiralPocketingPath( vOffs, vLinks, PockParams, vOffsFirstCurve))
|
|
return false ;
|
|
|
|
// controllo eventuali parti non svuotate ( e setto la Feed degli Offset e dei Link)...
|
|
PtrOwner<ISurfFlatRegion> pSfrUncleared( CreateSurfFlatRegion()) ;
|
|
if ( GetUnclearedRegionAndSetFeed( vOffsFirstCurve, vOffs, vLinks, pSfrOrig, PockParams, pSfrUncleared)) {
|
|
// modifico i percorsi
|
|
if ( ! RemoveUnclearedRegions( pSfrUncleared, vOffs, vOffsFirstCurve, PockParams))
|
|
return false ;
|
|
}
|
|
|
|
// creo il percorso di lavoro a partire dalla raccolta degli offset e dei collegamenti
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
|
|
// se collegamento da aggiungere
|
|
if ( ! IsNull( vLinks[i])) {
|
|
// accodo nel percorso di lavorazione
|
|
if ( ! pMCrv->AddCurve( Release( vLinks[i])))
|
|
return false ;
|
|
}
|
|
pMCrv->AddCurve( Release( vOffs[i])) ;
|
|
}
|
|
|
|
// semplifico la curva ottenuta
|
|
SimplifyCurveByFeeds( pMCrv, PockParams, 10 * EPS_SMALL) ;
|
|
|
|
// verifico il percorso di lavoro
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
return false ;
|
|
|
|
// reset delle proprietà temporanee delle curve componenti
|
|
for ( int i = 0 ; i < pMCrv->GetCurveCount() ; ++ i) {
|
|
pMCrv->SetCurveTempProp( i, 0, 0) ;
|
|
pMCrv->SetCurveTempProp( i, 0, 1) ;
|
|
}
|
|
|
|
// setto estrusione
|
|
pMCrv->SetExtrusion( Z_AX) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
static bool
|
|
CalcZigZagLink( ICurveComposite* pCrv1, ICurveComposite* pCrv2, const PocketParams& PockParams,
|
|
ICurveComposite* pCrvLink)
|
|
{
|
|
// se non richiesto, esco
|
|
if ( ! PockParams.bSmooth)
|
|
return true ;
|
|
|
|
// controllo dei parametri
|
|
if ( pCrv1 == nullptr || pCrv2 == nullptr)
|
|
return false ;
|
|
pCrvLink->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
|
|
// prendo i parametri per il raccordo
|
|
Vector3d vtS, vtE, vtH ;
|
|
pCrv1->GetEndDir( vtS) ;
|
|
pCrv2->GetStartDir( vtE) ;
|
|
Point3d ptS, ptE, ptE1, ptS2 ;
|
|
pCrv1->GetEndPoint( ptS) ;
|
|
pCrv2->GetStartPoint( ptE) ;
|
|
vtH = ptE - ptS ;
|
|
double dLen1, dLen2 ;
|
|
pCrv1->GetLength( dLen1) ;
|
|
pCrv2->GetLength( dLen2) ;
|
|
double dUS1, dUE1, dUS2, dUE2 ;
|
|
pCrv1->GetDomain( dUS1, dUE1) ;
|
|
pCrv2->GetDomain( dUS2, dUE2) ;
|
|
double dAngle, dAngleH ;
|
|
double dDist = Dist( ptS, ptE) ;
|
|
|
|
// curve per controllo validità del collegamento
|
|
PtrOwner<ICurveComposite> pCrvH1( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvH2( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvTempLink( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCrvTest( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvH1) || IsNull( pCrvH2) || IsNull( pCrvTempLink) || IsNull( pCrvTest))
|
|
return false ;
|
|
|
|
if ( vtS.GetAngle( vtE, dAngle) &&
|
|
abs( dAngle) < ANG_STRAIGHT + 5 * EPS_SMALL && abs( dAngle) > ANG_STRAIGHT - 5 * EPS_SMALL &&
|
|
vtH.GetAngle( vtE, dAngleH) &&
|
|
abs( dAngleH) < ANG_RIGHT + 5 * EPS_SMALL && abs( dAngleH) > ANG_RIGHT - 5 * EPS_SMALL &&
|
|
dLen1 > dDist && dLen2 > dDist &&
|
|
pCrvLink->GetCurveCount() == 1 && pCrvLink->GetFirstCurve()->GetType() == CRV_LINE) {
|
|
|
|
// creo una semicirconferenza
|
|
double dRad = dDist / 2 ; // raggio
|
|
Point3d ptNS, ptNE ;
|
|
pCrv1->GetParamAtLength( dLen1 - dRad, dUE1) ;
|
|
pCrv2->GetParamAtLength( dRad, dUS2) ;
|
|
pCrv1->GetPointD1D2( dUE1, ICurve::FROM_MINUS, ptNS) ; // parametro di intersezione su curva 1
|
|
pCrv2->GetPointD1D2( dUS2, ICurve::FROM_PLUS, ptNE) ; // parametro di intersezione su curva 2
|
|
|
|
PtrOwner<ICurveArc> pSemiCir( CreateCurveArc()) ;
|
|
if ( pSemiCir->Set2PVN( ptNS, ptNE, vtS, Z_AX)) {
|
|
pCrvH1.Set( GetCurveComposite( pCrv1->CopyParamRange( dUS1, dUE1))) ;
|
|
pCrvH2.Set( GetCurveComposite( pCrv2->CopyParamRange( dUS2, dUE2))) ;
|
|
pCrvTempLink->AddCurve( pSemiCir->Clone()) ;
|
|
}
|
|
|
|
// controllo finale sui raccordi ammissibili ...
|
|
if ( pCrvTest->AddCurve( pCrvH1->Clone()) && pCrvTest->AddCurve( pCrvTempLink->Clone()) &&
|
|
pCrvTest->AddCurve( pCrvH2->Clone())) {
|
|
|
|
if ( ! pCrv1->TrimEndAtParam( dUE1))
|
|
pCrv1->Clear() ;
|
|
pCrvLink->Clear() ;
|
|
pCrvLink->AddCurve( Release( pCrvTempLink)) ;
|
|
if ( ! pCrv2->TrimStartAtParam( dUS2))
|
|
pCrv2->Clear() ;
|
|
return true ;
|
|
}
|
|
}
|
|
else {
|
|
// devo smussare la curva formata da pCrv1 - pCrvLink ( da creare) - pCrv2
|
|
pCrvH1->AddCurve( pCrv1->Clone()) ;
|
|
pCrvH2->AddCurve( pCrv2->Clone()) ;
|
|
pCrvTempLink->AddCurve( pCrvLink->Clone()) ;
|
|
|
|
// prendo le lunghezze necessarie
|
|
double dLenSeg1 = EPS_SMALL ; double dLenSeg2 = EPS_SMALL ;
|
|
double dLenI_s = EPS_SMALL ; // lunghezza prima curva del Link
|
|
double dLenI_e = EPS_SMALL ; // lunghezza ultima curva del Link
|
|
pCrvH1->GetLength( dLenSeg1) ;
|
|
pCrvH2->GetLength( dLenSeg2) ;
|
|
pCrvTempLink->GetFirstCurve()->GetLength( dLenI_s) ;
|
|
pCrvTempLink->GetLastCurve()->GetLength( dLenI_e) ;
|
|
|
|
// raccordo pSeg1 con pCrvLink
|
|
double dTollLeft = 1 - ( dLen1 - ( ( 2 * PockParams.dRad) / 16)) / dLen1 ; // % parametro sinistro per smusso
|
|
double dTollRight = ( ( 2 * PockParams.dRad) / 16) / dLenI_s ; // % parametro destro di smusso
|
|
PtrOwner<ICurveComposite> pCrv_Seg1_Isl( CreateCurveComposite()) ; // curva unione di pSeg1 e pCrvLink smussata
|
|
pCrv_Seg1_Isl->AddCurve( pCrvH1->Clone()) ;
|
|
pCrv_Seg1_Isl->AddCurve( pCrvTempLink->Clone()) ;
|
|
ModifyCurveToSmoothed( pCrv_Seg1_Isl, PockParams, dTollLeft, dTollRight, true) ;
|
|
|
|
// raccordo questa curva con pSeg2
|
|
double dUS_1IH, dUE_1IH, dToTLen ;
|
|
pCrv_Seg1_Isl->GetDomain( dUS_1IH, dUE_1IH) ; // dominio della curva smussata tra pSeg1 e PCrvLink
|
|
pCrv_Seg1_Isl->GetLength( dToTLen) ; // nuova lunghezza della curva smussata tra pSeg1 e pCrvLink
|
|
dTollLeft = 1 - ( dLenI_e - ( ( 2 * PockParams.dRad) / 16)) / dLenI_e ; // % paramtro sinistro per smusso
|
|
dTollRight = ( ( 2 * PockParams.dRad) / 16) / dLen2 ; // % parametro destro per smusso
|
|
PtrOwner<ICurveComposite> pCrv_smoothed( CreateCurveComposite()) ; // curva unione del primo smusso con pSeg2
|
|
pCrv_smoothed->AddCurve( pCrv_Seg1_Isl->Clone()) ;
|
|
pCrv_smoothed->AddCurve( pCrvH2->Clone()) ;
|
|
ModifyCurveToSmoothed( pCrv_smoothed, PockParams, dTollLeft, dTollRight, true) ;
|
|
|
|
// recupero i parametri del nuovo collegamento che si è creato dallo smusso
|
|
pCrvH1->Clear() ;
|
|
pCrvTempLink->Clear() ;
|
|
pCrvH2->Clear() ;
|
|
int nStat = -1 ;
|
|
for ( int u = 0 ; u < pCrv_smoothed->GetCurveCount() ; ++ u) {
|
|
// recupero la curva u-esima
|
|
const ICurve* pCrv = pCrv_smoothed->GetCurve( u) ;
|
|
if ( pCrv->GetType() == CRV_LINE) {
|
|
if ( CheckSimpleOverlap( pCrv, pCrv1, nStat, EPS_SMALL) && nStat == 1)
|
|
pCrvH1->AddCurve( pCrv->Clone()) ;
|
|
else if ( CheckSimpleOverlap( pCrv, pCrv2, nStat, EPS_SMALL) && nStat == 1)
|
|
pCrvH2->AddCurve( pCrv->Clone()) ;
|
|
}
|
|
}
|
|
|
|
Point3d ptCrv1_e, ptCrv2_s ;
|
|
double dULink_s = EPS_SMALL ;
|
|
double dULink_e = EPS_SMALL ;
|
|
|
|
pCrvH1->GetEndPoint( ptCrv1_e) ;
|
|
pCrv1->GetParamAtPoint( ptCrv1_e, dULink_s) ;
|
|
if ( ! pCrv1->TrimEndAtParam( dULink_s))
|
|
pCrv1->Clear() ;
|
|
|
|
pCrvH2->GetStartPoint( ptCrv2_s) ;
|
|
pCrv2->GetParamAtPoint( ptCrv2_s, dULink_e) ;
|
|
if ( ! pCrv2->TrimStartAtParam( dULink_e))
|
|
pCrv2->Clear() ;
|
|
|
|
pCrv_smoothed->GetParamAtPoint( ptCrv1_e, dULink_s) ;
|
|
pCrv_smoothed->GetParamAtPoint( ptCrv2_s, dULink_e) ;
|
|
pCrvLink->Clear() ;
|
|
pCrvLink->AddCurve( GetCurveComposite( pCrv_smoothed->CopyParamRange( dULink_s, dULink_e))) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcZigZag( const ISurfFlatRegion* pSrfZigZag, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vpCrvs,
|
|
bool bFromInfill)
|
|
{
|
|
|
|
// check parametri
|
|
if ( pSrfZigZag == nullptr || ! pSrfZigZag->IsValid())
|
|
return true ;
|
|
|
|
// creo il vettore dei contorni
|
|
ICRVCOMPOPOVECTOR vFirstOff ;
|
|
for ( int c = 0 ; c < pSrfZigZag->GetChunkCount() ; ++ c)
|
|
for ( int l = 0 ; l < pSrfZigZag->GetLoopCount( c) ; ++ l)
|
|
vFirstOff.emplace_back( GetCurveComposite( pSrfZigZag->GetLoop( c, l))) ;
|
|
|
|
// ingombro del contorno offsettato
|
|
BBox3d b3Pocket ;
|
|
vFirstOff[0]->GetLocalBBox( b3Pocket) ;
|
|
Point3d ptMin ; double dDimX, dDimY, dDimZ ;
|
|
b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ;
|
|
|
|
// lunghezza del contorno offsettato
|
|
double dLen ; vFirstOff[0]->GetLength( dLen) ;
|
|
|
|
// passi in Y
|
|
int nYStep ;
|
|
double dYStep ;
|
|
if ( ! bFromInfill) {
|
|
nYStep = static_cast< int >( ceil(( dDimY - 30 * EPS_SMALL) / PockParams.dSideStep)) ;
|
|
dYStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ;
|
|
}
|
|
else {
|
|
nYStep = static_cast< int >( floor(( dDimY + 30 * EPS_SMALL) / PockParams.dSideStep)) ;
|
|
dYStep = PockParams.dSideStep ;
|
|
}
|
|
|
|
int nRef = (( nYStep + ( PockParams.bInvert ? 0 : 1)) % 2) ;
|
|
|
|
// tratto valido
|
|
struct Section {
|
|
bool bActive ;
|
|
Point3d ptS ;
|
|
Point3d ptE ;
|
|
double dOs ;
|
|
double dOe ;
|
|
int nOffIndS ;
|
|
int nOffIndE ;
|
|
} ;
|
|
|
|
struct ParIsland {
|
|
double dU ;
|
|
int nInd ;
|
|
} ;
|
|
|
|
// raccolta di tratti
|
|
typedef vector<vector<Section>> VECVECSECT ; VECVECSECT vvSec ;
|
|
vvSec.resize( nYStep + 1) ;
|
|
|
|
// calcolo le linee di svuotatura
|
|
int nCount = 0 ;
|
|
for ( int i = 0 ; i <= nYStep ; ++ i) {
|
|
// determino senso
|
|
bool bPlus = (( i % 2) == nRef) ;
|
|
// definisco la linea
|
|
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
|
|
const double EXP_LEN = 1.0 ;
|
|
Point3d ptStart ;
|
|
if ( ! bFromInfill)
|
|
ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + 10 * EPS_SMALL + i * dYStep, ptMin.z + dDimZ) ;
|
|
else {
|
|
double dShift = ( i == 0 ? 10 * EPS_SMALL : ( i == nYStep ? - 10 * EPS_SMALL : 0.)) ;
|
|
ptStart.Set( ptMin.x - EXP_LEN, ptMin.y + dShift + i * dYStep, ptMin.z + dDimZ) ;
|
|
}
|
|
if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN))
|
|
return false ;
|
|
|
|
if ( ! bPlus)
|
|
pLine->Invert() ;
|
|
|
|
// calcolo la classificazione della linea rispetto alla superficie
|
|
CRVCVECTOR ccClass ;
|
|
pSrfZigZag->GetCurveClassification( *pLine, EPS_SMALL, ccClass) ;
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
// se interno
|
|
if ( ccClass[j].nClass == CRVC_IN) {
|
|
// recupero il tratto
|
|
Section currSec ;
|
|
currSec.bActive = true ;
|
|
PtrOwner<ICurveLine> pSeg( GetCurveLine( pLine->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
|
|
if ( IsNull( pSeg))
|
|
continue ;
|
|
Point3d ptS, ptE ;
|
|
pSeg->GetStartPoint( ptS) ; pSeg->GetEndPoint( ptE) ;
|
|
// se troppo piccolo, lo scarto
|
|
if ( SqDist( ptS, ptE) < 250 * 250 * SQ_EPS_SMALL)
|
|
continue ;
|
|
currSec.ptS = ptS ; currSec.ptE = ptE ;
|
|
for ( int k = 0 ; k < int( vFirstOff.size()) ; ++ k) {
|
|
if ( vFirstOff[k]->IsPointOn( ptS)) {
|
|
currSec.nOffIndS = k ;
|
|
double dUOS; vFirstOff[k]->GetParamAtPoint( ptS, dUOS) ;
|
|
currSec.dOs = dUOS ;
|
|
}
|
|
if ( vFirstOff[k]->IsPointOn( ptE)){
|
|
currSec.nOffIndE = k ;
|
|
double dUOE; vFirstOff[k]->GetParamAtPoint( ptE, dUOE) ;
|
|
currSec.dOe = dUOE ;
|
|
}
|
|
}
|
|
vvSec[i].emplace_back( currSec) ;
|
|
++ nCount ; // numero di segmenti inseriti
|
|
}
|
|
}
|
|
}
|
|
|
|
int nStatus = 0 ; // 0 -> inizio oppure ho inserito una parte di isola | 2 -> sto inserendo un segmento
|
|
Point3d ptS_ref ;
|
|
PtrOwner<ICurveComposite> pLastSeg( CreateCurveComposite()) ; // ultimo segmento nel percorso
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ; // ultimo link aggiunto
|
|
// vettore dei link aggiunti ( per Feed )
|
|
ICRVCOMPOPOVECTOR vAddedLinks ;
|
|
bool bFirstLine = true ; // flag per prima linea di ogni percorso
|
|
|
|
// creo i percorsi di svuotatura
|
|
vpCrvs.reserve( nCount) ;
|
|
int nI = -1, nJ = -1 ;
|
|
while ( true) {
|
|
// se sezione non valida
|
|
if ( nI < 0 || nJ < 0) {
|
|
// ricerco la prima valida ( il primo segmento con bActive messo a true)
|
|
for ( int k = 0 ; k < int( vvSec.size()) && nI < 0 ; ++ k) {
|
|
for ( int l = 0 ; l < int( vvSec[k].size()) && nJ < 0 ; ++ l) {
|
|
if ( vvSec[k][l].bActive) {
|
|
nI = k ;
|
|
nJ = l ;
|
|
}
|
|
}
|
|
}
|
|
// se trovata, creo nuova curva composita
|
|
if ( nI >= 0 && nJ >= 0) {
|
|
// creo la curva
|
|
vpCrvs.emplace_back( CreateCurveComposite()) ;
|
|
nStatus = 0 ;
|
|
// aggiungo punto iniziale
|
|
vpCrvs.back()->AddPoint( vvSec[nI][nJ].ptS) ;
|
|
bFirstLine = true ;
|
|
}
|
|
// altrimenti, esco
|
|
else
|
|
break ;
|
|
}
|
|
// determino senso
|
|
bool bPlus = (( nI % 2) == nRef) ;
|
|
// aggiungo la sezione alla curva
|
|
Section& Sec = vvSec[nI][nJ] ;
|
|
Sec.bActive = false ;
|
|
// creo i vettori con le linee Under e Above nella struttura della Section
|
|
ICURVEPOVECTOR vLineAbove ;
|
|
if ( nI < nYStep) {
|
|
for ( int a = 0 ; a < ( int)vvSec[nI + 1].size() ; ++ a) {
|
|
if ( vvSec[nI + 1][a].bActive)
|
|
continue ;
|
|
PtrOwner<ICurveLine> pLA( CreateCurveLine()) ;
|
|
pLA->Set( vvSec[nI + 1][a].ptS, vvSec[nI + 1][a].ptE) ;
|
|
vLineAbove.emplace_back( Release( pLA)) ;
|
|
}
|
|
}
|
|
ICURVEPOVECTOR vLineUnder ;
|
|
if ( nI > 0) {
|
|
for ( int u = 0 ; u < int( vvSec[nI-1].size()) ; ++ u) {
|
|
if ( vvSec[nI-1][u].bActive)
|
|
continue ;
|
|
PtrOwner<ICurveLine> pLU( CreateCurveLine()) ;
|
|
pLU->Set( vvSec[nI-1][u].ptS, vvSec[nI-1][u].ptE) ;
|
|
vLineUnder.emplace_back( Release( pLU)) ;
|
|
}
|
|
}
|
|
// creo la linea come curva composita, aggiungerò dei Joint per ogni tratto con Feed differente
|
|
PtrOwner<ICurveComposite> pLineCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pLineCompo))
|
|
return false ;
|
|
|
|
Point3d ptE_l ;
|
|
if ( bFirstLine)
|
|
ptE_l = vvSec[nI][nJ].ptS ;
|
|
else
|
|
vpCrvs.back()->GetEndPoint( ptE_l) ;
|
|
|
|
pLineCompo->AddPoint( ptE_l) ;
|
|
pLineCompo->AddLine( vvSec[nI][nJ].ptE) ;
|
|
// assegno la feed al tratto lineare
|
|
if ( ! AssignFeedZigZagOneWay( pLineCompo, false, vLineAbove, vLineUnder, vAddedLinks, PockParams)) // Assegno la Feed
|
|
return false ;
|
|
|
|
bFirstLine = false ;
|
|
vpCrvs.back()->AddCurve( pLineCompo->Clone()) ; // aggiungo la curva al percorso
|
|
|
|
if ( nStatus == 0) { // primo segmento per il raccordo smussato
|
|
pLastSeg.Set( pLineCompo) ;
|
|
pLastSeg->GetStartPoint( ptS_ref) ;
|
|
}
|
|
if ( nStatus == 2) { // ho precedentemente aggiunto il bordo di un'isola, quindi mi salvo il secondo segmento
|
|
|
|
PtrOwner<ICurveComposite> pSeg1( pLastSeg->Clone()) ;
|
|
PtrOwner<ICurveComposite> pSeg2( pLineCompo->Clone()) ;
|
|
if ( IsNull( pSeg1) || IsNull( pSeg2))
|
|
return false ;
|
|
|
|
// aggiorno il link
|
|
if ( ! CalcZigZagLink( pSeg1, pSeg2, PockParams, pCrvLink))
|
|
return false ;
|
|
|
|
// aggiorno il vettore delle curve ZigZag
|
|
Point3d ptE_Seg1 ;
|
|
pSeg1->GetEndPoint( ptE_Seg1) ;
|
|
double dUS_Link = EPS_SMALL ;
|
|
vpCrvs.back()->GetParamAtPoint( ptE_Seg1, dUS_Link) ;
|
|
if ( ! vpCrvs.back()->TrimEndAtParam( dUS_Link))
|
|
vpCrvs.back()->Clear() ;
|
|
|
|
if ( bFromInfill)
|
|
for ( int u = 0 ; u < pCrvLink->GetCurveCount() ; ++ u)
|
|
pCrvLink->SetCurveTempProp( u, -1, 1) ;
|
|
|
|
if ( ! AssignFeedZigZagOneWay( pCrvLink, true, vLineAbove, vLineUnder, vAddedLinks, PockParams))
|
|
return false ;
|
|
|
|
vpCrvs.back()->AddCurve( pCrvLink->Clone()) ; // aggiungo il Link
|
|
vpCrvs.back()->AddCurve( pSeg2->Clone()) ; // aggiungo pSeg2
|
|
|
|
// aggiorno il vettore dei Link ( nel caso sia un link tra due segmenti sullo stesso livello )
|
|
Point3d ptS1 ;
|
|
pSeg1->GetStartPoint( ptS1) ;
|
|
Point3d ptS2 ;
|
|
pSeg2->GetStartPoint( ptS2) ;
|
|
if ( abs( ptS1.y - ptS2.y) < 50 * EPS_SMALL)
|
|
vAddedLinks.emplace_back( pCrvLink->Clone()) ;
|
|
|
|
// pSeg2 ora diventa pSeg1 e il procedimento si itera per tutto il percorso
|
|
pLastSeg.Set( pSeg2) ;
|
|
}
|
|
// cerco nella stessa fila o in quella successiva sezione successiva raccordabile tramite il contorno
|
|
double dUstart = Sec.dOe ;
|
|
int nIndexIslandE = Sec.nOffIndE ; // isola di arrivo
|
|
double dUmin, dUmax ;
|
|
vFirstOff[nIndexIslandE]->GetDomain( dUmin, dUmax) ;
|
|
double dUspan = dUmax - dUmin ;
|
|
double dUref = ( bPlus ? INFINITO : -INFINITO) ;
|
|
int nNextI = -1 ;
|
|
int nNextJ = -1 ;
|
|
int li = nJ + 1 ;
|
|
for ( int k = nI ; k <= nI + 1 && k < int( vvSec.size()) ; ++ k) {
|
|
for ( int l = li ; l < int( vvSec[k].size()) ; ++ l) {
|
|
if ( ! vvSec[k][l].bActive) // se sezione non attiva... non la considero
|
|
continue ;
|
|
if ( vvSec[k][l].nOffIndS != nIndexIslandE) // se isola diversa... non la considero
|
|
continue ;
|
|
double dU = vvSec[k][l].dOs ;
|
|
if ( bPlus) {
|
|
if ( dU < dUstart)
|
|
dU += dUspan ;
|
|
if ( dU < dUref) {
|
|
dUref = dU ;
|
|
nNextI = k ;
|
|
nNextJ = l ;
|
|
}
|
|
}
|
|
else {
|
|
if ( dU > dUstart)
|
|
dU -= dUspan ;
|
|
if ( dU > dUref) {
|
|
dUref = dU ;
|
|
nNextI = k ;
|
|
nNextJ = l ;
|
|
}
|
|
}
|
|
}
|
|
li = 0 ;
|
|
}
|
|
// se trovato, controllo il contorno dell'isola
|
|
if ( nNextI != -1) {
|
|
PtrOwner<ICurve> pCopy ;
|
|
if ( bPlus) {
|
|
if ( dUref > dUmax)
|
|
dUref -= dUspan ;
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ;
|
|
if ( ! IsNull( pCopy)) {
|
|
double dCLen ; pCopy->GetLength( dCLen) ;
|
|
if ( dCLen > 0.5 * dLen) {
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ;
|
|
if ( ! IsNull( pCopy))
|
|
pCopy->Invert() ;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ( dUref < dUmin)
|
|
dUref += dUspan ;
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUref, dUstart)) ;
|
|
if ( ! IsNull( pCopy)) {
|
|
pCopy->Invert() ;
|
|
double dCLen; pCopy->GetLength( dCLen) ;
|
|
if ( dCLen > 0.5 * dLen)
|
|
pCopy.Set( vFirstOff[nIndexIslandE]->CopyParamRange( dUstart, dUref)) ;
|
|
}
|
|
}
|
|
// controllo che nel percorso scelto non ritorni indietro superando la precendete passata
|
|
BBox3d b3Copy ;
|
|
if ( ! IsNull( pCopy))
|
|
pCopy->GetLocalBBox( b3Copy) ;
|
|
if ( ! b3Copy.IsEmpty() && ( b3Copy.GetMax().y - b3Copy.GetMin().y) < dYStep + 10 * EPS_SMALL) { // tengo la curva (non ritorno indietro più dello step)
|
|
Point3d ptS, ptE ;
|
|
pCrvLink->Clear() ;
|
|
pCrvLink->AddCurve( pCopy->Clone()) ;
|
|
vpCrvs.back()->AddCurve( Release( pCopy)) ;
|
|
nStatus = 2 ; // aggiunta parte di isola
|
|
// aggiungo il Link
|
|
nI = nNextI ;
|
|
nJ = nNextJ ;
|
|
}
|
|
else {
|
|
nI = -1 ;
|
|
nJ = -1 ;
|
|
}
|
|
}
|
|
else {
|
|
nI = -1 ;
|
|
nJ = -1 ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddSpiralIn( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
|
|
PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// ricavo il numero di Chunk da svuotare
|
|
if ( pSrfPock == nullptr)
|
|
return false ;
|
|
int nChunk = pSrfPock->GetChunkCount() ;
|
|
|
|
// scorro i Chunk
|
|
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
|
|
|
|
// indice del Chunk da corrente da svuotare
|
|
int nChunkInd = 0 ;
|
|
if ( PockParams.ptStart.IsValid()) {
|
|
// se il punto di riferimento è valido, il Chunk è quello ad esso più vicino
|
|
DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ;
|
|
int nMinLoop = 0 ;
|
|
double dMinPar = 0. ;
|
|
if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar))
|
|
return false ;
|
|
}
|
|
|
|
// Clono il Chunk attuale come regione FlatRegion
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
|
|
const int MAX_REGS = 50 ; // massimo numero di regioni create dal primo offset
|
|
int nReg = 0 ; // chunk nuovo corrente da svuotare
|
|
|
|
// ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-esimo
|
|
while ( nReg < MAX_REGS) {
|
|
// calcolo la spirale dall'esterno all'interno e la curva che unisce inizio e fine
|
|
PtrOwner<ICurveComposite> pMCrv( CreateCurveComposite()) ;
|
|
if ( IsNull( pMCrv))
|
|
return false ;
|
|
|
|
int nRegTot = nReg ;
|
|
Point3d ptStart ;
|
|
|
|
// cerco le curve originali del chunk cc-esimo ( per casi ottimizzati)
|
|
ICRVCOMPOPOVECTOR vCrvOrigChunkLoops ;
|
|
if ( ! GetOptCrvIndex( pSfrOrig, pSfrChunk, PockParams, nReg, vCrvOrigChunkLoops))
|
|
return false ;
|
|
|
|
// calcolo il percorso di svuotatura
|
|
bool bSomeOpen ;
|
|
Vector3d vtMidOut ;
|
|
if ( ! CalcSpiral( pSfrChunk, pSfrOrig, PockParams, nRegTot, ptStart, vCrvOrigChunkLoops, pMCrv, bSomeOpen, vtMidOut))
|
|
return false ;
|
|
|
|
// se terminate le regioni, esco
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
break ; // passo al chunk originale successivo
|
|
|
|
bool bIsExtended = false ;
|
|
if ( bSomeOpen)
|
|
ExtendPath( pMCrv, pSfrOrig, PockParams, vtMidOut, false, GetExtendPathLen( PockParams), bIsExtended) ;
|
|
|
|
// inserisco le curve nel vettore
|
|
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
|
|
++ nReg ; // incremento il numero di regione progressiva
|
|
// aggiorno il punto di riferimento per il Chunk Successivo
|
|
vCrvCompoRes.back()->GetEndPoint( PockParams.ptStart) ;
|
|
}
|
|
|
|
// rimuovo il Chunk dalla regione
|
|
pSrfPock->EraseChunk( nChunkInd) ;
|
|
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddSpiralOut( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig,
|
|
PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// ricavo il numero di Chunk da svuotare
|
|
if ( pSrfPock == nullptr)
|
|
return false ;
|
|
int nChunk = pSrfPock->GetChunkCount() ;
|
|
|
|
// ciclo sui chunk della superficie da svuotare
|
|
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
|
|
|
|
// indice del Chunk da corrente da svuotare
|
|
int nChunkInd = 0 ;
|
|
if ( PockParams.ptStart.IsValid()) {
|
|
// se il punto di riferimento è valido, il Chunk è quello ad esso più vicino
|
|
DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ;
|
|
int nMinLoop = 0 ;
|
|
double dMinPar = 0. ;
|
|
if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar))
|
|
return false ;
|
|
}
|
|
|
|
// Clono il Chunk attuale come regione FlatRegion
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
|
|
const int MAX_REGS = 50 ; // massimo numero di regioni create dal primo offset
|
|
int nReg = 0 ; // chunk nuovo corrente da svuotare
|
|
|
|
// ciclo su tutte le regioni che posso ottenere col primo Offset del chunk c-esimo
|
|
while ( nReg < MAX_REGS) {
|
|
|
|
// calcolo la spirale dall'interno all'esterno
|
|
PtrOwner<ICurveComposite> pMCrv( CreateCurveComposite()) ;
|
|
if ( IsNull( pMCrv))
|
|
return false ;
|
|
|
|
int nRegTot = nReg ;
|
|
Point3d ptStart ;
|
|
|
|
// cerco la curva originale del chunk cc-esimo ( per casi ottimizzati)
|
|
ICRVCOMPOPOVECTOR vCrvOrig ;
|
|
if ( ! GetOptCrvIndex( pSfrOrig, pSfrChunk, PockParams, nReg, vCrvOrig))
|
|
return false ;
|
|
|
|
// calcolo il percorso di svuotatura
|
|
bool bSomeOpen ;
|
|
Vector3d vtMidOut ;
|
|
if ( ! CalcSpiral( pSfrChunk, pSfrOrig, PockParams, nRegTot, ptStart, vCrvOrig, pMCrv, bSomeOpen, vtMidOut))
|
|
return false ;
|
|
|
|
// se terminate le regioni, esco
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
break ; // passo al chunk originale successivo
|
|
|
|
// inverto il percorso, perchè sono calcolati dall'esterno all'interno (solo nel caso non ottimizzato)
|
|
pMCrv->Invert() ;
|
|
|
|
// inserisco le curve nel vettore
|
|
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
|
|
// incremento il numero di regione progressiva
|
|
++ nReg ;
|
|
// aggiorno il punto di riferimento per il Chunk Successivo
|
|
vCrvCompoRes.back()->GetEndPoint( PockParams.ptStart) ;
|
|
}
|
|
|
|
// rimuovo il Chunk dalla regione
|
|
pSrfPock->EraseChunk( nChunkInd) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetZigZagOneWayBorderCrvs( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig,
|
|
const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvRes)
|
|
{
|
|
/*
|
|
Restituisce le curve di Primo Offset dei lati chiusi per i percorsi a ZigZag e OneWay.
|
|
Vengono calcolate le Feed ed eventuali entrate da fuori per i percorsi ricavati
|
|
*/
|
|
|
|
// controllo validità della regione
|
|
if ( pSfrPock == nullptr || ! pSfrPock->IsValid())
|
|
return false ;
|
|
|
|
// recupero la superficie limite e calcolo Offset
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrLimit))
|
|
return false ;
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
pSfrLimit.Set( PockParams.SfrLimit.Clone()) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid())
|
|
return false ;
|
|
pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset - 50 * EPS_SMALL, ICurve::OFF_FILLET) ;
|
|
}
|
|
|
|
// vettore delle curve chiuse Offsettate
|
|
ICRVCOMPOPOVECTOR vClosedOffs ;
|
|
|
|
// scorro i Chunk e i Loop della regione ( se OneWay potrei avere più Chunks)
|
|
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
|
|
for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) {
|
|
ICRVCOMPOPOVECTOR vClosedOffs_nC ;
|
|
// loop come curva composita
|
|
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ;
|
|
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
|
|
return false ;
|
|
// recupero i tratti con proprietà uniformi
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
GetHomogeneousParts( pCrvLoop, PockParams, vpCrvs) ;
|
|
if ( vpCrvs.size() > 1) {
|
|
// unisco il primo e l'ultimo se estremi compatibili
|
|
Point3d ptE ; vpCrvs.back()->GetEndPoint( ptE) ;
|
|
Point3d ptS ; vpCrvs[0]->GetStartPoint( ptS) ;
|
|
if ( AreSamePointApprox( ptS, ptE)) {
|
|
vpCrvs[0]->AddCurve( Release( vpCrvs.back()), false) ;
|
|
vpCrvs.erase( vpCrvs.end() - 1) ;
|
|
}
|
|
}
|
|
// le isole aperte vanno percorse
|
|
bool bOpenIsland = ( nL > 0 && int( vpCrvs.size()) == 1 &&
|
|
vpCrvs[0]->GetTempProp() == TEMP_PROP_OPEN_EDGE) ;
|
|
// scorro tutti i tratti omogenei
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
// se tratto chiuso o isola aperta
|
|
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE || bOpenIsland) {
|
|
// Offset per le curve
|
|
OffsetCurve OffsCrv ;
|
|
double dOffs = - PockParams.dRad - PockParams.dRadialOffset ;
|
|
// se isola aperta, allora l'offset è verso il suo interno
|
|
if ( bOpenIsland)
|
|
dOffs *= -1 ;
|
|
if ( OffsCrv.Make( vpCrvs[i], dOffs, ICurve::OFF_FILLET)) {
|
|
PtrOwner<ICurve> pOffLongestCrv( OffsCrv.GetLongerCurve()) ;
|
|
while ( ! IsNull( pOffLongestCrv)) {
|
|
// recupero solo i tratti di curva al di fuori della superficie limite
|
|
if ( pSfrLimit->IsValid()) {
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrLimit->GetCurveClassification( *pOffLongestCrv, EPS_SMALL, ccClass)) {
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
if ( ccClass[j].nClass == CRVC_OUT) {
|
|
// recupero il tratto di curva
|
|
PtrOwner<ICurve> pCrv( pOffLongestCrv->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE)) ;
|
|
if ( ! IsNull( pCrv) && pCrv->IsValid())
|
|
vClosedOffs_nC.emplace_back( ConvertCurveToComposite( Release( pCrv))) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
vClosedOffs_nC.emplace_back( ConvertCurveToComposite( Release( pOffLongestCrv))) ;
|
|
|
|
// passo alla successiva
|
|
pOffLongestCrv.Set( OffsCrv.GetLongerCurve()) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se non ho ricavato curve dal Loop nL-esimo, passo al successivo
|
|
if ( vClosedOffs_nC.empty())
|
|
continue ;
|
|
// concateno eventuali percorsi
|
|
if ( ! ChainCompoCurves( vClosedOffs_nC))
|
|
return false ;
|
|
// miglioro le curve
|
|
for ( auto& crv : vClosedOffs_nC)
|
|
crv->MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG) ;
|
|
|
|
// determino ora il punto di inizio, il tratto per il LeadIn
|
|
bool bIsChunkClosed = false ;
|
|
bool bIsChunkAllOpen = false ;
|
|
if ( ! IsChunkAllHomogeneous( pSfrPock, nC, bIsChunkClosed, bIsChunkAllOpen))
|
|
return false ;
|
|
if ( ! AdvanceExtendCurves( vClosedOffs_nC, pSfrOrig, PockParams, true))
|
|
return false ;
|
|
|
|
// aggiungo le curve ottenute al vettore
|
|
for ( int i = 0 ; i < int( vClosedOffs_nC.size()) ; ++ i)
|
|
vClosedOffs.emplace_back( Release( vClosedOffs_nC[i])) ;
|
|
}
|
|
}
|
|
// se non ho percorsi ho finito
|
|
if ( vClosedOffs.empty())
|
|
return true ;
|
|
|
|
// Calcolo ora le Feed e restituisco le curve ottenute
|
|
for ( int i = 0 ; i < int( vClosedOffs.size()) ; ++ i) {
|
|
// se ZigZag -> ho percorso i tratti a ZigZag, quindi posso andare a Feed massima
|
|
if ( PockParams.nType == POCKET_ZIGZAG)
|
|
AssignMaxFeed( vClosedOffs[i], PockParams) ;
|
|
// se OneWay -> prima percorro queste curva, quindi la Feed è minima
|
|
if ( PockParams.nType == POCKET_ONEWAY)
|
|
AssignMinFeed( vClosedOffs[i], PockParams) ;
|
|
// assegno la proprità alla curva composita
|
|
vClosedOffs[i]->SetTempProp( TEMP_PROP_BORDER_CURVE, 0) ;
|
|
// restituisco le curve ottenute
|
|
vCrvRes.emplace_back( Release( vClosedOffs[i])) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddZigZag( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// ricavo il numero di Chunk da svuotare
|
|
if ( pSrfPock == nullptr)
|
|
return false ;
|
|
int nChunk = pSrfPock->GetChunkCount() ;
|
|
|
|
// offset della regione per curva ZigZag
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
if ( PockParams.bAllowZigZagOneWayBorders && ! PockParams.bPolishing)
|
|
dOffs += PockParams.dOffsExtra ;
|
|
|
|
// ciclo su tutti i chunks della superficie originale
|
|
for ( int nC = 0 ; nC < nChunk ; ++ nC) {
|
|
|
|
// indice del Chunk da corrente da svuotare
|
|
int nChunkInd = 0 ;
|
|
if ( PockParams.ptStart.IsValid()) {
|
|
// se il punto di riferimento è valido, il Chunk è quello ad esso più vicino
|
|
DistPointSurfFr DistPtSfr( PockParams.ptStart, *pSrfPock) ;
|
|
int nMinLoop = 0 ;
|
|
double dMinPar = 0. ;
|
|
if ( ! DistPtSfr.GetParamAtMinDist( nChunkInd, nMinLoop, dMinPar))
|
|
return false ;
|
|
}
|
|
|
|
// creo la regione per il percorso a ZigZag ( mediante primo Offset)
|
|
PtrOwner<ISurfFlatRegion> pSrfZigZag( pSrfPock->CloneChunk( nChunkInd)) ;
|
|
if ( ! pSrfZigZag->Offset( - dOffs, ICurve::OFF_FILLET))
|
|
return false ;
|
|
// vettore con i percorsi a ZigZag relativi al Chunk attuale
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
|
|
// ciclo sui Chunk ottenuti dal primo offset
|
|
for ( int nC1 = 0 ; nC1 < pSrfZigZag->GetChunkCount() ; ++ nC1) {
|
|
// recupero il Chunk attuale
|
|
PtrOwner<ISurfFlatRegion> pSrfZigZagChunk( pSrfZigZag->CloneChunk( nC1)) ;
|
|
if ( IsNull( pSrfZigZagChunk) || ! pSrfZigZagChunk->IsValid())
|
|
return false ;
|
|
|
|
// calcolo i percorsi di ZigZag
|
|
if ( ! CalcZigZag( pSrfZigZagChunk, PockParams, vpCrvs, false))
|
|
return false ;
|
|
|
|
// controllo esistenza di lati aperti per il Chunk attuale
|
|
bool bIsChunkClosed = true ;
|
|
bool bIsChunkAllOpen = true ;
|
|
if ( ! PockParams.bAllClosed && ! PockParams.bPolishing) {
|
|
if ( ! IsChunkAllHomogeneous( pSrfPock, nChunkInd, bIsChunkClosed, bIsChunkAllOpen))
|
|
return false ;
|
|
// se il Chunk originale aveva lati aperti, estendo il percorso a ZigZag
|
|
for ( int nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU) {
|
|
Vector3d vtRef ; vpCrvs[nU]->GetStartDir( vtRef) ;
|
|
vtRef.Invert() ;
|
|
bool bIsExtended = false ;
|
|
if ( ! ExtendPath( vpCrvs[nU], pSfrOrig, PockParams, vtRef, false, GetExtendPathLen( PockParams), bIsExtended))
|
|
return false ;
|
|
vpCrvs[nU]->GetEndDir( vtRef) ;
|
|
if ( ! ExtendPath( vpCrvs[nU], pSfrOrig, PockParams, vtRef, true, GetExtendPathLen( PockParams), bIsExtended))
|
|
return false ;
|
|
}
|
|
}
|
|
// se lucidatura
|
|
if ( PockParams.bPolishing) {
|
|
// ciclo sui percorsi
|
|
for ( int k = 0 ; k < int( vpCrvs.size()) ; ++ k) {
|
|
// se attacco a scivolo
|
|
if ( PockParams.nLiType == LEAD_IN_GLIDE) {
|
|
double dU ;
|
|
vpCrvs[k]->GetParamAtLength( PockParams.dLiTang, dU) ;
|
|
vpCrvs[k]->AddJoint( dU) ;
|
|
Point3d ptStart ;
|
|
vpCrvs[k]->GetStartPoint( ptStart) ;
|
|
vpCrvs[k]->ModifyStart( ptStart + Z_AX * PockParams.dLiElev) ;
|
|
}
|
|
// se uscita a scivolo
|
|
if ( PockParams.nLoType == LEAD_OUT_GLIDE) {
|
|
double dLen, dU ;
|
|
vpCrvs[k]->GetLength( dLen) ;
|
|
vpCrvs[k]->GetParamAtLength( dLen - PockParams.dLoTang, dU) ;
|
|
vpCrvs[k]->AddJoint( dU) ;
|
|
Point3d ptEnd ;
|
|
vpCrvs[k]->GetEndPoint( ptEnd) ;
|
|
vpCrvs[k]->ModifyEnd( ptEnd + Z_AX * PockParams.dLiElev) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// inserisco le curve nel vettore risultante
|
|
for ( int nU = 0 ; nU < int( vpCrvs.size()) ; ++ nU)
|
|
vCrvCompoRes.emplace_back( Release( vpCrvs[nU])) ;
|
|
|
|
// libero il vettore di curve ZigZag per il chunk successivo
|
|
vpCrvs.clear() ;
|
|
}
|
|
|
|
// se richiesto, aggiungo le curve chiuse di Bordo
|
|
if ( PockParams.bAllowZigZagOneWayBorders && ! PockParams.bPolishing) {
|
|
// recupero il Chunk nC-esimo
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSrfPock->CloneChunk( nChunkInd)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid() ||
|
|
! GetZigZagOneWayBorderCrvs( pSfrChunk, pSfrOrig, PockParams, vCrvCompoRes))
|
|
return false ;
|
|
}
|
|
|
|
// rimuovo il Chunk dalla regione
|
|
pSrfPock->EraseChunk( nChunkInd) ;
|
|
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
OptimizeChunkOneWay( const ISurfFlatRegion* pSfrOrig, ISURFFRPOVECTOR& vSrfIdeal)
|
|
{
|
|
// controllo parametri
|
|
if ( pSfrOrig == nullptr || pSfrOrig->GetChunkCount() == 0)
|
|
return false ;
|
|
vSrfIdeal.clear() ;
|
|
|
|
/*
|
|
classifico i chunks in modo da creare delle regioni ideali; una regione ideale è formata
|
|
da un chunk principale con tutti gli altri contenuti in esso ( in questo modo ottimizzo i
|
|
percorsi per i bordi)
|
|
*/
|
|
|
|
INTVECTOR vChunksAvailable( pSfrOrig->GetChunkCount(), 1) ;
|
|
for ( int nC = 0 ; nC < pSfrOrig->GetChunkCount() ; ++ nC) {
|
|
PtrOwner<ISurfFlatRegion> pSrfIdeal( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfIdeal))
|
|
return false ;
|
|
// se Chunk valido...
|
|
if ( vChunksAvailable[nC] == 1) {
|
|
// prendo la curva esterna
|
|
PtrOwner<ICurve> pCrvExt( pSfrOrig->GetLoop( nC, 0)) ;
|
|
if ( IsNull( pCrvExt))
|
|
return false ;
|
|
// inserisco il chunk nC-esimo ( curva per curva, così non perdo le temp prop)
|
|
pSrfIdeal->AddExtLoop( pCrvExt->Clone()) ;
|
|
for ( int nL = 1 ; nL < pSfrOrig->GetLoopCount( nC) ; ++ nL)
|
|
pSrfIdeal->AddIntLoop( pSfrOrig->GetLoop( nC, nL)) ;
|
|
// il chunk nC-esimo non è più disponibile...
|
|
vChunksAvailable[nC] = -1 ;
|
|
// scorro tutti gli altri chunk i-esimi ancora disponibili
|
|
for ( int i = 0 ; i < int( vChunksAvailable.size()) ; ++ i) {
|
|
if ( vChunksAvailable[i] == 1) {
|
|
// prendo la curva esterna del chunk i-esimo disponibile
|
|
PtrOwner<ICurve> pCrvExtC( pSfrOrig->GetLoop( i, 0)) ;
|
|
if ( IsNull( pCrvExtC))
|
|
return false ;
|
|
// classifico i bordi esterni ( se il bordo del chunk nC-esimo contiene o è contenuto nel loop
|
|
// esterno del chunk i-esimo -> sono chunks appartenenti alla stessa superficie ideale)
|
|
IntersCurveCurve intCC( *pCrvExtC, *pCrvExt) ;
|
|
CRVCVECTOR ccClass, ccClass1 ;
|
|
intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ;
|
|
intCC.GetCurveClassification( 1, EPS_SMALL, ccClass1) ;
|
|
if (( int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) ||
|
|
( int( ccClass1.size()) == 1 && ccClass1[0].nClass == CRVC_IN)) {
|
|
// inserisco il chunk i-esimo ( curva per curva, così non perdo le temp prop)
|
|
pSrfIdeal->AddExtLoop( pSfrOrig->GetLoop( i, 0)) ;
|
|
for ( int nL = 1 ; nL < pSfrOrig->GetLoopCount( i) ; ++ nL)
|
|
pSrfIdeal->AddIntLoop( pSfrOrig->GetLoop( i, nL)) ;
|
|
vChunksAvailable[i] = -1 ; // chunk j-esimo non più disponibile
|
|
// NB: Se il chunk i-esimo contiene la pSrfIdeal ( quindi chunk i-esimo unito ad eventuali altri chunk
|
|
// j-esimi precedenti -> prendo come bordo esterno di riferimento il suo e ricontrollo dall'inizio i
|
|
// chunk disponibili
|
|
if ( int( ccClass1.size()) == 1 && ccClass1[0].nClass == CRVC_IN) {
|
|
pCrvExt.Set( pCrvExtC) ;
|
|
i = 0 ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( pSrfIdeal->GetChunkCount() > 0)
|
|
vSrfIdeal.emplace_back( Release( pSrfIdeal)) ;
|
|
}
|
|
|
|
return ( ! vSrfIdeal.empty()) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddOneWay( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// offset della regione per segmenti a ZigZag
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset ;
|
|
double dExtra = 0. ;
|
|
if ( PockParams.bAllowZigZagOneWayBorders)
|
|
dExtra = PockParams.dOffsExtra ;
|
|
|
|
// vettore delle regioni ideali
|
|
ISURFFRPOVECTOR vSrfFlat ;
|
|
if ( ! OptimizeChunkOneWay( pSrfPock, vSrfFlat))
|
|
return false ;
|
|
|
|
// scorro le regioni ideali
|
|
for ( int nIs = 0 ; nIs < int( vSrfFlat.size()) ; ++ nIs) {
|
|
|
|
// copio la superficie ideale ed effettuo il primo Offset
|
|
PtrOwner<ISurfFlatRegion> pSrfIdeal( CloneSurfFlatRegion( vSrfFlat[nIs])) ;
|
|
if ( IsNull( pSrfIdeal) || ! pSrfIdeal->Offset( - dOffs, ICurve::OFF_FILLET))
|
|
return false ;
|
|
|
|
// se sono richieste le curve di Bordo, le aggiungo
|
|
if ( PockParams.bAllowZigZagOneWayBorders) {
|
|
if ( ! GetZigZagOneWayBorderCrvs( vSrfFlat[nIs], pSfrOrig, PockParams, vCrvCompoRes))
|
|
return false ;
|
|
}
|
|
|
|
// recupero il Box della superficie attuale da svuotare
|
|
BBox3d b3Pocket ; pSrfIdeal->GetLocalBBox( b3Pocket) ;
|
|
Point3d ptMin ; double dDimX, dDimY, dDimZ ;
|
|
b3Pocket.GetMinDim( ptMin, dDimX, dDimY, dDimZ) ;
|
|
|
|
// passi in Y
|
|
int nYStep = static_cast<int>( ceil(( dDimY + 2 * dExtra) / PockParams.dSideStep)) ;
|
|
double dYStep = ( nYStep > 0 ? ( dDimY + 2 * dExtra) / nYStep : 0) ;
|
|
-- nYStep ;
|
|
|
|
// vettore dei segmenti al di sotto della linea corrente ( per le Feed)
|
|
ICURVEPOVECTOR vLineUnder ;
|
|
ICURVEPOVECTOR vCrvNull ;
|
|
ICRVCOMPOPOVECTOR vCrvCompoNull ;
|
|
|
|
// calcolo le linee di svuotatura
|
|
const double EXP_LEN = 1.0 ;
|
|
for ( int i = 1 ; i <= nYStep ; ++ i) {
|
|
|
|
// definisco la linea
|
|
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
|
|
Point3d ptStart( ptMin.x - EXP_LEN, ptMin.y + ( - dExtra + i * dYStep), ptMin.z + dDimZ) ;
|
|
if ( IsNull( pLine) || ! pLine->SetPVL( ptStart, X_AX, dDimX + 2 * EXP_LEN))
|
|
return false ;
|
|
|
|
// se richiesta inversione
|
|
if ( PockParams.bInvert)
|
|
pLine->Invert() ;
|
|
|
|
// linea come composita per Feed ( la dovrò spezzare in tratti uniformi di Feed)
|
|
PtrOwner<ICurveComposite> pCrvCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvCompo))
|
|
return false ;
|
|
|
|
// vettore di tutti i segmenti lineari che si formano nello step nYStep
|
|
ICURVEPOVECTOR vAddedLines ;
|
|
|
|
// riempio il vettore di segmenti
|
|
CRVCVECTOR ccClassSeg ;
|
|
pSrfIdeal->GetCurveClassification( *pLine, EPS_SMALL, ccClassSeg) ;
|
|
for ( int w = 0 ; w < int( ccClassSeg.size()) ; ++ w) {
|
|
if ( ccClassSeg[w].nClass == CRVC_IN) {
|
|
|
|
PtrOwner<ICurveLine> pCrvSeg( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ;
|
|
double duF, dLen ;
|
|
// accorcio leggermente il segmento per non toccare la prima curva di Offset
|
|
if ( ! pCrvSeg->GetLength( dLen) ||
|
|
dLen < 2 * dExtra ||
|
|
! pCrvSeg->GetParamAtLength( dLen - dExtra, duF) ||
|
|
! pCrvSeg->TrimStartAtLen( dExtra) ||
|
|
! pCrvSeg->TrimEndAtParam( duF))
|
|
pCrvSeg.Set( GetCurveLine( pLine->CopyParamRange( ccClassSeg[w].dParS, ccClassSeg[w].dParE))) ;
|
|
|
|
// aggiungo il segmento al vettore dei tratti correnti
|
|
vAddedLines.emplace_back( pCrvSeg->Clone()) ;
|
|
|
|
// trasformo il tratto lineare in curve composita per il calcolo della Feed
|
|
PtrOwner<ICurveComposite> pCrvSegCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvSegCompo))
|
|
return false ;
|
|
pCrvSegCompo->AddCurve( Release( pCrvSeg)) ;
|
|
// calcolo la Feed
|
|
AssignFeedZigZagOneWay( pCrvSegCompo, false, vCrvNull, vLineUnder, vCrvCompoNull, PockParams) ;
|
|
// estendo presso lati aperti se richiesto
|
|
if ( ! PockParams.bAllClosed) {
|
|
Vector3d vtRef ; pCrvSegCompo->GetStartDir( vtRef) ;
|
|
vtRef.Invert() ;
|
|
bool bIsExtended = false ;
|
|
if ( ! ExtendPath( pCrvSegCompo, pSfrOrig, PockParams, vtRef, false, GetExtendPathLen( PockParams), bIsExtended))
|
|
return false ;
|
|
pCrvSegCompo->GetEndDir( vtRef) ;
|
|
if ( ! ExtendPath( pCrvSegCompo, pSfrOrig, PockParams, vtRef, true, GetExtendPathLen( PockParams), bIsExtended))
|
|
return false ;
|
|
}
|
|
vCrvCompoRes.emplace_back( Release( pCrvSegCompo)) ;
|
|
}
|
|
}
|
|
// i tratti lineari correnti diventano i precedenti
|
|
vLineUnder.clear() ;
|
|
for ( int u = 0 ; u < int( vAddedLines.size()) ; ++ u)
|
|
vLineUnder.emplace_back( Release( vAddedLines[u])) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SmoothLinkByOffs( ICurveComposite* pCrvOffs0, ICurveComposite* pCrvOffs1, ICurveComposite* pCrvLink,
|
|
const PocketParams& PockParams, bool bFirstOffs0, bool bFirstOffs1, double dSmoothPar, double dTol)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
|
|
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid() ||
|
|
pCrvLink == nullptr || ! pCrvLink->IsValid())
|
|
return false ;
|
|
|
|
// definisco la lunghezza del segmento iniziale e finale sulle due curve di Offset
|
|
double dRefLenSeg = 2. * dSmoothPar ;
|
|
|
|
// --- Curva precedente ---
|
|
// recupero la lunghezza e il dominio del primo Offset
|
|
double dLen ;
|
|
pCrvOffs0->GetLength( dLen) ;
|
|
double dUStart, dUEnd ;
|
|
pCrvOffs0->GetDomain( dUStart, dUEnd) ;
|
|
// inizializzo la curva
|
|
PtrOwner<ICurveComposite> pCrvBef( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvBef))
|
|
return false ;
|
|
// se primo Offset di bordo o chiuso...
|
|
if ( bFirstOffs0 && pCrvOffs0->IsClosed()) {
|
|
double dU = dUEnd ;
|
|
if ( dLen > dRefLenSeg - EPS_SMALL)
|
|
pCrvOffs0->GetParamAtLength( dRefLenSeg, dU) ;
|
|
PtrOwner<ICurve> pMyCrv( pCrvOffs0->CopyParamRange( dUStart, dU)) ;
|
|
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
|
|
return false ;
|
|
pCrvBef->AddCurve( Release( pMyCrv)) ;
|
|
}
|
|
// altrimenti...
|
|
else {
|
|
double dU = dUStart ;
|
|
if ( dLen > dRefLenSeg)
|
|
pCrvOffs0->GetParamAtLength( dLen - dRefLenSeg, dU) ;
|
|
PtrOwner<ICurve> pMyCrv( pCrvOffs0->CopyParamRange( dU, dUEnd)) ;
|
|
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
|
|
return false ;
|
|
pCrvBef->AddCurve( Release( pMyCrv)) ;
|
|
}
|
|
// se non valida, errore
|
|
if ( ! pCrvBef->IsValid())
|
|
return false ;
|
|
|
|
// --- Curva successiva ---
|
|
// recupero la lunghezza e il dominio del primo Offset
|
|
pCrvOffs1->GetLength( dLen) ;
|
|
pCrvOffs1->GetDomain( dUStart, dUEnd) ;
|
|
// inizializzo la curva
|
|
PtrOwner<ICurveComposite> pCrvAft( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvAft))
|
|
return false ;
|
|
// calcolo il parametro di taglio
|
|
double dU = dUEnd ;
|
|
if ( dLen > dRefLenSeg - EPS_SMALL)
|
|
pCrvOffs1->GetParamAtLength( dRefLenSeg, dU) ;
|
|
PtrOwner<ICurve> pMyCrv( pCrvOffs1->CopyParamRange( dUStart, dU)) ;
|
|
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
|
|
return false ;
|
|
pCrvAft->AddCurve( Release( pMyCrv)) ;
|
|
// se non valida, errore
|
|
if ( ! pCrvAft->IsValid())
|
|
return false ;
|
|
|
|
// estendo il Link ( linea di ingresso, segmento Link, linea d'uscita)
|
|
pCrvLink->AddCurve( Release( pCrvBef), false, dTol) ;
|
|
pCrvLink->AddCurve( Release( pCrvAft), true, dTol) ;
|
|
|
|
// miglioro la curva
|
|
pCrvLink->MergeCurves( 100 * EPS_SMALL, ANG_TOL_STD_DEG) ;
|
|
|
|
// smusso la curva
|
|
ModifyCurveToSmoothed( pCrvLink, PockParams, dSmoothPar, dSmoothPar, false) ;
|
|
|
|
// toglo gli estremi, il Link è in tangenza e le linee di ingresso e d'uscita sono più
|
|
// estese dell'arco di smusso creato
|
|
delete( pCrvLink->RemoveFirstOrLastCurve( false)) ;
|
|
delete( pCrvLink->RemoveFirstOrLastCurve( true)) ;
|
|
|
|
// modifico il primo Offset per raccordarlo al nuovo Link
|
|
if ( bFirstOffs0 && pCrvOffs0->IsClosed()) {
|
|
const ICurve* pCrvFirst = pCrvLink->GetFirstCurve() ;
|
|
if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid())
|
|
return false ;
|
|
PtrOwner<ICurve> pMyCrv( pCrvFirst->Clone()) ;
|
|
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
|
|
return false ;
|
|
pCrvOffs0->AddCurve( Release( pMyCrv)) ;
|
|
pCrvLink->RemoveFirstOrLastCurve( false) ;
|
|
}
|
|
else {
|
|
Point3d ptS ; pCrvLink->GetStartPoint( ptS) ;
|
|
double dUE ; pCrvOffs0->GetParamAtPoint( ptS, dUE, dTol) ;
|
|
pCrvOffs0->TrimEndAtParam( dUE) ;
|
|
}
|
|
|
|
// modifico il secondo Offset per raccordarlo al nuovo Link
|
|
Point3d ptE ; pCrvLink->GetEndPoint( ptE) ;
|
|
double dUS ; pCrvOffs1->GetParamAtPoint( ptE, dUS, dTol) ;
|
|
if ( bFirstOffs1 && pCrvOffs1->IsClosed())
|
|
pCrvOffs1->ChangeStartPoint( dUS) ;
|
|
else
|
|
pCrvOffs1->TrimStartAtParam( dUS) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
static bool
|
|
GetConformalLinkForOpenCrv( const ICurveComposite* pCrvOffs0, const ICurveComposite* pCrvOffs1,
|
|
const ICRVCOMPOPOVECTOR& vCrvClassBorder, const PocketParams& PockParams,
|
|
double dTol, ICurveComposite* pCrvLink)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
|
|
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid())
|
|
return false ;
|
|
pCrvLink->Clear() ;
|
|
|
|
// ricavo gli estremi del Link
|
|
Point3d ptLinkS ; pCrvOffs0->GetEndPoint( ptLinkS) ;
|
|
Point3d ptLinkE ; pCrvOffs1->GetStartPoint( ptLinkE) ;
|
|
// ricavo i parametri sulla curva di bordo che interessa i due estremi ( 1 sola !)
|
|
for ( int i = 0 ; i < int( vCrvClassBorder.size()) ; ++ i) {
|
|
if ( vCrvClassBorder[i]->IsPointOn( ptLinkS, dTol) &&
|
|
vCrvClassBorder[i]->IsPointOn( ptLinkE, dTol)) {
|
|
double dPar0 ; vCrvClassBorder[i]->GetParamAtPoint( ptLinkS, dPar0, dTol) ;
|
|
double dPar1 ; vCrvClassBorder[i]->GetParamAtPoint( ptLinkE, dPar1, dTol) ;
|
|
// ricavo le due curve possibili di Link
|
|
PtrOwner<ICurveComposite> pCrvLinkA( ConvertCurveToComposite( vCrvClassBorder[i]->CopyParamRange( dPar0, dPar1))) ;
|
|
PtrOwner<ICurveComposite> pCrvLinkB( ConvertCurveToComposite( vCrvClassBorder[i]->CopyParamRange( dPar1, dPar0))) ;
|
|
// scelgo quella migliore
|
|
double dLenA = 0. ;
|
|
if ( ! IsNull( pCrvLinkA) && pCrvLinkA->IsValid())
|
|
pCrvLinkA->GetLength( dLenA) ;
|
|
double dLenB = 0. ;
|
|
if ( ! IsNull( pCrvLinkB) && pCrvLinkB->IsValid()) {
|
|
pCrvLinkB->Invert() ;
|
|
pCrvLinkB->GetLength( dLenB) ;
|
|
}
|
|
if ( dLenA > dLenB) {
|
|
if ( ! IsNull( pCrvLinkB) && pCrvLinkB->IsValid())
|
|
pCrvLink->CopyFrom( pCrvLinkB) ;
|
|
}
|
|
else {
|
|
if ( ! IsNull( pCrvLinkA) && pCrvLinkA->IsValid())
|
|
pCrvLink->CopyFrom( pCrvLinkA) ;
|
|
}
|
|
break ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcConformalLink( ICurveComposite* pCrvOffs0, ICurveComposite* pCrvOffs1, PocketParams& PockParams,
|
|
const ICRVCOMPOPOVECTOR& vCrvClassBorder, ICurveComposite* pCrvLink)
|
|
{
|
|
// controllo validità delle due curve di Offset
|
|
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
|
|
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid())
|
|
return false ;
|
|
pCrvLink->Clear() ;
|
|
const double TOL = 150 * EPS_SMALL ;
|
|
|
|
// flag per curve di Offset aperte o chiuse
|
|
bool bOpen0 = ( ! pCrvOffs0->IsClosed()) ;
|
|
bool bOpen1 = ( ! pCrvOffs1->IsClosed()) ;
|
|
|
|
// se passo da una curva aperta ad un'altra curva aperta ( uso i bordi di pSfrClass)
|
|
if ( bOpen0 && bOpen1) {
|
|
// ricavo la curva di Link
|
|
if ( ! GetConformalLinkForOpenCrv( pCrvOffs0, pCrvOffs1, vCrvClassBorder, PockParams, TOL, pCrvLink))
|
|
return false ;
|
|
// smusso il Link raccordandolo
|
|
if ( pCrvLink->IsValid())
|
|
SmoothLinkByOffs( pCrvOffs0, pCrvOffs1, pCrvLink, PockParams, false, false, PockParams.dRad / 8., TOL) ;
|
|
}
|
|
else {
|
|
// clono le due curve di Offset
|
|
PtrOwner<ICurveComposite> pCrvOffs0_cl( CloneCurveComposite( pCrvOffs0)) ;
|
|
PtrOwner<ICurveComposite> pCrvOffs1_cl( CloneCurveComposite( pCrvOffs1)) ;
|
|
if ( IsNull( pCrvOffs0_cl) || IsNull( pCrvOffs1_cl) || ! pCrvOffs0_cl->IsValid() || ! pCrvOffs1_cl->IsValid())
|
|
return false ;
|
|
// controllo se gli Offset sono stati ricavati alla prima iterazione
|
|
bool bFirstIterOffs0 = ( pCrvOffs0->GetTempProp( 0) == 0) ;
|
|
bool bFirstIterOffs1 = ( pCrvOffs1->GetTempProp( 0) == 0) ;
|
|
// definisco nuovo parametro di smusso
|
|
bool bSmooth = PockParams.bSmooth ;
|
|
PockParams.bSmooth = ( PockParams.nType == POCKET_CONFORMAL_ONEWAY && bSmooth) ;
|
|
// cerco il chunk ipotetico che contiene le due curve di Offset
|
|
INTVECTOR vIndOffs0, vIndOffs1 ;
|
|
if ( ! GetIndOfHypoteticalChunk( vCrvClassBorder, pCrvOffs0, pCrvOffs1, vIndOffs0, vIndOffs1))
|
|
return false ;
|
|
// se appartengono a due chunk diversi, allora il link non esiste
|
|
if ( int( vIndOffs0.size()) != int( vIndOffs1.size()))
|
|
return true ;
|
|
for ( int i = 0 ; i < int( vIndOffs0.size()) ; ++ i) {
|
|
if ( vIndOffs0[i] != vIndOffs1[i])
|
|
return true ;
|
|
}
|
|
// se vuoti, allora il link non esiste
|
|
if ( vIndOffs0.empty() || vIndOffs1.empty()) // &&
|
|
return true ;
|
|
// se appartenenti allo stesso chunk, allora creo link lineare
|
|
ICRVCOMPOPOVECTOR vCrvChunk ;
|
|
for ( int i = 0 ; i < int( vIndOffs0.size()) ; ++ i)
|
|
vCrvChunk.emplace_back( vCrvClassBorder[vIndOffs0[i]]->Clone()) ;
|
|
// se la curva successiva è chiusa, aggiorno il suo punto iniziale a quello a minima distanza
|
|
if ( ! bOpen1) {
|
|
Point3d ptE ; pCrvOffs0->GetEndPoint( ptE) ;
|
|
double dPar ;
|
|
int nFlag ;
|
|
if ( DistPointCurve( ptE, *pCrvOffs1).GetParamAtMinDistPoint( 0., dPar, nFlag))
|
|
pCrvOffs1->ChangeStartPoint( dPar) ;
|
|
}
|
|
if ( ! CutCurveToConnect( pCrvOffs0, pCrvOffs1, vCrvChunk, PockParams,
|
|
bOpen0 ? 0. : 10 * EPS_SMALL, bOpen1 ? 0. : 10 * EPS_SMALL, pCrvLink) ||
|
|
! pCrvLink->IsValid()) {
|
|
// se curva di Link non valida, cerco una strada più semplice
|
|
pCrvLink->Clear() ;
|
|
pCrvOffs0->CopyFrom( pCrvOffs0_cl) ;
|
|
pCrvOffs1->CopyFrom( pCrvOffs1_cl) ;
|
|
// recupero i vettori tangente iniziali ( le curve sono chiuse )
|
|
Vector3d vtS, vtE ;
|
|
Point3d ptS, ptE ;
|
|
if ( ! pCrvOffs0->GetStartDir( vtS) || ! pCrvOffs1->GetStartDir( vtE) ||
|
|
! pCrvOffs0->GetStartPoint( ptS) || ! pCrvOffs1->GetStartPoint( ptE))
|
|
return false ;
|
|
// creo il bi-arco tra esse
|
|
bool bSpecial = false ;
|
|
if ( ! CalcBoundedSmoothedLink( ptS, vtS, ptE, vtE, 0.5, vCrvChunk, PockParams, pCrvLink, bSpecial) ||
|
|
! pCrvLink->IsValid())
|
|
return false ;
|
|
}
|
|
PockParams.bSmooth = bSmooth ;
|
|
// nel caso di Conformal ZigZag
|
|
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) {
|
|
// effettuo smusso del tratto lineare
|
|
SmoothLinkByOffs( pCrvOffs0, pCrvOffs1, pCrvLink, PockParams, bFirstIterOffs0, bFirstIterOffs1,
|
|
PockParams.dSmooth, TOL) ;
|
|
// per sicurezza aggiorno i nuovi punti e i nuovi parametri
|
|
double dUNewE ;
|
|
Point3d ptStartNext ;
|
|
pCrvLink->GetEndPoint( ptStartNext) ;
|
|
pCrvOffs1->GetParamAtPoint( ptStartNext, dUNewE) ;
|
|
pCrvOffs1->ChangeStartPoint( dUNewE) ;
|
|
}
|
|
// nel caso Conformal OneWay, taglio e unisco le curve ricavate
|
|
else if ( PockParams.nType == POCKET_CONFORMAL_ONEWAY) {
|
|
// per sicurezza aggiorno i nuovi punti e i nuovi parametri
|
|
double dUNewS, dUNewE ;
|
|
Point3d ptS, ptStartNext ;
|
|
if ( ! pCrvLink->GetStartPoint( ptS) ||
|
|
! pCrvLink->GetEndPoint( ptStartNext) ||
|
|
! pCrvOffs0->GetParamAtPoint( ptS, dUNewS) ||
|
|
! pCrvOffs1->GetParamAtPoint( ptStartNext, dUNewE))
|
|
return false ;
|
|
|
|
// imposto il punto iniziale della curva successiva ( i+1 esima)
|
|
pCrvOffs1->ChangeStartPoint( dUNewE) ;
|
|
PtrOwner<ICurveComposite> pCrvNewOffs( CloneCurveComposite( pCrvOffs0)) ;
|
|
if ( dUNewS > EPS_SMALL) {
|
|
pCrvNewOffs.Set( ConvertCurveToComposite( pCrvOffs0->CopyParamRange( 0, dUNewS))) ;
|
|
// sostituisco la curva i esima con quella tagliata
|
|
pCrvOffs0->CopyFrom( pCrvNewOffs) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CheckConformalRetractLink( const ICurveComposite* pCrvOffs0, const ICurveComposite* pCrvOffs1,
|
|
const ICRVCOMPOPOVECTOR& vCrvClassBorder, const PocketParams& PockParams,
|
|
bool& bCalcLink)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvOffs0 == nullptr || ! pCrvOffs0->IsValid() ||
|
|
pCrvOffs1 == nullptr || ! pCrvOffs1->IsValid())
|
|
return false ;
|
|
bCalcLink = true ; // di base calcolo il Link
|
|
|
|
// recupero gli estremi di un possibile collegamento tra i punti
|
|
Point3d ptA ; pCrvOffs0->GetEndPoint( ptA) ;
|
|
Point3d ptB ; pCrvOffs1->GetStartPoint( ptB) ;
|
|
// se vicini -> no retroazione
|
|
if ( SqDist( ptA, ptB) < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL)
|
|
return true ;
|
|
// se distanti
|
|
else {
|
|
// controllo se sono aperte
|
|
bool bOpen_i = ( ! pCrvOffs0->IsClosed()) ;
|
|
bool bOpen_ii = ( ! pCrvOffs1->IsClosed()) ;
|
|
// se entrambe aperte
|
|
if ( bOpen_i && bOpen_ii) {
|
|
// calcolo il collegamento tra le due curve
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink) ||
|
|
! GetConformalLinkForOpenCrv( pCrvOffs0, pCrvOffs1, vCrvClassBorder, PockParams,
|
|
25 * EPS_SMALL, pCrvLink))
|
|
return false ;
|
|
// se Link valido calcolo la sua lunghezza
|
|
double dLinkLen = 0. ;
|
|
if ( pCrvLink->IsValid())
|
|
pCrvLink->GetLength( dLinkLen) ;
|
|
// minima distanza da ptA
|
|
DistPointCurve DistPtACrv( ptA, *pCrvOffs1) ;
|
|
double dMyDist ;
|
|
if ( DistPtACrv.GetSqDist( dMyDist) &&
|
|
dMyDist < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL) {
|
|
bCalcLink = ( dLinkLen < 4 * PockParams.dRad) ;
|
|
return true ;
|
|
}
|
|
// minima distanza da ptB
|
|
DistPointCurve DistPtBCrv( ptB, *pCrvOffs0) ;
|
|
if ( DistPtBCrv.GetSqDist( dMyDist) &&
|
|
dMyDist < 4 * PockParams.dRad * PockParams.dRad + 50 * EPS_SMALL) {
|
|
bCalcLink = ( dLinkLen < 4 * PockParams.dRad) ;
|
|
return true ;
|
|
}
|
|
}
|
|
// ... servono altri controlli ??? ( vedere dai casi)
|
|
}
|
|
|
|
bCalcLink = false ;
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetConformalIndices( const VICRVCOMPOPOVECTOR& vCrvOffs, int nMyInd0, int nMyInd1,
|
|
const PocketParams& PockParams, const ICRVCOMPOPOVECTOR& vCrvClassBorder,
|
|
int& nInd0, int& nInd1)
|
|
{
|
|
// controllo che l'indice sia sensato
|
|
if ( vCrvOffs.empty() || nMyInd0 < 0 || nMyInd0 >= int( vCrvOffs.size()) ||
|
|
( nMyInd1 < 0 || nMyInd1 >= int( vCrvOffs[nMyInd0].size())))
|
|
return false ;
|
|
nInd0 = -1 ;
|
|
nInd1 = -1 ;
|
|
|
|
// il punto di riferimento è il punto finale della curva corrente
|
|
Point3d ptRef ; vCrvOffs[nMyInd0][nMyInd1]->GetEndPoint( ptRef) ;
|
|
// controllo se la curva è chiusa o aperta
|
|
bool bIsClosed = ( vCrvOffs[nMyInd0][nMyInd1]->IsClosed()) ;
|
|
// controllo se la curva è di primo Offset ( in questo caso non ho curve successive)
|
|
bool bFistOffs = ( nMyInd0 == int( vCrvOffs.size() - 1)) ;
|
|
|
|
// scorro le curve di indice successivo
|
|
double dLimInfDist = INFINITO ;
|
|
int nInd = -1 ;
|
|
for ( int i = 0 ; ! bFistOffs && i < int( vCrvOffs[nMyInd0 + 1].size()) ; ++ i) {
|
|
// se non attiva, passo alla successiva
|
|
if ( vCrvOffs[nMyInd0 + 1][i]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE)
|
|
continue ;
|
|
// se di topologia differente, passo alla successiva
|
|
if ( ( bIsClosed != vCrvOffs[nMyInd0 + 1][i]->IsClosed()))
|
|
continue ;
|
|
// se richiede retroazione, passo alla successiva
|
|
bool bCalcLink = true ;
|
|
if ( ! CheckConformalRetractLink( vCrvOffs[nMyInd0][nMyInd1], vCrvOffs[nMyInd0 + 1][i],
|
|
vCrvClassBorder, PockParams, bCalcLink))
|
|
return false ;
|
|
if ( ! bCalcLink)
|
|
continue ;
|
|
// se aperta
|
|
if ( ! bIsClosed) {
|
|
Point3d ptStart ; vCrvOffs[nMyInd0 + 1][i]->GetStartPoint( ptStart) ;
|
|
double dDist = SqDist( ptRef, ptStart) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = i ;
|
|
}
|
|
}
|
|
// se chiusa
|
|
else {
|
|
DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nMyInd0 + 1][i]) ;
|
|
MinDistPCInfo aInfo ;
|
|
if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) {
|
|
double dDist = SqDist( ptRef, aInfo.ptQ) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = i ;
|
|
vCrvOffs[nMyInd0 + 1][i]->ChangeStartPoint( aInfo.dPar) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se curva trovata, restituisco gli indici
|
|
if ( nInd != -1) {
|
|
nInd0 = nMyInd0 + 1 ;
|
|
nInd1 = nInd ;
|
|
return true ;
|
|
}
|
|
/* se non ho trovato alcuna curva, allora possono verificarsi le seguenti situazioni :
|
|
- all'indice successivo ho solo curve di topologia differente
|
|
- ho già percorso tutte le curve topologicamente uguali all'indice successivo
|
|
- sono una curva di primo Offset
|
|
- tutte le curve all'indice succesivo di topologia equivalente richiedono una retroazione
|
|
=> devo azzerare nInd0 e ricercare la curva di medesima topologia valida più vicina
|
|
*/
|
|
nInd0 = 0 ;
|
|
nInd = -1 ;
|
|
for ( ; nInd0 < int( vCrvOffs.size()) ; ++ nInd0) {
|
|
nInd1 = 0 ;
|
|
dLimInfDist = INFINITO ;
|
|
for ( ; nInd1 < int( vCrvOffs[nInd0].size()) ; ++ nInd1) {
|
|
// se non attiva, passo alla successiva
|
|
if ( vCrvOffs[nInd0][nInd1]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE)
|
|
continue ;
|
|
// se di topologia differente, passo alla successiva
|
|
if ( ( bIsClosed != vCrvOffs[nInd0][nInd1]->IsClosed()))
|
|
continue ;
|
|
// se aperta
|
|
if ( ! bIsClosed) {
|
|
Point3d ptStart ; vCrvOffs[nInd0][nInd1]->GetStartPoint( ptStart) ;
|
|
double dDist = SqDist( ptRef, ptStart) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = nInd1 ;
|
|
}
|
|
}
|
|
// se chiusa
|
|
else {
|
|
DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nInd0][nInd1]) ;
|
|
MinDistPCInfo aInfo ;
|
|
if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) {
|
|
double dDist = SqDist( ptRef, aInfo.ptQ) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = nInd1 ;
|
|
vCrvOffs[nInd0][nInd1]->ChangeStartPoint( aInfo.dPar) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se curva trovata, restituisco gli indici
|
|
if ( nInd != -1) {
|
|
nInd1 = nInd ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
/* se non ho trovato alcuna curva, allora possono verificarsi le seguenti situazioni :
|
|
- ho solo curve topologicamente differenti dalla mia
|
|
- non ho curve attive
|
|
=> devo azzerare nInd0 e ricercare la curva valida più vicina
|
|
*/
|
|
nInd0 = 0 ;
|
|
dLimInfDist = INFINITO ;
|
|
nInd = -1 ;
|
|
for ( ; nInd0 < int( vCrvOffs.size()) ; ++ nInd0) {
|
|
nInd1 = 0 ;
|
|
for ( ; nInd1 < int( vCrvOffs[nInd0].size()) ; ++ nInd1) {
|
|
// se non attiva, passo alla successiva
|
|
if ( vCrvOffs[nInd0][nInd1]->GetTempProp( 0) != TEMP_PROP_CURVE_ACTIVE)
|
|
continue ;
|
|
// se la curva successiva è aperta
|
|
if ( ! vCrvOffs[nInd0][nInd1]->IsClosed()) {
|
|
Point3d ptStart ; vCrvOffs[nInd0][nInd1]->GetStartPoint( ptStart) ;
|
|
double dDist = SqDist( ptRef, ptStart) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = nInd1 ;
|
|
}
|
|
}
|
|
// se la curva successiva è chiusa
|
|
else {
|
|
DistPointCurve DistPtCrv( ptRef, *vCrvOffs[nInd0][nInd1]) ;
|
|
MinDistPCInfo aInfo ;
|
|
if ( DistPtCrv.GetMinDistInfo( 0, aInfo)) {
|
|
double dDist = SqDist( ptRef, aInfo.ptQ) ;
|
|
if ( dDist < dLimInfDist) {
|
|
dLimInfDist = dDist ;
|
|
nInd = nInd1 ;
|
|
vCrvOffs[nInd0][nInd1]->ChangeStartPoint( aInfo.dPar) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// se curva trovata, restituisco gli indici
|
|
if ( nInd != -1) {
|
|
nInd1 = nInd ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// non ho più curve disponibili
|
|
nInd0 = -1 ;
|
|
nInd1 = -1 ;
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustCloseEdgesForConformalGuide( ICurveComposite* pCrvCompo, const PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// se non ho una regione limite, allora non faccio nulla
|
|
if ( ! PockParams.SfrLimit.IsValid())
|
|
return true ;
|
|
|
|
// piccolo Offset per la superficie di classificazione
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CloneSurfFlatRegion( &PockParams.SfrLimit)) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid() ||
|
|
! pSfrLimit->Offset( 150 * EPS_SMALL, ICurve::OFF_FILLET))
|
|
return false ;
|
|
|
|
// recupero le parti omogenee della curva
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
GetHomogeneousParts( pCrvCompo, PockParams, vpCrvs) ;
|
|
|
|
// i tratti aperti che fanno overlap con la regione limite diventano chiusi
|
|
PtrOwner<ICurveComposite> pCrvFinal( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvFinal))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) {
|
|
// nuovo curva
|
|
PtrOwner<ICurveComposite> pMyCompo( CreateCurveComposite()) ;
|
|
if ( IsNull( pMyCompo))
|
|
return false ;
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrLimit->GetCurveClassification( *vpCrvs[i], EPS_SMALL, ccClass)) {
|
|
for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) {
|
|
// recupero il tratto di curva
|
|
PtrOwner<ICurveComposite> pMyCurve( ConvertCurveToComposite( vpCrvs[i]->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) ;
|
|
if ( ! IsNull( pMyCurve) && pMyCurve->IsValid()) {
|
|
// se non esterna alla regione limite, il sottotratto deve essere chiuso
|
|
if ( ccClass[j].nClass != CRVC_OUT) {
|
|
for ( int k = 0 ; k < pMyCurve->GetCurveCount() ; ++ k)
|
|
pMyCurve->SetCurveTempProp( k, TEMP_PROP_CLOSE_EDGE, 0) ;
|
|
}
|
|
if ( ! pMyCompo->AddCurve( Release( pMyCurve)))
|
|
return true ; // non faccio nulla...
|
|
}
|
|
}
|
|
}
|
|
vpCrvs[i].Set( pMyCompo) ;
|
|
}
|
|
if ( ! pCrvFinal->AddCurve( Release( vpCrvs[i])))
|
|
return true ; // non faccio nulla...
|
|
}
|
|
if ( pCrvFinal->IsValid())
|
|
pCrvCompo->CopyFrom( pCrvFinal) ;
|
|
|
|
return ( pCrvCompo != nullptr && pCrvCompo->IsValid()) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ModifyConformalStartPoint( ICurveComposite* pCrvFirst, const PocketParams& PockParams,
|
|
const ISurfFlatRegion* pSfrPock)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvFirst == nullptr || ! pCrvFirst->IsValid() ||
|
|
pSfrPock == nullptr || ! pSfrPock->IsValid())
|
|
return false ;
|
|
|
|
// se la curva è aperta, allora non devo fare nulla
|
|
if ( ! pCrvFirst->IsClosed())
|
|
return true ;
|
|
|
|
// ricavo i lati aperti della superficie di classificazione
|
|
ICRVCOMPOPOVECTOR vCrvOpenEdges ;
|
|
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
|
|
for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) {
|
|
// recupero il Loop
|
|
PtrOwner<ICurveComposite> pCrvLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ;
|
|
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
|
|
return false ;
|
|
// recupero le parti omogenee
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
GetHomogeneousParts( pCrvLoop, PockParams, vpCrvs) ;
|
|
// recupero le parti interne
|
|
for ( int i = 0 ; i < int( vpCrvs.size()) ; ++ i) {
|
|
if ( vpCrvs[i]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE)
|
|
vCrvOpenEdges.emplace_back( Release( vpCrvs[i])) ;
|
|
}
|
|
}
|
|
}
|
|
// se non ho ricavato tratti aperti, errore
|
|
if ( vCrvOpenEdges.empty())
|
|
return false ;
|
|
|
|
/* NB.
|
|
- Per i casi previsti dalle lavorazioni CONFORMAL devo trovare solo 1 tratto aperto, quindi
|
|
considero quello di indice 0
|
|
- Tra la curva pCrvFirst e la curva di lato aperto, devo trovare i punti più vicino
|
|
( per ora l'algoritmo è euristico, bisogna aggiornarlo con VORONOI)
|
|
*/
|
|
|
|
// Algoritmo Euristico da sostituire con Voronoi :
|
|
// 1) Cerco il punto più vicino al lato aperto dato il punto iniziale di pCrvFirst
|
|
Point3d ptStart ; pCrvFirst->GetStartPoint( ptStart) ;
|
|
DistPointCurve distCalculator0( ptStart, *vCrvOpenEdges[0]) ;
|
|
Point3d ptMinDist ;
|
|
int nFlag ;
|
|
if ( ! distCalculator0.GetMinDistPoint( 0., ptMinDist, nFlag))
|
|
return false ;
|
|
// 2) Cerco il punto più vicino su pCrvFirst da ptMinDist
|
|
DistPointCurve distCalculator1( ptMinDist, *pCrvFirst) ;
|
|
double dMinPar ;
|
|
if ( ! distCalculator1.GetParamAtMinDistPoint( 0., dMinPar, nFlag))
|
|
return false ;
|
|
// 3) Cambio il punto iniziale di pCrvFirst nel parametro ricavato
|
|
pCrvFirst->ChangeStartPoint( dMinPar) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcConformalOffsAndLinks( VICRVCOMPOPOVECTOR& vvCrvOffs, const ISurfFlatRegion* pSfrChunk,
|
|
const ISurfFlatRegion* pSfrClass, PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvOffs,
|
|
ICURVEPOVECTOR& vCrvLink)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() ||
|
|
pSfrClass == nullptr || ! pSfrClass->IsValid())
|
|
return false ;
|
|
vCrvOffs.clear() ;
|
|
vCrvLink.clear() ;
|
|
if ( vvCrvOffs.empty())
|
|
return true ;
|
|
|
|
// rendo attive tutte le curve di Offset mediante prima TmpProp
|
|
for ( int i = 0 ; i < int( vvCrvOffs.size()) ; ++ i) {
|
|
for ( int j = 0 ; j < int( vvCrvOffs[i].size()) ; ++ j)
|
|
vvCrvOffs[i][j]->SetTempProp( TEMP_PROP_CURVE_ACTIVE, 0) ;
|
|
}
|
|
|
|
/* NB. curve di vCrvOffs
|
|
TempProp0 -> Se l'offset è attivo/non attivo
|
|
TempProp1 -> Side {MDS_LEFT, MDS_RIGHT} per identificare l'interno della curva
|
|
TempParam0 -> { vuoto } ; verrà utilizzato per la Feed
|
|
TempParam1 -> { vuoto }
|
|
*/
|
|
|
|
// inverto il vettore degli Offset, in modo da partire dalle curve più distanti dai chiusi
|
|
reverse( vvCrvOffs.begin(), vvCrvOffs.end()) ;
|
|
|
|
// determino il punto iniziale della prima curva di Offset
|
|
if ( ! ModifyConformalStartPoint( vvCrvOffs[0][0], PockParams, pSfrChunk))
|
|
return false ;
|
|
|
|
/* NB.
|
|
I link che collegano due curve di Offset aperte devono partire dalla prima e, seguendo il
|
|
bordo della regione di classificazione ( pSfrClass) arrivare alla seconda.
|
|
*/
|
|
/* NB.
|
|
La regione di classificazione ( pSfrClass) deve tenere conto della regione limite; nel caso di
|
|
bordi chiusi, bisogna stare a distanza opportuna da essi, non raccordandoci i Links
|
|
*/
|
|
/* NB.
|
|
I link che collegano due curve di Offset chiuse tra di loro vengono calcolati esattamente
|
|
come per i percorsi SPIRAL, pertanto devo definire un insieme di curve di primo Offset sui
|
|
quali smussare i Link per evitare di uscire dalla regione di svuotatura
|
|
*/
|
|
ICRVCOMPOPOVECTOR vCrvSfrClass ;
|
|
PtrOwner<ISurfFlatRegion> pSfrClassReal( CloneSurfFlatRegion( pSfrClass)) ;
|
|
if ( IsNull( pSfrClassReal) || ! pSfrClassReal->IsValid())
|
|
return false ;
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CloneSurfFlatRegion( &PockParams.SfrLimit)) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid() ||
|
|
! pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET) ||
|
|
! pSfrClassReal->Subtract( *pSfrLimit))
|
|
return false ;
|
|
}
|
|
if ( ! GetSfrCrvCompoLoops( pSfrClassReal, vCrvSfrClass))
|
|
return false ;
|
|
|
|
/* NB.
|
|
Se POCKET_CONFORMAL_ZIGZAG :
|
|
- vCrvCompoOffs contiene gli Offset ordinati
|
|
- vCrvLinks contiene il link che collega l'Offset (i-1)-esimo con l'Offset (i)-esimo
|
|
[ per definizione il primo Link è null]
|
|
[ se tra due Offset non c'è Link, allora esso sarà null]
|
|
Se POCKET_CONFORMAL_ONEWAY
|
|
- vCrvCompoOffs contiene gli Offset ordinati
|
|
- vCrvLinks è un vettore di null
|
|
|
|
vCrvCompoOffs e vCrvLinks avranno la stessa dimensione
|
|
*/
|
|
|
|
// ------------ Ordino Gli Offset ------------
|
|
int nInd0 = 0 ;
|
|
int nInd1 = 0 ;
|
|
while ( nInd0 != -1 && nInd1 != -1) {
|
|
// inserisco la curva attuale nel vettore degli Offset
|
|
vCrvOffs.emplace_back( CloneCurveComposite( vvCrvOffs[nInd0][nInd1])) ;
|
|
// salvo come prima TmpProp il numero di Offset che ha generato tale curva
|
|
vCrvOffs.back()->SetTempProp( int( vvCrvOffs.size()) - nInd0 - 1, 0) ;
|
|
// disattivo tale curva per la ricerca
|
|
vvCrvOffs[nInd0][nInd1]->SetTempProp( TEMP_PROP_CURVE_INACTIVE, 0) ;
|
|
// cerco la curva successiva per il percorso
|
|
int nNextInd0 = -1 ;
|
|
int nNextInd1 = -1 ;
|
|
if ( ! GetConformalIndices( vvCrvOffs, nInd0, nInd1, PockParams, vCrvSfrClass, nNextInd0, nNextInd1))
|
|
return false ;
|
|
// aggiorno gli indici
|
|
nInd0 = nNextInd0 ;
|
|
nInd1 = nNextInd1 ;
|
|
}
|
|
// se non ho ottenuto nulla, esco
|
|
if ( vCrvOffs.empty())
|
|
return true ;
|
|
|
|
// ------------ Definizione dei Link ------------
|
|
// NB. Il link i-esimo collega l'Offset (i-1)-esimo con l'i-esimo
|
|
// per definizione il primo Link è nullo
|
|
vCrvLink.resize( 1) ;
|
|
for ( int i = 0 ; i < int( vCrvOffs.size()) - 1 ; ++ i) {
|
|
// controllo se bisogna calcolare il Link tra i due Offset
|
|
bool bCalcLink = true ;
|
|
// se Conformal ZigZag
|
|
if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) {
|
|
// Criteri per creazione di un possibile collegamento
|
|
if ( ! CheckConformalRetractLink( vCrvOffs[i], vCrvOffs[i+1], vCrvSfrClass, PockParams, bCalcLink))
|
|
return false ;
|
|
}
|
|
// se Conformal OneWay
|
|
else {
|
|
// gli unici link validi per il OneWay sono quelli che collegano due curve chiuse concentriche.
|
|
// In questo caso si può generare un Biarco dato che l'orientamento degli Offset è lo stesso
|
|
bCalcLink = ( vCrvOffs[i]->IsClosed() && vCrvOffs[i+1]->IsClosed()) ;
|
|
if ( bCalcLink) {
|
|
IntersCurveCurve ICC( *vCrvOffs[i], *vCrvOffs[i+1]) ;
|
|
CRVCVECTOR ccClass ;
|
|
bCalcLink = ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1) ;
|
|
if ( bCalcLink) {
|
|
if ( vCrvOffs[i]->GetTempProp( 1) == MDS_LEFT)
|
|
bCalcLink = ( ccClass[0].nClass == CRVC_IN) ;
|
|
else if ( vCrvOffs[i]->GetTempProp( 1) == MDS_RIGHT)
|
|
bCalcLink = ( ccClass[0].nClass == CRVC_OUT) ;
|
|
else
|
|
bCalcLink = false ;
|
|
}
|
|
}
|
|
}
|
|
// se link da calcolare
|
|
if ( bCalcLink) {
|
|
// lo creo e cerco di calcolarlo
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
// se non calcolabile, allora retroazione, altrimenti lo memorizzo
|
|
if ( ! CalcConformalLink( vCrvOffs[i], vCrvOffs[i+1], PockParams, vCrvSfrClass, pCrvLink) ||
|
|
IsNull( pCrvLink) || ! pCrvLink->IsValid() || pCrvLink->GetCurveCount() == 0)
|
|
vCrvLink.resize( int( vCrvLink.size()) + 1) ;
|
|
else
|
|
vCrvLink.emplace_back( Release( pCrvLink)) ;
|
|
}
|
|
else
|
|
vCrvLink.resize( int( vCrvLink.size()) + 1) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ExtendConformalOffsAndSetFeed( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvOffs, ICURVEPOVECTOR& vCrvLinks)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrPock == nullptr || ! pSfrPock->IsValid())
|
|
return false ;
|
|
if ( vCrvOffs.empty())
|
|
return true ;
|
|
|
|
// definisco la regione di ricerca delle aree non svuotate come la regione originale
|
|
PtrOwner<ISurfFlatRegion> pSfrBorder( CloneSurfFlatRegion( pSfrPock)) ;
|
|
if ( IsNull( pSfrBorder) || ! pSfrBorder->IsValid())
|
|
return false ;
|
|
|
|
// dalla superficie originale, rimuovo le parti definite dai lati chiusi
|
|
double dOffs = PockParams.dRad + PockParams.dRadialOffset - 2. * EPS_SMALL ;
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
|
|
for ( int nL = 0 ; nL < pSfrPock->GetLoopCount( nC) ; ++ nL) {
|
|
PtrOwner<ICurveComposite> pCompoLoop( ConvertCurveToComposite( pSfrPock->GetLoop( nC, nL))) ;
|
|
if ( IsNull( pCompoLoop) || ! pCompoLoop->IsValid())
|
|
return false ;
|
|
PtrOwner<ISurfFlatRegion> pSfrToDiscard( GetSurfFlatRegionFromFatCurve( pCompoLoop->Clone(), dOffs, false, false)) ;
|
|
if ( IsNull( pSfrToDiscard) || ! pSfrToDiscard->IsValid())
|
|
return false ;
|
|
if ( ! pSfrBorder->Subtract( *pSfrToDiscard))
|
|
return false ;
|
|
}
|
|
}
|
|
// se non ho nessuna superficie di bordo, allora vuol dire che la regione è più stretta del diametro utensile
|
|
// ( capita soprattutto se ho un SideStep da rispettare presso gli aperti)
|
|
// non controllo le regioni rimosse ( dovrebbero essere rimosse dalle passate)
|
|
if ( pSfrBorder->IsValid()) {
|
|
ICRVCOMPOPOVECTOR vCrvBorder ;
|
|
if ( ! GetSfrCrvCompoLoops( pSfrBorder, vCrvBorder))
|
|
return false ;
|
|
// determino eventuale regioni con parti non svuotate e imposto la Feed alle curve
|
|
PtrOwner<ISurfFlatRegion> pSfrUncleared( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrUncleared))
|
|
return false ;
|
|
if ( GetUnclearedRegionAndSetFeed( vCrvBorder, vCrvOffs, vCrvLinks, pSfrOrig, PockParams, pSfrUncleared)) {
|
|
// estendo i percorsi di Offset se richiesto
|
|
if ( ! RemoveUnclearedRegions( pSfrUncleared, vCrvOffs, vCrvBorder, PockParams))
|
|
return false ;
|
|
}
|
|
}
|
|
else {
|
|
for ( int i = 0 ; i < ssize( vCrvOffs) ; ++ i) {
|
|
if ( ! IsNull( vCrvOffs[i]) && vCrvOffs[i]->IsValid())
|
|
AssignMaxFeed( vCrvOffs[i], PockParams) ;
|
|
}
|
|
for ( int i = 0 ; i < ssize( vCrvLinks) ; ++ i) {
|
|
if ( ! IsNull( vCrvLinks[i]) && vCrvLinks[i]->IsValid()) {
|
|
PtrOwner<ICurveComposite> pCompoLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoLink))
|
|
return false ;
|
|
pCompoLink->AddCurve( Release( vCrvLinks[i])) ;
|
|
AssignMaxFeed( pCompoLink, PockParams) ;
|
|
vCrvLinks[i].Set( pCompoLink) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddLeadInLeadOutToCurveConformalPaths( const ISurfFlatRegion* pSfrOrig, const ISurfFlatRegion* pSfrPock,
|
|
const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvPaths)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrOrig == nullptr || ! pSfrOrig->IsValid() ||
|
|
pSfrPock == nullptr || ! pSfrPock->IsValid())
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) {
|
|
if ( vCrvPaths[i] == nullptr || ! vCrvPaths[i]->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
// ricavo le curve aperte della superficie di pocketing
|
|
ICRVCOMPOPOVECTOR vCrvLoops ;
|
|
if ( ! GetSfrCrvCompoLoops( pSfrPock, vCrvLoops))
|
|
return false ;
|
|
ICRVCOMPOPOVECTOR vCrvOpenEdge ;
|
|
for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) {
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
GetHomogeneousParts( vCrvLoops[i], PockParams, vpCrvs) ;
|
|
for ( int j = 0 ; j < int( vpCrvs.size()) ; ++ j) {
|
|
if ( vpCrvs[j]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE)
|
|
vCrvOpenEdge.emplace_back( Release( vpCrvs[j])) ;
|
|
}
|
|
}
|
|
|
|
// scorro i percorsi ricavati
|
|
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) {
|
|
// recupero il punto iniziale del percorso
|
|
Point3d ptStart ; vCrvPaths[i]->GetStartPoint( ptStart) ;
|
|
// se il punto inziale è interno alla superficie originaria
|
|
bool bIsInside ;
|
|
if ( IsPointInsideSurfFr( ptStart, pSfrOrig, EPS_SMALL, bIsInside) && bIsInside) {
|
|
// definizione del segmento per l'entrata
|
|
PtrOwner<ICurveLine> pSeg( CreateCurveLine()) ;
|
|
if ( IsNull( pSeg))
|
|
return false ;
|
|
double dSqLenMin = INFINITO ;
|
|
// ricavo il punto più vicino alle curve aperte della superficie di classificazione
|
|
for ( int j = 0 ; j < int( vCrvOpenEdge.size()) ; ++ j) {
|
|
DistPointCurve DistPtCrv( ptStart, *vCrvOpenEdge[j]) ;
|
|
Point3d ptMinDist ;
|
|
int nFlag ;
|
|
// controllo se il segmento che congiunge i due estremi non rovina il grezzo
|
|
bool bSafe = ( DistPtCrv.GetMinDistPoint( 0, ptMinDist, nFlag)) ;
|
|
// se troppo piccolo, non faccio nulla
|
|
double dSqSegLen = SqDist( ptStart, ptMinDist) ;
|
|
if ( dSqSegLen < SQ_EPS_SMALL) {
|
|
pSeg.Set( CreateCurveLine()) ;
|
|
break ;
|
|
}
|
|
// creazione del segmento
|
|
if ( bSafe) {
|
|
PtrOwner<ICurveLine> pCurrSeg( CreateCurveLine()) ;
|
|
if ( IsNull( pCurrSeg) || ! pCurrSeg->Set( ptMinDist, ptStart))
|
|
return false ;
|
|
double dSqSegLen = SqDist( ptStart, ptMinDist) ;
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CloneSurfFlatRegion( &PockParams.SfrLimit)) ;
|
|
if ( IsNull( pSfrLimit) || ! pSfrLimit->IsValid() ||
|
|
! pSfrLimit->Offset( PockParams.dRad + PockParams.dRadialOffset, ICurve::OFF_FILLET))
|
|
return false ;
|
|
// controllo distanza segmento
|
|
CRVCVECTOR ccClass ;
|
|
bSafe = ( pSfrLimit->GetCurveClassification( *pSeg, 25 * EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) ;
|
|
}
|
|
if ( bSafe && dSqSegLen < dSqLenMin) {
|
|
dSqLenMin = dSqSegLen ;
|
|
pSeg.Set( pCurrSeg) ;
|
|
}
|
|
}
|
|
}
|
|
// se segmento trovato, lo aggiungo alla curva
|
|
if ( pSeg->IsValid()) {
|
|
if ( ! vCrvPaths[i]->AddCurve( Release( pSeg), false, 25 * EPS_SMALL))
|
|
return false ;
|
|
vCrvPaths[i]->SetCurveTempParam( 0, GetMinFeed( PockParams), 0) ;
|
|
}
|
|
}
|
|
|
|
// estendo il percorso sul tratto iniziale e finale ( se ammissibile)
|
|
Vector3d vtTanS, vtTanE ;
|
|
vCrvPaths[i]->GetStartDir( vtTanS) ; vtTanS.Invert() ;
|
|
vCrvPaths[i]->GetEndDir( vtTanE) ;
|
|
const int MAXTRY = 8 ;
|
|
bool bOkStartExtension = false, bOkEndExtension = false ;
|
|
for ( int j = 0 ; j <= MAXTRY ; ++ j) {
|
|
if ( bOkStartExtension && bOkEndExtension)
|
|
break ;
|
|
double dDist = PockParams.dOpenMinSafe + j * ( GetExtendPathLen( PockParams) / ( 1. * MAXTRY)) ;
|
|
if ( ! bOkStartExtension) {
|
|
if ( ! ExtendPath( vCrvPaths[i], pSfrOrig, PockParams, vtTanS, false, dDist, bOkStartExtension))
|
|
return false ;
|
|
}
|
|
if ( ! bOkEndExtension) {
|
|
if ( ! ExtendPath( vCrvPaths[i], pSfrOrig, PockParams, vtTanE, true, dDist, bOkEndExtension))
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
OrderAndExtendConformalPaths( ICRVCOMPOPOVECTOR& vCrvPaths, const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrOrig,
|
|
PocketParams& PockParams)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() ||
|
|
pSfrOrig == nullptr || ! pSfrOrig->IsValid())
|
|
return false ;
|
|
|
|
// superficie di controllo per parti isolate
|
|
PtrOwner<ISurfFlatRegion> pSfrToRemove( CloneSurfFlatRegion( pSfrChunk)) ;
|
|
if ( IsNull( pSfrToRemove) || ! pSfrToRemove->IsValid() ||
|
|
! pSfrToRemove->Intersect( *pSfrOrig)) {
|
|
// riprovo con una leggera tolleranza
|
|
pSfrToRemove->Offset( - 10 * EPS_SMALL, ICurve::OFF_FILLET) ;
|
|
if ( ! pSfrToRemove->Intersect( *pSfrOrig))
|
|
return false ;
|
|
}
|
|
|
|
// creo le regioni piane di svuotatura dei percorsi
|
|
ISURFFRPOVECTOR vSfrRemoved ; vSfrRemoved.resize( vCrvPaths.size()) ;
|
|
ISURFFRPOVECTOR vSfrRemaining ; vSfrRemaining.reserve( vCrvPaths.size()) ;
|
|
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i) {
|
|
// creo la regione di svuotatura di tale percorso
|
|
vSfrRemoved[i].Set( GetSurfFlatRegionFromFatCurve( CloneCurveComposite( vCrvPaths[i]), PockParams.dRad + 500 * EPS_SMALL, false, false)) ;
|
|
if ( IsNull( vSfrRemoved[i]) || ! vSfrRemoved[i]->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
/* ------------------------ Ordinamento dei percorsi ------------------------ */
|
|
// scorro i percorsi ricavati
|
|
INTVECTOR vInds ; vInds.resize( vCrvPaths.size(), -1) ;
|
|
int nChunkRef = pSfrToRemove->GetChunkCount() ;
|
|
for ( int i = 0 ; i < int( vInds.size()) ; ++ i) {
|
|
int j = 0 ;
|
|
INTVECTOR vTempInds ;
|
|
for ( ; j < int( vCrvPaths.size()) ; ++ j) {
|
|
// se indice già presente
|
|
if ( find( vInds.begin(), vInds.end(), j) != vInds.end())
|
|
continue ;
|
|
// se sottraendo questa regione a quella originale ottengo più isole, allora non è il percorso ideale
|
|
PtrOwner<ISurfFlatRegion> pSfrRemain( CloneSurfFlatRegion( pSfrToRemove)) ;
|
|
if ( IsNull( pSfrRemain) || ! pSfrRemain->IsValid())
|
|
continue ;
|
|
pSfrRemain->Subtract( *vSfrRemoved[j]) ;
|
|
// se aumento il numero di Chunk, allora non è il percorso ideale
|
|
if ( pSfrRemain->GetChunkCount() > nChunkRef)
|
|
continue ;
|
|
vTempInds.push_back( j) ;
|
|
}
|
|
// 1) se non ho trovato curve candidate, prendo l'indice disponibile più basso
|
|
if ( vTempInds.empty()) {
|
|
for ( int k = 0 ; k < int( vInds.size()) ; ++ k) {
|
|
if ( find( vInds.begin(), vInds.end(), k) != vInds.end())
|
|
continue ;
|
|
vInds[i] = k ;
|
|
break ;
|
|
}
|
|
}
|
|
// 2) se la candidata è singola, allora è lei
|
|
else if ( int( vTempInds.size()) == 1)
|
|
vInds[i] = vTempInds[0] ;
|
|
// 3) se trovate più curve, allora effettuo algoritmi euristici
|
|
else {
|
|
// 3.1) cerco la curva che effettua meno overlap con la regione da limite
|
|
double dMinLen = INFINITO ;
|
|
double dMinArea = INFINITO ;
|
|
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
|
|
// privilegio le aperte
|
|
if ( vCrvPaths[vTempInds[k]]->IsClosed())
|
|
continue ;
|
|
// se la regione di incidenza è valida
|
|
if ( PockParams.SfrLimit.IsValid()) {
|
|
dMinLen = 0. ; // non mi interessa in questo caso la lunghezza della curva
|
|
PtrOwner<ISurfFlatRegion> pSfrTest( CloneSurfFlatRegion( vSfrRemoved[vTempInds[k]])) ;
|
|
if ( IsNull( pSfrTest) || ! pSfrTest->IsValid())
|
|
return false ;
|
|
pSfrTest->Intersect( PockParams.SfrLimit) ;
|
|
double dArea ; pSfrTest->GetArea( dArea) ;
|
|
if ( dArea < dMinArea) {
|
|
dMinArea = dArea ;
|
|
vInds[i] = vTempInds[k] ;
|
|
if ( dArea < EPS_SMALL)
|
|
break ; // è ottima...
|
|
}
|
|
}
|
|
// 3.2) se la regione di incidenza non è valida, scelgo la curva più corta
|
|
else {
|
|
double dLen ; vCrvPaths[vTempInds[k]]->GetLength( dLen) ;
|
|
if ( dLen < dMinLen) {
|
|
dMinLen = dLen ;
|
|
vInds[i] = vTempInds[k] ;
|
|
}
|
|
}
|
|
}
|
|
// 3.3) se ho solo curve adiacenti a regioni di incidenza, scelgo quella il cui bordo esterno
|
|
// contiene più curve di bordo esterno
|
|
if ( dMinArea > EPS_SMALL) {
|
|
struct MyCompoClass {
|
|
PtrOwner<ICurveComposite> pCrvExtLoop ;
|
|
int nIndex ;
|
|
int nBorders ;
|
|
} ;
|
|
vector<MyCompoClass> vCrvExtBorder( vTempInds.size() ) ;
|
|
for ( int j = 0 ; j < int( vCrvExtBorder.size()) ; ++ j) {
|
|
// recupero il bordo esterno (mi auguro sia un solo chunk...)
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( vSfrRemoved[vTempInds[j]]->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() ||
|
|
! vCrvExtBorder[j].pCrvExtLoop.Set( pCrvExtLoop))
|
|
return false ;
|
|
vCrvExtBorder[j].nIndex = vTempInds[j] ;
|
|
vCrvExtBorder[j].nBorders = 0 ;
|
|
}
|
|
for ( int j = 0 ; j < int( vCrvExtBorder.size()) - 1 ; ++ j) {
|
|
for ( int k = j + 1 ; k < int( vCrvExtBorder.size()) ; ++ k) {
|
|
IntersCurveCurve ICC( *vCrvExtBorder[j].pCrvExtLoop, *vCrvExtBorder[k].pCrvExtLoop) ;
|
|
CRVCVECTOR ccClass ;
|
|
if ( ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) &&
|
|
int( ccClass.size()) == 1) {
|
|
// se i-esima interna a j-esima
|
|
if ( ccClass[0].nClass == CRVC_IN)
|
|
++ vCrvExtBorder[k].nBorders ;
|
|
else if ( ccClass[0].nClass == CRVC_OUT)
|
|
++ vCrvExtBorder[j].nBorders ;
|
|
}
|
|
}
|
|
}
|
|
// ordino il vettore delle curve sulla base di quante ne contiene
|
|
sort( vCrvExtBorder.begin(), vCrvExtBorder.end(),
|
|
[] ( const MyCompoClass& a, const MyCompoClass& b) {
|
|
return a.nBorders > b.nBorders ;
|
|
}) ;
|
|
// recupero l'indice migliore
|
|
if ( ! vCrvExtBorder.empty())
|
|
vInds[i] = vCrvExtBorder[0].nIndex ;
|
|
}
|
|
// cerco solo tra le chiuse se non ho aperte
|
|
if ( dMinLen > INFINITO - 1) {
|
|
for ( int k = 0 ; k < int( vTempInds.size()) ; ++ k) {
|
|
if ( vCrvPaths[vTempInds[k]]->IsClosed()) {
|
|
double dLen ; vCrvPaths[vTempInds[k]]->GetLength( dLen) ;
|
|
if ( dLen < dMinLen) {
|
|
dMinLen = dLen ;
|
|
vInds[i] = vTempInds[k] ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// salvo la regione rimanente ( è già in posizione corretta nel vettore)
|
|
vSfrRemaining.emplace_back( CloneSurfFlatRegion( pSfrToRemove)) ;
|
|
// aggiorno la regione da svuotare e il numero di Chunk che si sono formati
|
|
pSfrToRemove->Subtract( *vSfrRemoved[vInds[i]]) ;
|
|
nChunkRef = pSfrToRemove->GetChunkCount() ;
|
|
}
|
|
|
|
/* ------------------------ Estensione dei percorsi ------------------------ */
|
|
|
|
// ordino percorsi e superfici progressive per estendendere le curve presso gli aperti progressivi
|
|
ICRVCOMPOPOVECTOR vCrvOrderedPaths ; vCrvOrderedPaths.resize( vCrvPaths.size()) ;
|
|
ISURFFRPOVECTOR vSfrRemovedOrdered ; vSfrRemovedOrdered.resize( vCrvPaths.size()) ;
|
|
for ( int i = 0 ; i < int( vInds.size()) ; ++ i) {
|
|
// ordino la superficie rimossa dal percorso attuale
|
|
vSfrRemovedOrdered[i].Set( vSfrRemoved[vInds[i]]) ;
|
|
// ordino il percorso
|
|
vCrvOrderedPaths[i].Set( vCrvPaths[vInds[i]]) ;
|
|
}
|
|
|
|
// controllo che i percorsi effettivamente rimuovano del materiale
|
|
/*
|
|
NB. Nel caso di lati aperti, può capitare che si formino delle piccole curve ( estese poi
|
|
per il LeadIn/LeadOut ) che siano interne ad eventuali estensioni per i lati aperti ma che
|
|
di fatto non svuotano materiale effettivo ( questo accade, perchè gli Offset non sono come
|
|
per il caso degli Spiral fatti a partire dai bordi, ma sono calcolati tagliando gli offset
|
|
dei lati chiusi su una superficie estesa di 1.05 * ( dRad + GetOffsR() )
|
|
*/
|
|
int nMax = vCrvOrderedPaths.size() ;
|
|
vCrvPaths.clear() ;
|
|
int i = 0 ;
|
|
for ( ; i < nMax ; ++ i) {
|
|
// se percorso non valido, passo al successivo
|
|
if ( IsNull( vCrvOrderedPaths[i]) || ! vCrvOrderedPaths[i]->IsValid())
|
|
continue ;
|
|
// se con il percorso i-esimo ho già rimosso tutta la superficie, allora ho finito
|
|
if ( IsNull( vSfrRemaining[i]) || ! vSfrRemaining[i]->IsValid())
|
|
break ;
|
|
// se superficie rimossa dal percorso attuale non valida, errore
|
|
if ( IsNull( vSfrRemovedOrdered[i]) || ! vSfrRemovedOrdered[i]->IsValid())
|
|
return false ;
|
|
// controllo se la superficie rimossa ( + 500 * EPS_SMALL ) interseca la regione da svuotare
|
|
if ( ! vSfrRemovedOrdered[i]->Intersect( *vSfrRemaining[i]) || IsNull( vSfrRemovedOrdered[i]))
|
|
return false ;
|
|
// se valida
|
|
if ( vSfrRemovedOrdered[i]->IsValid() && vSfrRemovedOrdered[i]->GetChunkCount() > 0) {
|
|
// il percorso rimuove materiale, aggiungo LeadIn e LeadOut
|
|
ICRVCOMPOPOVECTOR vCurrPath ; vCurrPath.resize( 1) ;
|
|
vCurrPath[0].Set( vCrvOrderedPaths[i]) ;
|
|
AddLeadInLeadOutToCurveConformalPaths( vSfrRemaining[i], pSfrChunk, PockParams, vCurrPath) ;
|
|
vCrvPaths.emplace_back( Release( vCurrPath[0])) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ChainConformalOffsWithLinks( ICRVCOMPOPOVECTOR& vCrvOffs, ICURVEPOVECTOR& vCrvLink,
|
|
ICRVCOMPOPOVECTOR& vCrvPaths)
|
|
{
|
|
// se non ho Offset non devo concatenare nulla
|
|
if ( vCrvOffs.empty())
|
|
return true ;
|
|
// le curve di Offset devono essere definite e valide
|
|
for ( int i = 0 ; i < int( vCrvOffs.size()) ; ++ i) {
|
|
if ( vCrvOffs[i] == nullptr || ! vCrvOffs[i]->IsValid())
|
|
return false ;
|
|
}
|
|
vCrvPaths.clear() ;
|
|
|
|
// percorso corrente
|
|
PtrOwner<ICurveComposite> pCrvPath( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvPath))
|
|
return false ;
|
|
|
|
for ( int i = 0 ; i < int( vCrvOffs.size()) - 1 ; ++ i) {
|
|
// aggiungo l'Offset corrente
|
|
if ( ! pCrvPath->AddCurve( Release( vCrvOffs[i])))
|
|
return false ;
|
|
|
|
// se il Link per l'Offset successivo è valido, lo aggiungo
|
|
if ( ! IsNull( vCrvLink[i+1]) && vCrvLink[i+1]->IsValid()) {
|
|
if ( ! pCrvPath->AddCurve( Release( vCrvLink[i+1]))) {
|
|
vCrvPaths.emplace_back( Release( pCrvPath)) ;
|
|
pCrvPath.Set( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvPath))
|
|
return false ;
|
|
}
|
|
}
|
|
// se Link non valido, ho terminato il percorso corrente
|
|
else {
|
|
vCrvPaths.emplace_back( Release( pCrvPath)) ;
|
|
pCrvPath.Set( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvPath))
|
|
return false ;
|
|
}
|
|
}
|
|
if ( ! pCrvPath->AddCurve( Release( vCrvOffs.back())))
|
|
return false ;
|
|
vCrvPaths.emplace_back( Release( pCrvPath)) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ExtendGuideByIteration( ICurveComposite* pCompoTempGuide, const ICurveComposite* pCompoPerimeter,
|
|
bool bInverted)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCompoTempGuide == nullptr || ! pCompoTempGuide->IsValid() ||
|
|
pCompoPerimeter == nullptr || ! pCompoPerimeter->IsValid())
|
|
return false ;
|
|
|
|
const double EXTENSION_VAL = 5000. ;
|
|
|
|
// ricavo il punto finale della guida
|
|
Point3d ptEndGuide ; pCompoTempGuide->GetEndPoint( ptEndGuide) ;
|
|
// direzione finale della guida
|
|
Vector3d vtEndGuide ; pCompoTempGuide->GetEndDir( vtEndGuide) ;
|
|
// creo un segmento diretto come il chiuso
|
|
PtrOwner<ICurveLine> pSeg( CreateCurveLine()) ;
|
|
if ( IsNull( pSeg) ||
|
|
! pSeg->Set( ptEndGuide, ptEndGuide + EXTENSION_VAL * vtEndGuide))
|
|
return false ;
|
|
// calcolo l'intersezione tra questo segmento e la curva di perimetro aperta
|
|
IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ;
|
|
// se l'unica intersezione è il punto iniziale del segmento, allora non estendo la guida
|
|
// ( vale anche nel caso di intersezione con Overlap)
|
|
if ( ICC.GetIntersCount() == 1) {
|
|
IntCrvCrvInfo aInfo ;
|
|
if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) {
|
|
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide))
|
|
return true ;
|
|
}
|
|
}
|
|
// passo alla PolyLine del perimetro
|
|
PolyLine PL ;
|
|
pCompoPerimeter->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, PL) ;
|
|
// cerco il punto più esterno rispetto alla direzione del chiuso
|
|
Point3d ptPoly ;
|
|
PL.GetFirstPoint( ptPoly) ;
|
|
while ( PL.GetNextPoint( ptPoly)) {
|
|
Vector3d vtDir = ptPoly - ptEndGuide ;
|
|
vtDir.Normalize() ;
|
|
if ( ! pSeg->Set( ptEndGuide, ptEndGuide + vtDir * EXTENSION_VAL))
|
|
return false ;
|
|
// calcolo l'intersezione tra questo segmento e la curva di perimetro aperta
|
|
IntersCurveCurve ICC( *pSeg, *pCompoPerimeter) ;
|
|
// se una intersezione, allora di Overlap con punto iniziale e finale definito
|
|
if ( ICC.GetIntersCount() == 1) {
|
|
IntCrvCrvInfo aInfo ;
|
|
if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) {
|
|
if ( aInfo.bOverlap) {
|
|
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide) &&
|
|
AreSamePointApprox( aInfo.IciB[1].ptI, ptPoly))
|
|
return ( pCompoTempGuide->AddLine( ptPoly)) ;
|
|
}
|
|
else {
|
|
if ( AreSamePointApprox( aInfo.IciB[0].ptI, ptEndGuide))
|
|
return ( pCompoTempGuide->AddLine( ptPoly)) ;
|
|
}
|
|
}
|
|
}
|
|
// se più di una intersezione, allora il perimetro deve essere tutto Interno al segmento
|
|
// ( Out nel caso in cui ho le curve invertite)
|
|
IntCrvCrvInfo aInfo ;
|
|
bool bOk = true ;
|
|
for ( int i = 0 ; i < ICC.GetIntersCount() && bOk ; ++ i) {
|
|
bOk = ( ICC.GetIntCrvCrvInfo( i, aInfo)) ;
|
|
bOk = ( aInfo.bOverlap ||
|
|
( aInfo.IciB[0].nPrevTy != ( ! bInverted ? CRVC_OUT : CRVC_IN) &&
|
|
aInfo.IciB[0].nNextTy != ( ! bInverted ? CRVC_OUT : CRVC_IN))) ;
|
|
}
|
|
if ( bOk)
|
|
return ( pCompoTempGuide->AddLine( ptPoly)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcConformalGuide( const ICurveComposite* pCrvCloseEdge, const ICurveComposite* pCrvOpenEdge,
|
|
const PocketParams& PockParams, ICurveComposite* pCrvGuide)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCloseEdge == nullptr || ! pCrvCloseEdge->IsValid() ||
|
|
pCrvOpenEdge == nullptr || ! pCrvOpenEdge->IsValid())
|
|
return false ;
|
|
pCrvGuide->Clear() ;
|
|
|
|
// creo una copia temporanea delle curve
|
|
PtrOwner<ICurveComposite> pCrvCL( CloneCurveComposite( pCrvCloseEdge)) ;
|
|
PtrOwner<ICurveComposite> pCrvOP( CloneCurveComposite( pCrvOpenEdge)) ;
|
|
if ( IsNull( pCrvCL) || IsNull( pCrvOP) || ! pCrvCL->IsValid() || ! pCrvOP->IsValid())
|
|
return false ;
|
|
|
|
// estendo la curva nel suo tratto finale ed iniziale
|
|
for ( int i = 0 ; i < 2 ; ++ i) {
|
|
if ( ! ExtendGuideByIteration( pCrvCL, pCrvOP, ( i == 1)))
|
|
return false ;
|
|
if ( ! pCrvCL->IsValid())
|
|
return true ;
|
|
pCrvCL->Invert() ;
|
|
pCrvOP->Invert() ;
|
|
// se la curva diventa chiusa, allora ho la guida
|
|
if ( pCrvCL->IsClosed()) {
|
|
pCrvGuide->CopyFrom( pCrvCL) ;
|
|
if ( i == 1)
|
|
pCrvGuide->Invert() ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// tratto iniziale
|
|
Vector3d vtStart ; pCrvCL->GetStartDir( vtStart) ;
|
|
Point3d ptStart ; pCrvCL->GetStartPoint( ptStart) ;
|
|
PtrOwner<ICurveLine> pLineStart( CreateCurveLine()) ;
|
|
if ( IsNull( pLineStart) || ! pLineStart->Set( ptStart - 1000. * vtStart, ptStart))
|
|
return false ;
|
|
// tratto finale ( potrebbe intersecare il tratto iniziale)
|
|
Vector3d vtEnd ; pCrvCL->GetEndDir( vtEnd) ;
|
|
Point3d ptEnd ; pCrvCL->GetEndPoint( ptEnd) ;
|
|
PtrOwner<ICurveLine> pLineEnd( CreateCurveLine()) ;
|
|
if ( IsNull( pLineEnd) || ! pLineEnd->Set( ptEnd, ptEnd + 1000. * vtEnd))
|
|
return false ;
|
|
|
|
// controllo eventuale intersezione
|
|
IntersCurveCurve ICC( *pLineStart, *pLineEnd) ;
|
|
if ( ICC.GetIntersCount() > 0) {
|
|
// se esiste allora spezzo sia il tratto iniziale che finale nel punto di intersezione
|
|
IntCrvCrvInfo aInfo ;
|
|
if ( ICC.GetIntCrvCrvInfo( 0, aInfo)) {
|
|
pLineStart->TrimStartAtParam( aInfo.IciA[0].dU) ;
|
|
pLineEnd->TrimEndAtParam( aInfo.IciB[0].dU) ;
|
|
}
|
|
}
|
|
|
|
// le due estensioni vanno tagliate presso i bordi dei lati aperti estesi; rischierei di non
|
|
// riuscire a passare presso dei lati aperti vicini ai tratti lineari di estensione
|
|
IntersCurveCurve ICCLS( *pLineStart, *pCrvOP) ;
|
|
if ( ICCLS.GetIntersCount() > 0) {
|
|
IntCrvCrvInfo aInfo ;
|
|
for ( int i = 0 ; i < ICCLS.GetCrossIntersCount() ; ++ i) {
|
|
if ( ICCLS.GetIntCrvCrvInfo( i, aInfo)) {
|
|
if ( ! AreSamePointApprox( aInfo.IciA[ aInfo.bOverlap ? 1 : 0].ptI, ptStart))
|
|
pLineStart->ModifyStart( aInfo.IciA[aInfo.bOverlap ? 1 : 0].ptI) ;
|
|
}
|
|
}
|
|
}
|
|
IntersCurveCurve ICCLE( *pLineEnd, *pCrvOP) ;
|
|
if ( ICCLE.GetIntersCount() > 0) {
|
|
IntCrvCrvInfo aInfo ;
|
|
for ( int i = 0 ; i < ICCLE.GetCrossIntersCount() ; ++ i) {
|
|
if ( ICCLE.GetIntCrvCrvInfo( 0, aInfo)) {
|
|
if ( ! AreSamePointApprox( aInfo.IciA[0].ptI, ptEnd))
|
|
pLineEnd->ModifyEnd( aInfo.IciA[0].ptI) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// aggiungo i tratti lineari di estensione della curva
|
|
pCrvCL->AddCurve( Release( pLineStart), false) ;
|
|
pCrvCL->AddCurve( Release( pLineEnd), true) ;
|
|
|
|
// recupero la curva guida
|
|
pCrvGuide->CopyFrom( pCrvCL) ;
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CombineClosedEdgesForConformalOffsets( const ISurfFlatRegion* pSfrChunk, const PocketParams& PockParam,
|
|
bool& bOk, ICRVCOMPOPOVECTOR& vpCrvs)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
vpCrvs.clear() ;
|
|
bOk = false ;
|
|
|
|
// se la superficie non ha isole, allora non calcolo nulla
|
|
if ( pSfrChunk->GetLoopCount( 0) == 1)
|
|
return true ;
|
|
|
|
// recupero il bordo esterno e l'eventuale guida associata
|
|
ICRVCOMPOPOVECTOR vpCrvHomo ;
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() ||
|
|
! AdjustCloseEdgesForConformalGuide( pCrvExtLoop, PockParam) ||
|
|
! IsCompoMadeBy2DifferentHomogeneousParts( pCrvExtLoop, PockParam, bOk, vpCrvHomo))
|
|
return false ;
|
|
if ( ! bOk)
|
|
return true ;
|
|
|
|
// recupero la guida ideale
|
|
PtrOwner<ICurveComposite> pCrvGuide( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvGuide) ||
|
|
! CalcConformalGuide( vpCrvHomo[0], vpCrvHomo[1], PockParam, pCrvGuide))
|
|
return false ;
|
|
if ( ! pCrvGuide->IsValid())
|
|
return true ;
|
|
// la guida definisce il tratto chiuso per gli Offsets
|
|
vpCrvs.emplace_back( Release( pCrvGuide)) ;
|
|
// recupero il bordo esterno della superficie originale
|
|
pCrvExtLoop.Set( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid())
|
|
return false ;
|
|
// considero questo bordo come tutto aperto
|
|
for ( int i = 0 ; i < pCrvExtLoop->GetCurveCount() ; ++ i)
|
|
pCrvExtLoop->SetCurveTempProp( i, 0, TEMP_PROP_OPEN_EDGE) ;
|
|
// recupero solo le isole chiuse (di sicuro esistono isole)
|
|
for ( int nL = 1 ; nL < pSfrChunk->GetLoopCount( 0) ; ++ nL) {
|
|
// recupero la curva dell'isola
|
|
PtrOwner<ICurveComposite> pCrvIsl( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, nL))) ;
|
|
if ( IsNull( pCrvIsl) || ! pCrvIsl->IsValid())
|
|
return false ;
|
|
// se curva tutta chiusa, allora la inserisco come isola
|
|
bool bAllOpen, bAllClose ;
|
|
if ( IsCurveCompoHomogeneous( pCrvIsl, bAllClose, bAllOpen) && bAllClose)
|
|
vpCrvs.emplace_back( Release( pCrvIsl)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetConformalOffsets( const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrClass,
|
|
const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParam,
|
|
VICRVCOMPOPOVECTOR& vCrvOffs)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrClass == nullptr || ! pSfrClass->IsValid() ||
|
|
pSfrChunk == nullptr || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
vCrvOffs.clear() ;
|
|
|
|
/* NB
|
|
vCrvOffs è un vettore di vettori di curve composite. Per ogni Offset progressivo i-esimo
|
|
vengono salvate tutte le parti interne j-esime alla pSfrClass.
|
|
( la posizione del vettore ICRVCOMPOPOVECTOR in VICRVCOMPOPOVECTOR è il numero di iterazione
|
|
progressiva di Offset)
|
|
*/
|
|
|
|
/* controllo se il chunk è svuotabile con lavorazione conformal
|
|
Sono previsti due casi di lavorazioni conformal :
|
|
1) Non esistono Isole e il bordo esterno è formato da due tratti disomogenei
|
|
2) Esistono solo Isole chiuse e il bordo esterno è tutto aperto
|
|
*/
|
|
bool bOk = false ;
|
|
ICRVCOMPOPOVECTOR vCrvCloseEdges ; // tratti chiusi da cui calcolare gli Offset
|
|
|
|
// 1) Non esistono Isole e il bordo esterno è formato da due tratti disomogenei
|
|
if ( pSfrChunk->GetLoopCount( 0) == 1) {
|
|
// recupero la curva di bordo
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
PtrOwner<ICurveComposite> pCrvExtLoop( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvExtLoop) || ! pCrvExtLoop->IsValid() ||
|
|
! IsCompoMadeBy2DifferentHomogeneousParts( pCrvExtLoop, PockParam, bOk, vpCrvs))
|
|
return false ;
|
|
if ( ! bOk)
|
|
return true ;
|
|
// recupero la guida ideale
|
|
PtrOwner<ICurveComposite> pCrvGuide( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvGuide) ||
|
|
! CalcConformalGuide( vpCrvs[0], vpCrvs[1], PockParam, pCrvGuide))
|
|
return false ;
|
|
if ( ! pCrvGuide->IsValid())
|
|
return true ;
|
|
// la guida definisce il tratto chiuso per gli Offsets
|
|
vCrvCloseEdges.emplace_back( Release( pCrvGuide)) ;
|
|
}
|
|
// 2) Esistono solo Isole chiuse e il bordo esterno è tutto aperto
|
|
else {
|
|
if ( ! IsSfrChunkMadeOnlyByClosedIslands( pSfrChunk, PockParam, bOk, vCrvCloseEdges))
|
|
return false ;
|
|
if ( ! bOk) {
|
|
// 3) provo a combinare punto 1) e 2)
|
|
if ( ! CombineClosedEdgesForConformalOffsets( pSfrChunk, PockParam, bOk, vCrvCloseEdges))
|
|
return false ;
|
|
if ( ! bOk)
|
|
return true ; // sarà in SpiralIn/Out...
|
|
}
|
|
}
|
|
|
|
/* Calcolo degli Offsets */
|
|
// Oggetto Voronoi per curve chiuse
|
|
Voronoi myVRONI ;
|
|
for ( int i = 0 ; i < int( vCrvCloseEdges.size()) ; ++ i) {
|
|
vCrvCloseEdges[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
myVRONI.AddCurve( vCrvCloseEdges[i]) ;
|
|
}
|
|
|
|
// determino la regione limite
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrLimit))
|
|
return false ;
|
|
if ( PockParam.SfrLimit.IsValid()) {
|
|
if ( ! pSfrLimit.Set( PockParam.SfrLimit.Clone()) ||
|
|
! pSfrLimit->IsValid() ||
|
|
! pSfrLimit->Offset( PockParam.dRad + PockParam.dRadialOffset - 5 * EPS_SMALL, ICurve::OFF_FILLET))
|
|
return false ;
|
|
}
|
|
|
|
int MAX_ITER = 1000 ;
|
|
int nIter = 0 ;
|
|
double dOffs = PockParam.dRad + PockParam.dRadialOffset ;
|
|
PtrOwner<ISurfFlatRegion> pSfrToRemove( CloneSurfFlatRegion( pSfrOrig)) ;
|
|
if ( IsNull( pSfrToRemove) || ! pSfrToRemove->IsValid())
|
|
return false ;
|
|
while ( nIter < MAX_ITER) {
|
|
// determino se le curve vanno invertite o meno
|
|
// essendo FatCurve, la parte interna alla regione di classificazione è invertita
|
|
bool bInvert = false ;
|
|
if ( PockParam.nType == POCKET_CONFORMAL_ZIGZAG)
|
|
bInvert = ! ( ( ! IsEven( nIter) && ! PockParam.bInvert) || ( IsEven( nIter) && PockParam.bInvert)) ;
|
|
else if ( PockParam.nType == POCKET_CONFORMAL_ONEWAY)
|
|
bInvert = ! ( PockParam.bInvert) ;
|
|
// recupero gli Offset dei tratti chiusi
|
|
ICURVEPOVECTOR vFatCrv ;
|
|
if ( ! myVRONI.CalcFatCurve( vFatCrv, dOffs, false, false))
|
|
return false ;
|
|
// vettore di Curve dentro alla regione di classificazione
|
|
ICRVCOMPOPOVECTOR vCrvOffsInside ;
|
|
// per tutte le curve ottenute, tengo solo per le parti interne alla regione di classificazione
|
|
bool bStop = true ;
|
|
for ( int j = 0 ; j < int( vFatCrv.size()) ; ++ j) {
|
|
CRVCVECTOR ccClass ;
|
|
if ( ! pSfrClass->GetCurveClassification( *vFatCrv[j], EPS_SMALL, ccClass))
|
|
return false ;
|
|
for ( int k = 0 ; k < int( ccClass.size()) ; ++ k) {
|
|
if ( ccClass[k].nClass == CRVC_IN) {
|
|
// recupero il tratto di curva
|
|
PtrOwner<ICurve> pMyCrv( vFatCrv[j]->CopyParamRange( ccClass[k].dParS, ccClass[k].dParE)) ;
|
|
if ( ! IsNull( pMyCrv) && pMyCrv->IsValid()) {
|
|
if ( ! IsNull( pSfrLimit) && pSfrLimit->IsValid()) {
|
|
// controllo che tale tratto sia esterno alla regione di incidenza
|
|
CRVCVECTOR ccClass1 ;
|
|
if ( ! pSfrLimit->GetCurveClassification( *pMyCrv, EPS_SMALL, ccClass1))
|
|
return false ;
|
|
for ( int kk = 0 ; kk < int( ccClass1.size()) ; ++ kk) {
|
|
if ( ccClass1[kk].nClass != CRVC_IN) {
|
|
PtrOwner<ICurve> pMyCrv1( pMyCrv->CopyParamRange( ccClass1[kk].dParS, ccClass1[kk].dParE)) ;
|
|
if ( ! IsNull( pMyCrv1) && pMyCrv1->IsValid()) {
|
|
// almeno una curva in trovata
|
|
bStop = false ;
|
|
// inverto se necessario
|
|
if ( bInvert)
|
|
pMyCrv1->Invert() ;
|
|
// salvo come seconda proprietà temporanea il lato interno
|
|
pMyCrv1->SetTempProp( bInvert ? MDS_LEFT : MDS_RIGHT, 1) ;
|
|
// memorizzo la curva nel vettore
|
|
vCrvOffsInside.emplace_back( ConvertCurveToComposite( Release( pMyCrv1))) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// almeno una curva in trovata
|
|
bStop = false ;
|
|
// inverto se necessario
|
|
if ( bInvert)
|
|
pMyCrv->Invert() ;
|
|
// salvo come seconda proprietà temporanea il lato interno
|
|
pMyCrv->SetTempProp( bInvert ? MDS_LEFT : MDS_RIGHT, 1) ;
|
|
// memorizzo la curva nel vettore
|
|
vCrvOffsInside.emplace_back( ConvertCurveToComposite( Release( pMyCrv))) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// concateno se necessario ( per tolleranza Offset)
|
|
int nTempProp0 = ( ! vCrvOffsInside.empty() ? vCrvOffsInside[0]->GetTempProp( 0) : TEMP_PROP_INVALID) ;
|
|
int nTempProp1 = ( ! vCrvOffsInside.empty() ? vCrvOffsInside[0]->GetTempProp( 1) : TEMP_PROP_INVALID) ;
|
|
if ( ! ChainCompoCurves( vCrvOffsInside))
|
|
return false ;
|
|
for ( auto& CrvCompo : vCrvOffsInside) {
|
|
CrvCompo->SetTempProp( nTempProp0, 0) ;
|
|
CrvCompo->SetTempProp( nTempProp1, 1) ;
|
|
}
|
|
// se ho trovato delle curve interne
|
|
if ( ! bStop) {
|
|
// inserisco le curva ricavate all'iterazione nIter nel vettore
|
|
vCrvOffs.resize( ++ nIter) ; // incremento nIter
|
|
for ( int i = 0 ; i < int( vCrvOffsInside.size()) ; ++ i) {
|
|
vCrvOffsInside[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
// recupero la regione rimossa da tale curva
|
|
PtrOwner<ISurfFlatRegion> pSfrFatCurve( GetSurfFlatRegionFromFatCurve( vCrvOffsInside[i]->Clone(), PockParam.dRad, false, false)) ;
|
|
if ( IsNull( pSfrFatCurve) || ! pSfrFatCurve->IsValid())
|
|
return false ;
|
|
if ( AreOppositeVectorApprox( pSfrFatCurve->GetNormVersor(), Z_AX))
|
|
pSfrFatCurve->Invert() ;
|
|
// controllo se tale regione interserca quella da rimuovere
|
|
PtrOwner<ISurfFlatRegion> pSfrFatCurveCL( CloneSurfFlatRegion( pSfrFatCurve)) ;
|
|
if ( IsNull( pSfrFatCurveCL) || ! pSfrFatCurveCL->IsValid())
|
|
return false ;
|
|
if ( pSfrToRemove->IsValid()) {
|
|
pSfrFatCurveCL->Intersect( *pSfrToRemove) ;
|
|
// se esiste l'intersezione
|
|
if ( pSfrFatCurveCL->IsValid()) {
|
|
// conservo la curva
|
|
vCrvOffs.back().emplace_back( Release( vCrvOffsInside[i])) ;
|
|
// aggiorno la regione da rimuovere
|
|
pSfrToRemove->Subtract( *pSfrFatCurve) ;
|
|
}
|
|
}
|
|
}
|
|
// se non ho inserito nulla, allora ho finito
|
|
if ( vCrvOffs.back().empty()) {
|
|
vCrvOffs.pop_back() ;
|
|
break ;
|
|
}
|
|
// aggiorno l'Offset progressivo
|
|
dOffs += PockParam.dSideStep ;
|
|
}
|
|
// se non ho ricavato curve interne, allora esco
|
|
else
|
|
break ;
|
|
}
|
|
|
|
// smusso le curve di Offset ( ad eccezione di quelle generate alla prima iterazione)
|
|
for ( int i = 1 ; i < int( vCrvOffs.size()) ; ++ i) {
|
|
for ( int j = 0 ; j < int( vCrvOffs[i].size()) ; ++ j)
|
|
ModifyCurveToSmoothed( vCrvOffs[i][j], PockParam, PockParam.dSmooth, PockParam.dSmooth, false) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CalcSpiralPocketing( const ISurfFlatRegion* pSfr, int nType, const PocketParams& PockParams,
|
|
ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// verifica validità della superficie
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
// il tipo può essere solo SpiralIn o SpiralOut
|
|
if ( nType != POCKET_SPIRALIN && nType != POCKET_SPIRALOUT)
|
|
return false ;
|
|
PtrOwner<ISurfFlatRegion> pSfrLimit( PockParams.SfrLimit.IsValid() ? PockParams.SfrLimit.Clone() : CreateSurfFlatRegion()) ;
|
|
// calcolo il percorso di svuotatura spiral
|
|
return ( CalcPocketing( pSfr, PockParams.dRad, PockParams.dRadialOffset, PockParams.dSideStep,
|
|
PockParams.dAngle, PockParams.dOpenMinSafe, nType, PockParams.bSmooth,
|
|
PockParams.bCalcUnclearedRegs, PockParams.bInvert, PockParams.bAvoidOpt,
|
|
PockParams.bConventionalMilling, PockParams.bAllowZigZagOneWayBorders, PockParams.bCalcFeed, PockParams.ptStart,
|
|
pSfrLimit, PockParams.bAvoidOpt, PockParams.dMaxOptSize,
|
|
PockParams.nLiType, PockParams.dLiTang, PockParams.dLiElev, PockParams.nLoType, PockParams.dLoTang,
|
|
PockParams.bPolishing, PockParams.dEpicyclesRad, PockParams.dEpicyclesDist, vCrvCompoRes)) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
GetOrigChunkForConformal( const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pSfrOrig,
|
|
set<int>& setChunks)
|
|
{
|
|
// NB. funzione migliorabile... potrei calcolare tutto all'inizio senza ripetere le intersezioni
|
|
// controllo validità delle regioni
|
|
if ( pSfrChunk == nullptr || ! pSfrChunk->IsValid() ||
|
|
pSfrOrig == nullptr || ! pSfrOrig->IsValid())
|
|
return false ;
|
|
|
|
// se la superficie originale ha un solo Chunk allora è lui stesso
|
|
if ( pSfrOrig->GetChunkCount() == 1) {
|
|
setChunks.insert( 0) ;
|
|
return true ;
|
|
}
|
|
|
|
// essendo la pSfrChunk estesa, cerco quale chunk di pSfrOrig ha una intersezione valida
|
|
for ( int nC = 0 ; nC < pSfrOrig->GetChunkCount() ; ++ nC) {
|
|
// se il Chunk è già stato analizzato in precedenza, passo al successivo
|
|
if ( setChunks.find( nC) != setChunks.end())
|
|
continue ;
|
|
// recupero il Chunk corrente
|
|
PtrOwner<ISurfFlatRegion> pSfrChunkOrig( pSfrOrig->CloneChunk( nC)) ;
|
|
if ( IsNull( pSfrChunkOrig) || ! pSfrChunkOrig->IsValid())
|
|
return false ;
|
|
// controllo se esiste l'intersezione
|
|
if ( pSfrChunkOrig->Intersect( *pSfrChunk) && pSfrChunkOrig->IsValid())
|
|
setChunks.insert( nC) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CreateSurfFromOtherChunks( ISurfFlatRegion* pSfr, const ISurfFlatRegion* pSfrOther, const set<int>& setChunks)
|
|
{
|
|
// controllo della regione
|
|
if ( pSfr == nullptr ||
|
|
pSfrOther == nullptr || ! pSfrOther->IsValid())
|
|
return false ;
|
|
|
|
// se non ho indici di Chunk non faccio nulla
|
|
if ( setChunks.empty())
|
|
return true ;
|
|
|
|
// scorro gli indici dei Chunks
|
|
for ( auto it = setChunks.begin() ; it != setChunks.end() ; ++ it) {
|
|
// recupero l'indice del Chunk
|
|
int nChunk = *it ;
|
|
// recupero il Chunk come regione piana
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfrOther->CloneChunk( nChunk)) ;
|
|
if ( ! IsNull( pSfrChunk) && pSfrChunk->IsValid()) {
|
|
if ( ! pSfr->IsValid())
|
|
pSfr->CopyFrom( pSfrChunk) ;
|
|
else if ( ! pSfr->Add( *pSfrChunk))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AddConformal( ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig,
|
|
PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfrPock == nullptr || ! pSfrPock->IsValid() ||
|
|
pSfrOrig == nullptr || ! pSfrOrig->IsValid())
|
|
return true ;
|
|
|
|
// se superifice tutta aperta, lavoro in SPIRAL_IN
|
|
if ( PockParams.bAllOpen)
|
|
return ( CalcSpiralPocketing( pSfrOrig, POCKET_SPIRALIN, PockParams, vCrvCompoRes)) ;
|
|
// se superficie tutta chiusa, lavoro in SPIRAL_OUT
|
|
if ( PockParams.bAllClosed)
|
|
return ( CalcSpiralPocketing( pSfrOrig, POCKET_SPIRALOUT, PockParams, vCrvCompoRes)) ;
|
|
|
|
// definisco eventuali regioni da lavorare in SpiralIn e SpiralOut
|
|
PtrOwner<ISurfFlatRegion> pSfrSpiralIn( CreateSurfFlatRegion()) ;
|
|
PtrOwner<ISurfFlatRegion> pSfrSpiralOut( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrSpiralIn) || IsNull( pSfrSpiralOut))
|
|
return false ;
|
|
// NB. La supercicie pSfrPock è estesa presso i lati aperti, quindi il suo numero di Chunk potrebbe
|
|
// essere differente dal numero di Chunk della superficie originale di svuotatura. Tengo un insieme di
|
|
// di indici dei Chunk che devono essere lavorati in SpiralIn e in SpiralOut
|
|
set<int> setIndChunkSpiralIn ;
|
|
set<int> setIndChunkSpiralOut ;
|
|
|
|
// scorro i chunk della superficie da lavorare
|
|
for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) {
|
|
|
|
// controllo se il Chunk ha tutte proprietà omogenee tra loro
|
|
bool bClose, bOpen ;
|
|
if ( ! IsChunkAllHomogeneous( pSfrPock, nC, bClose, bOpen))
|
|
return false ;
|
|
|
|
// recupero il Chunk come regione piana
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfrPock->CloneChunk( nC)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
|
|
// --- se Chunk tutto aperto, aggiorno i Chunk della superficie originale da lavorare in SpiralIn
|
|
if ( bOpen) {
|
|
if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn))
|
|
return false ;
|
|
continue ;
|
|
}
|
|
// --- se Chunk tutto chiuso, lo lavoro in SPIRAL_OUT
|
|
else if ( bClose) {
|
|
if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralOut))
|
|
continue ;
|
|
}
|
|
// --- se chunk non omogoneo, ricavo gli Offset ( se possibili) dei tratti chiusi del Chunk
|
|
VICRVCOMPOPOVECTOR vvCrvOffs ;
|
|
if ( ! GetConformalOffsets( pSfrChunk, pSfrChunk, pSfrOrig, PockParams, vvCrvOffs))
|
|
return false ;
|
|
|
|
// se non ottengo Curve di Offset, lavoro in SpiralIn ( il Chunk ha dei lati aperti)
|
|
if ( vvCrvOffs.empty()) {
|
|
if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn))
|
|
return false ;
|
|
continue ;
|
|
}
|
|
|
|
// definisco i vettori ordinati degli Offset e dei Link
|
|
ICRVCOMPOPOVECTOR vCrvOffs ;
|
|
ICURVEPOVECTOR vCrvLink ;
|
|
if ( ! CalcConformalOffsAndLinks( vvCrvOffs, pSfrChunk, pSfrPock, PockParams, vCrvOffs, vCrvLink)) {
|
|
if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn))
|
|
return false ;
|
|
continue ;
|
|
}
|
|
|
|
// flag per controllo
|
|
bool bOk = true ;
|
|
// estendo i percorsi per eventuali regioni non svuotate e calcolo le Feed
|
|
bOk = bOk && ExtendConformalOffsAndSetFeed( pSfrPock, pSfrOrig, PockParams, vCrvOffs, vCrvLink) ;
|
|
// concateno Offset e Links
|
|
ICRVCOMPOPOVECTOR vCrvPaths ;
|
|
bOk = bOk && ChainConformalOffsWithLinks( vCrvOffs, vCrvLink, vCrvPaths) ;
|
|
// estendo per lati aperti ed ordino i percorsi trovati
|
|
bOk = bOk && OrderAndExtendConformalPaths( vCrvPaths, pSfrChunk, pSfrOrig, PockParams) ;
|
|
|
|
// se qualche passaggio restituisce errore, provo in SpiralIn
|
|
if ( ! bOk) {
|
|
if ( ! GetOrigChunkForConformal( pSfrChunk, pSfrOrig, setIndChunkSpiralIn))
|
|
return false ;
|
|
continue ;
|
|
}
|
|
// altrimenti aggiungo i percorsi ricavati
|
|
for ( int i = 0 ; i < int( vCrvPaths.size()) ; ++ i)
|
|
vCrvCompoRes.emplace_back( Release( vCrvPaths[i])) ;
|
|
}
|
|
|
|
// se ho superfici da lavorare in SpiralIn, aggiungo le curve
|
|
if ( ! setIndChunkSpiralIn.empty()) {
|
|
// costruisco la regione di SpiralIn
|
|
PtrOwner<ISurfFlatRegion> pSfrSpiralIn( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrSpiralIn) ||
|
|
! CreateSurfFromOtherChunks( pSfrSpiralIn, pSfrOrig, setIndChunkSpiralIn))
|
|
return false ;
|
|
// recupero le curve di Pocketing SpiralIn
|
|
ICRVCOMPOPOVECTOR vCrvSpiralIn ;
|
|
if ( ! CalcSpiralPocketing( pSfrSpiralIn, POCKET_SPIRALIN, PockParams, vCrvSpiralIn))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vCrvSpiralIn.size()) ; ++ i) {
|
|
if ( vCrvSpiralIn[i] != nullptr && vCrvSpiralIn[i]->IsValid())
|
|
vCrvCompoRes.emplace_back( Release( vCrvSpiralIn[i])) ;
|
|
}
|
|
}
|
|
// se ho superfici da lavorare in SpiralOut, aggiungo le curve
|
|
if ( ! setIndChunkSpiralOut.empty()) {
|
|
// costruisco la regione di SpiralOut
|
|
PtrOwner<ISurfFlatRegion> pSfrSpiralOut( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSfrSpiralIn) ||
|
|
! CreateSurfFromOtherChunks( pSfrSpiralOut, pSfrOrig, setIndChunkSpiralOut))
|
|
return false ;
|
|
// recupero le curve di Pocketing SpiralOut
|
|
ICRVCOMPOPOVECTOR vCrvSpiralOut ;
|
|
if ( ! CalcSpiralPocketing( pSfrSpiralOut, POCKET_SPIRALOUT, PockParams, vCrvSpiralOut))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vCrvSpiralOut.size()) ; ++ i) {
|
|
if ( vCrvSpiralOut[i] != nullptr && vCrvSpiralOut[i]->IsValid())
|
|
vCrvCompoRes.emplace_back( Release( vCrvSpiralOut[i])) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
SmoothExtensionLinesByIntersection( ICRVCOMPOPOVECTOR& vCrvPaths, const PocketParams& PockParams)
|
|
{
|
|
// estendendo i percorsi con tratti in ingresso e in uscita, può capitare che si creino autointersezioni
|
|
// se ho meno di due percorsi non faccio nulla
|
|
if ( int( vCrvPaths.size()) < 2)
|
|
return true ;
|
|
|
|
// cerco le autointersezioni tra i percorsi successivi tra loro
|
|
double dTol = 50 * EPS_SMALL ;
|
|
for ( int i = 0 ; i < int( vCrvPaths.size()) - 1 ; ++ i) {
|
|
// se uno dei due percorsi presenta meno di 3 curve, non faccio nulla
|
|
if ( ! vCrvPaths[i]->IsValid() || vCrvPaths[i]->GetCurveCount() < 3 ||
|
|
! vCrvPaths[i+1]->IsValid() || vCrvPaths[i+1]->GetCurveCount() < 3)
|
|
continue ;
|
|
// se il percorso i-esimo non presenta una estensione in uscita e il percorso (i+1)-esimo
|
|
// non presenta una estensione in entrata, non faccio nulla
|
|
if ( vCrvPaths[i]->GetLastCurve()->GetTempProp() != TEMP_PROP_OUT_START ||
|
|
vCrvPaths[i+1]->GetFirstCurve()->GetTempProp() != TEMP_PROP_OUT_START)
|
|
continue ;
|
|
// recupero i due tratti lineari del percorso
|
|
const ICurveLine* pLineA = GetCurveLine( vCrvPaths[i]->GetLastCurve()) ;
|
|
const ICurveLine* pLineB = GetCurveLine( vCrvPaths[i+1]->GetFirstCurve()) ;
|
|
if ( pLineA == nullptr || ! pLineA->IsValid() ||
|
|
pLineB == nullptr || ! pLineB->IsValid())
|
|
continue ;
|
|
// se non esistono intersezioni tra i segmenti, non faccio nulla
|
|
IntersCurveCurve ICC( *pLineA, *pLineB) ;
|
|
if ( ICC.GetIntersCount() == 0)
|
|
continue ;
|
|
// definisco la curva di collegamento
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
Point3d ptS_A ; pLineA->GetStartPoint( ptS_A) ;
|
|
pCrvLink->AddPoint( ptS_A) ;
|
|
IntCrvCrvInfo aInfo ;
|
|
if ( ! ICC.GetIntCrvCrvInfo( 0, aInfo))
|
|
continue ;
|
|
pCrvLink->AddLine( aInfo.IciA[0].ptI) ;
|
|
if ( aInfo.bOverlap)
|
|
pCrvLink->AddLine( aInfo.IciA[1].ptI) ;
|
|
Point3d ptE_B ; pLineB->GetEndPoint( ptE_B) ;
|
|
pCrvLink->AddLine( ptE_B) ;
|
|
// se tutti i punti sono coincidenti, allora non ho bisogno di entrambe le estensioni
|
|
if ( ! pCrvLink->IsValid() || pCrvLink->GetCurveCount() == 0) {
|
|
delete( vCrvPaths[i]->RemoveFirstOrLastCurve( true)) ;
|
|
delete( vCrvPaths[i+1]->RemoveFirstOrLastCurve( false)) ;
|
|
vCrvPaths[i]->AddCurve( Release( vCrvPaths[i+1]), dTol) ;
|
|
vCrvPaths.erase( vCrvPaths.begin() + i + 1) ;
|
|
-- i ;
|
|
continue ;
|
|
}
|
|
// raccordo del Link con percorso attuale e successivo
|
|
double dLen ;
|
|
double dLenExtension ;
|
|
pLineA->GetLength( dLenExtension) ;
|
|
if ( vCrvPaths[i]->GetLength( dLen) && ( dLen - dLenExtension) > PockParams.dRad / 2.) {
|
|
double dUS, dUE ;
|
|
vCrvPaths[i]->GetDomain( dUS, dUE) ;
|
|
double dU ;
|
|
vCrvPaths[i]->GetParamAtLength( dLen - dLenExtension - ( PockParams.dRad / 2.), dU) ;
|
|
pCrvLink->AddCurve( ConvertCurveToComposite( vCrvPaths[i]->CopyParamRange( dU, dUE - 1)), false, dTol) ; ;
|
|
}
|
|
pLineB->GetLength( dLenExtension) ;
|
|
if ( vCrvPaths[i+1]->GetLength( dLen) && ( dLen - dLenExtension) > PockParams.dRad / 2.) {
|
|
double dUS, dUE ;
|
|
vCrvPaths[i+1]->GetDomain( dUS, dUE) ;
|
|
double dU ;
|
|
vCrvPaths[i+1]->GetParamAtLength( dLenExtension + ( PockParams.dRad / 2.), dU) ;
|
|
pCrvLink->AddCurve( ConvertCurveToComposite( vCrvPaths[i+1]->CopyParamRange( dUS + 1, dU)), true, dTol) ;
|
|
}
|
|
pCrvLink->MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG) ;
|
|
ModifyCurveToSmoothed( pCrvLink, PockParams, PockParams.dRad / 8., PockParams.dRad / 8., false) ;
|
|
// assegno Feed massima
|
|
AssignMaxFeed( pCrvLink, PockParams) ;
|
|
// modifico i percorsi
|
|
Point3d ptLinkS ; pCrvLink->GetStartPoint( ptLinkS) ;
|
|
Point3d ptLinkE ; pCrvLink->GetEndPoint( ptLinkE) ;
|
|
double dUE, dUS ;
|
|
if ( ! vCrvPaths[i]->GetParamAtPoint( ptLinkS, dUE, dTol) ||
|
|
! vCrvPaths[i]->TrimEndAtParam( dUE) ||
|
|
! vCrvPaths[i+1]->GetParamAtPoint( ptLinkE, dUS, dTol) ||
|
|
! vCrvPaths[i+1]->TrimStartAtParam( dUS))
|
|
continue ;
|
|
vCrvPaths[i]->AddCurve( Release( pCrvLink), dTol) ;
|
|
vCrvPaths[i]->AddCurve( Release( vCrvPaths[i+1]), dTol) ;
|
|
vCrvPaths.erase( vCrvPaths.begin() + i + 1) ;
|
|
-- i ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double dStep, double dAngle,
|
|
double dOpenMinSafe, int nType, bool bSmooth, bool bCalcUnclReg, bool bInvert, bool bAvoidOpt,
|
|
bool bConventionalMilling, bool bAllowZigZagOneWayBorders, bool bCalcFeed, const Point3d& ptEndPrec,
|
|
const ISurfFlatRegion* pSfrLimit, bool bAllOffs, double dMaxOptSize,
|
|
int nLiType, double dLiTang, double dLiElev, int nLoType, double dLoTang,
|
|
bool bPolishing, double dEpicyclesRad, double dEpicyclesDist, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid() ||
|
|
dStep < 10 * EPS_SMALL ||
|
|
( nType != POCKET_ZIGZAG && nType != POCKET_ONEWAY && nType != POCKET_SPIRALIN &&
|
|
nType != POCKET_SPIRALOUT && nType != POCKET_CONFORMAL_ZIGZAG && nType != POCKET_CONFORMAL_ONEWAY))
|
|
return false ;
|
|
|
|
// pulizia vettore delle curve elementari
|
|
vCrvCompoRes.clear() ;
|
|
|
|
// assegno dati di modulo
|
|
PocketParams myParams ;
|
|
myParams.nType = nType ;
|
|
myParams.dRad = dRad ;
|
|
myParams.dRadialOffset = dRadOffs ;
|
|
myParams.bSmooth = bSmooth ;
|
|
myParams.dAngle = dAngle ;
|
|
myParams.dSideStep = dStep ;
|
|
myParams.dOpenMinSafe = dOpenMinSafe ;
|
|
myParams.bCalcUnclearedRegs = bCalcUnclReg ;
|
|
myParams.bInvert = bInvert ;
|
|
myParams.bAvoidOpt = bAvoidOpt ;
|
|
myParams.bCalcFeed = bCalcFeed ;
|
|
myParams.bConventionalMilling = bConventionalMilling ;
|
|
myParams.bAllowZigZagOneWayBorders = bAllowZigZagOneWayBorders ;
|
|
myParams.bOptOffsets = ( ! bAllOffs) ;
|
|
myParams.dMaxOptSize = dMaxOptSize ;
|
|
myParams.nLiType = nLiType ;
|
|
myParams.dLiTang = dLiTang ;
|
|
myParams.dLiElev = dLiElev ;
|
|
myParams.nLoType = nLoType ;
|
|
myParams.dLoTang = dLoTang ;
|
|
myParams.bPolishing = bPolishing ;
|
|
myParams.dEpicyclesRad = dEpicyclesRad ;
|
|
myParams.dEpicyclesDist = dEpicyclesDist ;
|
|
if ( ptEndPrec.IsValid())
|
|
myParams.ptStart = ptEndPrec ;
|
|
if ( pSfrLimit != nullptr && pSfrLimit->IsValid())
|
|
myParams.SfrLimit.CopyFrom( pSfrLimit) ;
|
|
Point3d ptCenter ; pSfr->GetCentroid( ptCenter) ;
|
|
if ( ! myParams.frLocXY.Set( ptCenter, pSfr->GetNormVersor()) ||
|
|
! myParams.frLocXY.Rotate( ptCenter, pSfr->GetNormVersor() , myParams.dAngle))
|
|
return false ;
|
|
|
|
// porto la superficie da svuotare nel sistema di riferimento lungo Z_AX
|
|
PtrOwner<ISurfFlatRegion> pSfrAdj( pSfr->Clone()) ;
|
|
if ( IsNull( pSfrAdj) || ! pSfrAdj->ToLoc( myParams.frLocXY))
|
|
return false ;
|
|
// porto la superficie limite ( se valida) in tale sistema
|
|
if ( myParams.SfrLimit.IsValid())
|
|
myParams.SfrLimit.ToLoc( myParams.frLocXY) ;
|
|
// porto il punto iniziale di riferimento ( se valido) in tale sistema
|
|
if ( myParams.ptStart.IsValid())
|
|
myParams.ptStart.ToLoc( myParams.frLocXY) ;
|
|
// controllo se la superficie è tutta chiusa o tutta aperta
|
|
if ( ! IsSfrAllHomogeneous( pSfr, myParams.bAllClosed, myParams.bAllOpen))
|
|
return false ;
|
|
|
|
// ------------ casi ottimizzati ------------------------------------------
|
|
ICRVCOMPOPOVECTOR vCrvOptCurves ;
|
|
if ( ! GetPocketingOptimizedCurves( pSfrAdj, myParams, vCrvOptCurves))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vCrvOptCurves.size()) ; ++ i) {
|
|
vCrvOptCurves[i]->ToGlob( myParams.frLocXY) ;
|
|
vCrvCompoRes.emplace_back( Release( vCrvOptCurves[i])) ;
|
|
}
|
|
// se ho svuotato tutta la superficie, allora ho finito
|
|
if ( IsNull( pSfrAdj) || ! pSfrAdj->IsValid() || pSfrAdj->GetChunkCount() == 0)
|
|
return true ;
|
|
|
|
// ------------ recupero le singole curve di svuotatura -------------------
|
|
ICRVCOMPOPOVECTOR vCrvSingleCurves ;
|
|
if ( ! GetSinglePocketingCurves( pSfrAdj, myParams, vCrvSingleCurves))
|
|
return false ;
|
|
for ( int i = 0 ; i < int( vCrvSingleCurves.size()) ; ++ i) {
|
|
vCrvSingleCurves[i]->ToGlob( myParams.frLocXY) ;
|
|
vCrvCompoRes.emplace_back( Release( vCrvSingleCurves[i])) ;
|
|
}
|
|
|
|
// se ho svuotato tutta la superficie, allora ho finito
|
|
if ( IsNull( pSfrAdj) || ! pSfrAdj->IsValid() || pSfrAdj->GetChunkCount() == 0)
|
|
return true ;
|
|
|
|
// ------------ estensione lati aperti ------------------------------------
|
|
if ( ! ModifySurfByOpenEdges( pSfrAdj, myParams))
|
|
return false ;
|
|
|
|
// porto una copia della superficie originale nel frame creato
|
|
PtrOwner<ISurfFlatRegion> pSfr_Loc( CloneSurfFlatRegion( pSfr)) ;
|
|
if ( IsNull( pSfr_Loc) || ! pSfr_Loc->IsValid() || ! pSfr_Loc->ToLoc( myParams.frLocXY))
|
|
return false ;
|
|
|
|
// ------------ calcolo delle curve elementari della superficie -------------------
|
|
ICRVCOMPOPOVECTOR vCrvCompoPock ;
|
|
switch ( nType) {
|
|
case POCKET_ZIGZAG :
|
|
if ( ! AddZigZag( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
|
|
return false ;
|
|
break ;
|
|
case POCKET_ONEWAY :
|
|
if ( ! AddOneWay( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
|
|
return false ;
|
|
break ;
|
|
case POCKET_SPIRALIN :
|
|
if ( ! AddSpiralIn( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
|
|
return false ;
|
|
break ;
|
|
case POCKET_SPIRALOUT :
|
|
if ( ! AddSpiralOut( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
|
|
return false ;
|
|
break ;
|
|
case POCKET_CONFORMAL_ZIGZAG :
|
|
case POCKET_CONFORMAL_ONEWAY :
|
|
if ( ! AddConformal( pSfrAdj, pSfr_Loc, myParams, vCrvCompoPock))
|
|
return false ;
|
|
break ;
|
|
}
|
|
|
|
// ------------ intersezione delle estensioni ----------------------------
|
|
if ( ! myParams.bAllClosed) {
|
|
if ( ! SmoothExtensionLinesByIntersection( vCrvCompoPock, myParams))
|
|
return false ;
|
|
}
|
|
|
|
// riporto tutte le curve ottenute in globale
|
|
for ( int i = 0 ; i < int( vCrvCompoPock.size()) ; ++ i) {
|
|
vCrvCompoPock[i]->ToGlob( myParams.frLocXY) ;
|
|
vCrvCompoRes.emplace_back( Release( vCrvCompoPock[i])) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
IsPocketingHole( const ISurfFlatRegion* pSfr, double dToolRad, double dRadialOffs, double dSideStep, int nType, int nLiType,
|
|
double dLiTang, double dRatioSfrTool)
|
|
{
|
|
// verifico validità della superficie
|
|
if ( pSfr == nullptr || ! pSfr->IsValid())
|
|
return false ;
|
|
|
|
// la superficie deve avere un solo Chunk e non presentare isole
|
|
if ( pSfr->GetChunkCount() != 1 && pSfr->GetLoopCount( 0) != 1)
|
|
return false ;
|
|
|
|
// porto la superficie nel suo riferimento intrinseco, quindi lavoro nel piano XY
|
|
Point3d ptC ; pSfr->GetCentroid( ptC) ;
|
|
Frame3d frXY ;
|
|
if ( ! frXY.Set( ptC, pSfr->GetNormVersor()))
|
|
return false ;
|
|
PtrOwner<ISurfFlatRegion> pSfrCL( CloneSurfFlatRegion( pSfr)) ;
|
|
if ( IsNull( pSfrCL) || ! pSfrCL->IsValid() || ! pSfrCL->ToLoc( frXY))
|
|
return false ;
|
|
|
|
// recupero l'unica curva di bordo che descrive la regione piana
|
|
PtrOwner<ICurveComposite> pCrvBorder( ConvertCurveToComposite( pSfrCL->GetLoop( 0, 0))) ;
|
|
if ( IsNull( pCrvBorder) || ! pCrvBorder->IsValid())
|
|
return false ;
|
|
pCrvBorder->MergeCurves( 10. * EPS_SMALL, 10. * EPS_ANG_SMALL, true, true) ;
|
|
pCrvBorder->SetExtrusion( pSfrCL->GetNormVersor()) ; // Z_AX()
|
|
|
|
// verifico che le proprietà di lato Aperto/Chiuso siano uniformi
|
|
bool bOkSpiral = true ;
|
|
const ICurve* pFirstCrv = pCrvBorder->GetCurve( 0) ;
|
|
if ( pFirstCrv == nullptr || ! pFirstCrv->IsValid())
|
|
return false ;
|
|
int nTmpPropRef = pFirstCrv->GetTempProp( 0) ;
|
|
for ( int nU = 1 ; bOkSpiral && nU < pCrvBorder->GetCurveCount() ; ++ nU) {
|
|
const ICurve* pCrv = pCrvBorder->GetCurve( nU) ;
|
|
if ( pCrv == nullptr || ! pCrv->IsValid())
|
|
return false ;
|
|
bOkSpiral = ( pCrv->GetTempProp( 0) == nTmpPropRef) ;
|
|
}
|
|
if ( ! bOkSpiral)
|
|
return false ;
|
|
|
|
// definisco i parametri di una lavorazione Standard ( solo quelli utili per una svuotatura di un foro)
|
|
PocketParams PockParam ;
|
|
PockParam.dRad = dToolRad ;
|
|
PockParam.dRadialOffset = dRadialOffs ;
|
|
PockParam.dSideStep = dSideStep ;
|
|
PockParam.ptStart = P_INVALID ;
|
|
PockParam.bCalcFeed = false ;
|
|
PockParam.nType = nType ;
|
|
PockParam.nLiType = nLiType ;
|
|
PockParam.dLiTang = dLiTang ;
|
|
|
|
// controllo che il Loop sia una circonferenza
|
|
Point3d ptCen ; double dRad ;
|
|
if ( ! OptimizedSpiralCircle( pCrvBorder, 50. * EPS_SMALL, dRad, ptCen, bOkSpiral) || ! bOkSpiral)
|
|
return false ;
|
|
double dOffs = PockParam.dRad + PockParam.dRadialOffset ;
|
|
// se curva tutta Open, ingrandisco il raggio della circonferenza trovata
|
|
if ( nTmpPropRef == TEMP_PROP_OPEN_EDGE)
|
|
dRad += 1.05 * PockParam.dRad + PockParam.dRadialOffset ;
|
|
// se curva chiusa, controllo che il raggio sia compatibile con il primo Offset
|
|
else
|
|
bOkSpiral = ( dRad - dOffs > 10. * EPS_SMALL) ;
|
|
if ( ! bOkSpiral)
|
|
return false ;
|
|
|
|
// verifico di rispettare il Ratio
|
|
if ( dRad > dRatioSfrTool * dToolRad + EPS_SMALL)
|
|
return false ;
|
|
|
|
// verifico se l'entrata è compatibile con la geometria e calcolo un eventuale percorso
|
|
double dIntRad = 0 ;
|
|
if ( PockParam.nType == POCKET_SPIRALOUT && PockParam.nLiType == LEAD_IN_HELIX)
|
|
dIntRad = min( 0.5 * min( PockParam.dLiTang, 2. * PockParam.dRad), dRad - dOffs) ;
|
|
PtrOwner<ICurveComposite> pCrvRes( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvRes))
|
|
return false ;
|
|
bOkSpiral = CalcCircleSpiral( ptCen, pSfrCL->GetNormVersor(), dRad - dOffs, dIntRad, PockParam, pCrvRes) ;
|
|
return ( bOkSpiral && pCrvRes->IsValid() && pCrvRes->GetCurveCount() > 0) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustZigZagPathTangentLinks( ICurveComposite* pCrvCompo)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
|
|
// miglioro la curva
|
|
pCrvCompo->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
|
|
// scorro tutte le curve
|
|
int nCurrTmpProp = -1, nCurrTmpProp1 = -1 ;
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
|
|
if ( pCrvCompo->GetCurveTempProp( u, nCurrTmpProp, 1) && nCurrTmpProp == -1) {
|
|
// ricavo il link
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
pCrvLink->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ;
|
|
for ( int uu = u + 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) {
|
|
if ( pCrvCompo->GetCurveTempProp( uu, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
|
|
if ( ! pCrvLink->AddCurve( pCrvCompo->GetCurve( uu)->Clone()))
|
|
return false ;
|
|
}
|
|
else
|
|
break ;
|
|
}
|
|
if ( ! pCrvLink->IsValid())
|
|
return false ;
|
|
// se ho più di una curva nel link
|
|
if ( pCrvLink->GetCurveCount() > 1) {
|
|
// ricavo la curva precendente al Link ( se presente e se prima curva del Link è lineare)
|
|
if ( u != 0 && pCrvLink->GetFirstCurve()->GetType() == CRV_LINE) {
|
|
const ICurve* pCrvSegPrec = pCrvCompo->GetCurve( u - 1) ;
|
|
if ( pCrvSegPrec == nullptr || pCrvSegPrec->GetType() != CRV_LINE)
|
|
return false ;
|
|
// ricavo la sua direzione finale
|
|
Vector3d vtZigZagEndDir ; pCrvSegPrec->GetEndDir( vtZigZagEndDir) ;
|
|
// ricavo la direzione iniziale del link
|
|
Vector3d vtLinkStartDir ; pCrvLink->GetStartDir( vtLinkStartDir) ;
|
|
// se i vettori sono paralleli, considero la prima curva del link come parte del percorso a ZigZag
|
|
if ( AreSameVectorApprox( vtZigZagEndDir, vtLinkStartDir))
|
|
pCrvCompo->SetCurveTempProp( u, 0, 1) ;
|
|
}
|
|
// ricavo la curva successiva al Link ( se presente e se ultima curva del Link è lineare)
|
|
if ( u + pCrvLink->GetCurveCount() < pCrvCompo->GetCurveCount() && pCrvLink->GetLastCurve()->GetType() == CRV_LINE) {
|
|
const ICurve* pCrvSegSucc = pCrvCompo->GetCurve( u + pCrvLink->GetCurveCount()) ;
|
|
if ( pCrvSegSucc == nullptr || pCrvSegSucc->GetType() != CRV_LINE)
|
|
return false ;
|
|
// ricavo la sua direzione inziale
|
|
Vector3d vtZigZagStartDir ; pCrvSegSucc->GetStartDir( vtZigZagStartDir) ;
|
|
// ricavo la direzione finale del link
|
|
Vector3d vtLinkStartDir ; pCrvLink->GetEndDir( vtLinkStartDir) ;
|
|
// se i vettori sono paralleli, considero l'ultima curva del link come parte del percorso a ZigZag
|
|
if ( AreSameVectorApprox( vtZigZagStartDir, vtLinkStartDir))
|
|
pCrvCompo->SetCurveTempProp( u + pCrvLink->GetCurveCount() - 1, 0, 1) ;
|
|
}
|
|
}
|
|
// aggiorno
|
|
u += pCrvLink->GetCurveCount() ; // così la successiva è corretta nel ciclo
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustLinkSameY( ICurveComposite* pCrvLink, bool& bSplit)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvLink == nullptr || ! pCrvLink->IsValid())
|
|
return false ;
|
|
bSplit = false ;
|
|
|
|
// ricavo gli estremi del Link
|
|
Point3d ptS ; pCrvLink->GetStartPoint( ptS) ;
|
|
Point3d ptE ; pCrvLink->GetEndPoint( ptE) ;
|
|
|
|
// ricavo il versore uscente da ptS e diretto verso ptE
|
|
Vector3d vtDir = ptE - ptS ;
|
|
if ( vtDir.IsValid() && ! vtDir.IsSmall())
|
|
vtDir.Normalize() ;
|
|
else
|
|
return false ;
|
|
|
|
// creo un frame orientato come questo versore
|
|
Frame3d frLink ;
|
|
frLink.Set( ptS, Z_AX, vtDir) ;
|
|
if ( ! frLink.IsValid())
|
|
return false ;
|
|
|
|
// ricavo il box3d del Link in questo frame
|
|
BBox3d BBoxLink ;
|
|
pCrvLink->ToLoc( frLink) ;
|
|
if ( ! pCrvLink->GetLocalBBox( BBoxLink) || BBoxLink.IsEmpty())
|
|
return false ;
|
|
pCrvLink->ToGlob( frLink) ;
|
|
|
|
// controllo la dimensione del Box in Y
|
|
if ( abs( BBoxLink.GetDimY()) > 20 * EPS_SMALL)
|
|
bSplit = true ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustLinkDifferentY( const ICurve* pCrvSegPrec, const ICurve* pCrvSegSucc,
|
|
const ICurveComposite* pCrvLink, const PocketParams& PockParams, bool& bSplit)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvLink == nullptr || ! pCrvLink->IsValid())
|
|
return false ;
|
|
if ( pCrvSegPrec == nullptr && pCrvSegSucc == nullptr)
|
|
return true ;
|
|
bSplit = false ;
|
|
|
|
// creo un Trapezoide per regione di incidenza
|
|
Point3d ptH ;
|
|
PtrOwner<ISurfFlatRegion> pSfrTrap( CreateSurfFlatRegion()) ;
|
|
PtrOwner<ICurveComposite> pCrvTrapBorder( CreateCurveComposite()) ;
|
|
if ( IsNull( pSfrTrap) ||
|
|
IsNull( pCrvTrapBorder))
|
|
return false ;
|
|
|
|
// 0) primo tratto
|
|
if ( pCrvSegPrec != nullptr) {
|
|
// se presente lo aggiungo
|
|
if ( ! pCrvTrapBorder->AddCurve( pCrvSegPrec->Clone()))
|
|
return false ;
|
|
}
|
|
else {
|
|
// se non presente, inserisco il punto iniziale del Link
|
|
pCrvLink->GetStartPoint( ptH) ;
|
|
pCrvTrapBorder->AddPoint( ptH) ;
|
|
}
|
|
// 1) tratto lineare fino al punto finale del Link
|
|
if ( ! pCrvLink->GetEndPoint( ptH) ||
|
|
! pCrvTrapBorder->AddLine( ptH))
|
|
return false ;
|
|
// 2) terzo tratto
|
|
if ( pCrvSegSucc != nullptr) {
|
|
// se presente lo aggiungo
|
|
if ( ! pCrvTrapBorder->AddCurve( pCrvSegSucc->Clone()))
|
|
return false ;
|
|
}
|
|
// se non presente non aggiungo nulla, ho alla peggio il Link
|
|
// 4) ultimo tratto lineare per chiudere la curva
|
|
if ( ! pCrvTrapBorder->Close() || // ora creo la FlatRegion a trapezio
|
|
! pSfrTrap->AddExtLoop( Release( pCrvTrapBorder)) ||
|
|
! pSfrTrap->IsValid())
|
|
return false ;
|
|
|
|
// oriento
|
|
if ( AreOppositeVectorApprox( Z_AX, pSfrTrap->GetNormVersor()))
|
|
pSfrTrap->Invert() ;
|
|
|
|
// effettuo un piccolo Offset per una maggiore tolleranza ( se possibile)
|
|
PtrOwner<ISurfFlatRegion> pSfrTrap_c( pSfrTrap->Clone()) ;
|
|
if ( IsNull( pSfrTrap_c))
|
|
return false ;
|
|
if ( ! pSfrTrap->Offset( - PockParams.dRad / 5, ICurve::OFF_CHAMFER))
|
|
pSfrTrap.Set( pSfrTrap_c) ;
|
|
|
|
// se il Link interseca questa regione, allora va eliminato
|
|
CRVCVECTOR ccClass ;
|
|
if ( pSfrTrap->GetCurveClassification( *pCrvLink, EPS_SMALL, ccClass))
|
|
bSplit = int( ccClass.size()) > 1 ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustLinkZigZagInfill( ICurveComposite* pCrvCompo, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vNewCrv)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
vNewCrv.clear() ;
|
|
|
|
// sistemo la curva per evitare che i tratti estremi dei link siano lineari ed in tangenza con percorsi ZigZag
|
|
if ( ! AdjustZigZagPathTangentLinks( pCrvCompo))
|
|
return false ;
|
|
|
|
// definisco un vettore di intervalli su cui spezzare la curva
|
|
struct CopyRange {
|
|
double dParS ;
|
|
double dParE ;
|
|
} ;
|
|
vector<CopyRange> vIntervals ;
|
|
|
|
// parametri
|
|
int nCurrTmpProp1 = 0 ;
|
|
int nCrvStartBreak = 0 ;
|
|
|
|
// NB. I tratti di ZigZag che seguono dei contorni ( i Links) hanno TmpProp1 impostata come -1
|
|
for ( int u = 0 ; u < pCrvCompo->GetCurveCount() ; ++ u) {
|
|
if ( pCrvCompo->GetCurveTempProp( u, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
|
|
// ricavo il link
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
pCrvLink->AddCurve( pCrvCompo->GetCurve( u)->Clone()) ;
|
|
for ( int uu = u + 1 ; uu < pCrvCompo->GetCurveCount() ; ++ uu) {
|
|
if ( pCrvCompo->GetCurveTempProp( uu, nCurrTmpProp1, 1) && nCurrTmpProp1 == -1) {
|
|
if ( ! pCrvLink->AddCurve( pCrvCompo->GetCurve( uu)->Clone()))
|
|
return false ;
|
|
}
|
|
else
|
|
break ;
|
|
}
|
|
if ( ! pCrvLink->IsValid())
|
|
return false ;
|
|
// ricavo il punto iniziale e finale del Link
|
|
Point3d ptS ; pCrvLink->GetStartPoint( ptS) ;
|
|
Point3d ptE ; pCrvLink->GetEndPoint( ptE) ;
|
|
// parametro per spezzare o meno la curva, togliendo il Link
|
|
bool bSplit = false ;
|
|
// se il Link collega due tratti alla stessa quota...
|
|
if ( abs( ptS.y - ptE.y) < 20 * EPS_SMALL) {
|
|
if ( ! AdjustLinkSameY( pCrvLink, bSplit))
|
|
return false ;
|
|
}
|
|
// se il Link collega due tratti a quote diverse
|
|
else {
|
|
if ( ! AdjustLinkDifferentY( pCrvCompo->GetCurve( u - 1), pCrvCompo->GetCurve( u + pCrvLink->GetCurveCount()),
|
|
pCrvLink, PockParams, bSplit))
|
|
return false ;
|
|
}
|
|
// aggiorno
|
|
u += pCrvLink->GetCurveCount() ; // così la successiva è corretta nel ciclo
|
|
if ( bSplit) {
|
|
// se dimensione sopra la tolleranza, allora spezzo la curva e rimuovo il link
|
|
CopyRange CR {
|
|
1. * nCrvStartBreak, // dParS
|
|
1. * ( u - pCrvLink->GetCurveCount()) // dParE
|
|
} ;
|
|
// se il primo tratto è un link non valido, non considero l'intervallo
|
|
if ( ! ( nCrvStartBreak == 0 && abs( CR.dParS - CR.dParE) < 20 * EPS_SMALL))
|
|
vIntervals.push_back( CR) ;
|
|
nCrvStartBreak = u ;
|
|
}
|
|
}
|
|
}
|
|
// se non ho mai trovato link non validi, lascio la curva invariata
|
|
if ( vIntervals.empty())
|
|
vNewCrv.emplace_back( pCrvCompo->Clone()) ;
|
|
else {
|
|
// per ogni intervallo...
|
|
for ( int i = 0 ; i < int( vIntervals.size()) ; ++ i)
|
|
vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( vIntervals[i].dParS, vIntervals[i].dParE))) ;
|
|
// ultimo intervallo ( se l'ultimo intervallo è un link non valido, non inserisco)
|
|
if ( abs( vIntervals.back().dParE - pCrvCompo->GetCurveCount()) > 20 * EPS_SMALL)
|
|
vNewCrv.emplace_back( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nCrvStartBreak, pCrvCompo->GetCurveCount()))) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CalcZigZagInfill( const ISurfFlatRegion* pSfr, double dStep, bool bSmooth, bool bRemoveOverlapLink, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pSfr == nullptr || ! pSfr->IsValid() || dStep < EPS_SMALL)
|
|
return false ;
|
|
vCrvCompoRes.clear() ;
|
|
|
|
PocketParams myParams ;
|
|
myParams.bSmooth = false ;
|
|
myParams.dRad = 0.5 * dStep ;
|
|
myParams.bCalcFeed = false ;
|
|
myParams.dSideStep = dStep ;
|
|
myParams.bInvert = false ;
|
|
|
|
// vettore con i percorsi a ZigZag
|
|
ICRVCOMPOPOVECTOR vpCrvs ;
|
|
|
|
// ciclo su tutti i chunk della regione
|
|
for ( int c = 0 ; c < pSfr->GetChunkCount() ; ++ c) {
|
|
// prendo il Chunk
|
|
PtrOwner<ISurfFlatRegion> pSfrChunk( pSfr->CloneChunk( c)) ;
|
|
if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid())
|
|
return false ;
|
|
// calcolo i percorsi a ZigZag
|
|
if ( ! CalcZigZag( pSfrChunk, myParams, vpCrvs, true))
|
|
return false ;
|
|
|
|
if ( bRemoveOverlapLink) {
|
|
ICRVCOMPOPOVECTOR vCrvFinal ;
|
|
for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u)
|
|
vCrvFinal.emplace_back( Release( vpCrvs[u])) ;
|
|
vpCrvs.clear() ;
|
|
// sistemo le curve ottenute, aggiustando i Links
|
|
for ( int u = 0 ; u < int( vCrvFinal.size()) ; ++ u) {
|
|
ICRVCOMPOPOVECTOR vNewCrv ;
|
|
if ( ! AdjustLinkZigZagInfill( vCrvFinal[u], myParams, vNewCrv))
|
|
return false ;
|
|
for ( int uu = 0 ; uu < int( vNewCrv.size()) ; ++ uu)
|
|
vpCrvs.emplace_back( Release( vNewCrv[uu])) ;
|
|
}
|
|
}
|
|
|
|
// inserisco i percorsi da ritornare
|
|
for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) {
|
|
if ( bSmooth) {
|
|
myParams.bSmooth = true ;
|
|
ModifyCurveToSmoothed( vpCrvs[u], myParams, myParams.dRad / 16, myParams.dRad / 16, false) ;
|
|
}
|
|
vCrvCompoRes.emplace_back( Release( vpCrvs[u])) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CalcSmoothCurve( ICurveComposite* pCrv, double dRightLen, double dLeftLen, bool bAsParam)
|
|
{
|
|
// controllo dei parametri
|
|
if ( pCrv == nullptr || ! pCrv->IsValid())
|
|
return false ;
|
|
|
|
// definisco i parametri
|
|
PocketParams myPockParams ;
|
|
myPockParams.bSmooth = true ;
|
|
return ModifyCurveToSmoothed( pCrv, myPockParams, dRightLen, dLeftLen, bAsParam) ;
|
|
} |