//---------------------------------------------------------------------------- // 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 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 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 pCrv( pSmplCrv) ; if ( IsNull( pCrv)) return false ; // verifico lo stato if ( m_nStatus != OK && ! ( m_CrvSmplS.empty() && m_nStatus == TO_VERIFY)) 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)) 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)) 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)) { Point3d ptMid = Media( ptStart, ptEnd) ; if ( ! ModifyStart( ptMid) || ! ModifyEnd( ptMid)) return false ; } // altrimenti aggiungo la linea di chiusura else { PtrOwner 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 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 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 pCrvLine( CreateBasicCurveLine()) ; 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 ( abs( dBulge) < EPS_SMALL) { // creo la retta PtrOwner 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 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 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 ; } 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( 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 ++ 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( 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( 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( void) { // se non è chiusa, esco subito if ( ! IsClosed()) return true ; // verifico ed eventualmente aggiusto coincidenza punti estremi Point3d ptStart ; m_CrvSmplS.front()->GetStartPoint( ptStart) ; Point3d ptEnd ; m_CrvSmplS.back()->GetEndPoint( ptEnd) ; // 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) return GetBasicCurveArc( m_CrvSmplS.front())->ChangeAngCenter( 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 ; } } // 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( 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 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 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( floor( dUStart)) ; int nIdEnd = static_cast( ceil( dUEnd)) ; // creo la curva composita copia PtrOwner 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) { // 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) ; // 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) return false ; // assegno il punto m_ptStart = ptStart ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::AddLineTg( double dLen, bool bEndOrStart) { // verifico lo stato if ( m_nStatus != OK) return false ; // costruisco la linea PtrOwner 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 != TO_VERIFY) return false ; // costruisco la linea PtrOwner 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 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 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 pCrv1( pCurve->Clone()) ; PtrOwner 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() ; // verifico l'indice della giunzione if ( nU < 0 || nU > nCrvCount) return false ; // 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) ; } // altrimenti diventa un segmento di retta else { // creo la linea PtrOwner 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) ; } // altrimenti diventa un segmento di retta else { // creo la linea PtrOwner 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 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 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 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 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( 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.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 && 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 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 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 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 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) { 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) { 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) ; } // verifico se circonferenza completa bool bCirc = ( AreSamePointApprox( ptP1, ptP3)) ; if ( bCirc) { pArcC->GetMidPoint( ptP3) ; if ( ! bPlaneArcs) ptP3.Scale( frRef, 1, 1, 0) ; } CurveArc NewArc ; if ( NewArc.Set3P( ptP1, ptP2, ptP3, bCirc)) { // 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 pCrv ; while ( pCrv.Set( ccCopy.RemoveFirstOrLastCurve( false))) { // creo la regione sottesa dalla curva PtrOwner 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 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 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::FromPoint(Point3d& ptStart) { // verifico lo stato if ( m_nStatus != TO_VERIFY) return false ; // assegno il punto e setto lo stato m_ptStart = ptStart ; m_nStatus = IS_A_POINT ; return true ; } //---------------------------------------------------------------------------- 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 ; }