34089648b0
- in CalcPocketing sistemata gestione di memoria che poteva dare crash.
3316 lines
128 KiB
C++
3316 lines
128 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 <array>
|
|
|
|
using namespace std ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Calcolo delle curve elementari di Pocketing
|
|
//----------------------------------------------------------------------------
|
|
|
|
enum { POCKET_ZIGZAG = 0,
|
|
POCKET_ONEWAY = 1,
|
|
POCKET_SPIRALIN = 2,
|
|
POCKET_SPIRALOUT = 3 } ;
|
|
|
|
// variabili d'appoggio ( per non passare troppi parametri tra le funzioni secondarie)
|
|
static int s_nType = 0 ;
|
|
static double s_dRad = 0 ;
|
|
static bool s_bSmooth = false ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
AdjustContourStart( ICurveComposite* pCompo)
|
|
{
|
|
// 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 ; // lunghezza massima
|
|
int nCurrInd = 0 ; // indice della curva scelta
|
|
bool bStop = false ; // flag per stoppare la ricerca della curva
|
|
|
|
// mettendo chiuse tutte le curve ( in questo caso non servono i lati aperti)
|
|
for ( int u = 0 ; u < pCompo->GetCurveCount() ; ++ u) {
|
|
pCompo->SetCurveTempProp( u, 0, 0) ;
|
|
pCompo->SetCurveTempProp( u, 0, 1) ;
|
|
}
|
|
// miglioro la curva per la ricerca
|
|
pCompo->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
|
|
// scorro tutte le curve
|
|
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 è aperta...
|
|
if ( find( vIndUsed.begin(), vIndUsed.end(), i) == vIndUsed.end() &&
|
|
pCompo->GetCurveTempProp( i, nProp_i, 0) && nProp_i == 0) {
|
|
|
|
// ricavo 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 ;
|
|
}
|
|
|
|
// ora che ho riempito il vettore di indici lo scorro e cerco di entrare in questi lati chiusi secondo l'ordine
|
|
for ( int i = 0 ; i < ( int)vInd.size() ; ++ i) {
|
|
pCompo->ChangeStartPoint( vInd[0] + 0.5) ;
|
|
// in questo caso mi fermo al primo per semplicità
|
|
break ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
ModifyCurveToSmoothed( ICurveComposite* pCrv, double dRightLen, double dLeftLen, bool bAsParam)
|
|
{
|
|
// se non richiesto non faccio nulla
|
|
if ( ! s_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, 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, ICRVCOMPOPOVECTOR& vOffIslands,
|
|
ICurveComposite* pCrvLink)
|
|
{
|
|
// se senza smusso, ritorno tratto lineare
|
|
if ( ! s_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 && 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, 0.05, 0.05, true) ;
|
|
pCrvLink->AddCurve( Release( pCompo)) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, ICRVCOMPOPOVECTOR& vOffsCL,
|
|
ICRVCOMPOPOVECTOR& vFirstOffset, 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. : s_dRad) ;
|
|
double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : s_dRad) ;
|
|
dLenE = 0 ;
|
|
|
|
// calcolo i possibili BiArchi tra le due curve
|
|
int nIter = 0 ;
|
|
|
|
// 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, 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,
|
|
ISurfFlatRegion* pSrfToCut)
|
|
{
|
|
|
|
// controllo dei parametri
|
|
if ( vFirstOffs.empty() || vCrvs.size() < vFirstOffs.size())
|
|
return false ;
|
|
pSrfToCut->Clear() ;
|
|
|
|
// 1) creo la regione esterna
|
|
PtrOwner<SurfFlatRegion> pSrfExtern( CreateBasicSurfFlatRegion()) ;
|
|
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 ;
|
|
}
|
|
}
|
|
|
|
// 2) Creo la regione svuotata dal Tool negli Offset, nei Links e sia dagli Offsets che dai Links
|
|
PtrOwner<SurfFlatRegion> pSrfTool_Offs( CreateBasicSurfFlatRegion()) ;
|
|
PtrOwner<SurfFlatRegion> pSrfTool_Links( CreateBasicSurfFlatRegion()) ;
|
|
PtrOwner<SurfFlatRegion> pSrfTool( CreateBasicSurfFlatRegion()) ;
|
|
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 = ( s_nType == POCKET_SPIRALIN ? 0 : ( int)vCrvs.size() - 1) ;
|
|
( s_nType == POCKET_SPIRALIN ? i < int( vCrvs.size()) : i >= 0) ;
|
|
( s_nType == POCKET_SPIRALIN ? ++ i : -- i)) {
|
|
|
|
// ================= LINK ================================
|
|
if ( i <= int( vLinks.size()) && ! IsNull( vLinks[i]) && vLinks[i]->IsValid()) {
|
|
|
|
// prendo il Link i-esimo come composita ( potrebbe essere tratto lineare)
|
|
PtrOwner<CurveComposite> pCompoLink_i( CreateBasicCurveComposite()) ;
|
|
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), s_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 superficie svuotata
|
|
PtrOwner<CurveComposite> pCrvOffs_i( CloneBasicCurveComposite( vCrvs[i])) ;
|
|
if ( IsNull( pCrvOffs_i))
|
|
return false ;
|
|
PtrOwner<ISurfFlatRegion> pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( Release( pCrvOffs_i) , s_dRad + 5 * EPS_SMALL, false, false)) ;
|
|
if ( ! IsNull( pSrfToolRegOffi)) {
|
|
if ( ! pSrfTool_Offs->IsValid() || pSrfTool_Offs->GetChunkCount() == 0)
|
|
pSrfTool_Offs.Set( GetBasicSurfFlatRegion( Release( pSrfToolRegOffi))) ;
|
|
else
|
|
pSrfTool_Offs->Add( *pSrfToolRegOffi) ;
|
|
}
|
|
}
|
|
|
|
// 3) Calcolo la superficie 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,
|
|
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, s_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, 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), s_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, 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, 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, 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, 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, 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, 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, pPath1) ||
|
|
! CalcBoundedSmootedLink( ptE, vtTanS, ptS, vtTanS, 0.5, VFirstOff, 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,
|
|
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 * s_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 * s_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, 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, 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, pPath1))
|
|
if ( ! CalcBoundedLink( ptS, ptE, VFirstOff, pPath1))
|
|
return false ;
|
|
if ( ! CalcBoundedSmootedLink( ptH, vtH, ptS, vtTanS, 0.5, VFirstOff, 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, 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, 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, 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)
|
|
{
|
|
|
|
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, 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, 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, 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, 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, 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, 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, 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
|
|
CalcSpiral( const ISurfFlatRegion* pSrfPock, double dRad, double dStep, bool bSmooth, int& nReg, Point3d& ptStart,
|
|
ICurveComposite* pMCrv, ICurveComposite* pRCrv)
|
|
{
|
|
|
|
// inizializzo i risultati
|
|
pMCrv->Clear() ;
|
|
pRCrv->Clear() ;
|
|
|
|
// Offset corrente
|
|
double dOffs = dRad ;
|
|
|
|
// 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) ;
|
|
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 ;
|
|
}
|
|
|
|
// alla prima iterazione svuoto la regione nReg-esima passata alla funzione CalcSpiral
|
|
if ( nIter == 0) {
|
|
PtrOwner<ISurfFlatRegion> pSrfChunknReg( pSfrOffsVR->CloneChunk( nReg)) ;
|
|
nReg = pSfrOffsVR->GetChunkCount() ; // aggiorno il nuovo valore delle regioni
|
|
if ( IsNull( pSrfChunknReg)) // se supero i chunk ottenuti
|
|
return true ;
|
|
}
|
|
|
|
// numero di Chunk attuali
|
|
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() ;
|
|
// inserisco nel vettore degli Offset
|
|
vOffs.emplace_back( Release( pCrvCompoBorder)) ;
|
|
// salvo le curve di Offset esterne della prima iterazione
|
|
if ( nIter == 0) { // salvo il bordo per i link ( non invertiti)
|
|
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 < dRad + EPS_ZERO : dOffs - dOffsPrec < 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
|
|
dOffsPrec = dOffs ;
|
|
dOffs += dStep ;
|
|
}
|
|
// se devo usare un Offset più piccolo...
|
|
else if ( ! bSmallRad)
|
|
dOffs = dOffsPrec + ( nIter == 0 ? dRad : dRad) ; // superfluo, effettivamente non ho l'offset radiale
|
|
// 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
|
|
AdjustContourStart( vOffs[0]) ;
|
|
|
|
// smusso le curve di offset ( ad eccezione della prima, se richisto)
|
|
ICRVCOMPOPOVECTOR vOffsClosedCurves( vOffs.size()) ; // vettore con tutte le curve di Offset Chiuse
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) {
|
|
if ( i != 0) {
|
|
ModifyCurveToSmoothed( vOffs[i], s_dRad / 8, s_dRad / 8, false) ;
|
|
vOffs[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, true, true) ;
|
|
}
|
|
vOffsClosedCurves[i].Set( vOffs[i]->Clone()) ;
|
|
// NB. Salvo questo vettore in quanto il BiArco di raccordo tra la curva (i)-esima e la (i+1)-esima potrebbe
|
|
// intersecare altre curve oltre queste due. ( CutCurveToConnect( ...))
|
|
}
|
|
|
|
for ( int i = 0 ; i < int( vOffsFirstCurve.size()) ; ++ i) {
|
|
if ( i != 0)
|
|
ModifyCurveToSmoothed( vOffsFirstCurve[i], s_dRad / 8, s_dRad / 8, false) ;
|
|
vOffsFirstCurve[i]->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL) ;
|
|
}
|
|
|
|
// riordino le curve cambiando il loro punto di inizio e creando poi i collegamenti
|
|
int nClosestInd = -1 ; int nFlag ;
|
|
double dDist = INFINITO ;
|
|
Point3d ptHelp ;
|
|
ICURVEPOVECTOR vLinks( vOffs.size()) ;
|
|
|
|
for ( int i = 0 ; i < int( vOffs.size()) - 1 ; ++ i) {
|
|
|
|
// prendo il punto iniziale
|
|
Point3d ptS ;
|
|
if ( ! vOffs[i]->GetStartPoint( ptS))
|
|
return false ;
|
|
|
|
// setto i default delle variabili
|
|
if ( ! DistPointCurve( ptS, *vOffs[i+1]).GetMinDistPoint( EPS_SMALL, ptHelp, nFlag))
|
|
return false ;
|
|
dDist = INFINITO ;
|
|
|
|
// scorro le curve successive
|
|
for ( int j = i + 1 ; j <= int( vOffs.size()) - 1 ; ++ j) {
|
|
|
|
// cerco il punto più vicino della curva
|
|
Point3d ptE ;
|
|
if ( ! DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptE, nFlag))
|
|
return false ;
|
|
|
|
// memorizzo l'indice della curva più vicina ( aggiornando il valore della distanza)
|
|
if ( dDist > Dist( ptS, ptE) ) {
|
|
dDist = Dist( ptS, ptE) ;
|
|
nClosestInd = j ;
|
|
ptHelp.Set( ptE.x, ptE.y, ptE.z) ;
|
|
}
|
|
}
|
|
|
|
// 1) scambio la curva i con la curva più vicina trovata ( se non è la successiva)
|
|
if ( nClosestInd != i + 1) {
|
|
PtrOwner<ICurveComposite> ptoCCHelp( Release( vOffs[i + 1])) ;
|
|
vOffs[i + 1].Set( vOffs[nClosestInd]) ;
|
|
vOffs[nClosestInd].Set( ptoCCHelp) ;
|
|
}
|
|
|
|
// 2) cambio il suo punto iniziale ( nel punto più vicino trovato)...
|
|
double dU ;
|
|
Point3d ptNE( ptHelp.x, ptHelp.y, ptHelp.z) ;
|
|
if ( ! vOffs[i + 1]->GetParamAtPoint( ptNE, dU))
|
|
return false ;
|
|
vOffs[i + 1]->ChangeStartPoint( dU) ;
|
|
|
|
// 2.1) Accorcio la curva per raccordarla con la successiva ...
|
|
if (( int)vOffs.size() > 1) { // se ho almeno un Offset
|
|
// copio le curve (i)-esime e (i+1)-esime
|
|
double dUNS, dUNE ;
|
|
PtrOwner<ICurveComposite> pCrvTest( CreateCurveComposite()) ; // possibile collegamento tra la curva i ed i+1
|
|
if ( IsNull( pCrvTest))
|
|
return false ;
|
|
PtrOwner<ICurveComposite> pOff_i0( CloneCurveComposite( vOffs[i])) ;
|
|
PtrOwner<ICurveComposite> pOff_i1( CloneCurveComposite( vOffs[i + 1])) ;
|
|
// cerco di tagliare le curve e di raccordarle ( -> ottenendo il collegamento)
|
|
// NB. se la curva di offset è la prima, non devo tagliarla, accorcio solo le successive ( se non bordi di isole) ...
|
|
if ( ! s_bSmooth || ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsClosedCurves, vOffsFirstCurve, pCrvTest, ( i == 0 ? 0 : 0.01), 0.01, 2)) {
|
|
// se non sono riuscito ad accorcirle, ritorno alla configurazione iniziale
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( IsNull( pCrvLink))
|
|
return false ;
|
|
// vettori tangenti per raccordo
|
|
Vector3d vS, vE ;
|
|
vOffs[i].Set( pOff_i0) ;
|
|
vOffs[i+1].Set( pOff_i1) ;
|
|
if ( ! vOffs[i]->GetStartDir( vS) || ! vOffs[i+1]->GetStartDir( vE))
|
|
return false ;
|
|
// calcolo il raccordo
|
|
if ( CalcBoundedSmootedLink( ptS, vS, ptNE, vE, 0.5, vOffsFirstCurve, pCrvLink))
|
|
vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento
|
|
else
|
|
return false ;
|
|
// passo alla successiva
|
|
continue ;
|
|
}
|
|
|
|
// ricavo i parametri nei punti scelti
|
|
if ( ! pCrvTest->GetStartPoint( ptS) ||
|
|
! pCrvTest->GetEndPoint( ptNE) ||
|
|
! vOffs[i]->GetParamAtPoint( ptS, dUNS) ||
|
|
! vOffs[i+1]->GetParamAtPoint( ptNE, dUNE))
|
|
return false ;
|
|
|
|
// imposto il nuovo punto inziale della curva successiva
|
|
vOffs[i+1]->ChangeStartPoint( dUNE) ;
|
|
PtrOwner<ICurveComposite> pCrvNewOffS( CloneCurveComposite( vOffs[i])) ;
|
|
if ( IsNull( pCrvNewOffS) || ! pCrvNewOffS->IsValid())
|
|
return false ;
|
|
if ( dUNS > EPS_SMALL) { // se parametro di trim sufficientemente grande
|
|
pCrvNewOffS.Set( GetCurveComposite( vOffs[i]->CopyParamRange( 0, dUNS))) ;
|
|
// sostituisco la curva i-esima con quella tagliata
|
|
vOffs[i]->Clear() ;
|
|
vOffs[i].Set( pCrvNewOffS) ;
|
|
}
|
|
// aggiorno il collegamento
|
|
vLinks[i+1].Set( pCrvTest) ;
|
|
}
|
|
}
|
|
|
|
// 3) controllo eventuali parti non svuotate...
|
|
PtrOwner<ISurfFlatRegion> pSrfToCut( CreateSurfFlatRegion()) ;
|
|
if ( IsNull( pSrfToCut))
|
|
return false ;
|
|
if ( GetUnclearedRegion( vOffsFirstCurve, vOffs, vLinks, pSrfToCut)) {
|
|
// 4) Modifico i percorsi
|
|
if ( ! RemoveExtraParts( pSrfToCut, vOffs, vOffsClosedCurves, vOffsFirstCurve, vLinks))
|
|
return false ;
|
|
}
|
|
|
|
// calcolo il percorso di ritorno
|
|
if (( int)vOffs.size() >= 2) {
|
|
pRCrv->Clear() ;
|
|
// punto inziale e finale | vettore tangente iniziale e finale
|
|
Point3d ptStart ; vOffs.back()->GetEndPoint( ptStart) ;
|
|
Point3d ptEnd ; vOffs.front()->GetStartPoint( ptEnd) ;
|
|
Vector3d vtStart ; vOffs.back()->GetEndDir( vtStart) ;
|
|
Vector3d vtEnd ; vOffs.front()->GetStartDir( vtEnd) ;
|
|
|
|
// calcolo il ritorno ( garantendo che non esca dalla svuotatura)
|
|
PtrOwner<ICurveComposite> pCrvLink( CreateCurveComposite()) ;
|
|
if ( CalcBoundedSmootedLink( ptStart, vtStart, ptEnd, vtEnd, 0.5, vOffsFirstCurve, pCrvLink)) {
|
|
pRCrv->AddCurve( Release( pCrvLink)) ;
|
|
pRCrv->MergeCurves( 10 * EPS_SMALL, 10 * EPS_ANG_SMALL, false) ;
|
|
}
|
|
else
|
|
return false ;
|
|
|
|
}
|
|
|
|
// creo il percorso di lavoro a partire dalla raccolta degli offset e dei collegamenti
|
|
for ( int i = 0 ; i < int( vOffs.size()) ; ++i) {
|
|
// se collegamento da aggiungere
|
|
if ( ! IsNull( vLinks[i])) {
|
|
// accodo nel percorso di lavorazione
|
|
if ( ! pMCrv->AddCurve( 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) ;
|
|
pRCrv->ToGlob( frPocket) ;
|
|
ptStart.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, ICurveComposite* pCrvLink)
|
|
{
|
|
// se non richiesto, esco
|
|
if ( ! s_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 * s_dRad) / 16)) / dLen1 ; // % parametro sinistro per smusso
|
|
double dTollRight = ( ( 2 * s_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, 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 * s_dRad) / 16)) / dLenI_e ; // % paramtro sinistro per smusso
|
|
dTollRight = ( ( 2 * s_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, 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, ICRVCOMPOPOVECTOR& vpCrvs, double dStep, 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) / dStep)) ;
|
|
dYStep = ( nYStep > 0 ? ( dDimY - 30 * EPS_SMALL) / nYStep : 0) ;
|
|
}
|
|
else {
|
|
nYStep = static_cast< int >( floor(( dDimY + 30 * EPS_SMALL) / dStep)) ;
|
|
dYStep = dStep ;
|
|
}
|
|
|
|
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))) ;
|
|
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) ;
|
|
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, 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) ;
|
|
|
|
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, double dRad, double dStep, bool bSmooth, 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 ;
|
|
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 ;
|
|
// calcolo il percorso di svuotatura
|
|
if ( ! CalcSpiral( pSrfChunk, dRad, dStep, bSmooth, nRegTot, ptStart, pMCrv, pRCrv))
|
|
return false ;
|
|
|
|
// se terminate le regioni, esco
|
|
if ( pMCrv->GetCurveCount() == 0)
|
|
break ; // passo al chunk originale successivo
|
|
|
|
// inserisco le curve nel vettore
|
|
vCrvCompoRes.emplace_back( Release( pMCrv)) ;
|
|
++nReg ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
AddSpiralOut( const ISurfFlatRegion* pSrfPock, double dRad, double dStep, bool bSmooth, 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 ;
|
|
int nReg = 0 ;
|
|
|
|
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 ;
|
|
|
|
if ( ! CalcSpiral( pSrfChunk, dRad, dStep, bSmooth, nRegTot, ptStart, pMCrv, pRCrv))
|
|
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 ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
AddZigZag( const ISurfFlatRegion* pSrfPock, double dRad, double dStep, double dAngle, bool bSmooth, ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
|
|
// Offset della regione per curva ZigZag
|
|
double dOffs = dRad ;
|
|
|
|
// 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(), 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, vpCrvs, dStep, false))
|
|
return false ;
|
|
// aggiorno il vettore delle curve risultati
|
|
for ( int u = 0 ; u < int( vpCrvs.size()) ; ++ u) {
|
|
vpCrvs[u]->ToGlob( frPocket) ; // riportando prima in Globale
|
|
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, double dRad, double dStep, double dAngle, bool bSmooth,
|
|
ICRVCOMPOPOVECTOR& vCrvCompoRes)
|
|
{
|
|
|
|
double dOffs = 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(), 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) / dStep)) ;
|
|
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 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
|
|
s_nType = nType ;
|
|
s_dRad = dRad ;
|
|
s_bSmooth = bSmooth ;
|
|
|
|
// calcolo delle curve elementari della superficie
|
|
switch ( nType) {
|
|
case POCKET_ZIGZAG:
|
|
if ( ! AddZigZag( pSfr, dRad, dStep, dAngle, bSmooth, vCrvCompoRes))
|
|
return false ;
|
|
break ;
|
|
case POCKET_ONEWAY:
|
|
if ( ! AddOneWay( pSfr, dRad, dStep, dAngle, bSmooth, vCrvCompoRes))
|
|
return false ;
|
|
break ;
|
|
case POCKET_SPIRALIN:
|
|
if ( ! AddSpiralIn( pSfr, dRad, dStep, bSmooth, vCrvCompoRes))
|
|
return false ;
|
|
break ;
|
|
case POCKET_SPIRALOUT:
|
|
if ( ! AddSpiralOut( pSfr, dRad, dStep, bSmooth, 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,
|
|
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( - s_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, 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, 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() ;
|
|
s_bSmooth = false ;
|
|
s_dRad = dStep / 2 ;
|
|
|
|
// 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, vpCrvs, dStep, 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], 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) {
|
|
s_bSmooth = true ;
|
|
ModifyCurveToSmoothed( vpCrvs[u], s_dRad / 16, s_dRad / 16, false) ;
|
|
}
|
|
vCrvCompoRes.emplace_back( Release( vpCrvs[u])) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|