Files
EgtGeomKernel/CurveComposite.cpp
Daniele Bariletti bbc98fe282 EgtGeomKernel :
- cambiata la chiamata a ModifyJoint
- cambiato nome alla ModifySingleCurve
- estesa la ModifyJoint con tolleranza
- modifiche stilistiche e pulizia codice.
2026-04-28 11:09:34 +02:00

4025 lines
133 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2013-2022
//----------------------------------------------------------------------------
// File : CurveComposite.cpp Data : 23.01.22 Versione : 2.4a4
// Contenuto : Implementazione della classe CCurveComposite.
//
//
//
// Modifiche : 26.04.13 DS Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "CurveComposite.h"
#include "DistPointCrvComposite.h"
#include "CurveLine.h"
#include "CurveArc.h"
#include "CurveBezier.h"
#include "PolygonPlane.h"
#include "SurfFlatRegion.h"
#include "RemoveCurveDefects.h"
#include "GeoConst.h"
#include "GeoObjFactory.h"
#include "NgeWriter.h"
#include "NgeReader.h"
#include "Voronoi.h"
#include "/EgtDev/Include/EGkDistPointLine.h"
#include "/EgtDev/Include/EGkCurveByApprox.h"
#include "/EgtDev/Include/EGkArcSpecial.h"
#include "/EgtDev/Include/EGkSfrCreate.h"
#include "/EgtDev/Include/EGkIntervals.h"
#include "/EgtDev/Include/EGkStringUtils3d.h"
#include "/EgtDev/Include/EgtNumUtils.h"
#include "/EgtDev/Include/EgtPointerOwner.h"
#include <algorithm>
using namespace std ;
//----------------------------------------------------------------------------
static const double EPS_CONNECT = 0.01 * EPS_SMALL ;
//----------------------------------------------------------------------------
GEOOBJ_REGISTER( CRV_COMPO, NGE_C_CMP, CurveComposite) ;
//----------------------------------------------------------------------------
CurveComposite::CurveComposite( void)
: m_nStatus( TO_VERIFY), m_VtExtr(), m_dThick(), m_ptStart(),
m_nTempProp{0,0}, m_dTempParam{0.0,0.0}, m_pVoronoiObj( nullptr), m_Iter( m_CrvSmplS.end())
{
}
//----------------------------------------------------------------------------
CurveComposite::~CurveComposite( void)
{
Clear() ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Clear( void)
{
// ciclo di pulizia
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++Iter)
delete (*Iter) ;
m_CrvSmplS.clear() ;
m_nStatus = TO_VERIFY ;
m_VtExtr = V_NULL ;
m_dThick = 0 ;
m_ptStart = ORIG ;
m_nTempProp[0] = 0 ;
m_nTempProp[1] = 0 ;
m_dTempParam[0] = 0.0 ;
m_dTempParam[1] = 0.0 ;
m_Iter = m_CrvSmplS.end() ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddCurve( const ICurve& cCrv, bool bEndOrStart, double dLinTol)
{
// se curva semplice
if ( cCrv.IsSimple()) {
// creo una copia della curva
ICurve* pSmplCrv = cCrv.Clone() ;
if ( pSmplCrv == nullptr)
return false ;
// inserisco la curva
if ( ! AddSimpleCurve( pSmplCrv, bEndOrStart, dLinTol))
return false ;
}
// altrimenti curva composta, devo aggiungere le singole curve semplici
else {
const ICurveComposite* pCrvCompo ;
const ICurve* pCrvOri ;
ICurve* pSmplCrv ;
// recupero le curve componenti e le inserisco nella lista
pCrvCompo = GetCurveComposite( &cCrv) ;
if ( pCrvCompo == nullptr)
return false ;
pCrvOri = ( bEndOrStart ? pCrvCompo->GetFirstCurve() : pCrvCompo->GetLastCurve()) ;
while ( pCrvOri != nullptr) {
// creo una copia della curva
pSmplCrv = pCrvOri->Clone() ;
if ( pSmplCrv == nullptr)
return false ;
// inserisco la curva
if ( ! AddSimpleCurve( pSmplCrv, bEndOrStart, dLinTol))
return false ;
// passo alla prossima curva componente
pCrvOri = ( bEndOrStart ? pCrvCompo->GetNextCurve() : pCrvCompo->GetPrevCurve()) ;
}
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddCurve( ICurve* pCrv, bool bEndOrStart, double dLinTol)
{
// verifica curva
if ( pCrv == nullptr)
return false ;
// se curva semplice
if ( pCrv->IsSimple()) {
// inserisco la curva
if ( ! AddSimpleCurve( pCrv, bEndOrStart, dLinTol))
return false ;
}
// altrimenti curva composita, devo aggiungere le singole curve semplici
else {
// riloco le curve dalla composita sorgente alla corrente
PtrOwner<CurveComposite> pCrvCompo( GetBasicCurveComposite( pCrv)) ;
if ( IsNull( pCrvCompo) || ! AddCurveByRelocate( *pCrvCompo, bEndOrStart, dLinTol))
return false ;
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddCurveByRelocate( CurveComposite& ccSrc, bool bEndOrStart, double dLinTol)
{
// verifica curva
if ( &ccSrc == nullptr)
return false ;
// recupero le curve componenti e le inserisco nella lista
ICurve* pSmplCrv ;
while ( ( pSmplCrv = ccSrc.RemoveFirstOrLastCurve( ! bEndOrStart)) != nullptr) {
// inserisco la curva
if ( ! AddSimpleCurve( pSmplCrv, bEndOrStart, dLinTol))
return false ;
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddSimpleCurve( ICurve* pSmplCrv, bool bEndOrStart, double dLinTol)
{
// prendo la proprietà del puntatore
PtrOwner<ICurve> pCrv( pSmplCrv) ;
if ( IsNull( pCrv))
return false ;
// verifico lo stato
if ( m_nStatus != OK && ! ( m_CrvSmplS.empty() && ( m_nStatus == TO_VERIFY || m_nStatus == IS_A_POINT)))
return false ;
// controllo la tolleranza
dLinTol = max( dLinTol, EPS_SMALL) ;
// verifico che il parametro sia una curva semplice
if ( ! pCrv->IsSimple())
return false ;
// annullo l'estrusione e lo spessore
pCrv->SetExtrusion( V_NULL) ;
pCrv->SetThickness( 0) ;
// recupero i punti iniziali e finali della curva
Point3d ptCrvStart, ptCrvEnd ;
if ( ! pCrv->GetStartPoint( ptCrvStart) || ! pCrv->GetEndPoint( ptCrvEnd))
return false ;
// se non è la prima
if ( ! m_CrvSmplS.empty()) {
// se inserita alla fine
if ( bEndOrStart) {
// verifico sia in continuità con il finale attuale
Point3d ptEnd ;
GetEndPoint( ptEnd) ;
if ( ! AreSamePointEpsilon( ptCrvStart, ptEnd, EPS_CONNECT)) {
// se in tolleranza, modifico l'inizio dell'entità
if ( SqDist( ptCrvStart, ptEnd) < ( dLinTol * dLinTol)) {
// lunghezza della curva originale
double dOldLen ; pCrv->GetLength( dOldLen) ;
// eseguo modifica
if ( ! pCrv->ModifyStart( ptEnd)) {
CurveLine crvLine ;
if ( ! crvLine.Set( ptEnd, ptCrvEnd) || ! pCrv.Set( crvLine.Clone()))
return false ;
}
// verifico che la lunghezza non sia variata troppo
double dNewLen ; pCrv->GetLength( dNewLen) ;
if ( abs( dNewLen - dOldLen) > 10 * dLinTol)
return false ;
}
else
return false ;
}
}
// altrimenti inserita all'inizio
else {
// verifico sia in continuità con l'iniziale attuale
Point3d ptStart ;
GetStartPoint( ptStart) ;
if ( ! AreSamePointEpsilon( ptCrvEnd, ptStart, EPS_CONNECT)) {
// se in tolleranza, modifico la fine dell'entità
if ( SqDist( ptCrvEnd, ptStart) < ( dLinTol * dLinTol)) {
// lunghezza della curva originale
double dOldLen ; pCrv->GetLength( dOldLen) ;
// eseguo modifica
if ( ! pCrv->ModifyEnd( ptStart)) {
CurveLine crvLine ;
if ( ! crvLine.Set( ptCrvStart, ptStart) || ! pCrv.Set( crvLine.Clone()))
return false ;
}
// verifico che la lunghezza non sia variata troppo
double dNewLen ; pCrv->GetLength( dNewLen) ;
if ( abs( dNewLen - dOldLen) > 10 * dLinTol)
return false ;
}
else
return false ;
}
}
}
// inserisco la curva nella lista
if ( bEndOrStart)
m_CrvSmplS.push_back( ::Release( pCrv)) ;
else
m_CrvSmplS.push_front( ::Release( pCrv)) ;
// aggiorno lo stato
m_nStatus = OK ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
return TestClosure() ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Close( void)
{
// verifico curva valida
if ( m_nStatus != OK)
return false ;
// determino la distanza tra gli estremi
Point3d ptStart, ptEnd ;
if ( ! GetStartPoint( ptStart) ||
! GetEndPoint( ptEnd))
return false ;
// se distanza inferiore al limite ridotto, non faccio alcunché
if ( AreSamePointEpsilon( ptStart, ptEnd, EPS_CONNECT))
return true ;
// se molto vicini li modifico
if ( AreSamePointEpsilon( ptStart, ptEnd, 10 * EPS_SMALL)) {
// se un solo arco
if ( m_CrvSmplS.size() == 1 && m_CrvSmplS.front()->GetType() == CRV_ARC) {
CurveArc* pArc = GetBasicCurveArc( m_CrvSmplS.front()) ;
return pArc->ChangeAngCenter( pArc->GetAngCenter() > 0 ? ANG_FULL : -ANG_FULL) ;
}
// caso generale
Point3d ptMid = Media( ptStart, ptEnd) ;
if ( ! m_CrvSmplS.front()->ModifyStart( ptMid) ||
! m_CrvSmplS.back()->ModifyEnd( ptMid))
return false ;
}
// altrimenti aggiungo la linea di chiusura
else {
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
if ( ! pLine->Set( ptEnd, ptStart) ||
! AddSimpleCurve( Release( pLine)))
return false ;
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::FromSplit( const ICurve& cCrv, int nParts)
{
// verifico lo stato
if ( ! m_CrvSmplS.empty() || m_nStatus != TO_VERIFY)
return false ;
// deve essere valida e semplice
if ( ! cCrv.IsValid() || ! cCrv.IsSimple())
return false ;
// se 1 parte o meno, non fa alcunchè
if ( nParts <= 1)
return true ;
// calcolo la lunghezza della curva
double dTotLen ;
if ( ! cCrv.GetLength( dTotLen))
return false ;
// ciclo di inserimento delle parti
double dStartLen = 0 ;
double dEndLen = 0 ;
for ( int i = 1 ; i <= nParts ; ++ i) {
dStartLen = dEndLen ;
dEndLen = dTotLen * i / (double) nParts ;
PtrOwner<ICurve> pSmplCrv( cCrv.Clone()) ;
if ( IsNull( pSmplCrv))
return false ;
if ( ! pSmplCrv->TrimEndAtLen( dEndLen) ||
! pSmplCrv->TrimStartAtLen( dStartLen))
return false ;
if ( ! AddSimpleCurve( Release( pSmplCrv)))
return false ;
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::FromPolyLine( const PolyLine& PL)
{
// verifico lo stato
if ( ! m_CrvSmplS.empty() || m_nStatus != TO_VERIFY)
return false ;
// deve esserci almeno 1 linea (2 punti)
if ( PL.GetLineNbr() < 1)
return false ;
// ciclo di inserimento dei segmenti che uniscono i punti
double dParIni, dParFin ;
Point3d ptIni, ptFin ;
PL.GetFirstUPoint( &dParIni, &ptIni) ;
while ( PL.GetNextUPoint( &dParFin, &ptFin)) {
// se i punti della coppia coincidono, passo alla coppia successiva
if ( AreSamePointApprox( ptIni, ptFin))
continue ;
// creo il segmento di retta
PtrOwner<CurveLine> pCrvLine( CreateBasicCurveLine()) ;
if ( IsNull( pCrvLine))
return false ;
// assegno i punti estremi
if ( ! pCrvLine->Set( ptIni, ptFin))
return false ;
// assegno i parametri degli estremi
pCrvLine->SetTempParam( dParIni, 0) ;
pCrvLine->SetTempParam( dParFin, 1) ;
// aggiungo la retta alla curva composita
if ( ! AddSimpleCurve( Release( pCrvLine)))
return false ;
// aggiorno dati prossimo punto iniziale
dParIni = dParFin ;
ptIni = ptFin ;
}
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::FromPolyArc( const PolyArc& PA)
{
// verifico lo stato
if ( ! m_CrvSmplS.empty() || m_nStatus != TO_VERIFY)
return false ;
// deve esserci almeno 1 arco (2 punti)
if ( PA.GetArcNbr() < 1)
return false ;
// Creo le diverse curve semplici e le inserisco in quella composita
double dBulge, dNextBulge ;
Point3d ptIni, ptFin ;
PA.GetFirstPoint( ptIni, dBulge) ;
while ( PA.GetNextPoint( ptFin, dNextBulge)) {
// se i punti della coppia coincidono, passo alla coppia successiva
if ( AreSamePointApprox( ptIni, ptFin))
continue ;
// se retta
if ( abs( dBulge) < EPS_SMALL) {
// creo la retta
PtrOwner<CurveLine> pCrvLine( CreateBasicCurveLine()) ;
if ( IsNull( pCrvLine))
return false ;
// setto la linea
if ( ! pCrvLine->Set( ptIni, ptFin))
return false ;
// inserisco la linea nella curva composta
if ( ! AddSimpleCurve( ::Release( pCrvLine)))
return false ;
}
// altrimenti arco
else {
// creo l'arco
PtrOwner<CurveArc> pCrvArc( CreateBasicCurveArc()) ;
if ( IsNull( pCrvArc))
return false ;
// setto l'arco
if ( ! pCrvArc->Set2PNB( ptIni, ptFin, PA.GetExtrusion(), dBulge))
return false ;
// inserisco l'arco nella curva composta
if ( ! AddSimpleCurve( Release( pCrvArc)))
return false ;
}
// aggiorno dati prossimo punto iniziale
ptIni = ptFin ;
dBulge = dNextBulge ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::PolygonCenterCorner( int nSides, const Point3d& ptCen, const Point3d& ptCorner, const Vector3d& vtN)
{
// verifico lo stato
if ( ! m_CrvSmplS.empty() || m_nStatus != TO_VERIFY)
return false ;
// almeno 3 lati e normale ben definita
if ( nSides < 3 || vtN.IsSmall())
return false ;
// angolo al centro corrispondente ad un lato
double dAngCenSideDeg = ANG_FULL / nSides ;
double dCosAng = cos( dAngCenSideDeg * DEGTORAD) ;
double dSinAng = sin( dAngCenSideDeg * DEGTORAD) ;
// ciclo di inserimento dei segmenti che uniscono i punti
Point3d ptStart = ptCorner ;
Point3d ptEnd ;
for ( int i = 1 ; i <= nSides ; ++ i) {
// calcolo il secondo punto del lato
ptEnd = ptStart ;
ptEnd.Rotate( ptCen, vtN, dCosAng, dSinAng) ;
// se primo lato, verifico che la lunghezza sia superiore al minimo
if ( i == 1) {
if ( AreSamePointApprox( ptStart, ptEnd))
return false ;
}
// creo il segmento di retta
PtrOwner<CurveLine> pCrvLine( CreateBasicCurveLine()) ;
if ( IsNull( pCrvLine))
return false ;
// assegno i punti estremi
if ( ! pCrvLine->Set( ptStart, ptEnd))
return false ;
// aggiungo la retta alla curva composita
if ( ! AddSimpleCurve( Release( pCrvLine)))
return false ;
// aggiorno punto iniziale prossimo lato
ptStart = ptEnd ;
}
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::PolygonCenterMidSide( int nSides, const Point3d& ptCen, const Point3d& ptMidSide, const Vector3d& vtN)
{
// verifico lo stato
if ( ! m_CrvSmplS.empty() || m_nStatus != TO_VERIFY)
return false ;
// almeno 3 lati e normale ben definita
if ( nSides < 3 || vtN.IsSmall())
return false ;
// calcolo la posizione del corner
double dHalfCentSideAngDeg = ANG_FULL / ( 2 * nSides) ;
double dCoeff = tan( dHalfCentSideAngDeg * DEGTORAD) ;
Vector3d vtDir = ptCen - ptMidSide ;
vtDir -= ( vtDir * vtN) * vtN / vtN.SqLen() ;
vtDir.Rotate( vtN, 0, 1) ;
Point3d ptCorner = ptMidSide + vtDir * dCoeff ;
// ora costruisco come poligono con centro e corner
return PolygonCenterCorner( nSides, ptCen, ptCorner, vtN) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::PolygonSide( int nSides, const Point3d& ptStart, const Point3d& ptEnd, const Vector3d& vtN)
{
// verifico lo stato
if ( ! m_CrvSmplS.empty() || m_nStatus != TO_VERIFY)
return false ;
// almeno 3 lati e normale ben definita
if ( nSides < 3 || vtN.IsSmall())
return false ;
// verifico che la lunghezza sia superiore al minimo
if ( AreSamePointApprox( ptStart, ptEnd))
return false ;
// calcolo la posizione del centro
double dHalfCentSideAngDeg = ANG_FULL / ( 2 * nSides) ;
double dCoeff = 1 / ( 2 * tan( dHalfCentSideAngDeg * DEGTORAD)) ;
Vector3d vtDir = ptEnd - ptStart ;
vtDir -= ( vtDir * vtN) * vtN / vtN.SqLen() ;
vtDir.Rotate( vtN, 0, 1) ;
Point3d ptCen = Media( ptStart, ptEnd, 0.5) + vtDir * dCoeff ;
// ora costruisco come poligono con centro e corner
return PolygonCenterCorner( nSides, ptCen, ptStart, vtN) ;
}
//----------------------------------------------------------------------------
CurveComposite*
CurveComposite::Clone( void) const
{
// alloco oggetto
CurveComposite* pCrv = new( nothrow) CurveComposite ;
if ( pCrv != nullptr) {
if ( ! pCrv->CopyFrom( *this)) {
delete pCrv ;
return nullptr ;
}
}
return pCrv ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::CopyFrom( const IGeoObj* pGObjSrc)
{
// se sorgente è una curva composita
const CurveComposite* pCC = GetBasicCurveComposite( pGObjSrc) ;
if ( pCC != nullptr)
return CopyFrom( *pCC) ;
// se sorgente è un'altro tipo di curva
const ICurve* pCrv = ::GetCurve( pGObjSrc) ;
if ( pCrv != nullptr) {
Clear() ;
pCrv->GetExtrusion( m_VtExtr) ;
pCrv->GetThickness( m_dThick) ;
return AddCurve( *pCrv) ;
}
// altrimenti errroe
return false ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::CopyFrom( const CurveComposite& ccSrc)
{
if ( &ccSrc == this)
return true ;
Clear() ;
m_VtExtr = ccSrc.m_VtExtr ;
m_dThick = ccSrc.m_dThick ;
m_nTempProp[0] = ccSrc.m_nTempProp[0] ;
m_nTempProp[1] = ccSrc.m_nTempProp[1] ;
m_dTempParam[0] = ccSrc.m_dTempParam[0] ;
m_dTempParam[1] = ccSrc.m_dTempParam[1] ;
for ( auto& pCrv : ccSrc.m_CrvSmplS) {
if ( ! AddCurve( *pCrv))
return false ;
}
if ( ccSrc.m_nStatus == IS_A_POINT) {
m_ptStart = ccSrc.m_ptStart ;
m_nStatus = IS_A_POINT ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::RelocateFrom( CurveComposite& ccSrc)
{
if ( &ccSrc == this)
return true ;
Clear() ;
m_VtExtr = ccSrc.m_VtExtr ;
m_dThick = ccSrc.m_dThick ;
m_nTempProp[0] = ccSrc.m_nTempProp[0] ;
m_nTempProp[1] = ccSrc.m_nTempProp[1] ;
m_dTempParam[0] = ccSrc.m_dTempParam[0] ;
m_dTempParam[1] = ccSrc.m_dTempParam[1] ;
for ( ICurve* pCrv = ccSrc.RemoveFirstOrLastCurve( false) ;
pCrv != nullptr ;
pCrv = ccSrc.RemoveFirstOrLastCurve( false)) {
if ( ! AddSimpleCurve( pCrv))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
GeoObjType
CurveComposite::GetType( void) const
{
return static_cast<GeoObjType>( GEOOBJ_GETTYPE( CurveComposite)) ;
}
//----------------------------------------------------------------------------
const string&
CurveComposite::GetTitle( void) const
{
static const string sTitle = "CurveCompo" ;
return sTitle ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Dump( string& sOut, bool bMM, const char* szNewLine) const
{
// dati generali di una curva
if ( ! CurveDump( *this, sOut, bMM, szNewLine))
return false ;
// prealloco la stringa
const int MAX_CRV = 1000 ;
sOut.reserve( 100 + min( int (m_CrvSmplS.size()), MAX_CRV) * 60) ;
// parametri : numero di curve
sOut += "CrvNbr=" + ToString( int( m_CrvSmplS.size())) + szNewLine ;
// ciclo sulle curve componenti
int i = 0 ;
const ICurve* pCrvSmpl = GetFirstCurve() ;
while ( pCrvSmpl != nullptr && i < MAX_CRV) {
// assegno ed emetto nome e tipo della curva semplice
sOut += "#" + ToString( i) + " " + pCrvSmpl->GetTitle() + szNewLine ;
// dati della curva semplice
if ( ! pCrvSmpl->Dump( sOut, bMM, szNewLine))
return false ;
// passo alla successiva
++ i ;
pCrvSmpl = GetNextCurve() ;
}
if ( pCrvSmpl != nullptr)
sOut += string( ". . .") + szNewLine ;
return true ;
}
//----------------------------------------------------------------------------
int
CurveComposite::GetNgeId( void) const
{
return GEOOBJ_GETNGEID( CurveComposite) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Save( NgeWriter& ngeOut) const
{
// numero di curve
if ( ! ngeOut.WriteInt( int( m_CrvSmplS.size()), nullptr, true))
return false ;
// ciclo sulle curve componenti
int i = 0 ;
const ICurve* pCrvSmpl = GetFirstCurve() ;
while ( pCrvSmpl != nullptr) {
// recupero il gestore di lettura/scrittura della curva
const IGeoObjRW* pCSmplRW = dynamic_cast<const IGeoObjRW*>( pCrvSmpl) ;
if ( pCSmplRW == nullptr)
return false ;
// emetto tipo della curva semplice
if ( ! ngeOut.WriteKey( pCSmplRW->GetNgeId()))
return false ;
// assegno ed emetto nome della curva semplice
string sCrvName = "#" + ToString( ++i) ;
if ( ! ngeOut.WriteString( sCrvName, nullptr, true))
return false ;
// salvataggio della curva semplice
if ( ! pCSmplRW->Save( ngeOut))
return false ;
// passo alla successiva
pCrvSmpl = GetNextCurve() ;
}
// da versione 1008 : linea con VtEstrusione e Spessore
if ( ! ngeOut.WriteVector( m_VtExtr, ";"))
return false ;
if ( ! ngeOut.WriteDouble( m_dThick, ";", true))
return false ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Load( NgeReader& ngeIn)
{
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// leggo la prossima linea ( 1 parametro)
// recupero il numero di curve componenti
int nCounter ;
if ( ! ngeIn.ReadInt( nCounter, nullptr, true))
return false ;
// leggo le curve componenti
for ( int i = 0 ; i < nCounter ; ++ i) {
// recupero la prossima linea (con il tipo di oggetto)
int nNgeId ;
if ( ! ngeIn.ReadKey( nNgeId))
return false ;
// creo l'oggetto
int nType = GEOOBJ_NGEIDTOTYPE( nNgeId) ;
IGeoObj* pGeoO = GEOOBJ_CREATE( nType) ;
if ( pGeoO == nullptr)
return false ;
// recupero la linea con il nome
string sName ;
bool bOk = ngeIn.ReadString( sName, nullptr, true) ;
// ne leggo i dati
IGeoObjRW* pGObjRW = dynamic_cast<IGeoObjRW*>( pGeoO) ;
bOk = bOk && ( pGObjRW != nullptr && pGObjRW->Load( ngeIn)) ;
// verifico sia una curva
ICurve* pCrv = ::GetCurve( pGeoO) ;
bOk = bOk && ( pCrv != nullptr && pCrv->IsSimple()) ;
// aggiungo questa curva (sicuramente semplice)
bOk = bOk && AddSimpleCurve( pCrv, true, 10 * EPS_SMALL) ;
// se errore
if ( ! bOk)
return false ;
}
// da versione 1008 : linea con VtEstrusione e Spessore
if ( ngeIn.GetFileVersion() >= NGE_VER_1008) {
if ( ! ngeIn.ReadVector( m_VtExtr, ";"))
return false ;
if ( ! ngeIn.ReadDouble( m_dThick, ";", true))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetLocalBBox( BBox3d& b3Loc, int nFlag) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// inizializzo il box
b3Loc.Reset() ;
// ciclo sulle curve componenti
const ICurve* pCrvSmpl ;
pCrvSmpl = GetFirstCurve() ;
while ( pCrvSmpl != nullptr) {
BBox3d b3Crv ;
// recupero il box della curva
pCrvSmpl->GetLocalBBox( b3Crv, nFlag) ;
// aggiorno il box
b3Loc.Add( b3Crv) ;
// passo alla curva successiva
pCrvSmpl = GetNextCurve() ;
}
// se c'è estrusione, devo tenerne conto (curve componenti sempre con vtExtr e dThick nulli)
if ( ! m_VtExtr.IsSmall() && abs( m_dThick) > EPS_SMALL) {
Point3d ptMinExtr = b3Loc.GetMin() + m_VtExtr * m_dThick ;
Point3d ptMaxExtr = b3Loc.GetMax() + m_VtExtr * m_dThick ;
b3Loc.Add( ptMinExtr) ;
b3Loc.Add( ptMaxExtr) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetBBox( const Frame3d& frRef, BBox3d& b3Ref, int nFlag) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// verifico validità del frame
if ( frRef.GetType() == Frame3d::ERR)
return false ;
// inizializzo il box
b3Ref.Reset() ;
// ciclo sulle curve componenti (sempre senza estrusione)
const ICurve* pCrvSmpl ;
pCrvSmpl = GetFirstCurve() ;
while ( pCrvSmpl != nullptr) {
BBox3d b3Crv ;
// recupero il box della curva
pCrvSmpl->GetBBox( frRef, b3Crv, nFlag) ;
// aggiorno il box
b3Ref.Add( b3Crv) ;
// passo alla curva successiva
pCrvSmpl = GetNextCurve() ;
}
// se c'è estrusione, devo tenerne conto (curve componenti sempre con vtExtr e dThick nulli)
if ( ! m_VtExtr.IsSmall() && abs( m_dThick) > EPS_SMALL) {
Vector3d vtFrExtr = m_VtExtr ;
vtFrExtr.ToGlob( frRef) ;
Point3d ptMinExtr = b3Ref.GetMin() + vtFrExtr * m_dThick ;
Point3d ptMaxExtr = b3Ref.GetMax() + vtFrExtr * m_dThick ;
b3Ref.Add( ptMinExtr) ;
b3Ref.Add( ptMaxExtr) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Validate( void)
{
if ( m_nStatus == TO_VERIFY) {
Point3d ptPrevEnd ;
Point3d ptStart ;
// ciclo su tutte le curve
int nCount = 0 ;
for ( auto Iter = m_CrvSmplS.cbegin() ; Iter != m_CrvSmplS.cend() ; ++Iter) {
// verifico validità della curva e sua semplicità
if ( ! (*Iter)->IsValid() || (*Iter)->GetType() == CRV_COMPO) {
m_nStatus = ERR ;
return false ;
}
// incremento contatore
++ nCount ;
// verifico continuità con la precedente (se non è la prima)
if ( nCount > 1) {
(*Iter)->GetStartPoint( ptStart) ;
if ( ! AreSamePointApprox( ptPrevEnd, ptStart)) {
m_nStatus = ERR ;
return false ;
}
}
// recupero il punto finale
(*Iter)->GetEndPoint( ptPrevEnd) ;
}
// aggiorno
m_nStatus = ( nCount > 0 ? OK : TO_VERIFY) ;
}
// verifico chiusura
TestClosure() ;
return ( m_nStatus == OK) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::TestClosure( double dLinTol)
{
// se non valida o vuota, esco subito
if ( m_nStatus != OK || m_CrvSmplS.empty())
return true ;
// se non è chiusa entro la tolleranza, esco subito
Point3d ptStart, ptEnd ;
if ( ! m_CrvSmplS.front()->GetStartPoint( ptStart) ||
! m_CrvSmplS.back()->GetEndPoint( ptEnd) ||
! AreSamePointEpsilon( ptStart, ptEnd, dLinTol))
return true ;
// se singola retta, esco subito
if ( m_CrvSmplS.size() == 1 && m_CrvSmplS.front()->GetType() == CRV_LINE)
return true ;
// verifico ed eventualmente aggiusto coincidenza punti estremi
// se distanza superiore al limite ridotto forzo i punti a coincidere
if ( ! AreSamePointEpsilon( ptStart, ptEnd, EPS_CONNECT)) {
// se un solo arco
if ( m_CrvSmplS.size() == 1 && m_CrvSmplS.front()->GetType() == CRV_ARC) {
CurveArc* pArc = GetBasicCurveArc( m_CrvSmplS.front()) ;
return pArc->ChangeAngCenter( pArc->GetAngCenter() > 0 ? ANG_FULL : -ANG_FULL) ;
}
// caso generale
Point3d ptM = Media( ptStart, ptEnd) ;
return ( m_CrvSmplS.front()->ModifyStart( ptM) &&
m_CrvSmplS.back()->ModifyEnd( ptM)) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::IsFlat( Plane3d& plPlane, bool bUseExtrusion, double dToler) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// polilinea dei punti rappresentativi
PolyLine shPL ;
// punto iniziale
Point3d ptP ;
if ( ! GetStartPoint( ptP) ||
! shPL.AddUPoint( 0, ptP))
return false ;
// ciclo sulle curve semplici (aggiungo solo eventuali punti intermedi e finali)
int nCount = 0 ;
for ( const ICurve* pCrv = GetCurve( nCount) ;
pCrv != nullptr ;
pCrv = GetCurve( ++ nCount)) {
switch ( pCrv->GetType()) {
case CRV_LINE :
// punto finale
if ( ! pCrv->GetEndPoint( ptP) ||
! shPL.AddUPoint( nCount + 1, ptP))
return false ;
break ;
case CRV_ARC :
// punto a 1/3
if ( ! pCrv->GetPointD1D2( 0.3333, ICurve::FROM_MINUS, ptP) ||
! shPL.AddUPoint( nCount + 0.3333, ptP))
return false ;
// punto a 7/11
if ( ! pCrv->GetPointD1D2( 0.6363, ICurve::FROM_MINUS, ptP) ||
! shPL.AddUPoint( nCount + 0.6363, ptP))
return false ;
// punto finale
if ( ! pCrv->GetEndPoint( ptP) ||
! shPL.AddUPoint( nCount + 1, ptP))
return false ;
break ;
case CRV_BEZIER :
{ const CurveBezier* pBez = GetBasicCurveBezier( pCrv) ;
// inserisco tutti i punti di controllo tranne il primo
int nLastPC = pBez->GetDegree() ;
for ( int i = 1 ; i <= nLastPC ; ++ i) {
double dU = nCount + i / double( nLastPC) ;
if ( ! shPL.AddUPoint( dU, pBez->GetControlPoint( i)))
return false ;
}
} break ;
default :
return false ;
}
}
// recupero dati sulla planarità della polilinea
int nRank ;
Point3d ptCen ;
Vector3d vtDir ;
bool bFlat = shPL.IsFlat( nRank, ptCen, vtDir, dToler) ;
// se punto
switch ( nRank) {
case 0 : // punto
if ( bFlat)
plPlane.Set( ptCen, ( m_VtExtr.IsSmall() ? Z_AX : m_VtExtr)) ;
else
plPlane.Set( 0, ( m_VtExtr.IsSmall() ? V_NULL : m_VtExtr)) ;
break ;
case 1 : // linea
if ( m_VtExtr.IsSmall())
plPlane.Set( ptCen, FromUprightOrtho( vtDir)) ;
else {
Vector3d vtN = OrthoCompo( m_VtExtr, vtDir) ;
if ( ! vtN.Normalize())
vtN = FromUprightOrtho( vtDir) ;
plPlane.Set( ptCen, vtN) ;
bFlat = bFlat && ( ! bUseExtrusion || AreSameOrOppositeVectorApprox( m_VtExtr, vtN)) ;
}
break ;
default : // piana o 3d
if ( m_VtExtr.IsSmall())
plPlane.Set( ptCen, vtDir) ;
else {
plPlane.Set( ptCen, (( m_VtExtr * vtDir) > 0 ? vtDir : - vtDir)) ;
bFlat = bFlat && ( ! bUseExtrusion || AreSameOrOppositeVectorApprox( m_VtExtr, vtDir)) ;
}
break ;
}
return bFlat ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::IsClosed( void) const
{
// verifico lo stato
if ( m_nStatus != OK || m_CrvSmplS.empty())
return false ;
// ricavo punti iniziale e finale e li confronto
Point3d ptStart, ptEnd ;
return ( m_CrvSmplS.front()->GetStartPoint( ptStart) &&
m_CrvSmplS.back()->GetEndPoint( ptEnd) &&
AreSamePointApprox( ptStart, ptEnd)) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetStartPoint( Point3d& ptStart) const
{
// verifico lo stato
if ( m_nStatus != OK || m_CrvSmplS.empty())
return false ;
// assegno il punto
return m_CrvSmplS.front()->GetStartPoint( ptStart) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetEndPoint( Point3d& ptEnd) const
{
// verifico lo stato
if ( m_nStatus != OK || m_CrvSmplS.empty())
return false ;
// assegno il punto
return m_CrvSmplS.back()->GetEndPoint( ptEnd) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetMidPoint( Point3d& ptMid) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// determino il valore del parametro a metà lunghezza
double dLen, dMid ;
if ( ! GetLength( dLen) || ! GetParamAtLength( 0.5 * dLen, dMid))
return false ;
// calcolo il punto
return GetPointD1D2( dMid, FROM_MINUS, ptMid) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetCentroid( Point3d& ptCen) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// approssimo la curva con una polilinea
PolyLine PL ;
if ( ! ApproxWithLines( LIN_TOL_STD, ANG_TOL_STD_DEG, APL_SPECIAL_INT, PL))
return false ;
// calcolo il centro mediante PolygonPlane
Point3d ptP ;
PolygonPlane PolyPlane ;
for ( bool bFound = PL.GetFirstPoint( ptP) ; bFound ; bFound = PL.GetNextPoint( ptP))
PolyPlane.AddPoint( ptP) ;
if ( PolyPlane.GetCentroid( ptCen))
return true ;
// se non riuscito, uso il punto medio
return GetMidPoint( ptCen) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetStartDir( Vector3d& vtDir) const
{
// verifico lo stato
if ( m_nStatus != OK || m_CrvSmplS.empty())
return false ;
// assegno il punto
return m_CrvSmplS.front()->GetStartDir( vtDir) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetEndDir( Vector3d& vtDir) const
{
// verifico lo stato
if ( m_nStatus != OK || m_CrvSmplS.empty())
return false ;
// assegno il punto
return m_CrvSmplS.back()->GetEndDir( vtDir) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetMidDir( Vector3d& vtDir) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// determino il valore del parametro a metà lunghezza
double dLen, dMid ;
if ( ! GetLength( dLen) || ! GetParamAtLength( 0.5 * dLen, dMid))
return false ;
// calcolo la direzione
return ::GetTang( *this, dMid, FROM_MINUS, vtDir) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetDomain( double& dStart, double& dEnd) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// assegno gli estremi del dominio
dStart = 0.0 ;
dEnd = double( m_CrvSmplS.size()) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetIndSCurveAndLocPar( double dU, Side nS, int& nSCrv, double& dLocU) const
{
// deve esserci almeno una curva semplice
if ( m_CrvSmplS.empty())
return false ;
// valore massimo del parametro
double dMaxU = double( m_CrvSmplS.size()) ;
// verifico che il parametro non sia troppo fuori dai limiti
if ( dU < - 100 * EPS_PARAM || dU > dMaxU + 100 * EPS_PARAM)
return false ;
// il parametro U deve essere compreso tra 0 e m_nCounter (le curve chiuse sono cicliche)
if ( dU < ( 0 + EPS_PARAM)) {
// se chiusa e voglio valutare prima dell'inizio devo valutare prima della fine
if ( IsClosed() && nS == ICurve::FROM_MINUS)
dU = dMaxU ;
// altrimenti posso valutare solo dopo l'inizio
else {
dU = 0 ;
nS = ICurve::FROM_PLUS ;
}
}
else if ( dU > ( dMaxU - EPS_PARAM)) {
// se chiusa e voglio valutare dopo la fine devo valutare dopo l'inizio
if ( IsClosed() && nS == ICurve::FROM_PLUS)
dU = 0 ;
// altrimenti posso valutare solo prima della fine
else {
dU = dMaxU ;
nS = ICurve::FROM_MINUS ;
}
}
// determino la curva di appartenenza e il valore locale (ovvero nella curva) del parametro
nSCrv = static_cast<int>( dU) ;
dLocU = dU - nSCrv ;
if ( abs( dLocU) < 5 * EPS_PARAM && nSCrv > 0 && nS == ICurve::FROM_MINUS) {
-- nSCrv ;
dLocU = 1 ;
}
else if ( abs( dLocU) > 1 - 5 * EPS_PARAM && nSCrv < dMaxU - 1 && nS == ICurve::FROM_PLUS) {
++ nSCrv ;
dLocU = 0 ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetPointD1D2( double dU, Side nS, Point3d& ptPos, Vector3d* pvtDer1, Vector3d* pvtDer2) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// determino la curva di appartenenza e il valore locale del parametro
int nC ;
double dUc ;
if ( ! GetIndSCurveAndLocPar( dU, nS, nC, dUc))
return false ;
// eseguo il calcolo sulla curva semplice
return m_CrvSmplS[nC]->GetPointD1D2( dUc, ICurve::FROM_MINUS, ptPos, pvtDer1, pvtDer2) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetApproxLength( double& dLen) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// ciclo di calcolo
dLen = 0 ;
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++Iter) {
double dLenCrvSmpl ;
if ( (*Iter)->GetType() == CRV_BEZIER) {
if ( GetBasicCurveBezier(*Iter)->GetApproxLength( dLenCrvSmpl))
dLen += dLenCrvSmpl ;
else
return false ;
}
else {
if ( (*Iter)->GetLength( dLenCrvSmpl))
dLen += dLenCrvSmpl ;
else
return false ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetLength( double& dLen) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// ciclo di calcolo
dLen = 0 ;
for ( auto Iter = m_CrvSmplS.cbegin() ; Iter != m_CrvSmplS.cend() ; ++Iter) {
double dLenCrvSmpl ;
if ( (*Iter)->GetLength( dLenCrvSmpl))
dLen += dLenCrvSmpl ;
else
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetLengthAtParam( double dU, double& dLen) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// il parametro U deve essere compreso tra 0 e m_nCounter
if ( dU < - EPS_PARAM || dU > ( double( m_CrvSmplS.size()) + EPS_PARAM))
return false ;
// ciclo di calcolo
dLen = 0 ;
double dUToGo = dU ;
for ( auto Iter = m_CrvSmplS.cbegin() ; Iter != m_CrvSmplS.cend() ; ++Iter) {
// dominio parametrico della curva semplice
double dParStart, dParEnd ;
(*Iter)->GetDomain( dParStart, dParEnd) ;
// se resto nella curva
if ( dUToGo <= ( dParEnd - dParStart)) {
double dParamLen ;
if ( ! (*Iter)->GetLengthAtParam( dUToGo, dParamLen))
return false ;
dLen += dParamLen ;
break ;
}
// altrimenti
else {
// lunghezza parametrica rimanente
dUToGo -= ( dParEnd - dParStart) ;
// lunghezza progressiva
double dCrvLen ;
if ( ! (*Iter)->GetLength( dCrvLen))
return false ;
dLen += dCrvLen ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetParamAtLength( double dLen, double& dU) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// se prima di inizio, errore
if ( dLen < - EPS_SMALL)
return false ;
// inizio
if ( dLen < EPS_SMALL) {
dU = 0 ;
return true ;
}
// ciclo di calcolo
dU = 0 ;
double dLenToGo = dLen ;
for ( auto Iter = m_CrvSmplS.cbegin() ; Iter != m_CrvSmplS.cend() ; ++Iter) {
// lunghezza della curva semplice
double dCrvLen ;
if ( ! (*Iter)->GetLength( dCrvLen))
return false ;
// se resto nella curva
if ( dLenToGo <= dCrvLen) {
double dULen ;
if ( ! (*Iter)->GetParamAtLength( dLenToGo, dULen))
return false ;
dU += dULen ;
return true ;
}
// altrimenti
else {
// lunghezza rimanente
dLenToGo -= dCrvLen ;
// lunghezza parametrica progressiva
double dParStart, dParEnd ;
(*Iter)->GetDomain( dParStart, dParEnd) ;
dU += ( dParEnd - dParStart) ;
}
}
return false ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::IsPointOn( const Point3d& ptP, double dTol) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// verifico che il punto sia sulla curva ( distanza minore di tolleranza)
return ( DistPointCrvComposite( ptP, *this).IsEpsilon( dTol)) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetParamAtPoint( const Point3d& ptP, double& dPar, double dTol) const
{
DistPointCrvComposite DPC( ptP, *this) ;
if ( ! DPC.IsEpsilon( dTol))
return false ;
int nFlag ;
return DPC.GetParamAtMinDistPoint( 0, dPar, nFlag) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetLengthAtPoint( const Point3d& ptP, double& dLen, double dTol) const
{
double dU ;
if ( ! GetParamAtPoint( ptP, dU, dTol))
return false ;
return GetLengthAtParam( dU, dLen) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLine& PL) const
{
// pulisco la polilinea
PL.Clear() ;
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// limiti minimi su tolleranza e deviazione angolare
dLinTol = max( dLinTol, LIN_TOL_MIN) ;
dAngTolDeg = max( dAngTolDeg, ANG_TOL_MIN_DEG) ;
// se speciale, approssimo ogni singola entità e conservo le estremità interne (joint)
if ( nType == APL_SPECIAL || nType == APL_SPECIAL_INT) {
// eseguo approssimazione
double dStartPar = 0 ;
for ( auto& pCrv : m_CrvSmplS) {
// assegno estrusione e spessore della curva composita
pCrv->SetExtrusion( m_VtExtr) ;
pCrv->SetThickness( m_dThick) ;
// recupero approssimazione per curva semplice
PolyLine PLSmpl ;
if ( ! pCrv->ApproxWithLines( dLinTol, dAngTolDeg, nType, PLSmpl))
return false ;
// se richiesto almeno un punto interno con curve non rettilinee e ci sono solo gli estremi
if ( nType == APL_SPECIAL_INT && pCrv->GetType() != CRV_LINE && PLSmpl.GetPointNbr() == 2) {
// aggiungo il punto interno
Point3d ptMid ;
if ( ! pCrv->GetMidPoint( ptMid))
return false ;
double dU ;
PLSmpl.GetLastU( dU) ;
dU /= 2 ;
PNTULIST& List = PLSmpl.GetUPointList() ;
List.insert( ++ List.begin(), { ptMid, dU}) ;
}
// ripristino estrusione e spessore della curva semplice (annullandoli)
pCrv->SetExtrusion( V_NULL) ;
pCrv->SetThickness( 0) ;
// la accodo opportunamente a quella della curva composita
if ( ! PL.Join( PLSmpl, dStartPar))
return false ;
// incremento inizio parametro per prossima curva semplice
dStartPar += 1 ;
}
return true ;
}
// se lineare con lato obbligato...
if ( nType == APL_LEFT || nType == APL_LEFT_CONVEX ||
nType == APL_RIGHT || nType == APL_RIGHT_CONVEX) {
// prima approssimazione lineare alla tolleranza minima del programma
if ( ! ApproxWithLines( EPS_SMALL, dAngTolDeg, APL_SPECIAL, PL))
return false ;
// eliminazione dei punti in tolleranza andando solo dalla parte ammessa
Vector3d vtExtr = ( m_VtExtr.IsSmall() ? Z_AX : m_VtExtr) ;
if ( ! PL.ApproxOnSide( vtExtr, ( nType == APL_LEFT || nType == APL_LEFT_CONVEX), dLinTol))
return false ;
// se necessario, sistemo per convessità dalla parte ammessa
if ( nType == APL_RIGHT_CONVEX || nType == APL_LEFT_CONVEX) {
if ( ! PL.MakeConvex( vtExtr, ( nType == APL_LEFT_CONVEX)))
return false ;
}
return true ;
}
// altrimenti standard
// prima approssimazione lineare a 10 * Epsilon di ogni singola entità
if ( ! ApproxWithLines( 10 * EPS_SMALL, dAngTolDeg, APL_SPECIAL, PL))
return false ;
// eliminazione dei punti in tolleranza
return PL.RemoveAlignedPoints( dLinTol) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const
{
// pulisco il poliarco
PA.Clear() ;
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// determino riferimento naturale della curva in base all'estrusione o al piano medio se questa è nulla
Frame3d frNat ;
if ( ! m_VtExtr.IsSmall()) {
frNat.Set( ORIG, m_VtExtr) ;
}
else {
Plane3d plPlane ;
IsFlat( plPlane, false) ;
if ( plPlane.IsValid()) {
if ( plPlane.GetVersN().z < -EPS_ZERO)
plPlane.Invert() ;
frNat.Set( ORIG, plPlane.GetVersN()) ;
}
}
// eseguo approssimazione
double dStartPar = 0 ;
for ( const auto& pCrv : m_CrvSmplS) {
// ne faccio una copia
PtrOwner<ICurve> pCrvL( pCrv->Clone()) ;
if ( IsNull( pCrvL))
return false ;
// assegno estrusione e spessore della curva composita
pCrvL->SetExtrusion( m_VtExtr) ;
pCrvL->SetThickness( m_dThick) ;
// la porto nel riferimento naturale
pCrvL->ToLoc( frNat) ;
// recupero approssimazione per curva semplice
PolyArc PASmpl ;
if ( ! pCrvL->ApproxWithArcs( dLinTol, dAngTolDeg, PASmpl))
return false ;
// la accodo opportunamente a quella della curva composita
if ( ! PA.Join( PASmpl, dStartPar))
return false ;
// incremento inizio parametro per prossima curva semplice
dStartPar += 1 ;
}
// riporto l'approssimazione nel riferimento della composita
PA.ToGlob( frNat) ;
// assegno estrusione della curva composita
PA.SetExtrusion( m_VtExtr) ;
// eliminazione dei punti in tolleranza (opportunamente diminuita)
return PA.RemoveAlignedPoints( 0.5 * dLinTol) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ApproxWithArcsEx( double dLinTol, double dAngTolDeg, double dLinFea, PolyArc& PA) const
{
// pulisco il poliarco
PA.Clear() ;
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// preparazione per approssimazione di multilinee
bool bMultiLine = false ;
double dMlStartPar = 0 ;
CurveByApprox crvByApprox ;
// determino riferimento naturale della curva in base all'estrusione o al piano medio se questa è nulla
Frame3d frNat ;
if ( ! m_VtExtr.IsSmall()) {
frNat.Set( ORIG, m_VtExtr) ;
}
else {
Plane3d plPlane ;
IsFlat( plPlane, false) ;
if ( plPlane.IsValid()) {
if ( plPlane.GetVersN().z < -EPS_ZERO)
plPlane.Invert() ;
frNat.Set( ORIG, plPlane.GetVersN()) ;
}
}
// eseguo approssimazione
double dStartPar = 0 ;
for ( const auto& pCrv : m_CrvSmplS) {
// ne faccio una copia
PtrOwner<ICurve> pCrvL( pCrv->Clone()) ;
if ( IsNull( pCrvL))
return false ;
// assegno estrusione e spessore della curva composita
pCrvL->SetExtrusion( m_VtExtr) ;
pCrvL->SetThickness( m_dThick) ;
// la porto nel riferimento naturale
pCrvL->ToLoc( frNat) ;
// se segmento di linea non feature
double dLen ;
if ( pCrvL->GetType() == CRV_LINE && pCrvL->GetLength( dLen) && dLen < dLinFea) {
CurveLine* pLine = GetBasicCurveLine( pCrvL) ;
// se inizio di approx multilinea
if ( ! bMultiLine) {
bMultiLine = true ;
dMlStartPar = dStartPar ;
crvByApprox.Reset() ;
crvByApprox.AddPoint( pLine->GetStart()) ;
}
// aggiungo il punto finale
crvByApprox.AddPoint( pLine->GetEnd()) ;
}
// se altrimenti arco di circonferenza o curva di Bezier non feature
else if ( ( pCrvL->GetType() == CRV_ARC || pCrvL->GetType() == CRV_BEZIER) &&
pCrvL->GetLength( dLen) && dLen < dLinFea) {
// se inizio di approx multilinea
if ( ! bMultiLine) {
bMultiLine = true ;
dMlStartPar = dStartPar ;
crvByApprox.Reset() ;
Point3d ptStart ;
if ( ! pCrvL->GetStartPoint( ptStart))
return false ;
crvByApprox.AddPoint( ptStart) ;
}
// aggiungo i punti opportunamente campionati sulla curva (compreso il finale)
PolyLine PL ;
if ( ! pCrvL->ApproxWithLines( dLinTol / 2, dAngTolDeg / 2, ICurve::APL_STD, PL))
return false ;
Point3d ptFin ;
PL.GetFirstPoint( ptFin) ;
while ( PL.GetNextPoint( ptFin))
crvByApprox.AddPoint( ptFin) ;
}
// altrimenti
else {
// se in corso approx multilinee
if ( bMultiLine) {
bMultiLine = false ;
PolyArc PASmpl ;
if ( ! crvByApprox.GetArcsCorner( dLinTol, dAngTolDeg, dLinFea, PASmpl))
return false ;
// la accodo opportunamente a quella della curva composita
if ( ! PA.Join( PASmpl, dMlStartPar))
return false ;
}
// recupero approssimazione per curva semplice
PolyArc PASmpl ;
if ( ! pCrvL->ApproxWithArcs( dLinTol, dAngTolDeg, PASmpl))
return false ;
// la accodo opportunamente a quella della curva composita
if ( ! PA.Join( PASmpl, dStartPar))
return false ;
}
// incremento inizio parametro per prossima curva semplice
dStartPar += 1 ;
}
// se approssimazione multiline ancora aperta
if ( bMultiLine) {
bMultiLine = false ;
PolyArc PASmpl ;
if ( ! crvByApprox.GetArcsCorner( dLinTol, dAngTolDeg, dLinFea, PASmpl))
return false ;
// la accodo opportunamente a quella della curva composita
if ( ! PA.Join( PASmpl, dMlStartPar))
return false ;
}
// riporto l'approssimazione nel riferimento della composita
PA.ToGlob( frNat) ;
// assegno estrusione della curva composita
PA.SetExtrusion( m_VtExtr) ;
// eliminazione dei punti in tolleranza (opportunamente diminuita)
return PA.RemoveAlignedPoints( 0.5 * dLinTol) ;
}
//----------------------------------------------------------------------------
ICurve*
CurveComposite::CopyParamRange( double dUStart, double dUEnd) const
{
// valore massimo del parametro
double dMaxU = double( m_CrvSmplS.size()) ;
// i parametri start ed end devono essere compresi nel dominio parametrico della curva
if ( dUStart < - EPS_PARAM || dUStart > dMaxU + EPS_PARAM ||
dUEnd < - EPS_PARAM || dUEnd > dMaxU + EPS_PARAM)
return nullptr ;
// se i parametri coincidono, non resta alcunchè
if ( abs( dUEnd - dUStart) < EPS_PARAM)
return nullptr ;
// se il parametro start supera quello di end
if ( dUStart > dUEnd - EPS_PARAM) {
// se curva aperta, il trim la cancella completamente quindi non resta alcunchè
if ( ! IsClosed())
return nullptr ;
// se curva chiusa, il trim si avvolge attorno al punto di giunzione
else
// incremento il parametro finale della dimensione del dominio parametrico originale della curva
dUEnd += dMaxU ;
}
// determino gli indici della prima e dell'ultima curva da copiare
int nIdStart = static_cast<int>( floor( dUStart)) ;
int nIdEnd = static_cast<int>( ceil( dUEnd)) ;
// creo la curva composita copia
PtrOwner<CurveComposite> pCopy( new( nothrow) CurveComposite()) ;
if ( IsNull( pCopy))
return nullptr ;
// eseguo la copia delle sole curve semplici necessarie
for ( int i = nIdStart ; i < nIdEnd ; ++ i) {
// quando supero il numero di curve nella composita originale riparto dall'inizio
int j = i % m_CrvSmplS.size() ;
// accodo la curva alla copia
if ( ! pCopy->AddCurve( *(m_CrvSmplS[j])))
return nullptr ;
}
// aggiorno i parametri di trim, tenendo conto delle curve semplici saltate all'inizio
dUStart -= nIdStart ;
dUEnd -= nIdStart ;
// eseguo il trim della copia
if ( ! pCopy->TrimStartEndAtParam( dUStart, dUEnd))
return nullptr ;
return ( pCopy->m_CrvSmplS.empty() ? nullptr : ::Release( pCopy)) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Invert( void)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// inverto le singole curve
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++Iter)
(*Iter)->Invert() ;
// inverto l'ordine della lista
reverse( m_CrvSmplS.begin(), m_CrvSmplS.end()) ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::SimpleOffset( double dDist, int nType, double dMaxAngExt)
{
// se distanza di offset nulla, non devo fare alcunché
if ( abs( dDist) < EPS_SMALL)
return true ;
// --- l'offset va effettuato in un piano perpendicolare al vettore estrusione ---
// verifico il vettore estrusione
bool bNeedRef = ( ! m_VtExtr.IsSmall() && ! m_VtExtr.IsZplus()) ;
// se necessario cambio il riferimento
Frame3d frExtr ;
if ( bNeedRef) {
// calcolo il riferimento OCS con VtExtr come asse Z
if ( ! frExtr.Set( ORIG, m_VtExtr))
return false ;
// esprimo la curva in questo riferimento
ToLoc( frExtr) ;
}
// eseguo l'offset nel piano XY
bool bOk = SimpleOffsetXY( dDist, nType, dMaxAngExt) ;
// riporto la curva nel riferimento originale
if ( bNeedRef)
ToGlob( frExtr) ;
return bOk ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ModifyStart( const Point3d& ptNewStart)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// recupero la prima curva
if ( m_CrvSmplS.empty())
return false ;
ICurve* pCrv = m_CrvSmplS.front() ;
// modifico l'inizio di questa curva
if ( ! pCrv->ModifyStart( ptNewStart))
return false ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ModifyEnd( const Point3d& ptNewEnd)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// recupero l'ultima curva
if ( m_CrvSmplS.empty())
return false ;
ICurve* pCrv = m_CrvSmplS.back() ;
// modifico la fine di questa curva
if ( ! pCrv->ModifyEnd( ptNewEnd))
return false ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddPoint( const Point3d& ptStart)
{
// verifico lo stato
if ( m_nStatus != TO_VERIFY && m_nStatus != IS_A_POINT)
return false ;
// assegno il punto e setto lo stato
m_ptStart = ptStart ;
m_nStatus = IS_A_POINT ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddLineTg( double dLen, bool bEndOrStart)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// costruisco la linea
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
if ( IsNull( pLine))
return false ;
// se da aggiungere alla fine
if ( bEndOrStart) {
Point3d ptEnd ;
Vector3d vtEnd ;
if ( ! GetEndPoint( ptEnd) || ! GetEndDir( vtEnd))
return false ;
Point3d ptNew = ptEnd + vtEnd * dLen ;
if ( ! pLine->Set( ptEnd, ptNew))
return false ;
}
// altrimenti da aggiungere all'inizio
else {
Point3d ptStart ;
Vector3d vtStart ;
if ( ! GetStartPoint( ptStart) || ! GetStartDir( vtStart))
return false ;
Point3d ptNew = ptStart - vtStart * dLen ;
if ( ! pLine->Set( ptNew, ptStart))
return false ;
}
return AddCurve( Release( pLine), bEndOrStart) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddLine( const Point3d& ptNew, bool bEndOrStart)
{
// verifico lo stato
if ( m_nStatus != OK && m_nStatus != IS_A_POINT)
return false ;
// costruisco la linea
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
if ( IsNull( pLine))
return false ;
// se da aggiungere alla fine
if ( bEndOrStart) {
Point3d ptEnd = m_ptStart ;
if ( m_nStatus == OK)
GetEndPoint( ptEnd) ;
if ( ! pLine->Set( ptEnd, ptNew))
return false ;
}
// altrimenti da aggiungere all'inizio
else {
Point3d ptStart = m_ptStart ;
if ( m_nStatus == OK)
GetStartPoint( ptStart) ;
if ( ! pLine->Set( ptNew, ptStart))
return false ;
}
return AddCurve( Release( pLine), bEndOrStart) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddArcTg( const Point3d& ptNew, bool bEndOrStart)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// recupero il versore normale al piano ( estrusione oppure se nulla asse Z locale)
Vector3d vtN = ( m_VtExtr.IsSmall() ? Z_AX : m_VtExtr) ;
// costruisco l'arco (in casi articolari può essere una linea)
PtrOwner<ICurve> pCrv ;
// se da aggiungere alla fine
if ( bEndOrStart) {
Point3d ptEnd ;
GetEndPoint( ptEnd) ;
Vector3d vtDir ;
GetEndDir( vtDir) ;
pCrv.Set( GetArc2PVN( ptEnd, ptNew, vtDir, vtN)) ;
if ( IsNull( pCrv))
return false ;
}
// altrimenti da aggiungere all'inizio
else {
Point3d ptStart ;
GetStartPoint( ptStart) ;
Vector3d vtDir ;
GetStartDir( vtDir) ;
pCrv.Set( GetArc2PVN( ptStart, ptNew, -vtDir, vtN)) ;
if ( IsNull( pCrv))
return false ;
pCrv->Invert() ;
}
return AddCurve( Release( pCrv), bEndOrStart) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddArc2P( const Point3d& ptOther, const Point3d& ptNew, bool bEndOrStart)
{
// verifico lo stato
if ( m_nStatus != OK && m_nStatus != TO_VERIFY)
return false ;
// costruisco l'arco (in casi articolari può essere una linea)
PtrOwner<ICurve> pCrv ;
// se da aggiungere alla fine
if ( bEndOrStart) {
Point3d ptEnd = m_ptStart ;
if ( m_nStatus == OK)
GetEndPoint( ptEnd) ;
pCrv.Set( GetArc3P( ptEnd, ptOther, ptNew, false)) ;
if ( IsNull( pCrv))
return false ;
}
// altrimenti da aggiungere all'inizio
else {
Point3d ptStart = m_ptStart ;
if ( m_nStatus == OK)
GetStartPoint( ptStart) ;
pCrv.Set( GetArc3P( ptNew, ptOther, ptStart, false)) ;
if ( IsNull( pCrv))
return false ;
}
return AddCurve( Release( pCrv), bEndOrStart) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddJoint( double dU)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// recupero la curva in cui cade il parametro
int nSCrv ;
double dLocU ;
if ( ! GetIndSCurveAndLocPar( dU, FROM_MINUS, nSCrv, dLocU))
return false ;
ICurve* pCurve = m_CrvSmplS[ nSCrv] ;
// creo due copie della curva
PtrOwner<ICurve> pCrv1( pCurve->Clone()) ;
PtrOwner<ICurve> pCrv2( pCurve->Clone()) ;
if ( IsNull( pCrv1) || IsNull( pCrv2))
return false ;
// della prima curva tengo la parte dall'inizio al parametro, della seconda la rimanente
// ( se non riesco a trimmare, la nuova giunzione coincide con una già esistente, esco con successo)
if ( ! pCrv1->TrimEndAtParam( dLocU) || ! pCrv2->TrimStartAtParam( dLocU))
return true ;
// elimino la curva originale
delete pCurve ;
// devo ora inserire le due curve al posto di quella originale
m_CrvSmplS.insert( m_CrvSmplS.begin() + nSCrv, Release(pCrv1)) ;
m_CrvSmplS[ nSCrv+1] = Release( pCrv2) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ModifyJoint( int nU, const Point3d& ptNewJoint, double dTol)
{
int nCrvCount = GetCurveCount() ;
// verifico l'indice della giunzione
if ( nU < 0 || nU > nCrvCount)
return false ;
// salvo le vecchie curve e nel caso le ripristino
int nPrevCrv = -1 ;
// recupero l'indice e il puntatore alla curva precedente (se esiste)
if ( nU >= 0)
nPrevCrv = nU - 1 ;
else if ( IsClosed())
nPrevCrv = nCrvCount - 1 ;
PtrOwner<CurveComposite> pOrigCrv( CreateBasicCurveComposite()) ;
if ( nPrevCrv >= 0)
pOrigCrv->AddCurve( m_CrvSmplS[ nPrevCrv]->Clone()) ;
// recupero il puntatore alla curva successiva (se esiste)
int nNextCrv = -1 ;
if ( nU < nCrvCount)
nNextCrv = nU ;
else if ( IsClosed())
nNextCrv = 0 ;
else
nNextCrv = - 1 ;
if ( nNextCrv >= 0)
pOrigCrv->AddCurve( m_CrvSmplS[ nNextCrv]->Clone()) ;
int nCrvNmbr = GetCurveCount() ;
int nFlagDel = DeletedCurve::NONE ;
if ( ! ModifyJoint( nU, ptNewJoint, &nFlagDel))
return false ;
bool bErasedSomeCrv = nCrvCount > GetCurveCount() ;
bool bErasedPrev = ( nFlagDel == DeletedCurve::PREV) ;
bool bErasedNext = ( nFlagDel == DeletedCurve::NEXT) ;
if ( ( bErasedPrev && nNextCrv == -1) || ( bErasedNext && nPrevCrv == -1)) {
// se sono su un estremo di una curva aperta e ho cancellato la sottocurva di estremità devo verificare che fosse più piccola della tolleranza
if ( bErasedPrev && nNextCrv == -1) {
Point3d ptOrigEnd ; pOrigCrv->GetEndPoint( ptOrigEnd) ;
Point3d ptNewEnd ; GetEndPoint( ptNewEnd) ;
if ( Dist( ptOrigEnd, ptNewEnd) > dTol)
m_CrvSmplS.push_back( Release( pOrigCrv)) ;
return true ;
}
if ( bErasedNext && nPrevCrv == -1) {
Point3d ptOrigStart ; pOrigCrv->GetStartPoint( ptOrigStart) ;
Point3d ptNewStart ; GetStartPoint( ptNewStart) ;
if ( Dist( ptOrigStart, ptNewStart) > dTol)
m_CrvSmplS.insert( m_CrvSmplS.begin(), Release( pOrigCrv)) ;
return true ;
}
}
double dStart ;
double dEnd ;
if ( bErasedPrev) {
dStart = nU ;
dEnd = nNextCrv + 1 ;
}
else if ( bErasedNext) {
dStart = nPrevCrv ;
dEnd = nU ;
if ( nU == 0)
dStart -= 1 ;
}
else { // ! bErasedSomeCrv
dStart = ( nPrevCrv != -1 ? nPrevCrv : 0) ;
dEnd = ( nNextCrv != -1 ? nNextCrv + 1 : nCrvNmbr) ;
}
PtrOwner<ICurve> pNewCurve( CopyParamRange( dStart, dEnd)) ;
double dErr = 0 ;
if ( ! CalcApproxError( pOrigCrv, pNewCurve, dErr, 6) || dErr > dTol) {
// se ho fallito il check o la variazione è superiore alla tolleranza richiesta, ripristino le curve originali
if ( ! bErasedSomeCrv) {
if ( nNextCrv != -1) {
delete m_CrvSmplS[nNextCrv] ;
m_CrvSmplS[nNextCrv] = pOrigCrv->RemoveFirstOrLastCurve( true) ;
}
if ( nPrevCrv != -1) {
delete m_CrvSmplS[nPrevCrv] ;
m_CrvSmplS[nPrevCrv] = pOrigCrv->RemoveFirstOrLastCurve( true) ;
}
}
else {
if ( bErasedNext) {
int nPos = nU == 0 ? nPrevCrv - 1 : nU ;
delete m_CrvSmplS[nPos] ;
if ( nU == 0) {
m_CrvSmplS[nPos] = pOrigCrv->RemoveFirstOrLastCurve( false) ;
nPos = 0 ;
}
else
m_CrvSmplS[nPos] = pOrigCrv->RemoveFirstOrLastCurve( true) ;
m_CrvSmplS.insert( m_CrvSmplS.begin() + nPos, pOrigCrv->RemoveFirstOrLastCurve( true)) ;
}
else {
int nPos = nU == 0 ? nU : nPrevCrv ;
delete m_CrvSmplS[nPos] ;
if ( nU == 0) {
m_CrvSmplS[nPos] = pOrigCrv->RemoveFirstOrLastCurve( true) ;
nPos = nCrvNmbr - 1 ;
}
else
m_CrvSmplS[nPos] = pOrigCrv->RemoveFirstOrLastCurve( true) ;
m_CrvSmplS.insert( m_CrvSmplS.begin() + nPos, pOrigCrv->RemoveFirstOrLastCurve( true)) ;
}
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ModifyJoint( int nU, const Point3d& ptNewJoint, int* pnFlagDel)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// numero di curve
int nCrvCount = GetCurveCount() ;
// verifico l'indice della giunzione
if ( nU < 0 || nU > nCrvCount)
return false ;
if ( pnFlagDel != nullptr)
*pnFlagDel = DeletedCurve::NONE ;
// recupero l'indice e il puntatore alla curva precedente (se esiste)
int nPrevCrv = -1 ;
if ( nU > 0)
nPrevCrv = nU - 1 ;
else if ( IsClosed())
nPrevCrv = nCrvCount - 1 ;
ICurve* pPrevCrv = ( nPrevCrv != -1 ? m_CrvSmplS[ nPrevCrv] : nullptr) ;
// recupero il puntatore alla curva successiva (se esiste)
int nNextCrv = -1 ;
if ( nU < nCrvCount)
nNextCrv = nU ;
else if ( IsClosed())
nNextCrv = 0 ;
ICurve* pNextCrv = ( nNextCrv != -1 ? m_CrvSmplS[ nNextCrv] : nullptr) ;
// recupero punto iniziale dell'entità precedente (se esiste)
Point3d ptStart ;
if ( pPrevCrv != nullptr && ! pPrevCrv->GetStartPoint( ptStart))
return false ;
// recupero punto finale dell'entità successiva (se esiste)
Point3d ptEnd ;
if ( pNextCrv != nullptr && ! pNextCrv->GetEndPoint( ptEnd))
return false ;
// modifico il punto finale dell'eventuale entità precedente
if ( pPrevCrv != nullptr && ! pPrevCrv->ModifyEnd( ptNewJoint)) {
// se entità precedente si annulla, la elimino
if ( AreSamePointApprox( ptStart, ptNewJoint)) {
delete pPrevCrv ;
m_CrvSmplS.erase( m_CrvSmplS.begin() + nPrevCrv) ;
if ( pnFlagDel != nullptr)
*pnFlagDel = DeletedCurve::PREV ;
}
// altrimenti diventa un segmento di retta
else {
// creo la linea
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
if ( IsNull( pLine) || ! pLine->Set( ptStart, ptNewJoint))
return false ;
// sostituisco la linea alla curva precedente
m_CrvSmplS[nPrevCrv] = Release( pLine) ;
// elimino la curva originale
delete( pPrevCrv) ;
}
}
// modifico il punto iniziale dell'eventuale entità successiva
if ( pNextCrv != nullptr && ! pNextCrv->ModifyStart( ptNewJoint)) {
// se entità successiva si annulla, la elimino
if ( AreSamePointApprox( ptNewJoint, ptEnd)) {
delete pNextCrv ;
m_CrvSmplS.erase( m_CrvSmplS.begin() + nNextCrv) ;
if ( pnFlagDel != nullptr)
*pnFlagDel = DeletedCurve::NEXT ;
}
// altrimenti diventa un segmento di retta
else {
// creo la linea
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
if ( IsNull( pLine) || ! pLine->Set( ptNewJoint, ptEnd))
return false ;
// sostituisco la linea alla curva precedente
m_CrvSmplS[nNextCrv] = Release( pLine) ;
// elimino la curva originale
delete( pNextCrv) ;
}
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::RemoveJoint( int nU)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// numero di curve
int nCrvCount = GetCurveCount() ;
// verifico l'indice della giunzione
if ( nU < 0 || nU > nCrvCount)
return false ;
// devono esserci almeno 2 curve se composita aperta e almeno 3 se chiusa
if ( nCrvCount < 2 || ( nCrvCount < 3 && IsClosed()))
return false ;
// recupero l'indice della curva precedente (se esiste)
int nPrevCrv = - 1 ;
if ( nU > 0)
nPrevCrv = nU - 1 ;
else if ( IsClosed())
nPrevCrv = nCrvCount - 1 ;
// recupero l'indice della curva successiva (se esiste)
int nNextCrv = - 1 ;
if ( nU < nCrvCount)
nNextCrv = nU ;
else if ( IsClosed())
nNextCrv = 0 ;
// se non esiste curva precedente, va eliminata la prima curva
if ( nPrevCrv == -1) {
// recupero la curva la cancello e la rimuovo
ICurve* pCrv = *(m_CrvSmplS.begin()) ;
delete pCrv ;
m_CrvSmplS.pop_front() ;
}
// se non esiste curva successiva, va eliminata l'ultima curva
else if ( nNextCrv == -1) {
// recupero la curva la cancello e la rimuovo
ICurve* pCrv = *(m_CrvSmplS.rbegin()) ;
delete pCrv ;
m_CrvSmplS.pop_back() ;
}
// altrimenti devo modificare la precedente ed eliminare la successiva
else {
// recupero punto finale della curva successiva
Point3d ptEnd ;
m_CrvSmplS[nNextCrv]->GetEndPoint( ptEnd) ;
// modifico punto finale della curva precedente
if ( ! m_CrvSmplS[nPrevCrv]->ModifyEnd( ptEnd))
return false ;
// recupero la curva successiva, la cancello e la rimuovo
ICurve* pCrv = *(m_CrvSmplS.begin() + nNextCrv) ;
delete pCrv ;
m_CrvSmplS.erase( m_CrvSmplS.begin() + nNextCrv) ;
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::MoveCurve( int nCrv, const Vector3d& vtMove)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// numero di curve
int nCrvCount = GetCurveCount() ;
// la curva deve esistere
if ( nCrv < 0 || nCrv > nCrvCount - 1)
return false ;
// recupero la curva corrente
ICurve* pCrv = *(m_CrvSmplS.begin() + nCrv) ;
// recupero l'indice e il puntatore alla curva precedente (se esiste)
int nPrevCrv = -1 ;
if ( nCrv > 0)
nPrevCrv = nCrv - 1 ;
else if ( IsClosed())
nPrevCrv = nCrvCount - 1 ;
ICurve* pPrevCrv = ( nPrevCrv != -1 ? m_CrvSmplS[ nPrevCrv] : nullptr) ;
// recupero il puntatore alla curva successiva (se esiste)
int nNextCrv = -1 ;
if ( nCrv + 1 < nCrvCount)
nNextCrv = nCrv + 1 ;
else if ( IsClosed())
nNextCrv = 0 ;
ICurve* pNextCrv = ( nNextCrv != -1 ? m_CrvSmplS[ nNextCrv] : nullptr) ;
// recupero punti iniziale e finale dell'entità precedente (se esiste)
Point3d ptPrevStart, ptPrevEnd ;
if ( pPrevCrv != nullptr && ( ! pPrevCrv->GetStartPoint( ptPrevStart) || ! pPrevCrv->GetEndPoint( ptPrevEnd)))
return false ;
// recupero punti iniziale e finale dell'entità successiva (se esiste)
Point3d ptNextStart, ptNextEnd ;
if ( pNextCrv != nullptr && ( ! pNextCrv->GetStartPoint( ptNextStart) || ! pNextCrv->GetEndPoint( ptNextEnd)))
return false ;
// modifico il punto finale dell'eventuale entità precedente
if ( pPrevCrv != nullptr && ! pPrevCrv->ModifyEnd( ptPrevEnd + vtMove)) {
// se entità precedente si annulla, la elimino
if ( AreSamePointApprox( ptPrevStart, ptPrevEnd + vtMove)) {
delete pPrevCrv ;
m_CrvSmplS.erase( m_CrvSmplS.begin() + nPrevCrv) ;
if ( nPrevCrv < nNextCrv)
-- nNextCrv ;
}
// altrimenti diventa un segmento di retta
else {
// creo la linea
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
if ( IsNull( pLine) || ! pLine->Set( ptPrevStart, ptPrevEnd + vtMove))
return false ;
// sostituisco la linea alla curva precedente
m_CrvSmplS[nPrevCrv] = Release( pLine) ;
// elimino la curva originale
delete( pPrevCrv) ;
}
}
// modifico il punto iniziale dell'eventuale entità successiva
if ( pNextCrv != nullptr && ! pNextCrv->ModifyStart( ptNextStart + vtMove)) {
// se entità successiva si annulla, la elimino
if ( AreSamePointApprox( ptNextStart + vtMove, ptNextEnd)) {
delete pNextCrv ;
m_CrvSmplS.erase( m_CrvSmplS.begin() + nNextCrv) ;
}
// altrimenti diventa un segmento di retta
else {
// creo la linea
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
if ( IsNull( pLine) || ! pLine->Set( ptNextStart + vtMove, ptNextEnd))
return false ;
// sostituisco la linea alla curva precedente
m_CrvSmplS[nNextCrv] = Release( pLine) ;
// elimino la curva originale
delete( pNextCrv) ;
}
}
// traslo la curva corrente
pCrv->Translate( vtMove) ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ModifyCurveToArc( int nCrv, const Point3d& ptMid)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// numero di curve
int nCrvCount = GetCurveCount() ;
// la curva deve esistere
if ( nCrv < 0 || nCrv > nCrvCount - 1)
return false ;
// recupero la curva corrente
ICurve* pCrv = m_CrvSmplS[nCrv] ;
// recupero gli estremi
Point3d ptStart, ptEnd ;
if ( ! pCrv->GetStartPoint( ptStart) || ! pCrv->GetEndPoint( ptEnd))
return false ;
// creo l'arco
PtrOwner<CurveArc> pArc( CreateBasicCurveArc()) ;
if ( IsNull( pArc) || ! pArc->Set3P( ptStart, ptMid, ptEnd))
return false ;
// sostituisco l'arco alla curva precedente
m_CrvSmplS[nCrv] = Release( pArc) ;
// elimino la curva originale
delete( pCrv) ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ModifyCurveToLine( int nCrv)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// numero di curve
int nCrvCount = GetCurveCount() ;
// la curva deve esistere
if ( nCrv < 0 || nCrv > nCrvCount - 1)
return false ;
// recupero la curva corrente
ICurve* pCrv = m_CrvSmplS[nCrv] ;
// se già linea non devo fare alcunchè
if ( pCrv->GetType() == CRV_LINE)
return true ;
// recupero gli estremi
Point3d ptStart, ptEnd ;
if ( ! pCrv->GetStartPoint( ptStart) || ! pCrv->GetEndPoint( ptEnd))
return false ;
// creo la linea
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
if ( IsNull( pLine) || ! pLine->Set( ptStart, ptEnd))
return false ;
// sostituisco la linea alla curva precedente
m_CrvSmplS[nCrv] = Release( pLine) ;
// elimino la curva originale
delete( pCrv) ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::TrimStartAtParam( double dUTrim)
{
// verifico validità parametro
double dMaxU = double( m_CrvSmplS.size()) ;
if ( dUTrim < -EPS_PARAM || dUTrim > dMaxU - EPS_PARAM)
return false ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// ciclo sulle diverse curve dall'inizio
double dUToTrim = dUTrim ;
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ;) {
// dominio parametrico della curva semplice
double dParStart, dParEnd ;
(*Iter)->GetDomain( dParStart, dParEnd) ;
// lunghezza parametrica progressiva
dUToTrim -= ( dParEnd - dParStart) ;
// se lunghezza ancora da tagliare non nulla
if ( dUToTrim > EPS_PARAM) {
delete (*Iter) ;
Iter = m_CrvSmplS.erase( Iter) ;
}
// se lunghezza ancora da tagliare nulla (entro la tolleranza)
else if ( dUToTrim > - EPS_PARAM ||
! (*Iter)->TrimStartAtParam( 1 + dUToTrim)) {
delete (*Iter) ;
Iter = m_CrvSmplS.erase( Iter) ;
break ;
}
// altrimenti superata lunghezza ancora da tagliare (taglio già fatto al test sopra)
else {
break ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::TrimEndAtParam( double dUTrim)
{
// verifico validità parametro
double dMaxU = double( m_CrvSmplS.size()) ;
if ( dUTrim < EPS_PARAM || dUTrim > dMaxU + EPS_PARAM)
return false ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// ciclo sulle diverse curve dalla fine
bool bToErase = false ;
double dUToTrim = dUTrim ;
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ;) {
// dominio parametrico della curva semplice
double dParStart, dParEnd ;
(*Iter)->GetDomain( dParStart, dParEnd) ;
// lunghezza parametrica progressiva
dUToTrim -= ( dParEnd - dParStart) ;
// se da cancellare
if ( bToErase) {
// cancello l'entità, la tolgo dalla lista e passo alla successiva
delete (*Iter) ;
Iter = m_CrvSmplS.erase( Iter) ;
}
// se lunghezza parametrica ancora da tagliare non nulla
else if ( dUToTrim > EPS_PARAM) {
Iter ++ ;
}
// se lunghezza parametrica ancora da tagliare nulla (entro la tolleranza)
else if ( dUToTrim > - EPS_PARAM) {
// passo alla entità successiva
++ Iter ;
// dichiaro ingresso in zona da cancellare
bToErase = true ;
}
// altrimenti superata lunghezza parametrica ancora da tagliare
else {
// trimmo la curva semplice
if ( ! (*Iter)->TrimEndAtParam( 1 + dUToTrim)) {
// se trim fallito, rimaneva troppo poco e quindi la cancello
bToErase = true ;
continue ;
}
// passo alla entità successiva
++ Iter ;
// dichiaro ingresso in zona da cancellare
bToErase = true ;
}
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::TrimStartEndAtParam( double dUStartTrim, double dUEndTrim)
{
// valore massimo del parametro
double dMaxU = double( m_CrvSmplS.size()) ;
// i parametri start ed end devono essere compresi nel dominio parametrico della curva
if ( dUStartTrim < - EPS_PARAM || dUStartTrim > dMaxU + EPS_PARAM ||
dUEndTrim < - EPS_PARAM || dUEndTrim > dMaxU + EPS_PARAM)
return false ;
// se il parametro start supera quello di end
if ( dUStartTrim > dUEndTrim - EPS_PARAM) {
// se curva aperta, il trim la cancella completamente quindi errore
if ( ! IsClosed())
return false ;
// se curva chiusa, il trim interessa il punto di giunzione
else {
// calcolo il minimo numero di curve da accodare per coprire il range esteso
int nNumCrv = static_cast<int>( ceil( dUEndTrim)) ;
// incremento il parametro finale della dimensione del dominio parametrico originale della curva
dUEndTrim += dMaxU ;
// eseguo accodamento delle curve
for ( int i = 0 ; i < nNumCrv ; ++ i) {
if ( ! AddCurve( *(m_CrvSmplS[i])))
return false ;
}
}
}
// per parametro start : determino la curva di appartenenza e il valore locale del parametro
int nCs ;
double dUcs ;
if ( ! GetIndSCurveAndLocPar( dUStartTrim, ICurve::FROM_PLUS, nCs, dUcs))
return false ;
// per parametro end : determino la curva di appartenenza e il valore locale del parametro
int nCe ;
double dUce ;
if ( ! GetIndSCurveAndLocPar( dUEndTrim, ICurve::FROM_MINUS, nCe, dUce))
return false ;
// trim finale
if ( ! TrimEndAtParam( dUEndTrim))
return false ;
// se i due trim sono fatti sulla stessa curva devo compensare la riparametrizzazione dopo il primo
if ( nCs == nCe) {
double dNewUStartTrim = nCs + dUcs / dUce ;
return TrimStartAtParam( dNewUStartTrim) ;
}
// altrimenti, va bene il valore ricevuto come parametro
else
return TrimStartAtParam( dUStartTrim) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::TrimStartAtLen( double dLenTrim)
{
// verifico validità lunghezza risultante
if ( dLenTrim < EPS_ZERO)
return true ;
double dLen ;
if ( ! GetLength( dLen) || ( dLen - dLenTrim) < EPS_SMALL)
return false ;
// ciclo sulle diverse curve dall'inizio
double dLenToTrim = dLenTrim ;
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ;) {
// lunghezza della curva
double dCrvLen ;
if ( ! (*Iter)->GetLength( dCrvLen))
return false ;
// lunghezza progressiva
dLenToTrim -= dCrvLen ;
// se lunghezza ancora da tagliare non nulla
if ( dLenToTrim > EPS_SMALL) {
delete (*Iter) ;
Iter ++ ;
m_CrvSmplS.pop_front() ;
if ( m_CrvSmplS.empty())
return false ;
}
// se lunghezza ancora da tagliare nulla (entro la tolleranza)
else if ( dLenToTrim > - EPS_SMALL) {
delete (*Iter) ;
Iter ++ ;
m_CrvSmplS.pop_front() ;
if ( m_CrvSmplS.empty())
return false ;
break ;
}
// altrimenti superata lunghezza ancora da tagliare
else {
if ( ! (*Iter)->TrimStartAtLen( dCrvLen + dLenToTrim)) {
m_nStatus = ERR ;
return false ;
}
break ;
}
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::TrimEndAtLen( double dLenTrim)
{
// verifico validità lunghezza risultante
if ( dLenTrim < EPS_SMALL)
return false ;
double dLen ;
if ( ! GetLength( dLen))
return false ;
if ( ( dLen - dLenTrim) < EPS_ZERO)
return true ;
// ciclo sulle diverse curve dalla fine
bool bToErase = false ;
double dLenToTrim = dLenTrim ;
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ;) {
double dCrvLen = 0 ;
// se non sono già nella zona da cancellare, aggiorno lunghezze
if ( ! bToErase) {
// lunghezza della curva
if ( ! (*Iter)->GetLength( dCrvLen))
return false ;
// lunghezza progressiva
dLenToTrim -= dCrvLen ;
}
// se da cancellare
if ( bToErase) {
// cancello l'entità, la tolgo dalla lista e passo alla successiva
delete (*Iter) ;
Iter = m_CrvSmplS.erase( Iter) ;
}
// se lunghezza ancora da tagliare non nulla
else if ( dLenToTrim > EPS_SMALL) {
// passo alla entità successiva
++ Iter ;
}
// se lunghezza ancora da tagliare nulla (entro la tolleranza)
else if ( dLenToTrim > - EPS_SMALL) {
// passo alla entità successiva
++ Iter ;
// dichiaro ingresso in zona da cancellare
bToErase = true ;
}
// altrimenti superata lunghezza ancora da tagliare
else {
// trimmo la curva semplice
if ( ! (*Iter)->TrimEndAtLen( dCrvLen + dLenToTrim)) {
m_nStatus = ERR ;
return false ;
}
// passo alla entità successiva
++ Iter ;
// dichiaro ingresso in zona da cancellare
bToErase = true ;
}
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ExtendStartByLen( double dLenExt)
{
// la lunghezza di estensione deve essere positiva
if ( dLenExt < - EPS_ZERO)
return false ;
// recupero la prima curva ed estendo quella
if ( m_CrvSmplS.begin() == m_CrvSmplS.end())
return false ;
// estendo la prima curva
if ( ! (*m_CrvSmplS.begin())->ExtendStartByLen( dLenExt)) {
m_nStatus = ERR ;
return false ;
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ExtendEndByLen( double dLenExt)
{
// la lunghezza di estensione deve essere positiva
if ( dLenExt < - EPS_ZERO)
return false ;
// vado sulla fine
auto Iter = m_CrvSmplS.end() ;
if ( Iter == m_CrvSmplS.begin())
return false ;
-- Iter ;
// recupero la curva
if ( Iter == m_CrvSmplS.end())
return false ;
// estendo l'ultima curva
if ( ! (*Iter)->ExtendEndByLen( dLenExt)) {
m_nStatus = ERR ;
return false ;
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Translate( const Vector3d& vtMove)
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// traslo Voronoi
if ( m_pVoronoiObj != nullptr)
m_pVoronoiObj->Translate( vtMove) ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// traslo le singole curve
for ( auto& pCrv : m_CrvSmplS)
pCrv->Translate( vtMove) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, double dSinAng)
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità dell'asse di rotazione
if ( vtAx.IsSmall())
return false ;
// ruoto Voronoi
if ( m_pVoronoiObj != nullptr)
m_pVoronoiObj->Rotate( ptAx, vtAx, dCosAng, dSinAng) ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// ruoto le singole curve
for ( auto& pCrv : m_CrvSmplS)
pCrv->Rotate( ptAx, vtAx, dCosAng, dSinAng) ;
// ruoto il vettore estrusione
m_VtExtr.Rotate( vtAx, dCosAng, dSinAng) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double dCoeffZ)
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico non sia nulla
if ( abs( dCoeffX) < EPS_ZERO && abs( dCoeffY) < EPS_ZERO && abs( dCoeffZ) < EPS_ZERO)
return false ;
// verifico se chiusa
bool bClosed = IsClosed() ;
// calcolo bbox allineato con riferimento di scalatura e senza tener conto dello spessore
// lo scalo per verificare se tutto si riduce a un punto
BBox3d b3Ref ;
double dOriThick = 0 ;
swap( dOriThick, m_dThick) ;
if ( ! GetBBox( frRef, b3Ref))
return false ;
swap( dOriThick, m_dThick) ;
Vector3d vtDelta = b3Ref.GetMax() - b3Ref.GetMin() ;
if ( abs( vtDelta.x * dCoeffX) < EPS_SMALL &&
abs( vtDelta.y * dCoeffY) < EPS_SMALL &&
abs( vtDelta.z * dCoeffZ) < EPS_SMALL)
return false ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// se scalatura non omogenea, devo trasformare tutti gli archi in curve di Bezier
if ( abs( dCoeffX - dCoeffY) > EPS_SMALL || abs( dCoeffX - dCoeffZ) > EPS_SMALL) {
if ( ! ArcsToBezierCurves())
return false ;
}
// scalo le singole curve
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ) {
// eseguo la scalatura
bool bOk = (*Iter)->Scale( frRef, dCoeffX, dCoeffY, dCoeffZ) ;
// se tutto bene, passo alla prossima
if ( bOk)
++Iter ;
// altrimenti, l'entità si è annullata => devo toglierla dalla lista e cancellarla
else {
// si è annullata l'entità, la elimino e la tolgo dalla lista
delete (*Iter) ;
Iter = m_CrvSmplS.erase( Iter) ;
}
}
// se ingrandimento, aggiusto coincidenza estremi delle singole curve
if ( abs( dCoeffX) > 1 || abs( dCoeffY) > 1 || abs( dCoeffZ) > 1) {
int nCount = int( m_CrvSmplS.size()) ;
for ( int i = 0 ; i < nCount ; ++ i) {
int j = i - 1 ;
if ( j < 0) {
if ( bClosed)
j = nCount - 1 ;
else
continue ;
}
ICurve* pCrvPrev = m_CrvSmplS[j] ;
ICurve* pCrvCurr = m_CrvSmplS[i] ;
Point3d ptEndPrev ; pCrvPrev->GetEndPoint( ptEndPrev) ;
Point3d ptStaCurr ; pCrvCurr->GetStartPoint( ptStaCurr) ;
if ( ! AreSamePointApprox( ptEndPrev, ptStaCurr)) {
Point3d ptNew = Media( ptEndPrev, ptStaCurr) ;
pCrvPrev->ModifyEnd( ptNew) ;
pCrvCurr->ModifyStart( ptNew) ;
}
}
}
// scalo vettore estrusione, lo normalizzo e aggiusto spessore
m_VtExtr.Scale( frRef, dCoeffX, dCoeffY, dCoeffZ) ;
double dLen = m_VtExtr.Len() ;
if ( dLen > EPS_ZERO) {
m_VtExtr /= dLen ;
m_dThick *= dLen ;
}
m_nStatus = TO_VERIFY ;
return Validate() ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Mirror( const Point3d& ptOn, const Vector3d& vtNorm)
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità del piano di specchiatura
if ( vtNorm.IsSmall())
return false ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// specchio le singole curve
for ( auto& pCrv : m_CrvSmplS)
pCrv->Mirror( ptOn, vtNorm) ;
// specchio il vettore estrusione
m_VtExtr.Mirror( vtNorm) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Shear( const Point3d& ptOn, const Vector3d& vtNorm, const Vector3d& vtDir, double dCoeff)
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità dei parametri
if ( vtNorm.IsSmall() || vtDir.IsSmall())
return false ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// trasformo tutti gli archi in curve di Bezier (potrei controllare gli archi uno ad uno...)
if ( ! ArcsToBezierCurves())
return false ;
// eseguo scorrimento delle singole curve
for ( auto& pCrv : m_CrvSmplS)
pCrv->Shear( ptOn, vtNorm, vtDir, dCoeff) ;
// eseguo scorrimento del vettore estrusione, lo normalizzo e aggiusto spessore
m_VtExtr.Shear( vtNorm, vtDir, dCoeff) ;
double dLen = m_VtExtr.Len() ;
if ( dLen > EPS_ZERO) {
m_VtExtr /= dLen ;
m_dThick *= dLen ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ToGlob( const Frame3d& frRef)
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità del frame
if ( frRef.GetType() == Frame3d::ERR)
return false ;
// se frame identità, non devo fare alcunché
if ( IsGlobFrame( frRef))
return true ;
// trasformo Voronoi
if ( m_pVoronoiObj != nullptr)
m_pVoronoiObj->ToGlob( frRef) ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// trasformo le singole curve
for ( auto& pCrv : m_CrvSmplS)
pCrv->ToGlob( frRef) ;
// trasformo il vettore estrusione
m_VtExtr.ToGlob( frRef) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ToLoc( const Frame3d& frRef)
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità del frame
if ( frRef.GetType() == Frame3d::ERR)
return false ;
// se frame identità, non devo fare alcunché
if ( IsGlobFrame( frRef))
return true ;
// trasformo Voronoi
if ( m_pVoronoiObj != nullptr)
m_pVoronoiObj->ToLoc( frRef) ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// trasformo le singole curve
for ( auto& pCrv : m_CrvSmplS)
pCrv->ToLoc( frRef) ;
// trasformo il vettore estrusione
m_VtExtr.ToLoc( frRef) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::LocToLoc( const Frame3d& frOri, const Frame3d& frDest)
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico validità dei frame
if ( frOri.GetType() == Frame3d::ERR || frDest.GetType() == Frame3d::ERR)
return false ;
// se i due riferimenti coincidono, non devo fare alcunché
if ( AreSameFrame( frOri, frDest))
return true ;
// trasformo Voronoi
if ( m_pVoronoiObj != nullptr)
m_pVoronoiObj->LocToLoc( frOri, frDest) ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// trasformo le singole curve
for ( auto& pCrv : m_CrvSmplS)
pCrv->LocToLoc( frOri, frDest) ;
// trasformo il vettore estrusione
m_VtExtr.LocToLoc( frOri, frDest) ;
return true ;
}
//----------------------------------------------------------------------------
const ICurve*
CurveComposite::GetCurve( int nCrv) const
{
// la curva deve essere validata
if ( m_nStatus != OK)
return nullptr ;
// verifico che l'indice sia nei limiti
if ( nCrv < 0 || nCrv >= int( m_CrvSmplS.size()))
return nullptr ;
// restituisco la curva
return m_CrvSmplS[nCrv] ;
}
//----------------------------------------------------------------------------
const ICurve*
CurveComposite::GetFirstCurve( void) const
{
// la curva deve essere validata
if ( m_nStatus != OK)
return nullptr ;
// vado all'inizio
m_Iter = m_CrvSmplS.begin() ;
// recupero la curva
if ( m_Iter != m_CrvSmplS.end())
return (*m_Iter) ;
else
return nullptr ;
}
//----------------------------------------------------------------------------
const ICurve*
CurveComposite::GetNextCurve( void) const
{
// la curva deve essere validata
if ( m_nStatus != OK)
return nullptr ;
// vado al successivo
if ( m_Iter == m_CrvSmplS.end())
return nullptr ;
++ m_Iter ;
// recupero la curva
if ( m_Iter != m_CrvSmplS.end())
return (*m_Iter) ;
else
return nullptr ;
}
//----------------------------------------------------------------------------
const ICurve*
CurveComposite::GetLastCurve( void) const
{
// la curva deve essere validata
if ( m_nStatus != OK)
return nullptr ;
// vado alla fine
m_Iter = m_CrvSmplS.end() ;
if ( m_Iter == m_CrvSmplS.begin())
return nullptr ;
-- m_Iter ;
// recupero la curva
if ( m_Iter != m_CrvSmplS.end())
return (*m_Iter) ;
else
return nullptr ;
}
//----------------------------------------------------------------------------
const ICurve*
CurveComposite::GetPrevCurve( void) const
{
// la curva deve essere validata
if ( m_nStatus != OK)
return nullptr ;
// vado al precedente
if ( m_Iter == m_CrvSmplS.begin() || m_Iter == m_CrvSmplS.end())
return nullptr ;
-- m_Iter ;
// recupero la curva
if ( m_Iter != m_CrvSmplS.end())
return (*m_Iter) ;
else
return nullptr ;
}
//----------------------------------------------------------------------------
ICurve*
CurveComposite::RemoveFirstOrLastCurve( bool bLast)
{
// la curva composita deve essere validata
if ( m_nStatus != OK)
return nullptr ;
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// recupero la curva semplice iniziale o finale e la tolgo dalla lista
if ( ! m_CrvSmplS.empty()) {
ICurve* pCrv ;
if ( bLast) {
// recupero la curva
pCrv = *(m_CrvSmplS.rbegin()) ;
// la tolgo dalla lista
m_CrvSmplS.pop_back() ;
}
else {
// recupero la curva
pCrv = *(m_CrvSmplS.begin()) ;
// la tolgo dalla lista
m_CrvSmplS.pop_front() ;
}
// eseguo mini verifica
m_nStatus = ( ! m_CrvSmplS.empty() ? OK : TO_VERIFY) ;
// assegno estrusione e spessore della curva composita
pCrv->SetExtrusion( m_VtExtr) ;
pCrv->SetThickness( m_dThick) ;
return pCrv ;
}
else
return nullptr ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::IsParamAtJoint( double dU) const
{
// se aperta e all'inizio o alla fine o lontano dagli interi non è giunzione
bool bClosed = IsClosed() ;
if ( ( ! bClosed && abs( dU) < EPS_PARAM) ||
( ! bClosed && abs( dU - double( m_CrvSmplS.size())) < EPS_PARAM) ||
abs( dU - (int) ( dU + EPS_PARAM)) > EPS_PARAM)
return false ;
else
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ChangeStartPoint( double dU)
{
// la curva deve essere chiusa (ne verifica anche lo stato)
if ( ! IsClosed())
return false ;
// questa funzione gestisce già anche il cambio di inizio su curve chiuse
return TrimStartEndAtParam( dU, dU) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ArcsToBezierCurves( void)
{
// verifico le singole curve
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++Iter) {
// se arco, devo trasformare in una o più curve di Bezier
if ( (*Iter)->GetType() == CRV_ARC) {
// eseguo trasformazione
PtrOwner<ICurve> pNewCrv( ArcToBezierCurve( GetCurveArc( *Iter))) ;
if ( IsNull( pNewCrv))
return false ;
// se risultato è singola curva
if ( pNewCrv->IsSimple()) {
// elimino l'arco e lo sostituisco con la curva di Bezier
delete (*Iter) ;
(*Iter) = Release( pNewCrv) ;
}
// altrimenti è una curva composita
else {
CurveComposite* pCC = GetBasicCurveComposite( pNewCrv) ;
if ( pCC == nullptr)
return false ;
// inserisco le curve prima dell'arco
for ( auto Iter2 = pCC->m_CrvSmplS.begin() ; Iter2 != pCC->m_CrvSmplS.end() ; ++ Iter2) {
Iter = m_CrvSmplS.insert( Iter, (*Iter2)) ;
++ Iter ;
}
pCC->m_CrvSmplS.clear() ;
// elimino l'arco (e sposto l'iteratore alla curva precedente)
delete (*Iter) ;
Iter = m_CrvSmplS.erase( Iter) ;
-- Iter ;
}
}
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ArcsBezierCurvesToArcsPerpExtr( double dLinTol, double dAngTolDeg)
{
// verifico le singole curve
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++ Iter) {
// se arco in piano non perpendicolare ad estrusione o curva di Bezier trasformo
if ( ( (*Iter)->GetType() == CRV_ARC && ! AreSameVectorApprox( m_VtExtr, GetBasicCurveArc( *Iter)->GetNormVersor())) ||
(*Iter)->GetType() == CRV_BEZIER) {
// eseguo trasformazione
(*Iter)->SetExtrusion( m_VtExtr) ;
PtrOwner<ICurve> pNewCrv( CurveToArcsPerpExtrCurve( (*Iter), dLinTol, dAngTolDeg)) ;
(*Iter)->SetExtrusion( V_NULL) ;
if ( IsNull( pNewCrv))
return false ;
// se risultato è singola curva
if ( pNewCrv->IsSimple()) {
// elimino la curva originale e la sostituisco con l'arco
delete (*Iter) ;
(*Iter) = Release( pNewCrv) ;
}
// altrimenti è una curva composita
else {
CurveComposite* pCC = GetBasicCurveComposite( pNewCrv) ;
if ( pCC == nullptr)
return false ;
// inserisco le curve prima dell'originale
for ( auto Iter2 = pCC->m_CrvSmplS.begin() ; Iter2 != pCC->m_CrvSmplS.end() ; ++ Iter2) {
Iter = m_CrvSmplS.insert( Iter, (*Iter2)) ;
++ Iter ;
}
pCC->m_CrvSmplS.clear() ;
// elimino la curva originale (e sposto l'iteratore alla curva precedente)
delete (*Iter) ;
Iter = m_CrvSmplS.erase( Iter) ;
-- Iter ;
}
}
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::StraightArcsToLines( double dLinTol, double dAngTolDeg)
{
// controllo le tolleranze
dLinTol = max( dLinTol, EPS_SMALL) ;
dAngTolDeg = Clamp( dAngTolDeg, EPS_ANG_SMALL, ANG_RIGHT) ;
// verifico le singole curve
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++ Iter) {
CurveArc* pArc = GetBasicCurveArc( *Iter) ;
if ( pArc != nullptr &&
abs( pArc->GetAngCenter()) < dAngTolDeg &&
pArc->GetRadius() * ( 1 - cos( pArc->GetAngCenter() / 2 * DEGTORAD)) < dLinTol) {
// recupero gli estremi
Point3d ptStart, ptEnd ;
if ( ! pArc->GetStartPoint( ptStart) || ! pArc->GetEndPoint( ptEnd))
return false ;
// creo la linea
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
if ( IsNull( pLine) || ! pLine->Set( ptStart, ptEnd))
return false ;
// elimino la curva originale e la sostituisco con la nuova
delete (*Iter) ;
(*Iter) = Release( pLine) ;
}
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
static int
MergeTwoCurves( ICurve* pCrvP, ICurve* pCrvC, double& dCurrLinTol, double dCosAngTol, bool bNeedSameProp)
{
// verifico compatibilità delle proprietà
int nTpr0P = pCrvP->GetTempProp( 0) ;
int nTpr0C = pCrvC->GetTempProp( 0) ;
int nTpr1P = pCrvP->GetTempProp( 1) ;
int nTpr1C = pCrvC->GetTempProp( 1) ;
if ( bNeedSameProp && ( nTpr0P != nTpr0C || nTpr1P != nTpr1C))
return 0 ;
// se precedente molto corta
double dLenP ;
if ( pCrvP->GetLength( dLenP) && dLenP < dCurrLinTol) {
// se abbastanza allineata alla successiva
Vector3d vtDirP, vtDirC ;
if ( pCrvP->GetEndDir( vtDirP) && pCrvC->GetStartDir( vtDirC) && ( vtDirP * vtDirC) >= dCosAngTol) {
bool bModifStart = ( pCrvC->GetType() != CRV_ARC) ;
if ( ! bModifStart) {
/* nel caso in cui la curva corrente sia un arco, bisogna controllare che la somma tra
l'angolo al centro e l'angolo sotteso dalla curva precedente non superi l'angolo giro; in
caso positivo, la modifica del punto inziale dell'arco ( curva corrente) rimoverebbe
tutti gli angoli superiori a 360deg [curve a ricciolo per regioni non svuotate in Pocketing] */
Point3d ptS ; pCrvP->GetStartPoint( ptS) ;
Point3d ptE ; pCrvC->GetStartPoint( ptE) ;
const ICurveArc* pArcC = GetBasicCurveArc( pCrvC) ;
double dAngRef = ( Dist( ptS, ptE) / pArcC->GetRadius()) * RADTODEG ;
bModifStart = ( abs( pArcC->GetAngCenter()) + dAngRef < ANG_FULL - 10 * EPS_ANG_SMALL) ;
}
if ( bModifStart) {
Point3d ptStart ;
return ( pCrvP->GetStartPoint( ptStart) && pCrvC->ModifyStart( ptStart) ? -1 : 0) ;
}
}
}
// se corrente molto corta
double dLenC ;
if ( pCrvC->GetLength( dLenC) && dLenC < dCurrLinTol) {
// se abbastanza allineata alla precedente
Vector3d vtDirP, vtDirC ;
if ( pCrvP->GetEndDir( vtDirP) && pCrvC->GetStartDir( vtDirC) && ( vtDirP * vtDirC) >= dCosAngTol) {
bool bModifEnd = ( pCrvP->GetType() != CRV_ARC) ;
if ( ! bModifEnd) {
/* nel caso in cui la curva predecente sia un arco, bisogna controllare che la somma tra
l'angolo al centro e l'angolo sotteso dalla curva corrente non superi l'angolo giro; in
caso positivo, la modifica del punto finale dell'arco ( curva precedente) rimoverebbe
tutti gli angoli superiori a 360deg [curve a ricciolo per regioni non svuotate in Pocketing] */
Point3d ptS ; pCrvP->GetEndPoint( ptS) ;
Point3d ptE ; pCrvC->GetEndPoint( ptE) ;
const CurveArc* pArcP = GetBasicCurveArc( pCrvP) ;
double dAngRef = ( Dist( ptS, ptE) / pArcP->GetRadius()) * RADTODEG ;
bModifEnd = ( abs( pArcP->GetAngCenter()) + dAngRef < ANG_FULL - 10. * EPS_ANG_SMALL) ;
}
if ( bModifEnd) {
Point3d ptEnd ;
return ( pCrvC->GetEndPoint( ptEnd) && pCrvP->ModifyEnd( ptEnd) ? 1 : 0) ;
}
}
}
// coefficiente deduzione tolleranza
const double COEFF_TOL = 0.7 ;
// se entrambe rette
if ( pCrvP->GetType() == CRV_LINE && pCrvC->GetType() == CRV_LINE) {
CurveLine* pLineP = GetBasicCurveLine( pCrvP) ;
CurveLine* pLineC = GetBasicCurveLine( pCrvC) ;
// verifico se allineate
Vector3d vtDirP, vtDirC ;
if ( pLineP != nullptr && pLineP->GetStartDir( vtDirP) &&
pLineC != nullptr && pLineC->GetStartDir( vtDirC) &&
( vtDirP * vtDirC) >= dCosAngTol) {
// verifico se il punto di giunzione sia eliminabile
DistPointLine dPL( pLineP->GetEnd(), pLineP->GetStart(), pLineC->GetEnd()) ;
double dSqDist ;
if ( dPL.GetSqDist( dSqDist) && dSqDist < dCurrLinTol * dCurrLinTol) {
// se modifica linea corrente ok, procedo con l'unione
if ( pLineC->ModifyStart( pLineP->GetStart())) {
// diminuisco la tolleranza corrente dell'errore attuale
dCurrLinTol -= COEFF_TOL * sqrt( dSqDist) ;
// se curve originali con proprietà diversa, la cancello
if ( nTpr0P != nTpr0C)
pLineC->SetTempProp( 0, 0) ;
if ( nTpr1P != nTpr1C)
pLineC->SetTempProp( 0, 1) ;
// torno flag modifica
return -1 ;
}
}
}
}
// se entrambi archi
else if ( pCrvP->GetType() == CRV_ARC && pCrvC->GetType() == CRV_ARC) {
CurveArc* pArcP = GetBasicCurveArc( pCrvP) ;
CurveArc* pArcC = GetBasicCurveArc( pCrvC) ;
// centro del primo arco riportato alla quota del punto finale sulla normale dello stesso
Point3d ptC1Fin = pArcP->GetCenter() + pArcP->GetNormVersor() * pArcP->GetDeltaN() ;
// centro del secondo arco alla quota del punto iniziale sulla normale dello stesso
Point3d ptC2Ini = pArcC->GetCenter() ;
// verifico la coincidenza dei centri
if ( ! AreSamePointEpsilon( ptC1Fin, ptC2Ini, dCurrLinTol))
return 0 ;
// verifico la coincidenza dei raggi
if ( abs( pArcP->GetRadius() - pArcC->GetRadius()) > dCurrLinTol)
return 0 ;
// verifico la collinearità delle normali (tenendo conto del raggio)
if ( ! (( pArcP->GetNormVersor() - pArcC->GetNormVersor()) * pArcP->GetRadius()).IsSmall() &&
! (( pArcP->GetNormVersor() + pArcC->GetNormVersor()) * pArcP->GetRadius()).IsSmall())
return 0 ;
// verifico la coincidenza del senso di rotazione (verso delle normali e segno angoli al centro)
if ( pArcP->GetNormVersor() * pArcC->GetNormVersor() * pArcP->GetAngCenter() * pArcC->GetAngCenter() < 0)
return 0 ;
// verifico di non superare l'angolo giro al centro
if ( abs( pArcP->GetAngCenter() + pArcC->GetAngCenter()) > ANG_FULL + EPS_ANG_SMALL)
return 0 ;
// verifico se archi piatti
bool bPlaneArcs = pArcP->IsPlane() && pArcC->IsPlane() ;
// se archi non piatti verifico coincidenza pendenza sulla normale
if ( ! bPlaneArcs) {
double dN = pArcP->GetNormVersor() * pArcC->GetNormVersor() ;
if ( abs(( pArcC->GetDeltaN() * pArcP->GetAngCenter() - dN * pArcP->GetDeltaN() * pArcC->GetAngCenter()) /
( pArcP->GetAngCenter() + pArcC->GetAngCenter())) > dCurrLinTol)
return 0 ;
}
// se calcolo nuovo arco ok, procedo con l'unione
Point3d ptP1 ;
pArcP->GetStartPoint( ptP1) ;
Point3d ptP2 ;
pArcP->GetEndPoint( ptP2) ;
Point3d ptP3 ;
pArcC->GetEndPoint( ptP3) ;
// se archi non piani costruisco arco sul piano definito dalla normale e dal punto di partenza del primo arco
Frame3d frRef ;
if ( ! frRef.Set( ptP1, pArcP->GetNormVersor()))
return 0 ;
if ( ! bPlaneArcs) {
ptP1.Scale( frRef, 1, 1, 0) ;
ptP2.Scale( frRef, 1, 1, 0) ;
ptP3.Scale( frRef, 1, 1, 0) ;
ptC1Fin.Scale( frRef, 1, 1, 0) ;
}
// verifico se circonferenza completa
bool bCirc = ( AreSamePointEpsilon( ptP1, ptP3, dCurrLinTol)) ;
if ( bCirc) {
pArcC->GetMidPoint( ptP3) ;
if ( ! bPlaneArcs)
ptP3.Scale( frRef, 1, 1, 0) ;
}
CurveArc NewArc ;
if ( NewArc.Set3P( ptP1, ptP2, ptP3, bCirc)) {
// se vicino a circonferenza arco per 3 punti potrebbe non dare il risultato desiderato quindi faccio controllo su raggio e centro
if ( Dist( NewArc.GetCenter(), ptC1Fin) > 2 * dCurrLinTol || abs( NewArc.GetRadius() - pArcP->GetRadius()) > 2 * dCurrLinTol)
return 0 ;
// verifico normale al piano dell'arco
if ( NewArc.GetNormVersor() * pArcC->GetNormVersor() < 0)
NewArc.InvertN() ;
// se archi non piani ripristino il deltaN
if ( ! bPlaneArcs) {
double dDeltaN1 = pArcP->GetDeltaN() ;
double dDeltaN2 = pArcC->GetDeltaN() ;
NewArc.ChangeDeltaN( dDeltaN1 + dDeltaN2) ;
}
// se curve originali con la stessa proprietà, la riporto
if ( nTpr0P == nTpr0C)
NewArc.SetTempProp( nTpr0C, 0) ;
if ( nTpr1P == nTpr1C)
NewArc.SetTempProp( nTpr1C, 1) ;
// aggiorno l'arco corrente e torno flag modifica
*pArcC = NewArc ;
return -1 ;
}
else
return 0 ;
}
// nessuna fusione
return 0 ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::MergeCurves( double dLinTol, double dAngTolDeg, bool bStartEnd, bool bNeedSameProp)
{
// se non ci sono almeno 2 curve, esco subito
if ( m_CrvSmplS.size() < 2)
return true ;
// controllo sui limiti di tolleranza
dLinTol = max( dLinTol, EPS_SMALL) ;
dAngTolDeg = max( dAngTolDeg, EPS_ANG_SMALL) ;
double dCosAngTol = cos( dAngTolDeg * DEGTORAD) ;
// tolleranza lineare corrente
double dCurrLinTol = dLinTol ;
// devo verificare coppie di curve
auto iterP = m_CrvSmplS.begin() ;
auto iterC = next( iterP) ;
// mentre esiste la coppia
while ( iterC != m_CrvSmplS.end()) {
// se curve unite
switch ( MergeTwoCurves( *iterP, *iterC, dCurrLinTol, dCosAngTol, bNeedSameProp)) {
case -1 : // cancello l'entità precedente e la tolgo dalla lista
delete (*iterP) ;
iterC = m_CrvSmplS.erase( iterP) ;
break ;
case 1 : // cancello l'entità corrente e la tolgo dalla lista
delete (*iterC) ;
iterC = m_CrvSmplS.erase( iterC) ;
iterC = prev( iterC) ;
break ;
default : // ripristino la tolleranza
dCurrLinTol = dLinTol ;
break ;
}
// avanzo
iterP = iterC ;
iterC = next( iterC) ;
}
// se richiesto e curva chiusa devo confrontare anche ultima e prima curva
if ( bStartEnd && m_CrvSmplS.size() >= 2 && IsClosed()) {
iterC = m_CrvSmplS.begin() ;
switch ( MergeTwoCurves( *iterP, *iterC, dCurrLinTol, dCosAngTol, bNeedSameProp)) {
case -1 : // cancello l'entità precedente e la tolgo dalla lista
delete (*iterP) ;
m_CrvSmplS.erase( iterP) ;
break ;
case 1 : // cancello l'entità corrente e la tolgo dalla lista
delete (*iterC) ;
m_CrvSmplS.erase( iterC) ;
break ;
}
}
// imposto ricalcolo Voronoi
ResetVoronoiObject() ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::RemoveSmallParts( double dLinTol, double dAngTolDeg)
{
return ( RemoveCurveSmallParts( this, dLinTol)) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::RemoveSmallDefects( double dLinTol, double dAngTolDeg, bool bAlsoSpikes)
{
return (( ! bAlsoSpikes || RemoveCurveSpikes( this, dLinTol)) && RemoveCurveSmallZs( this, dLinTol)) ;
}
//----------------------------------------------------------------------------
static bool
SplitTopBottomArcs( CurveComposite& cCompo)
{
int i = 0 ;
const ICurve* pCrv = cCompo.GetCurve( i) ;
while ( pCrv != nullptr) {
if ( pCrv->GetType() == CRV_ARC) {
const CurveArc* pArc = GetBasicCurveArc( pCrv) ;
// determino la rotazione angolare dall'inizio alle direzioni destra e sinistra
double dAng1, dAng2 ;
bool bDet ;
pArc->GetStartVersor().GetRotation( X_AX, Z_AX, dAng1, bDet) ;
pArc->GetStartVersor().GetRotation( - X_AX, Z_AX, dAng2, bDet) ;
// oriento queste rotazioni come l'angolo al centro dell'arco
if ( pArc->GetAngCenter() > 0) {
if ( dAng1 < 0)
dAng1 += ANG_FULL ;
if ( dAng2 < 0)
dAng2 += ANG_FULL ;
}
else {
if ( dAng1 > 0)
dAng1 -= ANG_FULL ;
if ( dAng2 > 0)
dAng2 -= ANG_FULL ;
}
// le ordino in senso crescente di ampiezza assoluta
if ( abs( dAng1) > abs( dAng2))
swap( dAng1, dAng2) ;
// verifico se la prima di queste rotazioni è compresa nell'arco
if ( abs( dAng1) > EPS_ANG_SMALL && abs( dAng1) < abs( pArc->GetAngCenter()) - EPS_ANG_SMALL) {
cCompo.AddJoint( i + dAng1 / pArc->GetAngCenter()) ;
}
// verifico la seconda
else if ( abs( dAng2) > EPS_ANG_SMALL && abs( dAng2) < abs( pArc->GetAngCenter()) - EPS_ANG_SMALL) {
cCompo.AddJoint( i + dAng2 / pArc->GetAngCenter()) ;
}
}
// passo alla successiva
pCrv = cCompo.GetCurve( ++i) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::RemoveUndercutOnY( double dLinTol, double dAngTolDeg)
{
// verifico sia una curva che giace nel piano XY e con vettore estrusione nullo o Z
BBox3d b3Box ;
if ( ! GetLocalBBox( b3Box) || ( b3Box.GetMax().z - b3Box.GetMin().z) > dLinTol)
return false ;
if ( ! m_VtExtr.IsSmall() && ! AreSameVectorApprox( m_VtExtr, Z_AX))
return false ;
// ne faccio una copia
CurveComposite ccCopy ;
if ( ! ccCopy.CopyFrom( this))
return false ;
// eventuali modifiche deve essere formata solo da archi e rette
if ( ! ccCopy.ArcsBezierCurvesToArcsPerpExtr( dLinTol, dAngTolDeg))
return false ;
// divido gli archi in sopra e sotto
if ( ! SplitTopBottomArcs( ccCopy))
return false ;
// creo la regione unione delle regioni sottese ad ogni curva rispetto ad un Y inferiore al minimo del box
SurfFlatRegion SfrTot ;
const double DELTA_Y = 10 ;
double dYmin = b3Box.GetMin().y - DELTA_Y ;
PtrOwner<ICurve> pCrv ;
while ( pCrv.Set( ccCopy.RemoveFirstOrLastCurve( false))) {
// creo la regione sottesa dalla curva
PtrOwner<CurveComposite> pCompo( CreateBasicCurveComposite()) ;
if ( IsNull( pCompo))
return false ;
pCompo->AddCurve( Release( pCrv)) ;
// estremi
Point3d ptStart, ptEnd ;
pCompo->GetStartPoint( ptStart) ;
pCompo->GetEndPoint( ptEnd) ;
// se curva pressochè verticale non sottende alcunché, quindi la salto
if ( abs( ptStart.x - ptEnd.x) < EPS_SMALL)
continue ;
// ordino gli estremi
if ( ptStart.x < ptEnd.x) {
swap( ptStart, ptEnd) ;
pCompo->Invert() ;
}
// chiudo la curva
pCompo->AddLine( Point3d( ptEnd.x, dYmin, ptEnd.z)) ;
pCompo->AddLine( Point3d( ptStart.x, dYmin, ptStart.z)) ;
pCompo->Close() ;
// creo regione sottesa
SurfFlatRegion Sfr ;
Sfr.AddExtLoop( Release( pCompo)) ;
if ( ! Sfr.IsValid())
return false ;
// la unisco alla regione complessiva
if ( ! SfrTot.IsValid())
SfrTot = Sfr ;
else {
if ( ! SfrTot.Add( Sfr))
return false ;
}
}
// recupero il contorno esterno della regione
PtrOwner<ICurve> pOutLoop( SfrTot.GetLoop( 0, 0)) ;
if ( IsNull( pOutLoop))
return false ;
// elimino le parti sotto il minimo del box
// box della parte da eliminare
double dWidth = b3Box.GetMax().x - b3Box.GetMin().x + 20 * EPS_SMALL ;
double dLen = DELTA_Y + 10 * EPS_SMALL ;
PtrOwner<ISurfFlatRegion> pSfrCut( GetSurfFlatRegionRectangle( dWidth, dLen)) ;
if ( IsNull( pSfrCut))
return false ;
pSfrCut->Translate( b3Box.GetMin() - Point3d( 10 * EPS_SMALL, dLen, 0)) ;
// calcolo la classificazione della curva rispetto alla regione
CRVCVECTOR ccClass ;
if ( ! pSfrCut->GetCurveClassification( *pOutLoop, EPS_SMALL, ccClass))
return false ;
// determino gli intervalli di curva da conservare
Intervals inOk ;
for ( auto& ccOne : ccClass) {
if ( ccOne.nClass == CRVC_OUT)
inOk.Add( ccOne.dParS, ccOne.dParE) ;
}
// Sempre un solo intervallo effettivo su curva chiusa
double dTrimS, dTrimE, dDummy ;
if ( inOk.GetCount() == 1)
inOk.GetFirst( dTrimS, dTrimE) ;
else if ( inOk.GetCount() == 2) {
inOk.GetFirst( dDummy, dTrimE) ;
inOk.GetNext( dTrimS, dDummy) ;
}
else
return false ;
if ( ! pOutLoop->TrimStartEndAtParam( dTrimS, dTrimE))
return false ;
// assegno la nuova curva
Clear() ;
return AddCurve( Release( pOutLoop)) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::IsAPoint( void) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// ciclo di verifica
for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++Iter) {
if ( (*Iter)->GetType() != CRV_BEZIER ||
! GetBasicCurveBezier(*Iter)->IsAPoint())
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::IsALine( double dLinTol, Point3d& ptStart, Point3d& ptEnd) const
{
// verifico lo stato
if ( m_nStatus != OK || m_CrvSmplS.empty())
return false ;
// recupero gli estremi
if ( ! m_CrvSmplS.front()->GetStartPoint( ptStart) ||
! m_CrvSmplS.back()->GetEndPoint( ptEnd))
return false ;
// verifico non siano coincidenti
if ( AreSamePointEpsilon( ptStart, ptEnd, min( dLinTol, EPS_SMALL)))
return false ;
// provo ad approssimarla con segmenti e verifico se si riduce ad un segmento di retta
PolyLine PL ;
if ( ! ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, APL_SPECIAL, PL))
return false ;
// elimino i punti allineati entro la tolleranza
if ( ! PL.RemoveAlignedPoints( dLinTol))
return false ;
// se sono rimasti due punti è una retta
return ( PL.GetPointNbr() == 2) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::IsOneCircle( Point3d& ptCen, Vector3d& vtN, double& dRad, bool& bCCW) const
{
// deve essere una sola entità
if ( GetCurveCount() != 1)
return false ;
// deve essere un arco di circonferenza completo
const CurveArc* pArc = GetBasicCurveArc( GetFirstCurve()) ;
if ( pArc == nullptr || ! pArc->IsACircle())
return false ;
// assegno i parametri
ptCen = pArc->GetCenter() ;
vtN = pArc->GetNormVersor() ;
dRad = pArc->GetRadius() ;
bCCW = ( pArc->GetAngCenter() > 0) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::IsACircle( double dLinTol, Point3d& ptCen, Vector3d& vtN, double& dRad, bool& bCCW) const
{
// deve essere chiusa
if ( ! IsClosed())
return false ;
// se è formata da una sola entità arco che è una circonferenza
if ( IsOneCircle( ptCen, vtN, dRad, bCCW))
return true ;
// provo ad approssimarla con archi e verifico se si riduce ad una circonferenza
PolyArc PA ;
if ( ! ApproxWithArcs( dLinTol, ANG_TOL_STD_DEG, PA))
return false ;
CurveComposite CrvTemp ;
if ( ! CrvTemp.FromPolyArc( PA) || ! CrvTemp.MergeCurves( dLinTol, ANG_TOL_STD_DEG))
return false ;
return CrvTemp.IsOneCircle( ptCen, vtN, dRad, bCCW) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::IsARectangle( double dLinTol, Point3d& ptP, Vector3d& vtL1, Vector3d& vtL2) const
{
// deve essere chiusa
if ( ! IsClosed())
return false ;
// approssimo con segmenti di retta
PolyLine PL ;
if ( ! ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, APL_STD, PL))
return false ;
// deve giacere in un piano entro la tolleranza
Plane3d plPlane ;
if ( ! PL.IsFlat( plPlane, dLinTol))
return false ;
// deve essere formata da 4 segmenti
if ( PL.GetLineNbr() != 4)
return false ;
// recupero i 4 vertici
Point3d ptV1 ; PL.GetFirstPoint( ptV1) ;
Point3d ptV2 ; PL.GetNextPoint( ptV2) ;
Point3d ptV3 ; PL.GetNextPoint( ptV3) ;
Point3d ptV4 ; PL.GetNextPoint( ptV4) ;
// verifico che le diagonali si incontrino nel loro punto medio (-> è un parallelogramma)
if ( ! AreSamePointEpsilon( Media( ptV1, ptV3), Media( ptV2, ptV4), dLinTol / 2))
return false ;
// verifico che le diagonali abbiano la stessa lunghezza (-> è un rettangolo)
if ( abs( Dist( ptV1, ptV3) - Dist( ptV2, ptV4)) > dLinTol)
return false ;
// assegno i parametri del rettangolo
ptP = ptV1 ;
vtL1 = ptV2 - ptV1 ;
vtL2 = ptV4 - ptV1 ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::IsATrapezoid( double dLinTol, Point3d& ptP, Vector3d& vtB1, Vector3d& vtL1, Vector3d& vtB2) const
{
// deve essere chiusa
if ( ! IsClosed())
return false ;
// approssimo con segmenti di retta
PolyLine PL ;
if ( ! ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, APL_STD, PL))
return false ;
// deve giacere in un piano entro la tolleranza
Plane3d plPlane ;
if ( ! PL.IsFlat( plPlane, dLinTol))
return false ;
// deve essere formata da 4 segmenti
if ( PL.GetLineNbr() != 4)
return false ;
// recupero i 4 vertici
Point3d ptV1 ; PL.GetFirstPoint( ptV1) ;
Point3d ptV2 ; PL.GetNextPoint( ptV2) ;
Point3d ptV3 ; PL.GetNextPoint( ptV3) ;
Point3d ptV4 ; PL.GetNextPoint( ptV4) ;
// verifico se V4->V3 è parallelo a V1->V2
double dV3B12, dV4B12 ;
if ( ! DistPointLine( ptV3, ptV1, ptV2, false).GetDist( dV3B12) ||
! DistPointLine( ptV4, ptV1, ptV2, false).GetDist( dV4B12))
return false ;
if ( abs( dV3B12 - dV4B12) < EPS_SMALL) {
ptP = ptV1 ;
vtB1 = ptV2 - ptV1 ;
vtL1 = ptV4 - ptV1 ;
vtB2 = ptV3 - ptV4 ;
return true ;
}
// verifico se V1->V4 è parallelo a V2->V3
double dV1B23, dV4B23 ;
if ( ! DistPointLine( ptV1, ptV2, ptV3, false).GetDist( dV1B23) ||
! DistPointLine( ptV4, ptV2, ptV3, false).GetDist( dV4B23))
return false ;
if ( abs( dV1B23 - dV4B23) < EPS_SMALL) {
ptP = ptV2 ;
vtB1 = ptV3 - ptV2 ;
vtL1 = ptV1 - ptV2 ;
vtB2 = ptV4 - ptV1 ;
return true ;
}
// non è un trapezio
return false ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::SetCurveTempProp( int nCrv, int nProp, int nPropInd)
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico che l'indice sia nei limiti
if ( nCrv < 0 || nCrv >= int( m_CrvSmplS.size()))
return false ;
// eseguo assegnazione
m_CrvSmplS[nCrv]->SetTempProp( nProp, nPropInd) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetCurveTempProp( int nCrv, int& nProp, int nPropInd) const
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico che l'indice sia nei limiti
if ( nCrv < 0 || nCrv >= int( m_CrvSmplS.size()))
return false ;
// eseguo recupero
nProp = m_CrvSmplS[nCrv]->GetTempProp( nPropInd) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::SetCurveTempParam( int nCrv, double dParam, int nParamInd)
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico che l'indice sia nei limiti
if ( nCrv < 0 || nCrv >= int( m_CrvSmplS.size()))
return false ;
// eseguo assegnazione
m_CrvSmplS[nCrv]->SetTempParam( dParam, nParamInd) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetCurveTempParam( int nCrv, double& dParam, int nParamInd) const
{
// la curva deve essere validata
if ( m_nStatus != OK)
return false ;
// verifico che l'indice sia nei limiti
if ( nCrv < 0 || nCrv >= int( m_CrvSmplS.size()))
return false ;
// eseguo recupero
dParam = m_CrvSmplS[nCrv]->GetTempParam( nParamInd) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::CalcVoronoiObject() const
{
if ( m_nStatus != OK)
return false ;
// creo oggetto vroni con la curva
m_pVoronoiObj = new( std::nothrow) Voronoi( this, false) ;
if ( m_pVoronoiObj == nullptr)
return false ;
return true ;
}
//----------------------------------------------------------------------------
Voronoi*
CurveComposite::GetVoronoiObject() const
{
if ( m_nStatus != OK)
return nullptr ;
// se non è stato calcolato, lo calcolo
if ( m_pVoronoiObj == nullptr)
CalcVoronoiObject() ;
// restituisco Voronoi
return m_pVoronoiObj ;
}
//----------------------------------------------------------------------------
void
CurveComposite::ResetVoronoiObject() const
{
if ( m_pVoronoiObj != nullptr)
delete m_pVoronoiObj ;
m_pVoronoiObj = nullptr ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetOnlyPoint(Point3d& ptStart) const
{
// verifico lo stato
if ( m_nStatus != IS_A_POINT)
return false ;
// restituisco il punto
ptStart = m_ptStart ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::ReplaceSingleCurve( int nSubCrv, ICurve* pNewCurveToAdd, double dTolStartEnd, double dTolAlong)
{
// prendo il possesso e verifico la curva
PtrOwner<ICurve> pNewCurve( pNewCurveToAdd) ;
if ( IsNull( pNewCurve) || ! pNewCurve->IsValid())
return false ;
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// verifico l'indice sia sensato
if ( nSubCrv < 0 || nSubCrv > GetCurveCount())
return false ;
// verifico che start e end coincidano entro la tolleranza
Point3d ptStart ; m_CrvSmplS[nSubCrv]->GetStartPoint( ptStart) ;
Point3d ptEnd ; m_CrvSmplS[nSubCrv]->GetEndPoint( ptEnd) ;
Point3d ptNewStart ; pNewCurve->GetStartPoint( ptNewStart) ;
Point3d ptNewEnd ; pNewCurve->GetEndPoint( ptNewEnd) ;
if ( ! AreSamePointApprox( ptStart, ptNewStart) || ! AreSamePointApprox( ptEnd, ptNewEnd)) {
// se i punti di inizio e fine non sono entro EPS_SMALL ma sono entro la tolleranza passata allora modifico la curva da aggiungere
if ( AreSamePointEpsilon( ptStart, ptNewStart, dTolStartEnd) && AreSamePointEpsilon( ptEnd, ptNewEnd, dTolStartEnd)) {
if ( ! pNewCurve->ModifyStart( ptStart) || ! pNewCurve->ModifyEnd( ptEnd))
return false ;
}
else
return false ;
}
// se presente una tolleranza lungo la curva controllo che sia rispettata
if ( dTolAlong < INFINITO) {
double dErr = 0 ;
CalcApproxError( m_CrvSmplS[nSubCrv], pNewCurve, dErr, 20) ;
if ( dErr > dTolAlong)
return false ;
}
delete m_CrvSmplS[nSubCrv] ;
m_CrvSmplS[nSubCrv] = Release( pNewCurve) ;
return true ;
}