diff --git a/CurveAux.cpp b/CurveAux.cpp index 1aaceff..9291c93 100644 --- a/CurveAux.cpp +++ b/CurveAux.cpp @@ -1595,8 +1595,10 @@ FitWithBezier( const ICurve* pCrvOrig, const PNTVECTOR& vPnt, DBLVECTOR& vParam, //---------------------------------------------------------------------------- ICurve* -ApproxCurveWithBezier( const ICurve* pCrv , double dTol) +ApproxCurveWithBezier( const ICurve* pCrv , double dTol, const Vector3d& vtStart, const Vector3d& vtEnd) { + if ( pCrv == nullptr || ! pCrv->IsValid()) + return nullptr ; #if SAVECURVEPASSED SaveGeoObj( pCrv->Clone(), "D:\\Temp\\bezier\\approxWithBezier\\CurveDaApprossimare\\"+ToString(nCrvPassed) + ".nge") ; @@ -1668,6 +1670,10 @@ ApproxCurveWithBezier( const ICurve* pCrv , double dTol) VCT3DVECTOR vPrevDer ; VCT3DVECTOR vNextDer ; ComputeAkimaTangents( false, vParam, vPnt, vPrevDer, vNextDer) ; + if ( ! AreSameVectorExact(vtStart, V_NULL)) { + vNextDer[0] = vtStart ; + vPrevDer.back() = vtEnd ; + } int nOverSampling = ssize( vPntOverSampling) ; vParam.resize( nOverSampling) ; diff --git a/EgtGeomKernel.vcxproj.filters b/EgtGeomKernel.vcxproj.filters index e5df427..0143f2b 100644 --- a/EgtGeomKernel.vcxproj.filters +++ b/EgtGeomKernel.vcxproj.filters @@ -557,6 +557,9 @@ File di origine\Gdb + + + File di origine\GeoTrimming File di origine\GeoDist diff --git a/SbzFromCurves.cpp b/SbzFromCurves.cpp index c68f5ea..b613556 100644 --- a/SbzFromCurves.cpp +++ b/SbzFromCurves.cpp @@ -735,7 +735,7 @@ GetSurfBezierRuled( const ICurve* pCurve1, const ICurve* pCurve2, int nType, dou //------------------------------------------------------------------------------- ISurfBezier* -GetSurfBezierRuledSmooth( const ICurve* pCurve1, const ICurve* pCurve2, double dSampleLen) +GetSurfBezierRuledSmooth( const ICurve* pCurve1, const ICurve* pCurve2, BIPNTVECTOR& vSyncLines, double dSampleLen) { // verifica parametri if ( pCurve1 == nullptr || pCurve2 == nullptr) @@ -762,7 +762,7 @@ GetSurfBezierRuledSmooth( const ICurve* pCurve1, const ICurve* pCurve2, double d // creo e setto la superficie trimesh PtrOwner pSbz( CreateBasicSurfBezier()) ; - if ( IsNull( pSbz) || ! pSbz->CreateSmoothRuledByTwoCurves( pCC1, pCC2, dSampleLen)) + if ( IsNull( pSbz) || ! pSbz->CreateSmoothRuledByTwoCurves( pCC1, pCC2, dSampleLen, vSyncLines)) return nullptr ; // restituisco la superficie diff --git a/SurfBezier.cpp b/SurfBezier.cpp index 2b2e72d..3f9b541 100644 --- a/SurfBezier.cpp +++ b/SurfBezier.cpp @@ -57,6 +57,7 @@ || SAVEFAILEDTREE || SAVELIMITSURF || SAVEPACEDISO #include "/EgtDev/Include/EGkGeoObjSave.h" std::vector vGeo ; + std::vector vCol ; #endif using namespace std ; @@ -5896,15 +5897,31 @@ SurfBezier::CreateByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, int return true ; } +// Struttura per Spigolo struct SharpEdge { double dParU ; Point3d ptEdge ; - Vector3d vtTanPrev, vtTanAft ; + Vector3d vtTanPrev, vtTanNext ; SharpEdge( double dU, const Point3d ptE, const Vector3d& vtTP, const Vector3d& vtTA) - : dParU( dU), ptEdge( ptE), vtTanPrev( vtTP), vtTanAft( vtTA) {} + : dParU( dU), ptEdge( ptE), vtTanPrev( vtTP), vtTanNext( vtTA) {} } ; typedef vector SHARPEDGEVECTOR ; +// Struttura per Punto a Curvatura Massima +struct CurvaturePoint { + double dParU ; + Point3d ptCurvature ; + Vector3d vtTanPrev, vtTanNext ; + CurvaturePoint( double dU, const Point3d ptE, const Vector3d& vtTP, const Vector3d& vtTA) + : dParU( dU), ptCurvature( ptE), vtTanPrev( vtTP), vtTanNext( vtTA) {} +} ; +typedef vector SHARPEDGEVECTOR ; + +// --------------------------------------------------------------------------- +// Calcolo delle Curve di Sync tra gli spigoli di una curva e l'altra curva nella sua integrità +// [Dato un vettore di SharpEdge di una Curva di Bordo si calcola la Linea di Sincronizzazione tra ognuno di essi +// e l'altra Curva di Bordo] +// -> Restituisce un vettore di Linee di Sincronizzazione static bool CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite* pCompoSubEdge, const SHARPEDGEVECTOR& vSharpSubEdge, BIPNTVECTOR& vSyncLines) @@ -5919,13 +5936,20 @@ CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite* if ( vSharpEdge.empty()) return true ; + // Se il numero di Spigoli coincide, allora li unisco sequenzialmente tra loro ( considerazione buona ???) + if ( ssize( vSharpEdge) == ssize( vSharpSubEdge)) { + for ( int i = 0 ; i < ssize( vSharpEdge) ; ++ i) + vSyncLines.emplace_back( vSharpEdge[i].ptEdge, vSharpSubEdge[i].ptEdge) ; + return false ; + } + // Scorro gli SharpEdges double dUPrevSubEdge = 0., dUCurrSubEdge = 0. ; for ( const SharpEdge& mySharpEdge : vSharpEdge) { // Recupero il parametro sulla curva di sincronizzazione // --- Piano di taglio per punto a distanza minima - IntersCurvePlane ICP( *pCompoSubEdge, mySharpEdge.ptEdge, Media( mySharpEdge.vtTanPrev, mySharpEdge.vtTanAft)) ; + IntersCurvePlane ICP( *pCompoSubEdge, mySharpEdge.ptEdge, Media( mySharpEdge.vtTanPrev, mySharpEdge.vtTanNext)) ; int nIndParCloser = -1, nIndPointCloser = -1 ; double dSqMinDist = INFINITO ; for ( int nInfo = 0 ; nInfo < ICP.GetIntersCount() ; ++ nInfo) { @@ -5940,7 +5964,7 @@ CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite* } } } - bool bOkPlane = ( nIndParCloser != -1 && nIndPointCloser != -1) ; + bool bOkPlane = ( nIndParCloser != -1 && nIndPointCloser != -1) ; if ( bOkPlane) { // Se gli indici sono tra loro coerenti allora ho individuato il punto if ( nIndParCloser == nIndPointCloser) { @@ -5948,30 +5972,31 @@ CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite* ICP.GetIntCrvPlnInfo( nIndParCloser, aInfo) ; dUCurrSubEdge = aInfo.Ici[0].dU ; } + // Se gli indici sono discordi, devo scegliere quale dei due punti tenere + else { + // scelgo il punto più vicino al corrente + IntCrvPlnInfo aInfoPt, aInfoPar ; + ICP.GetIntCrvPlnInfo( nIndPointCloser, aInfoPt) ; + ICP.GetIntCrvPlnInfo( nIndParCloser, aInfoPar) ; + dUCurrSubEdge = ( SqDist( mySharpEdge.ptEdge, aInfoPt.Ici[0].ptI) < SqDist( mySharpEdge.ptEdge, aInfoPar.Ici[0].ptI) ? + aInfoPt.Ici[0].dU : aInfoPar.Ici[0].dU) ; + } } - // Se gli indici sono discordi, devo scegliere quale dei due punti tenere - else { - // scelgo il punto più vicino al corrente - IntCrvPlnInfo aInfoPt, aInfoPar ; - ICP.GetIntCrvPlnInfo( nIndPointCloser, aInfoPt) ; - ICP.GetIntCrvPlnInfo( nIndParCloser, aInfoPar) ; - dUCurrSubEdge = ( SqDist( mySharpEdge.ptEdge, aInfoPt.Ici[0].ptI) < SqDist( mySharpEdge.ptEdge, aInfoPar.Ici[0].ptI) ? - aInfoPt.Ici[0].dU : aInfoPar.Ici[0].dU) ; - if ( ! bOkPlane) { - // --- Altrimenti, cerco il punto a minima distanza - DistPointCurve DPC( mySharpEdge.ptEdge, *pCompoSubEdge) ; - int nFlag ; - bool bOkMinDist = ( DPC.GetParamAtMinDistPoint( dUPrevSubEdge, dUCurrSubEdge, nFlag) && dUCurrSubEdge > dUPrevSubEdge) ; - if ( ! bOkMinDist) { - // --- Eh.... bho ( se arrivo qui non so che fare...) - double dUStart, dUEnd ; - pCompoSubEdge->GetDomain( dUStart, dUEnd) ; - dUCurrSubEdge = ( dUEnd - dUCurrSubEdge) / 2. ; - } + if ( ! bOkPlane) { + // --- Altrimenti, cerco il punto a minima distanza + DistPointCurve DPC( mySharpEdge.ptEdge, *pCompoSubEdge) ; + int nFlag ; + bool bOkMinDist = ( DPC.GetParamAtMinDistPoint( dUPrevSubEdge, dUCurrSubEdge, nFlag) && dUCurrSubEdge > dUPrevSubEdge) ; + if ( ! bOkMinDist) { + // --- Alla peggio mi posiziono a metà tra la posizione corrente e quella finale + double dPrevLen ; pCompoSubEdge->GetLengthAtParam( dUPrevSubEdge, dPrevLen) ; + double dLen ; pCompoSubEdge->GetLength( dLen) ; + pCompoSubEdge->GetParamAtLength( ( dPrevLen + dLen) / 2., dUCurrSubEdge) ; } } // Verifico se tale parametro può essere avvicinato ad uno Spigolo presente sulla curva + // NB. Come descitto sopra gli spigoli in generale è meglio sincronizzarli tra loro if ( ! vSharpSubEdge.empty()) { const double EDGE_LEN_TOL = 5. ; for ( const SharpEdge& mySharpSubEdge : vSharpSubEdge) { @@ -6001,7 +6026,104 @@ CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite* } // --------------------------------------------------------------------------- -// Debug [Gestione Edge in Quadrangolazioni] +// Calcolo della curva di Sync tra un punto di una curva di Bordo (calcolato come il punto a curvatura massima) e il suo +// associato sull'altra Curva di Bordo +// -> Restituisce la singola Linea di Sincronizzazione +static bool +CalcSyncPointFromCurvature( const CurvaturePoint& PointK, const ICurveComposite* pCompoSubEdge, BIPOINT& SyncLine) +{ + // Verifico la validità del SubEdge su cui trovare il punto di Sincronizzazione + if ( pCompoSubEdge == nullptr || ! pCompoSubEdge->IsValid()) + return false ; + + // --- Piano di taglio per punto a distanza minima + double dUCurrSubEdge = 0. ; + IntersCurvePlane ICP( *pCompoSubEdge, PointK.ptCurvature, Media( PointK.vtTanPrev, PointK.vtTanNext)) ; + int nIndParCloser = -1, nIndPointCloser = -1 ; + double dSqMinDist = INFINITO ; + for ( int nInfo = 0 ; nInfo < ICP.GetIntersCount() ; ++ nInfo) { + IntCrvPlnInfo aInfo ; + if ( ICP.GetIntCrvPlnInfo( nInfo, aInfo)) { + if ( nIndParCloser == -1) + nIndParCloser = nInfo ; + double dSqDist = SqDist( PointK.ptCurvature, aInfo.Ici[0].ptI) ; + if ( dSqDist < dSqMinDist) { + dSqMinDist = dSqDist ; + nIndPointCloser = nInfo ; + } + } + } + bool bOkPlane = ( nIndParCloser != -1 && nIndPointCloser != -1) ; + if ( bOkPlane) { + // Se gli indici sono tra loro coerenti allora ho individuato il punto + if ( nIndParCloser == nIndPointCloser) { + IntCrvPlnInfo aInfo ; + ICP.GetIntCrvPlnInfo( nIndParCloser, aInfo) ; + dUCurrSubEdge = aInfo.Ici[0].dU ; + } + // Se gli indici sono discordi, devo scegliere quale dei due punti tenere + else { + // scelgo il punto più vicino al corrente + IntCrvPlnInfo aInfoPt, aInfoPar ; + ICP.GetIntCrvPlnInfo( nIndPointCloser, aInfoPt) ; + ICP.GetIntCrvPlnInfo( nIndParCloser, aInfoPar) ; + dUCurrSubEdge = ( SqDist( PointK.ptCurvature, aInfoPt.Ici[0].ptI) < SqDist( PointK.ptCurvature, aInfoPar.Ici[0].ptI) ? + aInfoPt.Ici[0].dU : aInfoPar.Ici[0].dU) ; + } + } + if ( ! bOkPlane) { + // --- Altrimenti, cerco il punto a minima distanza + DistPointCurve DPC( PointK.ptCurvature, *pCompoSubEdge) ; + int nFlag ; + bool bOkMinDist = ( DPC.GetParamAtMinDistPoint( 0., dUCurrSubEdge, nFlag)) ; + if ( ! bOkMinDist) { + // --- Alla peggio mi posiziono a metà tra la posizione corrente e quella finale + double dPrevLen ; pCompoSubEdge->GetLengthAtParam( 0., dPrevLen) ; + double dLen ; pCompoSubEdge->GetLength( dLen) ; + pCompoSubEdge->GetParamAtLength( ( dPrevLen + dLen) / 2., dUCurrSubEdge) ; + } + } + + // Definisco il primo estremo della linea di Sincronizzazione corrente + SyncLine.first = PointK.ptCurvature ; + + // Verifico se posso muovermi attorno al punto trovato per allinearmi meglio con le normali alla curva + // NB. Si cerca di privilgiare i punti più vicini ( in parametro) al punto calcolato sopra, in modo da cercare di + // non discostare troppo la Linea di sincronizzazione dal punto trovato. + const double MAX_DIST = 2.5 ; + const int NUM_SAMPLE_PNT = 20 ; + double dLen ; pCompoSubEdge->GetLength( dLen) ; + double dSyncLen ; pCompoSubEdge->GetLengthAtParam( dUCurrSubEdge, dSyncLen) ; + Vector3d vtSyncTanPrev, vtSyncTanNext ; + pCompoSubEdge->GetPointD1D2( dUCurrSubEdge, ICurve::FROM_MINUS, SyncLine.second, &vtSyncTanPrev) ; vtSyncTanPrev.Normalize() ; + pCompoSubEdge->GetPointD1D2( dUCurrSubEdge, ICurve::FROM_PLUS, SyncLine.second, &vtSyncTanNext) ; vtSyncTanNext.Normalize() ; + double dLimInfLen = Clamp( dSyncLen - MAX_DIST, 0., dLen) ; + double dLimSupLen = Clamp( dSyncLen + MAX_DIST, 0., dLen) ; + int nSamplePnt = int( ceil( NUM_SAMPLE_PNT / 4.)) ; + double dMaxCos = ( Media( PointK.vtTanPrev, PointK.vtTanNext) * Media( vtSyncTanPrev, vtSyncTanNext)) ; + double dShiftR = ( dLimSupLen - dSyncLen) / nSamplePnt ; + double dShiftL = ( dSyncLen - dLimInfLen) / nSamplePnt ; + array vdLens = { dSyncLen, dSyncLen} ; + for ( int i = 1 ; i <= nSamplePnt ; ++ i) { + vdLens[0] += dShiftR ; + vdLens[1] -= dShiftL ; + for ( int j = 0 ; j < 2 ; ++ j) { + double dU ; pCompoSubEdge->GetParamAtLength( vdLens[j], dU) ; + Point3d ptCurr ; Vector3d vtTan ; + pCompoSubEdge->GetPointD1D2( dU, ICurve::FROM_MINUS, ptCurr, &vtTan) ; vtTan.Normalize() ; + double dCos = ( vtTan * Media( PointK.vtTanPrev, PointK.vtTanNext)) ; + if ( dCos > dMaxCos) { + dMaxCos = dCos ; + SyncLine.second = ptCurr ; + } + } + } + + return true ; +} + +// --------------------------------------------------------------------------- +// Gestione degli spigoli all'interno della Quadrangolazione corrente static bool ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComposite* pSubEdge2, BIPNTVECTOR& vSyncLines) { @@ -6020,7 +6142,7 @@ ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp SHARPEDGEVECTOR vSharpEdges2 ; vSharpEdges2.reserve( max( 0, nCrv2 - 1)) ; // --- Cerco gli Spigoli sul SottoTratto del primo bordo - // Se la CurvaA ha solo una sottocurva, non faccio nulla + // Se la CurvaA ha più di una sottocurva, verifico l'esistenza di Spigoli (Sharp Edges) if ( nCrv1 > 1) { Point3d ptCurr1 ; // Scorro le sottocurve @@ -6029,17 +6151,17 @@ ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp double dU = double( nCrv + 1) ; // Recupero le tangenti prima e dopo tale parametro Vector3d vtTanPrev ; pSubEdge1->GetPointD1D2( dU, ICurve::FROM_MINUS, ptCurr1, &vtTanPrev) ; - Vector3d vtTanAft ; pSubEdge1->GetPointD1D2( dU, ICurve::FROM_PLUS, ptCurr1, &vtTanAft) ; + Vector3d vtTanNext ; pSubEdge1->GetPointD1D2( dU, ICurve::FROM_PLUS, ptCurr1, &vtTanNext) ; // Verifico se ho uno spigolo vtTanPrev.Normalize() ; - vtTanAft.Normalize() ; - if ( vtTanPrev * vtTanAft < COS_EDGE_LIMIT) - vSharpEdges1.emplace_back( dU, ptCurr1, vtTanPrev, vtTanAft) ; + vtTanNext.Normalize() ; + if ( vtTanPrev * vtTanNext < COS_EDGE_LIMIT) + vSharpEdges1.emplace_back( dU, ptCurr1, vtTanPrev, vtTanNext) ; } } // --- Cerco gli spigoli sul Sottotratto del secondo bordo - // Se la Curva B ha solo una sottocurva, non faccio nulla + // Se la Curva B ha più di una sottocurva, verifico l'esistena di Spigoli (Sharp Edges) if ( nCrv2 > 1) { Point3d ptCurr2 ; // Scorro le sottocurve @@ -6048,24 +6170,24 @@ ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp double dU = double( nCrv + 1) ; // Recupero le tangenti prima e dopo tale parametro Vector3d vtTanPrev ; pSubEdge2->GetPointD1D2( dU, ICurve::FROM_MINUS, ptCurr2, &vtTanPrev) ; - Vector3d vtTanAft ; pSubEdge2->GetPointD1D2( dU, ICurve::FROM_PLUS, ptCurr2, &vtTanAft) ; + Vector3d vtTanNext ; pSubEdge2->GetPointD1D2( dU, ICurve::FROM_PLUS, ptCurr2, &vtTanNext) ; // Verifico se ho uno spigolo vtTanPrev.Normalize() ; - vtTanAft.Normalize() ; - if ( vtTanPrev * vtTanAft < COS_EDGE_LIMIT) - vSharpEdges2.emplace_back( dU, ptCurr2, vtTanPrev, vtTanAft) ; + vtTanNext.Normalize() ; + if ( vtTanPrev * vtTanNext < COS_EDGE_LIMIT) + vSharpEdges2.emplace_back( dU, ptCurr2, vtTanPrev, vtTanNext) ; } } - // Se non ho alcuno spigolo, allora non faccio nulla + // Se non ho alcuno spigolo per entrambe le curve di Bordo, non faccio nulla if ( vSharpEdges1.empty() && vSharpEdges2.empty()) return true ; - // Recupero le Curve di Sincronizzazione dell'Edge 1 sull'Edge 2 + // Recupero le Curve di Sincronizzazione associate agli spigoli dal primo Bordo al secondo Bordo BIPNTVECTOR vSyncLines1 ; CalcSyncPointFromEdge( vSharpEdges1, pSubEdge2, vSharpEdges2, vSyncLines1) ; - // Recupero le Curve di Sincronizzazione dell'Edge 2 sull'Egde 1 + // Recupero le Curve di Sincronizzazione associate agli spigoli dal secondo Bordo al primo Bordo BIPNTVECTOR vSyncLines2 ; CalcSyncPointFromEdge( vSharpEdges2, pSubEdge1, vSharpEdges1, vSyncLines2) ; @@ -6078,7 +6200,7 @@ ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp }) ; }) ; - // Ordino tutte le curve in base al parametro dU della curva principale + // Ordino tutte le curve in base al parametro dU della prima curva di Bordo vector> vSyncLinesPar ; vSyncLinesPar.reserve( vSyncLines1.size() + vSyncLines2.size()) ; for ( BIPOINT& SyncLine1 : vSyncLines1) { vSyncLinesPar.emplace_back( make_pair( make_pair( SyncLine1.first, SyncLine1.second), @@ -6094,9 +6216,212 @@ ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp return SyncLineParA.second < SyncLineParB.second ; }) ; - // Restituisco il risultato - for ( auto Iter = vSyncLinesPar.begin() ; Iter != vSyncLinesPar.end() ; ++ Iter) - vSyncLines.emplace_back( Iter->first) ; + // Controllo che le linee di sincronizzazione non si intreccino e restituisco il risultato + // NB. Inserisco solo che curve che progressivamente non si intrecciano sulla seconda curva di Bordo + double dUSecondPrev = EPS_PARAM ; + for ( auto Iter = vSyncLinesPar.begin() ; Iter != vSyncLinesPar.end() ; ++ Iter) { + double dUSecondCurr ; pSubEdge2->GetParamAtPoint( ( *Iter).first.second, dUSecondCurr, TOL) ; + if ( dUSecondCurr > dUSecondPrev + EPS_SMALL) { + vSyncLines.emplace_back( Iter->first) ; + dUSecondPrev = dUSecondCurr ; + } + } + + return true ; +} + +// --------------------------------------------------------------------------- +// Calcolo delle Curve di Sync per evitare Twist +static bool +ManageTwistInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComposite* pSubEdge2, BIPNTVECTOR& vSyncLines) +{ + // Verifica validità delle curve + if ( pSubEdge1 == nullptr || ! pSubEdge1->IsValid() || pSubEdge2 == nullptr || ! pSubEdge2->IsValid()) + return false ; + + const double COS_LIMIT = cos( 50. * DEGTORAD) ; + const int NUM_SAMPLE_PNT = 20 ; + #if DEBUG_CURVATURE + vector> _vPtK1 ; + vector> _vPtK2 ; + IGeomDB* pGeomDB = GetCurrGeomDB() ; + VERIFY_GEOMDB( pGeomDB, false) + #endif + + // --- Verifico il cambiamento di Deviazione angolare tra Inizio-Fine della prima curva di Bordo + Vector3d vtS1 ; pSubEdge1->GetStartDir( vtS1) ; + Vector3d vtE1 ; pSubEdge1->GetEndDir( vtE1) ; + double dLen1 = - EPS_SMALL ; + bool bSplit1 = ( vtS1 * vtE1 < COS_LIMIT) ; + if ( bSplit1) { + // Individuo il punto a Curvatura Massima + pSubEdge1->GetLength( dLen1) ; + double dKMax1 = - INFINITO + 1, dUK1Max = - EPS_SMALL ; + Point3d ptKMax1 ; Vector3d vtTanKMax1Prev, vtTanKMax1Next ; + for ( int i = 0 ; i <= NUM_SAMPLE_PNT ; ++ i) { + // Ricavo la Lunghezza corrente e il parametro dU associato + double dCurrLen1 = Clamp( i * ( dLen1 / NUM_SAMPLE_PNT), 0., dLen1) ; + double dUCurr1 ; pSubEdge1->GetParamAtLength( dCurrLen1, dUCurr1) ; + // Calcolo la Curvatura + Point3d ptCurrSub1 ; + Vector3d vtCurrSub1Der1Prev, vtCurrSub1Der2Prev, vtCurrSub1Der1Next, vtCurrSub1Der2Next ; + pSubEdge1->GetPointD1D2( dUCurr1, ICurve::FROM_MINUS, ptCurrSub1, &vtCurrSub1Der1Prev, &vtCurrSub1Der2Prev) ; + pSubEdge1->GetPointD1D2( dUCurr1, ICurve::FROM_PLUS, ptCurrSub1, &vtCurrSub1Der1Next, &vtCurrSub1Der2Next) ; + Vector3d vtCurrSub1Der1 = Media( vtCurrSub1Der1Prev, vtCurrSub1Der1Next) ; + Vector3d vtCurrSub1Der2 = Media( vtCurrSub1Der2Prev, vtCurrSub1Der2Next) ; + double dCurrK1 = ( vtCurrSub1Der1 ^ vtCurrSub1Der2).Len() / max( EPS_SMALL, Pow( vtCurrSub1Der1.Len(), 3)) ; + // Se maggiore della massima trovata, aggiorno la massima + if ( dCurrK1 > dKMax1) { + dKMax1 = dCurrK1 ; + ptKMax1 = ptCurrSub1 ; + dUK1Max = dUCurr1 ; + vtTanKMax1Prev = vtCurrSub1Der1Prev ; + vtTanKMax1Next = vtCurrSub1Der1Next ; + } + #if DEBUG_CURVATURE + _vPtK1.emplace_back( make_pair( ptCurrSub1, dCurrK1)) ; + #endif + } + vtTanKMax1Prev.Normalize() ; + vtTanKMax1Next.Normalize() ; + // Calcolo la nuova linea di sincronizzazione + CurvaturePoint myCurvaturePoint( dUK1Max, ptKMax1, vtTanKMax1Prev, vtTanKMax1Next) ; + BIPOINT SyncLine ; + CalcSyncPointFromCurvature( myCurvaturePoint, pSubEdge2, SyncLine) ; + vSyncLines.emplace_back( SyncLine) ; + } + + // --- Verifico il cambiamento di Deviazione angolare tra Inizio-Fine del tratto per la prima curva di Bordo + Vector3d vtS2 ; pSubEdge2->GetStartDir( vtS2) ; + Vector3d vtE2 ; pSubEdge2->GetEndDir( vtE2) ; + double dLen2 = - EPS_SMALL ; + bool bSplit2 = ( vtS2 * vtE2 < COS_LIMIT) ; + if ( bSplit2) { + // Individuo il punto a Curvatura Massima + pSubEdge2->GetLength( dLen2) ; + double dKMax2 = - INFINITO + 1, dUK2Max = - EPS_SMALL ; + Point3d ptKMax2 ; Vector3d vtTanKMax2Prev, vtTanKMax2Next ; + for ( int i = 0 ; i <= NUM_SAMPLE_PNT ; ++ i) { + // Ricavo la Lunghezza corrente e il parametro dU associato + double dCurrLen2 = Clamp( i * ( dLen2 / NUM_SAMPLE_PNT), 0., dLen2) ; + double dUCurr2 ; pSubEdge2->GetParamAtLength( dCurrLen2, dUCurr2) ; + // Calcolo la Curvatura + Point3d ptCurrSub2 ; + Vector3d vtCurrSub2Der1Prev, vtCurrSub2Der2Prev, vtCurrSub2Der1Next, vtCurrSub2Der2Next ; + pSubEdge2->GetPointD1D2( dUCurr2, ICurve::FROM_MINUS, ptCurrSub2, &vtCurrSub2Der1Prev, &vtCurrSub2Der2Prev) ; + pSubEdge2->GetPointD1D2( dUCurr2, ICurve::FROM_PLUS, ptCurrSub2, &vtCurrSub2Der1Next, &vtCurrSub2Der2Next) ; + Vector3d vtCurrSub2Der1 = Media( vtCurrSub2Der1Prev, vtCurrSub2Der1Next) ; + Vector3d vtCurrSub2Der2 = Media( vtCurrSub2Der2Prev, vtCurrSub2Der2Next) ; + double dCurrK2 = ( vtCurrSub2Der1 ^ vtCurrSub2Der2).Len() / max( EPS_SMALL, Pow( vtCurrSub2Der1.Len(), 3)) ; + // Se maggiore della massima trovata, aggiorno la massima + if ( dCurrK2 > dKMax2) { + dKMax2 = dCurrK2 ; + ptKMax2 = ptCurrSub2 ; + dUK2Max = dUCurr2 ; + vtTanKMax2Prev = vtCurrSub2Der1Prev ; + vtTanKMax2Next = vtCurrSub2Der1Next ; + } + #if DEBUG_CURVATURE + _vPtK1.emplace_back( make_pair( ptCurrSub2, dCurrK2)) ; + #endif + } + vtTanKMax2Prev.Normalize() ; + vtTanKMax2Next.Normalize() ; + // Calcolo la nuova linea di sincronizzazione + CurvaturePoint myCurvaturePoint( dUK2Max, ptKMax2, vtTanKMax2Prev, vtTanKMax2Next) ; + BIPOINT SyncLine ; + CalcSyncPointFromCurvature( myCurvaturePoint, pSubEdge1, SyncLine) ; + // !<-- Inverto la curva di Sincronizzaione per coerenza con la prima -->! + swap( SyncLine.first, SyncLine.second) ; + vSyncLines.emplace_back( SyncLine) ; + } + + #if DEBUG_CURVATURE // Curvatura minima -> AQUA | Curvatura massima -> ORANGE + auto [itMin1, itMax1] = std::minmax_element( _vPtK1.begin(), _vPtK1.end(), [](auto const& A, auto const& B) { + return A.second < B.second ; + }) ; + double _dK1Max = itMax1->second ; + double _dK1Min = itMin1->second ; + for ( int i = 0 ; i < ssize( _vPtK1) ; ++ i) { + double _t = 0. ; + if ( _dK1Max > _dK1Min) + _t = ( _vPtK1[i].second - _dK1Min) / ( _dK1Max - _dK1Min) ; + double _dh = 30 - 180. ; // AQUA -> HSV( 180, 1, 1) | ORANGE -> HSV( 30, 1, 1) + if ( _dh > 180.) _dh -= 360.0 ; + if ( _dh < -180.0) _dh += 360.0 ; + double _h = 180. + _t * _dh ; + if ( _h < 0.0) _h += 360.0 ; + if ( _h >= 360.0) _h -= 360.0 ; + PtrOwner _ptC( CreateGeoPoint3d()) ; _ptC->Set( _vPtK1[i].first) ; + int _nId = pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, Release( _ptC)) ; + pGeomDB->SetMaterial( _nId, Color( GetColorFromHSV( HSV( _h, 1., 1.)))) ; + } + auto [itMin2, itMax2] = std::minmax_element( _vPtK2.begin(), _vPtK2.end(), [](auto const& A, auto const& B) { + return A.second < B.second ; + }) ; + double _dK2Max = itMax2->second ; + double _dK2Min = itMin2->second ; + for ( int i = 0 ; i < ssize( _vPtK2) ; ++ i) { + double _t = 0. ; + if ( _dK2Max > _dK2Min) + _t = ( _vPtK2[i].second - _dK2Min) / ( _dK2Max - _dK2Min) ; + double _dh = 30 - 180. ; // CYAN -> HSV( 180, 1, 1) | ORANGE -> HSV( 30, 1, 1) + if ( _dh > 180.) _dh -= 360.0 ; + if ( _dh < -180.0) _dh += 360.0 ; + double _h = 180. + _t * _dh ; + if ( _h < 0.0) _h += 360.0 ; + if ( _h >= 360.0) _h -= 360.0 ; + PtrOwner _ptC( CreateGeoPoint3d()) ; _ptC->Set( _vPtK2[i].first) ; + int _nId = pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, Release( _ptC)) ; + pGeomDB->SetMaterial( _nId, Color( GetColorFromHSV( HSV( _h, 1., 1.)))) ; + } + #endif + + // Se non ho alcuna linea di sincronizzazione, non faccio nulla + if ( vSyncLines.empty()) + return true ; + + const double TOL = 250. * EPS_SMALL ; + + // Elimino tutte le curve di Sincronizzazione a ridosso degli estremi della Quadrangolazione + // NB. Risultano ridondanti, creano SottoQuadrangolazioni molto strette o possono Inclinare eccssivamente l'Utensile + // NB. Così facendo evito anche di creare curve di Sync lungo le diagonali della Quadrangolazione + const double LEN_TOL = 2. ; + for ( int i = 0 ; i < ssize( vSyncLines) ; ++ i) { + double dSyncLen1 ; pSubEdge1->GetLengthAtPoint( vSyncLines[i].first, dSyncLen1, TOL) ; + bool bErase = ( dSyncLen1 < max( LEN_TOL, 0.2 * dLen1) || dSyncLen1 > min( dLen1 - LEN_TOL, 0.8 * dLen1)) ; + if ( ! bErase) { + double dSyncLen2 ; pSubEdge2->GetLengthAtPoint( vSyncLines[i].second, dSyncLen2, TOL) ; + bErase = ( dSyncLen2 < max( LEN_TOL, 0.2 * dLen2) || dSyncLen2 > min( dLen2 - LEN_TOL, 0.8 * dLen2)) ; + } + if ( bErase) { + vSyncLines.erase( vSyncLines.begin() + i) ; + -- i ; + } + } + if ( vSyncLines.empty()) + return true ; + + // Se due curve di Sincronizzazione rimaste + if ( ssize( vSyncLines) == 2) { + // Se si sovrappongono, ne elimino una + if ( AreSamePointEpsilon( vSyncLines[0].first, vSyncLines[1].first, LEN_TOL) && + AreSamePointEpsilon( vSyncLines[0].second, vSyncLines[1].second, LEN_TOL)) + vSyncLines.pop_back() ; + } + // Se due curve di Sincronizzazione rimaste + if ( ssize( vSyncLines) == 2) { + // Ordino tutte le linee di sincronizzazione in base al parametro dU della curva principale + double dU0 ; pSubEdge1->GetParamAtPoint( vSyncLines[0].first, dU0, TOL) ; + double dU1 ; pSubEdge1->GetParamAtPoint( vSyncLines[1].first, dU1, TOL) ; + if ( dU0 > dU1 - EPS_SMALL) + swap( vSyncLines[0], vSyncLines[1]) ; + // Verifico che le curve non si intreccino + pSubEdge2->GetParamAtPoint( vSyncLines[0].second, dU0, TOL) ; + pSubEdge2->GetParamAtPoint( vSyncLines[1].second, dU1, TOL) ; + if ( dU0 > dU1 - EPS_SMALL) + vSyncLines.pop_back() ; // lascio solo la prima... ( bisognerebbe scegliere quale lasciare con un criterio migliore ?) + } return true ; } @@ -6104,14 +6429,29 @@ ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp //---------------------------------------------------------------------------- bool SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, double dSampleLen) +{ + BIPNTVECTOR vSyncLines ; + return CreateSmoothRuledByTwoCurves( pCurve0, pCurve1, dSampleLen, vSyncLines) ; +} + +//---------------------------------------------------------------------------- +bool +SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, double dSampleLen, BIPNTVECTOR& vSyncLines) { // converto in bezier le curve iniziali - PtrOwner pCrvEdge1( GetCurveBezier( CurveToBezierCurve( pCurve0, 3, false))) ; - PtrOwner pCrvEdge2( GetCurveBezier( CurveToBezierCurve( pCurve1, 3, false))) ; + PtrOwner pCrvEdge1( CurveToBezierCurve( pCurve0, 3, false)) ; + PtrOwner pCrvEdge2( CurveToBezierCurve( pCurve1, 3, false)) ; if ( IsNull( pCrvEdge1) || IsNull( pCrvEdge2)) return false ; + #if SAVEPACEDISO + vGeo.clear() ; + vCol.clear() ; + #endif + + // Verifico che la distanza di campionamento sia ammissibile + double dSampleDist = Clamp( dSampleLen, 2., 30.) ; // 20.0 sembra un passo di campionamento ideale // Recupero parametri iniziali double dLen1 ; pCrvEdge1->GetLength( dLen1) ; double dLen2 ; pCrvEdge2->GetLength( dLen2) ; @@ -6124,10 +6464,9 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p Point3d ptPrev1, ptCurr1 ; pCrvEdge1->GetStartPoint( ptPrev1) ; Point3d ptPrev2, ptCurr2 ; pCrvEdge2->GetStartPoint( ptPrev2) ; Vector3d vtCurr1 = V_NULL, vtCurr2 = V_NULL ; - BIPNTVECTOR vEdgeSyncLines ; - while ( dLenPrev1 + dSampleLen < dLen1 - EPS_ZERO) { + while ( dLenPrev1 + dSampleDist < dLen1 - EPS_ZERO) { // Recupero dU, Point3d e dLen corrente sul primo bordo, per un incremento del passo di campionamento - dLenCurr1 = Clamp( dLenPrev1 + dSampleLen, 0., dLen1) ; + dLenCurr1 = Clamp( dLenPrev1 + dSampleDist, 0., dLen1) ; pCrvEdge1->GetParamAtLength( dLenCurr1, dUCurr1) ; pCrvEdge1->GetPointD1D2( dUCurr1, ICurve::FROM_MINUS, ptCurr1, &vtCurr1) ; vtCurr1.Normalize() ; @@ -6186,7 +6525,7 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p } // Verifico di non essermi allontanato troppo double dLen ; pCrvEdge2->GetLengthAtParam( dUCurr2, dLen) ; - bOkPlane = ( dLen < dLenPrev2 + 2. * dSampleLen) ; + bOkPlane = ( dLen < dLenPrev2 + 2. * dSampleDist) ; } if ( ! bOkPlane) { // --- Altrimenti, cerco il punto a minima distanza @@ -6196,11 +6535,11 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p // Verifico di non essermi allontanato troppo if ( bOkMinDist) { double dLen ; pCrvEdge2->GetLengthAtParam( dUCurr2, dLen) ; - bOkMinDist = ( dLen < dLenPrev2 + 2. * dSampleLen) ; + bOkMinDist = ( dLen < dLenPrev2 + 2. * dSampleDist) ; } if ( ! bOkMinDist) { // --- Aumento la distanza corrente del passo di campionamento - double dLen = Clamp( dLenPrev2 + dSampleLen, 0., dLen2) ; + double dLen = Clamp( dLenPrev2 + dSampleDist, 0., dLen2) ; pCrvEdge2->GetParamAtLength( dLen, dUCurr2) ; } } @@ -6213,10 +6552,10 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p // Verifico se le direzioni tangenti sono tra di loro circa parallele const double COS_ANG_TOL = cos( 15. * DEGTORAD) ; if ( vtCurr1 * vtCurr2 < COS_ANG_TOL) { - // Se fuori dalla tolleranza, recupero il miglior versore tangente sul secondo bordo nell'intervallo successivo di lunghezza ( 2. * dSampleLen) + // Se fuori dalla tolleranza, recupero il miglior versore tangente sul secondo bordo nell'intervallo successivo di lunghezza ( 2. * dMyDist) pCrvEdge2->GetLengthAtPoint( ptCurr2, dLenCurr2) ; - double dLimInfLen2 = Clamp( dLenCurr2 - dSampleLen, dLenPrev2, dLen2) ; - double dLimSupLen2 = Clamp( dLenCurr2 + dSampleLen, dLenPrev2, dLen2) ; + double dLimInfLen2 = Clamp( dLenCurr2 - dSampleDist, dLenPrev2, dLen2) ; + double dLimSupLen2 = Clamp( dLenCurr2 + dSampleDist, dLenPrev2, dLen2) ; // [Controllo migliorabile, magari mendiante metodo di bisezione (?)] const int NUM_STEP = 20 ; double dMinCos = - 1. - EPS_ZERO ; @@ -6246,33 +6585,85 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p pGeomDB->AddGeoObj( GDB_ID_NULL, nLay2, Release( vtGeo2)) ; #endif - // debug - //// Inserisco le curve di sincronizzazione nel Layer di destinazine - // PtrOwner pLine( CreateCurveLine()) ; pLine->Set( ptCurr1, ptCurr2) ; - // pGeomDB->AddGeoObj( GDB_ID_NULL, nDestGrp, Release( pLine)) ; +#if SAVEPACEDISO + // Inserisco le curve di sincronizzazione nel Layer di destinazine + PtrOwner pLine( CreateCurveLine()) ; pLine->Set( ptCurr1, ptCurr2) ; + vGeo.push_back( Release( pLine)) ; + vCol.push_back( LIME) ; +#endif + vSyncLines.emplace_back( ptCurr1, ptCurr2) ; - // --- Analisi degli spigoli all'interno della Quadrangolazione --- - // Perchè si fa questa cosa ? Perchè parametrizzando per la lunghezza i SottoTratti ricavati, non è sempre detto - // che uno spigolo di una curva sia sincronizzato con lo spigolo di un'altra ( se questi esistono)... Pertanto - // la Bezier Ruled ricavata non è detto che sia in grado di approssimare lo spigolo correttamente, potrebbe sdondarlo + // --- Analisi degli spigoli all'interno della Quadrangolazione corrente --- + // NB. Non è sempre detto che uno spigolo di una curva sia sincronizzato con lo spigolo di un'altra ( se questi esistono)... + // Pertanto la Bezier Ruled ricavata non è detto che sia in grado di approssimare lo spigolo correttamente, potrebbe sdondarlo PtrOwner pCrvQuad1( ConvertCurveToComposite( pCrvEdge1->CopyParamRange( dUPrev1, dUCurr1))) ; PtrOwner pCrvQuad2( ConvertCurveToComposite( pCrvEdge2->CopyParamRange( dUPrev2, dUCurr2))) ; + BIPNTVECTOR vEdgeSyncLines ; ManageEdgesInQuadrangulation( pCrvQuad1, pCrvQuad2, vEdgeSyncLines) ; +#if SAVEPACEDISO + for ( int i = 0 ; i < ssize( vEdgeSyncLines) ; ++ i) { + PtrOwner pLine( CreateCurveLine()) ; pLine->Set( vEdgeSyncLines[i].first, vEdgeSyncLines[i].second) ; + vGeo.push_back( Release( pLine)) ; + vCol.push_back( GREEN) ; + } +#endif - //debug - //for ( int i = 0 ; i < ssize( vEdgeSyncLines) ; ++ i) { - // PtrOwner pLine( CreateCurveLine()) ; pLine->Set( vEdgeSyncLines[i].first, vEdgeSyncLines[i].second) ; - // int nNewId = pGeomDB->AddGeoObj( GDB_ID_NULL, nDestGrp, Release( pLine)) ; - // pGeomDB->SetMaterial( nNewId, GREEN) ; - //} - + // --- Aggiunta di Linee di Sync all'interno della Quandrangolazione corrente --- + // Perchè parametrizzando per la lunghezza i SottoTratti ricavati, nel caso di elevate variazioni angolari tra l'inizio e la fine + // ( tra due curva di Sync) si potrebbero generare delle torsioni non volute + // -->! NB. Queste linee vengono create considerando le SubQuadrangolazioni con gli Spigoli. !<-- + BIPNTVECTOR vTwistSyncLines ; + if ( vEdgeSyncLines.empty()) + ManageTwistInQuadrangulation( pCrvQuad1, pCrvQuad2, vTwistSyncLines) ; + else { + BIPOINT SyncLinePrev, SyncLineNext ; + pCrvQuad1->GetStartPoint( SyncLinePrev.first) ; + pCrvQuad2->GetStartPoint( SyncLinePrev.second) ; + for ( int i = 0 ; i < ssize( vEdgeSyncLines) ; ++ i) { + if ( i == ssize( vEdgeSyncLines) - 1) { + pCrvQuad1->GetEndPoint( SyncLineNext.first) ; + pCrvQuad2->GetEndPoint( SyncLineNext.second) ; + } + else { + SyncLineNext.first = vEdgeSyncLines[i].first ; + SyncLineNext.second = vEdgeSyncLines[i].second ; + } + // Recupero i parametri correnti + double dUS1, dUE1, dUS2, dUE2 ; + pCrvQuad1->GetParamAtPoint( SyncLinePrev.first, dUS1) ; + pCrvQuad2->GetParamAtPoint( SyncLinePrev.second, dUS2) ; + pCrvQuad1->GetParamAtPoint( SyncLineNext.first, dUE1) ; + pCrvQuad2->GetParamAtPoint( SyncLineNext.second, dUE2) ; + PtrOwner pCrvSubQuad1( ConvertCurveToComposite( pCrvQuad1->CopyParamRange( dUS1, dUE1))) ; + PtrOwner pCrvSubQuad2( ConvertCurveToComposite( pCrvQuad2->CopyParamRange( dUE1, dUE2))) ; + ManageTwistInQuadrangulation( pCrvSubQuad1, pCrvSubQuad2, vTwistSyncLines) ; + // Aggiorno i parametri + SyncLinePrev = SyncLineNext ; + } + } + // aggiungo le nuove curve ( non importa che siano in ordine di parametro) + vSyncLines.insert( vSyncLines.end(), vEdgeSyncLines.begin(), vEdgeSyncLines.end()) ; + vSyncLines.insert( vSyncLines.end(), vTwistSyncLines.begin(), vTwistSyncLines.end()) ; +#if SAVEPACEDISO + for ( int i = 0 ; i < ssize( vTwistSyncLines) ; ++ i) { + PtrOwner pLine( CreateCurveLine()) ; pLine->Set( vTwistSyncLines[i].first, vTwistSyncLines[i].second) ; + vGeo.push_back( Release( pLine)) ; + vCol.push_back( OLIVE) ; + } +#endif // Aggiorno i parametri ptPrev1 = ptCurr1 ; dUPrev1 = dUCurr1 ; dLenPrev1 = dLenCurr1 ; ptPrev2 = ptCurr2 ; dUPrev2 = dUCurr2 ; dLenPrev2 = dLenCurr2 ; } - return CreateByIsoParamSet( pCrvEdge1, pCrvEdge2, vEdgeSyncLines) ; + #if SAVEPACEDISO + SaveGeoObj( vGeo, vCol, "D:\\Temp\\bezier\\ruled\\trimming\\smooth.nge") ; + #endif + + + // devo riempire vSyncLines + return CreateByIsoParamSet( pCrvEdge1, pCrvEdge2, vSyncLines) ; } //---------------------------------------------------------------------------- diff --git a/SurfBezier.h b/SurfBezier.h index 7db854f..c765695 100644 --- a/SurfBezier.h +++ b/SurfBezier.h @@ -155,6 +155,7 @@ class SurfBezier : public ISurfBezier, public IGeoObjRW bool SwapParameters( void) ; bool LimitSurfToTrimmedRegion( void) override ; bool CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, double dSampleLen) override ; + bool CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, double dSampleLen, BIPNTVECTOR& vSyncLines) override ; public : // IGeoObjRW int GetNgeId( void) const override ; diff --git a/Trimming.cpp b/Trimming.cpp index 8e6d286..7e187f4 100644 --- a/Trimming.cpp +++ b/Trimming.cpp @@ -42,7 +42,6 @@ #include // -------------------------- Debug -------------------------------------------- -#define DEBUG 0 #define DEBUG_BASIC_BORDERS 0 #define DEBUG_CHAIN_CURVES 0 #define DEBUG_ANG_APPROX 0 @@ -63,11 +62,12 @@ #define DEBUG_EDGES 0 #define DEBUG_SHAPE_STM 0 #define DEBUG_HOLES 0 +#define DEBUG_SMOOTH_CURVATURE 1 #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 + 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" @@ -284,58 +284,6 @@ GetPointSetByAngTol( const PolyLine& PL, double dAngTol, POLYLINEVECTOR& vPL) return true ; } -//----------------------------------------------------------------------------- -// Funzione per spezzare una curva compo in diversi tratti in corrispondenza -// di cambi di direzione maggiori della tolleranza angolare passata -static bool -SplitCurveCompoByAngTol( const ICurveComposite* pCC, double dAngTol, ICRVCOMPOPOVECTOR& vCC) -{ - int nCurves = pCC->GetCurveCount() ; - vCC.emplace_back( CreateCurveComposite()) ; - vCC.back()->AddCurve( pCC->GetCurve(0)->Clone()) ; - // Cos della tolleranza angolare massima - double dCosTol = cos( dAngTol * DEGTORAD) ; - - for ( int nC = 0 ; nC < nCurves - 2; ++ nC) { - // Recupero l'angolo tra la fine della curva corrente e l'inizio della successiva - const ICurve* pCrvCurr = pCC->GetCurve( nC) ; - const ICurve* pCrvNext = pCC->GetCurve( nC + 1) ; - Vector3d vtCurrEnd ; pCrvCurr->GetEndDir( vtCurrEnd) ; - Vector3d vtNextStart ; pCrvNext->GetStartDir( vtNextStart) ; - // Calcolo il Coseno tra i due versori - double dCos = vtCurrEnd * vtNextStart ; - // Se dentro alla tolleranza, allora i punti apparterranno alla stessa curva - if ( dCos > dCosTol) { - // Aggiungo la curva - vCC.back()->AddCurve( pCrvNext->Clone()) ; - } - // Se tratto al di fuori della tolleranza, devo definire una nuova curva - else { - vCC.emplace_back( CreateCurveComposite()) ; - vCC.back()->AddCurve( pCrvNext->Clone()) ; - } - } - - // Se curva originale chiusa - if ( pCC->IsClosed() && ssize( vCC) > 1) { - // Se ho più tratti, potrei riunire il primo con l'ultimo - const ICurve* pCrvFirst = pCC->GetCurve( 0) ; - const ICurve* pCrvLast = pCC->GetCurve( nCurves - 1) ; - Vector3d vtFirstStart ; pCrvFirst->GetEndDir( vtFirstStart) ; - Vector3d vtLastEnd ; pCrvLast->GetStartDir( vtLastEnd) ; - // Calcolo il Coseno tra i due versori - double dCos = vtFirstStart * vtLastEnd ; - // Se dentro alla tolleranza, allora i punti appartengono alla stessa curva - if ( dCos > dCosTol) { - // Aggiungo la curva - vCC.back()->AddCurve( Release( vCC.front())) ; - vCC.erase( vCC.begin()) ; - } - } - - return true ; -} - ////----------------------------------------------------------------------------- //// Funzione che approssima la curva di bordo per la costruzione della Bezier Ruled mediante //// Patches di curve di Bezier @@ -3959,26 +3907,26 @@ GetTrimmingSurfBzSyncPoints( const ICurve* pCrvEdge1, const ICurve* pCrvEdge2, #endif // Definisco la superficie di Bezier rigata - PtrOwner pSBzRuled( GetBasicSurfBezier( GetSurfBezierRuled( pCompoEdge1, pCompoEdge2, ISurfBezier::RLT_B_MINDIST_PLUS, dMyLinTol))) ; + PtrOwner pSBzRuled( GetBasicSurfBezier( GetSurfBezierRuledSmooth( pCompoEdge1, pCompoEdge2, vSyncPoints, 10))) ; 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)) ; - } - } + //// 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") ; @@ -4219,7 +4167,8 @@ GetTrimmingRuledBezier( const CISURFPVECTOR& vSurf, const ICurve* pCrvEdge1, // Se non ho punti di controllo forzati if ( vSyncPoints.empty()) { - pSurfBz.Set( GetSurfBezierRuledSmooth( pCompoEdge1, pCompoEdge2, 10)) ; + BIPNTVECTOR vSyncLines ; + pSurfBz.Set( GetSurfBezierRuledSmooth( pCompoEdge1, pCompoEdge2, vSyncLines, 10)) ; if ( IsNull( pSurfBz) || ! pSurfBz->IsValid()) { LOG_ERROR( GetEGkLogger(), "Error in Trimming : Ruled Bezier invalid") ; return nullptr ; @@ -4291,6 +4240,10 @@ GetTrimmingRuledBezier( const CISURFPVECTOR& vSurf, const ICurve* pCrvEdge1, } } + //////debug + //RegolarizeBordersLocally(pCrvEdge1,pCrvEdge2, pSurfBz, 0.2) ; + //return nullptr ; + return ( IsNull( pSurfBz) || ! pSurfBz->IsValid() ? nullptr : Release( pSurfBz)) ; } @@ -4950,3 +4903,160 @@ GetTrimmingHoleBorders( const CISURFPVECTOR& vpSurf, const Point3d& ptRef, doubl return true ; } + + +struct PntInfo{ + Point3d pt ; + int nSubCurve ; + int nPnt ; + double dDist ; + Vector3d vtN ; + int nSide ; + PntInfo( const Point3d& _pt, int _nSubCrv, int _nPnt, double _dDist, const Vector3d& _vtN, int _nSide) : + pt( _pt), nSubCurve( _nSubCrv), nPnt( _nPnt), dDist( _dDist), vtN( _vtN), nSide( _nSide) {;} +}; +typedef vector PNTINFOVECTOR ; + +//------------------------------------------------------------------------------ +// Funzione per la regolarizzazione delle curve di bordo di una lavorazione di trim +// Le curve vengono modificate entro una data tolleranza, in modo che +bool +RegolarizeBordersLocally( 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 + const Point3d& ptS1 = bpIsoStart.first ; + const Point3d& ptS2 = bpIsoEnd.first ; + const Point3d& ptE1 = bpIsoStart.second ; + const Point3d& ptE2 = bpIsoEnd.second ; + int nDegU, nDegV, nSpanU, nSpanV ; + bool bRat, bTrimmed ; + pSurfBz->GetInfo( nDegU, nDegV, nSpanU, nSpanV, bRat, bTrimmed) ; + if ( nDegU != 3) + return false ; + // individuo quali isocurve sono state indicate come inizio e fine + PtrOwner pCrv1( pSurfBz->GetSingleEdge3D( false, 2)) ; + PtrOwner pCrv2( pSurfBz->GetSingleEdge3D( false, 0)) ; + double dParS1 = -1 ; double dParS2 = -1 ; + if ( ! pCrv1->GetParamAtPoint( ptS1, dParS1) || ! pCrv1->GetParamAtPoint( ptS2, dParS2)) + return false ; + double dParE1 = -1 ; double dParE2 = -1 ; + if ( ! pCrv2->GetParamAtPoint( ptE1, dParE1) || ! pCrv2->GetParamAtPoint( ptE2, dParE2)) + return false ; + int nUS1 = int ( dParS1) ; + int nUS2 = int ( dParS2) ; + bool bInverted = false ; + if ( nUS1 > nUS2) { + swap( nUS1, nUS2) ; + swap( dParE1, dParE2) ; + bInverted = true ; + } + + // scorro le isocurve a partire dallo start e dell'end della zona indicata + // controllo il twist rispetto alla isocurva precedente + Point3d ptPrevS = ptS1 ; + Point3d ptPrevE = ! bInverted ? bpIsoStart.second : bpIsoEnd.second ; + Vector3d vtIsoPrev = ptPrevE - ptPrevS ; vtIsoPrev.Normalize() ; + Point3d ptBez ; Vector3d vtNPrev ; + pSurfBz->GetPointNrmD1D2( nUS1, 0.5, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBez, vtNPrev) ; + 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 ptThird1Curr = pSurfBz->GetControlPoint( i - 1, 0, &bOk) ; + Point3d ptThird2Curr = 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 ptCurrS = pSurfBz->GetControlPoint( i, 0, &bOk) ; + Point3d ptCurrE = pSurfBz->GetControlPoint( i, 1, &bOk) ; + Vector3d vtIsoCurr = ptCurrE - ptCurrS ; + double dDist = vtIsoCurr.Len() ; + vtIsoCurr.Normalize() ; + Vector3d vtDirPrev = vtIsoPrev ^ vtNPrev ; + Vector3d vtCurrInPlane = OrthoCompo( vtIsoCurr, vtDirPrev) ; + bool bDet = false ; + double dAng = 0 ; vtIsoPrev.GetRotation( vtCurrInPlane, vtDirPrev, dAng, bDet) ; + if ( abs( dAng) > 0) { + // se l'isocurva di separazione dalla patch successiva è torta rispetto alla precedente + // allora prendo il penultimo punto della curva corrente, l'ultimo e il secondo della prossima e li sposto lungo la normale della superficie + dDist *= dAng * DEGTORAD / 2 ; + ptThird1Curr += vtNPrev * dDist ; + ptCurrS += vtNPrev * dDist ; + ptSecond1Next += vtNPrev * dDist ; + ptThird2Curr -= vtNPrev * dDist ; + ptCurrE -= vtNPrev * dDist ; + ptSecond2Next -= vtNPrev * dDist ; + } + vPnt0.push_back( ptThird1Curr) ; + vPnt0.push_back( ptCurrS) ; + vPnt0.push_back( ptSecond1Next) ; + + vPnt1.push_back( ptThird2Curr) ; + vPnt1.push_back( ptCurrE) ; + vPnt1.push_back( ptSecond2Next) ; + + vtIsoPrev = ptCurrE - ptCurrS ; vtIsoPrev.Normalize() ; + vtNPrev = ( ptSecond1Next - ptCurrS) ^ vtIsoPrev ; vtNPrev.Normalize() ; + } + + 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)) ; + } + + // controllo di essere rimasto in tolleranza + PtrOwner pCrvOrig1( pCrv1->CopyParamRange( nUS1, dParE1)) ; + PtrOwner pCrvOrig2( pCrv2->CopyParamRange( nUS2, dParE2)) ; + double dErr = 0 ; + CalcApproxError( pCrvOrig1, pCC1, dErr, 20) ; + if ( dErr > dTol) + return false ; + CalcApproxError( pCrvOrig2, pCC2, dErr, 20) ; + if ( dErr > dTol) + return false ; + + // controllo di non aver creato dei cambi di concavità + // se ne trovo allora ruoto i terzetti attorno alla nuova isocurva + ////////////////// DA IMPLEMENTARE (prendendo dalla versione precedente della Regolarize) + + // aggiorno i punti di controllo della superficie di bezier + for ( int i = 0 ; i < ssize( vPnt0) ; ++i) { + pSurfBz->SetControlPoint( nUS1 + i, 0, vPnt0[i]) ; + pSurfBz->SetControlPoint( nUS1 + i, 1, vPnt1[i]) ; + } + #if DEBUG_SMOOTH_CURVATURE + VT.push_back( Release(pCC1)) ; + VT.push_back( Release(pCC2)) ; + VT.push_back( Release(pCrv1)) ; + VT.push_back( Release(pCrv2)) ; + VT.push_back(pSurfBz->Clone()); + SaveGeoObj( VT, "C:\\Temp\\bezier\\ruled\\smoothness\\regolarized.nge") ; + #endif + + return true ; +}