cd48e2de3b
- varie correzioni ortografiche.
2022 lines
68 KiB
C++
2022 lines
68 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 "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)
|
|
{
|
|
// 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 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[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<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() ;
|
|
// 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<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 ;
|
|
|
|
// 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<POINTU> 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 ;
|
|
bool bLast1 = false ;
|
|
bool bLast2 = false ;
|
|
while ( nAddedSpan < nPnt) {
|
|
if ( nCrv1 >= nPnt1) {
|
|
nCrv1 = nPnt1 - 1 ;
|
|
bLast1 = true ;
|
|
}
|
|
if ( nCrv2 >= nPnt2) {
|
|
nCrv2 = nPnt2 - 1 ;
|
|
bLast2 = true ;
|
|
}
|
|
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 ;
|
|
}
|