Files
EgtGeomKernel/CalcPocketing.cpp
T
Riccardo Elitropi 5841c9cae0 EgtGeomKernel :
- migliorie a CalcPocketing.
2024-06-14 16:46:56 +02:00

6817 lines
275 KiB
C++

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