//---------------------------------------------------------------------------- // EgalTech 2013-2014 //---------------------------------------------------------------------------- // File : LineTgTwoCurves.cpp Data : 25.11.14 Versione : 1.5k5 // Contenuto : Implementazione funzioni per calcolo rette tangenti a curve. // // // // Modifiche : 09.06.14 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "CreateCurveAux.h" #include "/EgtDev/Include/EGkDistPointLine.h" #include "/EgtDev/Include/EGkLineTgTwoCurves.h" #include "/EgtDev/Include/EgtPointerOwner.h" using namespace std ; //---------------------------------------------------------------------------- static CurveLine* GetLineTgTwoLines( const CurveLine& crvLine1, const Point3d& ptNear1, const CurveLine& crvLine2, const Point3d& ptNear2) ; static CurveLine* GetLineTgLineArc( const CurveLine& crvLine1, const Point3d& ptNear1, const CurveArc& crvArc2, const Point3d& ptNear2) ; static CurveLine* GetLineTgTwoArcs( const CurveArc& crvArc1, const Point3d& ptNear1, const CurveArc& crvArc2, const Point3d& ptNear2) ; static int CalcLineExtTg2Circles( const Point3d& ptC1, double dRad1, const Point3d& ptC2, double dRad2, BIPNTVECTOR& vBiPnt) ; static int CalcLineIntTg2Circles( const Point3d& ptC1, double dRad1, const Point3d& ptC2, double dRad2, BIPNTVECTOR& vBiPnt) ; static int CalcLineTg2Circles( const Point3d& ptC1, double dRad1, const Point3d& ptC2, double dRad2, BIPNTVECTOR& vBiPnt) ; //---------------------------------------------------------------------------- ICurveLine* GetLineTgTwoCurves( const ICurve& cCrv1, const Point3d& ptNear1, const ICurve& cCrv2, const Point3d& ptNear2) { switch ( cCrv1.GetType()) { case CRV_LINE : { const CurveLine& crvLine1 = *GetBasicCurveLine( &cCrv1) ; switch ( cCrv2.GetType()) { case CRV_LINE : { const CurveLine& crvLine2 = *GetBasicCurveLine( &cCrv2) ; return GetLineTgTwoLines( crvLine1, ptNear1, crvLine2, ptNear2) ; } case CRV_ARC : { const CurveArc& crvArc2 = *GetBasicCurveArc( &cCrv2) ; return GetLineTgLineArc( crvLine1, ptNear1, crvArc2, ptNear2) ; } case CRV_BEZIER : return nullptr ; case CRV_COMPO : return nullptr ; default : return nullptr ; } } break ; case CRV_ARC : { const CurveArc& crvArc1 = *GetBasicCurveArc( &cCrv1) ; switch ( cCrv2.GetType()) { case CRV_LINE : { const CurveLine& crvLine2 = *GetBasicCurveLine( &cCrv2) ; CurveLine* pCrvLine = GetLineTgLineArc( crvLine2, ptNear2, crvArc1, ptNear1) ; if ( pCrvLine != nullptr) pCrvLine->Invert() ; return pCrvLine ; } case CRV_ARC : { const CurveArc& crvArc2 = *GetBasicCurveArc( &cCrv2) ; return GetLineTgTwoArcs( crvArc1, ptNear1, crvArc2, ptNear2) ; } case CRV_BEZIER : return nullptr ; case CRV_COMPO : return nullptr ; default : return nullptr ; } } case CRV_BEZIER : return nullptr ; case CRV_COMPO : return nullptr ; default : return nullptr ; } } //---------------------------------------------------------------------------- CurveLine* GetLineTgTwoLines( const CurveLine& crvLine1, const Point3d& ptNear1, const CurveLine& crvLine2, const Point3d& ptNear2) { // le due linee devono essere allineate ( i punti dell'una devo appartenere all'altra) if ( ! DistPointLine( crvLine2.GetStart(), crvLine1, false).IsSmall() || ! DistPointLine( crvLine2.GetEnd(), crvLine1, false).IsSmall()) return nullptr ; // ricavo i due punti sulle linee più vicini ai rispettivi near Point3d ptP1 ; if ( ! DistPointLine( ptNear1, crvLine1).GetMinDistPoint( ptP1)) return nullptr ; Point3d ptP2 ; if ( ! DistPointLine( ptNear2, crvLine2).GetMinDistPoint( ptP2)) return nullptr ; // creo la linea PtrOwner pCrvLine( CreateBasicCurveLine()) ; if ( IsNull( pCrvLine)) return nullptr ; // costruisco la linea if ( ! pCrvLine->Set( ptP1, ptP2)) return nullptr ; // restituisco la linea return Release( pCrvLine) ; } //---------------------------------------------------------------------------- CurveLine* GetLineTgLineArc( const CurveLine& crvLine1, const Point3d& ptNear1, const CurveArc& crvArc2, const Point3d& ptNear2) { // l'arco deve essere piatto (no elica) if ( ! crvArc2.IsPlane()) return nullptr ; // versore della linea Vector3d vtDir1 ; if ( ! crvLine1.GetStartDir( vtDir1)) return nullptr ; // la linea deve giacere nel piano dell'arco o parallelo if ( ! AreOrthoApprox( vtDir1, crvArc2.GetNormVersor())) return nullptr ; // vettore spostamento dal piano linea parallelo al piano arco a questo Vector3d vtMove = ( crvArc2.GetCenter() - crvLine1.GetStart()) * crvArc2.GetNormVersor() * crvArc2.GetNormVersor() ; // sposto il centro sul piano della linea parallelo a quello dell'arco Point3d ptCen = crvArc2.GetCenter() - vtMove ; // il centro deve distare dalla linea quanto il raggio DistPointLine dstPL( ptCen, crvLine1, false) ; double dDist ; if ( ! dstPL.GetDist( dDist) || abs( dDist - crvArc2.GetRadius()) > EPS_SMALL) return nullptr ; // sposto il punto sulla circonferenza e verifico appartenga all'arco Point3d ptP2 ; dstPL.GetMinDistPoint( ptP2) ; ptP2 += vtMove ; if ( ! crvArc2.IsPointOn( ptP2)) return nullptr ; // punto sulla linea vicino al desiderato Point3d ptP1 ; if ( ! DistPointLine( ptNear1, crvLine1).GetMinDistPoint( ptP1)) return nullptr ; // creo la linea PtrOwner pCrvLine( CreateBasicCurveLine()) ; if ( IsNull( pCrvLine)) return nullptr ; // costruisco la linea if ( ! pCrvLine->Set( ptP1, ptP2)) return nullptr ; // restituisco la linea return Release( pCrvLine) ; } //---------------------------------------------------------------------------- CurveLine* GetLineTgTwoArcs( const CurveArc& crvArc1, const Point3d& ptNear1, const CurveArc& crvArc2, const Point3d& ptNear2) { // verifico che i due archi abbiano lo stesso piano intrinseco e siano piatti if ( ! AreSameOrOppositeVectorApprox( crvArc1.GetNormVersor(), crvArc2.GetNormVersor()) || ! crvArc1.IsPlane() || ! crvArc2.IsPlane()) return nullptr ; // calcolo il riferimento intrinseco del primo arco (Z->DirNorm e X->DirStart) Frame3d frIntr ; if ( ! frIntr.Set( crvArc1.GetCenter(), crvArc1.GetNormVersor(), crvArc1.GetStartVersor())) return nullptr ; // porto il secondo arco nel riferimento intrinseco del primo CurveArc crvArc2Loc = crvArc2 ; crvArc2Loc.ToLoc( frIntr) ; // calcolo le linee di tangenza alle due circonferenze BIPNTVECTOR vBiPnt( 4) ; int nSol = CalcLineTg2Circles( ORIG, crvArc1.GetRadius(), crvArc2Loc.GetCenter(), crvArc2Loc.GetRadius(), vBiPnt) ; if ( nSol == 0) return nullptr ; // porto i punti nel riferimento standard dell'arco for ( size_t i = 0 ; i < vBiPnt.size() ; ++ i) { vBiPnt[i].first.ToGlob( frIntr) ; vBiPnt[i].second.ToGlob( frIntr) ; } // elimino le soluzioni che non stanno sull'arco for ( int i = 3 ; i >= 0 ; -- i) { if ( nSol > i) { if ( ! crvArc1.IsPointOn( vBiPnt[i].first) || ! crvArc2.IsPointOn( vBiPnt[i].second)) { -- nSol ; for ( int j = i ; j < nSol ; ++ j) vBiPnt[j] = vBiPnt[j+1] ; } } } // se non sono rimaste soluzioni, esco if ( nSol == 0) return nullptr ; // scelgo la soluzione più vicina ai punti di riferimento int nIdOk = 0 ; double dMinDist = INFINITO ; for ( int i = 0 ; i < nSol ; ++ i) { double dDist = Dist( vBiPnt[i].first, ptNear1) + Dist( vBiPnt[i].second, ptNear2) ; if ( dDist < dMinDist) { dMinDist = dDist ; nIdOk = i ; } } // creo la linea PtrOwner pCrvLine( CreateBasicCurveLine()) ; if ( IsNull( pCrvLine)) return nullptr ; // costruisco la linea corrispondente alla soluzione scelta if ( pCrvLine->Set( vBiPnt[nIdOk].first, vBiPnt[nIdOk].second)) return Release( pCrvLine) ; else return nullptr ; } //---------------------------------------------------------------------------- int CalcLineExtTg2Circles( const Point3d& ptC1, double dRad1, const Point3d& ptC2, double dRad2, BIPNTVECTOR& vBiPnt) { // deve essere dRad2 >= dRad1 if ( dRad2 < dRad1) return 0 ; // inizializzo il numero di soluzioni int nSol = 0 ; // tangenti p.to - circonf. riducendo le circonferenze della differenza dei raggi BIPNTVECTOR vAuxBiPnt( 2) ; int nSol1 = CalcLinePointTgCircle( ptC1, ptC2, ( dRad2 - dRad1), vAuxBiPnt) ; // offsetto i risultati di dRad1 if ( nSol1 == 1) { Vector3d vtDir ; Point3d ptT1, ptT2 ; // direzione della retta vtDir = ptC2 - ptC1 ; vtDir.Normalize() ; vtDir.Rotate( Z_AX, 0, 1) ; vtDir *= dRad1 ; // offset a sinistra ptT1 = ptC1 + vtDir ; ptT2 = ptC2 + vtDir ; vBiPnt.emplace_back( ptT1, ptT2) ; ++ nSol ; // offset a destra ptT1 = ptC1 - vtDir ; ptT2 = ptC2 - vtDir ; vBiPnt.emplace_back( ptT1, ptT2) ; ++ nSol ; } else if ( nSol1 == 2) { Vector3d vtDir ; Point3d ptT1, ptT2 ; // direzione di offset della prima retta vtDir = vAuxBiPnt[0].second - ptC2 ; vtDir *= dRad1 / ( dRad2 - dRad1) ; // punti della prima retta ptT1 = ptC1 + vtDir ; ptT2 = vAuxBiPnt[0].second + vtDir ; vBiPnt.emplace_back( ptT1, ptT2) ; ++ nSol ; // direzione di offset della seconda retta vtDir = vAuxBiPnt[1].second - ptC2 ; vtDir *= dRad1 / ( dRad2 - dRad1) ; // punti della seconda retta ptT1 = ptC1 + vtDir ; ptT2 = vAuxBiPnt[1].second + vtDir ; vBiPnt.emplace_back( ptT1, ptT2) ; ++ nSol ; } return nSol ; } //---------------------------------------------------------------------------- int CalcLineIntTg2Circles( const Point3d& ptC1, double dRad1, const Point3d& ptC2, double dRad2, BIPNTVECTOR& vBiPnt) { // inizializzo il numero di soluzioni int nSol = 0 ; // tangenti p.to - circonf. riducendo la prima a un punto e aumentando la prima di dRad1 BIPNTVECTOR vAuxBiPnt( 2) ; int nSol1 = CalcLinePointTgCircle( ptC1, ptC2, ( dRad2 + dRad1), vAuxBiPnt) ; // non può esserci una sola soluzione if ( nSol1 == 1) { nSol = 0 ; } // sposto le rette di dRad1 else if ( nSol1 == 2) { Vector3d vtDir ; Point3d ptT1, ptT2 ; // direzione di offset della prima retta vtDir = ptC2 - vAuxBiPnt[0].second ; vtDir *= dRad1 / ( dRad1 + dRad2) ; // punti della prima retta ptT1 = ptC1 + vtDir ; ptT2 = vAuxBiPnt[0].second + vtDir ; vBiPnt.emplace_back( ptT1, ptT2) ; ++ nSol ; // direzione di offset della seconda retta vtDir = ptC2 - vAuxBiPnt[1].second ; vtDir *= dRad1 / ( dRad1 + dRad2) ; // punti della seconda retta ptT1 = ptC1 + vtDir ; ptT2 = vAuxBiPnt[1].second + vtDir ; vBiPnt.emplace_back( ptT1, ptT2) ; ++ nSol ; } return nSol ; } //---------------------------------------------------------------------------- int CalcLineTg2Circles( const Point3d& ptC1, double dRad1, const Point3d& ptC2, double dRad2, BIPNTVECTOR& vBiPnt) { // --- si lavora nel piano XY --- // svuoto il vettore dei risultati (sono coppie di punti) vBiPnt.clear() ; // se uno dei due raggi è negativo non ci sono soluzioni if ( dRad1 < - EPS_SMALL || dRad2 < - EPS_SMALL) return 0 ; // se almeno uno dei due raggi è nullo, si cade nel caso di linea per p.to tangente a circonferenza if ( dRad1 < EPS_SMALL) return CalcLinePointTgCircle( ptC1, ptC2, dRad2, vBiPnt) ; else if ( dRad2 < EPS_SMALL) { int nSol = CalcLinePointTgCircle( ptC2, ptC1, dRad1, vBiPnt) ; if ( nSol >= 1) swap( vBiPnt[0].first, vBiPnt[0].second) ; if ( nSol == 2) swap( vBiPnt[1].first, vBiPnt[1].second) ; } // numero di soluzioni trovate (il numero di punti è il doppio) int nSol = 0 ; // calcolo delle tangenti esterne if ( dRad2 >= dRad1) { nSol += CalcLineExtTg2Circles( ptC1, dRad1, ptC2, dRad2, vBiPnt) ; } else { nSol += CalcLineExtTg2Circles( ptC2, dRad2, ptC1, dRad1, vBiPnt) ; if ( nSol >= 1) swap( vBiPnt[0].first, vBiPnt[0].second) ; if ( nSol == 2) swap( vBiPnt[1].first, vBiPnt[1].second) ; } // calcolo delle tangenti interne nSol += CalcLineIntTg2Circles( ptC1, dRad1, ptC2, dRad2, vBiPnt) ; return nSol ; }