diff --git a/CalcPocketing.cpp b/CalcPocketing.cpp index 4af806b..d98d22d 100644 --- a/CalcPocketing.cpp +++ b/CalcPocketing.cpp @@ -43,7 +43,8 @@ using namespace std ; // variabili d'appoggio ( per non passare troppi parametri tra le funzioni secondarie) struct PocketParams { - int nType = POCKET_SPIRALIN ; + int nType = POCKET_SPIRALIN ; // tipo di lavorazione + int nLiType = LEAD_IN_NONE ; // tipo di ingresso (LeadIn) double dRad = 0. ; // raggio utensile double dRad_prec = -1. ; // raggio utensile della lavorazione precedente double dSideStep = 0. ; // step @@ -54,8 +55,8 @@ struct PocketParams { double dOpenMinSafe = 5. ; // estensione minima di sicurezza double dMaxOptSize = 0. ; // dimensione per ottimizzazione double dAngle = 0. ; // angolo per orientare le passate OneWay e ZigZag + double dLiTang = INFINITO ; // valore di LeadIn in ingresso bool bOptOffsets = true ; // flag per evitare Offset non necessari - bool bOptOffsetsAdv = false ; // flag per evitare Offset coperti da curve di MedialAxis in SpiralIn/Out bool bAboveHead = true ; // flag per testa da sopra ( Z+) bool bSmooth = false ; // curve smussate bool bInvert = false ; // inversione dei percorsi @@ -76,6 +77,7 @@ struct PocketParams { double dOpenEdgeRad = 0. ; // raggio effettivo di estensione degli aperti } ; static double TOL_TRAPEZOID = 50 * EPS_SMALL ; // tolleranza per casi a trapezio SpiralPocket +static double TOL_REMOVE_OFFSET = 2. ; // tolleranza per controllo materiale lasciato da un Offset typedef vector VICRVCOMPOPOVECTOR ; //--------------------------------------------------------------------------- @@ -85,11 +87,43 @@ typedef vector VICRVCOMPOPOVECTOR ; #include "EgtDev/Include/EGkGeoObjSave.h" #include "EgtDev/Include/EGkGeoPoint3d.h" #include "EgtDev/Include/EGkGeoVector3d.h" + + // Varibili ausiliarie vector VT ; vector VC ; + + //--------------------------------------------------------------------------- inline Color GetRandomColor() { return Color( double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, double( rand()) / RAND_MAX, 1.) ; } + + //--------------------------------------------------------------------------- + inline void DrawCurve( const ICurveComposite* pCrvCompo) { + if ( pCrvCompo == nullptr || ! pCrvCompo->IsValid()) + return ; + for ( int nU = 0 ; nU < pCrvCompo->GetCurveCount() ; ++ nU) { + int nProp0 ; pCrvCompo->GetCurveTempProp( nU, nProp0, 0) ; + VT.emplace_back( static_cast( pCrvCompo->GetCurve( nU)->Clone())) ; + VC.emplace_back( nProp0 == 0 ? BLUE : RED) ; + } + return ; + } + + //--------------------------------------------------------------------------- + inline void DrawSfrAndLoops( const ISurfFlatRegion* pSfr, Color cCol = Color( 0., 255., 0., .5)) { + if ( pSfr == nullptr || ! pSfr->IsValid()) + return ; + VT.emplace_back( static_cast( pSfr->Clone())) ; + VC.emplace_back( cCol) ; + for ( int nC = 0 ; nC < pSfr->GetChunkCount() ; ++ nC) { + for ( int nL = 0 ; nL < pSfr->GetLoopCount( nC) ; ++ nL) { + PtrOwner pCrvCompo( ConvertCurveToComposite( pSfr->GetLoop( nC, nL))) ; + DrawCurve( pCrvCompo->Clone()) ; + } + } + return ; + } + #endif //--------------------------------------------------------------------------- @@ -389,29 +423,52 @@ CheckSimpleOverlap( const ICurve* pCrv, const ICurveComposite* pCrvOri, int& nSt //---------------------------------------------------------------------------- static bool -AssignFeedForOpenEdge( ICurveComposite* pCrv, const ICurveComposite* pCrvOF_orig, +AssignFeedForOpenEdge( ICurveComposite* pCrv, const ISurfFlatRegion* pSfrOpenEdges, const PocketParams& PockParams) { + // NB. *pSfrOpenEdges deve essere una regione che all'esterno contiene i lati considerati aperti + // Solo ai tratti esterni vengono assegnate le Feed + // se non rischiesto il calcolo della Feed, lascio quella standard if ( ! PockParams.bCalcFeed) return AssignMaxFeed( pCrv, PockParams) ; - // controllo se qualche curva passa sopra ad un lato aperto... - if ( pCrvOF_orig != nullptr) { - for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { - const ICurve* pCrv_u = pCrv->GetCurve( u) ; - if ( pCrv_u == nullptr) - return false ; - int nStat = -1 ; - if ( CheckSimpleOverlap( pCrv_u, pCrvOF_orig, nStat, 1500 * EPS_SMALL) && nStat == 1) { - double dFeed = GetMinFeed( PockParams) ; - GetFeedForParam( PockParams.dOpenEdgeRad, PockParams, dFeed) ; - pCrv->SetCurveTempParam( u, dFeed, 0) ; + // se non ho una regione di classificazione, non faccio nulla + if ( pSfrOpenEdges == nullptr || ! pSfrOpenEdges->IsValid()) + return true ; + + // classifico la curva in base alla regione + CRVCVECTOR ccClass ; + if ( pSfrOpenEdges->GetCurveClassification( *pCrv, EPS_SMALL, ccClass)) { + PtrOwner pNewCrv( CreateCurveComposite()) ; + if ( IsNull( pNewCrv)) + return false ; + for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) { + // recupero il tratto di curva corrente + PtrOwner pCrvRange( ConvertCurveToComposite( pCrv->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) ; + if ( ! IsNull( pCrvRange) && pCrvRange->IsValid()) { + // se il tratto non è interno, calcolo la sua Feed + if ( ccClass[i].nClass != CRVC_IN) { + double dFeed = GetMinFeed( PockParams) ; + GetFeedForParam( 2. * PockParams.dRad - PockParams.dOpenEdgeRad, PockParams, dFeed) ; + AssignCustomFeed( pCrvRange, PockParams, dFeed) ; + } + // aggiungo il tratto alla sottocurva + if ( ! pNewCrv->AddCurve( Release( pCrvRange))) + return false ; } } + // assegno la curva calcolata + pNewCrv->SetTempProp( pCrv->GetTempProp( 0), 0) ; + pNewCrv->SetTempProp( pCrv->GetTempProp( 1), 1) ; + pNewCrv->SetTempParam( pCrv->GetTempParam( 0), 0) ; + pNewCrv->SetTempParam( pCrv->GetTempParam( 0), 0) ; + double dThick ; pCrv->GetThickness( dThick) ; + pNewCrv->SetThickness( dThick) ; + Vector3d vtExtr ; pCrv->GetExtrusion( vtExtr) ; + pNewCrv->SetExtrusion( vtExtr) ; + pCrv->CopyFrom( pNewCrv) ; } - else - AssignMaxFeed( pCrv, PockParams) ; return true ; } @@ -455,10 +512,10 @@ SimplifyCurveByFeeds( ICurveComposite* pCrvCompo, const PocketParams& PockParam, // lo semplifico // recupero la lunghezza del tratto e imposto associo la Feed corrente // se curva corta -> il parametro di Feed è definito da quello precedente - pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ; + pCrv->MergeCurves( 100 * EPS_SMALL, ANG_TOL_STD_DEG, false) ; pCrv->GetLength( dLen) ; - for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) - pCrv->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ; + for ( int nU = 0 ; nU < pCrv->GetCurveCount() ; ++ nU) + pCrv->SetCurveTempParam( nU, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ; pCrvSimple->AddCurve( Release( pCrv)) ; dCurrTempParam = dTempParam ; nParStart = i ; @@ -467,10 +524,10 @@ SimplifyCurveByFeeds( ICurveComposite* pCrvCompo, const PocketParams& PockParam, // ultima parte di curva uniforme ... PtrOwner pCrvLast( ConvertCurveToComposite( pCrvCompo->CopyParamRange( nParStart, pCrvCompo->GetCurveCount()))) ; if ( ! IsNull( pCrvLast)) { - pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ; + pCrvLast->MergeCurves( 100 * EPS_SMALL, ANG_TOL_STD_DEG, false) ; double dLen ; pCrvLast->GetLength( dLen) ; - for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u) - pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ; + for ( int nU = 0 ; nU < pCrvLast->GetCurveCount() ; ++ nU) + pCrvLast->SetCurveTempParam( nU, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ; pCrvSimple->AddCurve( Release( pCrvLast)) ; } @@ -488,7 +545,7 @@ SimplifyCurveByFeeds( ICurveComposite* pCrvCompo, const PocketParams& PockParam, PtrOwner pCrv( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, i))) ; if ( IsNull( pCrv)) return false ; - pCrv->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ; + pCrv->MergeCurves( 100 * EPS_SMALL, ANG_TOL_STD_DEG, false) ; // recupero la lunghezza di tale curve double dLen ; pCrv->GetLength( dLen) ; for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) @@ -502,11 +559,11 @@ SimplifyCurveByFeeds( ICurveComposite* pCrvCompo, const PocketParams& PockParam, pCrvLast.Set( ConvertCurveToComposite( pCrvSimple->CopyParamRange( nParStart, pCrvSimple->GetCurveCount()))) ; if ( ! IsNull( pCrvLast)) { // semplifico - pCrvLast->MergeCurves( 100 * EPS_SMALL, 100 * EPS_ANG_SMALL, false) ; + pCrvLast->MergeCurves( 100 * EPS_SMALL, ANG_TOL_STD_DEG, false) ; // recupero la lunghezza di tale curva e imposto la Feed pCrvLast->GetLength( dLen) ; - for ( int u = 0 ; u < pCrvLast->GetCurveCount() ; ++ u) - pCrvLast->SetCurveTempParam( u, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ; + for ( int nU = 0 ; nU < pCrvLast->GetCurveCount() ; ++ nU) + pCrvLast->SetCurveTempParam( nU, ( 0.1 * PockParam.dRad < dLen ? dCurrTempParam : dTempParam), 0) ; pCrvCompo->AddCurve( Release( pCrvLast)) ; } @@ -516,7 +573,7 @@ SimplifyCurveByFeeds( ICurveComposite* pCrvCompo, const PocketParams& PockParam, //---------------------------------------------------------------------------- static bool AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved, bool bIsLink, - bool bFirstOffs, const ICurveComposite* pCrv_orig, const PocketParams& PockParams, + bool bFirstOffs, const ISurfFlatRegion* pSfrOpenEdges, const PocketParams& PockParams, double dToll) { // controllo la validità della curva @@ -537,7 +594,7 @@ AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved, boo // se non ho una superificie svuotata, allora esco ( con Feed Minima) if ( pSrfRemoved == nullptr || ! pSrfRemoved->IsValid() || pSrfRemoved->GetChunkCount() == 0) { // controllo eventuali sovrapposizioni con lati aperti - return AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ; + return AssignFeedForOpenEdge( pCrv, pSfrOpenEdges, PockParams) ; } // parametro di Offset per regione svuotata ( restringo la superficie in maniera appropriata) @@ -567,9 +624,8 @@ AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved, boo if ( IsNull( pCrv_sez)) continue ; // curva troppo piccola, passo alla successiva // controllo se interno o fuori alla regione ( regolando quindi la Feed) - double dCurrFeed = GetMinFeed( PockParams) ; - if ( ccClass[i].nClass == CRVC_IN) // se interna alla regione rimossa... - dCurrFeed = GetMaxFeed( PockParams) ; + double dCurrFeed = ( ccClass[i].nClass == CRVC_IN ? GetMaxFeed( PockParams) : + GetMinFeed( PockParams)) ; // assegno tale Feed ad ogni sottocurva ricavata dal tratto di classificazione for ( int u = 0 ; u < pCrv_sez->GetCurveCount() ; ++ u) pCrv_sez->SetCurveTempParam( u, dCurrFeed, 0) ; @@ -581,13 +637,14 @@ AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved, boo pCrv->Clear() ; pCrv->AddCurve( Release( pCrv_new)) ; + // cerco di uniformare i tratti ricavati if ( ! bIsLink) { // ---------------- NEL CASO DI OFFSET ---------------- // creo un intervallo con tutte le Feed Minime Intervals IntMinFeed ; - for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { - double dParam ; pCrv->GetCurveTempParam( u, dParam, 0) ; - if ( abs( dParam - GetMinFeed( PockParams)) < 5 * EPS_SMALL) - IntMinFeed.Add( u, u + 1) ; + for ( int nU = 0 ; nU < pCrv->GetCurveCount() ; ++ nU) { + double dFeed ; pCrv->GetCurveTempParam( nU, dFeed, 0) ; + if ( abs( dFeed - GetMinFeed( PockParams)) < 5 * EPS_SMALL) + IntMinFeed.Add( nU, nU + 1) ; } double dParS = EPS_SMALL ; double dParE = EPS_SMALL ; // a questo intervallo tolgo tutti i sottotratti di lunghezza inferiore alla tolleranza richiesta @@ -600,13 +657,13 @@ AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved, boo IntMinFeed_noSmall.Add( dParS, dParE) ; bFound = IntMinFeed.GetNext( dParS, dParE) ; } - for ( int u = 0 ; u < pCrv->GetCurveCount() ; ++ u) { - if ( ! IntMinFeed_noSmall.IsInside( u + 0.5)) - pCrv->SetCurveTempParam( u, GetMaxFeed( PockParams), 0) ; + for ( int nU = 0 ; nU < pCrv->GetCurveCount() ; ++ nU) { + if ( ! IntMinFeed_noSmall.IsInside( nU + 0.5)) + pCrv->SetCurveTempParam( nU, GetMaxFeed( PockParams), 0) ; } } else { // ---------------- NEL CASO DI LINK ---------------- - // le curve con lunghezza < dToll vanno modificate + // le curve con lunghezza inferiore alla tolleranza vanno modificate for ( int j = 0 ; j < pCrv->GetCurveCount() ; ++ j) { double dLen = EPS_SMALL ; pCrv->GetCurve( j)->GetLength( dLen) ; @@ -646,8 +703,7 @@ AssignFeedSpiral( ICurveComposite* pCrv, const ISurfFlatRegion* pSrfRemoved, boo } if ( bFirstOffs) - AssignFeedForOpenEdge( pCrv, pCrv_orig, PockParams) ; - + AssignFeedForOpenEdge( pCrv, pSfrOpenEdges, PockParams) ; return true ; } @@ -1809,6 +1865,9 @@ GetPocketCurvesByClosedEdges( const ISurfFlatRegion* pSfrChunk, const PocketPara pSfrChunk->GetMaxOffset( dMaxOffs) ; if ( dMaxOffs > PockParams.dRad + 200 * EPS_SMALL) return true ; + // controllo che il MaxOptSize sia coerente + if ( 2 * dMaxOffs > PockParams.dMaxOptSize + 10 * EPS_SMALL) + return true ; /* NB. Si poteva calcolare la Fat curve del raggio utensile dell'Offset di tutti i chiusi e vedere @@ -1836,6 +1895,9 @@ GetPocketCurvesByClosedEdges( const ISurfFlatRegion* pSfrChunk, const PocketPara vpCrvs.erase( vpCrvs.end() - 1) ; } } + // CASO PARTICOLARE : Tunnel ( 2 tratti open e due tratto chiusi) -> non uso le curve singole + if ( int( vpCrvs.size() == 4)) + return true ; // controllo se il loop Esterno è uniforme ( quindi tutto chiuso o tutto aperto) bool bExtAllClose = false ; bool bExtAllOpen = false ; @@ -1847,7 +1909,7 @@ GetPocketCurvesByClosedEdges( const ISurfFlatRegion* pSfrChunk, const PocketPara } // NB. Se regione estena tutta chiusa o aperta e senza isole... allora non faccio nulla ( es caso trapezi) // Se tutta chiusa -> Chunk non svuotabile, il contro-offset del raggio utensile annulla la regione - // Se tutta aperta -> La regione è definita mediante Offset esterno del contorno aperto + // Se tutta aperta -> La regione è definita mediante Offset esterno del contorno aperto if ( ( bExtAllClose || bExtAllOpen) && ! bHasIslands) return true ; // Se il contorno esterno è misto -> non devo avere isole @@ -2387,6 +2449,9 @@ CalcCircleSpiral( const Point3d& ptCen, const Vector3d& vtN, double dOutRad, dou // assegno la Feed AssignFeedSpiralOpt( 0, PockParams, pMCrv) ; + + // assegno proprietà di riconoscimento + pMCrv->SetTempProp( TEMP_PROP_OPT_CIRCLE, 0) ; return true ; } @@ -2406,6 +2471,8 @@ CalcTrapezoidSpiralLocalFrame( ICurveComposite* pCrvTrap, const Vector3d& vtDir, } if ( nBaseId != 0 && nBaseId != 1) return false ; + if ( pCrvTrap->GetCurve( nBaseId)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) + nBaseId += 2 ; // imposto come lato iniziale per la curva uno dei lati paralleli a vtDir pCrvTrap->ChangeStartPoint( nBaseId) ; @@ -2609,20 +2676,21 @@ GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTr vtDir.Normalize() ; Vector3d vtOrtho = OrthoCompo( vtOtherDir, vtDir) ; dPocketSize = vtOrtho.Len() ; - if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap)) - return false ; // assegno le tempProp della curva trapezio for ( int nU = 0 ; nU < 4 ; ++ nU) { Vector3d vtCrvDir ; pCrvTrap->GetCurve( nU)->GetStartDir( vtCrvDir) ; for ( int nI = 0 ; nI < pCrvCompo->GetCurveCount() ; ++ nI) { - Vector3d vtCurrCrvDir ; pCrvTrap->GetCurve( nI)->GetStartDir( vtCurrCrvDir) ; + Vector3d vtCurrCrvDir ; pCrvCompo->GetCurve( nI)->GetStartDir( vtCurrCrvDir) ; if ( AreSameVectorEpsilon( vtCrvDir, vtCurrCrvDir, 150 * EPS_SMALL)) { pCrvTrap->SetCurveTempProp( nU, pCrvCompo->GetCurve( nI)->GetTempProp( 0), 0) ; break ; } } } - bool bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == 1 || pCrvTrap->GetCurve( 2)->GetTempProp( 0) == 1) ; + if ( ! CalcTrapezoidSpiralLocalFrame( pCrvTrap, vtDir, frTrap)) + return false ; + bool bBaseOpen = ( pCrvTrap->GetCurve( 0)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE || + pCrvTrap->GetCurve( 2)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) ; // controllo dimensioni della svuotatura if ( ! ( bBaseOpen && dPocketSize < dDiam + EPS_SMALL) && abs( dPocketSize - dDiam) > EPS_SMALL) { @@ -2630,7 +2698,8 @@ GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTr return true ; } // recupero flag aperto/chiuso dei lati - if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == 0 && pCrvTrap->GetCurve( 3)->GetTempProp( 0) == 0) { + if ( pCrvTrap->GetCurve( 1)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE && + pCrvTrap->GetCurve( 3)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) { double dLen0, dLen2 ; pCrvTrap->GetCurve( 0)->GetLength( dLen0) ; pCrvTrap->GetCurve( 2)->GetLength( dLen2) ; @@ -2682,7 +2751,7 @@ GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTr frTrap.Set( ptNewOrig, Z_AX, vtDir) ; // imposto tutte le 4 curve come aperte for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) - pCrvTrap->SetCurveTempProp( u, 1, 0) ; + pCrvTrap->SetCurveTempProp( u, TEMP_PROP_OPEN_EDGE, 0) ; // imposto le basi nBase = 0 ; nSecondBase = 2 ; @@ -2696,7 +2765,7 @@ GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTr if ( nType != -1) { // imposto tutte le curve come aperte tranne la prima ( estendo il solo lato chiuso come base del Box) for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) - pCrvTrap->SetCurveTempProp( u, u == 0 ? 0 : 1, 0) ; + pCrvTrap->SetCurveTempProp( u, u == 0 ? 0 : TEMP_PROP_OPEN_EDGE, 0) ; // memorizzo la dimensione di svuotatura pCrvTrap->GetCurve( 1)->GetLength( dPocketSize) ; // imposto le basi @@ -2732,7 +2801,7 @@ GetTrapezoidFromShape( const ICurveComposite* pCrvCompo, ICurveComposite* pCrvTr if ( DPL.GetDist( dDist) && abs( dDist - dDiam) < TOL_TRAPEZOID) { // imposto tutte le curve di indice dispari aperte for ( int u = 0 ; u < pCrvTrap->GetCurveCount() ; ++ u) - pCrvTrap->SetCurveTempProp( u, u % 2 == 0 ? 0 : 1, 0) ; + pCrvTrap->SetCurveTempProp( u, u % 2 == 0 ? TEMP_PROP_CLOSE_EDGE : TEMP_PROP_OPEN_EDGE, 0) ; // imposto le basi nBase = 0 ; nSecondBase = 2 ; @@ -3354,7 +3423,7 @@ CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double double dLen0, dLen1, dLen2, dLen3, dMaxLarg = 0. ; pCrvPocket->GetCurve( nBase)->GetLength( dLen0) ; pCrvPocket->GetCurve( nSecondBase)->GetLength( dLen2) ; - bool bRealTrap = pCrvPocket->GetCurveCount() == 4 ; + bool bRealTrap = ( pCrvPocket->GetCurveCount() == 4) ; for ( int i = 0 ; i < 4 && bRealTrap ; ++ i) bRealTrap = pCrvPocket->GetCurve( i)->GetType() == CRV_LINE ; if ( bRealTrap) { @@ -3382,9 +3451,9 @@ CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double else { // trovo la quota Y per centro del Tool double dYCoord ; - if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) // se base principale chiusa + if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) // se base principale chiusa dYCoord = 0.5 * dDiam + dOffsR ; - else if ( pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 0) // se base principale aperta e secondaria chiusa + else if ( pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) // se base principale aperta e secondaria chiusa dYCoord = dPocketSize - 0.5 * dDiam - dOffsR ; else // se entrambi i lati paralleli sono aperti mi posiziono a metà della svuotatura dYCoord = 0.5 * dPocketSize ; @@ -3426,23 +3495,23 @@ CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double pLine3->GetEndPoint( ptS) ; } - if ( vnProp[0] != 0) { + if ( vnProp[0] != TEMP_PROP_CLOSE_EDGE) { pMCrv->AddPoint( ptS) ; - if ( vnProp[2] != 0) + if ( vnProp[2] != TEMP_PROP_CLOSE_EDGE) pMCrv->AddLine( ptE) ; else pMCrv->AddLine( ptStart) ; } else { pMCrv->AddPoint( ptE) ; - if ( vnProp[0] != 0) + if ( vnProp[0] != TEMP_PROP_CLOSE_EDGE) pMCrv->AddLine( ptS) ; else pMCrv->AddLine( ptStart) ; pMCrv->Invert() ; } - pMCrv->SetCurveTempProp( 0, 1) ; + pMCrv->SetCurveTempProp( 0, TEMP_PROP_OPEN_EDGE) ; } } else { @@ -3452,13 +3521,13 @@ CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double return true ; // aggiustamenti al percorso per rimuovere materiale residuo negli angoli - if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 1 || - pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == 1) { + if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE || + pCrvPocket->GetCurve( nSecondBase)->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) { Frame3d frOpen ; bool bSwitch = false ; // Base principale chiusa e base Secondaria aperta - if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == 0) { + if ( pCrvPocket->GetCurve( nBase)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE) { pCrvPocket->ChangeStartPoint( nSecondBase) ; // oriento il Frame ( ho sempre l'origine nell'estremo superiore del lato aperto) Vector3d vtDir ; pCrvPocket->GetStartDir( vtDir) ; @@ -3476,12 +3545,12 @@ CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double bSwitch = true ; } - if ( pCrvPocket->GetCurve( 3)->GetTempProp( 0) == 0 && + if ( pCrvPocket->GetCurve( 3)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE && ! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, true)) { pMCrv->Clear() ; return false ; } - if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == 0 && + if ( pCrvPocket->GetCurve( 1)->GetTempProp( 0) == TEMP_PROP_CLOSE_EDGE && ! AdjustTrapezoidSpiralForAngles( pMCrv, pCrvPocket, PockParams, false)) { pMCrv->Clear() ; return false ; @@ -3502,7 +3571,7 @@ CalcTrapezoidSpiral( ICurveComposite* pCrvPocket, const Frame3d& frTrap, double pMCrv->ToGlob( frTrap) ; - if ( ! PockParams.bInvert) { + if ( PockParams.bInvert) { pMCrv->Invert() ; // inverto le proprietà in modo che nProp3 sia sempre legata al punto iniziale e nProp1 a quello finale swap( vnProp[1], vnProp[3]) ; @@ -3556,6 +3625,8 @@ GetSpiralOptimizedCurves( const ISurfFlatRegion* pSfrChunk, const PocketParams& bOkSpiral = ( dRad - dOffs > 10 * EPS_SMALL) ; if ( bOkSpiral) { double dIntRad = 0 ; + if ( PockParam.nType == POCKET_SPIRALOUT && PockParam.nLiType == LEAD_IN_HELIX) + dIntRad = min( 0.5 * min( PockParam.dLiTang, 2. * PockParam.dRad), dRad - dOffs) ; bool bOkSpiral = CalcCircleSpiral( ptCen, pSfrChunk->GetNormVersor(), dRad - dOffs, dIntRad, PockParam, pCrvRes) ; if ( bOkSpiral && pCrvRes->IsValid() && pCrvRes->GetCurveCount() > 0) { // se curva di bordo OPEN e lavorazione SpiralIn, imposto i parametri per LeadIn @@ -3642,7 +3713,8 @@ GetPocketingOptimizedCurves( ISurfFlatRegion* pSfr, const PocketParams& PockPara } // ricavo le curve ottimizzate a seconda della lavorazione richiesta - if ( PockParam.nType == POCKET_SPIRALIN || PockParam.nType == POCKET_SPIRALOUT) { + if ( PockParam.nType == POCKET_SPIRALIN || PockParam.nType == POCKET_SPIRALOUT || + PockParam.nType == POCKET_CONFORMAL_ZIGZAG || PockParam.nType == POCKET_CONFORMAL_ONEWAY) { // curva da resituire PtrOwner pCrvOptSpiral( CreateCurveComposite()) ; if ( IsNull( pCrvOptSpiral) || @@ -4621,14 +4693,10 @@ CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, const ICRVCOMPOPO for ( int j = 0 ; j < int( ccClass.size()) ; ++j ) { // se ho intersezione spezzo il segmento if ( ccClass[j].nClass == CRVC_OUT) { - Point3d ptS ; - pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ; - double dOffS ; - vOffIslands[i]->GetParamAtPoint( ptS, dOffS) ; - Point3d ptE ; - pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ; - double dOffE ; - vOffIslands[i]->GetParamAtPoint( ptE, dOffE) ; + Point3d ptS ; pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ; + double dOffS ; vOffIslands[i]->GetParamAtPoint( ptS, dOffS) ; + Point3d ptE ; pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ; + double dOffE ; vOffIslands[i]->GetParamAtPoint( ptE, dOffE) ; // recupero i due possibili percorsi e uso il più corto PtrOwner pCrvA( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ; PtrOwner pCrvB( vOffIslands[i]->CopyParamRange( dOffE, dOffS)) ; @@ -4656,191 +4724,213 @@ CalcBoundedLink( const Point3d& ptStart, const Point3d& ptEnd, const ICRVCOMPOPO return true ; } -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ static bool -ModifyBiArc( ICurve* pBiArcLink, double dToll, ICurveComposite* pNewBiArc) +CalcSpecialBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, + const Vector3d& vtEnd, const PocketParams& PockParams, + ICurveComposite* pCrvLink) { - // controllo dei parametri - if ( pBiArcLink == nullptr) + if ( pCrvLink == nullptr) return false ; - if ( dToll > 0.99 || dToll < 0.01) + pCrvLink->Clear() ; + + // parametro di estensione in tangenza + double dLenExtension = PockParams.dRad / 2. ; + + // determino i punti della curva + PNTVECTOR vPnts = { ptStart, ptStart + ( dLenExtension * vtStart), + ptEnd - ( dLenExtension * vtEnd), ptEnd} ; + int nInd = 1 ; + static const double COS_TOL = cos( 130 * DEGTORAD) ; + // se primo angolo minore della tolleranza, aggiungo un punto + Vector3d vtPrev = vPnts[nInd] - vPnts[nInd-1] ; vtPrev.Normalize() ; + Vector3d vtNext = vPnts[nInd+1] - vPnts[nInd] ; vtNext.Normalize() ; + double dCos = vtPrev * vtNext ; + if ( dCos < COS_TOL) { + Point3d ptA = vPnts[nInd] + dLenExtension * GetRotate( vtStart, Z_AX, ANG_RIGHT) ; + Point3d ptB = vPnts[nInd] - dLenExtension * GetRotate( vtStart, Z_AX, ANG_RIGHT) ; + if ( SqDist( ptA, vPnts[nInd+1]) < SqDist( ptB, vPnts[nInd+1])) + vPnts.insert( vPnts.begin() + nInd + 1, ptA) ; + else + vPnts.insert( vPnts.begin() + nInd + 1, ptB) ; + ++ nInd ; + } + ++ nInd ; + // se secondo angolo maggiore della tolleranza, aggiungo un punto + vtPrev = vPnts[nInd] - vPnts[nInd-1] ; vtPrev.Normalize() ; + vtNext = vPnts[nInd+1] - vPnts[nInd] ; vtNext.Normalize() ; + dCos = vtPrev * vtNext ; + if ( dCos < COS_TOL) { + Point3d ptA = vPnts[nInd] + dLenExtension * GetRotate( - vtEnd, Z_AX, ANG_RIGHT) ; + Point3d ptB = vPnts[nInd] - dLenExtension * GetRotate( - vtEnd, Z_AX, ANG_RIGHT) ; + if ( SqDist( ptA, vPnts[nInd-1]) < SqDist( ptB, vPnts[nInd-1])) + vPnts.insert( vPnts.begin() + nInd + 1, ptA) ; + else + vPnts.insert( vPnts.begin() + nInd + 1, ptB) ; + } + + // definisco raccordo a ZigZag ( Lineare) + PtrOwner pZigZagLink( CreateCurveComposite()) ; + if ( IsNull( pZigZagLink)) return false ; - pNewBiArc->Clear() ; + bool bFirst = true ; + for ( const Point3d& ptNew : vPnts) { + if ( bFirst) { + bFirst = false ; + pZigZagLink->AddPoint( ptNew) ; + } + else + pZigZagLink->AddLine( ptNew) ; + } - // dominio - double dUS, dUE ; - pBiArcLink->GetDomain( dUS, dUE) ; - - // prendo i due archi della curva BiArco - PtrOwner pArc1( pBiArcLink->CopyParamRange( dUS, ( dUS + dUE) / 2)) ; - PtrOwner pArc2( pBiArcLink->CopyParamRange(( dUS + dUE ) / 2, dUE)) ; - if ( IsNull( pArc1) || ! pArc1->IsValid() || IsNull( pArc2) || ! pArc2->IsValid()) - return false ; - - // primo pezzo - pArc1->GetDomain( dUS, dUE) ; - PtrOwner pArc1A( pArc1->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1 - PtrOwner pArc1B( pArc1->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2 - Point3d pt1A, pt1B ; - pArc1A->GetEndPoint( pt1A) ; - pArc1B->GetStartPoint( pt1B) ; - PtrOwner pLine1( CreateBasicCurveLine()) ; - pLine1->Set( pt1A, pt1B) ; - - // secondo pezzo - pArc2->GetDomain( dUS, dUE) ; - PtrOwner pArc2A( pArc2->CopyParamRange( dUS, dUS + ( dUE - dUS ) * dToll)) ; // Arc1 - PtrOwner pArc2B( pArc2->CopyParamRange( dUE - ( dUE - dUS ) * dToll, dUE)) ; // Arc2 - Point3d pt2A, pt2B ; - pArc2A->GetEndPoint( pt2A) ; - pArc2B->GetStartPoint( pt2B) ; - PtrOwner pLine2( CreateBasicCurveLine()) ; - pLine2->Set( pt2A, pt2B) ; - - // ricostruisco il nuovo link - if ( ! pNewBiArc->AddCurve( Release( pArc1A)) || - ! pNewBiArc->AddCurve( Release( pLine1)) || - ! pNewBiArc->AddCurve( Release( pArc1B)) || - ! pNewBiArc->AddCurve( Release( pArc2A)) || - ! pNewBiArc->AddCurve( Release( pLine2)) || - ! pNewBiArc->AddCurve( Release( pArc2B))) - return false ; - - return true ; + // questa composita contiene al massimo 5 curve e, in generale, quella una curva molto più lunga + // delle altre ( quella che collega le due estensioni calcolate). Spezzo la composita ottenuta in curve + // la cui lunghezza massima è circa il raggio utensile + PolyLine PL ; + pZigZagLink->ApproxWithLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL) ; + PL.AdjustForMaxSegmentLen( PockParams.dRad + 10 * EPS_SMALL) ; + pZigZagLink->Clear() ; + pZigZagLink->FromPolyLine( PL) ; + // Smusso il raccordo a ZigZag + ModifyCurveToSmoothed( pZigZagLink, PockParams, .4, .4, true) ; + pCrvLink->CopyFrom( pZigZagLink) ; + return ( pCrvLink != nullptr && pCrvLink->IsValid() && pCrvLink->GetCurveCount() > 0) ; } //------------------------------------------------------------------------------ static bool CalcBoundedSmoothedLink( const Point3d& ptStart, const Vector3d& vtStart, const Point3d& ptEnd, - const Vector3d& vtEnd, double dParMeet, const ICRVCOMPOPOVECTOR& vOffIslands, - const PocketParams& PockParams, ICurveComposite* pCrvLink) + const Vector3d& vtEnd, double dParMeet, const ICRVCOMPOPOVECTOR& vCrvBorders, + const PocketParams& PockParams, ICurveComposite* pCrvLink, bool& bSpecial) { - // se senza smusso, ritorno tratto lineare - if ( ! PockParams.bSmooth) - return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; - - // creo il BiArc che unisce i due punti - double dAngStart, dAngEnd ; - vtStart.GetAngleXY( X_AX, dAngStart) ; - vtEnd.GetAngleXY( X_AX, dAngEnd) ; - PtrOwner pBiArcLink ; - if ( dParMeet != 0) - pBiArcLink.Set( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, dParMeet)) ; - else { - PtrOwner pCrvCir( CreateBasicCurveArc()) ; - if ( IsNull( pCrvCir)) - return false ; - pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, PockParams.bInvert ? 360 : - 360, 0, Z_AX) ; - pBiArcLink.Set( pCrvCir->Clone()) ; - pCrvLink->AddCurve( pBiArcLink->Clone()) ; - return true ; - } - if ( IsNull( pBiArcLink)) - return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; - - // se BiArco troppo grande allora lo modifico - double dLenBiArc ; - pBiArcLink->GetLength( dLenBiArc) ; - if ( dLenBiArc > 200) { - PtrOwner pCrvNewBiArcS( CreateBasicCurveComposite()) ; - ModifyBiArc( pBiArcLink, 0.2, pCrvNewBiArcS) ; - pBiArcLink.Set( pCrvNewBiArcS) ; - } - - // creo la nuova curva formata inizialmente dai tratti di archi - PtrOwner pCompo( GetCurveComposite( pBiArcLink->Clone())) ; - if ( IsNull( pCompo)) + // controllo parametri + if ( pCrvLink == nullptr) return false ; - PtrOwner pCompoHelp( GetCurveComposite( pBiArcLink->Clone())) ; - if ( IsNull( pCompoHelp)) + pCrvLink->Clear() ; + + // se senza smusso (e non caso circonferenza), ritorno tratto lineare + bSpecial = false ; + if ( ! PockParams.bSmooth && dParMeet > EPS_ZERO) + return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; + + // inizializzo la curva di Link da restituire + PtrOwner pMyCrvLink( CreateCurveComposite()) ; + if ( IsNull( pMyCrvLink)) return false ; - // scorro tutte le curve per controllare le intersezioni ... - for ( int i = 0 ; i < int( vOffIslands.size()) ; ++ i) { + // controllo se la distanza lineare tra i due punti è maggiore della tolleranza... + static double dSqTol = 4 * PockParams.dRad * PockParams.dRad + PockParams.dSideStep * PockParams.dSideStep ; + if ( SqDist( ptStart, ptEnd) > dSqTol) + bSpecial = ( CalcSpecialBoundedSmoothedLink( ptStart, vtStart, ptEnd, vtEnd, PockParams, pMyCrvLink)) ; + if ( ! pMyCrvLink->IsValid() || pMyCrvLink->GetCurveCount() == 0) { + // ...in caso negativo creo il Biarco + double dAngStart ; vtStart.GetAngleXY( X_AX, dAngStart) ; + double dAngEnd ; vtEnd.GetAngleXY( X_AX, dAngEnd) ; + if ( dParMeet > EPS_ZERO) + pMyCrvLink.Set( ConvertCurveToComposite( GetBiArc( ptStart, -dAngStart, ptEnd, -dAngEnd, dParMeet))) ; + else { + // ... o una circonferenza, orientata in modo da poter essere classificata con le curve di bordo + PtrOwner pCrvCir( CreateBasicCurveArc()) ; + if ( IsNull( pCrvCir)) + return false ; + if ( pCrvCir->SetCPAN( Media( ptStart, ptEnd), ptStart, PockParams.bInvert ? 360 : -360, 0, Z_AX)) + pMyCrvLink.Set( ConvertCurveToComposite( Release( pCrvCir))) ; + } + } + if ( ! pMyCrvLink->IsValid() || pMyCrvLink->GetCurveCount() == 0) + return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; + // se il BiArco creato è troppo piccolo, lo approssimo ad un tratto lineare + BBox3d bBox3 ; + if ( pMyCrvLink->GetLocalBBox( bBox3)) { + double dRadBB ; + if ( bBox3.GetRadius( dRadBB) && dRadBB < 500 * EPS_SMALL) + return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; + } + + // il link ottenuto non deve uscire dalle curve di bordo ( Loop esterno ed Isole) + // curva di test + PtrOwner pCompoTest( CloneCurveComposite( pMyCrvLink)) ; + if ( IsNull( pCompoTest) || ! pCompoTest->IsValid()) + return false ; + // curva ausiliaria + PtrOwner pCompoHelp( CloneCurveComposite( pMyCrvLink)) ; + if ( IsNull( pCompoHelp) || ! pCompoHelp->IsValid()) + return false ; + + // scorro tutte le curve le curve di bordo + for ( const ICurveComposite* pCrvBorder : vCrvBorders) { + // calcolo eventuali intersezioni CRVCVECTOR ccClass ; - IntersCurveCurve intCC( *pCompo, *vOffIslands[i]) ; + IntersCurveCurve intCC( *pCompoTest, *pCrvBorder) ; intCC.GetCurveClassification( 0, EPS_SMALL, ccClass) ; - if ( ! pCompoHelp->Clear()) - return false ; - // per ogni intersezione j con l'offset dell'isola i - for ( int j = 0 ; j < int( ccClass.size()) ; ++ j) { + // pulisco la curva ausiliaria + pCompoHelp->Clear() ; + + // analizzo le intersezioni + for ( int i = 0 ; i < int( ccClass.size()) ; ++ i) { // se ho intersezione spezzo il segmento - if ( ccClass[j].nClass == CRVC_OUT && int( ccClass.size()) > 1) { - Point3d ptS ; - pCompo->GetPointD1D2( ccClass[j].dParS, ICurve::FROM_PLUS, ptS) ; - double dOffS ; - vOffIslands[i]->GetParamAtPoint( ptS, dOffS, 1500 * EPS_SMALL) ; - Point3d ptE ; - pCompo->GetPointD1D2( ccClass[j].dParE, ICurve::FROM_MINUS, ptE) ; - double dOffE ; - vOffIslands[i]->GetParamAtPoint( ptE, dOffE, 1500 * EPS_SMALL) ; + if ( ccClass[i].nClass == CRVC_OUT && int( ccClass.size()) > 1) { + // recupero eventuali punti di intersezioni e parametri + Point3d ptS ; pCompoTest->GetPointD1D2( ccClass[i].dParS, ICurve::FROM_PLUS, ptS) ; + double dOffS ; pCrvBorder->GetParamAtPoint( ptS, dOffS, 1500 * EPS_SMALL) ; + Point3d ptE ; pCompoTest->GetPointD1D2( ccClass[i].dParE, ICurve::FROM_MINUS, ptE) ; + double dOffE ; pCrvBorder->GetParamAtPoint( ptE, dOffE, 1500 * EPS_SMALL) ; // recupero i due possibili percorsi e uso il più corto - PtrOwner pCrvA( vOffIslands[i]->CopyParamRange( dOffS, dOffE)) ; - PtrOwner pCrvB( vOffIslands[i]->CopyParamRange( dOffE, dOffS)) ; - + PtrOwner pCrvA( pCrvBorder->CopyParamRange( dOffS, dOffE)) ; + PtrOwner pCrvB( pCrvBorder->CopyParamRange( dOffE, dOffS)) ; if ( IsNull( pCrvA) || IsNull( pCrvB)) - return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; - + return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; double dLenA ; pCrvA->GetLength( dLenA) ; double dLenB ; pCrvB->GetLength( dLenB) ; - - if ( j != 0) { + // definisco il nuovo bordo mediante la curva ausiliaria + if ( i != 0) { if ( dLenA < dLenB) { if ( ! pCompoHelp->AddCurve( Release( pCrvA))) - return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; + return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; } else { pCrvB->Invert() ; if ( ! pCompoHelp->AddCurve( Release( pCrvB))) - return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; + return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; } } else { pCrvB->Invert() ; if ( ! pCompoHelp->AddCurve( Release( pCrvB))) - return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; + return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; } } // se non interseco else { - if ( ! pCompoHelp->AddCurve( pCompo->CopyParamRange( ccClass[j].dParS, ccClass[j].dParE))) - return CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink) ; + if ( ! pCompoHelp->AddCurve( pCompoTest->CopyParamRange( ccClass[i].dParS, ccClass[i].dParE))) + return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; } } - - pCompo->Clear() ; - if ( ! pCompo->AddCurve( pCompoHelp->Clone())) - if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink)) + // la curva di test diventa la curva ausiliaria + pCompoTest->Clear() ; + if ( ! pCompoTest->AddCurve( pCompoHelp->Clone())) + if ( ! CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink)) return false ; } - - // controllo le dimensioni del BiArco - BBox3d bBox3 ; - if ( pCompo->GetLocalBBox( bBox3)) { - double dRadBB ; - // se troppo piccolo, lo approssimo con un segmento - if ( bBox3.GetRadius( dRadBB) && dRadBB < 500 * EPS_SMALL) { - if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink)) - return false ; - return true ; - } - // se troppo grande, lo approssimo con un segmento - if ( bBox3.GetRadius( dRadBB) && dRadBB > 3 * PockParams.dRad) { - if ( ! CalcBoundedLink( ptStart, ptEnd, vOffIslands, pCrvLink)) - return false ; - return true ; + // La curva necessita di un ulteriore smusso, in quanto tagliata su un bordo + ModifyCurveToSmoothed( pCompoTest, PockParams, PockParams.dRad / 4., PockParams.dRad / 4., false) ; + // nel caso speciale della circonferenza, devo impostare il punto iniziale + if ( dParMeet < EPS_ZERO) { + if ( pCompoTest->IsClosed()) { // sempre... + double dU = 0. ; + int nFlag = 0 ; + if ( DistPointCurve( ptStart, *pCompoTest).GetParamAtMinDistPoint( 0., dU, nFlag)) + pCompoTest->ChangeStartPoint( dU) ; } } - PtrOwner pCrvCompo_noSmooth( pCompo->Clone()) ; - if ( ! ModifyCurveToSmoothed( pCompo, PockParams, 0.05, 0.05, true) || - ! pCompo->IsValid()) - pCompo.Set( pCrvCompo_noSmooth) ; - pCrvLink->AddCurve( Release( pCompo)) ; - + // restituisco il Link + if ( IsNull( pCompoTest) || ! pCompoTest->IsValid() || pCompoTest->GetCurveCount() == 0) + return CalcBoundedLink( ptStart, ptEnd, vCrvBorders, pCrvLink) ; + pCrvLink->AddCurve( Release( pCompoTest)) ; return true ; } @@ -4856,42 +4946,39 @@ CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, const ICRVCOM pCrvLink->Clear() ; // curva finale da restituire - PtrOwner ptCrvFinal( CreateBasicCurveComposite()) ; - if ( IsNull( ptCrvFinal)) + PtrOwner pCrvFinal( CreateBasicCurveComposite()) ; + if ( IsNull( pCrvFinal)) return false ; - // Prendo i punti, i vettori tangenti e i parametri di essi per le curve - Point3d ptSS, ptSE, ptES, ptEE ; - Vector3d vS, vE ; - double dUSS, dUSE, dUES, dUEE, dLenS, dLenE ; - dUSS = 0 ; dUSE = 0 ; - pCrvS->GetEndPoint( ptSE) ; - pCrvS->GetStartPoint( ptSS) ; - pCrvE->GetStartPoint( ptES) ; - pCrvE->GetEndPoint( ptEE) ; - pCrvS->GetEndDir( vS) ; - pCrvE->GetStartDir( vE) ; - pCrvS->GetDomain( dUSS, dUSE) ; - pCrvE->GetDomain( dUES, dUEE) ; - pCrvS->GetLength( dLenS) ; - pCrvE->GetLength( dLenE) ; + // recupero gli estremi delle due curve ( S = Start, E = End) + Point3d ptSS ; pCrvS->GetStartPoint( ptSS) ; + Point3d ptSE ; pCrvS->GetEndPoint( ptSE) ; + Point3d ptES ; pCrvE->GetStartPoint( ptES) ; + Point3d ptEE ; pCrvE->GetEndPoint( ptEE) ; + // recupero i versori per il BiArco + Vector3d vS ; pCrvS->GetEndDir( vS) ; + Vector3d vE ; pCrvE->GetStartDir( vE) ; + // recupero i domini delle curve + double dUSS, dUSE ; pCrvS->GetDomain( dUSS, dUSE) ; + double dUES, dUEE ; pCrvE->GetDomain( dUES, dUEE) ; + // recupero le lunghezze delle curve + double dLenS ; pCrvS->GetLength( dLenS) ; + double dLenE ; pCrvE->GetLength( dLenE) ; - // se ho una curva di primo Offset allora non devo accorciarla ... ( si per bordi esterni che per isole) + // se ho una curva di primo Offset allora non devo accorciarla ( sia per bordi esterni che per isole) for ( int i = 0 ; i < int( vFirstOffset.size()) ; ++ i) { if ( vFirstOffset[i]->IsPointOn( ptSS) && vFirstOffset[i]->IsPointOn( ptSE)) - dLenPercS = 0 ; - if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE)) - dLenPercE = 0 ; + dLenPercS = 0. ; + if ( vFirstOffset[i]->IsPointOn( ptES) && vFirstOffset[i]->IsPointOn( ptEE)) + dLenPercE = 0. ; } - double dLStepS = ( dLenPercS < EPS_SMALL ? 0. : PockParams.dRad) ; + #if 0 // nel caso volessi estendere anche prima del punto finale + double dLStepS = ( dLenPercS < EPS_SMALL ? 0. : PockParams.dRad) ; + #endif double dLStepE = ( dLenPercE < EPS_SMALL ? 0. : PockParams.dRad) ; dLenE = 0 ; - /* - calcolo il biArco tra il punto iniziale e finale dei due offset consecutivi - se richiesto lo smusso -> accorcio entrambi i tratti di Offset e, riuscendo, si sotituisce - il biArco calcolato prima con quest'ultimo - */ + // recupero il numero massimo di iterazioni per il calcolo del BiArco int nMaxIter = 2 ; if ( ! PockParams.bSmooth) nMaxIter = 1 ; @@ -4901,46 +4988,45 @@ CutCurveToConnect( ICurveComposite* pCrvS, ICurveComposite* pCrvE, const ICRVCOM while ( nIter < nMaxIter) { // calcolo il biArco - PtrOwner ptBiArc( CreateBasicCurveComposite()) ; - if ( ! CalcBoundedSmoothedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, PockParams, ptBiArc) || - ptBiArc->GetCurveCount() == 0) + bool bSpecial = false ; + PtrOwner pCrvBiArc( CreateBasicCurveComposite()) ; + if ( IsNull( pCrvBiArc) || + ! CalcBoundedSmoothedLink( ptSE, vS, ptES, vE, 0.5, vFirstOffset, PockParams, pCrvBiArc, bSpecial) || + pCrvBiArc->GetCurveCount() == 0) return false ; + pCrvFinal->Clear() ; + pCrvFinal.Set( pCrvBiArc->Clone()) ; - ptCrvFinal->Clear() ; - ptCrvFinal.Set( ptBiArc->Clone()) ; - - if ( dLenPercE == 0 && dLenPercS == 0 ) + // se non devo cercarne altri, allora tengo quest'ultimo + if ( ( dLenPercE == 0 && dLenPercS == 0) || bSpecial) break ; - - // se ho trovato un BiArco valido, allora accorcio le due curve di Offset - dLenS -= dLStepS ; + // altrimenti, calcolo un nuovo BiArco, estendendo i suoi estremi + #if 0 // nel caso volessi estendere anche prima del punto finale + dLenS -= dLStepS ; + #endif dLenE += dLStepE ; // ricalcolo il punto iniziale e finale pCrvS->GetParamAtLength( dLenS, dUSE) ; - pCrvS->GetPointD1D2( dUSE, ICurve::FROM_MINUS, ptSE) ; + if ( ! pCrvS->GetPointD1D2( dUSE, ICurve::FROM_MINUS, ptSE, &vS)) + return false ; + vS.Normalize() ; pCrvE->GetParamAtLength( dLenE, dUES) ; - pCrvE->GetPointD1D2( dUES, ICurve::FROM_PLUS, ptES) ; - - // ricalcolo il vettore tangente iniziale e finale - PtrOwner pCrvS_clone( CloneCurveComposite( pCrvS)) ; - PtrOwner pCrvE_clone( CloneCurveComposite( pCrvE)) ; - pCrvS_clone->ChangeStartPoint( dUSE) ; - pCrvE_clone->ChangeStartPoint( dUES) ; - pCrvS_clone->GetStartDir( vS) ; - pCrvE_clone->GetStartDir( vE) ; + if ( ! pCrvE->GetPointD1D2( dUES, ICurve::FROM_PLUS, ptES, &vE)) + return false ; + vE.Normalize() ; ++ nIter ; } - pCrvLink->AddCurve( Release( ptCrvFinal)) ; // ultimo arco valido trovato + pCrvLink->AddCurve( Release( pCrvFinal)) ; // ultimo arco valido trovato return true ; } //---------------------------------------------------------- static bool GetUnclearedRegionAndSetFeed( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, - const ICurveComposite* pCrv_orig, const PocketParams& PockParams, ISurfFlatRegion* pSrfToCut) + const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ISurfFlatRegion* pSrfToCut) { // controllo dei parametri @@ -4948,7 +5034,10 @@ GetUnclearedRegionAndSetFeed( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& return false ; pSrfToCut->Clear() ; - // 1) creo la regione esterna + // creo la regione esterna ( definita dalle curve di primo Offset) + // NB. Questa regione è diversa dalla pSfrOrig : + // - i tratti chiusi sono Offsettati di R + OffsR() + // - i tratti aperti sono stati estesi all'esterno PtrOwner pSrfExtern( CreateSurfFlatRegion()) ; if ( IsNull( pSrfExtern)) return false ; @@ -4964,8 +5053,20 @@ GetUnclearedRegionAndSetFeed( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& } } - // ---- le seguenti regioni sono distinte per calcolar emeglio la Feed ---- - // Creo la regione svuotata dal Tool negli Offset, nei Links e sia dagli Offsets che dai Links + // 2) creo la regione che all'esterno contiene i lati aperti + // NB. Prendendo la superficie originale ( con lati Open/Close impostati), se effettuo un + // Offset interno dell'estensione degli aperti, tutto ciò che è all'esterno di tale regione è classificato + // come lato aperto + PtrOwner pSfrForOpenEdges( CreateSurfFlatRegion()) ; + if ( IsNull( pSfrForOpenEdges)) + return false ; + if ( pSfrOrig != nullptr && pSfrOrig->IsValid()) { + if ( ! pSfrForOpenEdges.Set( CloneSurfFlatRegion( pSfrOrig)) || + ! pSfrForOpenEdges->Offset( 1000 * EPS_SMALL, ICurve::OFF_FILLET)) + return false ; + } + + // definisco la regione rimossa dall'utensile PtrOwner pSfrTool( CreateSurfFlatRegion()) ; if ( IsNull( pSfrTool)) return false ; @@ -4983,19 +5084,23 @@ GetUnclearedRegionAndSetFeed( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& // Feed bool bFirstOffs = false ; - Point3d ptStart ; vOffs[i]->GetStartPoint( ptStart) ; - for ( int j = 0 ; j < int( vFirstOffs.size()) && ! bFirstOffs ; ++ j) - bFirstOffs = vFirstOffs[j]->IsPointOn( ptStart, 100 * EPS_SMALL) ; + if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) + bFirstOffs = true ; + else { + Point3d ptStart ; vOffs[i]->GetStartPoint( ptStart) ; + for ( int j = 0 ; j < int( vFirstOffs.size()) && ! bFirstOffs ; ++ j) + bFirstOffs = vFirstOffs[j]->IsPointOn( ptStart, 100 * EPS_SMALL) ; + } // calcolo gli intervalli di Feed per la curva di Offset - if ( ! AssignFeedSpiral( vOffs[i], pSfrTool, false, bFirstOffs, pCrv_orig, PockParams, 2 * PockParams.dRad / 3)) + if ( ! AssignFeedSpiral( vOffs[i], pSfrTool, false, bFirstOffs, pSfrForOpenEdges, PockParams, 2 * PockParams.dRad / 3)) return false ; // aggiorno la superificie svuotata - PtrOwner pSrfToolRegOffi( GetSurfFlatRegionFromFatCurve( vOffs[i]->Clone() , PockParams.dRad + 5 * EPS_SMALL, false, false)) ; - if ( ! IsNull( pSrfToolRegOffi)) { + PtrOwner pSrfToolOffs( GetSurfFlatRegionFromFatCurve( vOffs[i]->Clone() , PockParams.dRad + 5 * EPS_SMALL, false, false, 10 * EPS_SMALL, false)) ; + if ( ! IsNull( pSrfToolOffs)) { if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0) - pSfrTool.Set( pSrfToolRegOffi) ; + pSfrTool.Set( pSrfToolOffs) ; else - pSfrTool->Add( *pSrfToolRegOffi) ; + pSfrTool->Add( *pSrfToolOffs) ; } vOffs[i]->SetTempProp( nProp0, 0) ; vOffs[i]->SetTempProp( nProp1, 1) ; @@ -5013,15 +5118,15 @@ GetUnclearedRegionAndSetFeed( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& PtrOwner pCrvLink( ConvertCurveToComposite( vLinks[nLink_Ind]->Clone())) ; // calcolo gli intervalli di Feed per la curva di Link if ( IsNull( pCrvLink) || ! pCrvLink->IsValid() || - ! AssignFeedSpiral( pCrvLink, pSfrTool, true, false, pCrv_orig, PockParams, 2 * PockParams.dRad / 3)) + ! AssignFeedSpiral( pCrvLink, pSfrTool, true, false, pSfrForOpenEdges, PockParams, 2 * PockParams.dRad / 3)) return false ; // aggiorno la regione svuotata - PtrOwner pSrfToolRegLinki( GetSurfFlatRegionFromFatCurve( pCrvLink->Clone(), PockParams.dRad + 5 * EPS_SMALL, false, false)) ; - if ( ! IsNull( pSrfToolRegLinki)) { + PtrOwner pSfrToolLink( GetSurfFlatRegionFromFatCurve( pCrvLink->Clone(), PockParams.dRad + 10 * EPS_SMALL, false, false, 10 * EPS_SMALL, false)) ; + if ( ! IsNull( pSfrToolLink)) { if ( ! pSfrTool->IsValid() || pSfrTool->GetChunkCount() == 0) - pSfrTool.Set( pSrfToolRegLinki) ; - else - pSfrTool->Add( *pSrfToolRegLinki) ; + pSfrTool.Set( pSfrToolLink) ; + else + pSfrTool->Add( *pSfrToolLink) ; } // risetto il Link come curva composita ... pCrvLink->SetTempProp( nProp0, 0) ; @@ -5041,8 +5146,108 @@ GetUnclearedRegionAndSetFeed( ICRVCOMPOPOVECTOR& vFirstOffs, ICRVCOMPOPOVECTOR& //---------------------------------------------------------------------------- static bool -RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs, - ICRVCOMPOPOVECTOR& vOffsFirstCurve, const PocketParams& PockParams) +ChooseCurveForRemovingUnclearedRegion( const ICRVCOMPOPOVECTOR& vOffs, const ICRVCOMPOPOVECTOR& vOffsFirstCurve, + const Point3d& ptToGo, const PocketParams& PockParams, + int& nInd, Point3d& ptCloser, bool& bFirstOffs) +{ + /* + E' richiesto che le curve di Offset presentino le seguenti TempProps : + - TempProp0 -> Offset progressivo ricavato ( 0, 1, 2, ... nMaxIter) [per CONFORMAL] + - TempProp1 -> parte esterna della curva ( MDS_LEFT o MDS_LEFT) [per CONFORMAL] + [per SPIRAL le proprietà sono ricavate automaticamente] + */ + + // controllo dei parametri + nInd = -1 ; + if ( vOffs.empty()) + return true ; + + // scorro tutte le curve di Offset + int nFlag ; + struct OffsPtMinDist { + int nInd = 0 ; + double dSqDist = INFINITO - 1 ; + Point3d ptMinDist = P_INVALID ; + bool bInVsOut = true ; + bool bFirstOffs = false ; + } ; + vector vOffsPtMinDist( vOffs.size()) ; + for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) { + // indice dell'Offset + vOffsPtMinDist[i].nInd = i ; + // nel caso di Conformal, escludo le curve di primo Offset dalla ricerca + if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) { + bool bSkip = false ; + for ( int j = 0 ; j < int( vOffsFirstCurve.size()) && ! bSkip ; ++ j) + bSkip = ( vOffs[i]->GetTempProp( 0) == 0) ; // prima iterazione + if ( bSkip) { + vOffsPtMinDist[i].bFirstOffs = true ; + continue ; + } + } + // Controllo se il punto da raggiungere è interno o esterno alla curva + // NB. cerco di privilegiare le curve che presentano il punto da raggiungere al loro esterno; + // in questo modo riesco a creare le curve a ricciolo + int nSide ; + DistPointCurve DistPtCrv( ptToGo, *vOffs[i]) ; + bool bFirstBorderIsland = false ; + if ( DistPtCrv.GetSideAtMinDistPoint( EPS_SMALL, Z_AX, nSide)) { + // nel caso Spiral + if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_SPIRALOUT) { + // InVsOut + vOffsPtMinDist[i].bInVsOut = ( ( ! PockParams.bInvert && nSide == MDS_LEFT) || + ( PockParams.bInvert && nSide == MDS_RIGHT)) ; + // controllo se la curva in questione è di primo Offset + Point3d ptCheck ; vOffs[i]->GetStartPoint( ptCheck) ; + for ( int j = 0 ; j < int( vOffsFirstCurve.size()) ; ++ j) { + if ( vOffsFirstCurve[j]->IsPointOn( ptCheck, 100 * EPS_SMALL)) { + vOffsPtMinDist[i].bFirstOffs = true ; + // controllo se Isola o di bordo + double dArea = 0. ; + vOffs[i]->GetAreaXY( dArea) ; + bFirstBorderIsland = ( ( ! PockParams.bInvert && dArea < 0.) || + ( PockParams.bInvert && dArea > 0.)) ; + // se isola di primo Offset, InVsOut va invertito + if ( bFirstBorderIsland) + vOffsPtMinDist[i].bInVsOut = ( ! vOffsPtMinDist[i].bInVsOut) ; + break ; + } + } + } + // nel caso Conformal + else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG || PockParams.nType == POCKET_CONFORMAL_ONEWAY) { + if ( nSide != vOffs[i]->GetTempProp( 1)) + vOffsPtMinDist[i].bInVsOut = false ; + } + // recupero il punto a minima distanza e la distanza + DistPtCrv.GetMinDistPoint( EPS_SMALL, vOffsPtMinDist[i].ptMinDist, nFlag) ; + DistPtCrv.GetSqDist( vOffsPtMinDist[i].dSqDist) ; + // se il punto da raggiungere è interno alla curva, aggiungo una penalità ( euristica) + if ( vOffsPtMinDist[i].bInVsOut) { + vOffsPtMinDist[i].dSqDist += PockParams.dRad + PockParams.dRad + + PockParams.dSideStep + PockParams.dSideStep ; + } + // se curva di bordo esterno, aggiungo un'altra penalità ( euristica) + if ( vOffsPtMinDist[i].bFirstOffs && ! bFirstBorderIsland) + vOffsPtMinDist[i].dSqDist *= 2. ; + } + } + // ordino il vettore in base alle distanze calcolate + sort( vOffsPtMinDist.begin(), vOffsPtMinDist.end(), []( const OffsPtMinDist& a, const OffsPtMinDist& b) { + return a.dSqDist < b.dSqDist ; + }) ; + // recupero la curva migliore + OffsPtMinDist& BestOffs = vOffsPtMinDist[0] ; + nInd = BestOffs.nInd ; + ptCloser = BestOffs.ptMinDist ; + bFirstOffs = BestOffs.bFirstOffs ; + return true ; +} + +//---------------------------------------------------------------------------- +static bool +RemoveUnclearedRegions( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs, + ICRVCOMPOPOVECTOR& vOffsFirstCurve, const PocketParams& PockParams) { /* E' richiesto che le curve di Offset presentino le seguenti TempProps : @@ -5060,70 +5265,22 @@ RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs // scorro tutti i Chunk della regione ICRVCOMPOPOVECTOR vCrvCurl ; for ( int nC = 0 ; nC < pSfrUncleared->GetChunkCount() ; ++ nC) { - // recupero il centroide del Chunk da rimuovere Point3d ptCentroid ; pSfrUncleared->GetChunkCentroid( nC, ptCentroid) ; - /* Tra tutte le curve di Offset escludo le curve che : - Sono di primo Offset - Non contengono il centroide ( condizione già verificata per quelle di primo Offset) - ... e cerco la più vicina tra le rimanenti + ... cerco la più vicina tra le rimanenti */ + // recupero la curva migliore int nInd = -1 ; - double dRefDist = INFINITO - 1 ; - Point3d ptCloser ; // serve nel caso non bastasse la curva a ricciolo per svuotare il chunk nC-esimo - int nFlag ; + Point3d ptCloser ; bool bFirstOffs = false ; - for ( int i = 0 ; i < int( vOffs.size()) ; ++ i) { - // recupero il punto iniziale della curva - Point3d ptCheck ; vOffs[i]->GetStartPoint( ptCheck) ; - // nel caso di Conformal, escludo le curve di primo Offset dalla ricerca - if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) { - bool bSkip = false ; - for ( int j = 0 ; j < int( vOffsFirstCurve.size()) && ! bSkip ; ++ j) - bSkip = ( vOffs[i]->GetTempProp( 0) == 0) ; - if ( bSkip) - continue ; - } - // se la curva contiene il centroide, allora la salto; l'idea è che il ricciolo sia esterno - // alla curva - int nSide ; - bool bCurrFirstOffs = false ; - DistPointCurve DistPtCrv( ptCentroid, *vOffs[i]) ; - if ( DistPtCrv.GetSideAtMinDistPoint( EPS_SMALL, Z_AX, nSide)) { - // nel caso Spiral - if ( PockParams.nType == POCKET_SPIRALIN || PockParams.nType == POCKET_SPIRALOUT) { - // controllo se la curva in questione è di primo Offset, in questo caso devo determinare - // se MDS_LEFT o MDS_RIGHT - for ( int j = 0 ; j < int( vOffsFirstCurve.size()) && ! bCurrFirstOffs ; ++ j) - bCurrFirstOffs = ( vOffsFirstCurve[j]->IsPointOn( ptCheck, 100 * EPS_SMALL)) ; - // se non è di primo Offset, allora ho già la classificazione MDS_ - if ( ! bCurrFirstOffs) { - if ( nSide == ( ! PockParams.bInvert ? MDS_LEFT : MDS_RIGHT)) - continue ; - } - // se di primo Offset allora non controllo MDS_ - /* NB. Se fosse esterna al loop 0 allora non ha senso, esattamente come se fosse interna alla isole - * La cosa si complica se avessi più Offset, Ma dato che cerco la cura più vicina dovrei - * considerare in automatico le curve di Offset appartenenti al chunk in esame */ - } - // nel caso Conformal - else if ( PockParams.nType == POCKET_CONFORMAL_ZIGZAG) { - if ( nSide == vOffs[i]->GetTempProp( 1)) - continue ; - } - } - // controllo se la distanza è ammissibile - double dCurrDist = INFINITO ; - if ( DistPtCrv.GetSqDist( dCurrDist) && dCurrDist < dRefDist) { - dRefDist = dCurrDist ; - nInd = i ; - bFirstOffs = bCurrFirstOffs ; - DistPtCrv.GetMinDistPoint( EPS_SMALL, ptCloser, nFlag) ; - } - } - // se non ho trovato alcun indice, salto il Chunk nC-esimo + if ( ! ChooseCurveForRemovingUnclearedRegion( vOffs, vOffsFirstCurve, ptCentroid, PockParams, + nInd, ptCloser, bFirstOffs)) + return false ; + // se nessun indice trovato, salto il chunk if ( nInd == -1) continue ; @@ -5142,7 +5299,6 @@ RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs if ( ! bFirstOffs) bIsSmoothCrv = ( PockParams.bSmooth && pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ; else - // controllo se la sottocurva è un raccordo di Offset bIsSmoothCrv = ( pCrv->GetTempProp( 1) > 0) ; // clono la curva di Offset corrente ( in questo modo non modifico l'originale) @@ -5156,74 +5312,82 @@ RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs Point3d pt1, pt2, ptMain ; // paramtro di uscita e di ingresso dall'Offset attuale per creazione della curva a ricciolo double dURef1, dURef2 ; + // numero di curve dell'offset corrente + int nCrvNumber = ( pMyOffs->GetCurveCount()) ; // inizializzo la curva a ricciolo PtrOwner pCrvCurl( CreateCurveComposite()) ; if ( IsNull( pCrvCurl)) return false ; - // se è di raccordo, "ricostruisco lo spigolo vivo della curva originale di Offset" + // se è di raccordo, ricostruisco lo spigolo vivo della curva originale di Offset if ( bIsSmoothCrv) { - // cerco la prima curva in precedenza e in successione non di raccordo vt1 = V_INVALID ; - for ( int i = nCrv - 1 ; i >= 0 ; -- i) { + vt2 = V_INVALID ; + // cerco la prima sottocurva che non sia di raccordo in precedenza + for ( int i = ( nCrv + nCrvNumber - 1) % nCrvNumber ; ( i % nCrvNumber) == i ; -- i) { + // recupero la sottocurva const ICurve* pCrvPrec = pMyOffs->GetCurve( i) ; if ( pCrvPrec == nullptr || ! pCrvPrec->IsValid()) break ; + // se non di raccordo if ( pCrvPrec->GetTempProp( 1) != TEMP_PROP_SMOOTH) { // definisco il versore uscente pCrvPrec->GetEndDir( vt1) ; // definisco il punto di uscita pCrvPrec->GetEndPoint( pt1) ; // definisco il parametro di uscita - dURef1 = i + 1 ; + dURef1 = ( i + 1) % nCrvNumber ; break ; } } - if ( ! vt1.IsValid()) - continue ; - vt2 = V_INVALID ; - for ( int i = nCrv + 1 ; i < pMyOffs->GetCurveCount() ; ++ i) { - const ICurve* pCrvSucc = pMyOffs->GetCurve( i) ; - if ( pCrvSucc == nullptr || ! pCrvSucc->IsValid()) - break ; - if ( pCrvSucc->GetTempProp( 1) != TEMP_PROP_SMOOTH) { - // definisco il versore entrante - pCrvSucc->GetStartDir( vt2) ; - // definisco il punto di entrata - pCrvSucc->GetStartPoint( pt2) ; - // definisco il parametro di entrata - dURef2 = i ; - break ; + bIsSmoothCrv = ( vt1.IsValid()) ; + if ( bIsSmoothCrv) { + // cerco la prima sottocurva che non sia di raccordo in sucessione + for ( int i = ( nCrv + nCrvNumber + 1) % nCrvNumber ; ( i % nCrvNumber) == i ; ++ i) { + // recupero la sottocurva + const ICurve* pCrvSucc = pMyOffs->GetCurve( i) ; + if ( pCrvSucc == nullptr || ! pCrvSucc->IsValid()) + break ; + // se non di raccordo + if ( pCrvSucc->GetTempProp( 1) != TEMP_PROP_SMOOTH) { + // definisco il versore entrante + pCrvSucc->GetStartDir( vt2) ; + // definisco il punto di entrata + pCrvSucc->GetStartPoint( pt2) ; + // definisco il parametro di entrata + dURef2 = ( i % nCrvNumber) ; + break ; + } } } - if ( ! vt2.IsValid()) - continue ; - // trovo il punto di intersezione tra le due direzioni tangenti applicate - const double EXTENSION_LEN = 1000. ; - // segmento iniziale ------- - PtrOwner pLineS( CreateCurveLine()) ; - if ( IsNull( pLineS) || ! pLineS->Set( pt1, pt1 + vt1 * EXTENSION_LEN)) - continue ; - // segmento finale -------- - PtrOwner pLineE( CreateCurveLine()) ; - if ( IsNull( pLineE) || ! pLineE->Set( pt2, pt2 - vt2 * EXTENSION_LEN)) - continue ; - // intersezione - IntersCurveCurve ILL( *pLineS, *pLineE) ; - if ( ILL.GetCrossIntersCount() == 1) { - IntCrvCrvInfo aInfo ; - if ( ILL.GetIntCrvCrvInfo( 0, aInfo)) { - if ( ! pCrvCurl->AddPoint( pt1) || - ! pCrvCurl->AddLine( aInfo.IciA[0].ptI)) - continue ; - ptMain = aInfo.IciA[0].ptI ; + bIsSmoothCrv = bIsSmoothCrv && ( vt2.IsValid()) ; + if ( bIsSmoothCrv) { + // trovo il punto di intersezione tra le due direzioni tangenti applicate + const double EXTENSION_LEN = 1000. ; + // segmento iniziale ------- + PtrOwner pLineS( CreateCurveLine()) ; + if ( IsNull( pLineS) || ! pLineS->Set( pt1, pt1 + vt1 * EXTENSION_LEN)) + continue ; + // segmento finale -------- + PtrOwner pLineE( CreateCurveLine()) ; + if ( IsNull( pLineE) || ! pLineE->Set( pt2, pt2 - vt2 * EXTENSION_LEN)) + continue ; + // intersezione + IntersCurveCurve ILL( *pLineS, *pLineE) ; + bIsSmoothCrv = ( ILL.GetCrossIntersCount() == 1) ; + if ( bIsSmoothCrv) { + IntCrvCrvInfo aInfo ; + bIsSmoothCrv = ( ( ILL.GetIntCrvCrvInfo( 0, aInfo)) && + pCrvCurl->AddPoint( pt1) && + pCrvCurl->AddLine( aInfo.IciA[0].ptI)) ; + if ( bIsSmoothCrv) + ptMain = aInfo.IciA[0].ptI ; } } - else - continue ; } - // se invece non è di raccordo, allora ho già lo spigolo vivo - else { + // se non è di raccordo, allora ho già lo spigolo vivo + if ( ! bIsSmoothCrv) { + pCrvCurl->Clear() ; // definisco il versore entrante ed uscente // definisco il punto di entrata e di uscita ( sono coincidenti) dURef1 = dURef ; @@ -5235,9 +5399,10 @@ RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs } // recupero le due direzioni per i Biarchi bool bSameDir = AreSameVectorEpsilon( vt1, vt2, 2 * EPS_SMALL) ; + bool bSpecial = false ; // se parallele, allora trasformo in una circonferenza if ( bSameDir) { - if ( ! CalcBoundedSmoothedLink( ptMain, V_INVALID, ptCentroid, V_INVALID, 0., vOffsFirstCurve, PockParams, pCrvCurl)) + if ( ! CalcBoundedSmoothedLink( ptMain, vt1, ptCentroid, vt2, 0., vOffsFirstCurve, PockParams, pCrvCurl, bSpecial)) continue ; Vector3d vtStartCheck ; pCrvCurl->GetStartDir( vtStartCheck) ; if ( AreOppositeVectorApprox( vtStartCheck, vt1)) @@ -5256,17 +5421,15 @@ RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs PtrOwner pCrvBiArc1( CreateCurveComposite()) ; PtrOwner pCrvBiArc2( CreateCurveComposite()) ; if ( IsNull( pCrvBiArc1) || IsNull( pCrvBiArc2) || - ! CalcBoundedSmoothedLink( ptMain, vt1, ptCentroid, vtDir, bSameDir ? 0. : 0.5, vOffsFirstCurve, PockParams, pCrvBiArc1) || - ! CalcBoundedSmoothedLink( ptCentroid, vtDir, ptMain, vt2, bSameDir ? 0. : 0.5, vOffsFirstCurve, PockParams, pCrvBiArc2)) + ! CalcBoundedSmoothedLink( ptMain, vt1, ptCentroid, vtDir, bSameDir ? 0. : 0.5, vOffsFirstCurve, PockParams, pCrvBiArc1, bSpecial) || + ! CalcBoundedSmoothedLink( ptCentroid, vtDir, ptMain, vt2, bSameDir ? 0. : 0.5, vOffsFirstCurve, PockParams, pCrvBiArc2, bSpecial)) continue ; - // aggiorno la curva a ricciolo if ( ! pCrvCurl->AddCurve( Release( pCrvBiArc1)) || ! pCrvCurl->AddCurve( Release( pCrvBiArc2)) || ! pCrvCurl->IsValid()) continue ; } - // controllo se devo raccordarmi con Offset if ( bIsSmoothCrv) pCrvCurl->AddLine( pt2) ; @@ -5280,7 +5443,7 @@ RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs return false ; pSfrChunk->Subtract( *pSfrFatCurl) ; - // se la curva a ricciolo non svuota il Chunk nC-esimo -> percorro il bordo di tale Chunk + // controllo se la regione è tutta rimossa if ( pSfrChunk->IsValid() || pSfrChunk->GetChunkCount() > 0) { // so che il punto più vicino alla curva è ptCloser // ricavo dunque il punto più vicino sul bordo del Chunk nC-esimo @@ -5291,16 +5454,22 @@ RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs pCompoChunkBorder->Invert() ; DistPointCurve distPtChunkBorder( ptCloser, *pCompoChunkBorder) ; Point3d ptCloserOnChunk ; + int nFlag ; if ( ! distPtChunkBorder.GetMinDistPoint( EPS_SMALL, ptCloserOnChunk, nFlag)) continue ; // cambio il punto di inizio della curva su tale punto double dUS = 0. ; pCompoChunkBorder->GetParamAtPoint( ptCloserOnChunk, dUS) ; pCompoChunkBorder->ChangeStartPoint( dUS) ; + /* Creo due curve composite rappresentanti un piccolo tratto tangente sulla curva di Offset attuale; questi servono per la partenza e l'arrivo dei biArchi */ + if ( bSameDir) { + pt1 = ptMain ; + pt2 = ptMain ; + } PtrOwner pCompoHelpS( CreateCurveComposite()) ; if ( IsNull( pCompoHelpS) || ! pCompoHelpS->AddPoint( pt1 - vt1 / 4) || ! vt1.Normalize() || ! pCompoHelpS->AddLine( pt1)) @@ -5309,6 +5478,7 @@ RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs if ( IsNull( pCompoHelpE) || ! pCompoHelpE->AddPoint( pt2) || ! vt2.Normalize() || ! pCompoHelpE->AddLine( pt2 + vt2 / 4)) continue ; + /* creo il Biarco iniziale ( nel caso non si riuscisse diventa un segmento lineare) @@ -5330,6 +5500,7 @@ RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs // accorgio il bordo dell'Offset in raccordo al BiArco iniziale if ( dMyU > EPS_SMALL) pCompoChunkBorder->TrimStartAtParam( dMyU) ; + /* creo il Biarco finale ( nel caso non si riuscisse diventa un segmento lineare) @@ -5347,6 +5518,7 @@ RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs if ( ! pCrvBiArc2->GetStartPoint( ptMyPoint) || ! pCompoChunkBorder->GetParamAtPoint( ptMyPoint, dMyU)) continue ; + // la curva a ricciolo ora diventa l'unione di pCrvBiArc1 - pCompoChunkBorder - pCrvBiArc2 pCrvCurl->Clear() ; // smusso la curva di bordo del Chunk ( è aperta) @@ -5366,26 +5538,21 @@ RemoveExtraParts( const ISurfFlatRegion* pSfrUncleared, ICRVCOMPOPOVECTOR& vOffs if ( IsNull( pCrvTest)) return false ; // aggiungo tratto iniziale di Offset - pCrvTest->AddCurve( ConvertCurveToBasicComposite( pMyOffs->CopyParamRange( 0, dURef1))) ; + pCrvTest->AddCurve( ConvertCurveToComposite( pMyOffs->CopyParamRange( 0, dURef1))) ; // aggiungo la curva a ricciolo if ( ! pCrvTest->AddCurve( Release( pCrvCurl))) continue ; // aggiungo il tratto finale di Offset - pCrvTest->AddCurve( ConvertCurveToBasicComposite( pMyOffs->CopyParamRange( dURef2, pMyOffs->GetCurveCount()))) ; - // nel caso di direzioni parallele manca uno smusso - //if ( bSameDir) { - // double dSmoothPar = PockParams.dRad / 8. ; - // ModifyCurveToSmoothed( pCrvTest, PockParams, dSmoothPar, dSmoothPar, false) ; - //} - // provo a controllare che i punti inzili e finali coincidano, in caso aggiorno il nuovo Offset + pCrvTest->AddCurve( ConvertCurveToComposite( pMyOffs->CopyParamRange( dURef2, pMyOffs->GetCurveCount()))) ; + // provo a controllare che i punti inziali e finali coincidano, in caso aggiorno il nuovo Offset Point3d ptStart_Old ; vOffs[nInd]->GetStartPoint( ptStart_Old) ; Point3d ptEnd_Old ; vOffs[nInd]->GetEndPoint( ptEnd_Old) ; Point3d ptStart_New ; pCrvTest->GetStartPoint( ptStart_New) ; Point3d ptEnd_New ; pCrvTest->GetEndPoint( ptEnd_New) ; if ( AreSamePointApprox( ptStart_Old, ptStart_New) && AreSamePointApprox( ptEnd_Old, ptEnd_New)) { - pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 0), 0) ; - pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 1), 1) ; - vOffs[nInd].Set( pCrvTest) ; + pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 0), 0) ; + pCrvTest->SetTempProp( vOffs[nInd]->GetTempProp( 1), 1) ; + vOffs[nInd].Set( pCrvTest) ; } } @@ -5410,42 +5577,67 @@ CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, con Point3d ptS ; if ( ! vOffs[i]->GetStartPoint( ptS)) return false ; - - int nNextInd = -1 ; // indice della curva successiva - double dMinDist = INFINITO ; // distanza tra questa curva e la successiva - Point3d ptStartNext ; // punto iniziale della curva successiva - // tra le curve successive cerco la curva interna e più vicina ad essa + int nNextInd = -1 ; // indice della curva successiva + double dMinDist = INFINITO ; // distanza tra questa curva e la successiva + Point3d ptStartNext ; // punto iniziale della curva successiva + bool bMinDistAtSmooth = false ; // se punto a minima distanza su curva di smusso for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) { IntersCurveCurve IntCC( *vOffs[i], *vOffs[j]) ; CRVCVECTOR ccClass ; // se interna if ( IntCC.GetCurveClassification( 1, EPS_SMALL, ccClass) && - int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_IN) { + int( ccClass.size()) == 1 && + ( ( ! PockParams.bInvert && ccClass[0].nClass == CRVC_IN) || + ( PockParams.bInvert && ccClass[0].nClass == CRVC_OUT))) { // calcolo la distanza minima tra essa int nFlag ; + double dPar = 0. ; Point3d ptClosest ; - if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) { - double dCurrDist = SqDist( ptS, ptClosest) ; - if ( dCurrDist < dMinDist) { - dMinDist = dCurrDist ; - nNextInd = j ; - ptStartNext = ptClosest ; + DistPointCurve DistPtCrv( ptS, *vOffs[j]) ; + if ( DistPtCrv.GetParamAtMinDistPoint( EPS_SMALL, dPar, nFlag)) { + // controllo se la curva ottenuta è di raccordo, in caso positivo considero come + // punto più vicino il suo punto finale ( per avere poi successivamente Link tra + // Offset in tangenza ) + const ICurve* pCrv = vOffs[j]->GetCurve( static_cast( floor( dPar))) ; + if ( pCrv != nullptr && pCrv->IsValid()) { + if ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) + pCrv->GetEndPoint( ptClosest) ; + else + DistPtCrv.GetMinDistPoint( EPS_SMALL, ptClosest, nFlag) ; + double dCurrDist = SqDist( ptS, ptClosest) ; + if ( dCurrDist < dMinDist) { + dMinDist = dCurrDist ; + nNextInd = j ; + ptStartNext = ptClosest ; + bMinDistAtSmooth = ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ; + } } } } } + // se non ho trovato nessuna curva interna... cerco semplicemente la curva più vicina if ( nNextInd == -1) { for ( int j = i + 1 ; j < int( vOffs.size()) ; ++ j) { int nFlag ; + double dPar = 0. ; Point3d ptClosest ; - if ( DistPointCurve( ptS, *vOffs[j]).GetMinDistPoint( EPS_SMALL, ptClosest, nFlag)) { - double dCurrDist = SqDist( ptS, ptClosest) ; - if ( dCurrDist < dMinDist) { - dMinDist = dCurrDist ; - nNextInd = j ; - ptStartNext = ptClosest ; + DistPointCurve DistPtCrv( ptS, *vOffs[j]) ; + if ( DistPtCrv.GetParamAtMinDistPoint( EPS_SMALL, dPar, nFlag)) { + const ICurve* pCrv = vOffs[j]->GetCurve( static_cast( floor( dPar))) ; + if ( pCrv != nullptr && pCrv->IsValid()) { + if ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) + pCrv->GetEndPoint( ptClosest) ; + else + DistPtCrv.GetMinDistPoint( EPS_SMALL, ptClosest, nFlag) ; + double dCurrDist = SqDist( ptS, ptClosest) ; + if ( dCurrDist < dMinDist) { + dMinDist = dCurrDist ; + nNextInd = j ; + ptStartNext = ptClosest ; + bMinDistAtSmooth = ( pCrv->GetTempProp( 1) == TEMP_PROP_SMOOTH) ; + } } } } @@ -5457,7 +5649,6 @@ CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, con // scambio la curva i+1 esima con la j-esima ( se non sono già in ordine) if ( nNextInd != i + 1) swap( vOffs[nNextInd], vOffs[i+1]) ; - // cambio il suo punto iniziale nel punto più vicino tovato double dUS ; if ( ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUS, EPS_SMALL)) @@ -5476,31 +5667,30 @@ CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, con if ( IsNull( pCrvLink)) return false ; // NB. Le curve di Offset da tagliare non devono essere quelle di primo Offset - if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsFirstCurve, PockParams, - ( i == 0 ? 0. : 10 * EPS_SMALL), 10 * EPS_SMALL, pCrvLink) || + double dLenPercS = ( i == 0 ? 0. : 10 * EPS_SMALL) ; + double dLenPercE = ( bMinDistAtSmooth ? 0. : 10 * EPS_SMALL) ; + if ( ! CutCurveToConnect( vOffs[i], vOffs[i+1], vOffsFirstCurve, PockParams, dLenPercS, dLenPercE, pCrvLink) || ! pCrvLink->IsValid()) { - // se non sono riuscito, cerco una strada più semplice - // ripristino le curve + // se non sono riuscito, cerco una strada più semplice ripristinando le curve pCrvLink->Clear() ; vOffs[i].Set( pCrv_i) ; // chiuso vOffs[i+1].Set( pCrv_ii) ; // chiuso - // recupero i vettori tangente iniziali ( le curve sono chiuse ) + // recupero i vettori tangenti iniziali ( le curve sono chiuse ) Vector3d vtS, vtE ; if ( ! vOffs[i]->GetStartDir( vtS) || ! vOffs[i+1]->GetStartDir( vtE)) return false ; // creo il bi-arco tra esse - if ( CalcBoundedSmoothedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink)) + bool bSpecial = false ; + if ( CalcBoundedSmoothedLink( ptS, vtS, ptStartNext, vtE, 0.5, vOffsFirstCurve, PockParams, pCrvLink, bSpecial)) vLinks[i + 1].Set( pCrvLink) ; // aggiorno il collegamento else return false ; - continue ; // passo alla curva i+1 esima successiva } // per sicurezza aggiorno i nuovi punti e i nuovi parametri double dUNewS, dUNewE ; - if ( ! pCrvLink->GetStartPoint( ptS) || - ! pCrvLink->GetEndPoint( ptStartNext) || + if ( ! pCrvLink->GetEndPoint( ptStartNext) || ! vOffs[i]->GetParamAtPoint( ptS, dUNewS) || ! vOffs[i+1]->GetParamAtPoint( ptStartNext, dUNewE)) return false ; @@ -5525,8 +5715,8 @@ CreateSpiralPocketingPath( ICRVCOMPOPOVECTOR& vOffs, ICURVEPOVECTOR& vLinks, con //---------------------------------------------------------------------------- static bool -CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs, - const int nIter, const PocketParams& PockParams, bool& bInsert) +CheckIfOffsetIsNecessary( const ISurfFlatRegion* pSfrAct, const ICurveComposite* pCrvOffs, + double dOffs, double dOffsPrec, int nIter, const PocketParams& PockParams, bool& bInsert) { // controllo dei parametri @@ -5543,70 +5733,51 @@ CheckIfOffsetIsNecessary( const ICurveComposite* pCrvOffs, const double dOffs, double dMaxOffs ; if ( ! CalcCurveLimitOffset( *pCrvOffs, dMaxOffs)) return false ; - // se questa curva sparisce -> non serve inserirla come offset, non svuota parti aggiuntive - if ( dMaxOffs < PockParams.dRad - dOffs + 5 * EPS_SMALL) { + // controllo se l'Offset rimuove materiale + if ( dMaxOffs + ( dOffs - dOffsPrec) - PockParams.dRad < EPS_SMALL) { bInsert = false ; return true ; } - } - else - return true ; - - // se non richiesto percorso avanzato - if ( ! PockParams.bOptOffsetsAdv) - return true ; - - OffsetCurve OffsCrv ; // considero anche i casi in cui ho bSmallRad - if ( ! OffsCrv.Make( pCrvOffs, - PockParams.dRad + dOffs - 5 * EPS_SMALL , ICurve::OFF_FILLET)) - return false ; - - // controllo se richista ottimizzazione per Offsets mediante centroidi e medial Axis - if ( OffsCrv.GetCurveCount() > 1) - return true ; - - PtrOwner pCrvOffLonger( OffsCrv.GetLongerCurve()) ; - if ( IsNull( pCrvOffLonger) || ! pCrvOffLonger->IsValid()) - return true ; - - // Immagine del tool con centro nel centroide della zona da svuotare - Point3d ptC ; pCrvOffLonger->GetCentroid( ptC) ; - PtrOwner pCrvTool( CreateCurveArc()) ; - if( IsNull( pCrvTool)) - return false ; - pCrvTool->SetXY( ptC, PockParams.dRad - 5 * EPS_SMALL) ; - - CRVCVECTOR ccClass ; - IntersCurveCurve intCC( *pCrvOffLonger, *pCrvTool) ; - intCC.GetCurveClassification( 1, EPS_SMALL, ccClass) ; - if ( int( ccClass.size()) == 1 && ccClass[0].nClass == CRVC_OUT) { - // centroide - bInsert = false ; - return true ; - } - - // recupero la PolyLine per altezza e lunghezza del Box2D ( rettangolo) - PolyLine pl ; - if ( ! pCrvOffLonger->ApproxWithLines( 100 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_STD, pl)) - return true ; - Vector3d vtX ; - double dLen = 0 ; double dHeight = 0 ; - if ( ! pl.GetMinAreaRectangleXY( ptC, vtX, dLen, dHeight)) - return true ; - - // frame locale, recupero DimX e DimY - Frame3d frLoc ; frLoc.Set( ptC, Z_AX, vtX) ; - if ( ! frLoc.IsValid()) - return true ; - BBox3d bBox ; - frLoc.Invert() ; - pCrvOffLonger->GetBBox( frLoc, bBox) ; - double dDimX = bBox.GetDimX() ; - double dDimY = bBox.GetDimY() ; - - // se dimensioni accettabili, inserisco - if ( dDimX < 2 * PockParams.dRad - 100 * EPS_SMALL || dDimY < 4 * PockParams.dRad - 100 * EPS_SMALL) { - bInsert = false ; - return true ; + // controllo se l'Offset rimuove una quantità minima di materiale + else if ( dMaxOffs + ( dOffs - dOffsPrec) - PockParams.dRad < TOL_REMOVE_OFFSET) { + // inizializzo la curva di bordo della regione non svuotata + PtrOwner pCrvBorder( CreateCurveComposite()) ; + if ( IsNull( pCrvBorder)) + return false ; + // recupero la regione non svuotata e calcolo la curva di bordo + PtrOwner pSfrA( pSfrAct->CreateOffsetSurf( - dOffsPrec - PockParams.dRad, ICurve::OFF_FILLET)) ; + if ( IsNull( pSfrA)) + return false ; + if ( AreOppositeVectorApprox( pSfrA->GetNormVersor(), Z_AX)) + pSfrA->Invert() ; + for ( int nC = 0 ; nC < pSfrA->GetChunkCount() ; ++ nC) { + PtrOwner pSfrChunk( pSfrA->CloneChunk( nC)) ; + if ( IsNull( pSfrChunk) || ! pSfrChunk->IsValid()) + return false ; + PtrOwner pSfrB( CreateSurfFlatRegion()) ; + if ( IsNull( pSfrB) || ! pSfrB->AddExtLoop( pCrvOffs->Clone())) + return false ; + if ( AreOppositeVectorApprox( pSfrB->GetNormVersor(), Z_AX)) + pSfrB->Invert() ; + if ( pSfrB->Intersect( *pSfrChunk) && pSfrB->IsValid()) { + if ( ! pCrvBorder.Set( ConvertCurveToComposite( pSfrChunk->GetLoop( 0, 0)))) + return false ; + break ; + } + } + if ( pCrvBorder->IsValid()) { + // controllo se l'utensile posizionato nel centroide rimuove la regione + Point3d ptCentroid ; pCrvBorder->GetCentroid( ptCentroid) ; + PtrOwner pCrvArc( CreateCurveArc()) ; + if ( IsNull( pCrvArc)) + return false ; + pCrvArc->Set( ptCentroid, Z_AX, PockParams.dRad - 10 * EPS_SMALL) ; + IntersCurveCurve ICC( *pCrvBorder, *pCrvArc) ; + CRVCVECTOR ccClass ; + bInsert = ( ! ICC.GetCurveClassification( 0, EPS_SMALL, ccClass) || + int( ccClass.size()) != 1 || ccClass[0].nClass != CRVC_IN) ; + } + } } return true ; @@ -5650,7 +5821,7 @@ IsCompoMadeBy2DifferentHomogeneousParts( const ICurveComposite* pCompo, const Po //---------------------------------------------------------------------------- static bool -CalcSpiral( const ISurfFlatRegion* pSfrPock, const PocketParams& PockParams, int& nReg, Point3d& ptStart, +CalcSpiral( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, int& nReg, Point3d& ptStart, ICRVCOMPOPOVECTOR& vCrvOrigChunkLoops, ICurveComposite* pMCrv, bool& bMidOut, Vector3d& vtMidOut) { // inizializzo il percorso come vuoto @@ -5716,7 +5887,7 @@ CalcSpiral( const ISurfFlatRegion* pSfrPock, const PocketParams& PockParams, int pCrvCompoBorder->Invert() ; // controllo quali regioni di Offset possono essere sostituite bool bInsert = true ; - if ( ! CheckIfOffsetIsNecessary( pCrvCompoBorder, dOffs - dOffsPrec, nIter, PockParams, bInsert)) + if ( ! CheckIfOffsetIsNecessary( pSrfAct, pCrvCompoBorder, dOffs, dOffsPrec, nIter, PockParams, bInsert)) return false ; if ( bInsert) vOffs.emplace_back( Release( pCrvCompoBorder)) ; @@ -5814,9 +5985,9 @@ CalcSpiral( const ISurfFlatRegion* pSfrPock, const PocketParams& PockParams, int // controllo eventuali parti non svuotate ( e setto la Feed degli Offset e dei Link)... PtrOwner pSfrUncleared( CreateSurfFlatRegion()) ; - if ( GetUnclearedRegionAndSetFeed( vOffsFirstCurve, vOffs, vLinks, vCrvOrigChunkLoops[0], PockParams, pSfrUncleared)) { + if ( GetUnclearedRegionAndSetFeed( vOffsFirstCurve, vOffs, vLinks, pSfrOrig, PockParams, pSfrUncleared)) { // modifico i percorsi - if ( ! RemoveExtraParts( pSfrUncleared, vOffs, vOffsFirstCurve, PockParams)) + if ( ! RemoveUnclearedRegions( pSfrUncleared, vOffs, vOffsFirstCurve, PockParams)) return false ; } @@ -6379,7 +6550,7 @@ AddSpiralIn( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, // calcolo il percorso di svuotatura bool bSomeOpen ; Vector3d vtMidOut ; - if ( ! CalcSpiral( pSfrChunk, PockParams, nRegTot, ptStart, vCrvOrigChunkLoops, pMCrv, bSomeOpen, vtMidOut)) + if ( ! CalcSpiral( pSfrChunk, pSfrOrig, PockParams, nRegTot, ptStart, vCrvOrigChunkLoops, pMCrv, bSomeOpen, vtMidOut)) return false ; // se terminate le regioni, esco @@ -6456,7 +6627,7 @@ AddSpiralOut( ISurfFlatRegion* pSrfPock, const ISurfFlatRegion* pSfrOrig, // calcolo il percorso di svuotatura bool bSomeOpen ; Vector3d vtMidOut ; - if ( ! CalcSpiral( pSfrChunk, PockParams, nRegTot, ptStart, vCrvOrig, pMCrv, bSomeOpen, vtMidOut)) + if ( ! CalcSpiral( pSfrChunk, pSfrOrig, PockParams, nRegTot, ptStart, vCrvOrig, pMCrv, bSomeOpen, vtMidOut)) return false ; // se terminate le regioni, esco @@ -7093,7 +7264,8 @@ GetConformalLink( ICurveComposite* pCrvOffs0, ICurveComposite* pCrvOffs1, Pocket ! pCrvOffs0->GetStartPoint( ptS) || ! pCrvOffs1->GetStartPoint( ptE)) return false ; // creo il bi-arco tra esse - if ( ! CalcBoundedSmoothedLink( ptS, vtS, ptE, vtE, 0.5, vCrvChunk, PockParams, pCrvLink) || + bool bSpecial = false ; + if ( ! CalcBoundedSmoothedLink( ptS, vtS, ptE, vtE, 0.5, vCrvChunk, PockParams, pCrvLink, bSpecial) || ! pCrvLink->IsValid()) return false ; } @@ -7621,7 +7793,7 @@ CalcConformalOffsAndLinks( VICRVCOMPOPOVECTOR& vvCrvOffs, const ISurfFlatRegion* //---------------------------------------------------------------------------- static bool -ExtendConformalOffsAndSetFeed( const ISurfFlatRegion* pSfrPock, const PocketParams& PockParams, +ExtendConformalOffsAndSetFeed( const ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, const PocketParams& PockParams, ICRVCOMPOPOVECTOR& vCrvOffs, ICURVEPOVECTOR& vCrvLinks) { // controllo dei parametri @@ -7641,9 +7813,9 @@ ExtendConformalOffsAndSetFeed( const ISurfFlatRegion* pSfrPock, const PocketPara PtrOwner pSfrUncleared( CreateSurfFlatRegion()) ; if ( IsNull( pSfrUncleared)) return false ; - if ( GetUnclearedRegionAndSetFeed( vCrvFirstOffs, vCrvOffs, vCrvLinks, nullptr, PockParams, pSfrUncleared)) { + if ( GetUnclearedRegionAndSetFeed( vCrvFirstOffs, vCrvOffs, vCrvLinks, pSfrOrig, PockParams, pSfrUncleared)) { // estendo i percorsi di Offset se richiesto - if ( ! RemoveExtraParts( pSfrUncleared, vCrvOffs, vCrvFirstOffs, PockParams)) + if ( ! RemoveUnclearedRegions( pSfrUncleared, vCrvOffs, vCrvFirstOffs, PockParams)) return false ; } @@ -7665,18 +7837,16 @@ AddLeadInLeadOutToCurveConformalPaths( const ISurfFlatRegion* pSfrOrig, const IS } // ricavo le curve aperte della superficie di pocketing + ICRVCOMPOPOVECTOR vCrvLoops ; + if ( ! GetSfrCrvCompoLoops( pSfrPock, vCrvLoops)) + return false ; ICRVCOMPOPOVECTOR vCrvOpenEdge ; - { - ICRVCOMPOPOVECTOR vCrvLoops ; - if ( ! GetSfrCrvCompoLoops( pSfrPock, vCrvLoops)) - return false ; - for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) { - ICRVCOMPOPOVECTOR vpCrvs ; - GetHomogeneousParts( vCrvLoops[i], PockParams, vpCrvs) ; - for ( int j = 0 ; j < int( vpCrvs.size()) ; ++ j) { - if ( vpCrvs[j]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) - vCrvOpenEdge.emplace_back( Release( vpCrvs[j])) ; - } + for ( int i = 0 ; i < int( vCrvLoops.size()) ; ++ i) { + ICRVCOMPOPOVECTOR vpCrvs ; + GetHomogeneousParts( vCrvLoops[i], PockParams, vpCrvs) ; + for ( int j = 0 ; j < int( vpCrvs.size()) ; ++ j) { + if ( vpCrvs[j]->GetTempProp( 0) == TEMP_PROP_OPEN_EDGE) + vCrvOpenEdge.emplace_back( Release( vpCrvs[j])) ; } } @@ -8372,8 +8542,14 @@ GetConformalOffsets( const ISurfFlatRegion* pSfrChunk, const ISurfFlatRegion* pS } } // concateno se necessario ( per tolleranza Offset) + int nTempProp0 = ( ! vCrvOffsInside.empty() ? vCrvOffsInside[0]->GetTempProp( 0) : TEMP_PROP_INVALID) ; + int nTempProp1 = ( ! vCrvOffsInside.empty() ? vCrvOffsInside[0]->GetTempProp( 1) : TEMP_PROP_INVALID) ; if ( ! ChainCompoCurves( vCrvOffsInside)) return false ; + for ( auto& CrvCompo : vCrvOffsInside) { + CrvCompo->SetTempProp( nTempProp0, 0) ; + CrvCompo->SetTempProp( nTempProp1, 1) ; + } // se ho trovato delle curve interne if ( ! bStop) { // inserisco le curva ricavate all'iterazione nIter nel vettore @@ -8435,11 +8611,15 @@ AddConformal( ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, return true ; // se superifice tutta aperta, lavoro in SPIRAL_IN - if ( PockParams.bAllOpen) + if ( PockParams.bAllOpen) { + PockParams.nType = POCKET_SPIRALIN ; return ( AddSpiralIn( pSfrPock, pSfrOrig, PockParams, vCrvCompoRes)) ; + } // se superficie tutta chiusa, lavoro in SPIRAL_OUT - if ( PockParams.bAllClosed) + if ( PockParams.bAllClosed) { + PockParams.nType = POCKET_SPIRALOUT ; return ( AddSpiralOut( pSfrPock, pSfrOrig, PockParams, vCrvCompoRes)) ; + } // scorro i chunk della superficie da lavorare for ( int nC = 0 ; nC < pSfrPock->GetChunkCount() ; ++ nC) { @@ -8453,13 +8633,19 @@ AddConformal( ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, return false ; // se Chunk tutto aperto, lo lavoro in SPIRAL_IN if ( bOpen) { + int nType = PockParams.nType ; + PockParams.nType = POCKET_SPIRALIN ; if ( ! AddSpiralIn( pSfrChunk, pSfrOrig, PockParams, vCrvCompoRes)) return false ; + PockParams.nType = nType ; } // se Chunk tutto chiuso, lo lavoro in SPIRAL_OUT else if ( bClose) { + int nType = PockParams.nType ; + PockParams.nType = POCKET_SPIRALOUT ; if ( ! AddSpiralOut( pSfrChunk, pSfrOrig, PockParams, vCrvCompoRes)) return false ; + PockParams.nType = nType ; } // se Chunk non omogeneo else { @@ -8469,10 +8655,13 @@ AddConformal( ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, return false ; // se non ottengo Curve di Offset, lavoro in SpiralIn ( il Chunk ha dei lati aperti) if ( vvCrvOffs.empty()) { + int nType = PockParams.nType ; + PockParams.nType = POCKET_SPIRALIN ; PtrOwner pSfrChunk_cl( CloneSurfFlatRegion( pSfrChunk)) ; if ( IsNull( pSfrChunk_cl) || ! pSfrChunk_cl->IsValid() || ! AddSpiralIn( pSfrChunk_cl, pSfrOrig, PockParams, vCrvCompoRes)) return false ; + PockParams.nType = nType ; } else { // definisco vettore degli Offset e dei Link @@ -8481,16 +8670,19 @@ AddConformal( ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, // ordino le curve di Offset e raccordo if ( ! CalcConformalOffsAndLinks( vvCrvOffs, pSfrChunk, pSfrPock, PockParams, vCrvOffs, vCrvLink)) { // se calcolare i raccordi risulta troppo ambiguo o complesso, ritorno a SpiralIn + int nType = PockParams.nType ; + PockParams.nType = POCKET_SPIRALIN ; PtrOwner pSfrChunk_cl( CloneSurfFlatRegion( pSfrChunk)) ; if ( IsNull( pSfrChunk_cl) || ! pSfrChunk_cl->IsValid() || ! AddSpiralIn( pSfrChunk_cl, pSfrOrig, PockParams, vCrvCompoRes)) return false ; + PockParams.nType = nType ; continue ; } // flag per controllo bool bOk = true ; // estendo i percorsi per eventuali regioni non svuotate e calcolo le Feed - bOk = bOk && ExtendConformalOffsAndSetFeed( pSfrPock, PockParams, vCrvOffs, vCrvLink) ; + bOk = bOk && ExtendConformalOffsAndSetFeed( pSfrPock, pSfrOrig, PockParams, vCrvOffs, vCrvLink) ; // concateno Offset e Links ICRVCOMPOPOVECTOR vCrvPaths ; bOk = bOk && ChainConformalOffsWithLinks( vCrvOffs, vCrvLink, vCrvPaths) ; @@ -8498,10 +8690,13 @@ AddConformal( ISurfFlatRegion* pSfrPock, const ISurfFlatRegion* pSfrOrig, bOk = bOk && OrderAndExtendConformalPaths( vCrvPaths, pSfrChunk, pSfrOrig, PockParams) ; if ( ! bOk) { // se qualche passaggio restituisce errore, provo in SpiralIn + int nType = PockParams.nType ; + PockParams.nType = POCKET_SPIRALIN ; PtrOwner pSfrChunk_cl( CloneSurfFlatRegion( pSfrChunk)) ; if ( IsNull( pSfrChunk_cl) || ! pSfrChunk_cl->IsValid() || ! AddSpiralIn( pSfrChunk_cl, pSfrOrig, PockParams, vCrvCompoRes)) return false ; + PockParams.nType = nType ; } else { // aggiungo i percorsi ricavati @@ -8529,7 +8724,7 @@ SmoothExtensionLinesByIntersection( ICRVCOMPOPOVECTOR& vCrvPaths, const PocketPa for ( int i = 0 ; i < int( vCrvPaths.size()) - 1 ; ++ i) { // se uno dei due percorsi presenta meno di 3 curve, non faccio nulla if ( ! vCrvPaths[i]->IsValid() || vCrvPaths[i]->GetCurveCount() < 3 || - ! vCrvPaths[i+1]->IsValid() || vCrvPaths[i+1]->GetCurveCount() < 3) + ! vCrvPaths[i+1]->IsValid() || vCrvPaths[i+1]->GetCurveCount() < 3) continue ; // se il percorso i-esimo non presenta una estensione in uscita e il percorso (i+1)-esimo // non presenta una estensione in entrata, non faccio nulla @@ -8615,7 +8810,7 @@ bool CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double dStep, double dAngle, double dOpenMinSafe, int nType, bool bSmooth, bool bInvert, bool bAvoidOpt, bool bAllowZigZagOneWayBorders, bool bCalcFeed, const Point3d& ptEndPrec, const ISurfFlatRegion* pSfrLimit, bool bAllOffs, - double dMaxOptSize, ICRVCOMPOPOVECTOR& vCrvCompoRes) + double dMaxOptSize, double dLiTang, int nLiType, ICRVCOMPOPOVECTOR& vCrvCompoRes) { // controllo dei parametri if ( pSfr == nullptr || ! pSfr->IsValid() || @@ -8642,6 +8837,8 @@ CalcPocketing( const ISurfFlatRegion* pSfr, double dRad, double dRadOffs, double myParams.bAllowZigZagOneWayBorders = bAllowZigZagOneWayBorders ; myParams.bOptOffsets = ( ! bAllOffs) ; myParams.dMaxOptSize = dMaxOptSize ; + myParams.dLiTang = dLiTang ; + myParams.nLiType = nLiType ; if ( ptEndPrec.IsValid()) myParams.ptStart = ptEndPrec ; if ( pSfrLimit != nullptr && pSfrLimit->IsValid()) @@ -9014,6 +9211,9 @@ CalcZigZagInfill( const ISurfFlatRegion* pSfr, double dStep, bool bSmooth, bool PocketParams myParams ; myParams.bSmooth = false ; myParams.dRad = 0.5 * dStep ; + myParams.bCalcFeed = false ; + myParams.dSideStep = dStep ; + myParams.bInvert = false ; // vettore con i percorsi a ZigZag ICRVCOMPOPOVECTOR vpCrvs ; diff --git a/CurveAux.cpp b/CurveAux.cpp index 4bc1c70..a47fb8e 100644 --- a/CurveAux.cpp +++ b/CurveAux.cpp @@ -2043,13 +2043,14 @@ CalcCurvesMedialAxis( const CICURVEPVECTOR& vCrvC, ICURVEPOVECTOR& vCrvs, int nS //---------------------------------------------------------------------------- bool -CalcCurveFatCurve( const ICurve& crvC, ICURVEPOVECTOR& vCrvs, double dRadius, bool bSquareEnds, bool bSquareMids) +CalcCurveFatCurve( const ICurve& crvC, ICURVEPOVECTOR& vCrvs, double dRadius, bool bSquareEnds, bool bSquareMids, + bool bMergeOnlySameProps) { Voronoi* pVoronoiObj = GetCurveVoronoi( crvC) ; if ( pVoronoiObj == nullptr) return false ; - return pVoronoiObj->CalcFatCurve( vCrvs, dRadius, bSquareEnds, bSquareMids) ; + return pVoronoiObj->CalcFatCurve( vCrvs, dRadius, bSquareEnds, bSquareMids, bMergeOnlySameProps) ; } //---------------------------------------------------------------------------- diff --git a/CurveComposite.cpp b/CurveComposite.cpp index 35bc353..fa9f197 100644 --- a/CurveComposite.cpp +++ b/CurveComposite.cpp @@ -192,7 +192,7 @@ CurveComposite::AddSimpleCurve( ICurve* pSmplCrv, bool bEndOrStart, double dLinT return false ; // verifico lo stato - if ( m_nStatus != OK && ! ( m_CrvSmplS.empty() && m_nStatus == TO_VERIFY)) + if ( m_nStatus != OK && ! ( m_CrvSmplS.empty() && ( m_nStatus == TO_VERIFY || m_nStatus == IS_A_POINT))) return false ; // controllo la tolleranza @@ -1778,10 +1778,11 @@ bool CurveComposite::AddPoint( const Point3d& ptStart) { // verifico lo stato - if ( m_nStatus != TO_VERIFY) + if ( m_nStatus != TO_VERIFY && m_nStatus != IS_A_POINT) return false ; - // assegno il punto + // assegno il punto e setto lo stato m_ptStart = ptStart ; + m_nStatus = IS_A_POINT ; return true ; } @@ -1825,7 +1826,7 @@ bool CurveComposite::AddLine( const Point3d& ptNew, bool bEndOrStart) { // verifico lo stato - if ( m_nStatus != OK && m_nStatus != TO_VERIFY) + if ( m_nStatus != OK && m_nStatus != IS_A_POINT) return false ; // costruisco la linea PtrOwner pLine( CreateBasicCurveLine()) ; @@ -3168,11 +3169,26 @@ MergeTwoCurves( ICurve* pCrvP, ICurve* pCrvC, double& dCurrLinTol, double dCosAn // se precedente molto corta double dLenP ; if ( pCrvP->GetLength( dLenP) && dLenP < dCurrLinTol) { - // se abbastanza allineata alla successiva + // se abbastanza allineata alla successiva Vector3d vtDirP, vtDirC ; if ( pCrvP->GetEndDir( vtDirP) && pCrvC->GetStartDir( vtDirC) && ( vtDirP * vtDirC) >= dCosAngTol) { - Point3d ptStart ; - return ( pCrvP->GetStartPoint( ptStart) && pCrvC->ModifyStart( ptStart) ? -1 : 0) ; + bool bModifStart = ( pCrvC->GetType() != CRV_ARC) ; + if ( ! bModifStart) { + /* nel caso in cui la curva corrente sia un arco, bisogna controllare che la somma tra + l'angolo al centro e l'angolo sotteso dalla curva precedente non superi l'angolo giro; in + caso positivo, la modifica del punto inziale dell'arco ( curva corrente) rimoverebbe + tutti gli angoli superiori a 360deg [curve a ricciolo per regioni non svuotate in Pocketing] */ + Point3d ptS ; pCrvP->GetStartPoint( ptS) ; + Point3d ptE ; pCrvC->GetStartPoint( ptE) ; + const ICurveArc* pArcC = GetBasicCurveArc( pCrvC) ; + double dAngRef = ( Dist( ptS, ptE) / pArcC->GetRadius()) * RADTODEG ; + bModifStart = ( abs( pArcC->GetAngCenter()) + dAngRef < ANG_FULL - 10 * EPS_ANG_SMALL) ; + + } + if ( bModifStart) { + Point3d ptStart ; + return ( pCrvP->GetStartPoint( ptStart) && pCrvC->ModifyStart( ptStart) ? -1 : 0) ; + } } } // se corrente molto corta @@ -3181,10 +3197,24 @@ MergeTwoCurves( ICurve* pCrvP, ICurve* pCrvC, double& dCurrLinTol, double dCosAn // se abbastanza allineata alla precedente Vector3d vtDirP, vtDirC ; if ( pCrvP->GetEndDir( vtDirP) && pCrvC->GetStartDir( vtDirC) && ( vtDirP * vtDirC) >= dCosAngTol) { - Point3d ptEnd ; - return ( pCrvC->GetEndPoint( ptEnd) && pCrvP->ModifyEnd( ptEnd) ? 1 : 0) ; + bool bModifEnd = ( pCrvP->GetType() != CRV_ARC) ; + if ( ! bModifEnd) { + /* nel caso in cui la curva predecente sia un arco, bisogna controllare che la somma tra + l'angolo al centro e l'angolo sotteso dalla curva corrente non superi l'angolo giro; in + caso positivo, la modifica del punto finale dell'arco ( curva precedente) rimoverebbe + tutti gli angoli superiori a 360deg [curve a ricciolo per regioni non svuotate in Pocketing] */ + Point3d ptS ; pCrvP->GetEndPoint( ptS) ; + Point3d ptE ; pCrvC->GetEndPoint( ptE) ; + const CurveArc* pArcP = GetBasicCurveArc( pCrvP) ; + double dAngRef = ( Dist( ptS, ptE) / pArcP->GetRadius()) * RADTODEG ; + bModifEnd = ( abs( pArcP->GetAngCenter()) + dAngRef < ANG_FULL - 10. * EPS_ANG_SMALL) ; + } + if ( bModifEnd) { + Point3d ptEnd ; + return ( pCrvC->GetEndPoint( ptEnd) && pCrvP->ModifyEnd( ptEnd) ? 1 : 0) ; + } } - } + } // coefficiente deduzione tolleranza const double COEFF_TOL = 0.7 ; // se entrambe rette @@ -3279,8 +3309,6 @@ MergeTwoCurves( ICurve* pCrvP, ICurve* pCrvC, double& dCurrLinTol, double dCosAn CurveArc NewArc ; if ( NewArc.Set3P( ptP1, ptP2, ptP3, bCirc)) { // se vicino a circonferenza arco per 3 punti potrebbe non dare il risultato desiderato quindi faccio controllo su raggio e centro - double dDist = Dist( NewArc.GetCenter(), ptC1Fin) ; - double dDelta = abs( NewArc.GetRadius() - pArcP->GetRadius()) ; if ( Dist( NewArc.GetCenter(), ptC1Fin) > 2 * dCurrLinTol || abs( NewArc.GetRadius() - pArcP->GetRadius()) > 2 * dCurrLinTol) return 0 ; @@ -3801,19 +3829,6 @@ CurveComposite::ResetVoronoiObject() const m_pVoronoiObj = nullptr ; } -//---------------------------------------------------------------------------- -bool -CurveComposite::FromPoint(Point3d& ptStart) -{ - // verifico lo stato - if ( m_nStatus != TO_VERIFY) - return false ; - // assegno il punto e setto lo stato - m_ptStart = ptStart ; - m_nStatus = IS_A_POINT ; - return true ; -} - //---------------------------------------------------------------------------- bool CurveComposite::GetOnlyPoint(Point3d& ptStart) const diff --git a/CurveComposite.h b/CurveComposite.h index 1c9b2eb..646105e 100644 --- a/CurveComposite.h +++ b/CurveComposite.h @@ -177,8 +177,7 @@ class CurveComposite : public ICurveComposite, public IGeoObjRW bool GetCurveTempProp( int nCrv, int& nProp, int nPropInd = 0) const override ; bool SetCurveTempParam( int nCrv, double dParam, int nParamInd = 0) override ; bool GetCurveTempParam( int nCrv, double& dParam, int nParamInd = 0) const override ; - bool FromPoint( Point3d& ptStart) override ; // funzione per settare la curva ad un unico punto - bool GetOnlyPoint( Point3d& ptStart) const override ; // funzione per recuperare l'unico punto da cui è composta la curva ( degenere) + bool GetOnlyPoint( Point3d& ptStart) const override ; public : // IGeoObjRW int GetNgeId( void) const override ; diff --git a/EgtGeomKernel.rc b/EgtGeomKernel.rc index f7add89..bfc6520 100644 Binary files a/EgtGeomKernel.rc and b/EgtGeomKernel.rc differ diff --git a/EgtGeomKernel.vcxproj b/EgtGeomKernel.vcxproj index fe37790..b8b6bc4 100644 --- a/EgtGeomKernel.vcxproj +++ b/EgtGeomKernel.vcxproj @@ -320,6 +320,8 @@ copy $(TargetPath) \EgtProg\Dll64 + + diff --git a/EgtGeomKernel.vcxproj.filters b/EgtGeomKernel.vcxproj.filters index a313b23..6120010 100644 --- a/EgtGeomKernel.vcxproj.filters +++ b/EgtGeomKernel.vcxproj.filters @@ -546,6 +546,12 @@ File di origine\GeoDist + + File di origine\Geo + + + File di origine\Geo + diff --git a/PolygonPlane.cpp b/PolygonPlane.cpp index 54cdcbe..1f2a567 100644 --- a/PolygonPlane.cpp +++ b/PolygonPlane.cpp @@ -19,10 +19,10 @@ void PolygonPlane::AddPoint( const Point3d& ptP) { - // se è il primo punto (parto da -1 perchè verrà contato alla chiusura) + // se è il primo punto (parto da -1 perchè verrà contato alla chiusura) if ( m_nPntNbr == -1) { // inizializzazioni - m_dLenN = 0 ; + m_dLenN = -1 ; m_vtN = V_NULL ; m_ptMid = ORIG ; m_dSXy = 0 ; m_dSXz = 0 ; @@ -60,16 +60,16 @@ PolygonPlane::AddPoint( const Point3d& ptP) bool PolygonPlane::Finalize( void) { - // almeno 3 punti (il triangolo è il poligono con minimo numero di lati) + // almeno 3 punti (il triangolo è il poligono con minimo numero di lati) if ( m_nPntNbr + 1 < 3) return false ; - // se il poligono non è stato chiuso, aggiungo calcolo per ultimo lato + // se il poligono non è stato chiuso, aggiungo calcolo per ultimo lato if ( ! AreSamePointExact( m_ptFirst, m_ptLast)) { // aggiungo il primo punto per far eseguire i conti sul lato di chiusura AddPoint( m_ptFirst) ; } // se non effettuato, eseguo il calcolo finale - if ( m_dLenN < EPS_SMALL) { + if ( m_dLenN < 0) { // lunghezza della normale (doppio dell'area del poligono) m_dLenN = m_vtN.Len() ; if ( m_dLenN < SQ_EPS_SMALL) diff --git a/SfrCreate.cpp b/SfrCreate.cpp index cc18437..4535e90 100644 --- a/SfrCreate.cpp +++ b/SfrCreate.cpp @@ -115,7 +115,8 @@ GetSurfFlatRegionDisk( double dRadius) //------------------------------------------------------------------------------- ISurfFlatRegion* -GetSurfFlatRegionFromFatCurve( ICurve* pCrv, double dRadius, bool bSquareEnds, bool bSquareMids, double dOffsLinTol) +GetSurfFlatRegionFromFatCurve( ICurve* pCrv, double dRadius, bool bSquareEnds, bool bSquareMids, double dOffsLinTol, + bool bMergeOnlySameProps) { // metodo di calcolo impostato da USE_VORONOI @@ -330,7 +331,7 @@ GetSurfFlatRegionFromFatCurve( ICurve* pCrv, double dRadius, bool bSquareEnds, b else { // calcolo la fat curve con Voronoi ICURVEPOVECTOR vFatCurves ; - if ( ! CalcCurveFatCurve( *pCurve, vFatCurves, dRadius, bSquareEnds, bSquareMids)) + if ( ! CalcCurveFatCurve( *pCurve, vFatCurves, dRadius, bSquareEnds, bSquareMids, bMergeOnlySameProps)) return nullptr ; // costruisco la superficie a partire dalle curve diff --git a/SurfTriMesh.cpp b/SurfTriMesh.cpp index 87e5ffb..0461250 100644 --- a/SurfTriMesh.cpp +++ b/SurfTriMesh.cpp @@ -237,6 +237,10 @@ SurfTriMesh::AddTriangle( const int nIdVert[3], int nTFlag) // inserisco il triangolo try { m_vTria.emplace_back( nIdVert, nTFlag) ;} catch(...) { return SVT_NULL ;} + // aggiorno la sua normale + if ( ! vtN.Normalize( EPS_ZERO)) + return false ; + m_vTria.back().vtN = vtN ; // aggiorno massimo TFlag m_nMaxTFlag = max( m_nMaxTFlag, nTFlag) ; // ne determino l'indice @@ -1220,16 +1224,17 @@ SurfTriMesh::GetSilhouette( const Plane3d& plPlane, double dTol, POLYLINEVECTOR& // se rimasto qualcosa if ( pgTria.GetSideCount() > 0) { // lo proietto sul piano e creo la regione - pgTria.Scale( frOCS, 1, 1, 0) ; - PtrOwner pSfrTria( GetBasicSurfFlatRegion( GetSurfFlatRegionFromPolyLine( pgTria.GetPolyLine()))) ; - if ( ! IsNull( pSfrTria)) { - if ( bAllTria && Tria.GetN() * vtVers < 0) - pSfrTria->Invert() ; - pSfrTria->Offset( dTol, ICurve::OFF_FILLET) ; - if ( IsNull( pSfr)) - pSfr.Set( pSfrTria) ; - else - pSfr->Add( *pSfrTria) ; + if ( pgTria.Scale( frOCS, 1, 1, 0)) { + PtrOwner pSfrTria( GetBasicSurfFlatRegion( GetSurfFlatRegionFromPolyLine( pgTria.GetPolyLine()))) ; + if ( ! IsNull( pSfrTria)) { + if ( bAllTria && Tria.GetN() * vtVers < 0) + pSfrTria->Invert() ; + pSfrTria->Offset( dTol, ICurve::OFF_FILLET) ; + if ( IsNull( pSfr)) + pSfr.Set( pSfrTria) ; + else + pSfr->Add( *pSfrTria) ; + } } } } @@ -1687,11 +1692,13 @@ SurfTriMesh::AdjustVertices( void) //---------------------------------------------------------------------------- bool -SurfTriMesh::AdjustAdjacencies( void) +SurfTriMesh::AdjustAdjacencies( bool AdjustVert) { // sistemo le relazioni tra triangoli e vertici - if ( ! AdjustVertices()) - return false ; + if ( AdjustVert) { + if ( ! AdjustVertices()) + return false ; + } // matrice di incidenza vertici-triangoli INTMATRIX mVertTria( GetVertexSize()) ; for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { @@ -1874,7 +1881,7 @@ SurfTriMesh::TestSealing( void) m_vTria[i].nIdAdjac[1] == SVT_NULL || m_vTria[i].nIdAdjac[2] == SVT_NULL) { bClosed = false ; - break ; + break ; } } } @@ -1936,7 +1943,7 @@ SurfTriMesh::PackVertices( void) ++ nFirstFree ; } else - vVId.push_back( nId) ; + vVId.push_back( nId) ; } else { if ( nFirstFree == SVT_NULL) @@ -3043,7 +3050,7 @@ SurfTriMesh::AddBiTriangle( const int nIdVert[4]) //---------------------------------------------------------------------------- bool SurfTriMesh::DoCompacting( double dTol) -{ +{ // imposto ricalcolo m_nStatus = ERR ; m_OGrMgr.Reset() ; @@ -3084,6 +3091,7 @@ SurfTriMesh::DoCompacting( double dTol) int nVIdSize = int( vVId.size()) ; // sistemo gli indici dei vertici nei triangoli + INTVECTOR vInvalidIds ; for ( int nId = 0 ; nId < GetTriangleSize() ; ++ nId) { // salto i triangoli cancellati if ( m_vTria[nId].nIdVert[0] == SVT_DEL) @@ -3100,15 +3108,51 @@ SurfTriMesh::DoCompacting( double dTol) // aggiorno il triangolo m_vTria[nId].nIdVert[0] = vVId[vOId[0]] ; m_vTria[nId].nIdVert[1] = vVId[vOId[1]] ; - m_vTria[nId].nIdVert[2] = vVId[vOId[2]] ; - // se due vertici coincidono o la normale non è calcolabile, cancello il triangolo - if ( m_vTria[nId].nIdVert[0] == m_vTria[nId].nIdVert[1] || - m_vTria[nId].nIdVert[0] == m_vTria[nId].nIdVert[2] || - m_vTria[nId].nIdVert[1] == m_vTria[nId].nIdVert[2] || - ! CalcTriangleNormal( nId)) - RemoveTriangle( nId) ; + m_vTria[nId].nIdVert[2] = vVId[vOId[2]] ; + + // verifico se triangolo da rimuovere per vertici coincidenti + bool bRemove = false ; + int nTAdj1 = SVT_NULL, nTAdj2 = SVT_NULL ; + if ( m_vTria[nId].nIdVert[0] == m_vTria[nId].nIdVert[1]) { + nTAdj1 = m_vTria[nId].nIdAdjac[1] ; + nTAdj2 = m_vTria[nId].nIdAdjac[2] ; + bRemove = true ; + } + else if ( m_vTria[nId].nIdVert[0] == m_vTria[nId].nIdVert[2]) { + nTAdj1 = m_vTria[nId].nIdAdjac[0] ; + nTAdj2 = m_vTria[nId].nIdAdjac[1] ; + bRemove = true ; + } + else if ( m_vTria[nId].nIdVert[1] == m_vTria[nId].nIdVert[2]) { + nTAdj1 = m_vTria[nId].nIdAdjac[0] ; + nTAdj2 = m_vTria[nId].nIdAdjac[2] ; + bRemove = true ; + } + if ( bRemove) { + // sistemo le contro adiacenze + if ( nTAdj1 != SVT_NULL) { + for ( int j = 0 ; j < 3 ; ++ j) + if ( m_vTria[nTAdj1].nIdAdjac[j] == nId) + m_vTria[nTAdj1].nIdAdjac[j] = nTAdj2 ; + } + if ( nTAdj2 != SVT_NULL) { + for ( int j = 0 ; j < 3 ; ++ j) + if ( m_vTria[nTAdj2].nIdAdjac[j] == nId) + m_vTria[nTAdj2].nIdAdjac[j] = nTAdj1 ; + } + // rimuovo il triangolo + RemoveTriangle( nId) ; + } + + // verifico se il triangolo va rimosso per normale non calcolabile + else if ( ! CalcTriangleNormal( nId)) + vInvalidIds.emplace_back( nId) ; } + // elimino triangoli invalidi ( con gestione speciale per evitare T-junctions) + if ( ! vInvalidIds.empty()) + RemoveInvalidTriangles( vInvalidIds) ; + // compatto il vettore dei vertici if ( ! PackVertices()) return false ; @@ -3345,22 +3389,23 @@ SurfTriMesh::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double bMirror = ( bMirror ? ( dCoeffY > 0) : ( dCoeffY < 0)) ; bMirror = ( bMirror ? ( dCoeffZ > 0) : ( dCoeffZ < 0)) ; - // aggiorno le facce - for ( int i = 0 ; i < GetTriangleSize() ; ++ i) { - if ( m_vTria[i].nIdVert[0] != SVT_DEL) { - // se c'è mirror, devo invertire la faccia - if ( bMirror) - InvertTriangle( i) ; - // aggiorno la normale - if ( ! CalcTriangleNormal( i)) { - // elimino il triangolo - RemoveTriangle( i) ; - } - } + // se c'è mirror, devo invertire le facce + if ( bMirror) { + for ( int i = 0 ; i < GetTriangleSize() ; ++ i) + if ( m_vTria[i].nIdVert[0] != SVT_DEL) + InvertTriangle( i) ; } + + // rimuovo i triangoli resi invalidi dalla scalatura + bool bOk = DoCompacting() ; - return DoCompacting() ; + // rimuovo i triangoli doppi + bool bModified = false ; + bOk = bOk && RemoveDoubleTriangles( bModified) ; + if ( bModified) + bOk = bOk && ( AdjustVertices() && DoCompacting()) ; + return bOk ; } //---------------------------------------------------------------------------- @@ -4322,3 +4367,86 @@ SurfTriMesh::SetTempInt( int nId, int nTempInt) const m_vTria[nId].nTemp = nTempInt ; return true ; } + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::AddTriaFromZMap( const TRIA3DEXVECTOR& vTria, PointGrid3d& VertGrid, double dVertexTol) +{ + // scorro i triangoli + for ( const Triangle3dEx& Tria : vTria) { + // ciclo sui tre vertici + int nIdV[3]{} ; + for ( int nV = 0 ; nV < 3 ; ++ nV) { + // verifico se vertice già presente + int nId ; + if ( ! VertGrid.Find( Tria.GetP( nV), dVertexTol, nId)) { + // se non presente, lo aggiugo + nIdV[nV] = AddVertex( Tria.GetP( nV)) ; + if ( nIdV[nV] == SVT_NULL) + return false ; + VertGrid.InsertPoint( Tria.GetP( nV), nIdV[nV]) ; + } + else + nIdV[nV] = nId ; + } + // se i vertici sono tutti diversi tra loro, inserisco il triangolo + if ( nIdV[0] != nIdV[1] && nIdV[0] != nIdV[2] && nIdV[1] != nIdV[2]) { + int nT = AddTriangle( nIdV, Tria.GetGrade()) ; + if ( nT != SVT_NULL && nT != SVT_DEL) { + // associo relazione vertice-triangolo + for ( int i = 0 ; i < 3 ; ++ i) + m_vVert[nIdV[i]].nIdTria = nT ; + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::AdjustTopologyFromZMap( void) +{ + // cancello tutti i vertici che puntano a triangoli cancellati + bool bPack = false ; + for ( int i = 0 ; i < GetVertexSize() ; ++ i) { + if ( m_vVert[i].nIdTria == SVT_NULL) { + m_vVert[i].nIdTria = SVT_DEL ; + bPack = true ; + } + } + if ( bPack) + PackVertices() ; + m_nStatus = SurfTriMesh::OK ; + + // calcolo le adiacenze + if ( ! AdjustAdjacencies()) + return false ; + + // verifico l'orientamento ( TODO -- Da migliorare...) + // Ora per funzionare è necessario che il ciclo sui triangoli parta da un triangolo orientato + // correttamente e che i triangoli invertiti siano "circondati" da triangoli tutti orientati correttamente + if ( ! AdjustOrientations()) + return false ; + + // calcolo le facce + if ( ! VerifyFaceting()) + return false ; + + // rimozione delle TJunction + bool bModified = false ; + if ( ! RemoveTJunctions( bModified, SQ_EPS_SMALL)) + return false ; + if ( bModified) { + if ( ! AdjustVertices() || ! DoCompacting()) + return false ; + } + else + TestSealing() ; + + // semplifico le facce ( anche più piccola) + if ( ! SimplifyFacets( MAX_EDGE_LEN_STD, false, 5. * EPS_SMALL)) + LOG_ERROR( GetEGkLogger(), "Error in SimplifyFacets of Stm::AdjustTopologyFromZMap") + + return true ; +} diff --git a/SurfTriMesh.h b/SurfTriMesh.h index 5fbec74..36cc13a 100644 --- a/SurfTriMesh.h +++ b/SurfTriMesh.h @@ -18,6 +18,7 @@ #include "GeoObjRW.h" #include "/EgtDev/Include/EGkSurfTriMesh.h" #include "/EgtDev/Include/EGkHashGrids3d.h" +#include "/EgtDev/Include/EGkPointGrid3d.h" #include #include @@ -327,7 +328,7 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW bool Intersect( const ISurfTriMesh& Other) override ; bool Subtract( const ISurfTriMesh& Other) override ; bool GetSurfClassification( const ISurfTriMesh& ClassifierSurf, - INTVECTOR& vTriaIn, INTVECTOR& vTriaOut, INTVECTOR& vTriaOnP, INTVECTOR& vTriaOnM, INTVECTOR& vTriaIndef) override ; + INTVECTOR& vTriaIn, INTVECTOR& vTriaOut, INTVECTOR& vTriaOnP, INTVECTOR& vTriaOnM, INTVECTOR& vTriaIndef) override ; bool CutWithOtherSurf( const ISurfTriMesh& CutterSurf, bool bInVsOut, bool bSaveOnEq) override ; bool Repair( double dMaxEdgeLen = MAX_EDGE_LEN_STD) override ; bool GetAllTriaOverlapBox( const BBox3d& b3Box, INTVECTOR& vT) const override ; @@ -373,6 +374,8 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW bool GetTempInt( int nId, int& nTempInt) const ; bool ResetTempInts( void) const ; bool SetTempInt( int nId, int nTempInt) const ; + bool AddTriaFromZMap( const TRIA3DEXVECTOR& vTria, PointGrid3d& VertGrid, double dVertexTol = 2 * EPS_SMALL) ; + bool AdjustTopologyFromZMap( void) ; private : typedef std::vector VERTVECTOR ; @@ -388,7 +391,7 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW bool CopyFrom( const SurfTriMesh& clSrc) ; bool Validate( bool bCorrect = false) ; bool AdjustVertices( void) ; - bool AdjustAdjacencies( void) ; + bool AdjustAdjacencies( bool AdjustVert = true) ; bool AdjustOrientations( void) ; bool AdjustTriaOrientation( TRINTDEQUE& S3iQ) ; bool TestSealing( void) ; @@ -433,14 +436,16 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW bool IntersectTriMeshTriangle( SurfTriMesh& Other) ; bool IdentifyShells( void) const ; bool RemoveDoubleTriangles( bool& bModified) ; - bool RemoveTJunctions( bool& bModified) ; + bool RemoveTJunctions( bool& bModified, double dMinSqDist = SQ_EPS_TRIA_H) ; bool FlipTriangles( int nTA, int nTB) ; - bool SimplifyFacets( double dMaxEdgeLen = MAX_EDGE_LEN_STD, bool bForced = true) ; + bool SimplifyFacets( double dMaxEdgeLen = MAX_EDGE_LEN_STD, bool bForced = true, double dTolAlign = 50 * EPS_SMALL) ; bool AddChainToChain( const Chain& ChainToAdd, PNTVECTOR& OrigChain) ; bool DistPointFacet( const Point3d& ptP, const POLYLINEVECTOR& vPolyVec, double& dPointFacetDist) ; bool ChangeStart( const Point3d& ptNewStart, PNTVECTOR& Loop) ; bool SplitAtPoint( const Point3d& ptStop, const PNTVECTOR& Loop, PNTVECTOR& Loop1, PNTVECTOR& Loop2) ; - + bool AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, double dTolAlign, bool& bModif) const ; + bool RemoveInvalidTriangles( const INTVECTOR& vIds) ; + bool FindAdjacentOnLongerEdge( int nT, int& nEdge, int& nAdjTrg) const ; private : ObjGraphicsMgr m_OGrMgr ; // gestore grafica dell'oggetto Status m_nStatus ; // stato diff --git a/SurfTriMeshBooleans.cpp b/SurfTriMeshBooleans.cpp index ff10d40..3aed42c 100644 --- a/SurfTriMeshBooleans.cpp +++ b/SurfTriMeshBooleans.cpp @@ -502,9 +502,7 @@ SurfTriMesh::RetriangulationForBooleanOperation( CHAINMAP& LoopLines, TRIA3DVECT } } } - // ------------------------------------------------------------------------------------------- - - + // Creo il loop chiuso padre di tutti, il perimetro del triangolo. Questo viene diviso in sotto-loop // chiusi mediante quelli aperti. I loop chiusi trovati precedentemente sono interni a uno dei // sotto-loop chiusi di cui è formato il perimetro. @@ -704,16 +702,18 @@ SurfTriMesh::RetriangulationForBooleanOperation( CHAINMAP& LoopLines, TRIA3DVECT } } } - // Elimino loop interni non validi - bool bDouble = true ; + // Verifico se i loop interni sono validi + bool bAllInvalid = true ; for ( int nInnLoop = 0 ; nInnLoop < int( vInnerLoop.size()) ; ++ nInnLoop) { - if ( cvClosedChain[vInnerLoop[nInnLoop]].size() > 2) { - bDouble = false ; + // se chain formata da tre segmenti significa che si tratta di due linee sovrapposte ( il terzo tratto è quello aggiunto + // per forzare la chiusura) + if ( cvClosedChain[vInnerLoop[nInnLoop]].size() > 3) { + bAllInvalid = false ; break ; } } - if ( vInnerLoop.empty() || bDouble) { + if ( vInnerLoop.empty() || bAllInvalid) { // Eseguo triangolazione PNTVECTOR vPt ; INTVECTOR vTr ; @@ -753,63 +753,20 @@ SurfTriMesh::RetriangulationForBooleanOperation( CHAINMAP& LoopLines, TRIA3DVECT vPolygons.emplace_back( CurLoop) ; } - // poligono - Polygon3d pgPol ; - pgPol.FromPolyLine( vPolygons[1]) ; - - // controllo direzioni delle normali - bool bCodirectedNormals = trTria.GetN() * pgPol.GetVersN() > 0. ; - - // Aggiungo al loop esterno i punti dei loop interni che si trovano su di esso - PNTULIST& ExternLoopList = vPolygons[0].GetUPointList() ; - // Ciclo sui segmenti del loop esterno - auto itSt = ExternLoopList.begin() ; - auto itEn = itSt ; - ++ itEn ; - for ( ; itSt != ExternLoopList.end() && itEn != ExternLoopList.end() ; ++ itSt, ++ itEn) { - // Estremi del segmento corrente del loop esterno e scorrispondente vettore - Point3d ptSt = itSt->first ; - Point3d ptEn = itEn->first ; - Vector3d vtSeg = ptEn - ptSt ; - double dSegLen = vtSeg.Len() ; - vtSeg /= dSegLen ; - // Vettore dei punti dei loop interni che stanno sul segmento del loop esterno - PNTUVECTOR vPointWithOrder ; - // Ciclo sui loop interni - for ( int nInnPoly = 1 ; nInnPoly < int( vPolygons.size()) ; ++ nInnPoly) { - // Ciclo sui punti dei loop interni - Point3d ptInnPoint ; - bool bIsFirst = true ; - bool bContinue = vPolygons[nInnPoly].GetFirstPoint( ptInnPoint) ; - while ( bContinue) { - DistPointLine DistCalculator( ptInnPoint, ptSt, ptEn) ; - double dDist ; - DistCalculator.GetDist( dDist) ; - double dLongPos = ( ptInnPoint - ptSt) * vtSeg ; - if ( dDist < EPS_SMALL && dLongPos > 0. && dLongPos < dSegLen) { - POINTU NewPointU ; - NewPointU.first = ptInnPoint ; - NewPointU.second = dLongPos ; - if ( ! bIsFirst) - vPointWithOrder.emplace_back( NewPointU) ; - } - bIsFirst = false ; - bContinue = vPolygons[nInnPoly].GetNextPoint( ptInnPoint) ; - } - } - // Riordino i punti interni sul segmento esterno in funzione della distanza dall'origine di esso - for ( int nPi = 0 ; nPi < int( vPointWithOrder.size()) - 1 ; ++ nPi) { - for ( int nPj = nPi + 1 ; nPj < int( vPointWithOrder.size()) ; ++ nPj) { - if ( vPointWithOrder[nPi].second > vPointWithOrder[nPj].second) { - swap( vPointWithOrder[nPi], vPointWithOrder[nPj]) ; - } - } - } - // Aggiungo i punti al loop esterno - for ( int nPi = 0 ; nPi < int( vPointWithOrder.size()) ; ++ nPi) { - itSt = ExternLoopList.emplace( itEn, vPointWithOrder[nPi]) ; - } + // controllo la direzione della normale del triangolo con quella del poligono di area maggiore + // ( per gestire eventuali inscatolamenti) + Vector3d vtPoly = V_NULL ; + double dMaxArea = -1 ; + for ( int i = 1 ; i < int( vPolygons.size()) ; i ++) { + Plane3d plPoly ; + double dAreaPoly ; + vPolygons[i].IsClosedAndFlat( plPoly, dAreaPoly) ; + if ( dAreaPoly > dMaxArea) { + vtPoly = plPoly.GetVersN() ; + dMaxArea = dAreaPoly ; + } } + bool bCodirectedNormals = trTria.GetN() * vtPoly > 0. ; PNTVECTOR vPt ; INTVECTOR vTr ; @@ -836,6 +793,7 @@ SurfTriMesh::RetriangulationForBooleanOperation( CHAINMAP& LoopLines, TRIA3DVECT } // Divido i loop che si autointercettano + // TO DO : da verificare. Questa porzione di codice non dovrebbe andare prima della triangolazione? int nInitialLoopNum = int( vPolygons.size()) ; for ( int nL = 1 ; nL < nInitialLoopNum ; ++ nL) { // Lista dei punti della PolyLine Loop corrente @@ -883,11 +841,11 @@ SurfTriMesh::RetriangulationForBooleanOperation( CHAINMAP& LoopLines, TRIA3DVECT itSt2 = LoopPointList.emplace( itEn2, vAddingPointWithOrder[nPi]) ; } } - + // Spezzo i loop autointersecantesi POLYLINEVECTOR vAuxPolygons ; vAuxPolygons.emplace_back( vPolygons[nL]) ; - + bool bSplitted = true ; while ( bSplitted) { bSplitted = false ; @@ -956,16 +914,13 @@ SurfTriMesh::RetriangulationForBooleanOperation( CHAINMAP& LoopLines, TRIA3DVECT vPolygons.erase( vPolygons.begin() + i) ; else ++ i ; - } - - bool bCordirectedNormals_intLoop = bCodirectedNormals ; + // eventuale inversione della prima curva ( che determina il verso della triangolazione) per averla orientata + // come il triangolo if ( ! vPolygons.empty()) { Polygon3d pgPol ; pgPol.FromPolyLine( vPolygons[0]) ; - // controllo direzioni delle normali - bCordirectedNormals_intLoop = trTria.GetN() * pgPol.GetVersN() > 0. ; - if ( ! bCordirectedNormals_intLoop) + if ( trTria.GetN() * pgPol.GetVersN() < 0.) vPolygons[0].Invert() ; } @@ -981,7 +936,7 @@ SurfTriMesh::RetriangulationForBooleanOperation( CHAINMAP& LoopLines, TRIA3DVECT Surf.m_vTria[nNewTriaNum].nETempFlag[0] = 0 ; Surf.m_vTria[nNewTriaNum].nETempFlag[1] = 0 ; Surf.m_vTria[nNewTriaNum].nETempFlag[2] = 0 ; - if ( bCordirectedNormals_intLoop) + if ( bCodirectedNormals) Surf.m_vTria[nNewTriaNum].nTempShell = 1 ; else Surf.m_vTria[nNewTriaNum].nTempShell = -1 ; @@ -1123,7 +1078,7 @@ SurfTriMesh::AmbiguosTriangleManager( TRIA3DVECTORMAP& Ambiguos, SurfTriMesh& Su //---------------------------------------------------------------------------- bool SurfTriMesh::IntersectTriMeshTriangle( SurfTriMesh& Other) -{ +{ bool bModif = false ; SurfTriMesh& SurfB = Other ; @@ -1176,7 +1131,7 @@ SurfTriMesh::IntersectTriMeshTriangle( SurfTriMesh& Other) INTVECTOR vNearTria ; SurfB.GetAllTriaOverlapBox( b3dTriaA, vNearTria) ; - // I scorro tutti i triangoli di B che intersecano il box di A + // Scorro tutti i triangoli di B che intersecano il box di A for ( int nTB = 0 ; nTB < int( vNearTria.size()) ; ++ nTB) { // Se il triangolo B non è valido, continuo @@ -1365,7 +1320,7 @@ SurfTriMesh::IntersectTriMeshTriangle( SurfTriMesh& Other) // Triangoli sovrapposti if ( bContinue) { int nTriaNum2A = GetTriangleSize() ; - // Resetto e ricalcolo la HashGrid della superficie B + // Resetto e ricalcolo la HashGrid della superficie B SurfB.ResetHashGrids3d() ; for ( int nTA = 0 ; nTA < nTriaNum2A ; ++ nTA) { // Se il triangolo A non è valido, continuo @@ -1375,15 +1330,19 @@ SurfTriMesh::IntersectTriMeshTriangle( SurfTriMesh& Other) // Box del triangolo A BBox3d b3dTriaA ; trTriaA.GetLocalBBox( b3dTriaA) ; - // Recupero i triangoli di B che interferiscono col box del triangolo di A + // Recupero i triangoli di B che interferiscono col box del triangolo di A INTVECTOR vNearTria ; SurfB.GetAllTriaOverlapBox( b3dTriaA, vNearTria) ; for ( int nTB = 0 ; nTB < int( vNearTria.size()) ; ++ nTB) { - // Se il triangolo B non è valido, continuo + // Se il triangolo B non è valido, continuo Triangle3d trTriaB ; if ( ! SurfB.GetTriangle( vNearTria[nTB], trTriaB) || ! trTriaB.Validate( true)) continue ; - // Se i triangoli sono sovrapposti + // Se sono già stati classificati entrambi come sovrapposti continuo + if ( abs( m_vTria[nTA].nTempShell) == 2 && abs( SurfB.m_vTria[vNearTria[nTB]].nTempShell) == 2) + continue ; + + // Se i triangoli sono sovrapposti TRIA3DVECTOR vTriaAB ; Point3d ptTempA, ptTempB ; int nIntTypeAB = IntersTriaTria( trTriaA, trTriaB, ptTempA, ptTempB, vTriaAB) ; @@ -1475,7 +1434,7 @@ SurfTriMesh::Add( const ISurfTriMesh& Other) // tengo una copia di B ( la superficie B viene modificata durante la ritriangolazione ) SurfTriMesh SurfA_cl ; SurfA_cl.CopyFrom( this) ; - + // ritriangolo le due superfici mediante ogni intersezione Triangolo-Triangolo IntersectTriMeshTriangle( SurfB) ; @@ -1625,12 +1584,12 @@ SurfTriMesh::Intersect( const ISurfTriMesh& Other) AddTriangle( nNewVert, m_nMaxTFlag) ; } } - + // sistemazioni varie bool bOk = ( AdjustVertices() && DoCompacting()) ; bool bModified = false ; - bOk = bOk && RemoveDoubleTriangles( bModified) ; - if ( bModified) + bOk = bOk && RemoveDoubleTriangles( bModified) ; + if ( bModified) bOk = bOk && ( AdjustVertices() && DoCompacting()) ; bOk = bOk && RemoveTJunctions( bModified) ; if ( bModified) diff --git a/SurfTriMeshOffset.cpp b/SurfTriMeshOffset.cpp new file mode 100644 index 0000000..666e863 --- /dev/null +++ b/SurfTriMeshOffset.cpp @@ -0,0 +1,75 @@ +//---------------------------------------------------------------------------- +// EgalTech 2014-2022 +//---------------------------------------------------------------------------- +// File : SurfTriMeshOffset.cpp Data : 10.06.25 Versione : 2.7e4 +// Contenuto : Implementazione funzione per Offset di Superfici TriMesh. +// +// +// +// Modifiche : 10.06.25 RE Creazione modulo. +// +//---------------------------------------------------------------------------- + +//--------------------------- Include ---------------------------------------- +#include "stdafx.h" +#include "SurfTriMesh.h" +#include "VolZmap.h" + +using namespace std ; + +//---------------------------------------------------------------------------- +static ISurfTriMesh* +SumStm( const CISURFTMPVECTOR& vStm) +{ + // se vettore vuoto, non faccio nulla + if ( vStm.empty()) + return nullptr ; + // definisco la superficie somma tra tutte ( la prima deve essere valida) + PtrOwner pStmAdd( CreateSurfTriMesh()) ; + if ( IsNull( pStmAdd)) + return nullptr ; + // scorro le superfici + for ( const ISurfTriMesh* pStm : vStm) { + if ( pStm == nullptr || ! pStm->IsValid() || pStm->GetTriangleCount() == 0) + continue ; + if ( ! pStmAdd->IsValid() || pStmAdd->GetTriangleCount() == 0) { + if ( ! pStmAdd->CopyFrom( pStm)) + return nullptr ; + } + else + pStmAdd->Add( *pStm) ; + } + // restituisco la superficie ottenuta + return ( Release( pStmAdd)) ; +} + +//---------------------------------------------------------------------------- +/* Funzione che crea l'Offset di una superficie TriMesh chiusa */ +ISurfTriMesh* +CreateSurfTriMeshOffset( const ISurfTriMesh* pStm, double dOffs, double dLinTol) +{ + return CreateSurfTriMeshesOffset( { pStm}, dOffs, dLinTol) ; +} + +//---------------------------------------------------------------------------- +/* Funzione che crea l'Offset di un insieme di superfici */ +ISurfTriMesh* +CreateSurfTriMeshesOffset( const CISURFTMPVECTOR& vStm, double dOffs, double dLinTol) +{ + // se vettore delle superfici vuoto, non faccio nulla + if ( vStm.empty()) + return nullptr ; + // controllo sul valore di tolleranza lineare + double dMyLinTol = max( dLinTol, 10 * EPS_SMALL) ; + // --- NB. ( Il valore di Offset deve essere maggiore di 10 * EPS_SMALL in valore assoluto) + // Nel caso sia minore, restituisco semplicemente la somma delle superfici + // ( questo valore serve per rimanere coerente con l'Offset delle curve) + if ( abs( dOffs) < 10 * EPS_SMALL) + return SumStm( vStm) ; + // --- NB. Per la creazione dello Zmap è necessario che le superfici siano chiuse + PtrOwner pVolZmap( CreateVolZmap()) ; + if ( IsNull( pVolZmap) || ! pVolZmap->CreateFromTriMeshOffset( vStm, dOffs, dMyLinTol)) + return nullptr ; + // restituisco la superficie TriMesh + return ( pVolZmap->GetSurfTriMesh()) ; +} \ No newline at end of file diff --git a/SurfTriMeshUtilities.cpp b/SurfTriMeshUtilities.cpp index 4bbc787..7174040 100644 --- a/SurfTriMeshUtilities.cpp +++ b/SurfTriMeshUtilities.cpp @@ -17,7 +17,6 @@ #include "Triangulate.h" #include "/EgtDev/Include/EGkDistPointLine.h" #include "/EgtDev/Include/EGkDistLineLine.h" -#include using namespace std ; @@ -35,6 +34,7 @@ SurfTriMesh::RemoveDoubleTriangles( bool& bModified) // recupero i vertici dei triangoli int nIdV[3] ; GetTriangle( nT, nIdV) ; + bool bToRemove = false ; // ciclo sui triangoli adiacenti for ( int nE = 0 ; nE < 3 ; ++ nE) { // recupero triangolo adiacente, se non esiste passo al successivo @@ -53,10 +53,14 @@ SurfTriMesh::RemoveDoubleTriangles( bool& bModified) } } if ( nCoinc == 3) { - RemoveTriangle( nAdjT) ; + // se i vertici coincidono rimuovo entrambi i triangoli + bToRemove = true ; bModified = true ; + RemoveTriangle( nAdjT) ; } } + if ( bToRemove) + RemoveTriangle( nT) ; } return true ; @@ -86,8 +90,8 @@ SurfTriMesh::FlipTriangles( int nTA, int nTB) // Recupero i vertici del triangolo A Point3d ptSegSt, ptSegEn, ptVertA ; if ( ! GetVertex( m_vTria[nTA].nIdVert[nEdgeA], ptSegSt) || - ! GetVertex( m_vTria[nTA].nIdVert[( nEdgeA + 1) % 3], ptSegEn) || - ! GetVertex( m_vTria[nTA].nIdVert[( nEdgeA + 2) % 3], ptVertA)) + ! GetVertex( m_vTria[nTA].nIdVert[( nEdgeA + 1) % 3], ptSegEn) || + ! GetVertex( m_vTria[nTA].nIdVert[( nEdgeA + 2) % 3], ptVertA)) return false ; // Recupero il vertice opposto del triangolo B Point3d ptVertB ; @@ -98,23 +102,43 @@ SurfTriMesh::FlipTriangles( int nTA, int nTB) if ( ! DiagDist.IsSmall()) return false ; double dPos1, dPos2 ; - if ( ! DiagDist.GetPositionsAtMinDistPoints( dPos1, dPos2) || - dPos1 < EPS_SMALL || dPos1 > ( ptSegEn - ptSegSt).Len() - EPS_SMALL || - dPos2 < EPS_SMALL || dPos2 > ( ptVertB - ptVertA).Len() - EPS_SMALL) - return false ; + if ( ! DiagDist.GetPositionsAtMinDistPoints( dPos1, dPos2)) + return false ; + if ( dPos1 < - EPS_SMALL || dPos1 > ( ptSegEn - ptSegSt).Len() + EPS_SMALL || + dPos2 < - EPS_SMALL || dPos2 > ( ptVertB - ptVertA).Len() + EPS_SMALL) + return false ; + // Eseguo il flipping m_vTria[nTA].nIdVert[nEdgeA] = m_vTria[nTB].nIdVert[( nEdgeB + 2) % 3] ; m_vTria[nTB].nIdVert[nEdgeB] = m_vTria[nTA].nIdVert[( nEdgeA + 2) % 3] ; m_vTria[nTA].nIdAdjac[nEdgeA] = m_vTria[nTB].nIdAdjac[( nEdgeB + 2) % 3] ; - m_vTria[nTA].nIdAdjac[( nEdgeA + 2) % 3] = nTB ; m_vTria[nTB].nIdAdjac[nEdgeB] = m_vTria[nTA].nIdAdjac[( nEdgeA + 2) % 3] ; + m_vTria[nTA].nIdAdjac[( nEdgeA + 2) % 3] = nTB ; m_vTria[nTB].nIdAdjac[( nEdgeB + 2) % 3] = nTA ; + + // sistemo anche le contro-adiacenze + int nTC = m_vTria[nTA].nIdAdjac[nEdgeA] ; + if ( nTC != SVT_NULL) { + for ( int i = 0 ; i < 3 ; i++) + if ( m_vTria[nTC].nIdAdjac[i] == nTB) { + m_vTria[nTC].nIdAdjac[i] = nTA ; + break ; + } + } + int nTD = m_vTria[nTB].nIdAdjac[nEdgeB] ; + if ( nTD != SVT_NULL) { + for ( int i = 0 ; i < 3 ; i++) + if ( m_vTria[nTD].nIdAdjac[i] == nTA) { + m_vTria[nTD].nIdAdjac[i] = nTB ; + break ; + } + } return true ; } //---------------------------------------------------------------------------- bool -SurfTriMesh::RemoveTJunctions( bool& bModified) +SurfTriMesh::RemoveTJunctions( bool& bModified, double dMinSqDist) { bModified = false ; @@ -123,6 +147,11 @@ SurfTriMesh::RemoveTJunctions( bool& bModified) // Ciclo sui triangoli della superficie per determinare gli altri vertici sul loro perimetro for ( int nT = 0 ; nT < int( m_vTria.size()) ; ++ nT) { + // se adiacenze tutte valide, passo al successivo + if ( m_vTria[nT].nIdAdjac[0] != SVT_DEL && m_vTria[nT].nIdAdjac[0] != SVT_NULL && + m_vTria[nT].nIdAdjac[1] != SVT_DEL && m_vTria[nT].nIdAdjac[1] != SVT_NULL && + m_vTria[nT].nIdAdjac[2] != SVT_DEL && m_vTria[nT].nIdAdjac[2] != SVT_NULL) + continue ; // Se il triangolo non è valido, passo al successivo Triangle3d trTria ; if ( ! GetTriangle( nT, trTria) || ! trTria.Validate( true)) @@ -150,6 +179,8 @@ SurfTriMesh::RemoveTJunctions( bool& bModified) if ( dSegLen < EPS_SMALL) continue ; vtSeg /= dSegLen ; + int nV1 = m_vTria[nT].nIdVert[nSeg] ; + int nV2 = m_vTria[nT].nIdVert[Next( nSeg)] ; // Ciclo sui triangoli vicini for ( int nI = 0 ; nI < int( vNearTria.size()) ; ++ nI) { // Salto il triangolo se è quello di riferimento @@ -157,13 +188,16 @@ SurfTriMesh::RemoveTJunctions( bool& bModified) continue ; // Cerco i vertici che stanno sul lato del triangolo for ( int nVert = 0 ; nVert < 3 ; ++ nVert) { + int nCurrVert = m_vTria[vNearTria[nI]].nIdVert[nVert] ; + if ( nCurrVert == nV1 || nCurrVert == nV2) + continue ; Point3d ptVert ; if ( ! GetVertex( m_vTria[vNearTria[nI]].nIdVert[nVert], ptVert)) continue ; double dProj = ( ptVert - ptSegSt) * vtSeg ; - double dOrt = ( ( ptVert - ptSegSt) - dProj * vtSeg).SqLen() ; - if ( dProj > EPS_SMALL && dProj < dSegLen - EPS_SMALL && dOrt < SQ_EPS_TRIA_H) - vVertOtl.emplace_back( m_vTria[vNearTria[nI]].nIdVert[nVert]) ; + double dOrt = ( ( ptVert - ptSegSt) - dProj * vtSeg).SqLen() ; + if ( dProj > EPS_SMALL && dProj < dSegLen - EPS_SMALL && dOrt < dMinSqDist) + vVertOtl.emplace_back( m_vTria[vNearTria[nI]].nIdVert[nVert]) ; } } // Riordino i vertici sul segmento @@ -307,16 +341,42 @@ ChooseGoodStartPoint( PNTULIST& PointList) return false ; } -//---------------------------------------------------------------------------- -static bool -AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, bool& bModif) +// ------------------------------------------------------------- +bool +SurfTriMesh::AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, double dTolAlign, bool& bModif) const { + // vettore dei loop della faccia adiacente + POLYLINEVECTOR LoopVec ; // Ciclo sui punti del loop auto itLast = PointList.begin() ; for ( auto it = next( itLast) ; it != PointList.end() ; ++ it) { - - // Se dal punto corrente inizia un segmento adiacente a un'altra faccia - if ( itLast->second != it->second) { + + // bisogna fermarsi per analizzare il tratto corrente alla ricerca di punti allineati se dal punto corrente + // inizia un tratto adiacente ad un'altra faccia oppure se il punto corrente non verrà eliminato dal loop + // della faccia adiacente + + bool bAnalyze = ( itLast->second != it->second) ; + if ( bAnalyze) + LoopVec.clear() ; + if ( ! bAnalyze && itLast->second != - 1) { + if ( LoopVec.empty()) + GetFacetLoops( int( itLast->second), LoopVec) ; + for ( int i = 0 ; i < int( LoopVec.size()) && ! bAnalyze ; i ++) { + const PNTULIST& PointListAdj = LoopVec[i].GetUPointList() ; + int nSamePoints = 0 ; + for ( auto itAdj = PointListAdj.begin() ; itAdj != prev( PointListAdj.end()) ; ++ itAdj) { + // cerco il punto corrente sul loop della faccia adiacente + if ( AreSamePointApprox( it->first, itAdj->first)) + ++ nSamePoints ; + if ( ( nSamePoints == 1 && int( PointListAdj.size()) <= 4) || nSamePoints > 1) { + bAnalyze = true ; + break ; + } + } + } + } + + if ( bAnalyze) { // Raccolgo i punti in una polyline PolyLine PL ; int nPar = -1 ; @@ -326,7 +386,7 @@ AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, bool& bModif) } PL.AddUPoint( ++nPar, it->first) ; // Provo ad eliminare i punti allineati - PL.RemoveAlignedPoints( 50 * EPS_SMALL) ; + PL.RemoveAlignedPoints( dTolAlign) ; if ( PL.GetPointNbr() < nPar + 1) { // rimuovo dalla lista dei punti gli eliminati (salto gli estremi) int nUCurr = 1 ; @@ -418,7 +478,7 @@ AdjustLoop( PNTULIST& PointList, double dMaxEdgeLen, bool& bModif) //---------------------------------------------------------------------------- bool -SurfTriMesh::SimplifyFacets( double dMaxEdgeLen, bool bForced) +SurfTriMesh::SimplifyFacets( double dMaxEdgeLen, bool bForced, double dTolAlign) { // La trimesh deve essere valida if ( ! IsValid()) @@ -433,8 +493,8 @@ SurfTriMesh::SimplifyFacets( double dMaxEdgeLen, bool bForced) // Ciclo sulle facce della mesh per trovare quelle da ritriangolare unordered_map< int, pair< PNTVECTOR, INTVECTOR>> FacetMap ; for ( int nF = 0 ; nF < nFacetCnt ; ++ nF) { - - // Recupero i loop della faccia (il parametro indica la faccia adiacente) + + // Recupero i loop della faccia ( il parametro indica la faccia adiacente) POLYLINEVECTOR LoopVec ; GetFacetLoops( nF, LoopVec) ; @@ -445,13 +505,17 @@ SurfTriMesh::SimplifyFacets( double dMaxEdgeLen, bool bForced) // Lista dei punti del loop PNTULIST& PointList = LoopVec[nL].GetUPointList() ; + // Se il loop è un triangolo, non va modificato + if ( int( PointList.size()) <= 4) + continue ; + // Mi assicuro che il punto iniziale/finale non sia all'interno di un possibile segmento if ( ! ChooseGoodStartPoint( PointList)) continue ; // Sistemo il loop bool bModif = false ; - if ( ! AdjustLoop( PointList, dMaxEdgeLen, bModif)) + if ( ! AdjustLoop( PointList, dMaxEdgeLen, dTolAlign, bModif)) return false ; if ( bModif) bToRetriangulate = true ; @@ -479,9 +543,8 @@ SurfTriMesh::SimplifyFacets( double dMaxEdgeLen, bool bForced) // Eseguo la ritriangolazione della faccia PNTVECTOR vPt ; INTVECTOR vTr ; - if ( Triangulate().Make( LoopVec, vPt, vTr)) { + if ( Triangulate().Make( LoopVec, vPt, vTr) && ! vTr.empty()) FacetMap.emplace( nF, make_pair( vPt, vTr)) ; - } // Se non riesco a triangolare anche solo questa faccia, interrompo tutto else return false ; @@ -502,17 +565,17 @@ SurfTriMesh::SimplifyFacets( double dMaxEdgeLen, bool bForced) // Cancello i triangoli for ( int nT : vDelTria) RemoveTriangle( nT) ; - + // Applico le nuove triangolazioni delle facce for ( auto itF = FacetMap.begin() ; itF != FacetMap.end() ; ++ itF) { const PNTVECTOR& vPt = itF->second.first ; const INTVECTOR& vTr = itF->second.second ; - // Inserisco i nuovi triangoli + // Inserisco i nuovi triangoli bool bFirstTria = true ; for ( int n = 0 ; n < int( vTr.size()) - 2 ; n += 3) { int nNewId[3] = { AddVertex( vPt[vTr[n]]), - AddVertex( vPt[vTr[n + 1]]), - AddVertex( vPt[vTr[n + 2]])} ; + AddVertex( vPt[vTr[n + 1]]), + AddVertex( vPt[vTr[n + 2]])} ; auto itCol = ColorMap.find( itF->first) ; int nTFlag = ( itCol != ColorMap.end() ? itCol->second : 0) ; int nNewTriaId = AddTriangle( nNewId, nTFlag) ; @@ -525,7 +588,7 @@ SurfTriMesh::SimplifyFacets( double dMaxEdgeLen, bool bForced) } } } - + // dichiaro necessità ricalcolo della grafica e di hashgrids3d m_OGrMgr.Reset() ; ResetHashGrids3d() ; @@ -722,3 +785,113 @@ SurfTriMesh::SplitAtPoint( const Point3d& ptStop, const PNTVECTOR& Loop, PNTVECT return true ; } + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::FindAdjacentOnLongerEdge( int nT, int& nEdge, int& nAdjTrg) const +{ + // recupero il lato più lungo del triangolo + double dLen0 = SqDist( m_vVert[m_vTria[nT].nIdVert[0]].ptP, m_vVert[m_vTria[nT].nIdVert[1]].ptP) ; + double dLen1 = SqDist( m_vVert[m_vTria[nT].nIdVert[1]].ptP, m_vVert[m_vTria[nT].nIdVert[2]].ptP) ; + double dLen2 = SqDist( m_vVert[m_vTria[nT].nIdVert[2]].ptP, m_vVert[m_vTria[nT].nIdVert[0]].ptP) ; + nEdge = -1 ; + if ( dLen0 > dLen1 && dLen0 > dLen2) + nEdge = 0 ; + else if ( dLen1 > dLen2) + nEdge = 1 ; + else + nEdge = 2 ; + + // recupero il triangolo adiacente sul lato più lungo + nAdjTrg = m_vTria[nT].nIdAdjac[nEdge] ; + return true ; +} + +//---------------------------------------------------------------------------- +bool +SurfTriMesh::RemoveInvalidTriangles( const INTVECTOR& vIds) +{ + // al momento gestito solo per trimesh con adiacenze definite, eventualmente da estendere. + // Analoga a RemoveFistInvalidTrg in Triangulate.cpp + // TO DO da capire e gestire casi in cui flip lascia triangoli invalidi + + unordered_map InvalidMap ; + for ( auto nId : vIds) + InvalidMap[nId] = true ; + + for ( int i = 0 ; i < int( vIds.size()) ; i++) { + + int nTA = vIds[i] ; + if ( ! InvalidMap[nTA]) + continue ; + + // recupero il triangolo adiacente sul suo lato più lungo + int nTB, nEdgeA ; + FindAdjacentOnLongerEdge( nTA, nEdgeA, nTB) ; + + // se adiacente è nullo posso rimuovere tranquillamente il triangolo senza creare TJunctions + if ( nTB == SVT_NULL) { + RemoveTriangle( nTA) ; + continue ; + } + // se adiacente è valido posso fare il flip per rendere valido nTA + else if ( ! InvalidMap[nTB]) { + FlipTriangles( nTA, nTB) ; + InvalidMap[nTA] = false ; + } + // se adiacente è invalido creo una catena da risolvere non appena si trova un triangolo valido + else { + INTVECTOR vChain = {nTA} ; + INTVECTOR vChainEdges = {nEdgeA} ; + int nTCurr = nTB ; + + while ( nTCurr != SVT_NULL && InvalidMap[nTCurr]) { + + // calcolo il successivo + int nTOther, nEdgeCurr ; + FindAdjacentOnLongerEdge( nTCurr, nEdgeCurr, nTOther) ; + + if ( nTOther == vChain.back()) { + // se ho trovato un'adiacenza ambigua ( ovvero due triangoli invalidi adiacenti sui loro lati più lunghi) + // flip dei due triangoli per modificare il lato più lungo e togliere adiacenza ambigua + FlipTriangles( nTCurr, nTOther) ; + if ( vChain.size() > 1) { + vChain.pop_back() ; + vChainEdges.pop_back() ; + // individuo il nuovo adiacente all'ultimo triangolo della catena dopo aver fatto flip + nTOther = m_vTria[vChain.back()].nIdAdjac[vChainEdges.back()] ; + } + } + else { + vChain.emplace_back( nTCurr) ; + vChainEdges.emplace_back( nEdgeCurr) ; + } + // aggiorno per iterazione successiva + nTCurr = nTOther ; + } + + // se la catena termina su triangolo nullo, posso rimuovere tutti i triangoli della catena + if ( nTCurr == SVT_NULL) { + for ( int k = 0 ; k < int( vChain.size()) ; k++) { + RemoveTriangle( vChain[k]) ; + InvalidMap[vChain[k]] = false ; + } + } + // se catena termina su un triangolo valido, applico il flip a cascata a partire dall'ultimo triangolo invalido trovato + else { + FlipTriangles( vChain.back(), nTCurr) ; + InvalidMap[vChain.back()] = false ; + for ( int i = int( vChain.size()) - 2 ; i >= 0 ; i--) { + int nTA = vChain[i] ; + if ( ! InvalidMap[nTA]) + continue ; + // eseguo il flip con il triangolo adiacente sul suo lato più lungo + int nTOther = m_vTria[nTA].nIdAdjac[vChainEdges[i]] ; + FlipTriangles( nTA, nTOther) ; + InvalidMap[nTA] = false ; + } + } + } + } + return true ; +} diff --git a/Tree.cpp b/Tree.cpp index 47f0bfc..5064b47 100644 --- a/Tree.cpp +++ b/Tree.cpp @@ -2101,7 +2101,7 @@ Tree::UpdateSplitLoop( ICurveComposite* pCC, Point3d& pt) // potrei avere una compo vuota o con solo un punto // se non riesco ad aggiungere una linea allora era una compo vuota if ( ! pCC->GetOnlyPoint( ptLast)) { - pCC->FromPoint( pt) ; + pCC->AddPoint( pt) ; ptLast = pt ; } else { @@ -4139,7 +4139,7 @@ Tree::GetEdges3D( vector& mCCEdges, POLYLINEVECTOR& vPolygons pt3d = ORIG ; GetPoint( cNeigh.GetBottomLeft().x, cNeigh.GetTopRight().y, pt3d) ; mCCEdges.back().back()->AddLine( pt3d) ; if ( ! mCCEdges.back().back()->IsValid()) - mCCEdges.back().back()->FromPoint( pt3d) ; + mCCEdges.back().back()->AddPoint( pt3d) ; } else if ( i == 1 ) { while ( ! AreSamePointXYApprox(pt, cNeigh.GetTopLeft()) && plCell.GetNextPoint( pt)) { @@ -4155,7 +4155,7 @@ Tree::GetEdges3D( vector& mCCEdges, POLYLINEVECTOR& vPolygons pt3d = ORIG ; GetPoint( cNeigh.GetBottomLeft().x, cNeigh.GetBottomLeft().y, pt3d) ; mCCEdges.back().back()->AddLine( pt3d) ; if ( ! mCCEdges.back().back()->IsValid()) - mCCEdges.back().back()->FromPoint( pt3d) ; + mCCEdges.back().back()->AddPoint( pt3d) ; } else if ( i == 2) { while ( ! AreSamePointXYApprox(pt, cNeigh.GetBottomLeft()) && plCell.GetNextPoint( pt)) { @@ -4171,7 +4171,7 @@ Tree::GetEdges3D( vector& mCCEdges, POLYLINEVECTOR& vPolygons pt3d = ORIG ; GetPoint( cNeigh.GetTopRight().x, cNeigh.GetBottomLeft().y, pt3d) ; mCCEdges.back().back()->AddLine( pt3d) ; if ( ! mCCEdges.back().back()->IsValid()) - mCCEdges.back().back()->FromPoint( pt3d) ; + mCCEdges.back().back()->AddPoint( pt3d) ; } else if ( i == 3) { while ( ! AreSamePointXYApprox(pt, cNeigh.GetBottomRight()) && plCell.GetNextPoint( pt)) { @@ -4187,7 +4187,7 @@ Tree::GetEdges3D( vector& mCCEdges, POLYLINEVECTOR& vPolygons pt3d = ORIG ; GetPoint( cNeigh.GetTopRight().x, cNeigh.GetTopRight().y, pt3d) ; mCCEdges.back().back()->AddLine( pt3d) ; if ( ! mCCEdges.back().back()->IsValid()) - mCCEdges.back().back()->FromPoint( pt3d) ; + mCCEdges.back().back()->AddPoint( pt3d) ; } } } @@ -4212,7 +4212,7 @@ Tree::GetEdges3D( vector& mCCEdges, POLYLINEVECTOR& vPolygons pCC3D->AddLine( pt3D) ; } if ( ! pCC3D->IsValid()) - pCC3D->FromPoint( pt3D) ; + pCC3D->AddPoint( pt3D) ; // qui devo fare dei controlli prima di aggiungere questa polyline? mCCEdges[i].emplace_back( Release(pCC3D)) ; } diff --git a/Triangulate.cpp b/Triangulate.cpp index 8545f2c..4be97fb 100644 --- a/Triangulate.cpp +++ b/Triangulate.cpp @@ -818,7 +818,7 @@ Triangulate::TestTriangle( const PNTVECTOR& vPt, const INTVECTOR& vPol, } } // If vertex k is inside the ear triangle, then this is not an ear - else if ( TestPointInTriangle( vPt[vPol[k]], vPt[vPol[vPrev[i]]], vPt[vPol[i]], vPt[vPol[vNext[i]]])) { + else if ( TestPointInOrOnTriangle( vPt[vPol[k]], vPt[vPol[vPrev[i]]], vPt[vPol[i]], vPt[vPol[vNext[i]]])) { bIsEar = false ; break ; } @@ -978,6 +978,28 @@ Triangulate::TestPointInTriangle( const Point3d& ptP, const Point3d& ptA, const return true ; } +//---------------------------------------------------------------------------- +// test if point p is inside or on the border of triangle (a, b, c) +bool +Triangulate::TestPointInOrOnTriangle( const Point3d& ptP, const Point3d& ptA, const Point3d& ptB, const Point3d& ptC) +{ + // If P is on a vertex is considered inside + if ( AreSamePoint( ptP, ptA)) + return true ; + if ( AreSamePoint( ptP, ptB)) + return true ; + if ( AreSamePoint( ptP, ptC)) + return true ; + // If P is on the right of at least one edge is outside + if ( TriangleIsCCW( ptA, ptP, ptB, EPS_SMALL)) + return false ; + if ( TriangleIsCCW( ptB, ptP, ptC, EPS_SMALL)) + return false ; + if ( TriangleIsCCW( ptC, ptP, ptA, EPS_SMALL)) + return false ; + return true ; +} + //---------------------------------------------------------------------------- bool Triangulate::SortInternalLoops( const POLYLINEVECTOR& vPL, INTVECTOR& vOrd) @@ -1385,6 +1407,7 @@ RemoveFistInvalidTrg( PNTVECTOR& vPt, INTVECTOR& vTr) // I triangoli cap se eliminati danno origine a T-junctions, quindi devono essere gestiti opportunamente con dei flip. // I triangoli needle se eliminati non sono problematici, ma i loro vertici coincidenti vanno gestiti opportunamente nel // calcolo delle adiacenze dei triangoli cap. + // TO DO da capire e gestire casi in cui flip lascia triangoli invalidi int nTria = int( vTr.size()) / 3 ; INTVECTOR vCapTria ; @@ -1482,46 +1505,40 @@ RemoveFistInvalidTrg( PNTVECTOR& vPt, INTVECTOR& vTr) INTVECTOR vChain, vChainEdges ; vChain.emplace_back( nTA) ; vChainEdges.emplace_back( nEA) ; - - while ( nTB != -1 && ! vbIsValidTria[nTB]) { - // aggiungo alla catena - vChain.emplace_back( nTB) ; + int nTCurr = nTB ; + int nEOther ; + while ( nTCurr != -1 && ! vbIsValidTria[nTCurr]) { // calcolo il successivo - nTA = nTB ; - FindAdjacentOnLongerEdge( vPt, vTr, nTA, nEA, nTB, nEB) ; - vChainEdges.emplace_back( nEA) ; + int nTOther, nECurr ; + FindAdjacentOnLongerEdge( vPt, vTr, nTCurr, nECurr, nTOther, nEOther) ; - // verifico di non aver trovato un'adiacenza ambigua ( ovvero due triangoli invalidi adiacenti sui loro lati più lunghi) - // e quindi di non essere entrato in un loop - if ( nTB == vChain[vChain.size()-2]) { + if ( nTOther == vChain.back()) { + // se ho trovato un'adiacenza ambigua ( ovvero due triangoli invalidi adiacenti sui loro lati più lunghi) // flip dei due triangoli per modificare il lato più lungo e togliere adiacenza ambigua - FlipTrg( vTr, nTA, nTB, nEA, nEB) ; - // aggiorno per iterazione successiva - if ( vChain.size() == 2) { + FlipTrg( vTr, nTCurr, nTOther, nECurr, nEOther) ; + if ( vChain.size() > 1) { vChain.pop_back() ; vChainEdges.pop_back() ; - FindAdjacentOnLongerEdge( vPt, vTr, vChain.back(), nEA, nTB, nEB) ; - vChainEdges[0] = nEA ; + // individuo il nuovo adiacente all'ultimo triangolo della catena tra i due appena flippati + TestAdjacentOnEdge( vTr, vChain.back(), vChainEdges.back(), nTCurr, nTOther, nTB, nEB) ; + nTOther = nTB ; } - else { - // elimino gli ultimi due triangoli che sono appena stati flippati e ricalcolo adiacenza del triangolo - // precedente - vChain.pop_back() ; - vChain.pop_back() ; - vChainEdges.pop_back() ; - vChainEdges.pop_back() ; - int nTTest1 = nTA ; - int nTTest2 = nTB ; - TestAdjacentOnEdge( vTr, vChain.back(), vChainEdges.back(), nTTest1, nTTest2, nTB, nEB) ; - } } + else { + vChain.emplace_back( nTCurr) ; + vChainEdges.emplace_back( nECurr) ; + } + // aggiorno per iterazione successiva + nTCurr = nTOther ; } // se la catena termina su triangolo nullo, annullo tutti i triangoli della catena - if ( nTB == -1) { + if ( nTCurr == -1) { bRemovedTrg = true ; for ( int k = 0 ; k < int( vChain.size()) ; k++) { + if ( vbIsValidTria[vChain[k]]) + continue ; vTr[3*vChain[k]] = -1 ; vTr[3*vChain[k] + 1] = -1 ; vTr[3*vChain[k] + 2] = -1 ; @@ -1530,14 +1547,16 @@ RemoveFistInvalidTrg( PNTVECTOR& vPt, INTVECTOR& vTr) } // se catena termina su un triangolo valido, applico il flip a cascata a partire dall'ultimo triangolo invalido else { - FlipTrg( vTr, vChain.back(), nTB, vChainEdges.back(), nEB) ; + FlipTrg( vTr, vChain.back(), nTCurr, vChainEdges.back(), nEOther) ; vbIsValidTria[vChain.back()] = true ; int nTrgTest1 = vChain.back() ; - int nTrgTest2 = nTB ; - for ( int i = int( vChain.size()-2) ; i >= 0 ; i--) { + int nTrgTest2 = nTCurr ; + for ( int j = int( vChain.size()-2) ; j >= 0 ; j--) { // triangolo corrente - int nTA = vChain[i] ; - int nEA = vChainEdges[i] ; + int nTA = vChain[j] ; + if ( vbIsValidTria[nTA]) + continue ; + int nEA = vChainEdges[j] ; // devo trovare il nuovo adiacente dopo il flip dei successivi nella catena TestAdjacentOnEdge( vTr, nTA, nEA, nTrgTest1, nTrgTest2, nTB, nEB) ; // flip per rendere valido il triangolo corrente @@ -1608,7 +1627,7 @@ MakeByFist( const POLYLINEVECTOR& vPL, PNTVECTOR& vPt, INTVECTOR& vTr) vPt.reserve( fist.c_vertex.num_vertices) ; for ( int i = 0 ; i < fist.c_vertex.num_vertices ; i ++) vPt.emplace_back( fist.c_vertex.vertices[i].x, fist.c_vertex.vertices[i].y, fist.c_vertex.vertices[i].z) ; - + // recupero i triangoli da fist vTr.reserve( 3 * fist.c_vertex.num_triangles) ; for ( int i = 0 ; i < fist.c_vertex.num_triangles ; i ++) { diff --git a/Triangulate.h b/Triangulate.h index 7145157..b0d9d91 100644 --- a/Triangulate.h +++ b/Triangulate.h @@ -45,6 +45,7 @@ class Triangulate bool TriangleIsCCW( const Point3d& ptA, const Point3d& ptB, const Point3d& ptC, double dToler = 0.1 * EPS_SMALL) ; bool TestIntersection( const Point3d& ptA1, const Point3d& ptA2, const Point3d& ptB1, const Point3d& ptB2) ; bool TestPointInTriangle( const Point3d& ptP, const Point3d& ptA, const Point3d& ptB, const Point3d& ptC) ; + bool TestPointInOrOnTriangle( const Point3d& ptP, const Point3d& ptA, const Point3d& ptB, const Point3d& ptC) ; bool SortInternalLoops( const POLYLINEVECTOR& vPL, INTVECTOR& vOrd) ; bool GetPntVectorFromPolyline( const PolyLine& PL, bool bXmaxStart, PNTVECTOR& vPi) ; bool GetOuterPntToJoin( const PNTVECTOR& vPt, const Point3d& ptP, int& nI) ; diff --git a/VolZmap.cpp b/VolZmap.cpp index bdb0987..35a6ecd 100644 --- a/VolZmap.cpp +++ b/VolZmap.cpp @@ -1520,14 +1520,14 @@ VolZmap::AddSurfTm( const ISurfTriMesh* pStm) // ciclo sulle griglie bool bCompleted = true ; - for ( int g = 0 ; g < m_nMapNum ; ++ g) { + for ( int nG = 0 ; nG < m_nMapNum ; ++ nG) { // definisco dei sistemi di riferimento ausiliari Frame3d frMapFrame ; - if ( g == 0) + if ( nG == 0) frMapFrame = m_MapFrame ; - else if ( g == 1) + else if ( nG == 1) frMapFrame.Set( m_MapFrame.Orig(), Y_AX, Z_AX, X_AX) ; - else if ( g == 2) + else if ( nG == 2) frMapFrame.Set( m_MapFrame.Orig(), Z_AX, X_AX, Y_AX) ; // oggetto per calcolo massivo intersezioni @@ -1538,33 +1538,121 @@ VolZmap::AddSurfTm( const ISurfTriMesh* pStm) vector> vRes ; vRes.resize( nThreadMax) ; // se dimensione griglia in X maggiore di dimensione Y - if ( m_nNx[g] > m_nNy[g]) { - int nDexNum = m_nNx[g] / nThreadMax ; - int nRemainder = m_nNx[g] % nThreadMax ; + if ( m_nNx[nG] > m_nNy[nG]) { + int nDexNum = m_nNx[nG] / nThreadMax ; + int nRemainder = m_nNx[nG] % nThreadMax ; int nInfI = 0 ; int nSupI = 0 ; // aggiungo le parti interessate alla mappa for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { nInfI = nSupI ; nSupI = nInfI + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; - vRes[nThread] = async( launch::async, &VolZmap::AddMapPart, this, g, - nInfI, nSupI, 0, m_nNy[g], ref( vtLen), ref( m_MapFrame.Orig()), - ref( *pStm), ref( intPLSTM)) ; + vRes[nThread] = async( launch::async, &VolZmap::AddMapPart, this, nG, + nInfI, nSupI, 0, m_nNy[nG], ref( vtLen), ref( m_MapFrame.Orig()), + ref( *pStm), ref( intPLSTM)) ; } } // se dimensione griglia in Y maggiore di dimensione X else { - int nDexNum = m_nNy[g] / nThreadMax ; - int nRemainder = m_nNy[g] % nThreadMax ; + int nDexNum = m_nNy[nG] / nThreadMax ; + int nRemainder = m_nNy[nG] % nThreadMax ; int nInfJ = 0 ; int nSupJ = 0 ; // aggiungo le parti interessate alla mappa for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { nInfJ = nSupJ ; nSupJ = nInfJ + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; - vRes[nThread] = async( launch::async, &VolZmap::AddMapPart, this, g, - 0, m_nNx[g], nInfJ, nSupJ, ref( vtLen), ref( m_MapFrame.Orig()), - ref( *pStm), ref( intPLSTM)) ; + vRes[nThread] = async( launch::async, &VolZmap::AddMapPart, this, nG, + 0, m_nNx[nG], nInfJ, nSupJ, ref( vtLen), ref( m_MapFrame.Orig()), + ref( *pStm), ref( intPLSTM)) ; + } + } + + // ciclo per attendere che tutti gli async abbiano terminato. + int nTerminated = 0 ; + while ( nTerminated < nThreadMax) { + for ( int nL = 0 ; nL < nThreadMax ; ++ nL) { + // async terminato + if ( vRes[nL].valid() && vRes[nL].wait_for( chrono::microseconds{ 1}) == future_status::ready) { + ++ nTerminated ; + bCompleted = bCompleted && vRes[nL].get() ; + } + } + } + + if ( ! bCompleted) + return false ; + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +VolZmap::SubtractSurfTm( const ISurfTriMesh* pStm) +{ + // controllo sulla superficie + double dVol ; + if ( pStm == nullptr || ! pStm->IsValid() || ! pStm->IsClosed() || + ! pStm->GetVolume( dVol) || dVol < 0) + return false ; + + // controllo se il Box3d della superficie si interseca con il Box3d dello Zmap corrente + BBox3d BBox_stm, BBox_curr ; + if ( ! pStm->GetLocalBBox( BBox_stm) || ! GetLocalBBox( BBox_curr)) + return false ; + BBox3d BBox_inters ; + if ( BBox_stm.FindIntersection( BBox_curr, BBox_inters) && BBox_inters.IsEmpty()) + return true ; // se non ci sono intersezioni, la superficie non influenza lo Zmap + Vector3d vtLen = BBox_curr.GetMax() - BBox_curr.GetMin() ; // dimensione massima dello spillone + + // ciclo sulle griglie + bool bCompleted = true ; + for ( int nG = 0 ; nG < m_nMapNum ; ++ nG) { + // definisco dei sistemi di riferimento ausiliari + Frame3d frMapFrame ; + if ( nG == 0) + frMapFrame = m_MapFrame ; + else if ( nG == 1) + frMapFrame.Set( m_MapFrame.Orig(), Y_AX, Z_AX, X_AX) ; + else if ( nG == 2) + frMapFrame.Set( m_MapFrame.Orig(), Z_AX, X_AX, Y_AX) ; + + // oggetto per calcolo massivo intersezioni + IntersParLinesSurfTm intPLSTM( frMapFrame, *pStm) ; + + // numero massimo di thread + int nThreadMax = max( 1, int( thread::hardware_concurrency()) - 1) ; + vector> vRes ; + vRes.resize( nThreadMax) ; + // se dimensione griglia in X maggiore di dimensione Y + if ( m_nNx[nG] > m_nNy[nG]) { + int nDexNum = m_nNx[nG] / nThreadMax ; + int nRemainder = m_nNx[nG] % nThreadMax ; + int nInfI = 0 ; + int nSupI = 0 ; + // aggiungo le parti interessate alla mappa + for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { + nInfI = nSupI ; + nSupI = nInfI + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; + vRes[nThread] = async( launch::async, &VolZmap::SubtractMapPart, this, nG, + nInfI, nSupI, 0, m_nNy[nG], ref( vtLen), ref( m_MapFrame.Orig()), + ref( *pStm), ref( intPLSTM)) ; + } + } + // se dimensione griglia in Y maggiore di dimensione X + else { + int nDexNum = m_nNy[nG] / nThreadMax ; + int nRemainder = m_nNy[nG] % nThreadMax ; + int nInfJ = 0 ; + int nSupJ = 0 ; + // aggiungo le parti interessate alla mappa + for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { + nInfJ = nSupJ ; + nSupJ = nInfJ + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; + vRes[nThread] = async( launch::async, &VolZmap::SubtractMapPart, this, nG, + 0, m_nNx[nG], nInfJ, nSupJ, ref( vtLen), ref( m_MapFrame.Orig()), + ref( *pStm), ref( intPLSTM)) ; } } diff --git a/VolZmap.h b/VolZmap.h index 0b047d5..5ddb4d5 100644 --- a/VolZmap.h +++ b/VolZmap.h @@ -18,6 +18,7 @@ #include "Tool.h" #include "/EgtDev/Include/EGkVolZmap.h" #include "/EgtDev/Include/EGkIntersLineVolZmap.h" +#include "/EgtDev/Include/EGkSurfTriMesh.h" #include #include #include @@ -80,10 +81,12 @@ class VolZmap : public IVolZmap, public IGeoObjRW bool Create( const Point3d& ptO, double dDimX, double dDimY, double dDimZ, double dStep, bool bTriDex) override ; bool CreateEmpty( const Point3d& ptO, double dDimX, double dDimY, double dDimZ, double dStep, bool bTriDex) override ; bool CreateFromFlatRegion( const ISurfFlatRegion& Surf, double dDimZ, double dStep, bool bTriDex) override ; - bool CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex) override ; + bool CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex, double dExtraBox = 100 * EPS_SMALL) override ; + bool CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) override ; int GetBlockCount( void) const override ; int GetBlockUpdatingCounter( int nBlock) const override ; bool GetBlockTriangles( int nBlock, TRIA3DEXVECTOR& vTria) const override ; + ISurfTriMesh* GetSurfTriMesh( void) const override ; bool GetEdges( ICURVEPOVECTOR& vpCurve) const override ; bool GetVolume( double& dVol) const override ; bool IsTriDexel( void) const override @@ -93,7 +96,7 @@ class VolZmap : public IVolZmap, public IGeoObjRW { return m_nDexVoxRatio ; } bool ChangeResolution( int nDexVoxRatio) override ; void SetShowEdges( bool bShow) override - { m_bShowEdges = bShow ; // qui è necessario far ricreare la grafica + { m_bShowEdges = bShow ; // qui � necessario far ricreare la grafica m_OGrMgr.Clear() ; } bool GetShowEdges( void) const override { return m_bShowEdges ; } @@ -144,6 +147,7 @@ class VolZmap : public IVolZmap, public IGeoObjRW bool RemovePart( int nPart) override ; int GetPartMinDistFromPoint( const Point3d& ptP) const override ; bool AddSurfTm( const ISurfTriMesh* pStm) override ; + bool SubtractSurfTm( const ISurfTriMesh* pStm) override ; bool MakeUniform( double dToler) override ; public : // IGeoObjRW @@ -291,9 +295,9 @@ class VolZmap : public IVolZmap, public IGeoObjRW bool Conus_Drilling( int nGrid, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtToolDir) ; bool Conus_Milling( int nGrid, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtToolDir) ; bool Mrt_Drilling( int nGrid, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtToolDir, const Vector3d& vtAux) ; - bool Mrt_Milling( int nGrid, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtToolDir, const Vector3d& vtAux) ; // E' in realtà un Perp + bool Mrt_Milling( int nGrid, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtToolDir, const Vector3d& vtAux) ; // E' in realt� un Perp bool Chs_Drilling( int nGrid, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtToolDir, const Vector3d& vtAux) ; - bool Chs_Milling( int nGrid, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtToolDir, const Vector3d& vtAux) ; // E' in realtà un Perp + bool Chs_Milling( int nGrid, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtToolDir, const Vector3d& vtAux) ; // E' in realt� un Perp bool GenTool_Drilling( int nGrid, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtToolDir) ; bool GenTool_Milling( int nGrid, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtToolDir) ; // lavorazioni a 5 assi @@ -319,7 +323,7 @@ class VolZmap : public IVolZmap, public IGeoObjRW const Vector3d& vtArcNormMaxR, const Vector3d& vtArcNormMinR, int nToolNum) ; bool CompPar_ZMilling( int nGrid, double dLenX, double dLenY, double dLenZ, const Point3d& ptS, const Point3d& ptE, - const Vector3d& vtToolDir, const Vector3d& vtAux, int nToolNum) ; // E' in realtà MillingPerp + const Vector3d& vtToolDir, const Vector3d& vtAux, int nToolNum) ; // E' in realt� MillingPerp // Asse di simmetria con orientazione generica bool CompCyl_Drilling( int nGrid, const Point3d& ptS, const Point3d& ptE, const Vector3d& vtToolDir, double dHei, double dRad, bool bTapB, bool bTapT, int nToolNum) ; @@ -335,7 +339,7 @@ class VolZmap : public IVolZmap, public IGeoObjRW const Vector3d& vtArcNormMaxR, const Vector3d& vtArcNormMinR, int nToolNum) ; bool CompPar_Milling( int nGrid, double dLenX, double dLenY, double dLenZ, const Point3d& ptS, const Point3d& ptE, - const Vector3d& vtToolDir, const Vector3d& vtAux, int nToolNum) ; // E' in realtà MillingPerp + const Vector3d& vtToolDir, const Vector3d& vtAux, int nToolNum) ; // E' in realt� MillingPerp // lavorazioni a 5 assi bool Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& ptE, const VCT3DVECTOR& vtLs, const VCT3DVECTOR& vtLe, double dHeight, double dMaxRad, double dMinRad, int nToolNum) ; @@ -450,6 +454,17 @@ class VolZmap : public IVolZmap, public IGeoObjRW const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) ; bool AddMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig, const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) ; + bool SubtractMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig, + const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) ; + // Funzioni per Offset di superfici + bool CreateOffsSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid) ; + bool CreateOffsCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, int nGrid) ; + bool SubtractIntervalsForOffset( int nGrid, int nI, int nJ, + double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, + int nToolNum, bool bSkipSwap = false) ; + bool AddIntervalsForOffset( int nGrid, int nI, int nJ, + double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, + int nToolNum, bool bSkipSwap = false) ; public : // ------------------------- ENUM ---------------------------------------------------------------- enum MillingPhase { @@ -468,14 +483,14 @@ class VolZmap : public IVolZmap, public IGeoObjRW private : enum Status { ERR = 0, OK = 1, TO_VERIFY = 2} ; - enum Shape { GENERIC = 0, BOX = 1, EXTRUSION = 2} ; + enum Shape { GENERIC = 0, BOX = 1, EXTRUSION = 2, OFFSET = 3} ; static const int N_MAPS = 3 ; static const int N_VOXBLOCK = 32 ; private : ObjGraphicsMgr m_OGrMgr ; // gestore grafica dell'oggetto Status m_nStatus ; // stato - int m_nTempProp[2] ; // vettore proprietà temporanee + int m_nTempProp[2] ; // vettore propriet� temporanee double m_dTempParam[2] ; // vettore parametri temporanei bool m_bShowEdges ; // flag di visualizzazione spigoli vivi Frame3d m_MapFrame ; // riferimento intrinseco dello Zmap @@ -509,8 +524,8 @@ class VolZmap : public IVolZmap, public IGeoObjRW mutable BOOLVECTOR m_BlockToUpdate ; mutable INTVECTOR m_BlockUpdatingCounter ; - int m_nConnectedCompoCount ; // Se == - 1 il numero di componenti non è noto - // Se >= 0 è il numero di componenti connesse + int m_nConnectedCompoCount ; // Se == - 1 il numero di componenti non � noto + // Se >= 0 � il numero di componenti connesse mutable std::vector m_InterBlockVox ; mutable SharpTriaMatrix m_InterBlockOriginalSharpTria ; diff --git a/VolZmapCreation.cpp b/VolZmapCreation.cpp index 4fefdee..fef6eae 100644 --- a/VolZmapCreation.cpp +++ b/VolZmapCreation.cpp @@ -17,6 +17,7 @@ #include "CurveLine.h" #include "VolZmap.h" #include "GeoConst.h" +#include "/EgtDev/Include/EGkStmFromCurves.h" #include "/EgtDev/Include/EGkIntersLineSurfTm.h" #include "/EgtDev/Include/EgtNumUtils.h" #include @@ -597,6 +598,7 @@ VolZmap::CreateMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, co return true ; } +//---------------------------------------------------------------------------- bool VolZmap::AddMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig, const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) @@ -691,7 +693,100 @@ VolZmap::AddMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const //---------------------------------------------------------------------------- bool -VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex) +VolZmap::SubtractMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig, + const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) +{ + // controllo sui parametri + if ( nMap < 0 || nMap > 2 || + nInfI < 0 || nInfI > m_nNx[nMap] || + nSupI < 0 || nSupI > m_nNx[nMap] || + nInfJ < 0 || nInfJ > m_nNy[nMap] || + nSupJ < 0 || nSupJ > m_nNy[nMap]) + return false ; + + // determinazione e ridimensionamento dei dexel interni alla trimesh + for ( int i = nInfI ; i < nSupI ; ++ i) { + for ( int j = nInfJ ; j < nSupJ ; ++ j) { + + // definisco la retta da intersecare con la trimesh + double dX = ( i + 0.5) * m_dStep ; + double dY = ( j + 0.5) * m_dStep ; + Point3d ptP0( dX, dY, 0) ; + + // intersezioni della retta con la TriMesh + ILSIVECTOR IntersectionResults ; + intPLSTM.GetInters( ptP0, vtLen.v[(nMap+2)%3], IntersectionResults) ; + + // rimuovo le intersezioni in eccesso + for ( int nI = 0 ; nI < int( IntersectionResults.size()) - 3 ; ++ nI) { + int nJ = nI + 1 ; // prima successiva + int nK = nJ + 1 ; // seconda successiva + int nT = nK + 1 ; // terza successiva + // determino i segni delle 4 intersezioni tra la linea e il trangolo della TriMesh + int nSgnI = IntersectionResults[nI].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nI].dCosDN > -EPS_SMALL ? 0 : - 1 ; + int nSgnJ = IntersectionResults[nJ].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nJ].dCosDN > -EPS_SMALL ? 0 : - 1 ; + int nSgnK = IntersectionResults[nK].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nK].dCosDN > -EPS_SMALL ? 0 : - 1 ; + int nSgnT = IntersectionResults[nT].dCosDN > EPS_SMALL ? 1 : IntersectionResults[nT].dCosDN > -EPS_SMALL ? 0 : - 1 ; + // parametri dell'intersezione sulla linea + double dUJ = IntersectionResults[nJ].dU ; + double dUK = IntersectionResults[nK].dU ; + // controllo coerenza con segni... + if ( nSgnI != 0 && nSgnI == nSgnJ && + nSgnK != 0 && nSgnK == nSgnT && + nSgnI == - nSgnT && + abs( dUJ - dUK) < EPS_SMALL) { + // ... ed elimino le intersezioni in eccesso... + IntersectionResults.erase( IntersectionResults.begin() + nK) ; + IntersectionResults.erase( IntersectionResults.begin() + nJ) ; + } + } + + int nInt = int( IntersectionResults.size()) ; // numero di intersezioni valide + bool bInside = false ; // Flag entrata/uscita per tratto di retta + Point3d ptIn ; Vector3d vtInN ; + + // per ogni intersezione valida trovata... + for ( int k = 0 ; k < nInt ; ++ k) { + // ricavo il tipo di intersezione + int nIntType = IntersectionResults[k].nILTT ; + // se c'è intersezione + if ( nIntType != ILTT_NO) { + // ricavo il cos tra i vettori ( normale del triangolo e tangente alla retta) + double dCos = IntersectionResults[k].dCosDN ; + + // se entro nella superficie trimesh... + if ( dCos < - EPS_SMALL) { + ptIn = IntersectionResults[k].ptI ; // punto di intersezione + int nT = IntersectionResults[k].nT ; // triangolo di interesse + int nF = Surf.GetFacetFromTria( nT) ; // faccia di interesse + Surf.GetFacetNormal( nF, vtInN) ; + bInside = true ; // entrata + } + // ...se esco dalla superficie trimesh ( prima sono per forza entrato) + else if ( dCos > EPS_SMALL && bInside) { + Point3d ptOut = IntersectionResults[k].ptI ; // punto di intersezione + int nT = IntersectionResults[k].nT ; // triangolo di interesse + int nF = Surf.GetFacetFromTria( nT) ; // faccia di interesse + Vector3d vtOutN ; Surf.GetFacetNormal( nF, vtOutN) ; // vettore d'uscita + + // Aggiungo un tratto al dexel + SubtractIntervals( nMap, i, j, + ptIn.v[(nMap+2)%3] - ptMapOrig.v[(nMap+2)%3], + ptOut.v[(nMap+2)%3] - ptMapOrig.v[(nMap+2)%3], + - vtInN, - vtOutN, 0, true) ; + bInside = false ; // uscita + } + } + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +bool +VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex, double dExtraBox) { // Se la superficie non è chiusa oppure orientata al contrario non ha senso continuare double dVol ; @@ -704,14 +799,14 @@ VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex // Determino il bounding box della TriMesh BBox3d SurfBBox ; Surf.GetLocalBBox( SurfBBox) ; - - // Determino i punti estremi del bounding box - Point3d ptMapOrig, ptMapEnd ; - SurfBBox.GetMinMax( ptMapOrig, ptMapEnd) ; // Il dexel se parte da un triangolo della trimesh può non trovare l'intersezione, // quindi espandiamo il bounding box per ovviare al problema. - SurfBBox.Expand( 100 * EPS_SMALL, 100 * EPS_SMALL, 100 * EPS_SMALL) ; + SurfBBox.Expand( dExtraBox) ; + + // Determino i punti estremi del bounding box + Point3d ptMapOrig, ptMapEnd ; + SurfBBox.GetMinMax( ptMapOrig, ptMapEnd) ; // Sistema di riferimento intrinseco dello Zmap m_MapFrame.Set( ptMapOrig, Frame3d::TOP) ; @@ -760,15 +855,15 @@ VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex // ciclo sulle griglie bool bCompleted = true ; - for ( int g = 0 ; g < m_nMapNum ; ++ g) { + for ( int nG = 0 ; nG < m_nMapNum ; ++ nG) { // Definisco dei sistemi di riferimento ausiliari Frame3d frMapFrame ; - if ( g == 0) + if ( nG == 0) frMapFrame = m_MapFrame ; - else if ( g == 1) + else if ( nG == 1) frMapFrame.Set( ptMapOrig, Y_AX, Z_AX, X_AX) ; - else if ( g == 2) + else if ( nG == 2) frMapFrame.Set( ptMapOrig, Z_AX, X_AX, Y_AX) ; // Oggetto per calcolo massivo intersezioni @@ -778,28 +873,28 @@ VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex int nThreadMax = max( 1, int( thread::hardware_concurrency()) - 1) ; vector< future> vRes ; vRes.resize( nThreadMax) ; - if ( m_nNx[g] > m_nNy[g]) { - int nDexNum = m_nNx[g] / nThreadMax ; - int nRemainder = m_nNx[g] % nThreadMax ; + if ( m_nNx[nG] > m_nNy[nG]) { + int nDexNum = m_nNx[nG] / nThreadMax ; + int nRemainder = m_nNx[nG] % nThreadMax ; int nInfI = 0 ; int nSupI = 0 ; for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { nInfI = nSupI ; nSupI = nInfI + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; - vRes[nThread] = async( launch::async, &VolZmap::CreateMapPart, this, g, - nInfI, nSupI, 0, m_nNy[g], ref( vtLen), ref( ptMapOrig), ref( Surf), ref( intPLSTM)) ; + vRes[nThread] = async( launch::async, &VolZmap::CreateMapPart, this, nG, + nInfI, nSupI, 0, m_nNy[nG], ref( vtLen), ref( ptMapOrig), ref( Surf), ref( intPLSTM)) ; } } else { - int nDexNum = m_nNy[g] / nThreadMax ; - int nRemainder = m_nNy[g] % nThreadMax ; + int nDexNum = m_nNy[nG] / nThreadMax ; + int nRemainder = m_nNy[nG] % nThreadMax ; int nInfJ = 0 ; int nSupJ = 0 ; for ( int nThread = 0 ; nThread < nThreadMax ; ++ nThread) { nInfJ = nSupJ ; nSupJ = nInfJ + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ; - vRes[nThread] = async( launch::async, &VolZmap::CreateMapPart, this, g, - 0, m_nNx[g], nInfJ, nSupJ, ref( vtLen), ref( ptMapOrig), ref( Surf),ref( intPLSTM)) ; + vRes[nThread] = async( launch::async, &VolZmap::CreateMapPart, this, nG, + 0, m_nNx[nG], nInfJ, nSupJ, ref( vtLen), ref( ptMapOrig), ref( Surf),ref( intPLSTM)) ; } } diff --git a/VolZmapGraphics.cpp b/VolZmapGraphics.cpp index 01fe8ae..9fff5ef 100644 --- a/VolZmapGraphics.cpp +++ b/VolZmapGraphics.cpp @@ -19,6 +19,7 @@ #include "MC_Tables.h" #include "PolygonPlane.h" #include "IntersLineBox.h" +#include "SurfTriMesh.h" #include "/EgtDev/Include/EGkIntervals.h" #include "/EgtDev/Include/EGkStringUtils3d.h" #include "/EgtDev/Include/EGkChainCurves.h" @@ -571,6 +572,35 @@ VolZmap::GetBlockTriangles( int nBlock, TRIA3DEXVECTOR& vTria) const } } +//---------------------------------------------------------------------------- +ISurfTriMesh* +VolZmap::GetSurfTriMesh( void) const +{ + // controllo che lo Zmap sia valido + if ( ! IsValid()) + return nullptr ; + + // inizializzo la superficie + PtrOwner pStm( CreateBasicSurfTriMesh()) ; + if ( IsNull( pStm) || ! pStm->Init( 3, 1)) + return nullptr ; + PointGrid3d VertGrid ; VertGrid.Init( 50000) ; + + // ciclo lungo i blocchi dello Zmap + for ( int nB = 0 ; nB < GetBlockCount() ; ++ nB) { + TRIA3DEXVECTOR vTria ; + GetBlockTriangles( nB, vTria) ; + if ( ! pStm->AddTriaFromZMap( vTria, VertGrid)) + return nullptr ; + } + + // sistemo la topologia + if ( ! pStm->AdjustTopologyFromZMap()) + return nullptr ; + + return ( Release( pStm)) ; +} + //---------------------------------------------------------------------------- int VolZmap::GetBlockCount( void) const @@ -1142,7 +1172,7 @@ VolZmap::ExtMarchingCubes( int nBlock, VoxelContainer& vVox) const } // Controllo se il voxel ha una sola faccia che giace in un piano canonico e quindi ha gestione speciale - if ( m_nShape != BOX) { + if ( m_nShape != BOX && m_nShape != OFFSET) { // Faccia XY normale Z+ if ( nIndex == 15) { int nTool ; double dPos ; diff --git a/VolZmapOffset.cpp b/VolZmapOffset.cpp new file mode 100644 index 0000000..1812b0a --- /dev/null +++ b/VolZmapOffset.cpp @@ -0,0 +1,300 @@ +//---------------------------------------------------------------------------- +// EgalTech 2025-2025 +//---------------------------------------------------------------------------- +// File : OffsetSurfTm.cpp Data : 09.06.25 Versione : 2.7e3 +// Contenuto : Dichiarazione della funzione per calcolare l'offset di superfici TriMesh +// mediante Zmap +// +// +// +// Modifiche : 09.06.25 RE Creazione modulo. +// +// +//---------------------------------------------------------------------------- + +#include "stdafx.h" +#include "VolZmap.h" +#include "CurveLine.h" +#include "GeoConst.h" +#include "/EgtDev/Include/EGkStmFromCurves.h" +#include "/EgtDev/Include/EGkIntersLineSurfTm.h" +#include "/EgtDev/Include/EgtNumUtils.h" +#include + +using namespace std ; + + +//---------------------------------------------------------------------------- +/* Funzione per aggiungere intervalli lungo un Dexel per l'offset di una superficie TriMesh */ +bool +VolZmap::SubtractIntervalsForOffset( int nGrid, int nI, int nJ, + double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, + int nToolNum, bool bSkipSwap) +{ + // per ora la funzione è la stessa della differenza in generale + // TODO -- Aggiustare eventuali tolleranze + return SubtractIntervals( nGrid, nI, nJ, dMin, dMax, vtNMin, vtNMax, nToolNum, bSkipSwap) ; +} + +//---------------------------------------------------------------------------- +/* Funzione per aggiungere intervalli lungo un Dexel per l'offset di una superficie TriMesh */ +bool +VolZmap::AddIntervalsForOffset( int nGrid, int nI, int nJ, + double dMin, double dMax, const Vector3d& vtNMin, const Vector3d& vtNMax, + int nToolNum, bool bSkipSwap) +{ + // per ora la funzione è la stessa della somma generale + // TODO -- Aggiustare eventuali tolleranze + return AddIntervals( nGrid, nI, nJ, dMin, dMax, vtNMin, vtNMax, nToolNum, bSkipSwap) ; +} + +//---------------------------------------------------------------------------- +/* Funzione per la creazione di una sfera di Offset centrata sul vertice di una TriMesh con cui + aggiungere o sottrarre intervalli lungo i Dexel coinvolti */ +bool +VolZmap::CreateOffsSphereOnVertex( const Point3d& ptV, double dOffs, int nGrid) +{ + // determino il Box della sfera posizionata su tale vertice + BBox3d BBoxSphere( ptV - dOffs * Vector3d( 1., 1., 1.), + ptV + dOffs * Vector3d( 1., 1., 1.)) ; + // determino gli intervalli di interesse mediante intersezione con Box della sfera + int nStartI = max( 0, int( BBoxSphere.GetMin().x / m_dStep)) ; + int nEndI = min( m_nNx[nGrid] - 1, int( BBoxSphere.GetMax().x / m_dStep)) ; + int nStartJ = max( 0, int( BBoxSphere.GetMin().y / m_dStep)) ; + int nEndJ = min( m_nNy[nGrid] - 1, int( BBoxSphere.GetMax().y / m_dStep)) ; + // aggiorno gli spilloni interessati + double dSqRad = dOffs * dOffs ; + for ( int i = nStartI ; i <= nEndI ; ++ i) { + for ( int j = nStartJ ; j <= nEndJ ; ++ j) { + double dX = ( i + 0.5) * m_dStep ; + double dY = ( j + 0.5) * m_dStep ; + Point3d ptC( dX, dY, 0.) ; + double dStSqDXY = SqDistXY( ptC, ptV) ; + if ( dStSqDXY < dSqRad) { + double dMin = ptV.z - sqrt( dSqRad - dStSqDXY) ; + Vector3d vtNmin = Point3d( dX, dY, dMin) - ptV ; + vtNmin.Normalize() ; + double dMax = ptV.z + sqrt( dSqRad - dStSqDXY) ; + Vector3d vtNmax = Point3d( dX, dY, dMax) - ptV ; + vtNmax.Normalize() ; + if ( dOffs > 0.) + AddIntervalsForOffset( nGrid, i, j, dMin, dMax, vtNmin, vtNmax, 0) ; + else + SubtractIntervalsForOffset( nGrid, i, j, dMin, dMax, -vtNmin, -vtNmax, 0) ; + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per la creazione di un cilindro di Offset sul vertice di una TriMesh con cui + aggiungere o sottrarre intervalli lungo i Dexel coinvolti */ +bool +VolZmap::CreateOffsCylinderOnEdge( const Point3d& ptP1, const Point3d& ptP2, double dOffs, int nGrid) +{ + // determino la lunghezza dello spigolo corrente + double dH = Dist( ptP1, ptP2) ; + // asse del cilindro + Vector3d vtV = ptP2 - ptP1 ; vtV.Normalize() ; + // calcolo box del cilindro + BBox3d BBoxCylinder ; + BBoxCylinder.Add( ptP1) ; + BBoxCylinder.Add( ptP2) ; + if ( AreSameOrOppositeVectorApprox( vtV, X_AX)) + BBoxCylinder.Expand( 0., abs( dOffs), abs( dOffs)) ; + else if ( AreSameOrOppositeVectorApprox( vtV, Y_AX)) + BBoxCylinder.Expand( abs( dOffs), 0., abs( dOffs)) ; + else if ( AreSameOrOppositeVectorApprox( vtV, Z_AX)) + BBoxCylinder.Expand( abs( dOffs), abs( dOffs), 0.) ; + else { + double dExpandX = abs( dOffs) * sqrt( 1 - vtV.x * vtV.x) ; + double dExpandY = abs( dOffs) * sqrt( 1 - vtV.y * vtV.y) ; + double dExpandZ = abs( dOffs) * sqrt( 1 - vtV.z * vtV.z) ; + BBoxCylinder.Expand( dExpandX, dExpandY, dExpandZ) ; + } + // determino gli intervalli di interesse mediante intersezione + int nStartI = max( 0, int( BBoxCylinder.GetMin().x / m_dStep)) ; + int nEndI = min( m_nNx[nGrid] - 1, int( BBoxCylinder.GetMax().x / m_dStep)) ; + int nStartJ = max( 0, int( BBoxCylinder.GetMin().y / m_dStep)) ; + int nEndJ = min( m_nNy[nGrid] - 1, int( BBoxCylinder.GetMax().y / m_dStep)) ; + // aggiorno gli spilloni interessati + Frame3d CylFrame ; + if ( ! CylFrame.Set( ptP1, vtV)) + return false ; + for ( int i = nStartI ; i <= nEndI ; ++ i) { + for ( int j = nStartJ ; j <= nEndJ ; ++ j) { + Point3d ptC( ( i + 0.5) * m_dStep, ( j + 0.5) * m_dStep, 0) ; + Point3d ptInt1, ptInt2 ; + Vector3d vtN1, vtN2 ; + if ( IntersLineCylinder( ptC, Z_AX, CylFrame, dH, abs( dOffs), true, true, + ptInt1, vtN1, ptInt2, vtN2)) { + if ( dOffs > 0.) + AddIntervalsForOffset( nGrid, i, j, ptInt1.z, ptInt2.z, -vtN1, -vtN2, 0) ; + else + SubtractIntervalsForOffset( nGrid, i, j, ptInt1.z, ptInt2.z, vtN1, vtN2, 0) ; + } + } + } + + return true ; +} + +//---------------------------------------------------------------------------- +/* Funzione per la creazine di uno Zmap di Offset (positivo o negativo) a partire da una + superficie TriMesh */ +bool +VolZmap::CreateFromTriMeshOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) +{ + // controllo delle superfici + for ( const ISurfTriMesh* Surf : vSurf) { + if ( Surf == nullptr) + return false ; + } + // verifica sul parametro di Offset ( coerente con Curve e FlatRegion) + if ( abs( dOffs) < 10 * EPS_SMALL) + return true ; + // se non ho superfici, non faccio nulla + if ( vSurf.empty()) + return true ; + + // definisco lo Zmap di partenza a partire dalle superfici + // se una sola superficie + double dBoxExpansion = ( abs( dOffs) + 1.5 * dTol) + 10 * EPS_SMALL ; + if ( int( vSurf.size()) == 1) { + // controllo la validità della superficie + if ( ! vSurf[0]->IsValid() || vSurf[0]->GetTriangleCount() == 0) + return true ; + // definisco lo Zamp a partire dall'espansione del Box della superficie + if ( ! CreateFromTriMesh( *vSurf[0], dTol, true, dBoxExpansion)) + return false ; + } + // se più superfici + else { + // calcolo il Box complessivo delle superfici TriMesh + BBox3d BBoxGlob ; + for ( int i = 0 ; i < int( vSurf.size()) ; ++ i) { + // controllo la validità della superficie + if ( ! vSurf[i]->IsValid() || vSurf[i]->GetTriangleCount() == 0) + continue ; + // calcolo il Box della superficie + BBox3d BBoxSurf ; vSurf[i]->GetLocalBBox( BBoxSurf) ; + // aggiungo il Box a quello complessivo + BBoxGlob.Add( BBoxSurf) ; + } + // definisco uno Zmap vuoto a partire dal Box + BBoxGlob.Expand( dBoxExpansion) ; + if ( ! CreateEmpty( BBoxGlob.GetMin(), BBoxGlob.GetDimX(), BBoxGlob.GetDimY(), BBoxGlob.GetDimZ(), dTol, true)) + return false ; + for ( const ISurfTriMesh* Surf : vSurf) { + if ( ! AddSurfTm( Surf)) + return false ; + } + } + + /* Assunzioni : + - Idea Generale di Offset + - Su ogni vertice viene definita una sfera ( con raggio pari al valore di Offset e + centro il vertice corrente) + - Su ogni lato viene definito un cilindro ( con raggio di pase pari al valore di Offset + e asse definito dall'edge stesso) + - Su ogni faccia viene definita una superficie di estrusione ( dove le due basi sono + definite dalla traslazione sia in positivo che in negativo della faccia lungo la sua normale) + + - Segno dell'Offset : + - Positivo ( si sommano gli intervalli corrisipondenti alle entità create) + - Negativo ( si sottraggono gli intervalli corrispondenti alle enetità create) + + - Semplificazione entità : + La creazione di Sfere e Cilindri potrebbe essere resa più "corretta" definitendo solo + "spicchi 3d" di sfera e "spicchi 3d" di cilindri. Dato che le operazioni di somma, sottrazioni e + calcolo delle normali per gli spilloni sono elementari su queste figure, si rischia di appesantire + troppo i conti introducendo variabili angolari che non sommando tutte le parti. + */ + + // definisco vettore di frame Locali alle 3 griglie + FRAME3DVECTOR vFrGrid( 4) ; + vFrGrid[0].Set( ORIG, X_AX, Y_AX, Z_AX) ; + vFrGrid[1].Set( m_MapFrame.Orig(), m_MapFrame.VersX(), m_MapFrame.VersY(), m_MapFrame.VersZ()) ; + vFrGrid[2].Set( m_MapFrame.Orig(), m_MapFrame.VersY(), m_MapFrame.VersZ(), m_MapFrame.VersX()) ; + vFrGrid[3].Set( m_MapFrame.Orig(), m_MapFrame.VersZ(), m_MapFrame.VersX(), m_MapFrame.VersY()) ; + + + // scorro le superfici + for ( const ISurfTriMesh* Surf : vSurf) { + // se superficie non valida, passo alla successiva + if ( ! Surf->IsValid() || Surf->GetTriangleCount() == 0) + continue ; + // definisco una mappa dei vertici, in modo da sapere su quali sono state già create le sfere + BOOLVECTOR vbVert( Surf->GetVertexCount(), false) ; + // ----------------------- Cilindri e Sfere ----------------------- + // scorro gli Edge della superficie + for ( int nE = 0 ; nE < Surf->GetEdgeCount() ; ++ nE) { + // recupero lo spigolo + int nV1, nV2, nF1, nF2 ; double dAng ; + Surf->GetEdge( nE, nV1, nV2, nF1, nF2, dAng) ; + // controllo se il cilindro serve + // NB. la mancata creazione del cilindro comporta la mancata creazione delle sfere sui suoi vertici + // durante questa iterazione; non significa che questa sfera non verrà mai creata... + // Non esiste la sfera sul vertive V <=> non esiste alcun cilindro su tutti gli edge concorrenti + if ( dAng * dOffs < 0) + continue ; + // recupero le coordinate dei vertici + Point3d ptP1 ; Surf->GetVertex( nV1, ptP1) ; + Point3d ptP2 ; Surf->GetVertex( nV2, ptP2) ; + // ciclo sulle griglie + for ( int nGrid = 0 ; nGrid < 3 ; ++ nGrid) { + // esprimo gli estremi nel riferimento della griglia + ptP1.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; + ptP2.LocToLoc( vFrGrid[nGrid], vFrGrid[nGrid + 1]) ; + // aggiungo/sottraggo gli intervalli definiti dal cilindro + if ( ! CreateOffsCylinderOnEdge( ptP1, ptP2, dOffs, nGrid)) + return false ; + // aggiungo/sottraggo gli intervalli definiti dalla sfera + if ( ! vbVert[nV1]) { + vbVert[nV1] = ( nGrid == 2) ; + if ( ! CreateOffsSphereOnVertex( ptP1, dOffs, nGrid)) + return false ; + } + if ( ! vbVert[nV2]) { + vbVert[nV2] = ( nGrid == 2) ; + if ( ! CreateOffsSphereOnVertex( ptP2, dOffs, nGrid)) + return false ; + } + } + } + // ----------------------- Facce ----------------------- + // scorro tutte le facce definendo una superficie di estrusione + for ( int nF = 0 ; nF < Surf->GetFacetCount() ; ++ nF) { + // recupero lo faccia + POLYLINEVECTOR vPL ; Surf->GetFacetLoops( nF, vPL) ; + // recupero la normale della faccia + Vector3d vtN ; Surf->GetFacetNormal( nF, vtN) ; + // definisco la superficie di estrusione + CICURVEPVECTOR vpCrvs ; vpCrvs.reserve( vPL.size()) ; + for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { + vPL[i].Translate( - abs( dOffs) * vtN) ; + PtrOwner pCrvCompo( CreateCurveComposite()) ; + if ( IsNull( pCrvCompo) || ! pCrvCompo->FromPolyLine( vPL[i])) + return false ; + vpCrvs.emplace_back( Release( pCrvCompo)) ; + } + // recupero la TriMesh di estrusione + PtrOwner pStmExtr( GetSurfTriMeshByRegionExtrusion( vpCrvs, 2 * abs( dOffs) * vtN)) ; + if ( IsNull( pStmExtr) || ! pStmExtr->IsValid() || pStmExtr->GetTriangleCount() == 0) + return false ; + // aggiorno gli spilloni + if ( dOffs > 0.) + AddSurfTm( pStmExtr) ; + else + SubtractSurfTm( pStmExtr) ; + } + } + m_nShape = OFFSET ; // OFFSET (?) ... per ora va bene così + + return true ; +} + + + diff --git a/VolZmapVolume.cpp b/VolZmapVolume.cpp index b04339e..f912546 100644 --- a/VolZmapVolume.cpp +++ b/VolZmapVolume.cpp @@ -1999,13 +1999,28 @@ bool VolZmap::MillingGeneralMotionStep( const Point3d& ptPs, const Vector3d& vtDs, const Vector3d& vtAs, const Point3d& ptPe, const Vector3d& vtDe, const Vector3d& vtAe) { - // Calcolo angoli di rotazione utensile longitudinale e trasversale rispetto al movimento + // Deve essere definito l'utensile corrente + if ( m_nCurrTool < 0 || m_nCurrTool >= int( m_vTool.size())) + return false ; + // Calcolo angoli di rotazione utensile longitudinale e trasversale rispetto al movimento per eventuale suddivisione double dAlongAngDeg, dAcrossAngDeg ; GetAlongAcrossRotation( vtDs, vtDe, ptPe - ptPs, dAlongAngDeg, dAcrossAngDeg) ; // Divido il movimento in tratti con direzione utensile costante const double ANG_ACROSS_STEP = 1.0 ; const double ANG_ALONG_STEP = 1.0 ; int nStepCnt = int( max( { abs( dAlongAngDeg) / ANG_ALONG_STEP, abs( dAcrossAngDeg) / ANG_ACROSS_STEP, 1.})) ; + // Calcolo coefficiente di riduzione movimenti con direzione costante per evitare extra tagli + // !!! In attesa dell'algoritmo esatto !!! + double dToolLen = m_vTool[m_nCurrTool].GetHeigth() ; + double dToolCrad = m_vTool[m_nCurrTool].GetCornRadius() ; + Vector3d vtBase = ptPe - ptPs ; + double dBaseLen = vtBase.Len() ; + Vector3d vtTip = ptPe - dToolLen * vtDe - ( ptPs - dToolLen * vtDs) ; + double dTipLen = vtTip.Len() ; + double dK = ( vtBase * vtTip > EPS_SMALL ? dTipLen / dBaseLen : 0) ; + if ( dTipLen / nStepCnt < 0.1 * dToolCrad) + dK = 0 ; + // Divido il movimento in tratti con direzione utensile costante bool bOk = true ; //////// debug - vecchia modalità diff --git a/Voronoi.cpp b/Voronoi.cpp index 8321f49..4feb971 100644 --- a/Voronoi.cpp +++ b/Voronoi.cpp @@ -847,18 +847,19 @@ Voronoi::CalcSpecialPointOffset( PNTVECTVECTOR& vResult, double dOffs) //---------------------------------------------------------------------------- bool -Voronoi::CalcFatCurve( ICURVEPOVECTOR& vCrvs, double dOffs, bool bSquareEnds, bool bSquareMids) +Voronoi::CalcFatCurve( ICURVEPOVECTOR& vCrvs, double dOffs, bool bSquareEnds, bool bSquareMids, + bool bMergeOnlySameProps) { vCrvs.clear() ; if ( ! IsValid()) - return false ; - - // se offset nullo errore - if ( abs( dOffs) < EPS_SMALL) return false ; - ICRVCOMPOPLIST OffsList ; + // se offset nullo errore + if ( abs( dOffs) < EPS_SMALL) + return false ; + + ICRVCOMPOPLIST OffsList ; if ( ! CalcVroniOffset( OffsList, abs( dOffs))) return false ; @@ -873,7 +874,7 @@ Voronoi::CalcFatCurve( ICURVEPOVECTOR& vCrvs, double dOffs, bool bSquareEnds, bo // identifico i raccordi da modificare if ( bClosed && bSquareMids) { // se curva è chiusa tutti i raccordi rispondono a bSquareMids - IdentifyFillets( pCrvOffs, dOffs) ; + IdentifyFillets( pCrvOffs, dOffs) ; } else if ( ! bClosed && ( bSquareMids || bSquareEnds)) { // se curva è aperta devo distinguere i raccordi interni da quelli relativi agli estremi e @@ -889,9 +890,9 @@ Voronoi::CalcFatCurve( ICURVEPOVECTOR& vCrvs, double dOffs, bool bSquareEnds, bo } // unisco le parti allineate - pCrvOffs->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG, true, true) ; + pCrvOffs->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG, true, bMergeOnlySameProps) ; // aggiungo al vettore finale - vCrvs.emplace_back( pCrvOffs) ; + vCrvs.emplace_back( pCrvOffs) ; } // sistemo i raccordi diff --git a/Voronoi.h b/Voronoi.h index 8f6e1fc..d0f033b 100644 --- a/Voronoi.h +++ b/Voronoi.h @@ -57,7 +57,7 @@ class Voronoi bool CalcOffset( ICURVEPOVECTOR& vOffs, double dOffs, int nType) ; bool CalcSingleCurvesOffset( ICURVEPOVECTOR& vOffs, double dOffs) ; bool CalcSpecialPointOffset( PNTVECTVECTOR& vResult, double dOffs) ; - bool CalcFatCurve( ICURVEPOVECTOR& vOffs, double dOffs, bool bSquareEnds, bool bSquareMids) ; + bool CalcFatCurve( ICURVEPOVECTOR& vOffs, double dOffs, bool bSquareEnds, bool bSquareMids, bool bMergeOnlySameProps = true) ; bool CalcMedialAxis( ICURVEPOVECTOR& vCrvs, int nSide) ; bool CalcLimitOffset( int nCrv, bool bLeft, double& dOffs) ;