//---------------------------------------------------------------------------- // EgalTech 2025 //---------------------------------------------------------------------------- // File : IntersCurvePlane.cpp Data : 07.11.25 Versione : 2.7k1 // Contenuto : Implementazione della classe intersezione curva-piano. // // // // Modifiche : 07.11.25 DB Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "GeoConst.h" #include "CurveLine.h" #include "CurveComposite.h" #include "IntersLineLine.h" #include "IntersLineArc.h" #include "IntersArcArc.h" #include "IntersCrvCompoCrvCompo.h" #include "/EgtDev/Include/EGkIntersCurves.h" #include "/EgtDev/Include/EGkIntersLinePlane.h" #include "/EgtDev/Include/EGkIntersCurvePlane.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EGkPlane3d.h" #include "/EgtDev/Include/EgtPointerOwner.h" #include using namespace std ; //---------------------------------------------------------------------------- IntersCurvePlane::IntersCurvePlane( const ICurve& Curve, const Point3d& ptOrig, const Vector3d& vtN) { // Le intersezioni sono calcolate nel piano XY locale. // Il flag bAreSegments vale solo per intersezione tra due linee e riguarda entrambe. // inizializzazioni m_nIntersCount = 0 ; m_pCurve = &Curve ; m_plPlane.Set( ptOrig, vtN) ; // puntatore alla curva usata nei calcoli (originali o temporanee) const ICurve* pCalcCrv ; // per eventuale esplosione temporanea delle curve PtrOwner pTmpCrv ; // se curva è arco da approssimare oppure è curva di Bezier if ( m_pCurve->GetType() == CRV_ARC || m_pCurve->GetType() == CRV_BEZIER || m_pCurve->GetType() == CRV_COMPO) { // approssimo con rette PolyLine PL ; if ( ! m_pCurve->ApproxWithLines( EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL)) return ; pTmpCrv.Set( CreateBasicCurveComposite()) ; if ( IsNull( pTmpCrv)) return ; if ( ! GetBasicCurveComposite( pTmpCrv)->FromPolyLine( PL)) return ; pCalcCrv = pTmpCrv ; } else pCalcCrv = m_pCurve ; m_Info.clear() ; if ( pCalcCrv->GetType() == CRV_LINE) { CalcIntersLinePlane( m_plPlane, *pCalcCrv) ; } else if ( pCalcCrv->GetType() == CRV_COMPO){ for ( int i = 0 ; i < GetBasicCurveComposite( pCalcCrv)->GetCurveCount(); ++i) { const ICurve& subCurve = *GetBasicCurveComposite( pCalcCrv)->GetCurve( i) ; CalcIntersLinePlane( m_plPlane, subCurve, i) ; } OrderAndCompleteIntersections() ; } // per curve approssimate, sistemo... AdjustIntersParams( pCalcCrv != m_pCurve) ; } //---------------------------------------------------------------------------- bool IntersCurvePlane::CalcIntersLinePlane( const Plane3d& plPlane, const ICurve& Curve, int nCrv) { if ( Curve.GetType() != CRV_LINE) return false ; Point3d ptStart ; Curve.GetStartPoint( ptStart) ; Point3d ptEnd ; Curve.GetEndPoint( ptEnd) ; Point3d ptInt ; double dLen = 0 ; Curve.GetLength( dLen) ; int nIntersType = IntersLinePlane( ptStart, ptEnd, m_plPlane, ptInt, true) ; // intersezione con attraversamento if ( nIntersType == ILPT_YES) { IntCrvPlnInfo icpi ; icpi.Ici[0].ptI = ptInt ; icpi.Ici[0].dU = Dist( ptInt, ptStart) / dLen + nCrv ; Vector3d vtPos = ptStart - m_plPlane.GetPoint() ; icpi.Ici[0].nPrevTy = vtPos * m_plPlane.GetVersN() > 0 ? ICPT_OUT : ICPT_IN ; icpi.Ici[0].nNextTy = icpi.Ici[0].nPrevTy == ICPT_IN ? ICPT_OUT : ICPT_IN ; m_Info.push_back( icpi) ; } // intersezione con tocco else if ( nIntersType == ILPT_START || nIntersType == ILPT_END) { IntCrvPlnInfo icpi ; icpi.Ici[0].ptI = ptInt ; icpi.Ici[0].dU = nIntersType == ILPT_START ? 0 : 1 + nCrv ; if ( nIntersType == ILPT_START) { Vector3d vtPos = ptEnd - m_plPlane.GetPoint() ; icpi.Ici[0].nNextTy = vtPos * m_plPlane.GetVersN() > 0 ? ICPT_OUT : ICPT_IN ; icpi.Ici[0].nPrevTy = ICPT_NULL ; } else { Vector3d vtPos = ptStart - m_plPlane.GetPoint() ; icpi.Ici[0].nPrevTy = vtPos * m_plPlane.GetVersN() > 0 ? ICPT_OUT : ICPT_IN ; icpi.Ici[0].nNextTy = ICPT_NULL ; } m_Info.push_back( icpi) ; } // intersezione con sovrapposizione else if ( nIntersType == ILPT_INPLANE) { IntCrvPlnInfo icpi ; icpi.bOverlap = true ; icpi.Ici[0].ptI = ptStart ; icpi.Ici[0].dU = 0 + nCrv; icpi.Ici[1].ptI = ptEnd ; icpi.Ici[1].dU = 1 + nCrv ; icpi.Ici[0].nPrevTy = ICPT_NULL ; icpi.Ici[0].nNextTy = ICPT_ON ; icpi.Ici[1].nPrevTy = ICPT_ON ; icpi.Ici[1].nNextTy = ICPT_NULL ; m_Info.push_back( icpi) ; } return true ; } //---------------------------------------------------------------------------- void IntersCurvePlane::OrderAndCompleteIntersections() { if ( m_Info.size() < 2) return ; // cancello le interesezioni puntuali adiacenti a tratti di sovrapposizione // riempio le info PrevTy e NexyTy sort( m_Info.begin(), m_Info.end(), []( IntCrvPlnInfo& icpA, IntCrvPlnInfo& icpB) { return icpA.Ici[0].dU < icpA.Ici[0].dU ;}) ; for ( int curr = m_Info.size() - 1 ; curr > - 1 ; --curr) { int prev = curr == 0 ? m_Info.size() - 1 : curr - 1 ; int next = curr == m_Info.size() - 1 ? 0 : curr + 1 ; bool bErasedCurr = false ; // solo le intersezioni di sovrapposizione o puntuali sullo start o end delle curve possono avere il PrevTy o NextTy non definito if ( ! m_Info[curr].bOverlap) { if ( m_Info[curr].Ici[0].nPrevTy == ICPT_NULL) { if ( ! m_Info[prev].bOverlap) { m_Info[curr].Ici[0].nPrevTy = m_Info[prev].Ici[0].nNextTy ; // se ho due puntuali che coincidono cancello il successivo tra i due ( corrente) if ( AreSamePointApprox( m_Info[curr].Ici[0].ptI, m_Info[prev].Ici[0].ptI)) { m_Info.erase(m_Info.begin() + curr) ; bErasedCurr = true ; } } // se ho un'intersezione puntuale che in realtà è la fine di un tratto di sovrapposizione, la cancello else { m_Info[prev].Ici[1].nNextTy = m_Info[curr].Ici[0].nNextTy ; m_Info.erase(m_Info.begin() + curr) ; bErasedCurr = true ; } } if ( ! bErasedCurr && m_Info[curr].Ici[0].nNextTy == ICPT_NULL){ if ( ! m_Info[prev].bOverlap) m_Info[curr].Ici[0].nNextTy = m_Info[next].Ici[0].nPrevTy ; // se ho un'intersezione puntuale che in realtà è la fine di un tratto di sovrapposizione, la cancello else { m_Info[next].Ici[0].nPrevTy = m_Info[curr].Ici[0].nPrevTy ; m_Info.erase(m_Info.begin() + curr) ; } } } else { if ( m_Info[curr].Ici[0].nPrevTy == ICPT_NULL) { if ( ! m_Info[prev].bOverlap) m_Info[curr].Ici[0].nPrevTy = m_Info[prev].Ici[0].nNextTy ; else m_Info[curr].Ici[0].nPrevTy = m_Info[prev].Ici[1].nNextTy ; } if ( m_Info[curr].Ici[1].nNextTy == ICPT_NULL) { if ( ! m_Info[next].bOverlap) m_Info[curr].Ici[0].nNextTy = m_Info[prev].Ici[0].nPrevTy ; else m_Info[curr].Ici[0].nNextTy = m_Info[prev].Ici[1].nPrevTy ; } } } m_nIntersCount = m_Info.size() ; } //---------------------------------------------------------------------------- bool IntersCurvePlane::IsArcToApprox( const ICurve& Curve) { // recupero l'arco const CurveArc* pArc = GetBasicCurveArc( &Curve) ; if ( pArc == nullptr) return false ; // verifico se non è nel piano XY o ha più di un giro al centro return ( ( ! pArc->GetNormVersor().IsZplus() && ! pArc->GetNormVersor().IsZminus()) || abs( pArc->GetAngCenter()) > ANG_FULL + EPS_ANG_ZERO) ; } //---------------------------------------------------------------------------- bool IntersCurvePlane::AdjustIntersParams( bool bAdjCrv) { // se non ci sono intersezioni, non va fatto alcunché if ( m_Info.empty()) return true ; // se le curve originali non sono state approssimate, non va fatto alcunché if ( ! bAdjCrv) return true ; // procedo ad aggiustare for ( auto& aInfo : m_Info) { // se curve originali approssimate, devo ricalcolare i parametri dei punti di intersezione if ( bAdjCrv) { if ( ! m_pCurve->GetParamAtPoint( aInfo.Ici[0].ptI, aInfo.Ici[0].dU, 10 * EPS_SMALL)) return false ; if ( aInfo.bOverlap && ! m_pCurve->GetParamAtPoint( aInfo.Ici[1].ptI, aInfo.Ici[1].dU, 10 * EPS_SMALL)) return false ; } } return true ; } //---------------------------------------------------------------------------- int IntersCurvePlane::GetIntersCount( void) { return m_nIntersCount ; } //---------------------------------------------------------------------------- bool IntersCurvePlane::GetIntersPointNearTo( const Point3d& ptNear, Point3d& ptI, double& dParam) { if ( m_nIntersCount == 0) return false ; // ricerca del punto più vicino tra le intersezioni singole bool bFound = false ; double dMinSqDist = SQ_INFINITO ; for ( int i = 0 ; i < m_nIntersCount ; ++ i) { // se è un'intersezione singola if ( ! m_Info[i].bOverlap) { // faccio la verifica sul punto Point3d ptP = m_Info[i].Ici[0].ptI ; double dSqDist = SqDist( ptNear, ptP) ; if ( dSqDist < dMinSqDist) { dMinSqDist = dSqDist ; ptI = ptP ; dParam = m_Info[i].Ici[0].dU ; bFound = true ; } } // altrimenti else { // recupero il tratto di sovrapposizione double dUStartTrim, dUEndTrim ; dUStartTrim = m_Info[i].Ici[0].dU ; dUEndTrim = m_Info[i].Ici[1].dU ; PtrOwner pCrv( m_pCurve->CopyParamRange( dUStartTrim, dUEndTrim)) ; if ( IsNull( pCrv)) continue ; // cerco il punto int nFlag ; Point3d ptP ; if ( DistPointCurve( ptNear, *pCrv).GetMinDistPoint( 0.5, ptP, nFlag)) { // faccio la verifica double dSqDist = SqDist( ptNear, ptP) ; if ( dSqDist < dMinSqDist) { dMinSqDist = dSqDist ; ptI = ptP ; m_pCurve->GetParamAtPoint( ptP, dParam) ; bFound = true ; } } } } return bFound ; } //---------------------------------------------------------------------------- bool IntersCurvePlane::GetCurveClassification( double dLenMin, CRVPLNCVECTOR& ccClass) { // pulisco vettore classificazioni ccClass.clear() ; // verifico definizione della curva if ( m_pCurve == nullptr) return false ; // se esiste almeno una intersezione if ( m_nIntersCount >= 1) return CalcCurveClassification( m_pCurve, m_Info, dLenMin, ccClass) ; // altrimenti la curva è completamente interna oppure completamente esterna else return CalcCurveInOrOut( m_pCurve, ccClass) ; } //---------------------------------------------------------------------------- bool IntersCurvePlane::CalcCurveClassification( const ICurve* pCurve, const ICPIVECTOR& Info, double dLenMin, CRVPLNCVECTOR& ccClass) { // numero intersezioni int nNumInters = int( Info.size()) ; if ( nNumInters < 1) return false ; // recupero il dominio parametrico della curva in esame double dStartPar, dEndPar ; if ( pCurve == nullptr || ! pCurve->GetDomain( dStartPar, dEndPar)) return false ; // limito lunghezza minima dLenMin = max( dLenMin, EPS_ZERO) ; // elimino intersezioni senza attraversamento che giacciono in intervalli di sovrapposizione ICPIVECTOR InfoCorr ; InfoCorr.reserve( Info.size()) ; for ( size_t i = 0 ; i < Info.size() ; ++ i) { // se intersezione puntuale senza attraversamento if ( ! Info[i].bOverlap && Info[i].Ici[0].nPrevTy == Info[i].Ici[0].nNextTy) { // confronto con le intersezioni con sovrapposizione bool bToSkip = false ; for ( size_t j = 0 ; j < Info.size() ; ++ j) { // se coincide o puntuale if ( j == i || ! Info[j].bOverlap) continue ; // determino l'intervallo parametrico tenendo conto di eventuale avvolgimento attorno all'inizio double dU1 = Info[j].Ici[0].dU ; double dU2 = Info[j].Ici[1].dU ; if ( dU2 < dU1 && pCurve->IsClosed()) dU2 += dEndPar ; // se cade nell'intervallo è da saltare if ( Info[i].Ici[0].dU >= dU1 && Info[i].Ici[0].dU <= dU2) { bToSkip = true ; break ; } } if ( bToSkip) continue ; } // salvo dati intersezione InfoCorr.emplace_back( Info[i]) ; } // aggiorno numero di intersezioni da considerare nNumInters = int( InfoCorr.size()) ; // recupero la classificazione all'inizio della curva int nLastTy = ICCT_NULL ; double dCurrPar = dStartPar ; double dCurrLen = 0 ; double dEndLen ; pCurve->GetLength( dEndLen) ; // se è chiusa, recupero come finisce if ( pCurve->IsClosed()) { if ( ! InfoCorr[nNumInters-1].bOverlap) nLastTy = InfoCorr[nNumInters-1].Ici[0].nNextTy ; else { nLastTy = InfoCorr[nNumInters-1].Ici[1].nNextTy ; // se attraversa il punto di giunzione (parametro di fine minore di quello di inizio) if ( InfoCorr[nNumInters-1].Ici[1].dU < InfoCorr[nNumInters-1].Ici[0].dU) { dCurrPar = InfoCorr[nNumInters-1].Ici[1].dU ; double dTmpLen ; pCurve->GetLengthAtParam( dCurrPar, dTmpLen) ; dCurrLen = dTmpLen - dEndLen ; dEndPar = dCurrPar ; dEndLen = dTmpLen ; } } } // costruisco il vettore delle classificazioni for ( int i = 0 ; i < nNumInters ; ++ i) { // se è definito un tratto precedente double dLenU ; pCurve->GetLengthAtParam( InfoCorr[i].Ici[0].dU, dLenU) ; if ( InfoCorr[i].Ici[0].dU > dCurrPar + EPS_PARAM && dLenU - dCurrLen > dLenMin) { // verifico che la definizione sul tratto sia omogenea e valida int nPrevTy = InfoCorr[i].Ici[0].nPrevTy ; if ( ( nLastTy != ICCT_NULL && nPrevTy != nLastTy) || nPrevTy == ICCT_NULL || nPrevTy == ICCT_ON) return false ; // assegno i dati CrvPlaneClass segClass ; segClass.dParS = dCurrPar ; segClass.dParE = InfoCorr[i].Ici[0].dU ; segClass.nClass = (( nPrevTy == ICCT_IN) ? CRVC_IN : CRVC_OUT) ; ccClass.push_back( segClass) ; // salvo dati correnti dCurrPar = InfoCorr[i].Ici[0].dU ; dCurrLen = dLenU ; nLastTy = InfoCorr[i].Ici[0].nNextTy ; } // altrimenti, salvo il tipo else nLastTy = InfoCorr[i].Ici[0].nNextTy ; // se è definito un tratto in sovrapposizione if ( InfoCorr[i].bOverlap) { // assegno i dati CrvPlaneClass segClass ; segClass.dParS = dCurrPar ; segClass.dParE = InfoCorr[i].Ici[1].dU ; segClass.nClass = CRVPLN_ON ; ccClass.push_back( segClass) ; // salvo dati correnti dCurrPar = InfoCorr[i].Ici[1].dU ; dCurrLen = dLenU ; nLastTy = InfoCorr[i].Ici[1].nNextTy ; } } // eventuale tratto finale rimasto if ( dCurrPar < dEndPar - EPS_PARAM && dEndLen - dCurrLen > dLenMin) { // verifico che la definizione sul tratto sia valida if ( nLastTy == ICCT_NULL || nLastTy == ICCT_ON) return false ; // assegno i dati CrvPlaneClass segClass ; segClass.dParS = dCurrPar ; segClass.dParE = dEndPar ; segClass.nClass = (( nLastTy == ICCT_IN) ? CRVC_IN : CRVC_OUT) ; ccClass.push_back( segClass) ; } return true ; } //---------------------------------------------------------------------------- bool IntersCurvePlane::CalcCurveInOrOut( const ICurve* pCurve, CRVPLNCVECTOR& ccClass) { // controllo di non avere intersezioni int nNumInters = int( m_Info.size()) ; if ( nNumInters > 0) return false ; // se non ho intersezioni tra curva e piano devo solo capire da che parte del piano sta la curva CrvPlaneClass cpClass ; double dStartPar, dEndPar ; if ( pCurve == nullptr || ! pCurve->GetDomain( dStartPar, dEndPar)) return false ; Point3d ptStart ; pCurve->GetStartPoint( ptStart) ; Vector3d vtCrv = ptStart - m_plPlane.GetPoint() ; CrvPlaneClass segClass ; segClass.dParS = dStartPar ; segClass.dParE = dEndPar ; segClass.nClass = ( (vtCrv * m_plPlane.GetVersN() < 0) ? CRVC_IN : CRVC_OUT) ; ccClass.push_back( segClass) ; return true ; }