35618b1882
- correzione all'offset avanzato di Curve fase 8 di chain per casi con curve che in modifica per concatenazione si annullano.
1182 lines
44 KiB
C++
1182 lines
44 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2015-2020
|
|
//----------------------------------------------------------------------------
|
|
// File : OffsetCurve.cpp Data : 10.10.20 Versione : 2.2j2
|
|
// Contenuto : Classe per offset avanzato di Curve.
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 23.09.15 DS Creazione modulo.
|
|
// 24.06.19 DS Agg. GetShorterCurve.
|
|
// 10.10.20 DS Migliorata gestione angoli interni.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "CurveComposite.h"
|
|
#include "CurveLine.h"
|
|
#include "CurveArc.h"
|
|
#include "RemoveCurveDefects.h"
|
|
#include "GeoConst.h"
|
|
#include "/EgtDev/Include/EGkOffsetCurve.h"
|
|
#include "/EgtDev/Include/EGkIntersCurves.h"
|
|
#include "/EgtDev/Include/EGkDistPointCurve.h"
|
|
#include "/EgtDev/Include/EGkIntervals.h"
|
|
#include "/EgtDev/Include/EgtPointerOwner.h"
|
|
#include <algorithm>
|
|
|
|
using namespace std ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool PreviousIsLine( ICURVEPLIST::const_iterator iIter, const ICURVEPLIST& CrvLst, bool bClosed) ;
|
|
static bool PreviousIsLonger( int nInd1, const DBLVECTOR& vLens, bool bClosed) ;
|
|
static bool NextIsLine( ICURVEPLIST::const_iterator iIter, const ICURVEPLIST& CrvLst, bool bClosed) ;
|
|
static bool NextIsLonger( int nInd1, const DBLVECTOR& vLens, bool bClosed) ;
|
|
static bool CalcAngle( const ICurve* pCrv1, const ICurve* pCrv2, double& dAngDeg) ;
|
|
static bool VerifyAndAdjustSamePoint( ICurve* pCrv1, ICurve* pCrv2, int& nRes) ;
|
|
static bool VerifyAndAdjustInternalAngle( ICurve* pCrv1, ICurve* pCrv2, int& nRes) ;
|
|
static bool VerifyAndAdjustExternalAngle( ICurve* pCrv1, ICurve* pCrv2, double dAngDeg, double dDist, int nType,
|
|
CurveComposite& ccAux) ;
|
|
static double GetMinDist( double dCoeff, ICurve* pCrv1, ICurve* pCrv2) ;
|
|
static bool IsFillet( ICurve* pCrv, double dDist) ;
|
|
static bool ModifyFillet( ICurve* pCrv, double dDist, int nType, CurveComposite& ccAux) ;
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
OffsetCurve::~OffsetCurve( void)
|
|
{
|
|
Reset() ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
OffsetCurve::Reset( void)
|
|
{
|
|
for ( auto& pCrv : m_CrvLst) {
|
|
if ( pCrv != nullptr) {
|
|
delete pCrv ;
|
|
pCrv = nullptr ;
|
|
}
|
|
}
|
|
m_CrvLst.clear() ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
OffsetCurve::Make( const ICurve* pCrv, double dDist, int nType)
|
|
{
|
|
// pulisco tutto
|
|
Reset() ;
|
|
// verifico che la curva esista
|
|
if ( pCrv == nullptr)
|
|
return false ;
|
|
// verifico se la curva è un segmento di retta
|
|
bool bIsLine = false ;
|
|
const CurveLine* pLine = GetBasicCurveLine( pCrv) ;
|
|
if ( pLine != nullptr)
|
|
bIsLine = true ;
|
|
else {
|
|
const CurveComposite* pCompo = GetBasicCurveComposite( pCrv) ;
|
|
Point3d ptStart, ptEnd ;
|
|
if ( pCompo != nullptr && pCompo->IsALine( 10 * EPS_SMALL, ptStart, ptEnd))
|
|
bIsLine = true ;
|
|
}
|
|
// verifico che la curva sia piana
|
|
Plane3d plPlane ;
|
|
if ( ! pCrv->IsFlat( plPlane, bIsLine, 10 * EPS_SMALL))
|
|
return false ;
|
|
// recupero o assegno estrusione
|
|
Vector3d vtExtr ;
|
|
if ( ! pCrv->GetExtrusion( vtExtr) || vtExtr.IsSmall()) {
|
|
vtExtr = plPlane.GetVersN() ;
|
|
if ( vtExtr.IsZminus())
|
|
vtExtr.Invert() ;
|
|
}
|
|
// recupero normale al piano della curva
|
|
Vector3d vtNorm = plPlane.GetVersN() ;
|
|
if ( vtNorm * vtExtr < 0)
|
|
vtNorm.Invert() ;
|
|
// verifico che estrusione non sia troppo vicina al piano della curva (almeno 30 gradi di angolazione)
|
|
double dExtrOnN = vtExtr * vtNorm ;
|
|
if ( dExtrOnN < 0.5)
|
|
return false ;
|
|
// recupero spessore
|
|
double dThick ;
|
|
pCrv->GetThickness( dThick) ;
|
|
|
|
// se offset nullo, copio la curva ed esco
|
|
if ( abs( dDist) < 10 * EPS_SMALL) {
|
|
PtrOwner< CurveComposite> pCopy( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCopy) || ! pCopy->CopyFrom( pCrv))
|
|
return false ;
|
|
// assegno estrusione e spessore come curva originale
|
|
pCopy->SetExtrusion( vtExtr) ;
|
|
pCopy->SetThickness( dThick) ;
|
|
// unisco parti allineate (tranne gli estremi)
|
|
pCopy->MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG, false) ;
|
|
// sposto in lista
|
|
m_CrvLst.push_back( Release( pCopy)) ;
|
|
return true ;
|
|
}
|
|
|
|
// determino se necessario cambiare riferimento ( dal vettore estrusione)
|
|
bool bNeedRef = ( ! vtExtr.IsZplus()) ;
|
|
|
|
// determino se necessario effettuare scalatura
|
|
bool bNeedScale = ( abs( dExtrOnN) < cos( 0.1 * DEGTORAD)) ;
|
|
|
|
// creo una copia della curva
|
|
CurveComposite ccCopy ;
|
|
if ( ! ccCopy.CopyFrom( pCrv))
|
|
return false ;
|
|
ccCopy.SetExtrusion( vtExtr) ;
|
|
|
|
// se necessario cambio il riferimento
|
|
Frame3d frCopy ;
|
|
if ( bNeedScale) {
|
|
// calcolo il riferimento con vtNorm come asse Z e componente di vtExtr perpendicolare a vtNorm come asse X
|
|
if ( ! frCopy.Set( ORIG, vtNorm, vtExtr - dExtrOnN * vtNorm))
|
|
return false ;
|
|
ccCopy.SetExtrusion( vtNorm) ;
|
|
// esprimo la curva in questo riferimento
|
|
ccCopy.ToLoc( frCopy) ;
|
|
// scalo lungo l'asse X locale
|
|
ccCopy.Scale( GLOB_FRM, dExtrOnN, 1, 1) ;
|
|
}
|
|
else if ( bNeedRef) {
|
|
// calcolo il riferimento OCS con VtExtr come asse Z
|
|
if ( ! frCopy.Set( ORIG, vtExtr))
|
|
return false ;
|
|
// esprimo la curva in questo riferimento
|
|
ccCopy.ToLoc( frCopy) ;
|
|
}
|
|
|
|
// 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 ;
|
|
|
|
// verifico se curva chiusa e non forzata aperta
|
|
bool bClosed = ccCopy.IsClosed() && ( nType & ICurve::OFF_FORCE_OPEN) == 0 ;
|
|
|
|
// elimino eventuali piccole Z
|
|
if ( ! RemoveCurveSmallZs( &ccCopy, 10 * EPS_SMALL))
|
|
return false ;
|
|
|
|
// converto archi diritti in segmenti di retta
|
|
const double ANG_CEN_MAX = 0.5 ;
|
|
if ( ! ccCopy.StraightArcsToLines( 2 * EPS_SMALL, ANG_CEN_MAX))
|
|
return false ;
|
|
|
|
// unisco parti allineate (tranne gli estremi)
|
|
if ( ! ccCopy.MergeCurves( 10 * EPS_SMALL, ANG_TOL_STD_DEG, false))
|
|
return false ;
|
|
|
|
// calcolo le lunghezze delle diverse entità
|
|
DBLVECTOR vLens ;
|
|
{
|
|
const ICurve* pCrv1 = ccCopy.GetFirstCurve() ;
|
|
while ( pCrv1 != nullptr) {
|
|
double dLen ;
|
|
if ( ! pCrv1->GetLength( dLen))
|
|
return false ;
|
|
vLens.push_back( dLen) ;
|
|
pCrv1 = ccCopy.GetNextCurve() ;
|
|
}
|
|
}
|
|
// calcolo gli angoli tra le diverse entità
|
|
DBLVECTOR vAngs ;
|
|
{
|
|
vAngs.push_back( 0) ;
|
|
const ICurve* pCrv1 = ccCopy.GetFirstCurve() ;
|
|
const ICurve* pCrv2 = ccCopy.GetNextCurve() ;
|
|
while ( pCrv2 != nullptr) {
|
|
double dAngDeg ;
|
|
if ( ! CalcAngle( pCrv1, pCrv2, dAngDeg))
|
|
return false ;
|
|
vAngs.push_back( dAngDeg) ;
|
|
pCrv1 = pCrv2 ;
|
|
pCrv2 = ccCopy.GetNextCurve() ;
|
|
}
|
|
// se chiusa, calcolo angolo da ultima a prima
|
|
if ( bClosed) {
|
|
pCrv2 = ccCopy.GetFirstCurve() ;
|
|
double dAngDeg ;
|
|
if ( ! CalcAngle( pCrv1, pCrv2, dAngDeg))
|
|
return false ;
|
|
vAngs.push_back( dAngDeg) ;
|
|
vAngs[0] = dAngDeg ;
|
|
}
|
|
else
|
|
vAngs.push_back( 0) ;
|
|
}
|
|
|
|
// primo passo : estraggo entità dalla copia, loro offset elementare e aggiunta raccordi esterni (sempre fillet)
|
|
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 offsetto
|
|
PtrOwner<ICurve> pCrv1( ccCopy2.RemoveFirstOrLastCurve( false)) ;
|
|
if ( IsNull( pCrv1))
|
|
return false ;
|
|
pCrv1->SetTempProp( nInd1) ;
|
|
if ( ! pCrv1->SimpleOffset( dDist, ICurve::OFF_FILLET)) {
|
|
CurveArc* pArc = GetBasicCurveArc( pCrv1) ;
|
|
if ( pArc == nullptr)
|
|
return false ;
|
|
if ( pArc->MyExtendedOffset( dDist, true, ICurve::OFF_FILLET))
|
|
pCrv1->SetTempProp( - nInd1) ;
|
|
}
|
|
// curve successive
|
|
PtrOwner<ICurve> pCrv2( ccCopy2.RemoveFirstOrLastCurve( false)) ;
|
|
while ( ! IsNull( pCrv2)) {
|
|
// eseguo semplice offset
|
|
pCrv2->SetTempProp( nInd1 + 1) ;
|
|
if ( ! pCrv2->SimpleOffset( dDist, ICurve::OFF_FILLET)) {
|
|
CurveArc* pArc = GetBasicCurveArc( pCrv2) ;
|
|
if ( pArc == nullptr)
|
|
return false ;
|
|
if ( pArc->MyExtendedOffset( dDist, true, ICurve::OFF_FILLET))
|
|
pCrv2->SetTempProp( - ( nInd1 + 1)) ;
|
|
}
|
|
// verifico relazione con la curva precedente e aggiungo eventuali curve intermedie
|
|
CurveComposite ccTemp ;
|
|
VerifyAndAdjustExternalAngle( pCrv1, pCrv2, vAngs[nInd1], dDist, ICurve::OFF_FILLET, ccTemp) ;
|
|
// metto in lista curva precedente
|
|
m_CrvLst.push_back( Release( pCrv1)) ;
|
|
// se aggiunto qualcosa, lo metto in lista
|
|
if ( ccTemp.GetCurveCount() > 0) {
|
|
PtrOwner<ICurve> pCrv3( ccTemp.RemoveFirstOrLastCurve( false)) ;
|
|
while ( ! IsNull( pCrv3)) {
|
|
m_CrvLst.push_back( Release( pCrv3)) ;
|
|
pCrv3.Set( ccTemp.RemoveFirstOrLastCurve( false)) ;
|
|
}
|
|
}
|
|
// aggiorno curva precedente
|
|
++ nInd1 ;
|
|
pCrv1.Set( 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)) ;
|
|
|
|
// se originale chiuso, devo confrontare anche ultima e prima curva
|
|
if ( bClosed && m_CrvLst.size() >= 2) {
|
|
// la curva precedente è l'ultima dell'offset
|
|
ICurve* pCrv1 = m_CrvLst.back() ;
|
|
// la curva successiva ora è la prima dell'offset
|
|
ICurve* pCrv2 = m_CrvLst.front() ;
|
|
// verifico relazione con la curva precedente e aggiungo eventuali curve intermedie
|
|
CurveComposite ccTemp ;
|
|
VerifyAndAdjustExternalAngle( pCrv1, pCrv2, vAngs[nInd1], dDist, ICurve::OFF_FILLET, ccTemp) ;
|
|
// se aggiunto qualcosa, lo metto in lista
|
|
if ( ccTemp.GetCurveCount() > 0) {
|
|
PtrOwner<ICurve> pCrv3( ccTemp.RemoveFirstOrLastCurve( false)) ;
|
|
while ( ! IsNull( pCrv3)) {
|
|
m_CrvLst.push_back( Release( pCrv3)) ;
|
|
pCrv3.Set( ccTemp.RemoveFirstOrLastCurve( false)) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// aggiorno lunghezza segmenti di retta (possono essere stati allungati)
|
|
for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ; ++ iIter) {
|
|
const ICurve* pCrv = *iIter ;
|
|
if ( pCrv->GetType() == CRV_LINE) {
|
|
double dLen ; pCrv->GetLength( dLen) ;
|
|
int nInd = abs( pCrv->GetTempProp()) - 1 ;
|
|
if ( nInd >= 0 && nInd < int( vLens.size()))
|
|
vLens[nInd] = dLen ;
|
|
}
|
|
}
|
|
|
|
// secondo passo : eliminazione archi invertiti e rette impossibili
|
|
for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ;) {
|
|
ICurve* pCrv = *iIter ;
|
|
// controllo e rimuovo archi invertiti o nulli
|
|
if ( pCrv->GetTempProp() < 0 ||
|
|
( pCrv->GetType() == CRV_ARC && GetBasicCurveArc(pCrv)->GetRadius() < EPS_SMALL)) {
|
|
delete pCrv ;
|
|
pCrv = nullptr ;
|
|
iIter = m_CrvLst.erase( iIter) ;
|
|
continue ;
|
|
}
|
|
// controllo e rimuovo rette destinate all'eliminazione (da Voronoi con adiacenti)
|
|
if ( pCrv->GetType() == CRV_LINE) {
|
|
int nInd1 = abs( pCrv->GetTempProp()) ;
|
|
// verifico se angoli precedente e successivo interni con rette di lunghezza non inferiore
|
|
bool bPrevInt = PreviousIsLine( iIter, m_CrvLst, bClosed) &&
|
|
PreviousIsLonger( nInd1, vLens, bClosed) &&
|
|
( ( dDist < 0 && vAngs[nInd1-1] > 0) || ( dDist > 0 && vAngs[nInd1-1] < 0)) ;
|
|
bool bNextInt = NextIsLine( iIter, m_CrvLst, bClosed) &&
|
|
NextIsLonger( nInd1, vLens, bClosed) &&
|
|
( ( dDist < 0 && vAngs[nInd1] > 0) || ( dDist > 0 && vAngs[nInd1] < 0)) ;
|
|
// calcolo la massima estensione di offset (Voronoi con entità adiacenti)
|
|
double dMaxDist = INFINITO ;
|
|
if ( bPrevInt && bNextInt) {
|
|
double dTgA = tan( 0.5 * ( ANG_STRAIGHT - abs( vAngs[nInd1-1])) * DEGTORAD) ;
|
|
double dTgB = tan( 0.5 * ( ANG_STRAIGHT - abs( vAngs[nInd1])) * DEGTORAD) ;
|
|
dMaxDist = dTgA * dTgB / ( dTgA + dTgB) * vLens[nInd1-1] ;
|
|
}
|
|
else if ( bPrevInt) {
|
|
double dTgA = tan( 0.5 * ( ANG_STRAIGHT - abs( vAngs[nInd1-1])) * DEGTORAD) ;
|
|
dMaxDist = dTgA * vLens[nInd1-1] ;
|
|
}
|
|
else if ( bNextInt) {
|
|
double dTgB = tan( 0.5 * ( ANG_STRAIGHT - abs( vAngs[nInd1])) * DEGTORAD) ;
|
|
dMaxDist = dTgB * vLens[nInd1-1] ;
|
|
}
|
|
if ( abs( dDist) > dMaxDist + 5 * EPS_SMALL) {
|
|
delete pCrv ;
|
|
iIter = m_CrvLst.erase( iIter) ;
|
|
continue ;
|
|
}
|
|
}
|
|
// passo al successivo
|
|
++ iIter ;
|
|
}
|
|
|
|
// terzo passo : taglio angoli 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) {
|
|
int nRes ;
|
|
if ( VerifyAndAdjustSamePoint( pPrev, pCrv, nRes)) {
|
|
if ( nRes == 4)
|
|
VerifyAndAdjustInternalAngle( pPrev, pCrv, nRes) ;
|
|
// precedente da cancellare
|
|
if ( ( nRes & 1) != 0) {
|
|
auto iErase = prev( iIter != m_CrvLst.begin() ? iIter : m_CrvLst.end()) ;
|
|
delete pPrev ;
|
|
pPrev = nullptr ;
|
|
m_CrvLst.erase( iErase) ;
|
|
if ( m_CrvLst.empty())
|
|
break ;
|
|
auto iPrev = prev( iIter != m_CrvLst.begin() ? iIter : m_CrvLst.end()) ;
|
|
pPrev = *iPrev ;
|
|
}
|
|
// corrente da cancellare
|
|
if ( ( nRes & 2) != 0) {
|
|
delete pCrv ;
|
|
pCrv = nullptr ;
|
|
iIter = m_CrvLst.erase( iIter) ;
|
|
}
|
|
// se eseguita una cancellazione, non devo incrementare
|
|
if ( ( nRes & 3) != 0)
|
|
continue ;
|
|
}
|
|
}
|
|
// passo al successivo
|
|
pPrev = pCrv ;
|
|
++ iIter ;
|
|
}
|
|
|
|
// quarto 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 curva troppo piccola la salto
|
|
double dCrvLen ;
|
|
if ( ! pCrv->GetLength( dCrvLen)) {
|
|
delete( pCrv) ;
|
|
pCrv = nullptr ;
|
|
continue ;
|
|
}
|
|
// 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() ;
|
|
|
|
// 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()) ;
|
|
|
|
// quinto 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
|
|
while ( ! 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) ;
|
|
|
|
// sesto passo : se curva aperta, elimino i tratti che stanno nella circonferenza di offset dei punti estremi
|
|
if ( ! bClosed) {
|
|
// ciconferenza sull'estremità iniziale
|
|
Point3d ptStart ; ccCopy.GetStartPoint( ptStart) ;
|
|
PtrOwner<CurveArc> pCircS( CreateBasicCurveArc()) ;
|
|
if ( IsNull( pCircS) || ! pCircS->Set( ptStart, Z_AX, abs( dDist)))
|
|
return false ;
|
|
// elimino le parti di offset interne a questa circonferenza
|
|
for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ;) {
|
|
// recupero la curva e il suo dominio
|
|
ICurve* pCrv = *iIter ;
|
|
double dStart, dEnd ;
|
|
if ( ! pCrv->GetDomain( dStart, dEnd))
|
|
return false ;
|
|
// determino gli intervalli da conservare (inizializzo come tutta la curva per poi rimuovere la parte finale)
|
|
Intervals inOk( EPS_PARAM) ;
|
|
inOk.Set( dStart, dEnd) ;
|
|
IntersCurveCurve ccInt( *pCrv, *pCircS) ;
|
|
CRVCVECTOR ccPart ;
|
|
if ( ! ccInt.GetCurveClassification( 0, EPS_SMALL, ccPart))
|
|
return false ;
|
|
for ( auto& ccOne : ccPart) {
|
|
switch ( ccOne.nClass) {
|
|
case CRVC_IN :
|
|
// intervallo standard
|
|
if ( ccOne.dParS < ccOne.dParE - EPS_PARAM)
|
|
inOk.Subtract( ccOne.dParS, ccOne.dParE) ;
|
|
// intervallo che attraversa punto di chiusura della curva
|
|
else {
|
|
inOk.Subtract( ccOne.dParS, dEnd) ;
|
|
inOk.Subtract( dStart, ccOne.dParE) ;
|
|
}
|
|
break ;
|
|
case CRVC_NULL :
|
|
return false ;
|
|
}
|
|
}
|
|
// eseguo trim (solo della parte iniziale)
|
|
double dMin, dMax ;
|
|
if ( inOk.GetLast( dMin, dMax)) {
|
|
if ( dMin > dStart + 100 * EPS_PARAM)
|
|
pCrv->TrimStartAtParam( dMin) ;
|
|
}
|
|
else {
|
|
delete pCrv ;
|
|
iIter = m_CrvLst.erase( iIter) ;
|
|
continue ;
|
|
}
|
|
// passo alla successiva
|
|
++ iIter ;
|
|
}
|
|
// circonferenza sull'estremità finale
|
|
Point3d ptEnd ; ccCopy.GetEndPoint( ptEnd) ;
|
|
PtrOwner<CurveArc> pCircE( CreateBasicCurveArc()) ;
|
|
if ( IsNull( pCircE) || ! pCircE->Set( ptEnd, Z_AX, abs( dDist)))
|
|
return false ;
|
|
// elimino le parti di offset interna a questa circonferenza
|
|
for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ;) {
|
|
// recupero la curva e il suo dominio
|
|
ICurve* pCrv = *iIter ;
|
|
double dStart, dEnd ;
|
|
if ( ! pCrv->GetDomain( dStart, dEnd))
|
|
return false ;
|
|
// determino gli intervalli da conservare (inizializzo come tutta la curva per poi poter rimuovere parti)
|
|
Intervals inOk( EPS_PARAM) ;
|
|
inOk.Set( dStart, dEnd) ;
|
|
IntersCurveCurve ccInt( *pCrv, *pCircE) ;
|
|
CRVCVECTOR ccPart ;
|
|
if ( ! ccInt.GetCurveClassification( 0, EPS_SMALL, ccPart))
|
|
return false ;
|
|
for ( auto& ccOne : ccPart) {
|
|
switch ( ccOne.nClass) {
|
|
case CRVC_IN :
|
|
// intervallo standard
|
|
if ( ccOne.dParS < ccOne.dParE - EPS_PARAM)
|
|
inOk.Subtract( ccOne.dParS, ccOne.dParE) ;
|
|
// intervallo che attraversa punto di chiusura della curva
|
|
else {
|
|
inOk.Subtract( ccOne.dParS, dEnd) ;
|
|
inOk.Subtract( dStart, ccOne.dParE) ;
|
|
}
|
|
break ;
|
|
case CRVC_NULL :
|
|
return false ;
|
|
}
|
|
}
|
|
// eseguo trim (solo della parte finale)
|
|
double dMin, dMax ;
|
|
if ( inOk.GetFirst( dMin, dMax)) {
|
|
if ( dMax < dEnd - 100 * EPS_PARAM)
|
|
pCrv->TrimEndAtParam( dMax) ;
|
|
}
|
|
else {
|
|
delete pCrv ;
|
|
iIter = m_CrvLst.erase( iIter) ;
|
|
continue ;
|
|
}
|
|
// passo alla successiva
|
|
++ iIter ;
|
|
}
|
|
}
|
|
|
|
// settimo 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 ( GetMinDist( 0.5, pCrv, &ccCopy) < abs( dDist) - 5 * EPS_SMALL ||
|
|
GetMinDist( 0.25, pCrv, &ccCopy) < abs( dDist) - 5 * EPS_SMALL ||
|
|
GetMinDist( 0.75, pCrv, &ccCopy) < abs( dDist) - 5 * EPS_SMALL ||
|
|
GetMinDist( 0.125, pCrv, &ccCopy) < abs( dDist) - 5 * EPS_SMALL ||
|
|
GetMinDist( 0.875, pCrv, &ccCopy) < abs( dDist) - 5 * EPS_SMALL ||
|
|
GetMinDist( 0.0625, pCrv, &ccCopy) < abs( dDist) - 5 * EPS_SMALL ||
|
|
GetMinDist( 0.9375, pCrv, &ccCopy) < abs( dDist) - 5 * EPS_SMALL) {
|
|
delete pCrv ;
|
|
iIter = m_CrvLst.erase( iIter) ;
|
|
continue ;
|
|
}
|
|
// passo alla successiva
|
|
++ iIter ;
|
|
}
|
|
|
|
// ottavo 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)) {
|
|
if ( pCrvCo->AddCurve( pCrvCo2, true, 10 * EPS_SMALL))
|
|
ptEnd = ptEnd2 ;
|
|
m_CrvLst.erase( iIter2) ;
|
|
iIter2 = next( iIter) ;
|
|
}
|
|
else if ( AreSamePointEpsilon( ptEnd2, ptStart, 10 * EPS_SMALL)) {
|
|
if ( pCrvCo->AddCurve( pCrvCo2, false, 10 * EPS_SMALL))
|
|
ptStart = ptStart2 ;
|
|
m_CrvLst.erase( iIter2) ;
|
|
iIter2 = next( iIter) ;
|
|
}
|
|
else
|
|
++ iIter2 ;
|
|
}
|
|
++ iIter ;
|
|
}
|
|
|
|
// nono passo : se con smusso o estensione, sostituisco i fillet con questi
|
|
if ( ( nType & ICurve::OFF_CHAMFER) != 0 || ( nType & ICurve::OFF_EXTEND) != 0) {
|
|
for ( auto iIter = m_CrvLst.begin() ; iIter != m_CrvLst.end() ; ++ iIter) {
|
|
// processo le curve semplici della composita
|
|
CurveComposite* pCrvCo = GetBasicCurveComposite( *iIter) ;
|
|
ICURVEPLIST CrvLst ;
|
|
PtrOwner<ICurve> pCrv( pCrvCo->RemoveFirstOrLastCurve( false)) ;
|
|
while ( ! IsNull( pCrv)) {
|
|
// se fillet lo trasformo in smusso o estensione
|
|
if ( IsFillet( pCrv, dDist)) {
|
|
CurveComposite ccTemp ;
|
|
ModifyFillet( pCrv, dDist, nType, ccTemp) ;
|
|
// metto in lista le curve risultanti
|
|
if ( ccTemp.GetCurveCount() > 0) {
|
|
PtrOwner<ICurve> pCrv2( ccTemp.RemoveFirstOrLastCurve( false)) ;
|
|
while ( ! IsNull( pCrv2)) {
|
|
CrvLst.push_back( Release( pCrv2)) ;
|
|
pCrv2.Set( ccTemp.RemoveFirstOrLastCurve( false)) ;
|
|
}
|
|
}
|
|
}
|
|
// altrimenti salvo in lista
|
|
else
|
|
CrvLst.push_back( Release( pCrv)) ;
|
|
// passo alla curva successiva
|
|
pCrv.Set( pCrvCo->RemoveFirstOrLastCurve( false)) ;
|
|
}
|
|
// rimetto le curve nella composita
|
|
for ( auto pCrv : CrvLst) {
|
|
pCrvCo->AddCurve( pCrv) ;
|
|
}
|
|
// unisco tratti allineati
|
|
pCrvCo->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG) ;
|
|
}
|
|
}
|
|
|
|
// riporto il risultato nel riferimento originale
|
|
if ( bNeedScale) {
|
|
for ( auto pCrv : m_CrvLst) {
|
|
pCrv->Scale( GLOB_FRM, 1 / dExtrOnN, 1, 1) ;
|
|
pCrv->ToGlob( frCopy) ;
|
|
}
|
|
}
|
|
else if ( bNeedRef) {
|
|
for ( auto pCrv : m_CrvLst)
|
|
pCrv->ToGlob( frCopy) ;
|
|
}
|
|
|
|
// assegno estrusione e spessore come curva originale
|
|
for ( auto pCrv : m_CrvLst) {
|
|
pCrv->SetExtrusion( vtExtr) ;
|
|
pCrv->SetThickness( dThick) ;
|
|
}
|
|
|
|
// ordino le curve in ordine decrescente di lunghezza
|
|
if ( m_CrvLst.size() > 1) {
|
|
for ( auto pCrv : m_CrvLst) {
|
|
double dLen ;
|
|
if ( pCrv->GetLength( dLen))
|
|
pCrv->SetTempProp( int( 1000 * dLen)) ;
|
|
else
|
|
pCrv->SetTempProp( 0) ;
|
|
}
|
|
m_CrvLst.sort( []( const ICurve* pA, const ICurve* pB) { return ( pA->GetTempProp() > pB->GetTempProp()) ; }) ;
|
|
}
|
|
|
|
// elimino eventuali curve non uniche molto corte e aperte
|
|
while ( ( m_CrvLst.size() > 1)) {
|
|
ICurve* pCrv = m_CrvLst.back() ;
|
|
if ( ! pCrv->IsClosed() && pCrv->GetTempProp() < 1000 * 50 * EPS_SMALL) {
|
|
delete( pCrv) ;
|
|
m_CrvLst.pop_back() ;
|
|
}
|
|
else
|
|
break ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ICurve*
|
|
OffsetCurve::GetCurve( void)
|
|
{
|
|
return GetLongerCurve() ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ICurve*
|
|
OffsetCurve::GetLongerCurve( void)
|
|
{
|
|
if ( m_CrvLst.empty())
|
|
return nullptr ;
|
|
// le curve sono ordinate in senso decrescente di lunghezza
|
|
ICurve* pCrv = m_CrvLst.front() ;
|
|
m_CrvLst.pop_front() ;
|
|
return pCrv ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ICurve*
|
|
OffsetCurve::GetShorterCurve( void)
|
|
{
|
|
if ( m_CrvLst.empty())
|
|
return nullptr ;
|
|
// le curve sono ordinate in senso decrescente di lunghezza
|
|
ICurve* pCrv = m_CrvLst.back() ;
|
|
m_CrvLst.pop_back() ;
|
|
return pCrv ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
PreviousIsLine( ICURVEPLIST::const_iterator iIter, const ICURVEPLIST& CrvLst, bool bClosed)
|
|
{
|
|
// verifico non sia il primo
|
|
if ( iIter == CrvLst.begin())
|
|
return false ;
|
|
// passo al precedente
|
|
auto iPrev = prev( iIter) ;
|
|
// se non esiste e curva chiusa prendo l'ultimo
|
|
if ( iPrev == CrvLst.end() && bClosed)
|
|
-- iPrev ;
|
|
// se non esiste o non è una linea, test fallito
|
|
if ( iPrev == CrvLst.end() || (*iPrev)->GetType() != CRV_LINE )
|
|
return false ;
|
|
// test superato
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
PreviousIsLonger( int nInd1, const DBLVECTOR& vLens, bool bClosed)
|
|
{
|
|
// massimo indice nel vettore
|
|
int nMax = int( vLens.size()) - 1 ;
|
|
// verifico validità indice (questo indice è incrementato di 1)
|
|
if ( nInd1 < 1 || nInd1 > nMax + 1)
|
|
return false ;
|
|
// indice del precedente nel vettore di lunghezze
|
|
int nPrec = nInd1 - 2 ;
|
|
// se indice sotto minimo e chiuso, uso il finale
|
|
if ( nPrec < 0 && bClosed)
|
|
nPrec = nMax ;
|
|
// verifica finale dell'indice
|
|
if ( nPrec < 0)
|
|
return false ;
|
|
// eseguo confronto
|
|
return ( vLens[nPrec] > vLens[nInd1-1] - EPS_SMALL) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
NextIsLine( ICURVEPLIST::const_iterator iIter, const ICURVEPLIST& CrvLst, bool bClosed)
|
|
{
|
|
// passo al successivo
|
|
auto iNext = next( iIter) ;
|
|
// se non esiste e curva chiusa prendo il primo
|
|
if ( iNext == CrvLst.end() && bClosed)
|
|
iNext = CrvLst.begin() ;
|
|
// se non esiste o non è una linea, test fallito
|
|
if ( iNext == CrvLst.end() || (*iNext)->GetType() != CRV_LINE )
|
|
return false ;
|
|
// test superato
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
NextIsLonger( int nInd1, const DBLVECTOR& vLens, bool bClosed)
|
|
{
|
|
// massimo indice nel vettore
|
|
int nMax = int( vLens.size()) - 1 ;
|
|
// verifico validità indice (questo indice è incrementato di 1)
|
|
if ( nInd1 < 1 || nInd1 > nMax + 1)
|
|
return false ;
|
|
// indice del successivo nel vettore di lunghezze
|
|
int nNext = nInd1 ;
|
|
// se indice oltre il massimo e chiuso, uso l'iniziale
|
|
if ( nNext > nMax && bClosed)
|
|
nNext = 0 ;
|
|
// verifica finale dell'indice
|
|
if ( nNext > nMax)
|
|
return false ;
|
|
// eseguo confronto
|
|
return ( vLens[nNext] > vLens[nInd1-1] - EPS_SMALL) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CalcAngle( const ICurve* pCrv1, const ICurve* pCrv2, double& dAngDeg)
|
|
{
|
|
// calcolo direzioni tangenti sull'estremo in comune
|
|
Vector3d vtDir1, vtDir2 ;
|
|
if ( ! pCrv1->GetEndDir( vtDir1) || ! pCrv2->GetStartDir( vtDir2))
|
|
return false ;
|
|
if ( ! vtDir1.Normalize() || ! vtDir2.Normalize())
|
|
return false ;
|
|
|
|
// calcolo l'angolo di rotazione dalla prima direzione alla seconda
|
|
if ( ! vtDir1.GetAngleXY( vtDir2, dAngDeg))
|
|
return false ;
|
|
// se vicino all'angolo piatto, si devono ricalcolare spostandosi un poco
|
|
if ( abs( dAngDeg) > ( ANG_STRAIGHT - EPS_ANG_SMALL)) {
|
|
// eseguo calcolo spostato
|
|
double dU1 = 1 ;
|
|
double dU2 = 0 ;
|
|
Point3d ptDummy ;
|
|
Vector3d vtDir1b ;
|
|
Vector3d vtDir2b ;
|
|
if ( ! MoveParamToAvoidTg( dU1, ICurve::FROM_MINUS, *pCrv1) ||
|
|
! pCrv1->GetPointTang( dU1, ICurve::FROM_MINUS, ptDummy, vtDir1b) ||
|
|
! MoveParamToAvoidTg( dU2, ICurve::FROM_PLUS, *pCrv2) ||
|
|
! pCrv2->GetPointTang( dU2, ICurve::FROM_PLUS, ptDummy, vtDir2b))
|
|
return false ;
|
|
if ( ! vtDir1b.GetAngleXY( vtDir2b, dAngDeg))
|
|
return false ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
VerifyAndAdjustSamePoint( ICurve* pCrv1, ICurve* pCrv2, int& nRes)
|
|
{
|
|
// 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)) {
|
|
nRes = 4 ;
|
|
return true ;
|
|
}
|
|
// se coincidono esattamente, va bene così
|
|
if ( AreSamePointExact( ptP1, ptP2)) {
|
|
nRes = 0 ;
|
|
return true ;
|
|
}
|
|
// sono in tolleranza, ma devo ricongiungere gli estremi
|
|
Point3d ptS1, ptE2 ;
|
|
if ( ! pCrv1->GetStartPoint( ptS1) || ! pCrv2->GetEndPoint( ptE2))
|
|
return false ;
|
|
nRes = 0 ;
|
|
Point3d ptMid = 0.5 * ( ptP1 + ptP2) ;
|
|
if ( AreSamePointApprox( ptS1, ptMid) || ! pCrv1->ModifyEnd( ptMid))
|
|
nRes += 1 ;
|
|
if ( AreSamePointApprox( ptMid, ptE2) || ! pCrv2->ModifyStart( ptMid))
|
|
nRes += 2 ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
VerifyAndAdjustInternalAngle( ICurve* pCrv1, ICurve* pCrv2, int& nRes)
|
|
{
|
|
// 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 ptS1, ptE2 ;
|
|
if ( ! pCrv1->GetStartPoint( ptS1) || ! pCrv2->GetEndPoint( ptE2))
|
|
return false ;
|
|
nRes = 0 ;
|
|
Point3d ptNew = 0.5 * ( ptNew1 + ptNew2) ;
|
|
if ( AreSamePointApprox( ptS1, ptNew) || ! pCrv1->ModifyEnd( ptNew))
|
|
nRes += 1 ;
|
|
if ( AreSamePointApprox( ptNew, ptE2) || ! pCrv2->ModifyStart( ptNew))
|
|
nRes += 2 ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
VerifyAndAdjustExternalAngle( ICurve* pCrv1, ICurve* pCrv2, double dAngDeg, double dDist, int nType,
|
|
CurveComposite& ccAux)
|
|
{
|
|
// verifica dei puntatori
|
|
if ( pCrv1 == nullptr || pCrv2 == nullptr || &ccAux == nullptr)
|
|
return false ;
|
|
|
|
// pulisco la curva ausiliaria
|
|
ccAux.Clear() ;
|
|
|
|
// elimino dal tipo le parti estranee all'angolo esterno
|
|
nType &= ( ICurve::OFF_FILLET | ICurve::OFF_CHAMFER | ICurve::OFF_EXTEND) ;
|
|
|
|
// calcolo direzioni tangenti sull'estremo in comune
|
|
Vector3d vtDir1, vtDir2 ;
|
|
if ( ! pCrv1->GetEndDir( vtDir1) || ! pCrv2->GetStartDir( vtDir2))
|
|
return false ;
|
|
if ( ! vtDir1.Normalize( EPS_ZERO) || ! vtDir2.Normalize( EPS_ZERO))
|
|
return false ;
|
|
if ( pCrv1->GetTempProp() < 0) {
|
|
vtDir1.Invert() ;
|
|
vtDir2.Invert() ;
|
|
}
|
|
|
|
// verifico sia angolo esterno (accetto se entità quasi esattamente sovrapposte)
|
|
if ( abs( dAngDeg) < ( ANG_STRAIGHT - 10 * EPS_ANG_ZERO) &&
|
|
( ( dDist < 0 && dAngDeg > 0) ||
|
|
( dDist > 0 && dAngDeg < 0)))
|
|
return false ;
|
|
|
|
// se l'angolo esterno supera il retto, offset extend diventa offset chamfer
|
|
if ( nType == ICurve::OFF_EXTEND && abs( dAngDeg) > ANG_RIGHT + EPS_ANG_SMALL)
|
|
nType = ICurve::OFF_CHAMFER ;
|
|
|
|
// se angolo esterno molto piccolo, semplifico tutto
|
|
const double SMALL_EXT_ANG = 1.0 ;
|
|
bool bAngSmall = ( abs( dAngDeg) < SMALL_EXT_ANG) ;
|
|
if ( bAngSmall)
|
|
nType = ICurve::OFF_EXTEND ;
|
|
|
|
// congiungo le due curve
|
|
switch ( nType) {
|
|
case ICurve::OFF_FILLET :
|
|
{
|
|
Point3d ptP1, ptP2 ;
|
|
if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2))
|
|
return false ;
|
|
double dAngStart ;
|
|
vtDir1.ToSpherical( nullptr, nullptr, &dAngStart) ;
|
|
PtrOwner<CurveArc> pCrv( CreateBasicCurveArc()) ;
|
|
if ( IsNull( pCrv) || ! pCrv->Set2PD( ptP1, ptP2, dAngStart))
|
|
return false ;
|
|
// restituisco la curva
|
|
return ccAux.AddCurve( Release( pCrv)) ;
|
|
}
|
|
break ;
|
|
case ICurve::OFF_CHAMFER :
|
|
{
|
|
// lunghezza aggiuntiva in tangenza
|
|
double dLen = abs( dDist) * tan( abs( dAngDeg) / 4 * DEGTORAD) ;
|
|
// punti di costruzione smusso
|
|
Point3d ptP1, ptP1a, ptP2a, ptP2 ;
|
|
if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2))
|
|
return false ;
|
|
ptP1a = ptP1 + vtDir1 * dLen ;
|
|
ptP2a = ptP2 - vtDir2 * dLen ;
|
|
// se prima c'è linea posso allungarla
|
|
if ( pCrv1->GetType() == CRV_LINE)
|
|
pCrv1->ModifyEnd( ptP1a) ;
|
|
// altrimenti, devo aggiungere una nuova linea
|
|
else {
|
|
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
|
|
if ( IsNull( pCrv) || ! pCrv->Set( ptP1, ptP1a))
|
|
return false ;
|
|
if ( ! ccAux.AddCurve( Release( pCrv)))
|
|
return false ;
|
|
}
|
|
// tratto intermedio
|
|
{
|
|
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
|
|
if ( IsNull( pCrv) || ! pCrv->Set( ptP1a, ptP2a))
|
|
return false ;
|
|
if ( ! ccAux.AddCurve( Release( pCrv)))
|
|
return false ;
|
|
}
|
|
// se dopo c'è linea posso allungarla
|
|
if ( pCrv2->GetType() == CRV_LINE)
|
|
pCrv2->ModifyStart( ptP2a) ;
|
|
// altrimenti, devo aggiungere una nuova linea
|
|
else {
|
|
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
|
|
if ( IsNull( pCrv) || ! pCrv->Set( ptP2a, ptP2))
|
|
return false ;
|
|
if ( ! ccAux.AddCurve( Release( pCrv)))
|
|
return false ;
|
|
}
|
|
return true ;
|
|
}
|
|
break ;
|
|
case ICurve::OFF_EXTEND :
|
|
{
|
|
// lunghezza aggiuntiva in tangenza
|
|
double dLen = abs( dDist) * tan( abs( dAngDeg) / 2 * DEGTORAD) ;
|
|
// punti di costruzione estensione
|
|
Point3d ptP1, ptPc, ptP2 ;
|
|
if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2))
|
|
return false ;
|
|
ptPc = ptP1 + vtDir1 * dLen ;
|
|
// se prima c'è linea o angolo molto piccolo posso allungarla
|
|
if ( ( pCrv1->GetType() == CRV_LINE) || bAngSmall)
|
|
pCrv1->ModifyEnd( ptPc) ;
|
|
// altrimenti, devo aggiungere una nuova linea
|
|
else {
|
|
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
|
|
if ( IsNull( pCrv) || ! pCrv->Set( ptP1, ptPc))
|
|
return false ;
|
|
if ( ! ccAux.AddCurve( Release( pCrv)))
|
|
return false ;
|
|
}
|
|
// se dopo c'è linea o angolo molto piccolo posso allungarla
|
|
if ( ( pCrv2->GetType() == CRV_LINE) || bAngSmall)
|
|
pCrv2->ModifyStart( ptPc) ;
|
|
// altrimenti, devo aggiungere una nuova linea
|
|
else {
|
|
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
|
|
if ( IsNull( pCrv) || ! pCrv->Set( ptPc, ptP2))
|
|
return false ;
|
|
if ( ! ccAux.AddCurve( Release( pCrv)))
|
|
return false ;
|
|
}
|
|
return true ;
|
|
}
|
|
}
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
double
|
|
GetMinDist( double dCoeff, ICurve* pCrv1, ICurve* pCrv2)
|
|
{
|
|
double dUStart, dUEnd ;
|
|
pCrv1->GetDomain( dUStart, dUEnd) ;
|
|
Point3d ptP ;
|
|
pCrv1->GetPointD1D2( ( dCoeff * dUStart + ( 1 - dCoeff) * dUEnd), ICurve::FROM_MINUS, ptP) ;
|
|
double dMinDist ;
|
|
DistPointCurve dstPC( ptP, *pCrv2) ;
|
|
if ( ! dstPC.GetDist( dMinDist))
|
|
return 0 ;
|
|
return dMinDist ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
IsFillet( ICurve* pCrv, double dDist)
|
|
{
|
|
// deve essere un arco
|
|
if ( pCrv->GetType() != CRV_ARC)
|
|
return false ;
|
|
CurveArc* pArc = GetBasicCurveArc( pCrv) ;
|
|
// deve avere raggio uguale alla distanza di offset
|
|
if ( abs( pArc->GetRadius() - abs( dDist)) > EPS_SMALL)
|
|
return false ;
|
|
// deve essere CCW se offset a destra e CW se offset a sinistra
|
|
return ( pArc->GetAngCenter() * dDist > 0) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
ModifyFillet( ICurve* pCrv, double dDist, int nType, CurveComposite& ccAux)
|
|
{
|
|
// la curva deve essere un arco
|
|
CurveArc* pArc = GetBasicCurveArc( pCrv) ;
|
|
if ( pArc == nullptr)
|
|
return false ;
|
|
|
|
// angolo al centro dell'arco
|
|
double dAngDeg = pArc->GetAngCenter() ;
|
|
|
|
// elimino dal tipo le parti estranee all'angolo esterno
|
|
nType &= ( ICurve::OFF_FILLET | ICurve::OFF_CHAMFER | ICurve::OFF_EXTEND) ;
|
|
|
|
// se l'angolo esterno supera il retto, offset extend diventa offset chamfer
|
|
if ( nType == ICurve::OFF_EXTEND && abs( dAngDeg) > ANG_RIGHT + EPS_ANG_SMALL)
|
|
nType = ICurve::OFF_CHAMFER ;
|
|
|
|
// se angolo esterno molto piccolo, semplifico tutto
|
|
const double SMALL_EXT_ANG = 1.0 ;
|
|
bool bAngSmall = ( abs( dAngDeg) < SMALL_EXT_ANG) ;
|
|
if ( bAngSmall)
|
|
nType = ICurve::OFF_EXTEND ;
|
|
|
|
switch ( nType) {
|
|
case ICurve::OFF_CHAMFER :
|
|
{
|
|
// lunghezza aggiuntiva in tangenza
|
|
double dLen = abs( dDist) * tan( abs( dAngDeg) / 4 * DEGTORAD) ;
|
|
// punti di costruzione smusso
|
|
Point3d ptP1, ptP1a, ptP2a, ptP2 ;
|
|
if ( ! pArc->GetStartPoint( ptP1) || ! pArc->GetEndPoint( ptP2))
|
|
return false ;
|
|
Vector3d vtDir1, vtDir2 ;
|
|
if ( ! pArc->GetStartDir( vtDir1) || ! pArc->GetEndDir( vtDir2))
|
|
return false ;
|
|
ptP1a = ptP1 + vtDir1 * dLen ;
|
|
ptP2a = ptP2 - vtDir2 * dLen ;
|
|
// aggiungo una nuova linea
|
|
PtrOwner<CurveLine> pLine1( CreateBasicCurveLine()) ;
|
|
if ( IsNull( pLine1) || ! pLine1->Set( ptP1, ptP1a))
|
|
return false ;
|
|
if ( ! ccAux.AddCurve( Release( pLine1)))
|
|
return false ;
|
|
// tratto intermedio
|
|
PtrOwner<CurveLine> pLine2( CreateBasicCurveLine()) ;
|
|
if ( IsNull( pLine2) || ! pLine2->Set( ptP1a, ptP2a))
|
|
return false ;
|
|
if ( ! ccAux.AddCurve( Release( pLine2)))
|
|
return false ;
|
|
// aggiungo una nuova linea
|
|
PtrOwner<CurveLine> pLine3( CreateBasicCurveLine()) ;
|
|
if ( IsNull( pLine3) || ! pLine3->Set( ptP2a, ptP2))
|
|
return false ;
|
|
if ( ! ccAux.AddCurve( Release( pLine3)))
|
|
return false ;
|
|
return true ;
|
|
}
|
|
break ;
|
|
case ICurve::OFF_EXTEND :
|
|
{
|
|
// lunghezza aggiuntiva in tangenza
|
|
double dLen = abs( dDist) * tan( abs( dAngDeg) / 2 * DEGTORAD) ;
|
|
// punti di costruzione estensione
|
|
Point3d ptP1, ptPc, ptP2 ;
|
|
if ( ! pArc->GetStartPoint( ptP1) || ! pArc->GetEndPoint( ptP2))
|
|
return false ;
|
|
Vector3d vtDir1, vtDir2 ;
|
|
if ( ! pArc->GetStartDir( vtDir1) || ! pArc->GetEndDir( vtDir2))
|
|
return false ;
|
|
ptPc = ptP1 + vtDir1 * dLen ;
|
|
// aggiungo una nuova linea
|
|
PtrOwner<CurveLine> pLine1( CreateBasicCurveLine()) ;
|
|
if ( IsNull( pLine1) || ! pLine1->Set( ptP1, ptPc))
|
|
return false ;
|
|
if ( ! ccAux.AddCurve( Release( pLine1)))
|
|
return false ;
|
|
// aggiungo una nuova linea
|
|
PtrOwner<CurveLine> pLine2( CreateBasicCurveLine()) ;
|
|
if ( IsNull( pLine2) || ! pLine2->Set( ptPc, ptP2))
|
|
return false ;
|
|
if ( ! ccAux.AddCurve( Release( pLine2)))
|
|
return false ;
|
|
return true ;
|
|
}
|
|
}
|
|
return false ;
|
|
}
|