//---------------------------------------------------------------------------- // EgalTech 2013-2013 //---------------------------------------------------------------------------- // File : CurveArc.cpp Data : 22.11.13 Versione : 1.3a1 // Contenuto : Implementazione della classe Arco di Circonferenza. // // // // Modifiche : 16.04.13 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "CurveArc.h" #include "CurveComposite.h" #include "DistPointArc.h" #include "PolygonPlane.h" #include "GeoConst.h" #include "GeoObjFactory.h" #include "NgeWriter.h" #include "NgeReader.h" #include "/EgtDev/Include/EGkAngle.h" #include "/EgtDev/Include/EGkStringUtils3d.h" #include "/EgtDev/Include/EgtPointerOwner.h" #include using namespace std ; //---------------------------------------------------------------------------- GEOOBJ_REGISTER( CRV_ARC, NGE_C_ARC, CurveArc) ; //---------------------------------------------------------------------------- class ArcApproxer { public : ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const CurveArc& arArc) ; bool GetPoint( double& dU, Point3d& ptP) ; private : Point3d m_PtCen ; Vector3d m_VtN ; double m_dRad ; double m_dDeltaN ; Vector3d m_vtA1 ; Vector3d m_vtA2 ; double m_dCosA ; double m_dSinA ; int m_nTotPnt ; int m_nCurrPnt ; bool m_bInside ; } ; //---------------------------------------------------------------------------- CurveArc::CurveArc( void) : m_nStatus( TO_VERIFY), m_PtCen(), m_VtN(), m_VtS(), m_dRad(), m_dAngCenDeg(), m_dDeltaN(), m_VtExtr(), m_dThick(), m_nTempProp() { } //---------------------------------------------------------------------------- CurveArc::~CurveArc( void) { } //---------------------------------------------------------------------------- // Set generico, richiede tutti i parametri //---------------------------------------------------------------------------- bool CurveArc::Set( const Point3d& ptCen, const Vector3d& vtN, double dRad, const Vector3d& vtS, double dAngCenDeg, double dDeltaN) { // assegno i dati m_PtCen = ptCen ; m_VtN = vtN ; m_VtS = vtS ; m_dRad = dRad ; m_dAngCenDeg = dAngCenDeg ; m_dDeltaN = dDeltaN ; m_nStatus = TO_VERIFY ; // sistemo i versori m_VtN.Normalize() ; m_VtS.Normalize() ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return Validate() ; } //---------------------------------------------------------------------------- // Set per circonferenza completa //---------------------------------------------------------------------------- bool CurveArc::Set( const Point3d& ptCen, const Vector3d& vtN, double dRad) { // assegno e calcolo i dati m_PtCen = ptCen ; m_VtN = vtN ; m_VtN.Normalize() ; Frame3d frF ; if ( ! frF.Set( ORIG, vtN)) return false ; m_VtS = frF.VersX() ; m_dRad = dRad ; m_dAngCenDeg = ANG_FULL ; m_dDeltaN = 0 ; m_nStatus = TO_VERIFY ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return Validate() ; } //---------------------------------------------------------------------------- // Set per arco di circonferenza nel piano XY //---------------------------------------------------------------------------- bool CurveArc::SetXY( const Point3d& ptCen, double dRad, double dAngIniDeg, double dAngCenDeg, double dDeltaZ) { // assegno e calcolo i dati m_PtCen = ptCen ; m_VtN = Z_AX ; m_VtS = FromPolar( 1, dAngIniDeg) ; m_dRad = dRad ; m_dAngCenDeg = dAngCenDeg ; m_dDeltaN = dDeltaZ ; m_nStatus = TO_VERIFY ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return Validate() ; } //---------------------------------------------------------------------------- // Set per circonferenza completa nel piano XY //---------------------------------------------------------------------------- bool CurveArc::SetXY( const Point3d& ptCen, double dRad) { // assegno e calcolo i dati m_PtCen = ptCen ; m_VtN = Z_AX ; m_VtS = X_AX ; m_dRad = dRad ; m_dAngCenDeg = ANG_FULL ; m_dDeltaN = 0 ; m_nStatus = TO_VERIFY ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return Validate() ; } //---------------------------------------------------------------------------- // Set per arco di circonf. o circonferenza completa per 3 Punti // ( algoritmo da Comp. Geom. di Faux e Pratt pagg. 67 e 68 ) //---------------------------------------------------------------------------- bool CurveArc::Set3P( const Point3d& ptStart, const Point3d& ptOther, const Point3d& ptEnd, bool bCirc) { // arco non ancora definito correttamente m_nStatus = ERR ; // vettori dal primo punto agli altri due Vector3d vtA = ptOther - ptStart ; Vector3d vtB = ptEnd - ptStart ; // calcolo del versore normale m_VtN = vtA ^ vtB ; double dNSqLen = m_VtN.SqLen() ; // se i punti sono praticamente allineati non si può calcolare la circonferenza if ( ! m_VtN.Normalize( EPS_ZERO)) return false ; // calcolo del centro m_PtCen = ptStart + ( vtB.SqLen() * ( vtA.SqLen() - vtA * vtB) * vtA + vtA.SqLen() * ( vtB.SqLen() - vtA * vtB) * vtB) / ( 2 * dNSqLen) ; // calcolo del versore start e del raggio m_VtS = ptStart - m_PtCen ; m_dRad = m_VtS.Len() ; if ( m_dRad < EPS_SMALL) return false ; m_VtS /= m_dRad ; // calcolo dell'angolo al centro if ( bCirc) m_dAngCenDeg = ANG_FULL ; else { // calcolo dell'angolo di rotazione attorno al centro dal primo al secondo punto bool bDet1 ; double dAng1Deg ; if ( ! m_VtS.GetRotation( ( ptOther - m_PtCen), m_VtN, dAng1Deg, bDet1) || ! bDet1) return false ; // calcolo dell'angolo di rotazione attorno al centro dal primo al terzo punto bool bDet2 ; double dAng2Deg ; if ( ! m_VtS.GetRotation( ( ptEnd - m_PtCen), m_VtN, dAng2Deg, bDet2) || ! bDet2) return false ; // deduzione dell'angolo al centro // se uno dei due angoli è nullo, errore if ( fabs( dAng1Deg) < EPS_ANG_SMALL || fabs( dAng2Deg) < EPS_ANG_SMALL) return false ; // se i due angoli hanno lo stesso segno e Ang1 è minore di Ang2 in modulo else if ( dAng1Deg * dAng2Deg > 0 && fabs( dAng1Deg) < fabs( dAng2Deg)) m_dAngCenDeg = dAng2Deg ; // altrimenti hanno segno opposto oppure Ang1 è maggiore di Ang2 in modulo else m_dAngCenDeg = - _copysign( ANG_FULL, dAng2Deg) + dAng2Deg ; } // non c'è DeltaN m_dDeltaN = 0 ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; m_nStatus = TO_VERIFY ; return Validate() ; } //---------------------------------------------------------------------------- // Set per arco di circonf. definito dagli estremi, versore normale e bulge //---------------------------------------------------------------------------- bool CurveArc::Set2PNB( const Point3d& ptIni, const Point3d& ptFin, const Vector3d& vtN, double dBulge) { // arco non ancora definito correttamente m_nStatus = ERR ; // versore normale m_VtN = vtN ; if ( ! m_VtN.Normalize()) return false ; // vettore da inizio a fine Vector3d vtDiff = ptFin - ptIni ; if ( vtDiff.IsSmall()) return false ; // deltaN eventuale ( è componente parallela a VtN) m_dDeltaN = vtDiff * m_VtN ; if ( fabs( m_dDeltaN) > EPS_SMALL) vtDiff -= m_dDeltaN * m_VtN ; else m_dDeltaN = 0 ; // verifico sia un arco (uso la lunghezza della saetta) double dDist = vtDiff.Len() ; if ( fabs( dBulge * 0.5 * dDist) < EPS_ZERO) return false ; // calcolo del centro Vector3d vtOrtho = vtDiff ; // ruoto di +90 deg attorno a vtN (la moltiplica successiva tiene conto del segno di bulge) vtOrtho.Rotate( m_VtN, 0, 1) ; vtOrtho *= ( 1 - dBulge * dBulge) / ( 4 * dBulge) ; m_PtCen = ptIni + 0.5 * vtDiff + vtOrtho ; // calcolo il raggio m_dRad = fabs( dDist * ( 1 + dBulge * dBulge) / ( 4 * dBulge)) ; // calcolo il versore iniziale m_VtS = ptIni - m_PtCen ; m_VtS.Normalize() ; // calcolo l'angolo al centro m_dAngCenDeg = 4 * atan( dBulge) * RADTODEG ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; m_nStatus = TO_VERIFY ; return Validate() ; } //---------------------------------------------------------------------------- // Set per arco di circonferenza nel piano XY con ptStart, ptEnd, dirStart //---------------------------------------------------------------------------- bool CurveArc::Set2PD( const Point3d& ptStart, const Point3d& ptEnd, double dDirStartDeg) { // arco non ancora definito correttamente m_nStatus = ERR ; // vettore tangente iniziale ( nel piano XY) Vector3d vtA = FromPolar( 1, dDirStartDeg) ; // vettore da inizio a fine ( nel piano XY) Vector3d vtB = ptEnd - ptStart ; vtB.z = 0 ; // calcolo del versore normale m_VtN = vtA ^ vtB ; double dNSqLen = m_VtN.SqLen() ; // se tangente e punti sono praticamente allineati non si può calcolare la circonferenza if ( ! m_VtN.Normalize( EPS_ZERO)) return false ; // calcolo del centro m_PtCen = ptStart + vtB.SqLen() * ( vtB - ( vtA * vtB) * vtA) / ( 2 * dNSqLen) ; // calcolo del versore start e del raggio m_VtS = ptStart - m_PtCen ; m_dRad = m_VtS.Len() ; if ( m_dRad < EPS_SMALL) return false ; m_VtS /= m_dRad ; // calcolo dell'angolo al centro // angolo di rotazione da start a end bool bDet1 ; double dAng1Deg ; if ( ! m_VtS.GetRotation( ( ptEnd - m_PtCen), m_VtN, dAng1Deg, bDet1) || ! bDet1) return false ; // poichè il senso di rotazione è sempre CCW, se l'angolo è negativo prendo il suo complemento al giro if ( dAng1Deg < 0) m_dAngCenDeg = ANG_FULL + dAng1Deg ; else m_dAngCenDeg = dAng1Deg ; // DeltaN m_dDeltaN = ( ptEnd.z - ptStart.z) * m_VtN.z ; // se normale con Z negativa, inverto senza modificare la geometria if ( m_VtN.z < 0) { m_VtN.Invert() ; m_dAngCenDeg = - m_dAngCenDeg ; m_dDeltaN = - m_dDeltaN ; } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; m_nStatus = TO_VERIFY ; return Validate() ; } //---------------------------------------------------------------------------- // Set per arco di circonferenza in un piano generico con ptStart, ptEnd, vtDirStart, vtN //---------------------------------------------------------------------------- bool CurveArc::Set2PVN( const Point3d& ptStart, const Point3d& ptEnd, const Vector3d& vtDirS, const Vector3d& vtN) { // calcolo il riferimento OCS derivato da vtN Frame3d frOcs ; if ( ! frOcs.Set( ORIG, vtN)) return false ; // porto i punti in questo riferimento Point3d ptSloc = ptStart ; ptSloc.ToLoc( frOcs) ; Point3d ptEloc = ptEnd ; ptEloc.ToLoc( frOcs) ; // porto la direzione in questo riferimento e verifico che definisca un angolo nel piano Vector3d vtSloc = vtDirS ; vtSloc.ToLoc( frOcs) ; if ( fabs( vtSloc.x) < EPS_SMALL && fabs( vtSloc.y) < EPS_SMALL) return false ; double dDirStartDeg ; vtSloc.ToSpherical( nullptr, nullptr, &dDirStartDeg) ; // calcolo l'arco in questo piano if ( ! Set2PD( ptSloc, ptEloc, dDirStartDeg)) return false ; // riporto l'arco nel riferimento naturale return ToGlob( frOcs) ; } //---------------------------------------------------------------------------- // Set per arco di circonferenza nel piano XY con ptStart, ptEnd, dRad, bCCW // ( si suppone |dAngCenDeg| <= 180deg) //---------------------------------------------------------------------------- bool CurveArc::Set2PRS( const Point3d& ptStart, const Point3d& ptEnd, double dRad, bool bCCW) { // arco non ancora definito correttamente m_nStatus = ERR ; // il piano dell'arco coincide con XY m_VtN = Z_AX ; m_dDeltaN = ptEnd.z - ptStart.z ; // semi-vettore da inizio a fine Vector3d vtA = 0.5 * ( ptEnd - ptStart) ; vtA.z = 0 ; double dLenA = vtA.Len() ; if ( dLenA < EPS_ZERO || dLenA > ( dRad + EPS_ZERO)) return false ; // distanza del centro dalla corda tra inizio e fine double dLenH ; if ( dLenA > ( dRad - EPS_ZERO)) dLenH = 0 ; else dLenH= sqrt( dRad * dRad - dLenA * dLenA) ; // versore dal punto medio della corda al centro Vector3d vtH = vtA / dLenA ; vtH.Rotate( Z_AX, 0, ( bCCW ? 1 : -1)) ; // calcolo del centro m_PtCen = ptStart + vtA + vtH * dLenH ; // assegno il raggio if ( dRad < EPS_ZERO) return false ; m_dRad = dRad ; // calcolo il versore di start m_VtS = ( ptStart - m_PtCen) / m_dRad ; // calcolo l'angolo al centro bool bDet ; if ( ! m_VtS.GetRotation( ( ptEnd - m_PtCen), m_VtN, m_dAngCenDeg, bDet) || ! bDet) return false ; // quando è 180deg il segno è determinato solo dal senso if ( fabs( m_dAngCenDeg - ANG_STRAIGHT) < 10 * EPS_ANG_SMALL && ( ( bCCW && m_dAngCenDeg < 0) || ( ! bCCW && m_dAngCenDeg > 0))) m_dAngCenDeg = - m_dAngCenDeg ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; m_nStatus = TO_VERIFY ; return Validate() ; } //---------------------------------------------------------------------------- // Set per arco di circonferenza in un piano generico con ptStart, ptEnd, vtN, dRad, bCCW // ( si suppone |dAngCenDeg| <= 180deg) //---------------------------------------------------------------------------- bool CurveArc::Set2PNRS( const Point3d& ptStart, const Point3d& ptEnd, const Vector3d& vtN, double dRad, bool bCCW) { // calcolo il riferimento OCS derivato da vtN Frame3d frOcs ; if ( ! frOcs.Set( ORIG, vtN)) return false ; // porto i punti in questo riferimento Point3d ptSloc = ptStart ; ptSloc.ToLoc( frOcs) ; Point3d ptEloc = ptEnd ; ptEloc.ToLoc( frOcs) ; // calcolo l'arco in questo piano if ( ! Set2PRS( ptSloc, ptEloc, dRad, bCCW)) return false ; // riporto l'arco nel riferimento naturale return ToGlob( frOcs) ; } //---------------------------------------------------------------------------- // Set per arco di circonferenza nel piano XY con ptCen, ptStart, ptNearEnd // ( si suppone |dAngCenDeg| <= 180deg) //---------------------------------------------------------------------------- bool CurveArc::SetC2P( const Point3d& ptCen, const Point3d& ptStart, const Point3d& ptNearEnd) { // arco non ancora definito correttamente m_nStatus = ERR ; // il piano dell'arco coincide con XY m_VtN = Z_AX ; m_dDeltaN = ptNearEnd.z - ptStart.z ; // assegno il centro m_PtCen = ptCen ; m_PtCen.z = ptStart.z ; // assegno il versore iniziale e il raggio m_VtS = ptStart - m_PtCen ; m_dRad = m_VtS.Len() ; if ( m_dRad < EPS_ZERO) return false ; m_VtS /= m_dRad ; // calcolo l'angolo al centro (la funzione trova sempre il più piccolo) bool bDet ; if ( ! m_VtS.GetRotation( ( ptNearEnd - m_PtCen), m_VtN, m_dAngCenDeg, bDet) || ! bDet) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; m_nStatus = TO_VERIFY ; return Validate() ; } //---------------------------------------------------------------------------- // Set per arco di circonferenza in un piano generico con ptCen, ptStart, ptNearEnd, vtN // ( si suppone |dAngCenDeg| <= 180deg) //---------------------------------------------------------------------------- bool CurveArc::SetC2PN( const Point3d& ptCen, const Point3d& ptStart, const Point3d& ptNearEnd, const Vector3d& vtN) { // calcolo il riferimento OCS derivato da vtN Frame3d frOcs ; if ( ! frOcs.Set( ORIG, vtN)) return false ; // porto i punti in questo riferimento Point3d ptCloc = ptCen ; ptCloc.ToLoc( frOcs) ; Point3d ptSloc = ptStart ; ptSloc.ToLoc( frOcs) ; Point3d ptNEloc = ptNearEnd ; ptNEloc.ToLoc( frOcs) ; // calcolo l'arco in questo piano if ( ! SetC2P( ptCloc, ptSloc, ptNEloc)) return false ; // riporto l'arco nel riferimento naturale return ToGlob( frOcs) ; } //---------------------------------------------------------------------------- CurveArc* CurveArc::Clone( void) const { // alloco oggetto CurveArc* pCrv = new(nothrow) CurveArc ; if ( pCrv != nullptr) { if ( ! pCrv->CopyFrom( *this)) { delete pCrv ; return nullptr ; } } return pCrv ; } //---------------------------------------------------------------------------- bool CurveArc::CopyFrom( const IGeoObj* pGObjSrc) { const CurveArc* pCA = dynamic_cast( pGObjSrc) ; if ( pCA == nullptr) return false ; return CopyFrom( *pCA) ; } //---------------------------------------------------------------------------- bool CurveArc::CopyFrom( const CurveArc& caSrc) { if ( &caSrc == this) return true ; m_VtExtr = caSrc.m_VtExtr ; m_dThick = caSrc.m_dThick ; m_nTempProp = caSrc.m_nTempProp ; return Set( caSrc.m_PtCen, caSrc.m_VtN, caSrc.m_dRad, caSrc.m_VtS, caSrc.m_dAngCenDeg, caSrc.m_dDeltaN) ; } //---------------------------------------------------------------------------- GeoObjType CurveArc::GetType( void) const { return static_cast( GEOOBJ_GETTYPE( CurveArc)) ; } //---------------------------------------------------------------------------- const string& CurveArc::GetTitle( void) const { static const string sTitle = "Arc" ; return sTitle ; } //---------------------------------------------------------------------------- bool CurveArc::Dump( string& sOut, const char* szNewLine) const { // dati generali di una curva if ( ! CurveDump( *this, sOut, szNewLine)) return false ; // parametri : Centro, Normale, DirStart, Rad, AngCenDeg, DeltaN sOut += "C(" + ToString( m_PtCen, 3) + ") " + szNewLine ; sOut += "VN(" + ToString( m_VtN, 6) + ") " + szNewLine ; sOut += "VS(" + ToString( m_VtS, 6) + ") " + szNewLine ; sOut += "R=" + ToString( m_dRad, 3) ; sOut += " Ac=" + ToString( m_dAngCenDeg, 3) ; sOut += " Dn=" + ToString( m_dDeltaN, 3) + szNewLine ; return true ; } //---------------------------------------------------------------------------- int CurveArc::GetNgeId( void) const { return GEOOBJ_GETNGEID( CurveArc) ; } //---------------------------------------------------------------------------- bool CurveArc::Save( NgeWriter& ngeOut) const { // centro if ( ! ngeOut.WritePoint( m_PtCen, ";")) return false ; // versore Normale if ( ! ngeOut.WriteVector( m_VtN, ";")) return false ; // raggio if ( ! ngeOut.WriteDouble( m_dRad, ";")) return false ; // versore Iniziale if ( ! ngeOut.WriteVector( m_VtS, ";")) return false ; // angolo al centro if ( ! ngeOut.WriteDouble( m_dAngCenDeg, ";")) return false ; // deltaN if ( ! ngeOut.WriteDouble( m_dDeltaN, ";", true)) return false ; // 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 CurveArc::Load( NgeReader& ngeIn) { // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // leggo la prossima linea ( 6 parametri) // recupero il centro if ( ! ngeIn.ReadPoint( m_PtCen, ";")) return false ; // recupero il versore normale if ( ! ngeIn.ReadVector( m_VtN, ";")) return false ; // recupero il raggio if ( ! ngeIn.ReadDouble( m_dRad, ";")) return false ; // recupero il versore iniziale if ( ! ngeIn.ReadVector( m_VtS, ";")) return false ; // recupero l'angolo al centro if ( ! ngeIn.ReadDouble( m_dAngCenDeg, ";")) return false ; // recupero il delta N if ( ! ngeIn.ReadDouble( m_dDeltaN, ";", true)) 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 ; } // eseguo validazione return Validate() ; } //---------------------------------------------------------------------------- bool CurveArc::GetLocalBBox( BBox3d& b3Loc, int nFlag) const { // verifico lo stato if ( m_nStatus != OK) return false ; // assegno il box in locale b3Loc.Reset() ; ArcApproxer aAppr( LIN_TOL_APPROX, ANG_TOL_APPROX_DEG, false, *this) ; double dU ; Point3d ptPos ; while ( aAppr.GetPoint( dU, ptPos)) b3Loc.Add( ptPos) ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::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 ; // assegno il box nel riferimento b3Ref.Reset() ; ArcApproxer aAppr( LIN_TOL_APPROX, ANG_TOL_APPROX_DEG, false, *this) ; double dU ; Point3d ptPos ; while ( aAppr.GetPoint( dU, ptPos)) { ptPos.ToGlob( frRef) ; b3Ref.Add( ptPos) ; } return true ; } //---------------------------------------------------------------------------- bool CurveArc::Validate( void) { if ( m_nStatus == TO_VERIFY) { // limito l'angolo al centro a un giro se è piatto ( non è elica) if ( fabs( m_dDeltaN) < EPS_SMALL) { if ( m_dAngCenDeg > ANG_FULL) m_dAngCenDeg = ANG_FULL ; else if ( m_dAngCenDeg < - ANG_FULL) m_dAngCenDeg = - ANG_FULL ; } // eseguo il controllo m_nStatus = ( ( m_VtN.IsNormalized() && m_VtS.IsNormalized() && AreOrthoApprox( m_VtN, m_VtS) && m_dRad > EPS_SMALL && fabs( m_dAngCenDeg) > EPS_ANG_ZERO) ? OK : ERR) ; } return ( m_nStatus == OK) ; } //---------------------------------------------------------------------------- bool CurveArc::IsFlat( Plane3d& plPlane, double dToler) const { // verifico lo stato if ( m_nStatus != OK) return false ; // assegno dati possibile piano bool bFlat = IsFlat( dToler) ; if ( m_VtExtr.IsSmall()) { plPlane.vtN = m_VtN ; } else { plPlane.vtN = m_VtExtr ; bFlat = bFlat && AreSameOrOppositeVectorApprox( m_VtExtr, m_VtN) ; } plPlane.dDist = ( m_PtCen - ORIG) * plPlane.vtN ; // ritorno conferma return bFlat ; } //---------------------------------------------------------------------------- bool CurveArc::GetStartPoint( Point3d& ptStart) const { // verifico lo stato if ( m_nStatus != OK) return false ; // calcolo il punto ptStart = m_PtCen + m_dRad * m_VtS ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::GetEndPoint( Point3d& ptEnd) const { // verifico lo stato if ( m_nStatus != OK) return false ; // calcolo il punto double dAng = m_dAngCenDeg * DEGTORAD ; Vector3d vtDir = cos( dAng) * m_VtS + sin( dAng) * ( m_VtN ^ m_VtS) ; ptEnd = m_PtCen + m_dRad * vtDir ; if ( fabs( m_dDeltaN) > EPS_ZERO) ptEnd += m_dDeltaN * m_VtN ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::GetMidPoint( Point3d& ptMid) const { // verifico lo stato if ( m_nStatus != OK) return false ; // calcolo il punto double dAng = 0.5 * m_dAngCenDeg * DEGTORAD ; Vector3d vtDir = cos( dAng) * m_VtS + sin( dAng) * ( m_VtN ^ m_VtS) ; ptMid = m_PtCen + m_dRad * vtDir ; if ( fabs( m_dDeltaN) > EPS_ZERO) ptMid += ( 0.5 * m_dDeltaN) * m_VtN ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::GetCenterPoint( Point3d& ptCen) const { // verifico lo stato if ( m_nStatus != OK) return false ; // assegno il centro ptCen = m_PtCen ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::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, 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 CurveArc::GetDir( double dU, Vector3d& vtDir) const { // la curva deve essere valida if ( m_nStatus != OK) return false ; // il parametro U deve essere compreso tra 0 e 1 if ( dU < 0) dU = 0 ; else if ( dU > 1) dU = 1 ; // versore al punto nel piano della circonferenza (ruoto m_VtS di dU * m_dAngCenDeg attorno a m_VtN) double dAng = dU * m_dAngCenDeg * DEGTORAD ; Vector3d vtRad = cos( dAng) * m_VtS + sin( dAng) * ( m_VtN ^ m_VtS) ; // calcolo della tangente nel punto finale vtDir = ( m_dRad * m_dAngCenDeg * DEGTORAD) * ( m_VtN ^ vtRad) ; if ( fabs( m_dDeltaN) > EPS_ZERO) vtDir += m_dDeltaN * m_VtN ; // normalizzo return vtDir.Normalize() ; } //---------------------------------------------------------------------------- bool CurveArc::GetPointD1D2( double dU, Side nS, Point3d& ptPos, Vector3d* pvtDer1, Vector3d* pvtDer2) const { // la curva deve essere valida if ( m_nStatus != OK) return false ; // il parametro U deve essere compreso tra 0 e 1 if ( dU < 0) dU = 0 ; else if ( dU > 1) dU = 1 ; // versore al punto nel piano della circonferenza (ruoto m_VtS di dU di m_dAngCenDeg attorno a m_VtN) double dAng = dU * m_dAngCenDeg * DEGTORAD ; Vector3d vtDir = cos( dAng) * m_VtS + sin( dAng) * ( m_VtN ^ m_VtS) ; // calcolo del punto ptPos = m_PtCen + m_dRad * vtDir ; if ( fabs( m_dDeltaN) > EPS_ZERO) ptPos += ( dU * m_dDeltaN) * m_VtN ; // calcolo della derivata prima if ( pvtDer1 != nullptr) { *pvtDer1 = ( m_dRad * m_dAngCenDeg * DEGTORAD) * ( m_VtN ^ vtDir) ; if ( fabs( m_dDeltaN) > EPS_ZERO) *pvtDer1 += m_dDeltaN * m_VtN ; } // calcolo della derivata seconda if ( pvtDer2 != nullptr && pvtDer1 != nullptr) *pvtDer2 = - ( m_dRad * m_dAngCenDeg * DEGTORAD * m_dAngCenDeg * DEGTORAD) * vtDir ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::GetLength( double& dLen) const { // la curva deve essere validata if ( m_nStatus != OK) return false ; // lunghezza dell'arco piano dLen = m_dRad * fabs( m_dAngCenDeg) * DEGTORAD ; // aggiunta eventuale parte ortogonale if ( fabs( m_dDeltaN) > EPS_ZERO) dLen = sqrt( dLen * dLen + m_dDeltaN * m_dDeltaN) ; return ( dLen > EPS_SMALL) ; } //---------------------------------------------------------------------------- bool CurveArc::GetLengthAtParam( double dU, double& dLen) const { // verifico lo stato if ( m_nStatus != OK) return false ; // fuori dominio del parametro -> errore if ( dU < 0 - EPS_PARAM) return false ; if ( dU > ( 1 + EPS_PARAM)) return false ; // inizio if ( dU < 0 + EPS_PARAM) { dLen = 0 ; return true ; } // lunghezza totale double dTotLen ; if ( ! GetLength( dTotLen)) return false ; // fine if ( dU > 1 - EPS_PARAM) { dLen = dTotLen ; return true ; } // posizione intermedia dLen = dU * dTotLen ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::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 ; } // lunghezza totale double dTotLen ; if ( ! GetLength( dTotLen)) return false ; // se dopo fine, errore if ( dLen > dTotLen + EPS_SMALL) return false ; // fine if ( dLen > dTotLen - EPS_SMALL) { dU = 1 ; return true ; } // posizione intermedia dU = dLen / dTotLen ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::IsPointOn( const Point3d& ptP, double dTol) const { // verifico la distanza double dSqDist ; dTol = max( dTol, EPS_ZERO) ; return ( DistPointArc( ptP, *this).GetSqDist( dSqDist) && dSqDist < dTol * dTol) ; } //---------------------------------------------------------------------------- bool CurveArc::ApproxWithLines( double dLinTol, double dAngTolDeg, PolyLine& PL) const { // pulisco la polilinea PL.Clear() ; // la curva deve essere validata if ( m_nStatus != OK) return false ; // eseguo approssimazione ArcApproxer aAppr( dLinTol, dAngTolDeg, true, *this) ; double dU ; Point3d ptPos ; while ( aAppr.GetPoint( dU, ptPos)) { if ( ! PL.AddUPoint( dU, ptPos)) return false ; } return true ; } //---------------------------------------------------------------------------- bool CurveArc::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const { // pulisco la polilinea PA.Clear() ; // la curva deve essere validata if ( m_nStatus != OK) return false ; // determinazione versore normale al piano di riferimento Vector3d vtNref = Z_AX ; if ( ! m_VtExtr.IsSmall()) vtNref = m_VtExtr ; // se l'arco non ha direzione normale coincidente con il versore del piano, approssimo con rette if ( ! AreSameOrOppositeVectorApprox( m_VtN, vtNref)) { ArcApproxer aAppr( dLinTol, dAngTolDeg, true, *this) ; double dU ; Point3d ptPos ; while ( aAppr.GetPoint( dU, ptPos)) { if ( ! PA.AddUPoint( dU, ptPos, 0)) return false ; } return true ; } // calcolo il numero di archi con bulge da usare ( ognuno deve avere un angolo al centro <= 240deg) const double MAX_ANG_ARC_BULGE = 240 ; int nArcs = int( ceil( fabs( m_dAngCenDeg) / MAX_ANG_ARC_BULGE)) ; nArcs = max( nArcs, 1) ; double dBulge = tan( m_dAngCenDeg / ( 4 * nArcs) * DEGTORAD) ; // se direzioni opposte, senso di rotazione contrario if ( m_VtN * vtNref < 0) dBulge = - dBulge ; // inserisco i punti di inizio di ogni arco for ( int i = 0 ; i < nArcs ; ++ i) { Point3d ptStart ; double dU = i / double( nArcs) ; if ( ! GetPointD1D2( dU, ICurve::FROM_MINUS, ptStart) || ! PA.AddUPoint( dU, ptStart, dBulge)) return false ; } // inserisco punto finale senza bulge Point3d ptEnd ; if ( ! GetEndPoint( ptEnd) || ! PA.AddUPoint( 1, ptEnd, 0)) return false ; return true ; } //---------------------------------------------------------------------------- ICurve* CurveArc::CopyParamRange( double dUStart, double dUEnd) const { // i parametri start ed end devono essere compresi nel dominio parametrico della curva if ( dUStart < - EPS_PARAM || dUStart > 1 + EPS_PARAM || dUEnd < - EPS_PARAM || dUEnd > 1 + 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 { // eseguo la copia sulla curva composita equivalente CurveComposite cCompo ; cCompo.CopyFrom( this) ; return cCompo.CopyParamRange( dUStart, dUEnd) ; } } // creo l'arco copia PtrOwner pCopy( Clone()) ; if ( IsNull( pCopy)) return nullptr ; // eseguo il trim if ( ! pCopy->TrimStartEndAtParam( dUStart, dUEnd)) return nullptr ; return ( ::Release( pCopy)) ; } //---------------------------------------------------------------------------- bool CurveArc::Invert( void) { // la curva deve essere validata if ( m_nStatus != OK) return false ; // il centro va spostato di DeltaN if ( fabs( m_dDeltaN) > EPS_ZERO) m_PtCen += m_dDeltaN * m_VtN ; // il versore normale rimane inalterato // il versore iniziale diventa quello finale double dAng = m_dAngCenDeg * DEGTORAD ; m_VtS = cos( dAng) * m_VtS + sin( dAng) * ( m_VtN ^ m_VtS) ; // il raggio non cambia // l'angolo al centro inverte il segno m_dAngCenDeg = - m_dAngCenDeg ; // l'incremento sulla normale inverte il segno m_dDeltaN = - m_dDeltaN ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::SimpleOffset( double dDist, int nType) { // la curva deve essere validata if ( m_nStatus != OK) return false ; // determinazione versore normale al piano di offset Vector3d vtNref = Z_AX ; if ( ! m_VtExtr.IsSmall()) vtNref = m_VtExtr ; // la normale deve coincidere con il versore del piano di offset if ( ! AreSameOrOppositeVectorApprox( m_VtN, vtNref)) return false ; // calcolo il nuovo raggio e lo valido bool bCCW = ( ( m_dAngCenDeg > 0 && m_VtN * vtNref > 0) || ( m_dAngCenDeg < 0 && m_VtN * vtNref < 0)) ; double dNewRad = m_dRad + ( bCCW ? + dDist : - dDist) ; if ( dNewRad < EPS_SMALL) return false ; // aggiorno il raggio m_dRad = dNewRad ; // con i controlli sopra fatti rimane validata, ma la grafica va ricalcolata m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::ExtendedOffset( double dDist, int nType) { // la curva deve essere validata if ( m_nStatus != OK) return false ; // determinazione versore normale al piano di offset Vector3d vtNref = Z_AX ; if ( ! m_VtExtr.IsSmall()) vtNref = m_VtExtr ; // la normale deve coincidere con il versore del piano di offset if ( ! AreSameOrOppositeVectorApprox( m_VtN, vtNref)) return false ; // calcolo il nuovo raggio e lo valido bool bCCW = ( ( m_dAngCenDeg > 0 && m_VtN * vtNref > 0) || ( m_dAngCenDeg < 0 && m_VtN * vtNref < 0)) ; double dNewRad = m_dRad + ( bCCW ? + dDist : - dDist) ; if ( fabs( dNewRad) < EPS_SMALL) return false ; if ( dNewRad < 0) { dNewRad = - dNewRad ; m_VtS = - m_VtS ; } // aggiorno il raggio m_dRad = dNewRad ; // con i controlli sopra fatti rimane validata, ma la grafica va ricalcolata m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::ModifyStart( const Point3d& ptNewStart) { // verifico lo stato if ( m_nStatus != OK) return false ; // inverto l'arco Invert() ; // modifico la fine dell'arco invertito bool bOk = ModifyEnd( ptNewStart) ; // re-inverto Invert() ; return bOk ; } //---------------------------------------------------------------------------- bool CurveArc::ModifyEnd( const Point3d& ptNewEnd) { // verifico lo stato if ( m_nStatus != OK) return false ; // calcolo il riferimento intrinseco dell'arco (DirNorm->Z e DirStart->X) Frame3d frIntr ; if ( ! frIntr.Set( m_PtCen, m_VtN, m_VtS)) return false ; // determino vecchio e nuovo punto finale nel riferimento intrinsseco Point3d ptOldEndIntr ; GetEndPoint( ptOldEndIntr) ; ptOldEndIntr.ToLoc( frIntr) ; Point3d ptEndIntr = ptNewEnd ; ptEndIntr.ToLoc( frIntr) ; // se coincidono nel piano XY, è cambiato solo il deltaN if ( SqDistXY( ptOldEndIntr, ptEndIntr) < EPS_ZERO * EPS_ZERO) { m_dDeltaN += ptEndIntr.z - ptOldEndIntr.z ; } // altrimenti, calcolo un arco ausiliario nel riferimento intrinseco con i nuovi dati else { Point3d ptStartIntr ; GetStartPoint( ptStartIntr) ; ptStartIntr.ToLoc( frIntr) ; double dOldAngCenDeg = m_dAngCenDeg ; CurveArc arcAux ; if ( ! arcAux.Set2PD( ptStartIntr, ptEndIntr, ( m_dAngCenDeg > 0 ? ANG_RIGHT : - ANG_RIGHT))) return false ; // aggiorno i dati dell'arco con quelli appena calcolati m_PtCen = arcAux.m_PtCen ; m_PtCen.ToGlob( frIntr) ; m_VtN = arcAux.m_VtN ; m_VtN.ToGlob( frIntr) ; m_VtS = arcAux.m_VtS ; m_VtS.ToGlob( frIntr) ; m_dRad = arcAux.m_dRad ; // se elica, devo avere un numero di giri vicino ai precedenti if ( fabs( dOldAngCenDeg) >= ANG_FULL - EPS_ANG_ZERO) m_dAngCenDeg = AngleNearAngle( arcAux.m_dAngCenDeg, dOldAngCenDeg) ; else m_dAngCenDeg = arcAux.m_dAngCenDeg ; m_dDeltaN = arcAux.m_dDeltaN ; } // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::TrimStartAtParam( double dUTrim) { double dLen ; // riporto i parametri nel loro range dUTrim = ( ( dUTrim < 0) ? 0 : (( dUTrim > 1) ? 1 : dUTrim)) ; // recupero lunghezza if ( ! GetLength( dLen)) return false ; // utilizzo il trim sulle lunghezze return TrimStartAtLen( dUTrim * dLen) ; } //---------------------------------------------------------------------------- bool CurveArc::TrimEndAtParam( double dUTrim) { double dLen ; // riporto i parametri nel loro range dUTrim = ( ( dUTrim < 0) ? 0 : (( dUTrim > 1) ? 1 : dUTrim)) ; // recupero lunghezza if ( ! GetLength( dLen)) return false ; // utilizzo il trim sulle lunghezze return TrimEndAtLen( dUTrim * dLen) ; } //---------------------------------------------------------------------------- bool CurveArc::TrimStartEndAtParam( double dUStartTrim, double dUEndTrim) { // i parametri start ed end devono essere compresi nel dominio parametrico della curva if ( dUStartTrim < - EPS_PARAM || dUStartTrim > 1 + EPS_PARAM || dUEndTrim < - EPS_PARAM || dUEndTrim > 1 + EPS_PARAM) return false ; // verifico che i trim non cancellino interamente la curva if ( dUStartTrim > dUEndTrim - EPS_PARAM) return false ; // trim finale if ( ! TrimEndAtParam( dUEndTrim)) return false ; // trim iniziale con il parametro opportunamente ricalcolato double dNewUStartTrim = dUStartTrim / dUEndTrim ; return TrimStartAtParam( dNewUStartTrim) ; } //---------------------------------------------------------------------------- bool CurveArc::TrimStartAtLen( double dLenTrim) { // lunghezze negative vengono considerate nulle dLenTrim = __max( dLenTrim, 0) ; // verifico che sia abbastanza lunga double dLen ; if ( ! GetLength( dLen)) return false ; if ( ( dLen - dLenTrim) < EPS_SMALL) return false ; // eseguo il trim if ( dLenTrim > EPS_ZERO) { double dAngRot ; double dMoveN ; dAngRot = m_dAngCenDeg * dLenTrim / dLen ; m_VtS.Rotate( m_VtN, dAngRot) ; m_dAngCenDeg -= dAngRot ; if ( fabs( m_dDeltaN) > EPS_ZERO) { dMoveN = m_dDeltaN * dLenTrim / dLen ; m_PtCen.Translate( m_VtN * dMoveN) ; m_dDeltaN -= dMoveN ; } } // con i controlli sopra fatti rimane validata, ma la grafica va ricalcolata m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::TrimEndAtLen( double dLenTrim) { // lunghezze negative vengono considerate nulle dLenTrim = __max( dLenTrim, 0) ; // verifico che sia abbastanza lunga double dLen ; if ( ! GetLength( dLen)) return false ; if ( dLenTrim < EPS_SMALL) return false ; // eseguo il trim if ( ( dLen - dLenTrim) > EPS_ZERO) { m_dAngCenDeg *= dLenTrim / dLen ; if ( fabs( m_dDeltaN) > EPS_ZERO) m_dDeltaN *= dLenTrim / dLen ; } // con i controlli sopra fatti rimane validata, ma la grafica va ricalcolata m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::ExtendStartByLen( double dLenExt) { // la lunghezza di estensione deve essere positiva if ( dLenExt < - EPS_ZERO) return false ; // inverto l'arco, lo estendo alla fine o lo reinverto Invert() ; bool bOk = ExtendEndByLen( dLenExt) ; Invert() ; return bOk ; } //---------------------------------------------------------------------------- bool CurveArc::ExtendEndByLen( double dLenExt) { // la lunghezza deve essere positiva if ( dLenExt < - EPS_ZERO) return false ; // ricavo la lunghezza originale double dLen ; if ( ! GetLength( dLen)) return false ; // eseguo l'estensione m_dAngCenDeg *= ( dLen + dLenExt) / dLen ; if ( IsFlat() && fabs( m_dAngCenDeg) > ANG_FULL) m_dAngCenDeg = _copysign( ANG_FULL, m_dAngCenDeg) ; // con i controlli sopra fatti rimane validata, ma la grafica va ricalcolata m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::Translate( const Vector3d& vtMove) { // la curva deve essere validata if ( m_nStatus != OK) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // traslo il centro m_PtCen.Translate( vtMove) ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::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 ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // ruoto il centro e i versori m_PtCen.Rotate( ptAx, vtAx, dCosAng, dSinAng) ; m_VtN.Rotate( vtAx, dCosAng, dSinAng) ; m_VtS.Rotate( vtAx, dCosAng, dSinAng) ; // ruoto il vettore estrusione m_VtExtr.Rotate( vtAx, dCosAng, dSinAng) ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double dCoeffZ) { // la curva deve essere validata if ( m_nStatus != OK) return false ; // ammessa solo scalatura uniforme if ( fabs( dCoeffX - dCoeffY) > EPS_SMALL || fabs( dCoeffX - dCoeffZ) > EPS_SMALL) return false ; // verifico non sia nulla if ( fabs( dCoeffX) < EPS_ZERO) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // scalo il centro e le dimensioni lineari m_PtCen.Scale( frRef, dCoeffX, dCoeffX, dCoeffX) ; m_dRad *= fabs( dCoeffX) ; m_dDeltaN *= dCoeffX ; if ( dCoeffX < 0) m_VtS = - m_VtS ; // 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 CurveArc::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 della grafica m_OGrMgr.Reset() ; // specchio il centro e i versori, inverto l'angolo al centro m_PtCen.Mirror( ptOn, vtNorm) ; m_VtN.Mirror( vtNorm) ; m_VtS.Mirror( vtNorm) ; m_dAngCenDeg *= -1 ; // specchio il vettore estrusione m_VtExtr.Mirror( vtNorm) ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::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 ; // possibile solo se l'arco è piatto e il piano di scorrimento coincide con quello dell'arco if ( ! ( fabs( m_dDeltaN) < EPS_SMALL) || ! AreSameOrOppositeVectorExact( m_VtN, vtNorm)) return false ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // eseguo scorrimento del centro m_PtCen.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 CurveArc::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 ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // trasformo il centro e i versori return ( m_PtCen.ToGlob( frRef) && m_VtN.ToGlob( frRef) && m_VtS.ToGlob( frRef) && m_VtExtr.ToGlob( frRef)) ; } //---------------------------------------------------------------------------- bool CurveArc::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 ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // trasformo il centro e i versori return ( m_PtCen.ToLoc( frRef) && m_VtN.ToLoc( frRef) && m_VtS.ToLoc( frRef) && m_VtExtr.ToLoc( frRef)) ; } //---------------------------------------------------------------------------- bool CurveArc::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 ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // trasformo il centro e i versori return ( m_PtCen.LocToLoc( frOri, frDest) && m_VtN.LocToLoc( frOri, frDest) && m_VtS.LocToLoc( frOri, frDest) && m_VtExtr.LocToLoc( frOri, frDest)) ; } //---------------------------------------------------------------------------- bool CurveArc::InvertN( void) { // la curva deve essere validata if ( m_nStatus != OK) return false ; // si inverte il verso della normale senza modificare la geometria dell'arco m_VtN.Invert() ; m_dAngCenDeg = - m_dAngCenDeg ; m_dDeltaN = - m_dDeltaN ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::IsPointInSector( const Point3d& ptP) const { // verifico sia compreso nell'angolo al centro bool bDet ; double dAngDeg ; if ( ! m_VtS.GetRotation( ( ptP - m_PtCen), m_VtN, dAngDeg, bDet) || ! bDet) return false ; return AngleInSpan( dAngDeg, 0, m_dAngCenDeg) ; } //---------------------------------------------------------------------------- bool CurveArc::CalcPointAngle( const Point3d& ptP, double& dAngDeg) const { // verifico lo stato if ( m_nStatus != OK) return false ; // posizione angolare del punto rispetto al centro a partire dalla direzione start bool bDet ; if ( ! m_VtS.GetRotation( ( ptP - m_PtCen), m_VtN, dAngDeg, bDet) || ! bDet) return false ; if ( m_dAngCenDeg > 0 && dAngDeg < - EPS_ANG_ZERO) dAngDeg += ANG_FULL ; else if ( m_dAngCenDeg < 0 && dAngDeg > EPS_ANG_ZERO) dAngDeg -= ANG_FULL ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::CalcPointParamPosiz( const Point3d& ptP, double& dU, int& nPos) const { // calcolo la posizione angolare double dAngDeg ; if ( ! CalcPointAngle( ptP, dAngDeg)) return false ; // calcolo parametro dU = dAngDeg / m_dAngCenDeg ; // verifica posizione punto su arco nPos = PP_NULL ; // fuori if ( fabs( DiffAngle( dAngDeg, 0) * DEGTORAD * m_dRad) < EPS_SMALL) { nPos = PP_START ; // vicino a inizio dU = AngleNearAngle( dAngDeg, 0) / m_dAngCenDeg ; dU = max( dU, 0.) ; } else if ( fabs( DiffAngle( dAngDeg, m_dAngCenDeg) * DEGTORAD * m_dRad) < EPS_SMALL) { nPos = PP_END ; // vicino a fine dU = AngleNearAngle( dAngDeg, m_dAngCenDeg) / m_dAngCenDeg ; dU = min( dU, 1.) ; } else if ( dU > 0 && dU < 1) nPos = PP_MID ; // nell'interno return true ; } //---------------------------------------------------------------------------- bool CurveArc::ChangeRadius( double dNewRadius) { // la curva deve essere validata if ( m_nStatus != OK) return false ; // cambio il raggio m_dRad = dNewRadius ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // eseguo validazione m_nStatus = TO_VERIFY ; return Validate() ; } //---------------------------------------------------------------------------- bool CurveArc::ChangeDeltaN( double dNewDeltaN) { // la curva deve essere validata if ( m_nStatus != OK) return false ; // cambio il parametro m_dDeltaN = dNewDeltaN ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; // eseguo validazione m_nStatus = TO_VERIFY ; return Validate() ; } //---------------------------------------------------------------------------- bool CurveArc::ChangeStartPoint( double dU) { // la curva deve essere chiusa (ne verifica anche lo stato) if ( ! IsClosed()) return false ; // recupero il punto che corrisponde al valore del parametro Point3d ptP ; if ( ! GetPointD1D2( dU, ICurve::FROM_MINUS, ptP)) return false ; // cambio il versore start Vector3d vtDir = ptP - m_PtCen ; if ( ! vtDir.Normalize()) return false ; m_VtS = vtDir ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::ToAdditional( void) { // la curva deve essere validata if ( m_nStatus != OK) return false ; // l'angolo al centro deve essere minore di un giro if ( fabs( m_dAngCenDeg) > ( ANG_FULL - EPS_ANG_SMALL)) return false ; // basta prendere l'angolo al centro che completa il giro m_dAngCenDeg = - _copysign( ANG_FULL, m_dAngCenDeg) + m_dAngCenDeg ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; return true ; } //---------------------------------------------------------------------------- bool CurveArc::Flip( void) { // la curva deve essere validata if ( m_nStatus != OK) return false ; // l'angolo al centro deve essere minore di un giro if ( fabs( m_dAngCenDeg) > ( ANG_FULL - EPS_ANG_SMALL)) return false ; // si calcola l'arco simmetrico rispetto alla linea che unisce gli estremi Point3d ptStart, ptEnd ; if ( ! GetStartPoint( ptStart) || ! GetEndPoint( ptEnd)) return false ; // il punto finale mi interessa nel piano dell'arco if ( fabs( m_dDeltaN) > EPS_ZERO) ptEnd -= m_dDeltaN * m_VtN ; m_PtCen = ptStart + ( ptEnd - m_PtCen) ; m_VtS = ptStart - m_PtCen ; m_VtS.Normalize() ; m_dAngCenDeg = - m_dAngCenDeg ; // imposto ricalcolo della grafica m_OGrMgr.Reset() ; m_nStatus = TO_VERIFY ; return Validate() ; } //---------------------------------------------------------------------------- // Oggetto locale per approssimazione di archi // Approx interna è quella cordale standard. // Approx esterna è quella tangente a inizio e fine con metà tratto e un punto in più. // I punti si calcolano a partire dal triangolo tra due punti consecutivi interni e il centro, // usando il versore medio dal centro e moltiplicandolo per il coefficiente ( 2 / ( 1 + cosA)). // Il versore dell'ultimo punto è già stato calcolato per il penultimo. //---------------------------------------------------------------------------- ArcApproxer::ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const CurveArc& arArc) { int nStep ; double dAngStepDeg ; // inizializzazioni m_nTotPnt = 0 ; m_nCurrPnt = - 1 ; // la curva deve essere validata if ( ! arArc.IsValid()) return ; // limiti minimi su tolleranza e deviazione angolare dLinTol = max( dLinTol, LIN_TOL_MIN) ; dAngTolDeg = max( dAngTolDeg, ANG_TOL_MIN_DEG) ; // limite massimo su deviazione angolare se approssimazione esterna if ( ! bInside) dAngTolDeg = min( dAngTolDeg, ANG_TOL_EXT_MAX_DEG) ; // determinazione dello step angolare dAngStepDeg = sqrt( 8 * dLinTol / arArc.GetRadius()) * RADTODEG ; dAngStepDeg = min( dAngStepDeg, dAngTolDeg) ; // dall'angolo al centro ricavo il numero di passi nStep = (int) ( fabs( arArc.GetAngCenter()) / dAngStepDeg + 0.999) ; nStep = __max( nStep, 1) ; // sistemo lo step (per il numero intero di passi) dAngStepDeg = arArc.GetAngCenter() / nStep ; // versori di riferimento nel piano dell'arco m_vtA1 = arArc.GetStartVersor() ; m_vtA2 = arArc.GetNormVersor() ^ arArc.GetStartVersor() ; // seno e coseno dell'angolo di step m_dCosA = cos( dAngStepDeg * DEGTORAD) ; m_dSinA = sin( dAngStepDeg * DEGTORAD) ; // salvo i dati m_bInside = bInside ; m_nTotPnt = ( m_bInside ? ( nStep + 1) : ( nStep + 2)) ; m_PtCen = arArc.GetCenter() ; m_VtN = arArc.GetNormVersor() ; m_dRad = arArc.GetRadius() ; m_dDeltaN = arArc.GetDeltaN() ; } //---------------------------------------------------------------------------- bool ArcApproxer::GetPoint( double& dU, Point3d& ptP) { Vector3d vtA1p ; Vector3d vtA2p ; // incremento indice punto corrente ++ m_nCurrPnt ; // se oltrepassata la fine if ( m_nCurrPnt >= m_nTotPnt) return false ; // primo punto if ( m_nCurrPnt == 0) { dU = 0 ; ptP = m_PtCen + m_vtA1 * m_dRad ; return true ; } // se approx esterna e ultimo punto (uso rotazione precedente) if ( ! m_bInside && m_nCurrPnt == m_nTotPnt - 1) { dU = 1 ; ptP = m_PtCen + m_vtA1 * m_dRad ; if ( fabs( m_dDeltaN) > EPS_ZERO) ptP += ( dU * m_dDeltaN) * m_VtN ; return true ; } // punti successivi // calcolo parametro if ( m_bInside) dU = m_nCurrPnt / (double) ( m_nTotPnt - 1) ; else dU = ( m_nCurrPnt - 0.5) / (double) ( m_nTotPnt - 2) ; // nuovo valore versori vtA1p = m_vtA1 ; vtA2p = m_vtA2 ; m_vtA1 = m_dCosA * vtA1p + m_dSinA * vtA2p ; m_vtA2 = - m_dSinA * vtA1p + m_dCosA * vtA2p ; // calcolo del punto if ( m_bInside) ptP = m_PtCen + m_vtA1 * m_dRad ; else ptP = m_PtCen + ( vtA1p + m_vtA1) * ( m_dRad / ( 1 + m_dCosA)) ; if ( fabs( m_dDeltaN) > EPS_ZERO) ptP += ( dU * m_dDeltaN) * m_VtN ; return true ; }