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