计算几何全家桶(一)
【一】:图形之间的位置关系
1.点与线段
(一):判断点是否在线段上
给定点 ,线段的端点 ,如果点 在线段 上,则需满足 与 共线,且 在 之间。
bool check_xd(d p,d a,d b){//判断点 p 是否在线段 AB 上
return !jd(cj(p-a,b-a))&&jd(dj(p-a,p-b))<=0;
//用 len(p-a)+len(p-b)==len(a-b) 判断更为直观,但精度损失大
}//向量pa与pb共线且p在a,b之间
(二):求点到线段的距离
给定点 ,线段的端点 ,点到线段距离情况分三种。
第一种: 最近距离是线段 的长度
第二种: 最近距离是线段 的长度
第三种: 最近距离是点 到线段 的垂直距离
dd dis_xd(d p,d a,d b){//点 P 到线段 AB 的距离
if(a==b)return len(p-a);//特判线段两端点重合的情况
d x=p-a,y=p-b,z=b-a;//x:PA y:PB z:BA
if(jd(dj(x,z))<0)return len(x);
if(jd(dj(y,z))>0)return len(y);
return ABS(cj(x,z)/len(z));//用叉积计算面积除底边长就是点到线段垂直距离
}
2.点与直线
(一):判断点是否在直线上
给定点 ,直线上的两点 ,则只需判断直线 与直线 共线即可。
bool check_zx(d p,d a,d b){//判断点 P 是否在直线 AB 上
return !jd(cj(p-a,p-b));//PA,PB 共线
}
(二):点到直线的垂足
给定点 ,直线上的两点 ,求点到直线的垂足。
首先表示出 ,然后分别求出 在 上的投影长度也就是点积(图中红色和蓝色部分),就能求得垂足位置了。
d fd(d p,d a,d b){//点P到直线AB的垂足
d x=p-a,y=p-b,z=b-a;
dd len1=dj(x,z)/len(z),len2=-1.0*dj(y,z)/len(z);//分别计算AP,BP在AB,BA上的投影
return a+z*(len1/(len1+len2));//点A加上向量AF
}
(三):点关于直线的对称点
给定点 ,直线上的两点 ,求点关于直线的对称点。
很明显就是把上图的 延长一倍。
d dcd(d p,d a,d b){//点P关于直线AB的对称点
return p+(fd(p,a,b)-p)*2;//将PF延长一倍即可
}
3.线与线
(一):求两直线的交点
分别给定两直线上的两点,求两直线交点。
如上图中,要求的就是 ,可以先求出 ,也就是图中红色线段与蓝色线段长度比,而长度比很明显可以用叉积求面积比得出。
d cross_zx(d A,d B,d C,d D){//两直线AB,CD的交点
d x=B-A,y=D-C,z=A-C;
return A+x*(cj(y,z)/cj(x,y));//点A加上向量AF
}
(二):判断线段与直线是否相交
分别给定直线上两点和线段端点,判断是否相交。
稍微转换一下思路:可以先求出两直线的交点,然后判断交点是否在线段上即可。
用到的函数之前都已列出。
bool check_zx_xd(d A,d B,d C,d D){//判断直线AB与线段CD是否相交
return check_xd(cross_zx(A,B,C,D),C,D);//直线AB与直线CD的交点在线段CD上
}
(三):判断两线段是否相交
如图中的线段 ,很明显要同时保证 分别在直线 两侧且 分别在直线 两侧。
判断的方法也是用叉积,即保证异号。
bool check_xd(d A,d B,d C,d D){//判断两线段AB,CD是否相交
dd c1=cj(B-A,C-A),c2=cj(B-A,D-A);
dd d1=cj(D-C,A-C),d2=cj(D-C,B-C);
return jd(c1)*jd(c2)<0&&jd(d1)*jd(d2)<0;//分别在两侧
}
4.点与多边形
(一):判断点是否在任意多边形内(射线法)
给定点 和 条边的多边形,判断点 是否在多边形内部。
如果用射线法,时间复杂度是 ,主要是由点 为起点做一条射线,如果射线与多边形的交点个数为奇数个,说明点在多边形内部,否则在外部。
可以参考一下上面的图,如果一个点在多边形外部,以之为起点的射线最终必然在多边形外部,所以射线与多边形的交点个数必定为偶数个,可以类似于进去了必定还会出来。
然后就是要考虑到其中的一些特殊情况。
- 点在多边形边上的情况,我是直接将这种情况直接归为一类。
- 射线穿过多边形顶点的情况,对于这种情况比较难理解,可以看一下右上的图。
首先,线段与射线相交的条件是线段的两个端点在射线的两侧。
如果我们定义射线穿越的顶点属于某一侧,比如在图中规定射线经过的点均在射线的以上一侧。
所以图中射线 没有经过边 ,但经过了 ,所以在多边形内部。
图中射线 同时经过了边 ,所以在多边形外部。
图中射线 没有与多边形相交,所以在多边形外部。
- 射线与多边形的边重合的情况,很显然,对于这条重合的边的两个端点,都在射线以上的一侧,可以看做是射线没有穿过这条边来处理。
int PIP(d *P,int n,d a){//【射线法】判断点A是否在任意多边形Poly以内
int cnt=0;dd tmp;
for(int i=1;i<=n;i++){
int j=i<n?i+1:1;
if(check_xd(a,P[i],P[j]))return 2;//点在多边形上
if(a.y>=min(P[i].y,P[j].y)&&a.y<max(P[i].y,P[j].y))//纵坐标在该线段两端点之间
tmp=P[i].x+(a.y-P[i].y)/(P[j].y-P[i].y)*(P[j].x-P[i].x),cnt+=jd(tmp-a.x)>0;//交点在A右方
}return cnt&1;//穿过奇数次则在多边形以内
}
(二):判断点是否在凸多边形内(二分法)
二分法判断的步骤:
- 选择多边形的一个点为起点,连接其他点作射线。
- 判断给定的点是否在所有射线包围的区域之内。
- 二分找到正好包夹当前点的两条射线。
- 判断给定点在这条边的左方还是右方,由此判断给定点是否在三角形区域内,也就是是否在多边形内。
bool judge(d a,d L,d R){//判断AL是否在AR右边
return jd(cj(L-a,R-a))>0;//必须严格以内
}
int PIP_(d *P,int n,d a){//【二分法】判断点A是否在凸多边形Poly以内
//点按逆时针给出
if(judge(P[1],a,P[2])||judge(P[1],P[n],a))return 0;//在P[1_2]或P[1_n]外
if(check_xd(a,P[1],P[2])||check_xd(a,P[1],P[n]))return 2;//在P[1_2]或P[1_n]上
int l=2,r=n-1;
while(l<r){//二分找到一个位置pos使得P[1]_A在P[1_pos],P[1_(pos+1)]之间
int mid=l+r+1>>1;
if(judge(P[1],P[mid],a))l=mid;
else r=mid-1;
}
if(judge(P[l],a,P[l+1]))return 0;//在P[pos_(pos+1)]外
if(check_xd(a,P[l],P[l+1]))return 2;//在P[pos_(pos+1)]上
return 1;
}
5.线与多边形
(一):判断线段是否在任意多边形中
线段两端点均在多边形中且线段与多边形的边没有相交。
(二):判断线段是否在凸多边形中
线段两端点均在多边形中即可。
6.多边形与多边形
判断两个多边形是否相离
属于不同多边形的任意两边都不相交,且其中一个多边形上的任意点都不被另一个多边形所包含。
使用射线法和二分法的时间复杂度分别为
bool judge_PP(d *A,int n,d *B,int m){//判断多边形A与多边形B是否相离
for(int i=1;i<=n;i++){
int g=i<n?i+1:1;
for(int j=1;j<=m;j++){
int h=j<m?j+1:j;
if(cross_xd(A[i],A[g],B[j],B[h]))return 0;//两线段相交
if(PIP(B,m,A[i])||PIP(A,n,B[j]))return 0;//点包含在内
}
}return 1;
}
【二】:图形面积
1.任意多边形面积
对于图中情况,有:
所以很明显可以用叉积计算。通式就是:
dd Polyarea(d *P,int n){//任意多边形P的面积
dd s=0;
for(int i=1;i<=n;i++)s+=cj(P[i],P[i<n?i+1:1]);
return s/2.0;
}
2.圆的面积并
不会,咕咕咕
3.三角形的面积并
不会,咕咕咕
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)