您的位置:首页 > 其它

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

2015-12-23 22:51 417 查看
本文讨论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

#include "iostream"

#include "math.h"

using namespace std;

#define EPS 0.00001

struct pointf

{

float x;

float y;

pointf()

{

}

pointf(float m, float n) : x(m),y(n)

{

}

};

/**

* @brief 求线段与圆的交点

* @return 如果有交点返回true,否则返回false

* @note 与圆可能存在两个交点,如果存在两个交点在ptInter1和ptInter2都为有效值,如果有一个交点,则ptInter2的值为

* 无效值,此处为65536.0

*/

bool LineInterCircle(

const pointf ptStart, // 线段起点

const pointf ptEnd, // 线段终点

const pointf ptCenter, // 圆心坐标

const float Radius,

pointf& ptInter1,

pointf& ptInter2)

{

ptInter1.x = ptInter2.x = 65536.0f;

ptInter2.y = ptInter2.y = 65536.0f;

float fDis = sqrt((ptEnd.x - ptStart.x) * (ptEnd.x - ptStart.x) + (ptEnd.y - ptStart.y) * (ptEnd.y - ptStart.y));

pointf d;

d.x = (ptEnd.x - ptStart.x) / fDis;

d.y = (ptEnd.y - ptStart.y) / fDis;

pointf E;

E.x = ptCenter.x - ptStart.x;

E.y = ptCenter.y - ptStart.y;

float a = E.x * d.x + E.y * d.y;

float a2 = a * a;

float e2 = E.x * E.x + E.y * E.y;

float r2 = Radius * Radius;

if ((r2 - e2 + a2) < 0)

{

return false;

}

else

{

float f = sqrt(r2 - e2 + a2);

float t = a - f;

if( ((t - 0.0) > - EPS) && (t - fDis) < EPS)

{

ptInter1.x = ptStart.x + t * d.x;

ptInter1.y = ptStart.y + t * d.y;

}

t = a + f;

if( ((t - 0.0) > - EPS) && (t - fDis) < EPS)

{

ptInter2.x = ptStart.x + t * d.x;

ptInter2.y = ptStart.y + t * d.y;

}

return true;

}

}

void main(void)

{

pointf ptStart(3.0, 2.0);

pointf ptEnd(10.0, 6.0);

pointf ptCenter(10.0, 4.0);

float fR = 3.0;

pointf pt1, pt2;

if (!LineInterCircle(ptStart, ptEnd, ptCenter, fR, pt1, pt2))

{

cout<<"不相交!"<<endl;

}

else

{

cout<<"交点"<<endl;

cout<<pt1.x<<" "<<pt1.y<<endl;

cout<<pt2.x<<" "<<pt2.y<<endl;

}

}

程序输出:

交点

7.01444 4.29396

65536 65536

Press any key to continue
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: