From f48bf06f6444a33921503f1b942665f433eaefbb Mon Sep 17 00:00:00 2001 From: SaraP Date: Mon, 15 Jun 2026 09:57:55 +0200 Subject: [PATCH] EgtGeomKernel 3.1f3 : - migliorie nella creazione di solidi swept con sezione rettangolare smussata - correzioni errori e aggiunte funzioni per calcolo bisettori di Voronoi - aggiunto parametro a GetChainedCurves per fermare su biforcazione - piccola miglioria a AssociatePolyLinesMinDistPoints. --- CurveAux.cpp | 4 +- CurveAux.h | 3 +- EgtGeomKernel.rc | Bin 11710 -> 11710 bytes PolyLine.cpp | 10 + StmFromCurves.cpp | 1350 ++++++++++++++++++++++++--------------------- Voronoi.cpp | 73 ++- Voronoi.h | 2 + 7 files changed, 810 insertions(+), 632 deletions(-) diff --git a/CurveAux.cpp b/CurveAux.cpp index 1580d02..d0edc1a 100644 --- a/CurveAux.cpp +++ b/CurveAux.cpp @@ -2652,7 +2652,7 @@ ResetCurveVoronoi( const ICurve& crvC) //---------------------------------------------------------------------------- bool -GetChainedCurves( ICRVCOMPOPOVECTOR& vCrv, double dChainTol, bool bAllowInvert) +GetChainedCurves( ICRVCOMPOPOVECTOR& vCrv, double dChainTol, bool bAllowInvert, bool bHaltOnFork) { if ( ssize( vCrv) == 1) return true ; @@ -2670,7 +2670,7 @@ GetChainedCurves( ICRVCOMPOPOVECTOR& vCrv, double dChainTol, bool bAllowInvert) } INTVECTOR vIds ; Point3d ptStart = ORIG ; - while ( chainCrv.GetChainFromNear( ptStart, false, vIds)) { + while ( chainCrv.GetChainFromNear( ptStart, bHaltOnFork, vIds)) { int nFirst = vIds[0] ; bool bInvert = false ; if ( nFirst < 0) diff --git a/CurveAux.h b/CurveAux.h index 56c1b8e..761d2e3 100644 --- a/CurveAux.h +++ b/CurveAux.h @@ -34,5 +34,4 @@ bool CurveDump( const ICurve& crvC, std::string& sOut, bool bMM, const char* szN bool CopyExtrusion( const ICurve* pSouCrv, ICurve* pDestCrv) ; bool CopyThickness( const ICurve* pSouCrv, ICurve* pDestCrv) ; ICurveBezier* ApproxCurveBezierWithSingleCubic( const ICurve* pCrv) ; -Voronoi* GetCurveVoronoi( const ICurve& crvC) ; -bool GetChainedCurves( ICRVCOMPOPOVECTOR& vCrv, double dChainTol, bool bAllowInvert) ; \ No newline at end of file +Voronoi* GetCurveVoronoi( const ICurve& crvC) ; \ No newline at end of file diff --git a/EgtGeomKernel.rc b/EgtGeomKernel.rc index 2e1f88f10a8310c00edef3c805e6eb0f07b7c675..fe5c63d2773dc868159ab20154970c28ff15622b 100644 GIT binary patch delta 81 zcmdlNy)SyhH#SD&&FAG5nSs pCurrCrv( pFatCrv->RemoveFirstOrLastCurve( false)) ; while ( ! IsNull( pCurrCrv)) { - if ( abs( pCurrCrv->GetTempParam() - VRONI_JUNCTION_OPEN) < EPS_SMALL) { + if ( abs( pCurrCrv->GetTempParam() - VRONI_JUNCTION_OPEN) < EPS_SMALL || + pCurrCrv->GetTempProp() == RS_FLATCAP_START || + pCurrCrv->GetTempProp() == RS_FLATCAP_END) { if ( pCrv->IsValid()) { // salvo la catena trovata fino ad ora e la resetto vResCurves.emplace_back( Release( pCrv)) ; @@ -410,105 +414,125 @@ RemoveFatCurveJunctions( ICURVEPOVECTOR& vCrvs) } //------------------------------------------------------------------------------- -static ISurfFlatRegion* -CalcSweptSurface( ICurveComposite* pGuide, const Vector3d& vtNorm, double dOffs) +static SurfFlatRegion* +CalcSideSweptRegion( const ICurve* pOrigGuide, const Vector3d& vtNorm, double dOffs) { - PtrOwner pSrf( CreateSurfFlatRegion()) ; + PtrOwner pSrf( CreateBasicSurfFlatRegion()) ; if ( IsNull( pSrf)) return nullptr ; - - // per ogni sottotratto della guida calcolo la regione spazzata dalla sezione su quel tratto - for ( int i = 0 ; i < pGuide->GetCurveCount() ; i ++) { - PtrOwner pCrv( pGuide->GetCurve( i)->Clone()) ; - pCrv->SetExtrusion( vtNorm) ; - if ( pCrv->SimpleOffset( - dOffs)) { - PtrOwner pBorder( CreateCurveComposite()) ; - if ( IsNull( pBorder)) - return nullptr ; - pBorder->AddCurve( pGuide->GetCurve( i)->Clone()) ; - pCrv->Invert() ; - Point3d ptS ; pCrv->GetStartPoint( ptS) ; - pBorder->AddLine( ptS) ; - pBorder->AddCurve( Release( pCrv)) ; - pBorder->Close() ; - if ( ! pSrf->IsValid()) { - pSrf->AddExtLoop( Release( pBorder)) ; - } - else { - PtrOwner pSrfTmp( CreateSurfFlatRegion()) ; - if ( IsNull( pSrfTmp)) + PtrOwner pGuide( ConvertCurveToBasicComposite( pOrigGuide->Clone())) ; + if ( IsNull( pGuide)) + return nullptr ; + // per ogni sottotratto della guida calcolo la regione spazzata dal segmento su quel tratto + for ( int i = 0 ; i < pGuide->GetCurveCount() ; i ++) { + + // raccordo con curva precedente + if ( i > 0) { + Vector3d vtS ; pGuide->GetCurve( i-1)->GetEndDir( vtS) ; + Vector3d vtE ; pGuide->GetCurve( i)->GetStartDir( vtE) ; + + double dAng ; bool bDet ; + vtS.GetRotation( vtE, vtNorm, dAng, bDet) ; + if ( dAng * dOffs > EPS_SMALL) { + + Point3d ptC ; pGuide->GetCurve( i)->GetStartPoint( ptC) ; + vtS.Rotate( vtNorm, -90) ; + vtE.Rotate( vtNorm, -90) ; + + PtrOwner pBorder( CreateBasicCurveComposite()) ; + if ( IsNull( pBorder)) return nullptr ; - pSrfTmp->AddExtLoop( Release( pBorder)) ; - pSrf->Add( *pSrfTmp) ; + pBorder->AddPoint( ptC) ; + pBorder->AddLine( ptC + vtS * dOffs) ; + + PtrOwner pArc( CreateBasicCurveArc()) ; + if ( IsNull( pArc) || ! pArc->SetC2PN( ptC, ptC + vtS * dOffs, ptC + vtE * dOffs, vtNorm)) + pBorder->AddLine( ptC + vtE * dOffs) ; + else + pBorder->AddCurve( Release( pArc)) ; + pBorder->Close() ; + pBorder->Invert() ; + PtrOwner pSrfFillet( CreateBasicSurfFlatRegion()) ; + if ( IsNull( pSrfFillet)) + return nullptr ; + pSrfFillet->AddExtLoop( Release( pBorder)) ; + + if ( pSrfFillet->IsValid()) + pSrf->Add( *pSrfFillet) ; } } + + PtrOwner pCurr( pGuide->GetCurve( i)->Clone()) ; + pCurr->SetExtrusion( vtNorm) ; + PtrOwner pCrvOffs( pCurr->Clone()) ; + + PtrOwner pBorder( CreateBasicCurveComposite()) ; + if ( IsNull( pBorder)) + return nullptr ; + + if ( pCrvOffs->SimpleOffset( dOffs)) { + pBorder->AddCurve( Release( pCurr)) ; + pCrvOffs->Invert() ; + Point3d ptS ; pCrvOffs->GetStartPoint( ptS) ; + pBorder->AddLine( ptS) ; + pBorder->AddCurve( Release( pCrvOffs)) ; + pBorder->Close() ; + } else { - // se arco che collassa devo costruire separatamente i due spicchi - ICurveArc* pArc = GetCurveArc( pCrv) ; + // se arco che collassa costruisco il bordo del settore circolare + CurveArc* pArc = GetBasicCurveArc( pCurr) ; if ( pArc == nullptr) return nullptr ; - + Point3d ptC = pArc->GetCenter() ; Point3d ptS ; pArc->GetStartPoint( ptS) ; - Vector3d vtS = ptC - ptS ; - vtS.Normalize() ; - Point3d ptS2 = ptS + vtS * dOffs ; - - Point3d ptE ; pArc->GetEndPoint( ptE) ; - Vector3d vtE = ptC - ptE ; - vtE.Normalize() ; - Point3d ptE2 = ptE + vtE * dOffs ; - - PtrOwner pArc2( CreateCurveArc()) ; - if ( IsNull( pArc2) || ! pArc2->SetC2PN( ptC, ptS2, ptE2, vtNorm)) - return nullptr ; - - PtrOwner pBorder1( CreateCurveComposite()) ; - if ( IsNull( pBorder1)) - return nullptr ; - pBorder1->AddPoint( ptC) ; - pBorder1->AddLine( ptS) ; - pBorder1->AddCurve( Release( pCrv)) ; - pBorder1->Close() ; - - PtrOwner pBorder2( CreateCurveComposite()) ; - if ( IsNull( pBorder2)) - return nullptr ; - pBorder2->AddPoint( ptC) ; - pBorder2->AddLine( ptS2) ; - pBorder2->AddCurve( Release( pArc2)) ; - pBorder2->Close() ; - - PtrOwner pSrfTmp2( CreateSurfFlatRegion()) ; - if ( IsNull( pSrfTmp2)) - return nullptr ; - pSrfTmp2->AddExtLoop( Release( pBorder2)) ; - - if ( ! pSrf->IsValid()) { - pSrf->AddExtLoop( Release( pBorder1)) ; - pSrf->Add( *pSrfTmp2) ; - } - else { - PtrOwner pSrfTmp1( CreateSurfFlatRegion()) ; - if ( IsNull( pSrfTmp1)) - return nullptr ; - pSrfTmp1->AddExtLoop( Release( pBorder1)) ; - pSrf->Add( *pSrfTmp1) ; - pSrf->Add( *pSrfTmp2) ; - } + pBorder->AddPoint( ptC) ; + pBorder->AddLine( ptS) ; + pBorder->AddCurve( Release( pCurr)) ; + pBorder->Close() ; } + + PtrOwner pCurrSrf( CreateBasicSurfFlatRegion()) ; + if ( IsNull( pCurrSrf) || ! pCurrSrf->AddExtLoop( Release( pBorder))) + return nullptr ; + + if ( pSrf->IsValid()) { + if ( ! pSrf->Add( *pCurrSrf)) + return nullptr ; + } + else + pSrf.Set( pCurrSrf) ; } return Release( pSrf) ; } +//------------------------------------------------------------------------------- +static ICurveComposite* +CalcSegmentSweptRegionBorder( const ICurve* pGuide, const Vector3d& vtNorm, double dOffs) +{ + // costruisco le regioni spazzate a destra e sinistra + PtrOwner pSrfP( CalcSideSweptRegion( pGuide, vtNorm, dOffs)) ; + PtrOwner pSrfM( CalcSideSweptRegion( pGuide, vtNorm, - dOffs)) ; + + if ( IsNull( pSrfP) || IsNull( pSrfM)) + return nullptr ; + + pSrfP->Invert() ; + if ( ! pSrfP->Add( *pSrfM)) + return nullptr ; + + PtrOwner pBorder( ConvertCurveToBasicComposite( pSrfP->GetLoop( 0, 0))) ; + return Release( pBorder) ; +} + //------------------------------------------------------------------------------- static bool -AdjustSurfTriMeshRectSweptCaps( int nCapType, ICURVEPOVECTOR& vCrvs, const Vector3d& vtNorm, const ICurve* pOrigGuide, double dOffs) +AdjustSurfTriMeshRectSweptCaps( int nCapType, ICURVEPOVECTOR& vCrvs, const Vector3d& vtNorm, const ICurve* pGuide, double dOffs) { // se RSCAP_FLAT trasformo tutte le giunzioni relative agli estremi in linee controllando se necessario chiudere le curve - // con opportuni tratti di offset. + // con opportuni tratti della regione spazzata. // RSCAP_NONE viene gestito allo stesso modo perchè per la superficie top e bottom servono curve chiuse, le curve // di giunzione saranno rimosse successivamente per il calcolo della superficie laterale if ( nCapType == RSCAP_FLAT || nCapType == RSCAP_NONE) { @@ -516,52 +540,48 @@ AdjustSurfTriMeshRectSweptCaps( int nCapType, ICURVEPOVECTOR& vCrvs, const Vecto // spezzo le curve in corrispondenza delle junctions RemoveFatCurveJunctions( vCrvs) ; - // creo le linee di junction sugli estremi della giuda ( j = 0 start, j = 1 end) + // creo i flat caps sugli estremi della guida ( j = 0 start, j = 1 end) + Point3d ptGuideS ; pGuide->GetStartPoint( ptGuideS) ; + Vector3d vtGuideS ; pGuide->GetStartDir( vtGuideS) ; + vtGuideS.Rotate( vtNorm, ANG_RIGHT) ; + Point3d ptGuideE ; pGuide->GetEndPoint( ptGuideE) ; + Vector3d vtGuideE ; pGuide->GetEndDir( vtGuideE) ; + vtGuideE.Rotate( vtNorm, ANG_RIGHT) ; + for ( int j = 0 ; j < 2 ; j ++) { - Point3d ptP ; - Vector3d vtDir ; - if ( j == 0) { - pOrigGuide->GetStartPoint( ptP) ; - pOrigGuide->GetStartDir( vtDir) ; - } - else { - pOrigGuide->GetEndPoint( ptP) ; - pOrigGuide->GetEndDir( vtDir) ; - } - vtDir.Rotate( vtNorm, ANG_RIGHT) ; + Point3d ptP = ( j == 0 ? ptGuideS : ptGuideE) ; + Vector3d vtDir = ( j == 0 ? vtGuideS : vtGuideE) ; Point3d pt1 = ptP + dOffs * vtDir ; Point3d pt2 = ptP - dOffs * vtDir ; - // cerco su quali curve poggia la linea di junction + // cerco su quali curve poggia la linea del cap int nCrv1 = -1 ; bool bStart1 = true ; - int nCrv2 = -1 ; bool bStart2 = true ; + int nCrv2 = -1 ; for ( int i = 0 ; i < ssize( vCrvs) ; i ++) { Point3d ptS ; vCrvs[i]->GetStartPoint( ptS) ; Point3d ptE ; vCrvs[i]->GetEndPoint( ptE) ; - if ( AreSamePointEpsilon( ptS, pt1, 10 * EPS_SMALL)) { + if ( AreSamePointEpsilon( ptS, pt1, 50 * EPS_SMALL)) { nCrv1 = i ; bStart1 = true ; } - else if ( AreSamePointEpsilon( ptS, pt2, 10 * EPS_SMALL)) { + else if ( AreSamePointEpsilon( ptS, pt2, 50 * EPS_SMALL)) { nCrv2 = i ; - bStart2 = true ; } - if ( AreSamePointEpsilon( ptE, pt1, 10 * EPS_SMALL)) { + if ( AreSamePointEpsilon( ptE, pt1, 50 * EPS_SMALL)) { nCrv1 = i ; bStart1 = false ; } - else if ( AreSamePointEpsilon( ptE, pt2, 10 * EPS_SMALL)) { + else if ( AreSamePointEpsilon( ptE, pt2, 50 * EPS_SMALL)) { nCrv2 = i ; - bStart2 = false ; } } - int nFlatCapProp = ( j == 0 ? STM_RECTSWEPT_FLATCAP_START : STM_RECTSWEPT_FLATCAP_END) ; + int nFlatCapProp = ( j == 0 ? RS_FLATCAP_START : RS_FLATCAP_END) ; // se congiunge due curve le unisco if ( nCrv1 != -1 && nCrv2 != -1) { if ( nCrv1 == nCrv2) { // se è la stessa curva la chiudo semplicemente con una linea - ICurveComposite* pCompo = GetCurveComposite( vCrvs[nCrv1]) ; + CurveComposite* pCompo = GetBasicCurveComposite( vCrvs[nCrv1]) ; if ( pCompo == nullptr) return false ; pCompo->Close() ; @@ -570,7 +590,7 @@ AdjustSurfTriMeshRectSweptCaps( int nCapType, ICURVEPOVECTOR& vCrvs, const Vecto else { int nFirst = ( bStart1 ? nCrv2 : nCrv1) ; int nSecond = ( bStart1 ? nCrv1 : nCrv2) ; - ICurveComposite* pCompo = GetCurveComposite( vCrvs[nFirst]) ; + CurveComposite* pCompo = GetBasicCurveComposite( vCrvs[nFirst]) ; if ( pCompo == nullptr) return false ; Point3d ptRef ; vCrvs[nSecond]->GetStartPoint( ptRef) ; @@ -582,155 +602,87 @@ AdjustSurfTriMeshRectSweptCaps( int nCapType, ICURVEPOVECTOR& vCrvs, const Vecto vCrvs.pop_back() ; } } - - // se non congiunge due curve aggiungo il tratto lineare libero sull'estremo - else { - if ( nCrv1 != -1) { - ICurveComposite* pCompo = GetCurveComposite( vCrvs[nCrv1]) ; - if ( pCompo == nullptr) - return false ; - pCompo->AddLine( pt2, ! bStart1) ; - int nSubCrv = ( bStart1 ? 0 : pCompo->GetCurveCount() - 1) ; - pCompo->SetCurveTempProp( nSubCrv, nFlatCapProp) ; - } - else if ( nCrv2 != -1) { - ICurveComposite* pCompo = GetCurveComposite( vCrvs[nCrv2]) ; - if ( pCompo == nullptr) - return false ; - pCompo->AddLine( pt1, ! bStart2) ; - int nSubCrv = ( bStart2 ? 0 : pCompo->GetCurveCount() - 1) ; - pCompo->SetCurveTempProp( nSubCrv, nFlatCapProp) ; - } - } } - // controllo chiusura di tutte le curve + // recupero le curve aperte e cerco di chiuderle + INTVECTOR vOpenIdx ; for ( int i = 0 ; i < ssize( vCrvs) ; i++) { - if ( ! vCrvs[i]->IsClosed()) { + if ( ! vCrvs[i]->IsClosed()) + vOpenIdx.emplace_back( i) ; + } - ICurveComposite* pCompo = GetCurveComposite( vCrvs[i]) ; - if ( pCompo == nullptr) + if ( vOpenIdx.empty()) + return true ; + + // ricavo il bordo della regione spazzata da un segmento che scorre lungo la guida con il suo punto medio + PtrOwner pBorder( CalcSegmentSweptRegionBorder( pGuide, vtNorm, dOffs)) ; + if ( IsNull( pBorder)) + return false ; + + // ricavo le porzioni del bordo della regione spazzata che servono per collegare i tratti aperti + Point3d ptNewStart ; vCrvs[vOpenIdx[0]]->GetStartPoint( ptNewStart) ; + double dUStart = 0 ; pBorder->GetParamAtPoint( ptNewStart, dUStart) ; + pBorder->ChangeStartPoint( dUStart) ; + + INTDBLVECTOR vTrims ; + vTrims.reserve( 2 * vOpenIdx.size()) ; + vTrims.emplace_back( 0, 0.0) ; + for ( int i = 0 ; i < ssize( vOpenIdx) ; i ++) { + double dParS, dParE ; + if ( i > 0) { + Point3d ptS ; vCrvs[vOpenIdx[i]]->GetStartPoint( ptS) ; + if ( ! pBorder->GetParamAtPoint( ptS, dParS, 50 * EPS_SMALL)) return false ; - double dTempProp1 = pCompo->GetFirstCurve()->GetTempProp() ; - double dTempProp2 = pCompo->GetLastCurve()->GetTempProp() ; + vTrims.emplace_back( i, dParS) ; + } + Point3d ptE ; vCrvs[vOpenIdx[i]]->GetEndPoint( ptE) ; + if ( ! pBorder->GetParamAtPoint( ptE, dParE, 50 * EPS_SMALL)) + return false ; + vTrims.emplace_back( i, dParE) ; + } + double dS, dE ; pBorder->GetDomain( dS, dE) ; + vTrims.emplace_back( -1, dE) ; + sort( vTrims.begin(), vTrims.end(), [](const INTDBL& a, const INTDBL& b) { return a.second < b.second ;}) ; - // verifico se necessaria inversione della guida per renderla coerente con la fat curve - bool bInvert = false ; - if ( dTempProp1 < 0 || dTempProp2 < 0) { - bInvert = ( dTempProp1 == STM_RECTSWEPT_FLATCAP_END || dTempProp2 == STM_RECTSWEPT_FLATCAP_START) ; - } - else { - Point3d ptS ; pCompo->GetStartPoint( ptS) ; - DistPointCurve distPC( ptS, *pOrigGuide) ; - int nSide = 0 ; - distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; - bInvert = ( nSide == MDS_RIGHT) ; - } - - // ricavo la superficie spazzata dalla guida - PtrOwner pGuide( ConvertCurveToComposite( pOrigGuide->Clone())) ; - if ( bInvert) - pGuide->Invert() ; - PtrOwner pSurf( CalcSweptSurface( pGuide, vtNorm, dOffs)) ; - - // verifico come trimmare - bool bTrimStart = false ; - bool bTrimEnd = false ; - if ( dTempProp1 < 0 && dTempProp2 >= 0) - bTrimStart = true ; - else if ( dTempProp1 >= 0 && dTempProp2 < 0) - bTrimEnd = true ; - else if ( dTempProp1 < 0 && dTempProp2 < 0) { - bTrimStart = true ; - bTrimEnd = true ; - } - - // recupero il loop opportuno - PtrOwner pBorder( ConvertCurveToComposite( pSurf->GetLoop( 0, 0))) ; - if ( pSurf->GetChunkCount() > 1) { - Point3d ptRef ; pCompo->GetStartPoint( ptRef) ; - if ( bTrimStart) - pCompo->GetEndPoint( ptRef) ; - for ( int j = 0 ; j < pSurf->GetChunkCount() ; j ++) { - pBorder.Set( ConvertCurveToComposite( pSurf->GetLoop( j, 0))) ; - if ( pBorder->IsPointOn( ptRef)) - break ; - } - } - - double dPar1, dPar2 ; - if ( ! bTrimStart && ! bTrimEnd) { - Point3d ptS ; pCompo->GetStartPoint( ptS) ; - Point3d ptE ; pCompo->GetEndPoint( ptE) ; - pBorder->GetParamAtPoint( ptE, dPar1) ; - pBorder->GetParamAtPoint( ptS, dPar2) ; - } - else if ( bTrimStart && bTrimEnd) { - - const CurveLine* pLineS = GetBasicCurveLine( pCompo->GetFirstCurve()) ; - const CurveLine* pLineE = GetBasicCurveLine( pCompo->GetLastCurve()) ; - - if ( pLineS == nullptr || pLineE == nullptr) - return false ; - - IntersLineLine intLL( *pLineS, *pLineE) ; - if ( intLL.GetNumInters() > 0) { - IntCrvCrvInfo aInfo ; - intLL.GetIntCrvCrvInfo( aInfo) ; - pCompo->TrimStartEndAtParam( aInfo.IciA[0].dU, pCompo->GetCurveCount() - 1 + aInfo.IciB[0].dU) ; - continue ; - } - else { - // da fare in frame locale - IntersCurveCurve intCC( *pLineS, *pBorder) ; - IntCrvCrvInfo aInfo ; - intCC.GetIntCrvCrvInfo( intCC.GetIntersCount() - 1, aInfo) ; - dPar2 = aInfo.IciB[0].dU ; - pCompo->TrimStartAtParam( aInfo.IciA[0].dU) ; - - IntersCurveCurve intCC2( *pLineE, *pBorder) ; - intCC2.GetIntCrvCrvInfo( 0, aInfo) ; - dPar1 = aInfo.IciB[0].dU ; - - pCompo->TrimEndAtParam( pCompo->GetCurveCount() - 1 + aInfo.IciA[0].dU) ; - } - } - else if ( bTrimStart) { - Point3d ptE ; pCompo->GetEndPoint( ptE) ; - pBorder->GetParamAtPoint( ptE, dPar1) ; - - // intersezione con lo start - const ICurve* pLine = pCompo->GetFirstCurve() ; - IntersCurveCurve intCC( *pLine, *pBorder) ; - IntCrvCrvInfo aInfo ; - intCC.GetIntCrvCrvInfo( intCC.GetIntersCount() - 1, aInfo) ; - dPar2 = aInfo.IciB[0].dU ; - - pCompo->TrimStartAtParam( aInfo.IciA[0].dU) ; - } - else { - Point3d ptS ; pCompo->GetStartPoint( ptS) ; - pBorder->GetParamAtPoint( ptS, dPar2) ; - - // intersezione con end - const ICurve* pLine = pCompo->GetLastCurve() ; - IntersCurveCurve intCC( *pLine, *pBorder) ; - IntCrvCrvInfo aInfo ; - intCC.GetIntCrvCrvInfo( 0, aInfo) ; - dPar1 = aInfo.IciB[0].dU ; - - pCompo->TrimEndAtParam( pCompo->GetCurveCount() - 1 + aInfo.IciA[0].dU) ; - } - - pBorder->TrimStartEndAtParam( dPar1, dPar2) ; - // setto temp param per identificarli - for ( int j = 0 ; j < pBorder->GetCurveCount() ; j++) { - pBorder->SetCurveTempProp( j, STM_RECTSWEPT_FLATCAP_EXTRA) ; - } + // costruisco la curva chiusa risultante + CurveComposite* pResult = GetBasicCurveComposite( vCrvs[vOpenIdx[0]]) ; + if ( pResult == nullptr) + return false ; + for ( int i = 1 ; i < ssize( vTrims) ; i = i + 2) { + // ricavo la porzione di bordo + PtrOwner pBorderPart( ConvertCurveToBasicComposite( pBorder->CopyParamRange( vTrims[i].second, vTrims[i+1].second))) ; + if ( IsNull( pBorderPart)) + return false ; + // identifico se ha tratti corrispondenti al flat cap + for ( int j = 0 ; j < pBorderPart->GetCurveCount() ; j ++) { + pBorderPart->SetCurveTempProp( j, RS_FLATCAP_EXTRA) ; - pCompo->AddCurve( Release( pBorder)) ; + if ( pBorderPart->GetCurve( j)->GetType() == CRV_LINE) { + Vector3d vtS ; pBorderPart->GetCurve( j)->GetStartDir( vtS) ; + if ( AreSameOrOppositeVectorApprox( vtS, vtGuideS)) { + Point3d ptRef ; pBorderPart->GetCurve( j)->GetStartPoint( ptRef) ; + Vector3d vtDist = ptRef - ptGuideS ; + Vector3d vtOrtho = vtDist - vtDist * vtGuideS * vtGuideS ; + if ( vtDist * vtGuideS < dOffs + EPS_SMALL && vtOrtho.SqLen() < 100 * SQ_EPS_SMALL) + pBorderPart->SetCurveTempProp( j, RS_FLATCAP_START) ; + } + if ( AreSameOrOppositeVectorApprox( vtS, vtGuideE)) { + Point3d ptRef ; pBorderPart->GetCurve( j)->GetStartPoint( ptRef) ; + Vector3d vtDist = ptRef - ptGuideE ; + Vector3d vtOrtho = vtDist - vtDist * vtGuideE * vtGuideE ; + if ( vtDist * vtGuideE < dOffs + EPS_SMALL && vtOrtho.SqLen() < 100 * SQ_EPS_SMALL) + pBorderPart->SetCurveTempProp( j, RS_FLATCAP_END) ; + } + } } + // aggiungo la curva aperta successiva e la elimino dal vettore delle curve + int nNextIdx = vTrims[i+1].first ; + if ( nNextIdx != -1) { + pResult->AddCurve( Release( vCrvs[vOpenIdx[nNextIdx]])) ; + swap( vCrvs[vOpenIdx[nNextIdx]], vCrvs.back()) ; + vCrvs.pop_back() ; + } } } @@ -856,69 +808,126 @@ GetSurfTriMeshSharpRectSwept( double dDimH, double dDimV, const ICurve* pGuide, //------------------------------------------------------------------------------- static ICurveComposite* -Connect( const ICurve* pGuide, ICURVEPOVECTOR& vCrvs, const INTVECTOR& vIdx, double dRadius, double dBevelH, double dBevelV, - const Vector3d& vtNorm, int nCapType) +Connect( const ICurve* pGuide, ICURVEPOVECTOR& vCrvs, const INTVECTOR& vIdx, double dRadius, double dBevelH, + double dBevelV, const Vector3d& vtNorm, int nCapType) { - double dChainTol = 50 * EPS_SMALL ; double dOffsMin = 0.5 * dRadius - dBevelH ; double dOffsMax = 0.5 * dRadius ; - // calcolo il medial axis e conservo solo le porzioni interne alla regione tra i due offset ICURVEPOVECTOR vMedialAxis ; - CalcCurveMedialAxis( *pGuide, vMedialAxis, 0) ; - ICRVCOMPOPOVECTOR vMedialCrvs ; + CalcCurveMedialAxis( *pGuide, vMedialAxis, WMAT_BOTHSIDES) ; + + // ricavo i punti di collasso nella regione tra i due offset per identificare se sono necessari dei bisettori + // per ricreare al meglio la curva di autointersezione della superficie laterale del solido di swept + PNTVECTOR vPtRef ; for ( int i = 0 ; i < ssize( vMedialAxis) ; i ++) { + + for ( int j = 0 ; j < 2 ; j ++) { // j = 0 start, j = 1 end + + double dPar = vMedialAxis[i]->GetTempParam( j) ; + if ( dOffsMin + EPS_SMALL < dPar && dPar < dOffsMax + EPS_SMALL) { + Point3d ptP ; + if ( j == 0) + vMedialAxis[i]->GetStartPoint( ptP) ; + else + vMedialAxis[i]->GetEndPoint( ptP) ; + + // traslazione per quota finale + double dDelta = - dBevelV / dBevelH * ( dPar - dOffsMin) ; + ptP.Translate( dDelta * vtNorm) ; + + // verifico se è già stato individuato + bool bFound = false ; + for ( int k = 0 ; k < ssize( vPtRef) ; k ++) { + if ( AreSamePointApprox( vPtRef[k], ptP)) { + bFound = true ; + break ; + } + } + if ( bFound) + continue ; + + // traslo sul piano delle curve per trovare la distanza + ptP.Translate( - ( dDelta + dBevelV) * vtNorm) ; + DistPointCurve distPC( ptP, *vCrvs[0]) ; + double dDist ; distPC.GetDist( dDist) ; + if ( dDist > dBevelH + EPS_SMALL) { + ptP.Translate( ( dBevelV + dDelta) * vtNorm) ; + vPtRef.emplace_back( ptP) ; + } + } + } + } + + // se non serve collegamento perchè singola curva e non servono bisettori extra, non modifico la curva passata + if ( ssize( vIdx) == 1 && vPtRef.empty()) + return nullptr ; + + // conservo solo i tratti di medial axis compresi tra i due offset ( dOffsMin < <= dOffsMax) + ICRVCOMPOPOVECTOR vMedialCrvs ; + + Voronoi* pVoronoi = GetCurveVoronoi( *pGuide) ; + if ( pVoronoi == nullptr) + return nullptr ; + PNTVECTOR vFlatCapsPtRef ; + double dTmp, dLast ; + pGuide->GetDomain( dTmp, dLast) ; + + for ( int i = 0 ; i < ssize( vMedialAxis) ; i ++) { + double dPar1 = vMedialAxis[i]->GetTempParam( 0) ; double dPar2 = vMedialAxis[i]->GetTempParam( 1) ; - + // se non comprende l'offset non va considerato - if ( dPar1 > dOffsMax - EPS_SMALL || dPar2 < dOffsMin + EPS_SMALL) + if ( dPar1 > dOffsMax + EPS_ZERO || dPar2 < dOffsMin + EPS_ZERO) + continue ; + if ( abs( dPar1 - dOffsMax) < EPS_ZERO && abs( dPar2 - dOffsMax) > EPS_ZERO) continue ; - if ( dOffsMin - EPS_SMALL < dPar1 && dPar2 < dOffsMax + EPS_SMALL) { + // se flat caps ignoro i bisettori che hanno come sito un estremo della giuda + if ( nCapType == RSCAP_FLAT) { + + int nBisector = vMedialAxis[i]->GetTempProp() ; + int nCrv1, nSub1, nCrv2, nSub2, nTmp1, nTmp2 ; + pVoronoi->GetBisectorSites( nBisector, nCrv1, nTmp1, nSub1, nCrv2, nTmp2, nSub2) ; + + if ( nSub1 == 0 || nSub2 == 0 || nSub1 == dLast || nSub2 == dLast) { + // salvo i suoi estremi + Point3d ptS ; vMedialAxis[i]->GetStartPoint( ptS) ; + Point3d ptE ; vMedialAxis[i]->GetEndPoint( ptE) ; + double dDeltaS = - dBevelV / dBevelH * ( dPar1 - dOffsMin) ; + double dDeltaE = - dBevelV / dBevelH * ( dPar2 - dOffsMin) ; + vFlatCapsPtRef.emplace_back( ptS + dDeltaS * vtNorm) ; + vFlatCapsPtRef.emplace_back( ptE + dDeltaE * vtNorm) ; + continue ; + } + } + + + if ( dOffsMin + EPS_ZERO < dPar1 && dPar2 < dOffsMax + EPS_ZERO) { // tratto va conservato completamente vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ; } else { - // determino la parte compresa tra gli offset massimo e minimo - if ( vMedialAxis[i]->GetType() == CRV_LINE) { - double dTrimParS = ( dOffsMin - dPar1) / ( dPar2 - dPar1) ; - double dTrimParE = ( dOffsMax - dPar1) / ( dPar2 - dPar1) ; - dTrimParS = clamp( dTrimParS, 0.0, 1.0) ; - dTrimParE = clamp( dTrimParE, 0.0, 1.0) ; - vMedialAxis[i]->TrimStartEndAtParam( dTrimParS, dTrimParE) ; - if ( dTrimParS > EPS_SMALL) - vMedialAxis[i]->SetTempParam( dOffsMin, 0) ; - if ( dTrimParE < 1 - EPS_SMALL) - vMedialAxis[i]->SetTempParam( dOffsMax, 1) ; - vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ; - } - else { - ICurveComposite* pCompo = GetCurveComposite( vMedialAxis[i]) ; - if ( pCompo == nullptr) + // determino solo la parte compresa dall'offset. Per maggior precisione calcolo il punto di trim esatto con Voronoi + // senza affidarmi ai parametri salvati nel bisettore approssimato + int nBisector = vMedialAxis[i]->GetTempProp() ; + Point3d ptP ; + if ( pVoronoi->GetBisectorPointAtParam( nBisector, dOffsMin, ptP)) + continue ; + double dTmp, dTrimParE ; + vMedialAxis[i]->GetDomain( dTmp, dTrimParE) ; + if ( pVoronoi->GetBisectorPointAtParam( nBisector, dOffsMax, ptP)) + if ( ! vMedialAxis[i]->GetParamAtPoint( ptP, dTrimParE, 100 * EPS_SMALL)) return nullptr ; - double dS, dE ; pCompo->GetDomain( dS, dE) ; - double dTrimParS = dS ; - double dTrimParE = dE ; - for ( int j = 0 ; j < pCompo->GetCurveCount() ; j ++) { - double dPar1 = pCompo->GetCurve( j)->GetTempParam( 0) ; - double dPar2 = pCompo->GetCurve( j)->GetTempParam( 1) ; - if ( dPar1 - EPS_SMALL < dOffsMin && dOffsMin < dPar2 + EPS_SMALL) - dTrimParS = j + ( dOffsMin - dPar1) / ( dPar2 - dPar1) ; - if ( dPar1 - EPS_SMALL < dOffsMax && dOffsMax < dPar2 + EPS_SMALL) { - dTrimParE = j + ( dOffsMax - dPar1) / ( dPar2 - dPar1) ; - break ; - } - } - dTrimParS = clamp( dTrimParS, 0.0, dE) ; - dTrimParE = clamp( dTrimParE, 0.0, dE) ; - pCompo->TrimStartEndAtParam( dTrimParS, dTrimParE) ; - if ( dTrimParS > EPS_SMALL) - pCompo->SetCurveTempParam( 0, dOffsMin, 0) ; - if ( dTrimParE < dE - EPS_SMALL) - pCompo->SetCurveTempParam( pCompo->GetCurveCount() - 1, dOffsMax, 1) ; - vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ; - } + + if ( dTrimParE < EPS_SMALL) + continue ; // la curva si cancella completamente + vMedialAxis[i]->TrimEndAtParam( dTrimParE) ; + + // aggiusto i temp param + vMedialAxis[i]->SetTempParam( dOffsMax, 1) ; + vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ; } // aggiusto la quota @@ -942,108 +951,53 @@ Connect( const ICurve* pGuide, ICURVEPOVECTOR& vCrvs, const INTVECTOR& vIdx, dou vMedialCrvs.back()->ModifyJoint( j, ptJ + vtNorm * dDelta) ; } Point3d ptE ; vMedialCrvs.back()->GetEndPoint( ptE) ; - double dPar = vMedialCrvs.back()->GetLastCurve()->GetTempParam( 1) ; + double dPar = vMedialCrvs.back()->GetTempParam( 1) ; double dDelta = - dBevelV / dBevelH * ( dPar - dOffsMin) ; vMedialCrvs.back()->ModifyEnd( ptE + vtNorm * dDelta) ; } } - + // chain dei medialaxis - GetChainedCurves( vMedialCrvs, 5 * EPS_SMALL, true) ; + if ( ! GetChainedCurves( vMedialCrvs, EPS_SMALL, true, false)) + return nullptr ; - // se cap flat aggiungo tratti sugli estremi + // nel caso di flat caps i bisettori legati agli estremi devono essere collegati manualmente al bordo esterno if ( nCapType == RSCAP_FLAT) { - - Frame3d frLoc ; frLoc.Set( ORIG, vtNorm) ; + for ( int i = 0 ; i < ssize( vMedialCrvs) ; i ++) { + bool bConnect = false ; - for ( int i = 0 ; i < 2 ; i ++) { - // recupero il tratto start/end - int nRefProp = ( i == 0 ? STM_RECTSWEPT_FLATCAP_START : STM_RECTSWEPT_FLATCAP_END) ; - bool bFound = false ; - for ( int j = 0 ; j < ssize( vCrvs) && ! bFound ; j ++) { - ICurveComposite* pCompo = GetCurveComposite( vCrvs[j]) ; - if ( pCompo == nullptr) - return nullptr ; - for ( int k = 0 ; k < pCompo->GetCurveCount() ; k++) { - int nProp = pCompo->GetCurve( k)->GetTempProp() ; - if ( nProp == nRefProp) { - double dLen ; pCompo->GetCurve( k)->GetLength( dLen) ; - if ( abs( dLen - 2 * dOffsMax) > EPS_SMALL) { - Point3d ptP ; pCompo->GetCurve( k)->GetStartPoint( ptP) ; - Vector3d vtDir ; pCompo->GetCurve( k)->GetStartDir( vtDir) ; - if ( i == 0) - vtDir.Invert() ; - PtrOwner pLine( CreateCurveLine()) ; - if ( IsNull( pLine)) - return nullptr ; - pLine->SetPVL( ptP, vtDir, 2 * dOffsMax - dLen) ; - pLine->ToLoc( frLoc) ; + for ( int k = 0 ; k < 2 && ! bConnect ; k ++) { + Point3d ptP ; + if ( k == 0) + vMedialCrvs[i]->GetStartPoint( ptP) ; + else + vMedialCrvs[i]->GetEndPoint( ptP) ; - // verifico se interseca una curva dei medialaxis - for ( int l = 0 ; l < ssize( vMedialCrvs) ; l++) { - vMedialCrvs[l]->ToLoc( frLoc) ; - IntersCurveCurve intCC( *pLine, *vMedialCrvs[l]) ; - vMedialCrvs[l]->ToGlob( frLoc) ; - if ( intCC.GetIntersCount() == 1) { - IntCrvCrvInfo aInfo ; - intCC.GetIntCrvCrvInfo( 0, aInfo) ; - double dTempParam1 = vMedialCrvs[l]->GetFirstCurve()->GetTempParam() ; - double dTempParam2 = vMedialCrvs[l]->GetLastCurve()->GetTempParam() ; - if ( dTempParam1 < dTempParam2) { - vMedialCrvs[l]->TrimStartAtParam( aInfo.IciB[0].dU) ; - vMedialCrvs[l]->AddLine( ptP, false) ; - vMedialCrvs[l]->SetCurveTempParam( 0, dOffsMax, 0) ; - } - else { - vMedialCrvs[l]->TrimEndAtParam( aInfo.IciB[0].dU) ; - vMedialCrvs[l]->AddLine( ptP) ; - vMedialCrvs[l]->SetCurveTempParam( vMedialCrvs[l]->GetCurveCount() - 1, dOffsMax, 1) ; - } - break ; - } - } - } - bFound = true ; + for ( int j = 0 ; j < ssize( vFlatCapsPtRef) ; j ++) { + if ( AreSamePointApprox( ptP, vFlatCapsPtRef[j])) { + CurveComposite* pCompo = GetBasicCurveComposite( vMedialCrvs[i]) ; + if ( pCompo == nullptr) + return nullptr ; + DistPointCurve distPC( ptP, *vCrvs[0]) ; + Point3d ptMinDist ; int nFlag ; + distPC.GetMinDistPoint( 0, ptMinDist, nFlag) ; + pCompo->AddLine( ptMinDist, k == 1) ; + bConnect = true ; break ; } } - } } } // creo catena - ICurveComposite* pResult = nullptr ; - BOOLVECTOR vUsed1( vIdx.size(), false) ; - + double dChainTol = 50 * EPS_SMALL ; + CurveComposite* pResult = nullptr ; for ( int i = 0 ; i < ssize( vMedialCrvs) ; i++) { - // verifico se è una curva sensata per il collegamento ( ovvero se i parametri ai suoi estremi sono il valore di max offset) - bool bValid = false ; - if ( vMedialCrvs[i]->GetCurveCount() == 1) { - double dPar1 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam() ; - double dPar2 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam( 1) ; - bValid = ( abs( dPar1 - dOffsMax) < EPS_SMALL && abs( dPar2 - dOffsMax) < EPS_SMALL) ; - } - else { - // controllo sia parametro start ed end perchè il chain permette inversione - double dParS1 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam() ; - double dParS2 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam( 1) ; - double dParE1 = vMedialCrvs[i]->GetLastCurve()->GetTempParam( ) ; - double dParE2 = vMedialCrvs[i]->GetLastCurve()->GetTempParam( 1) ; - bValid = ( ( abs( dParS1 - dOffsMax) < EPS_SMALL || abs( dParS2 - dOffsMax) < EPS_SMALL) && - ( abs( dParE1 - dOffsMax) < EPS_SMALL || abs( dParE2 - dOffsMax) < EPS_SMALL)) ; - } - - if ( ! bValid) - continue ; - // setto tutte le temp prop ad un valore fissato per identificarla in seguito - for ( int nC = 0 ; nC < vMedialCrvs[i]->GetCurveCount() ; nC ++) - vMedialCrvs[i]->SetCurveTempProp( nC, STM_RECTSWEPT_LINK) ; - // trovo le curve che collega - Point3d ptS ; vMedialCrvs[i]->GetStartPoint( ptS) ; - Point3d ptE ; vMedialCrvs[i]->GetEndPoint( ptE) ; + Point3d ptS = P_INVALID ; vMedialCrvs[i]->GetStartPoint( ptS) ; + Point3d ptE = P_INVALID ; vMedialCrvs[i]->GetEndPoint( ptE) ; double dParS = -1, dParE = -1 ; int nS = -1, nE = -1 ; for ( int j = 0 ; j < ssize( vIdx) ; j ++) { @@ -1056,91 +1010,151 @@ Connect( const ICurve* pGuide, ICURVEPOVECTOR& vCrvs, const INTVECTOR& vIdx, dou if ( nS != -1 && nE != -1) break ; } - - if ( nS == -1 || nE == -1) - return nullptr ; - // i parametri devono essere su un joint per costruzione dParS = round( dParS) ; dParE = round( dParE) ; - pResult = GetCurveComposite( vCrvs[nS]) ; + // se non arriva su una curva va scartato + if ( nS == -1 && nE == -1) + continue ; + // se arriva su una sola curva, verifico che non si innesti su un altro bisettore e che sia un tratto necessario + // per la gestione di parti collassate controllando i ptRef + if ( nS == -1 || nE == -1) { + CurveComposite* pCompo = GetBasicCurveComposite( vCrvs[max(nS, nE)]) ; + if ( pCompo == nullptr) + return nullptr ; + double dRefPar = min( max( dParS, dParE), pCompo->GetCurveCount() - 1.0) ; + int nCrv = int( dRefPar + 0.5) ; + int nRefProp = pCompo->GetCurve( nCrv)->GetTempProp() ; + if ( nRefProp == RS_LINK || nRefProp == RS_LINK_BACK) + continue ; + bool bValid = false ; + for ( int j = 0 ; j < ssize( vPtRef) && ! bValid ; j ++) { + if ( vPtRef[j].IsValid()) + bValid = vMedialCrvs[i]->IsPointOn( vPtRef[j]) ; + } + if ( ! bValid) + continue ; + // sistemo i parametri in modo che sia sempre definita la curva per lo start + if ( nS == -1) { + nS = nE ; + dParS = dParE ; + nE = -1 ; + dParE = -1 ; + vMedialCrvs[i]->Invert() ; + } + } + + // setto tutte le temp prop ad un valore fissato per poter identificare in seguito il tratto di bisettore + for ( int nC = 0 ; nC < vMedialCrvs[i]->GetCurveCount() ; nC ++) + vMedialCrvs[i]->SetCurveTempProp( nC, RS_LINK) ; + + pResult = GetBasicCurveComposite( vCrvs[nS]) ; ICurve* pResultEnd = nullptr ; if ( dParS > EPS_SMALL) { pResultEnd = pResult->CopyParamRange( dParS, pResult->GetCurveCount()) ; pResult->TrimEndAtParam( dParS) ; } pResult->AddCurve( vMedialCrvs[i]->Clone(), true, dChainTol) ; - GetBasicCurveComposite( vCrvs[nE])->ChangeStartPoint( dParE) ; - pResult->AddCurve( Release( vCrvs[nE]), true, dChainTol) ; + if ( nE != -1) { + GetBasicCurveComposite( vCrvs[nE])->ChangeStartPoint( dParE) ; + pResult->AddCurve( Release( vCrvs[nE]), true, dChainTol) ; + } vMedialCrvs[i]->Invert() ; + for ( int nC = 0 ; nC < vMedialCrvs[i]->GetCurveCount() ; nC ++) + vMedialCrvs[i]->SetCurveTempProp( nC, RS_LINK_BACK) ; pResult->AddCurve( Release( vMedialCrvs[i]), true, dChainTol) ; if ( pResultEnd != nullptr) pResult->AddCurve( pResultEnd, true, dChainTol) ; // forzo chiusura pResult->Close() ; } - return pResult ; + + // verifico di aver collegato tutte le curve + INTVECTOR vResidualIdx ; + for ( int i = 0 ; i < ssize( vIdx) ; i ++) { + if ( !IsNull( vCrvs[vIdx[i]])) + vResidualIdx.emplace_back( vIdx[i]) ; + } + if ( ssize( vResidualIdx) > 1) + return nullptr ; + + // sistemo il punto di inizio della curva risultante su una curva derivante dalla guida e non su un tratto di bisettore + if ( pResult != nullptr) { + for ( int i = 0 ; i < pResult->GetCurveCount() ; i ++) { + if ( pResult->GetCurve(i)->GetTempProp() > EPS_SMALL) { + pResult->ChangeStartPoint( i + 0.5) ; + break ; + } + } + } + + return pResult ; } //------------------------------------------------------------------------------- static bool -GetTrimParams( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, int nOrigCrv, int nCurrCrv2, const Vector3d& vtNorm, - DBLVECTOR& vPar1, DBLVECTOR& vPar2) +GetSplitParams( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, int nCrv2, bool bStart, const Vector3d& vtNorm, + DBLVECTOR& vPar1, DBLVECTOR& vPar2) { - int nFlag ; + int nFlag ; double dParMinDist ; + Point3d ptP ; + int nTmpProp = pCrv2->GetCurve( nCrv2)->GetTempProp() ; + int nStart = int( floor( vPar1.back()) + 0.5) ; - Point3d ptP ; pCrv2->GetCurve( nCurrCrv2)->GetStartPoint( ptP) ; - int nStart = int( floor( vPar1.back() + 0.5)) ; + // se la curva di riferimento è un flat cap lo split è definito dal cap stesso + if ( nTmpProp == RS_FLATCAP_START || nTmpProp == RS_FLATCAP_END) + return true ; + + // se split per bisettore che torna su se stesso considero uno dei punti a min dist sull'altra curva + else if ( nTmpProp == RS_LINK || nTmpProp == RS_LINK_BACK) { + pCrv2->GetCurve( nCrv2)->GetEndPoint( ptP) ; + DistPointCurve distPC( ptP, *pCrv1) ; + distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ; + vPar1.emplace_back( dParMinDist) ; + vPar2.emplace_back( nCrv2 + 1) ; + } + + // se deriva da una curva ben definita della giuda cerco la corrispondente dal lato corretto sull'altra curva + else if ( nTmpProp != 0) { + if ( bStart) + pCrv2->GetCurve( nCrv2)->GetEndPoint( ptP) ; + else + pCrv2->GetCurve( nCrv2)->GetStartPoint( ptP) ; - // se deriva da una curva ben definita della giuda cerco la corrispondente dal lato corretto sull'altra curva - if ( nOrigCrv != 0) { for ( int j = nStart ; j < pCrv1->GetCurveCount() ; j ++) { - int nOrigCrv1 = pCrv1->GetCurve( j)->GetTempProp() ; - if ( nOrigCrv1 == nOrigCrv) { - // se estremo di flat cap spezzo il bisettore appena trovato nel punto a minima distanza - // e sulla curva 1 avrò corrispondenza con un punto - if ( nOrigCrv == STM_RECTSWEPT_FLATCAP_START || nOrigCrv == STM_RECTSWEPT_FLATCAP_END) { - Point3d ptRef ; pCrv1->GetCurve( j)->GetStartPoint( ptRef) ; - PtrOwner pCurrBisector( pCrv2->CopyParamRange( vPar2.back(), nCurrCrv2)) ; - DistPointCurve distPC( ptRef, *pCurrBisector) ; - double dParTrim ; distPC.GetParamAtMinDistPoint( 0, dParTrim, nFlag) ; - vPar1.emplace_back( j) ; - vPar2.emplace_back( vPar2.back() + dParTrim) ; - vPar1.emplace_back( j) ; - vPar2.emplace_back( nCurrCrv2) ; - } - else { - DistPointCurve distPC( ptP, *pCrv1->GetCurve( j)) ; - int nSide = 0 ; - double dParMinDist ; - distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; - if ( nSide == MDS_RIGHT) { - distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ; - dParMinDist += j ; - vPar1.emplace_back( dParMinDist) ; - vPar2.emplace_back( nCurrCrv2) ; - break ; - } + int nTmpProp1 = pCrv1->GetCurve( j)->GetTempProp() ; + if ( nTmpProp1 == nTmpProp) { + DistPointCurve distPC( ptP, *pCrv1->GetCurve( j)) ; + int nSide = 0 ; + distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; + if ( nSide == MDS_RIGHT) { + distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ; + vPar1.emplace_back( dParMinDist + j) ; + if ( bStart) + vPar2.emplace_back( nCrv2 + 1) ; + else + vPar2.emplace_back( nCrv2) ; + break ; } } } } // se deriva da raccordo devo controllare il più vicino - else { + else { + pCrv2->GetCurve( nCrv2)->GetMidPoint( ptP) ; double dMinDist = INFINITO ; - double dParMinDist ; for ( int j = nStart ; j < pCrv1->GetCurveCount() ; j ++) { - int nOrigCrv1 = pCrv1->GetCurve( j)->GetTempProp() ; - if ( nOrigCrv1 == nOrigCrv) { + int nTmpProp1 = pCrv1->GetCurve( j)->GetTempProp() ; + if ( nTmpProp1 == nTmpProp) { DistPointCurve distPC( ptP, *pCrv1->GetCurve( j)) ; int nSide = 0 ; distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; if ( nSide == MDS_RIGHT) { double dCurrDist ; distPC.GetDist( dCurrDist) ; - if ( dCurrDist < dMinDist) { + if ( dCurrDist < dMinDist - EPS_SMALL) { distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ; dParMinDist += j ; dMinDist = dCurrDist ; @@ -1149,7 +1163,7 @@ GetTrimParams( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, int n } } vPar1.emplace_back( dParMinDist) ; - vPar2.emplace_back( nCurrCrv2) ; + vPar2.emplace_back( nCrv2 + 0.5) ; } return true ; @@ -1157,71 +1171,81 @@ GetTrimParams( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, int n //------------------------------------------------------------------------------- static bool -Associate( const ICurveComposite* pCrv1, ICurveComposite* pCrv2, const Vector3d& vtNorm, DBLVECTOR& vPar1, DBLVECTOR& vPar2) +Associate( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, const Vector3d& vtNorm, DBLVECTOR& vPar1, DBLVECTOR& vPar2) { vPar1.emplace_back( 0) ; vPar2.emplace_back( 0) ; - // assegno il punto di inizio della curva 2 il più vicino possibile a quello della curva 1 preferendo curve non problematiche - int nStartCrvId = pCrv1->GetFirstCurve()->GetTempProp() ; - if ( nStartCrvId == STM_RECTSWEPT_FLATCAP_START) { - for ( int i = 0 ; i < pCrv2->GetCurveCount() ; i ++) { - int nOrigCrv = pCrv2->GetCurve( i)->GetTempProp() ; - if ( nOrigCrv == nStartCrvId) { - pCrv2->ChangeStartPoint( i) ; - break ; - } - } - } - else { - Point3d ptStart ; pCrv1->GetStartPoint( ptStart) ; - double dPar ; int nFlag ; - DistPointCurve( ptStart, *pCrv2).GetParamAtMinDistPoint(0, dPar, nFlag) ; - pCrv2->ChangeStartPoint( dPar) ; - } + // segnalo parametro di split per le curve : + // - in corrispondenza dei flat caps + // - quando trovo l'inizio o la fine di un bisettore + // - dove il bisettore ritorna su se stesso - // segnalo parametro di split per le curve quando trovo l'inizio o la fine di un tratto che deriva da un bisettore - // o da un tratto aggiunto per la chiusura dei flat caps bool bBisector = false ; for ( int i = 0 ; i < pCrv2->GetCurveCount() ; i++) { - int nTmpProp = pCrv2->GetCurve( i)->GetTempProp() ; - if ( nTmpProp == STM_RECTSWEPT_LINK || nTmpProp == STM_RECTSWEPT_FLATCAP_EXTRA) { - if ( ! bBisector) { - // inizio bisettore - bBisector = true ; - if ( i > 0) { - int nOrigCrv = pCrv2->GetCurve( i-1)->GetTempProp() ; - GetTrimParams( pCrv1, pCrv2, nOrigCrv, i, vtNorm, vPar1, vPar2) ; + int nTmpProp = pCrv2->GetCurve( i)->GetTempProp() ; + + // se flat cap lo associo con il corrispondente + if ( nTmpProp == RS_FLATCAP_START || nTmpProp == RS_FLATCAP_END) { + for ( int j = 0 ; j < pCrv1->GetCurveCount() ; j ++) { + int nOrigCrv1 = pCrv1->GetCurve( j)->GetTempProp() ; + if ( nOrigCrv1 == nTmpProp) { + vPar1.emplace_back( j) ; + vPar1.emplace_back( j + 1) ; + vPar2.emplace_back( i) ; + vPar2.emplace_back( i + 1) ; + break ; } } } + + // se bisettore o tratto aggiunto per chiudere flat cap + else if ( nTmpProp == RS_LINK || nTmpProp == RS_LINK_BACK || nTmpProp == RS_FLATCAP_EXTRA) { + if ( ! bBisector) { + bBisector = true ; // inizio bisettore + if ( i > 0) + GetSplitParams( pCrv1, pCrv2, i-1, true, vtNorm, vPar1, vPar2) ; + } + else { + // verfico se il bisettore sta tornando su se stesso ( ovvero si passa da link a link_back o viceversa) + int nTmpPropPrev = pCrv2->GetCurve( i-1)->GetTempProp() ; + if ( nTmpPropPrev != nTmpProp && ( nTmpPropPrev == RS_LINK || nTmpPropPrev == RS_LINK_BACK) && + ( nTmpProp == RS_LINK || nTmpProp == RS_LINK_BACK)) + GetSplitParams( pCrv1, pCrv2, i-1, true, vtNorm, vPar1, vPar2) ; + } + } + + // se fine del bisettore else { if ( bBisector) { - // fine del bisettore bBisector = false ; - int nOrigCrv = pCrv2->GetCurve(i)->GetTempProp() ; - GetTrimParams( pCrv1, pCrv2, nOrigCrv, i, vtNorm, vPar1, vPar2) ; + GetSplitParams( pCrv1, pCrv2, i, false, vtNorm, vPar1, vPar2) ; } } } - // aggiungo parametro finale + // parametro finale double dTmp, dE1, dE2 ; pCrv1->GetDomain( dTmp, dE1) ; pCrv2->GetDomain( dTmp, dE2) ; - vPar1.emplace_back( dE1) ; vPar2.emplace_back( dE2) ; - + + // verifico che i parametri calcolati siano ordinati + for ( int i = 1 ; i < ssize( vPar1) ; i ++) + if ( vPar1[i] < vPar1[i-1] - EPS_SMALL) + return false ; + return true ; } //------------------------------------------------------------------------------- static POLYLINEVECTOR -TrimPolyLine( const PolyLine& PLOrig, const DBLVECTOR& vPar) +SplitPolyLine( const PolyLine& PLOrig, const DBLVECTOR& vPar) { - POLYLINEVECTOR vPL ; vPL.reserve( vPar.size()) ; + POLYLINEVECTOR vPL ; + vPL.reserve( vPar.size()) ; double dPar1, dPar2 ; Point3d pt1, pt2 ; @@ -1229,7 +1253,7 @@ TrimPolyLine( const PolyLine& PLOrig, const DBLVECTOR& vPar) bool bFound = PLOrig.GetFirstUPoint( &dPar1, &pt1) ; PolyLine PLCurr ; PLCurr.AddUPoint( 0, pt1) ; bFound = PLOrig.GetNextUPoint( &dPar2, &pt2) ; - for ( int i = 0 ; i < ssize( vPar) && bFound ; i ++) { + for ( int i = 1 ; i < ssize( vPar) && bFound ; i ++) { while( dPar2 < vPar[i] - EPS_SMALL && bFound) { // se non si è ancora arrivati al parametro richiesto devo aggiungere il punto e passare al successivo PLCurr.AddUPoint( dPar2, pt2) ; @@ -1257,6 +1281,88 @@ TrimPolyLine( const PolyLine& PLOrig, const DBLVECTOR& vPar) return vPL ; } +//------------------------------------------------------------------------------- +static bool +AdjustStartPoint( ICurve* pCrv1, ICurveComposite* pCrv2, int nCapType) +{ + // se flat caps sistemo il punto di inizio sul cap start se presente + if ( nCapType == RSCAP_FLAT) { + ICurveComposite* pCompo1 = GetCurveComposite( pCrv1) ; + if ( pCompo1 == nullptr) + return false ; + bool bFlatCap = false ; + for ( int i = 0 ; i < pCompo1->GetCurveCount() ; i ++) { + if ( pCompo1->GetCurve( i)->GetTempProp() == RS_FLATCAP_START) { + pCompo1->ChangeStartPoint( i) ; + bFlatCap = true ; + break ; + } + } + if ( bFlatCap) { + for ( int i = 0 ; i < pCrv2->GetCurveCount() ; i ++) { + if ( pCrv2->GetCurve( i)->GetTempProp() == RS_FLATCAP_START) { + pCrv2->ChangeStartPoint( i) ; + break ; + } + } + return true ; + } + } + + Point3d ptStart ; pCrv1->GetStartPoint( ptStart) ; + double dPar ; int nFlag ; + DistPointCurve( ptStart, *pCrv2).GetParamAtMinDistPoint( 0, dPar, nFlag) ; + pCrv2->ChangeStartPoint( dPar) ; + + return true ; +} + +//------------------------------------------------------------------------------- +static POLYLINEVECTOR +RemoveBisectorsFromPolylines( const PolyLine& PL, const ICurveComposite* pCompo) +{ + POLYLINEVECTOR vPL ; + double dS, dE ; pCompo->GetDomain( dS, dE) ; + + double dPar ; + Point3d ptP ; + bool bFound = PL.GetFirstUPoint( &dPar, &ptP) ; + PolyLine PLCurr ; PLCurr.AddUPoint( 0, ptP) ; + // verifico se è un bisettore controllando la curva di cui è l'approssimazione + int nCrv = int( floor( dPar) + 0.5) ; + int nTempProp = pCompo->GetCurve( nCrv)->GetTempProp() ; + bool bBisector = ( nTempProp == RS_LINK || nTempProp == RS_LINK_BACK) ; + + bFound = PL.GetNextUPoint( &dPar, &ptP) ; + while( bFound) { + // aggiungo il punto + PLCurr.AddUPoint( 0, ptP) ; + // verifico se ultimo punto + if ( abs( dPar - dE) < EPS_SMALL) + break ; + // verifico se è un bisettore + int nCrv = int( floor( dPar) + 0.5) ; + int nTempProp = pCompo->GetCurve( nCrv)->GetTempProp() ; + bool bBisectorCurr = ( nTempProp == RS_LINK || nTempProp == RS_LINK_BACK) ; + // se passo da un bisettore ad una curva standard o viceversa salvo la polyline trovata fino a quel momento ( se non è bisettore) + // e la resetto + if ( bBisector != bBisectorCurr) { + if ( ! bBisector) + vPL.emplace_back( PLCurr) ; + PLCurr.Clear() ; + PLCurr.AddUPoint( 0, ptP) ; + bBisector = bBisectorCurr ; + } + bFound = PL.GetNextUPoint( &dPar, &ptP) ; + } + + // ultima curva + if ( PLCurr.GetPointNbr() > 1 && ! bBisector) + vPL.emplace_back( PLCurr) ; + + return vPL ; +} + //------------------------------------------------------------------------------- static ISurfTriMesh* GetSurfTriMeshBeveledRectSwept( double dDimH, double dDimV, double dBevelH, double dBevelV, const ICurve* pGuide, int nCapType, double dLinTol) @@ -1313,214 +1419,212 @@ GetSurfTriMeshBeveledRectSwept( double dDimH, double dDimV, double dBevelH, doub } } - // sposto il punto di start del loop esterno della FatCrv1 in modo sensato - ICurveComposite* pCompo = GetCurveComposite( vFatCrvs1[0]) ; - if ( pCompo == nullptr) - return nullptr ; - for ( int j = 0 ; j < pCompo->GetCurveCount() ; j ++) { - int nOrigCrv = pCompo->GetCurve( j)->GetTempProp() ; - if ( nOrigCrv > 0) { - Point3d ptP ; pCompo->GetCurve( j)->GetStartPoint( ptP) ; - DistPointCurve distPC( ptP, *vFatCrvs2[0]) ; - double dDist = 0 ; - distPC.GetDist( dDist) ; - if ( abs( dDist - dBevelH) < 100 * EPS_SMALL) { - pCompo->ChangeStartPoint( j + 0.5) ; - break ; - } - } - } - for ( int i = 0 ; i < ssize( vFatCrvs2) ; i++) vFatCrvs2[i]->Translate( - dBevelV * vtNorm) ; - // trasformo in polylines - POLYLINEVECTOR vPL1( vFatCrvs1.size()) ; - for ( int i = 0 ; i < ssize( vFatCrvs1) ; i++) - vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ; + // se necessario rimuovo le giunzioni per costruire le superfici laterali + ICURVEPOVECTOR vFatCrvsOrig1 ; + if ( ! bGuideClosed && nCapType == RSCAP_NONE) { + // salvo le curve con le giunzioni per il calcolo della superficie top + for ( int i = 0 ; i < ssize( vFatCrvs1) ; i++) + vFatCrvsOrig1.emplace_back( vFatCrvs1[i]->Clone()) ; + if ( ! RemoveFatCurveJunctions( vFatCrvs1) || ! RemoveFatCurveJunctions( vFatCrvs2)) + return nullptr ; + } - // costruisco le parti di superficie - PtrOwner pSrfTop( CreateSurfTriMesh()) ; - if ( IsNull( pSrfTop) || ! pSrfTop->CreateByPolygonWithHoles( vPL1)) - return nullptr ; - PtrOwner pSrfBot( pSrfTop->Clone()) ; - if ( IsNull( pSrfBot)) - return nullptr ; - pSrfBot->Translate( -dDimV * vtNorm) ; - pSrfBot->Invert() ; - - int nBuckets = max( 4 * ( pSrfTop->GetVertexSize()), 1000) ; + // creo le parti di superficie StmFromTriangleSoup stmSoup ; - if ( ! stmSoup.Start( nBuckets)) + if ( ! stmSoup.Start()) return nullptr ; - stmSoup.AddSurfTriMesh( *pSrfTop) ; - stmSoup.AddSurfTriMesh( *pSrfBot) ; + // superfici laterali e verticali + POLYLINEVECTOR vPL1( vFatCrvs1.size()) ; BOOLVECTOR bUsed( vFatCrvs2.size(), false) ; - bool bRepair = false ; - for ( int i = 0 ; i < ssize( vFatCrvs1) ; i ++) { + // cerco le FatCurve2 che contengono la FatCurve1 corrente INTVECTOR vAssociatedIdx ; for ( int j = 0 ; j < ssize( vFatCrvs2) ; j ++) { if ( ! bUsed[j]) { - double dS, dE ; vFatCrvs2[j]->GetDomain( dS, dE) ; - double dU = 0.5 ; - int nSide = MDS_ON ; - while ( nSide == MDS_ON && dU < dE) { - Point3d ptTest ; vFatCrvs2[j]->GetPointD1D2( dU, ICurve::FROM_MINUS, ptTest) ; - DistPointCurve distPC( ptTest, *vFatCrvs1[i]) ; - distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; - if ( nSide == MDS_RIGHT) { - bUsed[j] = true ; - vAssociatedIdx.emplace_back( j) ; + CurveComposite* pCompo2 = GetBasicCurveComposite( vFatCrvs2[j]) ; + if ( pCompo2 == nullptr) + return nullptr ; + for ( int k = 0 ; k < pCompo2->GetCurveCount() ; k ++) { + int nProp = pCompo2->GetCurve( k)->GetTempProp() ; + if ( nProp != RS_FLATCAP_START && nProp != RS_FLATCAP_END) { + Point3d ptTest ; vFatCrvs2[j]->GetPointD1D2( k + 0.5, ICurve::FROM_MINUS, ptTest) ; + DistPointCurve distPC( ptTest, *vFatCrvs1[i]) ; + int nSide ; + distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; + if ( nSide == MDS_RIGHT) { + bUsed[j] = true ; + vAssociatedIdx.emplace_back( j) ; + } + break ; } - dU += 1 ; } } } - ISURFTMPOVECTOR vSurfLatTop ; ISURFTMPOVECTOR vSurfLat ; + ISURFTMPOVECTOR vSurfVert ; - // a) se non ha associate allora collassa in un punto/curva ad una quota opportuna + // a) se non ha associate allora collassa in una curva ad una quota opportuna if ( vAssociatedIdx.empty()) { double dLimitOffs ; if ( ! CalcCurveLimitOffset( *vFatCrvs1[i], dLimitOffs)) return nullptr ; + + // per un risultato migliore applico un offset leggermente più piccolo di quello limite e aggiungo una superficie di chiusura OffsetCurve OffsCrv ; - OffsCrv.Make( vFatCrvs1[i], dLimitOffs, ICurve::OFF_FILLET) ; - - PtrOwner pSrfLatT( CreateBasicSurfTriMesh()) ; - if ( IsNull( pSrfLatT)) + OffsCrv.Make( vFatCrvs1[i], dLimitOffs - 10 * EPS_SMALL, ICurve::OFF_FILLET) ; + PtrOwner pAssociatedCrv( ConvertCurveToComposite( OffsCrv.GetLongerCurve())) ; + if ( IsNull( pAssociatedCrv)) return nullptr ; - - Point3d ptOffs ; Vector3d vtOut ; - if ( OffsCrv.GetPointOffset( ptOffs, vtOut)) { - // se collassa in un punto - ptOffs.Translate( - dBevelV / dBevelH * dLimitOffs * vtNorm) ; - pSrfLatT->CreateByPointCurve( ptOffs, vPL1[i]) ; - pSrfLatT->Invert() ; - } - else { - // se collassa in una curva - PtrOwner pAssociatedCrv( ConvertCurveToComposite( OffsCrv.GetLongerCurve())) ; - if ( IsNull( pAssociatedCrv)) - return nullptr ; - pAssociatedCrv->Translate( - dBevelV / dBevelH * dLimitOffs * vtNorm) ; - - Point3d ptStart ; vFatCrvs1[i]->GetStartPoint( ptStart) ; - double dPar ; int nFlag ; - DistPointCurve( ptStart, *pAssociatedCrv).GetParamAtMinDistPoint(0, dPar, nFlag) ; - pAssociatedCrv->ChangeStartPoint( dPar) ; - - PolyLine PL2 ; - pAssociatedCrv->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; - pSrfLatT->CreateByTwoCurves( PL2, vPL1[i], ISurfTriMesh::RLT_MINDIST) ; - } - vSurfLatTop.emplace_back( Release( pSrfLatT)) ; - } - - - // b) se ha una sola associata costruisco la rigata tra le due curve - else if ( vAssociatedIdx.size() == 1) { - - ICurveComposite* pAssociatedCrv = GetCurveComposite( vFatCrvs2[vAssociatedIdx[0]]) ; - if ( pAssociatedCrv == nullptr) - return nullptr ; - - // per avere rigata ottimale modifico il punto di inizio della seconda curva - Point3d ptStart ; vFatCrvs1[i]->GetStartPoint( ptStart) ; - double dPar ; int nFlag ; - DistPointCurve( ptStart, *pAssociatedCrv).GetParamAtMinDistPoint( 0, dPar, nFlag) ; - pAssociatedCrv->ChangeStartPoint( dPar) ; + pAssociatedCrv->Translate( - dBevelV / dBevelH * dLimitOffs * vtNorm) ; + AdjustStartPoint( vFatCrvs1[i], pAssociatedCrv, RSCAP_NONE) ; + vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ; PolyLine PL2 ; pAssociatedCrv->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; - - PtrOwner pSrfLatT( CreateBasicSurfTriMesh()) ; - if ( IsNull( pSrfLatT) || ! pSrfLatT->CreateByTwoCurves( PL2, vPL1[i], ISurfTriMesh::RLT_MINDIST)) + PtrOwner pSrfL( CreateBasicSurfTriMesh()) ; + if ( IsNull( pSrfL)) return nullptr ; - vSurfLatTop.emplace_back( Release( pSrfLatT)) ; + pSrfL->CreateByTwoCurves( vPL1[i], PL2, ISurfTriMesh::RLT_MINDIST) ; + pSrfL->Invert() ; - PtrOwner pSrfLat( CreateBasicSurfTriMesh()) ; - if ( IsNull( pSrfLat) || ! pSrfLat->CreateByExtrusion( PL2, ( - dDimV + 2 * dBevelV) * vtNorm)) + PtrOwner pSrfBase( CreateBasicSurfTriMesh()) ; + if ( IsNull( pSrfBase)) return nullptr ; - pSrfLat->Invert() ; - vSurfLat.emplace_back( Release( pSrfLat)) ; + pSrfBase->CreateByFlatContour( PL2) ; + pSrfBase->Invert() ; + vSurfLat.emplace_back( Release( pSrfL)) ; + vSurfLat.emplace_back( Release( pSrfBase)) ; } + + + // b) se ha una o più curve associate else { - bRepair = true ; - // superfici verticali - for ( int j = 0 ; j < ssize( vAssociatedIdx) ; j ++) { - PolyLine PL2 ; - vFatCrvs2[vAssociatedIdx[j]]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; - PtrOwner pSrfLat( CreateBasicSurfTriMesh()) ; - if ( IsNull( pSrfLat) || ! pSrfLat->CreateByExtrusion( PL2, ( - dDimV + 2 * dBevelV) * vtNorm)) - return nullptr ; - pSrfLat->Invert() ; - vSurfLat.emplace_back( Release( pSrfLat)) ; - } - - // collego le curve tramite bisettori + // collego le curve con opportuni bisettori e se verifico se sono necessari dei bisettori per ricreare al meglio + // la curva di autointersezione della superficie laterale del solido di swept ICurveComposite* pFatCrv2 = Connect( pGuide, vFatCrvs2, vAssociatedIdx, dDimH, dBevelH, dBevelV, vtNorm, nCapType) ; - if ( pFatCrv2 == nullptr) - return nullptr ; - - // individuo i punti ottimali dove spezzare le polylines per avere delle rigate ottimali - DBLVECTOR vPar1, vPar2 ; - Associate( GetCurveComposite( vFatCrvs1[i]), pFatCrv2, vtNorm, vPar1, vPar2) ; - PolyLine PL2 ; - pFatCrv2->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; - POLYLINEVECTOR vPolyLine1 = TrimPolyLine( vPL1[i], vPar1) ; - POLYLINEVECTOR vPolyLine2 = TrimPolyLine( PL2, vPar2) ; - - for ( int j = 0 ; j < ssize( vPolyLine1) ; j ++) { - - if ( vPolyLine1[j].GetPointNbr() == 1 ) { - // collassa - Point3d pt1 ; vPolyLine1[j].GetFirstPoint( pt1) ; - Point3d pt2 ; vPolyLine2[j].GetFirstPoint( pt2) ; - Point3d pt3 ; vPolyLine2[j].GetLastPoint( pt3) ; - - vPolyLine2[j].Close() ; - PtrOwner pSrfLatT( CreateBasicSurfTriMesh()) ; - if ( IsNull( pSrfLatT )) - return nullptr ; - pSrfLatT->CreateByFlatContour( vPolyLine2[j]) ; - vSurfLatTop.emplace_back( Release( pSrfLatT)) ; - - Triangle3d trExtra ; - trExtra.Set( pt1, pt2, pt3) ; - stmSoup.AddTriangle( trExtra) ; - trExtra.Mirror( ptCen, vtNorm) ; - stmSoup.AddTriangle( trExtra) ; + + if ( ssize( vAssociatedIdx) == 1 && pFatCrv2 == nullptr) { + // se aveva unica associata e non sono stati necessari bisettori extra è rigata tra le due curve + ICurveComposite* pAssociatedCrv = GetCurveComposite( vFatCrvs2[vAssociatedIdx[0]]) ; + if ( pAssociatedCrv == nullptr) + return nullptr ; + // per avere rigata ottimale modifico il punto di inizio + AdjustStartPoint( vFatCrvs1[i], pAssociatedCrv, nCapType) ; + + // creo le polylines + vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ; + PolyLine PL2 ; + pAssociatedCrv->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; + + PtrOwner pSrfL( CreateBasicSurfTriMesh()) ; + if ( IsNull( pSrfL) || ! pSrfL->CreateByTwoCurves( vPL1[i], PL2, ISurfTriMesh::RLT_MINDIST)) + return nullptr ; + pSrfL->Invert() ; + vSurfLat.emplace_back( Release( pSrfL)) ; + + PtrOwner pSrfV( CreateBasicSurfTriMesh()) ; + if ( IsNull( pSrfV) || ! pSrfV->CreateByExtrusion( PL2, ( - dDimV + 2 * dBevelV) * vtNorm)) + return nullptr ; + pSrfV->Invert() ; + vSurfVert.emplace_back( Release( pSrfV)) ; + } + else { + if ( pFatCrv2 == nullptr) + return nullptr ; + + // aggiusto il punto di inizio sulla curva 1 per avvicinarlo a quello ottimale già individuato dal connect per la curva 2 + Point3d ptStart ; pFatCrv2->GetStartPoint( ptStart) ; + double dPar ; int nFlag ; + DistPointCurve( ptStart, *vFatCrvs1[i]).GetParamAtMinDistPoint( 0, dPar, nFlag) ; + GetCurveComposite( vFatCrvs1[i])->ChangeStartPoint( dPar) ; + + // creo le polylines e le spezzo opportunamente per avere delle rigate ottimali ( i tratti derivanti dai bisettori + // essendo sovrapposti per tratto di andata e di ritorno potrebbero creare problemi nell'identificazione dei punti + // a minima distanza) + vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ; + PolyLine PL2 ; + pFatCrv2->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; + + DBLVECTOR vPar1, vPar2 ; + if ( ! Associate( GetCurveComposite( vFatCrvs1[i]), pFatCrv2, vtNorm, vPar1, vPar2)) + return nullptr ; + + POLYLINEVECTOR vPolyLine1 = SplitPolyLine( vPL1[i], vPar1) ; + POLYLINEVECTOR vPolyLine2 = SplitPolyLine( PL2, vPar2) ; + + // visto che spezzo la polyline 1 per le superfici laterali, per evitare TJunctions con la superficie top aggiorno + // la polyline 1 + vPL1[i].Clear() ; + for ( int j = 0 ; j < ssize( vPolyLine1) ; j ++) { + PNTULIST lUPnts = vPolyLine1[j].GetUPointList() ; + for ( POINTU& upt : lUPnts) + vPL1[i].AddUPoint( upt.second, upt.first) ; } - else { - PtrOwner pSrfLatT( CreateBasicSurfTriMesh()) ; - if ( IsNull( pSrfLatT)) - return nullptr ; - pSrfLatT->CreateByTwoCurves( vPolyLine1[j], vPolyLine2[j], ISurfTriMesh::RLT_MINDIST) ; - pSrfLatT->Invert() ; - vSurfLatTop.emplace_back( Release( pSrfLatT)) ; + + for ( int j = 0 ; j < ssize( vPolyLine1) ; j ++) { + if ( vPolyLine1[j].GetPointNbr() > 1 && vPolyLine2[j].GetPointNbr() > 1) { + PtrOwner pSrfL( CreateBasicSurfTriMesh()) ; + if ( IsNull( pSrfL)) + return nullptr ; + pSrfL->CreateByTwoCurves( vPolyLine1[j], vPolyLine2[j], ISurfTriMesh::RLT_MINDIST) ; + pSrfL->Invert() ; + vSurfLat.emplace_back( Release( pSrfL)) ; + } + } + + // creazione delle superfici laterali verticali: per evitare problemi di TJunctions uso le stesse polylines + // utilzzate per il calcolo delle superfici laterali, rimuovendo però i tratti derivanti dai bisettori + // perchè corrispondono alle curve di autointersezione della superficie e quindi non generano pareti verticali + for ( int j = 0 ; j < ssize( vPolyLine2) ; j ++) { + POLYLINEVECTOR vPL = RemoveBisectorsFromPolylines( vPolyLine2[j], pFatCrv2) ; + for ( int k = 0 ; k < ssize( vPL) ; k++) { + PtrOwner pSrfV( CreateBasicSurfTriMesh()) ; + if ( IsNull( pSrfV) || ! pSrfV->CreateByExtrusion( vPL[k], ( - dDimV + 2 * dBevelV) * vtNorm)) + return nullptr ; + pSrfV->Invert() ; + vSurfVert.emplace_back( Release( pSrfV)) ; + } } } } - - // inserisco le superfici laterali verticali - for ( int j = 0 ; j < ssize( vSurfLat) ; j ++) - stmSoup.AddSurfTriMesh( *vSurfLat[j]) ; - + + // inserisco le superfici verticali + for ( int j = 0 ; j < ssize( vSurfVert) ; j ++) + stmSoup.AddSurfTriMesh( *vSurfVert[j]) ; + // inserisco le superfici laterali rigate e il loro mirror - for ( int j = 0 ; j < ssize( vSurfLatTop) ; j ++) { - stmSoup.AddSurfTriMesh( *vSurfLatTop[j]) ; - vSurfLatTop[j]->Mirror( ptCen, vtNorm) ; - stmSoup.AddSurfTriMesh( *vSurfLatTop[j]) ; + for ( int j = 0 ; j < ssize( vSurfLat) ; j ++) { + stmSoup.AddSurfTriMesh( *vSurfLat[j]) ; + vSurfLat[j]->Mirror( ptCen, vtNorm) ; + stmSoup.AddSurfTriMesh( *vSurfLat[j]) ; } } + + // superficie top + if ( ! vFatCrvsOrig1.empty()) { + vPL1.clear() ; + vPL1.resize( vFatCrvsOrig1.size()) ; + for ( int i = 0 ; i < ssize( vFatCrvsOrig1) ; i++) + vFatCrvsOrig1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ; + } + PtrOwner pSrfTop( CreateSurfTriMesh()) ; + if ( IsNull( pSrfTop) || ! pSrfTop->CreateByPolygonWithHoles( vPL1)) + return nullptr ; + stmSoup.AddSurfTriMesh( *pSrfTop) ; + // superficie bottom + pSrfTop->Translate( -dDimV * vtNorm) ; + pSrfTop->Invert() ; + stmSoup.AddSurfTriMesh( *pSrfTop) ; + if ( ! stmSoup.End()) return nullptr ; @@ -1529,10 +1633,6 @@ GetSurfTriMeshBeveledRectSwept( double dDimH, double dDimV, double dBevelH, doub if ( IsNull( pSTM)) return nullptr ; - // se ho spezzato per la superficie laterale, rischio di aver creato Tjunctions - if ( bRepair) - pSTM->Repair() ; - // salvo tolleranza lineare usata e imposto angolo per smooth pSTM->SetLinearTolerance( dLinTol) ; pSTM->SetSmoothAngle( 20) ; diff --git a/Voronoi.cpp b/Voronoi.cpp index 09bf2b6..92a551d 100644 --- a/Voronoi.cpp +++ b/Voronoi.cpp @@ -434,6 +434,7 @@ Voronoi::GetBisectorCurve( int i) pLine->Set( ptS, ptE) ; pLine->SetTempParam( dParS, 0) ; pLine->SetTempParam( dParE, 1) ; + pLine->SetTempProp( i) ; pLine->ToGlob( m_Frame) ; return pLine ; } @@ -454,6 +455,7 @@ Voronoi::GetBisectorCurve( int i) pArc->SetC2P( ptC, ptS, ptE) ; pArc->SetTempParam( dParS, 0) ; pArc->SetTempParam( dParS, 1) ; // dParE = dParS + pArc->SetTempProp( i) ; pArc->ToGlob( m_Frame) ; return pArc ; } @@ -473,7 +475,7 @@ Voronoi::GetBisectorCurve( int i) bool bInvert = false ; double dPar1, dPar2 ; m_vroni->GetApproxedBisectorParams( i, dPar1, dPar2) ; - if ( dPar1 > dPar2 + EPS_SMALL) + if ( dPar1 > dPar2) bInvert = true ; // punto iniziale @@ -496,6 +498,11 @@ Voronoi::GetBisectorCurve( int i) dParPrev = dPar ; nCrvCount ++ ; } + else if ( ( bInvert && j == 0) || ( ! bInvert && j == nPoints - 1)) { + // se estremo devo forzarlo + pCompo->ModifyEnd( pt) ; + dParPrev = dPar ; + } // aggiorno per punto successivo if ( bInvert) j -- ; @@ -506,6 +513,7 @@ Voronoi::GetBisectorCurve( int i) // setto parametri sulla curva pCompo->SetTempParam( dParS, 0) ; pCompo->SetTempParam( dParPrev, 1) ; + pCompo->SetTempProp( i) ; pCompo->ToGlob( m_Frame) ; return pCompo ; @@ -700,8 +708,8 @@ Voronoi::CalcSingleCurvesOffset( ICURVEPOVECTOR& vOffs, double dOffs) // se necessario verifico se dal lato corretto rispetto ai siti di riferimento if ( bLeft && bRight) { // recupero i siti di riferimento - int nOrigCrv1, nOrigSubCrv1, nOrigCrv2, nOrigSubCrv2 ; - m_vroni->GetBisectorSites( i, nOrigCrv1, nOrigSubCrv1, nOrigCrv2, nOrigSubCrv2) ; + int nOrigCrv1, nOrigSubCrv1, nOrigSubPnt1, nOrigCrv2, nOrigSubCrv2, nOrigSubPnt2 ; + m_vroni->GetBisectorSites( i, nOrigCrv1, nOrigSubCrv1, nOrigSubPnt1, nOrigCrv2, nOrigSubCrv2, nOrigSubPnt2) ; if ( nOrigCrv1 != -1) { // verifico il lato rispetto al primo sito pCrv->SetTempProp( nOrigSubCrv1 + 1, 0) ; @@ -1261,3 +1269,62 @@ Voronoi::CalcLimitOffset( int nCrv, bool bLeft, double& dOffs) return true ; } + +//--------------------------------------------------------------------------- +bool +Voronoi::GetBisectorPointAtParam( int nCrv, double dPar, Point3d& ptP) +{ + if ( ! IsValid()) + return false ; + + try { + if ( ! m_bVDComputed) + CalcVoronoi() ; + + // verifico se il bisettore e il parametri richiesto sono sensati + if ( nCrv >= m_vroni->GetNumberOfEdges()) + return false ; + double dParS, dParE ; + m_vroni->GetBisectorParams( nCrv, dParS, dParE) ; + if ( dParS > dParE) + swap( dParS, dParE) ; + if ( dPar < dParS || dPar > dParE) + return false ; + + // calcolo il punto sul bisettore in corrispondenza dell'offset + m_vroni->GetBisectorPointAtParam( nCrv, dPar, ptP.v) ; + ptP.ToGlob( m_Frame) ; + + return true ; + } + catch (...) { + LOG_ERROR( GetEGkLogger(), m_vroni->GetExceptionMessage()) ; + return false ; + } + +} + +//--------------------------------------------------------------------------- +bool +Voronoi::GetBisectorSites( int nCrv, int& nCrv1, int& nSubCrv1, int& nSubPnt1, int& nCrv2, int& nSubCrv2, int& nSubPnt2) +{ + if ( ! IsValid()) + return false ; + + try { + if ( ! m_bVDComputed) + CalcVoronoi() ; + + // verifico se il bisettore è valido + if ( nCrv >= m_vroni->GetNumberOfEdges()) + return false ; + + // calcolo il punto sul bisettore in corrispondenza dell'offset + m_vroni->GetBisectorSites( nCrv, nCrv1, nSubCrv1, nSubPnt1, nCrv2, nSubCrv2, nSubPnt2) ; + return true ; + } + catch (...) { + LOG_ERROR( GetEGkLogger(), m_vroni->GetExceptionMessage()) ; + return false ; + } +} \ No newline at end of file diff --git a/Voronoi.h b/Voronoi.h index 9f83c12..ccf931e 100644 --- a/Voronoi.h +++ b/Voronoi.h @@ -60,6 +60,8 @@ class Voronoi 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) ; + bool GetBisectorPointAtParam( int nCrv, double dPar, Point3d& ptP) ; + bool GetBisectorSites( int nCrv, int& nCrv1, int& nSubCrv1, int& nSubPnt1, int& nCrv2, int& nSubCrv2, int& nSubPnt2) ; bool Translate( const Vector3d& vtMove) ; bool Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dAngDeg) ;