//---------------------------------------------------------------------------- // EgalTech 2014-2014 //---------------------------------------------------------------------------- // File : PolyArc.cpp Data : 14.08.14 Versione : 1.5h3 // Contenuto : Implementazione della classe PolyArc. // // // // Modifiche : 14.08.14 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "GeoConst.h" #include "CurveArc.h" #include "/EgtDev/Include/EGkDistPointLine.h" #include "/EgtDev/Include/EGkPolyArc.h" #include "/EgtDev/Include/EGkFrame3d.h" #include using namespace std ; //---------------------------------------------------------------------------- PolyArc::PolyArc( void) { m_vtExtr = Z_AX ; m_nRejected = 0 ; m_iter = m_lUPointBs.end() ; } //---------------------------------------------------------------------------- PolyArc::~PolyArc( void) { } //---------------------------------------------------------------------------- bool PolyArc::Clear( void) { m_vtExtr = Z_AX ; m_nRejected = 0 ; m_lUPointBs.clear() ; m_iter = m_lUPointBs.end() ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::SetExtrusion( const Vector3d& vtExtr) { if ( vtExtr.IsSmall()) return false ; m_vtExtr = vtExtr ; m_vtExtr.Normalize() ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::AddUPoint( double dPar, const Point3d& ptP, double dBulge) { // se il punto è uguale al precedente (ignoro parametro e bulge), non lo inserisco ma ok if ( ! m_lUPointBs.empty() && AreSamePointApprox( ptP, m_lUPointBs.back().ptP)) { // assegno parametro e bulge m_lUPointBs.back().dU = dPar ; m_lUPointBs.back().dB = dBulge ; // incremento contatore rifiutati ed esco ++ m_nRejected ; return true ; } // aggiungo il punto try { m_lUPointBs.emplace_back( dPar, ptP, dBulge) ; } catch (...) { return false ; } return true ; } //---------------------------------------------------------------------------- bool PolyArc::Close( void) { // ci devono essere almeno 2 punti if ( m_lUPointBs.size() < 2) return false ; // verifico non sia già chiuso if ( AreSamePointApprox( m_lUPointBs.front().ptP, m_lUPointBs.back().ptP)) return false ; // aggiungo un punto uguale al primo in coda return AddUPoint( m_lUPointBs.front().dU, m_lUPointBs.front().ptP, m_lUPointBs.front().dB) ; } //---------------------------------------------------------------------------- bool PolyArc::ModifyLastParam( double dPar) { // verifico esistano dei punti if ( m_lUPointBs.empty()) return false ; // eseguo la modifica m_lUPointBs.back().dU = dPar ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::ModifyLastBulge( double dBulge) { // verifico esistano dei punti if ( m_lUPointBs.empty()) return false ; // eseguo la modifica m_lUPointBs.back().dB = dBulge ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::EraseFirstUPoint( void) { if ( m_lUPointBs.empty()) return false ; m_lUPointBs.pop_front() ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::EraseLastUPoint( void) { if ( m_lUPointBs.empty()) return false ; m_lUPointBs.pop_back() ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::AddOffsetToU( double dOffset) { for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) iter->dU += dOffset ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::ParamLinearTransform( double dStartU, double dEndU) { // ci devono essere almeno 2 punti if ( m_lUPointBs.size() < 2) return false ; // recupero i vecchi estremi del parametro double dOriStartU = m_lUPointBs.front().dU ; double dOriEndU = m_lUPointBs.back().dU ; // determino i coefficienti di riparametrizzazione double dCoeff ; if ( abs( dEndU - dStartU) < EPS_PARAM) dCoeff = 0 ; else if ( abs( dOriEndU - dOriStartU) > EPS_PARAM) dCoeff = ( dEndU - dStartU) / ( dOriEndU - dOriStartU) ; else return false ; // eseguo la riparametrizzazione for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) iter->dU = dStartU + dCoeff * ( iter->dU - dOriStartU) ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::SetElevation( double dZ) { for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) iter->ptP.z = dZ ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::AddElevation( double dZ) { for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) iter->ptP.z += dZ ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::Translate( const Vector3d& vtMove) { // il vettore estrusione non subisce modifiche for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) iter->ptP.Translate( vtMove) ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, double dSinAng) { m_vtExtr.Rotate( vtAx, dCosAng, dSinAng) ; for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) iter->ptP.Rotate( ptAx, vtAx, dCosAng, dSinAng) ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::Scale( const Frame3d& frRef, double dCoeff) { m_vtExtr.Scale( frRef, dCoeff, dCoeff, dCoeff) ; for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) iter->ptP.Scale( frRef, dCoeff, dCoeff, dCoeff) ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::Mirror( const Point3d& ptOn, const Vector3d& vtNorm) { m_vtExtr.Mirror( vtNorm) ; for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) { iter->ptP.Mirror( ptOn, vtNorm) ; iter->dB *= - 1 ; } return true ; } //---------------------------------------------------------------------------- bool PolyArc::ToGlob( const Frame3d& frRef) { m_vtExtr.ToGlob( frRef) ; for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) iter->ptP.ToGlob( frRef) ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::ToLoc( const Frame3d& frRef) { m_vtExtr.ToLoc( frRef) ; for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) iter->ptP.ToLoc( frRef) ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::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 m_vtExtr.LocToLoc( frOri, frDest) ; for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) iter->ptP.LocToLoc( frOri, frDest) ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::Join( PolyArc& PA, double dOffsetPar) { // se l'altro poliarco non contiene alcunchè, esco con ok if ( PA.m_lUPointBs.empty()) return true ; // verifico che l'ultimo punto di questo poliarco coincida con il primo dell'altro if ( ! m_lUPointBs.empty() && ! AreSamePointApprox( m_lUPointBs.back().ptP, PA.m_lUPointBs.front().ptP)) return false ; // cancello l'ultimo di questo EraseLastUPoint() ; // aggiungo eventuale offset all'altro if ( abs( dOffsetPar) > EPS_PARAM) PA.AddOffsetToU( dOffsetPar) ; // sposto i punti dall'altra polilinea a questa e aggiorno i contatori m_lUPointBs.splice( m_lUPointBs.end(), PA.m_lUPointBs) ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::Split( double dU, PolyArc& PA) { // pulisco la polilinea destinazione PA.Clear() ; // ricerca del punto in cui dividere auto iter = m_lUPointBs.begin() ; while ( iter != m_lUPointBs.end() && iter->dU < ( dU + EPS_PARAM)) ++ iter ; if ( iter == m_lUPointBs.end()) return false ; // sposto i punti nell'altra polilinea PA.m_lUPointBs.splice( PA.m_lUPointBs.end(), m_lUPointBs, iter, m_lUPointBs.end()) ; // prepongo l'ultimo punto rimasto PA.m_lUPointBs.push_front( m_lUPointBs.back()) ; // annullo l'iteratore corrente m_iter = m_lUPointBs.end() ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::IsClosed( void) const { if ( m_lUPointBs.size() < 3) return false ; return ( AreSamePointApprox( m_lUPointBs.front().ptP, m_lUPointBs.back().ptP)) ; } //---------------------------------------------------------------------------- bool PolyArc::IsRectangleXY( BBox3d& b3Rect) const { b3Rect.Reset() ; // Deve essere chiusa if ( ! IsClosed()) return false ; // Verifico presenza di archi e linee oblique e calcolo il box Point3d ptIni, ptFin ; double dBulge ; bool bNext = GetFirstArc( ptIni, ptFin, dBulge) ; while ( bNext) { // non sono ammessi archi double dLeftDeflection = - dBulge * DistXY( ptIni, ptFin) / 2 ; if ( abs( dLeftDeflection) > 10 * EPS_SMALL) return false ; // non sono ammesse linee oblique if ( abs( ptFin.y - ptIni.y) > 10 * EPS_SMALL && abs( ptFin.x - ptIni.x) > 10 * EPS_SMALL) return false ; // la Z deve essere costante if ( abs( ptFin.z - ptIni.z) > 10 * EPS_SMALL) return false ; // aggiorno il box b3Rect.Add( ptIni) ; b3Rect.Add( ptFin) ; // passo alla curva successiva bNext = GetNextArc( ptIni, ptFin, dBulge) ; } // Verifico che tutte le linee stiano sulla frontiera del box bNext = GetFirstArc( ptIni, ptFin, dBulge) ; while ( bNext) { // verifico che l'inizio stia sulla frontiera del rettangolo if ( abs( ptIni.x - b3Rect.GetMin().x) > 10 * EPS_SMALL && abs( ptIni.x - b3Rect.GetMax().x) > 10 * EPS_SMALL && abs( ptIni.y - b3Rect.GetMin().y) > 10 * EPS_SMALL && abs( ptIni.y - b3Rect.GetMax().y) > 10 * EPS_SMALL) return false ; // verifico che la fine stia sulla frontiera del rettangolo if ( abs( ptFin.x - b3Rect.GetMin().x) > 10 * EPS_SMALL && abs( ptFin.x - b3Rect.GetMax().x) > 10 * EPS_SMALL && abs( ptFin.y - b3Rect.GetMin().y) > 10 * EPS_SMALL && abs( ptFin.y - b3Rect.GetMax().y) > 10 * EPS_SMALL) return false ; // passo alla curva successiva bNext = GetNextArc( ptIni, ptFin, dBulge) ; } return true ; } //---------------------------------------------------------------------------- bool PolyArc::IsCircle( double dLinTol, Point3d& ptCen, Vector3d& vtN, double& dRad, bool& bCCW) const { // Deve essere chiusa if ( ! IsClosed()) return false ; // Devono essere presenti solo archi con lo stesso centro, raggio e direzione bool bFirst = true ; Point3d ptIni, ptFin ; double dBulge ; bool bNext = GetFirstArc( ptIni, ptFin, dBulge) ; while ( bNext) { // non ammessi segmenti di retta double dLeftDeflection = - dBulge * DistXY( ptIni, ptFin) / 2 ; if ( abs( dLeftDeflection) < 10 * EPS_SMALL) return false ; // calcolo l'arco CurveArc cArc ; if ( ! cArc.Set2PNB( ptIni, ptFin, m_vtExtr, dBulge)) return false ; if ( bFirst) { bFirst = false ; ptCen = cArc.GetCenter() ; vtN = cArc.GetNormVersor() ; dRad = cArc.GetRadius() ; bCCW = ( cArc.GetAngCenter() > 0) ; } else { if ( ! AreSamePointEpsilon( ptCen, cArc.GetCenter(), dLinTol)) return false ; if ( ! AreSameVectorApprox( vtN, cArc.GetNormVersor())) return false ; if ( abs( dRad - cArc.GetRadius()) > dLinTol) return false ; if ( bCCW != ( cArc.GetAngCenter() > 0)) return false ; } // passo alla curva successiva bNext = GetNextArc( ptIni, ptFin, dBulge) ; } return true ; } //---------------------------------------------------------------------------- bool PolyArc::GetFirstUPoint( double* pdPar, Point3d* pptP, double* pdBulge, bool bNotLast) const { // cerco il primo punto m_iter = m_lUPointBs.begin() ; if ( m_iter == m_lUPointBs.end()) return false ; if ( bNotLast && m_iter == -- ( m_lUPointBs.end())) return false ; // assegno i dati if ( pdPar != nullptr) *pdPar = m_iter->dU ; if ( pptP != nullptr) *pptP = m_iter->ptP ; if ( pdBulge != nullptr) *pdBulge = m_iter->dB ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::GetNextUPoint( double* pdPar, Point3d* pptP, double* pdBulge, bool bNotLast) const { // cerco il prossimo punto if ( m_iter == m_lUPointBs.end()) return false ; ++ m_iter ; if ( m_iter == m_lUPointBs.end()) return false ; if ( bNotLast && m_iter == -- ( m_lUPointBs.end())) return false ; // assegno i dati if ( pdPar != nullptr) *pdPar = m_iter->dU ; if ( pptP != nullptr) *pptP = m_iter->ptP ; if ( pdBulge != nullptr) *pdBulge = m_iter->dB ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::GetFirstUArc( double* pdIni, Point3d* pptIni, double* pdFin, Point3d* pptFin, double* pdBulge) const { // parametro e punto iniziali m_iter = m_lUPointBs.begin() ; if ( m_iter == m_lUPointBs.end()) return false ; if ( pdIni != nullptr) *pdIni = m_iter->dU ; if ( pptIni != nullptr) *pptIni = m_iter->ptP ; // bulge dell'arco (è attaccato al punto iniziale) if ( pdBulge != nullptr) *pdBulge = m_iter->dB ; // parametro e punto finali ++ m_iter ; if ( m_iter == m_lUPointBs.end()) return false ; if ( pdFin != nullptr) *pdFin = m_iter->dU ; if ( pptFin != nullptr) *pptFin = m_iter->ptP ; return true ; } //---------------------------------------------------------------------------- bool PolyArc::GetNextUArc( double* pdIni, Point3d* pptIni, double* pdFin, Point3d* pptFin, double* pdBulge) const { // parametro e punto iniziali (è il precedente finale) if ( m_iter == m_lUPointBs.end()) return false ; if ( pdIni != nullptr) *pdIni = m_iter->dU ; if ( pptIni != nullptr) *pptIni = m_iter->ptP ; // bulge (è attaccato al punto iniziale) if ( pdBulge != nullptr) *pdBulge = m_iter->dB ; // parametro e punto finali ++ m_iter ; if ( m_iter == m_lUPointBs.end()) return false ; if ( pdFin != nullptr) *pdFin = m_iter->dU ; if ( pptFin != nullptr) *pptFin = m_iter->ptP ; return true ; } //---------------------------------------------------------------------------- // L'eventuale piano deve essere perpendicolare al vettore estrusione // Risultati : // false + rank = 0 nessun punto, + rank = 1 punto allineati come estrusione, + rank = 3 percorso 3d; // true + rank = 0 punti collassati, + rank = 2 percorso 2d. //---------------------------------------------------------------------------- bool PolyArc::IsFlat( int& nRank, Point3d& ptCen, Vector3d& vtDir, double dToler) const { // inizializzazioni nRank = 0 ; ptCen = ORIG ; vtDir = m_vtExtr ; // il primo punto fissa la proiezione di riferimento sull'asse di estrusione double dBulge ; Point3d ptStart ; if ( ! GetFirstPoint( ptStart, dBulge)) return false ; ptCen += ptStart ; // ciclo sui punti per verificare il valore relativo della proiezione Point3d ptP ; while ( GetNextPoint( ptP, dBulge)) { // componente della differenza tra i punti parallela al vettore estrusione double dPar = ( ptP - ptStart) * m_vtExtr ; // vettore componente della differenza perpendicolare al vettore estrusione Vector3d vtPerp = ( ptP - ptStart) - dPar * m_vtExtr ; // se il vettore componente perpendicolare è non nullo, punto non allineato su estrusione if ( ! vtPerp.IsSmall()) { if ( nRank == 0 || nRank == 1) nRank += 2 ; } // se la componente parallela è non nulla, punto non nel piano if ( abs( dPar) > dToler) { if ( nRank == 0 || nRank == 2) nRank += 1 ; } // aggiorno la somma dei punti per calcolare il centro ptCen += ptP ; } // aggiusto il centro ptCen /= double( m_lUPointBs.size()) ; return ( nRank == 0 || nRank == 2) ; } //---------------------------------------------------------------------------- bool PolyArc::Invert( bool bInvertU) { // verifico non sia vuota if ( m_lUPointBs.empty()) return true ; // inverto la lista m_lUPointBs.reverse() ; // sposto il bulge all'estremo iniziale di ogni tratto (l'ultimo non conta) e lo inverto for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) { if ( next( iter) != m_lUPointBs.end()) iter->dB = - next( iter)->dB ; else iter->dB = 0 ; } // 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_lUPointBs.front().dU ; // ciclo su tutti gli elementi for ( auto iter = m_lUPointBs.begin() ; iter != m_lUPointBs.end() ; ++ iter) { iter->dU = dUfin - iter->dU ; } } return true ; } //---------------------------------------------------------------------------- static bool PointsInTolerance( const PNTVECTOR& vRPT, const Point3d& ptP1, const Point3d& ptP2, double dSqTol) { for ( const auto& ptQ : vRPT) { double dSqDist ; if ( ! DistPointLine( ptQ, ptP1, ptP2).GetSqDist( dSqDist) || dSqDist > dSqTol) return false ; } return true ; } //---------------------------------------------------------------------------- bool PolyArc::RemoveAlignedPoints( double dToler) { // se non ci sono almeno 3 punti, esco subito if ( m_lUPointBs.size() < 3) return true ; // controllo minimo valore di tolleranza dToler = max( dToler, LIN_TOL_MIN) ; double dSqTol = dToler * dToler ; // si analizza la distanza di un punto dal segmento che unisce precedente e successivo // punto precedente auto precP = m_lUPointBs.begin() ; // punto corrente auto currP = next( precP) ; // punto successivo auto nextP = next( currP) ; // lista dei punti appena rimossi PNTVECTOR vRPT ; vRPT.reserve( 20) ; // mentre esiste un successivo while ( nextP != m_lUPointBs.end()) { double dSqDist = 2 * dSqTol ; // se precedente e corrente non hanno bulge (non sono archi i due tratti) if ( abs( precP->dB) < EPS_SMALL && abs( currP->dB) < EPS_SMALL) { // distanza del punto corrente dal segmento che unisce gli adiacenti DistPointLine( currP->ptP, precP->ptP, nextP->ptP).GetSqDist( dSqDist) ; } // se da eliminare e gli altri eliminati stanno nella tolleranza if ( dSqDist < dSqTol && PointsInTolerance( vRPT, precP->ptP, nextP->ptP, dSqTol)) { // aggiungo il punto nella lista dei rimossi vRPT.emplace_back( currP->ptP) ; // elimino il punto m_lUPointBs.erase( currP) ; // avanzo con corrente e successivo currP = nextP ; ++ nextP ; } // altrimenti da tenere else { // cancello la lista dei rimossi vRPT.clear() ; // avanzo il terzetto di uno step precP = currP ; currP = nextP ; ++ nextP ; } } // se curva chiusa con almeno 4 punti, devo analizzare il terzetto attorno alla chiusura if ( IsClosed() && m_lUPointBs.size() >= 4) { // precP e currP sono già corretti // il primo punto ripete l'ultimo (geometricamente coincide con currP) auto firstP = m_lUPointBs.begin() ; // questo è il vero successivo nextP = next( firstP) ; double dSqDist = 2 * dSqTol ; // se precedente e corrente non hanno bulge (non sono archi i due tratti) if ( abs( precP->dB) < EPS_SMALL && abs( firstP->dB) < EPS_SMALL) { // distanza del punto corrente dal segmento che unisce gli adiacenti DistPointLine( currP->ptP, precP->ptP, nextP->ptP).GetSqDist( dSqDist) ; } // se da eliminare if ( dSqDist < dSqTol && PointsInTolerance( vRPT, precP->ptP, nextP->ptP, dSqTol)) { // faccio coincidere il primo punto con il precedente firstP->ptP = precP->ptP ; // elimino il punto corrente m_lUPointBs.erase( currP) ; } } return true ; }