//---------------------------------------------------------------------------- // EgalTech 2015-2023 //---------------------------------------------------------------------------- // File : Voronoi.cpp Data : 23.11.23 Versione : 2.5k5 // Contenuto : Classe per Voronoi con libreria VRONI. // // // // Modifiche : 23.11.23 SP Creazione modulo. //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "GeoConst.h" #include "CurveComposite.h" #include "CurveArc.h" #include "CurveLine.h" #include "CurveBezier.h" #include "SurfFlatRegion.h" #include "OffsetAux.h" #include "RemoveCurveDefects.h" #include "Voronoi.h" #include "/EgtDev/Include/EGkDistPointCurve.h" using namespace std ; //---------------------------------------------------------------------------- Voronoi::Voronoi( const ICurve* pCrv, bool bAllowAdd) : m_vroni( nullptr), m_nBound( VORONOI_STD_BOUND), m_bVDComputed( false), m_bAllowAdd( true) { // tento di aggiungere la curva if ( ! AddCurve( pCrv)) Clear() ; m_bAllowAdd = bAllowAdd ; } //---------------------------------------------------------------------------- Voronoi::Voronoi( const ISurfFlatRegion* pSfr, bool bAllowAdd) : m_vroni( nullptr), m_nBound( VORONOI_STD_BOUND), m_bVDComputed( false), m_bAllowAdd( true) { // tento di aggiungere la superficie if ( ! AddSurfFlatRegion( pSfr)) Clear() ; m_bAllowAdd = bAllowAdd ; } //---------------------------------------------------------------------------- Voronoi::~Voronoi( void) { Clear() ; } //---------------------------------------------------------------------------- bool Voronoi::Clear( void) { // pulizia oggetto vroni if ( m_vroni != nullptr) delete m_vroni ; m_vroni = nullptr ; // ciclo di pulizia delle curve associate for ( auto pCrv : m_vpCrvs) delete pCrv ; m_vpCrvs.clear() ; m_nBound = VORONOI_STD_BOUND ; m_bVDComputed = false ; return true ; } //---------------------------------------------------------------------------- bool Voronoi::AddCurve( const ICurve* pCrv) { if ( ! m_bAllowAdd) return false ; if ( pCrv == nullptr) return false ; // verifico se è una linea int nType = pCrv->GetType() ; bool bIsLine = ( nType == CRV_LINE) ; if ( nType == CRV_COMPO) { const CurveComposite* pCompo = GetBasicCurveComposite( pCrv) ; Point3d ptStart, ptEnd ; if ( pCompo != nullptr && pCompo->IsALine( 10 * EPS_SMALL, ptStart, ptEnd)) bIsLine = true ; } // verifico sia piana e trovo piano su cui giace Plane3d plPlane ; if ( bIsLine) { // linea è sicuramente piana. Scelgo piano definito dall'estrusione ( se definita) Point3d ptS ; pCrv->GetStartPoint( ptS) ; Vector3d vtExtr ; pCrv->GetExtrusion( vtExtr) ; if ( ! vtExtr.IsSmall()) plPlane.Set( ptS, vtExtr) ; else plPlane.Set( ptS, Z_AX) ; } else { if ( ! pCrv->IsFlat( plPlane, false, 10 * EPS_SMALL)) return false ; } if ( m_vpCrvs.size() == 0) { // se prima curva considerata assegno il frame al Voronoi m_Frame.Set( plPlane.GetPoint(), plPlane.GetVersN()) ; } else { // altrimenti verifico sia complanare ad eventuali curve già presenti if ( ! AreSameOrOppositeVectorApprox( m_Frame.VersZ(), plPlane.GetVersN()) || ! PointInPlaneApprox( m_Frame.Orig(), plPlane)) return false ; } // creo una copia della curva e la porto in locale PtrOwner pCrvLoc( pCrv->Clone()) ; if ( IsNull( pCrvLoc)) return false ; pCrvLoc->ToLoc( m_Frame) ; try { // verifico se oggetto vroni è stato inizializzato if ( m_vroni == nullptr) { m_vroni = new( nothrow) vroniObject() ; if ( m_vroni == nullptr) return false ; m_vroni->apiInitializeProgram() ; } // aggiungo la curva in locale all'oggetto vroni if ( ! AddCurveToVroni( pCrvLoc)) return false ; } catch (...) { LOG_ERROR( GetEGkLogger(), m_vroni->GetExceptionMessage()) ; return false ; } // aggiorno il box complessivo BBox3d bBox ; pCrvLoc->GetLocalBBox( bBox) ; m_bBox.Add( bBox) ; return true ; } //---------------------------------------------------------------------------- bool Voronoi::AddSurfFlatRegion( const ISurfFlatRegion* pSfr) { if ( ! m_bAllowAdd) return false ; if ( pSfr == nullptr) return false ; // recupero il piano Point3d ptCen ; pSfr->GetCentroid( ptCen) ; Vector3d vtN = pSfr->GetNormVersor() ; if ( m_vpCrvs.size() == 0) { // assegno il frame al Voronoi m_Frame.Set( ptCen, vtN) ; } else { // verifico sia complanare ad eventuali curve già presenti Plane3d plPlane ; plPlane.Set( ptCen, pSfr->GetNormVersor()) ; if ( ! AreSameOrOppositeVectorApprox( m_Frame.VersZ(), pSfr->GetNormVersor()) || ! PointInPlaneApprox( m_Frame.Orig(), plPlane)) return false ; } try { // verifico se oggetto vroni è stato inizializzato if ( m_vroni == nullptr) { m_vroni = new( nothrow) vroniObject() ; if ( m_vroni == nullptr) return false ; m_vroni->apiInitializeProgram() ; } // aggiungo le curve di loop for ( int i = 0 ; i < pSfr->GetChunkCount() ; i ++) { for ( int j = 0 ; j < pSfr->GetLoopCount( i) ; j ++) { PtrOwner pCrvLoc( pSfr->GetLoop( i, j)) ; if ( IsNull( pCrvLoc)) return false ; pCrvLoc->ToLoc( m_Frame) ; if ( ! AddCurveToVroni( pCrvLoc)) return false ; } } } catch (...) { LOG_ERROR( GetEGkLogger(), m_vroni->GetExceptionMessage()) return false ; } // aggiorno il box complessivo BBox3d bBox ; Frame3d frSrf = m_Frame ; frSrf.Invert() ; pSfr->GetBBox( frSrf, bBox) ; m_bBox.Add( bBox) ; return true ; } //---------------------------------------------------------------------------- bool Voronoi::AddCurveToVroni( const ICurve* pCrv) { int nLoopId = m_vpCrvs.size() ; // aggiungo il punto iniziale Point3d ptStart ; if ( ! pCrv->GetStartPoint( ptStart)) return false ; int nCrv = m_vroni->HandlePnt( ptStart.x, ptStart.y, {nLoopId, 0}) ; // aggiungo la parte rimanente della curva switch ( pCrv->GetType()) { case CRV_LINE : m_vpCrvs.emplace_back( pCrv->Clone()) ; return AddLineToVroni( GetCurveLine( pCrv), nCrv, nLoopId) ; case CRV_ARC : m_vpCrvs.emplace_back( pCrv->Clone()) ; return AddArcToVroni( GetCurveArc( pCrv), nCrv, nLoopId) ; case CRV_COMPO : return AddCompoToVroni( GetCurveComposite( pCrv), nCrv, nLoopId) ; case CRV_BEZIER : return AddBezierToVroni( GetCurveBezier( pCrv), nCrv, nLoopId) ; default : return false ; } return false ; } //---------------------------------------------------------------------------- bool Voronoi::AddLineToVroni( const ICurveLine* pLine, int& nVroniCrv, int nLoopId, int nCrvId, Point3d ptEnd) { if ( pLine == nullptr) return false ; // verifico se il punto finale viene forzato oppure deve essere ricavato dalla pLine if ( ! ptEnd.IsValid()) { if ( ! pLine->GetEndPoint( ptEnd)) return false ; } m_vroni->AddSeg( &nVroniCrv, ptEnd.x, ptEnd.y, {nLoopId, nCrvId}) ; return true ; } //---------------------------------------------------------------------------- bool Voronoi::AddArcToVroni( const ICurveArc* pArc, int& nVroniCrv, int nLoopId, int nCrvId, Point3d ptEnd) { if ( pArc == nullptr) return false ; Point3d ptCen = pArc->GetCenter() ; double dAngCen = pArc->GetAngCenter() ; int nArcSiteType = ( dAngCen > 0 ? ARC : -ARC ) ; Vector3d vtN = pArc->GetNormVersor() ; if ( AreOppositeVectorApprox( vtN, Z_AX)) nArcSiteType *= -1 ; if ( pArc->IsACircle()) { // spezzo arco in due parti Point3d ptM ; if ( ! pArc->GetMidPoint( ptM)) return false ; m_vroni->AddArc( &nVroniCrv, ptM.x, ptM.y, ptCen.x, ptCen.y, nArcSiteType, {nLoopId, nCrvId}) ; // impogno che punto finale coincida con lo start ( per tolleranze vroni) Point3d ptStart ; pArc->GetStartPoint( ptStart) ; m_vroni->AddArc( &nVroniCrv, ptStart.x, ptStart.y, ptCen.x, ptCen.y, nArcSiteType, {nLoopId, nCrvId}) ; } else { // verifico se il punto finale viene forzato oppure deve essere ricavato dal pArc if ( ! ptEnd.IsValid()) { if ( ! pArc->GetEndPoint( ptEnd)) return false ; } m_vroni->AddArc( &nVroniCrv, ptEnd.x, ptEnd.y, ptCen.x, ptCen.y, nArcSiteType, {nLoopId, nCrvId}) ; } return true ; } //---------------------------------------------------------------------------- bool Voronoi::AddCompoToVroni( const ICurveComposite* pCompo, int& nVroniCrv, int nLoopId) { if ( pCompo == nullptr) return false ; PtrOwner pCopy( CloneBasicCurveComposite( pCompo)) ; if ( IsNull( pCopy)) return false ; // verifico che la curva sia fatta solo da rette e archi che giacciono nel piano XY ( estrusione deve essere Z+) Vector3d vtExtr ; pCopy->GetExtrusion( vtExtr) ; pCopy->SetExtrusion( Z_AX) ; if ( ! pCopy->ArcsBezierCurvesToArcsPerpExtr( 10 * EPS_SMALL, ANG_TOL_STD_DEG)) return false ; pCopy->SetExtrusion( vtExtr) ; // aggiungo tutte le sottocurve bool bClosed = pCopy->IsClosed() ; for ( int i = 0 ; i < pCopy->GetCurveCount() ; i++) { Point3d ptForcedEnd = P_INVALID ; // se curva è chiusa, forzo l'end point a coincidere con lo start ( per le tolleranze di vroni) if ( i == pCopy->GetCurveCount() - 1 && bClosed) pCompo->GetStartPoint( ptForcedEnd) ; // aggiungo const ICurve* pCrv = pCopy->GetCurve( i) ; int nType = pCrv->GetType() ; if ( nType == CRV_LINE) AddLineToVroni( GetCurveLine( pCrv), nVroniCrv, nLoopId, i, ptForcedEnd) ; else if ( nType == CRV_ARC) AddArcToVroni( GetCurveArc( pCrv), nVroniCrv, nLoopId, i, ptForcedEnd) ; } // aggiungo al vettore di curve m_vpCrvs.emplace_back( Release( pCopy)) ; return true ; } //---------------------------------------------------------------------------- bool Voronoi::AddBezierToVroni( const ICurveBezier* pBezier, int& nVroniCrv, int nLoopId) { if ( pBezier == nullptr) return false ; // riconduco al caso di curva composita PtrOwner pCompo( CreateBasicCurveComposite()) ; if ( IsNull( pCompo)) return false ; PolyArc PA ; if ( ! pBezier->ApproxWithArcsEx( LIN_TOL_STD, ANG_TOL_STD_DEG, LIN_FEA_LEN_STD, PA) || ! pCompo->FromPolyArc( PA)) return false ; Vector3d vtExtr ; pBezier->GetExtrusion( vtExtr) ; pCompo->SetExtrusion( vtExtr) ; return AddCompoToVroni( pCompo, nVroniCrv, nLoopId) ; } //---------------------------------------------------------------------------- ICurve* Voronoi::GetCurve( int nId) const { // verifico validità indice if ( nId < 0 || nId > ( int)m_vpCrvs.size() - 1) return nullptr ; // ne faccio una copia ICurve* pCrv = m_vpCrvs[nId]->Clone() ; if ( pCrv == nullptr) return nullptr ; // la porto nel riferimento globale pCrv->ToGlob( m_Frame) ; return pCrv ; } //---------------------------------------------------------------------------- bool Voronoi::CalcVoronoi( int nBound) { // se già stato calcolato con lo stesso bound non devo fare nulla if ( m_bVDComputed && nBound == m_nBound) return true ; // se già stato calcolato reset dei dati if ( m_bVDComputed) m_vroni->ResetVoronoiDiagram() ; // come valore minimo per il bound considero quello standard di vroni m_nBound = max( nBound, VORONOI_STD_BOUND) ; // calcolo m_bVDComputed = true ; string sTmp = "" ; m_vroni->apiComputeVD( false, true, false, m_nBound, 0, 0, &sTmp[0], false, false, false, &sTmp[0], true) ; return true ; } //---------------------------------------------------------------------------- ICurve* Voronoi::GetBisectorCurve( int i) { if ( i >= m_vroni->GetNumberOfEdges()) return nullptr ; // identifico il tipo di bisettore int nType = m_vroni->GetBisectorType( i) ; // linea if ( nType == BisectorType::LINE) { // recupero i dati del bisettore da vroni Point3d ptS, ptE ; double dParS, dParE ; m_vroni->GetLinearBisectorData( i, ptS.v, ptE.v, dParS, dParE) ; // creo la linea CurveLine* pLine = CreateBasicCurveLine() ; if ( pLine == nullptr) return nullptr ; // costruisco il bisettore orientato dal parametro minore al maggiore if ( dParS > dParE) { swap( ptS, ptE) ; swap( dParS, dParE) ; } pLine->Set( ptS, ptE) ; pLine->SetTempParam( dParS, 0) ; pLine->SetTempParam( dParE, 1) ; pLine->ToGlob( m_Frame) ; return pLine ; } // degenerate hyperellipse ( arco) else if ( nType == BisectorType::DEGENERATE_HYPERELL) { // recupero i dati del bisettore da vroni Point3d ptS, ptE, ptC ; double dParS, dParE ; m_vroni->GetDegenerateHyperEllipticBisectorData( i, ptS.v, ptE.v, ptC.v, dParS, dParE) ; // creo arco CurveArc* pArc = CreateBasicCurveArc() ; if ( pArc == nullptr) return nullptr ; pArc->SetC2P( ptC, ptS, ptE) ; pArc->SetTempParam( dParS, 0) ; pArc->SetTempParam( dParS, 1) ; // dParE = dParS pArc->ToGlob( m_Frame) ; return pArc ; } // bisettore generico else if ( nType != BisectorType::NONE) { // approssimo linearmente il bisettore int nPoints = m_vroni->GetApproxedBisectorPointsNbr( i) ; if ( nPoints < 2) return nullptr ; CurveComposite* pCompo = CreateBasicCurveComposite() ; if ( pCompo == nullptr) return nullptr ; // verifico se devo leggere i punti del bisettore al contrario per averlo orientato dal parametro minore al maggiore bool bInvert = false ; double dPar1, dPar2 ; m_vroni->GetApproxedBisectorParams( i, dPar1, dPar2) ; if ( dPar1 > dPar2 + EPS_SMALL) bInvert = true ; // punto iniziale Point3d pt ; double dParS ; m_vroni->GetApproxedBisectorPoint( i, bInvert ? nPoints - 1 : 0, pt.v, dParS) ; pCompo->AddPoint( pt) ; int nCrvCount = 0 ; double dParPrev = dParS ; int j = bInvert ? nPoints - 2 : 1 ; while ( ( bInvert && j >= 0) || ( ! bInvert && j < nPoints)) { double dPar ; m_vroni->GetApproxedBisectorPoint( i, j, pt.v, dPar) ; if ( pCompo->AddLine( pt)) { // setto i parametri sulla sottocurva pCompo->SetCurveTempParam( nCrvCount, dParPrev, 0) ; pCompo->SetCurveTempParam( nCrvCount, dPar, 1) ; // aggiorno parametro precedente dParPrev = dPar ; nCrvCount ++ ; } // aggiorno per punto successivo if ( bInvert) j -- ; else j ++ ; } // setto parametri sulla curva pCompo->SetTempParam( dParS, 0) ; pCompo->SetTempParam( dParPrev, 1) ; pCompo->ToGlob( m_Frame) ; return pCompo ; } return nullptr ; } //---------------------------------------------------------------------------- bool Voronoi::CalcVoronoiDiagram( ICURVEPOVECTOR& vCrvs, int nBound) { vCrvs.clear() ; if ( ! IsValid()) return false ; try { // verifico se necessario calcolo Voronoi if ( ! m_bVDComputed || nBound != m_nBound) CalcVoronoi( nBound) ; for ( int i = 4 ; i < m_vroni->GetNumberOfEdges() ; i ++) { // recupero la curva del bisettore PtrOwner pCrv( GetBisectorCurve( i)) ; if ( ! IsNull( pCrv) && pCrv->IsValid()) vCrvs.emplace_back( Release( pCrv)) ; } // libero la memoria di vroni utilizzata per calcolare bisettore m_vroni->apiFreeBisectorBuffer() ; } catch (...) { LOG_ERROR( GetEGkLogger(), m_vroni->GetExceptionMessage()) ; return false ; } return true ; } //---------------------------------------------------------------------------- bool Voronoi::CalcMedialAxis( ICURVEPOVECTOR& vCrvs, int nSide) { vCrvs.clear() ; if ( ! IsValid()) return false ; // lato per il medial axis bool bLeft = true ; bool bRight = true ; if ( nSide == WMAT_LEFT) bRight = false ; else if ( nSide == WMAT_RIGHT) bLeft = false ; try { if ( ! m_bVDComputed) CalcVoronoi() ; // calcolo medial axis m_vroni->apiComputeWMAT( false, 0.0, 0.0, false, bLeft, bRight) ; for ( int i = 4 ; i < m_vroni->GetNumberOfEdges() ; i ++) { // verifico se il lato appartiene al medial axis if ( m_vroni->IsWMATEdge( i)) { PtrOwner pCrv( GetBisectorCurve( i)) ; if ( ! IsNull( pCrv) && pCrv->IsValid()) vCrvs.emplace_back( Release( pCrv)) ; } } // libero la memoria di vroni utilizzata per calcolare bisettore m_vroni->apiFreeBisectorBuffer() ; } catch (...) { LOG_ERROR( GetEGkLogger(), m_vroni->GetExceptionMessage()) ; return false ; } return true ; } //---------------------------------------------------------------------------- bool Voronoi::CalcOffset( ICURVEPOVECTOR& vOffs, double dOffs, int nType) { vOffs.clear() ; if ( ! IsValid()) return false ; // verifico se curve sono coerenti per calcolo dell'offset if ( ! VerifyCurvesValidityForOffset()) return false ; // se offset nullo restituisco direttamente le curve if ( abs( dOffs) < EPS_SMALL) { for ( auto pCrv : m_vpCrvs) vOffs.emplace_back( pCrv->Clone()) ; } bool bClosed = m_vpCrvs[0]->IsClosed() ; // stabilisco lato offset bool bRightOffs = true ; bool bLeftOffs = true ; if ( bClosed) { bRightOffs = ( dOffs > EPS_SMALL ? true : false) ; bLeftOffs = ( dOffs < - EPS_SMALL ? true : false) ; } // calcolo offset ICRVCOMPOPLIST OffsList ; if ( ! CalcVroniOffset( OffsList, abs( dOffs), bRightOffs, bLeftOffs)) return false ; // sistemo le curve di offset calcolate con vroni for ( auto pCrv : OffsList) { // aggiustamento per curva aperta if ( ! bClosed) AdjustOpenOffsetCurve( *pCrv, dOffs) ; if ( pCrv->IsValid()) { // eventuale inversione if ( dOffs > EPS_SMALL) pCrv->Invert() ; // sistemo i raccordi if ( ( nType & ICurve::OFF_CHAMFER) != 0 || ( nType & ICurve::OFF_EXTEND) != 0) { IdentifyFillets( pCrv, dOffs) ; AdjustCurveFillets( pCrv, dOffs, nType) ; } if ( bClosed) { // forzo chiusura della curva per evitare piccole imprecisioni pCrv->Close() ; // sistemo il punto di inizio AdjustOffsetStart( *pCrv) ; } // porto nel frame globale pCrv->ToGlob( m_Frame) ; // unisco le parti allineate pCrv->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG, true, true) ; // aggiungo al vettore finale vOffs.emplace_back( pCrv) ; } else delete pCrv ; } return true ; } //---------------------------------------------------------------------------- bool Voronoi::CalcFatCurve( ICURVEPOVECTOR& vCrvs, double dOffs, bool bSquareEnds, bool bSquareMids) { vCrvs.clear() ; if ( ! IsValid()) return false ; // se offset nullo errore if ( abs( dOffs) < EPS_SMALL) return false ; ICRVCOMPOPLIST OffsList ; if ( ! CalcVroniOffset( OffsList, abs( dOffs), true, true)) return false ; // sistemo le curve di offset calcolate con vroni bool bClosed = m_vpCrvs[0]->IsClosed() ; for ( auto pCrvOffs : OffsList) { // eventuale inversione if ( dOffs > EPS_SMALL) pCrvOffs->Invert() ; // sistemo i raccordi if ( bClosed && bSquareMids) { // se curva è chiusa tutti i raccordi rispondono a bSquareMids IdentifyFillets( pCrvOffs, dOffs) ; AdjustCurveFillets( pCrvOffs, dOffs, ICurve::OFF_EXTEND) ; } else if ( ! bClosed && ( bSquareMids || bSquareEnds)) { // se curva è aperta devo distinguere i raccordi interni da quelli relativi agli estremi e // modificare solo quelli richiesti for ( int j = 0 ; j < pCrvOffs->GetCurveCount() ; j ++) { int nOrigCrv ; pCrvOffs->GetCurveTempProp( j, nOrigCrv) ; double dTmpParam ; pCrvOffs->GetCurveTempParam( j, dTmpParam) ; if ( nOrigCrv == 0 && ( ( bSquareMids && dTmpParam < EPS_SMALL) || ( bSquareEnds && dTmpParam > EPS_SMALL))) pCrvOffs->SetCurveTempParam( j, 1) ; else pCrvOffs->SetCurveTempParam( j, 0) ; } AdjustCurveFillets( pCrvOffs, dOffs, ICurve::OFF_EXTEND) ; } // porto nel frame globale pCrvOffs->ToGlob( m_Frame) ; // unisco le parti allineate pCrvOffs->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG, true, true) ; // aggiungo al vettore finale vCrvs.emplace_back( pCrvOffs) ; } return true ; } //---------------------------------------------------------------------------- bool Voronoi::CalcVroniOffset( ICRVCOMPOPLIST& OffsList, double dOffs, bool bRightOffs, bool bLeftOffs) { OffsList.clear() ; if ( ! IsValid()) return false ; int nOrigCrvCnt = 1 ; if ( m_vpCrvs[0]->GetType() == CRV_COMPO) { const CurveComposite * pOrigCompo = GetBasicCurveComposite( m_vpCrvs[0]) ; if ( pOrigCompo == nullptr) return false ; nOrigCrvCnt = pOrigCompo->GetCurveCount() ; } try { // reset di eventuali offset precedenti m_vroni->apiResetOffsetData() ; // verifico necessario calcolo o ricalcolo di Voronoi UpdateVoronoi( dOffs) ; string sTmp = "" ; m_vroni->apiComputeOff( false, &sTmp[0], false, false, dOffs, 0.0, false, bLeftOffs, bRightOffs) ; int nOffsCnt = m_vroni->GetOffsetCount() ; if ( nOffsCnt == 0) { // se non ho ottenuto offset ritento con valore leggermente diverso per le tolleranze di vroni m_vroni->apiResetOffsetData() ; m_vroni->apiComputeOff( false, &sTmp[0], false, false, dOffs - VRONI_OFFS_TOL, 0.0, false, bLeftOffs, bRightOffs) ; nOffsCnt = m_vroni->GetOffsetCount() ; } // recupero le curve di offset da vroni for ( int i = 0 ; i < nOffsCnt ; i++) { PtrOwner pCrvOffs ( CreateBasicCurveComposite()) ; int nCrvCnt = m_vroni->GetOffsetCurveCount( i) ; // numero di sottocurve for ( int j = 0 ; j < nCrvCnt ; j ++) { // recupero la sottocurva da vroni Point3d ptS, ptE, ptC ; int nType ; int nOrigCrv, nOrigLoop, nOrigPnt ; // sito m_vroni->GetOffsetCurve( i, j, nType, ptS.v, ptE.v, ptC.v, nOrigLoop, nOrigCrv, nOrigPnt) ; if ( j == 0) pCrvOffs->AddPoint( ptS) ; bool bOk = false ; if ( nType == t_site::SEG) bOk = pCrvOffs->AddLine( ptE) ; else { PtrOwner pArc( CreateBasicCurveArc()) ; pArc->Set2PRS( ptS, ptE, Dist( ptC, ptS), nType == CCW) ; bOk = pCrvOffs->AddCurve( Release( pArc)) ; } // se la curva è stata aggiunta if ( bOk) { // setto come info la sottocurva da cui si è generata int nCurrCrvId = pCrvOffs->GetCurveCount() - 1 ; pCrvOffs->SetCurveTempProp( nCurrCrvId, nOrigCrv + 1, 0) ; pCrvOffs->SetCurveTempProp( nCurrCrvId, nOrigLoop, 1) ; // verifico se è raccordo relativo agli estremi della curva if ( nOrigCrv == -1 && ( nOrigPnt == 0 || nOrigPnt == nOrigCrvCnt)) pCrvOffs->SetCurveTempParam( nCurrCrvId, 1.0, 0) ; } } // rimuovo tratti di lunghezza inferiore a 5 * EPS_SMALL RemoveCurveSmallParts( pCrvOffs, 5 * EPS_SMALL) ; // aggiungo la curva alla lista degli offset if ( ! IsNull( pCrvOffs) && pCrvOffs->IsValid()) OffsList.push_back( Release( pCrvOffs)) ; } // libero la memoria di vroni dedicata agli offset m_vroni->apiFreeOffsetData() ; } catch (...) { LOG_ERROR( GetEGkLogger(), m_vroni->GetExceptionMessage()) ; return false ; } return true ; } //---------------------------------------------------------------------------- bool Voronoi::UpdateVoronoi( double dOffs) { // calcolo il bound necessario per l'offset desiderato double dNeededBound = abs( dOffs) / 0.49 / sqrt( m_bBox.GetDimX() * m_bBox.GetDimX() + m_bBox.GetDimY() * m_bBox.GetDimY()) ; if ( ! m_bVDComputed || dNeededBound > m_nBound) { // aggiorno il valore del bound int nBound = ( int)( ceil( dNeededBound) + 0.5) ; // calcolo il nuovo diagramma CalcVoronoi( nBound) ; } return true ; } //---------------------------------------------------------------------------- bool Voronoi::VerifyCurvesValidityForOffset() { if ( m_vpCrvs.size() == 1) return true ; // se ho più curve, devono essere tutte chiuse for ( auto pCrv : m_vpCrvs) { if ( ! pCrv->IsClosed()) return false ; } // verifico se gli orientamenti delle curve sono coerenti DBLVECTOR vArea( m_vpCrvs.size()) ; double dRefArea = 0.0 ; for ( int i = 0 ; i < ( int)m_vpCrvs.size() ; i ++) { m_vpCrvs[i]->GetAreaXY( vArea[i]) ; if ( abs( vArea[i]) > abs( dRefArea)) dRefArea = vArea[i] ; } bool bRefCCW = ( dRefArea > EPS_SMALL) ; for ( int i = 0 ; i < ( int)vArea.size() ; i++) { // se curva con orientamento principale verifico sia esterna a tutte le altre curve con orientamento principale if ( vArea[i] * dRefArea > EPS_SMALL) { for ( int j = 0 ; j < ( int)vArea.size() ; j++) { if ( j != i && vArea[i] * vArea[j] > EPS_SMALL) { IntersCurveCurve ccInt( *m_vpCrvs[i], *m_vpCrvs[j]) ; int nRes = ccInt.GetRegionCurveClassification() ; if ( nRes == CCREGC_NULL || nRes == CCREGC_SAME || nRes == CCREGC_INTERS) return false ; if ( ( bRefCCW && nRes == CCREGC_IN1) || ( ! bRefCCW && nRes == CCREGC_IN2)) { // se è interna ad una curva con orientamento principale, verifico sia esterna ad almeno una curva con orientamento // diverso dal principale bool bFound = false ; for ( int k = 0 ; k < ( int)vArea.size() ; k++) { if ( k != i && vArea[i] * vArea[k] < EPS_SMALL) { IntersCurveCurve ccInt( *m_vpCrvs[i], *m_vpCrvs[k]) ; int nRes = ccInt.GetRegionCurveClassification() ; if ( ( bRefCCW && nRes == CCREGC_OUT) || ( ! bRefCCW && nRes == CCREGC_IN1)) { bFound = true ; break ; } } } if ( ! bFound) return false ; } } } } // se curva con orientamento diverso dal principale verifico sia contenuta in una curva con orientamento principale else if ( vArea[i] * dRefArea < - EPS_SMALL) { bool bInside = false ; for ( int j = 0 ; j < ( int)vArea.size() ; j++) { if ( j != i && vArea[j] * dRefArea > EPS_SMALL) { IntersCurveCurve ccInt( *m_vpCrvs[i], *m_vpCrvs[j]) ; int nRes = ccInt.GetRegionCurveClassification() ; if ( ( bRefCCW && nRes == CCREGC_IN1) || ( ! bRefCCW && nRes == CCREGC_OUT)) { bInside = true ; break ; } } } if ( ! bInside) return false ; } } return true ; } //---------------------------------------------------------------------------- bool Voronoi::AdjustOpenOffsetCurve( ICurveComposite& pCompo, double dOffs) { int nSideRef = dOffs < EPS_SMALL ? MDS_LEFT : MDS_RIGHT ; // recupero i raccordi relativi agli estremi della curva aperta INTVECTOR vJunctions ; for ( int i = 0 ; i < pCompo.GetCurveCount() ; i++) { double dParTmp ; pCompo.GetCurveTempParam( i, dParTmp) ; if ( dParTmp > EPS_SMALL) vJunctions.push_back( i) ; } if ( vJunctions.size() == 0) { // verifico se si trova dal lato corretto int nSide = GetOffsetCurveSide( pCompo, 0) ; if ( nSide != nSideRef) // se lato errato, tutta la curva va cancellata pCompo.Clear() ; } else if ( vJunctions.size() == 2) { // recupero i due tratti di curva PtrOwner pCompo1( ConvertCurveToBasicComposite( pCompo.CopyParamRange( vJunctions[0] + 1, vJunctions[1]))) ; PtrOwner pCompo2( ConvertCurveToBasicComposite( pCompo.CopyParamRange( vJunctions[1] + 1, vJunctions[0]))) ; pCompo.Clear() ; if ( ! IsNull( pCompo1) && pCompo1->IsValid()) { int nSide = GetOffsetCurveSide( *pCompo1, 0) ; if ( nSide == nSideRef) pCompo.CopyFrom( pCompo1) ; else if ( ! IsNull( pCompo2) && pCompo2->IsValid()) pCompo.CopyFrom( pCompo2) ; } else if ( ! IsNull( pCompo2) && pCompo2->IsValid()) { int nSide = GetOffsetCurveSide( *pCompo2, 0) ; if ( nSide == nSideRef) pCompo.CopyFrom( pCompo2) ; } } else { // mi posiziono dopo la junction pCompo.ChangeStartPoint( vJunctions[0] + 1) ; delete( pCompo.RemoveFirstOrLastCurve()) ; // verifico validità della curva int nSide = GetOffsetCurveSide( pCompo, 0) ; if ( nSide == nSideRef) { // scorro fino alla prima curva non valida ed elimino la curva da quel punto fino alla fine for ( int i = 1 ; i < pCompo.GetCurveCount() ; i++) { int nSide = GetOffsetCurveSide( pCompo, i) ; if ( nSide != nSideRef) pCompo.TrimEndAtParam( i) ; } } else { // elimino finchè non trovo una curva valida while( nSide != nSideRef && pCompo.IsValid()) { delete( pCompo.RemoveFirstOrLastCurve( false)) ; if ( pCompo.IsValid()) nSide = GetOffsetCurveSide( pCompo, 0) ; } } } return true ; } //--------------------------------------------------------------------------- int Voronoi::GetOffsetCurveSide( const ICurveComposite& pOffs, int nCrv) { Point3d ptM ; pOffs.GetCurve( nCrv)->GetMidPoint(ptM) ; int nOrigCrv ; pOffs.GetCurveTempProp( nCrv, nOrigCrv) ; const ICurve* pCrvRef = m_vpCrvs[0] ; // se ha una sottocurva di riferimento if ( nOrigCrv != 0 && m_vpCrvs[0]->GetType() == CRV_COMPO) { const CurveComposite* pCompoOrig = GetBasicCurveComposite( m_vpCrvs[0]) ; if ( pCompoOrig != nullptr) pCrvRef = pCompoOrig->GetCurve( nOrigCrv - 1) ; } DistPointCurve distPC( ptM, *pCrvRef) ; int nSide = MDS_ON ; distPC.GetSideAtMinDistPoint( 0, Z_AX, nSide) ; return nSide ; } //--------------------------------------------------------------------------- bool Voronoi::AdjustOffsetStart( ICurveComposite& pCrv) { for ( int i = 0 ; i < pCrv.GetCurveCount() ; i++) { // cerco il tratto associato alla prima curva originale int nProp ; pCrv.GetCurveTempProp( i, nProp) ; if ( nProp == 1) { pCrv.ChangeStartPoint( i) ; break ; } // oppure un raccordo di junction double dParam ; pCrv.GetCurveTempParam( i, dParam) ; if ( abs( dParam - 1) < EPS_SMALL) { pCrv.ChangeStartPoint( i + 1) ; break ; } } return true ; } //--------------------------------------------------------------------------- bool Voronoi::Translate( const Vector3d & vtMove) { if ( ! IsValid()) return false ; return m_Frame.Translate( vtMove) ; } //--------------------------------------------------------------------------- bool Voronoi::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dAngDeg) { if ( ! IsValid()) return false ; return m_Frame.Rotate( ptAx, vtAx, dAngDeg) ; } //--------------------------------------------------------------------------- bool Voronoi::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, double dSinAng) { if ( ! IsValid()) return false ; return m_Frame.Rotate( ptAx, vtAx, dCosAng, dSinAng) ; } //--------------------------------------------------------------------------- bool Voronoi::ToGlob( const Frame3d& frRef) { if ( ! IsValid()) return false ; return m_Frame.ToGlob( frRef) ; } //--------------------------------------------------------------------------- bool Voronoi::ToLoc( const Frame3d& frRef) { if ( ! IsValid()) return false ; return m_Frame.ToLoc( frRef) ; } //--------------------------------------------------------------------------- bool Voronoi::LocToLoc( const Frame3d& frOri, const Frame3d& frDest) { if ( ! IsValid()) return false ; return m_Frame.LocToLoc( frOri, frDest) ; }