Files
EgtGeomKernel/SfrCreate.cpp
T
Riccardo Elitropi 7edf4bced8 EgtGeomKernel :
- Aggiunto parametro alle funzioni per creare la FatCurve.
2025-05-27 08:28:56 +02:00

706 lines
26 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2015-2022
//----------------------------------------------------------------------------
// File : StmFromCurves.cpp Data : 08.12.22 Versione : 2.4l2
// Contenuto : Implementazione di funzioni per creazione di superfici Stm
// a partire da curve, con diversi metodi.
//
//
// Modifiche : 01.02.15 DS Creazione modulo.
// 11.08.16 DS In GetSurfFlatRegionFromFatCurve sostituito Offset a SimpleOffset.
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "CurveLine.h"
#include "CurveArc.h"
#include "CurveComposite.h"
#include "SurfFlatRegion.h"
#include "AdjustLoops.h"
#include "GeoConst.h"
#include "Voronoi.h"
#include "/EgtDev/Include/EGkPolyLine.h"
#include "/EgtDev/Include/EGkBiArcs.h"
#include "/EgtDev/Include/EGkOffsetCurve.h"
#include "/EgtDev/Include/EGkSfrCreate.h"
#include "/EgtDev/Include/EgtPointerOwner.h"
#include <algorithm>
using namespace std ;
//-------------------------------------------------------------------------------
ISurfFlatRegion*
GetSurfFlatRegionRectangle( double dWidth, double dLen)
{
// le dimensioni devono essere significative
if ( dWidth < EPS_SMALL || dLen < EPS_SMALL)
return nullptr ;
// creo il contorno
PolyLine PL ;
PL.AddUPoint( 0, ORIG) ;
PL.AddUPoint( 1, Point3d( dWidth, 0, 0)) ;
PL.AddUPoint( 2, Point3d( dWidth, dLen, 0)) ;
PL.AddUPoint( 3, Point3d( 0, dLen, 0)) ;
PL.AddUPoint( 4, ORIG) ;
PtrOwner<CurveComposite> pCC( CreateBasicCurveComposite()) ;
if ( IsNull( pCC) || ! pCC->FromPolyLine( PL))
return nullptr ;
// creo il rettangolo
PtrOwner<SurfFlatRegion> pSfr( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSfr) || ! pSfr->AddExtLoop( Release( pCC)))
return nullptr ;
else
return Release( pSfr) ;
}
//-------------------------------------------------------------------------------
ISurfFlatRegion*
GetSurfFlatRegionStadium( double dWidth, double dLen)
{
// le dimensioni devono essere significative
if ( dWidth < EPS_SMALL || dLen < EPS_SMALL)
return nullptr ;
// se dimensioni praticamente uguali, è un disco
if ( abs( dWidth - dLen) < 10 * EPS_SMALL)
return GetSurfFlatRegionDisk( ( dWidth + dLen) / 4) ;
// creo il contorno
PolyArc PA ;
if ( dWidth > dLen) {
double dRad = dLen / 2 ;
PA.AddUPoint( 0, Point3d( dRad, 0, 0), 0) ;
PA.AddUPoint( 1, Point3d( dWidth - dRad, 0, 0), 1) ;
PA.AddUPoint( 2, Point3d( dWidth - dRad, dLen, 0), 0) ;
PA.AddUPoint( 3, Point3d( dRad, dLen, 0), 1) ;
PA.AddUPoint( 4, Point3d( dRad, 0, 0), 0) ;
}
else {
double dRad = dWidth / 2 ;
PA.AddUPoint( 0, Point3d( dWidth, dRad, 0), 0) ;
PA.AddUPoint( 1, Point3d( dWidth, dLen - dRad, 0), 1) ;
PA.AddUPoint( 2, Point3d( 0, dLen - dRad, 0), 0) ;
PA.AddUPoint( 3, Point3d( 0, dRad, 0), 1) ;
PA.AddUPoint( 4, Point3d( dWidth, dRad, 0), 0) ;
}
PtrOwner<CurveComposite> pCC( CreateBasicCurveComposite()) ;
if ( IsNull( pCC) || ! pCC->FromPolyArc( PA))
return nullptr ;
// creo il rettangolo
PtrOwner<SurfFlatRegion> pSfr( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSfr) || ! pSfr->AddExtLoop( Release( pCC)))
return nullptr ;
else
return Release( pSfr) ;
}
//-------------------------------------------------------------------------------
ISurfFlatRegion*
GetSurfFlatRegionDisk( double dRadius)
{
// le dimensioni devono essere significative
if ( dRadius < EPS_SMALL)
return nullptr ;
// creo la circonferenza di riferimento
PtrOwner<CurveArc> pArc( CreateBasicCurveArc()) ;
if ( IsNull( pArc))
return nullptr ;
pArc->Set( ORIG, Z_AX, dRadius, X_AX, ANG_FULL, 0) ;
// creo il disco
PtrOwner<SurfFlatRegion> pSfr( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSfr) || ! pSfr->AddExtLoop( Release( pArc)))
return nullptr ;
else
return Release( pSfr) ;
}
//-------------------------------------------------------------------------------
ISurfFlatRegion*
GetSurfFlatRegionFromFatCurve( ICurve* pCrv, double dRadius, bool bSquareEnds, bool bSquareMids, double dOffsLinTol,
bool bMergeOnlySameProps)
{
// metodo di calcolo impostato da USE_VORONOI
// mi impossesso della curva
PtrOwner<ICurve> pCurve( pCrv) ;
if ( IsNull( pCurve))
return nullptr ;
// la inserisco in una curva composita
Vector3d vtExtr ; pCrv->GetExtrusion( vtExtr) ;
if ( vtExtr.IsSmall())
vtExtr = Z_AX ;
// ----------------- CALCOLO STANDARD --------------------------------
if ( ! USE_VORONOI) {
PtrOwner<CurveComposite> pCompo1 ;
if ( ! pCompo1.Set( ConvertCurveToBasicComposite( Release( pCurve))))
return nullptr ;
pCompo1->SetExtrusion( vtExtr) ;
// se distanza tra gli estremi minore di due volte il raggio la chiudo, purchè curva abbastanza lunga
Point3d ptStart, ptEnd, ptMid ;
Vector3d vtStart, vtEnd ;
pCompo1->GetStartPoint( ptStart) ;
pCompo1->GetStartDir( vtStart) ;
pCompo1->GetEndPoint( ptEnd) ;
pCompo1->GetEndDir( vtEnd) ;
pCompo1->GetMidPoint( ptMid) ;
if ( AreSamePointEpsilon( ptStart, ptEnd, 2 * dRadius) && Dist( ptStart, ptMid) > 2 * dRadius && Dist( ptEnd, ptMid) > 2 * dRadius) {
if ( AreSamePointEpsilon( ptStart, ptEnd, max( 0.1 * dRadius, 10 * EPS_SMALL))) {
Point3d ptNew = Media( ptStart, ptEnd) ;
pCompo1->ModifyStart( ptNew) ;
pCompo1->ModifyEnd( ptNew) ;
}
else {
// piano della curva
Frame3d frLoc ;
if ( ! AreSameVectorApprox( vtExtr, Z_AX))
frLoc.Set( ptStart, vtExtr) ;
// costruisco il biarco nel piano della curva
ptStart.ToLoc( frLoc) ;
ptEnd.ToLoc( frLoc) ;
Vector3d vtELoc( vtEnd) ; vtELoc.ToLoc( frLoc) ;
Vector3d vtSLoc( vtStart) ; vtSLoc.ToLoc( frLoc) ;
double dAngEnd ; vtELoc.ToSpherical( nullptr, nullptr, &dAngEnd) ;
double dAngStart ; vtSLoc.ToSpherical( nullptr, nullptr, &dAngStart) ;
PtrOwner<ICurve> pClose( GetBiArc( ptEnd, dAngEnd, ptStart, dAngStart, 0.5)) ;
// aggiungo il biarco
if ( ! IsNull( pClose)) {
// porto il biarco in globale
pClose->ToGlob( frLoc) ;
pCompo1->AddCurve( Release( pClose)) ;
}
else
pCompo1->Close() ;
}
}
// tipo di offset
int nOffsType = ( bSquareMids ? ICurve::OFF_EXTEND : ICurve::OFF_FILLET) ;
// se curva chiusa
if ( pCompo1->IsClosed()) {
// fondo le curve allineate
pCompo1->MergeCurves( LIN_TOL_FINE, ANG_TOL_STD_DEG) ;
// ne faccio una copia e la inverto
PtrOwner<CurveComposite> pCompo2( pCompo1->Clone()) ;
if ( IsNull( pCompo2) || ! pCompo2->Invert())
return nullptr ;
// per creare la regione
SurfFlatRegionByContours SfrCntr( false, false) ;
// offset della prima curva a destra del raggio
OffsetCurve OffsCrv1( dOffsLinTol) ;
if ( ! OffsCrv1.Make( pCompo1, dRadius, nOffsType))
return nullptr ;
ICurve* pOffs1 = OffsCrv1.GetLongerCurve() ;
while ( pOffs1 != nullptr) {
SfrCntr.AddCurve( pOffs1) ;
pOffs1 = OffsCrv1.GetLongerCurve() ;
}
// offset della seconda curva a destra del raggio (è invertita rispetto alla precedente)
OffsetCurve OffsCrv2( dOffsLinTol) ;
if ( ! OffsCrv2.Make( pCompo2, dRadius, nOffsType))
return nullptr ;
ICurve* pOffs2 = OffsCrv2.GetLongerCurve() ;
while ( pOffs2 != nullptr) {
SfrCntr.AddCurve( pOffs2) ;
pOffs2 = OffsCrv2.GetLongerCurve() ;
}
// creo la regione
return SfrCntr.GetSurf() ;
}
// altrimenti
else {
// se richiesti estremi squadrati, la allungo del raggio alle due estremità
if ( bSquareEnds) {
pCompo1->ExtendStartByLen( dRadius) ;
pCompo1->ExtendEndByLen( dRadius) ;
}
// fondo le curve allineate
pCompo1->MergeCurves( LIN_TOL_FINE, ANG_TOL_STD_DEG) ;
// ne faccio una copia e la inverto
PtrOwner<CurveComposite> pCompo2( pCompo1->Clone()) ;
if ( IsNull( pCompo2) || ! pCompo2->Invert())
return nullptr ;
// creo la regione
SurfFlatRegionByContours SfrCntr( false, false) ;
// offset della prima curva a destra del raggio
OffsetCurve OffsCrv1( dOffsLinTol) ;
if ( ! OffsCrv1.Make( pCompo1, dRadius, nOffsType))
return nullptr ;
ICurve* pOffs1 = OffsCrv1.GetLongerCurve() ;
if ( pOffs1 == nullptr)
return nullptr ;
pCompo1->Clear() ;
while ( pOffs1 != nullptr) {
if ( pOffs1->IsClosed())
SfrCntr.AddCurve( pOffs1) ;
else
pCompo1->AddCurve( pOffs1) ;
pOffs1 = OffsCrv1.GetLongerCurve() ;
}
// offset della seconda curva a destra del raggio
OffsetCurve OffsCrv2( dOffsLinTol) ;
if ( ! OffsCrv2.Make( pCompo2, dRadius, nOffsType))
return nullptr ;
ICurve* pOffs2 = OffsCrv2.GetLongerCurve() ;
if ( pOffs2 == nullptr)
return nullptr ;
pCompo2->Clear() ;
while ( pOffs2 != nullptr) {
if ( pOffs2->IsClosed())
SfrCntr.AddCurve( pOffs2) ;
else
pCompo2->AddCurve( pOffs2) ;
pOffs2 = OffsCrv2.GetLongerCurve() ;
}
// se estremi squadrati
if ( bSquareEnds) {
// aggiungo alla prima curva una linea che la unisca alla seconda
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
Point3d ptEnd1, ptStart2 ;
if ( IsNull( pLine) ||
! pCompo1->GetEndPoint( ptEnd1) ||
! pCompo2->GetStartPoint( ptStart2) ||
! pLine->Set( ptEnd1, ptStart2) ||
! pCompo1->AddCurve( Release( pLine)))
return nullptr ;
// unisco le due curve composite e le chiudo
if ( ! pCompo1->AddCurve( Release( pCompo2)) || ! pCompo1->Close())
return nullptr ;
}
// altrimenti estremi arrotondati
else {
// aggiungo alla prima curva un arco che la unisca alla seconda
PtrOwner<CurveArc> pArc1( CreateBasicCurveArc()) ;
Point3d ptEnd1, ptStart2 ;
Vector3d vtEnd1, vtStart2 ;
if ( IsNull( pArc1) ||
! pCompo1->GetEndPoint( ptEnd1) ||
! pCompo1->GetEndDir( vtEnd1) ||
! pCompo2->GetStartPoint( ptStart2) ||
! pCompo2->GetStartDir( vtStart2))
return nullptr ;
// verifico se arco tangente alla prima o alla seconda curva
if ( AreSameVectorApprox( vtEnd1, vtEnd)) {
if ( ! pArc1->Set2PVN( ptEnd1, ptStart2, vtEnd1, vtExtr) ||
! pCompo1->AddCurve( Release( pArc1)))
return nullptr ;
}
else {
if ( ! pArc1->Set2PVN( ptStart2, ptEnd1, -vtStart2, vtExtr) ||
! pArc1->Invert() ||
! pCompo1->AddCurve( Release( pArc1)))
return nullptr ;
}
// aggiungo alla seconda curva un arco che la unisca alla prima
PtrOwner<CurveArc> pArc2( CreateBasicCurveArc()) ;
Point3d ptEnd2, ptStart1 ;
Vector3d vtEnd2, vtStart1 ;
if ( IsNull( pArc2) ||
! pCompo2->GetEndPoint( ptEnd2) ||
! pCompo2->GetEndDir( vtEnd2) ||
! pCompo1->GetStartPoint( ptStart1) ||
! pCompo1->GetStartDir( vtStart1))
return nullptr ;
// verifico se arco tangente alla seconda o alla prima curva
if ( AreOppositeVectorApprox( vtEnd2, vtStart)) {
if ( ! pArc2->Set2PVN( ptEnd2, ptStart1, vtEnd2, vtExtr) ||
! pCompo2->AddCurve( Release( pArc2)))
return nullptr ;
}
else {
if ( ! pArc2->Set2PVN( ptStart1, ptEnd2, -vtStart1, vtExtr) ||
! pArc2->Invert() ||
! pCompo2->AddCurve( Release( pArc2)))
return nullptr ;
}
// unisco le due curve composite
if ( ! pCompo1->AddCurve( Release( pCompo2)))
return nullptr ;
}
SfrCntr.AddCurve( Release( pCompo1)) ;
return SfrCntr.GetSurf() ;
}
}
// ---------------------- CALCOLO CON VORONOI ------------------------------------
else {
// calcolo la fat curve con Voronoi
ICURVEPOVECTOR vFatCurves ;
if ( ! CalcCurveFatCurve( *pCurve, vFatCurves, dRadius, bSquareEnds, bSquareMids, bMergeOnlySameProps))
return nullptr ;
// costruisco la superficie a partire dalle curve
SurfFlatRegionByContours SfrCntr( false, false) ;
for ( int i = 0 ; i < ( int)vFatCurves.size() ; i++)
SfrCntr.AddCurve( Release( vFatCurves[i])) ;
return SfrCntr.GetSurf() ;
}
}
//----------------------------------------------------------------------------
ISurfFlatRegion*
GetSurfFlatRegionFromTriangle( const Triangle3d& Tria)
{
// Creo la regione.
PtrOwner<SurfFlatRegion> pSfr( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSfr))
return nullptr ;
// Creo curva composita.
PtrOwner<CurveComposite> pLoop( CreateBasicCurveComposite()) ;
if ( IsNull( pLoop))
return nullptr ;
if ( ! pLoop->AddPoint( Tria.GetP( 0)) ||
! pLoop->AddLine( Tria.GetP( 1)) ||
! pLoop->AddLine( Tria.GetP( 2)) ||
! pLoop->Close())
return nullptr ;
pSfr->AddExtLoop( Release( pLoop)) ;
return Release( pSfr) ;
}
//----------------------------------------------------------------------------
ISurfFlatRegion*
GetSurfFlatRegionFromPolyLine( const PolyLine& ContourPolyLine)
{
// Creo curva composita.
PtrOwner<CurveComposite> pLoop( CreateBasicCurveComposite()) ;
if ( IsNull( pLoop) || ! pLoop->FromPolyLine( ContourPolyLine))
return nullptr ;
// Creo la regione.
PtrOwner<SurfFlatRegion> pSfr( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSfr) || ! pSfr->AddExtLoop( Release( pLoop)))
return nullptr ;
return Release( pSfr) ;
}
//----------------------------------------------------------------------------
ISurfFlatRegion*
GetSurfFlatRegionFromPolyLineVector( const POLYLINEVECTOR& vContoursPolyLineVec)
{
// Creo la regione.
PtrOwner<SurfFlatRegion> pSfr( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSfr))
return nullptr ;
// Ciclo sulle PolyLine.
for ( int nL = 0 ; nL < int( vContoursPolyLineVec.size()) ; ++ nL) {
// Creo curva composita.
PtrOwner<CurveComposite> pLoop( CreateBasicCurveComposite()) ;
if ( IsNull( pLoop) || ! pLoop->FromPolyLine( vContoursPolyLineVec[nL]))
return nullptr ;
// Loop esterno
if ( nL == 0) {
if ( ! pSfr->AddExtLoop( Release( pLoop)))
return nullptr ;
}
// Loop interno
else {
if ( ! pSfr->AddIntLoop( Release( pLoop)))
return nullptr ;
}
}
return Release( pSfr) ;
}
//-------------------------------------------------------------------------------
// Classe SurfFlatRegionByContours
//-------------------------------------------------------------------------------
SurfFlatRegionByContours::~SurfFlatRegionByContours(void)
{
// cancello eventuali curve rimaste
for ( auto& pCrv : m_vpCrv) {
if ( pCrv != nullptr)
delete pCrv ;
pCrv = nullptr ;
}
m_vpCrv.clear() ;
}
//-------------------------------------------------------------------------------
bool
SurfFlatRegionByContours::AddCurve( ICurve* pCrv)
{
// acquisisco la curva
PtrOwner<ICurve> pMyCrv( pCrv) ;
if ( IsNull( pMyCrv) || ! pMyCrv->IsValid())
return false ;
// verifico sia chiusa
if ( ! pMyCrv->IsClosed())
return false ;
// verifico sia piana e imposto estrusione come normale al piano
double dArea ;
Plane3d plPlane ;
if ( ! pMyCrv->GetArea( plPlane, dArea))
return false ;
pMyCrv->SetExtrusion( plPlane.GetVersN()) ;
pMyCrv->SetThickness( 0) ;
// rimuovo eventuali sovrapposizioni (calcolate nel suo piano)
ICURVEPLIST CrvLst ;
if ( ! AdjustLoops( Release( pMyCrv), CrvLst, true))
return false ;
// la/le inserisco nel vettore delle curve
for ( auto& pSingCrv : CrvLst)
m_vpCrv.push_back( pSingCrv) ;
return true ;
}
//-------------------------------------------------------------------------------
bool
SurfFlatRegionByContours::Prepare( void)
{
// calcolo piano medio e area delle curve
m_vArea.reserve( m_vpCrv.size()) ;
Vector3d vtN0 ;
for ( int i = 0 ; i < int( m_vpCrv.size()) ; ++ i) {
// calcolo piano medio e area
Plane3d plPlane ;
double dArea ;
if ( ! m_vpCrv[i]->GetArea( plPlane, dArea))
return false ;
// imposto la normale del primo contorno come riferimento
if ( i == 0)
vtN0 = plPlane.GetVersN() ;
// verifico che le normali siano molto vicine
if ( ! AreSameOrOppositeVectorApprox( plPlane.GetVersN(), vtN0))
return false ;
// assegno il segno all'area secondo il verso della normale
if ( ( plPlane.GetVersN() * vtN0) > 0)
m_vArea.emplace_back( i, dArea) ;
else
m_vArea.emplace_back( i, - dArea) ;
}
// ordino in senso decrescente sull'area
sort( m_vArea.begin(), m_vArea.end(),
[]( const INDAREA& a, const INDAREA& b) { return ( abs( a.second) > abs( b.second)) ; }) ;
return true ;
}
//-------------------------------------------------------------------------------
ISurfFlatRegion*
SurfFlatRegionByContours::GetSurf( void)
{
// se è la prima superficie, devo preparare
if ( m_bFirst) {
if ( ! Prepare())
return nullptr ;
m_bFirst = false ;
}
// altrimenti, se ammessa una sola superficie errore
else if ( ! m_bAllowedMore) {
return nullptr ;
}
// creo la superficie
PtrOwner<SurfFlatRegion> pSfr( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSfr))
return nullptr ;
// aggiungo le diverse curve
bool bExtLoop = false ;
bool bFirstCrv ;
do {
bFirstCrv = true ;
for ( int i = 0 ; i < int( m_vArea.size()) ; ++ i) {
// recupero indice di percorso e verifico sia valido
int j = m_vArea[i].first ;
if ( j < 0)
continue ;
// la prima deve essere il loop esterno
if ( bFirstCrv) {
// ne faccio una copia
PtrOwner<ICurve> pCrv( m_vpCrv[j]->Clone()) ;
if ( IsNull( pCrv)) {
m_vArea[i].first = - 1 ;
continue ;
}
// provo a inserirla
if ( pSfr->AddExtLoop( Release( pCrv))) {
bExtLoop = true ;
bFirstCrv = false ;
delete m_vpCrv[j] ;
m_vpCrv[j] = nullptr ;
m_vArea[i].first = - 1 ;
}
}
// gli altri sono loop interni
else {
// ne faccio una copia
PtrOwner<ICurve> pCrv( m_vpCrv[j]->Clone()) ;
if ( IsNull( pCrv)) {
m_vArea[i].first = - 1 ;
continue ;
}
// provo a inserirla
if ( pSfr->AddIntLoop( Release( pCrv))) {
delete m_vpCrv[j] ;
m_vpCrv[j] = nullptr ;
m_vArea[i].first = - 1 ;
}
}
}
} while ( m_bAllowedMultiChunk && ! bFirstCrv) ;
// se non valida, errore
if ( ! bExtLoop || ! pSfr->IsValid())
return nullptr ;
// restituisco la superficie
return Release( pSfr) ;
}
//-------------------------------------------------------------------------------
bool
SurfFlatRegionByContours::AllCurvesUsed( void)
{
// verifico se sono rimaste delle curve
for ( auto& pCrv : m_vpCrv) {
if ( pCrv != nullptr)
return false ;
}
return true ;
}
//-------------------------------------------------------------------------------
bool
SurfFlatRegionByContours::GetUnusedCurveTempProps( INTVECTOR& vId)
{
vId.clear() ;
// verifico se sono rimaste delle curve
for ( auto& pCrv : m_vpCrv) {
if ( pCrv != nullptr)
vId.push_back( pCrv->GetTempProp()) ;
}
return ( ! vId.empty()) ;
}
//-------------------------------------------------------------------------------
bool
CalcRegionPolyLines( const POLYLINEVECTOR& vPL, Vector3d& vtN, INTMATRIX& vnPLIndMat, BOOLVECTOR& vbInvert)
{
// vnPLIndMat : ogni riga corrisponde ad un chunk, in posizione 0 c'è il loop esterno e nelle successive i loop interni
// vbInvert : riferito al vettore delle polyline, riporta true se la polyline è stata invertita
// ricavo versore normale
Plane3d plPlane ; double dArea ;
if ( ! vPL[0].IsClosedAndFlat( plPlane, dArea, 50 * EPS_SMALL))
return false ;
vtN = plPlane.GetVersN() ;
typedef std::pair<int,double> INDAREA ;
std::vector<INDAREA> m_vArea ;
// calcolo piano medio e area delle curve
m_vArea.reserve( vPL.size()) ;
VCT3DVECTOR vvtN ;
for ( int i = 0 ; i < int( vPL.size()) ; ++ i) {
// calcolo piano medio e area
Plane3d plPlane ;
double dArea ;
if ( ! vPL[i].IsClosedAndFlat( plPlane, dArea))
return false ;
// verifico che le normali siano molto vicine
if ( ! AreSameOrOppositeVectorApprox( plPlane.GetVersN(), vtN))
return false ;
// salvo la normale
vvtN.push_back( plPlane.GetVersN()) ;
// assegno il segno all'area secondo il verso della normale
if ( ( plPlane.GetVersN() * vtN) > 0)
m_vArea.emplace_back( i, dArea) ;
else
m_vArea.emplace_back( i, - dArea) ;
}
// ordino in senso decrescente sull'area
sort( m_vArea.begin(), m_vArea.end(),
[]( const INDAREA& a, const INDAREA& b) { return ( abs( a.second) > abs( b.second)) ; }) ;
// dalle PolyLine passo alle curve nel piano XY ( prendo la prima come riferimento, trascuro le Z delle successive)
Frame3d frRef ; frRef.Set( ORIG, vtN) ;
if ( ! frRef.IsValid())
return false ;
ICRVCOMPOPOVECTOR vCrvCompo( int( vPL.size())) ;
for ( int i = 0 ; i < int( vPL.size()) ; ++ i) {
vCrvCompo[i].Set( CreateCurveComposite()) ;
vCrvCompo[i]->FromPolyLine( vPL[i]) ;
vCrvCompo[i]->ToLoc( frRef) ;
}
// restituisco la normale del loop più grande
bool bInvertAll = vvtN[m_vArea[0].first] * vtN < 0 ;
vtN = vvtN[m_vArea[0].first] ;
//// vettore di indici per ordinare le PolyLine
INTVECTOR vPL_IndOrder ; vPL_IndOrder.resize( int( vPL.size())) ;
for ( int i = 0 ; i < int( m_vArea.size()) ; ++ i)
vPL_IndOrder[i] = m_vArea[i].first ;
// aggiungo le diverse curve
bool bFirstCrv ;
Plane3d plExtLoop ;
double dAreaExtLoop = 0. ;
vbInvert.resize( vPL.size()) ;
fill( vbInvert.begin(), vbInvert.end(), false) ;
do {
bFirstCrv = true ;
for ( int i = 0 ; i < int( m_vArea.size()) ; ++ i) {
// recupero indice di percorso e verifico sia valido
int j = m_vArea[i].first ;
if ( j < 0)
continue ;
// lo inserisco come esterno...
if ( bFirstCrv) {
vnPLIndMat.push_back({ j}) ;
m_vArea[i].first = -1 ;
dAreaExtLoop = m_vArea[i].second ;
// inverto se necessario
if ( m_vArea[i].second < EPS_SMALL) {
vCrvCompo[j]->Invert() ;
dAreaExtLoop *= -1 ;
vbInvert[j] = true ;
}
bFirstCrv = false ;
}
// ... altrimenti verifico se il loop è interno o no
else {
// il loop è interno se è sia interno al loop esterno della riga di vnPLIndMat e allo stesso tempo
// esterno a tutti i loop già inseriti nella riga attuale.
// verifica rispetto loop esterno
IntersCurveCurve ccInt( *vCrvCompo[vnPLIndMat.back().front()], *vCrvCompo[j]) ;
CRVCVECTOR ccClass ;
if ( ccInt.GetCrossOrOverlapIntersCount() > 0 ||
! ccInt.GetCurveClassification( 1, EPS_SMALL, ccClass) ||
ccClass.empty() || ccClass[0].nClass != CRVC_IN)
continue ;
// verifica rispetto ai loop interni
bool bOk = true ;
for ( int k = 1 ; k < int( vnPLIndMat.back().size()) ; ++ k) {
IntersCurveCurve ccInt2( *vCrvCompo[vnPLIndMat.back()[k]], *vCrvCompo[j]) ;
CRVCVECTOR ccClass2 ;
if ( ccInt2.GetCrossOrOverlapIntersCount() > 0 ||
! ccInt2.GetCurveClassification( 1, EPS_SMALL, ccClass2) ||
ccClass2.empty() || ccClass2[0].nClass != CRVC_IN) {
bOk = false ;
break ;
}
}
if ( bOk) {
// inserisco nella matrice
vnPLIndMat.back().push_back( j) ;
m_vArea[i].first = -1 ;
// inverto se necessario
if ( m_vArea[i].second * dAreaExtLoop > 0.) {
vCrvCompo[j]->Invert() ;
vbInvert[j] = true ;
}
}
}
}
} while ( ! bFirstCrv) ;
if ( bInvertAll) {
for ( int i = 0 ; i < int( vPL.size()) ; ++i)
vbInvert[i] = ! vbInvert[i] ;
}
return true ;
}