903f0c69bc
- aggiunto calcolo edge di superfici trimesh - piccole modifiche per usare direttamente oggetti anzichè le loro interfacce.
874 lines
28 KiB
C++
874 lines
28 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2015-2023
|
|
//----------------------------------------------------------------------------
|
|
// File : Voronoi.cpp Data : 23.11.23 Versione : 2.5k5
|
|
// Contenuto : Classe per Voronoi con libreria VRONI.
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 23.11.23 SP Creazione modulo.
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "GeoConst.h"
|
|
#include "CurveComposite.h"
|
|
#include "CurveArc.h"
|
|
#include "CurveLine.h"
|
|
#include "CurveBezier.h"
|
|
#include "SurfFlatRegion.h"
|
|
#include "OffsetAux.h"
|
|
#include "/EgtDev/Include/EGkVoronoi.h"
|
|
#include "/EgtDev/Include/EGkDistPointCurve.h"
|
|
|
|
using namespace std ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
Voronoi::Voronoi( const ICurve* pCrv, bool bAllowAdd)
|
|
: m_vroni( nullptr), m_nBound( VORONOI_STD_BOUND), m_bVDComputed( false), m_bAllowAdd( true)
|
|
{
|
|
// tento di aggiungere la curva
|
|
if ( ! AddCurve( pCrv))
|
|
Clear() ;
|
|
m_bAllowAdd = bAllowAdd ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Voronoi::Voronoi( const ISurfFlatRegion* pSfr, bool bAllowAdd)
|
|
: m_vroni( nullptr), m_nBound( VORONOI_STD_BOUND), m_bVDComputed( false), m_bAllowAdd( true)
|
|
{
|
|
// tento di aggiungere la superficie
|
|
if ( ! AddSurfFlatRegion( pSfr))
|
|
Clear() ;
|
|
m_bAllowAdd = bAllowAdd ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Voronoi::~Voronoi( void)
|
|
{
|
|
Clear() ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::Clear( void)
|
|
{
|
|
// pulizia oggetto vroni
|
|
if ( m_vroni != nullptr)
|
|
delete m_vroni ;
|
|
m_vroni = nullptr ;
|
|
|
|
// ciclo di pulizia delle curve associate
|
|
for ( auto pCrv : m_vpCrvs)
|
|
delete pCrv ;
|
|
m_vpCrvs.clear() ;
|
|
|
|
m_nBound = VORONOI_STD_BOUND ;
|
|
m_bVDComputed = false ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::AddCurve( const ICurve* pCrv)
|
|
{
|
|
if ( ! m_bAllowAdd)
|
|
return false ;
|
|
|
|
if ( pCrv == nullptr)
|
|
return false ;
|
|
|
|
// verifico se è una linea
|
|
int nType = pCrv->GetType() ;
|
|
bool bIsLine = ( nType == CRV_LINE) ;
|
|
if ( nType == CRV_COMPO) {
|
|
const CurveComposite* pCompo = GetBasicCurveComposite( pCrv) ;
|
|
Point3d ptStart, ptEnd ;
|
|
if ( pCompo != nullptr && pCompo->IsALine( 10 * EPS_SMALL, ptStart, ptEnd))
|
|
bIsLine = true ;
|
|
}
|
|
|
|
// verifico sia piana e trovo piano su cui giace
|
|
Plane3d plPlane ;
|
|
if ( bIsLine) {
|
|
// linea è sicuramente piana. Scelgo piano definito dall'estrusione ( se definita)
|
|
Point3d ptS ; pCrv->GetStartPoint( ptS) ;
|
|
Vector3d vtExtr ; pCrv->GetExtrusion( vtExtr) ;
|
|
if ( ! vtExtr.IsSmall())
|
|
plPlane.Set( ptS, vtExtr) ;
|
|
else
|
|
plPlane.Set( ptS, Z_AX) ;
|
|
}
|
|
else {
|
|
if ( ! pCrv->IsFlat( plPlane, false, 10 * EPS_SMALL))
|
|
return false ;
|
|
}
|
|
|
|
if ( m_vpCrvs.size() == 0) {
|
|
// se prima curva considerata assegno il frame al Voronoi
|
|
m_Frame.Set( plPlane.GetPoint(), plPlane.GetVersN()) ;
|
|
}
|
|
else {
|
|
// altrimenti verifico sia complanare ad eventuali curve già presenti
|
|
if ( ! AreSameOrOppositeVectorApprox( m_Frame.VersZ(), plPlane.GetVersN()) || ! PointInPlaneApprox( m_Frame.Orig(), plPlane))
|
|
return false ;
|
|
}
|
|
|
|
// verifico se oggetto vroni è stato inizializzato
|
|
if ( m_vroni == nullptr) {
|
|
m_vroni = new( nothrow) vroniObject() ;
|
|
m_vroni->apiInitializeProgram() ;
|
|
}
|
|
|
|
// creo una copia della curva e la porto in locale
|
|
PtrOwner<ICurve> pCrvLoc( pCrv->Clone()) ;
|
|
pCrvLoc->ToLoc( m_Frame) ;
|
|
|
|
// aggiungo la curva in locale all'oggetto vroni
|
|
if ( ! AddCurveToVroni( pCrvLoc))
|
|
return false ;
|
|
|
|
// aggiorno il box complessivo
|
|
BBox3d bBox ;
|
|
pCrvLoc->GetLocalBBox( bBox) ;
|
|
m_bBox.Add( bBox) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::AddSurfFlatRegion( const ISurfFlatRegion* pSfr)
|
|
{
|
|
if ( ! m_bAllowAdd)
|
|
return false ;
|
|
|
|
if ( pSfr == nullptr)
|
|
return false ;
|
|
|
|
// recupero il piano
|
|
Point3d ptCen ; pSfr->GetCentroid( ptCen) ;
|
|
Vector3d vtN = pSfr->GetNormVersor() ;
|
|
if ( m_vpCrvs.size() == 0) {
|
|
// assegno il frame al Voronoi
|
|
m_Frame.Set( ptCen, vtN) ;
|
|
}
|
|
else {
|
|
// verifico sia complanare ad eventuali curve già presenti
|
|
Plane3d plPlane ;
|
|
plPlane.Set( ptCen, pSfr->GetNormVersor()) ;
|
|
if ( ! AreSameOrOppositeVectorApprox( m_Frame.VersZ(), pSfr->GetNormVersor()) || ! PointInPlaneApprox( m_Frame.Orig(), plPlane))
|
|
return false ;
|
|
}
|
|
|
|
// verifico se oggetto vroni è stato inizializzato
|
|
if ( m_vroni == nullptr) {
|
|
m_vroni = new( nothrow) vroniObject() ;
|
|
m_vroni->apiInitializeProgram() ;
|
|
}
|
|
|
|
// aggiungo le curve di loop
|
|
for ( int i = 0 ; i < pSfr->GetChunkCount() ; i ++) {
|
|
for ( int j = 0 ; j < pSfr->GetLoopCount( i) ; j ++) {
|
|
PtrOwner<ICurve> pCrvLoc( pSfr->GetLoop( i, j)) ;
|
|
pCrvLoc->ToLoc( m_Frame) ;
|
|
if ( ! AddCurveToVroni( pCrvLoc))
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
// aggiorno il box complessivo
|
|
BBox3d bBox ;
|
|
Frame3d frSrf = m_Frame ;
|
|
frSrf.Invert() ;
|
|
pSfr->GetBBox( frSrf, bBox) ;
|
|
m_bBox.Add( bBox) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::AddCurveToVroni( const ICurve* pCrv)
|
|
{
|
|
int nLoopId = m_vpCrvs.size() ;
|
|
|
|
// aggiungo il punto iniziale
|
|
Point3d ptStart ;
|
|
if ( ! pCrv->GetStartPoint( ptStart))
|
|
return false ;
|
|
int nCrv = m_vroni->HandlePnt( ptStart.x, ptStart.y, {nLoopId, 0}) ;
|
|
|
|
// aggiungo la parte rimanente della curva
|
|
switch ( pCrv->GetType()) {
|
|
case CRV_LINE :
|
|
m_vpCrvs.emplace_back( pCrv->Clone()) ;
|
|
return AddLineToVroni( GetCurveLine( pCrv), nCrv, nLoopId) ;
|
|
case CRV_ARC :
|
|
m_vpCrvs.emplace_back( pCrv->Clone()) ;
|
|
return AddArcToVroni( GetCurveArc( pCrv), nCrv, nLoopId) ;
|
|
case CRV_COMPO :
|
|
return AddCompoToVroni( GetCurveComposite( pCrv), nCrv, nLoopId) ;
|
|
case CRV_BEZIER :
|
|
return AddBezierToVroni( GetCurveBezier( pCrv), nCrv, nLoopId) ;
|
|
default :
|
|
return false ;
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::AddLineToVroni( const ICurveLine* pLine, int& nVroniCrv, int nLoopId, int nCrvId, Point3d ptEnd)
|
|
{
|
|
if ( pLine == nullptr)
|
|
return false ;
|
|
if ( ! ptEnd.IsValid()) {
|
|
// recupero end point
|
|
if ( ! pLine->GetEndPoint( ptEnd))
|
|
return false ;
|
|
}
|
|
|
|
m_vroni->AddSeg( &nVroniCrv, ptEnd.x, ptEnd.y, {nLoopId, nCrvId}) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::AddArcToVroni( const ICurveArc* pArc, int& nVroniCrv, int nLoopId, int nCrvId, Point3d ptEnd)
|
|
{
|
|
if ( pArc == nullptr)
|
|
return false ;
|
|
|
|
Point3d ptCen = pArc->GetCenter() ;
|
|
double dAngCen = pArc->GetAngCenter() ;
|
|
int nArcSiteType = ( dAngCen > EPS_SMALL ? ARC : -ARC ) ;
|
|
Vector3d vtN = pArc->GetNormVersor() ;
|
|
if ( AreOppositeVectorApprox( vtN, Z_AX))
|
|
nArcSiteType *= -1 ;
|
|
|
|
if ( pArc->IsACircle()) {
|
|
// spezzo arco in due parti
|
|
Point3d ptM ;
|
|
if ( ! pArc->GetMidPoint( ptM))
|
|
return false ;
|
|
m_vroni->AddArc( &nVroniCrv, ptM.x, ptM.y, ptCen.x, ptCen.y, nArcSiteType, {nLoopId, nCrvId}) ;
|
|
// impogno che punto finale coincida con lo start ( per tolleranze vroni)
|
|
Point3d ptStart ; pArc->GetStartPoint( ptStart) ;
|
|
m_vroni->AddArc( &nVroniCrv, ptStart.x, ptStart.y, ptCen.x, ptCen.y, nArcSiteType, {nLoopId, nCrvId}) ;
|
|
}
|
|
else {
|
|
if ( ! ptEnd.IsValid()) {
|
|
// recupero end point dalla curva
|
|
if ( ! pArc->GetEndPoint( ptEnd))
|
|
return false ;
|
|
}
|
|
m_vroni->AddArc( &nVroniCrv, ptEnd.x, ptEnd.y, ptCen.x, ptCen.y, nArcSiteType, {nLoopId, nCrvId}) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::AddCompoToVroni( const ICurveComposite* pCompo, int& nVroniCrv, int nLoopId)
|
|
{
|
|
if ( pCompo == nullptr)
|
|
return false ;
|
|
|
|
PtrOwner<CurveComposite> pCopy( CloneBasicCurveComposite( pCompo)) ;
|
|
if ( IsNull( pCopy))
|
|
return false ;
|
|
|
|
// verifico che la curva sia fatta solo da rette e archi che giacciono nel piano XY ( estrusione deve essere Z+)
|
|
Vector3d vtExtr ; pCopy->GetExtrusion( vtExtr) ;
|
|
pCopy->SetExtrusion( Z_AX) ;
|
|
if ( ! pCopy->ArcsBezierCurvesToArcsPerpExtr( 10 * EPS_SMALL, ANG_TOL_STD_DEG))
|
|
return false ;
|
|
pCopy->SetExtrusion( vtExtr) ;
|
|
|
|
// aggiungo tutte le sottocurve
|
|
bool bClosed = pCopy->IsClosed() ;
|
|
for ( int i = 0 ; i < pCopy->GetCurveCount() ; i++) {
|
|
Point3d ptForcedEnd = P_INVALID ;
|
|
|
|
// se curva è chiusa, forzo l'end point a coincidere con lo start ( per le tolleranze di vroni)
|
|
if ( i == pCopy->GetCurveCount() - 1 && bClosed)
|
|
pCompo->GetStartPoint( ptForcedEnd) ;
|
|
|
|
// aggiungo
|
|
const ICurve* pCrv = pCopy->GetCurve( i) ;
|
|
int nType = pCrv->GetType() ;
|
|
if ( nType == CRV_LINE)
|
|
AddLineToVroni( GetCurveLine( pCrv), nVroniCrv, nLoopId, i, ptForcedEnd) ;
|
|
else if ( nType == CRV_ARC)
|
|
AddArcToVroni( GetCurveArc( pCrv), nVroniCrv, nLoopId, i, ptForcedEnd) ;
|
|
}
|
|
|
|
// aggiungo al vettore di curve
|
|
m_vpCrvs.emplace_back( Release( pCopy)) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::AddBezierToVroni( const ICurveBezier* pBezier, int& nVroniCrv, int nLoopId)
|
|
{
|
|
if ( pBezier == nullptr)
|
|
return false ;
|
|
|
|
// riconduco al caso di curva composita
|
|
PtrOwner<CurveComposite> pCompo( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCompo))
|
|
return false ;
|
|
PolyArc PA ;
|
|
if ( ! pBezier->ApproxWithArcsEx( LIN_TOL_STD, ANG_TOL_STD_DEG, LIN_FEA_LEN_STD, PA) || ! pCompo->FromPolyArc( PA))
|
|
return false ;
|
|
|
|
Vector3d vtExtr ; pBezier->GetExtrusion( vtExtr) ;
|
|
pCompo->SetExtrusion( vtExtr) ;
|
|
|
|
return AddCompoToVroni( pCompo, nVroniCrv, nLoopId) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ICurve*
|
|
Voronoi::GetCurve( int nId) const
|
|
{
|
|
// verifico validità indice
|
|
if ( nId < 0 || nId > ( int)m_vpCrvs.size() - 1)
|
|
return nullptr ;
|
|
// ne faccio una copia
|
|
ICurve* pCrv = m_vpCrvs[nId]->Clone() ;
|
|
if ( pCrv == nullptr)
|
|
return nullptr ;
|
|
// la porto nel riferimento globale
|
|
pCrv->ToGlob( m_Frame) ;
|
|
return pCrv ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::CalcVoronoi( int nBound)
|
|
{
|
|
// se già stato calcolato con lo stesso bound non devo fare nulla
|
|
if ( m_bVDComputed && nBound == m_nBound)
|
|
return true ;
|
|
|
|
// se già stato calcolato reset dei dati
|
|
if ( m_bVDComputed)
|
|
m_vroni->ResetVoronoiDiagram() ;
|
|
|
|
// calcolo
|
|
m_nBound = nBound ;
|
|
m_bVDComputed = true ;
|
|
string sTmp = "" ;
|
|
m_vroni->apiComputeVD( false, true, false, m_nBound, 0, 0, &sTmp[0], false, false, false, &sTmp[0], true) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ICurve*
|
|
Voronoi::GetBisectorCurve( int i)
|
|
{
|
|
if ( i >= m_vroni->GetNumberOfEdges())
|
|
return nullptr ;
|
|
|
|
// recupero estremi del bisettore
|
|
Point3d ptS, ptE ;
|
|
m_vroni->GetVDBisectorPoints( i, ptS.v, ptE.v) ;
|
|
if ( AreSamePointApprox( ptS, ptE))
|
|
return nullptr ;
|
|
|
|
// recupero parametri del bisettore
|
|
double dParS, dParE ;
|
|
m_vroni->GetVDBisectorParams( i, dParS, dParE) ;
|
|
|
|
// eventuale inversione
|
|
if ( dParS > dParE + EPS_SMALL) {
|
|
swap( dParS, dParE) ;
|
|
swap( ptS, ptE) ;
|
|
}
|
|
|
|
// costruisco la curva
|
|
int nType = m_vroni->GetVDBisectorType( i) ;
|
|
if ( nType == LINE) {
|
|
CurveLine* pLine = CreateBasicCurveLine() ;
|
|
if ( pLine == nullptr)
|
|
return nullptr ;
|
|
pLine->Set( ptS, ptE) ;
|
|
pLine->ToGlob( m_Frame) ;
|
|
// salvo le distanze nei suoi estremi come TempParam
|
|
pLine->SetTempParam( dParS, 0) ;
|
|
pLine->SetTempParam( dParE, 1) ;
|
|
return pLine ;
|
|
}
|
|
else if ( nType != NONE) {
|
|
// creo una composita campionando il bisettore
|
|
CurveComposite* pCompo = CreateBasicCurveComposite() ;
|
|
if ( pCompo == nullptr)
|
|
return nullptr ;
|
|
int nMaxPnts = 100 ;
|
|
double dDelta = ( dParE - dParS) / nMaxPnts ;
|
|
pCompo->AddPoint( ptS) ;
|
|
for ( int j = 1 ; j < nMaxPnts ; j ++) {
|
|
Point3d ptP ;
|
|
m_vroni->GetVDBisectorPointAtParam( i, dParS + j * dDelta, ptP.v) ;
|
|
if ( pCompo->AddLine( ptP)) {
|
|
// salvo le distanze dalla curva nei suoi estremi come temp param
|
|
pCompo->SetCurveTempParam( pCompo->GetCurveCount() - 1, dParS + ( j - 1) * dDelta, 0) ;
|
|
pCompo->SetCurveTempParam( pCompo->GetCurveCount() - 1, dParS + j * dDelta, 1) ;
|
|
}
|
|
}
|
|
// ultimo punto
|
|
if ( pCompo->AddLine( ptE)) {
|
|
pCompo->SetCurveTempParam( pCompo->GetCurveCount() - 1, dParS + ( nMaxPnts - 1) * dDelta, 0) ;
|
|
pCompo->SetCurveTempParam( pCompo->GetCurveCount() - 1, dParE, 1) ;
|
|
}
|
|
pCompo->SetTempParam( dParS, 0) ;
|
|
pCompo->SetTempParam( dParE, 1) ;
|
|
pCompo->ToGlob( m_Frame) ;
|
|
return pCompo ;
|
|
}
|
|
else
|
|
return nullptr ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::CalcVoronoiDiagram( ICURVEPOVECTOR& vCrvs, int nBound)
|
|
{
|
|
vCrvs.clear() ;
|
|
|
|
if ( ! IsValid())
|
|
return false ;
|
|
|
|
// verifico se necessario calcolo Voronoi
|
|
if ( ! m_bVDComputed || nBound != m_nBound)
|
|
CalcVoronoi( nBound) ;
|
|
|
|
for ( int i = 4 ; i < m_vroni->GetNumberOfEdges() ; i ++) {
|
|
// recupero la curva del bisettore
|
|
PtrOwner<ICurve> pCrv( GetBisectorCurve( i)) ;
|
|
if ( ! IsNull( pCrv))
|
|
vCrvs.emplace_back( Release( pCrv)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::CalcMedialAxis( ICURVEPOVECTOR& vCrvs, int nSide)
|
|
{
|
|
vCrvs.clear() ;
|
|
|
|
if ( ! IsValid())
|
|
return false ;
|
|
|
|
if ( ! m_bVDComputed)
|
|
CalcVoronoi() ;
|
|
|
|
// lato per il medial axis
|
|
bool bLeft = true ;
|
|
bool bRight = true ;
|
|
if ( nSide == WMAT_LEFT)
|
|
bRight = false ;
|
|
else if ( nSide == WMAT_RIGHT)
|
|
bLeft = false ;
|
|
|
|
// calcolo medial axis
|
|
m_vroni->apiComputeWMAT( false, 0.0, 0.0, false, bLeft, bRight) ;
|
|
|
|
for ( int i = 4 ; i < m_vroni->GetNumberOfEdges() ; i ++) {
|
|
// verifico se il lato appartiene al medial axis
|
|
if ( m_vroni->IsWMATEdge( i)) {
|
|
PtrOwner<ICurve> pCrv( GetBisectorCurve( i)) ;
|
|
if ( ! IsNull( pCrv))
|
|
vCrvs.emplace_back( Release( pCrv)) ;
|
|
}
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::CalcOffset( ICURVEPOVECTOR& vOffs, double dOffs, int nType)
|
|
{
|
|
vOffs.clear() ;
|
|
|
|
if ( ! IsValid())
|
|
return false ;
|
|
|
|
// verifico se curve sono coerenti per calcolo dell'offset
|
|
if ( ! VerifyCurvesValidityForOffset())
|
|
return false ;
|
|
|
|
// se offset nullo restituisco direttamente le curve
|
|
if ( abs( dOffs) < EPS_SMALL) {
|
|
for ( auto pCrv : m_vpCrvs)
|
|
vOffs.emplace_back( pCrv->Clone()) ;
|
|
}
|
|
|
|
bool bClosed = m_vpCrvs[0]->IsClosed() ;
|
|
|
|
// stabilisco lato offset
|
|
bool bRightOffs = true ;
|
|
bool bLeftOffs = true ;
|
|
if ( bClosed) {
|
|
bRightOffs = ( dOffs > EPS_SMALL ? true : false) ;
|
|
bLeftOffs = ( dOffs < - EPS_SMALL ? true : false) ;
|
|
}
|
|
|
|
// calcolo offset
|
|
ICRVCOMPOPLIST OffsList ;
|
|
if ( ! CalcVroniOffset( OffsList, abs( dOffs), bRightOffs, bLeftOffs))
|
|
return false ;
|
|
|
|
// sistemo le curve di offset calcolate con vroni
|
|
for ( auto pCrv : OffsList) {
|
|
|
|
// aggiustamento per curva aperta
|
|
if ( ! bClosed)
|
|
AdjustOpenOffsetCurve( *pCrv, dOffs) ;
|
|
|
|
if ( pCrv->IsValid()) {
|
|
// eventuale inversione
|
|
if ( dOffs > EPS_SMALL)
|
|
pCrv->Invert() ;
|
|
// sistemo il punto di inizio
|
|
if ( bClosed)
|
|
AdjustOffsetStart( *pCrv) ;
|
|
// sistemo i raccordi
|
|
if ( ( nType & ICurve::OFF_CHAMFER) != 0 || ( nType & ICurve::OFF_EXTEND) != 0) {
|
|
IdentifyFillets( pCrv, dOffs) ;
|
|
AdjustCurveFillets( pCrv, dOffs, nType) ;
|
|
}
|
|
// porto nel frame globale
|
|
pCrv->ToGlob( m_Frame) ;
|
|
// unisco le parti allineate
|
|
pCrv->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG, true, true) ;
|
|
// aggiungo al vettore finale
|
|
vOffs.emplace_back( pCrv) ;
|
|
}
|
|
else
|
|
delete pCrv ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::CalcFatCurve( ICURVEPOVECTOR& vCrvs, double dOffs, bool bSquareEnds, bool bSquareMids)
|
|
{
|
|
vCrvs.clear() ;
|
|
|
|
if ( ! IsValid())
|
|
return false ;
|
|
|
|
// se offset nullo errore
|
|
if ( abs( dOffs) < EPS_SMALL)
|
|
return false ;
|
|
|
|
ICRVCOMPOPLIST OffsList ;
|
|
if ( ! CalcVroniOffset( OffsList, abs( dOffs), true, true))
|
|
return false ;
|
|
|
|
// sistemo le curve di offset calcolate con vroni
|
|
bool bClosed = m_vpCrvs[0]->IsClosed() ;
|
|
for ( auto pCrvOffs : OffsList) {
|
|
|
|
// eventuale inversione
|
|
if ( dOffs > EPS_SMALL)
|
|
pCrvOffs->Invert() ;
|
|
|
|
// sistemo i raccordi
|
|
if ( bClosed && bSquareMids) {
|
|
// se curva è chiusa tutti i raccordi rispondono a bSquareMids
|
|
IdentifyFillets( pCrvOffs, dOffs) ;
|
|
AdjustCurveFillets( pCrvOffs, dOffs, ICurve::OFF_EXTEND) ;
|
|
}
|
|
else if ( ! bClosed && ( bSquareMids || bSquareEnds)) {
|
|
// se curva è aperta devo distinguere i raccordi interni da quelli relativi agli estremi e
|
|
// modificare solo quelli richiesti
|
|
for ( int j = 0 ; j < pCrvOffs->GetCurveCount() ; j ++) {
|
|
int nOrigCrv ; pCrvOffs->GetCurveTempProp( j, nOrigCrv) ;
|
|
double dTmpParam ; pCrvOffs->GetCurveTempParam( j, dTmpParam) ;
|
|
if ( nOrigCrv == 0 && ( ( bSquareMids && dTmpParam < EPS_SMALL) || ( bSquareEnds && dTmpParam > EPS_SMALL)))
|
|
pCrvOffs->SetCurveTempParam( j, 1) ;
|
|
else
|
|
pCrvOffs->SetCurveTempParam( j, 0) ;
|
|
}
|
|
AdjustCurveFillets( pCrvOffs, dOffs, ICurve::OFF_EXTEND) ;
|
|
}
|
|
|
|
// porto nel frame globale
|
|
pCrvOffs->ToGlob( m_Frame) ;
|
|
// unisco le parti allineate
|
|
pCrvOffs->MergeCurves( LIN_TOL_MIN, ANG_TOL_STD_DEG, true, true) ;
|
|
// aggiungo al vettore finale
|
|
vCrvs.emplace_back( pCrvOffs) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::CalcVroniOffset( ICRVCOMPOPLIST& OffsList, double dOffs, bool bRightOffs, bool bLeftOffs)
|
|
{
|
|
OffsList.clear() ;
|
|
|
|
if ( ! IsValid())
|
|
return false ;
|
|
|
|
int nOrigCrvCnt = 1 ;
|
|
if ( m_vpCrvs[0]->GetType() == CRV_COMPO) {
|
|
const CurveComposite * pOrigCompo = GetBasicCurveComposite( m_vpCrvs[0]) ;
|
|
if ( pOrigCompo == nullptr)
|
|
return false ;
|
|
nOrigCrvCnt = pOrigCompo->GetCurveCount() ;
|
|
}
|
|
|
|
// reset di eventuali offset precedenti
|
|
m_vroni->apiResetOffsetData() ;
|
|
|
|
// verifico necessario calcolo o ricalcolo di Voronoi
|
|
UpdateVoronoi( dOffs) ;
|
|
|
|
string sTmp = "" ;
|
|
m_vroni->apiComputeOff( false, &sTmp[0], false, false, dOffs - VRONI_OFFS_TOL, 0.0, false, bLeftOffs, bRightOffs) ;
|
|
|
|
// recupero le curve di offset da vroni
|
|
int nOffsCnt = m_vroni->GetOffsetCount() ; // numero curve di offset
|
|
for ( int i = 0 ; i < nOffsCnt ; i++) {
|
|
PtrOwner<CurveComposite> pCrvOffs ( CreateBasicCurveComposite()) ;
|
|
int nCrvCnt = m_vroni->GetOffsetCurveCount( i) ; // numero di sottocurve
|
|
|
|
for ( int j = 0 ; j < nCrvCnt ; j ++) {
|
|
// recupero la sottocurva da vroni
|
|
Point3d ptS, ptE, ptC ;
|
|
int nType ;
|
|
int nOrigCrv, nOrigLoop, nOrigPnt ; // sito
|
|
m_vroni->GetOffsetCurve( i, j, nType, ptS.v, ptE.v, ptC.v, nOrigLoop, nOrigCrv, nOrigPnt) ;
|
|
|
|
if ( j == 0)
|
|
pCrvOffs->AddPoint( ptS) ;
|
|
|
|
bool bOk = false ;
|
|
if ( nType == t_site::SEG)
|
|
bOk = pCrvOffs->AddLine( ptE) ;
|
|
else {
|
|
PtrOwner<CurveArc> pArc( CreateBasicCurveArc()) ;
|
|
pArc->Set2PRS( ptS, ptE, Dist( ptC, ptS), nType == CCW) ;
|
|
bOk = pCrvOffs->AddCurve( Release( pArc)) ;
|
|
}
|
|
|
|
// se la curva è stata aggiunta
|
|
if ( bOk) {
|
|
// setto come info la sottocurva da cui si è generata
|
|
int nCurrCrvId = pCrvOffs->GetCurveCount() - 1 ;
|
|
pCrvOffs->SetCurveTempProp( nCurrCrvId, nOrigCrv + 1, 0) ;
|
|
pCrvOffs->SetCurveTempProp( nCurrCrvId, nOrigLoop, 1) ;
|
|
// verifico se è raccordo relativo agli estremi della curva
|
|
if ( nOrigCrv == -1 && ( nOrigPnt == 0 || nOrigPnt == nOrigCrvCnt))
|
|
pCrvOffs->SetCurveTempParam( nCurrCrvId, 1.0, 0) ;
|
|
}
|
|
}
|
|
|
|
// aggiungo la curva alla lista degli offset
|
|
if ( ! IsNull( pCrvOffs) && pCrvOffs->IsValid())
|
|
OffsList.push_back( Release( pCrvOffs)) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::UpdateVoronoi( double dOffs)
|
|
{
|
|
double dNeededBound = abs( dOffs) / 0.49 / sqrt( m_bBox.GetDimX() * m_bBox.GetDimX() + m_bBox.GetDimY() * m_bBox.GetDimY()) ;
|
|
if ( ! m_bVDComputed || dNeededBound > m_nBound) {
|
|
// aggiorno il valore del bound
|
|
int nBound = ( int)( ceil( dNeededBound) + 0.5) ;
|
|
// calcolo il nuovo diagramma
|
|
CalcVoronoi( nBound) ;
|
|
}
|
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::VerifyCurvesValidityForOffset()
|
|
{
|
|
if ( m_vpCrvs.size() == 1)
|
|
return true ;
|
|
|
|
// se ho più curve, devono essere tutte chiuse
|
|
for ( auto pCrv : m_vpCrvs) {
|
|
if ( ! pCrv->IsClosed())
|
|
return false ;
|
|
}
|
|
|
|
// verifico se gli orientamenti delle curve sono coerenti
|
|
DBLVECTOR vArea( m_vpCrvs.size()) ;
|
|
double dRefArea = 0.0 ;
|
|
for ( int i = 0 ; i < ( int)m_vpCrvs.size() ; i ++) {
|
|
m_vpCrvs[i]->GetAreaXY( vArea[i]) ;
|
|
if ( abs( vArea[i]) > abs( dRefArea))
|
|
dRefArea = vArea[i] ;
|
|
}
|
|
bool bRefCCW = ( dRefArea > EPS_SMALL) ;
|
|
|
|
for ( int i = 0 ; i < ( int)vArea.size() ; i++) {
|
|
|
|
// se curva con orientamento principale verifico sia esterna a tutte le altre curve con orientamento principale
|
|
if ( vArea[i] * dRefArea > EPS_SMALL) {
|
|
for ( int j = i + 1 ; j < ( int)vArea.size() ; j++) {
|
|
if ( vArea[i] * vArea[j] > EPS_SMALL) {
|
|
IntersCurveCurve ccInt( *m_vpCrvs[i], *m_vpCrvs[j]) ;
|
|
int nRes = ccInt.GetRegionCurveClassification() ;
|
|
if ( ( bRefCCW && nRes != CCREGC_OUT) || ( ! bRefCCW && nRes != CCREGC_IN1))
|
|
return false ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// se curva con orientamento diverso dal principale verifico sia contenuta in una curva con orientamento principale
|
|
else if ( vArea[i] * dRefArea < - EPS_SMALL) {
|
|
bool bInside = false ;
|
|
for ( int j = 0 ; j < ( int)vArea.size() ; j++) {
|
|
if ( j != i && vArea[j] * dRefArea > EPS_SMALL) {
|
|
IntersCurveCurve ccInt( *m_vpCrvs[i], *m_vpCrvs[j]) ;
|
|
int nRes = ccInt.GetRegionCurveClassification() ;
|
|
if ( ( bRefCCW && nRes == CCREGC_IN1) || ( ! bRefCCW && nRes == CCREGC_OUT)) {
|
|
bInside = true ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
if ( ! bInside)
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::AdjustOpenOffsetCurve( ICurveComposite& pCompo, double dOffs)
|
|
{
|
|
int nSideRef = dOffs < EPS_SMALL ? MDS_LEFT : MDS_RIGHT ;
|
|
|
|
// recupero i raccordi relativi agli estremi della curva aperta
|
|
INTVECTOR vJunctions ;
|
|
for ( int i = 0 ; i < pCompo.GetCurveCount() ; i++) {
|
|
double dParTmp ;
|
|
pCompo.GetCurveTempParam( i, dParTmp) ;
|
|
if ( dParTmp > EPS_SMALL)
|
|
vJunctions.push_back( i) ;
|
|
}
|
|
|
|
if ( vJunctions.size() == 0) {
|
|
// verifico se si trova dal lato corretto
|
|
int nSide = GetOffsetCurveSide( pCompo, 0) ;
|
|
if ( nSide != nSideRef)
|
|
// se lato errato, tutta la curva va cancellata
|
|
pCompo.Clear() ;
|
|
}
|
|
else if ( vJunctions.size() == 2) {
|
|
// recupero i due tratti di curva
|
|
PtrOwner<CurveComposite> pCompo1( ConvertCurveToBasicComposite( pCompo.CopyParamRange( vJunctions[0] + 1, vJunctions[1]))) ;
|
|
PtrOwner<CurveComposite> pCompo2( ConvertCurveToBasicComposite( pCompo.CopyParamRange( vJunctions[1] + 1, vJunctions[0]))) ;
|
|
pCompo.Clear() ;
|
|
if ( ! IsNull( pCompo1) && pCompo1->IsValid()) {
|
|
int nSide = GetOffsetCurveSide( *pCompo1, 0) ;
|
|
if ( nSide == nSideRef)
|
|
pCompo.CopyFrom( pCompo1) ;
|
|
else if ( ! IsNull( pCompo2) && pCompo2->IsValid())
|
|
pCompo.CopyFrom( pCompo2) ;
|
|
}
|
|
else if ( ! IsNull( pCompo2) && pCompo2->IsValid()) {
|
|
int nSide = GetOffsetCurveSide( *pCompo2, 0) ;
|
|
if ( nSide == nSideRef)
|
|
pCompo.CopyFrom( pCompo2) ;
|
|
}
|
|
}
|
|
else {
|
|
// mi posiziono dopo la junction
|
|
pCompo.ChangeStartPoint( vJunctions[0] + 1) ;
|
|
delete( pCompo.RemoveFirstOrLastCurve()) ;
|
|
// verifico validità della curva
|
|
int nSide = GetOffsetCurveSide( pCompo, 0) ;
|
|
if ( nSide == nSideRef) {
|
|
// scorro fino alla prima curva non valida ed elimino la curva da quel punto fino alla fine
|
|
for ( int i = 1 ; i < pCompo.GetCurveCount() ; i++) {
|
|
int nSide = GetOffsetCurveSide( pCompo, i) ;
|
|
if ( nSide != nSideRef)
|
|
pCompo.TrimEndAtParam( i) ;
|
|
}
|
|
}
|
|
else {
|
|
// elimino finchè non trovo una curva valida
|
|
while( nSide != nSideRef && pCompo.IsValid()) {
|
|
delete( pCompo.RemoveFirstOrLastCurve( false)) ;
|
|
if ( pCompo.IsValid())
|
|
nSide = GetOffsetCurveSide( pCompo, 0) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
int
|
|
Voronoi::GetOffsetCurveSide( const ICurveComposite& pOffs, int nCrv)
|
|
{
|
|
Point3d ptM ; pOffs.GetCurve( nCrv)->GetMidPoint(ptM) ;
|
|
int nOrigCrv ; pOffs.GetCurveTempProp( nCrv, nOrigCrv) ;
|
|
|
|
const ICurve* pCrvRef = m_vpCrvs[0] ;
|
|
// se ha una sottocurva di riferimento
|
|
if ( nOrigCrv != 0 && m_vpCrvs[0]->GetType() == CRV_COMPO) {
|
|
const CurveComposite* pCompoOrig = GetBasicCurveComposite( m_vpCrvs[0]) ;
|
|
if ( pCompoOrig != nullptr)
|
|
pCrvRef = pCompoOrig->GetCurve( nOrigCrv - 1) ;
|
|
}
|
|
|
|
DistPointCurve distPC( ptM, *pCrvRef) ;
|
|
int nSide = MDS_ON ;
|
|
distPC.GetSideAtMinDistPoint( 0, Z_AX, nSide) ;
|
|
return nSide ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool
|
|
Voronoi::AdjustOffsetStart( ICurveComposite& pCrv)
|
|
{
|
|
for ( int i = 0 ; i < pCrv.GetCurveCount() ; i++) {
|
|
// cerco il tratto associato alla prima curva originale
|
|
int nProp ; pCrv.GetCurveTempProp( i, nProp) ;
|
|
if ( nProp == 1) {
|
|
pCrv.ChangeStartPoint( i) ;
|
|
break ;
|
|
}
|
|
// oppure un raccordo di junction
|
|
double dParam ; pCrv.GetCurveTempParam( i, dParam) ;
|
|
if ( abs( dParam - 1) < EPS_SMALL) {
|
|
pCrv.ChangeStartPoint( i + 1) ;
|
|
break ;
|
|
}
|
|
}
|
|
return true ;
|
|
}
|