From 3245f7fe75f6d14a02c5002296746893e148f6c6 Mon Sep 17 00:00:00 2001 From: Dario Sassi Date: Sun, 4 Jan 2026 20:39:33 +0100 Subject: [PATCH] EgtGeomKernel : - aggiunte funzioni di striping per trimming dei termoformati. --- EgtGeomKernel.vcxproj | 1 + EgtGeomKernel.vcxproj.filters | 6 + SurfTriMesh.cpp | 18 + SurfTriMesh.h | 1 + SurfTriMeshOffset.cpp | 3 +- Trimming.cpp | 3764 +++++++++++++++++++++++++++++++++ 6 files changed, 3792 insertions(+), 1 deletion(-) create mode 100644 Trimming.cpp diff --git a/EgtGeomKernel.vcxproj b/EgtGeomKernel.vcxproj index 5fe8537..c571c4d 100644 --- a/EgtGeomKernel.vcxproj +++ b/EgtGeomKernel.vcxproj @@ -322,6 +322,7 @@ copy $(TargetPath) \EgtProg\Dll64 + diff --git a/EgtGeomKernel.vcxproj.filters b/EgtGeomKernel.vcxproj.filters index e07bdb6..06c76e0 100644 --- a/EgtGeomKernel.vcxproj.filters +++ b/EgtGeomKernel.vcxproj.filters @@ -55,6 +55,9 @@ {865b76ee-b10d-41fc-861c-b48ce52fa277} + + {54901321-08f6-4428-80c7-a1f859136a32} + @@ -561,6 +564,9 @@ File di origine\GeoInters + + File di origine\GeoStriping + diff --git a/SurfTriMesh.cpp b/SurfTriMesh.cpp index 789b8a5..16f22f2 100644 --- a/SurfTriMesh.cpp +++ b/SurfTriMesh.cpp @@ -4514,3 +4514,21 @@ SurfTriMesh::AdjustTopologyFromZMap( void) return true ; } + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::GetTriaFromFace( int nF, int& nT) const +{ + // la superficie deve essere validata + if ( m_nStatus != OK) + return false ; + // verifico stato sfaccettatura + if ( ! VerifyFaceting()) + return false ; + // l'indice della faccia deve essere nei limiti + if ( nF < 0 || nF >= int( m_vFacet.size())) + return false ; + // recupero la normale di un triangolo della faccetta + nT = m_vFacet[nF] ; + return true ; +} diff --git a/SurfTriMesh.h b/SurfTriMesh.h index 12c886e..49b92a7 100644 --- a/SurfTriMesh.h +++ b/SurfTriMesh.h @@ -380,6 +380,7 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW bool SetTempInt( int nId, int nTempInt) const ; bool AddTriaFromZMap( const TRIA3DEXVECTOR& vTria, PointGrid3d& VertGrid, double dVertexTol = 2 * EPS_SMALL) ; bool AdjustTopologyFromZMap( void) ; + bool GetTriaFromFace( int nF, int& nT) const ; private : typedef std::vector VERTVECTOR ; diff --git a/SurfTriMeshOffset.cpp b/SurfTriMeshOffset.cpp index dd1841a..f1579f9 100644 --- a/SurfTriMeshOffset.cpp +++ b/SurfTriMeshOffset.cpp @@ -733,6 +733,7 @@ CreateSurfTriMeshShell( const ISurfTriMesh* pStm, double dThick, double dPrec) } if ( nIndOffsCrv == -1) return nullptr ; + vIndMatched[ nIndOffsCrv] = true ; // associo le due curve ICurveComposite* pCompoOffsLoop = vCompoOffsLoops[nIndOffsCrv] ; double dParMinDist = 0. ; @@ -787,4 +788,4 @@ CreateSurfTriMeshShell( const ISurfTriMesh* pStm, double dThick, double dPrec) #endif return ( ( ! IsNull( pStmRef) && pStmRef->IsValid()) ? Release( pStmRef) : nullptr) ; -} \ No newline at end of file +} diff --git a/Trimming.cpp b/Trimming.cpp new file mode 100644 index 0000000..bf42b82 --- /dev/null +++ b/Trimming.cpp @@ -0,0 +1,3764 @@ +//---------------------------------------------------------------------------- +// 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 "CurveArc.h" +#include "CurveComposite.h" +#include "SurfTriMesh.h" +#include "SurfBezier.h" +#include "CurveBezier.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/EGkSurfTriMeshAux.h" +#include "/EgtDev/Include/EgtNumUtils.h" +#include +#include + +// -------------------------- Debug -------------------------------------------- +#define DEBUG 0 +#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_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_BEZIER_RULED 0 +#define DEBUG_CURVATURE 0 +#define DEBUG_SIMPLE_PATCHES 0 +#define DEBUG_SURF_PATCHES 0 +#define DEBUG_EDGES 0 +#define DEBUG_SHAPE_STM 0 +#if DEBUG_BASIC_BORDERS || DEBUG_CHAIN_CURVES || DEBUG_ANG_APPROX || DEBUG_BEZIER_INTERP || \ + DEBUG_FACE_SEARCH || DEBUG_BRK_POINTS || DEBUG_BRK_THICK || DEBUG_BRK || DEBUG_BORDERS_BY_NORMALS || \ + DEBUG_SYNC_POINTS || DEBUG_BEZIER_RULED || DEBUG_CURVATURE || DEBUG_SIMPLE_PATCHES || DEBUG_SURF_PATCHES || \ + DEBUG_EDGES || DEBUG_SHAPE_STM || DEBUG + #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 VT ; + std::vector VC ; + std::vector VT_Glob ; + std::vector VC_Glob ; + std::string sFileName = "C:\\Temp\\Debug.nge" ; +#endif + +using namespace std ; + +// Vettori e Matrici di curva composite +typedef vector COMPOVECTOR ; +typedef vector 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 SimpleBorder ; +typedef vector SimpleBorderVector ; +typedef vector SimpleBorderMatrix ; +// Struttura per superficie con relative Patch +struct SurfPatches { + int nIndSurf ; + const ISurf* pSurf ; + vector vPatches ; // non definiscono per forza un bordo, sono solo patch non ordinate + COMPOVECTOR CompoPathces ; +} ; +typedef vector SURFPATCHESVECTOR ; +// Struttura per Calcolo delle Normali su una superficie lungo il suo bordo ( per parallelismo ) +struct BorderInfo { + const ISurf* pSurf ; + const ICurveComposite* pCompo ; + int nInd ; + double dParam ; + Point3d ptCrv ; + Vector3d vtTan ; + Vector3d vtNorm ; + Point3d ptFinal ; + BorderInfo( const ISurf* pS, const ICurveComposite* pC, int nI, double dP) + : pSurf( pS), pCompo( pC), nInd( nI), dParam( dP) {} +} ; +typedef vector VBORDERINFO ; + +//----------------------------------------------------------------------------- +static const string sCircle{ "circle"} ; + +//----------------------------------------------------------------------------- +//--------------------------- 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 = int( vPoints.size()) ; + 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 ( int( vvPoints.size()) > 1) { + Point3d& ptCurr = vPoints[int( vPoints.size()) - 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()[int( vvPoints.back().size()) - 1] ; + Point3d& ptLastLast = vvPoints.back()[int( vvPoints.back().size()) - 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 < int( vvPoints[0].size()) ; ++ 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( vvPoints.front().size() + 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[int( vPoints.size()) - 2]) ; + vvPoints.back().emplace_back( vPoints[int( vPoints.size()) - 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 < int( vvPoints.size()) ; ++ i) { + vPL.emplace_back( PolyLine()) ; + double dPar = -1. ; + for ( int j = 0 ; j < int( vvPoints[i].size()) ; ++ 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 +ApproxCurveWithBezier( ICurveComposite* pCrvCompo, double dLinTol, double dAngTol) +{ + // Controllo dei parametri + if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) + return false ; + + // Recupero la PolyLine associata ( senza alcuna approssimazione) + PolyLine myPL ; + if ( ! pCrvCompo->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, dAngTol, vPL)) + return false ; + #if DEBUG_BEZIER_INTERP + VT.clear() ; VC.clear() ; + VT.emplace_back( pCrvCompo->Clone()) ; + VC.emplace_back( RED) ; + for ( PolyLine& PL : vPL) { + if ( PL.GetPointNbr() > 1) { + Point3d ptCurr ; PL.GetFirstPoint( ptCurr) ; + IGeoPoint3d* pt = CreateGeoPoint3d() ; + pt->Set( ptCurr) ; + VT.emplace_back( pt) ; + VC.emplace_back( AQUA) ; + PL.GetLastPoint( ptCurr) ; + pt->Set( ptCurr) ; + VT.emplace_back( pt) ; + VC.emplace_back( AQUA) ; + } + } + SaveGeoObj( VT, VC, "C:\\Temp\\AngBorderApprox.nge") ; + VT.clear() ; VC.clear() ; + #endif + + // Pulisco la curva originale + pCrvCompo->Clear() ; + + // Ogni PolyLine ricavata viene approssimata con un tratto di Bezier + const double MAXLEN = 1.5 ; + for ( const PolyLine& PL : vPL) { + // Se meno di due punti, non la considero ( non dovrebbe mai capitare ) + if ( PL.GetPointNbr() < 2) + continue ; + // Recupero i punti + PNTVECTOR vPoints ; + Point3d ptCurr ; + bool bFound = PL.GetFirstPoint( ptCurr) ; + while ( bFound) { + vPoints.emplace_back( ptCurr) ; + bFound = PL.GetNextPoint( ptCurr) ; + } + #if DEBUG_BEZIER_INTERP + Color myCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; + for ( int i = 0 ; i < int( vPoints.size()) ; ++ i) { + IGeoPoint3d* pt = CreateGeoPoint3d() ; + pt->Set( vPoints[i]) ; + VT.emplace_back( pt) ; + VC.emplace_back( myCol) ; + } + #endif + // Approssimo con una Bezier + PtrOwner pCrvBz( InterpolatePointSetWithBezier( vPoints, dLinTol, MAXLEN)) ; + if ( IsNull( pCrvBz) || ! pCrvBz->IsValid()) { + LOG_ERROR( GetEGkLogger(), "Error : Interpolating points to bezier curve failed") ; + return false ; + } + #if DEBUG_BEZIER_INTERP + VT.emplace_back( pCrvBz->Clone()) ; + myCol.Set( myCol.GetRed() * .95, myCol.GetGreen() * .95, myCol.GetBlue() * .95) ; + VC.emplace_back( myCol) ; + #endif + // Aggiungo il tratto approssimato alla curva finale complessiva + if ( ! pCrvCompo->AddCurve( Release( pCrvBz))) + return false ; + } + #if DEBUG_BEZIER_INTERP + SaveGeoObj( VT, VC, "C:\\Temp\\InterpolationCurveBezier.nge") ; + #endif + + return ( pCrvCompo->IsValid()) ; +} + +//----------------------------------------------------------------------------- +//-------------------------------- Analisi Bordi ------------------------------ +//----------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +// Funzione per controllare e sistemare le curve di bordo di una riagata 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 pCrvEdge1CL( pCompoEdge1->Clone()) ; + PtrOwner pCrvEdge2CL( pCompoEdge2->Clone()) ; + if ( IsNull( pCrvEdge1CL) || IsNull( pCrvEdge2CL) || + ! pCrvEdge1CL->IsValid() || ! pCrvEdge2CL->IsValid()) + return false ; + PtrOwner pMyCompoEdge1( ConvertCurveToComposite( Release( pCrvEdge1CL))) ; + PtrOwner 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 Splittare una Patch con un'altra +static bool +SplitPatchWithPatch( SurfPatches& SurfPatchA, SurfPatches& SurfPatchB, double dLinTol) +{ + // Definisco la tolleranza di parametro + const double TOL_LINE_PARAM = 10. * EPS_SMALL ; // <-- euristica, modificabile + + // Scorro le Patch della superficie A + for ( int nPatchA = 0 ; nPatchA < int( SurfPatchA.CompoPathces.size()) ; ++ nPatchA) { + // Recupero la PatchA + CurveComposite& PatchA = SurfPatchA.CompoPathces[nPatchA] ; + Point3d& ptStartA = SurfPatchA.vPatches[nPatchA].ptStart ; + Point3d& ptEndA = SurfPatchA.vPatches[nPatchA].ptEnd ; + // Scorro le Patch della supericie B + for ( int nPatchB = 0 ; nPatchB < int( SurfPatchB.CompoPathces.size()) ; ++ nPatchB) { + // Recupero la PatchB + CurveComposite& PatchB = SurfPatchB.CompoPathces[nPatchB] ; + Point3d& ptStartB = SurfPatchB.vPatches[nPatchB].ptStart ; + Point3d& ptEndB = SurfPatchB.vPatches[nPatchB].ptEnd ; + + // Verifico se gli estremi giaciono sulle linee + double dDist = 0. ; + double dPar = 0., dParS = 0., dParE = 0. ; + int nFlag = 0 ; + bool bSplit = false ; + Point3d ptInters ; + + // Estremi B su PatchA ( sono tutti tratti lineari, quindi nel 3d va bene) + DistPointCurve DLL0( ptStartB, PatchA) ; + bSplit = ( DLL0.GetDist( dDist) && dDist < dLinTol && + DLL0.GetParamAtMinDistPoint( 0., dPar, nFlag) && + PatchA.GetDomain( dParS, dParE) && + dParS + TOL_LINE_PARAM < dPar && dPar < dParE - TOL_LINE_PARAM) ; + if ( ! bSplit) { + DistPointCurve DLL1( ptEndB, PatchA) ; + bSplit = ( DLL1.GetDist( dDist) && dDist < dLinTol && + DLL1.GetParamAtMinDistPoint( 0., dPar, nFlag) && + PatchA.GetDomain( dParS, dParE) && + dParS + TOL_LINE_PARAM < dPar && dPar < dParE - TOL_LINE_PARAM) ; + } + // Se Split della PatchA richiesto + if ( bSplit) { + // Split della Patch + PatchA.GetPointD1D2( dPar, ICurve::FROM_MINUS, ptInters) ; + SimplePatch NextPatch( ptInters, ptEndA, 0, false) ; + SurfPatchA.vPatches.insert( SurfPatchA.vPatches.begin() + nPatchA + 1, NextPatch) ; + ptEndA = ptInters ; + // Split della curva composita associata + CurveComposite* pCompoAfter( PatchA.Clone()) ; + PatchA.TrimEndAtParam( dPar) ; + pCompoAfter->TrimStartAtParam( dPar) ; + SurfPatchA.CompoPathces.insert( SurfPatchA.CompoPathces.begin() + nPatchA + 1, *pCompoAfter) ; + // Aggiorno i contatori + -- nPatchA ; + break ; + } + + // Estremi Curr su LineNext ( sono tutti tratti lineari, quindi nel 3d va bene) + DistPointCurve DLL2( ptStartA, PatchB) ; + bSplit = ( DLL2.GetDist( dDist) && dDist < dLinTol && + DLL2.GetParamAtMinDistPoint( 0., dPar, nFlag) && + PatchB.GetDomain( dParS, dParE) && + dParS + TOL_LINE_PARAM < dPar && dPar < dParE - TOL_LINE_PARAM) ; + if ( ! bSplit) { + DistPointCurve DLL3( ptEndA, PatchB) ; + bSplit = ( DLL3.GetDist( dDist) && dDist < dLinTol && + DLL3.GetParamAtMinDistPoint( 0., dPar, nFlag) && + PatchB.GetDomain( dParS, dParE) && + dParS + TOL_LINE_PARAM < dPar && dPar < dParE - TOL_LINE_PARAM) ; + } + // Se Split della PatchB richiesto + if ( bSplit) { + // Split della Patch + PatchB.GetPointD1D2( dPar, ICurve::FROM_MINUS, ptInters) ; + SimplePatch NextPatch( ptInters, ptEndB, 0, false) ; + SurfPatchB.vPatches.insert( SurfPatchB.vPatches.begin() + nPatchB + 1, NextPatch) ; + ptEndB = ptInters ; + // Split della curva composita associata + CurveComposite* pCompoAfter( PatchB.Clone()) ; + PatchB.TrimEndAtParam( dPar) ; + pCompoAfter->TrimStartAtParam( dPar) ; + SurfPatchB.CompoPathces.insert( SurfPatchB.CompoPathces.begin() + nPatchB + 1, *pCompoAfter) ; + // Aggiorno i contatori + -- nPatchB ; + } + } + } + + 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) ; + } + } + + // 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) + SimpleBorderVector vvSimpleBorder ; vvSimpleBorder.reserve( matPLPatches.size()) ; + for ( int i = 0 ; i < int( matPLPatches.size()) ; ++ i) { + vvSimpleBorder.emplace_back( SimpleBorder()) ; + for ( int j = 0 ; j < int( matPLPatches[i].size()) ; ++ j) { + Point3d ptS ; matPLPatches[i][j].GetFirstPoint( ptS) ; + Point3d ptE ; matPLPatches[i][j].GetLastPoint( ptE) ; + vvSimpleBorder.back().emplace_back( ptS, ptE, i, false) ; + } + } + #if DEBUG_CHAIN_CURVES + VC.clear() ; VT.clear() ; + for ( int i = 0 ; i < int( vvSimpleBorder.size()) ; ++ i) { + for ( int j = 0 ; j < int( vvSimpleBorder[i].size()) ; ++ j) { + VT.emplace_back( matCompoPatches[i][j].Clone()) ; + VC.emplace_back( RED) ; + IGeoPoint3d* ptS = CreateGeoPoint3d() ; + ptS->Set( vvSimpleBorder[i][j].ptStart) ; + VT.emplace_back( ptS) ; + VC.emplace_back( AQUA) ; + IGeoPoint3d* ptE = CreateGeoPoint3d() ; + ptE->Set( vvSimpleBorder[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 ) + const double TOL_LINE_PARAM = 10. * EPS_SMALL ; // <-- eursitica, modificabile + for ( int nCurrCrv = 0 ; nCurrCrv < int( vvSimpleBorder.size()) - 1 ; ++ nCurrCrv) { + // Recupero la curva corrente + SimpleBorder& CurrCurve = vvSimpleBorder[nCurrCrv] ; + // Scorro i suoi Edges + for ( int nCurrEdge = 0 ; nCurrEdge < int( CurrCurve.size()) ; ++ nCurrEdge) { + // Recupero il tratto di curva corrente + CurveComposite& CurrCompo = matCompoPatches[nCurrCrv][nCurrEdge] ; + // Scorro le curve successive + for ( int nNextCrv = nCurrCrv + 1 ; nNextCrv < int( vvSimpleBorder.size()) ; ++ nNextCrv) { + // Recupero la curva successiva + SimpleBorder& NextCurve = vvSimpleBorder[nNextCrv] ; + // Scorro i suoi Edges + for ( int nNextEdge = 0 ; nNextEdge < int( NextCurve.size()) ; ++ nNextEdge) { + // Se appartiene allo stesso bordo, passo alla seguente + if ( NextCurve[nNextEdge].nBorder == CurrCurve[nCurrEdge].nBorder) + continue ; + // Recupero il tratto di curva corrente + CurveComposite& NextCompo = matCompoPatches[nNextCrv][nNextEdge] ; + // Verifico se gli estremi giaciono sulle linee + double dDist = 0. ; + double dPar = 0., dParS = 0., dParE = 0. ; + int nFlag = 0 ; + bool bSplit = false ; + Point3d ptInters ; + + // Estremi Next su LineCurr ( sono tutti tratti lineari, quindi nel 3d va bene) + DistPointCurve DLL0( NextCurve[nNextEdge].ptStart, CurrCompo) ; + bSplit = ( DLL0.GetDist( dDist) && dDist < dLinTol && + DLL0.GetParamAtMinDistPoint( 0., dPar, nFlag) && + CurrCompo.GetDomain( dParS, dParE) && + dParS + TOL_LINE_PARAM < dPar && dPar < dParE - TOL_LINE_PARAM) ; + if ( ! bSplit) { + DistPointCurve DLL1( NextCurve[nNextEdge].ptEnd, CurrCompo) ; + bSplit = ( DLL1.GetDist( dDist) && dDist < dLinTol && + DLL1.GetParamAtMinDistPoint( 0., dPar, nFlag) && + CurrCompo.GetDomain( dParS, dParE) && + dParS + TOL_LINE_PARAM < dPar && dPar < dParE - TOL_LINE_PARAM) ; + } + // Split dell'Edge se richiesto + if ( bSplit) { + #if DEBUG_CHAIN_CURVES + VT.clear() ; VC.clear() ; + VT.emplace_back( NextCompo.Clone()) ; + VC.emplace_back( ORANGE) ; + VT.emplace_back( CurrCompo.Clone()) ; + VC.emplace_back( LIME) ; + SaveGeoObj( VT, VC, "C:\\Temp\\LineIntersection.nge") ; + #endif + // Aggiorno gli Edges + CurrCompo.GetPointD1D2( dPar, ICurve::FROM_MINUS, ptInters) ; + SimplePatch NextEdge( ptInters, CurrCurve[nCurrEdge].ptEnd, + CurrCurve[nCurrEdge].nBorder, false) ; + CurrCurve.insert( CurrCurve.begin() + nCurrEdge + 1, NextEdge) ; + CurrCurve[nCurrEdge].ptEnd = ptInters ; + // Aggiorno le Curve + CurveComposite* pCompoAfter( CurrCompo.Clone()) ; + CurrCompo.TrimEndAtParam( dPar) ; + pCompoAfter->TrimStartAtParam( dPar) ; + matCompoPatches[nCurrCrv].insert( matCompoPatches[nCurrCrv].begin() + nCurrEdge + 1, *pCompoAfter) ; + // Aggiorno i contatori + -- nCurrEdge ; + nNextCrv = int( vvSimpleBorder.size()) ; + break ; + } + + // Estremi Curr su LineNext ( sono tutti tratti lineari, quindi nel 3d va bene) + DistPointCurve DLL2( CurrCurve[nCurrEdge].ptStart, NextCompo) ; + bSplit = ( DLL2.GetDist( dDist) && dDist < dLinTol && + DLL2.GetParamAtMinDistPoint( 0., dPar, nFlag) && + NextCompo.GetDomain( dParS, dParE) && + dParS + TOL_LINE_PARAM < dPar && dPar < dParE - TOL_LINE_PARAM) ; + if ( ! bSplit) { + DistPointCurve DLL3( CurrCurve[nCurrEdge].ptEnd, NextCompo) ; + bSplit = ( DLL3.GetDist( dDist) && dDist < dLinTol && + DLL3.GetParamAtMinDistPoint( 0., dPar, nFlag) && + NextCompo.GetDomain( dParS, dParE) && + dParS + TOL_LINE_PARAM < dPar && dPar < dParE - TOL_LINE_PARAM) ; + } + // Split dell'Edge se richiesto + if ( bSplit) { + #if DEBUG_CHAIN_CURVES + VT.clear() ; VC.clear() ; + VT.emplace_back( NextCompo.Clone()) ; + VC.emplace_back( ORANGE) ; + VT.emplace_back( CurrCompo.Clone()) ; + VC.emplace_back( LIME) ; + SaveGeoObj( VT, VC, "C:\\Temp\\LineIntersection.nge") ; + #endif + // Aggiorno gli Edges + NextCompo.GetPointD1D2( dPar, ICurve::FROM_MINUS, ptInters) ; + SimplePatch NextEdge( ptInters, NextCurve[nNextEdge].ptEnd, + NextCurve[nNextEdge].nBorder, false) ; + NextCurve.insert( NextCurve.begin() + nNextEdge + 1, NextEdge) ; + NextCurve[nNextEdge].ptEnd = ptInters ; + // Aggiorno le curve + CurveComposite* pCompoAfter( NextCompo.Clone()) ; + NextCompo.TrimEndAtParam( dPar) ; + pCompoAfter->TrimStartAtParam( dPar) ; + matCompoPatches[nNextCrv].insert( matCompoPatches[nNextCrv].begin() + nNextEdge + 1, *pCompoAfter) ; + // Aggiorno i contatori + -- nNextEdge ; + } + } + } + } + } + #if DEBUG_CHAIN_CURVES + VC.clear() ; VT.clear() ; + for ( int i = 0 ; i < int( vvSimpleBorder.size()) ; ++ i) { + for ( int j = 0 ; j < int( vvSimpleBorder[i].size()) ; ++ j) { + VT.emplace_back( matCompoPatches[i][j].Clone()) ; + VC.emplace_back( RED) ; + IGeoPoint3d* pt = CreateGeoPoint3d() ; + pt->Set( vvSimpleBorder[i][j].ptStart) ; + VT.emplace_back( pt) ; + VC.emplace_back( AQUA) ; + pt = CreateGeoPoint3d() ; + pt->Set( vvSimpleBorder[i][j].ptEnd) ; + VT.emplace_back( pt) ; + VC.emplace_back( AQUA) ; + } + } + SaveGeoObj( VT, VC, "C:\\Temp\\BordersWithOverlaps.nge") ; + #endif + + // 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 < int( vvSimpleBorder.size()) - 1 ; ++ nCurrCrv) { + // Recupero la curva corrente + SimpleBorder& CurrCurve = vvSimpleBorder[nCurrCrv] ; + // Scorro i suoi Edges + for ( int nCurrEdge = 0 ; nCurrEdge < int( CurrCurve.size()) ; ++ 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 < int( vvSimpleBorder.size()) ; ++ nNextCrv) { + // Recupero la curva successiva + SimpleBorder& NextCurve = vvSimpleBorder[nNextCrv] ; + // Scorro i suoi Edges + for ( int nNextEdge = 0 ; nNextEdge < int( NextCurve.size()) ; ++ 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( vvSimpleBorder.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 < int( vvSimpleBorder.size()) ; ++ nCrv) { + // Recupero la curva + SimpleBorder& CrvEdge = vvSimpleBorder[nCrv] ; + // Scorro i suoi edges + for ( int nEdge = 0 ; nEdge < int( CrvEdge.size()) ; ++ 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 < int( vCompoResult.size()) ; ++ 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 < int( vCompoResult.size()) ; ++ 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 < int( vCompoPatches.size()) ; ++ 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 < int( vCompoPatches.size()) ; ++ 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 < int( vCompoPatches.size()) ; ++ 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 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) ; + // 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 < int( vCompoChained.size()) ; ++ 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() ; + // 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 ( int( vBiPntSplit.size()) != 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 < int( vBreakingPts.size()) ; ++ i) { + if ( AreSamePointEpsilon( vBreakingPts[i].first, vBreakingPts[i].second, dLinTol)) + return false ; + for ( int j = i + 1 ; j < int( vBreakingPts.size()) ; ++ 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 < int( vCompoParts.size()) ; ++ 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 pCrvA( vCompoParts[i]->CopyParamRange( dUS, dUE)) ; + PtrOwner 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 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 < int( vCompoParts.size()) ; ++ 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 ( int( vPL.size()) < 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 < int( vPL.size()) ; ++ 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 < int( vLoops.size()) ; ++ 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 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 < int( vSurf.size()) ; ++ 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 = int( vPLBorders.size()) ; + // Se non ho nessuna faccia e nessun triangolo da scartare, estraggo i bordi complessivi della superficie + if ( int( vFaces.size()) == 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 < int( vPLBorders.size()) ; ++ 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 ; +} + +// ---------------------------------------------------------------------------- +// ---------------------------------- Analisi Adiacenze ----------------------- +// ---------------------------------------------------------------------------- +static bool +ClassifyTriangles( const SurfTriMesh& Stm, const INTVECTOR& vTBox, int nFirstTria, double dAngTol, + unordered_set& setAvoidTria, unordered_set& setNewTria) +{ + // Collezione di triangoli già visitati + unordered_set setTria ; setTria.reserve( vTBox.size()) ; + // Collezioni di triangoli da analizzare + INTVECTOR vTria ; vTria.reserve( int( vTBox.size()) + 1) ; + vTria.push_back( nFirstTria) ; + + // 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 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)) { + // 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() ; + 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& 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 < int( vBiPnts.size()) ; ++ 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 ( int( vInters.size()) == 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 setBiPntsToSave( vEdgeToSave.begin(), vEdgeToSave.end()) ; + int nCurr = 0 ; + for ( int i = 0 ; i < int( vBiPnts.size()) ; ++ i) { + if ( setBiPntsToSave.count( i)) + vBiPnts[nCurr ++] = vBiPnts[i] ; + } + vBiPnts.resize( nCurr) ; + + #if DEBUG_FACE_SEARCH + for ( int i = 0 ; i < int( vBiPnts.size()) ; ++ 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 < int( vBiPnts.size()) ; ++ 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 < int( vBiPnts.size()) ; ++ 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 < int( vCompoChain.size()) ; ++ 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& 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& 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 < int( vCompoChain.size()) ; ++ _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 ( int( vCompoChain.size()) == 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 ( int( vCompoChain.size()) == 2) { + CompoATmp = vCompoChain[0] ; + CompoBTmp = vCompoChain[1] ; + } + // Se più di due, cerco le due curve più vicine + else if ( int( vCompoChain.size()) > 2) { + INTDBLVECTOR vIndSqDist ; vIndSqDist.reserve( vCompoChain.size()) ; + for ( int nCrv = 0 ; nCrv < int( vCompoChain.size()) ; ++ 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 ; + CompoA.CopyFrom( ConvertCurveToBasicComposite( CompoATmp.CopyParamRange( dUAS, dUAE))) ; + CompoB.CopyFrom( ConvertCurveToBasicComposite( CompoBTmp.CopyParamRange( dUBS, dUBE))) ; + + #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)) { + CompoPrev.CopyFrom( ConvertCurveToBasicComposite( CompoChain.CopyParamRange( dUBreak, dUE))) ; + CompoPrev.Invert() ; + } + else if ( Compo.IsPointOn( ptE) && ! Compo.IsPointOn( ptS)) + CompoPrev.CopyFrom( ConvertCurveToBasicComposite( CompoChain.CopyParamRange( dUS, dUBreak))) ; + #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)) + CompoAft.CopyFrom( ConvertCurveToBasicComposite( CompoChain.CopyParamRange( dUBreak, dUE))) ; + else if ( Compo.IsPointOn( ptE) && ! Compo.IsPointOn( ptS)) { + CompoAft.CopyFrom( ConvertCurveToBasicComposite( CompoChain.CopyParamRange( dUS, dUBreak))) ; + 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) { + CompoPrev.CopyFrom( ConvertCurveToBasicComposite( CompoChain.CopyParamRange( dUS, dUBreak))) ; + CompoAft.CopyFrom( ConvertCurveToBasicComposite( CompoChain.CopyParamRange( dUBreak1, dUE))) ; + } + else { + CompoPrev.CopyFrom( ConvertCurveToBasicComposite( CompoChain.CopyParamRange( dUBreak, dUE))) ; + CompoPrev.Invert() ; + CompoAft.CopyFrom( ConvertCurveToBasicComposite( CompoChain.CopyParamRange( dUS, dUBreak1))) ; + 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) { + CompoSingle.CopyFrom( ConvertCurveToBasicComposite( CompoChain.CopyParamRange( dUBreak, dUBreak1))) ; + 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 { + CompoSingle.CopyFrom( ConvertCurveToBasicComposite( CompoChain.CopyParamRange( dUBreak1, dUBreak))) ; + #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& 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] + if ( ! myCompoA.CopyFrom( CompoX.CopyParamRange( dUStartA, dUE)) || + ! myCompoB.CopyFrom( CompoX.CopyParamRange( dUS, dUEndB))) + 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 < int( vTBox.size()) ; ++ 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 MysetAvoidTria( setAvoidTria) ; + unordered_set MysetNewTria ; + if ( ! ClassifyTriangles( Stm, vTBox, nTria, dAngTol, MysetAvoidTria, MysetNewTria)) + 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 ( int( vCompoChain.size()) <= 1) + return true ; + // Se ottengo ancora due curve, allora la striscia non rispetta lo spessore minimo + bBreak = ( int( vCompoChain.size()) >= 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& setAvoidTria, unordered_set& setNewTria, + 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)) + 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& setAvoidTria, unordered_set& setValidTria, + 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 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, 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 il bordo di una TriMesh è una circonferenza +static bool +IsBorderACircle( const PolyLine& PL, double dShapeLinTol, Point3d& ptC, Vector3d& vtN, double& dRad) +{ + // Se PolyLine aperta o non piana, non è un cerchio + Plane3d PlanePL ; + if ( ! PL.IsClosed() || ! PL.IsFlat( PlanePL, dShapeLinTol)) + 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) + 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) ; + } + + // 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 < int( floor( 0.5 * int( vPts.size()))) ; i = i + 2) { + double dSqLineDist = INFINITO ; + DistPointLine( ORIG, vPts[i], vPts[i + 1]).GetSqDist( dSqLineDist) ; + if ( dSqLineDist < dSqMinDist) + dSqMinDist = dSqLineDist ; + double dSqPtDist = SqDist( ORIG, vPts[i]) ; + if ( dSqPtDist > dSqMaxDist) + dSqMaxDist = dSqPtDist ; + } + + // Se distanza minima e massima entro tolleranza, allora è una circonferenza + if ( abs( dSqMaxDist - dSqMinDist) < dShapeLinTol * dShapeLinTol) { + ptC = ptCentroid ; + vtN = PlanePL.GetVersN() ; + dRad = 0.5 * ( sqrt( dSqMaxDist) + sqrt( dSqMinDist)) ; + return true ; + } + + return false ; +} + +// ---------------------------------------------------------------------------- +// Funzione per estrarre Geometrie note da una sola TriMesh +static bool +GetShapesFromSurf( const ISurf* pSurf, double dShapeLinTol, double dShapeAngTol, double dLinTol, + double dEdgeLinTol, double dAngTol, double dFaceAngTol, const STRVECTOR& vsShapes, + ISURFPOVECTOR& vSurfSel, ICRVCOMPOPOMATRIX& matCompoBorders) +{ + vSurfSel.clear() ; + matCompoBorders.clear() ; + // Verifico la validità della superficie + if ( pSurf == nullptr || ! pSurf->IsValid()) + return false ; + + // Recupero il Tipo + int nType = pSurf->GetType() ; + + ISURFTMPOVECTOR vSurfTmSelTmp ; + // Se superficie di Besier, non faccio nulla (?) + if ( nType == SRF_BEZIER) + return true ; + // Se superficie TriMesh + else if ( nType == SRF_TRIMESH) { + // Recupero la TriMesh + const SurfTriMesh* pStmBasic = GetBasicSurfTriMesh( pSurf) ; + if ( pStmBasic == nullptr || ! pStmBasic->IsValid()) + return false ; + // Ciclo di Analisi Triangoli + const int MAXTRY = 1000 ; + int nCount = 0 ; + int nTriaCheck = 0 ; + BOOLVECTOR vbTria( pStmBasic->GetTriangleCount(), false) ; + while ( nCount < MAXTRY && nTriaCheck < pStmBasic->GetTriangleCount()) { + // Recupero il primo triangolo valido disponibile non ancora analizzato + int nFirstTria = 0 ; + for ( int nT = 0 ; nT < ssize( vbTria) ; ++ nT) { + if ( ! vbTria[nT]) { + nFirstTria = nT ; + break ; + } + } + unordered_set setNewTria ; setNewTria.reserve( pStmBasic->GetTriangleCount()) ; + unordered_set setAvoidTria ; setAvoidTria.reserve( pStmBasic->GetTriangleCount()) ; + // Collezioni di triangoli da analizzare + INTVECTOR vTria ; vTria.reserve( pStmBasic->GetTriangleCount()) ; + vTria.push_back( nFirstTria) ; + // 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 ( vbTria[nCurrTria]) + continue ; + vbTria[nCurrTria] = true ; + ++ nTriaCheck ; + + // Inserisco il triangolo corrente + setNewTria.insert( nCurrTria) ; + + // Recupero il triangolo corrente e le sue adiacenze + int nTriaVertices[3] ; + if ( ! pStmBasic->GetTriangle( nCurrTria, nTriaVertices)) + return false ; + Triangle3d Tria ; + if ( ! pStmBasic->GetTriangle( nCurrTria, Tria)) + return false ; + int nIdAdjTriaId[3] ; + if ( ! pStmBasic->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 non ho adiacenza, non faccio nulla + if ( nTriaAdj == SVT_NULL) + continue ; + // Recupero il triangolo adiacente + Triangle3d TriaAdj ; + if ( ! pStmBasic->GetTriangle( nTriaAdj, TriaAdj)) + return false ; + // Se la normale è fuori dalla tolleranza, memorizzo il triangolo tra quelli da scartare + if ( Tria.GetN() * TriaAdj.GetN() < cos( ( dFaceAngTol + EPS_ANG_SMALL) * DEGTORAD)) { + setAvoidTria.insert( nTriaAdj) ; + continue ; + } + // Inserisco il triangolo per la ricerca futura + vTria.push_back( nTriaAdj) ; + } + } + ++ nCount ; + // Se ho nuovi triangoli, definisco una superficie da essi + if ( ! setNewTria.empty()) { + + #if DEBUG_SHAPE_STM + VT.clear() ; VC.clear() ; + VT.emplace_back( pStmBasic->Clone()) ; + VC.emplace_back( BLACK) ; + StmFromTriangleSoup _TriaSoup ; _TriaSoup.Start() ; + for ( auto _Iter = setNewTria.begin() ; _Iter != setNewTria.end() ; ++ _Iter) { + Triangle3d _Tria ; + if ( ! pStmBasic->GetTriangle( *_Iter, _Tria)) + return false ; + _TriaSoup.AddTriangle( _Tria) ; + } + _TriaSoup.End() ; + VT.emplace_back( _TriaSoup.GetSurf()) ; + VC.emplace_back( LIME) ; + //SaveGeoObj( VT, VC, "C:\\Temp\\MyShape.nge") ; + VT.clear() ; VC.clear() ; + #endif + + StmFromTriangleSoup TriaSoup ; TriaSoup.Start() ; + for ( auto Iter = setNewTria.begin() ; Iter != setNewTria.end() ; ++ Iter) { + Triangle3d Tria ; + if ( ! pStmBasic->GetTriangle( *Iter, Tria)) + return false ; + TriaSoup.AddTriangle( Tria) ; + } + TriaSoup.End() ; + PtrOwner pStmTmp( TriaSoup.GetSurf()) ; + if ( ! IsNull( pStmTmp) && pStmTmp->IsValid() && pStmTmp->GetTriangleCount() > 0) { + if ( ! vSurfTmSelTmp.emplace_back( Release( pStmTmp))) + return false ; + } + } + } + } + + // Se non ho ricavato superfici, esco + if ( vSurfTmSelTmp.empty()) + return true ; + + // Per ognuna di esse ricerco le geometrie compatibili + for ( int i = 0 ; i < ssize( vSurfTmSelTmp) ; ++ i) { + // Recupero i Loops + POLYLINEVECTOR vPL ; + vSurfTmSelTmp[i]->GetLoops( vPL) ; + // Elimino i Loop che non sono chiusi + vPL.erase( + remove_if( vPL.begin(), vPL.end(), []( const PolyLine& PL) { + return ( ! PL.IsClosed()) ; + }), + vPL.end() + ) ; + // Se non ho esattamente due Loops, passo alla superficie successiva + if ( ssize( vPL) != 2) + continue ; + // Se devo ricercare Circonferenze + if ( find_if( vsShapes.begin(), vsShapes.end(), [&]( const string& sStr) { + return ( sStr.compare( sCircle) == 0) ; + }) != vsShapes.end()) { + // Verifico che le due PolyLine siano circonferenze + Point3d ptCA, ptCB ; Vector3d vtNA, vtNB ; double dRadA, dRadB ; + if ( IsBorderACircle( vPL[0], dShapeLinTol, ptCA, vtNA, dRadA) && + IsBorderACircle( vPL[1], dShapeAngTol, ptCB, vtNB, dRadB)) { + // Definisco le due circonferenze + PtrOwner pCirA( CreateCurveArc()) ; + PtrOwner pCirB( CreateCurveArc()) ; + if ( ! IsNull( pCirA) && ! IsNull( pCirB) && + pCirA->Set( ptCA, vtNA, dRadA) && + pCirB->Set( ptCB, vtNB, dRadB)) { + vSurfSel.emplace_back( GetSurf( Release( vSurfTmSelTmp[i]))) ; + matCompoBorders.emplace_back( ICRVCOMPOPOVECTOR{}) ; + matCompoBorders.back().resize( 2) ; + matCompoBorders.back()[0].Set( ConvertCurveToComposite( Release( pCirA))) ; + matCompoBorders.back()[1].Set( ConvertCurveToComposite( Release( pCirB))) ; + // l'Approssimazione delle curve prevede che io usi sempre tratti lineari, quindi approssimo + PolyLine PLA ; matCompoBorders.back()[0]->ApproxWithLines( dLinTol, dAngTol, ICurve::APL_SPECIAL, PLA) ; + PolyLine PLB ; matCompoBorders.back()[0]->ApproxWithLines( dLinTol, dAngTol, ICurve::APL_SPECIAL, PLB) ; + matCompoBorders.back()[0]->Clear() ; matCompoBorders.back()[0]->FromPolyLine( PLA) ; + matCompoBorders.back()[1]->Clear() ; matCompoBorders.back()[1]->FromPolyLine( PLB) ; + } + } + + #if DEBUG_SHAPE_STM + CurveComposite _CompoA ; _CompoA.FromPolyLine( vPL[0]) ; + CurveComposite _CompoB ; _CompoB.FromPolyLine( vPL[1]) ; + VT.emplace_back( _CompoA.Clone()) ; + VT.emplace_back( _CompoB.Clone()) ; + Color _cCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; + VC.emplace_back( _cCol) ; + VC.emplace_back( _cCol) ; + #endif + } + } + + // #if DEBUG_SHAPE_STM + // SaveGeoObj( VT, VC, "C:\\Temp\\myAutoBorders.nge") ; + // #endif + + 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 ; + } + + 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 ( int( vCompoBezierEdges.size()) == 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 ( int( vCompoBezierEdges.size()) == 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 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 di cui fare l'Offset + PtrOwner pStm( nullptr) ; + if ( int( vpSurf.size()) == 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( dMyLinTol))) ; + } + } + else { + // [ Soluzione temporanea ] + 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 pStmTmp( pSrfBz->GetApproxSurf( dMyLinTol)) ; + if ( ! IsNull( pStmTmp) && pStmTmp->IsValid()) + mySoup.AddSurfTriMesh( *pStmTmp) ; + } + } + mySoup.End() ; + pStm.Set( GetBasicSurfTriMesh( mySoup.GetSurf())) ; + } + if ( IsNull( pStm) || ! pStm->IsValid() || pStm->IsClosed()) + return false ; + + // Eseguo l'Offset in negativo della superficie passando per lo ZMap + double dPrec = max( min( dThick / 4., 10. * max( dMyLinTol, EPS_SMALL)), 0.5) ; + PtrOwner pStmOffs( CreateSurfTriMeshesOffset( { pStm}, - dThick, dPrec)) ; + if ( IsNull( pStmOffs) || ! pStmOffs->IsValid()) + return false ; + + // Recupero i Loop dalla superficie originaria e i Loop della superficie di Offset + POLYLINEVECTOR vPL, vPLOffs ; + if ( ! pStm->GetLoops( vPL) || ! pStmOffs->GetLoops( vPLOffs)) + return false ; + + #if DEBUG_BORDERS_BY_NORMALS + for ( const ISurf* _pSurf : vpSurf) { + VT.emplace_back( _pSurf->Clone()) ; + VC.emplace_back( LIME) ; + } + VT.emplace_back( pStmOffs->Clone()) ; + VC.emplace_back( ORANGE) ; + SaveGeoObj( VT, VC, "C:\\Temp\\TwoSurfs.nge") ; + #endif + + // Trasformo ogni Loop in curva composita + ICRVCOMPOPOVECTOR vCompoLoops ; vCompoLoops.reserve( vPL.size()) ; + for ( const PolyLine& PL : vPL) { + if ( PL.IsClosed()) { + if ( ! vCompoLoops.emplace_back( CreateBasicCurveComposite()) || + ! vCompoLoops.back()->FromPolyLine( PL) || + ! vCompoLoops.back()->IsValid()) + return false ; + } + } + ICRVCOMPOPOVECTOR vCompoOffsLoops ; vCompoOffsLoops.reserve( vPLOffs.size()) ; + for ( const PolyLine& PLOffs : vPLOffs) { + if ( PLOffs.IsClosed()) { + if ( ! vCompoOffsLoops.emplace_back( CreateCurveComposite()) || + ! vCompoOffsLoops.back()->FromPolyLine( PLOffs) || + ! vCompoOffsLoops.back()->IsValid()) + return false ; + } + } + + #if DEBUG_BORDERS_BY_NORMALS + for ( ICurveComposite* pCompo : vCompoLoops) { + VT.emplace_back( pCompo->Clone()) ; + VC.emplace_back( GREEN) ; + } + for ( ICurveComposite* pCompoOffs : vCompoOffsLoops) { + VT.emplace_back( pCompoOffs->Clone()) ; + VC.emplace_back( BLUE) ; + } + SaveGeoObj( VT, VC, "C:\\Temp\\TwoSurfs.nge") ; + #endif + + // Per ogni curva della superficie originale cerco la sua associata + BOOLVECTOR vIndMatched( vCompoOffsLoops.size(), false) ; + for ( const ICurveComposite* pCompoLoop : vCompoLoops) { + // sposto il punto iniziale della curva nel tratto più lungo + double dMaxLen = - INFINITO ; + int nIndCrv = 0 ; + for ( int nCrv = 0 ; nCrv < pCompoLoop->GetCurveCount() ; ++ nCrv) { + const ICurve* pCurve = pCompoLoop->GetCurve( nCrv) ; + if ( pCurve != nullptr && pCurve->IsValid()) { + double dCurrLen = 0. ; + pCurve->GetLength( dCurrLen) ; + if ( dCurrLen > dMaxLen) { + dMaxLen = dCurrLen ; + nIndCrv = nCrv ; + } + } + } + Point3d ptRef ; + pCompoLoop->GetPointD1D2( nIndCrv + 0.5, ICurve::FROM_MINUS, ptRef) ; + // dalle altre curve derivanti dalla superficie di Offset cerco quella più vicina al punto inziale + double dMinSqDist = INFINITO ; + int nIndOffsCrv = -1 ; + for ( int nOffsCrv = 0 ; nOffsCrv < int( vCompoOffsLoops.size()) ; ++ nOffsCrv) { + if ( vIndMatched[nOffsCrv]) + continue ; + // recupero la curva e calcolo la distanza + const ICurveComposite* pCompoOffsLoop = vCompoOffsLoops[nOffsCrv] ; + int nFlag = 0 ; + Point3d ptCurrMinDist ; + if ( DistPointCurve( ptRef, *pCompoOffsLoop).GetMinDistPoint( 0., ptCurrMinDist, nFlag)) { + double dCurrSqDist = SqDist( ptRef, ptCurrMinDist) ; + if ( dCurrSqDist < dMinSqDist) { + dMinSqDist = dCurrSqDist ; + nIndOffsCrv = nOffsCrv ; + } + } + } + if ( nIndOffsCrv == -1) + return false ; + vIndMatched[ nIndOffsCrv] = true ; + // associo le due curve ( quindi consecutive nel vettore delle curve risultanti) + vCompoBezierEdges.emplace_back( CloneCurveComposite( pCompoLoop)) ; + if ( ! ApproxCurveWithBezier( vCompoBezierEdges.back(), dMyLinTol, dMyAngTol)) + return false ; + vCompoBezierEdges.emplace_back( CloneCurveComposite( vCompoOffsLoops[nIndOffsCrv])) ; + if ( ! ApproxCurveWithBezier( vCompoBezierEdges.back(), dMyLinTol, dMyAngTol)) + return false ; + } + + 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 < int( vCompoRawEdges.size()) ; ++ _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 < int( vCompoRawEdges.size()) ; ++ i) { + PtrOwner pCompoTmp( CloneCurveComposite( vCompoRawEdges[i])) ; + if ( IsNull( pCompoTmp) || + ! ApproxCurveWithBezier( pCompoTmp, dMyLinTol, dMyAngTol) || + ! vBezierEdges.emplace_back( Release( pCompoTmp))) { + LOG_ERROR( GetEGkLogger(), "Error in Trimming : Approxing edge failed") ; + return false ; + } + } + + #if DEBUG_EDGES + VC.clear() ; VT.clear() ; + for ( int _i = 0 ; _i < int( vCompoTmpEdges.size()) ; ++ _i) { + VT.emplace_back( vCompoTmpEdges[_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) +{ + // Verifica validità delle curve per la creazione della Bezier rigata + PtrOwner pCompoEdge1( ConvertCurveToComposite( pCrvEdge1->Clone())) ; + PtrOwner pCompoEdge2( ConvertCurveToComposite( pCrvEdge2->Clone())) ; + if ( ! ManageRuledBorders( pCompoEdge1, pCompoEdge2)) + return false ; + vSyncPoints.clear() ; + + // 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 pSBzRuled( GetBasicSurfBezier( GetSurfBezierRuled( pCompoEdge1, pCompoEdge2, ISurfBezier::RLT_B_MINDIST_PLUS, dMyLinTol))) ; + 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 < int( vCrv.size()) ; ++ 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 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, + const INTVECTOR& vIndPriority, const INTVECTOR& vIndVisible) +{ + // Verifica validità delle curve per la creazione della Bezier rigata + PtrOwner pCompoEdge1( ConvertCurveToComposite( pCrvEdge1->Clone())) ; + PtrOwner pCompoEdge2( ConvertCurveToComposite( pCrvEdge2->Clone())) ; + 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 + + // Creo la superficie di Bezier + PtrOwner pSurfBz( CreateSurfBezier()) ; + if ( IsNull( pSurfBz)) + return nullptr ; + + // Se non ho punti di controllo forzati + if ( vSyncPoints.empty()) { + pSurfBz.Set( GetSurfBezierRuled( pCompoEdge1, pCompoEdge2, ISurfBezier::RLT_B_MINDIST_PLUS, dMyLinTol)) ; + if ( IsNull( pSurfBz) || ! pSurfBz->IsValid()) { + LOG_ERROR( GetEGkLogger(), "Error in Trimming : Ruled Bezier invalid") ; + return nullptr ; + } + } + // Se ho punti di controllo forzati + else { + // Recupero i tratti lineari di sincronizzazione + ICURVEPOVECTOR vCrv ; vCrv.reserve( vSyncPoints.size()) ; + for ( const BIPOINT& PtSPtE : vSyncPoints) { + PtrOwner pLine( CreateCurveLine()) ; + if ( IsNull( pLine) || ! pLine->Set( PtSPtE.first, PtSPtE.second)) + return nullptr ; + vCrv.emplace_back( Release( pLine)) ; + } + #if DEBUG_BEZIER_RULED + for ( int _i = 0 ; _i < int( vCrv.size()) ; ++ _i) { + VT_Glob.emplace_back( vCrv[_i]) ; + VC_Glob.emplace_back( LIME) ; + } + #endif + // Definisco la superficie + pSurfBz.Set( GetSurfBezierRuledGuided( pCompoEdge1, pCompoEdge2, vCrv, 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( LIME) ; + 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, const INTVECTOR& vTria, const PNTVECTOR& vPts, + double dAngTol, double dSize, double dSizeTol, INTVECTOR& vOtherTria) +{ + // Verifico la validità della superficie TriMesh e l'esistenza di triangoli di riferimento + if ( ! pStm->IsValid() || vTria.empty()) + 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( vTria[0], firstTria)) + return false ; + BBox3d BBoxCurr ; + BBoxCurr.Add( vPts[0]) ; + 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 ; + unordered_set setAvoidTria ; setAvoidTria.reserve( pStmBasic->GetTriangleCount()) ; + unordered_set setValidTria ; setValidTria.reserve( pStmBasic->GetTriangleCount()) ; + unordered_set setNewTria ; setNewTria.reserve( pStmBasic->GetTriangleCount()) ; + Point3d ptMid, ptMid1 ; + int nTria = 0, nTria1 = 0 ; + CurveComposite CompoA, CompoB ; + if ( ! GetBordersInBox( *pStmBasic, BBoxCurr, vTria[0], vPts[0], dSize, dSizeTol, true, true, + dAngTol, 2. * EPS_SMALL, setAvoidTria, setNewTria, 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) ; + return true ; + } + + // 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, 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, false, ptMid, nTria, + dSize, dSizeTol, dAngTol, bRelFirst, CompoA, CompoB, bStop)) + return false ; + } + + // Per ogni triangolo recuperato, cerco le facce a cui appartiene + unordered_set setValidFace ; + for ( auto Iter = setValidTria.begin() ; Iter != setValidTria.end() ; ++ Iter) + vOtherTria.push_back( *Iter) ; + + return true ; +} + +//------------------------------------------------------------------------------ +// 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 ; + } + + // creo l'insieme delle curve di bordo in continuità + SURFPATCHESVECTOR vSurfPatches ; vSurfPatches.reserve( vSurf.size() + vOtherSurf.size()) ; + BOXVECTOR vBBox3d ; vBBox3d.reserve( vSurf.size() + vOtherSurf.size()) ; + for ( int nS = 0 ; nS < int( vSurf.size()) ; ++ nS) { + vSurfPatches.emplace_back( SurfPatches()) ; + vSurfPatches.back().pSurf = vSurf[nS] ; + vSurfPatches.back().nIndSurf = nS ; + vBBox3d.emplace_back( BBox3d()) ; + POLYLINEVECTOR vPLBorders ; + if ( ! InsertLoopsOfSurf( vSurf[nS], vPLBorders)) + return false ; + for ( int nPL = 0 ; nPL < int( vPLBorders.size()) ; ++ nPL) + vPLBorders[nPL].GetLocalBBox( vBBox3d.back()) ; + vBBox3d.back().Expand( 2. * dLinTol) ; + POLYLINEMATRIX matPLPatchBorders ; + if ( ! CalcPatches( vPLBorders, dAngTol, matPLPatchBorders)) + return false ; + for ( int i = 0 ; i < int( matPLPatchBorders.size()) ; ++ i) { + for ( int j = 0 ; j < int( matPLPatchBorders[i].size()) ; ++ j) { + Point3d ptS ; matPLPatchBorders[i][j].GetFirstPoint( ptS) ; + Point3d ptE ; matPLPatchBorders[i][j].GetLastPoint( ptE) ; + vSurfPatches.back().vPatches.emplace_back( ptS, ptE, i, false) ; + vSurfPatches.back().CompoPathces.emplace_back( CurveComposite()) ; + vSurfPatches.back().CompoPathces.back().FromPolyLine( matPLPatchBorders[i][j]) ; + } + } + } + for ( int nS = 0 ; nS < int( vOtherSurf.size()) ; ++ nS) { + vSurfPatches.emplace_back( SurfPatches()) ; + vSurfPatches.back().pSurf = vOtherSurf[nS] ; + vSurfPatches.back().nIndSurf = int( vSurf.size()) + nS ; + vBBox3d.emplace_back( BBox3d()) ; + POLYLINEVECTOR vPLBorders ; + if ( ! InsertLoopsOfSurf( vOtherSurf[nS], vPLBorders)) + continue ; + for ( int nPL = 0 ; nPL < int( vPLBorders.size()) ; ++ nPL) + vPLBorders[nPL].GetLocalBBox( vBBox3d.back()) ; + vBBox3d.back().Expand( 2. * dLinTol) ; + POLYLINEMATRIX matPLPatchBorders ; + if ( ! CalcPatches( vPLBorders, dAngTol, matPLPatchBorders)) + return false ; + for ( int i = 0 ; i < int( matPLPatchBorders.size()) ; ++ i) { + for ( int j = 0 ; j < int( matPLPatchBorders[i].size()) ; ++ j) { + Point3d ptS ; matPLPatchBorders[i][j].GetFirstPoint( ptS) ; + Point3d ptE ; matPLPatchBorders[i][j].GetLastPoint( ptE) ; + vSurfPatches.back().vPatches.emplace_back( ptS, ptE, i, false) ; + vSurfPatches.back().CompoPathces.emplace_back( CurveComposite()) ; + vSurfPatches.back().CompoPathces.back().FromPolyLine( matPLPatchBorders[i][j]) ; + } + } + } + + // Se necessario, Splitto le Patch per far combiaciare gli estremi + for ( int i = 0 ; i < int( vSurfPatches.size()) - 1 ; ++ i) { + for ( int j = i + 1 ; j < int( vSurfPatches.size()) ; ++ j) { + if ( vBBox3d[i].Overlaps( vBBox3d[j])) { + if ( ! SplitPatchWithPatch( vSurfPatches[i], vSurfPatches[j], dLinTol)) + return false ; + } + } + } + + #if DEBUG_SIMPLE_PATCHES + VT.clear() ; VC.clear() ; + for ( int i = 0 ; i < int( vSurfPatches.size()) ; ++ i) { + Color myCol = Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; + for ( int j = 0 ; j < int( vSurfPatches[i].CompoPathces.size()) ; ++ j) { + VT.emplace_back( vSurfPatches[i].CompoPathces[j].Clone()) ; + VC.emplace_back( myCol) ; + IGeoPoint3d* _pt = CreateGeoPoint3d() ; _pt->Set( vSurfPatches[i].vPatches[j].ptStart) ; + VT.emplace_back( _pt) ; + VC.emplace_back( AQUA) ; + IGeoPoint3d* _pt1 = CreateGeoPoint3d() ; _pt1->Set( vSurfPatches[i].vPatches[j].ptEnd) ; + VT.emplace_back( _pt1) ; + VC.emplace_back( AQUA) ; + } + } + SaveGeoObj( VT, VC, "C:\\Temp\\SimplePatch.nge") ; + #endif + + // Verifico ora se le tangenze nei punti di giunzione sono al di sotto della tolleranza angolare + INTVECTOR vSurfToCheck ; + INTSET setSurf ; + for ( int i = 0 ; i < int( vSurf.size()) ; ++ 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 superficie corrente + const ISurf* pCurrSurf = vSurfPatches[nSurf].pSurf ; + + // Scorro le sua Patches + for ( int nCurrPatch = 0 ; nCurrPatch < int( vSurfPatches[nSurf].vPatches.size()) ; ++ nCurrPatch) { + // Recupero la Patch attuale + SimplePatch& currPatch = vSurfPatches[nSurf].vPatches[nCurrPatch] ; + // Se Patch già analizzata, passo alla successiva + if ( currPatch.bErase) + continue ; + currPatch.bErase = true ; + // Scorro le altre Patches + for ( int nOtherSurf = 0 ; nOtherSurf < int( vSurfPatches.size()) ; ++ 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 superficie di confronto + const ISurf* pOtherSurf = vSurfPatches[nOtherSurf].pSurf ; + // Scorro le sua Patches + for ( int nOtherPatch = 0 ; nOtherPatch < int( vSurfPatches[nOtherSurf].vPatches.size()) ; ++ nOtherPatch) { + // Recupero la Patch di confronto + SimplePatch& nextPatch = vSurfPatches[nOtherSurf].vPatches[nOtherPatch] ; + // Se Patch già analizzata, passo alla successiva + if ( nextPatch.bErase) + continue ; + // Se gli estremi coincidono + if ( ( AreSamePointEpsilon( currPatch.ptStart, nextPatch.ptStart, dLinTol) && + AreSamePointEpsilon( currPatch.ptEnd, nextPatch.ptEnd, dLinTol)) || + ( AreSamePointEpsilon( currPatch.ptStart, nextPatch.ptEnd, dLinTol) && + AreSamePointEpsilon( currPatch.ptEnd, nextPatch.ptStart, dLinTol))) { + // Controllo se le tangenze rimangono nella tolleranza + bool bTangent = true ; + // Campiono la Patch corrente + const int MAX_POINTS = 5 ; + BIPNTVECTOR vPtAPtB ; vPtAPtB.reserve( MAX_POINTS + 1) ; + CurveComposite& currCompoPatch = vSurfPatches[nSurf].CompoPathces[nCurrPatch] ; + CurveComposite& otherCompoPatch = vSurfPatches[nOtherSurf].CompoPathces[nOtherPatch] ; + 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 ; + DistPointCurve( ptCurr, otherCompoPatch).GetMinDistPoint( 0., ptOther, nFlag) ; + // 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) { + vSurfToCheck.emplace_back( nOtherSurf) ; + nextPatch.bErase = true ; + if ( nOtherSurf >= int( vSurf.size()) && + find( vIndOtherSurf.begin(), vIndOtherSurf.end(), nOtherSurf - int( vSurf.size())) == vIndOtherSurf.end()) + vIndOtherSurf.push_back( nOtherSurf - int( vSurf.size())) ; + } + } + } + } + } + } + + #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 la ricerca automatica delle Geometrie di Trimming +bool +GetTrimmingAutoEntities( const CISURFPVECTOR& vSurf, double dShapeLinTol, double dShapeAngTol, + double dLinTol, double dEdgeLinTol, double dAngTol, double dAngFaceTol, + const STRVECTOR& vsShapes, ISURFPOMATRIX& matSelSurf, + ICRVCOMPOPOMATRIX& matCompoBorders, ISURFBEZPOVECTOR& vSurfBz) +{ + // Pulizia dei parametri di ingresso + matSelSurf.clear() ; + matCompoBorders.clear() ; + vSurfBz.clear() ; + + // Se non ho superfici non devo fare nulla + if ( vSurf.empty()) + return true ; + // Verifico che le superfici presenti siano ben definite + for ( const ISurf* pSurf : vSurf) { + if ( pSurf == nullptr || ! pSurf->IsValid()) + return false ; + } + + // Controllo sulla tolleranza lineare + double dMyShapeLinTol = Clamp( dShapeLinTol, EPS_SMALL, 1e5 * EPS_SMALL) ; + double dMyLinTol = Clamp( dLinTol, EPS_SMALL, 1e5 * EPS_SMALL) ; + double dMyEdgeLinTol = Clamp( dEdgeLinTol, EPS_SMALL, 1e5 * EPS_SMALL) ; + // Controllo sulla tolleranza angolare + double dMyShapeAngTol = Clamp( dShapeAngTol, 1., 60.) ; + double dMyAngTol = Clamp( dAngTol, 1., 45.) ; + double dMyFaceAngTol = Clamp( dAngFaceTol, 1., 60.) ; + + ICRVCOMPOPOMATRIX matCompoBordersTmp ; + // Se una sola superficie, effettuo la ricerca per triangoli + if ( ssize( vSurf) == 1) { + ISURFPOVECTOR vSurfTmp ; + if ( ! GetShapesFromSurf( vSurf[0], dMyShapeLinTol, dMyShapeAngTol, dMyLinTol, dMyEdgeLinTol, + dMyAngTol, dMyFaceAngTol, vsShapes, vSurfTmp, matCompoBordersTmp)) + return false ; + for ( int i = 0 ; i < ssize( vSurfTmp) ; ++ i) { + matSelSurf.emplace_back( ISURFPOVECTOR{}) ; + matSelSurf.back().emplace_back( Release( vSurfTmp[i])) ; + } + } + // Se più superfici eseguo la ricerca per Patches + else { + + } + + // Se non ho alcuna Geometria voluta, esco + if ( matSelSurf.empty()) + return true ; + + // Per ogni Geometria, approssimo le Curve mediante delle Bezier e calolo la Rigata + for ( int i = 0 ; i < ssize( matSelSurf) ; ++ i) { + matCompoBorders.emplace_back( ICRVCOMPOPOVECTOR{}) ; matCompoBorders.back().reserve( 2) ; + if ( ! GetTrimmingBezierEdges( matCompoBordersTmp[i], dLinTol, dAngTol, matCompoBorders.back())) + return false ; + vSurfBz.emplace_back( GetTrimmingRuledBezier( vSurf, matCompoBorders.back()[0], matCompoBorders.back()[1], + dMyLinTol, BIPNTVECTOR{}, INTVECTOR{}, INTVECTOR{})) ; + } + + return true ; +}