Files
EgtGeomKernel/StmFromCurves.cpp
T
SaraP f48bf06f64 EgtGeomKernel 3.1f3 :
- migliorie nella creazione di solidi swept con sezione rettangolare smussata
- correzioni errori e aggiunte funzioni per calcolo bisettori di Voronoi
- aggiunto parametro a GetChainedCurves per fermare su biforcazione
- piccola miglioria a AssociatePolyLinesMinDistPoints.
2026-06-15 09:57:55 +02:00

2138 lines
84 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2015-2015
//----------------------------------------------------------------------------
// File : StmFromCurves.cpp Data : 01.02.15 Versione : 1.6b1
// Contenuto : Implementazione di funzioni per creazione di superfici Stm
// a partire da curve, con diversi metodi.
//
//
// Modifiche : 01.02.15 DS Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "GeoConst.h"
#include "CurveLine.h"
#include "CurveArc.h"
#include "CurveComposite.h"
#include "SurfTriMesh.h"
#include "SurfFlatRegion.h"
#include "Voronoi.h"
#include "IntersLineLine.h"
#include "/EgtDev/Include/EGkSfrCreate.h"
#include "/EgtDev/Include/EGkOffsetCurve.h"
#include "/EgtDev/Include/EGkStmFromCurves.h"
#include "/EgtDev/Include/EGkStmFromTriangleSoup.h"
#include "/EgtDev/Include/EGkRotationMinimizingFrame.h"
#include "/EgtDev/Include/EGkRotationXplaneFrame.h"
#include "/EgtDev/Include/EGkIntersCurves.h"
#include "/EgtDev/Include/EGkDistPointCurve.h"
#include "/EgtDev/Include/EgtPointerOwner.h"
#include <future>
using namespace std ;
//-------------------------------------------------------------------------------
// costanti per SurfTmRectSwept
static const int RS_FLATCAP_START = -1 ;
static const int RS_FLATCAP_END = -2 ;
static const int RS_LINK = -3 ;
static const int RS_LINK_BACK = -4 ;
static const int RS_FLATCAP_EXTRA = -5 ;
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshByFlatContour( const ICurve* pCurve, double dLinTol)
{
// verifica parametri
if ( pCurve == nullptr)
return nullptr ;
// calcolo la polilinea che approssima la curva
PolyLine PL ;
if ( ! pCurve->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL))
return nullptr ;
// creo e setto la superficie trimesh
PtrOwner<SurfTriMesh> pSTM( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSTM) || ! pSTM->CreateByFlatContour( PL))
return nullptr ;
// salvo tolleranza lineare usata
pSTM->SetLinearTolerance( dLinTol) ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshByRegion( const CICURVEPVECTOR& vpCurve, double dLinTol)
{
// verifica parametri
if ( &vpCurve == nullptr || vpCurve.empty())
return nullptr ;
// calcolo le polilinee che approssimano le curve della regione
POLYLINEVECTOR vPL ;
vPL.resize( vpCurve.size()) ;
for ( int i = 0 ; i < int( vpCurve.size()) ; ++ i) {
if ( ! vpCurve[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL[i]))
return nullptr ;
}
Vector3d vtN ;
INTMATRIX vnPLIndMat ;
BOOLVECTOR vbInvert ;
if ( ! CalcRegionPolyLines( vPL, vtN, vnPLIndMat, vbInvert))
return nullptr ;
for ( int i = 0 ; i < int( vnPLIndMat.size()) ; ++i) {
for ( int j = 0 ; j < int( vnPLIndMat[i].size()) ; ++j){
if ( vbInvert[vnPLIndMat[i][j]])
vPL[vnPLIndMat[i][j]].Invert() ;
}
}
// creo e setto la superficie trimesh
PtrOwner<SurfTriMesh> pSTM( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSTM) || ! pSTM->CreateByRegion( vPL, vnPLIndMat))
return nullptr ;
// salvo tolleranza lineare usata
pSTM->SetLinearTolerance( dLinTol) ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshByExtrusion( const ICurve* pCurve, const Vector3d& vtExtr,
bool bCapEnds, double dLinTol)
{
// verifica parametri
if ( pCurve == nullptr || &vtExtr == nullptr)
return nullptr ;
// calcolo la polilinea che approssima la curva
PolyLine PL ;
if ( ! pCurve->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL))
return nullptr ;
// se richiesta chiusura agli estremi
bool bDoCapEnds = false ;
if ( bCapEnds) {
// verifico che la curva sia chiusa e piatta
Plane3d plPlane ;
double dArea ;
if ( PL.IsClosedAndFlat( plPlane, dArea, 50 * EPS_SMALL)) {
// componente dell'estrusione perpendicolare al piano della curva
double dOrthoExtr = plPlane.GetVersN() * vtExtr ;
if ( ( abs( dOrthoExtr) > EPS_SMALL)) {
bDoCapEnds = true ;
// se negativa, inverto il senso del contorno
if ( dOrthoExtr < 0)
PL.Invert() ;
}
}
}
// creo e setto la superficie trimesh
PtrOwner<SurfTriMesh> pSTM( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSTM) || ! pSTM->CreateByExtrusion( PL, vtExtr))
return nullptr ;
// se da fare, metto i tappi sulle estremità
if ( bDoCapEnds) {
// creo la prima superficie di estremità
SurfTriMesh STM1 ;
if ( ! STM1.CreateByFlatContour( PL))
return nullptr ;
// la copio
SurfTriMesh STM2 = STM1 ;
// inverto la prima superficie
STM1.Invert() ;
// traslo la seconda
STM2.Translate( vtExtr) ;
// le unisco alla superficie del fianco
if ( ! pSTM->DoSewing( STM1) || ! pSTM->DoSewing( STM2))
return nullptr ;
}
// salvo tolleranza lineare usata
pSTM->SetLinearTolerance( dLinTol) ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshByRegionExtrusion( const CICURVEPVECTOR& vpCurve, const Vector3d& vtExtr, double dLinTol)
{
// verifica parametri
if ( &vpCurve == nullptr || vpCurve.empty() || &vtExtr == nullptr)
return nullptr ;
// se una sola curva, uso la funzione precedente
if ( vpCurve.size() == 1 )
return GetSurfTriMeshByExtrusion( vpCurve[0], vtExtr, true, dLinTol) ;
// calcolo le polilinee che approssimano le curve della regione
POLYLINEVECTOR vPL ;
vPL.resize( vpCurve.size()) ;
for ( int i = 0 ; i < int( vpCurve.size()) ; ++ i) {
if ( ! vpCurve[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL[i]))
return nullptr ;
}
Vector3d vtN ;
INTMATRIX vnPLIndMat ;
BOOLVECTOR vbInvert ;
if ( ! CalcRegionPolyLines( vPL, vtN, vnPLIndMat, vbInvert))
return nullptr ;
for ( int i = 0 ; i < int( vPL.size()) ; ++i) {
if ( vbInvert[i])
vPL[i].Invert() ;
}
// verifico la direzione di estrusione
double dOrthoExtr = vtN * vtExtr ;
if ( ( abs( dOrthoExtr) < EPS_SMALL))
return nullptr ;
// se componente estrusione negativa, inverto tutti i percorsi
if ( dOrthoExtr < 0) {
for ( int i = 0 ; i < int( vPL.size()) ; ++ i)
vPL[i].Invert() ;
}
// creo la prima superficie di estremità
PtrOwner<SurfTriMesh> pSTM( CreateBasicSurfTriMesh()) ;
// alla funzione CreateByRegion passo anche la matrice che contiene la struttura dei chunk. Le polyline hanno già il verso giusto
if ( IsNull( pSTM) || ! pSTM->CreateByRegion( vPL, vnPLIndMat))
return nullptr ;
// creo la seconda superficie e la unisco alla prima
{ // copio la prima superficie
SurfTriMesh STM2 = *pSTM ;
// inverto la prima superficie
pSTM->Invert() ;
// traslo la seconda
STM2.Translate( vtExtr) ;
// la unisco alla prima
if ( ! pSTM->DoSewing( STM2))
return nullptr ;
}
// creo e unisco le diverse superfici di estrusione
for ( int i = 0 ; i < int( vPL.size()) ; ++ i) {
// estrusione
SurfTriMesh STM2 ;
if ( ! STM2.CreateByExtrusion( vPL[i], vtExtr))
return nullptr ;
// la unisco alla superficie principale
if ( ! pSTM->DoSewing( STM2))
return nullptr ;
}
// compatto la superficie
if ( ! pSTM->DoCompacting())
return nullptr ;
// salvo tolleranza lineare usata
pSTM->SetLinearTolerance( dLinTol) ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshByRevolve( const ICurve* pCurve, const Point3d& ptAx, const Vector3d& vtAx,
bool bCapEnds, double dLinTol)
{
// verifica parametri
if ( pCurve == nullptr || &ptAx == nullptr || &vtAx == nullptr)
return nullptr ;
// limite minimo su tolleranza
dLinTol = max( dLinTol, EPS_SMALL) ;
// calcolo la polilinea che approssima la curva
PolyLine PL ;
if ( ! pCurve->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL))
return nullptr ;
// calcolo lo step di rotazione
double dMaxRad = 0 ;
if ( ! PL.GetMaxDistanceFromLine( ptAx, vtAx, 1, dMaxRad, false) || dMaxRad < EPS_SMALL)
return nullptr ;
double dStepRotDeg = sqrt( 8 * dLinTol / dMaxRad) * RADTODEG ;
// se richiesta chiusura degli estremi
if ( bCapEnds && ! PL.IsClosed()) {
Vector3d vtAxN = vtAx ;
vtAxN.Normalize() ;
double dPosIni = 0, dPosFin = 0 ;
Point3d ptP ;
// proietto l'ultimo punto sull'asse di rotazione
if ( PL.GetLastPoint( ptP)) {
dPosFin = ( ptP - ptAx) * vtAxN ;
Point3d ptPOnAx = ptAx + dPosFin * vtAxN ;
// se non giace sull'asse, aggiungo il punto proiettato
if ( ! AreSamePointApprox( ptP, ptPOnAx)) {
double dU ;
PL.GetLastU( dU) ;
PL.AddUPoint( ( dU + 1), ptPOnAx) ;
}
}
// inverto la polilinea
PL.Invert() ;
// proietto l'ultimo punto (era il primo) sull'asse di rotazione
if ( PL.GetLastPoint( ptP)) {
dPosIni = ( ptP - ptAx) * vtAxN ;
Point3d ptPOnAx = ptAx + dPosIni * vtAxN ;
// se non giace sull'asse, aggiungo il punto proiettato
if ( ! AreSamePointApprox( ptP, ptPOnAx)) {
double dU ;
PL.GetLastU( dU) ;
PL.AddUPoint( ( dU + 1), ptPOnAx) ;
}
}
// decido se reinvertire la polilinea
if ( dPosFin > dPosIni)
PL.Invert() ;
}
// creo e setto la superficie trimesh
PtrOwner<SurfTriMesh> pSTM( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSTM) || ! pSTM->CreateByScrewing( PL, ptAx, vtAx, ANG_FULL, dStepRotDeg, 0))
return nullptr ;
// se superficie risultante chiusa, verifico che la normale sia verso l'esterno
double dVol ;
if ( pSTM->GetVolume( dVol) && dVol < 0)
pSTM->Invert() ;
// salvo tolleranza lineare usata
pSTM->SetLinearTolerance( dLinTol) ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshByScrewing( const ICurve* pCurve, const Point3d& ptAx, const Vector3d& vtAx,
double dAngRotDeg, double dMove, bool bCapEnds, double dLinTol)
{
// verifica parametri
if ( pCurve == nullptr || &ptAx == nullptr || &vtAx == nullptr)
return nullptr ;
// limite minimo su tolleranza
dLinTol = max( dLinTol, EPS_SMALL) ;
// calcolo la polilinea che approssima la curva
PolyLine PL ;
if ( ! pCurve->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL))
return nullptr ;
// calcolo lo step di rotazione
double dMaxRad = 0 ;
if ( ! PL.GetMaxDistanceFromLine( ptAx, vtAx, 1, dMaxRad, false) || dMaxRad < EPS_SMALL)
return nullptr ;
double dStepRotDeg = sqrt( 8 * dLinTol / dMaxRad) * RADTODEG ;
// se superficie rototraslata, necessari limiti sulla lunghezza dei segmenti
if ( abs( dAngRotDeg) > EPS_ANG_SMALL && abs( dMove) > EPS_SMALL){
double dLenMax = 2.5 * abs( dMove * dStepRotDeg / dAngRotDeg) ;
if ( ! PL.AdjustForMaxSegmentLen( dLenMax))
return nullptr ;
}
// creo e setto la superficie trimesh
PtrOwner<SurfTriMesh> pSTM( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSTM) || ! pSTM->CreateByScrewing( PL, ptAx, vtAx, dAngRotDeg, dStepRotDeg, dMove))
return nullptr ;
// se richiesti caps
if ( bCapEnds) {
// determino se la sezione è chiusa e piatta
Plane3d plPlane ; double dArea ;
bool bSectClosedFlat = PL.IsClosedAndFlat( plPlane, dArea, 10 * EPS_SMALL) ;
// determino non sia una semplice rivoluzione
bool bRevolved = ( abs( abs( dAngRotDeg) - ANG_FULL) < EPS_ANG_SMALL && abs( dMove) < EPS_SMALL) ;
// se sezione chiusa e piatta e non rivoluzione, posso aggiungere i tappi
if ( bSectClosedFlat && ! bRevolved) {
// aggiungo il cap sull'inizio
PtrOwner<SurfTriMesh> pSci( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSci) || ! pSci->CreateByFlatContour( PL))
return nullptr ;
pSTM->DoSewing( *pSci) ;
// aggiungo il cap sulla fine
Vector3d vtMove = vtAx ;
vtMove.Normalize() ;
vtMove *= dMove ;
PL.Translate( vtMove) ;
PL.Rotate( ptAx, vtAx, dAngRotDeg) ;
PtrOwner<SurfTriMesh> pSce( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSce) || ! pSce->CreateByFlatContour( PL))
return nullptr ;
pSce->Invert() ;
pSTM->DoSewing( *pSce) ;
}
}
// se superficie risultante chiusa, verifico che la normale sia verso l'esterno
double dVol ;
if ( pSTM->GetVolume( dVol) && dVol < 0)
pSTM->Invert() ;
// salvo tolleranza lineare usata
pSTM->SetLinearTolerance( dLinTol) ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
static bool
RemoveFatCurveJunctions( ICURVEPOVECTOR& vCrvs)
{
ICURVEPOVECTOR vResCurves ;
for ( int i = 0 ; i < ssize( vCrvs) ; i ++) {
int nStart = ssize( vResCurves) ;
CurveComposite* pFatCrv = GetBasicCurveComposite( vCrvs[i]) ;
if ( pFatCrv == nullptr)
return false ;
PtrOwner<CurveComposite> pCrv( CreateBasicCurveComposite()) ;
if ( IsNull( pCrv))
return false ;
PtrOwner<ICurve> pCurrCrv( pFatCrv->RemoveFirstOrLastCurve( false)) ;
while ( ! IsNull( pCurrCrv)) {
if ( abs( pCurrCrv->GetTempParam() - VRONI_JUNCTION_OPEN) < EPS_SMALL ||
pCurrCrv->GetTempProp() == RS_FLATCAP_START ||
pCurrCrv->GetTempProp() == RS_FLATCAP_END) {
if ( pCrv->IsValid()) {
// salvo la catena trovata fino ad ora e la resetto
vResCurves.emplace_back( Release( pCrv)) ;
pCrv.Set( CreateBasicCurveComposite()) ;
if ( IsNull( pCrv))
return false ;
}
}
else {
// aggiungo la curva alla catena corrente
pCrv->AddCurve( Release( pCurrCrv)) ;
}
pCurrCrv.Set( pFatCrv->RemoveFirstOrLastCurve( false)) ;
}
if ( pCrv->IsValid()) {
if ( ssize( vResCurves) != nStart) {
// verifico se da concatenare alla prima
Point3d ptEnd ; pCrv->GetEndPoint( ptEnd) ;
Point3d ptStart ; vResCurves[nStart]->GetStartPoint( ptStart) ;
if ( AreSamePointApprox( ptStart, ptEnd)) {
ICurveComposite* pCompo = GetBasicCurveComposite( vResCurves[nStart]) ;
if ( pCompo == nullptr)
return false ;
pCompo->AddCurve( Release( pCrv), false) ;
}
else
vResCurves.emplace_back( Release( pCrv)) ;
}
else
vResCurves.emplace_back( Release( pCrv)) ;
}
}
swap( vResCurves, vCrvs) ;
return true ;
}
//-------------------------------------------------------------------------------
static SurfFlatRegion*
CalcSideSweptRegion( const ICurve* pOrigGuide, const Vector3d& vtNorm, double dOffs)
{
PtrOwner<SurfFlatRegion> pSrf( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSrf))
return nullptr ;
PtrOwner<CurveComposite> pGuide( ConvertCurveToBasicComposite( pOrigGuide->Clone())) ;
if ( IsNull( pGuide))
return nullptr ;
// per ogni sottotratto della guida calcolo la regione spazzata dal segmento su quel tratto
for ( int i = 0 ; i < pGuide->GetCurveCount() ; i ++) {
// raccordo con curva precedente
if ( i > 0) {
Vector3d vtS ; pGuide->GetCurve( i-1)->GetEndDir( vtS) ;
Vector3d vtE ; pGuide->GetCurve( i)->GetStartDir( vtE) ;
double dAng ; bool bDet ;
vtS.GetRotation( vtE, vtNorm, dAng, bDet) ;
if ( dAng * dOffs > EPS_SMALL) {
Point3d ptC ; pGuide->GetCurve( i)->GetStartPoint( ptC) ;
vtS.Rotate( vtNorm, -90) ;
vtE.Rotate( vtNorm, -90) ;
PtrOwner<CurveComposite> pBorder( CreateBasicCurveComposite()) ;
if ( IsNull( pBorder))
return nullptr ;
pBorder->AddPoint( ptC) ;
pBorder->AddLine( ptC + vtS * dOffs) ;
PtrOwner<CurveArc> pArc( CreateBasicCurveArc()) ;
if ( IsNull( pArc) || ! pArc->SetC2PN( ptC, ptC + vtS * dOffs, ptC + vtE * dOffs, vtNorm))
pBorder->AddLine( ptC + vtE * dOffs) ;
else
pBorder->AddCurve( Release( pArc)) ;
pBorder->Close() ;
pBorder->Invert() ;
PtrOwner<SurfFlatRegion> pSrfFillet( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pSrfFillet))
return nullptr ;
pSrfFillet->AddExtLoop( Release( pBorder)) ;
if ( pSrfFillet->IsValid())
pSrf->Add( *pSrfFillet) ;
}
}
PtrOwner<ICurve> pCurr( pGuide->GetCurve( i)->Clone()) ;
pCurr->SetExtrusion( vtNorm) ;
PtrOwner<ICurve> pCrvOffs( pCurr->Clone()) ;
PtrOwner<CurveComposite> pBorder( CreateBasicCurveComposite()) ;
if ( IsNull( pBorder))
return nullptr ;
if ( pCrvOffs->SimpleOffset( dOffs)) {
pBorder->AddCurve( Release( pCurr)) ;
pCrvOffs->Invert() ;
Point3d ptS ; pCrvOffs->GetStartPoint( ptS) ;
pBorder->AddLine( ptS) ;
pBorder->AddCurve( Release( pCrvOffs)) ;
pBorder->Close() ;
}
else {
// se arco che collassa costruisco il bordo del settore circolare
CurveArc* pArc = GetBasicCurveArc( pCurr) ;
if ( pArc == nullptr)
return nullptr ;
Point3d ptC = pArc->GetCenter() ;
Point3d ptS ; pArc->GetStartPoint( ptS) ;
pBorder->AddPoint( ptC) ;
pBorder->AddLine( ptS) ;
pBorder->AddCurve( Release( pCurr)) ;
pBorder->Close() ;
}
PtrOwner<SurfFlatRegion> pCurrSrf( CreateBasicSurfFlatRegion()) ;
if ( IsNull( pCurrSrf) || ! pCurrSrf->AddExtLoop( Release( pBorder)))
return nullptr ;
if ( pSrf->IsValid()) {
if ( ! pSrf->Add( *pCurrSrf))
return nullptr ;
}
else
pSrf.Set( pCurrSrf) ;
}
return Release( pSrf) ;
}
//-------------------------------------------------------------------------------
static ICurveComposite*
CalcSegmentSweptRegionBorder( const ICurve* pGuide, const Vector3d& vtNorm, double dOffs)
{
// costruisco le regioni spazzate a destra e sinistra
PtrOwner<SurfFlatRegion> pSrfP( CalcSideSweptRegion( pGuide, vtNorm, dOffs)) ;
PtrOwner<SurfFlatRegion> pSrfM( CalcSideSweptRegion( pGuide, vtNorm, - dOffs)) ;
if ( IsNull( pSrfP) || IsNull( pSrfM))
return nullptr ;
pSrfP->Invert() ;
if ( ! pSrfP->Add( *pSrfM))
return nullptr ;
PtrOwner<CurveComposite> pBorder( ConvertCurveToBasicComposite( pSrfP->GetLoop( 0, 0))) ;
return Release( pBorder) ;
}
//-------------------------------------------------------------------------------
static bool
AdjustSurfTriMeshRectSweptCaps( int nCapType, ICURVEPOVECTOR& vCrvs, const Vector3d& vtNorm, const ICurve* pGuide, double dOffs)
{
// se RSCAP_FLAT trasformo tutte le giunzioni relative agli estremi in linee controllando se necessario chiudere le curve
// con opportuni tratti della regione spazzata.
// RSCAP_NONE viene gestito allo stesso modo perchè per la superficie top e bottom servono curve chiuse, le curve
// di giunzione saranno rimosse successivamente per il calcolo della superficie laterale
if ( nCapType == RSCAP_FLAT || nCapType == RSCAP_NONE) {
// spezzo le curve in corrispondenza delle junctions
RemoveFatCurveJunctions( vCrvs) ;
// creo i flat caps sugli estremi della guida ( j = 0 start, j = 1 end)
Point3d ptGuideS ; pGuide->GetStartPoint( ptGuideS) ;
Vector3d vtGuideS ; pGuide->GetStartDir( vtGuideS) ;
vtGuideS.Rotate( vtNorm, ANG_RIGHT) ;
Point3d ptGuideE ; pGuide->GetEndPoint( ptGuideE) ;
Vector3d vtGuideE ; pGuide->GetEndDir( vtGuideE) ;
vtGuideE.Rotate( vtNorm, ANG_RIGHT) ;
for ( int j = 0 ; j < 2 ; j ++) {
Point3d ptP = ( j == 0 ? ptGuideS : ptGuideE) ;
Vector3d vtDir = ( j == 0 ? vtGuideS : vtGuideE) ;
Point3d pt1 = ptP + dOffs * vtDir ;
Point3d pt2 = ptP - dOffs * vtDir ;
// cerco su quali curve poggia la linea del cap
int nCrv1 = -1 ; bool bStart1 = true ;
int nCrv2 = -1 ;
for ( int i = 0 ; i < ssize( vCrvs) ; i ++) {
Point3d ptS ; vCrvs[i]->GetStartPoint( ptS) ;
Point3d ptE ; vCrvs[i]->GetEndPoint( ptE) ;
if ( AreSamePointEpsilon( ptS, pt1, 50 * EPS_SMALL)) {
nCrv1 = i ;
bStart1 = true ;
}
else if ( AreSamePointEpsilon( ptS, pt2, 50 * EPS_SMALL)) {
nCrv2 = i ;
}
if ( AreSamePointEpsilon( ptE, pt1, 50 * EPS_SMALL)) {
nCrv1 = i ;
bStart1 = false ;
}
else if ( AreSamePointEpsilon( ptE, pt2, 50 * EPS_SMALL)) {
nCrv2 = i ;
}
}
int nFlatCapProp = ( j == 0 ? RS_FLATCAP_START : RS_FLATCAP_END) ;
// se congiunge due curve le unisco
if ( nCrv1 != -1 && nCrv2 != -1) {
if ( nCrv1 == nCrv2) {
// se è la stessa curva la chiudo semplicemente con una linea
CurveComposite* pCompo = GetBasicCurveComposite( vCrvs[nCrv1]) ;
if ( pCompo == nullptr)
return false ;
pCompo->Close() ;
pCompo->SetCurveTempProp( pCompo->GetCurveCount() - 1, nFlatCapProp) ;
}
else {
int nFirst = ( bStart1 ? nCrv2 : nCrv1) ;
int nSecond = ( bStart1 ? nCrv1 : nCrv2) ;
CurveComposite* pCompo = GetBasicCurveComposite( vCrvs[nFirst]) ;
if ( pCompo == nullptr)
return false ;
Point3d ptRef ; vCrvs[nSecond]->GetStartPoint( ptRef) ;
pCompo->AddLine( ptRef) ;
pCompo->SetCurveTempProp( pCompo->GetCurveCount() - 1, nFlatCapProp) ;
pCompo->AddCurve( Release( vCrvs[nSecond])) ;
// elimino la curva mergiata dal vettore
swap( vCrvs[nSecond], vCrvs.back()) ;
vCrvs.pop_back() ;
}
}
}
// recupero le curve aperte e cerco di chiuderle
INTVECTOR vOpenIdx ;
for ( int i = 0 ; i < ssize( vCrvs) ; i++) {
if ( ! vCrvs[i]->IsClosed())
vOpenIdx.emplace_back( i) ;
}
if ( vOpenIdx.empty())
return true ;
// ricavo il bordo della regione spazzata da un segmento che scorre lungo la guida con il suo punto medio
PtrOwner<ICurveComposite> pBorder( CalcSegmentSweptRegionBorder( pGuide, vtNorm, dOffs)) ;
if ( IsNull( pBorder))
return false ;
// ricavo le porzioni del bordo della regione spazzata che servono per collegare i tratti aperti
Point3d ptNewStart ; vCrvs[vOpenIdx[0]]->GetStartPoint( ptNewStart) ;
double dUStart = 0 ; pBorder->GetParamAtPoint( ptNewStart, dUStart) ;
pBorder->ChangeStartPoint( dUStart) ;
INTDBLVECTOR vTrims ;
vTrims.reserve( 2 * vOpenIdx.size()) ;
vTrims.emplace_back( 0, 0.0) ;
for ( int i = 0 ; i < ssize( vOpenIdx) ; i ++) {
double dParS, dParE ;
if ( i > 0) {
Point3d ptS ; vCrvs[vOpenIdx[i]]->GetStartPoint( ptS) ;
if ( ! pBorder->GetParamAtPoint( ptS, dParS, 50 * EPS_SMALL))
return false ;
vTrims.emplace_back( i, dParS) ;
}
Point3d ptE ; vCrvs[vOpenIdx[i]]->GetEndPoint( ptE) ;
if ( ! pBorder->GetParamAtPoint( ptE, dParE, 50 * EPS_SMALL))
return false ;
vTrims.emplace_back( i, dParE) ;
}
double dS, dE ; pBorder->GetDomain( dS, dE) ;
vTrims.emplace_back( -1, dE) ;
sort( vTrims.begin(), vTrims.end(), [](const INTDBL& a, const INTDBL& b) { return a.second < b.second ;}) ;
// costruisco la curva chiusa risultante
CurveComposite* pResult = GetBasicCurveComposite( vCrvs[vOpenIdx[0]]) ;
if ( pResult == nullptr)
return false ;
for ( int i = 1 ; i < ssize( vTrims) ; i = i + 2) {
// ricavo la porzione di bordo
PtrOwner<CurveComposite> pBorderPart( ConvertCurveToBasicComposite( pBorder->CopyParamRange( vTrims[i].second, vTrims[i+1].second))) ;
if ( IsNull( pBorderPart))
return false ;
// identifico se ha tratti corrispondenti al flat cap
for ( int j = 0 ; j < pBorderPart->GetCurveCount() ; j ++) {
pBorderPart->SetCurveTempProp( j, RS_FLATCAP_EXTRA) ;
if ( pBorderPart->GetCurve( j)->GetType() == CRV_LINE) {
Vector3d vtS ; pBorderPart->GetCurve( j)->GetStartDir( vtS) ;
if ( AreSameOrOppositeVectorApprox( vtS, vtGuideS)) {
Point3d ptRef ; pBorderPart->GetCurve( j)->GetStartPoint( ptRef) ;
Vector3d vtDist = ptRef - ptGuideS ;
Vector3d vtOrtho = vtDist - vtDist * vtGuideS * vtGuideS ;
if ( vtDist * vtGuideS < dOffs + EPS_SMALL && vtOrtho.SqLen() < 100 * SQ_EPS_SMALL)
pBorderPart->SetCurveTempProp( j, RS_FLATCAP_START) ;
}
if ( AreSameOrOppositeVectorApprox( vtS, vtGuideE)) {
Point3d ptRef ; pBorderPart->GetCurve( j)->GetStartPoint( ptRef) ;
Vector3d vtDist = ptRef - ptGuideE ;
Vector3d vtOrtho = vtDist - vtDist * vtGuideE * vtGuideE ;
if ( vtDist * vtGuideE < dOffs + EPS_SMALL && vtOrtho.SqLen() < 100 * SQ_EPS_SMALL)
pBorderPart->SetCurveTempProp( j, RS_FLATCAP_END) ;
}
}
}
// aggiungo la curva aperta successiva e la elimino dal vettore delle curve
int nNextIdx = vTrims[i+1].first ;
if ( nNextIdx != -1) {
pResult->AddCurve( Release( vCrvs[vOpenIdx[nNextIdx]])) ;
swap( vCrvs[vOpenIdx[nNextIdx]], vCrvs.back()) ;
vCrvs.pop_back() ;
}
}
}
// se RSCAP_BEVEL approssimo tutte le junction relative agli estremi
else if ( nCapType == RSCAP_BEVEL) {
double dStepRotDeg = ANG_STRAIGHT / 4 ; // tolleranza
for ( int i = 0 ; i < ssize( vCrvs) ; i ++) {
CurveComposite* pFatCrv = GetBasicCurveComposite( vCrvs[i]) ;
if ( pFatCrv == nullptr)
return false ;
PtrOwner<CurveComposite> pNewCrv( CreateBasicCurveComposite()) ;
if ( IsNull( pNewCrv))
return false ;
PtrOwner<ICurve> pCurrCrv( pFatCrv->RemoveFirstOrLastCurve( false)) ;
while ( ! IsNull( pCurrCrv)) {
if ( pCurrCrv->GetTempParam() > EPS_SMALL) {
// sostituisco con la sua approssimazione
CurveComposite* ccTemp = CreateBasicCurveComposite() ;
CurveArc* pArc = GetBasicCurveArc( pCurrCrv) ;
if ( pArc == nullptr || ccTemp == nullptr)
return false ;
int nStep = max( 1, static_cast<int>( ceil( pArc->GetAngCenter() / dStepRotDeg))) ;
for ( int j = 0 ; j <= nStep ; ++ j) {
Point3d ptArc ;
pArc->GetPointD1D2( (double)j / nStep, ICurve::FROM_MINUS, ptArc) ;
if ( j == 0)
ccTemp->AddPoint( ptArc) ;
else
ccTemp->AddLine( ptArc) ;
}
pNewCrv->AddCurve( ccTemp) ;
}
else {
// aggiungo la curva
pNewCrv->AddCurve( Release( pCurrCrv)) ;
}
// passo alla curva successiva
pCurrCrv.Set( pFatCrv->RemoveFirstOrLastCurve( false)) ;
}
vCrvs[i].Set( pNewCrv) ;
}
}
return true ;
}
//-------------------------------------------------------------------------------
static ISurfTriMesh*
GetSurfTriMeshSharpRectSwept( double dDimH, double dDimV, const ICurve* pGuide, int nCapType, double dLinTol)
{
// verifico che la linea guida sia piana
Plane3d plGuide ;
if ( ! pGuide->IsFlat( plGuide, true, 10 * EPS_SMALL))
return nullptr ;
Vector3d vtNorm ; pGuide->GetExtrusion( vtNorm) ;
if ( vtNorm.IsSmall())
vtNorm = Z_AX ;
// determino se la guida è chiusa
bool bGuideClosed = pGuide->IsClosed() ;
// calcolo fat curve
ICURVEPOVECTOR vFatCrvs ;
if ( ! CalcCurveFatCurve( *pGuide, vFatCrvs, dDimH / 2, false, false))
return nullptr ;
// se necessario modifico i caps sugli estremi
if ( ! bGuideClosed) {
if ( ! AdjustSurfTriMeshRectSweptCaps( nCapType, vFatCrvs, vtNorm, pGuide, dDimH / 2))
return nullptr ;
}
PtrOwner<ISurfTriMesh> pSTM ;
if ( nCapType == RSCAP_NONE && ! bGuideClosed) {
// se nessun cap devo costruire separatamente le superfici top/bottom e quelle laterali
CICURVEPVECTOR vTopCurves ; vTopCurves.reserve( vFatCrvs.size()) ;
for ( int i = 0 ; i < ssize( vFatCrvs) ; i ++)
vTopCurves.emplace_back( vFatCrvs[i]) ;
PtrOwner<ISurfTriMesh> pSrfTop( GetSurfTriMeshByRegion( vTopCurves, dLinTol)) ;
if ( IsNull( pSrfTop))
return nullptr ;
PtrOwner<ISurfTriMesh> pSrfBot( pSrfTop->Clone()) ;
if ( IsNull( pSrfBot))
return nullptr ;
pSrfBot->Translate( -dDimV * vtNorm) ;
pSrfBot->Invert() ;
int nBuckets = max( 4 * ( pSrfTop->GetVertexSize()), 1000) ;
StmFromTriangleSoup stmSoup ;
if ( ! stmSoup.Start( nBuckets))
return nullptr ;
stmSoup.AddSurfTriMesh( *pSrfTop) ;
stmSoup.AddSurfTriMesh( *pSrfBot) ;
// rimuovo le giunzioni per costruire le superfici laterali
if ( ! RemoveFatCurveJunctions( vFatCrvs))
return nullptr ;
for ( int i = 0 ; i < ssize( vFatCrvs) ; i ++) {
PtrOwner<ISurfTriMesh> pSrfLat( GetSurfTriMeshByExtrusion( vFatCrvs[i], -dDimV * vtNorm, false, dLinTol)) ;
if ( IsNull( pSrfLat))
return nullptr ;
pSrfLat->Invert() ;
stmSoup.AddSurfTriMesh( *pSrfLat) ;
}
if ( ! stmSoup.End())
return nullptr ;
pSTM.Set( stmSoup.GetSurf()) ;
}
else {
// costruisco la superficie per estrusione dalle fat curve
CICURVEPVECTOR vCurves ; vCurves.reserve( vFatCrvs.size()) ;
for ( int i = 0 ; i < ssize( vFatCrvs) ; i ++)
vCurves.emplace_back( vFatCrvs[i]) ;
pSTM.Set( GetSurfTriMeshByRegionExtrusion( vCurves, - dDimV * vtNorm, dLinTol)) ;
}
if ( IsNull( pSTM))
return nullptr ;
// salvo tolleranza lineare usata e imposto angolo per smooth
pSTM->SetLinearTolerance( dLinTol) ;
pSTM->SetSmoothAngle( 20) ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
static ICurveComposite*
Connect( const ICurve* pGuide, ICURVEPOVECTOR& vCrvs, const INTVECTOR& vIdx, double dRadius, double dBevelH,
double dBevelV, const Vector3d& vtNorm, int nCapType)
{
double dOffsMin = 0.5 * dRadius - dBevelH ;
double dOffsMax = 0.5 * dRadius ;
ICURVEPOVECTOR vMedialAxis ;
CalcCurveMedialAxis( *pGuide, vMedialAxis, WMAT_BOTHSIDES) ;
// ricavo i punti di collasso nella regione tra i due offset per identificare se sono necessari dei bisettori
// per ricreare al meglio la curva di autointersezione della superficie laterale del solido di swept
PNTVECTOR vPtRef ;
for ( int i = 0 ; i < ssize( vMedialAxis) ; i ++) {
for ( int j = 0 ; j < 2 ; j ++) { // j = 0 start, j = 1 end
double dPar = vMedialAxis[i]->GetTempParam( j) ;
if ( dOffsMin + EPS_SMALL < dPar && dPar < dOffsMax + EPS_SMALL) {
Point3d ptP ;
if ( j == 0)
vMedialAxis[i]->GetStartPoint( ptP) ;
else
vMedialAxis[i]->GetEndPoint( ptP) ;
// traslazione per quota finale
double dDelta = - dBevelV / dBevelH * ( dPar - dOffsMin) ;
ptP.Translate( dDelta * vtNorm) ;
// verifico se è già stato individuato
bool bFound = false ;
for ( int k = 0 ; k < ssize( vPtRef) ; k ++) {
if ( AreSamePointApprox( vPtRef[k], ptP)) {
bFound = true ;
break ;
}
}
if ( bFound)
continue ;
// traslo sul piano delle curve per trovare la distanza
ptP.Translate( - ( dDelta + dBevelV) * vtNorm) ;
DistPointCurve distPC( ptP, *vCrvs[0]) ;
double dDist ; distPC.GetDist( dDist) ;
if ( dDist > dBevelH + EPS_SMALL) {
ptP.Translate( ( dBevelV + dDelta) * vtNorm) ;
vPtRef.emplace_back( ptP) ;
}
}
}
}
// se non serve collegamento perchè singola curva e non servono bisettori extra, non modifico la curva passata
if ( ssize( vIdx) == 1 && vPtRef.empty())
return nullptr ;
// conservo solo i tratti di medial axis compresi tra i due offset ( dOffsMin < <= dOffsMax)
ICRVCOMPOPOVECTOR vMedialCrvs ;
Voronoi* pVoronoi = GetCurveVoronoi( *pGuide) ;
if ( pVoronoi == nullptr)
return nullptr ;
PNTVECTOR vFlatCapsPtRef ;
double dTmp, dLast ;
pGuide->GetDomain( dTmp, dLast) ;
for ( int i = 0 ; i < ssize( vMedialAxis) ; i ++) {
double dPar1 = vMedialAxis[i]->GetTempParam( 0) ;
double dPar2 = vMedialAxis[i]->GetTempParam( 1) ;
// se non comprende l'offset non va considerato
if ( dPar1 > dOffsMax + EPS_ZERO || dPar2 < dOffsMin + EPS_ZERO)
continue ;
if ( abs( dPar1 - dOffsMax) < EPS_ZERO && abs( dPar2 - dOffsMax) > EPS_ZERO)
continue ;
// se flat caps ignoro i bisettori che hanno come sito un estremo della giuda
if ( nCapType == RSCAP_FLAT) {
int nBisector = vMedialAxis[i]->GetTempProp() ;
int nCrv1, nSub1, nCrv2, nSub2, nTmp1, nTmp2 ;
pVoronoi->GetBisectorSites( nBisector, nCrv1, nTmp1, nSub1, nCrv2, nTmp2, nSub2) ;
if ( nSub1 == 0 || nSub2 == 0 || nSub1 == dLast || nSub2 == dLast) {
// salvo i suoi estremi
Point3d ptS ; vMedialAxis[i]->GetStartPoint( ptS) ;
Point3d ptE ; vMedialAxis[i]->GetEndPoint( ptE) ;
double dDeltaS = - dBevelV / dBevelH * ( dPar1 - dOffsMin) ;
double dDeltaE = - dBevelV / dBevelH * ( dPar2 - dOffsMin) ;
vFlatCapsPtRef.emplace_back( ptS + dDeltaS * vtNorm) ;
vFlatCapsPtRef.emplace_back( ptE + dDeltaE * vtNorm) ;
continue ;
}
}
if ( dOffsMin + EPS_ZERO < dPar1 && dPar2 < dOffsMax + EPS_ZERO) {
// tratto va conservato completamente
vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ;
}
else {
// determino solo la parte compresa dall'offset. Per maggior precisione calcolo il punto di trim esatto con Voronoi
// senza affidarmi ai parametri salvati nel bisettore approssimato
int nBisector = vMedialAxis[i]->GetTempProp() ;
Point3d ptP ;
if ( pVoronoi->GetBisectorPointAtParam( nBisector, dOffsMin, ptP))
continue ;
double dTmp, dTrimParE ;
vMedialAxis[i]->GetDomain( dTmp, dTrimParE) ;
if ( pVoronoi->GetBisectorPointAtParam( nBisector, dOffsMax, ptP))
if ( ! vMedialAxis[i]->GetParamAtPoint( ptP, dTrimParE, 100 * EPS_SMALL))
return nullptr ;
if ( dTrimParE < EPS_SMALL)
continue ; // la curva si cancella completamente
vMedialAxis[i]->TrimEndAtParam( dTrimParE) ;
// aggiusto i temp param
vMedialAxis[i]->SetTempParam( dOffsMax, 1) ;
vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ;
}
// aggiusto la quota
if ( vMedialCrvs.back()->GetCurveCount() == 1) {
double dPar1 = vMedialCrvs.back()->GetTempParam( 0) ;
double dPar2 = vMedialCrvs.back()->GetTempParam( 1) ;
Point3d ptS, ptE ;
vMedialCrvs.back()->GetStartPoint( ptS) ;
vMedialCrvs.back()->GetEndPoint( ptE) ;
double dDeltaS = - dBevelV / dBevelH * ( dPar1 - dOffsMin) ;
double dDeltaE = - dBevelV / dBevelH * ( dPar2 - dOffsMin) ;
vMedialCrvs.back()->ModifyStart( ptS + vtNorm * dDeltaS) ;
vMedialCrvs.back()->ModifyEnd( ptE + vtNorm * dDeltaE) ;
}
else {
for ( int j = 0 ; j < vMedialCrvs.back()->GetCurveCount() ; j ++) {
double dPar = vMedialCrvs.back()->GetCurve( j)->GetTempParam( 0) ;
Point3d ptJ ;
vMedialCrvs.back()->GetPointD1D2( j, ICurve::FROM_MINUS, ptJ) ;
double dDelta = - dBevelV / dBevelH * ( dPar - dOffsMin) ;
vMedialCrvs.back()->ModifyJoint( j, ptJ + vtNorm * dDelta) ;
}
Point3d ptE ; vMedialCrvs.back()->GetEndPoint( ptE) ;
double dPar = vMedialCrvs.back()->GetTempParam( 1) ;
double dDelta = - dBevelV / dBevelH * ( dPar - dOffsMin) ;
vMedialCrvs.back()->ModifyEnd( ptE + vtNorm * dDelta) ;
}
}
// chain dei medialaxis
if ( ! GetChainedCurves( vMedialCrvs, EPS_SMALL, true, false))
return nullptr ;
// nel caso di flat caps i bisettori legati agli estremi devono essere collegati manualmente al bordo esterno
if ( nCapType == RSCAP_FLAT) {
for ( int i = 0 ; i < ssize( vMedialCrvs) ; i ++) {
bool bConnect = false ;
for ( int k = 0 ; k < 2 && ! bConnect ; k ++) {
Point3d ptP ;
if ( k == 0)
vMedialCrvs[i]->GetStartPoint( ptP) ;
else
vMedialCrvs[i]->GetEndPoint( ptP) ;
for ( int j = 0 ; j < ssize( vFlatCapsPtRef) ; j ++) {
if ( AreSamePointApprox( ptP, vFlatCapsPtRef[j])) {
CurveComposite* pCompo = GetBasicCurveComposite( vMedialCrvs[i]) ;
if ( pCompo == nullptr)
return nullptr ;
DistPointCurve distPC( ptP, *vCrvs[0]) ;
Point3d ptMinDist ; int nFlag ;
distPC.GetMinDistPoint( 0, ptMinDist, nFlag) ;
pCompo->AddLine( ptMinDist, k == 1) ;
bConnect = true ;
break ;
}
}
}
}
}
// creo catena
double dChainTol = 50 * EPS_SMALL ;
CurveComposite* pResult = nullptr ;
for ( int i = 0 ; i < ssize( vMedialCrvs) ; i++) {
// trovo le curve che collega
Point3d ptS = P_INVALID ; vMedialCrvs[i]->GetStartPoint( ptS) ;
Point3d ptE = P_INVALID ; vMedialCrvs[i]->GetEndPoint( ptE) ;
double dParS = -1, dParE = -1 ;
int nS = -1, nE = -1 ;
for ( int j = 0 ; j < ssize( vIdx) ; j ++) {
if ( vCrvs[vIdx[j]] == nullptr)
continue ;
if ( nS == -1 && vCrvs[vIdx[j]]->GetParamAtPoint( ptS, dParS, dChainTol))
nS = vIdx[j] ;
else if ( nE == -1 && vCrvs[vIdx[j]]->GetParamAtPoint( ptE, dParE, dChainTol))
nE = vIdx[j] ;
if ( nS != -1 && nE != -1)
break ;
}
// i parametri devono essere su un joint per costruzione
dParS = round( dParS) ;
dParE = round( dParE) ;
// se non arriva su una curva va scartato
if ( nS == -1 && nE == -1)
continue ;
// se arriva su una sola curva, verifico che non si innesti su un altro bisettore e che sia un tratto necessario
// per la gestione di parti collassate controllando i ptRef
if ( nS == -1 || nE == -1) {
CurveComposite* pCompo = GetBasicCurveComposite( vCrvs[max(nS, nE)]) ;
if ( pCompo == nullptr)
return nullptr ;
double dRefPar = min( max( dParS, dParE), pCompo->GetCurveCount() - 1.0) ;
int nCrv = int( dRefPar + 0.5) ;
int nRefProp = pCompo->GetCurve( nCrv)->GetTempProp() ;
if ( nRefProp == RS_LINK || nRefProp == RS_LINK_BACK)
continue ;
bool bValid = false ;
for ( int j = 0 ; j < ssize( vPtRef) && ! bValid ; j ++) {
if ( vPtRef[j].IsValid())
bValid = vMedialCrvs[i]->IsPointOn( vPtRef[j]) ;
}
if ( ! bValid)
continue ;
// sistemo i parametri in modo che sia sempre definita la curva per lo start
if ( nS == -1) {
nS = nE ;
dParS = dParE ;
nE = -1 ;
dParE = -1 ;
vMedialCrvs[i]->Invert() ;
}
}
// setto tutte le temp prop ad un valore fissato per poter identificare in seguito il tratto di bisettore
for ( int nC = 0 ; nC < vMedialCrvs[i]->GetCurveCount() ; nC ++)
vMedialCrvs[i]->SetCurveTempProp( nC, RS_LINK) ;
pResult = GetBasicCurveComposite( vCrvs[nS]) ;
ICurve* pResultEnd = nullptr ;
if ( dParS > EPS_SMALL) {
pResultEnd = pResult->CopyParamRange( dParS, pResult->GetCurveCount()) ;
pResult->TrimEndAtParam( dParS) ;
}
pResult->AddCurve( vMedialCrvs[i]->Clone(), true, dChainTol) ;
if ( nE != -1) {
GetBasicCurveComposite( vCrvs[nE])->ChangeStartPoint( dParE) ;
pResult->AddCurve( Release( vCrvs[nE]), true, dChainTol) ;
}
vMedialCrvs[i]->Invert() ;
for ( int nC = 0 ; nC < vMedialCrvs[i]->GetCurveCount() ; nC ++)
vMedialCrvs[i]->SetCurveTempProp( nC, RS_LINK_BACK) ;
pResult->AddCurve( Release( vMedialCrvs[i]), true, dChainTol) ;
if ( pResultEnd != nullptr)
pResult->AddCurve( pResultEnd, true, dChainTol) ;
// forzo chiusura
pResult->Close() ;
}
// verifico di aver collegato tutte le curve
INTVECTOR vResidualIdx ;
for ( int i = 0 ; i < ssize( vIdx) ; i ++) {
if ( !IsNull( vCrvs[vIdx[i]]))
vResidualIdx.emplace_back( vIdx[i]) ;
}
if ( ssize( vResidualIdx) > 1)
return nullptr ;
// sistemo il punto di inizio della curva risultante su una curva derivante dalla guida e non su un tratto di bisettore
if ( pResult != nullptr) {
for ( int i = 0 ; i < pResult->GetCurveCount() ; i ++) {
if ( pResult->GetCurve(i)->GetTempProp() > EPS_SMALL) {
pResult->ChangeStartPoint( i + 0.5) ;
break ;
}
}
}
return pResult ;
}
//-------------------------------------------------------------------------------
static bool
GetSplitParams( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, int nCrv2, bool bStart, const Vector3d& vtNorm,
DBLVECTOR& vPar1, DBLVECTOR& vPar2)
{
int nFlag ; double dParMinDist ;
Point3d ptP ;
int nTmpProp = pCrv2->GetCurve( nCrv2)->GetTempProp() ;
int nStart = int( floor( vPar1.back()) + 0.5) ;
// se la curva di riferimento è un flat cap lo split è definito dal cap stesso
if ( nTmpProp == RS_FLATCAP_START || nTmpProp == RS_FLATCAP_END)
return true ;
// se split per bisettore che torna su se stesso considero uno dei punti a min dist sull'altra curva
else if ( nTmpProp == RS_LINK || nTmpProp == RS_LINK_BACK) {
pCrv2->GetCurve( nCrv2)->GetEndPoint( ptP) ;
DistPointCurve distPC( ptP, *pCrv1) ;
distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ;
vPar1.emplace_back( dParMinDist) ;
vPar2.emplace_back( nCrv2 + 1) ;
}
// se deriva da una curva ben definita della giuda cerco la corrispondente dal lato corretto sull'altra curva
else if ( nTmpProp != 0) {
if ( bStart)
pCrv2->GetCurve( nCrv2)->GetEndPoint( ptP) ;
else
pCrv2->GetCurve( nCrv2)->GetStartPoint( ptP) ;
for ( int j = nStart ; j < pCrv1->GetCurveCount() ; j ++) {
int nTmpProp1 = pCrv1->GetCurve( j)->GetTempProp() ;
if ( nTmpProp1 == nTmpProp) {
DistPointCurve distPC( ptP, *pCrv1->GetCurve( j)) ;
int nSide = 0 ;
distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ;
if ( nSide == MDS_RIGHT) {
distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ;
vPar1.emplace_back( dParMinDist + j) ;
if ( bStart)
vPar2.emplace_back( nCrv2 + 1) ;
else
vPar2.emplace_back( nCrv2) ;
break ;
}
}
}
}
// se deriva da raccordo devo controllare il più vicino
else {
pCrv2->GetCurve( nCrv2)->GetMidPoint( ptP) ;
double dMinDist = INFINITO ;
for ( int j = nStart ; j < pCrv1->GetCurveCount() ; j ++) {
int nTmpProp1 = pCrv1->GetCurve( j)->GetTempProp() ;
if ( nTmpProp1 == nTmpProp) {
DistPointCurve distPC( ptP, *pCrv1->GetCurve( j)) ;
int nSide = 0 ;
distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ;
if ( nSide == MDS_RIGHT) {
double dCurrDist ; distPC.GetDist( dCurrDist) ;
if ( dCurrDist < dMinDist - EPS_SMALL) {
distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ;
dParMinDist += j ;
dMinDist = dCurrDist ;
}
}
}
}
vPar1.emplace_back( dParMinDist) ;
vPar2.emplace_back( nCrv2 + 0.5) ;
}
return true ;
}
//-------------------------------------------------------------------------------
static bool
Associate( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, const Vector3d& vtNorm, DBLVECTOR& vPar1, DBLVECTOR& vPar2)
{
vPar1.emplace_back( 0) ;
vPar2.emplace_back( 0) ;
// segnalo parametro di split per le curve :
// - in corrispondenza dei flat caps
// - quando trovo l'inizio o la fine di un bisettore
// - dove il bisettore ritorna su se stesso
bool bBisector = false ;
for ( int i = 0 ; i < pCrv2->GetCurveCount() ; i++) {
int nTmpProp = pCrv2->GetCurve( i)->GetTempProp() ;
// se flat cap lo associo con il corrispondente
if ( nTmpProp == RS_FLATCAP_START || nTmpProp == RS_FLATCAP_END) {
for ( int j = 0 ; j < pCrv1->GetCurveCount() ; j ++) {
int nOrigCrv1 = pCrv1->GetCurve( j)->GetTempProp() ;
if ( nOrigCrv1 == nTmpProp) {
vPar1.emplace_back( j) ;
vPar1.emplace_back( j + 1) ;
vPar2.emplace_back( i) ;
vPar2.emplace_back( i + 1) ;
break ;
}
}
}
// se bisettore o tratto aggiunto per chiudere flat cap
else if ( nTmpProp == RS_LINK || nTmpProp == RS_LINK_BACK || nTmpProp == RS_FLATCAP_EXTRA) {
if ( ! bBisector) {
bBisector = true ; // inizio bisettore
if ( i > 0)
GetSplitParams( pCrv1, pCrv2, i-1, true, vtNorm, vPar1, vPar2) ;
}
else {
// verfico se il bisettore sta tornando su se stesso ( ovvero si passa da link a link_back o viceversa)
int nTmpPropPrev = pCrv2->GetCurve( i-1)->GetTempProp() ;
if ( nTmpPropPrev != nTmpProp && ( nTmpPropPrev == RS_LINK || nTmpPropPrev == RS_LINK_BACK) &&
( nTmpProp == RS_LINK || nTmpProp == RS_LINK_BACK))
GetSplitParams( pCrv1, pCrv2, i-1, true, vtNorm, vPar1, vPar2) ;
}
}
// se fine del bisettore
else {
if ( bBisector) {
bBisector = false ;
GetSplitParams( pCrv1, pCrv2, i, false, vtNorm, vPar1, vPar2) ;
}
}
}
// parametro finale
double dTmp, dE1, dE2 ;
pCrv1->GetDomain( dTmp, dE1) ;
pCrv2->GetDomain( dTmp, dE2) ;
vPar1.emplace_back( dE1) ;
vPar2.emplace_back( dE2) ;
// verifico che i parametri calcolati siano ordinati
for ( int i = 1 ; i < ssize( vPar1) ; i ++)
if ( vPar1[i] < vPar1[i-1] - EPS_SMALL)
return false ;
return true ;
}
//-------------------------------------------------------------------------------
static POLYLINEVECTOR
SplitPolyLine( const PolyLine& PLOrig, const DBLVECTOR& vPar)
{
POLYLINEVECTOR vPL ;
vPL.reserve( vPar.size()) ;
double dPar1, dPar2 ;
Point3d pt1, pt2 ;
bool bFound = PLOrig.GetFirstUPoint( &dPar1, &pt1) ;
PolyLine PLCurr ; PLCurr.AddUPoint( 0, pt1) ;
bFound = PLOrig.GetNextUPoint( &dPar2, &pt2) ;
for ( int i = 1 ; i < ssize( vPar) && bFound ; i ++) {
while( dPar2 < vPar[i] - EPS_SMALL && bFound) {
// se non si è ancora arrivati al parametro richiesto devo aggiungere il punto e passare al successivo
PLCurr.AddUPoint( dPar2, pt2) ;
dPar1 = dPar2 ;
pt1 = pt2 ;
bFound = PLOrig.GetNextUPoint( &dPar2, &pt2) ;
}
if ( bFound) {
// ricavo il punto di interruzione
double dCoeff = ( vPar[i] - dPar1) / ( dPar2 - dPar1) ;
Point3d ptSplit = Media( pt1, pt2, dCoeff) ;
PLCurr.AddUPoint( vPar[i], ptSplit) ;
// ho concluso una polyline e pareparo la prossima
vPL.emplace_back( PLCurr) ;
PLCurr.Clear() ;
PLCurr.AddUPoint( vPar[i], ptSplit) ;
}
}
// ultima curva
if ( PLCurr.GetPointNbr() > 1)
vPL.emplace_back( PLCurr) ;
return vPL ;
}
//-------------------------------------------------------------------------------
static bool
AdjustStartPoint( ICurve* pCrv1, ICurveComposite* pCrv2, int nCapType)
{
// se flat caps sistemo il punto di inizio sul cap start se presente
if ( nCapType == RSCAP_FLAT) {
ICurveComposite* pCompo1 = GetCurveComposite( pCrv1) ;
if ( pCompo1 == nullptr)
return false ;
bool bFlatCap = false ;
for ( int i = 0 ; i < pCompo1->GetCurveCount() ; i ++) {
if ( pCompo1->GetCurve( i)->GetTempProp() == RS_FLATCAP_START) {
pCompo1->ChangeStartPoint( i) ;
bFlatCap = true ;
break ;
}
}
if ( bFlatCap) {
for ( int i = 0 ; i < pCrv2->GetCurveCount() ; i ++) {
if ( pCrv2->GetCurve( i)->GetTempProp() == RS_FLATCAP_START) {
pCrv2->ChangeStartPoint( i) ;
break ;
}
}
return true ;
}
}
Point3d ptStart ; pCrv1->GetStartPoint( ptStart) ;
double dPar ; int nFlag ;
DistPointCurve( ptStart, *pCrv2).GetParamAtMinDistPoint( 0, dPar, nFlag) ;
pCrv2->ChangeStartPoint( dPar) ;
return true ;
}
//-------------------------------------------------------------------------------
static POLYLINEVECTOR
RemoveBisectorsFromPolylines( const PolyLine& PL, const ICurveComposite* pCompo)
{
POLYLINEVECTOR vPL ;
double dS, dE ; pCompo->GetDomain( dS, dE) ;
double dPar ;
Point3d ptP ;
bool bFound = PL.GetFirstUPoint( &dPar, &ptP) ;
PolyLine PLCurr ; PLCurr.AddUPoint( 0, ptP) ;
// verifico se è un bisettore controllando la curva di cui è l'approssimazione
int nCrv = int( floor( dPar) + 0.5) ;
int nTempProp = pCompo->GetCurve( nCrv)->GetTempProp() ;
bool bBisector = ( nTempProp == RS_LINK || nTempProp == RS_LINK_BACK) ;
bFound = PL.GetNextUPoint( &dPar, &ptP) ;
while( bFound) {
// aggiungo il punto
PLCurr.AddUPoint( 0, ptP) ;
// verifico se ultimo punto
if ( abs( dPar - dE) < EPS_SMALL)
break ;
// verifico se è un bisettore
int nCrv = int( floor( dPar) + 0.5) ;
int nTempProp = pCompo->GetCurve( nCrv)->GetTempProp() ;
bool bBisectorCurr = ( nTempProp == RS_LINK || nTempProp == RS_LINK_BACK) ;
// se passo da un bisettore ad una curva standard o viceversa salvo la polyline trovata fino a quel momento ( se non è bisettore)
// e la resetto
if ( bBisector != bBisectorCurr) {
if ( ! bBisector)
vPL.emplace_back( PLCurr) ;
PLCurr.Clear() ;
PLCurr.AddUPoint( 0, ptP) ;
bBisector = bBisectorCurr ;
}
bFound = PL.GetNextUPoint( &dPar, &ptP) ;
}
// ultima curva
if ( PLCurr.GetPointNbr() > 1 && ! bBisector)
vPL.emplace_back( PLCurr) ;
return vPL ;
}
//-------------------------------------------------------------------------------
static ISurfTriMesh*
GetSurfTriMeshBeveledRectSwept( double dDimH, double dDimV, double dBevelH, double dBevelV, const ICurve* pGuide, int nCapType, double dLinTol)
{
// verifico che la linea guida sia piana
Plane3d plGuide ;
if ( ! pGuide->IsFlat( plGuide, true, 10 * EPS_SMALL))
return nullptr ;
Vector3d vtNorm ; pGuide->GetExtrusion( vtNorm) ;
if ( vtNorm.IsSmall())
vtNorm = Z_AX ;
// determino il punto centrale della sezione
Point3d ptCen ; pGuide->GetStartPoint( ptCen) ;
ptCen -= dDimV / 2 * vtNorm ;
// determino se la guida è chiusa
bool bGuideClosed = pGuide->IsClosed() ;
// calcolo le fat curve
ICURVEPOVECTOR vFatCrvs1 ;
if ( ! CalcCurveFatCurve( *pGuide, vFatCrvs1, dDimH / 2 - dBevelH, false, false))
return nullptr ;
ICURVEPOVECTOR vFatCrvs2 ;
if ( ! CalcCurveFatCurve( *pGuide, vFatCrvs2, dDimH / 2, false, false))
return nullptr ;
// aggiusto per eventuali caps
if ( ! bGuideClosed) {
if ( ! AdjustSurfTriMeshRectSweptCaps( nCapType, vFatCrvs1, vtNorm, pGuide, dDimH / 2 - dBevelH) ||
! AdjustSurfTriMeshRectSweptCaps( nCapType, vFatCrvs2, vtNorm, pGuide, dDimH / 2))
return nullptr ;
}
// setto estrusione
for ( int i = 0 ; i < ssize( vFatCrvs1) ; i++)
vFatCrvs1[i]->SetExtrusion( vtNorm) ;
for ( int i = 0 ; i < ssize( vFatCrvs2) ; i++)
vFatCrvs2[i]->SetExtrusion( vtNorm) ;
// ordino i vettori per avere il loop esterno per primo
for ( int i = 0 ; i < ssize( vFatCrvs1) ; i ++) {
Plane3d plPlane ; double dTmp ;
vFatCrvs1[i]->GetArea( plPlane, dTmp) ;
if ( AreSameVectorApprox( plPlane.GetVersN(), vtNorm)) {
swap( vFatCrvs1[i], vFatCrvs1[0]) ;
break ;
}
}
for ( int i = 0 ; i < ssize( vFatCrvs2) ; i ++) {
Plane3d plPlane ; double dTmp ;
vFatCrvs2[i]->GetArea( plPlane, dTmp) ;
if ( AreSameVectorApprox( plPlane.GetVersN(), vtNorm)) {
swap( vFatCrvs2[i], vFatCrvs2[0]) ;
break ;
}
}
for ( int i = 0 ; i < ssize( vFatCrvs2) ; i++)
vFatCrvs2[i]->Translate( - dBevelV * vtNorm) ;
// se necessario rimuovo le giunzioni per costruire le superfici laterali
ICURVEPOVECTOR vFatCrvsOrig1 ;
if ( ! bGuideClosed && nCapType == RSCAP_NONE) {
// salvo le curve con le giunzioni per il calcolo della superficie top
for ( int i = 0 ; i < ssize( vFatCrvs1) ; i++)
vFatCrvsOrig1.emplace_back( vFatCrvs1[i]->Clone()) ;
if ( ! RemoveFatCurveJunctions( vFatCrvs1) || ! RemoveFatCurveJunctions( vFatCrvs2))
return nullptr ;
}
// creo le parti di superficie
StmFromTriangleSoup stmSoup ;
if ( ! stmSoup.Start())
return nullptr ;
// superfici laterali e verticali
POLYLINEVECTOR vPL1( vFatCrvs1.size()) ;
BOOLVECTOR bUsed( vFatCrvs2.size(), false) ;
for ( int i = 0 ; i < ssize( vFatCrvs1) ; i ++) {
// cerco le FatCurve2 che contengono la FatCurve1 corrente
INTVECTOR vAssociatedIdx ;
for ( int j = 0 ; j < ssize( vFatCrvs2) ; j ++) {
if ( ! bUsed[j]) {
CurveComposite* pCompo2 = GetBasicCurveComposite( vFatCrvs2[j]) ;
if ( pCompo2 == nullptr)
return nullptr ;
for ( int k = 0 ; k < pCompo2->GetCurveCount() ; k ++) {
int nProp = pCompo2->GetCurve( k)->GetTempProp() ;
if ( nProp != RS_FLATCAP_START && nProp != RS_FLATCAP_END) {
Point3d ptTest ; vFatCrvs2[j]->GetPointD1D2( k + 0.5, ICurve::FROM_MINUS, ptTest) ;
DistPointCurve distPC( ptTest, *vFatCrvs1[i]) ;
int nSide ;
distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ;
if ( nSide == MDS_RIGHT) {
bUsed[j] = true ;
vAssociatedIdx.emplace_back( j) ;
}
break ;
}
}
}
}
ISURFTMPOVECTOR vSurfLat ;
ISURFTMPOVECTOR vSurfVert ;
// a) se non ha associate allora collassa in una curva ad una quota opportuna
if ( vAssociatedIdx.empty()) {
double dLimitOffs ;
if ( ! CalcCurveLimitOffset( *vFatCrvs1[i], dLimitOffs))
return nullptr ;
// per un risultato migliore applico un offset leggermente più piccolo di quello limite e aggiungo una superficie di chiusura
OffsetCurve OffsCrv ;
OffsCrv.Make( vFatCrvs1[i], dLimitOffs - 10 * EPS_SMALL, ICurve::OFF_FILLET) ;
PtrOwner<ICurveComposite> pAssociatedCrv( ConvertCurveToComposite( OffsCrv.GetLongerCurve())) ;
if ( IsNull( pAssociatedCrv))
return nullptr ;
pAssociatedCrv->Translate( - dBevelV / dBevelH * dLimitOffs * vtNorm) ;
AdjustStartPoint( vFatCrvs1[i], pAssociatedCrv, RSCAP_NONE) ;
vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ;
PolyLine PL2 ;
pAssociatedCrv->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ;
PtrOwner<SurfTriMesh> pSrfL( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfL))
return nullptr ;
pSrfL->CreateByTwoCurves( vPL1[i], PL2, ISurfTriMesh::RLT_MINDIST) ;
pSrfL->Invert() ;
PtrOwner<SurfTriMesh> pSrfBase( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfBase))
return nullptr ;
pSrfBase->CreateByFlatContour( PL2) ;
pSrfBase->Invert() ;
vSurfLat.emplace_back( Release( pSrfL)) ;
vSurfLat.emplace_back( Release( pSrfBase)) ;
}
// b) se ha una o più curve associate
else {
// collego le curve con opportuni bisettori e se verifico se sono necessari dei bisettori per ricreare al meglio
// la curva di autointersezione della superficie laterale del solido di swept
ICurveComposite* pFatCrv2 = Connect( pGuide, vFatCrvs2, vAssociatedIdx, dDimH, dBevelH, dBevelV, vtNorm, nCapType) ;
if ( ssize( vAssociatedIdx) == 1 && pFatCrv2 == nullptr) {
// se aveva unica associata e non sono stati necessari bisettori extra è rigata tra le due curve
ICurveComposite* pAssociatedCrv = GetCurveComposite( vFatCrvs2[vAssociatedIdx[0]]) ;
if ( pAssociatedCrv == nullptr)
return nullptr ;
// per avere rigata ottimale modifico il punto di inizio
AdjustStartPoint( vFatCrvs1[i], pAssociatedCrv, nCapType) ;
// creo le polylines
vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ;
PolyLine PL2 ;
pAssociatedCrv->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ;
PtrOwner<SurfTriMesh> pSrfL( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfL) || ! pSrfL->CreateByTwoCurves( vPL1[i], PL2, ISurfTriMesh::RLT_MINDIST))
return nullptr ;
pSrfL->Invert() ;
vSurfLat.emplace_back( Release( pSrfL)) ;
PtrOwner<SurfTriMesh> pSrfV( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfV) || ! pSrfV->CreateByExtrusion( PL2, ( - dDimV + 2 * dBevelV) * vtNorm))
return nullptr ;
pSrfV->Invert() ;
vSurfVert.emplace_back( Release( pSrfV)) ;
}
else {
if ( pFatCrv2 == nullptr)
return nullptr ;
// aggiusto il punto di inizio sulla curva 1 per avvicinarlo a quello ottimale già individuato dal connect per la curva 2
Point3d ptStart ; pFatCrv2->GetStartPoint( ptStart) ;
double dPar ; int nFlag ;
DistPointCurve( ptStart, *vFatCrvs1[i]).GetParamAtMinDistPoint( 0, dPar, nFlag) ;
GetCurveComposite( vFatCrvs1[i])->ChangeStartPoint( dPar) ;
// creo le polylines e le spezzo opportunamente per avere delle rigate ottimali ( i tratti derivanti dai bisettori
// essendo sovrapposti per tratto di andata e di ritorno potrebbero creare problemi nell'identificazione dei punti
// a minima distanza)
vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ;
PolyLine PL2 ;
pFatCrv2->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ;
DBLVECTOR vPar1, vPar2 ;
if ( ! Associate( GetCurveComposite( vFatCrvs1[i]), pFatCrv2, vtNorm, vPar1, vPar2))
return nullptr ;
POLYLINEVECTOR vPolyLine1 = SplitPolyLine( vPL1[i], vPar1) ;
POLYLINEVECTOR vPolyLine2 = SplitPolyLine( PL2, vPar2) ;
// visto che spezzo la polyline 1 per le superfici laterali, per evitare TJunctions con la superficie top aggiorno
// la polyline 1
vPL1[i].Clear() ;
for ( int j = 0 ; j < ssize( vPolyLine1) ; j ++) {
PNTULIST lUPnts = vPolyLine1[j].GetUPointList() ;
for ( POINTU& upt : lUPnts)
vPL1[i].AddUPoint( upt.second, upt.first) ;
}
for ( int j = 0 ; j < ssize( vPolyLine1) ; j ++) {
if ( vPolyLine1[j].GetPointNbr() > 1 && vPolyLine2[j].GetPointNbr() > 1) {
PtrOwner<SurfTriMesh> pSrfL( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfL))
return nullptr ;
pSrfL->CreateByTwoCurves( vPolyLine1[j], vPolyLine2[j], ISurfTriMesh::RLT_MINDIST) ;
pSrfL->Invert() ;
vSurfLat.emplace_back( Release( pSrfL)) ;
}
}
// creazione delle superfici laterali verticali: per evitare problemi di TJunctions uso le stesse polylines
// utilzzate per il calcolo delle superfici laterali, rimuovendo però i tratti derivanti dai bisettori
// perchè corrispondono alle curve di autointersezione della superficie e quindi non generano pareti verticali
for ( int j = 0 ; j < ssize( vPolyLine2) ; j ++) {
POLYLINEVECTOR vPL = RemoveBisectorsFromPolylines( vPolyLine2[j], pFatCrv2) ;
for ( int k = 0 ; k < ssize( vPL) ; k++) {
PtrOwner<SurfTriMesh> pSrfV( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfV) || ! pSrfV->CreateByExtrusion( vPL[k], ( - dDimV + 2 * dBevelV) * vtNorm))
return nullptr ;
pSrfV->Invert() ;
vSurfVert.emplace_back( Release( pSrfV)) ;
}
}
}
}
// inserisco le superfici verticali
for ( int j = 0 ; j < ssize( vSurfVert) ; j ++)
stmSoup.AddSurfTriMesh( *vSurfVert[j]) ;
// inserisco le superfici laterali rigate e il loro mirror
for ( int j = 0 ; j < ssize( vSurfLat) ; j ++) {
stmSoup.AddSurfTriMesh( *vSurfLat[j]) ;
vSurfLat[j]->Mirror( ptCen, vtNorm) ;
stmSoup.AddSurfTriMesh( *vSurfLat[j]) ;
}
}
// superficie top
if ( ! vFatCrvsOrig1.empty()) {
vPL1.clear() ;
vPL1.resize( vFatCrvsOrig1.size()) ;
for ( int i = 0 ; i < ssize( vFatCrvsOrig1) ; i++)
vFatCrvsOrig1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ;
}
PtrOwner<ISurfTriMesh> pSrfTop( CreateSurfTriMesh()) ;
if ( IsNull( pSrfTop) || ! pSrfTop->CreateByPolygonWithHoles( vPL1))
return nullptr ;
stmSoup.AddSurfTriMesh( *pSrfTop) ;
// superficie bottom
pSrfTop->Translate( -dDimV * vtNorm) ;
pSrfTop->Invert() ;
stmSoup.AddSurfTriMesh( *pSrfTop) ;
if ( ! stmSoup.End())
return nullptr ;
PtrOwner<ISurfTriMesh> pSTM ;
pSTM.Set( stmSoup.GetSurf()) ;
if ( IsNull( pSTM))
return nullptr ;
// salvo tolleranza lineare usata e imposto angolo per smooth
pSTM->SetLinearTolerance( dLinTol) ;
pSTM->SetSmoothAngle( 20) ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshRectSwept( double dDimH, double dDimV, double dBevelH, double dBevelV, const ICurve* pGuide, int nCapType, double dLinTol)
{
// verifica parametri
if ( pGuide == nullptr || dBevelH > 0.4 * dDimH || dBevelV > 0.4 * dDimV)
return nullptr ;
// determino se sezione squadrata o con smusso
bool bSharp = ( dBevelH < 100 * EPS_SMALL || dBevelV < 100 * EPS_SMALL) ;
// eseguo
if ( bSharp)
return GetSurfTriMeshSharpRectSwept( dDimH, dDimV, pGuide, nCapType, dLinTol) ;
else
return GetSurfTriMeshBeveledRectSwept( dDimH, dDimV, dBevelH, dBevelV, pGuide, nCapType, dLinTol) ;
}
//-------------------------------------------------------------------------------
static ISurfTriMesh*
GetSurfTriMeshSweptInPlane( const ICurve* pSect, const ICurve* pGuide, const Vector3d& vtNorm, bool bCapEnds, double dLinTol)
{
// determino se la sezione è chiusa
bool bSectClosed = pSect->IsClosed() ;
// determino se la guida è chiusa
bool bGuideClosed = pGuide->IsClosed() ;
// riferimento all'inizio della linea guida
Frame3d frStart ;
Point3d ptStart ;
pGuide->GetStartPoint( ptStart) ;
Vector3d vtStart ;
pGuide->GetStartDir( vtStart) ;
frStart.Set( ptStart, -vtStart, vtStart ^ vtNorm) ;
// calcolo la polilinea che approssima la sezione
PolyLine PL ;
if ( ! pSect->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL))
return nullptr ;
// porto la sezione in questo riferimento e ve la appiattisco
if ( ! PL.ToLoc( frStart) || ! PL.Flatten())
return nullptr ;
// preparo collettore delle superfici componenti
StmFromTriangleSoup StmFts ;
if ( ! StmFts.Start())
return nullptr ;
// superficie swept
PtrOwner<ICurve> pPrevCrv ;
Point3d ptP ;
bool bPoint = PL.GetFirstPoint( ptP) ;
while ( bPoint) {
// nuova curva ( definita dall'Offset )
OffsetCurve OffsCrv ;
if ( ! OffsCrv.Make( pGuide, ptP.x, ICurve::OFF_FILLET) || OffsCrv.GetCurveCount() == 0)
return nullptr ;
PtrOwner<ICurve> pCurrCrv( OffsCrv.GetLongerCurve()) ;
if ( IsNull( pCurrCrv))
return nullptr ;
pCurrCrv->Translate( ptP.y * frStart.VersY()) ;
// se esiste la curva precedente, costruisco la rigata ( di tipo minima distanza)
if ( ! IsNull( pPrevCrv)) {
PtrOwner<ISurfTriMesh> pSr( GetSurfTriMeshRuled( pPrevCrv, pCurrCrv, ISurfTriMesh::RLT_MINDIST, dLinTol)) ;
if ( IsNull( pSr))
return nullptr ;
// inserisco nel collettore
StmFts.AddSurfTriMesh( *pSr) ;
}
// salvo la curva come prossima precedente
pPrevCrv.Set( pCurrCrv) ;
// prossimo punto
bPoint = PL.GetNextPoint( ptP) ;
}
// recupero la supeficie risultante
if ( ! StmFts.End())
return nullptr ;
PtrOwner<SurfTriMesh> pSTM( GetBasicSurfTriMesh( StmFts.GetSurf())) ;
if ( IsNull( pSTM))
return nullptr ;
// salvo tolleranza lineare usata
pSTM->SetLinearTolerance( dLinTol) ;
// se richiesti caps e sezione chiusa e guida aperta
if ( bCapEnds && bSectClosed && ! bGuideClosed) {
// verifico che le due estremità siano chiuse e piatte
POLYLINEVECTOR vPL ;
if ( ! pSTM->GetLoops( vPL) || vPL.size() != 2)
return nullptr ;
Plane3d plEnds ; double dArea ;
if ( ! vPL[0].IsClosedAndFlat( plEnds, dArea, 100 * EPS_SMALL))
return nullptr ;
if ( ! vPL[1].IsClosedAndFlat( plEnds, dArea, 100 * EPS_SMALL))
return nullptr ;
// aggiungo il cap sull'inizio
PtrOwner<SurfTriMesh> pSci( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSci) || ! pSci->CreateByFlatContour( PL))
return nullptr ;
pSci->ToGlob( frStart) ;
// unisco
pSTM->DoSewing( *pSci) ;
// riferimento alla fine della linea guida
Frame3d frEnd ;
Point3d ptEnd ;
pGuide->GetEndPoint( ptEnd) ;
Vector3d vtEnd ;
pGuide->GetEndDir( vtEnd) ;
frEnd.Set( ptEnd, -vtEnd, vtEnd ^ vtNorm) ;
// aggiungo il cap sulla fine
PtrOwner<SurfTriMesh> pSce( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSce) || ! pSce->CreateByFlatContour( PL))
return nullptr ;
pSce->Invert() ;
pSce->ToGlob( frEnd) ;
// unisco
pSTM->DoSewing( *pSce) ;
}
// se superficie risultante chiusa, verifico che la normale sia verso l'esterno
double dVol ;
if ( pSTM->GetVolume( dVol) && dVol < 0)
pSTM->Invert() ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
static ISurfTriMesh*
GetSurfTriMeshSwept3d( const ICurve* pSect, const ICurve* pGuide, const Vector3d& vtAx, bool bCapEnds, double dLinTol)
{
// determino se la sezione è chiusa
bool bSectClosed = pSect->IsClosed() ;
// determino se la guida è chiusa
bool bGuideClosed = pGuide->IsClosed() ;
// determino algoritmo da usare per calcolare i riferimenti lungo la curva
bool bRMF = ( ! vtAx.IsValid() || vtAx.IsSmall()) ;
// riferimento all'inizio della linea guida
Point3d ptStart ;
pGuide->GetStartPoint( ptStart) ;
Vector3d vtStart ;
pGuide->GetStartDir( vtStart) ;
Frame3d frStart ;
if ( bRMF) {
if ( ! frStart.Set( ptStart, vtStart))
return nullptr ;
}
else {
Vector3d vtAxX = vtAx ^ vtStart ;
if ( vtAxX.IsSmall()) {
vtAxX = FromUprightOrtho( vtAx) ;
vtAxX.Rotate( vtAx, 0, 1) ;
}
if ( ! frStart.Set( ptStart, vtStart, vtAxX))
return nullptr ;
}
// calcolo la polilinea che approssima la sezione
PolyLine PL ;
if ( ! pSect->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL))
return nullptr ;
// recupero la sezione dalla PolyLine approssimata come Curva
PtrOwner<CurveComposite> pSecLocApprox( CreateBasicCurveComposite()) ;
if ( IsNull( pSecLocApprox) ||
! pSecLocApprox->FromPolyLine( PL) ||
! pSecLocApprox->IsValid())
return nullptr ;
// porto la PolyLine e la curva della sezione nel riferimento nel punto iniziale della guida
PL.ToLoc( frStart) ;
pSecLocApprox->ToLoc( frStart) ;
// calcolo il vettore di Frames campionati lungo la guida mediante la tolleranza definita
FRAME3DVECTOR vFrames ;
if ( bRMF) {
RotationMinimizingFrame RMF ;
if ( ! RMF.Set( pGuide, frStart) ||
! RMF.GetFramesByTolerance( dLinTol, vFrames) || vFrames.empty())
return nullptr ;
}
else {
RotationXplaneFrame RXF ;
if ( ! RXF.Set( pGuide, vtAx, frStart.VersX()) ||
! RXF.GetFramesByTolerance( dLinTol, vFrames) || vFrames.empty())
return nullptr ;
}
// preparo collettore delle superfici componenti
StmFromTriangleSoup StmFts ;
if ( ! StmFts.Start())
return nullptr ;
// per ogni Frame calcolato, la sezione va roto-traslata lungo la guida
for ( int i = 0 ; i < int( vFrames.size()) - 1 ; ++ i) {
// definisco la sezione allo step corrente
PtrOwner<ICurve> pSecCurr( pSecLocApprox->Clone()) ;
if ( IsNull( pSecCurr) || ! pSecCurr->IsValid())
return nullptr ;
// considero la sezione ( in locale ) come vista dal globale
if ( ! pSecCurr->ToGlob( vFrames[i]))
return nullptr ;
// definisco la sezione allo step successivo
PtrOwner<ICurve> pSecSucc( pSecLocApprox->Clone()) ;
if ( IsNull( pSecSucc) || ! pSecSucc->IsValid())
return nullptr ;
// considero la sezione ( in locale ) come vista dal globale
if ( ! pSecSucc->ToGlob( vFrames[i+1]))
return nullptr ;
// creo la rigata tra queste due sezioni
PtrOwner<ISurfTriMesh> pSr( GetSurfTriMeshRuled( pSecCurr, pSecSucc, ISurfTriMesh::RLT_ISOPAR_SMOOTH, dLinTol)) ;
if ( IsNull( pSr) || ! pSr->IsValid())
return nullptr ;
// la inverto
pSr->Invert() ;
// inserisco la superficie nel collettore
StmFts.AddSurfTriMesh( *pSr) ;
}
// se richiesti caps e sezione chiusa e guida aperta
if ( bCapEnds && bSectClosed && ! bGuideClosed) {
// aggiungo il cap sull'inizio ( portandolo nel frame del punto iniziale della guida )
PtrOwner<SurfTriMesh> pSci( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSci) || ! pSci->CreateByFlatContour( PL))
return nullptr ;
pSci->ToGlob( vFrames.front()) ;
// aggiungo
StmFts.AddSurfTriMesh( *pSci) ;
// aggiungo il cap sulla fine ( portandolo nel frame del punto finale della guida )
PtrOwner<SurfTriMesh> pSce( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSce) || ! pSce->CreateByFlatContour( PL))
return nullptr ;
pSce->ToGlob( vFrames.back()) ;
// inverto
pSce->Invert() ;
// aggiungo
StmFts.AddSurfTriMesh( *pSce) ;
}
// recupero la supeficie risultante
if ( ! StmFts.End())
return nullptr ;
PtrOwner<SurfTriMesh> pSTM( GetBasicSurfTriMesh( StmFts.GetSurf())) ;
if ( IsNull( pSTM))
return nullptr ;
// salvo tolleranza lineare usata
pSTM->SetLinearTolerance( dLinTol) ;
// se superficie risultante chiusa, verifico che la normale sia verso l'esterno
double dVol ;
if ( pSTM->GetVolume( dVol) && dVol < 0)
pSTM->Invert() ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshSwept( const ICurve* pSect, const ICurve* pGuide, const Vector3d& vtAx,
bool bCapEnds, double dLinTol)
{
// verifica parametri
if ( pSect == nullptr || pGuide == nullptr)
return nullptr ;
bool bIsLine = false ;
if ( pGuide->GetType() == CRV_LINE)
bIsLine = true ;
else {
const CurveComposite* pCompo = GetBasicCurveComposite( pGuide) ;
Point3d ptStart, ptEnd ;
if ( pCompo != nullptr && pCompo->IsALine( 10 * EPS_SMALL, ptStart, ptEnd))
bIsLine = true ;
}
// se la guida è piana e il vettore di riferimento è non definito oppure non nullo
Plane3d plGuide ;
if ( pGuide->IsFlat( plGuide, bIsLine, 10 * EPS_SMALL) && ( ! vtAx.IsValid() || ! vtAx.IsSmall()))
return GetSurfTriMeshSweptInPlane( pSect, pGuide, plGuide.GetVersN(), bCapEnds, dLinTol) ;
// altrimenti swept 3d
return GetSurfTriMeshSwept3d( pSect, pGuide, vtAx, bCapEnds, dLinTol) ;
}
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshSwept( const ISurfFlatRegion* pSfrSect, const ICurve* pGuide, const Vector3d& vtAx,
bool bCapEnds, double dLinTol)
{
// verifica dei parametri
if ( pSfrSect == nullptr || pGuide == nullptr)
return nullptr ;
// predispongo collettore superfici componenti
StmFromTriangleSoup StmSoup ;
StmSoup.Start() ;
// per ogni loop della superficie, creo una Swept
for ( int nC = 0 ; nC < pSfrSect->GetChunkCount() ; ++ nC) {
for ( int nL = 0 ; nL < pSfrSect->GetLoopCount( nC) ; ++ nL) {
// recupero il loop
PtrOwner<ICurve> pCrvLoop( pSfrSect->GetLoop( nC, nL)) ;
if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid())
return nullptr ;
// creo la Trimesh Swept
PtrOwner<ISurfTriMesh> pStmLoopSwept( GetSurfTriMeshSwept( pCrvLoop, pGuide, vtAx, false, dLinTol)) ;
if ( IsNull( pStmLoopSwept) || ! pStmLoopSwept->IsValid())
return nullptr ;
// aggiungo la Swept ricavata al risultato finale ( come triangoli )
StmSoup.AddSurfTriMesh( *pStmLoopSwept) ;
}
}
// Recupero la superficie
if ( ! StmSoup.End())
return nullptr ;
PtrOwner<ISurfTriMesh> pStmSwept( StmSoup.GetSurf()) ;
if ( IsNull( pStmSwept))
return nullptr ;
// se rischiesta chiusura...
// Controllo solo che la guida non sia chiusa, la sezione derivando da una Flatregion è sempre chiusa
if ( bCapEnds && ! pGuide->IsClosed()) {
// recupero i loop all'inizio (dalla regione e apportunamente approssimati)
POLYLINEVECTOR vPLi ;
for ( int nC = 0 ; nC < pSfrSect->GetChunkCount() ; ++ nC) {
for ( int nL = 0 ; nL < pSfrSect->GetLoopCount( nC) ; ++ nL) {
vPLi.emplace_back() ;
if ( ! pSfrSect->ApproxLoopWithLines( nC, nL, dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPLi.back()))
return nullptr ;
}
}
// creo il cap sull'inizio e lo attacco alla swept ( è già in posizione giusta)
PtrOwner<SurfTriMesh> pSci( CreateBasicSurfTriMesh()) ;
INTMATRIX vnPLIndMat ;
if ( ! pSci->CreateByRegion( vPLi, vnPLIndMat))
return nullptr ;
pStmSwept->DoSewing( *pSci) ;
// recupero i loops alla fine
POLYLINEVECTOR vPLe ;
if ( ! pStmSwept->GetLoops( vPLe))
return nullptr ;
// creo la superficie alla fine e la attacco
PtrOwner<SurfTriMesh> pSce( CreateBasicSurfTriMesh()) ;
vnPLIndMat.clear() ;
if ( ! pSce->CreateByRegion( vPLe, vnPLIndMat))
return nullptr ;
// attacco la superficie finale alla swept
pSce->Invert() ;
pStmSwept->DoSewing( *pSce) ;
}
// se superficie risultante chiusa, verifico che la normale sia verso l'esterno
double dVol ;
if ( pStmSwept->GetVolume( dVol) && dVol < 0)
pStmSwept->Invert() ;
return Release( pStmSwept) ;
}
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshTransSwept( const ICurve* pSect, const ICurve* pGuide, bool bCapEnds, double dLinTol)
{
// verifica parametri
if ( pSect == nullptr || pGuide == nullptr)
return nullptr ;
// determino se la sezione è chiusa
bool bSectClosed = pSect->IsClosed() ;
// punto iniziale della sezione e vettore a inizio guida
Point3d ptStart ;
if ( ! pSect->GetStartPoint( ptStart))
return nullptr ;
Point3d ptGuide ;
if ( ! pGuide->GetStartPoint( ptGuide))
return nullptr ;
Vector3d vtDelta = ptStart - ptGuide ;
// calcolo la polilinea che approssima la guida
PolyLine PLG ;
if ( ! pGuide->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PLG))
return nullptr ;
// determino se la guida è chiusa
bool bGuideClosed = PLG.IsClosed() ;
// calcolo la superficie
PtrOwner<SurfTriMesh> pSTM( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSTM))
return nullptr ;
// salvo tolleranza lineare usata
pSTM->SetLinearTolerance( dLinTol) ;
// superficie swept
PtrOwner<ICurve> pPrevCrv ;
Point3d ptP ;
bool bPoint = PLG.GetFirstPoint( ptP) ;
while ( bPoint) {
// nuova curva
PtrOwner<ICurve> pCurrCrv( pSect->Clone()) ;
if ( IsNull( pCurrCrv))
return nullptr ;
pCurrCrv->Translate( ptP - ptStart + vtDelta) ;
// se esiste la curva precedente, costruisco la rigata (di tipo minima distanza)
if ( ! IsNull( pPrevCrv)) {
PtrOwner<ISurfTriMesh> pSr( GetSurfTriMeshRuled( pPrevCrv, pCurrCrv, ISurfTriMesh::RLT_ISOPAR, dLinTol)) ;
if ( IsNull( pSr))
return nullptr ;
pSTM->DoSewing( *pSr) ;
}
// salvo la curva come prossima precedente
pPrevCrv.Set( pCurrCrv) ;
// prossimo punto
bPoint = PLG.GetNextPoint( ptP) ;
}
// se richiesti caps e sezione chiusa e guida aperta
if ( bCapEnds && bSectClosed && ! bGuideClosed) {
// calcolo la polilinea che approssima la sezione
PolyLine PLS ;
if ( ! pSect->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PLS))
return nullptr ;
// verifico che la sezione sia chiusa e piatta
Plane3d plSect ; double dArea ;
if ( PLS.IsClosedAndFlat( plSect, dArea, 100 * EPS_SMALL)) {
// aggiungo il cap sull'inizio
PtrOwner<SurfTriMesh> pSci( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSci) || ! pSci->CreateByFlatContour( PLS))
return nullptr ;
pSci->Invert() ;
Point3d ptGi ; PLG.GetFirstPoint( ptGi) ;
pSci->Translate( ptGi - ptStart + vtDelta) ;
pSTM->DoSewing( *pSci) ;
// aggiungo il cap sulla fine
PtrOwner<SurfTriMesh> pSce( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSce) || ! pSce->CreateByFlatContour( PLS))
return nullptr ;
Point3d ptGe ; PLG.GetLastPoint( ptGe) ;
pSce->Translate( ptGe - ptStart + vtDelta) ;
pSTM->DoSewing( *pSce) ;
}
}
// se superficie risultante chiusa, verifico che la normale sia verso l'esterno
double dVol ;
if ( pSTM->GetVolume( dVol) && dVol < 0)
pSTM->Invert() ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshRuled( const Point3d& ptP, const ICurve* pCurve, double dLinTol)
{
// verifica parametri
if ( &ptP == nullptr || pCurve == nullptr)
return nullptr ;
// calcolo la polilinea che approssima la curva
PolyLine PL ;
if ( ! pCurve->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL))
return nullptr ;
// creo e setto la superficie trimesh
PtrOwner<SurfTriMesh> pSTM( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSTM) || ! pSTM->CreateByPointCurve( ptP, PL))
return nullptr ;
// salvo tolleranza lineare usata
pSTM->SetLinearTolerance( dLinTol) ;
// restituisco la superficie
return Release( pSTM) ;
}
//-------------------------------------------------------------------------------
ISurfTriMesh*
GetSurfTriMeshRuled( const ICurve* pCurve1, const ICurve* pCurve2, int nType, double dLinTol)
{
// verifica parametri
if ( pCurve1 == nullptr || pCurve2 == nullptr)
return nullptr ;
// calcolo la polilinea che approssima la prima curva
PolyLine PL1 ;
if ( ! pCurve1->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL1))
return nullptr ;
// calcolo la polilinea che approssima la seconda curva
PolyLine PL2 ;
if ( ! pCurve2->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2))
return nullptr ;
// creo e setto la superficie trimesh
PtrOwner<SurfTriMesh> pSTM( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSTM) || ! pSTM->CreateByTwoCurves( PL1, PL2, nType))
return nullptr ;
// salvo tolleranza lineare usata
pSTM->SetLinearTolerance( dLinTol) ;
// restituisco la superficie
return Release( pSTM) ;
}