//---------------------------------------------------------------------------- // EgalTech 2013-2013 //---------------------------------------------------------------------------- // File : PolyLine.cpp Data : 22.12.13 Versione : 1.4l3 // Contenuto : Implementazione della classe PolyLine. // // // // Modifiche : 22.12.13 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "CurveLine.h" #include "IntersLineLine.h" #include "PolygonPlane.h" #include "PointsPCA.h" #include "GeoConst.h" #include "CurveComposite.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EGkPolyLine.h" #include "/EgtDev/Include/EGkPlane3d.h" #include "/EgtDev/Include/EGkDistPointLine.h" #include "/EgtDev/Include/EGnStringUtils.h" #include "/EgtDev/Include/EgtNumUtils.h" using namespace std ; //---------------------------------------------------------------------------- PolyLine::PolyLine( void) : m_nRejected( 0), m_nTempProp{0,0}, m_iter( m_lUPoints.end()) { } //---------------------------------------------------------------------------- PolyLine::~PolyLine( void) { } //---------------------------------------------------------------------------- bool PolyLine::Clear( void) { m_nRejected = 0 ; m_lUPoints.clear() ; m_iter = m_lUPoints.end() ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::AddUPoint( double dPar, const Point3d& ptP, bool bEndOrStart) { // se da aggiungere in coda if ( bEndOrStart) { // se il punto è uguale all'ultimo (ignoro parametro), non lo inserisco ma ok if ( ! m_lUPoints.empty() && AreSamePointApprox( ptP, m_lUPoints.back().first)) { ++ m_nRejected ; return true ; } // eseguo inserimento try { m_lUPoints.emplace_back( ptP, dPar) ; } catch (...) { return false ; } } // altrimenti si aggiunge in testa else { // se il punto è uguale al primo (ignoro parametro), non lo inserisco ma ok if ( ! m_lUPoints.empty() && AreSamePointApprox( ptP, m_lUPoints.front().first)) { ++ m_nRejected ; return true ; } // eseguo inserimento try { m_lUPoints.emplace_front( ptP, dPar) ; } catch (...) { return false ; } } return true ; } //---------------------------------------------------------------------------- bool PolyLine::Close( void) { // ci devono essere almeno 2 punti if ( m_lUPoints.size() < 2) return false ; // verifico non sia già chiuso if ( AreSamePointApprox( m_lUPoints.front().first, m_lUPoints.back().first)) return false ; // aggiungo un punto uguale al primo in coda return AddUPoint( m_lUPoints.front().second, m_lUPoints.front().first) ; } //---------------------------------------------------------------------------- bool PolyLine::ModifyLastParam( double dPar) { // verifico esistano dei punti if ( m_lUPoints.empty()) return false ; // eseguo la modifica m_lUPoints.back().second = dPar ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::EraseFirstUPoint( void) { if ( m_lUPoints.empty()) return false ; m_lUPoints.pop_front() ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::EraseLastUPoint( void) { if ( m_lUPoints.empty()) return false ; m_lUPoints.pop_back() ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::AddOffsetToU( double dOffset) { for ( auto iter = m_lUPoints.begin() ; iter != m_lUPoints.end() ; ++ iter) iter->second += dOffset ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::Translate( const Vector3d& vtMove) { for ( auto iter = m_lUPoints.begin() ; iter != m_lUPoints.end() ; ++ iter) iter->first.Translate( vtMove) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, double dSinAng) { for ( auto iter = m_lUPoints.begin() ; iter != m_lUPoints.end() ; ++ iter) iter->first.Rotate( ptAx, vtAx, dCosAng, dSinAng) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double dCoeffZ) { for ( auto iter = m_lUPoints.begin() ; iter != m_lUPoints.end() ; ++ iter) iter->first.Scale( frRef, dCoeffX, dCoeffY, dCoeffZ) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::Mirror( const Point3d& ptOn, const Vector3d& vtNorm) { for ( auto iter = m_lUPoints.begin() ; iter != m_lUPoints.end() ; ++ iter) iter->first.Mirror( ptOn, vtNorm) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::Shear( const Point3d& ptOn, const Vector3d& vtNorm, const Vector3d& vtDir, double dCoeff) { for ( auto iter = m_lUPoints.begin() ; iter != m_lUPoints.end() ; ++ iter) iter->first.Shear( ptOn, vtNorm, vtDir, dCoeff) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::ToGlob( const Frame3d& frRef) { for ( auto iter = m_lUPoints.begin() ; iter != m_lUPoints.end() ; ++ iter) iter->first.ToGlob( frRef) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::ToLoc( const Frame3d& frRef) { for ( auto iter = m_lUPoints.begin() ; iter != m_lUPoints.end() ; ++ iter) iter->first.ToLoc( frRef) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::LocToLoc( const Frame3d& frOri, const Frame3d& frDest) { // se i due riferimenti coincidono, non devo fare alcunché if ( AreSameFrame( frOri, frDest)) return true ; // ciclo sui punti for ( auto iter = m_lUPoints.begin() ; iter != m_lUPoints.end() ; ++ iter) iter->first.LocToLoc( frOri, frDest) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::Join( PolyLine& PL, double dOffsetPar) { // se l'altra polilinea non contiene alcunchè, esco con ok if ( PL.m_lUPoints.empty()) return true ; // verifico che l'ultimo punto di questa polilinea coincida con il primo dell'altra if ( ! m_lUPoints.empty() && ! AreSamePointApprox( m_lUPoints.back().first, PL.m_lUPoints.front().first)) return false ; // cancello l'ultimo di questa EraseLastUPoint() ; // aggiungo eventuale offset all'altra if ( abs( dOffsetPar) > EPS_PARAM) PL.AddOffsetToU( dOffsetPar) ; // sposto i punti dall'altra polilinea a questa m_lUPoints.splice( m_lUPoints.end(), PL.m_lUPoints) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::Split( double dU, PolyLine& PL) { // pulisco la polilinea destinazione PL.Clear() ; // ricerca del punto in cui dividere auto iter = m_lUPoints.cbegin() ; while ( iter != m_lUPoints.end() && iter->second < ( dU + EPS_PARAM)) ++ iter ; if ( iter == m_lUPoints.end()) return false ; // sposto i punti nell'altra polilinea PL.m_lUPoints.splice( PL.m_lUPoints.end(), m_lUPoints, iter, m_lUPoints.end()) ; // prepongo l'ultimo punto rimasto PL.m_lUPoints.push_front( m_lUPoints.back()) ; // annullo l'iteratore corrente m_iter = m_lUPoints.end() ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetLocalBBox( BBox3d& b3Loc) const { // assegno il box in locale, scorrendo tutti i punti b3Loc.Reset() ; for ( const auto& UPnt : m_lUPoints) b3Loc.Add( UPnt.first) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::IsClosed( void) const { if ( m_lUPoints.size() < 3) return false ; return ( AreSamePointApprox( m_lUPoints.front().first, m_lUPoints.back().first)) ; } //---------------------------------------------------------------------------- bool PolyLine::GetFirstUPoint( double* pdPar, Point3d* pptP, bool bNotLast) const { m_iter = m_lUPoints.begin() ; if ( m_iter == m_lUPoints.end()) return false ; if ( bNotLast && m_iter == -- ( m_lUPoints.end())) { m_iter = m_lUPoints.end() ; return false ; } if ( pdPar != nullptr) *pdPar = m_iter->second ; if ( pptP != nullptr) *pptP = m_iter->first ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetNextUPoint( double* pdPar, Point3d* pptP, bool bNotLast) const { if ( m_iter == m_lUPoints.end()) return false ; ++ m_iter ; if ( m_iter == m_lUPoints.end()) return false ; if ( bNotLast && m_iter == -- ( m_lUPoints.end())) { m_iter = m_lUPoints.end() ; return false ; } if ( pdPar != nullptr) *pdPar = m_iter->second ; if ( pptP != nullptr) *pptP = m_iter->first ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetLastUPoint( double* pdPar, Point3d* pptP, bool bNotFirst) const { m_iter = m_lUPoints.end() ; if ( m_iter == m_lUPoints.begin()) return false ; -- m_iter ; if ( m_iter == m_lUPoints.end()) return false ; if ( bNotFirst && m_iter == m_lUPoints.begin()) { m_iter = m_lUPoints.end() ; return false ; } if ( pdPar != nullptr) *pdPar = m_iter->second ; if ( pptP != nullptr) *pptP = m_iter->first ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetPrevUPoint( double* pdPar, Point3d* pptP, bool bNotFirst) const { if ( m_iter == m_lUPoints.begin()) return false ; -- m_iter ; if ( m_iter == m_lUPoints.end()) return false ; if ( bNotFirst && m_iter == m_lUPoints.begin()) { m_iter = m_lUPoints.end() ; return false ; } if ( pdPar != nullptr) *pdPar = m_iter->second ; if ( pptP != nullptr) *pptP = m_iter->first ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetCurrUPoint( double* pdPar, Point3d* pptP) const { // verifico validità punto corrente if ( m_iter == m_lUPoints.end()) return false ; if ( pdPar != nullptr) *pdPar = m_iter->second ; if ( pptP != nullptr) *pptP = m_iter->first ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetFirstULine( double* pdIni, Point3d* pptIni, double* pdFin, Point3d* pptFin) const { // parametro e punto iniziali m_iter = m_lUPoints.begin() ; if ( m_iter == m_lUPoints.end()) return false ; if ( pdIni != nullptr) *pdIni = m_iter->second ; if ( pptIni != nullptr) *pptIni = m_iter->first ; // parametro e punto finali ++ m_iter ; if ( m_iter == m_lUPoints.end()) return false ; if ( pdFin != nullptr) *pdFin = m_iter->second ; if ( pptFin != nullptr) *pptFin = m_iter->first ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetNextULine( double* pdIni, Point3d* pptIni, double* pdFin, Point3d* pptFin) const { // parametro e punto iniziali (è il precedente finale) if ( m_iter == m_lUPoints.end()) return false ; if ( pdIni != nullptr) *pdIni = m_iter->second ; if ( pptIni != nullptr) *pptIni = m_iter->first ; // parametro e punto finali ++ m_iter ; if ( m_iter == m_lUPoints.end()) return false ; if ( pdFin != nullptr) *pdFin = m_iter->second ; if ( pptFin != nullptr) *pptFin = m_iter->first ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetLastULine( double* pdIni, Point3d* pptIni, double* pdFin, Point3d* pptFin) const { // parametro e punto finali m_iter = m_lUPoints.end() ; if ( m_iter == m_lUPoints.begin()) return false ; -- m_iter ; if ( m_iter == m_lUPoints.end()) return false ; if ( pdFin != nullptr) *pdFin = m_iter->second ; if ( pptFin != nullptr) *pptFin = m_iter->first ; // parametro e punto iniziali if ( m_iter == m_lUPoints.begin()) return false ; -- m_iter ; if ( pdIni != nullptr) *pdIni = m_iter->second ; if ( pptIni != nullptr) *pptIni = m_iter->first ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetPrevULine( double* pdIni, Point3d* pptIni, double* pdFin, Point3d* pptFin) const { // parametro e punto finali if ( m_iter == m_lUPoints.end()) return false ; if ( pdFin != nullptr) *pdFin = m_iter->second ; if ( pptFin != nullptr) *pptFin = m_iter->first ; // parametro e punto iniziali if ( m_iter == m_lUPoints.begin()) return false ; -- m_iter ; if ( pdIni != nullptr) *pdIni = m_iter->second ; if ( pptIni != nullptr) *pptIni = m_iter->first ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::IsFlat( int& nRank, Point3d& ptCen, Vector3d& vtDir, double dToler) const { // cerco le componenti principali (tramite PCA) Point3d ptP1, ptP2 ; PointsPCA ptsPCA ; for ( bool bFound = GetFirstLine( ptP1, ptP2) ; bFound ; bFound = GetNextLine( ptP1, ptP2)) { double dLen = Dist( ptP1, ptP2) ; ptsPCA.AddPoint( Media( ptP1, ptP2, 0.25), dLen / 2) ; ptsPCA.AddPoint( Media( ptP1, ptP2, 0.75), dLen / 2) ; } // recupero il rango, ovvero la dimensionalità dell'insieme di punti nRank = ptsPCA.GetRank() ; // se dimensione nulla, o non ci sono punti o sono tutti praticamente coincidenti if ( nRank == 0) return ptsPCA.GetCenter( ptCen) ; // se dimensione 1, allora i punti sono distribuiti su una linea if ( nRank == 1) { // assegno il centro e la direzione della linea (il verso è indifferente) ptsPCA.GetCenter( ptCen) ; ptsPCA.GetPrincipalComponent( 0, vtDir) ; return true ; } // altrimenti dimensione 2 o 3, allora è determinato un piano principale, verifico se tutti i punti vi giacciono // Center and normal vector ptsPCA.GetCenter( ptCen) ; Vector3d vtX, vtY ; ptsPCA.GetPrincipalComponent( 0, vtX) ; ptsPCA.GetPrincipalComponent( 1, vtY) ; vtDir = vtX ^ vtY ; if ( ! vtDir.Normalize()) { // riduco la dimensionalità a lineare nRank = 1 ; // assegno il centro e la direzione della linea (il verso è indifferente) ptsPCA.GetCenter( ptCen) ; vtDir = vtX ; return true ; } if ( vtDir.z < 0) vtDir.Invert() ; // Plane calculation Plane3d plPlane ; plPlane.Set( ptCen, vtDir) ; // Test each vertex to see if it is farther from plane than allowed max distance Point3d ptP ; for ( bool bFound = GetFirstPoint( ptP) ; bFound ; bFound = GetNextPoint( ptP)) { double dDist = ( ( ptP - ORIG) * plPlane.GetVersN()) - plPlane.GetDist() ; if ( abs( dDist) > dToler) return false ; } return true ; } //---------------------------------------------------------------------------- bool PolyLine::IsFlat( Plane3d& plPlane, double dToler) const { // verifico non sia vuota if ( GetPointNbr() == 0) { plPlane.Reset() ; return false ; } // recupero dati sulla planarità della polilinea int nRank ; Point3d ptCen ; Vector3d vtDir ; bool bFlat = IsFlat( nRank, ptCen, vtDir, dToler) ; // imposto il piano a seconda della dimensionalità switch ( nRank) { case 0 : // punto plPlane.Set( ptCen, Z_AX) ; case 1 : // linea plPlane.Set( ptCen, FromUprightOrtho( vtDir)) ; break ; default : // piana o 3d plPlane.Set( ptCen, vtDir) ; break ; } return bFlat ; } //---------------------------------------------------------------------------- bool PolyLine::IsClosedAndFlat( Plane3d& plPlane, double& dArea, double dToler) const { // Test if closed if ( ! IsClosed()) return false ; Point3d ptP ; // Calcolo il centro (per minimizzare gli errori nelle successive operazioni) Point3d ptCen = ORIG ; int nCount = 0 ; for ( bool bFound = GetFirstPoint( ptP) ; bFound ; bFound = GetNextPoint( ptP)) { ptCen += ptP ; ++ nCount ; } ptCen /= nCount ; Vector3d vtMove = ptCen - ORIG ; // Compute a representative plane for the polygon (faccio il calcolo nel centro e poi traslo al contrario il piano) PolygonPlane PolyPlane ; for ( bool bFound = GetFirstPoint( ptP) ; bFound ; bFound = GetNextPoint( ptP)) PolyPlane.AddPoint( ptP - vtMove) ; if ( ! PolyPlane.GetPlane( plPlane) || ! PolyPlane.GetArea( dArea)) { dArea = 0 ; return IsFlat( plPlane, dToler) ; } plPlane.Translate( vtMove) ; // Sistemo il piano per l'offset utilizzato // Test each vertex to see if it is farther from plane than allowed max distance for ( bool bFound = GetFirstPoint( ptP) ; bFound ; bFound = GetNextPoint( ptP)) { double dDist = DistPointPlane( ptP, plPlane) ; if ( abs( dDist) > dToler) return false ; } // All points passed distance test, so polygon is considered planar return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetApproxLength( double& dLen) const { // calcolo la lunghezza approssimata come somma di ogni singolo tratto lineare dLen = 0 ; Point3d ptIni, ptFin ; for ( bool bFound = GetFirstLine( ptIni, ptFin) ; bFound ; bFound = GetNextLine( ptIni, ptFin)) { dLen += ApproxDist( ptIni, ptFin) ; } return ( dLen > EPS_ZERO) ; } //---------------------------------------------------------------------------- bool PolyLine::GetLength( double& dLen) const { // calcolo la lunghezza come somma di ogni singolo tratto lineare dLen = 0 ; Point3d ptIni, ptFin ; for ( bool bFound = GetFirstLine( ptIni, ptFin) ; bFound ; bFound = GetNextLine( ptIni, ptFin)) { dLen += Dist( ptIni, ptFin) ; } return ( dLen > EPS_ZERO) ; } //---------------------------------------------------------------------------- bool PolyLine::GetAreaXY( double& dArea) const { // verifico sia chiusa if ( ! IsClosed()) return false ; // calcolo l'area considerando solo XY (è la Z di Newell) dArea = 0 ; Point3d ptIni, ptFin ; for ( bool bFound = GetFirstLine( ptIni, ptFin) ; bFound ; bFound = GetNextLine( ptIni, ptFin)) { dArea += ( ptIni.x - ptFin.x) * ( ptIni.y + ptFin.y) ; // projection on xy } // considero anche la linea tra l'ultimo e il primo punto perchè in alcuni casi potrebbero definire area // significativa anche se sono coincidenti per le nostre tolleranze ptIni = ptFin ; GetFirstPoint( ptFin) ; if ( ! AreSamePointExact( ptIni, ptFin)) dArea += ( ptIni.x - ptFin.x) * ( ptIni.y + ptFin.y) ; dArea = 0.5 * dArea ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetMaxDistanceFromLine( const Point3d& ptLine, const Vector3d& vtLine, double dLen, double& dMaxDist, bool bIsSegment) const { // Verifico che la polilinea esista if ( GetPointNbr() < 1) return false ; // Calcolo la distanza di ogni punto dalla linea dMaxDist = 0 ; Point3d ptP ; for ( bool bFound = GetFirstPoint( ptP) ; bFound ; bFound = GetNextPoint( ptP)) { DistPointLine dstPL( ptP, ptLine, vtLine, dLen, bIsSegment) ; double dDist ; if ( dstPL.GetDist( dDist) && dDist > dMaxDist) dMaxDist = dDist ; } return true ; } //---------------------------------------------------------------------------- bool PolyLine::AdjustForMaxSegmentLen( double dMaxLen) { auto iter = m_lUPoints.begin() ; // se non ci sono punti, esco subito if ( iter == m_lUPoints.end()) return false ; // imposto il primo punto come precedente double dUprec = iter->second ; Point3d ptPprec = iter->first ; // passo al secondo ++ iter ; // ciclo sui punti try { while ( iter != m_lUPoints.end()) { // recupero dati correnti double dUcurr = iter->second ; Point3d ptPcurr = iter->first ; // verifico lunghezza segmento dal precedente double dSqLen = SqDist( ptPprec, ptPcurr) ; if ( dSqLen > dMaxLen * dMaxLen) { double dLen = sqrt( dSqLen) ; // determino il numero di divisioni int nStep = int( dLen / dMaxLen + 0.999) ; // inserisco i punti necessari for ( int i = 1 ; i < nStep ; ++ i) { double dCoeff = double( i) / nStep ; m_lUPoints.insert( iter, POINTU( Media( ptPprec, ptPcurr, dCoeff), (( 1 - dCoeff) * dUprec + dCoeff * dUcurr))) ; } } // passo al successivo dUprec = dUcurr ; ptPprec = ptPcurr ; ++ iter ; } } catch (...) { return false ; } return true ; } //----------------------------------------------------------------------------- static bool DouglasPeuckerSimplification( const PNTUVECTOR& vPtU, const double dSqTol, const int nIndStart, const int nIndEnd, INTVECTOR& vInd) { // se indici uguali, ritorno if ( nIndStart == nIndEnd) return true ; // distanza massima e indice del punto associato double dMaxSqDist = 0. ; int nMaxInd = 0 ; // scorro i punti intermedi tra nIndStart e nIndEnd for ( int i = nIndStart + 1 ; i < nIndEnd ; ++ i) { double dCurrSqDist = 0. ; // distanza tra il punto attuale e la retta tra i punti di indici nIndStart ed nIndEnd DistPointLine DPL( vPtU[i].first, vPtU[nIndStart].first, vPtU[nIndEnd].first) ; if ( DPL.GetSqDist( dCurrSqDist) && dCurrSqDist > dMaxSqDist) { // aggiorno i parametri dMaxSqDist = dCurrSqDist ; nMaxInd = i ; } } // se la distanza massima trovata è sopra la tolleranza, allora controllo la parte di PolyLine tra // (nIndStart, nMaxInd) e quella tra (nMaxInd, nIndEnd) if ( dMaxSqDist > dSqTol) { // inserisco il punto vInd.push_back( nMaxInd) ; // split if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, nIndStart, nMaxInd, vInd) || ! DouglasPeuckerSimplification( vPtU, dSqTol, nMaxInd, nIndEnd, vInd)) return false ; } return true ; } //---------------------------------------------------------------------------- bool PolyLine::RemoveAlignedPoints( double dToler, bool bStartEnd) { // se non ci sono almeno 3 punti, esco subito if ( m_lUPoints.size() < 3) return true ; // controllo minimo valore di tolleranza dToler = max( dToler, LIN_TOL_MIN) ; double dSqTol = dToler * dToler ; // vettore contenente i punti della polyline PNTUVECTOR vPtU ; vPtU.reserve( m_lUPoints.size()) ; for ( const auto& ptCurr : m_lUPoints) vPtU.emplace_back( ptCurr) ; // vettore indici dei punti rimanenti INTVECTOR vInd ; vInd.reserve( vPtU.size()) ; // se aperta if ( ! IsClosed()) { // considero tutti i punti della PolyLine vInd.push_back( 0) ; if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, 0, int( vPtU.size()) - 1, vInd)) return false ; vInd.push_back( int( vPtU.size()) - 1) ; } // altrimenti chiusa else { // cerco il punto più distante dal primo double dMaxDist = 0. ; int nMaxInd = 0 ; for ( int i = 1 ; i < int( vPtU.size()) ; ++ i) { double dCurrDist = Dist( vPtU[0].first, vPtU[i].first) ; if ( dCurrDist > dMaxDist) { dMaxDist = dCurrDist ; nMaxInd = i ; } } // recupero due PolyLine di approssimazione vInd.push_back( 0) ; if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, 0, nMaxInd, vInd)) return false ; vInd.push_back( nMaxInd) ; if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, nMaxInd, int( vPtU.size()) - 1, vInd)) return false ; vInd.push_back( int( vPtU.size()) - 1) ; } // ordino in senso crescente sort( vInd.begin(), vInd.end()) ; // se richiesto e chiusa e almeno 4 punti rimasti, controllo allineamento dell'inizio con precedente e successivo rimasti if ( bStartEnd && IsClosed() && vInd.size() >= 4) { if ( DistPointLine( vPtU[vInd[0]].first, vPtU[vInd[1]].first, vPtU[vInd[int(vInd.size())-2]].first).IsEpsilon( dToler)) { vInd.erase( vInd.begin()) ; vInd.back() = vInd.front() ; } } // rimetto in lista i soli punti rimasti m_lUPoints.clear() ; for ( auto Ind : vInd) m_lUPoints.push_back( vPtU[Ind]) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::ApproxOnSide( const Vector3d& vtN, bool bLeftSide, double dToler) { // eseguo una prima approssimazione if ( ! MyApproxOnSide( vtN, bLeftSide, dToler)) return false ; // se polilinea aperta, finito if ( ! IsClosed()) return true ; // *** polilinea chiusa : devo sistemare a cavallo dell'inizio/fine *** // sposto l'inizio dalla parte opposta MyChangeStart( int( m_lUPoints.size() / 2)) ; // rifaccio l'approssimazione con 3/4 della tolleranza ammessa if ( ! MyApproxOnSide( vtN, bLeftSide, 0.75 * dToler)) return false ; // risposto l'inizio dalla parte opposta, quindi lo porto vicino all'originale MyChangeStart( int( m_lUPoints.size() / 2)) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::MyChangeStart( int nPos) { // solo per polilinee chiuse if ( ! IsClosed()) return false ; // cancello l'ultimo punto ( coincide con il primo) m_lUPoints.pop_back() ; // sposto la parte iniziale dei punti alla fine m_lUPoints.splice( m_lUPoints.end(), m_lUPoints, m_lUPoints.begin(), next( m_lUPoints.begin(), nPos)) ; // aggiungo il punto finale come copia dell'iniziale m_lUPoints.push_back( m_lUPoints.front()) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::MyApproxOnSide( const Vector3d& vtN, bool bLeftSide, double dToler) { // se non ci sono almeno 3 punti, esco subito if ( m_lUPoints.size() < 3) return true ; // coefficienti deduzione tolleranza const double COEFF_TOL = 0.90 ; // punto precedente auto precP = m_lUPoints.begin() ; // punto corrente auto currP = next( precP) ; // punto successivo auto nextP = next( currP) ; // assegno la tolleranza corrente double dCurrToler = dToler ; // mentre esiste un successivo while ( nextP != m_lUPoints.end()) { // --- si verifica se possibile eliminare il punto corrente rimanendo dal lato voluto e in tolleranza --- // distanza del punto corrente dal segmento che unisce gli adiacenti DistPointLine dPL( currP->first, precP->first, nextP->first) ; double dSqDist = SQ_INFINITO ; dPL.GetSqDist( dSqDist) ; // se punti allineati if ( dSqDist < SQ_EPS_SMALL) { // ripristino la tolleranza corrente dCurrToler = dToler ; // elimino il corrente m_lUPoints.erase( currP) ; // avanzo con corrente e successivo currP = nextP ; ++ nextP ; continue ; } // se lato sinistro e gira a sinistra o lato destro e gira a destra double dCrossXY = ( ( currP->first - precP->first) ^ ( nextP->first - currP->first)) * vtN ; if ( ( bLeftSide && dCrossXY > 0) || ( ! bLeftSide && dCrossXY < 0)) { // se eliminabile if ( dSqDist < dCurrToler * dCurrToler) { // diminuisco la tolleranza corrente dell'errore attuale dCurrToler -= COEFF_TOL * sqrt( dSqDist) ; // elimino il punto corrente m_lUPoints.erase( currP) ; // avanzo con corrente e successivo currP = nextP ; ++ nextP ; continue ; } } // --- si verifica se possibile elimare segmento da corrente a successivo rimanendo in tolleranza e dal lato voluto --- // punto successivo del successivo auto nex2P = next( nextP) ; if ( nex2P != m_lUPoints.end()) { double dCros2XY = ( ( nextP->first - currP->first) ^ ( nex2P->first - nextP->first)) * vtN ; // se lato sinistro e 2 giri a destra o lato destro e 2 giri a sinistra if ( ( bLeftSide && dCrossXY < 0 && dCros2XY < 0) || ( ! bLeftSide && dCrossXY > 0 && dCros2XY > 0)) { // calcolo del punto di intersezione tra i segmenti precP-currP e nextP-next2P, allungati al centro CurveLine Line1, Line2 ; if ( Line1.Set( precP->first, currP->first) && Line2.Set( nextP->first, nex2P->first)) { // se la normale non coincide con l'asse Z, devo portare le linee nel riferimento OCS di questa Frame3d frNorm ; if ( ! vtN.IsZplus() && ! vtN.IsZminus()) frNorm.Set( ORIG, vtN) ; Line1.ToLoc( frNorm) ; Line2.ToLoc( frNorm) ; IntersLineLine IntLL( Line1, Line2, false) ; IntCrvCrvInfo IntInfo ; if ( IntLL.GetIntCrvCrvInfo( IntInfo) && ! IntInfo.bOverlap && IntInfo.IciA[0].dU > 1 && IntInfo.IciB[0].dU < 0) { Point3d ptInt = 0.5 * ( IntInfo.IciA[0].ptI + IntInfo.IciB[0].ptI) ; ptInt.ToGlob( frNorm) ; // verifico che distanza dell'intersezione dal segmento currP-nextP sia inferiore a tolleranza corrente DistPointLine dIL( ptInt, currP->first, nextP->first) ; double dSqDist2 = SQ_INFINITO ; dIL.GetSqDist( dSqDist2) ; // se eliminabile if ( dSqDist2 < dCurrToler * dCurrToler) { // diminuisco la tolleranza corrente dell'errore attuale dCurrToler -= COEFF_TOL * sqrt( dSqDist2) ; // sposto il successivo sull'intersezione e ne aggiorno il parametro nextP->first = ptInt ; nextP->second = 0.5 * ( currP->second + nextP->second) ; // elimino il punto corrente m_lUPoints.erase( currP) ; // avanzo con corrente e successivo currP = nextP ; nextP = nex2P ; continue ; } } } } } // non è stato eliminato alcunché // ripristino la tolleranza corrente dCurrToler = dToler ; // avanzo il terzetto di uno step precP = currP ; currP = nextP ; ++ nextP ; } return true ; } //---------------------------------------------------------------------------- bool PolyLine::MakeConvex( const Vector3d& vtN, bool bLeftSide) { // eseguo una prima modifica if ( ! MyMakeConvex( vtN, bLeftSide)) return false ; // se polilinea aperta, finito if ( ! IsClosed()) return true ; // *** polilinea chiusa : devo sistemare a cavallo dell'inizio/fine *** // sposto l'inizio dalla parte opposta MyChangeStart( int( m_lUPoints.size() / 2)) ; // rifaccio la modifica if ( ! MyMakeConvex( vtN, bLeftSide)) return false ; // risposto l'inizio dalla parte opposta, quindi lo porto vicino all'originale MyChangeStart( int( m_lUPoints.size() / 2)) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::MyMakeConvex( const Vector3d& vtN, bool bLeftSide) { // ciclo i controlli finchè non ci sono rimozioni bool bRemoved = true ; while ( bRemoved) { bRemoved = false ; // se non ci sono almeno 3 punti, esco subito if ( m_lUPoints.size() < 3) return true ; // punto precedente auto precP = m_lUPoints.begin() ; // punto corrente auto currP = next( precP) ; // punto successivo auto nextP = next( currP) ; // mentre esiste un successivo while ( nextP != m_lUPoints.end()) { // --- si verifica se possibile eliminare il punto corrente rimanendo dal lato voluto --- // se lato sinistro e gira a sinistra o lato destro e gira a destra double dCrossXY = ( ( currP->first - precP->first) ^ ( nextP->first - currP->first)) * vtN ; if ( ( bLeftSide && dCrossXY > - EPS_ZERO) || ( ! bLeftSide && dCrossXY < EPS_ZERO)) { // elimino il punto corrente m_lUPoints.erase( currP) ; // avanzo con corrente e successivo currP = nextP ; ++ nextP ; // dichiaro rimozione bRemoved = true ; continue ; } // non è stato eliminato alcunché : avanzo il terzetto di uno step precP = currP ; currP = nextP ; ++ nextP ; } // se rimasti due punti coincidenti, elimino il secondo if ( m_lUPoints.size() == 2 && AreSamePointApprox( m_lUPoints.front().first, m_lUPoints.back().first)) { m_lUPoints.pop_back() ; return false ; } } return true ; } //---------------------------------------------------------------------------- bool PolyLine::Invert( bool bInvertU) { // verifico non sia vuota if ( m_lUPoints.empty()) return true ; // inverto la lista m_lUPoints.reverse() ; // se richiesto, inverto anche il parametro U if ( bInvertU) { // recupero il primo valore di U che è il vecchio finale ed è il riferimento di inversione double dUfin = m_lUPoints.front().second ; // ciclo su tutti gli elementi for ( auto& UPoint : m_lUPoints) { UPoint.second = dUfin - UPoint.second ; } } return true ; } //---------------------------------------------------------------------------- bool PolyLine::MyRemoveSamePoints( double dToler) { // elimino i punti consecutivi diventati coincidenti (almeno 2) if ( m_lUPoints.size() < 2) return true ; // punto precedente auto precP = m_lUPoints.begin() ; // punto corrente auto currP = next( precP) ; // mentre esiste un corrente while ( currP != m_lUPoints.end()) { // se coincidono if ( AreSamePointEpsilon( precP->first, currP->first, dToler)) { // elimino il punto corrente currP = m_lUPoints.erase( currP) ; // il precedente rimane inalterato } // altrimenti da tenere else { // avanzo la coppia di uno step precP = currP ; ++ currP ; } } return true ; } //---------------------------------------------------------------------------- bool PolyLine::Flatten( double dZ) { // verifico non sia vuota if ( m_lUPoints.empty()) return true ; // ciclo su tutti gli elementi per portarli alla Z indicata for ( auto& UPoint : m_lUPoints) UPoint.first.z = dZ ; // elimino i punti consecutivi diventati coincidenti MyRemoveSamePoints() ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::FlattenInAutoPlane( double dToler) { // verifico non sia vuota if ( m_lUPoints.empty()) return true ; // recupero dati sulla planarità della polilinea int nRank ; Point3d ptCen ; Vector3d vtDir ; bool bFlat = IsFlat( nRank, ptCen, vtDir, dToler) ; // se non planare entro la tolleranza, esco if ( ! bFlat) return false ; // se punto o linea, non devo fare alcunché if ( nRank == 0 || nRank == 1) return true ; // assegno il piano medio Plane3d plPlane ; plPlane.Set( ptCen, vtDir) ; // proietto i punti su questo piano for ( auto& UPoint : m_lUPoints) UPoint.first = ProjectPointOnPlane( UPoint.first, plPlane) ; // elimino i punti consecutivi diventati coincidenti MyRemoveSamePoints() ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetConvexHullXY( PNTVECTOR& vConvHull) const { int nSize = int( m_lUPoints.size()) ; if ( nSize == 0) return false ; // inserisco i punti in un array ( considero solo x e y, annullo le z) PNTVECTOR vPnt( nSize) ; int k = 0 ; for ( const auto& Pnt : m_lUPoints) vPnt[k++] = Point3d( Pnt.first.x, Pnt.first.y, 0) ; // ordino secondo le X crescenti sort( vPnt.begin(), vPnt.end(), []( const Point3d& a, const Point3d& b) { return ( a.x < b.x) ; }) ; // elimino eventuali punti coincidenti for ( int i = 0 ; i < nSize ; ++ i) { for ( int j = i + 1 ; j < nSize ; ++ j) { if ( ( vPnt[j].x - vPnt[i].x) > EPS_SMALL) break ; else if ( AreSamePointXYApprox( vPnt[i], vPnt[j])) { vPnt.erase( vPnt.begin() + j) ; -- nSize ; } } } // applico l'algoritmo di Andrew int j = 0 ; vConvHull.resize( 2 * nSize) ; // costruisco la parte inferiore for ( int i = 0 ; i < nSize ; ++ i) { while ( j >= 2 && CrossXY( vConvHull[j-1] - vConvHull[j-2], vPnt[i] - vConvHull[j-2]) <= 0) -- j ; vConvHull[j++] = vPnt[i] ; } // costruisco la parte superiore for ( int i = nSize - 2, t = j + 1 ; i >= 0 ; -- i) { while ( j >= t && CrossXY( vConvHull[j-1] - vConvHull[j-2], vPnt[i] - vConvHull[j-2]) <= 0) -- j ; vConvHull[j++] = vPnt[i] ; } vConvHull.resize( j - 1) ; return true ; } //---------------------------------------------------------------------------- bool PolyLine::GetMinAreaRectangleXY( Point3d& ptCen, Vector3d& vtAx, double& dLen, double& dHeight) const { // Convex Hull PNTVECTOR vConvHull ; if ( ! GetConvexHullXY( vConvHull)) return false ; // all points in ConvexHull are different, minimum a segment int nCount = int( vConvHull.size()) ; if ( nCount < 2) return false ; // Starting edge nCount-1 -> 0 int l = 0, m = 0, n = 0 ; double dMinArea = SQ_INFINITO ; { // Edge indexes int i = 0 ; int j = nCount - 1 ; // Edge versor Vector3d vtE0 = vConvHull[i] - vConvHull[j] ; vtE0.Normalize() ; // Edge perpendicular versor Vector3d vtE1 = Vector3d( -vtE0.y, vtE0.x, 0) ; // Loop through all points to get maximum extents double dMin0 = INFINITO, dMax0 = - INFINITO, dMin1 = 0, dMax1 = - INFINITO ; for ( int k = 0 ; k < nCount ; ++ k) { // Project points onto axes vtE0 and vtE1 and keep track // of minimum and maximum values along both axes Vector3d vtDiff = vConvHull[k] - vConvHull[j] ; double dSca = ScalarXY( vtDiff, vtE0) ; if ( dSca < dMin0) { dMin0 = dSca ; l = k ; } if ( dSca > dMax0) { dMax0 = dSca ; m = k ; } dSca = ScalarXY( vtDiff, vtE1) ; if ( dSca > dMax1) { dMax1 = dSca ; n = k ; } } // Remember area, center and axes dMinArea = ( dMax0 - dMin0) * ( dMax1 - dMin1) ; ptCen = vConvHull[j] + 0.5 * (( dMin0 + dMax0) * vtE0 + ( dMin1 + dMax1) * vtE1) ; vtAx = vtE0 ; dLen = dMax0 - dMin0 ; dHeight = dMax1 - dMin1 ; } // Loop through all other edges (j trails i by 1) for ( int i = 1, j = 0 ; i < nCount ; j = i, ++ i) { // Get current edge, normalized Vector3d vtE0 = vConvHull[i] - vConvHull[j] ; vtE0.Normalize() ; // Get an axis vtE1 orthogonal to edge vtE0 Vector3d vtE1 = Vector3d( -vtE0.y, vtE0.x, 0) ; // Find new min on vtE0 double dMin0 = ScalarXY( ( vConvHull[l] - vConvHull[j]), vtE0) ; for ( int k = 0 ; k < nCount ; ++ k) { int lnext = ( l + 1) % nCount ; double dMin0next = ScalarXY( (vConvHull[lnext] - vConvHull[j]), vtE0) ; if ( dMin0next < dMin0) { dMin0 = dMin0next ; l = lnext ; } else break ; } // Find new max on vtE0 double dMax0 = ScalarXY( ( vConvHull[m] - vConvHull[j]), vtE0) ; for ( int k = 0 ; k < nCount ; ++ k) { int mnext = ( m + 1) % nCount ; double dMax0next = ScalarXY( (vConvHull[mnext] - vConvHull[j]), vtE0) ; if ( dMax0next > dMax0) { dMax0 = dMax0next ; m = mnext ; } else break ; } // Find new min on vtE1 double dMin1 = 0 ; // Find new max on vtE1 double dMax1 = ScalarXY( ( vConvHull[n] - vConvHull[j]), vtE1) ; for ( int k = 0 ; k < nCount ; ++ k) { int nnext = ( n + 1) % nCount ; double dMax1next = ScalarXY( (vConvHull[nnext] - vConvHull[j]), vtE1) ; if ( dMax1next > dMax1) { dMax1 = dMax1next ; n = nnext ; } else break ; } double dArea = ( dMax0 - dMin0) * ( dMax1 - dMin1) ; // If best so far, remember area, center, and axes if ( dArea < dMinArea) { dMinArea = dArea ; ptCen = vConvHull[j] + 0.5 * (( dMin0 + dMax0) * vtE0 + ( dMin1 + dMax1) * vtE1) ; vtAx = vtE0 ; dLen = dMax0 - dMin0 ; dHeight = dMax1 - dMin1 ; } } // Axis aligned with max dimension if ( dHeight > dLen) { vtAx.Rotate( Z_AX, 0, 1) ; swap( dLen, dHeight) ; } return true ; } //---------------------------------------------------------------------------- bool PolyLine::Trim( const Plane3d& plPlane, bool bInVsOut) { // se vuota non faccio alcunché if ( m_lUPoints.empty()) return false ; // determino le intersezioni dei lati con il piano int nIntPos = - 1 ; auto prevUP = m_lUPoints.begin() ; double dPrevDist = ( prevUP->first - ORIG) * plPlane.GetVersN() - plPlane.GetDist() ; auto currUP = next( prevUP) ; while ( currUP != m_lUPoints.end()) { // se non inizializzato e precedente in zona vietata, ne salvo l'indice if ( nIntPos == -1 && ( ( bInVsOut && dPrevDist > EPS_SMALL) || ( ! bInVsOut && dPrevDist < - EPS_SMALL))) nIntPos = int( distance( m_lUPoints.begin(), prevUP)) ; // distanze del punto dal piano double dCurrDist = ( currUP->first - ORIG) * plPlane.GetVersN() - plPlane.GetDist() ; // se il lato attraversa il piano, inserisco il punto di intersezione nel poligono if ( ( dPrevDist > EPS_SMALL && dCurrDist < - EPS_SMALL) || ( dPrevDist < - EPS_SMALL && dCurrDist > EPS_SMALL)) { double dCoeff = abs( dPrevDist) / ( abs( dPrevDist) + abs( dCurrDist)) ; Point3d ptInt = Media( prevUP->first, currUP->first, dCoeff) ; double dU = ( 1 - dCoeff) * prevUP->second + dCoeff * currUP->second ; m_lUPoints.insert( currUP, { ptInt, dU}) ; } // passo al successivo prevUP = currUP ; currUP = next( currUP) ; dPrevDist = dCurrDist ; } // se chiusa, sposto l'inizio su eventuale primo punto di intersezione con il piano if ( IsClosed() && nIntPos != -1) MyChangeStart( nIntPos) ; // elimino i punti che non rispettano la posizione rispetto al piano currUP = m_lUPoints.begin() ; while ( currUP != m_lUPoints.end()) { double dDist = ( currUP->first - ORIG) * plPlane.GetVersN() - plPlane.GetDist() ; if ( ( bInVsOut && dDist > EPS_SMALL) || ( ! bInVsOut && dDist < - EPS_SMALL)) { currUP = m_lUPoints.erase( currUP) ; } else currUP = next( currUP) ; } return true ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool DistPointPolyLine( const Point3d& ptP, const PolyLine& plPoly, double& dDist) { double dDummy ; return DistPointPolyLine( ptP, plPoly, dDist, dDummy) ; } //---------------------------------------------------------------------------- bool DistPointPolyLine( const Point3d& ptP, const PolyLine& plPoly, double& dDist, double& dMinDistPar) { // La polilinea deve contenere almeno due punti if ( plPoly.GetPointNbr() < 2) return false ; // Ciclo sui punti della polilinea dDist = INFINITO ; int nSeg = -1 ; Point3d ptStart, ptEnd ; plPoly.GetFirstPoint( ptStart) ; while ( plPoly.GetNextPoint( ptEnd)) { ++ nSeg ; // distanza del punto dal segmento della polilinea DistPointLine PointLineDistCalc( ptP, ptStart, ptEnd) ; double dPlDist ; PointLineDistCalc.GetDist( dPlDist) ; if ( dPlDist < dDist) { dDist = dPlDist ; PointLineDistCalc.GetParamAtMinDistPoint( dMinDistPar) ; dMinDistPar += nSeg ; } // assegno nuovo inizio ptStart = ptEnd ; } return true ; } //---------------------------------------------------------------------------- bool IsPointInsidePolyLine( const Point3d& ptP, const PolyLine& plPoly, double dToler) { // La polilinea deve essere chiusa e piatta, altrimenti non ha senso parlare di interno Plane3d plPlane ; double dArea ; if ( ! plPoly.IsClosedAndFlat( plPlane, dArea, 10 * EPS_SMALL)) return false ; // Il punto deve giacere nel piano della polilinea if ( ! PointInPlaneEpsilon( ptP, plPlane, 10 * EPS_SMALL)) return false ; // Riferimento alla lista dei punti PNTULIST& List = const_cast( plPoly).GetUPointList() ; // Ciclo sui segmenti della polilinea per cercare il segmento più vicino al punto double dMinSqDist = SQ_INFINITO ; Point3d ptMinDist ; auto itMinDistEnd = List.end() ; auto itStart = List.begin() ; auto itEnd = next( itStart) ; for ( ; itEnd != List.end() ; ++ itStart, ++ itEnd) { // Distanza del punto dal segmento corrente DistPointLine dDistCalc( ptP, itStart->first, itEnd->first) ; double dSqDist ; if ( dDistCalc.GetSqDist( dSqDist) && dSqDist < dMinSqDist) { dMinSqDist = dSqDist ; dDistCalc.GetMinDistPoint( ptMinDist) ; itMinDistEnd = itEnd ; } } // Determino tangente di riferimento Vector3d vtTang ; // se minima distanza nell'estremo iniziale del segmento if ( AreSamePointApprox( ptMinDist, prev( itMinDistEnd)->first)) { // direzione del segmento Vector3d vtCurrTg = itMinDistEnd->first - prev( itMinDistEnd)->first ; vtCurrTg.Normalize() ; // direzione del segmento precedente Vector3d vtPrevTg ; if ( prev( itMinDistEnd) != List.begin()) vtPrevTg = prev( itMinDistEnd)->first - prev( itMinDistEnd, 2)->first ; else vtPrevTg = prev( itMinDistEnd)->first - next( List.rbegin())->first ; vtPrevTg.Normalize() ; // tangente media vtTang = vtPrevTg + vtCurrTg ; vtTang.Normalize() ; } // se altrimenti minima distanza nell'estremo finale del segmento else if ( AreSamePointApprox( ptMinDist, itMinDistEnd->first)) { // direzione del segmento Vector3d vtCurrTg = itMinDistEnd->first - prev( itMinDistEnd)->first ; vtCurrTg.Normalize() ; // direzione del segmento successivo Vector3d vtNextTg ; if ( next( itMinDistEnd) != List.end()) vtNextTg = next( itMinDistEnd)->first - itMinDistEnd->first ; else vtNextTg = next( List.begin())->first - itMinDistEnd->first ; vtNextTg.Normalize() ; // tangente media vtTang = vtCurrTg + vtNextTg ; vtTang.Normalize() ; } // altrimenti minima distanza con l'interno else { vtTang = itMinDistEnd->first - prev( itMinDistEnd)->first ; } // Determino la posizione del punto Vector3d vtDiff = ptP - ptMinDist ; Vector3d vtOut = vtTang ^ plPlane.GetVersN() ; vtOut.Normalize() ; return ( vtDiff * vtOut < -dToler) ; } //---------------------------------------------------------------------------- bool GetPointParamOnPolyLine( const Point3d& ptP, const PolyLine& plPoly, double dToler, double& dPar) { // La polilinea deve contenere almeno due punti if ( plPoly.GetPointNbr() < 2) return false ; // Ciclo sui punti della polilinea int nSeg = -1 ; double dMinSqDist = SQ_INFINITO ; dPar = -1 ; Point3d ptStart, ptEnd ; plPoly.GetFirstPoint( ptStart) ; while ( plPoly.GetNextPoint( ptEnd)) { // aggiorno l'indice ++ nSeg ; // distanza del punto dal segmento della polilinea DistPointLine dDistCalc( ptP, ptStart, ptEnd) ; double dSqDist ; if ( dDistCalc.GetSqDist( dSqDist) && dSqDist < dMinSqDist) { dMinSqDist = dSqDist ; double dSegPar ; dDistCalc.GetParamAtMinDistPoint( dSegPar) ; dPar = nSeg + dSegPar ; } // assegno nuovo inizio ptStart = ptEnd ; } // Il punto è sulla linea se la sua distanza rispetta la tolleranza return ( dMinSqDist < dToler * dToler) ; } //---------------------------------------------------------------------------- bool ChangePolyLineStart( PolyLine& plPoly, const Point3d& ptNewStart, double dToler) { // La polilinea deve essere chiusa if ( ! plPoly.IsClosed()) return false ; // Riferimento alla lista dei punti PNTULIST& LoopList = const_cast( plPoly).GetUPointList() ; // Se il punto inziale è già quello, non faccio nulla if ( AreSamePointEpsilon( LoopList.begin()->first, ptNewStart, dToler)) return true ; // Ciclo sui segmenti della polilinea per cercare il segmento più vicino al punto double dMinSqDist = SQ_INFINITO ; auto itMinDistEnd = LoopList.end() ; auto itStart = LoopList.begin() ; auto itEnd = next( itStart) ; for ( ; itEnd != LoopList.end() ; ++ itStart, ++ itEnd) { // Distanza del punto dal segmento corrente DistPointLine dDistCalc( ptNewStart, itStart->first, itEnd->first) ; double dSqDist ; if ( dDistCalc.GetSqDist( dSqDist) && dSqDist < dMinSqDist) { dMinSqDist = dSqDist ; itMinDistEnd = itEnd ; } } // Se il punto non sta sulla polilinea, non ha senso cambiare l'inizio if ( dMinSqDist > dToler * dToler) return false ; // Se il punto non coincide con un vertice, lo aggiungo auto itNewStart = LoopList.end() ; if ( AreSamePointApprox( ptNewStart, prev( itMinDistEnd)->first)) itNewStart = prev( itMinDistEnd) ; else if ( AreSamePointApprox( ptNewStart, itMinDistEnd->first)) itNewStart = itMinDistEnd ; else itNewStart = LoopList.emplace( itMinDistEnd, ptNewStart, 0) ; // Cancello l'ultimo punto ( coincide con il primo) LoopList.pop_back() ; // Sposto la parte iniziale dei punti alla fine LoopList.splice( LoopList.end(), LoopList, LoopList.begin(), itNewStart) ; // Aggiungo il punto finale come copia dell'iniziale LoopList.push_back( LoopList.front()) ; return true ; } //---------------------------------------------------------------------------- bool SplitPolyLineAtPoint( const PolyLine& plPoly, const Point3d& ptP, double dToler, PolyLine& plPoly1, PolyLine& plPoly2) { // La polilinea deve contenere almeno due punti if ( plPoly.GetPointNbr() < 2) return false ; // Riferimento alla lista dei punti const PNTULIST& LoopList = const_cast( plPoly).GetUPointList() ; // Ciclo sui segmenti della polilinea per cercare il segmento più vicino al punto double dMinSqDist = SQ_INFINITO ; auto itMinDistEnd = LoopList.end() ; auto itStart = LoopList.begin() ; auto itEnd = next( itStart) ; for ( ; itEnd != LoopList.end() ; ++ itStart, ++ itEnd) { // Distanza del punto dal segmento corrente DistPointLine dDistCalc( ptP, itStart->first, itEnd->first) ; double dSqDist ; if ( dDistCalc.GetSqDist( dSqDist) && dSqDist < dMinSqDist) { dMinSqDist = dSqDist ; itMinDistEnd = itEnd ; } } // Se il punto non sta sulla polilinea, non ha senso spezzare if ( dMinSqDist > dToler * dToler) return false ; // Copio i punti opportuni nella prima parte PNTULIST& LoopList1 = plPoly1.GetUPointList() ; for ( auto it = LoopList.begin() ; it != itMinDistEnd ; ++ it) LoopList1.emplace_back( it->first, it->second) ; plPoly1.AddUPoint( 0, ptP) ; // Copio i punti opportuni nella seconda parte PNTULIST& LoopList2 = plPoly2.GetUPointList() ; for ( auto it = itMinDistEnd ; it != LoopList.end() ; ++ it) LoopList2.emplace_back( it->first, it->second) ; plPoly2.AddUPoint( 0, ptP, false) ; return true ; } //---------------------------------------------------------------------------- bool AssociatePolyLinesMinDistPoints( const PolyLine& PL1, const PolyLine& PL2, PNTIVECTOR& vPnt1, PNTIVECTOR& vPnt2, bool& bCommonInternalPoints) { // controllo che le polyline abbiano almeno un punto int nPnt1 = PL1.GetPointNbr() ; int nPnt2 = PL2.GetPointNbr() ; if ( nPnt1 == 0 || nPnt2 == 0) return false ; // indica la presenza di punti interni in comune tra le due polylines bCommonInternalPoints = false ; vPnt1.reserve( PL1.GetPointNbr()) ; Point3d ptP1 ; bool bF1 = PL1.GetFirstPoint( ptP1) ; while ( bF1) { vPnt1.emplace_back( ptP1, -1) ; bF1 = PL1.GetNextPoint( ptP1) ; } int nTotP1 = int( vPnt1.size()) ; vPnt2.reserve( PL2.GetPointNbr()) ; Point3d ptP2 ; bool bF2 = PL2.GetFirstPoint( ptP2) ; while ( bF2) { vPnt2.emplace_back( ptP2, -1) ; bF2 = PL2.GetNextPoint( ptP2) ; } int nTotP2 = int( vPnt2.size()) ; // calcoli per prima curva int nLastJ = 0 ; vPnt1[0].second = 0 ; for ( int i = 1 ; i < nTotP1 ; ++ i) { double dDist = INFINITO ; double dMinDistPar = nLastJ ; for ( int j = max( nLastJ, 1) ; j < nTotP2 ; ++ j) { // distanza del punto dal segmento della polilinea DistPointLine PointLineDistCalc( vPnt1[i].first, vPnt2[j-1].first, vPnt2[j].first) ; double dPlDist ; if ( PointLineDistCalc.GetDist( dPlDist) && dPlDist < dDist - EPS_SMALL) { dDist = dPlDist ; PointLineDistCalc.GetParamAtMinDistPoint( dMinDistPar) ; dMinDistPar += j - 1 ; } } int nMinJ = ( int)( dMinDistPar + 0.5) ; if ( nMinJ < nLastJ) nMinJ = nLastJ ; // verifica se è un punto interno in comune con l'altra polyline if ( i < nTotP1 - 1 && dDist < EPS_SMALL && abs( dMinDistPar - floor( dMinDistPar + 0.5)) < EPS_SMALL) bCommonInternalPoints = true ; vPnt1[i].second = nMinJ ; nLastJ = nMinJ ; } // calcoli per seconda curva int nLastI = 0 ; vPnt2[0].second = 0 ; for ( int j = 1 ; j < nTotP2 ; ++ j) { double dDist = INFINITO ; double dMinDistPar = nLastI ; for ( int i = max( nLastI, 1) ; i < nTotP1 ; ++ i) { // distanza del punto dal segmento della polilinea DistPointLine PointLineDistCalc( vPnt2[j].first, vPnt1[i-1].first, vPnt1[i].first) ; double dPlDist ; if ( PointLineDistCalc.GetDist( dPlDist) && dPlDist < dDist - EPS_SMALL) { dDist = dPlDist ; PointLineDistCalc.GetParamAtMinDistPoint( dMinDistPar) ; dMinDistPar += i - 1 ; } } int nMinI = ( int)( dMinDistPar + 0.5) ; if ( nMinI < nLastI) nMinI = nLastI ; if ( j < nTotP2 - 1 && dDist < EPS_SMALL && abs( dMinDistPar - floor( dMinDistPar + 0.5)) < EPS_SMALL) bCommonInternalPoints = true ; vPnt2[j].second = nMinI ; nLastI = nMinI ; } return true ; } //---------------------------------------------------------------------------- bool MatchPolyLinesAddingPoints( const PolyLine& PL1, const PolyLine& PL2, int nType, PNTIVECTOR& vPnt1, PNTIVECTOR& vPnt2) { // prima trovo le associazioni senza aggiunte di punti Point3d ptP2 ; CurveComposite cc1, cc2 ; cc1.FromPolyLine( PL1) ; cc2.FromPolyLine( PL2) ; int nPnt1 = PL1.GetPointNbr() ; int nPnt2 = PL2.GetPointNbr() ; vector vMatch2 ; PL2.GetFirstPoint( ptP2) ; while ( PL2.GetNextPoint( ptP2, true)) { DistPointCurve dpc( ptP2, cc1, false) ; int nFlag = 0 ; double dParam ; dpc.GetParamAtMinDistPoint( 0, dParam, nFlag) ; Point3d ptJoint ; dpc.GetMinDistPoint( 0, ptJoint, nFlag) ; vMatch2.emplace_back( ptJoint, dParam) ; } int nAtStart2 = 0 ; // match ripetuti dalla curva U0 allo start della curva U1 int nAtEnd2 = 0 ; Point3d ptP1 ; PL1.GetFirstPoint( ptP1) ; int c = 0 ; int nRep1 = 0 ; // match interni consecutivi uguali di punti della curva U0 con punti della curva U1 int nRep2 = 0 ; double dLastParamMatch = 0 ; Point3d ptLastPointMatch = ptP1 ; BOOLVECTOR vbRep1( nPnt1) ; INTVECTOR vnAddedOrNextIsRep1 ; // 0 non Rep, 1 Rep, 2 Next is Rep ; DBLVECTOR vdSplit1 ; DBLVECTOR vdMatch1 ; fill( vbRep1.begin(), vbRep1.end(), false) ; while ( PL1.GetNextPoint( ptP1, true)) { // devo salvarmi se matcho più punti con lo start o l'end della curva totale DistPointCurve dpc( ptP1, cc2, false) ; int nFlag = 0 ; double dParam ; dpc.GetParamAtMinDistPoint( 0, dParam, nFlag) ; Point3d ptJoint ; dpc.GetMinDistPoint( 0, ptJoint, nFlag) ; vdMatch1.push_back( dParam) ; if ( dParam < EPS_SMALL ) { ++nAtStart2 ; vbRep1[c] = true ; ++c ; continue ; } else if ( dParam > nPnt2 - EPS_SMALL ) { vbRep1[c] = nAtEnd2 == 0 ? false : true ; vbRep1.back() = true ; ++ nAtEnd2 ; ++c ; continue ; } if ( dParam <= dLastParamMatch || AreSamePointApprox( ptJoint, ptLastPointMatch)) { dParam = dLastParamMatch ; vbRep1[c] = true ; ++ nRep1 ; ++c ; continue ; } else { dLastParamMatch = dParam ; ptLastPointMatch = ptJoint ; // se sono già troppo vicino ad un split esistente allora non faccio nulla if ( abs(dParam - round( dParam)) < 100 * EPS_PARAM) { ++c ; continue ; } vdSplit1.push_back( dParam) ; // verifico se ho un match per questo punto // in tal caso vuol dire che sto creando una ripetizione nRep1 int nCase = 0 ; for ( int j = 0 ; j < int( vMatch2.size()) ; ++j) { if ( abs(vMatch2[j].second - (c + 1)) < EPS_SMALL) { // devo però verificare che non ci sia un match successivo, perché in quel caso non ho una ripetizione bool bFoundMatch = false ; for ( int z = int( vMatch2[j].second) ; z < int( vdMatch1.size()) ; ++z) { if ( abs( vdMatch1[z] - ( j + 1 )) < EPS_SMALL ) { bFoundMatch = true ; break ; } } if ( ! bFoundMatch) { ++ nRep2 ; // capisco se il punto è rep o se lo è il suo successivo if ( j + 1 < dParam) nCase = 1 ; else nCase = 2 ; } break ; } } vnAddedOrNextIsRep1.push_back( nCase) ; } ++c ; } int nAtStart1 = 0 ; int nAtEnd1 = 0 ; PL2.GetFirstPoint( ptP2) ; c = 0 ; dLastParamMatch = 0 ; ptLastPointMatch = ptP2 ; INTVECTOR vnAddedOrNextIsRep2 ; // 0 non Rep, 1 Rep, 2 Next is Rep ; DBLVECTOR vdSplit2 ; BOOLVECTOR vbRep2( nPnt2) ; fill( vbRep2.begin(), vbRep2.end(), false) ; while ( PL2.GetNextPoint( ptP2, true)) { DistPointCurve dpc( ptP2, cc1, false) ; int nFlag = 0 ; double dParam ; dpc.GetParamAtMinDistPoint( 0, dParam, nFlag) ; Point3d ptJoint ; dpc.GetMinDistPoint( 0, ptJoint, nFlag) ; if ( dParam < EPS_SMALL ) { ++nAtStart1 ; vbRep2[c] = true ; ++c ; continue ; } else if ( dParam > nPnt1 - EPS_SMALL ) { vbRep2[c] = ( nAtEnd1 == 0 ? false : true) ; vbRep2.back() = true ; ++ nAtEnd1 ; ++c ; continue ; } if ( dParam <= dLastParamMatch || AreSamePointApprox( ptJoint, ptLastPointMatch)) { dParam = dLastParamMatch ; vbRep2[c] = true ; ++ nRep2 ; ++c ; continue ; } else { dLastParamMatch = dParam ; ptLastPointMatch = ptJoint ; // se sono troppo vicino ad uno split esistente allora non faccio nulla if ( abs( dParam - round( dParam)) < 100 * EPS_PARAM) { ++c ; continue ; } vdSplit2.push_back( dParam) ; // verifico se ho un match per questo punto // in tal caso vuol dire che sto creando una ripetizione nRep0 int nCase = 0 ; for ( int j = 0 ; j < int( vdMatch1.size()) ; ++j) { if ( abs( vdMatch1[j] - (c + 1)) < EPS_SMALL) { // devo però verificare che non ci sia un match successivo, perché in quel caso non ho una ripetizione bool bFoundMatch = false ; for ( int z = int( vdMatch1[j]) ; z < int( vMatch2.size()) ; ++z) { if ( abs( vMatch2[z].second - ( j + 1 )) < EPS_SMALL) { bFoundMatch = true ; break ; } } if ( ! bFoundMatch) { ++nRep1 ; // capisco se il punto è rep o se lo è il suo successivo if ( j + 1 < dParam) nCase = 1 ; else nCase = 2 ; } break ; } } vnAddedOrNextIsRep2.push_back( nCase) ; } ++c ; } // applico effettivamente gli split e aggiungo gli elementi ai vettori vbRep int nUnit = 0 ; if ( ! vdSplit1.empty()) nUnit = int( vdSplit1.back()) ; for ( int z = int( vdSplit1.size()) - 1 ; z >= 0 ; --z) { double dSplit = vdSplit1[z] ; int nSplit = int( dSplit) ; // se sto cercando di fare uno split sulla stessa curva che ho già splittato al passaggio precedente allora devo // riscalare if ( nSplit == nUnit && z < int( vdSplit1.size()) - 1) { dSplit = nSplit + ( dSplit - nSplit) / ( vdSplit1[z+1] - nSplit) ; } nUnit = nSplit ; cc2.AddJoint( dSplit) ; switch ( vnAddedOrNextIsRep1[z]) { case 0 : if ( vbRep2[nSplit]) ++ nRep2 ; // di default aggiungerei false, ma se il successivo è già un Rep allora anche questo deve esserlo vbRep2.insert( vbRep2.begin() + nSplit, vbRep2[nSplit]) ; break ; case 1 : vbRep2.insert( vbRep2.begin() + nSplit, true) ; break ; case 2 : if ( vbRep2[nSplit]) --nRep2 ; else vbRep2[nSplit] = true ; vbRep2.insert( vbRep2.begin() + nSplit, false) ; break ; } } if ( ! vdSplit2.empty()) nUnit = int( vdSplit2.back()) ; for ( int z = int( vdSplit2.size()) - 1 ; z >= 0 ; --z) { double dSplit = vdSplit2[z] ; int nSplit = int( dSplit) ; // se sto cercando di fare uno split sulla stessa curva che ho già splittato al passaggio precedente allora devo // riscalare if ( nSplit == nUnit && z < int( vdSplit2.size()) - 1) { dSplit = nSplit + ( dSplit - nSplit) / (vdSplit2[z+1] - nSplit) ; } nUnit = nSplit ; cc1.AddJoint( dSplit) ; switch ( vnAddedOrNextIsRep2[z]) { case 0 : if ( vbRep1[nSplit]) ++ nRep1 ; // di default aggiungerei false, ma se il successivo è già un Rep allora anche questo deve esserlo vbRep1.insert( vbRep1.begin() + nSplit, vbRep1[nSplit]) ; break ; case 1 : vbRep1.insert( vbRep1.begin() + nSplit, true) ; break ; case 2 : if ( vbRep1[nSplit]) -- nRep1 ; else vbRep1[nSplit] = true ; vbRep1.insert( vbRep1.begin() + nSplit, false) ; break ; } } nPnt1 = cc1.GetCurveCount() ; nPnt2 = cc2.GetCurveCount() ; //aggiusto i vettori delle ripetizioni in modo in modo che non arrivino mai ad essere contemporaneamente true int nAddedSpan = 0 ; int nCrv1 = 0 ; int nCrv2 = 0 ; while ( nAddedSpan < nPnt1 + nAtStart1 + nAtEnd1 + nRep2) { if ( nCrv1 >= nPnt1) nCrv1 = nPnt1 - 1 ; if ( nCrv2 >= nPnt2) nCrv2 = nPnt2 - 1 ; bool bRep1 = vbRep1[nCrv1] ; bool bRep2 = vbRep2[nCrv2] ; if ( bRep1 && bRep2) { vbRep1[nCrv1] = false ; bRep1 = false ; vbRep2[nCrv2] = false ; bRep2 = false ; -- nRep1 ; -- nRep2 ; } if ( ! bRep1 || nCrv2 == nPnt2 - 1) ++ nCrv2 ; if ( ! bRep2 || nCrv1 == nPnt1 - 1) ++ nCrv1 ; ++ nAddedSpan ; } // se non sono arrivato all'ultima curva su U0 o U1 vuol dire che ho creato delle ripetizioni che non ho contato prima if ( nCrv2 < nPnt2) { nRep2 += nPnt2 - nCrv2 ; for ( int z = int( vbRep2.size()) - 1 ; z >= nCrv2 ; --z) vbRep2[z] = true ; } if ( nCrv1 < nPnt1) { nRep1 += nPnt1 - nCrv1 ; for ( int z = int( vbRep1.size()) - 1 ; z >= nCrv1 ; --z) vbRep1[z] = true ; } // trovo il numero di span che dovrà avere la superficie // ( numero di sottocurve che compongono la U0 + tutte le ripetizioni dei match di punti della curva U1 con i punti di U0) int nPnt = nPnt1 + nAtStart1 + nAtEnd1 + nRep2 ; if ( nPnt != nPnt2 + nAtStart2 + nAtEnd2 + nRep1) LOG_DBG_ERR( GetEGkLogger(), "There could be an error in the creation of a ruled surface in mode RLT_B_MINDIST_PLUS") ; // aggiungo i punti di controllo scorrendo in contemporanea le due curve nAddedSpan = 0 ; nCrv1 = 0 ; nCrv2 = 0 ; while ( nAddedSpan < nPnt) { if ( nCrv1 >= nPnt1) nCrv1 = nPnt1 - 1 ; if ( nCrv2 >= nPnt2) nCrv2 = nPnt2 - 1 ; bool bRep1 = vbRep1[nCrv1] ; bool bRep2 = vbRep2[nCrv2] ; const ICurve* pSubCrv1 = cc1.GetCurve( nCrv1) ; Point3d ptStart1 ; pSubCrv1->GetStartPoint( ptStart1) ; vPnt1.emplace_back( ptStart1, nAddedSpan) ; const ICurve* pSubCrv2 = cc2.GetCurve( nCrv2) ; Point3d ptStart2 ; pSubCrv2->GetStartPoint( ptStart2) ; vPnt2.emplace_back( ptStart2, nAddedSpan) ; if ( ! bRep2) ++ nCrv1 ; if ( ! bRep1) ++ nCrv2 ; ++ nAddedSpan ; } return true ; }