Files
EgtGeomKernel/StmFromCurves.cpp
T
SaraP 119bbe0bcb EgtGeomKernel :
- prime migliorie nella creazione di solidi swept con sezione rettangolare e bevel ( prima versione, da sistemare caps flat e none)
- correzione in Voronoi.
2026-05-28 15:30:41 +02:00

2038 lines
81 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 "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 STM_RECTSWEPT_FLATCAP_START = -1 ;
static const int STM_RECTSWEPT_FLATCAP_END = -2 ;
static const int STM_RECTSWEPT_LINK = -3 ;
static const int STM_RECTSWEPT_FLATCAP_EXTRA = -4 ;
//-------------------------------------------------------------------------------
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) {
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 ISurfFlatRegion*
CalcSweptSurface( ICurveComposite* pGuide, const Vector3d& vtNorm, double dOffs)
{
PtrOwner<ISurfFlatRegion> pSrf( CreateSurfFlatRegion()) ;
if ( IsNull( pSrf))
return nullptr ;
// per ogni sottotratto della guida calcolo la regione spazzata dalla sezione su quel tratto
for ( int i = 0 ; i < pGuide->GetCurveCount() ; i ++) {
PtrOwner<ICurve> pCrv( pGuide->GetCurve( i)->Clone()) ;
pCrv->SetExtrusion( vtNorm) ;
if ( pCrv->SimpleOffset( - dOffs)) {
PtrOwner<ICurveComposite> pBorder( CreateCurveComposite()) ;
if ( IsNull( pBorder))
return nullptr ;
pBorder->AddCurve( pGuide->GetCurve( i)->Clone()) ;
pCrv->Invert() ;
Point3d ptS ; pCrv->GetStartPoint( ptS) ;
pBorder->AddLine( ptS) ;
pBorder->AddCurve( Release( pCrv)) ;
pBorder->Close() ;
if ( ! pSrf->IsValid()) {
pSrf->AddExtLoop( Release( pBorder)) ;
}
else {
PtrOwner<ISurfFlatRegion> pSrfTmp( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfTmp))
return nullptr ;
pSrfTmp->AddExtLoop( Release( pBorder)) ;
pSrf->Add( *pSrfTmp) ;
}
}
else {
// se arco che collassa devo costruire separatamente i due spicchi
ICurveArc* pArc = GetCurveArc( pCrv) ;
if ( pArc == nullptr)
return nullptr ;
Point3d ptC = pArc->GetCenter() ;
Point3d ptS ; pArc->GetStartPoint( ptS) ;
Vector3d vtS = ptC - ptS ;
vtS.Normalize() ;
Point3d ptS2 = ptS + vtS * dOffs ;
Point3d ptE ; pArc->GetEndPoint( ptE) ;
Vector3d vtE = ptC - ptE ;
vtE.Normalize() ;
Point3d ptE2 = ptE + vtE * dOffs ;
PtrOwner<ICurveArc> pArc2( CreateCurveArc()) ;
if ( IsNull( pArc2) || ! pArc2->SetC2PN( ptC, ptS2, ptE2, vtNorm))
return nullptr ;
PtrOwner<ICurveComposite> pBorder1( CreateCurveComposite()) ;
if ( IsNull( pBorder1))
return nullptr ;
pBorder1->AddPoint( ptC) ;
pBorder1->AddLine( ptS) ;
pBorder1->AddCurve( Release( pCrv)) ;
pBorder1->Close() ;
PtrOwner<ICurveComposite> pBorder2( CreateCurveComposite()) ;
if ( IsNull( pBorder2))
return nullptr ;
pBorder2->AddPoint( ptC) ;
pBorder2->AddLine( ptS2) ;
pBorder2->AddCurve( Release( pArc2)) ;
pBorder2->Close() ;
PtrOwner<ISurfFlatRegion> pSrfTmp2( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfTmp2))
return nullptr ;
pSrfTmp2->AddExtLoop( Release( pBorder2)) ;
if ( ! pSrf->IsValid()) {
pSrf->AddExtLoop( Release( pBorder1)) ;
pSrf->Add( *pSrfTmp2) ;
}
else {
PtrOwner<ISurfFlatRegion> pSrfTmp1( CreateSurfFlatRegion()) ;
if ( IsNull( pSrfTmp1))
return nullptr ;
pSrfTmp1->AddExtLoop( Release( pBorder1)) ;
pSrf->Add( *pSrfTmp1) ;
pSrf->Add( *pSrfTmp2) ;
}
}
}
return Release( pSrf) ;
}
//-------------------------------------------------------------------------------
static bool
AdjustSurfTriMeshRectSweptCaps( int nCapType, ICURVEPOVECTOR& vCrvs, const Vector3d& vtNorm, const ICurve* pOrigGuide, double dOffs)
{
// se RSCAP_FLAT trasformo tutte le giunzioni relative agli estremi in linee controllando se necessario chiudere le curve
// con opportuni tratti di offset.
// 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 le linee di junction sugli estremi della giuda ( j = 0 start, j = 1 end)
for ( int j = 0 ; j < 2 ; j ++) {
Point3d ptP ;
Vector3d vtDir ;
if ( j == 0) {
pOrigGuide->GetStartPoint( ptP) ;
pOrigGuide->GetStartDir( vtDir) ;
}
else {
pOrigGuide->GetEndPoint( ptP) ;
pOrigGuide->GetEndDir( vtDir) ;
}
vtDir.Rotate( vtNorm, ANG_RIGHT) ;
Point3d pt1 = ptP + dOffs * vtDir ;
Point3d pt2 = ptP - dOffs * vtDir ;
// cerco su quali curve poggia la linea di junction
int nCrv1 = -1 ; bool bStart1 = true ;
int nCrv2 = -1 ; bool bStart2 = true ;
for ( int i = 0 ; i < ssize( vCrvs) ; i ++) {
Point3d ptS ; vCrvs[i]->GetStartPoint( ptS) ;
Point3d ptE ; vCrvs[i]->GetEndPoint( ptE) ;
if ( AreSamePointEpsilon( ptS, pt1, 10 * EPS_SMALL)) {
nCrv1 = i ;
bStart1 = true ;
}
else if ( AreSamePointEpsilon( ptS, pt2, 10 * EPS_SMALL)) {
nCrv2 = i ;
bStart2 = true ;
}
if ( AreSamePointEpsilon( ptE, pt1, 10 * EPS_SMALL)) {
nCrv1 = i ;
bStart1 = false ;
}
else if ( AreSamePointEpsilon( ptE, pt2, 10 * EPS_SMALL)) {
nCrv2 = i ;
bStart2 = false ;
}
}
int nFlatCapProp = ( j == 0 ? STM_RECTSWEPT_FLATCAP_START : STM_RECTSWEPT_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
ICurveComposite* pCompo = GetCurveComposite( 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) ;
ICurveComposite* pCompo = GetCurveComposite( 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() ;
}
}
// se non congiunge due curve aggiungo il tratto lineare libero sull'estremo
else {
if ( nCrv1 != -1) {
ICurveComposite* pCompo = GetCurveComposite( vCrvs[nCrv1]) ;
if ( pCompo == nullptr)
return false ;
pCompo->AddLine( pt2, ! bStart1) ;
int nSubCrv = ( bStart1 ? 0 : pCompo->GetCurveCount() - 1) ;
pCompo->SetCurveTempProp( nSubCrv, nFlatCapProp) ;
}
else if ( nCrv2 != -1) {
ICurveComposite* pCompo = GetCurveComposite( vCrvs[nCrv2]) ;
if ( pCompo == nullptr)
return false ;
pCompo->AddLine( pt1, ! bStart2) ;
int nSubCrv = ( bStart2 ? 0 : pCompo->GetCurveCount() - 1) ;
pCompo->SetCurveTempProp( nSubCrv, nFlatCapProp) ;
}
}
}
// controllo chiusura di tutte le curve
for ( int i = 0 ; i < ssize( vCrvs) ; i++) {
if ( ! vCrvs[i]->IsClosed()) {
ICurveComposite* pCompo = GetCurveComposite( vCrvs[i]) ;
if ( pCompo == nullptr)
return false ;
double dTempProp1 = pCompo->GetFirstCurve()->GetTempProp() ;
double dTempProp2 = pCompo->GetLastCurve()->GetTempProp() ;
// verifico se necessaria inversione della guida per renderla coerente con la fat curve
bool bInvert = false ;
if ( dTempProp1 < 0 || dTempProp2 < 0) {
bInvert = ( dTempProp1 == STM_RECTSWEPT_FLATCAP_END || dTempProp2 == STM_RECTSWEPT_FLATCAP_START) ;
}
else {
Point3d ptS ; pCompo->GetStartPoint( ptS) ;
DistPointCurve distPC( ptS, *pOrigGuide) ;
int nSide = 0 ;
distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ;
bInvert = ( nSide == MDS_RIGHT) ;
}
// ricavo la superficie spazzata dalla guida
PtrOwner<ICurveComposite> pGuide( ConvertCurveToComposite( pOrigGuide->Clone())) ;
if ( bInvert)
pGuide->Invert() ;
PtrOwner<ISurfFlatRegion> pSurf( CalcSweptSurface( pGuide, vtNorm, dOffs)) ;
// verifico come trimmare
bool bTrimStart = false ;
bool bTrimEnd = false ;
if ( dTempProp1 < 0 && dTempProp2 >= 0)
bTrimStart = true ;
else if ( dTempProp1 >= 0 && dTempProp2 < 0)
bTrimEnd = true ;
else if ( dTempProp1 < 0 && dTempProp2 < 0) {
bTrimStart = true ;
bTrimEnd = true ;
}
// recupero il loop opportuno
PtrOwner<ICurveComposite> pBorder( ConvertCurveToComposite( pSurf->GetLoop( 0, 0))) ;
if ( pSurf->GetChunkCount() > 1) {
Point3d ptRef ; pCompo->GetStartPoint( ptRef) ;
if ( bTrimStart)
pCompo->GetEndPoint( ptRef) ;
for ( int j = 0 ; j < pSurf->GetChunkCount() ; j ++) {
pBorder.Set( ConvertCurveToComposite( pSurf->GetLoop( j, 0))) ;
if ( pBorder->IsPointOn( ptRef))
break ;
}
}
double dPar1, dPar2 ;
if ( ! bTrimStart && ! bTrimEnd) {
Point3d ptS ; pCompo->GetStartPoint( ptS) ;
Point3d ptE ; pCompo->GetEndPoint( ptE) ;
pBorder->GetParamAtPoint( ptE, dPar1) ;
pBorder->GetParamAtPoint( ptS, dPar2) ;
}
else if ( bTrimStart && bTrimEnd) {
const CurveLine* pLineS = GetBasicCurveLine( pCompo->GetFirstCurve()) ;
const CurveLine* pLineE = GetBasicCurveLine( pCompo->GetLastCurve()) ;
if ( pLineS == nullptr || pLineE == nullptr)
return false ;
IntersLineLine intLL( *pLineS, *pLineE) ;
if ( intLL.GetNumInters() > 0) {
IntCrvCrvInfo aInfo ;
intLL.GetIntCrvCrvInfo( aInfo) ;
pCompo->TrimStartEndAtParam( aInfo.IciA[0].dU, pCompo->GetCurveCount() - 1 + aInfo.IciB[0].dU) ;
continue ;
}
else {
// da fare in frame locale
IntersCurveCurve intCC( *pLineS, *pBorder) ;
IntCrvCrvInfo aInfo ;
intCC.GetIntCrvCrvInfo( intCC.GetIntersCount() - 1, aInfo) ;
dPar2 = aInfo.IciB[0].dU ;
pCompo->TrimStartAtParam( aInfo.IciA[0].dU) ;
IntersCurveCurve intCC2( *pLineE, *pBorder) ;
intCC2.GetIntCrvCrvInfo( 0, aInfo) ;
dPar1 = aInfo.IciB[0].dU ;
pCompo->TrimEndAtParam( pCompo->GetCurveCount() - 1 + aInfo.IciA[0].dU) ;
}
}
else if ( bTrimStart) {
Point3d ptE ; pCompo->GetEndPoint( ptE) ;
pBorder->GetParamAtPoint( ptE, dPar1) ;
// intersezione con lo start
const ICurve* pLine = pCompo->GetFirstCurve() ;
IntersCurveCurve intCC( *pLine, *pBorder) ;
IntCrvCrvInfo aInfo ;
intCC.GetIntCrvCrvInfo( intCC.GetIntersCount() - 1, aInfo) ;
dPar2 = aInfo.IciB[0].dU ;
pCompo->TrimStartAtParam( aInfo.IciA[0].dU) ;
}
else {
Point3d ptS ; pCompo->GetStartPoint( ptS) ;
pBorder->GetParamAtPoint( ptS, dPar2) ;
// intersezione con end
const ICurve* pLine = pCompo->GetLastCurve() ;
IntersCurveCurve intCC( *pLine, *pBorder) ;
IntCrvCrvInfo aInfo ;
intCC.GetIntCrvCrvInfo( 0, aInfo) ;
dPar1 = aInfo.IciB[0].dU ;
pCompo->TrimEndAtParam( pCompo->GetCurveCount() - 1 + aInfo.IciA[0].dU) ;
}
pBorder->TrimStartEndAtParam( dPar1, dPar2) ;
// setto temp param per identificarli
for ( int j = 0 ; j < pBorder->GetCurveCount() ; j++) {
pBorder->SetCurveTempProp( j, STM_RECTSWEPT_FLATCAP_EXTRA) ;
}
pCompo->AddCurve( Release( pBorder)) ;
}
}
}
// 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 dChainTol = 50 * EPS_SMALL ;
double dOffsMin = 0.5 * dRadius - dBevelH ;
double dOffsMax = 0.5 * dRadius ;
// calcolo il medial axis e conservo solo le porzioni interne alla regione tra i due offset
ICURVEPOVECTOR vMedialAxis ;
CalcCurveMedialAxis( *pGuide, vMedialAxis, 0) ;
ICRVCOMPOPOVECTOR vMedialCrvs ;
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_SMALL || dPar2 < dOffsMin + EPS_SMALL)
continue ;
if ( dOffsMin - EPS_SMALL < dPar1 && dPar2 < dOffsMax + EPS_SMALL) {
// tratto va conservato completamente
vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ;
}
else {
// determino la parte compresa tra gli offset massimo e minimo
if ( vMedialAxis[i]->GetType() == CRV_LINE) {
double dTrimParS = ( dOffsMin - dPar1) / ( dPar2 - dPar1) ;
double dTrimParE = ( dOffsMax - dPar1) / ( dPar2 - dPar1) ;
dTrimParS = clamp( dTrimParS, 0.0, 1.0) ;
dTrimParE = clamp( dTrimParE, 0.0, 1.0) ;
vMedialAxis[i]->TrimStartEndAtParam( dTrimParS, dTrimParE) ;
if ( dTrimParS > EPS_SMALL)
vMedialAxis[i]->SetTempParam( dOffsMin, 0) ;
if ( dTrimParE < 1 - EPS_SMALL)
vMedialAxis[i]->SetTempParam( dOffsMax, 1) ;
vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ;
}
else {
ICurveComposite* pCompo = GetCurveComposite( vMedialAxis[i]) ;
if ( pCompo == nullptr)
return nullptr ;
double dS, dE ; pCompo->GetDomain( dS, dE) ;
double dTrimParS = dS ;
double dTrimParE = dE ;
for ( int j = 0 ; j < pCompo->GetCurveCount() ; j ++) {
double dPar1 = pCompo->GetCurve( j)->GetTempParam( 0) ;
double dPar2 = pCompo->GetCurve( j)->GetTempParam( 1) ;
if ( dPar1 - EPS_SMALL < dOffsMin && dOffsMin < dPar2 + EPS_SMALL)
dTrimParS = j + ( dOffsMin - dPar1) / ( dPar2 - dPar1) ;
if ( dPar1 - EPS_SMALL < dOffsMax && dOffsMax < dPar2 + EPS_SMALL) {
dTrimParE = j + ( dOffsMax - dPar1) / ( dPar2 - dPar1) ;
break ;
}
}
dTrimParS = clamp( dTrimParS, 0.0, dE) ;
dTrimParE = clamp( dTrimParE, 0.0, dE) ;
pCompo->TrimStartEndAtParam( dTrimParS, dTrimParE) ;
if ( dTrimParS > EPS_SMALL)
pCompo->SetCurveTempParam( 0, dOffsMin, 0) ;
if ( dTrimParE < dE - EPS_SMALL)
pCompo->SetCurveTempParam( pCompo->GetCurveCount() - 1, 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()->GetLastCurve()->GetTempParam( 1) ;
double dDelta = - dBevelV / dBevelH * ( dPar - dOffsMin) ;
vMedialCrvs.back()->ModifyEnd( ptE + vtNorm * dDelta) ;
}
}
// chain dei medialaxis
GetChainedCurves( vMedialCrvs, 5 * EPS_SMALL, true) ;
// se cap flat aggiungo tratti sugli estremi
if ( nCapType == RSCAP_FLAT) {
Frame3d frLoc ; frLoc.Set( ORIG, vtNorm) ;
for ( int i = 0 ; i < 2 ; i ++) {
// recupero il tratto start/end
int nRefProp = ( i == 0 ? STM_RECTSWEPT_FLATCAP_START : STM_RECTSWEPT_FLATCAP_END) ;
bool bFound = false ;
for ( int j = 0 ; j < ssize( vCrvs) && ! bFound ; j ++) {
ICurveComposite* pCompo = GetCurveComposite( vCrvs[j]) ;
if ( pCompo == nullptr)
return nullptr ;
for ( int k = 0 ; k < pCompo->GetCurveCount() ; k++) {
int nProp = pCompo->GetCurve( k)->GetTempProp() ;
if ( nProp == nRefProp) {
double dLen ; pCompo->GetCurve( k)->GetLength( dLen) ;
if ( abs( dLen - 2 * dOffsMax) > EPS_SMALL) {
Point3d ptP ; pCompo->GetCurve( k)->GetStartPoint( ptP) ;
Vector3d vtDir ; pCompo->GetCurve( k)->GetStartDir( vtDir) ;
if ( i == 0)
vtDir.Invert() ;
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ;
if ( IsNull( pLine))
return nullptr ;
pLine->SetPVL( ptP, vtDir, 2 * dOffsMax - dLen) ;
pLine->ToLoc( frLoc) ;
// verifico se interseca una curva dei medialaxis
for ( int l = 0 ; l < ssize( vMedialCrvs) ; l++) {
vMedialCrvs[l]->ToLoc( frLoc) ;
IntersCurveCurve intCC( *pLine, *vMedialCrvs[l]) ;
vMedialCrvs[l]->ToGlob( frLoc) ;
if ( intCC.GetIntersCount() == 1) {
IntCrvCrvInfo aInfo ;
intCC.GetIntCrvCrvInfo( 0, aInfo) ;
double dTempParam1 = vMedialCrvs[l]->GetFirstCurve()->GetTempParam() ;
double dTempParam2 = vMedialCrvs[l]->GetLastCurve()->GetTempParam() ;
if ( dTempParam1 < dTempParam2) {
vMedialCrvs[l]->TrimStartAtParam( aInfo.IciB[0].dU) ;
vMedialCrvs[l]->AddLine( ptP, false) ;
vMedialCrvs[l]->SetCurveTempParam( 0, dOffsMax, 0) ;
}
else {
vMedialCrvs[l]->TrimEndAtParam( aInfo.IciB[0].dU) ;
vMedialCrvs[l]->AddLine( ptP) ;
vMedialCrvs[l]->SetCurveTempParam( vMedialCrvs[l]->GetCurveCount() - 1, dOffsMax, 1) ;
}
break ;
}
}
}
bFound = true ;
break ;
}
}
}
}
}
// creo catena
ICurveComposite* pResult = nullptr ;
BOOLVECTOR vUsed1( vIdx.size(), false) ;
for ( int i = 0 ; i < ssize( vMedialCrvs) ; i++) {
// verifico se è una curva sensata per il collegamento ( ovvero se i parametri ai suoi estremi sono il valore di max offset)
bool bValid = false ;
if ( vMedialCrvs[i]->GetCurveCount() == 1) {
double dPar1 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam() ;
double dPar2 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam( 1) ;
bValid = ( abs( dPar1 - dOffsMax) < EPS_SMALL && abs( dPar2 - dOffsMax) < EPS_SMALL) ;
}
else {
// controllo sia parametro start ed end perchè il chain permette inversione
double dParS1 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam() ;
double dParS2 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam( 1) ;
double dParE1 = vMedialCrvs[i]->GetLastCurve()->GetTempParam( ) ;
double dParE2 = vMedialCrvs[i]->GetLastCurve()->GetTempParam( 1) ;
bValid = ( ( abs( dParS1 - dOffsMax) < EPS_SMALL || abs( dParS2 - dOffsMax) < EPS_SMALL) &&
( abs( dParE1 - dOffsMax) < EPS_SMALL || abs( dParE2 - dOffsMax) < EPS_SMALL)) ;
}
if ( ! bValid)
continue ;
// setto tutte le temp prop ad un valore fissato per identificarla in seguito
for ( int nC = 0 ; nC < vMedialCrvs[i]->GetCurveCount() ; nC ++)
vMedialCrvs[i]->SetCurveTempProp( nC, STM_RECTSWEPT_LINK) ;
// trovo le curve che collega
Point3d ptS ; vMedialCrvs[i]->GetStartPoint( ptS) ;
Point3d ptE ; 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 ;
}
if ( nS == -1 || nE == -1)
return nullptr ;
// i parametri devono essere su un joint per costruzione
dParS = round( dParS) ;
dParE = round( dParE) ;
pResult = GetCurveComposite( 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) ;
GetBasicCurveComposite( vCrvs[nE])->ChangeStartPoint( dParE) ;
pResult->AddCurve( Release( vCrvs[nE]), true, dChainTol) ;
vMedialCrvs[i]->Invert() ;
pResult->AddCurve( Release( vMedialCrvs[i]), true, dChainTol) ;
if ( pResultEnd != nullptr)
pResult->AddCurve( pResultEnd, true, dChainTol) ;
// forzo chiusura
pResult->Close() ;
}
return pResult ;
}
//-------------------------------------------------------------------------------
static bool
GetTrimParams( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, int nOrigCrv, int nCurrCrv2, const Vector3d& vtNorm,
DBLVECTOR& vPar1, DBLVECTOR& vPar2)
{
int nFlag ;
Point3d ptP ; pCrv2->GetCurve( nCurrCrv2)->GetStartPoint( ptP) ;
int nStart = int( floor( vPar1.back() + 0.5)) ;
// se deriva da una curva ben definita della giuda cerco la corrispondente dal lato corretto sull'altra curva
if ( nOrigCrv != 0) {
for ( int j = nStart ; j < pCrv1->GetCurveCount() ; j ++) {
int nOrigCrv1 = pCrv1->GetCurve( j)->GetTempProp() ;
if ( nOrigCrv1 == nOrigCrv) {
// se estremo di flat cap spezzo il bisettore appena trovato nel punto a minima distanza
// e sulla curva 1 avrò corrispondenza con un punto
if ( nOrigCrv == STM_RECTSWEPT_FLATCAP_START || nOrigCrv == STM_RECTSWEPT_FLATCAP_END) {
Point3d ptRef ; pCrv1->GetCurve( j)->GetStartPoint( ptRef) ;
PtrOwner<ICurve> pCurrBisector( pCrv2->CopyParamRange( vPar2.back(), nCurrCrv2)) ;
DistPointCurve distPC( ptRef, *pCurrBisector) ;
double dParTrim ; distPC.GetParamAtMinDistPoint( 0, dParTrim, nFlag) ;
vPar1.emplace_back( j) ;
vPar2.emplace_back( vPar2.back() + dParTrim) ;
vPar1.emplace_back( j) ;
vPar2.emplace_back( nCurrCrv2) ;
}
else {
DistPointCurve distPC( ptP, *pCrv1->GetCurve( j)) ;
int nSide = 0 ;
double dParMinDist ;
distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ;
if ( nSide == MDS_RIGHT) {
distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ;
dParMinDist += j ;
vPar1.emplace_back( dParMinDist) ;
vPar2.emplace_back( nCurrCrv2) ;
break ;
}
}
}
}
}
// se deriva da raccordo devo controllare il più vicino
else {
double dMinDist = INFINITO ;
double dParMinDist ;
for ( int j = nStart ; j < pCrv1->GetCurveCount() ; j ++) {
int nOrigCrv1 = pCrv1->GetCurve( j)->GetTempProp() ;
if ( nOrigCrv1 == nOrigCrv) {
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) {
distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ;
dParMinDist += j ;
dMinDist = dCurrDist ;
}
}
}
}
vPar1.emplace_back( dParMinDist) ;
vPar2.emplace_back( nCurrCrv2) ;
}
return true ;
}
//-------------------------------------------------------------------------------
static bool
Associate( const ICurveComposite* pCrv1, ICurveComposite* pCrv2, const Vector3d& vtNorm, DBLVECTOR& vPar1, DBLVECTOR& vPar2)
{
vPar1.emplace_back( 0) ;
vPar2.emplace_back( 0) ;
// assegno il punto di inizio della curva 2 il più vicino possibile a quello della curva 1 preferendo curve non problematiche
int nStartCrvId = pCrv1->GetFirstCurve()->GetTempProp() ;
if ( nStartCrvId == STM_RECTSWEPT_FLATCAP_START) {
for ( int i = 0 ; i < pCrv2->GetCurveCount() ; i ++) {
int nOrigCrv = pCrv2->GetCurve( i)->GetTempProp() ;
if ( nOrigCrv == nStartCrvId) {
pCrv2->ChangeStartPoint( i) ;
break ;
}
}
}
else {
Point3d ptStart ; pCrv1->GetStartPoint( ptStart) ;
double dPar ; int nFlag ;
DistPointCurve( ptStart, *pCrv2).GetParamAtMinDistPoint(0, dPar, nFlag) ;
pCrv2->ChangeStartPoint( dPar) ;
}
// segnalo parametro di split per le curve quando trovo l'inizio o la fine di un tratto che deriva da un bisettore
// o da un tratto aggiunto per la chiusura dei flat caps
bool bBisector = false ;
for ( int i = 0 ; i < pCrv2->GetCurveCount() ; i++) {
int nTmpProp = pCrv2->GetCurve( i)->GetTempProp() ;
if ( nTmpProp == STM_RECTSWEPT_LINK || nTmpProp == STM_RECTSWEPT_FLATCAP_EXTRA) {
if ( ! bBisector) {
// inizio bisettore
bBisector = true ;
if ( i > 0) {
int nOrigCrv = pCrv2->GetCurve( i-1)->GetTempProp() ;
GetTrimParams( pCrv1, pCrv2, nOrigCrv, i, vtNorm, vPar1, vPar2) ;
}
}
}
else {
if ( bBisector) {
// fine del bisettore
bBisector = false ;
int nOrigCrv = pCrv2->GetCurve(i)->GetTempProp() ;
GetTrimParams( pCrv1, pCrv2, nOrigCrv, i, vtNorm, vPar1, vPar2) ;
}
}
}
// aggiungo parametro finale
double dTmp, dE1, dE2 ;
pCrv1->GetDomain( dTmp, dE1) ;
pCrv2->GetDomain( dTmp, dE2) ;
vPar1.emplace_back( dE1) ;
vPar2.emplace_back( dE2) ;
return true ;
}
//-------------------------------------------------------------------------------
static POLYLINEVECTOR
TrimPolyLine( 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 = 0 ; 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 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 ;
}
}
// sposto il punto di start del loop esterno della FatCrv1 in modo sensato
ICurveComposite* pCompo = GetCurveComposite( vFatCrvs1[0]) ;
if ( pCompo == nullptr)
return nullptr ;
for ( int j = 0 ; j < pCompo->GetCurveCount() ; j ++) {
int nOrigCrv = pCompo->GetCurve( j)->GetTempProp() ;
if ( nOrigCrv > 0) {
Point3d ptP ; pCompo->GetCurve( j)->GetStartPoint( ptP) ;
DistPointCurve distPC( ptP, *vFatCrvs2[0]) ;
double dDist = 0 ;
distPC.GetDist( dDist) ;
if ( abs( dDist - dBevelH) < 100 * EPS_SMALL) {
pCompo->ChangeStartPoint( j + 0.5) ;
break ;
}
}
}
for ( int i = 0 ; i < ssize( vFatCrvs2) ; i++)
vFatCrvs2[i]->Translate( - dBevelV * vtNorm) ;
// trasformo in polylines
POLYLINEVECTOR vPL1( vFatCrvs1.size()) ;
for ( int i = 0 ; i < ssize( vFatCrvs1) ; i++)
vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ;
// costruisco le parti di superficie
PtrOwner<ISurfTriMesh> pSrfTop( CreateSurfTriMesh()) ;
if ( IsNull( pSrfTop) || ! pSrfTop->CreateByPolygonWithHoles( vPL1))
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) ;
BOOLVECTOR bUsed( vFatCrvs2.size(), false) ;
bool bRepair = 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]) {
double dS, dE ; vFatCrvs2[j]->GetDomain( dS, dE) ;
double dU = 0.5 ;
int nSide = MDS_ON ;
while ( nSide == MDS_ON && dU < dE) {
Point3d ptTest ; vFatCrvs2[j]->GetPointD1D2( dU, ICurve::FROM_MINUS, ptTest) ;
DistPointCurve distPC( ptTest, *vFatCrvs1[i]) ;
distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ;
if ( nSide == MDS_RIGHT) {
bUsed[j] = true ;
vAssociatedIdx.emplace_back( j) ;
}
dU += 1 ;
}
}
}
ISURFTMPOVECTOR vSurfLatTop ;
ISURFTMPOVECTOR vSurfLat ;
// a) se non ha associate allora collassa in un punto/curva ad una quota opportuna
if ( vAssociatedIdx.empty()) {
double dLimitOffs ;
if ( ! CalcCurveLimitOffset( *vFatCrvs1[i], dLimitOffs))
return nullptr ;
OffsetCurve OffsCrv ;
OffsCrv.Make( vFatCrvs1[i], dLimitOffs, ICurve::OFF_FILLET) ;
PtrOwner<SurfTriMesh> pSrfLatT( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfLatT))
return nullptr ;
Point3d ptOffs ; Vector3d vtOut ;
if ( OffsCrv.GetPointOffset( ptOffs, vtOut)) {
// se collassa in un punto
ptOffs.Translate( - dBevelV / dBevelH * dLimitOffs * vtNorm) ;
pSrfLatT->CreateByPointCurve( ptOffs, vPL1[i]) ;
pSrfLatT->Invert() ;
}
else {
// se collassa in una curva
PtrOwner<ICurveComposite> pAssociatedCrv( ConvertCurveToComposite( OffsCrv.GetLongerCurve())) ;
if ( IsNull( pAssociatedCrv))
return nullptr ;
pAssociatedCrv->Translate( - dBevelV / dBevelH * dLimitOffs * vtNorm) ;
Point3d ptStart ; vFatCrvs1[i]->GetStartPoint( ptStart) ;
double dPar ; int nFlag ;
DistPointCurve( ptStart, *pAssociatedCrv).GetParamAtMinDistPoint(0, dPar, nFlag) ;
pAssociatedCrv->ChangeStartPoint( dPar) ;
PolyLine PL2 ;
pAssociatedCrv->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ;
pSrfLatT->CreateByTwoCurves( PL2, vPL1[i], ISurfTriMesh::RLT_MINDIST) ;
}
vSurfLatTop.emplace_back( Release( pSrfLatT)) ;
}
// b) se ha una sola associata costruisco la rigata tra le due curve
else if ( vAssociatedIdx.size() == 1) {
ICurveComposite* pAssociatedCrv = GetCurveComposite( vFatCrvs2[vAssociatedIdx[0]]) ;
if ( pAssociatedCrv == nullptr)
return nullptr ;
// per avere rigata ottimale modifico il punto di inizio della seconda curva
Point3d ptStart ; vFatCrvs1[i]->GetStartPoint( ptStart) ;
double dPar ; int nFlag ;
DistPointCurve( ptStart, *pAssociatedCrv).GetParamAtMinDistPoint( 0, dPar, nFlag) ;
pAssociatedCrv->ChangeStartPoint( dPar) ;
PolyLine PL2 ;
pAssociatedCrv->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ;
PtrOwner<SurfTriMesh> pSrfLatT( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfLatT) || ! pSrfLatT->CreateByTwoCurves( PL2, vPL1[i], ISurfTriMesh::RLT_MINDIST))
return nullptr ;
vSurfLatTop.emplace_back( Release( pSrfLatT)) ;
PtrOwner<SurfTriMesh> pSrfLat( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfLat) || ! pSrfLat->CreateByExtrusion( PL2, ( - dDimV + 2 * dBevelV) * vtNorm))
return nullptr ;
pSrfLat->Invert() ;
vSurfLat.emplace_back( Release( pSrfLat)) ;
}
else {
bRepair = true ;
// superfici verticali
for ( int j = 0 ; j < ssize( vAssociatedIdx) ; j ++) {
PolyLine PL2 ;
vFatCrvs2[vAssociatedIdx[j]]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ;
PtrOwner<SurfTriMesh> pSrfLat( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfLat) || ! pSrfLat->CreateByExtrusion( PL2, ( - dDimV + 2 * dBevelV) * vtNorm))
return nullptr ;
pSrfLat->Invert() ;
vSurfLat.emplace_back( Release( pSrfLat)) ;
}
// collego le curve tramite bisettori
ICurveComposite* pFatCrv2 = Connect( pGuide, vFatCrvs2, vAssociatedIdx, dDimH, dBevelH, dBevelV, vtNorm, nCapType) ;
if ( pFatCrv2 == nullptr)
return nullptr ;
// individuo i punti ottimali dove spezzare le polylines per avere delle rigate ottimali
DBLVECTOR vPar1, vPar2 ;
Associate( GetCurveComposite( vFatCrvs1[i]), pFatCrv2, vtNorm, vPar1, vPar2) ;
PolyLine PL2 ;
pFatCrv2->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ;
POLYLINEVECTOR vPolyLine1 = TrimPolyLine( vPL1[i], vPar1) ;
POLYLINEVECTOR vPolyLine2 = TrimPolyLine( PL2, vPar2) ;
for ( int j = 0 ; j < ssize( vPolyLine1) ; j ++) {
if ( vPolyLine1[j].GetPointNbr() == 1 ) {
// collassa
Point3d pt1 ; vPolyLine1[j].GetFirstPoint( pt1) ;
Point3d pt2 ; vPolyLine2[j].GetFirstPoint( pt2) ;
Point3d pt3 ; vPolyLine2[j].GetLastPoint( pt3) ;
vPolyLine2[j].Close() ;
PtrOwner<SurfTriMesh> pSrfLatT( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfLatT ))
return nullptr ;
pSrfLatT->CreateByFlatContour( vPolyLine2[j]) ;
vSurfLatTop.emplace_back( Release( pSrfLatT)) ;
Triangle3d trExtra ;
trExtra.Set( pt1, pt2, pt3) ;
stmSoup.AddTriangle( trExtra) ;
trExtra.Mirror( ptCen, vtNorm) ;
stmSoup.AddTriangle( trExtra) ;
}
else {
PtrOwner<SurfTriMesh> pSrfLatT( CreateBasicSurfTriMesh()) ;
if ( IsNull( pSrfLatT))
return nullptr ;
pSrfLatT->CreateByTwoCurves( vPolyLine1[j], vPolyLine2[j], ISurfTriMesh::RLT_MINDIST) ;
pSrfLatT->Invert() ;
vSurfLatTop.emplace_back( Release( pSrfLatT)) ;
}
}
}
// inserisco le superfici laterali verticali
for ( int j = 0 ; j < ssize( vSurfLat) ; j ++)
stmSoup.AddSurfTriMesh( *vSurfLat[j]) ;
// inserisco le superfici laterali rigate e il loro mirror
for ( int j = 0 ; j < ssize( vSurfLatTop) ; j ++) {
stmSoup.AddSurfTriMesh( *vSurfLatTop[j]) ;
vSurfLatTop[j]->Mirror( ptCen, vtNorm) ;
stmSoup.AddSurfTriMesh( *vSurfLatTop[j]) ;
}
}
if ( ! stmSoup.End())
return nullptr ;
PtrOwner<ISurfTriMesh> pSTM ;
pSTM.Set( stmSoup.GetSurf()) ;
if ( IsNull( pSTM))
return nullptr ;
// se ho spezzato per la superficie laterale, rischio di aver creato Tjunctions
if ( bRepair)
pSTM->Repair() ;
// 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) ;
}