Files
EgtGeomKernel/PolyLine.cpp
T
Dario Sassi d60d38a24b Revert "EgtGeomKernel 2.6i1 :"
This reverts commit e598e7986f.
2024-11-29 18:22:48 +01:00

1661 lines
55 KiB
C++

//----------------------------------------------------------------------------
// 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 "/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.size() > 0 && 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.size() > 0 && 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.size() == 0)
return true ;
// verifico che l'ultimo punto di questa polilinea coincida con il primo dell'altra
if ( m_lUPoints.size() > 0 && ! 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)
{
// 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( 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( vPtU.size() - 1) ;
}
// ordino in senso crescente
sort( vInd.begin(), vInd.end()) ;
// se chiusa e almeno 4 punti rimasti, controllo allineamento dell'inizio con precedente e successivo rimasti
if ( IsClosed() && vInd.size() >= 4) {
if ( DistPointLine( vPtU[vInd[0]].first, vPtU[vInd[1]].first, vPtU[vInd[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::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 (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 ( AreSamePointApprox( precP->first, currP->first)) {
// 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::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.size() == 0)
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<PolyLine&>( 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<PolyLine&>( 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( 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<PolyLine&>( 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 ;
bCommonInternalPoints = false ; // indica la presenza di punti interni in comune tra le due polylines
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 ;
}