//---------------------------------------------------------------------------- // EgalTech 2013-2014 //---------------------------------------------------------------------------- // File : ArcXxTgArc.cpp Data : 12.06.14 Versione : 1.5f4 // Contenuto : Implementazione funzioni per calcolo archi tangenti a archi. // // // // Modifiche : 12.06.14 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "/EgtDev/Include/EgkCurveLine.h" #include "/EgtDev/Include/EgkArcXxTgArc.h" #include "/EgtDev/Include/EgtPointerOwner.h" using namespace std ; //---------------------------------------------------------------------------- static int CalcCircleCenTgCircle( const Point3d& ptC, const Point3d& ptCen, double dRad, BIPNTVECTOR& vCenPtg) ; //---------------------------------------------------------------------------- ICurveArc* GetCircleCenTgArc( const Point3d& ptCen, const ICurveArc& crvArc, const Point3d& ptNear) { // calcolo il riferimento intrinseco dell'arco (DirNorm->Z e DirStart->X) Frame3d frIntr ; if ( ! frIntr.Set( crvArc.GetCenter(), crvArc.GetNormVersor(), crvArc.GetStartVersor())) return nullptr ; // porto il punto nel riferimento intrinseco Point3d ptCLoc = ptCen ; ptCLoc.ToLoc( frIntr) ; // calcolo le circonferenze tangenti alla circonferenza BIPNTVECTOR vCenPtg( 2) ; int nSol = CalcCircleCenTgCircle( ptCLoc, ORIG, crvArc.GetRadius(), vCenPtg) ; if ( nSol == 0) return nullptr ; // porto i punti nel riferimento standard dell'arco for ( size_t i = 0 ; i < vCenPtg.size() ; ++ i) { vCenPtg[i].first.ToGlob( frIntr) ; vCenPtg[i].second.ToGlob( frIntr) ; } // elimino le soluzioni che non stanno sull'arco for ( int i = 1 ; i >= 0 ; -- i) { if ( nSol > i) { if ( ! crvArc.IsPointOn( vCenPtg[i].second)) { -- nSol ; for ( int j = i ; j < nSol ; ++ j) vCenPtg[j] = vCenPtg[j+1] ; } } } // se non sono rimaste soluzioni, esco if ( nSol == 0) return nullptr ; // scelgo la soluzione più vicina al punto di riferimento int nIdOk = 0 ; double dMinSqDist = INFINITO * INFINITO ; for ( int i = 0 ; i < nSol ; ++ i) { double dSqDist = SqDist( vCenPtg[i].second, ptNear) ; if ( dSqDist < dMinSqDist) { dMinSqDist = dSqDist ; nIdOk = i ; } } // creo l'arco PtrOwner pCrvArc( CreateCurveArc()) ; if ( IsNull( pCrvArc)) return nullptr ; // costruisco la circonferenza corrispondente alla soluzione scelta double dRad = DistXY( vCenPtg[nIdOk].first, vCenPtg[nIdOk].second) ; if ( pCrvArc->Set( vCenPtg[nIdOk].first, crvArc.GetNormVersor(), dRad)) return Release( pCrvArc) ; else return nullptr ; } //---------------------------------------------------------------------------- ICurveArc* GetArcCenTgArcPnt( const Point3d& ptCen, const ICurveArc& crvArc, const Point3d& ptNearTg, const Point3d& ptNearEnd) { // calcolo il riferimento intrinseco dell'arco (DirNorm->Z e DirStart->X) Frame3d frIntr ; if ( ! frIntr.Set( crvArc.GetCenter(), crvArc.GetNormVersor(), crvArc.GetStartVersor())) return nullptr ; // porto il centro nel riferimento intrinseco Point3d ptCLoc = ptCen ; ptCLoc.ToLoc( frIntr) ; // calcolo le circonferenze tangenti alla circonferenza BIPNTVECTOR vCenPtg( 2) ; int nSol = CalcCircleCenTgCircle( ptCLoc, ORIG, crvArc.GetRadius(), vCenPtg) ; if ( nSol == 0) return nullptr ; // porto i punti nel riferimento standard dell'arco for ( size_t i = 0 ; i < vCenPtg.size() ; ++ i) { vCenPtg[i].first.ToGlob( frIntr) ; vCenPtg[i].second.ToGlob( frIntr) ; } // elimino le soluzioni che non stanno sull'arco for ( int i = 1 ; i >= 0 ; -- i) { if ( nSol > i) { if ( ! crvArc.IsPointOn( vCenPtg[i].second)) { -- nSol ; for ( int j = i ; j < nSol ; ++ j) vCenPtg[j] = vCenPtg[j+1] ; } } } // se non sono rimaste soluzioni, esco if ( nSol == 0) return nullptr ; // scelgo la soluzione più vicina al punto di riferimento int nIdOk = 0 ; double dMinSqDist = INFINITO * INFINITO ; for ( int i = 0 ; i < nSol ; ++ i) { double dSqDist = SqDist( vCenPtg[i].second, ptNearTg) ; if ( dSqDist < dMinSqDist) { dMinSqDist = dSqDist ; nIdOk = i ; } } // creo l'arco PtrOwner pCrvArc( CreateCurveArc()) ; if ( IsNull( pCrvArc)) return nullptr ; // posso costruire l'arco solo nel riferimento intrinseco e poi portarlo in quello standard vCenPtg[nIdOk].first.ToLoc( frIntr) ; vCenPtg[nIdOk].second.ToLoc( frIntr) ; Point3d ptNEloc = ptNearEnd ; ptNEloc.ToLoc( frIntr) ; if ( pCrvArc->SetC2P( vCenPtg[nIdOk].first, vCenPtg[nIdOk].second, ptNEloc) && pCrvArc->ToGlob( frIntr)) return Release( pCrvArc) ; else return nullptr ; } //---------------------------------------------------------------------------- // Come la CurveArc::Set2PD, ma se raggio infinito restituisce una retta //---------------------------------------------------------------------------- ICurve* GetArc2PD( const Point3d& ptStart, const Point3d& ptEnd, double dDirStartDeg) { // creo l'oggetto arco ICurveArc* pArc = CreateCurveArc() ; if ( pArc == nullptr) return nullptr ; // inizializzo il puntatore a curva con l'arco PtrOwner pCrv( pArc) ; // calcolo l'arco, se ok lo restituisco ed esco if ( pArc->Set2PD( ptStart, ptEnd, dDirStartDeg)) return Release( pCrv) ; // calcolo arco non riuscito, verifico se retta va bene Vector3d vtDiff = ptEnd - ptStart ; vtDiff.z = 0 ; Vector3d vtDir = FromPolar( 1, dDirStartDeg) ; // verifico se i punti sono allineati con la direzione e nel giusto verso if ( fabs( CrossXY( vtDiff, vtDir)) < EPS_SMALL && ScalarXY( vtDiff, vtDir) > EPS_SMALL) { // creo l'oggetto retta ICurveLine* pLine = CreateCurveLine() ; if ( pLine == nullptr) return nullptr ; // inizializzo il puntatore a curva con la retta pCrv.Set( pLine) ; // calcolo retta, se ok la restituisco ed esco if ( pLine->Set( ptStart, ptEnd)) return Release( pCrv) ; } return nullptr ; } //---------------------------------------------------------------------------- ICurve* GetArcPntDirTgArc( const Point3d& ptP, double dDirStartDeg, const ICurveArc& crvArc, const Point3d& ptNearTg) { // se il raggio è praticamente nullo if ( crvArc.GetRadius() < EPS_SMALL) { return GetArc2PD( ptP, crvArc.GetCenter(), dDirStartDeg) ; } // versore ortogonale alla direzione iniziale (si lavora nel piano XY locale) Vector3d vtOrtho = FromPolar( 1, dDirStartDeg) ; vtOrtho.Rotate( Z_AX, 0, 1) ; // calcolo arco spostando il punto iniziale di + raggio in direzione ortogonale Point3d ptP1 = ptP + vtOrtho * crvArc.GetRadius() ; PtrOwner pCrv1( GetArc2PD( ptP1, crvArc.GetCenter(), dDirStartDeg)) ; bool bOk1 = ( ! IsNull( pCrv1)) ; // compenso lo spostamento bOk1 = bOk1 && pCrv1->Offset( crvArc.GetRadius(), ICurve::OFF_RIGHT) ; // verifico se il punto di tangenza sta sull'arco Point3d ptEnd1 ; bOk1 = bOk1 && pCrv1->GetEndPoint( ptEnd1) && crvArc.IsPointOn( ptEnd1) ; // calcolo arco spostando il punto iniziale di - raggio in direzione ortogonale Point3d ptP2 = ptP - vtOrtho * crvArc.GetRadius() ; PtrOwner pCrv2( GetArc2PD( ptP2, crvArc.GetCenter(), dDirStartDeg)) ; bool bOk2 = ( ! IsNull( pCrv2)) ; // compenso lo spostamento bOk2 = bOk2 && pCrv2->Offset( crvArc.GetRadius(), ICurve::OFF_LEFT) ; // verifico se il punto di tangenza sta sull'arco Point3d ptEnd2 ; bOk2 = bOk2 && pCrv2->GetEndPoint( ptEnd2) && crvArc.IsPointOn( ptEnd2) ; // se due soluzioni, verifico quale ha il punto di tg più vicino al richiesto if ( bOk1 && bOk2) { if ( SqDist( ptEnd1, ptNearTg) <= SqDist( ptEnd2, ptNearTg)) bOk2 = false ; else bOk1 = false ; } // restituisco la soluzione if ( bOk1) return Release( pCrv1) ; else if ( bOk2) return Release( pCrv2) ; else return nullptr ; } //---------------------------------------------------------------------------- static int CalcCircleCenTgCircle( const Point3d& ptC, const Point3d& ptCen, double dRad, BIPNTVECTOR& vCenPtg) { // --- si lavora nel piano XY --- // svuoto il vettore dei risultati (sono coppie centro-punto di tangenza) vCenPtg.clear() ; // se il raggio è negativo non ci sono soluzioni if ( dRad < - EPS_SMALL) return 0 ; // versore e distanza tra i centri nel piano XY Vector3d vtDir = ptCen - ptC ; vtDir.z = 0 ; double dDist = vtDir.Len() ; vtDir /= dDist ; // se il raggio è nullo ... if ( dRad < EPS_SMALL) { // se la distanza tra i punti è significativa, c'è una soluzione if ( dDist > EPS_SMALL) { vCenPtg.push_back( make_pair( ptC, ptCen)) ; return 1 ; } // altrimenti, nessuna soluzione else return 0 ; } // se questa distanza è uguale al raggio, non ci sono soluzioni if ( fabs( dDist - dRad) < EPS_SMALL) return 0 ; // se è minore del raggio, c'è una sola soluzione else if ( dDist < dRad) { vCenPtg.push_back( make_pair( ptC, ptCen - vtDir * dRad)) ; return 1 ; } // altrimenti ci sono due soluzioni else { vCenPtg.push_back( make_pair( ptC, ptCen - vtDir * dRad)) ; vCenPtg.push_back( make_pair( ptC, ptCen + vtDir * dRad)) ; return 2 ; } }