Files
EgtGeomKernel/CurveComposite.cpp
T
Dario Sassi a98b5d0d0a EgtGeomKernel 1.9k2 :
- correzioni a ApproxWithArcs per gestione vettore estrusione.
2018-11-26 09:23:06 +00:00

2911 lines
93 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2013-2013
//----------------------------------------------------------------------------
// File : CurveComposite.cpp Data : 23.11.13 Versione : 1.3a1
// Contenuto : Implementazione della classe CCurveComposite.
//
//
//
// Modifiche : 26.04.13 DS Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "CurveComposite.h"
#include "DistPointLine.h"
#include "DistPointCrvComposite.h"
#include "CurveLine.h"
#include "CurveArc.h"
#include "CurveBezier.h"
#include "PolygonPlane.h"
#include "SurfFlatRegion.h"
#include "GeoConst.h"
#include "GeoObjFactory.h"
#include "NgeWriter.h"
#include "NgeReader.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/EgtPointerOwner.h"
#include <algorithm>
using namespace std ;
//----------------------------------------------------------------------------
GEOOBJ_REGISTER( CRV_COMPO, NGE_C_CMP, CurveComposite) ;
//----------------------------------------------------------------------------
CurveComposite::CurveComposite( void)
: m_nStatus( TO_VERIFY), m_VtExtr(), m_dThick(), m_ptStart(), m_nTempProp(), 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 ;
m_Iter = m_CrvSmplS.end() ;
// 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 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
CurveComposite* pCrvCompo = dynamic_cast<CurveComposite*>( pCrv) ;
if ( ! AddCurveByRelocate( *pCrvCompo, bEndOrStart, dLinTol))
return false ;
// cancello la curva composita originaria
delete pCrvCompo ;
}
// 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 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))
return false ;
// 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 ptStart, ptEnd ;
if ( ! pCrv->GetStartPoint( ptStart) || ! pCrv->GetEndPoint( ptEnd))
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 ( ! AreSamePointApprox( ptStart, ptEnd)) {
// se in tolleranza, modifico l'inizio dell'entità
if ( SqDist( ptStart, ptEnd) < ( dLinTol * dLinTol)) {
if ( ! pCrv->ModifyStart( ptEnd))
return false ;
}
else
return false ;
}
}
// altrimenti inserita all'inizio
else {
// verifico sia in continuità con l'iniziale attuale
Point3d ptStart ;
GetStartPoint( ptStart) ;
if ( ! AreSamePointApprox( ptEnd, ptStart)) {
// se in tolleranza, modifico la fine dell'entità
if ( SqDist( ptEnd, ptStart) < ( dLinTol * dLinTol)) {
if ( ! pCrv->ModifyEnd( ptStart))
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 ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Close( void)
{
// verifico curva valida
if ( m_nStatus != OK)
return false ;
// se già chiusa, non faccio alcunché
if ( IsClosed())
return true ;
// aggiungo la linea di chiusura
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
Point3d ptStart, ptEnd ;
if ( ! GetStartPoint( ptStart) ||
! GetEndPoint( ptEnd) ||
! pLine->Set( ptEnd, ptStart) ||
! AddSimpleCurve( Release( pLine)))
return false ;
// 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 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
Point3d ptIni, ptFin ;
PL.GetFirstPoint( ptIni) ;
while ( PL.GetNextPoint( ptFin)) {
// se i punti della coppia coincidono, passo alla coppia successiva
if ( AreSamePointApprox( ptIni, ptFin))
continue ;
// creo il segmento di retta
PtrOwner<ICurveLine> pCrvLine( CreateCurveLine()) ;
if ( IsNull( pCrvLine))
return false ;
// assegno i punti estremi
if ( ! pCrvLine->Set( ptIni, ptFin))
return false ;
// aggiungo la retta alla curva composita
if ( ! AddSimpleCurve( Release( pCrvLine)))
return false ;
// aggiorno dati prossimo punto iniziale
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 ( fabs( 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 = ccSrc.m_nTempProp ;
for ( auto& pCrv : ccSrc.m_CrvSmplS) {
if ( ! AddCurve( *pCrv))
return false ;
}
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 = ccSrc.m_nTempProp ;
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 ;
// salvataggio della curva semplice
if ( ! pCrvSmpl->Dump( sOut, bMM, szNewLine))
return false ;
// passo alla successiva
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) ;
// 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() && fabs( 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() && fabs( 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 ;
PCSD_CONST_ITER Iter ;
for ( Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++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) ;
}
return ( m_nStatus == OK) ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::IsFlat( Plane3d& plPlane, 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 = GetFirstCurve() ;
pCrv != nullptr ;
pCrv = GetNextCurve(), ++ 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_BEZ :
{ 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 ;
}
}
// 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 {
plPlane.Set( ptCen, m_VtExtr) ;
bFlat = bFlat && ( AreOrthoApprox( m_VtExtr, vtDir)) ;
}
break ;
default : // piana o 3d
if ( m_VtExtr.IsSmall())
plPlane.Set( ptCen, vtDir) ;
else {
plPlane.Set( ptCen, m_VtExtr) ;
bFlat = bFlat && ( 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, ICurve::APL_SPECIAL, 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, 0.5 * m_CrvSmplS.size(), 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 ( fabs( dLocU) < 5 * EPS_PARAM && nSCrv > 0 && nS == ICurve::FROM_MINUS) {
-- nSCrv ;
dLocU = 1 ;
}
else if ( fabs( 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::GetLength( double& dLen) const
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// ciclo di calcolo
dLen = 0 ;
PCSD_CONST_ITER Iter ;
for ( Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++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 ;
PCSD_CONST_ITER Iter ;
for ( Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++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 ;
PCSD_CONST_ITER Iter ;
for ( Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++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) {
// 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 ;
// 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 a 10 * Epsilon
if ( ! ApproxWithLines( 10 * 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 ;
// 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
PolyArc PASmpl ;
if ( ! pCrv->ApproxWithArcs( dLinTol, dAngTolDeg, PASmpl))
return false ;
// la accodo opportunamente a quella della curva composita
if ( ! PA.Join( PASmpl, dStartPar))
return false ;
// ripristino estrusione e spessore della curva semplice (annullandoli)
pCrv->SetExtrusion( V_NULL) ;
pCrv->SetThickness( 0) ;
// incremento inizio parametro per prossima curva semplice
dStartPar += 1 ;
}
// assegno estrusione della curva composita
PA.SetExtrusion( m_VtExtr) ;
return true ;
}
//----------------------------------------------------------------------------
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 ;
// 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) ;
// se segmento di linea non feature
double dLen ;
if ( pCrv->GetType() == CRV_LINE && pCrv->GetLength( dLen) && dLen < dLinFea) {
CurveLine* pLine = GetBasicCurveLine( pCrv) ;
// 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()) ;
}
// altrimenti
else {
// se in corso approx multilinee
if ( bMultiLine) {
bMultiLine = false ;
PolyArc PASmpl ;
if ( ! crvByApprox.GetArcs( 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 ( ! pCrv->ApproxWithArcs( dLinTol, dAngTolDeg, PASmpl))
return false ;
// la accodo opportunamente a quella della curva composita
if ( ! PA.Join( PASmpl, dStartPar))
return false ;
}
// ripristino estrusione e spessore della curva semplice (annullandoli)
pCrv->SetExtrusion( V_NULL) ;
pCrv->SetThickness( 0) ;
// incremento inizio parametro per prossima curva semplice
dStartPar += 1 ;
}
// se approssimazione multiline ancora aperta
if ( bMultiLine) {
bMultiLine = false ;
PolyArc PASmpl ;
if ( ! crvByApprox.GetArcs( dLinTol, dAngTolDeg, dLinFea, PASmpl))
return false ;
// la accodo opportunamente a quella della curva composita
if ( ! PA.Join( PASmpl, dMlStartPar))
return false ;
}
// assegno estrusione della curva composita
PA.SetExtrusion( m_VtExtr) ;
return true ;
}
//----------------------------------------------------------------------------
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 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 ( ::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 della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::SimpleOffset( double dDist, int nType)
{
// se distanza di offset nulla, non devo fare alcunché
if ( fabs( 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) ;
// 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 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 della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddPoint( const Point3d& ptStart)
{
// verifico lo stato
if ( m_nStatus != TO_VERIFY)
return false ;
// assegno il punto
m_ptStart = ptStart ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::AddLine( const Point3d& ptNew, bool bEndOrStart)
{
// verifico lo stato
if ( m_nStatus != OK && m_nStatus != TO_VERIFY)
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)
{
// verifico lo stato
if ( m_nStatus != OK)
return false ;
// numero di curve
int nCrvCount = GetCurveCount() ;
// recupero il puntatore alla curva precedente (se esiste)
ICurve* pPrevCrv = nullptr ;
if ( nU > 0 && nU <= nCrvCount)
pPrevCrv = m_CrvSmplS[ nU - 1] ;
else if ( nU == 0 && IsClosed())
pPrevCrv = m_CrvSmplS[ nCrvCount - 1] ;
// recupero il puntatore alla curva successiva (se esiste)
ICurve* pNextCrv = nullptr ;
if ( nU >= 0 && nU < nCrvCount)
pNextCrv = m_CrvSmplS[ nU] ;
else if ( nU == nCrvCount && IsClosed())
pNextCrv = m_CrvSmplS[ 0] ;
// se non esiste curva precedente o curva successiva, errore
if ( pPrevCrv == nullptr || pNextCrv == nullptr)
return false ;
// modifico il punto finale dell'entità precedente
if ( ! pPrevCrv->ModifyEnd( ptNewJoint))
return false ;
// modifico il punto iniziale dell'entità successiva
if ( ! pNextCrv->ModifyStart( ptNewJoint)) {
// c'è stato errore, ripristino punto finale dell'entità precedente
Point3d ptJ ;
pNextCrv->GetStartPoint( ptJ) ;
pPrevCrv->ModifyEnd( ptJ) ;
return false ;
}
// 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() ;
// 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 && nU <= nCrvCount)
nPrevCrv = nU - 1 ;
else if ( nU == 0 && IsClosed())
nPrevCrv = nCrvCount - 1 ;
// recupero l'indice della curva successiva (se esiste)
int nNextCrv = - 1 ;
if ( nU >= 0 && nU < nCrvCount)
nNextCrv = nU ;
else if ( nU == nCrvCount && IsClosed())
nNextCrv = 0 ;
// se non esiste curva precedente o curva successiva, errore
if ( nPrevCrv == - 1 || nNextCrv == - 1)
return false ;
// 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 della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::TrimStartAtParam( double dUTrim)
{
// 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.pop_front() ;
}
// se lunghezza ancora da tagliare nulla (entro la tolleranza)
else if ( dUToTrim > - EPS_PARAM ||
! (*Iter)->TrimStartAtParam( 1 + dUToTrim)) {
delete (*Iter) ;
Iter ++ ;
m_CrvSmplS.pop_front() ;
if ( m_CrvSmplS.empty())
return false ;
break ;
}
// altrimenti superata lunghezza ancora da tagliare (taglio già fatto al test sopra)
else {
break ;
}
}
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::TrimEndAtParam( double dUTrim)
{
// 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 ;
}
}
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
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 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 ;
// 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 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 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 della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::Translate( const Vector3d& 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)
{
// verifico validità dell'asse di rotazione
if ( vtAx.IsSmall())
return false ;
// 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)
{
// verifico non sia nulla
if ( fabs( dCoeffX) < EPS_ZERO && fabs( dCoeffY) < EPS_ZERO && fabs( dCoeffZ) < EPS_ZERO)
return false ;
// 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 ( fabs( vtDelta.x * dCoeffX) < EPS_SMALL &&
fabs( vtDelta.y * dCoeffY) < EPS_SMALL &&
fabs( vtDelta.z * dCoeffZ) < EPS_SMALL)
return false ;
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
// se scalatura non omogenea, devo trasformare tutti gli archi in curve di Bezier
if ( fabs( dCoeffX - dCoeffY) > EPS_SMALL || fabs( 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) ;
}
}
// 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)
{
// verifico validità del piano di specchiatura
if ( vtNorm.IsSmall())
return false ;
// 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)
{
// verifico validità dei parametri
if ( vtNorm.IsSmall() || vtDir.IsSmall())
return false ;
// 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)
{
// verifico validità del frame
if ( frRef.GetType() == Frame3d::ERR)
return false ;
// 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)
{
// verifico validità del frame
if ( frRef.GetType() == Frame3d::ERR)
return false ;
// 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)
{
// 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 ;
// 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 ;
// 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.size() > 0 ? 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 && fabs( dU) < EPS_PARAM) ||
( ! bClosed && fabs( dU - double( m_CrvSmplS.size())) < EPS_PARAM) ||
fabs( 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( (*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 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_BEZ) {
// 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 della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::MergeCurves( double dLinTol, double dAngTolDeg, bool bStartEnd)
{
// 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) ;
// 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
if ( MergeTwoCurves( *iterP, *iterC, dCurrLinTol, dAngTolDeg)) {
// cancello l'entità precedente e la tolgo dalla lista
delete (*iterP) ;
iterC = m_CrvSmplS.erase( iterP) ;
}
// altrimenti ripristino la tolleranza
else
dCurrLinTol = dLinTol ;
// avanzo
iterP = iterC ;
iterC = next( iterC) ;
}
// se richiesto e curva chiusa devo confrontare anche ultima e prima curva
if ( bStartEnd && IsClosed()) {
if ( MergeTwoCurves( *iterP, *(m_CrvSmplS.begin()), dCurrLinTol, dAngTolDeg)) {
// cancello l'entità precedente e la tolgo dalla lista
delete (*iterP) ;
iterC = m_CrvSmplS.erase( iterP) ;
}
}
// imposto ricalcolo della grafica
m_OGrMgr.Reset() ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::MergeTwoCurves( ICurve* pCrvP, ICurve* pCrvC, double& dCurrLinTol, double dAngTolDeg)
{
// 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) >= cos( dAngTolDeg * DEGTORAD)) {
// verifico se il punto di giunzione sia eliminabile
DistPointLine dPL( pLineP->GetEnd(), pLineP->GetStart(), pLineC->GetEnd()) ;
double dSqDist ;
// se da eliminare
if ( dPL.GetSqDist( dSqDist) && dSqDist < dCurrLinTol * dCurrLinTol) {
// se calcolo nuova linea ok, procedo con l'unione
CurveLine NewLine ;
if ( NewLine.Set( pLineP->GetStart(), pLineC->GetEnd())) {
// diminuisco la tolleranza corrente dell'errore attuale
dCurrLinTol -= COEFF_TOL * sqrt( dSqDist) ;
// aggiorno la linea corrente e torno flag modifica
*pLineC = NewLine ;
return true ;
}
}
}
}
// 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 false ;
// verifico la coincidenza dei raggi
if ( fabs( pArcP->GetRadius() - pArcC->GetRadius()) > dCurrLinTol)
return false ;
// 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 false ;
// 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 false ;
// verifico di non superare l'angolo giro al centro
if ( abs( pArcP->GetAngCenter() + pArcC->GetAngCenter()) > ANG_FULL + EPS_ANG_SMALL)
return false ;
// se archi piatti
if ( pArcP->IsFlat() && pArcC->IsFlat()) {
// se calcolo nuovo arco ok, procedo con l'unione
Point3d ptP1 ;
pArcP->GetStartPoint( ptP1) ;
Point3d ptP2 ;
pArcP->GetEndPoint( ptP2) ;
Point3d ptP3 ;
pArcC->GetEndPoint( ptP3) ;
// verifico se circonferenza completa
bool bCirc = ( AreSamePointApprox( ptP1, ptP3)) ;
if ( bCirc)
pArcC->GetMidPoint( ptP3) ;
CurveArc NewArc ;
if ( NewArc.Set3P( ptP1, ptP2, ptP3, bCirc)) {
// verifico normale al piano dell'arco
if ( NewArc.GetNormVersor() * pArcC->GetNormVersor() < 0)
NewArc.InvertN() ;
// aggiorno l'arco corrente e torno flag modifica
*pArcC = NewArc ;
return true ;
}
else
return false ;
}
// verifico coincidenza pendenza sulla normale
double dN = pArcP->GetNormVersor() * pArcC->GetNormVersor() ;
if ( fabs(( pArcC->GetDeltaN() * pArcP->GetAngCenter() - dN * pArcP->GetDeltaN() * pArcC->GetAngCenter()) /
( pArcP->GetAngCenter() + pArcC->GetAngCenter())) < dCurrLinTol) {
// se calcolo nuovo arco ok, procedo con l'unione
Point3d ptP1 ;
pArcP->GetStartPoint( ptP1) ;
Vector3d vtDir1 ;
pArcP->GetStartDir( vtDir1) ;
Point3d ptP3 ;
pArcC->GetEndPoint( ptP3) ;
CurveArc NewArc ;
if ( NewArc.Set2PVN( ptP1, ptP3, vtDir1, pArcC->GetNormVersor())) {
// aggiorno l'arco corrente e torno flag modifica
*pArcC = NewArc ;
return true ;
}
else
return false ;
}
}
// nessuna fusione
return false ;
}
//----------------------------------------------------------------------------
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, 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::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::SetCurveTempProp( int nCrv, int nProp)
{
// 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) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveComposite::GetCurveTempProp( int nCrv, int& nProp) 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() ;
return true ;
}