diff --git a/SbzFromCurves.cpp b/SbzFromCurves.cpp index 09ddf2e..c68f5ea 100644 --- a/SbzFromCurves.cpp +++ b/SbzFromCurves.cpp @@ -726,7 +726,43 @@ GetSurfBezierRuled( const ICurve* pCurve1, const ICurve* pCurve2, int nType, dou // creo e setto la superficie trimesh PtrOwner pSbz( CreateBasicSurfBezier()) ; - if ( IsNull( pSbz) || ! pSbz->CreateSmoothByTwoCurves( pCC1, pCC2, 10)) + if ( IsNull( pSbz) || ! pSbz->CreateByTwoCurves( pCC1, pCC2, nType)) + return nullptr ; + + // restituisco la superficie + return Release( pSbz) ; +} + +//------------------------------------------------------------------------------- +ISurfBezier* +GetSurfBezierRuledSmooth( const ICurve* pCurve1, const ICurve* pCurve2, double dSampleLen) +{ + // verifica parametri + if ( pCurve1 == nullptr || pCurve2 == nullptr) + return nullptr ; + + // dLinTol servirà quando ci sarà la funzione ApproxWithCurveBezier + // se la curva è già una bezier singola la tengo, sennò la converto + PtrOwner pCC1( CreateCurveComposite()) ; + if ( pCurve1->GetType() != CRV_BEZIER) + pCC1->AddCurve( CurveToBezierCurve( pCurve1, 3, false)) ; + else + pCC1->AddCurve( pCurve1->Clone()) ; + if ( IsNull( pCC1) || ! pCC1->IsValid()) + return nullptr ; + + // se la curva è già una bezier singola la tengo, sennò la converto + PtrOwner pCC2( CreateCurveComposite()) ; + if ( pCurve2->GetType() != CRV_BEZIER) + pCC2->AddCurve( CurveToBezierCurve( pCurve2, 3, false)) ; + else + pCC2->AddCurve( pCurve2->Clone()) ; + if ( IsNull( pCC2) || ! pCC2->IsValid()) + return nullptr ; + + // creo e setto la superficie trimesh + PtrOwner pSbz( CreateBasicSurfBezier()) ; + if ( IsNull( pSbz) || ! pSbz->CreateSmoothRuledByTwoCurves( pCC1, pCC2, dSampleLen)) return nullptr ; // restituisco la superficie diff --git a/SurfBezier.cpp b/SurfBezier.cpp index 56f0f99..aa5436f 100644 --- a/SurfBezier.cpp +++ b/SurfBezier.cpp @@ -5896,173 +5896,272 @@ SurfBezier::CreateByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, int return true ; } -struct Edge { - Vector3d vtPrev ; - Vector3d vtCurr ; - Point3d ptCurr ; - Edge(const Vector3d& _vtPrev, const Vector3d& _vtCurr, const Point3d& _ptCurr) : - vtPrev( _vtPrev), vtCurr( _vtCurr), ptCurr( _ptCurr) {;} -}; -typedef vector EDGEVECTOR ; +struct SharpEdge { + double dParU ; + Point3d ptEdge ; + Vector3d vtTanPrev, vtTanAft ; + SharpEdge( double dU, const Point3d ptE, const Vector3d& vtTP, const Vector3d& vtTA) + : dParU( dU), ptEdge( ptE), vtTanPrev( vtTP), vtTanAft( vtTA) {} +} ; +typedef vector SHARPEDGEVECTOR ; -//---------------------------------------------------------------------------- -bool -SurfBezier::CreateSmoothByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, double dSampleLen) +static bool +CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite* pCompoSubEdge, const SHARPEDGEVECTOR& vSharpSubEdge, + BIPNTVECTOR& vSyncLines) { - // converto in bezier la curva iniziale - CurveComposite CrvU0 ; - // se la curva è già una bezier singola la tengo, sennò la converto - if ( pCurve0->GetType() != CRV_BEZIER) { - // se è una compo controllo che siano già tutte bezier - bool bAlreadyBez = true ; - if ( pCurve0->GetType() == CRV_COMPO) { - const ICurveComposite* pCC0 = GetBasicCurveComposite( pCurve0) ; - for ( int i = 0 ; i < pCC0->GetCurveCount() ; ++i) { - if ( pCC0->GetCurve( i)->GetType() != CRV_BEZIER) { - bAlreadyBez = false ; - break ; - } - } - } - else - bAlreadyBez = false ; - if ( ! bAlreadyBez) - CrvU0.AddCurve( CurveToBezierCurve( pCurve0)) ; - else - CrvU0.AddCurve( pCurve0->Clone()) ; - } - else - CrvU0.AddCurve( pCurve0->Clone()) ; - if ( ! CrvU0.IsValid()) - return false ; - // recupero span e grado nel parametro U - int nSpanU0 = int( CrvU0.GetCurveCount()) ; - int nDegU0 = ( GetCurveBezier( CrvU0.GetCurve(0)))->GetDegree() ; - bool bRat0 = (GetCurveBezier( CrvU0.GetCurve(0)))->IsRational() ; - // se la curva è già una bezier singola la tengo, sennò la converto - CurveComposite CrvU1 ; - // se la curva è già una bezier singola la tengo, sennò la converto - if ( pCurve1->GetType() != CRV_BEZIER) { - // se è una compo controllo che siano già tutte bezier - bool bAlreadyBez = true ; - if ( pCurve1->GetType() == CRV_COMPO) { - const ICurveComposite* pCC1 = GetBasicCurveComposite( pCurve1) ; - for ( int i = 0 ; i < pCC1->GetCurveCount() ; ++i) { - if ( pCC1->GetCurve( i)->GetType() != CRV_BEZIER) { - bAlreadyBez = false ; - break ; - } - } - } - else - bAlreadyBez = false ; - if ( ! bAlreadyBez) - CrvU1.AddCurve( CurveToBezierCurve( pCurve1)) ; - else - CrvU1.AddCurve( pCurve1->Clone()) ; - } - else - CrvU1.AddCurve( pCurve1->Clone()) ; - // recupero span e grado nel parametro U - int nSpanU1 = int( CrvU1.GetCurveCount()) ; - int nDegU1 = ( GetCurveBezier( CrvU1.GetCurve(0)))->GetDegree() ; - bool bRat1 = (GetCurveBezier( CrvU1.GetCurve(0)))->IsRational() ; - // se le due curve hanno grado diverso allora aumento il grado di quella con grado inferiore - if ( nDegU0 != nDegU1) { - while ( nDegU0 < nDegU1) { - CurveComposite CC ; - for ( int k = 0 ; k < nSpanU0 ; ++k) - CC.AddCurve( BezierIncreaseDegree( GetCurveBezier( CrvU0.GetCurve( k)))) ; - ++nDegU0 ; - CrvU0 = CC ; - } - while ( nDegU0 > nDegU1) { - CurveComposite CC ; - for ( int k = 0 ; k < nSpanU0 ; ++k) - CC.AddCurve( BezierIncreaseDegree( GetCurveBezier( CrvU1.GetCurve( k)))) ; - ++nDegU1 ; - CrvU1 = CC ; - } - } - // omogenizzo la razionalità - if ( bRat0 != bRat1) { - if ( bRat0) { - CurveComposite CC ; - for ( int k = 0 ; k < nSpanU0 ; ++k) { - const ICurveBezier* pCrvBez = GetCurveBezier( CrvU0.GetCurve( k)) ; - CC.AddCurve( EditBezierCurve( pCrvBez, nDegU0, false)) ; - } - CrvU0 = CC ; - bRat0 = false ; - } - if ( bRat1) { - CurveComposite CC ; - for ( int k = 0 ; k < nSpanU1 ; ++k) { - const ICurveBezier* pCrvBez = GetCurveBezier( CrvU1.GetCurve( k)) ; - CC.AddCurve( EditBezierCurve( pCrvBez, nDegU0, false)) ; - } - CrvU1 = CC ; - bRat1 = false ; - } - } + vSyncLines.clear() ; - if ( nDegU0 != nDegU1) + // Verifico la validità del SubEdge su cui trovare il punto di Sincronizzazione + if ( pCompoSubEdge == nullptr || ! pCompoSubEdge->IsValid()) return false ; - int nDegU = nDegU0 ; + // Se non ho spigoli da controllare, non devo fare nulla + if ( vSharpEdge.empty()) + return true ; - // in V decido che la funzione è una patch di grado 1 - int nSpanV = 1 ; - int nDegV = 1 ; + // Scorro gli SharpEdges + double dUPrevSubEdge = 0., dUCurrSubEdge = 0. ; + for ( const SharpEdge& mySharpEdge : vSharpEdge) { - bool bIsClosed0 = CrvU0.IsClosed() ; - bool bIsClosed1 = CrvU1.IsClosed() ; - - BIPNTVECTOR vPairs ; - - // Recupero parametri iniziali - double dLen0 ; CrvU0.GetLength( dLen0) ; - double dLen1 ; CrvU1.GetLength( dLen1) ; - double dUS0, dUE0 ; CrvU0.GetDomain( dUS0, dUE0) ; - double dUS1, dUE1 ; CrvU1.GetDomain( dUS1, dUE1) ; - double dLenPrev0 = 0., dLenCurr0 = 0. ; - double dLenPrev1 = 0., dLenCurr1 = 0. ; - double dUPrev0 = 0., dUCurr0 = 0. ; - double dUPrev1 = 0., dUCurr1 = 0. ; - Point3d ptPrev0, ptCurr0 ; CrvU0.GetStartPoint( ptPrev0) ; - Point3d ptPrev1, ptCurr1 ; CrvU1.GetStartPoint( ptPrev1) ; - Vector3d vtCurr0 = V_NULL, vtCurr1 = V_NULL ; - while ( dLenPrev0 + dSampleLen < dLen0 - EPS_ZERO) { - // Recupero dU, Point3d e dLen corrente sul primo bordo, per un incremento del passo di campionamento - dLenCurr0 = Clamp( dLenPrev0 + dSampleLen, 0., dLen0) ; - CrvU0.GetParamAtLength( dLenCurr0, dUCurr0) ; - CrvU0.GetPointD1D2( dUCurr0, ICurve::FROM_MINUS, ptCurr0, &vtCurr0) ; - vtCurr0.Normalize() ; - - // --- Piano di taglio per punto a minima distanza - IntersCurvePlane ICP( CrvU1, ptCurr0, vtCurr0) ; - int nIndParCloser = - 0 ; - int nIndPointCloser = -0 ; + // 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)) ; + int nIndParCloser = -1, nIndPointCloser = -1 ; double dSqMinDist = INFINITO ; for ( int nInfo = 0 ; nInfo < ICP.GetIntersCount() ; ++ nInfo) { IntCrvPlnInfo aInfo ; - if ( ICP.GetIntCrvPlnInfo( nInfo, aInfo) && aInfo.Ici[0].dU > dUPrev1) { - if ( nIndParCloser == -0) + if ( ICP.GetIntCrvPlnInfo( nInfo, aInfo) && aInfo.Ici[0].dU > dUPrevSubEdge) { + if ( nIndParCloser == -1) nIndParCloser = nInfo ; - double dSqDist = SqDist( ptCurr0, aInfo.Ici[0].ptI) ; + double dSqDist = SqDist( mySharpEdge.ptEdge, aInfo.Ici[0].ptI) ; if ( dSqDist < dSqMinDist) { dSqMinDist = dSqDist ; nIndPointCloser = nInfo ; } } } - bool bOkPlane = ( nIndParCloser != -0 && nIndPointCloser != -0) ; + 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) ; - dUCurr1 = aInfo.Ici[0].dU ; + 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) ; + 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. ; + } + } + } + + // Verifico se tale parametro può essere avvicinato ad uno Spigolo presente sulla curva + if ( ! vSharpSubEdge.empty()) { + const double EDGE_LEN_TOL = 5. ; + for ( const SharpEdge& mySharpSubEdge : vSharpSubEdge) { + // Se la linea di sincronizzazione è progressiva in parametro (ovvero non si attorciglia) + if ( mySharpSubEdge.dParU > dUPrevSubEdge) { + double dLenSub ; pCompoSubEdge->GetLengthAtParam( mySharpSubEdge.dParU, dLenSub) ; + double dLenSync ; pCompoSubEdge->GetLengthAtParam( dUCurrSubEdge, dLenSync) ; + // E Sufficientemente vicina ad uno Spigolo sull'altra + if ( abs( dLenSync - dLenSub) < EDGE_LEN_TOL) { + // Sposto il suo punto finale sullo Spigolo + dUCurrSubEdge = mySharpSubEdge.dParU ; + break ; // prendo la prima dato che sono ordinate per parametro + } + } + } + } + + // Definisco una nuova linea dallo spigolo corrente al parametro trovato sull'altra curva di bordo + vSyncLines.emplace_back( mySharpEdge.ptEdge, P_INVALID) ; + pCompoSubEdge->GetPointD1D2( dUCurrSubEdge, ICurve::FROM_MINUS, vSyncLines.back().second) ; + + // aggiorno i parametri + dUPrevSubEdge = dUCurrSubEdge ; + } + + return true ; +} + +// --------------------------------------------------------------------------- +// Debug [Gestione Edge in Quadrangolazioni] +static bool +ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComposite* pSubEdge2, BIPNTVECTOR& vSyncLines) +{ + // Verifica validità delle curve + if ( pSubEdge1 == nullptr || ! pSubEdge1->IsValid() || pSubEdge2 == nullptr || ! pSubEdge2->IsValid()) + return false ; + + // Recupero il numero delle curve dei Sottotratti correnti dei bordi + int nCrv1 = pSubEdge1->GetCurveCount() ; + int nCrv2 = pSubEdge2->GetCurveCount() ; + if ( nCrv1 < 2 && nCrv2 < 2) + return true ; + + const double COS_EDGE_LIMIT = cos( 35. * DEGTORAD) ; + SHARPEDGEVECTOR vSharpEdges1 ; vSharpEdges1.reserve( max( 0, nCrv1 - 1)) ; + 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 + if ( nCrv1 > 1) { + Point3d ptCurr1 ; + // Scorro le sottocurve + for ( int nCrv = 0 ; nCrv < nCrv1 - 1 ; ++ nCrv) { + // Recupero il parametro corrente sulla curva ( estremo finale) + 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) ; + // Verifico se ho uno spigolo + vtTanPrev.Normalize() ; + vtTanAft.Normalize() ; + if ( vtTanPrev * vtTanAft < COS_EDGE_LIMIT) + vSharpEdges1.emplace_back( dU, ptCurr1, vtTanPrev, vtTanAft) ; + } + } + + // --- Cerco gli spigoli sul Sottotratto del secondo bordo + // Se la Curva B ha solo una sottocurva, non faccio nulla + if ( nCrv2 > 1) { + Point3d ptCurr2 ; + // Scorro le sottocurve + for ( int nCrv = 0 ; nCrv < nCrv2 - 1 ; ++ nCrv) { + // Recupero il parametro corrente sulla curva ( estremo finale) + 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) ; + // Verifico se ho uno spigolo + vtTanPrev.Normalize() ; + vtTanAft.Normalize() ; + if ( vtTanPrev * vtTanAft < COS_EDGE_LIMIT) + vSharpEdges2.emplace_back( dU, ptCurr2, vtTanPrev, vtTanAft) ; + } + } + + // Se non ho alcuno spigolo, allora non faccio nulla + if ( vSharpEdges1.empty() && vSharpEdges2.empty()) + return true ; + + // Recupero le Curve di Sincronizzazione dell'Edge 1 sull'Edge 2 + BIPNTVECTOR vSyncLines1 ; + CalcSyncPointFromEdge( vSharpEdges1, pSubEdge2, vSharpEdges2, vSyncLines1) ; + + // Recupero le Curve di Sincronizzazione dell'Edge 2 sull'Egde 1 + BIPNTVECTOR vSyncLines2 ; + CalcSyncPointFromEdge( vSharpEdges2, pSubEdge1, vSharpEdges1, vSyncLines2) ; + + // Rimuovo eventuali linee sovrapposte ( le rimuovo da vSyncLines2) + const double TOL = 250. * EPS_SMALL ; + erase_if( vSyncLines2, [&]( const BIPOINT& SyncLine2) { + return any_of( vSyncLines1.begin(), vSyncLines1.end(), [&]( const BIPOINT& SyncLine1) { + return ( AreSamePointEpsilon( SyncLine1.first, SyncLine2.second, TOL) && + AreSamePointEpsilon( SyncLine1.second, SyncLine2.first, TOL)) ; + }) ; + }) ; + + // Ordino tutte le curve in base al parametro dU della curva principale + vector> vSyncLinesPar ; vSyncLinesPar.reserve( vSyncLines1.size() + vSyncLines2.size()) ; + for ( BIPOINT& SyncLine1 : vSyncLines1) { + vSyncLinesPar.emplace_back( make_pair( make_pair( SyncLine1.first, SyncLine1.second), + vSyncLinesPar.empty() ? 0. : vSyncLinesPar.back().second)) ; + pSubEdge1->GetParamAtPoint( SyncLine1.first, vSyncLinesPar.back().second, TOL) ; + } + for ( BIPOINT& SyncLine2 : vSyncLines2) { + vSyncLinesPar.emplace_back( make_pair( make_pair( SyncLine2.second, SyncLine2.first), + vSyncLinesPar.empty() ? 0. : vSyncLinesPar.back().second)) ; + pSubEdge1->GetParamAtPoint( SyncLine2.second, vSyncLinesPar.back().second, TOL) ; + } + sort( vSyncLinesPar.begin(), vSyncLinesPar.end(), []( const auto& SyncLineParA, const auto& SyncLineParB) { + return SyncLineParA.second < SyncLineParB.second ; + }) ; + + // Restituisco il risultato + for ( auto Iter = vSyncLinesPar.begin() ; Iter != vSyncLinesPar.end() ; ++ Iter) + vSyncLines.emplace_back( Iter->first) ; + + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, double dSampleLen) +{ + // converto in bezier le curve iniziali + PtrOwner pCrvEdge1( GetCurveBezier( CurveToBezierCurve( pCurve0, 3, false))) ; + PtrOwner pCrvEdge2( GetCurveBezier( CurveToBezierCurve( pCurve1, 3, false))) ; + + if ( IsNull( pCrvEdge1) || IsNull( pCrvEdge2)) + return false ; + + // Recupero parametri iniziali + double dLen1 ; pCrvEdge1->GetLength( dLen1) ; + double dLen2 ; pCrvEdge2->GetLength( dLen2) ; + double dUS1, dUE1 ; pCrvEdge1->GetDomain( dUS1, dUE1) ; + double dUS2, dUE2 ; pCrvEdge2->GetDomain( dUS2, dUE2) ; + double dLenPrev1 = 0., dLenCurr1 = 0. ; + double dLenPrev2 = 0., dLenCurr2 = 0. ; + double dUPrev1 = 0., dUCurr1 = 0. ; + double dUPrev2 = 0., dUCurr2 = 0. ; + 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) { + // Recupero dU, Point3d e dLen corrente sul primo bordo, per un incremento del passo di campionamento + dLenCurr1 = Clamp( dLenPrev1 + dSampleLen, 0., dLen1) ; + pCrvEdge1->GetParamAtLength( dLenCurr1, dUCurr1) ; + pCrvEdge1->GetPointD1D2( dUCurr1, ICurve::FROM_MINUS, ptCurr1, &vtCurr1) ; + vtCurr1.Normalize() ; + + #if DEBUG_SYNCLINES + PtrOwner ptGeo1( CreateGeoPoint3d()) ; ptGeo1->Set( ptCurr1) ; + PtrOwner vtGeo1( CreateGeoVector3d()) ; vtGeo1->Set( 5. * vtCurr1, ptCurr1) ; + pGeomDB->AddGeoObj( GDB_ID_NULL, nLay1, Release( ptGeo1)) ; + pGeomDB->AddGeoObj( GDB_ID_NULL, nLay1, Release( vtGeo1)) ; + #endif + + // --- Piano di taglio per punto a minima distanza + IntersCurvePlane ICP( *pCrvEdge2, ptCurr1, vtCurr1) ; + int nIndParCloser = - 1, nIndPointCloser = -1 ; + double dSqMinDist = INFINITO ; + for ( int nInfo = 0 ; nInfo < ICP.GetIntersCount() ; ++ nInfo) { + IntCrvPlnInfo aInfo ; + if ( ICP.GetIntCrvPlnInfo( nInfo, aInfo) && aInfo.Ici[0].dU > dUPrev2) { + if ( nIndParCloser == -1) + nIndParCloser = nInfo ; + double dSqDist = SqDist( ptCurr1, 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) ; + dUCurr2 = aInfo.Ici[0].dU ; } // Se gli indici sono discordi, devo scegliere quale dei due punti tenere else { @@ -6070,126 +6169,110 @@ SurfBezier::CreateSmoothByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve IntCrvPlnInfo aInfoPt, aInfoPar ; ICP.GetIntCrvPlnInfo( nIndPointCloser, aInfoPt) ; ICP.GetIntCrvPlnInfo( nIndParCloser, aInfoPar) ; - dUCurr1 = ( SqDist( ptCurr0, aInfoPt.Ici[0].ptI) < SqDist( ptCurr0, aInfoPar.Ici[0].ptI) ? + dUCurr2 = ( SqDist( ptCurr1, aInfoPt.Ici[0].ptI) < SqDist( ptCurr1, aInfoPar.Ici[0].ptI) ? aInfoPt.Ici[0].dU : aInfoPar.Ici[0].dU) ; + #if DEBUG_SYNCLINES && 0 + VT.clear() ; VC.clear() ; + VT.emplace_back( pCrvEdge1->Clone()) ; VC.emplace_back( Color( 0, 128, 255)) ; + VT.emplace_back( pCrvEdge2->Clone()) ; VC.emplace_back( Color( 0, 128, 255)) ; + PtrOwner ptCurr1Geo( CreateGeoPoint3d()) ; ptCurr1Geo->Set( ptCurr1) ; + VT.emplace_back( Release( ptCurr1Geo)) ; VC.emplace_back( BLUE) ; + PtrOwner ptPar( CreateGeoPoint3d()) ; ptPar->Set( aInfoPar.Ici[0].ptI) ; + PtrOwner ptPt( CreateGeoPoint3d()) ; ptPt->Set( aInfoPt.Ici[0].ptI) ; + VT.emplace_back( Release( ptPar)) ; VC.emplace_back( LIME) ; + VT.emplace_back( Release( ptPt)) ; VC.emplace_back( FUCHSIA) ; + SaveGeoObj( VT, VC, "C:\\Temp\\TestTrimmingPlane.nge") ; + #endif } // Verifico di non essermi allontanato troppo - double dLen ; CrvU1.GetLengthAtParam( dUCurr1, dLen) ; - bOkPlane = ( dLen < dLenPrev1 + 1. * dSampleLen) ; + double dLen ; pCrvEdge2->GetLengthAtParam( dUCurr2, dLen) ; + bOkPlane = ( dLen < dLenPrev2 + 2. * dSampleLen) ; } if ( ! bOkPlane) { // --- Altrimenti, cerco il punto a minima distanza - DistPointCurve DPC( ptCurr0, CrvU1) ; + DistPointCurve DPC( ptCurr1, *pCrvEdge2) ; int nFlag ; - bool bOkMinDist = ( DPC.GetParamAtMinDistPoint( dUPrev1, dUCurr1, nFlag) && dUCurr1 > dUPrev1) ; + bool bOkMinDist = ( DPC.GetParamAtMinDistPoint( dUPrev2, dUCurr2, nFlag) && dUCurr2 > dUPrev2) ; // Verifico di non essermi allontanato troppo if ( bOkMinDist) { - double dLen ; CrvU1.GetLengthAtParam( dUCurr1, dLen) ; - bOkMinDist = ( dLen < dLenPrev1 + 1. * dSampleLen) ; + double dLen ; pCrvEdge2->GetLengthAtParam( dUCurr2, dLen) ; + bOkMinDist = ( dLen < dLenPrev2 + 2. * dSampleLen) ; } if ( ! bOkMinDist) { // --- Aumento la distanza corrente del passo di campionamento - double dLen = Clamp( dLenPrev1 + dSampleLen, 0., dLen1) ; - CrvU1.GetParamAtLength( dLen, dUCurr1) ; + double dLen = Clamp( dLenPrev2 + dSampleLen, 0., dLen2) ; + pCrvEdge2->GetParamAtLength( dLen, dUCurr2) ; } } // Recupero il punto corrente e la direzione tangente sul secondo bordo - CrvU1.GetLengthAtParam( dUCurr1, dLenCurr1) ; - CrvU1.GetPointD1D2( dUCurr1, ICurve::FROM_MINUS, ptCurr1, &vtCurr1) ; - vtCurr1.Normalize() ; + pCrvEdge2->GetLengthAtParam( dUCurr2, dLenCurr2) ; + pCrvEdge2->GetPointD1D2( dUCurr2, ICurve::FROM_MINUS, ptCurr2, &vtCurr2) ; + vtCurr2.Normalize() ; // Verifico se le direzioni tangenti sono tra di loro circa parallele - const double COS_ANG_TOL = cos( 05. * DEGTORAD) ; - if ( vtCurr0 * vtCurr1 < COS_ANG_TOL) { - // Se fuori dalla tolleranza, recupero il miglior versore tangente sul secondo bordo nell'intervallo successivo di lunghezza ( 1. * dMyDist) - CrvU1.GetLengthAtPoint( ptCurr1, dLenCurr1) ; - double dLimInfLen1 = Clamp( dLenCurr1 - dSampleLen, dLenPrev1, dLen1) ; - double dLimSupLen1 = Clamp( dLenCurr1 + dSampleLen, dLenPrev1, dLen1) ; + 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) + pCrvEdge2->GetLengthAtPoint( ptCurr2, dLenCurr2) ; + double dLimInfLen2 = Clamp( dLenCurr2 - dSampleLen, dLenPrev2, dLen2) ; + double dLimSupLen2 = Clamp( dLenCurr2 + dSampleLen, dLenPrev2, dLen2) ; // [Controllo migliorabile, magari mendiante metodo di bisezione (?)] - const int NUM_STEP = 10 ; - double dMinCos = - 0. - EPS_ZERO ; + const int NUM_STEP = 20 ; + double dMinCos = - 1. - EPS_ZERO ; const double DEGTOL = 5. ; for ( int i = 0 ; i <= NUM_STEP ; ++ i) { - double dLen = dLimInfLen1 + i * ( dLimSupLen1 - dLimInfLen1) / NUM_STEP ; - double dUStep1 ; CrvU1.GetParamAtLength( dLen, dUStep1) ; - Point3d ptStep1 ; Vector3d vtStep1 = V_NULL ; - CrvU1.GetPointD1D2( dUStep1, ICurve::FROM_MINUS, ptStep1, &vtStep1) ; vtStep1.Normalize() ; - double dStepCos1 = vtCurr0 * vtStep1 ; - double dAngTol = ( i < NUM_STEP / 1 ? ( 1. * DEGTOL) / NUM_STEP * i : - ( - 1. * DEGTOL) / NUM_STEP * ( i - NUM_STEP)) ; - double dCosTol = 0. - cos( dAngTol * DEGTORAD) ; - if ( dStepCos1 + dCosTol > dMinCos) { - ptCurr1 = ptStep1 ; - vtCurr1 = vtStep1 ; - dUCurr1 = dUStep1 ; - dMinCos = dStepCos1 + dCosTol ; + double dLen = dLimInfLen2 + i * ( dLimSupLen2 - dLimInfLen2) / NUM_STEP ; + double dUStep2 ; pCrvEdge2->GetParamAtLength( dLen, dUStep2) ; + Point3d ptStep2 ; Vector3d vtStep2 = V_NULL ; + pCrvEdge2->GetPointD1D2( dUStep2, ICurve::FROM_MINUS, ptStep2, &vtStep2) ; vtStep2.Normalize() ; + double dStepCos2 = vtCurr1 * vtStep2 ; + double dAngTol = ( i < NUM_STEP / 2 ? ( 2. * DEGTOL) / NUM_STEP * i : + ( - 2. * DEGTOL) / NUM_STEP * ( i - NUM_STEP)) ; + double dCosTol = 1. - cos( dAngTol * DEGTORAD) ; + if ( dStepCos2 + dCosTol > dMinCos) { + ptCurr2 = ptStep2 ; + vtCurr2 = vtStep2 ; + dUCurr2 = dUStep2 ; + dMinCos = dStepCos2 + dCosTol ; } } } + + #if DEBUG_SYNCLINES + PtrOwner ptGeo2( CreateGeoPoint3d()) ; ptGeo2->Set( ptCurr2) ; + PtrOwner vtGeo2( CreateGeoVector3d()) ; vtGeo2->Set( 5. * vtCurr2, ptCurr2) ; + pGeomDB->AddGeoObj( GDB_ID_NULL, nLay2, Release( ptGeo2)) ; + pGeomDB->AddGeoObj( GDB_ID_NULL, nLay2, Release( vtGeo2)) ; + #endif - // analizzo l'eventuale presenza di edge - int nSubCrv = int( ceil( dUPrev0)) ; - EDGEVECTOR vEdge0, vEdge1 ; - double dCosEdge = cos( 35. * DEGTORAD) ; - while ( nSubCrv < dUCurr0) { - if ( nSubCrv == 0 && ! bIsClosed0) - continue ; - int nPrevCrv = ( nSubCrv > 0 ? nSubCrv - 1 : int( dUE0 - 1)) ; - Vector3d vtPrev ; CrvU0.GetCurve( nPrevCrv)->GetEndDir( vtPrev) ; - Point3d ptCurr ; CrvU0.GetCurve( nPrevCrv)->GetEndPoint( ptCurr) ; - CrvU0.GetCurve( nSubCrv)->GetStartDir( vtCurr0) ; - vtCurr0.Normalize() ; - vtPrev.Normalize() ; - if ( vtPrev * vtCurr0 < dCosEdge) - vEdge0.emplace_back( vtPrev, vtCurr0, ptCurr) ; - ++ nSubCrv ; - } - nSubCrv = int( ceil( dUPrev1)) ; - while ( nSubCrv < dUCurr1) { - if ( nSubCrv == 0 && ! bIsClosed0) - continue ; - int nPrevCrv = ( nSubCrv > 0 ? nSubCrv - 1 : int( dUE1 - 1)) ; - Vector3d vtPrev ; CrvU1.GetCurve( nPrevCrv)->GetEndDir( vtPrev) ; - Point3d ptCurr ; CrvU1.GetCurve( nPrevCrv)->GetEndPoint( ptCurr) ; - CrvU1.GetCurve( nSubCrv)->GetStartDir( vtCurr1) ; - vtCurr1.Normalize() ; - vtPrev.Normalize() ; - if ( vtPrev * vtCurr1 < dCosEdge) - vEdge1.emplace_back( vtPrev, vtCurr1, ptCurr) ; - ++ nSubCrv ; - } - if ( ssize( vEdge0) > 0 && ssize( vEdge1) > 0) { - int c = 0 ; - for ( int i = 0 ; i < ssize( vEdge0) ; ++i) { - for ( int j = c ; j < ssize( vEdge1) ; ++j) { - if ( vEdge0[i].vtPrev * vEdge1[j].vtPrev > COS_ANG_TOL || vEdge0[i].vtCurr * vEdge1[j].vtCurr > COS_ANG_TOL) { - vPairs.emplace_back( vEdge0[i].ptCurr, vEdge1[j].ptCurr) ; - c = j + 1 ; - break ; - } - } - } - } + // 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 - ICurveLine* CL = CreateCurveLine() ; CL->Set( ptCurr0, ptCurr1) ; - vGeo.push_back( CL) ; -#endif + // --- 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 + PtrOwner pCrvQuad1( ConvertCurveToComposite( pCrvEdge1->CopyParamRange( dUPrev1, dUCurr1))) ; + PtrOwner pCrvQuad2( ConvertCurveToComposite( pCrvEdge2->CopyParamRange( dUPrev2, dUCurr2))) ; + ManageEdgesInQuadrangulation( pCrvQuad1, pCrvQuad2, vEdgeSyncLines) ; + + //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) ; + //} - // Inserisco le curve di sincronizzazione nel Layer di destinazine - vPairs.emplace_back( ptCurr0, ptCurr1) ; // Aggiorno i parametri - ptPrev0 = ptCurr0 ; dUPrev0 = dUCurr0 ; dLenPrev0 = dLenCurr0 ; ptPrev1 = ptCurr1 ; dUPrev1 = dUCurr1 ; dLenPrev1 = dLenCurr1 ; + ptPrev2 = ptCurr2 ; dUPrev2 = dUCurr2 ; dLenPrev2 = dLenCurr2 ; } -#if SAVEPACEDISO - SaveGeoObj( vGeo, "D:\\Temp\\bezier\\ruled\\smoothness\\iso.nge") ; -#endif - - return CreateByIsoParamSet( &CrvU0, &CrvU1, vPairs) ; + return CreateByIsoParamSet( pCrvEdge1, pCrvEdge2, vEdgeSyncLines) ; } //---------------------------------------------------------------------------- diff --git a/SurfBezier.h b/SurfBezier.h index e484396..7db854f 100644 --- a/SurfBezier.h +++ b/SurfBezier.h @@ -154,7 +154,7 @@ class SurfBezier : public ISurfBezier, public IGeoObjRW bool RemoveCollapsedSpans( void) override ; bool SwapParameters( void) ; bool LimitSurfToTrimmedRegion( void) override ; - bool CreateSmoothByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, double dSampleLen) override ; + bool CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, double dSampleLen) override ; public : // IGeoObjRW int GetNgeId( void) const override ; diff --git a/Trimming.cpp b/Trimming.cpp index 098d9a9..8e6d286 100644 --- a/Trimming.cpp +++ b/Trimming.cpp @@ -4219,7 +4219,7 @@ GetTrimmingRuledBezier( const CISURFPVECTOR& vSurf, const ICurve* pCrvEdge1, // Se non ho punti di controllo forzati if ( vSyncPoints.empty()) { - pSurfBz.Set( GetSurfBezierRuled( pCompoEdge1, pCompoEdge2, ISurfBezier::RLT_B_MINDIST_PLUS, dMyLinTol)) ; + pSurfBz.Set( GetSurfBezierRuledSmooth( pCompoEdge1, pCompoEdge2, 10)) ; if ( IsNull( pSurfBz) || ! pSurfBz->IsValid()) { LOG_ERROR( GetEGkLogger(), "Error in Trimming : Ruled Bezier invalid") ; return nullptr ;