//---------------------------------------------------------------------------- // EgalTech 2023-2023 //---------------------------------------------------------------------------- // File : IntersLineCyl.cpp Data : 16.05.23 Versione : 2.5e3 // Contenuto : Implementazione della intersezione linea/cilindro. // // // // Modifiche : 16.05.23 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "IntersLineCyl.h" #include "/EgtDev/Include/EGkFrame3d.h" #include "/EgtDev/Include/EGkIntersLineCylinder.h" #include "/EgtDev/Include/ENkPolynomialRoots.h" using namespace std ; //---------------------------------------------------------------------------- // Linea e cilindro sono nel medesimo riferimento. // Il cilindro è centrato sull'asse Z e appoggiato sul piano XY. // In caso di intersezione viene restituito true e i parametri in dU1 e dU2. //---------------------------------------------------------------------------- bool IntersLineCyl( const Point3d& ptP, const Vector3d& vtV, double dH, double dRad, bool bTapLow, bool bTapUp, double& dU1, Point3d& ptInt1, Vector3d& vtN1, double& dU2, Point3d& ptInt2, Vector3d& vtN2, bool bIgnoreTap, bool bInvertNormals) { dU1 = NAN ; dU2 = NAN ; // Verifico il versore if ( vtV.IsSmall()) return false ; // Verifico il cilindro if ( dRad < EPS_SMALL || dH < EPS_SMALL) return false ; // Determino le eventuali intersezioni con le due basi a quota minima e massima (solo se linea non parallela ad esse) int nBasInt = 0 ; if ( abs( vtV.z) > EPS_ZERO) { // le linee tangenti al cilindro non sono considerate intersecanti double dEpsRad = ( vtV.IsZeroXY() ? - EPS_SMALL : EPS_SMALL) ; if ( bIgnoreTap) dEpsRad = 0. ; ptInt1 = ptP + ( ( 0 - ptP.z) / vtV.z) * vtV ; if ( ptInt1.x * ptInt1.x + ptInt1.y * ptInt1.y < dRad * dRad + 2 * dRad * dEpsRad) { nBasInt += 1 ; vtN1 = - Z_AX ; dU1 = ( ( 0 - ptP.z) / vtV.z) ; } ptInt2 = ptP + ( ( dH - ptP.z) / vtV.z) * vtV ; if ( ptInt2.x * ptInt2.x + ptInt2.y * ptInt2.y < dRad * dRad + 2 * dRad * dEpsRad) { nBasInt += 2 ; vtN2 = Z_AX ; dU2 = ( ( dH - ptP.z) / vtV.z) ; } } // Se la linea interseca entrambe le basi, si sono trovate le due intersezioni if ( nBasInt == 3) { if ( dU1 > dU2) { swap( dU1, dU2) ; swap( ptInt1, ptInt2) ; swap( vtN1, vtN2) ; } if ( bInvertNormals) { vtN1 *= - 1 ; vtN2 *= - 1 ; } // Trovate intersezioni return true ; } // Determino le intersezioni con la superficie laterale del cilindro DBLVECTOR vdCoeff{ ptP.x * ptP.x + ptP.y * ptP.y - dRad * dRad, 2 * ( ptP.x * vtV.x + ptP.y * vtV.y), vtV.x * vtV.x + vtV.y * vtV.y} ; DBLVECTOR vdRoots ; int nRoot = PolynomialRoots( 2, vdCoeff, vdRoots) ; // Epsilon per piani di tappo double dEpsLow = ( bTapLow ? - EPS_SMALL : EPS_SMALL) ; double dEpsUp = ( bTapUp ? EPS_SMALL : - EPS_SMALL) ; if ( bIgnoreTap) { dEpsLow = 0. ; dEpsUp = 0. ; } // Elimino le soluzioni cha danno intersezioni fuori dai limiti in Z del cilindro if ( nRoot == 2) { double dIntZ2 = ptP.z + vdRoots[1] * vtV.z ; if ( dIntZ2 < 0 + dEpsLow || dIntZ2 > dH + dEpsUp) -- nRoot ; } if ( nRoot >= 1) { double dIntZ1 = ptP.z + vdRoots[0] * vtV.z ; if ( dIntZ1 < 0 + dEpsLow || dIntZ1 > dH + dEpsUp) { if ( nRoot == 2) vdRoots[0] = vdRoots[1] ; -- nRoot ; } } // Due soluzioni: la retta interseca due volte la superficie laterale if ( nRoot == 2) { // Punti di intersezione con la superficie del cilindro ptInt1 = ptP + vdRoots[0] * vtV ; ptInt2 = ptP + vdRoots[1] * vtV ; dU1 = vdRoots[0] ; dU2 = vdRoots[1] ; // Determino le normali vtN1.Set( ptInt1.x, ptInt1.y, 0) ; vtN1.Normalize() ; vtN2.Set( ptInt2.x, ptInt2.y, 0) ; vtN2.Normalize() ; if ( dU1 > dU2) { swap( dU1, dU2) ; swap( ptInt1, ptInt2) ; swap( vtN1, vtN2) ; } if ( bInvertNormals) { vtN1 *= - 1 ; vtN2 *= - 1 ; } // Trovate intersezioni return true ; } // Una soluzione : la retta interseca la superficie laterale e un piano else if ( nRoot == 1) { // Se piano superiore if ( nBasInt == 2) { // Punto di intersezione dU1 = vdRoots[0] ; ptInt1 = ptP + vdRoots[0] * vtV ; // Normale alla superficie del cilindro verso l'interno vtN1.Set( ptInt1.x, ptInt1.y, 0) ; vtN1.Normalize() ; } // altrimenti piano inferiore else if ( nBasInt == 1) { // Punto di intersezione dU2 = vdRoots[0] ; ptInt2 = ptP + vdRoots[0] * vtV ; // Normale alla superficie del cilindro verso l'interno vtN2.Set( ptInt2.x, ptInt2.y, 0) ; vtN2.Normalize() ; } // altrimenti niente else return false ; if ( dU1 > dU2) { swap( dU1, dU2) ; swap( ptInt1, ptInt2) ; swap( vtN1, vtN2) ; } if ( bInvertNormals) { vtN1 *= - 1 ; vtN2 *= - 1 ; } // Trovate intersezioni return true ; } // Nessuna soluzione : nessuna intersezione else return false ; } //---------------------------------------------------------------------------- // Riferimento con origine nel centro della base e asse di simmetria coincidente con l'asse Z. // La funzione restituisce true in caso di intersezione, false altrimenti. //---------------------------------------------------------------------------- bool IntersLineCyl( const Point3d& ptLineSt, const Vector3d& vtLineDir, const Frame3d& CylFrame, double dH, double dRad, bool bTapLow, bool bTapUp, double& dU1, Point3d& ptInt1, Vector3d& vtN1, double& dU2, Point3d& ptInt2, Vector3d& vtN2, bool bIgnoreTap, bool bInvertNormals) { // Porto la linea nel riferimento del cilindro Point3d ptP = GetToLoc( ptLineSt, CylFrame) ; Vector3d vtV = GetToLoc( vtLineDir, CylFrame) ; if ( IntersLineCyl( ptP, vtV, dH, dRad, bTapLow, bTapUp, dU1, ptInt1, vtN1, dU2, ptInt2, vtN2, bIgnoreTap, bInvertNormals)) { ptInt1.ToGlob( CylFrame) ; vtN1.ToGlob( CylFrame) ; ptInt2.ToGlob( CylFrame) ; vtN2.ToGlob( CylFrame) ; return true ; } return false ; } //---------------------------------------------------------------------------- // Linea e cilindro sono nel medesimo riferimento. // Il cilindro è definito con centro della base, asse, raggio e altezza. // In caso di intersezione viene restituito true e i parametri in dU1 e dU2. //---------------------------------------------------------------------------- bool IntersLineCyl( const Point3d& ptL, const Vector3d& vtL, const Point3d& ptCyl, const Vector3d& vtCyl, double dRad, double dHeight, double& dU1, double& dU2) { // Riferimento intrinseco del cilindro Frame3d frCyl ; if ( ! frCyl.Set( ptCyl, vtCyl)) return false ; // Ora eseguo i conti nel riferimento intrinseco bool bTapLow = false ; bool bTapUp = false ; bool bIgnoreTap = true ; Point3d ptInt1, ptInt2 ; Vector3d vtN1, vtN2 ; return IntersLineCyl( ptL, vtL, frCyl, dHeight, dRad, bTapLow, bTapUp, dU1, ptInt1, vtN1, dU2, ptInt2, vtN2, bIgnoreTap) ; } //---------------------------------------------------------------------------- // Linea e cilindro sono nel medesimo riferimento. // Il cilindro è definito con centri delle due basi, e raggio. // In caso di intersezione viene restituito true e i parametri in dU1 e dU2. //---------------------------------------------------------------------------- bool IntersLineCyl( const Point3d& ptL, const Vector3d& vtL, const Point3d& ptCyl1, const Point3d& ptCyl2, double dRad, double& dU1, double& dU2) { // Determino asse ed altezza del cilindro Vector3d vtCyl = ptCyl2 - ptCyl1 ; double dHeight = vtCyl.Len() ; if ( dHeight < EPS_SMALL) return false ; vtCyl /= dHeight ; // Riferimento intrinseco del cilindro Frame3d frCyl ; if ( ! frCyl.Set( ptCyl1, vtCyl)) return false ; // Ora eseguo i conti nel riferimento intrinseco bool bTapLow = false ; bool bTapUp = false ; bool bIgnoreTap = true ; Point3d ptInt1, ptInt2 ; Vector3d vtN1, vtN2 ; return IntersLineCyl( ptL, vtL, frCyl, dHeight, dRad, bTapLow, bTapUp, dU1, ptInt1, vtN1, dU2, ptInt2, vtN2, bIgnoreTap) ; } //---------------------------------------------------------------------------- // linea già nel riferimento intrinseco del cilindro //---------------------------------------------------------------------------- bool IntersLineCyl( const Point3d& ptL, const Vector3d& vtL, double dRad, double dHeight, double& dU1, double& dU2) { Point3d ptInt1, ptInt2 ; Vector3d vtN1, vtN2 ; bool bTapLow = false, bTapUp = false ; bool bIgnoreTap = true ; return IntersLineCyl( ptL, vtL, dHeight, dRad, bTapLow, bTapUp, dU1, ptInt1, vtN1, dU2, ptInt2, vtN2, bIgnoreTap) ; } //---------------------------------------------------------------------------- // funzione esposta per altre dll //---------------------------------------------------------------------------- bool IntersLineCyl( const Point3d& ptLineSt, const Vector3d& vtLineDir, const Frame3d& CylFrame, double dH, double dRad, double& dU1, Point3d& ptInt1, Vector3d& vtN1, double& dU2, Point3d& ptInt2, Vector3d& vtN2) { return IntersLineCyl( ptLineSt, vtLineDir, CylFrame, dH, dRad, false, false, dU1, ptInt1, vtN1, dU2, ptInt2, vtN2, true, false) ; }