f48bf06f64
- 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.
2138 lines
84 KiB
C++
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) ;
|
|
}
|