线段和圆/球的相交性检测
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
如下图用圆心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
相关文章推荐
- python安装一些类库的时候遇到这个问题
- Eclipse相关
- 杭电-5597
- Java日志系统-- Log4j
- 函数参数传递
- 安卓开发--AsyncTask
- leetcode -- Max Points on a Line -- 重点
- 阿里云安装wamp时出错
- 关于ValueOperations的increment方法的解惑
- oracle 11g r2 rac ssh两节点互信对等配置Permission denied (publickey,gssapi-with-mic,password)
- Java成神之路
- CIKM 2013 Paper CQARank: Jointly Model Topics and Expertise in Community Question Answering
- 数据与内存
- 【转】 SqlServer性能检测和优化工具使用详细
- 机器学习公开课笔记(4):神经网络(Neural Network)——表示
- C++ 基础
- 【学神-RHEL7】1-23-RHEL7启动配置
- 《SDN: Software Defined Networks》笔记--SDN控制器,网络可编程
- jsp网页倒计时跳转页面
- 对象、类、组合的方法调用的内存分布图