//---------------------------------------------------------------------------- // EgalTech 2013-2013 //---------------------------------------------------------------------------- // File : CurveComposite.cpp Data : 23.11.13 Versione : 1.3a1 // Contenuto : Implementazione della classe CCurveComposite. // // // // Modifiche : 26.04.13 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "CurveComposite.h" #include "DistPointLine.h" #include "DistPointCrvComposite.h" #include "CurveLine.h" #include "CurveArc.h" #include "CurveBezier.h" #include "PolygonPlane.h" #include "GeoConst.h" #include "GeoObjFactory.h" #include "NgeWriter.h" #include "NgeReader.h" #include "/EgtDev/Include/EGkCurveByApprox.h" #include "/EgtDev/Include/EGkStringUtils3d.h" #include "/EgtDev/Include/EgtPointerOwner.h" #include using namespace std ; //---------------------------------------------------------------------------- GEOOBJ_REGISTER( CRV_COMPO, NGE_C_CMP, CurveComposite) ; //---------------------------------------------------------------------------- CurveComposite::CurveComposite( void) : m_nStatus( TO_VERIFY), m_VtExtr(), m_dThick(), m_nTempProp(), m_Iter( m_CrvSmplS.end()) { } //---------------------------------------------------------------------------- CurveComposite::~CurveComposite( void) { Clear() ; } //---------------------------------------------------------------------------- bool CurveComposite::Clear( void) { // ciclo di pulizia for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++Iter) delete (*Iter) ; m_CrvSmplS.clear() ; m_nStatus = TO_VERIFY ; m_VtExtr = V_NULL ; m_dThick = 0 ; m_nTempProp = 0 ; m_Iter = m_CrvSmplS.end() ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::AddCurve( const ICurve& cCrv, bool bEndOrStart, double dLinTol) { // se curva semplice if ( cCrv.IsSimple()) { // creo una copia della curva ICurve* pSmplCrv = cCrv.Clone() ; if ( pSmplCrv == nullptr) return false ; // inserisco la curva if ( ! AddSimpleCurve( pSmplCrv, bEndOrStart, dLinTol)) return false ; } // altrimenti curva composta, devo aggiungere le singole curve semplici else { const ICurveComposite* pCrvCompo ; const ICurve* pCrvOri ; ICurve* pSmplCrv ; // recupero le curve componenti e le inserisco nella lista pCrvCompo = GetCurveComposite( &cCrv) ; if ( pCrvCompo == nullptr) return false ; pCrvOri = ( bEndOrStart ? pCrvCompo->GetFirstCurve() : pCrvCompo->GetLastCurve()) ; while ( pCrvOri != nullptr) { // creo una copia della curva pSmplCrv = pCrvOri->Clone() ; if ( pSmplCrv == nullptr) return false ; // inserisco la curva if ( ! AddSimpleCurve( pSmplCrv, bEndOrStart, dLinTol)) return false ; // passo alla prossima curva componente pCrvOri = ( bEndOrStart ? pCrvCompo->GetNextCurve() : pCrvCompo->GetPrevCurve()) ; } } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::AddCurve( ICurve* pCrv, bool bEndOrStart, double dLinTol) { // verifica curva if ( pCrv == nullptr) return false ; // se curva semplice if ( pCrv->IsSimple()) { // inserisco la curva if ( ! AddSimpleCurve( pCrv, bEndOrStart, dLinTol)) return false ; } // altrimenti curva composita, devo aggiungere le singole curve semplici else { // riloco le curve dalla composita sorgente alla corrente CurveComposite* pCrvCompo = dynamic_cast( pCrv) ; if ( ! AddCurveByRelocate( *pCrvCompo, bEndOrStart, dLinTol)) return false ; // cancello la curva composita originaria delete pCrvCompo ; } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::AddCurveByRelocate( CurveComposite& ccSrc, bool bEndOrStart, double dLinTol) { // verifica curva if ( &ccSrc == nullptr) return false ; // recupero le curve componenti e le inserisco nella lista ICurve* pSmplCrv ; while ( ( pSmplCrv = ccSrc.RemoveFirstOrLastCurve( ! bEndOrStart)) != nullptr) { // inserisco la curva if ( ! AddSimpleCurve( pSmplCrv, bEndOrStart, dLinTol)) return false ; } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::AddSimpleCurve( ICurve* pSmplCrv, bool bEndOrStart, double dLinTol) { // prendo la proprietà del puntatore PtrOwner pCrv( pSmplCrv) ; if ( IsNull( pCrv)) return false ; // verifico lo stato if ( m_nStatus != OK && ! ( m_CrvSmplS.empty() && m_nStatus == TO_VERIFY)) return false ; // verifico che il parametro sia una curva semplice if ( ! pCrv->IsSimple()) return false ; // annullo l'estrusione e lo spessore pCrv->SetExtrusion( V_NULL) ; pCrv->SetThickness( 0) ; // recupero i punti iniziali e finali della curva Point3d ptStart, ptEnd ; if ( ! pCrv->GetStartPoint( ptStart) || ! pCrv->GetEndPoint( ptEnd)) return false ; // se non è la prima if ( ! m_CrvSmplS.empty()) { // se inserita alla fine if ( bEndOrStart) { // verifico sia in continuità con il finale attuale Point3d ptEnd ; GetEndPoint( ptEnd) ; if ( ! AreSamePointApprox( ptStart, ptEnd)) { // se in tolleranza, modifico l'inizio dell'entità if ( SqDist( ptStart, ptEnd) < ( dLinTol * dLinTol)) { if ( ! pCrv->ModifyStart( ptEnd)) return false ; } else return false ; } } // altrimenti inserita all'inizio else { // verifico sia in continuità con l'iniziale attuale Point3d ptStart ; GetStartPoint( ptStart) ; if ( ! AreSamePointApprox( ptEnd, ptStart)) { // se in tolleranza, modifico la fine dell'entità if ( SqDist( ptEnd, ptStart) < ( dLinTol * dLinTol)) { if ( ! pCrv->ModifyEnd( ptStart)) return false ; } else return false ; } } } // inserisco la curva nella lista if ( bEndOrStart) m_CrvSmplS.push_back( ::Release( pCrv)) ; else m_CrvSmplS.push_front( ::Release( pCrv)) ; // aggiorno lo stato m_nStatus = OK ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::Close( void) { // verifico curva valida if ( m_nStatus != OK) return false ; // se già chiusa, non faccio alcunché if ( IsClosed()) return true ; // aggiungo la linea di chiusura PtrOwner pLine( CreateBasicCurveLine()) ; Point3d ptStart, ptEnd ; if ( ! GetStartPoint( ptStart) || ! GetEndPoint( ptEnd) || ! pLine->Set( ptEnd, ptStart) || ! AddSimpleCurve( Release( pLine))) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::FromSplit( const ICurve& cCrv, int nParts) { // verifico lo stato if ( ! m_CrvSmplS.empty() || m_nStatus != TO_VERIFY) return false ; // deve essere valida e semplice if ( ! cCrv.IsValid() || ! cCrv.IsSimple()) return false ; // se 1 parte o meno, non fa alcunchè if ( nParts <= 1) return true ; // calcolo la lunghezza della curva double dTotLen ; if ( ! cCrv.GetLength( dTotLen)) return false ; // ciclo di inserimento delle parti double dStartLen = 0 ; double dEndLen = 0 ; for ( int i = 1 ; i <= nParts ; ++ i) { dStartLen = dEndLen ; dEndLen = dTotLen * i / (double) nParts ; PtrOwner pSmplCrv( cCrv.Clone()) ; if ( IsNull( pSmplCrv)) return false ; if ( ! pSmplCrv->TrimEndAtLen( dEndLen) || ! pSmplCrv->TrimStartAtLen( dStartLen)) return false ; if ( ! AddSimpleCurve( Release( pSmplCrv))) return false ; } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::FromPolyLine( const PolyLine& PL) { // verifico lo stato if ( ! m_CrvSmplS.empty() || m_nStatus != TO_VERIFY) return false ; // deve esserci almeno 1 linea (2 punti) if ( PL.GetLineNbr() < 1) return false ; // ciclo di inserimento dei segmenti che uniscono i punti Point3d ptIni, ptFin ; PL.GetFirstPoint( ptIni) ; while ( PL.GetNextPoint( ptFin)) { // se i punti della coppia coincidono, passo alla coppia successiva if ( AreSamePointApprox( ptIni, ptFin)) continue ; // creo il segmento di retta PtrOwner pCrvLine( CreateCurveLine()) ; if ( IsNull( pCrvLine)) return false ; // assegno i punti estremi if ( ! pCrvLine->Set( ptIni, ptFin)) return false ; // aggiungo la retta alla curva composita if ( ! AddSimpleCurve( Release( pCrvLine))) return false ; // aggiorno dati prossimo punto iniziale ptIni = ptFin ; } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::FromPolyArc( const PolyArc& PA) { // verifico lo stato if ( ! m_CrvSmplS.empty() || m_nStatus != TO_VERIFY) return false ; // deve esserci almeno 1 arco (2 punti) if ( PA.GetArcNbr() < 1) return false ; // Creo le diverse curve semplici e le inserisco in quella composita double dBulge, dNextBulge ; Point3d ptIni, ptFin ; PA.GetFirstPoint( ptIni, dBulge) ; while ( PA.GetNextPoint( ptFin, dNextBulge)) { // se i punti della coppia coincidono, passo alla coppia successiva if ( AreSamePointApprox( ptIni, ptFin)) continue ; // se retta if ( fabs( dBulge) < EPS_SMALL) { // creo la retta PtrOwner 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 = ccSrc.m_nTempProp ; for ( auto& pCrv : ccSrc.m_CrvSmplS) { if ( ! AddCurve( *pCrv)) return false ; } return true ; } //---------------------------------------------------------------------------- bool CurveComposite::RelocateFrom( CurveComposite& ccSrc) { if ( &ccSrc == this) return true ; Clear() ; m_VtExtr = ccSrc.m_VtExtr ; m_dThick = ccSrc.m_dThick ; m_nTempProp = ccSrc.m_nTempProp ; for ( ICurve* pCrv = ccSrc.RemoveFirstOrLastCurve( false) ; pCrv != nullptr ; pCrv = ccSrc.RemoveFirstOrLastCurve( false)) { if ( ! AddSimpleCurve( pCrv)) return false ; } return true ; } //---------------------------------------------------------------------------- GeoObjType CurveComposite::GetType( void) const { return static_cast( GEOOBJ_GETTYPE( CurveComposite)) ; } //---------------------------------------------------------------------------- const string& CurveComposite::GetTitle( void) const { static const string sTitle = "CComposite" ; 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 ; // 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) { // assegno ed emetto nome e tipo della curva semplice sOut += "#" + ToString( ++i) + " " + pCrvSmpl->GetTitle() + szNewLine ; // salvataggio della curva semplice if ( ! pCrvSmpl->Dump( sOut, bMM, szNewLine)) return false ; // passo alla successiva pCrvSmpl = GetNextCurve() ; } 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) ; // se errore if ( ! bOk) return false ; } // da versione 1008 : linea con VtEstrusione e Spessore if ( ngeIn.GetFileVersion() >= NGE_VER_1008) { if ( ! ngeIn.ReadVector( m_VtExtr, ";")) return false ; if ( ! ngeIn.ReadDouble( m_dThick, ";", true)) return false ; } return true ; } //---------------------------------------------------------------------------- bool CurveComposite::GetLocalBBox( BBox3d& b3Loc, int nFlag) const { // verifico lo stato if ( m_nStatus != OK) return false ; // inizializzo il box b3Loc.Reset() ; // ciclo sulle curve componenti const ICurve* pCrvSmpl ; pCrvSmpl = GetFirstCurve() ; while ( pCrvSmpl != nullptr) { BBox3d b3Crv ; // recupero il box della curva pCrvSmpl->GetLocalBBox( b3Crv, nFlag) ; // aggiorno il box b3Loc.Add( b3Crv) ; // passo alla curva successiva pCrvSmpl = GetNextCurve() ; } // se c'è estrusione, devo tenerne conto (curve componenti sempre con vtExtr e dThick nulli) if ( ! m_VtExtr.IsSmall() && fabs( m_dThick) > EPS_SMALL) { Point3d ptMinExtr = b3Loc.GetMin() + m_VtExtr * m_dThick ; Point3d ptMaxExtr = b3Loc.GetMax() + m_VtExtr * m_dThick ; b3Loc.Add( ptMinExtr) ; b3Loc.Add( ptMaxExtr) ; } return true ; } //---------------------------------------------------------------------------- bool CurveComposite::GetBBox( const Frame3d& frRef, BBox3d& b3Ref, int nFlag) const { // verifico lo stato if ( m_nStatus != OK) return false ; // verifico validità del frame if ( frRef.GetType() == Frame3d::ERR) return false ; // inizializzo il box b3Ref.Reset() ; // ciclo sulle curve componenti (sempre senza estrusione) const ICurve* pCrvSmpl ; pCrvSmpl = GetFirstCurve() ; while ( pCrvSmpl != nullptr) { BBox3d b3Crv ; // recupero il box della curva pCrvSmpl->GetBBox( frRef, b3Crv, nFlag) ; // aggiorno il box b3Ref.Add( b3Crv) ; // passo alla curva successiva pCrvSmpl = GetNextCurve() ; } // se c'è estrusione, devo tenerne conto (curve componenti sempre con vtExtr e dThick nulli) if ( ! m_VtExtr.IsSmall() && fabs( m_dThick) > EPS_SMALL) { Vector3d vtFrExtr = m_VtExtr ; vtFrExtr.ToGlob( frRef) ; Point3d ptMinExtr = b3Ref.GetMin() + vtFrExtr * m_dThick ; Point3d ptMaxExtr = b3Ref.GetMax() + vtFrExtr * m_dThick ; b3Ref.Add( ptMinExtr) ; b3Ref.Add( ptMaxExtr) ; } return true ; } //---------------------------------------------------------------------------- bool CurveComposite::Validate( void) { if ( m_nStatus == TO_VERIFY) { Point3d ptPrevEnd ; Point3d ptStart ; // ciclo su tutte le curve int nCount = 0 ; PCSD_CONST_ITER Iter ; for ( Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++Iter) { // verifico validità della curva e sua semplicità if ( ! (*Iter)->IsValid() || (*Iter)->GetType() == CRV_COMPO) { m_nStatus = ERR ; return false ; } // incremento contatore ++ nCount ; // verifico continuità con la precedente (se non è la prima) if ( nCount > 1) { (*Iter)->GetStartPoint( ptStart) ; if ( ! AreSamePointApprox( ptPrevEnd, ptStart)) { m_nStatus = ERR ; return false ; } } // recupero il punto finale (*Iter)->GetEndPoint( ptPrevEnd) ; } // aggiorno m_nStatus = ( nCount > 0 ? OK : TO_VERIFY) ; } return ( m_nStatus == OK) ; } //---------------------------------------------------------------------------- bool CurveComposite::IsFlat( Plane3d& plPlane, double dToler) const { // verifico lo stato if ( m_nStatus != OK) return false ; // polilinea dei punti rappresentativi PolyLine shPL ; // punto iniziale Point3d ptP ; if ( ! GetStartPoint( ptP) || ! shPL.AddUPoint( 0, ptP)) return false ; // ciclo sulle curve semplici (aggiungo solo eventuali punti intermedi e finali) int nCount = 0 ; for ( const ICurve* pCrv = GetFirstCurve() ; pCrv != nullptr ; pCrv = GetNextCurve(), ++ nCount) { switch ( pCrv->GetType()) { case CRV_LINE : // punto finale if ( ! pCrv->GetEndPoint( ptP) || ! shPL.AddUPoint( nCount + 1, ptP)) return false ; break ; case CRV_ARC : // punto a 1/3 if ( ! pCrv->GetPointD1D2( 0.3333, ICurve::FROM_MINUS, ptP) || ! shPL.AddUPoint( nCount + 0.3333, ptP)) return false ; // punto a 7/11 if ( ! pCrv->GetPointD1D2( 0.6363, ICurve::FROM_MINUS, ptP) || ! shPL.AddUPoint( nCount + 0.6363, ptP)) return false ; // punto finale if ( ! pCrv->GetEndPoint( ptP) || ! shPL.AddUPoint( nCount + 1, ptP)) return false ; break ; case CRV_BEZ : { const CurveBezier* pBez = GetBasicCurveBezier( pCrv) ; // inserisco tutti i punti di controllo tranne il primo int nLastPC = pBez->GetDegree() ; for ( int i = 1 ; i <= nLastPC ; ++ i) { double dU = nCount + i / double( nLastPC) ; if ( ! shPL.AddUPoint( dU, pBez->GetControlPoint( i))) return false ; } } break ; } } // recupero dati sulla planarità della polilinea int nRank ; Point3d ptCen ; Vector3d vtDir ; bool bFlat = shPL.IsFlat( nRank, ptCen, vtDir, dToler) ; // se punto switch ( nRank) { case 0 : // punto if ( bFlat) { plPlane.vtN = ( m_VtExtr.IsSmall() ? Z_AX : m_VtExtr) ; plPlane.dDist = ( ptCen - ORIG) * plPlane.vtN ; } else { plPlane.vtN = ( m_VtExtr.IsSmall() ? V_NULL : m_VtExtr) ; plPlane.dDist = 0 ; } break ; case 1 : // linea if ( m_VtExtr.IsSmall()) { plPlane.vtN = FromUprightOrtho( vtDir) ; } else { plPlane.vtN = m_VtExtr ; bFlat = bFlat && ( AreOrthoApprox( m_VtExtr, vtDir)) ; } plPlane.dDist = ( ptCen - ORIG) * plPlane.vtN ; break ; default : // piana o 3d if ( m_VtExtr.IsSmall()) { plPlane.vtN = vtDir ; } else { plPlane.vtN = m_VtExtr ; bFlat = bFlat && ( AreSameOrOppositeVectorApprox( m_VtExtr, vtDir)) ; } plPlane.dDist = ( ptCen - ORIG) * plPlane.vtN ; break ; } return bFlat ; } //---------------------------------------------------------------------------- bool CurveComposite::IsClosed( void) const { // verifico lo stato if ( m_nStatus != OK || m_CrvSmplS.empty()) return false ; // ricavo punti iniziale e finale e li confronto Point3d ptStart, ptEnd ; return ( m_CrvSmplS.front()->GetStartPoint( ptStart) && m_CrvSmplS.back()->GetEndPoint( ptEnd) && AreSamePointApprox( ptStart, ptEnd)) ; } //---------------------------------------------------------------------------- bool CurveComposite::GetStartPoint( Point3d& ptStart) const { // verifico lo stato if ( m_nStatus != OK || m_CrvSmplS.empty()) return false ; // assegno il punto return m_CrvSmplS.front()->GetStartPoint( ptStart) ; } //---------------------------------------------------------------------------- bool CurveComposite::GetEndPoint( Point3d& ptEnd) const { // verifico lo stato if ( m_nStatus != OK || m_CrvSmplS.empty()) return false ; // assegno il punto return m_CrvSmplS.back()->GetEndPoint( ptEnd) ; } //---------------------------------------------------------------------------- bool CurveComposite::GetMidPoint( Point3d& ptMid) const { // verifico lo stato if ( m_nStatus != OK) return false ; // determino il valore del parametro a metà lunghezza double dLen, dMid ; if ( ! GetLength( dLen) || ! GetParamAtLength( 0.5 * dLen, dMid)) return false ; // calcolo il punto return GetPointD1D2( dMid, FROM_MINUS, ptMid) ; } //---------------------------------------------------------------------------- bool CurveComposite::GetCentroid( Point3d& ptCen) const { // verifico lo stato if ( m_nStatus != OK) return false ; // approssimo la curva con una polilinea PolyLine PL ; if ( ! ApproxWithLines( LIN_TOL_STD, ANG_TOL_STD_DEG, ICurve::APL_STD, PL)) return false ; // calcolo il centro mediante PolygonPlane Point3d ptP ; PolygonPlane PolyPlane ; for ( bool bFound = PL.GetFirstPoint( ptP) ; bFound ; bFound = PL.GetNextPoint( ptP)) PolyPlane.AddPoint( ptP) ; if ( PolyPlane.GetCentroid( ptCen)) return true ; // se non riuscito, uso il punto medio return GetMidPoint( ptCen) ; } //---------------------------------------------------------------------------- bool CurveComposite::GetStartDir( Vector3d& vtDir) const { // verifico lo stato if ( m_nStatus != OK || m_CrvSmplS.empty()) return false ; // assegno il punto return m_CrvSmplS.front()->GetStartDir( vtDir) ; } //---------------------------------------------------------------------------- bool CurveComposite::GetEndDir( Vector3d& vtDir) const { // verifico lo stato if ( m_nStatus != OK || m_CrvSmplS.empty()) return false ; // assegno il punto return m_CrvSmplS.back()->GetEndDir( vtDir) ; } //---------------------------------------------------------------------------- bool CurveComposite::GetMidDir( Vector3d& vtDir) const { // verifico lo stato if ( m_nStatus != OK) return false ; // determino il valore del parametro a metà lunghezza double dLen, dMid ; if ( ! GetLength( dLen) || ! GetParamAtLength( 0.5 * dLen, dMid)) return false ; // calcolo la direzione return ::GetTang( *this, 0.5 * m_CrvSmplS.size(), FROM_MINUS, vtDir) ; } //---------------------------------------------------------------------------- bool CurveComposite::GetDomain( double& dStart, double& dEnd) const { // verifico lo stato if ( m_nStatus != OK) return false ; // assegno gli estremi del dominio dStart = 0.0 ; dEnd = double( m_CrvSmplS.size()) ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::GetIndSCurveAndLocPar( double dU, Side nS, int& nSCrv, double& dLocU) const { // 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 ( fabs( dLocU) < EPS_PARAM && nSCrv > 0 && nS == ICurve::FROM_MINUS) { -- nSCrv ; dLocU += 1 ; } return true ; } //---------------------------------------------------------------------------- bool CurveComposite::GetPointD1D2( double dU, Side nS, Point3d& ptPos, Vector3d* pvtDer1, Vector3d* pvtDer2) const { // verifico lo stato if ( m_nStatus != OK) return false ; // determino la curva di appartenenza e il valore locale del parametro int nC ; double dUc ; if ( ! GetIndSCurveAndLocPar( dU, nS, nC, dUc)) return false ; // eseguo il calcolo sulla curva semplice return m_CrvSmplS[nC]->GetPointD1D2( dUc, ICurve::FROM_MINUS, ptPos, pvtDer1, pvtDer2) ; } //---------------------------------------------------------------------------- bool CurveComposite::GetLength( double& dLen) const { // verifico lo stato if ( m_nStatus != OK) return false ; // ciclo di calcolo dLen = 0 ; PCSD_CONST_ITER Iter ; for ( Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++Iter) { double dLenCrvSmpl ; if ( (*Iter)->GetLength( dLenCrvSmpl)) dLen += dLenCrvSmpl ; else return false ; } return true ; } //---------------------------------------------------------------------------- bool CurveComposite::GetLengthAtParam( double dU, double& dLen) const { // verifico lo stato if ( m_nStatus != OK) return false ; // il parametro U deve essere compreso tra 0 e m_nCounter if ( dU < - EPS_PARAM || dU > ( double( m_CrvSmplS.size()) + EPS_PARAM)) return false ; // ciclo di calcolo dLen = 0 ; double dUToGo = dU ; PCSD_CONST_ITER Iter ; for ( Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++Iter) { // dominio parametrico della curva semplice double dParStart, dParEnd ; (*Iter)->GetDomain( dParStart, dParEnd) ; // se resto nella curva if ( dUToGo <= ( dParEnd - dParStart)) { double dParamLen ; if ( ! (*Iter)->GetLengthAtParam( dUToGo, dParamLen)) return false ; dLen += dParamLen ; break ; } // altrimenti else { // lunghezza parametrica rimanente dUToGo -= ( dParEnd - dParStart) ; // lunghezza progressiva double dCrvLen ; if ( ! (*Iter)->GetLength( dCrvLen)) return false ; dLen += dCrvLen ; } } return true ; } //---------------------------------------------------------------------------- bool CurveComposite::GetParamAtLength( double dLen, double& dU) const { // verifico lo stato if ( m_nStatus != OK) return false ; // se prima di inizio, errore if ( dLen < - EPS_SMALL) return false ; // inizio if ( dLen < EPS_SMALL) { dU = 0 ; return true ; } // ciclo di calcolo dU = 0 ; double dLenToGo = dLen ; PCSD_CONST_ITER Iter ; for ( Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++Iter) { // lunghezza della curva semplice double dCrvLen ; if ( ! (*Iter)->GetLength( dCrvLen)) return false ; // se resto nella curva if ( dLenToGo <= dCrvLen) { double dULen ; if ( ! (*Iter)->GetParamAtLength( dLenToGo, dULen)) return false ; dU += dULen ; break ; } // altrimenti else { // lunghezza rimanente dLenToGo -= dCrvLen ; // lunghezza parametrica progressiva double dParStart, dParEnd ; (*Iter)->GetDomain( dParStart, dParEnd) ; dU += ( dParEnd - dParStart) ; } } return true ; } //---------------------------------------------------------------------------- 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) double dSqDist ; dTol = max( dTol, EPS_ZERO) ; return ( DistPointCrvComposite( ptP, *this).GetSqDist( dSqDist) && dSqDist < dTol * dTol) ; } //---------------------------------------------------------------------------- bool CurveComposite::GetParamAtPoint( const Point3d& ptP, double& dPar, double dTol) const { double dSqDist ; dTol = max( dTol, EPS_ZERO) ; DistPointCrvComposite DPC( ptP, *this) ; if ( ! DPC.GetSqDist( dSqDist) || dSqDist > dTol * 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 ; // eseguo approssimazione double dStartPar = 0 ; for ( auto& pCrv : m_CrvSmplS) { // assegno estrusione e spessore della curva composita pCrv->SetExtrusion( m_VtExtr) ; pCrv->SetThickness( m_dThick) ; // recupero approssimazione per curva semplice PolyLine PLSmpl ; if ( ! pCrv->ApproxWithLines( dLinTol, dAngTolDeg, nType, PLSmpl)) return false ; // ripristino estrusione e spessore della curva semplice (annullandoli) pCrv->SetExtrusion( V_NULL) ; pCrv->SetThickness( 0) ; // la accodo opportunamente a quella della curva composita if ( ! PL.Join( PLSmpl, dStartPar)) return false ; // incremento inizio parametro per prossima curva semplice dStartPar += 1 ; } // se necessario, sistemo per convessità dalla parte ammessa if ( nType == APL_RIGHT_CONVEX || nType == APL_LEFT_CONVEX) { Vector3d vtExtr = ( m_VtExtr.IsSmall() ? Z_AX : m_VtExtr) ; if ( ! PL.MakeConvex( vtExtr, ( nType == APL_LEFT_CONVEX))) return false ; } return true ; } //---------------------------------------------------------------------------- bool CurveComposite::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const { // pulisco il poliarco PA.Clear() ; // verifico lo stato if ( m_nStatus != OK) return false ; // eseguo approssimazione double dStartPar = 0 ; for ( auto& pCrv : m_CrvSmplS) { // assegno estrusione e spessore della curva composita pCrv->SetExtrusion( m_VtExtr) ; pCrv->SetThickness( m_dThick) ; // recupero approssimazione per curva semplice PolyArc PASmpl ; if ( ! pCrv->ApproxWithArcs( dLinTol, dAngTolDeg, PASmpl)) return false ; // la accodo opportunamente a quella della curva composita if ( ! PA.Join( PASmpl, dStartPar)) return false ; // ripristino estrusione e spessore della curva semplice (annullandoli) pCrv->SetExtrusion( V_NULL) ; pCrv->SetThickness( 0) ; // incremento inizio parametro per prossima curva semplice dStartPar += 1 ; } // assegno estrusione della curva composita PA.SetExtrusion( m_VtExtr) ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::ApproxWithArcsEx( double dLinTol, double dAngTolDeg, double dLinFea, PolyArc& PA) const { // pulisco il poliarco PA.Clear() ; // verifico lo stato if ( m_nStatus != OK) return false ; // preparazione per approssimazione di multilinee bool bMultiLine = false ; double dMlStartPar = 0 ; CurveByApprox crvByApprox ; // eseguo approssimazione double dStartPar = 0 ; for ( auto& pCrv : m_CrvSmplS) { // assegno estrusione e spessore della curva composita pCrv->SetExtrusion( m_VtExtr) ; pCrv->SetThickness( m_dThick) ; // se segmento di linea non feature double dLen ; if ( pCrv->GetType() == CRV_LINE && pCrv->GetLength( dLen) && dLen < dLinFea) { CurveLine* pLine = GetBasicCurveLine( pCrv) ; // se inizio di approx multilinea if ( ! bMultiLine) { bMultiLine = true ; dMlStartPar = dStartPar ; crvByApprox.Reset() ; crvByApprox.AddPoint( pLine->GetStart()) ; } // aggiungo il punto finale crvByApprox.AddPoint( pLine->GetEnd()) ; } // altrimenti else { // se in corso approx multilinee if ( bMultiLine) { bMultiLine = false ; PolyArc PASmpl ; if ( ! crvByApprox.GetArcs( dLinTol, dAngTolDeg, dLinFea, PASmpl)) return false ; // la accodo opportunamente a quella della curva composita if ( ! PA.Join( PASmpl, dMlStartPar)) return false ; } // recupero approssimazione per curva semplice PolyArc PASmpl ; if ( ! pCrv->ApproxWithArcs( dLinTol, dAngTolDeg, PASmpl)) return false ; // la accodo opportunamente a quella della curva composita if ( ! PA.Join( PASmpl, dStartPar)) return false ; } // ripristino estrusione e spessore della curva semplice (annullandoli) pCrv->SetExtrusion( V_NULL) ; pCrv->SetThickness( 0) ; // incremento inizio parametro per prossima curva semplice dStartPar += 1 ; } // se approssimazione multiline ancora aperta if ( bMultiLine) { bMultiLine = false ; PolyArc PASmpl ; if ( ! crvByApprox.GetArcs( dLinTol, dAngTolDeg, dLinFea, PASmpl)) return false ; // la accodo opportunamente a quella della curva composita if ( ! PA.Join( PASmpl, dMlStartPar)) return false ; } // assegno estrusione della curva composita PA.SetExtrusion( m_VtExtr) ; return true ; } //---------------------------------------------------------------------------- ICurve* CurveComposite::CopyParamRange( double dUStart, double dUEnd) const { // valore massimo del parametro double dMaxU = double( m_CrvSmplS.size()) ; // i parametri start ed end devono essere compresi nel dominio parametrico della curva if ( dUStart < - EPS_PARAM || dUStart > dMaxU + EPS_PARAM || dUEnd < - EPS_PARAM || dUEnd > dMaxU + EPS_PARAM) return nullptr ; // se il parametro start supera quello di end if ( dUStart > dUEnd - EPS_PARAM) { // se curva aperta, il trim la cancella completamente quindi non resta alcunchè if ( ! IsClosed()) return nullptr ; // se curva chiusa, il trim si avvolge attorno al punto di giunzione else // incremento il parametro finale della dimensione del dominio parametrico originale della curva dUEnd += dMaxU ; } // determino gli indici della prima e dell'ultima curva da copiare int nIdStart = static_cast( floor( dUStart)) ; int nIdEnd = static_cast( ceil( dUEnd)) ; // creo la curva composita copia PtrOwner pCopy( new CurveComposite()) ; if ( IsNull( pCopy)) return nullptr ; // eseguo la copia delle sole curve semplici necessarie for ( int i = nIdStart ; i < nIdEnd ; ++ i) { // quando supero il numero di curve nella composita originale riparto dall'inizio int j = i % m_CrvSmplS.size() ; // accodo la curva alla copia if ( ! pCopy->AddCurve( *(m_CrvSmplS[j]))) return nullptr ; } // aggiorno i parametri di trim, tenendo conto delle curve semplici saltate all'inizio dUStart -= nIdStart ; dUEnd -= nIdStart ; // eseguo il trim della copia if ( ! pCopy->TrimStartEndAtParam( dUStart, dUEnd)) return nullptr ; return ( ::Release( pCopy)) ; } //---------------------------------------------------------------------------- bool CurveComposite::Invert( void) { // verifico lo stato if ( m_nStatus != OK) return false ; // inverto le singole curve for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++Iter) (*Iter)->Invert() ; // inverto l'ordine della lista reverse( m_CrvSmplS.begin(), m_CrvSmplS.end()) ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::SimpleOffset( double dDist, int nType) { // se distanza di offset nulla, non devo fare alcunché if ( fabs( dDist) < EPS_SMALL) return true ; // --- l'offset va effettuato in un piano perpendicolare al vettore estrusione --- // verifico il vettore estrusione bool bNeedRef = ( ! m_VtExtr.IsSmall() && ! m_VtExtr.IsZplus()) ; // se necessario cambio il riferimento Frame3d frExtr ; if ( bNeedRef) { // calcolo il riferimento OCS con VtExtr come asse Z if ( ! frExtr.Set( ORIG, m_VtExtr)) return false ; // esprimo la curva in questo riferimento ToLoc( frExtr) ; } // eseguo l'offset nel piano XY bool bOk = SimpleOffsetXY( dDist, nType) ; // riporto la curva nel riferimento originale if ( bNeedRef) ToGlob( frExtr) ; return bOk ; } //---------------------------------------------------------------------------- bool CurveComposite::ModifyStart( const Point3d& ptNewStart) { // verifico lo stato if ( m_nStatus != OK) return false ; // recupero la prima curva if ( m_CrvSmplS.empty()) return false ; ICurve* pCrv = m_CrvSmplS.front() ; // modifico l'inizio di questa curva if ( ! pCrv->ModifyStart( ptNewStart)) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::ModifyEnd( const Point3d& ptNewEnd) { // verifico lo stato if ( m_nStatus != OK) return false ; // recupero l'ultima curva if ( m_CrvSmplS.empty()) return false ; ICurve* pCrv = m_CrvSmplS.back() ; // modifico la fine di questa curva if ( ! pCrv->ModifyEnd( ptNewEnd)) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::TrimStartAtParam( double dUTrim) { // ciclo sulle diverse curve dall'inizio double dUToTrim = dUTrim ; for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ;) { // dominio parametrico della curva semplice double dParStart, dParEnd ; (*Iter)->GetDomain( dParStart, dParEnd) ; // lunghezza parametrica progressiva dUToTrim -= ( dParEnd - dParStart) ; // se lunghezza ancora da tagliare non nulla if ( dUToTrim > EPS_PARAM) { delete (*Iter) ; Iter ++ ; m_CrvSmplS.pop_front() ; } // se lunghezza ancora da tagliare nulla (entro la tolleranza) else if ( dUToTrim > - EPS_PARAM || ! (*Iter)->TrimStartAtParam( 1 + dUToTrim)) { delete (*Iter) ; Iter ++ ; m_CrvSmplS.pop_front() ; if ( m_CrvSmplS.empty()) return false ; break ; } // altrimenti superata lunghezza ancora da tagliare (taglio già fatto al test sopra) else { break ; } } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::TrimEndAtParam( double dUTrim) { // ciclo sulle diverse curve dalla fine bool bToErase = false ; double dUToTrim = dUTrim ; for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ;) { // dominio parametrico della curva semplice double dParStart, dParEnd ; (*Iter)->GetDomain( dParStart, dParEnd) ; // lunghezza parametrica progressiva dUToTrim -= ( dParEnd - dParStart) ; // se da cancellare if ( bToErase) { // cancello l'entità, la tolgo dalla lista e passo alla successiva delete (*Iter) ; Iter = m_CrvSmplS.erase( Iter) ; } // se lunghezza parametrica ancora da tagliare non nulla else if ( dUToTrim > EPS_PARAM) { Iter ++ ; } // se lunghezza parametrica ancora da tagliare nulla (entro la tolleranza) else if ( dUToTrim > - EPS_PARAM) { // passo alla entità successiva ++ Iter ; // dichiaro ingresso in zona da cancellare bToErase = true ; } // altrimenti superata lunghezza parametrica ancora da tagliare else { // trimmo la curva semplice if ( ! (*Iter)->TrimEndAtParam( 1 + dUToTrim)) { // se trim fallito, rimaneva troppo poco e quindi la cancello bToErase = true ; continue ; } // passo alla entità successiva ++ Iter ; // dichiaro ingresso in zona da cancellare bToErase = true ; } } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::TrimStartEndAtParam( double dUStartTrim, double dUEndTrim) { // valore massimo del parametro double dMaxU = double( m_CrvSmplS.size()) ; // i parametri start ed end devono essere compresi nel dominio parametrico della curva if ( dUStartTrim < - EPS_PARAM || dUStartTrim > dMaxU + EPS_PARAM || dUEndTrim < - EPS_PARAM || dUEndTrim > dMaxU + EPS_PARAM) return false ; // se il parametro start supera quello di end if ( dUStartTrim > dUEndTrim - EPS_PARAM) { // se curva aperta, il trim la cancella completamente quindi errore if ( ! IsClosed()) return false ; // se curva chiusa, il trim interessa il punto di giunzione else { // calcolo il minimo numero di curve da accodare per coprire il range esteso int nNumCrv = static_cast( 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) { // ciclo sulle diverse curve dall'inizio double dLenToTrim = dLenTrim ; for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ;) { // lunghezza della curva double dCrvLen ; if ( ! (*Iter)->GetLength( dCrvLen)) return false ; // lunghezza progressiva dLenToTrim -= dCrvLen ; // se lunghezza ancora da tagliare non nulla if ( dLenToTrim > EPS_SMALL) { delete (*Iter) ; Iter ++ ; m_CrvSmplS.pop_front() ; if ( m_CrvSmplS.empty()) return false ; } // se lunghezza ancora da tagliare nulla (entro la tolleranza) else if ( dLenToTrim > - EPS_SMALL) { delete (*Iter) ; Iter ++ ; m_CrvSmplS.pop_front() ; if ( m_CrvSmplS.empty()) return false ; break ; } // altrimenti superata lunghezza ancora da tagliare else { if ( ! (*Iter)->TrimStartAtLen( dCrvLen + dLenToTrim)) { m_nStatus = ERR ; return false ; } break ; } } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::TrimEndAtLen( double dLenTrim) { // ciclo sulle diverse curve dalla fine bool bToErase = false ; double dLenToTrim = dLenTrim ; for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ;) { double dCrvLen ; // se non sono già nella zona da cancellare, aggiorno lunghezze if ( ! bToErase) { // lunghezza della curva if ( ! (*Iter)->GetLength( dCrvLen)) return false ; // lunghezza progressiva dLenToTrim -= dCrvLen ; } // se da cancellare if ( bToErase) { // cancello l'entità, la tolgo dalla lista e passo alla successiva delete (*Iter) ; Iter = m_CrvSmplS.erase( Iter) ; } // se lunghezza ancora da tagliare non nulla else if ( dLenToTrim > EPS_SMALL) { // passo alla entità successiva ++ Iter ; } // se lunghezza ancora da tagliare nulla (entro la tolleranza) else if ( dLenToTrim > - EPS_SMALL) { // passo alla entità successiva ++ Iter ; // dichiaro ingresso in zona da cancellare bToErase = true ; } // altrimenti superata lunghezza ancora da tagliare else { // trimmo la curva semplice if ( ! (*Iter)->TrimEndAtLen( dCrvLen + dLenToTrim)) { m_nStatus = ERR ; return false ; } // passo alla entità successiva ++ Iter ; // dichiaro ingresso in zona da cancellare bToErase = true ; } } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::ExtendStartByLen( double dLenExt) { // la lunghezza di estensione deve essere positiva if ( dLenExt < - EPS_ZERO) return false ; // recupero la prima curva ed estendo quella if ( m_CrvSmplS.begin() == m_CrvSmplS.end()) return false ; // estendo la prima curva if ( ! (*m_CrvSmplS.begin())->ExtendStartByLen( dLenExt)) { m_nStatus = ERR ; return false ; } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::ExtendEndByLen( double dLenExt) { // la lunghezza di estensione deve essere positiva if ( dLenExt < - EPS_ZERO) return false ; // vado sulla fine auto Iter = m_CrvSmplS.end() ; if ( Iter == m_CrvSmplS.begin()) return false ; -- Iter ; // recupero la curva if ( Iter == m_CrvSmplS.end()) return false ; // estendo l'ultima curva if ( ! (*Iter)->ExtendEndByLen( dLenExt)) { m_nStatus = ERR ; return false ; } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::Translate( const Vector3d& vtMove) { // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // traslo le singole curve for ( auto& pCrv : m_CrvSmplS) pCrv->Translate( vtMove) ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, double dSinAng) { // verifico validità dell'asse di rotazione if ( vtAx.IsSmall()) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // ruoto le singole curve for ( auto& pCrv : m_CrvSmplS) pCrv->Rotate( ptAx, vtAx, dCosAng, dSinAng) ; // ruoto il vettore estrusione m_VtExtr.Rotate( vtAx, dCosAng, dSinAng) ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double dCoeffZ) { // verifico non sia nulla if ( fabs( dCoeffX) < EPS_ZERO && fabs( dCoeffY) < EPS_ZERO && fabs( dCoeffZ) < EPS_ZERO) return false ; // calcolo bbox allineato con riferimento di scalatura e senza tener conto dello spessore // lo scalo per verificare se tutto si riduce a un punto BBox3d b3Ref ; double dOriThick = 0 ; swap( dOriThick, m_dThick) ; if ( ! GetBBox( frRef, b3Ref)) return false ; swap( dOriThick, m_dThick) ; Vector3d vtDelta = b3Ref.GetMax() - b3Ref.GetMin() ; if ( fabs( vtDelta.x * dCoeffX) < EPS_SMALL && fabs( vtDelta.y * dCoeffY) < EPS_SMALL && fabs( vtDelta.z * dCoeffZ) < EPS_SMALL) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // se scalatura non omogenea, devo trasformare tutti gli archi in curve di Bezier if ( fabs( dCoeffX - dCoeffY) > EPS_SMALL || fabs( dCoeffX - dCoeffZ) > EPS_SMALL) { if ( ! ArcsToBezierCurves()) return false ; } // scalo le singole curve for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ) { // eseguo la scalatura bool bOk = (*Iter)->Scale( frRef, dCoeffX, dCoeffY, dCoeffZ) ; // se tutto bene, passo alla prossima if ( bOk) ++Iter ; // altrimenti, l'entità si è annullata => devo toglierla dalla lista e cancellarla else { // si è annullata l'entità, la elimino e la tolgo dalla lista delete (*Iter) ; Iter = m_CrvSmplS.erase( Iter) ; } } // scalo vettore estrusione, lo normalizzo e aggiusto spessore m_VtExtr.Scale( frRef, dCoeffX, dCoeffY, dCoeffZ) ; double dLen = m_VtExtr.Len() ; if ( dLen > EPS_ZERO) { m_VtExtr /= dLen ; m_dThick *= dLen ; } m_nStatus = TO_VERIFY ; return Validate() ; } //---------------------------------------------------------------------------- bool CurveComposite::Mirror( const Point3d& ptOn, const Vector3d& vtNorm) { // verifico validità del piano di specchiatura if ( vtNorm.IsSmall()) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // specchio le singole curve for ( auto& pCrv : m_CrvSmplS) pCrv->Mirror( ptOn, vtNorm) ; // specchio il vettore estrusione m_VtExtr.Mirror( vtNorm) ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::Shear( const Point3d& ptOn, const Vector3d& vtNorm, const Vector3d& vtDir, double dCoeff) { // verifico validità dei parametri if ( vtNorm.IsSmall() || vtDir.IsSmall()) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // trasformo tutti gli archi in curve di Bezier (potrei controllare gli archi uno ad uno...) if ( ! ArcsToBezierCurves()) return false ; // eseguo scorrimento delle singole curve for ( auto& pCrv : m_CrvSmplS) pCrv->Shear( ptOn, vtNorm, vtDir, dCoeff) ; // eseguo scorrimento del vettore estrusione, lo normalizzo e aggiusto spessore m_VtExtr.Shear( vtNorm, vtDir, dCoeff) ; double dLen = m_VtExtr.Len() ; if ( dLen > EPS_ZERO) { m_VtExtr /= dLen ; m_dThick *= dLen ; } return true ; } //---------------------------------------------------------------------------- bool CurveComposite::ToGlob( const Frame3d& frRef) { // verifico validità del frame if ( frRef.GetType() == Frame3d::ERR) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // trasformo le singole curve for ( auto& pCrv : m_CrvSmplS) pCrv->ToGlob( frRef) ; // trasformo il vettore estrusione m_VtExtr.ToGlob( frRef) ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::ToLoc( const Frame3d& frRef) { // verifico validità del frame if ( frRef.GetType() == Frame3d::ERR) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // trasformo le singole curve for ( auto& pCrv : m_CrvSmplS) pCrv->ToLoc( frRef) ; // trasformo il vettore estrusione m_VtExtr.ToLoc( frRef) ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::LocToLoc( const Frame3d& frOri, const Frame3d& frDest) { // verifico validità dei frame if ( frOri.GetType() == Frame3d::ERR || frDest.GetType() == Frame3d::ERR) return false ; // se i due riferimenti coincidono, non devo fare alcunché if ( AreSameFrame( frOri, frDest)) return true ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // trasformo le singole curve for ( auto& pCrv : m_CrvSmplS) pCrv->LocToLoc( frOri, frDest) ; // trasformo il vettore estrusione m_VtExtr.LocToLoc( frOri, frDest) ; return true ; } //---------------------------------------------------------------------------- const ICurve* CurveComposite::GetCurve( int nCrv) const { // la curva deve essere validata if ( m_nStatus != OK) return nullptr ; // verifico che l'indice sia nei limiti if ( nCrv < 0 || nCrv >= int( m_CrvSmplS.size())) return nullptr ; // restituisco la curva return m_CrvSmplS[nCrv] ; } //---------------------------------------------------------------------------- const ICurve* CurveComposite::GetFirstCurve( void) const { // la curva deve essere validata if ( m_nStatus != OK) return nullptr ; // vado all'inizio m_Iter = m_CrvSmplS.begin() ; // recupero la curva if ( m_Iter != m_CrvSmplS.end()) return (*m_Iter) ; else return nullptr ; } //---------------------------------------------------------------------------- const ICurve* CurveComposite::GetNextCurve( void) const { // la curva deve essere validata if ( m_nStatus != OK) return nullptr ; // vado al successivo if ( m_Iter == m_CrvSmplS.end()) return nullptr ; ++ m_Iter ; // recupero la curva if ( m_Iter != m_CrvSmplS.end()) return (*m_Iter) ; else return nullptr ; } //---------------------------------------------------------------------------- const ICurve* CurveComposite::GetLastCurve( void) const { // la curva deve essere validata if ( m_nStatus != OK) return nullptr ; // vado alla fine m_Iter = m_CrvSmplS.end() ; if ( m_Iter == m_CrvSmplS.begin()) return nullptr ; -- m_Iter ; // recupero la curva if ( m_Iter != m_CrvSmplS.end()) return (*m_Iter) ; else return nullptr ; } //---------------------------------------------------------------------------- const ICurve* CurveComposite::GetPrevCurve( void) const { // la curva deve essere validata if ( m_nStatus != OK) return nullptr ; // vado al precedente if ( m_Iter == m_CrvSmplS.begin() || m_Iter == m_CrvSmplS.end()) return nullptr ; -- m_Iter ; // recupero la curva if ( m_Iter != m_CrvSmplS.end()) return (*m_Iter) ; else return nullptr ; } //---------------------------------------------------------------------------- ICurve* CurveComposite::RemoveFirstOrLastCurve( bool bLast) { // la curva composita deve essere validata if ( m_nStatus != OK) return nullptr ; // recupero la curva semplice iniziale o finale e la tolgo dalla lista if ( ! m_CrvSmplS.empty()) { ICurve* pCrv ; if ( bLast) { // recupero la curva pCrv = *(m_CrvSmplS.rbegin()) ; // la tolgo dalla lista m_CrvSmplS.pop_back() ; } else { // recupero la curva pCrv = *(m_CrvSmplS.begin()) ; // la tolgo dalla lista m_CrvSmplS.pop_front() ; } // eseguo mini verifica m_nStatus = ( m_CrvSmplS.size() > 0 ? OK : TO_VERIFY) ; // assegno estrusione e spessore della curva composita pCrv->SetExtrusion( m_VtExtr) ; pCrv->SetThickness( m_dThick) ; return pCrv ; } else return nullptr ; } //---------------------------------------------------------------------------- bool CurveComposite::IsParamAtJoint( double dU) const { // se aperta e all'inizio o alla fine o lontano dagli interi non è giunzione bool bClosed = IsClosed() ; if ( ( ! bClosed && fabs( dU) < EPS_PARAM) || ( ! bClosed && fabs( dU - double( m_CrvSmplS.size())) < EPS_PARAM) || fabs( dU - (int) ( dU + EPS_PARAM)) > EPS_PARAM) return false ; else return true ; } //---------------------------------------------------------------------------- bool CurveComposite::ChangeStartPoint( double dU) { // la curva deve essere chiusa (ne verifica anche lo stato) if ( ! IsClosed()) return false ; // questa funzione gestisce già anche il cambio di inizio su curve chiuse return TrimStartEndAtParam( dU, dU) ; } //---------------------------------------------------------------------------- bool CurveComposite::ArcsToBezierCurves( void) { // verifico le singole curve for ( PCSD_ITER 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( Get( pNewCrv)) ; if ( pCC == nullptr) return false ; // inserisco le curve prima dell'arco for ( PCSD_ITER Iter2 = pCC->m_CrvSmplS.begin() ; Iter2 != pCC->m_CrvSmplS.end() ; ++ Iter2) { Iter = m_CrvSmplS.insert( Iter, (*Iter2)) ; ++ Iter ; } pCC->m_CrvSmplS.clear() ; // elimino l'arco (e sposto l'iteratore alla curva precedente) delete (*Iter) ; Iter = m_CrvSmplS.erase( Iter) ; -- Iter ; } } } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::ArcsBezierCurvesToArcsPerpExtr( double dLinTol, double dAngTolDeg) { // verifico le singole curve for ( auto Iter = m_CrvSmplS.begin() ; Iter != m_CrvSmplS.end() ; ++ Iter) { // se arco in piano non perpendicolare ad estrusione o curva di Bezier trasformo if ( ( (*Iter)->GetType() == CRV_ARC && ! GetBasicCurveArc((*Iter))->IsInPlanePerpExtr()) || (*Iter)->GetType() == CRV_BEZ) { // eseguo trasformazione PtrOwner pNewCrv( CurveToArcsPerpExtrCurve( (*Iter), dLinTol, dAngTolDeg)) ; 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( Get( pNewCrv)) ; if ( pCC == nullptr) return false ; // inserisco le curve prima dell'originale PCSD_ITER Iter2 ; for ( Iter2 = pCC->m_CrvSmplS.begin() ; Iter2 != pCC->m_CrvSmplS.end() ; ++ Iter2) { Iter = m_CrvSmplS.insert( Iter, (*Iter2)) ; ++ Iter ; } pCC->m_CrvSmplS.clear() ; // elimino l'arco (e sposto l'iteratore alla curva precedente) delete (*Iter) ; Iter = m_CrvSmplS.erase( Iter) ; -- Iter ; } } } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::MergeCurves( double dLinTol, double dAngTolDeg) { // se non ci sono almeno 2 curve, esco subito if ( m_CrvSmplS.size() < 2) return true ; // controllo sui limiti di tolleranza dLinTol = max( dLinTol, EPS_SMALL) ; dAngTolDeg = max( dAngTolDeg, EPS_ANG_SMALL) ; // tolleranza lineare corrente double dCurrLinTol = dLinTol ; // devo verificare coppie di curve auto iterP = m_CrvSmplS.begin() ; auto iterC = next( iterP) ; // mentre esiste la coppia while ( iterC != m_CrvSmplS.end()) { // se curve unite if ( MergeTwoCurves( *iterP, *iterC, dCurrLinTol, dAngTolDeg)) { // cancello l'entità precedente e la tolgo dalla lista delete (*iterP) ; iterC = m_CrvSmplS.erase( iterP) ; } // altrimenti ripristino la tolleranza else dCurrLinTol = dLinTol ; // avanzo iterP = iterC ; iterC = next( iterC) ; } // se curva chiusa devo confrontare anche ultima e prima curva if ( IsClosed()) { if ( MergeTwoCurves( *iterP, *(m_CrvSmplS.begin()), dCurrLinTol, dAngTolDeg)) { // cancello l'entità precedente e la tolgo dalla lista delete (*iterP) ; iterC = m_CrvSmplS.erase( iterP) ; } } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveComposite::MergeTwoCurves( ICurve* pCrvP, ICurve* pCrvC, double& dCurrLinTol, double dAngTolDeg) { // coefficiente deduzione tolleranza const double COEFF_TOL = 0.7 ; // se entrambe rette if ( pCrvP->GetType() == CRV_LINE && pCrvC->GetType() == CRV_LINE) { CurveLine* pLineP = GetBasicCurveLine( pCrvP) ; CurveLine* pLineC = GetBasicCurveLine( pCrvC) ; // verifico se allineate Vector3d vtDirP, vtDirC ; if ( pLineP != nullptr && pLineP->GetStartDir( vtDirP) && pLineC != nullptr && pLineC->GetStartDir( vtDirC) && ( vtDirP * vtDirC) >= cos( dAngTolDeg * DEGTORAD)) { // verifico se il punto di giunzione sia eliminabile DistPointLine dPL( pLineP->GetEnd(), pLineP->GetStart(), pLineC->GetEnd()) ; double dSqDist ; // se da eliminare if ( dPL.GetSqDist( dSqDist) && dSqDist < dCurrLinTol * dCurrLinTol) { // se calcolo nuova linea ok, procedo con l'unione CurveLine NewLine ; if ( NewLine.Set( pLineP->GetStart(), pLineC->GetEnd())) { // diminuisco la tolleranza corrente dell'errore attuale dCurrLinTol -= COEFF_TOL * sqrt( dSqDist) ; // aggiorno la linea corrente e torno flag modifica *pLineC = NewLine ; return true ; } } } } // se entrambi archi else if ( pCrvP->GetType() == CRV_ARC && pCrvC->GetType() == CRV_ARC) { CurveArc* pArcP = GetBasicCurveArc( pCrvP) ; CurveArc* pArcC = GetBasicCurveArc( pCrvC) ; // centro del primo arco riportato alla quota del punto finale sulla normale dello stesso Point3d ptC1Fin = pArcP->GetCenter() + pArcP->GetNormVersor() * pArcP->GetDeltaN() ; // centro del secondo arco alla quota del punto iniziale sulla normale dello stesso Point3d ptC2Ini = pArcC->GetCenter() ; // verifico la coincidenza dei centri if ( ! AreSamePointEpsilon( ptC1Fin, ptC2Ini, dCurrLinTol)) return false ; // verifico la coincidenza dei raggi if ( fabs( pArcP->GetRadius() - pArcC->GetRadius()) > dCurrLinTol) return false ; // verifico la collinearità delle normali (tenendo conto del raggio) if ( ! (( pArcP->GetNormVersor() - pArcC->GetNormVersor()) * pArcP->GetRadius()).IsSmall() && ! (( pArcP->GetNormVersor() + pArcC->GetNormVersor()) * pArcP->GetRadius()).IsSmall()) return false ; // verifico la coincidenza del senso di rotazione (verso delle normali e segno angoli al centro) if ( pArcP->GetNormVersor() * pArcC->GetNormVersor() * pArcP->GetAngCenter() * pArcC->GetAngCenter() < 0) return false ; // se archi piatti if ( pArcP->IsFlat() && pArcC->IsFlat()) { // se calcolo nuovo arco ok, procedo con l'unione Point3d ptP1 ; pArcP->GetStartPoint( ptP1) ; Point3d ptP2 ; pArcP->GetEndPoint( ptP2) ; Point3d ptP3 ; pArcC->GetEndPoint( ptP3) ; // verifico se circonferenza completa bool bCirc = ( AreSamePointApprox( ptP1, ptP3)) ; if ( bCirc) pArcC->GetMidPoint( ptP3) ; CurveArc NewArc ; if ( NewArc.Set3P( ptP1, ptP2, ptP3, bCirc)) { // verifico normale al piano dell'arco if ( NewArc.GetNormVersor() * pArcC->GetNormVersor() < 0) NewArc.InvertN() ; // aggiorno l'arco corrente e torno flag modifica *pArcC = NewArc ; return true ; } else return false ; } // verifico coincidenza pendenza sulla normale double dN = pArcP->GetNormVersor() * pArcC->GetNormVersor() ; if ( fabs(( pArcC->GetDeltaN() * pArcP->GetAngCenter() - dN * pArcP->GetDeltaN() * pArcC->GetAngCenter()) / ( pArcP->GetAngCenter() + pArcC->GetAngCenter())) < dCurrLinTol) { // se calcolo nuovo arco ok, procedo con l'unione Point3d ptP1 ; pArcP->GetStartPoint( ptP1) ; Vector3d vtDir1 ; pArcP->GetStartDir( vtDir1) ; Point3d ptP3 ; pArcC->GetEndPoint( ptP3) ; CurveArc NewArc ; if ( NewArc.Set2PVN( ptP1, ptP3, vtDir1, pArcC->GetNormVersor())) { // aggiorno l'arco corrente e torno flag modifica *pArcC = NewArc ; return true ; } else return false ; } } // nessuna fusione return false ; }