00 本文内容
- 问题描述
- 图像
- 输入输出
- 数学公式推导
- 三维坐标下直线的参数方程
- 直线与球面交点求解方法
- 判断距离是否与方向向量相同
- C++代码实现
- 函数化封装
- 可根据需要只修改函数参数
- 特殊情况
- 实际项目中遇到返回无效值
- 当该点在球面上,讨论相切与不相切
01 问题描述
问题描述:在三维坐标内,已知一球体sphere内有一点focus,该点沿某方向向量n0延伸,与球面交于一点f。求解该点focus沿该方向向量到球面的距离,即focus和f两点间距离。
输入:该点focus的坐标、方向向量n0的坐标、sphere球心坐标与半径R
输入:距离ans
02 数学公式推导
刚开始遇到这个问题,大致觉得可以编程实现,但是很多高中几何知识都忘了。经查阅,将数学描述放置于下方。
已知focus点的坐标
和方向向量n0
,可以得到两者所在直线的参数方程
即
而已知球面方程
将参数方程组带入球面桌面,可以得到关于参数t的一元二次方程组
其中A、B、C可用
等参数表示
求解可得
带回直线的参数方程,可得两交点坐标,可分别与focus求两点间距离。
最后判断是否沿方向向量方向,可以通过两向量对应部分符号是否相同来判断。
03 C++代码实现
这是最初版本,可根据实际需要修改。
#include <math.h>
#include<iostream>
using namespace std;
typedef struct Points
{
double x,y,z;
}Point;
double fun(Point focus,Point normalVector,Point sphere,double R)
{
//直接到直线参数方程带入球面式子,得到了关于参数t的一元二次方程
double A,B,C;//ABC为关于t的一元二次方程的参数
A=pow(normalVector.x,2) + pow(normalVector.y,2) + pow(normalVector.z,2);
B=2*( (focus.x-sphere.x)*normalVector.x + (focus.y-sphere.y)*normalVector.y + (focus.z-sphere.z)*normalVector.z );
C=pow(focus.x-sphere.x,2) + pow(focus.y-sphere.y,2) + pow(focus.z-sphere.z,2) - pow(R,2);
//cout<<"A B C t1 t2: "<<A<<" "<<B<<" "<<C<<" ";
//现在可得 A*t*t+B*t+C=0,解一元二次方程
double t1,t2;
t1 = (-B+sqrt(pow(B,2)-4*A*C)) / (2*A);
t2 = (-B-sqrt(pow(B,2)-4*A*C)) / (2*A);
//cout<<t1<<" "<<t2<<endl;
//将t带入带原直线参数方程中,可以得到交点坐标
Point intersection1,intersection2;//交点
intersection1.x=normalVector.x*t1+focus.x;
intersection1.y=normalVector.y*t1+focus.y;
intersection1.z=normalVector.z*t1+focus.z;
intersection2.x=normalVector.x*t2+focus.x;
intersection2.y=normalVector.y*t2+focus.y;
intersection2.z=normalVector.z*t2+focus.z;
//现在知道求内点focus,两交点intersection,求法向量方向的距离
if((intersection1.x-focus.x)*normalVector.x>0||(intersection1.y-focus.y)*normalVector.y>0||(intersection1.z-focus.z)*normalVector.z>0)
{
//这时候说明 intersection1 是和法向量同向的点
double ans=sqrt(pow(intersection1.x-focus.x,2)+pow(intersection1.y-focus.y,2)+pow(intersection1.z-focus.z,2));
return ans;
}
else
{
//这时候说明 intersection2 是和法向量同向的点
double ans=sqrt(pow(intersection2.x-focus.x,2)+pow(intersection2.y-focus.y,2)+pow(intersection2.z-focus.z,2));
return ans;
}
}
int main()
{
Point focus;//圆内该点
Point normalVector;//法向量
Point sphere;//园的圆心
double R;//圆的半径
cin>>focus.x>>focus.y>>focus.z;
cin>>normalVector.x>>normalVector.y>>normalVector.z;
cin>>sphere.x>>sphere.y>>sphere.z>>R;
double ans=fun(focus,normalVector,sphere,R);
cout<<"ans="<<ans<<endl;
}
04 特殊情况
但是很多实际情况中(给老师用后有错),该点focus很可能会出现在球面上,此时使用上述代码会出错。
因此接下来将讨论focus在球面上的情况:
- 方向向量与球面相切
- 方向向量与球面不相切,且该方向向量指向球体外部
- 方向向量与球面不相切,且该方向向量指向球体内部
情况1:只有一个交点,也就是focus本身,这时直接返回0
情况2:指向外部,则此时方向向量方向是0,返回0
情况3:指向内部,返回值应该为两交点间距离
修改后的代码如下:
#include <math.h>
#include<iostream>
using namespace std;
typedef struct Points
{
double x,y,z;
}Point;
double fun(Point focus,Point normalVector,Point sphere,double R)
{
//直接到直线参数方程带入球面式子,得到了关于参数t的一元二次方程
double A,B,C;//ABC为关于t的一元二次方程的参数
A=pow(normalVector.x,2) + pow(normalVector.y,2) + pow(normalVector.z,2);
B=2*( (focus.x-sphere.x)*normalVector.x + (focus.y-sphere.y)*normalVector.y + (focus.z-sphere.z)*normalVector.z );
C=pow(focus.x-sphere.x,2) + pow(focus.y-sphere.y,2) + pow(focus.z-sphere.z,2) - pow(R,2);
// cout<<"A B C t1 t2: "<<A<<" "<<B<<" "<<C<<" ";
//现在可得 A*t*t+B*t+C=0,解一元二次方程
double t1,t2;
t1 = (-B+sqrt(pow(B,2)-4*A*C)) / (2*A);
t2 = (-B-sqrt(pow(B,2)-4*A*C)) / (2*A);
// cout<<t1<<" "<<t2<<endl;
if(t1==0&&t2==0)//该点在球面上,且方向向量与球面相切 ,无论哪个方向都是0
return 0;
//将t带入带原直线参数方程中,可以得到交点坐标
Point intersection1,intersection2;//交点
intersection1.x=normalVector.x*t1+focus.x;
intersection1.y=normalVector.y*t1+focus.y;
intersection1.z=normalVector.z*t1+focus.z;
intersection2.x=normalVector.x*t2+focus.x;
intersection2.y=normalVector.y*t2+focus.y;
intersection2.z=normalVector.z*t2+focus.z;
double f1=(intersection1.x-focus.x)*normalVector.x+(intersection1.y-focus.y)*normalVector.y+(intersection1.z-focus.z)*normalVector.z;
double f2=(intersection2.x-focus.x)*normalVector.x+(intersection2.y-focus.y)*normalVector.y+(intersection2.z-focus.z)*normalVector.z;
//现在知道求内点focus,两交点intersection,求法向量方向的距离
if(f1>0||f2<0)
{
//这时候说明 intersection1 是和法向量同向的点
double ans=sqrt(pow(intersection1.x-focus.x,2)+pow(intersection1.y-focus.y,2)+pow(intersection1.z-focus.z,2));
return ans;
}
else if(f1<0||f2>0)
{
//这时候说明 intersection2 是和法向量同向的点
double ans=sqrt(pow(intersection2.x-focus.x,2)+pow(intersection2.y-focus.y,2)+pow(intersection2.z-focus.z,2));
return ans;
}
return -1;
}
int main()
{
Point focus;//圆内该点
Point normalVector;//法向量
Point sphere;//园的圆心
double R;//圆的半径
cin>>focus.x>>focus.y>>focus.z;
cin>>normalVector.x>>normalVector.y>>normalVector.z;
cin>>sphere.x>>sphere.y>>sphere.z>>R;
double ans=fun(focus,normalVector,sphere,R);
cout<<"ans="<<ans<<endl;
}