线段和圆/球的相交性检测

来源:互联网 发布:windows命令行创建文件 编辑:程序博客网 时间:2024/05/29 09:11

本文讨论2D中线段和圆的相交性检测,检测的方法也适用于3D中线段和球之间的相交性测试,这是因为可以在包含线段和球心的平面进行检测,从而将3D问题转化为2D问题。(如果线段包含在穿过球心的直线上,那么这个平面就不是唯一的,但这并不是问题,在这种情况下我们能使用任意包含射线和球心的平面来进行计算。)

  如下图用圆心c和半径r来定义球,线段的定义为:p(t)=p0+td,这里d为单位向量,t从0变化到l,l为线段长度。所要求的是交点处t的值:

  t = a - f;

  a的计算方法如下,设e为从p0直线圆心c的向量:

  e = c - p0;

  将e投影到d上,这个向量的的长度为a,它的计算式为:

  a = e.d(点乘)  (此式看不明白的请参考向量投影相关知识)

  现在的任务就是计算f。首先,根据勾股定理,可以很清楚地得到:

  f2+b2=r2

  在较大的三角形中用勾股定理求得b2:

  a2+b2=e2

  b2 = e2-a2

  e是从线段起点到圆心之间的距离,也就是向量e的长度,因此,e2:

  e2 = e.e

  代入并化简得:

  f = sqrt(r2-e2+a2)

 最后求得t:

 t = a - f

 这里考虑开根号出来的正负两种情况,从而得到两个交点。

  这里注意为负,那么射线与圆不相交。

 

代码实现如下:

[cpp] view plaincopy
  1. #include "iostream"  
  2. #include "math.h"  
  3. using namespace std;  
  4.   
  5. #define EPS 0.00001  
  6.   
  7. struct pointf   
  8. {  
  9.     float x;  
  10.     float y;  
  11.   
  12.     pointf()  
  13.     {  
  14.   
  15.     }  
  16.     pointf(float m, float n) : x(m),y(n)  
  17.     {  
  18.     }  
  19. };  
  20.   
  21. /** 
  22. * @brief 求线段与圆的交点 
  23. * @return 如果有交点返回true,否则返回false 
  24. * @note 与圆可能存在两个交点,如果存在两个交点在ptInter1和ptInter2都为有效值,如果有一个交点,则ptInter2的值为 
  25. *       无效值,此处为65536.0 
  26. */  
  27. bool LineInterCircle(  
  28.                 const pointf ptStart, // 线段起点  
  29.                 const pointf ptEnd, // 线段终点  
  30.                 const pointf ptCenter, // 圆心坐标  
  31.                 const float Radius,  
  32.                 pointf& ptInter1,  
  33.                 pointf& ptInter2)  
  34. {  
  35.     ptInter1.x = ptInter2.x = 65536.0f;  
  36.     ptInter2.y = ptInter2.y = 65536.0f;  
  37.   
  38.     float fDis = sqrt((ptEnd.x - ptStart.x) * (ptEnd.x - ptStart.x) + (ptEnd.y - ptStart.y) * (ptEnd.y - ptStart.y));  
  39.       
  40.     pointf d;  
  41.     d.x = (ptEnd.x - ptStart.x) / fDis;  
  42.     d.y = (ptEnd.y - ptStart.y) / fDis;  
  43.   
  44.     pointf E;  
  45.     E.x = ptCenter.x - ptStart.x;  
  46.     E.y = ptCenter.y - ptStart.y;  
  47.   
  48.     float a = E.x * d.x + E.y * d.y;  
  49.     float a2 = a * a;  
  50.   
  51.     float e2 = E.x * E.x + E.y * E.y;  
  52.   
  53.     float r2 = Radius * Radius;  
  54.   
  55.     if ((r2 - e2 + a2) < 0)  
  56.     {  
  57.         return false;  
  58.     }  
  59.     else  
  60.     {  
  61.         float f = sqrt(r2 - e2 + a2);  
  62.   
  63.         float t = a - f;  
  64.   
  65.         if( ((t - 0.0) > - EPS) && (t - fDis) < EPS)  
  66.         {  
  67.             ptInter1.x = ptStart.x + t * d.x;  
  68.             ptInter1.y = ptStart.y + t * d.y;  
  69.         }         
  70.   
  71.         t = a + f;  
  72.   
  73.         if( ((t - 0.0) > - EPS) && (t - fDis) < EPS)  
  74.         {  
  75.             ptInter2.x = ptStart.x + t * d.x;  
  76.             ptInter2.y = ptStart.y + t * d.y;  
  77.         }  
  78.   
  79.         return true;  
  80.     }  
  81. }  
  82.   
  83. void main(void)  
  84. {  
  85.     pointf ptStart(3.0, 2.0);  
  86.     pointf ptEnd(10.0, 6.0);  
  87.   
  88.     pointf ptCenter(10.0, 4.0);  
  89.     float fR = 3.0;  
  90.   
  91.     pointf pt1, pt2;  
  92.   
  93.     if (!LineInterCircle(ptStart, ptEnd, ptCenter, fR, pt1, pt2))  
  94.     {  
  95.         cout<<"不相交!"<<endl;  
  96.     }  
  97.     else  
  98.     {  
  99.         cout<<"交点"<<endl;  
  100.         cout<<pt1.x<<" "<<pt1.y<<endl;  
  101.   
  102.         cout<<pt2.x<<" "<<pt2.y<<endl;  
  103.     }  
  104.       
  105. }  

程序输出:

交点
7.01444 4.29396
65536 65536
Press any key to continue

 

0 0