5bd5efafb1
- miglioria in AdjustLoops per gestire piccole autointersezioni - piccola correzione in RemoveCurveSmallParts.
325 lines
13 KiB
C++
325 lines
13 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2015-2020
|
|
//----------------------------------------------------------------------------
|
|
// File : AdjustLoops.cpp Data : 14.04.20 Versione : 2.2d2
|
|
// Contenuto : Implementazione funzione di sistemazione curve chiuse per
|
|
// poter formare loop validi.
|
|
//
|
|
//
|
|
// Modifiche : 02.11.15 DS Creazione modulo.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "GeoConst.h"
|
|
#include "CurveComposite.h"
|
|
#include "RemoveCurveDefects.h"
|
|
#include "AdjustLoops.h"
|
|
#include "/EgtDev/Include/EGkIntersCurves.h"
|
|
#include "/EgtDev/Include/EGkIntervals.h"
|
|
#include "/EgtDev/Include/EgtNumUtils.h"
|
|
#include "/EgtDev/Include/EgtPointerOwner.h"
|
|
#include <algorithm>
|
|
|
|
using namespace std ;
|
|
|
|
//-------------------------------------------------------
|
|
static bool
|
|
SortCrvCrvInfo( const IntCrvCrvInfo& aInfo1, const IntCrvCrvInfo& aInfo2)
|
|
{
|
|
// confronto i valori del primo punto della curva A
|
|
double dU1 = aInfo1.IciA[0].dU ;
|
|
double dU2 = aInfo2.IciA[0].dU ;
|
|
if ( abs( dU1 - dU2) < EPS_SMALL)
|
|
// se sono uguali confronto i valori del primo punto della curva B
|
|
return aInfo1.IciB[0].dU > aInfo2.IciB[0].dU ;
|
|
else
|
|
return dU1 < dU2 ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static bool
|
|
MyAdjustLoops( ICurve* pCurve, ICURVEPLIST& CrvLst)
|
|
{
|
|
// Pulisco lista di ritorno
|
|
CrvLst.clear() ;
|
|
|
|
// Acquisisco la curva
|
|
PtrOwner<ICurve> pMyCrv( pCurve) ;
|
|
|
|
// Verifico validità curva
|
|
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid() || ! pMyCrv->IsClosed())
|
|
return false ;
|
|
|
|
// --- la verifica ed eventuale rimozione va effettuata in un piano perpendicolare al vettore estrusione ---
|
|
|
|
// verifico il vettore estrusione
|
|
Vector3d vtExtr ;
|
|
pMyCrv->GetExtrusion( vtExtr) ;
|
|
bool bNeedRef = ( ! vtExtr.IsSmall() && ! vtExtr.IsZplus()) ;
|
|
|
|
// 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
|
|
pMyCrv->ToLoc( frExtr) ;
|
|
}
|
|
|
|
// Calcolo le auto intersezioni
|
|
SelfIntersCurve sintC( *pMyCrv) ;
|
|
// se non ci sono auto-intersezioni che si attraversano o che si toccano (anche con sovrapposizione), non devo fare alcunché
|
|
if ( sintC.GetCrossIntersCount() == 0 && sintC.GetTouchIntersCount() == 0) {
|
|
// riporto la curva nel riferimento originale
|
|
if ( bNeedRef)
|
|
pMyCrv->ToGlob( frExtr) ;
|
|
// la inserisco in lista
|
|
CrvLst.push_back( Release( pMyCrv)) ;
|
|
return true ;
|
|
}
|
|
|
|
// Determino intervallo complessivo della curva
|
|
double dStart, dEnd ;
|
|
if ( ! pMyCrv->GetDomain( dStart, dEnd))
|
|
return false ;
|
|
Intervals inOk( EPS_PARAM) ;
|
|
inOk.Set( dStart, dEnd) ;
|
|
|
|
// recupero tutte le info delle autointersezioni
|
|
IntCrvCrvInfo iccInfo ;
|
|
ICCIVECTOR vIccInfo( sintC.GetIntersCount()) ;
|
|
for ( int i = 0 ; sintC.GetIntCrvCrvInfo( i, iccInfo) ; ++ i)
|
|
vIccInfo[i]= iccInfo ;
|
|
// ordino il vettore
|
|
sort( vIccInfo.begin(), vIccInfo.end(), SortCrvCrvInfo) ;
|
|
|
|
// Tolgo le parti da eliminare
|
|
int nCross = 0 ;
|
|
for ( int i = 0 ; i < int( vIccInfo.size()) ; ++ i) {
|
|
|
|
if ( i < int( vIccInfo.size()) - 1) {
|
|
// se anche l'intersezione successiva è overlap che parte dallo stesso punto
|
|
if ( vIccInfo[i].bOverlap && vIccInfo[i+1].bOverlap &&
|
|
abs( vIccInfo[i].IciA[0].dU - vIccInfo[i+1].IciA[0].dU) < EPS_SMALL &&
|
|
abs( vIccInfo[i].IciB[0].dU - vIccInfo[i+1].IciB[0].dU) < EPS_SMALL) {
|
|
// elimino il tratto
|
|
inOk.Subtract( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciB[0].dU) ;
|
|
// salto le intersezioni successive già considerate rimuovendo questo intervallo
|
|
int j = i + 2 ;
|
|
while ( j < ( int)vIccInfo.size() && vIccInfo[j].IciA[0].dU > vIccInfo[i].IciA[0].dU - EPS_SMALL &&
|
|
vIccInfo[j].IciA[1].dU < vIccInfo[i].IciB[0].dU + EPS_SMALL) {
|
|
j ++ ;
|
|
}
|
|
// aggiorno il contatore
|
|
i = j - 1 ;
|
|
continue ;
|
|
}
|
|
}
|
|
|
|
// se con sovrapposizione
|
|
if ( vIccInfo[i].bOverlap) {
|
|
// se solo touch
|
|
if ( ( vIccInfo[i].IciA[0].nPrevTy == vIccInfo[i].IciA[1].nNextTy ||
|
|
vIccInfo[i].IciA[0].nPrevTy == ICCT_SPK || vIccInfo[i].IciA[1].nNextTy == ICCT_SPK) &&
|
|
vIccInfo[i].IciA[0].nPrevTy != ICCT_NULL && vIccInfo[i].IciA[1].nNextTy != ICCT_NULL) {
|
|
// obbligatoriamente controversi, elimino entrambi i tratti
|
|
inOk.Subtract( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciA[1].dU) ;
|
|
inOk.Subtract( vIccInfo[i].IciB[0].dU, vIccInfo[i].IciB[1].dU) ;
|
|
}
|
|
// altrimenti attraversamento
|
|
else {
|
|
// elimino la parte interna
|
|
if ( vIccInfo[i].bCBOverEq)
|
|
inOk.Subtract( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciB[0].dU) ;
|
|
else
|
|
inOk.Subtract( min( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciB[1].dU), max( vIccInfo[i].IciB[0].dU, vIccInfo[i].IciA[1].dU)) ;
|
|
}
|
|
}
|
|
// altrimenti
|
|
else {
|
|
// se solo touch
|
|
if ( vIccInfo[i].IciA[0].nPrevTy == vIccInfo[i].IciA[0].nNextTy &&
|
|
vIccInfo[i].IciA[0].nPrevTy != ICCT_NULL && vIccInfo[i].IciA[0].nNextTy != ICCT_NULL) {
|
|
inOk.Subtract( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciA[0].dU) ;
|
|
inOk.Subtract( vIccInfo[i].IciB[0].dU, vIccInfo[i].IciB[0].dU) ;
|
|
}
|
|
// altrimenti attraversamento
|
|
else {
|
|
double dParA = vIccInfo[i].IciA[0].dU ;
|
|
double dParB = vIccInfo[i].IciB[0].dU ;
|
|
if ( abs( dParA - dEnd) < EPS_SMALL)
|
|
swap( dParA, dParB) ;
|
|
// verifico se uno dei due intervalli dà origine ad un tratto trascurabile
|
|
PtrOwner<ICurve> pCrv1( pMyCrv->CopyParamRange( dParA, dParB)) ;
|
|
PtrOwner<ICurve> pCrv2( pMyCrv->CopyParamRange( dParB, dParA)) ;
|
|
double dArea1 = 0, dArea2 = 0 ;
|
|
if ( ! IsNull( pCrv1))
|
|
pCrv1->GetAreaXY( dArea1) ;
|
|
if ( ! IsNull( pCrv2))
|
|
pCrv2->GetAreaXY( dArea2) ;
|
|
|
|
if ( abs( dArea1) > 1e6 * abs( dArea2)) {
|
|
// se il tratto dParB->dParA non è significativo
|
|
inOk.Subtract( dStart, dParA) ;
|
|
inOk.Subtract( dParB, dEnd) ;
|
|
}
|
|
else if ( abs( dArea2) > 1e6 * abs( dArea1)) {
|
|
// se il tratto dParA->dParB non è siginificativo
|
|
inOk.Subtract( dParA, dParB) ;
|
|
}
|
|
else {
|
|
// se entrambe le regioni sono significative
|
|
if ( IsEven( nCross))
|
|
inOk.Subtract( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciB[0].dU) ;
|
|
else
|
|
inOk.Add( vIccInfo[i].IciA[0].dU, vIccInfo[i].IciB[0].dU) ;
|
|
++ nCross ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copio le parti da conservare
|
|
double dParS, dParE ;
|
|
bool bFound = inOk.GetFirst( dParS, dParE) ;
|
|
while ( bFound) {
|
|
ICurve* pCrv = pMyCrv->CopyParamRange( dParS, dParE) ;
|
|
if ( pCrv != nullptr) {
|
|
if ( pCrv->GetType() == CRV_COMPO)
|
|
CrvLst.push_back( pCrv) ;
|
|
else {
|
|
CurveComposite* pCrvCo = new( std::nothrow) CurveComposite ;
|
|
if ( pCrvCo != nullptr) {
|
|
pCrvCo->AddCurve( pCrv) ;
|
|
CrvLst.push_back( pCrvCo) ;
|
|
}
|
|
}
|
|
}
|
|
bFound = inOk.GetNext( dParS, dParE) ;
|
|
}
|
|
|
|
// Concateno i percorsi risultanti (senza cambiare verso e ignorando le curve già chiuse) :
|
|
// primo ciclo con loop di due sole curve
|
|
for ( auto iIter = CrvLst.begin() ; iIter != CrvLst.end() ;) {
|
|
CurveComposite* pCrvCo = GetBasicCurveComposite( *iIter) ;
|
|
// recupero punti iniziale e finale della curva
|
|
Point3d ptStart ; pCrvCo->GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; pCrvCo->GetEndPoint( ptEnd) ;
|
|
// se aperta, cerco di concatenarla
|
|
if ( ! AreSamePointEpsilon( ptStart, ptEnd, 10 * EPS_SMALL)) {
|
|
// ciclo sulle curve successive per verificare se possibile concatenamento
|
|
for ( auto iIter2 = next( iIter) ; iIter2 != CrvLst.end() ;) {
|
|
CurveComposite* pCrvCo2 = GetBasicCurveComposite( *iIter2) ;
|
|
// recupero punti iniziale e finale della curva
|
|
Point3d ptStart2 ; pCrvCo2->GetStartPoint( ptStart2) ;
|
|
Point3d ptEnd2 ; pCrvCo2->GetEndPoint( ptEnd2) ;
|
|
// se aperta, verifico se concatenabile alla principale
|
|
if ( ! AreSamePointEpsilon( ptStart2, ptEnd2, 10 * EPS_SMALL)) {
|
|
if ( AreSamePointEpsilon( ptEnd, ptStart2, 10 * EPS_SMALL) &&
|
|
AreSamePointEpsilon( ptEnd2, ptStart, 10 * EPS_SMALL)) {
|
|
pCrvCo->AddCurve( pCrvCo2, true, 10 * EPS_SMALL) ;
|
|
CrvLst.erase( iIter2) ;
|
|
ptEnd = ptEnd2 ;
|
|
iIter2 = next( iIter) ;
|
|
}
|
|
else
|
|
++ iIter2 ;
|
|
}
|
|
else
|
|
++ iIter2 ;
|
|
}
|
|
}
|
|
++ iIter ;
|
|
}
|
|
// secondo ciclo con loop generici
|
|
for ( auto iIter = CrvLst.begin() ; iIter != CrvLst.end() ;) {
|
|
CurveComposite* pCrvCo = GetBasicCurveComposite( *iIter) ;
|
|
// recupero punti iniziale e finale della curva
|
|
Point3d ptStart ; pCrvCo->GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; pCrvCo->GetEndPoint( ptEnd) ;
|
|
// se aperta, cerco di concatenarla
|
|
if ( ! AreSamePointEpsilon( ptStart, ptEnd, 10 * EPS_SMALL)) {
|
|
// ciclo sulle curve successive per verificare se possibile concatenamento
|
|
for ( auto iIter2 = next( iIter) ; iIter2 != CrvLst.end() ;) {
|
|
CurveComposite* pCrvCo2 = GetBasicCurveComposite( *iIter2) ;
|
|
// recupero punti iniziale e finale della curva
|
|
Point3d ptStart2 ; pCrvCo2->GetStartPoint( ptStart2) ;
|
|
Point3d ptEnd2 ; pCrvCo2->GetEndPoint( ptEnd2) ;
|
|
// se aperta, verifico se concatenabile alla principale
|
|
if ( ! AreSamePointEpsilon( ptStart2, ptEnd2, 10 * EPS_SMALL)) {
|
|
if ( AreSamePointEpsilon( ptEnd, ptStart2, 100 * EPS_SMALL)) {
|
|
pCrvCo->AddCurve( pCrvCo2, true, 100 * EPS_SMALL) ;
|
|
CrvLst.erase( iIter2) ;
|
|
ptEnd = ptEnd2 ;
|
|
iIter2 = next( iIter) ;
|
|
}
|
|
else if ( AreSamePointEpsilon( ptEnd2, ptStart, 100 * EPS_SMALL)) {
|
|
pCrvCo->AddCurve( pCrvCo2, false, 100 * EPS_SMALL) ;
|
|
CrvLst.erase( iIter2) ;
|
|
ptStart = ptStart2 ;
|
|
iIter2 = next( iIter) ;
|
|
}
|
|
else
|
|
++ iIter2 ;
|
|
}
|
|
else
|
|
++ iIter2 ;
|
|
}
|
|
}
|
|
++ iIter ;
|
|
}
|
|
|
|
// elimino le curve troppo piccole
|
|
for ( auto iIter = CrvLst.begin() ; iIter != CrvLst.end() ;) {
|
|
CurveComposite* pCrvCo = GetBasicCurveComposite( *iIter) ;
|
|
BBox3d b3CrvCo ;
|
|
double dDiam ;
|
|
if ( ! pCrvCo->GetLocalBBox( b3CrvCo) || b3CrvCo.IsEmpty() ||
|
|
! b3CrvCo.GetDiameter( dDiam) || dDiam < 20 * EPS_SMALL) {
|
|
delete pCrvCo ;
|
|
pCrvCo = nullptr ;
|
|
iIter = CrvLst.erase( iIter) ;
|
|
}
|
|
else {
|
|
++ iIter ;
|
|
}
|
|
}
|
|
|
|
// riporto le curve nel riferimento originale
|
|
if ( bNeedRef) {
|
|
for ( auto pCrv : CrvLst)
|
|
pCrv->ToGlob( frExtr) ;
|
|
}
|
|
// assegno il versore estrusione originale
|
|
for ( auto pCrv : CrvLst)
|
|
pCrv->SetExtrusion( vtExtr) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
AdjustLoops( ICurve* pCurve, ICURVEPLIST& CrvLst, bool bNeedSameProp)
|
|
{
|
|
// elimino eventuali sovrapposizioni e accostamenti
|
|
if ( ! MyAdjustLoops( pCurve, CrvLst))
|
|
return false ;
|
|
// unisco eventuali tratti allineati ed elimino eventuali spikes
|
|
for ( auto pCrv : CrvLst) {
|
|
// se curva composita
|
|
CurveComposite* pCrvCo = GetBasicCurveComposite( pCrv) ;
|
|
if ( pCrvCo != nullptr) {
|
|
// elimino eventuali Spikes e Small Z
|
|
pCrvCo->RemoveSmallDefects( 2 * LIN_TOL_MIN, ANG_TOL_STD_DEG, true) ;
|
|
// unisco eventuali tratti allineati
|
|
pCrvCo->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG, true, bNeedSameProp) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|