6646aee01c
- estratta funzione per trovare il punto corrispondente sulla seconda curva di una coppia di curve da sincronizzare. - pulizia codice.
5398 lines
226 KiB
C++
5398 lines
226 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2025-2025
|
|
//----------------------------------------------------------------------------
|
|
// File : Trimming.cpp Data : 16.11.23 Versione : 2.7j1
|
|
// Contenuto : Dichiarazione delle funzioni per calcolare una superficie di Bezier
|
|
// Ruled mediante Estrazione di Edge e Selezione di Facce/Superfici
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 16.11.23 RE Creazione modulo.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "BiArcs.h"
|
|
#include "GeoConst.h"
|
|
#include "CurveLine.h"
|
|
#include "CurveArc.h"
|
|
#include "CurveBezier.h"
|
|
#include "CurveComposite.h"
|
|
#include "SurfTriMesh.h"
|
|
#include "SurfBezier.h"
|
|
#include "/EgtDev/Include/EGkTrimming.h"
|
|
#include "/EgtDev/Include/EGkCurveAux.h"
|
|
#include "/EgtDev/Include/EGkChainCurves.h"
|
|
#include "/EgtDev/Include/EGkDistPointLine.h"
|
|
#include "/EgtDev/Include/EGkDistPointCurve.h"
|
|
#include "/EgtDev/Include/EGkDistPointSurfTm.h"
|
|
#include "/EgtDev/Include/EGkDistPointSurfBz.h"
|
|
#include "/EgtDev/Include/EGkDistPointTria.h"
|
|
#include "/EgtDev/Include/EGkDistLineLine.h"
|
|
#include "/EgtDev/Include/EGkSbzFromCurves.h"
|
|
#include "/EgtDev/Include/EGkStmFromTriangleSoup.h"
|
|
#include "/EgtDev/Include/EGkIntersLineBox.h"
|
|
#include "/EgtDev/Include/EGkIntersCurvePlane.h"
|
|
#include "/EgtDev/Include/EGkSurfTriMeshAux.h"
|
|
#include "/EgtDev/Include/EGkRotationMinimizingFrame.h"
|
|
#include "/EgtDev/Include/EgtNumUtils.h"
|
|
#include <thread>
|
|
#include <future>
|
|
#include <numeric>
|
|
|
|
// -------------------------- Debug --------------------------------------------
|
|
#define DEBUG_BASIC_BORDERS 0
|
|
#define DEBUG_CHAIN_CURVES 0
|
|
#define DEBUG_ANG_APPROX 0
|
|
#define DEBUG_BEZIER_INTERP 0
|
|
#define DEBUG_FACE_SEARCH 0
|
|
#define DEBUG_FACE_SEARCH_TRIA_MODIF 0
|
|
#define DEBUG_BRK_POINTS 0
|
|
#define DEBUG_BRK_THICK 0
|
|
#define DEBUG_BRK 0
|
|
#define DEBUG_BORDERS_BY_NORMALS 0
|
|
#define DEBUG_SYNC_POINTS 0
|
|
#define DEBUG_SYNC_INTERPOLATION 0
|
|
#define DEBUG_BEZIER_RULED 0
|
|
#define DEBUG_CURVATURE 0
|
|
#define DEBUG_SIMPLE_PATCHES 0
|
|
#define DEBUG_SURF_PATCHES 0
|
|
#define DEBUG_RAW_EDGES 0
|
|
#define DEBUG_EDGES 0
|
|
#define DEBUG_SHAPE_STM 0
|
|
#define DEBUG_HOLES 0
|
|
#define DEBUG_SMOOTH_CURVATURE 0
|
|
#if DEBUG_BASIC_BORDERS || DEBUG_CHAIN_CURVES || DEBUG_ANG_APPROX || DEBUG_BEZIER_INTERP || \
|
|
DEBUG_FACE_SEARCH || DEBUG_FACE_SEARCH_TRIA_MODIF || DEBUG_BRK_POINTS || DEBUG_BRK_THICK || \
|
|
DEBUG_BRK || DEBUG_BORDERS_BY_NORMALS || DEBUG_SYNC_POINTS || DEBUG_SYNC_INTERPOLATION || \
|
|
DEBUG_BEZIER_RULED || DEBUG_CURVATURE || DEBUG_SIMPLE_PATCHES || DEBUG_SURF_PATCHES || \
|
|
DEBUG_RAW_EDGES || DEBUG_EDGES || DEBUG_SHAPE_STM || DEBUG_HOLES || DEBUG_SMOOTH_CURVATURE
|
|
#include "CurveLine.h"
|
|
#include "/EgtDev/Include/EGkGeoObjSave.h"
|
|
#include "/EgtDev/Include/EgtPerfCounter.h"
|
|
#include "/EgtDev/Include/EGnStringUtils.h"
|
|
#include "/EgtDev/Include/EGkGeoPoint3d.h"
|
|
#include "/EgtDev/Include/EGkGeoVector3d.h"
|
|
std::vector<IGeoObj*> VT ;
|
|
std::vector<Color> VC ;
|
|
std::vector<IGeoObj*> VT_Glob ;
|
|
std::vector<Color> VC_Glob ;
|
|
std::string sFileName = "C:\\Temp\\Debug.nge" ;
|
|
#endif
|
|
|
|
using namespace std ;
|
|
|
|
// Vettori e Matrici di Curve Composite
|
|
typedef vector<CurveComposite> COMPOVECTOR ;
|
|
typedef vector<COMPOVECTOR> COMPOMATRIX ;
|
|
|
|
// Strutta per Patch Semplici, Bordi e Insieme di Bordi
|
|
struct SimplePatch {
|
|
Point3d ptStart ;
|
|
Point3d ptEnd ;
|
|
bool bErase ;
|
|
int nBorder ;
|
|
SimplePatch()
|
|
: ptStart( P_INVALID), ptEnd( P_INVALID), bErase( true), nBorder( 0) {} ;
|
|
SimplePatch( const Point3d& ptS, const Point3d& ptE, int nB, bool bDelete)
|
|
: ptStart( ptS), ptEnd( ptE), bErase( bDelete), nBorder( nB) {} ;
|
|
} ;
|
|
typedef vector<SimplePatch> SimpleBorder ;
|
|
typedef vector<SimpleBorder> SimpleBorderVector ;
|
|
typedef vector<SimpleBorderVector> SimpleBorderMatrix ;
|
|
|
|
// Struttura per superficie con relative Patch
|
|
typedef vector<BOXVECTOR> BOXMATRIX ;
|
|
struct SurfPatches {
|
|
const ISurf* pSurf ;
|
|
BBox3d BoxSurf ;
|
|
SimpleBorderVector vBorderPatches ;
|
|
COMPOVECTOR vCompoBorders ;
|
|
COMPOMATRIX matCompoPatches ;
|
|
BOXMATRIX matBox3dPatches ;
|
|
vector<set<double>> vSetPatchPar ;
|
|
} ;
|
|
typedef vector<SurfPatches> SURFPATCHESVECTOR ;
|
|
|
|
// Classificazione tipologia di triangoli
|
|
enum TriaClass { OK = 0, SMALL = 1, CAP = 2, HAT = 3, FLIPPED = 4} ;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//--------------------------- Approssimazione ---------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Funzione per spezzare una PolyLine in un insieme di Patches, ovvero un insieme di
|
|
// PolyLine dove per ogni punto si discosta da due successivi per non più della tolleranza
|
|
// angolare passata
|
|
static bool
|
|
GetPointSetByAngTol( const PolyLine& PL, double dAngTol, POLYLINEVECTOR& vPL)
|
|
{
|
|
// Se PL chiusa, deve contenere almeno 3 punti
|
|
if ( PL.IsClosed() && PL.GetPointNbr() < 3)
|
|
return false ;
|
|
// Se PL aperta, deve contenere almeno 2 punti
|
|
if ( ! PL.IsClosed() && PL.GetPointNbr() < 2)
|
|
return false ;
|
|
|
|
// Se PL aperta con solo due punti, allora non devo fare nulla
|
|
if ( ! PL.IsClosed() && PL.GetPointNbr() == 2) {
|
|
vPL.emplace_back( PL) ;
|
|
return true ;
|
|
}
|
|
|
|
// Recupero l'insieme di punti associati alla PolyLine
|
|
PNTVECTOR vPoints ; vPoints.reserve( PL.GetPointNbr()) ;
|
|
Point3d ptCurr = P_INVALID ;
|
|
bool bFound = PL.GetFirstPoint( ptCurr) ;
|
|
while ( bFound) {
|
|
vPoints.emplace_back( ptCurr) ;
|
|
bFound = PL.GetNextPoint( ptCurr) ;
|
|
}
|
|
#if DEBUG_ANG_APPROX
|
|
VT.clear() ; VC.clear() ;
|
|
Color _col = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ;
|
|
for ( auto _p : vPoints) {
|
|
IGeoPoint3d* _pt = CreateGeoPoint3d() ;
|
|
_pt->Set( _p) ;
|
|
VT.emplace_back( _pt) ;
|
|
VC.emplace_back( _col) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\RawPoints.nge") ;
|
|
#endif
|
|
|
|
// Sin della tolleranza angolare massima
|
|
double dCosTol = cos( dAngTol * DEGTORAD) ;
|
|
|
|
// Scorro i punti controllando le loro deviazioni angolari
|
|
PNTMATRIX vvPoints ;
|
|
Vector3d vtTanCurr = V_INVALID, vtTanNext = V_INVALID ;
|
|
bool bNewCurve = true ;
|
|
int nPoints = ssize( vPoints) ;
|
|
for ( int nP = 0 ; nP < nPoints - 2 ; ++ nP) {
|
|
// Recupero il punto corrente e i due punti successivi
|
|
Point3d& ptCurr = vPoints[nP] ;
|
|
Point3d& ptNext = vPoints[nP + 1] ;
|
|
Point3d& ptNextNext = vPoints[nP + 2] ;
|
|
// Recupero i versori tangenti definiti dai tre punti ( ptCurr|ptNext e ptNext|ptNextNext)
|
|
if ( nP == 0) {
|
|
vtTanCurr = ptNext - ptCurr ;
|
|
vtTanCurr.Normalize() ;
|
|
}
|
|
else
|
|
vtTanCurr = vtTanNext ;
|
|
vtTanNext = ptNextNext - ptNext ; vtTanNext.Normalize() ;
|
|
// Calcolo il Coseno tra i due versori
|
|
double dCos = vtTanCurr * vtTanNext ;
|
|
// Se dentro alla tolleranza, allora i punti apparterranno alla stessa curva
|
|
if ( dCos > dCosTol) {
|
|
// Se devo definire una curva nuova
|
|
if ( bNewCurve) {
|
|
vvPoints.emplace_back( PNTVECTOR()) ;
|
|
vvPoints.back().emplace_back( ptCurr) ;
|
|
vvPoints.back().emplace_back( ptNext) ;
|
|
bNewCurve = false ;
|
|
}
|
|
// Aggiungo il punto
|
|
vvPoints.back().emplace_back( ptNextNext) ;
|
|
}
|
|
// Se tratto al di fuori della tolleranza, devo definire una nuova curva
|
|
else {
|
|
// Se curva nuova, allora è solamente tratto lineare
|
|
if ( bNewCurve) {
|
|
vvPoints.emplace_back( PNTVECTOR()) ;
|
|
vvPoints.back().emplace_back( ptCurr) ;
|
|
vvPoints.back().emplace_back( ptNext) ;
|
|
}
|
|
bNewCurve = true ;
|
|
}
|
|
}
|
|
// Se PolyLine originale chiusa
|
|
if ( PL.IsClosed()) {
|
|
// Se ho più tratti, potrei riuniore il primo con l'ultimo
|
|
if ( ssize( vvPoints) > 1) {
|
|
Point3d& ptCurr = vPoints[ssize( vPoints) - 2] ;
|
|
Point3d& ptNext = vPoints[0] ;
|
|
Point3d& ptNextNext = vPoints[1] ;
|
|
vtTanCurr = ptNext - ptCurr ; vtTanCurr.Normalize() ;
|
|
vtTanNext = ptNextNext - ptNext ; vtTanNext.Normalize() ;
|
|
double dCos = vtTanCurr * vtTanNext ;
|
|
// Se dentro alla tolleranza
|
|
if ( dCos > dCosTol) {
|
|
// Recupero la tangenza del tratto finale dell'ultimo tratto
|
|
Point3d& ptLast = vvPoints.back()[ssize( vvPoints.back()) - 1] ;
|
|
Point3d& ptLastLast = vvPoints.back()[ssize( vvPoints.back()) - 2] ;
|
|
Vector3d vtTanLast = ptLast - ptLastLast ; vtTanLast.Normalize() ;
|
|
dCos = vtTanLast * vtTanCurr ;
|
|
// Se nella tolleranza allora posso allacciare il primo e l'ultimo tratto
|
|
if ( dCos > dCosTol) {
|
|
for ( int i = bNewCurve ? 1 : 0 ; i < ssize( vvPoints[0]) ; ++ i)
|
|
vvPoints.back().emplace_back( vvPoints[0][i]) ;
|
|
vvPoints.erase( vvPoints.begin()) ;
|
|
}
|
|
// Altrimenti sposto il punto d'inizio in ptCurr del primo tratto
|
|
else {
|
|
PNTVECTOR vPointsFirst ; vPointsFirst.reserve( ssize( vvPoints.front()) + 1) ;
|
|
vPointsFirst.emplace_back( ptCurr) ;
|
|
for ( const Point3d& pt : vvPoints.front())
|
|
vPointsFirst.emplace_back( pt) ;
|
|
vvPoints.front() = vPointsFirst ;
|
|
}
|
|
}
|
|
// Se fuori dalla tolleranza, e l'ultimo punto non è già stato aggiunto
|
|
else if ( bNewCurve) {
|
|
// tratto lineare finale
|
|
vvPoints.emplace_back( PNTVECTOR{}) ;
|
|
vvPoints.back().emplace_back( ptCurr) ;
|
|
vvPoints.back().emplace_back( ptNext) ;
|
|
}
|
|
}
|
|
}
|
|
// Se invece PolyLine Aperta
|
|
else {
|
|
// Se devo creare una curva nuova, aggiungo il tratto finale
|
|
if ( bNewCurve) {
|
|
vvPoints.emplace_back( PNTVECTOR{}) ;
|
|
vvPoints.back().emplace_back( vPoints[ssize( vPoints) - 2]) ;
|
|
vvPoints.back().emplace_back( vPoints[ssize( vPoints) - 1]) ;
|
|
}
|
|
}
|
|
#if DEBUG_ANG_APPROX
|
|
VT.clear() ; VC.clear() ;
|
|
for ( auto _v : vvPoints) {
|
|
Color _col = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ;
|
|
for ( auto _p : _v) {
|
|
IGeoPoint3d* _pt = CreateGeoPoint3d() ;
|
|
_pt->Set( _p) ;
|
|
VT.emplace_back( _pt) ;
|
|
VC.emplace_back( _col) ;
|
|
}
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\GroupPoints.nge") ;
|
|
#endif
|
|
|
|
// Converto i punti ricavati in PolyLine risultati
|
|
for ( int i = 0 ; i < ssize( vvPoints) ; ++ i) {
|
|
vPL.emplace_back( PolyLine()) ;
|
|
double dPar = -1. ;
|
|
for ( int j = 0 ; j < ssize( vvPoints[i]) ; ++ j) {
|
|
vPL.back().AddUPoint( ++ dPar, vvPoints[i][j]) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
////-----------------------------------------------------------------------------
|
|
//// Funzione che approssima la curva di bordo per la costruzione della Bezier Ruled mediante
|
|
//// Patches di curve di Bezier
|
|
//static bool
|
|
//ApproxBorder( ICurveComposite* pCrvCompo, double dLinTol, double dAngTol, double dAngTolSplit)
|
|
//{
|
|
// // N.B.:in futuro bisognerebbe fare l'approssimazione direttamente con le bezier.
|
|
//
|
|
// // Controllo dei parametri
|
|
// if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
// return false ;
|
|
//
|
|
// // splitto la curva considerando la tolleranza angolare
|
|
// ICRVCOMPOPOVECTOR vCC ;
|
|
// SplitCurveCompoByAngTol( pCrvCompo, dAngTolSplit, vCC) ;
|
|
// #if DEBUG_BEZIER_INTERP
|
|
// VT.clear() ;
|
|
// for( int i = 0 ; i < ssize(vCC) ; ++i)
|
|
// VT.push_back( vCC[i]->Clone()) ;
|
|
// SaveGeoObj( VT, "D:\\Temp\\trimming\\AngBorderApprox.nge") ;
|
|
// VT.clear() ;
|
|
// #endif
|
|
//
|
|
// pCrvCompo->Clear() ;
|
|
//
|
|
// // Ogni PolyLine ricavata viene approssimata con un tratto di Bezier
|
|
// const double MAXLEN = 1.5 ;
|
|
// for ( ICurveComposite* pCC : vCC) {
|
|
// // Se meno di due curve, non la considero ( non dovrebbe mai capitare )
|
|
// if ( pCC->GetCurveCount() < 2)
|
|
// continue ;
|
|
// PolyArc PA ;
|
|
// if ( ! pCC->ApproxWithArcs( dLinTol, dAngTol, PA))
|
|
// return false ;
|
|
// CurveComposite CrvTemp ;
|
|
// if ( ! CrvTemp.FromPolyArc( PA) || ! CrvTemp.MergeCurves( dLinTol, dAngTol))
|
|
// return false ;
|
|
// #if DEBUG_BEZIER_INTERP
|
|
// VT.emplace_back( CrvTemp->Clone()) ;
|
|
// #endif
|
|
// // Converto in Bezier
|
|
// PtrOwner<ICurve> pCrvBz( CurveToBezierCurve( &CrvTemp)) ;
|
|
// if ( IsNull( pCrvBz) || ! pCrvBz->IsValid()) {
|
|
// LOG_ERROR( GetEGkLogger(), "Error : converrting curve to bezier") ;
|
|
// return false ;
|
|
// }
|
|
// // Aggiungo il tratto approssimato alla curva finale complessiva
|
|
// if ( ! pCrvCompo->AddCurve( Release( pCrvBz)))
|
|
// return false ;
|
|
// }
|
|
// #if DEBUG_BEZIER_INTERP
|
|
// SaveGeoObj( VT, VC, "D:\\Temp\\trimming\\bezier_edge.nge") ;
|
|
// #endif
|
|
//
|
|
// return ( pCrvCompo->IsValid()) ;
|
|
//}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Funzione che approssima la curva di bordo per la costruzione della Bezier Ruled mediante
|
|
// Patches di curve di Bezier
|
|
static bool
|
|
ApproxBorder( CurveComposite& CrvCompo, double dLinTol, double dAngTol)
|
|
{
|
|
// Controllo dei parametri
|
|
if ( ! CrvCompo.IsValid())
|
|
return false ;
|
|
|
|
#if DEBUG_BEZIER_INTERP
|
|
VT.emplace_back( CrvCompo.Clone()) ;
|
|
VC.emplace_back( RED) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\RawEdge.nge") ;
|
|
#endif
|
|
|
|
// Dalla Curva Grezza, recupero i suoi punti grezzi e le sue Patch
|
|
PolyLine PL ;
|
|
if ( ! CrvCompo.ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_SPECIAL, PL))
|
|
return false ;
|
|
POLYLINEVECTOR vPL ;
|
|
if ( ! GetPointSetByAngTol( PL, dAngTol, vPL))
|
|
return false ;
|
|
|
|
#if DEBUG_BEZIER_INTERP
|
|
vector<Color> vColor ; vColor.reserve( vPL.size()) ;
|
|
for ( const PolyLine& PL : vPL) {
|
|
CurveComposite CCTemp ; CCTemp.FromPolyLine( PL) ;
|
|
VT.emplace_back( CCTemp.Clone()) ;
|
|
vColor.emplace_back( Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.)) ;
|
|
VC.emplace_back( vColor.back()) ;
|
|
}
|
|
int nIndCol = -1 ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\RawEdge.nge") ;
|
|
#endif
|
|
|
|
// NB. L'approssimazione avviene prima mediante Archi e Rette e successivamente mediante
|
|
// Curve di Bezier. Entrambe vengono eseguite con un tolleranza lineare dimezzata in modo
|
|
// da rispettare la tolleranza lineare per la curva complessiva.
|
|
double dHalfLinTol = max( EPS_SMALL, dLinTol / 2.) ;
|
|
|
|
// Approssimo le Patch mediante Archi
|
|
CurveComposite CCApprox ;
|
|
for ( const PolyLine& PL : vPL) {
|
|
// Recupero la Patch Grezza
|
|
CurveComposite CCPatch ; CCPatch.FromPolyLine( PL) ;
|
|
// Creo la Patch Approssimata con Archi
|
|
PolyArc PA ;
|
|
CCPatch.ApproxWithArcs( dHalfLinTol, dAngTol, PA) ;
|
|
CurveComposite CCPatchApprox ; CCPatchApprox.FromPolyArc( PA) ;
|
|
// Rimozione delle Imperfezioni ( metà della tolleranza lineare)
|
|
CCPatchApprox.RemoveSmallDefects( dHalfLinTol / 2., dAngTol) ;
|
|
// Merge complessivo ( metà della tolleranza lineare)
|
|
CCPatchApprox.MergeCurves( dHalfLinTol / 2., dAngTol) ;
|
|
#if DEBUG_BEZIER_INTERP
|
|
VT.emplace_back( CCPatchApprox.Clone()) ;
|
|
VC.emplace_back( vColor[ ++ nIndCol]) ;
|
|
#endif
|
|
// Approssimo tale curva con una Bezier
|
|
PtrOwner<ICurve> pCrvPatchBZ( ApproxCurveWithBezier( &CCPatchApprox, dHalfLinTol)) ;
|
|
if ( IsNull( pCrvPatchBZ) || ! pCrvPatchBZ->IsValid()) {
|
|
LOG_ERROR( GetEGkLogger(), "Error : Interpolating points to bezier curve failed") ;
|
|
return false ;
|
|
}
|
|
#if DEBUG_BEZIER_INTERP
|
|
VT.emplace_back( pCrvPatchBZ->Clone()) ;
|
|
VC.emplace_back( vColor[nIndCol]) ;
|
|
#endif
|
|
// Aggiungo alla Curva complessiva Approssimata
|
|
if ( ! CCApprox.AddCurve( Release( pCrvPatchBZ)))
|
|
return false ;
|
|
}
|
|
#if DEBUG_BEZIER_INTERP
|
|
VT.emplace_back( CCApprox.Clone()) ;
|
|
VC.emplace_back( RED) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\RawEdge.nge") ;
|
|
#endif
|
|
|
|
if ( CCApprox.IsValid())
|
|
CrvCompo.CopyFrom( &CCApprox) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-------------------------------- Analisi Bordi ------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per controllare e sistemare le curve di bordo di una rigata Bezier
|
|
static bool
|
|
ManageRuledBorders( ICurveComposite* pCompoEdge1, ICurveComposite* pCompoEdge2)
|
|
{
|
|
// Se curve non valide, errore
|
|
if ( pCompoEdge1 == nullptr || ! pCompoEdge1->IsValid() ||
|
|
pCompoEdge2 == nullptr || ! pCompoEdge2->IsValid())
|
|
return false ;
|
|
|
|
// Le due curve devono essere entrambe aperte o entrambe chiuse
|
|
bool bAllClosed = ( pCompoEdge1->IsClosed() && pCompoEdge2->IsClosed()) ;
|
|
bool bAllOpen = ( ! pCompoEdge1->IsClosed() && ! pCompoEdge2->IsClosed()) ;
|
|
if ( ! bAllClosed && ! bAllOpen) {
|
|
LOG_ERROR( GetEGkLogger(), "Error in Trimming : Mixed Edges") ;
|
|
return false ;
|
|
}
|
|
|
|
// Eseguo una copia delle curve composite per non modificare i parametri
|
|
PtrOwner<ICurve> pCrvEdge1CL( pCompoEdge1->Clone()) ;
|
|
PtrOwner<ICurve> pCrvEdge2CL( pCompoEdge2->Clone()) ;
|
|
if ( IsNull( pCrvEdge1CL) || IsNull( pCrvEdge2CL) ||
|
|
! pCrvEdge1CL->IsValid() || ! pCrvEdge2CL->IsValid())
|
|
return false ;
|
|
PtrOwner<ICurveComposite> pMyCompoEdge1( ConvertCurveToComposite( Release( pCrvEdge1CL))) ;
|
|
PtrOwner<ICurveComposite> pMyCompoEdge2( ConvertCurveToComposite( Release( pCrvEdge2CL))) ;
|
|
if ( IsNull( pMyCompoEdge1) || IsNull( pMyCompoEdge2) ||
|
|
! pMyCompoEdge1->IsValid() || ! pMyCompoEdge2->IsValid())
|
|
return false ;
|
|
|
|
// Modifico il punto iniziale o oriento le curve allo stesso modo
|
|
// --- Se entrambe aperte
|
|
if ( bAllOpen) {
|
|
// Recupero il punto iniziale della prima curva
|
|
Point3d ptStart1 ; pMyCompoEdge1->GetStartPoint( ptStart1) ;
|
|
// Recupero gli estremi della seconda curva
|
|
Point3d ptStart2 ; pMyCompoEdge2->GetStartPoint( ptStart2) ;
|
|
Point3d ptEnd2 ; pMyCompoEdge2->GetEndPoint( ptEnd2) ;
|
|
// Se necessario inverto la seconda curva
|
|
if ( SqDist( ptStart1, ptEnd2) < SqDist( ptStart1, ptStart2))
|
|
pMyCompoEdge2->Invert() ;
|
|
}
|
|
// --- Se entrambe chiuse
|
|
else {
|
|
// Modifico il punto iniziale della prima curva a metà del suo tratto più lungo
|
|
double dMaxLen = - INFINITO ;
|
|
int nCrvInd = 0 ;
|
|
for ( int nCrv = 0 ; nCrv < pMyCompoEdge1->GetCurveCount() ; ++ nCrv) {
|
|
const ICurve* pCurve = pMyCompoEdge1->GetCurve( nCrv) ;
|
|
if ( pCurve == nullptr || ! pCurve->IsValid())
|
|
return false ;
|
|
double dLen = 0. ;
|
|
pCurve->GetLength( dLen) ;
|
|
if ( dLen > dMaxLen) {
|
|
dMaxLen = dLen ;
|
|
nCrvInd = nCrv ;
|
|
}
|
|
}
|
|
pMyCompoEdge1->ChangeStartPoint( nCrvInd + 0.5) ;
|
|
// Recupero il punto iniziale della prima curva
|
|
Point3d ptStart1 ; pMyCompoEdge1->GetStartPoint( ptStart1) ;
|
|
// Recupero il punto più vicino sulla seconda curva
|
|
double dUStart = 0. ;
|
|
int nFlag = 0 ;
|
|
DistPointCurve( ptStart1, *pMyCompoEdge2).GetParamAtMinDistPoint( 0., dUStart, nFlag) ;
|
|
pMyCompoEdge2->ChangeStartPoint( dUStart) ;
|
|
// Verifico che le tangenti iniziali siano concordi
|
|
Vector3d vtStart1 ; pMyCompoEdge1->GetStartDir( vtStart1) ;
|
|
Vector3d vtStart2 ; pMyCompoEdge2->GetStartDir( vtStart2) ;
|
|
if ( vtStart1 * vtStart2 < - EPS_SMALL)
|
|
pMyCompoEdge2->Invert() ;
|
|
}
|
|
|
|
// Sostiuisco i risultati
|
|
pCompoEdge1->Clear() ; pCompoEdge1->CopyFrom( pMyCompoEdge1) ;
|
|
pCompoEdge2->Clear() ; pCompoEdge2->CopyFrom( pMyCompoEdge2) ;
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per determinare la normale di una superficie generica in un punto
|
|
static bool
|
|
GetNormalAtPoint( const ISurf* pSurf, const Point3d& pt, Vector3d& vtN)
|
|
{
|
|
// Verifico che le due superfici siano valide
|
|
if ( pSurf == nullptr || ! pSurf->IsValid())
|
|
return false ;
|
|
const double dMinLen = 20. * EPS_SMALL ;
|
|
const double dMinArea = dMinLen * dMinLen ;
|
|
|
|
// Se è Trimesh, recupero la normale del triangolo più vicino al punto scelto
|
|
if ( pSurf->GetType() == SRF_TRIMESH) {
|
|
const ISurfTriMesh* pStm = GetSurfTriMesh( pSurf) ;
|
|
if ( pStm == nullptr || ! pStm->IsValid())
|
|
return false ;
|
|
int nTria = 0 ;
|
|
DistPointSurfTm( pt, *pStm).GetMinDistTriaIndex( nTria) ;
|
|
Triangle3d Tria ;
|
|
if ( ! pStm->GetTriangle( nTria, Tria))
|
|
return false ;
|
|
// se il triangolo è rappresentativo, recupero la sua normale
|
|
if ( Tria.GetArea() > dMinArea)
|
|
vtN = Tria.GetN() ;
|
|
else {
|
|
BBox3d BBoxPt ;
|
|
BBoxPt.Add( pt) ;
|
|
BBoxPt.Expand( dMinLen + 2. * EPS_SMALL) ;
|
|
INTVECTOR vT ;
|
|
pStm->GetAllTriaOverlapBox( BBoxPt, vT) ;
|
|
int nBestTria = -1 ;
|
|
double dMaxArea = dMinArea ;
|
|
for ( const int& nT : vT) {
|
|
if ( pStm->GetTriangle( nT, Tria)) {
|
|
double dCurrArea = Tria.GetArea() ;
|
|
if ( dCurrArea > dMaxArea) {
|
|
dMaxArea = dCurrArea ;
|
|
nBestTria = nT ;
|
|
}
|
|
}
|
|
}
|
|
if ( nBestTria == -1) // recupero lo stesso il più vicino ( calcolato prima)
|
|
nBestTria = nTria ;
|
|
if ( ! pStm->GetTriangle( nBestTria, Tria))
|
|
return false ;
|
|
vtN = Tria.GetN() ;
|
|
}
|
|
}
|
|
// Se è Bezier, la normale è calcolata automaticamente
|
|
else if ( pSurf->GetType() == SRF_BEZIER) {
|
|
const ISurfBezier* pSbz = GetSurfBezier( pSurf) ;
|
|
if ( pSbz == nullptr || ! pSbz->IsValid())
|
|
return false ;
|
|
if ( ! DistPointSurfBz( pt, *pSbz).GetNorm( vtN))
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per ottenere le Patch dei bordi di una superficie
|
|
static bool
|
|
CalcPatches( const POLYLINEVECTOR& vPLBorders, double dAngTol, POLYLINEMATRIX& matPLPatches)
|
|
{
|
|
// Ogni PolyLine di bordo viene vista come un insieme di Patches
|
|
matPLPatches.reserve( vPLBorders.size()) ;
|
|
for ( const PolyLine& PL : vPLBorders) {
|
|
// Approssimo mediante tolleranza angolare ricavando l'insieme delle Patches
|
|
matPLPatches.emplace_back( POLYLINEVECTOR{}) ;
|
|
if ( ! GetPointSetByAngTol( PL, dAngTol, matPLPatches.back()))
|
|
return false ;
|
|
// Trasferisco le TempProps
|
|
for ( PolyLine newPL : matPLPatches.back())
|
|
newPL.SetTempProp( 0, PL.GetTempProp( 0)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Funzione per scremare le curve di Bordo tenendo solo i tratti utili concatenabili
|
|
static bool
|
|
ManagePatches( const POLYLINEVECTOR& vPLBorders, double dLinTol, double dAngTol,
|
|
ICRVCOMPOPOVECTOR& vCompoResult)
|
|
{
|
|
// Se non ho Polyline non faccio nulla
|
|
if ( vPLBorders.empty())
|
|
return true ;
|
|
vCompoResult.clear() ;
|
|
|
|
// Approssimo le curve
|
|
// NB. Per Patch si intende un tratto di PolyLine dove la tangenza di ogni punto con i suoi 2 successivi
|
|
// rimane all'interno della tolleranza angolare definita
|
|
// Ogni curva di bordo viene quindi vista come un insieme di Patches
|
|
POLYLINEMATRIX matPLPatches ;
|
|
if ( ! CalcPatches( vPLBorders, dAngTol, matPLPatches))
|
|
return false ;
|
|
// Tengo anche la curva composita rispettiva per ogni Patch ( serve più avanti) [*]
|
|
COMPOMATRIX matCompoPatches ; matCompoPatches.reserve( vPLBorders.size()) ;
|
|
for ( const POLYLINEVECTOR& PLBorder : matPLPatches) {
|
|
matCompoPatches.emplace_back( COMPOVECTOR{}) ;
|
|
for ( const PolyLine& PLPatch : PLBorder) {
|
|
matCompoPatches.back().emplace_back( CurveComposite()) ;
|
|
matCompoPatches.back().back().FromPolyLine( PLPatch) ;
|
|
}
|
|
}
|
|
// Dalle PolyLine ricevute tengo il vettore di Curve Composite. Il punto iniziale coicide con
|
|
// Quello della prima Patch trovata
|
|
COMPOVECTOR vCompoBorders ; vCompoBorders.reserve( vPLBorders.size()) ;
|
|
for ( int i = 0 ; i < ssize( vPLBorders) ; ++ i) {
|
|
vCompoBorders.emplace_back( CurveComposite()) ;
|
|
vCompoBorders.back().FromPolyLine( vPLBorders[i]) ;
|
|
if ( ! vCompoBorders.back().IsValid())
|
|
return false ;
|
|
const PolyLine& PLFirstPatch = matPLPatches[i].front() ;
|
|
Point3d ptStart ;
|
|
if ( ! PLFirstPatch.GetFirstPoint( ptStart))
|
|
return false ;
|
|
double dUS = 0. ;
|
|
vCompoBorders.back().GetParamAtPoint( ptStart, dUS) ;
|
|
vCompoBorders.back().ChangeStartPoint( dUS) ;
|
|
}
|
|
|
|
// Ogni curva Patch diventa un insieme di tratti attivi definiti dagli estremi calcolati
|
|
// NB. In questo modo si salvano solo gli estremi della curva Patch, mentre la sua geometria
|
|
// è rimasta memorizzata in precedenza [*]
|
|
// Ogni bordo viene quindi visto come un insieme di Patch semplici ( per adesso tutte attive)
|
|
// Le Patch più corte della tolleranza vengono considerate invalide
|
|
SimpleBorderVector vSimpleBorder ; vSimpleBorder.reserve( matPLPatches.size()) ;
|
|
vector<set<double>> matPatchPar ; matPatchPar.reserve( matPLPatches.size()) ;
|
|
for ( int i = 0 ; i < ssize( matPLPatches) ; ++ i) {
|
|
vSimpleBorder.emplace_back( SimpleBorder()) ;
|
|
matPatchPar.emplace_back( set<double>()) ;
|
|
for ( int j = 0 ; j < ssize( matPLPatches[i]) ; ++ j) {
|
|
Point3d ptS ; matPLPatches[i][j].GetFirstPoint( ptS) ;
|
|
Point3d ptE ; matPLPatches[i][j].GetLastPoint( ptE) ;
|
|
vSimpleBorder.back().emplace_back( ptS, ptE, i, false) ;
|
|
double dPar = -1. ;
|
|
if ( j != ssize( matPLPatches[i]) - 1)
|
|
vCompoBorders[i].GetParamAtPoint( ptE, dPar, dLinTol) ;
|
|
else {
|
|
double dParS = 0. ;
|
|
vCompoBorders[i].GetDomain( dParS, dPar) ;
|
|
}
|
|
matPatchPar.back().insert( int( round( dPar))) ;
|
|
}
|
|
}
|
|
#if DEBUG_CHAIN_CURVES
|
|
VC.clear() ; VT.clear() ;
|
|
for ( int i = 0 ; i < ssize( vSimpleBorder) ; ++ i) {
|
|
for ( int j = 0 ; j < ssize( vSimpleBorder[i]) ; ++ j) {
|
|
VT.emplace_back( matCompoPatches[i][j].Clone()) ;
|
|
VC.emplace_back( RED) ;
|
|
IGeoPoint3d* ptS = CreateGeoPoint3d() ;
|
|
ptS->Set( vSimpleBorder[i][j].ptStart) ;
|
|
VT.emplace_back( ptS) ;
|
|
VC.emplace_back( AQUA) ;
|
|
IGeoPoint3d* ptE = CreateGeoPoint3d() ;
|
|
ptE->Set( vSimpleBorder[i][j].ptEnd) ;
|
|
VT.emplace_back( ptE) ;
|
|
VC.emplace_back( AQUA) ;
|
|
}
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\EdgesAndPoints.nge") ;
|
|
#endif
|
|
|
|
// In generale se sono state selezionate più superfici, non è garantito che tutti gli estremi
|
|
// delle Patch coicidano solo con altri estremi di altre Patches ( può capitare che un estremo di
|
|
// una Patch cada ad esempio a metà di un'altra Patch e non nei suoi estremi )
|
|
// Soluzione : si spezzano le Patch ulteriormente in modo che ogni estremo di patch sia o nell'intorno
|
|
// di un altro estremo di patch o isolato ( ad esempio nei bordi più esterni )
|
|
BOOLVECTOR vBorderModif( vSimpleBorder.size(), false) ;
|
|
for ( int nCurrBorder = 0 ; nCurrBorder < ssize( vSimpleBorder) ; ++ nCurrBorder) {
|
|
// Recupero la Composita associata al Bordo Corrente
|
|
CurveComposite& CurrCompoBorder = vCompoBorders[nCurrBorder] ;
|
|
// Recupero le Patch associate
|
|
SimpleBorder& vPatchCurrBorder = vSimpleBorder[nCurrBorder] ;
|
|
// Recupero il vettore dei Parametri di Split
|
|
set<double>& setSplitPar = matPatchPar[nCurrBorder] ;
|
|
#if DEBUG_CHAIN_CURVES
|
|
VC.clear() ; VT.clear() ;
|
|
VT.emplace_back( CurrCompoBorder.Clone()) ;
|
|
VC.emplace_back( RED) ;
|
|
for ( auto Iter = setSplitPar.begin() ; Iter != setSplitPar.end() ; ++ Iter) {
|
|
Point3d ptCurr ;
|
|
CurrCompoBorder.GetPointD1D2( *Iter, ICurve::FROM_MINUS, ptCurr) ;
|
|
PtrOwner<IGeoPoint3d> ptGeoCurr( CreateGeoPoint3d()) ; ptGeoCurr->Set( ptCurr) ;
|
|
VT.emplace_back( ptGeoCurr->Clone()) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
}
|
|
#endif
|
|
|
|
// Scorro gli altri Bordi
|
|
for ( int nOtherBorder = 0 ; nOtherBorder < ssize( vSimpleBorder) ; ++ nOtherBorder) {
|
|
if ( nOtherBorder == nCurrBorder)
|
|
continue ;
|
|
#if DEBUG_CHAIN_CURVES
|
|
CurveComposite& OtherCompoBorder = vCompoBorders[nOtherBorder] ;
|
|
VT.emplace_back( OtherCompoBorder.Clone()) ;
|
|
VC.emplace_back( BLACK) ;
|
|
#endif
|
|
// Recupero l'altro bordo
|
|
SimpleBorder& OtherBorder = vSimpleBorder[nOtherBorder] ;
|
|
// Scorro le sue Patch
|
|
for ( int nOtherPatch = 0 ; nOtherPatch < ssize( OtherBorder) ; ++ nOtherPatch) {
|
|
// Recupero gli estremi della Path attuale
|
|
DistPointCurve DPTCRV( OtherBorder[nOtherPatch].ptStart, CurrCompoBorder) ;
|
|
double dDist = INFINITO ;
|
|
if ( DPTCRV.GetDist( dDist) && dDist < dLinTol) {
|
|
Point3d ptMinDist ;
|
|
int nFlag = 0 ;
|
|
if ( DPTCRV.GetMinDistPoint( 0., ptMinDist, nFlag)) {
|
|
bool bInsert = true ;
|
|
for ( int nCurrPatch = 0 ; bInsert && nCurrPatch < ssize( vPatchCurrBorder) ; ++ nCurrPatch) {
|
|
const SimplePatch& Patch = vPatchCurrBorder[nCurrPatch] ;
|
|
bInsert = ( ! AreSamePointEpsilon( Patch.ptStart, ptMinDist, dLinTol) &&
|
|
! AreSamePointEpsilon( Patch.ptEnd, ptMinDist, dLinTol)) ;
|
|
}
|
|
if ( bInsert) {
|
|
double dPar = -1. ;
|
|
if ( DPTCRV.GetParamAtMinDistPoint( 0., dPar, nFlag)) {
|
|
setSplitPar.insert( dPar) ;
|
|
vBorderModif[nCurrBorder] = true ;
|
|
#if DEBUG_CHAIN_CURVES
|
|
PtrOwner<IGeoPoint3d> ptGeoCurr( CreateGeoPoint3d()) ;
|
|
ptGeoCurr->Set( ptMinDist) ;
|
|
VT.emplace_back( ptGeoCurr->Clone()) ;
|
|
VC.emplace_back( AQUA) ;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if DEBUG_CHAIN_CURVES
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\SplitPathces.nge") ;
|
|
#endif
|
|
}
|
|
for ( int nCurrBorder = 0 ; nCurrBorder < ssize( vSimpleBorder) ; ++ nCurrBorder) {
|
|
// Se il bordo corrente non ha subito Split, passo al successivo
|
|
if ( ! vBorderModif[nCurrBorder])
|
|
continue ;
|
|
// Pulisco le Composite associate associate a tale Bordo
|
|
COMPOVECTOR& vCompoPatches = matCompoPatches[nCurrBorder] ;
|
|
vCompoPatches.clear() ;
|
|
// Recupero la Curva Composita corrente
|
|
CurveComposite& CurrCompo = vCompoBorders[nCurrBorder] ;
|
|
// Inizializzo il nuovo Bordo semplice formato dalle Patch
|
|
SimpleBorder newSimpleBorder ;
|
|
// Recupero il vettore dei Parametri di Split
|
|
set<double>& setSplitPar = matPatchPar[nCurrBorder] ;
|
|
double dLastPar = 0 ;
|
|
for ( auto Iter = setSplitPar.begin() ; Iter != setSplitPar.end() ; ++ Iter) {
|
|
PtrOwner<ICurve> pCrvPatch( CurrCompo.CopyParamRange( dLastPar, *Iter)) ;
|
|
if ( ! IsNull( pCrvPatch) && pCrvPatch->IsValid()) { // ad esempio per punti coincidenti
|
|
PtrOwner<CurveComposite> pCompoPatch( ConvertCurveToBasicComposite( Release( pCrvPatch))) ;
|
|
if ( IsNull( pCompoPatch) || ! pCompoPatch->IsValid())
|
|
return false ;
|
|
vCompoPatches.emplace_back( *Get( pCompoPatch)) ;
|
|
Point3d ptStart ; vCompoPatches.back().GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; vCompoPatches.back().GetEndPoint( ptEnd) ;
|
|
newSimpleBorder.emplace_back( ptStart, ptEnd, nCurrBorder, false) ;
|
|
dLastPar = *Iter ;
|
|
}
|
|
}
|
|
vSimpleBorder[nCurrBorder] = newSimpleBorder ;
|
|
}
|
|
|
|
|
|
// Si eliminano ora tutte le Patch che si sovrappongono, in modo da ottenere solo un
|
|
// le Patch che rappresentano il bordo esterno
|
|
const double MAXPOINT = 10 ;
|
|
for ( int nCurrCrv = 0 ; nCurrCrv < ssize( vSimpleBorder) - 1 ; ++ nCurrCrv) {
|
|
// Recupero la curva corrente
|
|
SimpleBorder& CurrCurve = vSimpleBorder[nCurrCrv] ;
|
|
// Scorro i suoi Edges
|
|
for ( int nCurrEdge = 0 ; nCurrEdge < ssize( CurrCurve) ; ++ nCurrEdge) {
|
|
// Se Edge cancellato, passo al successivo
|
|
if ( CurrCurve[nCurrEdge].bErase)
|
|
continue ;
|
|
// Recupero i due estremi dell'Edge
|
|
Point3d& ptCurrStart = CurrCurve[nCurrEdge].ptStart ;
|
|
Point3d& ptCurrEnd = CurrCurve[nCurrEdge].ptEnd ;
|
|
// Scorro le curve successive
|
|
for ( int nNextCrv = nCurrCrv + 1 ; nNextCrv < ssize( vSimpleBorder) ; ++ nNextCrv) {
|
|
// Recupero la curva successiva
|
|
SimpleBorder& NextCurve = vSimpleBorder[nNextCrv] ;
|
|
// Scorro i suoi Edges
|
|
for ( int nNextEdge = 0 ; nNextEdge < ssize( NextCurve) ; ++ nNextEdge) {
|
|
// Se Edge cancellato, passo al successivo
|
|
#if 0
|
|
// Se pezze si superficie piccolissime, si rischia di eliminare tutti i
|
|
// tratti Next con il primo tratto Curr e lasciare poi il Secondo Curr senza
|
|
// elementi da eliminare ( TrimTractor.nge)
|
|
if ( NextCurve[nNextEdge].bErase)
|
|
continue ;
|
|
#endif
|
|
// Recupero i due estremi di riferimento
|
|
Point3d& ptNextStart = NextCurve[nNextEdge].ptStart ;
|
|
Point3d& ptNextEnd = NextCurve[nNextEdge].ptEnd ;
|
|
// Controllo se i due estremi coincidono
|
|
if ( ( AreSamePointEpsilon( ptCurrStart, ptNextStart, dLinTol) &&
|
|
AreSamePointEpsilon( ptCurrEnd, ptNextEnd, dLinTol)) ||
|
|
( AreSamePointEpsilon( ptCurrStart, ptNextEnd, dLinTol) &&
|
|
AreSamePointEpsilon( ptCurrEnd, ptNextStart, dLinTol))) {
|
|
// Controllo che i punti coincidano
|
|
CurveComposite* pCompoCurr = &matCompoPatches[nCurrCrv][nCurrEdge] ;
|
|
CurveComposite* pCompoNext = &matCompoPatches[nNextCrv][nNextEdge] ;
|
|
if ( pCompoCurr == nullptr || pCompoNext == nullptr)
|
|
return false ;
|
|
double dLenCurr ; pCompoCurr->GetLength( dLenCurr) ;
|
|
bool bErase = true ;
|
|
for ( int nP = 1 ; bErase && nP <= MAXPOINT ; ++ nP) {
|
|
double dPar = 0. ;
|
|
pCompoCurr->GetParamAtLength( ( dLenCurr / MAXPOINT) * nP, dPar) ;
|
|
Point3d ptCurr ;
|
|
pCompoCurr->GetPointD1D2( dPar, ICurve::FROM_MINUS, ptCurr) ;
|
|
double dSqDist = 0. ;
|
|
DistPointCurve( ptCurr, *pCompoNext).GetSqDist( dSqDist) ;
|
|
bErase = ( dSqDist < dLinTol * dLinTol + SQ_EPS_SMALL) ;
|
|
}
|
|
// Disabilito i rispettivi Edges se necessario
|
|
if ( bErase) {
|
|
CurrCurve[nCurrEdge].bErase = true ;
|
|
NextCurve[nNextEdge].bErase = true ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Dalle PolyLine recupero delle curve composite ( quindi anch'esse come insieme di tratti linari)
|
|
ICRVCOMPOPOVECTOR vCompoPatches ; vCompoPatches.reserve( vSimpleBorder.size()) ;
|
|
for ( const PolyLine& PL : vPLBorders) {
|
|
if ( ! vCompoPatches.emplace_back( CreateCurveComposite()) ||
|
|
! vCompoPatches.back()->FromPolyLine( PL))
|
|
return false ;
|
|
vCompoPatches.back()->SetTempProp( PL.GetTempProp( 0), 0) ;
|
|
}
|
|
|
|
// Recupero solo i sottotratti di curva validi ( quindi che non fanno overlap tra loro)
|
|
for ( int nCrv = 0 ; nCrv < ssize( vSimpleBorder) ; ++ nCrv) {
|
|
// Recupero la curva
|
|
SimpleBorder& CrvEdge = vSimpleBorder[nCrv] ;
|
|
// Scorro i suoi edges
|
|
for ( int nEdge = 0 ; nEdge < ssize( CrvEdge) ; ++ nEdge) {
|
|
// Se edge non attivo, passo al successivo
|
|
if ( CrvEdge[nEdge].bErase)
|
|
continue ;
|
|
// Se punto iniziale finale uguali
|
|
// [ Se pezze di superficie piccolissime, si rischia di eliminare tutti i
|
|
// tratti Next con il primo tratto Curr e lasciare poi il Secondo Curr senza
|
|
// elementi da eliminare ( TrimTractor.nge) ]
|
|
if ( AreSamePointApprox( CrvEdge[nEdge].ptStart, CrvEdge[nEdge].ptEnd))
|
|
vCompoResult.emplace_back( Release( vCompoPatches[nCrv])) ;
|
|
else {
|
|
// Recupero il tratto di curva e lo memorizzo
|
|
double dUStart = 0., dUEnd = 0. ;
|
|
vCompoPatches[nCrv]->GetParamAtPoint( CrvEdge[nEdge].ptStart, dUStart, dLinTol) ;
|
|
vCompoPatches[nCrv]->GetParamAtPoint( CrvEdge[nEdge].ptEnd, dUEnd, dLinTol) ;
|
|
vCompoResult.emplace_back( ConvertCurveToComposite( vCompoPatches[nCrv]->CopyParamRange( dUStart, dUEnd))) ;
|
|
vCompoResult.back()->SetTempProp( vCompoPatches[nCrv]->GetTempProp( 0), 0) ;
|
|
}
|
|
}
|
|
}
|
|
// Conservo solo le curve valide
|
|
for ( int nCrv = 0 ; nCrv < ssize( vCompoResult) ; ++ nCrv) {
|
|
if ( IsNull( vCompoResult[nCrv]) || ! vCompoResult[nCrv]->IsValid()) {
|
|
vCompoResult.erase( vCompoResult.begin() + nCrv) ;
|
|
-- nCrv ;
|
|
}
|
|
}
|
|
#if DEBUG_CHAIN_CURVES // Debug per le Patch risultanti
|
|
VT.clear() ; VC.clear() ;
|
|
for ( int i = 0 ; i < ssize( vCompoResult) ; ++ i) {
|
|
VT.emplace_back( vCompoResult[i]->Clone()) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\FinalChainedBorders.nge") ;
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Funzione per concatenare nello spazio 3d le Curve Patches.
|
|
// NB. Tutte le vCompoPathes sono formate da tratti lineari.
|
|
static bool
|
|
ChainCompoPatches( ICRVCOMPOPOVECTOR& vCompoPatches, double dChainTol,
|
|
ICRVCOMPOPOVECTOR& vCompoChained)
|
|
{
|
|
// Controllo dei parametri
|
|
if ( vCompoPatches.empty())
|
|
return false ;
|
|
vCompoChained.clear() ;
|
|
|
|
// Controllo la validità delle curve
|
|
for ( const ICurveComposite* pCrvCompo : vCompoPatches) {
|
|
if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
// La concatenazione parte dalla curva più lunga
|
|
double dMaxLen = - INFINITO ;
|
|
int nInd = 0 ;
|
|
for ( int i = 0 ; i < ssize( vCompoPatches) ; ++ i) {
|
|
double dLen = 0. ;
|
|
vCompoPatches[i]->GetLength( dLen) ;
|
|
if ( dLen > dMaxLen) {
|
|
dMaxLen = dLen ;
|
|
nInd = i ;
|
|
}
|
|
}
|
|
swap( vCompoPatches[0], vCompoPatches[nInd]) ;
|
|
|
|
// [ TODO : Algoritmo migliorabile nei controlli ]
|
|
// Scorro le curve
|
|
vCompoChained.reserve( vCompoPatches.size()) ;
|
|
BOOLVECTOR vbCrvChained( vCompoPatches.size(), false) ;
|
|
for ( int nCurrCrv = 0 ; nCurrCrv < ssize( vCompoPatches) ; ++ nCurrCrv) {
|
|
// Se curva già concatenata, passo alla successiva ( no biforcazioni ammesse -> errore )
|
|
if ( vbCrvChained[nCurrCrv])
|
|
continue ;
|
|
// Definisco una nuova curva di concatenazione e recupero il suo estremo finale
|
|
Point3d ptEnd ;
|
|
if ( ! vCompoChained.emplace_back( CloneCurveComposite( vCompoPatches[nCurrCrv])) ||
|
|
! vCompoChained.back()->GetEndPoint( ptEnd))
|
|
return false ;
|
|
// Trasferisco le TempProps alle sottocurve
|
|
for ( int nU = 0 ; nU < vCompoChained.back()->GetCurveCount() ; ++ nU)
|
|
vCompoChained.back()->SetCurveTempProp( nU, vCompoPatches[nCurrCrv]->GetTempProp( 0), 0) ;
|
|
// Cerco tra le altre curve disponibili una candidata alla concatenazione
|
|
for ( int nNextCrv = 0 ; nNextCrv < ssize( vCompoPatches) ; ++ nNextCrv) {
|
|
if ( nNextCrv == nCurrCrv || vbCrvChained[nNextCrv])
|
|
continue ;
|
|
// Recupero i suoi estremi [ sono tratti lineari, la ricerca è immediata ]
|
|
Point3d ptStartN ; vCompoPatches[nNextCrv]->GetStartPoint( ptStartN) ;
|
|
Point3d ptEndN ; vCompoPatches[nNextCrv]->GetEndPoint( ptEndN) ;
|
|
// Se estremi distanti, passo alla successiva
|
|
bool bChain = AreSamePointEpsilon( ptEnd, ptStartN, dChainTol) ;
|
|
bool bChainInv = ( ( ! bChain) && AreSamePointEpsilon( ptEnd, ptEndN, dChainTol)) ;
|
|
if ( ! bChain && ! bChainInv)
|
|
continue ;
|
|
// Recupero la lunghezza della curva
|
|
double dLenN = 0. ; vCompoPatches[nNextCrv]->GetLength( dLenN) ;
|
|
// Se la lunghezza della curva è più piccola della tolleranza
|
|
if ( dLenN < dChainTol + EPS_ZERO) {
|
|
// La curva corrente non viene aggiunta, ma viene modificato l'estremo finale corrente
|
|
// nel punto medio
|
|
vCompoChained.back()->ModifyEnd( Media( ptEndN, ptEnd)) ;
|
|
}
|
|
// Se la lunghezza è maggiore della tolleranza
|
|
else {
|
|
// Recupero la curva successiva
|
|
PtrOwner<ICurveComposite> pCrv( CloneCurveComposite( vCompoPatches[nNextCrv])) ;
|
|
if ( IsNull( pCrv) || ! pCrv->IsValid())
|
|
return false ;
|
|
// Se necessario inverto ( potrebbe capitare per triangoli male orientati)
|
|
if ( bChainInv)
|
|
pCrv->Invert() ;
|
|
// Trasferisco le TempProps alle sottocurve
|
|
for ( int nU = 0 ; nU < pCrv->GetCurveCount() ; ++ nU)
|
|
pCrv->SetCurveTempProp( nU, pCrv->GetTempProp( 0), 0) ;
|
|
// Se estremi più distanti della tolleranza, li cambio nel punto medio tra essi
|
|
Point3d ptEndCurr ; vCompoChained.back()->GetEndPoint( ptEndCurr) ;
|
|
Point3d ptStartNext ; pCrv->GetStartPoint( ptStartNext) ;
|
|
if ( SqDist( ptEndCurr, ptStartNext) > dChainTol * dChainTol) {
|
|
Point3d ptMid = Media( ptEndCurr, ptStartNext) ;
|
|
vCompoChained.back()->ModifyEnd( ptMid) ;
|
|
pCrv->ModifyStart( ptMid) ;
|
|
}
|
|
// Concateno
|
|
if ( ! vCompoChained.back()->AddCurve( pCrv->Clone(), true, dChainTol))
|
|
return false ;
|
|
}
|
|
// Aggiornamento dei parametri
|
|
vbCrvChained[nNextCrv] = true ; // curva in una concatenazione
|
|
nNextCrv = 0 ; // reset contatore per curva next
|
|
if ( ! vCompoChained.back()->GetEndPoint( ptEnd)) // aggiornamento punto finale della concatenazione
|
|
return false ;
|
|
}
|
|
}
|
|
// Verifico la validità delle curve ottenute
|
|
for ( int i = 0 ; i < ssize( vCompoChained) ; ++ i) {
|
|
if ( IsNull( vCompoChained[i]) || ! vCompoChained[i]->IsValid())
|
|
return false ;
|
|
// Se curva al di sotto della tolleranza, la scarto
|
|
// ( Capita se ci sono facce/superfici molto strette. La concatenazione chiude in automatico
|
|
// la curva complessiva isolando quindi queste entità)
|
|
double dLen = 0. ;
|
|
if ( ! vCompoChained[i]->GetLength( dLen) || dLen < dChainTol) {
|
|
vCompoChained.erase( vCompoChained.begin() + i) ;
|
|
-- i ;
|
|
}
|
|
}
|
|
#if DEBUG_CHAIN_CURVES // Debug curve concatenate grezze
|
|
VT.clear() ; VC.clear() ;
|
|
for ( const ICurveComposite* pCompo : vCompoChained) {
|
|
VT.emplace_back( pCompo->Clone()) ;
|
|
VC.emplace_back( Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.)) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\ChainedCurves.nge") ;
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Funzione che restituisce una prima semplificazione e concatenazione delle curve
|
|
// di bordi definiti mediante PolyLines
|
|
static bool
|
|
GetRawEdges( const POLYLINEVECTOR& vPLBorders, double dLinTol, double dAngTol,
|
|
ICRVCOMPOPOVECTOR& vCompoChained)
|
|
{
|
|
// Se non o curve di bordo, allora non faccio nulla
|
|
if ( vPLBorders.empty())
|
|
return true ;
|
|
vCompoChained.clear() ;
|
|
|
|
// Recupero le curve di Bordo che non effettuano Overlap tra loro ( sempre solo tratti lineari)
|
|
ICRVCOMPOPOVECTOR vCompoPatches ;
|
|
if ( ! ManagePatches( vPLBorders, dLinTol, dAngTol, vCompoPatches))
|
|
return false ;
|
|
|
|
// Concateno le curve risultanti
|
|
const double CHAIN_TOL = 50. * EPS_SMALL ;
|
|
if ( ! ChainCompoPatches( vCompoPatches, CHAIN_TOL, vCompoChained))
|
|
return false ;
|
|
|
|
// Se le curve risultano aperte con estremi entro la tolleranza, le chiudo
|
|
for ( int i = 0 ; i < int( vCompoChained.size()) ; ++ i) {
|
|
if ( vCompoChained[i] == nullptr || ! vCompoChained[i]->IsValid())
|
|
return false ;
|
|
Point3d ptS ; vCompoChained[i]->GetStartPoint( ptS) ;
|
|
Point3d ptE ; vCompoChained[i]->GetEndPoint( ptE) ;
|
|
if ( AreSamePointEpsilon( ptS, ptE, dLinTol)) {
|
|
vCompoChained[i]->Close() ; // utilizza il punto medio
|
|
// Riporto la Temp Prop
|
|
int nTmpProp0 = vCompoChained[i]->GetFirstCurve()->GetTempProp( 0) ;
|
|
vCompoChained[i]->SetCurveTempProp( vCompoChained[i]->GetCurveCount() - 1, nTmpProp0, 0) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Funzione per calcolare le Edge Curves per la creazione della superficie di Bezier Ruled
|
|
// mediante spessore e tolleranza associata
|
|
static bool
|
|
BreakCompoPathces( ICRVCOMPOPOVECTOR& vCompoChained, double dLinTol, double dAngTol,
|
|
double dThick, double dThickTol)
|
|
{
|
|
// Se non ho curve, non faccio nulla
|
|
if ( vCompoChained.empty())
|
|
return true ;
|
|
const double ANG_SPLIT = 65. ;
|
|
|
|
// Scorro le curve
|
|
ICRVCOMPOPOVECTOR vCrvResult ;
|
|
for ( const ICurveComposite* pCompoChained : vCompoChained) {
|
|
// Verifico la sua validità
|
|
if ( pCompoChained == nullptr || ! pCompoChained->IsValid())
|
|
return false ;
|
|
// Recupero la PolyLine associata ( senza alcuna approssimazione)
|
|
PolyLine myPL ;
|
|
if ( ! pCompoChained->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_SPECIAL, myPL))
|
|
return false ;
|
|
// Approssimo la curva mediante Set di punti considerando la tolleranza angolare
|
|
POLYLINEVECTOR vPL ;
|
|
if ( ! GetPointSetByAngTol( myPL, ANG_SPLIT, vPL))
|
|
return false ;
|
|
// Scorro le PolyLine ricavate e recupero i punti di Split ( tratti da rimuovere)
|
|
BIPNTVECTOR vBiPntSplit ; vBiPntSplit.reserve( 2) ;
|
|
for ( const PolyLine& PL : vPL) {
|
|
double dLen ; PL.GetLength( dLen) ;
|
|
if ( abs( dLen - dThick) < dThickTol) {
|
|
Point3d ptStart ; PL.GetFirstPoint( ptStart) ;
|
|
Point3d ptEnd ; PL.GetLastPoint( ptEnd) ;
|
|
vBiPntSplit.emplace_back( make_pair( ptStart, ptEnd)) ;
|
|
}
|
|
}
|
|
// Se non ho ottenuto esattamente due tratti, non analizzo più la curva corrente
|
|
if ( ssize( vBiPntSplit) != 2) {
|
|
LOG_INFO( GetEGkLogger(), "Warning in Trimming : Detected more Curves by Thick values") ;
|
|
break ; // lascio la curva invariata
|
|
}
|
|
if ( ! vCrvResult.emplace_back( CloneCurveComposite( pCompoChained)))
|
|
return false ;
|
|
double dU0 = 0., dU1 = 0. ;
|
|
vCrvResult.back()->GetParamAtPoint( vBiPntSplit[0].second, dU0) ;
|
|
vCrvResult.back()->ChangeStartPoint( dU0) ;
|
|
vCrvResult.back()->GetParamAtPoint( vBiPntSplit[1].first, dU1) ;
|
|
vCrvResult.back().Set( ConvertCurveToComposite( vCrvResult.back()->CopyParamRange( 0., dU1))) ;
|
|
if ( ! vCrvResult.emplace_back( CloneCurveComposite( pCompoChained)))
|
|
return false ;
|
|
vCrvResult.back()->ChangeStartPoint( dU0) ;
|
|
vCrvResult.back()->GetParamAtPoint( vBiPntSplit[1].second, dU0) ;
|
|
vCrvResult.back()->GetParamAtPoint( vBiPntSplit[0].first, dU1) ;
|
|
vCrvResult.back().Set( ConvertCurveToComposite( vCrvResult.back()->CopyParamRange( dU0, dU1))) ;
|
|
}
|
|
|
|
swap( vCrvResult, vCompoChained) ;
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Funzione per calcolare le Edge Curves per la creazione della superficie di Bezier Ruled
|
|
// mediante punti di rottura
|
|
static bool
|
|
BreakCompoPathces( ICRVCOMPOPOVECTOR& vCompoChained, double dLinTol, double dAngTol,
|
|
const BIPNTVECTOR& vBreakingPts)
|
|
{
|
|
// Se non ho curve, non faccio nulla
|
|
if ( vCompoChained.empty())
|
|
return true ;
|
|
// Se non ho punti di rottura, non faccio nulla
|
|
if ( vBreakingPts.empty())
|
|
return true ;
|
|
// Se ho punti di rottura in comune, errore
|
|
for ( int i = 0 ; i < ssize( vBreakingPts) ; ++ i) {
|
|
if ( AreSamePointEpsilon( vBreakingPts[i].first, vBreakingPts[i].second, dLinTol))
|
|
return false ;
|
|
for ( int j = i + 1 ; j < ssize( vBreakingPts) ; ++ j) {
|
|
if ( AreSamePointEpsilon( vBreakingPts[i].first, vBreakingPts[j].first, dLinTol) ||
|
|
AreSamePointEpsilon( vBreakingPts[i].first, vBreakingPts[j].second, dLinTol) ||
|
|
AreSamePointEpsilon( vBreakingPts[i].second, vBreakingPts[j].first, dLinTol) ||
|
|
AreSamePointEpsilon( vBreakingPts[i].second, vBreakingPts[j].second, dLinTol))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
#if DEBUG_BRK_POINTS
|
|
VC.clear() ; VT.clear() ;
|
|
for ( const ICurveComposite* _pCompoChained : vCompoChained) {
|
|
VT.emplace_back( _pCompoChained->Clone()) ;
|
|
VC.emplace_back( RED) ;
|
|
}
|
|
for ( const BIPOINT& _Bipt : vBreakingPts) {
|
|
IGeoPoint3d* _ptS = CreateGeoPoint3d() ; _ptS->Set( _Bipt.first) ;
|
|
IGeoPoint3d* _ptE = CreateGeoPoint3d() ; _ptE->Set( _Bipt.second) ;
|
|
VT.emplace_back( _ptS) ;
|
|
VT.emplace_back( _ptE) ;
|
|
VC.emplace_back( AQUA) ;
|
|
VC.emplace_back( LIME) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\Brk.nge") ;
|
|
#endif
|
|
|
|
// Scorro le curve
|
|
ICRVCOMPOPOVECTOR vCrvResult ;
|
|
for ( const ICurveComposite* pCompoChained : vCompoChained) {
|
|
// Verifico che sia valida
|
|
if ( pCompoChained == nullptr || ! pCompoChained->IsValid())
|
|
return false ;
|
|
// Copio la curva, dato che potrei spezzarla
|
|
ICRVCOMPOPOVECTOR vCompoParts ; vCompoParts.reserve( vBreakingPts.size()) ;
|
|
if ( ! vCompoParts.emplace_back( CloneCurveComposite( pCompoChained)))
|
|
return false ;
|
|
for ( const BIPOINT& BiPnts : vBreakingPts) {
|
|
// Scorro i tratti ( inizialmente la curva stessa)
|
|
for ( int i = 0 ; i < ssize( vCompoParts) ; ++ i) {
|
|
// Controllo che la coppia di punti giacia su quella curva
|
|
if ( ! vCompoParts[i]->IsPointOn( BiPnts.first, dLinTol) ||
|
|
! vCompoParts[i]->IsPointOn( BiPnts.second, dLinTol))
|
|
continue ;
|
|
// Se entrambi sulla curva, recupero il tratto più lungo da loro definito
|
|
double dUS = 0. ; vCompoParts[i]->GetParamAtPoint( BiPnts.first, dUS, dLinTol) ;
|
|
double dUE = 0. ; vCompoParts[i]->GetParamAtPoint( BiPnts.second, dUE, dLinTol) ;
|
|
PtrOwner<ICurve> pCrvA( vCompoParts[i]->CopyParamRange( dUS, dUE)) ;
|
|
PtrOwner<ICurve> pCrvB( vCompoParts[i]->CopyParamRange( dUE, dUS)) ;
|
|
// Se la curva corrente è chiusa
|
|
if ( vCompoParts[i]->IsClosed()) {
|
|
if ( IsNull( pCrvA) || IsNull( pCrvB))
|
|
return false ;
|
|
double dLenA = 0. ; pCrvA->GetLength( dLenA) ;
|
|
double dLenB = 0. ; pCrvB->GetLength( dLenB) ;
|
|
if ( dLenA > dLenB)
|
|
vCompoParts[i].Set( ConvertCurveToComposite( Release( pCrvA))) ;
|
|
else
|
|
vCompoParts[i].Set( ConvertCurveToComposite( Release( pCrvB))) ;
|
|
}
|
|
// Se la curva è aperta
|
|
else {
|
|
double dUDomS = 0., dUDomE = 0. ;
|
|
vCompoParts[i]->GetDomain( dUDomS, dUDomE) ;
|
|
PtrOwner<ICurveComposite> pCompoTmp( CloneCurveComposite( vCompoParts[i])) ;
|
|
if ( IsNull( pCompoTmp) || ! pCompoTmp->IsValid())
|
|
return false ;
|
|
if ( IsNull( pCrvA)) {
|
|
pCompoTmp->TrimStartAtParam( dUS) ;
|
|
vCompoParts.emplace_back( Release( pCompoTmp)) ;
|
|
vCompoParts[i]->TrimEndAtParam( dUE) ;
|
|
}
|
|
else if ( IsNull( pCrvB)) {
|
|
pCompoTmp->TrimStartAtParam( dUE) ;
|
|
vCompoParts.emplace_back( Release( pCompoTmp)) ;
|
|
vCompoParts[i]->TrimEndAtParam( dUS) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Memorizzo tutte le curve valide ottenute dai punti di rottura
|
|
for ( int i = 0 ; i < ssize( vCompoParts) ; ++ i) {
|
|
if ( ! IsNull( vCompoParts[i]) && vCompoParts[i]->IsValid())
|
|
vCrvResult.emplace_back( Release( vCompoParts[i])) ;
|
|
}
|
|
}
|
|
|
|
swap( vCrvResult, vCompoChained) ;
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static bool
|
|
BreakCompoPathces( ICRVCOMPOPOVECTOR& vCompoChained, double dLinTol, double dAngTol)
|
|
{
|
|
// Se non ho curve, non faccio nulla
|
|
if ( vCompoChained.empty())
|
|
return true ;
|
|
const double ANG_SPLIT = 65. ;
|
|
|
|
// Scorro le curve
|
|
ICRVCOMPOPOVECTOR vCrvResult ;
|
|
for ( const ICurveComposite* pCompoChained : vCompoChained) {
|
|
// Verifico la sua validità
|
|
if ( pCompoChained == nullptr || ! pCompoChained->IsValid())
|
|
return false ;
|
|
// Recupero la PolyLine associata ( senza alcuna approssimazione)
|
|
PolyLine myPL ;
|
|
if ( ! pCompoChained->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_SPECIAL, myPL))
|
|
return false ;
|
|
// Approssimo la curva mediante Set di punti considerando la tolleranza angolare
|
|
POLYLINEVECTOR vPL ;
|
|
if ( ! GetPointSetByAngTol( myPL, ANG_SPLIT, vPL))
|
|
return false ;
|
|
// Se meno di 4 PolyLine non faccio nulla
|
|
if ( ssize( vPL) < 4) {
|
|
LOG_INFO( GetEGkLogger(), "Warning in Trimming : Not Detected 2 Curves") ;
|
|
return true ;
|
|
}
|
|
// Scorro le PolyLine e ricavo le due più corte
|
|
INTDBLVECTOR vIndPLLen ; vIndPLLen.reserve( vPL.size()) ;
|
|
for ( int i = 0 ; i < ssize( vPL) ; ++ i) {
|
|
double dLen ; vPL[i].GetLength( dLen) ;
|
|
vIndPLLen.emplace_back( make_pair( i, dLen)) ;
|
|
}
|
|
sort( vIndPLLen.begin(), vIndPLLen.end(), []( const INTDBL& IndA, const INTDBL& IndB){
|
|
return ( IndA.second < IndB.second) ;
|
|
}) ;
|
|
BIPNTVECTOR vBiPntSplit ; vBiPntSplit.reserve( 2) ;
|
|
for ( int i = 0 ; i < 2 ; ++ i) {
|
|
Point3d ptS ; vPL[vIndPLLen[i].first].GetFirstPoint( ptS) ;
|
|
Point3d ptE ; vPL[vIndPLLen[i].first].GetLastPoint( ptE) ;
|
|
vBiPntSplit.emplace_back( make_pair( ptS, ptE)) ;
|
|
}
|
|
|
|
#if DEBUG_BRK
|
|
VT.emplace_back( pCompoChained->Clone()) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
for ( PolyLine& _PL : vPL) {
|
|
IGeoPoint3d* _ptS = CreateGeoPoint3d() ;
|
|
Point3d _ptStart ; _PL.GetFirstPoint( _ptStart) ; _ptS->Set( _ptStart) ;
|
|
VT.emplace_back( _ptS) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
IGeoPoint3d* _ptE = CreateGeoPoint3d() ;
|
|
Point3d _ptEnd ; _PL.GetLastPoint( _ptEnd) ; _ptE->Set( _ptEnd) ;
|
|
VT.emplace_back( _ptE) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
}
|
|
for ( BIPOINT& BiPt : vBiPntSplit) {
|
|
ICurveLine* pLine = CreateCurveLine() ;
|
|
pLine->Set( BiPt.first, BiPt.second) ;
|
|
VT.emplace_back( pLine) ;
|
|
VC.emplace_back( FUCHSIA) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\SplitRawEdges.nge") ;
|
|
#endif
|
|
|
|
if ( ! vCrvResult.emplace_back( CloneCurveComposite( pCompoChained)))
|
|
return false ;
|
|
double dU0 = 0., dU1 = 0. ;
|
|
vCrvResult.back()->GetParamAtPoint( vBiPntSplit[0].second, dU0) ;
|
|
vCrvResult.back()->ChangeStartPoint( dU0) ;
|
|
vCrvResult.back()->GetParamAtPoint( vBiPntSplit[1].first, dU1) ;
|
|
vCrvResult.back().Set( ConvertCurveToComposite( vCrvResult.back()->CopyParamRange( 0., dU1))) ;
|
|
if ( ! vCrvResult.emplace_back( CloneCurveComposite( pCompoChained)))
|
|
return false ;
|
|
vCrvResult.back()->ChangeStartPoint( dU0) ;
|
|
vCrvResult.back()->GetParamAtPoint( vBiPntSplit[1].second, dU0) ;
|
|
vCrvResult.back()->GetParamAtPoint( vBiPntSplit[0].first, dU1) ;
|
|
vCrvResult.back().Set( ConvertCurveToComposite( vCrvResult.back()->CopyParamRange( dU0, dU1))) ;
|
|
}
|
|
|
|
swap( vCrvResult, vCompoChained) ;
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//---------------------------- Estrazione dei Bordi ---------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione che restituisce l'insieme dei Bordi di una Superficie ( TriMesh o Bezier)
|
|
static bool
|
|
InsertLoopsOfSurf( const ISurf* pSurf, POLYLINEVECTOR& vPL)
|
|
{
|
|
// Se superficie TriMesh
|
|
if ( pSurf->GetType() == SRF_TRIMESH) {
|
|
// Recupero la superficie, verificandone la validità
|
|
const ISurfTriMesh* pStm = GetSurfTriMesh( pSurf) ;
|
|
if ( pStm == nullptr || ! pStm->IsValid())
|
|
return false ;
|
|
// verifico che tutte le parti siano valide
|
|
// ( nel recupero dei Loop alcune superfici presentano delle Part non valide)
|
|
double dPartArea = 0. ;
|
|
bool bValidParts = true ;
|
|
BOOLVECTOR vValidParts ; vValidParts.reserve( pStm->GetPartCount()) ;
|
|
for ( int nP = 0 ; nP < pStm->GetPartCount() ; ++ nP) {
|
|
vValidParts.emplace_back( pStm->GetPartArea( nP, dPartArea) && dPartArea > SQ_EPS_SMALL) ;
|
|
bValidParts = bValidParts && ( vValidParts.back()) ;
|
|
}
|
|
// Recupero le PolyLine di bordo
|
|
if ( bValidParts) {
|
|
if ( ! pStm->GetLoops( vPL))
|
|
return false ;
|
|
}
|
|
else {
|
|
for ( int nP = 0 ; nP < pStm->GetPartCount() ; ++ nP) {
|
|
if ( vValidParts[nP]) {
|
|
POLYLINEVECTOR vPLPart ;
|
|
if ( ! pStm->GetPartLoops( nP, vPLPart))
|
|
return false ;
|
|
for ( const PolyLine& PL : vPLPart)
|
|
vPL.emplace_back( PL) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Se superficie di Bezier
|
|
else if ( pSurf->GetType() == SRF_BEZIER) {
|
|
// Recupero la superficie, verificandone la validità
|
|
const ISurfBezier* pSbz = GetSurfBezier( pSurf) ;
|
|
if ( pSbz == nullptr || ! pSbz->IsValid())
|
|
return false ;
|
|
// Recupero i Bordi ( questa funzione restituisce dei tratti lineari)
|
|
ICRVCOMPOPOVECTOR vLoops ;
|
|
if ( ! pSbz->GetLoops( vLoops, true))
|
|
return false ;
|
|
// Li inserisco nel vettore ( come insieme di tratti lineari)
|
|
// NB. Tengo le tolleranze molto basse per ottenere dei loop rappresentativi della geometria
|
|
for ( int nL = 0 ; nL < ssize( vLoops) ; ++ nL) {
|
|
if ( ! IsNull( vLoops[nL]) && vLoops[nL]->IsValid()) {
|
|
vPL.emplace_back( PolyLine()) ;
|
|
if ( ! vLoops[nL]->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_STD, vPL.back()))
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per ottenere il bordo di una superficie TriMesh definito da un insieme di facce
|
|
static bool
|
|
GetSurfTmBordersByFaces( const SurfTriMesh* pStm, const INTVECTOR& vFaces,
|
|
double dLinTol, POLYLINEVECTOR& vPL)
|
|
{
|
|
// Controllo che la superficie sia valida
|
|
if ( pStm == nullptr || ! pStm->IsValid())
|
|
return false ;
|
|
|
|
// Inserisco il primo triangolo valido di ogni faccia
|
|
INTVECTOR vTria ;
|
|
for ( const int& nF : vFaces) {
|
|
int nT = SVT_NULL ;
|
|
if ( pStm->GetTriaFromFace( nF, nT))
|
|
vTria.emplace_back( nT) ;
|
|
}
|
|
|
|
// Definisco un vettore di concatenamento delle curve ( in questo caso segmenti, lati di triangoli)
|
|
ICRVCOMPOPOVECTOR vCompoChain ; vCompoChain.reserve( 3 * pStm->GetTriangleCount()) ;
|
|
|
|
// Collezione di triangoli già visitati
|
|
unordered_set<int> SetTria ; SetTria.reserve( pStm->GetTriangleCount()) ;
|
|
// Collezione di Edges come coppia di punti
|
|
BIPNTVECTOR vEdges ; vEdges.reserve( 3 * pStm->GetTriangleCount()) ;
|
|
|
|
// Finchè ho triangoli di adiacenza non ancora visitati
|
|
while ( ! vTria.empty()) {
|
|
// Recupero il triangolo corrente
|
|
int nCurrTria = vTria.back() ;
|
|
// Elimino il triangolo corrente
|
|
vTria.pop_back() ;
|
|
// Se triangolo non presente, lo aggiungo come visitato
|
|
auto IterTria = SetTria.find( nCurrTria) ;
|
|
if ( IterTria == SetTria.end())
|
|
SetTria.insert( nCurrTria) ;
|
|
// Se triangolo già visitato, passo al successivo
|
|
else
|
|
continue ;
|
|
|
|
// Recupero il triangolo corrente ( definito dai suoi 3 vertici )
|
|
int nTriaVertices[3] ;
|
|
pStm->GetTriangle( nCurrTria, nTriaVertices) ;
|
|
|
|
// Recupero i tre triangoli adiacenti al triangolo corrente
|
|
int nIdAdjTriaId[3] ;
|
|
if ( ! pStm->GetTriangleAdjacencies( nCurrTria, nIdAdjTriaId))
|
|
return false ;
|
|
// Scorro le adiacenze
|
|
for ( int nAdj = 0 ; nAdj < 3 ; ++ nAdj) {
|
|
// Recupero l'indice del triangolo adiacente
|
|
int nTriaAdj = nIdAdjTriaId[nAdj] ;
|
|
// Inserisco il lato orientato del triangolo se :
|
|
// - Non ho adiacenze
|
|
// - L'adiacenza avviene su una faccia da non considerare
|
|
bool bInsertEdge = false ;
|
|
if ( nTriaAdj == SVT_NULL)
|
|
bInsertEdge = true ;
|
|
else {
|
|
// Recupero la faccia del triangolo adiacente
|
|
int nFaceAdj = pStm->GetFacetFromTria( nTriaAdj) ;
|
|
// Se appartiene ad una faccia da inserire
|
|
if ( find( vFaces.begin(), vFaces.end(), nFaceAdj) != vFaces.end())
|
|
vTria.push_back( nTriaAdj) ;
|
|
// Se appartiene ad una faccia da non considerare, memorizzo il suo Edge orientato
|
|
else
|
|
bInsertEdge = true ;
|
|
}
|
|
|
|
// Se richiesto, memorizzo l'Edge
|
|
if ( bInsertEdge) {
|
|
Point3d ptS ; pStm->GetVertex( nTriaVertices[nAdj % 3], ptS) ;
|
|
Point3d ptE ; pStm->GetVertex( nTriaVertices[( nAdj + 1) % 3], ptE) ;
|
|
if ( vCompoChain.emplace_back( CreateCurveComposite())) {
|
|
vCompoChain.back()->AddPoint( ptS) ;
|
|
vCompoChain.back()->AddLine( ptE) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recupero i tratti di PolyLine dei triangoli
|
|
ICRVCOMPOPOVECTOR vCompoChained ;
|
|
if ( ! ChainCompoPatches( vCompoChain, dLinTol, vCompoChained))
|
|
return false ;
|
|
for ( const ICurveComposite* pCompoChained : vCompoChain) {
|
|
if ( pCompoChained != nullptr && pCompoChained->IsValid()) {
|
|
vPL.emplace_back( PolyLine()) ;
|
|
pCompoChained->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_SPECIAL, vPL.back()) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per il calcolo dei bordi delle superfici
|
|
static bool
|
|
GetSurfBorders( const CISURFPVECTOR& vSurf, const SELVECTOR& vSurfFace, double dLinTol,
|
|
POLYLINEVECTOR& vPLBorders)
|
|
{
|
|
// Verifico che tutte le superfici siano valide e ben definite
|
|
for ( const ISurf* pSurf : vSurf) {
|
|
if ( pSurf == nullptr || ! pSurf->IsValid())
|
|
return false ;
|
|
}
|
|
vPLBorders.clear() ;
|
|
|
|
// Definisco l'insieme delle PolyLine ricavate dai bordi
|
|
// NB. I bordi possono derivare da :
|
|
// - Loop di una Bezier
|
|
// - Loop di una Superficie TriMesh
|
|
// - Insieme di facce di una supericie TriMesh
|
|
// Scorro il vettore delle superfici
|
|
for ( int nS = 0 ; nS < ssize( vSurf) ; ++ nS) {
|
|
// Recupero la superficie
|
|
const ISurf* pSurf = vSurf[nS] ;
|
|
if ( pSurf == nullptr)
|
|
return false ;
|
|
// Recupero le facce associate a quella superficie dal vettore di selezione
|
|
INTVECTOR vFaces ;
|
|
for ( const SelData& SurfFace : vSurfFace) {
|
|
if ( SurfFace.nId == nS &&
|
|
find( vFaces.begin(), vFaces.end(), SurfFace.nSub) == vFaces.end())
|
|
vFaces.push_back( SurfFace.nSub) ;
|
|
}
|
|
// Recupero la dimensione del vettore delle PolyLine risultati attuale
|
|
int nSize = ssize( vPLBorders) ;
|
|
// Se non ho nessuna faccia e nessun triangolo da scartare, estraggo i bordi complessivi della superficie
|
|
if ( ssize( vFaces) == 1 && vFaces[0] == SEL_SUB_ALL) {
|
|
if ( ! InsertLoopsOfSurf( pSurf, vPLBorders))
|
|
return false ;
|
|
}
|
|
// Se ho più facce da inserire
|
|
else {
|
|
// Se TriMesh
|
|
if ( pSurf->GetType() == SRF_TRIMESH) {
|
|
// Recupero la regione TriMesh di base
|
|
const SurfTriMesh* pStm = GetBasicSurfTriMesh( pSurf) ;
|
|
if ( pStm == nullptr)
|
|
return false ;
|
|
// Aggiungo i tratti di curva necessari
|
|
if ( ! GetSurfTmBordersByFaces( pStm, vFaces, dLinTol, vPLBorders))
|
|
return false ;
|
|
}
|
|
// Se Bezier, errore, non ho facce per la superficie
|
|
if ( pSurf->GetType() == SRF_BEZIER)
|
|
return false ;
|
|
}
|
|
// Associo alle nuove PolyLine ricavate l'indice della superficie corrispondente
|
|
for ( int i = nSize ; i < ssize( vPLBorders) ; ++ i)
|
|
vPLBorders[i].SetTempProp( nS, 0) ;
|
|
}
|
|
|
|
#if DEBUG_BASIC_BORDERS // Debug per Bordi
|
|
VT.clear() ; VC.clear() ;
|
|
for ( const PolyLine& _PL : vPLBorders) {
|
|
ICurveComposite* _pCrv = CreateCurveComposite() ;
|
|
_pCrv->FromPolyLine( _PL) ;
|
|
VT.emplace_back( _pCrv->Clone()) ;
|
|
VC.emplace_back( Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.)) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\BasicBorders.nge") ;
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per il calcolo dei bordi mediante spessore
|
|
static bool
|
|
GetBorderByExtrusion( const SurfTriMesh* pStm, double dThick, double dLinTol, double dAngTol,
|
|
vector<pair<PolyLine, PolyLine>>& vPLLoopPLExtr)
|
|
{
|
|
// Verifico la validità della superficie
|
|
if ( pStm == nullptr || ! pStm->IsValid())
|
|
return false ;
|
|
vPLLoopPLExtr.clear() ;
|
|
|
|
#if DEBUG_BORDERS_BY_NORMALS
|
|
VT.emplace_back( pStm->Clone()) ;
|
|
VC.emplace_back( BLACK) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\ExtrLoops.nge") ;
|
|
#endif
|
|
|
|
// Recupero tutti i Loop della superficie
|
|
POLYLINEVECTOR vPLLoop ;
|
|
if ( ! pStm->GetLoops( vPLLoop))
|
|
return false ;
|
|
// Se Superficie Chiusa, non faccio nulla
|
|
if ( vPLLoop.empty())
|
|
return true ;
|
|
|
|
// Scorro i Loop
|
|
for ( PolyLine& PLLoop : vPLLoop) {
|
|
PolyLine PLLoopExtr ;
|
|
// Scorro i Punti
|
|
Point3d ptP ;
|
|
double dU ;
|
|
bool bFound = PLLoop.GetFirstUPoint( &dU, &ptP) ;
|
|
double dPar = -1 ;
|
|
while ( bFound) {
|
|
// Recupero il TriangoloEx associato al punto
|
|
Triangle3dEx Tria ;
|
|
if ( ! pStm->GetTriangle( lround( dU), Tria))
|
|
return false ;
|
|
// Cerco il vertice associato al punto
|
|
int nTriaP = 0 ;
|
|
for ( ; nTriaP < 3 ; ++ nTriaP) {
|
|
if ( AreSamePointApprox( Tria.GetP( nTriaP), ptP))
|
|
break ;
|
|
}
|
|
if ( nTriaP == 3)
|
|
return false ;
|
|
PLLoopExtr.AddUPoint( ++ dPar, ptP - Tria.GetVertexNorm( nTriaP) * dThick) ;
|
|
#if DEBUG_BORDERS_BY_NORMALS
|
|
IGeoVector3d* _vtN = CreateGeoVector3d() ; _vtN->Set( Tria.GetVertexNorm( nTriaP), ptP) ;
|
|
VT.emplace_back( _vtN) ;
|
|
Color _myCol = Color( abs( Tria.GetVertexNorm( nTriaP).x), abs( Tria.GetVertexNorm( nTriaP).y), abs( Tria.GetVertexNorm( nTriaP).z)) ;
|
|
VC.emplace_back( _myCol) ;
|
|
CurveLine _Line ; _Line.Set( ptP, ptP - Tria.GetVertexNorm( nTriaP) * dThick) ;
|
|
VT.emplace_back( _Line.Clone()) ;
|
|
VC.emplace_back( _myCol) ;
|
|
#endif
|
|
bFound = PLLoop.GetNextUPoint( &dU, &ptP) ;
|
|
}
|
|
#if DEBUG_BORDERS_BY_NORMALS
|
|
ICurveComposite* pC = CreateCurveComposite() ;
|
|
pC->FromPolyLine( PLLoopExtr) ;
|
|
VT.emplace_back( pC->Clone()) ;
|
|
VC.emplace_back( AQUA) ;
|
|
#endif
|
|
// Loop valido con almeno 4 punti
|
|
if ( PLLoopExtr.GetPointNbr() >= 4) {
|
|
// Primo e ultimo punto devono coincidere
|
|
PNTULIST& lPntU = PLLoopExtr.GetUPointList() ;
|
|
Point3d ptClose = Media( lPntU.front().first, lPntU.back().first) ;
|
|
lPntU.front().first = ptClose ;
|
|
lPntU.back().first = ptClose ;
|
|
vPLLoopPLExtr.emplace_back( make_pair( PLLoop, PLLoopExtr)) ;
|
|
}
|
|
}
|
|
|
|
#if DEBUG_BORDERS_BY_NORMALS
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\ExtrLoops.nge") ;
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// ---------------------------------- Analisi Adiacenze -----------------------
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per calcolare le Patch di una Superficie TriMesh
|
|
static bool
|
|
CalcSurfPatch( const ISurf* pSurf, double dLinTol, double dAngTol, SurfPatches& SrfPatches)
|
|
{
|
|
// Verifico che la Superficie sia valida
|
|
if ( pSurf == nullptr || ! pSurf->IsValid())
|
|
return false ;
|
|
|
|
// Recupero il Box della superficie ( ingrandito della tolleranza lineare)
|
|
if ( ! pSurf->GetLocalBBox( SrfPatches.BoxSurf))
|
|
return false ;
|
|
SrfPatches.BoxSurf.Expand( 2. * dLinTol + 10. * EPS_SMALL) ;
|
|
|
|
// Recupero i suoi Loops come PolyLines
|
|
POLYLINEVECTOR vPLBorders ;
|
|
if ( ! InsertLoopsOfSurf( pSurf, vPLBorders))
|
|
return false ;
|
|
|
|
// Dai Bordi, recupero le Patches
|
|
POLYLINEMATRIX matPLPatchBorders ;
|
|
if ( ! CalcPatches( vPLBorders, dAngTol, matPLPatchBorders))
|
|
return false ;
|
|
|
|
#if DEBUG_SURF_PATCHES
|
|
VT.clear() ; VC.clear() ;
|
|
VT.emplace_back( pSurf->Clone()) ;
|
|
VC.emplace_back( BLACK) ;
|
|
for ( int i = 0 ; i < ssize( matPLPatchBorders) ; ++ i) {
|
|
for ( int j = 0 ; j < ssize( matPLPatchBorders[i]) ; ++ j) {
|
|
CurveComposite CompoPatch ; CompoPatch.FromPolyLine( matPLPatchBorders[i][j]) ;
|
|
VT.emplace_back( CompoPatch.Clone()) ;
|
|
VC.emplace_back( Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.)) ;
|
|
}
|
|
}
|
|
//SaveGeoObj( VT, VC, "C:\\Temp\\Patches.nge") ;
|
|
#endif
|
|
|
|
// Definisco le Patches
|
|
// Recupero la Curva Composita associata per la Patch
|
|
// Memerizzo il Box3d della Patch
|
|
// Memorizzo i Parametri delle Patch sulle Curve Composite
|
|
SrfPatches.vCompoBorders.reserve( vPLBorders.size()) ;
|
|
SrfPatches.matCompoPatches.reserve( vPLBorders.size()) ;
|
|
SrfPatches.vBorderPatches.reserve( matPLPatchBorders.size()) ;
|
|
SrfPatches.matBox3dPatches.reserve( matPLPatchBorders.size()) ;
|
|
SrfPatches.vSetPatchPar.reserve( matPLPatchBorders.size()) ;
|
|
for ( int i = 0 ; i < ssize( matPLPatchBorders) ; ++ i) {
|
|
// Recupero il Bordo Corrente
|
|
const POLYLINEVECTOR& PLBorder = matPLPatchBorders[i] ;
|
|
// Recupero la Curva Composita di Bordo
|
|
SrfPatches.vCompoBorders.emplace_back( CurveComposite()) ;
|
|
SrfPatches.vCompoBorders.back().FromPolyLine( vPLBorders[i]) ;
|
|
// Inizializzo i Parametri
|
|
SrfPatches.vBorderPatches.emplace_back( SimpleBorder()) ;
|
|
SrfPatches.matBox3dPatches.emplace_back( BOXVECTOR{}) ;
|
|
SrfPatches.vSetPatchPar.emplace_back( set<double>()) ;
|
|
SrfPatches.matCompoPatches.emplace_back( COMPOVECTOR{}) ;
|
|
for ( int j = 0 ; j < ssize( PLBorder) ; ++ j) {
|
|
// Recupero la PolyLine associata alla Patch corrente
|
|
const PolyLine& PLPatch = PLBorder[j] ;
|
|
// Definizione della Patch Semplice
|
|
Point3d ptStart ; PLPatch.GetFirstPoint( ptStart) ;
|
|
if ( j == 0) {
|
|
double dUStart = 0. ;
|
|
SrfPatches.vCompoBorders.back().GetParamAtPoint( ptStart, dUStart) ;
|
|
SrfPatches.vCompoBorders.back().ChangeStartPoint( dUStart) ;
|
|
}
|
|
Point3d ptEnd ; PLPatch.GetLastPoint( ptEnd) ;
|
|
SrfPatches.vBorderPatches.back().emplace_back( ptStart, ptEnd, i, false) ;
|
|
SrfPatches.matCompoPatches.back().emplace_back( CurveComposite()) ;
|
|
SrfPatches.matCompoPatches.back().back().FromPolyLine( PLPatch) ;
|
|
// Box Della Patch
|
|
SrfPatches.matBox3dPatches.back().emplace_back( BBox3d()) ;
|
|
PLPatch.GetLocalBBox( SrfPatches.matBox3dPatches.back().back()) ;
|
|
SrfPatches.matBox3dPatches.back().back().Expand( 2. * dLinTol + 10. * EPS_SMALL) ;
|
|
// Parametri dU associati
|
|
double dPar = -1. ;
|
|
if ( j != ssize( matPLPatchBorders[i]) - 1)
|
|
SrfPatches.vCompoBorders.back().GetParamAtPoint( ptEnd, dPar, dLinTol) ;
|
|
else {
|
|
double dParS = 0. ;
|
|
SrfPatches.vCompoBorders.back().GetDomain( dParS, dPar) ;
|
|
}
|
|
SrfPatches.vSetPatchPar.back().insert( int( round( dPar))) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per individuare se un triangolo piccolo è trascurabile
|
|
static bool
|
|
CheckUnrelevantTriangle( const SurfTriMesh& Stm, int nCurrTria, int nTriaAdj, int nAdj, double dAngTol,
|
|
unordered_map<int, INTVECTOR>& mapAdjToAvoid, INTVECTOR& vNewTria)
|
|
{
|
|
vNewTria.clear() ; vNewTria.reserve( 2) ;
|
|
// Verifico che la TriMesh sia valida
|
|
if ( ! Stm.IsValid())
|
|
return false ;
|
|
|
|
// Verifico che il triangolo appartenga alla TriMesh
|
|
Triangle3d Tria, TriaAdj ;
|
|
if ( ! Stm.GetTriangle( nCurrTria, Tria) || ! Stm.GetTriangle( nTriaAdj, TriaAdj))
|
|
return false ;
|
|
|
|
// Verifico le sue adiacenze
|
|
int nUnrelAdjTriaIds[3] ;
|
|
if ( ! Stm.GetTriangleAdjacencies( nTriaAdj, nUnrelAdjTriaIds))
|
|
return false ;
|
|
int nInvAdj = 0 ;
|
|
for ( int nUnrelAdj = 0 ; nUnrelAdj < 3 ; ++ nUnrelAdj) {
|
|
// Se non esiste l'adiacenza, non faccio nulla
|
|
int nUnrelTriaAdj = nUnrelAdjTriaIds[nUnrelAdj] ;
|
|
if ( nUnrelTriaAdj == SVT_NULL)
|
|
continue ;
|
|
// Se l'Adiacenza è il triangolo di partenza, va scarata
|
|
if ( nUnrelTriaAdj == nCurrTria) {
|
|
nInvAdj = nUnrelAdj ;
|
|
continue ;
|
|
}
|
|
// Recupero il triangolo adiacente
|
|
Triangle3d TriaAdjUnrel ;
|
|
if ( ! Stm.GetTriangle( nUnrelTriaAdj, TriaAdjUnrel))
|
|
return false ;
|
|
// Se la normale è dentro alla tolleranza del triangolo di partenza, allora sarà
|
|
// fuori dalla tolleranza del triangolo di adiacenza corrente, quindi va scaratata
|
|
if ( Tria.GetN() * TriaAdjUnrel.GetN() > cos( ( dAngTol - EPS_ANG_SMALL) * DEGTORAD)) {
|
|
mapAdjToAvoid[nTriaAdj].push_back( nUnrelAdj) ;
|
|
int nIdAdjTriaId[3] ;
|
|
if ( ! Stm.GetTriangleAdjacencies( nUnrelTriaAdj, nIdAdjTriaId))
|
|
return false ;
|
|
for ( int nMyAdj = 0 ; nMyAdj < 3 ; ++ nMyAdj) {
|
|
if ( nIdAdjTriaId[nMyAdj] == nTriaAdj) {
|
|
mapAdjToAvoid[nUnrelTriaAdj].push_back( nMyAdj) ;
|
|
vNewTria.push_back( nUnrelTriaAdj) ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ! vNewTria.empty()) {
|
|
mapAdjToAvoid[nTriaAdj].push_back( nInvAdj) ;
|
|
mapAdjToAvoid[nCurrTria].push_back( nAdj) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per analisi del singolo triangolo
|
|
static bool
|
|
AnalyzeTriangle( const Triangle3d& Tria, const Triangle3d& TriaAdj, double dAngTol, int& nTriaClass)
|
|
{
|
|
// Verifico la validità del triangolo
|
|
if ( ! Tria.IsValid())
|
|
return false ;
|
|
nTriaClass = TriaClass::OK ;
|
|
|
|
// Verifico caso Small
|
|
double dArea = TriaAdj.GetArea() ;
|
|
if ( dArea < ( 70. * EPS_SMALL) * ( 70. * EPS_SMALL)) { // ~ 0.005
|
|
nTriaClass = TriaClass::SMALL ;
|
|
return true ;
|
|
}
|
|
|
|
bool bCap, bHat = false ;
|
|
// Verifico caso Cap
|
|
double dLen0 = Dist( TriaAdj.GetP( 0), TriaAdj.GetP( 1)) ;
|
|
double dLen1 = Dist( TriaAdj.GetP( 1), TriaAdj.GetP( 2)) ;
|
|
double dLen2 = Dist( TriaAdj.GetP( 2), TriaAdj.GetP( 0)) ;
|
|
double dLenMin = min( { dLen0, dLen1, dLen2}) ;
|
|
double dLenMax = max( { dLen0, dLen1, dLen2}) ;
|
|
double dRatio = ( dLenMin > EPS_SMALL ? dLenMax / dLenMin : INFINITO) ;
|
|
bCap = ( dRatio > 100.) ; // ~ 2 ordini di grandezza
|
|
if ( bCap) {
|
|
nTriaClass = TriaClass::CAP ;
|
|
return true ;
|
|
}
|
|
// Verifico caso Hat
|
|
double dH0 = ( dLen1 > EPS_SMALL ? ( ( 2. * dArea) / dLen1) : ( ( dLen0 + dLen2) / 2.)) ;
|
|
double dH1 = ( dLen2 > EPS_SMALL ? ( ( 2. * dArea) / dLen2) : ( ( dLen0 + dLen1) / 2.)) ;
|
|
double dH2 = ( dLen0 > EPS_SMALL ? ( ( 2. * dArea) / dLen0) : ( ( dLen1 + dLen2) / 2.)) ;
|
|
double dHMin = min( { dH0, dH1, dH2}) ;
|
|
double dAvgH = ( dH0 + dH1 + dH2) / 3. ;
|
|
double dRatioH = ( dAvgH > EPS_SMALL ? ( dHMin / dAvgH) : 0.) ;
|
|
double dAvgE = ( dLen0 + dLen1 + dLen2) / 3. ;
|
|
double dRatioE = ( dAvgE > EPS_SMALL ? ( dHMin / dAvgE) : 0.) ;
|
|
bHat = ( dRatioH < 1 / 100. || dRatioE < 1 / 100.) ;
|
|
if ( bHat)
|
|
nTriaClass = TriaClass::HAT ;
|
|
|
|
// Caso Flip
|
|
bool bFlipped = ( ( - Tria.GetN()) * TriaAdj.GetN() > cos( ( dAngTol - EPS_ANG_SMALL) * DEGTORAD)) ;
|
|
if ( bFlipped) {
|
|
nTriaClass = TriaClass::FLIPPED ;
|
|
return true ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per correzione dei triangoli
|
|
static bool
|
|
AdjustClassifiedTriangles( unordered_set<int>& setValidTria, const SurfTriMesh* pStm,
|
|
const unordered_map<int, int>& mapTriaClass, TRIA3DVECTOR& vAllTria)
|
|
{
|
|
// Se superficie non valida, errore
|
|
if ( pStm == nullptr || ! pStm->IsValid() || pStm->GetTriangleCount() == 0)
|
|
return false ;
|
|
// se non ho triangoli nel set, non faccio nulla
|
|
if ( setValidTria.empty())
|
|
return true ;
|
|
|
|
// se non ho una Mappa di classificazione, allora i triangoli sono già tutti validi e corretti
|
|
if ( mapTriaClass.empty()) {
|
|
vAllTria.reserve( setValidTria.size()) ;
|
|
for ( auto Iter = setValidTria.begin() ; Iter != setValidTria.end() ; ++ Iter) {
|
|
Triangle3d Tria ;
|
|
if ( ! pStm->GetTriangle( *Iter, Tria))
|
|
return false ;
|
|
vAllTria.emplace_back( Tria) ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
// Scorro gli elementi della Mappa
|
|
for ( auto &KeyVal : mapTriaClass) {
|
|
int nTria = KeyVal.first ;
|
|
int nTriaClass = KeyVal.second ;
|
|
// Recupero il Triangolo
|
|
Triangle3d Tria ;
|
|
if ( ! pStm->GetTriangle( nTria, Tria))
|
|
return false ;
|
|
|
|
// Se Triangolo Small, lo elimino
|
|
if ( nTriaClass == TriaClass::SMALL)
|
|
setValidTria.erase( nTria) ;
|
|
|
|
// Recupero le 3 adiacenze
|
|
int nIdAdjTriaId[3] ;
|
|
if ( ! pStm->GetTriangleAdjacencies( nTria, nIdAdjTriaId))
|
|
return false ;
|
|
// Controllo quali tra i miei triangoli devo analizzare
|
|
vector<pair<int, Triangle3d>> vMyAdjTria ; vMyAdjTria.reserve( 3) ;
|
|
for ( int nAdj = 0 ; nAdj < 3 ; ++ nAdj) {
|
|
int nTriaAdj = nIdAdjTriaId[nAdj] ;
|
|
if ( nTriaAdj == SVT_NULL ||
|
|
setValidTria.find( nTriaAdj) == setValidTria.end())
|
|
continue ;
|
|
Triangle3d myAdjTria ;
|
|
if ( ! pStm->GetTriangle( nTriaAdj, myAdjTria))
|
|
return false ;
|
|
vMyAdjTria.emplace_back( make_pair( nTriaAdj, myAdjTria)) ;
|
|
}
|
|
#if DEBUG_FACE_SEARCH_TRIA_MODIF
|
|
VT.clear() ; VC.clear() ;
|
|
VT.emplace_back( pStm->CloneTriangle( nTria)) ;
|
|
VC.emplace_back( RED) ;
|
|
for ( int i = 0 ; i < ssize( vMyAdjTria) ; ++ i) {
|
|
VT.emplace_back( pStm->CloneTriangle( vMyAdjTria[i].first)) ;
|
|
VC.emplace_back( GREEN) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\Vert0.nge") ;
|
|
#endif
|
|
|
|
// Se Triangolo CAP
|
|
if ( nTriaClass == TriaClass::CAP) {
|
|
// Se nessuna adiacenza valida, non faccio nulla
|
|
int nAdjTria = ssize( vMyAdjTria) ;
|
|
if ( nAdjTria == 0)
|
|
continue ;
|
|
// Se una sola,non faccio nulla
|
|
else if ( nAdjTria == 1)
|
|
continue ;
|
|
// Altrimenti
|
|
else {
|
|
// Recupero l'adiacenza più corta
|
|
int nShortestAdj = 0 ;
|
|
double dMinLen = INFINITO ;
|
|
for ( int nAdj = 0 ; nAdj < 3 ; ++ nAdj) {
|
|
double dEdgeLen = SqDist( Tria.GetP( nAdj), Tria.GetP( ( nAdj + 1) % 3)) ;
|
|
if ( dEdgeLen < dMinLen) {
|
|
dMinLen = dEdgeLen ;
|
|
nShortestAdj = nAdj ;
|
|
}
|
|
}
|
|
// Calcolo il punto medio di tale Edge, il più corto
|
|
Point3d ptA = Tria.GetP( nShortestAdj) ;
|
|
Point3d ptB = Tria.GetP( ( nShortestAdj + 1) % 3) ;
|
|
Point3d ptMid = Media( ptA, ptB) ;
|
|
// Cerco l'indice del vertice più vicino a ptA e ptB
|
|
int nVertA = -1, nVertB = -1 ;
|
|
for ( int nV = 0 ; ( nVertA == -1 || nVertB == -1) && nV < pStm->GetVertexCount() ; ++ nV) {
|
|
Point3d ptV ;
|
|
if ( ! pStm->GetVertex( nV, ptV))
|
|
return false ;
|
|
if ( AreSamePointEpsilon( ptV, ptA, 1e-6))
|
|
nVertA = nV ;
|
|
if ( AreSamePointEpsilon( ptV, ptB, 1e-6))
|
|
nVertB = nV ;
|
|
}
|
|
if ( nVertA == -1 || nVertB == -1)
|
|
continue ;
|
|
|
|
#if DEBUG_FACE_SEARCH_TRIA_MODIF
|
|
IGeoPoint3d* _ptA = CreateGeoPoint3d() ; _ptA->Set( ptA) ;
|
|
IGeoPoint3d* _ptB = CreateGeoPoint3d() ; _ptB->Set( ptB) ;
|
|
IGeoPoint3d* _ptM = CreateGeoPoint3d() ; _ptM->Set( ptMid) ;
|
|
VT.emplace_back( _ptA) ; VT.emplace_back( _ptB) ; VT.emplace_back( _ptM) ;
|
|
VC.emplace_back( FUCHSIA) ; VC.emplace_back( FUCHSIA) ; VC.emplace_back( FUCHSIA) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\Vert1.nge") ;
|
|
#endif
|
|
|
|
// Elimino il triangolo CAP
|
|
setValidTria.erase( nTria) ;
|
|
// Modifico tutti i triangoli attorno al Vertice A e B
|
|
INTVECTOR vTA, vTB ;
|
|
bool bCirc ;
|
|
if ( ! pStm->GetAllTriaAroundVertex( nVertA, vTA, bCirc) ||
|
|
! pStm->GetAllTriaAroundVertex( nVertB, vTB, bCirc))
|
|
return false ;
|
|
for ( const int& nVertTria : vTA) {
|
|
if ( nVertTria == SVT_NULL ||
|
|
setValidTria.find( nVertTria) == setValidTria.end())
|
|
continue ;
|
|
#if DEBUG_FACE_SEARCH_TRIA_MODIF
|
|
VT.emplace_back( pStm->CloneTriangle( nVertTria)) ;
|
|
VC.emplace_back( LIME) ;
|
|
#endif
|
|
Triangle3d newTria ;
|
|
if ( ! pStm->GetTriangle( nVertTria, newTria))
|
|
return false ;
|
|
for ( int j = 0 ; j < 3 ; ++ j) {
|
|
if ( AreSamePointApprox( newTria.GetP( j), ptA) ||
|
|
AreSamePointApprox( newTria.GetP( j), ptB)) {
|
|
newTria.SetP( j, ptMid) ;
|
|
break ;
|
|
}
|
|
}
|
|
setValidTria.erase( nVertTria) ;
|
|
vAllTria.emplace_back( newTria) ;
|
|
}
|
|
for ( const int& nVertTria : vTB) {
|
|
if ( nVertTria == SVT_NULL ||
|
|
setValidTria.find( nVertTria) == setValidTria.end())
|
|
continue ;
|
|
#if DEBUG_FACE_SEARCH_TRIA_MODIF
|
|
VT.emplace_back( pStm->CloneTriangle( nVertTria)) ;
|
|
VC.emplace_back( LIME) ;
|
|
#endif
|
|
Triangle3d newTria ;
|
|
if ( ! pStm->GetTriangle( nVertTria, newTria))
|
|
return false ;
|
|
for ( int j = 0 ; j < 3 ; ++ j) {
|
|
if ( AreSamePointApprox( newTria.GetP( j), ptA) ||
|
|
AreSamePointApprox( newTria.GetP( j), ptB)) {
|
|
newTria.SetP( j, ptMid) ;
|
|
break ;
|
|
}
|
|
}
|
|
setValidTria.erase( nVertTria) ;
|
|
vAllTria.emplace_back( newTria) ;
|
|
}
|
|
#if DEBUG_FACE_SEARCH_TRIA_MODIF
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\Vert2.nge") ;
|
|
#endif
|
|
}
|
|
}
|
|
// Se triangolo di tipo HAT
|
|
else if ( nTriaClass == TriaClass::HAT) {
|
|
// TODO ...
|
|
}
|
|
// Se Triangolo Invertito
|
|
else if ( nTriaClass == TriaClass::FLIPPED) { // TODO : Testare se capita
|
|
// 2 Possibilità :
|
|
// a) Triangolo Invertito senza altre intersezioni
|
|
// b) Triangolo Invertito senza altre intersezioni
|
|
|
|
// Se nessuna adiacenza valida, non faccio nulla
|
|
int nAdjTria = ssize( vMyAdjTria) ;
|
|
if ( nAdjTria == 0)
|
|
continue ;
|
|
// Altrimenti
|
|
else {
|
|
// Cerco il triangolo in cui cade il punto centrale
|
|
bool bPointIn = false ;
|
|
for ( int i = 0 ; ! bPointIn && i < nAdjTria ; ++ i) {
|
|
Triangle3d& TriaAdj = vMyAdjTria[i].second ;
|
|
Plane3d plAdjTria ; plAdjTria.Set( TriaAdj.GetP( 0), TriaAdj.GetN()) ;
|
|
for ( int nP = 0 ; nP < 3 ; ++ nP) {
|
|
Point3d ptTest = ProjectPointOnPlane( Tria.GetP( nP), plAdjTria) ;
|
|
if ( IsPointInsideTriangle( ptTest, TriaAdj, TriangleType::OPEN)) {
|
|
#if DEBUG_FACE_SEARCH_TRIA_MODIF
|
|
IGeoPoint3d* _pt = CreateGeoPoint3d() ;
|
|
_pt->Set( ptTest) ;
|
|
VT.emplace_back( _pt) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\Vert1.nge") ;
|
|
#endif
|
|
// Recupro il vertice più distante
|
|
Point3d ptA = Tria.GetP( ( nP + 1) % 3) ;
|
|
Point3d ptB = Tria.GetP( ( nP + 2) % 3) ;
|
|
for ( int nAdjP = 0 ; nAdjP < 3 ; ++ nAdjP) {
|
|
if ( ! AreSamePointApprox( TriaAdj.GetP( nAdjP), ptA) &&
|
|
! AreSamePointApprox( TriaAdj.GetP( nAdjP), ptB)) {
|
|
Triangle3d TriaA, TriaB ;
|
|
TriaA.Set( TriaAdj.GetP( nAdjP), TriaAdj.GetP( ( nAdjP + 1) % 3), ptTest) ;
|
|
TriaB.Set( TriaAdj.GetP( ( nAdjP + 2) % 3), TriaAdj.GetP( nAdjP), ptTest) ;
|
|
#if DEBUG_FACE_SEARCH_TRIA_MODIF
|
|
CurveComposite CompoA ;
|
|
CompoA.AddPoint( TriaA.GetP( 0)) ;
|
|
CompoA.AddLine( TriaA.GetP( 1)) ;
|
|
CompoA.AddLine( TriaA.GetP( 2)) ;
|
|
CompoA.Close() ;
|
|
CurveComposite CompoB ;
|
|
CompoB.AddPoint( TriaB.GetP( 0)) ;
|
|
CompoB.AddLine( TriaB.GetP( 1)) ;
|
|
CompoB.AddLine( TriaB.GetP( 2)) ;
|
|
CompoB.Close() ;
|
|
VT.emplace_back( CompoA.Clone()) ;
|
|
VC.emplace_back( FUCHSIA) ;
|
|
VT.emplace_back( CompoB.Clone()) ;
|
|
VC.emplace_back( FUCHSIA) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\Vert2.nge") ;
|
|
#endif
|
|
vAllTria.emplace_back( TriaA) ;
|
|
vAllTria.emplace_back( TriaB) ;
|
|
setValidTria.erase( nTria) ;
|
|
setValidTria.erase( nAdjTria) ;
|
|
// Se il triangolo diviso è l'unico di adiacenza
|
|
if ( nAdjTria == 1) {
|
|
// Inverto il triangolo corrente e aggiungo
|
|
Triangle3d TriaFlipped ;
|
|
TriaFlipped.Set( Tria.GetP( 0), Tria.GetP( 2), Tria.GetP( 1)) ;
|
|
vAllTria.emplace_back( TriaFlipped) ;
|
|
}
|
|
else {
|
|
for ( i = 0 ; i < nAdjTria ; ++ i) {
|
|
for ( int j = 0 ; j < 3 ; ++ j) {
|
|
if ( AreSamePointApprox( TriaAdj.GetP( j), Tria.GetP( nP))) {
|
|
TriaAdj.SetP( j, ptTest) ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
// Se non esiste un triangolo che contiene la proiezione del punto, inverto
|
|
if ( ! bPointIn) {
|
|
Tria.Set( Tria.GetP( 0), Tria.GetP( 2), Tria.GetP( 1)) ;
|
|
vAllTria.emplace_back( Tria) ;
|
|
setValidTria.erase( nTria) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Nel vettore dei triangoli nuovi, inserisco tutti i triangoli non scartati
|
|
for ( auto Iter = setValidTria.begin() ; Iter != setValidTria.end() ; ++ Iter) {
|
|
Triangle3d Tria ;
|
|
if ( ! pStm->GetTriangle( *Iter, Tria))
|
|
return false ;
|
|
vAllTria.emplace_back( Tria) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per classificare i triangoli all'interno di un Box
|
|
static bool
|
|
ClassifyTriangles( const SurfTriMesh& Stm, const INTVECTOR& vTBox, int nFirstTria, double dAngTol,
|
|
unordered_set<int>& setAvoidTria, unordered_set<int>& setNewTria,
|
|
unordered_map<int, int>& mapTriaClass)
|
|
{
|
|
// Collezione di triangoli già visitati
|
|
unordered_set<int> setTria ; setTria.reserve( vTBox.size()) ;
|
|
// Collezioni di triangoli da analizzare
|
|
INTVECTOR vTria ; vTria.reserve( ssize( vTBox) + 1) ;
|
|
vTria.push_back( nFirstTria) ;
|
|
// Collezioni di adiacenze da non riguardare
|
|
unordered_map<int, INTVECTOR> mapAdjToAvoid ; mapAdjToAvoid.reserve( ssize( vTBox) + 1) ;
|
|
|
|
// Finchè ho triangoli da visitare
|
|
while ( ! vTria.empty()) {
|
|
// Recupero il triangolo corrente
|
|
int nCurrTria = vTria.back() ;
|
|
// Elimino il triangolo corente
|
|
vTria.pop_back() ;
|
|
|
|
// Se triangolo da evitare, passo al successivo
|
|
if ( setAvoidTria.find( nCurrTria) != setAvoidTria.end())
|
|
continue ;
|
|
|
|
// Se triangolo mai visitato lo aggiungo tra quelli visitati, altrimenti passo al successivo
|
|
if ( setTria.find( nCurrTria) != setTria.end())
|
|
continue ;
|
|
setTria.insert( nCurrTria) ;
|
|
|
|
// Inserisco il triangolo corrente
|
|
setNewTria.insert( nCurrTria) ;
|
|
|
|
// Recupero il triangolo corrente e le sue adiacenze
|
|
int nTriaVertices[3] ;
|
|
if ( ! Stm.GetTriangle( nCurrTria, nTriaVertices))
|
|
return false ;
|
|
Triangle3d Tria ;
|
|
if ( ! Stm.GetTriangle( nCurrTria, Tria))
|
|
return false ;
|
|
int nIdAdjTriaId[3] ;
|
|
if ( ! Stm.GetTriangleAdjacencies( nCurrTria, nIdAdjTriaId))
|
|
return false ;
|
|
|
|
// Scorro le adiacenze
|
|
for ( int nAdj = 0 ; nAdj < 3 ; ++ nAdj) {
|
|
// Recupero l'indice del triangolo corrente
|
|
int nTriaAdj = nIdAdjTriaId[nAdj] ;
|
|
// Se adiacenza da scartare, la salto
|
|
auto mapIter = mapAdjToAvoid.find( nCurrTria) ;
|
|
if ( mapIter != mapAdjToAvoid.end()) {
|
|
bool bSkip = false ;
|
|
for ( int nAvoidAdj = 0 ; ! bSkip && nAvoidAdj < ssize( mapIter->second) ; ++ nAvoidAdj)
|
|
bSkip = ( mapIter->second[nAvoidAdj] == nAdj) ;
|
|
if ( bSkip) {
|
|
// Se presente, lo inserisco
|
|
vTria.push_back( nTriaAdj) ;
|
|
continue ;
|
|
}
|
|
}
|
|
// Se non ho adiacenza, non faccio nulla
|
|
if ( nTriaAdj == SVT_NULL)
|
|
continue ;
|
|
// Recupero il triangolo adiacente
|
|
Triangle3d TriaAdj ;
|
|
if ( ! Stm.GetTriangle( nTriaAdj, TriaAdj))
|
|
return false ;
|
|
// Se l'adiacenza non è interna al Box corrente, il triangolo rimane visitato
|
|
if ( find( vTBox.begin(), vTBox.end(), nTriaAdj) == vTBox.end())
|
|
setTria.insert( nTriaAdj) ;
|
|
// Se la normale è fuori dalla tolleranza
|
|
if ( Tria.GetN() * TriaAdj.GetN() < cos( ( dAngTol + EPS_ANG_SMALL) * DEGTORAD)) {
|
|
// Classifico il triangolo
|
|
int nTriaClass = TriaClass::OK ;
|
|
if ( ! AnalyzeTriangle( Tria, TriaAdj, dAngTol, nTriaClass))
|
|
return false ;
|
|
bool bUnrelTria = ( nTriaClass == TriaClass::FLIPPED ||
|
|
nTriaClass == TriaClass::CAP) ;
|
|
if ( bUnrelTria) {
|
|
INTVECTOR vNewTria ; vNewTria.reserve( 2) ;
|
|
if ( ! CheckUnrelevantTriangle( Stm, nCurrTria, nTriaAdj, nAdj, dAngTol, mapAdjToAvoid, vNewTria))
|
|
return false ;
|
|
// Se triangolo irrilevante, memorizzo tale informazione
|
|
if ( ! vNewTria.empty()) {
|
|
mapTriaClass[nTriaAdj] = nTriaClass ;
|
|
vTria.push_back( nTriaAdj) ;
|
|
vTria.push_back( vNewTria[0]) ;
|
|
if ( ssize( vNewTria) == 2)
|
|
vTria.push_back( vNewTria[1]) ;
|
|
continue ;
|
|
}
|
|
}
|
|
|
|
// Se tale non è già stato scartato
|
|
if ( find( setAvoidTria.begin(), setAvoidTria.end(), nTriaAdj) == setAvoidTria.end()) {
|
|
// Azzero i triangoli correnti e ricomincio l'algoritmo
|
|
setAvoidTria.insert( nTriaAdj) ;
|
|
vTria.clear() ;
|
|
vTria.push_back( nFirstTria) ;
|
|
setTria.clear() ;
|
|
mapAdjToAvoid.clear() ;
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.clear() ; VC.clear() ;
|
|
#endif
|
|
break ;
|
|
}
|
|
|
|
continue ;
|
|
}
|
|
// Inserisco il triangolo per la ricerca futura
|
|
vTria.push_back( nTriaAdj) ;
|
|
}
|
|
}
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
for ( auto Iter = setNewTria.begin() ; Iter != setNewTria.end() ; ++ Iter) {
|
|
VT.emplace_back( Stm.CloneTriangle( *Iter)) ;
|
|
VC.emplace_back( GREEN) ;
|
|
}
|
|
for ( auto Iter = setAvoidTria.begin() ; Iter != setAvoidTria.end() ; ++ Iter) {
|
|
VT.emplace_back( Stm.CloneTriangle( *Iter)) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\2.nge") ;
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per il recupero delle curve concatenate di bordo dai triangoli classificati
|
|
static bool
|
|
GetBordersByTriaInBox( const SurfTriMesh& Stm, const BBox3d& BBoxCurr, double dLinTol,
|
|
const unordered_set<int>& setNewTria, COMPOVECTOR& vCompoChain,
|
|
bool& bAmbiguous)
|
|
{
|
|
// I bordi Locali sono definiti dall'insieme degli Edges dei triangoli trovati come validi
|
|
BIPNTVECTOR vBiPnts ;
|
|
for ( auto Iter = setNewTria.begin() ; Iter != setNewTria.end() ; ++ Iter) {
|
|
int nIdVert[3] ;
|
|
if ( ! Stm.GetTriangle( *Iter, nIdVert))
|
|
return false ;
|
|
int nIdAdjTriaId[3] ;
|
|
if ( ! Stm.GetTriangleAdjacencies( *Iter, nIdAdjTriaId))
|
|
return false ;
|
|
for ( int nAdj = 0 ; nAdj < 3 ; ++ nAdj) {
|
|
int nTriaAdj = nIdAdjTriaId[nAdj] ;
|
|
if ( nTriaAdj == SVT_NULL ||
|
|
find( setNewTria.begin(), setNewTria.end(), nTriaAdj) == setNewTria.end()) {
|
|
vBiPnts.emplace_back( BIPOINT()) ;
|
|
if ( ! Stm.GetVertex( nIdVert[nAdj], vBiPnts.back().first) ||
|
|
! Stm.GetVertex( nIdVert[( nAdj + 1) % 3], vBiPnts.back().second))
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Per ogni Edge ricavato, conservo solo le parti interne al Box corrente
|
|
INTVECTOR vEdgeToSave ; vEdgeToSave.reserve( vBiPnts.size()) ;
|
|
for ( int nEdge = 0 ; nEdge < ssize( vBiPnts) ; ++ nEdge) {
|
|
Point3d& ptL1 = vBiPnts[nEdge].first ;
|
|
Point3d& ptL2 = vBiPnts[nEdge].second ;
|
|
Vector3d vtDir = ptL2 - ptL1 ;
|
|
vtDir.Normalize() ;
|
|
INTDBLVECTOR vInters ;
|
|
if ( IntersLineBox( ptL1, ptL2, BBoxCurr, vInters, true)) {
|
|
if ( ssize( vInters) == 2) {
|
|
vEdgeToSave.push_back( nEdge) ;
|
|
// Se edge sul bordo, lo spezzo
|
|
if ( vInters[1].first == ILBT_OUT)
|
|
ptL2 = ptL1 + ( vtDir * vInters[1].second) ;
|
|
if ( vInters[0].first == ILBT_IN)
|
|
ptL1 = ptL1 + ( vtDir * vInters[0].second) ;
|
|
}
|
|
}
|
|
}
|
|
unordered_set<int> setBiPntsToSave( vEdgeToSave.begin(), vEdgeToSave.end()) ;
|
|
int nCurr = 0 ;
|
|
for ( int i = 0 ; i < ssize( vBiPnts) ; ++ i) {
|
|
if ( setBiPntsToSave.count( i))
|
|
vBiPnts[nCurr ++] = vBiPnts[i] ;
|
|
}
|
|
vBiPnts.resize( nCurr) ;
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
for ( int i = 0 ; i < ssize( vBiPnts) ; ++ i) {
|
|
ICurveLine* pLine = CreateCurveLine() ;
|
|
pLine->Set( vBiPnts[i].first, vBiPnts[i].second) ;
|
|
VT.emplace_back( pLine) ;
|
|
VC.emplace_back( Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.)) ;
|
|
}
|
|
// SaveGeoObj( VT, VC, "C:\\Temp\\3.nge") ;
|
|
#endif
|
|
|
|
// Concateno i Segmenti interni al Box corrente
|
|
BOOLVECTOR vIndInChain ; vIndInChain.resize( vBiPnts.size(), false) ;
|
|
vCompoChain.reserve( vBiPnts.size()) ;
|
|
for ( int i = 0 ; i < ssize( vBiPnts) ; ++ i) {
|
|
// Se già in Chain, passo al successivo
|
|
if ( vIndInChain[i])
|
|
continue ;
|
|
vIndInChain[i] = true ;
|
|
// Recupero gli estremi correnti
|
|
Point3d ptCurrStart = vBiPnts[i].first ;
|
|
Point3d ptCurrEnd = vBiPnts[i].second ;
|
|
// Definisco la nuova curve composita di concatenamento
|
|
vCompoChain.emplace_back( CurveComposite()) ;
|
|
vCompoChain.back().AddPoint( ptCurrStart) ;
|
|
vCompoChain.back().AddLine( ptCurrEnd) ;
|
|
// Punti di controllo per Biforcazioni
|
|
PNTVECTOR vPtForks ; vPtForks.reserve( vBiPnts.size()) ;
|
|
for ( int j = 0 ; j < ssize( vBiPnts) ; ++ j) {
|
|
// Controllo un altro Edge non in Chain, recuperando i suoi estremi
|
|
if ( i == j || vIndInChain[j])
|
|
continue ;
|
|
Point3d& ptOtherStart = vBiPnts[j].first ;
|
|
Point3d& ptOtherEnd = vBiPnts[j].second ;
|
|
// Se concatenamento ammissibile
|
|
if ( AreSamePointEpsilon( ptCurrEnd, ptOtherStart, dLinTol)) {
|
|
// Se su un punto di Biforcazione, sono in un caso ambiguo
|
|
for ( const Point3d& ptFork : vPtForks) {
|
|
if ( AreSamePointApprox( ptFork, ptCurrEnd)) {
|
|
bAmbiguous = true ;
|
|
return true ;
|
|
}
|
|
}
|
|
vPtForks.emplace_back( ptCurrEnd) ;
|
|
// Altrimenti aggiorno i parametri di ricerca
|
|
vCompoChain.back().AddLine( ptOtherEnd) ;
|
|
ptCurrEnd = ptOtherEnd ;
|
|
vIndInChain[j] = true ;
|
|
j = 0 ;
|
|
}
|
|
else if ( AreSamePointEpsilon( ptCurrEnd, ptOtherEnd, dLinTol)) {
|
|
// Se su un punto di Biforcazione, sono in un caso ambiguo
|
|
for ( const Point3d& ptFork : vPtForks) {
|
|
if ( AreSamePointApprox( ptFork, ptCurrEnd)) {
|
|
bAmbiguous = true ;
|
|
return true ;
|
|
}
|
|
}
|
|
vPtForks.emplace_back( ptCurrEnd) ;
|
|
// Altrimenti aggiorno i parametri di ricerca
|
|
vCompoChain.back().AddLine( ptOtherStart) ;
|
|
ptCurrEnd = ptOtherStart ;
|
|
vIndInChain[j] = true ;
|
|
j = 0 ;
|
|
}
|
|
else if ( AreSamePointEpsilon( ptCurrStart, ptOtherEnd, dLinTol)) {
|
|
// Se su un punto di Biforcazione, sono in un caso ambiguo
|
|
for ( const Point3d& ptFork : vPtForks) {
|
|
if ( AreSamePointApprox( ptFork, ptCurrStart)) {
|
|
bAmbiguous = true ;
|
|
return true ;
|
|
}
|
|
}
|
|
vPtForks.emplace_back( ptCurrStart) ;
|
|
// Altrimenti aggiorno i parametri di ricerca
|
|
vCompoChain.back().AddLine( ptOtherStart, false) ;
|
|
ptCurrStart = ptOtherStart ;
|
|
vIndInChain[j] = true ;
|
|
j = 0 ;
|
|
}
|
|
else if ( AreSamePointEpsilon( ptCurrStart, ptOtherStart, dLinTol)) {
|
|
// Se su un punto di Biforcazione, sono in un caso ambiguo
|
|
for ( const Point3d& ptFork : vPtForks) {
|
|
if ( AreSamePointApprox( ptFork, ptCurrStart)) {
|
|
bAmbiguous = true ;
|
|
return true ;
|
|
}
|
|
}
|
|
vPtForks.emplace_back( ptCurrStart) ;
|
|
// Altrimenti aggiorno i parametri di ricerca
|
|
vCompoChain.back().AddLine( ptOtherEnd, false) ;
|
|
ptCurrStart = ptOtherEnd ;
|
|
vIndInChain[j] = true ;
|
|
j = 0 ;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
for ( int i = 0 ; i < ssize( vCompoChain) ; ++ i) {
|
|
VT.emplace_back( vCompoChain[i].Clone()) ;
|
|
VC.emplace_back( FUCHSIA) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\4.nge") ;
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per il calcolo dei punti medi tra due curve di Bordo
|
|
static bool
|
|
CalcMidPointForBorders( const CurveComposite& CompoA, const CurveComposite& CompoB,
|
|
const SurfTriMesh& Stm, const unordered_set<int>& setNewTria,
|
|
Point3d& ptMid, Point3d& ptMid1, int& nTria, int& nTria1)
|
|
{
|
|
// Se entrambe le curve non sono valide, errore
|
|
if ( ! CompoA.IsValid() && ! CompoB.IsValid())
|
|
return false ;
|
|
|
|
// Se entrambe le curve sono valide
|
|
if ( CompoA.IsValid() && CompoB.IsValid()) {
|
|
// Recupero ptMid1 e ptMid2 e i rispettivi triangoli di ricerca
|
|
Point3d ptA ; CompoA.GetStartPoint( ptA) ;
|
|
Point3d ptB ; CompoA.GetEndPoint( ptB) ;
|
|
Point3d ptC ; CompoB.GetStartPoint( ptC) ;
|
|
Point3d ptD ; CompoB.GetEndPoint( ptD) ;
|
|
if ( SqDist( ptA, ptD) < SqDist( ptA, ptC))
|
|
swap( ptC, ptD) ;
|
|
ptMid = Media( ptB, ptD) ;
|
|
ptMid1 = Media( ptA, ptC) ;
|
|
}
|
|
// Se una sola valida
|
|
else {
|
|
const CurveComposite& CurveX = ( CompoA.IsValid() ? CompoA : CompoB) ;
|
|
Point3d ptA ; CurveX.GetStartPoint( ptA) ;
|
|
Point3d ptD ; CurveX.GetEndPoint( ptD) ;
|
|
ptMid = Media( ptA, ptD) ;
|
|
ptMid1 = P_INVALID ;
|
|
nTria1 = SVT_NULL ;
|
|
}
|
|
// Recupero il triangolo a distanza minima dai due punti ricavati
|
|
double dSqDistMin = INFINITO ;
|
|
double dSqDistMin1 = INFINITO ;
|
|
for ( auto Iter = setNewTria.begin() ; Iter != setNewTria.end() ; ++ Iter) {
|
|
Triangle3d Tria ;
|
|
if ( ! Stm.GetTriangle( *Iter, Tria))
|
|
return false ;
|
|
double dSqDist = 0., dSqDist1 = 0. ;
|
|
DistPointTriangle( ptMid, Tria).GetSqDist( dSqDist) ;
|
|
if ( dSqDist < dSqDistMin) {
|
|
dSqDistMin = dSqDist ;
|
|
nTria = *Iter ;
|
|
}
|
|
if ( ptMid1.IsValid()) {
|
|
DistPointTriangle( ptMid1, Tria).GetSqDist( dSqDist1) ;
|
|
if ( dSqDist1 < dSqDistMin1) {
|
|
dSqDistMin1 = dSqDist1 ;
|
|
nTria1 = *Iter ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per l'analisi locale delle curve di bordo
|
|
static bool
|
|
CheckCurvesInBox( const SurfTriMesh& Stm, const COMPOVECTOR& vCompoChain, const Point3d& ptSel,
|
|
const unordered_set<int>& setNewTria, CurveComposite& CompoA,CurveComposite& CompoB,
|
|
Point3d& ptMid, Point3d& ptMid1, int& nTria, int& nTria1)
|
|
{
|
|
const double HALF_LEN = .5 ;
|
|
const double ANG_SPLIT = 50. ;
|
|
|
|
// Se non ho curve, errore
|
|
if ( vCompoChain.empty())
|
|
return false ;
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
for ( int _i = 0 ; _i < ssize( vCompoChain) ; ++ _i) {
|
|
VT.emplace_back( vCompoChain[_i].Clone()) ;
|
|
VC.emplace_back( PURPLE) ;
|
|
}
|
|
#endif
|
|
|
|
// Recupero le due curve di riferimento
|
|
CurveComposite CompoATmp, CompoBTmp ;
|
|
|
|
// Se ho una sola curva, allora i bordi si sono uniti
|
|
if ( ssize( vCompoChain) == 1) {
|
|
// Approssimo la curva in Patches
|
|
PolyLine PL ;
|
|
if ( ! vCompoChain[0].ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_SPECIAL, PL))
|
|
return false ;
|
|
POLYLINEMATRIX matPL ;
|
|
if ( ! CalcPatches( { PL}, ANG_SPLIT, matPL) && ! matPL.empty() && ! matPL[0].empty())
|
|
return false ;
|
|
CompoATmp.FromPolyLine( matPL[0].front()) ;
|
|
CompoBTmp.FromPolyLine( matPL[0].back()) ;
|
|
}
|
|
// Se due curve di bordo
|
|
else if ( ssize( vCompoChain) == 2) {
|
|
CompoATmp = vCompoChain[0] ;
|
|
CompoBTmp = vCompoChain[1] ;
|
|
}
|
|
// Se più di due, cerco le due curve più vicine
|
|
else if ( ssize( vCompoChain) > 2) {
|
|
INTDBLVECTOR vIndSqDist ; vIndSqDist.reserve( vCompoChain.size()) ;
|
|
for ( int nCrv = 0 ; nCrv < ssize( vCompoChain) ; ++ nCrv) {
|
|
vIndSqDist.emplace_back( make_pair( nCrv, 0.)) ;
|
|
if ( ! DistPointCurve( ptSel, vCompoChain[nCrv]).GetSqDist( vIndSqDist.back().second))
|
|
return false ;
|
|
}
|
|
sort( vIndSqDist.begin(), vIndSqDist.end(), []( const INTDBL& A, const INTDBL& B) {
|
|
return ( A.second < B.second) ;
|
|
}) ;
|
|
CompoATmp = vCompoChain[vIndSqDist[0].first] ;
|
|
CompoBTmp = vCompoChain[vIndSqDist[1].first] ;
|
|
}
|
|
|
|
// Entrambe le curve sono chiuse, non devo fare nulla
|
|
if ( CompoATmp.IsClosed() && CompoBTmp.IsClosed()) {
|
|
CompoA = CompoATmp ;
|
|
CompoB = CompoBTmp ;
|
|
return true ;
|
|
}
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.emplace_back( CompoATmp.Clone()) ;
|
|
VC.emplace_back( WHITE) ;
|
|
VT.emplace_back( CompoBTmp.Clone()) ;
|
|
VC.emplace_back( WHITE) ;
|
|
#endif
|
|
|
|
// Definisco le due curve di riferimento
|
|
double dUA = 0., dUB = 0. ;
|
|
int nFlag = 0 ;
|
|
if ( ! DistPointCurve( ptSel, CompoATmp).GetParamAtMinDistPoint( 0., dUA, nFlag) ||
|
|
! DistPointCurve( ptSel, CompoBTmp).GetParamAtMinDistPoint( 0., dUB, nFlag))
|
|
return false ;
|
|
// Se le curve sono chiuse devo fare attenzione
|
|
if ( CompoATmp.IsClosed()) {
|
|
// Se lunghezza inferiore del doppio della semilunghezza, errore ( curva troppo piccola per essere lavorata)
|
|
double dLenA ;
|
|
if ( ! CompoATmp.GetLength( dLenA) || dLenA < 2. * HALF_LEN + EPS_SMALL)
|
|
return false ;
|
|
// Cambio il punto inziale della curva
|
|
CompoATmp.ChangeStartPoint( dUA) ;
|
|
double dUS, dUE ;
|
|
if ( ! CompoATmp.GetDomain( dUS, dUE))
|
|
return false ;
|
|
CompoATmp.ChangeStartPoint( ( dUS + dUE) / 2.) ;
|
|
if ( ! DistPointCurve( ptSel, CompoATmp).GetParamAtMinDistPoint( 0., dUA, nFlag))
|
|
return false ;
|
|
}
|
|
if ( CompoBTmp.IsClosed()) {
|
|
// Se lunghezza inferiore del doppio della semilunghezza, errore ( curva troppo piccola per essere lavorata)
|
|
double dLenB ;
|
|
if ( ! CompoBTmp.GetLength( dLenB) || dLenB < 2. * HALF_LEN + EPS_SMALL)
|
|
return false ;
|
|
CompoBTmp.ChangeStartPoint( dUB) ;
|
|
double dUS, dUE ;
|
|
if ( ! CompoBTmp.GetDomain( dUS, dUE))
|
|
return false ;
|
|
CompoBTmp.ChangeStartPoint( ( dUS + dUE) / 2.) ;
|
|
if ( ! DistPointCurve( ptSel, CompoBTmp).GetParamAtMinDistPoint( 0., dUB, nFlag))
|
|
return false ;
|
|
}
|
|
// Recupero la lunghezza della curva e la lunghezza in tali parametri
|
|
double dULenA, dULenB, dLenA, dLenB ;
|
|
if ( ! CompoATmp.GetLengthAtParam( dUA, dULenA) ||
|
|
! CompoBTmp.GetLengthAtParam( dUB, dULenB) ||
|
|
! CompoATmp.GetLength( dLenA) ||
|
|
! CompoBTmp.GetLength( dLenB))
|
|
return false ;
|
|
// Recupero i due tratti di curva per l'analisi
|
|
double dLenMinA = Clamp( dULenA - HALF_LEN, 0., dLenA) ;
|
|
double dLenMaxA = Clamp( dULenA + HALF_LEN, 0., dLenA) ;
|
|
double dLenMinB = Clamp( dULenB - HALF_LEN, 0., dLenB) ;
|
|
double dLenMaxB = Clamp( dULenB + HALF_LEN, 0., dLenB) ;
|
|
// Recupero i parametri e le curve
|
|
double dUAS, dUAE, dUBS, dUBE ;
|
|
if ( ! CompoATmp.GetParamAtLength( dLenMinA, dUAS) ||
|
|
! CompoATmp.GetParamAtLength( dLenMaxA, dUAE) ||
|
|
! CompoBTmp.GetParamAtLength( dLenMinB, dUBS) ||
|
|
! CompoBTmp.GetParamAtLength( dLenMaxB, dUBE))
|
|
return false ;
|
|
PtrOwner<ICurve> pCrvATmp( CompoATmp.CopyParamRange( dUAS, dUAE)) ;
|
|
PtrOwner<ICurve> pCrvBTmp( CompoBTmp.CopyParamRange( dUBS, dUBE)) ;
|
|
CompoA.CopyFrom( pCrvATmp) ;
|
|
CompoB.CopyFrom( pCrvBTmp) ;
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.emplace_back( CompoA.Clone()) ;
|
|
VC.emplace_back( RED) ;
|
|
VT.emplace_back( CompoB.Clone()) ;
|
|
VC.emplace_back( RED) ;
|
|
#endif
|
|
|
|
// Recupero ptMid1 e ptMid2 e i rispettivi triangoli di ricerca
|
|
if ( ! CalcMidPointForBorders( CompoA, CompoB, Stm, setNewTria, ptMid, ptMid1, nTria, nTria1))
|
|
return false ;
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
IGeoPoint3d* _ptMid = CreateGeoPoint3d() ; _ptMid->Set( ptMid) ;
|
|
VT.emplace_back( _ptMid) ;
|
|
VC.emplace_back( AQUA) ;
|
|
if ( ptMid1.IsValid()) {
|
|
IGeoPoint3d* _ptMid1 = CreateGeoPoint3d() ; _ptMid1->Set( ptMid1) ;
|
|
VT.emplace_back( _ptMid1) ;
|
|
VC.emplace_back( AQUA) ;
|
|
}
|
|
VT.emplace_back( Stm.CloneTriangle( nTria)) ;
|
|
VC.emplace_back( LIME) ;
|
|
if ( nTria1 != SVT_NULL) {
|
|
VT.emplace_back( Stm.CloneTriangle( nTria1)) ;
|
|
VC.emplace_back( LIME) ;
|
|
}
|
|
//SaveGeoObj( VT, VC, "C:\\Temp\\5.nge") ;
|
|
#endif
|
|
|
|
// I punti, le curve e i triangoli ricavati, devono essere validi
|
|
return ( CompoA.IsValid() && CompoB.IsValid() && ptMid.IsValid() && ptMid1.IsValid() &&
|
|
nTria != SVT_NULL && nTria1 != SVT_NULL) ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per aggiornare il Bordo mediante adiacenze di triangoli
|
|
static bool
|
|
UpdateBorderByTriaAdj( CurveComposite& Compo, double dLinTol, const COMPOVECTOR& vCompoChain,
|
|
bool& bUpdate)
|
|
{
|
|
bUpdate = false ;
|
|
// Se Curva corrente non valida, non devo aggiornare nulla
|
|
if ( ! Compo.IsValid())
|
|
return true ;
|
|
// Se le Compo di concatenazione non sono valide, errore
|
|
for ( const CurveComposite& CompoChain : vCompoChain) {
|
|
if ( ! CompoChain.IsValid())
|
|
return false ;
|
|
}
|
|
|
|
// Recupero gli estremi della curva corrente
|
|
Point3d ptStart ; Compo.GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; Compo.GetEndPoint( ptEnd) ;
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.clear() ; VC.clear() ;
|
|
VT.emplace_back( Compo.Clone()) ;
|
|
VC.emplace_back( RED) ;
|
|
for ( const CurveComposite& CompoChain : vCompoChain) {
|
|
VT.emplace_back( CompoChain.Clone()) ;
|
|
VC.emplace_back( AQUA) ;
|
|
}
|
|
#endif
|
|
|
|
// Scorro le curve di concatenazione
|
|
bool bUpdateStart = false ;
|
|
bool bUpdateEnd = false ;
|
|
for ( const CurveComposite& CompoChain : vCompoChain) {
|
|
// Verifico gli estremi della curva corrente
|
|
bool bMyUpdateStart = ( CompoChain.IsPointOn( ptStart, dLinTol)) ;
|
|
bool bMyUpdateEnd = ( CompoChain.IsPointOn( ptEnd, dLinTol)) ;
|
|
if ( bMyUpdateStart || bMyUpdateEnd) {
|
|
// Recupero il dominio della curva di concatenzione
|
|
double dUS = 0., dUE = 0. ;
|
|
if ( ! CompoChain.GetDomain( dUS, dUE))
|
|
return false ;
|
|
// Recupero gli Estremi della curva di concatenazione
|
|
Point3d ptS ; CompoChain.GetStartPoint( ptS) ;
|
|
Point3d ptE ; CompoChain.GetEndPoint( ptE) ;
|
|
// Se solo tratto antecedente valido
|
|
if ( bMyUpdateStart && ! bMyUpdateEnd) {
|
|
double dUBreak = 0. ;
|
|
if ( ! CompoChain.GetParamAtPoint( ptStart, dUBreak, dLinTol))
|
|
return false ;
|
|
CurveComposite CompoPrev ;
|
|
if ( Compo.IsPointOn( ptS) && ! Compo.IsPointOn( ptE)) {
|
|
PtrOwner<ICurve> pCrv( CompoChain.CopyParamRange( dUBreak, dUE)) ;
|
|
CompoPrev.CopyFrom( pCrv) ;
|
|
CompoPrev.Invert() ;
|
|
}
|
|
else if ( Compo.IsPointOn( ptE) && ! Compo.IsPointOn( ptS)) {
|
|
PtrOwner<ICurve> pCrv( CompoChain.CopyParamRange( dUS, dUBreak)) ;
|
|
CompoPrev.CopyFrom( pCrv) ;
|
|
}
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.emplace_back( CompoPrev.Clone()) ;
|
|
VC.emplace_back( LIME) ;
|
|
#endif
|
|
bUpdateStart = ( Compo.AddCurve( CompoPrev, false, dLinTol)) ;
|
|
}
|
|
// Se solo tratto successivo valido
|
|
else if ( ! bMyUpdateStart && bMyUpdateEnd) {
|
|
double dUBreak = 0. ;
|
|
if ( ! CompoChain.GetParamAtPoint( ptEnd, dUBreak, dLinTol))
|
|
return false ;
|
|
CurveComposite CompoAft ;
|
|
if ( Compo.IsPointOn( ptS) && ! Compo.IsPointOn( ptE)) {
|
|
PtrOwner<ICurve> pCrv( CompoChain.CopyParamRange( dUBreak, dUE)) ;
|
|
CompoAft.CopyFrom( pCrv) ;
|
|
}
|
|
else if ( Compo.IsPointOn( ptE) && ! Compo.IsPointOn( ptS)) {
|
|
PtrOwner<ICurve> pCrv( CompoChain.CopyParamRange( dUS, dUBreak)) ;
|
|
CompoAft.CopyFrom( pCrv) ;
|
|
CompoAft.Invert() ;
|
|
}
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.emplace_back( CompoAft.Clone()) ;
|
|
VC.emplace_back( LIME) ;
|
|
#endif
|
|
bUpdateEnd = Compo.AddCurve( CompoAft, true, dLinTol) ;
|
|
}
|
|
// Se entrambi i tratti sono validi
|
|
else {
|
|
double dUBreak = 0., dUBreak1 = 0. ;
|
|
if ( ! CompoChain.GetParamAtPoint( ptStart, dUBreak, dLinTol) ||
|
|
! CompoChain.GetParamAtPoint( ptEnd, dUBreak1, dLinTol))
|
|
return false ;
|
|
if ( ! Compo.IsPointOn( ptS) && ! Compo.IsPointOn( ptE)) {
|
|
CurveComposite CompoPrev ;
|
|
CurveComposite CompoAft ;
|
|
if ( dUBreak < dUBreak1) {
|
|
PtrOwner<ICurve> pCrvPrev( CompoChain.CopyParamRange( dUS, dUBreak)) ;
|
|
PtrOwner<ICurve> pCrvAft( CompoChain.CopyParamRange( dUBreak1, dUE)) ;
|
|
CompoPrev.CopyFrom( pCrvPrev) ;
|
|
CompoAft.CopyFrom( pCrvAft) ;
|
|
}
|
|
else {
|
|
PtrOwner<ICurve> pCrvPrev( CompoChain.CopyParamRange( dUBreak, dUE)) ;
|
|
CompoPrev.CopyFrom( pCrvPrev) ;
|
|
CompoPrev.Invert() ;
|
|
PtrOwner<ICurve> pCrvAft( CompoChain.CopyParamRange( dUS, dUBreak1)) ;
|
|
CompoAft.CopyFrom( pCrvAft) ;
|
|
CompoAft.Invert() ;
|
|
}
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.emplace_back( CompoPrev.Clone()) ;
|
|
VC.emplace_back( LIME) ;
|
|
VT.emplace_back( CompoAft.Clone()) ;
|
|
VC.emplace_back( LIME) ;
|
|
#endif
|
|
bUpdateStart = ( Compo.AddCurve( CompoPrev, false, dLinTol)) ;
|
|
bUpdateEnd = ( Compo.AddCurve( CompoAft, true, dLinTol)) ;
|
|
}
|
|
else if ( Compo.IsPointOn( ptS) && Compo.IsPointOn( ptE)) {
|
|
CurveComposite CompoSingle ;
|
|
if ( dUBreak < dUBreak1) {
|
|
PtrOwner<ICurve> pCrv( CompoChain.CopyParamRange( dUBreak, dUBreak1)) ;
|
|
CompoSingle.CopyFrom( pCrv) ;
|
|
CompoSingle.Invert() ;
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.emplace_back( CompoSingle.Clone()) ;
|
|
VC.emplace_back( LIME) ;
|
|
#endif // false
|
|
if ( Compo.AddCurve( CompoSingle, true, dLinTol)) {
|
|
bUpdateStart = true ;
|
|
bUpdateEnd = true ;
|
|
}
|
|
}
|
|
else {
|
|
PtrOwner<ICurve> pCrv( CompoChain.CopyParamRange( dUBreak1, dUBreak)) ;
|
|
CompoSingle.CopyFrom( pCrv) ;
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.emplace_back( CompoSingle.Clone()) ;
|
|
VC.emplace_back( LIME) ;
|
|
#endif // false
|
|
if ( Compo.AddCurve( CompoSingle, true, dLinTol)) {
|
|
bUpdateStart = true ;
|
|
bUpdateEnd = true ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
//SaveGeoObj( VT, VC, "C:\\Temp\\6.nge") ;
|
|
#endif
|
|
bUpdate = ( bUpdateStart || bUpdateEnd) ;
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per controllare le curve di Bordo all'interno del Box
|
|
static bool
|
|
CheckBordersInBox( CurveComposite& CompoA, CurveComposite& CompoB,
|
|
const SurfTriMesh& Stm, const unordered_set<int>& setAvoidTria,
|
|
double dAngTol, double dLinTol, double dSize, double dSizeTol, bool& bBreak)
|
|
{
|
|
bBreak = false ;
|
|
// Verifico la validità delle Curve
|
|
bool bValidA = ( CompoA.IsValid()) ;
|
|
bool bValidB = ( CompoB.IsValid()) ;
|
|
|
|
// Se nessuna curva valida, errore
|
|
if ( ! bValidA && ! bValidB)
|
|
return false ;
|
|
|
|
// Recupero i due tratti di curva in analisi e le loro proprietà
|
|
CurveComposite myCompoA, myCompoB ;
|
|
// NB. la parametrizzazione deve avvenire per lunghezza, non per parametro
|
|
double dLenA = 0., dLenB = 0. ;
|
|
double dLenStartA = 0., dLenStartB = 0., dLenEndA = 0., dLenEndB = 0. ;
|
|
|
|
// Se entrambe le curve sono valide
|
|
if ( bValidA && bValidB) {
|
|
myCompoA = CompoA ;
|
|
myCompoB = CompoB ;
|
|
// Recupero la lunghezza delle due curve
|
|
if ( ! myCompoA.GetLength( dLenA) || ! myCompoB.GetLength( dLenB))
|
|
return false ;
|
|
// Recupero la lunghezza di Analisi scorrendo le curve :
|
|
// - per CompoA devo indietreggiare
|
|
// - per CompoB devo avanzare
|
|
dLenStartA = Clamp( dLenA - dSize - dSizeTol, 0., dLenA) ;
|
|
dLenEndB = Clamp( dSize + dSizeTol, 0., dLenB) ;
|
|
// Se curve troppo corte non le analizzo
|
|
if ( dLenA - dLenStartA < dSizeTol || dLenEndB < dSizeTol)
|
|
return true ;
|
|
dLenEndA = dLenA ;
|
|
dLenStartB = 0. ;
|
|
}
|
|
// Se una delle due valida
|
|
else {
|
|
// Recupero la myCompoA come estremo finale e myCompoB come estremo iniziale
|
|
const CurveComposite& CompoX = ( CompoA.IsValid() ? CompoA : CompoB) ;
|
|
double dLen ;
|
|
if ( ! CompoX.GetLength( dLen))
|
|
return false ;
|
|
if ( dLen < 2. * ( dSize + dSizeTol) + 5. * EPS_SMALL)
|
|
return true ;
|
|
double dUS, dUE, dUStartA, dUEndB ;
|
|
if ( ! CompoX.GetDomain( dUS, dUE) ||
|
|
! CompoX.GetParamAtLength( dLen - dSize - dSizeTol - 2. * EPS_SMALL, dUStartA) ||
|
|
! CompoX.GetParamAtLength( dSize + dSizeTol + 2. * EPS_SMALL, dUEndB))
|
|
return false ;
|
|
// Per sicurezza [ migliorabile se stabile]
|
|
PtrOwner<ICurve> pCrvA( CompoX.CopyParamRange( dUStartA, dUE)) ;
|
|
PtrOwner<ICurve> pCrvB( CompoX.CopyParamRange( dUS, dUEndB)) ;
|
|
if ( ! myCompoA.CopyFrom( pCrvA) || ! myCompoB.CopyFrom( pCrvB))
|
|
return false ;
|
|
dLenStartA = 0. ;
|
|
dLenStartB = 0. ;
|
|
if ( ! myCompoA.GetLength( dLenEndA) || ! myCompoB.GetLength( dLenEndB))
|
|
return false ;
|
|
dLenA = dLenEndA ;
|
|
dLenB = dLenEndB ;
|
|
}
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.clear() ; VC.clear() ;
|
|
// Disegno simbolico
|
|
if ( CompoA.IsValid()) {
|
|
VT.emplace_back( CompoA.Clone()) ;
|
|
VC.emplace_back( AQUA) ;
|
|
}
|
|
if ( CompoB.IsValid()) {
|
|
VT.emplace_back( CompoB.Clone()) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
}
|
|
//SaveGeoObj( VT, VC, "C:\\Temp\\Divergent.nge") ;
|
|
#endif
|
|
|
|
// Campiono le Curve alla ricerca di restrizioni
|
|
const int MAX_SAMPLE = 5 ;
|
|
const int MAX_OUT_SAMPLE = 2 ; // Per ignorare le piccole imperfezioni
|
|
int nCountOut = 0 ;
|
|
bool bExpandBox = false ;
|
|
for ( int i = 0 ; i <= MAX_SAMPLE ; ++ i) {
|
|
// Recupero i parametri su tale Curva
|
|
double dLenUA = Clamp( ( dLenStartA + i * ( dLenEndA - dLenStartA) / MAX_SAMPLE), EPS_SMALL, dLenA - EPS_SMALL) ;
|
|
double dLenUB = Clamp( ( dLenEndB - i * ( dLenEndB - dLenStartB) / MAX_SAMPLE), EPS_SMALL, dLenB - EPS_SMALL) ;
|
|
// Recupero i punti associati e la loro distanza
|
|
double dUA, dUB ;
|
|
if ( ! myCompoA.GetParamAtLength( dLenUA, dUA) || ! myCompoB.GetParamAtLength( dLenUB, dUB))
|
|
return false ;
|
|
Point3d ptA, ptB ;
|
|
if ( ! myCompoA.GetPointD1D2( dUA, ICurve::FROM_MINUS, ptA) ||
|
|
! myCompoB.GetPointD1D2( dUB, ICurve::FROM_PLUS, ptB))
|
|
return false ;
|
|
if ( SqDist( ptA, ptB) < ( dSize - dSizeTol) * ( dSize - dSizeTol))
|
|
++ nCountOut ;
|
|
else
|
|
nCountOut = 0 ;
|
|
#if DEBUG_FACE_SEARCH
|
|
CurveLine _Line ; _Line.Set( ptA, ptB) ;
|
|
VT.emplace_back( _Line.Clone()) ;
|
|
VC.emplace_back( nCountOut == 0 ? LIME : RED) ;
|
|
#endif
|
|
if ( nCountOut >= MAX_OUT_SAMPLE) {
|
|
bExpandBox = true ;
|
|
break ;
|
|
}
|
|
}
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
//SaveGeoObj( VT, VC, "C:\\Temp\\Divergent.nge") ;
|
|
#endif
|
|
|
|
// Se rilevata restrizione
|
|
if ( bExpandBox) {
|
|
// Recupero i due tratti lineari in tangenza
|
|
Vector3d vtA, vtB ;
|
|
if ( ! myCompoA.GetEndDir( vtA) || ! myCompoB.GetStartDir( vtB))
|
|
return false ;
|
|
Point3d ptA, ptB ;
|
|
if ( ! myCompoA.GetEndPoint( ptA) || ! myCompoB.GetStartPoint( ptB))
|
|
return false ;
|
|
// Cerco il punto a minima distanza tra esse ( suppongo che le linee tendano a convergere
|
|
// verso lo spigolo della striscia in analisi)
|
|
Point3d ptMinDistA, ptMinDistB ;
|
|
if ( ! DistLineLine( ptA, vtA, 1., ptB, - vtB, 1., false, false).GetMinDistPoints( ptMinDistA, ptMinDistB))
|
|
return false ;
|
|
Point3d ptInt = Media( ptMinDistA, ptMinDistB) ;
|
|
// Definisco un Box centrato nel punto medio di taglio tra CompoA e CompoB sul Box
|
|
BBox3d BBoxCheck ;
|
|
Point3d ptCenter = Media( ptA, ptB) ;
|
|
BBoxCheck.Add( ptCenter) ; // dSize / cos( 60deg)
|
|
BBoxCheck.Expand( dSizeTol + min( Dist( ptCenter, ptInt), 2. * dSize)) ;
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
CurveLine _LineA, _LineB ;
|
|
_LineA.Set( ptA, ptA + 1e2 * vtA) ;
|
|
_LineB.Set( ptB, ptB - 1e2 * vtB) ;
|
|
VT.emplace_back( _LineA.Clone()) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
VT.emplace_back( _LineB.Clone()) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
CurveComposite pCompoBox ;
|
|
pCompoBox.AddPoint( BBoxCheck.GetMin()) ;
|
|
pCompoBox.AddLine( BBoxCheck.GetMin() + BBoxCheck.GetDimX() * X_AX) ;
|
|
pCompoBox.AddLine( BBoxCheck.GetMax() - BBoxCheck.GetDimZ() * Z_AX) ;
|
|
pCompoBox.AddLine( BBoxCheck.GetMin() + BBoxCheck.GetDimY() * Y_AX) ;
|
|
pCompoBox.Close() ;
|
|
pCompoBox.SetExtrusion( Z_AX) ;
|
|
pCompoBox.SetThickness( BBoxCheck.GetDimZ()) ;
|
|
VT.emplace_back( pCompoBox.Clone()) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\Divergent.nge") ;
|
|
#endif
|
|
|
|
// Determino le due curve all'interno di questo Box
|
|
INTVECTOR vTBox ;
|
|
if ( ! Stm.GetAllTriaOverlapBox( BBoxCheck, vTBox) || vTBox.empty())
|
|
return false ;
|
|
double dMinSqDist = INFINITO ;
|
|
int nTria = 0 ;
|
|
for ( int nT = 0 ; nT < ssize( vTBox) ; ++ nT) {
|
|
Triangle3d Tria ;
|
|
if ( ! Stm.GetTriangle( vTBox[nT], Tria))
|
|
return false ;
|
|
double dSqDist = 0. ;
|
|
if ( DistPointTriangle( ptCenter, Tria).GetSqDist( dSqDist) && dSqDist < dMinSqDist) {
|
|
dMinSqDist = dSqDist ;
|
|
nTria = vTBox[nT] ;
|
|
}
|
|
}
|
|
unordered_set<int> MysetAvoidTria( setAvoidTria) ;
|
|
unordered_set<int> MysetNewTria ;
|
|
unordered_map<int, int> MymapTriaClass ;
|
|
if ( ! ClassifyTriangles( Stm, vTBox, nTria, dAngTol, MysetAvoidTria, MysetNewTria, MymapTriaClass))
|
|
return false ;
|
|
COMPOVECTOR vCompoChain ;
|
|
if ( ! GetBordersByTriaInBox( Stm, BBoxCheck, dLinTol, MysetNewTria, vCompoChain, bBreak))
|
|
return false ;
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
for ( CurveComposite& Compo : vCompoChain) {
|
|
VT.emplace_back( Compo.Clone()) ;
|
|
VC.emplace_back( FUCHSIA) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\Divergent.nge") ;
|
|
#endif
|
|
|
|
// Se ottengo una sola curva, allora la striscia si è chiusa
|
|
if ( ssize( vCompoChain) <= 1)
|
|
return true ;
|
|
// Se ottengo ancora due curve, allora la striscia non rispetta lo spessore minimo
|
|
bBreak = ( ssize( vCompoChain) >= 2) ;
|
|
if ( bBreak) {
|
|
// Se devo fermare la ricerca e ho ancora CompoA e CompoB valide
|
|
if ( bValidA && bValidB) {
|
|
// Collego CompoA con CompoB e pulisco CompoB
|
|
Point3d ptStartB ;
|
|
if ( ! CompoB.GetStartPoint( ptStartB))
|
|
return false ;
|
|
CompoA.AddLine( ptStartB) ;
|
|
if ( ! CompoA.AddCurve( CompoB, dLinTol))
|
|
return false ;
|
|
CompoB.Clear() ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per analisi locale della superficie all'interno di un Box
|
|
static bool
|
|
GetBordersInBox( const SurfTriMesh& Stm, const BBox3d& BBoxCurr, int nFirstTria, const Point3d& ptSel,
|
|
double dSize, double dSizeTol, bool bAbsFirst, bool bRelFirst, double dAngTol,
|
|
double dLinTol, unordered_set<int>& setAvoidTria, unordered_set<int>& setNewTria,
|
|
unordered_map<int, int>& mapTriaClass, CurveComposite& CompoA, CurveComposite& CompoB,
|
|
Point3d& ptMid, Point3d& ptMid1, int& nTria, int& nTria1, bool& bBreak, bool& bStop)
|
|
{
|
|
// Controllo dei parametri
|
|
if ( ! Stm.IsValid())
|
|
return false ;
|
|
bBreak = false ;
|
|
bStop = false ;
|
|
setNewTria.clear() ;
|
|
|
|
// Recupero tutti i Triangoli all'interno del Box
|
|
INTVECTOR vTBox ;
|
|
if ( ! Stm.GetAllTriaOverlapBox( BBoxCurr, vTBox) || vTBox.empty()) {
|
|
// [Non devo mai capitare qui, non ha senso]
|
|
bStop = true ;
|
|
return true ;
|
|
}
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.clear() ; VC.clear() ;
|
|
CurveComposite pCompoBox ;
|
|
pCompoBox.AddPoint( BBoxCurr.GetMin()) ;
|
|
pCompoBox.AddLine( BBoxCurr.GetMin() + BBoxCurr.GetDimX() * X_AX) ;
|
|
pCompoBox.AddLine( BBoxCurr.GetMax() - BBoxCurr.GetDimZ() * Z_AX) ;
|
|
pCompoBox.AddLine( BBoxCurr.GetMin() + BBoxCurr.GetDimY() * Y_AX) ;
|
|
pCompoBox.Close() ;
|
|
pCompoBox.SetExtrusion( Z_AX) ;
|
|
pCompoBox.SetThickness( BBoxCurr.GetDimZ()) ;
|
|
VT.emplace_back( pCompoBox.Clone()) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
VT.emplace_back( Stm.CloneTriangle( nFirstTria)) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
IGeoPoint3d* _ptSel = CreateGeoPoint3d() ; _ptSel->Set( ptSel) ;
|
|
VT.emplace_back( _ptSel) ;
|
|
VC.emplace_back( RED) ;
|
|
if ( bAbsFirst)
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\1.nge") ;
|
|
#endif
|
|
|
|
// Classifico i triangoli
|
|
if ( ! ClassifyTriangles( Stm, vTBox, nFirstTria, dAngTol, setAvoidTria, setNewTria, mapTriaClass))
|
|
return false ;
|
|
|
|
// Recupero solo i bordi validi interni al Box dai triangoli classificati
|
|
COMPOVECTOR vCompoChain ;
|
|
if ( ! GetBordersByTriaInBox( Stm, BBoxCurr, dLinTol, setNewTria, vCompoChain, bBreak))
|
|
return false ;
|
|
|
|
// Se primo triangolo in assoluto, ricavo i due tratti di interesse
|
|
if ( bAbsFirst) {
|
|
if ( ! CheckCurvesInBox( Stm, vCompoChain, ptSel, setNewTria, CompoA, CompoB, ptMid, ptMid1, nTria, nTria1))
|
|
return false ;
|
|
// Se entrambe chiuse, ho finito
|
|
if ( CompoA.IsClosed() && CompoB.IsClosed()) {
|
|
bStop = true ;
|
|
return true ;
|
|
}
|
|
}
|
|
// Se non sono il primo triangolo
|
|
else {
|
|
|
|
// Se primo triangolo relativo, tengo i punti di Trimming
|
|
if ( bRelFirst) {
|
|
// TODO -> non estendere le curve e tenerere i punti di Trim
|
|
}
|
|
|
|
// Aggiorno la CompoA con il nuovo tratto trovato
|
|
bool bUpdateA = false ;
|
|
if ( ! UpdateBorderByTriaAdj( CompoA, dLinTol, vCompoChain, bUpdateA))
|
|
return false ;
|
|
// Se CompoA si è aggiornata
|
|
if ( bUpdateA) {
|
|
// Se si è chiusa su se stessa, ho individuato il bordo
|
|
if ( CompoA.IsClosed()) {
|
|
bStop = true ;
|
|
return true ;
|
|
}
|
|
// Verifico se si è unita alla CompoB
|
|
if ( CompoB.IsValid()) {
|
|
CurveComposite CompoBTest ;
|
|
CompoBTest.CopyFrom( &CompoB) ;
|
|
bool bUpdateBTmp = false ;
|
|
if ( ! UpdateBorderByTriaAdj( CompoBTest, dLinTol, { CompoA}, bUpdateBTmp))
|
|
return false ;
|
|
if ( bUpdateBTmp) {
|
|
CompoB.Clear() ;
|
|
CompoA.CopyFrom( &CompoBTest) ;
|
|
bUpdateA = true ;
|
|
bBreak = true ;
|
|
}
|
|
}
|
|
}
|
|
// Aggiorno la Compo B con il nuovo tratto trovato
|
|
bool bUpdateB = false ;
|
|
if ( CompoB.IsValid()) {
|
|
if ( ! UpdateBorderByTriaAdj( CompoB, dLinTol, vCompoChain, bUpdateB))
|
|
return false ;
|
|
// Se CompoB si è aggiornata
|
|
if ( bUpdateB) {
|
|
// Se si è chiusa su se stessa, ho individuato il bordo
|
|
if ( CompoB.IsClosed()) {
|
|
bStop = true ;
|
|
return true ;
|
|
}
|
|
// Verifico se si è unita alla CompoA
|
|
if ( CompoA.IsValid()) {
|
|
CurveComposite CompoATest ;
|
|
CompoATest.CopyFrom( &CompoA) ;
|
|
bool bUpdateATmp = false ;
|
|
if ( ! UpdateBorderByTriaAdj( CompoATest, dLinTol, { CompoB}, bUpdateATmp))
|
|
return false ;
|
|
if ( bUpdateATmp) {
|
|
CompoA.Clear() ;
|
|
CompoB.CopyFrom( &CompoATest) ;
|
|
bUpdateA = true ;
|
|
bBreak = true ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Se sono il primo triangolo Relativo
|
|
if ( bRelFirst) {
|
|
// Accorcio le curve originarie
|
|
}
|
|
|
|
// Se non ho aggiornato nessuna delle due curve, sono in caso di divergenza
|
|
if ( ! bUpdateA && ! bUpdateB) {
|
|
// Può capitare con il primo Cubetto per triangoli grandi !
|
|
// ( da capire che cosa fare...)
|
|
// serve ????
|
|
}
|
|
|
|
// Verifico che le curve siano tra loro alla distanza stabilita
|
|
if ( dSizeTol > EPS_SMALL) {
|
|
if ( ! CheckBordersInBox( CompoA, CompoB, Stm, setAvoidTria, dAngTol, dLinTol, dSize, dSizeTol, bBreak))
|
|
return false ;
|
|
}
|
|
if ( bBreak) {
|
|
// Se caso ambiguo nel cubetto corrente, non aggiungo i triangoli trovati
|
|
setNewTria.clear() ;
|
|
return true ;
|
|
}
|
|
|
|
// Se CompoA e CompoB sono due curve distinte, calcolo i 2 punti medi e i 2 triangoli di ricerca,
|
|
// altrimenti ptMid1 sarà invalido e nTria1 sarà un triangolo nullo
|
|
if ( ! CalcMidPointForBorders( CompoA, CompoB, Stm, setNewTria, ptMid, ptMid1, nTria, nTria1))
|
|
return false ;
|
|
}
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.clear() ; VC.clear() ;
|
|
pCompoBox.Clear() ;
|
|
pCompoBox.AddPoint( BBoxCurr.GetMin()) ;
|
|
pCompoBox.AddLine( BBoxCurr.GetMin() + BBoxCurr.GetDimX() * X_AX) ;
|
|
pCompoBox.AddLine( BBoxCurr.GetMax() - BBoxCurr.GetDimZ() * Z_AX) ;
|
|
pCompoBox.AddLine( BBoxCurr.GetMin() + BBoxCurr.GetDimY() * Y_AX) ;
|
|
pCompoBox.Close() ;
|
|
pCompoBox.SetExtrusion( Z_AX) ;
|
|
pCompoBox.SetThickness( BBoxCurr.GetDimZ()) ;
|
|
VT.emplace_back( pCompoBox.Clone()) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
VT.emplace_back( Stm.CloneTriangle( nFirstTria)) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
_ptSel = CreateGeoPoint3d() ; _ptSel->Set( ptSel) ;
|
|
VT.emplace_back( _ptSel) ;
|
|
VC.emplace_back( RED) ;
|
|
if ( CompoA.IsValid()) {
|
|
VT.emplace_back( CompoA.Clone()) ;
|
|
VC.emplace_back( AQUA) ;
|
|
}
|
|
if ( CompoB.IsValid()) {
|
|
VT.emplace_back( CompoB.Clone()) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\1.nge") ;
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per muovere il BoxLocale lungo le curve di Bordo
|
|
static bool
|
|
MarchAlongPath( const BBox3d& BBoxCurr, const SurfTriMesh& Stm,
|
|
unordered_set<int>& setAvoidTria, unordered_set<int>& setValidTria,
|
|
unordered_map<int, int>& mapTriaClass, bool bFollowA, const Point3d& ptMid, int nTria,
|
|
double dSize, double dSizeTol, double dAngTol, bool& bRelFirst,
|
|
CurveComposite& CompoA, CurveComposite& CompoB, bool& bStop)
|
|
{
|
|
// Variabili di controllo per la ricerca
|
|
BBox3d BBoxLast = BBoxCurr ;
|
|
Point3d ptRef = ptMid ;
|
|
int nTriaRef = nTria ;
|
|
bool bBreak = false ;
|
|
|
|
// Costanti di ricerca e contatori associati
|
|
const double EXPAND_EXTRA_TOL = 10. * EPS_SMALL ;
|
|
const int MAX_TRY = 4 ;
|
|
int nCount = 0 ;
|
|
|
|
// Inizio della ricerca
|
|
BBox3d MyBBoxCurr ;
|
|
while ( ! bBreak) {
|
|
// Definisco il nuovo Box
|
|
MyBBoxCurr.Reset() ;
|
|
MyBBoxCurr.Add( ptRef) ;
|
|
MyBBoxCurr.Expand( dSize + dSizeTol + EXPAND_EXTRA_TOL) ;
|
|
// Ricerco i triangoli
|
|
unordered_set<int> setNewTria ; setNewTria.reserve( Stm.GetTriangleCount()) ;
|
|
Point3d myPtMid, myPtMid1 ;
|
|
int nMyTria = 0, nMyTria1 = 0 ;
|
|
if ( ! GetBordersInBox( Stm, MyBBoxCurr, nTriaRef, ptRef, dSize, dSizeTol, false, bRelFirst,
|
|
dAngTol, 2. * EPS_SMALL, setAvoidTria, setNewTria, mapTriaClass, CompoA, CompoB,
|
|
myPtMid, myPtMid1, nMyTria, nMyTria1, bBreak, bStop))
|
|
return false ;
|
|
bRelFirst = false ;
|
|
// Memorizzo i triangoli validi ricavati
|
|
for ( auto Iter = setNewTria.begin() ; Iter != setNewTria.end() ; ++ Iter)
|
|
setValidTria.insert( *Iter) ;
|
|
// Se Direzione terminata o Curva chiusa
|
|
if ( bBreak || bStop)
|
|
break ;
|
|
// Ho determinato due curve distinte, il nuovo punto è quello più distante dal Box precedente
|
|
Point3d ptLast = ptRef ;
|
|
if ( myPtMid.IsValid() && myPtMid1.IsValid()) {
|
|
if ( bFollowA) {
|
|
ptRef = myPtMid ;
|
|
nTriaRef = nMyTria ;
|
|
}
|
|
else {
|
|
ptRef = myPtMid1 ;
|
|
nTriaRef = nMyTria1 ;
|
|
}
|
|
}
|
|
else {
|
|
if ( myPtMid.IsValid()) {
|
|
ptRef = myPtMid ;
|
|
nTriaRef = nMyTria ;
|
|
}
|
|
else {
|
|
ptRef = myPtMid1 ;
|
|
nTriaRef = nMyTria1 ;
|
|
}
|
|
}
|
|
// Se il nuovo punto è vicino al precedente, aggiorno il contatore
|
|
if ( SqDist( ptLast, ptRef) < ( ( dSize + dSizeTol) * ( dSize + dSizeTol) / 16.))
|
|
++ nCount ;
|
|
else
|
|
nCount = 0 ;
|
|
// Se troppi punti vicini tra loro, blocco la ricerca per sicurezza
|
|
if ( nCount >= MAX_TRY)
|
|
bBreak = true ;
|
|
// Il Box Corrente diventa il Box precedente
|
|
BBoxLast = MyBBoxCurr ;
|
|
|
|
#if DEBUG_FACE_SEARCH
|
|
VT.clear() ; VC.clear() ;
|
|
CurveComposite CompoBox ;
|
|
CompoBox.AddPoint( MyBBoxCurr.GetMin()) ;
|
|
CompoBox.AddLine( MyBBoxCurr.GetMin() + MyBBoxCurr.GetDimX() * X_AX) ;
|
|
CompoBox.AddLine( MyBBoxCurr.GetMax() - MyBBoxCurr.GetDimZ() * Z_AX) ;
|
|
CompoBox.AddLine( MyBBoxCurr.GetMin() + MyBBoxCurr.GetDimY() * Y_AX) ;
|
|
CompoBox.Close() ;
|
|
CompoBox.SetExtrusion( Z_AX) ;
|
|
CompoBox.SetThickness( MyBBoxCurr.GetDimZ()) ;
|
|
VT.emplace_back( CompoBox.Clone()) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
IGeoPoint3d* _ptRef = CreateGeoPoint3d() ; _ptRef->Set( ptRef) ;
|
|
VT.emplace_back( _ptRef) ;
|
|
VC.emplace_back( RED) ;
|
|
if ( CompoA.IsValid()) {
|
|
VT.emplace_back( CompoA.Clone()) ;
|
|
VC.emplace_back( AQUA) ;
|
|
}
|
|
if ( CompoB.IsValid()) {
|
|
VT.emplace_back( CompoB.Clone()) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
}
|
|
//SaveGeoObj( VT, VC, "C:\\Temp\\CurveUpdate.nge") ;
|
|
#endif
|
|
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per definire se un bordo è una circonferenza
|
|
// ( Restituisce true nel caso lo sia e la circonferenza perfetta come ultimo parametro)
|
|
static bool
|
|
IsBorderACircle( const PolyLine& PL, double dLinTol, double dAngTol, Frame3d& frCircle, double& dRad,
|
|
CurveArc& CrvCircle)
|
|
{
|
|
// Se PolyLine aperta o non piana, non è un cerchio
|
|
Plane3d PlanePL ;
|
|
if ( ! PL.IsClosed() || ! PL.IsFlat( PlanePL, dLinTol))
|
|
return false ;
|
|
|
|
// Verifico che tutti i punti rispettino la tolleranza angolare
|
|
POLYLINEVECTOR vPL ;
|
|
if ( ! GetPointSetByAngTol( PL, dAngTol, vPL) || ssize( vPL) != 1)
|
|
return false ;
|
|
|
|
// Creo un sistema di riferimento centrato nella PolyLine
|
|
Point3d ptCentroid ;
|
|
Frame3d frCurr ;
|
|
CurveComposite CurveCompo ; CurveCompo.FromPolyLine( PL) ;
|
|
if ( ! CurveCompo.GetCentroid( ptCentroid) ||
|
|
! frCurr.Set( ptCentroid, PlanePL.GetVersN()))
|
|
return false ;
|
|
|
|
// Porto una copia della PL in questo frame
|
|
PolyLine PLLoc = PL ; PLLoc.ToLoc( frCurr) ;
|
|
|
|
// Per ogni tratto salvo i suoi estremi e il punto medio in successione ( senza ripetizioni)
|
|
// ( I punti vengono proeittati nel piano per una maggiore precisione)
|
|
PNTVECTOR vPts ;
|
|
Point3d ptNext ;
|
|
PLLoc.GetFirstPoint( ptNext) ;
|
|
vPts.push_back( ptNext) ;
|
|
while ( PLLoc.GetNextPoint( ptNext)) {
|
|
vPts.push_back( Media( vPts.back(), ptNext)) ;
|
|
vPts.push_back( ptNext) ;
|
|
}
|
|
for ( POINTU& ptU : PLLoc.GetUPointList())
|
|
ptU.first.z = 0. ;
|
|
|
|
#if DEBUG_HOLES
|
|
vector<IGeoObj*> VTCircle ;
|
|
vector<Color> VCCirle ;
|
|
CurveComposite CompoPL ; CompoPL.FromPolyLine( PLLoc) ;
|
|
VTCircle.emplace_back( CompoPL.Clone()) ;
|
|
VCCirle.emplace_back( RED) ;
|
|
IGeoPoint3d* ptCenter = CreateGeoPoint3d() ; ptCenter->Set( ptCentroid) ;
|
|
ptCenter->ToLoc( frCurr) ;
|
|
VTCircle.emplace_back( ptCenter) ;
|
|
VCCirle.emplace_back( BLUE) ;
|
|
for ( int i = 0 ; i < ssize( vPts) ; ++ i) {
|
|
IGeoPoint3d* ptOnCirle = CreateGeoPoint3d() ; ptOnCirle->Set( vPts[i]) ;
|
|
VTCircle.emplace_back( ptOnCirle) ;
|
|
VCCirle.emplace_back( GREEN) ;
|
|
}
|
|
SaveGeoObj( VTCircle, VCCirle, "C:\\Temp\\CircleDebug.nge") ;
|
|
Point3d ptIMin, ptEMin, ptMaxDist ;
|
|
#endif
|
|
|
|
// Per ogni coppia di punti calcolo la Distanza Massima e Minima dal centro del cerchio locale ( ORIG)
|
|
double dSqMaxDist = 0. ;
|
|
double dSqMinDist = INFINITO ;
|
|
for ( int i = 0 ; i < ssize( vPts) / 2 ; i = i + 2) {
|
|
double dSqLineDist = INFINITO ;
|
|
DistPointLine( ORIG, vPts[i], vPts[i + 1]).GetSqDist( dSqLineDist) ;
|
|
if ( dSqLineDist < dSqMinDist) {
|
|
dSqMinDist = dSqLineDist ;
|
|
#if DEBUG_HOLES
|
|
ptIMin = vPts[i] ; ptEMin = vPts[i + 1] ;
|
|
#endif
|
|
}
|
|
double dSqPtDist = SqDist( ORIG, vPts[i]) ;
|
|
if ( dSqPtDist > dSqMaxDist) {
|
|
dSqMaxDist = dSqPtDist ;
|
|
#if DEBUG_HOLES
|
|
ptMaxDist = vPts[i] ;
|
|
#endif
|
|
}
|
|
}
|
|
#if DEBUG_HOLES
|
|
CurveLine LineMinDist ; LineMinDist.Set( ptIMin, ptEMin) ;
|
|
VTCircle.emplace_back( LineMinDist.Clone()) ;
|
|
VCCirle.emplace_back( YELLOW) ;
|
|
IGeoPoint3d* myPtMaxDist = CreateGeoPoint3d() ; myPtMaxDist->Set( ptMaxDist) ;
|
|
VTCircle.emplace_back( myPtMaxDist) ;
|
|
VCCirle.emplace_back( YELLOW) ;
|
|
SaveGeoObj( VTCircle, VCCirle, "C:\\Temp\\CircleDebug.nge") ;
|
|
#endif
|
|
|
|
// Se distanza minima e massima entro tolleranza, allora è una circonferenza
|
|
double dMinDist = sqrt( dSqMinDist) ;
|
|
double dMaxDist = sqrt( dSqMaxDist) ;
|
|
if ( abs( dMaxDist - dMinDist) < dLinTol) {
|
|
frCircle.Set( ptCentroid, PlanePL.GetVersN()) ;
|
|
dRad = ( dMinDist + dMaxDist) / 2. ;
|
|
return ( CrvCircle.Set( frCircle.Orig(), frCircle.VersZ(), dRad)) ;
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Funzione per definire se il bordo di una TriMesh è un'asola
|
|
static bool
|
|
IsBorderAButtonHole( const PolyLine& PL, double dLinTol, double dAngTol, Frame3d& frBtHole,
|
|
double& dRectLen, double& dRad, CurveComposite& CompoBthole)
|
|
{
|
|
// Se PolyLine aperta o non piana, non è un'asola
|
|
Plane3d PlanePL ;
|
|
if ( ! PL.IsClosed() || ! PL.IsFlat( PlanePL, dLinTol))
|
|
return false ;
|
|
|
|
// Verifico che tutti i punti rispettino la tolleranza angolare
|
|
POLYLINEVECTOR vPL ;
|
|
if ( ! GetPointSetByAngTol( PL, dAngTol, vPL) || ssize( vPL) != 1)
|
|
return false ;
|
|
|
|
#if DEBUG_HOLES
|
|
vector<IGeoObj*> VTCircle ;
|
|
vector<Color> VCCirle ;
|
|
CurveComposite CompoPL ; CompoPL.FromPolyLine( PL) ;
|
|
VTCircle.emplace_back( CompoPL.Clone()) ;
|
|
VCCirle.emplace_back( RED) ;
|
|
SaveGeoObj( VTCircle, VCCirle, "C:\\Temp\\ButtonHoleDebug.nge") ;
|
|
#endif
|
|
|
|
// Creo un sistema di riferimento centrato nella PolyLine e porto una sua copia in esso
|
|
Frame3d frXY ;
|
|
if ( ! frXY.Set( PlanePL.GetPoint(), PlanePL.GetVersN()))
|
|
return false ;
|
|
PolyLine PLXY = PL ; PLXY.ToLoc( frXY) ;
|
|
for ( POINTU& ptU : PLXY.GetUPointList())
|
|
ptU.first.z = 0. ;
|
|
|
|
#if DEBUG_HOLES
|
|
CurveComposite CompoXY ; CompoXY.FromPolyLine( PLXY) ;
|
|
VTCircle.emplace_back( CompoXY.Clone()) ;
|
|
VCCirle.emplace_back( BLUE) ;
|
|
SaveGeoObj( VTCircle, VCCirle, "C:\\Temp\\ButtonHoleDebug.nge") ;
|
|
#endif
|
|
|
|
// Recupero il rettangolo ad area minima
|
|
Point3d ptCen ;
|
|
Vector3d vtRectAx ;
|
|
double dLen, dHeight ;
|
|
if ( ! PLXY.GetMinAreaRectangleXY( ptCen, vtRectAx, dLen, dHeight))
|
|
return false ;
|
|
|
|
// Verifico che non sia un rettangolo ben definito, non un quadrato
|
|
if ( dLen - dHeight < 1001. * EPS_SMALL)
|
|
return false ; // potrebbe essere una circonferenza
|
|
|
|
// Cambio il sistema di riferimento in quello definito dal rettangolo
|
|
Frame3d frRect ;
|
|
if ( ! frRect.Set( ptCen, Z_AX, vtRectAx))
|
|
return false ;
|
|
PLXY.ToLoc( frRect) ;
|
|
|
|
#if DEBUG_HOLES
|
|
CompoXY.Clear() ; CompoXY.FromPolyLine( PLXY) ;
|
|
VTCircle.emplace_back( CompoXY.Clone()) ;
|
|
VCCirle.emplace_back( AQUA) ;
|
|
SaveGeoObj( VTCircle, VCCirle, "C:\\Temp\\ButtonHoleDebug.nge") ;
|
|
#endif
|
|
|
|
// Individuo il punto in basso a sinistra del rettangolo
|
|
Point3d ptLeftBotton = ORIG - ( dLen / 2.) * X_AX - ( dHeight / 2.) * Y_AX ;
|
|
|
|
// Semicirconferenza a sinistra
|
|
Point3d ptArcLeftCenter = ptLeftBotton + X_AX * dHeight / 2. + Y_AX * dHeight / 2. ;
|
|
CurveArc ArcLeft ;
|
|
ArcLeft.Set( ptArcLeftCenter, Z_AX, dHeight / 2., Y_AX, ANG_STRAIGHT, 0.) ;
|
|
|
|
// Semicirconferenza a destra
|
|
Point3d ptArcRightCenter = ptLeftBotton + ( dLen - dHeight / 2.) * X_AX + Y_AX * dHeight / 2. ;
|
|
CurveArc ArcRight ;
|
|
ArcRight.Set( ptArcRightCenter, Z_AX, dHeight / 2., - Y_AX, ANG_STRAIGHT, 0.) ;
|
|
|
|
// Tratto Lineare Top
|
|
CurveLine LineTop ;
|
|
LineTop.Set( ptArcRightCenter + Y_AX * dHeight / 2., ptArcLeftCenter + Y_AX * dHeight / 2.) ;
|
|
|
|
// Tratto Lineare Botton
|
|
CurveLine LineBottom ;
|
|
LineBottom.Set( ptArcLeftCenter - Y_AX * dHeight / 2., ptArcRightCenter - Y_AX * dHeight / 2.) ;
|
|
|
|
// Curva Composita complessiva dell'asola
|
|
CurveComposite CompoButtonHolePerfect ;
|
|
CompoButtonHolePerfect.AddCurve( LineTop) ;
|
|
CompoButtonHolePerfect.AddCurve( ArcLeft) ;
|
|
CompoButtonHolePerfect.AddCurve( LineBottom) ;
|
|
CompoButtonHolePerfect.AddCurve( ArcRight) ;
|
|
|
|
#if DEBUG_HOLES
|
|
VTCircle.emplace_back( CompoButtonHolePerfect.Clone()) ;
|
|
VCCirle.emplace_back( WHITE) ;
|
|
SaveGeoObj( VTCircle, VCCirle, "C:\\Temp\\ButtonHoleDebug.nge") ;
|
|
#endif
|
|
|
|
// Verifico che tutti i punti dell PolyLine siano abbastanza vicini a tale curva
|
|
// Campiono usando i punti medi, altrimenti qualsiasi poligono iscritto nell'asola ideale
|
|
// sarebbe valido
|
|
PNTVECTOR vPts ;
|
|
Point3d ptNext ;
|
|
PLXY.GetFirstPoint( ptNext) ;
|
|
vPts.push_back( ptNext) ;
|
|
while ( PLXY.GetNextPoint( ptNext)) {
|
|
vPts.push_back( Media( vPts.back(), ptNext)) ;
|
|
vPts.push_back( ptNext) ;
|
|
}
|
|
#if DEBUG_HOLES
|
|
for ( int i = 0 ; i < ssize( vPts) ; ++ i) {
|
|
IGeoPoint3d* ptOnCirle = CreateGeoPoint3d() ; ptOnCirle->Set( vPts[i]) ;
|
|
VTCircle.emplace_back( ptOnCirle) ;
|
|
VCCirle.emplace_back( GREEN) ;
|
|
}
|
|
SaveGeoObj( VTCircle, VCCirle, "C:\\Temp\\ButtonHoleDebug.nge") ;
|
|
#endif
|
|
bool bButtonHole = true ;
|
|
for ( int i = 0 ; bButtonHole && i < ssize( vPts) ; ++ i) {
|
|
double dSqDist = INFINITO ;
|
|
bButtonHole = ( DistPointCurve( vPts[i], CompoButtonHolePerfect).GetSqDist( dSqDist) &&
|
|
dSqDist < dLinTol * dLinTol) ;
|
|
}
|
|
|
|
// Se Asola, restituisco i parametri
|
|
if ( bButtonHole) {
|
|
frBtHole.Set( GetToGlob( GetToGlob( ORIG, frRect), frXY),
|
|
PlanePL.GetVersN(),
|
|
GetToGlob( GetToGlob( X_AX, frRect), frXY)) ;
|
|
dRectLen = dLen - 2. * dHeight ;
|
|
dRad = dHeight / 2. ;
|
|
CompoBthole.CopyFrom( &CompoButtonHolePerfect) ;
|
|
CompoBthole.ToGlob( frRect) ;
|
|
CompoBthole.ToGlob( frXY) ;
|
|
return true ;
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-------------------------------- Interpolazioni -----------------------------
|
|
//-----------------------------------------------------------------------------
|
|
static bool
|
|
InterpolateSyncCurvesOnEndGuidePoints( const ICurveComposite* pGuide, const ICurveComposite* pOtherGuide,
|
|
const Plane3d& plStart, const Plane3d& plEnd, double dLinTol,
|
|
BIPNTVECTOR& vBiPts)
|
|
{
|
|
vBiPts.clear() ;
|
|
|
|
// Verifico che le curve siano valide
|
|
if ( pGuide == nullptr || ! pGuide->IsValid() ||
|
|
pOtherGuide == nullptr || ! pOtherGuide->IsValid())
|
|
return false ;
|
|
|
|
// Recupero la sua lunghezza
|
|
double dGuideLen = 0. ;
|
|
if ( ! pGuide->GetLength( dGuideLen) || dGuideLen < dLinTol)
|
|
return false ;
|
|
|
|
// Scorro le curve da restituire
|
|
double dProgLen = 0. ;
|
|
vBiPts.reserve( pGuide->GetCurveCount() - 1) ;
|
|
for ( int i = 0 ; i < pGuide->GetCurveCount() - 1 ; ++ i) {
|
|
// Recupero la curva corrente
|
|
const ICurve* pCrv = pGuide->GetCurve( i) ;
|
|
if ( pCrv == nullptr || ! pCrv->IsValid())
|
|
return false ;
|
|
// Ne recupero la lunghezza e aggiorno la lunghezza progressiva
|
|
double dLen = 0. ;
|
|
if ( ! pCrv->GetLength( dLen))
|
|
return false ;
|
|
dProgLen += dLen ;
|
|
// Recupero la percentuale di interpolazione rispetto alla lunghezze
|
|
double dInterPar = dProgLen / dGuideLen ;
|
|
// Interpolo le normali dei piani rispetto a tale valore
|
|
Vector3d vtN = Media( plStart.GetVersN(), plEnd.GetVersN(), dInterPar) ;
|
|
vtN.Normalize() ;
|
|
// Definisco il piano di intersezione
|
|
Point3d ptCurr ;
|
|
if ( ! pCrv->GetEndPoint( ptCurr))
|
|
return false ;
|
|
#if DEBUG_SYNC_INTERPOLATION
|
|
Frame3d frPl ; frPl.Set( ptCurr, vtN) ;
|
|
PtrOwner<IGeoFrame3d> frCurr( CreateGeoFrame3d()) ; frCurr->Set( frPl) ;
|
|
VT.emplace_back( Release( frCurr)) ;
|
|
VC.emplace_back( WHITE) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\SyncLinesPlanes.nge") ;
|
|
#endif
|
|
// Recupero il parametro di intersezione tra la curva e il piano
|
|
#if 0
|
|
VT.clear() ; VC.clear() ;
|
|
PtrOwner<IGeoPoint3d> PT( CreateGeoPoint3d()) ; PT->Set( ptCurr) ;
|
|
VT.emplace_back( Release( PT)) ;
|
|
VC.emplace_back( AQUA) ;
|
|
PtrOwner<IGeoVector3d> VECT( CreateGeoVector3d()) ; VECT->Set( vtN) ;
|
|
VECT->ChangeBase( ptCurr) ;
|
|
PtrOwner<ICurveArc> pArc( CreateCurveArc()) ; pArc->Set( ptCurr, vtN, 1000.) ;
|
|
PtrOwner<ISurfFlatRegion> pSfrPlane( CreateSurfFlatRegion()) ;
|
|
pSfrPlane->AddExtLoop( Release( pArc)) ;
|
|
VT.emplace_back( Release( pSfrPlane)) ;
|
|
VC.emplace_back( Color( 0., 0., 0., .5)) ;
|
|
VT.emplace_back( Release( VECT)) ;
|
|
VC.emplace_back( BLUE) ;
|
|
VT.emplace_back( pOtherGuide->Clone()) ;
|
|
VC.emplace_back( WHITE) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\SyncLinesPlanes.nge") ;
|
|
#endif
|
|
IntersCurvePlane IntCP( *pOtherGuide, ptCurr, vtN) ;
|
|
if ( IntCP.GetIntersCount() == 0)
|
|
return false ; // ambiguità
|
|
// Recupero il punto della prima intersezione trovata
|
|
Point3d ptInt ;
|
|
double dPar ;
|
|
if ( ! IntCP.GetIntersPointNearTo( ptCurr, ptInt, dPar))
|
|
return false ;
|
|
#if DEBUG_SYNC_INTERPOLATION
|
|
PtrOwner<IGeoPoint3d> ptG( CreateGeoPoint3d()) ; ptG->Set( ptInt) ;
|
|
VT.emplace_back( Release( ptG)) ;
|
|
VC.emplace_back( WHITE) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\SyncLinesPlanes.nge") ;
|
|
#endif
|
|
// Memorizzo tale punto
|
|
vBiPts.emplace_back( make_pair( ptCurr, ptInt)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-------------------------------- Funzioni Export ----------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Funzione per il calcolo degli Edges Grezzi
|
|
bool
|
|
GetTrimmingRawEdges( const CISURFPVECTOR& vSurf, const SELVECTOR& vSurfFace,
|
|
double dLinTol, double dAngTol,
|
|
ICRVCOMPOPOVECTOR& vCompoRawEdges)
|
|
{
|
|
// Controllo la validità delle superfici
|
|
for ( const ISurf* pSurf : vSurf) {
|
|
if ( pSurf == nullptr || ! pSurf->IsValid()) {
|
|
LOG_ERROR( GetEGkLogger(), "Error in Trimming : Detected invalid Surf") ;
|
|
return false ;
|
|
}
|
|
}
|
|
// Pulizia dei parametri di ritorno
|
|
vCompoRawEdges.clear() ;
|
|
// Se non ho elementi di selezione, non faccio nulla
|
|
if ( vSurfFace.empty())
|
|
return true ;
|
|
// Controllo sulla tolleranza lineare
|
|
double dMyLinTol = Clamp( dLinTol, EPS_SMALL, 1e5 * EPS_SMALL) ;
|
|
// Controllo sulla tolleranza angolare
|
|
double dMyAngTol = Clamp( dAngTol, 1., 45.) ;
|
|
|
|
// Recupero i bordi semplici definiti dalle superfici e dalle loro eventuali facce selezionate
|
|
POLYLINEVECTOR vPLBorders ;
|
|
if ( ! GetSurfBorders( vSurf, vSurfFace, dMyLinTol, vPLBorders)) {
|
|
LOG_ERROR( GetEGkLogger(), "Error in Trimming : Borders extraction failed") ;
|
|
return false ;
|
|
}
|
|
|
|
// Recupero le curve di bordo grezze
|
|
if ( ! GetRawEdges( vPLBorders, dMyLinTol, dMyAngTol, vCompoRawEdges)) {
|
|
LOG_ERROR( GetEGkLogger(), "Error in Trimming : Border simplification failed") ;
|
|
return false ;
|
|
}
|
|
|
|
#if DEBUG_RAW_EDGES
|
|
VT.clear() ; VC.clear() ;
|
|
for ( int i = 0 ; i < ssize( vSurf) ; ++ i) {
|
|
VT.emplace_back( vSurf[i]->Clone()) ;
|
|
VC.emplace_back( BLACK) ;
|
|
}
|
|
for ( int i = 0 ; i < ssize( vCompoRawEdges) ; ++ i) {
|
|
VT.emplace_back( vCompoRawEdges[i]) ;
|
|
VC.emplace_back( RED) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\RawEdges.nge") ;
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Funzione per il calcolo automatico degli Edge finali
|
|
// Se c'è la necessità di spezzare le curve di bordo si procede in questo modo :
|
|
// 1) Si controlla l'esistenza di eventuali punti di rottura
|
|
// 2) Se non ci sono punti di rottura, si cerca un valore di Thick e ThickTol
|
|
// 3) Se non c'è una valore di Thick e ThickTol si procede per una rottura generica
|
|
// Valori di Default dei parametri per evitare i rispettivi controlli :
|
|
// - vBreakingPts = {}
|
|
// - dThick = 0., dThickTol = 0.
|
|
bool
|
|
GetTrimmingFinalBorders( ICRVCOMPOPOVECTOR& vCompoBezierEdges, double dLinTol, double dAngTol,
|
|
BIPNTVECTOR& vBreakingPts, double dThick, double dThickTol)
|
|
{
|
|
// Controllo sulla tolleranza lineare
|
|
double dMyLinTol = Clamp( dLinTol, EPS_SMALL, 1e5 * EPS_SMALL) ;
|
|
// Controllo sulla tolleranza angolare
|
|
double dMyAngTol = Clamp( dAngTol, 1., 45.) ;
|
|
|
|
// Se non ho curve, errore
|
|
if ( vCompoBezierEdges.empty()) {
|
|
LOG_INFO( GetEGkLogger(), "Warning in Trimming : No Edges detected") ;
|
|
return false ;
|
|
}
|
|
// Se ho 1 curva, allora cerco di spezzarla
|
|
else if ( ssize( vCompoBezierEdges) == 1) {
|
|
// Se sono presenti dei punti di rottura
|
|
bool bOk = true ;
|
|
if ( ! vBreakingPts.empty())
|
|
bOk = BreakCompoPathces( vCompoBezierEdges, dMyLinTol, dMyAngTol, vBreakingPts) ;
|
|
// Se invece è presente il valore di Thick
|
|
else if ( dThick > EPS_SMALL)
|
|
bOk = BreakCompoPathces( vCompoBezierEdges, dMyLinTol, dMyAngTol, dThick, dThickTol) ;
|
|
else
|
|
// Se generica
|
|
bOk = BreakCompoPathces( vCompoBezierEdges, dMyLinTol, dMyAngTol) ;
|
|
if ( ! bOk)
|
|
LOG_INFO( GetEGkLogger(), "Warning in Trimming : Breaking curves failed") ;
|
|
}
|
|
// Se ho 2 curve, perfetto !
|
|
else if ( ssize( vCompoBezierEdges) == 2)
|
|
return true ;
|
|
// Se più curve, errore
|
|
else {
|
|
LOG_INFO( GetEGkLogger(), "Warning in Trimming : More than 2 Edges detected") ;
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Funzione per ricavare i bordi a partire da un insieme di superfici; questi vengono
|
|
// calcolati mediante analisi delle normali lungo il bordo delle Patches di Surfs
|
|
bool
|
|
GetTrimmingFinalBorders( CISURFPVECTOR& vpSurf, const SELVECTOR& vSurfFaces, double dSurfLinTol,
|
|
double dSurfAngTol, double dLinTol, double dAngTol, double dThick,
|
|
ICRVCOMPOPOVECTOR& vCompoBezierEdges)
|
|
{
|
|
// Pulizia dei parametri di ritorno
|
|
vCompoBezierEdges.clear() ;
|
|
// Se non ho elementi di selezione, non faccio nulla
|
|
if ( vSurfFaces.empty())
|
|
return true ;
|
|
// Lo spessore deve essere positivo
|
|
if ( dThick < 10. * EPS_SMALL) // coerente con Offset minimo
|
|
return false ;
|
|
|
|
// Verifico che le superfici siano tutte valide ed aperte
|
|
for ( const ISurf* pSurf : vpSurf) {
|
|
if ( pSurf == nullptr || ! pSurf->IsValid() || pSurf->IsClosed())
|
|
return false ;
|
|
}
|
|
|
|
// Controllo sulla tolleranza lineare
|
|
double dMyLinTol = Clamp( dLinTol, EPS_SMALL, 1e5 * EPS_SMALL) ;
|
|
// Controllo sulla tolleranza angolare
|
|
double dMyAngTol = Clamp( dAngTol, 1., 45.) ;
|
|
|
|
// Recupero la superficie TriMesh complessiva
|
|
PtrOwner<const SurfTriMesh> pStm( nullptr) ;
|
|
if ( ssize( vpSurf) == 1) {
|
|
const ISurf* pSurf = vpSurf[0] ;
|
|
if ( pSurf == nullptr)
|
|
return false ;
|
|
int nType = pSurf->GetType() ;
|
|
if ( nType == SRF_TRIMESH)
|
|
pStm.Set( CloneBasicSurfTriMesh( pSurf)) ;
|
|
else if ( nType == SRF_BEZIER) {
|
|
const ISurfBezier* pSurfBz = GetSurfBezier( pSurf) ;
|
|
if ( pSurfBz != nullptr)
|
|
pStm.Set( CloneBasicSurfTriMesh( pSurfBz->GetApproxSurf( dSurfLinTol))) ;
|
|
}
|
|
}
|
|
else {
|
|
StmFromTriangleSoup mySoup ; mySoup.Start() ;
|
|
for ( const ISurf* pSurf : vpSurf) {
|
|
if ( pSurf == nullptr)
|
|
return false ;
|
|
int nType = pSurf->GetType() ;
|
|
if ( nType == SRF_TRIMESH)
|
|
mySoup.AddSurfTriMesh( *GetSurfTriMesh( pSurf)) ;
|
|
else if ( nType == SRF_BEZIER) {
|
|
const ISurfBezier* pSrfBz = GetSurfBezier( pSurf) ;
|
|
if ( pSrfBz == nullptr)
|
|
return false ;
|
|
PtrOwner<ISurfTriMesh> pStmTmp( pSrfBz->GetApproxSurf( dSurfLinTol)) ;
|
|
if ( ! IsNull( pStmTmp) && pStmTmp->IsValid())
|
|
mySoup.AddSurfTriMesh( *pStmTmp) ;
|
|
}
|
|
}
|
|
mySoup.End() ;
|
|
pStm.Set( GetBasicSurfTriMesh( mySoup.GetSurf())) ;
|
|
}
|
|
if ( IsNull( pStm) || ! pStm->IsValid() || pStm->IsClosed())
|
|
return false ;
|
|
|
|
// Calcolo il Bordo mediante estrusione
|
|
vector<pair<PolyLine, PolyLine>> vPLLoopPLExtr ;
|
|
if ( ! GetBorderByExtrusion( pStm, dThick, dMyLinTol, dMyAngTol, vPLLoopPLExtr))
|
|
return false ;
|
|
|
|
// Trasformo ogni PolyLine in Curva Composita
|
|
for ( int i = 0 ; i < ssize( vPLLoopPLExtr) ; ++ i) {
|
|
PolyLine& PLLoop = vPLLoopPLExtr[i].first ;
|
|
PtrOwner<ICurveComposite> pCompoLoop( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoLoop) || ! pCompoLoop->FromPolyLine( PLLoop))
|
|
continue ;
|
|
PolyLine& PLLoopExtr = vPLLoopPLExtr[i].second ;
|
|
PtrOwner<ICurveComposite> pCompoLoopExtr( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoLoopExtr) || ! pCompoLoopExtr->FromPolyLine( PLLoopExtr))
|
|
continue ;
|
|
vCompoBezierEdges.emplace_back( Release( pCompoLoop)) ;
|
|
if ( ! ApproxBorder( *GetBasicCurveComposite( vCompoBezierEdges.back()), dMyLinTol, dMyAngTol))
|
|
return false ;
|
|
vCompoBezierEdges.emplace_back( Release( pCompoLoopExtr)) ;
|
|
if ( ! ApproxBorder( *GetBasicCurveComposite( vCompoBezierEdges.back()), dMyLinTol, dMyAngTol))
|
|
return false ;
|
|
}
|
|
|
|
// Restituisco le curve orientate correttamente per la creazione della superficie di Bezier
|
|
erase_if( vCompoBezierEdges,
|
|
[]( ICurveComposite* pBezierEdge) {
|
|
return ( pBezierEdge == nullptr || ! pBezierEdge->IsValid() || pBezierEdge->GetCurveCount() == 0) ;}) ;
|
|
for ( int i = 0 ; i < ssize( vCompoBezierEdges) - 1 ; i += 2) {
|
|
ManageRuledBorders( vCompoBezierEdges[i], vCompoBezierEdges[i+1]) ;
|
|
// Essendo l'Offset fatto in negativo, entrambe le curve vanno invertite
|
|
vCompoBezierEdges[i]->Invert() ;
|
|
vCompoBezierEdges[i+1]->Invert() ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Funzione per approssimare le curve di Bordo grezze ( tratti lineari) in
|
|
// Patch di curve di Bezier
|
|
bool
|
|
GetTrimmingBezierEdges( ICRVCOMPOPOVECTOR& vCompoRawEdges, double dLinTol, double dAngTol,
|
|
ICRVCOMPOPOVECTOR& vBezierEdges)
|
|
{
|
|
// Pulizia del parametro di ritorno
|
|
vBezierEdges.clear() ;
|
|
// Controllo sulla tolleranza lineare
|
|
double dMyLinTol = Clamp( dLinTol, EPS_SMALL, 1e5 * EPS_SMALL) ;
|
|
// Controllo sulla tolleranza angolare
|
|
double dMyAngTol = Clamp( dAngTol, 1., 45.) ;
|
|
|
|
#if DEBUG_EDGES
|
|
VC.clear() ; VT.clear() ;
|
|
for ( int _i = 0 ; _i < ssize( vCompoRawEdges) ; ++ _i) {
|
|
VT.emplace_back( vCompoRawEdges[_i]->Clone()) ;
|
|
VC.emplace_back( RED) ;
|
|
}
|
|
#endif
|
|
|
|
// Approssimo i bordi grezzi
|
|
vBezierEdges.reserve( vCompoRawEdges.size()) ;
|
|
for ( int i = 0 ; i < ssize( vCompoRawEdges) ; ++ i) {
|
|
PtrOwner<ICurveComposite> pCompoTmp( CloneCurveComposite( vCompoRawEdges[i])) ;
|
|
if ( IsNull( pCompoTmp) ||
|
|
! ApproxBorder( *GetBasicCurveComposite( pCompoTmp), dMyLinTol, dMyAngTol) ||
|
|
! vBezierEdges.emplace_back( Release( pCompoTmp))) {
|
|
LOG_ERROR( GetEGkLogger(), "Error in Trimming : Approxing edge failed") ;
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// Restituisco le curve orientate correttamente per la creazione della superficie di Bezier
|
|
erase_if( vBezierEdges,
|
|
[]( ICurveComposite* pBezierEdge) {
|
|
return ( pBezierEdge == nullptr || ! pBezierEdge->IsValid() || pBezierEdge->GetCurveCount() == 0) ;}) ;
|
|
for ( int i = 0 ; i < ssize( vBezierEdges) - 1 ; i += 2)
|
|
ManageRuledBorders( vBezierEdges[i], vBezierEdges[i+1]) ;
|
|
|
|
#if DEBUG_EDGES
|
|
VC.clear() ; VT.clear() ;
|
|
for ( int _i = 0 ; _i < ssize( vBezierEdges) ; ++ _i) {
|
|
VT.emplace_back( vBezierEdges[_i]->Clone()) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\ApproxBorder.nge") ;
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Funzione per ottenere i punti di sincronizzazione tra due curve di Bezier per
|
|
// la costruzione della superficie rigata stessa
|
|
bool
|
|
GetTrimmingSurfBzSyncPoints( const ICurve* pCrvEdge1, const ICurve* pCrvEdge2,
|
|
double dLinTol, BIPNTVECTOR& vSyncPoints)
|
|
{
|
|
vSyncPoints.clear() ;
|
|
|
|
// Verifica validità delle curve per la creazione della Bezier rigata
|
|
PtrOwner<ICurveComposite> pCompoEdge1( ConvertCurveToComposite( pCrvEdge1->Clone())) ;
|
|
PtrOwner<ICurveComposite> pCompoEdge2( ConvertCurveToComposite( pCrvEdge2->Clone())) ;
|
|
if ( IsNull( pCompoEdge1) || IsNull( pCompoEdge2) ||
|
|
! pCompoEdge1->IsValid() || ! pCompoEdge2->IsValid())
|
|
return false ;
|
|
|
|
//// Controllo sulla tolleranza lineare
|
|
//double dMyLinTol = Clamp( dLinTol, EPS_SMALL, 1e5 * EPS_SMALL) ;
|
|
|
|
#if DEBUG_SYNC_POINTS
|
|
VT.clear() ; VC.clear() ;
|
|
VT.emplace_back( pCompoEdge1->Clone()) ;
|
|
VC.emplace_back( AQUA) ;
|
|
VT.emplace_back( pCompoEdge2->Clone()) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
#endif
|
|
|
|
// Definisco la superficie di Bezier rigata
|
|
PtrOwner<SurfBezier> pSBzRuled( GetBasicSurfBezier( GetSurfBezierRuledSmooth( pCompoEdge1, pCompoEdge2, vSyncPoints, 20.0))) ;
|
|
if ( IsNull( pSBzRuled) || ! pSBzRuled->IsValid())
|
|
return false ;
|
|
|
|
//// Recupero i punti di sincronizzazione e li restituisco
|
|
// ICURVEPOVECTOR vCrv ;
|
|
// pSBzRuled->GetAllPatchesIsocurves( false, vCrv) ;
|
|
// vSyncPoints.reserve( vCrv.size()) ;
|
|
// for ( int i = 0 ; i < ssize( vCrv) ; ++ i) {
|
|
// if ( ! IsNull( vCrv[i]) && vCrv[i]->IsValid()) {
|
|
// #if DEBUG_SYNC_POINTS
|
|
// VT.emplace_back( vCrv[i]->Clone()) ;
|
|
// VC.emplace_back( LIME) ;
|
|
// #endif
|
|
// Point3d ptStart ; vCrv[i]->GetStartPoint( ptStart) ;
|
|
// Point3d ptEnd ; vCrv[i]->GetEndPoint( ptEnd) ;
|
|
// if ( ! AreSamePointApprox( ptStart, ptEnd))
|
|
// vSyncPoints.emplace_back( make_pair( ptStart, ptEnd)) ;
|
|
// }
|
|
// }
|
|
|
|
#if DEBUG_SYNC_POINTS
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\BorderSyncPoints.nge") ;
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Funzione per interpolare le Curve di Sincronizzazione tra due Curve di Bordo
|
|
bool
|
|
GetTrimmingSyncInterpolation( const ICurve* pCrvEdge1, const ICurve* pCrvEdge2,
|
|
const ICurve* pSync1, const ICurve* pSync2, double dLinTol,
|
|
double dAngTol, BIPNTVECTOR& vSyncPoints)
|
|
{
|
|
vSyncPoints.clear() ;
|
|
|
|
// Verifico la validità dei parametri
|
|
if ( pCrvEdge1 == nullptr || ! pCrvEdge1->IsValid() ||
|
|
pCrvEdge2 == nullptr || ! pCrvEdge2->IsValid() ||
|
|
pSync1 == nullptr || ! pSync1->IsValid() ||
|
|
pSync2 == nullptr || ! pSync2->IsValid())
|
|
return false ;
|
|
|
|
// Le curve di Bordo devono essere entrambe aperte o entrambe chiuse
|
|
if ( pCrvEdge1->IsClosed() != pCrvEdge2->IsClosed())
|
|
return false ;
|
|
|
|
// Verifico i valori delle tolleranze
|
|
double dMyLinTol = Clamp( dLinTol, EPS_SMALL, 1e5 * EPS_SMALL) ;
|
|
//double dMyAngTol = Clamp( dAngTol, EPS_ANG_SMALL, 60.) ;
|
|
|
|
// Verifico le due curve di sincronizzazione abbiano gli estremi sulle due curve di bordo
|
|
Point3d ptS1 ; pSync1->GetStartPoint( ptS1) ;
|
|
Point3d ptE1 ; pSync1->GetEndPoint( ptE1) ;
|
|
if ( AreSamePointEpsilon( ptS1, ptE1, dMyLinTol))
|
|
return false ;
|
|
double dUA, dUB, dUC, dUD ; // [A,C]->Edge1 | [B,D]->Edge2
|
|
if ( ( ! pCrvEdge1->GetParamAtPoint( ptS1, dUA, dMyLinTol) || ! pCrvEdge2->GetParamAtPoint( ptE1, dUB, dMyLinTol)) &&
|
|
( ! pCrvEdge1->GetParamAtPoint( ptE1, dUA, dMyLinTol) || ! pCrvEdge2->GetParamAtPoint( ptS1, dUB, dMyLinTol)))
|
|
return false ;
|
|
Point3d ptS2 ; pSync2->GetStartPoint( ptS2) ;
|
|
Point3d ptE2 ; pSync2->GetEndPoint( ptE2) ;
|
|
if ( AreSamePointEpsilon( ptS2, ptE2, dMyLinTol))
|
|
return false ;
|
|
if ( ( ! pCrvEdge1->GetParamAtPoint( ptS2, dUC, dMyLinTol) || ! pCrvEdge2->GetParamAtPoint( ptE2, dUD, dMyLinTol)) &&
|
|
( ! pCrvEdge1->GetParamAtPoint( ptE2, dUC, dMyLinTol) || ! pCrvEdge2->GetParamAtPoint( ptS2, dUD, dMyLinTol)))
|
|
return false ;
|
|
|
|
// Recupero i due tratti di curva
|
|
PtrOwner<CurveComposite> pCompoGuide1( nullptr) ;
|
|
PtrOwner<CurveComposite> pCompoGuide2( nullptr) ;
|
|
// se entrambe chiuse
|
|
if ( pCrvEdge1->IsClosed() && pCrvEdge2->IsClosed()) {
|
|
// Recupero la porzione di curva di Edge1 di lunghezza minima tra il parametro A e C
|
|
PtrOwner<CurveComposite> pCompoAC( ConvertCurveToBasicComposite( pCrvEdge1->CopyParamRange( dUA, dUC))) ;
|
|
PtrOwner<CurveComposite> pCompoCA( ConvertCurveToBasicComposite( pCrvEdge1->CopyParamRange( dUC, dUA))) ;
|
|
if ( IsNull( pCompoAC) || IsNull( pCompoCA) || ! pCompoAC->IsValid() || ! pCompoCA->IsValid())
|
|
return false ;
|
|
double dLenAC ; pCompoAC->GetLength( dLenAC) ;
|
|
double dLenCA ; pCompoCA->GetLength( dLenCA) ;
|
|
if ( dLenAC < dLenCA)
|
|
pCompoGuide1.Set( Release( pCompoAC)) ;
|
|
else
|
|
pCompoGuide1.Set( Release( pCompoCA)) ;
|
|
// Recupero la porzione di curva di Edge2 di lunghezza minima tra il parametro B e D
|
|
PtrOwner<CurveComposite> pCompoBD( ConvertCurveToBasicComposite( pCrvEdge2->CopyParamRange( dUB, dUD))) ;
|
|
PtrOwner<CurveComposite> pCompoDB( ConvertCurveToBasicComposite( pCrvEdge2->CopyParamRange( dUD, dUB))) ;
|
|
if ( IsNull( pCompoBD) || IsNull( pCompoBD) || ! pCompoBD->IsValid() || ! pCompoDB->IsValid())
|
|
return false ;
|
|
double dLenBD ; pCompoBD->GetLength( dLenBD) ;
|
|
double dLenDB ; pCompoDB->GetLength( dLenDB) ;
|
|
if ( dLenBD < dLenDB)
|
|
pCompoGuide2.Set( Release( pCompoBD)) ;
|
|
else
|
|
pCompoGuide2.Set( Release( pCompoDB)) ;
|
|
}
|
|
// se entrambe aperte
|
|
else if ( ! pCrvEdge1->IsClosed() && ! pCrvEdge2->IsClosed()) {
|
|
pCompoGuide1.Set( ConvertCurveToBasicComposite( pCrvEdge1->Clone())) ;
|
|
if ( IsNull( pCompoGuide1) || ! pCompoGuide1->IsValid())
|
|
return false ;
|
|
if ( dUA < dUC)
|
|
pCompoGuide1->TrimStartEndAtParam( dUA, dUC) ;
|
|
else
|
|
pCompoGuide1->TrimStartEndAtParam( dUC, dUA) ;
|
|
pCompoGuide2.Set( ConvertCurveToBasicComposite( pCrvEdge2->Clone())) ;
|
|
if ( IsNull( pCompoGuide2) || ! pCompoGuide2->IsValid())
|
|
return false ;
|
|
if ( dUB < dUD)
|
|
pCompoGuide2->TrimStartEndAtParam( dUB, dUD) ;
|
|
else
|
|
pCompoGuide2->TrimStartEndAtParam( dUD, dUB) ;
|
|
}
|
|
if ( IsNull( pCompoGuide1) || IsNull( pCompoGuide2) ||
|
|
! pCompoGuide1->IsValid() || ! pCompoGuide2->IsValid())
|
|
return false ;
|
|
|
|
// Determino quale curva è la più lunga, servirà come riferimento per la parametrizzazione e l'interpolazione
|
|
double dLen1, dLen2 ;
|
|
if ( ! pCompoGuide1->GetLength( dLen1) || ! pCompoGuide2->GetLength( dLen2) ||
|
|
dLen1 < dMyLinTol || dLen2 < dMyLinTol)
|
|
return false ;
|
|
if ( dLen2 > dLen1) {
|
|
swap( pCompoGuide1, pCompoGuide2) ;
|
|
swap( dLen1, dLen2) ;
|
|
}
|
|
// Aggiorno gli estremi delle curve
|
|
pCompoGuide1->GetStartPoint( ptS1) ;
|
|
pCompoGuide2->GetStartPoint( ptE1) ;
|
|
pCompoGuide1->GetEndPoint( ptS2) ;
|
|
pCompoGuide2->GetEndPoint( ptE2) ;
|
|
|
|
#if DEBUG_SYNC_INTERPOLATION
|
|
VT.clear() ; VC.clear() ;
|
|
VT.emplace_back( pCompoGuide1->Clone()) ;
|
|
VC.emplace_back( AQUA) ;
|
|
VT.emplace_back( pCompoGuide2->Clone()) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
PtrOwner<IGeoPoint3d> ptG( CreateGeoPoint3d()) ; ptG->Set( ptS1) ;
|
|
VT.emplace_back( ptG->Clone()) ; VC.emplace_back( AQUA) ;
|
|
ptG->Set( ptE1) ;
|
|
VT.emplace_back( ptG->Clone()) ; VC.emplace_back( ORANGE) ;
|
|
ptG->Set( ptS2) ;
|
|
VT.emplace_back( ptG->Clone()) ; VC.emplace_back( AQUA) ;
|
|
ptG->Set( ptE2) ;
|
|
VT.emplace_back( ptG->Clone()) ; VC.emplace_back( ORANGE) ;
|
|
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ; pLine->Set( ptS1, ptE1) ;
|
|
VT.emplace_back( pLine->Clone()) ; VC.emplace_back( PURPLE) ;
|
|
pLine->Set( ptS2, ptE2) ;
|
|
VT.emplace_back( pLine->Clone()) ; VC.emplace_back( PURPLE) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\SyncLinesPlanes.nge") ;
|
|
#endif
|
|
|
|
// Determino le Normali dei piani di taglio agli agli estremi delle due curve
|
|
Vector3d vtStart1, vtStart2 ;
|
|
if ( ! pCompoGuide1->GetStartDir( vtStart1) || ! pCompoGuide2->GetStartDir( vtStart2))
|
|
return false ;
|
|
Vector3d vtAux = ptE1 - ptS1 ; vtAux.Normalize() ;
|
|
Vector3d vtMTan = Media( vtStart1, vtStart2) ; vtMTan.Normalize() ;
|
|
Vector3d vtN = OrthoCompo( vtMTan, vtAux) ; vtN.Normalize() ;
|
|
Plane3d plStart ;
|
|
if ( ! plStart.Set( ptS1, vtN))
|
|
return false ;
|
|
Vector3d vtEnd1, vtEnd2 ;
|
|
if ( ! pCompoGuide1->GetEndDir( vtEnd1) || ! pCompoGuide2->GetEndDir( vtEnd2))
|
|
return false ;
|
|
vtAux = ptE2 - ptS2 ; vtAux.Normalize() ;
|
|
vtMTan = Media( vtEnd1, vtEnd2) ; vtMTan.Normalize() ;
|
|
vtN = OrthoCompo( vtMTan, vtAux) ; vtN.Normalize() ;
|
|
Plane3d plEnd ;
|
|
if ( ! plEnd.Set( ptS2, vtN))
|
|
return false ;
|
|
|
|
#if DEBUG_SYNC_INTERPOLATION
|
|
Frame3d frPlStart ; frPlStart.Set( ptS1, plStart.GetVersN()) ;
|
|
Frame3d frPlEnd ; frPlEnd.Set( ptS2, plEnd.GetVersN()) ;
|
|
PtrOwner<IGeoFrame3d> frS( CreateGeoFrame3d()) ; frS->Set( frPlStart) ;
|
|
PtrOwner<IGeoFrame3d> frE( CreateGeoFrame3d()) ; frE->Set( frPlEnd) ;
|
|
VT.emplace_back( Release( frS)) ;
|
|
VC.emplace_back( WHITE) ;
|
|
VT.emplace_back( Release( frE)) ;
|
|
VC.emplace_back( WHITE) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\SyncLinesPlanes.nge") ;
|
|
#endif
|
|
|
|
// Curve di Sincronizzazione del Bordo 1 sul Bordo 2
|
|
BIPNTVECTOR vBiPts1 ;
|
|
if ( ! InterpolateSyncCurvesOnEndGuidePoints( pCompoGuide1, pCompoGuide2, plStart, plEnd, dMyLinTol, vBiPts1))
|
|
return false ;
|
|
|
|
// Curve di Sincronizzazione del Bordo 2 sul Bordo 1
|
|
BIPNTVECTOR vBiPts2 ;
|
|
if ( ! InterpolateSyncCurvesOnEndGuidePoints( pCompoGuide2, pCompoGuide1, plStart, plEnd, dMyLinTol, vBiPts2))
|
|
return false ;
|
|
|
|
// Restituisco le Curve di Sincronizzazione
|
|
// [Da Bordo 1 a Bordo 2 originale]
|
|
vSyncPoints.reserve( ssize( vBiPts1) + ssize( vBiPts2)) ;
|
|
for ( int i = 0 ; i < ssize( vBiPts1) ; ++ i) {
|
|
vSyncPoints.emplace_back( vBiPts1[i]) ;
|
|
if ( pCrvEdge1->IsPointOn( vBiPts1[i].second, dMyLinTol))
|
|
swap( vSyncPoints.back().first, vSyncPoints.back().second) ;
|
|
}
|
|
for ( int i = 0 ; i < ssize( vBiPts2) ; ++ i) {
|
|
vSyncPoints.emplace_back( vBiPts2[i]) ;
|
|
if ( pCrvEdge1->IsPointOn( vBiPts2[i].second, dMyLinTol))
|
|
swap( vSyncPoints.back().first, vSyncPoints.back().second) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Funzione per il calcolo della superficie di Bezier rigata
|
|
// NB. Possono essere passati dei parametri opzionali come vIndPriority e vIndVisible :
|
|
// - Se non presenti, la superficie Ruled Bezier viene costruita in modo standard
|
|
// - Se sono presenti degli indici di priorità e/o degli indici di visibilità, significa che
|
|
// si vuole costruire una superficie diversa da quella standard, dove i tratti prioritari
|
|
// sono quelli che più determinano il cambiamento della superficie ( in quanto diversi da quelli
|
|
// originali) e i tratti visibili servono per determinare l'intervallo di influenza della modifica
|
|
// alla superficie nei pressi dei tratti lineari di sincronizzazione prioritari
|
|
ISurfBezier*
|
|
GetTrimmingRuledBezier( const CISURFPVECTOR& vSurf, const ICurve* pCrvEdge1,
|
|
const ICurve* pCrvEdge2, double dLinTol, const BIPNTVECTOR& vSyncPoints)
|
|
{
|
|
// Verifica validità delle curve per la creazione della Bezier rigata
|
|
if ( pCrvEdge1 == nullptr || pCrvEdge2 == nullptr ||
|
|
! pCrvEdge1->IsValid() || ! pCrvEdge2->IsValid())
|
|
return nullptr ;
|
|
// Se una chiusa e una aperta, errore
|
|
if ( pCrvEdge1->IsClosed() != pCrvEdge2->IsClosed())
|
|
return nullptr ;
|
|
|
|
PtrOwner<ICurveComposite> pCompoEdge1( ConvertCurveToComposite( pCrvEdge1->Clone())) ;
|
|
PtrOwner<ICurveComposite> pCompoEdge2( ConvertCurveToComposite( pCrvEdge2->Clone())) ;
|
|
if ( IsNull( pCompoEdge1) || IsNull( pCompoEdge2))
|
|
return nullptr ;
|
|
// Se entrambe aperte potrei doverle invertire, altrimenti non le modifico
|
|
if ( ! pCrvEdge1->IsClosed() && ! pCrvEdge2->IsClosed()) {
|
|
if ( ! ManageRuledBorders( pCompoEdge1, pCompoEdge2))
|
|
return nullptr ;
|
|
}
|
|
// Controllo sulla tolleranza lineare
|
|
double dMyLinTol = Clamp( dLinTol, EPS_SMALL, 1e5 * EPS_SMALL) ;
|
|
|
|
#if DEBUG_BEZIER_RULED
|
|
VT_Glob.emplace_back( pCompoEdge1->Clone()) ;
|
|
VT_Glob.emplace_back( pCompoEdge2->Clone()) ;
|
|
VC_Glob.emplace_back( ORANGE) ;
|
|
VC_Glob.emplace_back( AQUA) ;
|
|
#endif
|
|
|
|
// Inzializzo la superficie di Bezier
|
|
PtrOwner<ISurfBezier> pSurfBz( CreateSurfBezier()) ;
|
|
if ( IsNull( pSurfBz))
|
|
return nullptr ;
|
|
|
|
// Se non ho punti di controllo forzati
|
|
if ( vSyncPoints.empty()) {
|
|
BIPNTVECTOR vSyncLines ;
|
|
pSurfBz.Set( GetSurfBezierRuledSmooth( pCompoEdge1, pCompoEdge2, vSyncLines, 20.0)) ;
|
|
if ( IsNull( pSurfBz) || ! pSurfBz->IsValid()) {
|
|
LOG_ERROR( GetEGkLogger(), "Error in Trimming : Ruled Bezier invalid") ;
|
|
return nullptr ;
|
|
}
|
|
}
|
|
// Se ho punti di controllo forzati
|
|
else {
|
|
#if DEBUG_BEZIER_RULED
|
|
for ( int i = 0 ; i < ssize( vSyncPoints) ; ++ i) {
|
|
const Point3d& ptS = vSyncPoints[i].first ;
|
|
const Point3d& ptE = vSyncPoints[i].second ;
|
|
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
|
|
if ( IsNull( pLine) || ! pLine->Set( ptS, ptE))
|
|
return nullptr ;
|
|
VT_Glob.emplace_back( pLine->Clone()) ;
|
|
if ( find( vnShown.begin(), vnShown.end(), i) != vnShown.end()) {
|
|
if ( find( vnEdited.begin(), vnEdited.end(), i) != vnEdited.end())
|
|
VC_Glob.emplace_back( GREEN) ;
|
|
else if ( find( vnNew.begin(), vnNew.end(), i) != vnNew.end())
|
|
VC_Glob.emplace_back( ORANGE) ;
|
|
else
|
|
VC_Glob.emplace_back( LIME) ;
|
|
}
|
|
else
|
|
VC_Glob.emplace_back( GRAY) ;
|
|
}
|
|
SaveGeoObj( VT_Glob, VC_Glob, "C:\\Temp\\TestBz.nge") ;
|
|
#endif
|
|
// Definisco la superficie
|
|
INTVECTOR vInd( ssize( vSyncPoints)) ; iota( vInd.begin(), vInd.end(), 0) ;
|
|
pSurfBz.Set( GetSurfBezierRuledGuided( pCompoEdge1, pCompoEdge2, vSyncPoints, dMyLinTol)) ;
|
|
if ( IsNull( pSurfBz) || ! pSurfBz->IsValid()) {
|
|
LOG_ERROR( GetEGkLogger(), "Error in Trimming : Ruled Bezier with Sync Points invalid") ;
|
|
return nullptr ;
|
|
}
|
|
}
|
|
|
|
#if DEBUG_BEZIER_RULED
|
|
VT_Glob.emplace_back( pSurfBz->Clone()) ;
|
|
VC_Glob.emplace_back( BLACK) ;
|
|
SaveGeoObj( VT_Glob, VC_Glob, "C:\\Temp\\TestBz.nge") ;
|
|
#endif
|
|
|
|
// La superficie deve essere orientata correttamente con le geometrie iniziali ( privilegio le TriMesh)
|
|
if ( ! vSurf.empty()) {
|
|
bool bOk = true ;
|
|
Point3d ptPos ;
|
|
Vector3d vtN ;
|
|
if ( pSurfBz->GetPointNrmD1D2( 0.1, 0.1, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptPos, vtN)) {
|
|
Point3d ptCheck = ptPos + 10. * EPS_SMALL * vtN ;
|
|
for ( const ISurf* pSurf : vSurf) {
|
|
if ( pSurf->GetType() == SRF_TRIMESH) {
|
|
if ( DistPointSurfTm( ptCheck, *GetSurfTriMesh( pSurf)).IsPointOnLeftSide())
|
|
pSurfBz->Invert() ;
|
|
bOk = true ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( ! bOk) {
|
|
for ( const ISurf* pSurf : vSurf) {
|
|
if ( pSurf->GetType() == SRF_BEZIER) {
|
|
if ( DistPointSurfBz( ptCheck, *GetSurfBezier( pSurf)).IsPointOnLeftSide())
|
|
pSurfBz->Invert() ;
|
|
bOk = true ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ( IsNull( pSurfBz) || ! pSurfBz->IsValid() ? nullptr : Release( pSurfBz)) ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Funzione per la ricerca di facce adiacenti su una superficie TriMesh.
|
|
// Dato un insieme iniziale di facce, per ciascuna di esse vengono analizzate le facce adiacenti.
|
|
// Se la normale di una faccia adiacente forma un angolo entro la tolleranza specificata,
|
|
// quella faccia viene memorizzata e considerata come nuovo punto di partenza per la ricerca
|
|
// di ulteriori adiacenze.
|
|
bool
|
|
GetTrimmingStmAdjTria( const ISurfTriMesh* pStm, int nStartTria, const Point3d& ptStartTria,
|
|
double dAngTol, double dSize, double dSizeTol, ISurfTriMesh* pStmAdjTria)
|
|
{
|
|
// La superfcicie deve essere inizializzata
|
|
if ( pStmAdjTria == nullptr)
|
|
return false ;
|
|
|
|
// Verifico la validità della superficie TriMesh
|
|
if ( ! pStm->IsValid())
|
|
return false ;
|
|
|
|
// Recupero la superficie di base
|
|
const SurfTriMesh* pStmBasic = GetBasicSurfTriMesh( pStm) ;
|
|
if ( pStmBasic == nullptr || ! pStmBasic->IsValid())
|
|
return false ;
|
|
|
|
// Definisco un Box3d nel punto di Selezione di dimensioni pari al doppio della dimensione della
|
|
// striscia di ricerca considerando la tolleranza
|
|
Triangle3d firstTria ;
|
|
if ( ! pStmBasic->GetTriangle( nStartTria, firstTria))
|
|
return false ;
|
|
BBox3d BBoxCurr ;
|
|
BBoxCurr.Add( ptStartTria) ;
|
|
const double EXPAND_EXTRA_TOL = 10. * EPS_SMALL ;
|
|
BBoxCurr.Expand( ( dSize + dSizeTol + EXPAND_EXTRA_TOL)) ;
|
|
|
|
// Recupero i triangoli validi all'interno del Box e definisco i primi due tratti lineari
|
|
// per le curve di Bordo
|
|
bool bBreak = false, bStop = false ;
|
|
INTVECTOR vOtherTria ; vOtherTria.reserve( pStmBasic->GetTriangleCount()) ;
|
|
unordered_set<int> setAvoidTria ; setAvoidTria.reserve( pStmBasic->GetTriangleCount()) ;
|
|
unordered_set<int> setValidTria ; setValidTria.reserve( pStmBasic->GetTriangleCount()) ;
|
|
unordered_set<int> setNewTria ; setNewTria.reserve( pStmBasic->GetTriangleCount()) ;
|
|
unordered_map<int, int> mapTriaClass ; mapTriaClass.reserve( pStmBasic->GetTriangleCount()) ;
|
|
Point3d ptMid, ptMid1 ;
|
|
int nTria = 0, nTria1 = 0 ;
|
|
CurveComposite CompoA, CompoB ;
|
|
if ( ! GetBordersInBox( *pStmBasic, BBoxCurr, nStartTria, ptStartTria, dSize, dSizeTol, true, true,
|
|
dAngTol, 2. * EPS_SMALL, setAvoidTria, setNewTria, mapTriaClass, CompoA, CompoB,
|
|
ptMid, ptMid1, nTria, nTria1, bBreak, bStop))
|
|
return false ;
|
|
|
|
// Se triangolo iniziale non significativo, devo sceglierne un altro
|
|
if ( bBreak) {
|
|
return true ; // ??? come lo comunico all'utente
|
|
}
|
|
// Se ho già ricavato tutto, ho finito
|
|
else if ( bStop) {
|
|
for ( auto Iter = setNewTria.begin() ; Iter != setNewTria.end() ; ++ Iter)
|
|
vOtherTria.push_back( *Iter) ;
|
|
}
|
|
// Altrimenti ricerco gli altri triangoli validi
|
|
else {
|
|
// I nuovi triangoli della striscia vengono memorizzati
|
|
for ( auto Iter = setNewTria.begin() ; Iter != setNewTria.end() ; ++ Iter)
|
|
setValidTria.insert( *Iter) ;
|
|
|
|
// Ricerco nella direzione di ptMid1
|
|
bool bRelFirst = true ;
|
|
if ( ! MarchAlongPath( BBoxCurr, *pStmBasic, setAvoidTria, setValidTria, mapTriaClass, true,
|
|
ptMid1, nTria1, dSize, dSizeTol, dAngTol, bRelFirst, CompoA, CompoB, bStop))
|
|
return false ;
|
|
|
|
// Se Non ho ricavato un'intera striscia chiusa, allora avvio la ricerca in direzione di ptMid
|
|
if ( ! bStop) {
|
|
if ( ! MarchAlongPath( BBoxCurr, *pStmBasic, setAvoidTria, setValidTria, mapTriaClass, false,
|
|
ptMid, nTria, dSize, dSizeTol, dAngTol, bRelFirst, CompoA, CompoB, bStop))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// Aggiusto i Triangoli in base alla loro classificazione ( modificandoli se necessario)
|
|
TRIA3DVECTOR vAllTria ; vAllTria.reserve( pStmBasic->GetTriangleCount()) ;
|
|
if ( ! AdjustClassifiedTriangles( setValidTria, pStmBasic, mapTriaClass, vAllTria))
|
|
return false ;
|
|
|
|
// Creo la superficie TriMesh dai triangoli ricavati
|
|
StmFromTriangleSoup TriaSoup ;
|
|
TriaSoup.Start() ;
|
|
for ( const Triangle3d& Tria : vAllTria) {
|
|
if ( ! TriaSoup.AddTriangle( Tria))
|
|
return false ;
|
|
}
|
|
TriaSoup.End() ;
|
|
PtrOwner<ISurfTriMesh> pStmTria( TriaSoup.GetSurf()) ;
|
|
|
|
if ( IsNull( pStmTria) || ! pStmTria->IsValid() || pStmTria->GetTriangleCount() == 0)
|
|
return false ;
|
|
return ( pStmAdjTria->CopyFrom( pStmTria)) ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Funzione per la ricerca di Superfici in tangenza ( Simile a quanto descritto nella funzione sopra)
|
|
bool
|
|
GetTrimmingAdjSurfs( const CISURFPVECTOR& vSurf, const CISURFPVECTOR& vOtherSurf, double dLinTol,
|
|
double dAngTol, double dFaceAngTol, INTVECTOR& vIndOtherSurf)
|
|
{
|
|
// Verifica dei parametri
|
|
for ( const ISurf* pSurf : vSurf) {
|
|
if ( pSurf == nullptr || ! pSurf->IsValid())
|
|
return false ;
|
|
}
|
|
for ( const ISurf* pOtherSurf : vOtherSurf) {
|
|
if ( pOtherSurf == nullptr || ! pOtherSurf->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
// Recupero le Patch delle superfici
|
|
SURFPATCHESVECTOR vSurfPatches ; vSurfPatches.reserve( vSurf.size() + vOtherSurf.size()) ;
|
|
// --- Superfici Selezionate
|
|
for ( int nS = 0 ; nS < ssize( vSurf) ; ++ nS) {
|
|
vSurfPatches.emplace_back( SurfPatches()) ;
|
|
vSurfPatches.back().pSurf = vSurf[nS] ;
|
|
if ( ! CalcSurfPatch( vSurf[nS], dLinTol, dAngTol, vSurfPatches.back()))
|
|
return false ;
|
|
}
|
|
// --- Altre Superfici
|
|
for ( int nS = 0 ; nS < ssize( vOtherSurf) ; ++ nS) {
|
|
vSurfPatches.emplace_back( SurfPatches()) ;
|
|
vSurfPatches.back().pSurf = vOtherSurf[nS] ;
|
|
if ( ! CalcSurfPatch( vOtherSurf[nS], dLinTol, dAngTol, vSurfPatches.back()))
|
|
return false ;
|
|
}
|
|
|
|
#if DEBUG_SURF_PATCHES
|
|
VT.clear() ; VC.clear() ;
|
|
for ( int i = 0 ; i < ssize( vSurfPatches) ; ++ i) {
|
|
Color myCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ;
|
|
for ( int j = 0 ; j < ssize( vSurfPatches[i].vCompoBorders) ; ++ j) {
|
|
VT.emplace_back( vSurfPatches[i].vCompoBorders[j].Clone()) ;
|
|
VC.emplace_back( myCol) ;
|
|
set<double>& setPar = vSurfPatches[i].vSetPatchPar[j] ;
|
|
for ( auto Iter = setPar.begin() ; Iter != setPar.end() ; ++ Iter) {
|
|
Point3d ptCurr ;
|
|
vSurfPatches[i].vCompoBorders[j].GetPointD1D2( *Iter, ICurve::FROM_MINUS, ptCurr) ;
|
|
PtrOwner<IGeoPoint3d> ptGeoCurr( CreateGeoPoint3d()) ; ptGeoCurr->Set( ptCurr) ;
|
|
VT.emplace_back( ptGeoCurr->Clone()) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
}
|
|
}
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\SurfPatches.nge") ;
|
|
#endif
|
|
|
|
// Se necessario, Splitto le Patch per far combiaciare gli estremi
|
|
vector<BOOLVECTOR> matBorderModif( vSurfPatches.size()) ;
|
|
for ( int i = 0 ; i < ssize( vSurfPatches) ; ++ i) {
|
|
// Recupero la SurfPatch corrente
|
|
SurfPatches& CurrSurfPatch = vSurfPatches[i] ;
|
|
// Recupero i Suoi Bordi e i Parametri associati
|
|
const SimpleBorderVector& vSimpleBorder = CurrSurfPatch.vBorderPatches ;
|
|
matBorderModif[i].resize( vSimpleBorder.size(), false) ;
|
|
const COMPOVECTOR& vCompoBorders = CurrSurfPatch.vCompoBorders ;
|
|
vector<set<double>>& vSetSplitPar = CurrSurfPatch.vSetPatchPar ;
|
|
// Scorro i Bordi della Superficie
|
|
for ( int nCurrBorder = 0 ; nCurrBorder < ssize( vSimpleBorder) ; ++ nCurrBorder) {
|
|
// Recupero la Composita associata al Bordo Corrente e il suo Box
|
|
const CurveComposite& CurrCompoBorder = vCompoBorders[nCurrBorder] ;
|
|
BBox3d BBoxCurrCompoBorder ; CurrCompoBorder.GetLocalBBox( BBoxCurrCompoBorder) ;
|
|
// Recupero le Patch associate
|
|
const SimpleBorder& vPatchCurrBorder = vSimpleBorder[nCurrBorder] ;
|
|
// Recupero il vettore dei Parametri di Split
|
|
set<double>& setSplitPar = vSetSplitPar[nCurrBorder] ;
|
|
#if DEBUG_SURF_PATCHES
|
|
VC.clear() ; VT.clear() ;
|
|
VT.emplace_back( CurrCompoBorder.Clone()) ;
|
|
VC.emplace_back( RED) ;
|
|
for ( auto Iter = setSplitPar.begin() ; Iter != setSplitPar.end() ; ++ Iter) {
|
|
Point3d ptCurr ;
|
|
CurrCompoBorder.GetPointD1D2( *Iter, ICurve::FROM_MINUS, ptCurr) ;
|
|
PtrOwner<IGeoPoint3d> ptGeoCurr( CreateGeoPoint3d()) ; ptGeoCurr->Set( ptCurr) ;
|
|
VT.emplace_back( ptGeoCurr->Clone()) ;
|
|
VC.emplace_back( ORANGE) ;
|
|
}
|
|
#endif
|
|
// Scorro le altre SurfPatches
|
|
for ( int j = 0 ; j < ssize( vSurfPatches) ; ++ j) {
|
|
// Se stessa SurfPath, passo alla successiva
|
|
if ( i == j)
|
|
continue ;
|
|
// Recupero l'altra SurfPatch
|
|
SurfPatches& OtherSurfPatch = vSurfPatches[j] ;
|
|
// Recupero i Suoi Bordi e i Parametri associati
|
|
const SimpleBorderVector& vSimpleOtherBorder = OtherSurfPatch.vBorderPatches ;
|
|
#if DEBUG_SURF_PATCHES
|
|
const COMPOVECTOR& vCompoBorders = OtherSurfPatch.vCompoBorders ;
|
|
#endif
|
|
// Scorro i suoi bordi
|
|
for ( int nOtherBorder = 0 ; nOtherBorder < ssize( vSimpleOtherBorder) ; ++ nOtherBorder) {
|
|
// Recupero le Patch associate
|
|
const SimpleBorder& vPatchOtherBorder = vSimpleOtherBorder[nOtherBorder] ;
|
|
// Recupero la Composita associata al Bordo
|
|
#if DEBUG_SURF_PATCHES
|
|
const CurveComposite& OtherCompoBorder = vCompoBorders[nOtherBorder] ;
|
|
VT.emplace_back( OtherCompoBorder.Clone()) ;
|
|
VC.emplace_back( BLACK) ;
|
|
#endif
|
|
// Scorro le sue Patches
|
|
for ( int nOtherPatch = 0 ; nOtherPatch < ssize( vPatchOtherBorder) ; ++ nOtherPatch) {
|
|
// Recupero il Box della patch
|
|
BBox3d BBoxOtherPath = OtherSurfPatch.matBox3dPatches[nOtherBorder][nOtherPatch] ;
|
|
// Se Tali Box ( espansi già della tolleranza lineare) non interferiscono, allora passo
|
|
// alla Patch successiva
|
|
if ( ! BBoxCurrCompoBorder.Overlaps( BBoxOtherPath))
|
|
continue ;
|
|
// Recupero gli estremi della Path attuale
|
|
DistPointCurve DPTCRV( vPatchOtherBorder[nOtherPatch].ptStart, CurrCompoBorder) ;
|
|
double dDist = INFINITO ;
|
|
if ( DPTCRV.GetDist( dDist) && dDist < dLinTol) {
|
|
Point3d ptMinDist ;
|
|
int nFlag = 0 ;
|
|
if ( DPTCRV.GetMinDistPoint( 0., ptMinDist, nFlag)) {
|
|
bool bInsert = true ;
|
|
for ( int nCurrPatch = 0 ; bInsert && nCurrPatch < ssize( vPatchCurrBorder) ; ++ nCurrPatch) {
|
|
const SimplePatch& Patch = vPatchCurrBorder[nCurrPatch] ;
|
|
bInsert = ( ! AreSamePointEpsilon( Patch.ptStart, ptMinDist, dLinTol) &&
|
|
! AreSamePointEpsilon( Patch.ptEnd, ptMinDist, dLinTol)) ;
|
|
}
|
|
if ( bInsert) {
|
|
double dPar = -1. ;
|
|
if ( DPTCRV.GetParamAtMinDistPoint( 0., dPar, nFlag)) {
|
|
setSplitPar.insert( dPar) ;
|
|
matBorderModif[i][nCurrBorder] = true ;
|
|
#if DEBUG_SURF_PATCHES
|
|
PtrOwner<IGeoPoint3d> ptGeoCurr( CreateGeoPoint3d()) ;
|
|
ptGeoCurr->Set( ptMinDist) ;
|
|
VT.emplace_back( ptGeoCurr->Clone()) ;
|
|
VC.emplace_back( AQUA) ;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if DEBUG_SURF_PATCHES
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\SplitPathces.nge") ;
|
|
#endif
|
|
}
|
|
// Scorro tutte le Surf Patches
|
|
for ( int i = 0 ; i < ssize( vSurfPatches) ; ++ i) {
|
|
// Recupero la SurfPatch corrente
|
|
SurfPatches& CurrSurfPatch = vSurfPatches[i] ;
|
|
// Recupero i Suoi Bordi e i Parametri associati
|
|
SimpleBorderVector& vSimpleBorder = CurrSurfPatch.vBorderPatches ;
|
|
COMPOMATRIX& matCompoPatches = CurrSurfPatch.matCompoPatches ;
|
|
COMPOVECTOR& vCompoBorders = CurrSurfPatch.vCompoBorders ;
|
|
const vector<set<double>>& vSetSplitPar = CurrSurfPatch.vSetPatchPar ;
|
|
// Scorro i Bordi
|
|
for ( int nCurrBorder = 0 ; nCurrBorder < ssize( vSimpleBorder) ; ++ nCurrBorder) {
|
|
// Se il bordo corrente non ha subito Split, passo al successivo
|
|
if ( ! matBorderModif[i][nCurrBorder])
|
|
continue ;
|
|
// Pulisco la Composita associata a tale Bordo
|
|
COMPOVECTOR& vCompoPatches = matCompoPatches[nCurrBorder] ;
|
|
vCompoPatches.clear() ;
|
|
// Recupero la Curva Composita corrente
|
|
CurveComposite& CurrCompo = vCompoBorders[nCurrBorder] ;
|
|
// Inizializzo il nuovo Bordo semplice formato dalle Patch
|
|
SimpleBorder newSimpleBorder ;
|
|
// Recupero il vettore dei Parametri di Split
|
|
const set<double>& setSplitPar = vSetSplitPar[nCurrBorder] ;
|
|
double dLastPar = 0 ;
|
|
for ( auto Iter = setSplitPar.begin() ; Iter != setSplitPar.end() ; ++ Iter) {
|
|
PtrOwner<ICurve> pCrvPatch( CurrCompo.CopyParamRange( dLastPar, *Iter)) ;
|
|
if ( ! IsNull( pCrvPatch) && pCrvPatch->IsValid()) {
|
|
PtrOwner<CurveComposite> pCompoPatch( ConvertCurveToBasicComposite( Release( pCrvPatch))) ;
|
|
if ( IsNull( pCompoPatch) || ! pCompoPatch->IsValid())
|
|
return false ;
|
|
vCompoPatches.emplace_back( *Get( pCompoPatch)) ;
|
|
Point3d ptStart ; vCompoPatches.back().GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; vCompoPatches.back().GetEndPoint( ptEnd) ;
|
|
newSimpleBorder.emplace_back( ptStart, ptEnd, nCurrBorder, false) ;
|
|
dLastPar = *Iter ;
|
|
}
|
|
}
|
|
vSimpleBorder[nCurrBorder] = newSimpleBorder ;
|
|
}
|
|
}
|
|
#if DEBUG_SURF_PATCHES
|
|
VT.clear() ; VC.clear() ;
|
|
for ( int i = 0 ; i < ssize( vSurfPatches) ; ++ i) {
|
|
Color myCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ;
|
|
for ( int j = 0 ; j < ssize( vSurfPatches[i].matCompoPatches) ; ++ j) {
|
|
for ( int k = 0 ; k < ssize( vSurfPatches[i].matCompoPatches[j]) ; ++ k) {
|
|
VT.emplace_back( vSurfPatches[i].matCompoPatches[j][k].Clone()) ;
|
|
VC.emplace_back( myCol) ;
|
|
IGeoPoint3d* _pt = CreateGeoPoint3d() ; _pt->Set( vSurfPatches[i].vBorderPatches[j][k].ptStart) ;
|
|
VT.emplace_back( _pt) ;
|
|
VC.emplace_back( AQUA) ;
|
|
IGeoPoint3d* _pt1 = CreateGeoPoint3d() ; _pt1->Set( vSurfPatches[i].vBorderPatches[j][k].ptEnd) ;
|
|
VT.emplace_back( _pt1) ;
|
|
VC.emplace_back( AQUA) ;
|
|
}
|
|
}
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\SplitPatch.nge") ;
|
|
#endif
|
|
|
|
// Verifico ora se le Normali nei Tratti di giunzione tra Patches sono al di sotto
|
|
// della tolleranza angolare
|
|
INTVECTOR vSurfToCheck ;
|
|
INTSET setSurf ;
|
|
for ( int i = 0 ; i < ssize( vSurf) ; ++ i)
|
|
vSurfToCheck.push_back( i) ;
|
|
|
|
// Finchè ho una superficie da analizzare
|
|
while ( ! vSurfToCheck.empty()) {
|
|
// Recupero l'indice della superficie corrente
|
|
int nSurf = vSurfToCheck.back() ;
|
|
// Elimino l'indice corrente
|
|
vSurfToCheck.pop_back() ;
|
|
// Se superficie non analizzata, la aggiungo come visitata
|
|
auto IterSurf = setSurf.find( nSurf) ;
|
|
if ( IterSurf == setSurf.end())
|
|
setSurf.insert( nSurf) ;
|
|
// Se già visitata, passo alla successiva
|
|
else
|
|
continue ;
|
|
|
|
// Recupero la Surf Patch Corrente
|
|
SurfPatches& currSurfPatch = vSurfPatches[nSurf] ;
|
|
// Recupero la Superficie corrente
|
|
const ISurf* pCurrSurf = currSurfPatch.pSurf ;
|
|
// Recupero il Box Corrente della Superficie
|
|
const BBox3d& BBoxCurrSurf = currSurfPatch.BoxSurf ;
|
|
|
|
// Scorro i suoi Bordi semplici
|
|
for ( int nCurrBorder = 0 ; nCurrBorder < ssize( currSurfPatch.vBorderPatches) ; ++ nCurrBorder) {
|
|
// Recupero il Bordo Corrente
|
|
SimpleBorder& CurrSimpleBorder = currSurfPatch.vBorderPatches[nCurrBorder] ;
|
|
// Scorro le sua Patches
|
|
for ( int nCurrPatch = 0 ; nCurrPatch < ssize( CurrSimpleBorder) ; ++ nCurrPatch) {
|
|
// Recupero la Patch attuale
|
|
SimplePatch& currPatch = CurrSimpleBorder[nCurrPatch] ;
|
|
// Se Patch già analizzata, passo alla successiva
|
|
if ( currPatch.bErase)
|
|
continue ;
|
|
currPatch.bErase = true ;
|
|
|
|
// Scorro le altre Superfici
|
|
for ( int nOtherSurf = 0 ; nOtherSurf < ssize( vSurfPatches) ; ++ nOtherSurf) {
|
|
// Se stessa superficie, passo alla successiva
|
|
if ( nOtherSurf == nSurf)
|
|
continue ;
|
|
// Se superficie già analizzata, passo alla successiva
|
|
if ( setSurf.find( nOtherSurf) != setSurf.end())
|
|
continue ;
|
|
|
|
// Recupero la Surf Patch
|
|
SurfPatches OtherSurfPatch = vSurfPatches[nOtherSurf] ;
|
|
// Recupero la Superficie
|
|
const ISurf* pOtherSurf = OtherSurfPatch.pSurf ;
|
|
// Recupero il Box della Superficie
|
|
const BBox3d& BBoxOtherSurf = OtherSurfPatch.BoxSurf ;
|
|
// Se non c'è Intersezione dei Box ( opportunamente estesi), passo alla SurfPatch successiva
|
|
if ( ! BBoxCurrSurf.Overlaps( BBoxOtherSurf))
|
|
continue ;
|
|
|
|
// Scorro i suoi Bordi semplici
|
|
for ( int nOtherBorder = 0 ; nOtherBorder < ssize( OtherSurfPatch.vBorderPatches) ; ++ nOtherBorder) {
|
|
// Recupero il Bordo
|
|
SimpleBorder& OtherSimpleBorder = OtherSurfPatch.vBorderPatches[nOtherBorder] ;
|
|
// Scorro le sue Patches
|
|
for ( int nOtherPatch = 0 ; nOtherPatch < ssize( OtherSimpleBorder) ; ++ nOtherPatch) {
|
|
// Recupero la Patch di confronto
|
|
SimplePatch& OtherPatch = OtherSimpleBorder[nOtherPatch] ;
|
|
// Se Patch già analizzata, passo alla successiva
|
|
if ( OtherPatch.bErase)
|
|
continue ;
|
|
// Se gli estremi coincidono
|
|
if ( ( AreSamePointEpsilon( currPatch.ptStart, OtherPatch.ptStart, dLinTol) &&
|
|
AreSamePointEpsilon( currPatch.ptEnd, OtherPatch.ptEnd, dLinTol)) ||
|
|
( AreSamePointEpsilon( currPatch.ptStart, OtherPatch.ptEnd, dLinTol) &&
|
|
AreSamePointEpsilon( currPatch.ptEnd, OtherPatch.ptStart, dLinTol))) {
|
|
// Controllo se le tangenze rimangono nella tolleranza
|
|
bool bTangent = true ;
|
|
// Campiono la Patch corrente ( passando per la Composita associata)
|
|
const int MAX_POINTS = 5 ;
|
|
BIPNTVECTOR vPtAPtB ; vPtAPtB.reserve( MAX_POINTS + 1) ;
|
|
CurveComposite& currCompoPatch = currSurfPatch.matCompoPatches[nCurrBorder][nCurrPatch] ;
|
|
CurveComposite& otherCompoPatch = OtherSurfPatch.matCompoPatches[nOtherBorder][nOtherPatch] ;
|
|
bTangent = ( currCompoPatch.IsValid() && otherCompoPatch.IsValid()) ;
|
|
if ( bTangent) {
|
|
double dLen = 0.;
|
|
currCompoPatch.GetLength( dLen) ;
|
|
for ( int i = 0 ; bTangent && i <= MAX_POINTS ; ++ i) {
|
|
double dU = 0. ;
|
|
currCompoPatch.GetParamAtLength( ( dLen / MAX_POINTS) * i, dU) ;
|
|
Point3d ptCurr ;
|
|
currCompoPatch.GetPointD1D2( dU, ICurve::FROM_MINUS, ptCurr) ;
|
|
Point3d ptOther ;
|
|
int nFlag = 0 ;
|
|
if ( ! DistPointCurve( ptCurr, otherCompoPatch).GetMinDistPoint( 0., ptOther, nFlag))
|
|
bTangent = false ;
|
|
// Recupero le due normali sulle curva patch campionate
|
|
Vector3d vtNCurr = V_INVALID, vtNOther = V_INVALID ;
|
|
if ( ! GetNormalAtPoint( pCurrSurf, ptCurr, vtNCurr) ||
|
|
! GetNormalAtPoint( pOtherSurf, ptOther, vtNOther))
|
|
return false ;
|
|
bTangent = ( vtNCurr * vtNOther > cos( ( dFaceAngTol - EPS_ANG_SMALL) * DEGTORAD)) ;
|
|
}
|
|
}
|
|
// Se superficie in tangenza, aggiorno i parametri
|
|
if ( bTangent) {
|
|
#if DEBUG_SURF_PATCHES
|
|
VC.clear() ; VT.clear() ;
|
|
VT.emplace_back( pCurrSurf->Clone()) ;
|
|
VC.emplace_back( LIME) ;
|
|
VT.emplace_back( pOtherSurf->Clone()) ;
|
|
VC.emplace_back( GREEN) ;
|
|
VT.emplace_back( currCompoPatch.Clone()) ;
|
|
VC.emplace_back( RED) ;
|
|
VT.emplace_back( otherCompoPatch.Clone()) ;
|
|
VC.emplace_back( BLUE) ;
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\Test.nge") ;
|
|
#endif
|
|
vSurfToCheck.emplace_back( nOtherSurf) ;
|
|
OtherPatch.bErase = true ;
|
|
if ( nOtherSurf >= ssize( vSurf) &&
|
|
find( vIndOtherSurf.begin(), vIndOtherSurf.end(), nOtherSurf - ssize( vSurf)) == vIndOtherSurf.end())
|
|
vIndOtherSurf.push_back( nOtherSurf - ssize( vSurf)) ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if DEBUG_SURF_PATCHES
|
|
VT.clear() ; VC.clear() ;
|
|
for ( const int& nInd : vIndOtherSurf) {
|
|
VT.emplace_back( vOtherSurf[nInd]->Clone()) ;
|
|
VC.emplace_back( GRAY) ;
|
|
}
|
|
for ( const ISurf* pSurf : vSurf) {
|
|
VT.emplace_back( pSurf->Clone()) ;
|
|
VC.emplace_back( YELLOW) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\SurfPatches.nge") ;
|
|
#endif
|
|
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Funzione per l'estrazione dei Bordi di Fori e Asole dato un vettore di Superfici
|
|
bool
|
|
GetTrimmingHoleBorders( const CISURFPVECTOR& vpSurf, const Point3d& ptRef, double dSurfLinTol, double dSurfAngTol,
|
|
double dEdgeLinTol, double dEdgeAngTol, double dEdgeThick,
|
|
ICRVCOMPOPOVECTOR& vHoleBorders)
|
|
{
|
|
vHoleBorders.clear() ;
|
|
// Se non ho superfici, non faccio nulla
|
|
if ( vpSurf.empty())
|
|
return true ;
|
|
// Verifico che tutte le superfici siano valide
|
|
for ( const ISurf* pSurf : vpSurf) {
|
|
if ( pSurf == nullptr || ! pSurf->IsValid())
|
|
return false ;
|
|
}
|
|
|
|
#if DEBUG_HOLES
|
|
VT.clear() ; VC.clear() ;
|
|
for ( int i = 0 ; i < ssize( vpSurf) ; ++ i) {
|
|
VT.emplace_back( vpSurf[i]->Clone()) ;
|
|
VC.emplace_back( Color( 0., 0., 0., 0.25)) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\HoleBorders.nge") ;
|
|
#endif
|
|
|
|
// Aggiusto le tolleranze
|
|
double dMySurfLinTol = Clamp( dSurfLinTol, EPS_SMALL, 1e5) ;
|
|
double dMySurfAngTol = Clamp( dSurfAngTol, EPS_ANG_SMALL, 60.) ;
|
|
double dMyEdgeLinTol = Clamp( dEdgeLinTol, EPS_SMALL, 1e5) ;
|
|
double dMyEdgeAngTol = Clamp( dEdgeAngTol, EPS_ANG_SMALL, 60.) ;
|
|
double dMyEdgeThick = max( 10. * EPS_SMALL, dEdgeThick) ; // coerente con Offset
|
|
|
|
// Dalle superfici ricavo i bordi Grezzi
|
|
SELVECTOR vSelIds ; vSelIds.resize( vpSurf.size()) ;
|
|
for ( int i = 0 ; i < ssize( vpSurf) ; ++ i)
|
|
vSelIds.emplace_back( SelData( i, SEL_SUB_ALL)) ;
|
|
ICRVCOMPOPOVECTOR vRawEdges ;
|
|
if ( ! GetTrimmingRawEdges( vpSurf, vSelIds, dMySurfLinTol, dMySurfAngTol, vRawEdges))
|
|
return false ;
|
|
if ( vRawEdges.empty())
|
|
return true ;
|
|
|
|
#if DEBUG_HOLES
|
|
for ( int i = 0 ; i < ssize( vRawEdges) ; ++ i) {
|
|
VT.emplace_back( CloneCurveComposite( vRawEdges[i])) ;
|
|
VC.emplace_back( RED) ;
|
|
}
|
|
SaveGeoObj( VT, VC, "C:\\Temp\\HoleBorders.nge") ;
|
|
#endif
|
|
|
|
// Tra le curve ricavate, tengo in considerazione solo Fori e Asole
|
|
enum HoleType { CIRCLE = 0, BUTTONHOLE = 1 } ;
|
|
struct Hole {
|
|
PtrOwner<ICurveComposite> pCompoHole ;
|
|
int nType ;
|
|
Frame3d frCompo ;
|
|
double dRad ;
|
|
double dLen ;
|
|
Hole( ICurveComposite* pHole, int nT, const Frame3d& frC, double dR, double dL)
|
|
: nType( nT), frCompo( frC), dRad( dR), dLen( dL) {
|
|
pCompoHole.Set( pHole) ;
|
|
}
|
|
} ;
|
|
vector<Hole> vHoles ; vHoles.reserve( vRawEdges.size()) ;
|
|
for ( int i = 0 ; i < ssize( vRawEdges) ; ++ i) {
|
|
// Verifico che la curva sia valida
|
|
if ( IsNull( vRawEdges[i]) || ! vRawEdges[i]->IsValid())
|
|
continue ;
|
|
// Recupero la PolyLine associata
|
|
PolyLine PL ;
|
|
if ( ! vRawEdges[i]->ApproxWithLines( EPS_SMALL, EPS_ANG_SMALL, ICurve::APL_SPECIAL, PL))
|
|
return false ;
|
|
// Verifico che -- CIRCONFERENZA --
|
|
Frame3d frCompo ;
|
|
double dRad = 0. ;
|
|
CurveArc CrvCircle ;
|
|
if ( IsBorderACircle( PL, dMyEdgeLinTol, dMyEdgeAngTol, frCompo, dRad, CrvCircle)) {
|
|
PtrOwner<ICurveComposite> pCompoCircle( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoCircle) || ! pCompoCircle->AddCurve( CrvCircle))
|
|
return false ;
|
|
vHoles.emplace_back( Release( pCompoCircle), 0, frCompo, dRad, -1.) ;
|
|
continue ;
|
|
}
|
|
// Verifico se -- ASOLA --
|
|
double dLen = 0. ;
|
|
CurveComposite CompoBtHole ;
|
|
if ( IsBorderAButtonHole( PL, dMyEdgeLinTol, dMyEdgeAngTol, frCompo, dLen, dRad, CompoBtHole)) {
|
|
PtrOwner<ICurveComposite> pCompoBtHole( CreateCurveComposite()) ;
|
|
if ( IsNull( pCompoBtHole) || ! pCompoBtHole->AddCurve( CompoBtHole))
|
|
return false ;
|
|
vHoles.emplace_back( Release( pCompoBtHole), HoleType::BUTTONHOLE, frCompo, dRad, dLen) ;
|
|
continue ;
|
|
}
|
|
}
|
|
|
|
// Vedo se ci sono curve da accoppiare tra di loro
|
|
vHoleBorders.reserve( vHoles.size()) ;
|
|
for ( int i = 0 ; i < ssize( vHoles) - 1 ; ++ i) {
|
|
// Recupero la curva corrente, se non presente allora passo alla successiva
|
|
if ( IsNull( vHoles[i].pCompoHole))
|
|
continue ;
|
|
int nIndJ = -1 ;
|
|
// Scorro le curve successive
|
|
for ( int j = i + 1 ; nIndJ == -1 && j < ssize( vHoles) ; ++ j) {
|
|
// Recupero la curva corrente, se non presente allora passo alla successiva
|
|
if ( IsNull( vHoles[j].pCompoHole))
|
|
continue ;
|
|
// Se il tipo è differente non possono essere in coppia
|
|
if ( vHoles[i].nType != vHoles[j].nType)
|
|
continue ;
|
|
// Essendo della stessa topologia, verifico i parametri
|
|
bool bCouple = false ;
|
|
Vector3d vtCenCen ;
|
|
if ( vHoles[i].nType == HoleType::CIRCLE) {
|
|
bCouple = ( abs( vHoles[i].dRad - vHoles[j].dRad) < dMyEdgeLinTol &&
|
|
AreSameOrOppositeVectorEpsilon( vHoles[i].frCompo.VersZ(), vHoles[j].frCompo.VersZ(), dMyEdgeLinTol)) ;
|
|
if ( bCouple) {
|
|
vtCenCen = ( vHoles[i].frCompo.Orig() - vHoles[j].frCompo.Orig()) ;
|
|
bCouple = ( vtCenCen.SqLen() > dMyEdgeLinTol * dMyEdgeLinTol) ;
|
|
if ( bCouple) {
|
|
bCouple = ( vtCenCen.Normalize() &&
|
|
AreSameOrOppositeVectorEpsilon( vtCenCen, vHoles[i].frCompo.VersZ(), dMyEdgeLinTol)) ;
|
|
}
|
|
}
|
|
}
|
|
else if ( vHoles[i].nType == HoleType::BUTTONHOLE) {
|
|
bCouple = ( abs( vHoles[i].dRad - vHoles[j].dRad) < dMyEdgeLinTol &&
|
|
abs( vHoles[i].dLen - vHoles[j].dLen) < dMyEdgeLinTol &&
|
|
AreSameOrOppositeVectorEpsilon( vHoles[i].frCompo.VersZ(), vHoles[j].frCompo.VersZ(), dMyEdgeLinTol)) ;
|
|
if ( bCouple) {
|
|
vtCenCen = ( vHoles[i].frCompo.Orig() - vHoles[j].frCompo.Orig()) ;
|
|
bCouple = ( vtCenCen.SqLen() > dMyEdgeLinTol * dMyEdgeLinTol) ;
|
|
if ( bCouple) {
|
|
bCouple = ( vtCenCen.Normalize() &&
|
|
AreSameOrOppositeVectorEpsilon( vtCenCen, vHoles[i].frCompo.VersZ(), dMyEdgeLinTol)) ;
|
|
}
|
|
}
|
|
}
|
|
if ( bCouple)
|
|
nIndJ = j ;
|
|
}
|
|
|
|
// Determino l'indice della curva da conservare
|
|
int nInd = i ;
|
|
// Se curva non in coppia, allora ho finito, altrimenti devo sceglierne una
|
|
if ( nIndJ != -1) {
|
|
int nIndToErase = nIndJ ;
|
|
// Recupero i due Box
|
|
BBox3d BBoxI ; vHoles[i].pCompoHole->GetLocalBBox( BBoxI) ;
|
|
BBox3d BBoxJ ; vHoles[nIndJ].pCompoHole->GetLocalBBox( BBoxJ) ;
|
|
// Recupero le Coordinate massime di ZGlob
|
|
double dZI = BBoxI.GetMax().z ;
|
|
double dZJ = BBoxJ.GetMax().z ;
|
|
// Se le 2 curve hanno ZGlob massime differente tra loro
|
|
if ( abs( dZI - dZJ) > 5. * EPS_SMALL) {
|
|
// Tengo la Curva con ZGlob massima
|
|
if ( dZJ > dZI)
|
|
swap( nInd, nIndToErase) ;
|
|
}
|
|
// Se invece le ZGlob sono circa uguali
|
|
else {
|
|
// Tengo la Curva più distante dal punto di Riferimento ( se Valido)
|
|
if ( ptRef.IsValid()) {
|
|
double dSqDistI = INFINITO, dSqDistJ = INFINITO ;
|
|
DistPointCurve( ptRef, *vHoles[i].pCompoHole).GetSqDist( dSqDistI) ;
|
|
DistPointCurve( ptRef, *vHoles[nIndJ].pCompoHole).GetSqDist( dSqDistJ) ;
|
|
if ( dSqDistJ > dSqDistI)
|
|
swap( nInd, nIndToErase) ;
|
|
}
|
|
}
|
|
// Elimino la curva non significativa delle 2
|
|
vHoles[nIndToErase].pCompoHole.Reset() ;
|
|
}
|
|
|
|
// Imposto Estrusione alla curva nInd-esima
|
|
Vector3d vtExtr = vHoles[nInd].frCompo.VersZ() ;
|
|
// Se Estrusione perpendicolare a Z_AX
|
|
if ( OrthoCompo( Z_AX, vtExtr).Len() > 1 - 5. * EPS_SMALL) {
|
|
// Se punto di Riferimento valido
|
|
if ( ptRef.IsValid()) {
|
|
Vector3d vtCenCen = vHoles[nInd].frCompo.Orig() - ptRef ;
|
|
vtCenCen.Normalize() ;
|
|
vHoles[nInd].pCompoHole->SetExtrusion( ( vtExtr * vtCenCen > 0.) ? vtExtr : - vtExtr) ;
|
|
}
|
|
else
|
|
vHoles[nInd].pCompoHole->SetExtrusion( vtExtr) ;
|
|
}
|
|
// Se estrusione non perpendicolare a Z_AX
|
|
else
|
|
vHoles[nInd].pCompoHole->SetExtrusion( ( vtExtr * Z_AX > 0.) ? vtExtr : - vtExtr) ;
|
|
|
|
// Imposso Spessore alla curva nInd-esima
|
|
vHoles[nInd].pCompoHole->SetThickness( - dMyEdgeThick) ;
|
|
|
|
// Memorizzo la curva nInd-esima
|
|
vHoleBorders.emplace_back( Release( vHoles[nInd].pCompoHole)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
struct PntInfo{
|
|
Point3d pt ;
|
|
double dDist ;
|
|
Vector3d vtPos ;
|
|
PntInfo( const Point3d& _pt, double _dDist, const Vector3d& _vtPos) :
|
|
pt( _pt), dDist( _dDist), vtPos( _vtPos) {;}
|
|
};
|
|
typedef vector<PntInfo> PNTINFOVECTOR ;
|
|
|
|
//------------------------------------------------------------------------------
|
|
static bool
|
|
FillPntInfo( const PNTVECTOR& vPnt, const ICurveComposite* pCC, PNTINFOVECTOR& vPntInfo)
|
|
{
|
|
for ( int i = 0 ; i < ssize( vPnt) - 3 ; i+=3) {
|
|
bool bOk = false ;
|
|
const ICurveBezier* pSubCrv = GetCurveBezier( pCC->GetCurve( i / 3)) ;
|
|
for ( int j = i == 0 ? 0 : 1 ; j <= 3 ; ++j) {
|
|
Point3d pt = pSubCrv->GetControlPoint( j, &bOk) ;
|
|
double dDist = 0 ;
|
|
Vector3d vtPos = V_NULL ;
|
|
if ( j > 0 && j < 3){
|
|
DistPointCurve dpc( pt, *pSubCrv) ;
|
|
dpc.GetDist( dDist) ;
|
|
int nFlag = - 1 ;
|
|
Point3d ptMinDist ;
|
|
dpc.GetMinDistPoint( 0., ptMinDist, nFlag) ;
|
|
vtPos = pt - ptMinDist ;
|
|
}
|
|
vPntInfo.emplace_back( pt, dDist, vtPos) ;
|
|
}
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static bool
|
|
RemoveInflexionPoints( PNTVECTOR& vPnt, PNTINFOVECTOR& vPntInfo, PNTINFOVECTOR& vPntRefInfo)
|
|
{
|
|
// se trovo tre punti di fila che sono dallo stesso lato, opposto a quello degli altri punti attorno, allora cerco di spostarli lungo la
|
|
// normale alla superficie in modo da evitare cambi di concavità
|
|
bool bSameSideAsPrev = true ;
|
|
double dSmallDist = 5 * EPS_SMALL ;
|
|
for ( int i = 2 ; i < ssize( vPntInfo) - 2 ; ++i) {
|
|
// se è un punto di split o sta sulla curva vado avanti
|
|
if ( vPntInfo[i].dDist < dSmallDist)
|
|
continue ;
|
|
int nPrev = vPntInfo[i-1].dDist < EPS_ZERO ? i - 2 : i - 1 ;
|
|
double dProj = vPntInfo[i].vtPos * vPntInfo[nPrev].vtPos ;
|
|
bSameSideAsPrev = dProj > EPS_ZERO ;
|
|
if ( abs(dProj) < EPS_ZERO){
|
|
int nPrevPrev = nPrev - 1 ;
|
|
dProj = vPntInfo[i].vtPos * vPntInfo[nPrevPrev].vtPos ;
|
|
bSameSideAsPrev = dProj > EPS_ZERO ;
|
|
}
|
|
if ( ! bSameSideAsPrev) {
|
|
// devo verificare anche che sia diverso anche dal successivo ( o dal quello dopo ancora, se il successivo sta sulla curva)
|
|
bool bCurrOrPrev = vPntInfo[i+1].dDist < EPS_ZERO ;
|
|
int nFirst, nSecond, nThird ;
|
|
if ( bCurrOrPrev) {
|
|
nFirst = i ;
|
|
nSecond = i + 1 ;
|
|
nThird = i + 2 ;
|
|
}
|
|
else {
|
|
nFirst = i - 2 ;
|
|
nSecond = i - 1 ;
|
|
nThird = i ;
|
|
}
|
|
int nNext = bCurrOrPrev ? i + 2 : i + 1 ;
|
|
// se il successivo è diverso ho un terzetto anomalo da aggiustare
|
|
// altrimenti ho un cambio naturale di concavità
|
|
if ( vPntInfo[i].vtPos * vPntInfo[nNext].vtPos < 0 || vPntInfo[nNext].dDist < dSmallDist) {
|
|
// ruoto il terzetto fino a matchare la tangente sull'altra curva
|
|
// ruoto il punto solo se non stava già esattamente sulla SubCrv ( che suppongo essere un tratto rettilineo)
|
|
if ( vPntInfo[nFirst].dDist > dSmallDist) {
|
|
Vector3d vtCurr = vPntInfo[nSecond].pt - vPntInfo[nFirst].pt ;
|
|
Vector3d vtRef = vPntRefInfo[nSecond].pt - vPntRefInfo[nFirst].pt ;
|
|
Vector3d vtAx = vPntRefInfo[nSecond].pt - vPntInfo[nSecond].pt ;
|
|
bool bDet = false ;
|
|
double dAng = 0 ; vtCurr.GetRotation(vtRef, vtAx, dAng, bDet) ;
|
|
if ( abs(dAng) > 170)
|
|
dAng = 180 - dAng ;
|
|
vPnt[nFirst].Rotate( vPnt[nSecond], vtAx, dAng) ;
|
|
}
|
|
if ( vPntInfo[nThird].dDist > dSmallDist) {
|
|
Vector3d vtCurr = vPntInfo[nThird].pt - vPntInfo[nSecond].pt ;
|
|
Vector3d vtRef = vPntRefInfo[nThird].pt - vPntRefInfo[nSecond].pt ;
|
|
Vector3d vtAx = vPntRefInfo[nSecond].pt - vPntInfo[nSecond].pt ;
|
|
bool bDet = false ;
|
|
double dAng = 0 ; vtCurr.GetRotation(vtRef, vtAx, dAng, bDet) ;
|
|
if ( abs(dAng) > 170)
|
|
dAng = 180 - dAng ;
|
|
vPnt[nThird].Rotate( vPnt[nSecond], vtAx, dAng) ;
|
|
}
|
|
if ( bCurrOrPrev)
|
|
i += 2 ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
// Funzione per la regolarizzazione delle curve di bordo di una lavorazione di trim
|
|
// Le curve vengono modificate entro una data tolleranza, in modo che
|
|
ISurfBezier*
|
|
RegolarizeBordersLocallyRMF( const ISurfBezier* pSurfBz, const BIPOINT& bpIsoStart, const BIPOINT& bpIsoEnd, double dTol)
|
|
{
|
|
#if DEBUG_SMOOTH_CURVATURE
|
|
VT.clear() ;
|
|
#endif
|
|
|
|
// prendo per buone le isocurve di inizio e fine tratto e devo identificare tra loro le isocurve che creano troppo twist e che sono da raddrizzare
|
|
Point3d ptS1 = bpIsoStart.first ;
|
|
Point3d ptS2 = bpIsoEnd.first ;
|
|
Vector3d vtDir1 = bpIsoStart.second - ptS1 ;
|
|
Vector3d vtDir2 = bpIsoEnd.second - ptS2 ;
|
|
|
|
int nDegU, nDegV, nSpanU, nSpanV ;
|
|
bool bRat, bTrimmed ;
|
|
pSurfBz->GetInfo( nDegU, nDegV, nSpanU, nSpanV, bRat, bTrimmed) ;
|
|
if ( nDegU != 3)
|
|
return nullptr ;
|
|
// individuo quali isocurve sono state indicate come inizio e fine
|
|
PtrOwner<ICurveComposite> pCrv1( pSurfBz->GetSingleEdge3D( false, 2)) ;
|
|
PtrOwner<ICurveComposite> pCrv2( pSurfBz->GetSingleEdge3D( false, 0)) ;
|
|
// inverto la curva corrispondente al bordo 2 della bezier per avere le due guide concordi
|
|
pCrv2->Invert() ;
|
|
double dParS1 = -1 ; double dParS2 = -1 ;
|
|
if ( ! pCrv1->GetParamAtPoint( ptS1, dParS1) || ! pCrv1->GetParamAtPoint( ptS2, dParS2))
|
|
return nullptr ;
|
|
int nUS1 = int ( dParS1) * nDegU ;
|
|
int nUS2 = int ( dParS2) * nDegU ;
|
|
if ( nUS1 > nUS2) {
|
|
swap( nUS1, nUS2) ;
|
|
swap( dParS1, dParS2) ;
|
|
swap( ptS1, ptS2) ;
|
|
swap( vtDir1, vtDir2) ;
|
|
}
|
|
PtrOwner<ICurveComposite> pCrvOrig1( ConvertCurveToComposite( pCrv1->CopyParamRange( dParS1, dParS2))) ;
|
|
PtrOwner<ICurveComposite> pCrvOrig2( ConvertCurveToComposite( pCrv2->CopyParamRange( dParS1, dParS2))) ;
|
|
|
|
/////////////////////// versione con RMF
|
|
// campiono finemente la prima curva e ottengo il punto che dovrebbe stare sull'altra curva
|
|
Vector3d vtTang1 ; pCrvOrig1->GetStartDir( vtTang1) ;
|
|
Frame3d frStart1 ; frStart1.Set( ptS1, vtTang1, vtDir1) ; // uso la tangente (come z) e l'isocurva in V (come x) per il frame iniziale
|
|
RotationMinimizingFrame rmf ; rmf.Set( pCrvOrig1, frStart1) ;
|
|
double dLenTot = 0. ; pCrvOrig1->GetLength( dLenTot) ;
|
|
double dStep = dLenTot / ceil( dLenTot) ;
|
|
FRAME3DVECTOR vRMF ;
|
|
rmf.GetFramesByStep( dStep, true, vRMF) ;
|
|
PNTVECTOR vPnt1 ;
|
|
PolyLine PL2 ;
|
|
double dLenCurr = 0. ;
|
|
double dWidth = vtDir1.Len() ;
|
|
for ( int i = 0 ; i < ssize( vRMF) ; ++i) {
|
|
double dPar ; pCrvOrig1->GetParamAtLength( dLenCurr, dPar) ;
|
|
Point3d pt0 ; pCrvOrig1->GetPointD1D2( dPar, ICurve::FROM_MINUS, pt0) ;
|
|
Point3d pt1 = pt0 + vRMF[i].VersX() * dWidth ;
|
|
vPnt1.push_back( pt1) ;
|
|
PL2.AddUPoint( i, pt1) ;
|
|
|
|
dLenCurr += dStep ;
|
|
}
|
|
|
|
CurveComposite CCToApprox2 ; CCToApprox2.FromPolyLine( PL2) ;
|
|
Vector3d vtStart2 ; pCrvOrig2->GetStartDir( vtStart2) ;
|
|
Vector3d vtEnd2 ; pCrvOrig2->GetEndDir( vtEnd2) ;
|
|
PtrOwner<ICurveComposite> pCC2( ConvertCurveToComposite( ApproxCurveWithBezier( &CCToApprox2, 0.05, vtStart2, vtEnd2))) ;
|
|
if ( IsNull( pCC2) || ! pCC2->IsValid())
|
|
return nullptr ;
|
|
|
|
// dalla seconda ricostruisco la prima
|
|
Vector3d vtTang2 ; pCC2->GetStartDir( vtTang2) ;
|
|
Frame3d frStart2 ; frStart2.Set( bpIsoStart.second, vtTang2, vtDir1) ; // uso la tangente (come z) e l'isocurva in V (come x) per il frame iniziale
|
|
RotationMinimizingFrame rmf2 ; rmf2.Set( pCC2, frStart2) ;
|
|
double dLenTot2 = 0. ; pCC2->GetLength( dLenTot2) ;
|
|
double dStep2 = dLenTot2 / ceil( dLenTot2) ;
|
|
FRAME3DVECTOR vRMF2 ;
|
|
rmf2.GetFramesByStep( dStep2, true, vRMF2) ;
|
|
PNTVECTOR vPnt0 ;
|
|
PolyLine PL1 ;
|
|
double dLenCurr2 = 0. ;
|
|
for ( int i = 0 ; i < ssize( vRMF2) ; ++i) {
|
|
double dPar ; pCC2->GetParamAtLength( dLenCurr2, dPar) ;
|
|
Point3d pt1 ; pCC2->GetPointD1D2( dPar, ICurve::FROM_MINUS, pt1) ;
|
|
Point3d pt0 = pt1 - vRMF2[i].VersX() * dWidth ;
|
|
vPnt0.push_back( pt0) ;
|
|
PL1.AddUPoint( i, pt0) ;
|
|
|
|
dLenCurr2 += dStep2 ;
|
|
}
|
|
|
|
CurveComposite CCToApprox1 ; CCToApprox1.FromPolyLine( PL1) ;
|
|
Vector3d vtStart1 ; pCrvOrig1->GetStartDir( vtStart1) ;
|
|
Vector3d vtEnd1 ; pCrvOrig1->GetEndDir( vtEnd1) ;
|
|
PtrOwner<ICurveComposite> pCC1( ConvertCurveToComposite( ApproxCurveWithBezier( &CCToApprox1, 0.05, vtStart1, vtEnd1))) ;
|
|
if ( IsNull( pCC1) || ! pCC1->IsValid())
|
|
return nullptr ;
|
|
|
|
#if DEBUG_SMOOTH_CURVATURE
|
|
for( int i = 0 ; i < ssize( vPnt1) ; ++i) {
|
|
PtrOwner<IGeoPoint3d> pPT( CreateGeoPoint3d()) ; pPT->Set( vPnt1[i]) ;
|
|
VT.push_back( Release( pPT)) ;
|
|
}
|
|
for( int i = 0 ; i < ssize( vPnt0) ; ++i) {
|
|
PtrOwner<IGeoPoint3d> pPT( CreateGeoPoint3d()) ; pPT->Set( vPnt0[i]) ;
|
|
VT.push_back( Release( pPT)) ;
|
|
}
|
|
VT.push_back( pCC1->Clone()) ;
|
|
VT.push_back( pCC2->Clone()) ;
|
|
SaveGeoObj( VT, "C:\\Temp\\bezier\\ruled\\smoothness\\regolarized_RMF.nge") ;
|
|
#endif
|
|
|
|
// controllo di essere rimasto in tolleranza
|
|
double dErr = 0 ;
|
|
CalcApproxError( pCrvOrig1, pCC1, dErr, 20) ;
|
|
if ( dErr > dTol)
|
|
return nullptr ;
|
|
dErr = 0 ;
|
|
CalcApproxError( pCrvOrig2, pCC2, dErr, 20) ;
|
|
if ( dErr > dTol)
|
|
return nullptr ;
|
|
|
|
// creo una surf di bezier uguale a quella di partenza, ma a cui cambio la parte da modificare
|
|
PtrOwner<SurfBezier> pNewSurf( CreateBasicSurfBezier()) ;
|
|
int nNewCrvs = pCC1->GetCurveCount() ;
|
|
if ( pCC2->GetCurveCount() != nNewCrvs)
|
|
return nullptr ;
|
|
int nDiff = nNewCrvs - pCrvOrig1->GetCurveCount() ;
|
|
pNewSurf->Init( nDegU, nDegV, nSpanU, nSpanV, bRat) ;
|
|
// copio la parte uguale
|
|
for ( int i = 0 ; i < nSpanU * nDegU + 1 ; ++i) {
|
|
if ( i > nUS1 && i < nUS2)
|
|
continue ;
|
|
bool bOk = false ;
|
|
Point3d pt = pSurfBz->GetControlPoint( i, 0, &bOk) ;
|
|
int nNewI = i ;
|
|
if ( i > nUS2)
|
|
nNewI = i + nDiff ;
|
|
pNewSurf->SetControlPoint( nNewI, 0, pt) ;
|
|
pt = pSurfBz->GetControlPoint( i, 1, &bOk) ;
|
|
pNewSurf->SetControlPoint( nNewI, 1, pt) ;
|
|
}
|
|
// aggiungo la parte diversa
|
|
for ( int i = 0 ; i < nNewCrvs * nDegU + 1 ; ++i) {
|
|
int nSub = i / 3 ;
|
|
int nPnt = i % 3 ;
|
|
if ( nSub == nNewCrvs) {
|
|
--nSub ;
|
|
nPnt = 3 ;
|
|
}
|
|
const ICurveBezier* pSubCrv1 = GetCurveBezier( pCC1->GetCurve( nSub)) ;
|
|
Point3d pt = pSubCrv1->GetControlPoint( nPnt) ;
|
|
int nNewI = i + nUS1 ;
|
|
pNewSurf->SetControlPoint( nNewI, 0, pt) ;
|
|
const ICurveBezier* pSubCrv2 = GetCurveBezier( pCC2->GetCurve( nSub)) ;
|
|
pt = pSubCrv2->GetControlPoint( nPnt) ;
|
|
pNewSurf->SetControlPoint( nNewI, 1, pt) ;
|
|
}
|
|
|
|
return Release( pNewSurf) ;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Funzione per la regolarizzazione delle curve di bordo di una lavorazione di trim
|
|
// Le curve vengono modificate entro una data tolleranza, in modo che
|
|
ISurfBezier*
|
|
RegolarizeBordersLocally( const ISurfBezier* pSurfBz, const BIPOINT& bpIsoStart, const BIPOINT& bpIsoEnd, double dTol, int nType)
|
|
{
|
|
if ( nType == RegolarizeType::RMF)
|
|
return RegolarizeBordersLocallyRMF( pSurfBz, bpIsoStart, bpIsoEnd, dTol) ;
|
|
|
|
#if DEBUG_SMOOTH_CURVATURE
|
|
VT.clear() ;
|
|
#endif
|
|
|
|
// prendo per buone le isocurve di inizio e fine tratto e devo identificare tra loro le isocurve che creano troppo twist e che sono da raddrizzare
|
|
Point3d ptS1 = bpIsoStart.first ;
|
|
Point3d ptS2 = bpIsoEnd.first ;
|
|
Vector3d vtDir1 = bpIsoStart.second - ptS1 ;
|
|
Vector3d vtDir2 = bpIsoEnd.second - ptS2 ;
|
|
//double dInterpolateAngTol = 4 ;
|
|
//double dAngInterp = 0 ;
|
|
//vtDir1.GetAngle( vtDir2, dAngInterp) ;
|
|
//bool bInterpolate = dAngInterp > dInterpolateAngTol ;
|
|
|
|
int nDegU, nDegV, nSpanU, nSpanV ;
|
|
bool bRat, bTrimmed ;
|
|
pSurfBz->GetInfo( nDegU, nDegV, nSpanU, nSpanV, bRat, bTrimmed) ;
|
|
if ( nDegU != 3)
|
|
return nullptr ;
|
|
// individuo quali isocurve sono state indicate come inizio e fine
|
|
PtrOwner<ICurveComposite> pCrv1( pSurfBz->GetSingleEdge3D( false, 2)) ;
|
|
PtrOwner<ICurveComposite> pCrv2( pSurfBz->GetSingleEdge3D( false, 0)) ;
|
|
// inverto la curva corrispondente al bordo 2 della bezier per avere le due guide concordi
|
|
pCrv2->Invert() ;
|
|
double dParS1 = -1 ; double dParS2 = -1 ;
|
|
if ( ! pCrv1->GetParamAtPoint( ptS1, dParS1) || ! pCrv1->GetParamAtPoint( ptS2, dParS2))
|
|
return nullptr ;
|
|
int nUS1 = int ( dParS1) * nDegU ;
|
|
int nUS2 = int ( dParS2) * nDegU ;
|
|
bool bInverted = false ;
|
|
if ( nUS1 > nUS2) {
|
|
swap( nUS1, nUS2) ;
|
|
swap( dParS1, dParS2) ;
|
|
swap( ptS1, ptS2) ;
|
|
swap( vtDir1, vtDir2) ;
|
|
bInverted = true ;
|
|
}
|
|
PtrOwner<ICurve> pCrvOrig1( pCrv1->CopyParamRange( dParS1, dParS2)) ;
|
|
PtrOwner<ICurve> pCrvOrig2( pCrv2->CopyParamRange( dParS1, dParS2)) ;
|
|
double dLen = 0 ; pCrvOrig1->GetLength( dLen) ;
|
|
|
|
///// versione con correzioni a mano
|
|
Point3d ptPrevS = ptS1 ;
|
|
Point3d ptPrevE = ! bInverted ? bpIsoStart.second : bpIsoEnd.second ;
|
|
Vector3d vtIsoPrev = ptPrevE - ptPrevS ; vtIsoPrev.Normalize() ;
|
|
Point3d ptBez ; Vector3d vtNCurr ;
|
|
pSurfBz->GetPointNrmD1D2( dParS1, 0.5, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBez, vtNCurr) ;
|
|
int nPoints = ( nUS2 - nUS1) * nDegU + 1 ;
|
|
PNTVECTOR vPnt0 ; vPnt0.reserve( nPoints) ; vPnt0.push_back( ptPrevS) ;
|
|
PNTVECTOR vPnt1 ; vPnt1.reserve( nPoints) ; vPnt1.push_back( ptPrevE) ;
|
|
// salvo il secondo punto di controllo della patch
|
|
bool bOk = false ;
|
|
Point3d ptSecond1Curr = pSurfBz->GetControlPoint( nUS1 + 1, 0, &bOk) ;
|
|
vPnt0.push_back( ptSecond1Curr) ;
|
|
Point3d ptSecond2Curr = pSurfBz->GetControlPoint( nUS1 + 1, 1, &bOk) ;
|
|
vPnt1.push_back( ptSecond2Curr) ;
|
|
// scorro le isocurve di separazione tra patch
|
|
for ( int i = nUS1 + 3 ; i < nUS2 ; i +=3) {
|
|
// recupero precedente e successivo
|
|
Point3d ptThird1Prev = pSurfBz->GetControlPoint( i - 1, 0, &bOk) ;
|
|
Point3d ptThird2Prev = pSurfBz->GetControlPoint( i - 1, 1, &bOk) ;
|
|
Point3d ptSecond1Next = pSurfBz->GetControlPoint( i + 1, 0, &bOk) ;
|
|
Point3d ptSecond2Next = pSurfBz->GetControlPoint( i + 1, 1, &bOk) ;
|
|
// recupero corrente e verifico la torsione
|
|
Point3d ptCurr1 = pSurfBz->GetControlPoint( i, 0, &bOk) ;
|
|
Point3d ptCurr2 = pSurfBz->GetControlPoint( i, 1, &bOk) ;
|
|
Vector3d vtIsoCurr = ptCurr2 - ptCurr1 ;
|
|
double dDist = vtIsoCurr.Len() ;
|
|
vtIsoCurr.Normalize() ;
|
|
//Vector3d vtDirPrev = vtIsoPrev ^ vtNPrev ;
|
|
Vector3d vtDirCurr = ptSecond1Next - ptCurr1 ; vtDirCurr.Normalize() ;
|
|
double dLenCurr = 0 ; pCrvOrig1->GetLengthAtParam( i, dLenCurr) ;
|
|
double dCoeff = dLenCurr / dLen ;
|
|
Vector3d vtIsoInterp = Media( vtDir1, vtDir2, dCoeff) ; vtIsoInterp.Normalize() ;
|
|
bool bDet = false ;
|
|
//double dAng = 0 ; vtIsoCurr.GetRotation( vtIsoPrev, vtDirPrev, dAng, bDet) ;
|
|
double dAng = 0 ; vtIsoCurr.GetRotation( vtIsoInterp, vtDirCurr, dAng, bDet) ;
|
|
vtNCurr = vtDirCurr ^ vtIsoCurr ; vtNCurr.Rotate( vtDirCurr, dAng) ;
|
|
double dSinAngTol = sin( 5 * DEGTORAD) ;
|
|
Vector3d vtPrev1 = ptCurr1 - ptThird1Prev ; vtPrev1.Normalize() ;
|
|
Vector3d vtNext1 = ptSecond1Next - ptCurr1 ; vtNext1.Normalize() ;
|
|
bool bAngularPoint1 = ! AreSameVectorEpsilon( vtPrev1, vtNext1, dSinAngTol) ;
|
|
Vector3d vtPrev2 = ptCurr2 - ptThird2Prev ; vtPrev2.Normalize() ;
|
|
Vector3d vtNext2 = ptSecond2Next - ptCurr2 ; vtNext2.Normalize() ;
|
|
bool bAngularPoint2 = ! AreSameVectorEpsilon( vtPrev2, vtNext2, dSinAngTol) ;
|
|
if ( abs( dAng) > 0) {
|
|
// se l'isocurva di separazione dalla patch successiva è torta rispetto alla precedente
|
|
// allora prendo il penultimo punto della curva precedente, il punto di joint e il secondo della prossima e li sposto lungo la normale della superficie
|
|
dDist *= dAng * DEGTORAD / 2 ;
|
|
if ( ! bAngularPoint1) {
|
|
// se non ho un punto angoloso muovo tutto il terzetto insieme
|
|
ptThird1Prev -= vtNCurr * dDist ;
|
|
ptSecond1Next -= vtNCurr * dDist ;
|
|
ptCurr1 -= vtNCurr * dDist ;
|
|
}
|
|
else {
|
|
// altrimenti sposto solo il punto corrente verso la congiungente tra il precedente e il successivo
|
|
DistPointLine dpl( ptCurr1, ptThird1Prev, ptSecond1Next, true) ;
|
|
Point3d ptMinDist ; dpl.GetMinDistPoint( ptMinDist) ;
|
|
Vector3d vtCorrDir = ptMinDist - ptCurr1 ; vtCorrDir.Normalize() ;
|
|
double dProjDir = vtNCurr * vtCorrDir ;
|
|
if ( dProjDir < 0)
|
|
LOG_ERROR( GetEGkLogger(), "Error : regolarizing crv0 near an angular point") ;
|
|
double dDistCorr = min( dDist, Dist( ptMinDist, ptCurr1)) ;
|
|
ptCurr1 -= vtCorrDir * dDistCorr ;
|
|
}
|
|
if ( ! bAngularPoint2) {
|
|
ptThird2Prev += vtNCurr * dDist ;
|
|
ptSecond2Next += vtNCurr * dDist ;
|
|
ptCurr2 += vtNCurr * dDist ;
|
|
}
|
|
else {
|
|
// altrimenti sposto solo il punto corrente verso la congiungente tra il precedente e il successivo
|
|
DistPointLine dpl( ptCurr2, ptThird2Prev, ptSecond2Next, true) ;
|
|
Point3d ptMinDist ; dpl.GetMinDistPoint( ptMinDist) ;
|
|
Vector3d vtCorrDir = ptMinDist - ptCurr2 ; vtCorrDir.Normalize() ;
|
|
double dProjDir = vtNCurr * vtCorrDir ;
|
|
if ( dProjDir < 0)
|
|
LOG_ERROR( GetEGkLogger(), "Error : regolarizing crv1 near an angular point") ;
|
|
double dDistCorr = min( dDist, Dist( ptMinDist, ptCurr2)) ;
|
|
ptCurr2 += vtCorrDir * dDistCorr ;
|
|
}
|
|
}
|
|
vPnt0.push_back( ptThird1Prev) ;
|
|
vPnt0.push_back( ptCurr1) ;
|
|
vPnt0.push_back( ptSecond1Next) ;
|
|
|
|
vPnt1.push_back( ptThird2Prev) ;
|
|
vPnt1.push_back( ptCurr2) ;
|
|
vPnt1.push_back( ptSecond2Next) ;
|
|
|
|
//vtIsoPrev = ptCurr2 - ptCurr1 ; vtIsoPrev.Normalize() ;
|
|
//vtNPrev = ( ptSecond1Next - ptCurr1) ^ vtIsoPrev ; vtNPrev.Normalize() ;
|
|
}
|
|
// aggiungo gli ultimi due punti
|
|
Point3d ptThird1Prev = pSurfBz->GetControlPoint( nUS2 - 1, 0, &bOk) ;
|
|
vPnt0.push_back( ptThird1Prev) ;
|
|
Point3d ptFourth1Curr = pSurfBz->GetControlPoint( nUS2, 0, &bOk) ;
|
|
vPnt0.push_back( ptFourth1Curr) ;
|
|
Point3d ptThird2Prev = pSurfBz->GetControlPoint( nUS2 - 1, 1, &bOk) ;
|
|
vPnt1.push_back( ptThird2Prev) ;
|
|
Point3d ptFourth2Curr = pSurfBz->GetControlPoint( nUS2, 1, &bOk) ;
|
|
vPnt1.push_back( ptFourth2Curr) ;
|
|
|
|
PtrOwner<ICurveComposite> pCC1( CreateCurveComposite()) ;
|
|
PtrOwner<ICurveComposite> pCC2( CreateCurveComposite()) ;
|
|
for ( int i = 0 ; i < ssize( vPnt0) - 3 ; i+=3) {
|
|
PtrOwner<ICurveBezier> cb1( CreateCurveBezier()) ; cb1->Init( 3, false) ;
|
|
cb1->SetControlPoint( 0, vPnt0[i]) ;
|
|
cb1->SetControlPoint( 1, vPnt0[i+1]) ;
|
|
cb1->SetControlPoint( 2, vPnt0[i+2]) ;
|
|
cb1->SetControlPoint( 3, vPnt0[i+3]) ;
|
|
pCC1->AddCurve( Release( cb1)) ;
|
|
|
|
PtrOwner<ICurveBezier> cb2( CreateCurveBezier()) ; cb2->Init( 3, false) ;
|
|
cb2->SetControlPoint( 0, vPnt1[i]) ;
|
|
cb2->SetControlPoint( 1, vPnt1[i+1]) ;
|
|
cb2->SetControlPoint( 2, vPnt1[i+2]) ;
|
|
cb2->SetControlPoint( 3, vPnt1[i+3]) ;
|
|
pCC2->AddCurve( Release( cb2)) ;
|
|
}
|
|
|
|
////// N.B.:dovrei tener conto anche della patch PRECEDENTE e SUCCESSIVA a quelle indicate, altrimenti non vedo se ho creato flessi al bordo della zona
|
|
#if DEBUG_SMOOTH_CURVATURE
|
|
VT.push_back( pCC1->Clone()) ;
|
|
VT.push_back( pCC2->Clone()) ;
|
|
SaveGeoObj( VT, "C:\\Temp\\bezier\\ruled\\smoothness\\regolarized_first_step.nge") ;
|
|
#endif
|
|
|
|
// ora verifico l'eventuale presenza di cambi di concavità non desiderati
|
|
// se ne trovo su una curva e non sull'altra allora ruoto il terzetto di punti della curva con flesso in modo
|
|
// da matchare la tangente dell'altra curva
|
|
PNTINFOVECTOR vPntInfo1, vPntInfo2 ;
|
|
FillPntInfo( vPnt0, pCC1, vPntInfo1) ;
|
|
FillPntInfo( vPnt1, pCC2, vPntInfo2) ;
|
|
|
|
RemoveInflexionPoints( vPnt0, vPntInfo1, vPntInfo2) ;
|
|
RemoveInflexionPoints( vPnt1, vPntInfo2, vPntInfo1) ;
|
|
|
|
pCC1->Clear() ;
|
|
pCC2->Clear() ;
|
|
for ( int i = 0 ; i < ssize( vPnt0) - 3 ; i+=3) {
|
|
PtrOwner<ICurveBezier> cb1( CreateCurveBezier()) ; cb1->Init( 3, false) ;
|
|
cb1->SetControlPoint( 0, vPnt0[i]) ;
|
|
cb1->SetControlPoint( 1, vPnt0[i+1]) ;
|
|
cb1->SetControlPoint( 2, vPnt0[i+2]) ;
|
|
cb1->SetControlPoint( 3, vPnt0[i+3]) ;
|
|
pCC1->AddCurve( Release( cb1)) ;
|
|
|
|
PtrOwner<ICurveBezier> cb2( CreateCurveBezier()) ; cb2->Init( 3, false) ;
|
|
cb2->SetControlPoint( 0, vPnt1[i]) ;
|
|
cb2->SetControlPoint( 1, vPnt1[i+1]) ;
|
|
cb2->SetControlPoint( 2, vPnt1[i+2]) ;
|
|
cb2->SetControlPoint( 3, vPnt1[i+3]) ;
|
|
pCC2->AddCurve( Release( cb2)) ;
|
|
}
|
|
|
|
// controllo di essere rimasto in tolleranza
|
|
double dErr = 0 ;
|
|
CalcApproxError( pCrvOrig1, pCC1, dErr, 20) ;
|
|
if ( dErr > dTol)
|
|
return nullptr ;
|
|
CalcApproxError( pCrvOrig2, pCC2, dErr, 20) ;
|
|
if ( dErr > dTol)
|
|
return nullptr ;
|
|
|
|
PtrOwner<ISurfBezier> pNewSurf( pSurfBz->Clone()) ;
|
|
// aggiorno i punti di controllo della superficie di bezier
|
|
for ( int i = 0 ; i < ssize( vPnt0) ; ++i) {
|
|
pNewSurf->SetControlPoint( nUS1 + i, 0, vPnt0[i]) ;
|
|
pNewSurf->SetControlPoint( nUS1 + i, 1, vPnt1[i]) ;
|
|
}
|
|
#if DEBUG_SMOOTH_CURVATURE
|
|
VT.clear() ;
|
|
VT.push_back( Release(pCC1)) ;
|
|
VT.push_back( Release(pCC2)) ;
|
|
SaveGeoObj( VT, "C:\\Temp\\bezier\\ruled\\smoothness\\regolarized.nge") ;
|
|
#endif
|
|
|
|
return Release( pNewSurf) ;
|
|
} |