5bcd4bb67d
- aggiunta classe Polygon3d (da EgtExchange) - razionalizzata classe Plane3d - corretta funzione IntersLineTria.
474 lines
17 KiB
C++
474 lines
17 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2016-2016
|
|
//----------------------------------------------------------------------------
|
|
// File : OffsetCurveOnX.cpp Data : 02.04.16 Versione : 1.6o4
|
|
// Contenuto : Classe per offset di Curve lungo la sola X.
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 02.04.16 DS Creazione modulo.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "CurveComposite.h"
|
|
#include "CurveLine.h"
|
|
#include "CurveArc.h"
|
|
#include "GeoConst.h"
|
|
#include "/EgtDev/Include/EgkOffsetCurveOnX.h"
|
|
#include "/EgtDev/Include/EgkIntersCurves.h"
|
|
#include "/EgtDev/Include/EGkDistPointCurve.h"
|
|
#include "/EgtDev/Include/EgtPointerOwner.h"
|
|
#include <algorithm>
|
|
|
|
using namespace std ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool SplitLeftRightArcs( CurveComposite& cCompo) ;
|
|
static bool SimpleOffsetOnX( ICurve* pCrv, double dDist) ;
|
|
static bool VerifyAndAdjustSamePoint( ICurve* pCrv1, ICurve* pCrv2) ;
|
|
static bool VerifyAndAdjustInternalAngle( ICurve* pCrv1, ICurve* pCrv2) ;
|
|
static bool SafeDist( double dDist, double dCoeff, ICurve* pCrv1, ICurve* pCrv2) ;
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
OffsetCurveOnX::~OffsetCurveOnX( void)
|
|
{
|
|
for ( auto& pCrv : m_CrvLst) {
|
|
if ( pCrv != nullptr) {
|
|
delete pCrv ;
|
|
pCrv = nullptr ;
|
|
}
|
|
}
|
|
m_CrvLst.clear() ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
OffsetCurveOnX::Make( const ICurve* pCrv, double dDist)
|
|
{
|
|
// verifico che la curva esista e sia piana
|
|
Plane3d plPlane ;
|
|
if ( pCrv == nullptr || ! pCrv->IsFlat( plPlane))
|
|
return false ;
|
|
// se esiste estrusione, verifico sia perpendicolare al piano della curva
|
|
Vector3d vtExtr ;
|
|
if ( pCrv->GetExtrusion( vtExtr) && ! vtExtr.IsSmall()) {
|
|
if ( ! AreSameOrOppositeVectorApprox( plPlane.GetVersN(), vtExtr))
|
|
return false ;
|
|
}
|
|
else
|
|
vtExtr = plPlane.GetVersN() ;
|
|
|
|
// determino se necessario cambiare riferimento ( dal vettore estrusione)
|
|
bool bNeedRef = ( ! vtExtr.IsZplus()) ;
|
|
|
|
// creo una copia della curva
|
|
CurveComposite ccCopy ;
|
|
if ( ! ccCopy.CopyFrom( pCrv))
|
|
return false ;
|
|
ccCopy.SetExtrusion( vtExtr) ;
|
|
|
|
// se necessario cambio il riferimento
|
|
Frame3d frExtr ;
|
|
if ( bNeedRef) {
|
|
// calcolo il riferimento OCS con VtExtr come asse Z
|
|
if ( ! frExtr.Set( ORIG, vtExtr))
|
|
return false ;
|
|
// esprimo la curva in questo riferimento
|
|
ccCopy.ToLoc( frExtr) ;
|
|
}
|
|
|
|
// verifico che la curva sia fatta solo da rette e archi che giacciono nel piano XY (VtExtr è ora Z+)
|
|
if ( ! ccCopy.ArcsBezierCurvesToArcsPerpExtr( 10 * EPS_SMALL, ANG_TOL_STD_DEG))
|
|
return false ;
|
|
|
|
// spezzo eventuali archi che cambiano pendenza
|
|
if ( ! SplitLeftRightArcs( ccCopy))
|
|
return false ;
|
|
|
|
// se offset qusi nullo, copio la curva nel risultato ed esco
|
|
if ( abs( dDist) < 10 * EPS_SMALL) {
|
|
PtrOwner<CurveComposite> pCopy( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCopy) || ! pCopy->RelocateFrom( ccCopy))
|
|
return false ;
|
|
m_CrvLst.push_back( Release( pCopy)) ;
|
|
return true ;
|
|
}
|
|
|
|
// verifico se curva chiusa
|
|
bool bClosed = ccCopy.IsClosed() ;
|
|
|
|
// Primo passo : estraggo entità dalla copia ed eseguo opportuno offset orizzontale
|
|
CurveComposite ccCopy2 ;
|
|
if ( ! ccCopy2.CopyFrom( &ccCopy))
|
|
return false ;
|
|
// indice della prima curva (1-based, per poter assegnare un significato al segno)
|
|
int nInd1 = 1 ;
|
|
// recupero la prima curva e la processo
|
|
PtrOwner<ICurve> pCrv1( ccCopy2.RemoveFirstOrLastCurve( false)) ;
|
|
if ( IsNull( pCrv1))
|
|
return false ;
|
|
// eseguo semplice offset orizzontale
|
|
pCrv1->SetTempProp( nInd1) ;
|
|
SimpleOffsetOnX( pCrv1, dDist) ;
|
|
// curve successive
|
|
PtrOwner<ICurve> pCrv2( ccCopy2.RemoveFirstOrLastCurve( false)) ;
|
|
while ( ! IsNull( pCrv2)) {
|
|
// eseguo semplice offset
|
|
pCrv2->SetTempProp( nInd1 + 1) ;
|
|
SimpleOffsetOnX( pCrv2, dDist) ;
|
|
// metto in lista curva precedente
|
|
m_CrvLst.push_back( Release( pCrv1)) ;
|
|
// aggiorno curva precedente
|
|
++ nInd1 ;
|
|
pCrv1.Set( Release( pCrv2)) ;
|
|
// passo alla curva successiva
|
|
pCrv2.Set( ccCopy2.RemoveFirstOrLastCurve( false)) ;
|
|
}
|
|
// Metto in lista l'ultima curva rimasta
|
|
if ( ! IsNull( pCrv1))
|
|
m_CrvLst.push_back( Release( pCrv1)) ;
|
|
|
|
// Secondo passo : taglio angoli tipo interni
|
|
ICurve* pPrev = nullptr ;
|
|
if ( bClosed && ! m_CrvLst.empty())
|
|
pPrev = m_CrvLst.back() ;
|
|
for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ;) {
|
|
ICurve* pCrv = *iIter ;
|
|
// gestione e verifica di estremi quasi coincidenti ed angoli interni
|
|
if ( pPrev != nullptr) {
|
|
if ( ! VerifyAndAdjustSamePoint( pPrev, pCrv))
|
|
VerifyAndAdjustInternalAngle( pPrev, pCrv) ;
|
|
}
|
|
// passo al successivo
|
|
pPrev = pCrv ;
|
|
++ iIter ;
|
|
}
|
|
|
|
// Terzo passo : conversione in curva composita chiudendo gli eventuali buchi con rette
|
|
PtrOwner<CurveComposite> pCrvCompo( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCrvCompo))
|
|
return false ;
|
|
Point3d ptPrec ;
|
|
if ( ! m_CrvLst.empty())
|
|
m_CrvLst.front()->GetStartPoint( ptPrec) ;
|
|
for ( auto& pCrv : m_CrvLst) {
|
|
// se punto iniziale diverso da precedente, inserisco segmento di collegamento
|
|
Point3d ptStart ;
|
|
pCrv->GetStartPoint( ptStart) ;
|
|
if ( ! AreSamePointApprox( ptPrec, ptStart)) {
|
|
CurveLine crvLine ;
|
|
crvLine.Set( ptPrec, ptStart) ;
|
|
pCrvCompo->AddCurve( crvLine) ;
|
|
}
|
|
// salvo punto finale come nuovo precedente
|
|
pCrv->GetEndPoint( ptPrec) ;
|
|
// aggiungo la curva alla composita
|
|
pCrvCompo->AddCurve( pCrv) ;
|
|
pCrv = nullptr ;
|
|
}
|
|
m_CrvLst.clear() ;
|
|
// se curva chiusa, verifico anche gap tra inizio e fine
|
|
if ( bClosed)
|
|
pCrvCompo->Close() ;
|
|
// unisco tutte le curve possibili
|
|
pCrvCompo->MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG) ;
|
|
|
|
// se non rimasto nulla, esco
|
|
double dLen ;
|
|
if ( ! pCrvCompo->IsValid() || ! pCrvCompo->GetLength( dLen) || dLen < 10 * EPS_SMALL)
|
|
return true ;
|
|
// inserisco la curva nella lista
|
|
m_CrvLst.push_back( Release( pCrvCompo)) ;
|
|
CurveComposite* pCompo1 = GetBasicCurveComposite( m_CrvLst.front()) ;
|
|
|
|
// Quarto passo : spezzatura della curva composita negli eventuali punti di auto-intersezione
|
|
// calcolo le auto-intersezioni
|
|
SelfIntersCurve sintC( *pCompo1) ;
|
|
DBLVECTOR vU ;
|
|
IntCrvCrvInfo iccInfo ;
|
|
for ( int i = 0 ; sintC.GetIntCrvCrvInfo( i, iccInfo) ; ++ i) {
|
|
if ( ! iccInfo.bOverlap) {
|
|
vU.push_back( iccInfo.IciA[0].dU) ;
|
|
vU.push_back( iccInfo.IciB[0].dU) ;
|
|
}
|
|
else {
|
|
vU.push_back( iccInfo.IciA[0].dU) ;
|
|
vU.push_back( iccInfo.IciA[1].dU) ;
|
|
vU.push_back( iccInfo.IciB[0].dU) ;
|
|
vU.push_back( iccInfo.IciB[1].dU) ;
|
|
}
|
|
}
|
|
// ordino il vettore in senso crescente
|
|
sort( vU.begin(), vU.end(), less<double>()) ;
|
|
// calcolo il vettore delle lunghezze di ogni parte di curva
|
|
DBLVECTOR vLen ;
|
|
for ( const auto& dU : vU) {
|
|
double dULen ;
|
|
pCompo1->GetLengthAtParam( dU, dULen) ;
|
|
vLen.push_back( dULen) ;
|
|
}
|
|
// se ultima parte molto piccola, la elimino
|
|
if ( ! vU.empty() && ( dLen - vLen.back()) < 2 * EPS_SMALL) {
|
|
vU.pop_back() ;
|
|
vLen.pop_back() ;
|
|
}
|
|
// eseguo la divisione
|
|
int nCount = 1 ;
|
|
double dUPrev = 0 ;
|
|
double dLenPrev = 0 ;
|
|
for ( int i = 0 ; i < int( vU.size()) ; ++ i) {
|
|
// se lunghezza della curva risulta piccola, salto
|
|
if ( ( vLen[i] - dLenPrev) < 2 * EPS_SMALL)
|
|
continue ;
|
|
// copio la curva
|
|
PtrOwner<CurveComposite> pCopy( pCompo1->Clone()) ;
|
|
if ( IsNull( pCopy))
|
|
return false ;
|
|
m_CrvLst.push_back( Release( pCopy)) ;
|
|
++ nCount ;
|
|
// trimmo l'originale
|
|
pCompo1->TrimStartEndAtParam( dUPrev, vU[i]) ;
|
|
// la copia diventa il nuovo corrente
|
|
pCompo1 = GetBasicCurveComposite( m_CrvLst.back()) ;
|
|
dUPrev = vU[i] ;
|
|
dLenPrev = vLen[i] ;
|
|
}
|
|
// se fatta almeno una suddivisione, trimmo l'ultima parte
|
|
if ( dUPrev > EPS_PARAM)
|
|
pCompo1->TrimStartAtParam( dUPrev) ;
|
|
|
|
// Quinto passo : elimino le parti che sono troppo vicine al percorso originale
|
|
for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ;) {
|
|
ICurve* pCrv = *iIter ;
|
|
// distanza minima di alcuni punti interni della curva dalla curva originale
|
|
if ( ! SafeDist( dDist, 0.53, pCrv, &ccCopy) ||
|
|
! SafeDist( dDist, 0.13, pCrv, &ccCopy) ||
|
|
! SafeDist( dDist, 0.89, pCrv, &ccCopy)) {
|
|
delete pCrv ;
|
|
iIter = m_CrvLst.erase( iIter) ;
|
|
continue ;
|
|
}
|
|
// passo alla successiva
|
|
++ iIter ;
|
|
}
|
|
|
|
// Sesto passo : concateno i percorsi risultanti (senza cambiare verso)
|
|
for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ;) {
|
|
CurveComposite* pCrvCo = GetBasicCurveComposite( *iIter) ;
|
|
// recupero punti iniziale e finale della curva
|
|
Point3d ptStart, ptEnd ;
|
|
pCrvCo->GetStartPoint( ptStart) ;
|
|
pCrvCo->GetEndPoint( ptEnd) ;
|
|
// ciclo sulle curve successive per verificare se possibile concatenamento
|
|
for ( auto iIter2 = next( iIter) ; iIter2 != m_CrvLst.end() ;) {
|
|
CurveComposite* pCrvCo2 = GetBasicCurveComposite( *iIter2) ;
|
|
// recupero punti iniziale e finale della curva
|
|
Point3d ptStart2, ptEnd2 ;
|
|
pCrvCo2->GetStartPoint( ptStart2) ;
|
|
pCrvCo2->GetEndPoint( ptEnd2) ;
|
|
// verifiche di concatenamento
|
|
if ( AreSamePointEpsilon( ptEnd, ptStart2, 10 * EPS_SMALL)) {
|
|
pCrvCo->AddCurve( pCrvCo2, true, 10 * EPS_SMALL) ;
|
|
m_CrvLst.erase( iIter2) ;
|
|
ptEnd = ptEnd2 ;
|
|
iIter2 = next( iIter) ;
|
|
}
|
|
else if ( AreSamePointEpsilon( ptEnd2, ptStart, 10 * EPS_SMALL)) {
|
|
pCrvCo->AddCurve( pCrvCo2, false, 10 * EPS_SMALL) ;
|
|
m_CrvLst.erase( iIter2) ;
|
|
ptStart = ptStart2 ;
|
|
iIter2 = next( iIter) ;
|
|
}
|
|
else
|
|
++ iIter2 ;
|
|
}
|
|
++ iIter ;
|
|
}
|
|
|
|
// riporto il risultato nel riferimento originale
|
|
if ( bNeedRef) {
|
|
for ( auto pCrv : m_CrvLst)
|
|
pCrv->ToGlob( frExtr) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ICurve*
|
|
OffsetCurveOnX::GetCurve( void)
|
|
{
|
|
if ( m_CrvLst.empty())
|
|
return nullptr ;
|
|
ICurve* pCrv = m_CrvLst.front() ;
|
|
m_CrvLst.pop_front() ;
|
|
return pCrv ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ICurve*
|
|
OffsetCurveOnX::GetLongerCurve( void)
|
|
{
|
|
if ( m_CrvLst.empty())
|
|
return nullptr ;
|
|
// cerco la curva più lunga
|
|
double dMaxLen = 0 ;
|
|
auto iMaxIter = m_CrvLst.end() ;
|
|
for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ; ++ iIter) {
|
|
double dLen ;
|
|
if ( (*iIter)->GetLength( dLen) && dLen > dMaxLen) {
|
|
dMaxLen = dLen ;
|
|
iMaxIter = iIter ;
|
|
}
|
|
}
|
|
// se trovata
|
|
if ( iMaxIter != m_CrvLst.end()) {
|
|
ICurve* pCrv = (*iMaxIter) ;
|
|
m_CrvLst.erase( iMaxIter) ;
|
|
return pCrv ;
|
|
}
|
|
|
|
return nullptr ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
SplitLeftRightArcs( CurveComposite& cCompo)
|
|
{
|
|
int i = 0 ;
|
|
const ICurve* pCrv = cCompo.GetCurve( i) ;
|
|
while ( pCrv != nullptr) {
|
|
if ( pCrv->GetType() == CRV_ARC) {
|
|
const CurveArc* pArc = GetBasicCurveArc( pCrv) ;
|
|
// determino la rotazione angolare dall'inizio alle direzioni su e giù
|
|
double dAng1, dAng2 ;
|
|
bool bDet ;
|
|
pArc->GetStartVersor().GetRotation( Y_AX, Z_AX, dAng1, bDet) ;
|
|
pArc->GetStartVersor().GetRotation( - Y_AX, Z_AX, dAng2, bDet) ;
|
|
// oriento queste rotazioni come l'angolo al centro dell'arco
|
|
if ( pArc->GetAngCenter() > 0) {
|
|
if ( dAng1 < 0)
|
|
dAng1 += ANG_FULL ;
|
|
if ( dAng2 < 0)
|
|
dAng2 += ANG_FULL ;
|
|
}
|
|
else {
|
|
if ( dAng1 > 0)
|
|
dAng1 -= ANG_FULL ;
|
|
if ( dAng2 > 0)
|
|
dAng2 -= ANG_FULL ;
|
|
}
|
|
// le ordino in senso crescente di ampiezza assoluta
|
|
if ( abs( dAng1) > abs( dAng2))
|
|
swap( dAng1, dAng2) ;
|
|
// verifico se la prima di queste rotazioni è compresa nell'arco
|
|
if ( abs( dAng1) > EPS_ANG_SMALL && abs( dAng1) < abs( pArc->GetAngCenter()) - EPS_ANG_SMALL) {
|
|
cCompo.AddJoint( i + dAng1 / pArc->GetAngCenter()) ;
|
|
}
|
|
// verifico la seconda
|
|
else if ( abs( dAng2) > EPS_ANG_SMALL && abs( dAng2) < abs( pArc->GetAngCenter()) - EPS_ANG_SMALL) {
|
|
cCompo.AddJoint( i + dAng2 / pArc->GetAngCenter()) ;
|
|
}
|
|
}
|
|
// passo alla successiva
|
|
pCrv = cCompo.GetCurve( ++i) ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
SimpleOffsetOnX( ICurve* pCrv, double dDist)
|
|
{
|
|
Point3d ptStart, ptEnd ;
|
|
pCrv->GetStartPoint( ptStart) ;
|
|
pCrv->GetEndPoint( ptEnd) ;
|
|
if ( abs( ptStart.y - ptEnd.y) < EPS_SMALL)
|
|
;
|
|
else if ( ptStart.y < ptEnd.y)
|
|
pCrv->Translate( Vector3d( dDist, 0, 0)) ;
|
|
else
|
|
pCrv->Translate( Vector3d( - dDist, 0, 0)) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
VerifyAndAdjustSamePoint( ICurve* pCrv1, ICurve* pCrv2)
|
|
{
|
|
// verifica dei puntatori
|
|
if ( pCrv1 == nullptr || pCrv2 == nullptr)
|
|
return false ;
|
|
|
|
// calcolo dei punti estremi (finale per prima curva, iniziale per seconda)
|
|
Point3d ptP1, ptP2 ;
|
|
if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2))
|
|
return false ;
|
|
// verifica distanza tra estremi
|
|
if ( ! AreSamePointXYEpsilon( ptP1, ptP2, 10 * EPS_SMALL))
|
|
return false ;
|
|
// se coincidono esattamente, va bene così
|
|
if ( AreSamePointExact( ptP1, ptP2))
|
|
return true ;
|
|
// sono in tolleranza, ma devo ricongiungere gli estremi
|
|
Point3d ptMid = 0.5 * ( ptP1 + ptP2) ;
|
|
return ( pCrv1->ModifyEnd( ptMid) && pCrv2->ModifyStart( ptMid)) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
VerifyAndAdjustInternalAngle( ICurve* pCrv1, ICurve* pCrv2)
|
|
{
|
|
// verifica dei puntatori
|
|
if ( pCrv1 == nullptr || pCrv2 == nullptr)
|
|
return false ;
|
|
|
|
// calcolo l'intersezione tra le due curve
|
|
IntersCurveCurve intCC( *pCrv1, *pCrv2) ;
|
|
if ( intCC.GetIntersCount() == 0)
|
|
return false ;
|
|
// prendo l'intersezione più vicina al punto medio tra gli estremi delle curve
|
|
Point3d ptP1, ptP2 ;
|
|
if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2))
|
|
return false ;
|
|
Point3d ptMid = 0.5 * ( ptP1 + ptP2) ;
|
|
Point3d ptNew1, ptNew2 ;
|
|
if ( ! intCC.GetIntersPointNearTo( 0, ptMid, ptNew1) ||
|
|
! intCC.GetIntersPointNearTo( 1, ptMid, ptNew2))
|
|
return false ;
|
|
// modifico le due curve sul punto medio di intersezione
|
|
Point3d ptNew = 0.5 * ( ptNew1 + ptNew2) ;
|
|
return ( pCrv1->ModifyEnd( ptNew) && pCrv2->ModifyStart( ptNew)) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
SafeDist( double dDist, double dCoeff, ICurve* pCrv1, ICurve* pCrv2)
|
|
{
|
|
// calcolo il punto e due vicini
|
|
double dUStart, dUEnd ;
|
|
pCrv1->GetDomain( dUStart, dUEnd) ;
|
|
double dU = ( 1 - dCoeff) * dUStart + dCoeff * dUEnd ;
|
|
Point3d ptP ;
|
|
pCrv1->GetPointD1D2( dU, ICurve::FROM_MINUS, ptP) ;
|
|
Point3d ptPrev, ptNext ;
|
|
Vector3d vtPrev, vtNext ;
|
|
pCrv1->GetPointTang( dU - 100 * EPS_PARAM, ICurve::FROM_PLUS, ptPrev, vtPrev) ;
|
|
pCrv1->GetPointTang( dU + 100 * EPS_PARAM, ICurve::FROM_MINUS, ptNext, vtNext) ;
|
|
// non si considerano i tratti orizzontali (tolleranza fino a 1deg)
|
|
const double SEN_ANG_LIM = 0.0175 ;
|
|
if ( abs( vtPrev.y) < SEN_ANG_LIM || abs( vtNext.y) < SEN_ANG_LIM)
|
|
return true ;
|
|
// segmento orizzontale di prova centrato sul punto e lungo dDist + dDist
|
|
Vector3d vtDelta( abs(dDist) - 5 * EPS_SMALL, 0, 0) ;
|
|
CurveLine Line ;
|
|
Line.Set( ptP - vtDelta, ptP + vtDelta) ;
|
|
// interseco il segmento di test con la curva originale
|
|
IntersCurveCurve IntCC( Line, *pCrv2) ;
|
|
return ( IntCC.GetIntersCount() == 0) ;
|
|
}
|